code: purgatorio

Download patch

ref: 7975eec3e602fd5bc3e93c78f4166259190f2fe9
parent: 147d51bf7c6cd8798ddb26e33d857be13a66c548
author: henesy <devnull@localhost>
date: Sun Nov 4 06:36:16 EST 2018

add os and such

diff: cannot open b/locale/en_US/dict//null: file does not exist: 'b/locale/en_US/dict//null' diff: cannot open b/locale/en_US//null: file does not exist: 'b/locale/en_US//null' diff: cannot open b/locale//null: file does not exist: 'b/locale//null' diff: cannot open b/man/1//null: file does not exist: 'b/man/1//null' diff: cannot open b/man/10//null: file does not exist: 'b/man/10//null' diff: cannot open b/man/2//null: file does not exist: 'b/man/2//null' diff: cannot open b/man/3//null: file does not exist: 'b/man/3//null' diff: cannot open b/man/4//null: file does not exist: 'b/man/4//null' diff: cannot open b/man/5//null: file does not exist: 'b/man/5//null' diff: cannot open b/man/6//null: file does not exist: 'b/man/6//null' diff: cannot open b/man/7//null: file does not exist: 'b/man/7//null' diff: cannot open b/man/8//null: file does not exist: 'b/man/8//null' diff: cannot open b/man/9//null: file does not exist: 'b/man/9//null' diff: cannot open b/man/lib/lookman//null: file does not exist: 'b/man/lib/lookman//null' diff: cannot open b/man/lib//null: file does not exist: 'b/man/lib//null' diff: cannot open b/man//null: file does not exist: 'b/man//null' diff: cannot open b/mkfiles//null: file does not exist: 'b/mkfiles//null' diff: cannot open b/module/alphabet//null: file does not exist: 'b/module/alphabet//null' diff: cannot open b/module/grid/demo//null: file does not exist: 'b/module/grid/demo//null' diff: cannot open b/module/grid//null: file does not exist: 'b/module/grid//null' diff: cannot open b/module/math//null: file does not exist: 'b/module/math//null' diff: cannot open b/module//null: file does not exist: 'b/module//null' diff: cannot open b/opt//null: file does not exist: 'b/opt//null' diff: cannot open b/os/boot/arm1110//null: file does not exist: 'b/os/boot/arm1110//null' diff: cannot open b/os/boot/libflate//null: file does not exist: 'b/os/boot/libflate//null' diff: cannot open b/os/boot/mpc//null: file does not exist: 'b/os/boot/mpc//null' diff: cannot open b/os/boot/pc//null: file does not exist: 'b/os/boot/pc//null' diff: cannot open b/os/boot/puma//null: file does not exist: 'b/os/boot/puma//null' diff: cannot open b/os/boot/rpcg//null: file does not exist: 'b/os/boot/rpcg//null' diff: cannot open b/os/boot//null: file does not exist: 'b/os/boot//null' diff: cannot open b/os/cerf1110//null: file does not exist: 'b/os/cerf1110//null' diff: cannot open b/os/cerf250//null: file does not exist: 'b/os/cerf250//null' diff: cannot open b/os/cerf405//null: file does not exist: 'b/os/cerf405//null' diff: cannot open b/os/fads//null: file does not exist: 'b/os/fads//null' diff: cannot open b/os/gum//null: file does not exist: 'b/os/gum//null' diff: cannot open b/os/init//null: file does not exist: 'b/os/init//null' diff: cannot open b/os/ip//null: file does not exist: 'b/os/ip//null' diff: cannot open b/os/ipaq1110//null: file does not exist: 'b/os/ipaq1110//null' diff: cannot open b/os/ipengine//null: file does not exist: 'b/os/ipengine//null' diff: cannot open b/os/js//null: file does not exist: 'b/os/js//null' diff: cannot open b/os/ks32//null: file does not exist: 'b/os/ks32//null' diff: cannot open b/os/manga//null: file does not exist: 'b/os/manga//null' diff: cannot open b/os/mpc//null: file does not exist: 'b/os/mpc//null' diff: cannot open b/os/omap//null: file does not exist: 'b/os/omap//null' diff: cannot open b/os/pc//null: file does not exist: 'b/os/pc//null' diff: cannot open b/os/port//null: file does not exist: 'b/os/port//null' diff: cannot open b/os/pxa//null: file does not exist: 'b/os/pxa//null' diff: cannot open b/os/rpcg//null: file does not exist: 'b/os/rpcg//null' diff: cannot open b/os/sa1110//null: file does not exist: 'b/os/sa1110//null' diff: cannot open b/os//null: file does not exist: 'b/os//null'
--- /dev/null
+++ b/locale/Argentina
@@ -1,0 +1,1 @@
+ART -10800 ART -10800
--- /dev/null
+++ b/locale/Australia_ACT
@@ -1,0 +1,24 @@
+EST 36000 EST 39600
+  57722400   68004000   89172000  100058400  120621600  131508000
+ 152071200  162957600  183520800  195012000  215575200  226461600
+ 247024800  257911200  278474400  289360800  309924000  320810400
+ 341373600  352260000  372823200  386733600  404877600  415764000
+ 436327200  447213600  467776800  478663200  499226400  511322400
+ 530071200  542772000  562125600  574826400  594180000  606276000
+ 625629600  636516000  657079200  667965600  688528800  699415200
+ 719978400  731469600  752032800  762919200  783482400  794368800
+ 814932000  825818400  846381600  857268000  877831200  888717600
+ 909280800  920772000  941335200  952221600  972784800  983671200
+1004234400 1015120800 1035684000 1046570400 1067133600 1078624800
+1099188000 1110074400 1130637600 1141524000 1162087200 1172973600
+1193536800 1204423200 1224986400 1235872800 1256436000 1267927200
+1288490400 1299376800 1319940000 1330826400 1351389600 1362276000
+1382839200 1393725600 1414288800 1425175200 1445738400 1457229600
+1477792800 1488679200 1509242400 1520128800 1540692000 1551578400
+1572141600 1583028000 1603591200 1615082400 1635645600 1646532000
+1667095200 1677981600 1698544800 1709431200 1729994400 1740880800
+1761444000 1772330400 1792893600 1804384800 1824948000 1835834400
+1856397600 1867284000 1887847200 1898733600 1919296800 1930183200
+1950746400 1962237600 1982800800 1993687200 2014250400 2025136800
+2045700000 2056586400 2077149600 2088036000 2108599200 2119485600
+2140048800
--- /dev/null
+++ b/locale/Australia_Broken-Hill
@@ -1,0 +1,24 @@
+??? 32400 ??? 36000
+  57722400   68004000   89172000  100058400  120621600  131508000
+ 152071200  162957600  183520800  195012000  215575200  226461600
+ 247024800  257911200  278474400  289360800  309924000  320810400
+ 341373600  352260000  372823200  386733600  404877600  415764000
+ 436327200  447213600  467776800  478663200  499226400  511322400
+ 530071200  542772000  562125600  574826400  594180000  606276000
+ 625629600  636516000  657079200  667965600  688528800  699415200
+ 719978400  731469600  752032800  762919200  783482400  794368800
+ 814932000  825818400  846381600  857268000  877831200  888717600
+ 909280800  920772000  941335200  952221600  972784800  983671200
+1004234400 1015120800 1035684000 1046570400 1067133600 1078624800
+1099188000 1110074400 1130637600 1141524000 1162087200 1172973600
+1193536800 1204423200 1224986400 1235872800 1256436000 1267927200
+1288490400 1299376800 1319940000 1330826400 1351389600 1362276000
+1382839200 1393725600 1414288800 1425175200 1445738400 1457229600
+1477792800 1488679200 1509242400 1520128800 1540692000 1551578400
+1572141600 1583028000 1603591200 1615082400 1635645600 1646532000
+1667095200 1677981600 1698544800 1709431200 1729994400 1740880800
+1761444000 1772330400 1792893600 1804384800 1824948000 1835834400
+1856397600 1867284000 1887847200 1898733600 1919296800 1930183200
+1950746400 1962237600 1982800800 1993687200 2014250400 2025136800
+2045700000 2056586400 2077149600 2088036000 2108599200 2119485600
+2140048800
--- /dev/null
+++ b/locale/Australia_LHI
@@ -1,0 +1,24 @@
+??? 37800 ??? 41400
+  57722400   68004000   89172000  100058400  120621600  131508000
+ 152071200  162957600  183520800  195012000  215575200  226461600
+ 247024800  257911200  278474400  289360800  309924000  320810400
+ 341373600  352260000  372823200  386733600  404877600  415764000
+ 436327200  447213600  467776800  478663200  499226400  511322400
+ 530071200  542772000  562125600  574826400  594180000  606276000
+ 625629600  636516000  657079200  667965600  688528800  699415200
+ 719978400  731469600  752032800  762919200  783482400  794368800
+ 814932000  825818400  846381600  857268000  877831200  888717600
+ 909280800  920772000  941335200  952221600  972784800  983671200
+1004234400 1015120800 1035684000 1046570400 1067133600 1078624800
+1099188000 1110074400 1130637600 1141524000 1162087200 1172973600
+1193536800 1204423200 1224986400 1235872800 1256436000 1267927200
+1288490400 1299376800 1319940000 1330826400 1351389600 1362276000
+1382839200 1393725600 1414288800 1425175200 1445738400 1457229600
+1477792800 1488679200 1509242400 1520128800 1540692000 1551578400
+1572141600 1583028000 1603591200 1615082400 1635645600 1646532000
+1667095200 1677981600 1698544800 1709431200 1729994400 1740880800
+1761444000 1772330400 1792893600 1804384800 1824948000 1835834400
+1856397600 1867284000 1887847200 1898733600 1919296800 1930183200
+1950746400 1962237600 1982800800 1993687200 2014250400 2025136800
+2045700000 2056586400 2077149600 2088036000 2108599200 2119485600
+2140048800
--- /dev/null
+++ b/locale/Australia_NSW
@@ -1,0 +1,24 @@
+EST 36000 EST 39600
+  57722400   68004000   89172000  100058400  120621600  131508000
+ 152071200  162957600  183520800  195012000  215575200  226461600
+ 247024800  257911200  278474400  289360800  309924000  320810400
+ 341373600  352260000  372823200  386733600  404877600  415764000
+ 436327200  447213600  467776800  478663200  499226400  511322400
+ 530071200  542772000  562125600  574826400  594180000  606276000
+ 625629600  636516000  657079200  667965600  688528800  699415200
+ 719978400  731469600  752032800  762919200  783482400  794368800
+ 814932000  825818400  846381600  857268000  877831200  888717600
+ 909280800  920772000  941335200  952221600  972784800  983671200
+1004234400 1015120800 1035684000 1046570400 1067133600 1078624800
+1099188000 1110074400 1130637600 1141524000 1162087200 1172973600
+1193536800 1204423200 1224986400 1235872800 1256436000 1267927200
+1288490400 1299376800 1319940000 1330826400 1351389600 1362276000
+1382839200 1393725600 1414288800 1425175200 1445738400 1457229600
+1477792800 1488679200 1509242400 1520128800 1540692000 1551578400
+1572141600 1583028000 1603591200 1615082400 1635645600 1646532000
+1667095200 1677981600 1698544800 1709431200 1729994400 1740880800
+1761444000 1772330400 1792893600 1804384800 1824948000 1835834400
+1856397600 1867284000 1887847200 1898733600 1919296800 1930183200
+1950746400 1962237600 1982800800 1993687200 2014250400 2025136800
+2045700000 2056586400 2077149600 2088036000 2108599200 2119485600
+2140048800
--- /dev/null
+++ b/locale/Australia_North
@@ -1,0 +1,1 @@
+CST 34200 CST 34200
--- /dev/null
+++ b/locale/Australia_Queensland
@@ -1,0 +1,3 @@
+EST 36000 EST 39600
+  57722400   68004000  625629600  636516000  657079200  667965600
+ 688528800  699415200
--- /dev/null
+++ b/locale/Australia_South
@@ -1,0 +1,24 @@
+CST 34200 CST 37800
+  57722400   68608800   89172000  100058400  120621600  131508000
+ 152071200  162957600  183520800  195012000  215575200  226461600
+ 247024800  257911200  278474400  289360800  309924000  320810400
+ 341373600  352260000  372823200  384314400  404877600  415764000
+ 436327200  447213600  467776800  478663200  499226400  511322400
+ 530676000  542772000  562125600  574826400  594180000  606276000
+ 625629600  638330400  657079200  667965600  688528800  701229600
+ 719978400  731469600  752032800  764733600  783482400  794368800
+ 814932000  827632800  846381600  857268000  877831200  890532000
+ 909280800  920772000  941335200  954036000  972784800  983671200
+1004234400 1016935200 1035684000 1046570400 1067133600 1080439200
+1099188000 1110074400 1130637600 1143338400 1162087200 1172973600
+1193536800 1206237600 1224986400 1235872800 1256436000 1269741600
+1288490400 1299376800 1319940000 1332640800 1351389600 1362276000
+1382839200 1395540000 1414288800 1425175200 1445738400 1459044000
+1477792800 1488679200 1509242400 1521943200 1540692000 1551578400
+1572141600 1584842400 1603591200 1615082400 1635645600 1648346400
+1667095200 1677981600 1698544800 1711245600 1729994400 1740880800
+1761444000 1774144800 1792893600 1804384800 1824948000 1837648800
+1856397600 1867284000 1887847200 1900548000 1919296800 1930183200
+1950746400 1964052000 1982800800 1993687200 2014250400 2026951200
+2045700000 2056586400 2077149600 2089850400 2108599200 2119485600
+2140048800
--- /dev/null
+++ b/locale/Australia_Sturt
@@ -1,0 +1,24 @@
+??? 32400 ??? 36000
+  57722400   68004000   89172000  100058400  120621600  131508000
+ 152071200  162957600  183520800  195012000  215575200  226461600
+ 247024800  257911200  278474400  289360800  309924000  320810400
+ 341373600  352260000  372823200  386733600  404877600  415764000
+ 436327200  447213600  467776800  478663200  499226400  511322400
+ 530071200  542772000  562125600  574826400  594180000  606276000
+ 625629600  636516000  657079200  667965600  688528800  699415200
+ 719978400  731469600  752032800  762919200  783482400  794368800
+ 814932000  825818400  846381600  857268000  877831200  888717600
+ 909280800  920772000  941335200  952221600  972784800  983671200
+1004234400 1015120800 1035684000 1046570400 1067133600 1078624800
+1099188000 1110074400 1130637600 1141524000 1162087200 1172973600
+1193536800 1204423200 1224986400 1235872800 1256436000 1267927200
+1288490400 1299376800 1319940000 1330826400 1351389600 1362276000
+1382839200 1393725600 1414288800 1425175200 1445738400 1457229600
+1477792800 1488679200 1509242400 1520128800 1540692000 1551578400
+1572141600 1583028000 1603591200 1615082400 1635645600 1646532000
+1667095200 1677981600 1698544800 1709431200 1729994400 1740880800
+1761444000 1772330400 1792893600 1804384800 1824948000 1835834400
+1856397600 1867284000 1887847200 1898733600 1919296800 1930183200
+1950746400 1962237600 1982800800 1993687200 2014250400 2025136800
+2045700000 2056586400 2077149600 2088036000 2108599200 2119485600
+2140048800
--- /dev/null
+++ b/locale/Australia_Tasmania
@@ -1,0 +1,24 @@
+EST 39600 EST 36000
+   5713200   25671600   37767600   57726000   68007600   89175600
+ 100062000  120625200  131511600  152074800  162961200  183524400
+ 195015600  215578800  226465200  247028400  257914800  278478000
+ 289364400  309927600  320814000  341377200  352263600  372826800
+ 386132400  404881200  417582000  436330800  447217200  467780400
+ 478666800  499230000  510116400  530074800  542775600  562129200
+ 574830000  594183600  606279600  625633200  637729200  657082800
+ 670388400  686718000  701838000  718167600  733287600  749617200
+ 764737200  781066800  796186800  812516400  828241200  844570800
+ 859690800  876020400  891140400  907470000  922590000  938919600
+ 954039600  970369200  985489200 1002423600 1017543600 1033873200
+1048993200 1065322800 1080442800 1096772400 1111892400 1128222000
+1143342000 1159671600 1174791600 1191726000 1206846000 1223175600
+1238295600 1254625200 1269745200 1286074800 1301194800 1317524400
+1332644400 1349578800 1364698800 1381028400 1396148400 1412478000
+1427598000 1443927600 1459047600 1475377200 1490497200 1506826800
+1521946800 1538881200 1554001200 1570330800 1585450800 1601780400
+1616900400 1633230000 1648350000 1664679600 1679799600 1696129200
+1711854000 1728183600 1743303600 1759633200 1774753200 1791082800
+1806202800 1822532400 1837652400 1853982000 1869102000 1886036400
+1901156400 1917486000 1932606000 1948935600 1964055600 1980385200
+1995505200 2011834800 2026954800 2043284400 2058404400 2075338800
+2090458800 2106788400 2121908400 2138238000
--- /dev/null
+++ b/locale/Australia_Victoria
@@ -1,0 +1,24 @@
+EST 36000 EST 39600
+  57722400   68004000   89172000  100058400  120621600  131508000
+ 152071200  162957600  183520800  195012000  215575200  226461600
+ 247024800  257911200  278474400  289360800  309924000  320810400
+ 341373600  352260000  372823200  384314400  404877600  415764000
+ 436327200  447213600  467776800  478663200  499226400  511322400
+ 530071200  542772000  561520800  574826400  594180000  606276000
+ 625629600  637725600  657079200  667965600  688528800  699415200
+ 719978400  731469600  752032800  762919200  783482400  794368800
+ 814932000  825818400  846381600  857268000  877831200  888717600
+ 909280800  920772000  941335200  952221600  972784800  983671200
+1004234400 1015120800 1035684000 1046570400 1067133600 1078624800
+1099188000 1110074400 1130637600 1141524000 1162087200 1172973600
+1193536800 1204423200 1224986400 1235872800 1256436000 1267927200
+1288490400 1299376800 1319940000 1330826400 1351389600 1362276000
+1382839200 1393725600 1414288800 1425175200 1445738400 1457229600
+1477792800 1488679200 1509242400 1520128800 1540692000 1551578400
+1572141600 1583028000 1603591200 1615082400 1635645600 1646532000
+1667095200 1677981600 1698544800 1709431200 1729994400 1740880800
+1761444000 1772330400 1792893600 1804384800 1824948000 1835834400
+1856397600 1867284000 1887847200 1898733600 1919296800 1930183200
+1950746400 1962237600 1982800800 1993687200 2014250400 2025136800
+2045700000 2056586400 2077149600 2088036000 2108599200 2119485600
+2140048800
--- /dev/null
+++ b/locale/Australia_West
@@ -1,0 +1,3 @@
+WST 28800 WDT 32400
+ 152071200  162957600  436327200  447213600  690343200  699415200
+1193536800 1206842400 1224986400 1238292000
--- /dev/null
+++ b/locale/Australia_Yancowinna
@@ -1,0 +1,24 @@
+CST 34200 CST 37800
+  57722400   68004000   89172000  100058400  120621600  131508000
+ 152071200  162957600  183520800  195012000  215575200  226461600
+ 247024800  257911200  278474400  289360800  309924000  320810400
+ 341373600  352260000  372823200  386733600  404877600  415764000
+ 436327200  447213600  467776800  478663200  499226400  511322400
+ 530071200  542772000  562125600  574826400  594180000  606276000
+ 625629600  636516000  657079200  667965600  688528800  699415200
+ 719978400  731469600  752032800  762919200  783482400  794368800
+ 814932000  825818400  846381600  857268000  877831200  888717600
+ 909280800  920772000  941335200  952221600  972784800  983671200
+1004234400 1015120800 1035684000 1046570400 1067133600 1078624800
+1099188000 1110074400 1130637600 1141524000 1162087200 1172973600
+1193536800 1204423200 1224986400 1235872800 1256436000 1267927200
+1288490400 1299376800 1319940000 1330826400 1351389600 1362276000
+1382839200 1393725600 1414288800 1425175200 1445738400 1457229600
+1477792800 1488679200 1509242400 1520128800 1540692000 1551578400
+1572141600 1583028000 1603591200 1615082400 1635645600 1646532000
+1667095200 1677981600 1698544800 1709431200 1729994400 1740880800
+1761444000 1772330400 1792893600 1804384800 1824948000 1835834400
+1856397600 1867284000 1887847200 1898733600 1919296800 1930183200
+1950746400 1962237600 1982800800 1993687200 2014250400 2025136800
+2045700000 2056586400 2077149600 2088036000 2108599200 2119485600
+2140048800
--- /dev/null
+++ b/locale/Brazil_Acre
@@ -1,0 +1,18 @@
+AST -18000 ADT -14400
+ 561952800  571626000  593402400  603075600  625543200  634525200
+ 656906400  665974800  688356000  697424400  719805600  729478800
+ 751255200  760928400  782704800  792378000  814845600  823827600
+ 846208800  855277200  877658400  887418000  909108000  918781200
+ 940557600  950230800  972698400  981680400 1004061600 1013130000
+1035511200 1044579600 1066960800 1076720400 1098410400 1108083600
+1129860000 1139533200 1162000800 1170982800 1193364000 1202432400
+1224813600 1234573200 1256263200 1265936400 1287712800 1297386000
+1319162400 1328835600 1351216800 1360285200 1382666400 1391734800
+1414116000 1423875600 1445565600 1455238800 1477015200 1486688400
+1509156000 1518138000 1540519200 1549587600 1571968800 1581037200
+1603418400 1613091600 1634868000 1644541200 1666317600 1675990800
+1698458400 1707440400 1729821600 1738890000 1761271200 1771030800
+1792720800 1802394000 1824170400 1833843600 1856311200 1865293200
+1887674400 1896742800 1919124000 1928192400 1950573600 1960333200
+1982023200 1991696400 2013472800 2023146000 2045613600 2054595600
+2076976800 2086045200 2108426400 2118186000 2139876000
--- /dev/null
+++ b/locale/Brazil_DeNoronha
@@ -1,0 +1,18 @@
+FST -7200 FDT -3600
+ 561952800  571626000  593402400  603075600  625543200  634525200
+ 656906400  665974800  688356000  697424400  719805600  729478800
+ 751255200  760928400  782704800  792378000  814845600  823827600
+ 846208800  855277200  877658400  887418000  909108000  918781200
+ 940557600  950230800  972698400  981680400 1004061600 1013130000
+1035511200 1044579600 1066960800 1076720400 1098410400 1108083600
+1129860000 1139533200 1162000800 1170982800 1193364000 1202432400
+1224813600 1234573200 1256263200 1265936400 1287712800 1297386000
+1319162400 1328835600 1351216800 1360285200 1382666400 1391734800
+1414116000 1423875600 1445565600 1455238800 1477015200 1486688400
+1509156000 1518138000 1540519200 1549587600 1571968800 1581037200
+1603418400 1613091600 1634868000 1644541200 1666317600 1675990800
+1698458400 1707440400 1729821600 1738890000 1761271200 1771030800
+1792720800 1802394000 1824170400 1833843600 1856311200 1865293200
+1887674400 1896742800 1919124000 1928192400 1950573600 1960333200
+1982023200 1991696400 2013472800 2023146000 2045613600 2054595600
+2076976800 2086045200 2108426400 2118186000 2139876000
--- /dev/null
+++ b/locale/Brazil_East
@@ -1,0 +1,18 @@
+EST -10800 EDT -7200
+ 561952800  571626000  593402400  603075600  625543200  634525200
+ 656906400  665974800  688356000  697424400  719805600  729478800
+ 751255200  760928400  782704800  792378000  814845600  823827600
+ 846208800  855277200  877658400  887418000  909108000  918781200
+ 940557600  950230800  972698400  981680400 1004061600 1013130000
+1035511200 1044579600 1066960800 1076720400 1098410400 1108083600
+1129860000 1139533200 1162000800 1170982800 1193364000 1202432400
+1224813600 1234573200 1256263200 1265936400 1287712800 1297386000
+1319162400 1328835600 1351216800 1360285200 1382666400 1391734800
+1414116000 1423875600 1445565600 1455238800 1477015200 1486688400
+1509156000 1518138000 1540519200 1549587600 1571968800 1581037200
+1603418400 1613091600 1634868000 1644541200 1666317600 1675990800
+1698458400 1707440400 1729821600 1738890000 1761271200 1771030800
+1792720800 1802394000 1824170400 1833843600 1856311200 1865293200
+1887674400 1896742800 1919124000 1928192400 1950573600 1960333200
+1982023200 1991696400 2013472800 2023146000 2045613600 2054595600
+2076976800 2086045200 2108426400 2118186000 2139876000
--- /dev/null
+++ b/locale/Brazil_West
@@ -1,0 +1,18 @@
+WST -14400 WDT -10800
+ 561952800  571626000  593402400  603075600  625543200  634525200
+ 656906400  665974800  688356000  697424400  719805600  729478800
+ 751255200  760928400  782704800  792378000  814845600  823827600
+ 846208800  855277200  877658400  887418000  909108000  918781200
+ 940557600  950230800  972698400  981680400 1004061600 1013130000
+1035511200 1044579600 1066960800 1076720400 1098410400 1108083600
+1129860000 1139533200 1162000800 1170982800 1193364000 1202432400
+1224813600 1234573200 1256263200 1265936400 1287712800 1297386000
+1319162400 1328835600 1351216800 1360285200 1382666400 1391734800
+1414116000 1423875600 1445565600 1455238800 1477015200 1486688400
+1509156000 1518138000 1540519200 1549587600 1571968800 1581037200
+1603418400 1613091600 1634868000 1644541200 1666317600 1675990800
+1698458400 1707440400 1729821600 1738890000 1761271200 1771030800
+1792720800 1802394000 1824170400 1833843600 1856311200 1865293200
+1887674400 1896742800 1919124000 1928192400 1950573600 1960333200
+1982023200 1991696400 2013472800 2023146000 2045613600 2054595600
+2076976800 2086045200 2108426400 2118186000 2139876000
--- /dev/null
+++ b/locale/CET
@@ -1,0 +1,19 @@
+CET 3600 CET 7200
+ 512532000  528256800  543981600  559706400  575431200  591156000
+ 606880800  622605600  638330400  654660000  670384800  686109600
+ 701834400  717559200  733284000  749008800  764733600  780458400
+ 796183200  811908000  828237600  846381600  859687200  877831200
+ 891136800  909280800  922586400  941335200  954036000  972784800
+ 985485600 1004234400 1017540000 1035684000 1048989600 1067133600
+1080439200 1099188000 1111888800 1130637600 1143338400 1162087200
+1174788000 1193536800 1206842400 1224986400 1238292000 1256436000
+1269741600 1288490400 1301191200 1319940000 1332640800 1351389600
+1364695200 1382839200 1396144800 1414288800 1427594400 1445738400
+1459044000 1477792800 1490493600 1509242400 1521943200 1540692000
+1553997600 1572141600 1585447200 1603591200 1616896800 1635645600
+1648346400 1667095200 1679796000 1698544800 1711850400 1729994400
+1743300000 1761444000 1774749600 1792893600 1806199200 1824948000
+1837648800 1856397600 1869098400 1887847200 1901152800 1919296800
+1932602400 1950746400 1964052000 1982800800 1995501600 2014250400
+2026951200 2045700000 2058400800 2077149600 2090455200 2108599200
+2121904800 2140048800
--- /dev/null
+++ b/locale/CST.CDT
@@ -1,0 +1,24 @@
+CST -21600 CDT -18000
+   9943200   25664400   41392800   57718800   73447200   89168400
+ 104896800  120618000  126669600  152067600  162352800  183517200
+ 199245600  215571600  230695200  247021200  262749600  278470800
+ 294199200  309920400  325648800  341370000  357098400  372819600
+ 388548000  404874000  419997600  436323600  452052000  467773200
+ 483501600  499222800  514951200  530672400  544586400  562122000
+ 576036000  594176400  607485600  625626000  638935200  657075600
+ 670989600  688525200  702439200  719974800  733888800  752029200
+ 765338400  783478800  796788000  814928400  828842400  846378000
+ 860292000  877827600  891741600  909277200  923191200  941331600
+ 954640800  972781200  986090400 1004230800 1018144800 1035680400
+1049594400 1067130000 1081044000 1099184400 1112493600 1130634000
+1143943200 1162083600 1175392800 1193533200 1207447200 1224982800
+1238896800 1256432400 1270346400 1288486800 1301796000 1319936400
+1333245600 1351386000 1365300000 1382835600 1396749600 1414285200
+1428199200 1445734800 1459648800 1477789200 1491098400 1509238800
+1522548000 1540688400 1554602400 1572138000 1586052000 1603587600
+1617501600 1635642000 1648951200 1667091600 1680400800 1698541200
+1712455200 1729990800 1743904800 1761440400 1775354400 1792890000
+1806804000 1824944400 1838253600 1856394000 1869703200 1887843600
+1901757600 1919293200 1933207200 1950742800 1964656800 1982797200
+1996106400 2014246800 2027556000 2045696400 2059005600 2077146000
+2091060000 2108595600 2122509600 2140045200
--- /dev/null
+++ b/locale/Canada_Atlantic
@@ -1,0 +1,24 @@
+AST -14400 ADT -10800
+   9943200   25664400   41392800   57718800   73447200   89168400
+ 104896800  120618000  136346400  152067600  167796000  183517200
+ 199245600  215571600  230695200  247021200  262749600  278470800
+ 294199200  309920400  325648800  341370000  357098400  372819600
+ 388548000  404874000  419997600  436323600  452052000  467773200
+ 483501600  499222800  514951200  530672400  544586400  562122000
+ 576036000  594176400  607485600  625626000  638935200  657075600
+ 670989600  688525200  702439200  719974800  733888800  752029200
+ 765338400  783478800  796788000  814928400  828842400  846378000
+ 860292000  877827600  891741600  909277200  923191200  941331600
+ 954640800  972781200  986090400 1004230800 1018144800 1035680400
+1049594400 1067130000 1081044000 1099184400 1112493600 1130634000
+1143943200 1162083600 1175392800 1193533200 1207447200 1224982800
+1238896800 1256432400 1270346400 1288486800 1301796000 1319936400
+1333245600 1351386000 1365300000 1382835600 1396749600 1414285200
+1428199200 1445734800 1459648800 1477789200 1491098400 1509238800
+1522548000 1540688400 1554602400 1572138000 1586052000 1603587600
+1617501600 1635642000 1648951200 1667091600 1680400800 1698541200
+1712455200 1729990800 1743904800 1761440400 1775354400 1792890000
+1806804000 1824944400 1838253600 1856394000 1869703200 1887843600
+1901757600 1919293200 1933207200 1950742800 1964656800 1982797200
+1996106400 2014246800 2027556000 2045696400 2059005600 2077146000
+2091060000 2108595600 2122509600 2140045200
--- /dev/null
+++ b/locale/Canada_Central
@@ -1,0 +1,24 @@
+CST -21600 CDT -18000
+   9943200   25664400   41392800   57718800   73447200   89168400
+ 104896800  120618000  136346400  152067600  167796000  183517200
+ 199245600  215571600  230695200  247021200  262749600  278470800
+ 294199200  309920400  325648800  341370000  357098400  372819600
+ 388548000  404874000  419997600  436323600  452052000  467773200
+ 483501600  499222800  514951200  530672400  544586400  562122000
+ 576036000  594176400  607485600  625626000  638935200  657075600
+ 670989600  688525200  702439200  719974800  733888800  752029200
+ 765338400  783478800  796788000  814928400  828842400  846378000
+ 860292000  877827600  891741600  909277200  923191200  941331600
+ 954640800  972781200  986090400 1004230800 1018144800 1035680400
+1049594400 1067130000 1081044000 1099184400 1112493600 1130634000
+1143943200 1162083600 1175392800 1193533200 1207447200 1224982800
+1238896800 1256432400 1270346400 1288486800 1301796000 1319936400
+1333245600 1351386000 1365300000 1382835600 1396749600 1414285200
+1428199200 1445734800 1459648800 1477789200 1491098400 1509238800
+1522548000 1540688400 1554602400 1572138000 1586052000 1603587600
+1617501600 1635642000 1648951200 1667091600 1680400800 1698541200
+1712455200 1729990800 1743904800 1761440400 1775354400 1792890000
+1806804000 1824944400 1838253600 1856394000 1869703200 1887843600
+1901757600 1919293200 1933207200 1950742800 1964656800 1982797200
+1996106400 2014246800 2027556000 2045696400 2059005600 2077146000
+2091060000 2108595600 2122509600 2140045200
--- /dev/null
+++ b/locale/Canada_East-Saskatchewan
@@ -1,0 +1,1 @@
+CST -21600 CST -21600
--- /dev/null
+++ b/locale/Canada_Eastern
@@ -1,0 +1,24 @@
+EST -18000 EDT -14400
+   9943200   25664400   41392800   57718800   73447200   89168400
+ 104896800  120618000  136346400  152067600  167796000  183517200
+ 199245600  215571600  230695200  247021200  262749600  278470800
+ 294199200  309920400  325648800  341370000  357098400  372819600
+ 388548000  404874000  419997600  436323600  452052000  467773200
+ 483501600  499222800  514951200  530672400  544586400  562122000
+ 576036000  594176400  607485600  625626000  638935200  657075600
+ 670989600  688525200  702439200  719974800  733888800  752029200
+ 765338400  783478800  796788000  814928400  828842400  846378000
+ 860292000  877827600  891741600  909277200  923191200  941331600
+ 954640800  972781200  986090400 1004230800 1018144800 1035680400
+1049594400 1067130000 1081044000 1099184400 1112493600 1130634000
+1143943200 1162083600 1175392800 1193533200 1207447200 1224982800
+1238896800 1256432400 1270346400 1288486800 1301796000 1319936400
+1333245600 1351386000 1365300000 1382835600 1396749600 1414285200
+1428199200 1445734800 1459648800 1477789200 1491098400 1509238800
+1522548000 1540688400 1554602400 1572138000 1586052000 1603587600
+1617501600 1635642000 1648951200 1667091600 1680400800 1698541200
+1712455200 1729990800 1743904800 1761440400 1775354400 1792890000
+1806804000 1824944400 1838253600 1856394000 1869703200 1887843600
+1901757600 1919293200 1933207200 1950742800 1964656800 1982797200
+1996106400 2014246800 2027556000 2045696400 2059005600 2077146000
+2091060000 2108595600 2122509600 2140045200
--- /dev/null
+++ b/locale/Canada_Mountain
@@ -1,0 +1,24 @@
+MST -25200 MDT -21600
+   9943200   25664400   41392800   57718800   73447200   89168400
+ 104896800  120618000  136346400  152067600  167796000  183517200
+ 199245600  215571600  230695200  247021200  262749600  278470800
+ 294199200  309920400  325648800  341370000  357098400  372819600
+ 388548000  404874000  419997600  436323600  452052000  467773200
+ 483501600  499222800  514951200  530672400  544586400  562122000
+ 576036000  594176400  607485600  625626000  638935200  657075600
+ 670989600  688525200  702439200  719974800  733888800  752029200
+ 765338400  783478800  796788000  814928400  828842400  846378000
+ 860292000  877827600  891741600  909277200  923191200  941331600
+ 954640800  972781200  986090400 1004230800 1018144800 1035680400
+1049594400 1067130000 1081044000 1099184400 1112493600 1130634000
+1143943200 1162083600 1175392800 1193533200 1207447200 1224982800
+1238896800 1256432400 1270346400 1288486800 1301796000 1319936400
+1333245600 1351386000 1365300000 1382835600 1396749600 1414285200
+1428199200 1445734800 1459648800 1477789200 1491098400 1509238800
+1522548000 1540688400 1554602400 1572138000 1586052000 1603587600
+1617501600 1635642000 1648951200 1667091600 1680400800 1698541200
+1712455200 1729990800 1743904800 1761440400 1775354400 1792890000
+1806804000 1824944400 1838253600 1856394000 1869703200 1887843600
+1901757600 1919293200 1933207200 1950742800 1964656800 1982797200
+1996106400 2014246800 2027556000 2045696400 2059005600 2077146000
+2091060000 2108595600 2122509600 2140045200
--- /dev/null
+++ b/locale/Canada_Newfoundland
@@ -1,0 +1,24 @@
+NST -12600 NDT -9000
+   9943200   25664400   41392800   57718800   73447200   89168400
+ 104896800  120618000  136346400  152067600  167796000  183517200
+ 199245600  215571600  230695200  247021200  262749600  278470800
+ 294199200  309920400  325648800  341370000  357098400  372819600
+ 388548000  404874000  419997600  436323600  452052000  467773200
+ 483501600  499222800  514951200  530672400  544586400  562122000
+ 576036000  594176400  607485600  625626000  638935200  657075600
+ 670989600  688525200  702439200  719974800  733888800  752029200
+ 765338400  783478800  796788000  814928400  828842400  846378000
+ 860292000  877827600  891741600  909277200  923191200  941331600
+ 954640800  972781200  986090400 1004230800 1018144800 1035680400
+1049594400 1067130000 1081044000 1099184400 1112493600 1130634000
+1143943200 1162083600 1175392800 1193533200 1207447200 1224982800
+1238896800 1256432400 1270346400 1288486800 1301796000 1319936400
+1333245600 1351386000 1365300000 1382835600 1396749600 1414285200
+1428199200 1445734800 1459648800 1477789200 1491098400 1509238800
+1522548000 1540688400 1554602400 1572138000 1586052000 1603587600
+1617501600 1635642000 1648951200 1667091600 1680400800 1698541200
+1712455200 1729990800 1743904800 1761440400 1775354400 1792890000
+1806804000 1824944400 1838253600 1856394000 1869703200 1887843600
+1901757600 1919293200 1933207200 1950742800 1964656800 1982797200
+1996106400 2014246800 2027556000 2045696400 2059005600 2077146000
+2091060000 2108595600 2122509600 2140045200
--- /dev/null
+++ b/locale/Canada_Pacific
@@ -1,0 +1,24 @@
+PST -28800 PDT -25200
+   9943200   25664400   41392800   57718800   73447200   89168400
+ 104896800  120618000  136346400  152067600  167796000  183517200
+ 199245600  215571600  230695200  247021200  262749600  278470800
+ 294199200  309920400  325648800  341370000  357098400  372819600
+ 388548000  404874000  419997600  436323600  452052000  467773200
+ 483501600  499222800  514951200  530672400  544586400  562122000
+ 576036000  594176400  607485600  625626000  638935200  657075600
+ 670989600  688525200  702439200  719974800  733888800  752029200
+ 765338400  783478800  796788000  814928400  828842400  846378000
+ 860292000  877827600  891741600  909277200  923191200  941331600
+ 954640800  972781200  986090400 1004230800 1018144800 1035680400
+1049594400 1067130000 1081044000 1099184400 1112493600 1130634000
+1143943200 1162083600 1175392800 1193533200 1207447200 1224982800
+1238896800 1256432400 1270346400 1288486800 1301796000 1319936400
+1333245600 1351386000 1365300000 1382835600 1396749600 1414285200
+1428199200 1445734800 1459648800 1477789200 1491098400 1509238800
+1522548000 1540688400 1554602400 1572138000 1586052000 1603587600
+1617501600 1635642000 1648951200 1667091600 1680400800 1698541200
+1712455200 1729990800 1743904800 1761440400 1775354400 1792890000
+1806804000 1824944400 1838253600 1856394000 1869703200 1887843600
+1901757600 1919293200 1933207200 1950742800 1964656800 1982797200
+1996106400 2014246800 2027556000 2045696400 2059005600 2077146000
+2091060000 2108595600 2122509600 2140045200
--- /dev/null
+++ b/locale/Canada_Yukon
@@ -1,0 +1,24 @@
+YST -32400 YDT -28800
+   9943200   25664400   41392800   57718800   73447200   89168400
+ 104896800  120618000  136346400  152067600  167796000  183517200
+ 199245600  215571600  230695200  247021200  262749600  278470800
+ 294199200  309920400  325648800  341370000  357098400  372819600
+ 388548000  404874000  419997600  436323600  452052000  467773200
+ 483501600  499222800  514951200  530672400  544586400  562122000
+ 576036000  594176400  607485600  625626000  638935200  657075600
+ 670989600  688525200  702439200  719974800  733888800  752029200
+ 765338400  783478800  796788000  814928400  828842400  846378000
+ 860292000  877827600  891741600  909277200  923191200  941331600
+ 954640800  972781200  986090400 1004230800 1018144800 1035680400
+1049594400 1067130000 1081044000 1099184400 1112493600 1130634000
+1143943200 1162083600 1175392800 1193533200 1207447200 1224982800
+1238896800 1256432400 1270346400 1288486800 1301796000 1319936400
+1333245600 1351386000 1365300000 1382835600 1396749600 1414285200
+1428199200 1445734800 1459648800 1477789200 1491098400 1509238800
+1522548000 1540688400 1554602400 1572138000 1586052000 1603587600
+1617501600 1635642000 1648951200 1667091600 1680400800 1698541200
+1712455200 1729990800 1743904800 1761440400 1775354400 1792890000
+1806804000 1824944400 1838253600 1856394000 1869703200 1887843600
+1901757600 1919293200 1933207200 1950742800 1964656800 1982797200
+1996106400 2014246800 2027556000 2045696400 2059005600 2077146000
+2091060000 2108595600 2122509600 2140045200
--- /dev/null
+++ b/locale/Chile_Continental
@@ -1,0 +1,18 @@
+CST -14400 CDT -10800
+ 560916000  574218000  592365600  605667600  623815200  637117200
+ 655869600  668566800  687319200  700016400  718768800  732070800
+ 750218400  763520400  781668000  794970000  813117600  826419600
+ 845172000  857869200  876621600  889318800  908071200  921373200
+ 939520800  952822800  970970400  984272400 1003024800 1015722000
+1034474400 1047171600 1065924000 1079226000 1097373600 1110675600
+1128823200 1142125200 1160272800 1173574800 1192327200 1205024400
+1223776800 1236474000 1255226400 1268528400 1286676000 1299978000
+1318125600 1331427600 1350180000 1362877200 1381629600 1394326800
+1413079200 1425776400 1444528800 1457830800 1475978400 1489280400
+1507428000 1520730000 1539482400 1552179600 1570932000 1583629200
+1602381600 1615683600 1633831200 1647133200 1665280800 1678582800
+1696730400 1710032400 1728784800 1741482000 1760234400 1772931600
+1791684000 1804986000 1823133600 1836435600 1854583200 1867885200
+1886637600 1899334800 1918087200 1930784400 1949536800 1962838800
+1980986400 1994288400 2012436000 2025738000 2043885600 2057187600
+2075940000 2088637200 2107389600 2120086800 2138839200
--- /dev/null
+++ b/locale/Chile_EasterIsland
@@ -1,0 +1,18 @@
+EST -21600 EDT -18000
+ 560916000  574218000  592365600  605667600  623815200  637117200
+ 655869600  668566800  687319200  700016400  718768800  732070800
+ 750218400  763520400  781668000  794970000  813117600  826419600
+ 845172000  857869200  876621600  889318800  908071200  921373200
+ 939520800  952822800  970970400  984272400 1003024800 1015722000
+1034474400 1047171600 1065924000 1079226000 1097373600 1110675600
+1128823200 1142125200 1160272800 1173574800 1192327200 1205024400
+1223776800 1236474000 1255226400 1268528400 1286676000 1299978000
+1318125600 1331427600 1350180000 1362877200 1381629600 1394326800
+1413079200 1425776400 1444528800 1457830800 1475978400 1489280400
+1507428000 1520730000 1539482400 1552179600 1570932000 1583629200
+1602381600 1615683600 1633831200 1647133200 1665280800 1678582800
+1696730400 1710032400 1728784800 1741482000 1760234400 1772931600
+1791684000 1804986000 1823133600 1836435600 1854583200 1867885200
+1886637600 1899334800 1918087200 1930784400 1949536800 1962838800
+1980986400 1994288400 2012436000 2025738000 2043885600 2057187600
+2075940000 2088637200 2107389600 2120086800 2138839200
--- /dev/null
+++ b/locale/Cuba
@@ -1,0 +1,21 @@
+CST -18000 CDT -14400
+ 290563200  308703600  322012800  340153200  358300800  371602800
+ 389750400  403052400  421200000  434502000  453254400  466556400
+ 484704000  498006000  516153600  529455600  547603200  560905200
+ 579052800  592354800  611107200  623804400  642556800  655858800
+ 674006400  687308400  705456000  718758000  736905600  750207600
+ 768355200  781657200  800409600  813106800  831859200  845161200
+ 863308800  876610800  894758400  908060400  926208000  939510000
+ 958262400  970959600  989712000 1003014000 1021161600 1034463600
+1052611200 1065913200 1084060800 1097362800 1115510400 1128812400
+1147564800 1160262000 1179014400 1192316400 1210464000 1223766000
+1241913600 1255215600 1273363200 1286665200 1304812800 1318114800
+1336867200 1350169200 1368316800 1381618800 1399766400 1413068400
+1431216000 1444518000 1462665600 1475967600 1494720000 1507417200
+1526169600 1539471600 1557619200 1570921200 1589068800 1602370800
+1620518400 1633820400 1651968000 1665270000 1684022400 1696719600
+1715472000 1728774000 1746921600 1760223600 1778371200 1791673200
+1809820800 1823122800 1841875200 1854572400 1873324800 1886626800
+1904774400 1918076400 1936224000 1949526000 1967673600 1980975600
+1999123200 2012425200 2031177600 2043874800 2062627200 2075929200
+2094076800 2107378800 2125526400 2138828400
--- /dev/null
+++ b/locale/EET
@@ -1,0 +1,13 @@
+EET 7200 EEST 10800
+638334000 657082800 670388400 688532400 701838000 719982000
+733287600 752036400 764737200 783486000 796186800 814935600
+828241200 846385200 859690800 877834800 891140400 909284400
+922590000 941338800 954039600 972788400 985489200 1004238000
+1017543600 1035687600 1048993200 1067137200 1080442800 1099191600
+1111892400 1130641200 1143342000 1162090800 1174791600 1193540400
+1206846000 1224990000 1238295600 1256439600 1269745200 1288494000
+1301194800 1319943600 1332644400 1351393200 1364698800 1382842800
+1396148400 1414292400 1427598000 1445742000 1459047600 1477796400
+1490497200 1509246000 1521946800 1540695600 1554001200 1572145200
+1585450800 1603594800 1616900400 1635649200 1648350000 1667098800
+1679799600 1698548400 1711854000 1729998000 1743303600 1761447600
--- /dev/null
+++ b/locale/EST.EDT
@@ -1,0 +1,24 @@
+EST -18000 EDT -14400
+   9943200   25664400   41392800   57718800   73447200   89168400
+ 104896800  120618000  126669600  152067600  162352800  183517200
+ 199245600  215571600  230695200  247021200  262749600  278470800
+ 294199200  309920400  325648800  341370000  357098400  372819600
+ 388548000  404874000  419997600  436323600  452052000  467773200
+ 483501600  499222800  514951200  530672400  544586400  562122000
+ 576036000  594176400  607485600  625626000  638935200  657075600
+ 670989600  688525200  702439200  719974800  733888800  752029200
+ 765338400  783478800  796788000  814928400  828842400  846378000
+ 860292000  877827600  891741600  909277200  923191200  941331600
+ 954640800  972781200  986090400 1004230800 1018144800 1035680400
+1049594400 1067130000 1081044000 1099184400 1112493600 1130634000
+1143943200 1162083600 1175392800 1193533200 1207447200 1224982800
+1238896800 1256432400 1270346400 1288486800 1301796000 1319936400
+1333245600 1351386000 1365300000 1382835600 1396749600 1414285200
+1428199200 1445734800 1459648800 1477789200 1491098400 1509238800
+1522548000 1540688400 1554602400 1572138000 1586052000 1603587600
+1617501600 1635642000 1648951200 1667091600 1680400800 1698541200
+1712455200 1729990800 1743904800 1761440400 1775354400 1792890000
+1806804000 1824944400 1838253600 1856394000 1869703200 1887843600
+1901757600 1919293200 1933207200 1950742800 1964656800 1982797200
+1996106400 2014246800 2027556000 2045696400 2059005600 2077146000
+2091060000 2108595600 2122509600 2140045200
--- /dev/null
+++ b/locale/Egypt
@@ -1,0 +1,23 @@
+EET 7200 EET 10800
+  10375200   23590800   41911200   55126800   73533600   86749200
+ 105069600  118285200  136605600  149821200  168141600  181357200
+ 199764000  212979600  231300000  244515600  262836000  276051600
+ 294372000  307587600  325994400  339210000  420602400  433818000
+ 452224800  465440400  483760800  496976400  515296800  528512400
+ 546832800  560048400  578455200  591670800  609991200  623206800
+ 641527200  654742800  673063200  686278800  704685600  717901200
+ 736221600  749437200  767757600  780973200  799293600  812509200
+ 830916000  844131600  862452000  875667600  893988000  907203600
+ 925524000  938739600  957146400  970362000  988682400 1001898000
+1020218400 1033434000 1051754400 1064970000 1083376800 1096592400
+1114912800 1128128400 1146448800 1159664400 1177984800 1191200400
+1209607200 1222822800 1241143200 1254358800 1272679200 1285894800
+1304215200 1317430800 1335837600 1349053200 1367373600 1380589200
+1398909600 1412125200 1430445600 1443661200 1462068000 1475283600
+1493604000 1506819600 1525140000 1538355600 1556676000 1569891600
+1588298400 1601514000 1619834400 1633050000 1651370400 1664586000
+1682906400 1696122000 1714528800 1727744400 1746064800 1759280400
+1777600800 1790816400 1809136800 1822352400 1840759200 1853974800
+1872295200 1885510800 1903831200 1917046800 1935367200 1948582800
+1966989600 1980205200 1998525600 2011741200 2030061600 2043277200
+2061597600 2074813200 2093220000 2106435600 2124756000 2137971600
--- /dev/null
+++ b/locale/GB-Eire
@@ -1,0 +1,19 @@
+GMT 0 BST 3600
+ 512528400  530672400  543978000  562122000  575427600  593571600
+ 606877200  625626000  638326800  657075600  670381200  688525200
+ 701830800  719974800  733280400  751424400  764730000  782874000
+ 796179600  814928400  828234000  846378000  859683600  877827600
+ 891133200  909277200  922582800  940726800  954032400  972781200
+ 985482000 1004230800 1017536400 1035680400 1048986000 1067130000
+1080435600 1099184400 1111885200 1130634000 1143334800 1162083600
+1174784400 1193533200 1206838800 1224982800 1238288400 1256432400
+1269738000 1288486800 1301187600 1319936400 1332637200 1351386000
+1364691600 1382835600 1396141200 1414285200 1427590800 1445734800
+1459040400 1477789200 1490490000 1509238800 1521939600 1540688400
+1553994000 1572138000 1585443600 1603587600 1616893200 1635642000
+1648342800 1667091600 1679792400 1698541200 1711846800 1729990800
+1743296400 1761440400 1774746000 1792890000 1806195600 1824944400
+1837645200 1856394000 1869094800 1887843600 1901149200 1919293200
+1932598800 1950742800 1964048400 1982797200 1995498000 2014246800
+2026947600 2045696400 2058397200 2077146000 2090451600 2108595600
+2121901200 2140045200 2147483647
--- /dev/null
+++ b/locale/GMT
@@ -1,0 +1,1 @@
+GMT 0
--- /dev/null
+++ b/locale/HST
@@ -1,0 +1,1 @@
+HST -36000 HST -36000
--- /dev/null
+++ b/locale/Hongkong
@@ -1,0 +1,1 @@
+HKT 28800 HKT 28800
--- /dev/null
+++ b/locale/Iceland
@@ -1,0 +1,1 @@
+WET 0 WET 0
--- /dev/null
+++ b/locale/Iran
@@ -1,0 +1,18 @@
+IST 12600 IDT 16200
+ 575431200  590547600  606880800  621997200  638330400  653446800
+ 670384800  684896400  701834400  716950800  733284000  748400400
+ 764733600  779850000  796183200  811299600  828237600  842749200
+ 859687200  874803600  891136800  906253200  922586400  937702800
+ 954036000  969152400  985485600 1000602000 1017540000 1032051600
+1048989600 1064106000 1080439200 1095555600 1111888800 1127005200
+1143338400 1158454800 1174788000 1189904400 1206842400 1221958800
+1238292000 1253408400 1269741600 1284858000 1301191200 1316307600
+1332640800 1347757200 1364695200 1379206800 1396144800 1411261200
+1427594400 1442710800 1459044000 1474160400 1490493600 1505610000
+1521943200 1537059600 1553997600 1568509200 1585447200 1600563600
+1616896800 1632013200 1648346400 1663462800 1679796000 1694912400
+1711850400 1726362000 1743300000 1758416400 1774749600 1789866000
+1806199200 1821315600 1837648800 1852765200 1869098400 1884214800
+1901152800 1915664400 1932602400 1947718800 1964052000 1979168400
+1995501600 2010618000 2026951200 2042067600 2058400800 2073517200
+2090455200 2105571600 2121904800 2137021200
--- /dev/null
+++ b/locale/Israel
@@ -1,0 +1,4 @@
+IST 7200 IDT 10800
+609890400 620776800 638316000 651621600 669765600 683676000
+701820000 715730400 733701600 747180000 765151200 778024800
+796600800 810079200
--- /dev/null
+++ b/locale/Jamaica
@@ -1,0 +1,24 @@
+EST -18000 EDT -14400
+   9943200   25664400   41392800   57718800   73447200   89168400
+ 104896800  120618000  126669600  152067600  162352800  183517200
+ 199245600  215571600  230695200  247021200  262749600  278470800
+ 294199200  309920400  325648800  341370000  357098400  372819600
+ 388548000  404874000  419997600  436323600  452052000  467773200
+ 483501600  499222800  514951200  530672400  544586400  562122000
+ 576036000  594176400  607485600  625626000  638935200  657075600
+ 670989600  688525200  702439200  719974800  733888800  752029200
+ 765338400  783478800  796788000  814928400  828842400  846378000
+ 860292000  877827600  891741600  909277200  923191200  941331600
+ 954640800  972781200  986090400 1004230800 1018144800 1035680400
+1049594400 1067130000 1081044000 1099184400 1112493600 1130634000
+1143943200 1162083600 1175392800 1193533200 1207447200 1224982800
+1238896800 1256432400 1270346400 1288486800 1301796000 1319936400
+1333245600 1351386000 1365300000 1382835600 1396749600 1414285200
+1428199200 1445734800 1459648800 1477789200 1491098400 1509238800
+1522548000 1540688400 1554602400 1572138000 1586052000 1603587600
+1617501600 1635642000 1648951200 1667091600 1680400800 1698541200
+1712455200 1729990800 1743904800 1761440400 1775354400 1792890000
+1806804000 1824944400 1838253600 1856394000 1869703200 1887843600
+1901757600 1919293200 1933207200 1950742800 1964656800 1982797200
+1996106400 2014246800 2027556000 2045696400 2059005600 2077146000
+2091060000 2108595600 2122509600 2140045200
--- /dev/null
+++ b/locale/Japan
@@ -1,0 +1,1 @@
+JST 32400 JST 32400
--- /dev/null
+++ b/locale/Libya
@@ -1,0 +1,20 @@
+EET 7200 EET 10800
+ 386474400  402195600  418010400  433731600  449632800  465354000
+ 481168800  496890000  512704800  528426000  544240800  559962000
+ 575863200  591584400  607399200  623120400  638935200  654656400
+ 670471200  686192400  702093600  717814800  733629600  749350800
+ 765165600  780886800  796701600  812422800  828324000  844045200
+ 859860000  875581200  891396000  907117200  922932000  938653200
+ 954554400  970275600  986090400 1001811600 1017626400 1033347600
+1049162400 1064883600 1080784800 1096506000 1112320800 1128042000
+1143856800 1159578000 1175392800 1191114000 1207015200 1222736400
+1238551200 1254272400 1270087200 1285808400 1301623200 1317344400
+1333245600 1348966800 1364781600 1380502800 1396317600 1412038800
+1427853600 1443574800 1459476000 1475197200 1491012000 1506733200
+1522548000 1538269200 1554084000 1569805200 1585706400 1601427600
+1617242400 1632963600 1648778400 1664499600 1680314400 1696035600
+1711936800 1727658000 1743472800 1759194000 1775008800 1790730000
+1806544800 1822266000 1838167200 1853888400 1869703200 1885424400
+1901239200 1916960400 1932775200 1948496400 1964397600 1980118800
+1995933600 2011654800 2027469600 2043190800 2059005600 2074726800
+2090628000 2106349200 2122164000 2137885200
--- /dev/null
+++ b/locale/MET
@@ -1,0 +1,19 @@
+MET 3600 MDT 7200
+ 512532000  528256800  543981600  559706400  575431200  591156000
+ 606880800  622605600  638330400  654660000  670384800  686109600
+ 701834400  717559200  733284000  749008800  764733600  780458400
+ 796183200  811908000  828237600  846381600  859687200  877831200
+ 891136800  909280800  922586400  941335200  954036000  972784800
+ 985485600 1004234400 1017540000 1035684000 1048989600 1067133600
+1080439200 1099188000 1111888800 1130637600 1143338400 1162087200
+1174788000 1193536800 1206842400 1224986400 1238292000 1256436000
+1269741600 1288490400 1301191200 1319940000 1332640800 1351389600
+1364695200 1382839200 1396144800 1414288800 1427594400 1445738400
+1459044000 1477792800 1490493600 1509242400 1521943200 1540692000
+1553997600 1572141600 1585447200 1603591200 1616896800 1635645600
+1648346400 1667095200 1679796000 1698544800 1711850400 1729994400
+1743300000 1761444000 1774749600 1792893600 1806199200 1824948000
+1837648800 1856397600 1869098400 1887847200 1901152800 1919296800
+1932602400 1950746400 1964052000 1982800800 1995501600 2014250400
+2026951200 2045700000 2058400800 2077149600 2090455200 2108599200
+2121904800 2140048800
--- /dev/null
+++ b/locale/MST.MDT
@@ -1,0 +1,24 @@
+MST -25200 MDT -21600
+   9943200   25664400   41392800   57718800   73447200   89168400
+ 104896800  120618000  126669600  152067600  162352800  183517200
+ 199245600  215571600  230695200  247021200  262749600  278470800
+ 294199200  309920400  325648800  341370000  357098400  372819600
+ 388548000  404874000  419997600  436323600  452052000  467773200
+ 483501600  499222800  514951200  530672400  544586400  562122000
+ 576036000  594176400  607485600  625626000  638935200  657075600
+ 670989600  688525200  702439200  719974800  733888800  752029200
+ 765338400  783478800  796788000  814928400  828842400  846378000
+ 860292000  877827600  891741600  909277200  923191200  941331600
+ 954640800  972781200  986090400 1004230800 1018144800 1035680400
+1049594400 1067130000 1081044000 1099184400 1112493600 1130634000
+1143943200 1162083600 1175392800 1193533200 1207447200 1224982800
+1238896800 1256432400 1270346400 1288486800 1301796000 1319936400
+1333245600 1351386000 1365300000 1382835600 1396749600 1414285200
+1428199200 1445734800 1459648800 1477789200 1491098400 1509238800
+1522548000 1540688400 1554602400 1572138000 1586052000 1603587600
+1617501600 1635642000 1648951200 1667091600 1680400800 1698541200
+1712455200 1729990800 1743904800 1761440400 1775354400 1792890000
+1806804000 1824944400 1838253600 1856394000 1869703200 1887843600
+1901757600 1919293200 1933207200 1950742800 1964656800 1982797200
+1996106400 2014246800 2027556000 2045696400 2059005600 2077146000
+2091060000 2108595600 2122509600 2140045200
--- /dev/null
+++ b/locale/Mexico_BajaNorte
@@ -1,0 +1,18 @@
+PST -28800 PDT -25200
+ 544586400  562122000  576036000  594176400  607485600  625626000
+ 638935200  657075600  670989600  688525200  702439200  719974800
+ 733888800  752029200  765338400  783478800  796788000  814928400
+ 828842400  846378000  860292000  877827600  891741600  909277200
+ 923191200  941331600  954640800  972781200  986090400 1004230800
+1018144800 1035680400 1049594400 1067130000 1081044000 1099184400
+1112493600 1130634000 1143943200 1162083600 1175392800 1193533200
+1207447200 1224982800 1238896800 1256432400 1270346400 1288486800
+1301796000 1319936400 1333245600 1351386000 1365300000 1382835600
+1396749600 1414285200 1428199200 1445734800 1459648800 1477789200
+1491098400 1509238800 1522548000 1540688400 1554602400 1572138000
+1586052000 1603587600 1617501600 1635642000 1648951200 1667091600
+1680400800 1698541200 1712455200 1729990800 1743904800 1761440400
+1775354400 1792890000 1806804000 1824944400 1838253600 1856394000
+1869703200 1887843600 1901757600 1919293200 1933207200 1950742800
+1964656800 1982797200 1996106400 2014246800 2027556000 2045696400
+2059005600 2077146000 2091060000 2108595600 2122509600 2140045200
--- /dev/null
+++ b/locale/Mexico_BajaSur
@@ -1,0 +1,1 @@
+MST -25200 MST -25200
--- /dev/null
+++ b/locale/Mexico_General
@@ -1,0 +1,1 @@
+CST -21600 CST -21600
--- /dev/null
+++ b/locale/Moscow
@@ -1,0 +1,12 @@
+MSK 10800 MSD 14400
+638330400 657079200 670384800 688528800 701834400 719978400
+733284000 752032800 764733600 783482400 796183200 814932000
+828237600 846381600 859687200 877831200 891136800 909280800
+922586400 941335200 954036000 972784800 985485600 1004234400
+1017540000 1035684000 1048989600 1067133600 1080439200 1099188000
+1111888800 1130637600 1143338400 1162087200 1174788000 1193536800
+1206842400 1224986400 1238292000 1256436000 1269741600 1288490400
+1301191200 1319940000 1332640800 1351389600 1364695200 1382839200
+1396144800 1414288800 1427594400 1445738400 1459044000 1477792800
+1490493600 1509242400 1521943200 1540692000 1553997600 1572141600
+1585447200 1603591200 1616896800 1635645600 1648346400 
--- /dev/null
+++ b/locale/NOTICE
@@ -1,0 +1,29 @@
+This copyright NOTICE applies to all files in this directory and
+subdirectories, unless another copyright notice appears in a given
+file or subdirectory.  If you take substantial code from this software to use in
+other programs, you must somehow include with it an appropriate
+copyright notice that includes the copyright notice and the other
+notices below.  It is fine (and often tidier) to do that in a separate
+file such as NOTICE, LICENCE or COPYING.
+
+	Copyright © 1994-1999 Lucent Technologies Inc.  All rights reserved.
+	Portions Copyright © 2000-2007 Vita Nuova Holdings Limited (www.vitanuova.com)
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in
+all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+THE SOFTWARE.
+
--- /dev/null
+++ b/locale/NZ
@@ -1,0 +1,23 @@
+NZS 43200 NZD 46800
+ 152071200  162957600  183520800  195012000  215575200  226461600
+ 247024800  257911200  278474400  289360800  309924000  320810400
+ 341373600  352260000  372823200  384314400  404877600  415764000
+ 436327200  447213600  467776800  478663200  499226400  510112800
+ 530676000  541562400  562125600  573616800  594180000  605066400
+ 623815200  637725600  655264800  669175200  686714400  700624800
+ 718164000  732679200  749613600  764128800  781063200  795578400
+ 812512800  827028000  844567200  858477600  876016800  889927200
+ 907466400  921981600  938916000  953431200  970365600  984880800
+1002420000 1016330400 1033869600 1047780000 1065319200 1079834400
+1096768800 1111284000 1128218400 1142733600 1159668000 1174183200
+1191117600 1207447200 1222567200 1238896800 1254016800 1270346400
+1285466400 1301796000 1316916000 1333245600 1348970400 1365300000
+1380420000 1396749600 1411869600 1428199200 1443319200 1459648800
+1474768800 1491098400 1506823200 1522548000 1538272800 1554602400
+1569722400 1586052000 1601172000 1617501600 1632621600 1648951200
+1664071200 1680400800 1696125600 1712455200 1727575200 1743904800
+1759024800 1775354400 1790474400 1806804000 1821924000 1838253600
+1853978400 1869703200 1885428000 1901757600 1916877600 1933207200
+1948327200 1964656800 1979776800 1996106400 2011226400 2027556000
+2043280800 2059005600 2074730400 2091060000 2106180000 2122509600
+2137629600
--- /dev/null
+++ b/locale/NZ_CHAT
@@ -1,0 +1,1 @@
+NZ- 45900 NZ- 45900
--- /dev/null
+++ b/locale/Navajo
@@ -1,0 +1,24 @@
+MST -25200 MDT -21600
+   9943200   25664400   41392800   57718800   73447200   89168400
+ 104896800  120618000  126669600  152067600  162352800  183517200
+ 199245600  215571600  230695200  247021200  262749600  278470800
+ 294199200  309920400  325648800  341370000  357098400  372819600
+ 388548000  404874000  419997600  436323600  452052000  467773200
+ 483501600  499222800  514951200  530672400  544586400  562122000
+ 576036000  594176400  607485600  625626000  638935200  657075600
+ 670989600  688525200  702439200  719974800  733888800  752029200
+ 765338400  783478800  796788000  814928400  828842400  846378000
+ 860292000  877827600  891741600  909277200  923191200  941331600
+ 954640800  972781200  986090400 1004230800 1018144800 1035680400
+1049594400 1067130000 1081044000 1099184400 1112493600 1130634000
+1143943200 1162083600 1175392800 1193533200 1207447200 1224982800
+1238896800 1256432400 1270346400 1288486800 1301796000 1319936400
+1333245600 1351386000 1365300000 1382835600 1396749600 1414285200
+1428199200 1445734800 1459648800 1477789200 1491098400 1509238800
+1522548000 1540688400 1554602400 1572138000 1586052000 1603587600
+1617501600 1635642000 1648951200 1667091600 1680400800 1698541200
+1712455200 1729990800 1743904800 1761440400 1775354400 1792890000
+1806804000 1824944400 1838253600 1856394000 1869703200 1887843600
+1901757600 1919293200 1933207200 1950742800 1964656800 1982797200
+1996106400 2014246800 2027556000 2045696400 2059005600 2077146000
+2091060000 2108595600 2122509600 2140045200
--- /dev/null
+++ b/locale/PRC
@@ -1,0 +1,24 @@
+CST 28800 CDT 32400
+   8733600   22039200   40183200   53488800   71632800   84938400
+ 103082400  116388000  135136800  147837600  166586400  179892000
+ 198036000  211341600  229485600  242791200  260935200  274240800
+ 292384800  305690400  324439200  337744800  355888800  369194400
+ 387338400  400644000  418788000  432093600  450237600  463543200
+ 482292000  494992800  513741600  527047200  545191200  558496800
+ 576640800  589946400  608090400  621396000  639540000  652845600
+ 671594400  684295200  703044000  716349600  734493600  747799200
+ 765943200  779248800  797392800  810698400  829447200  842148000
+ 860896800  874202400  892346400  905652000  923796000  937101600
+ 955245600  968551200  986695200 1000000800 1018749600 1031450400
+1050199200 1063504800 1081648800 1094954400 1113098400 1126404000
+1144548000 1157853600 1175997600 1189303200 1208052000 1221357600
+1239501600 1252807200 1270951200 1284256800 1302400800 1315706400
+1333850400 1347156000 1365904800 1378605600 1397354400 1410660000
+1428804000 1442109600 1460253600 1473559200 1491703200 1505008800
+1523152800 1536458400 1555207200 1567908000 1586656800 1599962400
+1618106400 1631412000 1649556000 1662861600 1681005600 1694311200
+1713060000 1725760800 1744509600 1757815200 1775959200 1789264800
+1807408800 1820714400 1838858400 1852164000 1870308000 1883613600
+1902362400 1915063200 1933812000 1947117600 1965261600 1978567200
+1996711200 2010016800 2028160800 2041466400 2059610400 2072916000
+2091664800 2104970400 2123114400 2136420000
--- /dev/null
+++ b/locale/PST.PDT
@@ -1,0 +1,24 @@
+PST -28800 PDT -25200
+   9943200   25664400   41392800   57718800   73447200   89168400
+ 104896800  120618000  126669600  152067600  162352800  183517200
+ 199245600  215571600  230695200  247021200  262749600  278470800
+ 294199200  309920400  325648800  341370000  357098400  372819600
+ 388548000  404874000  419997600  436323600  452052000  467773200
+ 483501600  499222800  514951200  530672400  544586400  562122000
+ 576036000  594176400  607485600  625626000  638935200  657075600
+ 670989600  688525200  702439200  719974800  733888800  752029200
+ 765338400  783478800  796788000  814928400  828842400  846378000
+ 860292000  877827600  891741600  909277200  923191200  941331600
+ 954640800  972781200  986090400 1004230800 1018144800 1035680400
+1049594400 1067130000 1081044000 1099184400 1112493600 1130634000
+1143943200 1162083600 1175392800 1193533200 1207447200 1224982800
+1238896800 1256432400 1270346400 1288486800 1301796000 1319936400
+1333245600 1351386000 1365300000 1382835600 1396749600 1414285200
+1428199200 1445734800 1459648800 1477789200 1491098400 1509238800
+1522548000 1540688400 1554602400 1572138000 1586052000 1603587600
+1617501600 1635642000 1648951200 1667091600 1680400800 1698541200
+1712455200 1729990800 1743904800 1761440400 1775354400 1792890000
+1806804000 1824944400 1838253600 1856394000 1869703200 1887843600
+1901757600 1919293200 1933207200 1950742800 1964656800 1982797200
+1996106400 2014246800 2027556000 2045696400 2059005600 2077146000
+2091060000 2108595600 2122509600 2140045200
--- /dev/null
+++ b/locale/Poland
@@ -1,0 +1,19 @@
+MET 3600 MET 7200
+ 512528400  528253200  543978000  559702800  575427600  591152400
+ 606877200  622602000  638326800  654656400  670381200  686106000
+ 701830800  717555600  733280400  749005200  764730000  780454800
+ 796179600  811904400  828234000  843958800  859683600  875408400
+ 891133200  906858000  922582800  938307600  954032400  969757200
+ 985482000 1001811600 1017536400 1033261200 1048986000 1064710800
+1080435600 1096160400 1111885200 1127610000 1143334800 1159059600
+1174784400 1191114000 1206838800 1222563600 1238288400 1254013200
+1269738000 1285462800 1301187600 1316912400 1332637200 1348966800
+1364691600 1380416400 1396141200 1411866000 1427590800 1443315600
+1459040400 1474765200 1490490000 1506214800 1521939600 1538269200
+1553994000 1569718800 1585443600 1601168400 1616893200 1632618000
+1648342800 1664067600 1679792400 1695517200 1711846800 1727571600
+1743296400 1759021200 1774746000 1790470800 1806195600 1821920400
+1837645200 1853370000 1869094800 1885424400 1901149200 1916874000
+1932598800 1948323600 1964048400 1979773200 1995498000 2011222800
+2026947600 2042672400 2058397200 2074726800 2090451600 2106176400
+2121901200 2137626000
--- /dev/null
+++ b/locale/README
@@ -1,0 +1,5 @@
+To change the default time zone you see on your local system,
+copy the appropriate file to /locale/timezone.
+
+Most of these files were provided by David Hogan.
+The Israel file was provided by Amos Shapir.
--- /dev/null
+++ b/locale/ROC
@@ -1,0 +1,1 @@
+CST 28800 CST 28800
--- /dev/null
+++ b/locale/ROK
@@ -1,0 +1,2 @@
+KST 32400 KDT 36000
+ 547610400  560916000  579060000  592365600
--- /dev/null
+++ b/locale/Singapore
@@ -1,0 +1,1 @@
+SST 28800 SST 28800
--- /dev/null
+++ b/locale/Turkey
@@ -1,0 +1,19 @@
+EET 10800 EET 14400
+ 512528400  528249600  543978000  559699200  575427600  591148800
+ 606877200  622598400  638326800  654652800  670381200  686102400
+ 701830800  717552000  733280400  749001600  764730000  780451200
+ 796179600  811900800  828234000  843955200  859683600  875404800
+ 891133200  906854400  922582800  938304000  954032400  969753600
+ 985482000 1001808000 1017536400 1033257600 1048986000 1064707200
+1080435600 1096156800 1111885200 1127606400 1143334800 1159056000
+1174784400 1191110400 1206838800 1222560000 1238288400 1254009600
+1269738000 1285459200 1301187600 1316908800 1332637200 1348963200
+1364691600 1380412800 1396141200 1411862400 1427590800 1443312000
+1459040400 1474761600 1490490000 1506211200 1521939600 1538265600
+1553994000 1569715200 1585443600 1601164800 1616893200 1632614400
+1648342800 1664064000 1679792400 1695513600 1711846800 1727568000
+1743296400 1759017600 1774746000 1790467200 1806195600 1821916800
+1837645200 1853366400 1869094800 1885420800 1901149200 1916870400
+1932598800 1948320000 1964048400 1979769600 1995498000 2011219200
+2026947600 2042668800 2058397200 2074723200 2090451600 2106172800
+2121901200 2137622400
--- /dev/null
+++ b/locale/US_Alaska
@@ -1,0 +1,24 @@
+AKS -32400 AKD -28800
+   9943200   25664400   41392800   57718800   73447200   89168400
+ 104896800  120618000  126669600  152067600  162352800  183517200
+ 199245600  215571600  230695200  247021200  262749600  278470800
+ 294199200  309920400  325648800  341370000  357098400  372819600
+ 388548000  404874000  419997600  436323600  452052000  467773200
+ 483501600  499222800  514951200  530672400  544586400  562122000
+ 576036000  594176400  607485600  625626000  638935200  657075600
+ 670989600  688525200  702439200  719974800  733888800  752029200
+ 765338400  783478800  796788000  814928400  828842400  846378000
+ 860292000  877827600  891741600  909277200  923191200  941331600
+ 954640800  972781200  986090400 1004230800 1018144800 1035680400
+1049594400 1067130000 1081044000 1099184400 1112493600 1130634000
+1143943200 1162083600 1173578400 1194141600 1205028000 1225591200
+1236477600 1257040800 1268532000 1289095200 1299981600 1320544800
+1331431200 1351994400 1362880800 1383444000 1394330400 1414893600
+1425780000 1446343200 1457834400 1478397600 1489284000 1509847200
+1520733600 1541296800 1552183200 1572746400 1583632800 1604196000
+1615687200 1636250400 1647136800 1667700000 1678586400 1699149600
+1710036000 1730599200 1741485600 1762048800 1772935200 1793498400
+1804989600 1825552800 1836439200 1857002400 1867888800 1888452000
+1899338400 1919901600 1930788000 1951351200 1962842400 1983405600
+1994292000 2014855200 2025741600 2046304800 2057191200 2077754400
+2088640800 2109204000 2120090400 2140653600
--- /dev/null
+++ b/locale/US_Arizona
@@ -1,0 +1,1 @@
+MST -25200 MST -25200
--- /dev/null
+++ b/locale/US_Central
@@ -1,0 +1,24 @@
+CST -21600 CDT -18000
+   9943200   25664400   41392800   57718800   73447200   89168400
+ 104896800  120618000  126669600  152067600  162352800  183517200
+ 199245600  215571600  230695200  247021200  262749600  278470800
+ 294199200  309920400  325648800  341370000  357098400  372819600
+ 388548000  404874000  419997600  436323600  452052000  467773200
+ 483501600  499222800  514951200  530672400  544586400  562122000
+ 576036000  594176400  607485600  625626000  638935200  657075600
+ 670989600  688525200  702439200  719974800  733888800  752029200
+ 765338400  783478800  796788000  814928400  828842400  846378000
+ 860292000  877827600  891741600  909277200  923191200  941331600
+ 954640800  972781200  986090400 1004230800 1018144800 1035680400
+1049594400 1067130000 1081044000 1099184400 1112493600 1130634000
+1143943200 1162083600 1173578400 1194141600 1205028000 1225591200
+1236477600 1257040800 1268532000 1289095200 1299981600 1320544800
+1331431200 1351994400 1362880800 1383444000 1394330400 1414893600
+1425780000 1446343200 1457834400 1478397600 1489284000 1509847200
+1520733600 1541296800 1552183200 1572746400 1583632800 1604196000
+1615687200 1636250400 1647136800 1667700000 1678586400 1699149600
+1710036000 1730599200 1741485600 1762048800 1772935200 1793498400
+1804989600 1825552800 1836439200 1857002400 1867888800 1888452000
+1899338400 1919901600 1930788000 1951351200 1962842400 1983405600
+1994292000 2014855200 2025741600 2046304800 2057191200 2077754400
+2088640800 2109204000 2120090400 2140653600
--- /dev/null
+++ b/locale/US_East-Indiana
@@ -1,0 +1,12 @@
+EST -18000 EDT -14400
+1143943200 1162083600 1173578400 1194141600 1205028000 1225591200
+1236477600 1257040800 1268532000 1289095200 1299981600 1320544800
+1331431200 1351994400 1362880800 1383444000 1394330400 1414893600
+1425780000 1446343200 1457834400 1478397600 1489284000 1509847200
+1520733600 1541296800 1552183200 1572746400 1583632800 1604196000
+1615687200 1636250400 1647136800 1667700000 1678586400 1699149600
+1710036000 1730599200 1741485600 1762048800 1772935200 1793498400
+1804989600 1825552800 1836439200 1857002400 1867888800 1888452000
+1899338400 1919901600 1930788000 1951351200 1962842400 1983405600
+1994292000 2014855200 2025741600 2046304800 2057191200 2077754400
+2088640800 2109204000 2120090400 2140653600
--- /dev/null
+++ b/locale/US_Eastern
@@ -1,0 +1,24 @@
+EST -18000 EDT -14400
+   9943200   25664400   41392800   57718800   73447200   89168400
+ 104896800  120618000  126669600  152067600  162352800  183517200
+ 199245600  215571600  230695200  247021200  262749600  278470800
+ 294199200  309920400  325648800  341370000  357098400  372819600
+ 388548000  404874000  419997600  436323600  452052000  467773200
+ 483501600  499222800  514951200  530672400  544586400  562122000
+ 576036000  594176400  607485600  625626000  638935200  657075600
+ 670989600  688525200  702439200  719974800  733888800  752029200
+ 765338400  783478800  796788000  814928400  828842400  846378000
+ 860292000  877827600  891741600  909277200  923191200  941331600
+ 954640800  972781200  986090400 1004230800 1018144800 1035680400
+1049594400 1067130000 1081044000 1099184400 1112493600 1130634000
+1143943200 1162083600 1173578400 1194141600 1205028000 1225591200
+1236477600 1257040800 1268532000 1289095200 1299981600 1320544800
+1331431200 1351994400 1362880800 1383444000 1394330400 1414893600
+1425780000 1446343200 1457834400 1478397600 1489284000 1509847200
+1520733600 1541296800 1552183200 1572746400 1583632800 1604196000
+1615687200 1636250400 1647136800 1667700000 1678586400 1699149600
+1710036000 1730599200 1741485600 1762048800 1772935200 1793498400
+1804989600 1825552800 1836439200 1857002400 1867888800 1888452000
+1899338400 1919901600 1930788000 1951351200 1962842400 1983405600
+1994292000 2014855200 2025741600 2046304800 2057191200 2077754400
+2088640800 2109204000 2120090400 2140653600
--- /dev/null
+++ b/locale/US_Hawaii
@@ -1,0 +1,1 @@
+HST -36000 HST -36000
--- /dev/null
+++ b/locale/US_Michigan
@@ -1,0 +1,24 @@
+EST -18000 EDT -14400
+   9943200   25664400   41392800   57718800   73447200   89168400
+ 104896800  120618000  126669600  152067600  162352800  183517200
+ 199245600  215571600  230695200  247021200  262749600  278470800
+ 294199200  309920400  325648800  341370000  357098400  372819600
+ 388548000  404874000  419997600  436323600  452052000  467773200
+ 483501600  499222800  514951200  530672400  544586400  562122000
+ 576036000  594176400  607485600  625626000  638935200  657075600
+ 670989600  688525200  702439200  719974800  733888800  752029200
+ 765338400  783478800  796788000  814928400  828842400  846378000
+ 860292000  877827600  891741600  909277200  923191200  941331600
+ 954640800  972781200  986090400 1004230800 1018144800 1035680400
+1049594400 1067130000 1081044000 1099184400 1112493600 1130634000
+1143943200 1162083600 1173578400 1194141600 1205028000 1225591200
+1236477600 1257040800 1268532000 1289095200 1299981600 1320544800
+1331431200 1351994400 1362880800 1383444000 1394330400 1414893600
+1425780000 1446343200 1457834400 1478397600 1489284000 1509847200
+1520733600 1541296800 1552183200 1572746400 1583632800 1604196000
+1615687200 1636250400 1647136800 1667700000 1678586400 1699149600
+1710036000 1730599200 1741485600 1762048800 1772935200 1793498400
+1804989600 1825552800 1836439200 1857002400 1867888800 1888452000
+1899338400 1919901600 1930788000 1951351200 1962842400 1983405600
+1994292000 2014855200 2025741600 2046304800 2057191200 2077754400
+2088640800 2109204000 2120090400 2140653600
--- /dev/null
+++ b/locale/US_Mountain
@@ -1,0 +1,24 @@
+MST -25200 MDT -21600
+   9943200   25664400   41392800   57718800   73447200   89168400
+ 104896800  120618000  126669600  152067600  162352800  183517200
+ 199245600  215571600  230695200  247021200  262749600  278470800
+ 294199200  309920400  325648800  341370000  357098400  372819600
+ 388548000  404874000  419997600  436323600  452052000  467773200
+ 483501600  499222800  514951200  530672400  544586400  562122000
+ 576036000  594176400  607485600  625626000  638935200  657075600
+ 670989600  688525200  702439200  719974800  733888800  752029200
+ 765338400  783478800  796788000  814928400  828842400  846378000
+ 860292000  877827600  891741600  909277200  923191200  941331600
+ 954640800  972781200  986090400 1004230800 1018144800 1035680400
+1049594400 1067130000 1081044000 1099184400 1112493600 1130634000
+1143943200 1162083600 1173578400 1194141600 1205028000 1225591200
+1236477600 1257040800 1268532000 1289095200 1299981600 1320544800
+1331431200 1351994400 1362880800 1383444000 1394330400 1414893600
+1425780000 1446343200 1457834400 1478397600 1489284000 1509847200
+1520733600 1541296800 1552183200 1572746400 1583632800 1604196000
+1615687200 1636250400 1647136800 1667700000 1678586400 1699149600
+1710036000 1730599200 1741485600 1762048800 1772935200 1793498400
+1804989600 1825552800 1836439200 1857002400 1867888800 1888452000
+1899338400 1919901600 1930788000 1951351200 1962842400 1983405600
+1994292000 2014855200 2025741600 2046304800 2057191200 2077754400
+2088640800 2109204000 2120090400 2140653600
--- /dev/null
+++ b/locale/US_Pacific
@@ -1,0 +1,24 @@
+PST -28800 PDT -25200
+   9943200   25664400   41392800   57718800   73447200   89168400
+ 104896800  120618000  126669600  152067600  162352800  183517200
+ 199245600  215571600  230695200  247021200  262749600  278470800
+ 294199200  309920400  325648800  341370000  357098400  372819600
+ 388548000  404874000  419997600  436323600  452052000  467773200
+ 483501600  499222800  514951200  530672400  544586400  562122000
+ 576036000  594176400  607485600  625626000  638935200  657075600
+ 670989600  688525200  702439200  719974800  733888800  752029200
+ 765338400  783478800  796788000  814928400  828842400  846378000
+ 860292000  877827600  891741600  909277200  923191200  941331600
+ 954640800  972781200  986090400 1004230800 1018144800 1035680400
+1049594400 1067130000 1081044000 1099184400 1112493600 1130634000
+1143943200 1162083600 1173578400 1194141600 1205028000 1225591200
+1236477600 1257040800 1268532000 1289095200 1299981600 1320544800
+1331431200 1351994400 1362880800 1383444000 1394330400 1414893600
+1425780000 1446343200 1457834400 1478397600 1489284000 1509847200
+1520733600 1541296800 1552183200 1572746400 1583632800 1604196000
+1615687200 1636250400 1647136800 1667700000 1678586400 1699149600
+1710036000 1730599200 1741485600 1762048800 1772935200 1793498400
+1804989600 1825552800 1836439200 1857002400 1867888800 1888452000
+1899338400 1919901600 1930788000 1951351200 1962842400 1983405600
+1994292000 2014855200 2025741600 2046304800 2057191200 2077754400
+2088640800 2109204000 2120090400 2140653600
--- /dev/null
+++ b/locale/US_Yukon
@@ -1,0 +1,24 @@
+YST -32400 YDT -28800
+   9943200   25664400   41392800   57718800   73447200   89168400
+ 104896800  120618000  126669600  152067600  162352800  183517200
+ 199245600  215571600  230695200  247021200  262749600  278470800
+ 294199200  309920400  325648800  341370000  357098400  372819600
+ 388548000  404874000  419997600  436323600  452052000  467773200
+ 483501600  499222800  514951200  530672400  544586400  562122000
+ 576036000  594176400  607485600  625626000  638935200  657075600
+ 670989600  688525200  702439200  719974800  733888800  752029200
+ 765338400  783478800  796788000  814928400  828842400  846378000
+ 860292000  877827600  891741600  909277200  923191200  941331600
+ 954640800  972781200  986090400 1004230800 1018144800 1035680400
+1049594400 1067130000 1081044000 1099184400 1112493600 1130634000
+1143943200 1162083600 1173578400 1194141600 1205028000 1225591200
+1236477600 1257040800 1268532000 1289095200 1299981600 1320544800
+1331431200 1351994400 1362880800 1383444000 1394330400 1414893600
+1425780000 1446343200 1457834400 1478397600 1489284000 1509847200
+1520733600 1541296800 1552183200 1572746400 1583632800 1604196000
+1615687200 1636250400 1647136800 1667700000 1678586400 1699149600
+1710036000 1730599200 1741485600 1762048800 1772935200 1793498400
+1804989600 1825552800 1836439200 1857002400 1867888800 1888452000
+1899338400 1919901600 1930788000 1951351200 1962842400 1983405600
+1994292000 2014855200 2025741600 2046304800 2057191200 2077754400
+2088640800 2109204000 2120090400 2140653600
--- /dev/null
+++ b/locale/W-SU
@@ -1,0 +1,19 @@
+??? 10800 ??? 14400
+ 512532000  528256800  543981600  559706400  575431200  591156000
+ 606880800  622605600  638330400  654660000  670384800  686109600
+ 701834400  717559200  733284000  749008800  764733600  780458400
+ 796183200  811908000  828237600  843962400  859687200  875412000
+ 891136800  906861600  922586400  938311200  954036000  969760800
+ 985485600 1001815200 1017540000 1033264800 1048989600 1064714400
+1080439200 1096164000 1111888800 1127613600 1143338400 1159063200
+1174788000 1191117600 1206842400 1222567200 1238292000 1254016800
+1269741600 1285466400 1301191200 1316916000 1332640800 1348970400
+1364695200 1380420000 1396144800 1411869600 1427594400 1443319200
+1459044000 1474768800 1490493600 1506218400 1521943200 1538272800
+1553997600 1569722400 1585447200 1601172000 1616896800 1632621600
+1648346400 1664071200 1679796000 1695520800 1711850400 1727575200
+1743300000 1759024800 1774749600 1790474400 1806199200 1821924000
+1837648800 1853373600 1869098400 1885428000 1901152800 1916877600
+1932602400 1948327200 1964052000 1979776800 1995501600 2011226400
+2026951200 2042676000 2058400800 2074730400 2090455200 2106180000
+2121904800 2137629600
--- /dev/null
+++ b/locale/WET
@@ -1,0 +1,19 @@
+WET 0 WET 3600
+ 512528400  528253200  543978000  559702800  575427600  591152400
+ 606877200  622602000  638326800  654656400  670381200  686106000
+ 701830800  717555600  733280400  749005200  764730000  780454800
+ 796179600  811904400  828234000  843958800  859683600  875408400
+ 891133200  906858000  922582800  938307600  954032400  969757200
+ 985482000 1001811600 1017536400 1033261200 1048986000 1064710800
+1080435600 1096160400 1111885200 1127610000 1143334800 1159059600
+1174784400 1191114000 1206838800 1222563600 1238288400 1254013200
+1269738000 1285462800 1301187600 1316912400 1332637200 1348966800
+1364691600 1380416400 1396141200 1411866000 1427590800 1443315600
+1459040400 1474765200 1490490000 1506214800 1521939600 1538269200
+1553994000 1569718800 1585443600 1601168400 1616893200 1632618000
+1648342800 1664067600 1679792400 1695517200 1711846800 1727571600
+1743296400 1759021200 1774746000 1790470800 1806195600 1821920400
+1837645200 1853370000 1869094800 1885424400 1901149200 1916874000
+1932598800 1948323600 1964048400 1979773200 1995498000 2011222800
+2026947600 2042672400 2058397200 2074726800 2090451600 2106176400
+2121901200 2137626000
--- /dev/null
+++ b/locale/en_US/dict/calendar
@@ -1,0 +1,33 @@
+"Calendar"
+"Set"
+"Show"
+"Save"
+"Del"
+"Date (YYYY/MM/DD):"
+"Time (HH:MM.SS):"
+"Cannot set time: "
+"Set Time"
+"Cannot set time: "
+"Invalid date syntax"
+"Invalid time syntax"
+"Invalid time or date given"
+"write failed:"
+"Sun"
+"Mon"
+"Tue"
+"Wed"
+"Thu"
+"Fri"
+"Sat"
+"Jan"
+"Feb"
+"Mar"
+"Apr"
+"May"
+"Jun"
+"Jul"
+"Aug"
+"Sep"
+"Oct"
+"Nov"
+"Dec"
--- /dev/null
+++ b/locale/en_US/location
@@ -1,0 +1,1 @@
+en_US
--- /dev/null
+++ b/locale/location
@@ -1,0 +1,1 @@
+en_US
--- /dev/null
+++ b/locale/timezone
@@ -1,0 +1,19 @@
+GMT 0 BST 3600
+ 512528400  530672400  543978000  562122000  575427600  593571600
+ 606877200  625626000  638326800  657075600  670381200  688525200
+ 701830800  719974800  733280400  751424400  764730000  782874000
+ 796179600  814928400  828234000  846378000  859683600  877827600
+ 891133200  909277200  922582800  940726800  954032400  972781200
+ 985482000 1004230800 1017536400 1035680400 1048986000 1067130000
+1080435600 1099184400 1111885200 1130634000 1143334800 1162083600
+1174784400 1193533200 1206838800 1224982800 1238288400 1256432400
+1269738000 1288486800 1301187600 1319936400 1332637200 1351386000
+1364691600 1382835600 1396141200 1414285200 1427590800 1445734800
+1459040400 1477789200 1490490000 1509238800 1521939600 1540688400
+1553994000 1572138000 1585443600 1603587600 1616893200 1635642000
+1648342800 1667091600 1679792400 1698541200 1711846800 1729990800
+1743296400 1761440400 1774746000 1792890000 1806195600 1824944400
+1837645200 1856394000 1869094800 1887843600 1901149200 1919293200
+1932598800 1950742800 1964048400 1982797200 1995498000 2014246800
+2026947600 2045696400 2058397200 2077146000 2090451600 2108595600
+2121901200 2140045200 2147483647
--- /dev/null
+++ b/makemk.sh
@@ -1,0 +1,78 @@
+#!/bin/sh
+
+# this file is used only to bootstrap mk onto a platform
+# that currently lacks a binary for mk.  after that, mk can
+# look after itself.
+
+#	support@vitanuova.com
+
+# change these defines as appropriate here or in mkconfig
+# ROOT should be the root of the Inferno tree
+ROOT=/usr/inferno
+SYSTARG=FreeBSD
+OBJTYPE=386
+SYSTYPE=posix
+
+# if you have already changed mkconfig from the distribution, we'll use the definitions from that
+grep -s 'SYSTARG=Plan9' mkconfig || . ./mkconfig
+
+PLAT=$ROOT/$SYSTARG/$OBJTYPE
+
+# you might need to adjust the CC, LD, AR, and RANLIB definitions after this point
+CC="p gcc -m32 -c -I$PLAT/include -I$ROOT/include -I$ROOT/utils/include"
+LD="p gcc -m32"
+AR="p ar crvs"
+RANLIB=":"	# some systems still require `ranlib'
+
+error() {
+	echo $* >&2
+	exit 1
+}
+
+ofiles() {
+	echo $* | sed 's/\.c/.o/g'
+}
+
+p() {
+	echo $*
+	"$@"
+}
+
+# make sure we start off clean
+echo removing old libraries and binaries
+rm -f $PLAT/lib/*.a $PLAT/bin/*
+rm -f utils/cc/y.tab.?
+
+# ensure the output directories exist
+mkdir -p $PLAT/lib $PLAT/bin
+
+# libregexp
+cd $ROOT/utils/libregexp || error cannot find libregexp directory
+CFILES="regaux.c regcomp.c regerror.c regexec.c regsub.c rregexec.c rregsub.c"
+$CC $CFILES || error libregexp compilation failed
+$AR $PLAT/lib/libregexp.a `ofiles $CFILES` || error libregexp ar failed
+$RANLIB $PLAT/lib/libregexp.a || error libregexp ranlib failed
+
+# libbio
+cd $ROOT/libbio || error cannot find libbio directory
+$CC *.c || error libbio compilation failed
+$AR $PLAT/lib/libbio.a *.o || error libbio ar failed
+$RANLIB $PLAT/lib/libbio.a || error libbio ranlib failed
+
+# lib9
+cd $ROOT/lib9 || error cannot find lib9 directory
+CFILES="dirstat-$SYSTYPE.c rerrstr.c errstr-$SYSTYPE.c getuser-$SYSTYPE.c"	# system specific
+CFILES="$CFILES charstod.c cleanname.c create.c dirwstat.c *print*.c *fmt*.c exits.c getfields.c  pow10.c print.c qsort.c rune.c runestrlen.c seek.c strdup.c strtoll.c utflen.c utfrrune.c utfrune.c utf*.c *str*cpy*.c"
+$CC $CFILES || error lib9 compilation failed
+$AR $PLAT/lib/lib9.a `ofiles $CFILES` || error lib9 ar failed
+$RANLIB $PLAT/lib/lib9.a || error lib9 ranlib failed
+
+# mk itself
+cd $ROOT/utils/mk
+CFILES="Posix.c sh.c"	# system specific
+CFILES="$CFILES arc.c archive.c bufblock.c env.c file.c graph.c job.c lex.c main.c match.c mk.c parse.c recipe.c rule.c run.c shprint.c symtab.c var.c varsub.c word.c"
+$CC $CFILES || error mk compilation failed
+$LD -o mk `ofiles $CFILES` $PLAT/lib/libregexp.a $PLAT/lib/libbio.a $PLAT/lib/lib9.a || error mk link failed
+cp mk $PLAT/bin || error mk binary install failed
+
+echo mk binary built successfully!
--- /dev/null
+++ b/man/1/0intro
@@ -1,0 +1,188 @@
+.TH INTRO 1
+.SH NAME
+intro \- introduction to Inferno
+.SH DESCRIPTION
+Inferno is a virtualised operating system that can
+run natively across a wide range of processor architectures
+or hosted on a wide range of operating systems.
+The principal components of the system are:
+.IP •
+The Inferno kernel which can run both native and `hosted' on a range of platforms
+and which presents the same interface to programs in both cases.
+.IP •
+The Dis virtual machine.
+.IP •
+Styx - the tiny broad-spectrum file service protocol.
+.IP •
+Limbo - a new simple, modular, concurrent programming language.
+.IP •
+Tk and Prefab - graphical user interface (`GUI') primitives without a lot of goo.
+.IP •
+The portable cross-development suites that allow any native Inferno platform
+to be cross-compiled on any hosted system.
+.SS Manual conventions
+Throughout this volume, manual entries are cross referenced
+by a notation of the form
+.IR entry ( n ),
+where
+.I entry
+is the name of the page (in italics) and
+.I n
+is the manual section holding the page.
+The same name may be found in more than one section.
+For example, the environment variable inspection command
+documented in
+.IR env (1),
+is quite distinct from the module interface to environment variables
+which is documented in
+.IR env (2),
+which in turn is distinct from the component documented by
+.IR env (3),
+which describes the underlying device that implements environment variables.
+.PP
+Pathnames are understood to exist in the file system space visible from
+Inferno. The root of this space when viewed from the host operating
+system is the Inferno installation directory, sometimes called the
+Inferno root directory. Unless otherwise enabled, the result of
+changes made by Inferno programs to files in the file system space
+is generally restricted to this portion of the host file system.
+.SS Name spaces
+One of the great strengths of Inferno is the
+.I name space
+interface to the resources available to a process,
+a hierarchical structure
+which looks very similar to a conventional file system.
+Resources look like files and directories that can be read
+and written, created and deleted in a way familiar to
+most programmers.
+.PP
+While this interface
+.I is
+used to provide programs with access to conventional
+disk-based filestore, it is also used to control devices
+and user level programs
+.I mounted
+in a process's name space.
+Once a program or a device has been attached to a process's
+name space, the program or device interprets any access
+to the attachment point;
+it can synthesise on demand the names of new files or directories,
+create their contents on the fly as the process reads from them,
+and interpret written data as commands or data as appropriate
+(See
+.IR bind (1)
+and
+.IR sys-bind (2)).
+.PP
+Each new Inferno process inherits its parent's name space,
+but it can divorce its own name space from that of its parent (see
+.IR sys-pctl (2)),
+giving programs the capability to attach
+resources to their own name space without making them globally
+visible. This per-process name space is potent
+but potentially confusing, so, to help programs that might be
+confused,
+.IR namespace (4)
+gives some conventions that should be adhered to if programs
+are to work properly. (That page also gives a general overview
+of the Inferno source tree.)
+.SS Start up
+See ``Installation of the Inferno Software'' in Volume 2
+for details of how to start up Inferno.
+.SS Window/development environment
+Inferno provides a powerful development environment in which to write, compile,
+execute and debug programs written in the Limbo language.
+It gives the developer a clean platform from which he can utilise
+an operating system which contains
+many new and innovative ideas and some, carefully chosen,
+older concepts that have survived the test of time and are likely to be
+familiar to most Plan 9 or Unix users.
+.PP
+Superficially, the Inferno shell
+.IR sh (1)
+looks and behaves much like
+its Plan 9 or Unix contemporaries but, at heart, it is quite different.
+The shell takes advantage of the dynamic module loading
+services that Inferno provides to allow it to be dynamically extended
+in appropriate and interesting ways. For example, by loading the
+.IR sh-tk (1)
+builtin module, a shell script can provide all the programming logic
+required to manage a
+.I Tk
+window with full
+.I Tk
+functionality in, surprisingly, few lines of code; by loading the
+.IR sh-file2chan (1)
+builtin module, a shell script can create a file in the name space
+whose properties are completely under the control of the script.
+.PP
+The Inferno window
+manager
+.IR wm (1)
+allows the user to manage the order and position of a dynamic collection of application
+windows in which to perform various tasks.
+.IR Acme (1)
+is targeted at programmers. It is an editor, a shell and window system all rolled
+into one, which through thoughtful and consistent application of simple principles
+results in a rich and productive programming environment with a user interface
+that feels right.
+.I Acme
+requires a three-button mouse and
+attaches distinct functions to the three mouse buttons and, indeed, to chords of buttons to
+maximise the productivity of the programmer's mouse. For more details of the
+.I Acme
+user interface see the paper
+"Acme: A User Interface for Programmers" in Volume 2.
+.PP
+Limbo programs are compiled with
+.IR limbo (1). 
+This compiles Limbo source into a machine-independent format (Dis) for
+execution by the Inferno Dis virtual machine. The virtual machine is designed to provide 
+safe execution of programs even on machines without memory protection.
+Debugging is made straightforward by use of either
+.IR stack (1)
+, to display the execution stack of a process
+or, if a finer inspection is required,
+.IR deb (1),
+a novel window based debugger that allows the user to identify the exact location of
+problems, set break points and walk the data structures of any module loaded by the program. See "Program Development in Inferno" in Volume 2 for details on how to use the
+development tools that Inferno provides.
+.SH SEE ALSO
+.nf
+Section (1) (this section) for the commonly-used commands.
+Section (2) for Limbo modules, including Inferno's system calls.
+Section (3) for kernel devices (accessed by `bind').
+Section (4) for file services (accessed by `mount').
+Section (5) for the Styx file service protocol.
+Section (6) for file formats and system conventions.
+Section (7) for databases and database access modules.
+Section (8) for administrative modules and system services.
+Section (9) for the reference for Inferno's Tk variant, Limbo/Tk.
+Section (10) for the build environment and device driver implementation.
+.PP
+Volume 2 contains papers and other documentation about Inferno.
+.PP
+The back of this volume contains a permuted index.
+.SH DIAGNOSTICS
+On successful execution, a process can simply exit.
+Programs (modules) that wish to return error status to
+the command interpreters
+.IR sh (1)
+and
+.IR mash (1)
+do so by raising a special exception (eg, using the
+.B raise
+statement in Limbo).
+The exception's value is a string
+beginning with the text
+.RB ` fail: '.
+.SH SEE ALSO
+.IR intro (2),
+.IR intro (3),
+.IR intro (4),
+.IR intro (5),
+.IR intro (6),
+.IR intro (7),
+.IR intro (8),
+.IR intro (9),
+.IR intro (10)
--- /dev/null
+++ b/man/1/9win
@@ -1,0 +1,71 @@
+.TH 9WIN 1
+.SH NAME
+9win \- create a Plan 9 window within Inferno
+.SH SYNOPSIS
+.B 9win
+[
+-s
+]
+[
+.B -x width
+]
+[
+.B -y height
+]
+[
+.IR cmd
+[
+.I arg ...
+]
+]
+.SH DESCRIPTION
+.I 9win
+creates a window for a graphical Plan 9 command (default
+.BR rio )
+to run in. If provided,
+.I width
+and
+.I height
+give a desired width and height for the new window.
+.I Cmd
+gives the command to run,
+and
+.I arg
+its arguments.
+.PP
+The
+.B -s
+option tells
+.I 9win
+to run in server mode. Used by
+.IR 9cpu (1),
+it exports on its standard input
+a namespace
+suitable for a graphical Plan 9 program to run
+within. With this option, no command may be given.
+.SH SOURCE
+.B /appl/cmd/9win.b
+.SH SEE ALSO
+.IR 9cpu (1),
+.IR import (4)
+.SH FILES
+.B /dev/winname
+.br
+.B /dev/mouse
+.br
+.B /dev/cons
+.br
+.B /dev/consctl
+.br
+	Files served by
+.IR 9win .
+.SH BUGS
+.I 9win
+does not export a full
+.I rio
+environment, so Plan 9 programs
+that wish to create new windows will not work correctly
+(unless running inside a
+.I rio
+started by
+.IR 9win )
--- /dev/null
+++ b/man/1/INDEX
@@ -1,0 +1,324 @@
+intro 0intro
+9win 9win
+acme acme
+win acme
+abc alphabet-abc
+alphabet-abc alphabet-abc
+alphabet-fs alphabet-fs
+fs alphabet-fs
+alphabet-grid alphabet-grid
+grid alphabet-grid
+alphabet-main alphabet-main
+main alphabet-main
+ar ar
+asm asm
+disdump asm
+auhdr auplay
+auplay auplay
+raw2iaf auplay
+wav2iaf auplay
+avr avr
+basename basename
+bind bind
+mount bind
+unmount bind
+blur blur
+brutus brutus
+cal cal
+calc calc
+calendar calendar
+cat cat
+cd cd
+charon charon
+chgrp chgrp
+chmod chmod
+cleanname cleanname
+cmp cmp
+collab collab
+connect collab
+chat collab-clients
+collab collab-clients
+collab-clients collab-clients
+poll collab-clients
+poller collab-clients
+whiteboard collab-clients
+comm comm
+cook cook
+cp cp
+fcp cp
+cprof cprof
+cpu cpu
+aescbc crypt
+crypt crypt
+date date
+dd dd
+deb deb
+diff diff
+disdep disdep
+dmview dmview
+dmwm dmview
+du du
+ebook ebook
+echo echo
+emu emu
+env env
+fc fc
+filename filename
+fmt fmt
+fortune fortune
+freq freq
+fs fs
+ftest ftest
+newer ftest
+ftree ftree
+gettar gettar
+lstar gettar
+puttar gettar
+grep grep
+grid-monitor grid-monitor
+monitor grid-monitor
+grid grid-ns
+grid-ns grid-ns
+ns grid-ns
+grid grid-query
+grid-query grid-query
+query grid-query
+grid grid-register
+grid-register grid-register
+register grid-register
+grid grid-session
+grid-session grid-session
+session grid-session
+gunzip gzip
+gzip gzip
+idea idea
+itest itest
+itreplay itest
+keyboard keyboard
+pen keyboard
+broke kill
+kill kill
+limbo limbo
+dial listen
+listen listen
+styxlisten listen
+logon logon
+logwindow logwindow
+look look
+lc ls
+ls ls
+m4 m4
+lookman man
+man man
+man2html man
+man2txt man
+wm/man man
+mash mash
+mash mash-make
+mash-make mash-make
+mash mash-tk
+mash-tk mash-tk
+ack math-misc
+crackerbarrel math-misc
+factor math-misc
+fibonacci math-misc
+fit math-misc
+genprimes math-misc
+math-misc math-misc
+mersenne math-misc
+parts math-misc
+perms math-misc
+pi math-misc
+powers math-misc
+primes math-misc
+sieve math-misc
+mc mc
+mdb mdb
+miniterm miniterm
+mk mk
+mkdir mkdir
+mprof mprof
+wm/mprof mprof
+mux mux
+mv mv
+netkey netkey
+netstat netstat
+ns ns
+nsbuild nsbuild
+os os
+p p
+passwd passwd
+plumb plumb
+prof prof
+wm/prof prof
+ps ps
+pwd pwd
+rcmd rcmd
+read read
+rm rm
+runas runas
+secstore secstore
+sendmail sendmail
+builtin sh
+exit sh
+load sh
+loaded sh
+local sh
+quote sh
+run sh
+set sh
+sh sh
+unload sh
+unquote sh
+whatis sh
+ sh-alphabet
+alphabet sh-alphabet sh-alphabet
+autoconvert sh-alphabet
+autodeclare
+declare sh-alphabet
+define sh-alphabet
+import sh-alphabet
+sh-alphabet sh-alphabet
+type sh-alphabet
+typeset sh-alphabet
+arg sh-arg
+sh-arg sh-arg
+csv sh-csv
+getcsv sh-csv
+sh-csv sh-csv
+expr sh-expr
+mpexpr sh-expr
+ntest sh-expr
+sh-expr sh-expr
+file2chan sh-file2chan
+rblock sh-file2chan
+rdata sh-file2chan
+rerror sh-file2chan
+rget sh-file2chan
+rread sh-file2chan
+rreadone sh-file2chan
+rwrite sh-file2chan
+sh-file2chan sh-file2chan
+mload sh-mload
+munload sh-mload
+sh-mload sh-mload
+match sh-regex
+re sh-regex
+sh-regex sh-regex
+els sh-sexprs
+islist sh-sexprs
+mklist sh-sexprs
+mktext sh-sexprs
+mktextlist sh-sexprs
+sexprs sh-sexprs
+sh-sexprs sh-sexprs
+text sh-sexprs
+textels sh-sexprs
+! sh-std
+and sh-std
+apply sh-std
+env sh-std
+fn sh-std
+getlines sh-std
+hd sh-std
+if sh-std
+index sh-std
+join sh-std
+no sh-std
+or sh-std
+parse sh-std
+pctl sh-std
+pid sh-std
+raise sh-std
+rescue sh-std
+sh-std sh-std
+split sh-std
+status sh-std
+std sh-std
+tl sh-std
+while sh-std
+~ sh-std
+alen sh-string
+drop sh-string
+in sh-string
+len sh-string
+prefix sh-string
+sh-string sh-string
+slice sh-string
+splitl sh-string
+splitr sh-string
+splitstrl sh-string
+splitstrr sh-string
+take sh-string
+tolower sh-string
+toupper sh-string
+report sh-test
+sh-test sh-test
+alt sh-tk
+chan sh-tk
+recv sh-tk
+send sh-tk
+sh-tk sh-tk
+tk sh-tk
+pause sleep
+sleep sleep
+sort sort
+join spree-join
+spree-join spree-join
+stack stack
+stream stream
+strings strings
+md5sum sum
+sha1sum sum
+sum sum
+tail tail
+tcs tcs
+tee tee
+telnet telnet
+time time
+timestamp timestamp
+rm tiny
+sh tiny
+tiny tiny
+tkcmd tkcmd
+tktester tktester
+toolbar toolbar
+touch touch
+tr tr
+tsort tsort
+unicode unicode
+uniq uniq
+units units
+uudecode uuencode
+uuencode uuencode
+vacget vacget
+vacput vacget
+wc wc
+webgrab webgrab
+wish wish
+wm wm
+about wm-misc
+clock wm-misc
+coffee wm-misc
+colors wm-misc
+date wm-misc
+edit wm-misc
+mand wm-misc
+memory wm-misc
+polyhedra wm-misc
+reversi wm-misc
+rt wm-misc
+stopwatch wm-misc
+sweeper wm-misc
+task wm-misc
+tetris wm-misc
+unibrowse wm-misc
+view wm-misc
+winctl wm-misc
+wm-misc wm-misc
+mash wm-sh
+sh wm-sh
+wm-sh wm-sh
+xd xd
+yacc yacc
+zeros zeros
--- /dev/null
+++ b/man/1/acme
@@ -1,0 +1,1203 @@
+.TH ACME 1
+.SH NAME
+acme, win \- interactive text windows
+.SH SYNOPSIS
+.B acme
+[
+.B -f
+.I varfont
+]
+[
+.B -F
+.I fixfont
+]
+[
+.B -c
+.I ncol
+]
+[
+.B -b
+]
+[
+.B -l
+.I file
+|
+.I file
+\&... ]
+.LP
+.B win
+[
+.I command
+]
+.SH DESCRIPTION
+.I Acme
+manages windows of text that may be edited interactively or by external programs.
+The interactive interface uses the keyboard and mouse; external programs
+use a set of files served by
+.IR acme ;
+these are discussed in
+.IR acme (4).
+.PP
+Any named
+.I files
+are read into
+.I acme
+windows before
+.I acme
+accepts input.
+With the
+.B -l
+option, the state of the entire system is loaded
+from
+.IR file ,
+which should have been created by a
+.B Dump
+command (q.v.),
+and subsequent
+.I file
+names are ignored.
+Plain files display as text; directories display as columnated lists of the
+names of their components with the names of subdirectories having a slash appended.
+.PP
+The
+.B -f
+.RB ( -F )
+option sets the default variable-pitch (fixed-pitch)
+font; the default is
+.B /fonts/lucidasans/euro.8.font
+.RB ( \&.../lucm/unicode.9.font ).
+Tab intervals are set to the width of 4 numeral zeros in the variable-pitch font.
+.PP
+.SS Windows
+.I Acme
+windows are in two parts: a one-line
+.I tag
+above a multi-line
+.IR body .
+The body typically contains an image of a file or the output of a program.
+The tag contains a number of
+blank-separated words, followed by a vertical bar character, followed by anything.
+The first word is the name of the window, typically the name of the associated
+file or directory, and the other words are commands available in that window.
+Any text may be added after the bar; examples are strings to search for or
+commands to execute in that window.
+Changes to the text left of the bar will be ignored,
+unless the result is to change the name of the
+window.
+.PP
+If a window holds a directory, the name (first word of the tag) will end with
+a slash.
+.SS Scrolling
+Each window has a scroll bar to the left of the body.
+Scrolling occurs when the button is pressed, rather than released,
+and continues
+as long as the mouse button is held down in the scroll bar.
+For example, to scroll slowly through a file,
+hold button 3 down near the top of the scroll bar.  Moving the mouse
+down the scroll bar speeds up the rate of scrolling. Scrolling backwards is performed
+similarly using button 1. Button 2 allows absolute movement within the text; clicking it
+at different heights within the scroll bar changes the focused text without intermediate
+scrolling.
+.SS Layout
+.I Acme
+windows are arranged in columns.  By default, it creates two columns when starting;
+this can be overridden with the
+.B -c
+option.
+Placement is automatic but may be adjusted
+using the
+.I layout box
+in the upper left corner of each window and column.
+Pressing and holding any mouse button in the box drags
+the associated window or column.
+For windows, just
+clicking in the layout box grows the window in place: button 1
+grows it a little, button 2 grows it as much as it can, still leaving all other
+tags in that column visible, and button 3 takes over the column completely,
+temporarily hiding other windows in the column.
+(They will return
+.I en masse
+if any of them needs attention.)
+The layout box in a window is normally white; when it is black in the center,
+it records that the file is `dirty':
+.I Acme
+believes it is modified from its original
+contents.
+.PP
+Tags exist at the top of each column and across the whole display.
+.I Acme
+pre-loads them with useful commands.
+Also, the tag across the top maintains a list of executing long-running commands.
+.SS Typing
+The behaviour of typed text is as one would expect
+except that the characters are delivered to the tag or body under the mouse; there is no
+`click to type'.
+(The experimental option
+.B -b
+causes typing to go to the most recently clicked-at or made window.)
+The usual backspacing conventions apply.
+The ESC key selects the text typed since the last mouse action,
+a feature particularly useful when executing commands.
+A side effect is that typing ESC with text already selected is identical
+to a
+.B Cut
+command
+.RI ( q.v. ).
+.PP
+Most text, including the names of windows, may be edited uniformly.
+The only exception is that the command names to the
+left of the bar in a tag are maintained automatically; changes to them are repaired
+by
+.IR acme .
+.SS "Directory context
+Each window's tag names a directory: explicitly if the window
+holds a directory; implicitly if it holds a regular file
+(e.g. the directory
+.B /module
+if the window holds
+.BR /module/sys.m ).
+This directory provides a
+.I context
+for interpreting file names in that window.
+For example, the string
+.B sys.m
+in a window labelled
+.B /module/
+or
+.B /module/draw.m
+will be interpreted as the file name
+.BR /module/sys.m .
+The directory is defined purely textually, so it can be a non-existent
+directory or a real directory associated with a non-existent file
+(e.g.
+.BR /module/not-a-file ).
+File names beginning with a slash
+are assumed to be absolute file names.
+.SS Errors
+Windows whose names begin with
+.B -
+or
+.B +
+conventionally hold diagnostics and other data
+not directly associated with files.
+A window labelled
+.B +Errors
+receives all diagnostics produced by
+.I acme
+itself.
+Diagnostics from commands run by
+.I acme
+appear in a window named
+.IB directory /+Errors
+where
+.I directory
+is identified by the context of the command.
+These error windows are created when needed.
+.SS "Mouse button 1
+Mouse button 1 selects text and double-clicking highlights the text for replacement text to be
+typed in. 
+.PP
+Button 1 is also useful for matching symbols. For example to match curly brackets in some limbo
+source, 
+double click button 1 immediately after the open curly bracket.
+The whole of the text up to any matching end curly bracket will be highlighted. A similar match
+is made if the double click is performed immediately before the end bracket. In all,
+.I acme
+will match the pairs { and }, [ and ], ( and ), < and >, « and », ' and ', " and ", ` and `.
+Also whole lines of text may be highlighted by double clicking at the beginning or end of the line.
+.SS "Mouse button 2
+By an
+action similar to selecting text with button 1,
+button 2 indicates text to execute as a command.
+If the indicated text has multiple white-space-separated words,
+the first is the command name and the second and subsequent
+are its arguments.
+If button 2 is `clicked'\(emindicates a null string\(em\c
+.I acme
+.I expands
+the indicated text to find a command to run:
+if the click is within button-1-selected text,
+.I acme
+takes that selection as the command;
+otherwise it takes the largest string of valid file name characters containing the click.
+Valid file name characters are alphanumerics and
+.B _
+.B .
+.B -
+.B +
+.BR / .
+This behaviour is similar to double-clicking with button 1 but,
+because a null command is meaningless, only a single click is required.
+.PP
+Some commands, all by convention starting with a capital letter, are
+.I built-ins
+that are executed directly by
+.IR acme :
+.TF "Lineno\ \ "
+.PD
+.TP
+.B Cut
+Delete most recently selected text and place in snarf buffer.
+.TP
+.B Del
+Delete window.  If window is dirty, instead print a warning; a second
+.B Del
+will succeed.
+.TP
+.B Delcol
+Delete column and all its windows, after checking that windows are not dirty.
+.TP
+.B Delete
+Delete window without checking for dirtiness.
+.TP
+.B Dump
+Write the state of
+.I acme
+to the file name, if specified, or
+.B $home/acme.dump
+by default.
+.TP
+.B Edit
+Treat the argument as a text editing command in the style of Plan9's
+.IR sam.
+The full
+.B Sam
+language is implemented except for the commands
+.BR k ,
+.BR n ,
+.BR q ,
+and
+.BR ! .
+The
+.B =
+command is slightly different: it includes the file name and
+gives only the line address unless the command is explicitly
+.BR =# .
+The `current window' for the command is the body of the window in which the
+.B Edit
+command is executed.
+Usually the
+.B Edit
+command would be typed in a tag; longer commands may be prepared in a
+scratch window and executed, with
+.B Edit
+itself in the current window, using the 2-1 chord described below. See the later
+section on editing for a full description of the commands available here.
+.TP
+.B Exit
+Exit
+.I acme
+after checking that windows are not dirty.
+.TP
+.B Font
+With no arguments, change the font of the associated window from fixed-spaced to
+proportional-spaced or
+.I vice versa\f1.
+Given a file name argument, change the font of the window to that stored in the named file.
+If the file name argument is prefixed by
+.B var
+.RB ( fix ),
+also set the default proportional-spaced (fixed-spaced) font for future use to that font.
+Other existing windows are unaffected.
+.TP
+.B Get
+Load file into window, replacing previous contents (after checking for dirtiness as in
+.BR Del ).
+With no argument, use the existing file name of the window.
+Given an argument, use that file but do not change the window's file name.
+.TP
+.B ID
+Print window ID number
+.RI ( q.v. ).
+.TP
+.B Incl
+When opening `include' files with button 3,
+.I acme
+searches in the directories
+.B /module
+and
+.B /include .
+.B Incl
+adds its arguments to a supplementary list of include directories, analogous to
+the
+.B -I
+option to the compilers.
+This list is per-window and is inherited when windows are created by actions in that window, so
+.I Incl
+is most usefully applied to a directory containing relevant source.
+With no arguments,
+.I Incl
+prints the supplementary list.
+.TP
+.B Kill
+Send a
+.B kill
+note to
+.IR acme -initiated
+commands named as arguments.
+.TP
+.B Lineno
+Give the line number(s) of the currently selected text.
+.TP
+.B Load
+Restore the state of
+.I acme
+from a file (default
+.BR $home/acme.dump )
+created by the
+.B Dump
+command.
+.TP
+.B Local
+When prefixed to a command
+run the
+command in the same file name space and environment variable group as
+.IR acme .
+The environment of the command
+is restricted but is sufficient to run
+.IR bind (1),
+.IR mount ,
+etc.,
+and to set environment variables.
+.TP
+.B Look
+Search in body for occurrence of literal text indicated by the argument or,
+if none is given, by the selected text in the body.
+.TP
+.B New
+Make new window.  With arguments, load the named files into windows.
+.TP
+.B Newcol
+Make new column.
+.TP
+.B Paste
+Replace most recently selected text with contents of snarf buffer.
+.TP
+.B Put
+Write window to the named file.
+With no argument, write to the file named in the tag of the window.
+.TP
+.B Putall
+Write all dirty windows whose names indicate existing regular files.
+.TP
+.B Redo
+Complement of
+.BR Undo .
+.TP
+.B Send
+Append selected text or snarf buffer to end of body; used mainly with
+.IR win .
+.TP
+.B Snarf
+Place selected text in snarf buffer.
+.TP
+.B Sort
+Arrange the windows in the column from top to bottom in lexicographical
+order based on their names.
+.TP
+.B Undo
+Undo last textual change or set of changes.
+.TP
+.B Zerox
+Create a copy of the window containing most recently selected text.
+.PP
+A common place to store text for commands is in the tag; in fact
+.I acme
+maintains a set of commands appropriate to the state of the window
+to the left of the bar in the tag.
+.PP
+If the text indicated with button 2 is not a recognized built-in, it is executed as
+a shell command.  For example, indicating
+.B date
+with button 2 runs
+.IR date (1).
+The standard
+and error outputs of commands are sent to the error window associated with
+the directory from which the command was run, which will be created if
+necessary.
+For example, in a window
+.B /module/sys.m
+executing
+.B pwd
+will produce the output
+.B /module
+in a (possibly newly-created) window labelled
+.BR /adm/+Errors ;
+in a window containing
+.B /appl/cmd/date.b
+executing
+.B "limbo date.b"
+will run
+.IR limbo (1)
+in
+.BR /appl/cmd ,
+producing output in a window labelled
+.BR /appl/cmd/+Errors .
+.SS "Mouse button 3
+Pointing at text with button 3 instructs
+.I acme
+to locate or acquire the file, string, etc. described by the indicated text and
+its context.
+This description follows the actions taken when
+button 3 is released after sweeping out some text.
+In the description,
+.I text
+refers to the text of the original sweep or, if it was null, the result of
+applying the same expansion rules that apply to button 2 actions.
+.PP
+If the text names an existing window,
+.I acme
+moves the mouse cursor to the selected text in the body of that window.
+If the text names an existing file with no associated window,
+.I acme
+loads the file into a new window and moves the mouse there.
+If the text is a file name contained in double quotes,
+.I acme
+loads the indicated include file from the directory appropriate to the
+suffix of the file name of the window holding the text.
+(The
+.B Incl
+command adds directories to the standard list.)
+.PP
+If the text begins with a colon, it is taken to be an address
+within the body of the window containing the text.
+The address is evaluated, the resulting text highlighted, and the mouse moved to it.
+Thus, in
+.IR acme ,
+one must type
+.B :/regexp
+or
+.B :127
+not just
+.B /regexp
+or
+.BR 127 .
+(There is an easier way to locate literal text; see below.)
+.PP
+If the text is a file name followed by a colon and an address,
+.I acme
+loads the file and evaluates the address.  For example, clicking button 3 anywhere
+in the text
+.B file.c:27
+will open
+.BR file.c ,
+select line
+27, and put the mouse at the beginning of the line.  The rules about Error
+files, directories, and so on all combine to make this an efficient way to
+investigate errors from compilers, etc.
+.PP
+If the text is not an address or file, it is taken to
+be literal text, which is then searched for in the body of the window
+in which button 3 was clicked.  If a match is found, it is selected and the mouse is
+moved there.  Thus, to search for occurrences of a word in a file,
+just click button 3 on the word.  Because of the rule of using the
+selection as the button 3 action, subsequent clicks will find subsequent
+occurrences without moving the mouse.
+.PP
+In all these actions, the mouse motion is not done if the text is a null string
+within a non-null selected string in the tag, so that (for example) complex regular expressions
+may be selected and applied repeatedly to the
+body by just clicking button 3 over them.
+.SS "Chords of mouse buttons
+Several operations are bound to multiple-button actions.
+After selecting text, with button 1 still down, pressing button 2
+executes
+.B Cut
+and button 3 executes
+.BR Paste .
+After clicking one button, the other undoes
+the first; thus (while holding down button 1) 2 followed by 3 is a
+.B Snarf
+that leaves the file undirtied;
+3 followed by 2 is a no-op.
+These actions also apply to text selected by double-clicking because
+the double-click expansion is made when the second
+click starts, not when it ends.
+.PP
+Thus to copy a word a number of times, double click on the word with button 1 to highlight it leaving
+button 1 down, press and release button 2 to cut it and save it in the snarf buffer, press and 
+release button 3 to paste it back and then release button 1. Now move the cursor to any selected
+place in the text, press button 1 down, then button 3 and the word is copied in.
+.PP
+Similarly lines may be deleted by double clicking at the beginning or end of the line and then 
+pressing button 2 with button 1 still down.
+.PP
+Commands may be given extra arguments by a mouse chord with buttons 2 and 1.
+While holding down button 2 on text to be executed as a command, clicking button 1
+appends the text last pointed to by button 1 as a distinct final argument.
+For example, to search for literal
+.B text
+one may execute
+.B Look text
+with button 2 or instead point at
+.B text
+with button 1 in any window, release button 1,
+then execute
+.BR Look ,
+clicking button 1 while 2 is held down.
+.PP
+When an external command (e.g.
+.IR echo (1))
+is executed this way, the extra argument is passed as expected and an
+environment variable
+.B $acmeaddr
+is created that holds, in the form interpreted by button 3,
+the fully-qualified address of the extra argument.
+.SS "Support programs
+.I win
+creates a new
+.I acme
+window and runs a
+.I command
+(default
+.BR /dis/sh.dis )
+in it, turning the window into a shell window in which commands may be executed.
+Executing text in a
+.I win
+window with button
+2 is similar to using
+.BR Send .
+.PP
+Similarly
+.I winm
+creates a new window but runs the shell
+.BR /dis/mash.dis
+by default.
+.I adiff
+behaves as diff in finding the difference between two files but the listing uses
+filename:linenumber format to allow the user to simply click on this to be sent to that line
+in the file.
+.I agrep
+does for grep what adiff does for diff above.
+.I cd
+changes directory but when used in a win window for example, sends information to the
+window to display a new heading reflecting the new directory.
+.SS "Mail"
+In the directory 
+.B /acme/mail
+there are two mail programs that may be used under acme. These
+.I Mail
+and
+.I Mailpop3
+can be run to display the user's current mail, read the mail, reply to mail, save or delete mail,
+send mail and write the user's mail box.
+.PP
+The former expects the user's mail box to be in the directory and file specified as its first argument,
+the latter uses the POP3 protocol to connect to a server for the user's mail and will prompt for a 
+password when first run. Otherwise their behaviour is the same.
+.SS "Applications and guide files
+In the directory
+.B /acme
+live several subdirectories, each corresponding to a program or
+set of related programs that employ
+.I acme's
+user interface.
+Each subdirectory includes dis files and a
+.B readme
+file for further information.
+It also includes a
+.BR guide ,
+a text file holding sample commands to invoke the programs.
+The idea is to find an example in the guide that best matches
+the job at hand, edit it to suit, and execute it.
+.PP
+Whenever a command is executed by
+.IR acme ,
+the default search path includes the directory of the window containing
+the command.
+Also,
+.I acme
+binds the directory
+.B /acme/dis
+in front of
+.B /dis
+when it starts; this is where
+.IR acme -specific
+programs such as
+.I win
+reside.
+.SH EDITING
+This section explains the commands available when using acme's Edit command.
+.PP
+.SS Regular expressions
+Regular expressions are as in
+.IR regexp (6)
+with the addition of
+.BR \en
+to represent newlines.
+A regular expression may never contain a literal newline character.
+The empty
+regular expression stands for the last complete expression encountered.
+A regular expression
+matches the longest leftmost substring formally
+matched by the expression.
+Searching in the reverse direction is equivalent
+to searching backwards with the catenation operations reversed in
+the expression.
+.SS Addresses
+An address identifies a substring in a file.
+In the following, `character
+.IR n '
+means the null string
+after the
+.IR n -th
+character in the file, with 1 the
+first character in the file.
+`Line
+.IR n '
+means the
+.IR n -th
+match,
+starting at the beginning of the file, of the regular expression
+.LR .*\en? .
+All files always have a current substring, called dot,
+that is the default address.
+.SS Simple Addresses
+.PD0
+.TP
+.BI # n
+The empty string after character
+.IR n ;
+.B #0
+is the beginning of the file.
+.TP
+.I n
+Line
+.IR n ;
+.B 0
+is the beginning of the file.
+.TP
+.BI  / regexp /
+.PD0
+.TP
+.BI ? regexp ?
+The substring that matches the regular expression,
+found by looking toward the end 
+.RB ( / )
+or beginning
+.RB ( ? )
+of the file,
+and if necessary continuing the search from the other end to the
+starting point of the search.
+The matched substring may straddle
+the starting point.
+When entering a pattern containing a literal question mark
+for a backward search, the question mark should be
+specified as a member of a class.
+.PD
+.TP
+.B 0
+The string before the first full line.
+This is not necessarily
+the null string; see
+.B +
+and
+.B -
+below.
+.TP
+.B $
+The null string at the end of the file.
+.TP
+.B .
+Dot.
+.TP
+.B \&'
+The mark in the file.
+.TP
+\fL"regexp"\fP
+Preceding a simple address (default
+.BR . ),
+refers to the address evaluated in the unique file whose menu line
+matches the regular expression.
+.PD
+.SS Compound Addresses
+In the following,
+.I a1
+and
+.I a2
+are addresses.
+.TF a1+a2
+.TP
+.IB a1 + a2
+The address
+.I a2
+evaluated starting at the end of
+.IR a1 .
+.TP
+.IB a1 - a2
+The address
+.I a2
+evaluated looking in the reverse direction
+starting at the beginning of
+.IR a1 .
+.TP
+.IB a1 , a2
+The substring from the beginning of
+.I a1
+to the end of
+.IR a2 .
+If
+.I a1
+is missing,
+.B 0
+is substituted.
+If
+.I a2
+is missing,
+.B $
+is substituted.
+.TP
+.IB  a1 ; a2
+Like
+.IB a1 , a2\f1,
+but with
+.I a2
+evaluated at the end of, and dot set to,
+.IR a1 .
+.PD
+.PP
+The operators
+.B +
+and
+.B -
+are high precedence, while
+.B ,
+and
+.B ;
+are low precedence.
+.PP
+In both
+.B +
+and
+.B -
+forms, if
+.I a2
+is a line or character address with a missing
+number, the number defaults to 1.
+If
+.I a1
+is missing,
+.L .
+is substituted.
+If both
+.I a1
+and
+.I a2
+are present and distinguishable,
+.B +
+may be elided.
+.I a2
+may be a regular
+expression; if it is delimited by
+.LR ? 's,
+the effect of the
+.B +
+or
+.B -
+is reversed.
+.PP
+It is an error for a compound address to represent a malformed substring.
+Some useful idioms: 
+.IB a1 +-
+(\f2a1-+\fP)
+selects the line containing
+the end (beginning) of a1.
+.BI 0/ regexp /
+locates the first match of the expression in the file.
+(The form
+.B 0;//
+sets dot unnecessarily.)
+.BI ./ regexp /// 
+finds the second following occurrence of the expression,
+and
+.BI .,/ regexp /
+extends dot.
+.SS Commands
+In the following, text demarcated by slashes represents text delimited
+by any printable
+character except alphanumerics.
+Any number of
+trailing delimiters may be elided, with multiple elisions then representing
+null strings, but the first delimiter must always
+be present.
+In any delimited text,
+newline may not appear literally;
+.B \en
+may be typed for newline; and
+.B \e/
+quotes the delimiter, here 
+.LR / .
+Backslash is otherwise interpreted literally, except in
+.B s
+commands.
+.PP
+Most commands may be prefixed by an address to indicate their range
+of operation.
+Those that may not are marked with a 
+.L *
+below.
+If a command takes
+an address and none is supplied, dot is used.
+The sole exception is
+the
+.B w
+command, which defaults to
+.BR 0,$ .
+In the description, `range' is used
+to represent whatever address is supplied.
+Many commands set the
+value of dot as a side effect.
+If so, it is always set to the `result'
+of the change: the empty string for a deletion, the new text for an
+insertion, etc. (but see the
+.B s
+and
+.B e
+commands).
+.br
+.ne 1.2i
+.SS Text commands
+.PD0
+.TP
+.BI a/ text /
+.TP
+or
+.TP
+.B  a
+.TP
+.I lines of text
+.TP
+.B .
+Insert the text into the file after the range.
+Set dot.
+.PD
+.TP
+.B c\fP
+.br
+.ns
+.TP
+.B i\fP
+Same as
+.BR a ,
+but
+.B c
+replaces the text, while
+.B i
+inserts
+.I before
+the range.
+.TP
+.B d
+Delete the text in the range.
+Set dot.
+.TP
+.BI s/ regexp / text /
+Substitute
+.I text
+for the first match to the regular expression in the range.
+Set dot to the modified range.
+In 
+.I text
+the character
+.B &
+stands for the string
+that matched the expression. 
+Backslash behaves as usual unless followed by
+a digit:
+.BI \e d
+stands for the string that matched the
+subexpression begun by the
+.IR d -th
+left parenthesis.
+If
+.I s
+is followed immediately by a
+number
+.IR n ,
+as in
+.BR s2/x/y/ ,
+the
+.IR n -th
+match in the range is substituted.
+If the
+command is followed by a
+.BR g ,
+as in
+.BR s/x/y/g ,
+all matches in the range
+are substituted.
+.TP
+.BI m " a1
+.br
+.ns
+.TP
+.BI t " a1
+Move
+.RB ( m )
+or copy
+.RB ( t )
+the range to after
+.IR a1 .
+Set dot.
+.SS Display commands
+.PD 0
+.TP
+.B p
+Print the text in the range.
+Set dot.
+.TP
+.B =
+Print the file name and line address of the range.
+.TP
+.B =#
+Print the file name and character address of the range.
+.PD
+.SS File commands
+.PD0
+.TP
+.BI * " b " file-list
+Set the current file to the first file named in the list
+that
+.I acme
+has displayed.
+The list may be expressed
+.BI < "command"
+in which case the file names are taken as words (in the shell sense)
+generated by the command.
+.TP
+.BI * " B " file-list
+Same as
+.BR b ,
+except that file names not displayed are entered there,
+and all file names in the list are examined.
+.TP
+.BI * " D " file-list
+Delete the named files from the menu.
+If no files are named, the current file is deleted.
+It is an error to
+.B D
+a modified file, but a subsequent
+.B D
+will delete such a file.
+.PD
+.SS I/O Commands
+.PD0
+.TP
+.BI * " e " filename
+Replace the file by the contents of the named external file.
+Set dot to the beginning of the file.
+.TP
+.BI r " filename
+Replace the text in the range by the contents of the named external file.
+Set dot.
+.TP
+.BI w " filename
+Write the range (default
+.BR 0,$ )
+to the named external file.
+.TP
+.BI * " f " filename
+Set the file name and print the resulting menu entry.
+.PP
+If the file name is absent from any of these, the current file name is used.
+.B e
+always sets the file name;
+.B r
+and
+.B w
+do so if the file has no name.
+.TP
+.BI < " command
+Replace the range by the standard output of the command.
+.TP
+.BI > " command
+Send the range to the standard input of the command.
+.TP
+.BI | " command
+Send the range to the standard input, and replace it by
+the standard output, of the command.
+.TP
+.BI * " cd " directory
+Change working directory.
+If no directory is specified,
+.B $home
+is used.
+.PD
+.PP
+In any of
+.BR < ,
+.BR > ,
+or
+.BR | ,
+if the
+.I command
+is omitted the last
+.I command
+(of any type) is substituted.
+.SS Loops and Conditionals
+.PD0
+.TP
+.BI x/ regexp / " command
+For each match of the regular expression in the range, run the command
+with dot set to the match.
+Set dot to the last match.
+If the regular
+expression and its slashes are omitted, 
+.L /.*\en/
+is assumed.
+Null string matches potentially occur before every character
+of the range and at the end of the range.
+.TP
+.BI y/ regexp / " command
+Like
+.BR x ,
+but run the command for each substring that lies before, between,
+or after
+the matches that would be generated by
+.BR x .
+There is no default regular expression.
+Null substrings potentially occur before every character
+in the range.
+.TP
+.BI * " X/ regexp / " command
+For each file whose menu entry matches the regular expression,
+make that the current file and
+run the command.
+If the expression is omitted, the command is run
+in every file.
+.TP
+.BI * " Y/ regexp / " command
+Same as
+.BR X ,
+but for files that do not match the regular expression,
+and the expression is required.
+.TP
+.BI g/ regexp / " command
+.br
+.ns
+.TP
+.BI v/ regexp / " command
+If the range contains
+.RB ( g )
+or does not contain
+.RB ( v )
+a match for the expression,
+set dot to the range and run the command.
+.PP
+These may be nested arbitrarily deeply, but only one instance of either
+.B X
+or
+.B Y
+may appear in a \%single command.
+An empty command in an
+.B x
+or
+.B y
+defaults to
+.BR p ;
+an empty command in
+.B X
+or
+.B Y
+defaults to
+.BR f .
+.B g
+and
+.B v
+do not have defaults.
+.PD
+.SS Miscellany
+.TF (empty)
+.TP
+.BI * " u " n
+Undo the last
+.I n
+(default 1)
+top-level commands that changed the contents or name of the
+current file, and any other file whose most recent change was simultaneous
+with the current file's change.
+Successive
+.BR u 's
+move further back in time.
+The only commands for which u is ineffective are
+.BR cd ,
+.BR u ,
+.B w
+and
+.BR D .
+If
+.I n
+is negative,
+.B u
+`redoes,' undoing the undo, going forwards in time again.
+.TP
+(empty)
+If the range is explicit, set dot to the range.
+If no address is specified (the
+command is a newline) dot is extended in either direction to
+line boundaries and printed.
+If dot is thereby unchanged, it is set to
+.B .+1 
+and printed.
+.PD
+.SS Grouping and multiple changes
+Commands may be grouped by enclosing them in braces
+.BR {} .
+Commands within the braces must appear on separate lines (no backslashes are
+required between commands).
+Semantically, an opening brace is like a command:
+it takes an (optional) address and sets dot for each sub-command.
+Commands within the braces are executed sequentially, but changes made
+by one command are not visible to other commands (see the next
+paragraph).
+Braces may be nested arbitrarily.
+.PP
+When a command makes a number of changes to a file, as in
+.BR x/re/c/text/ ,
+the addresses of all changes to the file are computed in the original file.
+If the changes are in sequence,
+they are applied to the file.
+Successive insertions at the same address are catenated into a single
+insertion composed of the several insertions in the order applied.
+.SH FILES
+.TF /appl/acme/acme/*/src
+.TP
+.B $home/acme.dump
+default file for
+.B Dump
+and
+.BR Load ;
+also where state is written if
+.I acme
+dies unexpectedly.
+.TP
+.B /acme/*/guide
+template files for applications
+.TP
+.B /acme/*/readme
+informal documentation for applications
+.TP
+.B /appl/acme/acme/*/src
+source for applications
+.TP
+.B /acme/dis
+dis files for applications
+.SH SOURCE
+.B /appl/acme
+.br
+.B /appl/acme/acme/bin/src/win.b
+.SH SEE ALSO
+.IR acme (4)
+.br
+Rob Pike,
+.IR "Acme: A User Interface for Programmers" ", Volume 2"
+.SH BUGS
+With the
+.B -l
+option or
+.B Load
+command,
+the recreation of windows under control of external programs
+such as
+.I win
+is just to rerun the command; information may be lost.
--- /dev/null
+++ b/man/1/alphabet-abc
@@ -1,0 +1,141 @@
+.TH ALPHABET-ABC 1
+.SH NAME
+abc \- alphabet declarations
+.SH SYNOPSIS
+.EX
+load alphabet
+typeset /abc
+type /abc
+.EE
+.SH DESCRIPTION
+.B Grid
+is a typeset for
+.I alphabet
+(see
+.IR sh-alphabet (1))
+which enables
+allows direct interconnection of
+remote and local processing components. It defines one new type,
+.BR endpoint ,
+which represents
+a place in the network to which two parties can
+connect and exchange data.
+.PP
+In the following descriptions, if a type
+is not
+.B endpoint
+or a type defined in the root typeset (see
+.IR alphabet-main (1)),
+it is assumed to be of type
+.BR /string .
+.PP
+Modules currently provided within 
+the
+.B /grid
+typeset include:
+.TP 10
+\f5farm [\f5-lnkavA\fP] \fIendpoint\fP \fIaddr\fP \fItasktype\fP [\fIarg\fP...] -> \fIendpoint\fP
+.B Farm
+connects to a grid labour exchange (see
+.IR scheduler (4))
+at
+.IR addr ,
+starts a new job of type
+.BR workflow ,
+and passes all the data read from
+.I endpoint
+to be processed by the currently available labour.
+The data is split into records, each one of which will be
+processed on a worker node by
+.IR tasktype ,
+with its associated
+.IR arg uments.
+Other than
+.BR -A ,
+which specifies unauthenticated access to the
+scheduler,
+the various options are all passed verbatim to
+.IR workflow :
+.B -l
+causes it to split its input on newline-separated records;
+.B -n
+specifies that no record separation is necessary on output;
+.B -k
+specifies that intermediate data for failed tasks should
+be kept around;
+.B -a
+specifies that intermediate data for all tasks should
+be kept around, and
+.B -v
+specifies that
+.I workflow
+should produce a wordy description of what it is doing.
+.TP
+\f5local\fP \fIendpoint\fP -> \fI/fd\fP
+.B Local
+reads everything from 
+.IR endpoint ,
+and writes it to
+.IR fd .
+.TP
+\f5remote\fP [-a \fIaddr\fP] \fIfd\fP -> \fIendpoint\fP
+.B Remote
+is the inverse of
+.BR local :
+it reads data from
+.I fd
+and writes it to a newly created endpoint.
+If
+.B -a
+is given,
+.I addr
+specifies the network address of an endpoint server
+on which to create the new endpoint.
+.TP
+\f5rexec\fP [\f5-A\fP] \fIendpoint\fP \fIaddr\fP \fIcmd\fP -> \fIendpoint\fP
+.B Rexec
+connects to a remote execution server at
+.I addr
+(unauthenticated if
+.B -A
+is specified), and arranges to execute the
+.I alphabet
+expression
+.I cmd
+there. The expression should be compatible with usage
+.BR "fd -> fd" .
+Data from the argument
+.I endpoint
+will be piped through this expression, and
+made available as the resulting
+.I endpoint
+endpoint.
+.SH EXAMPLES
+The examples below that a local endpoint is available, and the following
+.I alphabet
+declarations:
+.EX
+	load alphabet
+	typeset /grid
+	type /string /endpoint /fd
+	import /grid/local /grid/remote
+	autoconvert fd endpoint remote
+.EE
+Set up a rendering pipeline:
+.EX
+	-{/read /tmp/somedata |
+		remote |
+		rexec tcp!node1!rexec "{(/fd); /filter $1 "{os render_stage1}} |
+		rexec tcp!node2!rexec "{(/fd); /filter $1 "{os render_stage2}} |
+		/create /tmp/somedata.result
+	}
+.EE
+.SH SOURCE
+.BR /appl/alphabet/grid.b ,
+.BR /appl/alphabet/gridtypes.b
+.br
+.B /appl/cmd/grid/*.b
+.SH SEE ALSO
+.IR sh-alphabet (1),
+.IR alphabet-main (1),
+.IR sh (1)
--- /dev/null
+++ b/man/1/alphabet-fs
@@ -1,0 +1,425 @@
+.TH ALPHABET-FS 1
+.SH NAME
+fs \- file-hierarchy traversal
+.SH SYNOPSIS
+.EX
+load alphabet
+typeset /fs
+type /fs/fs
+type /fs/entries
+type /fs/gate
+type /fs/selector
+.EE
+.SH DESCRIPTION
+.B Fs
+is a typeset for
+.I alphabet
+(see
+.IR sh-alphabet (1))
+which enables filtering of the contents of hierarchical filesystems.
+.I Fs
+defines four new types:
+.TP 10
+.B fs
+The complete contents of a filesystem.
+.TP
+.B entries
+Information about the entries in a filesystem without
+their content.
+.TP
+.B gate
+A condition that can be used with conditional verbs.
+A gate is open to entries satisfying particular
+criteria.
+.TP
+.B selector
+A comparator which compares two entries
+and selects one, both or neither of them.
+.PP
+In the following description of the verbs provided,
+an entry such as:
+.TP 10
+.B print \fIentries\fP \fR->\fP status
+.PP
+describes a verb
+.BR print ,
+which takes one argument of type
+.IR entries ,
+and the result of which is of type
+.BR status .
+If the type is not one of those described above,
+it should be taken to be of type
+.IR string .
+.PP
+All types and modules names are taken to be relative to
+the typeset root,
+.BR /fs .
+.PP
+Modules defined within
+.I fs
+include:
+.TP 10
+\f5and\fP \fIgate gate\fP [\fIgate\fP...] -> \fIgate\fP
+.B And
+is a gate that is open to an entry if all its arguments are open.
+.TP
+\f5bundle\fP \fIfs\fP -> \fIvoid\fP
+.B Bundle
+converts
+.I fs
+to an archival format and writes it to the standard output.
+.TP
+\f5compose\fP [\f5-d\fP] \fIop\fP -> \fIselector\fP
+.B Compose
+implements ``compositing''-style operators, useful when
+merging filesystems.
+.I Op
+specifies the operator, taking its name from
+the graphical Porter-Duff equivalent:
+.BR AinB ,
+.BR AinB ,
+.BR BinA ,
+.BR AoutB ,
+.BR BoutA ,
+.BR A ,
+.BR AoverB ,
+.BR AatopB ,
+.BR AxorB ,
+.BR B ,
+.BR BoverA ,
+or
+.BR BatopA.
+For instance,
+.B AinB
+gives the intersection of A and B;
+.B AatopB
+gives A whereever both A and B exist, and B otherwise.
+When used as a selector for
+.BR merge ,
+operators that exclude
+the union of A and B are not very useful, as they will
+exclude all common directories at the top level.
+Given the
+.B -d
+option, compose will allow through directories that
+would otherwise be excluded in this way, making
+operators such as
+.B AxorB
+(all that A does not hold in common with B)
+more useful, although accurate only for regular files.
+.TP
+\f5depth\fP \fIn\fP -> \fIgate\fP
+.B Depth
+is a gate open only to entries which are within
+.I n
+levels of the root of the filesystem.
+.TP
+\f5entries\fP \fIfs\fP -> \fIentries\fP
+.B Entries
+produces all the entries contained within
+.IR fs .
+.TP
+\f5filter\fP  [\f5-d\fP] \fIfs\fP\fIgate\fP -> \fIfs\fP
+The result of
+.B filter
+is a filesystem from which all entries that will
+not pass through
+.IR gate ,
+and their descendents, have been removed.
+If the
+.B -d
+flag is given, only files are filtered \- directories bypass the gate.
+.TP
+\f5ls\fP [\f5-um\fP] \fIentries\fP -> \fIvoid\fP
+Print each entry in the style of
+.B ls -l
+(see
+.IR ls (1)).
+If the
+.B -u
+flag is given, the file access time rather than the file modification time
+will be printed. If the
+.B -m
+flag is given, the name of the user that last modified the file
+is printed too.
+.TP
+\f5exec\fP [\f5-pP\fP] [\f5-t\fP \fIcmd\fP] [\f5-n\fP \fIn\fP] \fIentries cmd\fP -> \fIvoid\fP
+Run its argument
+.I cmd
+for each entry in
+.I entries .
+If the
+.B -n
+flag is specified,
+.B exec
+will try to gather
+.I n
+entries together before invoking the command (default 1).
+The environent variable
+.B $file
+is set to the names of the entries that have been gathered.
+If the
+.B -p
+flag is given, environment variables are set giving information
+about the mode, owner, modification time and size of the entry
+(they are named after the equivalent field
+names in the
+.B Dir
+structure; see
+.IR sys-stat (2)).
+This option is only valid when
+.I n
+is 1.
+The
+.B -P
+flag causes all the other fields in the Dir structure to be included too.
+Note that the command is run in the same shell context each time,
+so environment variable set on one execution can
+be retrieved on the next. The
+.B -t
+flag can be used to specify a command which will be executed
+just before termination.
+.TP
+\f5match\fP [\f5-ar\fP] \fIpattern\fP -> \fIgate\fP
+.B Match
+is a gate that is open if the entry's filename
+matches the
+.IR pattern .
+If the
+.B -a
+flag is given, the whole path will be used
+for the match.
+If
+.B -r
+is specified, the pattern is evaluated as a regular expression,
+otherwise it is a shell-style pattern in the style of
+.IR filepat (2).
+.TP
+\f5merge\fP [\f5-1\fP] [\f5-c\fP \fIselector\fP] \fIfs fs\fP [\fIfs\fP...] -> \fIfs\fP
+Recursively merge the contents of its argument
+filesystems.
+.I Selector
+is consulted to see which entries are chosen for the result;
+if not given, entries are resolved in favour of the first filesystem
+(equivalent to
+.BR "{compose AoverB}").
+If the
+.B -1
+flag is given, merging takes place only in the top-level directory.
+.TP
+\f5mode\fP \fIspec\fP -> \fIgate\fP
+.B Mode
+is a gate that lets through entries whose file permissions
+satisfy
+.IR spec ,
+which is a string in the style of
+.IR chmod (1).
+If the
+.I op
+field is
+.BR + ,
+the specified permissions must be present; if
+.BR - ,
+they must be absent, and if
+.BR = ,
+they must be exactly as given.
+The directory and auth modes are specified with
+the characters ``\f5d\fP'' and ``\f5A\fP''
+respectively.
+.TP
+\f5not\fP \fIgate\fP -> \fIgate\fP
+.B Not
+is a gate open to an entry if its argument is not.
+.TP
+\f5or\fP \fIgate gate\fP [\fIgate\fP...] -> \fIgate\fP
+.B Or
+is a gate open to an entry if any argument is open.
+.TP
+\f5path\fP [\f5-x\fP] \fIpath\fP... -> \fIgate\fP
+.B Path
+is a gate open to an entry whose full pathname
+is an ancestor or a descendent of any
+.IR path.
+If
+.B -x
+is specified, the gate is open to any path
+.I except
+descendents of the paths given.
+.TP
+\f5pipe\fP [\f5-1pP\fP] \fIfs cmd\fP -> \fIstatus\fP
+.B Pipe
+is similar to exec, except that the contents of all files
+in
+.I fs
+are piped through
+.IR cmd .
+Unless the
+.B -1
+option is given,
+.I cmd
+is started once for each file, with
+.B $file
+set to its name, and other environment variables
+set according to the
+.B -p
+or
+.B -P
+options, as for
+.BR exec .
+If the
+.B -1
+option is specified,
+.I cmd
+is started once only \- all file data is piped through that.
+.TP
+\f5print\fP \fIentries\fP -> \fIfd\fP
+Print the path name of each entry to
+.IR fd .
+.TP
+\f5proto\fP [\f5-r\fP \fIroot\fP] \fIprotofile\fP -> \fIfs\fP
+Evaluate
+.I protofile
+as a
+.IR mkfs (8)
+.I proto
+file. If
+.I root
+is specified, it will be used as the root of the resulting
+.IR fs .
+.TP
+\f5query\fP \fIcmd\fP -> \fIgate\fP
+.B Query
+is a gate that runs
+.I cmd
+to determine whether it is open: an empty
+exit status from the command yields an open gate.
+The environment variable
+.B $file
+is set for the command to the path name of the entry that is being queried for.
+.TP
+\f5run\fP \fIcmd\fP -> \fIstring\fP
+.B Run
+runs
+.I cmd
+and substitutes the value of the environment variable
+.B $s
+after its invocation.
+.B $s
+must have exactly one element.
+.TP
+\f5select\fP \fIgate entries\fP -> \fIentries\fP
+Select only those entries within
+.I entries
+that will pass through
+.IR gate .
+Descendents of elided entries are not affected.
+.TP
+\f5setroot\fP [\f5-c\fP] \fIfs\fP \fIpath\fP -> \fIfs\fP
+.B Setroot
+sets the name of the root directory of
+.IR fs .
+If the
+.B -c
+flag is given, the elements in the root directory
+will be made explicit in the hierarchy (i.e. the
+name of the top directory will not contain any
+.B /
+characters).
+.TP
+\f5size\fP \fIentries\fP -> \fIfd\fP
+Print the sum of the size of all entries, in bytes to
+.IR fd .
+.TP
+\f5unbundle\fP \fIfd\fP -> \fIfs\fP
+.B Unbundle
+reads an archive as produced by
+.B bundle
+from
+.IR fd ;
+its result is the contents of the filesystem that was
+originally bundled.
+.TP
+\f5walk\fP \fIpath\fP -> \fIfs\fP
+.B Walk
+produces a filesystem that is the result of
+traversing all the files and directories underneath
+.IR path .
+.TP
+\f5write\fP \fIfs dir\fP -> \fIvoid\fP
+Write the contents of
+.I fs
+to the filesystem rooted at
+.I dir .
+If
+.I dir
+is empty,
+.I fs
+will be written to the root directory originally associated with fs.
+.SH EXAMPLES
+The examples below assume the following
+.I alphabet
+declarations:
+.EX
+	load alphabet
+	typeset /fs
+	type /string /fd /fs/fs /fs/entries /fs/gate
+	import /fs/size /fs/walk /fs/select /fs/mode /fs/merge
+	import /fs/compose /fs/exec /fs/bundle /fs/write /fs/unbundle
+	import /fs/print /fs/depth /fs/filter /fs/query
+	autoconvert string fs walk
+	autoconvert fs entries /fs/entries
+	autoconvert string gate /fs/match
+	autoconvert entries fd /fs/print
+	autoconvert fd /status {(/fd); /print $1 1}
+.EE
+Print the size of all files below the current directory:
+.EX
+	-{size .}
+.EE
+Show the names of all files in x that aren't in y:
+.EX
+	-{walk x | merge -c {compose -d AoutB} y | select {mode -d}}
+.EE
+Remove all files from /appl ending in
+.BR .dis :
+.EX
+	-{walk /appl | select '*.dis' | exec "{rm $file}}
+.EE
+Recursively copy the current directory to
+.BR /tmp/foo .
+.EX
+	-{write . /tmp/foo}
+.EE
+Interactively remove all regular files from one level of the current directory:
+.IP
+.EX
+	-{walk . |
+		filter {depth 1} |
+		select {mode -d} |
+		select {
+			query "{echo -n $file:; ~ `{read} y yes}
+		} |
+		exec "{rm $file}
+	}
+.EE
+.PP
+Create a new archive containing those files from below the current directory
+that were held in an old archive:
+.EX
+	-{merge -c {compose AinB} . {unbundle old.bundle} |
+		bundle |
+		/create new.bundle
+	}
+.EE
+.SH SOURCE
+.BR /appl/alphabet/fs.b ,
+.BR /appl/alphabet/fstypes.b
+.BR /appl/alphabet/auxi/fsfilter.b
+.br
+.B /appl/cmd/fs/*.b
+.br
+.SH SEE ALSO
+.IR sh-alphabet (1),
+.IR alphabet-main (1),
+.IR alphabet-fs (2),
+.IR sh (1)
--- /dev/null
+++ b/man/1/alphabet-grid
@@ -1,0 +1,141 @@
+.TH ALPHABET-GRID 1
+.SH NAME
+grid \- peer-to-peer data distribution
+.SH SYNOPSIS
+.EX
+load alphabet
+typeset /grid
+type /grid/endpoint
+.EE
+.SH DESCRIPTION
+.B Grid
+is a typeset for
+.I alphabet
+(see
+.IR sh-alphabet (1))
+which enables
+allows direct interconnection of
+remote and local processing components. It defines one new type,
+.BR endpoint ,
+which represents
+a place in the network to which two parties can
+connect and exchange data.
+.PP
+In the following descriptions, if a type
+is not
+.B endpoint
+or a type defined in the root typeset (see
+.IR alphabet-main (1)),
+it is assumed to be of type
+.BR /string .
+.PP
+Modules currently provided within 
+the
+.B /grid
+typeset include:
+.TP 10
+\f5farm [\f5-lnkavA\fP] \fIendpoint\fP \fIaddr\fP \fItasktype\fP [\fIarg\fP...] -> \fIendpoint\fP
+.B Farm
+connects to a grid labour exchange (see
+.IR scheduler (4))
+at
+.IR addr ,
+starts a new job of type
+.BR workflow ,
+and passes all the data read from
+.I endpoint
+to be processed by the currently available labour.
+The data is split into records, each one of which will be
+processed on a worker node by
+.IR tasktype ,
+with its associated
+.IR arg uments.
+Other than
+.BR -A ,
+which specifies unauthenticated access to the
+scheduler,
+the various options are all passed verbatim to
+.IR workflow :
+.B -l
+causes it to split its input on newline-separated records;
+.B -n
+specifies that no record separation is necessary on output;
+.B -k
+specifies that intermediate data for failed tasks should
+be kept around;
+.B -a
+specifies that intermediate data for all tasks should
+be kept around, and
+.B -v
+specifies that
+.I workflow
+should produce a wordy description of what it is doing.
+.TP
+\f5local\fP \fIendpoint\fP -> \fI/fd\fP
+.B Local
+reads everything from 
+.IR endpoint ,
+and writes it to
+.IR fd .
+.TP
+\f5remote\fP [-a \fIaddr\fP] \fIfd\fP -> \fIendpoint\fP
+.B Remote
+is the inverse of
+.BR local :
+it reads data from
+.I fd
+and writes it to a newly created endpoint.
+If
+.B -a
+is given,
+.I addr
+specifies the network address of an endpoint server
+on which to create the new endpoint.
+.TP
+\f5rexec\fP [\f5-A\fP] \fIendpoint\fP \fIaddr\fP \fIcmd\fP -> \fIendpoint\fP
+.B Rexec
+connects to a remote execution server at
+.I addr
+(unauthenticated if
+.B -A
+is specified), and arranges to execute the
+.I alphabet
+expression
+.I cmd
+there. The expression should be compatible with usage
+.BR "fd -> fd" .
+Data from the argument
+.I endpoint
+will be piped through this expression, and
+made available as the resulting
+.I endpoint
+endpoint.
+.SH EXAMPLES
+The examples below that a local endpoint is available, and the following
+.I alphabet
+declarations:
+.EX
+	load alphabet
+	typeset /grid
+	type /string /endpoint /fd
+	import /grid/local /grid/remote
+	autoconvert fd endpoint remote
+.EE
+Set up a rendering pipeline:
+.EX
+	-{/read /tmp/somedata |
+		remote |
+		rexec tcp!node1!rexec "{(/fd); /filter $1 "{os render_stage1}} |
+		rexec tcp!node2!rexec "{(/fd); /filter $1 "{os render_stage2}} |
+		/create /tmp/somedata.result
+	}
+.EE
+.SH SOURCE
+.BR /appl/alphabet/grid.b ,
+.BR /appl/alphabet/gridtypes.b
+.br
+.B /appl/cmd/grid/*.b
+.SH SEE ALSO
+.IR sh-alphabet (1),
+.IR alphabet-main (1),
+.IR sh (1)
--- /dev/null
+++ b/man/1/alphabet-main
@@ -1,0 +1,240 @@
+.TH ALPHABET-MAIN 1
+.SH NAME
+main \- operators on the basic Alphabet types
+.SH SYNOPSIS
+.EX
+load alphabet
+type /string
+type /fd
+type /wfd
+type /status
+type /cmd
+.br
+.SH DESCRIPTION
+.I Main
+refers to operators defined
+.IR Alphabet 's
+(see
+.IR sh-alphabet (1))
+root typeset
+.RB ( / ).
+.PP
+In the following description of the modules provided,
+an entry such as:
+.TP 10
+.B echo \fIstring\fP \fR->\fP fd
+.PP
+describes a verb
+.BR echo ,
+which takes one argument of type
+.IR string ,
+and the result of which is of type
+.BR fd .
+If the type is not one of those described above,
+it should be taken to be of type
+.IR string .
+.PP
+All types and modules names are taken to be relative to
+the typeset root,
+.BR / .
+.PP
+Modules defined within
+.I main
+include:
+.TP 10
+\f5auth\fP [\f5-v\fP] [\f5-k\fP \fIkeyfile\fP] [\f5-C\fP \fIalg\fP] \fIwfd\fP -> \fIwfd\fP
+.B Auth
+authenticates to a server connected to its argument
+.IR wfd ,
+and optionally adds encryption to the stream.
+If
+.I keyfile
+is given, it gives the filename of a key file (by default
+.BI /usr/ user /keyring/default
+is used).
+If
+.I alg
+is given, it specifies the encryption/hash algorithm to
+push (e.g.
+.BR rc4_256/md5 ).
+If the
+.B -v
+flag is given,
+.B auth
+will print the name of the authenticated user to its diagnostic stream.
+.TP
+\f5cat\fP [\fIfd\fP...] -> \fIfd\fP
+.B Cat
+reads all the data from each
+.I fd
+in sequence
+and writes it to its resulting
+.IR fd .
+.TP
+\f5create\fP \fIfd\fP \fIf\fP -> \fIstatus\fP
+.B Create
+creates a file named
+.I f
+and writes to it all the data from
+.IR fd .
+.I Status
+will be empty if the writing has completed successfully.
+.TP
+\f5dial\fP \fIaddr\fP -> \fIwfd\fP
+.B Dial
+makes a connection to network address
+.IR addr
+(see
+.IR dial (2)
+for the address format),
+and returns the resulting connection.
+.TP
+\f5echo\fP [-\fIn\fP] \fIstring\fP -> \fIfd\fP
+.B Echo
+writes its argument
+.I string
+to its resulting
+.IR fd .
+If the
+.B -n
+option is given, no newline will be appended.
+.TP
+\f5export\fP \fIdir\fP -> \fIwfd\fP
+.B Export
+exports the namespace rooted at
+.I dir
+and serves it as a styx service on
+.IR wfd .
+.TP
+\f5fd\fP \fIn\fP -> \fIwfd\fP
+.B Fd
+takes file descriptor
+.IR n ,
+and makes it available for reading and/or writing
+as
+.IR wfd .
+.TP
+\f5filter\fP \fIfd\fP \fIcmd\fP \fIarg\fP... -> \fIfd\fP
+.B Filter
+starts the shell command
+.IR cmd ,
+and pipes through this all the data from its
+argument
+.I fd
+to its resulting
+.IR fd .
+The provided
+.IR arg uments
+are accessible in the shell command as
+.BR $* .
+.TP
+\f5mount\fP [\f5-abc\fP] [\f5-x\fP \fIaname\fP] \fIwfd\fP \fIdir\fP -> \fIstatus\fP
+.B Mount
+mounts a connection to a styx server (\fIwfd\fP)
+onto
+.IR dir .
+The meaning of the
+.BR -a ,
+.BR -b ,
+and
+.B -c
+flags is the same as for
+.IR mount (1).
+.IR Aname ,
+if given, gives the attach name that will be passed with the mount request.
+'\".TP
+'\"\f5par\fP \fIstatus\fP... -> \fIstatus\fP
+'\".B Par
+'\"allows all its arguments to run in parallel.
+'\"Its exit status is that of the last argument that
+'\"returned a non-clean status.
+.TP
+\f5parse\fP \fIstring\fP -> \fIcmd\fP
+.B Parse
+parses
+.I string
+as a shell command or alphabet expression,
+and returns the result.
+.TP
+\f5print\fP \fIfd\fP \fIn\fP -> \fIstatus\fP
+.B Print
+writes all the data from
+.I fd
+to file descriptor
+.IR n .
+.TP
+\f5pretty\fP \fIcmd\fP -> \fIstring\fP
+.B Pretty
+returns a string representation of the alphabet expression
+.IR cmd
+which is intended to be easier to read.
+.TP
+\f5read\fP \fIf\fP -> \fIfd\fP
+.B Read
+reads the data from file
+.I f
+and writes it to its resulting
+.IR fd .
+.TP
+\f5rewrite\fP [\f5-d\fP \fIdsttype\fP] \fIcmd\fP \fIcmd\fP -> \fIcmd\fP
+.B Rewrite
+rewrites an alphabet expression to its canonical form,
+applying all auto-conversions, expanding all definitions,
+expanding pipe notation and checking that all types are compatible.
+The first
+.I cmd
+argument
+gives the
+.I alphabet
+expression to be rewritten;
+the second
+.I cmd
+should contain shell commands acceptable to
+.IR sh-alphabet (1),
+declaring all the modules used in the expression.
+If
+.I dsttype
+is given, it specifies the return type of the final expression;
+auto-conversions will be applied to attain this type, if possible.
+'\".TP
+'\"\f5seq\fP [\f5-ao\fP] \fIstatus\fP... -> \fIstatus\fP
+'\".B Seq
+'\"allows each of its arguments to run in sequence.
+'\"If the
+'\".B -a
+'\"flag is given, the first non-clean status it encounters
+'\"will cause it to terminate all subsequent arguments.
+'\"If the
+'\".B -o
+'\"flag is given, the first
+'\".I clean
+'\"status does the same.
+'\"Note that some commands (e.g.
+'\".BR create )
+'\"do some work regardless of sequence.
+'\"The resulting status is that of the last command
+'\"that was not terminated.
+.TP
+\f5unparse\fP \fIcmd\fP -> \fIstring\fP
+.B Unparse
+is the inverse operation to
+.BR parse :
+it converts
+.I cmd
+to a string, and returns the result.
+.TP
+\f52fd\fP \fIwfd\fP -> \fIfd\fP
+.B 2fd
+converts the read-write file
+.I wfd
+to the
+read-only
+.IR fd .
+.SH SOURCE
+.BR /appl/alphabet/alphabet.b
+.br
+.B /appl/alphabet/main/*.b
+.SH SEE ALSO
+.IR sh-alphabet (1),
+.IR alphabet-main (2),
+.IR sh (1)
--- /dev/null
+++ b/man/1/ar
@@ -1,0 +1,163 @@
+.TH AR 1
+.SH NAME
+ar \- archive maintainer
+.SH SYNOPSIS
+.B ar
+.I key
+[
+.I posname
+]
+.I afile
+[
+.I file ...
+]
+.SH DESCRIPTION
+.I Ar
+maintains groups of files
+combined into a single archive file,
+.IR afile .
+The main use of
+.I ar
+is to produce and manipulate UNIX archive files, for instance to create
+program packages for Debian Linux.
+Note that only
+.IR iar (10.1)
+can manage archives used as object file libraries by the Inferno and Plan 9 loaders.
+.PP
+.I Key
+is one character from the set
+.BR drqtpmx ,
+optionally concatenated with
+one or more of
+.BR vuaibclo .
+The
+.I files
+are constituents of the archive
+.IR afile .
+The meanings of the
+.I key
+characters are:
+.TP
+.B d
+Delete 
+.I files 
+from the archive file.
+.TP
+.B r
+Replace
+.I files 
+in the archive file, or add them if missing.
+Optional modifiers are
+.RS
+.PD0
+.TP
+.B u
+Replace only files with
+modified dates later than that of
+the archive.
+.TP
+.B a
+Place new files after
+.I posname
+in the archive rather than at the end.
+.TP
+.BR b " or " i
+Place new files before
+.I posname
+in the archive.
+.RE
+.PD
+.TP
+.B q
+Quick.  Append
+.I files 
+to the end of the archive without checking for duplicates.
+Avoids quadratic behavior in
+.LR "for (i in *.v) ar r lib.a $i" .
+.TP
+.B t
+List a table of contents of the archive.
+If names are given, only those files are listed.
+.TP
+.B p
+Print the named files in the archive.
+.TP
+.B m
+Move the named files to the end or elsewhere,
+specified as with 
+.LR r .
+.TP
+.B o
+Preserve the access and modification times of files
+extracted with the
+.B x
+command.
+.TP
+.B x
+Extract the named files.
+If no names are given, all files in the archive are
+extracted.
+In neither case does
+.B x
+alter the archive file.
+.TP
+.B v
+Verbose.
+Give a file-by-file
+description of the making of a
+new archive file from the old archive and the constituent files.
+With
+.BR p ,
+precede each file with a name.
+With
+.BR t ,
+give a long listing of all information about the files,
+somewhat like a listing by
+.IR ls (1),
+showing
+.br
+.ns
+.IP
+.B
+	mode uid/gid size date name
+.TP
+.B c
+Create.
+Normally
+.I ar
+will create a new archive when
+.I afile
+does not exist, and give a warning.
+Option 
+.B c
+discards any old contents and suppresses the warning.
+.SH EXAMPLE
+.TP
+.L
+ar cr arch.a manifest *.tar.gz
+Replace the contents of
+.L arch.a
+with the
+.B manifest
+file and all the
+.IR gzip (1)'d
+.I tar
+files in the current directory.
+.SH FILES
+.TF /tmp/art.*.*
+.TP
+.B /tmp/art.*.*
+temporaries
+.SH SOURCE
+.B /appl/cmd/ar.b
+.SH "SEE ALSO"
+.IR 2l (10.1),
+.IR iar (10.1),
+.IR ar (10.6)
+.SH BUGS
+If the same file is mentioned twice in an argument list,
+it may be put in the archive twice.
+.br
+The file format used by this command is taken from UNIX, and
+makes some invalid assumptions,
+for instance that user IDs are numeric.
--- /dev/null
+++ b/man/1/asm
@@ -1,0 +1,48 @@
+.TH ASM 1
+.SH NAME
+asm, disdump \- Dis assembler, Dis disassembler
+.SH SYNOPSIS
+.B asm
+.RB [ -l ]
+.I file
+.br
+.B disdump
+.IR file ...
+.SH DESCRIPTION
+.I Asm
+reads one Dis assembly language
+.I file
+and translates it
+into instructions for the Dis virtual machine.
+The output is written to a file whose name is created
+by taking the last element
+of the input file, stripping any extension, and appending
+.B \&.dis
+For example, the output file for
+.B abc
+would be
+.BR abc.dis ;
+the output file for
+.BR dir/def.s ,
+would be
+.BR def.dis .
+.PP
+The assembler has one option:
+.TP
+.B -l
+Generate a listing, showing the generated object code.
+.PP
+.I Disdump
+prints to the standard output the Dis virtual machine
+instructions in each of its Dis
+.I file
+arguments.
+.SH SOURCE
+.B /appl/cmd/asm
+.br
+.B /appl/cmd/disdump.b
+.SH "SEE ALSO"
+.IR emu (1),
+.IR limbo (1)
+.PP
+``The Dis Virtual Machine'' in Volume 2.
--- /dev/null
+++ b/man/1/auplay
@@ -1,0 +1,120 @@
+.TH AUPLAY 1
+.SH NAME
+auplay, auhdr, raw2iaf, wav2iaf \- basic audio output and conversion
+.SH SYNOPSIS
+.B auplay
+.I file
+\&...
+.PP
+.B auhdr
+.I file
+\&...
+.PP
+.B raw2iaf
+[
+.I option
+\&...
+] [
+.B -o
+.I output
+]
+.I input
+.PP
+.B wav2iaf
+[
+.I input
+]
+.PP
+.B wav2iaf
+.SH DESCRIPTION
+.I Auplay
+plays each
+.I file
+in turn on the audio device
+.B /dev/audio
+(see
+.IR audio (3)),
+setting the device's characteristics in
+.B /dev/audioctl
+to match those of the
+.IR file .
+It uses
+.I stream
+(see
+.IR sys-read (2))
+to stream the data to the device at high priority.
+All files played must be in `Inferno audio format',
+as defined by
+.IR audio (6).
+.PP
+.I Auhdr
+writes the header of each Inferno audio
+.I input
+file to the standard output.
+The header describes the data in a form that can be written directly to
+.BR /dev/audioctl
+to set the device's characteristics.
+.PP
+.I Raw2iaf
+converts the
+.I input
+file, adds an appropriate header to describe the data
+in the Inferno format, and writes the result to the
+.I output
+file.
+The options tell how the bytes in the input file should be interpreted:
+.TP
+.B -8
+rate is 8000 Hz
+.TP
+.B -1
+rate is 11025 Hz
+.TP
+.B -2
+rate is 22050 Hz
+.TP
+.B -4
+rate is 44100 Hz
+.TP
+.B -m
+mono (one channel)
+.TP
+.B -s
+stereo (two channels)
+.TP
+.B -b
+each sample in each channel is one byte (unsigned)
+.TP
+.B -w
+each sample in each channel is 16-bits (little-endian)
+.TP
+.B -a
+input is a-law encoded
+.TP
+.B -u
+input is \(*m-law encoded
+.TP
+.B -p
+input is PCM encoded
+.PP
+.I Wav2iaf
+reads the
+.I input
+file, which must be in Windows WAV format and encoded using PCM,
+and converts the data to Inferno format
+on the standard output.
+.SH SOURCE
+.B /appl/cmd/auplay.b
+.br
+.B /appl/cmd/raw2iaf.b
+.br
+.B /appl/cmd/wav2iaf.b
+.SH SEE ALSO
+.IR sys-read (2),
+.IR audio (3),
+.IR audio (6)
+\" sets rate, chans, bits, enc
+.\" raw2iaf: -8 8000 -1 11025 -2 22050 -4 44100 -m [chan=1] -s [chan=2] -b [byte] -w [16-bit] -a [alaw] -u [ulaw] -p [pcm] [-o output|-] [input|-]
+.\" %s -8124 -ms -bw -aup -o out in
+.\" wav2iaf [infile]
+
--- /dev/null
+++ b/man/1/avr
@@ -1,0 +1,47 @@
+.TH AVR 1
+.SH NAME
+avr \- Atmel AVR support
+.SH SYNOPSIS
+.B avr/burn
+.RB [ -e ]
+.RB [ -r ]
+[
+.BI "-d " device
+]
+.I file.srec
+.SH DESCRIPTION
+Currently there is just one Inferno application to support Atmel AVRs
+but others should appear.
+.PP
+.I Burn
+initialises or verifies the contents of an Atmel ATmega128 AVR.
+The programming board must be connected to a serial port (see
+.IR eia (3)),
+.B /dev/eia0
+by default.
+.I File.srec
+is a file containing the desired flash contents in Motorola S-record format,
+as produced by
+.IR ms2 (10.1).
+By default,
+.I burn
+erases the AVR chip (both flash and EEPROM), then loads the flash with the contents of
+.IR file.srec .
+The
+.B -e
+option stops
+.I burn
+from erasing first, allowing additional code or data to be loaded (but only into an already-erased region).
+If the
+.B -r
+option is given,
+.I burn
+instead compares the contents of the AVR with the data in
+.IR file.srec .
+.I Burn
+supports the MIB510 programming board from Crossbow Technology Inc.
+.SH SOURCE
+.B /appl/cmd/avr/burn.b
+.SH "SEE ALSO"
+.IR eia (3),
+.IR ms2 (10.1)
--- /dev/null
+++ b/man/1/basename
@@ -1,0 +1,40 @@
+.TH BASENAME 1 
+.SH NAME
+basename \- strip file name affixes
+.SH SYNOPSIS
+.B basename
+[
+.B -d
+]
+.I string
+[
+.I suffix
+]
+.SH DESCRIPTION
+.I Basename
+deletes any prefix ending in slash
+.RB ( / )
+and the
+.IR suffix ,
+if present in
+.IR string ,
+from
+.IR string ,
+and prints the result on the standard output.
+.PP
+The
+.B -d
+option instead prints the directory component,
+that is,
+.I string
+up to but not including the final slash.
+If the string contains no slash,
+the current directory name
+.L .
+is printed.
+.SH SOURCE
+.B /appl/cmd/basename.b
+.SH SEE ALSO
+.IR cleanname (1),
+.IR pwd (1),
+.IR names (2)
--- /dev/null
+++ b/man/1/bind
@@ -1,0 +1,283 @@
+.TH BIND 1
+.SH NAME
+bind, mount, unmount \- change name space
+.SH SYNOPSIS
+.B bind
+[
+.I option ...
+]
+.I source target
+.PP
+.B mount
+[
+.I option ...
+]
+.I source target
+[
+.I spec
+]
+.PP
+.B unmount
+[
+.I source
+]
+.I target
+.SH DESCRIPTION
+The
+.IR bind
+and
+.IR mount
+commands modify the file name space of the current process
+and other processes in the same name space group
+(see
+.IR sys-pctl (2)).
+For both calls, 
+.I target
+is the name of an existing file or directory in the
+current name space where the modification is to be made.
+.PP
+For
+.IR bind ,
+.I source
+is the name of an existing file or directory in the current name space.
+After a successful
+.IR bind ,
+the file name
+.I target
+is an alias for the object originally named by
+.IR source ;
+if the modification doesn't hide it,
+.I source
+will also still refer to its original file.
+The evaluation of
+.I source
+(see
+.IR sys-intro (2))
+happens at the time of the
+.IR bind ,
+not when the binding is later used.
+.PP
+Both
+.I source
+and
+.I target
+files must be of the same type: either both directories or both files.
+.PP
+For 
+.IR mount,
+.I source
+can be a 
+shell command,
+a network address,
+or a file name.
+If
+.I source
+is surrounded by brace characters
+.RB ( {
+and
+.BR } ),
+it is invoked as a
+.IR sh (1)
+command and its standard input is mounted (no
+authentication takes place in this case).
+If
+.I source
+contains an exclamation mark
+.RB ( ! ),
+or there is no file of that name,
+it is assumed to be a network address for a machine acting
+as a file server.
+This argument should then conform to the conventions
+described in
+.IR dial (2).
+Otherwise
+.I source
+should be the name of a file that when opened gives a connection
+to a file server, something serving the 9P protocol
+described in
+.IR intro (5),
+formerly called `Styx'.
+The optional
+.I spec
+argument to
+.I mount
+is passed in the
+.IR attach (5)
+message and selects amongst different file trees offered by the server.
+.PP
+The effects of
+.I bind
+and
+.I mount
+can be undone by
+.IR unmount .
+If two arguments are given to 
+.IR unmount ,
+the effect is to undo a
+.I bind
+or
+.I mount
+with the same arguments. If only one argument is given, everything bound to or mounted on
+.I target
+is unmounted.
+.PP
+By default,
+.I bind
+and
+.IR mount
+replace the
+.I target
+file by the new one,
+.IR source .
+Henceforth, an evaluation of the pathname
+.I target
+will be translated to the new file.
+If they are directories (for
+.IR mount ,
+this condition is true by definition),
+.I target
+becomes a
+.I "union directory"
+consisting of one directory (the 
+.IR source
+directory).
+.PP
+A union directory unites the contents of the source and target directories.
+If the same name appears in both directories, the name used is the one in the
+directory that is bound before the other.
+In particular, if the directories have subdirectories of the same name, only
+the contents of the subdirectory in the top directory will be seen.
+If the subdirectory contents are themselves to be united, that must be done
+first in a separate
+.I bind
+or
+.IR mount .
+.PP
+Note that the
+.B #
+character in the name of a kernel device
+must be quoted when used in a
+.I bind
+or
+.I unmount
+command, or the shell will take it as the start of a comment.
+.PP
+Options control aspects of the modification to the name space:
+.TP
+.B -b
+Both files must be directories.
+Add the 
+.IR source
+directory to the beginning
+of the union directory represented by the 
+.IR target 
+directory.
+.TP
+.B -a
+Both files must be directories.
+Add the 
+.IR
+source 
+directory to the end
+of the union directory represented by the 
+.IR target 
+directory.
+.TP
+.B -c
+This can be used in addition to any of the above to permit
+creation in a union directory.
+When a new file is created in a union directory,
+it is placed in the first element of the union that has been
+bound or mounted with the
+.B -c
+option.
+If that directory has not got write permission,
+the create fails.
+.TP
+.B -q
+Exit quietly without printing a diagnostic if the bind or mount fails.
+.TP
+.B -A
+For 
+.I mount
+only. Do not authenticate the connection to the server before proceeding with
+.IR mount .
+Otherwise the connection is authenticated by
+.IR security-auth (2).
+.TP
+.BI -C " alg"
+For
+.I mount
+only,
+specify the algorithm, 
+.IR alg ,
+to be used following authentication for digesting or encryption. See 
+.IR ssl (3)
+for the supported algorithms.
+The default is
+.BR none :
+.IR ssl (3)
+is not used after authentication.
+.TP
+.BI -k " kfile "
+For mount only, specify the keyfile to be used when authenticating.
+The default is
+.BI /usr/ user /keyring/default .
+See 
+.IR keyring-auth (2)
+for more details.
+(If the
+.B -9
+option is given, this option is interpreted differently: see below.)
+.TP
+.B -9
+For
+.I mount
+only, and only when hosted on Plan 9.
+.I Source
+is a Plan 9 file server; use Plan 9's
+.I factotum
+as authentication agent to authenticate the mount.
+(Note that a Plan 9 file service that is known not to authenticate can be mounted from
+any Inferno host, by using the
+.B -A
+option to suppress Inferno authentication.)
+The existing Plan 9 file servers do not encrypt connections, so the
+.B -C
+option is ignored.
+The value of the
+.B -k
+option is added to the key specification for
+.IR factotum (4)
+for authentication.
+.TP
+.B -P
+When
+.I source
+is a network address, use
+.IR styxpersist (2)
+to try to simulate a permanent connection, even should the server reboot.
+Note the caveats on that page.
+.TP
+.B -o
+For
+.I mount
+only,
+the file server serves the 1995 version of Inferno's Styx protocol, and
+.I mount
+inserts a process that translates to the current version.
+.SH SOURCE
+.B /appl/cmd/bind.b
+.br
+.B /appl/cmd/mount.b
+.br
+.B /appl/cmd/unmount.b
+.SH SEE ALSO
+.IR sh (1),
+.IR dial (2),
+.IR keyring-auth (2),
+.IR security-auth (2),
+.IR sys-intro (2),
+.IR sys-bind (2),
+.IR intro (3),
+.IR getauthinfo (8)
--- /dev/null
+++ b/man/1/blur
@@ -1,0 +1,74 @@
+.TH BLUR 1
+.SH NAME
+blur \- an example program to demonstrate splitting a task over several machines.
+.SH SYNOPSIS
+.TP 40
+.B grid/demo/blur [imagefile]
+- master process
+.TP
+.B grid/demo/blur
+- slave process
+.SH DESCRIPTION
+.I Blur
+is a small program that works in two parts to manipulate an
+.IR image (6).
+The master process takes an image file as an argument and displays the image in a window on screen whilst waiting for and displaying results from the slave processes. Each slave process takes a block of the image at a time and blurs it, reduces the contrast and overlays the result of a simple edge detection analysis.
+.PP
+The only requirement for a master and slave process to work together is that they both have a common
+.B /tmp
+directory. Within this a
+.B /blur
+directory is created and used by both processes. All communication and synchronisation is done through files in this directory. There can be many slave processes running concurrently in order to improve performance.
+
+.SH COMMUNICATION
+All the communication takes place through files in the
+.B /tmp/blur 
+directory which is common to all the processses. Once the master process has started, it creates:
+.TP 17
+.B image.bit
+the image being processed
+.TP
+.B data.dat
+processing parameters e.g. block size, blur radius etc
+.TP
+.B working
+tells slave processes to continue reading
+.B todo/
+directory
+.TP
+.B todo/
+contains files showing which blocks still need processing
+.PP
+Within the
+.B todo/
+directory, the master process creates a file for each block to be processed. Starting at
+.BR block.0.a ,
+the name of each file denotes the block number and the version id. The version id is used to indicate the current version of a block being worked on. This is to allow the master process to ask another slave process to work on a block if it believes the current one has crashed or is taking too long. Once a block has been processed, the master removes the file from
+.BR todo/ .
+.PP
+Each slave process reads the list of files in
+.B todo/
+and for each block in turn, attempts to create a directory with the same name e.g.
+.B block.1.a/
+in
+.BR /tmp/blur .
+It is not possible to create a directory if it already exists and as the slave will only process blocks for which it
+.I has
+been able to create a directory, no two slave processes can be simultaneously working on the same block. Once a slave has managed to create a directory, it processes the relevant block of the image and writes the result to 
+.B img.bit
+in the directory. Having completed a block, the slave creates a file called
+.B done
+in order to let the master process know that it can read the completed result.
+.PP
+The master process keeps track of all the blocks being processed and if any of them have not been completed after a certain time, it creates a new file for that block in 
+.B todo/
+with a new version id. This guarantees that all blocks will be processed as long as at least one working slave process remains. Once all the blocks have been processed, the master process removes all the files created in
+.BR /tmp/blur .
+Each slave, upon seeing that the 
+.B working
+file has gone, will then exit.
+
+.SH SOURCE
+.B /appl/grid/demo/blur.b
+.br
+.B /appl/grid/demo/block.b
--- /dev/null
+++ b/man/1/brutus
@@ -1,0 +1,120 @@
+.TH BRUTUS 1
+.SH NAME
+brutus \- screen editor with support for SGML
+.SH SYNOPSIS
+.B wm/brutus
+[
+.I file
+]
+.SH DESCRIPTION
+.I Brutus
+is a multi-file editor for UTF format text files.
+.PP
+When editing multiple files, each file appears in its own window.
+Button 1 can be used to make a window current. Within the current
+window, button 1 selects text.
+Double clicking selects text up to the boundaries of words, lines,
+quoted text or bracketed text depending upon the text at the point of the
+double click. Double clicking at the start of a line
+selects the entire line. Double clicking just inside various forms of
+brace selects text up to the matching brace, correctly handling nested braces.
+Button 2 displays a menu of editing commands:
+.TF snarf
+.TP
+.B cut
+Delete the selected text and save it in the snarf buffer.
+.TP
+.B paste
+Replace the selected text by the contents of the snarf buffer.
+.TP
+.B snarf
+Save the selected text in the snarf buffer.
+.TP
+.B look
+Search forwards and select the next occurrence of the selected text.
+.PD
+.PP
+Mouse chording is implemented, as in
+.IR acme (1).
+Dragging a selection with button-1 held down and then also clicking
+button-2 cuts the selected text into the Snarf buffer.
+Clicking button-3 instead of button-2 replaces the selected text with the contents
+of the Snarf buffer.
+.PP
+Clicking button 3 extracts the whitespace-bounded string around the point of the
+click and plumbs it to the appropriate application (see
+.IR plumber (8)).
+.PP
+A
+.I brutus
+console window is always displayed from which new files may be opened
+or from which existing open files may be selected.
+Typing
+.IP
+.EX
+.BI / word
+.EE
+.PP
+in the console window will search for the character sequence
+.I word
+in the file associated with the current window. Typing
+.IP
+.EX
+.BI ? word
+.EE
+.PP
+in the console window will search backwards for the character sequence
+.IR word .
+If text has been selected in the current window the search begins from the
+end of the selection if searching forwards and the beginning of the selection if
+searching backwards.
+If no text has been selected the search begins from the current insertion point.
+Typing
+.IP
+.EX
+.I linenumber
+.EE
+.PP
+in the console window selects all the text on line
+.I linenumber
+and moves the window to show the selected text.
+.SS SGML
+If the first line of a file is exactly:
+.IP
+.EX
+<SGML>
+.EE
+.PP
+it is assumed to be in SGML format and the contents are displayed according
+to some predefined formatting rules. Tags of the form
+.BI < font . size >
+are recognised and used to control the visual appearance
+of text. The
+.I font
+may be one of:
+.BR Roman ,
+.BR Italic , 
+.BR Bold ,
+and
+.B Type
+giving normal, italicised, emboldened, and constant width text.
+The
+.I size
+may be one of
+.BR 6 ,
+.BR 8 ,
+.BR 10 ,
+.BR 12 ,
+or
+.BR 16 ,
+and determines the point size of the displayed text.
+.SH PLUMBING
+.B wm/brutus
+listens for
+.B edit
+messages and plumbs text selected by button 3
+.SH SOURCE
+.B /appl/wm/brutus.b
+.SH SEE ALSO
+.IR acme (1),
+.IR cook (1)
--- /dev/null
+++ b/man/1/cal
@@ -1,0 +1,46 @@
+.TH CAL 1
+.SH NAME
+cal \- print calendar
+.SH SYNOPSIS
+.B cal
+[
+.I month
+]
+[
+.I year
+]
+.SH DESCRIPTION
+.I Cal
+prints a calendar.
+.I Month
+is either a number from 1 to 12,
+a lower case month name,
+or a lower case three-letter prefix of a month name.
+.I Year
+can be between 1
+and 9999.
+If either
+.I month
+or
+.I year
+is omitted, the current month or year is used.
+If only one argument is given, and it is a number larger than 12,
+a calendar for all twelve months of the given year is produced;
+otherwise a calendar for just one month is printed.
+The calendar
+produced is that for England and her colonies.
+.PP
+Try
+.EX
+	cal sep 1752
+.EE
+.SH SOURCE
+.B /appl/cmd/cal.b
+.SH BUGS
+The year is always considered to start in January even though this
+is historically naive.
+.br
+Beware that
+.L "cal 90"
+refers to the early Christian era,
+not the 20th century.
--- /dev/null
+++ b/man/1/calc
@@ -1,0 +1,272 @@
+.TH CALC 1
+.SH NAME
+calc \- calculator language
+.SH SYNOPSIS
+.B calc
+[
+.B -s
+] [
+.I file
+]
+.PP
+.B calc
+[
+.B -s
+] [
+.I expression
+]
+.SH DESCRIPTION
+.I Calc
+interprets a simple language for floating-point arithmetic
+with Limbo-like syntax and
+functions.
+.PP
+If no
+.I file
+or
+.I expression
+is given
+.I calc
+interprets the standard input.
+.PP
+.I Calc
+input consists of
+.I expressions
+and
+.IR statements .
+Expressions are evaluated and their results printed.
+Statements, typically assignments and function
+definitions, produce no output unless they explicitly call
+.IR print .
+.PP
+Comments begin with # and extend to the end of the line as in Limbo.
+.PP
+Numbers may have a base specified using C or Limbo syntax.
+.PP
+Variable names have the usual syntax, including 
+.LR _ .
+They may be introduced without a declaration and have an initial default value
+of 0.0.
+.PP
+The predefined variable
+.B degrees
+can be set to specify angles in degrees rather than radians in the trigonometric functions below. It is initially 0 (angles in radians by default).
+.PP
+The predefined variable
+.B printbase
+can be set to any integer between 2 and 36 inclusive to specify
+the base of all values output.
+.PP
+The constants
+.BR e ,
+.BR Pi (π) ,
+.BR Phi (φ) ,
+.BR Gamma (γ) ,
+.BR Infinity (∞) ,
+and
+.BR Nan (NaN)
+are predefined.
+.PP
+Expressions are formed with these Limbo-like operators, listed by
+decreasing precedence.
+.TP
+.B ! ~ + - ++ --
+.TP
+.B **
+.TP
+.B * / % //
+.TP
+.B + -
+.TP
+.B << >>
+.TP
+.B > >= < <= <->
+.TP
+.B == != -> <-
+.TP
+.BR & " " " " ↑
+.TP
+.B ^
+.TP
+.BR | " " " " ↓
+.TP
+.B &&
+.TP
+.B ||
+.TP
+.B ? :
+.TP
+.B = := += -= *= /= %= //= &= ^= |= <<= >>=
+.TP
+.B ,
+.PP
+If the
+.B -s
+flag is given, a strict interpretation of the declaration rules are enforced - all variables must be declared and initialized first using the 
+.B :=
+operator. Otherwise undeclared variables are declared and initialized to 0.0 in the
+current scope. In either case, 
+.B :=
+always forces a new declaration.
+.PP
+The extra non-Limbo operators are factorial (! when postfix), integer division (//),
+conditional (? and :) comma (,), logical equivalence (<->), implication (->), reverse implication (<-), nand (↑) and nor (↓).
+.PP
+Unary operators, assignment operators, **, ? and : are right associative as usual.
+.PP
+The comma operator may be replaced by white space in expressions.
+.PP
+Built in functions are
+.BR abs ,
+.BR acos ,
+.BR acosh ,
+.BR asin ,
+.BR asinh ,
+.BR atan ,
+.BR atanh ,
+.BR atan2 ,
+.BR cbrt ,
+.BR ceiling ,
+.BR cos ,
+.BR cosh ,
+.BR erf ,
+.BR exp ,
+.BR floor ,
+.BR frac ,
+.BR gamma (Γ) ,
+.BR int ,
+.BR log ,
+.BR log10 ,
+.BR max ,
+.BR min ,
+.BR pow ,
+.BR rand ,
+.BR round ,
+.BR sign ,
+.BR sin ,
+.BR sinh ,
+.BR sqrt ,
+.BR tan ,
+and
+.BR tanh .
+.PP
+Functions of one argument may be written without brackets:
+.sp
+.EX
+	sin 45
+	sqrt 2
+.EE
+.sp
+These behave as unary operators with the highest precedence.
+.PP
+Sum and product operators are available using sigma (Σ) and pi (Π).
+For example
+.sp
+.EX
+	sigma(i = 0, 100, 1/i!)
+.EE
+.sp
+gives the value 2.7182818284590455 .
+.PP
+Simple definite integration can be done:
+.sp
+.EX
+	integral(x = -1.96, 1.96, exp(-0.5*x*x)/sqrt(2*Pi))
+.EE
+.sp
+outputs 0.9500042096998785 .
+∫ may be used in place of integral.
+.PP
+For the sake of completeness, the derivative of a function at a given
+point can be calculated:
+.sp
+.EX
+	differential(x=1, x*x+5*x+6)
+.EE
+.sp
+gives 7.
+Δ may be used in place of differential.
+.PP
+There is limited support for the solution of equations.
+For example
+.sp
+.EX
+	solve(x**2-5*x+6==0)
+.EE
+.sp
+outputs the values 2 and 3. The value returned by
+.B solve
+is the largest of the roots. To specify the variable to solve for, if
+ambiguous, simply add it as a second parameter as in, for example,
+.sp
+.EX
+	solve(x**2-5*x+6==y**3+z, x)
+.EE
+.sp
+This will substitute the current values of
+.B y
+and
+.B z
+and solve for
+.B x.
+To tune the solution process, the predefined variables
+.B solvelimit
+(default value 100) and
+.B solvestep
+(default value 1) are available.
+The former specifies the maximum absolute solution
+to search for. The latter
+specifies the interval increment to apply when searching
+for sign changes.
+.PP
+.B Print
+prints a list of expressions that may include
+string constants such as
+\fL"hello\en"\f1.\fP
+.PP
+.B Read
+reads in a list of values interactively. The list of variables to assign
+these values should follow.
+.PP
+Other files may be read in using the Limbo include statement.
+.PP
+Control flow statements are
+.BR break ,
+.BR continue ,
+.BR exit ,
+.BR return ,
+.BR if - else ,
+.BR while ,
+.BR do - while ,
+and
+.BR for ,
+with braces for grouping.
+.PP
+The use of semi-colon and newline is optional.
+.PP
+Functions are introduced by the keyword
+.BR fn .
+.SH EXAMPLE
+.EX
+fn ack(a, b)
+{
+	n = n+1
+	if(a == 0)
+		return b+1;
+	if(b == 0)
+		return ack(a-1, 1);
+	return ack(a-1, ack(a, b-1));
+}
+
+for(i = 0; i < 4; i++)
+	for(j = 0; j < 4; j++){
+		n = 0
+		print "ack(", i, ",", j, ")=", ack(i, j), "\en"
+		print n, " calls", "\en"
+	}
+.EE
+.SH SOURCE
+.B /appl/cmd/calc.b
+.SH "SEE ALSO"
+.IR fc (1),
+.IR math-intro (2)
--- /dev/null
+++ b/man/1/calendar
@@ -1,0 +1,47 @@
+.TH CALENDAR 1
+.SH NAME
+calendar \- calendar and diary
+.SH SYNOPSIS
+.B wm/calendar
+[
+.IR mntdir | datafile
+]
+.br
+.SH DESCRIPTION
+.B Calendar
+is a simple calendar and diary program.
+Its optional argument specifies either a directory
+on which has been mounted a
+.I rawdbfs
+(see
+.IR dbfs (4))
+filesystem, or a file in which to store schedule entries
+(which must already exist). In the latter case,
+.I calendar
+starts up an instance of
+.I rawdbfs
+to serve the file. The default argument to
+.I calendar
+is
+.BR /mnt/schedule .
+If two instances of calendar
+are using the same datafile, care should be taken to run
+.I rawdbfs
+.I before
+running the
+.I calendar
+programs, otherwise the data file will be corrupted.
+.SH EXAMPLE
+Start
+.I calendar
+using
+.B $home/cal
+for the data entries:
+.EX
+	rawdbfs $home/cal /mnt/schedule
+	wm/calendar
+.EE
+.SH SOURCE
+.B /appl/wm/calendar.b
+.SH SEE ALSO
+.IR dbfs (4)
--- /dev/null
+++ b/man/1/cat
@@ -1,0 +1,49 @@
+.TH CAT 1
+.SH NAME
+cat \- concatenate files
+.SH SYNOPSIS
+.B cat
+[
+.B -
+]
+[
+.I file ...
+]
+.SH DESCRIPTION
+.I Cat
+reads each
+.I file
+in turn and writes it on the standard output.
+Thus
+.IP
+.EX
+cat file
+.EE
+.PP
+prints the file to standard output, and the following
+.IP
+.EX
+cat file1 file2 >file3
+.EE
+.PP
+concatenates two files onto a third.
+.PP
+If no
+.I file
+is given, or where
+.B \-
+is given as an argument,
+.I cat
+reads from the standard input.
+Output is buffered in blocks matching the input.
+.SH SOURCE
+.B /appl/cmd/cat.b
+.SH "SEE ALSO"
+.IR cp (1),
+.IR stream (1)
+.SH BUGS
+Beware of
+.B "cat a b >a"
+and
+.B "cat a b >b"
+which destroy input files before reading them.
--- /dev/null
+++ b/man/1/cd
@@ -1,0 +1,32 @@
+.TH CD 1
+.SH NAME
+cd \- change working directory
+.SH SYNOPSIS
+.B cd
+[
+.I directory
+]
+.SH DESCRIPTION
+.I Cd
+changes the working directory for the shell and all processes in its
+name space group (see
+.IR sys-pctl (2))
+to
+.IR directory .
+If no argument is given,
+.I cd
+attempts to change to
+.BI /usr/ user
+where
+.I user
+is the name read from
+.BR /dev/user .
+.SH FILES
+.B /dev/user
+.SH SOURCE
+.B /appl/cmd/cd.b
+.SH "SEE ALSO"
+.IR pwd (1),
+.IR sys-chdir (2),
+.IR sys-pctl (2),
+.IR cons (3)
--- /dev/null
+++ b/man/1/charon
@@ -1,0 +1,442 @@
+.TH CHARON 1
+.SH NAME
+charon \- web browser
+.SH SYNOPSIS
+.B charon
+[
+.BI - option
+.RI [ value ]
+]
+.RI [ url ]
+.SH DESCRIPTION
+.I Charon
+is the Inferno graphical web browser, supporting a variety of web standards for
+the download, viewing, automation and navigation of web based information and images.
+.PP
+The browser runs under the
+.IR wm (1)
+window manager, or directly on the
+.IR draw (3)
+and
+.IR cons (3)
+devices.
+When
+.I charon
+starts, it checks to see if
+.I wm
+is running; if it is then
+.I charon
+creates a new
+.I wm
+window for its display, otherwise it uses the whole area of the display device.
+.PP
+.I Charon
+implements an image cache to help reduce the overhead of revisiting pages.
+The image cache resides in memory for the duration of a session.
+The cache is managed by discarding least recently used images once the cache bounds
+have been reached.
+Currently,
+.I charon
+does not provide a general purpose web cache; all other resources have to be reloaded
+when needed.
+.SS Navigation
+.I Charon
+provides navigation controls familiar to any web-surfer:
+back, forward, reload, home, stop and URL entry.
+Navigation of web-based information is by means of following HTML
+.IR links,
+this is done by clicking on them using mouse button-1, or the touch-screen.
+Clicking mouse button-2 on a link causes its address to be displayed in the
+.I status
+line of
+.IR charon 's
+display.
+Navigation to other information is achieved by clicking on the URL entry
+field and typing the address of the resource, followed by the Enter key.
+.PP
+The retrieval and processing of the current page
+is immediately terminated by clicking the Stop button.
+.PP
+As resources are browsed, a history of their web addresses (URLs) is built up.
+At any time you can navigate forwards and backwards through this list using
+the Forward and Back buttons.
+Additionally, clicking the History button, displays the entire history list as a set of
+.I links
+enabling the user to quickly jump to any position in the list.
+.PP
+Sometimes, it is not possible to retrieve all of the components of a web document at the time
+that it is requested.
+Often this is because the remote server is very busy or not available.
+This can result in the presentation of the document being incomplete or even corrupted.
+Clicking the Reload button causes
+.I charon
+to attempt to retrieve the current document and all of its components again.
+.PP
+.I Charon
+displays a progress panel to indicate the download status of each of the components of the
+current document.
+The progress panel is displayed at the bottom of the
+.I charon
+window.
+Each component of the current document is represented by a rectangular block in the
+progress panel.
+As the download of a component progresses, its corresponding block is gradually filled in.
+If an error occurs while downloading or processing the component, its block is coloured red.
+The component address and amount downloaded, or reason for failure, can
+be obtained by clicking mouse button-1 on its progress block.
+.SS "Web Standards"
+Standards are the life-blood of the World-Wide-Web;
+without them, the web could not exist.
+Web standards are under constant review with revised editions and complete new standards
+being published all of the time.
+This section lists the standards supported by
+.I charon
+at the time of writing this manual entry.
+The version of the standard is given, if appropriate, alongside any comments on its
+implementation.
+.PP
+.TP 15
+Protocols
+HTTP versions 1.0 and 1.1
+.IP
+HTTPS: SSL Protocol versions 2.0 and 3.0.
+X.509.v3 server certificates.
+.IP
+FTP:
+.I charon
+supports retrieval of plain text files only, directory listings are not generated.
+.IP
+FILE:
+.I Charon
+attempts to determine the file type by a combination of the filename extension and
+examination of the first few bytes of the file.  Directory listings are not generated.
+.TP
+HTML
+It is intended that
+.I charon
+supports HTML version 3.2, but in reality there is
+no single standard!
+.I Charon
+attempts to be as close as possible to Netscape Navigator Version 3 in terms of its
+markup support.
+Obvious bugs in Navigator 3 and
+the Netscape security model have not been adopted.
+.TP
+JavaScript
+.I Charon
+implements ECMAscript-262 2nd Edition, which is roughly equivalent to JavaScript1.1.
+The ECMA-262 standard does not define the
+.I host
+objects and classes that should implemented by the browser.
+.I Charon
+implements the set of host objects and classes of Netscape Navigator version 3.
+.TP
+Encodings
+US_Ascii, ISO_8859_1, UTF_8, Unicode (big-endian)
+.TP
+Images
+GIF87a and GIF89a - animated GIFs always loop.
+.br
+JPEG, XBitmap, 
+Inferno BIT
+.IR image (6)
+format.
+.SS Configuration
+While using
+.IR charon ,
+a sub-set of the configuration options can be modified.
+Clicking the Configuration button displays a config popup window, enabling the user
+to modify the current values of the available options.
+.PP
+The full set of configuration options can be modified on the command line or in
+a configuration file.
+Comand line options are processed after the configuration file.
+The configuration file is loaded from
+.BI /usr/ user /charon/config
+where
+.I user
+is obtained by reading the file
+.BR /dev/user .
+If this file does not exist, the file
+.B /services/config/charon.cfg
+is read instead.
+Options are processed in order; some options override settings affected
+by others and so the order in which they are used is important.
+.PP
+Options are specified using a
+.IR key ,
+.I value
+pair.
+On the command line this takes the form:
+.IP
+.BI - key
+.I value
+.PP
+Where
+.BI - key
+and
+.I value
+are individual items in the argument list to
+.IR charon .
+.PP
+In the config file options take the form:
+.IP
+.IB key = value
+.PP
+Where
+.I value
+is the remainder of the input line after the
+.BR ` = '
+character.
+Any text lines in the config file that start with a
+.RB ` # '
+character are ignored as comment lines.
+.SH OPTIONS
+.TF 10
+.TP
+.B userdir
+The directory where
+.I charon
+expects to find its configuration files such as, bookmark and cookie files.
+The default value is
+.BI /usr/ user /charon/
+where
+.I user
+is obtained by reading the file
+.BR /dev/user .
+.TP
+.B starturl
+Specifies the URL of the first document to be displayed by
+.I charon
+at startup.
+The default value is
+.B file://localhost/services/webget/start.html
+.TP
+.B homeurl
+Specifies the URL of the document to retrieve when the Home button is clicked.
+The homeurl can only be changed if the
+.B change_homeurl
+option is enabled.
+Setting 
+.B homeurl
+also set the value of the
+.B starturl
+option.
+The default value is
+.B file://localhost/services/webget/start.html
+.TP
+.B change_homeurl
+Enables editing of the Home URL in the configuration popup window.
+A non-zero integer value enables this option, all other values disable it
+This option also enables the
+.B homeurl option.
+The default value is 1.
+.TP
+.B helpurl
+Specifies the URL of the document to retrieve when the Help button is clicked.
+The default value is
+.B file://localhost/services/webget/help.html
+.TP
+.B httpproxy
+Specifies the host name and port of a web proxy server.
+The address is given in the form of a URL, where the optional port number
+can be specified after the server name by separating them by a colon
+.RB ` : '.
+The default value is the empty string, specifying that no web proxy server should be used.
+.TP
+.BI noproxy " or " noproxydoms
+Specifies a list of network domains for which a web proxy should not be used.
+The domains in the list can be separated by semicolon, comma, space or tab characters.
+The default value is the empty list.
+.TP
+.B usessl
+Extends SSL support.
+Accepted values are
+.RB `` v2 ''
+and
+.RB `` v3 ''.
+Initially SSL support is configured off.
+Enabling version 1 or version 2 support restricts SSL support to that specific version.
+Specifying the option twice, once with each of the options, enables dual version
+SSL support whereby the remote server is probed to determine which version it supports.
+Some servers only support one of the versions and may not tolerate the special
+version2/3 probe.
+.TP
+.B buttons
+Specifies the set of buttons that appear on the window manager title-bar.
+The buttons are given as a list of button names separated by comma, space or tab characters.
+Valid button names are
+.BR help ,
+.B resize
+and
+.BR hide .
+The default value is for all buttons to be displayed.
+.TP
+.BI defaultwidth " or " width
+Set the initial window width.
+This option is only meaningful when running under the window manager.
+If the specified width exceeds the screen width then the screen width is used.
+The default value is 630.
+.TP
+.BI defaultheight " or " height
+Set the initial height of the main display panel, this does not include the height
+of the control and progress panels.
+This option is only meaningful when running under the window manager.
+If the total height of the
+.I charon
+window exceeds the screen height, the main display panel height will be reduced to fit.
+The default value is 450.
+.TP
+.BI x " and/or " y
+Set the initial window position.
+These options are only meaningful when running under the window manager.
+The default value for both options is 0.
+.TP
+.B imagelvl
+Specify how to handle image components of a document.
+This option takes a numeric argument.
+A value of 0 prevents images from being downloaded or displayed.
+A value of 1 will download and display images but not animate GIFS - only the first
+frame of an animated GIF will be displayed.
+A value of 2 or more enables full image processing.
+The default value enables full image processing.
+.TP
+.B imagecachenum
+Specify the maximum number of images that can remain resident in the image cache.
+The default value is 60.
+.TP
+.B imagecachemem
+Specify the maximum amount of image memory available to the image cache in bytes.
+The cache is managed such that neither the
+.B imagecachenum
+nor
+.B imagecachemem
+limits are exceeded.
+The image cache tries to ensure that no more than 80% of available system image memory
+is taken by the cache, irrespective of the value of this option.
+The default value is 80% of the system image memory that was available
+.I when charon was started.
+.TP
+.B docookies
+Enable cookie handling.
+A non-zero numeric value enables cookie handling, all other values disable it.
+The cookie cache is maintained in the
+.B cookies
+file in the
+.I userdir
+directory.
+The default value is 0, cookie handling disabled.
+.TP
+.B doscripts
+Enable JavaScript support.
+A non-zero numeric value enables JavaScript, all other values disable it.
+The default value is 0, JavaScript processing disabled.
+.TP
+.B showprogress
+A non-zero numeric value results in the progress panel being displayed.
+All other values hide the progress panel, leaving more vertical space for the main
+display area.
+The default value is 1, causing the progress panel to be displayed
+.TP
+.B http
+Set the version of HTTP to use when communicating with web servers.
+Supported versions are 1.0 and 1.1.
+Any value other than 1.1 results in HTTP1.0 being used.
+The default value is 1.0.
+.TP
+.B nthreads
+Specifies the maximum number of concurrent downloads of document components.
+Generally, if this number is higher, pages will complete faster as
+.I charon
+will not have to wait for the download of one component to complete before another
+can be started.
+The downside is that a higher number of concurrent downloads will use more memory
+during the download process.
+The default value is 4.
+.SH FILES
+.TF /services/config/charon.cfg
+.TP
+.B /services/config/charon.cfg
+The default configuration file.
+.TP
+.IB userdir /config
+The
+.I user
+specific configuration file.
+.I userdir
+is given by the value of the
+.B userdir
+option.
+.TP
+.IB userdir /cookies
+The cookie cache.
+.I userdir
+is given by the value of the
+.B userdir
+option.
+.TP
+.B /services/webget/start.html
+The default start page.
+.TP
+.B /services/webget/help.html
+The default help page.
+.SH SOURCE
+.TF /appl/lib/ecmascript/
+.TP
+.B /appl/charon/
+The main
+.I charon
+source files.
+.TP
+.B /appl/lib/ecmascript/
+Javascript (ECMA-262) implementation.
+.SH BUGS
+.I Charon
+has more than its fair share of real bugs.
+The following list documents the problems that are most likely to be
+encountered.
+.PP
+.I Charon
+implements its table layout as per the algorithm described in rfc1942.
+This sometimes results in table-based documents being laid out differently to other
+browsers.
+.PP
+JavaScript is a source of many problems.
+Many scripts do not specify the language version they employ.
+Additionally different language versions and browsers imply
+a different set of
+.I host
+objects and classes.
+Such differences often give rise to syntax or null reference errors.
+This whole situation places a great burden on the script author to write
+safe and compliant scripts; unfortunately authors
+are rarely aware of this burden!
+.PP
+The following elements of JavaScript1.1 are not fully implemented:
+.PP
+.BR Document.applets ,
+.B Document.embeds
+.I and
+.BR Document.plugins :
+Java Applets are not supported, the arrays are always empty.
+.PP
+.BR Document.onunload :
+The property exists and can be assigned to, but the event is never raised.
+.PP
+.BR Window.open() :
+A new window is never opened. If a URL is specified for the new window, the
+current document will be replaced with that of the new URL.
+.PP
+Other annoyances include:
+.PP
+Window resize forces a complete document reload.
+.PP
+Frames in a frameset are processed one at a time, not concurrently.
+.PP
+It is not possible to save downloaded data to file.  This is particularly annoying
+for MIME types that
+.I charon
+does not support.
+.PP
+The history list can get confused, especially when following links in framesets
+before the complete frameset has been downloaded.
--- /dev/null
+++ b/man/1/chgrp
@@ -1,0 +1,44 @@
+.TH CHGRP 1
+.SH NAME
+chgrp \- change file's group or owner
+.SH SYNOPSIS
+.B chgrp
+[
+.B -uo
+]
+.I id
+.I file ...
+.SH DESCRIPTION
+.I Chgrp
+changes the group ownership of each
+.I file
+to the given
+.IR id ,
+if the file's server
+permits it.
+The
+.B -u
+and
+.B -o
+options are equivalent
+and cause
+.I chgrp
+to change file ownership instead,
+if the file's server permits it.
+.PP
+Normally, a file's group can be changed by the file's owner, if the
+owner is a member of the new group, or by the leader of both
+the file's current group and the new group,
+but various underlying devices, file servers and host operating systems
+might not permit the operation at all.
+Group and ownership changes allowed by a given device are documented by
+the device's manual page.
+See
+.IR sys-stat (2)
+for the variations amongst host operating systems.
+.SH SOURCE
+.B /appl/cmd/chgrp.b
+.SH SEE ALSO
+.IR chmod (1),
+.IR ls (1)
+.IR sys-stat (2)
--- /dev/null
+++ b/man/1/chmod
@@ -1,0 +1,105 @@
+.TH CHMOD 1
+.SH NAME
+chmod \- change file mode (permissions)
+.SH SYNOPSIS
+.B chmod
+.I mode
+.I file ...
+.SH DESCRIPTION
+.I Chmod
+changes the mode (permissions) of each
+.I file
+according to
+.IR mode ,
+which may be an octal number or a symbolic change to the existing mode.
+.PP
+A
+.I mode
+can be numerically formed as the
+.SM OR
+of the following octal values (a leading
+.B 8r
+is ignored):
+.TF 0000
+.TP
+0400
+read by owner
+.TP
+0200
+write by owner
+.TP
+0100
+execute (search in directory) by owner
+.TP
+0070
+read, write, execute (search) by group
+.TP
+0007
+read, write, execute (search) by others
+.PD
+.PP
+A symbolic
+.I mode
+has the form:
+.IP
+.RI [ who ]
+.I op permission
+.PP
+The
+.I who
+part is a combination of the letters
+.B u
+(for user's permissions),
+.B g
+(group) and
+.B o
+(other).
+The letter
+.B a
+stands for
+.BR ugo .
+If
+.I who
+is omitted, the default is
+.BR a .
+.PP
+The
+.I op
+field can be:
+.B +
+to add 
+.I permission
+to the file's mode,
+.B -
+to take away
+.IR permission ,
+.B =
+to assign
+.I permission
+absolutely (all other bits being reset).
+.PP
+The
+.I permission
+field is any combination of the letters
+.B r
+(read),
+.B w
+(write),
+.B x
+(execute),
+.B a
+(append only),
+.B l
+(exclusive access), and
+.B t
+(temporary, not archived).
+.SH SOURCE
+.B /appl/cmd/chmod.b
+.PP
+.SH "SEE ALSO"
+.IR chgrp (1),
+.IR ls (1),
+.IR sys-stat (2)
+.SH BUGS
+The interpretation of the modes is limited on some host operating systems,
+particularly variants of Windows.
--- /dev/null
+++ b/man/1/cleanname
@@ -1,0 +1,37 @@
+.TH CLEANNAME
+.SH NAME
+cleanname \- clean a path name
+.SH SYNOPSIS
+.B cleanname
+[
+.BI -d " dir"
+]
+.I name
+\&...
+.SH DESCRIPTION
+.I Cleanname
+tidies up each file
+.I name
+it is given and prints the result on standard output.
+It removes redundant slashes and interprets
+.L .
+and
+.L ..
+directory names lexically.
+If the
+.B -d
+option is given, each
+.I name
+that does not not start with
+.B /
+or
+.B #
+will be prefixed by
+.IB dir /
+before processing.
+.SH SOURCE
+.B /appl/cmd/cleanname.b
+.SH SEE ALSO
+.IR basename (1),
+.IR pwd (1),
+.IR names (2)
--- /dev/null
+++ b/man/1/cmp
@@ -1,0 +1,50 @@
+.TH CMP 1 
+.SH NAME
+cmp \- compare two files
+.SH SYNOPSIS
+.B cmp
+[
+.B \-lsL
+]
+.I file1 file2
+[
+.I offset1
+[
+.I offset2
+]
+]
+.SH DESCRIPTION
+The two files are
+compared.
+A diagnostic results if the contents differ, otherwise
+there is no output.
+By default,
+.B cmp
+prints the byte number of the first differing byte.
+.PP
+The options are:
+.TP
+.B -l
+Print the byte number (decimal) and the
+differing bytes (hexadecimal) for each difference.
+.TP
+.B -s
+Print nothing for differing files,
+but set the exit status.
+.TP
+.B -L
+Print the line number of the first differing byte.
+.PP
+If offsets are given,
+comparison starts at the designated byte position
+of the corresponding file.
+Offsets that begin with
+.B 0x
+are hexadecimal;
+with
+.BR 0 ,
+octal; with anything else, decimal.
+.SH SOURCE
+.B /appl/cmd/cmp.b
+.SH "SEE ALSO"
+.IR diff (1) 
--- /dev/null
+++ b/man/1/collab
@@ -1,0 +1,84 @@
+.TH COLLAB 1
+.SH NAME
+collab: connect \- connect to collaborative files and services
+.SH SYNOPSIS
+.B collab/connect
+[
+.BI -C " alg"
+] [
+.BI -k " keyfile"
+] [
+.B -v
+] [
+.I netaddress
+[
+.I localdir
+] ]
+.SH DESCRIPTION
+.I Connect
+dials network address
+.I netaddr
+(default:
+.BR "net!$collab!9999" ),
+which should be served by an instance of
+.IR collabsrv (8),
+and mounts the resulting connection on
+.BR /n/remote ;
+it then binds
+.BR /n/remote/collab
+on
+.I localdir
+(default:
+.BR /n/ftree/collab ).
+.PP
+The resulting hierarchy under
+.B /n/remote
+is determined by the site configuration, but usually includes the
+directory
+.BR /n/remote/services ,
+which gives access to the server side of collaborative activities, as described in
+.IR collabsrv (8),
+and the directory
+.BR /n/remote/collab ,
+which contains the file hierarchy that contains all collaborative resources offered to a client device.
+.PP
+If an instance of
+.IR wm-ftree (1)
+is running in the same name space, then
+after mounting the connection and adjusting the name space,
+.IR connect
+tells it that the name space has changed,
+so any displayed view of it can be updated.
+.PP
+.I Connect
+accepts the following options:
+.TP
+.BI -C " alg"
+Specify the algorithm, 
+.IR alg ,
+to be used following authentication for digesting or encryption. See 
+.IR ssl (3)
+for the supported algorithms.
+The default is
+.BR none :
+.IR ssl (3)
+is not used after authentication.
+.TP
+.BI -k " kfile "
+Specify the keyfile to be used when authenticating.
+The default is
+.BI /usr/ user /keyring/ netaddr ,
+if that exists, and otherwise
+.BI /usr/ user /keyring/default .
+See 
+.IR keyring-auth (2)
+for more details.
+.TP
+.B -v
+Causes
+.I connect
+to chat about its actions on standard output.
+.SH SOURCE
+.B /appl/collab/connect.b
+.SH SEE ALSO
+.IR collabsrv (8)
--- /dev/null
+++ b/man/1/collab-clients
@@ -1,0 +1,202 @@
+.TH COLLAB-CLIENTS 1
+.SH NAME
+collab: chat, poll, poller, whiteboard \- collaborative activities
+.SH SYNOPSIS
+.B collab/clients/chat
+[
+.I servicedir
+]
+.I chatroom
+.PP
+.B collab/clients/poll
+[
+.B -d
+]
+[
+.I servicedir
+]
+.I station
+.PP
+.B collab/clients/poller
+[
+.B -d
+]
+[
+.I servicedir
+]
+.I station
+.PP
+.B collab/clients/whiteboard
+[
+.I servicedir
+]
+.I id
+.SH DESCRIPTION
+These commands are
+.IR wm (1)
+programs that are clients of the services of
+.IR collabsrv (8).
+It must therefore be running in the network for any of them
+to be usable.
+Furthermore,
+.IR collabsrv 's
+service directory must appear somewhere in the client's name space,
+for instance by using
+.IR connect (1),
+although plain
+.I mount
+(see
+.IR bind (1))
+can also be used.
+In all cases,
+the optional parameter
+.I servicedir
+names the service directory
+(default:
+.BR /n/remote/services ).
+Finally,
+.I collabsrv
+must be configured to provide the service.
+.PP
+.I Chat
+is a simple multi-user chat program.
+Each user that wishes to chat starts
+.I chat
+naming the desired
+.IR chatroom ,
+which is an identifying string agreed amongst the clients.
+(It is often convenient to use the path name of a shared file.)
+.I Chat
+attempts to enter the given
+.IR chatroom .
+It announces the results of the connection, and if successful,
+displays subsequent chat room messages.
+Its window provides a scrollable text area that forms a transcript
+of the current conversation, and a single line of editable text at the bottom
+of the window for sending messages.
+Messages sent by others appear in the transcript tagged with the sender's name.
+When the user types a new line (return, enter) in the text entry area,
+.I chat
+sends the text to all the members of the chat room,
+and it subsequently appears in the user's own transcript,
+tagged with
+.B <you>
+in place of the user's name.
+.I Chat
+also notes in the transcript the arrival and departure of other users.
+.PP
+.I Poll
+and
+.I poller
+together enable simple real-time polls.
+One user runs
+.IR poller ,
+which activates the given polling
+.IR station .
+The other users can subsequently join using
+.IR poll ,
+naming the same
+.IR station ,
+and can come and go as they please as long
+as the
+.I poller
+remains.
+The polling station closes when the
+.I poller
+leaves.
+.PP
+.I Poller
+drives the interaction for a sequence of one or more real-time polls.
+It is assumed that the poller is in the same room as those polled, allowing the
+questions and answers to be read out each time, as in quiz shows and exit polls.
+Alternatively, something like
+.I chat
+could be used to pose questions to a distant audience.
+For each poll, the polling user selects, in
+.IR poller 's
+window, the number of possible answers (2, 3, or 4) using radio buttons,
+and hits the
+.B Start
+button.
+A bar chart shows results as they come in: each bar shows the percentage of those polled
+(thus far) that have selected the corresponding alternative.
+Once the polling user hits
+.BR Stop ,
+no further results are accepted, and the bar chart represents the final result.
+The
+.B \-d
+option causes
+.I poller
+to display a debugging transcript of the messages it receives.
+.PP
+Each user being polled runs
+.IR poll ,
+and initially
+sees an array of radio buttons with labels
+.BR A ,
+.BR B ,
+.BR C
+and
+.BR D .
+They remain disabled until the
+.I poller
+hits
+.BR Start ,
+at which point
+.I poll
+enables as many radio buttons as allowed by the poller for this round.
+If the user selects a button,
+.I poll
+immediately send the selection to the polling station
+(and thus to the
+.IR poller ),
+and disables all the buttons, although the user's selection remains marked.
+All buttons are also disabled when the
+.I poller
+says to stop,
+whether or not a choice has been made.
+Buttons are enabled again at the start of the next question.
+The
+.B \-d
+option causes
+.I poll
+to display a debugging transcript.
+.PP
+.I Whiteboard
+allows several users to draw on the shared canvas with the given
+.IR id ,
+which is an identifying string agreed amongst the clients.
+The whiteboard window
+contains a canvas to be drawn on with stylus, or mouse button 1.
+Strokes drawn in a given
+.I whiteboard
+appear in all others with the
+same board
+.IR id .
+There are two controls at the bottom of the window:
+the lower left-hand corner has a small pop-up menu of brush shapes,
+including one for erasing; and a long coloured button showing the current drawing colour that
+pops up a choice of drawing colour from a palette.
+Artists can come and go as they please, but
+the drawing vanishes for ever when the last artist leaves the whiteboard.
+.SH FILES
+.TF /n/remote/services
+.TP
+.B /n/remote
+default mount point of collaborative resources
+.br
+.TP
+.B /n/remote/services
+.IR collabsrv (8)
+collaborative activity services directory
+.SH SOURCE
+.B /appl/collab/clients/chat.b
+.br
+.B /appl/collab/clients/poll.b
+.br
+.B /appl/collab/clients/poller.b
+.br
+.B /appl/collab/clients/whiteboard.b
+.SH SEE ALSO
+.IR connect (1),
+.IR collabsrv (8)
--- /dev/null
+++ b/man/1/comm
@@ -1,0 +1,46 @@
+.TH COMM 1 
+.SH NAME
+comm \- select or reject lines common to two sorted files
+.SH SYNOPSIS
+.B comm
+[
+.B -123
+]
+.I file1 file2
+.SH DESCRIPTION
+.I Comm
+reads lines from
+.I file1
+and
+.IR file2 ,
+which are in lexicographical order,
+and produces a three column output: lines only in
+.IR file1 ;
+lines only in
+.IR file2 ;
+and lines in both files.
+The file name
+.L -
+means the standard input.
+.PP
+Each option digit
+.LR 1 ,
+.LR 2 ,
+or
+.LR 3
+suppresses printing of the corresponding
+column.
+.SH EXAMPLE
+.TP
+.EX
+comm -12 file1 file2
+.EE
+.IP
+Print lines common to two sorted files.
+.SH SOURCE
+.B /appl/cmd/comm.b
+.SH "SEE ALSO"
+.IR sort (1),
+.IR cmp (1), 
+.IR diff (1), 
+.IR uniq (1)
--- /dev/null
+++ b/man/1/cook
@@ -1,0 +1,97 @@
+.TH COOK 1
+.SH NAME
+cook \- SGML converter
+.SH SYNOPSIS
+.B cook
+[
+.B -f
+.I format
+] [
+.B -o
+.I outfile
+] [
+.I infile
+]
+.SH DESCRIPTION
+.B Cook
+reads a file in SGML format and produces an interpretation of the file in
+either LaTex or HTML format on standard output.
+.PP
+If no
+.I infile
+argument is present, the standard input is read.
+If an
+.I outfile
+argument is present, the results are written to that file instead
+of standard output.
+The
+.I format
+argument must be either
+.B html
+to produce HTML format output or one of:
+.BR latex ,
+.BR latexbook ,
+.BR latexpart ,
+.BR latexproc ,
+or
+.B latexslides
+to produce LaTex output.
+.PP
+The
+.B latexpart
+format assumes that the resulting output is to be included in
+the body of a controlling LaTex document. The other LaTex
+format options result in the generation of various LaTex
+wrapping commands.
+.PP
+.B Cook
+was designed to operate on the output of the
+.B Brutus
+editor (See 
+.IR brutus (1)
+) and so its mapping of SGML tags is closely linked to those
+generated by
+.IR brutus .
+.PP
+The following tags are recognised:
+.BR Author ,
+.BR Bold. "\fIn\fR,"
+.BR Caption ,
+.BR Example ,
+.BR Exercise ,
+.BR Extension ,
+.BR Float ,
+.BR Heading ,
+.BR Index ,
+.BR Index-topic ,
+.BR Italic. "\fIn\fR,"
+.BR Label ,
+.BR Label-ref ,
+.BR List ,
+.BR List-elem ,
+.BR No-fill ,
+.BR Par ,
+.BR Roman. "\fIn\fR,"
+.BR SGML ,
+.BR Title ,
+and
+.BR Type. "\fIn\fR,"
+where
+.I n
+is a character point size and must be one of:
+.BR 6 ,
+.BR 8 ,
+.BR 10 ,
+.BR 12 " or"
+.BR 16 .
+.SH FILES
+.B /dis/wm/brutus/
+.SH SOURCE
+.B /appl/cmd/cook.b
+.SH SEE ALSO
+.IR brutus (1)
+.SH DIAGNOSTICS
+.BR "Not an SGML file" :
+The first line of the input file must contain the SGML tag
+.B <SGML> 
+and nothing else.
--- /dev/null
+++ b/man/1/cp
@@ -1,0 +1,134 @@
+.TH CP 1
+.SH NAME
+cp, fcp \- copy files
+.SH SYNOPSIS
+.B cp
+[
+.B -gux
+]
+.I fromfile tofile
+.br
+.B cp
+[
+.B -gux
+]
+.I fromfile
+\&...
+.I todir
+.br
+.B cp -r
+[
+.B -gux
+]
+.I fromdir
+\&...
+.I todir
+.PP
+.B
+fcp
+[
+.BI -R " nr"
+] [
+.BI -W " nw"
+]
+.I fromfile tofile
+.br
+.B fcp
+[
+.BI -R " nr"
+] [
+.BI -W " nw"
+]
+.I fromfile
+\&...
+.I todir
+.br
+.B fcp -r
+[
+.BI -R " nr"
+] [
+.BI -W " nw"
+]
+.I fromdir
+\&...
+.I todir
+.SH DESCRIPTION
+In the first form,
+.I fromfile
+is any name and
+.I tofile
+is any name except an existing directory.
+In the second form, the commands copy one or more
+.I fromfiles
+into
+.I dir
+under their original file names, as if by a sequence of commands in the first form. For example:
+.IP
+.B "cp f1 f2 dir"
+.PP
+is equivalent to:
+.IP
+.B "cp f1 dir/f1;  cp f2 dir/f2"
+.PP
+.I Cp
+copies the contents of plain (non-directory) file
+.I fromfile
+to
+.IR tofile .
+The mode and owner of
+.I tofile
+are preserved if it already exists; the permissions of
+.I fromfile
+is used otherwise.
+The
+.B -x
+option sets the full mode and modified time of
+.I file2
+from
+.IR file1 ;
+.B -g
+sets the group id; and
+.B -u
+sets the group id and user id (which is usually only possible if the file server is in an administrative mode).
+.PP
+The
+.B -r
+option directs
+.I cp
+to copy recursively the named directories
+.I "fromdir ..."
+to the target directory
+.IR todir .
+.PP
+.I Fcp
+behaves like
+.IR cp ,
+but copies many blocks in parallel.
+It works only with files that respect read and write offsets (see
+.B pread
+and
+.B pwrite
+in
+.IR sys-read (2)),
+which usually excludes files representing devices or services.
+When it applies, however, it is often
+much faster than
+.IR cp .
+The
+.B -R
+and
+.B -W
+options set the number of readers and writers (default for each: 8).
+.SH SOURCE
+.B /appl/cmd/cp.b
+.br
+.B /appl/cmd/fcp.b
+.SH "SEE ALSO"
+.IR cat (1),
+.IR mv (1),
+.IR sys-stat (2)
+.SH DIAGNOSTICS
+.I Cp
+and
+.I fcp
+refuse to copy a file onto itself.
--- /dev/null
+++ b/man/1/cprof
@@ -1,0 +1,222 @@
+.TH CPROF 1
+.SH NAME
+cprof \- coverage profiling of limbo programs
+.SH SYNOPSIS
+.B cprof
+[
+.B -nfer
+] [
+.BI -m " modname"
+] ... [
+.I "cmd arg ..."
+]
+.PP
+.B wm/cprof
+[
+.B -efr
+] [
+.BI -m " modname"
+] ... [
+.I "cmd arg ..."
+]
+.SH DESCRIPTION
+.I Cprof
+is a coverage profiling tool which shows whether lines of limbo source have been
+executed or not. It can also show the number of times a line of code has been
+executed and can accumulate results over a series of runs if so desired. The source in
+question should be compiled with the 
+.B -g
+flag so that the relevant symbol table files exist.
+.PP
+The
+.B -n
+option lists the name of the file along with the line number.
+.PP
+The
+.B -f
+option shows the number of times source code is executed rather than simply
+indicating coverage.
+.PP
+The
+.B -r
+options indicates that the profiling results should be recorded. Any profiled dis file
+of the form <name>.dis will have the raw profiling results stored in a file named
+<name>.prf. If this file already existed before the run, the results will be added to
+this file. The profiling results are not shown when this option is given.
+.PP
+The
+.B -m
+option lists the module names which are to be profiled. If none are given, all the
+modules loaded by the kernel will be profiled. The name may be the actual name of
+the module or its path name.
+.PP
+The
+.B -e
+option profiles the module that is loaded first in any following command. In this case
+there is no need to give a
+.B -m
+option as this is added automatically.
+.PP
+Any remaining arguments are assumed to
+specify a command and set of arguments to the command. If this is the case,
+.B cprof
+will automatically start profiling, run the command to completion and then
+stop profiling before either recording the results or showing the profile statistics.
+.PP
+If no command is given to profile, then
+.B cprof
+will show the profile statistics from any existing recorded results in .prf files instead.
+.PP
+.B Cprof
+discriminates between different sections of code on the same line. A limbo
+for statement, for example, consisting of initialization, condition and step all on the same line
+of source code will be dealt with as three separate sections.
+.PP
+.B Cprof
+displays the profile statistics as a list of the limbo source preceded by a line
+number and an indication of whether the line was executed or not. For each section
+of code on each line, a plus sign indicates that it was executed, a minus sign that
+it was not and a question mark indicates that some of the dis instructions associated
+with the section of code were executed but some were not. Lines with no
+associated dis code do not have an indication. Of course, given the
+.B -f
+option, the number of times each section is executed is shown instead.
+.PP
+.I Wm/cprof
+is a graphical coverage profiling tool which shows which lines of limbo source have not been
+executed. It can accumulate results over a series of runs if so desired.
+.PP
+The
+.B -r
+options indicates that the profiling results should be recorded. Any profiled dis file
+of the form <name>.dis will have the raw profiling results stored in a file named
+<name>.prf. If this file already existed before the run, the results will be added to
+this file.
+.PP
+The
+.B -m
+option lists the module names which are to be profiled. If none are given, all the
+modules loaded by the kernel will be profiled. The name may be the actual name of
+the module or its path name.
+.PP
+The
+.B -e
+option profiles the module that is loaded first in any following command. In this case
+there is no need to give a
+.B -m
+option as this is added automatically.
+.PP
+The
+.B -f
+option allows a view of the execution profile rather than coverage profile. Each source
+line is preceded by the number of times it was executed and the text is coloured according
+to this: the darker the colour the more times the section of code was executed.
+.PP
+Any remaining arguments are assumed to
+specify a command and set of arguments to the command. If this is the case,
+.B wm/cprof
+will automatically start profiling, run the command to completion and then
+stop profiling before optionally recording the results and showing the profile statistics.
+.PP
+If no command is given to profile, then
+.B wm/cprof
+will show the profile statistics from any existing recorded results in .prf files instead.
+.PP
+.B Wm/cprof
+displays the profile statistics graphically. When the
+.B -f
+option is not present, code that has not been executed is shown
+in white against a red background. Code whose corresponding dis instructions have
+not been wholly executed are shown in red against a white background. Typically a
+line of code such as
+.EX
+	x = !x;
+.EE
+might show only partial execution if x has changed value from 1 to 0 but not
+vice-verse.
+.PP
+The top of the text window names the module along with any modules before and
+after it in the list. If a module has 100% coverage this is stated as well. To help find
+unexecuted code, use the find icon in the menu bar. To move to the next or go back to 
+any other profiled modules, use the arrow icons in the menu bar. The last icon, the reload 
+icon, pops up a menu of profiled modules to choose from.
+.PP
+.B wm/cprof
+calls
+.I cprof
+to do the actual work.
+.SH EXAMPLE
+To profile a particular command
+.IP
+.EX
+cprof /dis/math/sieve 100
+.EE
+.PP
+To profile the same command but restrict attention to its own module (Sieve).
+.IP
+.EX
+cprof -m Sieve /dis/math/sieve 100
+.EE
+.PP
+A shorter version of the above:
+.IP
+.EX
+cprof -e /dis/math/sieve 100
+.EE
+.PP
+Make 3 runs recording results as we go:
+.IP
+.EX
+cprof -e -r /dis/math/sieve 100
+cprof -e -r /dis/math/sieve 1000
+cprof -e -r /dis/math/sieve 10000
+.EE
+.PP
+Now look at the cumulative results:
+.IP
+.EX
+cprof -m /dis/math/sieve.dis
+.EE
+.PP
+To profile a particular command:
+.IP
+.EX
+wm/cprof /dis/math/sieve 100
+.EE
+.PP
+To profile the same command but restrict attention to its own module (Partitions).
+.IP
+.EX
+wm/cprof -m Sieve /dis/math/sieve 100
+.EE
+.PP
+A shorter version of the above:
+.IP
+.EX
+wm/cprof -e /dis/math/sieve 100
+.EE
+.PP
+Make 3 runs recording results as we go using cprof for simplicity:
+.IP
+.EX
+cprof -e -r /dis/math/sieve 100
+cprof -e -r /dis/math/sieve 1000
+cprof -e -r /dis/math/sieve 10000
+.EE
+.PP
+Now look at the cumulative results graphically:
+.IP
+.EX
+wm/cprof -m /dis/math/sieve.dis
+.EE
+.SH SOURCE
+.B /appl/cmd/cprof.b
+.br
+.B /appl/wm/cprof.b
+.SH SEE ALSO
+.IR mprof (1),
+.IR prof (1),
+.IR prof (2),
+.IR prof (3)
+.SH BUGS
+Neither command can profile compiled limbo programs.
--- /dev/null
+++ b/man/1/cpu
@@ -1,0 +1,64 @@
+.TH CPU 1
+.SH NAME
+cpu \- execute a remote command
+.SH SYNOPSIS
+.B cpu
+[
+.B -C
+.I alg
+]
+.RI [ net\fB!\fP ] host
+[
+.I command
+[
+.IR arg ...
+]
+]
+.SH DESCRIPTION
+.B Cpu
+dials
+.I host
+(using network
+.B tcp
+if
+.I net
+is not given), exports the local namespace and executes the given
+.I command
+on that machine. The local namespace is visible to the
+command in
+.BR /n/client ;
+local device files are bound into the remote device directory.
+If
+.I command
+is not given, then
+.B /dis/sh
+is run.
+.PP
+The
+.B -C
+option sets the algorithm
+to be used following authentication for digesting or encryption, to
+.IR alg .
+See 
+.IR ssl (3)
+for the supported algorithms.
+The default is
+.BR none :
+.IR ssl (3)
+is not used after authentication.
+.SH SOURCE
+.B /appl/cmd/cpu.b
+.SH SEE ALSO
+.IR dial (2),
+.IR keyring-auth (2),
+.IR security-auth (2)
+.SH BUGS
+Although the draw device files are visible
+to the remote command, the original implementation
+of Tk meant that windowing applications could not
+receive events when
+run remotely.
+That has been fixed in this release, but
+.I cpu
+has not yet been updated to take advantage.
+A later update will do that.
--- /dev/null
+++ b/man/1/crypt
@@ -1,0 +1,98 @@
+.TH CRYPT 1
+.SH NAME
+crypt, aescbc \- data encryption
+.SH SYNOPSIS
+.B crypt
+[
+.B -d
+] [
+.BI -a " alg\fP\fR[\f5/\fP\fIalg\fP\fR]\fP"
+] [
+.BI -f " keyfile"
+] [
+.BI -k " key"
+] [
+.B -?
+]
+.PP
+.B auth/aescbc
+[
+.B -d
+] [
+.B -e
+] [
+.BI -f " keyfile"
+] [
+.BI -k " key"
+]
+.SH DESCRIPTION
+.I Crypt
+reads a data stream from its standard input and writes it encrypted to standard output,
+preceded by a header that gives details of the algorithm used.
+If the
+.B -d
+option is given,
+.I crypt
+decrypts the standard input instead, writing the clear text on standard output.
+The options are:
+.TP
+.BI -a " alg..."
+Specifies one or two algorithms, for encryption and/or digests.
+The algorithms are those supported by
+.IR ssl (3).
+If two algorithms are given, they should be separated by a slash
+.RB ( / )
+or space, following the conventions of
+.IR ssl (3).
+.TP
+.BI -f " keyfile"
+Read the encryption key from the given file, which obviously should be carefully protected.
+Trailing newlines are ignored.
+.TP
+.BI -k " key"
+Use
+.I key
+as the encryption key.
+.TP
+.B -?
+Print a list of the available encryption and digest algorithms.
+.PP
+If the secret
+.I key
+is not otherwise supplied,
+.I crypt
+prompts for it on
+.BR /dev/cons .
+There is no need to give algorithms when decrypting, because they are taken from the header.
+The default algorithm is
+.BR md5/ideacbc .
+It might be necessary to change that when using
+.I crypt
+for commercial purposes, as noted in
+.IR keyring-crypt (2).
+.PP
+.I Aescbc
+encrypts and decrypts using AES (Rijndael) in cypher
+block chaining (CBC) mode.
+It uses input and output formats compatible with Plan 9's
+.I aescbc
+command; it also
+accepts input in the format used by
+.IR keyfs (4)
+and Plan 9's
+.IR secstore .
+The
+.B -e
+option causes it to encrypt; the
+.B -d
+option to decrypt.
+The other options are just as for
+.IR crypt .
+.SH SOURCE
+.B /appl/cmd/crypt.b
+.br
+.B /appl/cmd/auth/aescbc.b
+.SH SEE ALSO
+.IR ssl (3),
+.IR keyfs (4)
+
--- /dev/null
+++ b/man/1/date
@@ -1,0 +1,55 @@
+.TH DATE 1
+.SH NAME
+date \- print the date
+.SH SYNOPSIS
+.B date
+[
+.B -u
+] [
+.B -n
+] [
+.I seconds
+]
+.SH DESCRIPTION
+Prints the date to standard output, in the format:
+.IP
+.EX
+Wed Jan 12 11:54:06 GMT 2002
+.EE
+.PP
+The options are
+.TP
+.B -u
+Report Greenwich Mean Time (GMT) rather than local time.
+.TP
+.B -n
+Report the date as the number of seconds since the
+epoch, 00:00:00 GMT, January 1, 1970.
+.PP
+Current time is obtained
+by reading
+.BR /dev/time .
+The conversion from Greenwich Mean Time to local time depends on the contents of
+.BR /locale/timezone .
+.PP
+If the optional argument
+.I seconds
+is present, it is used as the time to convert rather than
+the real time.
+.SH FILES
+.TF "/locale/timezone  "
+.TP
+.B /locale/timezone
+Current time zone name and adjustments
+.TP
+.B /locale
+A directory containing time zone tables
+.TP
+.B /dev/time
+microseconds since the epoch, 00:00:00 GMT, 1 January 1970
+.SH SOURCE
+.B /appl/cmd/date.b
+.SH "SEE ALSO"
+.IR time (1),
+.IR daytime (2),
+.IR cons (3)
--- /dev/null
+++ b/man/1/dd
@@ -1,0 +1,182 @@
+.TH DD 1
+.SH NAME
+dd \- convert and copy a file
+.SH SYNOPSIS
+.B dd
+[
+.I option value
+]
+\&...
+.SH DESCRIPTION
+.I Dd\^
+copies the specified input file
+to the specified output with
+possible conversions.
+The standard input and output are used by default.
+The input and output block size may be
+specified to take advantage of raw physical I/O.
+The options are
+.TF "oseek\ \ "
+.PD
+.TP
+.BI -if\  f
+Open file
+.I f
+for input.
+.TP
+.BI -of\  f
+Open file
+.I f
+for output.
+.TP
+.BI -ibs\  n\^
+Set input block size to
+.I n\^
+bytes (default 512).
+.TP
+.BI -obs\  n\^
+Set output block size (default 512).
+.TP
+.BI -bs\  n\^
+Set both input and output block size,
+superseding
+.I ibs\^
+and
+.IR obs .
+If no conversion is specified,
+preserve the input block size instead of packing short blocks
+into the output buffer.
+This is particularly efficient since no in-core copy need be done.
+.TP
+.BI -cbs\  n\^
+Set conversion buffer size.
+.TP
+.BI -skip\  n\^
+Skip
+.I n
+input records before copying.
+.TP
+.BI -iseek\  n\^
+Seek
+.I n
+records forward on input file
+before copying.
+.TP
+.BI -files\  n\^
+Catenate
+.I n 
+input files (useful only for magnetic tape or similar input device).
+.TP
+.BI -oseek\  n\^
+Seek
+.I n\^
+records from beginning of output file before copying.
+.TP
+.BI -count\  n\^
+Copy only
+.I n
+input records.
+.HP
+\fL-conv\ ascii\ \ \ \ \fRConvert
+.SM EBCDIC
+to
+.SM ASCII.
+.PD0
+.RS "\w'\fL-conv\ \fP'u"
+.TP "\w'\fLunblock\ \ \fP'u"
+.B ebcdic
+Convert
+.SM ASCII
+to
+.SM EBCDIC.
+.TP
+.B ibm
+Like
+.B ebcdic
+but with a slightly different character map.
+.TP
+.B block
+Convert variable length
+.SM ASCII
+records to fixed length.
+.TP
+.B unblock
+Convert fixed length
+.SM ASCII
+records to variable length.
+.TP
+.B lcase
+Map alphabetics to lower case.
+.TP
+.B ucase
+Map alphabetics to upper case.
+.TP
+.B swab
+Swap every pair of bytes.
+.TP
+.B noerror
+Do not stop processing on an error.
+.TP
+.B sync
+Pad every input record to
+.I  ibs\^
+bytes.
+.RE
+.PD
+.PP
+.fi
+Where sizes are specified,
+a number of bytes is expected.
+A number may end with
+.L k
+or
+.LR b
+to specify multiplication by
+1024 or 512 respectively;
+a pair of numbers may be separated by
+.L x
+to indicate a product.
+Multiple conversions may be specified in the style:
+.LR "-conv ebcdic,ucase" .
+.PP
+.L Cbs\^
+is used only if
+.LR ascii\^ ,
+.LR unblock\^ ,
+.LR ebcdic\^ ,
+.LR ibm\^ ,
+or
+.L block\^
+conversion is specified.
+In the first two cases,
+.I n
+characters are copied into the conversion buffer, any specified
+character mapping is done,
+trailing blanks are trimmed and new-line is added
+before sending the line to the output.
+In the latter three cases, characters are read into the
+conversion buffer and blanks are added to make up an
+output record of size
+.IR n .
+If
+.L cbs\^
+is unspecified or zero, the
+.LR ascii\^ ,
+.LR ebcdic\^ ,
+and
+.L ibm\^
+options convert the character set without changing the block
+structure of the input file; the
+.L unblock\^
+and
+.L block\^
+options become a simple file copy.
+.SH SOURCE
+.B /appl/cmd/dd.b
+.SH DIAGNOSTICS
+.I Dd
+reports the number of full + partial input and output
+blocks handled.
+.SH "SEE ALSO"
+.IR cat (1),
+.IR cp (1)
--- /dev/null
+++ b/man/1/deb
@@ -1,0 +1,172 @@
+.TH DEB 1
+.SH NAME
+deb \- graphical Limbo debugger
+.SH SYNOPSIS
+.B wm/deb
+.RB [ -f
+.IR file ]
+.RB [ -p
+.IR pid ]
+.SH DESCRIPTION
+.B Wm/deb
+displays two windows, the main debugging window and
+a stack window for browsing the data values of the thread
+currently being debugged.
+.PP
+Debugging is performed using the facilities of the
+.B prog
+device bound to the local
+.B /prog
+directory (see
+.IR prog (3)).
+Debugging of code running on a remote machine can be performed by
+binding the remote
+.B /prog
+directory in place of the local one. (See
+.IR bind (1)).
+.PP
+In order to display source code and set breakpoints accurately, an up to date
+symbol file
+.RB ( .sbl )
+must be available as well as the limbo source file
+.RB ( .b ).
+.SS "Main window"
+The main window is comprised of a menu bar, an icon bar and three text panels.
+One panel, labelled
+.IR Threads ,
+lists the process IDs (PIDs) of the threads being debugged.
+Another panel, labelled
+.IR Break ,
+lists the set of breakpoints, each given a unique number.
+The third and largest panel displays the source code of the thread being debugged.
+.PP
+Breakpoint positions are shown by red text in the source code display.
+Clicking on a breakpoint number in the
+.I Break
+panel results in the source code panel being scrolled or updated to show
+the breakpoint.
+Breakpoints are toggled by clicking on the statement or sub-expression in the source
+code window and clicking the breakpoint
+button on the icon bar.
+A breakpoint can be hit by any of the threads being debugged -
+breakpoints are set on source code, not on individual threads.
+.PP
+Clicking on a PID in the
+.I Threads
+panel results in the source code panel being scrolled or updated to highlight
+where in the code the thread is blocked, broken or halted on a breakpoint.
+.PP
+A running thread can be halted by clicking the stop button on the icon bar.
+A thread will also halt when it hits a breakpoint.
+Once a thread is halted (not blocked or broken) its execution can be advanced
+a step at a time by means of the
+buttons on the icon bar.
+The thread can be stepped one operation at a time or a statement at a time.
+Normally when single stepping, function calls are stepped into.
+Stepping commands allow for stepping over function calls, whereby the function is still
+called but its whole execution is treated as a single step.
+Stepping out of a function is also provided, whereby execution continues unabated
+until returning from the function.
+Execution of the halted thread can be continued, running until it terminates, breaks
+or hits another breakpoint.
+.PP
+Any of the threads being debugged can be killed or detached from the debugger
+using buttons on the icon bar.
+Detaching a halted thread resumes its execution.
+.PP
+The main window provides a set of menus for viewing source files, attaching to
+other threads, setting debugger options and searching for text in the source code window.
+.SS "Stack Window"
+The stack window is used to inspect the values of local and global variables and function
+arguments.
+Items are displayed in a hierarchical manner.
+Any item that contains other items, such as a variable of an ADT type, can be expanded
+to show the values of the sub-items.
+The sub-items are displayed by clicking on the expand button on the left of the
+containing item, they can be hidden by pressing the button again.
+The sub-item list is displayed indented from its container as a visual cue to their
+association.
+.PP
+The stack window shows the full stack trace of the current thread.
+The stack trace is displayed as a list of frames, the current frame displayed
+at the top of the window.
+Each frame is given by the function name and source location of the code being
+executed in the frame.
+Each frame has the following sub-items:
+.TP 10
+.B locals
+Local variables declared in the function of the frame.
+.TP
+.B args
+The arguments passed to the frame function.
+.TP
+.B module
+The module global variables in the implementation module
+of the frame function.
+.PP
+Clicking on the name of a variable or function argument highlights the declaration of
+that name in the source panel of the main debug window.
+Clicking on the function name of a stack frame causes the main window source panel to
+display the current statement of the frame.
+.PP
+The debugger has a stack button which simply brings
+that window to the front of the display.
+.PP
+The options menu has a layout configuration which
+allows the user to have a horizontal scroll bar or to wrap long
+lines (the default); and an option to strip carriage return characters that precede newlines (for the benefit of those using Windows' editors).
+The state of the options is saved in the file
+.BI /usr/ username /lib/deb
+if that file can be created. The debugger attempts to read
+this file on startup to set the user's preferred options.
+.SH OPTIONS
+.TP 10
+.BI -f " file"
+Specifies a source file
+.RB ( .b )
+to load.
+The associated executable file
+.RB ( .dis )
+is not launched until the continue
+.RI ( "run to breakpoint" )
+button is pressed.
+This option takes precedence over the
+.B -p
+option.
+.TP
+.BI -p " pid"
+Attach to the already running thread given by
+.IR pid .
+.SH PLUMBING
+.B wm/deb
+plumbs the address of text selected using
+button 3 in the source display panel, as
+.B text
+of the form
+.IP
+.IB "file-name" : "line-number"
+.SH FILES
+.BI /prog/ n /*
+.br
+.BI /usr/ username /lib/deb
+.SH SOURCE
+.B /appl/wm/deb.b
+.br
+.B /appl/wm/debdata.b
+.br
+.B /appl/wm/debsrc.b
+.SH "SEE ALSO"
+.IR limbo (1),
+.IR prog (3)
+.SH BUGS
+Displaying large arrays in the Stack window can use excessive amounts of
+memory.
+.PP
+When setting breakpoints there is no visual cue for the selected statement or operation until
+the breakpoint is actually set.
+.PP
+It is only possible to debug modules executed by the interpreter.
+Code that has been JITed, the compilation execution method, yields
+stack information that does not correspond to information in the symbol
+.RB ( .sbl )
+file.
--- /dev/null
+++ b/man/1/diff
@@ -1,0 +1,136 @@
+.TH DIFF 1 
+.SH NAME
+diff \- differential file comparator
+.SH SYNOPSIS
+.B diff
+[
+.B -efbwr
+] file1 ... file2
+.SH DESCRIPTION
+.I Diff
+tells what lines must be changed in two files to bring them
+into agreement.
+If one file
+is a directory,
+then a file in that directory with basename the same as that of
+the other file is used.
+If both files are directories, similarly named files in the
+two directories are compared by the method of 
+.I diff
+for text
+files and
+.IR cmp (1)
+otherwise.
+If more than two file names are given, then each argument is compared
+to the last argument as above.
+The 
+.B -r
+option causes
+.I diff
+to process similarly named subdirectories recursively.
+The normal output contains lines of these forms:
+.IP "" 5
+.I n1
+.B a
+.I n3,n4
+.br
+.I n1,n2
+.B d
+.I n3
+.br
+.I n1,n2
+.B c
+.I n3,n4
+.PP
+These lines resemble Plan 9 or Unix
+.I ed
+commands to convert
+.I file1
+into
+.IR file2 .
+The numbers after the letters pertain to
+.IR file2 .
+In fact, by exchanging `a' for `d' and reading backward
+one may ascertain equally how to convert 
+.I file2
+into
+.IR file1 .
+As in 
+.IR ed ,
+identical pairs where
+.I n1
+=
+.I n2
+or
+.I n3
+=
+.I n4
+are abbreviated as a single number.
+.PP
+Following each of these lines come all the lines that are
+affected in the first file flagged by `<', 
+then all the lines that are affected in the second file
+flagged by `>'.
+.PP
+The
+.B -b
+option causes
+trailing blanks (spaces and tabs) to be ignored
+and other strings of blanks to compare equal.
+The
+.B -w
+option causes all white-space to be removed from input lines
+before applying the difference algorithm.
+.PP
+The
+.B -e
+option produces a script of
+.I "a, c"
+and 
+.I d
+commands for the Plan 9 or Unix editor
+.IR ed ,
+which will recreate
+.I file2
+from
+.IR file1 .
+The
+.B -f
+option produces a similar script,
+not useful with
+.IR ed ,
+in the opposite order. It may, however, be
+useful as input to a stream-oriented post-processor.
+.PP
+Except in rare circumstances,
+.I diff
+finds a smallest sufficient set of file
+differences.
+.SH FILES
+.B /tmp/diff[12]
+.SH SOURCE
+.B /appl/cmd/diff.b
+.SH DIAGNOSTICS
+Exit status is the empty string
+for no differences,
+.L some
+for some, 
+and
+.L error
+for trouble.
+.SH "SEE ALSO"
+.IR cmp (1)
+.SH BUGS
+Editing scripts produced under the
+.BR -e " or"
+.BR -f " option are naive about"
+creating lines consisting of a single `\fB.\fR'.
+.br
+When running
+.I diff
+on directories, the notion of what is a text
+file is open to debate.
+.br
+Nothing directly interprets the
+.I ed
+scripts within the Inferno environment.
--- /dev/null
+++ b/man/1/disdep
@@ -1,0 +1,68 @@
+.TH DISDEP 1
+.SH NAME
+disdep \- print load dependencies for Dis file
+.SH SYNOPSIS
+.B disdep
+[
+.B -a
+]
+[
+.B -d
+]
+[
+.B -o
+]
+[
+.B -p
+]
+[
+.B -s
+]
+.I file
+\&...
+.SH DESCRIPTION
+.B Disdep
+reads each
+.IR file ,
+which must be a Dis object file,
+and finds all unique strings in it that end in
+.BR .dis .
+It takes each such string as the name of a Dis file, and
+if the file exists, it does the same for it, and so on, recursively.
+It writes each unique name to the standard output.
+The result is a list of all statically-named Dis files that might be referenced by
+an application, typically as the operand of a Limbo
+.B load
+operator.
+Several options change or extend the output:
+.TP
+.B -a
+Print all names as they are encountered in the search, including duplicates.
+.TP
+.B -d
+Indent to show the dependency structure.
+.TP
+.B -o
+Show only the immediate (outermost) dependencies of each
+.IR file .
+.TP
+.B -p
+Print the dependency relation as pairs:
+a file, a space, and the name of a file on which it depends.
+Only the the first name is printed when a file depends on no other.
+This format is useful as input to
+.IR mk (10.1)
+dependency generators, or dependency graphing programs.
+.TP
+.B -s
+Include strings of the form
+.B
+\&"$[A-Z].*"
+on the assumption
+they are the names of system modules loaded by the application.
+.SH SOURCE
+.B /appl/cmd/disdep.b
+.SH "SEE ALSO"
+.IR limbo (1)
+.SH BUGS
+It cannot see file names that the program calculates.
--- /dev/null
+++ b/man/1/dmview
@@ -1,0 +1,41 @@
+.TH DMVIEW 1
+.SH NAME
+dmview, dmwm \- view remote displays
+.SH SYNOPSIS
+.B wm/dmview
+.I address
+.PP
+.B wm/dmwm
+[
+.BI -p " port"
+]
+.SH DESCRIPTION
+.I Dmwm
+is run in place of the usual Inferno window manager
+.IR wm (1),
+to allow the display's contents to be viewed elsewhere.
+.I Dmwm
+waits for incoming viewing requests on the given TCP/IP
+.I port
+(default: 9998).
+On each connection attempt,
+.I dmwm
+prompts the user to accept or reject the request for a remote view.
+.PP
+.I Dmview
+opens a connection to an instance of
+.I dmwm
+at the given network
+.I address
+and, if the remote user accepts the connection,
+opens a new window on
+.IR dmview 's
+own display that contains a replica of the remote display.
+.SH SOURCE
+.B /appl/wm/drawmux/dmview.b
+.br
+.B /appl/wm/drawmux/dmwm.b
+.br
+.B /appl/wm/drawmux/drawmux.b
+.SH SEE ALSO
+.IR drawmux (2)
--- /dev/null
+++ b/man/1/du
@@ -1,0 +1,66 @@
+.TH DU 1
+.SH NAME
+du \- disk usage
+.SH SYNOPSIS
+.RB du " [ \-anstu ]
+[
+.BI -b " blocksize"
+]
+.RI [ "file ..." ]
+.SH DESCRIPTION
+.I Du
+writes to standard output the total size of files specified as arguments.
+Directories are recursively tallied.
+A summary line is produced for each argument
+.I file
+and subdirectory.
+If no filenames are provided,
+the command operates on the current directory.
+.PP
+Output sums are rounded up to the nearest 1k unit (1024 bytes).
+By default, storage is assumed to be quantised in units of 1024 bytes.
+The
+.B -b
+option sets a different
+.I blocksize
+for quantisation,
+optionally suffixed by
+.B k
+for units of kilobytes;
+output is still in 1k units.
+.PP
+.I Du
+accepts the following options:
+.TP
+.B \-a
+Output usage information for all subordinate files,
+not just directories.
+.TP
+.B \-n
+Output just the filenames (but see the
+.B -t
+option below); implies
+.BR -a .
+.TP
+.B \-s
+Print only the summary line for each
+.IR file .
+.TP
+.B \-t
+Display the last-modified time for each file, not its size;
+when used with the
+.B \-n
+option,
+outputs the filename,
+modification time (seconds since the epoch),
+size (in bytes),
+and checksum.
+The checksum field is always 0; it is a place-holder for a value computed by another command in a pipeline.
+.TP
+.B \-u
+Display the last-accessed time for each file, not its size.
+.SH SOURCE
+.B /appl/cmd/du.b
+.SH SEE ALSO
+.IR sh (1),
+.IR sys-stat (2)
--- /dev/null
+++ b/man/1/ebook
@@ -1,0 +1,73 @@
+.TH EBOOK 1
+.SH NAME
+ebook \- Open Ebook browser
+.SH SYNOPSIS
+.B ebook/ebook
+[
+.B -m
+]
+.I file
+.SH DESCRIPTION
+.I Ebook
+provides a graphical browser for a set of files in Open eBook (OEB) version 1.0.1
+format. It takes some care to try to ensure that memory usage does
+not grow proportionally to the size of the book that is being viewed.
+.I File
+names either an OEB package file (conventional suffix
+.BR .opf )
+or an OEB document (conventional suffix
+.B .html
+or
+.BR .xml ).
+The
+.B -m
+option causes the window to be created in ``mini'' size
+as suitable for display on a 240x320 pixel device.
+.SS "GUI controls"
+Controls at the top of the window enable the user to move
+forward and backwards by pages through the document.
+A ``Guide'' menu provides access to the guide as found in
+the ebook package (if there is one). If the links in this are
+followed, or if the reader follows links embedded within
+the document, the up and down arrows enable moving
+backwards and forwards in the ``link history''.
+Arrows on the keyboard mimic the actions of the buttons
+at the top of the window.
+.PP
+Clicking in the text allows an annotation to made on the
+text; a text window is popped up and any text typed in
+it will appear in a label attached to that text.
+Annotations are stored in persistent storage and will
+last from view to view of the document.
+.SH FILES
+.TF /lib/ebook/default.css
+.TP
+.B /lib/ebook/default.css
+Initial stylesheet settings.
+.TP
+.B \fIdocument\fP.index
+Index file for the OEB
+.IR document ,
+one display size only.
+.TP
+.B \fIdocument\fP.annot
+Annotations for the OEB
+.IR document .
+.TP
+.B /lib/ebooks
+Standard place to keep ebooks.
+.SH SOURCE
+.B /appl/ebook
+.SH SEE ALSO
+.IR xml (2),
+``The Open eBook Publication Structure 1.0.1''
+.SH BUGS
+Does not do floats.
+.br
+Does not do borders & backgrounds properly.
+.br
+Large top-level constructs are not bounded in memory usage.
+.br
+Does not do links to external documents.
+.br
+Does not do fallbacks.
--- /dev/null
+++ b/man/1/echo
@@ -1,0 +1,32 @@
+.TH ECHO 1
+.SH NAME
+echo \- print arguments
+.SH SYNOPSIS
+.B echo
+.RB [ \-n ]
+.RI [ "arg ..." ]
+.PP
+.B load echo
+.br
+.B echo
+.RB [ \-n ]
+.RI [ "arg ..." ]
+.SH DESCRIPTION
+.I Echo
+writes its arguments separated by blanks
+and terminated by a newline on the standard output.
+Option
+.B \-n
+suppresses the newline.
+.PP
+A version of
+.I echo
+can optionally be loaded into the Shell
+.IR sh (1)
+if need be, to make shell scripts a little faster.
+.SH SOURCE
+.B /appl/cmd/echo.b
+.br
+.B /appl/cmd/sh/echo.b
+.SH SEE ALSO
+.IR sh (1)
--- /dev/null
+++ b/man/1/emu
@@ -1,0 +1,285 @@
+.TH EMU 1E
+.SH NAME
+emu \- Inferno emulator (hosted Inferno)
+.SH SYNOPSIS
+.B emu
+[
+.BI \-g " Xsize x Ysize"
+]
+[
+.BI \-c n
+]
+[
+.BI -d " daemon"
+]
+.RB [ \-s ]
+[
+.BI \-p " pool=maxsize"
+]
+[
+.BI \-f " font"
+]
+[
+.BI \-r " rootpath"
+]
+.RB [ \-7 ]
+[
+.RB \-C " channel"
+]
+[
+.RB \-S
+]
+[
+.B -v
+]
+[
+.I cmd
+.RI [ " arg ... " ]
+]
+.SH DESCRIPTION
+.I Emu
+provides the Inferno emulation environment,
+otherwise known as `hosted Inferno'.
+The emulator runs as an application under the
+machine's native operating system, and
+provides system services and a Dis virtual machine for Inferno applications.
+.PP
+.I Emu
+starts an Inferno initialisation program
+.BR /dis/emuinit.dis ,
+whose path name is interpreted in the
+Inferno file name space,
+not in
+the native operating system's name space.
+It in turn invokes the shell
+.BR /dis/sh.dis
+by default or the optional
+.I cmd
+and its arguments.
+If the
+.B \-d
+option is specified,
+.I emu
+instead invokes
+.BR daemon ,
+turning the
+.I emu
+instance into an Inferno service process on the network,
+running the given
+.I daemon
+service or services.
+.PP
+The emulator supports the following options:
+.TP
+.BI \-c n
+Unless specified otherwise by the module (see
+.B wm/rt
+in
+.IR wm-misc (1)),
+.I emu
+uses an interpreter to execute Dis instructions.
+Setting
+.I n
+to 1 (the default value is 0)
+makes the default behaviour
+to compile Dis
+into native instructions when a module is loaded,
+resulting in faster execution but larger run-time size.
+Setting
+.I n
+to values larger than 1 enables increasingly detailed traces of the compiler.
+.TP
+.BI \-d " daemon"
+Run 
+.I emu
+as a server, invoking
+.I daemon
+instead of
+.BR /dis/emuinit.dis ,
+and disabling
+input from
+.B cons
+(see
+.IR cons (3)).
+.TP
+.BI \-g Xsize x Ysize
+Define screen width and height in pixels.
+The default values are 640x480 and the minimum values are 64x48.
+Values smaller than the minimum or greater than the
+available display size are ignored.
+.TP
+.BI \-f font
+Specify the default font for the
+.B tk
+module.
+The path is interpreted in the Inferno name space.
+If unspecified, the
+.B font
+variable has value
+.BR /fonts/lucm/unicode.9.font .
+.TP
+.BI \-r rootpath
+Specify the host system directory that
+.I emu
+will serve as its root.
+The default value is
+.B /usr/inferno
+on most systems, but
+.BR \einferno
+on Windows.
+.TP
+.B \-s
+Specify how the emulator deals with traps reported by the operating system.
+By default, they suspend execution of the offending thread within the virtual machine
+abstraction.
+The
+.B \-s
+option causes
+.I emu
+itself to trap, permitting debugging of the
+broken host operating system process that results when a trap occurs.
+(This is intended to allow debugging of
+.IR emu ,
+not Inferno applications.)
+.TP
+.BI \-p pool = maxsize
+Specify the maximum size in bytes of the named memory allocation pool.
+The pools
+are:
+.RS
+.TP \w'imagexxx'u
+.B main
+the general malloc arena
+.TP
+.B heap
+the Dis virtual machine heap
+.TP
+.B image
+image storage for the display
+.RE
+.TP
+.B \-7
+When host graphics is provided by X11, request a 7-bit colour map;
+use this option only if X11 refused to allow
+.I emu
+to configure the normal (default) 8-bit Inferno colour map.
+.TP
+.B \-C channel
+Use the given
+.I channel
+for the display, if possible.
+See
+.IR image (6)
+for the full range of channel descriptors.
+For example,
+.B k8
+gives 8 bit greyscale, and
+.B x8r8g8b8
+gives 24 bit colour on a PC.
+The set of channels supported is platform-dependent.
+.TP
+.B \-S
+Force stylus input behaviour for Tk mouse events:
+motion events are received only when a button is down
+(just as a stylus produces no events until it touches the screen).
+This option only affects the behaviour of Tk mouse events, it does not
+affect the behaviour of
+.B /dev/pointer
+as described in 
+.IR cons (3).
+.TP
+.B \-v
+Print version data: edition and revision date.
+.PP
+Options may also be set in the host operating system's environment variable
+.BR EMU ;
+they are overridden by options supplied on the command line.
+.PP
+.I Emu
+finds the host system directory that will serve as its Inferno root directory
+as the last value found as follows:
+it is the value built-in to the executable, by default; or
+the value of the host system's environment variable
+.BR INFERNO ;
+or the value of the environment variable
+.BR ROOT ;
+or the value of a
+.B -r
+option in the environment variable
+.BR EMU ;
+or the
+.I rootpath
+set by a
+.B -r
+option to the
+.I emu
+command itself.
+.PP
+.I Emu
+sets several Inferno environment variables:
+.TF emuwdirxxx
+.PD
+.TP
+.B cputype
+host processor architecture:
+.B 386
+(for any x86),
+.BR arm ,
+.BR mips ,
+.B power
+(any Power or PowerPC),
+.BR sparc ,
+and
+.BR spim
+(little-endian MIPS).
+.TP
+.B emuargs
+arguments with which
+.I emu
+was invoked
+.TP
+.B emuhost
+host operating system type, such as:
+.BR FreeBSD ,
+.BR Irix ,
+.BR Linux ,
+.BR MacOSX ,
+.BR NetBSD ,
+.BR Nt
+(used for Windows generally),
+.BR OpenBSD ,
+.BR Plan9 ,
+.B Solaris
+and
+.BR Unixware .
+.TP
+.B emuroot
+name of directory in host file system that acts as Inferno's root directory
+.TP
+.B emuwdir
+name in host file system of directory where
+.I emu
+was invoked
+.PD
+.SH EXAMPLE
+To start
+.B wm/logon
+directly:
+.IP
+.EX
+EMU='-g800x600 -c1'
+emu /dis/wm/wm.dis wm/logon -u inferno
+.EE
+.SH FILES
+.TF /dis/emuinit.dis
+.TP
+.B /dis/emuinit.dis
+The default initialisation program.
+.TP
+.B /dis/sh.dis
+The default Inferno shell.
+.SH SOURCE
+.B /emu
+.SH "SEE ALSO"
+.IR limbo (1),
+.IR wm-misc (1)
--- /dev/null
+++ b/man/1/env
@@ -1,0 +1,27 @@
+.TH ENV 1
+.SH NAME
+env \- display environment variables
+.SH SYNOPSIS
+.B env
+.SH DESCRIPTION
+.I Env
+prints on standard output the current process's environment
+(see
+.IR sh (1)
+and
+.IR env (3))
+listing each variable and its current value.
+.PP
+Individual variable values can be set by assignment in
+.IR sh (1).
+.SH SOURCE
+.B /appl/cmd/env.b
+.SH SEE ALSO
+.IR acme (1),
+.IR sh (1),
+.IR env (2)
+.SH BUGS
+.I Env
+does not understand that environment variables as stored by
+.IR sh (1)
+are lists.
--- /dev/null
+++ b/man/1/fc
@@ -1,0 +1,183 @@
+.TH FC 1
+.SH NAME
+fc \- command-line floating point calculator
+.SH SYNOPSIS
+.B fc
+[
+.I base
+]
+.I expression
+.br
+.SH DESCRIPTION
+.B Fc
+calculates the result of its argument
+.I expression
+and prints the result in the format indicated by
+the optional base argument.
+.I Base
+can be one of:
+.TP
+.B -d
+Decimal, as produced by the
+.B %g
+format of
+.IR sys-print (2).
+.br
+.TP
+.B -x
+Hexadecimal, prefixed with
+.BR 0x .
+.TP
+.B -o
+Octal, prefixed with
+.BR 0 .
+.TP
+.B -b
+Binary, prefixed with
+.BR 0b .
+.TP
+.B -B
+As
+.BR -b ,
+but with extra lines to help bit-counting.
+.TP
+.BI -r\  radix
+In base
+.IR radix ,
+prefixed with
+.IB \fR``\fPradix r\fR'',\fP
+as understood by Limbo (e.g.
+.BR 16r3fff ).
+.TP
+.B -c
+As a unicode character, prefixed with
+.BR @ .
+.RE
+.PP
+.I Expression
+is in reverse polish notation:
+each command line argument is either an operand (number) or an operator.
+Operands are pushed on a stack; operators pop items from
+the stack (the number of items depends on the operator)
+and push their result. All operands are converted to double precision
+floating point numbers before being pushed.
+Integer operations convert their operands to big (64-bit) integers.
+When all arguments are exhausted, all the values currently
+on the stack are printed, first-pushed first, in the specified
+output format.
+.PP
+Operands can be given in any of the formats that
+.I fc
+can print, as detailed above.
+.PP
+When an operation is not commutative, the argument values
+will be taken from the stack first-pushed first.
+Most functions from from
+.IR math-elem (2),
+.IR math-fp(2)
+are provided.
+In addition, other provided operators include:
+.TP
+.B + - / x
+Representing the four rules. Note the use of
+.B x
+rather than
+.BR * ,
+to avoid clashes with shell metacharacters.
+.br
+.TP
+.B xx
+To the power. (equivelant to 'pow')
+.br
+.TP
+.B rad deg
+Convert value to or from radians.
+.br
+.TP
+!
+Factorial.
+.br
+.TP
+.B _
+Unary minus.
+.br
+.TP
+.B and or xor not
+Bitwise operations.
+.br
+.TP
+.B shl shr
+Bitwise shift left/right.
+.TP
+.B p
+Print the current top value on the stack.
+.br
+.TP
+.B sum
+Sum all the values currently pushed on the stack.
+.br
+.TP
+.B swap
+Swap the top two stack items.
+.br
+.TP
+.B dup
+Duplicate the top item on the stack.
+.br
+.TP
+.B rep
+Repeatedly execute the last operator until
+there is only only one item left on the stack.
+This is only valid for operators that take exactly two
+arguments.
+.RE
+.PP
+A few symbolic names for operands are recognised, including
+.B pi
+(or
+.BR π ),
+.BR e ,
+and
+.BR macheps .
+.SH EXAMPLES
+.PP
+fc 22 7 /
+.PP
+.nf
+	gives 3.1428571429
+.fi
+.PP
+fc -b 1 2 3 4 sum
+.PP
+.nf
+	gives 0b00001010
+.fi
+.PP
+fc 10 0b10 010 0x10 x rep 0xa00 swap -
+.PP
+.nf
+	gives 0
+.fi
+.PP
+fc -help
+.PP
+.nf
+	gives a usage summary, including a list of
+	the names of all the operators.
+.fi
+.ne 5
+.SH SEE ALSO
+.IR calc (1),
+.IR math-fp (2),
+.IR math-elem (2),
+.IR sh-expr (1)
+.SH DIAGNOSTICS
+An error message is displayed if
+an operator is called on a stack with
+too few elements. This also causes
+.I fc
+to yield a non-null exit status.
+.SH BUGS
+The
+.B -B
+option will only work for fixed-width fonts.
--- /dev/null
+++ b/man/1/filename
@@ -1,0 +1,61 @@
+.TH FILENAME 1
+.SH NAME
+filename \- interactively select a file
+.SH SYNOPSIS
+.B wm/filename
+[
+.B -d
+.I startdir
+]
+[
+.B -g
+.I geom
+]
+[
+.B -t
+.I title
+]
+[
+.IR pattern ...
+]
+.SH DESCRIPTION
+.B Filename
+pops up a file browser window, allows the user to select
+a file and prints the name of that file to the standard output.
+The optional list of patterns gives a list of wildcard patterns
+as understood by
+.IR filepat (2);
+the file browser will initially show only files matching one
+or more of the patterns. (N.B. patterns must be quoted to
+prevent the shell from interpreting them). The following options
+are recognised:
+.TP 10
+.B -d
+.I Startdir
+gives the initial directory shown by the file browser.
+.TP
+.B -g
+.I Geom
+is given as a tk argument to the file browser window.
+.TP
+.B -t
+.I Title
+specifies the title of the file browser window.
+.SH EXAMPLE
+The following
+.IR sh (1)
+command compiles an interactively chosen Limbo source file,
+placing the file browser window at a particular spot on the screen
+and starting at joe's home directory.
+.EX
+limbo `{wm/filename -t 'Select a source file' -g '-x 50 -y 50'
+	-d/usr/joe '*.b' '*.m'}
+.EE
+.SH SOURCE
+.B /appl/wm/filename.b
+.SH SEE ALSO
+.IR wmlib (2),
+.IR sh (1)
+.SH BUGS
+The file browser window actually appears 20 pixels below and to the right
+of the position specified.
--- /dev/null
+++ b/man/1/fmt
@@ -1,0 +1,47 @@
+.TH FMT 1
+.SH NAME
+fmt \- simple text formatter
+.SH SYNOPSIS
+.B fmt
+[
+.BI -l " n"
+] [
+.BI -i " n"
+] [
+.B -j
+]
+[
+.I file ...
+]
+.SH DESCRIPTION
+.I Fmt
+copies the given
+.I files
+(standard input by default)
+to its standard output, filling and indenting lines.
+The options are
+.TP
+.BI -l " n
+Output line length is
+.IR n ,
+including indent (default 70).
+.TP
+.BI -w " n
+A synonym for
+.BR -l .
+.TP
+.BI -i " n
+Indent
+.I n
+spaces (default 0).
+.TP
+.BI -j
+Do not join short lines: only fold long lines.
+.PP
+Empty lines and initial white space in input lines are preserved.
+Empty lines are inserted between input files.
+.PP
+.I Fmt
+is idempotent: it leaves already formatted text unchanged.
+.SH SOURCE
+.B /appl/cmd/fmt.b
--- /dev/null
+++ b/man/1/fortune
@@ -1,0 +1,23 @@
+.TH FORTUNE 1
+.SH NAME
+fortune \- sample lines from a file
+.SH SYNOPSIS
+.B fortune
+[
+.I file
+]
+.SH DESCRIPTION
+.I Fortune
+prints a one-line aphorism chosen at random.
+If a
+.I file
+is specified, the saying is taken from that file;
+otherwise it is selected from
+.BR /lib/games/fortunes .
+.SH FILES
+.B /lib/games/fortunes
+.br
+.B /lib/games/fortunes.index
+\ \ fast lookup table, maintained automatically
+.SH SOURCE
+.B /appl/cmd/fortune.b
--- /dev/null
+++ b/man/1/freq
@@ -1,0 +1,40 @@
+.TH FREQ 1
+.SH NAME
+freq \- print histogram of character frequencies
+.SH SYNOPSIS
+.B freq
+[
+.B -dxocr
+]
+[
+.I file ...
+]
+.SH DESCRIPTION
+.I Freq
+reads the given files (default standard input)
+and prints histograms of the character frequencies.
+By default,
+.I freq
+counts the value of each byte;
+under the
+.B -r
+option it instead counts
+.SM UTF
+byte sequences, that is, runes.
+.PP
+Each non-zero entry of the table is printed preceded by the byte value,
+in decimal, octal, hex, and
+Unicode
+character (if printable).
+If any options are given, the
+.BR -d ,
+.BR -x ,
+.BR -o ,
+.B -c
+flags specify a subset of value formats: decimal, hex, octal, and
+character, respectively.
+.SH SOURCE
+.B /appl/cmd/freq.b
+.SH SEE ALSO
+.IR utf (6),
+.IR wc (1)
--- /dev/null
+++ b/man/1/fs
@@ -1,0 +1,449 @@
+.TH FS 1
+.SH NAME
+fs \- file-hierarchy traversal
+.SH SYNOPSIS
+.B fs
+.I verb arg
+...
+.br
+.SH DESCRIPTION
+.B Fs
+evaluates an expression whose values represent
+the contents of a hierarchical filesystem.
+There are six types of value:
+.TP 10
+.B fs
+The complete contents of a filesystem.
+.TP
+.B entries
+Information about the entries in a filesystem without
+their content.
+.TP
+.B gate
+A condition that can be used with conditional verbs.
+A gate is open to entries satisfying particular
+criteria.
+.TP
+.B selector
+A comparator which compares two entries
+and selects one, both or neither of them.
+.TP
+.B string
+A simple string literal, represented by itself,
+or quoted according to the usual shell quoting rules.
+.TP
+.B command
+A shell command, represented by an
+.RB `` @ ''
+character followed by a braced block
+containing the shell commands.
+.TP
+.B void
+No value. An expression of this type
+cannot be used as an argument to any verb.
+.PP
+A value is represented either by a literal (a string or shell command),
+or by a braced block,
+.BI { verb
+.RI [ arg ...]\f5}\fP,
+whose value is the result of evaluating
+.I verb
+with the given arguments.
+.PP
+In the following description of the verbs provided,
+an entry such as:
+.TP 10
+.B print \fIentries\fP \fR->\fP void
+.PP
+describes a verb
+.BR print ,
+which takes one argument of type
+.IR entries ,
+and the result of which is of type
+.BR void .
+If the type is not one of those described above,
+it should be taken to be of type
+.IR string .
+.PP
+With no arguments,
+.I fs
+prints a summary of the available verbs.
+Verbs understood by
+.I fs
+include:
+.TP 10
+\f5and\fP \fIgate gate\fP [\fIgate\fP...] -> \fIgate\fP
+.B And
+is a gate that is open to an entry if all its arguments are open.
+.TP
+\f5bundle\fP \fIfs\fP -> \fIvoid\fP
+.B Bundle
+converts
+.I fs
+to an archival format and writes it to the standard output.
+.TP
+\f5compose\fP [\f5-d\fP] \fIop\fP -> \fIselector\fP
+.B Compose
+implements ``compositing''-style operators, useful when
+merging filesystems.
+.I Op
+specifies the operator, taking its name from
+the graphical Porter-Duff equivalent:
+.BR AinB ,
+.BR AinB ,
+.BR BinA ,
+.BR AoutB ,
+.BR BoutA ,
+.BR A ,
+.BR AoverB ,
+.BR AatopB ,
+.BR AxorB ,
+.BR B ,
+.BR BoverA ,
+or
+.BR BatopA.
+For instance,
+.B AinB
+gives the intersection of A and B;
+.B AatopB
+gives A whereever both A and B exist, and B otherwise.
+When used as a selector for
+.BR merge ,
+operators that exclude
+the union of A and B are not very useful, as they will
+exclude all common directories at the top level.
+Given the
+.B -d
+option, compose will allow through directories that
+would otherwise be excluded in this way, making
+operators such as
+.B AxorB
+(all that A does not hold in common with B)
+more useful, although accurate only for regular files.
+.TP
+\f5depth\fP \fIn\fP -> \fIgate\fP
+.B Depth
+is a gate open only to entries which are within
+.I n
+levels of the root of the filesystem.
+.TP
+\f5entries\fP \fIfs\fP -> \fIentries\fP
+.B Entries
+produces all the entries contained within
+.IR fs .
+.TP
+\f5eval\fP \fIexpr\fP -> \fIany\fP
+.B Eval
+evaluates an
+.I fs
+expression and yields its result.
+.TP
+\f5filter\fP [\f5-d\fP]\fIgate fs\fP -> \fIfs\fP
+The result of
+.B filter
+is a filesystem from which all entries that will
+not pass through
+.IR gate ,
+and their descendents, have been removed.
+If the
+.B -d
+flag is given, only files are filtered \- directories bypass the gate.
+.TP
+\f5ls\fP [\f5-um\fP] \fIentries\fP -> \fIvoid\fP
+Print each entry in the style of
+.B ls -l
+(see
+.IR ls (1)).
+If the
+.B -u
+flag is given, the file access time rather than the file modification time
+will be printed. If the
+.B -m
+flag is given, the name of the user that last modified the file
+is printed too.
+.TP
+\f5exec\fP [\f5-pP\fP] [\f5-t\fP \fIcommand\fP] [\f5-n\fP \fIn\fP] \fIcommand entries\fP -> \fIvoid\fP
+Run its argument
+.I command
+for each entry in
+.I entries .
+If the
+.B -n
+flag is specified,
+.B exec
+will try to gather
+.I n
+entries together before invoking the command (default 1).
+The environent variable
+.B $file
+is set to the names of the entries that have been gathered.
+If the
+.B -p
+flag is given, environment variables are set giving information
+about the mode, owner, modification time and size of the entry
+(they are named after the equivalent field
+names in the
+.B Dir
+structure; see
+.IR sys-stat (2)).
+This option is only valid when
+.I n
+is 1.
+The
+.B -P
+flag causes all the other fields in the Dir structure to be included too.
+Note that the command is run in the same shell context each time,
+so environment variable set on one execution can
+be retrieved on the next. The
+.B -t
+flag can be used to specify a command which will be executed
+just before termination.
+.TP
+\f5match\fP [\f5-ar\fP] \fIpattern\fP -> \fIgate\fP
+.B Match
+is a gate that is open if the entry's filename
+matches the
+.IR pattern .
+If the
+.B -a
+flag is given, the whole path will be used
+for the match.
+If
+.B -r
+is specified, the pattern is evaluated as a regular expression,
+otherwise it is a shell-style pattern in the style of
+.IR filepat (2).
+.TP
+\f5merge\fP [\f5-1\fP] [\f5-c\fP \fIselector\fP] \fIfs fs\fP [\fIfs\fP...] -> \fIfs\fP
+Recursively merge the contents of its argument
+filesystems.
+.I Selector
+is consulted to see which entries are chosen for the result;
+if not given, entries are resolved in favour of the first filesystem
+(equivalent to
+.BR "{compose AoverB}").
+If the
+.B -1
+flag is given, merging takes place only in the top-level directory.
+.TP
+\f5mode\fP \fIspec\fP -> \fIgate\fP
+.B Mode
+is a gate that lets through entries whose file permissions
+satisfy
+.IR spec ,
+which is a string in the style of
+.IR chmod (1).
+If the
+.I op
+field is
+.BR + ,
+the specified permissions must be present; if
+.BR - ,
+they must be absent, and if
+.BR = ,
+they must be exactly as given.
+The directory and auth modes are specified with
+the characters ``\f5d\fP'' and ``\f5A\fP''
+respectively.
+.TP
+\f5not\fP \fIgate\fP -> \fIgate\fP
+.B Not
+is a gate open to an entry if its argument is not.
+.TP
+\f5or\fP \fIgate gate\fP [\fIgate\fP...] -> \fIgate\fP
+.B Or
+is a gate open to an entry if any argument is open.
+.TP
+\f5path\fP [\f5-x\fP] \fIpath\fP... -> \fIgate\fP
+.B Path
+is a gate open to an entry whose full pathname
+is an ancestor or a descendent of any
+.IR path.
+If
+.B -x
+is specified, the gate is open to any path
+.I except
+descendents of the paths given.
+.TP
+\f5pipe\fP [\f5-1pP\fP] \fIcommand fs\fP -> \fIvoid\fP
+.B Pipe
+is similar to exec, except that the contents of all files
+in
+.I fs
+are piped through
+.IR command .
+Unless the
+.B -1
+option is given,
+.I command
+is started once for each file, with
+.B $file
+set to its name, and other environment variables
+set according to the
+.B -p
+or
+.B -P
+options, as for
+.BR exec .
+If the
+.B -1
+option is specified,
+.I command
+is started once only \- all file data is piped through that.
+.TP
+\f5print\fP \fIentries\fP -> \fIvoid\fP
+Print the path name of each entry.
+.TP
+\f5proto\fP [\f5-r\fP \fIroot\fP] \fIprotofile\fP -> \fIfs\fP
+Evaluate
+.I protofile
+as a
+.IR mkfs (8)
+.I proto
+file. If
+.I root
+is specified, it will be used as the root of the resulting
+.IR fs .
+.TP
+\f5query\fP \fIcommand\fP -> \fIgate\fP
+.B Query
+is a gate that runs
+.I command
+to determine whether it is open: an empty
+exit status from the command yields an open gate.
+The environment variable
+.B $file
+is set for the command to the path name of the entry that is being queried for.
+.TP
+\f5run\fP \fIcommand\fP -> \fIstring\fP
+.B Run
+runs
+.I command
+and substitutes the value of the environment variable
+.B $s
+after its invocation.
+.B $s
+must have exactly one element.
+.TP
+\f5select\fP \fIgate entries\fP -> \fIentries\fP
+Select only those entries within
+.I entries
+that will pass through
+.IR gate .
+Descendents of elided entries are not affected.
+.TP
+\f5setroot\fP [\f5-c\fP] \fIpath\fP \fIfs\fP -> \fIfs\fP
+.B Setroot
+sets the name of the root directory of
+.IR fs .
+If the
+.B -c
+flag is given, the elements in the root directory
+will be made explicit in the hierarchy (i.e. the
+name of the top directory will not contain any
+.B /
+characters).
+.TP
+\f5size\fP \fIentries\fP -> \fIvoid\fP
+Print the sum of the size of all entries, in bytes.
+.TP
+\f5unbundle\fP \fIfile\fP -> \fIfs\fP
+.B Unbundle
+reads an archive as produced by
+.B bundle
+from
+.IR file ;
+its result is the contents of the filesystem that was
+originally bundled.
+If
+.I file
+is
+.IB `` - '',
+the standard input is read.
+.TP
+\f5walk\fP \fIpath\fP -> \fIfs\fP
+.B Walk
+produces a filesystem that's the result of
+traversing all the files and directories underneath
+.IR path .
+.TP
+\f5write\fP \fIdir fs\fP -> \fIvoid\fP
+Write the contents of
+.I fs
+to the filesystem rooted at
+.I dir .
+If
+.I dir
+is empty,
+.I fs
+will be written to the root directory originally associated with fs.
+.PP
+As a convenience,
+.I fs
+carries out some automatic type conversions
+(conversions are applied recursively, so for instance,
+an
+.BR fs -valued
+expression at the top level will converted
+to void by applying
+.B {print {entries
+.IB fs }}\fR.
+.TP
+.BR string -> fs
+The result is \f5{walk\fP \fIstring\f5}\fP.
+.TP
+.BR fs -> entries
+The result is \f5{entries\fP \fIfs\f5}\fP.
+.TP
+.BR string -> gate
+The result is \f5{match\fP \fIstring\f5}\fP.
+.TP
+.BR entries -> void
+The result is \f5{print\fP \fIentries\f5}\fP.
+.TP
+.BR command -> string
+The result is \f5{run\fP \fIcommand\f5}\fP.
+.SH EXAMPLES
+Print the size of all files below the current directory:
+.EX
+	fs size .
+.EE
+Show the names of all files in x that aren't in y:
+.EX
+	fs select {mode -d} {merge -c {compose -d AoutB} x y}
+.EE
+Remove all files from /appl ending in
+.BR .dis :
+.EX
+	fs exec @{rm $file} {select *.dis /appl}
+.EE
+Recursively copy the current directory to
+.BR /tmp/foo .
+.EX
+	fs bundle . | fs write /tmp/foo {unbundle -}
+.EE
+A simpler method of the above:
+.EX
+	fs write /tmp/foo .
+.EE
+Interactively remove all regular files from one level of the current directory:
+.IP
+.EX
+fs exec @{rm $file} {select {query
+	@{echo -n $file:; ~ `{read} y yes}}
+	{select {mode -d} {filter {depth 1} .}}}
+.EE
+.PP
+Create a new archive containing those files from below the current directory
+that were held in an old archive:
+.EX
+	fs bundle {merge -c {compose AinB} . {unbundle old.bundle}} > new.bundle
+.EE
+.SH SOURCE
+.B /appl/cmd/fs.b
+.br
+.B /appl/cmd/fs/*.b
+.br
+.B /appl/lib/fslib.b
+.SH SEE ALSO
+.IR sh (1)
--- /dev/null
+++ b/man/1/ftest
@@ -1,0 +1,82 @@
+.TH FTEST 1
+.SH NAME
+ftest, newer \- test file attributes
+.SH SYNOPSIS
+.B ftest
+.I test
+.I arg
+.PP
+.B newer
+.I file1
+.I file2
+.SH DESCRIPTION
+.I Ftest
+checks the specified attribute of 
+.I arg
+according to
+.I test
+and yields an exit status signifying the result.
+For all
+.IR test s
+apart from
+.BR -t ,
+.I arg
+is the name of the file to be tested;
+for
+.BR -t ,
+it is the number of a file descriptor.
+Available tests are:
+.TP
+.B -d
+True if the file exists and is a directory.
+.TP
+.B -e
+True if the file exists.
+.TP
+.B -f
+True if the file exists and is a regular file.
+.TP
+.B -r
+True if the file exists and is readable.
+.TP
+.B -s
+True if the file exists and has non-zero size.
+.TP
+.B -t
+True if the open file represented by
+the number
+.I arg
+represents the same file as
+.BR /dev/cons .
+.TP
+.B -w
+True if the file exists and is writable.
+.TP
+.B -x
+True if the file exists and is executable.
+.LP
+.I Newer
+checks whether
+.I file1
+exists and is no older than
+.IR file2 ,
+which must also exist;
+if so, it yields a true exit status.
+Otherwise, it yields an error status.
+Neither file may be a directory.
+.SH SOURCE
+.B /appl/cmd/ftest.b
+.br
+.B /appl/cmd/newer.b
+.SH SEE ALSO
+.IR sys-stat (2)
+.SH BUGS
+These commands
+work only with
+.IR sh (1)
+as none of the other Inferno shells
+can check the exit status of a command.
+.PP
+Checking for read, write and execute capabilities
+is speculative - the file server has the last say.
+Group permissions are ignored.
--- /dev/null
+++ b/man/1/ftree
@@ -1,0 +1,95 @@
+.TH FTREE 1
+.SH NAME
+ftree \- file tree browser
+.SH SYNOPSIS
+.B wm/ftree
+[
+.B [-e
+] [
+.B -E
+] [
+.B -p
+] [
+.B -d
+] [
+.I root
+]
+.SH DESCRIPTION
+.I Ftree
+displays the given
+.I root
+directory
+(default:
+.BR / )
+in a graphical form as a tree.
+Files and subdirectories are listed
+beneath the directory that contains them.
+Initially, the contents of a subdirectory are not displayed, but selecting the
+.B ⊕
+symbol next to its name with button 1 causes its contents to be displayed there, and
+the
+.B ⊕
+symbol changes to
+.BR ⊖ ;
+clicking that collapses the subdirectory display back to its name.
+(If the directory is empty, the circle will be empty.)
+Selecting a file or directory name with button 1 pops up a menu of
+operations:
+.BR Open ,
+.BR Copy ,
+.BR "Paste into" ,
+and
+.BR Remove .
+.B Open
+plumbs the full path name of the file or directory; it is up to the
+.IR plumber (8)
+to act appropriately based on the structure of the file name,
+as controlled by the user's
+.IR plumbing (6)
+file.
+Typically images will be displayed in a separate window, source files will be opened in an editor,
+and so on.
+Directory structure can be copied by invoking
+.B Copy
+on the source, then
+.B "Paste into"
+on the destination directory.
+The
+.B -d
+option disallows all operations except
+.BR Open .
+.PP
+Normally,
+.I ftree
+displays the usual
+.IR wm (1)
+controls, and interprets them as usual.
+The other options change that behaviour:
+.TP
+.B -e
+Ignore `exit' but offer move or resize.
+.TP
+.B -E
+Ignore exit, and do not offer move or resize.
+.TP
+.B -p
+Do exit, but do not offer move or resize.
+.PP
+The different options are used to program a user interface for handheld touch screen devices.
+For instance, a start panel can be created by invoking
+.I ftree
+with the
+.B -E
+option, ensuring that the
+.I ftree
+screen is always there, and all subsequent interaction can be controlled
+by the construction of the
+.I root
+namespace and suitable choice of plumbing rules.
+.SH SOURCE
+.B /appl/wm/ftree
+.SH SEE ALSO
+.IR filename (1),
+.IR wm (1),
+.IR plumbing (6),
+.IR plumber (8)
--- /dev/null
+++ b/man/1/gettar
@@ -1,0 +1,86 @@
+.TH GETTAR 1
+.SH NAME
+gettar, lstar, puttar \- tar archive utilities
+.SH SYNOPSIS
+.B gettar
+[
+.B -k
+] [
+.B -v
+] [
+.B -R
+]
+[
+.IR name " ..."
+]
+.br
+.B lstar
+.br
+.B puttar
+[
+.I file ...
+]
+.SH DESCRIPTION
+These commands manage POSIX.1 tar archives in Inferno.
+.PP
+.I Gettar
+reads a tar file from standard input and unpacks the contents into the current directory tree.
+By default,
+.I gettar
+converts absolute path names, including names starting with
+.LR  # ,
+into names relative to the current directory; the
+.B -R
+option extracts such names as-is.
+The
+.B -k
+option tells
+.I gettar
+to keep existing files rather than overwriting them with files from the archive.
+The
+.B -v
+option causes
+.I gettar
+to print on standard error the names of files extracted.
+Finally, listing one or more
+.I names
+as arguments will extract only those files.
+.PP
+.I Lstar
+reads a tar file from standard input and lists the files contained therein,
+one per line, with four space-separated fields giving the file name, modification time (in seconds since the epoch),
+size (in bytes), and a constant 0 (the place holder for a checksum).
+The format is the same as that produced by
+.BR "du -n -t" .
+.PP
+.I Puttar
+writes a tar file to standard output that contains each
+.IR file ,
+and its substructure if it is a directory.
+Given no arguments,
+.I puttar
+instead reads a list of file names from standard input and includes
+each file or directory named; it does not copy directory substructure.
+.SH EXAMPLE
+The following commands create a tar file with two files
+.B test.b
+and
+.BR srv.b :
+.IP
+.EX
+$ cat tarlist
+test.b
+srv.b
+$ puttar <tarlist >test.tar
+$ lstar <test.tar
+test.b 867178082 1104 0
+srv.b 866042662 3865 0
+.EE
+.SH SOURCE
+.B /appl/cmd/gettar.b
+.br
+.B /appl/cmd/lstar.b
+.br
+.B /appl/cmd/puttar.b
+.SH SEE ALSO
+.IR tarfs (4)
--- /dev/null
+++ b/man/1/grep
@@ -1,0 +1,51 @@
+.TH GREP 1
+.SH NAME
+grep \- pattern matching
+.SH SYNOPSIS
+.B grep
+[
+.B -lnviLs
+] 
+.I pattern
+[
+.IR file ...
+]
+.SH DESCRIPTION
+.B Grep
+prints lines from each
+.I file
+that match the
+.IR pattern ,
+a regular expression as defined in
+.IR regex (2).
+If no files are given, standard input is used.
+If more than one file is given, each line of output
+is preceded by the name of the file it was found in.
+The options are:
+.TP 10
+.B -l
+Print only the name of each file that contains a match.
+.TP
+.B -L
+Print the name of each file that does not contain a match.
+.TP
+.B -n
+Precede each line of output with its line number.
+.TP
+.B -v
+Print only lines that do not match
+.IR pattern .
+.TP
+.B -i
+Pattern matching is case insensitive (roman letters only).
+.TP
+.B -s
+Do not print anything; yield only the exit status.
+.SH SOURCE
+.B /appl/cmd/grep.b
+.SH SEE ALSO
+.IR regex (2)
+.SH DIAGNOSTICS
+.I Grep
+returns a non-nil exit status if no matches have been made,
+or if an error has occurred.
--- /dev/null
+++ b/man/1/grid-monitor
@@ -1,0 +1,86 @@
+.TH GRID-MONITOR 1
+.SH NAME
+monitor \- graphical display for viewing resource use.
+.SH SYNOPSIS
+.I command
+.B | grid/srv/monitor
+.I interface
+.B [
+.I wintitle
+.B ]
+.SH DESCRIPTION
+.I Monitor
+is designed to work with resources that deal with incoming connections (such as
+.IR grid-register (1))
+to visually display the connections to a specific resource. 
+.I Interface
+sets the style of interface, this can be 1 or 2. Interface 1 is the simplest, displaying only a list of connections to the resource. Interface 2 is slightly more complex, allowing extra data to be displayed for each connection. While interface 1 shows all the connections at any one time, interface 2 displays a selection of 'slots' which have buttons that light up to signify a connection. Clicking on a lit button will display any data given about that connection in the main window.
+.I Wintitle
+optionally sets the title of the 
+.I monitor
+window.
+.I Command
+must write status messages to
+.I stdout
+which are then read and displayed (if appropriate) by
+.IR monitor .
+.I Monitor
+reads on
+.I stdin
+and accepts the following input:
+.PP
+.SS Interface 1
+.TP 30
+.BI add " addr"
+display a connection from address
+.I addr
+.TP
+.BI del " addr"
+remove a connection from address
+.I addr
+.PP
+.SS Interface 2
+.TP 30
+.BI "data set" " id" " {" " data" " }"
+set and display the string
+.I data
+in slot 
+.IR id .
+.TP
+.BI "data set" " id" " finished"
+clear slot
+.I id
+.PP
+.SS Common
+.TP 30
+.BI "setup maxusers" " n"
+set display to fit a maximum of 
+.I n
+connections. -1 signifies unlimited connections.
+.TP
+.BI starting " pid"
+add
+.I pid
+to a list of pids whose process group is to be killed if the user closes
+.I monitor.
+.TP
+.BI error " errstr"
+print out the error string 
+.I errstr
+to the console.
+.TP
+.B exit
+close down
+.I monitor
+and kill all processes in the current group.
+.PP
+.PP
+Input messages of any other form will simply be ignored.
+.SH SOURCE
+.B /appl/grid/srv/monitor.b
+.br
+
+.SH "SEE ALSO"
+.IR grid-register (1),
+.IR ns (1),
+.IR cpu (4)
--- /dev/null
+++ b/man/1/grid-ns
@@ -1,0 +1,67 @@
+.TH GRID-NS 1
+.SH NAME
+grid: ns \- exports a selected namespace and serves it on
+standard input
+.SH SYNOPSIS
+.BI "grid/srv/ns	[ -r " "relpath " "]" " path1 path2...path n"
+.br
+.BI "grid/runns	[ -r " "relpath " "]" " path1 path2...path n"
+.SH DESCRIPTION
+.I Ns
+exports a selected subsection of the local namespace and serves it on 
+.I stdin.
+The path arguments specify which directories are to be exported. If subdirectories are exported without their parents, 
+.I ns
+attempts to maintain the namespace structure by creating the parents but populating them only with the selected subdirectories. For example, exporting 
+.B /appl/lib
+would mean that the exported 
+.B /appl
+directory contained nothing apart from the
+.B lib
+subdirectory. If
+.I relpath
+is specified, all paths will be viewed relative to this path. For instance, if
+.I relpath
+is
+.BR /usr/inferno ,
+then
+.B /usr/inferno/bin/dis
+would be listed as
+.B /bin/dis
+and
+.B /tmp
+would not be listed at all.
+.PP
+.IR Grid-register (1)
+may be used in conjunction with 
+.I ns
+to register it with a 
+.IR registry (4)
+and to export and serve its namespace across
+.IR dial (2) 
+network connections. Incoming connections may also be displayed visually using
+.IR grid-monitor (1).
+For example:
+.PP
+.BI "grid/register [" " options ..." " ] { grid/srv/ns " "paths..." " } | grid/srv/monitor 1 'NS resource'
+.PP
+This set of commands is encapsulated within the shell script
+.I runns
+which will automatically register 
+.I ns
+with a
+.IR registry (4)
+if possible and start up the graphical display to show connections to the resource. There is no need for the user to execute
+.I ns
+outside of
+.I runns
+unless the namespace it provides is required to be accessible in a different way to that provided by
+.IR grid-register (1).
+.SH SOURCE
+.B /appl/grid/srv/ns.b
+
+.SH "SEE ALSO"
+.IR grid-cpu (4),
+.IR grid-monitor (1),
+.IR grid-register (1),
+.IR grid-session (1)
--- /dev/null
+++ b/man/1/grid-query
@@ -1,0 +1,44 @@
+.TH GRID-QUERY 1
+.SH NAME
+grid: query \- graphical interface to view resources registered with a known
+.IR registry (4)
+.SH SYNOPSIS
+.B grid/query
+.SH DESCRIPTION
+.I Query
+displays the resources currently registered with a given
+.IR registry (4)
+and allows the user to mount and browse through the namespaces they provide. 
+.PP
+Resource categories are displayed in the main window, opening any of these (by clicking on the '+' next to the name) will reveal the names of the individual resources within that category. Each resource has a set of attributes which may be viewed at the lowest level of the displayed tree. Clicking on a specific resource and clicking the
+.B Mount
+button that appears will mount that resource (if possible) and display the imported namespace in a new window. From this window, the user can then browse this namespace as well as opening and running files. A shell window may also be opened to allow more complicated tasks to be performed.
+.PP
+Clicking
+.B Search
+brings up a search window. Here, the current
+.IR registry (4)
+may be searched for a resource containing the specified attribute names and values.
+.B Refresh
+updates the list of resources in the main window in case any have been added or removed.
+.PP
+
+.SH SOURCE
+.B /appl/grid/query.b
+.br
+.B /appl/grid/lib/browse.b
+.br
+.B /appl/grid/lib/srvbrowse.b
+
+.SH BUGS
+Currently, searching for a resource on a
+.IR registry (4)
+is only implemented in a very simple way. Given the attribute 
+.I (name, value)
+pairs, the search will return all resources whose attributes exactly match the specified values.
+.B *
+may be used to match any attribute value but not name.
+.SH "SEE ALSO"
+.IR grid-localreg (1),
+.IR registries (2),
+.IR registry (4)
--- /dev/null
+++ b/man/1/grid-register
@@ -1,0 +1,60 @@
+.TH GRID-REGISTER 1
+.SH NAME
+grid: register \- registers a resource with a known
+.IR registry (4)
+.SH SYNOPSIS
+.B grid/register [
+.I option ...
+.B ] {
+.I command ...
+.B }
+
+.SH DESCRIPTION
+.I Register
+takes a program that serves a namespace using the 9P protocol on its standard input
+and registers it with a known
+.IR registery (4).
+(See
+.IR intro (5)
+for a description of the protocol.)
+It then marshals the service by listening for incoming connections and exporting the namespace across them.
+.I Register
+prints out various status messages to
+.I stdout
+in the form which may be read by
+.IR grid-monitor (1).
+.SH OPTIONS
+.TP 17
+.BI -u " maxusers"
+Specifies the maximum number of connections to the resource at any one time. If not given, any number of connections are allowed.
+.TP
+.BI -e " n"
+Tells 
+.I register
+to exit after the last connection has gone away. 
+.I N
+is a threshold value, so that 
+.I register
+will not exit upon the last connection going away until the number of incoming connections has reached at least 
+.I n.
+.TP
+.BI -a " 'name=value'"
+Specifies the name and value of an attribute to be listed as belonging to the resource when registered with the
+.IR registry (4).
+.TP
+.BI -A  " address"
+Allows the user to specify the address for the service to announce on. 
+.I Address
+takes the form
+.BI tcp! machine ! port
+.TP
+.B -m
+Includes the amount of free main memory as part of the attributes list.
+
+.SH SOURCE
+.B /appl/grid/register.b
+
+.SH "SEE ALSO"
+.IR registries (2),
+.IR registry (4),
+.IR grid-localreg (1)
--- /dev/null
+++ b/man/1/grid-session
@@ -1,0 +1,112 @@
+.TH GRID-SESSION 1
+.SH NAME
+grid: session \- graphical interface for configuring tasks using
+.IR grid-cpu (4)
+and
+.IR grid-ns (1)
+resources
+.SH SYNOPSIS
+.B grid/session
+.SH DESCRIPTION
+.I Session
+allows the user to build up a single namespace from various different namespace resources and configure a set of commands to be executed within this namespace. The execution then takes place on one or more selected
+.IR grid-cpu (4)
+resources.
+.PP
+Once started, 
+.IR session
+presents a heirarchical view of currently registered resources (such as
+.IR grid-ns (1))
+that serve name spaces using 9P.
+(See
+.IR intro (5)
+for a description of the protocol.)
+The top level displays the type of resource, for instance
+.BR 'CPU resource' , 
+whilst the second level shows the name of each individual resource. Descending futher down the tree will reveal the attributes of the selected resource. To mount a resource and see the namespace it exports, click mouse button 3 on the resource name. The view will switch to a split pane view displaying the resource namespace with directories listed on the left and all files in the current directory displayed on the right.
+.PP
+Clicking mouse button 3 on a file or directory will cause it to be added to the 
+.I command
+or
+.I namespace
+list at the bottom of the window. Mouse button 3 is again used on items in either of these lists to display a menu containing the name of the resource on which they are located as well as the option to remove them. The
+.B Cmd
+and
+.B Ns
+buttons located at the top of the window toggle the view between the selected commands and the selected namespace. The 
+.B Resources
+button returns to the initial view containing the currently registered resources and
+.B Refresh
+updates the list in case any resources have been added or removed.
+
+.SS The Command List
+This contains a list of all commands to be executed. Any arguments may be entered by clicking on the gap after
+.IR args :
+and typing into the box. Commands will be executed in order from top (first) to bottom (last).
+
+.SS The Namespace List
+This contains a list of all the directories to be imported in order to create the local namespace of the selected 
+.IR grid-cpu (4)
+resource(s). By default, several directories will be imported from the local machine. To select these, click button 3 on
+.B import local namespace
+and (un)check the directories as required. To toggle this option on and off, click button 1 on
+.B import local namespace.
+These default directories are required by many commands for normal execution so it is recommended that they are not disabled unless they are definitely not needed. Importing a directory of the same name as one of the default directories will automatically overwrite the default. Therefore, it is not necessary to disable a default directory if the same name is to be imported from elsewhere.
+.PP
+A given directory can only be imported once so it is not possible, for example, to import
+.B /usr/inferno
+from two different machines.
+.I Session
+will automatically import the directories containing each of the selected commands but any others required must be explicitly imported. For example: If one of the commands is:
+.PP
+.EX
+	/usr/inferno/runme.dis -f /appl/lib
+.EE
+.PP
+.I Session
+will import
+.B /usr/inferno/
+but
+.B /appl/lib
+would have to be specified in the namespace list.
+.SS Execution
+Once the command and namespace lists have been configured, click 
+.B Ok
+on the 
+.I Session
+window. This will bring up the 
+.IR grid-query (1)
+window containing a list of the available
+.IR grid-cpu (4)
+resources. To select one or more of these for execution, click on the name with button 3 and it will be added to the list. If a specific
+.IR grid-cpu (4)
+resource is not required, click button 3 on
+.B CPU resource
+and select the number you require by clicking on the number following the list entry and dragging the mouse up or down.
+.PP
+Once the required
+.IR grid-cpu  (4)
+resources have been selected, click
+.B Ok
+and execution will commence.
+
+
+.SH SOURCE
+.B /appl/grid/session.b
+.br
+.B /appl/grid/ns.b
+.br
+.B /appl/grid/monitor.b
+.br
+.B /appl/grid/query.b
+.br
+.B /appl/grid/lib/browse.b
+.br
+.B /appl/grid/lib/srvbrowse.b
+
+.SH "SEE ALSO"
+.IR grid-query (1),
+.IR registries (2)
+.IR grid-ns (1),
+.IR grid-cpu (4),
+.IR registry (4)
--- /dev/null
+++ b/man/1/gzip
@@ -1,0 +1,69 @@
+.TH GZIP 1
+.SH NAME
+gzip, gunzip \- compression and decompression utilities
+.SH SYNOPSIS
+.B gzip
+[
+.B \-v
+] [
+.BI \- level
+] [
+.IR file ...
+]
+.br
+.B gunzip
+[
+.IR file ...
+]
+.SH DESCRIPTION
+.B Gzip
+and
+.B gunzip
+perform data compression and decompression
+compatible with the Unix
+.I gzip
+file format,
+which uses a hybrid Lempel-Ziv 1977 and Huffman compression algorithm
+called `deflate'.
+If no arguments are given
+.B gzip
+compresses each
+.I file
+replacing it by a file of the same
+name with the a
+.B .gz
+suffix appended.
+If no files are given, standard input
+and standard output are used.
+.I Level
+is a single decimal digit between 1 and 9;
+higher numbers give better compression.
+The default
+.I level
+is 6.
+The
+.B \-v
+option causes
+.B gzip
+to report the compression ratio of each file that
+it compresses.
+.PP
+.B Gunzip
+performs the opposite operation to
+.BR gzip ;
+each
+.I file
+(which must have a
+.B .gz
+suffix) is uncompressed, and replaced
+by a file of the same name with the
+.B .gz
+suffix removed. If no files are given,
+standard input and standard output are used.
+.SH SOURCE
+.B /appl/cmd/gzip.b
+.br
+.B /appl/cmd/gunzip.b
+.SH SEE ALSO
+.IR filter (2),
+.IR filter-deflate (2)
--- /dev/null
+++ b/man/1/idea
@@ -1,0 +1,65 @@
+.TH IDEA 1 
+.SH NAME
+idea \- encrypt/decrypt a file with the IDEA cipher
+.SH SYNOPSIS
+.B idea
+[
+.B -e
+]
+[
+.B -d
+]
+.I key
+[
+.I datafile
+]
+.SH DESCRIPTION
+.I Idea
+encrypts or decrypts data using the IDEA (International Data Encryption Algorithm) cipher
+first proposed by Xuejia Lai and James Massey in 1990.
+.PP
+The
+.B -e
+option encrypts the data
+and the
+.B -d
+option decrypts the data. Exactly one of these must be specified on the command line.
+.PP
+The
+.I key
+is any 16 character string which is used as the key for both encryption and decryption.
+.PP
+The data to be encrypted/decrypted is either in
+.I datafile
+or is read from standard input. If no input file is given, the output from
+.I idea
+is always sent to standard output. For encryption, if an input file is specified the output
+is sent to a file with the name
+.I datafile.id.
+For decryption, if an input file is specified it
+should have a .id extension and the output is sent to a file whose name is that of
+.I datafile 
+without the .id extension.
+.SH EXAMPLES
+.PP
+Encrypt the data in the file A10076795.gz:
+.IP
+.EX 
+idea -e 'abcd2345 $+*LMNO' A10076795.gz
+.EE
+.PP
+The encrypted data is put in the file A10076795.gz.id. Once this file is transmitted,
+the receiver can then decrypt it, as long as he has the key, with:
+.IP
+.EX
+idea -d 'abcd2345 $+*LMNO' A10076795.gz.id
+.EE
+.PP
+The decryped data is put in the file A10076795.gz.
+.PP
+Note that the quotes around the key are interpreted by the shell and simply delimit
+the key string.
+.SH SOURCE
+.B /appl/cmd/idea.b
+.br
+.B /utils/idea/idea.c
--- /dev/null
+++ b/man/1/itest
@@ -1,0 +1,124 @@
+.TH ITEST 1
+.SH NAME
+itest, itreplay \- run tests and replay results
+.SH SYNOPSIS
+.B itest
+[
+.I -eo
+]
+[
+.I -c cflag
+]
+[
+.I -r count
+]
+[
+.I -v vlevel
+]
+[
+.I -C configfile
+]
+[
+.I -R recordroot
+]
+[
+.I testdir ...
+]
+.PP
+.B itreplay
+[
+.I -eo
+]
+[
+.I -v verbosity
+]
+[
+.I recorddir ...
+]
+.SH DESCRIPTION
+.I Itest
+runs a sequence of tests, optionally recording all the results in
+a directory tree. The
+.IR itreplay
+command replays the results of one or more recorded tests.
+Some options are common to both commands:
+.TP
+.B -e
+Display the standard error produced by the tests.
+.TP
+.B -o
+Display the standard output produced by the tests.
+.TP
+.BI -v " vlevel"
+Set the verbosity level to
+.I vlevel
+(0-9). The higher the value, the more detail is displayed; the default level is 3.
+.PP
+The tests run by
+.IR itest
+are specified as one or more directories either on the command line or in a configuration file. Options:
+.TP
+.BI -c " cflag"
+Set the value in /dev/jit (usually 0 or 1; 0 for interpreted mode, 1 for compiled mode) to
+.I cflag
+.TP
+.BI -r " count"
+Run the set of tests 
+.I count
+times; a value of 0 means repeat indefinitely.
+.TP
+.BI -v " vlevel"
+Set the verbosity level to
+.I vlevel
+(0-9). The higher the value, the more detail is displayed; the default level is 3.
+.TP
+.BI -C " cfile"
+Use the configuration file
+.I cfile.
+The file should contain a list of test directories, one per line.
+.TP
+.BI -R " recroot"
+Store the test results in a tree rooted at 
+.I recroot.
+Record directories are named as integers starting at 1. Each test run creates a new
+directory numbered one greater than the highest existing directory.
+.PP
+The test results to be replayed by
+.IR itreplay
+are specified on the command line as one or more record directories.
+.SS TEST FORMAT
+A test directory must contain either a t.dis file or a t.sh file, depending
+on whether the test is written as a Limbo program or as a
+.IR sh (1)
+script. Limbo programs should use 
+.IR itslib (2);
+sh scripts should use
+.IR sh-test (2).
+A test directory should also contain a README file (which is displayed by
+.I itest
+if the verbosity level is greater than 8), and any other files required
+for the test.
+.PP
+Tests are run with their working directory set to their own test directory.
+.SS RECORD DIRECTORY FORMAT
+Each record directory contains a number of files:
+.TP
+.BI msgs
+All the messages generated by the test.
+.TP
+.BI stderr
+Standard error from the test.
+.TP
+.BI stdout
+Standard output from the test.
+.TP
+.BI summary
+a one-line file containing the start time in seconds, elapsed time in ms, cflag
+and the name of the test directory.
+.SH SOURCE
+.B /appl/cmd/itest.b
+.br
+.B /appl/cmd/itreplay.b
+.SH SEE ALSO
+.IR itslib (2),
+.IR sh-test (2)
--- /dev/null
+++ b/man/1/keyboard
@@ -1,0 +1,57 @@
+.TH KEYBOARD 1
+.SH NAME
+keyboard, pen \- character input for touch screen devices
+.SH SYNOPSIS
+.B wm/keyboard
+[
+.B -e
+] [
+.B -t
+]
+.br
+.PP
+.B wm/pen
+[
+.B -e
+] [
+.B -r
+] [
+.B -t
+]
+.SH DESCRIPTION
+.B Wm/keyboard
+provides a soft keyboard for touch-screen devices.
+Characters selected on the on-screen keyboard using a stylus (or mouse button 1) are passed to Tk via
+.B Tk->keyboard
+in
+.IR tk (2).
+The
+.B -t
+option causes it to put itself on the
+.IR wm (1)
+task bar from the start.
+The
+.B -e
+option causes it to treat the `exit' button on the title bar as
+a request to put itself back on the task bar.
+.PP
+.B Wm/pen
+provides character input using single-stroke gestures with the stylus
+(or mouse button 1).
+The
+.B -r
+option allows the pen window to be reshaped.
+Options are otherwise the same as for
+.BR wm/keyboard .
+.SH FILES
+.TF "/lib/strokes/*.clx"
+.TP
+.B /lib/strokes/*.cl
+Raw sample data for different stroke sets (eg, letters, digits)
+.TP
+.B /lib/strokes/*.clx
+Compact canonical versions of the stroke sets
+.SH SOURCE
+.B /appl/wm/keyboard.b
+.br
+.B /appl/wm/pen.b
--- /dev/null
+++ b/man/1/kill
@@ -1,0 +1,72 @@
+.TH KILL 1
+.SH NAME
+kill, broke \- terminate process(es)
+.SH SYNOPSIS
+.B kill
+[
+.B -g
+]
+[
+.I pid ...
+]
+[
+.I module ...
+]
+.PP
+.B broke
+[
+.I user
+]
+.SH DESCRIPTION
+.I Kill
+terminates each process (for a numeric
+process ID
+.IR pid )
+or
+process running a given
+.I module
+(for a non-numeric module name),
+by writing a
+.L kill
+message to the corresponding process's control file
+in
+.IR prog (3).
+The
+.B -g
+option causes
+.I kill
+to write a
+.L killgrp
+message instead, killing all processes in the given process's process group
+(see
+.IR sys-pctl (2)).
+Processes running a
+.I module
+are identified by their
+.L status
+file, and the process ID of each such process is printed on standard output.
+.PP
+A process that incurs an exception (eg, array bounds check)
+is normally suspended in the `broken' state to allow debugging.
+.I Broke
+finds all such processes owned by
+.I user
+(default: the current user), and
+prints
+.I sh (1)
+commands to kill them.
+The commands can be piped to the shell or selectively run,
+releasing back to the system any resources owned by those processes.
+.SH FILES
+.TF "/prog/pid/status  "
+.TP
+.BI /prog/ pid /ctl
+.TP
+.BI /prog/ pid /status
+.SH SOURCE
+.B /appl/cmd/kill.b
+.br
+.B /dis/broke
+.SH "SEE ALSO"
+.IR ps (1),
+.IR prog (3)
--- /dev/null
+++ b/man/1/limbo
@@ -1,0 +1,218 @@
+.TH LIMBO 1E
+.SH NAME
+limbo \- Limbo compiler
+.SH SYNOPSIS
+.EX
+limbo [ \f2option ...\fP ] [ \f2file ...\fP ]
+.EE
+.SH DESCRIPTION
+.B Limbo
+compiles the named Limbo
+.I files
+into machine-independent object files for the Dis virtual machine.
+Depending on the options, the compiler may create output
+files or write information to its standard output.
+Conventional files and their extensions include the following.
+.TP 10
+.IB file .b
+Limbo source file.
+.TP
+.IB file .dis
+Object code for the Dis virtual machine.
+.TP
+.IB file .m
+Limbo source file for
+.B module
+declarations.
+.TP
+.IB file .s
+Assembly code.
+.TP
+.IB file .sbl
+Symbolic debugging information.
+.PP
+With no options,
+.B limbo
+produces a
+.B \&.dis
+file for each
+source file.
+.PP
+The compiler options are:
+.TP 1i
+.B -a
+Print on standard output
+type definitions and call frames
+useful for writing C language implementations of Limbo modules.
+Suppresses normal output file generation.
+.TP
+.B -C
+Mark the Dis object file to prevent run-time compilation.
+.TP
+.B -c
+Mark the Dis object file to guarantee run-time compilation.
+.TP
+.BI -D " flags"
+Turn on debugging
+.IR flags .
+Flags include
+.B A
+for arrays,
+.B a
+for
+.B alt
+statements,
+.B b
+for booleans,
+.B C
+for
+.B case
+body statements,
+.B c
+for
+.B case
+statements,
+.B D
+for use descriptors,
+.B d
+for declarations,
+.B e
+for expressions,
+.B E
+for extended expressions,
+.B F
+for function information,
+.B f
+for constant folding,
+.B m
+for modules,
+.B n
+for
+.B nil
+references,
+.B P
+for program counter manipulations,
+.B r
+for reference types,
+.B S
+for type signatures,
+.B s
+for a code generation summary,
+.B T
+for tuples,
+.B t
+for type checking,
+and
+.B v
+for variable initialization.
+.TP
+.B -e
+Increase the number of errors the compiler will report before exiting.
+.TP
+.B -G
+Annotate assembly language output with debugging information.
+A no-op unless
+.B -S
+is set.
+.TP
+.B -g
+Generate debugging information for the input files and place it in a file
+named by stripping any trailing
+.B \&.b
+from the input file name and appending
+.BR .sbl .
+.TP
+.B -i
+Disable inlining of functions. Currently functions containing a
+single return statement or two return statements and an if clause are candidates for inlining.
+.TP
+.BI \-I " dir"
+An
+.B include
+file whose name does not begin with slash
+is sought first relative to the working directory,
+regardless of the source
+.I file
+argument.
+If this fails,
+.B limbo
+sequences through directories named in 
+.B \-I
+options,
+then searches in
+.BR /module .
+An
+.B include
+file contains Limbo source code, normally holding one or more
+.B module
+declarations.
+.TP
+.BI \-o " obj"
+Place output in file
+.I obj
+(allowed only if there is a single input
+.IR file ).
+The output file will hold either object or assembly code,
+depending on
+.BR \-S .
+Default is to take the last element of the input file name,
+strip any trailing
+.BR .b ,
+and append
+.B .dis
+for object code and
+.B .s
+for assembly code.
+Thus, the default output file for
+.B dir/mod.b
+would be
+.BR mod.dis .
+.TP
+.B \-S
+Create assembly language output instead of object code.
+.TP
+\f5\-T\fP\ \f2module
+Print on standard output C stub functions,
+useful for implementing Limbo modules in the C language for linkage
+with the interpreter.
+.TP
+\f5\-t\fP\ \f2module
+Print on standard output
+a table of runtime functions,
+to link C language implementations of modules with the Limbo interpreter.
+Suppresses normal output file generation.
+.TP
+.B \-w
+Print warning messages about unused variables, etc.
+More \f5w\fP's (e.g., \f5\-ww\fP) increase the pedantry of the checking.
+.PP
+.SH FILES
+.TF /module
+.TP
+.B /module
+directory for Limbo
+.B include
+modules
+.SH SOURCE
+.TF /appl/limbo
+.TP
+.B /appl/limbo
+compiler source in Limbo
+.TP
+.B /limbo
+compiler source in C for host
+.SH "SEE ALSO"
+.IR asm (1),
+.IR emu (1),
+.IR mk (10.1),
+.IR intro (2),
+.IR sys-intro (2),
+.IR tk (2)
+.PP
+``The Limbo Programming Language''
+.br
+``Program Development in Inferno''
+.br
+``A Descent into Limbo''
+.br
+in Volume 2.
--- /dev/null
+++ b/man/1/listen
@@ -1,0 +1,237 @@
+.TH LISTEN 1
+.SH NAME
+listen, styxlisten, dial \- network connections
+.SH SYNOPSIS
+.B listen
+[
+.B -Ats
+] [
+.B -a
+.I alg
+]... [
+.B -k
+.I keyfile
+] [
+.B -i
+.BI { initscript }
+]
+.I addr
+.I command
+[
+.IR arg ...
+]
+.br
+.B styxlisten
+[
+.B -Ats
+] [
+.B -a
+.I alg
+]... [
+.B -k
+.I keyfile
+]
+.I addr
+.I command
+[
+.IR arg ...
+]
+.br
+.B dial
+[
+.B -A
+] [
+.B -a
+.I alg
+] [
+.B -k
+.I keyfile
+]
+.I addr
+.I command
+[
+.IR arg ...
+]
+.SH DESCRIPTION
+.I Listen
+waits for an incoming network connection on
+.IR addr ,
+(as accepted by
+.B announce
+in
+.IR dial (2))
+and then invokes
+.IR sh (1)
+to run the associated
+.IR command .
+If the
+.B -A
+option is specified, no authentication or encryption will
+take place on the connection; otherwise
+.I listen
+will attempt to authenticate the party at the other
+end of the connection, allowing any given
+.I alg
+to be used to encrypt and/or digest the
+connection's data. If neither
+.B -A
+or any
+.B -a
+option is given, then
+.I listen
+will allow any algorithm allowed by the local
+.IR ssl (3)
+device.
+If
+.I keyfile
+is specified, then that will be used as the server's certificate;
+otherwise
+.BI /usr/ user /keyring/default
+will be used.
+.PP
+If an
+.I initscript
+is provided, it is executed by each listener
+after announcing its network connection,
+with the shell variable
+.B net
+set to the name of the corresponding network directory
+(see
+.IR dial (2)),
+before listening for incoming calls.
+This can be used to change, or find out the characteristics
+of an announced port (for instance to find out
+the actual port number that has been announced).
+.PP
+By default,
+.I listen
+backgrounds itself (after checking that the port
+announcement proceeded ok); giving it the
+.B -s
+option causes it to run synchronously.
+.PP
+.I Listen
+currently makes available the whole of its current name space visible to the command,
+which might be undesirable, and perhaps should be optional, with a new name space
+constructed for an incoming call.
+The
+.B -t
+option declares the command to be `trusted' giving it access to
+elements of the current name space such as
+.B /mnt/keys
+on an authentication server.
+By default it has not got that access.
+.PP
+.I Styxlisten
+is similar to
+.IR listen ,
+except that it multiplexes a single
+.I styx
+(see
+.IR intro (5))
+server between multiple clients.
+.I Styxlisten
+starts its
+.I cmd
+only once; it assumes it will serve styx messages
+through file descriptor 0 when started. For each client that attaches to
+.IR address ,
+the command will see a new
+.IR attach (5)
+message indicating the new connection.
+Unless the
+.B -A
+option has been given, the
+.B uname
+field in the attach message will be the name of the
+authenticated user.
+When the command exits, the process listening
+on
+.I address
+is stopped.
+.PP
+.I Dial
+is the complement of
+.IR listen .
+It tries to make a connection to
+.IR addr .
+If the
+.B -A
+option is given, no authentication or encryption will
+take place; otherwise Inferno authentication and encryption
+will be performed as usual, using
+.I alg
+if given, or a default algorithm otherwise.
+.I Keyfile
+is used for the certificate if given, otherwise
+.BI /usr/ user /keyring/ addr\fR,\fP
+if it exists, and failing that,
+.BI /usr/ user /keyring/default\fR.\fP
+.I Alg
+is used for the encryption/digest algorithm
+on the connection.
+When the connection is made,
+.I command
+is run in the context of that connection, as described below.
+.PP
+For both
+.I dial
+and
+.IR listen ,
+when the command is run,
+.B $user
+is set to the name of the authenticated user at the other
+end of the connection (if authentication
+is being used), and
+.B $net
+is set to the
+.B /net
+directory corresponding to the connection.
+The standard input and output of the command
+is redirected to the network connection (standard
+error is unaffected).
+.SH EXAMPLES
+Run a custom login daemon and an echo server that
+logs incoming connections:
+.IP
+.EX
+listen 'tcp!*!echo' {
+	echo connection from `{cat $net/remote} >[1=2]
+	echo user is $user >[1=2]
+	cat &
+}
+.EE
+.PP
+Dial up the above echo server:
+.IP
+.EX
+dial tcp!somehost!echo {
+	echo made connection >[1=2]; echo hello; cat >[1=2]
+}
+.EE
+.PP
+Make the current name-space available to all:
+.IP
+.EX
+styxlisten 'tcp!*!styx' export /
+.EE
+.SH SOURCE
+.B /appl/cmd/dial.b
+.br
+.B /appl/cmd/listen.b
+.SH BUGS
+The way that
+.I styxlisten
+is implemented means that the
+.B aname
+from the remote
+.IR mount (2)
+request cannot be passed through to the
+attach message seen by the command that
+has been started by
+.IR styxlisten .
+.SH "SEE ALSO"
+.IR dial (2),
+.IR ssl (3),
+.IR auth (6),
+.IR svc (8)
--- /dev/null
+++ b/man/1/logon
@@ -1,0 +1,77 @@
+.TH LOGON 1
+.SH NAME
+logon \- log on to Inferno
+.SH SYNOPSIS
+[
+.B wm/wm
+]
+.B wm/logon
+[
+.B -l
+] [
+.BI "-n nsfile"
+] [
+.BI "-u user"
+]
+.SH DESCRIPTION
+.I Logon
+logs a user in to the Inferno environment.
+It requires
+.I wm (1)
+to be started first.
+If no
+.I user
+name is specified by the
+.B -u
+option,
+.I logon
+displays a login panel to prompt for one.
+The user name must have a directory
+.BI /usr/ user,
+which will become the current directory.
+(Otherwise,
+.I logon
+will display a diagnostic panel and prompt again.)
+The user name is written to
+.B /dev/user
+(see
+.IR cons (3)),
+which is the name presented on subsequent attaches to file servers.
+.PP
+Normally,
+.I logon
+expects keyboard input to provide a name,
+but if the
+.B -l
+option is given,
+.I logon
+displays a list of the names in
+.BR /usr ,
+allowing one to be selected using a mouse or touch screen.
+.PP
+Once the current directory has been set,
+.I logon
+creates a new name space for the user using the contents of
+.I nsfile
+(default:
+.BR namespace ),
+as described in
+.IR namespace (6).
+It then starts
+.IR toolbar (1)
+to provide the initial application environment.
+.SH FILES
+.TF /dev/userxx
+.TP
+.B /dev/user
+Inferno user name
+.TP
+.BI /usr/ user
+.IR user 's
+home directory
+.SH SOURCE
+.B /appl/wm/logon.b
+.SH SEE ALSO
+.IR toolbar (1),
+.IR wm (1),
+.IR namespace (6)
--- /dev/null
+++ b/man/1/logwindow
@@ -1,0 +1,23 @@
+.TH LOGWINDOW 1
+.SH NAME
+logwindow \- window that pops up when data becomes available.
+.SH SYNOPSIS
+.B wm/logwindow
+.RI [ title ]
+.BI < logfile
+.SH DESCRIPTION
+.I Logwindow
+reads data from its standard input (often a file served by
+.IR logfile (4))
+and shows it in a text widget.
+If the window is hidden (it is hidden initially),
+it will reappear when
+data appears on its standard input. If
+.I title
+is given, it will be used as the title of the window.
+.SH SOURCE
+.B /appl/wm/logwindow.b
+.SH SEE ALSO
+.IR logfile (4)
+.SH BUGS
+The text buffer grows without bound.
--- /dev/null
+++ b/man/1/look
@@ -1,0 +1,90 @@
+.TH LOOK 1
+.SH NAME
+look \- find lines in a sorted list
+.SH SYNOPSIS
+.B look
+[
+.BI -dfnix
+] [
+.BI -r " endkey"
+] [
+.BI -t c
+] [
+.I string
+]
+[
+.I file
+]
+.SH DESCRIPTION
+.I Look
+consults a sorted
+.I file
+and prints all lines that begin with
+.IR string .
+It uses binary search.
+.PP
+The following options are recognised:
+.TP
+.B -i
+Interactive.
+There is no
+.I string
+argument; instead
+.I look
+takes lines from the standard input as strings to be looked up.
+.TP
+.B -x
+Exact.
+Print only lines of the file whose key matches
+.I string
+exactly.
+.TP
+.B  -d
+`Directory' order:
+only letters, digits,
+tabs and blanks participate in comparisons.
+.TP
+.B  -f
+Fold.
+Upper case letters compare equal to lower case.
+.TP
+.B -n
+Numeric comparison with initial string of digits, optional minus sign,
+and optional decimal point.
+.TP
+.BI -r " endkey"
+Limit the range of matching values, to include
+the word
+.I endkey
+but no larger values.
+.TP
+.BR -t [ \f2c\f1 ]
+Character
+.I c
+terminates the sort key in the
+.IR file .
+By default, tab terminates the key.  If
+.I c
+is missing the entire line comprises the key.
+.PP
+If no
+.I file
+is specified,
+.B /lib/words
+is assumed, with collating sequence
+.BR df .
+.SH FILES
+.B /lib/words
+.SH SOURCE
+.B /appl/cmd/look.b
+.SH "SEE ALSO"
+.IR sort (1), 
+.IR grep (1)
+.SH DIAGNOSTICS
+The exit status is
+.B \&"not found"
+if no match is found, and
+.B \&"no dictionary"
+if
+.I file
+or the default dictionary cannot be opened.
--- /dev/null
+++ b/man/1/ls
@@ -1,0 +1,173 @@
+.TH LS 1
+.SH NAME
+ls, lc \- list files
+.SH SYNOPSIS
+.B ls
+[
+.B -lpmnqduntscrFT
+] [
+.IR file ...
+]
+.LP
+.B lc
+[
+.B -lpmnqduntscrFT
+] [
+.IR file ...
+]
+.SH DESCRIPTION
+.I Ls
+lists the named
+.IR file s
+in an order and format determined by its options.
+The options determining the output format are:
+.TP 10
+.B -l
+Produce output in long format. The information given in
+each column is as follows:
+.RS
+.IP 1.
+The permission mode of the file. This is formatted as 11 characters;
+the first is
+.RB ` d '
+if the file is a directory,
+.RB ` a '
+if the file is append-only,
+.RB ` A '
+if it is an authentication file,
+or
+.RB ` - '
+otherwise.
+The next character is
+.RB ` l '
+if the file is exclusive-use,
+or
+.RB ` - '
+otherwise.
+The remaining characters are in three groups
+of three, each representing one permission bit. Each character
+is either
+.RB ` r '
+(read permission),
+.RB ` w '
+(write permission),
+.RB ` x '
+(execute permission)
+or 
+.RB ` - '
+(no permission).
+The three groups represent permissions granted for that file
+to the file's owner, members of the file's group and anybody else
+respectively.
+.IP 2.
+The device type (this is the `#' device letter for local devices
+or `M' for files mounted over a 9P connection).
+.IP 3.
+The device instance number (this distinguishes between
+separately mounted instances of the same device).
+.IP 4.
+The file's owner.
+.IP 5.
+The file's group.
+.IP 7.
+The size of the file in bytes.
+.IP 8.
+The date and time the file was last modified (see also the
+.B -u
+and the
+.B -e
+options).
+.IP 9.
+The name of the file.
+.RE
+.TP
+.B -m
+Print the name of the user who most recently modified
+the file.
+.TP
+.B -q
+Print the file's
+.I qid
+(see
+.IR sys-stat (2))
+at the beginning of each line;
+the printed fields are in the order
+path, version, and type.
+.TP
+.B -u
+Applicable only to the
+.B -l
+and
+.B -t
+options: causes time-sorted listings to be listed by
+time of last access, and the access time to be printed
+in long-format listings instead of the modification time.
+.TP
+.B -e
+Applicable only to the
+.B -l
+and
+.B -u
+options: causes the time to be displayed as seconds since the epoch.
+.TP
+.B -p
+Print each filename as a bare name, without the name
+of the containing directory.
+.PP
+The other options relate to the order in which the listed files
+are printed, and which files are selected. Usually, each
+.I file
+that is a directory has its contents printed. The
+.B -d
+option causes the directory itself to be listed.
+In a union directory, it is possible for there to be
+two or more instances of a file with the same name.
+The
+.B -c
+option causes only the first one occurring to be
+listed. The options relating to ordering are:
+.TP 10
+.B -n
+Do not sort the files at all.
+.TP
+.B -t
+Sort by modification time (most recent first)
+or access time if the
+.B -u
+option is also specified.
+.TP
+.B -s
+Sort by size (smallest first).
+.TP
+.B -r
+Reverse the sort order.
+.TP
+.B  -F
+Add the character
+.B /
+after all directory names
+and the character
+.B *
+after all executable files.
+.TP
+.B -T
+Print the character
+.B t
+before each file if it has the temporary flag set, and
+.B -
+otherwise.
+.PP
+.I Lc
+is the same as
+.IR ls ,
+but sets the
+.B -p
+option and pipes the output through
+.IR mc (1).
+.SH SOURCE
+.B /appl/cmd/ls.b
+.br
+.B /dis/lc
+.SH SEE ALSO
+.IR readdir (2),
+.IR mc (1)
--- /dev/null
+++ b/man/1/m4
@@ -1,0 +1,272 @@
+.TH M4 1 
+.SH NAME
+m4 \- macro processor
+.SH SYNOPSIS
+.B m4
+[
+.BI -D name = value
+] [
+.BI -Q name = value
+] [
+.BI -U name
+] [
+.I file
+\&...
+]
+.SH DESCRIPTION
+.I M4
+is a general-purpose macro processor.
+It copies text from each of the input
+.I files
+in order (or standard input by default), and writes the processed text to the standard output.
+.PP
+Macro calls
+have the form
+.IP
+.IB name ( arg1,\ arg2,\ ...,\ argn )
+.PP
+The `(' must immediately follow the name of the macro.
+If a defined macro name is not followed by a `(',
+it is deemed to have no arguments.
+Leading unquoted blanks, tabs, and newlines are ignored while collecting arguments.
+A comma within a nested parenthesis is part of an argument value, not an argument separator.
+Potential macro names consist of alphabetic letters, Unicode characters,
+digits, and underscore `\_', where the first character is not a digit.
+.PP
+Comments begin with the
+.B #
+character and extend to the end of that line; the characters in a comment are copied to the current
+output stream unchanged.
+The comment start and end sequences may be changed using the
+.B changecom
+call described below.
+.PP
+The left and right single quotes (ie, grave and acute accents \`\|\' ) are used to quote strings.
+Because the left and right quotes are distinct, quoted strings may nest.
+The value of a quoted string is the string stripped of the outermost quotes.
+The left and right quote characters may be changed using the
+.B changequote
+call described below.
+.PP
+When
+.I m4
+recognises a macro name, followed by a `(', it collects arguments up to a matching right parenthesis.
+Macro evaluation proceeds normally during this collection, and the text produced by those macro calls
+is interpreted exactly as if it had been in the original input stream (in place of the corresponding macro call).
+Thus, any commas or right parentheses within the value of a nested
+call are as effective as those in the original input text.
+(Remember however that commas within
+.I nested
+parentheses are not argument separators.)
+After argument collection,
+the value of the macro is pushed back onto the input stream
+and rescanned.
+.PP
+.I M4
+makes available the following built-in macros.
+They may be redefined, but once this is done the original meaning is lost.
+Their values are null unless otherwise stated.
+.TF changequote
+.TP
+changecom
+Change the starting and ending delimiters for subsequent comments to the first and second arguments.
+If the second argument is missing or an empty string, comments will be ended by newline.
+If there are no arguments, there are no comments.
+.TP
+changequote
+Change quote characters to the first and second arguments.
+.I Changequote
+without arguments restores the original values of
+.BR `\|' .
+.TP
+copydef
+The second argument is installed with the value of the macro
+named by the first argument,
+which may be a built-in macro.
+Typically both arguments are quoted to prevent too early expansion.
+A macro can be renamed using
+.B copydef
+followed by
+.BR undefine .
+.TP
+define
+The second argument is installed as the value of the macro
+named by the first argument.
+When the macro is later called (expanded),
+each occurrence in the replacement text of
+.BI $ n,
+where
+.I n
+is a digit,
+is replaced by the
+.IR n -th
+argument of that macro call.
+Argument 0 is the name of the macro;
+missing arguments are replaced by the null string.
+If the macro value is the same as its name, or the value is
+.BR $0 ,
+the result is the macro name.
+To prevent expansion of a name when redefining a macro, quote the first argument.
+.TP
+divert
+.I M4
+maintains 10 output streams,
+numbered 0-9.
+The final output is the concatenation of the streams
+in numerical order;
+initially stream 0 is the current stream.
+The
+.I divert
+macro changes the current output stream to its (digit-string)
+argument.
+Output diverted to a stream other than 0 through 9
+is discarded.
+.TP
+divnum
+Returns the value of the current output stream.
+.TP
+dnl
+Reads and discards characters up to and including the next newline.
+.TP
+dumpdef
+Prints current names and definitions,
+for the named items, or for all if no arguments are given.
+.TP
+errprint
+Prints its argument
+on the diagnostic output file.
+.TP
+eval
+Evaluates its argument as an arithmetic expression, using 32-bit arithmetic,
+and returns the result as a signed decimal integer.
+The only literals are decimal integers.
+Operators are those of Limbo: the binary operators
+.BR || ,
+.BR && ,
+.BR | ,
+.BR ^ ,
+.BR & ,
+.BR "== !=" ,
+.BR "< > >= <=" ,
+.B "<< >>"
+(arithmetic shifts),
+.BR "+ -" ,
+.BR "* / %" ,
+.BR "**" " (power)";
+the unary operators
+.BR + ,
+.BR - ,
+.BR ~ ,
+.BR ! ;
+and parenthesis.
+Operator precedence is the same as in Limbo.
+Right shifts are signed.
+.TP
+ifdef
+If the first argument is defined, the value is the second argument, otherwise the third.
+If there is no third argument, the value is null.
+The word
+.B inferno
+is predefined with
+.RB ` inferno '
+as its replacement text.
+.TP
+ifelse
+Has three or more arguments.
+If the first argument is the same string as the second,
+then the value is the third argument.
+If not, the process is repeated with arguments 4, 5, 6 and so on, in groups of three.
+If no match is found, the result is the remaining argument (not part of a group of three),
+or null if none is present.
+.TP
+include
+Returns the contents of the file named in the argument.
+.TP
+incr
+Returns the value of its argument incremented by 1.
+The value of the argument is calculated
+by interpreting an initial digit-string as a decimal number.
+.TP
+index
+Returns the position in its first argument where the second argument begins (zero origin),
+or \-1 if the second argument does not occur.
+.TP
+len
+Returns the number of characters in its argument.
+.TP
+maketemp
+Returns its first argument after replacing any trailing XXXs by the current host name, process ID, and a unique letter.
+Normally used to create unique temporary file names.
+.TP
+sinclude
+The same as
+.I include,
+except that it
+says nothing if the file is inaccessible.
+.TP
+substr
+Returns a substring of its first argument.
+The second argument is a zero origin
+number selecting the first character;
+the third argument indicates the length of the substring.
+A missing third argument is taken to be large enough to extend to
+the end of the first string.
+.TP
+syscmd
+Runs the first argument as an
+.IR sh (1)
+command.
+No value is returned.
+Note that the output of a command can be redirected to a temporary file named by
+.BR maketemp ,
+included, and then removed.
+.TP
+translit
+Transliterates the characters in its first argument
+from the set given by the second argument to the set given by the third.
+No abbreviations are permitted.
+.TP
+undefine
+Removes the definition of the macro named in its argument.
+.TP
+undivert
+Causes immediate output of text from diversions named as
+arguments, or all diversions if no argument.
+Text may be undiverted into another diversion.
+Undiverting discards the diverted text.
+.PD
+.PP
+.I M4
+interprets its command line options after installing the predefined macro set.
+The
+.B -D
+option defines
+.I name
+as a macro with the given
+.IR value ;
+.B -Q
+defines
+.I name
+as a macro with the given
+.IR value
+that is regarded as always quoted (ie, is never rescanned).
+Neither
+.B -D
+nor
+.B -Q
+may change a predefined macro.
+The
+.B -U
+option
+.I undefines
+the given macro
+.IR name ,
+which may be one of the predefined macros.
+.PP
+.I M4
+in Inferno is more closely related to the original
+.I m4
+in Seventh Edition UNIX than its more elaborate relatives in System V and POSIX.
+.SH "SEE ALSO"
+B. W. Kernighan and D. M. Ritchie,
+.I The M4 Macro Processor
--- /dev/null
+++ b/man/1/man
@@ -1,0 +1,244 @@
+.TH MAN 1
+.SH NAME
+man, wm/man, man2html, man2txt, lookman, sig \-
+print or find manual pages
+.SH SYNOPSIS
+.B man
+[
+.B -b
+] [
+.B -n
+] [
+.B -p
+] [
+.B -S
+] [
+.B -w
+]
+[
+.I section ...
+]
+.I title ...
+.br
+.B man -f
+.I file ...
+.br
+.B wm/man
+[
+.I section ...
+]
+.I title ...
+.br
+.B wm/man -f
+.I file ...
+.br
+.B man2html
+[
+.BI -h " header"
+] [
+.BI -i " initial"
+] [
+.BI -t " trailer"
+]
+.I file
+[
+.I section
+]
+.br
+.B man2txt
+[
+.B -p
+.I width
+]
+[
+.I file ...
+]
+.br
+.B lookman
+[
+.B -f
+]
+.I keyword ...
+.PP
+.B sig
+.I function ...
+.SH DESCRIPTION
+Both
+.I man
+and
+.BI wm/ man
+locate entries in this manual and display them.
+The pages for entries named
+.I title
+within each specified
+.IR section
+are displayed.
+If no sections are specified, matching pages
+from all sections are printed.
+Sections are given by number.
+.PP
+The
+.B -f
+option to
+.I man
+and
+.I wm/man
+prevent lookup in the manual index.
+Instead, the remaining arguments are treated as
+filenames.
+.I Man
+processes each file in turn.
+.I Wm/man
+adds each file to its page history and displays the first document in the list.
+.PP
+The
+.I man
+command prints the manual pages as formatted plain text to standard output.
+Manual pages are written using Plan9
+.I "troff -man"
+macros for their markup and so
+some detail is lost in conversion to plain text.
+.BI Wm/ man
+displays the pages in a graphical Wm window, providing a more faithful
+reproduction of the intended layout.
+.PP
+.I Man
+also accepts the following options:
+.TP
+.B -b
+Print the pages and send them to
+.IR plumber (1)
+for display in an editor.
+.TP
+.B -n
+Use
+.I man2txt
+to format the pages (default).
+.TP
+.B -p
+Display the pages using
+.IR wm/man .
+.TP
+.B -S
+Do not search the manual indices for the names: only print pages whose file names match the
+.IR titles .
+.TP
+.B -w
+Print the names of the man page source files instead of formatting them.
+.PP
+.I Man2html
+converts
+.B "troff -man"
+macro markup to an approximation in HTML on standard output.
+Only one file is processed at a time.
+It is assumed the input
+.I file
+is a manual page, in the given
+.I section
+(default: 1).
+The optional
+.I header
+string replaces the default header
+.BR "<HTML><HEAD>" .
+The optional
+.I initial
+text will appear immediately after
+.BR "<BODY>" .
+The optional
+.I trailer
+string replaces the default trailer
+.BR "</BODY></HTML>" .
+.PP
+.I Man2txt
+converts
+.B "troff -man"
+macro markup
+to plain text.
+Each file is processed separately.
+If no arguments are given, text from standard input is processed.
+The converted text is written to standard output.
+The
+.B -p
+option to
+.I man2txt
+specifies the page width in characters.
+.PP
+.I Lookman
+finds the manual pages, in any section, that
+contain all of the
+.I keywords
+given as arguments, and prints
+.I man
+commands and manual references for them, one per line.
+In a
+.IR wm-sh (1)
+window,
+any of the
+.I man
+commands can be selected with mouse button 2 and
+sent
+as a command; a manual reference can simply be
+.IR plumb (1)'d
+using mouse button 3.
+The
+.B -f
+option causes
+.I lookman
+just to list the file names.
+.PP
+.I Sig
+prints the type signature \- parameters and return types \- of
+each
+.I function
+found in section 2 of this manual.
+.SH FILES
+.TF /man/1/INDEX
+.TP
+.B /man/?/*
+Source files of manual pages.
+.TP
+.B /man/1/man
+The source file for this manual page.
+.TP
+.B /man/?/INDEX
+Used by
+.I man
+and
+.BI wm/ man
+to locate the source file containing a particular title.
+.TP
+.B /man/index
+The
+.I lookman
+index.
+.SH SOURCE
+.B /appl/wm/man.b
+.br
+.B /dis/man
+.IR sh (1)
+script
+.br
+.B /appl/cmd/man2txt.b
+.br
+.B /dis/lookman
+.IR sh (1)
+script
+.br
+.B /dis/sig
+.IR sh (1)
+script
+.br
+.B /appl/lib/parseman.b
+.SH "SEE ALSO"
+.IR wm (1)
+.SH BUGS
+.I Man2txt
+only knows about
+.I "troff -man"
+macros.
+Other troff macro packages or output from preprocessors
+such as
+.I pic
+or
+.I tbl
+will not be presented correctly.
--- /dev/null
+++ b/man/1/mash
@@ -1,0 +1,635 @@
+.TH MASH 1
+.SH NAME
+mash \- programmable shell
+.SH SYNOPSIS
+.B mash
+[
+.B -denx
+][
+.BI -c command
+] [
+.I file
+[
+.I arg ...
+]]
+.SH DESCRIPTION
+.I Mash
+is an older alternative to the original Inferno shell
+(now
+.IR tiny (1)).
+.I Mash
+is a programmable shell that also allows the definition of simple dependency
+rules, resembling those of Unix
+.IR make .
+It executes commands read from standard input 
+or a file or, with the
+.B -c
+flag, from
+.IR mash 's
+argument list. Its syntax and 
+semantics are similar to that of Plan 9's
+.IR rc .
+
+.SS Invocation
+If
+.I mash
+is started with no arguments it reads commands from standard 
+input. Otherwise its first non-flag argument is the name of a file from 
+which to read commands (but see
+.B -c
+below). Subsequent arguments 
+become the initial value of
+.BR $args .
+Mash accepts the following 
+command-line flags.
+.PD 0
+.TF "-c string"
+.TP
+.BI -c " string"
+Commands are read from string.
+.TP
+.B -d
+Dump parsed commands before execution.
+.TP
+.B -e
+Fail if a top level command does. 
+.TP
+.B -n 
+Parse but do not execute. 
+.TP
+.B -x 
+Print each simple command before executing it.
+.PD
+.PP
+If invoked without arguments
+.I mash
+first runs the commands found in
+.BR /lib/mashinit .
+
+.SS "Command Lines"
+Each command is terminated with an ampersand or a semicolon (& or ;). 
+When reading from
+.B /dev/cons
+a newline 
+not escaped with a backslash (\\) is treated as a semicolon.
+.I Mash
+does not 
+wait for a command followed by
+.B &
+to finish executing before starting the 
+following command. 
+.PP
+A number-sign (#) and any following characters up to (but not including) the next newline are 
+ignored, except in quotation marks. 
+
+.SS "Simple Commands"
+A simple command is a sequence of words interspersed with I/O 
+redirections. If the first word is the name of a
+.I mash
+function or of one of 
+.IR mash 's
+built-in commands, it is executed by
+.IR mash .
+Otherwise, if the name 
+starts with a slash
+.RB ( / ),
+it must be the path name of a Dis
+file to be loaded 
+and executed. Names containing no initial slash are searched for in the 
+current directory and then in
+.BR /dis .
+The
+.B .dis
+extension need not be 
+supplied. 
+.PP
+The keywords are
+.EX
+	case else fn for hd if in len rescue tl while 
+.EE
+
+.SS Words and Variables
+A number of constructions may be used where
+.IR mash 's
+syntax requires a 
+word to appear. In many cases a construction's value will be a list of 
+strings rather than a single string. 
+.PP
+The simplest kind of word is the unquoted word: a sequence of one or 
+more characters none of which is a blank, tab, newline, or any of the 
+following:
+.EX
+	# : ; ! ~ @ & | ^ $ = " ` ' { } ( ) < > 
+.EE
+An unquoted word that contains any of the characters
+.BR * ,\  ?
+or
+.B [
+is a pattern 
+for matching against file names. The character
+.B *
+matches any sequence of 
+characters,
+.B ?
+matches any single character, and
+.B [
+.I class
+.B ]
+matches any 
+character in the class. The class may also contain pairs of characters 
+separated by
+.BR \- ,
+standing for all characters lexically between the two. The 
+character
+.B /
+must appear explicitly in a pattern. A pattern is replaced by a 
+list of words, one for each path name matched, except that a pattern 
+matching no names is not replaced by the empty list, but rather stands for 
+itself. Pattern matching is done after all other operations. Thus,
+.EX
+	x=/tmp; echo $x^/*.c; 
+.EE
+matches
+.BR /tmp/*.c ,
+rather than matching
+.B /*.c
+and then prefixing
+.BR /tmp . 
+.PP
+A quoted word is a sequence of characters surrounded by single quotes 
+.RB ( ' ).
+A single quote is escaped with a backslash. 
+.PP
+Each of the following is a word.
+.PD 0
+.HP
+.BI $ identifier 
+.br
+The identifier after the
+.B $
+is the name of a variable whose value is 
+substituted. Variable values are lists of strings. It is an error if the 
+named variable has never been assigned a value.
+.HP
+.BI $ number 
+.br
+See the description of rules for the 
+.IR mash-make (1)
+builtin.
+.HP
+.B $"\c
+.I identifier
+.br
+The value is a single string containing the components of the named 
+variable separated by spaces.
+.HP
+.BI `{ commands }
+.br
+.I mash
+executes the commands and reads the standard output, splitting 
+it into a list of words, using the whitespace characters (space, tab,
+newline and carriage return) as separators.
+.HP
+.B
+"{\c
+.IB commands }
+.br
+.I mash
+executes the commands and reads the standard output, splitting 
+it into a list of words, using the whitespace characters (space, tab,
+newline and carriage return) as separators.
+.HP
+.BI <{ commands }
+.HP
+.BI >{ commands }
+.br
+The commands are executed asynchronously with their standard 
+output or standard input connected to a pipe. The value of the word 
+is the name of a file referring to the other end of the pipe. This 
+allows the construction of non-linear pipelines. For example, the 
+following runs two commands
+.B old
+and
+.B new
+and uses
+.B cmp
+to compare their outputs
+.EX
+	cmp <{old} <{new}; 
+.EE
+.TP
+.BI ( expr )
+.I mash
+evaluates
+.I expr
+as an expression. The value is a (possibly null) list of words.
+Expressions are described in detail below.
+.HP
+.IB word ^ word
+.br
+The
+.B ^
+operator concatenates its two operands. If either operand is a 
+singleton, the concatenation is distributive. Otherwise the lists are 
+concatenated pairwise.
+
+.PD
+.SS Free Carets
+In most circumstances,
+.I mash
+will insert the
+.B ^
+operator automatically 
+between words that are not separated by white space. Whenever one of
+.B $ 
+.BR ' or
+.B `
+(dollar, single quote or back quote)
+follows a quoted or unquoted word or an unquoted word follows a 
+quoted word with no intervening blanks or tabs, a
+.B ^
+is inserted between 
+the two. If an unquoted word immediately follows a
+.B $
+and contains a 
+character other than an alphanumeric, or underscore, a
+.B ^
+is inserted before 
+the first such character. Thus 
+.IP
+.B limbo -$flags $stem.b
+.LP
+is equivalent to 
+.IP
+.B limbo -^$flags $stem^.b 
+
+.SS I/O Redirections
+The sequence
+.BI > file
+redirects the standard output file (file descriptor 1, 
+normally
+.BR /dev/cons )
+to the named
+.IR file ;
+.BI >> file
+appends standard 
+output to the file. The standard input file (file descriptor 0, also normally 
+.BR /dev/cons )
+may be redirected from a file by the sequence
+.BR <file .
+The 
+sequence
+.B <>file
+opens the file for read/write and associates both file 
+descriptor 0 and 1 with it.
+
+.SH Compound Commands
+A pair of commands separated by a pipe operator
+.RB ( | )
+is a command. The 
+standard output of the left command is sent through a pipe to the 
+standard input of the right command. 
+.PP
+Each of the following is a command.
+.PD 0
+.HP
+.B if (
+.I expr
+.B )
+.I command1
+.HP
+.B if (
+.I expr
+.B )
+.I command1
+.B else
+.I command2
+.br
+The
+.I expr
+ is evaluated and if the result is not null, then 
+.I command1
+is executed. In the second form
+.I command2
+is executed if 
+the result is null.
+.HP
+.B for (
+.I name
+.B in
+.I list
+.BI ) command
+.br
+The
+.I command
+is executed once for each word in
+.I list
+with that 
+word assigned to
+.IR name .
+.HP
+.B while (
+.I expr
+.B )
+.I command
+.br
+The
+.I expr
+is evaluated repeatedly until its value is null. Each time it 
+evaluates to non-null, the command is executed.
+.HP
+.B case
+.I expr
+.B {
+.I pattern-list
+.B =>
+.I command ...
+.B }
+.br
+.HP
+.B case
+.I expr-list
+.B {
+.I pattern
+.B =>
+.I command ...
+.B }
+.br
+In the first form of the command the
+.I expr
+is matched against a series of lists of regular expressions (See
+.IR regex (2)).
+The command associated with the matching expression is executed.
+In the second form the
+.I command
+associated with the first pattern to match one of the words in
+.I expr-list
+is executed. An
+.I expr-list
+will never match a
+.IR pattern-list .
+.HP
+.BI { commands }
+.HP
+.BI @{ commands }
+.br
+Braces serve to alter the grouping of commands implied by operator 
+priorities. The body is a sequence of commands separated by
+.B &
+or
+.BR ; . 
+The second form is executed with a new scope. Either form can be 
+followed by redirections.
+.HP
+.BI fn name { list }
+.HP
+.BI fn name
+.br
+The first form defines a function with the given
+.IR name .
+Subsequently, 
+whenever a command whose first word is
+.I name
+is encountered, the 
+current value of the remainder of the command's word list will be 
+assigned to the local variable
+.BR args ,
+in a new scope, and
+.I mash
+will  execute the list. The second form removes
+.IR name 's
+function definition.
+.HP
+.IB name = list
+.HP
+.IB name := list
+.br
+The first form is an assignment to a variable. If the name is currently 
+defined as a local variable its value will be updated. Otherwise a 
+global variable with the given name will be defined or updated. The 
+second form is an explicit definition or update of a local variable.
+.HP
+.IB list : list
+.HP
+.IB list : list { commands }
+.HP
+.IB word :~ word { commands }
+.br
+These forms define dependencies and rules for the
+.I make
+loadable 
+builtin. The first form defines a simple dependency, the second a 
+dependency with an explicit rule. The third form defines an implicit 
+rule where the left-hand word is a file pattern, the right-hand word is 
+the prerequisite. The right-hand word and the commands can 
+contain references to the characters matched by the
+.B *
+meta-character 
+in the pattern
+.RB ( $1
+evaluates to the characters matched by the first
+.BR * , 
+.B $2
+the second and so on;
+.B $0
+is the entire match). 
+.PD
+
+.SS Expressions
+Expressions evaluate to possibly null lists of strings. A word is an 
+expression. An expression may take one of the following forms
+.PD 0
+.HP
+.BI ( " expr " )
+.br
+Parentheses are used for grouping.
+.HP
+.BI hd " expr"
+.HP
+.BI tl " expr"
+.HP
+.BI len " expr"
+.HP
+.BI ! " expr"
+.br
+.I hd
+is the first element of a list,
+.I tl
+the remainder.
+.I len
+is the length of 
+a list. Both evaluate to the null list if their operand is a null list.
+.B !
+is the not operator and evaluates to true for a null list or to a null list 
+otherwise.
+.HP
+.IB expr " ^ " expr
+.HP
+.IB expr " :: " expr
+.HP
+.IB expr " == " expr
+.HP
+.IB expr " != " expr
+.HP
+.IB expr " ~ " expr
+.br
+.B ^
+is concatenation (as defined above),
+.B ::
+is list concatenation,
+.B ==
+and 
+.B !=
+are the equality operators evaluating to true or the null list, 
+depending on the equality or inequality of the two operands.
+.B ~
+is the match operator, true if a singleton string matches one of a list of 
+regular expressions, or one of a list of strings matches a regular 
+expression. (If neither operand is a singleton it evaluates to the null 
+list.)
+.B ^
+has the highest precedence, followed by
+.B ::
+followed by the 
+other three. All associate to the left except
+.BR :: .
+
+.SS Built-in Commands
+
+.I Mash
+supports loadable modules of builtins. The
+.I Mashbuiltin
+module definition and description is in
+.BR mash.m .
+One such module,
+.IR builtins ,
+is loaded before
+.I mash
+begins parsing. This module defines the following commands
+.PD 0
+.HP
+.B env
+.br
+Print global and local variables. Global variables are displayed using a
+.IB name = value
+format, and local variables using a
+.IB name := value
+format.
+.HP
+.B eval
+.br
+Concatenate arguments and use as mash input.
+.HP
+.B exit
+.br
+Cause
+.I mash
+to raise an
+.B "exit"
+exception.
+.HP
+.B load
+.I file
+.br
+Load a builtin module. The
+.I file
+must be a module with type
+.BR Mashbuiltin .
+The argument
+.I file
+is assumed to contain a path to the loadable module. If no such module
+is found then the string
+.B /dis/lib/mash/
+is prepended to
+.I file
+and the load is retried.
+.HP
+.B prompt
+.HP
+.BI prompt text
+.HP
+.BI prompt "text contin"
+.br
+When called with no arguments causes the current value of the
+.I mash
+prompt to be printed to standard output. The default value is
+.BR mash% .
+The second form sets a new prompt. The final form sets a new prompt
+and additionally a continuation string. Initially the continuation
+string is set to a single tab character.
+.I Mash
+uses the continuation string in place of the prompt string to indicate that
+the preceding line has been continued by escaping with a final backslash
+.RB ( \e )
+character.
+.HP
+.BI quote args...
+.br
+Print arguments quoted as input for
+.IR mash . 
+.HP
+.BI "run -" [ denx "] file [arg...]"
+.br
+Interpret a file as input to
+.IR mash .
+.HP
+.BI time "cmd [arg...]"
+.br
+Time the execution of a command. The total execution time is reported
+in seconds and on standard error when the command completes.
+.HP
+.BI whatis name
+.br
+Print variable, function or builtin. The object given by
+.I name
+is described on standard output in a format that reflects its type.
+.PP
+The
+.I make
+loadable builtin provides `make` functionality.
+The
+.I tk
+loadable builtin provides control over some of the visual elements of a
+.I mash
+window.
+
+.SS Adding Builtins
+New builtins can be added to
+.I mash
+by creating a
+.I Dis
+module that can be loaded with a
+.B Mashbuiltin
+module interface (defined in mash.m).
+The new module is loaded with the builtin
+.B load
+command which calls its
+.B mashinit
+function to initialise it with an argument containing the
+.B load
+command line. The function should use this call to register the set of builtins
+that the module will provide using the
+.B Env.defbuiltin
+function. Thereafter, each time one of the registered builtins is invoked
+the module's
+.B mashcmd
+function is called passing as an argument a list containing the invoked
+builtin name and its arguments. See the examples in
+.BR mash/builtins.b ,
+.BR mash/make.b ", and"
+.BR mash/tk.b .
+.SH FILES
+.B /lib/mashinit
+.br
+.B /dis/lib/mash
+.SH SOURCE
+.B /appl/cmd/mash 
+.SH SEE ALSO
+.IR mash-tk (1),
+.IR mash-make (1),
+.IR regex (2)
+.PP
+Tom Duff,
+``Rc  \- The Plan 9 Shell'', in the
+.I "Plan 9 Programmer's Manual", Second Edition,
+Volume 2.
--- /dev/null
+++ b/man/1/mash-make
@@ -1,0 +1,182 @@
+.TH MASH-MAKE 1
+.SH NAME
+mash-make \- builtin `make' functionality
+.SH SYNOPSIS
+.B load make
+
+.B make
+[
+.I -clear
+]
+.br
+.B depends
+[
+.I target
+]
+.br
+.B match
+.I pattern
+.br
+.B rules
+[
+.I pattern
+]
+.br
+.SH DESCRIPTION
+.I Make
+is a loadable builtin for
+.IR mash .
+It can be taught about dependencies that exist between components of a program and
+rules for reconstructing the components of a program.
+.PP
+.I Make
+will examine file modification times to determine which components need
+to be updated and will
+issue commands to reconstruct them in the correct sequence.
+It will update a target if any of its prerequisites are more up to date than the target
+or if the target does not exist.
+.PP
+Typically, dependencies and rules are kept in a file called
+.BR mashfile .
+The sequence
+.PP
+.EX
+	load make
+	run mashfile
+.EE
+.PP
+is used to load the
+.I make
+builtin and read the rules from
+.BR mashfile .
+Thereafter, the command
+.IP
+.B make
+.I target
+.PP
+will perform the correct sequence of operations to reconstruct
+.I target
+and its dependents.
+.PP
+A dependency is specified in
+.I mash
+with a line of the form:
+.IP
+.IB target-list " : " dependent-list " ; "
+.PP
+or
+.IP
+.IB target-list " : " dependent-list  " { " rules " }  ;"
+.PP
+Each of the targets in
+.I target-list
+depends upon each of the dependents in
+.IR dependent-list .
+The optional
+.I rules
+specify how to build the targets. For example
+.EX
+	lflags = -Cg;
+	rotta.dis : rotta.b rotta.m { limbo $lflags rotta.b };
+.EE
+If the rules are omitted
+.I make
+must be able to infer them from implicit rules.
+.PP
+An implicit rule is defined with a line of the form:
+.IP
+.I pattern
+.B :~
+.I dependency
+.B {
+.I rules
+.B }
+.PP
+If
+.I pattern
+matches a target that
+.I make
+needs to build then
+.I make
+will build the target by first making the
+.I dependency
+and then applying the
+.IR rules .
+The wildcard components of the matched target are available in
+the 
+.I dependency
+and the
+.I rules
+as variables
+.BR $1 ,
+.BR $2 ,
+.BR $3 ...
+with
+.B $1
+containing the text matched by the first wildcard,
+.B $2
+the second wildcard
+and so on. The whole of the target is available in the variable
+.BR $0 .
+For example,
+.PP
+.EX
+    /*/*.m :~ $2.m  { cp $2.m /$1/$2.m };
+    /module/rotta.m: rotta.m;
+    /altmodule/frame.m: frame.m;
+.EE
+.PP
+More commonly, implicit rules are defined to provide
+.I make
+with knowledge of how to compile
+.I limbo
+source to produce
+.I Dis
+format binaries.
+Typically,
+.PP
+.EX
+    *.dis       :~ $1.b     { limbo $lflags $1.b};
+    /dis/*.dis  :~ $1.dis   { cp $1.dis /dis};
+.EE
+.PP
+A target is built with the command
+.IP
+.B make
+.I target
+.PP
+The list of rules can be reset with
+.IP
+.B make -clear
+.PP
+The list of dependencies for a target or for all targets can be
+displayed with the command
+.IP
+.B depends
+[
+.I target
+]
+.PP
+The rules that match a pattern and the components of the pattern
+can be displayed with
+.IP
+.B match
+.BI ' pattern '
+.PP
+Taking care to hide the pattern from
+.I mash
+file name pattern matching with quotes.
+The command
+.IP
+.B rules
+[
+.I pattern
+]
+.PP
+will display all the rules or the rules that apply to pattern
+.I pattern
+(if given).
+.SH SOURCE
+.B /appl/cmd/mash/make.b
+.SH "SEE ALSO"
+.IR mash (1)
--- /dev/null
+++ b/man/1/mash-tk
@@ -1,0 +1,212 @@
+.TH MASH-TK 1
+.SH NAME
+mash-tk \- control visual elements of mash window
+.SH SYNOPSIS
+.B load tk
+.br
+.B tk clear
+.br
+.B tk def button
+.I name value
+.br
+.B tk def ibutton
+.I name value image
+.br
+.B tk def menu
+.I name
+.br
+.B tk def item
+.I menu name value
+.br
+.B tk dialog
+.I title mesg default label ...
+.br
+.B tk dump [
+.I name ...
+.B ]
+.br
+.B tk env
+.br
+.B tk file
+.I title dir pattern ...
+.br
+.B tk geom
+.br
+.B tk layout [
+.I name ...
+.B ]
+.br
+.B tk notice
+.I message
+.br
+.B tk sel
+.br
+.B tk sget
+.br
+.B tk sput
+.I string
+.br
+.B tk string
+.I mesg
+.br
+.B tk taskbar
+.I string
+.br
+.B tk text
+.br
+.SH DESCRIPTION
+.I Tk
+is a loadable builtin for
+.IR mash.
+It provides a set of primitives for customizing a
+.I mash
+window and building fairly sophisticated graphical functions. It is currently implemented as a single command with a variety of subcommands. For the
+.I tk
+command to work,
+.I mash
+must have been started using
+.BR wm/wmmash .
+In the following descriptions, references to return values indicate strings put on a command's standard output.
+.SS Creating a Menu Bar
+The
+.B def
+subcommand is used to define graphical pushbuttons and menus. The
+.B def button
+and
+.B def ibutton
+commands are used to define pushbuttons labelled with text or graphical icons, respectively.
+The
+.I name
+parameter is used to label buttons, and to layout both buttons and ibuttons.
+.I Value
+is a command to be executed when the button is clicked, and must be quoted if it contains white space.
+.I Image
+is the name of a bitmap file; it is looked for in
+.BR /icon/tk ,
+unless the name begins with
+.BR@ ,
+which suppresses prepending
+.BR /icon/tk .
+.br
+.B Def menu
+is used to name and label menu buttons, and
+.B def item
+specifies items within the corresponding pulldown menus. In
+.BR "def item" ,
+.I menu
+is a name supplied on a
+.B def menu
+subcommand,
+.I name
+is the label for this menu item, and
+.I value
+is a command to execute when this menu item is selected.
+All the items in a menu are simple command buttons;
+there is no provision for any other kind of control, or for cascading menus.
+.br
+The
+.B layout
+subcommand creates and makes visible a menu bar, using menus and buttons defined with
+.B def
+subcommands. The current components, if any, are removed first,
+so layout with no parameters just removes all the current buttons and menus from the
+.I mash
+window. The components are laid out from left to right, in the order presented in the
+.B layout
+subcommand. A copy of the current
+.I mash
+environment is made, and commands executed as a result of clicking buttons
+or selecting menu items are executed in that environment.
+For example, variables will have the values they had when the layout was done.
+.br
+The
+.B env
+subcommand can be used to make a new copy of the environment for use by button
+or menu actions.
+.SS Displaying Popup Widgets
+The
+.B notice
+subcommand pops up a window containing
+.I message
+and a single button to dismiss the window. The icon displayed in the window is
+.BR /icons/tk/error .
+No value is returned by
+.BR notice .
+.br
+The
+.B dialog
+subcommand is more complex;
+.I title
+is used to name the window, and multiple buttons labelled according to the
+.B label
+parameter(s) are provided.
+.I Default
+is the number of the button which is the default choice. The leftmost button is numbered 0.
+When the user selects one of the buttons, the dialog box is popped down and the
+number of the button selected is returned.
+.br
+The
+.B file
+subcommand pops up a standard Inferno file selection box.
+.I Dir
+specifies the initial directory to display, and pattern specifies which non-directory files to include in the list of files. If the Cancel button in the file dialog is clicked, no value is returned. If a file is selected and the Exit button is clicked, the full pathname of the file is returned, complete with final
+.B /
+if the file is a directory. Double clicking on a non-directory file in the list will likewise return that file's path. Double clicking on a directory in the list will display the contents of that directory.
+.br
+The
+.B string
+subcommand pops up a small window with
+.I mesg
+as the label of a text field. Characters typed into the text field, up to but not including ENTER, are returned.
+.SS Dealing With the Selection
+.br
+The
+.B sel
+subcommand returns whatever is currently selected. When typing into the shell's window, nothing is selected, so nothing is returned. However, if invoked via a pushbutton and there is a selection, it is returned.
+.br
+The
+.B sput
+subcommand puts
+.I string
+into the snarf buffer maintained by the window manager, and the
+.B sget
+subcommand retrieves and returns the current contents of the snarf buffer. This provides a way to pass text between the shell and other applications. The Snarf and Paste buttons on the popup menu associated with mouse button two can also be used to do this.
+
+.SS Miscellaneous Tk Subcommands
+The
+.B taskbar
+subcommand lets you put
+.I string
+in the title bar of the
+.I mash
+window. The old value is returned.
+.br
+The
+.B text
+subcommand returns the contents of the
+.I mash
+window.
+.br
+The
+.B clear
+subcommand removes all text from the window.
+.br
+The
+.B dump
+subcommand returns the
+.I mash-tk
+commands needed to define the buttons and menus currently defined,
+and to recreate the currently visible set of buttons and menus, or, if
+.B dump
+has parameters, the commands needed to define the buttons and menus named by the parameters.
+.br
+The
+.B geom
+subcommand returns the position of the upper left corner of the
+.I mash
+window relative to the upper left corner of the Inferno screen.
+
+.SH SOURCE
+.B /appl/cmd/mash/tk.b
+.SH "SEE ALSO"
+.IR mash (1)
--- /dev/null
+++ b/man/1/math-misc
@@ -1,0 +1,223 @@
+.TH MATH-MISC 1
+.SH NAME
+ack, crackerbarrel, factor, fibonacci, fit, genprimes, mersenne, parts, perms, pi, powers, primes, sieve \- miscellaneous mathematical applications
+.SH SYNOPSIS
+.B math/ack
+[
+.IR m
+]
+[
+.IR n
+]
+.br
+.B math/crackerbarrel
+[
+.IR n
+]
+.br
+.B math/factor
+[
+.IR n
+]
+.br
+.B math/fibonacci
+.br
+.B math/fit
+[
+.BI -d deg
+]
+[
+.B -v
+]
+[
+.IR file
+]
+.br
+.B math/genprimes
+[
+.IR lim
+]
+.br
+.B math/mersenne
+[
+.IR num
+]
+.br
+.B math/parts
+[
+.B -a
+]
+[
+.IR num ...
+]
+.br
+.B math/perms
+[
+.IR n
+]
+.br
+.B math/pi
+[
+.IR dp
+]
+.br
+.B math/powers
+[
+.B -p num
+]
+[
+.B -n num
+]
+[
+.B -f num
+]
+[
+.B -l num
+]
+[
+.B -m num
+]
+[
+.B -v
+]
+.br
+.B math/primes
+[
+.IR m
+]
+[
+.IR n
+]
+.br
+.B math/sieve
+[
+.B -a alg
+]
+[
+.IR lim
+]
+.SH DESCRIPTION
+A collection of simple mathematical utilities.
+.PP
+.TP
+.B math/ack
+Calculates and times Ackermann's function A(m, n).
+.TP
+.B math/crackerbarrel
+Solves the crackerbarrel puzzle
+.B n
+times and outputs the time taken. See the source for details of the puzzle.
+.TP
+.B math/factor
+Factors the number n.
+.TP
+.B math/fibonacci
+Generates the first few terms of the Fibonacci series using recursion
+and user defined exceptions.
+.TP
+.B math/fit
+Fits a polynomial of degree
+.I deg
+to a set of points (x, y) where x is the independent variable, y the dependent one.
+All x and y values should be seperated by white space
+and can be real or integer. The values are read from
+.IR file
+or standard input if none is given. The
+.B -v
+option prints a table of actual and expected y values.
+.TP
+.B math/genprimes
+Generates primes numbers up to and including
+.B lim
+using spawned processes and buffered channels.
+.TP
+.B math/mersenne
+Tests the primality of the Mersenne numbers ie numbers of the form 2^n-1.
+The argument
+.IR num
+is the power of 2 in the above.
+.TP
+.B math/parts
+Calculates the number of partitions of the given number(s). The
+.B -a
+option will print out a table of the number of partitions of all numbers up to  the
+given number(s).
+.TP
+.B math/perms
+Prints out all permutations of
+.B n
+elements.
+.TP
+.B math/pi
+Calculates the value of pi to
+.B dp
+decimal places.
+.TP
+.B math/powers
+Investigates the number of representations of an integer as a sum of
+powers.
+The
+.B -p
+option denotes the power of use (default 2). The
+.B -n
+option denotes the number of powers to sum (default 2). The
+.B -f
+option denotes the minimum number of such representations found before
+reporting them (default 2). The
+.B -l
+and
+.B -m
+options denote the smallest and largest numbers to consider respectively (defaults 0 and 8192). Finally the
+.B -v
+option prints various statistics during the search.
+.TP
+.B math/primes
+Prints out all primes between
+.B m
+and
+.B n .
+.TP
+.B math/sieve
+Prints out prime numbers up to
+.B lim
+using a sieve algorithm. The
+.B -a
+option indicates the level of sophistication of the algorithm (0-4).
+.PP
+.SH EXAMPLE
+.EX
+
+	math/powers -p 3 -m 30000
+gives
+	[2] 1729 = 1**3 + 12**3 = 9**3 + 10**3
+	[2] 4104 = 2**3 + 16**3 = 9**3 + 15**3
+	[2] 20683 = 10**3 + 27**3 = 19**3 + 24**3
+The number of representations found for each integer is indicated in
+square brackets.
+.EE
+.SH SOURCE
+.B /appl/math/ack.b
+.br
+.B /appl/math/crackerbarrel.b
+.br
+.B /appl/math/factor.b
+.br
+.B /appl/math/fibonacci.b
+.br
+.B /appl/math/fit.b
+.br
+.B /appl/math/genprimes.b
+.br
+.B /appl/math/mersenne.b
+.br
+.B /appl/math/parts.b
+.br
+.B /appl/math/perms.b
+.br
+.B /appl/math/pi.b
+.br
+.B /appl/math/powers.b
+.br
+.B /appl/math/primes.b
+.br
+.B /appl/math/sieve.b
--- /dev/null
+++ b/man/1/mc
@@ -1,0 +1,40 @@
+.TH MC 1
+.SH NAME
+mc \- multicolumn print
+.SH SYNOPSIS
+.B mc
+[
+.BI -c " columns"
+]
+[
+.I file ...
+]
+.SH DESCRIPTION
+.I Mc
+formats the contents of
+.I files
+(standard input by default) into columns.
+.I Columns
+is an integer specifying the number of
+character widths into which
+.IR mc 's
+output is formatted.
+If run in an
+.IR acme (1)
+window, the default
+.I columns
+is the number of zeros that will fit across the window;
+otherwise the default
+.I columns
+is 65.
+.SH SOURCE
+.B /appl/cmd/mc.b
+.SH "SEE ALSO"
+.IR acme (1)
+.SH BUGS
+The columns are not aligned properly
+if the input contains tabs.
+.br
+The output doesn't fit the width of a
+.IR wm-sh (1)
+window by default.
--- /dev/null
+++ b/man/1/mdb
@@ -1,0 +1,251 @@
+.TH MDB 1
+.SH NAME
+mdb - binary file editor
+.SH SYNOPSIS
+.B mdb
+[
+.B -w
+]
+.I file
+[
+.I command
+]
+.SH DESCRIPTION
+.I Mdb
+allows inspection of the contents
+of
+.IR file .
+If the
+.B -w
+option is given, then modification of the contents is also
+allowed.
+.I Mdb
+accepts commands of the form
+.IP
+.RI [ address ]
+.RB [ ,
+.IR count ]
+.RI [ command ]
+.PP
+If a
+.I command
+is given as an argument, then
+.I mdb
+will execute that command, otherwise
+it will read and execute commands from the standard input.
+If
+.I address
+is present then the current position, called `dot', is
+set to
+.IR address.
+Initially dot is set to 0. 
+.I Command
+is repeated
+.I count
+times with dot advancing between repetitions. The default
+count is 1.
+.I Address
+and
+.I count
+are expressions.
+.SS Expressions
+Expressions take one of the following forms:
+.TP 10
+\&.
+The value of dot.
+.TP
++
+The value of dot.
+.TP
+^
+The value of dot.
+.TP
+"
+The value of the last address typed.
+.TP
+.I integer
+A number, decimal by default. A 
+.RB ` 0 '
+prefix causes
+it to be interpreted as octal; a
+.RB ` 0x '
+prefix causes it to be interpreted as hexadecimal.
+.TP
+.BI ( expr )
+The value of the expression
+.IR expr .
+.PP
+.I Operators
+.RS
+.TP
+.IB e1 + e2
+Integer addition.
+.TP
+.IB e1 - e2
+Integer subtraction.
+.TP
+.IB e1 * e2
+Integer multiplication.
+.TP
+.IB e1 % e2
+Integer division. (N.B.
+.I not
+modulus).
+.TP
+.IB e1 | e2
+Bitwise disjunction.
+.TP
+.IB e1 & e2
+Bitwise conjunction.
+.RE
+.SS Commands
+Commands have the following syntax:
+.TP
+.BI / f
+Locations starting at
+.I address
+in
+.I file
+are printed according to the format
+.IR f .
+.TP
+.BI ? f
+Same as
+.RB ` / '.
+.TP
+.BI = f
+The value of
+.I address
+itself is printed according to the format
+.IR f .
+.PP
+A
+.I format
+consists of one or more characters that specify
+a style of printing. Each
+.I format
+fetches some data, prints it, and if the
+.I command
+is not
+.RB ` = ',
+advances dot by the amount of data fetched.
+All data is assumed to be held in little-endian
+form (least significant byte first).
+.RS
+.TP
+.PD 0
+.B o
+Print a two-byte integer in octal.
+.TP
+.B O
+Print a four-byte integer in octal.
+.TP
+.B d
+Print a two-byte integer in decimal.
+.TP
+.B D
+Print a four-byte integer in decimal.
+.TP
+.B u
+Print a two-byte integer in unsigned decimal.
+.TP
+.B U
+Print a four-byte integer in unsigned decimal.
+.TP
+.B b
+Print a single byte in hexadecimal.
+.TP
+.B x
+Print a two-byte integer in hexadecimal.
+.TP
+.B X
+Print a four-byte integer in hexadecimal.
+.TP
+.B n
+Prints a newline. No data is fetched.
+.TP
+.B +
+Increments dot by 1. No data is printed.
+.TP
+.B -
+Decrements dot by 1. No data is printed.
+.TP
+.B ^
+Increments dot by the size of the last format encountered.
+.TP
+.B c
+Prints a single byte as a character.
+.TP
+.B C
+Prints a single byte as a printable character, converting
+it to backslash escaped hex if necessary.
+.RE
+.PD
+Other commands include:
+.TP 10
+.RB [ ?/ ] w\ \fIvalue\fP
+Write the two-byte
+.I value
+to the addressed location.
+.TP
+.RB [ ?/ ] W\ \fIvalue\fP
+Write the four-byte
+.I value
+to the addressed location.
+.TP
+.RB [ ?/ ] i
+Disassemble
+.I count
+instructions starting at
+.I address
+.RI ( dot
+by default).
+.TP
+.BI $ modifier
+.I File
+must be a
+.IR dis (6)
+file.
+.I Modifier
+is one of the following subcommands:
+.RS
+.TP
+.PD 0
+.B D
+Print the descriptor section.
+.TP
+.B h
+Print the file header.
+.TP
+.B l
+Print the links section.
+.TP
+.B i
+Print the import section.
+.TP
+.B d
+Print the data section.
+.TP
+.B H
+Print exception handler tables.
+.TP
+.B s
+Print the name of the source file.
+.PD
+.RE
+.SH SOURCE
+.B /appl/cmd/mdb.b
+.SH SEE ALSO
+.IR dis (6)
+.SH BUGS
+Most of the more useful features of
+.IR mdb 's
+antecedent
+.I db
+are unimplemented.
+.PP
+It is not possible to print strings or UTF-8 characters.
+.PP
+As there is no ``native'' word format in Inferno,
+the assumption that all words are little-endian is hard
+to justify.
--- /dev/null
+++ b/man/1/miniterm
@@ -1,0 +1,142 @@
+.TH MINITERM 1
+.SH NAME
+miniterm \- Minitel® emulator
+.SH SYNOPSIS
+.B wm/minitel/miniterm
+[
+.I address
+]
+.SH DESCRIPTION
+.I Miniterm
+connects to the Minitel® service at the given
+.IR address ,
+by default the France Telecom Internet gateway
+to its Minitel® service,
+.BR tcp!pdc.minitelfr.com!513 .
+The
+.I address
+can be any form acceptable to
+.IR dial (2).
+.I Miniterm
+opens a new window that mimics a Minitel display.
+An array of buttons on the right hand side (in 40 character mode)
+or bottom (in 80 character mode) offer the special Minitel functions,
+with French abbreviations.
+Characters typed on the keyboard are sent to the server;
+typing the return key acts as
+.B Suite
+or
+.B Envoi
+as required.
+Clicking on a word with the mouse sends it to the server.
+.PP
+The France Telecom gateway offers a demonstration service
+using ID
+.B ZXNET1
+with password
+.BR DEMFTD .
+Once connected, the services
+.B PAGESM
+(a directory of all Minitel services) and
+.B FT
+(France Telecom's information service)
+are available without charge.
+See
+.B www.minitelfr.com
+for further information.
+.PP
+On certain native Inferno hardware,
+.I miniterm
+can also connect directly via a modem connection, using
+an address of the form
+.BI modem! modeminit ! number,
+which sends the string
+.I modeminit
+to the modem to initialise it,
+then dials the given
+.IR number .
+For example:
+.IP
+.EX
+wm/minitel/miniterm modem!F3!00133836431414
+.EE
+.PP
+Here, the
+.L F3
+is a code that tells the modem to enable V.23, which must be used when
+connecting to the France Telecom servers.
+To use pulse dialing instead of tone dialing the phone number
+can be prefixed with a
+.LR P ,
+as in:
+.IP
+.EX
+wm/minitel/miniterm modem!F3!P3614
+.EE
+.PP
+If the parameter specifies a network connection or a direct connection
+with a phone number the software will attempt to connect immediately.
+If the on-screen button
+.B Cx/Fin
+is used to disconnect and then re-connect,
+.I miniterm
+will use the
+same address if it is a network connection but prompt for a new
+phone number for a direct connection. When prompting
+for a new number the top row of the screen is used to allow the user
+to edit the last used number. Simple editing is available, and the minitel
+keys do the obvious things.
+.PP
+The Minitel function keys are displayed on the right hand side of the screen
+in 40 column mode on a network connection 
+and can be swapped to the left hand side by hitting the
+.L <-
+key.
+In direct dial mode and 80 column network mode the keys are
+displayed at the bottom of the screen.
+In network mode they are redisplayed as appropriate on 40 to 80
+column mode changes.
+.PP
+.I Miniterm
+provides a software keyboard, activated by the
+.B Clavier
+button, which understands some of
+the Minitel keyboard mappings. For example, hitting
+.RB ` A '
+results
+in a capital
+.L A
+on the screen in spite of the Videotex case mapping.
+.SH FILES
+.TF /fonts/minitel
+.TP
+.B /dev/modem
+soft modem (no longer supported)
+.TP
+.B /dev/modemctl
+.TP
+.B /fonts/minitel
+text and semigraphic characters
+.SH SOURCE
+.B /appl/wm/miniterm
+.SH SEE ALSO
+.IR charon (1),
+.IR telnet (1)
+.SH BUGS
+There is no button to control use of error correction in direct dial mode;
+it will be enabled only if the server requests it, but not all do.
+Without it direct dial screens are occasionally corrupted.
+.PP
+The software keyboard is not AZERTY.
+Worse, it does not always come to the top on a mode change.
+.PP
+Some screens look wrong in 80 column
+mode.
+.PP
+On a network connection, choosing
+.B USA
+displays
+.LR iC
+at the bottom left hand
+corner of the screen. The server really is sending this sequence.
+Both the FT emulator and their explorer plug-in suffer from it too.
--- /dev/null
+++ b/man/1/mk
@@ -1,0 +1,579 @@
+.TH MK 1
+.SH NAME
+mk \- maintain (make) related files
+.SH SYNOPSIS
+.B mk
+[
+.B -f
+.I mkfile
+] ...
+[
+.I option ...
+]
+[
+.I target ...
+]
+.SH DESCRIPTION
+.I Mk
+uses the dependency rules specified in
+.I mkfile
+to control the update (usually by compilation) of
+.I targets
+(usually files)
+from the source files upon which they depend.
+The
+.I mkfile
+(default
+.LR mkfile )
+contains a
+.I rule
+for each target that identifies the files and other
+targets upon which it depends and a
+.IR sh (1)
+script, a
+.IR recipe ,
+to update the target.
+The script is run if the target does not exist
+or if it is older than any of the files it depends on.
+.I Mkfile
+may also contain
+.I meta-rules
+that define actions for updating implicit targets.
+If no
+.I target
+is specified, the target of the first rule (not meta-rule) in
+.I mkfile
+is updated.
+.PP
+The environment variable
+.B $NPROC
+determines how many targets may be updated simultaneously;
+ the default value is 1.
+.PP
+Options are:
+.TP \w'\fL-d[egp]\ 'u
+.B -a
+Assume all targets to be out of date.
+Thus, everything is updated.
+.PD 0
+.TP
+.BR -d [ egp ]
+Produce debugging output
+.RB ( p
+is for parsing,
+.B g
+for graph building,
+.B e
+for execution).
+.TP
+.B -e
+Explain why each target is made.
+.TP
+.B -i
+Force any missing intermediate targets to be made.
+.TP
+.B -k
+Do as much work as possible in the face of errors.
+.TP
+.B -n
+Print, but do not execute, the commands
+needed to update the targets.
+.TP
+.B -s
+Make the command line arguments sequentially rather than in parallel.
+.TP
+.B -t
+Touch (update the modified date of) file targets, without
+executing any recipes.
+.TP
+.BI -w target1 , target2,...
+Pretend the modify time for each
+.I target
+is the current time; useful in conjunction with
+.B -n
+to learn what updates would be triggered by
+modifying the
+.IR targets .
+.PD
+.SS The mkfile
+A
+.I mkfile
+consists of
+.I assignments
+(described under `Environment') and
+.IR rules .
+A rule contains
+.I targets
+and a
+.IR tail .
+A target is a literal string
+and is normally a file name.
+The tail contains zero or more 
+.I prerequisites
+and an optional
+.IR recipe ,
+which is a
+.B sh
+script.
+Each line of the recipe must begin with white space.
+A rule takes the form
+.IP
+.EX
+target: prereq1 prereq2
+        sh \f2recipe using\fP prereq1, prereq2 \f2to build\fP target
+.EE
+.PP
+When the recipe is executed,
+the first character on every line is elided.
+.PP
+After the colon on the target line, a rule may specify
+.IR attributes ,
+described below.
+.PP
+A
+.I meta-rule 
+has a target of the form
+.IB A % B
+where
+.I A
+and
+.I B
+are (possibly empty) strings.
+A meta-rule acts as a rule for any potential target whose
+name matches
+.IB A % B
+with
+.B %
+replaced by an arbitrary string, called the
+.IR stem .
+In interpreting a meta-rule,
+the stem is substituted for all occurrences of
+.B %
+in the prerequisite names.
+In the recipe of a meta-rule, the environment variable
+.B $stem
+contains the string matched by the
+.BR % .
+For example, a meta-rule to compile a limbo program anmd install it
+might be:
+.IP
+.EX
+%.dis:    %.b
+        limbo $stem.b
+	cp $stem.dis /dis
+.EE
+.PP
+Meta-rules may contain an ampersand
+.B &
+rather than a percent sign
+.BR % .
+A
+.B %
+matches a maximal length string of any characters;
+an
+.B &
+matches a maximal length string of any characters except period
+or slash.
+.PP
+The text of the
+.I mkfile
+is processed as follows.
+Lines beginning with
+.B <
+followed by a file name are replaced by the contents of the named
+file.
+Blank lines and comments, which run from unquoted
+.B #
+characters to the following newline, are deleted.
+The character sequence backslash-newline is deleted,
+so long lines in
+.I mkfile
+may be folded.
+Non-recipe lines are processed by substituting for
+.BI `{ command }
+the output of the
+.I command
+when run by
+.IR sh .
+References to variables are replaced by the variables' values.
+Special characters may be quoted using single quotes
+.BR \&''
+as in
+.IR sh (1).
+.PP
+Assignments and rules are distinguished by
+the first unquoted occurrence of
+.B :
+(rule)
+or
+.B =
+(assignment).
+.PP
+A later rule may modify or override an existing rule under the
+following conditions:
+.TP
+\-
+If the targets of the rules exactly match and one rule
+contains only a prerequisite clause and no recipe, the
+clause is added to the prerequisites of the other rule.
+If either or both targets are virtual, the recipe is
+always executed.
+.TP
+\-
+If the targets of the rules match exactly and the
+prerequisites do not match and both rules
+contain recipes,
+.I mk
+reports an ``ambiguous recipe'' error.
+.TP
+\-
+If the target and prerequisites of both rules match exactly,
+the second rule overrides the first.
+.SS Environment
+Rules may make use of
+.B sh
+environment variables.
+A legal reference of the form
+.B $OBJ
+or
+.B ${name}
+is expanded as in
+.IR sh (1).
+A reference of the form
+.BI ${name: A % B = C\fL%\fID\fL}\fR,
+where
+.I A, B, C, D
+are (possibly empty) strings,
+has the value formed by expanding
+.B $name
+and substituting
+.I C
+for
+.I A
+and
+.I D
+for
+.I B
+in each word in
+.B $name
+that matches pattern
+.IB A % B\f1.
+.PP
+Variables can be set by
+assignments of the form
+.I
+        var\fL=\fR[\fIattr\fL=\fR]\fIvalue\fR
+.br
+Blanks in the
+.I value
+break it into words, as in
+.I sh
+but without the surrounding parentheses.
+Such variables are exported
+to the environment of
+recipes as they are executed, unless
+.BR U ,
+the only legal attribute
+.IR attr ,
+is present.
+The initial value of a variable is
+taken from (in increasing order of precedence)
+the default values below,
+.I mk's
+environment, the
+.IR mkfiles ,
+and any command line assignment as an argument to
+.IR mk .
+A variable assignment argument overrides the first (but not any subsequent)
+assignment to that variable.
+The variable
+.B MKFLAGS
+contains all the option arguments (arguments starting with
+.L -
+or containing
+.LR = )
+and
+.B MKARGS
+contains all the targets in the call to
+.IR mk .
+.PP
+Dynamic information may be included in the mkfile by using a line of the form
+.I
+        \fR<| \fIcommand\fR \fIargs\fR
+.br
+This runs the command 
+.I command
+with the given arguments
+.I args
+and pipes its standard output to
+.I mk
+to be included as part of the mkfile. For instance, the file
+.B /os/sa1100/mkfile
+uses this technique
+to run a shell command with an awk script and a configuration file as arguments in order for
+the
+.I awk
+script to process the file and output a set of variables and their values.
+.SS Execution
+.PP
+During execution,
+.I mk
+determines which targets must be updated, and in what order,
+to build the
+.I names
+specified on the command line.
+It then runs the associated recipes.
+.PP
+A target is considered up to date if it has no prerequisites or
+if all its prerequisites are up to date and it is newer
+than all its prerequisites.
+Once the recipe for a target has executed, the target is
+considered up to date.
+.PP
+The date stamp
+used to determine if a target is up to date is computed
+differently for different types of targets.
+If a target is
+.I virtual
+(the target of a rule with the
+.B V
+attribute),
+its date stamp is initially zero; when the target is
+updated the date stamp is set to
+the most recent date stamp of its prerequisites.
+Otherwise, if a target does not exist as a file,
+its date stamp is set to the most recent date stamp of its prerequisites,
+or zero if it has no prerequisites.
+Otherwise, the target is the name of a file and
+the target's date stamp is always that file's modification date.
+The date stamp is computed when the target is needed in
+the execution of a rule; it is not a static value.
+.PP
+Nonexistent targets that have prerequisites
+and are themselves prerequisites are treated specially.
+Such a target
+.I t
+is given the date stamp of its most recent prerequisite
+and if this causes all the targets which have
+.I t
+as a prerequisite to be up to date,
+.I t
+is considered up to date.
+Otherwise,
+.I t
+is made in the normal fashion.
+The
+.B -i
+flag overrides this special treatment.
+.PP
+Files may be made in any order that respects
+the preceding restrictions.
+.PP
+A recipe is executed by supplying the recipe as standard input to
+the command
+.B
+        $SHELL -e -I
+.br
+where the
+.I SHELL
+variable is the appropriate shell on the current platform - typically
+.B /dis/sh
+.
+The appropriate value is automatically supplied in the Inferno build environment.
+The
+.B -e
+is omitted if the
+.B E
+attribute is set. 
+The environment is augmented by the following variables:
+.TP 14
+.B $alltarget
+all the targets of this rule.
+.TP
+.B $newprereq
+the prerequisites that caused this rule to execute.
+.TP
+.B $nproc
+the process slot for this recipe.
+It satisfies
+.RB 0≤ $nproc < $NPROC .
+.TP
+.B $pid
+the process id for the
+.I mk
+executing the recipe.
+.TP
+.B $prereq
+all the prerequisites for this rule.
+.TP
+.B $stem
+if this is a meta-rule,
+.B $stem
+is the string that matched
+.B %
+or
+.BR & .
+Otherwise, it is empty.
+For regular expression meta-rules (see below), the variables
+.LR stem0 ", ...,"
+.L stem9
+are set to the corresponding subexpressions.
+.TP
+.B $target
+the targets for this rule that need to be remade.
+.PP
+These variables are available only during the execution of a recipe,
+not while evaluating the
+.IR mkfile .
+.PP
+Unless the rule has the
+.B Q
+attribute,
+the recipe is printed prior to execution
+with recognizable environment variables expanded.
+Commands returning error status
+cause
+.I mk
+to terminate.
+.PP
+Recipes and backquoted
+.B sh
+commands in places such as assignments
+execute in a copy of
+.I mk's
+environment; changes they make to
+environment variables are not visible from
+.IR mk .
+.PP
+Variable substitution in a rule is done when
+the rule is read; variable substitution in the recipe is done
+when the recipe is executed.  For example:
+.IP
+.EX
+bar=a.b
+foo:    $bar
+        limbo -o foo $bar
+bar=b.b
+.EE
+.PP
+will compile
+.B b.b
+into
+.BR foo ,
+if
+.B a.b
+is newer than
+.BR foo .
+.SS Aggregates
+Names of the form
+.IR a ( b )
+refer to member
+.I b
+of the aggregate
+.IR a .
+Currently, there are no aggregates supported under Inferno.
+.SS Attributes
+The colon separating the target from the prerequisites
+may be
+immediately followed by
+.I attributes
+and another colon.
+The attributes are:
+.TP
+.B D
+If the recipe exits with a non-null status, the target is deleted.
+.TP
+.B E
+Continue execution if the recipe draws errors.
+.TP
+.B N
+If there is no recipe, the target has its time updated.
+.TP
+.B n
+The rule is a meta-rule that cannot be a target of a virtual rule.
+Only files match the pattern in the target.
+.TP
+.B P
+The characters after the
+.B P
+until the terminating
+.B :
+are taken as a program name.
+It will be invoked as
+.B "sh -c prog 'arg1' 'arg2'"
+and should return a null exit status
+if and only if arg1 is not out of date with respect to arg2.
+Date stamps are still propagated in the normal way.
+.TP
+.B Q
+The recipe is not printed prior to execution.
+.TP
+.B R
+The rule is a meta-rule using regular expressions.
+In the rule,
+.B %
+has no special meaning.
+The target is interpreted as a regular expression as defined in
+.IR regexp (6).
+The prerequisites may contain references
+to subexpressions in form
+.BI \e n\f1,
+as in the substitute command of
+.IR sed (1).
+.TP
+.B U
+The targets are considered to have been updated
+even if the recipe did not do so.
+.TP
+.B V
+The targets of this rule are marked as virtual.
+They are distinct from files of the same name.
+.PD
+.SH EXAMPLES
+A simple mkfile to compile and install limbo programs:
+.IP
+.EX
+O=dis
+prog:   a.$O b.$O c.$O
+    cp $prereq /dis
+
+%.$O:   %.b
+    limbo $stem.b
+.EE
+.PP
+String expression variables to derive names from a master list:
+.IP
+.EX
+NAMES=alloc arc bquote builtins expand main match mk var word
+OBJ=${NAMES:%=%.$O}
+.EE
+.PP
+Regular expression meta-rules:
+.IP
+.EX
+([^/]*)/(.*)\e.dis:R:  \e1/\e2.b
+	cd $stem1; limbo $stem2.b
+.EE
+.SH SOURCE
+.B /appl/cmd/mk
+.SH SEE ALSO
+.IR sh (1),
+.IR regexp (6)
+.br
+A. Hume,
+``Mk: a Successor to Make''.
+.br
+Bob Flandrena,
+``Plan 9 Mkfiles''.
+.SH BUGS
+Identical recipes for regular expression meta-rules only have one target.
+.br
+The recipes printed by
+.I mk
+before being passed to
+.I sh
+for execution are sometimes erroneously expanded
+for printing.  Don't trust what's printed; rely
+on what
+.I sh
+does.
--- /dev/null
+++ b/man/1/mkdir
@@ -1,0 +1,49 @@
+.TH MKDIR 1
+.SH NAME
+mkdir \- make a directory
+.SH SYNOPSIS
+.B mkdir
+[
+.B -p
+]
+[
+.I dirname ...
+]
+.SH DESCRIPTION
+.I Mkdir
+creates the specified directories. It requires write permission in the parent directory.
+.PP
+The
+.B -p
+option causes
+.I mkdir
+to create the whole path
+.IR dirname ,
+including any missing parent directories; it also will not
+complain if
+.I dirname
+already exists and is a directory.
+.PP
+The new directories are created with permissions starting with
+.B 8r777
+but masked with the permissions of the parent directory according to the
+procedure followed by
+.IR sys-open (2).
+For example, if the parent directory lacks write permission for group
+and has no permissions for others,
+so will the newly created directory.
+.SH SOURCE
+.B /appl/cmd/mkdir.b
+.SH "SEE ALSO"
+.IR chmod (1),
+.IR cd (1),
+.IR rm (1),
+.IR sys-open (2)
+.SH DIAGNOSTICS
+If any directory cannot be created successfully,
+.I mkdir
+prints a warning, and continues with any remaining directories,
+but returns
+.B
+\&"error"
+exit status at the end.
--- /dev/null
+++ b/man/1/mprof
@@ -1,0 +1,169 @@
+.TH MPROF 1
+.SH NAME
+mprof, wm/mprof \- memory profiling limbo programs
+.SH SYNOPSIS
+.B mprof
+[
+.B -bcMflnve123
+] [
+.BI -m " modname"
+] ... 
+[
+.BI "cmd arg ..."
+]
+.PP
+.B wm/mprof
+[
+.B -e12
+] [
+.BI -m " modname"
+] ... 
+[
+.BI "cmd arg ..."
+]
+.PP
+.SH DESCRIPTION
+.I Mprof
+is a simple memory profiling tool which calculates the amount of main, heap and image memory
+used on a 
+particular line of limbo source or in a particular limbo function or module. The main memory
+in question covers the space taken by dis code, jit code, runtime stacks and dynamic C module text, data and relocation areas. The heap memory covers all other limbo data structures. The source in
+question should be compiled with the 
+.B -g
+flag so that the relevant symbol table files exist. In its simplest form, the memory
+profiler shows a line number, the current memory in bytes allocated
+when executing this line, the high water memory in bytes allocated when executing
+this line and the limbo source. This information is also available at the function and
+module level.
+.PP
+The tk version of the profiler
+.I wm/mprof
+shows this information in a text widget and colours the lines of source according
+to the amount of memory allocated by the line. The darker the colour, the
+more memory used.
+.PP
+The
+.B -b
+option starts profiling.
+.PP
+The
+.B -c
+option clears all profiling statistics and state in the memory profiling device. If any commands are specified to
+.B mprof
+, this is done automatically. It's specific
+use is to end the accumulation of statistics when profiling interactively. See the
+example below.
+.PP
+The
+.B -M
+option shows the memory statistics for each module
+The
+.B -f
+option shows the memory statistics for each function.
+.PP
+The
+.B -l
+option shows the memory statistics for each line. If neither this option nor the
+.B -M
+and
+.B -f
+options are given, 
+.B -M
+and
+.B -l
+are assumed.
+.PP
+The
+.B -n
+option lists the name of the file along with the line number.
+.PP
+The
+.B -v
+option outputs all functions and/or lines even when they do not involve memory
+allocating or freeing operations.
+.PP
+The
+.B -m
+option lists the module names which are to be profiled. If none are given, all the
+modules loaded by the kernel will be profiled. The name may be the actual name of
+the module or its path name.
+.PP
+The
+.B -e
+option profiles the module that is loaded first in any following command. In this case
+there is no need to give a
+.B -m
+option as this is added automatically.
+.PP
+The
+.B -1
+option profiles only main memory (pool number 1 in the kernel). The default is
+to profile main, heap and image memory.
+.PP
+The
+.B -2
+option profiles only heap memory (pool number 2 in the kernel). The default is
+to profile main, heap and image memory.
+.PP
+The
+.B -3
+option profiles only image memory (pool number 3 in the kernel). The default is
+to profile main, heap and image memory.
+.PP
+Any remaining arguments are assumed to
+specify a command and set of arguments to the command. If this is the case,
+.B mprof
+will automatically start profiling, run the command to completion and then
+stop profiling before showing the profile statistics.
+.PP
+.B Mprof
+displays the profile statistics (unless the
+.B -b
+option is being used) according to the output format required.
+.PP
+.SH EXAMPLE
+.EX
+To profile a particular command
+	mprof /dis/math/parts 10000
+	wm/mprof /dis/math/parts 10000
+To profile the same command but restrict attention to its own module (Partitions).
+	mprof -m Partitions /dis/math/parts 10000
+	wm/mprof -m Partitions /dis/math/parts 10000
+A shorter version of the above
+	mprof -e /dis/math/parts 10000
+	wm/mprof -e /dis/math/parts 10000
+To profile interactively
+	mprof -b -m Polyhedra
+	wm/polyhedra &
+	mprof -M -f -l -n
+	<interact with wm/polyhedra ...>
+	mprof -M -f -l -n
+	wm/mprof
+	mprof -c
+.EE
+.PP
+Note that the output format options (
+.B -M
+,
+.B -f
+, 
+.B -l
+, 
+.B -n
+, 
+.B -v
+) are ignored when 
+.B -b
+is present.
+.SH SOURCE
+.B /appl/cmd/mprof.b
+.PP
+.B /appl/wm/mprof.b
+.SH BUGS
+Can take quite a time to present statistics when profiling all modules in the
+system.
+.SH SEE ALSO
+.IR cprof (1),
+.IR prof (1),
+.IR prof (2),
+.IR prof (3)
--- /dev/null
+++ b/man/1/mux
@@ -1,0 +1,149 @@
+.TH MUX 1
+.SH NAME
+mux \- interactive television demo
+.SH SYNOPSIS
+.B mux/mux
+.SH DESCRIPTION
+.I Mux
+is a standalone application environment run from the Inferno console
+in
+.IR emu (1)
+or started
+automatically by
+.IR init (8)
+in a native environment.
+It directly uses the
+.IR draw (3)
+device and either keyboard or Infrared,
+and cannot be run under
+.IR wm (1).
+It is included in this release only as an example of the use of the Prefab graphics
+module described by
+.IR prefab-intro (2).
+The simpler style of graphics and interaction provided by Prefab and
+demonstrated by
+.I mux
+might be more appropriate than Tk
+on devices that use infrared remote control
+for interaction, such as televisions,
+or devices with limited screen space, such as pocket devices or portable telephones.
+.SS Configuration
+.I Mux
+produces a menu derived from the configuration file
+.BR /services/basic .
+Each line in the file has three fields, separated by
+.BR : ,
+of the following form:
+.IP
+.IB icon : app : label
+.PP
+The
+.I icon
+is the name of a bitmap file to displayed in the menu alongside the textual
+.I label
+(which is the rest of the line).
+When the item is selected, as described below,
+.I mux
+runs the Dis file
+.IP
+.BI /dis/mux/ app .dis
+.SS Applications
+The following applications are available:
+.TF audioctl
+.TP
+.B fnn
+Financial reports:
+a scrolling `ticker tape' along the bottom of the screen.
+.TP
+.B movie
+Movies: select a film from a menu of categories
+.TP
+.B news
+Today's Newspaper: on-screen newspapers
+.TP
+.B tv
+Television
+.TP
+.B tvlist
+TV Timetable
+.TP
+.B pizza
+Order Pizza
+.TP
+.B email
+Internet mail
+.TP
+.B web
+Internet Web Browser: simplistic web browser
+.TP
+.B register
+Register with a service provider
+.TP
+.B ovid
+Presentations
+.TP
+.B audioctl
+Audio Control
+.PD
+.PP
+.SS Interaction
+.I Mux
+can be controlled using an infrared device, but for demonstration purposes
+under
+.IR emu (1)
+the infrared is emulated using the keyboard (see
+.IR ir (2)).
+The following are the common controls:
+.TF newline
+.TP
+.B r
+channel up
+.TP
+.B c
+channel down
+.TP
+.B t
+volume up
+.TP
+.B v
+volume down
+.TP
+.B i
+cursor up
+.TP
+.B m
+cursor down
+.TP
+.B j
+cursor left; rewind
+.TP
+.B k
+cursor right; fast forward
+.TP
+.B x
+return to main menu leaving application running; recall
+.TP
+.B newline
+select item
+.TP
+.B space
+exit and return to the previous screen or menu
+.SH FILES
+.B /services/basic
+.br
+.B /icons/*.bit
+.SH SOURCE
+.B /appl/mux
+.SH SEE ALSO
+.IR wm (1),
+.IR ir (2),
+.IR prefab-intro (2),
+.IR virgil (2),
+.IR manufacture (8),
+.IR register (8),
+.IR signer (8),
+.IR virgild (8)
+.SH BUGS
+The video demonstrations currently work only on native machines with specific hardware.
+.br
+For copyright reasons, some databases are not distributed, or have randomly-generated content.
--- /dev/null
+++ b/man/1/mv
@@ -1,0 +1,43 @@
+.TH MV 1
+.SH NAME
+mv \- move files
+.SH SYNOPSIS
+.B mv
+.I fromfile
+.I tofile
+.br
+.B mv
+.I fromfile ...
+.I todir
+.SH DESCRIPTION
+.I Mv
+moves
+.I fromfile
+to
+.IR tofile .
+If the files are in the same directory,
+.I fromfile
+is simply renamed;
+a previously existing file named
+.I tofile
+will be (silently) removed.
+Otherwise,
+.I mv
+copies
+.I fromfile
+to
+.IR tofile ,
+then removes
+.IR fromfile .
+This requires write permission for the parent directories involved.
+.PP
+If the last argument is a directory, the earlier arguments (all files) will be moved into that directory. Any previously existing files of the same name will be overwritten.
+Directories can only be renamed:
+.I mv
+refuses to move one into another.
+.SH SOURCE
+.B /appl/cmd/mv.b
+.SH "SEE ALSO"
+.IR cp (1),
+.IR rm (1),
+.IR sys-stat (2)
--- /dev/null
+++ b/man/1/netkey
@@ -1,0 +1,18 @@
+.TH NETKEY 1
+.SH NAME
+netkey \- calculate response to authentication challenge
+.SH SYNOPSIS
+.B netkey
+.SH DESCRIPTION
+.I Netkey
+calculates a response to a challenge made by a system to authenticate a user,
+based on a shared secret (password), using
+the same algorithm as a SecureNet device.
+It reads and writes
+.BR /dev/cons .
+It prompts once for the secret (echo is turned off).
+It then repeatedly prompts for a remote system's challenge,
+and once given it, calculates and prints the corresponding response.
+It exits on an empty challenge or end of file.
+.SH SOURCE
+.B /appl/cmd/netkey.b
--- /dev/null
+++ b/man/1/netstat
@@ -1,0 +1,42 @@
+.TH NETSTAT 1
+.SH NAME
+netstat \- summarize network connections
+.SH SYNOPSIS
+.B netstat
+.SH DESCRIPTION
+.I Netstat
+prints information about network connections.
+The following is presented for each connection:
+.IP
+connection name: the protocol and conversation directory in
+.B /net
+(eg.
+.BR tcp/8 )
+.br
+user name
+.br
+status of the connection
+.br
+address of each end of the connection (eg host and port if IP)
+.PP
+The information is obtained from the
+.B status
+file of each entry under the network directories
+.BR /net/tcp ,
+.BR /net/udp
+and
+.BR /net/il .
+.PP
+.I Netstat
+relies on a populated
+.B /net
+directory; the
+.B #I
+device must therefore
+have previously been bound there.
+.SH FILES
+.B /net/*/status
+.SH SOURCE
+.B /appl/cmd/netstat.b
+.SH "SEE ALSO"
+.IR ip (3)
--- /dev/null
+++ b/man/1/ns
@@ -1,0 +1,42 @@
+.TH NS 1
+.SH NAME
+ns \- display current namespace
+.SH SYNOPSIS
+.B ns
+[
+.B -r
+] [
+.I pid
+]
+.SH DESCRIPTION
+.I Ns
+displays the construction of the namespace of the given
+.IR pid ,
+or its own (as inherited) by default.
+Based on the contents of
+.BI /prog/ pid /ns ,
+it prints on standard output a sequence of bind and mount commands
+(see
+.IR bind (1))
+that might reconstruct the same name space if executed.
+In practice, mounts of services such as
+.IR ftpfs (4)
+often refer to the names of pipe ends that are now inaccessible.
+Furthermore, if any file involved has been renamed since the
+mount or bind, the original name of the file is shown.
+.PP
+Mounts of file services on a network show the network address as
+given to
+.IR dial (2)
+instead of the name of the data file for the connection; the
+.B -r
+option causes
+.I ns
+to show the raw file name instead.
+.SH SOURCE
+.B /appl/cmd/ns.b
+.SH "SEE ALSO"
+.IR bind (1),
+.IR prog (3),
+.IR namespace (4),
+.IR namespace (6)
--- /dev/null
+++ b/man/1/nsbuild
@@ -1,0 +1,49 @@
+.TH NSBUILD 1
+.SH NAME
+nsbuild \- build Inferno namespace 
+.SH SYNOPSIS
+.B nsbuild
+[
+.B file
+]
+.SH DESCRIPTION
+.B Nsbuild
+builds a file name space for Inferno.
+It reads a
+.I file
+(by default, a file called
+.B namespace
+in the current directory)
+and interprets the
+name space commands found in that file.
+.PP
+The commands executed by
+.B nsbuild
+include 
+.B bind
+and
+.BR mount .
+See
+.IR namespace (6)
+for details on the format of the file.
+.SH FILES
+.TP 1.5i
+.B namespace
+The default namespace file.
+.SH SOURCE
+.B /appl/cmd/nsbuild.b
+.SH "SEE ALSO"
+.IR bind (1),
+.IR cd (1),
+.IR newns (2),
+.IR namespace (6)
+.SH BUGS
+The
+.BR new
+and
+.BR fork
+operations of
+.IR namespace (6)
+are ineffective because
+.I nsbuild
+runs as a separate process.
--- /dev/null
+++ b/man/1/os
@@ -1,0 +1,101 @@
+.TH OS 1 hosted
+.SH NAME
+os \- interface to host OS commands (hosted Inferno only)
+.SH SYNOPSIS
+.B bind -a '#C' /
+.br
+.B os
+[
+.B -b
+] [
+.B -m
+.I mountpoint
+] [
+.BI -d " dir"
+] [
+.B -n
+] [
+.BI -N " level"
+]
+.I cmd
+[
+.IR arg ...
+]
+.SH DESCRIPTION
+.I Os
+uses a
+.IR cmd (3)
+device to execute a command,
+.IR cmd ,
+on a host system.
+If the
+.B -m
+option is given,
+.I os
+uses the device at
+.IR mountpoint ,
+otherwise it is asssumed to be at
+.BR /cmd ,
+and is bound into the local namespace if necessary.
+.PP
+The
+.B -d
+option causes the command to run in directory
+.IR dir ;
+an error results and the command will not run if
+.I dir
+does not exist or is inaccessible.
+The standard output and standard error of the command appear on the standard output
+and standard error streams of the
+.I os
+command itself.
+.I Os
+copies the standard input to the remote command's standard input; redirect
+.IR os 's
+input to
+.B /dev/null
+if there is no input to the command.
+.I Os
+terminates when
+.I cmd
+does, and its exit status reflects the status of
+.I cmd
+(if available).
+.PP
+If the
+.I os
+command is killed or exits (eg, for lack of input and output),
+the host's own process control operations are used to (attempt to) kill
+.IR cmd ,
+if it is still running.
+The
+.B -b
+(background) option suppresses that behaviour.
+.PP
+The
+.B -n
+option causes
+.I cmd
+to run with less than normal priority (`nice').
+The
+.B -N
+option sets low priority to a particular
+.I level
+from 1 to 3.
+.SH FILES
+.B /cmd/clone
+.SH SOURCE
+.B /appl/cmd/os.b
+.SH "SEE ALSO"
+.IR cpu (1),
+.IR rcmd (1),
+.IR cmd (3)
+.SH DIAGNOSTICS
+The exit status of
+.I os
+reflects any error that occurs when starting
+.I cmd
+and, if it starts successfully, the status of
+.I os
+is the exit status of
+.IR cmd .
--- /dev/null
+++ b/man/1/p
@@ -1,0 +1,33 @@
+.TH P 1
+.SH NAME
+p \- paginate
+.SH SYNOPSIS
+.B p
+[
+.BI - number
+]
+[
+.I file ...
+]
+.SH DESCRIPTION
+.I P
+copies its standard input, or the named files if given,
+to its standard output,
+stopping at the end of every page,
+to wait for a newline from the user.
+The option sets the
+.I number
+of lines on a page (default: 22).
+.PP
+While waiting for a newline,
+.I p
+interprets the commands:
+.TP
+.B !
+Pass the rest of the line to the shell as a command.
+.TP
+.B q
+Quit.
+.PP
+.SH SOURCE
+.B /appl/cmd/p.b
--- /dev/null
+++ b/man/1/passwd
@@ -1,0 +1,75 @@
+.TH PASSWD 1
+.SH NAME
+passwd \- change user password
+.SH SYNOPSIS
+.B auth/passwd
+[
+.BI -u " user"
+] [
+.BI -s " signer"
+] [
+.I keyfile
+]
+.SH DESCRIPTION
+.I Passwd
+changes the secret shared between the invoker
+and the authentication server
+.I signer
+(default:
+.BR $SIGNER ).
+The
+.I signer
+must offer the
+.IR keysrv (4)
+service.
+.PP
+The secret is associated with a remote user name that need not be
+the same as the name of the invoking user on the local system.
+That remote user name is specified by a certificate signed by
+.IR signer ,
+and obtained from
+.IR keyfile .
+.I Keyfile
+identifies a file containing a certificate (default:
+.LR default ).
+If
+.I keyfile
+is not an absolute pathname,
+the file used will be
+.BI /usr/ user /keyring/ keyfile.
+.I User
+by default is the invoking user's name (read from
+.BR /dev/user ),
+but the
+.B -u
+option can name another.
+.PP
+.I Passwd
+connects to the
+.IR signer ,
+authenticating using the certificate in
+.IR keyfile ,
+and checks that the user in the certificate
+is registered there with an existing secret.
+.I Passwd
+then prompts for the (remote) user's old secret, to double-check identity, then prompts for a new one, which must be confirmed.
+.PP
+Secrets must be at least eight characters long.
+Try to make them hard to guess.
+.SH FILES
+.TF /mnt/keysrv
+.TP
+.B /dev/user
+current user name
+.TP
+.B /mnt/keysrv
+local mount point for connection to
+remote
+.IR keysrv (4)
+.SH SOURCE
+.B /appl/cmd/auth/passwd.b
+.SH SEE ALSO
+.IR keyfs (4),
+.IR keysrv (4),
+.IR changelogin (8),
+.IR logind (8)
--- /dev/null
+++ b/man/1/plumb
@@ -1,0 +1,92 @@
+.TH PLUMB 1
+.SH NAME
+plumb \- send message to plumber
+.SH SYNOPSIS
+.B plumb
+[
+.BI -s " src"
+] [
+.BI -d " dest"
+] [
+.BI -w " wdir"
+] [
+.BI -t " type"
+] [
+.BI -a " name value"
+] [
+.B -i
+]
+.I data
+\&...
+.SH DESCRIPTION
+.I Plumb
+sends a message to the plumber,
+.IR plumber (8),
+which is normally started by
+.IR wm (1)'s
+start up script.
+.PP
+The options and arguments are used as components of the message.
+See
+.IR plumbing (6)
+for their interpretation.
+The options are:
+.TP
+.BI -s " src"
+Set the source to
+.I src
+(default: unspecified).
+.TP
+.BI -d " dest"
+Set the destination to
+.I dest
+(default: unspecified).
+.TP
+.BI -w " wdir"
+Set the working directory to
+.I wdir
+(default: current directory as reported by
+.IR pwd (1)
+or
+.IR workdir (2)).
+.TP
+.BI -t " type"
+Set the type of data to
+.I type
+(default:
+.BR text )
+.TP
+.BI -a " name value"
+Include an attribute
+.RI ` name = value ';
+there can be more than one.
+.TP
+.B -i
+Take the
+.I data
+from the standard input not from the argument strings.
+If an
+.B action
+attribute is not otherwise specified,
+.I plumb
+will add an
+.B action=showdata
+attribute to the message.
+.PP
+The remaining arguments are sent, separated by spaces, as the
+.I data
+of the message.
+The plumber
+will apply its rules to the resulting message to decide how to route it.
+.SH FILES
+.TF /chan/plumb.input
+.TP
+.B /chan/plumb.input
+.IR plumber (8)
+input channel
+.SH SOURCE
+.B /appl/cmd/plumb.b
+.SH SEE ALSO
+.IR plumbmsg (2),
+.IR plumbing (6),
+.IR plumber (8)
--- /dev/null
+++ b/man/1/prof
@@ -1,0 +1,135 @@
+.TH PROF 1
+.SH NAME
+prof, wm/prof \- profiling limbo programs
+.SH SYNOPSIS
+.B prof
+[
+.B -bflnve
+] [
+.BI -m " modname"
+] ... [
+.BI -s " rate"
+] [
+.BI "cmd arg ..."
+]
+.PP
+.B wm/prof
+[
+.B -e
+] [
+.BI -m " modname"
+] ... [
+.BI -s " rate"
+] [
+.BI "cmd arg ..."
+]
+.SH DESCRIPTION
+.I Prof
+is a simple profiling tool which calculates the percentage of time spent on a 
+particular line of limbo source or spent in a particular limbo function. It can 
+determine where a module or set of modules is spending its time. The source in
+question should be compiled with the 
+.B -g
+flag so that the relevant symbol table files exist.
+.PP
+The tk version of the profiler
+.I wm/prof
+shows this information in a text widget and colours the lines of source according
+to the amount of time spent on each line. The darker the colour, the longer
+spent.
+.PP
+The
+.B -b
+option starts profiling.
+.PP
+The
+.B -f
+option shows the function profile.
+.PP
+The
+.B -l
+option shows the line profile. If neither this option nor the
+.B -f
+option are given, 
+.B -l
+is assumed.
+.PP
+The
+.B -n
+option lists the name of the file along with the line number.
+.PP
+The
+.B -v
+option outputs all functions and/or lines even when the percentage
+of time spent in them is zero.
+.PP
+The
+.B -m
+option lists the module names which are to be profiled. If none are given, all the
+modules loaded by the kernel will be profiled. The name may be the actual name of
+the module or its path name.
+.PP
+The
+.B -e
+option profiles the module that is loaded first in any following command. In this case
+there is no need to give a
+.B -m
+option as this is added automatically.
+.PP
+The
+.B -s
+option sets the sample interval
+.I rate
+and is expressed in ms. The default is 100 ms.
+.PP
+Any remaining arguments are assumed to
+specify a command and set of arguments to the command. If this is the case,
+.B prof
+will automatically start profiling, run the command to completion and then
+stop profiling before showing the profile statistics.
+.PP
+.B Prof
+displays the profile statistics (unless the
+.B -b
+option is being used) according to the output format required.
+.PP
+.SH EXAMPLE
+.EX
+To profile a particular command
+	prof /dis/math/parts 10000
+	wm/prof /dis/math/parts 10000
+To profile the same command but restrict attention to its own module (Partitions).
+	prof -m Partitions /dis/math/parts 10000
+	wm/prof -m Partitions /dis/math/parts 10000
+A shorter version of the above
+	prof -e /dis/math/parts 10000
+	wm/prof -e /dis/math/parts 10000
+To profile interactively
+	prof -b -m Partitions -s 10
+	/dis/math/parts 10000
+	prof -f -l -n 
+.EE
+.PP
+Note that the output format options (
+.B -f
+, 
+.B -l
+, 
+.B -n
+, 
+.B -v
+) are ignored when 
+.B -b
+is present.
+.SH SOURCE
+.B /appl/cmd/prof.b
+.PP
+.B /appl/wm/prof.b
+.SH SEE ALSO
+.IR cprof (1),
+.IR mprof (1),
+.IR prof (2),
+.IR prof (3)
+.SH BUGS
+.I Prof
+cannot profile compiled limbo programs.
--- /dev/null
+++ b/man/1/ps
@@ -1,0 +1,45 @@
+.TH PS 1
+.SH NAME
+ps \- process (thread) status
+.SH SYNOPSIS
+.B bind '#p' /prog
+.PP
+.B ps
+.SH DESCRIPTION
+.I Ps
+prints to the standard output information about all current Inferno processes.
+It looks in
+.B /prog
+for process status files;
+normally that requires that the
+.IR prog (3)
+device has previously been bound there (as shown above),
+but it is also possible to import
+.B /prog
+from a remote machine.
+.PP
+Each line of information printed consists of seven columns:
+the process id, the process group id, the owner of the
+process, cpu time used by the process, the run state of the process, the amount of memory used
+by the process, and the name of the module containing the
+currently running function.
+.SH FILES
+.B /prog/*/status
+.SH SOURCE
+.B /appl/cmd/ps.b
+.SH "SEE ALSO"
+.IR deb (1),
+.IR kill (1),
+.IR stack (1),
+.I wm/task
+and
+.I wm/memory
+in
+.IR wm-misc (1),
+.IR prog (3)
+.SH BUGS
+The amount reported as ``memory used'' does not accurately
+reflect the amount of memory referred to by the process,
+because the heap is shared.
+.br
+The cpu time used is currently shown as zero in most hosted implementations.
--- /dev/null
+++ b/man/1/pwd
@@ -1,0 +1,18 @@
+.TH PWD 1
+.SH NAME
+pwd \- print working directory
+.SH SYNOPSIS
+pwd
+.SH DESCRIPTION
+.I Pwd
+prints the path name of the working (current) directory.
+It is guaranteed to return the same path that was used to enter the directory.
+Note that if meanwhile the name space has changed, or directories in the path have been renamed,
+the path name may no longer be valid.
+.SH SOURCE
+.B /appl/cmd/pwd.b
+.SH "SEE ALSO"
+.IR cd (1),
+.IR bind (1),
+.IR sys-fd2path (2),
+.IR workdir (2)
--- /dev/null
+++ b/man/1/rcmd
@@ -1,0 +1,72 @@
+.TH RCMD 1
+.SH NAME
+rcmd \- remote command execution
+.SH SYNOPSIS
+.B rcmd
+[
+.B -e
+.I cryptoalg
+] [
+.B -x
+.I exportpath
+]
+.I host
+[
+.I cmd
+.I arg ...
+]
+.SH DESCRIPTION
+.I Rcmd
+executes
+.I cmd
+on the given
+.IR host .
+If no
+.I cmd
+is given,
+.IR sh (1)
+is assumed.
+The
+.I host
+must have enabled the
+.L rstyx
+service (typically started via
+.IR svc (8)).
+.PP
+For authentication,
+.I rcmd
+will use the certificate in the file
+.IP
+.BI /usr/ username /keyring/ net ! machine
+.PP
+if it exists, and otherwise it will use the certificate in
+.IP
+.BI /usr/ username /keyring/default .
+.PP
+The
+.B -e
+option sets the algorithm
+.I cryptoalg
+to be used following authentication for digesting or encryption.
+See 
+.IR ssl (3)
+for the supported algorithms.
+The default is
+.BR none :
+.IR ssl (3)
+is not used after authentication.
+.PP
+The
+.B -x
+option sets the path to be exported as root from the local machine (defaults to
+.B /
+if not specified).
+.SH SOURCE
+.B /appl/cmd/rcmd.b
+.SH "SEE ALSO"
+.IR security-intro (2),
+.IR security-auth (2),
+.IR security-login (2),
+.IR getauthinfo (8),
+.IR rstyxd (8),
+.IR svc (8)
--- /dev/null
+++ b/man/1/read
@@ -1,0 +1,62 @@
+.TH READ 1
+.SH NAME
+read \- read from standard input with optional seek
+.SH SYNOPSIS
+.B read
+[
+.BR - [ eor ]
+.I offset
+] [
+.I count
+]
+.SH DESCRIPTION
+.I Read
+does a single read of
+.I count
+bytes (default:
+8192 bytes)
+from the standard input and writes
+the result to the standard output.
+If the optional
+.I offset
+argument is given,
+.I read
+will first apply
+.IR sys-seek (2):
+.TP
+.BI -o " offset"
+seek
+.I offset
+bytes from the start of the file
+.TP
+.BI -e " offset"
+seek
+.I offset
+bytes from the end of the file
+.TP
+.BI -r " offset"
+seek
+.I offset
+bytes from the standard input's current file offset
+.PP
+In all cases the file offset changes to
+reflect the result of the seek, and the number of bytes
+read.
+.SH SOURCE
+.B /appl/cmd/read.b
+.SH DIAGNOSTICS
+.I Read
+prints a diagnostic and returns a non-empty exit
+status
+.L fail:error
+on an I/O error;
+it quietly returns status
+.L fail:eof
+if the read returns zero bytes (conventionally, end of file).
+.SH SEE ALSO
+.IR cat (1),
+.I getline
+in
+.IR sh-std (1),
+.IR stream (1),
+.IR sys-read (2)
--- /dev/null
+++ b/man/1/rm
@@ -1,0 +1,28 @@
+.TH RM 1
+.SH NAME
+rm \- remove file(s)
+.SH SYNOPSIS
+.B rm
+[
+.B -fr
+]
+.I file ...
+.SH DESCRIPTION
+.I Rm
+removes the specified files or directories.
+A directory is removed only if it is empty (but
+see the
+.B -r
+option).
+Removal of a file requires write permission in its directory, but requires neither read nor write permission on the file itself.  The options are:
+.TP
+.B -f
+Suppress diagnostics
+.TP
+.B -r
+Recursively remove a directory's substructure before removing the directory.
+.SH SOURCE
+.B /appl/cmd/rm.b
+.SH "SEE ALSO"
+.IR tiny (1),
+.IR sys-remove (2)
--- /dev/null
+++ b/man/1/runas
@@ -1,0 +1,23 @@
+.TH RUNAS 1
+.SH NAME
+runas \- run command as another user
+.SH SYNOPSIS
+.B runas
+.I user
+.I cmd
+[
+.IR arg ...
+]
+.SH DESCRIPTION
+.I Runas
+writes
+.I user
+to /dev/user and invokes
+.I cmd
+with the given arguments.
+The command is only invoked if setting of the user name succeeds.
+.SH SOURCE
+.B /appl/cmd/runas.b
+.SH "SEE ALSO"
+.IR cons (3),
+.
\ No newline at end of file
--- /dev/null
+++ b/man/1/secstore
@@ -1,0 +1,141 @@
+.TH SECSTORE 1
+.SH NAME
+secstore \- retrieve files from secure store
+.SH SYNOPSIS
+.B auth/secstore
+[
+.B -iv
+] [
+.BI -k " key"
+] [
+.BI -p " pin"
+] [
+.BI -s " address"
+] [
+.BI -u " user"
+] [
+.I op
+[
+.I file
+] ... ]
+.SH DESCRIPTION
+.I Secstore
+manages files on the eponymous Plan 9 secure storage service.
+It holds a set of files for each of its users.
+The service is most often used to store a file
+.B factotum
+containing user credentials in a form ready to be loaded into
+.IR factotum (4).
+.I Op
+is one of the following operations:
+.TP
+.B d
+Delete the given files on the server.
+.TP
+.B p
+Print the contents of each file on standard output.
+Each line is written separately, so that files of keys will be received correctly when written to
+.IR factotum (4).
+.TP
+.B r
+Replace the contents of files on the server by the contents of the named files,
+after encrypting them.
+In each case, the file name on the server is the last component of the local file name
+(ie, everything after the final
+.RB ` / ').
+.TP
+.B t
+List a table of contents of
+.IR user 's
+collection on the the server.
+By default, only the names are listed, one per line, but
+given the
+.B -v
+option, each line displays name, file size in bytes, date last stored, and SHA-1 hash of the file's contents.
+.TP
+.B x
+Extract the named files into files of the same name in the current directory.
+By default, they are decrypted (ie, in clear text).
+.PP
+If no
+.I op
+is specified,
+.I secstore
+connects to the server (thus checking the connection and the validity
+of both
+.I key
+and
+.IR user ),
+but does nothing with it.
+.PP
+By default,
+.I secstore
+prompts for a secret key to authenticate the user and the
+.B secstore
+service.
+The service might be configured to demand an extra authentication code, such as a `pin', in which case
+.I secstore
+will then prompt for that as well.
+The options are:
+.TP
+.B -i
+Read one or two lines from the standard input:
+the first line contains the secret; the optional second line contains the extra authentication code.
+.TP
+.BI -k " key"
+Use
+.I key
+as the secret to authenticate with the
+.B secstore
+service.
+.TP
+.BI -p " pin"
+Supply
+.I pin
+as the extra authentication code if the server demands it.
+.TP
+.BI -s " address"
+Connect to the server at the given network
+.IR address ,
+as defined by
+.IR dial (2),
+and translated by
+.IR cs (8).
+The default is
+.BR net!$auth!secstore .
+.TP
+.BI -u " user"
+Authenticate as
+.I user
+(default: the Inferno user name contained in
+.BR /dev/user )
+.TP
+.B -v
+Make the output more verbose:
+display the name announced by the remote server; and use the long form of the table of contents.
+.SH EXAMPLE
+Retrieve the
+.B factotum
+file and feed the keys therein to
+.IR factotum (4):
+.IP
+.EX
+auth/secstore p factotum >/mnt/factotum/ctl
+.EE
+.SH SOURCE
+.B /appl/cmd/auth/secstore.b
+.SH SEE ALSO
+.IR crypt (1),
+.IR secstore (2),
+.IR factotum (4),
+.br
+``Plan 9 Security'',
+.IR "Plan 9 Programmer's Manual" ,
+Fourth Edition,
+Volume 2, 2003.
+.SH BUGS
+Perhaps
+.I secstore
+should allow several
+.B -s
+options as a simple way to replicate the same files on different servers.
--- /dev/null
+++ b/man/1/sendmail
@@ -1,0 +1,48 @@
+.TH SENDMAIL 1
+.SH NAME
+sendmail \- send mail messages
+.SH SYNOPSIS
+.B sendmail
+[
+.I recipient ...
+]
+.SH DESCRIPTION
+.B Sendmail
+sends mail to each
+.I recipient
+named as an argument. It reads its standard input to
+get the text of the message.
+If no
+.I recipient
+is named, the recipient(s) will be taken from the message header.
+.PP
+The mail message is scanned for lines beginning
+.RL ` From: ',
+.RL ` To: '
+and
+.RL ` Cc: '.
+.br
+If no `from'
+line is found, the sender is assumed to be the current user. The recipient(s) of the message
+can be mentioned as arguments or in a list of names in a
+.RL ` To: '
+line but not as both. 
+If the sender's name is unqualified (is just a user name),
+.I sendmail
+appends the value of the environment variable
+.LR DOMAIN .
+.PP
+.B Sendmail
+delivers the mail using the module
+.IR smtp (2),
+which
+connects to a mail server running the Simple Mail Transport Protocol (SMTP),
+using the symbolic host name
+.B $MAILSERVER
+(see
+.IR db (6)).
+.SH SOURCE
+.B /appl/cmd/sendmail.b
+.SH SEE ALSO
+.IR smtp (2),
+.IR db (6)
--- /dev/null
+++ b/man/1/sh
@@ -1,0 +1,980 @@
+.TH SH 1
+.SH NAME
+sh, builtin, exit, load, loaded, local, whatis, quote, run, set, unload, unquote \- command language
+.SH SYNOPSIS
+.B sh
+[
+.B -ilxvn
+]
+[
+.B -c command
+]
+[
+.I file
+[
+.I arg ...
+]
+.SH DESCRIPTION
+.I Sh
+is a programmable user level interface (a shell) for Inferno.
+It executes command lines read from a terminal or a file or, with the
+.B -c
+flag, from
+.IR sh 's
+argument list. It can also be used to give programmable functionality
+to Limbo modules (see
+.IR sh "" "" (2)).
+.SS Command Lines
+A command line is a sequence of commands, separated by ampersands or semicolons
+.RB ( &
+or
+.BR ; ),
+terminated by a newline.
+The commands are executed in sequence
+from left to right.
+.I Sh
+does not wait for a command followed by
+.B &
+to finish executing before starting
+the following command.
+Whenever a command followed by
+.B &
+is executed, its process id is assigned to the
+.I sh
+variable
+.BR $apid .
+Whenever a command
+.I not
+followed by
+.B &
+exits or is terminated, the
+.I sh
+variable
+.B $status
+gets the process's wait message (see
+.IR prog (3));
+it will be the null string if the command was successful.
+.PP
+A number-sign
+.RB ( # )
+and any following characters up to (but not including) the next newline
+are ignored, except in quotation marks.
+.SS Simple Commands
+A simple command is a sequence of arguments interspersed with I/O redirections.
+If the first argument is the name of a
+.I sh
+builtin or it is a braced command block (see
+.IR "Compound Commands",
+below), it is executed by
+.IR sh .
+If the first character of the name is a brace
+.RB ( { ),
+the shell tries to parse it and execute it as a braced command block;
+if the parsing fails, an exception is raised.
+Otherwise
+.I sh
+looks for an external program to execute.
+.PP
+If the name ends in
+.BR .dis ,
+.I sh
+looks for a Dis module of that name; otherwise
+it tries first to find a Dis module of
+that name with
+.B .dis
+appended and failing that, it looks for
+an executable file of the same name, which should
+be a readable, executable script file.
+If the name does not start with a slash
+.RB ( / )
+or dot-slash
+.RB ( ./ ),
+then the name is first looked for relative to
+.BR /dis ,
+and then relative to the current directory.
+A Dis module will be executed only if it
+implements the
+.B Command
+interface (see
+.IR sh (1));
+a script file will be executed only if it
+starts with the characters
+.RB `` #! ''
+followed by the name of a file executable
+under the rules above. In this case the
+command will be executed with any following arguments mentioned
+in the
+.B #!
+header, followed by the path of the script file,
+followed by any arguments originally given to the command.
+.PP
+For example, to execute the simple command
+.BR "ls" ,
+.I sh
+will look for one of the following things, in order,
+stopping the search when one is found:
+.RS
+.IP 1)
+a built-in command named
+.RB `` ls ''.
+.IP 2)
+a Dis module named
+.RB `` /dis/ls.dis '',
+.IP 3)
+an executable script file named
+.RB `` /dis/ls '',
+.IP 4)
+a Dis module named
+.RB `` ./ls.dis '',
+.IP 5)
+an executable script file named
+.RB `` ./ls ''.
+.RE
+.SS Arguments and Variables
+A number of constructions may be used where
+.I sh's
+syntax requires an argument to appear.
+In many cases a construction's
+value will be a list of arguments rather than a single string.
+.PP
+The simplest kind of argument is the unquoted word:
+a sequence of one or more characters none of which is a blank, tab,
+newline, or any of the following:
+.EX
+	# ; & | ^ $ ` ' { } ( ) < > " =
+.EE
+An unquoted word that contains any of the characters
+.B *
+.B ?
+.B [
+is a pattern for matching against file names.
+The character
+.B *
+matches any sequence of characters,
+.B ?
+matches any single character, and
+.BI [ class ]
+matches any character in the
+.IR class .
+If the first character of
+.I class
+is
+.BR ^ ,
+the class is complemented. (As this character
+is special to the shell, it may only be included in a pattern
+if this character is quoted, as long as the leading
+.B [
+is not quoted).
+The
+.I class
+may also contain pairs of characters separated by
+.BR - ,
+standing for all characters lexically between the two.
+The character
+.B /
+must appear explicitly in a pattern.
+A pattern is replaced by a list of arguments, one for each path name matched,
+except that a pattern matching no names is not replaced by the empty list,
+but rather stands for itself.
+Pattern matching is done after all other
+operations.
+Thus,
+.EX
+	x=/tmp; echo $x^/*.b
+.EE
+matches
+.BR /tmp/*.b ,
+rather than matching
+.B "/*.b
+and then prefixing
+.BR /tmp .
+.PP
+A quoted word is a sequence of characters surrounded by single quotes
+.RB ( ' ).
+A single quote is represented in a quoted word by a pair of quotes
+.RB ( '' ).
+.PP
+.ne 3
+Each of the following is an argument.
+.PD 0
+.HP
+.BI ( arguments )
+.br
+The value of a sequence of arguments enclosed in parentheses is
+a list comprising the members of each element of the sequence.
+Argument lists have no recursive structure, although their syntax may
+suggest it.
+The following are entirely equivalent:
+.EX
+	echo hi there everybody
+	((echo) (hi there) everybody)
+	echo (hi
+	there
+	everybody
+	)
+.EE
+Newlines within parentheses count as simple white space;
+they do not terminate the command. This can be useful to give
+some more freedom of layout to commands that take several
+commands as arguments, for instance several of the commands
+defined in
+.IR sh-std (1).
+.HP
+.BI $ argument
+.br
+The
+.I argument
+after the
+.B $
+is the name of a variable whose value is substituted.
+Multiple levels
+of indirection are possible.
+Variable values
+are lists of strings.
+If
+.I argument
+is a number
+.IR n ,
+the value is the
+.IR n th
+element of
+.BR $* ,
+unless
+.B $*
+doesn't have
+.I n
+elements, in which case the value is empty.
+Assignments to variables are described under
+.I "Assignment" ,
+below.
+.HP
+.BI $# argument
+.br
+The value is the number of elements in the named variable.
+A variable
+never assigned a value has zero elements.
+.HP
+\f5$"\fP\fIargument\fP
+.br
+The value is a single string containing the components of the named variable
+separated by spaces.  A variable with zero elements yields the empty string.
+.HP
+.BI `{ command }
+.HP
+.I
+\f5"{\fPcommand\f5}\fP
+.br
+.I Sh
+executes the
+.I command
+and reads its standard output. If backquote
+.RB ( ` )
+is used, it is split into a list of arguments,
+using characters in
+.B $ifs
+as separators.
+If
+.B $ifs
+is not otherwise set, its value is
+.BR "'\ \et\en'" .
+If doublequote (\f5"\fP)
+is used, no tokenization takes place.
+.HP
+.IB argument ^ argument
+.br
+The
+.B ^
+operator concatenates its two operands.
+If the two operands
+have the same number of components, they are concatenated pairwise.
+If not,
+then one operand must have one component, and the other must be non-empty,
+and concatenation is distributive.
+.HP
+.BI ${ command }
+.br
+.I Command
+must be a simple command with no redirections;
+its first word
+must be the name of a builtin substitution operator.
+The operator is invoked and its value substituted.
+See
+.IR "Built-in Commands" ,
+below, for more information on builtins.
+.HP
+.BI <{ command }
+.HP
+.BI >{ command }
+.br
+The
+.I command
+is executed asynchronously with its standard output or standard input
+connected to a pipe.
+The value of the argument is the name of a file
+referring to the other end of the pipe.
+This allows the construction of
+non-linear pipelines.
+For example, the following runs two commands
+.B old
+and
+.B new
+and uses
+.B cmp
+to compare their outputs
+.EX
+	cmp <{old} <{new}
+.EE
+.PD
+.SS Free Carets
+In most circumstances,
+.I sh
+will insert the
+.B ^
+operator automatically between words that are not separated by white space.
+Whenever one of
+.B $
+.B '
+.B `
+follows a quoted or unquoted word or an unquoted word follows a quoted word
+with no intervening blanks or tabs,
+a
+.B ^
+is inserted between the two.
+If an unquoted word immediately follows a
+.BR $ 
+and contains a character other than an alphanumeric, underscore,
+or
+.BR * ,
+a
+.B ^
+is inserted before the first such character.
+Thus
+.IP
+.B limbo -$flags $stem.b
+.LP
+is equivalent to
+.IP
+.B limbo -^$flags $stem^.b
+.SS Assignment
+A command of the form
+.IB name = value
+or
+.IB name := value
+assigns
+.I value
+to the environment variable named
+.IR name .
+.I Value
+is either a list of arguments or an assignment statement. In
+the latter case
+.I value
+is taken from the value assigned in the assignment statement.
+If
+.B :=
+is used, the value is stored in the innermost local scope.
+A local scope is created every time a braced block is entered,
+and destroyed when the block is left. If
+.B =
+is used, the value is stored in the innermost scope
+that contains any definition of
+.IR name .
+.PP
+A list of names can also be used in place of
+.IR name ,
+which causes each element of
+.I value
+in turn to be assigned the respective variable name in
+the list. The last variable in the list is assigned any elements
+that are left over. If there are more variable names than
+elements in
+.IR value ,
+the remaining elements are assigned the null list.
+For instance, after the assignment:
+.EX
+	(a b c) = one two three four five
+.EE
+.B $a
+is
+.BR one ,
+.B $b
+is
+.BR two ,
+and
+.B $c
+contains the remaining three elements
+.BR "(three four five)" .
+.SS I/O Redirections
+The sequence
+.BI > file
+redirects the standard output file (file descriptor 1, normally the
+terminal) to the named
+.IR file ;
+.BI >> file
+appends standard output to the file.
+The standard input file (file descriptor 0, also normally the terminal)
+may be redirected from a file by the sequence
+.BI < file \f1,
+or by the sequence
+.BI <> file \f1,
+which opens the file for writing as well as reading.
+Note that if
+.I file
+is in fact a parsed braced block, the redirection will be treated as
+pipe to the given command - it is identical to the
+.B "<{}"
+operator mentioned above.
+.PP
+Redirections may be applied to a file-descriptor other than standard input
+or output by qualifying the redirection operator
+with a number in square brackets.
+For example, the diagnostic output (file descriptor 2)
+may be redirected by writing
+.BR "limbo junk.b >[2] junk" .
+.PP
+A file descriptor may be redirected to an already open descriptor by writing
+.BI >[ fd0 = fd1 ]
+or
+.BI <[ fd0 = fd1 ]\f1.
+.I Fd1
+is a previously opened file descriptor and
+.I fd0
+becomes a new copy (in the sense of 
+.IR sys-dup (2))
+of it.
+.PP
+Redirections are executed from left to right.
+Therefore,
+.B limbo junk.b >/dev/null >[2=1]
+and
+.B limbo junk.b >[2=1] >/dev/null
+have different effects: the first puts standard output in
+.BR /dev/null
+and then puts diagnostic output in the same place, where the second
+directs diagnostic output to the terminal and sends standard output to
+.BR /dev/null .
+.SS Compound Commands
+A pair of commands separated by a pipe operator
+.RB ( | )
+is a command.
+The standard output of the left command is sent through a pipe
+to the standard input of the right command.
+The pipe operator may be decorated
+to use different file descriptors.
+.BI |[ fd ]
+connects the output end of the pipe to file descriptor
+.I fd
+rather than 1.
+.BI |[ fd0 = fd1 ]
+connects output to
+.I fd1
+of the left command and input to
+.I fd0
+of the right command.
+.PP
+A sequence of commands separated by
+.BR & ,
+.BR ; ,
+or newline
+may be grouped by surrounding
+them with braces
+.RB ( {} ),
+elsewhere referred to as a
+.IR "braced block" .
+A braced block may be used anywhere that a simple word
+is expected. If a simple command is found with
+a braced block as its first word, the
+variable
+.B $*
+is set to any following arguments,
+.B $0
+is set to the block itself, and the commands
+are executed in sequence. If a braced block
+is passed as an argument, no execution takes place:
+the block is converted to a functionally equivalent
+string, suitable for later re-interpretation by the shell.
+The null command 
+.RB ( {} )
+has no effect and always gives a nil status. For instance
+the following commands all produce the same result:
+.EX
+	echo hello world
+	{echo hello world}
+	'{echo hello world}'
+	{echo $*} hello world
+	sh -c {echo hello world}
+	{$*} {echo hello world}
+	{$*} {{$*} {echo hello world}}
+	"{echo {echo hello world}}
+	'{echo hello' ^ ' world}'
+	x := {echo hello world}; $x
+.EE
+It is important to note that the value of
+.B $*
+is lost every time a braced block is entered, so
+for instance, the following command prints an empty string:
+.EX
+	{{echo $*}} hello world
+.EE
+.PD
+.SS Built-in Commands
+The term ``built-in command'', or just ``builtin'', is used somewhat loosely
+in this document to refer to any command that is executed
+directly by the shell; most built-in commands are defined
+by externally loaded modules; there are a few that are not,
+known as ``internal'' builtins, listed below.
+.PP
+Given
+.IR sh 's
+ability to pass compound commands (braced blocks) as
+arguments to other commands, most control-flow
+functionality that is traditionally hard-wired into a shell
+is in
+.I sh
+implemented by loadable modules. See
+.IR sh-std (1),
+.IR sh-expr (1),
+and
+.IR sh-tk (1)
+for more details.
+.PP
+There are two classes of built-in commands;
+the first class, known simply as ``builtins'', are used in
+the same way as normal commands, the only difference
+being that builtins can raise exceptions, while external
+commands cannot, as they are run in a separate process.
+The second class, known as
+``builtin substitutions'' can only be used as the first
+word of the command in the
+.B ${}
+operator. The two classes exist in different name-spaces:
+a builtin  may do something quite different from a
+builtin substitution of the same name.
+.PP
+In general, normal builtins perform some action
+or test some condition;
+the return status of a normal builtin usually
+indicates error status or conditional success. The
+rôle of a substitution builtin is to yield a value,
+(possibly a list)
+which is substituted directly into place as part
+of the argument list of a command.
+.PP
+.PD 0
+.HP
+.BI @ " command ..."
+.br
+Execute
+.I command
+in a subshell, allowing (for instance) the name-space to be
+forked independently of main shell.
+.HP
+.BI run " file ..."
+.br
+Execute commands from
+.IR file .
+.B $*
+is set for the duration to the remainder of the argument list following
+.IR file .
+.HP
+.BI builtin " command ..."
+.br
+Execute
+.I command
+as usual except that any command defined by an external
+module is ignored in favour of the original meaning.
+This command cannot be redefined by an external module.
+.HP
+.B exit
+.br
+Terminate the current process.
+.HP
+.BI load " path..."
+.br
+.B Load
+tries to load each of its arguments as a builtin module
+into
+.IR sh .
+If a module load succeeds, each builtin
+command defined by that module is
+added to the list of builtin commands.
+If there was a previous definition of the command,
+it is replaced, with the exception of internal
+.I sh
+builtins, which are covered up and reappear when
+the module is unloaded. If a module with
+the same
+.I path
+has already been loaded,
+.I sh
+does not try to load it again.
+Unless the path begins with
+.B /
+or
+.BR ./ ,
+the shell looks in the standard builtins directory
+.B /dis/sh
+for the module.
+If a load fails, a
+.B bad module
+exception is raised.
+The environment variable
+.B $autoload
+can be set to a list of Shell modules that
+each instance of
+.I sh
+should load automatically during its initialisation.
+(More precisely, the modules are loaded
+when a new
+.B Sh->Context
+is created: see
+.IR sh (2)
+for details.)
+.HP
+.BI unload " path..."
+.br
+.B Unload
+undoes previous load commands. To succeed,
+.I path
+must be the same as that given to a previous
+invocation of
+.BR load .
+.HP
+.B loaded
+.br
+.B Loaded
+prints all the builtin commands currently
+defined, along with the name of the module that defined them.
+Internally defined commands are tagged with
+module
+.BR builtin .
+.HP
+.BI whatis " name ..."
+.br
+Print the value of each
+.I name
+in a form suitable for input to
+.IR sh .
+The forms are:
+.RS 10
+.TP
+.I varname = "value..."
+.I Varname
+is a non-nil environment variable.
+.TP
+.BI load\  module ;\  name
+.I Name
+has been defined as a builtin by the externally loaded
+.IR module .
+.TP
+.BI load\  module ;\ ${ name }
+.I Name
+has been defined as a builtin substitution by the externally loaded
+.IR module .
+.TP
+.BI builtin\  name
+.I Name
+is defined as a builtin internally by
+.IR sh .
+.TP
+.BI ${ name }
+.I Name
+is defined as a builtin substitution
+internally by the shell.
+.TP
+.I pathname
+The completed pathname of an external file.
+.RE
+.HP
+.B ${builtin
+.I command
+...
+.B }
+.br
+Does for substitution builtin commands
+what
+.B builtin
+does for normal commands.
+.HP
+.B ${loaded}
+.br
+The
+.B loaded
+builtin substitution yields a list of the names of all
+the modules currently loaded, as passed to
+.BR load .
+.HP
+.BI ${quote \ list }
+.br
+.B Quote
+yields a single element
+list which if reparsed by the shell
+will recreate
+.IR list .
+.HP
+.BI ${bquote \ list }
+.br
+Same as
+.B quote
+except that items in
+.I list
+that are known to be
+well-formed command blocks are not quoted.
+.HP
+.BI ${unquote \ arg}
+.br
+.B Unquote
+reverses the operation of
+.BR quote ,
+yielding the original list of values. For example,
+.BI "${unquote ${quote " list }}
+yields
+.IR list .
+A list quoted with
+.B bquote
+can only be unquoted by parsing.
+.PD
+.SS Environment
+The
+.I environment
+is a list of strings made available to externally executing commands by the
+.B env
+module
+(see
+.IR env (2)).
+If the
+.B env
+module does not exist or cannot be loaded, no error will be
+reported, but no variables can be exported to external commands.
+.I Sh
+creates an environment entry for each variable whose value is non-empty.
+This is formatted as if it had been run through
+.BR ${quote} .
+Note that in order for a variable to be exported, its
+name must conform to the restrictions imposed
+by
+.IR env (3);
+names that do not will not be exported.
+.PP
+When
+.I sh
+starts executing it reads variable definitions from its
+environment.
+.PP
+Internally, the shell holds a
+.IR context ,
+which holds a stack of environment variables, the
+current execution flags and the list of built-in modules.
+A copy is made whereever parallel access to the context might
+occur. This happens for processes executing
+in a pipeline,
+processes run asynchronously with
+.BR & ,
+and in any builtin command that runs a shell command
+asynchronously.
+.SS Exceptions
+When
+.I sh
+encounters an error processing its input, an exception is raised,
+and if the
+.B -v
+flag is set, an error message is printed to
+standard error.
+An exception causes processing of the current command to terminate
+and control to be transferred back up the invocation stack.
+In an interactive shell, the central command processing loop
+catches all exceptions and sets
+.B $status
+to the name of the exception.
+Exceptions are not propagated between processes. Any
+command that requires I/O redirection is run in a separate
+process, namely pipes
+.RB ( | ),
+redirections
+.RB ( > ,
+.BR < ,
+.BR >> ,
+and
+.BR <> ),
+backquote substitution
+(\f5`\fP, \f5"\fP)
+and background processes
+.RB ( & ).
+Exceptions can be raised and rescued using
+the
+.B raise
+and
+.B rescue
+functions in the standard builtins module,
+.BR std .
+(See
+.IR sh-std (1)).
+Names of exceptions raised by
+.I sh
+include:
+.TP 10
+.B parse error
+An error has occurred trying to parse a command.
+.TP
+.B usage
+A builtin has been passed an invalid set of arguments;
+.TP
+.B bad redir
+An error was encountered trying to open files prior
+to running a process.
+.TP
+.B bad $ arg
+An invalid name was given to the $ or ${} operator.
+.TP
+.B no pipe
+.I Sh
+failed to make a pipe.
+.TP
+.B bad wait read
+An error occurred while waiting for a process to exit.
+.TP
+.B builtin not found
+A substitution builtin was named but not found.
+.SS Special Variables
+The following variables are set or used by
+.IR sh .
+.PD 0
+.TP \w'\fL$promptXX'u
+.B $*
+Set to
+.IR sh 's
+argument list during initialization.
+Whenever a
+braced block
+is executed, the current value is saved and
+.B $*
+receives the new argument list.
+The saved value is restored on completion of the
+.BR block .
+.TP
+.B $apid
+Whenever a process is started asynchronously with
+.BR & ,
+.B $apid
+is set to its process id.
+.TP
+.B $ifs
+The input field separators used in backquote substitutions.
+If
+.B $ifs
+is not set in
+.IR sh 's
+environment, it is initialized to blank, tab and newline.
+.TP
+.B $prompt
+When
+.I sh
+is run interactively, the first component of
+.B $prompt
+is printed before reading each command.
+The second component is printed whenever a newline is typed and more lines
+are required to complete the command.
+If not set in the environment, it is initialized by
+.BR "prompt=('%\ '\ '')" .
+.TP
+.B $status
+Set to the wait message of the last-executed program,
+the return status of the last-executed builtin
+(unless started with
+.BR &),
+or the name of the last-raised exception, whichever
+is most recent.
+When
+.I sh
+exits at end-of-file of its input,
+.B $status
+is its exit status.
+.PD
+.SS Invocation
+If
+.I sh
+is started with no arguments it reads commands from standard input.
+Otherwise its first non-flag argument is the name of a file from which
+to read commands (but see
+.B -c
+below).
+Subsequent arguments become the initial value of
+.BR $* .
+.I Sh
+accepts the following command-line flags.
+.PD 0
+.TP \w'\fL-c\ \fIstring\fLXX'u
+.BI -c " string"
+Commands are read from
+.IR string .
+.TP
+.B -i
+If
+.B -i
+is present, or
+.I sh
+is given no arguments and its standard input is a terminal,
+it runs interactively.
+Commands are prompted for using
+.BR $prompt .
+This option implies
+.BR -v .
+.TP
+.B -l
+If
+.B -l
+is given or the first character of argument zero is
+.BR - ,
+.I sh
+reads commands from
+.BR /lib/sh/profile ,
+if it exists, and then
+.BR ./lib/profile ,
+if it exists, before reading its normal input.
+.TP
+.B -n
+Normally,
+.I sh
+forks its namespace on startup; if
+.B -n
+is given, this behaviour is suppressed.
+.TP
+.B -v
+Within a non-interactive shell, informational messages
+printed to standard error are usually disabled;
+giving the
+.B -v
+flag enables them.
+.TP
+.B -x
+Print each simple command to stderr before executing it.
+.PD
+.SH SOURCE
+.B /appl/cmd/sh/sh.y
+.SH "SEE ALSO"
+.IR sh (1),
+.IR sh-std (1),
+.IR sh-expr (1),
+.IR sh-file2chan (1),
+.IR sh-tk (1),
+.IR sh-arg (1),
+.IR sh-regex (1),
+.IR sh-string (1),
+.IR sh-csv (1),
+.IR sh (2),
+.IR env (2)
+.SH BUGS
+Due to lack of system support, appending to
+a file with
+.B >>
+will not work correctly when there are
+multiple concurrent writers (but see the
+examples section of
+.IR sh-file2chan (1)
+for one solution to this).
+.PP
+While it
+.I is
+possible to use the shell as a general
+purpose programming language, it is a very slow one!
+Intensive tasks are best done in Limbo, which is a much
+safer language to boot.
--- /dev/null
+++ b/man/1/sh-alphabet
@@ -1,0 +1,417 @@
+.TH SH-ALPHABET 1
+.SH NAME
+alphabet, typeset, declare, import, type, define, autodeclare,
+autoconvert, -, rewrite, modules, types, usage, info, clear \- typed shell interface
+.SH SYNOPSIS
+.B load alphabet
+
+.B type
+.IR qname ...
+.br
+.B declare
+.I name
+[
+.I usage
+]
+.br
+.B undeclare
+.IR name ...
+.br
+.B define
+.I name
+.I expr
+.br
+.B import
+.IR qname ...
+.br
+.B typeset
+.I qname
+.br
+.B autoconvert
+.I srctype dsttype expr
+.br
+.B autodeclare
+.BR "" 0 | 1
+.br
+.B -
+.BI { expression }
+.br
+.B ${rewrite {\fIexpression\fP}
+[
+.I dsttype
+]
+.B }
+.br
+.B ${modules}
+.br
+.B ${types
+.I typeset
+.B }
+.br
+.B ${usage
+.I qname
+.B }
+.br
+.B info
+.br
+.B clear
+.SH DESCRIPTION
+.I Alphabet
+is a loadable
+.IR sh (1)
+module providing an interface to a simple, experimental, typed shell.
+It initially provides a small set of basic types, which can be added
+to by loading new
+.IR typeset s.
+Types are atomic;
+.I alphabet
+provides no support for higher-order
+types.
+The
+.IR "root typeset" ,
+named
+.BR / ,
+consists of the following kinds of value:
+.TP 10
+.B string
+A simple string literal, represented by itself,
+or quoted according to the usual shell quoting rules.
+.TP
+.B cmd
+A shell command or uninterpreted
+.I alphabet
+expression, represented by the syntax \f5"{\fIblock\f5}\fR.
+.TP
+.B fd
+A file open for reading.
+.TP
+.B wfd
+A file open for reading and writing.
+.TP
+.B status
+The status of a completed command.
+.PP
+Each typeset implements a set of
+types, and has an associated set of
+.IR module s.
+Initially, types may only be referred to by their
+.IR "qualified name" s,
+consisting of the name of the type prefixed with
+the name of its typeset; for instance
+.B /string
+for the string type,
+or
+.B /grid/data
+for a type named
+.B data
+in the typeset
+.BR /grid .
+An
+.I "unqualified name"
+is the qualified name without the typeset prefix;
+for instance
+.B string
+or
+.BR data .
+.PP
+To make a type available as its unqualified name,
+use the
+.B type
+command, which imports the type named
+by the qualified name
+.I qname
+from its parent typeset.
+This is a no-op if the type has previously been imported
+from the same typeset; an error is raised if the type
+has previously been imported from a different typeset.
+.PP
+.B Declare
+declares the module
+.IR name
+with type
+.IR usage .
+If
+.I name
+is a qualified name, the module must exist in the
+specified typeset and be compatible with the specified usage.
+If
+.I usage
+is not given, the module itself will be loaded and queried
+to find it out.
+If
+.I name
+is not qualified, the declaration is
+.IR virtual :
+the module cannot actually be used, but is available
+for typechecking and expression rewriting.
+.B Declare
+is a no-op if the module has already been declared
+with a compatible usage, otherwise an error is raised.
+The syntax of
+.I usage
+is similar to the usage messages printed by normal
+shell commands, and defines the argument types expected
+by a module. For example:
+.IP
+.EX
+declare /foo/bar '[-a] [-x string] fd string [string...] -> fd'
+.EE
+.PP
+The above declares the module
+.B bar
+from typeset
+.BR /foo ,
+which takes
+two possible options (one of which requires a single
+associated argument of type
+.BR string ),
+two mandatory arguments,
+of type
+.B fd
+and
+.B string
+respectively,
+and any number of additional arguments of
+type
+.BR string .
+The module returns a value of type
+.BR fd .
+.PP
+When first loaded,
+.I alphabet
+is lax about declaration requirements:
+if a module is referred to by its qualified name,
+and is not currently declared, the module will automatically
+be declared and used as appropriate (as long as the module
+actually exists).
+Use
+.B autodeclare
+to change this behaviour.
+.B "Autodeclare 0"
+turns off all automatic declaration: all modules used in an
+expression must have previously been declared;
+.B "autodeclare 1"
+reverts to the original behaviour.
+.PP
+Once a module is declared, it may be referred to
+by its qualified name.
+.B Import
+makes the module available under its unqualified name.
+It is an error if a module of the same name has
+already been imported from a different typeset.
+For instance:
+.IP
+.EX
+declare /read 'string -> fd'
+import /read
+.EE
+.PP
+This would declare a module named
+.B read
+from the root typeset (checking that it
+accepts a single string argument and returns a file),
+and make it available under the name
+.BR read .
+.PP
+.B Undeclare
+removes the previously declared
+.IR name .
+Note that an imported module has two names:
+its qualified name and its unqualified name.
+.PP
+.B Typeset
+loads the new typeset
+.IR qname .
+Typesets are hierarchical in the same
+way that types and modules are: a typeset adds some
+types to its parent typeset, and has an associated set of
+modules that provide transformations between those types
+and between those of its parent.
+.PP
+.B Autoconvert
+specifies an automatic conversion between
+.I srctype
+and
+.IR dsttype ,
+i.e. whereever a module expects an argument
+of type
+.IR dsttype ,
+and the argument is actually of type
+.IR srctype ,
+the module block (see below for definition)
+.I expr
+(which must be compatible with type
+.IB srctype -> dsttype\fR),
+will be invoked to convert between the two.
+Several conversions will be applied
+atop one another if necessary.
+It is an error if adding the auto-conversion
+creates an ambiguous conversion path
+between two types.
+As a convenience,
+.I expr
+may simply be the name of a module,
+in which case the expression will be rewritten as
+its identity module block: \f5{(\fIsrctype\fP); \fIexpr\fP}\fR.
+.PP
+The
+.B -
+command evaluates the
+.I alphabet
+.IR expression ,
+of the form:
+.IP
+.EX
+{\fIcommand arg\fR...\f5}
+.EE
+.PP
+Usually,
+.I command
+is the name of a previously declared module,
+which must also have been imported if it is not a qualified name.
+The arguments must conform to those expected by
+the module. Each argument is either a literal string or shell-command,
+as described earlier, or a subexpression of the same form as above.
+All subexpressions are evaluated fully before
+.I command
+is invoked.
+The result of the outermost expression must be convertible to the type
+.BR /status ;
+the shell status of the
+.B -
+command will reflect this when the command has completed.
+.PP
+As a convenience,
+.I alphabet
+provides a pipe-like syntax. It will rewrite any expression of the
+form \fIm1 m1args\f5|\fIm2 m2args\fR
+as \fIm2 \f5{\fIm1 m1args\f5}\fIm2args\fR.
+This occurs before any auto-conversions have been applied.
+.PP
+.I Command
+may also be a
+.IR "module block" ,
+of the form:
+.IP
+.EX
+{(\fIargtype\fR...\f5); \fIcommand arg\fR...\f5}
+.EE
+.PP
+The
+.I argtype
+values given in the brackets
+specify the types of the arguments expected by the module block;
+these can be referred to in the arguments to
+.I command
+(and subexpressions thereof) with values of the form
+.BR $1 ,
+.BR $2 ,
+etc.
+For instance,
+.IP
+.EX
+{(/string); echo $1} hello}
+.EE
+.PP
+is exactly equivalent to:
+.IP
+.EX
+{echo hello}
+.EE
+.PP
+In a module block with no arguments, the argument declaration
+section may be omitted.
+.PP
+.B Define
+defines a new module in terms of previously declared
+modules.
+.I Name
+(which must be an unqualified name)
+gives the name of the
+module to define, and
+.I expr
+is a module block giving the expression to evaluate when
+.I name
+is used. The usage of the module is taken from the types declared
+in the module block; its return type is inferred from the return type
+of
+.IR expr .
+All modules used in the evaluation of
+.I expr
+are evaluated when the definition takes place, so evaluation of
+.I name
+is unaffected if any modules it uses are later undeclared.
+.PP
+To show the complete form of an expression, after pipe
+transformations and auto-conversions have been applied, and
+module definitions expanded, use
+.BR ${rewrite} ,
+which returns the expression's canonical form
+without actually executing it.
+If
+.I dsttype
+is given, auto-conversions will be applied to try to
+convert the result of
+.I expression
+to
+.IR dsttype .
+.B Rewrite
+raises an error if it finds any declarations incompatible with
+.IR expression
+or if the final type cannot be converted successfully.
+.PP
+.I Alphabet
+also provides some shell operations that give information
+on its current state:
+.B ${modules}
+yields a list of all the currently declared module names
+(including entries for both qualified and unqualified names);
+.B ${types}
+yields a list of all currently available types
+(giving only the types in
+.I typeset
+if specified);
+and
+.B ${usage}
+provides usage information on module
+.IR qname ,
+which need not be declared.
+Additionally,
+.B info
+searches the module directories on all currently loaded typesets,
+and prints usage information for everything it can find there,
+along with information on all currently installed auto-conversions.
+.PP
+Finally,
+.B clear
+clears all existing declarations and definitions, and starts again
+with a clean slate.
+.SH EXAMPLE
+.EX
+load alphabet
+type /string
+type /fd
+type /status
+import /cat /filter
+autoconvert string fd /read
+autoconvert fd status {(fd); /print $1 1}
+define wc {(fd); /filter $1 "{wc}}
+- {cat /lib/polyhedra /dis/sh.dis | wc}
+echo ${rewrite {cat /lib/polyhedra /dis/sh.dis | wc} status}
+.EE
+.SH SOURCE
+.B /appl/alphabet/*.b
+.br
+.B /appl/alphabet/*/*.b
+.SH BUGS
+.I Alphabet
+expressions involving external typesets and file descriptors cannot have
+their I/O
+redirected at the shell level. Unfortunately it is not possible
+to provide a diagnostic when this occurs.
+.SH SEE ALSO
+.IR sh (1),
+.IR alphabet (2),
+.IR alphabet-main (1),
+.IR alphabet-fs (1),
+.IR alphabet-grid (1)
--- /dev/null
+++ b/man/1/sh-arg
@@ -1,0 +1,129 @@
+.TH SH-ARG 1
+.SH NAME
+arg \- shell command-line argument parsing
+.SH SYNOPSIS
+.B load arg
+.br
+.B arg
+[
+.I opts command
+]...
+.B -
+.I args
+.SH DESCRIPTION
+.I Arg
+is a loadable module for
+.IR sh (1)
+that parses command-line arguments in the same
+form as
+.IR arg (2).
+It accepts a list of
+.RI ( opts ,\  command )
+pairs, where
+each character in
+.I opts
+is an acceptable option, and
+.I command
+is a shell command to be run if any character
+in
+.I opts
+is found.
+Any trailing plus
+.RB ( + )
+characters in
+.I opts
+cause
+.I arg
+to extract the same number of arguments associated
+with the option before running
+.IR command .
+
+For the duration of
+.IR command ,
+the environment variable
+.B $opt
+will be set to the option that has been found,
+and 
+.B $arg
+will be set to the option's arguments (if the correct number
+of arguments have been extracted;
+otherwise a message will be printed, and a
+.B usage
+exception raised).
+The option character asterisk
+.RB ( * )
+matches any option letter (this must
+be quoted, to avoid the usual special interpretation
+by the shell).
+Only one command will be run for any option found;
+if there is no matching option letter, then
+a default error message will be printed, and a
+.B usage
+exception raised.
+.PP
+The list of option specifications is terminated with a single
+minus
+.RB ( - );
+the arguments to be parsed follow this.
+When the argument parsing has finished
+the environment variable
+.B $*
+is set to the remaining list of arguments.
+.SH EXAMPLE
+The following shell script,
+.BR script ,
+takes options
+.BR b ,
+.B c
+and
+.BR f ,
+where
+.B f
+takes a file name argument.
+.EX
+#!/dis/sh
+load arg
+bflag := cflag := 0
+file  := ()
+args  := $*
+(arg 
+    bc  {$opt^flag = 1}
+    f+  {file=$arg}
+    r++++ {rect=$arg}
+    '*' {echo unknown option $opt}
+    - $args
+)
+echo $0 $bflag $cflag $file
+echo rect $rect
+echo arguments are $*
+.EE
+.PP
+When invoked as follows:
+.IP
+.B "script -bc -r 0 10 50 100 -ffile a b c"
+.PP
+the output is:
+.IP
+.EX
+\&./script 1 1 file
+rect 0 10 50 100
+arguments are a b c
+.EE
+.PP
+and when invoked by:
+.IP
+.B "script -b -f file -z -- -bc"
+.PP
+the output is:
+.IP
+.EX
+unknown option z
+\&./script 1 0 file
+arguments are -bc
+.EE
+.SH SOURCE
+.B /appl/cmd/sh/arg.b
+.SH SEE ALSO
+.IR sh (1),
+.IR arg (2),
+.IR sh-std (1)
--- /dev/null
+++ b/man/1/sh-csv
@@ -1,0 +1,66 @@
+.TH SH-CSV 1
+.SH NAME
+csv, getcsv \- parse ``comma-separated values''
+.SH SYNOPSIS
+.B load csv
+
+.B getcsv
+.I command
+.br
+.B ${csv
+.IB list }
+.br
+
+.SH DESCRIPTION
+.B Csv
+is a loadable module for
+.IR sh (1)
+that provides the facility to parse and generate
+``comma-separated value'' lists, a widely used
+data exchange format.
+Data in this format is usually in the form of a table,
+each row of which contains one or more items,
+each separated by a comma
+.RB ( , ).
+Items that contain
+a comma or a newline are surrounded with double-quotes
+(\f5"\fP).
+A double-quote within an item is represented by a pair
+of double-quotes.
+Two primitives are provided:
+.TP 10
+.B getcsv
+.B Getcsv
+works similiarly to
+.B getlines
+in
+.IR sh-std (1).
+It reads from the standard input, and for every line read,
+invokes
+.I command
+with
+.B $line
+set to the items found on that line (one element per item).
+.B Getcsv
+recognises the usual loop
+.B break
+and
+.B continue
+exceptions.
+.TP
+.B ${csv}
+.B Csv
+yields a single element containing all the items in
+.IR list ,
+comma-separated and quoted as necessary.
+.SH SOURCE
+.B /appl/cmd/sh/csv.b
+.SH SEE ALSO
+.IR sh (1),
+.IR sh-std (1)
+.SH BUGS
+Empty lines are ambiguous:
+.B csv
+treats an empty line as containing a single,
+empty element; there is thus no way of
+representing a line containing no elements at all.
--- /dev/null
+++ b/man/1/sh-expr
@@ -1,0 +1,188 @@
+.TH SH-EXPR 1
+.SH NAME
+expr, ntest, mpexpr \- shell module for simple arithmetic.
+.SH SYNOPSIS
+.B load expr
+OR
+.B load mpexpr
+
+.B ${expr
+[
+-r
+.I radix
+]
+[
+.I arg...
+]
+.B }
+.br
+.B ntest
+.I num
+.br
+.SH DESCRIPTION
+.I Expr
+and
+.I mpexpr
+are loadable modules for
+.IR sh (1)
+that provide support for integer arithmetic.
+.I Expr
+uses 64-bit signed integers;
+.I mpexpr
+uses arbitrary-precision signed integers.
+They each provide the same interface:
+a command
+.IR ntest ,
+which performs a simple boolean test
+on its integer argument, and the
+substitution operator
+.IR expr ,
+which takes an expression in Reverse Polish
+notation, and yields its result.
+.PP
+.I Ntest
+returns true if its argument
+.I num
+is non-zero,
+and false otherwise.
+.PP
+.I Expr
+evaluates each
+.I arg
+in turn; if it is an integer it gets pushed onto
+the stack; otherwise it should name
+one of the operators below, whereupon
+the appropriate number of operands are
+popped off the stack, evaluated as arguments
+to the operator, and the result pushed back onto
+the stack. Arguments are passed to the operator
+first-pushed first, so, for instance,
+.B ${expr 2 1 -}
+yields 1, not -1.
+Alternative names are given for some operators;
+this is to avoid the necessity of quoting operators
+that contain
+.IR sh (1)
+metacharacters. Integers are given in the same form acceptable
+to Limbo. The relational operators yield either
+1 (true) or 0 (false). If the
+.B -r
+option is given,
+.I radix
+specifies an output base for printed numbers.
+It may be from 2 to 36;
+.I mpexpr
+also allows 64 to specify base64 notation.
+Numbers are printed in a form suitable for re-interpretation
+by
+.IR expr .
+.PP
+When all its arguments have been evaluated,
+.B expr
+yields all the values remaining on its stack, first pushed
+first. Note that bitwise operators treat their operands as if they
+were stored in two's complement form. The operators supported by expr are as follows (the number
+of operands required in is given parentheses).
+.TP 15
+.BR + \ (2)
+Addition
+.TP
+.BR - \ (2)
+Subtraction
+.TP
+.BR x ,\  * \ (2)
+Multiplication
+.TP
+.BR / \ (2)
+Division. Division by zero raises a
+.B divide by zero
+exception.
+.TP
+.BR % \ (2)
+Modulus. A zero modulus will cause a
+.B divide by zero
+exception to be raised.
+.TP
+.BR and \ (2)
+Bitwise-and.
+.TP
+.BR or \ (2)
+Bitwise-or.
+.TP
+.BR xor \ (2)
+Bitwise-xor.
+.TP
+.BR ~ \ (1)
+Bitwise-complement..
+.TP
+.BR _ \ (1)
+Unary minus.
+.TP
+.BR << ,\  shl \ (2)
+Shift left.
+.TP
+.BR >> ,\  shr \ (2)
+Shift right.
+.TP
+.BR = ", " == ", " eq " (2)"
+Equality.
+.TP
+.BR != ", " neq " (2)"
+Inequality.
+.TP
+.BR > ", " gt " (2)"
+Greater than.
+.TP
+.BR < ", " lt " (2)"
+Less than.
+.TP
+.BR <= ", " le " (2)"
+Less than or equal to.
+.TP
+.BR >= ", " ge " (2)"
+Greater than or equal to.
+.TP
+.BR ! ", " not " (1)"
+Logical negation.
+.TP 
+.BI rep \ \f1(\fPn\f1)\fP
+.B Rep
+repeats the last operation (which must
+have been a two-operand operation other
+than
+.BR seq )
+until the values in the stack are exhausted.
+.TP
+.BR seq \ (2)
+.B Seq
+pushes on the stack a sequence of numbers ranging
+numerically from its first argument up to and including
+its second argument. If its second argument is
+less than its first, the sequence will descend.
+.TP
+.BR rand \ (1)
+(\fImpexpr\fP only). Push a secure random number;
+the argument value gives the size of the number, in bits.
+.TP
+.BR bits \ (1)
+(\fImpexpr\fP only). Push the size, in bits, of the argument.
+.TP
+.BR expmod ", " invert " (2)"
+(\fImpexpr\fP only). See
+.IR keyring-ipint (2).
+.TP
+.BR exp ", " xx ", " **
+(\fImpexpr\fP only). Exponentiation.
+.SH SOURCE
+.B /appl/cmd/sh/expr.b
+.SH SEE ALSO
+.IR sh (1),
+.IR sh-std (1),
+.IR sh-tk (1),
+.IR keyring-ipint (2)
+.SH BUGS
+Postfix notation can be confusing.
+Any operators that contain shell metacharacters (e.g. ``*'', ``>'')
+must be quoted to avoid interpretation by the shell.
+Base64 notation can contain # characters, which need
+quoting to avoid interpretation by the shell.
--- /dev/null
+++ b/man/1/sh-file2chan
@@ -1,0 +1,263 @@
+.TH SH-FILE2CHAN 1
+.SH NAME
+file2chan, rblock, rdata, rerror, rget, rread, rreadone, rwrite \- shell interface to file2chan
+.SH SYNOPSIS
+.B load file2chan
+
+.B file2chan
+.I filename
+.I readcmd writecmd
+[
+.I closecmd
+]
+.br
+.B rblock
+[
+.I tag
+]
+.br
+.B fetchwdata
+[
+.I tag
+]
+.br
+.B putrdata
+[
+.I tag
+]
+.br
+.B rerror
+[
+.I tag
+]
+.I errmsg
+.br
+.B rread
+[
+.I tag
+]
+.I readdata
+.br
+.B rreadone
+[
+.I tag
+]
+.I readdata
+.br
+.B rwrite
+[
+.I tag
+[
+.I count
+] ]
+.br
+.B ${rget
+.RB ( data\fP|\fPcount\fP|\fPoffset\fP|\fPfid )
+[
+.I tag
+]
+.B }
+.br
+.SH DESCRIPTION
+.I File2chan
+is a loadable module for
+.IR sh (1)
+that provides facilities to create a file in the namespace
+with properties determined by a shell script.
+.B File2chan
+creates
+.I filename
+in the namespace and spawns a new thread to serve the file.
+If the creation succeeds and the thread is spawned successfully,
+then the environment variable
+.B $apid
+is set to the process id of the new thread; otherwise an error (non-nil)
+status is returned.
+.IR Readcmd ,
+.IR writecmd
+and
+.I closecmd
+should be
+executable
+.IR sh (1)
+command blocks.
+Subsequently, whenever a process reads from
+.IR filename ,
+.I readcmd
+will be invoked; whenever a process writes
+to
+.IR filename ,
+.I writecmd
+will be invoked; whenever an open file on
+.I filename
+is closed, then
+.I closecmd
+will be invoked, if present.
+.PP
+When a read or write request arrives,
+it is added to a list of currently outstanding
+.I tags
+maintained by
+.IR file2chan .
+If the request is not replied to or acknowledged by the time
+the invoked command has finished, then a reply
+will be made automatically (the default is to accept
+all writes and to give an error on all reads).
+Each tag is assigned a unique
+.I tag id
+which is stored in the environment variable
+.B $tag
+for the duration of the invoked command.
+Most commands take an optional
+.I tag
+argument which should be the
+.I tag id
+of a currently outstanding request; if omitted,
+the value of
+.B $tag
+will be used.
+The following commands are provided to reply to requests
+and obtain information about requests:
+.TP 10
+.B rblock
+.B Rblock
+marks
+.I tag
+as a blocking request - no automatic reply will be made when
+the currently invoked command has terminated; the
+process making the request will block until a reply is made.
+.TP
+.B fetchwdata
+.B Fetchwdata
+writes the data associated with
+.I tag
+(which must be a write request) to its standard output.
+It is useful if an uncorrupted version of binary data is
+wanted, as it avoids the data being interpreted as a utf-8
+string.
+.TP
+.B putrdata
+.B Putrdata
+is the converse of
+.BR fetchwdata :
+it reads data from its standard input and replies to
+.I tag
+(which must be a read request) with the data read.
+Any data in excess of that requested will be lost.
+.TP
+.B rerror
+.B Rerror
+replies to
+.I tag
+with an error code; the remote read
+or write request will return an
+error, with the description
+.IR errmsg .
+.TP
+.B rread
+.B Rread
+replies to the read request
+.I tag
+with the data in
+.IR readdata .
+If
+.I readdata
+is longer than the number of bytes requested,
+then only the requested number of bytes of
+.I readdata
+will
+be sent. The offset of the read request is ignored.
+.TP
+.B rreadone
+.B Rreadone
+is similar to
+.B rread
+except that it honours the offset of the client's
+read request, so the client can use consecutive
+reads to retrieve all of
+.IR readdata .
+.TP
+.B rwrite
+.B Rwrite
+replies to the write request
+.IR tag .
+If
+.I count
+is given, then the client's write request will return
+that number (it is usually considered an error if the return
+from
+.I write
+(see
+.IR sys-read (2))
+is not the same as the number of bytes written).
+If
+.I count
+is omitted, all the bytes are assumed to have been written.
+.TP
+.B ${rget}
+.B Rget
+retrieves information associated with
+.IR tag .
+The information it yields depends on its first argument,
+which must be one of:
+.RS
+.TP
+.B data
+The data associated with write request
+.IR tag .
+.TP
+.B count
+The number  of bytes requested by read request
+.IR tag .
+.TP
+.B fid
+The client's file identifier associated with
+.IR tag .
+A unique
+.I fid
+is associated with all client requests emanating from
+the same open file. This is the only
+.B rget
+request valid with the
+.I tag
+associated with a close operation.
+.TP
+.B offset
+The file offset associated with the request
+.IR tag .
+.RE
+.SH EXAMPLES
+The following code creates a very simple
+memory-based file called
+.BR /tmp/memfile .
+.EX
+	file2chan /tmp/memfile {rreadone $data} {data = ${rget data}}
+.EE
+It is, however, very limited, as binary data stored in the file
+will be corrupted, and the size of the file is limited to the amount
+of data that can be transmitted in a single write
+(see
+.IR sys-read (2)).
+.PP
+The following code implements a single-threaded logfile
+which can support multiple concurrent writers:
+.EX
+	{file2chan /chan/log {} {fetchwdata}} >> /tmp/logfile
+.EE
+.PP
+The following code makes the command
+.B cmd
+available to external programs, and defines a shell function
+to use it. Note that there's an approximate 8K limit on the size of the argument
+list that can be passed in this way.
+.EX
+	load std
+	file2chan /chan/cmdchan {} {cmd ${unquote ${rget data}}}
+	fn runcmd {echo -n ${quote $*} > /chan/cmdchan}
+.EE
+.SH SOURCE
+.B /appl/cmd/sh/file2chan.b
+.SH SEE ALSO
+.IR sys-file2chan (2),
+.IR sh (1),
+.IR intro (5),
--- /dev/null
+++ b/man/1/sh-mload
@@ -1,0 +1,68 @@
+.TH SH-MLOAD 1
+.SH NAME
+mload, munload \- namespace separation for shell modules
+.SH SYNOPSIS
+.B load mload
+
+.B mload
+.I name
+[
+.IR path ...
+]
+.br
+.B munload
+.I name
+[
+.IR path ...
+]
+.br
+.SH DESCRIPTION
+.I Mload
+is a loadable module for
+.IR sh (1)
+that allows the simultaneous use of shell modules with
+potentially clashing command name spaces.
+.B Mload
+creates a new namespace
+.I name
+and loads each
+.I path
+as a builtin module in the same way as
+.B load
+(see
+.IR sh (1)).
+Any commands or substitution builtins defined
+by the modules are accessible by giving
+the command and its arguments as arguments to
+the
+.I name
+command.
+.PP
+.B Munload
+unloads a module from the namespace
+.IR name .
+If no modules remain in the namespace,
+.I name
+is undefined as a command.
+.SH EXAMPLE
+Load
+.I mpexpr
+in a different namespace from
+.I expr
+(see
+.IR sh-expr (1)):
+.EX
+load expr
+mload mp mpexpr
+echo ${expr 1 2 +}
+echo ${mp expr 2 300 xx}
+.EE
+.SH SOURCE
+.B /appl/cmd/sh/mload.b
+.SH SEE ALSO
+.IR sh (1),
+.SH BUGS
+Because of the way shell modules are implemented,
+the namespaces are global across all processes that
+share an instance of
+.IR mload .
--- /dev/null
+++ b/man/1/sh-regex
@@ -1,0 +1,178 @@
+.TH SH-REGEX 1
+.SH NAME
+re, match \- shell script regular expression handling
+.SH SYNOPSIS
+.B load regex
+
+.B match
+.I regex
+[
+.IR arg ...
+]
+.br
+.B ${re
+.I op
+.IR arg...
+.B }
+.br
+.SH DESCRIPTION
+.I Regex
+is a loadable module for
+.IR sh (1)
+that provides access to regular-expression
+pattern matching and substitution.
+For details of regular expression syntax in Inferno,
+see
+.IR regexp (6).
+.I Regex
+defines one builtin command,
+.BR match ,
+and one builtin substitution operator,
+.BR re .
+.B Match
+gives a false exit status if its argument
+.I regex
+fails to match any
+.IR arg .
+.B Re
+provides several operations, detailed below:
+.TP 10
+\f5${re g\fP \fIregexp\fP \fR[\fP \fIarg\fP\fR...\fP\fR]\fP\f5}\fP
+Yields a list of each
+.I arg
+that matches
+.IR regexp .
+.TP
+\f5${re v\fP \fIregexp\fP \fR[\fP \fIarg\fP\fR...\fP\fR]\fP\f5}\fP
+Yields a list of each
+.I arg
+that does not match
+.IR regexp .
+.TP
+\f5${re m\fP \fIregexp\fP \fIarg\fP\f5}\fP
+Yields the portion of
+.I arg
+that matches
+.IR regexp ,
+or an empty list if there was no match.
+.TP
+\f5${re M\fP \fIregexp\fP \fIarg\fP\f5}\fP
+Yields a list consisting of the portion
+of 
+.I arg
+that matches
+.IR regexp ,
+followed by list elements giving the portion
+of
+.I arg
+that matched each parenthesized subexpression
+in turn.
+.TP
+\f5${re mg\fP \fIregexp\fP \fIarg\fP\f5}\fP
+Similar to
+.B re m
+except that it applies the match consecutively
+through
+.IR arg ,
+yielding a list of all the portions of
+.I arg
+that match
+.IR regexp .
+If a match is made to the null string,
+no subsequent substitutions will take place.
+.TP
+\f5${re s\fP \fIregexp\fP \fIsubs\fP [ \fIarg\fP... ]\f5}\fP
+For each
+.IR arg ,
+.B re s
+substitutes the first occurrence of
+.I regexp
+(if any) by
+.IR subs .
+If
+.I subs
+contains a sequence of the form
+.BI \e d
+where
+.I d
+is a single decimal digit,
+the
+.IR d th
+parenthesised subexpression in
+.I regexp
+will be substituted in its place.
+.B \e0
+is substituted by the entire match.
+If any other character follows a
+backslash
+.RB ( \e ),
+that character will be substituted.
+Arguments which contain no match to
+.I regexp
+will be left unchanged.
+.TP
+\f5${re sg\fP \fIregexp\fP \fIsubs\fP [ \fIarg\fP... ]\f5}\fP
+Similar to
+.B re s
+except that all matches of
+.I regexp
+within each
+.I arg
+will be substituted for, rather than just the
+first match. Only one occurrence of the null string is
+substituted.
+.PP
+.SH EXAMPLES
+List all files in the current directory that
+end in
+.B .dis
+or
+.BR .sbl :
+.EX
+	ls -l ${re g '\e.(sbl|dis)$' *}
+.EE
+.PP
+Break
+.I string
+up into its constituent characters,
+putting the result in shell variable
+.BR x :
+.EX
+	x = ${re mg '.|\en' \fIstring\fP}
+.EE
+.PP
+Quote a string
+.B s
+so that it can be used as
+a literal regular expression without worrying
+about metacharacters:
+.EX
+	s = ${re sg '[*|[\e\e+.^$()?]' '\e\e\e0' $s}
+.EE
+.PP
+Define a substitution function
+.B pat2regexp
+to convert shell-style
+patterns into equivalent regular expressions
+(e.g.
+.RB `` ?.sbl* ''
+would become
+.RB `` ^.\e.sbl.*$ ''):
+.EX
+	load std
+	subfn pat2regexp {
+		result = '^' ^ ${re sg '\e*' '.*'
+			${re sg '\?' '.'
+				${re sg '[()+\e\e.^$|]' '\e\e\e0' $*}
+			}
+		} ^ '$'
+	}
+.EE
+.SH SOURCE
+.B /appl/cmd/sh/regex.b
+.SH SEE ALSO
+.IR regexp (6),
+.IR regex (2),
+.IR sh (1),
+.IR string (2),
+.IR sh-std (1)
--- /dev/null
+++ b/man/1/sh-sexprs
@@ -1,0 +1,131 @@
+.TH SH-SEXPRS 1
+.SH NAME
+sexprs, islist, els, text, textels, mktext, mklist, mktextlist \- parse and generate S-expressions
+.SH SYNOPSIS
+.B load sexprs
+
+.B getsexprs
+.I command
+.br
+.B islist
+.I sexpr
+.br
+.B ${els
+.IB sexpr }
+.br
+.B ${text
+.IB sexpr }
+.br
+.B ${textels
+.IB sexpr }
+.br
+.B ${mktext
+.IB val }
+.br
+.B ${mklist
+[
+.IR val ...
+.BR "" ] }
+.br
+.B ${mktextlist
+[
+.IR val ...
+.BR "" ] }
+.br
+.SH DESCRIPTION
+.B Sexprs
+is a loadable module for
+.IR sh (1)
+that provides the facility to parse and generate
+S-expressions (see
+.IR sexprs (2)).
+The following primitives are provided:
+.TP 10
+.B getsexprs
+.B Getsexprs
+works similiarly to
+.B getlines
+in
+.IR sh-std (1).
+It reads S-expressions from the standard input, and for expression read, it
+invokes
+.I command
+with
+.B $sexp
+set to the text representation of that expression.
+.B Getsexprs
+recognises the usual loop
+.B break
+and
+.B continue
+exceptions.
+.TP
+.BI islist\  sexp
+.B Islist
+yields a nil (true) status if
+.IR sexp ,
+which must be a well-formed S-expression,
+is a list element.
+.TP
+.BI ${els\  sexp }
+If
+.I sexp
+is an S-expression containing a list,
+then
+.B els
+returns a list of the S-expressions it contains.
+It is an error if
+.I sexp
+is not a valid S-expression.
+.TP
+.BI ${text\  sexp }
+If
+.I sexp
+is an S-expression containing a simple element,
+then
+.B text
+returns the value of that element. If
+.I sexp
+is a list, the return value will be an empty string.
+Note that elements
+containing binary data will likely be corrupted
+by conversion to utf-8.
+It is an error if
+.I sexp
+is not a valid S-expression.
+.TP
+.BR ${textels\  sexp }
+If
+.I sexp
+is an S-expression containing a list, then
+.B textels
+returns a list of the text values in that S-expression,
+converted as with
+.BR ${text} .
+It is an error if
+.I sexp
+is not a valid S-expression.
+.TP
+.BR ${mktext\  val }
+.B Mktext
+returns a text representation of the S-expression
+containing the simple value
+.IR val .
+.TP
+.BR ${mklist\  \fR[\fPsexp\fR...]\fP }
+.B Mklist
+returns a text representation of the S-expression list
+containing its arguments, which must be well-format
+S-expressions.
+.TP
+.BR ${mktextlist\  \fR[\fPval\fR...]\fP }
+.B Mktextlist
+returns a text representation of the S-expression list
+containing one simple element for each
+.IR val .
+.SH SOURCE
+.B /appl/cmd/sh/sexprs.b
+.SH SEE ALSO
+.IR sh (1),
+.IR sh-std (1),
+.IR sexprs (2)
--- /dev/null
+++ b/man/1/sh-std
@@ -1,0 +1,532 @@
+.TH SH-STD 1
+.SH NAME
+std, if, while, ~, no, !, apply, getlines, status, pctl, fn, and, or, raise, rescue, hd, tl, index, split, join, pid, parse, pipe, env \- standard shell builtins module.
+.SH SYNOPSIS
+.B load std
+
+.B !
+.I command
+.br
+.B ~
+.I value
+[
+.IR pattern ...
+]
+.br
+.B no
+[
+.IR arg ...
+]
+.br
+.B and
+.IR command ...
+.br
+.B apply
+.I command
+[
+.IR arg ...
+]
+.br
+.B getlines
+[
+.I separators
+]
+.I command
+.br
+.B flag
+.I f
+[
+.B +-
+]
+.br
+.B for
+.I var
+.B in
+[
+.IR arg ...
+]
+.I command
+.br
+.B fn
+.I name command
+.br
+.B if
+.I condition action
+[
+.I condition action
+]... [
+.I elseaction
+]
+.br
+.B or
+.IR command ...
+.br
+.B pctl
+.IR flag...
+.br
+.B raise
+.I name
+.br
+.B rescue
+.I pattern rescueblock command
+.br
+.B status
+.I value
+.br
+.B subfn
+.I name command
+.br
+.B while
+.I condition command
+.br
+.B ${hd
+.IB list }
+.br
+.B ${index
+.I number
+.IB list }
+.br
+.B ${pid}
+.br
+.B ${split
+[
+.I separators
+]
+.IB arg }
+.br
+.B ${join
+.I separator
+.IB list }
+.br
+.B ${tl
+.IB list }
+.br
+.B ${parse
+.IB arg ]
+.br
+.B ${pipe
+(
+.B from
+|
+.B to
+|
+.I fdnum
+)
+.IB command }
+.br
+.B ${env}
+.SH DESCRIPTION
+.B Std
+is a loadable module for
+.IR sh (1)
+that provides the equivalent of a
+``standard library'' for the shell, including
+a set of control-flow constructs and some
+other miscellaneous commands.
+In the following descriptions, if an argument is
+executed, then it should be a braced block
+suitable for executing by
+.IR sh .
+A true exit status is defined to be nil;
+any non-nil exit status is false.
+Unless otherwise stated, the return value
+of a command is that of the last command that
+it executed.
+If invalid arguments are passed to any command,
+a
+.B usage
+exception is raised, and a message printed to stderr.
+.PP
+Each of the looping commands
+.BR for ,
+.BR apply ,
+.BR while ,
+and
+.B getlines
+installs an exception handler for the duration of the
+loop to catch the exceptions
+.B break
+and
+.BR continue .
+If a
+.B break
+exception is caught, the loop is terminated; if a
+.B continue
+exception is caught, the loop will continue executing as
+usual.
+The commands are as follows:
+.TP 10
+.B !
+.B !
+inverts the exit status of a command (non-null is changed to null,
+null is changed to non-null).
+.TP
+.B ~
+.B ~
+matches
+.I value
+against each
+.I pattern
+in turn, returning true if any of them match and false otherwise.
+The patterns are of the same form as those accepted by
+the shell for filename pattern matching except that / is
+not treated specially. (see
+.IR filepat (2)).
+Patterns
+must be quoted to stop the shell from interpreting them.
+.TP
+.B no
+True if there are no arguments. Useful for testing if there are any items in a list
+without counting the items with
+.BR $# .
+.TP
+.BI and
+.B And
+evaluates each
+.I command
+in turn until one returns false.
+.TP
+.B apply
+.B Apply
+evaluates
+.I command
+once for each
+.IR arg ,
+passing it in the variable
+.BR $1 .
+.TP
+.B getlines
+.B Getlines
+reads lines from the standard input,
+executing
+.I command
+for each line, setting the environment variable
+.B $line
+to the line read, with any terminating character
+removed. If
+.I separators
+is given, a line is terminated when any character
+in
+.I separators
+is found; the default separator string
+is a single newline character.
+.TP
+.B flag
+Either set
+.RB ( + ),
+clear
+.RB ( - ),
+or test (neither
+.B +
+or
+.BR - )
+the flag
+.IR f ,
+where
+.I f
+is a single character, one of the
+command line flags to
+.I sh
+(see
+.IR sh (1)).
+.TP
+.B fn
+.B Fn
+defines a new builtin command named
+.IR name ;
+when run, this command evaluates
+.IR command .
+The command is stored in the environment
+variable
+.BI fn- name\f1;\fP
+any variables of this form found when
+when
+.B std
+is loaded will be defined in this way.
+If
+.I command
+is not given, then the builtin will be removed.
+.TP
+.B subfn
+.B Subfn
+is similar to
+.B fn
+except that it defines a new substitution builtin
+.IR name .
+When
+.I name
+is invoked, it creates a new local variable
+.B result
+and executes
+.IR command .
+The value of
+.B $result
+when
+.I command
+has terminated is the value yielded by the substitution builtin
+.IR name .
+.I Command
+is stored in and restored from the environment in a similar
+way to
+.BR fn ,
+except that
+.BI sfn- name
+is used as the name of the environment variable.
+.TP
+.B if
+.B If
+executes
+.IR condition ;
+if it returns true, then
+.I action
+is executed, otherwise each of the next
+.IR condition - action
+pairs is evaluated in the same way; if no
+.I condition
+is satisfied, then
+.I elseaction
+will be executed, if present.
+.TP
+.B for
+.B For
+is similar to
+.BR apply ;
+it runs
+.I command
+once for each
+.IR arg ,
+but it performs a local assignment of
+.I arg
+to
+.I var
+each time.
+.TP
+.B or
+.B Or
+evaluates each
+.I command
+in turn until one returns true.
+.TP
+.B pctl
+.B Pctl
+is an interface to the Inferno system call
+.IR sys-pctl (2);
+each argument specifies one bit in the bitmask
+passed to that function. The possible flags are
+.BR newfd ,
+.BR forkfd ,
+.BR newns ,
+.BR forkns ,
+.BR newpgrp
+and
+.BR nodevs .
+See
+.IR sys-pctl (2)
+for details of the meaning of these flags.
+.B Pctl
+returns true.
+.TP
+.B raise
+.B Raise
+raises the exception
+.IR name ;
+.I name
+will be truncated if it is longer than
+that allowed by
+.I raise
+(128 bytes in
+.IR utf (6)
+representation).
+Control will be transferred to the innermost
+rescue block in the same process that
+matches
+.IR name .
+If there is no rescue block in place,
+the current process will exit, yielding
+.I name
+as its exit status.
+If no
+.I name
+is given, the exception named in
+.B $exception
+is raised; if this is null, a
+.B bad raise context
+exception is raised.
+The default command prompt catches all
+exceptions.
+.TP
+.B rescue
+.B Rescue
+executes
+.I command
+with an exception handler installed for the duration
+of the call. It will catch all exceptions with a name
+matching
+.IR pattern ,
+where
+.I pattern
+is of the same form accepted by
+Limbo's
+.B exception
+handling statement.
+Specifically, the pattern is a string that matches literally,
+except that a trailing
+.RB ` * '
+character will match any sequence of characters.
+If an exception is caught,
+.B rescue
+executes
+.IR rescueblock ,
+setting
+.B $exception
+to the name of the exception raised.
+.TP
+.B status
+returns its first argument word as its exit status,
+or nil if none is given.
+.TP
+.B while
+.B While
+repeatedly executes
+.I condition
+and then
+.I action
+until
+.I condition
+does not return true.
+.TP
+.B ${env}
+.B Env
+yields a list of the names of all
+currently set non-nil environment variables.
+.TP
+.B ${hd}
+.B Hd
+yields the first of its arguments, or nil if there
+are no arguments.
+.TP
+.B ${index}
+.B Index
+yields the
+.IR n 'th
+element in its argument list, indexed from 1.
+.I N
+must be a decimal integer.
+.TP
+.B ${join}
+.B Join
+yields a single element which is the concatenation of all
+the elements in
+.I list
+separated by
+.IR separator .
+If there are no elements in
+.IR list ,
+it yields an empty string.
+The shell operator
+\f5$"\f2var\f1
+is exactly equivalent to
+.BI "${join ' ' $" var }\f1.\fP
+.TP
+.B ${parse}
+.B Parse
+parses
+.I arg
+according to the usual syntax rules, raising a
+.B parse error
+exception if it fails.
+.I Arg
+must be a well-formed command block
+surrounded by braces.
+.B Parse
+yields a functionally equivalent version 
+of
+.IR arg .
+.TP
+.B ${pid}
+.B Pid
+yields the process id of the current process.
+.TP
+.B ${pipe}
+.B Pipe
+runs
+.I command
+asynchronously, with one of its file descriptors connected
+to a bidirectional pipe. The first argument to
+.B pipe
+determines which file descriptor is connected: if
+the argument is
+.BR from ,
+its standard output is connected; if the argument is
+.BR to ,
+its standard input is connected; otherwise file descriptor
+.I fdnum
+is connected.
+.B Pipe
+yields the name of a file that can be opened to access
+the other end of the pipe. Note that this command is now
+deprecated in favour of the
+.B <{}
+redirection operator built in to the shell.
+.TP
+.B ${split}
+.B Split
+splits
+.I arg
+into list elements at every point where one or
+more characters in
+.I separators
+appear. If
+.I separators
+is not given, the value of
+.B $ifs
+is used.
+.TP
+.B ${tl}
+.B Tl
+yields all but the first of its arguments,
+or nil if there are no arguments.
+.SS Syntactic considerations
+It is worth being aware of a few pitfalls that await the user of
+some of these commands. Unlike other shells, the syntax of
+.I sh
+does not include the syntax of the control flow commands,
+so it is important to be aware of the rules that govern
+the gathering of the arguments for a command.
+In particular, the following code, written to print a message
+a filename ends in
+.B .b
+will not work: it will always print ``file is Limbo source''.
+.EX
+	and
+		{~ $filename '*.b'}
+		{echo file is Limbo source}
+.EE
+This is because newlines separate shell commands, so
+the above code first invokes
+.B and
+with no arguments, and then each of the braced block
+commands on each subsequent line.
+It is usual to use round brackets in order to group together arguments
+on separate lines, e.g.
+.EX
+	and (
+		{~ $filename '*.b'}
+		{echo file is Limbo source}
+	)
+.EE
+This has the originally intended meaning.
+.SH FILES
+.TF /tmp/pipes/*
+.TP
+.B /tmp/pipe.*d
+Temporary placeholder directory for named pipes.
+.TP
+.B /tmp/pipes/*
+Mount point for named pipes.
+.SH SOURCE
+.B /appl/cmd/sh/std.b
+.SH SEE ALSO
+.IR sh (1),
+.IR sh-expr (1),
+.IR sh-tk (1)
--- /dev/null
+++ b/man/1/sh-string
@@ -1,0 +1,186 @@
+.TH SH-STRING 1
+.SH NAME
+prefix, in, splitl, splitr, drop, take, splitstrl, splitstrr, tolower, toupper, len, alen, slice \- shell script string manipulation
+.SH SYNOPSIS
+.B load string
+
+.B prefix
+.I pre s
+.br
+.B in
+.I c cl
+.br
+.B ${splitl
+.IB "s cl" }
+.br
+.B ${splitr
+.IB "s cl" }
+.br
+.B ${splitstrl
+.IB "s t" }
+.br
+.B ${splitstrr
+.IB "s t" }
+.br
+.B ${take
+.IB "s cl" }
+.br
+.B ${tolower
+.IB s }
+.br
+.B ${toupper
+.IB s }
+.br
+.B ${len
+.IB s }
+.br
+.B ${alen
+.IB s }
+.br
+.B ${slice
+.IB "start end s" }
+.br
+.B ${fields
+.IB "cl s" }
+.br
+.B ${padl
+.I n
+[
+.IR s ...
+.RB ] }
+.br
+.B ${padr
+.I n
+[
+.IR s ...
+.RB ] }
+.br
+.SH DESCRIPTION
+.I String
+is a loadable module for
+.IR sh (1)
+that provides a shell-script interface to the string
+manipulation commands found in
+.IR string (2),
+with a couple of other facilities thrown in for good
+measure. Each of the substitution builtins
+.BR splitl ,
+.BR splitr ,
+.BR drop ,
+.BR take ,
+.BR splitstrl ,
+.BR splitstrr ,
+.BR tolower ,
+and
+.BR toupper
+implements the same functionality as that provided by
+the function of the same name in
+.IR string (2).
+Where a function in the
+.IR string (2)
+module returns a tuple, the equivalent builtin yields
+a two-element list; the others yield a list with a single
+element.
+.PP
+In all
+.I string
+commands, the number of arguments provided must
+be exactly that required by the command so, for instance,
+giving an undefined variable (a zero element list) as
+an argument will cause a
+.B usage
+exception to be generated.
+.PP
+The two builtins
+.B prefix
+and
+.B in
+are again similar to their
+.IR string (2)
+counterparts - their return value is true (an empty string)
+if the equivalent
+.IR string (2)
+function would be non-zero.
+.PP
+.B Len
+returns the length of its argument
+.IR s .
+.B Alen
+returns the length of its argument
+.IR s
+when converted to a byte-array. (This will be
+different from the result of
+.B len
+when
+.I s
+contains non-ASCII characters).
+.B Slice
+is similar to the string-slicing operator in Limbo;
+it returns the section of
+.I s
+starting at index
+.I start
+and ending just before index
+.IR end .
+.I End
+may be the literal string
+.BR end ,
+in which
+.BI "${slice " start " end}"
+is the same as
+.BI "${slice " start " ${len " s "}}"\fR.\fP
+Unlike in Limbo, nothing untoward happens if
+an out-of-range slice is taken: any out of
+range indices are trimmed to within the bounds
+of
+.IR s .
+.PP
+.B Fields
+is similar to
+.B ${split}
+in
+.IR sh (1),
+but does not merge field separator characters.
+It splits
+.I s
+into fields separated by characters in class
+.IR cl ;
+if there are
+.I n
+characters matching
+.I cl
+inside
+.IR s ,
+.B fields
+will yield
+.IR n +1
+items in its result.
+.PP
+.B Padl
+and
+.B padr
+widen the string
+.I s
+to
+.I n
+characters, padding it with spaces on the
+right (for 
+.BR padl )
+or the left (for
+.BR padr )
+as necessary.
+If
+.I s
+is already at least
+.I n
+characters wide, it is left unchanged.
+If several arguments are given, they
+are concatenated, separated with spaces, before
+padding takes place.
+.SH SOURCE
+.B /appl/cmd/sh/string.b
+.SH SEE ALSO
+.IR string (2),
+.IR sh (1),
+.IR sh-std (1),
+.IR sh-expr (1)
--- /dev/null
+++ b/man/1/sh-test
@@ -1,0 +1,46 @@
+.TH SH-TEST 1
+.SH NAME
+report \- shell module for test reporting.
+.SH SYNOPSIS
+.B load test
+
+.br
+.B report
+.I severity verbosity message[...]
+.br
+.SH DESCRIPTION
+.B Its
+is a loadable module for
+.IR sh (1)
+that provides a simple error reporting facility for tests which can be run
+by 
+.IR itest (1) .
+It provides one command,
+.BR report ,
+which is used by a test to report a message with specified severity and verbosity.
+.I Severity
+must be one of INF, WRN, ERR or FTL for Information, warnings, errors and fatal errors
+respectively.
+.I Verbosity
+is an integer between 0 and 9.
+For informatory messages (severity INF), the message will only be
+displayed if the current
+verbosity level is greater than or equal to
+.I verbosity.
+.SH EXAMPLE
+.EX
+#!/dis/sh
+
+load std test
+
+echo 1 > /tmp/a
+echo 2 >/tmp/b
+report INF 5 testing cmp command
+if {cmp /tmp/a /tmp/b} {
+	report ERR 0 'cmp failed - reported different files as the same'
+}{
+	report INF 6 'cmp ok - reported different files as different'
+}
+.EE
+.SH SEE ALSO
+.IR itest (1)
--- /dev/null
+++ b/man/1/sh-tk
@@ -1,0 +1,281 @@
+.TH SH-TK 1
+.SH NAME
+tk, chan, send, recv, alt \- loadable tk module for sh.
+.SH SYNOPSIS
+.B load tk
+
+.B chan
+.IR name ...
+.br
+.B send
+.I chan value
+.br
+.B tk window
+.I title
+[
+.I args...
+]
+.br
+.B tk winctl
+.I winid cmd
+.br
+.B tk wintitle
+.I winid title
+.br
+.B tk namechan
+.I chan
+[
+.I name
+]
+.br
+.B tk del
+.I name
+.br
+.B tk
+.I winid tkcmd
+.br
+.B ${tk window
+.I title
+[
+.I args...
+]
+.B }
+.br
+.B ${tk onscreen
+.I winid
+[
+.I how
+]
+.B }
+.br
+.B ${tk
+.I winid tkcmd
+.B }
+.br
+.B ${recv
+.I chan
+.B }
+.br
+.B ${alt
+.I chan
+\& ...
+.B }
+.br
+.SH DESCRIPTION
+.B Tk
+is a loadable module for
+.IR sh (1)
+that provides access to Inferno Tk graphics
+and string channels.
+Most of the builtin commands that it defines map
+closely to primitives within
+.IR wmlib (2)
+and
+.IR tk (2).
+Unless otherwise stated, if a command requires
+a
+.I winid
+argument, if no window with that id is found, a
+.B bad win
+exception is raised. Similarly, a reference to
+an unknown channel name will raise a
+.B bad chan
+exception.
+There is no requirement that this module be used in
+a windowing context: although window creation will
+fail if there is no context, the channel communication
+primitives will work regardless.
+.TP 10
+.B chan
+For each
+.I name
+in turn,
+.B chan
+creates a new channel called
+.I name
+within the tk module. 
+.I Name
+henceforth represents a Limbo
+.B chan
+.B of
+.B string
+and can be used to send string values between
+.I sh
+processes running in parallel. A
+.I chan
+is also used to receive events arriving from the window
+manager. It is illegal to create a channel whose name
+consists entirely of numeric digits.
+.TP
+.B send
+.B Send
+sends its argument
+.I value
+down the channel
+.IR chan ,
+blocking until a corresponding receive operation
+takes place on the channel.
+.TP
+.B tk window
+.B Tk window
+creates a new top-level window with the text of
+.I title
+in the titlebar at the top. Each window created by the
+tk module is assigned a unique numeric id. This id
+is printed by this command; to get access to the value
+of the
+.I winid
+in a script, use
+.BR "${tk window}" .
+All the remaining arguments are joined together
+by spaces and passed as the tk options for the window.
+When a window is created, a corresponding
+channel of the same name is created. Events from
+the window manager arrive on this channel, and
+should be responded to appropriately using
+.BR "tk winctl" .
+.TP
+.B tk onscreen
+.B Tk onscreen
+must be called to make window
+.I winid
+visible for the first time, the same as
+.I onscreen
+in
+.IR tkclient (2).
+.I How
+is the same as for that call - if given, it must be one of
+.BR place ,
+.BR onscreen
+or
+.BR exact .
+.BR 
+.TP
+.B tk winctl
+.B Tk winctl
+is used to communicate requests to the window manager.
+(see
+.B winctl()
+in
+.IR wmlib (2)).
+If an event arriving on a window's channel is passed
+to
+.BR "tk winctl" ,
+a suitable default action will take place.
+The set of possible actions include:
+.RS
+.TP
+.B exit
+A request to close the window.
+.TP
+.B size
+A request to resize the window.
+.TP
+.B task
+A request to miniaturise the window.
+.TP
+.B move
+A request to move the window.
+.RE
+.TP
+.B tk wintitle
+.B Tk wintitle
+changes the title of the window
+.I winid
+to
+.IR title .
+.TP
+.B tk del
+.B Tk del
+deletes a channel or a window. If
+.I name
+is the
+.I winid
+of an existing window, then both the
+window and its associated channel are destroyed.
+A
+.B del
+of a non-existent channel or window is ignored.
+.TP
+.B tk namechan
+.B Tk namechan
+invokes the Tk module's
+.B namechan()
+function to give a tk name to a
+channel within the tk module.
+If
+.I name
+is omitted, then the tk name given will be
+the same as
+.IR chan .
+.TP
+.BI tk \ winid
+If
+.I winid
+is the id of an existing window, the rest of the
+arguments joined together by spaces
+and sent as a tk command to be interpreted
+in that window. If the shell is in interactive mode,
+then the string returned by tk will be printed.
+The exit status of
+.B tk
+is false if the string returned by tk begins with
+a bang
+.RB ( ! )
+character.
+.TP
+.B ${tk window}
+.B Tk window
+is the same as its command counterpart, except
+that it yields the
+.I winid
+of the newly created window rather than printing
+it.
+.TP
+.BI ${tk \ winid }
+This command is the same as its command counterpart,
+except that it yields the return value from the
+Tk command as its result.
+.TP
+.B ${recv}
+.B Recv
+receives a string value from
+.I chan
+and yields that value. It will block until a corresponding
+send operation takes place on the channel.
+.TP
+.B ${alt}
+.B Alt
+waits until a value is available on any of the
+named
+.IR chan s.
+It yields a list containing two elements,
+the name of the channel from which the value was
+received, and the actual value received.
+.SH EXAMPLE
+The following code creates a window and allows
+normal window manager operations on it. Another
+shell in a new process group is created in order to prevent
+the shell window from
+disappearing when the tk window is deleted.
+.IP
+.EX
+sh
+load std tk
+pctl newpgrp
+wid=${tk window 'My window'}
+tk onscreen $wid
+tk $wid update
+while {} {tk winctl $wid ${recv $wid}} &
+.EE
+.SH SOURCE
+.B /appl/cmd/sh/tk.b
+.SH SEE ALSO
+.IR sh (1),
+.IR sh-std (1),
+.IR sh-expr (1),
+.IR tkcmd (1),
+.IR tk (2),
+.IR tkclient (2),
+.IR wmlib (2),
+``The Tk Reference Manual''
--- /dev/null
+++ b/man/1/sleep
@@ -1,0 +1,24 @@
+.TH SLEEP 1
+.SH NAME
+sleep, pause \- suspend execution for an interval
+.SH SYNOPSIS
+.B sleep
+.I n
+.PP
+.B pause
+.SH DESCRIPTION
+.I Sleep
+suspends its own execution for
+.I n
+seconds before returning.
+.PP
+.I Pause
+never returns,
+and is typically used to stop a command
+interpreter reading any more from the standard input.
+.SH SOURCE
+.B /appl/cmd/sleep.b
+.br
+.B /appl/cmd/pause.b
+.SH "SEE ALSO"
+.IR sys-sleep (2)
--- /dev/null
+++ b/man/1/sort
@@ -1,0 +1,33 @@
+.TH SORT 1
+.SH NAME
+sort \- sort file
+.SH SYNOPSIS
+.B sort
+[
+.B -nr
+] [
+.I file
+]
+.SH DESCRIPTION
+.I Sort
+sorts the lines of
+.I file
+(default: standard input)
+and writes the sorted output to
+standard output.
+.PP
+Whole lines are sorted into increasing order, using lexicographic ordering of Unicode characters
+by default.
+The sort is stable, so that lines that compare equal will appear in the output
+in the same order as in the original file.
+The sort order is affected by the following options:
+.TP
+.B -n
+Each line is assumed to have an initial numeric string representing an integer,
+with optional plus or minus sign, and the lines
+are sorted by those numeric values into increasing order.
+.TP
+.B -r
+Reverses the sense of comparisons.
+.SH BUGS
+The entire file is read into memory to be sorted.
--- /dev/null
+++ b/man/1/spree-join
@@ -1,0 +1,45 @@
+.TH SPREE-JOIN 1
+.SH NAME
+join \- join a spree clique.
+.SH SYNOPSIS
+.B spree/join
+[
+.B -d
+.I mntdir
+] [
+.B -j
+.I joinrequest
+]
+.I name
+.SH DESCRIPTION
+When a
+.IR spree (4)
+instance has been mounted from somewhere on the
+network,
+.B join
+can be used to join a clique and fire up the appropriate
+module to perform the client-side display.
+.I Mntdir
+gives the directory where the spree
+instance is mounted (default
+.BR /n/remote );
+.I joinrequest
+gives the initial join request to be passed to the clique
+(default
+.IR join ).
+.I Name
+is the name of the clique's directory within the spree
+directory.
+.SH EXAMPLE
+When spree is started, it creates a ``lobby'' engine
+that keeps a record of what cliques have been started, who
+is a member of which, etc.
+.EX
+	spree/join 0
+.EE
+will connect to this session.
+.SH SOURCE
+.B /appl/spree/join.b
+.SH "SEE ALSO"
+.IR spree (4),
+.IR spree (2)
--- /dev/null
+++ b/man/1/stack
@@ -1,0 +1,166 @@
+.TH STACK 1
+.SH NAME
+stack, stackv \- examine call stack
+.SH SYNOPSIS
+.B "bind '#p' /prog"
+.br
+.B stack
+[
+.B -v
+]
+[
+.B -p
+.I dispath
+.I sblpath
+]...
+.I pid
+.br
+.B stackv
+[
+.B -Tlm
+] [
+.B -r
+.I maxdepth
+] [
+.I pid\fR[\f5.\fIsym\fR] ...] ...
+.SH DESCRIPTION
+.I Stack
+writes to the standard output a stack trace for process
+.IR pid ,
+by
+decoding the stack traceback data contained in the file
+.BI /prog/ pid /stack .
+The
+.B -v
+option causes
+.I stack
+to print values of arguments and variables.
+The output is most useful when the Limbo program
+was compiled with the
+.B -g
+option to produce a
+.B .sbl
+symbol file.
+.PP
+.I Stack
+has a built-in list of associations between
+.B dis
+directories and their associated source directories
+(e.g. it can automatically map from
+.B /dis/ls.dis
+to
+.BR /appl/cmd/ls.sbl ).
+Giving the
+.B -p
+option adds a new association to the head of this list:
+if a module path prefix matches
+.IR dispath ,
+.I stack
+will search for a symbol file in
+.IR sblpath .
+If the environment variable
+.B $sblpath
+is set, pairs of items from it are added to the
+association list, as given as
+.B -p
+options.
+The
+.B -p
+options take precedence over
+.BR $sblpath .
+.PP
+.I Stackv
+recursively traverses the symbols it finds, printing
+values as it goes. Repeated identical structure is not
+shown \- only the pointer value is printed, followed by
+.BR (qv) .
+Each argument gives a starting point
+for the traversal, rooted by a process id,
+.IR pid .
+If an unadorned process id is given, all values in all
+stack frames in the process will be printed; adding names
+specifies the starting point. For instance,
+.B 123.init.ctxt.display
+might specify the
+.B display
+field inside the
+.B ctxt
+adt inside the
+.B init
+function inside the process
+.BR 123 .
+.I Stackv
+understands the following options:
+.TP 10
+.B -l
+Show source line information with each item.
+.TP
+.B -m
+Show module variables accessible from each stack frame.
+.TP
+.B -T
+Do not show the Limbo types of value encountered.
+.TP
+.BI -r \ maxdepth
+Restrict the maximum traversal depth to
+.I maxdepth
+levels.
+.SH EXAMPLE
+Run
+.I stack
+on process with ID 1:
+.IP
+.EX
+$ stack 1
+unknown fn() Module $Sys PC 742103
+waitfor() shnew.b:105.7, 38
+runpipeline() shnew.b:483.2, 14
+runit() shnew.b:552.3, 29
+init() shnew.b:83.3, 28
+.EE
+.PP
+The process is executing in the
+.B Sys
+module, a call to
+.B sys->read
+that originated at line 105 (characters 7 to 38) of the
+.B waitfor
+function in
+.BR shnew.b .
+.PP
+Once again, with the
+.B -v
+option to reveal more:
+.IP
+.EX
+$ stack -v 1
+unknown fn() Module $Sys PC 742103
+waitfor(pid=18) shnew.b:105.7, 38
+        status=[0] ""
+        buf=[64] @b419a4
+        n=-1
+        who=-1
+runpipeline(ctx=nil, pipeline=@b41454) shnew.b:483.2, 14
+        pid=18
+runit(ctx=nil, pipes=nil) shnew.b:552.3, 29
+        pipeline=@b41454
+init(ctxt=nil, argv=nil) shnew.b:83.3, 28
+        buf=[1024] @b40f04
+        n=4
+        arg=@b41634
+        prompt=[21] "$ "
+$
+.EE
+.SH FILES
+.BI /prog/ pid /stack
+.br
+.BI /prog/ pid /status
+.SH SOURCE
+.B /appl/cmd/stack.b
+.br
+.B /appl/cmd/stackv.b
+.SH "SEE ALSO"
+.IR deb (1),
+.IR ps (1),
+.IR prog (3),
+.IR debug (2)
--- /dev/null
+++ b/man/1/stream
@@ -1,0 +1,54 @@
+.TH STREAM 1
+.SH NAME
+stream \- stream data between source and sink
+.SH SYNOPSIS
+.B stream
+.RB [ \-a ]
+.RB [ \-b
+.IR bufsize ]
+.I file1
+[
+.I file2
+]
+.SH DESCRIPTION
+.I Stream
+creates a process that uses
+.I stream
+(see
+.IR sys-read (2))
+to stream data in chunks of at most
+.I bufsize
+bytes (default:
+.LR Sys->ATOMICIO ,
+or 8192 bytes) from
+.I file1
+to the standard output.
+If
+.I file2
+is provided,
+the two files are instead cross-connected by two streaming processes:
+one process streams data from
+.I file1
+to
+.IR file2 ,
+and the other streams data from
+.I file2
+to
+.IR file1 .
+In all cases,
+.I stream
+writes data to the destination file in full buffers of
+.I bufsize
+bytes.
+.PP
+.I Stream
+waits for all streaming processes to stop before returning,
+unless the
+.B -a
+(asynchronous) option is given, which causes it to
+return after spawning the streamers.
+.SH SOURCE
+.B /appl/cmd/stream.b
+.SH SEE ALSO
+.IR cat (1),
+.IR sys-read (2)
--- /dev/null
+++ b/man/1/strings
@@ -1,0 +1,27 @@
+.TH STRINGS 1
+.SH NAME
+strings \- extract printable strings
+.SH SYNOPSIS
+.B strings
+[
+.I file ...
+]
+.SH DESCRIPTION
+.I Strings
+finds and prints strings containing 6 or more
+consecutive printable UTF-encoded characters
+in a (typically) binary file, default
+standard input.
+Printable characters are taken to be
+.SM ASCII
+characters from blank through tilde (hexadecimal 20 through 7E), inclusive,
+and
+all other characters from value 00A0 to FFFF.
+Strings reports
+the decimal offset within the file at which the string starts and the text
+of the string. If the string is longer than 70 runes the line is
+terminated by three dots and the printing is resumed on the next
+line with the offset of the continuation line.
+.SH SOURCE
+.B /appl/cmd/strings.b
+
--- /dev/null
+++ b/man/1/sum
@@ -1,0 +1,47 @@
+.TH SUM 1
+.SH NAME
+sum, md5sum, sha1sum \- calculate file's checksum
+.SH SYNOPSIS
+.B sum
+.IR file " ..."
+.PP
+.B md5sum
+[
+.I file " ..."
+]
+.SH DESCRIPTION
+.I Sum
+prints the 32-bit checksum (actually a CRC), length in bytes, and name of each
+.IR file ,
+one per line.
+It ignores directories.
+.PP
+.I Md5sum
+prints the MD5 message digest (as 32 hexadecimal digits) and the name of each
+.IR file ,
+separated by a tab,
+one per line.
+If no files are given, the standard input is read.
+.PP
+.I Sha1sum
+is similar, but uses the US National Institute of Standards and Technology's secure
+hash algorithm SHA1 instead of MD5.
+For each
+.IR file ,
+it prints its SHA1 secure hash (as 40 hexadecimal digits) and the file's name,
+separated by a tab,
+one per line.
+If no files are given,
+.I sha1sum
+reads the standard input.
+.SH SOURCE
+.B /appl/cmd/sum.b
+.br
+.B /appl/cmd/md5sum.b
+.br
+.B /appl/cmd/sha1sum.b
+.SH SEE ALSO
+.IR cmp (1),
+.IR wc (1),
+.IR crc (2),
+.IR keyring-sha (2)
--- /dev/null
+++ b/man/1/tail
@@ -1,0 +1,79 @@
+.TH TAIL 1
+.SH NAME
+tail \- deliver the last part of a file
+.SH SYNOPSIS
+.B tail
+[
+.RB [\f5+-\fP] \fInumber\fP [\f5lbc\fP][ rf ]
+]
+[
+.I file
+]
+.PP
+.B tail
+[
+.B -fr
+]
+[
+.B -n
+.I nlines
+]
+[
+.B -c
+.I ncharacters
+]
+[
+.I file
+]
+.SH DESCRIPTION
+.I Tail
+copies the named file to the standard output beginning
+at a designated place.
+If no file is named, the standard input is copied.
+.PP
+Copying begins at position
+.BI + number
+measured from the beginning, or
+.BI - number
+from the end of the input.
+.I Number
+is counted in lines, 1K blocks or characters,
+according to the appended flag
+.LR l ,
+.LR b ,
+or
+.LR c .
+Default is
+.B -10l
+(ten ell).
+.PP
+The further flag
+.L r
+causes tail to print lines from the end of the file in reverse order;
+.L f
+(follow) causes
+.I tail,
+after printing to the end, to keep watch and
+print further data as it appears.
+.PP
+The second syntax is that promulgated by POSIX, where the
+numbers of lines (-n) or characters (-c) are specified separately as signed
+integers. Note than an unsigned value is treated as negative,
+ie '-n 2' is equivalent to '-n -2'.
+.PP
+.SH EXAMPLES
+.TP
+.B tail file
+Print the last 10 lines of a file.
+.TP
+.B tail +0f file
+Print a file, and continue to watch
+data accumulate as it grows.
+.br
+According to custom, option
+.BI + number
+counts lines from 1, and counts
+blocks and characters from 0.
+.TP
+.B tail -nlines +3 file
+Print a file starting at line number 3
--- /dev/null
+++ b/man/1/tcs
@@ -1,0 +1,67 @@
+.TH TCS 1
+.SH NAME
+tcs \- translate character sets
+.SH SYNOPSIS
+.B tcs
+.B -l
+.RB [ -v ]
+[
+.I csname
+]
+.PP
+.B tcs
+[
+.B -f
+.I ics
+] [
+.B -t
+.I ocs
+] [
+.I file ...
+]
+.SH DESCRIPTION
+.I Tcs
+converts its input from the
+.I ics
+character set encoding into Unicode runes and then outputs the data in the
+encoding of the
+.I ocs
+character set.
+The default values of
+.I ics
+and
+.I ocs
+are both
+.BR utf8 .
+This is the standard Inferno Unicode text encoding as described by
+.IR utf (6).
+If no files are specified
+.I tcs
+reads from its standard input.
+.PP
+The
+.B -l
+option causes
+.I tcs
+to output the list of character sets that it understands.
+The
+.B -v
+option causes a more detailed listing to be given.
+Giving a
+.I csname
+argument to the
+.B -l
+option causes
+.I tcs
+to list the known aliases for that name.
+The first name in the list is the standard (or preferred) name.
+.SH FILES
+.TF /lib/convcs/charsets
+.TP
+.B /lib/convcs/charsets
+Specifies the supported character set names, their aliases and the implementation
+of their converters.
+.SH SOURCE
+/appl/cmd/tcs.b
+.SH SEE ALSO
+.IR convcs (2)
--- /dev/null
+++ b/man/1/tee
@@ -1,0 +1,30 @@
+.TH TEE 1
+.SH NAME
+tee \- pipe fitting
+.SH SYNOPSIS
+.B tee
+[
+.B \-a
+]
+[
+.I file
+\&...
+]
+.SH DESCRIPTION
+.I Tee
+reads the standard input and writes a copy of each block read
+to each
+.I file
+in turn, and then to the standard output.
+Normally
+.I tee
+rewrites each
+.IR file ;
+the
+.B \-a
+option causes data to be appended instead.
+.SH SOURCE
+.B /appl/cmd/tee.b
+.SH SEE ALSO
+.IR cat (1),
+.IR tail (1)
--- /dev/null
+++ b/man/1/telnet
@@ -1,0 +1,35 @@
+.TH TELNET 1
+.SH NAME
+telnet \- make a remote telnet connection
+.SH SYNOPSIS
+.B telnet
+.I machine
+.SH DESCRIPTION
+.B Telnet
+uses the Telnet protocol to talk to a remote
+.IR machine ,
+addressed using any form acceptable to
+.IR dial (2):
+.IB net ! host ! port
+in general.
+The default
+.I net
+is
+.BR tcp ,
+and the default
+.I port
+is 23, the standard Telnet login port.
+.PP
+.I Telnet
+connects to the given
+.I machine
+and interprets the Telnet protocol.
+It reads data from its standard input
+and sends it to the remote machine, and
+copies to the standard output the data it receives from the remote machine.
+.SH SOURCE
+.B /appl/cmd/telnet.b
+.SH SEE ALSO
+.IR cpu (1)
+.br
+``Telnet protocol specification'', RFC854 (1 May 1983) and related RFCs.
--- /dev/null
+++ b/man/1/time
@@ -1,0 +1,34 @@
+.TH TIME 1
+.SH NAME
+time \- time command execution
+.SH SYNOPSIS
+.B time
+.I command
+[
+.I arg ...
+]
+.SH DESCRIPTION
+.I Time
+executes the
+.I command
+with the given arguments, and reports on standard error the command's
+load time, real time for execution, and the total, in seconds.
+The load time
+is just the time for
+.I time
+itself to load
+.IR command ;
+loads done later by the command are included in the real time.
+To time a pipeline, use the
+.B -c
+option to
+.IR sh (1):
+.IP
+.B "time sh -c 'ps | grep Sh'"
+.SH SOURCE
+.B /appl/cmd/time.b
+.SH "SEE ALSO"
+.IR sh (1),
+.IR sys-millisec (2)
+.SH BUGS
+There is no way to measure CPU time (real or virtual) used by a command.
--- /dev/null
+++ b/man/1/timestamp
@@ -1,0 +1,36 @@
+.TH TIMESTAMP 1
+.SH NAME
+timestamp \- log event times
+.SH SYNOPSIS
+.B timestamp
+[
+.I tag
+]
+.SH DESCRIPTION
+.I Timestamp
+reads lines from its standard input and writes them to its
+standard output, prefixing each line with the time that
+it was written, in decimal milliseconds since
+.I timestamp
+was started. If
+.I tag
+is given, it is written along with the time stamp on each line,
+to enable tagging if several writers are each writing
+to the same log file.
+.PP
+The first line written gives the time that
+.I timestamp
+was started, in milliseconds since the epoch.
+Note that logging events can cause timings to change,
+and that the timestamps are only as accurate as the
+times reported by
+.BR /dev/time .
+.SH SOURCE
+.B /appl/cmd/timestamp.b
+.SH EXAMPLE
+.EX
+% {echo hello; sleep 1; echo goodbye} | timestamp eg
+0000000000 eg start 1080156139468
+0000000007 eg hello
+0000000988 eg goodbye
+.EE
--- /dev/null
+++ b/man/1/tiny
@@ -1,0 +1,259 @@
+.TH TINY 1
+.SH NAME
+tiny: sh, broke, kill, rm \- reduced command line interface to the Inferno system
+.SH SYNOPSIS
+.B tiny/sh
+[
+.B -n
+] [
+.BI -c command
+] [
+.I file
+]
+.PP
+.B tiny/broke
+.PP
+.B tiny/kill
+[
+.B -g
+]
+[
+.I pid ...
+]
+[
+.I module ...
+]
+.PP
+.B tiny/rm
+[
+.I file
+\&...
+]
+.SH DESCRIPTION
+The
+.I tiny
+commands are smaller, simpler versions of more capable but larger Inferno commands.
+They are provided for use on devices where a certain level of functionality
+might be useful for configuration or maintenance (or development), but
+device constraints are such as to make the use of the normal, fleshier versions
+of the commands unattractive.
+For example, the Dis object files can be as much as 5 times smaller (or better) than the
+mainstream alternatives.
+They are also useful when initially porting the system.
+They live in the directory
+.BR /dis/tiny ,
+but could be placed in the
+.B /dis
+of a small device
+(eg, via
+.IR root (3))·
+.PP
+.I Broke
+kills broken processes and prints their process IDs.
+.PP
+.I Kill
+terminates each process (for a numeric
+process ID
+.IR pid )
+or
+process running a given
+.I module
+(for a non-numeric module name),
+by writing a
+.L kill
+message to the corresponding process's control file
+in
+.IR prog (3).
+The
+.B -g
+option causes
+.I kill
+to write a
+.L killgrp
+message instead, killing all processes in the given process's process group
+(see
+.IR sys-pctl (2)).
+Processes running a
+.I module
+are identified by their
+.L status
+file, and the process ID of each such process is printed on standard output.
+.PP
+.I Rm
+removes files and empty directories, subject to the permission rules given in
+.IR rm (1).
+There are no options.
+.PP
+.I Sh
+provides a simple user level interface (a shell) to the Inferno system.
+(It was once the only Inferno shell.)
+It reads input lines, identifies a command and arguments for that command, and arranges for execution of the corresponding Inferno module.
+There are features that allow input/output redirection, creating pipelines, and performing tasks in background.
+It is nevertheless a rudimentary shell designed for starting
+and debugging applications.
+It is not intended to serve as a general-purpose programmable shell.
+.PP
+If a file is named as a command line argument, that file is the source of input; otherwise, standard input is read.
+.PP
+Options are:
+.TP
+.B -n
+Don't fork the namespace.  By default, 
+.I sh
+forks the namespace, making subsequent namespace changes invisible to the previous namespace group.
+.TP
+.BI -c command
+Execute the single
+.I command
+rather than prompting to read commands from the standard input.
+.SS "Command line syntax"
+Each line consists of one or more command pipelines each separated by either an ampersand (&) which indicates that the pipeline should be run in background or a semi-colon (;). The semi-colon need not be provided for the last command pipeline on a line.
+.PP
+Command pipelines are not allowed to span lines.
+.PP
+Each command pipeline consists of one or more commands separated by a vertical bar
+.RB ( | )
+character. The standard output of one command is made the standard input of the next command to the right.
+.PP
+Redirection of input/output to pipes takes precedence over redirection from/to files.
+.PP
+In the limit case, a command pipeline consists of a single command with no pipes.
+.PP
+A command consists of one or more fields. The first (leftmost) field is the command field. It is used to determined the executable file to be loaded and run; see below. The remaining fields are parsed and become command line arguments that are passed to the module's init function as a list of strings.
+.PP
+Any input following a
+.B #
+on a line is discarded as comment.
+.SS "Finding the module"
+The command field is converted to the pathname of the Dis file of some module. That field can be either an absolute pathname, starting from
+.BR / ,
+or a relative pathname from the current directory.
+.PP
+As a convenience, the user need not specify the
+.B .dis
+suffix to the filename. If missing, it will be added by the shell.
+.PP
+If the load fails there is, in general, a second attempt to load the module by resolving the pathname relative to the
+.B /dis
+directory (or any directory bound to the
+.B /dis
+directory in the current namespace).
+.PP
+There are two exceptions to this second attempt. The second load attempt is not performed if the command field provides an absolute pathname or a relative pathname starting with dot-slash
+.RB ( ./ ).
+Such explicit naming is taken to mean that the user will accept no substitutions.
+.PP
+The shell requires that the Dis file implement a module with an interface equivalent to the
+.L Command
+module as specified in
+.B /module/sh.m
+(see
+.IR command (2)).
+Otherwise, the named file will not load.
+.PP
+In lieu of a path mechanism, a process can create a union directory at
+.BR /dis .
+.SS "File name expansion"
+Command line arguments (including the command field itself) are expanded by the shell according to the regular expression rules described in
+.IR filepat (2).
+.PP
+This expansion is not applied to the filenames used for input/output redirection.
+.SS "Quoting"
+The shell special characters can be stripped of their meaning and treated as literals by enclosing them in single quotes. Inside a quoted string, the special meaning of the single quote can be removed by immediately following it with another single quote. Command lines with un-terminated quoted strings are rejected and cause an error message.
+.PP
+For example:
+.IP
+.EX
+$ echo ''''
+\&'
+$ echo 'don''t'
+don't
+$ echo 'hello' 'world
+sh: unmatched quote
+$ echo 'a'b
+ab
+$ echo a'b'
+ab
+$
+.EE
+.SS "Shell special characters"
+The following characters are treated specially by
+.I sh
+and must be quoted to be taken literally:
+.TP
+blank
+white space, except in a quoted string
+.TP
+tab
+white space, except in a quoted string
+.TP
+newline
+command line terminator
+.TP
+.B #
+Start of comment
+.TP
+.B '
+Start of/end of quoted string (single quote)
+.TP
+.B |
+Interface between commands in a command pipeline.
+.TP
+.B &
+Terminator for command pipelines to be run in background.
+.TP
+.B ;
+Terminator for command pipelines to be run synchronously by the shell.
+.TP
+.B >
+Output re-direction: create file if it does not exist; truncate file if it exists
+.TP
+.B >>
+Output re-direction: create file if it does not exist; append to file if it exists
+.TP
+.B <
+Input re-direction.
+.SS "Prompt"
+The shell uses a prompt consisting of the system name as provided by
+.B /dev/sysname
+suffixed by
+.BR $ .
+.PP
+.SS "Input/output re-directions"
+By default, standard input is the console keyboard and standard output the console display. Each command can specify that standard input be taken from a file and standard output be written to a file.
+.PP
+Attempts to redirect standard input to a non-existing file will fail. Redirecting standard output to a non-existing file will cause that file to be created. If the destination file already exists, it will be overwritten. Any previous contents are lost.
+.PP
+In cases of competing re-direction mechanisms (re-direct to a file and to a pipe), the pipe has precedence.
+.PP
+.SS "Background tasks"
+In general, the shell waits for the termination of a command pipeline before continuing execution, for example, prompting the user for the next command. However, if the command pipeline is terminated by an ampersand
+.RB ( & )
+character, the wait stage is skipped and the shell continues execution immediately, in this case the command pipeline executes as a background task.
+.PP
+.SS "Name space concerns"
+When started, the shell creates an independent file name space that is a copy of the file name space of the shell's creator.
+.PP
+Command pipelines started by the shell are executed by threads that share the shell's name space. If those commands modify the file name space (and they have not mimicked the shell in creating their own independent name space), those modifications will be perceived by the shell when it continues execution. See
+.IR bind (1)
+and
+.IR sys-pctl (2).
+.SH FILES
+.BI /prog/ n /wait
+.SH SOURCE
+.B /appl/tiny/broke.b
+.br
+.B /appl/tiny/kill.b
+.br
+.B /appl/tiny/rm.b
+.br
+.B /appl/tiny/sh.b
+.SH "SEE ALSO"
+.IR bind (1),
+.IR sh (1),
+.IR filepat (2),
+.IR command (2),
+.IR sys-pctl (2),
+.IR cons (3),
+.IR pipe (3),
+.IR prog (3)
--- /dev/null
+++ b/man/1/tkcmd
@@ -1,0 +1,53 @@
+.TH TKCMD 1
+.SH NAME
+tkcmd \- enter Tk commands interactively
+.SH SYNOPSIS
+.B tkcmd
+[
+.B -iu
+] [
+.I tkarg
+]
+.SH DESCRIPTION
+.B Tkcmd
+allows interactive entry of Tk commands; it is useful for
+testing out features of Tk prior to incorporating them in
+a Limbo program. It accepts two arguments:
+.TP
+.B -i
+Force interactive mode. In interactive mode (the default if the
+standard input is directed at
+.BR /dev/cons ),
+a prompt is printed before every tk command entered.
+.TP
+.B -u
+Suppress the automatic tk update command after
+every entered command.
+.PP
+When
+.I tkcmd
+is started, a new top level window is created;
+.I tkcmd
+creates a titlebar and handles all the normal
+window window manager interactions.
+When the window is closed,
+.I tkcmd
+terminates.
+Each line typed to
+.I tkcmd
+is passed directly to the function
+.B Tk->cmd
+(see
+.IR tk (2))
+and executed in the context of the top level window;
+a Tk ``update'' command is then issued.
+Any return value from the command is printed.
+There is one predefined Tk channel,
+named ``stdout''; anything sent down this channel will
+be printed to the standard output.
+.SH SOURCE
+.B /appl/cmd/tkcmd.b
+.SH SEE ALSO
+.IR tk (2),
+.IR intro (9),
+.IR sh-tk (1)
--- /dev/null
+++ b/man/1/tktester
@@ -1,0 +1,154 @@
+.TH TKTESTER 1
+.SH NAME
+tktester \- test Tk widgets and help design Tk layouts
+.SH SYNOPSIS
+.B wm/tktester
+[
+.B -import
+]
+.SH DESCRIPTION
+.I Tktester
+not only tests the
+.IR tk (2)
+widget implementation but can help when designing Tk application layouts.
+Its main window contains the design area where widgets are placed and edited. Most of the commands for creating and moving widgets are located on the control bar just below the design area although a few commands may be found in the menus at the top of the window. Output is sent to the text box below the control bar.  
+.PP
+Widget properties may be modified using the config window and widget commands called from the command window.
+.SH Main Window
+This is split into four areas:
+.PP
+.SS Menu Bar
+This contains various file operations as well as a few commands
+.TP 10
+.B File
+New:	Starts a new file
+.br 
+Open:		Opens a saved file
+.br 
+Snarf:		Sends the current file to snarf
+.br 
+Save:		Saves the current file
+.br 
+Save as:	Asks for a new filename and then saves the file
+.br 
+Exit:		Close \f1tktester\fR
+.TP
+.PP
+Files are saved in the form of a list of tk commands. This means that they can easily be imported into programs as part of an array. Files to be loaded must have .f as the top frame with no widgets outside it.
+.TP 10
+.B Row/Column 
+The current row/column is the one in which the currently selected widget/empty cell is located
+Insert - inserts a new row/column either before or after the currently selected row/column
+Delete - deletes currently selected row/column
+Format - sets the properties for the current row/column
+.TP
+.B Hidden
+Forget - removes the current widget from the display area but does not delete it. The widget name is then added to the 'Hidden' menu and can still be selected by clicking on its name there. This can be useful for operations which require widgets that are not currently packed e.g. placing a frame as a window within a canvas. Forgotten items will still be saved.
+.TP
+.B Disabled
+This menu only appears when a widget has been disabled. In this state, button bindings are ignored so it becomes impossible to select the widget. When a widget is disabled, its name is automatically added to the 'Disabled' menu and can be selected from there.
+.PP
+.SS Design Area
+This is the main area of the main window where the widgets are displayed. To select a widget, click on it with mouse button 3, the control bar shows the name and other information about the currently selected widget. Frames themselves can only be selected by clicking on their label, clicking elsewhere on the frame (if there is no widget there) will select the empty cell, any new widget created will be placed here. Once a widget has been selected, you can move it by clicking on the empty destination cell with mouse button 2. Individual widgets can be moved from one frame to another but frames themselves can currently only be moved within the same parent frame. Clicking mouse button 2 on a widget will delete that widget if the \f1Free Delete\fR button on the control bar is on.
+.SS Control Bar
+This is split into three different menus. To select the different submenus, click on the >> at the end of the menu title.
+.PP 
+\fIData Menu\fR
+.TP 10
+.B Widget
+shows information about the current widget
+.br
+clicking on the \f1Destroy\fR button will delete the currently selected widget.
+The \f1Free Delete\fR button to the right of the \f1Destroy\fR button can be toggled on and off by clicking on it. When turned on (red background) , clicking mouse button 2 on a widget in the design area will delete it.
+.TP
+.B Grid
+shows information about the current grid
+.br
+Clicking the \f1Hide Labels\fR button will hide the frame labels so that you can see what the screen will look like normally.
+.PP
+\f1Position and Formatting Menu\fR
+.TP 10
+.B Move
+Move the widget within its current frame
+.TP
+.B Spanning
+Change row and column spanning properties
+.TP
+.B Padding
+Set cell padding, checkbox selects internal or external padding
+.TP
+.B Position
+Adjust widget position, widget can be stretched if opposite positions are selected e.g. to flill horizontaly, select < and >
+.PP
+\f1New Menu\fR
+.PP
+Clicking on 'New' will bring up the pack menu, here you can set packing to down or right. This is used when a new widget is created. If the user has not selected an empty cell, the new widget will be placed either below or to the right of the currently selected widget. Clicking on '>>' will scroll the buttons within the menu to the left to allow access to those that might be off screen.
+.PP
+.SS Output Box
+Output and errors are reported here. This box may be hidden by clicking on the grey button located at the bottom of the main window.
+.PP
+.SH POPUP WINDOWS
+.B
+.PP
+.SS Config Window
+This window is opened by clicking on the red button located at the bottom of the main window. To configure a widget's options, the widget must be selected. To modify an option, type the new value required into the relevant entry box and click the 'set' button. Any output (including error messages) returned will be sent to the output box.
+.PP
+There are two template buttons at the bottom of the config window, set as default and save as default. Set will cause each new widget of the same type to be created with the same options as the currently selected widget. This default will not be remembered once \f1tktester\fR has been closed. Save does the same as set except the default is saved so it will be not be lost if \f1tktester\fR is closed.
+.PP
+Default Template Options
+.PP A few special characters can be used when setting default widget options.
+.TP 5
+.B %n
+the name of the widget e.g. .f.f1.b2
+.TP
+.B %t
+the widget type e.g. button
+.TP
+.B %i
+the number of the widget
+.PP
+By default, each widget with a -text option is set to {%t %i} e.g. button 2. Note: These options only work with default templates, setting the -text option of a specific button to {%t %i} will just cause '%t %i' to be displayed.
+.PP
+.SS Command Window
+This window is opened by clicking on the green button located at the bottom of the main window. To call the commands for a particular widget, the widget must be selected. 
+.PP
+The command window is split into two listboxes and one entry box. The first listbox contains all the main commands available for the current widget type. Selecting a command will bring up a list of subcommands (if they exist) in the second listbox as well as displaying any arguments required above the entrybox. To run a command, first select the command (and any subcommand), then enter the required arguments into the entry box and click run. The command, as well as any output, is sent to the output box on the main window. If no output is returned, the output box will display 'ok'.
+.PP
+.SH OPTIONS
+.TP 10 
+.B -import
+Tells
+.I tktester
+to import valid widget commands from the man pages. This data is saved in the
+.B tkwargs/
+directory, which must already exist.
+.SH FILES
+The file
+.B tkwargs/widgets
+must contain a list of widgets, one per line as follows:
+.IP
+.RI [ name ]
+.RB [ abv ]
+.PP
+with the fields separated by tabs or spaces.
+For example:
+.IP
+.B "menubutton mb"
+.br
+.B "listbox lb"
+.PP
+.SH SOURCE
+.B /appl/wm/tktester
+.SH BUGS
+The command window sometimes lists a command more than once. It does not matter which one is used.
+.PP
+In a saved file, any grid commands must put -row and -column options \f1before\fR -rowspan or -columnspan.
+.PP
+Tktester can crash when loading a file if it is not in the correct format.
+.PP
+.SH PROPOSED ADDITIONS
+.SS Allow renaming of widgets
+At the moment, \f1tktester\fR can only load, save and use tk commands where the widget names adhere to the format .abv[n] where abv is the abreviation for the widget type e.g. b for buttons, sb for scrollbars etc and n is an optional number. It would be better to allow users to have more meaningful names such as .f.fmenu. Implementing this would also make it possible to load in commands written outside of \f1tktester\fR for testing or modification purposes.
+.PP
+.SS Column and Row indicators
+This would more clearly show which rows and columns widgets were in (especially when widgets are spanning more than one). Also could be used to select individual rows and columns more explicity and maybe for multiple selections.
--- /dev/null
+++ b/man/1/toolbar
@@ -1,0 +1,99 @@
+.TH TOOLBAR 1
+.SH NAME
+toolbar \- window manager toolbar
+.SH SYNOPSIS
+.B wm/toolbar
+[
+.B -s
+] [
+.B -p
+]
+.SH DESCRIPTION
+.I Toolbar
+is designed to be run as the controlling application under
+an instance of
+.IR wm (1).
+It runs an initialisation shell script,
+provides a menu allowing the user to start new programs.
+and shows icons representing windows that have been hidden.
+.PP
+When
+.I toolbar
+is started, it configures itself by means of the
+.B /lib/wmsetup
+shell script.
+.I Toolbar
+loads the shell
+.IR sh (1),
+and defines the following
+shell built-in
+commands before executing the script:
+.HP
+.B menu
+.I title1
+.RI [ title2]
+.I command
+.br
+Insert an item at the top of the start menu.
+.I Title1
+is the text of the item on the main menu.
+If
+.I title2
+is given then
+.I title1
+is a sub-menu with
+.I title2
+as the menu item.
+.I Command
+is executed by the shell whenever the item is selected.
+An item with an empty command is displayed as a separator.
+.HP
+.B delmenu
+.br
+Forget all menu items.
+.PP
+The standard
+.B /lib/wmsetup
+script executes the script
+.BI /usr/ username /lib/wmsetup ,
+enabling each user to have their own window manager configuration.
+.PP
+Normally
+.I toolbar
+packs a menu button referring to the start menu at the left hand side of the tool bar.
+The
+.B -s
+option suppresses that.
+.PP
+.I Toolbar
+serves the shared
+.I "snarf buffer"
+used by cut and paste in
+.IR wm (1)
+applications,
+except in hosted Inferno where the host's standard clipboard system is used instead,
+via
+.IR snarf (3).
+If
+.I toolbar
+cannot find
+.B /chan/snarf
+or the
+.B -p
+option is given,
+.I toolbar
+will create its own snarf buffer, private to the set of
+applications running under the current instance of
+.IR wm (1).
+.SH FILES
+.TP
+.B /lib/wmsetup
+Initialisation shell-script.
+.SH SOURCE
+.B /appl/wm/toolbar.b
+.SH "SEE ALSO"
+.IR wm(1),
+.IR tkclient (2),
+.IR wmclient (2),
+.IR toolbar (1),
+.IR logon (1).
--- /dev/null
+++ b/man/1/touch
@@ -1,0 +1,27 @@
+.TH TOUCH 1
+.SH NAME
+touch \- update the modification time of one or more files
+.SH SYNOPSIS
+.B touch
+.RB [ -c ]
+[
+.BI -t " time"
+]
+.I files
+.SH DESCRIPTION
+.B Touch
+attempts to set the modification time of the specified files to
+.I time
+(by default, the current system time).
+If a file does not exist,
+.B touch
+will attempt to create it, unless the
+.B -c
+option is given.
+.SH SOURCE
+.B /appl/cmd/touch.b
+.SH SEE ALSO
+.IR ls (1),
+.IR chmod (1),
+.IR sys-stat (2),
+.IR stat (5)
--- /dev/null
+++ b/man/1/tr
@@ -1,0 +1,95 @@
+.TH TR 1
+.SH NAME
+tr \- translate characters
+.SH SYNOPSIS
+.B tr
+[
+.B -cds
+]
+[
+.I string1
+[
+.I string2
+]
+]
+.SH DESCRIPTION
+.I Tr
+copies the standard input to the standard output with
+substitution or deletion of selected characters (runes).
+Input characters found in
+.I string1
+are mapped into the corresponding characters of
+.IR string2 .
+When
+.I string2
+is short it is padded to the length of
+.I string1
+by duplicating its last character.
+Any combination of the options
+.B -cds
+may be used:
+.TP
+.B -c
+Complement
+.IR string1 :
+replace it with a lexicographically ordered
+list of all other characters.
+.TP
+.B -d
+Delete from input all characters in
+.IR string1 .
+.TP
+.B -s
+Squeeze repeated output characters that occur in
+.I string2
+to single characters.
+.PP
+In either string a noninitial sequence
+.BI - x\f1,
+where 
+.I x
+is any character (possibly quoted), stands for
+a range of characters:
+a possibly empty sequence of codes running from
+the successor of the previous code up through
+the code for
+.IR x .
+The character
+.L \e
+followed by 1, 2 or 3 octal digits stands for the
+character whose
+16-bit
+value is given by those digits.
+The character sequence
+.L \ex
+followed by 1 to 6 hexadecimal digits stands
+for the character whose
+21-bit value is given by those digits.
+A 
+.L \e
+followed by any other character stands
+for that character.
+.SH EXAMPLES
+Replace all upper-case
+.SM ASCII
+letters by lower-case.
+.IP
+.EX
+tr A-Z a-z <mixed >lower
+.EE
+.PP
+Create a list of all
+the words in
+.L file1
+one per line in
+.LR file2 ,
+where a word is taken to be a maximal string of alphabetics.
+.I String2
+is given as a quoted newline.
+.IP
+.EX
+tr -cs A-Za-z '
+\&' <file1 >file2
+.EE
+.SH SOURCE
+.B /appl/cmd/tr.b
--- /dev/null
+++ b/man/1/tsort
@@ -1,0 +1,26 @@
+.TH TSORT 1
+.SH NAME
+tsort \- topological sort
+.SH SYNOPSIS
+.B tsort
+.SH DESCRIPTION
+.I Tsort
+reads a set of partial order relations between labels (sequences of non-space characters)
+from its standard input,
+and lists the labels on its standard output one per line following a topological sort.
+Each input line represents a set of inequalities: the first label on the line is less than
+all the others on the same line, and should appear earlier
+in sorted order.
+(The relation might for instance represent arcs in a directed graph, from
+the first label on a line to the others, or dependency relationships.)
+Labels on a line are separated by space or tab.
+.SH DIAGNOSTICS
+If the input contains cycles,
+.I tsort
+prints a diagnostic on standard error for each cycle, listing its members.
+The members of each cycle will also appear on the standard output, in any order,
+but after any predecessors outside the cycle.
+.SH SOURCE
+.B /appl/cmd/tsort.b
+.SH SEE ALSO
+.IR sort (1)
--- /dev/null
+++ b/man/1/unicode
@@ -1,0 +1,62 @@
+.TH UNICODE 1
+.SH NAME
+unicode \- interpret Unicode characters
+.SH SYNOPSIS
+.B unicode
+[
+.B -nt
+]
+.IB hexmin - hexmax
+.PP
+.B unicode
+[
+.B -t
+]
+.I hex ...
+.PP
+.B unicode
+[
+.B -n
+]
+.I char ...
+.SH DESCRIPTION
+.I Unicode
+converts between UTF and character values from the Unicode Standard (see
+.IR utf (6)).
+.PP
+If given a range of hexadecimal numbers,
+.I unicode
+prints a table of the specified Unicode characters including their values and UTF representations.
+Otherwise, it translates from UTF to numeric value or numeric value to UTF, depending on the appearance of the supplied text.
+If converting to UTF, the characters are printed one per line.
+.PP
+The options are:
+.TP
+.B -n
+Forces numeric output to avoid ambiguity with numeric characters.
+.TP
+.B -t
+Output a single string containing the specified characters, rather than one per line.
+.PP
+The output of
+.I unicode
+might not be helpful if the characters printed are not available in the current font.
+.SH EXAMPLES
+.TP
+.B "unicode p"
+Print the hex value of
+.BR p .
+.TP
+.B "unicode 2200-22f1"
+Print a table of miscellaneous mathematical symbols.
+.SH FILES
+.TF /lib/unicode
+.TP
+.B /lib/unicode
+Table of characters and descriptions.
+.SH SOURCE
+.B /appl/cmd/unicode.b
+.SH "SEE ALSO"
+.IR tr (1),
+.IR utf (6),
+.IR font (6)
--- /dev/null
+++ b/man/1/uniq
@@ -1,0 +1,33 @@
+.TH UNIQ 1
+.SH NAME
+uniq \- report repeated lines in a file
+.SH SYNOPSIS
+.B uniq
+[
+.B -ud
+]
+[
+.I file
+]
+.SH DESCRIPTION
+.I Uniq
+copies the input
+.IR file ,
+or the standard input, to the
+standard output, comparing adjacent lines.
+In the normal case, the second and succeeding copies
+of repeated lines are
+removed.
+Repeated lines must be adjacent
+in order to be found.
+.TP
+.B -u
+Print unique lines.
+.TP
+.B -d
+Print (one copy of) duplicated lines.
+.SH SOURCE
+.B /appl/cmd/uniq.b
+.SH "SEE ALSO"
+.IR comm (1),
+.IR sort (1) 
--- /dev/null
+++ b/man/1/units
@@ -1,0 +1,110 @@
+.TH UNITS 1
+.if n .ds / /
+.SH NAME
+units \- conversion program
+.SH SYNOPSIS
+.B units
+[
+.B -v
+]
+[
+.I file
+]
+.SH DESCRIPTION
+.I Units
+converts quantities expressed
+in various standard scales to
+their equivalents in other scales.
+It works interactively in this fashion:
+.IP
+.EX
+you have: inch
+you want: cm
+    * 2.54
+	/ 0.3937008
+.EE
+.PP
+A quantity is specified as a multiplicative combination
+of units and floating point numbers.
+Operators have the following precedence:
+.IP
+.EX
+.ta \w'\fLXXXXXXXXXXXXXXX'u
+\fL+\fP \fL-\fP	\f1add and subtract
+\fL*\fP \fL/\fP \fL×\fP \fL÷\fP	\f1multiply and divide
+catenation	multiply
+\fL²\fP \fL³\fP \fL^\fP	\f1exponentiation
+\fL|\fP	\f1divide
+\fL(\fP ... \fL)\fP	\f1grouping
+.EE
+.PP
+Most familiar units,
+abbreviations, and metric prefixes are recognized,
+together with a generous leavening of exotica
+and a few constants of nature including:
+.IP
+.de fq
+\fL\\$1\\fP	\\$2 \\$3 \\$4 \\$5 \\$6
+..
+.ta \w'\fLwaterXXX'u
+.nf
+.fq pi,\f1π\fP ratio of circumference to diameter
+.fq c speed of light
+.fq e charge on an electron
+.fq g acceleration of gravity
+.fq force same as \fLg\fP
+.fq mole Avogadro's number
+.fq water "pressure head per unit height of water"
+.fq au astronomical unit
+.fi
+.PP
+The
+.L pound
+is a unit of
+mass.
+Compound names are run together, e.g.
+.LR lightyear .
+British units that differ from their US counterparts
+are prefixed thus:
+.LR brgallon .
+Currency is denoted
+.LR belgiumfranc ,
+.LR britainpound ,
+etc.
+.PP
+The complete list of units can be found in
+.BR /lib/units .
+A
+.I file
+argument to
+.I units
+specifies a file to be used instead of
+.BR /lib/units.
+The
+.B -v
+flag causes
+.I units
+to print its entire database.
+.SH EXAMPLE
+.EX
+you have: 15 pounds force/in²
+you want: atm
+	* 1.020689
+	/ 0.9797299
+.EE
+.SH FILES
+.B /lib/units
+.SH SOURCE
+.B /appl/cmd/units.y
+.br
+.B /appl/cmd/units.b
+.SH BUGS
+Since
+.I units
+does only multiplicative scale changes,
+it can convert Kelvin to Rankine but not Centigrade to
+Fahrenheit, except that the latter is handled as a special case.
+.br
+Currency conversions are only as accurate as the last time someone
+updated
+.BR /lib/units .
--- /dev/null
+++ b/man/1/uuencode
@@ -1,0 +1,79 @@
+.TH UUENCODE 1 
+.SH NAME
+uuencode, uudecode \- encode/decode a file
+.SH SYNOPSIS
+.B uuencode
+[
+.I sourcefile
+] 
+.I remotefile
+.PP
+.B uudecode
+[
+.B -p
+]
+[
+.I encodedfile ...
+]
+.SH DESCRIPTION
+.I Uuencode
+and
+.I Uudecode
+are used to transmit files over transmission mediums that do not support other than simple
+ASCII data.
+.PP
+.I Uuencode
+converts a file to a purely ASCII based representation. It encodes the contents of
+.I sourcefile
+or the standard input if no source file is given. The 
+.I remotefile
+is included in the encoded file's header  as the name of the file into which
+.I uudecode
+should place the decoded data. The header also includes the permission modes of the
+source file so that these  can be preserved on decoding. The encoded output of
+.I uuencode
+is sent to the standard output.
+.PP
+.I Uudecode
+reads a file, ignoring any leading and trailing lines that are not part of the encoding, and
+recreates the original file with the filename and mode specified in it's header. The file
+to decode is 
+.I encodedfile
+or standard input if none is given. The 
+.B -p
+flag can be used to send the decoded data to standard output rather than saving it in
+the file whose name is specified in the header.
+.SH EXAMPLES
+.PP
+Encode a dis file limbo.dis so that it can be included in a mail message:
+.IP
+.EX 
+uuencode limbo.dis limbo.dis > tmp
+<place in mail message and send to recipient>
+.EE
+.PP
+Decode the mail message(msg say):
+.IP
+.EX
+cat msg | uudecode
+.EE
+.PP
+This creates the file limbo.dis.
+.PP
+Decode the mail message into a file of your choosing(tmp.dis say):
+.IP
+.EX
+cat msg | uudecode -p > tmp.dis
+.EE
+.SH SOURCE
+.B /appl/cmd/uuencode.b
+.br
+.B /appl/cmd/uudecode.b
+.SH BUGS
+The encoded file is expanded by at least a third.
+.br
+Decoding a file may overwrite an existing file.
+.br
+Uuencode should take the remote file name to be the same as the source file if one
+is not given.
+
--- /dev/null
+++ b/man/1/vacget
@@ -1,0 +1,103 @@
+.TH VACGET 1
+.SH NAME
+vacget, vacput \- venti archive utilities
+.SH SYNOPSIS
+.B vacget
+[
+.B -vdpt
+] [
+.B -a
+.I addr
+]
+.I vac:score
+.br
+.B vacput
+[
+.B -vd
+] [
+.B -i
+|
+.B -x
+] [
+.B -a
+.I addr
+] [
+.B -b
+.I blocksize
+] [
+.B -n
+.I name
+] [
+.B -u uid
+] [
+.B -g gid
+]
+.I path ...
+.SH DESCRIPTION
+.I Vacget
+retrieves a venti archive from a venti server to the current working directory.
+.PP
+.I Vacput
+writes a venti archive to a venti server.  The
+.I paths
+are walked recursively and all files and directories written to the archive.  Temporary files, i.e. those with the
+.B DMTMP
+bit set, are skipped.  Writing only changed files relative to a previously written archive is not implemented.
+.TP
+.B -d
+Print debug messages.
+.TP
+.B -p
+Try to preserve file permissions and owner/group.  Only for vacget.
+.TP
+.B -v
+Be verbose.  Prints files as they are being retrieved or written.
+.TP
+.B -t
+List files, do not write them.  Only for vacget.
+.TP
+.BI -a " address"
+Dial
+.I address
+instead of the default venti server.
+.TP
+.BI -b " blocksize"
+Use blocks with
+.I blocksize
+bytes instead of the default 8192 byte blocks.  Only for vacput.
+.TP
+.BI -n " name"
+Use
+.I name
+as the name in the root block.  Only for vacput.
+.TP
+.BR -i " or " -x
+Read a list of files from stdin to include
+.RB ( -i )
+or exclude
+.RB ( -x )
+from the (recursively walked)
+.I paths
+specified on the command-line.
+Only for vacput.
+.TP
+.BI -u " uid"
+Use
+.I uid
+for all files.
+Only for vacput.
+.TP
+.BI -g " gid"
+Use
+.I gid
+for all files.
+Only for vacput.
+.SH SOURCE
+.B /appl/cmd/vacget.b
+.br
+.B /appl/cmd/vacput.b
+.SH SEE ALSO
+.IR venti (2),
+.IR vacfs (4)
+.SH BUGS
+These tools need more testing.
--- /dev/null
+++ b/man/1/wc
@@ -1,0 +1,29 @@
+.TH WC 1
+.SH NAME
+wc \- count lines, words, and characters
+.SH SYNOPSIS
+.B wc
+[
+.B -lwceb
+]
+[
+.I file ...
+]
+.SH DESCRIPTION
+.I Wc
+writes to standard output a tally of lines, words, and characters found in each
+.IR file ,
+assumed to be text in UTF format.
+If no files are named, standard input is read. One line is output per file. If several files are specified, an additional line is written giving totals.
+.PP
+`Words' are maximal sequences of characters separated by blanks, tabs and newlines.
+.PP
+Counts are output in the same order as the listing of the option letters
+.BR lwceb ;
+select lines, words, UTF characters, erroneously-encoded characters, and bytes, respectively.
+If no options are given, lines, words and characters are counted.
+.SH SOURCE
+.B /appl/cmd/wc.b
+.SH "SEE ALSO"
+.IR tr (1),
+.IR utf (6)
--- /dev/null
+++ b/man/1/webgrab
@@ -1,0 +1,141 @@
+.TH WEBGRAB 1
+.SH NAME
+webgrab \- fetch web page content as files
+.SH SYNOPSIS
+.B webgrab
+[
+.B -r
+] [
+.B -v
+] [
+.BI -o " stem"
+] [
+.BI -p " body"
+]
+.I url
+.SH DESCRIPTION
+.I Webgrab
+connects to the web server named in the
+.IR url .
+It fetches the content of the web page also determined by the
+.IR url ,
+and stores it locally in a file.
+If the page is written in HTML,
+.I webgrab
+reads it to build a list of sub-component pages (eg, frames) and images.
+It fetches those, saving the content in separate files.
+It adds a comment to the end of each HTML file giving the time, and the file's origin.
+It automatically follows redirections offered by the server.
+.PP
+The
+.I stem
+of the names of the output files is normally derived from a component of the
+.IR url .
+If the
+.I url
+contains a path name, the
+.I stem
+is the component of that path, less any dot-separated suffix and prefix.
+For example, given
+.IP
+.BR http://www.vitanuova.com/inferno/old.index.html
+.PP
+the stem would be
+.BR index .
+If there is no path name, but the
+.I url
+contains a domain name, the
+.I stem
+is the penultimate component of the domain name (eg, excluding
+trailing
+.BR .com ,
+and initial
+.BR www ,
+etc).
+For example, given
+.IP
+.B www.innerhost.vitanuova.com
+.PP
+the stem would be
+.BR vitanuova .
+If all else fails,
+.I webgrab
+uses the
+.I stem
+.BR webgrab .
+.PP
+Given a
+.IR stem ,
+the initial page is stored in
+.IB stem . suffix
+where
+.I suffix
+is the suffix (eg,
+.BR .html )
+of the name of the original page.
+Subordinate pages are saved in a similar way in files named
+.IB stem _1. suffix1,
+.IB stem _2. suffix2,
+\&... .
+.PP
+The options are:
+.TP
+.B -r
+do not fetch subcomponents (just the `raw' source of
+.I url
+itself)
+.TP
+.B -v
+print a progress report
+.TP
+.B -vv
+print a chatty progress report
+.TP
+.BI -o " stem"
+use the
+.I stem
+as given
+.TP
+.BI -p " body"
+Use HTTP
+.B POST
+instead of
+.BR GET ,
+posting
+.I body
+as the data
+.PP
+.I Webgrab
+reads the
+configuration file
+.B /services/webget/config
+(if it exists),
+to look for the address of an optional HTTP proxy
+(in the
+.L httpproxy
+entry), and list of domains for which a proxy should not be used
+(in the
+.B noproxy
+or
+.B noproxydoms
+entry). If symbolic network and service names might be involved, the
+connection server
+.B lib/cs
+needs to be already running.
+.SH FILES
+.B /services/webget/config
+.SH SOURCE
+.B /appl/cmd/webgrab.b
+.SH BUGS
+It should read the proxy name from the
+.IR charon (1)
+configuration file and not the
+.I webget
+configuration file.
+.br
+It cannot do `secure' transfers
+.RB ( https ).
+.br
+Its HTML parsing is naive, but on the other hand, it is less likely to trip over HTML novelties.
+.SH "SEE ALSO"
+.IR cs (8)
--- /dev/null
+++ b/man/1/wish
@@ -1,0 +1,86 @@
+.TH WISH 1
+.SH NAME
+wish \- interface to the Tk graphics toolkit
+.SH SYNOPSIS
+.B wish
+[
+.I file ...
+]
+.PP
+.B wm/wish
+[
+.B -k
+]
+[
+.BI -f " file"
+]
+.SH DESCRIPTION
+.I Wish
+provides a Tcl
+command line interface to the Limbo/Tk graphics toolkit.
+The input should be in the format of Tcl or Tk commands
+(see
+.IR intro (9)).
+There are two variants.
+.PP
+Plain
+.B wish
+must be run from the Inferno console instead of
+.IR mux (1)
+or
+.IR wm (1).
+It takes input from each
+.I file
+in turn, then prompts for further commands from the standard input.
+Its commands operate on a Tk toplevel covering the whole screen.
+.PP
+.BR Wm/wish
+runs instead in a shell window under
+.IR wm (1).
+It reads initial input from the optional
+.IR file ,
+then prompts for further commands on its standard input unless
+the
+.B -k
+option is given.
+Its commands operate on the Tk toplevel of a new window
+created by
+.B Wmlib->titlebar
+(see
+.IR wmlib (2)),
+initially titled
+.BR WishPad .
+.PP
+The following special input sequences are recognised:
+.TP
+.B \en
+newline
+.TP
+.B \et
+tab
+.TP
+.B \eb
+backspace
+.TP
+.B \e\e
+backslash
+.SH FILES
+.B /dev/pointer
+.br
+.B /dev/keyboard
+.SH SOURCE
+.B /appl/cmd/wish.b
+.br
+.B /appl/wm/wish.b
+.SH "SEE ALSO"
+.IR sh-tk (1),
+.IR tkcmd (1),
+.IR tk (2),
+.IR intro (9)
+.SH BUGS
+Arguably this has been superseded by
+.IR sh-tk (1)
+for scripting Tk applications,
+and
+.IR tkcmd (1)
+for development and testing of Tk configurations for use by Limbo programs.
--- /dev/null
+++ b/man/1/wm
@@ -1,0 +1,216 @@
+.TH WM 1
+.SH NAME
+wm \- window manager
+.SH SYNOPSIS
+.B wm/wm
+[
+.I command
+[
+.IR arg ...
+]
+]
+.SH DESCRIPTION
+.I Wm
+is an inferno window manager, providing the control mechanism for
+the user to manage to order and position of a dynamic collection
+of application windows.
+When started, it runs
+.IR command ,
+(by default
+.BR wm/toolbar ,
+see
+.IR toolbar (1))
+By itself,
+.I wm
+does not provide any means for starting new applications;
+that facility must be provided by an auxilliary program,
+.B wm/toolbar
+being one such example.
+.SS Control Interface
+.I Wm
+provides a control interface to programs running inside it
+through the
+.B Wmcontext
+adt that can be obtained from the
+.B Draw->Context
+that it passes to applications that it starts
+(see
+.IR draw-context (2)).
+Control messages it understands include:
+.TP
+.B start \fIwhat\fP
+Start input on
+.IR what .
+.I What
+can be
+.BR ptr 
+(pointer events),
+.BR kbd
+(keyboard events)
+and
+.BR control
+(window manager control events, see below).
+.TP
+.B key \fIcharval\fP
+Simulate a key event.
+.I Charval
+is the decimal value of the character that has been
+pressed. The character will be sent exactly as if it
+had been typed on the keyboard. This facility
+is used by
+.IR keyboard (1).
+.TP
+.B !reshape \fItag\fP \fIreqid\fP \fIminx miny maxx maxy\fI \fR[\fIhow\fR]
+Reshape or create the window named
+.IR tag .
+.I Reqid
+is ignored;
+.I minx..maxy
+give the desired bounding rectangle of the
+new window.
+If
+.I how
+is not given, or is
+.BR exact ,
+then
+.I wm
+will attempt to satisfy the request exactly.
+Otherwise,
+.I how
+tells
+.I wm
+how to determine the rectangle of the resulting image;
+it can be one of
+.B place
+(choose some appropriate position and size on screen),
+.B onscreen
+(modify the requested rectangle only so as to bring
+it on screen), or
+.B max
+(request the maximum available rectangle).
+.TP
+.B delete \fItag\fP
+Delete the window named by \fItag\fP.
+.TP
+.B raise
+Raise all windows for the current context above the others.
+.TP
+.B lower
+Send all windows for the current context to the bottom.
+.TP
+.B !move \fItag\fP \fIreqid\fP \fIstartx\fP \fIstarty\fP
+Drag window
+.I tag
+interactively.
+.I Reqid
+is ignored.
+.I Startx
+and
+.I starty
+give the location of the pointer when the drag was initiated.
+.TP
+.B !size \fItag\fP \fIreqid\fP
+Interactively resize window \fItag\fP.
+.TP
+.B fixedorigin
+By default, if a window changes position but not size,
+.I wm
+changes the origin of the window without creating a new image.
+Sending
+.B fixedorigin
+caused
+.I wm
+always to create a new image in that case.
+.TP
+.B kbdfocus \fR[\fIin\fR]
+If the decimal integer
+.I in
+is non-zero, request the keyboard focus,
+otherwise lose the keyboard focus.
+.PP
+.I Wm
+generates control messages to inform applications of
+things that have happened. These include:
+.TP
+.B rect \fIminx miny maxx maxy\fP
+The screen rectangle has changed.
+.I "minx..maxy"
+gives the new bounding box of the screen.
+.TP
+.B haskbdfocus \fIin\fP
+Informs an application of its current keyboard focus state.
+This message is generated in response to pointer events,
+and due to
+.B kbdfocus
+requests.
+.TP
+.B exit
+The window manager is closing down.
+.SS Controlling Application
+The first application that starts under
+.I wm
+is given the privilege of being able to control
+other applications running under the same
+.IR wm .
+If it sends a
+.B start control
+message, then it will also see any control requests
+sent by applications that
+.I wm
+itself does not understand and information about applications
+starting and leaving.
+.I Wm
+accepts several other control messages from its
+controlling application:
+.TP
+.B ctl \fIid\fP \fImsg\fP
+Send message
+.I msg
+to application
+.IR id .
+.TP
+.B endcontrol
+Relinquish controller status.
+The next application that starts will get control status.
+This is used, for instance, to segue smoothly between
+.IR logon (1)
+and
+.IR toolbar (1).
+.PP
+If the controlling application has started control messages,
+.I wm
+sends it the following messages:
+.TP
+.B newclient \fIid\fP
+A new client has connected, identified by
+.IR id .
+.TP
+.B delclient \fIid\fP
+Client
+.I id
+has left.
+.TP
+.B request \fIid\fP \fImsg\fP
+Client
+.I id
+sent the request
+.I msg
+to the window manager, which it did not understand.
+This facility is used, for instance, by
+.IR toolbar (1)
+to implement the
+.B task
+and
+.B untask
+requests.
+.SH FILES
+.TP
+.B /chan/wmrect
+File holding current screen rectangle.
+.SH SOURCE
+.B /appl/wm/wm.b
+.SH "SEE ALSO"
+.IR toolbar (1),
+.IR logon (1),
+.IR tkclient (2),
+.IR wmclient (2)
--- /dev/null
+++ b/man/1/wm-misc
@@ -1,0 +1,247 @@
+.TH WM-MISC 1
+.SH NAME
+about, clock, coffee, colors, date, edit, mand, memory, polyhedra, reversi, rt, stopwatch, sweeper, task, tetris, unibrowse, view, winctl \- miscellaneous graphical applications
+.SH SYNOPSIS
+.B wm/about
+.br
+.B wm/clock
+.br
+.B wm/coffee
+.br
+.B wm/colors
+.br
+.B wm/date
+.br
+.B wm/edit
+.RI [ file ]
+.br
+.B wm/mand
+.br
+.B wm/memory
+.br
+.B wm/polyhedra
+.br
+.B wm/reversi
+.br
+.B wm/rt
+.br
+.B wm/stopwatch
+.br
+.B wm/sweeper
+.br
+.B wm/task
+.br
+.B wm/tetris
+[
+.B -b
+.I blocksize
+]
+.br
+.B wm/unibrowse
+.br
+.B wm/view
+[
+.B -i
+]
+.RI [ file... ]
+.br
+.B wm/winctl
+.SH DESCRIPTION
+A collection of simple applications and utilities that operate under the
+Wm window manager.
+Other Wm applications exist, see their respective manual pages for
+more information.
+.PP
+.TP
+.B wm/about
+Display system version and copyright information.
+.TP
+.B wm/clock
+Display an analogue clock.
+.TP
+.B wm/coffee
+A whimsical plaything.
+.TP
+.B wm/colors
+Displays the Inferno palette.
+Clicking on a particular colour displays its RGB values.
+.TP
+.B wm/date
+Displays the current date and time in a window.
+.TP
+.B wm/edit
+A simple cut-and-paste text editor.
+Several menus provide the usual editing commands.
+Text selections are dragged out using mouse button 1.
+Mouse button-2 displays a pop-up menu of the Cut, Copy and Paste commands.
+.TP
+.B wm/mand
+A fractal browser to explore the Mandelbrot and Julia sets.
+Button 1 drags a rectangle to zoom into, button 2 shows the  Julia set at the chosen point, button 3 zooms out.
+To produce more accurate pictures, the iteration depth may be increased by altering 
+the depth scale factor. The default number of iterations per point is 253. The sets are
+plotted by filling regions of (apparently) the same colour. Deselecting the fill option
+will plot the points in the usual fashion.
+.TP
+.B wm/memory
+Displays memory usage.
+Three usage bars are displayed, one for each of the Inferno memory
+pools: main, heap and image. The current usage (in bytes) is displayed
+to the left of each bar, and the number of blocks in use appears in red.
+The maximum permitted size of each pool is given (in megabytes) to the right of its
+usage bar.
+Each bar also sports a highwater mark.
+The usage data is re-read and displayed once every second.
+.TP
+.B wm/polyhedra
+A program to display convex regular polyhedra. The menu options allow the user
+to alter the speed of rotation and the axis of rotation. To display a different
+solid, move forward or back with the prev and next boxes. Selecting the dual
+box will show the dual of a solid rather than the original solid. Finally the 
+edges, clear and faces boxes determine whether edges are shown, whether the
+screen is cleared before the next plot and whether faces are shown respectively.
+.TP
+.B wm/reversi
+An implementation of the popular game. The default set up is for black to be the
+machine and white the human player. Use the Black and White menu options to
+change this. The level of any machine player may be set using the Black level
+and White level boxes. This determines the amount of lookahead performed
+by the tree search algorithm.
+.TP
+.B wm/rt
+A Dis module inspector: it can show the Dis instructions, strings,
+types and other attributes of a module; it also allows the user
+to set some attributes stored in a module's header.
+.TP
+.B wm/stopwatch
+A simple-minded stopwatch.
+Only useful for coarse-grained timings.
+.TP
+.B wm/sweeper
+Mine sweeping game.
+.TP
+.B wm/task
+Task manager: it lists the processes running when it starts,
+and offers buttons to kill a selected process, kill its process group,
+show its open files, or debug it using
+.IR deb (1).
+A process is selected from the list using mouse button 1.
+.I Task
+does not automatically refresh the list; there is a
+.B Refresh
+button to prompt it to do so.
+.TP
+.B wm/tetris
+The ubiquitous and annoyingly addictive tile dropping game.
+The game keys are:
+.RB ` 7 '
+move left;
+.RB ` 8 '
+rotate (anti-clockwise);
+.RB ` 9 '
+move right;
+.RB ` p '
+pause;
+.RI ` space '
+drop and
+.RB ` q '
+quit.
+A mouse or stylus can also be used to guide the pieces (eg, by tapping the screen in the desired direction).
+Scores are stored in the file
+.BR /lib/scores/tetris .
+Score file updates are not interlocked \- it's only a game!
+.TP
+.B wm/unibrowse
+A handy utility for browsing the unicode character set, finding out what
+particular characters look like in different fonts, finding out exactly which
+characters a font provides, and finding the name of a character
+that you have managed to grab into the snarf buffer.
+.TP
+.B wm/view
+Image viewer.
+Displays GIF, Inferno
+.IR image (6),
+JPEG,
+PNG
+and X bitmap image files.
+The viewer creates a new window to display the contents of each
+.IR file .
+If no arguments are given, the file browser panel
+.IR filename (1)
+is displayed to prompt the user to select an image file to view.
+If the
+.B -i
+option is given,
+.I view
+continues to listen for requests from the
+.IR plumber (8);
+the
+.B -i
+option will normally appear only in rules in
+.IR plumbing (6)
+files.
+.TP
+.B wm/winctl
+Window management tool.
+Displays a set of buttons that provide for:
+raising a window to the top or lowering it to the bottom of the
+screen window stack; moving a window to a new position; iconising a window;
+deleting a window.
+.IP
+Click on the button for the required action then click on the window to apply it to.
+When moving a window, click and drag the target.
+Deleting a window is error-prone. Currently using this tool on a charon or acme
+window has strange effects.
+.SH PLUMBING
+.B wm/view
+receives
+.B view
+messages
+.SH FILES
+.TP
+.B /lib/polyhedra
+Polyhedra data base.
+.TP
+.B /lib/scores/tetris
+Tetris high score table.
+.TP
+.B /lib/unidata
+Directory holding Unicode character set information, used by
+.BR unibrowse .
+.TP
+.B /dev/memory
+Provides
+.B memory
+with memory usage statistics.
+.SH SOURCE
+.B /appl/wm/about.b
+.br
+.B /appl/wm/coffee.b
+.br
+.B /appl/wm/colors.b
+.br
+.B /appl/wm/date.b
+.br
+.B /appl/wm/edit.b
+.br
+.B /appl/wm/mand.b
+.br
+.B /appl/wm/memory.b
+.br
+.B /appl/wm/polyhedra.b
+.br
+.B /appl/wm/reversi.b
+.br
+.B /appl/wm/rt.b
+.br
+.B /appl/wm/stopwatch.b
+.br
+.B /appl/wm/task.b
+.br
+.B /appl/wm/tetris.b
+.br
+.B /appl/wm/unibrowse.b
+.br
+.B /appl/wm/view.b
+.br
+.B /appl/wm/winctl.b
--- /dev/null
+++ b/man/1/wm-sh
@@ -1,0 +1,274 @@
+.TH WM-SH 1
+.SH NAME
+sh, mash \- Window frames for the Inferno shells
+.SH SYNOPSIS
+.B wm/sh
+[
+.B -w
+.I width
+] [
+.B -h
+.I height
+] [
+.B -f
+.I font
+]
+.I sh-args
+.br
+.B wm/mash
+.I mash-args
+.br
+.SH DESCRIPTION
+Both
+.B wm/sh
+and
+.B wm/mash
+provide a graphical framework to their respective shells.
+Both wrappers manage the input and output of the shell.
+They provide facilities for scrolling and editing the output buffer
+and for constructing input to be sent to the shell.
+.PP
+.B Wm/sh
+invokes the shell
+.IR sh (1)
+with the arguments
+.B -n
+.IR sh-args ;
+.B wm/mash
+invokes
+.IR mash (1)
+with the arguments
+.IR mash-args .
+.B Wm/sh
+accepts the following additional options, which are
+not passed through to
+.IR sh :
+.HP
+.B -w
+.I width
+.br
+The window should be at least
+.I width
+pixels wide.
+.HP
+.B -h
+.I height
+.br
+The window should be at least
+.I height
+pixels high.
+.HP
+.B -f
+.I font
+.br
+Specify the font to use in the window.
+.I Font
+should be the name of a valid
+.IR font (6)
+file.
+.PP
+.B Wm/sh
+and
+.B wm/mash
+both provide their own versions of
+.B /dev/cons
+and
+.B /dev/consctl
+files in the namespace of the invoked shell
+(see
+.IR cons (3)
+for the originals) and attach the standard input
+of the invoked shell to the virtualised
+.B /dev/cons
+file.
+Output from the shell, or of any commands run by the shell, is displayed in
+a scrollable text window, appearing at the
+.IR "output position" ,
+which is at the end of any previously output text, before
+any as-yet-unread user input text.
+.PP
+Any text displayed on the console can be edited.
+Typed text is always inserted at the position of the input cursor.
+The input cursor can be moved to any point in the text by
+clicking mouse button-1 at the desired position.
+Selections can be made by dragging the mouse with button-1 held down.
+Typing into a selection copies its text to the Snarf buffer, the
+selected text is deleted and the typed character inserted.
+.PP
+Text typed beyond the output point will be made available
+to commands reading from
+.BR /dev/cons .
+Normally this text is made available when newline is typed,
+but typing ESC turns on
+.I hold
+mode (the text turns blue), deferring the availability of the text until ESC is
+typed again, turning hold mode off. This allows simple
+multi-line editing of the standard input to a command.
+.PP
+Writing
+.B rawon
+to
+.B /dev/consctl
+changes the above behaviour, making each character typed beyond
+the output point available to commands as soon as it is typed;
+the character is not automatically echoed. Writing
+.B rawoff
+to
+.B /dev/consctl
+reverses this behaviour.
+.PP
+In addition to dragging out selections, they can be made by double clicking
+mouse button-1.
+Double clicking over a word selects the whole word.
+Double clicking next to a brace or bracket selects the text between it and its matching
+brace or bracket.
+If there is no match then no selection is made.
+.PP
+Clicking mouse button-2 displays a pop-up menu of editing commands:
+.TP
+.B Cut
+Copy the current selection to the Snarf buffer and then delete
+the selected text.
+This command has no effect if there is no selected text, the Snarf buffer
+is not cleared.
+.TP
+.B Paste
+When there is no text selected, the contents of the Snarf buffer are
+inserted at the current input cursor.
+If a selection exits, its text is replaced by that of the Snarf buffer.
+The new text is then selected.
+The contents of the Snarf buffer remain unaltered.
+.TP
+.B Snarf
+Copy the selected text to the Snarf buffer.
+This command has no effect if there is no selected text.
+.TP
+.B Send
+If there is any text selected it is copied to the Snarf buffer.
+The contents of the Snarf buffer is then
+appended to the end of the current shell input line, forwarding
+any NewLine completed lines to the shell's input stream.
+.PP
+Mouse chording is implemented as in
+.IR acme (1).
+Dragging a selection with button-1 held down and then also clicking
+button-2 cuts the selected text into the Snarf buffer.
+Clicking button-3 instead of button-2 replaces the selected text with the contents
+of the Snarf buffer.
+.PP
+Clicking mouse button-3 plumbs the word or selection under the click point.
+See
+.IR plumber (8)
+for more information on plumbing.
+.PP
+.I Wm/sh
+also serves the file
+.BR /chan/shctl .
+The following commands may be written to this file:
+.HP
+.B cwd
+.I dir
+.br
+Causes any plumbing request generated by
+.I wm/sh
+to be created with
+.I dir
+as its ``current directory''. This is shown in the
+title bar of the window.
+Note that it is up to the command running inside
+.I wm/sh
+to keep this up to date (for instance, see EXAMPLES,
+below).
+.HP
+.B button
+.I "title sendtext"
+.br
+A Tk button is created at the top of the shell window, labeled
+with
+.IR title .
+When activated,
+.I sendtext
+will be sent to the shell window as if it had been typed.
+.HP
+.B action
+.I "title sendtext"
+.br
+A button is created as for the
+.B button
+command, except that activation of the button causes
+.I sendtext
+to be sent to any process reading from
+.BR /chan/shctl .
+.HP
+.B clear
+.br
+Delete any buttons that have been created.
+.PP
+Arguments to commands sent to
+.B /chan/shctl
+follow
+.IR sh (1)
+quoting rules (the same as implemented by
+.I quoted
+and
+.I unquoted
+in
+.IR string (2)).
+A process reading from
+.B /chan/shctl
+will block until an
+.B action
+button is activated, whereupon it will yield the
+.I sendtext
+associated with the button.
+.SH PLUMBING
+Both
+.B wm/sh
+and
+.B wm/mash
+plumb text selected by button 3;
+an empty selection plumbs the white-space bounded text
+surrounding the selection.
+.SH EXAMPLES
+Define a
+.IR sh (1)
+function to update the current directory automatically:
+.PP
+.EX
+	fn cd {builtin cd $*; echo cwd `{pwd} >/chan/shctl}
+.EE
+.PP
+Note that this will not work in all cases, as it is possible to
+change the current directory without using the
+.I cd
+command.
+.PP
+Create a button to automate a
+.I mount
+command (note the newline in the argument string):
+.PP
+.EX
+	echo ${quote button mount 'mount kremvax /n/remote
+	'} > /chan/shctl
+.EE
+.PP
+Create a new
+.I wm/sh
+window with the above button already created:
+.PP
+.EX
+	wm/sh -ic {
+		echo ${quote button mount 'mount kremvax /n/remote
+	'} > /chan/shctl
+	}
+.EE
+.SH SOURCE
+.B /appl/wm/sh.b
+.br
+.B /appl/wm/mash.b
+.br
+.SH "SEE ALSO"
+.IR sh (1),
+.IR mash (1),
+.IR wm (1),
+.IR plumber (8)
--- /dev/null
+++ b/man/1/xd
@@ -1,0 +1,92 @@
+.TH XD 1 
+.SH NAME
+xd \- dump file contents in multiple formats
+.SH SYNOPSIS
+.B xd
+[
+.I option
+\&... ] [
+.BI - format
+\&... ] [
+.IR file ...
+]
+.SH DESCRIPTION
+.B Xd
+dumps the contents of each
+.I file
+in one or more formats.
+If no file is specified, standard input is read.
+Input is processed in 16 byte chunks.
+Each chunk is output in each of the specified
+.IR format s,
+one format per line.
+Each line of output is prefixed its file offset.
+This is zero padded for the first format; subsequent formats are blank padded.
+If more than one
+.I file
+is specified, the name of each file is output at the start of its dump.
+.PP
+Output formats are specified by a two character name,
+.B 4x
+by default.
+The first character defines the byte width of the format; the second character
+defines the style of output.
+.PP
+The available width specifiers are:
+.TF "1 or b"
+.TP
+.BR 1 " or " b
+1 byte units.
+.TP
+.BR 2 " or " w
+2 byte big-endian units.
+.TP
+.BR 4 " or " l
+4 byte big-endian units.
+.TP
+.BR 8 " or " v
+8 byte big-endian units.
+.PD
+.PP
+The available styles are:
+.TP 0
+.B o
+Octal
+.PD 0
+.TP
+.B d
+Decimal
+.TP
+.B x
+Hexadecimal
+.PD
+.PP
+In addition to the above format specifiers,
+.B -c
+can be used to denote ASCII format.
+This is the same as
+.B 1x
+except that codes are printed as printable ASCII characters or Limbo escape sequences
+where possible.
+.SH OPTIONS
+.TP 10
+.B -u
+Unbuffered output.  The output buffer is flushed after the dump of each 16 byte input chunk
+has been generated.
+.TP
+.B -r
+Mark repeated 16 byte input chunks.
+The first chunk is output as per the format specifiers, followed by an asterisk representing
+1 or more occurrences of identical data.
+.TP
+.B -s
+Reverse the order of input bytes in chunks of 4 before processing for output.
+.TP
+.BI -a style
+Print file addresses in the given
+.IR style .
+.SH SOURCE
+.TP
+.B /appl/cmd/xd.b
+.SH BUGS
+There is no means of dumping 2 or 8 byte wide values in little-endian form.
--- /dev/null
+++ b/man/1/yacc
@@ -1,0 +1,349 @@
+.TH YACC 1
+.SH NAME
+yacc \- yet another compiler-compiler (Limbo version)
+.SH SYNOPSIS
+.B yacc
+[
+.I option ...
+]
+.I grammar
+.SH DESCRIPTION
+.I Yacc
+converts a context-free grammar and translation code
+into a set of
+tables for an LR(1) parser and translator.
+The grammar may be ambiguous;
+specified precedence rules are used to break ambiguities.
+.PP
+The output from
+.I yacc
+is a Limbo module
+.B y.tab.b
+containing the parse function
+.B yyparse
+which must be provided with a
+.B YYLEX
+adt providing the parser access to a lexical analyser
+routine
+.BR lex() ,
+an error routine
+.BR error() ,
+and any other context required.
+.PP
+The options are
+.TP "\w'\fL-o \fIoutput\fLXX'u"
+.BI -o " output
+Direct output to the specified file instead of
+.BR y.tab.b .
+.TP
+.BI -D n
+Create file
+.BR y.debug ,
+containing diagnostic messages.
+To incorporate them in the parser, give an
+.I n
+greater than zero.
+The amount of 
+diagnostic output from the parser is regulated by
+value
+.IR n :
+.RS 
+.TP
+1
+Report errors.
+.TP
+2
+Also report reductions.
+.TP
+3
+Also report the name of each token returned by
+.LR yylex .
+.RE
+.TP
+.B -v
+Create file
+.BR y.output ,
+containing a description of the parsing tables and of
+conflicts arising from ambiguities in the grammar.
+.TP
+.B -d
+Create file
+.BR y.tab.m ,
+containing the module
+declaration for the parser, along with
+definitions of the constants
+that associate
+.IR yacc -assigned
+`token codes' with user-declared `token names'.
+Include it in source files other than
+.B y.tab.b
+to give access to the token codes and the parser module.
+.TP
+.BI -s " stem
+Change the prefix
+.L y 
+of the file names
+.BR y.tab.b ,
+.BR y.tab.m ,
+.BR y.debug ,
+and
+.B y.output
+to
+.IR stem .
+.TP
+.B -m
+Normally
+.I yacc
+defines the type of the
+.B y.tab.b
+module within the text of the module according
+to the contents of the
+.B %module
+directive.
+Giving the
+.B -m
+option suppresses this behaviour, leaving the implementation
+free to define the module's type from an external
+.B .m
+file. The module's type name is still taken from the
+.B %module
+directive.
+.TP
+.BI -n " size
+Specify the initial
+.I size
+of the token stack created for the
+parser (default: 200).
+.SS Differences from C yacc
+The Limbo
+.I yacc
+is in many respects identical to the C
+.IR yacc .
+The differences are summarised below:
+.PP
+Comments follow the Limbo convention (a
+.B #
+symbol gives a comment until the end of the line).
+.PP
+A
+.B %module
+directive is required, which replaces the
+.B %union
+directive. It is of the form:
+.RS
+.IP
+.B %module
+.I modname
+.B {
+.br
+.I 	module types, functions and constants
+.br
+.B }
+.RE
+.B Modname
+will be the module's implementation type;
+the body of the directive, augmented with
+.B con
+definitions for the
+.IR yacc -assigned
+token codes, gives the type of the module,
+unless the
+.B -m
+option is given, in which case no module
+definition is emitted.
+.PP
+A type
+.B YYSTYPE
+must be defined, giving the type
+associated with
+.I yacc
+tokens. If
+the angle bracket construction is used after
+any of the
+.BR %token ,
+.BR %left ,
+.BR %right ,
+.BR %nonassoc 
+or
+.B %type
+directives in order to associate a type with a token or production,
+the word inside the angle brackets
+refers to a member of 
+an instance of
+.BR YYSTYPE ,
+which should be an adt.
+.PP
+An adt
+.B YYLEX
+must be defined, providing context to the parser.
+The definition must consist of at least the following:
+.EX
+	YYLEX: adt {
+		lval: YYSTYPE;
+		lex: fn(l: self ref YYLEX): int;
+		error: fn(l: self ref YYLEX, msg: string);
+	}
+.EE
+.B Lex
+should invoke a lexical analyser to return the
+next token for
+.I yacc
+to analyse. The value of the token should
+be left in
+.BR lval .
+.B Error
+will be called when a parse error occurs.
+.B Msg
+is a string describing the error.
+.PP
+.B Yyparse
+takes one argument, a reference to the
+.B YYLEX
+adt that will be used to provide it with tokens.
+.PP
+The parser is fully re-entrant;
+.I i.e.
+it does not
+hold any parse state in any global variables
+within the module.
+.SH EXAMPLE
+The following is a small but complete example of the
+use of Limbo
+.I yacc
+to build a simple calculator.
+.EX
+%{
+    include "sys.m";
+    sys: Sys;
+
+    include "bufio.m";
+    bufio: Bufio;
+    Iobuf: import bufio;
+
+    include "draw.m";
+
+    YYSTYPE: adt { v: real; };
+    YYLEX: adt {
+        lval:   YYSTYPE;
+        lex: fn(l: self ref YYLEX): int;
+        error: fn(l: self ref YYLEX, msg: string);
+    };
+%}
+
+%module Calc{
+    init:   fn(ctxt: ref Draw->Context, args: list of string);
+}
+
+%left   '+' '-'
+%left   '*' '/'
+
+%type   <v> exp uexp term
+%token  <v> REAL
+
+%%
+top :
+    | top '\en'
+    | top exp '\en'
+    {
+        sys->print("%g\en", $2);
+    }
+    | top error '\en'
+    ;
+
+exp : uexp
+    | exp '*' exp   { $$ = $1 * $3; }
+    | exp '/' exp   { $$ = $1 / $3; }
+    | exp '+' exp   { $$ = $1 + $3; }
+    | exp '-' exp   { $$ = $1 - $3; }
+    ;
+
+uexp    : term
+    | '+' uexp  { $$ = $2; }
+    | '-' uexp  { $$ = -$2; }
+    ;
+
+term    : REAL
+    | '(' exp ')'
+    {
+        $$ = $2;
+    }
+    ;
+
+%%
+
+in: ref Iobuf;
+stderr: ref Sys->FD;
+
+init(nil: ref Draw->Context, nil: list of string)
+{
+	sys = load Sys Sys->PATH;
+	bufio = load Bufio Bufio->PATH;
+	in = bufio->fopen(sys->fildes(0), Bufio->OREAD);
+	stderr = sys->fildes(2);
+	lex := ref YYLEX;
+	yyparse(lex);
+}
+
+YYLEX.error(nil: self ref YYLEX, err: string)
+{
+	sys->fprint(stderr, "%s\en", err);
+}
+
+YYLEX.lex(lex: self ref YYLEX): int
+{
+	for(;;){
+		c := in.getc();
+		case c{
+		' ' or '\et' =>
+			;
+		'-' or '+' or '*' or '/' or '\en' or '(' or ')' =>
+			return c;
+		'0' to '9' or '.' =>
+			s := "";
+			i := 0;
+			s[i++] = c;
+			while((c = in.getc()) >= '0' && c <= '9' ||
+			      c == '.' ||
+			      c == 'e' || c == 'E')
+				s[i++] = c;
+			in.ungetc();
+			lex.lval.v = real s;
+			return REAL;
+		* =>
+			return -1;
+		}
+	}
+}
+.EE
+.SH FILES
+.TF /lib/yaccpar
+.TP
+.B y.output
+.TP
+.B y.tab.b
+.TP
+.B y.tab.m
+.TP
+.B y.debug
+.TP
+.B /lib/yaccpar
+parser prototype
+.SH SOURCE
+.B /appl/cmd/yacc.b
+.SH "SEE ALSO"
+S. C. Johnson and R. Sethi,
+``Yacc: A parser generator'',
+.I
+Unix Research System Programmer's Manual,
+Tenth Edition, Volume 2
+.br
+B. W. Kernighan and Rob Pike,
+.I
+The UNIX Programming Environment,
+Prentice Hall, 1984
+.SH BUGS
+The parser may not have full information when it writes to
+.B y.debug
+so that the names of the tokens returned by
+.L yylex
+may be missing.
--- /dev/null
+++ b/man/1/zeros
@@ -1,0 +1,44 @@
+.TH ZEROS 1
+.SH NAME
+zeros \- write sequence of bytes
+.SH SYNOPSIS
+.B zeros
+[
+.IR -r
+]
+[
+.IR -v value
+]
+[ [
+.IR blocksize ...
+[
+.IR numblocks
+] ]
+.SH DESCRIPTION
+.B Zeros
+writes a sequence of bytes to standard output. The arguments
+specify the nature of the bytes,
+block size in bytes, and the number of blocks to output.
+The
+.I -r
+option requests that each block be the same, but randomly
+generated. The
+.IR -v value
+option sets the value of each byte (default 0).
+Typically
+.IR zeros
+has a specialised use:
+ensuring a file has the desired number of blocks in it to hold a file system image,
+before reaming it.
+.SH EXAMPLE
+To create and initialize a file system containing 2048 1024 byte
+blocks 
+.IP
+.EX
+zeros 1024 2048 >kfs.file
+mount -c {disk/kfs -r kfs.file} /n/local
+.EE
+.SH SOURCE
+.B /appl/cmd/zeros.b
+.SH SEE ALSO
+.IR kfs (4)
--- /dev/null
+++ b/man/10/0intro
@@ -1,0 +1,74 @@
+.TH INTRO 10
+.SH NAME
+intro \- introduction to hosted and native implementation
+.SH DESCRIPTION
+Inferno provides a collection of compiler suites, libraries and two closely-related
+kernels to span a range of host and native platforms.
+Section 10 of this manual is divided into subsections numbered
+in the same way as the main manual: 10.1 for commands, 10.2
+for library and kernel routines, and 10.6 for file formats.
+.PP
+Section 10.1 describes the various compiler and utility commands
+provided to support compilation and cross-compilation of native
+kernels.
+These are derived from similarly named programs of the system Plan 9 from Bell Labs,
+converted to ANSI C to
+provide a consistent, portable environment for cross-compiling
+any native kernel on any host platform.
+.PP
+Section 10.2 describes the functions publicly available to the authors of
+kernel code, particularly device drivers (real and virtual).
+This section will eventually be much expanded, but this makes a start.
+See the description of the conventional header files below.
+.PP
+Section 10.6 describes include the native object file formats,
+the Inferno (Plan 9) object library (archive) format,
+and system configuration files.
+.PP
+Section 10.8 describes bootstrap programs and procedures for
+native Inferno systems.
+.SS Native kernel declarations
+The
+.SM SYNOPSIS
+subsections in section 10.2 do not show the header files needed for
+the standard kernel declarations.
+The primary combinations summarised below:
+.IP
+.RS
+.ta \w'\fL#include 'u
+.nf
+.B
+#include	"u.h"
+.B
+#include	"../port/lib.h"
+.B
+#include	"mem.h"
+.B
+#include	"dat.h"
+.B
+#include	"fns.h"
+.B
+#include	"../port/error.h"
+.PP
+.I "furthermore, added in IP code:"
+.br
+.B
+#include "../ip/ip.h"
+.PP
+.I "furthermore, in hardware device drivers:"
+.br
+.B
+#include "io.h"
+.br
+.B
+#include "ureg.h"
+.PP
+.I "furthermore, in network interfaces or ether drivers:"
+.B
+#include "../port/netif.h"
+.fi
+.RE
+.PP
+There might also be specific include files needed by
+drivers on particular platforms or to use specialised kernel interfaces.
+The easiest method is to check the source of likely-looking drivers nearby.
--- /dev/null
+++ b/man/10/2a
@@ -1,0 +1,55 @@
+.TH 2A 10.1 
+.SH NAME
+0a, 1a, 2a, 5a, 6a, 7a, 8a, ka, qa, va \- assemblers
+.SH SYNOPSIS
+.B 2a
+[
+.I option ...
+]
+[
+.I name ...
+]
+.br
+etc.
+.SH DESCRIPTION
+These programs
+assemble the named files into object files
+for the corresponding architectures; see
+.IR 2c (10.1)
+for the correspondence between an architecture and the character
+.RB ( 1 ,
+.RB 2 ,
+etc.) that specifies it.
+The assemblers handle the most common C preprocessor directives and the associated
+command-line options
+.BR -D
+and
+.BR -I .
+Other options are:
+.TP
+.BI -o " obj"
+Place output in file
+.I obj
+(allowed only if there is just one input file).
+Default is to take the last element of the input path name,
+strip any trailing
+.BR .s ,
+and append
+.RI . O ,
+where
+.I O
+is first letter of the assembler's name.
+.SH FILES
+The directory
+.B /sys/include
+is searched for include files after
+machine-dependent files in
+.BR /$objtype/include .
+.SH SOURCE
+.BR /utils/2a ,
+etc.
+.SH SEE ALSO
+.IR 2c (10.1),
+.IR 2l (10.1).
+.PP
+Rob Pike, ``A manual for the Plan 9/Inferno assembler'', Volume 2
--- /dev/null
+++ b/man/10/2c
@@ -1,0 +1,437 @@
+.TH 2C 10.1 
+.SH NAME
+0c, 1c, 2c, 5c, 6c, 7c, 8c, kc, qc, vc \- C compilers
+.SH SYNOPSIS
+.B 2c
+[
+.I option ...
+]
+[
+.I file ...
+]
+.br
+etc.
+.SH DESCRIPTION
+These commands compile the named C
+.I files
+into object files for the corresponding architecture.
+Associated with each compiler is a string
+.IR objtype ,
+for example
+.TP 1.5i
+.B "0c spim
+Little-endian MIPS
+.TP
+.B "1c 68000
+Motorola MC68000
+.TP
+.B "2c 68020
+Motorola MC68020
+.TP
+.B "5c arm
+ARM 7500
+.TP
+.B "6c amd64
+AMD64 extension to x86
+.TP
+.B "7c alpha
+Digital Alpha APX
+.TP
+.B "8c 386
+Intel i386, i486, Pentium, etc.
+.TP
+.B "kc sparc
+Sun SPARC
+.TP
+.B "qc power
+Power PC,
+.TP
+.B "vc mips
+big-endian MIPS 3000 family
+.PP
+Let the first letter of the compiler name be
+.IR O =
+.BR 0 ,
+.BR 1 ,
+.BR 2 ,
+.BR 5 ,
+.BR 6 ,
+.BR 7 ,
+.BR 8 ,
+.BR k ,
+.BR q ,
+or
+.BR v .
+The output object files end in
+.RI . O .
+The letter is also the prefix of related programs:
+.IB O a
+is the assembler,
+.IB O l
+is the loader.
+.PP
+Plan 9 conventionally sets the
+.B $objtype
+environment variable to the
+.I objtype
+string appropriate to the current machine's type.
+Plan 9 also conventionally has
+.RI / objtype
+directories, which contain among other things:
+.BR include ,
+for machine-dependent include files;
+.BR lib ,
+for public object code libraries;
+.BR bin ,
+for public programs;
+and
+.BR mkfile ,
+for preconditioning
+.IR mk (10.1).
+.PP
+For Inferno cross-compilation on all platforms, not just Plan 9, both
+.B $objtype
+and
+.B $OBJTYPE
+are set by every native kernel
+.B mkfile
+to correspond to the target processor type.
+The Inferno
+.B mkfiles
+also set the
+.B -I
+option appropriately to search the Inferno include directories,
+since the Plan 9 defaults are inappropriate.
+.PP
+The compiler options are:
+.TP 1i
+.BI -o " obj"
+Place output in file
+.I obj
+(allowed only if there is just one input file).
+Default is to take the last element of the input file name,
+strip any trailing
+.BR .c ,
+and append
+.RI . O .
+.TP
+.B -w
+Print warning messages about unused variables, etc.
+.TP
+.B -B
+Accept functions without a new-style
+ANSI C function prototype.
+By default, the compilers reject functions
+used without a defined prototype,
+although ANSI C permits them.
+.TP
+.BI -D\*S name=def
+.br
+.ns
+.TP
+.BI -D \*Sname
+Define the
+.I name
+to the preprocessor,
+as if by
+.LR #define .
+If no definition is given, the name is defined as
+.LR 1 .
+.TP
+.B -F
+Warn when the elements of a format
+(eg, those used by
+.IR print )
+disagree with in type or size with the corresponding parameter,
+or there is a mismatch in number.
+See the discussion of extensions, below.
+.TP
+.BI -I \*Sdir
+An
+.L #include
+file whose name does not begin with 
+slash
+or is enclosed in double quotes
+is always
+sought first in the directory 
+of the
+.I file
+argument.  If this fails,
+the
+.I -.
+flag is given or the name is enclosed in
+.BR <> ,
+it is then sought
+in directories named in 
+.B -I
+options,
+then in
+.BR /sys/include ,
+and finally in
+.BR /$objtype/include .
+.TP
+.B -.
+Suppress the automatic searching for include files in
+the directory of the file argument.
+.TP
+.B -N
+Suppress automatic registerization and optimization.
+.TP
+.B -S
+Print an assembly language version of the object code
+on standard output as well as generating the
+.RI . O
+file.
+.TP
+.B -T
+Pass type signatures on all external and global entities.
+The signature is based on the C
+.B signof
+operator,
+an extension in this compiler.
+See
+.IR dynld (10.2).
+.TP
+.B -V
+By default, the compilers are non-standardly lax about type equality between
+.B void*
+values and other pointers; this flag requires ANSI C conformance.
+.TP
+.B -a
+Instead of compiling, print on standard output acid functions (see
+.IR acid (10.1))
+for examining structures declared in the source files.
+.TP
+.B -aa
+Like
+.B -a
+except suppress information about structures
+declared in included header files.
+.PP
+The compilers handle most preprocessing directives themselves, but support
+excludes the
+.B #if
+and
+.B #elif
+directives, and the
+.B ##
+preprocessor operation.
+.PP
+The compilers support several extensions to ANSI C:
+.TP
+\-
+A structure or union may contain unnamed substructures and subunions.
+The fields of the substructures or
+subunions can then be used as if they were members of the parent
+structure or union (the resolution of a name conflict is unspecified).
+When a pointer to the outer structure or union is used in a context
+that is only legal for the unnamed substructure, the compiler promotes
+the type and adjusts the pointer value to point at the substructure.
+If the unnamed structure or union is of a type with a tag name specified by a
+.B typedef
+statement, 
+the unnamed structure or union can be explicitly referenced
+by <struct variable>.<tagname>.
+.TP
+\-
+A structure value can be formed with an expression such as
+.EX
+    (struct S){v1, v2, v3}
+.EE
+where the list elements are values for the fields of struct
+.BR S .
+.TP
+\-
+Array initializers can specify the indices of the array in square
+brackets, as
+.EX
+    int a[] = { [3] 1, [10] 5 };
+.EE
+which initializes the third and tenth elements of the eleven-element array
+.BR a .
+.TP
+\-
+Structure initializers can specify the structure element by using the name
+following a period, as
+.EX
+    struct { int x; int y; } s = { .y 1, .x 5 };
+.EE
+which initializes elements
+.B y
+and then
+.B x
+of the structure
+.BR s .
+These forms also accept the new ANSI C notation, which includes an equal sign:
+.EX
+    int a[] = { [3] = 1, [10] = 5 };
+    struct { int x; int y; } s = { .y = 1, .x = 5 };
+.EE
+.TP
+\-
+A global variable can be dedicated to a register
+by declaring it
+.B "extern register"
+in
+.I all
+modules and libraries.
+.TP
+\-
+A
+.B #pragma
+of the form
+.EX
+    #pragma lib "libbio.a"
+.EE
+records that the program needs to be loaded with file
+.BR /$objtype/lib/libbio.a ;
+such lines, typically placed in library header files, obviate the
+.B -l
+option of the loaders.  To help identify files in non-standard directories,
+within the file names in the
+.B #pragmas
+the string
+.B $M
+represents the name of the architecture
+(e.g.,
+.BR mips )
+and
+.B $O
+represents its identifying character
+(e.g.,
+.BR v ).
+.TP
+\-
+Two
+.B #pragma
+requests to define rules for checking
+.IR print -like
+formats (see the
+.B -F
+option above).
+One
+.B #pragma
+tells for a given routine which argument is the format.
+For example:
+.EX
+    #pragma varargck argpos print 1
+    #pragma varargck argpos sprint 2
+.EE
+say that
+.I print
+has a format as its first argument,
+and
+.I sprint
+has one as its second.
+Another
+.B #pragma
+associates format character sequences and types:
+.EX
+   #pragma varargck type "lld" vlong
+   #pragma varargck type "lx" void*
+   #pragma varargck type "S" Rune*
+.EE
+where the format characters are those following the
+.B %
+in the format (ignoring any preceding formatting flags).
+Note the assumption that all formats arguments are compatible.
+The system include files have appropriate
+.B #pragma
+lines for the standard format elements and formatting functions.
+.TP
+\-
+A
+.B #pragma
+of the form
+.EX
+    #pragma incomplete \fItype\fP
+.EE
+tells the compiler that
+.I type
+should have its signature calculated as an incomplete type
+even when it is fully defined.
+This allows the type signature mechanism to work in the presence
+of opaque types declared in header files, with their full definitions
+visible only to the code which manipulates them.
+With some imported software it might be necessary to turn off the
+signature generation completely for a large body of code (typically
+at the start and end of a particular include file).
+If
+.I type
+is the word
+.BR _off_ ,
+signature generation is turned off; if
+.I type
+is the word
+.BR _on_ ,
+the compiler will generate signatures.
+.TP
+\-
+The C++ comment
+.RB ( //
+to end of line)
+is accepted as well as the normal
+convention of
+.B /*
+.BR */ .
+.TP
+\-
+The compilers accept
+.B long
+.B long
+variables as a 64-bit type.
+The standard header typedefs this to
+.BR vlong .
+Arithmetic on
+.B  vlong
+values is usually emulated by a run-time library.
+.SH EXAMPLE
+For the 68020, produce a program
+.B prog
+from C files
+.BR main.c
+and
+.BR sub.c :
+.IP
+.EX
+2c -FVw main.c sub.c
+2l -o prog main.2 sub.2
+.EE
+.SH FILES
+.TF /$objtype/include
+.TP
+.B /sys/include
+host system area for machine-independent
+.B #include
+directives.
+.TP
+.B /$objtype/include
+host system area for machine-dependent
+.B #include
+directives.
+.SH SOURCE
+.TF /utils/2c,\ etc.
+.TP
+.B /utils/cc
+machine-independent part
+.TP
+.BR /utils/2c ,\ etc.
+machine-dependent part
+.SH "SEE ALSO"
+.IR 2a (10.1),
+.IR 2l (10.1),
+.IR mk (10.1),
+.IR inm (10.1),
+.IR acid (10.1),
+.PP
+Rob Pike,
+``How to Use the Plan 9 C Compiler''
+.SH BUGS
+The preprocessor only handles
+.LR #define ,
+.LR #include ,
+.LR #undef ,
+.LR #ifdef ,
+.LR #line ,
+and
+.LR #ifndef .
--- /dev/null
+++ b/man/10/2l
@@ -1,0 +1,201 @@
+.TH 2L 10.1
+.SH NAME
+0l, 1l, 2l, 5l, 6l, 7l, 8l, kl, ql, vl \- loaders
+.SH SYNOPSIS
+.B 2l
+[
+.I option ...
+]
+[
+.I file ...
+]
+.br
+etc.
+.SH DESCRIPTION
+These commands load the named
+.I files
+into executable files for the corresponding architectures; see
+.IR 2c (10.1)
+for the correspondence between an architecture and the character
+.RB ( 1 ,
+.BR 2 ,
+etc.)
+that specifies it.
+The files should be object files or libraries (archives of object files)
+for the appropriate architecture.
+Also, a name like
+.BI -l ext
+represents the library
+.BI lib ext .a
+in
+.BR /$objtype/lib ,
+where
+.I objtype
+is one of
+.BR 68000 ,
+etc. as listed in
+.IR 2c (10.1).
+The libraries must have tables of contents
+(see
+.IR iar (10.1)).
+.PP
+In practice,
+.B -l
+options are rarely necessary as the header files for
+the libraries cause their archives to be included automatically in the load
+(see
+.IR 2c (10.1)).
+For example, any program that includes header file
+.B libc.h
+causes the loader
+to search the C library
+.BR /$objtype/lib/libc.a .
+Also, the loader creates an undefined symbol
+.B _main
+(or
+.B _mainp
+if profiling is enabled) to force loading of the
+startup linkage from the C library.
+.PP
+The order of search to resolve undefined symbols is to load all files and libraries
+mentioned explicitly on the command line, and then to resolve remaining symbols
+by searching in topological order
+libraries mentioned in header files included by files already loaded.
+When scanning such libraries, the algorithm is to scan each library repeatedly until
+no new undefined symbols are picked up, then to start on the next library.  Thus if library
+.I A
+needs
+.I B
+which needs
+.I A
+again, it may be necessary to mention
+.I A
+explicitly so it will be read a second time.
+.PP
+The loader options are:
+.TP 1i
+.B -l
+(As a bare option.)
+Suppress the default loading of the startup linkage and libraries
+specified by header files.
+.TP
+.BI -o " out"
+Place output in file
+.IR out .
+Default is
+.IB O .out\f1,
+where
+.I O
+is the first letter of the loader name.
+.TP
+.B -p
+Insert profiling code into the executable output; no special action is needed
+during compilation or assembly.
+.TP
+.B -s
+Strip the symbol tables from the output file.
+.TP
+.B -a
+Print the object code in assembly language, with addresses.
+.TP
+.B -v
+Print debugging output that annotates the activities of the load.
+.TP
+.BI -M
+.RI ( kl
+only) Generate instructions rather than calls to emulation routines
+for multiply and divide.
+.TP
+.BI -E symbol
+The entry point for the binary is
+.I symbol
+(default
+.BR _main ;
+.B _mainp
+under
+.BR -p ).
+.TP
+.B -x 
+[
+.I file
+]
+Produce an export table in the executable.
+The optional
+.I file
+restricts the exported symbols to those listed in the file.
+See
+.IR dynld (10.2).
+.TP
+.B -u 
+[
+.I file
+]
+Produce an export table, import table
+and a dynamic load section in the executable.
+The optional
+.I file
+restricts the imported symbols to those listed in the file.
+See
+.IR dynld (10.2).
+.TP
+.BI -H n
+Executable header is type
+.IR n .
+The meaning of the types is architecture-dependent; typically
+type 1 is Plan 9 boot format and type 2 is the
+regular Plan 9 format, the default.  These are reversed on the MIPS.
+The Next boot format is 3.  Type 4 in
+.I vl
+creates a MIPS executable for an SGI Unix system.
+.TP
+.BI -T t
+The text segment starts at address
+.IR t .
+.TP
+.BI -D d
+The data segment starts at address
+.IR d .
+.TP
+.BI -R r
+The text segment is rounded to a multiple of
+.I r
+(if
+.I r
+is nonzero).
+.PP
+The numbers in the above options can begin with
+.L 0x
+or
+.L 0
+to change the default base from decimal to hexadecimal or octal.
+The defaults for the values depend on the compiler and the
+header type.
+.PP
+The loaded image has several symbols inserted by the loader:
+.B etext
+is the address of the end of the text segment;
+.B bdata
+is the address of the beginning of the data segment;
+.B edata
+is the address of the end of the data segment;
+and
+.B end
+is the address of the end of the bss segment, and of the program.
+.SH FILES
+.TF /$objtype/lib
+.TP
+.B /$objtype/lib
+for
+.BI -l lib
+arguments.
+.SH SOURCE
+.B /utils/2l
+etc.
+.SH "SEE ALSO"
+.IR 2c (10.1),
+.IR 2a (10.1),
+.IR iar (10.1),
+.IR inm (10.1)
+.PP
+Rob Pike,
+``How to Use the Plan 9 C Compiler''
--- /dev/null
+++ b/man/10/5coff
@@ -1,0 +1,82 @@
+.TH 5COFF 10.1
+.SH NAME
+5coff \- converter to coff format
+.SH SYNOPSIS
+.B 5coff
+[
+.B -T
+.I t
+]
+[
+.B -D
+.I d
+]
+[
+.B -R
+.I r
+]
+[
+.B -E
+.I e
+]
+[
+.B -d
+]
+.I ifile ofile
+.SH DESCRIPTION
+.I 5coff
+converts an executable file 
+.I ifile
+in
+.IR a.out (10.6)
+format as
+produced by
+.I 5l
+(see
+.IR 2l (10.1))
+to one in
+.SM COFF
+format, which it writes to
+.IR ofile .
+The options to
+.I 5coff
+are as follows:
+.TP
+.BI -T t
+The text segment starts at address
+.I t.
+.TP
+.BI -D d
+The data segment starts at address
+.I d.
+.TP
+.BI -R r
+The text segment is rounded up to a multiple of
+.I r
+if non-zero.
+.TP
+.BI -E e
+The entry point is at address
+.I e.
+.TP
+.B -d
+Print debugging information.
+.PP
+.SH EXAMPLE
+An executable built with the command
+.IP
+.EX
+5l -T0x04010000 -R4 -o abc ...
+.EE
+.PP
+can be converted to coff format by
+.IP
+.EX
+5coff -T0x04010000 -R4 abc abc.coff
+.EE
+.SH SOURCE
+.B /utils/5coff
+.SH SEE ALSO
+.IR 2l (10.1),
+.IR 5cv (10.1),
+.IR a.out (10.6)
--- /dev/null
+++ b/man/10/5cv
@@ -1,0 +1,127 @@
+.TH 5CV 10.1
+.SH NAME
+5cv, mkppcimage, sqz \- convert kernel executable to boot format
+.SH SYNOPSIS
+.B 5cv
+[
+.BI -D n
+] [
+.BI -H n
+] [
+.B -s
+]
+.I "executable outfile"
+.PP
+.B mkppcimage
+[
+.BI -l " loadaddr"
+]
+.I "executable outfile"
+.PP
+.B sqz
+[
+.B -w
+] [
+.B -t
+]
+.I executable
+.SH DESCRIPTION
+These commands convert a kernel executable in Inferno/Plan 9
+.IR a.out (10.5)
+format into another
+format used by a third party's boot loader.
+Most convert the input
+.I executable
+and write the new format to
+.IR outfile .
+.PP
+.IR 5cv
+converts an ARM executable into one of several alternative formats.
+The output format is controlled by the
+.B -H
+option:
+.TP 8n
+.BI -H1
+AIF for RISCOS.
+.TP
+.BI -H2
+Plan 9.
+.TP
+.BI -H3
+Boot for NetBSD.
+.TP
+.BI -H4
+Headerless, stripped, and padded to 2K in length. Used for the ROM resident serial
+bootstrap
+loader in a Cirrus EP72xx.
+.TP
+.BI -H5
+Headerless, and stripped, for general use.
+.TP
+.BI -H6
+EPOC IMG format. Not a complete conversion, currently sufficient for use with some
+NT based downloaders which autosense the file type by the "EP" signature, and then
+ignore the contents of the header.
+.PP
+The other options are:
+.TP
+.BI -s
+Strip symbol table.
+.TP
+.BI -D n
+Enables debug output.
+.PP
+.I Mkppcimage
+converts a PowerPC or ARM
+.I executable
+to a boot image format used by
+.SM PPCBOOT
+and
+.SM UBOOT\c
+\&.
+The output file has a
+.SM PPCBOOT
+image with one component labelled as an `OS kernel' for the appropriate architecture,
+containing the
+.IR a.out (10.6)
+header, text and initialised data, all uncompressed.
+Symbols are not included.
+By default the load address is deduced from the executable's entry point;
+the
+.B -l
+option allows
+.I loadaddr
+to be set explicitly, with the number in C syntax (decimal by default).
+Other attributes are deduced from the executable.
+.PP
+.I Sqz
+squeezes (compresses) the given
+ARM or PowerPC
+.I executable
+using a method that achieves respectable compression for executables but is much faster to decompress than
+(say)
+.BR gzip 's.
+By default, both the program text and initialised data are compressed; the
+.B -t
+option causes
+.I sqz
+to compress only the program text, leaving the data as-is.
+By default,
+.I sqz
+prints compression statistics on its standard error output;
+the
+.B -w
+option causes it also to write the compressed file on its standard output.
+Either the bootstrap that loads it must decompress the result, or a small uncompressed
+stub must also be loaded that decompresses the remainder.
+.SH SOURCE
+.B /utils/5cv
+.br
+.B /utils/mkppcimage
+.br
+.B /utils/sqz
+.SH "SEE ALSO"
+.IR 2l (10.1),
+.IR 5cv (10.1),
+.IR ms2 (10.1),
+.IR a.out (10.5)
--- /dev/null
+++ b/man/10/9load
@@ -1,0 +1,411 @@
+.TH 9LOAD 10.8
+.SH NAME
+9load, ld, 9pxeload \- PC bootstrap program
+.SH SYNOPSIS
+.I "(Under MS-DOS)
+.br
+[
+.I drive
+:][
+.I path
+.RB ] ld
+[
+.I 9load
+]
+.SH DESCRIPTION
+On the PC, bootstrap programs from Plan 9 are used to boot Inferno as well
+(hence the naming convention).
+.I 9load
+and
+.I ld
+are programs that reside in a FAT file system and bootstrap Inferno.
+.I 9load
+loads the kernel, but it cannot be run from DOS; use
+.I ld
+to bootstrap (by starting
+.IR 9load )
+if DOS is running.
+.I 9load
+is run automatically by the boot procedures described below;
+it cannot be run directly by hand.
+There are three bootstrap sequences:
+.IP \-
+BIOS, MBR, disk partition PBS,
+.IR 9load ,
+kernel
+.IP \-
+BIOS, floppy PBS,
+.IR 9load ,
+kernel
+.IP \-
+BIOS, MBR, DOS,
+.IR ld ,
+.IR 9load ,
+kernel.
+.PP
+Details follow.
+.PP
+.I 9load
+is a bootstrap program that loads and starts a program,
+typically the kernel, on a PC.
+It is run by the PC partition boot sector program (PBS),
+which usually resides in the first
+sector of the active partition.
+A copy of the Plan 9 PBS is kept in
+.BR /Inferno/386/pbs ,
+but due to the ``cylinder-head-sector'' (CHS) addressing mode of old BIOSes, it can only
+operate up to 8.5GB into the disk.
+Plan 9 partitions further into the disk
+can only be booted using
+.BR /Inferno/386/pbslba ,
+and then only if the machine's BIOS supports
+linear block addressing (LBA) mode for disk transfers.
+.PP
+When booting from floppy or hard disk, the BIOS loads the
+first sector of the medium at location 0x7C00.  In the
+case of a floppy, this is the PBS.  In the case of a hard
+disk it it the master boot record (MBR).
+The MBR copies itself to address
+.BR 0x600 ,
+finds the active partition and loads its PBS at address
+.BR 0x7C00 .
+A copy of the Plan 9 MBR is kept in
+.BR /Inferno/386/mbr ;
+some commercial MBRs cannot read sectors
+past 2GB.
+The Plan 9 MBR can read sectors up to 8.5GB into
+the disk, and further if the BIOS supports LBA.
+The single file
+.B /Inferno/386/mbr
+detects whether the BIOS supports LBA and
+acts appropriately, defaulting to CHS mode
+when LBA is not present.
+The PBSs cannot do this due to code size considerations.
+The Plan 9 MBR is suitable for booting non-Plan 9
+operating systems,
+and (modulo the large disk constraints just described)
+non-Plan 9 MBRs are suitable for booting Plan 9.
+.PP
+Thus the default sequence is: BIOS, MBR, PBS,
+.IR 9load ,
+kernel.
+.PP
+Because it contains many device drivers for different
+disks and networks,
+.I 9load
+is larger than 64K and cannot be run as a DOS
+.RB `` .com ''
+executable.
+A stripped-down version that knows about disks but not networks,
+called
+.I ld
+(really
+.BR ld.com ),
+fits in 64K and can be used under DOS to load and start a program (default
+.IR 9load )
+from the FAT16 partition.
+Its command line argument is of the same format as the
+.I bootfile
+specifiers described below.
+This profusion of loaders is unfortunate, but at least
+.I ld
+and
+.I 9load
+are compiled from the same source.
+.PP
+.I 9load
+begins execution at address
+.B 0x80010000
+(64K) and
+loads the
+.I bootfile
+at the entry address specified by the header,
+usually
+.BR 0x80100020 .
+After loading, control is passed to the entry location.
+.PP
+Finally,
+.I 9pxeload
+is a version of
+.I 9load
+that can be booted using the PXE download
+found on some ethernet card BIOSs.
+.PP
+In summary,
+Inferno and Plan 9 can be booted on a PC three different ways:
+either by booting MS-DOS and using
+.I ld
+to start
+.I 9load
+in the appropriate directory,
+by booting directly from an Inferno/Plan 9 boot floppy or disk
+partition
+prepared using
+.B format
+to install the appropriate files and bootstrap sectors
+(see
+.IR prep (8)),
+or by using a PXE capable BIOS to boot
+.I 9pxeload
+directly over the ethernet.
+.PP
+The
+.IR bootfile ,
+which may be compressed with
+.IR gzip (1),
+can be specified to
+.I 9load
+as a
+.B bootfile=
+entry in
+.IR plan9.ini ,
+or if booting from the ethernet, by a BOOTP server.
+If the
+.B plan9.ini
+file contains multiple
+.B bootfile=
+entries,
+.I 9load
+will present a numerical menu of the choices; type
+the corresponding number to select an entry.
+.PP
+The format of the
+.I bootfile
+name is
+.IB device ! file
+or
+.IB device ! partition ! file\f1.
+If
+.BI ! file
+is omitted, the default for the particular
+.I device
+is used.
+Supported
+.I devices
+are
+.TF \fLethern
+.TP
+.BI fd n
+An MS-DOS floppy disk.
+.I N
+specifies the floppy drive, either
+0 or 1.
+The
+.I bootfile
+is the contents of the MS-DOS
+.IR file .
+There is no default file.
+For compatibility with hard disks, a
+.I partition
+may be given, but only
+.B dos
+is recognized:
+.BI fd0!dos! file\f1.
+.TP
+.BI ether n
+Ethernet.
+.I N
+specifies the Ethernet device number.
+If a
+.I partition
+is specified, it is taken to be the name of a host machine
+from which to load the kernel.
+.I file
+is determined by the
+.B /lib/ndb
+(see
+.IR ndb (6))
+entry for this PC.
+.TP
+.BI sd Cn
+Non-floppy disk.
+The device name format is described in
+.IR sd (3).
+A
+.I partition
+must be given and must
+name a partition containing a FAT file system.
+The name
+.B dos
+refers to the first DOS partition on a given device.
+It is common for Inferno/Plan 9 partitions to contain a small
+FAT file system for configuration.
+By convention, this partition is called
+.BR 9fat .
+There is no default partition or pathname.
+.PD
+.PP
+When
+.I 9load
+starts running at physical address 0x10000,
+it switches to 32-bit mode.
+It then double maps the first 16Mb of physical memory to
+virtual addresses 0 and 0x80000000.
+Physical memory from 0x300000 upwards is used as data
+space.
+Next, in order to find configuration information,
+.I 9load
+searches all units on devices
+.BR fd
+and
+.BI sd Cn \fR,
+in that order, for a file called
+.B plan9\eplan9.ini
+or
+.B plan9.ini
+(see
+.IR plan9.ini (10.6))
+on a partition named
+.B dos
+or 
+.BR 9fat .
+If one is found, searching stops and the file is read into memory
+at physical address 0x1200
+where it can be found later by any loaded
+.IR bootfile .
+Some options in
+.B plan9.ini
+are used by
+.IR 9load :
+.TF bootfile=manual
+.TP
+.B console
+.TP
+.B baud
+Specifies the console device and baud rate if not a display.
+.TP
+.BI ether n
+Ethernet interfaces. These can be used to load the
+.I bootfile
+over a network.
+Probing for Ethernet interfaces is too prone to error.
+.TP
+.BI bootfile= bootfile
+Specifies the
+.IR bootfile .
+This option is overridden by a command-line argument.
+.TP
+.B bootfile=auto
+Default.
+.TP
+.B bootfile=local
+Like
+.IR auto ,
+but do not attempt to load over the network.
+.TP
+.B bootfile=manual
+After determining which devices are available for loading from,
+enter prompt mode.
+.PD
+.PP
+When the search for
+.B plan9.ini
+is done,
+.I 9load
+proceeds to determine which bootfile to load.
+If there was no
+.I bootfile
+option,
+.I 9load
+chooses a default
+from the following prioritized device list:
+.EX
+	fd sd ether
+.EE
+.I 9load
+then attempts to load the
+.I bootfile
+unless
+the
+.B bootfile=manual
+option was given, in which case prompt mode is entered immediately.
+If the default device is
+.BR fd ,
+.I 9load
+will prompt the user for input before proceeding with the
+default bootfile load after 5 seconds;
+this prompt is omitted if
+a command-line argument or
+.I bootfile
+option
+was given.
+.PP
+.I 9load
+prints the list of available
+.IR device s
+and
+enters prompt mode on encountering any error
+or if directed to do so by a
+.B bootfile=manual
+option.
+In prompt mode, the user is required to type
+a
+.IB bootfile
+in response to the
+.L "Boot from:
+prompt.
+.PP
+.I 9load
+parses the master boot record and Plan 9 partition tables
+(see
+.IR prep (8)),
+leaving partitioning information appended to the
+in-memory contents of
+.I plan9.ini
+for the
+.IR bootfile .
+This is used by
+.IR sd (3)
+to initialize partitions so that a
+file system in a partition can be found and mounted as the root file system.
+A more extensive partitioning is typically done by system initialisation in
+.B osinit.dis
+(see
+.IR root (3)).
+.PP
+A
+control-P
+character typed at any time on the console causes
+.B 9load
+to perform a hardware reset
+(Ctrl-Alt-Del can also be used on a PC keyboard).
+.PP
+When loaded from a PBS (rather than from
+.IR ld.com ),
+.I 9load
+must be contiguously allocated on
+the disk.
+See 
+.IR dossrv (4)
+for information on ensuring this.
+.SH FILES
+.RI [ drive :]
+[
+.I path
+.RB ] 9load
+.br
+.RI [ drive :]
+[
+.I path
+.RB ] ld
+.br
+.IB "FAT filesystem" :\eplan9\eplan9.ini
+.br
+.IB "FAT filesystem" :\eplan9.ini
+.SH SOURCE
+.B /os/boot/pc
+.SH "SEE ALSO"
+.IR plan9.ini (10.6),
+.IR prep (8)
+.SH BUGS
+Much of the work done by
+.B 9load
+is duplicated by the loaded kernel.
+.PP
+If
+.I ld
+detects an installed MS-DOS Extended Memory Manager,
+it attempts to de-install it, but the technique
+used may not always work.
+It is safer not to install the Extended Memory Manager before running
+.IR ld .
--- /dev/null
+++ b/man/10/INDEX
@@ -1,0 +1,265 @@
+intro 0intro
+0a 2a
+1a 2a
+2a 2a
+5a 2a
+6a 2a
+7a 2a
+8a 2a
+ka 2a
+qa 2a
+va 2a
+0c 2c
+1c 2c
+2c 2c
+5c 2c
+6c 2c
+7c 2c
+8c 2c
+kc 2c
+qc 2c
+vc 2c
+0l 2l
+1l 2l
+2l 2l
+5l 2l
+6l 2l
+7l 2l
+8l 2l
+kl 2l
+ql 2l
+vl 2l
+5coff 5coff
+5cv 5cv
+mkppcimage 5cv
+sqz 5cv
+9load 9load
+9pxeload 9load
+ld 9load
+a.out a.out
+acid acid
+adjustblock allocb
+allocb allocb
+blen allocb
+blocklen allocb
+checkb allocb
+concatblock allocb
+copyblock allocb
+freeb allocb
+freeblist allocb
+iallocb allocb
+packblock allocb
+padblock allocb
+pullblock allocb
+pullupblock allocb
+trimblock allocb
+ar ar
+atoi atoi
+atol atoi
+charstod atoi
+strtod atoi
+strtol atoi
+strtoll atoi
+strtoul atoi
+c2l c2l
+conf conf
+addclock0link delay
+delay delay
+microdelay delay
+dev dev
+devattach devattach
+devbread devattach
+devbwrite devattach
+devclone devattach
+devcreate devattach
+devdir devattach
+devdirread devattach
+devgen devattach
+devinit devattach
+devopen devattach
+devremove devattach
+devreset devattach
+devshutdown devattach
+devstat devattach
+devwalk devattach
+devwstat devattach
+openmode devattach
+dmacount dmainit
+dmadone dmainit
+dmaend dmainit
+dmainit dmainit
+dmasetup dmainit
+dynfindsym dynld
+dynfreeimport dynld
+dynld dynld
+dynloadfd dynld
+dynloadgen dynld
+dynobjfree dynld
+dyntabsize dynld
+error error
+nexterror error
+poperror error
+waserror error
+eve eve
+iseve eve
+getfields getfields
+tokenize getfields
+iar iar
+inb inb
+inl inb
+ins inb
+insb inb
+insl inb
+inss inb
+outb inb
+outl inb
+outs inb
+outsb inb
+outsl inb
+outss inb
+inm inm
+intrdisable intrenable
+intrenable intrenable
+kbdclock kbdputc
+kbdputc kbdputc
+kbdq kbdputc
+kbdrepeat kbdputc
+kproc kproc
+pexit kproc
+setpri kproc
+swiproc kproc
+kprof kprof
+ksize ksize
+kstrip kstrip
+canlock lock
+ilock lock
+iunlock lock
+lock lock
+unlock lock
+calloc malloc
+free malloc
+malloc malloc
+mallocz malloc
+realloc malloc
+smalloc malloc
+master master
+master.local master
+memccpy memory
+memchr memory
+memcmp memory
+memcpy memory
+memmove memory
+memory memory
+memset memory
+mk mk
+ms2 ms2
+cclose newchan
+chanfree newchan
+eqchan newchan
+eqqid newchan
+fdtochan newchan
+isdir newchan
+namec newchan
+newchan newchan
+ntsrv ntsrv
+odbc odbc
+panic panic
+parsecmd parsecmd
+plan9.ini plan9.ini
+fprint print
+print print
+seprint print
+smprint print
+snprint print
+sprint print
+vfprint print
+vseprint print
+vsmprint print
+vsnprint print
+qbread qio
+qbwrite qio
+qcanread qio
+qclose qio
+qconsume qio
+qcopy qio
+qdiscard qio
+qflush qio
+qfree qio
+qfull qio
+qget qio
+qhangup qio
+qio qio
+qiwrite qio
+qlen qio
+qnoblock qio
+qopen qio
+qpass qio
+qproduce qio
+qread qio
+qreopen qio
+qsetlimit qio
+qwindow qio
+qwrite qio
+canqlock qlock
+qlock qlock
+qunlock qlock
+rlock qlock
+runlock qlock
+wlock qlock
+wunlock qlock
+readnum readnum
+readstr readnum
+decref ref
+incref ref
+ref ref
+chartorune rune
+fullrune rune
+rune rune
+runelen rune
+runetochar rune
+utflen rune
+utfrrune rune
+utfrune rune
+utfutf rune
+hz seconds
+ms2hz seconds
+ms2tk seconds
+seconds seconds
+ticks seconds
+tk2ms seconds
+tk2sec seconds
+return0 sleep
+sleep sleep
+tsleep sleep
+wakeup sleep
+islo splhi
+splhi splhi
+spllo splhi
+splx splhi
+srclist srclist
+strcat strcat
+strchr strcat
+strcmp strcat
+strcpy strcat
+strdup strcat
+strlen strcat
+strncmp strcat
+strncpy strcat
+strrchr strcat
+strstr strcat
+convd2m styx
+convm2d styx
+convm2s styx
+convs2m styx
+dirfmt styx
+dirmodefmt styx
+fcall styx
+fcallfmt styx
+sized2m styx
+sizes2m styx
+statcheck styx
+styx styx
+styxserver styxserver
+xalloc xalloc
+xfree xalloc
+xspanalloc xalloc
--- /dev/null
+++ b/man/10/a.out
@@ -1,0 +1,242 @@
+.TH A.OUT 10.6
+.SH NAME
+a.out \- native kernel object file format
+.SH SYNOPSIS
+.B #include <a.out.h>
+.SH DESCRIPTION
+An executable native binary file has up to six sections:
+a header, the program text, the data,
+a symbol table, a PC/SP offset table (MC680x0 only),
+and finally a PC/line number table.
+The header, given by a structure in
+.BR <a.out.h> ,
+contains 4-byte integers in big-endian order:
+.PP
+.EX
+typedef struct Exec {
+    long    magic;  /* magic number */
+    long    text;   /* size of text segment */
+    long    data;   /* size of initialized data */
+    long    bss;    /* size of uninitialized data */
+    long    syms;   /* size of symbol table */
+    long    entry;  /* entry point */
+    long    spsz;   /* size of pc/sp offset table */
+    long    pcsz;   /* size of pc/line number table */
+} Exec;
+#define _MAGIC(b)   ((((4*b)+0)*b)+7)
+#define A_MAGIC     _MAGIC(8)   /* 68020 */
+#define I_MAGIC     _MAGIC(11)  /* intel 386 */
+#define J_MAGIC     _MAGIC(12)  /* intel 960 */
+#define K_MAGIC     _MAGIC(13)  /* sparc */
+#define V_MAGIC     _MAGIC(16)  /* mips 3000 */
+#define X_MAGIC     _MAGIC(17)  /* att dsp 3210 */
+#define M_MAGIC     _MAGIC(18)  /* mips 4000 */
+#define D_MAGIC     _MAGIC(19)  /* amd 29000 */
+#define E_MAGIC     _MAGIC(20)  /* arm 7-something */
+#define Q_MAGIC     _MAGIC(21)  /* powerpc */
+#define N_MAGIC     _MAGIC(22)  /* mips 4000-le */
+#define L_MAGIC     _MAGIC(23)  /* dec alpha */
+.EE
+.DT
+.PP
+Sizes are expressed in bytes.
+The size of the header is not included in any of the other sizes.
+.PP
+When a Plan 9 binary file is executed,
+a memory image of three segments is
+set up: the text segment, the data segment, and the stack.
+The text segment begins at a virtual address which is
+a multiple of the machine-dependent page size.
+The text segment consists of the header and the first
+.B text
+bytes of the binary file.
+The
+.B entry
+field gives the virtual address of the entry point of the program.
+The data segment starts at the first page-rounded virtual address
+after the text segment.
+It consists of the next
+.B data
+bytes of the binary file, followed by
+.B bss
+bytes initialized to zero.
+The stack occupies the highest possible locations
+in the core image, automatically growing downwards.
+.PP
+The next
+.B syms
+(possibly zero)
+bytes of the file contain symbol table
+entries, each laid out as:
+.IP
+.EX
+uchar value[4];
+char  type;
+char  name[\f2n\fP];   /* NUL-terminated */
+.EE
+.PP
+The
+.B value
+is in big-endian order and
+the size of the
+.B name
+field is not pre-defined: it is a zero-terminated array of
+variable length.
+.PP
+The
+.B type
+field is one of the following characters:
+.RS
+.TP
+.B T
+text segment symbol
+.PD0
+.TP
+.B t
+static text segment symbol
+.TP
+.B L
+leaf function text segment symbol
+.TP
+.B l
+static leaf function text segment symbol
+.TP
+.B D
+data segment symbol
+.TP
+.B d
+static data segment symbol
+.TP
+.B B
+bss segment symbol
+.TP
+.B b
+static bss segment symbol
+.TP
+.B a
+automatic (local) variable symbol
+.TP
+.B p
+function parameter symbol
+.RE
+.PD
+.PP
+A few others are described below.
+The symbols in the symbol table appear in the same order
+as the program components they describe.
+.PP
+The Plan 9 compilers implement a virtual stack frame pointer rather
+than dedicating a register;
+moreover, on the MC680x0
+there is a variable offset between the stack pointer and the
+frame pointer.
+Following the symbol table,
+MC680x0 executable files contain a
+.BR spsz -byte
+table encoding the offset
+of the stack frame pointer as a function of program location;
+this section is not present for other architectures.
+The PC/SP table is encoded as a byte stream.
+By setting the PC to the base of the text segment
+and the offset to zero and interpreting the stream,
+the offset can be computed for any PC.
+A byte value of 0 is followed by four bytes that hold, in big-endian order,
+a constant to be added to the offset.
+A byte value of 1 to 64 is multiplied by four and added, without sign
+extension, to the offset.
+A byte value of 65 to 128 is reduced by 64, multiplied by four, and
+subtracted from the offset.
+A byte value of 129 to 255 is reduced by 129, multiplied by the quantum
+of instruction size
+(e.g. two on the MC680x0),
+and added to the current PC without changing the offset.
+After any of these operations, the instruction quantum is added to the PC.
+.PP
+A similar table, occupying
+.BR pcsz -bytes,
+is the next section in an executable; it is present for all architectures.
+The same algorithm may be run using this table to
+recover the absolute source line number from a given program location.
+The absolute line number (starting from zero) counts the newlines
+in the C-preprocessed source seen by the compiler.
+Three symbol types in the main symbol table facilitate conversion of the absolute
+number to source file and line number:
+.RS
+.TP
+.B f
+source file name components
+.TP
+.B z
+source file name
+.TP
+.B Z
+source file line offset
+.RE
+.PP
+The
+.B f
+symbol associates an integer (the
+.B value
+field of the `symbol') with
+a unique file path name component (the
+.B name
+of the `symbol').
+These path components are used by the
+.B z
+symbol to represent a file name: the
+first byte of the name field is always 0; the remaining
+bytes hold a zero-terminated array of 16-bit values (in big-endian order)
+that represent file name components from
+.B f
+symbols.
+These components, when separated by slashes, form a file name.
+The initial slash of a file name is recorded in the symbol table by an
+.B f
+symbol; when forming file names from
+.B z
+symbols an initial slash is not to be assumed.
+The
+.B z
+symbols are clustered, one set for each object file in the program,
+before any text symbols from that object file.
+The set of
+.B z
+symbols for an object file form a
+.I history stack
+of the included source files from which the object file was compiled.
+The value associated with each
+.B z
+symbol is the absolute line number at which that file was included in the source;
+if the name associated with the
+.B z
+symbol is null, the symbol represents the end of an included file, that is,
+a pop of the history stack.
+If the value of the
+.B z
+symbol is 1 (one),
+it represents the start of a new history stack.
+To recover the source file and line number for a program location,
+find the text symbol containing the location
+and then the first history stack preceding the text symbol in the symbol table.
+Next, interpret the PC/line offset table to discover the absolute line number
+for the program location.
+Using the line number, scan the history stack to find the set of source
+files open at that location.
+The line number within the file can be found using the line numbers
+in the history stack.
+The
+.B Z
+symbols correspond to
+.B #line
+directives in the source; they specify an adjustment to the line number
+to be printed by the above algorithm.  The offset is associated with the
+first previous
+.B z
+symbol in the symbol table.
+.SH "SEE ALSO"
+.IR acid (10.1), 
+.IR 2a (10.1), 
+.IR 2l (10.1), 
+.IR inm (10.1)
+.SH BUGS
+There is no type information in the symbol table.
--- /dev/null
+++ b/man/10/acid
@@ -1,0 +1,373 @@
+.TH ACID 10.1
+.SH NAME
+acid \- debugger
+.SH SYNOPSIS
+.B acid
+[
+.BI -l " libfile
+]
+[
+.B -wq
+] [
+.B -m
+.I machine
+] [
+.I pid
+]
+[
+.I textfile
+]
+.SH DESCRIPTION
+.I Acid
+is a programmable symbolic debugger.
+It can inspect one or more processes that share an address space.
+A program to be debugged may be specified by the process id of
+a running or defunct process,
+or by the name of the program's text file 
+.RB ( v.out
+by default).
+At the prompt,
+.I acid
+will store function definitions or print the value of expressions.
+Options are
+.TP .9i
+.B -w
+Allow the textfile to be modified.
+.TP
+.B -q
+Don't print variable renamings at startup.
+.TP
+.BI -l " library
+Load from 
+.I library
+at startup; see below.
+.TP
+.BI -m " machine
+Assume instructions are for the given CPU type
+(one of
+.BR 386 ,
+.BR 86 ,
+.BR 68020 ,
+.BR 960 ,
+.BR power ,
+.BR arm ,
+.BR mips ,
+.BR mipsco ,
+.BR sparc ,
+or
+.BR sunsparc )
+instead of using the magic number to select
+the CPU type.
+.PP
+At startup,
+.I acid
+obtains standard function definitions from the library file
+.BR /lib/acid/port ,
+architecture-dependent functions from
+.BR /lib/acid/$objtype ,
+user-specified functions from
+.BR $home/lib/acid ,
+and further functions from 
+.B -l
+files.
+Definitions in any file may override previously defined functions.
+If the function
+.IR acidinit ()
+is defined, it will be invoked after all modules have been loaded.
+See
+.IR 2c (10.1)
+for information about creating
+.I acid
+functions for examining data structures.
+.SS Language
+Symbols of the program being debugged become integer 
+variables whose values are addresses.
+Contents of addresses are obtained by indirection.
+Local variables are qualified by
+function name, for example
+.BR main:argv .
+When program symbols conflict with
+.I acid
+words, distinguishing 
+.B $
+signs are prefixed.
+Such renamings are reported at startup; option
+.B -q
+suppresses them.
+.PP
+Variable types
+.RI ( "integer, float, list, string" )
+and formats are inferred from assignments.
+Truth values false/true are attributed to zero/nonzero
+integers or floats and to empty/nonempty lists or strings.
+Lists are sequences of expressions surrounded by
+.BR {\^} 
+and separated by commas.
+.PP
+Expressions are much as in C,
+but yield both a value and a format.
+Casts to complex types are allowed.
+Lists admit the following operators, with
+subscripts counted from 0.
+.IP
+.BI head " list
+.br
+.BI tail " list
+.br
+.BI append " list", " element
+.br
+.BI delete " list", " subscript
+.PP
+Format codes are the same as in
+.IR db (10.1).
+Formats may be attached to (unary) expressions with
+.BR \e ,
+e.g.
+.BR (32*7)\eD .
+There are two indirection operators,
+.B *
+to address a core image,
+.B @
+to address a text file.
+The type and format of the result are determined by the format of the operand,
+whose type must be integer.
+.PP
+Statements are
+.IP
+.BI if " expr " then " statement " "\fR[ \fPelse\fI statement \fR]
+.br
+.BI while " expr " do " statement
+.br
+.BI loop " expr" , " expr " do " statement
+.br
+.BI defn " name" ( args ") {" " statement \fP}
+.br
+.BI local " name
+.br
+.BI return " expr
+.br
+.BR whatis " [ \fI name \fP]
+.PP
+Here is a partial list of functions; see the manual for a complete list.
+.TF asm(address)
+.TP
+.B stk()
+Print a stack trace for current process.
+.TP
+.B lstk()
+Print a stack trace with values of local variables.
+.TP
+.B gpr()
+Print general registers.
+Registers can also be accessed by name, for example
+.BR *R0 .
+.TP
+.B spr()
+Print special registers such as program counter and stack pointer.
+.TP
+.B fpr()
+Print floating-point registers.
+.TP
+.B regs()
+Same as
+.BR spr();gpr() .
+.TP
+.BI fmt( expr , format )
+Expression 
+.I expr
+with format given by the character value of expression
+.IR format .
+.TP
+.BI src( address )
+Print 10 lines of source around the program address.
+.TP
+.BI Bsrc( address )
+Get the source line for the program address
+into a window of a running
+editor
+and select it.
+(This works only on Plan 9, or a Unix-like system running `Plan 9 Ports'.)
+.TP
+.BI line( address )
+Print source line nearest to the program address.
+.TP
+.B source()
+List current source directories.
+.TP
+.BI addsrcdir( string )
+Add a source directory to the list.
+.TP
+.BI filepc( where )
+Convert a string of the form 
+.IB sourcefile : linenumber
+to a machine address.
+.TP
+.BI pcfile( address )
+Convert a machine address to a source file name.
+.TP
+.BI pcline( address )
+Convert a machine address to a source line number.
+.TP
+.BI bptab()
+List breakpoints set in the current process.
+.TP
+.BI bpset( address )
+Set a breakpoint in the current process at the given address.
+.TP
+.BI bpdel( address )
+Delete a breakpoint from the current process.
+.TP
+.B cont()
+Continue execution of current process and wait for it to stop.
+.TP
+.B step()
+Execute a single machine instruction in the current process.
+.TP
+.B func()
+Step repeatedly until after a function return.
+.TP
+.BI stopped( pid )
+This replaceable function is called automatically when the given process
+stops.
+It normally prints the program counter and returns to the prompt.
+.TP
+.BI asm( address )
+Disassemble 30 machine instructions beginning at the given address.
+.TP
+.BI mem( address , string )
+Print a block of memory
+interpreted according to a string of format codes.
+.TP
+.BI dump( address , n , string\fP)
+Like
+.BR mem (),
+repeated for 
+.I n
+consecutive blocks.
+.TP
+.BI print( expr , ... )
+Print the values of the expressions.
+.TP
+.BI newproc( arguments )
+Start a new process with arguments given as a string
+and halt at the first instruction.
+.TP
+.B new()
+Like 
+.IR newproc (),
+but take arguments (except
+.BR argv[0] )
+from string variable
+.BR progargs .
+.TP
+.B win()
+Like 
+.IR new (),
+but run the process in a separate window.
+.TP
+.BI start( pid )
+Start a stopped process.
+.TP
+.BI kill( pid )
+Kill the given process.
+.TP
+.BI setproc( pid )
+Make the given process current.
+.TP
+.BI rc( string )
+Escape to the shell,
+.....IR rc (10.1),
+to execute the command string.
+.SH EXAMPLES
+Start to debug
+.BR /bin/ls ;
+set some breakpoints; run up to the first one:
+.IP
+.EX
+% acid /bin/ls
+/bin/ls: mips plan 9 executable
+/lib/acid/port
+/lib/acid/mips
+acid: new()
+70094: system call  _main       ADD     $-0x14,R29
+70094: breakpoint   main+0x4    MOVW    R31,0x0(R29)
+acid: pid
+70094
+acid: argv0 = **main:argv\es
+acid: whatis argv0
+integer variable format s
+acid: *argv0
+/bin/ls
+acid: bpset(ls)
+acid: cont()
+70094: breakpoint  ls   ADD $-0x16c8,R29
+acid: 
+.EE
+.PP
+Display elements of a linked list of structures:
+.IP
+.EX
+complex Str { 'D' 0 val; 'X' 4 next; };
+complex Str s;
+s = *headstr;
+while s != 0 do{
+	print(s.val, "\en");
+	s = s.next;
+}
+.EE
+.PP
+Note the use of the
+.B .
+operator instead of
+.BR -> .
+.PP
+Display an array of bytes declared in C as
+.BR "char array[]" .
+.IP
+.EX
+*(array\es)
+.EE
+.PP
+This example gives
+.B array
+string format, then prints the string beginning at the address (in
+.I acid
+notation)
+.BR *array .
+.SH FILES
+.B /proc/*/text
+.br
+.B /proc/*/mem
+.br
+.B /proc/*/ctl
+.br
+.B /proc/*/note
+.br
+.B /lib/acid/$objtype
+.br
+.B /lib/acid/port
+.br
+.B $home/lib/acid
+.SH SOURCE
+.B /utils/acid
+.SH "SEE ALSO"
+.IR 2a (10.1),
+.IR 2c (10.1),
+.IR 2l (10.1),
+.IR mk (10.1),
+.IR db (10.1)
+.br
+Phil Winterbottom,
+``Acid Manual''.
+.SH DIAGNOSTICS
+At termination, kill commands are proposed
+for processes that are still active.
+.SH BUGS
+There is no way to redirect the standard input and standard output
+of a new process.
+.br
+Source line selection near the beginning of a file may pick
+an adjacent file.
+.br
+With the extant stepping commands, one cannot step through instructions
+outside the text segment and it is hard to debug across process forks.
--- /dev/null
+++ b/man/10/allocb
@@ -1,0 +1,314 @@
+.TH ALLOCB 10.2
+.SH NAME
+allocb, iallocb, freeb, freeblist, BLEN, blocklen, concatblock, copyblock, trimblock, packblock, padblock, pullblock, pullupblock, adjustblock, checkb \- data block management
+.SH SYNOPSIS
+.ta \w'\fLBlock* 'u
+.B
+Block*	allocb(int size)
+.PP
+.B
+Block*	iallocb(int size)
+.PP
+.B
+void	freeb(Block *b)
+.PP
+.B
+void	freeblist(Block *b)
+.PP
+.B
+long	BLEN(Block *b)
+.PP
+.B
+int	blocklen(Block *b)
+.PP
+.B
+Block*	concatblock(Block *b)
+.PP
+.B
+Block*	copyblock(Block *b, int n)
+.PP
+.B
+Block*	trimblock(Block *b, int offset, int n)
+.PP
+.B
+Block*	packblock(Block *b)
+.PP
+.B
+Block*	padblock(Block *b, int n)
+.PP
+.B
+int	pullblock(Block **bph, int n)
+.PP
+.B
+Block*	pullupblock(Block *b, int n)
+.PP
+.B
+Block*	adjustblock(Block *b, int n)
+.PP
+.B
+void	checkb(Block *b, char *msg)
+.SH DESCRIPTION
+A
+.B Block
+provides a receptacle for data:
+.IP
+.EX
+.DT
+typedef
+struct Block
+{
+    Block*  next;
+    Block*  list;
+    uchar*  rp;     /* first unconsumed byte */
+    uchar*  wp;     /* first empty byte */
+    uchar*  lim;    /* 1 past the end of the buffer */
+    uchar*  base;   /* start of the buffer */
+    void    (*free)(Block*);
+    ulong   flag;
+} Block;
+.EE
+.PP
+Each
+.B Block
+has an associated buffer, located at
+.BR base ,
+and accessed via
+.B wp
+when filling the buffer, or
+.B rp
+when fetching data from it.
+Each pointer should be incremented to reflect the amount of data written or read.
+A
+.B Block
+is empty when
+.B rp
+reaches
+.BR wp .
+The pointer
+.B lim
+bounds the allocated space.
+Some operations described below accept lists of
+.BR Block s,
+which are
+chained via their
+.B next
+pointers, with a null pointer ending the list.
+.B Blocks
+are usually intended for a
+.B Queue
+(see
+.IR qio (10.2)),
+but can be used independently.
+.PP
+A
+.B Block
+and its buffer are normally allocated by one call to
+.IR malloc (10.2)
+and aligned on an 8 byte (\fLBY2V\fP) boundary.
+Some devices with particular allocation constraints
+(eg, requiring certain addresses for DMA) might allocate their own
+.B Block
+and buffer;
+.B free
+must then point to a function that can deallocate the specially allocated
+.BR Block .
+.PP
+Many
+.B Block
+operations cannot be used in interrupt handlers
+because they either
+.IR sleep (10.2)
+or raise an
+.IR error (10.2).
+Of operations that allocate blocks, only
+.IR iallocb
+is usable.
+.PP
+.I Allocb
+allocates a
+.B Block
+of at least
+.IR size
+bytes.
+The block
+is initially empty:
+.B rp
+and
+.B wp
+point to the start of the data.
+If it cannot allocate memory,
+.I allocb
+raises an
+.IR error (10.2);
+it cannot be used by an interrupt handler.
+.PP
+.IR Iallocb
+is similar to
+.IR allocb
+but is intended for use by interrupt handlers,
+and returns a null pointer if no memory is available.
+It also limits its allocation to a quota allocated at system initialisation to interrupt-time buffering.
+.PP
+.I Freeb
+frees a single
+.B Block
+(and its buffer).
+.PP
+.I Freeblist
+frees the whole
+list of blocks headed by
+.IR b .
+.PP
+.I BLEN
+returns the number of unread bytes in a single block
+.IR b ;
+it is implemented as a macro.
+.PP
+.I Blocklen
+returns the number of bytes of unread data in the whole list of blocks headed by
+.IR b .
+.PP
+.I Concatblock
+returns
+.I b
+if it is not a list, and otherwise
+returns a single
+.B Block
+containing all the data in the list of blocks
+.IR b ,
+which it frees.
+.PP
+.I Copyblock
+by contrast returns a single
+.B Block
+containing a copy of the first
+.I n
+bytes of data in the block list
+.IR b ,
+padding with zeroes if the list contained less than
+.I n
+bytes.
+The list
+.I b
+is unchanged.
+.PP
+.I Padblock
+can pad a single
+.B Block
+at either end, to reserve space for protocol headers or trailers.
+If
+.IR n ≥ 0 ,
+it inserts
+.I n
+bytes at the start of the block,
+setting the read pointer
+.B rp
+to point to the new space.
+If
+.IR n < 0 ,
+it adds
+.I n
+bytes at the end of the block,
+leaving the write pointer
+.B wp
+pointing at the new space.
+In both cases, it allocates a new
+.B Block
+if necessary, freeing the old, and
+it always returns a pointer to the resulting
+.BR Block .
+.PP
+.I Trimblock
+trims the list
+.I b
+to contain no more than
+.I n
+bytes starting at
+.I offset
+bytes into the data of the original list.
+It returns a new list, freeing unneeded parts of the old.
+If no data remains, it returns a null pointer.
+.PP
+.I Packblock
+examines each
+.B Block
+in the list
+.IR b ,
+reallocating any block in the list that has four times more available space than actual data.
+It returns a pointer to the revised list.
+.PP
+.I Pullblock
+discards up to
+.I n
+bytes from the start of the list headed by
+.BI * bph \f1.\f0
+Unneeded blocks are freed.
+.I Pullblock
+sets
+.BI * bph
+to point to the new list head
+and returns the number of bytes discarded (which might be less than
+.IR n ).
+It is used by transport protocols to discard ack'd data at
+the head of a retransmission queue.
+.PP
+.I Pullupblock
+rearranges the data in the list of blocks
+.I b
+to ensure that there are at least
+.I n
+bytes of contiguous data in the first block,
+and returns a pointer to the new list head.
+It frees any blocks that it empties.
+It returns a null pointer if there is not enough data in the list.
+.PP
+.I Adjustblock
+ensures that the block
+.I b
+has at least
+.I n
+bytes of data, reallocating or padding with zero if necessary.
+It returns a pointer to the new
+.BR Block .
+(If
+.I n
+is negative, it frees the block and returns a null pointer.)
+.PP
+.I Checkb
+does some consistency checking of
+the state of
+.IR b ;
+a
+.IR panic (10.2)
+results if things look grim.
+It is intended for internal use by the queue I/O routines (see
+.IR qio (10.2))
+but could be used elsewhere.
+.PP
+The only functions that can be called at interrupt level are
+.IR iallocb ,
+.IR freeb ,
+.IR freeblist ,
+.IR BLEN ,
+.IR blocklen ,
+.IR trimblock
+and
+.IR pullupblock .
+The others allocate memory and can potentially block.
+.SH DIAGNOSTICS
+Many functions directly or indirectly can raise an
+.IR error (10.2),
+and callers must therefore provide for proper error recovery
+as described therein to prevent memory leaks and other bugs.
+Except for
+.IR iallocb ,
+any functions that allocate new blocks or lists
+are unsuitable for use by interrupt handlers.
+.IR Iallocb
+returns a null pointer when it runs out of memory.
+.SH SOURCE
+.B /os/port/qio.c
+.br
+.B /emu/port/qio.c
+.SH SEE ALSO
+.IR qio (10.2)
--- /dev/null
+++ b/man/10/ar
@@ -1,0 +1,98 @@
+.TH AR 10.6
+.SH NAME
+ar \- archive (library) file format
+.SH SYNOPSIS
+.B #include <ar.h>
+.SH DESCRIPTION
+The archive command
+.IR iar (10.1)
+is used to combine several files into
+one.
+Archives are used mainly as libraries to be searched
+by the loaders
+.IR 2l (10.1)
+.I et al.
+.PP
+A file produced by
+.I ar
+has a magic string at the start,
+followed by the constituent files, each preceded by a file header.
+The magic number and header layout as described in the
+include file are:
+.IP
+.EX
+.ec %
+#define ARMAG   "!<arch>\en"
+#define SARMAG  8
+
+#define ARFMAG  "`\en"
+
+struct ar_hdr {
+    char    name[16];
+    char    date[12];
+    char    uid[6];
+    char    gid[6];
+    char    mode[8];
+    char    size[10];
+    char    fmag[2];
+};
+#define SAR_HDR 60
+.ec \
+.EE
+.LP
+The
+.B name
+is a blank-padded string.
+The
+.L fmag
+field contains
+.L ARFMAG
+to help verify the presence of a header.
+The other fields are left-adjusted, blank-padded numbers.
+They are decimal except for
+.LR mode ,
+which is octal.
+The date is the modification date of the file (see
+.IR sys-stat (2))
+at the time of its insertion into the archive.
+The mode is the low 9 bits of the file permission mode.
+The length of the header is
+.LR SAR_HDR .
+Because the
+.L ar_hdr
+structure is padded in an architecture-dependent manner,
+the structure should never be read or written as a unit;
+instead, each field should be read or written independently.
+.PP
+Each file begins on an even (0 mod 2) boundary;
+a newline is inserted between files if necessary.
+Nevertheless 
+.B size
+reflects the
+actual size of the file exclusive of padding.
+.PP
+When all members of an archive are object files of
+the same architecture,
+.B ar
+automatically adds an extra file, named
+.BR __.SYMDEF ,
+as the first member of the archive.  This file
+contains an index used by the loaders to locate all
+externally defined text and data symbols in the archive.
+.PP
+There is no provision for empty areas in an archive
+file.
+.SH "SEE ALSO"
+.IR iar (10.1), 
+.IR 2l (10.1), 
+.IR inm (10.1),
+.IR sys-stat (2)
+.SH BUGS
+The
+.B uid
+and
+.B gid
+fields are unused.
+They provide compatibility with Unix
+.I ar
+format.
--- /dev/null
+++ b/man/10/atoi
@@ -1,0 +1,125 @@
+.TH ATOI 10.2
+.SH NAME
+atoi, atol, charstod, strtod, strtol, strtoul, strtoll \- convert text to numbers
+.SH SYNOPSIS
+.ta \w'\fLdouble 'u
+.B
+int	atoi(char *nptr)
+.PP
+.B
+long	atol(char *nptr)
+.PP
+.B
+double	charstod(int (*f)(void *), void *a)
+.PP
+.B
+double	strtod(char *nptr, char **rptr)
+.PP
+.B
+long	strtol(char *nptr, char **rptr, int base)
+.PP
+.B
+ulong	strtoul(char *nptr, char **rptr, int base)
+.PP
+.B
+vlong	strtoll(char *nptr, char **rptr, int base)
+.SH DESCRIPTION
+.IR Atoi
+and
+.I atol
+convert a string pointed to by
+.I nptr
+to integer, and long integer
+representation respectively.
+The first unrecognized character ends the string.
+Leading C escapes are understood, as in
+.I strtol
+with
+.I base
+zero.
+.PP
+.I Atoi
+and
+.I atol
+recognize an optional string of tabs and spaces,
+then an optional sign, then a string of
+decimal digits.
+.PP
+.IR Strtod ,
+.IR strtol ,
+.IR strtoul ,
+and
+.I strtoll
+behave similarly to 
+.I atol
+and, if
+.I rptr
+is not zero, set
+.I *rptr
+to point to the input character
+immediately after the string converted.
+.PP
+.I Strtod
+recognizes an optional string of tabs and spaces,
+then an optional sign, then
+a string of digits optionally containing a decimal
+point, then an optional 
+.L e
+or 
+.L E
+followed
+by an optionally signed integer.
+.PP
+.IR Strtol ,
+.I strtoul
+and
+.I strtoll
+interpret the digit string in the specified
+.IR base ,
+from 2 to 36,
+each digit being less than the base.
+Digits with value over 9 are represented by letters,
+a-z or A-Z.
+If
+.I base
+is 0, the input is interpreted as an integral constant in
+the style of C (with no suffixed type indicators):
+numbers are octal if they begin with
+.LR 0 ,
+hexadecimal if they begin with
+.L 0x
+or
+.LR 0X ,
+otherwise decimal.
+.I Strtoul
+does not recognize signs.
+.PP
+.I Charstod
+interprets floating point numbers in the same syntax as
+.IR strtod ,
+but it gets successive characters by calling
+.BR (*\fIf\fP)(\f2a\f5) .
+The last call to
+.I f
+terminates the scan, so it must have returned a character that
+is not a legal continuation of a number.
+Therefore, it may be necessary to back up the input stream one character
+after calling
+.IR charstod .
+.SH SOURCE
+.B /libkern/atol.c
+.br
+.B /libkern/charstod.c
+.br
+.B /libkern/strtod.c
+.br
+.B /libkern/strtol.c
+.br
+.B /libkern/strtoul.c
+.br
+.B /libkern/utils.c
+.SH DIAGNOSTICS
+Zero is returned if the beginning of the input string is not
+interpretable as a number; even in this case,
+.I rptr
+will be updated.
--- /dev/null
+++ b/man/10/c2l
@@ -1,0 +1,231 @@
+.TH C2L 10.1 
+.SH NAME
+c2l \- C to Limbo translator
+.SH SYNOPSIS
+.B c2l
+[
+.I option ...
+]
+.I file
+.SH DESCRIPTION
+.I C2l
+translates the named C
+.I file
+into Limbo. The translated code should be almost always syntactically correct
+but will certainly never be semantically correct as certain constructs in C
+(strings for example) are almost impossible to convert automatically into Limbo.
+Otherwise it tries to do a good job of translating the C constructs that have some
+sort of equivalence in Limbo. The C ternary
+.B ?:
+operator is replaced where possible.
+C library calls are mapped to calls to the Limbo system module, maths module or
+the provided Limbo libc modules. Some library calls, such as malloc, are instead
+mapped directly into Limbo wherever possible.
+.PP
+Once a translation has been made, running the 
+.IR limbo (1)
+compiler on the resulting output should pick out the areas where hand
+editing is required.
+.PP
+.I C2l
+normally puts all mapped C code (plus that from included files) into a
+single .b file.
+.PP
+The options to
+.I c2l
+are:
+.TP
+.B -p
+Use an ANSI preprocessor in place of the internal one.
+.TP
+.BI -D name=def
+.br
+.ns
+.TP
+.BI -D name
+Define the
+.I name
+to the preprocessor,
+as if by
+.LR #define .
+If no definition is given, the name is defined as
+.LR 1 .
+.TP
+.BI -I dir
+An
+.L #include
+file whose name does not begin with 
+slash
+or is enclosed in double quotes
+is always
+sought first in the directory 
+of the
+.I file
+argument.  If this fails, or the name is enclosed in
+.BR <> ,
+it is then sought
+in directories named in 
+.B -I
+options,
+then in
+.BR /sys/include ,
+and finally in
+.BR /$objtype/include .
+.TP
+.B -m
+Put the mapped code of any included
+.B .h
+files into its corresponding
+.B .m
+file instead of
+the
+.B .b
+file.
+.TP
+.B -i
+Send the mapped code of any included
+.B .h
+files to
+.BR /dev/null .
+.TP
+.B -l
+Send the mapped code of any non-local included
+.B .h
+files to
+.BR /dev/null . 
+.TP
+.B -c
+Just generate code corresponding to the C code ie don't include any prologue
+or epilogue code such as an implement header, include declarations, module
+declarations or an init function.
+.TP 
+.B -v
+Outputs any warnings to standard error as well as putting them in the output source.
+.TP
+.B -s
+Map C strings to NUL-terminated arrays of bytes in Limbo. This just about preserves
+the semantics of strings and makes the process of hand editing much easier. It is
+useful as a first attempt at translation. In this case the module
+.B /module/libc0.m
+is used in place of the standard one
+.B /module/libc.m.
+.TP
+.B -S
+Map
+.B "char*"
+in C to string in Limbo. Incompatible with the
+.B -s
+option.
+.TP
+.B -M
+Indicates this file is the one containing the C main program. Used with the
+.B -q
+option below when 
+.I c2l
+does not always know this until it's too late.
+.TP
+.B -q
+This reduces the number of passes that
+.I c2l
+makes over the C code. It makes it faster but more liable to miss some
+transformations. Cyclic data structures might not be detected.
+.TP
+.B -a
+For functions which are passed the address of a scalar typed (ie not a structure
+or union) expression as a parameter, pass the expression itself and
+rewrite the function and all calls of it to return the expression. For example :-
+.PP
+.EX
+		int
+		f(int x, int *y)
+		{
+			*y = x*x*x;
+			return x*(*y);
+		}
+
+		void
+		g()
+		{
+			int p3, p4;
+
+			p4 = f(1729, &p3);
+		}
+.EE
+.PP
+		becomes
+.PP
+.EX
+		f(x: int, y: int): (int, int)
+		{
+			y = x*x*x;
+			return (x*y, y);
+		}
+
+		g()
+		{
+			p3, p4: int;
+
+			(p4, p3) = f(1729, p3);
+		}
+.EE
+.PP
+.I C2l
+runs the preprocessor on the C code before starting translation. As
+a special case it will convert definitions of constants into Limbo constant declarations.
+It makes no attempt to convert any definitions into function declarations.
+.PP
+Identifier names that clash with Limbo keywords have letter
+.B x
+appended so, for example,
+a structure member called
+.B type
+would become
+.BR typex .
+.PP
+Warning messages preceded by the acronym TBA (to be addressed) are issued for
+NUL bytes in strings, ... as an argument, array indices in declarations, use of void type, use of unions, bit fields, use of address operator, negative array
+indices, element specifiers, initial values in Limbo modules, labels, gotos and case
+statement fall through.
+.PP
+The C types
+.B char
+and
+.B "unsigned char"
+are mapped to the Limbo
+.B byte
+type.
+The C types short, unsigned short, int, unsigned int, long and unsigned long
+are mapped to the Limbo int type. The C types long long and unsigned long long
+are mapped to the Limbo big type. Finally the C types float and double are mapped
+to the Limbo real type.
+.PP
+Anonymous C structures and unions map to a name of the form <module>_adt_<num> where module is the name of the module which is, in turn, derived from the file name. Anonymous member names in strucures and unions have a
+name of the form anon_<num>. Finally,temporary variables generated by
+.I c2l
+have a name of the form tmp_<num>. In all cases <num> is a unique identifier.
+.SH SOURCE
+.TF /utils/c2l
+.TP
+.B /module/libc.m
+.TP
+.B /module/libc0.m
+.TP
+.B /appl/lib/libc.b
+.TP
+.B /appl/lib/libc0.b
+.TP
+.SH "SEE ALSO"
+.IR 2c (10.1),
+.IR limbo (1)
+.SH BUGS
+.I C2l
+is not a pretty printer. It has its own idea of how Limbo should be laid out.
+.PP
+.I C2l
+may well crash if given invalid C code.
+.PP
+.I c2l -a
+does not always do all possible conversions.
+
+
+
--- /dev/null
+++ b/man/10/conf
@@ -1,0 +1,335 @@
+.TH CONF 10.6
+.SH NAME
+conf \- native and hosted kernel configuration file
+.SH DESCRIPTION
+Native and hosted Inferno kernels are built for a given target
+.I platform
+in the host environment in directory
+.BI /os/ platform
+or
+.BI /emu/ platform .
+Existing
+platforms include
+.B pc
+and
+.B ipaq
+for native kernels and
+.BR Plan9 ,
+.BR Linux ,
+.B Nt
+(for all versions of Windows),
+and
+.BR Solaris ,
+amongst others.
+Each
+.I platform
+can have different kernels with different configurations.
+A given configuration is built in the platform's directory using the
+.IR mk (10.1)
+command:
+.IP
+.EX
+mk 'CONF=\fIconf\fP'
+.EE
+.PP
+where
+.I conf
+is a text file that specifies drivers, protocols and other parameters for that
+particular kernel:
+a parts list.
+The result of a successful
+.I mk
+is
+an executable or bootable file with a name determined by the
+.IR platform 's
+.BR mkfile ,
+typically
+.BI i conf
+for all native platforms,
+.BI $O. conf
+for Plan 9, Unix and clones,
+and
+.BI i conf .exe
+for Windows.
+.PP
+A kernel configuration file has several sections of the form
+.IP
+.EX
+.I "label"
+.IR "	item" " [ " "subitem ..." " ]"
+\&	...
+.EE
+.PP
+Each section begins with a
+.I label
+at the start of a line, which names a configuration
+category, followed by
+a list of each
+.I item
+to select from that category,
+one line per item, with white space (ie, blank or tab) at the start of the line.
+An
+.I item
+line can optionally list one or more
+.I subitems
+that must be included in the kernel to support it.
+A line that starts with a
+.L #
+is a comment.
+Empty lines are ignored.
+.PP
+.I Labels
+are chosen from the following set, listed in the order
+in which they conventionally appear in a configuration file:
+.TF etherxx
+.TP
+.B dev
+Device drivers
+.TP
+.B ip
+IP protocols (native kernels only) taken from
+.B ../ip
+.TP
+.B link
+Hardware-specific parts of device drivers.
+.TP
+.B misc
+Architecture-specific files; specific VGA and SCSI interfaces
+.TP
+.B lib
+Libraries to link with the kernel
+.TP
+.B mod
+Builtin Dis modules
+.TP
+.B port
+Portable components (other than drivers) from
+.B ../port
+.TP
+.B code
+C code and declarations to include as-is in the generated configuration file
+.TP
+.B init
+Dis init program
+.TP
+.B root
+List of files and directories to put in the
+.IR root (3)
+file system
+.PD
+.PP
+When an
+.I item
+is listed
+under a given
+.I label
+it causes a corresponding component to be included in the kernel.
+The details depend on the
+.IR label ,
+as discussed below.
+Each
+.I subitem
+represents a kernel subcomponent required by the corresponding
+.IR item .
+Both items and subitems can be either portable (platform-independent)
+or platform-specific.
+The source file for a given item or subitem
+is sought in the platform-directory
+(for platform-specific code), and
+in directories
+.BR ../port
+and
+.BR ../ip ,
+under control of the platform's
+.BR mkfile
+and
+.B ../port/portmkfile
+(which is included by
+.BR mkfile ).
+Resulting object files are left in the
+.I platform
+directory.
+.PP
+Outside the
+.B dev
+section,
+each item and subitem
+.I x
+causes the kernel image to include the code compiled from
+.IB x .c ,
+(or
+.IB x .s
+or
+.IB x .S
+for assembly-language support),
+or
+.IB portdir / x .c ,
+where
+.I portdir
+is one of the portable directories mentioned above.
+In the
+.B dev
+section, an item
+.I x
+corresponds instead to the driver source file
+.BI dev x .c
+in the current (platform-specific)
+directory or a portable driver
+.IB portdir /dev x .c .
+Subitems are handled as in any other section.
+Typically they are auxiliary files that are needed by the associated driver.
+.PP
+For instance, in a native kernel
+the portable driver for the
+.B draw
+device uses platform-specific code from
+.BR screen.c .
+That can be represented as follows:
+.IP
+.EX
+dev
+	draw	screen
+.EE
+.PP
+Each item
+.I x
+in the
+.B ip
+section
+corresponds to a protocol implementation compiled from
+.BI ../ip/ x .c .
+Any subitems
+are dealt with in the same way as in the
+.B dev
+section.
+.PP
+The
+.B link
+section provides a way for hardware-specific
+parts of drivers to link at runtime to the hardware-invariant part of a device
+drivers.
+For each item
+.IR x ,
+the kernel will call the function
+.IB x link
+during its initialisation.
+Typically that function makes itself known to the device driver by
+calling a function provided by that driver,
+passing the address of a interface-specific data structure or linkage table.
+For example,
+.B ethersmc
+is an interface-specific component:
+.IP
+.EX
+link
+	\fR...\fP
+	ethersmc
+.EE
+.PP
+and its source file
+.B ethersmc.c
+provides a function
+.B ethersmclink
+that
+calls
+.B addethercard
+in the interface-invariant part of the driver,
+.BR devether.c :
+.IP
+.EX
+void
+ethersmclink(void)
+{
+	addethercard("smc91cXX", reset);
+}
+.EE
+.PP
+Similarly, during kernel initialisation, for each item
+.I x
+in the
+.B mod
+section, the kernel calls the function
+.IB x init ,
+to initialise the corresponding built-in Limbo module.
+.PP
+The
+.B init
+section selects the first Dis program run by the system.
+For native kernels, a given item
+.I x
+refers to
+.BI ../init/ x .dis ,
+which is automatically built from
+.BI ../init/ x .b .
+For hosted kernels,
+.B emuinit
+is normally used, referring to
+.BR /dis/emuinit.dis .
+.PP
+The
+.B lib
+section lists the libraries to include when linking the kernel,
+in an order that satisfies any dependencies amongst them.
+Each item
+.I x
+corresponds to
+.BI /$SYSTARG/$OBJTYPE/lib x .a ,
+a target-specific library
+produced by compiling the C source code in
+.BI /lib item,
+where
+.B SYSTARG
+and
+.B OBJTYPE
+are set in
+.B mkfile
+to the target system and object types.
+.PP
+An item in the
+.B root
+section
+has one of the forms:
+.IP
+.EX
+.I name
+.I "name source"
+.EE
+.PP
+where
+.I name
+and
+.I source
+are both absolute path names rooted at the Inferno source tree.
+The kernel's initial root file system (see
+.IR root (3))
+will contain a file or directory with the given
+.IR name .
+.I Name
+must exist in the Inferno root, or an existing
+.I source
+file must be named.
+In either case,
+if the existing name refers to a file, the file in the root file system will have that file's current contents.
+If it is a directory, the root file file system will have a directory with that name,
+but the directory will contain only those names listed in
+the configuration file as belonging to that directory.
+.I Source
+is often
+.L /
+to force a target
+.I name
+to be a directory.
+.SH FILES
+.B /emu/port/mkdevc
+.br
+.B /emu/port/mkdevlist
+.br
+.B /emu/port/mkroot
+.br
+.B /os/port/mkdevc
+.br
+.B /os/port/mkdevlist
+.br
+.B /os/port/mkroot
+.SH SEE ALSO
+.IR mk (10.1)
--- /dev/null
+++ b/man/10/delay
@@ -1,0 +1,36 @@
+.TH DELAY 10.2
+.SH NAME
+delay, microdelay, addclock0link \- small delays, clock interrupts
+.SH SYNOPSIS
+.ta \w'\fLvoid 'u
+.B
+void	delay(int n)
+.PP
+.B
+void	microdelay(int n)
+.PP
+.B
+void	addclock0link(void(*clockf)(void))
+.SH DESCRIPTION
+.I Delay
+busy waits for
+.I n
+milliseconds, forced to be at least one millisecond.
+.PP
+.I Microdelay
+is similar, but busy waits for
+.IR n
+microseconds.
+.PP
+For delays on the order of clock ticks,
+.I tsleep
+(see
+.IR sleep (10.2))
+provides a better alternative to the busy waiting of these routines.
+.PP
+.I Addclock0link
+adds
+.I clockf
+to a list of functions to be executed at each clock interrupt.
+.SH SEE ALSO
+.IR sleep (10.2)
--- /dev/null
+++ b/man/10/dev
@@ -1,0 +1,435 @@
+.TH DEV 10.2
+.SH NAME
+Dev \- device driver interface
+.SH SYNOPSIS
+.EX
+struct Dev
+{
+    int dc;
+    char*   name;
+
+    void    (*reset)(void);        /* native only */
+    void    (*init)(void);
+    void    (*shutdown)(void);     /* native */
+    Chan*   (*attach)(char *spec);
+    Walkqid*  (*walk)(Chan *c, Chan *nc, char **name, int nname);
+    int     (*stat)(Chan *c, uchar *db, int dbsize);
+    Chan*   (*open)(Chan *c, int mode);
+    void    (*create)(Chan *c, char *name, int mode, ulong perm);
+    void    (*close)(Chan *c);
+    long    (*read)(Chan *c, void *buf, long nbytes, vlong offset);
+    Block*  (*bread)(Chan *c, long nbytes, ulong offset);
+    long    (*write)(Chan *c, void*, long, vlong offset);
+    long    (*bwrite)(Chan *c, Block *b, ulong offset);
+    void    (*remove)(Chan *c);
+    int     (*wstat)(Chan *c, uchar *db, int dbsize);
+    void    (*power)(int on);        /* native only */
+    int     (*config)(int on, char *spec, DevConf *cf); /* native */
+};
+.EE
+.SH DESCRIPTION
+Every device driver serves a unique name space that represents to the corresponding device(s).
+Applications act on the space using the operations of
+.IR sys-bind (2),
+.IR sys-open (2),
+.IR sys-read (2),
+.IR sys-stat (2),
+and other system calls.
+Within the kernel, the
+.B Dev
+structure defines the interface between the kernel and a device driver for
+all operations on that driver's name space.
+.PP
+.B Dev
+identifies the driver, and lists a set of C functions that are the driver's operations.
+Most are operations on the
+.B Chan
+type that is the kernel representation of a file or directory active in a name space.
+The kernel converts system calls acting on file descriptors into calls to a device's
+.B Dev
+operations acting on channel values.
+All channel values presented through the
+.B Dev
+interface are associated with the corresponding device driver:
+for channel
+.IR c ,
+.IB c ->type
+specifies that driver.
+Within the driver, the
+.IB c ->qid.path
+of a channel
+.I c
+identifies a file in the driver's name space, or even a client-specific instance of a file
+(eg, for multiplexors such as
+.IR ip (3)).
+The interpretation of the
+.B path
+is completely determined by the driver.
+.PP
+A device driver in the source file
+.BI dev x .c
+exports an initialised instance of
+.BI "Dev " x devtab .
+For instance,
+.B devcons.c
+contains the global initialiser:
+.IP
+.EX
+Dev consdevtab = {
+    'c',
+    "cons",
+
+    devreset,
+    consinit,
+    devshutdown,
+    consattach,
+    conswalk,
+    consstat,
+    consopen,
+    devcreate,
+    consclose,
+    consread,
+    devbread,
+    conswrite,
+    devbwrite,
+    devremove,
+    devwstat,
+};
+.EE
+.PP
+The kernel accesses the driver only through its
+.B Dev
+structure, and consequently entry points such as
+.BR consinit ,
+.BR consread ,
+etc. can (and should) be declared
+.BR static ,
+and thus local to the file.
+.PP
+The following elements of
+.B Dev
+identify the driver:
+.TP
+.B dc
+The device's type, represented by a Unicode character (`rune') that must be unique
+amongst those in a given kernel (and ideally for a given platform).
+Its value is the value of
+.B Dir.dtype
+in the result of a
+.IR sys-stat (2)
+applied to any file in the device.
+.TP
+.B name
+The name that identifies the driver in a kernel configuration file and in
+.B /dev/drivers
+(see
+.IR cons (3)).
+.PP
+All the other entries are functions.
+In many cases, the values given in a device's
+.B Dev
+will be the default operations provided by
+.IR devattach (10.2).
+.TP
+.B reset()
+Called once during system initialisation by the native
+kernel's
+.B main
+after initialising all supporting subsystems, including memory allocation, traps, screen, MMU (if used),
+but with interrupts disabled, and before any kernel process environment has been established.
+Typically used on some platforms to force some devices into a sane state
+before interrupts are enabled.
+.TP
+.B init()
+Called once during system initialisation in the context of the first kernel process,
+with interrupts enabled, before the virtual machine has been started.
+.TP
+.B shutdown()
+Called once in native kernels during system shut down.
+Used on only a few platforms to force a device into a state that will allow it
+to function correctly during and after a soft reboot (eg, without doing a full system hardware reset).
+.TP
+.BI attach( spec )
+Called on each new attach to the device (eg, a reference to
+.BI # c
+by
+.IR sys-bind (2)).
+.I Spec
+is the string following the device character and before a subsequent
+.RB ` / '
+in the bind request.
+It is the empty string for most devies.
+If the attach is successful,
+.B attach
+should return a
+.B Chan
+the refers to the root of the tree served by the device driver.
+Normally, it will suffice to return the value of
+.IR devattach (10.2).
+.TP
+.BI walk( c\fP,\fP\ nc\fP,\fP\ name\fP,\fP\ nname )
+Walks existing channel
+.I c
+from its current position in the device tree to that specified by the
+path represented by
+.BR name[0] ,
+\&...
+.BR name[nname-1] .
+The driver must interpret
+.RB ` .. '
+as a walk from the current position one level up towards the root of the device tree.
+The result is represented by a dynamically-allocated
+.B Walkqid
+value,
+with contents as described in
+.IR devattach (10.2).
+Most drivers simply pass parameters on to
+.B devwalk
+in
+.IR devattach (10.2)
+and return its result.
+.TP
+.BI stat( c\fP,\fP\ db\fP,\fP\ nbytes )
+Fill
+.I db
+with
+.IR stat (5)
+data describing the file referenced by
+.IR c .
+.I Nbytes
+gives the size of
+.IR db ;
+if the data will not fit, return the value specified for
+.B convD2M
+in
+.IR styx (10.2).
+Most drivers simply pass parameters on to
+.B devstat
+in
+.IR devattach (10.2);
+a few fill a local copy of a
+.B Dir
+structure, and call
+.B convD2M
+to store the machine-independent representation in
+.IR db .
+.TP
+.BI open( c\fP,\fP\ mode )
+Open the file represented by
+.B Chan
+.IR c ,
+in the given
+.I mode
+(see
+.IR sys-open (2)),
+and if successful, return a
+.B Chan
+value representing the result
+(usually
+.IR c ).
+Many drivers simply apply
+.B devopen
+of
+.IR devattach (10.2).
+Exclusive use drivers might check and increment a reference count.
+.TP
+.BI create( c\fP,\fP\ name\fP,\fP\ mode\fP,\fP\ perm )
+.I C
+should be a directory.
+Create a new file
+.I name
+in that directory, with permissions
+.IR perm ,
+opened with the given
+.IR mode .
+If successful, make
+.I c
+refer to the newly created file.
+Most drivers return an error on all creation attempts,
+by specifying
+.B devcreate
+of
+.IR devattach (10.2)
+in the
+.B Dev
+table.
+.TP
+.BI close( c )
+Close channel
+.IR c .
+This must be implemented by all drivers; there is no default,
+although the function often is a no-op.
+Exclusive use drivers might decrement a reference count.
+.TP
+.BI read( c\fP,\fP\ buf\fP,\fP\ nbytes\fP,\fP\ offset )
+Implement a
+.IR sys-read (2)
+of
+.I nbytes
+of data from the given
+.I offset
+in file
+.IR c ,
+and if successful, place the data in
+.IR buf ,
+and return the number of bytes read,
+which must be no greater than
+.IR nbytes .
+Devices sometimes ignore the
+.IR offset .
+All device drivers must implement
+.BR read ;
+there is no default.
+Note that if
+.I c
+is a directory, the data has an array of
+.IR stat (5)
+data listing the directory contents, in the format prescribed by
+.IR read (5).
+Most drivers have
+.B devdirread
+of
+.IR devattach (10.2)
+do the work when
+.I c
+is the root directory of the device's tree.
+.TP
+.BI bread( c\fP,\fP\ nbytes\fP,\fP\ offset )
+Implement a
+.IR sys-read (2)
+of
+.I nbytes
+of data from the given offset in file
+.IR c ,
+and if successful return the data in a
+.B Block
+(see
+.IR allocb (10.2)
+and
+.IR qio (10.2)).
+Most drivers use the default
+.B devbread
+provided by
+.IR devattach(10.2),
+and nearly all ignore the
+.I offset
+in any case.
+Drivers that manipulate Blocks internally, such as
+.IR ip (3),
+.IR ssl (3)
+and similar protocol devices,
+and drivers that are likely to provide data to those devices,
+will provide a
+.B devbread
+implementation so as to reduce the number of times the data is copied.
+.TP
+.BI write( c\fP,\fP\ buf\fP,\fP\ nbytes\fP,\fP\ offset )
+Implement a write of
+.I nbytes
+of data from
+.I buf
+to file
+.IR c ,
+which must not be a directory,
+starting at the given byte
+.IR offset .
+Return the number of bytes actually written.
+There is no default, but drivers that do not
+implement writes to any of their files can simply call
+.B error(Eperm)
+to signal an error.
+.TP
+.BI bwrite( c\fP,\fP\ b\fP,\fP\ offset )
+Similar to the
+.B write
+entry point, but the data is contained in a
+.B Block
+.I b
+(see
+.IR allocb (10.2)).
+.I B
+should be freed before return, whether the driver signals an error or not.
+Most drivers use the default
+.B devbwrite
+from
+.IR devattach (10.2),
+which calls the driver's
+.B write
+entry point using the data in
+.IR b .
+Drivers that manipulate Blocks internally, such as
+.IR ip (3),
+.IR ssl (3)
+and similar protocol devices,
+will provide a
+.B devbwrite
+implementation so as to avoid copying the data needlessly.
+.TP
+.BI remove( c )
+Remove the file referenced by
+.IR c .
+Most drivers raise an error by using the default
+.B devremove
+from
+.IR devattach (10.2).
+.TP
+.BI wstat( c\fP,\fP\ db\fP,\fP\ dbsize )
+Change the attributes of file
+.IR c ,
+using the
+.IR stat (5)
+data in buffer
+.IR db ,
+which is
+.I dbsize
+bytes long.
+Usually a driver will use
+.B convM2D
+of
+.IR styx (10.2)
+to convert the data to a
+.B Dir
+structure, then apply the rules of
+.IR stat (5)
+to decide which attributes are to be changed (and whether the change is allowed).
+Most drivers simply return an error on all
+.B wstat
+requests by using the default
+.B devwstat
+from
+.IR devattach (10.2).
+.TP
+.BI power( on )
+Reserved for use in native kernels, to allow the kernel
+to power the device on and off for power-saving;
+.I on
+is non-zero if the device is being powered up, and
+zero if it is being powered down.
+The device driver should save the device state if necessary.
+Leave the
+.B Dev
+entry null for now.
+.TP
+.BI config( on\fP,\fP\ spec\fP,\fP\ cf )
+Reserved for use in native kernels to allow a device
+to be configured on and off dynamically.
+Leave the
+.B Dev
+entry null for now.
+.PD
+.PP
+The elements
+.IR reset ,
+.IR shutdown ,
+.IR power ,
+and
+.IR config
+are currently present only in the native kernels.
+.SH SEE ALSO
+.IR intro (2),
+.IR intro (5),
+.IR allocb (10.2),
+.IR devattach (10.2),
+.IR newchan (10.2),
+.IR qio (10.2)
+
--- /dev/null
+++ b/man/10/devattach
@@ -1,0 +1,698 @@
+.TH DEVATTACH 10.2
+.SH NAME
+devattach, devclone, devdir, devgen, devwalk, devdirread, devstat, devopen, devbread, devbwrite, devcreate, devremove, devwstat, devreset, devinit, devshutdown, openmode \- common device driver support
+.SH SYNOPSIS
+.ta \w'\fLBlock* 'u +10n
+.B
+typedef int
+.B
+Devgen(Chan *c, char *name, Dirtab *tab, int ntab, int i, Dir *dp)
+.PP
+.B
+Chan*	devattach(int tc, char *spec)
+.PP
+.B
+Chan*	devclone(Chan *c)
+.PP
+.B
+void	devdir(Chan *c, Qid qid, char *n, long length,
+.B
+		char *user, long perm, Dir *dp)
+.PP
+.B
+int	devgen(Chan *c, char *name, Dirtab *tab, int ntab,
+.B
+		int i, Dir *dp)
+.PP
+.B
+Walkqid* devwalk(Chan *c, Chan *nc, char **name, int nname,
+.B
+		Dirtab *tab, int ntab, Devgen *gen)
+.PP
+.B
+void	devstat(Chan *c, uchar *db, int n, Dirtab *tab,
+.B
+		int ntab, Devgen *gen)
+.PP
+.B
+long	devdirread(Chan *c, char *d, long n, Dirtab *tab,
+.B
+		int ntab, Devgen *gen)
+.PP
+.B
+Chan*	devopen(Chan *c, int omode, Dirtab *tab,
+.B
+		int ntab, Devgen *gen)
+.PP
+.B
+Block*	devbread(Chan *c, long n, ulong offset)
+.PP
+.B
+long	devbwrite(Chan *c, Block *bp, ulong offset)
+.PP
+.B
+void	devcreate(Chan*, char*, int, ulong)
+.PP
+.B
+void	devremove(Chan*)
+.PP
+.B
+void	devwstat(Chan*, uchar*, int)
+.PP
+.B
+void	devreset(void)
+.PP
+.B
+void	devinit(void)
+.PP
+.B
+void	devshutdown(void)
+.PP
+.B
+int	openmode(ulong mode)
+.SH DESCRIPTION
+Device drivers call these functions to carry out essential tasks and default actions.
+They do most of the name space management
+for a driver that serves a simple name space
+(eg, data and control files),
+leaving the driver to concentrate on the device-specific details
+of the I/O requests.
+More complex drivers also make good use of them at the leaves
+of their name space, and to help manage the
+.B Chan
+structures correctly.
+.PP
+A device has an associated
+.IR type ,
+represented as a Unicode character (`rune') that identifies the device
+inside and outside the kernel.
+It appears as the value of the
+.B type
+field in the
+.B Dir
+resulting from a
+.IR sys-stat (2)
+of any file provided by the device.
+A device is named outside the kernel using
+a path name starting with
+.B #
+followed by the device character
+(eg,
+.B c
+in
+.B #c
+for the console).
+Any subsequent characters before
+the next '/' or end of string is the `device specifier',
+interpreted solely by the device itself.
+.PP
+.I Devattach
+returns a new channel representing
+the root of the file tree
+corresponding to device type
+.IR tc ,
+with device specifier
+.IR spec .
+It is normally called by a driver's
+.I attach
+function (see
+.IR dev (10.2)).
+The
+.B qid
+for the new channel is
+.BR "(Qid){0,0,QTDIR}" ,
+suitable for a root directory for many devices, but
+a device driver is free to change it (provided the
+.B QTDIR
+bit remains in the
+.BR Qid.type ).
+.PP
+.I Devclone
+returns a new channel that is a copy of
+.IR c .
+An attempt to clone an open channel causes a
+.IR panic (10.2).
+.PP
+The
+.L Dir
+structure is shown below:
+.IP
+.EX
+typedef
+struct Dir
+{
+    /* system-modified data */
+    ushort  type;   /* server type */
+    uint    dev;    /* server subtype */
+    /* file data */
+    Qid     qid;    /* unique id from server */
+    ulong   mode;   /* permissions */
+    ulong   atime;  /* last read time */
+    ulong   mtime;  /* last write time */
+    vlong   length; /* file length */
+    char    *name;  /* last element of path */
+    char    *uid;   /* owner name */
+    char    *gid;   /* group name */
+    char    *muid;  /* last modifier name */
+} Dir;
+.EE
+.PP
+This
+.B Dir
+structure corresponds directly to the Limbo
+.B Dir
+adt described in
+.IR sys-stat (2).
+.PP
+Given a channel and assorted other information, 
+.I devdir
+initialises a Dir structure at
+.IR dp .
+.I Devdir
+supplies the following data itself:
+.RS
+.TF length
+.TP
+.B atime
+last access time (set to current time)
+.TP
+.B mtime
+last modification time (set to kernel creation date)
+.TP
+.B gid
+group name (set to
+.IR eve (10.2))
+.TP
+.B length
+length in bytes (set to zero, which
+is normal for most devices)
+.RE
+.PD
+.PP
+Note that
+.I devdir
+assigns the values of
+.I name
+and
+.I user
+directly to fields of
+.BI * dp,
+and consequently those values must remain valid until the last use of
+.BI * dp.
+(Sometimes that requires the use of an auxiliary buffer, such as
+.BR up->genbuf .)
+If channel
+.I c
+corresponds to a file descriptor on which 9P is served,
+.I devdir
+sets both the flag bit
+.B QTMOUNT
+in
+.IB dp ->qid.type
+and the flag bit
+.B DMMOUNT
+in
+.IB dp ->mode
+(see
+.IR sys-export (2)
+and
+.I mount
+in
+.IR sys-bind (2)).
+.PP
+A simple name space can be represented in a driver by an array of
+.B Dirtab
+structures.
+The array is typically static when the names and permissions
+are static, but can be dynamically allocated and initialised if required.
+The structure of
+.B Dirtab
+is shown below:
+.IP
+.EX
+typedef
+struct Dirtab
+{
+        char    name[KNAMELEN];
+        Qid     qid;
+        vlong   length;
+        long    perm;
+} Dirtab;
+.EE
+.PP
+The name
+.RB ` . '
+.I must
+appear as the first entry in a
+.B Dirtab
+if the default
+.I devgen
+function is used.
+On the other hand, the name
+.RB ` .. '
+must never appear in a
+.B Dirtab
+table.
+Drivers that support a directory hierarchy must walk up the hierarchy towards
+the root when their
+.I walk
+function receives
+.RB ` .. '
+as a file name component.
+The name
+.RB ` . '
+is never seen by a driver.
+.PP
+The
+.IR devdirread ,
+.IR devopen ,
+.IR devstat ,
+and
+.IR devwalk
+functions all take a
+.I gen
+function argument,
+of type
+.BR Devgen ,
+which they invoke to retrieve the items in
+a
+.B Chan
+that represents a directory.
+.I Gen
+takes a channel
+.I c
+(a directory),
+a file
+.I name
+(which is nil except during
+.IR devwalk ),
+an array of
+.B Dirtab
+structures
+.I tab
+of length
+.IR ntab ,
+and a table index
+.IR i .
+The functions calling
+.I gen
+expect it to place the
+.IR i 'th
+entry in the directory into
+.IR \f5*\fPdp .
+It should return 1
+if the call was successful,
+-1 if
+.I i
+is beyond the index of the last directory entry,
+or 0 if there is no entry at
+.IR i ,
+but there are entries beyond it.
+When
+.I i
+has the special value
+.B DEVDOTDOT
+then
+.I gen
+should set
+.IR \f5*\fPdp
+to reflect the parent of
+.IR c ;
+if
+.I c
+is a one-level device directory, then `..' is equivalent to `.'.
+Custom implementations of
+.I gen
+often ignore
+.IR devtab ,
+and instead return their own dynamically generated
+set of directory entries from some other source.
+Exceptionally, during
+.I devwalk
+a non-nil
+.I name
+is provided: it is the name being looked up, and a device-specific
+.I gen
+can short-circuit the search by returning -1 if the name does not exist,
+or filling in
+.IR \f5*\fPdp
+and returning 1 if it does exist.
+.PP
+The function
+.I devgen
+is compatible with
+.BR Devgen ;
+it returns the
+.IR i 'th
+entry in
+.IR devtab ,
+and can be used to provide a simple, static
+set of directory entries.
+.PP
+.I Devwalk
+walks channel
+.I c
+to the file in the device named by the path encoded in
+.IR name ,
+which is an array of strings of length
+.IR nname .
+It provides the interface to
+.IR walk (5)
+within the kernel, and that specification must be well understood to appreciate
+all the nuances of its interface.
+Fortunately, in nearly all device drivers, a device's
+.I walk
+function typically passes its parameters on to
+.I devwalk
+(adding the device's own
+.B Dirtab
+array as the the value of
+.IR tab ),
+and simply returning the result of
+.IR devwalk .
+.PP
+.I Devwalk
+walks
+.I c
+using the given set of names, and if the walk is successful, the
+channel
+.I nc
+will refer to the result of the walk
+(specifically,
+.IB nc ->qid
+is set to the Qid for the file).
+If
+.I nc
+is nil,
+.I devwalk
+will allocate a new channel itself, that is initially a clone of
+.IR c .
+As in
+.IR walk (5),
+.I devwalk
+can return a partial result,
+represented by
+a dynamically allocated value of the following structure:
+.IP
+.EX
+struct Walkqid
+{
+    Chan  *clone;
+    int   nqid;
+    Qid   qid[1];	/* actually nname in length */
+};
+.EE
+.PP
+The value must be freed after use.
+For each element of
+.I name ,
+.I devwalk
+passes
+the
+.I tab
+parameter to
+.I gen
+together with the currently-sought element of
+.IR name .
+If the first element is not found,
+.I devwalk
+returns nil; otherwise, it returns a
+.B Walkqid
+value in which
+.B nqid
+elements of the array
+.B qid
+are set to the qids (see
+.IR intro (5))
+of each valid element of
+.IR name .
+If all
+.I nname
+elements were successfully traversed, then
+.B nqid
+will have the value
+.IR nname ,
+and
+.B clone
+will refer to the result of the walk,
+which is either
+.I nc
+if given, or
+the new channel allocated by
+.IR devwalk .
+Otherwise, at least one element succeeded and
+.B nqid
+is less than
+.I nname
+and
+.B clone
+is nil.
+On an error or incomplete walk,
+the error string is set to the error that stopped the walk (eg,
+.B Enonexist
+or
+.BR Enotdir ).
+.PP
+.I Devstat
+fills the array of bytes
+.I db
+with data in the format produced by
+.IR stat (5)
+that describes the file
+referenced by channel
+.IR c ,
+which must have a corresponding entry
+returned by
+.IR gen
+(ie, an entry with matching
+.BR Qid.path ).
+If
+.I c
+is a communications channel connecting a 9P server to a current mount point,
+the
+.B DMMOUNT
+bit is set in the resulting
+.BR Dir.mode ,
+and
+.B QTMOUNT
+is set in
+.BR Dir.qid.type .
+As in
+.IR stat (5),
+the length of the data written to
+.I db
+varies; if more than
+.I n
+bytes are needed,
+.I devstat
+raises the
+.IR error (10.2)
+.BR Ebadarg .
+Otherwise, it returns the number of bytes in
+.I db
+actually used.
+.PP
+If an entry with the desired qid is not found in the table, but
+.I c
+corresponds to a directory
+(ie,
+.B QTDIR
+is set in
+.IR c\f5->qid.type\fP ),
+it is taken to be a
+.I stat
+of a notional directory containing the files listed in
+.IR tab .
+.I Dirstat
+then builds the corresponding Dir structure:
+its
+.B Dir.name
+is taken from
+.IR c\f5->path->elem\fP ;
+the length is
+.BI DIRLEN*nelem(tab) ;
+and
+.B Dir.perm
+is 0555 (read-execute for all).
+.PP
+.I Devdirread
+calls
+.I gen
+to obtain successive
+.B Dir
+structures representing entries in the open directory
+.IR c .
+These are converted to standard format (see
+.I convD2M
+in
+.IR styx (10.2))
+and placed in the buffer
+.IR b .
+It returns the number of bytes in the result.
+At most
+.I n
+bytes will be returned, in multiples of
+.BR DIRLEN .
+Because the kernel maintains the current offset in
+.IR c ,
+successive calls to
+.I devdirread
+return successive directory components.
+.PP
+.I Devopen
+is called to check and complete a request to open channel
+.I c
+for I/O according to
+.IR omode
+(the open mode of
+.IR sys-open (2)).
+It calls
+.I gen
+to obtain successive directory entries
+which it searches
+for a Qid matching that of
+.IR c ,
+and ensures that the current user has permission to open
+.I c
+with the given mode,
+.IR omode ,
+and that the mode itself is valid
+(see
+.I openmode
+below).
+Permission is checked against the permission in the
+matching entry.
+If no matching Qid is found, it is assumed
+that the notional parent directory of the files represented in
+.I tab
+is to be opened.
+Such a directory is deemed to have mode
+0555, allowing access by any user.
+A directory can only be opened for reading
+.RB ( OREAD ).
+.I Devopen
+returns the channel
+.I c
+on success.
+Last, it sets the bit
+.B COPEN
+in
+.B Chan.flag
+to mark
+.I c
+as open.
+This convention can always be relied upon by the driver's
+.I close
+function to tell if an open succeeded.
+On the otherhand,
+if the open request was unsuccessful,
+.I devopen
+raises an appropriate
+.IR error (10.2)
+and does not return.
+.PP
+.I Devbread
+returns a
+.B Block
+(see
+.IR allocb (10.2))
+containing up to
+.I n
+bytes read,
+using
+.BI "devtab[" c "->type]->read" ,
+from
+.I c
+starting at the given
+.IR offset .
+The read pointer in the returned
+.B Block
+points to the start of the data;
+the write pointer points to the next available byte.
+.PP
+.I Devbwrite
+writes the data in
+.B Block
+.I bp
+to the file
+.I c
+at the given
+.IR offset ,
+using the write function
+.BI "devtab[" c "->type]->write" .
+It then frees the block list
+.I bp
+before
+returning the number of bytes written.
+.PP
+Most built-in devices do not allow
+.IR create ,
+.IR remove
+or
+.I wstat
+on their files.
+.IR Devcreate ,
+.I devremove
+and
+.I devwstat
+are stubs that raise an
+.IR error (10.2),
+.BR Eperm .
+They can be named directly in a device driver's device
+switch (the
+.B Dev
+structure in
+.BR /os/port/portdat.h :
+see
+.IR dev (10.2)).
+.PP
+.IR Devreset ,
+.I devinit
+and
+.I devshutdown
+are also stubs;
+they do nothing.
+A device driver puts them in its
+.B Dev
+structure when it need take no action on device reset, initialisation, or shut down.
+.PP
+.I Openmode
+is used by a driver that does not use
+.IR devopen ,
+to check the open mode it receives in its open
+routine.
+.I Openmode
+returns mode
+.IR o ,
+the mode parameter to
+.IR sys-open (2)
+or
+.IR sys-create ,
+shorn of
+.BR OTRUNC
+and similar options,
+and reduced to one of
+.BR OREAD ,
+.BR OWRITE
+or
+.BR ORDWR .
+In particular,
+.B OEXEC
+becomes
+.B OREAD
+within the kernel.
+.I Openmode
+raises an
+.IR error (10.2)
+.B Ebadarg
+instead of returning, if
+.I o
+is an invalid mode (eg, reserved bits set).
+.SH SOURCE
+.B /emu/port/dev.c
+.br
+.B /os/port/dev.c
+.SH SEE ALSO
+.IR allocb (10.2),
+.IR eve (10.2),
+.IR qio (10.2)
--- /dev/null
+++ b/man/10/dmainit
@@ -1,0 +1,86 @@
+.TH DMAINIT 10.2 x86
+.SH NAME
+dmainit, dmasetup, dmadone, dmaend, dmacount \- platform-specific DMA support
+.SH SYNOPSIS
+.ta \w'\fLushort 'u
+.B
+void	dmainit(int chan)
+.PP
+.B
+long	dmasetup(int chan, void *va, long len, int isread)
+.PP
+.B
+int	dmadone(int chan)
+.PP
+.B
+void	dmaend(int chan)
+.PP
+.B
+int	dmacount(int chan)
+.PP
+.SH DESCRIPTION
+These functions manage DMA on a bus that uses ISA-style DMA controllers.
+They were originally devised for the x86 platform, but the same interface, and similar code,
+is used by other platforms that use similar controllers.
+They compensate as best they can for the limitations of older DMA implementations
+(eg, alignment, boundary and length restrictions).
+There are 8 DMA channels:
+0 to 3 are byte-oriented; 4 to 7 are word-oriented (16-bit words).
+.PP
+.I Dmainit
+must be called early in a driver's initialisation to prepare
+.I chan
+for use.
+Amongst other things, it allocates a page-sized buffer to help circumvent hardware
+restrictions on DMA addressing.
+.PP
+.I Dmasetup
+prepares DMA channel
+.IR chan
+for a transfer between a device configured to use it
+and the virtual address
+.IR va .
+(The transfer is started by issuing a command to the device.)
+If
+.I va
+lies outside the kernel address space,
+the transfer crosses a 64k boundary,
+or exceeds the 16 Mbyte limit imposed by some DMA controllers,
+the transfer will be split into page-sized transfers using the buffer previously allocated by
+.IR dmainit .
+If
+.I isread
+is true (non-zero), data is to be transferred from
+.I chan
+to
+.IR va ;
+if false, data is transferred from
+.I va
+to
+.IR chan .
+In all cases,
+.I dmasetup
+returns the number of bytes to be transferred.
+That value (rather than
+.IR len )
+must be given to the device in the read or write request that starts the transfer.
+.PP
+.I Dmadone
+returns true (non-zero) if
+.I chan
+is idle.
+.PP
+.I Dmaend
+must be called at the end of every DMA operation.
+It disables
+.IR chan ,
+preventing further access to the previously associated memory and,
+if a low-memory buffer was required for input, transfers its contents
+to the appropriate part of the target buffer.
+.PP
+.I Dmacount
+returns the number of bytes that were last transferred by channel
+.IR chan .
+The count is always even for word-oriented DMA channels.
+.SH SOURCE
+.B /os/pc/dma.c
--- /dev/null
+++ b/man/10/dynld
@@ -1,0 +1,287 @@
+.TH DYNLD 10.2
+.SH NAME
+dynfindsym, dynfreeimport, dynloadfd, dynloadgen, dynobjfree, dyntabsize \- load object file dynamically
+.SH SYNOPSIS
+.B #include <lib9.h>
+.br
+.B #include <a.out.h>
+.br
+.B #include <dynld.h>
+.PP
+.ta \w'\fLDynsym*** 'u
+.B
+Dynsym*	dynfindsym(char *name, Dynsym *syms, int nsym)
+.PP
+.B
+Dynobj*	dynloadfd(int fd, Dynsym *exports, int nexport,
+.br
+.B
+	  ulong maxsize)
+.PP
+.B
+Dynobj*	dynloadgen(void *file, long (*read)(void*,void*,long),
+.br
+.B
+	  vlong (*seek)(void*,vlong,int), void (*err)(char*),
+.br
+.B
+	  Dynsym *exports, int nexport, ulong maxsize)
+.PP
+.B
+void*	dynimport(Dynobj *o, char *name, ulong sig)
+.PP
+.B
+void	dynfreeimport(Dynobj *o)
+.PP
+.B
+void	dynobjfree(Dynobj *o)
+.PP
+.B
+int	dyntabsize(Dynsym *t)
+.PP
+.B
+extern Dynsym  _exporttab[];
+.DT
+.SH DESCRIPTION
+These functions allow a process to load further code and data
+into the currently executing image.
+A dynamically-loadable file, called a
+.I module
+here, is a variant of the
+.IR a.out (10.6)
+executable format with some extra components.
+The loader for the architecture
+(see
+.IR 2l (1))
+creates a module file from component object file(s) when given the
+.B -u
+option.
+A module contains text and data sections, an import table, an export table,
+and relocation data.
+The import table lists the symbols the module needs from the loading program;
+the export table lists symbols the module provides when loaded.
+A program that loads a module provides a table of its own symbols to match
+the symbols in the module's import table.
+.PP
+A symbol entry in a symbol table names a global function or data item, and has an associated
+.I signature
+value representing the type of the corresponding function or data in the source code.
+The
+.B Dynsym
+structure defines a symbol:
+.IP
+.EX
+typedef struct {
+	ulong	sig;
+	ulong	addr;
+	char*	name;
+} Dynsym;
+.EE
+.PP
+The structure is known to the loaders
+.IR 2l (1).
+.I Name
+is the linkage name of the function or data.
+.I Addr
+is its address, which is relative to the start of the module before loading,
+and an address in the current address space after loading.
+The signature
+.I sig
+is the value produced by the C compiler's
+.B signof
+operator applied to the type.
+Symbol tables must be sorted by
+.IR name .
+.PP
+An executable that wishes to load modules will normally be linked using the
+.B -x
+option to the appropriate loader
+.IR 2l (1).
+The resulting executable contains an export table
+.B _exporttab
+that lists all the exported symbols of the program (by default, all external symbols).
+A nil name marks the end of the table.
+See
+.IR 2l (1)
+for details.
+The table can be given to the functions below to allow a loaded module
+to access those symbols.
+.PP
+A loaded module is described by a
+.B Dynobj
+structure:
+.IP
+.EX
+typedef struct {
+	ulong	size;	/* total size in bytes */
+	ulong	text;	/* bytes of text */
+	ulong	data;	/* bytes of data */
+	ulong	bss;		/* bytes of bss */
+	uchar*	base;	/* start of text, data, bss */
+	int		nexport;
+	Dynsym*	export;	/* export table */
+	int		nimport;
+	Dynsym**	import;	/* import table */
+} Dynobj;
+.EE
+.PP
+Several fields give sizes of the module's components, as noted in comments above.
+.I Base
+gives the address at which the module has been loaded.
+All its internal
+references have been adjusted where needed to reflect its current address.
+.I Export
+points to a symbol table listing the symbols exported by the module;
+.I nexport
+gives the table's length.
+.I Import
+points to a list of symbols imported by the module;
+note that each entry actually points to an entry in a symbol table
+provided by the program that loaded the module (see below).
+.I Nimport
+gives the import table's length.
+If the import table is not required, call
+.I dynfreeimport
+on the module pointer to free it.
+.PP
+.I Dynfindysm
+looks up the entry for the given
+.I name
+in symbol table
+.I syms
+(of length
+.IR nsym ).
+It returns a pointer to the entry if found; nil otherwise.
+The symbol table must be sorted by name in ascending order.
+.PP
+.I Dyntabsize
+returns the length of symbol table
+.IR t ,
+defined to be the number of
+.B Dynsym
+values starting at
+.I t
+that have non-nil
+.I name
+fields.
+It is used to find the length of
+.BR _exporttab .
+.PP
+.I Dynloadfd
+loads a module from the file open for reading on
+.IR fd ,
+and returns the resulting module pointer on success,
+or nil on error.
+If
+.I maxsize
+is non-zero
+the size of the dynamically-loaded module's code and data
+is limited to
+.I maxsize
+bytes.
+.I Exports
+is an array of
+.I nexport
+symbols in the current program that can be imported by the current module.
+It uses
+.IR read (2)
+and
+.IR seek (2)
+to access
+.IR fd ,
+and calls
+.I werrstr
+(see
+.IR errstr (2))
+to set the error string if necessary.
+.PP
+.I Dynloadgen
+is a more general function that can load a module from an
+arbitrary source, not just an open file descriptor.
+(In particular, it can be
+called by the kernel using functions internal to the kernel
+instead of making system calls.)
+.IR Exports ,
+.I nexport
+and
+.I maxsize
+are just as for
+.IR dynloadfd .
+.I File
+is a pointer to a structure defined by the caller that represents the file
+containing the module.
+It is passed to
+.I read
+and
+.IR seek .
+.I Read
+is invoked as
+.BI (*read)( file , buf ,\ \fInbytes\fP)\fR.\fP
+.I Read
+should read
+.I nbytes
+of data from
+.I file
+into
+.I buf
+and return the number of bytes transferred.
+It should return -1 on error.
+.I Seek
+is invoked as
+.BI (*seek)( file , n ,\ \fItype\fP)
+where
+.I n
+and
+.I type
+are just as for
+.IR seek (2);
+it should seek to the requested offset in
+.IR file ,
+or return -1 on error.
+.I Dynloadgen
+returns a pointer to the loaded module on success.
+On error,
+it returns nil after calling its
+.I err
+parameter to set the error string.
+.PP
+.I Dynimport
+returns a pointer to the value of the symbol
+.I name
+in loaded module
+.IR o ,
+or
+.I nil
+if
+.I o
+does not export a symbol with the given
+.IR name .
+If
+.I sig
+is non-zero, the exported symbol's signature must equal
+.IR sig ,
+or
+.I dynimport
+again returns nil.
+For example:
+.IP
+.EX
+Dev *d;
+d = dynimport(obj, "XXXdevtab", signof(*d));
+if(d == nil)
+	error("not a dynamically-loadable driver");
+.EE
+.PP
+.I Dynobjfree
+frees the module
+.IR o .
+There is no reference counting: it is the caller's responsibility to decide whether
+a module is no longer needed.
+.SH SEE ALSO
+.IR 2l (10.1),
+.\".IR mach (2),
+.IR a.out (10.6)
+.SH DIAGNOSTICS
+Functions that return pointers return nil on error.
+.I Dynloadfd
+sets the error string and returns nil.
--- /dev/null
+++ b/man/10/error
@@ -1,0 +1,175 @@
+.TH ERROR 10.2
+.SH NAME
+error, nexterror, poperror, waserror \- error handling functions
+.SH SYNOPSIS
+.ta \w'\fLchar* 'u
+.B
+void	error(char*)
+.PP
+.B
+void	nexterror(void)
+.PP
+.B
+void	poperror(void)
+.PP
+.B
+int	waserror(void)
+.SH DESCRIPTION
+The kernel handles error conditions using non-local gotos,
+similar to
+.IR setjmp / longjmp
+in ANSI C,
+but using a stack of error labels to implement nested exception handling.
+This simplifies many of the internal interfaces by eliminating the need
+for returning and checking error codes at every level of the call stack,
+at the cost of requiring kernel routines to adhere to a strict discipline.
+.PP
+Each kernel process
+(see
+.IR kproc (10.2))
+has in its defining
+.B Proc
+structure a stack of labels,
+currently 32 elements deep.
+A kernel function that must perform a clean up or recovery action on an error
+makes a stylised call to
+.IR waserror ,
+.IR nexterror
+and
+.IR poperror :
+.IP
+.EX
+.DT
+if(waserror()){
+	/* recovery action */
+	nexterror();
+}
+/* normal action */
+poperror();
+.EE
+.PP
+When called in the normal course of events,
+.I waserror
+registers an error handling block by pushing its label onto the stack,
+and returns zero.
+The return value of
+.I waserror
+should be tested as shown above.
+If non-zero (true), the calling function should perform the needed
+error recovery, ended by a call to
+.I nexterror
+to transfer control to the next location on the error stack.
+Typical recovery actions include deallocating memory, unlocking resources, and
+resetting state variables.
+.PP
+Within the recovery block,
+after handling an error condition, there must normally
+be a call to
+.I nexterror
+to transfer control to any error recovery lower down in the stack.
+The main exception is in the outermost function in a process,
+which must not call
+.I nexterror
+(there being nothing further on the stack), but calls
+.I pexit
+(see
+.IR kproc (10.2))
+instead,
+to terminate the process.
+.PP
+When the need to recover a particular resource has passed,
+a function that has called
+.I waserror
+must
+remove the corresponding label from the stack by calling
+.IR poperror .
+This
+must
+be done before returning from the function; otherwise, a subsequent call to
+.I error
+will return to an obsolete activation record, with unpredictable but unpleasant consequences.
+.PP
+.I Error
+copies the given error message, which is limited to
+.B ERRMAX
+bytes, into the
+.B Osenv.errstr
+of the current process,
+enables interrupts by calling
+.I spllo
+.RI ( native
+only),
+and finally calls
+.I nexterror
+to start invoking the recovery procedures currently stacked by
+.IR waserror .
+The files
+.B /os/port/error.h
+and
+.B /emu/port/error.h
+offer a wide selection of predefined error messages, suitable for almost any occasion.
+The message set by the most recent call to
+.I error
+can be obtained within the kernel by examining
+.B up->env->error
+and in an application, by using the
+.L %r
+directive of
+.IR sys-print (2).
+.PP
+A complex function can have nested error handlers.
+A
+.I waserror
+block will follow the acquisition of a resource, releasing it
+on error before calling
+.I nexterror,
+and a
+.I poperror
+will precede its release in the normal case.
+For example:
+.IP
+.EX
+.DT
+void
+outer(Thing *t)
+{
+    qlock(t);
+    if(waserror()){      /* A */
+        qunlock(t);
+        nexterror();
+    }
+    m = mallocz(READSTR, 0);
+    if(m == nil)
+        error(Enomem);
+    if(waserror()){     /* B */
+        free(m);
+        nexterror();    /* invokes A */
+    }
+    inner(t);
+    poperror();         /* pops B */
+    free(m);
+    poperror();         /* pops A */
+    qunlock(t);
+}
+.sp 1v
+void
+inner(Thing *t)
+{
+    if(t->bad)
+        error(Egreg);   /* error() call returns to B */
+    t->valid++;
+}
+.EE
+.SH SOURCE
+.B /os/port/proc.c
+.br
+.B /emu/port/main.c
+.SH CAVEATS
+The description above has many instances of
+.IR should ,
+.IR will ,
+.I must
+and
+.IR "must not" .
+.SH SEE ALSO
+.IR panic (10.2)
--- /dev/null
+++ b/man/10/eve
@@ -1,0 +1,42 @@
+.TH EVE 10.2
+.SH NAME
+eve, iseve \- privileged user
+.SH SYNOPSIS
+.ta \w'\fLchar* 'u
+.B
+char	eve[NAMELEN] = "inferno";
+.PP
+.B
+int	iseve(void)
+.SH DESCRIPTION
+.I Eve
+is a null-terminated string containing the name of the privileged user, often called
+the `host owner', in
+the Inferno system.
+The default identity in native systems is
+.LR inferno ,
+and in hosted Inferno it is the user who started
+.IR emu (1).
+The initial process created by system initialisation is given the
+.I eve
+identity.
+.PP
+.I Iseve
+returns true if the current user is
+.IR eve .
+Several drivers use
+.I iseve
+to check the caller's identity
+before granting permission to perform certain actions.
+For example, the console driver allows only the user
+.I eve
+to write a new identity into the
+.B /dev/user
+file.
+The privileges are strictly local and do not extend into the network
+(in particular, to file servers).
+.PP
+Note that the comparison performed by
+.I iseve
+is case-sensitive, even when running hosted on systems where
+usernames are case-insensitive.
--- /dev/null
+++ b/man/10/getfields
@@ -1,0 +1,76 @@
+.TH GETFIELDS 10.2
+.SH NAME
+getfields, tokenize \- break a string into fields
+.SH SYNOPSIS
+.ta \w'\fLchar* \fP'u
+.B
+int	getfields(char *str, char **args, int maxargs, int multiflag,
+.br
+.B
+	    char *delims)
+.PP
+.B
+int	tokenize(char *str, char **args, int maxargs)
+.SH DESCRIPTION
+.I Getfields
+breaks the null-terminated
+.SM UTF
+string
+.I str
+into at most
+.I maxargs
+null-terminated fields and places pointers to the start of these fields in the array
+.IR args .
+Some of the bytes in
+.I str
+are overwritten.
+If there are more than
+.I maxargs
+fields,
+only the first
+.I maxargs
+fields will be set.
+.I Delims
+is a
+.SM UTF
+string defining a set of delimiters.
+.PP
+If
+.I multiflag
+is zero,
+adjacent fields are separated by exactly one delimiter.
+A string containing
+.I n
+delimiter characters
+contains
+.IR n +1
+fields.
+If the
+.I multiflag
+argument is not zero,
+a field is a non-empty string of non-delimiters.
+.PP
+Getfields
+return the number of tokens processed.
+.PP
+.I Tokenize
+is the same as
+.I getfields
+with
+.I multiflag
+non-zero and
+.I delims
+\f5"\et\er\en "\fP,
+except that fields may be quoted using single quotes, in the manner
+of
+the command interpreter.
+.SH SOURCE
+.B /libkern/getfields.c
+.br
+.B /libkern/tokenize.c
+.br
+.B /lib9/getfields.c
+.br
+.B /lib9/tokenize.c
+.SH SEE ALSO
+.IR strcat (10.2)
--- /dev/null
+++ b/man/10/iar
@@ -1,0 +1,183 @@
+.TH IAR 10.1
+.SH NAME
+iar \- archive and library maintainer
+.SH SYNOPSIS
+.B iar
+.I key
+[
+.I posname
+]
+.I afile
+[
+.I file ...
+]
+.SH DESCRIPTION
+.I Iar
+maintains groups of files
+combined into a single archive file,
+.IR afile .
+The main use of
+.I iar
+is to create and update library files for the loaders
+.IR 2l (10.1),
+etc.
+It can be used, though, for any similar purpose.
+.PP
+.I Key
+is one character from the set
+.BR drqtpmx ,
+optionally concatenated with
+one or more of
+.BR vuaibclo .
+The
+.I files
+are constituents of the archive
+.IR afile .
+The meanings of the
+.I key
+characters are:
+.TP
+.B d
+Delete 
+.I files 
+from the archive file.
+.TP
+.B r
+Replace
+.I files 
+in the archive file, or add them if missing.
+Optional modifiers are
+.RS
+.PD0
+.TP
+.B u
+Replace only files with
+modified dates later than that of
+the archive.
+.TP
+.B a
+Place new files after
+.I posname
+in the archive rather than at the end.
+.TP
+.BR b " or " i
+Place new files before
+.I posname
+in the archive.
+.RE
+.PD
+.TP
+.B q
+Quick.  Append
+.I files 
+to the end of the archive without checking for duplicates.
+Avoids quadratic behavior in
+.LR "for (i in *.v) ar r lib.a $i" .
+.TP
+.B t
+List a table of contents of the archive.
+If names are given, only those files are listed.
+.TP
+.B p
+Print the named files in the archive.
+.TP
+.B m
+Move the named files to the end or elsewhere,
+specified as with 
+.LR r .
+.TP
+.B o
+Preserve the access and modification times of files
+extracted with the
+.B x
+command.
+.TP
+.B x
+Extract the named files.
+If no names are given, all files in the archive are
+extracted.
+In neither case does
+.B x
+alter the archive file.
+.TP
+.B v
+Verbose.
+Give a file-by-file
+description of the making of a
+new archive file from the old archive and the constituent files.
+With
+.BR p ,
+precede each file with a name.
+With
+.BR t ,
+give a long listing of all information about the files,
+somewhat like a listing by
+.IR ls (1),
+showing
+.br
+.ns
+.IP
+.B
+	mode uid/gid size date name
+.TP
+.B c
+Create.
+Normally
+.I iar
+will create a new archive when
+.I afile
+does not exist, and give a warning.
+Option 
+.B c
+discards any old contents and suppresses the warning.
+.TP
+.B l
+Local.
+Normally
+.I iar
+places its temporary files in the directory
+.BR /tmp .
+This option causes them to be placed in the local directory.
+.PP
+When a
+.BR d ,
+.BR r ,
+or
+.BR m
+.I key
+is specified and all members of the archive are valid object files for
+the same architecture,
+.I iar
+inserts a table of contents, required by the loaders, at
+the front of the library.
+The table of contents is
+rebuilt whenever the archive is modified, except
+when the
+.B q
+.I key
+is specified or when the table of contents is
+explicitly moved or deleted.
+.SH EXAMPLE
+.TP
+.L
+iar cr lib.a *.v
+Replace the contents of library
+.L lib.a
+with the object files in the current directory.
+.SH FILES
+.TF /tmp/vxxxx
+.TP
+.B /tmp/v*
+temporaries
+.SH SOURCE
+.B /utils/iar
+.SH "SEE ALSO"
+.IR 2l (10.1), 
+.IR ar (10.6)
+.SH BUGS
+If the same file is mentioned twice in an argument list,
+it may be put in the archive twice.
+.br
+The file format used by this command is taken from UNIX, and
+makes some invalid assumptions,
+for instance that user IDs are numeric.
--- /dev/null
+++ b/man/10/inb
@@ -1,0 +1,84 @@
+.TH INB 10.2 x86
+.SH NAME
+inb, ins, inl, outb, outs, outl, insb, inss, insl, outsb, outss, outsl \- programmed I/O
+.SH SYNOPSIS
+.ta \w'\fLushort 'u
+.B
+int	inb(int port)
+.PP
+.B
+ushort	ins(int port)
+.PP
+.B
+ulong	inl(int port)
+.PP
+.B
+void	outb(int port, int value)
+.PP
+.B
+void	outs(int port, ushort value)
+.PP
+.B
+void	outl(int port, ulong value)
+.PP
+.B
+void	insb(int port, void *address, int count)
+.PP
+.B
+void	inss(int port, void *address, int count)
+.PP
+.B
+void	insl(int port, void *address, int count)
+.PP
+.B
+void	outsb(int port, void *address, int count)
+.PP
+.B
+void	outss(int port, void *address, int count)
+.PP
+.B
+void	outsl(int port, void *address, int count)
+.SH DESCRIPTION
+The
+.I x86
+implementation provides functions to allow kernel code
+written in C to access the I/O address space.
+On several other architectures such as the PowerPC and Strongarm,
+the platform-dependent code provides similar functions to access
+devices with an I/O space interface, even when that is memory mapped, to encourage portability of device drivers.
+.PP
+.IR Inb ,
+.I ins
+and
+.I inl
+apply the corresponding hardware instruction to fetch the next byte, short or long
+from the I/O
+.IR port .
+.IR Outb ,
+.I outs
+and
+.I outl
+output a
+.I value
+to the I/O
+.IR port .
+.PP
+The remaining functions transfer
+.I count
+bytes, shorts, or longs using programmed I/O between a memory
+.I address
+and
+.IR port .
+Functions
+.BI ins x
+copy values into memory; functions
+.BI outs x
+copy values from memory.
+The
+.I count
+is in elements, not bytes.
+.SH SOURCE
+.B /os/pc/l.s
+.SH SEE ALSO
+.IR dma (10.2),
+.IR isaconfig (10.2)
--- /dev/null
+++ b/man/10/inm
@@ -1,0 +1,102 @@
+.TH INM 10.1
+.SH NAME
+inm \- Inferno name list (symbol table)
+.SH SYNOPSIS
+.B inm
+[
+.B -aghnsu
+]
+.I file ...
+.SH DESCRIPTION
+.I Inm
+prints the name list of each executable or object
+.I file
+in the argument list.
+If the
+.I file
+is an archive
+(see
+.IR iar (10.1)),
+the name list of each file in the archive is printed.
+If more than one file is given in the argument list,
+the name of each file is printed at the beginning of each line.
+.PP
+Each symbol name is preceded by its hexadecimal
+value (blanks if undefined)
+and one of the letters
+.TP
+.B T
+text segment symbol
+.PD0
+.TP
+.B t
+static text segment symbol
+.TP
+.B L
+leaf function text segment symbol
+.TP
+.B l
+static leaf function text segment symbol
+.TP
+.B D
+data segment symbol
+.TP
+.B d
+static data segment symbol
+.TP
+.B B
+bss segment symbol
+.TP
+.B b
+static bss segment symbol
+.TP
+.B a
+automatic (local) variable symbol
+.TP
+.B p
+function parameter symbol
+.TP
+.B z
+source file name
+.TP
+.B Z
+source file line offset
+.TP
+.B f
+source file name components
+.PD
+.PP
+The output is sorted alphabetically.
+.PP
+Options are:
+.TP
+.B -a
+Print all symbols; normally only user-defined text, data,
+and bss segment symbols are printed.
+.TP
+.B -g
+Print only global
+.RB ( T ,
+.BR L ,
+.BR D ,
+.BR B )
+symbols.
+.TP
+.B -h
+Do not print file name headers with output lines.
+.TP
+.B -n
+Sort according to the address of the symbols.
+.TP
+.B -s
+Don't sort; print in symbol-table order.
+.TP
+.B -u
+Print only undefined symbols.
+.SH SOURCE
+.B /utils/nm
+.SH SEE ALSO
+.IR iar (10.1),
+.IR 2l (10.1), 
+.IR acid (10.1),
+.IR a.out (10.6)
--- /dev/null
+++ b/man/10/intrenable
@@ -1,0 +1,106 @@
+.TH INTRENABLE 10.2
+.SH NAME
+intrenable, intrdisable \- enable (disable) an interrupt handler
+.SH SYNOPSIS
+.ta \w'\fLvoid* 'u
+.B
+void intrenable(int v, void (*f)(Ureg*, void*), void* a, int tbdf, char *name)
+.PP
+.B
+void intrdisable(int v, void (*f)(Ureg*, void*), void* a, int tbdf, char *name)
+.SH DESCRIPTION
+.I Intrenable
+registers
+.I f
+to be called by the kernel's interrupt controller driver each time
+an interrupt denoted by
+.I v
+occurs, and unmasks the corresponding interrupt in the interrupt controller.
+The encoding of
+.I v
+is platform-dependent; it is often an interrupt vector number, but
+can be more complex.
+.I Tbdf
+is a platform-dependent value that might further qualify
+.IR v .
+It might for instance
+denote the type of bus, bus instance, device number and function
+(following the PCI device indexing scheme), hence its name,
+but can have platform-dependent meaning.
+.I Name
+is a string that should uniquely identify the corresponding device (eg, \f5"uart0"\fP);
+again it is usually platform-dependent.
+.I Intrenable
+supports sharing of interrupt levels when the hardware does.
+.PP
+Almost invariably
+.I f
+is a function defined in a device driver to carry out the device-specific work associated with a given interrupt.
+The pointer
+.I a
+is passed to
+.IR f ;
+typically it points to the driver's data for a given device or controller.
+It also passes
+.I f
+a
+.B Ureg*
+value that
+contains the registers saved by the interrupt handler (the
+contents are platform specific;
+see the platform's include file
+.BR "ureg.h" ).
+.PP
+.I F
+is invoked by underlying code in the kernel that is invoked directly from the hardware vectors.
+It is therefore not running in any process (see
+.IR kproc (10.2);
+indeed, on many platforms
+the current process pointer
+.RB ( up )
+will be nil.
+There are many restrictions on kernel functions running outside a process, but a fundamental one is that
+they must not
+.IR sleep (10.2),
+although they often call
+.B wakeup
+to signal the occurrence of an event associated with the interrupt.
+.IR Qio (10.2)
+and other manual pages note which functions are safe for
+.I f
+to call.
+.PP
+The interrupt controller driver does whatever is
+required to acknowledge or dismiss the interrupt signal in the interrupt controller,
+before calling
+.IR f ,
+for edge-triggered interrupts,
+and after calling
+.IR f
+for level-triggered ones.
+.I F
+is responsible for deal with the cause of the interrupt in the device, including any
+acknowledgement required in the device, before it returns.
+.PP
+.I Intrdisable
+removes any registration previously made by
+.I intrenable
+with matching parameters, and if no other
+interrupt is active on
+.IR v ,
+it masks the interrupt in the controller.
+Device drivers that are not dynamically configured tend to call
+.I intrenable
+during reset or initialisation (see
+.IR dev (10.2)),
+but can call it at any appropriate time, and
+instead of calling
+.I intrdisable
+they can simply enable or disable interrupts in the device as required.
+.SH SOURCE
+.B /os/*/trap.c
+.SH SEE ALSO
+.IR malloc (10.2),
+.IR qio (10.2),
+.IR sleep (10.2),
+.IR splhi (10.2)
--- /dev/null
+++ b/man/10/kbdputc
@@ -1,0 +1,105 @@
+.TH KBDPUTC 10.2
+.SH NAME
+kbdputc, kbdrepeat, kbdclock, kbdq \- keyboard interface to \fIcons\fP(3)
+.SH SYNOPSIS
+.ta \w'\f5extern\ \ \f1'u
+.B
+#include "keyboard.h"
+.PP
+.B
+void	kbdputc(Queue *q, int c)
+.PP
+.B
+void	kbdrepeat(int on)
+.PP
+.B
+void	kbdclock(void)
+.PP
+.B
+extern	Queue *kbdq;
+.SH DESCRIPTION
+This is the internal interface between
+.B /dev/keyboard
+of
+.IR cons (3)
+and the architecture-dependent keyboard driver.
+Before calling any of these functions,
+the global variable
+.B kbdq
+must be initialised;
+.IR cons (3)
+does not initialise it.
+This is usually done during system initialisation by the keyboard driver's
+.I kbdinit
+function ,
+as follows:
+.IP
+.EX
+kbdq = qopen(4*1024, 0, 0, 0);
+qnoblock(kbdq, 1);
+.EE
+.PP
+.I Kbdputc
+puts a 16-bit Unicode character
+.I c
+(ie, a `rune')
+on the given
+.IR q ,
+as a sequence of bytes in UTF-8 encoding
+(see
+.IR utf (6)).
+If
+.I c
+is the special value
+.B Latin
+(defined by
+.BR keyboard.h ),
+.I kbdputc
+starts collecting characters, looking for the typeable representations of
+Unicode characters defined by
+.IR keyboard (6);
+at the end of a complete such sequence,
+.I kbdputc
+queues the UTF-8 encoding of the corresponding Unicode character.
+It is up to the keyboard driver to map a suitable physical keyboard character
+(or combination of characters) to the code
+.BR Latin .
+.PP
+Drivers that need to implement repeat of keypresses in software
+should call
+.IP
+.EX
+addclock0link(kbdclock);
+.EE
+.PP
+at the end of
+.IR kbdinit ,
+to cause
+.I kbdclock
+to be called each clock tick.
+.I Kbdrepeat
+can then be called to enable
+.RI ( on
+is non-zero)
+or disable it
+.RI ( on
+is zero).
+When repeat is on,
+.I kbdclock
+(when called) will periodically call
+.BI "kbdputc(" kbdq , c )
+where
+.I c
+is the last rune given to
+.IR kbdputc .
+The driver is responsible for enabling and disabling repeat appropriately;
+for instance, function keys and certainly
+.B Latin
+should typically not be repeated.
+.SH SOURCE
+.B /os/*/kbd*.c
+.SH SEE ALSO
+.IR cons (3),
+.IR utf (6),
+.IR qio (10.2)
+
--- /dev/null
+++ b/man/10/kproc
@@ -1,0 +1,142 @@
+.TH KPROC 10.2
+.SH NAME
+kproc, setpri, swiproc, pexit \- kernel process creation, priority change, interrupt and termination
+.SH SYNOPSIS
+.ta \w'\fLchar* 'u
+.B
+void	kproc(char *name, void (*func)(void*), void *arg, int flags);
+.PP
+.B
+int	setpri(int pri);
+.PP
+.B
+void	swiproc(Proc *p, int interp);
+.PP
+.B
+void	pexit(char*, int);
+.SH DESCRIPTION
+.I Kproc
+creates a new Inferno kernel process
+to run the function
+.IR func ,
+which is invoked as 
+.BR "(*func)(arg)" .
+The string
+.I name
+is copied into the
+.B text
+field of the
+.B Proc
+structure of the new process; although the value is not visible to Limbo
+applications, it can appear in system messages written to the console.
+The process is made runnable; it
+will run when selected by the scheduler.
+.PP
+The new process always acquires the following attributes from the creating process:
+.IP
+owner (Inferno user name)
+.br
+host user and group IDs (in
+.I emu
+only)
+.br
+floating-point attributes
+.PP
+Several resources can be shared with the creating process on request, as determined by
+.IR flags ,
+which is the logical OR
+of a subset of the following:
+.TF KPDUPENVG
+.TP
+.B KPDUPPG
+If set, the new process shares the caller's process group,
+which includes its process group ID
+(for
+.IR killgrp ),
+name space (mounts, root and current directory),
+and PIN for
+.B /dev/pin
+(see
+.IR cons (3)).
+.TP
+.B KPDUPFDG
+If set, the new process shares the caller's file descriptor group;
+otherwise, it has no file descriptor group, and (if it intends to open files) must call
+.IR newfgrp (10.2)
+to obtain an empty file descriptor group.
+.TP
+.B KPDUPENVG
+If set, the new process shares the caller's environment group
+(currently applies in
+.I emu
+only).
+.TP
+.B KPDUP
+Equivalent to all of the above.
+.PD
+.PP
+If a particular option is not set, the new process will have a
+.B nil
+reference for the corresponding resource.
+.PP
+.I Setpri
+sets the priority of the calling process to
+.I pri
+and returns its previous priority level.
+If a (now) higher priority process is ready to run, the system will reschedule.
+The available priority levels are shown below,
+arranged from highest to lowest priority,
+with examples of the type of processes intended to use them:
+.TF PriBackground
+.TP
+.B PriLock
+The highest priority, used by
+.IR lock (10.2)
+for a process entering a critical section
+.TP
+.B PriRealtime
+Intended for processes supporting applications with real-time constraints, such as video telephony.
+.TP
+.B PriHicodec
+MPEG codec
+.TP
+.B PriLocodec
+Audio codec
+.TP
+.B PriHi
+Any task with keen time constraints.
+.TP
+.B PriNormal
+The priority of most processes in the system.
+.TP
+.B PriLo
+.TP
+.B PriBackground
+.PD
+.PP
+.I Swiproc
+sends a software interrupt to process
+.IR p ,
+causing it to wake from
+.IR sleep (10.2)
+with an
+.IR error (10.2)
+`interrupted'.
+Unless
+.I interp
+is non-zero (ie, the Dis interpreter is the caller), the process is also marked `killed'.
+.PP
+An Inferno process terminates only when it calls
+.IR pexit ,
+thereby terminating itself.
+There is no mechanism for one process to force the termination of another,
+although it can send a software interrupt using
+.IR swiproc .
+The arguments to
+.I pexit
+are ignored in Inferno, but are included for compatibility
+with kernel components of Plan 9; use
+.IP
+.EX
+pexit("", 0);
+.EE
--- /dev/null
+++ b/man/10/kprof
@@ -1,0 +1,34 @@
+.TH KPROF 10.1
+.SH NAME
+kprof \- display kernel profiling data
+.SH SYNOPSIS
+.B kprof
+.I kernel
+.I kpdata
+.SH DESCRIPTION
+.I Kprof
+presents the data accumulated by the kernel
+profiling device,
+.IR kprof (3) .
+The symbol table is taken from the file
+.IR kernel ,
+the executable file of the operating system kernel that
+was profiled.
+The data file
+.I kpdata
+is usually a copy of
+.BR /dev/kpdata
+from the device running the profiling kernel.
+.PP
+The symbol table
+is read and correlated with the
+profile data.
+For each symbol, the time (in milliseconds)
+spent executing between that symbol
+and the next
+is printed (in decreasing order),
+together with the percentage of the total time.
+.SH SOURCE
+.B /utils/kprof
+.SH SEE ALSO
+.IR kprof (3)
--- /dev/null
+++ b/man/10/ksize
@@ -1,0 +1,29 @@
+.TH KSIZE 10.1
+.SH NAME
+ksize \- print size of kernel images
+.SH SYNOPSIS
+.B ksize
+[
+.I file ...
+]
+.SH DESCRIPTION
+.I Ksize
+prints the size of the segments for each of the argument executable files
+(default
+.BR 8.out ).
+The format is
+.IP
+.IB textsize t
++
+.IB datasize d
++
+.IB bsssize b
+=
+.I total
+.PP
+where the numbers are in bytes.
+.SH SOURCE
+.B /utils/ksize
+.SH "SEE ALSO
+.IR kstrip (10.1),
+.IR a.out (10.6)
--- /dev/null
+++ b/man/10/kstrip
@@ -1,0 +1,32 @@
+.TH KSTRIP 10.1
+.SH NAME
+kstrip \- remove symbols from kernel images
+.SH SYNOPSIS
+.B kstrip
+.I file ...
+.PP
+.B kstrip
+.B -o
+.I ofile
+.I file
+.SH DESCRIPTION
+.I Kstrip
+removes symbol table segments from executable files,
+rewriting the files in place.
+Stripping a file requires write permission on the file
+and the directory it is in.
+.PP
+If the
+.B -o
+flag is given, 
+the single input file
+.I file
+is stripped and the result written to
+.IR ofile .
+.I File
+is unchanged.
+.SH SOURCE
+.B /utils/kstrip
+.SH "SEE ALSO"
+.IR ksize (10.1),
+.IR a.out (10.6)
--- /dev/null
+++ b/man/10/lock
@@ -1,0 +1,110 @@
+.TH LOCK 10.2
+.SH NAME
+lock, canlock, ilock, iunlock, unlock \- spin locks
+.SH SYNOPSIS
+.ta \w'\fLvoid 'u
+.B
+void    lock(Lock *l)
+.PP
+.B
+int     canlock(Lock *l)
+.PP
+.B
+void    unlock(Lock *l)
+.PP
+.B
+void    ilock(Lock *l)
+.PP
+.B
+void    iunlock(Lock *l)
+.SH DESCRIPTION
+These primitives control access to shared
+resources using spin locks.
+They in turn are used to build higher-level synchronisation mechanisms
+such as those described in
+.IR sleep (10.2),
+.IR qlock (10.2)
+and
+.IR qio (10.2).
+They should be used only to protect short critical sections
+that update shared data structures.
+.PP
+.I Lock
+loops repeatedly attempting acquire the spin lock
+.I l
+until it succeeds.
+.I Lock
+should not be used to lock a structure shared with an interrupt handler
+unless interrupts are disabled by
+.IR splhi (10.2)
+before attempting the lock;
+it is better to use
+.IR ilock ,
+below.
+.PP
+.I Canlock
+is non-blocking.
+Only one attempt is made for the lock.
+It returns non-zero if the lock was successfully acquired; 0 otherwise.
+.PP
+.I Unlock
+releases the lock
+.IR l .
+A lock must be unlocked only by the locking process.
+.PP
+When called by a process, the functions above temporarily boost its priority
+to the highest priority,
+.BR PriLock ;
+its original priority is restored at the end of the critical section by
+.IR unlock .
+On a uniprocessor, if
+.I l
+is unavailable,
+.I lock
+can reschedule unless interrupts are disabled before entering
+.I lock
+or there is no current process (eg, when executing the scheduler).
+.PP
+.I Ilock
+disables interrupts before attempting to acquire the lock.
+It should be used to lock a resource shared between a process and an interrupt handler.
+On a uniprocessor, disabling interrupts is sufficient to exclude an interrupt handler
+from the critical section,
+and on a multiprocessor the spin lock excludes an interrupt handler running on another processor.
+.I Ilock
+never reschedules the caller, nor must a caller allow itself to be rescheduled
+(eg, by calling
+.IR sleep (10.2))
+before releasing the lock.
+.PP
+.I Iunlock
+releases a lock previously got by
+.IR ilock .
+.SH DIAGNOSTICS
+The lock functions
+guard against the possibility of never acquiring the lock by capping the number of lock attempts.
+If the limit is reached, a message of
+the following form is written on the console:
+.IP
+.EX
+lock loop on \fIlock-address\fP key \fIkey-value\fP pc \fIcaller-pc\fP held by pc \fIlock-pc\fP
+.EE
+.PP
+Most lock loops represent deadlocks caused by failing to unlock a resource,
+attempting to lock (eg, by recursive call) a resource already held by the process,
+inconsistent locking and unlocking of nested resources, using a spin-lock
+to guard code that reschedules, using
+.I lock
+not
+.I ilock
+to interlock with an interrupt routine, and similar blunders.
+.SH SOURCE
+.B /os/port/taslock.c
+.br
+.B /os/*/l.s
+.br
+.B /emu/port/lock.c
+.br
+.B /emu/*/os.c
+.SH SEE ALSO
+.IR qlock (10.2)
--- /dev/null
+++ b/man/10/malloc
@@ -1,0 +1,81 @@
+.TH MALLOC 10.2
+.SH NAME
+malloc, mallocz, smalloc, free, realloc, calloc \- kernel memory allocators
+.SH SYNOPSIS
+.ta \w'\fLvoid* 'u
+.B
+void*	malloc(ulong size)
+.PP
+.B
+void*	mallocz(ulong size, int clr)
+.PP
+.B
+void*	smalloc(ulong size)
+.PP
+.B
+void*	realloc(void *p, ulong size)
+.PP
+.B
+void*	calloc(ulong n, ulong szelem)
+.PP
+.B
+void	free(void *p)
+.SH DESCRIPTION
+These functions allocate memory from the
+.B mainmem
+memory pool.
+All but
+.I smalloc
+(which sleeps)
+may safely be called by interrupt handlers.
+.PP
+.I Malloc
+returns a pointer to a block of at least
+.I size
+bytes, initialised to zero.
+The result is aligned on a 32-bit boundary.
+.I Mallocz
+is similar, but only clears the memory if
+.I clr
+is non-zero.
+.PP
+.I Smalloc
+returns a pointer to a block of
+.I size
+bytes, initialised to zero.
+If the memory is not immediately available,
+.I smalloc
+retries every 100 milliseconds until the memory is acquired.
+.PP
+.I Calloc
+returns a pointer to a block of memory of at least
+.I "n*szelem"
+bytes, initialised to zero.
+.PP
+.I Realloc
+changes the size of the block pointed to by
+.I p
+to
+.I size
+bytes,
+if possible without moving the data,
+and returns a pointer to the block.
+The contents are unchanged up to the lesser of old and new sizes,
+and any new space allocated is initialised to zero.
+If
+.I p
+is a null pointer,
+.I realloc
+returns the equivalent of
+.BR "malloc(size)" .
+.PP
+The argument to
+.I free
+is a pointer to a block of memory allocated by one of the routines above, which
+is returned to the allocation pool, or a null pointer, which is ignored.
+.SH DIAGNOSTICS
+All functions except
+.I smalloc
+return a null pointer if space is unavailable.
+.SH SEE ALSO
+.IR xalloc (10.2)
--- /dev/null
+++ b/man/10/master
@@ -1,0 +1,70 @@
+.TH MASTER 10.6
+.SH NAME
+master, master.local \- list of device specifiers
+.SH SYNOPSIS
+.B /emu/port/master
+.br
+.B /emu/port/master.local
+.PP
+.B os/port/master
+.br
+.B os/port/master.local
+.SH DESCRIPTION
+Each device has a name of the form
+.BI # x
+where
+.I x
+is a single Unicode character,
+which represents the root of the name space served (generated) by the driver.
+(There can be a further
+.I specifier
+that selects a particular hierarchy within the device, but that is not relevant here.)
+These names allow the devices' name spaces to be added to a process's name space
+(see
+.IR sys-intro (2)),
+using
+.IR bind (1)
+commands,
+.IR sys-bind (2)
+operations and
+.IR namespace (6)
+files.
+A given character can represent only one driver in a running system,
+but the same character might be used by several drivers in the source tree
+if they are never configured into the same system (eg, different platforms or
+mutually-exclusive configurations).
+The file
+.B #c/drivers
+gives the local system's assignment (see
+.IR cons (3)).
+A list of the set of characters used across all drivers on all platforms
+can be found in the several
+.B master
+files.
+In the distribution, the
+.B master.local
+files list characters that are reserved for use by locally-written drivers.
+They are updated by hand at a given installation.
+The
+.B master
+files by contrast are generated automatically by
+.B emu/port/mkfile
+and
+.B os/port/mkfile
+from the source for all platforms and
+.BR master.local .
+In the distribution, they might include names for drivers that exist but are not
+distributed.
+.PP
+Except for a few fundamental drivers, the character assignment is
+arbitrary, can vary between installations, and is notionally subject to change
+(although that rarely happens).
+The
+.IR indir (3)
+allows referring to drivers using a longer name that is even less likely to change.
+.SH SEE ALSO
+.IR bind (1),
+.IR sys-intro (2),
+.IR indir (3),
+.IR namespace (6),
+.IR dev (10.2)
--- /dev/null
+++ b/man/10/memory
@@ -1,0 +1,117 @@
+.TH MEMORY 10.2
+.SH NAME
+memccpy, memchr, memcmp, memcpy, memmove, memset \- memory operations
+.SH SYNOPSIS
+.ta \w'\fLvoid* 'u
+.B
+void*	memccpy(void *s1, void *s2, int c, long n)
+.PP
+.B
+void*	memchr(void *s, int c, long n)
+.PP
+.B
+int	memcmp(void *s1, void *s2, long n)
+.PP
+.B
+void*	memcpy(void *s1, void *s2, long n)
+.PP
+.B
+void*	memmove(void *s1, void *s2, long n)
+.PP
+.B
+void*	memset(void *s, int c, long n)
+.SH DESCRIPTION
+These functions operate efficiently on memory areas
+(arrays of bytes bounded by a count, not terminated by a zero byte).
+They do not check for the overflow of any receiving memory area.
+.PP
+.I Memccpy
+copies bytes from memory area
+.I s2
+into
+.IR s1 ,
+stopping after the first occurrence of byte
+.I c
+has been copied, or after
+.I n
+bytes have been copied, whichever comes first.
+It returns a pointer to the byte after
+the copy of
+.I c
+in
+.IR s1 ,
+or zero if
+.I c
+was not found in the first
+.I n
+bytes of
+.IR s2 .
+.PP
+.I Memchr
+returns a pointer to the first
+occurrence of byte
+.I c
+in the first
+.I n
+bytes of memory area
+.IR s,
+or zero if
+.I c
+does not occur.
+.PP
+.I Memcmp
+compares its arguments, looking at the first
+.I n
+bytes only, and returns an integer
+less than, equal to, or greater than 0,
+according as
+.I s1
+is lexicographically less than, equal to, or
+greater than
+.IR s2 .
+The comparison is bytewise unsigned.
+.PP
+.I Memmove
+copies
+.I n
+bytes from memory area 
+.I s2
+to
+.IR s1 .
+It returns
+.IR s1 .
+It is guaranteed to work if
+.I s1
+and
+.IR s2
+overlap.
+.PP
+In the Inferno kernel
+.I memcpy
+is equivalent to
+.IR memmove .
+(In ANSI C, by contrast,
+.I memcpy
+does not account for overlapping memory regions.)
+.PP
+.I Memset
+sets the first
+.I n
+bytes in memory area
+.I s
+to the value of the least significant byte of
+.IR c .
+It returns
+.IR s .
+.SH DIAGNOSTICS
+If
+.I memcpy
+and
+.I memmove
+are handed negative counts, they abort.
+.SH SOURCE
+.BR /libkern/mem*.c
+.br
+.BR /libkern/mem*-\fIobjtype\fP.s
+.SH SEE ALSO
+.IR strcat (10.2)
--- /dev/null
+++ b/man/10/mk
@@ -1,0 +1,661 @@
+.TH MK 10.1
+.SH NAME
+mk \- maintain (make) related files
+.SH SYNOPSIS
+.B mk
+[
+.B -f
+.I mkfile
+] ...
+[
+.I option ...
+]
+[
+.I target ...
+]
+.SH DESCRIPTION
+.I Mk
+uses the dependency rules specified in
+.I mkfile
+to control the update (usually by compilation) of
+.I targets
+(usually files)
+from the source files upon which they depend.
+The
+.I mkfile
+(default
+.LR mkfile )
+contains a
+.I rule
+for each target that identifies the files and other
+targets upon which it depends and an
+.IR rc (10.1)
+script, a
+.IR recipe ,
+to update the target.
+The script is run if the target does not exist
+or if it is older than any of the files it depends on.
+.I Mkfile
+may also contain
+.I meta-rules
+that define actions for updating implicit targets.
+If no
+.I target
+is specified, the target of the first rule (not meta-rule) in
+.I mkfile
+is updated.
+.PP
+The environment variable
+.B $NPROC
+determines how many targets may be updated simultaneously;
+Plan 9 sets
+.B $NPROC
+automatically to the number of CPUs on the current machine.
+.PP
+Options are:
+.TP \w'\fL-d[egp]\ 'u
+.B -a
+Assume all targets to be out of date.
+Thus, everything is updated.
+.PD 0
+.TP
+.BR -d [ egp ]
+Produce debugging output
+.RB ( p
+is for parsing,
+.B g
+for graph building,
+.B e
+for execution).
+.TP
+.B -e
+Explain why each target is made.
+.TP
+.B -i
+Force any missing intermediate targets to be made.
+.TP
+.B -k
+Do as much work as possible in the face of errors.
+.TP
+.B -n
+Print, but do not execute, the commands
+needed to update the targets.
+.TP
+.B -s
+Make the command line arguments sequentially rather than in parallel.
+.TP
+.B -t
+Touch (update the modified date of) file targets, without
+executing any recipes.
+.TP
+.BI -w target1 , target2,...
+Pretend the modify time for each
+.I target
+is the current time; useful in conjunction with
+.B -n
+to learn what updates would be triggered by
+modifying the
+.IR targets .
+.PD
+.SS The \fLmkfile\fP
+A
+.I mkfile
+consists of
+.I assignments
+(described under `Environment') and
+.IR rules .
+A rule contains
+.I targets
+and a
+.IR tail .
+A target is a literal string
+and is normally a file name.
+The tail contains zero or more 
+.I prerequisites
+and an optional
+.IR recipe ,
+which is an
+.B rc
+script.
+Each line of the recipe must begin with white space.
+A rule takes the form
+.IP
+.EX
+target: prereq1 prereq2
+        rc \f2recipe using\fP prereq1, prereq2 \f2to build\fP target
+.EE
+.PP
+When the recipe is executed,
+the first character on every line is elided.
+.PP
+After the colon on the target line, a rule may specify
+.IR attributes ,
+described below.
+.PP
+A
+.I meta-rule 
+has a target of the form
+.IB A % B
+where
+.I A
+and
+.I B
+are (possibly empty) strings.
+A meta-rule acts as a rule for any potential target whose
+name matches
+.IB A % B
+with
+.B %
+replaced by an arbitrary string, called the
+.IR stem .
+In interpreting a meta-rule,
+the stem is substituted for all occurrences of
+.B %
+in the prerequisite names.
+In the recipe of a meta-rule, the environment variable
+.B $stem
+contains the string matched by the
+.BR % .
+For example, a meta-rule to compile a C program using
+.IR 2c (10.1)
+might be:
+.IP
+.EX
+%.2:    %.c
+        2c $stem.c
+        2l -o $stem $stem.2
+.EE
+.PP
+Meta-rules may contain an ampersand
+.B &
+rather than a percent sign
+.BR % .
+A
+.B %
+matches a maximal length string of any characters;
+an
+.B &
+matches a maximal length string of any characters except period
+or slash.
+.PP
+The text of the
+.I mkfile
+is processed as follows.
+Lines beginning with
+.B <
+followed by a file name are replaced by the contents of the named
+file.
+Blank lines and comments, which run from unquoted
+.B #
+characters to the following newline, are deleted.
+The character sequence backslash-newline is deleted,
+so long lines in
+.I mkfile
+may be folded.
+Non-recipe lines are processed by substituting for
+.BI `{ command }
+the output of the
+.I command
+when run by
+.IR rc .
+References to variables are replaced by the variables' values.
+Special characters may be quoted using single quotes
+.BR \&''
+as in
+.IR rc (10.1).
+.PP
+Assignments and rules are distinguished by
+the first unquoted occurrence of
+.B :
+(rule)
+or
+.B =
+(assignment).
+.PP
+A later rule may modify or override an existing rule under the
+following conditions:
+.TP
+\-
+If the targets of the rules exactly match and one rule
+contains only a prerequisite clause and no recipe, the
+clause is added to the prerequisites of the other rule.
+If either or both targets are virtual, the recipe is
+always executed.
+.TP
+\-
+If the targets of the rules match exactly and the
+prerequisites do not match and both rules
+contain recipes,
+.I mk
+reports an ``ambiguous recipe'' error.
+.TP
+\-
+If the target and prerequisites of both rules match exactly,
+the second rule overrides the first.
+.SS Environment
+Rules may make use of
+.B rc
+environment variables.
+A legal reference of the form
+.B $OBJ
+or
+.B ${name}
+is expanded as in
+.IR rc (10.1).
+A reference of the form
+.BI ${name: A % B = C\fL%\fID\fL}\fR,
+where
+.I A, B, C, D
+are (possibly empty) strings,
+has the value formed by expanding
+.B $name
+and substituting
+.I C
+for
+.I A
+and
+.I D
+for
+.I B
+in each word in
+.B $name
+that matches pattern
+.IB A % B\f1.
+.PP
+Variables can be set by
+assignments of the form
+.I
+        var\fL=\fR[\fIattr\fL=\fR]\fIvalue\fR
+.br
+Blanks in the
+.I value
+break it into words, as in
+.I rc
+but without the surrounding parentheses.
+Such variables are exported
+to the environment of
+recipes as they are executed, unless
+.BR U ,
+the only legal attribute
+.IR attr ,
+is present.
+The initial value of a variable is
+taken from (in increasing order of precedence)
+the default values below,
+.I mk's
+environment, the
+.IR mkfiles ,
+and any command line assignment as an argument to
+.IR mk .
+A variable assignment argument overrides the first (but not any subsequent)
+assignment to that variable.
+The variable
+.B MKFLAGS
+contains all the option arguments (arguments starting with
+.L -
+or containing
+.LR = )
+and
+.B MKARGS
+contains all the targets in the call to
+.IR mk .
+.PP
+It is recommended that mkfiles start with
+.IP
+.EX
+</$objtype/mkfile
+.EE
+.PP
+to set
+.BR CC ,
+.BR LD ,
+.BR AS ,
+.BR O ,
+.BR ALEF ,
+.BR YACC ,
+and
+.B MK
+to values appropriate to the target architecture (see the examples below).
+.PP
+Dynamic information may be included in the mkfile by using a line of the form
+.I
+        \fR<| \fIcommand\fR \fIargs\fR
+.br
+This runs the command 
+.I command
+with the given arguments
+.I args
+and pipes its standard output to
+.I mk
+to be included as part of the mkfile. For instance, the file
+.B /os/sa1100/mkfile
+uses this technique
+to run a shell command with an awk script and a configuration file as arguments in order for
+the
+.I awk
+script to process the file and output a set of variables and their values.
+.SS Execution
+.PP
+During execution,
+.I mk
+determines which targets must be updated, and in what order,
+to build the
+.I names
+specified on the command line.
+It then runs the associated recipes.
+.PP
+A target is considered up to date if it has no prerequisites or
+if all its prerequisites are up to date and it is newer
+than all its prerequisites.
+Once the recipe for a target has executed, the target is
+considered up to date.
+.PP
+The date stamp
+used to determine if a target is up to date is computed
+differently for different types of targets.
+If a target is
+.I virtual
+(the target of a rule with the
+.B V
+attribute),
+its date stamp is initially zero; when the target is
+updated the date stamp is set to
+the most recent date stamp of its prerequisites.
+Otherwise, if a target does not exist as a file,
+its date stamp is set to the most recent date stamp of its prerequisites,
+or zero if it has no prerequisites.
+Otherwise, the target is the name of a file and
+the target's date stamp is always that file's modification date.
+The date stamp is computed when the target is needed in
+the execution of a rule; it is not a static value.
+.PP
+Nonexistent targets that have prerequisites
+and are themselves prerequisites are treated specially.
+Such a target
+.I t
+is given the date stamp of its most recent prerequisite
+and if this causes all the targets which have
+.I t
+as a prerequisite to be up to date,
+.I t
+is considered up to date.
+Otherwise,
+.I t
+is made in the normal fashion.
+The
+.B -i
+flag overrides this special treatment.
+.PP
+Files may be made in any order that respects
+the preceding restrictions.
+.PP
+A recipe is executed by supplying the recipe as standard input to
+the command
+.B
+        $SHELL -e -I
+.br
+where the
+.I SHELL
+variable is the appropriate shell on the current platform - typically
+.B /bin/sh
+or
+.B /bin/rc.
+The appropriate value is automatically supplied in the Inferno build environment.
+The
+.B -e
+is omitted if the
+.B E
+attribute is set. 
+The environment is augmented by the following variables:
+.TP 14
+.B $alltarget
+all the targets of this rule.
+.TP
+.B $newprereq
+the prerequisites that caused this rule to execute.
+.TP
+.B $nproc
+the process slot for this recipe.
+It satisfies
+.RB 0≤ $nproc < $NPROC .
+.TP
+.B $pid
+the process id for the
+.I mk
+executing the recipe.
+.TP
+.B $prereq
+all the prerequisites for this rule.
+.TP
+.B $stem
+if this is a meta-rule,
+.B $stem
+is the string that matched
+.B %
+or
+.BR & .
+Otherwise, it is empty.
+For regular expression meta-rules (see below), the variables
+.LR stem0 ", ...,"
+.L stem9
+are set to the corresponding subexpressions.
+.TP
+.B $target
+the targets for this rule that need to be remade.
+.PP
+These variables are available only during the execution of a recipe,
+not while evaluating the
+.IR mkfile .
+.PP
+Unless the rule has the
+.B Q
+attribute,
+the recipe is printed prior to execution
+with recognizable environment variables expanded.
+Commands returning error status
+cause
+.I mk
+to terminate.
+.PP
+Recipes and backquoted
+.B rc
+commands in places such as assignments
+execute in a copy of
+.I mk's
+environment; changes they make to
+environment variables are not visible from
+.IR mk .
+.PP
+Variable substitution in a rule is done when
+the rule is read; variable substitution in the recipe is done
+when the recipe is executed.  For example:
+.IP
+.EX
+bar=a.c
+foo:    $bar
+        $CC -o foo $bar
+bar=b.c
+.EE
+.PP
+will compile
+.B b.c
+into
+.BR foo ,
+if
+.B a.c
+is newer than
+.BR foo .
+.SS Aggregates
+Names of the form
+.IR a ( b )
+refer to member
+.I b
+of the aggregate
+.IR a .
+Currently, the only aggregates supported are
+.IR ar (10.1)
+archives.
+.SS Attributes
+The colon separating the target from the prerequisites
+may be
+immediately followed by
+.I attributes
+and another colon.
+The attributes are:
+.TP
+.B D
+If the recipe exits with a non-null status, the target is deleted.
+.TP
+.B E
+Continue execution if the recipe draws errors.
+.TP
+.B N
+If there is no recipe, the target has its time updated.
+.TP
+.B n
+The rule is a meta-rule that cannot be a target of a virtual rule.
+Only files match the pattern in the target.
+.TP
+.B P
+The characters after the
+.B P
+until the terminating
+.B :
+are taken as a program name.
+It will be invoked as
+.B "rc -c prog 'arg1' 'arg2'"
+and should return a null exit status
+if and only if arg1 is not out of date with respect to arg2.
+Date stamps are still propagated in the normal way.
+.TP
+.B Q
+The recipe is not printed prior to execution.
+.TP
+.B R
+The rule is a meta-rule using regular expressions.
+In the rule,
+.B %
+has no special meaning.
+The target is interpreted as a regular expression as defined in
+.IR regexp (6).
+The prerequisites may contain references
+to subexpressions in form
+.BI \e n\f1,
+as in the substitute command of
+.IR sed (10.1).
+.TP
+.B U
+The targets are considered to have been updated
+even if the recipe did not do so.
+.TP
+.B V
+The targets of this rule are marked as virtual.
+They are distinct from files of the same name.
+.PD
+.SH EXAMPLES
+A simple mkfile to compile a program:
+.IP
+.EX
+</$objtype/mkfile
+
+prog:   a.$O b.$O c.$O
+    $LD $CFLAGS -o $target $prereq
+
+%.$O:   %.c
+    $CC $stem.c
+.EE
+.PP
+Override flag settings in the mkfile:
+.IP
+.EX
+% mk target 'CFLAGS=-S -w'
+.EE
+.PP
+To get the prerequisites for an aggregate:
+.IP
+.EX
+% membername 'libc.a(read.2)' 'libc.a(write.2)'
+read.2 write.2
+.EE
+.PP
+Maintain a library:
+.IP
+.EX
+libc.a(%.$O):N: %.$O
+libc.a: libc.a(abs.$O) libc.a(access.$O) libc.a(alarm.$O) ...
+    names=`{membername $newprereq}
+    ar r libc.a $names && rm $names
+.EE
+.PP
+String expression variables to derive names from a master list:
+.IP
+.EX
+NAMES=alloc arc bquote builtins expand main match mk var word
+OBJ=${NAMES:%=%.$O}
+.EE
+.PP
+Regular expression meta-rules:
+.IP
+.EX
+([^/]*)/(.*)\e.o:R:  \e1/\e2.c
+	cd $stem1; $CC $CFLAGS $stem2.c
+.EE
+.PP
+A correct way to deal with
+.IR yacc (10.1)
+grammars.
+The file
+.B lex.c
+includes the file
+.B x.tab.h
+rather than
+.B y.tab.h
+in order to reflect changes in content, not just modification time.
+.IP
+.EX
+lex.o:           x.tab.h
+x.tab.h:         y.tab.h
+    cmp -s x.tab.h y.tab.h || cp y.tab.h x.tab.h
+y.tab.c y.tab.h: gram.y
+    $YACC -d gram.y
+.EE
+.PP
+The above example could also use the
+.B P
+attribute for the
+.B x.tab.h
+rule:
+.IP
+.EX
+x.tab.h:Pcmp -s: y.tab.h
+	cp y.tab.h x.tab.h
+.EE
+.SH SOURCE
+.B /utils/mk
+.SH SEE ALSO
+.IR rc (10.1),
+.IR regexp (6)
+.br
+A. Hume,
+``Mk: a Successor to Make''.
+.br
+Bob Flandrena,
+``Plan 9 Mkfiles''.
+.SH BUGS
+Identical recipes for regular expression meta-rules only have one target.
+.br
+Seemingly appropriate input like
+.B CFLAGS=-DHZ=60
+is parsed as an erroneous attribute; correct it by inserting
+a space after the first 
+.LR = .
+.br
+The recipes printed by
+.I mk
+before being passed to
+.I rc
+for execution are sometimes erroneously expanded
+for printing.  Don't trust what's printed; rely
+on what
+.I rc
+does.
--- /dev/null
+++ b/man/10/ms2
@@ -1,0 +1,58 @@
+.TH MS2 10.1
+.SH NAME
+ms2 \- convert executable or raw file to Motorola S record format
+.SH SYNOPSIS
+.B ms2
+[
+.I option ...
+]
+.I infile
+.SH DESCRIPTION
+.IR ms2
+converts the
+.I infile
+onto the standard output in Motorola S record format. If the
+.B -b
+option is not given, the
+.I infile
+is presumed to be an executable, and the header and symbol
+table stripped. If the
+.B -b
+option is given, the file is treated as raw binary.
+The options are:
+.TP 1i
+.BI -d
+Encode the data segment only.
+.TP
+.BI -s
+Omit the end record, presumably to accomodate poorly implemented
+decoders.
+.TP
+.BI -T addr
+.TP
+.BI -A addr
+Set the address of the text segment, i.e. the first record output.
+.B -a
+is an older, deprecated option.
+.TP
+.BI -R n
+.TP
+.BI -p n
+Set the page size in use by the linker.
+.B -p
+is an older, deprecated option.
+.TP
+.BI -b
+The
+.I infile
+is binary. Ignores the
+.B -R
+and
+.B -d
+options.
+.TP
+.BI -S addr
+Sets the entry address output in the end record.
+.PP
+.SH SOURCE
+.IR 5cv (10.1)
--- /dev/null
+++ b/man/10/newchan
@@ -1,0 +1,227 @@
+.TH NEWCHAN 10.2
+.SH NAME
+newchan, chanfree, cclose, eqqid, eqchan, isdir, fdtochan, namec \- channel operations
+.SH SYNOPSIS
+.ta \w'\fLChan* 'u
+.B
+Chan*	newchan(void)
+.PP
+.B
+void	chanfree(Chan *c)
+.PP
+.B
+int	eqqid(Qid a, Qid b)
+.PP
+.B
+int	eqchan(Chan *a, Chan *b, int pathonly)
+.PP
+.B
+void	isdir(Chan *c)
+.PP
+.B
+Chan*	fdtochan(Fgrp *f, int fd, int mode, int chkmnt, int iref)
+.PP
+.B
+Chan*	namec(char *pathname, int amode, int omode, ulong perm)
+.PP
+.B
+void	cclose(Chan *c)
+.SH DESCRIPTION
+A value of type
+.B Chan
+represents a kernel channel for I/O and name space operations.
+It has the following public structure:
+.IP
+.EX
+typedef struct Chan{
+	ushort    type;       /* driver name */
+	ulong     dev;        /* instance number */
+	ushort    mode;       /* open mode */
+	ushort    flag;       /* COPEN set once opened */
+	ulong     offset;     /* current file offset */
+	Qid       qid;        /* unique id (path, vers) */
+	Cname*     name;	/* name by which it was accessed */
+.EE
+.PP
+.I Newchan
+returns a pointer to a newly allocated channel (sleeping if necessary until memory is available).
+Device drivers do not normally call
+.IR newchan
+directly, but instead allocate channels using either
+.IR devattach ,
+when a process attaches to the device's root,
+or
+.IR devclone ,
+when an existing channel is cloned;
+see
+.IR devattach (10.2).
+.PP
+.I Chanfree
+frees the channel structure
+.I c
+for reuse.
+.PP
+.I Eqqid
+returns 1 if
+.B Qid
+values
+.I a
+and
+.I b
+are equal
+(ie,
+both their
+.B path
+and
+.B vers
+members are equal);
+it returns 0 otherwise.
+.PP
+.I Eqchan
+returns 1 if
+.I a
+and
+.I b
+have the same
+.BR qid ,
+.BR type
+and
+.BR dev
+members
+(ie, they represent the same file);
+it returns 0 otherwise.
+If
+.I pathonly
+is non-zero, the comparison of the two
+.B qid
+members compares only their
+.B path
+values,
+ignoring the version field
+.BR vers .
+.PP
+.I Isdir
+checks that a given channel
+.I c
+is a directory.
+If so, it returns;
+otherwise, it generates an
+.IR error (10.2),
+.BR Enotdir .
+.PP
+The
+.B Fgrp
+structure represents an array of open files, each
+represented by a
+.BR Chan ,
+indexed by integer file descriptors.
+A given
+.B Fgrp
+can be shared between processes.
+.PP
+.I Fdtochan
+returns a pointer to the
+.B Chan
+corresponding to file descriptor
+.I fd
+in file descriptor group
+.I f
+(almost invariably
+.BR up->env->fgrp ,
+the file descriptor group for the current process).
+If
+.I mode
+is a valid mode for
+.IR sys-open (2),
+typically
+.BR OREAD ,
+.B OWRITE
+or
+.BR ORDWR ,
+it must correspond to the mode with which
+.I fd
+was originally opened; if
+.I mode
+is
+.BR -1 ,
+no check is made.
+If
+.I chkmnt
+is non-zero,
+.I c
+must not be a channel in use by the mount driver
+.IR mnt (3).
+On successful return, if
+.I iref
+is non-zero, the channel's reference count has been incremented.
+.I Fdtochan
+calls
+.IR error (10.2)
+if it detects invalid uses, in particular an invalid file descriptor
+.IR fd .
+.PP
+.I Namec
+looks up a
+.I pathname
+in the current name space and returns a channel.
+.I Amode
+determines the mode of look up, and must be one of the constants below:
+.TF Aaccess
+.PD
+.TP
+.B Aaccess
+Access file for information, as in the stat command or call.
+.TP
+.B Atodir
+Access file as directory (the
+.B QTDIR
+bit of its
+.B qid.type
+must be set).
+.TP
+.B Aopen
+Access for I/O.
+.TP
+.B Amount
+Access directory to be mounted upon.
+.TP
+.B Acreate
+File is to be created.
+.PP
+If
+.I amode
+is
+.B Aopen
+or
+.BR Acreate ,
+.I omode
+should be a mode suitable for
+.IR sys-open (2);
+if
+.BR Acreate ,
+.I perm
+should be valid file permissions.
+In all other cases,
+.I omode
+and
+.I perm
+can be zero.
+.PP
+.I Cclose
+decrements the reference count on
+.IR c ;
+if no further references remain, it
+calls the corresponding device's
+.B Dev.close
+to close the channel, and frees
+.IR c .
+.SH DIAGNOSTICS
+Most functions call
+.IR error (10.2)
+on any sort of error.
+.SH SOURCE
+.B /os/port/chan.c
+.br
+.B /emu/port/chan.c
+.SH SEE ALSO
+.IR ref (10.2)
--- /dev/null
+++ b/man/10/ntsrv
@@ -1,0 +1,69 @@
+.TH NTSRV 10.1 Windows
+.SH NAME
+ntsrv \- start hosted Inferno as Windows service
+.SH SYNOPSIS
+.B ntsrv
+.B add
+.IR "svcname rootdir cmd" " [" arg " ... ]"
+.PP
+.B ntsrv
+.B del
+.I svcname
+.SH DESCRIPTION
+.I Ntsrv
+is a command (for Windows only) that allows hosted Inferno
+to run automatically as a Windows service, under Windows 2000 and XP.
+For Windows NT, use the command
+.BR ntsrv4 ,
+which otherwise has identical interface.
+.I Svcname
+is the (proposed) Windows service name;
+.I rootdir
+is the root of the Inferno directory tree,
+including the drive letter.
+Once added, a service can be controlled using the Windows service manager:
+it may be stopped, started, or have its attributes changed.
+.I Svcname
+is set to start automatically when Windows starts up.
+Services run by default under the local Windows `System' identity.
+All Windows processes and threads the service creates (eg, using
+.IR cmd (3))
+are destroyed when the service is stopped.
+There may be many different services, each with a distinct
+.IR svcname .
+.PP
+.I Ntsrv 's
+first parameter tells it what to do:
+.TP
+.B add
+Add a new service
+.I svcname
+that when started will invoke
+.IR emu (1)
+to run the Inferno command
+.I cmd
+with any additional arguments.
+.TP
+.B del
+Delete an existing service
+.IR svcname .
+.PP
+.I Ntsrv
+requires the directory
+.IR rootdir /Nt/386/bin
+to contain both
+.B emu.exe
+(ie,
+.IR emu (1))
+and
+.B ntsrv.exe
+itself.
+.SH FILES
+.TF c:/inferno/Nt/386/bin
+.TP
+.IB rootdir /Nt/386/bin
+Windows host executables
+.SH SOURCE
+.B /utils/ntsrv
+.SH SEE ALSO
+.IR emu (1)
--- /dev/null
+++ b/man/10/odbc
@@ -1,0 +1,277 @@
+.TH ODBC 10.4
+.SH NAME
+ODBC \- A Windows ODBC file server
+.SH SYNOPSIS
+.B odbc.exe
+[
+.B -d
+] [
+.BI -p " port"
+] [
+.BI -u " user"
+]
+.SH DESCRIPTION
+.I Odbc
+is a file server that runs under Windows and
+exports a 9P namespace
+(see
+.IR intro (5)).
+An Inferno process that mounts the namespace
+can use it to manipulate Windows ODBC databases.
+.PP
+The
+.B -d
+option causes
+.I odbc
+to print debugging information.
+.PP
+The
+.B -p
+option gives the port number to listen on for connections. The default is 6700.
+.PP
+By default, the user
+.B inferno
+owns the files in the name space.
+The
+.B -u
+option makes
+.I user
+own the files instead.
+.SS Name space
+.I Odbc
+presents the following name space:
+.PP
+.nf
+.BI /nclients
+.BI /db
+.BI /db/new
+.BI /db/ n
+.BI /db/ n /cmd
+.BI /db/ n /ctl
+.BI /db/ n /data
+.BI /db/ n /error
+.BI /db/ n /format
+.BI /db/ n /sources
+.BI /db/ n /status
+.fi
+.PP
+The top level read-only file
+.B nclients
+contains the current number of active clients on the server.
+.PP
+The top level
+.B db
+directory contains a
+.B new
+file and subdirectories numbered
+.B n
+from zero to the maximum number of
+configured conversations.
+.PP
+Opening the
+.B new
+file reserves a conversation, represented
+by one of the numbered directories. The resulting file descriptor will be open
+on the control file,
+.BR ctl ,
+of the newly allocated conversation.
+Reading the
+.B ctl
+file returns a text string representing the
+number of the conversation.
+A conversation is used to converse with the server - in ODBC terms
+it is equivalent to obtaining a connection handle. This is the level at
+which ODBC transactions are managed.
+.PP
+A conversation is controlled by writing text strings to the
+associated
+.B ctl
+file. ODBC commands may be sent to the
+server by writing them, as text strings, to the
+.B cmd
+file.
+For commands that return a record set, the results may be read
+from the
+.B data
+file; each read returning a single record.
+If a command results in an error, the write to the
+.B cmd
+file
+will fail. The full ODBC error message can be obtained by reading
+the
+.B error
+file.
+A conversation remains open while at least one of the
+.BR ctl ,
+.B cmd
+or
+.B data
+files remains open.
+.PP
+The following commands can be written to the
+.B ctl
+file:
+.TP
+.B  connect " datasource" " [user!auth]"
+Connect to the ODBC datasource using the username and authentication, if given.
+.TP
+.B disconnect
+Disconnect from the datasource.
+.TP
+.B fixed
+Reads from the
+.B data
+file will return data in a fixed format. The format can
+be read from the
+.B format
+file after writing the command to the
+.B cmd
+file and before reading the data from the
+.B data
+file.
+.TP
+.B float " [fs< [rs<]]"
+Reads from the data file will return data using the character
+.B fs
+to separate fields and the character
+.B rs
+to separate records.
+The default values for
+.B fs and 
+.B rs
+are the pipe symbol '|'
+and the newline character.
+.TP
+.B trans " begin"
+Enter ODBC manual-commit mode for transactions. A transaction will
+not complete until one of
+.B trans commit
+or
+.B trans rollback
+is written to the
+.B ctl
+file.
+.TP
+.B trans " auto"
+Enter ODBC auto-commit mode for transactions (the default).
+Each database statement is wrapped by a transaction that is automatically
+commited when the statement is executed.
+.TP
+.B trans " commit"
+Commit a transaction when in manual-commit mode.
+.TP
+.B trans " rollback"
+Rollback a transation when in manual-commit mode.
+.PP
+Once a conversation has been established and transaction mode and
+output formats determined the
+.B cmd
+file is
+used to send ODBC commands to the server.
+The following commands can be written to the
+.B cmd
+file:
+.TP
+.B tables
+The result of calling the ODBC API function SQLTables is returned
+in the
+.B data
+file.
+.TP
+.B columns " tablename"
+The result of calling the ODBC API function SQLColumns with the
+given
+.B tablename
+as a parameter is returned in the
+.B data
+file.
+.TP
+.B any " ODBC" " command"
+Any ODBC command written to the
+.B cmd
+file is passed
+to the ODBC API function SQLExecDirect. This most commonly includes
+select, update, insert, and delete
+commands.
+.PP
+The
+.B format
+file is used to determine column names and how to extract
+individual columns from the record read from the
+.B data
+file when using
+fixed format output. A read of it gives
+a single record read returning one line
+for each column in the result data. Each line has three components separated
+by a single space: a number giving the character position of the start of the field
+in the result data, a number giving the character position one beyond the
+end of the field in the result data, and the field name.
+.PP
+The result of database enquiries can be read from the
+.B data
+file.
+After writing a command that returns data to the
+.B cmd
+file,
+reads from the
+.B data
+file will return the results one record at a time.
+When the last record has been read the following read will return zero
+bytes indicating the end of the data.
+.PP
+The read-only file
+.B sources
+gives a newline separated list of sources. Each line consists
+of the source name and the source type separated by a colon.
+.PP
+The read-only file
+.B status
+return the status of the current conversation.
+.SH EXAMPLE
+For example, the Inferno shell can be used to retrieve
+values from a database. The shell commands:
+.PP
+.EX
+mount -A tcp!localhost!6700 /n/remote
+{
+	d=/n/remote/db/`{cat}
+	echo -n 'float' > $d/ctl
+	echo -n 'connect cellar' > $d/ctl
+	echo -n 'select name from wine' > $d/cmd
+	cat $d/data
+} < /n/remote/db/new
+.EE
+.PP
+produces the output:
+.PP
+.EX
+Chardonnay
+Jo. Riesling
+Fume Blanc
+Wh. Burgundy
+Gewurztraminer
+Cab. Sauvignon
+Pinot Noir
+Zinfandel
+Gamay
+.EE
+.PP
+Here the server has been started on the local machine, listening on port
+6700 for network connections.
+The braced block and the redirection from
+.B /n/remote/db/new
+reserve a conversation with the server and ensures that it remains open
+for the duration of the execution of the set of commands within the block.
+The
+.B -A
+option to mount is used because this server can not
+authenticate a connection.
+.SH SOURCE
+.B /tools/odbc/odbc.c
+.SH SEE ALSO
+.IR bind (1),
+.IR sys-bind (2),
+.IR intro (5),
+.IR db (7),
+.IR dbsrv (7),
+.IR svc (8)
--- /dev/null
+++ b/man/10/panic
@@ -1,0 +1,25 @@
+.TH PANIC 10.2
+.SH NAME
+panic \- abandon hope
+.SH SYNOPSIS
+.ta \w'\fLchar* 'u
+.B
+void	panic(char *fmt, ...)
+.SH DESCRIPTION
+.I Panic
+writes a message to the console and
+causes the system to give up the ghost.
+It enables interrupts, dumps the kernel stack,
+and halts the current processor;
+if more than one, others will gradually come to a halt.
+Depending on configuration settings, the platform-dependent
+.I exit
+might reboot the system.
+The format
+.I fmt
+and associated arguments are the same as those for
+.IR print (10.2).
+.I Panic
+adds a prefix
+.L "panic: "
+and a trailing newline.
--- /dev/null
+++ b/man/10/parsecmd
@@ -1,0 +1,68 @@
+.TH PARSECMD 10.2
+.SH NAME
+parsecmd \- parse device commands
+.SH SYNOPSIS
+.ta \w'\fLCmdbuf* 'u
+.B
+Cmdbuf*	parsecmd(char *a, int n)
+.SH DESCRIPTION
+.I Parsecmd
+is an interface to
+.I tokenize
+(see
+.IR getfields (10.2)),
+that safely parses a command, with blank-separated fields, as might be
+written to a device's
+.B ctl
+file.
+The buffer
+.I a
+and count
+.I n
+can be those passed to the driver's
+.I write
+function.
+.I Parsecmd
+converts the byte array (which might not be null-terminated) to a null-terminated string,
+trimming any trailing new line,
+before invoking
+.I tokenize
+to break the string into arguments, interpreting blank and tab as field separators
+when they are not quoted
+(in the style of
+.IR sh (1)).
+It returns a pointer to a dynamically-allocated
+.B Cmdbuf
+structure,
+which holds a copy of the string as
+modified by
+.IR parsefields ,
+and the resulting fields; it is defined as follows:
+.IP
+.EX
+.ta 6n +\w'char* 'u
+typedef
+struct Cmdbuf
+{
+	char	buf[128];
+	char	*f[16];
+	int	nf;
+} Cmdbuf;
+.EE
+.PP
+The array
+.B f
+holds the field pointers;
+.B nf
+gives the number of fields.
+.B Cmdbuf
+is allocated by
+.I smalloc
+(see
+.IR malloc (10.2)),
+and the caller is responsible for freeing it using
+.IR free .
+.SH SOURCE
+.B /os/port/parse.c
+.br
+.B /emu/port/dev.c
--- /dev/null
+++ b/man/10/plan9.ini
@@ -1,0 +1,789 @@
+.TH PLAN9.INI 10.6
+.SH NAME
+plan9.ini \- configuration file for PCs
+.SH SYNOPSIS
+.I none
+.SH DESCRIPTION
+When booting Inferno on a PC, the Plan 9 bootstrap programs
+are used, hence the references to Plan 9 below.
+The DOS program
+.IR 9load (10.8)
+first reads a DOS file
+containing configuration information from the boot disk.
+This file,
+.BR plan9.ini ,
+looks like a shell script containing lines of the form
+.PP
+.EX
+	name=\f2value\fP
+.EE
+.LP
+each of which defines a kernel or device parameter.
+.PP
+For devices, the generic format of
+.I value
+is
+.PP
+.EX
+	type=TYPE [port=N] [irq=N] [mem=N] [size=N] [dma=N] [ea=N]
+.EE
+.LP
+specifying the controller type,
+the base I/O port of the interface, its interrupt
+level, the physical starting address of any mapped memory,
+the length in bytes of that memory, the DMA channel,
+and for Ethernets an override of the physical network address.
+Not all elements are relevant to all devices; the relevant values
+and their defaults are defined below in the description of each device.
+.PP
+The file is used by
+.B 9load
+and the kernel to configure the hardware available.
+The information it contains is also passed to the boot
+process, and subsequently other programs,
+as environment variables
+(see also
+.B osinit.dis
+in
+.IR root (3)).
+However, values whose names begin with an asterisk
+.B *
+are used by the kernel and are not converted into environment variables.
+.PP
+The following sections describe how variables are used.
+.SS \fLetherX=value\fP
+This defines an Ethernet interface.
+.IR X ,
+a unique monotonically increasing number beginning at 0,
+identifies an Ethernet card to be probed at system boot.
+Probing stops when a card is found or there is no line for
+.BR etherX+1 .
+After probing as directed by the
+.BI ether X
+lines, any remaining ethernet cards that can be automatically
+detected are added.
+Almost all cards can be automatically detected.
+For debugging purposes, automatic probing can
+be disabled by specifying the line
+.BR *noetherprobe= .
+This automatic probing is only done by the kernel, not by
+.IR 9load (10.8).
+Thus, if you want to load a kernel over the ethernet, you need
+to specify an
+.B ether0
+line so that
+.I 9load
+can find the ethernet card, even if the kernel would
+have automatically detected it.
+.PP
+Some cards are software configurable and do not require all options.
+Unspecified options default to the factory defaults.
+.PP
+Known types are
+.TP
+.B ne2000
+Not software configurable. 16-bit card.
+Defaults are
+.EX
+	port=0x300 irq=2 mem=0x04000 size=0x4000
+.EE
+The option (no value)
+.B nodummyrr
+is needed on some (near) clones to turn off a dummy remote read in the driver.
+.TP
+.B amd79c970
+The AMD PCnet PCI Ethernet Adapter (AM79C970).
+(This is the ethernet adapter used by VMware.)
+Completely configurable, no options need be given.
+.TP
+.B wd8003
+Includes WD8013 and SMC Elite and Elite Ultra cards. There are varying degrees
+of software configurability. Cards may be in either 8-bit or 16-bit slots.
+Defaults are
+.EX
+	port=0x280 irq=3 mem=0xD0000 size=0x2000
+.EE
+BUG: On many machines only the 16 bit card works.
+.TP
+.B elnk3
+The 3COM Etherlink III series of cards including the 5x9, 59x, and 905 and 905B.
+Completely configurable, no options need be given.
+The media may be specified by setting
+.B media=
+to the value
+.BR 10BaseT ,
+.BR 10Base2 ,
+.BR 100BaseTX ,
+.BR 100BaseFX ,
+.BR aui ,
+and
+.BR mii .
+If you need to force full duplex, because for example the Ethernet switch does not negotiate correctly,
+just name the word (no value)
+.B fullduplex
+or
+.BR 100BASE-TXFD .
+Similarly, to force 100Mbit operation, specify
+.BR force100 .
+Port 0x110 is used for the little ISA configuration dance.
+.TP
+.B 3c589
+The 3COM 3C589 series PCMCIA cards, including the
+3C562 and the 589E.
+There is no support for the modem on the 3C562.
+Completely configurable, no options need be given.
+Defaults are
+.EX
+	port=0x240 irq=10
+.EE
+The media may be specified as
+.B media=10BaseT
+or
+.BR media=10Base2 .
+.TP
+.B ec2t
+The Linksys Combo PCMCIA EthernetCard (EC2T),
+EtherFast 10/100 PCMCIA cards (PCMPC100) and integrated controllers (PCM100),
+the Netgear FA410TX 10/100 PCMCIA card
+and the Accton EtherPair-PCMCIA (EN2216).
+Completely configurable, no options need be given.
+Defaults are
+.EX
+	port=0x300 irq=9
+.EE
+These cards are NE2000 clones.
+Other NE2000 compatible PCMCIA cards may be tried
+with the option
+.EX
+	id=string
+.EE
+where
+.B string
+is a unique identifier string contained in the attribute
+memory of the card (see
+.IR pcmcia (8));
+unlike most options in 
+.BR plan9.ini ,
+this string is case-sensitive.
+The option
+.B dummyrr=[01]
+can be used to turn off (0) or on (1) a dummy remote read in the driver
+in such cases,
+depending on how NE2000 compatible they are.
+.TP
+.B i82557
+Cards using the Intel 8255[789] Fast Ethernet PCI Bus LAN Controller such as the
+Intel EtherExpress PRO/100B.
+Completely configurable, no options need be given.
+If you need to force the media, specify
+one of the options (no value)
+.BR 10BASE-T ,
+.BR 10BASE-2 ,
+.BR 10BASE-5 ,
+.BR 100BASE-TX ,
+.BR 10BASE-TFD ,
+.BR 100BASE-TXFD ,
+.BR 100BASE-T4 ,
+.BR 100BASE-FX ,
+or
+.BR 100BASE-FXFD .
+.TP
+.B 2114x
+Cards using the Digital Equipment (now Intel) 2114x PCI Fast Ethernet Adapter Controller,
+for example the Netgear FA310.
+Completely configurable, no options need be given.
+Media can be specified the same was as for the
+.BR i82557 .
+Some cards using the
+.B PNIC
+and
+.B PNIC2
+near-clone chips may also work.
+.\" .TP
+.\" .B ga620
+.\" Netgear GA620 and GA620T Gigabit Ethernet cards,
+.\" and other cards using the Alteon Acenic chip such as the
+.\" Alteon Acenic fiber and copper cards,
+.\" the DEC DEGPA-SA and the SGI Acenic.
+.\" Completely configurable.
+.TP
+.B wavelan
+Lucent Wavelan (Orinoco) IEEE 802.11b 
+and compatible PCMCIA cards.
+Compatible cards include the Dell TrueMobile 1150
+and the Linksys Instant Wireless Network PC Card.
+Port and IRQ defaults are 0x180 and 3 respectively.
+
+These cards take a number of unique options to aid in
+identifying the card correctly on the 802.11b network.
+The network may be
+.I "ad hoc"
+or
+.I managed
+(i.e. use an access point):
+.EX
+	mode=[adhoc, managed]
+.EE
+and defaults to
+.IR managed .
+The 802.11b network to attach to
+.RI ( managed
+mode)
+or identify as
+.RI ( "ad hoc"
+mode),
+is specified by
+.EX
+	essid=string
+.EE
+and defaults to a null string.
+The card station name is given by
+.EX
+	station=string
+.EE
+and defaults to
+.IR "Plan 9 STA" .
+The channel to use is given by
+.EX
+	channel=number
+.EE
+where
+.I number
+lies in the range 1 to 16 inclusive;
+the channel is normally negotiated automatically.
+
+If the card is capable of encryption,
+the following options may be used:
+.EX
+	crypt=[off, on]
+.EE
+and defaults to
+.IR on .
+.EX
+	key\fIN\fP=string
+.EE
+sets the encryption key
+.I n
+(where
+.I n
+is in the range 1 to 4 inclusive) to
+.IR string ;
+this will also set the transmit key to
+.I n
+(see below).
+.EX
+	txkey=number
+.EE
+sets the transmit key to use to be
+.I number
+in the range 1 to 4 inclusive.
+If it is desired to exclude or include unencrypted packets
+.EX
+	clear=[off, on]
+.EE
+configures reception and defaults to inclusion.
+
+The defaults are intended to match the common case of
+a managed network with encryption and a typical entry would 
+only require, for example
+.EX
+	essid=left-armpit key2=fishcalledraawaru
+.EE
+if the port and IRQ defaults are used.
+These options may be set after boot by writing to the device's
+.I ctl
+file using a space as the separator between option and value, e.g.
+.EX
+	echo 'key2 fishcalledraawaru' > /net/ether0/0/ctl
+.EE
+.TP
+.B wavelanpci
+PCI ethernet adapters that use the same Wavelan
+programming interface.
+Currently the only tested cards are those based on the
+Intersil Prism 2.5 chipset.
+.TP
+.B 83815
+National Semiconductor DP83815-based adapters, notably
+the Netgear FA311, Netgear FA312, and various SiS built-in
+controllers such as the SiS900.
+On the SiS controllers, the ethernet address is not detected properly;
+specify it with an
+.B ea=
+attribute.
+.\" .TP
+.\" .B 83820
+.\" National Semiconductor DP83820-based gigabit ethernet adapters, notably
+.\" the D-Link DGE-500T.
+.TP
+.B rtl8139
+The Realtek 8139.
+.TP
+.B 82543gc
+The Intel RS-82543GC gigabit ethernet controller,
+as found on the Intel PRO/1000[FT] server adapter.
+The older non-[FT] cards based on the 82542 (LSI L2A1157)
+chip are not supported, although support would probably be
+easy to add.
+.TP
+.B smc91cxx
+SMC 91cXX chip-based PCMCIA adapters, notably the SMC EtherEZ card.
+.TP
+.B sink
+A
+.B /dev/null
+for ethernet packets \(em the interface discards sent
+packets and never receives any.
+This is used to provide a test bed for
+some experimental ethernet bridging software.
+.SS \fLusbX=type=uhci port=xxx irq=xxx\fP
+This specifies the settings for a USB UHCI controller.
+Like the ethernet controllers, USB controllers are autodetected
+after scanning for the ones listed in
+.IR plan9.ini .
+Thus, most systems will not need a
+.B usbX
+line.
+Also like the ethernet controllers, USB autoprobing can be
+disabled by specifying the line
+.BR *nousbprobe= .
+.SS \fLscsiX=value\fP
+This defines a SCSI interface which cannot be automatically detected
+by the kernel.
+.PP
+Known types are
+.TP
+.B aha1542
+The Adaptec 154x series of controllers (and clones).
+Almost completely configurable, only the
+.EX
+	port=0x300
+.EE
+option need be given.
+.PP
+NCR/Symbios/LSI Logic 53c8xx-based adapters
+and Mylex MultiMaster (Buslogic BT-*) adapters are
+automatically detected and need no entries.
+.PP
+By default, the NCR 53c8xx driver searches for up to 32 controllers.
+This can be changed by setting the variable
+.BR *maxsd53c8xx .
+.PP
+By default the Mylex driver resets SCSI cards by using
+both the hard reset and SCSI bus reset flags in the driver interface.
+If a variable
+.BR *noscsireset
+is defined, the SCSI bus reset flag is omitted.
+.SS Uarts
+Plan 9 automatically configures COM1 and COM2, if found,
+as
+.B eia0
+(port 0x3F8, IRQ4)
+and
+.B eia1
+(port 0x2F8, IRQ3)
+respectively.
+These devices can be disabled by adding a line:
+.EX
+    eia\fIX\fP=disabled
+.EE
+This is typically done in order to reuse the IRQ for 
+another device.
+.PP
+The system used to support various serial concentrators,
+including the TTC 8 serial line card and various models
+in the Star Gate Avanstar series of intelligent serial boards.
+These are no longer supported; the much simpler
+Perle PCI-Fast4, PCI-Fast8, and PCI-Fast16 controllers
+have taken their places.
+These latter cards are automatically detected
+and need no configuration lines.
+.PP
+The line
+.B serial=type=com
+can be used to specify settings for a PCMCIA modem.
+.SS \fLmouseport=value\fP
+This specifies where the mouse is attached.
+.I Value
+can be
+.TP
+.B ps2
+the PS2 mouse/keyboard port.  The BIOS setup procedure
+should be used to configure the machine appropriately.
+.TP
+.B ps2intellimouse
+an Intellimouse on the PS2 port.
+.TP
+.B 0
+for COM1
+.TP
+.B 1
+for COM2
+.SS \fLmodemport=value\fP
+Picks the UART line to call out on.
+This is used when connecting to a file server over
+an async line.
+.I Value
+is the number of the port.
+.SS \fLpccard0=disabled\fP
+Disable probing for and automatic configuration of PC card controllers.
+.SS \fLpcmciaX=type=XXX irq=value\fP
+If the default IRQ for the
+PCMCIA
+is correct, this entry can be omitted.  The value of
+.B type
+is ignored.
+.SS \fLpcmcia0=disabled\fP
+Disable probing for and automatic configuration of PCMCIA controllers.
+.SS \fLconsole=value params\fP
+This is used to specify the console device.
+The default
+value is
+.BR cga ;
+a number
+.B 0
+or
+.B 1
+specifies
+.I COM1
+or
+.I COM2
+respectively.
+A serial console is initially configured with the
+.IR eia (3)
+configuration string
+.B b9600
+.B l8
+.B pn
+.BR s1 ,
+specifying 9600 baud,
+8 bit bytes, no parity, and one stop bit.
+If
+.I params
+is given, it will be used to further
+configure the uart.
+Notice that there is no
+.B =
+sign in the
+.I params
+syntax.
+For example,
+.EX
+    console=0 b19200 po
+.EE
+would use COM1 at 19,200 baud
+with odd parity.
+.SS \fLbootfile=value\fP
+This is used to direct the actions of
+.IR 9load (10.8)
+by naming the device and file from which to load the kernel.
+.SS \fLpartition=value\fP
+This defines the partition table
+.IR 9load (10.8)
+will examine to find disk partitioning information.
+By default, a partition table in a Plan 9 partition
+is consulted; if no such table is found, an old-Plan 9
+partition table on the next-to-last or last sector
+of the disk is consulted.
+A value of 
+.B new
+consults only the first table,
+.B old
+only the second.
+.SS \fL*maxmem=value\fP
+This defines the maximum physical address that the system will scan when sizing memory.
+By default the operating system will scan up to 768 megabytes, but setting
+.B *maxmem
+will limit the scan.
+If the system has more than 768 megabytes, you must set
+.B *maxmem
+for the kernel to find it.
+.B *maxmem
+must be less than 1.75 gigabytes.
+.SS \fL*kernelpercent=value\fP
+This defines what percentage of available memory is reserved for the kernel allocation pool.
+The remainder is left for user processes.  The default
+.I value
+is
+.B 30
+on CPU servers,
+.B 60
+on terminals with less than 16MB of memory,
+and
+.B 40
+on terminals with memories of 16MB or more.
+Terminals use more kernel memory because
+.IR draw (3)
+maintains its graphic images in kernel memory.
+This deprecated option is rarely necessary in newer kernels.
+.SS \fL*nomce=value\fP
+If machine check exceptions are supported by the processor,
+then they are enabled by default.
+Setting this variable to
+.B 1
+causes them to be disabled even when available.
+.SS \fL*nomp=\fP
+A multiprocessor machine will enable all processors by default.
+Setting
+.B *nomp
+restricts the kernel to starting only one processor and using the
+traditional interrupt controller.
+.SS \fL*ncpu=value\fP
+Setting
+.B *ncpu
+restricts the kernel to starting at most
+.I value
+processors.
+.SS \fL*pcimaxbno=value\fP
+This puts a limit on the maximum bus number probed
+on a PCI bus (default 255).
+For example, a
+.I value
+of 1 should suffice on a 'standard' motherboard with an AGP slot.
+This, and 
+.B *pcimaxdno
+below are rarely used and only on troublesome or suspect hardware.
+.SS \fL*pcimaxdno=value\fP
+This puts a limit on the maximum device number probed
+on a PCI bus (default 31).
+.SS \fL*nopcirouting=\fP
+Disable pci routing during boot.  May solve interrupt routing
+problems on certain machines.
+.\" .SS \fL*nobios=\fP
+.\" what does this do?  something with pci
+.SS \fLioexclude=value\fP
+Specifies a list of ranges I/O ports to exclude from use by drivers.
+Ranges are inclusive on both ends and separated by commas.
+For example:
+.EX
+    ioexclude=0x330-0x337,0x430-0x43F
+.EE
+.SS \fLapm0=\fP
+This enables the ``advanced power management'' interface
+as described in
+.IR apm (3).
+....and
+.....IR apm (8).
+The main feature of the interface is the ability to watch
+battery life.
+....battery life (see
+.....IR stats (8)).
+It is not on by default because it causes problems on some laptops.
+.SS \fLmonitor=value\fP
+.SS \fLvgasize=value\fP
+These are used not by the kernel but by system initialisation.
+.SS \fL*dpms=value\fP
+This is used to specify the screen blanking behavior of the MGA4xx
+video driver.
+Values are
+.BR standby ,
+.BR suspend ,
+and
+.BR off .
+The first two specify differing levels of power saving;
+the third turns the monitor off completely.
+.SS \fLnvr=value\fP
+This is used by a file server kernel to locate a file holding information
+to configure the file system.
+The file cannot live on a SCSI disk.
+The default is
+.B fd!0!plan9.nvr
+(sic),
+unless
+.B bootfile
+is set, in which case it is
+.B plan9.nvr
+on the same disk as
+.BR bootfile .
+The syntax is either
+.BI fd! unit ! name
+or
+.BI hd! unit ! name
+where
+.I unit
+is the numeric unit id.
+This variant syntax is a vestige of the file server kernel's origins.
+.SS \fLaudioX=value\fP
+This defines a sound interface.
+.PP
+Known types are
+.TP
+.B sb16
+Sound Blaster 16.
+.TP
+.B ess1688
+A Sound Blaster clone.
+.PP
+The DMA channel may be any of 5, 6, or 7.
+The defaults are
+.EX
+	port=0x220 irq=7 dma=5
+.EE
+.SS \fLfs=a.b.c.d\fP
+.SS \fLauth=a.b.c.d\fP
+These specify the IP address of the file and authentication server
+to use when mounting a network-provided root file system.
+They are used only if the addresses cannot be determined via DHCP.
+.SH Multiple Configurations
+.PP
+A
+.B plan9.ini
+file may contain multiple configurations,
+each within a block beginning with a line
+.EX
+	[tag]
+.EE
+A special block with the tag
+.B menu
+gives a list of blocks from which the user may
+interactively select the contents of
+.BR plan9.ini .
+There may also be multiple blocks with the tag
+.B common
+which will be included in all selections;
+if any lines appear in
+.B plan9.ini
+before the first block,
+they are treated as a
+.B common
+block.
+.LP
+Within the
+.B menu
+block the following configuration lines are allowed:
+.SS \fLmenuitem=tag[, description]
+The block identified by
+.B tag
+will appear in the presented menu.
+The menu entry will consist of the
+.B tag
+unless the optional
+.B description
+is given.
+.SS \fLmenudefault=tag[, timeout]
+Identifies a default block to be given in the
+menu selection prompt.
+If the optional
+.B timeout
+is given (in seconds),
+the default block will be selected if there is no user
+input within the timeout period.
+.SS \fLmenuconsole=value[, baud]
+Selects a serial console upon which to present the menu
+as no
+.B console
+or
+.B baud
+configuration information will have been processed yet
+(the
+.B plan9.ini
+contents are still to be decided...).
+.LP
+In response to the menu being printed,
+the user is prompted to select a menu item from the list.
+If the numeric response is followed by a
+.BR p ,
+the selected configuration is printed and the menu presented
+again.
+.LP
+The line
+.EX
+	menuitem=tag
+.EE
+is prefixed to the selected configuration as an aid to
+user-level initialization scripts.
+.SH EXAMPLES
+.PP
+A representative
+.BR plan9.ini :
+.IP
+.EX
+% cat /n/c:/plan9.ini
+ether0=type=3C509
+mouseport=ps2
+modemport=1
+serial0=type=generic port=0x3E8 irq=5
+monitor=445x
+vgasize=1600x1200x8
+% 
+.EE
+.PP
+Minimum CONFIG.SYS and AUTOEXEC.BAT files to use
+COM2 as a console:
+.IP
+.EX
+% cat /n/c:/config.sys
+SHELL=COMMAND.COM COM2 /P
+% cat /n/c:/autoexec.bat
+@ECHO OFF
+PROMPT $p$g
+PATH C:\eDOS;C:\eBIN
+mode com2:96,n,8,1,p
+SET TEMP=C:\eTMP
+% 
+.EE
+.PP
+Simple
+.B plan9.ini
+with multiple configurations:
+.IP
+.EX
+[menu]
+menuitem=vga, Plan 9 with VGA
+menuitem=novga, Plan 9 no automatic VGA
+menudefault=vga
+
+[vga]
+monitor=multisync135
+vgasize=1024x768x8
+
+[novga]
+
+[common]
+ether0=type=i82557
+audio0=type=sb16 port=0x220 irq=5 dma=1
+.EE
+.PP
+With this, the following menu will be presented on boot:
+.IP
+.EX
+Plan 9 Startup Menu:
+====================
+    1. Plan 9 with VGA
+    2. Plan 9 no automatic VGA
+Selection[default==1]: 
+.EE
+.PP
+Selecting item 1 generates the following
+.B plan9.ini
+to be used by the remainder of the bootstrap process:
+.IP
+.EX
+menuitem=vga
+monitor=multisync135
+vgasize=1024x768x8
+ether0=type=i82557
+audio0=type=sb16 port=0x220 irq=5 dma=1
+.EE
+.PP
+and selecting item 2:
+.IP
+.EX
+menuitem=novga
+ether0=type=i82557
+audio0=type=sb16 port=0x220 irq=5 dma=1
+.EE
+.SH "SEE ALSO"
+.IR root (3),
+.IR 9load (10.8)
+.SH BUGS
+Being able to set the console device to other than a
+display is marginally useful on file servers; MS-DOS
+and the programs which run under it are so tightly bound
+to the display that it is necessary to have a display if any
+setup or reconfiguration programs need to be run.
+Also, the delay before any messages appear at boot time
+is disconcerting, as any error messages from the BIOS
+are lost.
+.PP
+This idea is at best an interesting experiment that needs another iteration.
--- /dev/null
+++ b/man/10/print
@@ -1,0 +1,402 @@
+.TH PRINT 10.2
+.SH NAME
+print, fprint, sprint, snprint, seprint, smprint, vfprint, vsnprint, vseprint, vsmprint \- print formatted output
+.SH SYNOPSIS
+.ta \w'\fLchar* 'u
+.B
+int	print(char *format, ...)
+.PP
+.B
+int	fprint(int fd, char *format, ...)
+.PP
+.B
+int	sprint(char *s, char *format, ...)
+.PP
+.B
+int	snprint(char *s, int len, char *format, ...)
+.PP
+.B
+char*	seprint(char *s, char *e, char *format, ...)
+.PP
+.B
+char*	smprint(char *format, ...)
+.PP
+.B
+int	vfprint(int fd, char *format, va_list v)
+.PP
+.B
+int	vsnprint(char *s, int len, char *format, va_list v)
+.PP
+.B
+char*	vseprint(char *s, char *e, char *format, va_list v)
+.PP
+.B
+char*	vsmprint(char *format, va_list v)
+.SH DESCRIPTION
+.I Print
+writes text to the standard output.
+.I Fprint
+writes to the named output
+file descriptor;
+a buffered form
+is described in
+.IR bio (2).
+.I Sprint
+places text
+followed by the NUL character
+.RB ( \e0 )
+in consecutive bytes starting at
+.IR s ;
+it is the user's responsibility to ensure that
+enough storage is available.
+Each function returns the number of bytes
+transmitted (not including the NUL
+in the case of
+.IR sprint ),
+or
+a negative value if an output error was encountered.
+.PP
+.I Snprint
+is like
+.IR sprint ,
+but will not place more than
+.I len
+bytes in
+.IR s .
+Its result is always NUL-terminated and holds the maximal
+number of complete UTF-8 characters that can fit.
+.I Seprint
+is like
+.IR snprint ,
+except that the end is indicated by a pointer
+.I e
+rather than a count and the return value points to the terminating NUL of the
+resulting string.
+.I Smprint
+is like
+.IR sprint ,
+except that it prints into and returns a string of the required length, which is
+allocated by
+.IR malloc (10.2).
+.PP
+Finally, the routines
+.IR vfprint ,
+.IR vsnprint ,
+.I vseprint
+and
+.I vsmprint
+are like their
+.BR v-less
+relatives except they take as arguments a
+.B va_list
+parameter, so they can be called within a variadic function.
+The Example section shows a representative usage.
+.PP
+Each of these functions
+converts, formats, and prints its
+trailing arguments
+under control of a
+.IR format 
+string.
+The
+format
+contains two types of objects:
+plain characters, which are simply copied to the
+output stream,
+and conversion specifications,
+each of which results in fetching of
+zero or more
+arguments.
+The results are undefined if there are arguments of the
+wrong type or too few
+arguments for the format.
+If the format is exhausted while
+arguments remain, the excess
+is ignored.
+.PP
+Each conversion specification has the following format:
+.IP
+.B "% [flags] verb
+.PP
+The verb is a single character and each flag is a single character or a
+(decimal) numeric string.
+Up to two numeric strings may be used;
+the first is called
+.IR width ,
+the second
+.IR precision .
+A period can be used to separate them, and if the period is
+present then
+.I width
+and
+.I precision
+are taken to be zero if missing, otherwise they are `omitted'.
+Either or both of the numbers may be replaced with the character
+.BR * ,
+meaning that the actual number will be obtained from the argument list
+as an integer.
+The flags and numbers are arguments to
+the
+.I verb
+described below.
+.PP
+The numeric verbs
+.BR d ,
+.BR o ,
+.BR b ,
+.BR x ,
+and
+.B X
+format their arguments in decimal,
+octal, binary, hexadecimal, and upper case hexadecimal.
+Each interprets the flags
+.BR 0 ,
+.BR h ,
+.BR hh ,
+.BR l ,
+.BR u ,
+.BR + ,
+.BR - ,
+.BR , ,
+and
+.B #
+to mean pad with zeros,
+short, byte, long, unsigned, always print a sign, left justified, commas every three digits,
+and alternate format.
+Also, a space character in the flag
+position is like
+.BR + ,
+but prints a space instead of a plus sign for non-negative values.
+If neither
+short nor long is specified,
+then the argument is an
+.BR int .
+If unsigned is specified,
+then the argument is interpreted as a
+positive number and no sign is output.
+If two
+.B l
+flags are given,
+then the argument is interpreted as a
+.B vlong
+(usually an 8-byte, sometimes a 4-byte integer).
+If
+.I precision
+is not omitted, the number is padded on the left with zeros
+until at least
+.I precision
+digits appear.
+Then, if alternate format is specified,
+for
+.B o
+conversion, the number is preceded by a
+.B 0
+if it doesn't already begin with one;
+for
+.B x
+conversion, the number is preceded by
+.BR 0x ;
+for
+.B X
+conversion, the number is preceded by
+.BR 0X .
+Finally, if
+.I width
+is not omitted, the number is padded on the left (or right, if
+left justification is specified) with enough blanks to
+make the field at least
+.I width
+characters long.
+.PP
+The floating point verbs
+.BR f ,
+.BR e ,
+.BR E ,
+.BR g ,
+and
+.B G
+take a
+.B double
+argument.
+Each interprets the flags
+.BR + ,
+.BR - ,
+and
+.B #
+to mean
+always print a sign,
+left justified,
+and
+alternate format.
+.I Width
+is the minimum field width and,
+if the converted value takes up less than
+.I width
+characters, it is padded on the left (or right, if `left justified')
+with spaces.
+.I Precision
+is the number of digits that are converted after the decimal place for
+.BR e ,
+.BR E ,
+and
+.B f
+conversions,
+and
+.I precision
+is the maximum number of significant digits for
+.B g
+and
+.B G
+conversions.
+The 
+.B f
+verb produces output of the form
+.RB [ - ] digits [ .digits\fR].
+.B E
+conversion appends an exponent
+.BR E [ - ] digits ,
+and
+.B e
+conversion appends an exponent
+.BR e [ - ] digits .
+The
+.B g
+verb will output the argument in either
+.B e
+or
+.B f
+with the goal of producing the smallest output.
+Also, trailing zeros are omitted from the fraction part of
+the output, and a trailing decimal point appears only if it is followed
+by a digit.
+The
+.B G
+verb is similar, but uses
+.B E
+format instead of
+.BR e .
+When alternate format is specified, the result will always contain a decimal point,
+and for
+.B g
+and
+.B G
+conversions, trailing zeros are not removed.
+.PP
+The
+.B s
+verb copies a string
+(pointer to
+.BR char )
+to the output.
+The number of characters copied
+.RI ( n )
+is the minimum
+of the size of the string and
+.IR precision .
+These
+.I n
+characters are justified within a field of
+.I width
+characters as described above.
+The
+.B S
+verb is similar, but it interprets its pointer as an array
+of runes (see
+.IR utf (6));
+the runes are converted to
+.SM UTF
+before output.
+.PP
+The
+.B c
+verb copies a single
+.B char
+(promoted to
+.BR int )
+justified within a field of
+.I width
+characters as described above.
+The
+.B C
+verb is similar, but works on runes.
+.PP
+The
+.B p
+verb formats a pointer value.
+At the moment, it is a synonym for
+.BR ux ,
+but that will change once pointers and integers are different sizes.
+.PP
+The
+.B r
+verb takes no arguments; it copies the error string returned by a call to
+the emulated environment's
+`system calls'.
+It must not be used within the kernels.
+....PP
+...Custom verbs may be installed using
+....IR fmtinstall (2).
+.SH EXAMPLE
+This function prints an error message with a variable
+number of arguments and then quits.
+.IP
+.EX
+.ta 6n +6n +6n
+void fatal(char *msg, ...)
+{
+	char buf[1024], *out;
+	va_list arg;
+
+	out = vseprint(buf, buf+sizeof(buf), "Fatal error: ");
+	va_start(arg, msg);
+	out = vseprint(out, buf+sizeof(buf), msg, arg);
+	va_end(arg);
+	write(2, buf, out-buf);
+	exits("fatal error");
+}
+.EE
+.SH SOURCE
+.B /lib9/fmt*
+.br
+.B /libkern/fmt*
+.SH SEE ALSO
+.IR utf (6)
+.SH DIAGNOSTICS
+Routines that write to a file descriptor or call
+.IR malloc
+set
+.IR errstr .
+.SH BUGS
+The formatting is close to that specified for ANSI
+.IR fprintf (2);
+the main difference is that
+.B b
+is not in ANSI and
+.B u
+is a flag here instead of a verb.
+Also, and distinctly not a bug,
+.I print
+and friends generate
+.SM UTF
+rather than
+.SM ASCII.
+.PP
+There is no
+.BR runeprint ,
+.BR runefprint ,
+etc. because runes are byte-order dependent and should not be written directly to a file; use the
+UTF output of
+.I print
+or
+.I fprint
+instead.
+Also,
+.I sprint
+is deprecated for safety reasons; use
+.IR snprint ,
+.IR seprint ,
+or
+.I smprint
+instead.
+Safety also precludes the existence of
+.IR runesprint .
--- /dev/null
+++ b/man/10/qio
@@ -1,0 +1,482 @@
+.TH QIO 10.2
+.SH NAME
+qio: qget, qdiscard, qconsume, qpass, qproduce, qcopy, qopen, qbread, qread, qbwrite, qwrite, qiwrite, qfree, qclose, qhangup, qreopen, qlen, qwindow, qcanread, qsetlimit, qnoblock, qflush, qfull \- queued I/O for devices
+.SH SYNOPSIS
+.ta \w'\fLQueue*  'u
+.B
+Queue*	qopen(int limit,int msg, void (*kick)(void*),void *arg)
+.PP
+.B
+void	qhangup(Queue *q, char *reason)
+.PP
+.B
+void	qclose(Queue *q)
+.PP
+.B
+void	qreopen(Queue *q)
+.PP
+.B
+void	qfree(Queue *q)
+.PP
+.B
+long	qbwrite(Queue *q, Block *b)
+.PP
+.B
+long	qwrite(Queue *q, void *buf, int len)
+.PP
+.B
+int	qpass(Queue *q, Block *b)
+.PP
+.B
+int	qpassnolim(Queue *q, Block *b)
+.PP
+.B
+int	qproduce(Queue *q, void	*buf, int len)
+.PP
+.B
+int	qiwrite(Queue *q, void *buf, int len)
+.PP
+.B
+Block*	qbread(Queue *q, int len)
+.PP
+.B
+long	qread(Queue *q, void *buf, int len)
+.PP
+.B
+Block*	qcopy(Queue *q, int len, ulong offset)
+.PP
+.B
+Block*	qget(Queue *q)
+.PP
+.B
+int	qconsume(Queue *q, void *buf, int len)
+.PP
+.B
+int	qdiscard(Queue *q, int len)
+.PP
+.B
+void	qflush(Queue *q)
+.PP
+.B
+int	qlen(Queue *q)
+.PP
+.B
+int	qwindow(Queue *q)
+.PP
+.B
+int	qcanread(Queue *q)
+.PP
+.B
+void	qsetlimit(Queue *q, int limit)
+.PP
+.B
+void	qnoblock(Queue *q, int nonblock)
+.PP
+.B
+int	qfull(Queue *q);
+.SH DESCRIPTION
+This suite of functions provides serial data buffering for device drivers.
+Data is stored in a
+.B Queue
+structure as a sequence of variable-sized
+.BR Blocks ;
+see
+.IR allocb (10.2).
+.PP
+.I Qopen
+initialises and returns a pointer to a new
+.BR Queue ,
+configuring it according to the following parameters:
+.TF limit
+.PD
+.TP
+.I limit
+Set the queue limit (high water mark) in bytes.
+.TP
+.I msg
+Set message mode if non-zero; otherwise, stream mode (discussed below).
+.TP
+.I kick
+Optional flow-control function called by
+.I qbread
+to restart writers, and by
+.I qbwrite
+(also
+.IR qiwrite )
+to restart readers.
+.TP
+.I arg
+Argument to pass to
+.I kick
+.PP
+.I Qhangup
+marks
+.I q
+as `hung up'
+for the given
+.IR reason
+.RB ( Ehungup
+by default).
+Subsequent attempts to write to the queue raise an
+.IR error (10.2).
+.I Qhangup
+does not flush the queue: subsequent read requests are
+handled normally until the queue empties.
+.I Qread
+and the other functions then return their conventional values
+for a hungup stream: 0, -1 or a null pointer, depending on the function.
+After a few such attempts by any process, an
+.IR error (10.2)
+is raised (typically
+.BR Ehungup )
+on each subsequent read.
+.PP
+If queued data is left unread, and not flushed by
+.I qflush
+or
+.IR qclose ,
+the data will again be readable following a subsequent
+.IR qreopen .
+.PP
+.I Qclose
+also marks a given
+.I q
+as `hung up',
+but removes and frees any queued data Blocks.
+.I Qclose
+ignores calls when
+.I q
+is null.
+.PP
+.I Qreopen
+makes a closed or hung up queue available for use again.
+The queue's data limit is reset to the
+.I limit
+value given when the queue was first created by
+.IR qopen ,
+cancelling the effect of any previous call to
+.IR qsetlimit .
+.PP
+.I Qfree
+closes
+.I q
+with
+.I qclose
+and frees it.
+The caller must ensure that no references remain;
+these functions do not keep a reference count.
+.SS "Flow control"
+The queue I/O routines provide a flow control mechanism to coordinate producers and consumers.
+Each queue has a limit on the number of bytes queued, its `high water mark',
+initially set when the queue is created, but adjustable by
+.IR qsetlimit ,
+below. 
+The low water mark is not set explicitly:
+it is always half the current queue limit.
+When the high water mark is exceeded, writes normally block until a reader drains the
+queue below its low water mark; the writer is then allowed to proceed.
+Conversely, readers normally block when the queue is empty, until a writer
+arrives with data, or the queue is closed.
+.PP
+A queue can be given a
+.I kick
+function when the queue is created by
+.IR qopen .
+The function is invoked by
+.IR qread
+and
+.IR qbread ,
+to prod an output routine when the queue falls below the low-water mark, and by
+.IR qwrite ,
+.IR qbwrite
+and
+.IR qiwrite ,
+to notify a reader that a queue is no longer empty.
+Because
+.I kick
+is called from the reading (or writing) process, or an interrupt handler, it
+must not block.
+.PP
+Interrupt handlers must not
+.IR sleep (10.2),
+and are therefore restricted to using only the non-blocking functions described below.
+.SS "Stream mode and message mode"
+In stream mode,
+no read will return more than one
+block
+of data, but
+a read can split a block that contains more data than requested, leaving the remainder
+in a new block at the front of the Queue.
+Writes of more than the maximum
+.B Block
+size (currently 128k bytes)
+are split into as many Blocks as required, each written separately to the queue,
+in order, but with possible flow-control between them.
+The queue is locked meanwhile, however, so that data from other writers is not intermingled.
+.PP
+In message mode, by contrast, a read will return at most
+one block's worth of data, but the remainder of a partially-read block will be discarded,
+not returned to the queue.
+If a write count exceeds the maximum
+.B Block
+size, the excess data is discarded:
+at most a single block can be queued.
+.PP
+The mode of the queue should be taken into account in the descriptions below
+of the following functions:
+.IR qwrite ,
+.IR qiwrite ,
+.IR qbread
+and
+.IR qconsume .
+No other functions are aware of the distinction.
+.SS "Write operations (flow controlled)"
+.I Qwrite
+copies
+.I len
+bytes of data from
+.I buf
+into one or more
+.B Blocks
+which it places on the
+.IR q .
+.I Qwrite
+always returns
+.IR len .
+It can implement message mode.
+.PP
+.I Qbwrite
+places the single Block
+.I b
+on the tail of
+.IR q ,
+waking any sleeping reader.
+If the queue is full, the
+writing process blocks until a reader
+has reduced the queued data to
+the low-water mark;
+if the queue is non-blocking
+(see
+.I qnoblock
+below),
+the data is discarded without notice.
+.I Qbwrite
+normally returns
+.IR len ,
+but raises an
+.IR error (10.2)
+if the queue is closed (see
+.I qhangup
+and
+.IR qclose ).
+The block
+.I b
+is always freed.
+Note that
+.I b
+can be empty (zero-length), to punctuate the data in a queue.
+.I Qbwrite
+cannot handle a list of Blocks;
+.I qpass
+must be used instead.
+.SS Non-blocking writes
+.PP
+.I Qproduce
+returns -1immediately  if
+.I q
+is full.
+Otherwise, it queues
+.I len
+bytes of data from
+.I buf
+in a single
+.B Block
+on
+.I q
+and returns the number of bytes written.
+.PP
+.I Qpass
+attempts to place the list of Blocks headed by
+.I b
+on
+.IR q ,
+returning the number of bytes written if successful.
+If
+.I q
+was full, it
+frees the Block list
+.I b
+and returns -1.
+.PP
+.I Qpassnolim
+puts the Block list
+.I b
+on
+.I q
+regardless of flow control; it returns the number of bytes in the list
+.IR b .
+.PP
+.I Qiwrite
+is a variant of
+.I qwrite
+used exclusively by the kernel print function,
+to allow printing by interrupt handlers;
+.I qiwrite
+could be used with care by other routines, but
+.IR qproduce
+is preferable.
+.I Qiwrite
+writes the
+.I len
+bytes of data at
+.I buf
+into the
+.I q
+without regard to flow control;
+the writer never blocks.
+The queue is assumed to be open.
+.I Qiwrite
+always returns
+.IR len .
+It can implement message mode.
+.SS "Read operations (flow controlled)"
+.I Qbread
+blocks until data arrives on
+.IR q ,
+then
+returns the first
+.BR Block ;
+it limits the data returned
+to
+.I len
+bytes (in the manner depending on the mode of
+.IR q ).
+It returns a null pointer if the queue has hung up.
+.PP
+.I Qread
+reads a Block of up to
+.I len
+bytes from
+.I q
+using
+.IR qbread ,
+and copies the data in the Block into
+.IR buf ,
+then frees the Block and returns
+the number of bytes read.
+.I Qread
+returns 0 on end of file or error (hangup).
+It can implement message mode.
+.PP
+.I Qcopy
+returns a Block with a copy of data from the queue (the data remains on the queue).
+The copy begins
+.I offset
+bytes into the queue's data and proceeds until
+.I len
+bytes have been copied or no more data remains.
+The Block's read and write pointers delimit the data copied into it.
+.I Qcopy
+can be used by a reliable transport protocol to copy a packet for transmission,
+leaving the data queued for possible retransmission, if unacknowledged.
+.SS Non-blocking reads
+.PP
+.I Qconsume
+returns -1 immediately if
+.I q
+is empty.
+Otherwise, it
+copies up to
+.I len
+bytes from the first
+.B Block
+on the queue into
+.IR buf ,
+returning the number of bytes copied.
+It can implement message mode.
+.PP
+.I Qget
+returns a null pointer immediately if
+.I q
+is empty or closed.
+Otherwise, it
+returns the first
+.B Block
+on the queue.
+.SS "Discard and flush"
+.I Qdiscard
+removes the first
+.I len
+data bytes from
+.IR q ;
+it returns the number of bytes actually discarded, in case
+the queue is shorter than
+.IR len .
+If the queue drains below the low-water mark,
+.I qdiscard
+wakes any sleeping writers.
+Since it does not block,
+.I qdiscard
+can safely be called from interrupt handlers.
+It is useful in transport protocol drivers to remove data from the queue
+once acknowledged.
+.PP
+.I Qflush
+discards all data waiting on
+.IR q ,
+waking any waiting writer.
+.SS "Queue status"
+The following functions return a Queue's status.
+Note that between a call to one of these functions and another operation,
+the state can change if a driver allows concurrent access by
+either another process or an interrupt handler.
+.PP
+.I Qlen
+returns the number of bytes queued on
+.IR q .
+.PP
+.I Qwindow
+returns the number of bytes that can be written before reaching the queue's high-water mark.
+A return of 0 means that a write operation will certainly block;
+a non-zero return gives no guarantees (see
+.IR qfull ,
+below).
+.PP
+.I Qcanread
+returns 1 if any data queued is queued. A subsequent read operation will not block.
+.PP
+.I Qfull
+returns non-zero if
+.I q
+is flow-controlled and a write would block or a non-blocking write would return an error.
+(Note that the implementation allows
+.I qwindow
+to return non-zero yet
+.I qfull
+to return true.)
+.SS "Queue control"
+.I Qsetlimit
+sets the high water mark for the queue to
+.IR limit .
+Note that
+.I qopen
+saves the initial queue limit.
+If the queue is closed and reopened (by
+.IR qreopen )
+that initial limit is restored.
+.PP
+.I Qnoblock
+sets or resets non-blocking mode.
+If
+.I nonblock
+is non-zero,
+the queue becomes non-blocking, and
+data written to a queue beyond its high water mark is discarded
+by calls that would otherwise block.
+.SH SOURCE
+.B /os/port/qio.c
+.br
+.B /emu/port/qio.c
+.SH SEE ALSO
+.IR allocb (10.2),
+.IR ref (10.2)
--- /dev/null
+++ b/man/10/qlock
@@ -1,0 +1,106 @@
+.TH QLOCK 10.2
+.SH NAME
+qlock, qunlock, canqlock, rlock, runlock, wlock, wunlock \- serial synchronisation
+.SH SYNOPSIS
+.ta \w'\fLvoid 'u
+.B
+void	qlock(QLock *l)
+.PP
+.B
+void	qunlock(QLock *l)
+.PP
+.B
+int	canqlock(QLock *l)
+.PP
+.B
+void	rlock(RWlock *l)
+.PP
+.B
+void	runlock(RWlock *l)
+.PP
+.B
+int	canrlock(RWlock *l)
+.PP
+.B
+void	wlock(RWlock *l)
+.PP
+.B
+void	wunlock(RWlock *l)
+.SH DESCRIPTION
+The primitive locking functions described in
+.IR lock (10.2)
+guarantee mutual exclusion, but they implement spin locks,
+and should not be used if the process might
+.IR sleep (10.2)
+within a critical section.
+The following functions serialise access to a resource by forming an orderly
+queue of processes.
+.PP
+Each resource to be controlled is given an associated
+.B QLock
+structure; it is usually most straightforward to put the
+.B QLock
+in the structure that represents the resource.
+It must be initialised to zero before use
+(as guaranteed for global variables and for structures allocated by
+.IR malloc ).
+.PP
+On return from
+.IR qlock ,
+the process has acquired the lock
+.IR l ,
+and can assume exclusive access to the associated resource.
+If the lock is not immediately available, the requesting process is placed on a
+FIFO queue of processes that have requested the lock.
+Processes on this list are blocked in the
+.L Queueing
+state.
+.PP
+.I Qunlock
+unlocks
+.I l
+and schedules the first process queued for it (if any).
+.PP
+.I Canqlock
+is a non-blocking form of
+.IR qlock .
+It tries to obtain the lock
+.I l
+and returns true if successful, and 0 otherwise;
+it always returns immediately.
+.PP
+.B RWlock
+is a form of lock for resources that have distinct readers and writers.
+It allows concurrent readers but gives each writer exclusive access.
+A caller announces its read or write intentions by choice of lock (and unlock) function;
+the system assumes the caller will not modify a structure accessed under read lock.
+.PP
+.I Rlock
+acquires
+.I l
+for reading.
+The holder can read but agrees not to modify the resource.
+There may be several concurrent readers.
+.I Canrlock
+is non-blocking: it returns non-zero if it successfully acquired the lock immediately,
+and 0 if the resource was unavailable.
+.PP
+.I Runlock
+returns a read lock;
+the last reader out enables the first writer waiting (if any).
+.PP
+.I Wlock
+acquires a write lock.
+The holder of such a lock may assume exclusive access to the resource,
+and is allowed to modify it.
+.PP
+.I Wunlock
+returns a write lock.
+The next pending process, whether reader or writer, is scheduled.
+.SH SOURCE
+.B /os/port/qlock.c
+.br
+.B /os/emu/port/lock.c
+.SH SEE ALSO
+.IR lock (10.2),
+.IR splhi (10.2)
--- /dev/null
+++ b/man/10/readnum
@@ -1,0 +1,60 @@
+.TH READNUM 10.2
+.SH NAME
+readnum, readstr \- return values from read from device
+.SH SYNOPSIS
+.ta \w'\fLchar* 'u
+.B
+int readstr(ulong off, char *buf, ulong n, char *str)
+.PP
+.B
+int readnum(ulong off, char *buf, ulong n, ulong val, int size)
+.SH DESCRIPTION
+.I Readstr
+and
+.I readnum
+simplify the return of strings and numbers from device
+.I read
+routines,
+because they deal with any buffering and boundary cases.
+Several parameters to the read call are often handed on directly
+to these functions:
+the file offset, as
+.IR off ;
+the address of the user's buffer, as
+.IR buf ;
+and the number of bytes requested, as
+.IR n .
+Both functions return the number of bytes they have stored in
+.IR buf ,
+and which can often be returned directly from the device read routine.
+.PP
+.I Readstr
+satisfies a read by copying data into
+.I buf
+from the NUL-terminated string in
+.IR str .
+The data transferred is selected and limited by
+.IR off ,
+.I n
+and the length of
+.IR str .
+.PP
+.I Readnum
+converts the unsigned integer
+.I val
+to a decimal representation in
+.IR buf .
+The value is right-justified in a field of
+.IR size "-1"
+places and is followed by a blank.
+.I Size
+can be the global constant
+.L NUMSIZE
+for 32-bit integers;
+the largest
+.I size
+allowed is 64 bytes. 
+.SH SOURCE
+.B /os/port/devcons.c
+.br
+.B /emu/port/devcon.c
--- /dev/null
+++ b/man/10/ref
@@ -1,0 +1,61 @@
+.TH REF 10.2
+.SH NAME
+Ref, incref, decref \- reference counts
+.SH SYNOPSIS
+.ta \w'\fLchar* 'u
+.PP
+.B
+int	incref(Ref *r)
+.PP
+.B
+int	decref(Ref *r)
+.SH DESCRIPTION
+A
+.B Ref
+structure holds a reference count for a data structure:
+.IP
+.EX
+typedef struct
+struct Ref
+{
+    Lock;
+    long ref;
+} Ref;
+.EE
+.PP
+The reference count proper is found in
+.BR ref ;
+the
+.B Lock
+prevents concurrent updates
+(see
+.IR lock (10.2)).
+.PP
+.I Incref
+atomically increments the reference count
+.IR r ,
+and returns the new count.
+.PP
+.I Decref
+atomically decrements the reference count
+.IR r ,
+and returns the new count.
+.SH EXAMPLES
+Release a structure containing a
+.B Ref
+on last use.
+.IP
+.EX
+if(decref(s) == 0)
+	free(s);
+.EE
+.SH DIAGNOSTICS
+.I Decref
+will
+.IR panic (10.2)
+if the count goes negative,
+revealing a reference counting bug.
+.SH SOURCE
+.B /os/port/chan.c
+.br
+.B /emu/port/chan.c
--- /dev/null
+++ b/man/10/rune
@@ -1,0 +1,183 @@
+.TH RUNE 10.2
+.SH NAME
+runetochar, chartorune, runelen, runenlen, fullrune, utfecpy, utflen, utfnlen, utfrune, utfrrune, utfutf \- rune/UTF conversion
+.SH SYNOPSIS
+.ta \w'\fLchar*xx'u
+.PP
+.B
+int	runetochar(char *s, Rune *r)
+.PP
+.B
+int	chartorune(Rune *r, char *s)
+.PP
+.B
+int	runelen(long r)
+.PP
+.B
+int	runenlen(Rune *r, int n)
+.PP
+.B
+int	fullrune(char *s, int n)
+.PP
+.B
+char*	utfecpy(char *s1, char *es1, char *s2)
+.PP
+.B
+int	utflen(char *s)
+.PP
+.B
+int	utfnlen(char *s, long n)
+.PP
+char*	utfrune(char *s, long c)
+.PP
+.B
+char*	utfrrune(char *s, long c)
+.PP
+.B
+char*	utfutf(char *s1, char *s2)
+.SH DESCRIPTION
+These routines convert to and from a
+.SM UTF
+byte stream and runes.
+.PP
+.I Runetochar
+copies one rune at
+.I r
+to at most
+.B UTFmax
+bytes starting at
+.I s
+and returns the number of bytes copied.
+.BR UTFmax ,
+defined as
+.B 4
+in
+.BR <libc.h> ,
+is the maximum number of bytes required to represent a rune.
+.PP
+.I Chartorune
+copies at most
+.B UTFmax
+bytes starting at
+.I s
+to one rune at
+.I r
+and returns the number of bytes copied.
+If the input is not exactly in
+.SM UTF
+format,
+.I chartorune
+will convert to
+.B Runeerror
+(0xFFFD) and return 1.
+.PP
+.I Runelen
+returns the number of bytes
+required to convert
+.I r
+into
+.SM UTF.
+.PP
+.I Runenlen
+returns the number of bytes
+required to convert the
+.I n
+runes pointed to by
+.I r
+into
+.SM UTF.
+.PP
+.I Fullrune
+returns 1 if the string
+.I s
+of length
+.I n
+is long enough to be decoded by
+.I chartorune
+and 0 otherwise.
+This does not guarantee that the string
+contains a legal
+.SM UTF
+encoding.
+This routine is used by programs that
+obtain input a byte at
+a time and need to know when a full rune
+has arrived.
+.PP
+The following routines are analogous to the
+corresponding string routines with
+.B utf
+substituted for
+.B str
+and
+.B rune
+substituted for
+.BR chr .
+.PP
+.I Utflen
+returns the number of runes that
+are represented by the
+.SM UTF
+string
+.IR s .
+.PP
+.I Utfnlen
+returns the number of complete runes that
+are represented by the first
+.I n
+bytes of
+.SM UTF
+string
+.IR s .
+If the last few bytes of the string contain an incompletely coded rune,
+.I utfnlen
+will not count them; in this way, it differs from
+.IR utflen ,
+which includes every byte of the string.
+.PP
+.I Utfrune
+.RI ( utfrrune )
+returns a pointer to the first (last)
+occurrence of rune
+.I c
+in the
+.SM UTF
+string
+.IR s ,
+or 0 if
+.I c
+does not occur in the string.
+The NUL byte terminating a string is considered to
+be part of the string
+.IR s .
+.PP
+.I Utfutf
+returns a pointer to the first occurrence of
+the
+.SM UTF
+string
+.I s2
+as a
+.SM UTF
+substring of
+.IR s1 ,
+or 0 if there is none.
+If
+.I s2
+is the null string,
+.I utfutf
+returns
+.IR s1 .
+.SH SOURCE
+.B /libkern/rune.c
+.br
+.B /libkern/runestrlen.c
+.br
+.B /libkern/utflen.c
+.br
+.B /libkern/utfrrune.c
+.br
+.B /libkern/utfrune.c
+.SH SEE ALSO
+.IR convcs (2),
+.IR utf (6)
--- /dev/null
+++ b/man/10/seconds
@@ -1,0 +1,64 @@
+.TH SECONDS 10.2
+.SH NAME
+seconds, ticks, HZ, MS2HZ, MS2TK, TK2MS, TK2SEC \- kernel times and time conversions
+.SH SYNOPSIS
+.ta \w'\fL#define 'u
+.B
+long	seconds(void)
+.PP
+.B
+ulong   	ticks(void)
+.PP
+.EX
+#define	HZ          ...
+#define	MS2HZ(t)    ...
+#define	TK2SEC(t)   ...
+#define	TK2MS(t)    ...
+#define	MS2TK(m)    ...
+.EE
+.SH DESCRIPTION
+.I Seconds
+returns the system's idea of the current time as the number of seconds
+since the start of the epoch
+(00:00:00 GMT, January 1, 1970).
+.PP
+.I Ticks
+returns the number of system-dependent clock ticks since system boot.
+.PP
+The system clock frequency is platform-dependent.
+Several symbolic constants and macro functions are defined by
+the file
+.B mem.h
+to convert between different time units:
+.TF TK2SEC(t)
+.PD
+.TP
+.B HZ
+The number of clock ticks per second.
+.TP
+.B MS2HZ
+Milliseconds per clock tick.
+.TP
+.BI TK2SEC( t )
+Convert
+.I t
+clock ticks to seconds and return the result (truncating not rounding).
+.TP
+.BI TK2MS( t )
+Convert
+.I t
+clock ticks to milliseconds and return the result.
+.TP
+.BI MS2TK( m )
+Convert
+.I m
+milliseconds to clock ticks and return the result (truncating).
+.PP
+The functions are often used to calculate delays for timing functions,
+for instance:
+.IP
+.EX
+if(atactlrwait(dp->cp, DHmagic, 0, MS2TK(100))){
+	...
+}
+.EE
--- /dev/null
+++ b/man/10/sleep
@@ -1,0 +1,125 @@
+.TH SLEEP 10.2
+.SH NAME
+sleep, wakeup, tsleep, return0 \- process synchronisation
+.SH SYNOPSIS
+.ta \w'\fLvoid 'u
+.B
+void	sleep(Rendez *r, int (*f)(void*), void *arg)
+.PP
+.B
+void	wakeup(Rendez *r)
+.PP
+.B
+void	tsleep(Rendez *r, int (*f)(void*), void *arg, int ms)
+.PP
+.B
+int	return0(void *arg)
+.PP
+.SH DESCRIPTION
+A process running in the kernel can use these functions to
+synchronise with an interrupt handler or another kernel process.
+In particular, they are used by device drivers to wait for an event to be signalled on
+receipt of an interrupt.
+(In practice, they are most often used indirectly, through
+.IR qio (10.2)
+for instance.)
+.PP
+The caller of
+.I sleep
+and a caller of
+.I wakeup
+share a
+.B Rendez
+structure, to provide a rendezvous point between them
+to synchronise on an event.
+.I Sleep
+uses a condition function
+.I f
+that returns true if the event has occurred.
+.PP
+.I Sleep
+evaluates
+.IB f ( arg ).
+If true, the event has happened and
+.I sleep
+returns immediately.
+Otherwise,
+.I sleep
+blocks on the event variable
+.IR r ,
+awaiting
+.IR wakeup .
+.PP
+.I Wakeup
+is called by either a process or an interrupt handler to wake any process
+sleeping at
+.IR r ,
+signifying that the corresponding condition is true (the event has occurred).
+It has no effect if there is no sleeping process.
+.PP
+.I Tsleep
+is similar to
+.IR sleep ,
+except that if the condition
+.IB f ( arg )
+is false and the caller does sleep,
+and nothing else wakes it within
+.I ms
+millliseconds,
+the system will wake it.
+.IR Tsleep 's
+caller must check its environment to decide whether timeout or the event
+occurred.
+The timing provided by
+.I tsleep
+is imprecise, but adequate in practice for the normal use of protecting against
+lost interrupts and otherwise unresponsive devices or software.
+.PP
+.I Return0
+ignores its arguments and returns zero. It is commonly used as
+the predicate
+.I f
+in a call to
+.I tsleep
+to obtain a time delay, using a
+.B Rendez
+variable
+.B sleep
+in the
+.B Proc
+structure, for example:
+.IP
+.B tsleep(&up->sleep, return0, nil, 10);
+.PP
+Both
+.I sleep
+and
+.I tsleep
+can be interrupted by
+.IR swiproc
+(see
+.IR kproc (10.2)),
+causing a non-local goto through a call to
+.IR error (10.2).
+.SH DIAGNOSTICS
+There can be at most one process waiting on a
+.BR Rendez ,
+and if two processes collide, the system will
+.IR panic (10.2)
+.RB (`` "double sleep" '').
+Access to a
+.B Rendez
+must therefore be serialised by some other mechanism, usually
+.IR qlock (10.2).
+.SH SOURCE
+.B /os/port/proc.c
+.br
+.B /emu/port/proc.c
+.SH SEE ALSO
+.IR lock (10.2),
+.IR qlock (10.2),
+.IR delay (10.2)
+.br
+``Process Sleep and Wakeup on a Shared-memory Multiprocessor'',
+in
+.I "Plan 9 Programmer's Manual: Volume 2".
--- /dev/null
+++ b/man/10/splhi
@@ -1,0 +1,55 @@
+.TH SPLHI 10.2
+.SH NAME
+splhi, spllo, splx, islo \- enable and disable interrupts
+.SH SYNOPSIS
+.ta \w'\fLvoid 'u
+.B
+int	spllo(void)
+.PP
+.B
+int	splhi(void)
+.PP
+.B
+void	splx(int x)
+.PP
+.B
+int	islo(void)
+.SH DESCRIPTION
+These primitives enable and disable maskable interrupts on the current
+processor.
+Generally, device drivers should use
+.I ilock
+(see
+.IR lock (10.2)),
+.IR sleep (10.2),
+or the functions in
+.IR qio (10.2)
+to control interaction between processes and interrupt handlers.
+Those routines (but not these) provide correct synchronisation on multiprocessors.
+.PP
+.I Spllo
+enables interrupts and returns a flag representing the previous interrupt enable state.
+It must not normally be called from interrupt level.
+.PP
+.I Splhi
+disables all maskable interrupts and returns the previous interrupt enable state.
+The period during which interrupts are disabled had best be short,
+or real-time applications will suffer.
+.PP
+.I Splx
+restores the interrupt enable state
+state to
+.IR x ,
+which must be a value returned
+by a previous call to
+.I splhi
+or
+.IR spllo .
+.PP
+.I Islo
+returns true (non-zero) if interrupts are currently enabled, and 0 otherwise.
+.SH SEE ALSO
+.IR lock (10.2),
+.IR qio (10.2),
+.IR sleep (10.2),
+.IR intrenable (10.2)
--- /dev/null
+++ b/man/10/srclist
@@ -1,0 +1,41 @@
+.TH SRCLIST 10.1
+.SH NAME
+srclist \- list source files used to build an executable
+.SH SYNOPSIS
+.B srclist
+[
+.B -ce
+] [
+.BI -r " rootdir"
+\&...
+]
+.I executable
+.SH DESCRIPTION
+.I Srclist
+prints on standard output, one per line, the names of source files used to build
+the given
+.IR executable ,
+which should be
+in
+.IR a.out (10.6)
+format.
+.PP
+By default, all source file names are listed.
+The
+.B -c
+option restricts the output to names ending
+.RB ` .c '.
+The
+.B -e
+option restricts the output to file names that currently exist.
+Each
+.B -r
+option adds a new root directory
+.I rootdir
+to an internal list.
+Only the names of source files that appear below one of the root directories
+will be printed.
+.SH SOURCE
+.B /utils/srclist
+.SH SEE ALSO
+.IR inm (10.1)
--- /dev/null
+++ b/man/10/strcat
@@ -1,0 +1,170 @@
+.TH STRCAT 10.2
+.SH NAME
+strcat, strcmp, strncmp, strcpy, strncpy, strlen, strchr, strrchr, strdup, strstr \- string operations
+.SH SYNOPSIS
+.ta \w'\fLchar* \fP'u
+.B
+char*	strcat(char *s1, char *s2)
+.PP
+.B
+int	strcmp(char *s1, char *s2)
+.PP
+.B
+int	strncmp(char *s1, char *s2, long n)
+.PP
+.B
+char*	strcpy(char *s1, char *s2)
+.PP
+.B
+char*	strncpy(char *s1, char *s2, long n)
+.PP
+.B
+long	strlen(char *s)
+.PP
+.B
+char*	strchr(char *s, char c)
+.PP
+.B
+char*	strrchr(char *s, char c)
+.PP
+.B
+char*	strdup(char *s)
+.PP
+.B
+char*	strstr(char *s1, char *s2)
+.SH DESCRIPTION
+The arguments
+.I s1, s2
+and
+.I s
+point to null-terminated strings.
+The functions
+.IR strcat ,
+.IR strcpy ,
+and
+.I strncpy
+all alter
+.IR s1 .
+These functions do not check for overflow of
+the array pointed to by
+.IR s1 .
+.PP
+.I Strcat
+appends a copy of string
+.I s2
+to the end of string
+.IR s1 ,
+and
+returns a pointer to the null-terminated result.
+.PP
+.I Strcmp
+compares its arguments and returns an integer
+less than, equal to, or greater than 0,
+according as
+.I s1
+is lexicographically less than, equal to, or
+greater than
+.IR s2 .
+.I Strncmp
+makes the same comparison but examines at most
+.I n
+bytes.
+The comparisons are made with unsigned bytes.
+.PP
+.I Strcpy
+copies string
+.I s2
+to
+.IR s1 ,
+stopping after the null byte has been copied.
+.I Strncpy
+copies exactly
+.I n
+bytes,
+truncating
+.I s2
+or adding
+null bytes to
+.I s1
+if necessary.
+The result will not be null-terminated if the length
+of
+.I s2
+is
+.I n
+or more.
+Each function returns
+.IR s1 .
+.PP
+.I Strlen
+returns the number of bytes in
+.IR s ,
+not including the terminating null byte.
+.PP
+.I Strchr
+.RI ( strrchr )
+returns a pointer to the first (last)
+occurrence of byte
+.I c
+in string
+.IR s ,
+or
+.L 0
+if
+.I c
+does not occur in the string.
+The null byte terminating a string is considered to
+be part of the string.
+.PP
+.I Strdup
+returns a pointer to a distinct copy of the null-terminated string
+.I s
+in space obtained from
+.IR malloc (10.2)
+or
+.L 0
+if no space can be obtained.
+.PP
+.I Strstr
+returns a pointer to the first occurrence of
+.I s2
+as a substring of
+.IR s1 ,
+or 0 if there is none.
+If
+.I s2
+is the null string,
+.I strstr
+returns
+.IR s1 .
+.SH SOURCE
+.B /libkern/str*.c
+.br
+.B /libkern/str*-\fIobjtype\fP.c
+.br
+.B /lib9/strdup.c
+.SH SEE ALSO
+.IR memory (10.2),
+.IR rune (10.2)
+.SH BUGS
+These routines know nothing about
+.SM UTF.
+Use the routines in
+.IR rune (10.2)
+as appropriate.
+Note, however, that the definition of UTF guarantees that
+.I strcmp
+compares UTF strings correctly.
+.PP
+The outcome of overlapping moves varies among implementations.
+.PP
+Note the absence of ANSI C's
+.IR strncat ,
+.IR strpbrk ,
+.IR strspn ,
+.IR strcspn
+and
+.IR strtok ,
+but the presence of
+.IR strdup .
+...strtod.c strtol.c strtoul.c strtoll.c
--- /dev/null
+++ b/man/10/styx
@@ -1,0 +1,365 @@
+.TH STYX 10.2
+.SH NAME
+Fcall, convS2M, convD2M, convM2S, convM2D, fcallfmt, dirfmt, dirmodefmt, statcheck, sizeS2M, sizeD2M \- interface to Inferno File protocol
+.SH SYNOPSIS
+.B #include <lib9.h>
+.br
+.br
+.B #include <styx.h>
+.PP
+.B
+uint convS2M(Fcall *f, uchar *ap, uint nap)
+.PP
+.B
+uint convD2M(Dir *d, uchar *ap, uint nap)
+.PP
+.B
+uint convM2S(uchar *ap, uint nap, Fcall *f)
+.PP
+.B
+uint convM2D(uchar *ap, uint nap, Dir *d, char *strs)
+.PP
+.B
+int dirfmt(Fmt*)
+.PP
+.B
+int fcallfmt(Fmt*)
+.PP
+.B
+int dirmodefmt(Fmt*)
+.PP
+.B
+int statcheck(uchar *buf, uint nbuf)
+.PP
+.B
+uint sizeS2M(Fcall *f)
+.PP
+.B
+uint sizeD2M(Dir *d)
+.SH DESCRIPTION
+These
+routines convert messages in the machine-independent format of
+the Inferno file protocol,
+described by
+.IR intro (5),
+to and from a more convenient form,
+an
+.B Fcall
+structure:
+.PP
+.EX
+.if n .ta 4n +6n +5n +6n +18n +4n
+.if t .ta \w'xxxx'u +\w'short 'u +\w'xxxx'u +\w'ushort 'u +\w'ticket[TICKETLEN]; 'u +\w'/* 'u
+#define MAXWELEM 16
+
+typedef
+struct Fcall
+{
+	uchar	type;
+	u32int	fid;
+	ushort	tag;
+	union {
+		struct {
+			u32int	msize;	/* Tversion, Rversion */
+			char	*version;	/* Tversion, Rversion */
+		};
+		struct {
+			ushort	oldtag;	/* Tflush */
+		};
+		struct {
+			char	*ename;	/* Rerror */
+		};
+		struct {
+			Qid	qid;	/* Rattach, Ropen, Rcreate */
+			u32int	iounit;	/* Ropen, Rcreate */
+		};
+		struct {
+			Qid	aqid;	/* Rauth */
+		};
+		struct {
+			u32int	afid;	/* Tauth, Tattach */
+			char	*uname;	/* Tauth, Tattach */
+			char	*aname;	/* Tauth, Tattach */
+		};
+		struct {
+			u32int	perm;	/* Tcreate */ 
+			char	*name;	/* Tcreate */
+			uchar	mode;	/* Tcreate, Topen */
+		};
+		struct {
+			u32int	newfid;	/* Twalk */
+			ushort	nwname;	/* Twalk */
+			char	*wname[MAXWELEM];	/* Twalk */
+		};
+		struct {
+			ushort	nwqid;	/* Rwalk */
+			Qid	wqid[MAXWELEM];	/* Rwalk */
+		};
+		struct {
+			vlong	offset;	/* Tread, Twrite */
+			u32int	count;	/* Tread, Twrite, Rread */
+			char	*data;	/* Twrite, Rread */
+		};
+		struct {
+			ushort	nstat;	/* Twstat, Rstat */
+			uchar	*stat;	/* Twstat, Rstat */
+		};
+	};
+} Fcall;
+.EE
+.EX
+
+/* these are implemented as macros */
+
+uchar	GBIT8(uchar*)
+ushort	GBIT16(uchar*)
+ulong	GBIT32(uchar*)
+vlong	GBIT64(uchar*)
+
+void	PBIT8(uchar*, uchar)
+void	PBIT16(uchar*, ushort)
+void	PBIT32(uchar*, ulong)
+void	PBIT64(uchar*, vlong)
+
+#define	BIT8SZ	1
+#define	BIT16SZ	2
+#define	BIT32SZ	4
+#define	BIT64SZ	8
+.EE
+.PP
+This structure is defined in
+.BR <styx.h> .
+See section 5
+for a full description of 9P messages and their encoding.
+For all message types, the
+.B type
+field of an
+.B Fcall
+holds one of
+.BR Tversion ,
+.BR Rversion ,
+.BR Tattach ,
+.BR Rattach ,
+etc. (defined in an enumerated type in
+.BR <styx.h> ).
+.B Fid
+is used by most messages, and
+.B tag
+is used by all messages.
+The other fields are used selectively by the message types
+given in comments.
+.PP
+.I ConvM2S
+takes a 9P message at
+.I ap
+of length
+.IR nap ,
+and uses it to fill in
+.B Fcall
+structure
+.IR f .
+If the passed message
+including any data for
+.B Twrite
+and
+.B Rread
+messages
+is formatted properly,
+the return value is the number of bytes the message occupied in the buffer
+.IR ap ,
+which will always be less than or equal to
+.IR nap ;
+otherwise it is 0.
+For
+.B Twrite
+and
+.B Tread
+messages,
+.B data
+is set to a pointer into the argument message,
+not a copy.
+.PP
+.I ConvS2M
+does the reverse conversion, turning
+.I f
+into a message starting at
+.IR ap .
+The length of the resulting message is returned.
+For
+.B Twrite
+and
+.B Rread
+messages,
+.B count
+bytes starting at
+.B data
+are copied into the message.
+.PP
+The constant
+.B IOHDRSZ
+is a suitable amount of buffer to reserve for storing
+the 9P header;
+the data portion of a
+.B Twrite
+or
+.B Rread
+will be no more than the buffer size negotiated in the
+.BR Tversion/Rversion
+exchange, minus
+.BR IOHDRSZ .
+.PP
+The routine
+.I sizeS2M
+returns the number of bytes required to store the machine-independent representation of the
+.B Fcall
+structure
+.IR f ,
+including its initial 32-bit size field.
+In other words, it reports the number of bytes produced
+by a successful call to
+.IR convS2M .
+.PP
+Another structure is
+.BR Dir ,
+used by C functions in much the same way as the Limbo versions
+described in
+.IR sys-stat (2).
+.I ConvM2D
+converts the machine-independent form starting at
+.I ap
+into
+.IR d
+and returns the length of the machine-independent encoding.
+The strings in the returned
+.B Dir
+structure are stored at successive locations starting at
+.BR strs .
+Usually
+.B strs
+will point to storage immediately after the
+.B Dir
+itself.
+It can also be a
+.B nil
+pointer, in which case the string pointers in the returned
+.B Dir
+are all
+.BR nil ;
+however, the return value still includes their length.
+.PP
+.I ConvD2M
+does the reverse translation,
+also returning the length of the encoding.
+If the buffer is too short, the return value will be
+.B BIT16SZ
+and the correct size will be returned in the first
+.B BIT16SZ
+bytes.
+(If the buffer is less than
+.BR BIT16SZ ,
+the return value is zero; therefore a correct test for
+complete packing of the message is that the return value is
+greater than
+.BR BIT16SZ ).
+The macro
+.B GBIT16
+can be used to extract the correct value.
+The related macros with different sizes retrieve the corresponding-sized quantities.
+.B PBIT16
+and its brethren place values in messages.
+With the exception of handling short buffers in
+.IR convD2M ,
+these macros are not usually needed except by internal routines.
+.PP
+Analogous to
+.IR sizeS2M ,
+.I sizeD2M
+returns the number of bytes required to store the machine-independent representation of the
+.B Dir
+structure
+.IR d ,
+including its initial 16-bit size field.
+.PP
+The routine
+.B statcheck
+checks whether the
+.I nbuf
+bytes of
+.I buf
+contain a validly formatted machine-independent
+.B Dir
+entry.
+It checks that the sizes of all the elements of the the entry sum to exactly
+.IR nbuf ,
+which is a simple but effective test of validity.
+.I Nbuf
+and
+.I buf
+should include the second two-byte (16-bit) length field that precedes the entry when
+formatted in a 9P message (see
+.IR stat (5));
+in other words,
+.I nbuf
+is 2 plus the sum of the sizes of the entry itself.
+.I Statcheck
+also verifies that the length field has the correct value (that is,
+.IB nbuf -2\f1).
+It returns
+.B 0
+for a valid entry and
+.B -1
+for an incorrectly formatted entry.
+.PP
+.IR Dirfmt ,
+.IR fcallfmt ,
+and
+.I dirmodefmt
+are formatting routines, suitable for
+.IR fmtinstall (10.2).
+They convert
+.BR Dir* ,
+.BR Fcall* ,
+and
+.BR long
+values into string representations of the directory buffer,
+.B Fcall
+buffer,
+or file mode value.
+.I Fcallfmt
+assumes that
+.I dirfmt
+has been installed with format letter
+.L D
+and
+.I dirmodefmt
+with format letter
+.LR M .
+They currently cannot be used in the kernels because they clash
+with the use of format
+.L D
+for Dis instructions.
+.SH SOURCE
+.B /lib9/convM2D.c
+.br
+.B /lib9/convM2D.c
+.br
+.B /lib9/convM2S.c
+.br
+.B /lib9/convS2M.c
+.br
+.B /lib9/fcallfmt.c
+.br
+.B /libkern/convM2D.c
+.br
+.B /libkern/convM2D.c
+.br
+.B /libkern/convM2S.c
+.br
+.B /libkern/convS2M.c
+.br
+.B /libkern/fcallfmt.c
+.SH SEE ALSO
+.IR intro (2),
+.IR styx (2),
+.IR sys-stat (2),
+.IR intro (5)
--- /dev/null
+++ b/man/10/styxserver
@@ -1,0 +1,623 @@
+.TH STYXSERVER 10.2
+.SH NAME
+Styxserver \- C Styx server library
+.SH SYNOPSIS
+.EX
+#include <lib9.h>
+#include <styx.h>
+#include <styxserver.h>
+#define Qroot	0
+
+#define MSGMAX	((((8192+128)*2)+3) & ~3)
+
+extern char Enomem[];	/* out of memory */
+extern char Eperm[];		/* permission denied */
+extern char Enodev[];	/* no free devices */
+extern char Ehungup[];	/* i/o on hungup channel */
+extern char Eexist[];		/* file exists */
+extern char Enonexist[];	/* file does not exist */
+extern char Ebadcmd[];	/* bad command */
+extern char Ebadarg[];	/* bad arguments */
+
+typedef uvlong	Path;
+typedef struct Styxserver	Styxserver;
+typedef struct Styxops Styxops;
+typedef struct Styxfile Styxfile;
+typedef struct Client Client;
+
+struct Styxserver
+{
+	Styxops *ops;
+	Path qidgen;
+	int connfd;
+	Client *clients;
+	Client *curc;
+	Styxfile *root;
+	Styxfile **ftab;
+	void	*priv;	/* private */
+};
+
+struct Client
+{
+	Styxserver *server;
+	Client *next;
+	int		fd;
+	char	msg[MSGMAX];
+	uint		nread;		/* valid bytes in msg (including nc)*/
+	int		nc;		/* bytes consumed from front of msg by convM2S */
+	char	data[MSGMAX];	/* Tread/Rread data */
+	int		state;
+	Fid		*fids;
+	char		*uname;	/* uid */
+	char		*aname;	/* attach name */
+	void		*u;
+};
+
+struct Styxops
+{
+	char *(*newclient)(Client *c);
+	char *(*freeclient)(Client *c);
+
+	char *(*attach)(char *uname, char *aname);
+	char *(*walk)(Qid *qid, char *name);
+	char *(*open)(Qid *qid, int mode);
+	char *(*create)(Qid *qid, char *name, int perm, int mode);
+	char *(*read)(Qid qid, char *buf, ulong *n, vlong offset);
+	char *(*write)(Qid qid, char *buf, ulong *n, vlong offset);
+	char *(*close)(Qid qid, int mode);
+	char *(*remove)(Qid qid);
+	char *(*stat)(Qid qid, Dir *d);
+	char *(*wstat)(Qid qid, Dir *d);
+};
+
+struct Styxfile
+{
+	Dir	d;
+	Styxfile *parent;
+	Styxfile *child;
+	Styxfile *sibling;
+	Styxfile *next;
+	int ref;
+	int open;
+	void	*u;
+};
+
+void styxsetowner(char *user);
+char *styxinit(Styxserver *server, Styxops *ops, char *port, int perm, int needfile);
+char *styxwait(Styxserver *server);
+char *styxprocess(Styxserver *server);
+char *styxend(Styxserver *server);
+
+Client *styxclient(Styxserver *server);
+
+Styxfile *styxaddfile(Styxserver *server, Path pqid, Path qid, char *name,
+					int mode, char *owner);
+Styxfile *styxadddir(Styxserver *server, Path pqid, Path qid, char *name,
+					int mode, char *owner);
+int styxrmfile(Styxserver *server, Path qid);
+Styxfile *styxfindfile(Styxserver *server, Path qid);
+
+int	styxperm(Styxfile *file, char *uid, int mode);
+long styxreadstr(ulong off, char *buf, ulong n, char *str);
+Qid styxqid(int path, int isdir);
+void *styxmalloc(int bytes);
+void styxfree(void *p);
+void styxdebug(void);
+.EE
+.SH DESCRIPTION
+The C Styx server library provides a small suite of functions to enable the
+production of a file server based on the Inferno Styx protocol. The following
+elements define the primary routines in the interface:
+.TP
+.BI styxinit(server\fP,\fP\ ops\fP,\fP\ port\fP,\fP\ perm\fP,\fP\ needfile )
+Initializes the interface given a pointer to a Styxserver structure
+.I server
+, a callback table of operations
+.I ops
+, a port number
+.I port
+to announce the file service on
+and the permissions
+.I perm
+on the root directory. The default permission is 0555 (read and execute for user, 
+group and others) if the latter is specified as -1. If the last argument
+.I needfile
+is set to true, the styx library will check that each path number it deals with
+has a corresponding file associated with it and, if it hasn't, it will issue a
+"file does not exist" message automatically. In case of an error, the error message is
+returned, otherwise nil is returned to indicate success.
+By default, files are owned by
+.BR inferno ;
+.I styxsetowner
+can be called before
+.I styxinit
+to make
+.I user
+the default owner of files.
+.TP
+.BI styxwait(server )
+Waits for communication from a client. Return value as above.
+.TP
+.BI styxprocess(server )
+Processes the client message after a successful call to
+.I styxwait .
+This may result in calls to the functions in the table provided to
+.I styxinit .
+Return value as above.
+.TP
+.BI styxend(server )
+End all file service. Return value as above.
+.TP 
+.BI styxclient(server )
+Returns the client whose request is currently being processed.
+.PP
+The next set of functions allow the creation of a file system structure based
+upon the
+.I Styxfile
+structure. This contains a Dir structure 
+.I d
+describing the properties of the file
+(defined in lib9.h) and pointers to other files in the file tree:
+.I parent
+,
+.I child
+,
+.I sibling
+and
+.I next .
+The
+.I ref
+field
+counts current references to the file. The
+.I open
+field counts the current number of opens on the file. Finally the
+.I u
+field allows further fields to be tagged onto each file. It is not
+used by the Styx server library.
+.PP
+Each file must have a unique path number in the server. The root of
+the tree
+.I Qroot
+always has path number zero. It's corresponding file is created during library initialization
+and placed in the
+.I root
+field of the server structure. All other files must be supplied with a path number
+to identify them. Files are created/deleted as follows:
+.TP
+.BI styxaddfile(server\fP,\fP\ ppath\fP,\fP\ path\fP,\fP\ name\fP,\fP\ mode\fP,\fP\ owner )
+Add a new file (ie non-directory) with the given path
+.I path
+, name
+.I name
+, mode
+.I mode
+and owner
+.I owner
+to the directory identified by the path
+.I ppath .
+If
+.I path
+is -1 the library will generate a unique path number instead.
+Returns nil if the parent file with path
+.I ppath
+does not exist, if the parent is not a directory, if the path number
+.I path
+already is assigned to a file or if the parent already contains a file of name
+.I name .
+.TP
+.BI styxadddir(server\fP,\fP\ ppath\fP,\fP\ path\fP,\fP\ name\fP,\fP\ mode\fP,\fP\ owner )
+Add a new directory with the given path
+.I path
+, name
+.I name
+, mode
+.I mode
+and owner
+.I owner
+to the directory identified by the path
+.I ppath .
+Returns nil in the same circumstances as
+.I styxaddfile .
+.TP
+.BI styxrmfile(server\fP,\fP\  path )
+Remove the file or directory with path
+.I path
+from the file server tree. If the file is a directory, it's contents will be recursively
+removed. If the file does not exist, -1 is returned, otherwise 0 is returned for
+success.
+.TP
+.BI styxfindfile(server\fP,\fP\  path )
+Return the file structure corresponding to the file or directory with path
+.I path .
+Nil is returned if the file does not exist.
+.PP
+If the file system is created in this way the Styx library will check read/write/execute
+permissions, check for invalid uses of files and check that path numbers exist
+in the file system (see
+.I styxinit
+for the latter). If it's not feasible to do this (for instance if there is a more suitable
+way of describing the file system in question), then all file checking must be
+done as part of the callback functions below.
+.PP
+The library provides a callback mechanism so that the implementer of the
+file server can take corresponding action when a particular request is made
+of the server. All of these functions may return an error message which will
+be communicated back to the client. Otherwise they should return nil to
+indicate the success of the operation. Any of these functions may be nil in which case the library
+performs a default operation which will be described below. These routines use
+the
+.I Qid
+structure defined in lib9.h to describe files. This structure contains the path number(
+.I path
+), a version number(
+.I vers
+) typically zero and a type(
+.I type
+) which indicates whether the file is a directory, append-only etc.
+.TP
+.BI newclient(c )
+Called whenever a new client connects to the server. The Client structure
+.I c
+contains mainly private data but the
+.I uname
+field contains a user name and the
+.I aname
+field an attach name if required. The
+.I u
+field may be used to tag further data onto each client. It is not used by
+the Styx server library.
+.TP
+.BI freeclient(c )
+Called whenever a client disconnects from the server.
+.TP
+.BI attach(uname\fP,\fP\ aname )
+Called when a client user first mounts the file server. The
+.I uname
+is the user id and
+.I aname
+is typically the file tree to access if the server provides a choice.
+The default action is to allow the attach to the root of the file system.
+.TP
+.BI walk(qid\fP,\fP\ name )
+In a directory represented by
+.I qid
+, find a file member whose name is that given and place it's Qid in
+.I qid .
+The default action is to perform the walk using any directory structure provided.
+.TP
+.BI open(qid\fP,\fP\ mode )
+Open the file represented by
+.I qid
+with mode
+.I mode .
+The latter may be one of OREAD, OWRITE, ORDWR etc (see lib9.h). If the Qid
+of the newly opened file is different from that given (a file server may understand
+the opening of a file called "new" say to signify the creation of a directory whose
+Qid is returned instead) place it's Qid in
+.I qid .
+The default action is to nominally allow the open.
+.TP
+.BI create(qid\fP,\fP\ name\fP,\fP\ perm\fP,\fP\ mode )
+Create a file in the directory given by
+.I qid
+with name
+.I name
+, permissions
+.I perm
+and mode
+.I mode .
+Place the Qid of the newly created file in
+.I qid .
+The default action is to issue a permission denied message.
+.TP
+.BI read(qid\fP,\fP\ buf\fP,\fP\ n\fP,\fP\ offset )
+Read
+.I n
+bytes of the file represented by
+.I qid
+at offset
+.I offset
+and place the result in
+.I buf.
+ Place in
+.I n
+the actual number of bytes read.
+The default action is to read directories but to issue permission denied on ordinary
+files.
+.TP
+.BI write(qid\fP,\fP\ buf\fP,\fP\ n\fP,\fP\ offset )
+Write
+.I n
+bytes to the file represented by
+.I qid
+at offset
+.I offset
+from the buffer
+.I buf.
+ Place in
+.I n
+the actual number of bytes written.
+The default action is to issue permission denied.
+.TP
+.BI close(qid\fP,\fP\ mode )
+Close the file represented by
+.I qid .
+The mode it was originally opened with is given by
+.I mode .
+The default action is to allow the close.
+.TP
+.BI remove(qid )
+Remove the file represented by
+.I qid .
+The default action is to issue a permission denied message.
+.TP
+.BI stat(qid\fP,\fP\ d )
+Place the information for the file represented by
+.I qid
+in the Dir structure(see lib9.h)
+.I d .
+The default action is to allow the stat using any information in the file tree.
+.TP
+.BI wstat(qid\fP,\fP\ d )
+Update the information for the file represented by
+.I qid
+according to the Dir structure
+.I d .
+The default action is to disallow this with a permission denied message.
+.PP
+A small number of utility functions are provided:
+.TP
+.BI styxperm(file\fP,\fP\ uid\fP,\fP\ mode )
+Does the file/directory
+.I file
+allow the user
+.I uid
+the permission given by
+.I mode .
+For example use
+.I OREAD
+for read permission,
+.I OWRITE
+for write permission and
+.I ORDWR
+for both.
+.TP
+.BI styxreadstr(off\fP,\fP\ buf\fP,\fP\ n\fP,\fP\ str )
+Read
+.I n
+bytes of data from the string
+.I str
+at offset
+.I off
+and place the result in
+.I buf .
+Returns the actual number of bytes read.
+.TP
+.BI styxqid(path\fP,\fP\ isdir )
+Returns a typical Qid structure with the given path number
+.I path
+and whether the Qid is for a directory
+.I isdir .
+.TP
+.BI styxmalloc(n )
+Allocate
+.I n
+bytes of memory and return it.
+.TP
+.BI styxfree(p )
+Free the memory pointed to by
+.I p .
+.TP
+.BI styxdebug()
+Print out some of the actions of the server.
+.SH EXAMPLE
+.PP
+A very small file server example is illustrated. First the include files and globals.
+.PP
+.EX
+	#include <lib9.h>
+	#include "styxserver.h"
+
+	int nq;
+	Styxserver *server;
+.EE
+.PP
+The main processing loop:
+.PP
+.EX
+	main(int argc, char **argv)
+	{
+		Styxserver s;
+
+		server = &s;
+		styxinit(&s, &ops, "6701", 100, 0555, 0);
+		myinit(&s);
+		for(;;) {
+			styxwait(&s);
+			styxprocess(&s);
+		}
+		return 0;
+	}
+.EE
+.PP
+Here the port number is 6701 and the root file permissions are 0555 - no write
+permission for anyone which implies that files and directories cannot be 
+created in the root directory.
+.PP
+The creation of the directory tree:
+.PP
+.EX
+	myinit(Styxserver *s)
+	{
+		styxaddfile(s, Qroot, 1, "fred", 0664, "inferno");
+		styxaddfile(s, Qroot, 2, "joe", 0664, "inferno");
+		styxadddir(s, Qroot, 3, "adir", 0775, "inferno");
+		styxaddfile(s, 3, 4, "bill", 0664, "inferno");
+		styxadddir(s, Qroot, 5, "new", 0775, "inferno");
+		styxadddir(s, 5, 6, "cdir", 0775, "inferno");
+		styxaddfile(s, 6, 7, "cfile", 0664, "inferno");
+		nq = 8;
+	}
+.EE
+.PP
+This creates two files
+.I fred
+and
+.I joe
+and two directories
+.I adir
+and
+.I new
+at the top level.
+.I adir
+contains a file called
+.I bill
+and
+.I new
+contains a directory called
+.I cdir
+which contains a file called
+.I cfile .
+Note that each new path number is unique.
+.PP
+The callback functions:
+.PP
+.EX
+	Styxops ops = {
+		nil,			/* newclient */
+		nil,			/* freeclient */
+
+		nil,			/* attach */
+		nil,			/* walk */
+		nil,			/* open */
+		mycreate,		/* create */
+		myread,		/* read */
+		nil,			/* write */
+		nil,			/* close */
+		myremove,	/* remove */
+		nil,			/* stat */
+		nil,			/* wstat */
+	};
+.EE
+.PP
+Here we choose the defaults most of the time.
+.PP
+The supplied callback routines:
+.PP
+.EX
+	char *
+	mycreate(Qid *qid, char *name, int perm, int mode)
+	{
+		int isdir;
+		Styxfile *f;
+
+		isdir = perm&DMDIR;
+		if(isdir)
+			f = styxadddir(server, qid->path, nq++, name , perm, "inferno");
+		else
+			f = styxaddfile(server, qid->path, nq++, name, perm, "inferno");
+		if(f == nil)
+			return Eexist;
+		*qid = f->d.qid;
+		return nil;
+	}
+
+	char *
+	myremove(Qid qid)
+	{
+		Styxfile *f;
+
+		f = styxfindfile(server, qid.path);
+		if(f != nil && (f->d.qid.type&QTDIR) && f->child != nil)
+			return "directory not empty";
+
+		if(styxrmfile(server, qid.path) < 0)
+			return Enonexist;
+		return nil;
+	}
+
+	char *
+	myread(Qid qid, char *d, ulong *n, vlong offset)
+	{
+		if(qid.path != 1){
+			*n = 0;
+			return nil;
+		}
+		*n = styxreadstr(offset, d, *n, "abcdeghijklmn");
+		return nil;
+	}
+.EE
+.PP
+Permission checking for walk (need execute permission on directory), open (the
+given mode must be compatible with the file permissions), create and remove (both of
+which need write permission on directory) is done automatically whenever
+possible. The functions
+.I mycreate
+and
+.I myremove
+below therefore can omit these checks.
+.PP
+The function
+.I mycreate
+simply creates a directory or file and
+if the file cannot be added to the directory tree it returns a 'file exists' error string.
+It sets the Qid for the newly created file before returning.
+.PP
+The function
+.I myremove
+first checks to see if the file represents a non-empty directory, in which case it
+disallows it's removal. Otherwise it
+removes the file if it can find it and returns a 'file does not exist' error string if it
+can't.
+.PP
+The function
+.I myread
+considers all files to be empty except for
+.I fred
+which notionally contains
+.I abcdefghijklmn .
+Note that the number of bytes read is returned in the argument
+.I n .
+.PP
+Once this file server is running, the root can be accessed by doing for example
+.PP
+.EX
+	mount -A tcp!<address>!6701 /n/remote
+.EE
+.PP
+under Inferno. Here
+.I <address>
+is the address of the machine running the file server (or the loopback address
+127.0.0.1 if it's all on one machine). The 
+.I -A
+option is used to prevent authentication which is not supported at the moment.
+Then we can do
+.PP
+.EX
+	cd /n/remote
+	ls
+	adir
+	fred
+	joe
+	new
+	...
+.EE
+.PP
+For a more complicated file server see /tools/styxtest/styxtest.c.
+.PP
+The file /tools/styxtest/mkfile shows how to compile and link the file server
+sources.
+.SH SOURCE
+.B /Nt/386/include/lib9.h
+.br
+.B /tools/libstyx/styxserver.h
+.br
+.B /tools/libstyx/styxserver.c
+.br
+.B /tools/styxtest/styxtest.c
+.br
+.B /tools/styxtest/styxtest0.c
+.SH BUGS
+Currently the library is available under Windows, Linux and Solaris only.
+.br
+Authentication is not supported.
--- /dev/null
+++ b/man/10/xalloc
@@ -1,0 +1,70 @@
+.TH XALLOC 10.2
+.SH NAME
+xalloc, xspanalloc, xfree \- basic memory management
+.SH SYNOPSIS
+.ta \w'\fLvoid* 'u
+.B
+void*	xalloc(ulong size)
+.PP
+.B
+void*	xspanalloc(ulong size, int align, ulong span)
+.PP
+.B
+void	xfree(void *p)
+.SH DESCRIPTION
+.I Xalloc
+and
+.I xfree
+are primitives used by higher-level memory allocators in the kernel,
+such as
+.IR malloc (10.2).
+They are not intended for use directly by most kernel routines.
+The main exceptions are routines that permanently allocate large structures,
+or need the special alignment properties guaranteed by
+.IR xspanalloc .
+.PP
+.I Xalloc
+returns a pointer to a range of size bytes of memory. The memory will be zero filled and aligned on a 8 byte
+.RB ( BY2V )
+address. If the memory is not available,
+.B xalloc
+returns a null pointer.
+.PP
+.I Xspanalloc
+allocates memory given alignment and spanning constraints.
+The block returned will contain
+.I size
+bytes, aligned on a boundary that is
+.BI "0 mod" " align,"
+in such a way that the memory in the block does not
+span an address that is
+.BI "0 mod" " span."
+.I Xspanalloc
+is intended for use
+allocating hardware data structures (eg, page tables) or I/O buffers
+that must satisfy specific alignment restrictions.
+If
+.I xspanalloc
+cannot allocate memory to satisfy the given constraints, it will
+.IR panic (10.2).
+The technique it uses can sometimes cause memory to be wasted.
+Consequently,
+.I xspanalloc
+should be used sparingly.
+.PP
+.I Xfree
+frees the block of memory at
+.IR p ,
+which must be an address previously returned by
+.I xalloc
+(not
+.IR xspanalloc ).
+.SS Allocation status
+Some memory allocation statistics are written to the console in response to
+the debugging sequence
+.LR "control-T control-T x" .
+The output includes the total free space, the number of free holes,
+and a summary of active holes.
+Each line shows `address top size'.
+.SH SEE ALSO
+.IR malloc (10.2)
--- /dev/null
+++ b/man/2/0intro
@@ -1,0 +1,189 @@
+.TH INTRO 2
+.SH NAME
+intro \- introduction to Limbo modules for the Inferno system
+.SH SYNOPSIS
+.EX
+include "sys.m";
+sys := load Sys Sys->PATH;
+
+include "draw.m";
+draw := load Draw Draw->PATH;
+
+include "tk.m";
+tk := load Tk Tk->PATH;
+
+.I "... etc."
+
+.I "Generically:"
+.EE
+.EX
+include "\fImodule\fP.m";
+.EE
+.fi
+.IB module " := "
+.BI load " Module"
+.IB Module ->PATH;
+.SH DESCRIPTION
+This section introduces the Limbo modules available to the programmer;
+see the corresponding manual pages for more information.
+Each module is declared with a single Limbo
+.B include
+file.
+Before calling a module's functions, an application must
+.B load
+the module; the application stores the resulting value in a variable for later use as
+the module qualifier.
+The examples above illustrate the style.
+It will usually be necessary in some cases to qualify names with the appropriate
+module pointer or to
+.B import
+the types and functions; the manual pages assume the names are accessible
+in the current scope.
+.PP
+Although many modules are self-contained,
+dependencies may exist.
+For example, the system module,
+.BR Sys ,
+provides basic services that many other modules require.
+These are the Inferno equivalent to `system calls'.
+.PP
+In a few cases, several related modules
+share a single
+.B include
+file;
+for instance,
+.BR security.m .
+.PP
+The manual pages describe how to
+.B include
+a module definition during
+compilation and
+.B load
+an implementation during execution.
+The documentation also lists relevant functions or abstract
+data types.
+Although the
+.B include
+files declare these components, the manual pages list them explicitly.
+In all cases, the enclosing
+.B module
+declaration is assumed so that unqualified identifiers can be
+used in the text without ambiguity, reducing clutter in the text.
+In practice when programming, many consider it good style to
+use an explicit module reference for functions and constants.
+.PP
+The Limbo modules are identical on any machine that is running Inferno,
+whether native or hosted, which enables Limbo programs to be written
+and tested on any Inferno system.
+.PP
+Many modules are described in a single page, such as
+.IR regex (2).
+Several larger modules are explained in several sections, such as
+.IR math-intro (2),
+.IR math-elem (2),
+.IR math-fp (2),
+and
+.IR math-linalg (2).
+.SS Exceptions
+Exception handling is now part of the Limbo language, replacing an older
+scheme that used special system calls.
+Various exceptions can be raised by the virtual machine when run-time errors
+are detected.
+These are the common ones:
+.RS
+.TP
+.B "alt send/recv on same chan"
+It is currently illegal for a channel to appear in two
+.B alt
+statements if they either both receive or both send on it.
+(It is fine to send in one and receive in the other.)
+.TP
+.B "array bounds error"
+Array subscript out of bounds.
+.TP
+.B "dereference of nil"
+Attempt to use a
+.B "ref" 
+adt or index an array with value
+.B "nil" .
+.TP
+.B "invalid math argument"
+Inconsistent values provided to functions of
+.IR math-intro (2).
+.TP
+.B "module not loaded"
+Attempt to use an uninitialised module variable.
+.TP
+.B "negative array size"
+The limit in an array constructor was negative.
+.TP
+.BI "out of memory:" " pool"
+The given memory
+.I pool
+is exhausted.
+.I Pool
+is currently one of
+.B main
+(kernel memory including Tk allocations),
+.B heap
+(most Limbo data),
+and
+.B image
+memory for
+.IR draw (3).
+.TP
+.B "zero divide"
+Integer division (or mod) by zero.
+.RE
+.PP
+There are currently two more classes of exception string with a conventional interpretation
+imposed not by the run-time system proper, but by Limbo components:
+.RS
+.TP
+.BI "fail:" "reason"
+Commands use this exception to provide an `exit status' to a calling program,
+particularly the shell
+.IR sh (1);
+see also
+.IR sh (2).
+The status is given by the
+.I reason
+following the
+.RB ` fail: '
+prefix.
+.TP
+.BI "assertion:" "error"
+A module detected the specified internal
+.IR error .
+This is most often used for cases where a particular possibility ``cannot happen''
+and there is no other need for an error value in the interface.
+.RE
+.PP
+Otherwise, most module interfaces tend to use explicit error return values, not exceptions.
+.PP
+Note that a Limbo exception handler can do pattern matching to catch a class of exceptions:
+.IP
+.EX
+{
+	\f2body of code to protect\fP
+} exception e {
+"out of memory:*" =>
+	\f2recovery action\fP
+"assertion:*" =>
+	fatal_error(e);
+}
+.EE
+.PP
+The effect of an unhandled exception in a process that is part of an error-recovery group
+can be controlled using the mechanisms described in
+.IR prog (3)
+as accessed using
+.IR exception (2).
+.SH SEE ALSO
+.IR draw-intro (2),
+.IR exception (2),
+.IR keyring-intro (2),
+.IR math-intro (2),
+.IR prefab-intro (2),
+.IR security-intro (2),
+.IR sys-intro (2)
--- /dev/null
+++ b/man/2/9p-ninep
@@ -1,0 +1,416 @@
+.TH 9P-NINEP 2
+.SH NAME
+Ninep: Rmsg, Tmsg, dir2text, istmsg, packdir, packdirsize, readmsg, qid2text, unpackdir \- interface to 9P file protocol
+.SH SYNOPSIS
+.EX
+include "9p.m";
+ninep := load Ninep Ninep->PATH;
+
+Tmsg: adt {
+    tag: int;
+    pick {
+    Readerror =>
+        error: string;      # tag is unused in this case
+    Version =>
+        msize: int;
+        version: string;
+    Auth =>
+        afid: int;
+        uname, aname: string;
+    Attach =>
+        fid, afid: int;
+        uname, aname: string;
+    Flush =>
+        oldtag: int;
+    Walk =>
+        fid, newfid: int;
+        names: array of string;
+    Open =>
+        fid, mode: int;
+    Create =>
+        fid: int;
+        name: string;
+        perm, mode: int;
+    Read =>
+        fid: int;
+        offset: big;
+        count: int;
+    Write =>
+        fid: int;
+        offset: big;
+        data: array of byte;
+    Clunk or
+    Stat or
+    Remove => 
+        fid: int;
+    Wstat =>
+        fid: int;
+        stat: Sys->Dir;
+    }
+
+    read:   fn(fd: ref Sys->FD, msize: int): ref Tmsg;
+    unpack: fn(a: array of byte): (int, ref Tmsg);
+    pack:   fn(nil: self ref Tmsg): array of byte;
+    packedsize: fn(nil: self ref Tmsg): int;
+    text:   fn(nil: self ref Tmsg): string;
+    mtype:  fn(nil: self ref Tmsg): int;
+};
+
+Rmsg: adt {
+    tag: int;
+    pick {
+    Readerror =>
+        error: string;      # tag is unused in this case
+    Version =>
+        msize: int;
+        version: string;
+    Auth =>
+        aqid: Sys->Qid;
+    Attach =>
+        qid: Sys->Qid;
+    Flush =>
+    Error =>
+        ename: string;
+    Clunk or
+    Remove or
+    Wstat =>
+    Walk =>
+        qids: array of Sys->Qid;
+    Create or
+    Open =>
+        qid: Sys->Qid;
+        iounit: int;
+    Read =>
+        data: array of byte;
+    Write =>
+        count: int;
+    Stat =>
+        stat: Sys->Dir;
+    }
+
+    read:   fn(fd: ref Sys->FD, msize: int): ref Rmsg;
+    unpack: fn(a: array of byte): (int, ref Rmsg);
+    pack:   fn(nil: self ref Rmsg): array of byte;
+    packedsize: fn(nil: self ref Rmsg): int;
+    text:   fn(nil: self ref Rmsg): string;
+    mtype:  fn(nil: self ref Rmsg): int;
+};
+
+init:        fn();
+
+readmsg:     fn(fd: ref Sys->FD, msize: int): (array of byte, string);
+istmsg:      fn(f: array of byte): int;
+
+compatible:  fn(t: ref Tmsg.Version, msize: int, version: string): (int, string);
+
+packdirsize: fn(d: Sys->Dir): int;
+packdir:     fn(d: Sys->Dir): array of byte;
+unpackdir:   fn(f: array of byte): (int, Sys->Dir);
+dir2text:    fn(d: Sys->Dir): string;
+qid2text:    fn(q: Sys->Qid): string;
+
+VERSION:  con "9P2000";
+MAXWELEM: con 16;
+NOTAG:    con 16rFFFF;
+NOFID:    con ~0;
+IOHDRSZ:  con \fIimplementation-defined\f5;
+DEFMSIZE: con \fIimplementation-defined\f5;
+.EE
+.SH DESCRIPTION
+.B Ninep
+provides a Limbo interface to send and receive messages of the 9P file service protocol,
+described by Section 5 of this manual, a thorough reading of which
+is advised before using this module.
+.B Init
+must be called before using any other function in the module.
+.PP
+A 9P client transmits requests to a server as `T-messages'
+and receives replies in matching `R-messages'.
+A T-message is here represented by values of the type
+.BR Tmsg ,
+and an R-message by values of type
+.BR Rmsg .
+Every message has a
+.B tag
+value, and the alternatives of the pick adt represent the possible operation types of a T-message,
+generally with parameter names and types corresponding to those described in section 5.
+The exceptions are:
+.B Tmsg.Write
+and
+.B Rmsg.Read
+contain an array of byte,
+.BR data ,
+to hold the data for the corresponding message, and the `count' parameter of the message is simply the length of that array;
+and there is an alternative labelled
+.B Readerror
+that does not appear in the protocol but is used to represent input errors as described below.
+Also note that values that are `unsigned' integers in the protocol are typically given signed integer
+types in the Limbo representation (in particular, fids, qid paths, counts and offsets), and applications
+should take appropriate care when manipulating them.
+.PP
+The following functions are provided by
+.BR Tmsg:
+.TP
+.BI read( fd\fP,\fP\ msize )
+Read file descriptor
+.I fd
+to obtain exactly one T-message and return (a reference to) the corresponding
+.BR Tmsg .
+A nil value is returned on end of file.
+Otherwise, if the read fails or the data read does not form a valid T-message,
+the value returned will be a
+.B Tmsg.Readerror
+value in which the
+.B error
+member describes the error.
+.I Msize
+gives the maximum number of bytes in any acceptable T-message,
+and should be the value negotiated in the exchange of
+.B version
+messages;
+any incoming message larger than that will result in a diagnostic as a
+.B Tmsg.Readerror
+value.
+An
+.I msize
+of 0 means `no limit negotiated' and should (only) be used until a message size
+has been negotiated by exchange of
+.IR version (5)
+messages.
+.TP
+.IB t .pack()
+Return an array of bytes containing the value of
+.I t
+in the machine-independent format described in Section 5.
+It can return nil only if the message
+.I t
+is itself nil or has an invalid type.
+.TP
+.BI unpack( a )
+The array
+.I a
+is assumed to contain zero or more T-messages.
+.B Unpack
+attempts to unpack the first message, and returns a tuple of the form
+.RI ( n , v ).
+If successful,
+.I n
+is the number of bytes at the start of
+.I a
+used by the message, and
+.I v
+is the corresponding
+.B Tmsg
+value.
+If
+.I a
+contains the prefix of a valid message but more data is required to complete it,
+.I n
+is zero (and
+.I v
+is nil); the caller will typically read more data, append it to
+.IR a ,
+and try again.
+If the message is invalid,
+.I n
+is -1
+and
+.I v
+is nil.
+.TP
+.IB t .packedsize()
+Return the number of bytes required for the value of
+.I t
+when packed in its machine-independent format.
+(Zero is returned if
+.I t
+is invalid.)
+.TP
+.IB t .text()
+Return a printable string showing the contents of
+.IR t ,
+for tracing or debugging.
+.TP
+.IB t .mtype()
+Return the 9P message type of the message.
+.PP
+An R-message is represented by
+.BR Rmsg .
+Its member functions behave exactly as those for
+.BR Tmsg ,
+except that they operate on R-messages not T-messages.
+.PP
+When a client reads a directory, the data returned in the reply must be formatted
+as described in
+.IR read (5):
+an array of directory entries, one per file, with each entry formatted in
+a machine-independent format.
+An appropriate array value can be produced by
+.B packdir
+from a
+.B Sys->Dir
+structure, as used by
+.IR sys-stat (2).
+The space that packed representation will take can be calculated beforehand by
+.BR packdirsize .
+The server will usually fill the buffer for the reply to the read
+with as many entries as will fit,
+checking the space remaining against the result of
+.B packdirsize
+and if the value will fit, storing the result of
+.BR packdir .
+Given an array
+.I a
+containing at most one packed directory value (as produced by
+.BR packdir ),
+.B unpackdir
+returns a tuple
+.RI ( n,\ d )
+where
+.I n
+is \-1 if
+.I a
+is illegally formatted;
+.I n
+is zero if
+.I a
+does not contain a complete directory entry value;
+and otherwise
+.I n
+is the number of bytes of
+.I a
+used to produce the unpacked
+.B Dir
+value
+.I d .
+.PP
+The functions
+.B dir2text
+and
+.B qid2text
+produce printable strings showing the contents of the corresponding data structures,
+for use when tracing or debugging.
+.PP
+Applications that acts as file servers will read T-messages and
+reply with R-messages.
+They can use
+.B Tmsg.read
+to read each T-message, build an
+.B Rmsg
+reply value
+.IR r ,
+and use
+.IB r .pack
+to produce an array of bytes to be written in reply by
+.B Sys->write
+(see
+.IR sys-read (2)).
+.PP
+A few specialised programs might need the lower-level function
+.B readmsg
+that underlies
+.B Tmsg.read
+and
+.BR Rmsg.read .
+It reads a single message, which can be either a T-message or R-message,
+and returns it as an array of bytes,
+which can then be unpacked using
+.B Tmsg.unpack
+or
+.BR Rmsg.unpack .
+.I Msize
+is the negotiated message size, or 0 meaning `no limit'.
+The predicate
+.B istmsg
+returns true if the contents of array
+.I f
+looks like a packed representation of a T-message,
+judging only by its
+.I type
+byte.
+.PP
+When generating the
+.B version
+message (see
+.IR version (5)),
+the constant
+.B NOTAG
+can be used in
+.B Tmsg.tag
+and
+.B Rmsg.tag
+to represent `no tag value'.
+The constant
+.B VERSION
+names the current version of the protocol, and can be
+used as the value of
+.BR Tmsg.version .
+.PP
+.B Compatible
+can be used by a server to
+compare its
+.I msize
+and
+.I version
+(which is typically
+.BR VERSION )
+to those in the
+.B Tmsg.Version
+message received from a client, to decide its reply,
+following the rules in
+.IR version (5).
+It returns a tuple
+.RI ( m ", " v )
+with values for use in the
+.B Rmsg.Version
+reply.
+.I M
+is the lesser of
+.I msize
+and
+.IB t .msize ,
+and
+.I v
+is the negotiated protocol version, or the value \f5"unknown"\f1
+if no version could be agreed.
+The constant
+.B DEFMSIZE
+is a reasonable value for
+.I msize
+on current systems.
+The resulting value
+.I m
+can subsequently be given to the various read functions as the limit
+.IR msize .
+The constant
+.B IOHDRSZ
+gives the amount to allow for protocol overhead, when limiting data size for
+.B Tmsg.Write
+and
+.BR Rmsg.Read .
+.PP
+The constant
+.B NOFID
+can be used as the value of
+.B afid
+of the
+.B attach
+message when authentication is not required (see
+.IR attach (5)).
+.PP
+The constant
+.B MAXWELEM
+gives the protocol-defined limit on the length of the arrays
+.B Tmsg.names
+and
+.BR Rmsg.qids .
+For specialised applications, the module defines constants
+.BR Tversion ,
+.BR Rversion ,
+etc. for the message types of the protocol, and the
+other constants mentioned in Section 5.
+.SH SOURCE
+.B /appl/lib/ninep.b
+.SH SEE ALSO
+.IR styxservers (2),
+.IR intro (5)
--- /dev/null
+++ b/man/2/INDEX
@@ -1,0 +1,551 @@
+intro 0intro
+alphabet alphabet-intro
+alphabet-intro alphabet-intro
+arg arg
+asn1 asn1
+decode asn1
+encode asn1
+attrdb attrdb
+bloomfilter bloomfilter
+bufio bufio
+bufiofill bufio
+bufio bufio-chanfill
+bufio-chanfill bufio-chanfill
+chanfill bufio-chanfill
+attr cfg
+cfg cfg
+record cfg
+tuple cfg
+command command
+complete complete
+btos convcs
+convcs convcs
+stob convcs
+crc crc
+csv csv
+daytime daytime
+filet daytime
+gmt daytime
+local daytime
+now daytime
+string2tm daytime
+text daytime
+time daytime
+tm2epoch daytime
+dbf dbm
+dbm dbm
+init dbm
+debug debug
+devpointer devpointer
+applycfg dhcpclient
+bootconf dhcpclient
+bootp dhcpclient
+dhcp dhcpclient
+dhcpclient dhcpclient
+lease dhcpclient
+removecfg dhcpclient
+accept dial
+announce dial
+dial dial
+listen dial
+netinfo dial
+netmkaddr dial
+reject dial
+dialog dialog
+getstring dialog
+prompt dialog
+dict dict
+dis dis
+block diskblocks
+disk diskblocks
+diskblocks diskblocks
+tempfile diskblocks
+chstext disks
+disk disks
+disks disks
+pcpart disks
+readn disks
+dividers dividers
+draw draw-0intro
+draw-intro draw-0intro
+context draw-context
+draw-context draw-context
+display draw-display
+draw-display draw-display
+draw draw-example
+draw-example draw-example
+example draw-example
+draw-font draw-font
+font draw-font
+draw-image draw-image
+image draw-image
+draw-point draw-point
+point draw-point
+draw-pointer draw-pointer
+pointer draw-pointer
+draw-rect draw-rect
+rect draw-rect
+draw-screen draw-screen
+screen draw-screen
+drawmux drawmux
+dec encoding
+enc encoding
+encoding encoding
+env env
+ether ether
+exception exception
+attrtext factotum
+challenge factotum
+copyattrs factotum
+delattr factotum
+factotum factotum
+factotum factotum factotum
+findattr factotum
+findattrval factotum
+getuserpassd factotum
+mount factotum
+open factotum
+parseattrs factotum
+proxy
+publicattrs factotum
+respond factotum
+response factotum
+rpc factotum
+rpcattrs factotum
+takeattrs factotum
+expand filepat
+filepat filepat
+match filepat
+filter filter
+deflate filter-deflate
+filter-deflate filter-deflate
+inflate filter-deflate
+filter-slip filter-slip
+slip filter-slip
+format format
+fsproto fsproto
+readprotofile fsproto
+readprotostring fsproto
+geodesy geodesy
+hash hash
+hashtable hash
+consistent ida
+frag ida
+fragment ida
+ida ida
+reconstruct ida
+imagefile imagefile
+readgif imagefile
+readjpg imagefile
+readpicfile imagefile
+readpng imagefile
+readxbitmap imagefile
+remap imagefile
+ip ip
+ir ir
+itslib itslib
+json json
+jvalue json
+readjson json
+writejson json
+keyring intro keyring-0intro
+keyring-intro keyring-0intro
+auth keyring-auth
+keyring keyring-auth
+keyring-auth keyring-auth
+readauthinfo keyring-auth
+writeauthinfo keyring-auth
+certtostr keyring-certtostr
+keyring keyring-certtostr
+keyring-certtostr keyring-certtostr
+pktostr keyring-certtostr
+sktostr keyring-certtostr
+strtocert keyring-certtostr
+strtopk keyring-certtostr
+strtosk keyring-certtostr
+aescbc keyring-crypt
+aessetup keyring-crypt
+descbc keyring-crypt
+desecb keyring-crypt
+dessetup keyring-crypt
+ideacbc keyring-crypt
+ideaecb keyring-crypt
+ideasetup keyring-crypt
+keyring keyring-crypt
+keyring-crypt keyring-crypt
+dhparams keyring-gensk
+gensk keyring-gensk
+genskfrompk keyring-gensk
+keyring keyring-gensk
+keyring-gensk keyring-gensk
+sktopk keyring-gensk
+getmsg keyring-getmsg
+keyring keyring-getmsg
+keyring-getmsg keyring-getmsg
+senderrmsg keyring-getmsg
+sendmsg keyring-getmsg
+getbytearray keyring-getstring
+getstring keyring-getstring
+keyring keyring-getstring
+keyring-getstring keyring-getstring
+putbytearray keyring-getstring
+puterror keyring-getstring
+putstring keyring-getstring
+ipint keyring-ipint
+keyring keyring-ipint
+keyring-ipint keyring-ipint
+keyring keyring-rc4
+keyring-rc4 keyring-rc4
+rc4 keyring-rc4
+rc4back keyring-rc4
+rc4setup keyring-rc4
+rc4skip keyring-rc4
+hmac_md5 keyring-sha1
+hmac_sha1 keyring-sha1
+keyring keyring-sha1
+keyring-sha1 keyring-sha1
+md4 keyring-sha1
+md5 keyring-sha1
+sha1 keyring-sha1
+sign keyring-sha1
+verify keyring-sha1
+keyset keyset
+allsat lists
+anysat lists
+append lists
+combine lists
+concat lists
+delete lists
+filter lists
+ismember lists
+last lists
+lists lists
+map lists
+pair lists
+partition lists
+rev lists
+unpair lists
+lock lock
+intro math-0intro
+math math-0intro
+math-intro math-0intro
+acos math-elem
+acosh math-elem
+asin math-elem
+asinh math-elem
+atan math-elem
+atan2 math-elem
+atanh math-elem
+cbrt math-elem
+cos math-elem
+cosh math-elem
+erf math-elem
+erfc math-elem
+exp math-elem
+expm1 math-elem
+hypot math-elem
+j0 math-elem
+j1 math-elem
+jn math-elem
+lgamma math-elem
+log math-elem
+log10 math-elem
+log1p math-elem
+math math-elem
+math-elem math-elem
+pow math-elem
+pow10 math-elem
+sin math-elem
+sinh math-elem
+sqrt math-elem
+tan math-elem
+tanh math-elem
+y0 math-elem
+y1 math-elem
+yn math-elem
+export_int math-export
+export_real math-export
+export_real32 math-export
+import_int math-export
+import_real math-export
+import_real32 math-export
+math math-export
+math-export math-export
+math math-fp
+math-fp math-fp
+dot math-linalg
+gemm math-linalg
+iamax math-linalg
+math math-linalg
+math-linalg math-linalg
+norm1 math-linalg
+norm2 math-linalg
+sort math-linalg
+mpeg mpeg
+getbytearray msgio
+getmsg msgio
+getstring msgio
+msgio msgio
+putbytearray msgio
+puterror msgio
+putstring msgio
+senderrmsg msgio
+sendmsg msgio
+basename names
+cleanname names
+dirname names
+elements names
+isprefix names
+names names
+pathname names
+relative names
+rooted names
+newns newns
+newuser newns
+categories palmfile
+dbinfo palmfile
+doc palmfile
+entry palmfile
+palmfile palmfile
+pfile palmfile
+record palmfile
+plumbmsg plumbmsg
+pop3 pop3
+add popup
+changebutton popup
+event popup
+mkbutton popup
+popup popup
+intro prefab-0intro
+prefab prefab-0intro
+prefab-intro prefab-0intro
+compound prefab-compound
+prefab prefab-compound
+prefab-compound prefab-compound
+element prefab-element
+prefab prefab-element
+prefab-element prefab-element
+environ prefab-environ
+prefab prefab-environ
+prefab-environ prefab-environ
+prefab prefab-style
+prefab-style prefab-style
+style prefab-style
+print print
+prof prof
+profile prof
+pslib pslib
+rand rand
+readdir readdir
+regex regex
+registries registries
+rfc822 rfc822
+scsi scsiio
+scsiio scsiio
+secstore secstore
+intro security-0intro
+security-intro security-0intro
+auth security-auth
+client security-auth
+init security-auth
+security-auth security-auth
+server security-auth
+login security-login
+security-login security-login
+random security-random
+randombuf security-random
+randomint security-random
+security-random security-random
+connect security-ssl
+secret security-ssl
+security-ssl security-ssl
+ssl security-ssl
+selectfile selectfile
+sets sets
+sexp sexprs
+sexprs sexprs
+sh sh
+smtp smtp
+cert spki
+hash spki
+key spki
+name spki
+seqel spki
+signature spki
+spki spki
+subject spki
+toplev spki
+valid spki
+spki-verifier spki-verifier
+verifier spki-verifier
+verify spki-verifier
+spree spree
+allow spree-allow
+spree-allow spree-allow
+cardlib spree-cardlib
+spree-cardlib spree-cardlib
+gatherengine spree-gather
+spree-gather spree-gather
+objstore spree-objstore
+spree-objstore spree-objstore
+srv srv
+append string
+drop string
+in string
+prefix string
+quoted string
+splitl string
+splitr string
+splitstrl
+splitstrr string
+string string
+string string string
+take string
+tobig string
+toint string
+tolower string
+toreal string
+toupper string
+unquoted string
+stringinttab stringinttab
+dir2text styx
+istmsg styx
+packdir styx
+packdirsize styx
+qid2text styx
+readmsg styx
+rmsg styx
+styx styx
+tmsg styx
+unpackdir styx
+styxconv styxconv
+styxflush styxflush
+styxpersist styxpersist
+styxservers styxservers
+nametree styxservers-nametree
+styxservers styxservers-nametree
+styxservers-nametree styxservers-nametree
+intro sys-0intro
+sys sys-0intro
+sys-intro sys-0intro
+bind sys-bind
+mount sys-bind
+sys-bind sys-bind
+unmount sys-bind
+byte2char sys-byte2char
+char2byte sys-byte2char
+sys-byte2char sys-byte2char
+chdir sys-chdir
+sys-chdir sys-chdir
+announce sys-dial
+dial sys-dial
+listen sys-dial
+sys-dial sys-dial
+dirread sys-dirread
+sys-dirread sys-dirread
+dup sys-dup
+fildes sys-dup
+sys-dup sys-dup
+export sys-export
+sys-export sys-export
+fauth sys-fauth
+sys-fauth sys-fauth
+fd2path sys-fd2path
+sys-fd2path sys-fd2path
+file2chan sys-file2chan
+sys-file2chan sys-file2chan
+fversion sys-fversion
+sys-fversion sys-fversion
+iounit sys-iounit
+sys-iounit sys-iounit
+millisec sys-millisec
+sys-millisec sys-millisec
+create sys-open
+open sys-open
+sys-open sys-open
+pctl sys-pctl
+sys-pctl sys-pctl
+pipe sys-pipe
+sys-pipe sys-pipe
+aprint sys-print
+fprint sys-print
+print sys-print
+sprint sys-print
+sys-print sys-print
+pread sys-read
+pwrite sys-read
+read sys-read
+stream sys-read
+sys-read sys-read
+write sys-read
+remove sys-remove
+sys-remove sys-remove
+seek sys-seek
+sys-seek sys-seek
+self sys-self
+sys-self sys-self
+sleep sys-sleep
+sys-sleep sys-sleep
+fstat sys-stat
+fwstat sys-stat
+stat sys-stat
+sys-stat sys-stat
+wstat sys-stat
+sys-tokenize sys-tokenize
+tokenize sys-tokenize
+sys-utfbytes sys-utfbytes
+utfbytes sys-utfbytes
+sys-werrstr sys-werrstr
+werrstr sys-werrstr
+mktabs tabs
+tabs tabs
+tabsctl tabs
+tftp tftp
+timers timers
+cmd tk
+imageget tk
+imageput tk
+keyboard tk
+namechan tk
+pointer tk
+quote tk
+tk tk
+toplevel tk
+handler tkclient
+makedrawcontext tkclient
+onscreen tkclient
+settitle tkclient
+snarfget tkclient
+snarfput tkclient
+startinput
+tkclient tkclient
+tkclient tkclient tkclient
+toplevel tkclient
+wmctl tkclient
+mkdictname translate
+opendict translate
+opendicts translate
+translate translate
+readubf ubfa
+ubfa ubfa
+uvalue ubfa
+writeubf ubfa
+venti venti
+virgil virgil
+volume volume
+w3c w3c-css
+w3c-css w3c-css
+w3c w3c-uris
+w3c-uris w3c-uris
+w3c w3c-xpointers
+w3c-xpointers w3c-xpointers
+wait wait
+makedrawcontext wmclient
+snarfget wmclient
+snarfput wmclient
+window wmclient
+wmclient wmclient
+wmlib wmlib
+wmsrv wmsrv
+workdir workdir
+xml xml
--- /dev/null
+++ b/man/2/alphabet-intro
@@ -1,0 +1,236 @@
+.TH ALPHABET-INTRO 2
+.SH NAME
+Alphabet \- experimental typed shell
+.SH DESCRIPTION
+.SS "Values, type characters and signatures"
+Each Alphabet typeset defines one Limbo data type, conventionally
+named
+.BR Value .
+It is usually a discriminated union (pick), with each arm of the
+pick representing one of the types in the typeset.
+Each one of these types is given a character. These characters are
+used to describe all value- and module-types within alphabet.
+The set of typeset characters implemented by a typeset is
+known as its
+.IR alphabet .
+.PP
+For example, in the
+.I alphabet
+root typeset
+(see
+.IR alphabet-main (2)),
+a string is represented by
+the letter ``s'', held at the Limbo level as
+a
+.BR "ref Value.S" .
+.PP
+Each alphabet module has a
+.I "type signature"
+which describes its
+return type and the number and type of any flags or
+arguments that it allows.
+Inside an
+.I alphabet
+typeset, this signature is represented as a simple
+string where the first character (always present)
+indicates the return type of the module. Subsequent
+characters up until the first minus (``-'') sign (or the
+end of the string) indicate
+the module's required argument types.
+If the last character is an asterisk
+.RB ( * ),
+it allows an unlimited repetition of the preceding argument type.
+.PP
+These may be followed by any number of options, each indicated with a
+minus character, followed by the option character and then the type
+characters of any arguments it requires.
+.PP
+For instance, the following Alphabet declaration:
+.EX
+	/mount [-abc] [-x /string] /wfd /string -> /status
+.EE
+can be represented by the signature
+.BR `` rws-a-b-xs-c ''.
+.SS "Typesets and proxies"
+The root typeset
+(see
+.IR alphabet-main(2))
+is implemented internally to the
+.I alphabet
+module. All other types are defined by
+.I external
+typesets.
+.PP
+An external
+.I alphabet
+typeset is conventionally represented by two header files
+defining the interface to the typeset, and two modules
+giving its implementation. Suppose we are to
+create a new typeset, say
+.B /foo .
+We would create the following files:
+.TP 10
+.B /module/alphabet/foo.m
+.B Foo.m
+declares the interface used by all modules within the typeset.
+The existing typeset interface files (for instance
+.BR alphabet/grid.m ,
+documented in
+.IR alphabet-grid (2))
+provide examples of this kind of interface.
+.TP
+.B /appl/alphabet/footypes.b
+This module translates
+between
+.I "internal values"
+(each held as a
+.B Value
+as declared in
+.BR foo.m )
+and
+.I "external values"
+(each held as a
+.B Value
+as declared in the parent typeset, in this case by
+the
+.I alphabet
+module itself).
+Since Limbo does not provide a way of
+holding an arbitrary type directly, internal
+values are instead stored in a table by a local
+.IR proxy
+(see below),
+and referred to externally by their index there.
+.TP
+.B /appl/alphabet/foo.b
+.B Foo.b
+provides the basic type-manipulation
+primitives needed by the typeset, for instance
+the translation from type character to type name.
+It is also a convenient place to implement
+helper functions to make using the typeset easier.
+For instance, it is conventional for
+a typeset's
+.B Value
+adt to contain one eponymously named member function for each
+type character, making sure that the
+.B Value
+is actually of that type and returning the widened type,
+or raising an exception otherwise.
+For instance, in the root typeset,
+.IB v .s()
+returns the type
+.BR "ref Value.S" ,
+or raises an error if
+.I v
+is not of that type.
+.TP
+.B /module/alphabet/footypes.m
+.B Footypes.m
+provides an interface to the typeset proxy module,
+.BR footypes.b ,
+that allows direct
+access to values in the
+.B foo
+typeset, while still allowing
+manipulation of those values by an
+.I alphabet
+instance.
+.PP
+
+The proxy module,
+.BR footypes.b ,
+must define at least one function,
+.BR proxy ,
+which returns a channel through which
+all operations on the typeset take place.
+The
+.B Proxy
+module (see
+.IR alphabet-proxy (2))
+provides a generic implementation of such a translator;
+if
+.B footypes.b
+uses this, it needs only define the mapping
+between values in its parent typeset and its own values.
+.PP
+
+
+.SS "alphabet-main(2)"
+.TP 10
+.B types()
+.B Types
+is always the first function in a module to be called.
+It should do nothing but return the type signature string of the module.
+.TP
+.B init()
+.B Init
+is called to allow the module to initialise its global
+state. It is called once only.
+It is permissible for this function to raise a
+.RB `` fail: ''
+exception on failure. The text
+following the
+.RB `` fail: ''
+prefix should describe the reason why.
+.TP
+\f5run\fR(\fIerrorc\fP, \fIr\fP, \fIopts\fP, \fIargs\fP)
+.RS
+.B Run
+runs an actual instance of the module. It must be re-entrant.
+The signature of the
+.B run
+function varies from typeset to typeset,
+but usually includes the above arguments.
+.I Args
+holds a list of the arguments passed to the module;
+.I opts
+holds all the flags that have been specified.
+Each flag is represented with a tuple, say: (\fIc\fR, \fIoptargs\fP),
+where
+.I c
+gives the option character, and
+.I optargs
+is a list holding any arguments the flag requires.
+The arguments and options passed to the module are guaranteed
+to conform with the type signature returned from
+.BR types .
+Note that each flag may be passed multiple times to the module.
+.PP
+If the run succeeds, it should return the
+resulting value. If the module returns a value that was passed in,
+and it contains a reference-count, the count should
+be incremented before returning,
+If the module succeeds, it is responsible for the
+disposal of any arguments and option arguments that it has been given.
+Appropriate disposal depends on the type of the argument,
+but
+.IB v .free(0)
+is always sufficient to dispose of value
+.IR v .
+.PP
+If the run fails, it should return
+.BR nil ;
+its arguments will automatically be freed in this case.
+.PP
+While processing the
+.B run
+request, the module should send error and debugging diagnostics
+to the
+.I errorc
+channel (it should take care never to send an empty string).
+If it spawns any new processes, it can use the
+.BR Report ,
+.IR r ,
+(see
+.IR alphabet-reports (2))
+to create new diagnostic channels for these processes.
+When such an diagnostic channel is no longer in use,
+the module should send an empty string on it.
+It should take care that
+.B Report.start
+is called
+.I before
+.B run
+returns.
+.RE
--- /dev/null
+++ b/man/2/arg
@@ -1,0 +1,168 @@
+.TH ARG 2
+.SH NAME
+arg \- parse program arguments
+.SH SYNOPSIS
+.EX
+include "arg.m";
+arg := load Arg Arg->PATH;
+
+init:     fn(argv: list of string);
+setusage: fn(s: string);
+usage:    fn();
+progname: fn(): string;
+opt:      fn(): int;
+arg:      fn(): string;
+earg:     fn(): string;
+argv:     fn(): list of string;
+.EE
+.SH DESCRIPTION
+.B Arg
+parses a program's argument list in a traditional form,
+as received from a shell or other program
+(see
+.IR command (2)).
+The list must be passed to
+.B init
+to set the state for the other functions.
+.PP
+.B Arg
+takes the first argument to be the program name.
+Subsequent calls to
+.B progname
+return it.
+.PP
+Options are arguments containing one or more letters preceded by
+.B \-
+(dash, hyphen, minus).
+The list of options ends
+before the first argument that does not begin with a
+.BR \- .
+Option lists also end
+after an argument
+.BR \-\- ,
+to allow programs
+to accept arguments
+that would otherwise look like options
+(eg, file names for
+.IR rm (1)
+or a pattern for
+.IR grep (1)).
+Finally, option lists end
+before an argument
+.BR \- ,
+which is traditionally interpreted by some commands as referring to the standard input or output
+(depending on context).
+.PP
+Successive calls to
+.B opt
+return option characters in turn; 0 is returned at the end of the list.
+A program might take a parameter to a given option (eg, an option of the form
+.BI -f file
+or
+.BI -f " file" \f1).\f0
+Following a call to
+.BR opt ,
+a call to
+.BR arg
+will return the rest of the current argument string if not empty,
+failing that, the next argument string if any,
+and otherwise
+.BR nil .
+.B Earg
+is like
+.B arg
+except that if there is no argument associated
+with the option, an error message is printed to
+standard error, and a "\f5fail:usage\fP"
+exception raised.
+.B Setusage
+sets the error message that will be printed in
+this case (preceded by
+.RB ` usage: '
+and followed by a newline).
+.PP
+The argument list remaining after the last call to
+.B opt
+is returned by
+.BR argv .
+.SH EXAMPLE
+The following Limbo program takes options
+.BR b ,
+.B c
+and
+.BR f ,
+where
+.B f
+takes a file name argument.
+.br
+.ne 2i
+.PP
+.EX
+.ps -1
+.vs -1
+.ta \w'12345678'u +\w'12345678'u +\w'12345678'u +\w'12345678'u +\w'12345678'u
+implement Prog;
+include "sys.m";
+    sys: Sys;
+include "draw.m";
+include "arg.m";
+    arg: Arg;
+Prog: module
+{
+    init: fn(nil: ref Draw->Context, nil: list of string);
+};
+
+init(nil: ref Draw->Context, args: list of string)
+{
+    sys = load Sys Sys->PATH;
+    arg = load Arg Arg->PATH;
+
+    bflag := cflag := 0;
+    file := "";
+    arg->init(args);
+    while((c := arg->opt()) != 0)
+        case c {
+        'b' => bflag = 1;
+        'c' => cflag = 1;
+        'f' => file = arg->arg();
+        * =>   sys->print("unknown option (%c)\en", c);
+        }
+    args = arg->argv();
+    sys->print("%s %d %d %s\en", arg->progname(), bflag, cflag, file);
+    for(; args != nil; args = tl args)
+        sys->print("%s\en", hd args);
+}
+.ps +1
+.vs +1
+.EE
+.PP
+When invoked as follows:
+.IP
+.B "prog -bc -ffile a b c"
+.PP
+the output is:
+.IP
+.EX
+prog 1 1 file
+a
+b
+c
+.EE
+.PP
+and when invoked by:
+.IP
+.B "./prog -b -f file -z -- -bc"
+.PP
+the output is:
+.IP
+.EX
+unknown option (z)
+\&./prog 1 0 file
+-bc
+.EE
+.SH SOURCE
+.B /appl/lib/arg.b
+.SH SEE ALSO
+.IR sh (1),
+.IR mash (1),
+.IR command (2)
--- /dev/null
+++ b/man/2/asn1
@@ -1,0 +1,621 @@
+.TH ASN1 2
+.SH NAME
+asn1: decode, encode \- ASN.1 (X.208), BER (X.209) encoding
+.SH SYNOPSIS
+.EX
+include "asn1.m";
+asn1 := load ASN1 ASN1->PATH;
+asn1->init();
+
+Elem: adt {
+    tag: Tag;
+    val: ref Value;
+
+    is_seq:         fn(e: self ref Elem): (int, list of ref Elem);
+    is_set:         fn(e: self ref Elem): (int, list of ref Elem);
+    is_int:         fn(e: self ref Elem): (int, int);
+    is_bigint:      fn(e: self ref Elem): (int, array of byte);
+    is_bitstring:   fn(e: self ref Elem): (int, int, array of byte);
+    is_octetstring: fn(e: self ref Elem): (int, array of byte);
+    is_oid:         fn(e: self ref Elem): (int, ref Oid);
+    is_string:      fn(e: self ref Elem): (int, string);
+    is_time:        fn(e: self ref Elem): (int, string);
+    tostring:       fn(e: self ref Elem): string;
+};
+
+Tag: adt {
+    class: int;
+    num: int;
+    constr: int;
+
+    tostring: fn(t: self Tag): string;
+};
+
+Value: adt {
+    pick {
+        Bool or Int =>
+            v: int;
+        Octets or BigInt or Real or Other =>
+            bytes: array of byte;
+        BitString =>
+            unusedbits: int;
+            bits: array of byte;
+        Null or EOC =>
+            ;
+        ObjId =>
+            id: ref Oid;
+        String =>
+            s: string;
+        Seq or Set =>
+            l: list of ref Elem;
+    }
+
+    tostring: fn(v: self ref Value): string;
+};
+
+Oid: adt {
+    nums: array of int;
+    tostring: fn(o: self ref Oid): string;
+};
+
+init: fn();
+decode:       fn(a: array of byte): (string, ref Elem);
+decode_seq:   fn(a: array of byte): (string, list of ref Elem);
+decode_value: fn(a: array of byte, kind, constr: int):
+              (string, ref Value);
+encode:       fn(e: ref Elem): (string, array of byte);
+oid_lookup:   fn(o: ref Oid, tab: array of Oid): int;
+print_elem:   fn(e: ref Elem);
+
+.EE
+.SH DESCRIPTION
+.B ASN1
+supports decoding and encoding of the ASN.1 Basic Encoding Rules
+(BER, ITU-T Recommendation X.209).
+Despite its name, the module is not a parser for Abstract Syntax Notation One
+(ASN.1, ITU-T Recommendation X.208).
+.PP
+.B ASN1
+handles the BER encodings of all types from the ASN.1 Universal class, and
+provides a simple OBJECT IDENTIFIER comparison facility.
+.PP
+For simplicity,
+.B ASN1
+does not take a description of the ASN.1 module of the data being
+processed.
+Consequently, the (de)composition of tagged types must be performed by the
+application.
+.B ASN1
+does not know the
+context of tagged values and so cannot determine the underlying Universal type
+to be able to encode or decode the value automatically.
+See the section on Tagging for details on how the application
+should handle both implicit and explicit tagging.
+.TP
+.B init()
+The module must be initialised by calling this function
+before any other module functions or associated adt member functions are called.
+.TP
+.BI decode( a )
+Convert the BER encoding given by the byte array
+.I a
+into an
+.B Elem
+representing the ASN.1 value.
+The byte array must contain
+the entire BER encoding of the value and any component values.
+.IP
+Item values not tagged as a Universal type are converted to an
+.B Elem
+comprised of the decoded
+.B Tag
+and a
+value given by the
+.B Value.Octets
+variant, which contains
+the original encoding of the value
+stripped of the BER tag and length header.
+.IP
+The function returns a tuple composed of an error string and
+the decoded
+.BR Elem .
+If no errors are encountered the error string is nil.
+.TP
+.BI decode_seq( a )
+Like
+.B decode
+except that the data in
+.I a
+is the encoding of an item of type SEQUENCE, SEQUENCE OF, SET or SET OF
+which has been stripped of its tag and length header.
+The function decodes all of the items in the SEQUENCE or SET.
+.IP
+The return value is a tuple composed of an error string and the list of
+.BR Elem s
+forming the SEQUENCE or SET.
+.HP
+.BI decode_value( a ,
+.IB kind ,
+.IB constr )
+.br
+Convert the encoding of a single item value to a
+.B Value
+data structure.
+.IP
+The array
+.I a
+does not include the tag and length header.
+Instead, the value's Universal type is given by the
+.I kind
+argument and length is given by that of the array.
+The
+.B constr
+argument indicates if the encoding is in the BER
+constructed form or not.
+A value of 0 indicates that the primitive encoding is used, all other values
+indicate the constructed encoding.
+.IP
+The function returns a tuple composed of an error string and a
+.B Value
+reference.
+.TP
+.BI encode( e )
+Convert the
+.B Elem
+.I e
+to a BER encoding of the element.
+If the element is of a structured type, such as SEQUENCE or SET,
+then all component values are also exhaustively encoded.
+.IP
+The encoding can fail if the
+.B Tag
+and
+.B Value
+of the element are not compatible.
+The
+.I constr
+field of the
+.B Tag
+is currently ignored.
+.IP
+The function returns a tuple comprising an error string and
+the BER encoding.  
+If no errors are encountered the error string is nil and the
+second part of the returned tuple is a byte array of the
+BER encoding.
+.TP
+.BI oid_lookup( o ", " tab )
+Lookup an OBJECT IDENTIFIER value in an array of such values.
+Returns the index of the first exact match of
+.I o
+in the
+.I tab
+array.
+Returns -1 if no match is found.
+.TP
+.BI print_elem( e )
+Print a textual representation of the element to standard output.
+The output is that given by
+.BR Elem.tostring() ,
+followed by a newline character.
+.SS "Elem adt"
+This is the principal data structure, representing the value of an ASN.1 item.
+The adt couples a data representation, the
+.BR Value ,
+with its type specifier, the
+.BR Tag .
+.TP
+.B "Elem.tag"
+Specifies the ASN.1 type of the element value.
+See the description of the
+.B Tag
+adt for more details.
+.TP
+.B "Elem.val"
+The value of the element.
+See the description of the
+.B Value
+adt for more details.
+.PP
+All of the
+.IB e .is_ Type
+member functions test whether the specific
+.B Value
+pick variant of
+.B Elem.val
+and the ASN.1 Universal type, given by
+the tag, match and are of the requested form.
+A successful match yields the type specific data from the
+.B Value
+pick variant.
+The association of Universal types to
+.B Value
+pick variants is given in the section on the 
+.B Value
+adt.
+.PP
+The function
+.IB e .is_int
+succeeds for BOOLEAN and INTEGER ASN.1 types.
+The function
+.IB e .is_string
+succeeds for all of the ASN.1 Universal string types.
+.PP
+Except for
+.BR is_bitstring ,
+each function returns a tuple of two values.
+The first tuple item is an integer, 1 for success, 0 for failure.
+The second item is the type specific data from the
+.B Value
+pick variant.
+.PP
+.TP
+.IB e ".is_bitstring()"
+Like the
+.BI is_ Type
+functions described above.
+Tests that the element is a BIT STRING and returns its data.
+.IP
+The return value is a tuple comprised of two integers and an array of bytes.
+The byte array represents the bit string.
+The first integer is 1 for success, 0 for failure.
+The second integer is the number of unused bits in the last byte of the data
+array.
+See the description of the
+.B Value.BitString
+variant for more information.
+.TP
+.IB e ".tostring()"
+returns a textual representation of the element formed by joining
+the strings returned from
+.IB e ".tag.tostring()"
+and
+.IB e ".val.tostring()" .
+.PP
+.SS "Tag adt"
+The
+.B Tag
+adt denotes the ASN.1 type of a
+.B Value
+instance.
+.TP
+.B "Tag.class"
+Specifies the class of the type and can take one of the values:
+.BR ASN1->Universal ,
+.BR ASN1->Application ,
+.B ASN1->Context
+or
+.BR ASN1->Private .
+.TP
+.B "Tag.num"
+Identifies the particular type, or tag, within the specified class.
+Tag numbers for the Universal class are given in the
+.B asn1.m
+header file.
+The inconsistent use of upper-case and mixed-case identifiers comes
+straight from the ITU-T Recommendation.
+.TP
+.B "Tag.constr"
+This flag is set by the
+.B ASN1
+decode functions to mark if the BER
+.I constructed
+encoding was used for the value.
+A zero value indicates the BER primitive encoding, non-zero indicates
+the constructed encoding.
+.TP
+.IB t ".tostring()"
+Returns a string representation of the
+.BR Tag .
+For Universal class tags the function returns
+the string
+.RB `` UNIVERSAL
+.IR "Name" '',
+where
+.I Name
+is the standard name of the specified Universal type.
+For other classes the function returns the class name, in upper-case,
+followed by the tag number.
+.SS "Value adt"
+This pick adt provides the representation for values of each of the various
+Universal class types.
+Values of all other classes are represented by the
+.B Value.Octets
+branch of the pick.
+.TP
+.IB v ".tostring()"
+Returns a string representation of the
+.BR Value .
+.PP
+The following table lists
+each variant of the pick, indicating the ASN.1 Universal type values
+it represents, followed by a brief description.
+For each variant of the pick,
+.I v
+is taken to be of that particular type.
+.TP
+.B Value.Bool
+BOOLEAN
+.IP
+.IB v .v
+equals zero for FALSE, non-zero values represent TRUE.
+.TP
+.B Value.Int
+INTEGER, ENUMERATED
+.IP
+The value is given by
+.IB v .v
+.TP
+.B Value.BigInt
+Used for INTEGER values too large to fit a Limbo int.
+.IP
+The array
+.IB v .bytes
+contains the encoding of the value.
+The array does not include the tag and length prefix.
+.TP
+.B Value.Octets
+OCTET_STRING, ObjectDescriptor
+.IP
+The octet string is given by the
+.IB v .bytes
+array.
+.TP
+.B Value.Null
+NULL
+.TP
+.B Value.ObjId
+OBJECT_ID
+.IP
+The OBJECT_ID value is represented by the
+.B Oid
+adt given by
+.IB v .id .
+.TP
+.B Value.Real
+REAL
+.IP
+.B ASN1
+does not convert the value into the Limbo
+.B real
+data type.
+The encoding of the value is given by the
+.IB v .bytes
+array, which does not include the tag and length prefix.
+.TP
+.B Value.Other
+EXTERNAL, EMBEDDED_PDV and Unknown Universal types
+.IP
+The raw bytes of the value, excluding the tag nad length header,
+are given by the
+.IB v .bytes
+array.
+.TP
+.B Value.BitString
+BIT_STRING
+.IP
+The number of bits in the BIT_STRING value does not have to be
+a multiple of 8.
+Bits are packed into bytes MSB first.
+The bytes representing the BIT_STRING value, including the potentially
+incomplete last byte, are given by the
+.IB v .bits
+array.
+The number of unused bits in the last byte of the array is given by
+.IB v .unused ,
+counting from the LSB.
+.IP
+The BER constructed encoding of values other than zero-length is not implemented.
+.TP
+.B Value.EOC
+End of Contents octets marker.
+.IP
+This value is not normally returned to the application; it is used
+privately by BER to support indefinite length value encodings.
+.TP
+.B Value.String
+NumericString, PrintableString, TeletexString,
+VideotexString, IA5String, UTCTime,
+GeneralizedTime, GraphicString, VisibleString,
+GeneralString, UniversalString or BMPString.
+.IP
+The text is given by the
+.IB v .s
+Limbo string.
+Currently no character-set conversion is performed between
+the ASN.1 string byte codes and the Unicode code-points of the Limbo string.
+.TP
+.B Value.Seq
+SEQUENCE, SEQUENCE OF
+.IP
+ASN.1 assigns both constructs the same type tag.
+The difference between them is that, within the
+ASN.1 notation, the elements
+of a SEQUENCE OF structure are constrained to be of the same type.
+BER and, consequently,
+.B ASN1
+do not directly enforce the restriction.
+.IP
+The elements of the sequence are given by the
+.IB v .l
+list.
+.TP
+.B Value.Set
+SET, SET OF
+.IP
+ASN.1 assigns both constructs the same type tag.
+The difference between them is that, within the ASN.1 notation,
+SET items are formed from an unordered list of distinct types, whereas
+SET OF items are formed from an unordered list of the same type.
+BER and
+.B ASN1
+do not enforce these constraints.
+.IP
+The elements of the set are given by the
+.IB v .l
+list.
+.SS "Oid adt"
+The
+.B Oid
+adt provides the value representation for OBJECT IDENTIFERs.
+Within the ASN.1 notation OBJECT IDENTIFIERs ultimately map
+to an ordered list of INTEGERs.
+.TP
+.B Oid.nums
+The value of the OBJECT IDENTIFIER, given as an
+.BR "array of int" .
+.TP
+.IB o .tostring()
+Returns a textual representation of the OBJECT IDENTIFIER in the
+form of a `.' separated list of numbers.
+.SS Tagging
+Tagging is an ASN.1 mechanism for disambiguating values.
+It is usually applied to component types, where several components
+of a structured type have the same underlying Universal class type.
+Tagging allows the client application to determine to which item of the
+structured type a value instance belongs.
+.PP
+There are two types of tagging, implicit and explicit, defining
+the manner in which the values are encoded.
+.PP
+Implicitly tagged values are encoded in the same way as the underlying type,
+but with the tag class and number replaced by that specified.
+.PP
+Explicitly tagged values are encoded in a nested fashion.
+The outermost item bears the specified tag and its contents is the
+full encoding of the original value using the tag of its underlying type.
+.PP
+The following examples of how to decode and encode simple tagged types
+should make the distinction clear.
+.SS "Decoding Tagged Values"
+Consider the following ASN.1 type definitions:
+.PP
+.EX
+    Type1 ::= INTEGER
+    Type2 ::= [Application 2] Type1     -- Explicitly tagged
+    Type3 ::= [3] IMPLICIT Type1        -- Implicitly tagged
+
+.EE
+For each of the types
+the value 16r55 will be decoded as follows:
+.PP
+.EX
+	(error, elem) := asn1->decode(data);
+.EE
+.TP
+.BR Type1 " (primitive type)"
+.EX
+elem.tag.class == Universal
+elem.tag.num == INTEGER
+tagof elem.val == tagof Value.Int
+elem.is_int() == (1, 16r55)
+.EE
+.TP
+.BR Type2 " (explicitly tagged)"
+.EX
+elem.tag.class == Application
+elem.tag.num == 2
+tagof elem.val == tagof Value.Octets
+
+.EE
+The
+.B bytes
+array of the
+.B Value.Octets
+value contains the complete encoding of the
+.B Type1
+value.
+The actual value can be obtained as follows:
+.IP
+.EX
+pick v := elem.val {
+Octets =>
+	(err2, e2) := asn1->decode(v.bytes);
+}
+.EE
+with
+.B e2
+having exactly the same properties as
+.B elem
+in the
+.B Type1
+case above.
+.TP
+.BR Type3 " (implicitly tagged)"
+.EX
+elem.tag.class == Context
+elem.tag.num == 3
+tagof elem.val == tagof Value.Octets
+
+.EE
+In this case the
+.B bytes
+array of the
+.B Value.Octets
+value contains the encoding of just the value part of the Type1 value,
+not the complete encoding.
+The actual value can be obtained as follows:
+.IP
+.EX
+pick v := e.val {
+Octets =>
+    constr := e.tag.constr;
+    (err, val) := asn1->decode_value(v.bytes, INTEGER, constr);
+}
+
+.EE
+Note that the application has to infer the type of the value from
+the context in which it occurs.
+The resultant
+.B val
+is of the type
+.B Value.Int
+with the value 16r55 stored in the
+.B v
+member variable.
+.SS "Encoding Tagged Values"
+To encode the value 16r55 in each of the above types, the following
+data structures are required.
+.TP
+.BR Type1 "(primitive type)"
+.EX
+tag := Tag(Universal, INTEGER, 0);
+val := Value.Int(16r55);
+elem := ref Elem(tag, val);
+(err, data) := asn1->encode(elem);
+.EE
+.TP
+.BR Type2 "(explicitly tagged)"
+.EX
+tag1          := Tag(Universal, INTEGER, 0);
+val1          := Value.Int(16r55);
+elem1         := ref Elem(tag1, val1);
+(err1, data1) := asn1->encode(elem1);
+tag2          := Tag(Application, 2, 0);
+val2          := Value.Octets(data1);
+elem2         := ref Elem(tag2, val2);
+(err, data)   := asn1->encode(elem2);
+.EE
+.TP
+.BR Type3 "(implicitly tagged)"
+.EX
+tag         := Tag(Context, 3, 0);
+val         := Value.Int(16r55);
+elem        := ref Elem(tag, val);
+(err, data) := asn1->encode(elem);
+.EE
+.SH SOURCE
+.B /appl/lib/asn1.b
+.SH BUGS
+It is irritating that REAL values are not converted by the module.
+This forces the application to do the conversion to and from the
+raw BER encoding.  Fortunately they are rarely used.
+.PP
+String encodings are converted as UTF-8 byte sequences.
+This will result in strings comprising any character codes above 127
+being incorrectly converted.
+.PP
+There is a particular form of BER encoding that the module will
+handle incorrectly, resulting in a decoding error.
+The error occurs when a tagged value is encoded using the
+indefinite length specifier and the constructed representation.
--- /dev/null
+++ b/man/2/attrdb
@@ -1,0 +1,215 @@
+.TH ATTRDB 2
+.SH NAME
+attrdb \- database of attribute-value pairs
+.SH SYNOPSIS
+.EX
+include "bufio.m";
+include "attrdb.m";
+attrdb := load Attrdb Attrdb->PATH;
+
+Attr: adt {
+    attr:   string;
+    val:    string;
+    tag:    int;        # application-defined data, initially 0
+};
+
+Tuples: adt {
+    n:  int;
+    pairs:  list of ref Attr;
+
+    hasattr:    fn(t: self ref Tuples, attr: string): int;
+    haspair:    fn(t: self ref Tuples,
+                   attr: string, value: string): int;
+    find:       fn(t: self ref Tuples, attr: string): list of ref Attr;
+    findbyattr: fn(t: self ref Tuples,
+                   attr: string, value: string, rattr: string):
+                   list of ref Attr;
+};
+
+Dbentry: adt {
+    n:  int;
+    lines:  list of ref Tuples;
+
+    find:       fn(e: self ref Dbentry, attr: string):
+                  list of (ref Tuples, list of ref Attr);
+    findfirst:  fn(e: self ref Dbentry, attr: string): string;
+    findpair:   fn(e: self ref Dbentry,
+                  attr: string, value: string):
+                  list of ref Tuples;
+    findbyattr: fn(e: self ref Dbentry,
+                  attr: string, value: string, rattr: string):
+                  list of (ref Tuples, list of ref Attr);
+};
+
+Db: adt {
+    open:    fn(path: string): ref Db;
+    sopen:   fn(data: string): ref Db;
+    changed: fn(db: self ref Db): int;
+    reopen:  fn(db: self ref Db): int;
+    append:  fn(db1: self ref Db, db2: ref Db): ref Db;
+
+    find:       fn(db: self ref Db, start: ref Dbptr,
+                  attr: string): (ref Dbentry, ref Dbptr);
+    findpair:   fn(db: self ref Db, start: ref Dbptr,
+                  attr: string, value: string):
+                  (ref Dbentry, ref Dbptr);
+    findbyattr: fn(db: self ref Db, start: ref Dbptr,
+                  attr: string, value: string, rattr: string):
+                  (ref Dbentry, ref Dbptr);
+};
+
+init:   fn(): string;
+
+parseentry: fn(s: string, lno: int): (ref Dbentry, int, string);
+parseline:  fn(s: string, lno: int): (ref Tuples, string);
+.EE
+.SH DESCRIPTION
+.B Attrdb
+fetches data from textual databases that contain groups of attribute-value pairs.
+The format is defined by
+.IR attrdb (6).
+.PP
+.B Init
+must be called before any other function in the module.
+.PP
+Each logical database is represented by a
+.B Db
+value.
+It can span several physical files, named in the body of a
+.B database
+attribute in the primary file of the database.
+(If no such attribute appears, there is just the one physical file in the database.)
+.TP
+.BI Db.open( path )
+Opens
+.I path
+as a database, and
+returns a (reference to a)
+.B Db
+value that represents it.
+On an error, it returns nil and the system error string contains a diagnostic.
+If
+.I path
+contains a
+.B database
+attribute with associated attributes of the form
+.BI file= filename,
+the logical database is formed by (logically) concatenating the contents
+of each
+.I filename
+in the order listed.
+See
+.IR attrdb (6)
+for details.
+.TP
+.BI Db.sopen( data )
+Treat the contents of the string
+.I data
+as a database, and return a
+.B Db
+value representing it.
+.TP
+.IB db1 .append( db2 )
+Return a
+.B Db
+value that represents the result of logically appending
+the contents of database
+.I db2
+to
+.IR db1 .
+.TP
+.IB db .changed()
+Return true iff one or more of the underlying files have changed since they were last read.
+.TP
+.IB db .reopen()
+Discard any cached data, and reopen the database files.
+Return 0 on success but -1 if any file could not be reopened.
+.TP
+.IB db .find( ptr , attr )
+Starting at
+.IR ptr ,
+look in
+.I db
+for the next entry that contains an attribute
+.I attr
+and return a tuple
+.BI ( e , ptr )
+where
+.I e
+is a
+.B Dbentry
+value representing the whole entry, and
+.I ptr
+is a database pointer for the next entry.
+If
+.I attr
+cannot be found,
+.I e
+is nil.
+.TP
+.IB db .findpair( ptr\fP,\fP\ attr\fP,\fP\ value\fP)
+Starting at
+.IR ptr ,
+look in
+.I db
+for the next entry that contains the pair
+.IB attr = value,
+and return a tuple
+.BI ( e , ptr )
+where
+.I e
+is a
+.B Dbentry
+value representing the whole entry, and
+.I ptr
+is a database pointer for the next entry.
+If the given pair
+cannot be found,
+.I e
+is nil.
+.TP
+.IB db .findbyattr( ptr\fP,\fP\ attr\fP,\fP\ value\fP,\fP\ rattr\fP )
+Starting at
+.I ptr
+in
+.IR db ,
+look for the next entry containing both the pair
+.IB attr = value
+and a pair with attribute
+.IR rattr ;
+return a tuple
+.BI ( e , ptr )
+where
+.I e
+is a
+.B Dbentry
+value representing the whole entry, and
+.I ptr
+is a database pointer for the next entry.
+If no such entry can be found,
+.I e
+is nil.
+.PP
+.B Parseline
+takes a line containing a set of space-separated
+.IB attribute = value
+pairs, and returns a tuple
+.BI ( ts , err ) .
+If the line's syntax is correct,
+.I ts
+is a
+.B Tuples
+value that represents the pairs as a list of
+.B Attr
+values.
+If the syntax is wrong (eg, unmatched quote),
+.I ts
+is nil and
+.I err
+contains a diagnostic.
+.SH SOURCE
+.B /appl/lib/attrdb.b
+.SH SEE ALSO
+.IR cfg (2),
+.IR attrdb (6),
+.IR ndb (6)
--- /dev/null
+++ b/man/2/bloomfilter
@@ -1,0 +1,89 @@
+.TH BLOOMFILTER 2
+.SH NAME
+Bloomfilter \- Bloom filters
+.SH SYNOPSIS
+.EX
+include "sets.m";
+include "bloomfilter.m";
+bloomfilter := load Bloomfilter Bloomfilter->PATH;
+
+init:   fn();
+filter: fn(d: array of byte, logm, k: int): Sets->Set;
+.EE
+.SH DESCRIPTION
+A Bloom filter is a method of representing a set to support probabilistic
+membership queries. It uses independent hash functions of members
+of the set to set elements of a bit-vector.
+.I Init
+should be called first to initialise the module.
+.I Filter
+returns a Set
+.I s
+representing the Bloom filter for the single-member
+set
+.RI { d }.
+.I K
+independent hash functions are used, each of range
+.RI "[0, 2^" logm ),
+to return a Bloom filter
+.RI 2^ logm
+bits wide. It is an error if
+.I logm
+is less than 3 or greater than 30.
+.PP
+Bloom filters can be combined by set union.
+The set represented by Bloom filter
+.I a
+is not a subset of another
+.I b
+if there are any members in
+.I a
+that are not in
+.IR b .
+Together,
+.IR logm ,
+.IR k ,
+and
+.IR n
+(the number of members in the set)
+determine the
+.I "false positve" rate
+(the probability that a membership test will not eliminate
+a member that is not in fact in the set).
+The probability of a
+.I "false positive"
+is approximately (1-e^(-\fIkn\fP/(2^\fIlogm\fP))^\fIk\fP.
+For a given false positive rate,
+.IR f ,
+a useful formula to determine appropriate parameters
+is: \fIk\fP=ceil(-log₂(\fIf\fP)),
+and \fIlogm\fP=ceil(log₂(\fInk\fP)).
+.SH EXAMPLES
+Create a 128 bit-wide bloom filter
+.I f
+representing all the elements
+in the string array
+.IR elems ,
+with
+.IR k =6.
+.EX
+    A, B, None: import Sets;
+    for(i:=0; i<len elems; i++)
+        f = f.X(A|B, filter(array of byte elems[i], 7, 6));
+.EE
+Test whether the string
+.I s
+is a member of
+.IR f .
+If there were 12 elements in
+.IR elems ,
+the probability of a false positive would be
+approximately 0.0063.
+.EX
+    if(filter(array of byte s, 7, 6).X(A&~B, f).eq(None))
+        sys->print("'%s' might be a member of f\en", s);
+.EE
+.SH SOURCE
+.B /appl/lib/bloomfilter.b
+.SH SEE ALSO
+.IR sets (2)
--- /dev/null
+++ b/man/2/bufio
@@ -1,0 +1,318 @@
+.TH BUFIO 2
+.SH NAME
+bufio, bufiofill \- buffered input/output module
+.SH SYNOPSIS
+.EX
+include "bufio.m";
+
+bufio := load Bufio Bufio->PATH;
+Iobuf: import bufio;
+
+SEEKSTART:  con Sys->SEEKSTART;
+SEEKRELA:   con Sys->SEEKRELA;
+SEEKEND:    con Sys->SEEKEND;
+
+OREAD:      con Sys->OREAD;
+OWRITE:     con Sys->OWRITE;
+ORDWR:      con Sys->ORDWR;
+
+EOF:        con -1;
+ERROR:      con -2;
+
+Iobuf: adt {
+    seek:   fn(b: self ref Iobuf, n: big, where: int): big;
+    offset: fn(b: self ref Iobuf): big;
+
+    read:   fn(b: self ref Iobuf, a: array of byte, n: int): int;
+    write:  fn(b: self ref Iobuf, a: array of byte, n: int): int;
+
+    getb:   fn(b: self ref Iobuf): int;
+    getc:   fn(b: self ref Iobuf): int;
+    gets:   fn(b: self ref Iobuf, sepchar: int): string;
+    gett:   fn(b: self ref Iobuf, sepstring: string): string;
+
+    ungetb: fn(b: self ref Iobuf): int;
+    ungetc: fn(b: self ref Iobuf): int;
+
+    putb:   fn(b: self ref Iobuf, b: byte): int;
+    putc:   fn(b: self ref Iobuf, c: int): int;
+    puts:   fn(b: self ref Iobuf, s: string): int;
+
+    flush:  fn(b: self ref Iobuf): int;
+    close:  fn(b: self ref Iobuf);
+
+    setfill: fn(b: self ref Iobuf, f: BufioFill);
+};
+
+open:   fn(name: string, mode: int): ref Iobuf;
+create: fn(name: string, mode, perm: int): ref Iobuf;
+fopen:  fn(fd: ref Sys->FD, mode: int): ref Iobuf;
+aopen: fn(a: array of byte): ref Iobuf;
+sopen:  fn(s: string): ref Iobuf;
+
+BufioFill: module
+{
+    fill:   fn(b: ref Bufio->Iobuf): int;
+};
+.EE
+.SH DESCRIPTION
+.B Bufio
+provides an interface for buffered I/O.
+A buffer is an adt which
+is created with
+.BR open ,
+.BR fopen ,
+.BR create ,
+.B aopen
+and
+.BR sopen .
+.PP
+.B Open
+takes two parameters, a
+.I filename
+and a
+.IR mode .
+The mode must be
+one of
+.BR OREAD ,
+.BR OWRITE ,
+or
+.B ORDWR
+(also defined in the
+.B Sys
+module).
+.PP
+.B Create
+is similar, but
+creates a new file if necessary, with file permissions
+specified by
+.IR perm
+(see
+.B create
+in
+.IR sys-open (2)),
+or truncates an existing file (without changing its permissions),
+before opening it in the given
+.IR mode ,
+and returning a reference to an
+.B Iobuf
+instance.
+.PP
+Buffered I/O on an already open file is made possible using
+.BR "fopen" ,
+which takes a file descriptor
+.I fd
+and an open
+.IR mode ,
+which must be compatible with the mode of the file descriptor.
+.PP
+The file open functions return a
+.B ref
+.B Iobuf
+to be used in subsequent calls.  Thus:
+.PP
+.EX
+	lc := bufio->open("/net/tcp/0/local", bufio->OREAD);
+	addr := lc.gets('\en');
+	lc = nil;
+.EE
+.PP
+will open the file
+.B /net/tcp/0/local
+and read a line (including the terminating newline
+character) from this file to initialize the string variable
+.BR addr .
+The file is closed implicitly by discarding (assigning
+.B nil
+to) the only
+reference to its
+.BR Iobuf .
+.PP
+The function
+.B aopen
+makes the contents of an array of byte
+.I a
+readable through an Iobuf (it may not be written).
+The function
+.B sopen
+similarly makes the contents of a string
+.I s
+readable.
+.PP
+Processes can share the same instance of
+.B Bufio
+and safely open and close different files concurrently, but two processes must not access the same
+.B Iobuf
+concurrently; they must coordinate their access using some external mechanism
+(eg,
+.IR lock (2)).
+.PP
+Each output file must be flushed or closed individually (see
+.B flush
+and
+.B close
+operations below).
+.PP
+The calls implemented by
+.B Iobuf
+are:
+.PP
+.TF setfill
+.PD
+.TP
+.BR seek , \ read ", and " write
+Each has the same parameters as its complement in
+.B Sys
+(see
+.IR sys-seek (2),
+.IR sys-read (2)).
+Note that
+.BR SEEKSTART
+etc. are defined by
+.B Bufio
+as well as by
+.BR Sys ,
+for use by
+.BR seek .
+.TP
+.B offset
+Return the current file offset in bytes, taking account of buffered data.
+.TP
+.B getb
+Read a single byte from the buffered stream and return its value
+as an
+.BR int .
+.TP
+.B getc
+Read a single Unicode character, encoded in UTF
+(see
+.IR utf (6)),
+and
+return its value as an
+.BR int .
+.TP
+.B gets
+Read a line, up to and including a character specified by
+.IR sepchar ,
+typically a newline.
+If none is found, read to the end of the file.
+The returned string includes the terminating character.
+.TP
+.B gett
+Read characters until one of the characters in
+.IR sepstring .
+The returned string includes the separator.
+If none of the separator characters is found,
+read to the end of the file.
+.TP
+.BR ungetb , \ ungetc
+Undoes the effect of the last
+.B getb
+or
+.BR getc ,
+so that a subsequent read will reread the byte
+.RB ( ungetb ),
+or reread the byte(s) of a UTF-encoded character
+.RB ( ungetc )
+.TP
+.BR putb , \ putc ", and " puts
+Each
+writes its argument, a byte, a character, or
+a string, respectively.
+Text is encoded in UTF.
+.TP
+.B setfill
+Associates a
+.B BufioFill
+module instance
+.I f
+with
+.B Iobuf
+.IR b ;
+discussed below.
+.TP
+.B flush
+Flush remaining data in the buffer, if necessary.
+For files opened for writing, data is flushed to the file.
+For files opened for reading, any internally buffered data is discarded,
+and the next read will read from the file.
+.TP
+.B close
+Flush remaining data in the buffer, if necessary, close the
+associated file, and discard buffers associated with the file.
+After close, no further method calls are allowed on the
+.B iobuf
+adt.
+.PP
+The
+.B BufioFill
+module interface can be ignored by most applications.
+It allows an
+.B Iobuf
+to be used to read data from an arbitrary source.
+There is no `standard' implementation to load.
+Instead,
+an application using this interface uses a separate
+.B BufioFill
+module instance such as
+.IR bufio-chanfill (2),
+or provides one itself using
+.IR sys-self (2).
+The resulting module reference is associated,
+using
+.BR setfill ,
+with an
+.B Iobuf
+previously obtained by
+.BR sopen
+(the string parameter limits the buffer space allocated).
+It is up to the
+.B BufioFill
+module's implementation how its
+.B fill
+function replenishes the buffer;
+it should return the number of bytes now in the buffer, or
+.BR Bufio->EOF .
+.SH SOURCE
+.B /appl/lib/bufio.b
+.SH SEE ALSO
+.IR bufio-chanfill (2),
+.IR intro (2),
+.IR sys-open (2),
+.IR sys-read (2),
+.IR sys-seek (2)
+.SH DIAGNOSTICS
+Calls that return a
+.B ref
+type
+(eg.
+.RB open ,
+.BR fopen ,
+.BR gets ,
+and
+.BR gett )
+return
+.B nil
+when encountering end of file or errors.  When an error occurs, the
+error string, printable with the
+.B %r format,
+will usually be set as a consequence of an error in the underlying
+.B Sys
+module.
+The other calls return
+.B EOF
+upon encountering end of file, and
+.B ERROR
+when encountering other errors.
+.SH BUGS
+A given
+.B Iobuf
+instance may not be accessed concurrently.
+.PP
+An
+.B Iobuf
+instance must be manipulated by the same module instance that created it.
+.PP
+The
+.B BufioFill
+interface is subject to change.
--- /dev/null
+++ b/man/2/bufio-chanfill
@@ -1,0 +1,57 @@
+.TH BUFIO-CHANFILL 2
+.SH NAME
+bufio: chanfill \- buffered I/O interface to named channel
+.SH SYNOPSIS
+.EX
+include "bufio.m";
+chanfill := load ChanFill ChanFill->PATH;
+
+init:   fn(data: array of byte, fid: int,
+            wc: Sys->Rwrite, r: ref Sys->FileIO,
+            b: Bufio): ref Bufio->Iobuf;
+fill:   fn(b: ref Bufio->Iobuf): int;
+.EE
+.SH DESCRIPTION
+.B ChanFill
+is an implementation of
+.B BufioFill
+(see
+.IR bufio (2))
+that refills an
+.B Iobuf
+as data is written to a file created by
+.IR sys-file2chan (2),
+which is allowed only one writer.
+.B Init
+returns an
+.B Iobuf
+allocated from the
+.B Bufio
+instance
+.IR b ,
+that when read will return data written to the file by another process.
+.IR Data ,
+.IR fid
+and
+.IR wc
+are the values in the tuple presented by
+.B Sys->file2chan
+on the first write.
+.I Data
+becomes the initial data for the
+.BR Iobuf ;
+.B init
+replies to the writer on
+.IR wc .
+The other values are saved for use by
+.BR fill :
+on later calls to
+by
+.B Bufio
+to refill the buffer,
+.B fill
+waits for a new write request on
+.IB fio .write
+and either fills the buffer or signals end-of-file appropriately.
+.SH SEE ALSO
+.IR bufio (2)
--- /dev/null
+++ b/man/2/cfg
@@ -1,0 +1,159 @@
+.TH CFG 2
+.SH NAME
+Cfg, Record, Tuple, Attr \- configuration file parser
+.SH SYNOPSIS
+.EX
+include "cfg.m";
+cfg := load Cfg Cfg->PATH;
+
+Attr: adt {
+	name: string;
+	value: string;
+};
+
+Tuple: adt {
+	lnum: int;
+	attrs: list of Attr;
+	lookup: fn(t: self ref Tuple, name: string): string;
+};
+
+Record: adt {
+	tuples: list of ref Tuple;
+	lookup: fn(r: self ref Record, name: string)
+		: (string, ref Tuple);
+};
+
+init: fn(path: string): string;
+lookup: fn(name: string): list of (string, ref Record);
+getkeys: fn(): list of string;
+
+.EE
+.SH DESCRIPTION
+.B Cfg
+parses its configuration file format into a set of
+.BR Record s.
+.PP
+Each line of the configuration file is comprised of a tuple of attributes.
+Comments are introduced by the
+.RB ' # '
+character and run to the end of the input line.
+Empty lines and comments are ignored.
+.PP
+An attribute has a name followed by an optional value.
+The value is specified by separating the name and value
+by an
+.RB ' = '
+character.
+Attribute names and values are input
+.IR words .
+A word is delimited by spacing characters and the
+.RB ' = '
+character.
+If a word needs to include any of these characters then the word may be
+quoted using single or double quotes.
+The start and end quotes must be the same.
+The quoting character may be included in the word by appearing twice.
+.PP
+Examples:
+.EX
+	'a b c'	yields	a b c
+	"a b c"	yields	a b c
+	'a " c'	yields	a " c
+	'a '' c'	yields	a ' c
+.EE
+.PP
+The name of the first attribute of a tuple is its
+.IR key .
+The
+.I primary tuple value
+is the value of its first attribute.
+.PP
+Tuples whose first attribute name appears at the start of a line (having no
+preceeding spacing characters)
+are treated as the start of a new record.
+A record incorporates all tuples up to the start of the next record.
+The
+.I record key
+is defined to be the name of its first attribute.
+The
+.I primary record value
+is the value of the first attribute.
+.PP
+The
+.B adt
+types
+.BR Attr ,
+.B Tuple
+and
+.B Record
+are direct analogues of the constructs defined above.
+.P
+.TP
+.BI init( path )
+.B Init
+initialises the
+.B Cfg
+module, causing it to open and parse the configuration file
+given by the
+.I path
+argument.
+If an error is encountered in processing the file then
+an error string is returned.
+If there are no errors
+.B init
+returns
+.BR nil .
+.TP
+.BI lookup( name )
+.B Lookup
+returns
+the set of
+.BR Record s
+whose
+.I key
+matches the
+.I name
+argument.
+The return value is a list of
+.RI ( "primary record value" , " record" )
+pairs.
+.TP
+.B getkeys()
+.B Getkeys
+returns a list of the record keys that appear in the configuration file.
+Note that more than one record can have the same key.
+Duplicate key names are not returned by
+.BR getkeys() .
+.TP
+.IB record .lookup( name )
+Returns the first tuple in
+.I record
+whose key matches
+.IR name .
+The return value is
+.RI ( "primary tuple value" , " tuple" ).
+If no matching tuple is found then the value
+.B (nil, nil)
+is returned.
+Note that more than one tuple of the record could have a
+.I key
+that matches
+.IR name .
+Only the first matching tuple is returned.
+If an application makes use of
+multiple tuples with the same
+.I key
+then the
+.IB record .tuple
+list will have to be handled explicitly by the application.
+.TP
+.IB tuple .lookup( name )
+Returns the first attribute in
+.I tuple
+whose name matches
+.IR name .
+The return value is the value of the attribute or
+.B nil
+if no matching attribute was found.
+.SH SOURCE
+.B /appl/lib/cfg.b
--- /dev/null
+++ b/man/2/command
@@ -1,0 +1,131 @@
+.TH COMMAND 2
+.SH NAME
+command \- command interface
+.SH SYNOPSIS
+.nf
+.B
+include "sh.m";
+.BI "cmd := load Command" " path" ;
+
+.EX
+PATH: con "/dis/sh.dis";
+init: fn(ctxt: ref Draw->Context, args: list of string);
+.EE
+.SH DESCRIPTION
+.B Command
+defines the module interface for programs started by the Inferno shells
+.IR sh (1)
+and
+.IR mash (1),
+and the window manager
+.IR wm (1).
+Applications to be run as commands must adhere to it,
+and any application wishing to start another command may use it.
+.PP
+Every command must have an
+.B init
+function with the signature shown above.
+Note that Limbo rules allow a module to expose a larger interface for use by
+other applications (see for instance
+.IR sh (2));
+provided it includes the form of
+.B init
+shown above,
+the module can also be invoked as a command.
+.PP
+.B Ctxt
+provides the graphics context for a windowing application,
+which typically passes it to
+.IR wmlib (2)
+(eg, to
+.IR titlebar )
+or directly to
+.IR tk (2).
+It is
+.B nil
+for commands started by the shells.
+.PP
+The arguments to the command are passed as a simple list of strings,
+.BR args .
+By convention, the name of the command or the name of its
+.B .dis
+file heads the list.
+.PP
+.B PATH
+names the file containing
+.IR sh (1),
+(on small systems, this might actually name an
+instance of
+.IR tiny (1))
+but usually the path name of
+another command will be given to the Limbo
+.B load
+operator:
+.IP
+.EX
+include "sh.m";
+.EE
+\&...
+.IP
+.EX
+cmd := load Command "/dis/date.dis";
+cmd->init(nil, "date" :: nil);
+.EE
+.PP
+In practice more care must be taken when invoking programs.
+In the example above, the current process executes the body of
+.B cmd->init
+and if that executes
+.BR exit ,
+raises an exception,
+or otherwise modifies the state of the process,
+the caller is affected.
+The following is more prudent:
+.IP
+.EX
+.ta 6n +6n
+child(file: string, args: list of string, pidc: chan of int)
+{
+	pidc <-= sys->pctl(Sys->NEWFD|Sys->FORKNS|Sys->NEWPGRP,
+		list of {0, 1, 2});
+	cmd := load Command file;
+	if(cmd == nil){
+		sys->print("can't load %s: %r\en", file);
+		exit;
+	}
+	cmd->init(nil, args);
+}
+parent()
+{
+	pidc := chan of int;
+	spawn child(disfile, args, pidc);
+	pid := <-pidc;
+	\&...
+}
+.EE
+.PP
+A new
+.B child
+process runs the command only after using
+.IR sys-pctl
+to insulate the caller from untoward changes to its environment.
+Note the idiomatic use of a channel to return the child's process ID
+to its parent, which can then if desired use the
+.B wait
+file of
+.IR prog (3)
+to watch over the child and wait for its demise.
+or use the
+.B ctl
+file of
+.IR prog (3)
+to dispose of it.
+Furthermore, any state shared between parent and child can safely
+be accessed by the child before it sends the ID
+because the parent is blocked on the receive.
+.SH SEE ALSO
+.IR mash (1),
+.IR sh (1),
+.IR wm (1),
+.IR sh (2),
+.IR sys-pctl (2)
--- /dev/null
+++ b/man/2/complete
@@ -1,0 +1,83 @@
+.TH COMPLETE 2
+.SH NAME
+complete \- file name completion
+.SH SYNOPSIS
+.EX
+include "complete.m";
+complete := load Complete Complete->PATH;
+
+Completion: adt {
+    advance:  int;    # whether forward progress has been made
+    complete: int;    # whether the completion now represents a file or directory
+    str:      string; # string to advance, suffixed " " (file) or "/" (directory)
+    nmatch:   int;    # number of files that matched
+    filename: array of string;    # their names
+};
+
+init:     fn();
+complete: fn(dir, s: string): (ref Completion, string);
+.EE
+.SH DESCRIPTION
+.B Complete
+implements file name completion.
+.PP
+.B Init
+must be called before any other operation of the module.
+.PP
+Given a directory
+.I dir
+and a string
+.IR s ,
+.B complete
+returns a tuple
+.BI ( c,\ err ),
+where
+.I c
+is an analysis of the file names in that directory that begin with the string
+.IR s .
+The field
+.B nmatch
+will be set to the number of files that match the prefix and
+.B filename
+will be filled in with their names.
+If the file named is a directory, a slash character will be appended to it.
+On an error,
+.I c
+is nil and
+.I err
+is a diagnostic string.
+.PP
+If no files match the string,
+.B nmatch
+will be zero, but
+.I complete
+will return the full set of files in the directory.
+.PP
+The flag
+.B advance
+reports whether the string
+.I s
+can be extended without changing the set of files that match.  If true,
+.B str
+will be set to the extension; that is, the value of
+.B str
+may be appended to
+.I s
+by the caller to extend the embryonic file name unambiguously.
+.PP
+The flag
+.B complete
+reports whether the extended file name uniquely identifies a file.
+If true,
+.B str
+will be suffixed with a blank, or a slash and a blank,
+depending on whether the resulting file name identifies a plain file or a directory.
+.SH SOURCE
+.B /appl/lib/complete.b
+.SH SEE ALSO
+.IR names (2)
+.SH DIAGNOSTICS
+The
+.I complete
+function returns a nil reference and a diagnostic string
+if the directory is unreadable or there is some other error.
--- /dev/null
+++ b/man/2/convcs
@@ -1,0 +1,367 @@
+.TH CONVCS 2
+.SH NAME
+Convcs,  Btos, Stob \- character set conversion suite
+.SH SYNOPSIS
+.EX
+include "convcs.m";
+convcs := load Convcs Convcs->PATH;
+
+Btos: module {
+	init: fn(arg: string): string;
+	btos: fn(s: Convcs->State, b: array of byte, nchars: int)
+			: (Convcs->State, string, int);
+};
+
+Stob: module {
+	init: fn (arg: string): string;
+	stob: fn(s: Convcs->State, str: string)
+			: (Convcs->State, array of byte);
+};
+
+Convcs: module {
+	State: type string;
+	Startstate: con "";
+
+	init: fn(csfile: string): string;
+	getbtos: fn(cs: string): (Btos, string);
+	getstob: fn(cs: string): (Stob, string);
+	enumcs: fn(): list of (string, string, int);
+	aliases: fn(cs: string): (string, list of string);
+};
+
+.EE
+.SH DESCRIPTION
+The
+.I Convcs
+suite is a collection of
+modules for converting various standard
+coded character sets and character encoding schemes
+to and from the Limbo strings.
+.PP
+The
+.B Convcs
+module provides an entry point to the suite, mapping character set names and aliases
+to their associated 
+converter implementation.
+.SS "The Convcs module"
+.TP
+.BI init( csfile )
+.B Init
+should be called once to initialise the internal state of
+.BR Convcs .
+The
+.I csfile
+argument specifies the path of the converter mapping file.
+If this argument is nil, the default mapping file
+.B /lib/convcs/charsets
+is used.
+.TP
+.BI getbtos( cs )
+.B Getbtos
+returns an initialised
+.B Btos
+module for converting from the requested encoding.
+.IP
+The return value is a tuple, holding the module reference and an error string.
+If any errors were encountered in locating, loading or initialising the requested
+converter, the module reference will be nil and the string will contain an explanation.
+.IP
+The character set name,
+.IR cs ,
+is normalised by mapping all upper-case latin1 characters to lower-case before
+comparison with character set names and aliases from the
+.B charsets
+file.
+.TP
+.BI getstob( cs )
+.B Getstob
+returns an initialised
+.B Stob
+module for converting from strings to the requested encoding.
+Apart from the different module type, the return value and semantics are
+the same as for
+.BR getbtos() .
+.TP
+.B enumcs()
+Returns a list describing the set of available converters.
+The list is comprised of
+.RI ( name ,
+.IR desc ,
+.IR mode )
+tuples.
+.IP
+.I Name
+is the standard name of the character set.
+.IP
+.I Desc
+is a more friendly description taken directly from the
+.B desc
+attribute given in the
+.I charsets
+file.
+If the attribute is not given then
+.I desc
+is set to
+.IR name .
+.IP
+.I Mode
+is a bitmap that details the converters available for the given character set.
+Valid
+.I mode
+bits are
+.B Convcs->BTOS
+and
+.BR Convcs->STOB .
+For convenience,
+.B Convcs->BOTH
+is also defined.
+.TP
+.BI aliases( cs )
+Returns the aliases for the character set name
+.IR cs .
+The return value is the tuple
+.BI ( desc ", " aliases ).
+.IP
+.I Desc
+is the descriptive text for the character set, as returned by
+.BR enumcs() .
+.IP
+.I Aliases
+lists all the known aliases for
+.IR cs .
+The first name in the list is the default character set name \- the name
+that all the other aliases refer to.
+If the
+.I aliases
+list is
+.B nil
+then there was an error in looking up the character set name and
+.I desc
+will detail the error.
+.SS "Using a Btos converter"
+The
+.B Btos
+module returned by
+.B getbtos()
+is already initialised and is ready to start the conversion.
+Conversions can be made on a individual basis,
+or in a `streamed' mode.
+.PP
+.IB converter "->btos(" s ,
+.IB b ,
+.IB nchars )
+.RS
+Converts raw byte codes of the character set encoding to a Limbo string.
+.PP
+The argument
+.I s
+is a converter state as returned from the previous call to
+.B btos
+on the same input stream.
+The first call to
+.B btos
+on a particular input stream should give
+.B Convcs->Startstate
+(or
+.BR nil)
+as the value for
+.IR s .
+The argument
+.I b
+is the bytes to be converted.
+The argument
+.I nchars
+is the maximal length of the string to be returned.
+If this argument is
+.B -1
+then as much of
+.I b
+will be consumed as possible.
+A value of
+.B 0
+indicates to the converter that there is no more data and
+that any pending state should be flushed.
+.PP
+The return value of
+.B btos
+is the tuple
+.RI ( state ,
+.IR str ,
+.IR nbytes )
+where
+.I state
+is the new state of the converter,
+.I str
+is the converted string, and
+.I nbytes
+is the number of bytes from
+.I b
+consumed by the conversion.
+.PP
+The same converter module can be used for multiple conversion streams
+by maintaining a separate
+.I state
+variable for each stream.
+.RE
+.SS "Using an Stob converter"
+The
+.B Stob
+module returned by
+.B getstob()
+is already initialised and is ready to start the conversion.
+.PP
+.IB converter "->stob(" s ,
+.IB str )
+.RS
+Converts the limbo string
+.I str
+to the raw byte codes of the character set encoding.
+The argument
+.I s
+represents the converter state and is treated in the same way as for
+.IB converter ->btos()
+described above.
+To terminate a conversion
+.B stob
+should be called with an empty string in order to flush
+the converter state.
+.PP
+The return value of
+.B stob
+is the tuple
+.RI ( state ,
+.IR bytes )
+where
+.I state
+is the new state of the converter and
+.I bytes
+is the result of the conversion.
+.RE
+.SS "Conversion errors"
+When using 
+.IB converter "->btos()"
+to convert data to Limbo strings,
+any byte sequences that are not valid for the specific character encoding scheme
+will be converted to the Unicode error character 16rFFFD. 
+.PP
+When using
+.IB converter "->stob()"
+to convert Limbo strings,
+any Unicode characters that can not be mapped
+into the character set will normally be substituted by the US-ASCII code for `?'.
+Note that this may be inappropriate for certain conversions, such converters will use
+a suitable error character for their particular character set and encoding scheme.
+.SS "Charset file format"
+The file
+.B /lib/convcs/charsets
+provides the mapping between character set names and their implementation modules.
+The file format conforms to that supported by
+.IR cfg (2).
+The following description relies on terms defined in the
+.IR cfg (2)
+manual page.
+.PP
+Each record name defines a character set name.
+If the primary value of the record is non-empty then the name is an alias,
+the value being the real name.
+An alias record must point to an actual converter record, not to another alias, as
+.I Convcs
+only follows one level of aliasing.
+.PP
+Each converter record consists of a set of tuples with the following primary attributes:
+.TP
+.B desc
+A more descriptive name for the character set encoding.
+This attribute is used for the description returned by
+.BR enumcs() .
+.TP
+.B btos
+The path to the
+.B Btos
+module implementation of this converter.
+.TP
+.B stob
+The path to the
+.B Stob
+module implementation of this converter.
+.PP
+Both the
+.B btos
+and
+.B stob
+tuples can have an optional
+.B arg
+attribute which is passed to the
+.B init()
+function of the converter when initialised by
+.IR Convcs .
+If a converter record has neither an
+.B stob
+nor a
+.B btos
+tuple, then it is ignored.
+.PP
+The following example is an extract from the standard Inferno
+.I charsets
+file:
+.PP
+.EX
+cp866=ibm866
+866=ibm866
+ibm866=
+    desc='Russian MS-DOS CP 866'
+    stob=/dis/lib/convcs/cp_stob.dis    arg=/lib/convcs/ibm866.cp
+    btos=/dis/lib/convcs/cp_btos.dis    arg=/lib/convcs/ibm866.cp
+.EE
+.PP
+This entry defines
+.I Stob
+and
+.I Btos
+converters for the character set called
+.BR ibm866 .
+The converters are actually the generic codepage converters
+.B cp_stob
+and
+.B cp_btos
+paramaterized with a codepage file.
+The entry also defines the aliases
+.B cp866
+and
+.B 866
+for the name
+.BR ibm866 .
+.SH FILES
+.TP
+.B /lib/convcs/charsets
+The default mapping between character set names and their implementation modules.
+.TP
+.BI /lib/convcs/ csname .cp
+Codepage files for use by the generic codepage converters
+.B cp_stob
+and
+.BR cp_btos .
+Each file consists of 256 unicode runes mapping codepage byte values to unicode by their index.
+The runes are stored in the utf-8 encoding.
+.SH SOURCE
+.TF /appl/lib/convcs/convcs.b
+.TP
+.B /appl/lib/convcs/convcs.b
+Implementation of the
+.B Convcs
+module.
+.TP
+.B /appl/lib/convcs/cp_btos.b
+Generic
+.I Btos
+codepage converter.
+.TP
+.B /appl/lib/convcs/cp_stob.b
+Generic
+.I Stob
+codepage converter.
+.br
+.PP
+.SH SEE ALSO
+.IR tcs (1),
+.IR cfg (2)
--- /dev/null
+++ b/man/2/crc
@@ -1,0 +1,62 @@
+.TH CRC 2
+.SH NAME
+crc \- Crc module
+.SH SYNOPSIS
+.EX
+include "crc.m";
+crc := load Crc Crc->PATH;
+
+CRCstate: adt {
+	crc: int;
+	crctab: array of int;
+	reg: int;
+};
+
+init: fn(poly: int, reg: int): ref CRCstate;
+crc: fn(state: ref CRCstate, buf: array of byte, nb: int): int;
+reset: fn(state: ref CRCstate);
+.EE
+.SH DESCRIPTION
+.B Crc
+provides the routines to calculate the CRC (cyclic redundancy
+check) over blocks of data.
+.PP
+.B Init
+initializes the module and must be called first. The parameter
+.I poly
+is the polynomial to use when calculating the CRC value. If a value of
+0 is given, the default polynomial 16redb88320 (8r035556101440) is used. The polynomial
+has its implicit top bit set. The second parameter
+.I reg
+is the number with which to initialize the CRC register. This is commonly 0 but, for example, is
+16rffffffff in the CRC32 algorithm. The final CRC value is also XORed with this number.
+The function
+returns a pointer to an adt that holds the current CRC value,
+the auxiliary table the algorithm uses and the initial register value. These fields should not be accessed
+directly - they are only for internal use.
+.PP
+.B Crc
+calculates the CRC value of the first
+.I nb
+bytes of the array
+.I buf
+given the CRC state
+.I state
+as returned by the 
+.I init 
+function. It returns the current CRC value. It may be called repeatedly
+to calculate the CRC of a series of arrays of bytes, for example, when
+calculating the CRC value for the bytes in a file.
+.PP
+.B Reset
+sets the CRC state to its initial value in readiness for a new CRC
+calculation. It avoids the need to call
+.I init
+again.
+.SH SOURCE
+.B /appl/lib/crc.b
+.SH SEE ALSO
+.IR sum (1)
+
+
+
--- /dev/null
+++ b/man/2/crypt-0intro
@@ -1,0 +1,98 @@
+.TH CRYPT-INTRO 2
+.SH NAME
+Crypt intro \- introduction to the
+.B Crypt
+cryptography
+module
+.SH SYNOPSIS
+.EX
+include "ipints.m";
+ipints := load IPints IPints->PATH;
+IPint: import ipints;
+
+include "crypt.m";
+crypt := load Crypt Crypt->PATH;
+.EE
+.SH DESCRIPTION
+.B Crypt
+contains a mixed set of functions that variously:
+.IP \(bu
+form cryptographically secure digests; see
+.IR crypt-sha1 (2)
+.IP \(bu
+generate public/private key pairs; see
+.IR crypt-gensk (2)
+.IP \(bu
+encrypt data, using AES, DES, or IDEA; see
+.IR crypt-crypt (2)
+.IP \(bu
+create and verify cryptographic signatures using the
+public keys; see
+.IR crypt-sign (2)
+.SS "Public Key Cryptography"
+Public key cryptography has many uses.
+Inferno relies on it only for digital signatures.
+The private key may be used to digitally
+sign data, the public one to verify the signature.
+.PP
+Inferno provides three data types to represent the different components of the public key signature scheme.
+The
+.B PK
+adt contains the data necessary to construct a public key;
+the
+.B SK
+adt contains the data necessary to construct a secret key.
+A key contains the public or secret parameters for the signature algorithm specified by the adt's pick tag.
+Ownership of a key is not recorded in the key value itself but in a separate certificate.
+Finally,
+the
+.B PKsig
+adt contains one or more values representing a given form of digital signature.
+.PP
+Certificates and indeed signature representations are varied, and implemented by other modules.
+.SS "Large Precision Arithmetic"
+Many
+.B Crypt
+operations require integers much larger than
+.B int
+or
+.BR big .
+It therefore uses the multiple-precision package
+.IR ipints (2).
+That module's
+.B IPint
+adt
+stands for infinite precision integer, though, for
+space considerations, our
+implementation limits the maximum integer to
+2\u\s-2\&8192\s0\d-1.
+.PP
+An
+.B IPint
+can be converted into two external formats.
+The first is
+an array of bytes in which the first byte is the highest order
+byte of the integer.  This format is useful when
+communicating with the
+.IR ssl (3)
+device.
+The second is similar but represents the array of bytes as text, using either base 16 or a MIME base 64 format,
+allowing
+.BR IPint s
+to be stored in files or transmitted across
+networks in a human readable form.
+.SH SOURCE
+.br
+.B /libinterp/crypt.c
+.br
+.B /libinterp/ipint.c
+.br
+.B /libmp
+.br
+.B /libsec
+.SH SEE ALSO
+.IR security-intro (2)
+.br
+B. Schneier,
+.IR "Applied Cryptography" ,
+1996, J. Wiley & Sons, Inc.
--- /dev/null
+++ b/man/2/crypt-crypt
@@ -1,0 +1,139 @@
+.TH CRYPT-CRYPT 2
+.SH NAME
+crypt: aessetup, aescbc, dessetup, descbc, desecb, ideasetup, ideacbc, ideaecb \- data encryption
+.SH SYNOPSIS
+.EX
+include "ipints.m";
+include "crypt.m";
+crypt := load Crypt Crypt->PATH;
+
+Encrypt:   con 0;
+Decrypt:   con 1;
+
+AESbsize:  con 16;
+
+aessetup:  fn(key: array of byte, ivec: array of byte): ref AESstate;
+aescbc:    fn(state: ref AESstate, buf: array of byte,
+             n: int, direction: int);
+
+BFbsize: con 8;
+
+blowfishsetup: fn(key: array of byte, ivec: array of byte): ref BFstate;
+blowfishcbc:   fn(state: ref BFstate, buf: array of byte,
+             n: int, direction: int);
+
+DESbsize:  con 8;
+
+dessetup:  fn(key: array of byte, ivec: array of byte): ref DESstate;
+descbc:    fn(state: ref DESstate, buf: array of byte,
+             n: int, direction: int);
+desecb:    fn(state: ref DESstate, buf: array of byte,
+             n: int, direction: int);
+
+IDEAbsize: con 8;
+
+ideasetup: fn(key: array of byte, ivec: array of byte): ref IDEAstate;
+ideacbc:   fn(state: ref IDEAstate, buf: array of byte,
+             n: int, direction: int);
+ideaecb:   fn(state: ref IDEAstate, buf: array of byte,
+             n: int, direction: int);
+.EE
+.SH DESCRIPTION
+These functions encrypt and decrypt blocks of data using different
+encryption algorithms.
+The interfaces are similar.
+.PP
+Each algorithm has an adt that holds the current state for a given encryption.
+It is produced by the setup function for the algorithm,
+.IB alg setup ,
+which is given a secret
+.I key
+and an initialisation vector
+.IR ivec .
+A sequence of blocks of data can then be encrypted or decrypted by repeatedly calling
+.IB alg cbc
+(for `cipher block chaining'), or
+.IB alg ebc
+(the less secure `electronic code book', if provided).
+On each call,
+.I buf
+provides
+.I n
+bytes of the data to encrypt or decrypt.
+.I N
+must be a multiple of the encryption block size
+.IB ALG bsize .
+Exceptionally,
+.B aescbc
+allows
+.I n
+to be other than a multiple of
+.B AESbsize
+in length, but then
+for successful decryption, the decryptor must use the same
+sequence of buffer sizes as the encryptor.
+.I Direction
+is the constant
+.B Encrypt
+or
+.B Decrypt
+as required.
+.I State
+maintains the encryption state, initially produced by the setup function,
+and updated as each buffer is encrypted or decrypted.
+.PP
+The algorithms currently available are:
+.TP
+.B aes
+The Advanced Encryption Standard, AES (also known as Rijndael).
+The
+.I key
+should be 16, 24 or 32 bytes long (128, 192 or 256 bits).
+.I Ivec
+should be
+.B AESbsize
+bytes of random data: random enough to be unlikely to be reused but
+not cryptographically strongly unpredictable.
+.TP
+.B blowfish
+Bruce Schneier's symmetric block cipher.
+The
+.I key
+is any length from 4 to 56 bytes.
+.I Ivec
+if non-nil is
+.B BFbsize
+bytes of random data.
+For
+.BR blowfishcbc ,
+.I n
+must be a multiple of
+.BR BFbsize .
+.TP
+.B des
+The older Data Encryption Standard, DES.
+.I Key
+is 8 bytes (64 bits), containing a 56-bit key
+encoded into 64 bits where every eighth bit is parity.
+.I Ivec
+is
+.B DESbsize
+bytes of random data.
+.TP
+.B idea
+The International Data Encryption Standard, IDEA™.
+The
+.I key
+is 16 bytes long (128 bits).
+.I Ivec
+is
+.B IDEAbsize
+bytes of random data.
+.SH SEE ALSO
+.IR crypt-intro (2),
+.IR crypt-rc4 (2),
+.IR security-random (2)
+.PP
+IDEA was patented by Ascom-Tech AG (EP 0 482 154 B1, US005214703),
+currently held by iT_SEC Systec Ltd.
+The patent expired in 2012.
--- /dev/null
+++ b/man/2/crypt-dsagen
@@ -1,0 +1,94 @@
+.TH CRYPT-DSAGEN 2
+.SH NAME
+crypt: dsagen, eggen, rsagen, rsafill,  rsaencrypt, rsadecrypt \- specific public key systems
+.SH SYNOPSIS
+.EX
+include "ipints.m";
+ipints := load IPints IPints->PATH;
+IPint: import ipints;
+
+include "crypt.m";
+crypt := load Crypt Crypt->PATH;
+
+dsagen:     fn(oldpk: ref PK.DSA): ref SK.DSA;
+
+eggen:      fn(nlen: int, nrep: int): ref SK.Elgamal;
+
+rsagen:     fn(nlen: int, elen: int, nrep: int): ref SK.RSA;
+rsafill:    fn(n: ref IPint, ek: ref IPint, dk: ref IPint,
+               p: ref IPint, q: ref IPint): ref SK.RSA;
+rsaencrypt: fn(k: ref PK.RSA, m: ref IPint): ref IPint;
+rsadecrypt: fn(k: ref SK.RSA, m: ref IPint): ref IPint;
+.EE
+.SH DESCRIPTION
+.IR Crypt-gensk (2)
+describes a set of functions that generate public/private key pairs given an algorithm name
+and a key length.
+Some key types allow further parameters for key generation or support further operations.
+.PP
+.B Dsagen
+generates a DSA public/private key pair, represented by the pick adt
+.BR SK.DSA ,
+and compatible with the containing type
+.BR SK .
+If the parameter
+.B oldpk
+is not nil,
+.B dsagen
+takes the new key's modulus and group order from the existing key;
+otherwise it generates a new pair of primes.
+.PP
+.B Eggen
+generates a new El-Gamal key pair, represented by the pick adt
+.BR SK.Elgamal .
+.I Nlen
+is the length of the modulus;
+.I nrep
+is the number of repetitions of the Miller-Rabin primality test (0 gives the default, currently 18).
+.PP
+.B Rsagen
+generates an RSA public/private key pair, represented by the pick adt
+.BR SK.RSA ,
+and compatible with the containing type
+.BR SK .
+.I Nlen
+gives the length of the key modulus in bits;
+.I elen
+gives the exponent length in bits; and
+.I nrep
+is as above.
+.PP
+The RSA private key representation used by Inferno includes some extra values to speed computation.
+.B Rsagen
+provides those values but keys imported from other systems might not.
+Given the essential set of RSA private key parameters for a given key, represented as IPints,
+.B rsafill
+returns a suitable
+.B SK.RSA
+for that key, including the extra values.
+.PP
+The public key of type
+.B PK.RSA
+can be extracted from a given private key value
+.I sk
+by referencing the field
+.BI sk .pk .
+.PP
+.B Rsaencrypt
+encrypts a message
+.IR m ,
+represented by an IPint,
+using the public key
+.IR pk .
+.PP
+.B Rsadecrypt
+decrypts
+.I m
+using private key
+.IR sk .
+The result is again returned as an IPint.
+.SH SEE ALSO
+.IR crypt-gensk (2),
+.IR crypt-sha1 (2),
+.IR security-auth (2),
+.IR security-oldauth (2)
--- /dev/null
+++ b/man/2/crypt-gensk
@@ -1,0 +1,159 @@
+.TH CRYPT-GENSK 2
+.SH NAME
+crypt: genSK, genSKfromPK, sktopk, dhparams, sign, verify \- generate keys and digital signatures
+.SH SYNOPSIS
+.EX
+include "ipints.m";
+ipints := load IPints IPints->PATH;
+IPint: import ipints;
+
+include "crypt.m";
+crypt := load Crypt Crypt->PATH;
+
+PK: adt
+{
+    pick {
+    RSA =>
+        n:     ref IPint;   # modulus
+        ek:    ref IPint;   # exp (encryption key)
+    Elgamal =>
+        p:     ref IPint;   # modulus
+        alpha: ref IPint;   # generator
+        key:   ref IPint;   # encryption key (alpha**secret mod p)
+    DSA =>
+        p:     ref IPint;   # modulus
+        q:     ref IPint;   # group order, q divides p-1
+        alpha: ref IPint;   # group generator
+        key:   ref IPint;   # encryption key (alpha**secret mod p)
+    }
+};
+
+SK: adt
+{
+    pick {
+    RSA =>
+        pk:     ref PK.RSA;
+        dk:     ref IPint;   # exp (decryption key)
+        p:      ref IPint;   # q in pkcs
+        q:      ref IPint;   # p in pkcs
+        # precomputed crt values
+        kp:     ref IPint;   # k mod p-1
+        kq:     ref IPint;   # k mod q-1
+        c2:     ref IPint;   # for converting residues to number
+    Elgamal =>
+        pk:     ref PK.Elgamal;
+        secret: ref IPint;   # decryption key
+    DSA =>
+        pk:     ref PK.DSA;
+        secret: ref IPint;   # decryption key
+    }
+};
+
+PKsig: adt
+{
+    pick {
+    RSA =>
+        n:  ref IPint;
+    Elgamal =>
+        r:  ref IPint;
+        s:  ref IPint;
+    DSA =>
+        r:  ref IPint;
+        s:  ref IPint;
+    }
+};
+
+genSK:       fn(algname: string, length: int): ref SK;
+genSKfromPK: fn(pk: ref PK): ref SK;
+sktopk:      fn(sk: ref SK): ref PK;
+
+sign:        fn(sk: ref SK, m: ref IPint): ref PKsig;
+verify:      fn(pk: ref PK, sig: ref PKsig, m: ref IPint): int;
+
+dhparams:    fn(nbits: int): (ref IPint, ref IPint);
+.EE
+.SH DESCRIPTION
+.B Crypt
+implements a set of public-key signature algorithms.
+The public/private key pairs are represented by values of the adt
+.BR SK ,
+containing both the private (secret) and public parts of the pair,
+and
+.BR PK ,
+containing only the public part.
+The several algorithms are represented by different pick variants.
+.PP
+.B GenSK
+generates a new public/private key pair, represented by
+.BR SK .
+.I Algname
+is the name of the algorithm to use; in the current implementation,
+.BR dsa ,
+.B elgamal
+and
+.B rsa
+are possible.
+.I Length
+gives the length of the key modulus in bits.
+.B GenSK
+returns nil if an unknown algorithm has been specified.
+.PP
+.B GenSKfromPK
+generates a private key that has the system parameters as the public key
+.IR pk .
+It is used to generate new keys that are of the same complexity as old keys.
+.PP
+.B Sktopk
+returns a reference to the public part of private key
+.IR sk .
+.PP
+.B Sign
+creates a digital signature of a message
+.IR m ,
+represented by an IPint,
+using the private key
+.IR sk .
+Typically
+.I m
+represents a secure hash (eg, using
+.IR crypt-sha1 (2))
+of a much larger message.
+.PP
+.B Verify
+uses public key
+.I pk
+to verify that the value
+.I sig
+is a digital signature of the message
+.I m
+using the private key corresponding to
+.IR pk .
+It returns non-zero (true) if the signature is valid; zero (false) otherwise.
+.PP
+Most applications use generic operations on public and private keys,
+referring to
+.B PK
+and
+.BR SK ,
+but specific variants can be named, such as
+.BR PK.RSA
+for RSA keys, allowing use of RSA-specific operations.
+.IR Crypt-dsagen (2)
+describes functions for key generation that are specific to various algorithms,
+using algorithm-specific parameters.
+.PP
+.B Dhparams
+creates Diffie-Hellman parameters. It returns
+a tuple of IPints
+.RI ( alpha , p ).
+.I P
+is an
+.I nbits
+long prime number that serves as the modulus.
+.I Alpha
+is a primitive root in the integer field defined by that modulus.
+.SH SEE ALSO
+.IR crypt-dsagen (2),
+.IR crypt-sha1 (2),
+.IR security-auth (2),
+.IR security-oldauth (2)
--- /dev/null
+++ b/man/2/crypt-rc4
@@ -1,0 +1,49 @@
+.TH CRYPT-RC4 2
+.SH NAME
+crypt: rc4setup, rc4, rc4skip, rc4back \- RC4 encryption
+.SH SYNOPSIS
+.EX
+include "ipints.m";
+ipints := load IPints IPints->PATH;
+IPint: import ipints;
+
+include "crypt.m";
+crypt := load Crypt Crypt->PATH;
+
+rc4setup: fn(seed: array of byte): ref RC4state;
+rc4:      fn(state: ref RC4state, buf: array of byte, n: int);
+rc4skip:  fn(state: ref RC4state, n: int);
+rc4back:  fn(state: ref RC4state, n: int);
+.EE
+.SH DESCRIPTION
+These functions implement the stream encryption algorithm that is claimed to
+be equivalent to RSA Security's RC4.
+It is a pseudo-random number generator with a 256
+byte state and a long cycle.
+.PP
+.B Rc4setup
+sets the initial
+.IR seed ,
+which can be any non-zero length, and
+returns a representation of the initial state of the algorithm,
+which is used in subsequent calls.
+.PP
+.B Rc4
+runs the generator starting with the given
+.IR state ,
+and XORs the output of the generator with
+the first
+.I n
+bytes of
+.IR buf ,
+updating the
+.IR state .
+.B Rc4
+is symmetric and is used both to encrypt and decrypt.
+.B Rc4skip
+skips over bytes (eg, to account for lost transmissions);
+.B rc4back
+runs the generator backwards (eg, to account for retransmissions).
+.SH SEE ALSO
+.IR crypt-intro (2),
+.IR crypt-crypt (2)
--- /dev/null
+++ b/man/2/crypt-sha1
@@ -1,0 +1,144 @@
+.TH CRYPT-SHA1 2
+.SH NAME
+crypt: sha1, sha224, sha256, sha384, sha512, md4, md5, hmac_sha1, hmac_md5 \- cryptographic digests
+.SH SYNOPSIS
+.EX
+include "ipints.m";
+ipints := load IPints IPints->PATH;
+IPint: import ipints;
+
+include "crypt.m";
+crypt := load Crypt Crypt->PATH;
+
+DigestState: adt
+{
+    # hidden state
+    copy:   fn(d: self ref DigestState): ref DigestState;
+};
+
+.ta \w'verify:\ 'u +\w'fn(\ \ \ 'u
+sha1:	fn(buf: array of byte, n: int, digest: array of byte,
+		state: ref DigestState): ref DigestState;
+sha224:	fn(buf: array of byte, n: int, digest: array of byte,
+		state: ref DigestState): ref DigestState;
+sha256:	fn(buf: array of byte, n: int, digest: array of byte,
+		state: ref DigestState): ref DigestState;
+sha384:	fn(buf: array of byte, n: int, digest: array of byte,
+		state: ref DigestState): ref DigestState;
+sha512:	fn(buf: array of byte, n: int, digest: array of byte,
+		state: ref DigestState): ref DigestState;
+md4:	fn(buf: array of byte, n: int, digest: array of byte,
+		state: ref DigestState): ref DigestState;
+md5:	fn(buf: array of byte, n: int, digest: array of byte,
+		state: ref DigestState): ref DigestState;
+
+SHA1dlen, SHA224dlen, SHA256dlen, SHA384dlen, SHA512dlen, MD4dlen, MD5dlen: con ...;
+
+hmac_sha1: fn(buf: array of byte, n: int, key: array of byte,
+		    digest: array of byte,
+		    state: ref DigestState): ref DigestState;
+hmac_md5:  fn(buf: array of byte, n: int, key: array of byte,
+		    digest: array of byte,
+		    state: ref DigestState): ref DigestState;
+.EE
+.SH DESCRIPTION
+.BR Sha1 ,
+.BR sha224 ,
+.BR sha256 ,
+.BR sha384 ,
+.BR sha512 ,
+.B md4
+and
+.B md5
+are cryptographically secure hash functions that produce output called a message digest.
+Each function computes a hash of
+.I n
+bytes of the data in
+.IR buf ,
+using the named algorithm,
+and updates the current
+.IR state .
+They can be called iteratively to form a single digest for many data blocks.
+The state is kept in the
+.B DigestState
+value referenced by
+.I state
+between calls.
+.I State
+should be
+.B nil
+on the first call, and a newly allocated
+.B DigestState
+will be returned for use in subsequent calls.
+On a call in which
+.I digest
+is not
+.BR nil ,
+the hash is completed and copied into the
+.I digest
+array.
+.B Sha1
+produces a 20-byte hash
+.RB ( SHA1dlen ),
+.B sha224
+a 28-byte hash
+.RB ( SHA224dlen ),
+.B sha256
+a 32-byte hash
+.RB ( SHA256dlen ),
+.B sha384
+a 48-byte hash
+.RB ( SHA384dlen ),
+.B sha256
+a 64-byte hash
+.RB ( SHA512dlen ),
+.B md4
+and
+.B md5
+a 16-byte one
+.RB ( MD4len
+and
+.BR MD5len ).
+.PP
+.B Hmac_sha1
+and
+.B hmac_md5
+are keyed versions of the hashing functions, following Internet RFC2104.
+The
+.I key
+must be provided in each call, but otherwise
+the calling conventions are those of
+.BR sha1 .
+The
+.I key
+must currently be no more than 64 bytes.
+.PP
+.B DigestState
+hides the state of partially completed hash functions during processing.
+Its
+.B copy
+operation returns a reference to a new copy of a given state.
+.SH EXAMPLES
+A program to read a file and hash it using SHA might contain the following inner loop:
+.IP
+.EX
+state: ref DigestState = nil;
+while((n := sys->read(fd, buf, len buf)) > 0)
+	state = kr->sha1(buf, n, nil, state);
+digest := array[kr->SHA1dlen] of byte;
+kr->sha1(buf, 0, digest, state);
+.EE
+.SH SOURCE
+.B /libinterp/crypt.c
+.br
+.B /libsec/port/hmac.c
+.br
+.B /libsec/port/md4.c
+.br
+.B /libsec/port/md5.c
+.br
+.B /libsec/port/sha1.c
+.SH BUGS
+The MD4 algorithm is included only to allow communication with software
+that might still use it; it should not otherwise be used now, because it
+is easily broken.
--- /dev/null
+++ b/man/2/csv
@@ -1,0 +1,58 @@
+.TH CSV 2
+.SH NAME
+csv \- comma-separated values
+.SH SYNOPSIS
+.EX
+include "bufio.m";
+include "csv.m";
+csv := load CSV CSV->PATH;
+
+init:    fn(bufio: Bufio);
+getline: fn(fd: ref Bufio->Iobuf): list of string;
+quote:   fn(s: string): string;
+.EE
+.SH DESCRIPTION
+.B CSV
+provides a few functions to help read and write data in ``comma-separated value'' format.
+The
+.I csv
+format has a sequence of fields on a line (terminated by a newline, carriage return,
+or carriage return and newline).
+Fields are separated by commas, and may be empty.
+A field that starts with a double-quote is a quoted field, continuing
+to the next lone double-quote character, including commas and line terminators,
+but taking two double-quotes to represent one double-quote.
+Any text following quoted text, up to the next comma or line terminator, is included
+in the field value.
+Note that in a field that does not start with a double-quote, double-quotes are not treated specially.
+.PP
+.B Init
+must be called before any other operation of the module.
+.I Bufio
+is the instance of the
+.IR bufio (2)
+module that will provide the open files to be read.
+.PP
+.B Getline
+reads a sequence of fields in
+.I csv
+format from
+.B Iobuf
+.IR fd ,
+unquotes fields as required, and returns them in order in a list.
+It returns nil on end-of-file (or read error).
+An empty line is regarded as containing a single empty field.
+.PP
+.B Quote
+returns string
+.I s
+quoted as required by the
+.I csv
+format:
+the result is quoted if
+.I s
+contains comma, newline, or a quote (and all embedded quotes are doubled).
+.SH SOURCE
+.B /appl/lib/csv.b
+.SH SEE ALSO
+.IR sh-csv (1)
--- /dev/null
+++ b/man/2/daytime
@@ -1,0 +1,145 @@
+.TH DAYTIME 2
+.SH NAME
+daytime: text, filet, gmt, local, now, string2tm, time, tm2epoch \- time conversions
+.SH SYNOPSIS
+.EX
+include "daytime.m";
+daytime := load Daytime Daytime->PATH;
+
+Tm: adt
+{
+    sec:    int;    # seconds (0 to 59)
+    min:    int;    # minutes (0 to 59)
+    hour:   int;    # hours (0 to 23)
+    mday:   int;    # day of the month (1 to 31)
+    mon:    int;    # month (0 to 11)
+    year:   int;    # year-1900; 2000AD is 100
+    wday:   int;    # day of week (0 to 6, Sunday is 0)
+    yday:   int;    # day of year (0 to 365)
+    zone:   string; # time zone name
+    tzoff:  int;    # time zone offset (seconds from GMT)
+};
+
+text:      fn(tm: ref Tm): string;
+filet:     fn(now, t: int): string;
+gmt:       fn(tim: int): ref Tm;
+local:     fn(tim: int): ref Tm;
+now:       fn(): int;
+time:      fn(): string;
+tm2epoch:  fn(tm: ref Tm): int;
+string2tm: fn(date: string): ref Tm;
+.EE
+.SH DESCRIPTION
+These routines perform time conversions relative to the
+epoch 00:00:00 GMT, Jan. 1, 1970.
+Note the range of values for each member of the
+.B Tm
+adt.
+The conventions are the same as those of C's
+.IR ctime .
+.PP
+.B Text
+converts a time structure referenced by
+.I tm
+from local or GMT time to a string in the format:
+.IP
+.B "Sat Jan  1 13:00:00 GMT 2000"
+.PP
+.B Filet
+converts the file access or modification time
+.I t
+from seconds since the epoch to local time as a string
+in the format:
+.IP
+.B "Jan  1 13:00"
+.PP
+if the file is less than 6 months old or
+.IP
+.B "Jan  1 2000"
+.PP
+if the file is older than 6 months, compared to the time
+.IR now .
+.PP
+.B Gmt
+converts seconds since the epoch, received in
+.IR tim ,
+to a time structure in Greenwich Mean Time (GMT).
+.PP
+.B Local
+converts seconds since the epoch, received in
+.IR tim ,
+to a time structure in local time.
+.PP
+.B Now
+returns the time in seconds since the epoch, obtained by reading
+.B /dev/time
+(see
+.IR cons (3)).
+.PP
+.B Time
+converts seconds since the epoch
+to the local time as a string in the format
+.BR "Fri May 19 17:01:36 BST 2000" .
+.PP
+.B Tm2epoch
+converts a time structure referenced by
+.I tm
+from local or GMT time to seconds since the epoch.
+.PP
+.B String2tm
+returns a reference to a
+.B Tm
+value corresponding to the date and time in textual form in string
+.IR s ,
+which must have one of the forms below:
+.IP
+.EX
+Sun, 06 Nov 1994 08:49:37 GMT    \fR(RFC822, RFC1123)\fP
+Sunday, 06-Nov-94 08:49:37 GMT   \fR(RFC850)\fP
+Sun Nov  6 08:49:37 GMT 1994     \fR(output of \fPtext\fR, above)\fP
+.EE
+.PP
+A missing time zone in any format is assumed to be
+.BR GMT .
+.B String2tm
+returns nil if
+.I s
+is not correctly formed.
+.PP
+When local time is first requested,
+.B daytime
+reads a table for time zone conversion from the
+.B timezone
+environment variable, if that is set,
+and otherwise from the file
+.BR /locale/timezone ,
+which is copied from one of the other files in
+.B /locale
+when the system is installed.
+The timezone table is a text file containing lines of space-separated fields.
+The first line gives the normal time zone name and its difference from GMT
+in seconds followed by an alternative time zone name (eg, for `daylight savings' or `summer' time) and
+its difference from GMT followed by a newline.
+The remainder is a list of pairs of times
+(seconds past the start of 1970, in the first time zone)
+when the alternative time zone applies.
+For example:
+.IP
+.EX
+EST -18000 EDT -14400
+ 9943200 25664400 41392800 57718800 ...
+.EE
+.PP
+Greenwich Mean Time is represented by
+.IP
+.EX
+GMT 0
+.EE
+.SH SOURCE
+.B /appl/lib/daytime.b
+.SH SEE ALSO
+.IR cons (3),
+.IR sys-millisec (2)
+.SH BUGS
+The sign bit of a Limbo integer holding a time will turn on 68 years from the
+epoch.
--- /dev/null
+++ b/man/2/dbm
@@ -1,0 +1,222 @@
+.TH DBM 2
+.SH NAME
+Dbm: Dbf, init \- data base with hashed indexing
+.SH SYNOPSIS
+.EX
+include "dbm.m";
+
+dbm := load Dbm Dbm->PATH;
+Datum, Dbf: import dbm;
+
+Datum:  type array of byte;
+
+Dbf: adt {
+    create: fn(file: string, perm: int): ref Dbf;
+    open:   fn(file: string, flags: int): ref Dbf;
+
+    fetch:  fn(db: self ref Dbf, key: Datum): Datum;
+    delete: fn(db: self ref Dbf, key: Datum): int;
+    store:  fn(db: self ref Dbf, key: Datum, data: Datum,
+                  replace: int): int;
+
+    firstkey: fn(db: self ref Dbf): Datum;
+    nextkey:  fn(db: self ref Dbf, key: Datum): Datum;
+
+    flush:    fn(db: self ref Dbf);
+    isrdonly: fn(db: self ref Dbf): int;
+};
+
+init:   fn();
+.EE
+.SH DESCRIPTION
+.B Dbm
+maintains
+key/content pairs in a data base.
+The functions will handle very large
+(a billion blocks)
+databases and will access a keyed item
+in one or two filesystem accesses.
+.PP
+.IR Key s
+and
+.IR content s
+are both represented by arrays of bytes
+(with the synonym
+.BR Datum ),
+allowing
+arbitrary binary values.
+.PP
+The data base is stored in two files.
+One file is a directory containing a bit map
+and has
+.L .dir
+as its suffix.
+The second file contains all data and has
+.L .pag
+as its suffix.
+An application can access several databases at once, but must avoid
+concurrent operations on any one database (eg, by using a monitor process to control access).
+.PP
+.B Init
+must be called before any other operation of the module.
+.PP
+A database is created by
+.IR Dbf.create ,
+which accepts a file permission parameter
+.IR perm ,
+as described for
+.B Sys->create
+(see
+.IR sys-open (2));
+it creates the two files
+.IB file .dir
+and
+.IB file .pag .
+If successful, it returns a
+.B Dbf
+reference describing the database, which
+is open for reading and writing.
+(It will truncate an existing database.)
+It returns nil if it cannot create the database for some reason,
+and sets the error string.
+.PP
+.B Dbf.open
+accepts a
+.I mode
+parameter as described in
+.IR sys-open (2),
+and opens the existing database in
+.IB file .dir
+and
+.IB file .pag .
+If successful, it returns a
+.B Dbf
+reference describing the database,
+which is open either for reading and writing (ie,
+.BR Sys->ORDWR ),
+or only for reading
+.RB ( Sys->OREAD )
+as determined by
+.IR mode .
+It returns nil if the database cannot be opened successfully, and sets the error string.
+.PP
+The remaining operations apply to an existing
+.B Dbf
+reference
+.IR db :
+.TP
+.IB db .fetch( key )
+Return the data stored under a
+.IR key ;
+nil is returned if the key is not in the database.
+.TP
+.IB db .store( key,\ data,\ replace )
+Store
+.I data
+under the given
+.IR key .
+If
+.I replace
+is non-zero,
+.B store
+will simply replace the existing value by the new one if the key is already
+in the database;
+if
+.I replace
+is zero
+.B store
+will return 0 if the new item was inserted, but
+1 if the key already appears in the database,
+and the new value will not be stored.
+.TP
+.IB db .delete( key )
+.I Key
+and its associated value
+is removed from the database.
+.TP
+.IB db .firstkey()
+Return the first key in the database;
+return nil if the database is empty.
+.TP
+.IB db .nextkey( key )
+Return the key following the given
+.IR key ,
+or nil if there is none.
+.TP
+.IB db .flush()
+Discard any data cached from the file.
+The cache is write-through, so it is not necessary to flush the file
+before the application exits.
+.TP
+.IB db .isrdonly()
+Return true if
+.I db
+was opened only for reading and writes are not allowed.
+.SH EXAMPLE
+.PP
+A linear pass through all keys in a database
+may be made,
+in an (apparently) random order,
+by use of
+.B Dbf.firstkey
+and
+.BR Dbf.nextkey .
+This code will traverse the data base:
+.IP
+.EX
+for(key := db.firstkey(); key != nil; key = db.nextkey(key)){
+	d := db.fetch(key);
+}
+.EE
+.PP
+The order of keys presented by
+.B Dbf.firstkey
+and
+.B Dbf.nextkey
+depends on a hashing function, not on anything
+interesting.
+.SH SOURCE
+.B /appl/lib/dbm.b
+.SH DIAGNOSTICS
+All functions that return an
+.I int
+indicate errors with negative values.
+A zero return indicates success.
+Routines that return pointers, including values of
+.BR Datum ,
+return nil values on error.
+.B Dbf.create
+and
+.B Dbf.open
+return nil on failure to access the database,
+setting the error string to a more detailed diagnostic.
+.SH BUGS
+On some systems (notably Plan 9 but also some Unix systems),
+the
+.B .pag
+file might contain holes where no data block has ever been written so
+that its apparent size is about
+four times its actual content.
+These files cannot be copied
+by normal means (cp, cat)
+without filling in the holes.
+.PP
+Except for
+.B firstkey
+and
+.BR nextkey ,
+.B Datum
+values returned
+by these functions
+point to storage
+that is changed by subsequent calls.
+.PP
+The sum of the sizes of a
+key/content pair must not exceed
+the internal block size
+(currently 512 bytes).
+Moreover all key/content pairs that hash
+together must fit on a single block.
+.B Dbf.store
+will return an error in the event that
+a block fills with inseparable data.
--- /dev/null
+++ b/man/2/debug
@@ -1,0 +1,382 @@
+.TH DEBUG 2
+.SH NAME
+debug \- process debugging
+.SH SYNOPSIS
+.EX
+include "debug.m";
+debug := load Debug Debug->PATH;
+
+Pos: adt
+{
+    file:       string;
+    line:       int;
+    pos:        int;
+};
+Src: adt
+{
+    start:      Pos;        # range within source files
+    stop:       Pos;
+};
+Sym: adt
+{
+    srctopc:    fn(s: self ref Sym, src: ref Src): int;
+    pctosrc:    fn(s: self ref Sym, pc: int): ref Src;
+};
+
+Module: adt
+{
+    addsym: fn(m: self ref Module, sym: ref Sym);
+    stdsym: fn(m: self ref Module);
+    dis:    fn(m: self ref Module): string;
+    sbl:    fn(m: self ref Module): string;
+};
+
+Prog: adt
+{
+    cont:   fn(p: self ref Prog): string;
+    delbpt: fn(p: self ref Prog, dis: string, pc: int): string;
+    event:  fn(p: self ref Prog): string;
+    grab:   fn(p: self ref Prog): string;
+    kill:   fn(p: self ref Prog): string;
+    setbpt: fn(p: self ref Prog, dis: string, pc: int): string;
+    stack:  fn(p: self ref Prog): (array of ref Exp, string);
+    start:  fn(p: self ref Prog): string;
+    status: fn(p: self ref Prog): (int, string, string, string);
+    step:   fn(p: self ref Prog, how: int): string;
+    stop:   fn(p: self ref Prog): string;
+    unstop: fn(p: self ref Prog): string;
+};
+
+Exp: adt
+{
+    name:   string;
+    m:      ref Module;
+
+    expand: fn(e: self ref Exp): array of ref Exp;
+    val:    fn(e: self ref Exp): (string, int);
+    src:    fn(e: self ref Exp): ref Src;
+    findsym:fn(e: self ref Exp): string;
+    srcstr: fn(e: self ref Exp): string;
+};
+
+init:       fn(): int;
+startprog:  fn(dis, dir: string, ctxt: ref Draw->Context,
+               argv: list of string): (ref Prog, string);
+prog:       fn(pid: int): (ref Prog, string);
+sym:        fn(sbl: string): (ref Sym, string);
+.EE
+.SH DESCRIPTION
+.B Debug
+is the module interface to the debugging facilities provided by
+.IR prog (3).
+It allows facilities for inspection of a program's data structures,
+as it is running, and to start and stop a running program
+under program control.
+.B Init
+must be called before any other function to initialise
+.IR debug 's
+global state.
+.PP
+.B Startprog
+starts up a program under control of the debug module.
+.I Dis
+is the full pathname of the Dis module to load (which must
+be compatible with
+.IR command (2);
+.I dir
+is the current directory in which to put the new process;
+.I ctxt
+and
+.I argv
+are the arguments given to the new process.
+.B Startprog
+returns a tuple
+.RI ( prog ,\  err )
+where
+.I prog
+can be used to interrogate and control the running process,
+as detailed below, unless there is an error, in which case
+.I prog
+will be nil, and
+.I err
+contains a description of the error.
+.B Prog
+is similar to startprog, except that it attaches to an
+already running process identified by
+.IR pid .
+.SS Controlling a process
+A
+.B Prog
+adt provides routines for controlling a running process.
+It implements the following routines.
+Unless otherwise stated, they return
+.B nil
+on success and a diagnostic string on error.
+.TP 10
+.IB prog .cont()
+Run the program until a break point is reached.
+.TP
+.IB prog .delbpt(\fIdis\fP,\ \fIpc\fP)
+.B Delbpt
+deletes the breakpoint in the Dis module with filename
+.I dis
+at Dis instruction
+.IR pc .
+.TP
+.IB prog .event()
+.B Event
+waits for a state transition in the running
+.I prog
+and returns the new state, as returned by a read
+of the
+.B dbgctl
+file (see
+.IR prog (3)).
+.TP
+.IB prog .grab()
+.B Grab
+stops the
+.I prog
+and puts it into a state where single stepping
+is possible.
+.TP
+.IB prog .kill()
+.B Kill
+kills
+.IR prog .
+.TP
+.IB prog .setbpt(\fIdis\fP,\ \fIpc\fP)
+.B Setbpt
+sets a breakpoint in the Dis module with filename
+.I dis
+at Dis instruction
+.IR pc .
+.TP
+.IB prog .stack()
+.B Stack
+returns a tuple
+.RI ( exps ,\  err )
+where
+.I exps
+is an array of
+.B Exp
+adts, each representing one frame of the current
+execution stack of
+.IR prog .
+If an error occurs,
+.I exps
+will be nil, and
+.I err
+will contain a description of the error.
+.TP
+.IB prog .start()
+.B Start
+runs
+.I prog
+until it hits a breakpoint or exits.
+.TP
+.IB prog .status()
+.B Status
+returns a tuple
+(\fIpgrp\fP, \fPuser\fP, \fPstate\fP, \fPmodule\fP)
+where
+.I pgrp
+is the process group id of
+.IR prog ,
+.I user
+is the owner of the process,
+.I state
+is the current state of the process, and
+.I module
+is the module implementation name of the currently
+executing module.
+.TP
+.IB prog .step(\fIhow\fP)
+.B Step
+steps
+.I prog
+forward in a manner specified by
+.IR how ,
+which is one of the following constants:
+.RS
+.TP
+.B StepExp
+Step one expression evaluation.
+.TP
+.B StepStmt
+Step one source statement.
+.TP
+.B StepOver
+Step over one called function.
+.TP
+.B StepOut
+Step until the current function has returned.
+.RE
+.TP
+.IB prog .stop()
+Stop
+.IR prog
+from running.
+.TP
+.IB prog .unstop()
+Release a program from its stopped state;
+breakpoints will no longer be triggered.
+.SS "Inspecting data"
+The
+.B Exp
+adt provides facilities for inspecting
+the data structures of a running Dis process.
+A hierarchical data structure can be expanded into
+an array of its component
+.BR Exp s,
+as long as the appropriate symbol information has
+been located correctly using
+.B stdsym
+or
+.BR addsym ,
+and
+.BR findsym .
+.PP
+A
+.B Pos
+represents a position in a Limbo source code file;
+it holds the source file name, the line number (origin 1)
+and the character within the line (origin 0).
+The
+.B Src
+adt represents a range in a Limbo source code file;
+.B Src.start
+and
+.B Src.stop
+represent the beginning and the end of the
+range, respectively.
+.PP
+A
+.B Sym
+represents a
+.B .sbl
+symbol file, and is created by calling
+.BI sym( p )
+where
+.I p
+is the pathname of the symbol file;
+.B sym
+returns a tuple
+.RI ( sym ,\  err ),
+where if
+.I sym
+is nil,
+.I err
+contains an error message.
+A
+.B Sym
+can map between a Dis PC and a source file address,
+and vice versa.
+For a given
+.B Sym
+.IR sym ,
+.IB sym .srctopc( src )
+returns the PC associated with
+.IR src
+(or -1 on error);
+.IB sym .pctosrc
+converts the other way (and returns
+.B nil
+on error).
+.PP
+Each element 
+.I e
+in the top level stack, as returned by
+.BR Prog.stack ,
+has an associated
+.B Module
+.IB e .m
+which needs to be associated with a
+.B Sym
+so that
+.I debug
+can glean from it the type information it needs.
+Given a module
+.IR m ,
+.IB m .stdsym()
+will try and find a symbol file in
+a standard place, but this will fail if the symbol file
+or the Dis file is in a non-standard place.
+.IB M .addsym( s )
+sets the symbol file for
+.I m
+to the
+.B Sym
+.IR s .
+.IB M .dis()
+and
+.IB m .sbl()
+return the paths of the Dis and symbol files associated
+with
+.I m
+respectively.
+.PP
+Each top level stack element
+expands into three elements, ``args'', ``locals'',
+and ``module'', representing the arguments to the function,
+the function's local variables, and the module-global
+variables of the function's module respectively.
+Before a top level stack element can be expanded,
+it is necessary to call
+.B findsym
+on it to locate the function's data.
+.TP 10
+.IB exp .name
+The name of the symbol.
+.TP
+.IB exp .expand()
+Expand a hierarchical structure into an array of its
+component elements. A list element expands into two
+elements named ``hd'' and ``tl''; a tuple into elements
+named ``t0'', ``t1'',..., an array into elements named ``0'', ``1'',..., etc.
+.TP
+.IB exp .val()
+.B Val
+returns a tuple
+.RI ( s ,\  flag )
+where
+.I s
+is a string representation of the value of
+.IR exp ,
+and if
+.I flag
+is zero,
+.I exp
+cannot be expanded.
+.TP
+.IB exp .src()
+.B Src
+returns the file range associated with
+.IR exp.
+.TP
+.IB exp .findsym()
+If
+.I exp
+is a top level stack frame (i.e. one of the members of the array
+returned by
+.BR Prog.stack )
+then
+.B findsym
+will attempt to locate its type and name. If it succeeds,
+it returns the null string, otherwise it returns
+an error indicating the problem.
+.TP
+.IB exp .srcstr()
+.B Srcstr
+returns a string representing the position in the source file
+of
+.IR exp .
+.SH FILES
+.BI /prog/ pid /*
+.SH SOURCE
+.B /appl/lib/debug.b
+.SH SEE ALSO
+.IR wm-deb (1),
+.IR prog (3)
+.SH BUGS
+There is no way of looking at the types of the data extracted.
--- /dev/null
+++ b/man/2/devpointer
@@ -1,0 +1,78 @@
+.TH DEVPOINTER 2 mux
+.SH NAME
+devpointer \- I/O interface for the pointer device
+.SH SYNOPSIS
+.EX
+include "draw.m";   # for Draw->Pointer
+include "devpointer.m";
+ptr := load Devpointer Devpointer->PATH;
+
+init:      fn();
+reader:    fn(file: string, posn: chan of ref Draw->Pointer,
+              pid: chan of (int, string));
+bytes2ptr: fn(buf: array of byte)    : ref Draw->Pointer;
+ptr2bytes: fn(ptr: ref Draw->Pointer): array of byte;
+.EE
+.SH DESCRIPTION
+.B Devpointer
+reads messages from pointer devices with the same data format as
+.BR /dev/pointer ,
+converting them to
+.B Pointer
+adts.
+.PP
+.B Init
+must be called before any other operation of the module.
+.PP
+.B Reader
+should be spawned by the caller.
+It opens the pointer device specified by
+.IR file ,
+and sends a value
+.BI ( id,\ err )
+on channel
+.IR pid .
+On success,
+.I id
+is the process ID of the spawned process and
+.I err
+is nil; on an error,
+.I err
+is a diagnostic, and
+.I id
+is undefined.
+If the file was opened successfully,
+.B reader
+continually reads the pointer device and sends
+.B Pointer
+adts over the channel
+.IR posn .
+If
+.I file
+is
+.BR nil ,
+the default device is
+.BR /dev/pointer .
+.PP
+.B Bytes2ptr
+converts the array of bytes
+.I buf
+to a
+.B Pointer
+and returns a reference to it.
+.I Buf
+should consist of exactly
+.B Devpointer->Size
+bytes and be in the format returned by
+.BR /dev/pointer .
+.PP
+.B Ptr2bytes
+provides the inverse transformation of
+.BR bytes2ptr :
+it packs the data into an array of bytes of appropriate format,
+which it returns.
+.SH FILES
+.B /dev/pointer
+.SH "SEE ALSO"
+.IR draw-intro (2),
+.IR draw-pointer (2)
--- /dev/null
+++ b/man/2/dhcpclient
@@ -1,0 +1,354 @@
+.TH DHCPCLIENT 2
+.SH NAME
+Dhcpclient: Bootconf, Lease, bootp, dhcp, applycfg, removecfg \- client's side of dynamic host configuration protocol
+.SH SYNOPSIS
+.EX
+include "dhcp.m";   # sic
+dhcpclient := load Dhcpclient Dhcpclient->PATH;
+Bootconf, Lease: import dhcpclient;
+
+Bootconf: adt {
+    ip:      string;
+    ipgw:    string;
+    ipmask:  string;
+    bootf:   string;
+    bootip:  string;
+    dhcpip:  string;
+    siaddr:  string;
+    serverid: string;
+    sys:     string;
+    dom:     string;
+    lease:   int;
+    options: array of array of byte;
+    vendor:  array of array of byte;
+
+    new:     fn(): ref Bootconf;
+    get:     fn(c: self ref Bootconf, n: int): array of byte;
+    getint:  fn(c: self ref Bootconf, n: int): int;
+    getip:   fn(c: self ref Bootconf, n: int): string;
+    getips:  fn(c: self ref Bootconf, n: int): list of string;
+    gets:    fn(c: self ref Bootconf, n: int): string;
+    put:     fn(c: self ref Bootconf, n: int, a: array of byte);
+    putint:  fn(c: self ref Bootconf, n: int, v: int);
+    putips:  fn(c: self ref Bootconf, n: int, ips: list of string);
+    puts:    fn(c: self ref Bootconf, n: int, s: string);
+};
+
+Lease: adt {
+    configs: chan of (ref Bootconf, string);
+
+    release: fn(l: self ref Lease);
+};
+
+init:      fn();
+tracing:   fn(debug: int);
+bootp:     fn(net: string, ctlifc: ref Sys->FD, device: string,
+               init: ref Bootconf): (ref Bootconf, string);
+dhcp:      fn(net: string, ctlifc: ref Sys->FD, device: string,
+               init: ref Bootconf, options: array of int):
+               (ref Bootconf, ref Lease, string);
+
+applycfg:  fn(net: string, ctlifc: ref Sys->FD,
+               conf: ref Bootconf): string;
+removecfg: fn(net: string, ctlifc: ref Sys->FD,
+               conf: ref Bootconf): string;
+.EE
+.SH DESCRIPTION
+.B Dhcpclient
+implements the client side of the Dynamic Host Configuration Protocol (DHCP) of Internet RFC2131.
+In the interface, Internet addresses are represented as strings, in forms that
+.IR ip (2)
+can parse, and that can be written directly to control files in
+.IR ip (3).
+.PP
+.B Init
+must be called before invoking any other operation of the module.
+.PP
+.B Bootp
+reserves the UDP port on
+.I net
+for use by BOOTP/DHCP clients, and sends a BOOTP request (ie, one without a DHCP operation code).
+.I Net
+is the name of the network directory (if nil, the default is
+.BR /net ).
+If
+.B bootp
+is to configure the interface according to the results received,
+.I ctlifc
+should be open on the control file of the
+.IB net /ipifc
+directory for the interface to be configured; otherwise it should be nil.
+.B Bootp
+repeats the request periodically until it either receives a reply or has made 5 attempts.
+It returns a tuple
+.BI ( conf,\ err ).
+If it has received a reply,
+.I conf
+refers to a
+.B Bootconf
+value that contains the values received, and
+.I err
+is nil.
+If
+.I ctlifc
+is not nil, the interface will also have been configured appropriately.
+If a valid reply has not been received, or some other error occurred,
+.I conf
+is nil, and
+.I err
+is a diagnostic.
+.PP
+.B Dhcp
+has a similar interface, but runs the full DHCP protocol.
+The
+.I options
+array has integers representing possible DHCP options;
+.B dhcp
+asks the server to provide values for them.
+If
+.I options
+is nil, a few option values are requested that might be useful for Inferno
+(eg, subnet mask, gateway, DNS server, authentication and file servers, and so on).
+If the server does supply them, they can be retrieved either from
+specific fields of
+.BR Bootconf ,
+or using its
+.I get
+operations.
+.I Init
+is also usually nil, but can refer to a
+.B Bootconf
+that provides some values to suggest to the server, for instance if the client
+knows a previously-assigned address stored in non-volatile memory.
+.B Dhcp
+returns a tuple
+.BI ( conf,\ lease,\ err ),
+where
+.I conf
+and
+.I err
+are just as for
+.BR bootp ,
+and the new component
+.I lease
+is a reference to a
+.B Lease
+value that gives access to the state of the client's address assignment.
+.PP
+DHCP allows a server to assign a client an address permanently, or to lease it for a specified time.
+In the latter case,
+.B Bootconf.lease
+will have a non-zero value, and
+the client must periodically renew the lease to retain the address, and
+.B dhcp
+creates a process to do so.
+The
+.B Lease
+value provides a way for that process to communicate changes (if any) to the network configuration.
+Each time the configuration changes, the process will send a message on the channel
+.BR configs .
+(The channel is buffered, and
+.B dhcp
+first discards any previous notifications not yet received, so there are no ill effects
+if no process ever receives from the channel.)
+Each message is a tuple
+.BI ( conf,\ diag ).
+If a new state change has been made successfully,
+.I conf
+refers to a
+.B Bootconf
+value with the details.
+Otherwise,
+.I conf
+is nil and
+.I diag
+explains what went wrong.
+In any case, the watchdog process continues to try to extend the lease, or failing that,
+obtain a new network configuration, perhaps from another server.
+.B Lease.release
+may be called to release the leased address and stop the watchdog.
+.PP
+.B Bootconf
+has the following operations:
+.TP
+.B new()
+Return a reference to a
+.B Bootconf
+with values initialised to nil or 0.
+.TP
+.IB bc .get( n )
+Return the value of DHCP option
+.I n
+as a raw array of bytes.
+Return nil if the option is not set.
+.TP
+.IB bc .getint( n )
+Return the value of option
+.I n
+interpreted as an integer.
+Return zero if the option is not set.
+.TP
+.IB bc .getip( n )
+Return the first Internet address provided for option
+.IR n .
+.TP
+.IB bc .getips( n )
+Return a list of all the Internet addresses provided for option
+.IR n .
+.TP
+.IB bc .gets( n )
+Return the value of option
+.I n
+as a string.
+.TP
+.IB bc .put( n,\ a )
+Set the value of DHCP option
+.I n
+to the bytes of byte array
+.IR a .
+If
+.I a
+is nil,
+.B put
+removes any existing value for the option.
+.TP
+.IB bc .putint( n,\ v)
+Set option
+.I n
+to the integer value
+.IR v .
+.TP
+.IB bc .putips( n,\ ips )
+Set option
+.I n
+to the list of Internet addresses
+.IR ips .
+.TP
+.IB bc .puts( n,\ s )
+Set option
+.I n
+to the string
+.IR n .
+.PP
+.B Dhcpclient
+names a few constants representing commonly-used configuration options (attributes).
+They are suitable parameters for the option selector
+.I n
+of
+.BR Bootconf 's
+.I get
+and
+.I put
+functions.
+The first set of constants name options for both BOOTP and DHCP:
+.PP
+.PD 0
+.TP 25
+.B Odnsserver
+Internet address(es) of Domain Name Servers
+.TP
+.B Odomainname
+Current domain (see
+.BR Bootconf.dom )
+.TP
+.B Ohostname
+Host name (see
+.BR Bootconf.sys )
+.TP
+.B Omask
+Network mask (IPv4).
+Also see
+.BR Bootconf.ipmask .
+.TP
+.B Onetbiosns
+NetBIOS servers
+.TP
+.B Ontpserver
+Network Time Protocol servers
+.TP
+.B Opop3server
+POP3 mail servers
+.TP
+.B Orouter
+Default router for subnet (see
+.BR Bootconf.ipgw )
+.TP
+.B Osmtpserver
+SMTP mail delivery servers
+.TP
+.B Ovendorinfo
+Vendor-specific data (see below)
+.TP
+.B Owwwserver
+HTTP proxy
+.PD
+.PP
+The second set has DHCP options:
+.PP
+.PD 0
+.TP 25
+.B Obootfile
+Name of the file containing a kernel for the client  to load (eg, by TFTP); see
+.BR Bootconf.bootf .
+.TP
+.B Olease
+Lease time for IP address, in seconds (also see
+.BR Bootconf.lease )
+.TP
+.B Omaxmsg
+Maximum DHCP size the client is willing to accept (minimum 576 bytes).
+.TP
+.B Orebindingtime
+Time interval in seconds from address assignment to the time address must be rebound.
+.TP
+.B Orenewaltime
+Time interval in seconds from address assignment to first attempt to renew the address.
+.TP
+.B Otftpserver
+TFTP server from which to fetch kernel and parameter files; see
+.BR Bootconf.bootip .
+.TP
+.B Ovendorclass
+Identify vendor type and configuration of client. Inferno sets
+this to
+.B plan9_386
+(sic) to encourage Plan 9 DHCP servers to respond; other servers will ignore it.
+.PD
+.PP
+The final set give vendor-specific options that Inferno shares with Plan 9:
+.PP
+.PD 0
+.TP 25
+.B Ovendor
+Flag OR'd in to an option number to mark it as destined for the `vendor information' section.
+.TP
+.B OP9auth
+Authentication server
+.RB ( Ovendor|129 )
+.TP
+.B OP9fs
+File server
+.RB ( Ovendor|128 )
+.PD
+.PP
+Given a network configuration in
+.IR conf ,
+and a valid file descriptor for a network interface's control file,
+in the network
+.IR net ,
+.B applycfg
+sets the basic interface parameters (address, network mask, default gateway),
+and writes other parameters to
+.IR net /ndb ;
+conversely,
+.B removecfg
+removes from the interface just those parameters set by
+.IR conf .
+Normally these functions are called automatically, as required, by
+.B dhcp
+and its watchdog process.
+.SH SOURCE
+.B /appl/lib/dhcpclient.b
+.SH SEE ALSO
+.IR bootpd (8),
+.IR dhcp (8)
--- /dev/null
+++ b/man/2/dial
@@ -1,0 +1,332 @@
+.TH DIAL 2
+.SH NAME
+Dial: accept, announce, dial, listen, netinfo, netmkaddr, reject \- make network connections
+.SH SYNOPSIS
+.EX
+include "dial.m";
+dial := load Dial Dial->PATH;
+
+Connection: adt
+{
+    dfd:  ref FD;  # data file
+    cfd:  ref FD;  # control file
+    dir:  string;  # pathname of line directory
+};
+
+announce:  fn(addr: string):        ref Connection;
+dial:      fn(addr, local: string): ref Connection;
+listen:    fn(c: ref Connection):       ref Connection;
+accept:    fn(c: ref Connection): ref Sys->FD;
+reject:    fn(c: ref Connection, why: string);
+
+netmkaddr: fn(addr, defnet, defsvc: string): string;
+
+Conninfo: adt
+{
+    dir:   string;   # connection directory
+    root:  string;   # network mount point
+    spec:  string;   # its binding spec
+    lsys:  string;   # local host address
+    lserv: string;   # local service
+    rsys:  string;   # remote host address
+    rserv: string;   # remote service
+    laddr: string;   # local address in dial form
+    raddr: string;   # remote address in dial form
+};
+
+netinfo:   fn(c: ref Connection): ref Conninfo;
+.EE
+.SH DESCRIPTION
+.B Dial
+establishes network connections.
+The description below uses the following definitions:
+.TF network
+.PD
+.TP
+.I addr
+is a network address in one of the following forms:
+.br
+.IP
+.IB network ! netaddr ! service\f1
+.br
+.IB network ! netaddr\f1
+.br
+.IR netaddr
+.TP
+.I network
+Any directory listed in
+.B /net
+(eg,
+.BR tcp ),
+or the special token,
+.BR net .
+The special name
+.B net
+stands for any network that connects
+the current host and
+.IR netaddr .
+A network name can be preceded by the full path name of a directory
+of networks, using the form
+.I /dir/network
+(eg,
+.BR /net.alt/tcp ).
+.TP
+.I netaddr
+A host name, a domain name, a network address,
+or a meta-name of the form
+.BI $ attribute\f1,
+which
+is replaced by
+.I value
+from the corresponding attribute-value pair
+in the connection server data base (see
+.IR db (6)).
+.PP
+The functions
+.B dial
+and
+.B announce
+translate a given
+.I addr
+to an actual network address using
+the connection server
+.IR cs (8).
+If a logical name
+.I addr
+corresponds to several network addresses,
+for instance if a destination machine has several interfaces,
+.I cs
+will return them all;
+.B dial
+or
+.B announce
+will try each in turn until one works.
+In particular, if
+.I addr
+is
+.BR net ,
+.I cs
+will return addresses on
+all networks that are common to source and destination.
+The translation procedure accesses
+.I cs
+using its interface file
+.BR cs ,
+which is sought as follows:
+first, in an explicit directory
+.BI / dir
+if one was given in
+.IR network ;
+second, in the standard directory
+.BR /net ;
+and finally in the directory
+.BR /net.alt
+.RB ( dial
+only).
+If the connection server cannot be found,
+the
+.I addr
+is used as-is.
+.PP
+If a connection attempt is successful, the
+.B dir
+member of the resulting
+.B Connection
+will be
+the path name of a
+.I line directory
+that has files for accessing the connection.
+One line directory exists for each possible connection.
+The
+.B data
+file in the line directory is opened to
+make a connection, and read and written to communicate with the destination.
+The
+.B ctl
+file in the line directory can be used to send commands to the line.
+See
+.IR ip (3)
+for messages that can be written to the
+.B ctl
+file.
+The last close of both
+.B data
+and
+.B ctl
+file will close the connection.
+The
+.B remote
+file in the line directory contains the address called; the file
+.B local
+contains the local address assigned.
+.PP
+The function
+.B dial
+calls destination
+.I addr
+on a multiplexed network.
+If the connection server returns several possible locations for
+.IR addr ,
+.B dial
+tries each in turn, until a connection is made,
+or no address remains to be tried.
+.B Dial
+returns a reference to a
+.B Connection
+value containing a string
+.B dir
+that names the conversation directory for the connection,
+a file descriptor
+.B dfd
+open for reading and writing the
+.B data
+file in that directory, and
+a file descriptor
+.B cfd
+open for reading and writing the directory's
+.B ctl
+file.
+If
+.IR local
+is non-empty, and the network allows the local address to be set,
+as is the case with UDP and TCP port numbers,
+the local address will be set to
+.IR local .
+.PP
+.B Announce
+and
+.B listen
+are the complements of
+.BR dial .
+.B Announce
+establishes a network name to which incoming calls can be made.
+In
+.IR addr ,
+.I netaddr
+gives the name or address of one of the local host's interfaces on which to listen for
+calls to the given
+.IR service ;
+it can be
+.B *
+to listen for calls on any interface on
+.IR network .
+.B Announce
+returns a reference to a
+.B Connection
+value in which only the
+.B cfd
+descriptor is open, on the control file representing the announcement.
+.B Listen
+takes as its only argument a reference to the
+.B Connection
+returned by a successful call to
+.BR announce .
+When a call is received,
+.B listen
+returns a reference to a new
+.B Connection
+value that refers to the conversation directory for the incoming call;
+only the
+.B cfd
+descriptor is open.
+That call can be accepted or rejected.
+Use
+.B accept
+to obtain a file descriptor for the data file for the conversation.
+Use
+.B reject
+to reject the incoming call; some networks will also tell the caller the reason
+.IR why .
+.PP
+.B Netmkaddr
+makes
+.I addr
+into a full network address,
+suitable for
+.B dial
+or
+.BR announce .
+It adds the default network
+.I defnet
+(usually "\f5net\fP")
+and a default service
+.I defsvc
+to the given
+.I addr
+as required, including
+.RB ` ! '
+separators,
+and returns the result.
+.PP
+Given a
+.BR Connection ,
+.B netinfo
+returns a reference to a
+.B Conninfo
+value that gives details about the connection and its network.
+.SH EXAMPLES
+.PP
+Make a call and return an open file descriptor to
+use for communications:
+.IP
+.EX
+callkremvax(): ref Sys->FD
+{
+	c := dial->dial("tcp!kremvax!80", nil);
+	if(c == nil)
+		return nil;
+	return c.dfd;
+}
+.EE
+.PP
+Call the local certificate signer:
+.IP
+.EX
+dialsigner(service: string): ref Sys->FD
+{
+	c := dial->dial("net!$SIGNER!inflogin", nil);
+	if(c == nil)
+		return nil;
+	return c.dfd;
+}
+.EE
+.PP
+Listen for incoming calls.
+.IP
+.EX
+listener()
+{
+	ac := dial->announce("tcp!*!9995");
+	if(ac == nil){
+		sys->print("can't announce: %r\en");
+		exit;
+	}
+	for(;;){
+		lc := dial->listen(ac);
+		if(lc == nil){
+			sys->print("listen: %r\en");
+			exit;
+		}
+		sys->print("incoming: %s\en", hd ctext(lc));
+		spawn client(lc);
+	}
+}
+
+client(c: ref Connection)
+{
+	dfd := dial->accept(c);
+	if(dfd == nil){
+		sys->print("%s: can't accept: %r\en", c.dir);
+		exit;
+	}
+	buf := array[Sys->ATOMICIO] of byte;
+	while((n := sys->read(dfd, buf, len buf)) > 0)
+		sys->write(dfd, buf, n);
+}
+.EE
+.SH SOURCE
+.B /appl/lib/dial.b
+.SH DIAGNOSTICS
+The integer valued functions return 0 on success and -1 on error;
+functions returning a reference return nil on error.
+In those cases the system error string is set.
--- /dev/null
+++ b/man/2/dialog
@@ -1,0 +1,74 @@
+.TH DIALOG 2
+.SH NAME
+dialog: prompt, getstring \-
+basic dialog boxes
+.SH SYNOPSIS
+.EX
+include "dialog.m";
+dialog := load Dialog Dialog->PATH;
+
+init:        fn();
+prompt:      fn(ctxt: ref Draw->Context, p: ref Draw->Image,
+               icon, title, msg: string,
+               dflt: int, labs: list of string): int;
+getstring:   fn(ctxt: ref Draw->Context, p: ref Draw->Image,
+               s: string): string;
+.EE
+.SH DESCRIPTION
+.B Dialog
+module provides two simple dialog boxes
+for use by
+.IR wm (1)
+applications.
+.B Init
+should be called once to initialise its internal state.
+.PP
+.B Prompt
+uses the graphics context
+.I ctxt
+to pop up a dialog box to prompt the user to choose from a set of alternatives,
+or to acknowledge a message.
+The box is created near the northwest corner of
+the parent window
+.IR p ,
+represented by the window's image.
+(If the parent window is a Tk Toplevel
+.IR t ,
+for instance, the appropriate value is
+.IB t .image \f1.)\fP
+If
+.I p
+is nil, the box is centred on the screen.
+The box
+has the given
+.I title
+and an optional
+.IR icon .
+It displays the given
+.I msg
+and a number of buttons, labelled with the strings in
+.IR labs .
+The dialog box waits for the user to push a button, and then
+returns the index of the button pushed (the first element of
+.I labs
+is index 0).
+If the user types a newline, the
+.I dflt
+value is returned.  The button with the
+.I dflt
+index is specially outlined in the dialog box.
+.PP
+.B Getstring
+pops up a dialog box near the parent window
+.IR p .
+The box contains the
+.I msg
+and an entry widget.
+It waits for the user to type a string and a newline,
+and then returns the typed string, without the newline.
+.SH SOURCE
+.B /appl/lib/dialog.b
+.SH SEE ALSO
+.IR draw-context (2),
+.IR tk (2),
+.IR wmlib (2)
--- /dev/null
+++ b/man/2/dict
@@ -1,0 +1,57 @@
+.TH DICT 2
+.SH NAME
+dict - list of string pairs
+.SH SYNOPSIS
+.EX
+include "dict.m";
+dict := load Dictionary Dictionary->PATH;
+
+Dict: adt {
+	add: fn(d: self ref Dict, e: (string, string));
+	delete: fn(d: self ref Dict, k: string);
+	lookup: fn(d: self ref Dict, k: string): string;
+	keys: fn(d: self ref Dict): list of string;
+};
+.EE
+.SH DESCRIPTION
+.B Dict
+provides a simple string to string association list:
+.TP
+.IB d .add( e )
+Adds a new tuple
+.IR e
+to the list,
+representing a new
+.RI ( "key ,\ value" )
+pair.
+.TP
+.IB d .delete ( k )
+Deletes all pairs with key
+.I k
+from the list.
+.TP
+.IB d .lookup( k )
+Tries to find a pair with key
+.I k
+and returns its associated value,
+or nil if the key was not found.
+.TP
+.IB d .keys()
+Returns a list of all
+keys
+in the list.
+.SH SOURCE
+.B /appl/lib/dict.b
+.SH SEE ALSO
+.IR hash (2)
+.SH BUGS
+No attempt is made to
+keep keys unique in the list; if more
+than one pair exists with the same key, then
+.B lookup
+will select one of them arbitrarily.
+.PP
+Computational overhead of lookup and deletion of
+keys is proportional
+to the number of pairs in the list.
+
--- /dev/null
+++ b/man/2/dis
@@ -1,0 +1,488 @@
+.TH DIS 2
+.SH NAME
+dis \- read Dis object files
+.SH SYNOPSIS
+.EX
+include "dis.m";
+dis := load Dis Dis->PATH;
+
+Inst: adt
+{
+	op:		int;
+	addr:	int;
+	mid:		int;
+	src:		int;
+	dst:		int;
+};
+
+Type: adt
+{
+	size:	int;
+	map:		array of byte;
+};
+
+Data: adt
+{
+	op:	int;		# encoded op
+	n:	int;		# number of elements
+	off:	int;		# byte offset in data space
+	pick {
+	Zero =>		# DEFZ
+	Bytes =>		# DEFB
+		bytes:	array of byte;
+	Words =>		# DEFW
+		words:	array of int;
+	String =>		# DEFS
+		str:	string;
+	Reals =>		# DEFF
+		reals:	array of real;
+	Array =>		# DEFA
+		typex:	int;
+		length:	int;
+	Aindex =>		# DIND
+		index:	int;
+	Arestore =>	# DAPOP
+	Bigs =>		# DEFL
+		bigs:		array of big;
+	}
+};
+
+Link: adt
+{
+	pc:		int;
+	desc:	int;
+	sig:		int;
+	name:	string;
+};
+
+Import: adt
+{
+	sig:	int;
+	name:	string;
+};
+
+Except: adt
+{
+	s:	string;
+	pc:	int;
+};
+
+Handler: adt
+{
+	pc1:	int;
+	pc2:	int;
+	eoff:	int;
+	ne:	int;
+	t:	ref Type;
+	etab:	array of ref Except;
+};
+
+Mod: adt
+{
+	name:	string;
+	srcpath:	string;
+
+	magic:	int;
+	rt:		int;
+	ssize:	int;
+	isize:	int;
+	dsize:	int;
+	tsize:	int;
+	lsize:	int;
+	entry:	int;
+	entryt:	int;
+
+	inst:	array of ref Inst;
+	types:	array of ref Type;
+	data:	list of ref Data;
+	links:	array of ref Link;
+	imports:	array of array of ref Import;
+	handlers:	array of ref Handler;
+
+	sign:	array of byte;
+};
+
+init:	fn();
+loadobj:	fn(file: string): (ref Mod, string);
+op2s:	fn(op: int): string;
+inst2s:	fn(i: ref Inst): string;
+.EE
+.SH DESCRIPTION
+The
+.B Dis
+module decodes the contents of a Dis object file containing a single module,
+of the format defined by
+.IR dis (6).
+The module defines many constants, giving symbolic names to
+Dis instruction codes, addressing mode masks, magic numbers, and other
+bits of the object code.
+.PP
+.B Init
+must be called before any other function, to initialise the module.
+.PP
+.B Loadobj
+reads a Dis object file from
+.IR file ,
+and returns a reference to a
+.B Mod
+adt that represents the module's contents, as the first element of the tuple;
+the string element of the tuple is nil.
+On error, the string element contains a diagnostic, and the
+reference is nil.
+.PP
+.B Op2s
+returns the assembly-language representation, as used by
+.IR asm (1),
+of the Dis operation code
+.IR op .
+It returns the string
+.IB ` OP op'
+if
+.I op
+does not correspond to a known operation code.
+.PP
+.B Inst2s
+returns a string corresponding to a disassembly of Dis instruction
+.IR i ,
+including addressing modes.
+.PP
+The module defines integer constants giving
+symbolic names to the Dis instruction codes, all of the form
+.BI I name
+where
+.I name
+is the name of the instruction, all in upper case:
+.IP
+.BR INOP ,
+.BR IALT ,
+.BR INBALT ,
+\&...
+.BR INEWZ ,
+.BR INEWAZ ,
+.BR IRAISE
+.PP
+The name
+.B MAXDIS
+is also defined; it has the value of the first unassigned Dis operation code.
+.PP
+Most of the members of the adt types have an obvious interpretation
+on reference to
+.IR dis (6).
+.PP
+The adt
+.B Mod
+represents a single module.
+It contains values extracted from the module's header,
+and references to structures representing the contents of
+the Dis file's code, data, type
+and external linkage sections:
+.TF entryt
+.PD
+.TP
+.B magic
+The constant
+.B XMAGIC
+(unsigned Dis module)
+or the constant
+.B SMAGIC
+(signed Dis module).
+.TP
+.B sign
+If
+.B magic
+is
+.BR SMAGIC ,
+the
+.B sign
+field contains the bytes in the signature section of the module header.
+Otherwise, there is no signature and
+.B sign
+is
+.BR nil .
+.TP
+.B name
+The name of the implementation module.
+.TP
+.B srcpath
+The source of the dis file relative to the inferno root.
+.TP
+.B rt
+Run-time options: a bit mask of the constants
+.BR MUSTCOMPILE ,
+.B DONTCOMPILE
+and
+.BR SHAREMP .
+.TP
+.B ssize
+Stack extent
+.TP
+.B isize
+Number of instructions
+.TP
+.B dsize
+Size in bytes of the module's global data area
+.TP
+.B tsize
+Number of type descriptors
+.TP
+.B lsize
+Number of external linkage descriptors
+.TP
+.B entry
+PC (instruction offset) of the default entry point for the module
+.TP
+.B entryt
+Index of the type descriptor for the module's entry point
+.TP
+.B inst
+Array representing the contents of the code segment;
+length
+.IB m .isize
+.TP
+.B types
+Array of the module's type descriptors;
+length
+.IB m .tsize
+.TP
+.B data
+list of data descriptors representing instructions for creating the module's data segment
+.TP
+.B links
+array of the module's external linkage descriptors (for exported functions); length
+.IB m .lsize
+.TP
+.B imports
+an array of import descriptor tables, one table for each module imported by this
+module. Each table is an array of pairs giving the signature and name of each
+function imported.
+.TP
+.B handlers
+an array of exception handlers used in this module. Each handler consists of
+the range of pc's it covers, the exception structure offset within the
+frame, the number of declared exceptions (as opposed to strings) in the handler,
+the type (if any) of any memory to clear when the exception occurs and a table
+of exceptions. The latter is an array containing pairs of exceptions and
+pc values. The final entry gives the pc to jump to
+in the '*' case or -1 if not applicable.
+.PP
+The
+.B Type
+adt represents the value of a type descriptor:
+.TF entryt
+.PD
+.TP
+.B size
+Size in bytes of the object represented by this descriptor
+.TP
+.B map
+Bitmap describing the location of pointers in the object (see
+.IR dis (6))
+.PP
+The
+.B Link
+adt represents the value of a link descriptor:
+.TF entryt
+.PD
+.TP
+.B name
+Name of the exported function
+.TP
+.B pc
+Instruction index in
+.B Mod.code
+of the function's entry point
+.TP
+.B desc
+Index in
+.B Mod.types
+of the type describing the function's stack frame
+.TP
+.B sig
+Integer hash of the function's type signature
+.PP
+The
+.B Inst
+adt represents a single Dis instruction in the instruction stream.
+The member
+.B op
+is the Dis instruction code.
+The member
+.B addr
+contains the addressing mode flags for middle, source and destination operands.
+Constants are defined to help unpack it.
+.PP
+The middle operand description is selected by the constant mask
+.BR ARM :
+.IP
+.IB i ".addr & ARM"
+.PP
+The valid results and interpretation are as follows:
+.IP
+.RS
+.TF AXNON
+.TP
+.B AXNON
+No middle operand.
+.TP
+.B AXIMM
+.BI $ n
+.TP
+.B AXINF
+.IB n (fp)
+.TP
+.B AXINM
+.IB n (mp)
+.PD
+.RE
+.PP
+The source operand's addressing mode is extracted as follows:
+.IP
+.BI ( i ".addr>>3)&AMASK"
+.PP
+The following combinations are valid, where
+.I n
+is the value in
+.IB i .src :
+.IP
+.RS
+.TF AIND|AMP
+.TP
+.B AXXX
+No operand
+.TP
+.B AFP
+The operand is
+.IB n (fp)
+.TP
+.B AMP
+The operand is
+.IB n (mp)
+.TP
+.B AIMM
+The operand is
+.BI $ n
+(ie, immediate literal
+.IR n )
+.TP
+.B AIND|AFP
+The operand is
+.IB si ( fi (fp))
+.TP
+.B AIND|AMP
+The operand is
+.IB si ( fi (mp))
+.RE
+.PD
+.PP
+where
+.I fi
+is the offset for the first indirection, extracted from
+.IR n :
+.IP
+.BI ( n ">>16)&16rFFFF)" ,
+.PP
+and
+.I si
+is the offset for the second indirection, also extracted from
+.IR n:
+.IP
+.BI ( n "&16rFFFF)" .
+.PP
+The destination addressing mode is interpreted in a similar way,
+except that the addressing mode is extracted as follows:
+.IP
+.BI ( i ".addr&AMASK)"
+.PP
+and the value of the offset
+.I n
+is found in
+.IB i .dst .
+.I Fi
+and
+.I si
+are extracted from
+.I n
+as before.
+.PP
+Finally,
+.B Data
+adt represents a data item, which tells the system's module loader
+how to initialise part of the module's global data segment.
+It has the following members:
+.TP
+.B op
+the encoded type and length; usually ignored: the
+.B pick
+tag and
+.BR n ,
+below,
+usually suffice
+.TP
+.B n
+the number of data values
+.TP
+.B off
+the byte offset of the first data value to initialise, relative to the current loading base
+.PP
+The alternatives of the
+.B pick
+select the correct variant to see the data values encoded in the
+object file as Limbo values
+of the correct type.
+The interpretation is straightforward for the tags
+.BR Bytes ,
+.BR Words ,
+.B Bigs
+and
+.BR Reals :
+the corresponding array members are arrays of
+.B n
+elements of the appropriate type.
+The remaining cases are as follows:
+.TF Arestore
+.PD
+.TP
+.B String
+The member
+.B str
+has the decoded representation of the
+corresponding
+.I n
+data bytes from the object file.
+.TP
+.B Array
+The member
+.B typex
+is the index in
+.B Mod.types
+of the array's type, and member
+.B length
+is its length.
+.TP
+.B Aindex
+This alternative can appear only following a value of
+.BR Data.Array .
+The member
+.B index
+is an index into the corresponding array as represented in the global data space,
+which determines a new loading base address for subsequent
+.B Data
+items.
+The previous base address is stacked on an internal stack.
+.TP
+.B Arestore
+Pop the address from the internal address stack and make that the current
+loading address.
+The request marks the end of a sequence of
+.B Data
+items initialising an array.
+.SH SOURCE
+.B /appl/lib/dis.b
+.SH SEE ALSO
+.IR disdep (1),
+.B wm/rt
+in
+.IR wm-misc (1),
+.IR dis (6)
+.br
+"The Dis Virtual Machine", in Volume 2.
--- /dev/null
+++ b/man/2/diskblocks
@@ -1,0 +1,120 @@
+.TH DISKBLOCKS 2
+.SH NAME
+Diskblocks: Block, Disk, tempfile \- temporary storage of variable-sized blocks
+.SH SYNOPSIS
+.EX
+include "diskblocks.m";
+diskblocks := load Diskblocks Diskblocks->PATH;
+
+Block: adt {
+   addr: big;   # address on file
+   n:    int;   # size in bytes
+};
+
+Disk: adt {
+   init:    fn(fd: ref Sys->FD, gran: int, maxblock: int): ref Disk;
+   new:     fn(d: self ref Disk, n: int): ref Block;
+   release: fn(d: self ref Disk, b: ref Block);
+   read:    fn(d: self ref Disk, b: ref Block,
+               a: array of byte, n: int): int;
+   write:   fn(d: self ref Disk, b: ref Block,
+               a: array of byte, n: int): ref Block;
+};
+
+init:     fn();
+tempfile: fn(): ref Sys->FD;
+.EE
+.SH DESCRIPTION
+.B Diskblocks
+manages a set of variable-sized blocks on a temporary file.
+.PP
+.B Init
+must be called before any other function in the module.
+.PP
+Each block has an address and a size in bytes, represented by a value of type
+.BR Block .
+.PP
+Each file is represented by the type
+.BR Disk ,
+providing the following operations:
+.TF 8n
+.TP
+.BI init( fd\f5,\fP\ gran\f5,\fP\ maxblock )
+Initialises the file
+.I fd
+for use as temporary block storage and returns a reference to a
+.B Disk
+to describe it.
+.I Fd
+must be open for reading and writing, and must refer to a file that allows random access.
+Blocks are allocated in multiples of the granularity
+.IR gran ,
+in bytes;
+the largest possible block is
+.I maxblock
+bytes, which must be a multiple of
+.IR gran .
+.TP
+.IB d .new( n )
+Allocate a block of
+.I n
+bytes on Disk
+.I d
+and return a reference to it.
+.TP
+.IB d .release( b )
+Free the Block
+.IR b ,
+making it available for reallocation.
+.TP
+.IB d .write( b\f5,\fP\ a\f5,\fP\ n )
+Write
+.I n
+bytes from array
+.I a
+to Block
+.I b
+on Disk
+.IR d ,
+returning a reference to the resulting Block.
+If
+.I b
+is nil or
+.I n
+exceeds
+.IR b 's
+current size,
+.B write
+allocates a new block (releasing
+.IR b ).
+Thus the returned value might differ from
+.IR b ,
+and must be used in subsequent IO requests.
+.TP
+.IB d .read( b\f5,\fP\ a\f5,\fP\ n )
+Read
+.I n
+bytes from Block
+.I b
+on Disk
+.I d
+into array
+.IR a ,
+returning the number of bytes read.
+.I N
+must not exceed
+.IB b .n .
+.PD
+.PP
+.B Tempfile
+returns a file descriptor referring to a newly-created temporary file,
+suitable for use by
+.BR Disk.init .
+The file will be removed automatically
+when the file descriptor is closed.
+.SH SOURCE
+.B /appl/lib/diskblocks.b
+.SH DIAGNOSTICS
+A function that returns an integer returns -1 on error; a function that returns a reference
+returns nil on error.
+The system error string is set in either case.
--- /dev/null
+++ b/man/2/disks
@@ -1,0 +1,317 @@
+.TH DISKS 2
+.SH NAME
+disks: Disk, PCpart, readn, chstext \- generic disk and partition interface
+.SH SYNOPSIS
+.EX
+include "disks.m";
+
+disks := load Disks Disks->PATH;
+
+Disk: adt {
+    prefix:  string;    # prefix before partition name
+    part:    string;    # partition name (nil if not partition)
+    fd:      ref Sys->FD;
+    wfd:     ref Sys->FD;
+    ctlfd:   ref Sys->FD;
+    rdonly:  int;       # non-zero if readonly
+    dtype:   string;    # device type
+
+    secs:    big;    # number of sectors in device or partition
+    secsize: int;    # device's sector size
+    size:    big;    # size of device or partition
+    offset:  big;    # within larger disk, perhaps
+    width:   int;    # of disk size in bytes as decimal string
+    c:       int;    # geometry: cyl, head, sectors
+    h:       int;
+    s:       int;
+    chssrc:  string; # source of c/h/s values
+
+    open:    fn(f: string, mode: int, noctl: int): ref Disk;
+};
+
+PCpart: adt {
+    active: int;    # Active or 0
+    ptype:  int;
+    base:   big;    # base block address
+    offset: big;    # block offset from base to partition
+    size:   big;    # in sectors
+
+    extract: fn(a: array of byte, d: ref Disk): PCpart;
+    bytes:   fn(p: self PCpart, d: ref Disk): array of byte;
+};
+
+init:	 fn();
+readn:   fn(fd: ref Sys->FD, buf: array of byte, n: int): int;
+chstext: fn(p: array of byte): string;
+.EE
+.SH DESCRIPTION
+.B Disks
+provides a simple way to gather
+and use information about 
+.IR floppy (3)
+and
+.IR sd (3)
+disks and disk partitions,
+as well as plain files.
+.PP
+.B Init
+must be called before invoking any other operations of the module
+.PP
+.B Disk.open
+opens
+.I file
+and returns a reference to a
+.B Disk
+value to represent the disk.
+.I Mode
+should be either
+.BR Sys->OREAD
+or
+.BR Sys->ORDWR
+to establish the open mode.
+.B Open
+always opens
+.I file
+for reading and stores that file descriptor in
+the element
+.IR fd .
+If the mode is not
+.BR Sys->OREAD ,
+.I opendisk
+also opens
+.I file
+for writing and stores that file descriptor in
+.BR wfd .
+The two file descriptors are kept separate to
+help prevent accidents.
+If
+.I noctl
+is not set, 
+.B open
+looks for a
+.B ctl
+file in the same directory as the
+disk file;
+if it finds one, it declares
+the disk to be
+an
+.IR sd (3)
+device,
+setting
+.B dtype
+to
+\f5"sd"\fP.
+If the passed
+.I file
+is named
+.BI fd n disk \fR,
+it looks for a file
+.BI fd n ctl \fR,
+and if it finds that,
+declares the disk to be
+a floppy disk, of type
+\f5"floppy"\fP.
+If either control
+file is found, it is opened for reading
+and writing, and the resulting file descriptor
+is saved as 
+.BR ctlfd .
+Otherwise the returned disk
+has type
+\f5"file"\fP.
+.PP
+.B Open
+then stores the file's length
+(as given by
+.IR sys-stat (2))
+in
+.BR size .
+If the disk is an
+.IR sd (3)
+partition, 
+.B open
+reads the sector size from the control
+file and stores it in 
+.BR secsize ;
+otherwise the sector size is assumed to be 512,
+as is the case for floppy disks.
+.B Open
+stores the disk size measured in sectors in
+.BR secs .
+.PP
+If the disk is an
+.IR sd (3)
+partition, 
+.B open
+parses the
+control
+file to find the partition's offset
+within its disk;
+otherwise it sets
+.B offset
+to zero.
+If the disk is an ATA disk,
+.B open
+reads
+the disk geometry (number of cylinders, heads, and sectors)
+from the 
+.B geometry
+line in the
+.I sd
+control file;
+otherwise it sets these to zero as well.
+.B Name
+is initialized with the base name of
+the disk partition, and is useful for forming messages to the
+.I sd
+control file.
+.B Prefix
+is set to the original
+.I file
+name without the 
+.B name
+suffix.
+.PP
+The IBM PC BIOS interface allocates
+10 bits for the number of cylinders, 8 for 
+the number of heads, and 6 for the number of sectors per track.
+Disk geometries are not quite so simple
+anymore, but to keep the interface useful,
+modern disks and BIOSes present geometries
+that still fit within these constraints.
+These numbers are still used when partitioning
+and formatting disks.
+.B Open
+employs a number of heuristics to discover this
+supposed geometry and store it in the
+.BR c ,
+.BR h ,
+and
+.B s
+elements of
+.BR Disk .
+Disk offsets in partition tables and
+in FAT descriptors are stored in a form
+dependent upon these numbers, so
+.I opendisk
+works hard to report numbers that
+agree with those used by other operating
+systems; the numbers bear little or no resemblance
+to reality.
+.PP
+.B Chssrc
+names the source of the geometry values:
+.B disk
+(values returned by disk itself);
+.B part
+(values stored in PC partition table);
+or
+.B guess
+(calculated by module's heuristics).
+.PP
+.B Readn
+attempts to read exactly
+.I n
+bytes from file
+.I fd
+into
+.IR buf ,
+using as many
+.IR sys-read (2)
+calls as required.
+It helps insulate a program from any peculiar underlying IO boundaries
+of a device.
+It returns less than
+.I n
+only if end-of-file is reached.
+It returns -1 if the first read fails.
+.PP
+The PC BIOS and operating systems support an arcane system of disk partitions:
+a partition table at the end of the master boot record
+defines up to four partitions.
+One (or perhaps more) of those can be an extended partition
+that heads a chain (or perhaps roots a tree) of partition tables
+elsewhere on disk, allowing many more than four partitions in all.
+.B Disks
+represents a partition table entry by a value of type
+.BR PCpart .
+It provides the following operations and values:
+.TP
+.B active
+Has the value
+.B Disks->Active
+if it is bootable, and zero otherwise.
+.TP
+.B ptype
+Partition type;
+.B Disks->Type9
+is used by Plan 9 and Inferno (see
+.IR prep (8)).
+.TP
+.B base
+Address of the extended partition that started the chain (or rooted the tree) containing this partition.
+Zero for primary partitions defined by the master boot record.
+.TP
+.B offset
+Block address of the start of the partition relative to the
+.BR base .
+.TP
+.B size
+Size of the partition in sectors.
+.TP
+.BI extract( "a, d" )
+Extracts the relevant data from an array of bytes
+.I a
+containing a PC-format partition table entry on
+.B Disk
+.IR d ,
+and returns a
+.B PCpart
+value that represents the partition.
+.TP
+.IB pc .bytes( d )
+Return an array of bytes containing the PC-format partition
+table entry corresponding to
+.IR pc .
+It will always be
+.B TentrySize
+bytes long.
+.PP
+Several other values are defined here for convenience:
+.TP
+Active
+Value for
+.B PCpart.active
+if the partition is bootable.
+.TP
+.B Type9
+Partition type used by Plan 9 and Inferno.
+.TP
+.B Toffset
+Offset (in bytes) of the partition table in a master boot record or extended partition start sector.
+.TP
+.B TentrySize
+Size in bytes of a partition table entry.
+.TP
+.B NTentry
+Number of table entries.
+.TP
+.B Magic0
+.PD0
+.TP
+.B Magic1
+Each sector containing a partition table should end with
+these two bytes (a master boot record must end with them).
+.PP
+.B Chstext
+takes a 3-byte array containing the packed cylinder/head/sector
+representation of a disk address and returns the corresponding text
+in the form
+.BI c / h / s.
+.SH SOURCE
+.B /appl/lib/disks.b
+.SH SEE ALSO
+.IR scsiio (2),
+.IR floppy (3),
+.IR sd (3),
+.IR prep (3)
--- /dev/null
+++ b/man/2/dividers
@@ -1,0 +1,68 @@
+.TH DIVIDERS 2
+.SH NAME
+Dividers \- user-draggable tk dividing bars
+.SH SYNOPSIS
+.EX
+include "dividers.m";
+dividers := load Dividers Dividers->PATH;
+Divider: import dividers;
+
+init: fn();
+Divider: adt {
+    new: fn(win: ref Tk->Toplevel, w: string, wl: list of string, dir: int):
+        (ref Divider, chan of string);
+    event: fn(d: self ref Divider, e: string);
+};
+.EE
+.SH DESCRIPTION
+.I Dividers
+presents an interface allowing Tk widgets to
+be arranged within a window, divided by bars which
+can be dragged by the user to determine the proportion
+of the available space to allocate to each widget.
+The groups can be stacked vertically or horizontally.
+.I Divider
+widgets can be nested.
+.PP
+.B Init
+must be called before anything else, to allow
+.I Dividers
+to initialise its internal state.
+.B Divider.new
+creates a new divider widget; it will be named
+.IR w ,
+and it will divide up the widgets named in
+the list
+.IR wl .
+.I Dir
+can be
+.BR Dividers->NS ,
+to stack the widgets one on top of another,
+or
+.BR Dividers->EW
+to stack the widgets left-right.
+.B Divider.new
+returns a new
+.B Divider
+adt, and a channel through which
+.B Divider
+events will be received.
+The application should arrange that events
+received on this channel be passed to the
+.B event()
+function.
+.PP
+A
+.B Divider
+widget must be informed if its size has changed
+by configuring its width and height appropriately;
+it does the same to the items it is dividing.
+.SH SOURCE
+.B /appl/lib/dividers.b
+.SH BUGS
+It should not be necessary to inform the
+.B Divider
+widget of size changes.
+.PP
+The event-based mechanism seems somewhat
+contrary to the preferred Limbo way of doing things.
--- /dev/null
+++ b/man/2/draw-0intro
@@ -1,0 +1,268 @@
+.TH DRAW-INTRO 2
+.SH NAME
+draw \- basic graphics facilities module
+.SH SYNOPSIS
+.EX
+include "draw.m";
+draw := load Draw Draw->PATH;
+
+setalpha:  fn(rgba: int, alpha: int): int;
+.EE
+.SH DESCRIPTION
+Inferno's
+.B Draw
+module provides basic graphics facilities, defining drawing
+contexts, images, character fonts, and rectangular geometric operations.
+See
+.IR wmlib (2)
+and
+.IR tk (2)
+for higher level operations, such as windows and menu handling.
+.SS Pixels
+Images are defined on a rectangular region of
+an integer plane with a picture element, or
+.IR pixel ,
+at each grid point.
+Pixel values are integers with between 1 and 32 bits per pixel, and all
+pixels in a given image have the same size, or
+.IR depth .
+Some operations allow images with different depths to be combined,
+for example to do masking.
+Images have one or more channels: colour channels, greyscale channels, colour map indices,
+and others, as described in
+.IR colour (6).
+Each pixel value contains a component of each such channel.
+All pixels in an image have the same size, or
+.IR depth ,
+and the same component structure.
+.PP
+When an image is displayed, the value of each pixel determines the colour
+of the display, according to the interpretation of the image's channels.
+For instance, on `true colour' displays, the display image might contain red, blue and green
+colour channels, and each pixel value will have red, blue and green colour components.
+For displays with only 8 bits per pixel or less,
+Inferno uses a fixed colour map for each display depth (see
+.IR colour (6)).
+Facilities exist in
+.IR draw-display (2)
+to convert between (red, green, blue)
+triplets and colour-mapped pixel values,
+but the mapping is often done automatically by the graphics operations
+when images with different channel structures are combined.
+.PP
+.B Draw
+uses a standard representation of colour constants in calls to create coloured images
+or to initialise new images with a given colour.
+This is referred to as `32-bit RGBA format'.
+Each constant colour is represented as a 32-bit integer, with 8-bit red, blue and green colour components,
+and an 8-bit alpha component, in that order from most to least significant byte.
+.PP
+The RGB values in a colour are
+.I premultiplied
+by the alpha value; for example, a 50% red is
+.B "int 16r7F00007F"
+not
+.BR "int 16rFF00007F" .
+The function
+.B Draw->setalpha
+performs the alpha computation on a given colour
+.I rgba
+in 32-bit RGBA format,
+ignoring its initial alpha value, and returning the
+result of multiplying each colour component by the supplied
+.BR alpha .
+For example, to make a 50% red color value, one could execute
+.B draw->setalpha(Draw->Red,
+.BR 16r7F) .
+.SS Terminology
+.TF Pointer
+.PD
+.TP
+.B Point
+The graphics plane is defined on an integer grid,
+with each
+.RI ( x ",\ " y )
+coordinate identifying
+the upper left corner of the corresponding pixel.
+The plane's origin, (0,\ 0), resides at the upper left corner of the screen;
+.I x
+and
+.I y
+coordinates increase to the right and down.
+The abstract data type,
+.BR Point
+defines a coordinate position.
+.TP
+.B Rect
+The type
+.B Rect
+defines a rectangular region of the plane.
+It comprises two
+.BR Points ,
+.B min
+and
+.BR max ,
+and specifies the region defined by pixels with coordinates
+greater than or equal to
+.B min
+and strictly less than
+.BR max ,
+in both
+.I x
+and
+.IR y .
+This
+.I half-open
+property allows rectangles that share an edge to have equal coordinates on the edge.
+.TP
+.B Display
+The type
+.B Display
+represents a physical display, corresponding to a single connection to a
+.IR draw (3)
+device.
+Besides the image of the display itself, the
+.B Display
+type also stores references to off-screen images, fonts, and so on.
+The contents of such images are stored in the display device, not in the client
+of the display, which affects how they are allocated and used, see for example
+.IR draw-image (2).
+.TP
+.B Screen
+The
+.B Screen
+type is used to manage a set of windows on an image, typically but not necessarily
+that of a display.
+.B Screens
+and hence windows may be built recursively upon windows for
+subwindowing or even on off-screen images.
+.TP
+.B Image
+The
+.B Image
+type provides basic operations on groups of pixels.
+Through a few simple operations, most importantly the
+.B draw
+image combination operator
+(see
+.IR draw-image (2)),
+the
+.B Image
+type provides the building blocks for
+.BR Display ,
+.BR Screen ,
+and
+.BR Font .
+.TP
+.B Font
+A
+.B Font
+defines which character image to draw for each character code value.
+Although all character drawing operations ultimately use the
+.B draw
+primitive on the underlying images,
+.B Fonts
+provide convenient and efficient management of display text.
+Inferno uses the 16-bit Unicode character encoding, so
+.B Fonts
+are managed hierarchically to control their size and to make
+common subsets such as ASCII or Greek efficient in practice.
+See
+.IR draw-font (2),
+.IR utf (6),
+and
+.IR font (6).
+.TP
+.B Context
+A
+.B Context
+provides an interface to the system graphics and interactive devices.
+The system creates this context when it starts an application.
+.TP
+.B Pointer
+The
+.B Pointer
+type conveys information for pointing devices, such as mice or trackballs.
+.SS More about Images
+.PP
+An image occupies a rectangle,
+.BR Image.r ,
+of the graphics plane.
+A second rectangle,
+.BR Image.clipr ,
+defines a clipping region for the image.
+Typically, the clipping rectangle is the same as the basic image,
+but they may differ.
+For example, the clipping region may be made smaller and centered on
+the basic image to define a protected border.
+.PP
+The pixel structure of an
+.B Image
+is stored as
+.B Chans
+value
+.BR Image.chans ;
+the image's pixel depth in bits is stored as integer
+.BR Image.depth .
+.PP
+An image may be marked for replication: when set, the boolean
+.B Image.repl
+causes the image
+to behave as if replicated across the entire integer plane,
+thus tiling the destination graphics area
+with copies of the source image.
+When replication is turned on,
+the clipping rectangle limits the extent of the replication and may
+even usefully be disjoint from
+.BR Image.r .
+See
+.IR draw-image (2)
+for examples.
+.PP
+The
+.B Image
+member functions provide facilities for drawing text and geometric objects,
+manipulating windows, and so on.
+.PP
+Objects of type
+.BR Display ,
+.BR Font ,
+.BR Screen ,
+and
+.B Image
+must be allocated by the member functions;
+if such objects are created with a regular Limbo
+definition, they will not behave properly and may generate run-time errors.
+.PP
+There are no ``free'' routines for graphics objects.
+Instead Limbo's garbage
+collection frees them automatically.
+As is generally so within Limbo,
+one can eliminate references by assigning
+.B nil
+to reference variables, returning from functions
+whose local variables hold references, etc.
+.SH RETURN VALUES
+Most drawing operations operate asynchronously, so they have
+no error return.
+Functions that allocate objects return
+.B nil
+for failure; in such cases the system error string may be
+interrogated (such as by the
+.B %r
+format (see
+.IR sys-print (2))) 
+for more information.
+.SH SOURCE
+.B /libinterp/draw.c
+.br
+.B /libdraw/*.c
+.SH SEE ALSO
+.IR draw (3),
+.IR ir (2),
+.IR prefab-intro (2),
+.IR tk (2),
+.IR wmlib (2),
+.IR colour (6),
+.IR font (6),
+.IR image (6)
--- /dev/null
+++ b/man/2/draw-context
@@ -1,0 +1,167 @@
+.TH DRAW-CONTEXT 2
+.SH NAME
+Context \-
+graphics environment
+.SH SYNOPSIS
+.EX
+include "draw.m";
+draw := load Draw Draw->PATH;
+
+Context: adt
+{
+   display: ref Display;      # frame buffer on which windows reside
+   wm:      chan of (string,
+               chan of (string, ref Wmcontext)); # wmgr connection
+};
+
+# connection to window manager for one or more windows (as Images)
+Wmcontext: adt
+{
+   kbd:     chan of int;         # incoming characters from keyboard
+   ptr:     chan of ref Pointer; # incoming stream of mouse positions
+   ctl:     chan of string;      # commands from wm to application
+   wctl:    chan of string;      # commands from application to wm
+   images:  chan of ref Image;   # exchange of images
+   connfd:  ref Sys->FD;         # connection control
+   ctxt:    ref Draw->Context;
+};
+.EE
+.SH DESCRIPTION
+The
+.B Context
+type encapsulates the data types and channels used by an interactive application,
+and establishes a context for graphics output and window management.
+A reference to the
+.B Context
+is passed as the first argument to an application when it begins execution:
+.PP
+.EX
+include "draw.m"
+
+Command: module
+{
+	init: fn(nil: ref Draw->Context; nil: list of string);
+};
+.EE
+.PP
+Most programs do not create
+.B Contexts
+but instead inherit one from their parent, typically a shell or window system.
+.PP
+.SS Context interface
+.PP
+The following elements of
+.B Context
+are used by
+.IR wm :
+.TF display
+.PD
+.TP
+.B display
+The
+.B Display
+adt to which the application is connected; may be
+.BR nil .
+See
+.IR draw-display (2).
+.TP
+.B wm
+A shared channel through which a private channel
+can be set up with a window manager.
+A client application sends a tuple containing a request string
+(of a format defined by the window manager) and a private reply channel.
+It receives a tuple in reply on that channel; the tuple contains
+a string (eg, an acknowledgement or diagnostic) and a reference to
+a
+.B Wmcontext
+value containing channels by which the application can interact with the
+window manager.
+.SS Wmcontext interface
+The
+.B Wmcontext
+provides a set of channels and file descriptors
+through which the window manager and application interact.
+The elements of the adt are used as follows:
+.PP
+.TF connfd
+.PD
+.TP
+.B kbd
+A channel of type
+.B int
+that delivers keystrokes from a keyboard.
+.TP
+.B ptr
+A channel of type
+.B ref
+.B Pointer
+that delivers events from a pointing device such as a mouse.
+See
+.IR devpointer (2).
+.TP
+.B ctl
+A channel of type
+.B string
+that delivers control messages from the
+window manager to the application.
+.TP
+.B wctl
+A channel of type
+.BR string ,
+which if initialised is
+used by the application to send control messages to the window manager.
+It is not used by the current
+.IR wm (1)
+or
+.IR tkclient (2).
+.TP
+.B images
+A channel of type
+.B ref
+.B Image
+that allows the window manager and application to exchange images
+(eg, when resizing, or to supply a cursor image).
+.TP
+.B connfd
+A file descriptor that can be used to provide per-client connection control.
+For instance, a client can store a file descriptor open on a
+.IR sys-file2chan (2)
+provided by the window manager, and the window manager will shut down
+input to the application when the connection closes (eg, if the application
+exits unexpectedly).
+.B Connfd
+is also used to write requests to the window manager.
+Conventionally a request is a list of words formatted as
+by
+.B quoted
+in
+.IR string (2).
+A request starting with an exclamation mark
+.BR "" ( ! )
+if successful will result in an image being sent down the
+.B image
+channel; the rectangle of the image indicates the rectangle
+that has been allocated on the screen. If only the origin
+is to be changed by the window manager, a nil image is
+sent first (giving the application a chance to suspend operations
+on the window), and then the original image, with its origin set
+appropriately.
+.TP
+.B image
+This is used as described above.
+.TP
+.B ctxt
+Initialised with the
+.B ctxt
+value that provided the initial connection on the
+.B wm
+channel.
+.RE
+.SH SEE ALSO
+.IR wm (1),
+.IR wmlib (2),
+.IR mux (1),
+.IR draw-intro (2),
+.IR ir (2),
+.IR prefab-intro (2),
+.IR tk (2)
--- /dev/null
+++ b/man/2/draw-display
@@ -1,0 +1,389 @@
+.TH DRAW-DISPLAY 2
+.SH NAME
+Display \-
+connection to draw device
+.SH SYNOPSIS
+.EX
+include	"draw.m";
+draw := load Draw Draw->PATH;
+
+Display: adt
+{
+    image:       ref Image;
+    white:       ref Image;
+    black:       ref Image;
+    opaque:      ref Image;
+    transparent: ref Image;
+
+    allocate:    fn(dev: string): ref Display;
+    startrefresh:fn(d: self ref Display);
+    publicscreen:fn(d: self ref Display, id: int):
+                 ref Screen;
+    newimage:    fn(d: self ref Display,
+                 r: Rect, chans: Chans,
+                 repl, rgba: int):
+                 ref Image;
+    color:       fn(d: self ref Display, rgba: int):
+                 ref Image;
+    colormix:    fn(d: self ref Display, one: int, three: int):
+                 ref Image;
+    rgb:         fn(d: self ref Display, red, green, blue: int):
+                 ref Image;
+    namedimage:  fn(d: self ref Display, name: string):
+                 ref Image;
+    open:        fn(d: self ref Display, name: string):
+                 ref Image;
+    readimage:   fn(d: self ref Display, fd: ref Sys->FD):
+                 ref Image;
+    writeimage:  fn(d: self ref Display, fd: ref Sys->FD,
+                 i: ref Image): int;
+    rgb2cmap:    fn(d: self ref Display, red, green, blue: int):
+                 int;
+    cmap2rgb:    fn(d: self ref Display, c: int):
+                 (int, int, int);
+    cmap2rgba:   fn(d: self ref Display, c: int):
+                 int;
+};
+
+Chans: adt
+{
+   mk:    fn(s: string): Chans;
+   text:  fn(c: self Chans): string;
+   eq:    fn(c: self Chans, d: Chans): int;
+   depth: fn(c: self Chans): int;
+};
+.EE
+.SH DESCRIPTION
+The
+.B Display
+type represents a connection to a
+.IR draw (3)
+device.
+This device is the external representation of a physical
+display, such as a CRT, and its associated memory.
+It contains the storage for all images,
+even invisible ones, so all
+.B Image
+objects must be allocated
+through
+.B Display
+member functions.
+Graphics operations that use multiple
+.B Image
+objects may not mix images from different
+.BR Displays .
+.PP
+The pixel channel structure of an
+.B Image
+is determined when the image is allocated (including the image allocated by the system
+to represent a physical display).
+This structure is described externally by a channel format string,
+described in
+.IR colour (6),
+and internally by a value of the
+.B Chans
+adt,
+which is used when allocating new images in the calls below.
+.B Draw
+defines a set of constants of type
+.B Chans
+for common channel types:
+.BR GREY1 ,
+.BR GREY2
+and
+.BR GREY8
+for greyscale (depths 1, 2 and 8);
+.BR CMAP8
+for 8-bit
+.IR rgbv (8)
+colour-mapped images;
+.BR RGB16
+for 16-bit
+.B r5g6b5
+colour images;
+.BR RGB24
+for 24-bit colour;
+and
+.BR RGBA32
+for 24-bit colour with alpha channel.
+.B Chans
+has the following operations:
+.TP 10
+.BI Chans.mk( s )
+Return the
+.B Chans
+value corresponding to the channel format string
+.I s
+(see
+.IR image (6)
+for the syntax of
+.IR s ).
+.TP 10
+.IB c .depth()
+Return the depth in bits of
+.IR c .
+The result is 0 if
+.I c
+is invalid; in particular,
+.BI Chans.mk( s ).depth()
+is zero if
+.I s
+is invalid.
+.TP
+.IB c .text()
+Return the format string corresponding to
+.IR c .
+.TP
+.IB c .eq( d )
+Return true if
+.I d
+has the same channel structure as
+.IR c ;
+return false otherwise.
+.PP
+Colours in the calls below are specified as 32-bit integers (`32-bit RGBA format') containing
+red, green, blue and alpha components as 8-bit values, in order
+from most to least significant byte.
+The 8-bit colour component values express illumination, ranging from 0 (no colour)
+to 255 (saturated).
+For the alpha component, 0 is fully transparent, and 255 is fully opaque.
+.PP
+.B Display
+itself has the following components:
+.PP
+.TP 10
+.B image
+The visible contents of the display;
+draw on
+.B image
+to change the display.
+.TP
+.BR white ", " black
+Replicated images of a single pixel,
+either all ones (white) or all zeroes (black).
+.TP
+.BR opaque ", " transparent
+Replicated images of a single pixel,
+either all ones (fully opaque) or all zeroes (fully transparent).
+Used as mattes for
+basic graphical operations.
+.TP
+.BI allocate( dev )
+Attach to a new display, represented by the
+.IR draw (3)
+device mounted in the specified
+.I dev
+directory.
+If
+.I dev
+is the empty string,
+.B /dev
+is used.
+The return value is
+.B nil
+if the allocation fails.
+.TP
+.IB d .startrefresh()
+After allocating a
+.B Display
+object, the application should spawn a process to call
+.BR startrefresh ;
+this thread will receive and process window refresh events
+from the device.
+.TP
+.IB d .publicscreen( id )
+Create a locally addressable pointer to a public
+.BR Screen ;
+see \f2display-screen\fP(2).
+.TP
+.IB d .newimage( r\fP,\fP\ chans\fP,\fP\ repl\fP,\fP\ rgba )
+Allocate an off-screen
+.BR Image .
+The arguments supply values for the
+.BR Image 's
+.BR r ,
+.BR chans ,
+and
+.BR repl ,
+and an initial pixel value
+.I rgba
+in 32-bit RGBA format,
+used to paint the image
+when created.
+It can be
+.B Draw\->Transparent
+to create a fully transparent image to draw on to form an
+arbitrarily-shaped image or matte.
+If it is
+.BR Draw\->Nofill ,
+the image is not initialised.
+The image's
+.B clipr
+is initialized to
+.BR r .
+.TP
+.IB d .color( rgba )
+Creates a single-pixel,
+replicated off-screen image of the specified colour,
+expressed in 32-bit RGBA format.
+The
+.B Draw
+module defines constants for several dozen colours:
+.RS
+.IP
+.EX
+Opaque:         con int 16rFFFFFFFF;
+Transparent:    con int 16r00000000;
+Black:          con int 16r000000FF;
+White:          con int 16rFFFFFFFF;
+Red:            con int 16rFF0000FF;
+Green:          con int 16r00FF00FF;
+Blue:           con int 16r0000FFFF;
+Cyan:           con int 16r00FFFFFF;
+Magenta:        con int 16rFF00FFFF;
+Yellow:         con int 16rFFFF00FF;
+Grey:           con int 16rEEEEEEFF;
+Paleyellow:     con int 16rFFFFAAFF;
+Darkyellow:     con int 16rEEEE9EFF;
+Darkgreen:      con int 16r448844FF;
+Palegreen:      con int 16rAAFFAAFF;
+Medgreen:       con int 16r88CC88FF;
+Darkblue:       con int 16r000055FF;
+Palebluegreen:  con int 16rAAFFFFFF;
+Paleblue:       con int 16r0000BBFF;
+Bluegreen:      con int 16r008888FF;
+Greygreen:      con int 16r55AAAAFF;
+Palegreygreen:  con int 16r9EEEEEFF;
+Yellowgreen:    con int 16r99994CFF;
+Medblue:        con int 16r000099FF;
+Greyblue:       con int 16r005DBBFF;
+Palegreyblue:   con int 16r4993DDFF;
+Purpleblue:     con int 16r8888CCFF;
+Notacolor:      con int 16rFFFFFF00;
+Nofill:         con Notacolor;
+.EE
+.PP
+The special values
+.BR Draw\->Opaque
+(fully opaque)
+and
+.BR Draw\->Transparent
+(fully transparent)
+are useful as the pixel values for
+.B Display.newimage
+when forming a matte.
+The special value
+.B Draw\->Nofill
+tells
+.B Display.newimage
+not to paint a new image with any colour, leaving it uninitialised.
+.RE
+.TP
+.IB d .colormix( one\fP,\fP\ three )
+Allocate background colours.
+On true color displays, it returns a 1×1 replicated image whose pixel is the result of mixing the two
+colours in a one to three ratio;
+both colours are expressed in 32-bit RGBA format.
+On 8-bit color-mapped displays, it returns a 2×2 replicated image
+with one pixel coloured
+.I one
+and the other three
+with
+.I three
+(after translation through the colour map).
+This simulates a wider range of tones than can
+be represented by a single pixel value on a colour-mapped display.
+.TP
+.IB d .rgb( red\fP,\fP\ green\fP,\fP\ blue )
+Uses the values of red, green, and blue to create
+a single-pixel replicated image of that colour.
+The values are intensities that range from 0 (no colour) to 255 (saturated).
+The alpha component is always 255 (fully opaque).
+.TP
+.IB d .namedimage ( name )
+Returns a reference to the image published as
+.I name
+on display
+.I d
+by
+.B Image.name
+(see
+.IR draw-image (2)).
+This allows unrelated processes to share the image (eg, a window manager and client).
+.TP
+.IB d .open( name )
+Read an image
+description from the named
+file and return an
+.B Image
+holding the picture.
+See
+.IR image (6)
+for more information about image files.
+.TP
+.IB d .readimage( fd )
+Analogous to
+.BR open ,
+but from an open file descriptor rather than a named file.
+.TP
+.IB d .writeimage( fd\fP,\fP\ i )
+Complement of
+.BR readimage :
+write an image file representing
+.B i
+to the open file descriptor.
+.TP
+.IB d .rgb2cmap( red\fP,\fP\ green\fP,\fP\ blue )
+Return the
+.I rgbv
+colour map index (see
+.IR colour (6))
+of the colour that best matches
+the given colour triple.  The values of the components range from
+0 (no colour) to 255 (saturated).
+.TP
+.IB d .cmap2rgb( c )
+Return the colour triple (red, blue, green) corresponding to colour
+map index
+.IR c .
+.TP
+.IB d .cmap2rgba( c )
+Return the 32-bit RGBA representation of the colour corresponding to colour
+map index
+.IR c .
+The alpha component is always 255 (fully opaque).
+.ig
+.TP
+.IB d .cursor(\fIi\fP,\ \fIp\fP)
+Set the current cursor.
+If
+.I i
+is the image of the current display,
+then the graphics cursor will be set
+to its default value, otherwise
+.I i
+must be an image with ldepth 0
+and the following rules apply: the size of the
+cursor will be half the horizontal height of
+.I i
+(subject to system-dependent restrictions on cursor
+size). The top half and the bottom half of the image
+are treated as two independent masks. When the
+cursor is drawn, pixels congruent with non-zero bits
+in the top half are cleared
+and then pixels congruent with non-zero bits in the
+bottom half are set.
+.I P
+gives the offset added to the mouse position when drawing
+the cursor image.
+.TP
+.IB d .cursorset(\fIp\fP)
+Set the position of the mouse cursor to
+.IR p .
+.SH BUGS
+The interface to
+.B cursor
+does not allow the use of colour mouse cursors,
+even on systems that allow them. The interface is likely
+to change in this respect.
+..
--- /dev/null
+++ b/man/2/draw-example
@@ -1,0 +1,116 @@
+.TH DRAW-EXAMPLE 2
+.SH NAME
+draw: example \-
+simple program illustrating image primitives
+.SH DESCRIPTION
+This manual page presents a self-contained simple program that illustrates most
+of the feature of the basic draw library.
+It must be run at the top-level Inferno shell prompt, not within a window system,
+as it establishes its own connection to the display and writes directly
+on the display, not in a private window.
+.PP
+The program exercises the drawing primitives, taking particular care
+to maintain a consistent coordinate system for the combinations of
+images on the display.  Comments in the code introduce each step.
+.PP
+.EX
+implement Test;
+
+include "sys.m";
+
+include "draw.m";
+
+Test: module
+{
+	init:	fn(ctxt: ref Draw->Context, argv: list of string);
+};
+
+init(nil: ref Draw->Context, nil: list of string)
+{
+	sys := load Sys Sys->PATH;
+	draw := load Draw Draw->PATH;
+	Display, Font, Rect, Point, Image, Screen: import draw;
+
+	#
+	# Set up connection to display and initialize colours.
+	#
+	display := draw->Display.allocate(nil);
+	disp := display.image;
+	red := display.color(Draw->Red);
+	blue := display.color(Draw->Blue);
+	white := display.color(Draw->White);
+	yellow := display.color(Draw->Yellow);
+
+	#
+	# Paint the screen red.
+	#
+	disp.draw(disp.r, red, nil, disp.r.min);
+	sys->sleep(5000);
+
+	#
+	# Texture a region with rectangular tiles.
+	#
+	texture := display.newimage(((0,0),(2,3)),
+		disp.chans, 1, Draw->Black);
+	texture.clipr = ((-10000,-10000),(10000,10000));
+	# put something in the texture
+	texture.draw(((0,0),(1,3)), white, nil, (0,0));
+	texture.draw(((0,0),(2, 1)), white, nil, (0,0));
+	# use texture as both source and mask to let
+	# destination colour show through
+	disp.draw(((100,100),(200,300)), texture,
+		texture, (0,0));
+	sys->sleep(5000);
+
+	#
+	# White-out a quarter of the pixels in a region,
+	# to make the region appear shaded.
+	#
+	stipple := display.newimage(((0,0),(2,2)),
+				disp.chans, 1, Draw->Transparent);
+	stipple.draw(((0,0),(1,1)), display.opaque,
+				nil, (0,0));
+	disp.draw(((100,100),(300,250)), white,
+				stipple, (0,0));
+	sys->sleep(5000);
+
+	#
+	# Draw textured characters.
+	#
+	font := Font.open(display, "*default*");
+	disp.text((100,310), texture, (0,0), font,
+			"Hello world");
+	sys->sleep(5000);
+
+	#
+	# Draw picture in elliptical frame.
+	#
+	delight := display.open("/icons/delight.bit");
+	piccenter := delight.r.min.add(delight.r.max).div(2);
+	disp.fillellipse((250,250), 150, 50,
+		delight, piccenter);
+	disp.ellipse((250,250), 150, 50, 3, yellow, (0,0));
+	sys->sleep(5000);
+
+	#
+	# Draw a parabolic brush stroke using an elliptical brush
+	# to reveal more of the picture, consistent with what's
+	# already visible.
+	#
+	dx : con 15;
+	dy : con 3;
+	brush := display.newimage(((0,0),(2*dx+1,2*dy+1)), disp.chans,
+                               0, Draw->Black);
+	brush.fillellipse((dx,dy), dx, dy, display.white,
+				(0,0));
+	for(x:=delight.r.min.x; x<delight.r.max.x; x++){
+		y := (x-piccenter.x)*(x-piccenter.x)/80;
+		y += 2*dy+1;	# so whole brush is visible at top
+		xx := x+(250-piccenter.x)-dx;
+		yy := y+(250-piccenter.y)-dy;
+		disp.gendraw(((xx,yy),(xx+2*dx+1,yy+2*dy+1)),
+                       delight, (x-dx, y-dy), brush,
+			(0,0));
+	}
+}
+.EE
--- /dev/null
+++ b/man/2/draw-font
@@ -1,0 +1,111 @@
+.TH DRAW-FONT 2
+.SH NAME
+Font \-
+character images for Unicode text
+.SH SYNOPSIS
+.EX
+include	"draw.m";
+draw := load Draw Draw->PATH;
+
+Font: adt
+{
+    name:    string;
+    height:  int;
+    ascent:  int;
+    display: ref Display;
+
+    open:    fn(d: ref Display, file:  string):        ref Font;
+    build:   fn(d: ref Display, name, desc: string):   ref Font;
+    width:   fn(f: self ref Font, str: string):        int;
+};
+.EE
+.SH DESCRIPTION
+The
+.B Font
+type defines the appearance of characters drawn with the
+.B Image.text
+primitive (see
+.IR draw-image (2)).
+.B Fonts
+are usually read from files and are selected based on their
+size, their style, the portion of Unicode space they represent,
+and so on.
+.PP
+Fonts are built from a series of subfonts that define contiguous portions
+of the Unicode character space, such as the ASCII or the
+Greek alphabet.
+Font files are textual descriptions of the allocation of characters in
+the various regions of the Unicode space; see
+.IR font (6)
+for the format.
+Subfonts are not visible from Limbo.
+.PP
+A default font, named
+.BR *default* ,
+is always available.
+.PP
+The type incorporates:
+.TP 10
+.BR ascent ", " height
+These define the vertical sizes
+of the font, in pixels.
+The
+.B ascent
+is the distance from the font baseline to the top of
+a line of text;
+.B height
+gives the interline spacing, that is, the distance from
+one baseline to the next.
+.TP
+.B name
+This field
+identifies the font, either
+the name of
+the file from which the font was read, or
+.B
+"*default*"
+for the default font.
+.TP
+.B display
+Tells on which display the font resides.
+.TP
+.BI open( d\fP,\fP\ file\fP)
+The
+.B open
+method creates a
+.B Font
+by reading the contents of the named
+.IR file .
+Fonts are cached, so an open request may return a pointer to an
+existing
+.BR Font ,
+without rereading the file.
+The name
+.B
+"*default*"
+always describes a defined font.
+Fonts are created for an instance of a
+.B Display
+object, even though the creation
+functions are in type
+.BR Font .
+.TP
+.BI build( d\fP,\fP\ name\fP,\fP\ desc )
+.B Build
+creates a
+.B Font
+object by reading the description from the string
+.B desc
+rather than a file.
+.I Name
+specifies the name of the font to be created.
+.TP
+\f2f\fP\f5.width(\fP \f2str\fP \f5)\fP
+The
+.B width
+method returns the width in pixels that
+.I str
+would occupy if drawn by
+.B Image.text
+in the Font
+.IR f .
--- /dev/null
+++ b/man/2/draw-image
@@ -1,0 +1,909 @@
+.TH DRAW-IMAGE 2
+.SH NAME
+Image \-
+pictures and drawing
+.SH SYNOPSIS
+.EX
+include	"draw.m";
+draw := load Draw Draw->PATH;
+
+# compositing operators
+SinD:   con 1<<3;
+DinS:   con 1<<2;
+SoutD:  con 1<<1;
+DoutS:  con 1<<0;
+
+S:      con SinD|SoutD;
+SoverD: con SinD|SoutD|DoutS;
+SatopD: con SinD|DoutS;
+SxorD:  con SoutD|DoutS;
+
+D:      con DinS|DoutS;
+DoverS: con DinS|DoutS|SoutD;
+DatopS: con DinS|SoutD;
+DxorS:  con DoutS|SoutD;
+
+Clear:  con 0;
+
+Image: adt
+{
+    r:          Rect;
+    clipr:      Rect;
+    chans:      Chans;
+    depth:      int;
+    repl:       int;
+
+    display:    ref Display;
+    screen:     ref Screen;
+
+    draw:       fn(dst: self ref Image, r: Rect, src: ref Image,
+                   mask: ref Image, p: Point);
+    drawop:       fn(dst: self ref Image, r: Rect, src: ref Image,
+                   mask: ref Image, p: Point, op: int);
+    gendraw:    fn(dst: self ref Image, r: Rect, src: ref Image,
+                   p0: Point, mask: ref Image, p1: Point);
+    gendrawop:    fn(dst: self ref Image, r: Rect, src: ref Image,
+                   p0: Point, mask: ref Image, p1: Point, op: int);
+    line:       fn(dst: self ref Image, p0,p1: Point,
+                   end0,end1,thick: int,
+                   src: ref Image, sp: Point);
+    lineop:       fn(dst: self ref Image, p0,p1: Point,
+                   end0,end1,thick: int,
+                   src: ref Image, sp: Point, op: int);
+    poly:       fn(dst: self ref Image, p: array of Point,
+                   end0,end1,thick: int,
+                   src: ref Image, sp: Point);
+    polyop:       fn(dst: self ref Image, p: array of Point,
+                   end0,end1,thick: int,
+                   src: ref Image, sp: Point, op: int);
+    bezspline:  fn(dst: self ref Image, p: array of Point,
+                   end0,end1,thick: int,
+                   src: ref Image, sp: Point);
+    bezsplineop:  fn(dst: self ref Image, p: array of Point,
+                   end0,end1,thick: int,
+                   src: ref Image, sp: Point, op: int);
+    fillpoly:   fn(dst: self ref Image, p: array of Point,
+                   wind: int, src: ref Image, sp: Point);
+    fillpolyop:   fn(dst: self ref Image, p: array of Point,
+                   wind: int, src: ref Image, sp: Point, op: int);
+    fillbezspline: fn(dst: self ref Image, p: array of Point,
+                   wind: int, src: ref Image, sp: Point);
+    fillbezsplineop: fn(dst: self ref Image, p: array of Point,
+                   wind: int, src: ref Image, sp: Point, op: int);
+    ellipse:    fn(dst: self ref Image, c: Point, a, b,
+                   thick: int, src: ref Image, sp: Point);
+    ellipseop:    fn(dst: self ref Image, c: Point, a, b,
+                   thick: int, src: ref Image, sp: Point, op: int);
+    fillellipse:fn(dst: self ref Image, c: Point, a, b: int,
+                   src: ref Image, sp: Point);
+    fillellipseop:fn(dst: self ref Image, c: Point, a, b: int,
+                   src: ref Image, sp: Point, op: int);
+    arc:        fn(dst: self ref Image, c: Point, a, b, thick: int,
+                   src: ref Image, sp: Point, alpha, phi: int);
+    arcop:      fn(dst: self ref Image, c: Point, a, b, thick: int,
+                   src: ref Image, sp: Point,
+                   alpha, phi: int, op: int);
+    fillarc:    fn(dst: self ref Image, c: Point, a, b: int,
+                   src: ref Image, sp: Point, alpha, phi: int);
+    fillarcop:  fn(dst: self ref Image, c: Point, a, b: int,
+                   src: ref Image, sp: Point,
+                   alpha, phi: int, op: int);
+    bezier:     fn(dst: self ref Image, a,b,c,d: Point,
+                   end0,end1,thick: int,
+                   src: ref Image, sp: Point);
+    bezierop:     fn(dst: self ref Image, a,b,c,d: Point,
+                   end0,end1,thick: int,
+                   src: ref Image, sp: Point, op: int);
+    fillbezier: fn(dst: self ref Image, a,b,c,d: Point, wind:int,
+                   src: ref Image, sp: Point);
+    fillbezierop: fn(dst: self ref Image, a,b,c,d: Point, wind:int,
+                   src: ref Image, sp: Point, op: int);
+    arrow:      fn(a,b,c: int): int;
+    text:       fn(dst: self ref Image, p: Point, src: ref Image,
+                   sp: Point, font: ref Font, str: string): Point;
+    textop:       fn(dst: self ref Image, p: Point, src: ref Image,
+                   sp: Point, font: ref Font, str: string,
+                   op: int): Point;
+    textbg:     fn(dst: self ref Image, p: Point, src: ref Image,
+                   sp: Point, font: ref Font, str: string,
+                   bg: ref Image, bgp: Point): Point;
+    textbgop:     fn(dst: self ref Image, p: Point, src: ref Image,
+                   sp: Point, font: ref Font, str: string,
+                   bg: ref Image, bgp: Point, op: int): Point;
+    border:     fn(dst: self ref Image, r: Rect, i: int,
+                   src: ref Image, sp: Point);
+    borderop:     fn(dst: self ref Image, r: Rect, i: int,
+                   src: ref Image, sp: Point, op: int);
+
+    readpixels: fn(src: self ref Image, r: Rect,
+                   data: array of byte): int;
+    writepixels:fn(dst: self ref Image, r: Rect,
+                   data: array of byte): int;
+    name:       fn(im: self ref Image, s: string, in: int): int;
+    top:        fn(win: self ref Image);
+    bottom:     fn(win: self ref Image);
+    flush:      fn(win: self ref Image, func: int);
+    origin:     fn(win: self ref Image, log, scr: Point): int;
+};
+.EE
+.SH DESCRIPTION
+The
+.B Image
+type defines rectangular pictures and the methods to draw upon them;
+it is also the building block for higher level objects such as
+windows and fonts.
+In particular, a window is represented as an
+.BR Image ;
+no special operators are needed to draw on a window.
+Off-screen images can have an alpha channel, which gives each pixel an opacity
+factor, which in turn allows non-rectangular images to be defined
+(ie, pixels made fully transparent by the alpha channel
+do not appear when the image is displayed).
+Many drawing operations allow images to be shaped, or partial transparency added, by using the alpha
+channel of another image as a mask (also called a `matte').
+There are two functions in
+.B Image
+for each such operation.
+One has an
+.B op
+suffix, and takes an explicit image compositing operator:
+.BR S ,
+.BR D ,
+.BR SinD , ...,
+.BR SoverD
+and so on.
+(See the Porter-Duff paper mentioned below for the meaning of each operation.)
+The other function (without the
+.B op
+suffix) provides as its default operation the most common operation,
+.BR SoverD ,
+by which the source image, within its matte, is drawn over the destination image.
+.PP
+An
+.B Image
+has a pixel channel structure as described in
+.IR colour (6),
+represented by a value of the
+.B Chans
+adt,
+defined in
+.IR draw-display (2).
+The channel structure of an image is fixed when the image is allocated.
+.PP
+.B Image
+has the following components:
+.TP 10
+.B display
+Tells on which display the image resides.
+.TP
+.B screen
+If the image is a window on a
+.B Screen
+(see
+.IR draw-screen (2)),
+this field refers to that screen; otherwise it is nil.
+.TP
+.B r
+The coordinates of the rectangle in the plane for which the
+.B Image
+has defined pixel values.
+It should not be modified after the image is created.
+.TP
+.B clipr
+The clipping rectangle: operations that read or write
+the image will not access pixels outside
+.BR clipr .
+Frequently,
+.B clipr
+is the same as
+.BR Image.r ,
+but it may differ; see in particular the discussion of
+.BR Image.repl .
+The clipping region may be modified dynamically.
+.TP
+.B chans
+The pixel channel structure of the image; the value
+should not be modified after the image is created.
+.TP
+.B depth
+The number of bits per pixel in the picture:
+it is simply a convenience since it is necessarily equal to
+.BR chans.depth() ,
+and it should not be modified after the image is created.
+.TP
+.B repl
+A boolean value specifying whether the image is tiled to cover
+the plane when used as a source for a drawing operation.
+If
+.B Image.repl
+is zero, operations are restricted to the intersection of
+.B Image.r
+and
+.BR Image.clipr .
+If
+.B Image.repl
+is set,
+.B Image.r
+defines the tile to be replicated and
+.B Image.clipr
+defines the portion of the plane covered by the tiling, in other words,
+.B Image.r
+is replicated to cover
+.BR Image.clipr ;
+in such cases
+.B Image.r
+and
+.B Image.clipr
+are independent.
+.IP
+For example, a replicated image with
+.B Image.r
+set to ((0,\ 0),\ (1,\ 1)) and
+.B Image.clipr
+set to ((0,\ 0),\ (100,\ 100)),
+with the single pixel of
+.B Image.r
+set to blue,
+behaves identically to an image with
+.B Image.r
+and
+.B Image.clipr
+both set to ((0,\ 0),\ (100,\ 100)) and all pixels set to blue.
+However,
+the first image requires far less memory.
+The replication flag may be modified dynamically along with the clipping
+rectangle.
+.TP
+.IB dst .draw( r\fP,\fP\ src\fP,\fP\ mask\fP,\fP\ p\fP )
+.PD0
+.TP
+.IB dst .drawop( r\fP,\fP\ src\fP,\fP\ mask\fP,\fP\ p\fP,\fP\ op )
+.PD
+.B Draw
+is the standard drawing function.
+Only those pixels within the intersection of
+.IB dst .r
+and
+.IB dst .clipr
+will be affected;
+.B draw
+ignores
+.IB dst .repl\fR.
+The operation proceeds as follows
+(this is a description of the behavior, not the implementation):
+.RS
+.IP 1.
+If
+.B repl
+is set in
+.I src
+or
+.IR mask ,
+replicate their contents to fill
+their clip rectangles.
+.IP 2.
+Translate
+.I src
+and
+.I mask
+so
+.I p
+is aligned with
+.IB r .min\fR.
+.IP 3.
+Set
+.I r
+to the intersection of
+.I r
+and
+.IB dst .r\fR.
+.IP 4.
+Intersect
+.I r
+with
+.IB src .clipr\fR.
+If
+.IB src .repl
+is false, also intersect
+.I r
+with
+.IB src .r\fR.
+.IP 5.
+Intersect
+.I r
+with
+.IB mask .clipr\fR.
+If
+.IB mask .repl
+is false, also intersect
+.I r
+with
+.IB mask .r\fR.
+.IP 6.
+For each location in
+.IR r ,
+combine the
+.I dst
+pixel using the alpha value corresponding to the
+.I mask
+pixel.
+If the
+.I mask
+has an explicit alpha channel, the alpha value corresponding to the
+.I mask
+pixel is simply that pixel's alpha channel.
+Otherwise, the alpha value is the NTSC greyscale equivalent of the colour value,
+with white meaning opaque and black transparent.
+.RE
+.IP
+In terms of the Porter-Duff compositing algebra,
+.I draw
+replaces the
+.I dst
+pixels with
+.RI ( src
+in
+.IR mask )
+over
+.IR dst .
+.I Drawop
+is almost identical, but applies the compositing operation
+.I op
+instead:
+.RI ( src
+in
+.IR mask )
+.I op
+.IR dst .
+.IP
+The various
+pixel channel formats
+involved need not be identical.
+If the channels involved are smaller than 8-bits, they will
+be promoted before the calculation by replicating the extant bits;
+after the calculation, they will be truncated to their proper sizes.
+For
+.B draw
+and
+.B gendraw
+only,
+if
+.I mask
+is nil, no mask is used.
+.TP
+\f2dst\fP.\f5gendraw(\f2r\fP, \f2src\fP, \f2p0\fP, \f2mask\fP, \f2p1\fP)\fP
+.PD0
+.TP
+\f2dst\fP.\f5gendrawop(\f2r\fP, \f2src\fP, \f2p0\fP, \f2mask\fP, \f2p1\fP\f5, \f2op\fP)\fP
+.PD
+Similar to \f5draw()\fP except that it aligns the source and mask differently:
+.I src
+is aligned so
+.I p0
+corresponds to
+.IB r . min
+and
+.I mask
+is aligned so
+.I p1
+corresponds to
+.IB r . min .
+For most purposes with simple masks and source images,
+.B draw
+is sufficient, but
+.B gendraw
+is the general operator and the one the other drawing primitives are built upon.
+.TP
+\f2dst\fP.\f5line(\f2p0\fP, \f2p1\fP, \f2end0\fP, \f2end1\fP, \f2thick\fP, \f2src\fP, \f2sp\fP)
+.PD0
+.TP
+\f2dst\fP.\f5lineop(\f2p0\fP, \f2p1\fP, \f2end0\fP, \f2end1\fP, \f2thick\fP, \f2src\fP, \f2sp\fP, \f2op\fP)
+.PD
+.B Line
+draws in
+.I dst
+a line of width
+.RI 1+2* thick
+pixels joining points
+.I p0
+and
+.IR p1 .
+The line is drawn using pixels from the
+.I src
+image aligned so
+.I sp
+in the source corresponds to
+.I p0
+in the destination.
+The line touches both
+.I p0
+and
+.IR p1 ,
+and
+.I end0
+and
+.I end1
+specify how the ends of the line are drawn.
+.B Draw->Endsquare
+terminates the line perpendicularly to the direction of the line; a thick line with
+.B Endsquare
+on both ends will be a rectangle.
+.B Draw->Enddisc
+terminates the line by drawing a disc of diameter
+.RI 1+2* thick
+centered on the end point.
+.B Draw->Endarrow
+terminates the line with an arrowhead whose tip touches the endpoint.
+See the description of
+.B arrow
+for more information.
+.IP
+.B Line
+and the other geometrical operators are equivalent to calls to
+.B gendraw
+using a mask produced by the geometric procedure.
+.TP
+\f2dst\fP.\f5poly(\f2p\fP, \f2end0\fP, \f2end1\fP, \f2thick\fP, \f2src\fP, \f2sp\fP)
+.PD0
+.TP
+\f2dst\fP.\f5polyop(\f2p\fP, \f2end0\fP, \f2end1\fP, \f2thick\fP, \f2src\fP, \f2sp\fP, \f2op\fP)
+.PD
+.B Poly
+draws a general polygon; it
+is equivalent to a series of calls to
+.B line
+joining adjacent points in the array of
+.B Points
+.IR p .
+The ends of the polygon are specified as in
+.BR line ;
+interior lines are terminated with
+.B Enddisc
+to make smooth joins.
+The source is aligned so
+.I sp
+corresponds to
+.IB p [0]\f1.
+.TP
+\f2dst\fP.\f5bezspline(\f2p\fP, \f2end0\fP, \f2end1\fP, \f2thick\fP, \f2src\fP, \f2sp\fP)
+.PD0
+.TP
+\f2dst\fP.\f5bezsplineop(\f2p\fP, \f2end0\fP, \f2end1\fP, \f2thick\fP, \f2src\fP, \f2sp\fP, \f2op\fP)
+.PD
+.B Bezspline
+takes the same arguments as
+.B poly
+but draws a quadratic B-spline (despite its name) rather than a polygon.
+If the first and last points in
+.I p
+are equal, the spline has periodic end conditions.
+.TP
+\f2dst\fP.\f5fillpoly(\f2p\fP, \f2wind\fP, \f2src\fP, \f2sp\fP)
+.PD0
+.TP
+\f2dst\fP.\f5fillpolyop(\f2p\fP, \f2wind\fP, \f2src\fP, \f2sp\fP, \f2op\fP)
+.PD
+.B Fillpoly
+is like
+.B poly
+but fills in the resulting polygon rather than outlining it.
+The source is aligned so
+.I sp
+corresponds to
+.IB p [0]\f1.
+The winding rule parameter
+.I wind
+resolves ambiguities about what to fill if the polygon is self-intersecting.
+If
+.I wind
+is
+.BR ~0 ,
+a pixel is inside the polygon if the polygon's winding number about the point
+is non-zero.
+If
+.I wind
+is 1,
+a pixel is inside if the winding number is odd.
+Complementary values (0 or ~1) cause outside pixels to be filled.
+The meaning of other values is undefined.
+The polygon is closed with a line if necessary.
+.TP
+\f2dst\fP.\f5fillbezspline(\f2p\fP, \f2wind\fP, \f2src\fP, \f2sp\fP)
+.PD0
+.TP
+\f2dst\fP.\f5fillbezsplineop(\f2p\fP, \f2wind\fP, \f2src\fP, \f2sp\fP, \f2op\fP)
+.PD
+.B Fillbezspline
+is like
+.B fillpoly
+but fills the quadratic B-spline rather than the polygon outlined by
+.IR p .
+The spline is closed with a line if necessary.
+.TP
+\f2dst\fP.\f5ellipse(\f2c\fP, \f2a\fP, \f2b\fP, \f2thick\fP, \f2src\fP, \f2sp\fP)
+.PD0
+.TP
+\f2dst\fP.\f5ellipseop(\f2c\fP, \f2a\fP, \f2b\fP, \f2thick\fP, \f2src\fP, \f2sp\fP, \f2op\fP)
+.PD
+.B Ellipse
+draws in
+.I dst
+an ellipse centered on
+.I c
+with horizontal and vertical semiaxes
+.I a
+and
+.IR b .
+The source is aligned so
+.I sp
+in
+.I src
+corresponds to
+.I c
+in
+.IR dst .
+The ellipse is drawn with thickness
+.RI 1+2* thick .
+.TP
+\f2dst\fP.\f5fillellipse(\f2c\fP, \f2a\fP, \f2b\fP, \f2src\fP, \f2sp\fP)
+.PD0
+.TP
+\f2dst\fP.\f5fillellipseop(\f2c\fP, \f2a\fP, \f2b\fP, \f2src\fP, \f2sp\fP, \f2op\fP)
+.PD
+.B Fillellipse
+is like
+.B ellipse
+but fills the ellipse rather than outlining it.
+.TP
+.IB dst .arc(\fIc\fP,\ \fIa\fP,\ \fIb\fP,\ \fIthick\fP,\ \fIsrc\fP,\ \fIsp\fP,\ \fIalpha\fP,\ \fIphi\fP)
+.PD0
+.TP
+.IB dst .arcop(\fIc\fP,\ \fIa\fP,\ \fIb\fP,\ \fIthick\fP,\ \fIsrc\fP,\ \fIsp\fP,\ \fIalpha\fP,\ \fIphi\fP,\ \fIop\fP)
+.PD
+.I Arc
+is like
+.IR ellipse ,
+but draws only that portion of the ellipse starting at angle
+.I alpha
+and extending through an angle of
+.IR phi .
+The angles are measured in degrees counterclockwise from the positive
+.I x
+axis.
+.TP
+.IB dst .fillarc(\fIc\fP,\ \fIa\fP,\ \fIb\fP,\ \fIsrc\fP,\ \fIsp\fP,\ \fIalpha\fP,\ \fIphi\fP)
+.PD0
+.TP
+.IB dst .fillarcop(\fIc\fP,\ \fIa\fP,\ \fIb\fP,\ \fIsrc\fP,\ \fIsp\fP,\ \fIalpha\fP,\ \fIphi\fP,\ \fIop\fP)
+.PD
+.I Fillarc
+is like
+.IR arc ,
+but fills the sector with the source color.
+.TP
+\f2dst\fP.\f5bezier(\f2a\fP, \f2b\fP, \f2c\fP, \f2d\fP, \f2end0\fP, \f2end1\fP, \f2thick\fP, \f2src\fP, \f2sp\fP)
+.PD0
+.TP
+\f2dst\fP.\f5bezierop(\f2a\fP, \f2b\fP, \f2c\fP, \f2d\fP, \f2end0\fP, \f2end1\fP, \f2thick\fP, \f2src\fP, \f2sp\fP, \f2op\fP)
+.PD
+.B Bezier
+draws the
+cubic Bezier curve defined by
+.B Points
+.IR a ,
+.IR b ,
+.IR c ,
+and
+.IR d .
+The end styles are determined by
+.I end0
+and
+.IR end1 ;
+the thickness of the curve is
+.RI 1+2* thick .
+The source is aligned so
+.I sp
+in
+.I src
+corresponds to
+.I a
+in
+.IR dst .
+.TP
+\f2dst\fP.\f5fillbezier(\f2a\fP, \f2b\fP, \f2c\fP, \f2d\fP, \f2wind\fP, \f2src\fP, \f2sp\fP)
+.PD0
+.TP
+\f2dst\fP.\f5fillbezierop(\f2a\fP, \f2b\fP, \f2c\fP, \f2d\fP, \f2wind\fP, \f2src\fP, \f2sp\fP, \f2op\fP)
+.PD
+.B Fillbezier
+is to
+.B bezier
+as
+.B fillpoly
+is to
+.BR poly .
+.TP
+.BI arrow( "a,\ b,\ c" )
+.B Arrow
+is a function to describe general arrowheads; its result is passed as
+.I end
+parameters to
+.BR line ,
+.BR poly ,
+etc.
+If all three parameters are zero, it produces the default arrowhead,
+otherwise,
+.I a
+sets the distance along line from end of the regular line to tip,
+.I b
+sets the distance along line from the barb to the tip,
+and
+.I c
+sets the distance perpendicular to the line from edge of line to the tip of the barb,
+all in pixels.
+.TP
+.IB dst .border( r\fP,\fP\ i\fP,\fP\ src\fP,\fP\ sp\fP)
+.PD0
+.TP
+.IB dst .borderop( r\fP,\fP\ i\fP,\fP\ src\fP,\fP\ sp\fP,\ \f2op\fP)
+.PD
+.I Border
+draws in
+.I dst
+an outline of rectangle
+.I r
+in the given
+.I src
+colour.
+The outline has width
+.IR i ;
+if positive, the border goes inside the rectangle; negative, outside.
+The source is aligned so
+.I sp
+corresponds to
+.IB r .min .
+.TP
+.IB dst .text( p\fP,\fP\ src\fP,\fP\ sp\fP,\fP\ font\fP,\fP\ str\fP)
+.PD0
+.TP
+.IB dst .textop( p\fP,\fP\ src\fP,\fP\ sp\fP,\fP\ font\fP,\fP\ str\fP,\ \f2op\fP)
+.TP
+.IB dst .textbg( p\fP,\fP\ src\fP,\fP\ sp\fP,\fP\ font\fP,\fP\ str\fP,\ \f2bg\fP,\ \f2bgp\fP)
+.PD0
+.TP
+.IB dst .textbgop( p\fP,\fP\ src\fP,\fP\ sp\fP,\fP\ font\fP,\fP\ str\fP,\ \f2bg\fP,\ \f2bgp\fP,\ \f2op\fP)
+.PD
+.B Text
+draws in
+.I dst
+characters specified by the string
+.I str
+and font
+.IR font ;
+it is equivalent to a series of calls to
+.B gendraw
+using source
+.I src
+and masks determined by the character shapes.
+The text is positioned with the left of the first character at
+.IB p .x
+and the top of the line of text at
+.IB p .y\f1.
+The source is positioned so
+.I sp
+in
+.I src
+corresponds to
+.I p
+in
+.IR dst .
+.B Text
+returns a
+.B Point
+that is the position of the next character that would be drawn if the string were longer.
+.IP
+For characters with undefined
+or zero-width images in the font, the character at font position 0 (NUL) is drawn.
+.IP
+.B Text
+draws the text leaving the background intact.
+.B Textbg
+draws the background colour
+.I bg
+behind the characters, with the alignment specified by point
+.IR bgp ;
+it is otherwise the same as
+.BR text .
+.TP
+.IB src .readpixels( r\fP,\fP\ data )
+.B Readpixels
+fills the
+.I data
+array with pixels from the specified rectangle of the
+.I src
+image.
+The pixels are presented one horizontal line at a time,
+starting with the top-left pixel of
+.IR r .
+Each scan line starts with a new byte in the array,
+leaving the last byte of the previous line partially empty, if necessary.
+Pixels are packed as tightly as possible within
+.IR data ,
+regardless of the rectangle being extracted.
+Bytes are filled from most to least significant bit order,
+as the
+.I x
+coordinate increases, aligned so
+.IR x =0
+would appear as the leftmost pixel of its byte.
+Thus, for a 1-bit deep greyscale image,
+the pixel at
+.I x
+offset 165 within the rectangle will be in a
+.I data
+byte with mask value
+.B 16r04
+regardless of the overall
+rectangle: 165 mod 8 equals 5, and
+.B "16r80\ >>\ 5" equals
+.BR 16r04 .
+It is an error to call
+.B readpixels
+with an array that is too small to hold the rectangle's pixels.
+The return value is the number of bytes copied.
+The arrangement of pixels in arrays of bytes is described in
+.IR image (6).
+.TP
+.IB dst .writepixels( r\fP,\fP\ data )
+.B Writepixels
+copies pixel values from the
+.I data
+array to the specified rectangle in the
+.I dst
+image.
+The format of the data is that produced by
+.BR readpixels .
+The return value is the number of bytes copied.
+It is an error to call
+.B writepixels
+with an array that is too small to fill the rectangle.
+.TP
+.IB im .name( s , in )
+Publish the image
+.I im
+on its display under name
+.IR s ,
+if
+.I in is non-zero;
+otherwise,
+.I s
+must be an already published name and it is withdrawn from publication.
+A published image can be retrieved using
+.B Display.namedimage
+(see
+.IR draw-display (2)).
+This function returns -1 on error, typically because the name is already in use
+(for
+.I in
+non-zero), or does not exist
+(for
+.I in
+zero).
+.TP
+.IB win .top()
+If the image
+.I win
+is a window,
+.B top
+pulls it to the ``top'' of the stack of windows on its
+.BR Screen ,
+perhaps obscuring other images.
+If
+.I win
+is not a window,
+.B top
+has no effect.
+.TP
+.IB win .bottom()
+If the image
+.I win
+is a window,
+.B bottom
+pulls it to the ``bottom'' of the stack of windows on its
+.BR Screen ,
+perhaps obscuring it.
+If
+.I win
+is not a window,
+.B bottom
+has no effect.
+.TP
+.IB image .flush( flag )
+The connection to a display has a buffer used to gather graphics requests
+generated by calls to the draw library.
+By default, the library flushes the buffer at the conclusion of any
+call that affects the visible display
+image itself.
+The
+.B flush
+routine allows finer control of buffer management.
+The
+.I flag
+has three possible values:
+.B Flushoff
+turns off all automatic flushing caused by writes to
+.IR image ,
+typically a window or the display image itself
+(buffers may still be written when they fill or when other objects on the display
+are modified);
+.B Flushnow
+causes the buffer to be flushed immediately;
+and
+.B Flushon
+restores the default behaviour.
+.TP
+\f2win\fP.\f5origin(\f2log\fP, \f2scr\fP)
+When a window is created (see
+.IR draw-screen (2)),
+the coordinate system within the window is identical to that of the screen:
+the upper left corner of the window rectangle is its physical location on the display,
+not for example (0, 0).
+This symmetry may be broken, however:
+.B origin
+allows control of the location of the window on the display and the coordinate
+system used by programs drawing on the window.
+The first argument,
+.IR log ,
+sets the upper left corner of the logical (in-window) coordinate system without
+changing the position of the window on the screen.
+The second argument,
+.IR scr ,
+sets the upper left corner of physical (on-screen) coordinate system, that is, the
+window's location on the display, without changing the internal coordinate system.
+Therefore, changing
+.I scr
+without changing
+.I log
+moves the window without requiring the client using it to be notified of the change;
+changing
+.I log
+without changing
+.I scr
+allows the client to set up a private coordinate system regardless of the window's
+location.
+It is permissible for values of
+.I scr
+to move some or all of the window off screen.
+.B Origin
+returns \-1 if the image is not a window or, in the case of changes to
+.IR scr ,
+if there are insufficient resources available to move the window;
+otherwise it returns 1.
+.SH SOURCE
+.B /libdraw
+.SH SEE ALSO
+.IR draw-intro (2),
+.IR draw-display (2),
+.IR draw-point (2),
+.IR draw-rect (2),
+.IR draw-screen (2),
+.IR colour (6),
+.IR image (6),
+.IR font (6)
+.IR utf (6)
+.PP
+T. Porter, T. Duff.
+``Compositing Digital Images'', 
+.I "Computer Graphics
+(Proc. SIGGRAPH), 18:3, pp. 253-259, 1984.
+.SH DIAGNOSTICS
+These functions raise exceptions if argument images are nil,
+except for
+.B draw
+and
+.B gendraw
+where the mask image is optional and may be nil.
+.SH BUGS
+Anti-aliased characters can be drawn by defining a font
+with multiple bits per pixel, but there are
+no anti-aliasing geometric primitives.
--- /dev/null
+++ b/man/2/draw-point
@@ -1,0 +1,66 @@
+.TH DRAW-POINT 2
+.SH NAME
+Point \-
+coordinate position
+.SH SYNOPSIS
+.EX
+include    "draw.m";
+draw := load Draw Draw->PATH;
+
+Point: adt
+{
+    x:    int;
+    y:    int;
+
+    add:  fn(p: self Point, q: Point):  Point;
+    sub:  fn(p: self Point, q: Point):  Point;
+    mul:  fn(p: self Point, i: int):    Point;
+    div:  fn(p: self Point, i: int):    Point;
+    eq:   fn(p: self Point, q: Point):  int;
+    in:   fn(p: self Point, r: Rect):   int;
+};
+.EE
+.SH DESCRIPTION
+.PP
+The
+.B Point
+data type specifies a position in the integer grid.
+.TP 10
+.BR x ", " y
+The coordinate position. The coordinates increase to the right
+.RI ( x )
+and down
+.RI ( y ).
+.TP
+.IB p .add( q )
+Returns the point
+.BI ( p .x+ q .x,
+.IB p .y+ q .y)\fR.
+.TP
+.IB p .sub( q )
+Returns the point
+.BI ( p .x\- q .x,
+.IB p .y\- q .y)\fR.
+.TP
+.IB p .mul( i )
+Returns the point
+.BI ( p .x* i ,
+.IB p .y* i )\fR.
+.TP
+.IB p .div( i )
+Returns the point
+.BI ( p .x/ i ,
+.IB p .y/ i )\fR.
+.TP
+.IB p .eq( q )
+Returns non-zero if the points' coordinates are equal and zero otherwise.
+.TP
+.IB p .in( r )
+Returns non-zero if point
+.I p
+lies within rectangle
+.I r
+and zero otherwise.
+.SH SEE ALSO
+.IR draw-intro (2),
+.IR draw-rect (2)
--- /dev/null
+++ b/man/2/draw-pointer
@@ -1,0 +1,38 @@
+.TH DRAW-POINTER 2
+.SH NAME
+Pointer \-
+state of a pointer device such as a mouse
+.SH SYNOPSIS
+.EX
+include	"draw.m";
+draw := load Draw Draw->PATH;
+
+Pointer: adt
+{
+    buttons:  int;
+    xy:       Point;
+};
+.EE
+.SH DESCRIPTION
+.TP 10
+.B buttons
+Each button on the device corresponds to a bit in
+.BR buttons ;
+zero bits indicate released (or non-existent), and one bits indicate pressed.
+The bits, from least to most significant positions,
+represent the buttons from left to right.
+.TP
+.B xy
+The pointer's screen coordinates.
+.PP
+.IR Mux (1)
+uses the
+.B cptr
+member of the
+.B Draw->Context
+adt to pass pointer events through to applications.
+.SH SEE ALSO
+.IR devpointer (2),
+.B mouse
+in
+.IR tk (2)
--- /dev/null
+++ b/man/2/draw-rect
@@ -1,0 +1,138 @@
+.TH DRAW-RECT 2
+.SH NAME
+Rect \-
+rectangular portion of the plane
+.SH SYNOPSIS
+.EX
+include	"draw.m";
+draw := load Draw Draw->PATH;
+
+Rect: adt
+{
+  min:      Point;
+  max:      Point;
+
+  canon:    fn(r: self Rect):             Rect;
+  dx:       fn(r: self Rect):             int;
+  dy:       fn(r: self Rect):             int;
+  eq:       fn(r: self Rect, s: Rect):    int;
+  Xrect:    fn(r: self Rect, s: Rect):    int;
+  inrect:   fn(r: self Rect, s: Rect):    int;
+  clip:     fn(r: self Rect, s: Rect):    (Rect, int);
+  combine:  fn(r: self Rect, s: Rect):    Rect;
+  contains: fn(r: self Rect, p: Point):   int;
+  addpt:    fn(r: self Rect, p: Point):   Rect;
+  subpt:    fn(r: self Rect, p: Point):   Rect;
+  inset:    fn(r: self Rect; n: int):     Rect;
+};
+.EE
+.SH DESCRIPTION
+The type
+.B Rect
+defines a rectangular portion of the integer grid.
+.TP 10
+.BR min ", " max
+These
+members define the upper left
+.RB ( min )
+and lower right
+.RB ( max )
+points for the rectangle.
+The rectangle contains the pixels
+.BI "min.x\ \fR\(<=\ " "x\ \fR<\ " max.x
+and
+.BI "min.y\ \fR\(<=\ " "y\ \fR<\ " max.y\fR.
+In general,
+.B Rect
+coordinates should be in canonical form:
+.BR min.x "\ \(<=\ " max.x
+and
+.BR min.y "\ \(<=\ " max.y .
+Some functions give undefined results if the
+input rectangles are not canonical.
+.TP
+.IB r .canon()
+Returns a canonical rectangle by sorting the coordinates of
+.IR r .
+.TP
+.IB r .dx()
+Returns the horizontal dimension of
+.IR r .
+.TP
+.IB r .dy()
+Returns the vertical dimension of
+.IR r .
+.TP
+.IB r .eq( s )
+Returns non-zero if the rectangles
+.I r
+and
+.I s
+have the same coordinates and zero otherwise.
+.TP
+.IB r .Xrect( s )
+Returns non-zero if the rectangles
+.I r
+and
+.I s
+intersect and zero otherwise.
+.I Intersection
+means the rectangles share at least one pixel; zero-sized rectangles do not intersect.
+.TP
+.IB r .inrect( s )
+Returns non-zero if
+.I r
+is completely inside
+.I s
+and zero otherwise.
+Rectangles with equal coordinates are considered to be inside each other.
+Zero-sized rectangles contain no rectangles.
+.TP
+.IB r .clip( s )
+Computes the intersection between
+.I r
+and
+.IR s .
+If the input rectangles intersect,
+.B clip
+returns the resulting rectangle
+and a non-zero integer value.
+If the rectangles do not intersect,
+.B clip
+returns
+.I r
+and a zero value.
+.TP
+.IB r .combine( s )
+Returns the smallest rectangle sufficient
+to cover all the pixels of
+.I r
+and
+.IR s .
+.TP
+.IB r .contains( p )
+Returns non-zero if the rectangle
+.I r
+contains the pixel with the coordinates of
+.I p
+and zero otherwise.
+Zero-sized rectangles contain no points.
+.TP
+.IB r .addpt( p )
+Returns the rectangle
+.BI ( r .min.add( p ),
+.IB r .max.add( p ))\fR.
+.TP
+.IB r .subpt( p )
+Returns the rectangle
+.BI ( r .min.sub( p ),
+.IB r .max.sub( p ))\fR.
+.TP
+.IB r .inset( n )
+Returns the rectangle
+.BI ( r .min.add(( n ,
+.IB n )),
+.IB r .max.sub(( n ,
+.IB n ))\fR.
+The result will not be in canonical form if the inset amount is
+too large for the rectangle.
--- /dev/null
+++ b/man/2/draw-screen
@@ -1,0 +1,137 @@
+.TH DRAW-SCREEN 2
+.SH NAME
+Screen \-
+windows and subwindows on a display
+.SH SYNOPSIS
+.EX
+include	"draw.m";
+draw := load Draw Draw->PATH;
+
+Screen: adt
+{
+  id:        int;
+  image:     ref Image;
+  fill:      ref Image;
+  display:   ref Display;
+
+  allocate:  fn(image, fill: ref Image, public: int): ref Screen;
+  newwindow: fn(screen: self ref Screen, r: Rect,
+                backing:int, rgba: int): ref Image;
+  top:       fn(screen: self ref Screen, wins: array of ref Image);
+};
+.EE
+.SH DESCRIPTION
+A
+.B Screen
+is the data structure representing a set of windows visible on a particular
+.B Image
+such as the display or a parent window.
+.TP 10
+.B id
+When a
+.B Screen
+object is allocated (see
+.B allocate
+below), the system assigns it a unique integer,
+.BR id .
+It may be declared ``public'' and accessible to
+arbitrary processes and machines with access to the screen's
+.BR Display .
+The
+.B id
+value may be used as an argument to
+.BR Display.publicscreen ;
+see
+.IR draw-display (2).
+.TP
+.B fill
+When windows are deleted from a screen, the system uses the
+.B fill
+image to repaint the screen's base image.
+.TP
+.B image
+The image upon which the windows appear.
+.TP
+.B display
+The display upon which the screen resides.
+.TP
+.BI allocate( image\fP,\fP\ fill\fP,\fP\ public )
+.B Allocate
+makes a new
+.B Screen
+object.
+The
+.I image
+argument provides the base image on which the windows will be made.
+The
+.I fill
+argument provides the
+.B Screen.fill
+image.
+.B Allocate
+does not affect the contents of
+.IR image ;
+it may be necessary after allocation to paint the base image with
+.BR fill .
+.IP
+Using a non-zero
+.I public
+argument allocates a public screen; zero requests a private screen.
+Public screens may be attached by any process
+on any machine with access to the
+.B Display
+upon which the screen is allocated, enabling remote processes to
+create windows on the screen.
+Knowing only the
+.B id
+field of the original
+.BR Screen ,
+the remote process can call the
+.B Display.publicscreen
+function to acquire a handle to the screen.
+The
+.B image
+and
+.B fill
+fields of a
+.B Screen
+obtained this way are
+.BR nil ,
+but they are not needed for ordinary window management.
+.TP
+.IB screen .newwindow( r\fP,\fP\ backing\fP,\fP\ rgba )
+Allocates a window
+on the display at the specified rectangle with the background
+colour
+.IR rgba ,
+expressed in 32-bit RGBA format; the return value is an
+.B Image
+that may be used like any other.
+The
+.I backing
+parameter can be
+.BR Draw->Refbackup ,
+which provides backing store to store obscured parts of the window when necessary,
+and is used by the window manager and its clients; or
+.BR Draw->Refnone ,
+which provides no refresh, and is used for windows that are transient, or are already protected by backing
+store.
+.TP
+.IB screen .top( wins )
+.B Top
+organizes a group of windows on a screen.
+Given
+.IR wins ,
+an array of window images, it places the
+.I wins[0]
+element at the top,
+.I wins[1]
+behind that, and so on,
+with the last element of
+.I wins
+in front of the all the windows on the screen not in
+.IR wins .
+Images in the array must be on the specified
+.I screen
+.RB ( nil
+elements are ignored).
--- /dev/null
+++ b/man/2/drawmux
@@ -1,0 +1,68 @@
+.TH DRAWMUX 2
+.SH NAME
+drawmux \- multiplex stream of draw requests
+.SH SYNOPSIS
+.EX
+include "drawmux.m";
+drawmux := load Drawmux Drawmux->PATH;
+
+init:	    fn(): (string, ref Draw->Display);
+newviewer:  fn(fd: ref Sys->FD);
+.EE
+.SH DESCRIPTION
+.B Drawmux
+puts itself between the invoking application and
+.B /dev/draw
+(see
+.IR draw (3)),
+so that the contents of the current display can be replicated elsewhere.
+.PP
+.B Init
+returns a new
+.B Display
+(see
+.IR draw-display (2))
+representing a connection to a virtual display device.
+Subsequent
+.B Draw
+requests to that display
+(see
+.IR draw-intro (2))
+are forwarded to the underlying
+.IR draw (3)
+device to appear on the physical display, but can also replicated
+elsewhere, typically in a window on a remote display.
+.PP
+.B Newviewer
+prepares the virtual display end of a
+.B Drawmux
+connection for each new viewer of the display created by a previous call to
+.BR init .
+The file descriptor
+.I fd
+is a connection to the viewer.
+The remote viewer must first write 24 bytes on that connection,
+containing two decimal numbers (each 11 digits and a space),
+giving the identifier of a public screen on the viewer's display, on which
+.B newviewer
+will replicate the
+.B Drawmux
+display,
+and the log (base 2) of the number of bits per pixel for windows created on that screen.
+The remote viewer must then use
+.B Sys->export
+(see
+.IR sys-export (2))
+to export its
+.BR /dev/draw ,
+which
+.B newviewer
+expects to find as the root of the exported hierarchy.
+Subsequent draw operations on the
+.B Drawmux
+display will be replicated on the public screen exported by the viewer.
+Shutting down the connection shuts down the multiplexor for that viewer.
+.SH SEE ALSO
+.IR wm-dmview (1),
+.IR draw-intro (2),
+.IR draw (3)
--- /dev/null
+++ b/man/2/encoding
@@ -1,0 +1,50 @@
+.TH ENCODING 2
+.SH NAME
+Encoding: enc, dec \- encoding and decoding of byte arrays as text
+.SH SYNOPSIS
+.EX
+include "encoding.m";
+base16 := load Encoding Encoding->BASE16PATH;
+base32 := load Encoding Encoding->BASE32PATH;
+base32a := load Encoding Encoding->BASE32APATH;
+base64 := load Encoding Encoding->BASE64PATH;
+
+enc: fn(a: array of byte): string;
+dec: fn(s: string): array of byte
+.EE
+.SH DESCRIPTION
+.B Encoding
+presents a common interface to several ways of encoding binary data (represented in arrays of bytes)
+as printable text, to be included in essentially textual data (such as XML) or
+sent through e-mail systems (as in MIME).
+.PP
+.B Enc
+returns a string with a textual encoding of the binary data in
+.IR a .
+.PP
+.B Dec
+returns an array of bytes containing the binary data encoded in
+.IR s .
+.PP
+Four encodings are provided, including all those defined by RFC3548;
+load the one required from the given path.
+.TP
+.B BASE16PATH
+Encode in base 16, representing each byte as a pair of hexadecimal digits, using upper-case letters (RFC3548).
+.TP
+.B BASE32PATH
+Encode in base 32, encoding 5 bits per character, using upper-case letters, digits `2' to `7', padded with `=', as per RFC3548.
+.TP
+.B BASE32APATH
+Alternative encoding in base 32, encoding 5 bits per character, using digits `2' to `7', letters (either case) except `l' and `o', not padded.
+.TP
+.B BASE64PATH
+Encode in base 64, encoding 6 bits per character, using upper- and lower-case letters, digits, `+' and `/',
+padded with `=' (RFC3548).
+.PP
+When decoding, white space and illegal characters are ignored;
+base 16 and base 32 decoders are case-insensitive.
+.SH SOURCE
+.B /appl/lib/encoding
+.SH SEE ALSO
+.IR convcs (2)
--- /dev/null
+++ b/man/2/env
@@ -1,0 +1,52 @@
+.TH ENV 2
+.SH NAME
+env \- environment module
+.SH SYNOPSIS
+.EX
+include "env.m";
+env = load Env Env->PATH;
+
+getenv: fn(var: string): string;
+setenv: fn(var: string, val: string): int;
+getall: fn(): list of (string, string);
+clone:  fn(): int;
+new:    fn(): int;
+.EE
+.SH DESCRIPTION
+.B Env
+provides an interface to manipulate environment variables which may then be shared 
+between processes.
+.B Getenv
+returns the value of the environment variable
+.I var
+passed as a parameter,
+or
+.B nil
+if the variable is not set. It does
+this by reading the contents of
+.BI /env/ var.
+.B Setenv
+sets the value of the environment variable
+.I var
+to
+.I val.
+The value may be nil to unset the variable. It does this by writing the string
+.I val
+to
+.BI /env/ var.
+The routine returns a negative number if it fails to set the variable for any reason.
+.B Getall
+returns all the variables in the current environment as a list of (variable, value) pairs.
+.B Clone
+copies the current environment and places the process in a new environment group. Changes now
+made to the environment will not affect the environment of other processes.
+.B New
+places the process in a new empty environment group. Changes made in this new environment will
+not affect other processes.
+.SH SOURCE
+.B /appl/lib/env.b
+.SH SEE ALSO
+.IR env (1),
+.IR sys-pctl (2),
+.IR env (3)
+
--- /dev/null
+++ b/man/2/ether
@@ -1,0 +1,77 @@
+.TH ETHER 2
+.SH NAME
+ether \- Ethernet address manipulation
+.SH SYNOPSIS
+.EX
+include "ether.m";
+ether := load Ether Ether->PATH;
+
+Eaddrlen:	con 6;
+
+init:   fn();
+parse:  fn(s: string): array of byte;
+text:   fn(a: array of byte): string;
+addressof:  fn(dev: string): array of byte;
+eqaddr: fn(a, b: array of byte): int;
+.SH DESCRIPTION
+.B Ether
+provides a small set of functions that manipulate Ethernet MAC addresses,
+for the use of the few applications such as
+.IR bootpd (8)
+that must work with them.
+.PP
+.B Init
+must be called before using any other function in the module.
+.PP
+.B Parse
+takes a textual representation of a MAC address in
+.I s
+and returns its internal representation as an array of bytes of length
+.BR Eaddrlen ,
+the form used in packets read and written via
+.IR ether (3).
+.I S
+is a string of twelve hexadecimal digits, corresponding to the six bytes of a MAC address.
+Each pair of digits can optionally be separated by a colon.
+If
+.I s
+is invalid,
+.B parse
+returns nil.
+.PP
+.B Text
+takes an array of bytes of length
+.B Eaddrlen
+and returns its textual representation (a string of twelve hexadecimal digits).
+It returns
+.LR <invalid>
+if the array is less than
+.BR Eaddrlen ,
+but it ignores any bytes beyond that.
+.PP
+.B Addressof
+returns the MAC address of the given Ether device
+.I dev
+(eg,
+.LR /net/ether0 ),
+which it reads from
+.IB dev /addr .
+It returns nil and sets the error string if that file does not exist or is invalid.
+.PP
+.B Eqaddr
+returns true iff
+.I a
+and
+.I b
+are the same address.
+.SH FILES
+.TF /net/etherN/addr
+.TP
+.IB net /ether N /addr
+hardware address of Ether
+.I N
+.SH SOURCE
+.B /appl/lib/ether.b
+.SH SEE ALSO
+.IR ip (2),
+.IR ether (3)
--- /dev/null
+++ b/man/2/exception
@@ -1,0 +1,65 @@
+.TH EXCEPTION 2
+.SH NAME
+exception \- obtain data about exceptions and set exception-handling modes
+.SH SYNOPSIS
+.EX
+include "exception.m";
+exc := load Exception Exception->PATH;
+
+NOTIFYLEADER, PROPAGATE: con iota;
+
+getexc:	fn(pid: int): (int, string, string);
+setexcmode:	fn(mode: int): int;
+.EE
+.SH DESCRIPTION
+.B Exception
+provides functions to fetch data about exceptions in other processes
+and change exception-handling modes.
+It encapsulates exception-related operations on files in
+.IR prog (3).
+.PP
+When a process incurs an exception, the system saves details before processing the exception,
+following Limbo's rules.
+.B Getexc
+returns a tuple
+.BI ( pc,\ module,\ description )
+that describes the most recent exception raised in process
+.IR pid .
+(A process id of -1 is taken to mean the current process.)
+The tuple contains the pc value, the module name, and the exception name.
+The value
+.B "(0,\ nil,\ nil)"
+is returned if no exception has occurred.
+.PP
+By default, when a process incurs an exception that is not handled —
+no
+.B exception
+clause matches that exception —
+the process is stopped in a `Broken' state for debugging,
+and the exception does not propagate further.
+.B Setexcmode
+changes the way that the system handles exceptions for the current process
+and those it later spawns.
+A
+.I mode
+of
+.BR NOTIFYLEADER
+makes the invoking process the leader of the
+current process group for exception handling.
+Subsequently, an exception in any process in the current process group
+will atomically destroy all processes in the group except the leader,
+and the exception will be raised in the leader.
+(This works even when the leader itself incurs the exception.)
+A
+.I mode
+of
+.B PROPAGATE
+instead causes an exception in any process in the group to be raised in
+all processes in the group, allowing them all to initiate local error recovery
+(ie, within each process).
+.SH SOURCE
+.B /appl/lib/exception.b
+.SH DIAGNOSTICS
+Both functions return -1 on error and set the system error string.
+.SH SEE ALSO
+.IR prog (3)
--- /dev/null
+++ b/man/2/factotum
@@ -1,0 +1,366 @@
+.TH FACTOTUM 2
+.SH NAME
+Factotum: attrtext, challenge, copyattrs, delattr, findattr, findattrval, getuserpassd, mount, open, parseattrs, proxy,
+publicattrs, takeattrs, respond, response, rpc, rpcattrs \- client interface to factotum
+.SH SYNOPSIS
+.EX
+include "factotum.m";
+auth := load Factotum Factotum->PATH;
+
+Authinfo: adt{
+    cuid: string;    # ID on caller
+    suid: string;    # ID on server
+    cap:  string;    # capability (only valid on server side)
+    secret: array of byte;   # key for encryption
+};
+
+AuthRpcMax: con \fR...\fP;
+
+init:          fn();
+mount:  fn(fd: ref Sys->FD, mnt: string, flags: int,
+            aname: string, keyspec: string): (int, ref Authinfo);
+
+getuserpasswd:	fn(keyspec: string): (string, string);
+
+Challenge: adt {
+    user:  string;      # user (some protocols)
+    chal:  string;      # challenge string
+};
+
+challenge: fn(keyspec: string): ref Challenge;
+response:  fn(c: ref Challenge, resp: string): ref Authinfo;
+respond:   fn(chal: string, keyspec: string): (string, string);
+
+open:      fn(): ref Sys->FD;
+rpc:       fn(facfd: ref Sys->FD, verb: string, a: array of byte):
+              (string, array of byte);
+proxy:     fn(afd: ref Sys->FD, facfd: ref Sys->FD, arg: string):
+              ref Authinfo;
+rpcattrs:  fn(facfd: ref Sys->FD): list of ref Attr;
+
+Attr: adt {
+    tag:    int;      # Aattr, Aval, or Aquery
+    name:   string;
+    val:    string;
+
+    text:   fn(a: self ref Attr): string;
+};
+
+parseattrs:  fn(s: string): list of ref Attr;
+copyattrs:   fn(l: list of ref Attr): list of ref Attr;
+delattr:     fn(l: list of ref Attr, n: string): list of ref Attr;
+takeattrs:   fn(l: list of ref Attr, names: list of string): list of ref Attr;
+findattr:    fn(l: list of ref Attr, n: string): ref Attr;
+findattrval: fn(l: list of ref Attr, n: string): string;
+publicattrs: fn(l: list of ref Attr): list of ref Attr;
+attrtext:    fn(l: list of ref Attr): string;
+.EE
+.SH DESCRIPTION
+.B Factotum
+interacts with an instance of the authentication agent
+.IR factotum (4)
+to authenticate a client to a server.
+It can also interact with Plan 9's
+.I factotum
+if that is in the name space (as well as or instead of
+.IR factotum (4)).
+.PP
+.B Factotum
+supports both the basic RPC interface to
+.I factotum
+and various high-level operations built on that.
+The high-level functions will be described first.
+.PP
+.B Init
+must be called before any other function.
+.PP
+.B Mount
+is similar to
+.B Sys->mount
+(see
+.IR sys-bind (2)),
+but uses
+.I factotum
+to authenticate,
+if the server requires it.
+.B Factotum->mount
+should be used instead of
+.B Sys->mount
+when mounting file servers that use
+.IR attach (5)
+to authenticate.
+(If the server on
+.I fd
+does not require authentication,
+.B Factotum->mount
+simply calls
+.BR Sys->mount .)
+.B Mount
+returns
+.RI ( v , ai ).
+If the integer
+.I v
+is non-negative, the mount succeeded;
+on error,
+.I v
+is negative, either the authentication or the mount failed,
+.I ai
+is nil, and the system error string contains a diagnostic.
+If the server required authentication and that was successful,
+.I ai
+is a non-nil reference to an
+.B Authinfo
+value containing the agreed user IDs, a capability for
+.IR cap (3)
+that is valid only on the server, and an array of bytes
+containing a shared secret that can be used by client
+and server to create encryption and hashing keys for the conversation.
+.PP
+.B Getuserpasswd
+returns a tuple
+.RI ( user , password )
+containing the values for the
+.B user
+and
+.B !password
+attributes of a factotum entry that has
+.B proto=pass
+and matches the given
+.IR keyspec .
+The tuple values are nil if no entry matches or the caller lacks permission to see them.
+.PP
+The pair of functions
+.B challenge
+and
+.B response
+give servers access to challenge/response protocols provided by
+.IR factotum .
+All such authentication protocols are embedded in an application-level protocol
+such as FTP, IMAP, POP3 and so on, in a way specific to that protocol.
+These functions calculate parameters for application-specific messages.
+.PP
+A server calls
+.B challenge
+to get a challenge value to present to the user who will answer the challenge.
+The server verifies the user's response by calling
+.BR response ,
+which returns an
+.B Authinfo
+value (as described above) if the response is correct.
+The specific challenge/response protocol is selected by the
+.B proto
+attribute in the
+.I keyspec
+for
+.BR challenge ,
+which opens a connection to
+.IR factotum ,
+obtains a string representing a challenge value,
+and returns a reference to a
+.B Challenge
+adt that gives the challenge and a user name.
+Some protocols take the user name from a
+.B user
+attribute in the
+.IR keyspec ,
+and others negotiate it within the authentication protocol.
+In the latter case, the
+.B user
+field of the resulting
+.B Challenge
+gives the name negotiated.
+.PP
+In the other direction, a process uses
+.B respond
+to calculate the correct response to a given challenge.
+.I Chal
+is the challenge presented by a server, and
+.I keyspec
+gives the attributes of the relevant key in the local
+.IR factotum .
+.B Respond
+interacts with
+.I factotum
+and returns a tuple
+.RI ( response,\ user )
+that gives the
+.I response
+string to return to the server, and an optional
+.I user
+name, depending on the protocol.
+On error, the
+.I response
+is nil, and the system error string contains a diagnostic.
+.PP
+.IR Factotum (4)
+represents keys as attribute value pairs in textual form, with space-separated fields.
+A field can be a value-less
+.IR attribute ,
+an
+.IB attribute = value
+pair, or
+.IB attribute ?
+to mark a required attribute with unknown value.
+An
+.I attribute
+name that begins with an exclamation mark
+.RB ( ! )
+is to be considered hidden or secret.
+Values containing white space or special characters can be quoted using the conventions of
+.IR sh (1).
+.PP
+.B Parseattrs
+parses a string
+.I s
+of that form into a list of
+.B Attr
+references.
+Each
+.B Attr
+has a
+.B tag
+that identifies the value as
+.B Aattr
+(a value-less attribute), a
+.B Aval
+(an attribute-value pair), or
+.B Aquery
+(an attribute for which a value is needed).
+Other operations include:
+.TP 15n
+.B copyattrs
+Return a copy of attribute list
+.IR l .
+.TP
+.B delattr
+Return a copy of
+.I l
+removing all attributes with name
+.IR n .
+.TP
+.B takeattrs
+Return the subset of list
+.I l
+that contains only the attributes listed in
+.IR names .
+.TP
+.B findattr
+Return a reference to the first
+.B Attr
+in
+.I l
+with the name
+.IR n .
+.TP
+.B findattrval
+Return the value of the first attribute in
+.I l
+with name
+.IR n ;
+return nil if the attribute is not found or has no value.
+.TP
+.B publicattrs
+Return a copy of
+.I l
+in which all secret attributes have been removed.
+.TP
+.B attrtext
+Return the textual representation of attribute list
+.IR l ,
+acceptable to
+.BR parseattrs .
+.PP
+The low-level interfaces to
+.I factotum
+are as follows.
+.PP
+.B Proxy
+links an authenticating server on
+.I afd
+with the
+.I factotum
+agent on
+.IR facfd .
+Typically
+.I facfd
+is the result of calling
+.BR Factotum->open ,
+which is equivalent to
+.IP
+.EX
+sys->open("/mnt/factotum/rpc", Sys->ORDWR)
+.EE
+.PP
+.I Afd
+is a file descriptor that represents a connection to the service that requires authentication.
+It is typically the result of
+.IR sys-open (2),
+.IR dial (2),
+or
+.IR sys-fauth (2).
+.I Params
+gives any parameters for
+.IR factotum ,
+as a string containing space-separated
+.IB attr = value
+pairs.
+.B Proxy
+ferries messages between the server and
+.I factotum
+until the end of the selected authentication protocol.
+If authentication failed,
+.B proxy
+returns nil; otherwise on success it always returns a non-nil reference to an
+.B Authinfo
+value with contents as above, but if the protocol does not
+supply that authentication data, all the values are nil.
+.PP
+.B Rpcattrs
+returns the initial attributes provided in the
+.B start
+request, and any others added later by the protocol.
+.PP
+.B Rpc
+does one message exchange with the
+.I factotum
+on
+.IR facfd .
+It writes a message containing the given
+.I verb
+and optional binary parameter
+.IR a ,
+and returns
+.RI ( v , a )
+where
+.I v
+is the response string from
+.I factotum
+and
+.I a
+is an optional binary parameter for that response.
+Exceptionally,
+.I v
+is the string
+\f5"rpc failure"\fP
+if communication fails or a message is garbled, or
+\f5"no key"\fP
+if
+.B rpc
+cannot find a suitable key.
+See
+.IR factotum (4)
+for details of the protocol.
+.PP
+.B AuthRpcMax
+is an integer constant giving the maximum size of a message in an
+.B rpc
+exchange.
+.SH SOURCE
+.B /appl/lib/factotum.b
+.SH SEE ALSO
+.IR sys-bind (2),
+.IR sys-fauth (2),
+.IR factotum (4),
+.IR attach (5)
+.SH DIAGNOSTICS
+Functions that  return nil references on error also set the system error string.
--- /dev/null
+++ b/man/2/filepat
@@ -1,0 +1,80 @@
+.TH FILEPAT 2
+.SH NAME
+filepat: expand, match \- file pattern matching
+.SH SYNOPSIS
+.EX
+include "filepat.m";
+filepat := load Filepat Filepat->PATH;
+
+expand:  fn(pat: string): list of string;
+match:   fn(pat, name: string): int;
+.EE
+.SH DESCRIPTION
+.B Expand
+builds a list of file names in alphabetical order that match
+the pattern
+.IR pat .
+The pattern is tokenised using
+.B /
+as a delimiter.
+.PP
+.B Match
+returns 1 if
+.I name
+matches the pattern
+.IR pat ,
+and 0 otherwise.
+.PP
+The
+.I pat
+passed to
+.B match
+and
+.B expand
+may include combinations of the special characters
+.BR * ,
+.BR ? ,
+.BR [ ,
+and
+.BR ] .
+.PP
+The asterisk
+.B *
+matches a string of zero or more characters.
+.PP
+The query
+.B ?
+matches any single character.
+.PP
+The notation
+.BI [ s ]\f1,
+where
+.I s
+is a nonempty string, matches any
+single character in
+.IR s .
+The notation
+.BI [^ s ]\f1,
+where
+.IR s
+is a nonempty string, matches any
+single character not in
+.IR s .
+The characters
+.BR * ,
+.BR ? ,
+and
+.BR [
+have no special meaning within s.
+.PP
+If any character
+is preceded by the character
+.BR \e ,
+that character loses any special meaning
+and is interpreted literally.
+.SH SOURCE
+.B /appl/lib/filepat.b
+.SH SEE ALSO
+.IR sys-tokenize (2),
+.IR readdir (2)
+.IR regex (2)
--- /dev/null
+++ b/man/2/filter
@@ -1,0 +1,112 @@
+.TH FILTER 2
+.SH NAME
+filter \- data processing interface
+.SH SYNOPSIS
+.B
+include "filter.m";
+.br
+.BI "filter := load Filter " filterpath ";"
+
+.EX
+Rq: adt {
+	pick {
+	Start =>
+		pid: int;
+	Fill or Result =>
+		buf: array of byte;
+		reply: chan of int;
+	Finished =>
+		buf: array of byte;
+	Info =>
+		msg: string;
+	Error =>
+		e: string;
+	}
+};
+
+init:  fn();
+start: fn(param: string): chan of ref Rq;
+.EE
+.SH DESCRIPTION
+.B Filter
+defines a general module interface for byte-stream processing.
+This manual page documents how to use the interface, and
+by implication how a
+.B Filter
+module should behave.
+There is a different implementation module for each filter type
+and algorithm (eg, for compression or line encoding).
+All implementations are instances of type
+.BR Filter ,
+loaded from the Dis file
+.IR filterpath ,
+given in the manual page for each standard filter
+(or you can write your own to match this specification).
+For details of each existing filter module, see
+.IR filter-deflate (2)
+and following manual pages.
+.PP
+.B Init
+must be called before any other operation of a filter module.
+.PP
+.B Start
+sets the filter going;
+.I param
+can be used to pass any filter-specific information
+to the processor.
+.B Start
+spawns a new thread to do the processing; it returns
+a channel that is used to receive requests from the
+filter.
+The first message sent is always
+.BR Rq.Start ;
+.I pid
+is the process id of the new process spawned.
+.PP
+Subsequent messages are:
+.TF Rq.Finished
+.PD
+.TP
+.B Rq.Fill
+A request by the filter to fill
+.I buf
+with data.
+The number of bytes that have actually
+been placed in the buffer should be sent
+on
+.IR reply .
+If \-1 is sent, the filter will terminate.
+If the value is 0, the filter will terminate once it has processed
+all its input.
+.TP
+.B Rq.Result
+.I Buf
+contains data from the filter.
+Receipt of the the data must be acknowledged
+by sending a value on
+.IR reply .
+If the value is \-1, the filter will terminate.
+.TP
+.B Rq.Finished
+The filter has finished processing.
+.I Buf
+contains any data that was not consumed
+by the filter. The filter terminates after
+sending this message.
+.TP
+.B Rq.Info
+This message is used to send a string of
+arbitrary information from the filter
+during the course of its processing.
+.TP
+.B Rq.Error
+The filter has encountered an error when processing.
+.I E
+is a string describing the error. The filter terminates
+after sending this message.
+.SH SOURCE
+.B /module/filter.m
+.SH SEE ALSO
+.IR gzip (1),
+.IR filter-deflate (2),
+.IR filter-slip (2)
--- /dev/null
+++ b/man/2/filter-deflate
@@ -1,0 +1,101 @@
+.TH FILTER-DEFLATE 2
+.SH NAME
+deflate, inflate \- data compression filters
+.SH SYNOPSIS
+.EX
+include "filter.m";
+
+deflate := load Filter Filter->DEFLATEPATH;
+inflate := load Filter Filter->INFLATEPATH;
+
+init:  fn();
+start: fn(param: string): chan of ref Rq;
+.EE
+.SH DESCRIPTION
+These implementation modules conform to the
+.B Filter
+module interface for data-processing filters.
+For details of the interface, see
+.IR filter (2).
+.PP
+.I Deflate
+implements gzip-compatible stream compression.
+The
+.I param
+string argument to
+.B start
+can contain one or more of the following option characters:
+.RS 10
+.TP
+.RB ` d '
+Enable debugging output. Each line of debugging output
+is provided in an
+.B Rq.Info
+message.
+.TP
+.RB ` v '
+Enable verbose mode. Each line of verbose output
+is provided in an
+.B Rq.Info
+message.
+.TP
+.RB ` h '
+Add a gzip header and footer to the data. With this flag,
+the data after filtering will be in exactly the same
+format as a gzip file, with accompanying checksum.
+.TP
+.RB ` z '
+Add a zlib header and footer to the data.  The footer contains a
+checksum.
+.TP
+.RB ` 0 '\ to\ ` 9 '
+Specifies the level of compression to be used (9 highest). See
+.IR gzip (1).
+.RE
+.PP
+.I Inflate
+performs the inverse operation to
+.BR deflate .
+If the
+.I param
+argument to
+.B start
+begins with the character
+.RB ` h '
+then the input to the filter is assumed to be in the
+standard gzip file format; if it starts with the character
+.RB ` z '
+it is assumed to be in zlib format; the output will be checked
+for integrity in both cases. While processing a gzip stream, the
+.B Rq.Info
+message is used to transmit some information; the type
+of information is determined by the first word of
+.IR msg ,
+as follows:
+.RS
+.TP
+.B file
+The rest of
+.I msg
+(after a following space) is the name of the original filename
+before compression.
+.TP
+.B mtime
+The rest of
+.I msg
+(after a following space) is the modification time of the
+original file before compression.
+.RE
+.SH SOURCE
+.B /appl/lib/deflate.b
+.br
+.B /appl/lib/inflate.b
+.SH SEE ALSO
+.IR gzip (1),
+.IR filter (2)
+.br
+Internet RFCs
+.IR RFC1950 ,
+.IR RFC1951 ,
+and
+.IR RFC1952 .
--- /dev/null
+++ b/man/2/filter-slip
@@ -1,0 +1,52 @@
+.TH FILTER-SLIP 2
+.SH NAME
+slip \- SLIP data framing protocol
+.SH SYNOPSIS
+.EX
+include "filter.m";
+
+slip := load Filter Filter->SLIPPATH;
+
+init:  fn();
+start: fn(param: string): chan of ref Rq;
+.EE
+.SH DESCRIPTION
+.I Slip
+provides the SLIP data framing protocol described by RFC1055.
+The module is an implementation of the general data-processing module type
+.BR Filter ;
+see
+.IR filter (2)
+for details of that general interface.
+.PP
+.B Init
+must be called before any other operation of the module.
+.PP
+.B Start
+begins SLIP line encoding or decoding via the channel it returns,
+following the protocol of
+.IR filter (2).
+.I Param
+is one of the two following strings:
+.TF encode
+.PD
+.TP
+.B encode
+The filter takes the block of data obtained by each
+.B Rq.Fill
+message, adds framing and escape characters as required,
+and returns the resulting data block in an
+.B Rq.Result
+message.
+.TP
+.B decode
+The filter operates on the data in
+.B Rq.Fill
+messages as a single stream of bytes, providing an
+.B Rq.Result
+message for each framed message found in the stream,
+with escape characters processed to retrieve the original data.
+.SH SOURCE
+.B /appl/lib/slip.b
+.SH SEE ALSO
+.IR filter (2)
--- /dev/null
+++ b/man/2/format
@@ -1,0 +1,249 @@
+.TH FORMAT 2
+.SH NAME
+format \- structured data interchange
+.SH SYNOPSIS
+.EX
+include "sys.m";
+include "bufio.m";
+include "sexprs.m";
+include "format.m";
+format := load Format Format->PATH;
+
+Fmtspec: adt {
+	name:   string;
+	fields: cyclic array of Fmtspec;
+};
+Fmt: adt {
+	kind:   int;
+	fields: cyclic array of Fmt;
+};
+Fmtval: adt {
+	text:   fn(v: self Fmtval): string;
+	val:    ref Sexprs->Sexp;
+	recs:   cyclic array of array of Fmtval;
+};
+Fmtfile: adt {
+	spec:   array of Fmtspec;
+	descr:  array of byte;
+
+	new:    fn(spec: array of Fmtspec): Fmtfile;
+	open:   fn(f: self Fmtfile, name: string):
+               ref Bufio->Iobuf;
+	read:   fn(f: self Fmtfile, iob: ref Bufio->Iobuf):
+               (array of Fmtval, string);
+};
+init:     fn();
+spec2se:  fn(spec: array of Fmtspec): list of ref Sexprs->Sexp;
+spec2fmt: fn(spec: array of Fmtspec): array of Fmt;
+se2fmt:   fn(spec: array of Fmtspec, se: ref Sexprs->Sexp):
+             (array of Fmt, string);
+rec2val:  fn(spec: array of Fmtspec, rec: ref Sexprs->Sexp):
+             (array of Fmtval, string);
+.EE
+.SH DESCRIPTION
+.B Format
+provides support for programs that wish to marshal and unmarshal
+structured data. It is designed to enable a client to
+request that the structure data is provided by the server in a particular
+format, and for the server to be able to check that it is capable of providing
+that format.
+.PP
+A
+.I record
+consists of a set of
+.IR fields ,
+each represented by one element in an
+.IR sexprs (2)
+list. The content of a field can be a simple value, or it can hold a list containing
+any number of sub-records, each holding a set of fields, recursively defined as above.
+.PP
+The
+.B Fmtspec
+type defines the format for a field in a record.
+.I Name
+gives the name of the field, and
+.I fields
+gives the structure of the fields in any sub-records it contains
+(for a field with a simple value, this will be nil).
+Thus an array of
+.B Fmtspec
+values can define the structure of all the fields in a record.
+Here is an example structure specification:
+.IP
+.EX
+Name, Address, Phone: con iota;
+Number, Kind: con iota;
+spec := array[] of {
+	Address => Fmtspec("address", nil),
+	Name => Fmtspec("name", nil),
+	Phone => Fmtspec("phone", array[] of {
+		Kind => Fmtspec("kind", nil),
+		Number => Fmtspec("number", nil),
+	}),
+};
+.EE
+.PP
+By placing each field in the structure specification
+at a known index, a link is made from the symbolic constants in the program
+to the textual field names.
+.PP
+A structure specification may also be represented by
+a list of S-expressions, where each member of the list
+names a field in the structure. If a member is itself
+a list, it specifies a field containing sub-records: its first member gives the name of the field, and
+subsequent members specify the fields in its sub-records.
+For example, the above specification could be written as the
+S-expression:
+.IP
+.EX
+(name address (phone number kind))
+.EE
+.PP
+The
+.B Fmt
+type also defines a record structure, but
+.I "with respect"
+to an existing
+.B Fmtspec
+structure specification.
+An
+.B Fmt
+value represents a field, and
+.I kind
+holds the index of that field in the original structure specification.
+.PP
+.I Se2fmt
+converts from an S-expression list,
+.IR se 
+(a structure specification),
+to a set of
+.B Fmt
+values.
+The specification must be a subset of
+.IR spec ;
+i.e. each field in
+.I se
+must exist in
+.IR spec .
+.I Se2fmt
+returns a tuple
+.BI ( "f, err" ) .
+If the specification is bad,
+.I f
+will be nil, and
+.I err
+describes the error.
+Otherwise, each member of
+.I f
+gives a field specified by
+.IR se .
+For example, given the above structure definition, after executing:
+.IP
+.EX
+	se := Sexp.parse("(address (phone number))").t0;
+	(f, err) := se2fmt(spec, se);
+.EE
+.PP
+.IB f [0].kind
+will hold the symbolic constant
+.BR Address ,
+and
+.IB f [1].fields[0].kind
+will hold
+.BR Number .
+.PP
+.I Spec2se
+converts from a structure representation to its S-expression
+equivalent.
+.I Spec2fmt
+converts it to an array of
+.B Fmt
+structures mirroring it (equivalent to, but more efficient than,
+.BI se2fmt(spec2se( spec )).t0\fR)
+.SS "Data representation"
+The above specifications do not define a format for the
+representation of the actual data. For convenience however,
+.B Format
+defines its own S-expression-based data format.
+In this form, the fields in a record are represented by items
+in an S-expression list. A field containing sub-records is represented as
+a (possibly empty) list containing the sub-records;
+otherwise the value of a field may be an arbitrary S-expression.
+.PP
+For example, a record corresponding to the
+structure specification
+.IP
+.EX
+(name address (phone kind number))
+.EE
+.PP
+might look like:
+.IP
+.EX
+("Jonny Dawes" "24 Crag Lane"
+	((home "+44 7924 43535") (office "034 433 645")))
+.EE
+.PP
+.I Rec2val
+cracks such a record,
+.IR rec ,
+into its component values, checking
+that it is structurally compatible with the specification,
+.IR spec ,
+and returning a tuple
+.BI ( "fields, err" ) .
+If it failed,
+.I fields
+will be nil, and
+.I err
+describes the error. Otherwise
+each member of
+.IR fields ,
+say
+.IR v ,
+holds the value of its equivalent field in
+.IR spec .
+For fields without sub-records,
+.IB v .val
+holds the field's value;
+otherwise
+.IB v .recs
+holds an array with one member for each
+sub-record, each holding an array of fields
+defined recursively as above.
+.PP
+Some file servers
+provide files to which a format specification may
+be written, and which provide a sequence of records
+in that format when read.
+The
+.B Fmtfile
+type provides support for such files.
+It provides the following operations:
+.TP
+.BI Fmtfile.new( spec )
+returns a new
+.B Fmtfile
+value
+that can be used to open files and read records
+conforming to the given
+.IR spec .
+.TP
+.IB f .open(\fIname\fP)
+opens such a file, writes the format specification to it,
+and returns an
+.B Iobuf
+(see
+.IR bufio (2))
+ready for reading the file.
+.TP
+.IB f .read
+reads a record from the file; its return is the
+same as that from
+.BR rec2val .
+.SH SOURCE
+/appl/lib/format.b
+.SH SEE ALSO
+.IR sexprs (2),
+.IR bufio (2),
+.IR sexprs (6)
--- /dev/null
+++ b/man/2/fsproto
@@ -1,0 +1,88 @@
+.TH FSPROTO 2
+.SH NAME
+FSproto: readprotofile, readprotostring \- read file system prototype file
+.SH SYNOPSIS
+.EX
+include "fsproto.m";
+fsproto := load FSproto FSproto->PATH;
+
+Direntry: type (string, string, ref Sys->Dir);
+
+init:   fn(): string;
+
+readprotofile: fn(proto: string, root: string,
+        entries: chan of Direntry,
+        warnings: chan of (string, string)): string;
+
+readprotostring: fn(proto: string, root: string,
+        entries: chan of Direntry,
+        warnings: chan of (string, string));
+.EE
+.SH DESCRIPTION
+.B FSproto
+provides an interface to read a file system prototype file,
+as defined by
+.IR proto (6).
+.PP
+.B Init
+must be called before any other function in the module.
+.PP
+.B Readprotofile
+reads a file system prototype from the file
+.IR proto .
+It traverses the file system, starting at the given
+.IR root ,
+and each file or directory encountered that is mentioned
+in the prototype causes
+.B readprotofile
+to send a
+.B Direntry
+tuple on the channel
+.IR entries .
+The tuple has the form
+.BI ( old , new , dir\fP\f5)\fP
+where
+.I old
+is the name of the current file or directory,
+rooted at
+.IR root ,
+.I new
+is the same file's name
+.I relative
+to
+.IR root ,
+and
+.I dir
+is a reference to the
+.B Sys->Dir
+directory information for
+.IR old ,
+as produced by
+.IR sys-stat (2).
+When all files in
+.I root
+have been examined,
+.B readprotofile
+sends a single tuple with all nil components on
+.IR entries .
+For each error that occurs during processing (eg, unable to open a directory)
+.B readprotofile
+sends a tuple
+.BI ( old , diag )
+on the channel
+.IR warnings ,
+naming the file and giving a diagnostic string,
+but processing continues.
+.PP
+.B Readprotostring
+reads a file system prototype from the string
+.I proto
+itself.
+Otherwise, its operation is the same as
+.BR readprotofile .
+.SH SOURCE
+.B /appl/lib/fsproto.b
+.SH SEE ALSO
+.IR fs (1),
+.IR proto (6),
+.IR mkfs (8)
--- /dev/null
+++ b/man/2/geodesy
@@ -1,0 +1,146 @@
+.TH GEODESY 2
+.SH NAME
+geodesy \- Geodesy module
+.SH SYNOPSIS
+.EX
+include "geodesy.m";
+geodesy := load Geodesy Geodesy->PATH;
+
+# easting, northing
+Eano: adt{
+	e: real;
+	n: real;
+};
+
+# latitude, longitude in radians
+Lalo: adt{
+	la: real;
+	lo: real;
+};
+
+OSGB36, Ireland65, ED50, WGS84, ITRS2000, ETRS89: con iota;
+
+Natgrid, IrishNatgrid, UTMEur, UTM: con iota;
+
+init: fn(d: int, t: int, z: int);
+format: fn(d: int, t: int, z: int);
+os2en: fn(s: string): (int, Eano);
+en2os: fn(en: Eano): string;
+str2lalo: fn(s: string): (int, Lalo);
+lalo2str: fn(lalo: Lalo): string;
+str2en: fn(s: string): (int, Eano);
+en2lalo: fn(en: Eano): Lalo;
+lalo2en: fn(lalo: Lalo): Eano;
+datum2datum: fn(lalo: Lalo, f: int, t: int): Lalo;
+.EE
+.SH DESCRIPTION
+.B Geodesy
+provides routines to deal with (some) terrestrial coordinate systems.
+.PP
+Eastings and northings are defined by the
+.B Eano
+adt and are measured in metres and latitude and longitude (which should be in radians) by the
+.B Lalo
+adt. Latitude is in the range -π/2 to π/2 radians and longitude in the 
+range -π to π radians.
+.PP
+.B OSGB36
+(Ordnance Survey Great Britain 1936),
+.B Ireland65
+(Ireland 1965),
+.B ED50
+(European Datum 1950),
+.B WGS84
+(World Geodetic System 1984),
+.B ITRS2000
+(International Terrestrial Reference System 2000), and
+.B ETRS89
+(European Terrestrial Reference System 1989) are the current datums defined. Helmert transformations are used
+to convert between them. Note that
+.B Ireland65
+and
+.B ED50
+are currently not supported and 
+.B WGS84
+and 
+.B ITRS2000
+are considered equivalent.
+.PP
+.B Natgrid
+(National Grid),
+.B IrishNatgrid
+(Irish National Grid),
+.B UTMEur
+(European Universal Transverse Mercator), and
+.B UTM
+(Universal Transverse Mercator) are the current transverse Mercator projections supported.
+.PP
+The following functions are provided
+.TP
+.B init
+The module should always be initialized first. The parameters are
+the required datum (default WGS84), transverse Mercator projection (default Natgrid) and UTM zone (default 30) if appropriate. Negative values or out of range values are
+ignored.
+.TP
+.B format
+Parameters as above. Alters the current settings at any time though care
+must be taken to ensure existing coordinates are still treated as in their
+original form.
+.TP
+.B os2en
+Converts an Ordnance Survey National Grid reference to an easting, northing.
+The formats XYen, XYeenn, XYeeennn, XYeeeennnn, XYeeeeennnnn, eenn,
+eeennn, eeeennnn, eeeeennnnn and eeeeeennnnnn are allowed. Here X and
+Y are upper case letters, e is an easting digit and n is a northing digit. The
+reference can therefore be rounded to the nearest decakilometre, kilometre,
+hectometre, decametre or metre. The first element of the returned tuple is zero
+if the string has an incorrect format.
+.TP
+.B en2os
+Converts an easting, northing to an OS reference in the form XYeeeeennnnn
+as  above.
+.TP
+.B str2lalo
+Converts a latitude/longitude string to a latitude, longitude. The string
+may have the formats deg[N|S], deg:min[N|S] or deg:min:sec[N|S] for
+latitude, deg[E|W], deg:min[E|W] or deg:min:sec[E|W] for longitude. The
+latitude must come first, then optional white space, then the longitude.
+Degrees, minutes and seconds may be integer or real. Format errors
+are indicated as in
+.B os2en.
+.TP
+.B lalo2str
+Converts a latitude, longitude to string format as above. Seconds are given
+to two decimal places.
+.TP
+.B str2en
+Converts a string in OS or latitude/longitude format as above to an easting, northing.
+Format errors indicated as in
+.B os2en.
+.TP
+.B en2lalo
+Converts an easting, northing to latitude, longitude using the current transverse
+Mercator projection.If the latter is
+.B UTM
+or
+.B UTMEur
+the current zone setting will be used.
+.TP
+.B lalo2en
+Converts latitude, longitude to an easting, northing using the current transverse
+Mercator projection.
+.TP
+datum2datum
+Approximate (Helmert) transformation of a latitude, longitude between any of
+.B OSGB36,
+.B WGS84,
+.B ITRS2000
+or
+.B ETRS89.
+The `from' datum appears first in the parameter list.
+.SH SOURCE
+.B /module/math/geodesy.m
+.br
+.B /appl/math/geodesy.b
+.SH BUGS
+The module is heavily biased towards Great Britain.
--- /dev/null
+++ b/man/2/hash
@@ -1,0 +1,91 @@
+.TH HASH 2
+.SH NAME
+hash, HashTable \- hash table
+.SH SYNOPSIS
+.EX
+include "hash.m";
+hash := load Hash Hash->PATH;
+
+new: fn(size:int):ref HashTable;
+
+HashTable: adt{
+	insert: fn(h:self ref HashTable, key:string, val:HashVal);
+	find: fn(h:self ref HashTable, key:string):ref HashVal;
+	delete: fn(h:self ref HashTable, key:string);
+	all: fn(h:self ref HashTable): list of HashNode;
+};
+HashVal: adt{
+	i: int;
+	r: real;
+	s: string;
+};
+HashNode: adt{
+	key: string;
+	val: ref HashVal;
+};
+fun1, fun2: fn(s:string, n:int):int;
+.EE
+.SH DESCRIPTION
+The hash module provides support for arrays that are indexed by keys of type
+.BR string .
+
+The values may be any combination of
+.BR int ,
+.BR real ,
+or
+.BR string .
+.B New
+creates and returns a new
+.B HashTable
+with
+.I size
+slots. The hashing works best if
+.I size
+is a prime number. The
+.B HashVal
+adt contains the data values of the hash.
+The
+.B HashNode
+adt contains the key/value pair for each element in the table.
+.TP
+.IB ht .insert( "key, value" )
+Adds a new
+.IR key / value
+pair to the table.
+If an element with the same
+.I key
+already exists,
+it will acquire the new
+.IR value .
+.TP
+.IB ht .find( key )
+Search the table for an element with the given
+.I key
+and return the value found; return nil if none was found.
+.TP
+.IB ht .delete( key )
+Removes any element with the given
+.I key
+from the table.
+.TP
+.IB ht .all()
+Returns a list of all key/value pairs stored in the table.
+.PP
+.B Fun1
+and
+.B fun2
+provide access to two different string hashing functions.
+.B Fun1
+is the function used internally;
+.B fun2
+is the same as that used in the Limbo compiler.
+They each compute the hash value of 
+.I s
+and return a value between 0 and
+.IR n \-1.
+.SH SOURCE
+.B /appl/lib/hash.b
+.SH BUGS
+.B HashVal
+should really be a
+.BR pick .
--- /dev/null
+++ b/man/2/ida
@@ -1,0 +1,151 @@
+.TH IDA 2
+.SH NAME
+Ida: Frag, fragment, consistent, reconstruct \- information dispersal algorithm
+.SH SYNOPSIS
+.EX
+include "ida.m";
+ida := load Ida Ida->PATH;
+
+Frag: adt {
+    dlen: int;            # length of original data
+    m:    int;            # minimum pieces for reconstruction
+    a:    array of int;   # encoding array row for this fragment
+    enc:  array of int;   # encoded data
+
+    tag:  array of byte;  # user data, such as SHA1 hash
+    pack: fn(f: self ref Frag): array of byte;
+    unpack: fn(d: array of byte): ref Frag;
+};
+
+init:        fn();
+fragment:    fn(data: array of byte, m: int): ref Frag;
+consistent:  fn(frags: array of ref Frag): array of ref Frag;
+reconstruct: fn(frags: array of ref Frag): (array of byte, string);
+.EE
+.SH DESCRIPTION
+.B Ida
+implements Rabin's Information Dispersal Algorithm (IDA), an effective scheme
+for fault-tolerant storage and message routing.
+The algorithm breaks an array of bytes (for instance a file or a block of data) into
+.I n
+pieces,
+in such a way that the original data can be recovered using only
+.I m
+of them, where
+.I n
+and
+.I m
+are parameters.
+The module provides the fundamental operations.
+.PP
+.B Init
+must be called before invoking any other operation of the module.
+.PP
+.B Fragment
+takes an array of
+.IR data ,
+and
+.IR m ,
+the minimum number of pieces required for reconstruction,
+and returns a reference to a
+.B Frag
+value representing one encoded fragment of the data.
+At least
+.I m
+calls must be made to
+.B fragment
+to obtain enough such fragments to be able to rebuild the data;
+invariably more fragments are generated to provide the desired level of redundancy.
+.PP
+Each fragment
+.B Frag
+has the following components:
+.TP
+.B dlen
+The length in bytes of the
+.IR data .
+.TP
+.B m
+The minimum number of fragments for reconstruction.
+.TP
+.B a
+The row of an \fIm\fP×\fIm\fP encoding matrix that corresponds to this fragment.
+.TP
+.B enc
+The encoded data, represented by an array of integer values.
+For
+.I L
+bytes of input data, the array will have length
+.\\"$ left ceil L over 2m right ceil $.
+.rm 11 
+.ds 12 "\f2L\fP
+.ds 13 "\f12\fP\f2\^m\fP
+.nr 12 0\w'\s+0\*(12'
+.nr 13 0\w'\s+0\*(13'
+.nr 14 \n(12
+.if \n(13>\n(14 .nr 14 \n(13
+.nr 14 \n(14+0.5m
+.ds 12 \v'0.7m'\h'\n(14u-\n(13u/2u'\*(13\v'-0.7m'\
+\h'-\n(13u-\n(12u/2u'\v'-0.6m'\*(12\v'0.6m'\
+\h'-\n(14u-\n(12u/2u+0.1m'\v'-0.3m'\l'\n(14u-0.2m'\h'0.1m'\v'0.3m'
+.ds 12 \^\v'0.13m'\b'\(lc\(bv\(bv'\v'-0.13m'\*(12\v'0.13m'\b'\(rc\(bv\(bv'\v'-0.13m'
+.ds 12 \x'0-0.85m'\*(12\x'1.05m'
+.as 11 \*(12
+.lf 4
+.as 11 ".
+\*(11
+.lf 5
+.PP
+All those values must be stored or transmitted for later use, to reconstruct the data.
+The values in
+.B a
+are in the interval
+.RI "[ 1,\ 65536 ]"
+and those in
+.B enc
+are in the interval
+.RI "[ 0,\ 65536 ]."
+.PP
+.B Reconstruct
+takes an array
+.I frags
+of distinct fragments previously produced by repeated calls to
+.BI fragment( data,\ m )
+and returns a tuple
+.BI ( data,\ err ) .
+Provided at least
+.I m
+suitable fragments are found in
+.IR frags ,
+the
+.I data
+returned will be that originally provided to
+.BR fragment .
+If the parameters of the various fragments in
+.I frags
+disagree, or some other error occurs,
+.I data
+will be nil and
+.I err
+will contain a diagnostic.
+.B Reconstruct
+assumes the fragments it receives are consistent:
+they represent the same encoding parameters, including the value of
+.IR m .
+If it detects an inconsistency, it returns a diagnostic.
+.PP
+.B Consistent
+checks the consistency of a set of fragments, and returns a new subset containing
+only those fragments that agree with the majority in
+.I frags
+on each parameter.
+.SH SOURCE
+.B /appl/lib/ida/ida.b
+.br
+.B /appl/lib/ida/idatab.b
+.SH SEE ALSO
+M Rabin, ``Efficient Dispersal of Information for Security, Load Balancing, and Fault Tolerance'',
+.I JACM
+.BR 36(2) ,
+April 1989,
+pp. 335-348.
--- /dev/null
+++ b/man/2/imagefile
@@ -1,0 +1,157 @@
+.TH IMAGEFILE 2
+.SH NAME
+imagefile: readgif, readjpg, readpicfile, readpng, readxbitmap, remap \- processing external image file formats
+.SH SYNOPSIS
+.EX
+include "imagefile.m";
+
+gifreader  := load RImagefile RImagefile->READGIFPATH;
+jpgreader  := load RImagefile RImagefile->READJPGPATH;
+xbmreader  := load RImagefile RImagefile->READXBMPATH;
+picreader  := load RImagefile RImagefile->READPICPATH;
+pngreader := load RImagefile RImagefile->READPNGPATH;
+
+imageremap := load Imageremap Imageremap->PATH;
+
+Rawimage: adt
+{
+    r:         Draw->Rect;
+    cmap:      array of byte;
+    nchans:    int;
+    chans:     array of array of byte;
+    chandesc:  int;
+
+    init:      fn(bufio: Bufio);
+    read:      fn(fd: ref Bufio->Iobuf): (ref Rawimage, string);
+    readmulti: fn(fd: ref Bufio->Iobuf):
+                (array of ref Rawimage, string);
+};
+
+init:          fn(bufio: Bufio);
+writeimage:    fn(fd: ref Bufio->Iobuf, image: ref Draw->Image)
+                                : string;
+
+remap:         fn(i: ref RImagefile->Rawimage, d: ref Draw->Display,
+                errdiff: int): (ref Draw->Image, string);
+.EE
+.SH DESCRIPTION
+The
+.B Rawimage
+.B adt
+of module
+.B RImagefile
+defines an internal representation
+and routines for reading images such as GIF and JPEG files.
+To read a set of files of a given format, load the appropriate module,
+pass its
+.B init
+function an implementation of the
+.B Bufio
+module, and pass
+.B read
+an
+.B Bufio->Iobuf
+for each file.
+.B Read
+returns a tuple: a
+.B ref
+.B Rawimage
+that holds the image and an error string.
+If the
+.B Rawimage
+is
+.BR nil ,
+the error string will report the reason.
+Files (particularly GIF files) are often incorrectly encoded but yield usable pictures,
+so even if a
+.B Rawimage
+is returned, the error string may still report problems.
+.PP
+Some image file types (eg, GIF) support having several images in a single file.
+They can be read in all at once using
+.BR readmulti ,
+which returns a tuple with the array of images, and an error string as above.
+.PP
+The
+.B Rawimage
+is always defined as one or more bytes per pixel, with
+.B nchans
+channels of data stored in the array
+.BR chans .
+The
+.B chandesc
+field, described below, specifies the contents of
+.BR chans .
+The
+rectangle
+.B r
+describes the shape of the picture.
+.PP
+The
+.B Rawimage
+type can be converted to a regular
+.B Image
+(see
+.IR draw-image (2))
+by calling module
+.BR Imageremap 's
+function
+.B remap.
+.B Remap
+is passed the
+.BR Rawimage ,
+a
+.B Display
+on which to create the image, and a flag that specifies whether to apply Floyd-Steinberg
+error diffusion code to the result, for smoother rendering of colours at the cost of
+some noise in the image.
+.PP
+How to remap is defined by the
+.B RImagefile
+itself: the field
+.B chandesc
+specifies the type of the various
+.B chans
+of data:
+.B RImagefile->CRGB
+specifies a 3-colour RGB image with no colour map;
+.B RImagefile->CY
+a monotone (luminance-only, grey-scale) image with no colour map;
+and
+.B RImagefile->CRGB1
+a single-channel image with RGB colour map in
+.BR cmap .
+The file readers set
+.B chandesc
+as appropriate for the format of the file.
+.PP
+The writing of image files is defined by the module 
+.BR WImagefile.
+To initialize the module, call its
+.B init 
+function with an instance of the
+.B Bufio
+module and pass its 
+.B writeimage
+function a
+.B Bufio->Iobuf
+representing the output stream and an image of type
+.B Draw->Image.
+.PP
+These functions are split into separate modules to give applications control over
+the memory they need to process images.
+.SH SOURCE
+.B /appl/lib/readgif.b
+.br
+.B /appl/lib/readjpg.b
+.br
+.B /appl/lib/readxbitmap.b
+.br
+.B /appl/lib/readpicfile.b
+.br
+.B /appl/lib/readpng.b
+.SH NOTES
+The JPEG reader handles only the Baseline sequential format as defined by
+the JFIF 1.02 file exchange format.
+.PP
+Functions to write these formats are as yet unimplemented.
--- /dev/null
+++ b/man/2/ip
@@ -1,0 +1,334 @@
+.TH IP 2
+.SH NAME
+IP \- Internet Protocol addresses and interfaces
+.SH SYNOPSIS
+.EX
+include "ip.m";
+ip := load IP IP->PATH;
+IPaddr: import IP;
+
+IPaddr: adt {
+   newv6:   fn(nil: array of byte): IPaddr;
+   newv4:   fn(nil: array of byte): IPaddr;
+   copy:    fn(nil: self IPaddr): IPaddr;
+   eq:      fn(nil: self IPaddr, v: IPaddr): int;
+   mask:    fn(nil: self IPaddr, m: IPaddr): IPaddr;
+   isv4:    fn(nil: self IPaddr): int;
+   ismulticast: fn(nil: self IPaddr): int;
+   isvalid: fn(nil: self IPaddr): int;
+
+   v4:      fn(nil: self IPaddr): array of byte;
+   v6:      fn(nil: self IPaddr): array of byte;
+   class:   fn(nil: self IPaddr): int;
+   classmask: fn(nil: self IPaddr): IPaddr;
+
+   parse:     fn(s: string): (int, IPaddr);
+   parsemask: fn(s: string): (int, IPaddr);
+   parsecidr: fn(s: string): (int, IPaddr, IPaddr);
+
+   text:     fn(nil: self IPaddr): string;
+   masktext: fn(nil: self IPaddr): string;
+};
+
+v4bcast, v4allsys, v4allrouter, noaddr, allbits: IPaddr;
+selfv6, selfv4: IPaddr;
+v4prefix: array of byte;
+
+Ifcaddr: adt {
+   ip:   IPaddr;     # local interface address
+   mask: IPaddr;     # subnet mask
+   net:  IPaddr;     # ip & mask
+   preflt:  big;     # preferred life time
+   validlt: big;     # valid life time
+};
+
+Ipifc: adt {
+   index:   int;     # /net/ipifc/N
+   dev:     string;  # bound device
+   addrs:   list of ref Ifcaddr;
+   sendra:  int;     # !=0, send router adverts
+   recvra:  int;     # !=0, receive router adverts
+   mtu:     int;
+   pktin:   big;     # packets in
+   pktout:  big;     # packets out
+   errin:   big;     # input errors
+   errout:  big;     # output errors
+   rp:      IPv6rp;  # IPv6 route advert parameters
+};
+
+IPv6rp: adt {
+   mflag:   int;
+   oflag:   int;
+   maxraint: int;    # max route advert interval
+   minraint: int;    # min route advert interval
+   linkmtu:  int;
+   reachtime: int;
+   rxmitra:   int;
+   ttl:       int;
+   routerlt:  int;
+};
+
+init: fn();
+readipifc:  fn(net: string, index: int): (list of ref Ipifc, string);
+.EE
+.SH DESCRIPTION
+.B IP
+provides data types and operations that manipulate Internet Protocol addresses,
+and operations that convert between internal and textual address forms,
+for both IPv4 and IPv6.
+The textual forms are those defined by RFC2373.
+Briefly, an IPv6 address is 16 bytes, represented textually as a sequence
+of 8 colon-separated hexadecimal values ranging from
+.B 0
+to
+.BR FFFF ,
+except that any one sequence of zeroes can be replaced by
+.BR :: .
+IPv4 addresses are embedded in the IPv6 space with a prefix of either
+.B 0:0:0:0:0:FFFF
+(for addresses of `IPv4-mapped' nodes), or
+.BR 0:0:0:0:0:0
+(for `IPv4-compatible' IPv6 nodes).
+See RFC2373 for the distinction.
+For convenience in working with such addresses, the textual syntax
+allows the last 4 bytes of an IPv6 address to be specified using a
+restricted IPv4 syntax, allowing an address to end in four dot-separated decimal values
+(for example,
+.BR 0:0:0:0:0:FFFF:127.0.0.1
+for the IPv4 loopback address).
+The functions here also accept the common forms of IPv4 syntax with one or two values
+omitted (eg,
+.B 127.1
+for the loopback address),
+and accept IPv4 format for masks
+(eg,
+.BR 255.255.254.0 ).
+.PP
+.B Init
+must be called once before using any value or function of the module.
+.PP
+An Internet address or network mask  is represented by an
+.B IPaddr
+value.
+It has the following operations:
+.TP
+.BI IPaddr.newv6( a )
+Return an
+.B IPaddr
+representing the IPv6 address stored in
+.I a
+as an array of 16 bytes
+.TP
+.BI IPaddr.newv4( a )
+Return an
+.B IPaddr
+representing the IPv4 address stored in
+.I a
+as an array of 4 bytes
+.TP
+.BI IPaddr.parse( s )
+Return a tuple
+.BI ( ok , ip ).
+If
+.I ok
+is 0,
+.I ip
+is an
+.B IPaddr
+representing the address in textual format in the string
+.IR s ,
+which can be in either IPv4 or IPv6 syntax.
+If
+.I ok
+is negative,
+.I s
+was invalid.
+.TP
+.BI IPaddr.parsemask( s )
+.I S
+is a text string defining a mask, in one of three forms:
+.BI / nbits
+where
+.I nbits
+is the number of leading one bits in the mask, ranging from 0 to 128;
+an IPv4 mask (eg,
+.BR 255.255.254.0 );
+or an IPv6 mask.
+Return a tuple
+.BI ( ok , m )\fR.
+If
+.I ok
+is 0,
+.I m
+is an
+.B IPaddr
+representing the mask given by
+.IR s .
+If
+.I ok
+is negative,
+.I s
+was invalid.
+.TP
+.BI IPaddr.parsecidr( s )
+.I S
+is an address-mask combination in Classless Inter-Domain Routing (CIDR) format:
+.IB ip-address / prefix-length,
+where
+.I ip-address
+is an address in any form accepted by
+.B parse
+above, and
+.I prefix-length
+is a decimal value giving the number of leftmost bits in
+.I ip-address
+that form the addressing prefix (ie, subnet prefix).
+Return a tuple
+.BI ( ok , ip , m\fR).
+If
+.I ok
+is 0,
+.I ip
+and
+.I m
+are
+.B IPaddr
+values for the address and mask given by
+.IR s .
+If
+.I ok
+is negative,
+.I s
+is invalid.
+.TP
+.IB ip .copy()
+Return a copy of the value
+.I ip
+.TP
+.IB ip .eq( v )
+Return true (non-zero) if
+.I ip
+represents the same address as
+.IR v ;
+return false (zero) otherwise.
+.TP
+.IB ip .mask( m )
+Return the value
+.BI ( ip & m ) ,
+that is, address
+.I ip
+masked by
+.I m
+.TP
+.IB ip .isv4()
+Return true if
+.I ip
+is an IPv4 address; return false if
+otherwise (it can only be used on a full IPv6 network)
+.TP
+.IB ip .v4()
+Return the value of
+.I ip
+as a 4-byte array in IPv4 representation if it can be so represented;
+if
+.I ip
+is not an IPv4 address, return nil.
+.TP
+.IB ip .v6()
+Return the value of
+.I ip
+in IPv6 addressing format as an array of 16 bytes
+.TP
+.IB ip .class()
+If
+.I ip
+is an IPv4 address, return its class (0 to 3); if it is an IPv6 address, return 6.
+.TP
+.IB ip .classmask()
+If
+.I ip
+is an IPv4 address, return the mask associated with its class; if
+.I ip
+is an IPv6 address, return a mask that is all ones.
+.TP
+.IB ip .ismulticast()
+Return true if
+.I ip
+is a multicast or broadcast address.
+.TP
+.IB ip .isvalid()
+Return true if
+.I ip
+is not the zero address in either IPv4 or IPv6 address space
+.TP
+.IB ip .text()
+Return a textual representation of the address
+.I ip
+in either IPv4 or IPv6 format as appropriate.
+.TP
+.IB ip .masktext()
+Return a textual representation of the address
+.I ip
+as one of: an IPv4 mask;
+.BI / n
+where
+.I n
+is the number of leading 1 bits, as used in CIDR syntax; or
+as a full IPv6 textual address.
+The format used is appropriate to the structure of the value.
+.PP
+The module provides some predefined
+.B IPaddr
+values, mainly for common IPv4 addresses:
+.B v4bcast
+(broadcast address),
+.B v4allsys
+(all hosts multicast address),
+.B v4allrouter
+(all routers multicast address),
+.B selfv4
+(loopback in IPv4),
+.B selfv6
+(loopback in IPv6),
+.B noaddr
+(all zero address, used before a node has an address),
+.B v4noaddr
+(all zero address with IPv4 prefix),
+and
+.B allbits
+(address of all 1 bits).
+The 12-byte IPv6 prefix for IPv4 embedded addresses is provided in
+the array of bytes
+.BR v4prefix .
+.PP
+.B Readipifc
+returns a list of the host's IP interfaces
+and the attributes and addresses of each,
+read from the interface status files in
+.BR /net/ipifc .
+On an error, the string in the returned tuple contains a diagnostic and the list is nil.
+Each interface is represented by an
+.B Ipifc
+value, which contains a list of local interface addresses,
+.BR addrs .
+Each local address is represented by an
+.B Iplifc
+value in that list.
+.SH FILES
+.TF /net/ipifc/N/status
+.TP
+.B /net/ipifc
+directory of IP interfaces
+.TP
+.BI /net/ipifc/ n /status
+status and addresses of interface
+.I n
+.SH SOURCE
+.B /appl/lib/ip.b
+.SH SEE ALSO
+.IR ether (2),
+.IR ip (3)
+.SH BUGS
+.B Readipifc
+is currently only usable in native Inferno.
+That will change shortly.
--- /dev/null
+++ b/man/2/ipints
@@ -1,0 +1,193 @@
+.TH IPINTS 2
+.SH NAME
+ipints: IPint \- `infinite' precision integer utility functions
+.SH SYNOPSIS
+.EX
+include "ipints.m"
+ipints:= load IPints IPints->PATH;
+
+IPint: adt
+{
+  iptob64:   fn(i: self ref IPint): string;
+  iptob64z:  fn(i: self ref IPint): string;
+  b64toip:   fn(str: string)  : ref IPint;
+  iptobytes: fn(i: self ref IPint): array of byte;
+  bytestoip: fn(buf: array of byte): ref IPint;
+  iptobebytes: fn(i: self ref IPint): array of byte;
+  bebytestoip: fn(buf: array of byte): ref IPint;
+  inttoip:   fn(i: int): ref IPint;
+  iptoint:   fn(i: self ref IPint): int;
+  iptostr:   fn(i: self ref IPint, base: int): string;
+  strtoip:   fn(str: string, base: int): ref IPint;
+  random:    fn(nbits: int): ref IPint;
+  copy:      fn(i: self ref IPint): ref IPint;
+  bits:      fn(i:  self ref IPint): int;
+  expmod:    fn(base: self ref IPint, exp, mod: ref IPint):ref IPint;
+  add:  fn(i1: self ref IPint, i2: ref IPint): ref IPint;
+  sub:  fn(i1: self ref IPint, i2: ref IPint): ref IPint;
+  neg:  fn(i: self ref IPint): ref IPint;
+  mul:  fn(i1: self ref IPint, i2: ref IPint): ref IPint;
+  div:  fn(i1: self ref IPint, i2: ref IPint): (ref IPint, ref IPint);
+  mod:  fn(i1: self ref IPint, i2: ref IPint): ref IPint;
+  eq:   fn(i1:  self ref IPint, i2: ref IPint): int;
+  cmp:  fn(i1: self ref IPint, i2: ref IPint): int;
+  shl:  fn(i: self ref IPint, n: int): ref IPint;
+  shr:  fn(i: self ref IPint, n: int): ref IPint;
+  and:  fn(i1: self ref IPint, i2: ref IPint): ref IPint;
+  ori:  fn(i1: self ref IPint, i2: ref IPint): ref IPint;
+  not:  fn(i: self ref IPint): ref IPint;
+  xor:  fn(i1: self ref IPint, i2: ref IPint): ref IPint;
+};
+.EE
+.SH DESCRIPTION
+.B IPint
+provides the following arbitrary-length integer manipulation functions required for cryptographic support in Limbo:
+.TP
+.IB i .iptob64()
+Returns a string that represents a large integer textually in base 64 for convenient transmission over a network connection.
+.TP
+.IB i .iptob64z()
+Returns a similar representation to
+.B iptob64
+but ensures that the top bit of the received value is zero.
+.TP
+.BI b64toip( str )
+Returns the
+.B IPint
+represented by the base-64 encoded
+.IR str .
+.TP
+.IB i .iptobytes()
+Returns an array of bytes representing a large integer. The representation includes both positive and negative numbers.
+.TP
+.BI bytestoip( buf )
+The inverse operation of
+.BR iptobytes .
+.TP
+.IB i .iptobebytes()
+Returns an array of bytes in big-endian format representing the magnitude of a large integer; used for instance to pass a value to
+.IR ssl (3).
+Only non-negative numbers are represented.
+.TP
+.BI bebytestoip( buf )
+The inverse operation of
+.BR iptobebytes .
+.TP
+.BI inttoip( i )
+Creates a new large integer from integer
+.IR i .
+.TP
+.IB i .iptoint()
+Converts a large integer
+.I i
+to an
+.BR int ;
+returns 0 on error.
+.TP
+.IB i .iptostr( base )
+Converts a large integer to a string in base
+.IR base ;
+returns nil on error.
+Only the bases 10, 16, 32, and 64 are
+supported.  Anything else defaults to 16.
+.TP
+.BI strtoip( str , base )
+Converts a string
+.I str
+representing a number in in base
+.I base
+to a large integer; returns nil on error.
+Only the bases 10, 16, 32, and 64 are
+supported.
+.TP
+.BI random( nbits )
+Returns a large random number of length at most
+.IR minbits .
+The largest number allowed in the current implementation is
+2^8192-1 .
+The seed for the generator is obtained by duelling clocks.
+.TP
+.IB i .copy()
+Returns a reference to the same value as
+.IR i .
+.TP
+.IB i .bits()
+Returns the number of bits of precision of
+.IR i .
+.TP
+.IB base .expmod( "exp , mod" )
+Returns
+.BI ( base ** exp ") mod " mod.
+.TP
+.IB i1 .add( i2 )
+Returns
+.RI ( i1 + i2 ).
+.TP
+.IB i1 .sub( i2 )
+Returns
+.RI ( i1 - i2 ).
+.TP
+.IB i1 .mul ( i2 )
+Returns
+.IR i1*i2 .
+.TP
+.IB i1 .div ( i2 )
+Returns
+.RI ( i1 / i2,
+.IR i1 rem i2 ).
+.TP
+.IB i1 .mod ( i2 )
+Returns
+.RI ( i1 mod i2 ).
+.TP
+.IB i1 .eq( i2 )
+Returns 1 if
+.I i1
+and
+.I i2
+are equal; 0 otherwise.
+.TP
+.IB i1 .cmp( i2 )
+Compares two large integers, returning 1 if
+.I i1
+is larger,
+-1 if
+.I i2
+is larger, and 0 if they are equal.
+.TP
+.IB i .shl( n )
+Returns
+.IR i << n
+.TP
+.IB i .shr( n )
+Returns
+.IR i >> n
+.TP
+.IB i1 .and( i2 )
+Returns
+.IR i & n ,
+bitwise AND
+.TP
+.IB i1 .ori( i2 )
+Returns
+.IR i | n ,
+bitwise inclusive-OR
+(it is
+.B ori
+because plain
+.B or
+is a Limbo keyword)
+.TP
+.IB i .not()
+Returns
+.RI ~ i ,
+bitwise ones-complement
+.TP
+.IB i1 .xor( i2 )
+Returns
+.IR i ^ n ,
+bitwise exclusive-OR
+.SH SOURCE
+.B /libinterp/ipint.c
+.br
+.B /libmp
--- /dev/null
+++ b/man/2/ipints-genprime
@@ -1,0 +1,112 @@
+.TH IPINTS-GENPRIME 2
+.SH NAME
+ipints: genprime, gensafeprime, genstrongprime, DSAprimes, probably_prime  \- prime number generation
+.SH SYNOPSIS
+.EX
+include "ipints.m";
+ipints := load IPints IPints->PATH;
+IPint: import ipints;
+
+probably_prime: fn(n: ref IPint, nrep: int): int;
+
+genprime: fn(nbits: int, nrep: int): ref IPint;
+gensafeprime: fn(nbits: int, nrep: int): (ref IPint, ref IPint);	# p, alpha
+genstrongprime: fn(nbits: int, nrep: int): ref IPint;
+DSAprimes: fn(): (ref IPint, ref IPint, array of byte);	# q, p, seed
+.EE
+.SH DESCRIPTION
+This set of functions in
+.B IPints
+(see
+.IR ipints (2))
+helps Limbo applications
+generate and test large prime numbers with relative efficiency.
+The numbers are all represented by
+.BR IPint .
+.PP
+.I Probably_prime
+uses the Miller-Rabin test to test
+.IR n .
+It returns true (non-zero) if
+.I P
+is probably prime.  The probability of
+.I n
+not being prime is
+1/4**\fInrep\fR.
+If
+.I probably_prime
+returns false (zero),
+.I n
+is certainly not prime.
+.PP
+.I Genprime
+returns a random prime of length
+.IR nbits .
+Since it uses the Miller-Rabin test,
+.I nrep
+is the repetition count passed to
+.IR probably_prime .
+.PP
+.I Gensafeprime
+returns a tuple
+.BI ( p,\ alpha ),
+where
+.I p
+is a prime of length
+.I nbits
+and
+.I alpha
+is a generator of the multiplicative group of integers mod \fIp\fR;
+there is a prime \fIq\fR such that \fIp-1=2*q\fR.
+.PP
+.I Genstrongprime
+returns a prime
+.I p
+with the following properties:
+.IP \-
+(\fIp\fR-1)/2 is prime.  Therefore
+.IR p -1
+has a large prime factor,
+.IR p '.
+.IP \-
+.IR p '-1
+has a large prime factor
+.IP \-
+.IR p +1
+has a large prime factor
+.PP
+.I DSAprimes
+uses the NIST recommended algorithm for generating DSA primes and
+returns a tuple
+.BI ( q,\ p,\ seed ) ,
+where
+.I p
+and
+.I q
+are primes, and
+.I q
+divides
+.IR p -1.
+The random
+.I seed
+used is also returned, so that sceptics
+can later confirm the computation.
+.SH SOURCE
+.B /libinterp/ipint.c
+.br
+.B /libsec/port/probably_prime.c
+.br
+.B /libsec/port/dsaprimes.c
+.br
+.B /libsec/port/genprime.c
+.br
+.B /libsec/port/gensafeprime.c
+.br
+.B /libsec/port/genstrongprime.c
+.br
+.SH SEE ALSO
+.IR crypt-intro (2),
+.IR crypt-crypt (2),
+.IR crypt-dsagen (2),
+.IR crypt-gensk (2),
+.IR ipints (2)
--- /dev/null
+++ b/man/2/ir
@@ -1,0 +1,254 @@
+.TH IR 2
+.SH NAME
+ir \- infrared remote control module
+.SH SYNOPSIS
+.EX
+include     "ir.m";
+
+ir    := load Ir Ir->PATH; # for real remotes
+simir := load Ir Ir->SIMPATH; # for keyboard simulator
+
+init:       fn(irc: chan of int, pidc: chan of int): int;
+translate:  fn(key: int): int;
+.EE
+.SH DESCRIPTION
+Programs running with the Prefab
+toolkit (see
+.IR prefab-intro (2))
+are controlled by an infrared remote
+control device.
+If such a device is not present, the system may simulate
+it from the keyboard by loading the module in file
+.BR Ir->SIMPATH .
+Although originally designed for use with Prefab,
+these modules are general enough to be used directly by non-Prefab
+applications.
+.PP
+The
+.B Ir
+module defines codes
+for representing the remote control keys.
+Whether the remote is real or simulated, the
+.B init
+function does the appropriate actions to initialize the device, and then spawns
+a process to return the codes on the
+.I irc
+channel.
+The process ID of that process
+is sent on the channel
+.I pidc
+when the process starts;
+.BR init 's
+caller must receive it.
+It can be used to kill the controlling process when the application finishes.
+.PP
+The codes are:
+.PP
+.TP
+.BR Ir\->ChanUP ", " Ir\->ChanDN
+The Channel-Up and Channel-Down buttons.
+The keyboard equivalents are
+.B r
+and
+.BR c .
+.TP
+.B Ir\->Enter
+The Enter button.
+The keyboard equivalent is the \s-1SPACE\s0 bar.
+.TP
+.B Ir\->EOF
+An end of file from the remote device.
+After sending one, no more codes will be sent on
+.IR irc .
+.TP
+.B Ir\->Error
+An unknown or invalid input from the remote device.
+.TP
+.BR Ir\->FF ", " Ir\->Rew
+The Fast-Forward and Rewind buttons.
+The keyboard equivalents are
+.B k
+and
+.BR j .
+.TP
+.B Ir\->Mute
+The Mute button.
+There is no keyboard equivalent.
+.TP
+.B Ir\->Power
+The Power button.
+The keyboard equivalent is the
+.B Delete
+key.
+.TP
+.B Ir\->Rcl
+The Recall button.
+The keyboard equivalent is
+.BR x .
+.TP
+.B Ir\->Record
+The Record button.
+There is no keyboard equivalent.
+.TP
+.B Ir\->Select
+The Select button.
+The keyboard equivalent is the
+.B Return
+or
+.B Enter
+key.
+.TP
+.BR Ir\->Up ", " Ir\->Dn
+The Up and Down buttons.
+The keyboard equivalents are
+.B i
+and
+.BR m .
+.TP
+.BR Ir\->VolUP ", " Ir\->VolDN
+The Volume-Up and Volume-Down buttons.
+The keyboard equivalents are
+.B t
+and
+.BR v .
+.TP
+.B
+Ir\->Zero\fR,\fP Ir\->One\fR,\fP Ir\->Two\fR, etc.
+.PD
+The digit buttons, 0 through 9.
+The keyboard equivalents are the corresponding numeral keys.
+.PP
+The
+.B translate
+function converts the device's raw codes into the constants defined by
+the module.
+For example, with the simulated remote control,
+.B translate('3')
+returns
+.BR Ir->Three .
+.B Translate
+is only necessary for programs that wish to manage their own simulation of the remote.
+.PP
+Programs
+that  drive the remote control directly,
+must load the appropriate Ir implementation module and initialise it.
+The following example uses the absence of a simulator module
+to infer that a real remote control is available.
+.PP
+.EX
+implement Irtest;
+
+include "sys.m";
+include "draw.m";
+include "ir.m";
+
+Irtest: module
+{
+   init:  fn(ctxt: ref Draw->Context, argv: list of string);
+};
+
+init(nil: ref Draw->Context, nil: list of string)
+{
+    sys := load Sys Sys->PATH;
+
+    # First try the keyboard Ir simulator.
+    # If that is not present, use Ir directly.
+
+    ir := load Ir Ir->SIMPATH;
+    if(ir == nil)
+        ir = load Ir Ir->PATH;
+    if(ir == nil){
+        sys->print("Ir module not loaded: %r\en");
+        return;
+    }
+    irc := chan of int;
+    pidc := chan of int;
+    if(ir->init(irc, pidc) < 0){
+        sys->print("Can't initialize Ir device: %r\en");
+        return;
+    }
+    pid := <-pidc;
+    while((irraw := <-irc) != Ir->EOF) {
+        irval := ir->translate(irraw);
+        if(irval == Ir->Power)
+            break;
+        sys->print("command %d -> %d\en", irraw, irval);
+    }
+    kill(pid);
+}
+.EE
+.PP
+.IR Mux (1)
+provides one model for the use of an infrared remote control to control
+a group of applications.
+.B Init
+is invoked once by
+.IR mux ,
+and the codes then multiplexed between its applications as follows.
+.I Mux
+creates a graphics context for each application
+(see
+.IR draw-context (2)).
+This context includes channels to the
+.B mux
+program and to the
+.B Ir
+device:
+.B Draw->Context.ctomux
+and
+.BR Draw->Context.cinput .
+Applications do not see the
+.B Ir->Rcl
+command.
+Instead,
+.I mux
+program intercepts it and reactivates its own menu.
+The following example establishes communication with
+.I mux
+and then reads
+.B Ir
+commands until it see
+.BR Ir->Enter .
+.PP
+.EX
+implement Command;
+
+include "sys.m";
+include "draw.m";
+include "ir.m";
+
+Command: module
+{
+  init: fn(ref Draw->Context; list of string);
+};
+
+init(ctxt: ref Draw->Context; argv: list of string)
+{
+  sys := load Sys Sys->PATH;
+
+  # Tell mux to start sending input.
+  ctxt.ctomux <-= Draw->AMstartinput;
+  for(;;) {
+    key := <-ctxt.cinput;
+    sys->print("command %d\en", key);
+    if(key == Ir->Enter)
+      break;
+  }
+
+  #  Tell mux this thread is going away.
+  ctxt.ctomux <-= Draw->AMexit;
+}
+.EE
+.SH SOURCE
+.B /appl/lib/ir.b
+.br
+.B /appl/lib/irmpath.b
+.br
+.B /appl/lib/irsim.b
+.SH SEE ALSO
+.IR limbo (1),
+.IR mux (1),
+.IR intro (2),
+.IR draw-intro (2),
+.IR prefab-intro (2)
+ 
--- /dev/null
+++ b/man/2/itslib
@@ -1,0 +1,102 @@
+.TH ITSLIB 2
+.SH NAME
+itslib \- test library
+.SH SYNOPSIS
+.EX
+include "itslib.m";
+itslib := load Itslib Itslib->PATH;
+
+S_INFO: con 0;
+S_WARN: con 1;
+S_ERROR: con 2;
+S_FATAL: con 3;
+S_STIME: con 4;
+S_ETIME: con 5;
+ENV_VERBOSITY: con "ITS_VERBOSITY";
+ENV_MFD: con "ITS_MFD";
+
+Tconfig: adt {
+	verbosity: int;
+	mfd: ref Sys->FD;
+	report: fn(t: self ref Tconfig, sev: int, verb: int, msg: string);
+	done: fn(t: self ref Tconfig);
+};
+
+init: fn(): ref Tconfig;
+.EE
+.SH DESCRIPTION
+.B Itslib
+provides a simple error reporting facility for tests which can be run
+by 
+.IR itest (1) .
+.PP
+The module must first be initialised by calling
+.B init
+which returns an adt containing the verbosity setting specified
+for this test run, and the message file descriptor 
+used to pass messages back to 
+.I itest.
+These two items of information are passed to the test via the
+environment variables ITS_VERBOSITY and ITS_MFD.
+.TP
+.BI Tconf.report( severity ,  verbosity , message)
+Report a message with specified severity and verbosity.
+.I Severity
+must be one of S_INFO, S_WARN, S_ERROR or S_FATAL for Information,
+warnings, errors and fatal errors respectively.
+.I Verbosity
+is an integer between 0 and 9.
+For informatory messages (severity S_INFO), the message will only be
+displayed if the current
+verbosity level is greater than or equal to
+.I verbosity.
+.TP
+.BI Tconf.done()
+Tell the test system that all significant work has been completed. 
+This useful when for example results are displayed in an interactive
+manner following the test proper. For recording purposes the test
+will be regarded as having finished when this function is called,
+rather than when the test finally exits.
+.SH EXAMPLE
+A very simple test program.
+.EX
+
+implement T;
+include "sys.m";
+	sys: Sys;
+include "draw.m";
+	draw: Draw;
+include "itslib.m";
+	itslib: Itslib;
+	Tconfig, S_INFO, S_WARN, S_ERROR, S_FATAL: import itslib;
+
+T: module
+{
+	init:	fn(ctxt: ref Draw->Context, argv: list of string);
+};
+
+tconf: ref Tconfig;
+
+init(ctxt: ref Draw->Context, argv: list of string)
+{
+	sys = load Sys Sys->PATH;
+	itslib = load Itslib Itslib->PATH;
+	if (itslib != nil) tconf = itslib->init();
+	report(S_INFO, 5, "Testing addition of 1 + 2");
+	x := 1 + 2;
+	if (x == 3)
+		report(S_INFO, 6, "Addition of 1 + 2 OK");
+	else
+		report(S_ERROR, 5, sys->sprint("Addition of 1 + 2 gave %d", x));
+}
+
+report(sev: int, verb: int, msg: string)
+{
+	if (tconf != nil) tconf.report(sev, verb, msg);
+	else sys->print("%d:%s\en", sev, msg);
+}
+
+.EE
+.SH SEE ALSO
+.IR itest (1),
+.IR sh-test (2)
--- /dev/null
+++ b/man/2/json
@@ -1,0 +1,218 @@
+.TH JSON 2
+.SH NAME
+json: readjson, writejson, JValue \- read, write and represent values in JavaScript Object Notation
+.SH SYNOPSIS
+.EX
+include "json.m";
+json := load JSON JSON->PATH;
+
+JValue: adt {
+    pick{
+    Object =>
+        mem:   cyclic list of (string, ref JValue);
+    Array =>
+        a:     cyclic array of ref JValue;
+    String =>
+        s:     string;
+    Int =>
+        value: big;
+    Real =>
+        value: real;
+    True or
+    False or
+    Null =>
+    }
+    isarray:   fn(v: self ref JValue): int;
+    isfalse:   fn(v: self ref JValue): int;
+    isint:     fn(v: self ref JValue): int;
+    isnull:    fn(v: self ref JValue): int;
+    isnumber:  fn(v: self ref JValue): int;
+    isobject:  fn(v: self ref JValue): int;
+    isreal:    fn(v: self ref JValue): int;
+    isstring:  fn(v: self ref JValue): int;
+    istrue:    fn(v: self ref JValue): int;
+    copy:      fn(v: self ref JValue): ref Jvalue;
+    eq:        fn(v: self ref JValue, v: ref JValue): int;
+    get:       fn(v: self ref JValue, mem: string): ref JValue;
+    set:       fn(v: self ref JValue, mem: string, value: ref JValue);
+    text:      fn(v: self ref JValue): string;
+};
+
+init:      fn(bufio: Bufio);
+readjson:  fn(input: ref Bufio->Iobuf): (ref JValue, string);
+writejson: fn(output: ref Bufio->Iobuf, val: ref JValue): int;
+
+jvarray:   fn(a: array of ref JValue): ref JValue.Array;
+jvbig:     fn(b: big): ref JValue.Int;
+jvfalse:   fn(): ref JValue.False;
+jvint:     fn(i: int): ref JValue.Int;
+jvnull:    fn(): ref JValue.Null;
+jvobject:  fn(m: list of (string, ref JValue)): ref JValue.Object;
+jvreal:    fn(r: real): ref JValue.Real;
+jvstring:  fn(s: string): ref JValue.String;
+jvtrue:    fn(): ref JValue.True;
+.EE
+.SH DESCRIPTION
+.B JSON
+provides value representations, and encoding and decoding operations for the JavaScript Object Notation (JSON)
+described by Internet RFC4627
+and summarised in
+.IR json (6).
+.PP
+.B Init
+must be called before invoking any other operation of the module.
+The
+.I bufio
+parameter must refer to the instance of
+.IR bufio (2)
+that provides the
+.B Iobuf
+parameters used for input and output.
+.PP
+.B JValue
+is the internal representation of values transmitted by the JSON encoding.
+They are distinguished in a pick adt:
+.TP
+.B JValue.False
+Represents the value
+.BR false .
+.TP
+.B JValue.Null
+Represents a null value of any type.
+.TP
+.B JValue.True
+Represents the value
+.BR true .
+.TP
+.B JValue.Int
+Represents as a Limbo
+.B big
+a JavaScript number with no fractional part.
+.TP
+.B JValue.Real
+Represents as a Limbo
+.B real
+a JavaScript number with a fractional part.
+.TP
+.B JValue.String
+Represents a JavaScript string of Unicode characters from
+.B +U'0000'
+to
+.BR +U'FFFF' .
+.TP
+.B JValue.Array
+Represents a JavaScript array of values of any type.
+.TP
+.B JValue.Object
+Represents a tuple of (member/property name, value) pairs corresponding to the value of a JavaScript object,
+or an associative array or table, indexed by strings.
+The member names must be unique within a value.
+.PP
+.B Readjson
+reads a single value in JSON
+format from the
+.I input
+stream and returns a tuple
+.BI ( val,\ err ).
+On success,
+.I val
+is a
+.B JValue
+that represents the value successfully read.
+If an error occurs,
+.I val
+is nil and
+.I err
+contains a diagnostic.
+.PP
+.B Writejson
+writes to the
+.I output
+stream in JSON format a
+representation of the value
+.IR v .
+It returns 0 on success and -1 on error (setting the system error string).
+.PP
+The easiest way to create a new
+.B JValue
+for subsequent output is with one of the module-level functions
+.BR jvarray ,
+.BR jvint ,
+.BR jvobject ,
+.BR jvstring ,
+and so on.
+As values of a pick adt, a
+.B JValue
+can be inspected using Limbo's
+.B tagof
+operator and the appropriate variant accessed using a
+.B pick
+statement.
+.B JValue
+also supports several groups of common operations, for smaller, tidier code.
+First, the set of enquiry functions
+.IB v .is X ()
+return true if the value
+.I v
+is an instance of the JavaScript type
+.I X
+.RI ( array ,
+.IR int ,
+.IR object ,
+.IR real ,
+.IR string ,
+etc).
+A
+.I numeric
+value is either
+.I int
+or
+.IR real .
+The other operations are:
+.TP
+.IB v .copy()
+Return a (reference to) a copy of value
+.IR v .
+The copying is not recursive.
+.TP
+.IB v .eq( w )
+Return true if the values of
+.I v
+and
+.I w
+are equal, including the values of corresponding subcomponents, recursively
+.TP
+.IB v .get( mem )
+Return the value associated with the member named
+.I mem
+in
+.IR v ;
+return null if there is none.
+.TP
+.IB v .set( mem,\ value )
+Adds or changes the
+.I value
+associated with the member named
+.I mem
+in
+.IR v ,
+which must be a
+.BR JValue.Object (
+raises exception
+.B "json: set non-object"
+otherwise).
+.TP
+.IB v .text()
+Return a string containing the JSON representation of
+.IR v .
+.SH SOURCE
+.B /appl/lib/json.b
+.SH SEE ALSO
+.IR sexprs (2),
+.IR ubfa (2) ,
+.IR json (6),
+.IR sexprs (6),
+.IR ubfa (6)
+.br
+D Crockford, ``The application/json Media Type for JavaScript Object Notation (JSON)'',
+.IR RFC4627 .
--- /dev/null
+++ b/man/2/keyring-0intro
@@ -1,0 +1,296 @@
+.TH KEYRING-INTRO 2
+.SH NAME
+Keyring intro \- introduction to the
+.B Keyring
+module
+.SH SYNOPSIS
+.EX
+include "keyring.m";
+keyring := load Keyring Keyring->PATH;
+
+SigAlg: adt
+{
+    name:   string;
+};
+
+PK: adt
+{
+    sa:     ref SigAlg;
+    owner:  string;
+};
+
+SK: adt
+{
+    sa:     ref SigAlg;
+    owner:  string;
+};
+
+Certificate: adt
+{
+    sa:     ref SigAlg;
+    ha:     string;
+    signer: string;
+    exp:    int;
+};
+
+DigestState: adt
+{
+    # hidden state
+    copy:   fn(d: self ref DigestState): ref DigestState;
+};
+
+Authinfo: adt
+{
+    mysk:   ref SK;
+    mypk:   ref PK;
+    cert:   ref Certificate;
+    spk:    ref PK;
+    alpha:  ref IPint;
+    p:      ref IPint;
+};
+.EE
+.SH DESCRIPTION
+This module contains a mixed set of functions that variously:
+.IP \(bu
+perform infinite precision modular arithmetic; see
+.IR keyring-ipint (2)
+.IP \(bu
+form cryptographically secure digests; see
+.IR keyring-sha1 (2)
+.IP \(bu
+generate public/private key pairs and transform them
+to and from textual form; see
+.IR keyring-gensk (2)
+and
+.IR keyring-certtostr (2)
+.IP \(bu
+encrypt data, using AES, DES, or IDEA; see
+.IR keyring-crypt (2)
+.IP \(bu
+create and verify cryptographic signatures using the
+public keys; see
+.IR keyring-auth (2)
+.IP \(bu
+authenticate the parties on a connection; see
+.IR keyring-auth (2)
+.IP \(bu
+read and write files containing the information
+needed to authenticate the parties on a connection; see
+.IR keyring-auth (2)
+.IP \(bu
+send Limbo byte arrays and strings across a connection; see
+.IR keyring-getstring (2)
+.PP
+Each collection is discussed in turn.
+.SS "Large Precision Arithmetic"
+The
+.B IPint
+adt
+is provided to allow some cryptographic functions to
+be implemented in Limbo.
+.B IPint
+stands for infinite precision integer, though, for
+space considerations, our
+implementation limits the maximum integer to
+2\u\s-2\&8192\s0\d-1.
+.PP
+An
+.B IPint
+can be converted into two external formats.
+The first is
+an array of bytes in which the first byte is the highest order
+byte of the integer.  This format is useful when
+communicating with the
+.IR ssl (3)
+device.
+The second is a MIME base 64 format, that
+allows
+.BR IPint s
+to be stored in files or transmitted across
+networks in a human readable form.
+.SS "Public Key Cryptography"
+Public key cryptography has many uses.
+Inferno relies on it only for digital signatures.
+Each Inferno user may generate a
+pair of matched keys, one public and
+one private.
+The private key may be used to digitally
+sign data, the public one to verify the signature.
+Public key algorithms have been chosen to
+make it difficult to spoof a signature or guess
+the private key.
+.PP
+For public keys algorithms to work, there must be a way to
+distribute the public keys:
+in order to verify that
+.B X
+signed something, we must know
+.BR X 's
+public key.
+To simplify the problem, we have instituted a
+trust hierarchy that requires people to
+know only the public keys of certifying authorities (CAs).
+After generating a public key, one can have the
+concatenation of one's name, expiration date, and key
+signed by a CA.
+The information together with the name of the CA
+and the signature is called a
+.IR certificate .
+.PP
+At the beginning of a conversation, the parties
+exchange certificates.
+They then use the CA's public key to verify each
+other's public keys.
+The CA's public key, a system wide Diffie-Hellman
+base and modulus, one's private key, one's
+public key and certificate are kept in
+a Limbo adt called
+.BR Keyring->Authinfo .
+An
+.B Authinfo
+adt can be read from from a file using
+.B readauthinfo
+or written to a file
+using
+.BR writeauthinfo ,
+both from
+.IR keyring-auth (2).
+.PP
+.B Authinfo
+adts are normally created during the login and
+registration procedures described below.
+.SS "Authentication"
+Two parties conversing on a network connection can
+authenticate each other's identity using the functions in
+.IR keyring-auth (2).
+They use the
+.B Keyring->Authinfo
+information to run the Station to Station (STS)
+authentication protocol.
+STS not only authenticates each party's identity to the other but also
+establishes a random bit string known
+only to the two parties.
+This bit string can be used
+as a key to encrypt or authenticate subsequent messages
+sent between the two parties.
+.SS "Secure Communications"
+After exchanging secrets, communicating
+parties may encode the conversation to
+guarantee varying levels of security:
+.IP •
+none
+.IP •
+messages cannot be forged
+.IP •
+messages cannot be intercepted
+.LP
+Encoding uses the line formats
+provided by the Secure Sockets Layer.
+See
+.IR security-intro (2)
+for more detail.
+.SS "Login and registration"
+The Inferno authentication procedure
+requires that both parties possess an
+.B Authinfo
+adt containing
+a locally generated public/private key pair,
+the public key of a commonly trusted CA,
+and a signed certificate from the CA that links
+the party's identity and public key.
+This
+.B Authinfo
+adt is normally kept in a file.
+At some point, however, it must be created, and later
+conveyed securely between the user's machine
+and the CA.
+There are two ways to do this, the login procedure
+and the registration procedure.
+Both require an out of band channel between the
+CA and the user.
+.PP
+The login procedures are used by typed
+commands and by programs using Tk.
+The login procedure relies on the CA and
+the user having established a common secret
+or password.
+This is done securely off line, perhaps by mail or telephone.
+This secret is then used to provide a secure
+path between CA and user machine to transfer
+the certificate and CA public key.
+See
+.IR security-intro (2)
+for more detail.
+.PP
+The registration procedure is built into the
+.IR mux (1)
+interface and is intended for the set top box
+environment.
+When the set top box is first turned on, it
+creates a public/private key pair and
+dials the service provider's CA to get a key
+signed.
+The CA returns its public key and a signed
+certificate, blinded by a random bit string
+known only to the CA.
+A hash of the information is then displayed on the
+user screen.
+The user must then telephone the CA and compare this
+hashed foot print with the one at the CA.
+If they match and the user proves that he is
+a customer, the CA makes the blinding string
+publicly known.
+.SS Data Types
+.TP
+.B SigAlg
+The
+.B SigAlg
+adt contains a single string that specifies the algorithm used for digital signatures.
+The allowable values are
+.BR md5 ,
+.BR md4
+and
+.BR sha1
+that specify which one-way hash function is used to produce a digital signature
+or message digest.
+.TP
+.BR PK " and " SK
+The
+.B PK
+adt contains the data necessary to construct a public key;
+the
+.B SK
+adt contains the data necessary to construct a secret key.
+Both keys are built from the combination of a specified signature algorithm
+and a string representing the name of the owner of the key.
+.TP
+.B Certificate
+The
+.B Certificate
+adt contains a digital signature with the certification of the trusted authority (CA).
+.TP
+.B DigestState
+The
+.B DigestState
+adt contains the hidden state of partially completed hash functions during processing.
+Its
+.B copy
+operation returns a reference to a copy of a given state.
+.TP
+.B Authinfo
+The
+.B Authinfo
+adt contains an individual user's private and public key, the signer's certificate
+and the signer's public key, and the Diffie-Hellman parameters.
+.SH SOURCE
+.B /libcrypt/*.c
+.br
+.B /libinterp/keyring.c
+.br
+.B /libkeyring/*.c
+.SH SEE ALSO
+.IR security-intro (2)
+.br
+B. Schneier,
+.IR "Applied Cryptography" ,
+1996, J. Wiley & Sons, Inc.
--- /dev/null
+++ b/man/2/keyring-auth
@@ -1,0 +1,101 @@
+.TH KEYRING-AUTH 2
+.SH NAME
+keyring: auth, readauthinfo, writeauthinfo \- authenticate a connection
+.SH SYNOPSIS
+.EX
+include "keyring.m";
+keyring := load Keyring Keyring->PATH;
+auth:           fn(fd: ref Sys->FD, info: ref Authinfo, setid: int)
+                : (string, array of byte);
+readauthinfo:   fn(filename: string): ref Authinfo;
+writeauthinfo:  fn(filename: string, info: ref Authinfo): int;
+.EE
+.SH DESCRIPTION
+.B Auth
+performs mutual authentication over a network connection, usually between a client and a server.
+The function is symmetric: each party runs it on their end of the connection.
+.I Info
+holds the public key of a certifying authority
+.RB ( PKca ),
+the private key of the user
+.RB ( SKu ),
+the public key
+.RB ( PKu )
+of the user signed by the certifying authority
+.RB ( CERTu ),
+and Diffie-Hellman parameters
+.RB ( alpha ,
+.BR p ).
+.PP
+.B Auth
+returns a string and a byte array.
+If the byte array is nil then the authentication has failed and the string is an error message. If the byte array is non-nil, it represents a secret shared by the two communicating parties,
+and the string names the party at the other end of the connection.
+.PP
+If the authentication is successful and
+.I setid
+is non-zero then
+.B auth
+attempts to write the name of the party at the other end of the connection into
+.B /dev/user
+(see
+.IR cons (3));
+no error is generated if that does not succeed.
+If the authentication is not successful and
+.I setid
+is non-zero,
+.B auth
+writes the name
+.B nobody
+into
+.BR /dev/user .
+.PP
+The authentication protocol is based on the Station-to-Station protocol. In the following, the parties are labelled 0 and 1.
+.BI Sig0( x )
+is
+.I x
+signed with 0's private key.
+.IP
+.EX
+0 → 1  alpha**r0 mod p, CERTu0, PKu0
+1 → 0  alpha**r1 mod p, CERTu1, PKu1
+0 → 1  sig0(alpha**r0 mod p, alpha**r1 mod p)
+1 → 0  sig1(alpha**r0 mod p, alpha**r1 mod p)
+.EE
+.PP
+At this point both 0 and 1 share the secret
+.B "alpha**(r0*r1)"
+which is returned in the byte array.
+Amongst other things, it can be the secret to digest or encrypt a conversation
+(see
+.IR security-ssl (2)).
+.PP
+.B Readauthinfo
+reads a representation of an
+.B Authinfo
+from a file.
+It returns nil if there is a read error or a conversion error;
+it returns a reference to the
+.B Authinfo
+otherwise.
+.PP
+.B Writeauthinfo
+writes a representation of
+.I info
+to a file. It returns -1 if the write operation fails, 0 otherwise.
+.SH FILES
+.TF /usr/user/keyring/defaultxxx
+.TP
+.BI /usr/ user /keyring
+The conventional directory for storing
+.B Authinfo
+files
+.TP
+.BI /usr/ user /keyring/default
+The key file normally used by server programs
+.TP
+.BI /usr/ user /keyring/ net ! server
+The key file normally used by clients for a given
+.I server
+.SH SOURCE
+.B /libinterp/keyring.c
--- /dev/null
+++ b/man/2/keyring-certtostr
@@ -1,0 +1,56 @@
+.TH KEYRING-CERTTOSTR 2
+.SH NAME
+keyring: certtostr, pktostr, sktostr, strtocert, strtopk, strtosk \- encryption key conversion functions
+.SH SYNOPSIS
+.EX
+include "keyring.m"
+keyring:= load Keyring Keyring->PATH;
+
+strtocert: fn(s: string)         : ref Certificate;
+certtostr: fn(c: ref Certificate): string;
+strtopk:   fn(s: string)         : ref PK;
+pktostr:   fn(pk: ref PK)        : string;
+strtosk:   fn(s: string)         : ref SK;
+sktostr:   fn(sk: ref SK)        : string;
+.EE
+.SH DESCRIPTION
+Certificates, public keys, and private keys are passed over networks and between applications using a Unicode representation. This collection of functions provide a means to convert adts supplied by the system to and from their portable textual representation. These routines are typically used as part of an I/O package for implementing security.
+.PP
+.B Strtocert
+takes a string argument containing a user name, a hash algorithm, a certifying authority and an expiration time. Fields are separated by a newline. The return value is a
+.BR Certificate .
+If the string is of improper format, the result is
+.IR nil .
+.PP
+.B Certtostr
+performs the inverse operation: takes the
+.B Certificate
+.I c
+and produces a text string suitable for communication over a network.
+.PP
+.B Strtopk
+and
+.B strtosk
+take as their arguments a string
+.I s
+representing the public and private keys respectively.
+.I S
+must contain an algorithm name, a user name and the key. Fields are separated by a newline.
+.B Strtopk
+returns a reference to the resulting
+.BR PK ;
+.B strtosk
+returns a reference to the resulting
+.BR SK .
+If the format of
+.I s
+is invalid, the result is
+.IR nil .
+.PP
+.B Pktostr
+and
+.B sktostr
+perform the inverse operations:
+they take a public key (secret key) and produce a printable representation as a string.
+.SH SOURCE
+.B /libinterp/keyring.c
--- /dev/null
+++ b/man/2/keyring-crypt
@@ -1,0 +1,138 @@
+.TH KEYRING-CRYPT 2
+.SH NAME
+keyring: aessetup, aescbc, dessetup, descbc, desecb, ideasetup, ideacbc, ideaecb \- data encryption
+.SH SYNOPSIS
+.EX
+include "keyring.m";
+keyring := load Keyring Keyring->PATH;
+
+Encrypt:   con 0;
+Decrypt:   con 1;
+
+AESbsize:  con 16;
+
+aessetup:  fn(key: array of byte, ivec: array of byte): ref AESstate;
+aescbc:    fn(state: ref AESstate, buf: array of byte,
+             n: int, direction: int);
+
+BFbsize: con 8;
+
+blowfishsetup: fn(key: array of byte, ivec: array of byte): ref BFstate;
+blowfishcbc:   fn(state: ref BFstate, buf: array of byte,
+             n: int, direction: int);
+
+DESbsize:  con 8;
+
+dessetup:  fn(key: array of byte, ivec: array of byte): ref DESstate;
+descbc:    fn(state: ref DESstate, buf: array of byte,
+             n: int, direction: int);
+desecb:    fn(state: ref DESstate, buf: array of byte,
+             n: int, direction: int);
+
+IDEAbsize: con 8;
+
+ideasetup: fn(key: array of byte, ivec: array of byte): ref IDEAstate;
+ideacbc:   fn(state: ref IDEAstate, buf: array of byte,
+             n: int, direction: int);
+ideaecb:   fn(state: ref IDEAstate, buf: array of byte,
+             n: int, direction: int);
+.EE
+.SH DESCRIPTION
+These functions encrypt and decrypt blocks of data using different
+encryption algorithms.
+The interfaces are similar.
+.PP
+Each algorithm has an adt that holds the current state for a given encryption.
+It is produced by the setup function for the algorithm,
+.IB alg setup ,
+which is given a secret
+.I key
+and an initialisation vector
+.IR ivec .
+A sequence of blocks of data can then be encrypted or decrypted by repeatedly calling
+.IB alg cbc
+(for `cipher block chaining'), or
+.IB alg ebc
+(the less secure `electronic code book', if provided).
+On each call,
+.I buf
+provides
+.I n
+bytes of the data to encrypt or decrypt.
+.I N
+must be a multiple of the encryption block size
+.IB ALG bsize .
+Exceptionally,
+.B aescbc
+allows
+.I n
+to be other than a multiple of
+.B AESbsize
+in length, but then
+for successful decryption, the decryptor must use the same
+sequence of buffer sizes as the encryptor.
+.I Direction
+is the constant
+.B Encrypt
+or
+.B Decrypt
+as required.
+.I State
+maintains the encryption state, initially produced by the setup function,
+and updated as each buffer is encrypted or decrypted.
+.PP
+The algorithms currently available are:
+.TP
+.B aes
+The Advanced Encryption Standard, AES (also known as Rijndael).
+The
+.I key
+should be 16, 24 or 32 bytes long (128, 192 or 256 bits).
+.I Ivec
+should be
+.B AESbsize
+bytes of random data: random enough to be unlikely to be reused but
+not cryptographically strongly unpredictable.
+.TP
+.B blowfish
+Bruce Schneier's symmetric block cipher.
+The
+.I key
+is any length from 4 to 56 bytes.
+.I Ivec
+if non-nil is
+.B BFbsize
+bytes of random data.
+For
+.BR blowfishcbc ,
+.I n
+must be a multiple of
+.BR BFbsize .
+.TP
+.B des
+The older Data Encryption Standard, DES.
+.I Key
+is 8 bytes (64 bits), containing a 56-bit key
+encoded into 64 bits where every eighth bit is parity.
+.I Ivec
+is
+.B DESbsize
+bytes of random data.
+.TP
+.B idea
+The International Data Encryption Standard, IDEA™.
+The
+.I key
+is 16 bytes long (128 bits).
+.I Ivec
+is
+.B IDEAbsize
+bytes of random data.
+.SH SEE ALSO
+.IR keyring-intro (2),
+.IR keyring-rc4 (2),
+.IR security-random (2)
+.PP
+IDEA was patented by Ascom-Tech AG (EP 0 482 154 B1, US005214703),
+currently held by iT_SEC Systec Ltd.
+The patent expired in 2012.
--- /dev/null
+++ b/man/2/keyring-gensk
@@ -1,0 +1,49 @@
+.TH KEYRING-GENSK 2
+.SH NAME
+keyring: genSK, genSKfromPK, sktopk, dhparams \- generate keys
+.SH SYNOPSIS
+.EX
+include "keyring.m";
+keyring := load Keyring Keyring->PATH;
+genSK:  fn(algname, owner: string, length: int): ref SK;
+genSKfromPK:  fn(pk: ref PK, owner: string): ref SK;
+sktopk:  fn(sk: ref SK): ref PK;
+dhparams:  fn(nbits: int): (ref IPint, ref IPint);
+.EE
+.SH DESCRIPTION
+.B GenSK
+generates a public/private key pair, represented by
+.BR SK .
+(Although we call it a `private key',
+.B SK
+contains both the private and public parts of a public key.)
+.I Algname
+is the name of the algorithm to use; in the current implementation, only
+.B elgamal
+and
+.B rsa
+are possible.
+.I Owner
+is the user name to be associated with the key.
+.I Length
+gives the length of the key modulus in bits.
+.B GenSK
+returns nil if an unknown algorithm has been specified.
+.PP
+.B GenSKfromPK
+generates a private key that has the same system parameters as the public key
+.IR pk .
+It is used to generate new keys that are of the same complexity as old keys.
+.PP
+.B Sktopk
+extracts the public part of private key.
+.PP
+.B Dhparams
+creates Diffie-Hellman parameters. The second
+.B IPint
+returned is an
+.I nbits
+long prime number that serves as the modulus.
+The first
+.B IPint
+is a primitive root in the integer field defined by that modulus.
--- /dev/null
+++ b/man/2/keyring-getmsg
@@ -1,0 +1,68 @@
+.TH KEYRING-GETMSG 2
+.SH NAME
+keyring: getmsg, sendmsg, senderrmsg \- send and receive messages on undelimited streams
+.SH SYNOPSIS
+.EX
+include "keyring.m";
+keyring := load Keyring Keyring->PATH;
+
+getmsg:  fn(fd: ref Sys->FD): array of byte;
+sendmsg: fn(fd: ref Sys->FD, buf: array of byte, n: int): int;
+senderrmsg: fn(fd: ref Sys->FD, s: string): int;
+.EE
+.SH DESCRIPTION
+These functions allow arbitrary data, packed into arrays of bytes, to be exchanged
+on network connections using connection-oriented transport protocols that do not preserve
+record boundaries (eg, TCP/IP without
+.IR ssl (3)).
+They are used to implement various authentication protocols, including
+.IR auth (6),
+as implemented by
+.IR keyring-auth (2).
+.PP
+Each data message is transmitted with a five-byte header containing a four-character zero-padded decimal count
+.I n
+terminated by a newline, followed by
+.I n
+bytes of message data.
+An error message has a similar structure, except that the first character
+of the count is replaced by an exclamation mark
+.RB ( ! );
+the message data following
+contains the diagnostic string in its UTF-8 encoding (see
+.IR utf (6)).
+.PP
+.B Getmsg
+reads the next message from
+.I fd
+and returns its data content.
+.PP
+.B Sendmsg
+sends the first
+.I n
+bytes of
+.I buf
+as a message on
+.IR fd ,
+and returns
+.IR n .
+.PP
+.B Senderrmsg
+sends the error message
+.IR s .
+.SH SOURCE
+.B /libinterp/keyring.c
+.SH DIAGNOSTICS
+.B Sendmsg
+and
+.B senderrmsg
+return -1 if there was an error writing to
+.IR fd ;
+they set the system error string.
+.B Getmsg
+returns nil if there was an error reading from
+.IR fd ;
+it sets the system error string to reflect the cause.
+It also returns nil
+if an error message was received instead of a data message;
+the system error string will contain the error message's diagnostic.
--- /dev/null
+++ b/man/2/keyring-getstring
@@ -1,0 +1,90 @@
+.TH KEYRING-GETSTRING 2
+.SH NAME
+keyring: getstring, putstring, getbytearray, putbytearray, puterror \- exchange data on delimited streams
+.SH SYNOPSIS
+.EX
+include "keyring.m"
+keyring:= load Keyring Keyring->PATH;
+
+getstring:    fn(fd: ref Sys->FD): (string, string);
+putstring:    fn(fd: ref Sys->FD, s: string): int;
+getbytearray: fn(fd: ref Sys->FD): (array of byte, string);
+putbytearray: fn(fd: ref Sys->FD, a: array of byte, n: int): int;
+puterror:     fn(fd: ref Sys->FD, s: string): int;
+.EE
+.SH DESCRIPTION
+These functions provide
+I/O for strings, byte arrays and error strings over network connections that
+provide a record structure for communication (as provided for arbitrary
+networks by
+.IR ssl (3)).
+.PP
+.B Putstring
+writes string
+.I s
+to
+.IR fd.
+It returns the number of bytes written, or -1 if an error occurred.
+Messages written by
+.B putstring
+are truncated to 4096 bytes.
+.PP
+.B Getstring
+reads a string as written by
+.B putstring
+from
+.IR fd
+and returns a tuple
+.RI ( result , error ).
+If successful, the error
+string is nil.
+.PP
+.B Putbytearray
+writes the array of bytes
+.I a
+to
+.IR fd .
+It returns the number of bytes written, or -1 if an error occurred.
+Messages written by
+.B putbytearray
+are truncated to 4096 bytes.
+.PP
+.B Getbytearray
+reads an array of bytes as written by
+.B putbytearray
+from
+.IR fd
+and returns a tuple of the form
+.RI ( result , error ).
+If successful, the error string is nil.
+.PP
+.B Puterror
+writes an error string
+.I s
+to
+.IR fd .
+It can be used in place of
+.B putstring
+or
+.B putbytearray
+to cause a corresponding
+.B getstring
+or
+.B getbytearray
+to fail
+(in the receiving process),
+forcing them to return the error string
+.IR s .
+It may not be longer than
+.BR Sys->ERRMAX
+bytes.
+.SH SOURCE
+.B /libinterp/keyring.c
+.SH DIAGNOSTICS
+The output functions return an
+.B int
+which is -1 if there was an I/O error,
+and a non-negative value otherwise.
+The input functions
+return a tuple that includes a string indicating the cause of the
+error, as the second element of the tuple.
--- /dev/null
+++ b/man/2/keyring-ipint
@@ -1,0 +1,180 @@
+.TH KEYRING-IPINT 2
+.SH NAME
+keyring: IPint \- `infinite' precision integer utility functions
+.SH SYNOPSIS
+.EX
+include "keyring.m"
+keyring:= load Keyring Keyring->PATH;
+
+IPint: adt
+{
+  iptob64:   fn(i: self ref IPint): string;
+  b64toip:   fn(str: string)  : ref IPint;
+  iptobytes: fn(i: self ref IPint): array of byte;
+  bytestoip: fn(buf: array of byte): ref IPint;
+  iptobebytes: fn(i: self ref IPint): array of byte;
+  bebytestoip: fn(buf: array of byte): ref IPint;
+  inttoip:   fn(i: int): ref IPint;
+  iptoint:   fn(i: self ref IPint): int;
+  iptostr:   fn(i: self ref IPint, base: int): string;
+  strtoip:   fn(str: string, base: int): ref IPint;
+  random:    fn(minbits, maxbits: int): ref IPint;
+  copy:      fn(i: self ref IPint): ref IPint;
+  bits:      fn(i:  self ref IPint): int;
+  expmod:    fn(base: self ref IPint, exp, mod: ref IPint):ref IPint;
+  add:  fn(i1: self ref IPint, i2: ref IPint): ref IPint;
+  sub:  fn(i1: self ref IPint, i2: ref IPint): ref IPint;
+  neg:  fn(i: self ref IPint): ref IPint;
+  mul:  fn(i1: self ref IPint, i2: ref IPint): ref IPint;
+  div:  fn(i1: self ref IPint, i2: ref IPint): (ref IPint, ref IPint);
+  eq:   fn(i1:  self ref IPint, i2: ref IPint): int;
+  cmp:  fn(i1: self ref IPint, i2: ref IPint): int;
+  shl:  fn(i: self ref IPint, n: int): ref IPint;
+  shr:  fn(i: self ref IPint, n: int): ref IPint;
+  and:  fn(i1: self ref IPint, i2: ref IPint): ref IPint;
+  ori:  fn(i1: self ref IPint, i2: ref IPint): ref IPint;
+  not:  fn(i: self ref IPint): ref IPint;
+  xor:  fn(i1: self ref IPint, i2: ref IPint): ref IPint;
+};
+.EE
+.SH DESCRIPTION
+.B IPint
+provides the following arbitrary-length integer manipulation functions required for cryptographic support in Limbo:
+.TP
+.IB i .iptob64()
+Returns a string that represents a large integer textually in base 64 for convenient transmission over a network connection.
+.TP
+.BI b64toip( str )
+Returns the
+.B IPint
+represented by the base-64 encoded
+.IR str .
+.TP
+.IB i .iptobytes()
+Returns an array of bytes representing a large integer. The representation includes both positive and negative numbers.
+.TP
+.BI bytestoip( buf )
+The inverse operation of
+.BR iptobytes .
+.TP
+.IB i .iptobebytes()
+Returns an array of bytes in big-endian format representing the magnitude of a large integer; used for instance to pass a value to
+.IR ssl (3).
+Only non-negative numbers are represented.
+.TP
+.BI bebytestoip( buf )
+The inverse operation of
+.BR iptobebytes .
+.TP
+.BI inttoip( i )
+Creates a new large integer from integer
+.IR i .
+.TP
+.IB i .iptoint()
+Converts a large integer
+.I i
+to an
+.BR int ;
+returns 0 on error.
+.TP
+.IB i .iptostr( base )
+Converts a large integer to a string in base
+.IR base ;
+returns nil on error.
+Only the bases 10, 16, 32, and 64 are
+supported.  Anything else defaults to 16.
+.TP
+.BI strtoip( str , base )
+Converts a string
+.I str
+representing a number in in base
+.I base
+to a large integer; returns nil on error.
+Only the bases 10, 16, 32, and 64 are
+supported.
+.TP
+.BI random( minbits , maxbits )
+Returns a large random number with length from
+.I minbits
+to
+.IR maxbits .
+The largest number allowed in the current implementation is
+2^8192-1 .
+The seed for the generator is obtained by duelling clocks.
+.TP
+.IB i .copy()
+Returns a reference to the same value as
+.IR i .
+.TP
+.IB i .bits()
+Returns the number of bits of precision of
+.IR i .
+.TP
+.IB base .expmod( "exp , mod" )
+Returns
+.BI ( base ** exp ") mod " mod.
+.TP
+.IB i1 .add( i2 )
+Returns
+.RI ( i1 + i2 ).
+.TP
+.IB i1 .sub( i2 )
+Returns
+.RI ( i1 - i2 ).
+.TP
+.IB i1 .mul ( i2 )
+Returns
+.IR i1*i2 .
+.TP
+.IB i1 .div ( i2 )
+Returns
+.RI ( i1 / i2,
+.IR i1 mod i2 ).
+.TP
+.IB i1 .eq( i2 )
+Returns 1 if
+.I i1
+and
+.I i2
+are equal; 0 otherwise.
+.TP
+.IB i1 .cmp( i2 )
+Compares two large integers, returning 1 if
+.I i1
+is larger,
+-1 if
+.I i2
+is larger, and 0 if they are equal.
+.TP
+.IB i .shl( n )
+Returns
+.IR i << n
+.TP
+.IB i .shr( n )
+Returns
+.IR i >> n
+.TP
+.IB i1 .and( i2 )
+Returns
+.IR i & n ,
+bitwise AND
+.TP
+.IB i1 .ori( i2 )
+Returns
+.IR i | n ,
+bitwise inclusive-OR
+(it is
+.B ori
+because plain
+.B or
+is a Limbo keyword)
+.TP
+.IB i .not()
+Returns
+.RI ~ i ,
+bitwise ones-complement
+.TP
+.IB i1 .xor( i2 )
+Returns
+.IR i ^ n ,
+bitwise exclusive-OR
--- /dev/null
+++ b/man/2/keyring-rc4
@@ -1,0 +1,45 @@
+.TH KEYRING-RC4 2
+.SH NAME
+keyring: rc4setup, rc4, rc4skip, rc4back \- RC4 encryption
+.SH SYNOPSIS
+.EX
+include "keyring.m";
+keyring := load Keyring Keyring->PATH;
+
+rc4setup: fn(seed: array of byte): ref RC4state;
+rc4:      fn(state: ref RC4state, buf: array of byte, n: int);
+rc4skip:  fn(state: ref RC4state, n: int);
+rc4back:  fn(state: ref RC4state, n: int);
+.EE
+.SH DESCRIPTION
+These functions implement the stream encryption algorithm that is claimed to
+be equivalent to RSA Security's RC4.
+It is a pseudo-random number generator with a 256
+byte state and a long cycle.
+.PP
+.B Rc4setup
+sets the initial
+.IR seed ,
+which can be any non-zero length, and
+returns a representation of the initial state of the algorithm,
+which is used in subsequent calls.
+.PP
+.B Rc4
+runs the generator starting with the given
+.IR state ,
+and XORs the output of the generator with
+the first
+.I n
+bytes of
+.IR buf ,
+updating the
+.IR state .
+.B Rc4
+is symmetric and is used both to encrypt and decrypt.
+.B Rc4skip
+skips over bytes (eg, to account for lost transmissions);
+.B rc4back
+runs the generator backwards (eg, to account for retransmissions).
+.SH SEE ALSO
+.IR keyring-intro (2),
+.IR keyring-crypt (2)
--- /dev/null
+++ b/man/2/keyring-sha1
@@ -1,0 +1,142 @@
+.TH KEYRING-SHA1 2
+.SH NAME
+keyring: sha1, md4, md5, hmac_sha1, hmac_md5, sign, verify \- cryptographic digests and digital signatures
+.SH SYNOPSIS
+.EX
+include "keyring.m";
+keyring := load Keyring Keyring->PATH;
+
+.ta \w'verify:\ 'u +\w'fn(\ \ \ 'u
+sha1:	fn(buf: array of byte, n: int, digest: array of byte,
+		state: ref DigestState): ref DigestState;
+md4:	fn(buf: array of byte, n: int, digest: array of byte,
+		state: ref DigestState): ref DigestState;
+md5:	fn(buf: array of byte, n: int, digest: array of byte,
+		state: ref DigestState): ref DigestState;
+hmac_sha1:	fn(buf: array of byte, n: int, key: array of byte, digest: array of byte,
+		state: ref DigestState): ref DigestState;
+hmac_md5:	fn(buf: array of byte, n: int, key: array of byte, digest: array of byte,
+		state: ref DigestState): ref DigestState;
+sign:	fn(sk: ref SK, exp: int, state: ref DigestState,
+		ha: string): ref Certificate;
+verify:	fn(pk: ref PK, cert: ref Certificate,
+		state: ref DigestState): int;
+.EE
+.SH DESCRIPTION
+.BR Sha1 ,
+.B md4
+and
+.B md5
+are cryptographically secure hash functions that produce output called a message digest.
+Each function computes a hash of
+.I n
+bytes of the data in
+.IR buf ,
+and updates the current
+.IR state .
+They can be called iteratively to form a single digest for many data blocks.
+The state is kept in the
+.B DigestState
+value referenced by
+.I state
+between calls.
+.I State
+should be
+.B nil
+on the first call, and a newly allocated
+.B DigestState
+will be returned for use in subsequent calls.
+On a call in which
+.I digest
+is not
+.BR nil ,
+the hash is completed and copied into the
+.I digest
+array.
+.B Sha1
+produces a 20-byte hash
+.RB ( SHA1dlen ),
+.B md4
+and
+.B md5
+a 16-byte one
+.RB ( MD4len
+and
+.BR MD5len ).
+.PP
+.B Hmac_sha1
+and
+.B hmac_md5
+are keyed versions of the hashing functions, following Internet RFC2104.
+The
+.I key
+must be provided in each call, but otherwise
+the calling conventions are those of
+.BR sha1 .
+The
+.I key
+must currently be no more than 64 bytes.
+.PP
+.B Sign
+creates a digital signature of a digest from the concatenation of: a message, the name of the signer, and an expiration time.
+.I State
+is the digest state after running
+.BR sha1 ,
+.B md4
+or
+.B md5
+over the message.
+.I Ha
+is a string specifying the hash algorithm to use:
+.B
+"sha"\fR,
+.B
+"sha1"\fR,
+.B
+"md4"\fR
+or
+.B
+"md5"\fR.
+.B Sign
+extends the digest to cover the signer's name
+(taken from the private key,
+.IR sk )
+and the expiration time.
+It returns a certificate containing the digital signature of the digest, signer name, hash algorithm and signature algorithm.
+If any parameter is invalid,
+.B sign
+returns nil.
+The signature algorithm is implied by the type of the private key.
+.PP
+.B Verify
+uses public key
+.I pk
+to verify a certificate.
+It returns non-zero (true) if the certificate is valid; zero (false) otherwise.
+.I State
+is the digest state after running the chosen digest algorithm
+over the message.
+.SH EXAMPLES
+A program to read a file and hash it using SHA might contain the following inner loop:
+.IP
+.EX
+state: ref DigestState = nil;
+while((n := sys->read(fd, buf, len buf)) > 0)
+	state = kr->sha1(buf, n, nil, state);
+digest := array[kr->SHA1dlen] of byte;
+kr->sha1(buf, 0, digest, state);
+.EE
+.SH SOURCE
+.B /libinterp/keyring.c
+.br
+.B /libcrypt/hmac.c
+.br
+.B /libcrypt/md4.c
+.br
+.B /libcrypt/md5.c
+.br
+.B /libcrypt/sha1.c
+.SH BUGS
+The MD4 algorithm is included only to allow communication with software
+that might still use it; it should not otherwise be used now, because it
+is easily broken.
--- /dev/null
+++ b/man/2/keyset
@@ -1,0 +1,79 @@
+.TH KEYSET 2
+.SH NAME
+keyset \- find authentication keys matching a signer
+.SH SYNOPSIS
+.EX
+include "keyset.m";
+keyset := load Keyset Keyset->PATH;
+
+init:      fn(): string;
+keysforsigner: fn(signername: string, spkthumb: string,
+                  user: string, dir: string):
+                  (list of (string, string, string), string);
+pkhash:    fn(pk: string): string;
+.EE
+.SH DESCRIPTION
+.B Keyset
+looks through a set of certified public keys
+to find one or more keys that have were certified by a given signer.
+.PP
+.B Init
+must be called before any other function in the module.
+It returns nil on success or a diagnostic string on failure.
+.PP
+.B Keysforsigner
+looks for public keys that satisfy given conditions:
+.I signername
+is either the name of a signer or nil (don't care);
+.I spkthumb
+is either a thumbprint of the signer's public key (as produced by
+.BR pkhash ,
+below), or nil (don't care).
+.I User
+is the name of the user that owns the set of keys; if it is nil,
+the user's name is read from
+.BR /dev/user .
+.I Dir
+is the name of the directory holding a collection of the
+.IR user 's
+signed keys as obtained for instance using
+.IR getauthinfo (8);
+if it is nil, the directory
+.BI /usr/ user /keyring
+is used by default.
+Only signed (certified) unexpired keys are considered.
+.B Keysforsigner
+returns a tuple
+.BI ( keys , err ).
+.I Keys
+is list of tuples
+.BI ( keyfile\fB,\fP\ owner\fB,\fP\ signername )
+where
+.I keyfile
+is the full name of a file in
+.I dir
+that holds an apparently suitable key;
+.I owner
+is the name of the key's owner; and
+.I signername
+is the name of the signer in the certificate attached to the key.
+The list is nil if no keys could be found that matched the criteria.
+On an error,
+.I err
+is non-nil and gives a diagnostic.
+.PP
+.B Pkhash
+returns the hexadecimal representation of the SHA-1 hash of public key
+.IR pk ,
+which must be in the canonical textual form produced by
+.B Keyring->pktostr
+(see
+.IR keyring-certtostr (2)).
+.SH SOURCE
+.B /appl/lib/keyset.b
+.SH SEE ALSO
+.IR bind (1),
+.IR keyring-gensk (2),
+.IR keyring-sha1 (2),
+.IR security-auth (2),
+.IR logind (8)
--- /dev/null
+++ b/man/2/lists
@@ -1,0 +1,204 @@
+.TH LISTS 2
+.SH NAME
+lists: allsat, anysat, append, combine, concat, delete, filter, find, ismember, last, map, pair, partition, reverse, unpair\- list operations
+.SH SYNOPSIS
+.EX
+include "lists.m"
+lists := load Lists Lists->PATH;
+
+append:    fn[T](l: list of T, x: T): list of T;
+combine:   fn[T](l1: list of T, l2: list of T): list of T;
+concat:    fn[T](l1: list of T, l2: list of T): list of T;
+delete:    fn[T](x: T, l: list of T): list of T
+             for { T => eq:  fn(a, b: T): int; };
+find:      fn[T](x: T, l: list of T): list of T
+             for { T => eq:  fn(a, b: T): int; };
+ismember:  fn[T](x: T, l: list of T): int
+             for { T =>  eq: fn(a, b: T): int; };
+last:      fn[T](l: list of T): T;
+pair:      fn[T1, T2](l1: list of T1, l2: list of T2):
+             list of (T1, T2);
+reverse:   fn[T](l: list of T): list of T;
+unpair:    fn[T1, T2](l: list of (T1, T2)):
+             (list of T1, list of T2);
+
+allsat:    fn[T](p: ref fn(x: T): int, l: list of T): int;
+anysat:    fn[T](p: ref fn(x: T): int, l: list of T): int;
+filter:    fn[T](p: ref fn(x: T): int, l: list of T): list of T;
+map:       fn[T](f: ref fn(x: T): T, l: list of T): list of T;
+partition: fn[T](p: ref fn(x: T): int, l: list of T):
+             (list of T, list of T);
+.EE
+.SH DESCRIPTION
+The module provides operations on lists of type
+.IR T ,
+which may be any reference type, or
+.BR string .
+Reference types are
+.BR array ,
+.BR chan ,
+.BR list ,
+.BR module ,
+and
+.BI ref " adt".
+None of the operations change their parameter lists: they always return a new list.
+.PP
+First, there is a group of common list operations.
+.PP
+.B Append
+returns a new list with the elements of
+.I l
+followed by the element
+.IR x .
+.PP
+.B Combine
+returns a new list that has the elements of both
+.I l1
+and
+.I l2
+in no defined order.
+.PP
+.B Concat
+returns a new list that has the elements of
+.I l1
+followed by the elements of
+.IR l2 .
+.PP
+.B Delete
+returns a new list in which the first element of
+.I l
+that is equal to
+.I x
+has been removed (all others remain).
+.I X
+must be an adt reference type
+.I T
+with an operation
+.IB T .eq
+that returns true (non-zero) if two values of type
+.I T
+are considered equal.
+.PP
+.B Find
+finds the first instance of
+.I x
+in list
+.I l
+and returns the tail of
+.I l
+with
+.I x
+at its head.
+.B Find
+returns nil if there is no
+instance of
+.I x
+in
+.IR l .
+.PP
+.B Ismember
+returns 1 if
+there is an element of
+.I l
+that is equal to
+.IR x .
+.PP
+.B Last
+returns the value of the element at the end of list
+.IR l .
+A run-time error occurs if
+.I l
+is nil.
+.PP
+.B Pair
+takes two lists
+.I l1
+and
+.IR l2 ,
+and returns a new list of tuples
+.BI ( v1,\ v2 )
+in which each element of
+.I l1
+has been paired with the corresponding element of
+.IR l2 .
+A run-time error occurs if the lists are not equal in length.
+.PP
+.B Reverse
+returns a new list containing the elements of
+.I l
+in reverse order.
+.PP
+.B Unpair
+takes a list of tuples
+.BI ( v1,\ v2 )
+and returns a tuple of lists
+.BI ( l1,\ l2 )
+where
+.I l1
+contains the first element of each tuple, and
+.I l2
+contains the second element of each tuple.
+.PP
+A second group of operations applies a function
+.I f
+or a Boolean predicate
+.I p
+to each element of a list
+.IR l ,
+returning a transformed list or a Boolean value.
+A predicate
+.I p
+must return non-zero if its parameter value satisfies its condition, and must return zero if it does not.
+.PP
+.B Allsat
+returns 1 if all elements of
+.I l
+satisfy
+.IR p ,
+and returns 0 otherwise.
+.PP
+.B Anysat
+returns 1 if any element of
+.I l
+satisfies
+.IR p ,
+and returns 0 otherwise.
+.PP
+.B Filter
+returns a new list containing only the elements of
+.I l
+that satisfy
+.IR p .
+.PP
+.B Map
+returns a new list in which each element of
+.I l
+has been transformed by
+.I f
+(ie, if
+.I l
+is
+.IB e0 :: e1 ::  ...
+then the result is
+.IB f(e0) :: f(e1) :: ... ).
+.PP
+.B Partition
+returns a tuple of lists
+.BI ( l1,\ l2 )
+of lists where
+.I l1
+contains all elements of
+.I l
+that satisfy
+.I p
+and
+.I l2
+contains all elements of
+.I l
+that do not satisfy
+.IR p .
+.SH SOURCE
+.B /appl/lib/lists.b
+.SH BUGS
+The current implementation of polymorphism is limited to reference types and strings,
+which in turn limits use of this module.
--- /dev/null
+++ b/man/2/lock
@@ -1,0 +1,39 @@
+.TH LOCK 2
+.SH NAME
+lock \-
+thread locking.
+.SH SYNOPSIS
+.EX
+include "lock.m";
+lock := load Lock Lock->PATH;
+Semaphore: adt {
+	c: chan of int;
+	obtain: fn(s: self ref Semaphore);
+	release: fn(s: self ref Semaphore);
+	new: fn(): ref Semaphore;
+};
+init: fn();
+.EE
+.SH DESCRIPTION
+.B Lock
+provides semaphore-based mutual exclusion.
+.B Init
+must be called before creating any locks.
+.TP
+.B Semaphore.new()
+Creates and returns a reference to a new
+.B Semaphore
+(lock).
+.TP
+.IB s .obtain()
+Obtains exclusive access to the lock
+.IR s .
+It will block until it can do so.
+.TP
+.IB s .release()
+Releases access to the lock
+.I s
+and allows processes waiting on it to proceed.
+.SH SOURCE
+.B /appl/lib/lock.b
+
--- /dev/null
+++ b/man/2/math-0intro
@@ -1,0 +1,79 @@
+.TH MATH-INTRO 2
+.SH NAME
+Math: intro \- elementary numerics
+.SH SYNOPSIS
+.EX
+include "math.m";
+math := load Math Math->PATH;
+.EE
+.SH DESCRIPTION
+Inferno's math module and Limbo compiler provide the
+fundamental floating point environment and ``elementary functions''.
+.PP
+Limbo expressions involving only literal and named constants are
+evaluated at compile time with all exceptions ignored.
+However,
+arithmetic on variables is left to run-time, even if data path analysis
+shows the value to be a compile time constant.
+This implies that tools
+generating Limbo source must do their own simplification, and not
+expect the compiler to change
+.B x/x
+into
+.BR 1 ,
+or
+.B -(y-x)
+into
+.BR x-y ,
+or even
+.BR x-0
+into
+.BR x .
+.PP
+Subexpression elimination and other forms of code motion may be done by the
+compiler,
+but not across calls to the mode and status functions described in
+.IR math-fp (2).
+Removal of
+parentheses or factoring is not performed by the compiler.
+The evaluation
+order of
+.B a+b+c
+follows the parse tree and is therefore the same as for
+.BR (a+b)+c .
+These rules are the same as for Fortran and C.
+.PP
+Contracted multiply-add instructions (with a single rounding) are not
+generated by the compiler, though they may be used in the native BLAS
+(linear algebra)
+libraries.
+All arithmetic follows the IEEE floating point standard, except that
+denormalized numbers may be replaced by flush-to-0, depending on what
+the hardware makes feasible.
+.PP
+Binary/decimal conversion is properly rounded.
+In particular, printing
+a real using
+.B %g
+and reading it on a different machine is guaranteed to
+recover identical bits, including
+conversions done by the compiler.
+The one exception is that
+smaller, faster, but sloppier run-time conversion
+routines may be used when mandated by
+limited memory embedded systems.
+Programmers may assume, however,
+that the
+features described in these man pages are present in all Inferno
+systems intended for general computing.
+.SH SOURCE
+.B /libinterp/math.c
+.SH SEE ALSO
+See
+.IR math-fp (2)
+for floating point control and primitive arithmetic operations,
+.IR math-elem (2)
+for the classical elementary functions of applied mathematics,
+and
+.IR math-linalg (2)
+for basic linear algebra functions.
--- /dev/null
+++ b/man/2/math-elem
@@ -1,0 +1,106 @@
+.TH MATH-ELEM 2
+.SH NAME
+Math: cbrt, sqrt, pow, pow10, hypot, exp, expm1, log, log10, log1p, cos, cosh, sin, sinh, tan, tanh, acos, asin, acosh, asinh, atan, atanh, atan2, lgamma, erf, erfc, j0, j1, y0, y1, jn, yn \- elementary functions of applied mathematics
+.SH SYNOPSIS
+.EX
+include "math.m";
+math := load Math Math->PATH;
+
+cbrt, sqrt: fn(x: real): real;
+pow: fn(x, y: real): real;
+pow10: fn(p: int): real;
+hypot: fn(x, y: real): real;
+exp, expm1, log, log10, log1p: fn(x: real): real;
+
+cos, cosh, sin, sinh, tan, tanh: fn(x: real): real;
+acos, asin, acosh, asinh, atan, atanh: fn(x: real): real;
+atan2: fn(y, x: real) of real;
+
+lgamma: fn(x: real): (int,real);
+erf, erfc: fn(x: real): real;
+j0, j1, y0, y1: fn(x: real): real;
+jn, yn: fn(n: int, x: real): real;
+
+.EE
+.SH DESCRIPTION
+These routines implement the basic elementary functions of applied mathematics.
+.PP
+.BI Sqrt( x )
+computes the square root of
+.IR x ,
+.BI cbrt( x )
+the cube root.
+.BI Pow( x , y )
+computes
+.I x
+raised to the exponent
+.IR y ;
+.B pow10
+raises 10 to the integer power
+.IR n .
+.BI Hypot( x , y )
+computes
+\f5sqrt(\f2x\f5*\f2x\f5+\f2y\f5*\f2y\f5)\f1.
+.PP
+.BI Exp( x )
+returns the exponential function of
+.IR x ,
+and
+.BI expm1( x )
+is
+.BI exp( x )-1.
+.PP
+.BI Log( x )
+returns the natural logarithm of
+.IR x ,
+while
+.BI log10( x )
+returns the logarithm base 10 and
+.BI log1p( x )
+returns the logarithm of
+.BI 1+ x\f1.
+.PP
+The trigonometric functions use radians.
+The ranges of the inverse functions are:
+.BI acos
+in [0,\(*p];
+.B asin
+in [-\(*p/2,\(*p/2];
+.B atan
+in [-\(*p/2,\(*p/2];
+and
+.BI atan2( y , x )
+.B =
+.BI arctan( y / x )
+in [-\(*p,\(*p];
+.PP
+The gamma function is implemented by
+.BI lgamma( x )\f1;
+the tuple it returns, say
+.BI ( s , lg )\f1,
+encodes the gamma function by
+.RI \(*G( x )
+=
+.IB s *exp( lg )\f1.
+.PP
+The hyperbolic trigonometric functions
+.B sinh
+etc. behave as expected.
+.B Erf
+is the error function and
+.BI erfc( x )
+is
+.BI 1-erf( x )\f1.
+.PP
+The Bessel functions are computed by
+.BI j0 ,
+.BI j1 ,
+.BI jn ,
+.BI y0 ,
+.BI y1 ,
+and
+.BR yn .
+.SH SOURCE
+.B /libinterp/math.c
+.SH SEE ALSO
+.IR math-intro (2)
--- /dev/null
+++ b/man/2/math-export
@@ -1,0 +1,52 @@
+.TH MATH-EXPORT 2
+.SH NAME
+Math: export_int, export_real, export_real32, import_int, import_real, import_real32 \- conversion to and from external representation
+.SH SYNOPSIS
+.EX
+include "math.m";
+math := load Math Math->PATH;
+
+export_int:    fn(b: array of byte, x: array of int);
+export_real32: fn(b: array of byte, x: array of real);
+export_real:   fn(b: array of byte, x: array of real);
+
+import_int:    fn(b: array of byte, x: array of int);
+import_real32: fn(b: array of byte, x: array of real);
+import_real:   fn(b: array of byte, x: array of real);
+.EE
+.SH DESCRIPTION
+These routines convert between Limbo's internal representation of
+integer and floating-point values, and equivalent external representations as
+arrays of bytes, allowing efficient input/output of binary representations.
+.PP
+.BI Export_int( b , \ x )
+converts each integer element of array
+.I x
+into a sequence of 4 bytes in array
+.IR b .
+.PP
+.BI Export_real( b , \ x )
+converts each double-precision floating-point element of array
+.I x
+into a sequence of 8 bytes in array
+.IR b .
+.PP
+.BI Export_real32( b , \ x )
+converts each double-precision floating-point element of array
+.I x
+to a single-precision value, then encodes it as a sequence of 4 bytes in array
+.IR b .
+.PP
+Each
+.BI import_ T
+operation reverses the transformation of the corresponding
+.BI export_ T
+operation, converting an array of bytes containing a sequence of external representations
+into an array of values of the appropriate internal form.
+.PP
+Values are encoded in big-endian order (most significant byte first), with floating-point
+values represented in IEEE 32-bit or 64-bit form (again, most significant byte first).
+.SH SOURCE
+.B /libinterp/math.c
+.SH SEE ALSO
+.IR math-intro (2)
--- /dev/null
+++ b/man/2/math-fp
@@ -1,0 +1,205 @@
+.TH MATH-FP 2
+.SH NAME
+math-fp \- floating point
+.SH SYNOPSIS
+.EX
+include	"math.m";
+math := load Math Math->PATH;
+
+Infinity, NaN, MachEps, Pi, Degree: real;
+INVAL, ZDIV, OVFL, UNFL, INEX: int;
+RND_NR, RND_NINF, RND_PINF, RND_Z, RND_MASK: int;
+
+getFPcontrol, getFPstatus: fn():  int;
+FPcontrol, FPstatus: fn(r, mask: int): int;
+ilogb: fn(x: real): int;
+scalbn: fn(x: real, n: int): real;
+copysign: fn(x, s: real): real;
+finite, isnan: fn(x: real): int;
+nextafter: fn(x, y: real): real;
+
+fdim, fmin, fmax: fn(x, y: real): real;
+fabs: fn(x: real): real;
+ceil, floor: fn(x: real): real;
+remainder: fn(x, p: real): real;
+fmod: fn(x, y: real): real;
+modf: fn(x: real): (int,real);
+rint: fn(x: real): real;
+.EE
+.SH DESCRIPTION
+These constants and functions provide control over rounding modes,
+exceptions, and other properties of floating point arithmetic.
+.PP
+.B Infinity
+and
+.B NaN
+are constants containing
+the positive infinity and quiet not-a-number values
+of the IEEE binary floating point standard, double precision.
+.B MachEps
+is 2\u\s-2-52\s0\d, the smallest
+.I e
+such that 1+\f2e\f1 is not equal to 1.
+.B Pi
+is the nearest machine number to the infinitely precise value.
+.B Degree
+is
+.BR Pi/ 180.
+.PP
+Each thread has a floating point control word (governing rounding mode and
+whether a particular floating point exception causes a trap)
+and a floating point status word (storing accumulated exception flags).
+The functions
+.B FPcontrol
+and
+.B FPstatus
+copy bits to the control or status word,
+in positions specified by a mask, returning the previous values of the bits
+specified in the mask.
+The functions
+.B getFPcontrol
+and
+.B getFPstatus
+return the words unchanged.
+.PP
+.BR INVAL ,
+.BR ZDIV ,
+.BR OVFL ,
+.BR UNFL ,
+and
+.B INEX
+are non-overlapping single-bit masks
+used to compose arguments or return values.
+They stand for the five IEEE exceptions:
+`invalid operation' (0/0,0+NaN,Infinity-Infinity,sqrt(-1)),
+`division by zero' (1/0), `overflow' (1.8e308), `underflow' (1.1e-308),
+and `inexact' (.3*.3).
+.PP
+.BR RND_NR ,
+.BR RND_NINF ,
+.BR RND_PINF ,
+and
+.BR RND_Z
+are distinct bit patterns
+for `round to nearest even', `round toward negative infinity',
+`round toward infinity', and `round toward 0',
+any of which can be set or extracted from the floating point control
+word using
+.BR RND_MASK .
+For example,
+.B FPcontrol(0,
+.B UNFL)
+makes underflow silent;
+.B FPstatus(0,
+.B INEX)
+checks and clears the inexact flag; and
+.B FPcontrol(RND_PINF,
+.B RND_MASK)
+sets directed rounding.
+.PP
+By default,
+.B INEX
+is quiet,
+.BR OVFL ,
+.BR UNFL , 
+and
+.B ZDIV
+are fatal,
+and rounding is to nearest even.
+Limbo modules are
+entitled to assume this, and if they wish to use quiet
+underflow, overflow, or zero-divide, they must either
+set and restore the control register or clearly document that
+their caller must do so.
+.PP
+The
+.B ilogb
+function
+returns the nearest integral logarithm base 2 of the absolute value of
+.IR x:
+for positive finite
+.IR x ,
+1 \(<=
+.IB x *2\u-\s-2ilogb( x )\s0\d
+< 2,
+and
+.BI ilogb(- x )
+.B =
+.BI ilogb( x )\f1.
+.BI Scalbn( x , n )
+is a scaled power of two:
+.IB x *2\u\s-2n\s0\d\f1.
+.BI Copysign( x , s )
+has the magnitude of
+.I x
+and the
+sign bit of
+.IR s .
+.BI Nextafter( x , y )
+is the machine number nearest x closer to y.
+Finally,
+.BI finite( x )
+is 0 if
+.I x
+is
+.B Nan
+or
+.B Infinity,
+1 otherwise, and
+.BI isnan( x )
+is 1 if
+.I x
+is
+.B Nan
+and 0 otherwise.
+.PP
+The function
+.BI fdim( x , y)
+=
+.IB x - y
+if
+.I x
+is greater than
+.IR  y ,
+otherwise it is 0.
+The functions
+.BR fmin ,
+.BR fmax ,
+.BR fabs ,
+.BR ceil ,
+and
+.B floor
+are the
+customary minimum,
+maximum,
+absolute value,
+and integer rounding routines.
+.PP
+There are two functions for computing the modulus:
+.BI fmod( x , y )
+is the function defined by the C standard which gives the value
+.IB x - i*y
+for some
+.I i
+such that the remainder has the sign of
+.I x
+and magnitude less than the magnitude of
+.IR y ,
+while
+.BI remainder( x , y )
+is the function defined by the IEEE standard
+which gives a remainder of magnitude no more than half the
+magnitude of
+.IR y .
+The function
+.BI modf( x )
+breaks
+.I x
+into integer and fractional parts returned in a tuple, and
+.B rint
+rounds to an integer, following the
+rounding mode specified in the floating point control word.
+.SH SOURCE
+.B /libinterp/math.c
+.SH SEE ALSO
+.IR math-intro (2)
--- /dev/null
+++ b/man/2/math-linalg
@@ -1,0 +1,146 @@
+.TH MATH-LINALG 2
+.SH NAME
+Math: dot, norm1, norm2, iamax, gemm, sort \- linear algebra primitives
+.SH SYNOPSIS
+.EX
+include	"math.m";
+math := load Math Math->PATH;
+
+dot: fn(x, y: array of real): real;
+norm1, norm2: fn(x: array of real): real;
+iamax: fn(x: array of real): int;
+gemm: fn(transa, transb: int,  # upper case N or T
+		m, n, k: int, alpha: real,
+		a: array of real, lda: int,
+		b: array of real, ldb: int, beta: real,
+		c: array of real, ldc: int);
+
+sort: fn(x: array of real, p: array of int);
+.EE
+.SH DESCRIPTION
+These routines implement the basic functions of linear algebra.
+The standard vector inner product and norms are defined as follows:
+.IP
+.BI dot( "x , y" )
+=
+.BI sum( x [ i ]* y\fP[\fPi\fP])
+.IP
+.BI norm1( x )
+=
+.BI sum(fabs( x [ i\  \f5]))
+.IP
+.BI norm2( x )
+=
+.BI sqrt(dot( "x , x" ))
+.PP
+For the infinity norm, the function
+iamax(x)
+computes an integer
+.I i
+such that
+.BI fabs( x [ i ])
+is maximal.
+These are all standard BLAS (basic linear algebra subroutines)
+except that in Inferno they apply only to contiguous (unit stride)
+vectors.
+.PP
+We assume the convention that matrices are represented as
+singly-subscripted arrays with Fortran storage order.
+So for an
+.I m
+by
+.I n
+matrix
+.I A
+we use loops
+with
+.BI 0\(<= i < m
+and
+.BI 0\(<= j < n
+and access
+\f2A\f5[\f2i\f5+\f2m\f5*\f2j\f5]\f1.
+.PP
+Rather than provide the huge number of entry points in BLAS2 and BLAS3,
+Inferno provides the matrix multiply primitive,
+.BR gemm ,
+defined by
+.PP
+.EX
+    \f2A\fP = \f1\(*a\fP*\f2A\fP\f1'\fP*\f2B\fP\f1'\fP + \f1\(*b\fP*\f2C\fP
+.EE
+.PP
+where the apostrophes indicate an optional transposition.
+As shown by the
+work of Kagstrom, Ling, and Van Loan, the other BLAS functionality can
+be built efficiently on top of
+.BR gemm .
+.PP
+Matrix
+.IR a '
+is
+.I m
+by
+.IR k ,
+matrix
+.IR b '
+is
+.I k
+by
+.IR n ,
+and matrix
+.I c
+is
+.I m
+by
+.IR n .
+Here
+.IR a '
+means
+.I a
+.RI ( a ')
+if
+.I transa
+is the constant
+.B 'N'
+.RB ( 'T' ),
+and similarly for
+.IR b '.
+The sizes
+.IR m ,
+.IR n ,
+and
+.I k
+denote the `active' part of
+the matrix.
+The parameters
+.IR lda ,
+.IR ldb ,
+and
+.I ldc
+denote the `leading dimension'
+or size for purposes of indexing.
+So to loop over
+.I c
+use loops
+with
+.BI 0\(<= i < m
+and
+.BI 0\(<= j < n
+and access
+\f2a\f5[\f2i\f5+\f2ldc\f5*\f2j\f5]\f1.
+.PP
+The sorting function
+.BI sort( x , p )
+updates a 0-origin permutation
+.I p
+so that
+.IB x [ p [ i ]]
+\(<=
+.IB x [ p [ i +1]]\f1,
+leaving
+.I x
+unchanged.
+.SH SOURCE
+.B /libinterp/math.c
+.SH SEE ALSO
+.IR math-intro (2)
--- /dev/null
+++ b/man/2/mpeg
@@ -1,0 +1,74 @@
+.TH MPEG 2
+.SH NAME
+mpeg \- interface to the mpeg device driver
+.SH SYNOPSIS
+.EX
+include "draw.m";
+include "mpeg.m";
+mpeg:= load Mpeg Mpeg->PATH;
+
+play:     fn(d:       ref Display,
+               w:       ref Image,
+               dopaint: int,
+               r:       Rect,
+               file:    string,
+               notify:  chan of string): string;
+ctl:      fn(msg: string): int;
+keycolor: fn(d: ref Display): ref Image;
+.EE
+.SH DESCRIPTION
+.B Mpeg
+provides a primitive interface to the
+.IR mpeg (3)
+device.
+.PP
+.B Play
+plays the specified MPEG
+.I file
+in rectangle
+.I r
+within window
+.I w
+on display
+.IR d .
+The
+.I dopaint
+flag specifies whether, before playing the movie, to paint the rectangle with the chroma key colour of the device.
+.PP
+.I Notify
+is a channel upon which to receive errors and status. If
+.I notify
+is
+.BR nil ,
+.B play
+runs synchronously, returning the empty string ("") when the movie completes, or a description of any error.
+If
+.I notify
+is not
+.BR nil ,
+.B play
+spawns an asynchronous process to play the movie and returns the empty string immediately; the process returns the status on the
+.I notify
+channel when the movie completes.
+.PP
+.B Ctl
+writes
+.IR msg ,
+the string given as an argument, to the MPEG control interface
+.BR /dev/mpegctl .
+.PP
+.B Keycolor
+uses the
+.B Display
+.I d
+to create a single-pixel, replicated off screen
+.B Image
+of colour
+.B Chroma
+.RB ( 16r05 ).
+.SH FILES
+.B /dev/mpeg
+.br
+.B /dev/mpegctl
+.SH SOURCE
+.B /appl/lib/mpeg.b
--- /dev/null
+++ b/man/2/msgio
@@ -1,0 +1,155 @@
+.TH MSGIO 2
+.SH NAME
+msgio: getmsg, sendmsg, senderrmsg, getstring, putstring, getbytearray, putbytearray, puterror \- exchange data on delimited and undelimited streams
+.SH SYNOPSIS
+.EX
+include "msgio.m";
+msgio := load Msgio Msgio->PATH;
+
+init:         fn();
+
+getmsg:       fn(fd: ref Sys->FD): array of byte;
+sendmsg:      fn(fd: ref Sys->FD, buf: array of byte, n: int): int;
+senderrmsg:   fn(fd: ref Sys->FD, s: string): int;
+
+getstring:    fn(fd: ref Sys->FD): (string, string);
+putstring:    fn(fd: ref Sys->FD, s: string): int;
+getbytearray: fn(fd: ref Sys->FD): (array of byte, string);
+putbytearray: fn(fd: ref Sys->FD, a: array of byte, n: int): int;
+puterror:     fn(fd: ref Sys->FD, s: string): int;
+.EE
+.SH DESCRIPTION
+.B Msgio
+provides two complementary sets of functions for the exchange of data over a network
+connection that uses a connection-oriented transport protocols.
+The connection is represented by a file descriptor
+.IR fd .
+.PP
+.B Init
+must be called before any other operation of the module.
+.PP
+The first set allows arbitrary data, packed into arrays of bytes, to be exchanged
+on network connections using protocols such as TCP/IP that do not preserve
+record boundaries.
+They are used to implement various authentication protocols, including
+.IR auth (6).
+.PP
+Each data message is transmitted with a five-byte header containing a four-character zero-padded decimal count
+.I n
+terminated by a newline, followed by
+.I n
+bytes of message data.
+An error message has a similar structure, except that the first character
+of the count is replaced by an exclamation mark
+.RB ( ! );
+the message data following
+contains the diagnostic string in its UTF-8 encoding (see
+.IR utf (6)).
+.PP
+.B Getmsg
+reads the next message from
+.I fd
+and returns its data content.
+.PP
+.B Sendmsg
+sends the first
+.I n
+bytes of
+.I buf
+as a message on
+.IR fd ,
+and returns
+.IR n .
+.PP
+.B Senderrmsg
+sends the error message
+.IR s .
+.PP
+The second set of functions provide
+I/O for strings, byte arrays and error strings over network connections that
+provide a record structure for communication (as provided for arbitrary
+networks by
+.IR ssl (3)).
+.PP
+.B Putstring
+writes string
+.I s
+to
+.IR fd.
+It returns the number of bytes written, or -1 if an error occurred.
+Messages written by
+.B putstring
+are truncated to 4096 bytes.
+.PP
+.B Getstring
+reads a string as written by
+.B putstring
+from
+.IR fd
+and returns a tuple
+.RI ( result , error ).
+If successful, the error
+string is nil.
+.PP
+.B Putbytearray
+writes the array of bytes
+.I a
+to
+.IR fd .
+It returns the number of bytes written, or -1 if an error occurred.
+Messages written by
+.B putbytearray
+are truncated to 4096 bytes.
+.PP
+.B Getbytearray
+reads an array of bytes as written by
+.B putbytearray
+from
+.IR fd
+and returns a tuple of the form
+.RI ( result , error ).
+If successful, the error string is nil.
+.PP
+.B Puterror
+writes an error string
+.I s
+to
+.IR fd .
+It can be used in place of
+.B putstring
+or
+.B putbytearray
+to cause a corresponding
+.B getstring
+or
+.B getbytearray
+to fail
+(in the receiving process),
+forcing them to return the error string
+.IR s .
+It may not be longer than
+.BR Sys->ERRMAX
+bytes.
+.SH SOURCE
+.B /appl/lib/msgio.b
+.SH DIAGNOSTICS
+The output functions return an
+.B int
+which is -1 if there was an I/O error,
+and a non-negative value otherwise;
+they also set the system error string.
+.B Getmsg
+returns nil if there was an error reading from
+.IR fd ;
+it sets the system error string to reflect the cause.
+It also returns nil
+if an error message was received instead of a data message;
+the system error string will contain the error message's diagnostic.
+The other input functions (for streams with delimiters)
+return a tuple that includes a string indicating the cause of the
+error, as the second element of the tuple.
+.SH BUGS
+The module is really intended to retrofit the original Inferno authentication
+protocols into a new regime, and is not yet intended for general-purpose use,
+hence the irregular naming and calling conventions, inherited from the original implementation in
+.BR Keyring .
--- /dev/null
+++ b/man/2/names
@@ -1,0 +1,113 @@
+.TH NAMES 2
+.SH NAME
+Names: basename, cleanname, dirname, elements, isprefix, pathname, relative, rooted \- file name manipulation
+.SH SYNOPSIS
+.EX
+include "names.m";
+names := load Names Names->PATH;
+
+basename:  fn(name: string, suffix: string): string;
+cleanname: fn(name: string): string;
+dirname:   fn(name: string): string;
+elements:  fn(name: string): list of string;
+isprefix:  fn(a: string, b: string): int;
+pathname:  fn(els: list of string): string;
+relative:  fn(name: string, root: string): string;
+rooted:    fn(root: string, name: string): string;
+.EE
+.SH DESCRIPTION
+.B Names
+provides operations on file names (path names):
+.TF cleanname
+.PD
+.TP
+.B basename
+Return the trailing component of
+.I name
+(the text after the final
+.LR / ),
+shorn of
+.I suffix
+(which may be null).
+.TP
+.B cleanname
+Return a `cleaner' version of
+.IR name :
+there are no redundant and trailing slashes,
+and directory names
+.L .
+and
+.L ..
+have been interpreted lexically.
+If the result would otherwise be the empty string,
+the name
+.L .
+is returned instead.
+.TP
+.B dirname
+Return the directory component of
+.IR name :
+the string up to but not including the final slash(es).
+.TP
+.B elements
+Return a list of the path elements of
+.IR name :
+the words between slashes.
+If
+.I name
+starts with a
+.LR / ,
+the head of the list will be the string
+\&\f5"/"\fP
+but otherwise slashes do not appear.
+.TP
+.B pathname
+Return a path name formed from a list of elements as produced by
+.BR elements .
+.TP
+.B isprefix
+Return true iff path name
+.I a
+is a prefix of path name
+.IR b .
+.TP
+.B relative
+If
+.I name
+is
+.IB root / X
+for some
+.IR X ,
+return
+.IR X ;
+otherwise return
+.I name
+unchanged.
+.TP
+.B rooted
+Return the path name for
+.I name
+relative to a given
+.I root
+directory (either name may be nil).
+If
+.I name
+itself starts with
+.L /
+or
+.LR # ,
+the result is
+.IR name ;
+otherwise it is
+.IB root / name.
+.PP
+See
+.IR sys-intro (2)
+for details of file name syntax and its interpretation.
+.SH SOURCE
+.B /appl/lib/names.b
+.SH SEE ALSO
+.IR sys-intro (2),
+.IR sys-fd2path (2),
+.IR workdir (2)
+
--- /dev/null
+++ b/man/2/newns
@@ -1,0 +1,49 @@
+.TH NEWNS 2
+.SH NAME
+Newns: newns, newuser \- build a new name space from a description file
+.SH SYNOPSIS
+.EX
+include "newns.m";
+ns := load Newns Newns->PATH;
+
+newns:   fn(user: string, nsfile: string): string;
+newuser: fn(user: string, cap: string, nsfile: string): string;
+.EE
+.SH DESCRIPTION
+.B Newns
+reads file
+.I nsfile
+and builds a new name space based on the commands it contains.
+The file has the format defined by
+.IR namespace (6).
+If
+.I nsfile
+is
+.B nil
+or empty, then
+.B newns
+attempts to read file the file
+.BR namespace .
+.PP
+.B Newuser
+uses a capability
+.I cap
+suitable for
+.IR cap (3)
+to change the current process's user name to
+.IR user ,
+and uses
+.B newns
+and
+.I nsfile
+to build it a new name space.
+The capability is typically obtained via
+.IR factotum (4).
+.SH SOURCE
+.B /appl/lib/newns.b
+.SH SEE ALSO
+.IR sys-bind (2),
+.IR sys-chdir (2),
+.IR sys-pctl (2),
+.IR factotum (4),
+.IR namespace (6)
--- /dev/null
+++ b/man/2/palmfile
@@ -1,0 +1,542 @@
+.TH PALMFILE 2
+.SH NAME
+Palmfile: Categories, DBInfo, Doc, Entry, Pfile, Record \- read Palm™ file formats
+.SH SYNOPSIS
+.EX
+include "bufio.m";
+include "palmfile.m";
+
+palmfile := load Palmfile Palmfile->PATH;
+
+Pfile: adt {
+    fname:  string;
+
+    appinfo:  array of byte;
+    sortinfo: array of int;
+
+    entries:  array of ref Entry;
+
+    open:   fn(name: string, mode: int): (ref Pfile, string);
+. #    create: fn(nil: string, mode: int, perm: int, nil: ref DBInfo): ref Pfile;
+    close:  fn(pf: self ref Pfile): int;
+    read:   fn(pf: self ref Pfile, index: int): (ref Record, string);
+. #    append: fn(pf: self ref Pfile, r: ref Record): int;
+    stat:   fn(pf: self ref Pfile): ref DBInfo;
+. #    wstat:  fn(pf: self ref Pfile, nil: ref DBInfo): string;
+. #       readid:  fn(pf: self ref Pfile, nil: int): (ref Record, string);
+. #       setappinfo:   fn(pf: self ref Pfile, nil: array of byte);
+. #       setsortinfo:  fn(pf: self ref Pfile, nil: array of int);
+};
+
+DBInfo: adt {
+    name:    string; # database name on Palm
+    attr:    int;    # file attributes (defined below)
+    dtype:   string; # database type (byte[4])
+    version: int;    # version of data layout, defined by application
+    creator: string; # creating application (byte[4])
+    ctime:   int;    # creation time
+    mtime:   int;    # modification time
+    btime:   int;    # last backup
+    modno:   int;    # modification number
+    uidseed: int;    # unique record ID seed
+
+    new:     fn(name: string, attr: int, dtype: string,
+                   version: int, creator: string): ref DBInfo;
+};
+
+Entry: adt {
+    id: int;        # resource: id; record: unique ID
+    offset: int;    # offset in file
+    size:   int;    # size in bytes
+    name:   int;    # resource entry only
+    attr:   int;    # record entry only
+};
+
+Record: adt {
+    id:     int;    # resource: ID; data: unique record ID
+    index:  int;    # record index (origin 0)
+    name:   int;    # byte[4]: resource record only
+    attr:   int;    # attributes, defined below, data record only
+    cat:    int;    # category, data record only
+    data:   array of byte;   # content of record
+
+. #       new:    fn(size: int): ref Record;
+};
+
+Categories: adt {
+    renamed: int;    # which categories have been renamed
+    labels:  array of string; # category names
+    uids:    array of int;    # corresponding unique IDs
+    lastuid: int;             # last unique ID assigned
+    appdata: array of byte;   # remaining application-specific data
+
+    new:     fn(labels: array of string): ref Categories;
+    unpack:  fn(a: array of byte): ref Categories;
+    pack:    fn(c: self ref Categories): array of byte;
+};
+
+Doc: adt {
+    version:  int;
+    length:   int;  # uncompressed
+    nrec:     int;  # text records only
+    recsize:  int;  # uncompressed
+    position: int;
+    sizes:    array of int;   # sizes of uncompressed records
+
+    open:     fn(file: ref Pfile): (ref Doc, string);
+    read:     fn(doc: self ref Doc, i: int): (string, string);
+    iscompressed:  fn(doc: self ref Doc): int;
+    unpacktext: fn(doc: self ref Doc, a: array of byte):
+                   (string, string);
+    textlength: fn(doc: self ref Doc, a: array of byte): int;
+};
+
+init:     fn(): string;
+
+filename: fn(s: string): string;
+dbname:   fn(s: string): string;
+
+gets:     fn(a: array of byte): string;
+puts:     fn(a: array of byte, s: string);
+get2:     fn(a: array of byte): int;
+get3:     fn(a: array of byte): int;
+get4:     fn(a: array of byte): int;
+put2:     fn(a: array of byte, v: int);
+put3:     fn(a: array of byte, v: int);
+put4:     fn(a: array of byte, v: int);
+.EE
+.SH DESCRIPTION
+.B Palmfile
+provides read-only access to files in the Palm™ database and document formats.
+It currently handles three types of files:
+Palm Database
+.RB ( .pdb )
+files, which store data for applications;
+Palm Resource
+.RB ( .prc )
+files, which store code resources and user interface resource elements;
+and
+Palm Doc
+.RB ( .doc )
+files, which store compressed documents for the Palm document and e-book readers.
+Database and resource files have a similar structure, with slight differences
+in representation, and differing mainly in how the contents are used.
+.PP
+.B Init
+must be called before any other function in the file.
+It returns a diagnostic if it cannot initialise the module.
+.PP
+.B Pfile
+represents an open Palm file of any type:
+.TP
+.B open()
+Opens file
+.I name
+with the given
+.I mode
+(which must currently be
+.BR Sys->OREAD )
+and returns a tuple
+.RI ( pf , err ).
+.I Pf
+is a new
+.B Pfile
+instance giving access to the file,
+or nil if the open failed, in which case the
+.I err
+string contains a diagnostic.
+.TP
+.IB pf .close()
+Close the file (needed only when writing to a file is eventually supported).
+.TP
+.IB pf .read( index )
+Returns a tuple
+.RI ( rec,\ err )
+where
+.I rec
+is a
+.B Record
+containing the data of the record
+with the given
+.I index
+(origin 0), or
+nil if no such record index exists or it cannot be read.
+In the latter case,
+.I err
+is a diagnostic string.
+.TP
+.IB pf .stat()
+Return the database information for
+.IR pf .
+.TP
+.B entries
+An array of
+.B Entry
+values (see below), one per record.
+The length of the array is consequently the length of
+the file in records.
+It can be nil or empty.
+.TP
+.B appinfo
+Optional application-specific data (see
+.B Categories
+below).
+.TP
+.B sortinfo
+Optional application-specific data (typically
+an array of record IDs in a chosen sorting order).
+.TP
+.B fname
+File name given to
+.BR Pfile.open .
+.PP
+.B DBInfo
+gives the database information for a file:
+.TF creator
+.PD
+.TP
+.B name
+Database name used on the Palm, maximum of 31 characters.
+.TP
+.B attr
+A bit set of file attributes, containing the following values:
+.RS
+.TF Fappinfodirty
+.PD
+.TP
+.B Fresource
+File is a resource file
+.RB ( .prc )
+not a database file
+.RB ( .pdb ).
+.TP
+.B Fronly
+File is read only.
+.TP
+.B Fappinfodirty
+Application information has changed.
+.TP
+.B Fbackup
+No conduit program exists (the whole file must be backed up).
+.TP
+.B Foverwrite
+Overwrite older copy if present.
+.TP
+.B Freset
+Reset PDA after installing this file.
+.TP
+.B Fprivate
+Don't allow copy of this file to be beamed.
+.RE
+.TP
+.B dtype
+String identifying database type (up to 4 characters).
+It is usually the string
+\f5"appI"\fP for resource files.
+.TP
+.B version
+Identifies the version of the data format (application specific).
+.TP
+.B creator
+String identifying creating application (up to 4 characters).
+.TP
+.B ctime
+File creation time, in seconds from the Inferno epoch (see
+.IR daytime (2)).
+.TP
+.B mtime
+Time file last modified, in seconds from the epoch.
+.TP
+.B btime
+Time file last backed up, in seconds from the epoch.
+.TP
+.B uidseed
+Seed for generating unique record IDs (typically set to 0 for database files,
+always 0 for resource files).
+.TP
+.BI new( name,\ attr,\ dtype,\ creator )
+Return a new
+.B DBInfo
+with the given values.
+.PP
+In some applications, it is useful to use a data base name
+(ie,
+.BR DBInfo.name )
+as a component of an Inferno file name.
+The device allows space and slash characters in names, though,
+which makes it hard to use the name directly.
+.B Filename
+maps each space character
+in
+.I s
+to U+00A0 (unpaddable space)
+and each slash character to U+2215 (`division /'),
+and returns the result.
+.B Dbname
+maps the other way.
+.SS Entries and Records
+Each record in the file is represented by an
+.B Entry
+in memory, which holds the record's essential attributes,
+leaving the data on file.
+The meaning of some of the elements depends on whether
+the file is a data file or a resource file.
+.TF offset
+.PD
+.TP
+.B id
+Resource ID, 16 bits (resource file); unique record ID, 24 bits (data file).
+.TP
+.B offset
+Offset in file, in bytes.
+.TP
+.B size
+Size of record in bytes.
+.TP
+.B name
+Name of the resource (resource record only).
+.TP
+.B attrs
+Record attributes (data record only):
+.RS
+.TF Rarchive
+.PD
+.TP
+.B Rdelete
+Delete the record when file next synchronised.
+.TP
+.B Rdirty
+Record has been modified.
+.TP
+.B Rinuse
+Record in use (not typically used in Inferno).
+.TP
+.B Rsecret
+Record is secret (shown on the device only with use of a password).
+.TP
+.B Rarchive
+Archive this record when file next synchronised.
+.TP
+.B Rmcat
+Mask for the 4-bit category field (in
+.B Entry.attrs
+only).
+.RE
+.PP
+Records read from the file are represented by a
+.B Record
+adt containing its data and associated values.
+Some fields are valid only for particular classes of records.
+.TF index
+.PD
+.TP
+.B id
+Resource or record ID, as for
+.BR Entry .
+.TP
+.B index
+Index (origin 0) of the record in the file.
+.TP
+.B name
+Resource name (resource record only).
+.TP
+.B attr
+Record attributes, as above (data record only).
+.TP
+.B cat
+Record's category ID (data record only).
+.TP
+.B data
+The actual data.
+.SS Application data
+The contents of both the ``application information'' and ``sort information'' sections of the file
+are defined by an application in general.
+Even so, both have conventional uses with many Palm applications.
+The Palm software often assigns data records to particular categories (eg, ``Business'' or ``Personal''),
+and stores up to 16 category names and IDs in the application data in a fixed format
+(possibly followed by further data that is application specific).
+This is represented by an instance of
+.BR Categories ,
+which provides the following:
+.TF renamed
+.PD
+.TP
+.B renamed
+Bit set indicating which categories have been renamed (for category 0, bit
+.BR 1<<0 ,
+for 1, bit
+.BR 1<<1 ,
+and so on).
+.TP
+.B labels
+Array of 16 category labels.
+.TP
+.B uids
+Array of 16 category IDs, each in the range 0 to 255.
+(It is said that the Palm itself assigns 0 to 127 and desktop applications assign 128 to 255.)
+.TP
+.B lastuid
+Last unique category ID assigned.
+.TP
+.B appdata
+Any data that remained after unpacking the category data.
+.TP
+.BI new( labels )
+Return a new
+.B Categories
+value for the given array of
+.IR labels ,
+assigning unique IDs to each in turn, starting from 128.
+There can be at most 16 labels; if there are fewer, the remaining labels will be marked as unused
+(empty strings).
+.TP
+.BI unpack( a )
+Unpack the application data in array
+.I a
+(typically
+.IB pf .appinfo
+for some
+.B Pfile
+.IR pf ),
+returning a reference to a new
+.B Categories
+instance.
+A nil value is returned if the array is too short to hold valid category data.
+.TP
+.IB c .pack()
+Pack
+.I c
+into a form suitable for writing back to a file's application information area.
+.PP
+Binary data in Palm files is typically encoded in big-endian form.
+.B Palmfile
+functions account for that internally, but some Palm applications might use
+big-endian data in their own data records.
+Several functions are therefore provided to decode and encode big-endian data:
+.BI get n
+retrieves an integer from the first
+.I n
+bytes of array
+.IR a ;
+.BI put n
+stores a big-endian representation of the value
+.I v
+in the first
+.I n
+bytes of array
+.IR a .
+.PP
+Strings are stored in fixed-length arrays of bytes, always terminated by a zero byte.
+The character encoding is (apparently) Latin-1 (ISO 8859-1), not UTF-8,
+so functions
+.B gets
+and
+.B puts
+are provided to convert between that representation and a Limbo string.
+.SS Documents
+.B Doc
+provides read-only access to Palm documents and (unencrypted) e-books:
+.TF position
+.PD
+.TP
+.BI open( file )
+Given a
+.B Pfile
+.IR file ,
+return a tuple
+.RI ( doc,\ err )
+where
+.I doc
+is a new
+.B Doc
+instance giving access to the document contents in
+.IR file .
+If an error occurs, in particular if
+.I file
+does not appear to be a valid Palm document,
+.I doc
+is nil and the string
+.I err
+diagnoses the error.
+.TP
+.IB doc .iscompressed()
+Returns true (non-zero) if the document is compressed; returns false (zero) otherwise.
+.TP
+.IB doc .read( i )
+Read text record with index
+.I i
+(origin 0),
+returning a tuple
+.RI ( s,\ err )
+where
+.I s
+is the uncompressed text for record
+.IR i ,
+or nil if the record does not exist (or there is an error reading it).
+On any error,
+.I err
+is a diagnostic string.
+Note that
+.I i
+is an index into the set of text records, and is not an index into the set of all records.
+It must be no greater than
+.IB doc .nrec .
+.IB doc .unpacktext( a )
+Returns a tuple
+.RI ( s,\ err )
+where
+.I s
+is the text in array
+.IR a ,
+after uncompressing if
+.I doc
+contains compressed records.
+Following Palm conventions, the text is assumed to be written in the Latin-1 encoding (ISO-8859-1).
+If it is compressed but the data in
+.I a
+is corrupt (cannot be uncompressed),
+.I s
+is nil and
+.I err
+diagnoses the problem.
+.TP
+.IB doc .textlength( a )
+Returns the number of bytes required to store the text in array
+.I a
+once it has been uncompressed (if necessary).
+.TP
+.B version
+The document's version number.
+.TP
+.B length
+The length of the whole document in bytes, when uncompressed.
+.TP
+.B nrec
+Number of text records in the document.
+.TP
+.B recsize
+Size of uncompressed text records.
+.TP
+.B position
+Possibly records the position where reading last stopped.
+.TP
+.B sizes
+Array giving sizes of all text records, once uncompressed.
+.PP
+Most document-reading applications require only
+.B Doc.open
+and
+.BR Doc.read ,
+and perhaps
+.BR Doc.length
+to guide the construction of scroll bars (for instance).
+.SH SOURCE
+.B /appl/lib/palmfile.b
+.SH SEE ALSO
+.IR "Palm® File Format Specification" ,
+Gary Hillerson, Palm Inc.,
+Document Number 3008-004,
+1 May 2001.
+. br
+.IR "[Palm®] standard text document file format" ,
+Paul Lucas, 18 August 1998.
--- /dev/null
+++ b/man/2/plumbmsg
@@ -1,0 +1,291 @@
+.TH PLUMBMSG 2
+.SH NAME
+plumbmsg \- plumbing message module
+.SH SYNOPSIS
+.EX
+include "plumbmsg.m";
+plumbmsg := load Plumbmsg Plumbmsg->PATH;
+Msg: import plumbmsg;
+
+Msg: adt
+{
+    src:    string;
+    dst:    string; 
+    dir:    string;
+    kind:   string;
+    attr:   string;
+    data:   array of byte;
+    # used by applications
+    send:   fn(msg: self ref Msg): int;
+    recv:   fn(): ref Msg;
+    # used by plumb and send, recv
+    pack:   fn(msg: self ref Msg): array of byte;
+    unpack: fn(b: array of byte): ref Msg;
+};
+
+Attr: adt
+{
+    name: string;
+    val:  string;
+};
+
+init:         fn(willsend: int, rcvport: string, maxdata: int): int;
+shutdown:     fn();
+string2attrs: fn(s: string): list of ref Attr;
+attrs2string: fn(l: list of ref Attr): string;
+lookup:       fn(attrs: list of ref Attr, name: string): (int, string);
+.EE
+.SH DESCRIPTION
+.B Plumbmsg
+is an interface for message-passing between applications
+via the
+.IR plumber (8).
+It allows applications to receive messages from the plumber on a logical input port,
+and send messages to other applications via the plumber.
+.PP
+.B Init
+must be called once when the application starts, to
+set up its plumbing connections.
+Applications can choose to send messages, receive them, or do both.
+Note that
+the plumber must be running before any of these functions are useful.
+Normally, that is done by the window system's initialisation procedure,
+but in specialised systems, plumbing can be used for attribute-oriented
+communication even without a window system.
+.PP
+If the application will be sending
+messages via the plumber, the value
+.I willsend
+must be non-zero, and
+.B init
+will open an appropriate channel to the plumber; if the application
+will not send messages, the value should be zero.
+.PP
+If the application is prepared to receive messages, the parameter
+.I rcvport
+names its logical input port,
+which must also be known to the plumber (ie, it must
+be named as a possible destination in
+.IR plumbing (6));
+.B init
+will open an appropriate channel to receive messages from the plumber.
+The parameter
+.I maxdata
+gives the size in bytes of the largest message
+the application is prepared to receive.
+Applications that only send messages set
+.I rcvport
+to nil.
+.PP
+.B Init
+returns returns -1 if for any reason either connection cannot be set up correctly,
+in particular if the plumber is not running or the input port is unknown.
+Otherwise it returns a non-negative value.
+.PP
+The following program fragment establishes input and output plumbing
+for an application `edit':
+.IP
+.EX
+plumbed := 0;
+plumbmsg = load Plumbmsg Plumbmsg->PATH;
+if(plumbmsg->init(1, "edit", 1000) >= 0)
+	plumbed = 1;
+.EE
+.PP
+The variable
+.B plumbed
+is set to allow the application to disable its plumbing user interface
+(and not attempt to send messages) if initialisation fails.
+.PP
+The
+.B Msg
+adt
+encapsulates the message data routed between applications and
+provides member functions to send and receive them.
+Its components are used as follows:
+.TF dataxx
+.PD
+.TP
+.B src
+The name of the program generating the message.
+.TP
+.B dst
+The output port to which the plumber should route the message.
+In practice, destination is often left empty, and
+the destination port will be determined by
+the plumber applying the automatic routing rules
+(cf.
+.IR plumbing (6))
+.TP
+.B dir
+The directory in which to interpret the data (eg, if the data is a local file name).
+.TP
+.B kind
+The format of the data.
+Currently,
+.RB ` text '
+is the only type that applications understand, but the plumbing system
+can route any kind of data.
+.TP
+.B attr
+A string containing
+.IB name = value
+pairs (eg,
+.BR click=7 ),
+separated by tabs.
+Normally the value should be created using
+.B attrs2string
+and parsed using
+.BR string2attrs ,
+described below.
+.TP
+.B data
+The message to be conveyed.
+If
+.B kind
+is
+.BR text ,
+and the message is a string
+.IR s ,
+.B data
+will be
+.RB ` array
+.B of
+.BI byte " s'"
+(ie, its UTF encoding).
+.PD
+.PP
+Plumbing messages are created directly using Limbo's
+.B ref
+operator, giving the desired value to each field.
+For example:
+.IP
+.EX
+	msg := ref Msg(
+		"WmSh",
+		"",
+		workdir->init(),
+		"text",
+		attr,
+		array of byte text);
+.EE
+.PP
+The plumbing messages are exchanged with
+the plumber
+using two member functions:
+.TP
+.IB m .send( msg )
+Writes a plumbing message to the
+plumber.
+It returns the number of bytes written (the result of
+.I write
+in
+.IR sys-read (2)
+which does the writing).
+It returns -1 if the plumber cannot route the message.
+.TP
+.B Msg.recv()
+Reads a plumbing message from the file
+representing the application's input port,
+previously opened by
+.BR init .
+It waits for a message, and returns a reference to a
+.B Msg
+that contains the message data.
+.PP
+.B Shutdown
+sends a message to the plumber
+that shuts down plumbing for the application's input port
+.IR rcvport .
+An application
+.I must
+call it before it exits if it has an active input port.
+.PP
+.B String2attrs
+takes a string containing a tab-separated list of attribute pairs and returns a list of references to
+.B Attr
+adts.
+.PP
+.B Attr2string
+converts a list of references to
+.B Attr
+adts into a string of the form
+.IB name = value name = value
+\&. . .  .
+The
+.IB name = value
+pairs are separated by a single tab.
+.PP
+.B Lookup
+searches
+.I attrs
+for an attribute
+.IR name ,
+and if found, returns the tuple
+.BI (1, value ) .
+If
+.I name
+is not found,
+.B lookup
+returns
+.BR "(0, nil)" .
+.SS Packed message format
+The format of plumbing messages as transmitted, and member functions
+that encapsulate it, are documented here for completeness, and in case
+the details are useful in interpreting plumbing messages outside the Inferno environment.
+.PP
+Plumbing messages have a fixed structure: five lines of text
+giving UTF representations of the
+corresponding fields of
+.BR Msg ,
+then a line giving the length of
+.B data
+in decimal,
+followed by the bytes of
+.BR data :
+.IP
+.nf
+.IB source \en
+.IB destination \en
+.IB directory \en
+.IB kind \en
+.IB attributes \en
+.IB n \en
+.IB n " bytes"
+.fi
+.PP
+The details are encapsulated in two functions:
+.TP
+.IB m .pack()
+.B Pack
+packs the contents
+.B Msg
+.I m
+into an array of byte for subsequent transmission using
+.I write
+(see
+.IR sys-read (2)).
+.TP
+.BI Msg.unpack( b )
+Unpack unpacks an array of byte
+.I b
+to form a copy of the original
+.BR Msg ,
+which it returns.
+.SH FILES
+.TF /chan/plumb.rcvport
+.TP
+.B /chan/plumb.input
+file to send messages to the plumber
+.TP
+.BI /chan/plumb. rcvport
+file to receive messages routed to the logical name
+.I rcvport
+.SH SOURCE
+.B /appl/lib/plumbmsg.b
+.SH BUGS
+.I Shutdown
+should not be needed:
+the
+.IR plumber (8),
+as a file server, must know that a particular client has vanished.
--- /dev/null
+++ b/man/2/pop3
@@ -1,0 +1,80 @@
+.TH POP3 2
+.SH NAME
+pop3 \- Post Office Protocol
+.SH SYNOPSIS
+.EX
+include "pop3.m";
+pop3 := load Pop3 Pop3->PATH;
+
+open:       fn(user, password, server: string): (int, string);
+stat:       fn(): (int, string, int, int);
+msglist:    fn(): (int, string, list of (int, int));
+msgnolist:  fn(): (int, string, list of int);
+top:        fn(m: int): (int, string, string);
+get:        fn(m: int): (int, string, string);
+delete:     fn(m: int): (int, string);
+close:      fn(): (int, string);
+.EE
+.SH DESCRIPTION
+.B Pop3
+provides an interface to the Post Office Protocol
+POP3 through a set of functions.
+.PP
+.B Open
+opens a connection to a POP3
+.IR server ,
+logging in as the specified
+.IR user
+with the given
+.IR password .
+If
+.I server
+is
+.IR nil ,
+.B open
+uses
+.BR $pop3 ,
+which must be defined in
+.IR ndb (6).
+The remaining functions assume a successfully opened connection.
+.PP
+.B Stat
+returns the status of the user's mailbox.
+The third element of its return tuple is the number of
+messages and the fourth is the total number of bytes in the messages.
+.PP
+.B Msglist
+lists the user's mailbox. The third element in its return tuple gives a list of pairs of numbers
+comprising
+.RI ( "message number, bytes in message" ).
+.PP
+.B Msgnolist
+lists the user's mailbox as above but omits the bytes in each message.
+.PP
+.B Top
+returns the top of message
+.IR m .
+.PP
+.B Get
+returns the full text of message
+.IR m .
+.PP
+.B Delete
+deletes message
+.IR m .
+.PP
+.B Close
+closes the connection to the POP3 server.
+Note that subsequent reconnections to the server
+may renumber the messages in the mail box and will certainly do so if the last connection
+deleted messages.
+.PP
+Note also that a connection is static in the sense that mail messages entering the server during 
+a connection will not be accessible. A reconnection is needed to see newly arrived messages.
+.SH SOURCE
+.B /appl/lib/pop3.b
+.SH SEE ALSO
+.IR acme (1)
+.SH DIAGNOSTICS
+All these functions return -1
+and an error message on failure as the first two entries in their return tuples.
--- /dev/null
+++ b/man/2/popup
@@ -1,0 +1,102 @@
+.TH POPUP 2
+.SH NAME
+Popup: mkbutton, changebutton, event, add \- popup list box pseudo-widget
+.SH SYNOPSIS
+.EX
+include "popup.m";
+popup := load Popup Popup->PATH;
+
+init:         fn();
+mkbutton:     fn(win: ref Tk->Toplevel, name: string,
+                 items: array of string, n: int): chan of string;
+changebutton: fn(win: ref Tk->Toplevel, name: string,
+                 items: array of string, n: int);
+event:        fn(win: ref Tk->Toplevel, e: string,
+                 items: array of string): int;
+add:          fn(items: array of string, s: string):
+                 (array of string, int);
+.EE
+.SH DESCRIPTION
+.B Popup
+implements popup list boxes as Tk pseudo-widgets.
+This module has since been superseded by
+.IR choicebutton (2)
+in Tk itself, but remains for the moment for compatibility.
+.PP
+.B Init
+must be called once to initialise the module.
+.PP
+.B Mkbutton
+creates a new button
+.IR name ,
+in Tk toplevel
+.IR win .
+It returns a channel on which it delivers events (see
+.BR event ,
+below).
+Once created,
+.I name
+can be packed like any other Tk widget.
+When the button is pressed with button 1,
+a menu pops up offering a choice from the given
+.IR items .
+The value
+.I n
+is the index in
+.I items
+of the button's initial value.
+The current choice is always displayed in the button.
+If
+.I items
+is nil or an empty array, the string
+.RB ` ----- '
+is displayed instead.
+.PP
+Having created a popup button, the application must receive values on
+the channel returned by
+.B mkbutton
+and pass each value it receives to
+.BR event
+(as parameter
+.IR e ).
+(Typically the application will receive on the channel in an
+.B alt
+statement that watches other channels too.)
+.B Event
+returns the result of a selection, or -1 if no selection was made.
+In either case, the text of the button
+.I name
+will reflect the currently active selection
+(the application can fetch it using
+.RB ` cget\ -text ').
+The
+.I items
+parameter must match that given to
+.B mkbutton
+(or most recently given to
+.BR changebutton ).
+.PP
+.B Changebutton
+changes the list of
+.I items
+in an existing popup button
+.IR name ,
+and sets its initial selection to the item with index
+.IR n .
+.PP
+.B Add
+adds string
+.I s
+to the array
+.IR items ,
+if it is not already there,
+and in either case returns the resulting new array and
+the index of
+.I s
+therein.
+It is useful for calculating a list of items dynamically.
+.SH SOURCE
+.B /appl/lib/popup.b
+.SH SEE ALSO
+.IR tabs (2),
+.IR tk (2)
--- /dev/null
+++ b/man/2/prefab-0intro
@@ -1,0 +1,75 @@
+.TH PREFAB-INTRO 2
+.SH NAME
+Prefab: intro \- Interactive TV tookit
+.SH SYNOPSIS
+.EX
+include "draw.m";
+include "prefab.m";
+prefab := load Prefab Prefab->PATH;
+.EE
+.SH DESCRIPTION
+The
+.B Prefab
+module contains components for building graphics objects suitable for
+Interactive Television (ITV) applications using infrared remote controls.
+Using the
+.B Draw
+module's operations
+for simple text and images
+(see
+.IR draw-intro (2)),
+the toolkit can group individual items,
+treat those groups as units, and then activate the
+items on command.
+The other user interface toolkit,
+.IR tk (2),
+provides facilities for keyboard- and mouse-driven applications.
+.PP
+The objects on the screen are of type
+.BR Compound ,
+each of which occupies a unique window on the
+display and contains objects of type
+.BR Element .
+An
+.B Element
+may be a single object or a list of further
+.BR Elements ,
+to build hierarchically structured components.
+.PP
+.B Prefab
+defines
+.B Environ
+and
+.B Style
+types that specify the appearance of objects: their colours, fonts,
+backgrounds, and so on.
+A
+.B Style
+gives font and colour information, while
+an
+.B Environ
+identifies the
+.B Screen
+upon which the items will be displayed and the
+.B Style
+in which they will be drawn.
+.PP
+Applications should allocate
+.B Elements
+and
+.B Compounds
+only through the appropriate member functions, as described in the corresponding
+sections of the manual.
+Items created with regular Limbo
+definitions will not work properly.
+Moreover, except where indicated,
+applications should not modify the data members directly.
+Although the type definitions make data members visible,
+the members should usually be treated as read-only data.
+.SH SOURCE
+.B /libinterp/prefab.c
+.br
+.B /libprefab/*.c
+.SH SEE ALSO
+.IR draw-intro (2),
+.IR ir (2)
--- /dev/null
+++ b/man/2/prefab-compound
@@ -1,0 +1,262 @@
+.TH PREFAB-COMPOUND 2
+.SH NAME
+prefab: Compound \- windows for ITV toolkit
+.SH SYNOPSIS
+.EX
+include "draw.m";
+include "prefab.m";
+prefab := load Prefab Prefab->PATH;
+
+Compound: adt
+{
+    image:     ref Draw->Image;
+    environ:   ref Environ;
+    r:         Draw->Rect;
+    title:     ref Element;
+    contents:  ref Element;
+  
+    iconbox:   fn(env: ref Environ,
+               p: Draw->Point, title: string, icon,
+               mask: ref Draw->Image):
+               ref Compound;
+    textbox:   fn(env: ref Environ,
+               r: Draw->Rect,  title, text: string):
+               ref Compound;
+    layoutbox: fn(env: ref Environ,
+               r: Draw->Rect,  title: string, lay: list of Layout):
+               ref Compound;
+    box:       fn(env: ref Environ,
+               p: Draw->Point, title, elist: ref Element):
+               ref Compound;
+    draw:      fn(comp: self ref Compound);
+    redraw:      fn(comp: self ref Compound, r: Draw->Rect);
+    scroll:    fn(comp: self ref Compound, elem: ref Element,
+			d: Draw->Point);
+    show:      fn(comp: self ref Compound, elem: ref Element): int;
+    select:    fn(comp: self ref Compound,
+               elem: ref Element, i: int, c: chan of int):
+               (int, int, ref Element);
+    tagselect: fn(comp: self ref Compound,
+               elem: ref Element, i: int, c: chan of int):
+               (int, int, ref Element);
+    highlight: fn(comp: self ref Compound,
+               elem: ref Element, on: int);
+};
+.EE
+.SH DESCRIPTION
+.B Compound
+is the data type defining boxes drawn on the screen.
+Each appears in a new window,
+.BR Compound.image ,
+and holds a (possibly
+.BR nil )
+title
+.B Element
+and contents
+.BR Element .
+It occupies the space on
+the screen defined by
+.BR Compound.r .
+Allocating a
+.B Compound
+creates
+a window but does not draw it;
+after the
+.B Compound
+is built,
+.B Compound.draw
+must be called to make it visible.
+Compounds have a border around them, drawn in
+.B Style.edgecolor
+and contain from top to bottom the title (if any),
+a horizontal line (if there is a title), and the contents.
+.PP
+.TP
+.B "Compound.iconbox(\f2env\fP, \f2p\fP, \f2title\fP, \f2icon\fP, \f2mask\fP)
+Creates a
+.B Compound
+whose contents are made by calling
+.B Element.icon
+(see
+.IR prefab-element (2))
+using the
+.I icon
+and
+.IR mask .
+The
+.BR Compound 's
+upper left corner is at
+.IR p ;
+its size is determined by the size of the
+.I icon
+and
+.IR mask .
+.TP
+.B "Compound.textbox(\f2env\fP, \f2r\fP, \f2title\fP, \f2text\fP)
+Creates a
+.B Compound
+whose contents are made by calling
+.B Element.text
+using the supplied
+.IR text .
+As with
+.BR Element.text ,
+the resulting contents
+.B Element
+will be actually a list of text
+.B Elements
+if the text occupies multiple lines on the screen.
+The rectangle behaves as in
+.BR Element.text .
+.TP
+.B "Compound.layoutbox(\f2env\fP, \f2r\fP, \f2title\fP, \f2layoutlist\fP)
+Like
+.BR Compound.textbox ,
+but based on
+.B Element.layout
+rather than
+.BR Element.text .
+.TP
+.B "Compound.box(\f2env\fP, \f2p\fP, \f2title\fP, \f2element\fP)
+Creates a
+.B Compound
+whose contents are made from an existing
+.BR Element .
+To build complex structures, use the
+.BR Element -building
+functions,
+size the
+.B Element
+appropriately,
+and use
+.BR Compound.box .
+The result is positioned with its upper left corner at
+.IR p ,
+and with size determined by that of the
+.BR Element .
+.PP
+However a
+.B Compound
+is built,
+its size computation makes allowance for the border and title.
+Moreover, if the requested size and placement makes part
+appear offscreen, the result may be moved to display it better.
+.TP
+.B Compound.draw()
+This function
+uses the description of the title and contents to paint the
+on-screen image.
+It takes no arguments;
+all the information is included in the description.
+.TP
+.BI Compound.redraw( r )
+Like
+.BR Compound.draw ,
+but restricts the drawing to the specified
+.B Rect
+.IR r .
+.PP
+.TP
+.ft 5
+Compound.select( \f2element\fP, \f2index\fP, \f2c\fP ): (int, int, Element)
+.ft 1
+The channel
+.I c
+is a
+.B chan
+of
+.B int
+connected to an IR remote control interface, typically acquired through the program's
+.B Context
+(see
+.IR draw-context (2)).
+The
+.I element
+is contained in the
+.BR Compound ,
+and may be at any level of its structure.
+It
+is most usefully a list,
+but may be a singleton, in which case it behaves like a list of one element.
+The arrow keys on the remote control scroll through the members of the list,
+using the
+.B Style.highlightcolor
+associated with each member element to indicate selection.
+A
+.B Select
+key on the remote control triggers
+.B select
+to return a tuple
+.BI ( key ,
+.IB index ,
+.IB elem )\f1;
+.I key
+is the key code of the action (here
+.BR Select ),
+.I index
+is the number of the element in the list (ignoring separators),
+and
+.I elem
+is the list member highlighted when the key was hit.
+Any other key returns the same tuple with
+.I index
+\-1 and the value of the key.
+The
+.I elem
+returned is always the most recently highlighted,
+even if the result was not a selection.
+When
+.B select
+returns, it always restores the default appearance of the list.
+.PP
+.TP
+.ft 5
+Compound.tagselect( \f2element\fP, \f2index\fP, \f2c\fP): (int, int, Element)
+.ft 1
+Like
+.BR Compound.select ,
+but rather than selecting among all the elements
+of a list, it selects among only those elements with
+defined
+tags in the
+structure contained within the specified
+.IR element .
+.TP
+\f5Compound.highlight(\fP\f2element\fP\f5,\  \fP\f2on\fP\f5)\fP
+Set the highlighting to be ``on'' or ``off''
+for the
+.I element
+within the compound.
+.TP
+\f5Compound.scroll(\fP\f2element\fP\f5,\  \fP\f2d\fP\f5)\fP
+Like
+.BR Element.scroll :
+scroll the
+.I element
+within the compound.
+The updated image is redrawn after the scrolling.
+.TP
+.ft 5
+\f5Compound.show(\fP\f2element\fP\f5,\  \fP\f2d\fP\f5)\fP
+.ft 1
+Like
+.BR Element.show :
+make sure the
+.I element
+is visible
+within the rectangle of the top-level
+.B Element
+of the
+.BR Compound ,
+that is, in effect call
+.ft 5
+Element.show(Compound.contents, \f2element\fP);
+.ft 1
+.SH SOURCE
+.B /libinterp/prefab.c
+.br
+.B /libprefab/*.c
+.SH SEE ALSO
+.IR prefab-element (2),
+.IR prefab-environ (2),
+.IR prefab-style (2)
--- /dev/null
+++ b/man/2/prefab-element
@@ -1,0 +1,468 @@
+.TH PREFAB-ELEMENT 2
+.SH NAME
+prefab: Element \- menu and display elements for ITV toolkit
+.SH SYNOPSIS
+.EX
+include "draw.m";
+include "prefab.m";
+prefab := load Prefab Prefab->PATH;
+
+# types of Elements
+EIcon:      con 0;
+EText:      con 1;
+ETitle:     con 2;
+EHorizontal:con 3;
+EVertical:  con 4;
+ESeparator: con 5;
+
+# first arg to Element.adjust: size of elements
+Adjpack:    con 10; # leave alone, pack tightly
+Adjequal:   con 11; # make equal
+Adjfill:    con 12; # make equal, filling available space
+
+# second arg: position of element within space
+Adjleft:    con 20;
+Adjup:      con 20;
+Adjcenter:  con 21;
+Adjright:   con 22;
+Adjdown:    con 22;
+
+Layout: adt
+{
+  font:    ref Draw->Font;
+  color:   ref Draw->Image;
+  text:    string;
+  icon:    ref Draw->Image;
+  mask:    ref Draw->Image;
+  tag:     string;
+};
+
+Element: adt
+{
+  kind:   int;
+  r:      Draw->Rect;
+  environ:ref Environ;  
+  tag:    string;       
+  
+  # different fields defined for different kinds of Elements
+  kids:   cyclic list of ref Element; # children of elists
+  str:    string;           # text in an EText element
+  mask:   ref Draw->Image;  # part of Eicon, ESeparator
+  image:  ref Draw->Image;  # part of Eicon, ESeparator, EText, Etitle
+  font:   ref Draw->Font;   # part of EText, Etitle
+  
+  icon:      fn(env: ref Environ, r: Draw->Rect,
+               icon, mask: ref Draw->Image):
+               ref Element;
+  text:      fn(env: ref Environ, text: string,
+               r: Draw->Rect, kind: int): ref Element;
+  layout:    fn(env: ref Environ, lay: list of Layout,
+               r: Draw->Rect, kind: int): ref Element;
+  elist:     fn(env: ref Environ, elem: ref Element, kind: int):
+               ref Element;
+  separator: fn(env:ref Environ, r: Draw->Rect,
+               icon, mask: ref Draw->Image): ref Element;
+
+  append:    fn(elist: self ref Element, elem: ref Element): int;
+  adjust:    fn(elem: self ref Element, equal: int, dir: int);
+  clip:      fn(elem: self ref Element, r: Draw->Rect);
+  scroll:    fn(elem:self ref Element, d: Draw->Point);
+  translate: fn(elem:self ref Element, d: Draw->Point);
+  show:      fn(elist: self ref Element, elem: ref Element): int;
+};
+.EE
+.SH DESCRIPTION
+.B Prefab
+.B Elements
+are the building blocks of the ITV toolkit.
+They represent and display text and pictures that can be grouped in
+arbitrary two-dimensional menus to be walked by the infrared remote control.
+.B Elements
+are packaged within
+.B Compounds
+(see
+.IR prefab-compound (2))
+for display.
+.TF environ
+.PD
+.TP
+.B environ
+This specifies the element's environment.
+.TP
+.B image
+If the element needs an
+.B Image
+object (see
+.B kind
+below), this member specifies it.
+.TP
+.B kind
+The
+.B Prefab
+module defines six
+.B Element
+varieties, each labelled by a defined constant in the
+.B kind
+member.
+.RS
+.TP
+.B EIcon
+An image.
+.TP
+.B EHorizontal
+A horizontal list of elements.
+.TP
+.B ESeparator
+An
+.B Image
+object, like an
+.BR EIcon ,
+but intended to fill space in a list, rather than to serve
+as an element of the list.
+Separators are ignored when selecting or highlighting list elements.
+.TP
+.B EText
+A single line of text.
+Text for this element will be drawn with
+.B Style.textfont
+and
+.BR Style.textcolor .
+.TP
+.B ETitle
+A single line of text, usually giving the title of a
+.B Compound
+object.
+Text for this element will be drawn with
+.B Style.titlefont
+and
+.BR Style.titlecolor .
+.TP
+.B EVertical
+A vertical list of elements.
+.RE
+.TP
+.B mask
+When an element contains an
+.B Image
+object, the
+.B Image.draw
+function will be used to display it on the screen.
+The
+.B mask
+image is used as an argument to
+.BR Image.draw ;
+see
+.IR draw-intro (2)
+for more information.
+.TP
+.B r
+All
+.B Elements
+are rectangular, occupying the
+position on the display specified by
+.BR r.min .
+The size of the element also depends on
+.BR r .
+During creation,
+if the rectangle is degenerate (has
+zero size), the element takes its size from the
+sizes of its components:
+the image size for icons, text width for strings, etc.
+Otherwise, the element's size matches the rectangle.
+.TP
+tag
+The
+.B tag
+of an element serves two purposes: it allows an element to be labelled
+with arbitrary invisible text, and provides a mechanism to control which
+elements of a list may be selected: see the description of
+.B Compound.tagselect
+in
+.IR prefab-compound (2).
+The
+.B tag
+field of an element may be modified freely after the element is created.
+.TP
+.BI icon( env\fP,\fP\ r\fP,\fP\ icon\fP,\fP\ mask )
+Build one
+.B EIcon
+element to be drawn with the
+.I icon
+and
+.IR mask .
+The rectangle,
+.IR r ,
+gives the element's position and size.
+.TP
+.BI text( env\fP,\fP\ text\fP,\fP\ r\fP,\fP\ kind )
+Build a textual element or a list of textual elements.
+.I Kind
+may be
+.B EText
+or
+.BR ETitle ,
+determining the style of the drawn
+.IR text .
+The resulting
+.B Element
+object may be a single element or a
+.B EVertical
+list of the appropriate kind, if the text occupies
+more than one line.
+The text is folded as necessary to
+accommodate the available horizontal space; newlines in
+the text cause line breaks.
+The width of the
+text is determined by
+.IR r ,
+but if
+.I r
+has no width, it will be
+set by the text itself.
+The height of the Element is also
+determined by
+.IR r ;
+again, if the height of
+.I r
+is zero, the Element will be made as tall as necessary (if
+.I r
+is not tall enough, the rest of the text may be made visible
+by calls to
+.BR Element.scroll ).
+Thus one may choose
+a specific size or let the
+.I text
+determine the size by setting
+.I r
+suitably.
+.TP
+.BI layout( env\fP,\fP\ lay\fP,\fP\ r\fP,\fP\ kind )
+Like
+.BR Element.text ,
+but builds a structure using the contents of the list
+.I lay
+of
+.B Layout
+structures.  The
+.B Layout
+type
+allows construction of a more general form of textual display
+by providing fine control over the font and colour in which to
+display text and the inclusion of images as textual elements.
+It also allows setting of the
+.B tag
+for each component of the resulting element or list of elements.
+If the
+.B Layout
+has a
+.RB non- nil
+.B image
+field, it is taken as a description of a
+picture to be incorporated in the text as an
+.B EIcon
+element (and the
+.B text
+field is ignored);
+otherwise the
+.B text
+field specifies the text to be drawn in the indicated
+.B font
+and
+.BR color .
+As with
+.BR Element.text ,
+.B Element.layout
+does all the geometry management, text line breaking and folding, etc.
+.TP
+.BI elist( env\fP,\fP\ elem\fP,\fP\ kind )
+Start a list of
+.B Element
+objects.
+The
+.I kind
+may be
+.B Prefab\->EHorizontal
+or
+.BR Prefab\->EVertical ,
+specifying the orientation of the list.
+.I Elem
+will be the first element of the list.
+It may be
+.BR nil ,
+which creates an empty list of the
+requested orientation.
+Subsequent
+.B Element.append
+calls build the list.
+.TP
+.BI separator( env\fP,\fP\ r\fP,\fP\ icon\fP,\fP\ mask )
+Build one
+.B ESeparator
+element to be drawn with the
+.I icon
+and
+.IR mask .
+The rectangle,
+.IR r ,
+gives the element's position and size.
+.TP
+.IB elist .append( elem )
+Append one
+.BR Element ,
+.IR elem ,
+to an existing list,
+.IR elist .
+The new element will appear after those already
+there, either to the right for
+.B EHorizontal
+or below for
+.B EVertical
+lists.
+.TP
+.IB elem .adjust( spacing\fP,\fP\ dir )
+Format the list so its elements abut.
+The list-building functions such as
+.B append
+attempt to build a sensible geometry.
+Alternatively, one can build a list using
+degenerate geometry and then let
+.B adjust
+compute the geometry for the whole list.
+For example, one could place all the elements
+at (0,\ 0) and then call
+.B adjust
+to decide where the elements belong.
+.IP ""
+.I Spacing
+specifies how the elements fit together:
+.RS
+.TP
+.B Adjequal
+Makes them all equal sized in the dimension of the list,
+but only as big as the largest element.
+For example, if the element is a horizontal list,
+all elements will be as wide as the widest item.
+If the list is too big for the allocated space,
+only some will be visible. 
+.TP
+.B Adjfill
+Makes them all equal sized in the dimension of the list,
+expanding the elements to fit the space of the list's
+.BR Element.r .
+If the list is too big for the allocated space,
+only some will be visible. 
+.TP
+.B Adjpack
+Packs elements as tightly as possible,
+using the ``natural'' size of each element
+and setting their rectangles against one another.
+.RE
+.IP ""
+.I Dir
+controls how each element is placed in its allotted space:
+.RS
+.TP
+.B Adjcenter
+Place each element in the middle of its space.
+.TP
+.B Adjdown
+In a vertical list, move each element to the bottom.
+.TP
+.B Adjleft
+In a horizontal list, move each element to the left.
+.TP
+.B Adjright
+In a horizontal list, move each element to the right.
+.TP
+.B Adjup
+In a vertical list, move each element to the top.
+.RE
+.TP
+.IB elem .clip( r )
+The drawing model for
+.B Element
+objects is that they occupy some space in 
+the plane, which may be larger or smaller than the space occupied
+by its constituent text, icons, sub-elements, etc.
+The
+.I clip
+function sets the rectangle of
+.I elem
+to
+.IR r ,
+without changing its internal geometry.
+Any space made newly visible by this
+will be filled in by the list's
+.BR Style.elemcolor .
+For example, if
+.B e
+is an icon element just large enough to display its image,
+.B e.clip(e.r.inset(-2))
+will make a two-pixel-wide border around the icon when
+it is drawn.
+As another example, lists are scrolled by leaving their clip
+rectangle intact while translating the list elements' coordinates.
+.TP
+.IB elem .scroll( d )
+.I D
+is a
+.BR Point ,
+representing a vector;
+.I elem
+is an
+.B Element
+object to be scrolled.
+The
+.B scroll
+function leaves the element's
+.B r
+alone and translates all the constituent pieces of the list by
+.IR d ,
+causing a different portion of
+.I elem
+to be visible in its rectangle.
+.TP
+.IB elem .translate( d )
+Like
+.IB elem .scroll( d ),
+but moves
+.B r
+too, thus translating the entire
+.B Element
+rather than just the visible part within a fixed rectangle.
+.TP
+.IB elist .show( elem )
+.B Show
+does nothing if
+.I elem
+is not a list.
+If it is a list, the list is scrolled
+so
+.IR elem ,
+which must be a member of the list, is visible through
+.IR elist.r .
+.PP
+The geometry of elements and the membership of lists
+may be modified only through
+the provided functions; the Limbo-visible structure is
+(intended to be) read-only.
+Tags, text, and images may be modified freely
+by the application, but at the moment there is no way to recalculate
+the geometry if the components of an
+textual or image icon change size.
+.PP
+.B Element
+objects are never drawn explicitly, nor are they drawn after any
+.B Element
+operation.
+They are made visible only by calls to
+.BR Compound.draw ,
+described by
+.IR prefab-compound (2).
+.SH SOURCE
+.B /libinterp/prefab.c
+.B /libprefab/*.c
+.SH SEE ALSO
+.IR prefab-compound (2)
+.IR prefab-environ (2)
+.IR prefab-style (2)
--- /dev/null
+++ b/man/2/prefab-environ
@@ -1,0 +1,54 @@
+.TH PREFAB-ENVIRON 2
+.SH NAME
+prefab: Environ \- environment for ITV toolkit
+to provide a graphics framework for a collection of items
+.SH SYNOPSIS
+.EX
+include "draw.m";
+include "prefab.m";
+prefab := load Prefab Prefab->PATH;
+
+Environ: adt
+{
+    screen: ref Draw->Screen;
+    style:  ref Style;
+
+};
+.EE
+.SH DESCRIPTION
+The
+.B Environ
+type collects the
+.B Style
+and
+.B Screen
+types necessary to specify how and where to draw
+.B Prefab
+items.
+.TP 10
+.B screen
+Specifies the
+.B Screen
+upon which items will be displayed.
+.TP
+.B style
+Specifies the
+.B Style
+for items in an environment.
+To draw various items in different fonts or colors,
+the application should associate different
+.B Environ
+objects with the various.
+(But see also the
+.B layout
+functions in
+.IR prefab-element (2)
+and
+.IR prefab-compound (2)).
+.SH SOURCE
+.B /libinterp/prefab.c
+.B /libprefab/*.c
+.SH SEE ALSO
+.IR prefab-intro (2),
+.IR draw-screen (2),
+.IR prefab-style (2)
--- /dev/null
+++ b/man/2/prefab-style
@@ -1,0 +1,95 @@
+.TH PREFAB-STYLE 2
+.SH NAME
+prefab: Style \- fonts and colours for ITV toolkit
+.SH SYNOPSIS
+.EX
+include "draw.m";
+include "prefab.m";
+prefab := load Prefab Prefab->PATH;
+
+Style: adt
+{
+    titlefont:      ref Draw->Font;
+    textfont:       ref Draw->Font;
+    elemcolor:      ref Draw->Image;
+    edgecolor:      ref Draw->Image;
+    titlecolor:     ref Draw->Image;
+    textcolor:      ref Draw->Image;
+    highlightcolor: ref Draw->Image;
+};
+.EE
+.SH DESCRIPTION
+The
+.B Style
+type collects the font and colour information for an application or a set of
+items within an application.  Except when using the
+.B layout
+routines (see
+.IR prefab-compound (2)
+and
+.IR prefab-element (2)),
+the members of a
+.B Style
+are the only way to control the appearance of
+.B Prefab
+items.
+Note that although the
+.RB `... color'
+members of
+.B Style
+in practice often refer to a literal colour (a single replicated pixel of colour),
+they can be any image.
+.PP
+.B Styles
+are allocated by regular Limbo definitions; there is no allocation function.
+All the members of a
+.B Style
+must be defined.
+Although it will not cause errors to modify the members of a
+.B Style
+after it has been created and passed to a
+.B Prefab
+routine, the results may be unpredictable.
+.PP
+.TP 10
+.B edgecolor
+This
+.B Image
+specifies how to draw the edges, or borders, of compounds.
+.TP
+.B elemcolor
+This
+.B Image
+specifies how to draw the base, or background, of elements and compounds.
+.TP
+.B highlightcolor
+This
+.B Image
+specifies the colour to use to highlight a selected element.
+.TP
+.B textcolor
+This
+.B Image
+specifies the colour in which to draw an item's regular text.
+.TP
+.B textfont
+This
+.B Font
+specifies the font in which to draw an item's regular text.
+.TP
+.B titlecolor
+This
+.B Image
+specifies the colour in which to draw an item's title text.
+.TP
+.B titlefont
+This
+.B Font
+specifies the font in which to draw an item's title text.
+.SH SOURCE
+.B /libinterp/prefab.c
+.br
+.B /libprefab/*.c
+.SH SEE ALSO
+.IR prefab-intro (2),
+.IR prefab-environ (2)
--- /dev/null
+++ b/man/2/print
@@ -1,0 +1,341 @@
+.TH PRINT 2
+.SH NAME
+Print \- printing system
+.SH SYNOPSIS
+.EX
+include "print.m";
+print := load Print Print->PATH;
+
+Print: module
+{
+	PATH: con "/dis/lib/print/print.dis";
+	CONFIG_PATH: con "/lib/print/";
+
+	init: fn(): int;
+	set_printfd: fn(fd: ref Sys->FD);
+	print_image: fn(p: ref Printer, display: ref Draw->Display, im: ref Draw->Image, pcwidth: int): int;
+	print_textfd: fn(p: ref Printer, fd: ref Sys->FD, ps: real, pr: int, wrap: int): int;
+	get_defprinter: fn(): ref Printer;
+	set_defprinter: fn(p: ref Printer);
+	get_size: fn(p: ref Printer): (int, real, real);	# dpi, xinches, yinches
+	get_printers: fn(): list of ref Printer;
+	get_papers: fn(): list of ref Paper;
+	save_settings: fn(): int;
+
+# Printer types
+
+Ptype: adt {
+	name: string;
+	modes: list of ref Pmode;
+	driver: string;
+	hpmapfile: string;
+};
+
+# Paper sizes
+
+Paper: adt {
+	name: string;
+	hpcode: string;
+	width_inches: real;
+	height_inches: real;
+};
+
+
+
+# Print modes
+
+Pmode: adt {
+	name: string;
+	desc: string;
+	resx: int;
+	resy: int;
+	blackdepth: int;
+	coldepth: int;
+	blackresmult: int;
+};
+
+# Print options
+
+Popt: adt {
+	name: string;
+	mode: ref Pmode;
+	paper: ref Paper;
+	orientation: int;
+	duplex: int;
+};
+
+# Printer instance
+
+PORTRAIT: con 0;
+LANDSCAPE: con 1;
+
+DUPLEX_OFF: con 0;
+DUPLEX_LONG: con 1;
+DUPLEX_SHORT: con 2;
+
+Printer: adt {
+	name: string;
+	ptype: ref Ptype;
+	device: string;
+	popt: ref Popt;
+	pdriver: Pdriver;
+};
+
+};
+
+
+Pdriver: module
+{
+	PATHPREFIX: con "/dis/lib/print/";
+	DATAPREFIX: con "/lib/print/";
+
+	init: fn(debug: int);
+	sendimage: fn(p: ref Print->Printer, tfd: ref Sys->FD, display: ref Draw->Display, im: ref Draw->Image, width: int, lmargin: int): int;
+	sendtextfd: fn(p: ref Print->Printer, pfd, tfd: ref Sys->FD, ps: real, pr: int, wrap: int): int;
+	printable_pixels: fn(p: ref Print->Printer): (int, int);
+
+};
+
+
+.EE
+.SH DESCRIPTION
+The
+.I Print
+module provides an interface to one or more printers.
+.SS "The Print module"
+.TP
+.BI init()
+.B Init
+must be called once to initialise the internal state of
+.BR Print .
+.TP
+.BI set_printfd( fd )
+.B set_printfd
+provides a file descriptor to be used for output. By default, output is sent to the file or device
+specified in the
+.B Printer
+adt.
+.TP
+.BI print_image( p, display, im, pcwidth )
+.B print_image
+prints a page containing a single image. The image is centred horizontally, and is scaled up to fill the percentage of the available
+width specified by 
+.I pcwidth.
+.TP
+.BI print_textfd( p, fd, ps, pr, wrap )
+.B print_textfd
+prints one or more pages of text on printer 
+.I p
+from the file open for reading on 
+.I fd.
+The point size is controlled by 
+.I p.
+If the printer does not support the specified point size an alternative size will be used.
+A point size of 0.0 can be used to select the printer's default size (usually 12 point).
+If 
+.I pr is non-zero, a proportionally-spaced font will be used (if available).
+If
+.I wrap
+is non-zero, lines will be wrapped if they overflow the page width (if this feature is supported by the printer).
+.TP
+.BI get_defprinter(  )
+.B get_defprinter
+returns the default printer (in
+.I /lib/print/defprinter).
+.TP
+.BI set_defprinter( p  )
+.B set_defprinter
+sets the default printer (in
+.IR /lib/print/defprinter ).
+.TP
+.BI get_size(  p  )
+.B get_size
+returns the resolution in dots per inch and the total number of pixels available for printing in the x and y direction.
+Before calling this function the 
+.I orientation
+should be set if required.
+.TP
+.BI get_printers(  )
+.B get_printers
+returns a list of all available printers (in 
+.I /lib/print/printer.cfg).
+.TP
+.BI get_papers(  )
+.B get_papers
+returns a list of all available paper sizes (in 
+.I /lib/print/paper.cfg).
+.SS "Data Structures"
+.TP
+.BI Ptype
+ - specifies a Printer Type, with fields:
+.RS
+.PD
+.TP
+.B name:
+Name used to refer to this printer type
+.TP
+.B desc
+Description
+.TP
+.B modes
+List of supported print modes
+.TP
+.B driver:
+The .dis file specification of the printer driver
+.TP
+.B hpmapfile:
+For HP printers, the name of the color map file
+.RE
+.TP
+.BI Paper
+ - specifies the dimensions of a type of paper
+.RS
+.PD
+.TP
+.B name:
+Name used to refer to this paper size
+.TP
+.B hpcode:
+For HP printers, PCL code used for this paper size
+.TP
+.B width_inches:
+Width of paper in inches
+.TP
+.B height_inches:
+Height of paper in inches
+.RE
+.TP
+.BI Pmode
+ - specifies a print mode
+.RS
+.PD
+.TP
+.B name:
+Name used to refer to this print mode
+.TP
+.B desc:
+Description
+.TP
+.B resx:
+X resolution in dots per inch
+.TP
+.B resx:
+Y resolution in dots per inch
+.TP
+.B blackdepth:
+Depth of black component in bytes
+.TP
+.B coldepth:
+Depth of color components in bytes
+.TP
+.B blackresmult:
+Resolution multiplier of black component (1 means same as base x resolution)
+.RE
+.TP
+.BI Popt
+ - specifies a set of Print Options
+.RS
+.PD
+.TP
+.B name:
+Name of a Printer to which these print options apply
+.TP
+.B mode:
+The name of a print mode
+.TP
+.B paper:
+The name of a paper size
+.TP
+.B orientation:
+Paper orientation - either PORTRAIT or LANDSCAPE
+.TP
+.B duplex (NB DUPLEX IS NOT CURRENTLY SUPPORTED):
+Duplex setting - DUPLEX_OFF or DUPLEX_SHORT or DUPLEX_LONG 
+.RE
+.TP
+.BI Printer
+ - specifies a printer instance
+.RS
+.PD
+.TP
+.B name:
+Name used to refer to this printer
+.TP
+.B ptype:
+The printer type
+.TP
+.B device:
+The name of the file to be used for output (eg /dev/lpt1data)
+.TP
+.B popt:
+The print options
+.TP
+.B driver:
+The printer driver module handle
+.RE
+.SS "Configuration Files"
+.PP
+There are configuration files to initialize the Printer Types, Print Modes, Paper Sizes Printers and Print Modes.
+They all have a similar format, with fields corresponding to the relevant adt.
+Here is an extract from the 
+.I paper.cfg
+file:
+.PP
+.EX
+A4=
+	hpcode=26
+	width_inches=8.3
+	height_inches=11.7
+A5=
+	hpcode=25
+	width_inches=4.15
+	height_inches=5.85
+.EE
+.PP
+Aliases can also be defined, such as
+.PP
+.EX
+myA4=A4
+.EE
+.PP
+The final configuration file, 
+.B defprinter,
+just contains the name of the default printer.
+.SH FILES
+.TP
+.B /lib/print/*.map
+HP color maps.
+.TP
+.BI /lib/print/ptype.cfg
+Print Type configuration file.
+.TP
+.BI /lib/print/pmode.cfg
+Print Mode configuration file.
+.TP
+.BI /lib/print/paper.cfg
+Paper Size configuration file.
+.TP
+.BI /lib/print/printer.cfg
+Printer Instance configuration file.
+.TP
+.BI /lib/print/popts.cfg
+Print options configuration file.
+.TP
+.BI /lib/print/defprinter
+Holds the name of the default printer. Not needed if there is only one printer available.
+.SH SOURCE
+.TF /appl/lib/print/print.b
+.TP
+.B /appl/lib/print/print.b
+Implementation of the
+.B Print
+module.
+.TP
+.B /appl/lib/print/*_driver.b
+Printer-specific driver modules
+.TP
+.B /appl/lib/print/scaler.b
+Scaler module
+.SH SEE ALSO
+.IR draw-image (2),
+.IR image (6)
--- /dev/null
+++ b/man/2/prof
@@ -1,0 +1,225 @@
+.TH PROFILE 2
+.SH NAME
+profile \- profiling library
+.SH SYNOPSIS
+.EX
+include "profile.m";
+
+profile := load Profile Profile->PATH;
+
+Range: adt{
+    l: int;
+    u: int;
+    f: int;
+    n: cyclic ref Range;
+};
+
+Funprof: adt{
+    name: string;
+    line: int;
+    count: int;
+    counte: int;
+};
+
+Modprof: adt{
+    name: string;
+    path: string;
+    srcpath: string;
+    rawtab: array of (int, int);
+    linetab: array of int;
+    rngtab: array of ref Range;
+    funtab: array of Funprof;
+    total: int;
+    totals: array of int;
+    covered: int;
+};
+
+Prof: adt{
+    mods: list of Modprof;
+    total: int;
+    totals: array of int;
+};
+
+Coverage: type list of (string, int, list of (list of (int, int, byte), string));
+
+MODULE: con 1;	# give stats for each module
+FUNCTION: con 2;	# give stats for each function
+LINE: con 4;	# give stats for each line
+VERBOSE: con 8;	# full stats
+FULLHDR: con 16;	# file:lineno: on each line of output
+FREQUENCY: con 32;	# show frequency rather than coverage
+
+init: fn(): int;
+profile: fn(m: string): int;
+sample: fn(i: int): int;
+start: fn(): int;
+stop: fn(): int;
+stats: fn() : Prof;
+show: fn(p: Prof, v: int): int;
+end: fn(): int;
+
+# coverage profiling specific functions
+
+cpstart: fn(pid: int): int;
+cpstats: fn(rec: int, v: int): Prof;
+cpfstats: fn(v: int): Prof;
+cpshow: fn(p: Prof, v: int): int;
+
+coverage: fn(p: Prof, v: int): Coverage;
+
+# memory profiling specific functions
+
+memstart: fn(): int;
+memstats: fn(): Prof;
+memshow: fn(p: Prof, v: int): int;
+
+lasterror: fn(): string;
+.EE
+.SH DESCRIPTION
+.B Profile
+provides an interface to the kernel profile device. It contains routines to start and stop
+profiling, to gather profiling statistics and to display these statistics in relation to the
+relevant limbo source code. All of these routines apart from
+.B lasterror
+return a negative integer if an error occurred during their execution. Once this
+happens, a call to
+.B lasterror
+will give the error message.
+.PP
+.B init
+initializes the module and binds the kernel profile device onto the /prof directory.
+It should be called before any other functions in this module.
+.PP
+.B profile
+takes a module name or a path name as its argument and indicates a module to be profiled.
+Repeated calls of this function allow a number of modules to be profiled together.
+In the absence of any calls to this routine, the profile device profiles all modules
+loaded by the kernel.
+.PP
+.B sample
+sets the sampling interval of the profiling. The argument is in ms. The default value
+in the profile device is 100ms.
+.PP
+.B start
+starts profiling.
+.PP
+.B stop
+stops profiling.
+.PP
+.B stats
+returns profiling statistics. It is recommended that this is called once profiling has
+been stopped, otherwise the results may be slightly inaccurate. It returns an adt
+of type 
+.B Prof.
+Its first field
+.B mods
+is a list of profile statistics for each module under profile.
+Its second field 
+.B total 
+is the number of samples made altogether. The statistics for each module are
+represented in the
+.B Modprof
+adt. Its fields are
+.TP 10n
+.B name
+The name of the module.
+.TP 10n
+.B path
+The path of the module's corresponding dis file.
+.TP 10n
+.B srcpath
+The path of the module's corresponding source file.
+.TP 10n
+.B linetab
+The line frequency count table. The number of samples made on a particular line of
+the above source file is found by indexing this table.
+.TP 10n
+.B funtab
+The function frequency count table. This array of
+.B Funprof
+adt gives the name, line number and number of samples made for each function in
+the above source file.
+.TP 10n
+.B total
+The total number of samples made in this module.
+.PP
+.B stats
+will ignore modules if it fails to locate the symbol (.sbl) file for a particular dis file.
+.PP
+.B show
+simply prints out the profile information returned by
+.B stats.
+For each module it will print out either the line number statistics or the function
+statistics or both. The former gives the line number, percentage of time spent on
+this line and the source code for each line in the source file. The latter gives the
+line number, function name and percentage of time spent in this function for each
+function in the source file. The amount of output can be controlled by the second
+argument to
+.B show.
+The following should be ored together in the required combination to get the
+desired effect.
+.PP
+.TP 10n
+.B FUNCTION
+Print the function table.
+.TP 10n
+.B LINE
+Print the line number table.
+.TP 10n
+.B VERBOSE
+Normally lines and functions are not shown if the sample frequency for them is
+zero. This option prevents this.
+.TP 10n
+.B FULLHDR
+Prints the file name as well as the line number in a form that can be selected in
+.B acme
+to show that particular line in a window.
+.PP
+.B show
+will ignore modules if it fails to locate the source (.b) file for a particular dis file.
+.PP
+.B end
+ends the profiling session. This should be called when all statistics have been 
+gathered in order to clean up the device profile's data structures.
+.PP
+In order to support coverage profiling as well the following new routines are
+provided.
+.PP
+.B cpstart
+Start coverage profiling on the process whose pid is given.
+.PP
+.B cpstats
+Returns coverage profiling statistics from the profile device. If the first argument is
+true, the raw results will be stored in a profile file whose name is <file>.prf where
+<file> is the prefix of the corresponding dis file.
+.PP
+.B cpfstats
+Returns coverage profiling statistics from any saved profile files.
+.PP
+.B cpshow
+Shows the coverage profiling statistics.
+.PP
+.B coverage
+Given the coverage profiler statistics this returns the list of modules profiled. Each module
+has a name, a boolean indicating whether all instructions in the module were executed and a
+list of lines. Each line consists of a list of ranges and the source code.
+The list of ranges contains the character positions on the line of code corresponding to instructions that were not executed and an indicator of whether
+partial execution was performed.
+.PP
+For the further support of memory profiling the following routines are available.
+.PP
+.B memstart
+Start memory profiling.
+.PP
+.B memstats
+Return the memory profile statisics.
+.PP
+.B memshow
+Send memory profile statistics to standard output.
+.PP
+.SH SOURCE
+.B /module/profile.m
+.br
+.B /appl/lib/profile.b
+.SH SEE ALSO
+.IR prof (3)
--- /dev/null
+++ b/man/2/pslib
@@ -1,0 +1,46 @@
+.TH PSLIB 2
+.SH NAME
+pslib - postscript generation
+.SH SYNOPSIS
+.EX
+include "pslib.m";
+pslib := load Pslib Pslib->PATH;
+
+init: fn(bufio: Bufio);
+writeimage: fn(f: ref Bufio->Iobuf,
+			img: ref Draw->Image, dpi: int): string;
+.EE
+.SH DESCRIPTION
+.B Pslib
+must first be initialised by calling
+.B Init
+with a loaded Bufio module.
+.B Writeimage
+writes a Postscript file containing the data within
+.I img
+to
+.IR f ,
+which should first have been opened for writing
+by
+.IR bufio .
+.I Dpi
+is a value specifying the pixel width of pixels in
+.IR img ;
+the width (and height) of
+.I dpi
+dots in
+.I img
+will be one inch when the Postscript is
+rendered.
+.SH SOURCE
+.B /appl/lib/pslib.b
+.SH SEE ALSO
+.IR bufio (2),
+.IR draw-image (2)
+.SH BUGS
+The resulting Postscript is really only suitable for
+use as encapsulated Postscript, as there's no way
+to set the destination paper size.
+.PP
+There should be many more useful functions
+in this module.
--- /dev/null
+++ b/man/2/rabin
@@ -1,0 +1,54 @@
+.TH RABIN 2
+.SH NAME
+rabin \- rabin fingerprinting
+.SH SYNOPSIS
+.EX
+include "rabin.m";
+rabin := load Rabin Rabin->PATH;
+Rcfg, Rfile: import rabin;
+
+init:	fn(bufio: Bufio);
+open:	fn(rcfg: ref Rcfg, b: ref Iobuf, min, max: int): (ref Rfile, string);
+
+Rcfg: adt {
+	mk:	fn(prime, width, mod: int): (ref Rcfg, string);
+};
+
+Rfile: adt {
+	read:	fn(r: self ref Rfile): (array of byte, big, string);
+};
+.EE
+.SH DESCRIPTION
+.B Rabin
+implements a data fingerprinting algorithm.  A rolling checksum is calculated while reading data.  Certain checksum values are taken to be data boundaries and used to split the data into chunks.
+.PP
+.B Rcfg
+represents the parameters to the algorithm;
+.B Rcfg.mk
+creates a new instance.
+.I Prime
+should be a prime number.
+.I Width
+is the width of the rolling checksum window in bytes.  A wider window results in more diverse boundary patterns.  A window of 30 bytes should be reasonable for most uses.
+.I Mod
+effectively sets the mean desired chunk size.  The rolling checksum is calculated modulo
+.IR mod .
+All three parameters influence where chunk boundaries will be found.
+.PP
+.B Rfile
+represents a file to read chunks from.
+.B Open 
+returns an initialised Rfile or an error string.
+.I Min
+and
+.I max
+are the minimum and maximum size in bytes of chunks that will be returned.  Only the last chunk in a file can be smaller than the minimum chunk size.  Note that the mean chunk size may be off due to these parameters.
+Data is read from
+.B Iobuf
+.IR b .
+.B Rfile.read
+returns subsequent chunks of data and the file offset at which they were found, or an error message.  After end of file, the returned chunks are zero bytes long.
+.SH SOURCE
+.B /appl/lib/rabin.b
+.SH AUTHOR
+Mechiel Lukkien, during GSoC 2007
--- /dev/null
+++ b/man/2/rand
@@ -1,0 +1,35 @@
+.TH RAND 2
+.SH NAME
+rand \- pseudo random number generation
+.SH SYNOPSIS
+.EX
+include "rand.m";
+rand = load Rand Rand->PATH;
+
+init:    fn(seed: int);
+rand:    fn(modulus: int): int;
+bigrand: fn(modulus: big): big;
+.EE
+.SH DESCRIPTION
+.B Init
+initialises the pseudo-random number generator
+with
+.IR seed ;
+subsequent calls to 
+.B rand
+and
+.B bigrand
+return a pseudo-random sequence of integers
+or bigs respectively, between 0 and
+.IR modulus \-1.
+.I Modulus
+should be a non-negative integer;
+for
+.BR bigrand ,
+it should be less than 2^53.
+.SH SOURCE
+.B /appl/lib/rand.b
+.SH SEE ALSO
+.IR security-random (2)
+.SH BUGS
+The quality of the algorithm currently used is questionable.
--- /dev/null
+++ b/man/2/readdir
@@ -1,0 +1,101 @@
+.TH READDIR 2
+.SH NAME
+readdir \- read directory and sort files
+.SH SYNOPSIS
+.EX
+include "readdir.m";
+readdir := load Readdir Readdir->PATH;
+
+NAME, ATIME, MTIME, SIZE, NONE: con iota;
+COMPACT:    con (1<<4);
+DESCENDING: con (1<<5);
+init:    fn(path: string, sortkey: int): (array of ref Sys->Dir, int);
+readall: fn(fd: ref Sys->FD, sortkey: int): (array of ref Sys->Dir, int);
+sortdir: fn(a: array of ref Sys->Dir, key: int): (array of ref Sys->Dir, int);
+.EE
+.SH DESCRIPTION
+.B Readdir
+provides functions to read and sort the contents of a directory.
+Each function
+returns its result as a tuple that represents the
+directory contents as an array of references to
+.B Sys->Dir
+values, one per file
+(see
+.IR sys-stat (2)
+for a description of
+.BR Dir ).
+The integer element of the tuple is the number of entries
+returned, or \-1 if there was an error reading the directory.
+.B Readdir
+differs from
+.I sys-dirread (2)
+in returning the contents of the whole directory, not just a chunk of it,
+and in allowing the result to be sorted.
+.PP
+.B Init
+is most often used: it
+reads the contents of the directory
+.I path
+and sorts the resulting array according to
+.IR sortkey .
+.PP
+The sorting criteria for the returned array are based on
+.I sortkey
+as follows:
+.PP
+.TF MTIME
+.PD
+.TP
+.B NAME
+Sort files alphabetically by name.
+.TP
+.B ATIME
+Sort files by access time, most recently accessed first.
+.TP
+.B MTIME
+Sort files by modification time, most recently modified first.
+.TP
+.B SIZE
+Sort files by size, largest file first.
+.TP
+.B NONE
+Files are left in directory order, unsorted.
+.PP
+If the value
+.B DESCENDING
+is or'd into any of the values above, except
+.BR NONE ,
+the order of sorting is reversed.
+.PP
+The sort used is stable, of particular importance in the presence of
+duplicate names in a union mount.
+If the value
+.B COMPACT
+is or'd into any of the values above, including
+.BR NONE ,
+only the first (outermost) entry with a given name will be returned from reading
+a union mount, if names are duplicated in the union.
+.PP
+.B Readall
+reads file descriptor
+.I fd
+which must be open on a directory,
+and returns the contents after applying
+.I sortkey
+as described above for
+.BR init .
+.PP
+.B Sortdir
+sorts the array
+.I a
+according to the given
+.IR key ,
+as defined earlier, except that
+the
+.B COMPACT
+option has no effect.
+.SH SOURCE
+.B /appl/lib/readdir.b
+.SH SEE ALSO
+.IR sys-dirread (2)
--- /dev/null
+++ b/man/2/regex
@@ -1,0 +1,108 @@
+.TH REGEX 2
+.SH NAME
+regex \- regular expression recognizer module
+.SH SYNOPSIS
+.EX
+include "regex.m";
+regex := load Regex Regex->PATH;
+
+compile:   fn(e: string, flag: int): (Re, string);
+execute:   fn(x: Re; s: string): array of (int,int);
+executese: fn(x: Re, s: string, se: (int, int), bol: int, eol: int):
+			array of (int, int);
+.EE
+.SH DESCRIPTION
+Regular expressions are defined by
+.IR regexp (6).
+.B Compile
+returns (as the first element of the result tuple)
+a compiled form of the regular expression given in string 
+.IR e .
+If
+.I e
+is not a valid regular expression,
+.B compile
+returns the tuple
+.BI "(nil" ", diag" )
+where
+.I diag
+is a diagnostic string.
+The effect of 
+.I flag
+is described below.
+.PP
+.B Execute
+matches the compiled regular expression
+.I x
+against string
+.I s.
+It returns
+.B nil
+on no match, otherwise it returns an array.
+The element with index 0 gives the character positions
+of the first character of some longest leftmost match and
+the first character beyond the match.
+If the compilation
+.I flag
+was 0, there are no more elements.
+If
+.I flag
+was 1, there is one element for each pair of
+parentheses in the regular expression, counting
+left parentheses left to right starting at 1.
+The
+.IR n th
+element gives the position of the last match to the 
+.IR n th
+parenthesized subexpression, or (\-1,\-1) if the subexpression
+does not participate in the overall match.
+.PP
+.B Executese
+is similar to
+.B execute
+but allows the start and end points in the string to be specified,
+as tuple
+.IR se :
+.BI ( "start , end" ) ,
+where
+.I start
+is the index in
+.I s
+of the initial character to be matched,
+and
+.I end
+is the index in
+.I s
+of the first character beyond the substring to be matched
+(and can be the length of
+.IR s ).
+If
+.I bol
+is non-zero, the
+initial character is at the beginning of a line,
+allowing an initial match by the regular expression operator
+.RB ` ^ ';
+if
+.I eol
+is non-zero, the
+last character is at the end of a line, allowing a match
+by the operator `\f5$\fP'.
+.SH EXAMPLES
+.EX
+(re, nil) := regex->compile("(thick )*(chocolate |raspberry )?"+
+			"(topp|fill)ing", 0);
+
+(re, nil) := regex->compile("[ABCb-z]+", 0);
+a := regex->execute(re, s:="aAbBcCdD");
+(beg, end) := a[0];
+s[beg:end] == "AbBcCd";
+
+(re, nil) := regex->compile("a*b*", 0);
+a := regex->execute(re, "bbaabb");
+(beg, end) := a[0];
+(beg, end) == (0,2);
+.EE
+.SH SOURCE
+.B /appl/lib/regex.b
+.SH SEE ALSO
+.IR regexp (6)
--- /dev/null
+++ b/man/2/registries
@@ -1,0 +1,318 @@
+.TH REGISTRIES 2
+.SH NAME
+registries \- access services registry
+.SH SYNOPSIS
+.EX
+include "registries.m";
+registries := load Registries Registries->PATH;
+
+init: fn();
+
+Attributes: adt {
+   attrs: list of (string, string);
+
+   get:   fn(a: self ref Attributes, attr: string): string;
+   set:   fn(a: self ref Attributes, attr, val: string);
+   new:   fn(attrs: list of (string, string)): ref Attributes;
+};
+
+Attached: adt {
+   fd:           ref Sys->FD;  # connection to the service
+   signerpkhash: string;     # hash of signer's key
+   localuser:    string;     # user name here
+   remoteuser:   string;     # user name there
+};
+
+Service: adt {
+   addr:    string;          # dial this to connect to the service
+   attrs:   ref Attributes;  # service description
+
+   attach:  fn(s: self ref Service, user: string,
+               keydir: string): ref Attached;
+};
+
+Registered: adt {
+   addr:    string;          # address where registered
+   reg:     ref Registry;    # registry where registered
+   fd:      ref Sys->FD;     # file representing registration entry
+};
+
+Registry: adt {
+   dir:     string;
+
+   new:        fn(dir: string): ref Registry;
+   connect:    fn(svc: ref Service, user: string, keydir: string):
+                  ref Registry;
+   services:   fn(r: self ref Registry):
+                  (list of ref Service, string);
+   find:       fn(r: self ref Registry, a: list of (string, string)):
+                  (list of ref Service, string);
+   register:   fn(r: self ref Registry, addr: string,
+                  attrs: ref Attributes, persist: int):
+                  (ref Registered, string);
+   unregister: fn(r: self ref Registry, addr: string): string;
+};
+.EE
+.SH DESCRIPTION
+.B Registries
+helps access and update the contents of one or more
+.I registry (4)
+servers.
+Each registry lists services, each described by a set of attribute/value pairs.
+The attributes need not identify the service uniquely.
+.PP
+.B Init
+must be called before any other function in the module.
+.PP
+.B Attributes
+represents a set of attribute/value pairs;
+a given attribute name cannot appear more than once.
+It provides the following members and operations:
+.TF attrs
+.PD
+.TP
+.B attrs
+The current value of the set, as a list of
+.BI ( attr , value )
+tuples.
+.TP
+.BI Attributes.new( attrs )
+Return a new
+.B Attributes
+value given
+.IR attrs ,
+a list of
+.BI ( attr , value )
+tuples.
+.TP
+.IB a .get( attr )
+Return the value of
+.I attr
+in attribute set
+.IR a .
+.TP
+.IB a .set( attr\fB,\fP\ value )
+Set the
+.I value
+of
+.I attr
+in attribute set
+.IR a .
+.PP
+A
+.B Service
+value represents a service registered in some registry.
+It has the following members and operations:
+.TF attrs
+.PD
+.TP
+.B addr
+A string that represents where the service can be reached: it is often a
+.IR dial (2)
+address string, but might be a name in the name space; the interpretation is internal to
+.BR Registries .
+.TP
+.B attrs
+The service's attributes (complete service description).
+.TP
+.IB svc .attach( user , keydir )
+Attempts to attach to the service
+.IR svc ,
+and if successful returns an
+.B Attached
+value to represent the connection;
+otherwise returns nil and sets the system error string.
+.I User
+is the local name of the user making the connection; if nil, the contents of
+.B /dev/user
+are used by default.
+.I Keydir
+is the name of a directory containing the
+.IR user 's
+certified keys for authentication; if nil, the default is
+.BI /usr/ user /keyring.
+If an error occurs, the return value is nil and the system error string contains a diagnostic.
+Not all services demand authentication, and either or both values can be nil to
+select suitable defaults.
+If authentication is done, the certified data determines the remote identity, not
+.IR user ,
+which is used only to help find a suitable set of keys and has no effect on security.
+.PP
+.B Attached
+holds data about an active connection to a particular service.
+(There can be several distinct connections to the same
+.BR Service .)
+It provides the following members:
+.TF attrs
+.PD
+.TP
+.B fd
+A file descriptor that can be read and written to exchange data with the service.
+The meaning of the data depends on the service (eg, it might be a 9P connection
+or a SOAP implementation).
+Typically an attribute of the service description hints at the protocol.
+.TP
+.B signerpkhash
+A hexadecimal string giving the SHA-1 hash of the key of the signer used for
+mutual authentication (ie, its thumbprint); if no authentication was needed or
+did not use public key methods, it is nil.
+.TP
+.B localuser
+If authentication was required, the name used as the local name for authentication;
+otherwise nil.
+.TP
+.B remoteuser
+If authentication was required, the remote identity for this connection; otherwise nil.
+.PP
+A service provider registers using one of the
+.B Registry
+functions listed below.
+An active registration is represented by a value of type
+.BR Registered :
+.TF attrs
+.PD
+.TP
+.B addr
+Location of service, as registered.
+.TP
+.B reg
+The registry that registered the service.
+.TP
+.B fd
+File descriptor open for on the service file in
+.IR registry (4)
+that holds the service description.
+Unless the service has a non-zero
+.B persist
+attribute, the service registration will be removed when this file is closed
+(eg, when this
+.B Registration
+value is freed by the garbage collector if there is no other copy of
+.BR fd ).
+The file can be written as described in
+.IR registry (4)
+to change the some or all of current set of attributes and/or values
+in the service description.
+.PP
+A
+.B Registry
+represents a connection to a single
+.IR registry (4)
+instance.
+It provides the following members and operations:
+.TF attrs
+.PD
+.TP
+.B dir
+Location of the registry in the name space.
+.TP
+.BI Registry.new( dir )
+Return a new
+.B Registry
+value for the registry located at
+.I dir
+in the name space; if
+.I dir
+is nil, the default is
+.BR /mnt/registry .
+.TP
+.BI Registry.connect( svc\fB,\fP\ user\fB,\fP\ keydir )
+Connect to the registry at the location determined by the service description
+.IR svc ;
+if
+.I svc
+is nil, the default is to try
+.IR dial (2)
+to
+.BR net!$registry!registry ,
+the symbolic name of the default registry for the current host.
+(The registry might or might not be local.)
+Attributes of
+.I svc
+determine the method used to connect to the registry and whether authentication is required.
+.I User
+and
+.I keydir
+provide user name and certificate directory (see
+.IR getauthinfo (8))
+if authentication is required; either or both can be nil to select the defaults for the given authentication method.
+On successful connection,
+.B connect
+returns a
+.B Registry
+value that can subsequently be used to access that registry and its services.
+It returns nil on an error and sets the system error string.
+.TP
+.IB reg ".services()"
+Returns a tuple
+.BI ( svcs , err )
+where
+.I svcs
+is a list of
+.B Service
+values, one for each service registered by
+.I reg
+at time of asking.
+If no services are registered, both values are nil.
+If an error occurred,
+.I svcs
+is nil but
+.I err
+is a diagnostic string.
+.TP
+.IB reg .find( attrs )
+Return a tuple
+.BI ( svcs , err )
+where
+.I svcs
+is a list of
+.B Service
+values, one for each service registered by
+.I reg
+that (at time of asking) matched all of the attribute/value pairs in
+.IR attrs .
+A value of
+.B \&"*"
+matches any value.
+If no services matched, both values are nil.
+If an error occurred,
+.I svcs
+is nil but
+.I err
+is a diagnostic string.
+.TP
+.IB reg .register( addr\fB,\fP\ attrs\fB,\fP\ persist )
+Attempts to register with
+.I reg
+a service named
+.I addr
+and the given attributes
+.IR attrs ,
+and returns a tuple
+.BI ( r , err )
+where
+.I r
+is a non-nil
+.B Registration
+value for the entry if successful, and
+.I err
+is a diagnostic otherwise.
+.I Persist
+is non-zero if the service registration should survive the
+.B Registration
+value (connection); normally it is zero and the registry entry vanishes when
+the service frees the
+.B Registration
+value.
+.TP
+.IB reg .unregister( addr )
+Attempt to remove the registration entry at
+.I reg
+for the service named
+.IR addr .
+Only the service owner can do so.
+Returns nil on success and a diagnostic otherwise.
+.SH SEE ALSO
+.IR attrdb (2),
+.IR registry (4),
+.IR svc (8)
--- /dev/null
+++ b/man/2/rfc822
@@ -1,0 +1,327 @@
+.TH RFC822 2
+.SH NAME
+rfc822 \- RFC822 mail format helpers
+.SH SYNOPSIS
+.EX
+include "bufio.m";
+include "rfc822.m";
+rfc822 := load RFC822 RFC822->PATH;
+Content, Rfclex: import rfc822;
+
+Word, QString: con ...;
+Maxrequest: con 16*1024;   # more than enough for anything sensible
+
+init:         fn(b: Bufio);
+
+Rfclex: adt {
+   tok:     int;
+   wordval: string;
+
+   mk:      fn(a: array of byte): ref Rfclex;
+   getc:    fn(p: self ref Rfclex): int;
+   ungetc:  fn(p: self ref Rfclex);
+   lex:     fn(p: self ref Rfclex): int;
+   unlex:   fn(p: self ref Rfclex);
+   skipws:  fn(p: self ref Rfclex): int;
+
+   line:    fn(p: self ref Rfclex): string;
+};
+
+readheaders:  fn(fd: ref Bufio->Iobuf, limit: int):
+                  array of (string, array of byte);
+parseparams:  fn(ps: ref Rfclex): list of (string, string);
+parsecontent: fn(ps: ref Rfclex, multipart: int,
+                  head: list of ref Content): list of ref Content;
+mimefields:   fn(ps: ref Rfclex):
+                  list of (string, list of (string, string));
+
+quotable:     fn(s: string): int;
+quote:        fn(s: string): string;
+
+sec2date:     fn(secs: int): string;
+date2sec:     fn(s: string): int;
+now:          fn(): int;
+time:         fn(): string;
+
+Content: adt{
+   generic:   string;
+   specific:  string;
+   params:    list of (string, string);
+
+   mk:        fn(specific: string, generic: string,
+                 params: list of (string, string)): ref Content;
+   check:     fn(c: self ref Content, oks: list of ref Content): int;
+   text:      fn(c: self ref Content): string;
+};
+
+suffixclass:  fn(name: string): (ref Content, ref Content);
+dataclass:    fn(a: array of byte): (ref Content, ref Content);
+.EE
+.SH DESCRIPTION
+.B RFC822
+provides types and functions to help read and parse RFC822 e-mail headers
+(following the more streamlined rules of RFC2822, which has replaced RFC822),
+including some MIME extensions.
+Currently the focus is on operations that support HTTP and other W3C protocols,
+rather than mail reading.
+.PP
+.B Init
+must be called before any other operation in the module.
+It must be given an instance of the
+.B Bufio
+module (see
+.IR bufio (2)).
+.PP
+.B Readheaders
+reads a set of RFC822 header lines from
+.IR fd ,
+ended by an empty line.
+It returns an array of tuples
+.BI ( fieldname,\ value ),
+one per header line.
+The string
+.I fieldname
+is the header line's field name, in lower case.
+The
+.I value
+gives the rest of the line, after removing any initial white space and appending any continuation lines, uninterpreted,
+as an array of bytes (not a string).
+.I Limit
+is the maximum allowed size of the header in bytes;
+usually that is
+.BR Maxrequest .
+.B Readheaders
+returns the tuple
+.B (nil,\ nil)
+on end of file or if the header size limit is exceeded.
+.PP
+.B Rfclex
+takes a header line's
+.I value
+and produces a sequence of tokens.
+It provides the following operations:
+.TP
+.BI Rfclex.mk( a )
+Return a reference to a new
+.B Rfclex
+value that will produce tokens from the array of byte
+.IR a ,
+which is normally the
+.I value
+of one of the header lines returned by
+.BR readheaders .
+.TP
+.IB rfl .getc()
+Return the next character from the input stream, in the Latin-1 (ISO 8859-1) character set.
+Return a negative value
+.RB ( Bufio->EOF )
+on end-of-file.
+.TP
+.IB rfl .ungetc()
+Put back the last character read, which will be returned again by the next call to
+.IR rfl .getc .
+.TP
+.IB rfl .lex()
+Return the next token from the input stream, ignoring any leading white space.
+Most tokens are single characters representing themselves.
+The token value is also assigned to
+.IB rfl .tok .
+There are two special token values:
+.BR Word ,
+representing an unquoted word in the RFC2822 grammar; and
+.BR QString ,
+representing a quoted string.
+In both cases
+.IB rfl .wordval
+will contain the text of the word or string (without quotes).
+.TP
+.IB rfl .unlex()
+Put back the last token read; the next call to
+.IB rfl .lex
+will return it again.
+.TP
+.IB rfl .skipws()
+Skip over any white space at the current position;
+return the initial character of the next token (which is not consumed).
+.TP
+.IB rfl .line()
+Return a string giving the remainder of the input line.
+.PP
+Several functions take an
+.B Rfclex
+referring to a header line's value, parse it, and return a higher-level representation of its value.
+.PP
+.B Parseparams
+parses a sequence of
+.I parameter
+settings of the form (\c
+.BI ; attribute = value\c
+)*
+and returns a corresponding list of tuples
+.BI ( attribute,\ value ) .
+It returns nil if no parameters are found.
+.PP
+.B Parsecontent
+parses the values of fields such as
+.B Content-Type
+and
+.BR Accept .
+The syntax is (loosely) a sequence of comma-separated elements of the form
+.IR type ,
+.IB type /* ,
+or
+.IB type / subtype
+followed by an optional sequence of parameters of the form (\c
+. BI ; attribute = value
+)*.
+The
+.IB type  / subtype
+form is allowed only if
+.I multipart
+is true (non-zero).
+It returns a corresponding list of
+.B Content
+values followed by the initial list
+.IR head .
+.PP
+.B Mimefields
+parses a sequence of comma-separated elements of the form
+.IR word (\c
+.BI ; attr = val
+)*
+as used for instance in the rule
+.IR transfer-coding .
+It returns a corresponding list of tuples
+.BI ( word,\ l )
+where
+.I l
+is an optional list of tuples
+.BI ( attr,\ val ) .
+.PP
+When producing an RFC822 header line, words must be quoted when they contain certain
+special characters.
+.B Quotable
+returns true iff the string
+.I s
+contains any of those characters.
+.B Quote
+returns the value of
+.I s
+with quotes added as required.
+.PP
+RFC822 headers have a particular syntax for dates, different from that of
+.IR daytime (2).
+The function
+.B sec2date
+returns a string in RFC822 date format representing the time
+.I secs
+(in seconds from the Epoch).
+.B Date2sec
+takes a string in RFC822 date format and returns the time in seconds from the Epoch.
+.B Now
+returns the current time in seconds from the epoch
+(it is equivalent to
+.B Daytime->now()
+from
+.IR daytime (2)).
+.B Time
+returns the current time in RFC822's date format.
+.PP
+The Multipurpose Internet Mail Extensions (see RFC2045-7) include syntax
+for describing different types of media, content, and content encodings.
+.B Content
+values represent those descriptions.
+Its fields and operations are as follows:
+.TP
+.B generic
+General class of content (eg,
+.BR application ,
+.BR image ,
+.BR text ,
+etc)
+.TP
+.B specific
+Optional particular type within that class
+(eg,
+.B octet-stream
+within
+.BR application ,
+or
+.B plain
+within
+.BR text )
+.TP
+.B params
+Optional list of
+.BI ( attr,\ value )
+pairs giving parameters to the content type or encoding.
+The particular attribute
+.B q
+has a floating-point
+.I value
+that specifies the relative quality (utility) of a particular type or encoding to a client.
+.TP
+.BI mk( generic,\ specific,\ params )
+Return a reference to a new
+.B Content
+value, initialised with the given parameters.
+.TP
+.IB c .check( ok )
+Return true if
+.I c
+is acceptable content/media/encoding according to the list of allowable forms in
+.IR ok .
+.I C
+is always acceptable if
+.I ok
+is nil (ie, there are no restrictions).
+Otherwise, at least one of the
+.B Content
+values in
+.I ok
+must match
+.IR c .
+That is, an
+.I ok
+value must have the same generic and specific types as
+.IR c ,
+or specify
+.RB ` * '
+in one or both positions (to match any value in
+.IR c ).
+Any
+.I params
+are ignored.
+.TP
+.IB c .text()
+Return the RFC822 syntax for
+.IR c :
+.IB generic / specific ; a\f5=\fIv\fR\&...
+where the component words are quoted if necessary.
+.PP
+Given the
+.I name
+of a file,
+.B suffixclass
+returns a tuple
+.BI ( type,\ enc )
+where
+.I type
+gives the MIME
+.B Content-Type
+of
+.I name
+(or nil, if its type is not known), and
+.I enc
+gives the MIME
+.B Content-Encoding
+of
+.I name
+(or nil, if it is not encoded).
+.SH FILES
+.TF /lib/mimetype
+.TP
+/lib/mimetype
+map between file suffix and MIME content types
+.SH SOURCE
+.B /appl/lib/rfc822.b
--- /dev/null
+++ b/man/2/scsiio
@@ -1,0 +1,144 @@
+.TH SCSIIO 2
+.SH NAME
+ScsiIO: Scsi \- SCSI device operations
+.SH SYNOPSIS
+.EX
+include "scsi.m";
+
+scsiio := load ScsiIO ScsiIO->PATH;
+Scsi: adt {
+    inquire:    string;
+    rawfd:      ref Sys->FD;
+    nchange:    int;
+    changetime: int;
+
+    open:       fn(devdir: string): ref Scsi;
+    rawcmd:     fn(s: self ref Scsi, cmd: array of byte,
+                   data: array of byte, io: int): int;
+    cmd:        fn(s: self ref Scsi, cmd: array of byte,
+                   data: array of byte, io: int): int;
+    ready:      fn(s: self ref Scsi): int;
+};
+
+Sread, Swrite, Snone: con iota;
+
+scsierror:  fn(asc: int, ascq: int): string;
+
+init:   fn(verbose: int);
+.EE
+.SH DESCRIPTION
+.B ScsiIO
+provides a low-level interface to a SCSI or ATAPI device via
+.IR sd (3).
+.PP
+.B Init
+must be called before using any other operation of the module.
+If
+.I verbose
+is non-zero
+the module will produce a fair
+amount of debugging output on file descriptor 2
+when SCSI commands fail.
+.PP
+.B Scsi.open
+attempts to open the file
+.IB devdir /raw
+on which to send raw SCSI commands.
+On success, it reads the device's inquiry
+string and returns a reference to a
+.B Scsi
+value to represent the connection.
+It provides the following operations:
+.TP
+.IB s .ready()
+Sends the ``unit ready'' command up to three times,
+returning zero once the unit responds that it is ready,
+or \-1 on error.
+.TP
+.IB s .rawcmd( "cmd, data, io" )
+.PD 0
+.TP
+.IB s .cmd( "cmd, data, io" )
+Both these functions execute a single SCSI command on the named device.
+The command data is in the byte array
+.IR cmd .
+If
+.I io
+is 
+.BR Sread ,
+a successful operation will store the resulting bytes in
+.IR data ,
+up to its length,
+returning the number of bytes stored.
+If
+.I io
+is
+.BR Swrite ,
+the bytes in
+.I data
+are transmitted as the data argument to
+the command, and the
+number of bytes written is returned.
+If
+.I io
+is
+.BR Snone ,
+.I data
+is ignored and may be nil.
+.B Rawcmd
+simply issues the command and
+returns the result;
+.B cmd
+works a bit harder and
+is the more commonly used routine.
+It attempts to send the commmand;
+if it is successful, 
+.B cmd
+returns the result.
+Otherwise,
+.B cmd
+sends a request sense command to
+obtain the reason for the failure,
+sends a unit ready command in
+an attempt to bring the unit out of any
+inconsistent states, and tries again.
+It also accounts for media change.
+If the second try fails,
+.B cmd
+returns an error.
+On error, both functions return \-1 and set the system error string.
+.PD
+.PP
+The 
+.B nchange
+and
+.B changetime
+elements of
+.B Scsi
+the number of times a media
+change has been detected, and the
+first time the current media was detected (the
+first time a SCSI command was issued
+after it was inserted).
+They are maintained by 
+.BR Scsi.cmd .
+The initial SCSI inquiry result is kept in
+.BR inquire .
+.PP
+.I Scsierror
+returns a textual description of the SCSI status
+denoted by the ASC and ASCQ sense codes.
+The description is found by consulting
+.BR /lib/scsicodes .
+.SH FILES
+.TF
+.TP
+.B /lib/scsicodes
+List of textual messages corresponding to SCSI error codes;
+consulted by
+.BR scsierror .
+.SH SOURCE
+.B /appl/lib/scsiio.b
+.SH SEE ALSO
+.IR disks (2),
+.IR sd (3)
--- /dev/null
+++ b/man/2/secstore
@@ -1,0 +1,278 @@
+.TH SECSTORE 2
+.SH NAME
+secstore \- fetch data from Plan 9's secure storage service
+.SH SYNOPSIS
+.EX
+include "dial.m";
+include "secstore.m";
+secstore := load Secstore Secstore->PATH;
+
+Maxfilesize: con 128*1024;	# default
+
+init:       fn();
+privacy:    fn(): int;
+cansecstore:    fn(addr: string, user: string): int;
+mkseckey:   fn(pass: string): array of byte;
+dial:       fn(addr: string): ref Dial->Connection;
+auth:       fn(conn: ref Dial->Connection, user: string, seckey: array of byte):
+                (string, string);
+connect:    fn(addr: string, user: string, seckey: array of byte):
+                (ref Dial->Connection, string, string);
+sendpin:    fn(conn: ref Dial->Connection, pin: string): int;
+files:       fn(conn: ref Dial->Connection):
+                list of (string, int, string, string, array of byte);
+getfile:    fn(conn: ref Dial->Connection, name: string,
+                maxsize: int): array of byte;
+putfile:    fn(conn: ref Dial->Connection, name: string, data: array of byte): int;
+remove:     fn(conn: ref Dial->Connection, file: string): int;
+bye:        fn(conn: ref Dial->Connection);
+
+mkfilekey:   fn(pass: string): array of byte;
+decrypt:    fn(data: array of byte, filekey: array of byte): array of byte;
+encrypt:    fn(data: array of byte, filekey: array of byte): array of byte;
+erasekey:   fn(key: array of byte);
+
+lines:      fn(file: array of byte): list of array of byte;
+.EE
+.SH DESCRIPTION
+.B Secstore
+establishes a secure authenticated connection with a Plan 9
+.I secstore
+service (or equivalent, such as Plan 9 from User Space),
+that can then be used to fetch and decrypt data files, such as the
+.B factotum
+file containing the initial keys for an instance of
+.IR factotum (4).
+The module's functions hold the file descriptors for the connection in a
+.BR Dial->Connection
+value,
+as returned by
+.IR dial (2).
+The
+.I addr
+parameter that gives the network address of the
+.I secstore
+service is also as defined in
+.IR dial (2).
+A nil value defaults to
+.BR "net!$auth!secstore" ,
+for translation in the usual way by
+.IR cs (8).
+.PP
+.B Init
+must be called before invoking any other operation of the module.
+.PP
+.B Privacy
+ensures the memory of the calling process cannot be read, for instance by
+.IR prog (3).
+It returns zero on success and a negative value on failure.
+.PP
+.B Cansecstore
+returns true if a connection can be made to a
+.I secstore
+at network address
+.IR addr ,
+and the given
+.I user
+has a
+.I secstore
+account;
+it returns false otherwise.
+.PP
+Users authenticate themselves to the service using a secret key and a special protocol that does not
+reveal the key itself to the remote service.
+The textual secret (eg, password or pass phrase) is not used directly by the following functions,
+but only after transformation by
+.BR mkseckey ,
+which hashes it into an array of bytes.
+That is the
+.I key
+parameter to the functions.
+.PP
+.B Dial
+dials the
+.I secstore
+at network address
+.I addr
+(as defined by
+.IR dial (2))
+and returns a reference to the resulting
+.BR Dial->Connection .
+It returns nil on an error and sets the error string.
+.PP
+.B Auth
+authenticates a fresh connection as belonging to a given
+.I user
+of the service.
+The parameter
+.I conn
+refers to the
+.B Dial->Connection
+value representing the connection.
+.I User
+names a user registered with the service.
+The parameter
+.I seckey
+is the result of applying
+.B mkseckey
+to the user's secret.
+.I Auth
+returns a tuple
+.BI ( srvname,\ diag ).
+.I Srvname
+is the service name configured in the remote host (often simply
+.BR secstore ).
+On an error,
+.I srvname
+is nil, and
+.I diag
+is a diagnostic.
+If the remote service
+has been configured to demand extra authentication data, then
+.I diag
+contains a demand for it.
+Currently the only such value is
+.RB ` need pin ';
+call
+.B sendpin
+to provide it to the connection.
+If
+.B sendpin
+succeeds, it returns zero, and
+.I conn
+can be used normally; on error,
+.B sendpin
+returns -1 and the connection cannot be used.
+.PP
+.B Connect
+combines the actions of
+.B dial
+and
+.BR auth :
+dials the
+.I secstore
+at
+.IR addr ,
+and mutually authenticates the server and the given
+.I user
+using the user's secret
+.I key
+for that service.
+It returns a tuple
+.BI ( conn,\ srvname,\ diag ),
+where each component is as described for
+.B dial
+and
+.B auth
+above.
+On an error,
+.I conn
+is nil, and
+.I diag
+contains a diagnostic.
+.PP
+.B Getfile
+retrieves the file
+.I name
+from the secure store, and returns its contents as an array of bytes.
+.I Maxsize
+gives the largest acceptable file size; if the value is zero or negative,
+a large value is used by default.
+The files stored on the service are separately encrypted under the user's secret key.
+.B Mkfilekey
+takes a textual secret
+.I key
+and returns a hash of it as an array of bytes,
+suitable for use as the
+.I filekey
+parameter in subsequent calls to
+.BR decrypt .
+(The
+.I filekey
+is not the same value as the
+.I seckey
+used for initial authentication, although the secret text is the same.)
+.PP
+.B Putfile
+writes
+.I data
+under file
+.I name
+to the secure store, overwriting a possibly existing file by that name.
+.I Data
+should already be encrypted.
+The caller can arrange this by calling
+.BR encrypt .
+.B Putfile
+returns 0 on success and a negative value on error.
+.PP
+.B Remove
+deletes the given
+.I file
+from the server.
+It returns 0 on success and a negative value on error.
+.PP
+.B Decrypt
+decrypts the
+.I data
+previously fetched from a file on the secure store.
+It uses the
+.I filekey
+produced by
+.B mkfilekey
+to decrypt the data in place (ie, modifying the contents of
+.IR data )
+and returns a slice of
+.I data
+that excludes any headers and trailers in the encoding.
+It returns nil if the file could not be decrypted (usually because the
+.I key
+value is not actually the encryption key).
+.PP
+.B Encrypt
+does the opposite of
+.BR decrypt .
+Given plain
+.I data
+and
+.I filekey
+produced by
+.BR mkfilekey ,
+it returns an encrypted version of data, including headers and trailers.
+This data is suitable for writing to the secure store with
+.BR putfile .
+.PP
+.B Erasekey
+clears the bytes of
+.I key
+to zero; it should be called on every value produced by
+.B mkfilekey
+and
+.BR mkseckey ,
+after use,
+but can also be used on the data arrays returned by
+.B getfile
+and
+.BR decrypt .
+.PP
+.B Lines
+returns a list of slices of
+.IR file ,
+representing each line of
+.I file
+in turn (including newline).
+.IR Factotum (4)
+for instance requires keys to be written to its control file one at a time.
+.PP
+.B Bye
+closes the connection to the
+.IR secstore .
+.SH SOURCE
+.B /appl/lib/secstore.b
+.SH DIAGNOSTICS
+As well as returning the error values described above, functions set the system error string.
+.SH SEE ALSO
+.IR crypt (1),
+.IR secstore (1),
+.IR factotum (2),
+.IR factotum (4)
--- /dev/null
+++ b/man/2/security-0intro
@@ -1,0 +1,162 @@
+.TH SECURITY-INTRO 2
+.SH NAME
+intro \- introduction to security
+.SH SYNOPSIS
+.EX
+include "keyring.m";
+include "security.m";
+.EE
+.SH DESCRIPTION
+This is an introduction to some of the principals behind computer security as well as a description of how these principals are used in Inferno. More detailed descriptions of the methods and principals for ensuring secure communications on computers can be found in texts such as
+.I "Applied Cryptography"
+by Bruce Schneier (published 1996, J. Wiley & Sons, Inc.).
+.PP
+Inferno provides several levels of security:
+.IP \(bu
+Mutual authentication means that two users or applications that want to communicate can establish that they are who they say they are. It is the basic level of security provided by Inferno. Thus, for example, when a user connects to
+an Inferno service, they can and must establish that they are a legitimate user.
+.IP \(bu
+Message digesting is a technique to ensure that an interloper cannot modify messages sent between users.
+.IP \(bu
+Encryption protects the confidentiality of messages so that only the party or parties for whom the messages are intended can decrypt and read them. 
+Inferno makes it easy to enforce any one or all of these levels of security.
+.SS "Mutual Authentication"
+Authentication requires a combination of elements: a third party that each user can trust, an algorithm or mathematical method to secure messages between users, and a protocol for exchanging messages that ensures that a third party or intruder cannot pretend to be one of the users, or use some other method to undermine their communication.
+.PP
+One important method for authenticating users in Inferno is the use of digital signatures. Like signing a letter a digital signature testifies to the identity of the sender. Fortunately, it is much more difficult to forge a digital signature. 
+.PP
+Even after users are authenticated to each other, it is possible for someone `listening' to their communication to read and possibly modify their messages without the users knowing it. So authentication solves one security requirement, but not all of them.
+.SS "Message Digesting"
+Message digesting uses a mathematical hashing algorithm to convert a message
+into an indecipherable string of fixed length (a digest).
+By appending the hashed value to the message,
+the authenticity of the message can be verified.
+The recipient takes the message, applies the same hashing algorithm used by the sender, and compares the value to the one sent.
+If the values are the same, then the message received must be the same as the one that was sent.
+.PP
+Inferno includes a counter in the digest to check that messages were received in the correct order and
+that no messages were inserted by a third party listening in on the line.
+A secret key is also included in the digest to verify the identity of the sender.
+.PP
+A message digest ensures that no one has tampered with a message.
+It does not prevent someone from reading it.
+.SS "Message Encryption"
+The traditional notion of encryption is translating a message, called a plaintext in cryptography, into something unreadable, called a ciphertext. Its most obvious use is to provide confidentiality. Only someone able to decrypt the message, or translate it back to its original form, can interpret it.
+.PP
+A mathematical algorithm is used to both encrypt and decrypt a message. Encryption algorithms depend on keys or bit strings of a specified length for encryption and decryption. The nature of an algorithm and the size of the key determine the degree of security.
+.PP
+Two basic types of algorithms are used in cryptography: private key (or symmetric key) and public key algorithms. With symmetric algorithms the same key is used to encrypt and decrypt a message. This key must be a secret, known only to the users who want to communicate. It is often called a private or secret key. 
+.PP
+A public key algorithm may use a private or secret key to encrypt a message and a public key to decrypt it, or vice-versa. The private or secret key is known only to one user. The public key, however, does not have to be kept secret and may be distributed to anyone the user wishes to communicate with. 
+.PP
+Inferno uses a public key algorithm for digital signatures and symmetric key algorithms for encryption.
+.PP
+A user can encrypt a message with or without appending a message digest.
+.PP
+.SS "Algorithms Supplied With Inferno"
+Some of the considerations when choosing algorithms are speed, degree of security, and
+political restrictions on export.
+The algorithms used in Inferno are well known and rigorously tested.
+.TP
+One-way hashing algorithms
+SHA1 and MD5 are well known (in cryptographic circles) one-way hashing algorithms. MD5 is a high-speed, 128-bit hash. SHA1 is a somewhat slower but more secure 160-bit hash.
+.TP
+Elgamal and RSA public key signature algorithms
+Elgamal is a public key system widely used for creating digital signatures. It uses a private key for signing a message and a public key for verifying it.
+Inferno also supports the widely-used RSA and DSS-1 signature algorithms.
+Because Inferno initially used Elgamal
+keys, it does not assume that either a private or public key can be used for encryption or decryption. With constant advances in the field of cryptography, one of the design goals of Inferno is to create a security component that will be easy to enhance as new algorithms are developed.
+.TP
+Encryption algorithms
+DES (the Data Encryption Standard) was adopted by the US government in 1976 as a standard encryption/decryption system for unclassified data in the United States. It is widely used, especially by the financial services industry. Two types of DES are offered: DES-ECB and DES-CBC. ECB or Electronic Code Book and CBC or Chain Block Coding are part of the ANSI Banking Standard. CBC is more complex and less vulnerable than ECB. Both versions of DES provide 56-bit keys.
+.RS
+.PP
+RC4 is a symmetric or private key system that is about 10 times faster than DES.
+.RE
+.TP
+Diffie-Hellman key exchange algorithm
+Diffie-Hellman is an algorithm for creating a secret key to be shared by users for encrypting messages (sometimes called a shared secret). It requires each user to exchange certain information with the other. This information can be exchanged in the open, that is, without encryption. Each user is able to create the same, secret key from this information. However, no one else listening to their exchange would be able to create or determine the secret key.
+.SS "Security Protocols"
+Cryptanalysis is the study of how to break cryptographic systems. Attempts to disrupt or listen to confidential communications are called attacks. Usually the objective of an attack is to figure out the secret key, decrypt a message, or add or modify messages in some way. 
+.PP
+There are many methods or strategies for attacking a confidential communication. One method is called a man-in-the-middle attack, where someone listening to a communication pretends to be one of the parties; another is a replay attack, where an interloper reuses messages that have already been exchanged in an attempt to discover a pattern. 
+.PP
+In order to thwart such attacks and establish some level of trust between communicating parties, it is necessary to employ certain protocols.
+Inferno uses two well-established protocols to permit keys to be exchanged
+and to permit
+mutual authentication of the identities of two communicating parties.
+.PP
+A
+.I "digital signature"
+is one way to guarantee that a message sent by a user is indeed from that user and not someone else. A signature does not require that a message be encrypted. It can be appended to a message in order to guarantee the identity of the sender. With Elgamal, creating a signature requires that the user have a secret or private key. Uniquely associated with the private key is another key that can be distributed publicly. This public key is used along with the private key to create a signature, and is used by others to verify the signature.
+.PP
+To create a signature the Elgamal algorithm is applied to a combination of the private key, the public key, and the message to be signed. The output of the algorithm is the signature. 
+.PP
+To verify the signature the receiver applies the Elgamal algorithm to the public key and the signature. If the output is the same message that was sent with the signature, then the signature is valid. This method ensures that the user receiving a message is indeed communicating with someone who owns the public key. 
+.PP
+The next step is to determine who the owner of the public key is, and to ensure that it belongs to the user that the receiver wants to communicate with. This is accomplished by having a third party create a
+.I "certificate"
+testifying to the identity of the owner of the public key. This third party is called a certifying authority (CA). If a user trusts the certifying authority, a copy of a certificate is sufficient to determine the ownership of a public key, and therefore, the signature and identity of the user sending a message.
+.PP
+A certificate includes a variety of information: a user's public key, the identity of the user, Diffie-Hellman parameters, an expiration time for the certificate, and the signature of the CA. The CA's public key is sent to the user along with the certificate to verify the CA's signature.
+.PP
+Inferno provides two different methods for obtaining a certificate depending on whether a user has access to a keyboard or not. For users with a keyboard, Inferno offers a variation of the Encrypted-Key-Exchange (EKE) protocol,
+described in
+.IR login (6).
+The protocol depends on establishing trust between a user and a CA
+using a shared secret (password).
+The secret must initially be established at the CA by some secure means:
+typing a password on a secure console at the CA, or transmitting the
+password securely off-line, perhaps by unintercepted letter or untapped phone call.
+To obtain a certificate, a user can subsequently enter
+the secret on the client machine's keyboard; the protocol obtains
+a certificate without revealing the secret.
+.PP
+For an application or user on a set-top box, which normally does not have a keyboard, entering a password would be difficult. Therefore, Inferno provides a different method to establish trust. When the set-top box is turned on, it creates a private/public key pair and dials the service provider's CA to get a certificate. The CA returns a certificate blinded or scrambled with a random bit string known only to the CA. A hashed version of the string is displayed on the user's screen. The user telephones the CA and compares what is displayed with what the CA has sent. If they match, and the user can prove his or her identity, the CA makes the random bit string known to the user, so the certificate can be unscrambled.
+.SS "Authentication"
+Mutual authentication in Inferno requires that two parties who want to communicate must have a certificate from the same CA. As described above, the public key of the CA is used to check the certificate sent by the other user. The certificate is used to verify that the public key belongs to the party that the user wants to communicate with.
+.PP
+If a user can trust the public key, then the key can be used to check the signature sent by the other party. If the public key unlocks the signature, then whoever sent the signature must have the corresponding secret key, and therefore, must be the owner of the public key.
+.PP
+The default protocol provided by Inferno for mutual authentication is
+the station-to-station protocol described in
+.IR auth (6).
+It has the property that both parties can derive the
+same key from exchanged and validated data but no eavesdropper
+can determine the key.
+.SS "Security at the Application Layer"
+An application can make use of the algorithms and protocols described previously by using only a few library routines such as:
+.IR security-login (2),
+.IR security-auth (2)
+and
+.IR connect
+(see
+.IR security-ssl (2)).
+The
+.B Login
+module enables an application that shares a password with a server acting as the CA to obtain a certificate. After obtaining certificates, two applications establish a mutually authenticated connection by calling
+.IR auth .
+.I Auth
+performs the entire STS protocol.
+.I Connect
+connects an application to an SSL (security sockets layer) device. Each application can create message digests or encrypt messages by writing to this device. Messages are received and decrypted by reading from the SSL device.
+.PP
+Although Inferno provides these routines to make it easy to establish secure communications, an application is not restricted to their use. Lower-level routines used by
+.I login
+and
+.I auth
+are also available to an application. These routines enable an application to create alternate methods for establishing security, or to perform specialized functions like signing files.
+.PP
+Inferno also provides security routines tailored for set-top boxes. For example, a set-top-box can use
+.IR register (8)
+instead of
+.I login
+(see 
+.IR security-login (2)).
+.I Register
+obtains a certificate without requiring a user to enter a password.
+.PP
+There are also commands in section 8 that establish a server as a Certifying Authority or `signer'. For example, a CA needs a key and password to create a certificate. These can be created on the server using the commands
+.IR changelogin (8)
+and
+.IR createsignerkey (8).
--- /dev/null
+++ b/man/2/security-auth
@@ -1,0 +1,157 @@
+.TH SECURITY-AUTH 2
+.SH NAME
+Auth: init, client, server \- authenticated connections between client and server
+.SH SYNOPSIS
+.EX
+include "keyring.m";
+include "security.m";
+auth := load Auth Auth->PATH;
+
+init:   fn(): string;
+client: fn(alg: string, ai: ref Keyring->Authinfo,
+            fd: ref Sys->FD): (ref Sys->FD, string);
+server: fn(algs: list of string, ai: ref Keyring->Authinfo,
+            fd: ref Sys->FD, setid: int): (ref Sys->FD, string);
+.EE
+.SH DESCRIPTION
+.B Auth
+establishes authenticated connections using the station to station protocol described
+in
+.IR auth (6).
+It encapsulates the use of the primitives of
+.IR keyring-auth (2)
+and
+.IR security-ssl (2)
+for the particular case where the stations
+play the rôles of `client' and `server'.
+The underlying primitives must still be accessed directly in some cases,
+for instance when completely symmetric authentication is needed between peers.
+.PP
+.B Init
+must be called before using any other functions in
+.BR Auth ;
+it returns nil if successful, and a diagnostic message otherwise.
+.PP
+.B Client
+authenticates a connection with the server on
+.I fd
+using the authentication data in
+.IR ai .
+If successful, and
+.I alg
+is neither
+.B nil
+nor the value
+.B
+"none"\c
+,
+.B client
+will set the connection to digest or encrypt the data,
+using the
+digest or encryption algorithm specified in
+.IR alg .
+It returns the file descriptor for the connection,
+and a string with information about the connection.
+If an authenticated connection cannot be established,
+.B client
+returns a nil file descriptor and an error message.
+.PP
+.B Server
+authenticates a client connection
+.IR fd ,
+as described in
+.IR keyring-auth (2),
+using the server's authentication data in
+.IR ai .
+If successful, and the client requested the use of a digest or
+encryption algorithm, and that algorithm is listed in
+.IR algs ,
+.B server
+enables the security layer
+.IR ssl (3)
+using the selected algorithm.
+Furthermore, if
+.I setid
+is non-zero, the current user name is set to the
+newly authenticated name.
+.B Server
+returns a file descriptor for the connection,
+and a string with information about the connection.
+If an authenticated connection cannot be established,
+or the client's chosen algorithm is not listed,
+.B server
+returns a nil file descriptor and an error message.
+.PP
+Any string acceptable to
+.IR ssl (3),
+including
+.B
+"clear"\c
+, can be given as an
+.I alg
+to
+.BR client ,
+or listed in
+.I algs
+for
+.BR server .
+Furthermore, the special string
+.B
+"none"
+tells both functions
+that
+.IR ssl (3)
+should not be used at all on a connection.
+.SH EXAMPLE
+This selection from
+.B /appl/cmd/mount.b
+illustrates client-side use.
+.PP
+.EX
+	au := load Auth Auth->PATH;
+	err := au->init();
+	if(err != nil){
+		sys->fprint(stderr, "mount: %s\en", err);
+		exit;
+	}
+	fd: ref Sys->FD;
+	(fd, err) = au->client("none", ai, c.dfd);
+	if(fd == nil){
+		sys->fprint(stderr, "mount: authentication failed: %s\en", err);
+		exit;
+	}
+	dir := hd argv;
+	ok = sys->mount(fd, dir, flags, "");
+	if(ok < 0)
+		sys->fprint(stderr, "mount: %r\en");
+.EE
+.PP
+The following example from
+.B /appl/lib/styxd.b
+shows server-side use;
+note that
+.B readauthinfo
+is called first to fetch the authentication data to pass to
+.BR server .
+.PP
+.EX
+	kr := load Keyring Keyring->PATH;
+	...
+	ai := kr->readauthinfo("/usr/"+user+"/keyring/default");
+	auth->init();
+	(fd, info_or_err) := auth->server(argv, ai, stdin, 1);
+	if(fd == nil){
+		sys->fprint(stderr, "styxd: %s\en", info_or_err);
+		exit;
+	}
+	sys->pctl(Sys->FORKNS, nil);
+	if(sys->export(fd, Sys->EXPASYNC) < 0)
+		sys->fprint(stderr, "styxd: file export: %r\en");
+.EE
+.SH SOURCE
+.B /appl/lib/auth.b
+.SH "SEE ALSO"
+.IR keyring-auth (2),
+.IR security-ssl (2),
+.IR ssl (3),
+.IR auth (6)
--- /dev/null
+++ b/man/2/security-login
@@ -1,0 +1,93 @@
+.TH SECURITY-LOGIN 2
+.SH NAME
+login \- verify credentials
+.SH SYNOPSIS
+.EX
+include "keyring.m";
+include "security.m";
+login := load Login Login->PATH;
+
+login: fn(name, password, addr: string):
+	(string, ref Keyring->Authinfo);
+.EE
+.SH DESCRIPTION
+The
+.BR Login
+module is provided for use by a client of
+a certifying authority (CA) or `signer'.
+The
+.B login
+function communicates
+with a certifying authority (CA)
+in order to create a
+.B Keyring->Authinfo
+adt
+which contains a public/private key pair and a certificate
+signed by the CA
+(see
+.IR keyring-intro (2)).
+The public/private key pair is generated by
+.B login
+using the same parameters as those in the signer's key
+(eg, algorithm and key length);
+see
+.IR keyring-gensk (2).
+The procedure assumes a secret, i.e. a
+password, has already been established
+between the user and the CA.
+See
+.IR changelogin (8)
+and
+.IR keyfs (4)
+for how this password is managed at the
+CA.
+.PP
+.B Login
+connects, using
+.IR dial (2),
+to the signer at network address
+.IR addr ,
+which is any form accepted by
+.IR cs (8),
+including the special address
+.BR $SIGNER ,
+which
+.IR cs
+will translate to the client's default signer (if there is one).
+Normally the incoming call will be given to
+.IR logind (8)
+by
+.IR svc (8).
+.PP
+.B Login
+sends the user
+.I name
+and
+.IR password ,
+using the protocol described in
+.IR login (6),
+to justify the server's
+issuing a certificate, which is returned in a
+.B Keyring->Authinfo
+adt on success.
+The certificate can if desired be stored by
+.BR Keyring->writeauthinfo ;
+see
+.IR keyring-auth (2).
+The password is used by the encrypted
+key exchange protocol to establish
+a secure channel between user and CA.
+.SH SOURCE
+.B /appl/lib/login.b
+.SH SEE ALSO
+.IR getauthinfo (8),
+.IR keyring-auth (2),
+.IR login (6),
+.IR createsignerkey (8),
+.IR logind (8)
+.SH DIAGNOSTICS
+.B Login
+returns nil in the string component
+on success and a diagnostic string on error (with a nil
+.B Keyring->Authinfo
+reference).
--- /dev/null
+++ b/man/2/security-oldauth
@@ -1,0 +1,246 @@
+.TH SECURITY-OLDAUTH 2
+.SH NAME
+oldauth: certtostr, pktostr, sktostr, strtocert, strtopk, strtosk, sign, verify, readauthinfo, writeauthinfo \- encoding for original Inferno authentication protocol
+.SH SYNOPSIS
+.EX
+include "ipints.m";
+include "crypt.m";
+include "oldauth.m";
+oldauth := load Oldauth Oldauth->PATH;
+
+Certificate: adt
+{
+    sa:     string;
+    ha:     string;
+    signer: string;
+    exp:    int;
+    sig:    ref Crypt->PKsig;
+};
+
+Authinfo: adt
+{
+    mysk:   ref Crypt->SK;
+    mypk:   ref Crypt->PK;
+    owner:  string;
+    cert:   ref Certificate;
+    spk:    ref Crypt->PK;
+    alpha:  ref IPints->IPint;
+    p:      ref IPints->IPint;
+};
+
+sign:   fn (sk: ref Crypt->SK, signer: string, exp: int,
+          state: ref Crypt->DigestState, ha: string): ref Certificate;
+verify: fn (pk: ref Crypt->PK, cert: ref Certificate,
+          state: ref Crypt->DigestState): int;
+
+strtocert: fn(s: string): ref Certificate;
+certtostr: fn(c: ref Certificate): string;
+strtopk:   fn(s: string): (ref Crypt->PK, string);
+pktostr:   fn(pk: ref Crypt->PK, owner: string): string;
+strtosk:   fn(s: string): (ref Crypt->SK, string);
+sktostr:   fn(sk: ref Crypt->SK, owner: string): string;
+
+readauthinfo:   fn(filename: string): ref Authinfo;
+writeauthinfo:  fn(filename: string, info: ref Authinfo): int;
+.EE
+.SH DESCRIPTION
+Certificates, public keys, and private keys are passed over networks and between applications using a Unicode representation.
+This collection of functions provide a means to convert adts supplied by the system to and from their portable textual representation. These routines are used by
+.IR login (2)
+and
+.IR factotum (4)
+to implement the Inferno authentication protocol.
+.PP
+Public and private keys are represented by
+.B Crypt->PK
+and
+.B Crypt->SK
+(see
+.IR keyring-intro (2)).
+An authentication domain is represented by
+the public key of the domain's
+.IR signer ,
+typically in control of a
+.IR keyfs (4)
+and running a
+.IR logind (8).
+Two adts associate a public/private key pair with a user name within a specific authentication domain:
+.TP
+.B Authinfo
+The
+.B Authinfo
+adt contains an individual user's private and public key, a human-readable name for the key (eg, a user name),
+the signer's certificate
+and the signer's public key, and the Diffie-Hellman parameters.
+The signer's certificate binds the user's public key to the given key name in the signer's domain.
+.TP
+.B Certificate
+The
+.B Certificate
+adt contains a digital signature with the certification of the trusted authority (CA).
+The signature covers not only the user's public key, but the key's name, the signer's name and
+the expiration time of the certificate.
+Both the key's name and the signer's name are local to the signer's domain.
+.PP
+.B Init
+must be called before using any other operation in the module.
+.PP
+.B Sign
+returns a Certificate containing the digital signature using secret key
+.I sk
+of a digest's
+.IR state ,
+which is the output of the hash algorithm named
+.IR ha ,
+combined with the hash of the signer's name, and the certificate's expiration time (in seconds from the Epoch).
+Valid hash algorithms are
+.B sha1
+and
+.BR md5 .
+The expiry time should be zero if the certificate does not expire.
+Typically the
+.I state
+is the result of hashing
+.IP
+.EX
+array of byte pktostr(pk, username)
+.EE
+.PP
+for a given public key
+.I pk
+that is associated with the given
+.I username
+by the signer.
+.PP
+.B Verify
+checks that the given Certificate is the result of signing the given
+.I state
+using the secret (private) key corresponding to public key
+.IR pk .
+It returns true (non-zero) if the certificate is valid, including the signer's name, and the expiration time;
+the caller must enforce the expiration time if desired.
+It returns false (zero) if the certificate is invalid.
+.	#######
+.PP
+.B Sign
+creates a digital signature of a digest from the concatenation of: a message, the name of the signer, and an expiration time.
+.I State
+is the digest state after running
+.BR sha1 ,
+.B md4
+or
+.B md5
+over the message.
+.I Ha
+is a string specifying the hash algorithm to use:
+.B
+"sha"\fR,
+.B
+"sha1"\fR,
+.B
+"md4"\fR
+or
+.B
+"md5"\fR.
+.B Sign
+extends the digest to cover the signer's name
+(taken from the private key,
+.IR sk )
+and the expiration time.
+It returns a certificate containing the digital signature of the digest, signer name, hash algorithm and signature algorithm.
+If any parameter is invalid,
+.B sign
+returns nil.
+The signature algorithm is implied by the type of the private key.
+.PP
+.B Verify
+uses public key
+.I pk
+to verify a certificate.
+It returns non-zero (true) if the certificate is valid; zero (false) otherwise.
+.I State
+is the digest state after running the chosen digest algorithm
+over the message.
+.	#######
+.PP
+The remaining operations fetch and store those values and convert to and from text representations for use in protocols and for storage.
+.PP
+.B Strtocert
+takes a string argument containing a varying number of newline-separated fields:
+a signature algorithm, a hash algorithm, a signer's name, an expiration time, and values representing a digital signature.
+It returns the corresponding
+.BR Certificate .
+If the string is of improper format, the result is
+.IR nil .
+.PP
+.B Certtostr
+performs the inverse operation: takes the
+.B Certificate
+.I c
+and produces a text string suitable for communication over a network.
+Note that the string will contain newline characters.
+.PP
+.B Strtopk
+and
+.B strtosk
+take as their arguments a string
+.I s
+representing the public and private keys respectively.
+.I S
+contains an algorithm name, a user name and values representing the key.
+Each returns a tuple
+.BI ( k,\ s ),
+where
+.I k
+is the resulting key value (ie,
+.B Crypt->PK
+or
+.BR Crypt->SK )
+and
+.I s
+is a string giving the name associated with the key, typically a user name.
+If the format of
+.I s
+is invalid,
+.I k
+is
+.IR nil ,
+and
+.I s
+contains a diagnostic.
+.PP
+.B Pktostr
+and
+.B sktostr
+perform the inverse operations:
+they take a public key (secret key)
+.I pk
+or
+.IR sk ,
+the
+.I owner
+name to be associated with that key, and produce a printable representation as a string.
+The
+.I owner
+names the user that owns the key; in the case of a public key,
+the user is expected to possess the corresponding private key.
+.PP
+.B Readauthinfo
+reads a representation of an
+.B Authinfo
+from a file.
+It returns nil if there is a read error or a conversion error;
+it returns a reference to the
+.B Authinfo
+otherwise.
+.PP
+.B Writeauthinfo
+writes a representation of
+.I info
+to a file. It returns -1 if the write operation fails, 0 otherwise.
+.SH SOURCE
+.B /appl/lib/oldauth.b
+.SH SEE ALSO
+.IR crypt-intro (2),
+.IR ipints (2),
+.IR security-intro (2)
--- /dev/null
+++ b/man/2/security-random
@@ -1,0 +1,43 @@
+.TH SECURITY-RANDOM 2
+.SH NAME
+random: randomint, randombuf \-
+random number generation
+.SH SYNOPSIS
+.EX
+include "security.m";
+random := load Random Random->PATH;
+
+randomint:  fn(which: int): int;
+randombuf:  fn(which, n: int): array of byte;
+.EE
+.SH DESCRIPTION
+.B Randomint
+and
+.B randombuf
+return random or not-quite-random data
+obtained from
+.B /dev/random
+or
+.BR /dev/notquiterandom .
+.B Randomint
+returns a random integer;
+.B randombuf
+returns an array of length
+.I n
+filled with random bytes.
+In both functions,
+.I which
+may be either
+.B ReallyRandom
+or
+.B NotQuiteRandom
+to select the random data source.
+.SH FILES
+.B /dev/random
+.br
+.B /dev/notquiterandom
+.SH SOURCE
+.B /appl/lib/random.b
+.SH SEE ALSO
+.IR rand (2),
+.IR cons (3)
--- /dev/null
+++ b/man/2/security-ssl
@@ -1,0 +1,65 @@
+.TH SECURITY-SSL 2
+.SH NAME
+ssl: connect, secret \- interface to the Secure Sockets Layer
+.SH SYNOPSIS
+.EX
+include "sys.m";
+include "security.m";
+ssl := load SSL SSL->PATH;
+
+connect: fn(fd: ref Sys->FD): (string, ref Sys->Connection);
+secret:  fn(c: ref Sys->Connection, secretin,
+                secretout: array of byte): string;
+.EE
+.SH DESCRIPTION
+.B SSL
+provides an interface to the secure sockets layer device
+.IR ssl (3).
+.PP
+.B Connect
+allocates a new
+.IR ssl (3)
+connection directory.
+It pushes
+file descriptor
+.I fd
+into the
+.B data
+file of that connection, and if successful,
+returns a reference to a
+.B Connection
+adt describing the connection.
+The
+.B Connection
+adt has its members set as follows:
+.B dir
+names the resulting connection directory;
+.B cfd
+is open on the connection's
+control file; and
+.B dfd
+is open on the connection's
+.B data
+file,
+which is read and written to exchange data on the original
+.I fd
+using SSL.
+.PP
+.B Secret
+sets the secrets for each direction on Connection
+.I c ;
+if a secret is nil, the existing setting for that direction remains unchanged.
+The string returned describes errors encountered, if any; otherwise it is nil.
+.PP
+.SH SOURCE
+.B /appl/lib/ssl.b
+.SH "SEE ALSO"
+.IR security-auth (2),
+.IR ssl (3)
+.SH DIAGNOSTICS
+.B Connect
+returns a tuple containing a string and a
+.B Connection
+reference.
+On success the string is nil, and the connection reference is not nil;
+on error, the string contains a diagnostic, and the connection reference is nil.
--- /dev/null
+++ b/man/2/selectfile
@@ -1,0 +1,57 @@
+.TH SELECTFILE 2
+.SH NAME
+selectfile \-
+file browser
+.SH SYNOPSIS
+.EX
+include "selectfile.m";
+selectfile := load Selectfile Selectfile->PATH;
+
+init:        fn();
+filename:	fn(ctxt: ref Draw->Context, parent: ref Draw->Image,
+               title: string,
+               pat: list of string,
+               dir: string): string;
+.EE
+.SH DESCRIPTION
+.B Selectfile
+provides an interactive file browser for use by a
+.IR wm (1)
+application.
+It allows a user to browse the
+file system to select a file of a give type.
+.PP
+.B Init
+should be called once to initialise the module's internal state.
+.PP
+.B Filename
+makes a dialog panel for selecting a file.
+It is created in the graphics context
+.IR ctxt ,
+near the northeast corner of a given parent window,
+.IR parent ,
+represented by that window's Image.
+(If the parent window is a Tk Toplevel
+.IR t ,
+for instance, the appropriate value is
+.IB t .image \f1.)\fP
+If
+.I parent
+is nil, the panel is centred on the screen.
+.I Dir
+gives the directory where the file search should begin.
+Only files that match
+.I pat
+are displayed.
+The returned string is the name of the selected file,
+or the empty string if no file was selected.
+.SH SOURCE
+.B /appl/lib/selectfile.b
+.SH SEE ALSO
+.IR dialog (2),
+.IR dividers (2),
+.IR draw-context (2),
+.IR tabs (2),
+.IR tk (2),
+.IR wmlib (2)
+
--- /dev/null
+++ b/man/2/sets
@@ -1,0 +1,226 @@
+.TH SETS 2
+.SH NAME
+Sets \-
+sets of non-negative integers
+.SH SYNOPSIS
+.EX
+include    "sets.m";
+\fIOR\fP include "sets32.m";
+sets := load Sets Sets->PATH;
+A, B: import Sets;
+
+Sets: adt {
+    init:       fn();
+    set:        fn(): Set;
+    str2set:    fn(str: string): Set;
+    bytes2set:  fn(d: array of byte): Set;
+    Set: adt {
+        # opaque data
+
+        X:          fn(s1: self Set, op: int, s2: Set): Set;
+        add:        fn(s: self Set, n: int): Set;
+        addlist:    fn(s: self Set, ns: list of int): Set;
+        del:        fn(s: self Set, n: int): Set;
+        invert:     fn(s: self Set): Set;
+
+        eq:         fn(s1: self Set, s2: Set): int;
+        holds:      fn(s: self Set, n: int): int;
+        isempty:      fn(s: self Set): int;
+        msb:        fn(s: self Set): int;
+        limit:      fn(s: self Set): int;
+
+        str:        fn(s: self Set): string;
+        bytes:      fn(s: self Set, n: int): array of byte;
+    };
+};
+.EE
+.SH DESCRIPTION
+.PP
+The
+.B Sets
+module provides routines for manipulating sets
+of small non-negative integers. There are currently
+two implementations available:
+the implementation declared in
+.B sets32.m
+stores sets of numbers from 0 to 31 inclusive;
+the implementation in
+.B sets.m
+stores arbitrary sets of non-negative integers.
+The description given is for the more general
+implementation; behaviour of the other is undefined
+if an integer higher than 31 is used.
+.PP
+.B Init
+must be called first, to allow
+.B Sets
+to initialise its internal state.
+.B Set
+returns a new set, containing nothing.
+.B Str2set
+converts a string to a new set; the string
+should have been created with
+.BR Set.str() .
+.B Bytes2set
+converts an array of bytes,
+.IR d ,
+as returned by
+.BR Set.bytes() ,
+to a new set.
+.PP
+Note that all set operations are copy operations;
+none change an existing set.
+.TP 10
+.IB s1 .X(\fIop\fP,\ \fIs2\fP)
+Returns a new set, the result of combining
+.I s1
+and
+.I s2
+according to boolean operator
+.IR op .
+.I Op
+can be any bitwise boolean combination of the
+two constants
+.B A
+and
+.BR B ,
+defined in the module. Notionally, each
+set is an infinitely long string of bits, each
+bit representing a non-negative integer:
+zero if the integer is present, and one if absent.
+For each corresponding bit in
+.I s1
+and
+.IR s2 ,
+.B X
+sets a corresponding bit in the returned set
+according to the calculation
+.IR "s1 op s2" .
+.TP
+.IB s .add(\fIn\fP)
+Returns the set
+.I s
+with
+.I n
+added.
+.TP
+.IB s .addlist(\fIns\fP)
+.B Addlist
+is the same as calling
+.B add
+on each member of the list
+.IR ns ,
+but somewhat more efficient.
+.TP
+.IB s .del(\fIn\fP)
+Returns
+.I s
+with
+.I n
+removed.
+.TP
+.IB s .invert()
+.B Invert
+returns a set holding all non-negative integers
+other than those already in
+.IR s .
+Hence
+.B set().invert()
+holds all non-negative integers.
+.TP
+.IB s1 .eq(\fIs2\fP)
+Returns non-zero if
+.I s1
+is identical to
+.IR s2 .
+.TP
+.IB s .holds(\fIn\fP)
+Returns non-zero if
+.I s
+holds
+.I n
+as a member.
+.TP
+.IB s .isempty()
+Returns non-zero if
+.I s
+holds no members.
+.TP
+.IB s .msb()
+Returns the "most significant bit": the membership
+status of all members that have not been explicitly
+set. For example,
+.B set().msb()
+is 0;
+.B set().invert().msb()
+is 1.
+.TP
+.IB s .limit()
+If
+.IB s .msb()
+is zero,
+.IB s .limit()
+returns one more than the largest member contained in
+.IR s ,
+otherwise it returns one more than the largest member
+.I not
+contained in
+.IR s .
+Thus
+.B set().limit()
+yields 0,
+and
+.B set().invert().del(5).limit()
+yields 6.
+.TP
+.IB s .str()
+Returns a string corresponding to
+.IR s .
+The format is
+.IB hexdigits : msb\fR,\fP
+where
+.I hexdigits
+give the least significant members of the set,
+most significant on the left, in hexadecimal format;
+.I msb
+gives the padding bit that fills the rest of the set.
+Note that this format is compatible between the
+two implementations.
+.TP
+.IB s .bytes(\fIn\fP)
+Returns a packed byte representaton of
+.I s .
+The array is held in little-endian order,
+with the topmost bit of the top byte
+holding the msb of the set.
+The array returned will contain at least
+.I n
+bytes.
+.SH EXAMPLES
+Given two sets,
+.I s1
+and
+.IR s2 ,
+.IB s1 ".X(A&B," " s2" )
+gives their intersection;
+.IB s1 ".X(A|B," " s2" )
+their union;
+.IB s1 ".X(A&~B," " s2" )
+gives the set of all members of
+.I s1
+that aren't in
+.IR s2 ;
+.IB s1 ".X(~(A|B), " s2 )
+gives the set of all integers in neither
+.I s1
+nor
+.IR s2 .
+.PP
+.EX
+	sys->print("%s\en", set().addlist(1::2::5::nil)
+		.invert().X(A|B, set().add(2)).str());
+.EE
+produces the string
+.RB `` dd:1 '',
+corresponding to the set of all non-negative
+integers except 1 and 5.
--- /dev/null
+++ b/man/2/sexprs
@@ -1,0 +1,362 @@
+.TH SEXPRS 2
+.SH NAME
+Sexprs: Sexp \- S-expressions
+.SH SYNOPSIS
+.EX
+include "bufio.m";
+include "sexprs.m";
+sexprs := load Sexprs Sexprs->PATH;
+
+Sexp: adt {
+    pick {
+    String =>
+        s:    string;
+        hint: string;
+    Binary =>
+        data: array of byte;
+        hint: string;
+    List =>
+        l:    cyclic list of ref Sexp;
+    }
+
+    read:   fn(b: ref Bufio->Iobuf): (ref Sexp, string);
+    parse:  fn(s: string): (ref Sexp, string, string);
+    pack:   fn(e: self ref Sexp): array of byte;
+    packedsize: fn(e: self ref Sexp): int;
+    text:   fn(e: self ref Sexp): string;
+    b64text: fn(e: self ref Sexp): string;
+    unpack: fn(a: array of byte): (ref Sexp, array of byte, string);
+
+    eq:     fn(e: self ref Sexp, t: ref Sexp): int;
+    copy:   fn(e: self ref Sexp): ref Sexp;
+
+    astext: fn(e: self ref Sexp): string;
+    asdata: fn(e: self ref Sexp): array of byte;
+
+    islist: fn(e: self ref Sexp): int;
+    els:    fn(e: self ref Sexp): list of ref Sexp;
+    op:     fn(e: self ref Sexp): string;
+    args:   fn(e: self ref Sexp): list of ref Sexp;
+};
+
+init:   fn();
+.EE
+.SH DESCRIPTION
+.B Sexprs
+provides a data type and I/O for S-expressions, or `symbolic expressions',
+which represent complex data as trees.
+This implementation provides the variant defined by
+Rivest in Internet Draft
+.L draft-rivest-sexp-00.txt
+(4 May 1997),
+as used for instance by the Simple Public Key Infrastructure (SPKI).
+It offers a basic set of operations on the internal representation,
+and input and output in both canonical and advanced transport encodings.
+.I Canonical
+form conveys binary data directly and efficiently (unlike some
+other schemes such as XML).
+Canonical encoding must be used when exchanging S-expressions between computers,
+and when digitally signing an expression.
+.I Advanced
+encoding is a more elaborate
+form similar to that used by Lisp interpreters, typically using
+only printable characters: representing any binary data in hexadecimal or base 64 encodings,
+and quoting strings containing special characters, using escape sequences as required.
+Unquoted text is called a
+.IR token ,
+restricted by the standard to a specific alphabet:
+it must start with a letter or a character from the set
+.LR "-./_:*+=" ,
+and contain only letters, digits and characters from that set.
+Upper- and lower-case letters are distinct.
+See
+.IR sexprs (6)
+for a precise description.
+.PP
+.B Init
+must be called before invoking any other operation of the module.
+.PP
+.B Sexp
+is the internal representation of S-expression data, as lists and non-list values (atoms) that
+in general can form a tree structure;
+that is, a list may contain not just atoms but other lists as its elements, and so on recursively.
+The atoms are strings of text or binary.
+A well-formed S-expression might be a tree, but cannot contain cycles.
+.PP
+For convenience in processing,
+.B Sexp
+distinguishes three variants represented in a pick adt:
+.TP
+.B Sexp.String
+An atom that can be represented as a textual string
+.IR s ,
+including all tokens but also any other data that contains no characters outside the 7-bit ASCII
+set and no control-characters other than space.
+.I Hint
+is the `display hint', typically nil (see the Internet Draft for its intended use).
+.TP
+.B Sexp.Binary
+An atom that must be represented as an array of bytes
+.I data
+(typically because it is purely binary data or contains non-space control-characters).
+.I Hint
+again is the display hint.
+.TP
+.B Sexp.List
+A list of S-expression values,
+.IR l .
+.PP
+.B Sexp
+provides the following operations for input and output, using
+.IR bufio (2)'s
+buffered channels (directly or indirectly):
+.TP
+.BI read( b )
+Read one S-expression (a list or a single token) from
+.B Iobuf
+.IR b .
+Return a tuple of the form
+.RI ( e , err ),
+where
+.I e
+is the
+.B Sexp
+representing the data just read, and
+.I err
+is nil on success;
+.I b
+is positioned at the first character after the end of the S-expression.
+On an error,
+.I e
+is nil, and
+.I err
+contains the diagnostic string.
+On end-of-file, both
+.I e
+and
+.I err
+are nil.
+.TP
+.BI parse( s )
+Parse the first S-expression in string
+.IR s ,
+and return a tuple
+.RI ( e , t , err ),
+where
+.I e
+is the
+.B Sexp
+representating that expression,
+.I t
+is the unparsed tail of string
+.IR s ,
+and
+.I err
+is a diagnostic string that is nil on success.
+On an error,
+.I e
+is nil,
+.I t
+is as before, and
+.I err
+contains the diagnostic.
+.TP
+.IB e .pack()
+Return an array of byte that represents
+.B Sexp
+.I e
+as an S-expression in canonical transport form.
+.TP
+.IB e .packedsize()
+Return the size in bytes of the canonical transport representation of
+.IR e .
+.TP
+.IB e .b64text()
+Return a string that contains the base-64 representation of the canonical representation of
+.IR e ,
+surrounded by braces.
+.TP
+.IB e .text()
+Return a string that represents
+.I e
+as an S-expression in advanced (`human-readable') transport form containing no newlines.
+The result of
+.B text
+can always be interpreted by
+.B Sexp.read
+and
+.BR Sexp.parse ,
+and furthermore
+.BI "Sexp.parse(" e ".text())"
+yields the same tree value as
+.I e
+(similarly for
+.BR Sexp.read ).
+.TP
+.BI unpack( a )
+Parse the first S-expression in array of byte
+.IR a ,
+and return a tuple
+.RI ( e , r , err ),
+where
+.I e
+is the
+.B Sexp
+representing the S-expression,
+.I r
+is a slice of
+.I a
+giving the portion of
+.I a
+after the S-expression, and
+.I err
+is nil on success.
+On error,
+.I e
+is nil,
+.I r
+is as before,
+and
+.I err
+contains a diagnostic string.
+The data in
+.I a
+is typically in canonical transport form, read from a file or network connection.
+.PP
+All input functions accept S-expression in either canonical or advanced form, or
+any legal mixture of forms.
+Expressions can cross line boundaries.
+For output in canonical form, use
+.BR pack ;
+for output in advanced form (similar to Lisp's S-expressions), use
+.BR text .
+.PP
+.B Sexp
+provides a further small collection of operations:
+.TP
+.IB e1 .eq( e2 )
+Return non-zero if expression
+.I e1
+and
+.I e2
+are identical (isomorphic in tree structure and atoms in corresponding positions in
+.I e1
+and
+.I e2
+equal);
+return 0 otherwise.
+.TP
+.IB e .copy()
+Return a new
+.B Sexp
+value equal to
+.IR e ,
+but sharing no storage with it.
+(In other words, it returns a copy of the
+whole tree
+.IR e ).
+.TP
+.IB e .islist()
+Return true iff
+.I e
+is a list
+(ie, a value of type
+.BR Sexp.List ).
+.PP
+Two operations provide a shorthand for fetching the value of an atom, returning nil if
+applied to a list:
+.TP
+.IB e .astext()
+Return the value of
+.I e
+as a
+.BR string ;
+binary data is assumed to be a string in
+.IR utf (6)
+representation.
+.TP
+.IB e .asdata()
+Return the value of
+.I e
+as an array of bytes.
+A
+.B String
+value will be converted to an array of bytes giving its
+.IR utf (6).
+.PP
+The remaining operations extract values from lists,
+and return nil if applied to an atom:
+.TP
+.IB e .els()
+Return the elements of list
+.IR e ;
+return nil if
+.I e
+is not a list.
+.TP
+.IB e .op()
+Return the first token of list
+.IR e ,
+if it is a string; return nil if it is not a string or
+.I e
+is not a list.
+The first token of a list often gives an operation name.
+.TP
+.IB e .args()
+Return a list containing the second and subsequent values in list
+.IR e ;
+useful when the first value is an operation name and the rest represent parameters
+(arguments) to that operation.
+.SH EXAMPLES
+The following S-expression is in advanced transport form:
+.IP
+.EX
+(snicker "abc" (#03# |YWJj|))
+.EE
+.PP
+It represents a list of three elements: the token
+.LR snicker ,
+the token
+.LR abc ,
+and a sub-list with two elements (a hexadecimal constant
+representing the byte
+.LR 03 ,
+and a base-64 constant
+.L YWjj
+that represents the bytes
+.LR abc ).
+.PP
+Here is another in advanced form with two sublists:
+.IP
+.EX
+(certificate
+     (issuer bob)
+     (subject "alice b"))
+.EE
+.PP
+Its equivalent in canonical form (as produced by
+.BR pack )
+is shown below:
+.IP
+.EX
+(11:certificate(6:issuer3:bob)(7:subject7:alice b))
+.EE
+.PP
+Nesting parentheses
+still mark the start and end of lists, but there is no other punctuation or white space, and
+the byte sequence representing each atom
+is preceded by a decimal count, so that binary values appear unencoded,
+and for instance the space
+in the last string is not a delimiter but part of the token.
+.SH SOURCE
+.B /appl/lib/sexprs.b
+.SH SEE ALSO
+.IR bufio (2),
+.IR xml (2),
+.IR sexprs (6)
+.PP
+R. Rivest, ``S-expressions'', Network Working Group Internet Draft,
+.L http://theory.lcs.mit.edu/~rivest/sexp.txt
+(4 May 1997),
+reproduced in
+.BR /lib/sexp .
--- /dev/null
+++ b/man/2/sh
@@ -1,0 +1,560 @@
+.TH SH 2
+.SH NAME
+Sh \- module interface to the shell
+.SH SYNOPSIS
+.EX
+.ps -1
+.vs -1
+include "sh.m";
+sh := load Sh Sh->PATH;
+Context, Listnode: import sh;
+
+system:           fn(drawctxt: ref Draw->Context, cmd: string): string;
+run:              fn(drawctxt: ref Draw->Context, argv: list of string): string;
+parse:            fn(s: string): (ref Cmd, string);
+cmd2string:       fn(c: ref Cmd): string;
+list2stringlist:  fn(nl: list of ref Listnode): list of string;
+stringlist2list:  fn(sl: list of string): list of ref Listnode;
+
+Context: adt {
+        new:            fn(drawcontext: ref Draw->Context): ref Context;
+        get:            fn(c: self ref Context,
+                           name: string): list of ref Listnode;
+        set:            fn(c: self ref Context,
+                           name: string,
+                           value: list of ref Listnode);
+        setlocal:       fn(c: self ref Context,
+                           name: string,
+                           value: list of ref Listnode);
+        envlist:        fn(c: self ref Context):
+			              list of (string, list of ref Listnode);
+        push, pop:      fn(c: self ref Context);
+        copy:           fn(c: self ref Context, copyenv: int): ref Context;
+        run:            fn(c: self ref Context,
+                           args: list of ref Listnode,
+                           last: int): string;
+        addmodule:      fn(c: self ref Context, name: string,
+                           mod: Shellbuiltin);
+        addbuiltin:     fn(c: self ref Context, name: string,
+                           mod: Shellbuiltin);
+        removebuiltin:  fn(c: self ref Context, name: string,
+                           mod: Shellbuiltin);
+        addsbuiltin:    fn(c: self ref Context, name: string,
+                           mod: Shellbuiltin);
+        removesbuiltin: fn(c: self ref Context, name: string,
+                           mod: Shellbuiltin);
+        fail:           fn(c: self ref Context, ename, msg: string);
+        options:        fn(c: self ref Context): int;
+        setoptions:     fn(c: self ref Context, flags, on: int): int;
+};
+
+Listnode: adt {
+        cmd:    ref Cmd;
+        word:   string;
+};
+
+Cmd: adt {
+        # private data
+};
+
+Shellbuiltin: module {
+        initbuiltin:    fn(ctxt: ref Context, sh: Sh): string;
+        whatis:         fn(ctxt: ref Sh->Context, sh: Sh,
+                           name: string, wtype: int): string;
+       runbuiltin:     fn(ctxt: ref Context, sh: Sh,
+                           cmd: list of ref Listnode,
+                           last: int): string;
+        runsbuiltin:    fn(ctxt: ref Context, sh: Sh,
+                           cmd: list of ref Listnode): list of ref Listnode;
+        getself:        fn(): Shellbuiltin;
+};
+
+.ps +1
+.vs +1
+.EE
+.SH DESCRIPTION
+.I Sh
+is a command-line interpreter and a scripting language;
+it also presents a module interface to allow Limbo
+modules to access its functionality at a lower level.
+The
+.B Sh
+module can be used in several different ways.
+At the simplest level, it can be run as a command-line
+program; for details of this, see
+.IR sh (1).
+The simplest access at the Limbo level is through
+the
+.B system
+function, which given a
+.I draw
+.B Context
+(see
+.IR draw-context (2))
+and a string
+executes the
+.I sh
+command contained in
+.I s
+and returns its result. It catches any exceptions raised by the command.
+Almost as simple is
+.BR run ,
+which runs
+.I argv
+as a command, taking the first word as the command to be
+executed (it can be a braced block) and giving the rest as arguments,
+catching any exceptions raised.
+.PP
+Although program arguments are passed to external programs
+as lists of strings, at the
+.B Sh
+module level, an argument list is held as a
+.BR "list of ref Listnode" .
+A
+.B Listnode
+holds either a simple string, or a braced block
+that has been parsed by the shell. Sometimes it can hold
+both; in this case the string and the block both represent
+the same thing.
+.B Parse
+converts from a string to a
+.B Cmd
+(a braced block). It returns a tuple
+.RI ( cmd ,\  error )
+where
+.I cmd
+holds the parsed block,
+and
+.I error
+is non-empty if an error has occurred doing so.
+.B Cmd2string
+performs the opposite conversion; it returns
+a string that when parsed will yield the same command
+block it was passed.
+The utility functions
+.B List2stringlist
+and
+.B stringlist2list
+convert from and to a
+.B list of ref Listnode
+to or from a
+.B list of string
+respectively.
+.PP
+A
+.B Context
+holds all the state information needed by a currently running
+.I sh
+process; this adt holds current values of environment variables
+and a list of currently loaded modules and builtin commands.
+It is specific to the process within which it was created.
+If it is desired to run
+.I sh
+commands in a newly spawned process, a new
+.B Context
+must be created, or a copy of an existing Context made (making
+sure to synchronise access until the copy has been made).
+.TP 10
+.BI Context.new( drawcontext )
+.B New
+creates a new context.
+.I Drawcontext
+represents the current graphics context
+within which
+.I sh
+commands will be run
+(see
+.IR draw-context (2)).
+.TP
+.IB ctxt .get(\fPname\fP)
+.B Get
+retrieves the value of environment variable
+.I name
+from
+.IR ctxt .
+It is retrieved from the innermost scope in which
+a value for
+.I name
+has been set.
+.TP
+.IB ctxt .set(\fPname\fP,\ \fPvalue\fP)
+.B Set
+sets the value of environment variable
+.I name
+in
+.IR ctxt
+to
+.IR value .
+It is set in the innermost scope in which a value
+for
+.I name
+has been set, or the outermost level if it has
+not been set.
+.TP
+.IB ctxt .setlocal(\fPname\fP,\ \fPvalue\fP)
+Similar to
+.B set()
+except that the value is set in the innermost scope
+that has been pushed.
+.TP
+.IB ctxt .envlist()
+.B Envlist
+retrieves the list of all the environment variables
+currently in scope, and their values.
+It returns a list of
+.RI ( name ,\  value )
+tuples, where
+.I name
+is the name of the variable and
+.I value
+is its value.
+.TP
+.IB ctxt .push()
+.B Push
+creates a new innermost environment variable scope.
+.TP
+.IB ctxt .pop()
+.B Pop
+discards the current innermost scope, losing the
+values of all variables that have been defined there.
+It is an error to
+.B pop
+a context that has not been pushed.
+Care must be taken to ensure that a
+.B push
+is always matched by a
+.BR pop.
+In particular, exceptions should be caught,
+the context popped, and the exception re-raised.
+.TP
+.IB ctxt .copy(\fPcopyenv\fP)
+The shell's
+.B Context
+is associated with a particular process;
+.B copy
+returns a copy of
+.I ctxt
+associated with the current process. If
+.I copyenv
+is non-zero, the whole environment will be copied - this
+should be set if the new process is to run asynchronously - i.e.
+if there is a chance that there might be two processes accessing the
+context in parallel. It is an error to copy a context if not
+within a new process.
+.TP
+.IB ctxt .run(\fPargs\fP,\ \fPlast\fP)
+.B Run
+executes a
+.I sh
+command.
+.I Last
+should be non-zero if this is the last time
+that
+.B run
+will be called, so
+.I sh
+does not have to spawn a new process in order
+to hide file redirection side-effects.
+.TP
+.IB ctxt .addmodule(\fPname\fP,\ \fPmod\fP)
+.B Addmodule
+adds the
+.B Shellbuiltin
+module
+.I mod
+to its list of builtin modules.
+The module will be initialised as described in
+``Builtin modules'', below.
+.TP
+.IB ctxt .addbuiltin(\fPname\fP,\ \fPmod\fP)
+.B Addbuiltin
+may be called by a module that has previously
+been loaded by
+.B addmodule
+or by the
+.B load
+.I sh
+command to add a new builtin command
+to the shell. Any subsequent invocation of
+.I name
+within
+.I ctxt
+will result in a call of
+.B runbuiltin()
+to
+.IR mod .
+Any attempt to redefine the command
+.RB `` builtin ''
+will be ignored.
+.TP
+.IB ctxt .removebuiltin(\fPname\fP,\ \fPmod\fP)
+.B Removebuiltin
+removes
+.I name
+from the list of builtin commands in
+.IR ctxt .
+If
+.I name
+had not previously been defined by
+.IR mod ,
+or had subsequently been replaced, then
+this function does nothing.
+.TP
+.IB ctxt .addsbuiltin(\fPname\fP,\ \fPmod\fP)
+.B Addsbuiltin
+may be called by a module that has previously
+been loaded by
+.B addmodule
+or by the
+.B load
+.I sh
+command to add a new builtin substitution operator
+to the shell.
+Any subsequent invocation of
+.BI ${ name }
+within
+.I ctxt
+will result in a call of
+.B runsbuiltin()
+to
+.IR mod .
+.TP
+.IB ctxt .removesbuiltin(\fPname\fP,\ \fPmod\fP)
+.B Removesbuiltin
+removes
+.I name
+from the list of builtin substitution operators in
+.IR ctxt .
+If
+.I name
+had not previously been defined by
+.IR mod ,
+or had subsequently been replaced, then
+this function does nothing.
+.TP
+.IB ctxt .fail(\fPename\fP,\ \fPmsg\fP)
+.B Fail
+prints
+.I msg
+to the standard error if message printing
+is currently enabled, and raises
+the exception
+.BI fail: ename\f1.\fP
+.TP
+.IB ctxt .options()
+.B Options
+returns a bitmask of the options currently enabled in
+.IR ctxt .
+The bits are defined by constants declared within
+.BR Context .
+They include:
+.RS
+.TP
+.IB ctxt .INTERACTIVE
+.I Sh
+is currently being run from an interactive command-line.
+.TP
+.IB ctxt .VERBOSE
+Message printing is currently enabled.
+.TP
+.IB ctxt .EXECPRINT
+Commands are printed to standard error
+as they are executed.
+.TP
+.IB ctxt .ERROREXIT
+An exception will be raised when the first
+simple command returns an error status.
+.PP
+Options are defined in the innermost scope
+of
+.I ctxt
+and will be lost when it is
+.BR pop ped.
+.RE
+.TP
+.IB ctxt .setoptions(\fPflags\fP,\ \fPon\fP)
+.B Setoptions
+sets the specified
+.I flags
+within
+.IR ctxt .
+.I Flags
+is a bitmask of options, as described in
+.BR options ,
+above. If
+.I on
+is non-zero, the specified bits will be set;
+otherwise they will be reset.
+.B Setoptions
+returns the previously set options bitmask.
+.SS Builtin modules
+.B Shellbuiltin
+specifies the interface to a loadable
+.I sh
+builtin module. Any Limbo module
+.I mod
+adhering to this
+interface may be loaded into the shell.
+.TP 10
+.IB mod ->initbuiltin(\fPctxt\fP,\ \fPsh\fP)
+.B Initbuiltin
+is called when
+.I sh
+loads
+.I mod
+either via the
+.B load
+command, or via the
+.B loadmodule()
+function.
+.I Ctxt
+is the context within which the builtin has been
+loaded, and
+.I sh
+is the
+.B Sh
+module itself. When
+.B initbuiltin
+is called,
+.I mod
+is expected to call
+.IB ctxt .addbuiltin
+and
+.IB ctxt .addsbuiltin
+to define any builtin commands and builtin substitution
+operators that it wants. If an error occurs on
+initialisation,
+.B initbuiltin
+should return a non-nil value; this will cause the load to fail.
+.TP
+.IB mod ->runbuiltin(\fPctxt\fP,\ \fPsh\fP,\ \fPcmd\fP,\ \fPlast\fP)
+.B Runbuiltin
+is invoked when
+.I sh
+executes a command that has previously been
+defined as a builtin command by
+.IR mod .
+.I Ctxt
+is the current execution context (which may not be
+the original context passed to
+.BR initbuiltin() ),
+.I sh
+is the running
+.B Sh
+module, and
+.I cmd
+is the command to be executed.
+.I Last
+is true if this is the last command to be executed
+in the current process; it can be passed to
+.IB ctxt .run()
+as appropriate.
+The name of the command can be found in
+.BR "(hd cmd).word" .
+.B Runbuiltin
+returns its exit status; by convention this
+is the exit status of the last command executed.
+A non-nil exit status is usually treated as false.
+By convention, if an invalid set of arguments are
+passed to a builtin command, a
+.B usage
+exception is raised by calling 
+.IB ctxt .fail
+with
+.B "usage"
+and an explanatory usage message as arguments.
+.TP
+.IB mod ->runsbuiltin(\fPctxt\fP,\ \fPsh\fP,\ \fPcmd\fP)
+Similar to
+.BR runbuiltin ,
+.B runsbuiltin
+is called when
+.I sh
+encounters a builtin substitution operator
+that has previously been defined by
+.IR mod .
+It returns the list of values that will be
+substituted in place of the operator.
+.TP
+.IB mod ->getself()
+.B Getself
+should return the
+.B Shellbuiltin
+module handle for
+.IR mod ,
+usually obtained by invoking
+.BR "load $self" .
+N.B. it is important that the value returned
+by
+.B getself
+is the same as that passed to
+.B addbuiltin
+or
+.BR addsbuiltin .
+As the Limbo
+.B load
+operator returns a different value each time,
+the value to be returned by
+.B getself()
+should be initialised once,
+during the call to 
+.BR initbuiltin() .
+.TP 10
+.IB mod ->whatis(\fPctxt\fP,\ \fPsh\fP,\ \fPname\fP,\ \fPwtype\fP)
+.B Whatis
+is called by the shell's
+.B whatis
+command to query the definition of a name.
+.I Wtype
+gives the type of name that is being asked about; it can be
+.B BUILTIN
+(conventional commands),
+.BR SBUILTIN
+(substitution builtins),
+or
+.BR OTHER
+(any other names that the module defines).
+Return
+.B nil
+to get the usual default behaviour. The
+.B std
+module, for example, uses this feature to
+display the definition of a shell function
+correctly.
+.SS Exceptions
+The exceptions used within
+.I sh
+are exactly the same as those used within Limbo,
+except that all exceptions generated by the
+shell are prefixed by the string
+.RB `` fail: '',
+and any exception caught with the prefix
+.B fail:
+has its first 5 characters removed before
+being made available to the
+.I sh
+script.
+This adheres to the convention defined by
+other shells within Inferno that a process
+that raises an exception with a
+.B fail:
+prefix is just returning a non-zero exit status,
+and should not be left in a Broken state.
+It also means that the number of bytes available
+for the exception string is reduced by 5
+(to 59). Care must therefore be taken to avoid
+generating an exception with a name that is too long;
+.I sh
+takes the pragmatic approach of truncating any
+exception string that is too long.
+.SH FILES
+.TP 10
+.BI /prog/ pid /wait
+The file used by the shell to wait for dead child processes.
+.SH SOURCE
+.B /appl/cmd/sh/sh.y
+.SH SEE ALSO
+.IR sh (1),
+.IR sh-std (1),
+.IR sh-expr (1),
+.IR sh-tk (1)
--- /dev/null
+++ b/man/2/smtp
@@ -1,0 +1,56 @@
+.TH SMTP 2
+.SH NAME
+smtp \- Simple Mail Transfer Protocol
+.SH SYNOPSIS
+.EX
+include "smtp.m";
+smtp := load Smtp Smtp->PATH;
+
+open:     fn(server: string): (int, string);
+sendmail: fn(fromwhom: string, 
+             towhom: list of string, 
+             cc: list of string,
+             msg: list of string): (int, string);
+close:    fn(): (int, string);
+.EE
+.SH DESCRIPTION
+.B Smtp
+provides an interface to the mail transport protocol SMTP.
+.PP
+.B Open
+opens a connection to the given SMTP
+.IR server .
+If
+.I server
+is nil,
+.B open
+uses the
+default mail server
+.BR $smtp ,
+which must be defined in
+.IR ndb (6).
+It returns -1 and an error message if the connection fails.
+.PP
+.B Sendmail
+sends mail to the SMTP server for subsequent delivery.
+The first argument names the sender, the list
+.I towhom
+names the
+recipients,
+.I cc
+is a list of CC's,
+and
+.I msg
+has the text of the message. The latter
+may simply be a list of one item containing the whole message, a list of lines of the message
+or any intermediate format. It returns -1 and an error message on failure.
+.PP
+.B Close
+closes the connection to the SMTP server.
+.SH SOURCE
+.B /appl/lib/smtp.b
+.SH SEE ALSO
+.IR sendmail (1),
+.IR acme (1)
+.SH BUGS
+Too much copying of strings is done, especially with large messages.
--- /dev/null
+++ b/man/2/spki
@@ -1,0 +1,601 @@
+.TH SPKI 2
+.SH NAME
+SPKI: Cert, Hash, Key, Name, Seqel, Signature, Subject, Toplev, Valid \- simple public key infrastructure
+.SH SYNOPSIS
+.EX
+include "bufio.m";
+include "sexprs.m";
+include "keyring.m";
+include "spki.m";
+spki := load SPKI SPKI->PATH;
+
+Hash: adt {
+    alg:    string;
+    hash:   array of byte;
+
+    sexp:   fn(h: self ref Hash): ref Sexprs->Sexp;
+    text:   fn(h: self ref Hash): string;
+    eq:     fn(h1: self ref Hash, h2: ref Hash): int;
+};
+
+Key: adt {
+    pk:     ref Keyring->PK;    # either pk/sk or hash might be nil
+    sk:     ref Keyring->SK;
+    nbits:  int;
+    halg:   string;             # basic signature hash algorithm
+    henc:   string;             # pre-signature encoding
+    hash:   list of ref Hash;
+
+    hashed:  fn(k: self ref Key, alg: string): array of byte;
+    hashexp: fn(k: self ref Key, alg: string): ref Hash;
+    public:  fn(k: self ref Key): ref Key;
+    sigalg:  fn(k: self ref Key): string;
+    text:    fn(k: self ref Key): string;
+    sexp:    fn(k: self ref Key): ref Sexprs->Sexp;
+    eq:      fn(k1: self ref Key, k2: ref Key): int;
+};
+
+Name: adt {
+    principal:  ref Key;
+    names:  list of string;
+
+    isprincipal: fn(n: self ref Name): int;
+    local:    fn(n: self ref Name): ref Name;
+    islocal:  fn(n: self ref Name): int;
+    isprefix: fn(n1: self ref Name, n2: ref Name): int;
+    text:     fn(n: self ref Name): string;
+    sexp:     fn(n: self ref Name): ref Sexprs->Sexp;
+    eq:       fn(n1: self ref Name, n2: ref Name): int;
+};
+
+Cert: adt {
+    e:        ref Sexprs->Sexp;   # S-expression, if originally parsed
+    issuer:   ref Name;
+    subject:  ref Subject;
+    valid:    ref Valid;
+    pick {
+    A or KH or O => # auth, keyholder or object
+        delegate:   int;
+        tag:        ref Sexprs->Sexp;
+    N =>    # name
+    }
+
+    text:     fn(c: self ref Cert): string;
+    sexp:     fn(c: self ref Cert): ref Sexprs->Sexp;
+};
+
+Subject: adt {
+    pick{
+    P =>
+        key:    ref Key;
+    N =>
+        name:   ref Name;
+    O =>
+        hash:   ref Hash;
+    KH =>
+        holder: ref Name;
+    T =>
+        k, n:   int;
+        subs:   cyclic list of ref Subject;
+    }
+
+    eq:     fn(s1: self ref Subject, s2: ref Subject): int;
+    principal:  fn(s: self ref Subject): ref Key;
+    text:   fn(s: self ref Subject): string;
+    sexp:   fn(s: self ref Subject): ref Sexprs->Sexp;
+};
+
+Signature: adt {
+    hash:   ref Hash;
+    key:    ref Key;    # find by hash if necessary
+    sa:     string;     # alg[-[encoding-]hash
+    sig:    list of (string, array of byte);
+
+    algs:   fn(s: self ref Signature): (string, string, string);
+    sexp:   fn(s: self ref Signature): ref Sexprs->Sexp;
+    text:   fn(s: self ref Signature): string;
+};
+
+Seqel: adt {
+    pick{
+    C =>
+        c: ref Cert;
+    K =>
+        k: ref Key;
+    O =>
+        op: string;
+        args: list of ref Sexprs->Sexp;
+    S =>
+        sig: ref Signature;
+    E =>
+        exp:    ref Sexprs->Sexp;
+    }
+
+    sexp:   fn(se: self ref Seqeql): ref Sexprs->Sexp;
+    text:   fn(se: self ref Seqel): string;
+};
+
+Valid: adt {
+    notbefore:  string;
+    notafter:   string;
+
+    intersect:  fn(a: self Valid, b: Valid): (int, Valid);
+    text:   fn(a: self Valid): string;
+    sexp:   fn(a: self Valid): ref Sexprs->Sexp;
+};
+
+Toplev: adt {
+    pick {
+    C =>
+        v:  ref Cert;
+    Sig =>
+        v:  ref Signature;
+    K =>
+        v:  ref Key;
+    Seq =>
+        v:  list of ref Seqel;
+    }
+
+    sexp:   fn(t: self ref Toplev): ref Sexprs->Sexp;
+    text:   fn(t: self ref Toplev): string;
+};
+
+init:   fn();
+date2epoch: fn(s: string): int; # YYYY-MM-DD_HH:MM:SS
+epoch2date: fn(t: int): string;
+time2secs:  fn(s: string): int; # HH:MM:SS
+secs2time:  fn(t: int): string;
+sigalgs:    fn(spec: string): (string, string, string);
+
+# parse structures
+parse:      fn(s: ref Sexprs->Sexp): (ref Toplev, string);
+parseseq:   fn(s: ref Sexprs->Sexp): list of ref Seqel;
+parsecert:  fn(s: ref Sexprs->Sexp): ref Cert;
+parsesig:   fn(s: ref Sexprs->Sexp): ref Signature;
+parsename:  fn(s: ref Sexprs->Sexp): ref Name;
+parsekey:   fn(s: ref Sexprs->Sexp): ref Key;
+parsehash:  fn(s: ref Sexprs->Sexp): ref Hash;
+parsecompound:  fn(s: ref Sexprs->Sexp): ref Name;
+parsevalid: fn(s: ref Sexprs->Sexp): ref Valid;
+
+# signature checking
+checksig:   fn(c: ref Cert, sig: ref Signature): string;
+sig2icert:  fn(sig: ref Signature, signer: string, exp: int): ref Keyring->Certificate;
+
+# signature making
+signcert:   fn(c: ref Cert, sigalg: string, key: ref Key):
+                 (ref Signature, string);
+signbytes:  fn(a: array of byte, sigalg: string, key: ref Key):
+                 (ref Signature, string);
+
+# tags
+maketag:    fn(e: ref Sexprs->Sexp): ref Sexprs->Sexp;
+tagintersect:  fn(t1: ref Sexprs->Sexp, t2: ref Sexprs->Sexp):
+                 ref Sexprs->Sexp;
+tagimplies: fn(t1: ref Sexprs->Sexp, t2: ref Sexprs->Sexp): int;
+
+# hash canonical s-expression
+hashbytes:  fn(a: array of byte, alg: string): array of byte;
+hashexp:    fn(e: ref Sexprs->Sexp, alg: string): array of byte;
+.EE
+.SH DESCRIPTION
+.B SPKI
+provides data types and operations to help build implementations of the Simple Public Key Infrastructure
+(SPKI).
+It provides types for hash values, public and private keys, local and extended names,
+principals and compound principles, certificates, validity periods, signatures, and proof sequences.
+It also provides operations on authorisation tags.
+Externally, SPKI represents all such things as particular forms of S-expression, internally represented using
+.B Sexprs->Sexp
+from
+.IR sexprs (2).
+.PP
+.B Init
+must be called before invoking any other operation of the module.
+.PP
+Most types defined here provide several common operations:
+.TP
+.IB t1 .eq( t2 )
+Return true iff values
+.I t1
+and
+.I t2
+are equal.
+.TP
+.IB t .sexp()
+Return an S-expression
+.I s
+representing the value of
+.IR t .
+Subsequently, the
+.B Sexp
+operation
+.IR s .pack()
+will yield an array of bytes containing the value
+.I t
+in SPKI's canonical S-expression form.
+.TP
+.IB t .text()
+Return a textual representation of the value
+.IR t ;
+it is often just the textual representation of the corresponding S-expression.
+.PP
+.B Hash
+is the internal representation of hash values,
+containing an
+algorithm name
+.B alg
+and then the
+.B hash
+itself as an array of bytes.
+SPKI entities such as the public key of a principal or a signed certificate are often represented by the hash
+values of their corresponding S-expressions, where the hash value is later used as a compact way to refer
+to the original entity.
+For example, a
+.B <principal>
+is either a
+.B <public-key>
+or a
+.BR <hash-of-key> ,
+where the latter refers to some instance of the former.
+Current hash algorithms are \f5"sha1"\fP and \f5"md5\fP.
+A
+.B Hash
+value can be created from an S-expression representing a SPKI
+.B <hash>
+element
+by
+.BR parsehash .
+It returns nil if the S-expression was ill-formed.
+.PP
+.B Key
+represents public and private keys,
+with an optional associated pre-hash encoding
+.BR henc ,
+the hash algorithm
+.B halg
+to be used when signing, and an optional list of
+currently known hashes of the public component of the key itself.
+SPKI identifies principals and public keys, thus each instance of a principal
+in the other data structures is represented by a
+.B Key
+giving the corresponding public key, or its hash, or both.
+Currently the public and private (secret) key values have types defined by
+.IR keyring-intro (2).
+A
+.B Key
+value can be created from an S-expression representing a SPKI
+.B <public-key>
+element by
+.BR parsekey .
+It returns nil if the S-expression was ill-formed.
+For a given
+.B Key
+.IR k :
+.TP
+.IB k .ishash()
+Returns true if
+.I k
+is just a hash of a key, with no public or private components.
+.TP
+.IB k .public()
+Returns the public key for
+.IR k ,
+which is simply
+.I k
+if it is already a public key, but if it
+is a private key, then a new key is returned
+that has only public components.
+.B Public
+returns a nil value if
+.I k
+is just a hash of a key value.
+.TP
+.IB k .sigalg()
+Returns the SPKI signature algorithm for the key.
+.TP
+.IB k .hashed( alg )
+Return an array of bytes giving the hash of the Key
+.I k
+using algorithm
+.IR alg .
+It returns nil if
+.IB k .ishash()
+is true, and
+.I k
+has no associated hash value for
+.IR alg .
+.TP
+.IB k .hashexp( alg )
+Similar to
+.BR hashed ,
+but returns a
+.B Hash
+value instead of the raw data.
+.PP
+.B Name
+represents both local and extended names, and simple principals consisting of just a key.
+The field
+.B principal
+gives the key that defines the name space in which the list of names is interpreted.
+For simple principles, the list of
+.B names
+is nil.
+A local name has exactly one name in the list.
+Two parsing functions convert to
+.B Name
+from S-expressions.
+.B Parsename
+parses a SPKI
+.B <name>
+element:
+.BI (name
+[
+.I principal
+]
+.I name
+\&...
+.BR ),
+where
+.I principal is either a
+.B <public-key>
+or a
+.B <hash>
+element.
+.B Parsecompound
+accepts either a
+.B <name>
+element as above, or a
+.B <public-key>
+or its
+.BR <hash> .
+Both functions return nil if the S-expression is ill-formed.
+.PP
+.B Subject
+represents the subjects of SPKI name and authorisation certificates.
+It has several variants in a
+.B pick
+adt, with suitable fields for each variant:
+.TP
+.B Subject.P
+A simple principal: a
+.BR key .
+.TP
+.B Subject.N
+A group of principals or a delayed binding to a principal: a
+.BR name .
+.TP
+.B Subject.O
+The
+.B hash
+of an object.
+.TP
+.B Subject.KH
+A keyholder certificate, that says something about a key's
+.B holder
+(represented by a
+.BR Name ).
+.TP
+.B Subject.T
+A
+.I threshold
+subject, used only in authorisation certificates.
+The
+.I n
+subsidiary subjects are listed in
+.BR subs ;
+of those, at least
+.I k
+must sign a request for it to be authorised.
+.TP
+.B Subject
+provides the common operations
+.BR eq ,
+.B sexp
+and
+.BR text ,
+and a further operation:
+.TP
+.IB s .principal()
+If
+.I s
+is a simple principal or a name, return the
+.B Key
+defining the principal, if known; return nil otherwise.
+.PP
+Subjects appear only as a subsidiary item in certificates and do not have a parsing function.
+.PP
+.B Cert
+represents SPKI certificates.
+There are four variants, represented by a pick adt:
+.B Cert.A
+(authorisation);
+.B Cert.KH
+(keyholder);
+.B Cert.O
+(object); and
+.B Cert.N
+(name).
+The following fields and operations are common to all variants:
+.TP
+.B e
+original S-expression (if created by
+.BR parsecert )
+to allow hashes and signatures to be computed on the SPKI canonical form of the certificate
+.TP
+.B issuer
+The simple principal (represented as a name) that issued an authorisation, keyholder or object certificate,
+or the
+.B <issuer-name>
+of a name certificate (allowing both local and extended names not just simple principals).
+.TP
+.B subject
+The
+.B Subject
+of the certificate.
+Name certificates may not have threshold subjects.
+.TP
+.B valid
+Optional restriction on the certificate's validity
+(see
+.B Valid
+for details).
+.PP
+Name certificates have only the fields above; the others have several more fields:
+.TP
+.B
+delegate
+True iff the certificate carries delegation rights (ie,
+.B (propagate)
+in the S-expression representation).
+.TP
+.B tag
+An S-expression that expresses the authority granted by the certificate.
+The expression
+.B "(tag (*))"
+means `all permissions'.
+.PP
+A
+.B Cert
+value
+can be created from an S-expression representing a SPKI
+.B <cert>
+element by
+.BR parsecert .
+It returns nil if the expression was ill-formed.
+.PP
+SPKI
+.B tag
+expressions, represented internally by
+.B Sexprs->Sexpr
+trees, form a partial order,
+including the pattern operations
+.BR (*) ,
+.BR "(* set " ...
+.BR ),
+.BR "(* prefix " ...
+.BR ),
+.BR "(* range " ...
+.BR ),
+and as an extension,
+.BR "(* suffix " ...
+.BR ).
+Given two tag expressions
+.I t1
+and
+.IR t2 ,
+.I tagintersect
+returns a tag expression representing
+.I t1
+∩
+.IR t2 ;
+.B tagimplies
+returns true iff tag
+.I t1
+implies tag
+.IR t2 :
+(\fIt1\fP∩\fIt2\fP)=\fIt2\fP.
+Both functions work correctly when
+.I t1
+and
+.I t2
+contain any legal combination of pattern operations.
+.PP
+SPKI structures are converted to a canonical form of S-expression to be hashed or signed
+(with or without hashing).
+.B Hashbytes
+returns an array of bytes containing the result of hashing array
+.I a
+using hash algorithm
+.I alg
+(either
+.B sha1
+or
+.BR md5 ).
+.B Hashexp
+returns an array of bytes containing the hash of the canonical form of expression
+.I e
+using hash algorithm
+.IR alg .
+.PP
+.B Signature
+associates
+.B hash ,
+the
+.B Hash
+value of something (eg, a public key) with the result of applying a public-key signature
+algorithm
+.B sa
+ to that hash value.
+The name of the algorithm has the form
+.IP
+.EX
+\fIalg\fP\fR[-[\fP\fIencoding\fP\fR-]\fP\fIhash\fP\fR]\fP
+.EE
+with up to three subcomponents (separated by dashes),
+where
+.I alg
+is a public key algorithm such as
+.B rsa
+or
+.BR dsa ,
+.I encoding
+is an optional encoding to apply to the value before signing,
+and
+.I hash
+is the secure hash algorithm to apply to the encoded value before signing.
+For example, the usual algorithms for RSA keys are
+.B rsa-pkcs1-sha1
+and
+.BR rsa-pkcs1-md5 .
+.PP
+Signatures are created by
+.BR signcert ,
+which signs a SPKI certificate represented by
+.I c
+with
+.I key
+using the signature algorithm
+.IR sigalg .
+.I Key
+must contain both public and secret (private) components.
+Any other binary data can be signed by
+.BR signbytes ,
+which signs arbitrary data represented by an array of bytes
+.IR a .
+Both functions apply any encoding and hash algorithms mentioned by
+.IR sigalg ,
+and return a tuple
+.BI ( sig , err ).
+On success,
+.I sig
+refers to a
+.B Signature
+value that can be converted to an S-expression using
+.IB sig .sexp()
+and
+.I err
+is nil.
+On an error,
+.I sig
+is nil and
+.I err
+contains a diagnostic.
+.PP
+A certificate's signature can be checked by
+.BR checksig .
+If
+.I sig
+is a valid signature for certificate
+.IR c ,
+.B checksig
+returns nil.
+If the signature is invalid,
+.I checksig
+returns a diagnostic.
+.SH SOURCE
+.B /appl/lib/spki.b
+.SH SEE ALSO
+.IR bufio (2),
+.IR sexprs (2),
+.IR spki-verifier (2)
--- /dev/null
+++ b/man/2/spki-verifier
@@ -1,0 +1,91 @@
+.TH SPKI-VERIFIER 2
+.SH NAME
+verifier: verify \- verify sequence of SPKI elements
+.SH SYNOPSIS
+.EX
+include "bufio.m";
+include "sexprs.m";
+include "spki.m";
+
+sexprs := load Sexprs Sexprs->PATH;
+Sexp: import sexprs;
+
+spki := load SPKI SPKI->PATH;
+Name, Seqel, Subject, Valid: import spki;
+
+verifier := load Verifier Verifier->PATH;
+
+Speaksfor: adt {
+   subject:   ref Subject;
+   name:      ref Name;
+   regarding: ref Sexp;
+   valid:     ref Valid;
+};
+
+init:   fn();
+verify: fn(seq: list of ref Seqel):
+          (ref Speaksfor, list of ref Seqel, string);
+.EE
+.SH DESCRIPTION
+.B Verifier
+checks SPKI proof sequences.
+This initial implementation provides a single basic operation.
+Further work will allow (via channels and processes) verification
+to detect and refresh expired credentials, to support `pull' authentication,
+for instance.
+.PP
+.B Init
+must be called before any other operation of the module.
+.PP
+A
+.B Speaksfor
+value represents a claim that a given
+.I subject
+entity
+speaks for (on behalf of) a given
+.I name
+regarding a set of statements, with validity optionally limited to a given period.
+That is, when during the agreed time,
+.I subject
+makes a statement that is in the agreed set, it is treated
+as if
+.I name
+had said it directly.
+The set of statements is defined by a SPKI `tag' expression, represented as
+an S-expression.
+In particular, the
+.B "(tag (*))"
+means that
+.I subject
+speaks for
+.I name
+about everything.
+A claim can be taken as true if supported by acceptable evidence,
+for instance a collection of signed certificates.
+.PP
+.B Verify
+does the actual verification of a sequence
+.I seq
+of SPKI certificates, signatures and operations
+that makes and supports a claim that an entity speaks for another.
+It returns a tuple
+.BI ( claim,\ badel,\ err ) .
+On success,
+.I claim
+refers to a
+.B Speaksfor
+value that summaries the statement verified by the sequence.
+On failure,
+.I claim
+is nil,
+.I badel
+is a list of sequence elements headed by the first element of
+.I seq
+that failed verification, and
+.I err
+is the reason it failed.
+.SH SOURCE
+.B /appl/lib/spki/verifier.b
+.SH SEE ALSO
+.IR sexprs (2),
+.IR spki (2)
--- /dev/null
+++ b/man/2/spree
@@ -1,0 +1,604 @@
+.TH SPREE 2
+.SH NAME
+Spree \- distributed interactive sessions.
+.SH SYNOPSIS
+.EX
+.ps -1
+.vs -1
+include "sys.m";
+include "draw.m";
+include "sets.m";
+include "spree.m";
+spree := load Spree Spree->PATH;
+Range, Object, Clique, Member: import spree;
+Set: import Sets;
+Archive: import Archives;
+
+Range: adt {
+    start:  int;
+    end:    int;
+};
+
+Object: adt {
+    transfer:           fn(o: self ref Object,
+            r: Range, dst: ref Object, i: int);
+    setvisibility:      fn(o: self ref Object,
+            visibility: Set);
+    setattrvisibility:  fn(o: self ref Object,
+            name: string, visibility: Set);
+    setattr:        fn(o: self ref Object,
+            name: string, val: string, vis: Set);
+    getattr:        fn(o: self ref Object, name: string): string;
+    delete:     fn(o: self ref Object);
+    deletechildren: fn(o: self ref Object, r: Range);
+
+    id:     int;
+    parentid:   int;
+    children:   array of ref Object;
+    objtype:    string;
+    visibility: Set;
+    # ...private data
+};
+
+Clique: adt {
+    new:       fn(parent: self ref Clique, archive: ref Archive,
+				owner: string): (int, string, string);
+    newobject: fn(clique: self ref Clique, parent: ref Object,
+            visibility: int, objtype: string): ref Object;
+    action:   fn(clique: self ref Clique, cmd: string,
+               objs: list of int, rest: string, whoto: int);
+    member:   fn(clique: self ref Clique, id: int): ref Member;
+    start:    fn(clique: self ref Clique);
+    breakmsg: fn(clique: self ref Clique, whoto: Sets->Set);
+    members:  fn(clique: self ref Clique): list of ref Member;
+    owner:    fn(clique: self ref Clique): string;
+    hangup:   fn(clique: self ref Clique);
+    notify:   fn(clique: self ref Clique, cliqueid: int, msg: string);
+
+    objects:  array of ref Object;
+    cliqueid: int;
+    # ...private data
+};
+
+Member: adt {
+    obj:    fn(m: self ref Member, id: int): ref Object;
+    del:    fn(m: self ref Member, suspend: int);
+
+    id:     int;
+    name:   string;
+    # ...private data
+};
+
+Engine: module {
+	init:			fn(srvmod: Spree, clique: ref Clique, argv: list of string): string;
+	command:	fn(member: ref Member, e: string): string;
+	join:			fn(member: ref Member , e: string, suspended: int): string;
+	leave:		fn(member: ref Member): int;
+	notify:		fn(fromid: int, s: string);
+	readfile:		fn(f: int, offset: big, count: int): array of byte;
+};
+
+Archives: module {
+	Archive: adt {
+		argv:		list of string;			# how to restart the session.
+		members:	array of string;			# members involved.
+		info:		list of (string, string);	# any other information.
+		objects:	array of ref Object;
+	};
+	init:			fn(mod: Spree);
+	write:		fn(clique: ref Clique, info: list of (string, string), file: string, members: Set): string;
+	read:			fn(file: string): (ref Archive, string);
+	readheader:	fn(file: string): (ref Archive, string);
+};
+
+rand:   fn(n: int): int;
+.ps +1
+.vs +1
+.EE
+.SH DESCRIPTION
+.I Spree
+provides a general server interface that allows sets of distributed
+clients,
+.IR cliques ,
+to interact in a controlled manner, with the
+interaction mediated
+by Limbo modules, known as
+.IR engines .
+Each engine decides on the rules
+of its particular clique; the engine interface is described
+at the end of this manual page, under
+``Module Interface''.
+.PP
+This manual page describes the
+interface as presented to an engine
+once it has been loaded by
+.IR spree .
+A loaded instance of an engine is responsible for a particular
+.IR clique ,
+in which one or more
+.I members
+participate. Messages sent by members
+are interpreted by the engine, which
+responds by making changes to the hierarchical
+.I object
+database held by the clique.
+Behind the scenes
+.I spree
+distributes updates to this database to members
+of the clique as appropriate (see
+.IR spree (4)
+for details).
+.SS "Objects and visibility"
+Objects hold a clique's visible state. An object
+has a unique integer
+.IR id ,
+which is an index into the array
+.IB clique .objects\fR;\fP
+it also holds a set of attribute-value pairs, a type, and
+zero or more child objects. Together, all the objects
+in the clique form a hierarchical tree, rooted at
+the
+.IR "root object"
+(id 0), which always exists.
+Each attribute and each object also has an associated
+.IR "visibility set" ,
+the set of member that sees updates to the attributes or the children
+of the object.  Each member has a unique id;
+in a visibility set (see
+.IR sets (2)),
+a member is ``visible'' if the set contains the member's id.
+.PP
+Note that the visibility set of an object does not alter the visibility
+of that object's attributes, but only that of its children (and of
+their children: in general an object is visible to a member if the
+intersection of all its ancestors' visibility sets contains that
+member).
+.PP
+Objects can be transferred inside the hierarchy from one parent to
+another. If an object is moved to a parent whose visibility conceals it
+from a member, then it will appear to that member to have been deleted;
+if it is later made visible, then it will be recreated for that
+member.
+A clique engine can almost always ignore this technicality,
+except for one thing: the identifier used by a particular member to
+identify an object is not necessarily the same as that used by the clique
+engine. Thus when an engine receives an object id in a member's
+message, it should convert it using the
+.IB member .obj()
+function.
+.SS \fBClique\fP
+The
+.B Clique
+type holds all the objects in a clique. It allows the
+creation of new objects, and provides a way of communicating
+with members directly.
+All data members of a
+.B Clique
+should be treated as read-only.
+.TP 10
+.IB clique .objects
+This array holds the objects in the clique. An object with
+identifier
+.I id
+is found at
+.IB clique .objects[ id ]\fR.\fP
+.TP
+.IB clique .new(\fIarchive\fP, \fIowner\fP)
+.B New
+creates a new clique.
+.I Archive
+is an archive of the game to be created;
+.IB archive \.argv
+should be non-nil; its first element should name
+the engine to be loaded (as a path relative to the
+engine module directory, and without the
+.B .dis
+extension).
+.TP
+.IB clique .newobject(\fIparent\fP,\ \fIvisibility\fP,\ \fIobjtype\fP)
+.B Newobject
+creates a new object at the end
+of
+.IR parent 's
+children;
+If
+.I parent
+is nil, the new object is created under the root object.
+The new object has visibility
+.IR visibility ,
+and type
+.IR objtype .
+An object's type cannot be changed once
+it has been created.
+.TP
+.IB clique .action(\fIcmd\fP,\ \fIobjs\fP,\ \fIrest\fP,\ \fIwhoto\fP)
+.B Action
+sends a message to some members without affecting
+the object hierarchy. It can be used to send transient
+events that have no meaning when stored statically
+(for example, network latency probes).
+The message is sent to the set of members given by
+.IR whoto .
+.I Objs
+is assumed to be a list of object ids, which are
+converted appropriately for each member
+receiving the message; the final
+message is a string built by concatenating
+.IR cmd ,
+the list of object ids, and
+.IR rest ,
+separated by spaces.
+.TP
+.IB clique .breakmsg(\fIwhoto\fP)
+Messages are usually sent to clients in an uninterrupted
+stream (as documented in
+.IR spree (4)),
+with a single read returning a potentially large
+set of messages.
+.B Breakmsg
+arranges that subsequent messages received by the members specified in
+.I whoto
+will see not be merged with messages sent prior to the call to
+.BR breakmsg .
+This is used to enable a new client module to be started
+without needing to pass it data received in the previous read.
+.TP
+.IB clique .member(\fIid\fP)
+.B Member
+yields the member corresponding to identifier
+.IR id ,
+or
+.B nil
+if there is none.
+.TP
+.IB clique .membernamed(\fIname\fP)
+.B Membernamed
+searches for a member of
+.I clique
+named
+.I name
+and returns it if it finds it, otherwise
+.BR nil .
+.TP
+.IB clique .members()
+.B Members
+returns a list of all the members of
+.IR clique ,
+including those that have been suspended.
+.TP 
+.IB clique .owner()
+.B Owner
+returns the name of the owner of the clique;
+i.e. the user that created it.
+.TP
+.IB clique .hangup()
+.B Hangup
+terminates a game and informs all the
+players of that fact.
+.TP
+.IB clique .notify(\fIcliqueid\fP, \fImsg\fP)
+.B Notify
+sends an informational message to another clique.
+The clique so referenced must be either the parent
+or a child of
+.IR clique .
+The message is not sent synchronously,
+and care should be taken not to send messages that
+can cause an indefinite recursion.
+.SS Member
+The
+.B Member
+type represents a member of a clique.
+.TP 10
+.IB member .id
+The member's identifier is an integer  unique across all current members
+of the clique,
+but ids of members that have left the clique will
+be reused.
+There may not be two members of the same name in the
+same clique.
+.TP
+.IB member .name
+.B Name
+holds the authenticated name of the member.
+This is necessarily unique over the members
+of a clique.
+.TP
+.IB member .obj(\fIid\fP)
+.B Obj
+converts from a member's external object
+identifier to the clique's local
+.B Object
+that it represents. It returns
+.B nil
+if there is no such object.
+.TP
+.IB member .del(\fIsuspend\fP)
+.B Del
+deletes
+.I member
+from the clique;
+no more requests from
+.I member
+will be received by the clique engine.
+If
+.I suspend
+is non-zero, if a member of the same name joins again
+it will be allocated the same object id, allowing a member
+to leave and join again without losing state.
+.SS \fBObject\fP
+The
+.B Object
+type is the basic unit of clique engine state.
+An object's children can be selectively concealed
+from members; it holds a set of
+.RI ( attribute ,\  value )
+pairs, each of which can be concealed likewise.
+Where an argument
+.IR r ,
+of
+.B Range
+type is used, it refers to a range of an object's
+children starting at index
+.IB r .start\fR,\fP
+and finishing at
+.IB r .end-1\fR.\fP
+All the data members of an
+.B Object
+should be treated as read-only.
+.TP 10
+.IB obj .setattr(\fIname\fP,\ \fIval\fP,\ \fIvis\fP)
+.B Setattr
+sets attribute
+.I name
+in
+.I obj
+to
+.IR val.
+If the attribute is being created for the
+first time, then it will be given visibility
+.IR vis .
+.I Name
+should be non-empty, and should not
+contain any space characters.
+Note that it is not possible for an attribute
+to refer directly to an object by its identifier;
+if this facility is needed, another identifying
+scheme should be used. This also applies
+to member identifiers, which will change
+if the clique is saved and loaded again.
+.TP
+.IB obj .getattr(\fIname\fP)
+.B Getattr
+yields the current value of the
+attribute
+.I name
+in
+.IR obj .
+If an attribute is not set, it yields
+.BR nil .
+.TP
+.IB obj .delete()
+.B Delete
+removes
+.I obj
+from the object
+hierarchy.
+.TP
+.IB obj .deletechildren(\fIr\fP)
+.B Deletechildren
+deletes children in range
+.I r
+from
+.IR obj .
+.TP
+.IB obj .transfer(\fIr\fP,\ \fIdst\fP,\ \fIi\fP)
+.B Transfer
+transfers the children in range
+.I r
+from
+.I obj
+to just before the object at index
+.I i
+in
+.IR dst .
+It is permissible for
+.I obj
+and
+.I dst
+to be the same object.
+.TP
+.IB obj .setvisibility(\fIvisibility\fP)
+.B Setvisibility
+allows the set of members
+given in
+.I visibility
+to see the children of
+.IR obj ,
+and denies access to all others.
+Members are notified of the change.
+.TP
+.IB obj .setattrvisibility(\fIname\fP,\ \fIvisibility\fP)
+.B Setattrvisibility
+allows the set of members
+given in
+.I visibility
+to see the value of
+.IR obj 's
+attribute
+.IR name ,
+and denies access to all others.
+Members are not notified of the change;
+if there is a need to communicate
+the fact of an attribute becoming invisible to
+members, it should be done by using another
+(visible) attribute to communicate the change.
+.SS "Archives"
+The
+.B Archives
+module provides a means of committing a clique
+to permanent storage and retrieving it later.
+It should first be initialised by calling
+.BR init ,
+passing the
+.B Spree
+module in
+.IR mod .
+.B Write
+writes
+.I clique
+to the file
+.IR file .
+.I Info
+gives a set of attributes and values associated with the clique;
+.I members
+gives the set of clique members for which visibility information
+will be archived; visibility information for other members is forgotten.
+.PP
+.B Read
+opens
+.I file
+and returns it as an
+.B Archive
+adt, say
+.IR a .
+.IB A .argv
+holds the command line arguments to the clique module
+(including the name of the module, its first element);
+.IB a .members
+gives the names of all archived members - the id
+of an archived member is given by its index in the array;
+.IB a .info
+gives the list of attributes an values as stored by
+.BR write ;
+.IB a .objects
+holds the clique objects.
+.B Readheader
+is just like
+.B read
+except that it parses the header only, so will return an
+.B Archive
+adt such that
+.IB a .objects
+is nil.
+.SS "Module Interface"
+An engine module,
+.IR mod ,
+must implement the
+following functions. Where a function returns a string,
+it is interpreted as an error response to the member
+responsible for the request; an empty string signifies
+no error.
+.TP
+.IB mod ->init(\fIsrvmod\fP, \fIclique\fP, \fIargv\fP)
+.B Init
+initialises the clique engine.
+.I Clique
+is the clique that the engine is controlling,
+and
+.I srvmod
+is the
+.B Spree
+module holding its associated data.
+An error response from this function
+causes the clique to be aborted.
+.I Argv
+gives a list of arguments to the engine, starting
+with its module name.
+.TP
+.IB mod ->join(\fImember\fP, \fIe\fP, \fIsuspended\fP)
+.I Member
+has made a request to join the clique;
+an error response causes the request to be
+refused, otherwise the member joins the
+clique.
+.I E
+is a message from the client about how it would like to
+join the clique (e.g.
+.BR join ,
+.BR watch ,
+etc).
+.I Suspended
+is non-zero if the member was previously suspended from the clique.
+.TP
+.IB mod ->leave(\fImember\fP)
+.I Member
+has left the clique.
+If
+.B leave
+returns zero, the member will not be deleted from the
+clique, but merely suspended until they should join again.
+.TP
+.IB mod ->command(\fImember\fP,\ \fIe\fP)
+.I Member
+has sent the command
+.IR e .
+The command usually follows
+the simple message conventions
+used in
+.IR spree (4),
+i.e. simple space-separated tokens.
+.TP
+.IB mod ->notify(\fIcliqueid\fP, \fIs\fP)
+A notification,
+.IR s ,
+has been posted to
+the current clique
+by the clique identified by
+.IR cliqueid .
+The posting clique is either a parent or a child of the current clique.
+.TP
+.IB mod .
+.SH EXAMPLE
+The following is a small, but working example
+of a clique engine that acts as a chat server
+(parsing error checking omitted, and white-space
+compressed to save paper):
+.PP
+.EX
+.ps -1
+.vs -1
+implement Cliquemodule;
+include "sys.m";
+    sys: Sys;
+include "draw.m";
+include "../spree.m";
+    spree: Spree;
+    Clique, Member: import spree;
+clique: ref Clique;
+clienttype(): string
+{
+    return "chat";
+}
+init(g: ref Clique, srvmod: Spree): string
+{
+    (sys, clique, spree) = (load Sys Sys->PATH, g, srvmod);
+    return nil;
+}
+join(nil: ref Member): string
+{
+    return nil;
+}
+leave(nil: ref Member)
+{
+}
+command(member: ref Member, cmd: string): string
+{
+    clique.action("say " + string member.id + " " + cmd, nil, nil, ~0);
+    return nil;
+}
+.ps +1
+.vs +1
+.EE
+.SH SOURCE
+.B /appl/cmd/cliques/spree.b
+.SH "SEE ALSO"
+.IR spree (4),
+.IR spree-objstore (2),
+.IR spree-cardlib (2),
+.IR spree-allow (2),
+.SH BUGS
+The reuse of object ids can lead to
+problems when objects are deleted and
+recreated on the server before clients become
+aware of the changes.
--- /dev/null
+++ b/man/2/spree-allow
@@ -1,0 +1,129 @@
+.TH SPREE-ALLOW 2
+.SH NAME
+Allow \- filter client actions
+.SH SYNOPSIS
+.EX
+include "spree.m";
+include "spree/allow.m";
+Clique, Member, Object: import Spree;
+allow := load Allow Allow->PATH;
+
+init:		fn(m: Spree, c: ref Clique);
+add:		fn(tag: int, member: ref Member, pattern: string);
+del:		fn(tag: int, member: ref Member);
+action:	fn(member: ref Member, cmd: string): (string, int, list of string);
+archive:	fn(archiveobj: ref Object);
+unarchive: fn(archiveobj: ref Object);
+.EE
+.SH DESCRIPTION
+A
+.IR spree (2)
+client can send arbitrary actions to a running
+engine.  The
+.B Allow
+module enables an engine to filter clients'
+actions, permitting only actions matching
+certain provided patterns to get through.
+.PP
+An action is conventionally formatted as
+a sequence of space-separated words.
+A
+.I pattern
+is a string consisting of a sequence of such words.
+For a pattern to match a client's action, each word in the pattern
+must match each word in the action.
+Most pattern words are not special: they must match
+literally. The exceptions are:
+.TP
+.B *
+An asterisk matches any single word.
+.TP
+.B "&"
+An ampersand matches any set of words.
+Any words in the pattern after this are ignored.
+.TP
+.B %d
+Matches a decimal integer.
+.TP
+.B %p
+Matches a valid player id (decimal integer).
+.TP
+.B %o
+Matches any valid object id (decimal integer).
+The corresponding word in the list returned by
+.B action
+will be changed to the local object id from
+the member's external representation.
+.PP
+.B Init
+must be called first with the spree module,
+.IR m ,
+and the current clique,
+.IR c ,
+to initialise the module.
+.PP
+.B Add
+adds the new
+.I pattern
+to the list of allowed actions;
+.I tag
+is an integer tag that the caller can later use to identify the
+action, and
+I member
+is the clique member that is allowed to perform the action
+(if nil, then any member will be allowed to perform the action).
+.B Del
+deletes patterns tagged with
+.I tag
+from the list of allowed actions.
+If
+.I member
+is non-nil, then only patterns specific to
+that member will be deleted.
+.PP
+.B Action
+matches
+.IR
+.IR cmd ,
+an action performed by
+.IR member ,
+against the list of all the allowed patterns.
+It returns a tuple, say
+.RI ( err\fR,\ \fItag\fR,\  \fItoks\fR).
+If
+.I cmd
+does not match any pattern, then
+.I err
+will be non-nil and holds a string describing the
+nature of the failure.
+If a match is made, then
+.I tag
+holds the matched pattern's tag, as passed to
+.BR add ,
+and
+.I toks
+holds the list of words in the action, with
+object ids matched by
+.B %o
+changed to their local representation using
+.IB member .obj\fR.
+.PP
+.B Archive
+stores all the
+.I allow
+module's internal state as attributes on
+.I archiveobj
+(for card game engines, this is usually the
+object returned from
+.B cardlib->archive
+(see
+.IR cardlib (2))).
+.B Unarchive
+reverses this, restoring the module's internal state from
+.IR archiveobj .
+.SH SOURCE
+/appl/spree/lib/allow.b
+.SH "SEE ALSO"
+.IR spree (2) ,
+.IR spree-cardlib (2) ,
+.IR spree-objstore (2)
--- /dev/null
+++ b/man/2/spree-cardlib
@@ -1,0 +1,628 @@
+.TH SPREE-CARDLIB 2
+.SH NAME
+Cardlib \- support for card games in Spree engines.
+.SH SYNOPSIS
+.EX
+include "sys.m";
+include "draw.m";
+include "sets.m";
+include "spree.m";
+include "spree/cardlib.m";
+
+Object: import Spree;
+cardlib := load Cardlib Cardlib->PATH;
+
+init:			fn(spree: Spree, clique: ref Clique, archived: int);
+selection:	fn(stack: ref Object): ref Selection;
+
+makecard:	fn(deck: ref Object, c: Card, rear: string): ref Object;
+makecards:	fn(stack: ref Object, r: Range, rear: string);
+getcard:		fn(card: ref Object): Card;
+getcards:		fn(stack: ref Object): array of Card;
+setface:		fn(card: ref Object, face: int);
+flip:			fn(stack: ref Object);
+shuffle:		fn(stack: ref Object);
+discard:		fn(stk, pile: ref Object, facedown: int);
+deal:			fn(stack: ref Object, n: int, stacks: array of ref Object, first: int);
+sort:			fn(stack: ref Object, rank, suitrank: array of int);
+
+addlayframe:	fn(name: string, parent: string, layout: ref Layout, packopts: int, facing: int);
+addlayobj:	fn(name: string, parent: string, layout: ref Layout, packopts: int, obj: ref Object);
+dellay:		fn(name: string, layout: ref Layout);
+maketable:	fn(parent: string);
+
+newstack:		fn(parent: ref Object, p: ref Member, spec: Stackspec): ref Object;
+
+archive:		fn(): ref Object;
+unarchive:	fn(): ref Object;
+setarchivename: fn(o: ref Object, name: string);
+archivearray:	fn(a: array of ref Object, name: string);
+getarchiveobj:	fn(name: string): ref Object;
+getarchivearray: fn(name: string): array of ref Object;
+
+nmembers:	fn(): int;
+
+Layout: adt {
+	lay:	ref Object;
+};
+
+Stackspec: adt {
+	style:	string;
+	maxcards:	int;
+	title:		string;
+	conceal:	int;
+};
+
+Card: adt {
+	suit:		int;
+	number:	int;
+	face:		int;
+};
+
+# a member currently playing
+Cmember: adt {
+	ord:		int;
+	id:		int;
+	p:		ref Member;
+	obj:		ref Object;
+	layout:	ref Layout;
+	sel:		ref Selection;
+
+	join:		fn(p: ref Member, ord: int): ref Cmember;
+	index:	fn(ord: int): ref Cmember;
+	find:		fn(p: ref Member): ref Cmember;
+	findid:	fn(id: int): ref Cmember;
+	leave:	fn(cp: self ref Cmember);
+	next:		fn(cp: self ref Cmember, fwd: int): ref Cmember;
+	prev:		fn(cp: self ref Cmember, fwd: int): ref Cmember;
+};
+
+Selection: adt {
+	stack:	ref Object;
+	ownerid:	int;
+	isrange:	int;
+	r:		Range;
+	idxl:		list of int;
+
+	set:		fn(sel: self ref Selection, stack: ref Object);
+	setexcl:	fn(sel: self ref Selection, stack: ref Object): int;
+	setrange:	fn(sel: self ref Selection, r: Range);
+	addindex:	fn(sel: self ref Selection, i: int);
+	delindex:	fn(sel: self ref Selection, i: int);
+	isempty:	fn(sel: self ref Selection): int;
+	isset:		fn(sel: self ref Selection, index: int): int;
+	transfer:	fn(sel: self ref Selection, dst: ref Object, index: int);
+	owner:	fn(sel: self ref Selection): ref Cmember;
+};
+.EE
+.SH DESCRIPTION
+.I Cardlib
+provides facilities to help in the implementation
+of
+.I spree (2)
+engines that implement the
+.IR spree-cards (4)
+interface.
+Facilities include the layout of clients' cards,
+support for card selections, and card manipulation.
+.PP
+.B Init
+must be called first to initialise the
+.I Cardlib
+module, giving it the
+.I spree
+module and the current clique.
+.I Archived
+should be non-zero if the card game is being restored from
+an archive.
+.SS Cards
+The value of a playing card is represented by the
+.B Card
+adt, having attributes
+.IR suit ,
+.IR number ,
+and
+.IR face .
+.I Suit
+ranges from 0 to 3 inclusive, representing clubs,
+diamonds, hearts and spades respectively;
+.I number
+ranges from 0 to 12 inclusive for the standard cards,
+with ace low and king high - a joker is represented
+by a number greater than 12;
+.I face
+represents whether the card is face up or face down
+(0 is face down).
+.PP
+A actual card is represented by an object in the object hierarchy
+of type
+.BR card ,
+with attributes
+.BR number ,
+.BR face ,
+and
+.BR rear .
+.B Number
+is the suit/number of the card (held as
+.IR n ,
+where
+.IR n %4
+gives the suit, and
+.IR n /4
+the rank).
+.B Face
+is as held in the
+.B Card
+adt, and
+.B rear
+is a number that represents the pattern on the
+back of the card (numbered from 0 upwards).
+Conventionally the
+.B number
+attribute is made invisible to all players when
+the
+.B face
+attribute is set to zero.
+.PP
+.B Makecard
+creates a new card of value
+.IR c ,
+placing the new card object at the end of
+.IR deck ,
+and setting the
+.B rear
+attribute to
+.I rear
+if it is non-nil.
+.B Makecards
+makes a set of cards, all face down,
+in all four suits, having numbers within the range
+.IR r .
+.PP
+.B Getcard
+gets the value representation of a card from object
+.IR card ;
+.B getcards
+gets the values of all the card objects within
+.IR stack .
+.B Setface
+sets of
+.I card
+to
+.IR face ;
+the visibility of the card's number is changed appropriately.
+.PP
+The following few routines operate on stacks of cards: objects
+which contain only card objects:
+.B flip
+reverses a stack of cards, reversing their faces as it does so;
+.B shuffle
+shuffles a stack of cards, and
+.B sort
+sorts a stack of cards by suit and then number, according to
+.I rank
+and
+.IR suitrank .
+.I Rank
+and
+.I suitrank
+are permutations mapping number/suit to sort precedence (0 low).
+If either of these are nil, then a default ranking scheme is chosen
+(two low, ace high for number).
+.B Discard
+moves all the cards in
+.I stk
+onto
+.IR pile ,
+turning them face down if
+.I facedown
+is non-zero.
+.B Deal
+deals out all the cards in
+.I stack
+as evenly as possible amongst
+.IR stacks ,
+dealing to
+.IB stacks [ first ]
+first.
+.SS Members and card selection
+.I Cardlib
+keeps a record of the current players of the game;
+a player is represented by a
+.B Cmember
+adt; the players are assumed to sit in a circle,
+numbered from 0 updwards;
+.B nmembers
+gives the number of current players.
+Each player has a unique integer id, and an
+associated selection
+and card layout.
+.TP 10
+.IB m .join(\fIm\fP,\ \fIord\fP)
+Join a new player to the game;
+.I m
+is the clique member that's joining, and
+.I ord
+is where to slot the player in the circle of existing players.
+If
+.I ord
+is -1, the player will be added at the end.
+.TP
+.IB m .leave()
+Remove
+.I m
+from the list of current players.
+.TP
+.IB m .index(\fIord\fP)
+.B Index
+returns the
+.IR ord th
+player around the table.
+.TP
+.IB m .find(\fIm\fP)
+Find the
+.B Cmember
+corresponding to member
+.IR m .
+.TP
+.IB m .findid(\fIid\fP)
+Find the
+.B Cmember
+with identifier
+.IR id ,
+and return it.
+.IB m .next(\fIfwd\fP)
+.B Next
+returns the next player around the table
+from
+.IR m .
+If
+.I fwd
+is non-zero, it counts upwards, otherwise it counts
+downwards.
+.IB m .prev(\fIfwd\fP)
+.B Prev
+is the opposite of
+.BR next .
+If
+.I fwd
+is non-zero, it counts downwards, otherwise it
+counts upwards.
+.SS Selection
+Each
+.B Cmember
+.I m
+has an associated selection,
+.IB m .sel\fR,
+which consists of a selection
+of some cards from a single stack of cards.
+A selection can consist of either a range of cards within
+a stack, or an arbitrary set of cards within a stack.
+A stack can only be the subject of one selection; the
+member that has that selection is known as its
+owner.
+.TP 10
+.IB sel .set(\fIstack\fP)
+.B Set
+makes
+.I stack
+(an object containing only card objects)
+the subject of
+.IR sel 's
+selection. If
+.I stack
+is nil, the selection is cleared.
+.TP
+.IB sel .setexcl(\fIstack\fP)
+.B Setexcl
+is the same as
+.B set
+except that it will fail if the stack is owned
+by a different player. It returns 0 if it fails,
+otherwise non-zero.
+.TP
+.IB sel .setrange(\fIr\fP)
+.B Setrange
+sets the selection
+.I sel
+to be a range of cards within its stack.
+If the selection had been of distinct cards (set using
+.BR addindex ),
+it is first cleared.
+.TP
+.IB sel .addindex(\fIi\fP)
+.B Addindex
+adds the card at index
+.I i
+to the selection
+.IR sel .
+If a range had previously been selected,
+it is first cleared.
+.TP
+.IB sel .delindex(\fIi\fP)
+.B Delindex
+deletes the card at index
+.I i
+from the selection.
+If the selection was previously a range,
+this is a no-op.
+.TP
+.IB sel .isempty()
+.B Isempty
+returns non-zero if
+.I sel
+holds an empty selection.
+.TP
+.IB sel .isset(\fIindex\fP)
+.B Isset
+returns non-zero if the card at index
+.I index
+is contained within the selection
+.IR sel .
+.TP
+.IB sel .transfer(\fIdst\fP,\ \fIindex\fP)
+.B Transfer
+moves all the cards in the selection
+.I sel
+to just before
+.I index
+within the stack
+.IR dst .
+.IB sel .owner()
+.B Owner
+returns the
+.B Cmember
+that owns the selection
+.IR sel .
+.SS Layout
+Creating a stack of cards does not specify how it is to be displayed
+to members of the game. Each member has a
+.I layout
+object which defines which objects are to be displayed to
+that member, and how they are to be laid out.
+Any member must see at most one layout object
+(it is conventional to make a layout object visible only to
+its owner).
+Objects are laid out using tk-like
+.IR pack (9)
+semantics:
+.I frames
+pack together display objects or other frames.
+A display object can lay out anything the card client knows
+how to display (see ``Display Objects'', below).
+.PP
+.B Addlayframe
+adds a new frame named
+.I name
+within
+a layout frame named
+.IR parent ,
+specific to
+.IR layout .
+If
+.I parent
+is nil, the frame is added to the root of the hierarchy.
+If
+.I layout
+is nil, a frame is added to
+.I parent
+for each member that has a layout frame of that name.
+.I Packopts
+specifies how the frame is to be packed within its parent:
+it is a bitmask, specifying the side of the cavity against which it is to be packed,
+the place it is to be anchored should the cavity be bigger than its requested size,
+how to fill its cavity, whether to expand its requested size to fill extra available space.
+See
+.IR pack (9)
+for details of the packing algorithm.
+The packing direction is specified with one of
+.BR dTOP ,
+.BR dLEFT ,
+.BR dBOTTOM
+or
+.BR dRIGHT .
+The anchor direction is specified with one of
+.BR aCENTRE ,
+.BR aUPPERCENTRE ,
+.BR aUPPERLEFT ,
+.BR aCENTRELEFT ,
+.BR aLOWERLEFT ,
+.BR aLOWERCENTRE ,
+.BR aLOWERRIGHT ,
+.BR aCENTRERIGHT ,
+or
+.BR aUPPERRIGHT .
+.B FILLX
+and
+.B FILLY
+specify how to fill unused space in its cavity
+(not mutually exclusive),
+and
+.B EXPAND
+requests unused space.
+.I Facing
+influences direction that objects are packed in
+underneath the frame. It should be one of the
+pack direction constants specified above
+(e.g.
+.BR dTOP ).
+For instance, if
+.B dRIGHT
+is specified, then all objects packed underneath
+have their attributes modified 90° clockwise,
+as if the player in question was sitting on the
+left of the table, looking right.
+This feature means that it is possible
+to set up a ``table'' in which layout objects
+can be added to all players at the same time, but
+which nonetheless looks different to each player
+around the table.
+.PP
+.B Maketable
+creates such a 	``table'' for between 0 and 4 players.
+It creates a frame for each player, named
+.BI p n\fR,
+where
+.I n
+is the ordinal number of the player around the table;
+and an inner space, named
+.BR public .
+The
+.I parent
+argument to
+.B maketable
+gives the frame within which the table is to be created.
+.PP
+.B Addlayobj
+adds a new display object 
+.I obj to the layout hierarchy.
+.IR Name ,
+.IR parent ,
+.IR layout ,
+and
+.I packopts
+are the same as for
+.B addlayframe
+except that
+if it is a stack object, then
+.I packopts also specifies the
+orientation of the stack, with one of the constants
+.BR oRIGHT ,
+.BR oUP ,
+.BR oLEFT ,
+or
+.BR oDOWN ,
+giving the direction in which cards are laid out
+within the stack.
+.PP
+.B Dellay
+deletes the object named
+.I name
+from the layout hierarchy. If
+.I layout
+is nil, it is deleted from all layouts,
+otherwise from
+.I layout
+only.
+.SS "Display Objects"
+Currently, two kinds of objects can be displayed: stacks and
+widgets. A stack has object type
+.BR stack ,
+and contains only cards objects.
+Attributes on the stack object define its appearance:
+.B maxcards
+gives the default size of the stack;
+.B style
+gives the style of stack layout,
+currently one of
+.B pile
+(all the cards piled directly on top of one another),
+or
+.B display
+(cards are spread out in the direction specified by
+the orientation given in the packing options, see
+Layout, above);
+.B title
+gives a title to display with the stack.
+.PP
+.B Newstack
+creates a new stack according to the specifications in
+.IR spec ,
+where
+.I spec
+is an adt that holds
+.BR style ,
+.BR maxcards ,
+and
+.BR title ,
+as described above.
+If
+.IB spec .conceal
+is non-zero, the contents of the new stack will be made
+invisible to all (except
+.IR owner ,
+if
+.I owner
+is non-nil).
+.PP
+Widgets are created by making an object
+of type
+.RB `` widget
+.IR type '',
+where
+.I type
+is one of
+.BR button ,
+.BR entry , or
+.BR menu .
+The
+.B text
+attribute controls the text that is displayed in the widget;
+.B command
+gives the text that will be sent to the engine
+when the widget is activated,
+and
+.B width
+specifies the widget of the widget, in multiples of the width
+of the ``0'' character.
+.PP
+Entries can be made in a menu widget
+by creating new objects of type
+.B menuentry
+inside a menu object. The
+.B text
+and
+.B command
+attributes have the usual meaning here.
+.SS Archival
+Engines that use
+.I cardlib
+should not use
+.IR spree-objstore (2)
+to archive their objects:
+.I cardlib
+provides an interface to do this,
+and also knows how to archive and unarchive its own
+internal state.
+.PP
+.B Archive
+commits all the internal state of
+.I cardlib
+to the object hierarchy, prior to archival.
+It returns an ``archive'' object
+that can be used as a convenient place to put
+attributes that need archiving but are not associated with
+any particular object.
+.B Setarchivename
+associates
+.I name
+with the object
+.I o
+such that it can be retrieved when
+unarchiving by calling
+.B getarchiveobj
+with the same name.
+Similarly
+.B Archivearray
+associates a name with each object in the array
+.I a
+such that the array can be retrieved when unarchiving
+by calling
+.B getarchivearray
+with the same name.
+.I Name
+should not end in a decimal digit.
+.B Unarchive
+unarchives
+.IR cardlib 's
+internal state. It returns the same archive object
+that was returned by
+.BR archive .
+.SH SOURCE
+.B /appl/spree/lib/cardlib.b
+.SH "SEE ALSO"
+.IR spree (2),
+.IR spree-allow (2),
+.IR spree-objstore (2)
+.SH BUGS
+This interface is not complete and is liable to change.
--- /dev/null
+++ b/man/2/spree-gather
@@ -1,0 +1,105 @@
+.TH SPREE-GATHER 2
+.SH NAME
+Gatherengine \- module interface for pre-assembled groups.
+.SH SYNOPSIS
+.EX
+implement Gatherengine;
+
+include "spree.m";
+include "spree/gather.m";
+Clique, Member: import Spree;
+
+init:			fn(m: Spree, c: ref Clique, argv: list of string, archived: int): string;
+clienttype:	fn(): string;
+maxmembers:	fn(): int;
+propose:		fn(members: array of string): string;
+start:			fn(members: array of ref Member, archived: int);
+command:	fn(member: ref Member, c: string): string;
+archive:		fn();
+readfile:		fn(f: int, offset: big, n: int): array of byte;
+.EE
+.SH DESCRIPTION
+When implementing a
+.IR spree (2)
+engine, it is common to have the requirement that a certain number
+of members are grouped together before actually starting the
+engine.
+.I Spree
+provides no support for this directly; instead the
+.I gather
+module acts as an intermediate layer: engines that wish
+this functionality should implement the
+.B Gatherengine
+interface.
+The
+.I gather
+module also provides facilities for the automatic suspension and resumption
+of players, clique archival, and members that watch but do not
+participate.
+.PP
+.B Init
+is called first, with
+.IR m ,
+the
+.I spree
+module, and
+.IR c ,
+the new clique.
+.I Argv
+gives a list of arguments to the engine (the first being
+the name of the engine itself);
+.I archived
+is non-zero if the engine has been restored from an archive.
+If
+.B init
+returns a non-nil string, it is taken to describe an error, and the
+engine will be discarded.
+.B Maxmembers
+should return the maximum number of members that the engine
+can accept;
+.B clienttype
+should return the kind of client that is expected by the
+engine (e.g.
+.B cards
+for a card game engine).
+.PP
+.B Propose
+proposes that a clique consisting of
+.I members
+(the names of the proposed members)
+be started. It returns an error string: if this is non-nil, then
+the clique is not started, otherwise the proposed
+members are accepted into the clique,
+and
+.B start
+is called, where
+.I members
+is the array of actual members (corresponding to the
+members passed to
+.BR propose ),
+and
+.I archived
+is non-zero if the clique is being restored from an archive
+(same as passed to
+.BR init ).
+.PP
+Once a clique has been successfully started,
+.B command
+is called when a member sends a command to the
+engine;
+.I member
+has sent the command,
+and
+.I c
+is the command itself.
+.B Command
+should return a non-nil error string if the command fails.
+.PP
+When a clique is being archived,
+.B archive
+will be called to request the engine to store all its
+internal state into the object hierarchy (this is the moment,
+for instance, to call
+.BR cardlib->archive ).
+.SH "SEE ALSO"
+.IR spree (2) ,
--- /dev/null
+++ b/man/2/spree-objstore
@@ -1,0 +1,63 @@
+.TH SPREE-OBJSTORE 2
+.SH NAME
+Objstore \- support for object archiving in Spree engines.
+.SH SYNOPSIS
+.EX
+include "spree.m";
+include "spree/objstore.m";
+Object: import Spree;
+objstore := load Objstore Objstore->PATH;
+
+init:       fn(m: Spree, c: ref Clique);
+setname:    fn(o: ref Object, name: string);
+unarchive:  fn();
+get:        fn(name: string): ref Object;
+.EE
+.SH DESCRIPTION
+The
+.IR spree (2)
+module provides an
+.B Archives
+module to allow storage of an engine's object hierarchy to a persistent
+medium. This does not store the state internal to an engine that
+is not reflected in the object hierarchy, e.g. global variables.
+In particular, if an engine holds a variable referring to an object,
+it is not able to reset that variable correctly when the archive is
+later restored.
+The
+.B Objstore
+module provides some support for the naming of objects
+before archival, and for their retrieval by name, in order to enable this.
+.PP
+.B Init
+must be called first with the spree module,
+.IR m ,
+and the current clique,
+.IR c .
+When a module is archiving itself, it should name each object
+that it wishes to retrieve, using
+.BR setname .
+It is permissible for a single object to be given multiple
+names: the object will later be retrievable under any of its given
+names.
+.PP
+When restoring from an archive, a module should call
+.BR unarchive ,
+which scans through the object hierarchy looking
+for named objects and storing their names.
+After this,
+.BI get( name )
+will return the object,
+.IR o ,
+that was named
+.IR name
+by a previous
+.BI setname( o ,\  name )\fR,
+or
+.B nil
+if there was none such.
+.PP
+The names for an object are stored in the attribute ``§'',
+which is cleared when
+.B unarchive
+is called.
--- /dev/null
+++ b/man/2/srv
@@ -1,0 +1,66 @@
+.TH SRV 2
+.SH NAME
+srv \- network name and address translation when hosted
+.SH SYNOPSIS
+.EX
+include "srv.m"
+srv := load Srv Srv->PATH;
+Srv: module
+{
+  init:  fn();
+  iph2a: fn(host: string): list of string;
+  ipa2h: fn(addr: string): list of string;
+  ipn2p: fn(protocol, service: string): string;
+};
+.EE
+.SH DESCRIPTION
+.B Srv
+provides access to the host operating system's
+name and address translation when Inferno is running hosted.
+The module's implementation is usually built-in to
+.IR emu (1),
+and then only on some platforms.
+It uses the Internet name resolution services of the host operating system
+(eg,
+.B gethostbyname
+on Unix systems).
+Its services are normally only used internally by
+.IR cs (8)
+and
+.IR dns (8),
+and even they give priority to data in
+.IR services (6)
+and
+.IR dns (6)
+if available.
+Other Inferno applications normally give network addresses to the functions of
+.IR dial (2),
+and they are then translated automatically, using the services of
+.IR cs (8).
+.PP
+.B Init
+must be called before any other function is used.
+.PP
+Given a host name,
+.B iph2a
+returns a list of its Internet addresses (if any).
+Given an Internet address,
+.B ipa2h
+returns a list of host names (if any) that have that address.
+The results are only as accurate as the host system's name service.
+.PP
+.B Ipn2p
+returns the port number (as a string) for the given
+.I service
+when accessed using a particular protocol
+.I protocol
+(typically
+.B tcp
+or
+.BR udp ).
+.SH SOURCE
+.B /emu/port/srv.c
+.SH SEE ALSO
+.IR sys-file2chan (2),
+.IR cs (8),
+.IR dns (8)
--- /dev/null
+++ b/man/2/string
@@ -1,0 +1,200 @@
+.TH STRING 2
+.SH NAME
+string: append, drop, in, prefix, quoted, splitl, splitr, splitstrl,
+splitstrr, take, tobig, toint, toreal, tolower, toupper, unquoted \- string operations
+.SH SYNOPSIS
+.EX
+include "string.m";
+str := load String String->PATH;
+
+append:    fn(s: string, l: list of string): list of string;
+drop:      fn(s, cl: string): string;
+in:        fn(c: int, cl: string): int;
+prefix:    fn(pre, s: string): int;
+splitl:    fn(s, cl: string): (string, string);
+splitr:    fn(s, cl: string): (string, string);
+splitstrl: fn(s, t: string): (string, string);
+splitstrr: fn(s, t: string): (string, string);
+take:      fn(s, cl: string): string;
+tobig:     fn(s: string, base: int): (big, string);
+toint:     fn(s: string, base: int): (int, string);
+toreal:    fn(s: string, base: int): (real, string);
+tolower:   fn(s: string): string;
+toupper:   fn(s: string): string;
+quoted:    fn(args: list of string): string;
+unquoted:  fn(s: string): list of string;
+.EE
+.SH DESCRIPTION
+The
+.I cl
+argument to some of these functions is a character class in which
+a
+.B -
+between any two characters indicates a range and a
+.B ^
+in the first position means
+.I not in
+the class.
+Example of classes are
+.B \&"a-zA-Z"
+and
+.B
+\&"^acg-mr"\c
+\&.
+.PP
+.B Append
+appends string
+.I s
+to the end of string list
+.IR l .
+.PP
+.B Drop
+removes the maximal prefix of string
+.I s
+that is in class
+.IR cl .
+.PP
+.B In
+returns 1 if
+character
+.I c
+is in class
+.I cl
+and 0 if it is not. 
+.PP
+.B Prefix
+returns 1 if string
+.I pre
+is a prefix of string
+.I s
+and 0 if it is not.
+.PP
+.B Splitl
+splits string
+.I s
+just before the first character in class
+.IR cl .
+.PP
+.B Splitr
+splits string
+.I s
+just after the last character in class
+.IR cl .
+.PP
+.B Splitstrl
+splits string
+.I s
+just before the leftmost segment of string
+.I s
+that consists entirely of string
+.IR t ,
+and returns a tuple with the resulting pair of strings.
+If
+.I t
+does not occur in
+.IR s ,
+the result is
+.RI ( s , nil ).
+.PP
+.B Splitstrr
+splits string
+.I s
+just after the rightmost segment of string
+.I s
+that consists entirely of string
+.IR t ,
+and returns a tuple with the resulting pair of strings.
+If
+.I t
+does not occur in
+.IR s ,
+the result is
+.RI ( nil, s ).
+.PP
+.B Take
+returns the maximal prefix of string
+.I s
+that is in class
+.IR cl .
+.PP
+.B Toint
+returns as an integer the value represented by the string
+.IR s .
+The string is scanned up to the first character inconsistent
+with
+.IR base .
+The first inconsistent character marks the beginning of the
+returned string.
+Leading white-space characters are ignored.
+The
+.I base
+can be any integer in the range 2 to 36, inclusive;
+or 0 in which case the base can be specified as part
+of the string, in Limbo style (e.g. 16rffff).
+.PP
+.B Tobig
+has the same specification as
+.B toint
+except that converts to 64-bit
+.BR big .
+.PP
+.B Toreal
+is similar to
+.BR toint ,
+except that it expects a floating-point number after optional leading white space:
+an optional sign, then a string of digits containing a decimal point, then an optional
+.RB ` e '
+or
+.RB ` E '
+followed by an optionally signed decimal integer exponent.
+The string of digits can optionally be preceded by a base (radix) specifier
+of the form
+.IB B r ,
+as for integers.
+Any exponent is then interpreted as a power of that base.
+Alternatively, following any leading white space and an optional sign, either
+.B nan
+or
+.B infinity
+can appear, in any case, and
+.B toreal
+will return the appropriate value for IEEE floating-point.
+.PP
+.B Tolower
+converts all upper case letters in the string
+.I s
+to lower case letters.
+.PP
+.B Toupper
+converts all lower case letters in the string
+.I s
+to upper case letters.
+.PP
+.B Quoted
+takes a list of strings,
+.IR args ,
+and returns a single string with the value of each element of
+.I args
+separated from the next by a single space.
+When forming the string, the text of any element that
+contains white space or single quotes is first quoted by
+surrounding it by single quotes
+.RB ( ' ... ' )
+within which each existing single quote is doubled
+.RB ( '' ),
+following the conventions of
+.IR sh (1).
+.PP
+.B Unquoted
+takes a string
+.IR s ,
+quoted according to the conventions of
+.BR quoted ,
+and splits it into separate strings.
+It splits the string at each maximal sequence of
+unquoted white space (blank, newline or tab),
+stripping single quotes except where paired,
+to form the corresponding list of strings,
+which it returns.
+.SH SOURCE
+.B /appl/lib/string.b
--- /dev/null
+++ b/man/2/stringinttab
@@ -1,0 +1,87 @@
+.TH STRINGINTTAB 2
+.SH NAME
+stringinttab \- string table lookup module
+.SH SYNOPSIS
+.EX
+include "strinttab.m";
+StringInt: import StringIntTab;
+
+strinttab := load StringIntTab StringIntTab->PATH;
+
+StringInt: adt{
+    key: string;
+    val: int;
+};
+
+lookup:    fn(tab: array of StringInt, key: string) : (int, int);
+revlookup: fn(tab: array of StringInt, val: int)    : string;
+.EE
+.SH DESCRIPTION
+.B Stringinttab
+provides functions that perform forward and reverse searches of an ordered table of
+.I key\-value
+pairs.
+.PP
+A table should be constructed as an array of
+.B StringInt
+entries, ordered by increasing
+.I key
+value.  Keys ordering is determined by comparison of the Unicode
+value of their characters.
+.PP
+.B Lookup
+performs a binary search of
+.I tab
+for the
+.B StringInt
+entry whose
+.B key
+field has a value equal to
+.I key
+and returns a tuple: an
+.B int
+whose value is non-zero if a match has been found and zero otherwise,
+and an
+.B int
+whose value is equal to the
+.B val
+field of the matching
+.B StringInt
+entry.
+.PP
+.B Revlookup
+searches
+.I tab
+for the first
+.B StringInt
+entry whose
+.B val
+field has a value equal to
+.I val
+and returns the corresponding
+.B key
+string.
+.SH EXAMPLES
+.EX
+T := load StringIntTab StringIntTab->PATH;
+
+fmtstringtab := array[] of { Strinttab->StringInt
+	("html", FHtml),
+	("latex", FLatex),
+	("latexbook", FLatexBook),
+	("latexpart", FLatexPart),
+	("latexproc", FLatexProc),
+	("latexslides", FLatexSlides),
+};
+
+(fnd, fmt) := T->lookup(fmtstringtab, "latexbook");
+fmtstring := T->revlookup(fmtstringtab, FLatex);
+.EE
+.SH SOURCE
+.B /appl/lib/strinttab.b
+.SH SEE ALSO
+.IR hash (2)
+.SH BUGS
+.B Revlookup
+performs a sequential search of the table.
+
--- /dev/null
+++ b/man/2/styx
@@ -1,0 +1,417 @@
+.TH STYX 2
+.SH NAME
+Styx: Rmsg, Tmsg, dir2text, istmsg, packdir, packdirsize, readmsg, qid2text, unpackdir \- interface to 9P (Styx) file protocol
+.SH SYNOPSIS
+.EX
+include "styx.m";
+styx := load Styx Styx->PATH;
+
+Tmsg: adt {
+    tag: int;
+    pick {
+    Readerror =>
+        error: string;      # tag is unused in this case
+    Version =>
+        msize: int;
+        version: string;
+    Auth =>
+        afid: int;
+        uname, aname: string;
+    Attach =>
+        fid, afid: int;
+        uname, aname: string;
+    Flush =>
+        oldtag: int;
+    Walk =>
+        fid, newfid: int;
+        names: array of string;
+    Open =>
+        fid, mode: int;
+    Create =>
+        fid: int;
+        name: string;
+        perm, mode: int;
+    Read =>
+        fid: int;
+        offset: big;
+        count: int;
+    Write =>
+        fid: int;
+        offset: big;
+        data: array of byte;
+    Clunk or
+    Stat or
+    Remove => 
+        fid: int;
+    Wstat =>
+        fid: int;
+        stat: Sys->Dir;
+    }
+
+    read:   fn(fd: ref Sys->FD, msize: int): ref Tmsg;
+    unpack: fn(a: array of byte): (int, ref Tmsg);
+    pack:   fn(nil: self ref Tmsg): array of byte;
+    packedsize: fn(nil: self ref Tmsg): int;
+    text:   fn(nil: self ref Tmsg): string;
+    mtype:  fn(nil: self ref Tmsg): int;
+};
+
+Rmsg: adt {
+    tag: int;
+    pick {
+    Readerror =>
+        error: string;      # tag is unused in this case
+    Version =>
+        msize: int;
+        version: string;
+    Auth =>
+        aqid: Sys->Qid;
+    Attach =>
+        qid: Sys->Qid;
+    Flush =>
+    Error =>
+        ename: string;
+    Clunk or
+    Remove or
+    Wstat =>
+    Walk =>
+        qids: array of Sys->Qid;
+    Create or
+    Open =>
+        qid: Sys->Qid;
+        iounit: int;
+    Read =>
+        data: array of byte;
+    Write =>
+        count: int;
+    Stat =>
+        stat: Sys->Dir;
+    }
+
+    read:   fn(fd: ref Sys->FD, msize: int): ref Rmsg;
+    unpack: fn(a: array of byte): (int, ref Rmsg);
+    pack:   fn(nil: self ref Rmsg): array of byte;
+    packedsize: fn(nil: self ref Rmsg): int;
+    text:   fn(nil: self ref Rmsg): string;
+    mtype:  fn(nil: self ref Rmsg): int;
+};
+
+init:        fn();
+
+readmsg:     fn(fd: ref Sys->FD, msize: int): (array of byte, string);
+istmsg:      fn(f: array of byte): int;
+
+compatible:  fn(t: ref Tmsg.Version, msize: int, version: string): (int, string);
+
+packdirsize: fn(d: Sys->Dir): int;
+packdir:     fn(d: Sys->Dir): array of byte;
+unpackdir:   fn(f: array of byte): (int, Sys->Dir);
+dir2text:    fn(d: Sys->Dir): string;
+qid2text:    fn(q: Sys->Qid): string;
+
+VERSION:  con "9P2000";
+MAXWELEM: con 16;
+NOTAG:    con 16rFFFF;
+NOFID:    con ~0;
+IOHDRSZ:  con \fIimplementation-defined\f5;
+MAXRPC:   con \fIimplementation-defined\f5;
+.EE
+.SH DESCRIPTION
+.B Styx
+provides a Limbo interface to send and receive messages of the 9P file service protocol,
+described by Section 5 of this manual, a thorough reading of which
+is advised before using this module.
+(The protocol was previously called `Styx' in Inferno, hence the module's name.)
+.B Init
+must be called before using any other function in the module.
+.PP
+A 9P client transmits requests to a server as `T-messages'
+and receives replies in matching `R-messages'.
+A T-message is here represented by values of the type
+.BR Tmsg ,
+and an R-message by values of type
+.BR Rmsg .
+Every message has a
+.B tag
+value, and the alternatives of the pick adt represent the possible operation types of a T-message,
+generally with parameter names and types corresponding to those described in section 5.
+The exceptions are:
+.B Tmsg.Write
+and
+.B Rmsg.Read
+contain an array of byte,
+.BR data ,
+to hold the data for the corresponding message, and the `count' parameter of the message is simply the length of that array;
+and there is an alternative labelled
+.B Readerror
+that does not appear in the protocol but is used to represent input errors as described below.
+Also note that values that are `unsigned' integers in the protocol are typically given signed integer
+types in the Limbo representation (in particular, fids, qid paths, counts and offsets), and applications
+should take appropriate care when manipulating them.
+.PP
+The following functions are provided by
+.BR Tmsg:
+.TP
+.BI read( fd\fP,\fP\ msize )
+Read file descriptor
+.I fd
+to obtain exactly one T-message and return (a reference to) the corresponding
+.BR Tmsg .
+A nil value is returned on end of file.
+Otherwise, if the read fails or the data read does not form a valid T-message,
+the value returned will be a
+.B Tmsg.Readerror
+value in which the
+.B error
+member describes the error.
+.I Msize
+gives the maximum number of bytes in any acceptable T-message,
+and should be the value negotiated in the exchange of
+.B version
+messages;
+any incoming message larger than that will result in a diagnostic as a
+.B Tmsg.Readerror
+value.
+An
+.I msize
+of 0 means `no limit negotiated' and should (only) be used until a message size
+has been negotiated by exchange of
+.IR version (5)
+messages.
+.TP
+.IB t .pack()
+Return an array of bytes containing the value of
+.I t
+in the machine-independent format described in Section 5.
+It can return nil only if the message
+.I t
+is itself nil or has an invalid type.
+.TP
+.BI unpack( a )
+The array
+.I a
+is assumed to contain zero or more T-messages.
+.B Unpack
+attempts to unpack the first message, and returns a tuple of the form
+.RI ( n , v ).
+If successful,
+.I n
+is the number of bytes at the start of
+.I a
+used by the message, and
+.I v
+is the corresponding
+.B Tmsg
+value.
+If
+.I a
+contains the prefix of a valid message but more data is required to complete it,
+.I n
+is zero (and
+.I v
+is nil); the caller will typically read more data, append it to
+.IR a ,
+and try again.
+If the message is invalid,
+.I n
+is -1
+and
+.I v
+is nil.
+.TP
+.IB t .packedsize()
+Return the number of bytes required for the value of
+.I t
+when packed in its machine-independent format.
+(Zero is returned if
+.I t
+is invalid.)
+.TP
+.IB t .text()
+Return a printable string showing the contents of
+.IR t ,
+for tracing or debugging.
+.TP
+.IB t .mtype()
+Return the 9P message type of the message.
+.PP
+An R-message is represented by
+.BR Rmsg .
+Its member functions behave exactly as those for
+.BR Tmsg ,
+except that they operate on R-messages not T-messages.
+.PP
+When a client reads a directory, the data returned in the reply must be formatted
+as described in
+.IR read (5):
+an array of directory entries, one per file, with each entry formatted in
+a machine-independent format.
+An appropriate array value can be produced by
+.B packdir
+from a
+.B Sys->Dir
+structure, as used by
+.IR sys-stat (2).
+The space that packed representation will take can be calculated beforehand by
+.BR packdirsize .
+The server will usually fill the buffer for the reply to the read
+with as many entries as will fit,
+checking the space remaining against the result of
+.B packdirsize
+and if the value will fit, storing the result of
+.BR packdir .
+Given an array
+.I a
+containing at most one packed directory value (as produced by
+.BR packdir ),
+.B unpackdir
+returns a tuple
+.RI ( n,\ d )
+where
+.I n
+is \-1 if
+.I a
+is illegally formatted;
+.I n
+is zero if
+.I a
+does not contain a complete directory entry value;
+and otherwise
+.I n
+is the number of bytes of
+.I a
+used to produce the unpacked
+.B Dir
+value
+.I d .
+.PP
+The functions
+.B dir2text
+and
+.B qid2text
+produce printable strings showing the contents of the corresponding data structures,
+for use when tracing or debugging.
+.PP
+Applications that acts as file servers will read T-messages and
+reply with R-messages.
+They can use
+.B Tmsg.read
+to read each T-message, build an
+.B Rmsg
+reply value
+.IR r ,
+and use
+.IB r .pack
+to produce an array of bytes to be written in reply by
+.B Sys->write
+(see
+.IR sys-read (2)).
+.PP
+A few specialised programs might need the lower-level function
+.B readmsg
+that underlies
+.B Tmsg.read
+and
+.BR Rmsg.read .
+It reads a single message, which can be either a T-message or R-message,
+and returns it as an array of bytes,
+which can then be unpacked using
+.B Tmsg.unpack
+or
+.BR Rmsg.unpack .
+.I Msize
+is the negotiated message size, or 0 meaning `no limit'.
+The predicate
+.B istmsg
+returns true if the contents of array
+.I f
+looks like a packed representation of a T-message,
+judging only by its
+.I type
+byte.
+.PP
+When generating the
+.B version
+message (see
+.IR version (5)),
+the constant
+.B NOTAG
+can be used in
+.B Tmsg.tag
+and
+.B Rmsg.tag
+to represent `no tag value'.
+The constant
+.B VERSION
+names the current version of the protocol, and can be
+used as the value of
+.BR Tmsg.version .
+.PP
+.B Compatible
+can be used by a server to
+compare its
+.I msize
+and
+.I version
+(which is typically
+.BR VERSION )
+to those in the
+.B Tmsg.Version
+message received from a client, to decide its reply,
+following the rules in
+.IR version (5).
+It returns a tuple
+.RI ( m ", " v )
+with values for use in the
+.B Rmsg.Version
+reply.
+.I M
+is the lesser of
+.I msize
+and
+.IB t .msize ,
+and
+.I v
+is the negotiated protocol version, or the value \f5"unknown"\f1
+if no version could be agreed.
+The constant
+.B MAXRPC
+is a reasonable value for
+.I msize
+on current systems.
+The resulting value
+.I m
+can subsequently be given to the various read functions as the limit
+.IR msize .
+The constant
+.B IOHDRSZ
+gives the amount to allow for protocol overhead, when limiting data size for
+.B Tmsg.Write
+and
+.BR Rmsg.Read .
+.PP
+The constant
+.B NOFID
+can be used as the value of
+.B afid
+of the
+.B attach
+message when authentication is not required (see
+.IR attach (5)).
+.PP
+The constant
+.B MAXWELEM
+gives the protocol-defined limit on the length of the arrays
+.B Tmsg.names
+and
+.BR Rmsg.qids .
+For specialised applications, the module defines constants
+.BR Tversion ,
+.BR Rversion ,
+etc. for the message types of the protocol, and the
+other constants mentioned in Section 5.
+.SH SOURCE
+.B /appl/lib/styx.b
+.SH SEE ALSO
+.IR styxservers (2),
+.IR intro (5)
--- /dev/null
+++ b/man/2/styxconv
@@ -1,0 +1,78 @@
+.TH STYXCONV 2
+.SH NAME
+styxconv \- convert between old 1995 Styx and current Styx (9P)
+.SH SYNOPSIS
+.EX
+include "styxconv.m";
+styxconv := load Styxconv Styxconv->PATHNEW2OLD;
+styxconv := load Styxconv Styxconv->PATHOLD2NEW;
+
+init:     fn();
+styxconv: fn(client: ref Sys->FD, server: ref Sys->FD);
+.EE
+.SH DESCRIPTION
+.B Styxconv
+converts between the obsolete 1995 version of the Styx protocol,
+as used for instance in Inferno's Third Edition and earlier,
+and the current file service protocol,
+previously also called `Styx' but
+based on 9P2000 and defined by
+.IR intro (5).
+.PP
+.B Init
+must be called before any other function in the module.
+.PP
+The function
+.B styxconv
+takes two file descriptors:
+.I client
+should be a connection to a client requiring
+one version of the protocol;
+.I server
+should be a connection to a server serving
+the other version of the protocol.
+There are two conversion modules:
+.B PATHNEW2OLD
+converts from a new client to an old server;
+.B PATHOLD2NEW
+converts from an old client to a new server.
+.PP
+.B Styxconv
+should be spawned by the caller, and copies messages between
+.I client
+and
+.IR server ,
+converting as required.
+See the example below.
+.SH EXAMPLE
+Apply
+.B styxconv
+to file descriptor
+.IR fd ,
+connected to an old Styx server, and return
+a file descriptor suitable for mounting with
+.IR mount (2).
+.IP
+.EX
+cvstyx(fd: ref Sys->FD): ref Sys->FD
+{
+	styxconv := load Styxconv Styxconv->NEW2OLDPATH;
+	if(styxconv == nil)
+		return nil;
+	p := array[2] of ref Sys->FD;
+	if(sys->pipe(p) < 0)
+		return nil;
+	styxconv->init();
+	spawn styxconv->styxconv(p[1], fd);
+	return p[0];
+}
+.EE
+.SH SOURCE
+.B /appl/lib/styxconv
+.SH SEE ALSO
+.IR bind (1),
+.IR sys-bind (2),
+.IR sys-pipe (2),
+.IR intro (5)
+.SH BUGS
+There is no provision for 9P2000 authentication.
--- /dev/null
+++ b/man/2/styxflush
@@ -1,0 +1,156 @@
+.TH STYXFLUSH 2
+.SH NAME
+styxflush \- handler for 9P (Styx) flush protocol
+.SH SYNOPSIS
+.EX
+include "sys.m";
+include "styx.m";
+include "styxflush.m";
+
+styxflush := load Styxflush Styxflush->PATH;
+init: fn();
+tmsg: fn(m: ref Styx->Tmsg,
+	flushc: chan of (int, chan of int),
+	reply: chan of ref Styx->Rmsg): (int, ref Styx->Rmsg);
+rmsg: fn(m: ref Styx->Rmsg): int;
+Einterrupted: con "interrupted";
+.EE
+.SH DESCRIPTION
+Getting the semantics of the 9P
+.IR flush (5)
+protocol correct when handling requests concurrently
+is surprisingly hard to do.
+.I Styxflush
+is designed to help get it right. It deals with 9P
+messages for a single 9P session \- if a server needs to
+deal with multiple sessions, then multiple instances of
+.I styxflush
+should be loaded. It assumes there is a loop in a central process
+that both reads T-messages and sends their R-message replies.
+.I Styxflush
+handles the flushing of requests that are being run
+outside the central process.
+.PP
+.B Init
+must be called before anything else in
+.I styxflush
+to intialise its internal data structures.
+.PP
+When a T-message request arrives that will be dealt with concurrently,
+.B tmsg(\fIm\fP,\ \fIflushc\fP,\ \fIreply\fP)
+should be called to inform
+.I styxflush
+of the new request.
+.I M
+gives the T-message;
+.I flushc
+gives a channel that will be used if the request is flushed (see below),
+and
+.I reply
+should hold an unbuffered channel that can be used to send a reply
+to the central loop.
+.I Flushc
+will usually be a fresh channel for each request, but several
+requests may share the same
+.IR flushc
+if, for instance, one process is managing several requests.
+.B Tmsg
+returns a tuple
+(\fIhandled,\ rm\fP),
+where
+.I handled
+is non-zero if
+.I styxflush
+has dealt with the request itself. If it has, then
+the caller must not handle the request; it
+must send 
+.I rm
+as a reply if it is not nil.
+.PP
+.B Rmsg
+should be called when a reply message arrives at the central process
+(the same process that has called
+.BR tmsg ).
+It returns non-zero if the reply message should actually be
+sent to the client - otherwise it should be discarded.
+.SS "Flush Channel"
+.I Styxflush
+notifies a request that it has been flushed by sending a tuple,
+say
+.IR "" ( tag ,\  rc )
+on its flush channel.
+.I Tag
+gives the tag of the message that has been flushed,
+and
+.I rc
+is a channel that should be replied on when the
+request has been dealt with. There is no requirement
+that a request read on its flush channel - if it does not,
+then the replies to any flushes of that request will be delayed
+until the request is replied to.
+If it does read a flush request, however, it must reply
+to the original request before sending on
+.IR rc .
+If it has succeeded in aborting the request, it should
+send an
+.IR error (5)
+R-message with the message
+.B interrupted
+(defined as
+.BR Einterrupted );
+otherwise it should send its
+reply as usual.
+.SH SOURCE
+.B /appl/lib/styxflush.b
+.SH EXAMPLE
+This is a skeleton of a prototypical structure of a program
+that uses
+.IR styxflush .
+.EX
+replyc: chan of ref Rmsg;
+centralloop(tm: chan of ref Tmsg, fd: ref Sys->FD)
+{
+	replyc = chan of Rmsg;
+	for(;;)alt{
+	m := <-tm =>
+		if(m == nil || tagof m == tagof Tmsg.Readerror){
+			cleanup();		# kill outstanding processes, etc.
+			return;
+		}
+		flushc := chan of (int, chan of int);
+		(handled, rm) := styxflush->tmsg(m, flushc, replyc);
+		if(!handled)
+			spawn request(m, flushc);
+		else if(rm != nil)
+			sendreply(rm);
+	rm := <- replyc =>
+		if(styxflush->rmsg(rm))
+			sendreply(rm);
+	}
+}
+		
+sendreply(fd: ref Sys->FD, rm: ref Rmsg)
+{
+	d := rm.pack();
+	sys->write(fd, d, len d);
+}
+
+request(tm: ref Tmsg, flushc: chan of (int, chan of int))
+{
+	pick m := tm {
+	Open =>
+		replyc <-= ref Rmsg.Open(m.tag, ...);
+	Read =>
+		[...]
+		alt{
+		x := <-readc =>
+			# read from data produced on readc
+			replyc <-= ref Rmsg.Read(m.tag, ...);
+		(nil, rc) := <-flushc =>
+			# read request has been flushed.
+			replyc <-= ref Rmsg.Error(m.tag, Einterrupted);
+			rc <-= 1;
+		}
+	etc ...
+	}
+}
--- /dev/null
+++ b/man/2/styxpersist
@@ -1,0 +1,80 @@
+.TH STYXPERSIST 2
+.SH NAME
+styxpersist \- persistent 9P (Styx) connection
+.SH SYNOPSIS
+.EX
+include "sys.m";
+include "styxpersist.m";
+styxpersist := load Styxpersist Styxpersist->PATH;
+
+init: fn(clientfd: ref Sys->FD, usefac: int, keyspec: string):
+        (chan of chan of ref Sys->FD, string);
+.EE
+.SH DESCRIPTION
+.I Styxpersist
+tries to maintain a persistent 9P (Styx) connection for its client.
+.B Init
+starts a process to serve 9P for the client on
+.IR clientfd .
+If
+.I usefac
+is non-zero,
+.I styxpersist
+will use
+.IR factotum (2)
+to do plan 9-style authentication if necessary,
+and
+.I keyspec
+gives attribute-value pairs to pass to
+.IR factotum 's
+.B proxy
+function.
+.PP
+.B Init
+returns a tuple, say
+.RI ( c ,\  err ).
+If things have started successfully,
+.I c
+holds a channel that
+.I styxpersist
+uses to request a new connection to the server.
+The caller of
+.B init
+should arrange that a process is ready to repeatedly receive on this channel,
+dial the server, and send the resulting file descriptor (or
+.B nil
+if the connection was unsuccessful)
+on the channel received, 
+.PP
+If
+.B init
+failed,
+.I c
+will be
+.B nil
+and
+.I err
+describes the error.
+.PP
+If the server goes down, it redials as above, and tries to
+re-open all the files that were open in the previous connection.
+In the meantime all client access to the namespace is blocked.
+.PP
+Note that this service should only be used on conventional
+file servers, where most reads and writes are idempotent.
+.SH SOURCE
+.B /appl/lib/styxpersist.b
+.SH BUGS
+Even on a conventional fileserver, some operations are non-idempotent;
+namely create, rename, remove, and write to an append-only file.
+If a fileserver dies while such a request is outstanding, it is unclear
+what it should do. Currently it returns an error to the client if such a sitution occurs.
+.PP
+.I Styxpersist
+is dependent on the capabilities of the local factotum to re-authenticate
+Plan 9 style. If a client uses an external factotum with different keys,
+.I styxpersist
+may be unable to re-authenticate.
+.SH "SEE ALSO"
+.IR mount (1),
+.IR dial (2)
--- /dev/null
+++ b/man/2/styxservers
@@ -1,0 +1,941 @@
+.TH STYXSERVERS 2
+.SH NAME
+styxservers \-
+9P (Styx) server implementation assistance
+.SH SYNOPSIS
+.EX
+include "sys.m";
+include "styx.m";
+Tmsg, Rmsg: import Styx;
+include "styxservers.m";
+styxservers := load Styxservers Styxservers->PATH;
+Styxserver, Fid, Navigator: import styxservers;
+
+Styxserver: adt {
+    fd:      ref Sys->FD;     # file server end of connection
+    t:       ref Navigator;   # name space navigator for this server
+    msize:   int;             # negotiated 9P message size
+ 
+    new:     fn(fd: ref Sys->FD, t: ref Navigator, rootpath: big)
+                  :(chan of ref Tmsg, ref Styxserver);
+    reply:   fn(srv: self ref Styxserver, m: ref Rmsg): int;
+
+    # protocol operations
+    attach:  fn(srv: self ref Styxserver, m: ref Tmsg.Attach): ref Fid;
+    clunk:   fn(srv: self ref Styxserver, m: ref Tmsg.Clunk): ref Fid;
+    walk:    fn(srv: self ref Styxserver, m: ref Tmsg.Walk): ref Fid;
+    open:    fn(srv: self ref Styxserver, m: ref Tmsg.Open): ref Fid;
+    read:    fn(srv: self ref Styxserver, m: ref Tmsg.Read): ref Fid;
+    remove:  fn(srv: self ref Styxserver, m: ref Tmsg.Remove): ref Fid;
+    stat:    fn(srv: self ref Styxserver, m: ref Tmsg.Stat);
+    default: fn(srv: self ref Styxserver, gm: ref Tmsg);
+
+    replychan: chan of ref Rmsg;             # replies sent here if not nil.
+    replydirect: fn(srv: self ref Styxserver, gm: ref Rmsg): int; # called by receiver for replychan
+
+   # check validity
+    cancreate: fn(srv: self ref Styxserver, m: ref Tmsg.Create)
+                  :(ref Fid, int, ref Sys->Dir, string);
+    canopen:   fn(srv: self ref Styxserver, m: ref Tmsg.Open)
+		          :(ref Fid, int, ref Sys->Dir, string);
+    canread:   fn(srv: self ref Styxserver, m: ref Tmsg.Read)
+                  :(ref Fid, string);
+    canwrite:  fn(srv: self ref Styxserver, m: ref Tmsg.Write)
+                  :(ref Fid, string);
+    canremove: fn(srv: self ref Styxserver, m: ref Tmsg.Remove)
+                  :(ref Fid, big, string);
+
+    # fid management
+    getfid:  fn(srv: self ref Styxserver, fid: int): ref Fid;
+    newfid:  fn(srv: self ref Styxserver, fid: int): ref Fid;
+    delfid:  fn(srv: self ref Styxserver, c: ref Fid);
+    allfids: fn(srv: self ref Styxserver): list of ref Fid;
+
+    iounit:  fn(srv: self ref Styxserver): int;
+};
+
+Fid: adt {
+    fid:    int;       # client's fid
+    path:   big;       # file's 64-bit unique path
+    qtype:  int;       # file's qid type (eg, Sys->QTDIR if directory)
+    isopen: int;       # non-zero if file is open
+    mode:   int;       # if open, the open mode
+    uname:  string;    # user name from original attach
+    param:  string;    # attach aname from original attach
+    data:   array of byte;   # application data
+
+    clone:  fn(f: self ref Fid, nf: ref Fid): ref Fid;
+    open:   fn(f: self ref Fid, mode: int, qid: Sys->Qid);
+    walk:   fn(f: self ref Fid, qid: Sys->Qid);
+};
+
+Navop: adt {
+    reply:  chan of (ref Sys->Dir, string);  # channel for reply
+    path:   big;      # file or directory path
+    pick {
+    Stat =>
+    Walk =>
+        name: string;
+    Readdir =>
+        offset: int;  # index (origin 0) of first entry to return
+        count:  int;  # number of directory entries requested
+    }
+};
+
+Navigator: adt {
+    new:    fn(c: chan of ref Navop): ref Navigator;
+    stat:   fn(t: self ref Navigator, path: big): (ref Sys->Dir, string);
+    walk:   fn(t: self ref Navigator, parent: big, name: string)
+               : (ref Sys->Dir, string);
+    readdir:fn(t: self ref Navigator, path: big,
+               offset, count: int): array of ref Sys->Dir;
+};
+
+init:      fn(styx: Styx);
+traceset:  fn(on: int);
+
+readbytes: fn(m: ref Styx->Tmsg.Read, d: array of byte):
+              ref Styx->Rmsg.Read;
+readstr:   fn(m: ref Styx->Tmsg.Read, s: string):
+              ref Styx->Rmsg.Read;
+openok:    fn(uname: string, omode,
+              perm: int, funame, fgname: string): int;
+openmode:  fn(o: int): int;
+.EE
+.SH DESCRIPTION
+When writing a file server, there are some
+commonly performed tasks that are
+fiddly or tedious to implement each time.
+.B Styxservers
+provides a framework to automate some of these
+routine tasks.
+In particular, it helps manage the fid space,
+implements common default processing for protocol messages,
+and assists walking around the
+directory hierarchy and reading of directories. Other
+tasks, such as defining the structure of the
+name space, and reading and writing files in it, are
+left to the file server program itself.
+Familiarity with Section 5 of the manual which defines the protocol
+(see
+.IR intro (5)),
+and with the representation of 9P messages in Limbo
+(see
+.IR styx (2)),
+is a prerequisite for use of this module.
+.PP
+.B Styxservers
+does not define or store any of the directory hierarchy itself;
+instead it queries an external process for information
+when necessary, through a value of type
+.BR Navigator ,
+which encapsulates communication with that process.
+That process must be started up
+independently of each
+.BR Styxserver ;
+a channel to such a process should be provided
+when starting a new
+.BR Styxserver .
+The channel carries messages of type
+.BR Navop .
+.IR Styxservers-nametree (2)
+provides a ready-made
+implementation of such a process that is sufficient for many applications.
+.PP
+.B Styxserver
+keeps tabs on the fids that are currently in use, and remembers
+some associated information, such as the Qid path
+of the file, whether it has been opened, etc.
+It does this using values of type
+.BR Fid .
+.PP
+Once the
+.B Styxservers
+module has been loaded,
+the
+.B init
+function must be called before anything else,
+to initialise its internal state. The
+.I styx
+argument should be an implementation of
+the
+.IR styx (2)
+module, which will be used to translate messages.
+Individual
+.B Styxserver
+instances do not share state, and are therefore
+independently thread-safe.
+.SS Fid representation
+.B Styxservers
+represents each active fid as a
+.B Fid
+value,
+which has the following public members:
+.TF param
+.TP
+.B fid
+The integer
+.I fid
+value provided by the client to refer to an active instance of a file in the file server,
+as described in
+.IR intro (5).
+.TP
+.B path
+The 64-bit qid path that uniquely identifies the file on the file server,
+as described in
+.IR intro (5).
+It is set by
+.IB f .walk
+and
+.IB f .open
+(see below).
+.TP
+.B qtype
+The file's qid type; it is
+.B Sys->QTDIR
+if and only if the fid refers to a directory.
+The value is set by
+.IB f .walk
+and
+.IB f .open
+(see below).
+.TP
+.B isopen
+Non-zero if and only if the fid has been opened by an
+.IR open (5)
+message.
+It is initially zero, and set by
+.IB f .open
+(see below).
+.TP
+.B mode
+Valid only if the fid has been opened.
+It has one of the values
+.BR Sys->OREAD ,
+.BR Sys->OWRITE ,
+.BR Sys->ORDWR ,
+possibly ORed with
+.BR Sys->ORCLOSE ,
+corresponding to the mode with which the file was opened.
+It is set by
+.IB f .open
+(see below).
+.TP
+.B uname
+The name of the user that created the fid.
+.TP
+.B param
+Set by
+.B Styxservers
+to the
+.B aname
+of the initial
+.IR attach (5)
+message,
+and subsequently inherited by each new fid created by
+.IR walk (5),
+but not otherwise used by
+.B Styxservers
+itself, and may be changed by the application.
+.TP
+.B data
+Unused by
+.BR Styxservers ;
+for application use.
+It might be used, for instance, to implement a file that gives different
+data to different clients.
+.TP
+.IB f .clone( nf )
+Copy the current state of all members of
+.I f
+except
+.IB f .fid\f1,\fP
+into
+.IR nf ,
+and return
+.IR nf .
+Used by
+.BR Styxserver.walk ,
+and is needed by an application only if it replaces that function.
+.TP
+.IB f .walk( qid )
+Make
+.I f
+refer to the file with the given
+.IR qid :
+set
+.IB f .path
+and
+.IB f .qtype
+from
+.IB qid .path
+and
+.IB qid .qtype .
+Used by
+.IB Styxserver.walk
+and is needed by an application only if it replaces that function.
+.TP
+.IB f .open( mode,\ qid )
+Mark
+.I f
+as `open',
+set
+.IR f .mode
+to
+.IR mode ,
+and set
+.B path
+and
+.B qtype
+to the path and type of
+.IR qid .
+Used by the
+implementations of
+.B open
+and
+.B create
+messages.
+The default implementation of
+.IR open (5)
+in
+.B Styxserver
+obtains the value of
+.I mode
+from
+.B Styxserver.canopen
+(below),
+and
+obtains the value of
+.I qid
+by querying the application's navigator.
+.SS Styxserver and file server state
+Each
+.B Styxserver
+value holds the state for a single file server, including its active fids,
+the link to the external name space process, and other internal data.
+Most of the state is manipulated through the member functions described below.
+The exceptions are two read-only values:
+the
+.B Navigator
+reference
+.IB srv .t
+which can be used to access that navigator; and
+the file descriptor
+.IB srv .fd
+that is the file server's end of the connection to the 9P client.
+Both values are initially provided by the file serving application,
+but can be accessed through the
+.B Styxserver
+value for convenience.
+The file descriptor value is normally used only through
+.BR Styxserver.reply ,
+but will be needed directly if the caller needs the file descriptor value
+as a parameter to
+.IR sys-pctl (2)
+when insulating the serving process's file descriptors from the surrounding environment.
+.PP
+The first set of functions in
+.B Styxserver
+provides common and default actions:
+.TP
+.B Styxserver.new(\fIfd\fP,\ \fIt\fP,\ \fIrootpath\fP)
+Create a new
+.BR Styxserver .
+It returns a tuple, say
+.RI ( c ", " srv ),
+and spawns a new process, which uses
+.IR styx (2)
+to read and parse 9P messages read
+from
+.IR fd ,
+and send them down
+.IR c ;
+.I t
+should be a
+.B Navigator
+adt which the
+.B Styxserver
+can use to answer queries
+on the name space (see ``Navigating file trees'', below).
+.I Rootpath
+gives the Qid path of the root of the served name space.
+.TP
+.IB srv .reply(\fIm\fP)
+Send a reply (R-message) to a client. The various utility methods,
+listed below, call this function to make their response. When
+.IB srv .replychan
+is not nil
+the function sends the R-message to this channel. It is assumed that a process
+will drain replies from it and call
+.IB srv .replydirect
+when appropriate.
+.TP
+.IB srv .attach(\fIm\fP)
+Respond to an
+.IR attach (5)
+message
+.IR m ,
+creating a new fid in the process, and returning it.
+Returns
+.B nil
+if
+.IB m .fid
+is a duplicate of an existing fid.
+The value of the attach parameter
+.IB m .aname
+is copied into the new fid's
+.B param
+field, as is the attaching user name,
+.IB m .uname .
+.TP
+.IB srv .clunk(\fIm\fP)
+Respond to a
+.IR clunk (5)
+message
+.IR m ,
+and return the old
+.BR Fid .
+Note that this does nothing about remove-on-close
+files; that should be programmed explicitly if needed.
+.TP
+.IB srv .walk(\fIm\fP)
+Respond to a
+.IR walk (5)
+message
+.IR m ,
+querying
+.IB srv . t
+for information on existing files.
+.TP
+.IB srv .open(\fIm\fP)
+Respond to an
+.IR open (5)
+message
+.IR m .
+This will allow a file to be opened if its permissions allow the
+specified mode of access.
+.TP
+.IB srv .read(\fIm\fP)
+Respond to a
+.IR read (5)
+message
+.IR m .
+If a directory is being read, the appropriate reply
+is made; for files, an error is given.
+.TP
+.IB srv .remove(\fIm\fP)
+Respond to a
+.IR remove (5)
+message
+.IR m
+with an error, clunking the fid as it does so,
+and returning the old
+.BR Fid .
+.TP
+.IB srv .stat(\fIm\fP)
+Respond to a
+.IR stat (5)
+message
+.IR m .
+.TP
+.IB srv .default(\fIgm\fP)
+Respond to an arbitrary T-message,
+.IR gm ,
+as appropriate (eg, by calling
+.IB srv .walk
+for a
+.IR walk (5)
+message).
+It responds appropriately to
+.IR version (5),
+and replies to
+.B Tauth
+(see
+.IR attach (5))
+stating that authentication is not required.
+Other messages without an associated
+.B Styxserver
+function are generally responded to
+with a ``permission denied'' error.
+.PP
+All the functions above check the validity of the fids, modes, counts and offsets
+in the messages, and automatically reply to the client with a suitable
+.IR error (5)
+message on error.
+.PP
+The following further
+.B Styxserver
+operations are useful
+in applications that override all or part of the default handling
+(in particular,
+to process read and write requests):
+.TP
+.IB srv .canopen( m )
+Check whether it is legal to open a file as requested by message
+.IR m :
+the fid is valid but not already open, the corresponding file exists and its
+permissions allow access in the requested mode, and if
+.B Sys->ORCLOSE
+is requested, the parent directory is writable (to allow the file to be removed when closed).
+.B Canopen
+returns a tuple, say
+.RI ( f ,\  mode ,\  d,\ err\ \fP).
+If the open request was invalid,
+.I f
+will be nil, and the string
+.I err
+will diagnose the error (for return to the client in an
+.B Rmsg.Error
+message).
+If the request was valid:
+.I f
+contains the
+.B Fid
+representing the file to be opened;
+.I mode
+is the access mode derived from
+.IB m .mode ,
+.BR Sys->OREAD ,
+.BR Sys->OWRITE ,
+.BR Sys->ORDWR ,
+ORed with
+.BR Sys->ORCLOSE ;
+.I d
+is a
+.B Dir
+value giving the file's attributes, obtained from the navigator;
+and
+.I err
+is nil.
+Once the application has done what it must to open the file,
+it must call
+.IB f .open
+to mark it open.
+.TP
+.IB srv .cancreate( m )
+Checks whether the
+creation of the file requested by
+message
+.I m
+is legal:
+the fid is valid but not open, refers to a directory,
+the permissions returned by
+.IR srv .t.stat
+show that directory is writable by the requesting user,
+the name does not already exist in that directory,
+and the mode with which the new file would be opened is valid.
+.B Cancreate
+returns a tuple, say
+.RI ( f ,\  mode,\  d,\ err\ \fP).
+If the creation request was invalid,
+.I f
+will be nil, and the string
+.I err
+will diagnose the error, for use in an error reply to the client.
+If the request was valid:
+.I f
+contains the
+.B Fid
+representing the parent directory;
+.I mode
+is the open mode as defined for
+.B canopen
+above;
+.I d
+is a
+.B Dir
+value containing some initial attributes for the new file or directory;
+and
+.I err
+is nil.
+The initial attributes set in
+.I d
+are:
+.IB d .name
+(the name of the file to be created);
+.IB d .uid
+and
+.IB d .muid
+(the user that did the initial attach);
+.IB d .gid ,
+.IB d .dtype ,
+.IB d .dev
+(taken from the parent directory's attributes);
+and
+.IB d .mode
+holds the file mode that should be attributed to the new
+file (taking into account the parent mode, as
+described in
+.IR open (5)).
+The caller must supply
+.IB d .qid
+once the file has successfully been created,
+and
+.IB d .atime
+and
+.IB d .mtime ;
+it must also call
+.IB f .open
+to mark
+.I f
+open and set its path to the file's path.
+If the file cannot be created successfully, the application should reply with
+an
+.IR error (5)
+message and leave
+.I f
+untouched.
+The
+.B Fid
+.I f
+will then continue to refer to the original directory, and remain unopened.
+.TP
+.IB srv .canread( m )
+Checks whether
+.IR read (5)
+message
+.I m
+refers to a valid fid that has been opened for reading,
+and that the count and file offset are non-negative.
+.B Canread
+returns a tuple, say
+.RI ( f ,\  err );
+if the attempted access is illegal,
+.I f
+will be nil, and
+.I err
+contains a description of the error,
+otherwise
+.I f
+contains the
+.B Fid
+corresponding to the file in question.
+It is typically called by an application's implementation of
+.B Tmsg.Read
+to obtain the
+.B Fid
+corresponding to the fid in the message, and check the access.
+.TP
+.IB srv .canwrite( m )
+Checks whether
+message
+.I m
+refers to a valid fid that has been opened for writing,
+and that the file offset is non-negative.
+.B Canwrite
+returns a tuple
+.RI ( f ,\  err );
+if the attempted access is illegal,
+.I f
+will be nil, and
+.I err
+contains a description of the error,
+otherwise
+.I f
+contains the
+.B Fid
+corresponding to the file in question.
+It is typically called by an application's implementation of
+.B Tmsg.Write
+to obtain the
+.B Fid
+corresponding to the fid in the message, and check the access.
+.TP
+.IB srv .canremove( m )
+Checks whether the
+removal of the file requested by
+message
+.I m
+is legal: the fid is valid, it is not a root directory, and there is write permission
+in the parent directory.
+.B Canremove
+returns a tuple
+.RI ( f,\ path,\ err );
+if the attempted access is illegal,
+.I f
+will be nil and
+.I err
+contains a description of the error;
+otherwise
+.I f
+contains the
+.B Fid
+for the file to be removed, and
+.I path
+is the
+.B Qid.path
+for the parent directory.The caller should remove the file, and in every case must call
+.B srv.delfid
+before replying,
+because the protocol's remove operation always clunks the fid.
+.TP
+.IB srv .iounit()
+Return an appropriate value for use as the
+.I iounit
+element in
+.B Rmsg.Open
+and
+.B Rmsg.Create
+replies,
+as defined in
+.IR open (5),
+based on the message size negotiated by the initial
+.IR version (5)
+message.
+.PP
+The remaining functions are normally used only by servers that need to
+override default actions.
+They maintain and access the mapping between a client's fid values presented in
+.B Tmsg
+messages and the
+.B Fid
+values that represent the corresponding files internally.
+.TP
+.IB srv .newfid(\fIfid\fP)
+Create a new
+.B Fid
+associated with number
+.I fid
+and return it.
+Return nil if the
+.I fid
+is already in use (implies a client error if the server correctly clunks fids).
+.TP
+.IB srv .getfid(\fIfid\fP)
+Get the
+.B Fid
+data associated with numeric id
+.IR fid ;
+return nil if there is none such (a malicious or erroneous client
+can cause this).
+.TP
+.IB srv .delfid(\fIfid\fP)
+Delete
+.I fid
+from the table of fids in the
+.BR Styxserver .
+(There is no error return.)
+.TP
+.IB srv .allfids()
+Return a list of all current fids (ie, the files currently active on the client).
+.PP
+.B Newfid
+is required when processing
+.IR auth (5),
+.IR attach (5)
+and
+.IR walk (5)
+messages to create new fids.
+.B Delfid
+is used to clunk fids when processing
+.IR clunk (5),
+.IR remove (5),
+and in a failed
+.IR walk (5)
+when it specified a new fid.
+All other messages should refer only to already existing fids, and the associated
+.B Fid
+data is fetched by
+.BR getfid .
+.SS Navigating file trees
+When a
+.B Styxserver
+instance needs to know about the namespace,
+it queries an external process through a channel
+by sending a
+.B Navop
+request;
+each such request carries with it a
+.B reply
+channel through which the
+reply should be made.
+The reply tuple has a reference to a
+.B Sys->Dir
+value that is non-nil on success, and a diagnostic string
+that is non-nil on error.
+.PP
+Files in the tree are referred to
+by their Qid
+.BR path .
+The requests are:
+.TF Walk
+.TP
+.BR Stat
+.br
+Find a file in the hierarchy by its
+.BR path ,
+and reply with the corresponding
+.B Dir
+data if found (or a diagnostic on error).
+.TP
+.BR Walk
+.br
+Look for file
+.B name
+in the directory with the given
+.BR path .
+.TP
+.BR Readdir
+.br
+Get information on selected files in the directory with the given
+.BR path .
+In this case, the reply channel is used to send
+a sequence of values, one for each entry in the directory, finishing with a tuple value
+.BR (nil,nil) .
+The entries to return are those selected by an
+.B offset
+that is the index (origin 0) of the first directory entry to return,
+and a
+.B count
+of a number of entries to return starting with that index.
+Note that both values are expressed in units of directory entries, not as byte counts.
+.PP
+.B Styxserver
+provides a
+.B Navigator
+adt to enable convenient access to this functionality; calls
+into the
+.B Navigator
+adt are bundled up into requests on the channel, and the
+reply returned.
+The functions provided are:
+.TP 10
+.BI Navigator.new( c )
+Create a new
+.BR Navigator ,
+sending requests down
+.IR c .
+.TP
+.IB t .stat(\fIpath\fP)
+Find the file with the given
+.IR path .
+Return a tuple
+.RI ( d ,\  err ),
+where
+.I d
+holds directory information for the file
+if found; otherwise
+.I err
+contains an error message.
+.TP
+.IB t .walk(\fIparent\fP,\ \fIname\fP)
+Find the file with name
+.I name
+inside parent directory
+.IR parent .
+Return a tuple as for
+.BR stat .
+.TP
+.IB t .readdir(\fIpath\fP,\ \fIoffset\fP,\ \fIcount\fP)
+Return directory data read from directory
+.IR path ,
+starting at entry
+.I offset
+for
+.I count
+entries.
+.SS Other functions
+The following functions provide some commonly used functionality:
+.TP 10
+.BI readbytes( m ,\  d )
+Assuming that the file in question contains data
+.IR d ,
+.B readbytes
+returns an appropriate reply to
+.IR read (5)
+message
+.IR m ,
+taking account of
+.IB m .offset
+and
+.IB m.count
+when extracting data from
+.IR d .
+.TP 10
+.BI readstr( m ,\  s )
+Assuming that the file in question contains string
+.IR s ,
+.B readstr
+returns an appropriate reply to
+.IR read (5)
+message
+.IR m ,
+taking account of
+.IB m .offset
+and
+.IB m.count
+when extracting data from the UTF-8 representation of
+.IR s .
+.TP
+.BI openok (\fIuname\fP,\ \fIomode\fP,\ \fIperm\fP,\ \fIfuid\fP,\ \fIfgid\fP)
+Does standard permission checking, assuming user
+.I uname
+is trying to open a file with access mode
+.IR omode ,
+where the file is owned by
+.IR fuid ,
+has group
+.IR fgid ,
+and permissions
+.IR perm .
+Returns true (non-zero) if permission would be granted, and false (zero) otherwise.
+.TP
+.BI openmode( o )
+Checks to see whether the open mode
+.I o
+is well-formed; if it is not,
+.B openmode
+returns -1; if it is, it returns the mode
+with OTRUNC and ORCLOSE flags removed.
+.TP
+.BI traceset( on )
+If
+.I on
+is true (non-zero),
+will trace 9P requests and replies, on standard error.
+This option must be set before creating a
+.BR Styxserver ,
+to ensure that it preserves its standard error descriptor.
+.SS Constants
+.B Styxservers
+defines a number of constants applicable to the writing
+of 9P servers, including:
+.TP
+.BR Einuse\fP,\fP\ Ebadfid\fP,\fP\ Eopen\fP,\fP\ Enotfound\fP,\fP\ Enotdir\fP,\fP\ Eperm\fP,\fP\ Ebadarg\fP,\fP\ Eexists
+These provide standard strings for commonly used error conditions,
+to be used in
+.B Rmsg.Error
+replies.
+.SS Authentication
+If authentication is required beyond that provided at the link level
+(for instance by
+.IR security-auth (2)),
+the server application must handle
+.B Tauth
+itself,
+remember the value of
+.I afid
+in that message, and generate an
+.B Rauth
+reply with a suitable Qid referring to a file with
+.B Qid.qtype
+of
+.BR QTAUTH .
+Following successful authentication by read and write on that file,
+it must associate that status with the
+.IR afid .
+Then, on a subsequent
+.B Tattach
+message, before calling
+.I srv .attach
+it must check that the
+.BR Tattach 's
+.I afid
+value corresponds to one previously authenticated, and
+reply with an appropriate error if not.
+.SH SOURCE
+.B /appl/lib/styxservers.b
+.SH SEE ALSO
+.IR styxservers-nametree (2),
+.IR sys-stat (2),
+.IR intro (5)
--- /dev/null
+++ b/man/2/styxservers-nametree
@@ -1,0 +1,180 @@
+.TH STYXSERVERS-NAMETREE 2
+.SH NAME
+Styxservers: nametree \-
+hierarchical name storage for use with Styxservers.
+.SH SYNOPSIS
+.EX
+include "sys.m";
+include "styx.m";
+include "styxservers.m";
+nametree := load Nametree Nametree->PATH;
+	Tree: import nametree;
+
+Tree: adt {
+    create: fn(t: self ref Tree, parentpath: big, d: Sys->Dir): string;
+    remove: fn(t: self ref Tree, path: big): string;
+    wstat:   fn(t: self ref Tree, path: big, d: Sys->Dir);
+    quit:   fn(t: self ref Tree);
+};
+init:       fn();
+start:      fn(): (ref Tree, chan of ref Styxservers->Navop);
+.EE
+.SH DESCRIPTION
+.B Nametree
+provides the storage for a hierarchical namespace
+to be used by
+.IR styxservers (2).
+After the module is loaded, the
+.B init
+function should be called to
+initialise the module's internal variables.
+.B Start
+spawns a new
+.B nametree
+process; it returns a tuple, say
+.RI ( tree ,\  c ),
+where c is a channel that can be used to create
+an instance of
+.BR Styxservers->Navigator ,
+to access files inside
+.BR nametree ,
+and
+.I tree
+is an adt that allows creation and removal of those files.
+On failure, these functions return a string describing
+the error.
+.PP
+Note that the full set of operations on
+.B Nametree
+(i.e. stat, walk, readdir, wstate, create and remove),
+is only available in conjunction with
+.BR Styxserver 's
+.B Navigator
+interface.
+Files in the name space are ultimately identified by a 64-bit
+.I path
+value, which forms the path component of the file's Qid.
+(See
+.IR intro (5)
+for a description of the system's interpretation of Qids.)
+.PP
+The
+.B Tree
+operations
+are:
+.TP 10
+.IB t .create(\fIparentpath\fP,\ \fId\fP)
+Create a new file or directory.
+.I D
+gives the directory information that will be stored
+for the file, including its own path value,
+given by
+.IB d .qid.path .
+If the file referenced by
+.I parentpath
+does not exist, creation will not be allowed,
+other than in the special case when
+.IB d .qid.path
+is equal to
+.IR parentpath ,
+in which case it is assumed to be a root directory
+and may be created. This potentially allows a single
+.B Nametree
+instance to hold many distinct directory hierarchies.
+Note that no attempt is made to ensure that
+.I parentpath
+refers to a directory; the check is assumed to have
+been made previously.
+When a hierarchy is traversed,
+.B Nametree
+interprets the name
+.RB ` .. '
+itself as `parent directory', and that name should not be created explicitly.
+.TP
+.IB t .remove(\fIpath\fP)
+Remove the file referred to by
+.IR path ,
+and all its descendants.
+.TP
+.IB t .wstat(\fIpath\fP,\ \fId\fP)
+Change the directory information held on file
+.IR path .
+The Qid path itself cannot be changed by
+.IR d .
+.TP
+.IB t .quit()
+Shut down the
+.B nametree
+process.
+.SH EXAMPLE
+Here is a complete example that uses
+.B Nametree
+in conjunction with
+.B Styxservers
+in order to serve two files
+.B data
+and
+.BR ctl " ..."
+and do nothing with them:
+.EX
+implement Tst;
+include "sys.m";
+	sys: Sys;
+include "draw.m";
+include "styx.m";
+include "styxservers.m";
+	styxservers: Styxservers;
+	Styxserver, Navigator: import styxservers;
+	nametree: Nametree;
+	Tree: import nametree;
+
+Tst: module
+{
+	init: fn(nil: ref Draw->Context, argv: list of string);
+};
+
+Qroot, Qctl, Qdata: con big iota;	# paths
+init(nil: ref Draw->Context, args: list of string)
+{
+	sys = load Sys Sys->PATH;
+	styx := load Styx Styx->PATH;
+	styx->init();
+	styxservers = load Styxservers Styxservers->PATH;
+	styxservers->init(styx);
+	nametree = load Nametree Nametree->PATH;
+	nametree->init();
+	sys->pctl(Sys->FORKNS, nil);
+	(tree, treeop) := nametree->start();
+	tree.create(Qroot, dir(".", 8r555|Sys->DMDIR, Qroot));
+	tree.create(Qroot, dir("ctl", 8r666, Qctl));
+	tree.create(Qroot, dir("data", 8r444, Qdata));
+	(tchan, srv) := Styxserver.new(sys->fildes(0),
+						Navigator.new(treeop), Qroot);
+	while((gm := <-tchan) != nil) {
+		# normally a pick on gm would act on
+		# Tmsg.Read and Tmsg.Write at least
+		srv.default(gm);
+	}
+	tree.quit();
+}
+
+dir(name: string, perm: int, qid: big): Sys->Dir
+{
+	d := sys->zerodir;
+	d.name = name;
+	d.uid = "me";
+	d.gid = "me";
+	d.qid.path = qid;
+	if (perm & Sys->DMDIR)
+		d.qid.qtype = Sys->QTDIR;
+	else
+		d.qid.qtype = Sys->QTFILE;
+	d.mode = perm;
+	return d;
+}
+.EE
+.SH SOURCE
+.B /appl/lib/nametree.b
+.SH SEE ALSO
+.IR styxservers (2),
+.IR intro (5)
--- /dev/null
+++ b/man/2/sys-0intro
@@ -1,0 +1,345 @@
+.TH SYS-INTRO 2
+.SH NAME
+Sys: intro \- introduction to the
+.B Sys
+module
+.SH SYNOPSIS
+.EX
+include "sys.m";
+sys := load Sys Sys->PATH;
+.EE
+.SH DESCRIPTION
+Inferno system calls are provided by the built-in module declared by
+.BR sys.m .
+It contains the fundamental system data structures and interfaces.
+There are currently 42 calls, providing: file access; basic I/O;
+name space manipulation; formatted output
+for Limbo; and basic character and string manipulation.
+.SS "File Name Space"
+Files are collected into a hierarchical organization called a
+.I file
+.I tree
+starting in a
+.I directory
+called the
+.IR root.
+.IR Filenames ,
+also called
+.IR paths ,
+consist of a number of
+.BR / -separated
+.I "path elements
+with the slashes corresponding to directories.
+A path element must contain only printable
+characters (those outside ASCII and Latin-1 control space).
+A path element cannot contain a slash.
+The path element
+.B \&..
+refers to the parent directory of the directory containing that element.
+.PP
+When a process presents a file name to Inferno,
+it is
+.I evaluated
+by the following algorithm.
+.TP
+1.
+Start with a directory that depends on the first character of the path:
+.B /
+means the root of the main hierarchy,
+.B #
+means the separate root of a kernel device's file tree
+(see Section 3),
+and anything else means the process's current working directory.
+.TP
+2.
+For each path element, look up the element
+in the directory, advance to that directory,
+do a possible translation (see below).
+.TP
+3.
+Repeat.
+The last step may yield a directory or regular file.
+.PP
+The collection of files reachable from the root is called the
+.I "name space
+of a process.
+.PP
+A program can use
+.B bind
+or
+.B mount
+(see
+.IR sys-bind (2))
+to say that whenever a specified file is reached during an evaluation,
+that evaluation continues instead from a second specified file.
+Also, these same calls create
+\f2union directories\fP,
+which are concatenations of ordinary directories
+that are searched sequentially until the desired element is found.
+Using
+.B bind
+and
+.B mount
+to do name space adjustment affects only
+the current name space group (see below, and
+.IR sys-pctl (2)).
+Certain conventions about the layout of the name space should be preserved;
+see
+.IR namespace (4).
+.PP
+The operating system kernel records the file name used to access each open file or directory.
+If the file is opened by a relative path name (one that does not begin
+.B /
+or
+.BR # ),
+the system makes the stored name absolute by prefixing
+the string associated with the current directory.
+Similar lexical adjustments are made for path names containing
+.B .
+(dot) or
+.B ..
+(dot-dot).
+By this process, the system maintains a record of the route by which each file was accessed.
+Although there is a possibility for error—the name is not maintained after the file is opened,
+so removals and renamings can confound it—this simple method usually
+permits the system to return, via
+.IR sys-fd2path (2)
+and related calls such as those of
+.IR workdir (2),
+a valid name that may be used to find a file again.
+This is also the source of the names reported in the name space listing of
+.IR ns (1)
+or the
+.B ns
+file of
+.IR prog (3).
+.PP
+Inferno gives special meaning in path names only to `/' and an initial `#',
+but individual file servers might impose further restrictions or conventions of their own.
+For instance, the set of characters allowed in names by
+.IR fs (3)
+ultimately depends on the host operating system;
+and
+.IR dial (2)
+and
+.IR cs (8)
+amongst others use `!' as a delimiter in network names,
+preventing their use in the names of network devices.
+.SS "File I/O"
+Files are opened for input or output
+by
+.B open
+or
+.B create
+(see
+.IR sys-open (2)).
+These calls return a reference to an object of type
+.B FD
+(file descriptor)
+that identifies the file to subsequent I/O calls,
+notably
+.B read
+and
+.B write
+(see
+.IR sys-read (2)).
+When the last reference to an
+.B FD
+disappears, the file descriptor is released—closed, in Unix parlance.
+The
+.B FD
+contains an integer file descriptor, similar to those in Unix, but the
+.B FD
+type is the one passed to Limbo I/O routines.
+.PP
+Integer file descriptor values range from 0 to
+.I n
+in the current system, where the upper bound
+depends on the underlying operating system.
+The system allocates the numbers by selecting the lowest unused descriptor.
+They may be reassigned using
+.B dup
+(see
+.IR sys-dup (2)).
+Integer file descriptor values are indices into a
+kernel-resident
+.IR "file descriptor table" ,
+which is inherited from the parent when a process is created by a Limbo
+.B spawn
+operation.
+A set of processes, called a
+.IR "file descriptor group" ,
+shares that table, so files opened by one process may be
+read and written by other processes in the group.  See
+.IR sys-pctl (2)
+for more information.
+.PP
+By convention,
+file descriptor 0 is the standard input,
+1 is the standard output,
+and 2 is the standard error output.
+The operating system is unaware of these conventions;
+it is permissible to close file 0,
+or even to replace it by a file open only for writing,
+but many programs will be confused by such chicanery.
+.PP
+Files are normally read or written in sequential order.
+The I/O position in the file is called the
+.IR "file offset"
+and may be set arbitrarily using the
+.B seek
+system call
+.RI ( sys-seek (2)).
+An offset can also be passed as a parameter to
+.B pread
+and
+.B pwrite
+(see
+.I sys-read (2)).
+.PP
+Inferno provides no guarantee of consistency should
+several processes access a file concurrently.
+Guaranteed synchronous writes are not available.
+Whether the exclusive-use attributes described in
+.IR sys-open (2)
+and
+.IR sys-stat (2)
+will be honoured for a file depends entirely on the underlying file server
+(eg,
+.IR fs (3)).
+Record locking in the underlying file system is not supported by Inferno.
+Processes can coordinate their file operations by other mechanisms.
+.PP
+Atomicity is guaranteed for byte counts no larger than the
+.I 9P
+message size;
+see
+.IR read (5).
+.PP
+Directories may be opened and read
+much like regular files (see
+.IR sys-dirread (2)).
+They contain an integral number of records,
+called
+.IR "directory entries" .
+Each entry is a machine-independent representation of
+the information about an existing file in the directory,
+including the
+name,
+ownership,
+permission,
+access dates,
+and so on.
+.PP
+The entry
+corresponding to an arbitrary file can be retrieved by
+.B stat
+or
+.B fstat
+(see
+.IR sys-stat (2));
+.B wstat
+and
+.B fwstat
+write back entries, thus changing the properties of a file.
+.PP
+New files are made with
+.B create
+and deleted with
+.B remove
+(see
+.IR sys-open (2)
+and
+.IR sys-remove (2)).
+Directories may not directly be written;
+.BR create ,
+.BR remove ,
+.BR wstat ,
+and
+.B fwstat
+change them.
+.SS "Process execution and control"
+A Limbo
+.IR process ,
+also called a
+.IR thread ,
+is the basic unit of computation for Limbo application programming
+in the Inferno operating system.
+.PP
+A newly spawned thread shares the same
+.I "address space
+as that of its creator thread.
+That is, the set of global  variables that is in scope to
+one is in scope to the other.
+A change made by one can be detected by the other.
+Since they are scheduled independently,
+they should synchronize their
+actions to share this data coherently.
+.PP
+The newly created thread also shares the same set of open file descriptors
+and the current working directory.
+.PP
+Processes are also organized into
+.I "process groups
+.RB ( pgrps )
+that represent the set of threads of a single
+application and can be terminated by a single kill request; see
+.IR prog (3).
+.PP
+A newly-spawned thread automatically inherits the following attributes:
+file name space (including shared
+current directory); file descriptor group; and process group.
+A thread can subsequently
+acquire a new, independent name space, new or modified file descriptor group,
+or new process group.
+See
+.IR  sys-pctl (2).
+.SS "User/Group Identity"
+The Inferno operating system maintains user identifier
+.RB ( uid )
+and group identifier
+.RB ( gid )
+strings
+for each process.
+These values are also attributes of files and directories.
+See
+.IR sys-stat (2)
+and
+.IR stat (5).
+A comparison of process and file identities take place when a process
+attempts to open or create a file.
+.PP
+When a path name crosses from one server to another the process identities are
+mapped by each server receiving a file request.
+.PP
+The
+.B uid
+and
+.B gid
+strings are assigned to the thread created
+when a user logs into Inferno and cannot be changed.
+.SH SOURCE
+.B /emu/port/inferno.c
+.br
+.B /os/port/inferno.c
+.SH DIAGNOSTICS
+System calls often return an integer status, or tuples containing results and
+an integer status,
+and follow the convention that a status of -1 is returned when an error occurred;
+a non-negative value (usually 0) is returned on success.
+If an error occurred, a detailed error message can be obtained for the
+most recent error, using the
+.RB ` %r '
+format of
+.IR sys-print (2).
+Exceptions to this general rule are noted in the
+`DIAGNOSTICS' sections.
+.PP
+From Limbo, system calls that return values on the heap, for instance strings in
+.B Dir
+structures returned by
+.IR sys-stat (2),
+and arrays of directory entries returned by
+.IR sys-readdir (2),
+can also raise ``out of memory: heap'' exceptions when attempting
+to create the return value.
--- /dev/null
+++ b/man/2/sys-bind
@@ -1,0 +1,201 @@
+.TH SYS-BIND 2
+.SH NAME
+bind, mount, unmount \- change file name space
+.SH SYNOPSIS
+.EX
+include "sys.m";
+sys := load Sys Sys->PATH;
+
+bind:     fn(name, old: string, flag: int): int;
+mount:    fn(fd: ref FD; afd: ref FD, old: string, flag: int, aname: string):
+             int;
+unmount:  fn(name, old: string): int;
+.EE
+.SH DESCRIPTION
+.B Bind
+and
+.B mount
+modify the file name space of the current process and its name space group.
+For both calls,
+.I old
+is the name of an existing file or directory in the
+current name space where the modification is to be made.
+The name
+.I old
+is
+evaluated
+as described in
+.IR sys-intro (2)
+except that no translation of the final path element is done.
+.PP
+For
+.BR bind ,
+.I name
+is the name of another (or possibly the same)
+existing file or directory in
+the current name space.
+After a successful
+.B bind
+call, the file name
+.I old
+is an alias for the object originally named by
+.IR name ;
+if the modification does not hide the original,
+.I name
+will also still refer to its original file.
+The evaluation of
+.I name
+happens at the time of the
+.BR bind ,
+not when the binding is later used.
+.PP
+The
+.I fd
+argument to
+.B mount
+is a file descriptor of an open pipe or network connection
+to a file server ready to receive 9P messages.
+The
+.I old
+file must be a directory.
+After a successful
+.BR mount ,
+the file tree
+.I served
+(see below) by
+.I fd
+will be visible with its root directory having name
+.IR old .
+If the requested service requires authentication, the file descriptor
+.I afd
+must be open on an authentication file for the
+requested service;
+otherwise it should be
+.BR nil .
+.PP
+The
+.I flag
+controls details of the modification made to the name space.
+In the following,
+.I new
+refers to the file
+as defined by
+.I name
+or the root directory served by
+.IR fd .
+Either both
+.I old
+and new files must be directories,
+or both must not be directories.
+.I Flag
+can be one of:
+.TF Sys->MBEFORE
+.TP 
+.B Sys->MREPL
+Replace the
+.I old
+file by the new one.
+Henceforth, an evaluation of
+.I old
+will be translated to the new file.
+If they are directories (for
+.BR mount ,
+this condition is true by definition),
+.I old
+becomes a
+.I "union directory"
+consisting of one directory (the new file).
+.TP
+.B Sys->MBEFORE
+Both the
+.I old
+and new files must be directories.
+Add the constituent files of the new directory
+to the union directory at
+.I old
+so its contents appear first in the union.
+After a
+.B Sys->MBEFORE
+.B bind
+or
+.BR mount ,
+the new directory will be searched first when evaluating file names
+in the union directory.
+.TP
+.B Sys->MAFTER
+Like
+.B Sys->MBEFORE
+but the new directory goes at the end of the union.
+.PD
+.PP
+In addition, there is a
+.B Sys->MCREATE
+flag that can be OR'd with any of the above.
+When a
+.B create
+call (see
+.IR sys-open (2))
+attempts to create in a union directory, and the file does not exist,
+the elements of the union are searched in order until one is found
+with
+.B Sys->MCREATE
+set.
+The file is created in that directory; if that attempt fails,
+the
+.B create
+fails.
+.PP
+With
+.BR mount ,
+the file descriptor
+.I fd
+must be open for reading and writing
+and connected to a file server.
+After the
+.BR mount ,
+the file tree starting at
+.I old
+is served by a kernel
+.IR mnt (3)
+device.
+That device will turn operations in the tree into messages to the server on
+.IR fd .
+.I Aname
+selects among different
+file trees on the server; the
+empty (or nil)
+string chooses the default tree.
+.PP
+The effects of
+.B bind
+and
+.B mount
+can be undone by
+.BR unmount .
+If
+.I name
+is
+.BR nil ,
+everything bound to or mounted upon
+.I old
+is unbound or unmounted.
+If
+.I name
+is not
+.BR nil ,
+it is evaluated as described above for
+.BR bind ,
+and the effect of binding or mounting that particular result on
+.I old
+is undone.
+.SH SEE ALSO
+.IR sys-intro (2)
+.SH DIAGNOSTICS
+The return value is a positive integer (a unique sequence number) for
+success, \-1 for failure.
+.SH BUGS
+.B Mount
+will not return until it has successfully attached
+to the file server, so the thread doing a
+.B mount
+cannot be the one serving.
--- /dev/null
+++ b/man/2/sys-byte2char
@@ -1,0 +1,68 @@
+.TH SYS-BYTE2CHAR 2
+.SH NAME
+byte2char, char2byte \- convert between bytes and characters
+.SH SYNOPSIS
+.EX
+include "sys.m";
+sys := load Sys Sys->PATH;
+
+byte2char: fn(buf: array of byte, n: int): (int, int, int);
+char2byte: fn(c: int, buf: array of byte, n: int): int;
+.EE
+.SH DESCRIPTION
+.B Byte2char
+converts a byte sequence to one Unicode character.
+.I Buf
+is an array of bytes and
+.I n
+is the index of the first byte to examine in the array.
+The returned tuple, say
+.BI ( c ,
+.IB length ,
+.IB status )\f1,
+specifies the result of the translation:
+.I c
+is the resulting Unicode character,
+.I status
+is non-zero if the bytes are a valid UTF sequence and zero otherwise,
+and
+.I length
+is set to the number of bytes consumed by the translation.
+If the input sequence is not long enough to determine its validity,
+.B byte2char
+consumes zero bytes;
+if the input sequence is otherwise invalid,
+.B byte2char
+consumes one input byte and generates an error character
+.RB ( Sys->UTFerror ,
+.BR 16r80 ),
+which prints in most fonts as a boxed question mark.
+.PP
+.B Char2byte
+performs the inverse of
+.BR byte2char .
+It translates a Unicode character,
+.IR c ,
+to a UTF byte sequence, which
+is placed in successive bytes starting at
+.IR buf [\c
+.IR n ].
+The longest UTF sequence for a single Unicode character is
+.B Sys->UTFmax
+(4) bytes.
+If the translation succeeds,
+.B char2byte
+returns the number of bytes placed in the buffer.
+If the buffer is too small to hold the result,
+.B char2byte
+returns zero and leaves the array unchanged.
+.SH SOURCE
+.B /libinterp/runt.c
+.SH SEE ALSO
+.IR sys-intro (2),
+.IR sys-utfbytes (2),
+.IR utf (6)
+.SH DIAGNOSTICS
+A run-time error occurs if
+.I n
+exceeds the bounds of the array.
--- /dev/null
+++ b/man/2/sys-chdir
@@ -1,0 +1,40 @@
+.TH SYS-CHDIR 2
+.SH NAME
+chdir \- change working directory
+.SH SYNOPSIS
+.EX
+include "sys.m";
+sys := load Sys Sys->PATH;
+
+chdir:  fn(path: string): int;
+.EE
+.SH DESCRIPTION
+.B Chdir
+changes the working directory
+of the invoking process and its file name space group to
+.IR path .
+.PP
+The working directory is the starting point for
+evaluating file names that do not begin with
+.B /
+or
+.BR # ,
+as explained in
+.IR sys-intro (2).
+.PP
+When Inferno boots,
+the initial process has
+.B /
+for its working directory.
+.PP
+Applications that invoke
+.B chdir
+normally use the
+.B FORKNS
+option of
+.IR sys-pctl (2)
+to prevent the change from affecting the surrounding environment.
+.SH DIAGNOSTICS
+Returns 0 on success; -1 on failure.
+.SH SEE ALSO
+.IR sys-intro (2)
--- /dev/null
+++ b/man/2/sys-dial
@@ -1,0 +1,255 @@
+.TH SYS-DIAL 2
+.SH NAME
+announce, dial, listen \- make network connections
+.SH SYNOPSIS
+.EX
+include "sys.m";
+sys := load Sys Sys->PATH;
+
+Connection: adt
+{
+    dfd:  ref FD;  # data file
+    cfd:  ref FD;  # control file
+    dir:  string;  # pathname of line directory
+};
+
+announce: fn(addr: string):           (int, Connection);
+dial:     fn(addr, local: string):    (int, Connection);
+listen:   fn(c: Connection):          (int, Connection);
+.EE
+.SH DESCRIPTION
+These routines in the
+.B Sys
+module are still used by some existing code.
+They are deprecated, and new applications should use the
+.B Dial
+module described in
+.IR dial (2).
+.PP
+These routines establish network connections.
+Their description uses the following definitions:
+.TF network
+.PD
+.TP
+.I addr
+is a network address in one of the following forms:
+.br
+.IP
+.IB network ! netaddr ! service\f1
+.br
+.IB network ! netaddr\f1
+.br
+.IR netaddr
+.TP
+.I network
+Any directory listed in
+.B /net
+(eg,
+.BR tcp ),
+or the special token,
+.BR net .
+The name
+.B net
+acts as a free variable that stands for any network in common
+between the source and
+.IR netaddr .
+A network name can be preceded by the full path name of a directory
+of networks, using the form
+.I /dir/network
+(eg,
+.BR /net.alt/tcp ).
+.TP
+.I netaddr
+A host name, a domain name, a network address,
+or a meta-name of the form
+.BI $ attribute\f1,
+which
+is replaced by
+.I value
+from the corresponding attribute-value pair
+in the connection server data base (see
+.IR db (6)).
+.PP
+The functions
+.B dial
+and
+.B announce
+translate a given
+.I addr
+to an actual network address using
+the connection server
+.IR cs (8).
+If a logical name
+.I addr
+corresponds to several network addresses,
+for instance if a destination machine has several interfaces,
+.I cs
+will return them all.
+In particular, if
+.I addr
+is
+.BR net ,
+.I cs
+will return addresses on
+all networks that are common to source and destination.
+The translation procedure accesses
+.I cs
+using its interface file
+.BR cs ,
+which is sought as follows:
+first, in an explicit directory
+.BI / dir
+if one was given in
+.IR network ;
+second, in the standard directory
+.BR /net ;
+and finally in the directory
+.BR /net.alt
+.RB ( dial
+only).
+If the connection server cannot be found,
+the
+.I addr
+is used as-is.
+.PP
+If a connection attempt is successful, the
+.B dir
+member of the resulting
+.B Connection
+will be
+the path name of a
+.I line directory
+that has files for accessing the connection.
+One line directory exists for each possible connection.
+The
+.B data
+file in the line directory is opened to
+make a connection, and read and written to communicate with the destination.
+The
+.B ctl
+file in the line directory can be used to send commands to the line.
+See
+.IR ip (3)
+for messages that can be written to the
+.B ctl
+file.
+The last close of the
+.B data
+or
+.B ctl
+file will close the connection.
+The
+.B remote
+file in the line directory contains the address called; the file
+.B local
+contains the local address assigned.
+.PP
+The
+.B dial
+routine
+makes a call to destination
+.I addr
+on a multiplexed network.
+If the connection server returns several addresses,
+.B dial
+tries each in turn, until a connection is made
+or no addresses remain to be tried.
+It returns a
+.B Connection
+containing a file descriptor
+.B dfd
+open for reading and writing the
+.B data
+file in the line directory,
+and a file descriptor
+.B cfd
+open for reading and writing the
+.B ctl
+file.
+If
+.IR local
+is non-empty, and
+the network allows the local address to be set,
+as is the case with UDP and TCP port numbers,
+the local address will be set to
+.IR local .
+.PP
+.B Announce
+and
+.B listen
+are the complements of
+.BR dial .
+.B Announce
+establishes a network name to which incoming calls can be made.
+In
+.IR addr ,
+.I netaddr
+gives the name or address of one of the local host's interfaces on which to listen for
+calls to the given
+.IR service ;
+it can be
+.B *
+to listen for calls on any interface on
+.IR network .
+.B Announce
+returns a
+.B Connection
+structure in which only the
+.B cfd
+descriptor is open, on the control file representing the announcement.
+.B Listen
+takes as its only argument the
+.B Connection
+structure of a successful call to
+.BR announce .
+When a call is received,
+.B listen
+returns an open
+.B Connection
+structure as if from
+.BR dial ,
+except that only the
+.B cfd
+descriptor is open,
+.B dfd
+is nil,
+and the caller must open the data file for itself.
+.SH EXAMPLES
+.PP
+Make a call and return an open file descriptor to
+use for communications:
+.IP
+.EX
+callkremvax(): (int, Connection)
+{
+	return sys->dial("tcp!kremvax!80", nil);
+}
+.EE
+.PP
+Call the local certificate signer:
+.IP
+.EX
+dialsigner(service: string): (int, Connection)
+{
+	return sys->dial("net!$SIGNER!inflogin", nil);
+}
+.EE
+.SH SOURCE
+.B /emu/port/inferno.c
+.br
+.B /emu/port/dial.c
+.br
+.B /os/port/inferno.c
+.br
+.B /os/port/dial.c
+.SH DIAGNOSTICS
+The integer valued functions return 0 on success and -1 on error;
+the system error string is set.
+The integer component of the tuple returned by the other functions
+follows the same convention.
+.SH BUGS
+Note that
+.B listen
+does not open the
+.B data
+file.
--- /dev/null
+++ b/man/2/sys-dirread
@@ -1,0 +1,59 @@
+.TH SYS-DIRREAD 2
+.SH NAME
+dirread \- read directory
+.SH SYNOPSIS
+.EX
+include "sys.m";
+sys := load Sys Sys->PATH;
+
+dirread:  fn(fd: ref FD): (int, array of Dir);
+.EE
+.SH DESCRIPTION
+.B Dirread
+reads the contents of the directory pointed to by the open file descriptor
+.IR fd ,
+returning a tuple containing an array
+with one
+.B Dir
+structure for each directory entry read.
+These
+.B Dir
+structures are equivalent to the result of a
+.B stat
+call on each file in the directory.
+See
+.IR sys-stat (2)
+for a description of
+.B stat
+and
+.BR Dir .
+.PP
+A successful
+.B dirread
+returns a tuple giving the number of entries read and the resulting array.
+A return of (0,nil) indicates the end of the directory.
+Directory entries are variable length in general;
+the file offset is advanced by the number of bytes actually read.
+.PP
+Seeks (see
+.IR sys-seek (2))
+are allowed on directories only to seek to the start.
+.PP
+In general, several calls to
+.B dirread
+will be needed to read the whole directory.
+.I Readdir (2)
+provides functions that return all the directory entries at once,
+optionally sorted.
+.SH SEE ALSO
+.IR readdir (2),
+.IR sys-intro (2),
+.IR sys-open (2),
+.IR sys-read (2),
+.IR sys-seek (2),
+.IR sys-stat (2)
+.SH DIAGNOSTICS
+The integer in
+.BR dirread 's
+return tuple has a value of -1 on error, and 0 on end of file.
+The array element of the tuple is nil in both cases.
--- /dev/null
+++ b/man/2/sys-dup
@@ -1,0 +1,75 @@
+.TH SYS-DUP 2
+.SH NAME
+dup, fildes \- duplicate an open file descriptor
+.SH SYNOPSIS
+.EX
+include "sys.m";
+sys := load Sys Sys->PATH;
+
+dup:    fn(oldfd, newfd: int):  int;
+fildes: fn(fd: int):            ref FD;
+.EE
+.fi
+.SH DESCRIPTION
+The Limbo programming language and its libraries
+manage I/O via references to instances of abstract data type,
+.BR FD ,
+called a
+.IR "Limbo file descriptor",
+or simply `file descriptor' when the context is understood.
+.B FD
+holds an integer-valued file descriptor, the form used
+by the operating system, in a structure that can be reference counted
+and garbage collected.
+When the
+.B FD
+value is reclaimed, the system automatically closes the associated integer file descriptor.
+There are occasions when a program must access the underlying
+integer file descriptor, such as when rearranging the standard input
+and output for a new subprocess.
+.PP
+The
+.B dup
+call takes a valid integer file descriptor,
+.IR oldfd ,
+referring to an open file,
+and
+returns a new integer file descriptor referring to the same file.
+If
+.I newfd
+is in the range of legal file descriptors,
+.B dup
+will use that for the new file descriptor
+(closing any old file associated with
+.IR newfd );
+if
+.I newfd
+is \-1 the system chooses the lowest available file descriptor.
+If a suitable file descriptor cannot be found,
+.B dup
+returns \-1.
+.PP
+.B Fildes
+duplicates the integer file descriptor
+.IR fd ,
+as if by
+.BI "sys->dup(" fd ",-1"),
+and returns a reference to the new descriptor as an
+.B FD
+value,
+making it usable by other functions in
+.BR Sys ,
+such as
+.IR sys-print (2)
+and
+.IR sys-read (2).
+(Note that as described above, the newly-allocated file descriptor will be closed automatically when the
+.B FD
+value is reclaimed.)
+.B Fildes
+returns nil
+if it cannot duplicate
+.IR fd .
+.SH SEE ALSO
+.IR sys-intro (2),
+.IR sys-open (2)
--- /dev/null
+++ b/man/2/sys-export
@@ -1,0 +1,116 @@
+.TH SYS-EXPORT 2
+.SH NAME
+export \- export a name space
+.SH SYNOPSIS
+.EX
+include "sys.m";
+sys := load Sys Sys->PATH;
+
+export:   fn(fd: ref FD, dir: string, flag: int):   int;
+.EE
+.SH DESCRIPTION
+.B Export
+receives and replies
+to 9P requests from a client on a connection represented by
+.IR fd ,
+for file operations on the part of the current file name space
+rooted at
+.IR dir ,
+which is thus exported.
+This is the server end of the client's
+.B mount
+call.
+Names presented by the client are interpreted relative to directory
+.IR dir ,
+which can be adjusted using
+.IR sys-pctl (2)
+and
+.IR sys-bind (2)
+before export.
+The file descriptor
+.I fd
+must be open for reading and writing, and neither mounted elsewhere nor already exported.
+.PP
+Commonly,
+.BR export 's
+first argument is a file descriptor open on the data file in the
+.B dir
+of a
+.B Connection
+returned by
+.B listen
+(see
+.IR dial (2)).
+Before calling
+.BR export ,
+the connection on
+.I fd
+can optionally be authenticated and set for encryption or digesting using the
+functions in
+.IR security-auth (2).
+.PP
+The
+.B export
+function takes two mutually exclusive flags:
+.TP
+.B Sys->EXPWAIT
+.B Export
+blocks until all client requests are complete.
+.TP
+.B Sys->EXPASYNC
+Client requests are handled by a background (kernel) process.
+.B Export
+returns immediately.
+The serving process terminates when the client hangs up.
+.SH EXAMPLES
+.PP
+Export a given directory on
+.BR fd ,
+protecting it from subsequent changes:
+.IP
+.EX
+exportdir(fd: ref Sys->FD, dir: string, pid: chan of int)
+{
+	pid <-= sys->pctl(Sys->FORKNS|Sys->FORKENV|Sys->NEWFD, fd.fd :: nil);
+	sys->export(fd, dir, Sys->EXPWAIT);
+}
+.EE
+.PP
+The
+.B FORKNS
+given to
+.B pctl
+forks the name space, and
+prevents the
+.B sys->export
+from seeing the effects of subsequent mounts by the process
+that calls or spawns
+.BR exportdir .
+The
+.B exportdir
+function above might be called using:
+.IP
+.EX
+pid := chan of int;
+spawn exportdir(fd, "/", pid);
+expid := <-pid;
+.EE
+.PP
+Service will stop automatically when the connection
+.B fd
+returns end-of-file (eg, when it hangs up),
+but it can also be stopped locally by killing
+.BR expid .
+.EE
+.SH SOURCE
+.B /emu/port/inferno.c
+.br
+.B /emu/port/exportfs.c
+.br
+.B /os/port/inferno.c
+.br
+.B /os/port/exportfs.c
+.SH DIAGNOSTICS
+.B Export
+returns a non-negative value on success and -1 on error;
+the system error string is set.
--- /dev/null
+++ b/man/2/sys-fauth
@@ -1,0 +1,61 @@
+.TH SYS-FAUTH 2
+.SH NAME
+fauth \- set up authentication on a file descriptor to a file server
+.SH SYNOPSIS
+.EX
+include "sys.m";
+sys := load Sys Sys->PATH;
+
+fauth:  fn(fd: ref FD, aname: string): ref FD;
+.EE
+.SH DESCRIPTION
+.B Fauth
+provides a means for the current user to authenticate to access
+resources available through the 9P connection represented by
+.IR fd .
+The return value is a file descriptor, conventionally called
+.IR afd ,
+that is subsequently used to execute the authentication protocol
+for the server.
+After successful authentication,
+.I afd
+may be passed as the second argument to a subsequent
+.B mount
+call (see
+.IR sys-bind (2)),
+with the same
+.IR aname,
+as a ticket-of-entry for the user.
+.PP
+If
+.B fauth
+returns nil, the error case, that means the file server does not require
+authentication for the connection, and
+.I afd
+should be nil
+in the call to
+.BR mount.
+.ig
+.PP
+It is rare to use
+.IR fauth
+directly; more commonly
+.I amount
+(see
+.IR auth (2))
+is used.
+..
+.SH SEE ALSO
+.IR attach (5),
+.IR security-auth (2)
+.ig
+(particularly
+.BR amount ),
+.IR authsrv (6)
+..
+.SH DIAGNOSTICS
+The system error string is set on error,
+including the server not requiring authentication,
+and
+.B fauth
+returns nil.
--- /dev/null
+++ b/man/2/sys-fd2path
@@ -1,0 +1,46 @@
+.TH FD2PATH 2
+.SH NAME
+fd2path \- return file name associated with file descriptor
+.SH SYNOPSIS
+.EX
+include "sys.m";
+sys := load Sys Sys->PATH;
+
+fd2path(fd: ref FD): string;
+.EE
+.SH DESCRIPTION
+As described in
+.IR intro (2),
+the kernel stores a rooted path name with every open file or directory;
+typically, it is the name used in the original access of the file.
+.B Fd2path
+returns the path name associated with open file descriptor
+.IR fd .
+(It returns nil if an error occurs.)
+.PP
+Changes to the underlying name space do not update the path name
+stored with the file descriptor.
+Therefore,
+the path returned by
+.B fd2path
+may no longer refer to the same file (or indeed any file)
+after some component directory or file in the path has been removed, renamed
+or rebound.
+.PP
+As an example,
+.IR workdir (2)
+is implemented by opening
+.B .
+and executing
+.B fd2path
+on the resulting file descriptor.
+.SH SEE ALSO
+.IR bind (1),
+.IR ns (1),
+.IR sys-bind (2),
+.IR sys-intro (2),
+.IR workdir (2),
+.IR prog (3)
+.SH DIAGNOSTICS
+.B Fd2path
+returns nil on error and sets the system error string.
--- /dev/null
+++ b/man/2/sys-file2chan
@@ -1,0 +1,171 @@
+.TH SYS-FILE2CHAN 2
+.SH NAME
+file2chan \- create file connected to Limbo channel
+.SH SYNOPSIS
+.EX
+include "sys.m";
+sys := load Sys Sys->PATH;
+
+Rread:  type chan of (array of byte, string);
+Rwrite: type chan of (int, string);
+FileIO: adt
+{
+    read:   chan of (int, int, int, Rread);
+    write:  chan of (int, array of byte, int, Rwrite);
+};
+
+file2chan:  fn(dir, file: string): ref FileIO;
+.EE
+.fi
+.SH DESCRIPTION
+.B File2chan
+presents an interface for binding Limbo channels to files in the file name space.
+A
+.I server
+program calls
+.B file2chan
+to create a
+.I file
+in a directory
+.IR dir ,
+which must be a directory on which device
+.RB ` #s '
+has been bound
+(see
+.IR srv (3)).
+A
+.I client
+program can open the file for reading or writing (see
+.IR sys-open (2)
+and
+.IR sys-read (2))
+to communicate with the server.
+.PP
+.B File2chan
+returns a
+.B FileIO
+type holding two channels used to deliver tuples representing
+the contents of the
+.B Tread
+and
+.B Twrite
+9P messages received by the system on the server's behalf; see
+.IR intro (5).
+.PP
+When the client invokes the
+.B read
+system call on the file, the server
+receives a tuple, say
+.BI ( offset\fP,\fP\ count\fP,\fP\ fid\fP,\fP\ rc )\f1,
+on the
+.B read
+channel.
+The request asks for
+.I count
+bytes of data, starting at
+.I offset
+bytes from the beginning of the file
+associated with
+.IR fid .
+The server sends its reply to the client by transmitting a tuple, say
+.BI ( data\fP,\fP\ error )\f1,
+that contains the
+.I data
+for the
+.BR read ,
+on the channel
+.I rc
+received as part of the
+.B read
+tuple.
+If the request was successful, the
+.I error
+string should be
+.BR nil .
+If an error occurred,
+.I error
+should be a diagnostic string,
+and the
+.I data
+array should be
+.BR nil .
+The client blocks in the
+.B read
+system call until the server sends its reply.
+The client receives only
+.I count
+bytes even if
+.I data
+is larger.
+.PP
+When the client does a
+.B write
+system call on the file,
+the server receives a tuple,
+.BI ( offset\fP,\fP\ data\fP,\fP\ fid\fP,\fP\ wc )\f1,
+on the
+.B write
+channel.
+A
+.BI ( count\fP,\fP\ error )
+response is sent back on the
+.I wc
+channel received in the
+.B write
+tuple:
+.I count
+gives the number of bytes written (zero if an error occurs), and
+.I error
+is an empty or
+.B nil
+string or an explanation of the problem.
+.PP
+The
+.I fid
+received by the server can be used to manage the multiplexing of multiple
+active clients sharing a single file; see
+.IR intro (5)
+for details.
+Each distinct
+open of the file will yield a distinct
+.IR fid ,
+unique to that open, which will appear in the read and write
+messages corresponding to reads and writes on that open instance of the file.
+.PP
+When the client closes the file,
+the server will be sent a
+.B read
+message with a nil
+.IR rc ,
+then a
+.B write
+message with a nil
+.IR wc .
+A server typically ignores the former and uses the latter as a signal to stop processing for the given client
+(more precisely, the given
+.IR fid ).
+Note that the
+.I fid
+can later be re-used by another client.
+.SH SOURCE
+.B /emu/port/devsrv.c
+.br
+.B /os/port/devsrv.c
+.SH SEE ALSO
+.IR sh-file2chan (1),
+.IR sys-intro (2),
+.IR sys-open (2),
+.IR sys-read (2),
+.IR sys-bind (2),
+.IR intro (5)
+.SH BUGS
+.B Read
+and
+.B write
+system calls for the file will not return until the
+server sends its reply on the appropriate channel,
+so the process doing the
+.B read
+or
+.B write
+cannot be the one serving.
--- /dev/null
+++ b/man/2/sys-fversion
@@ -1,0 +1,71 @@
+.TH SYS-FVERSION 2
+.SH NAME
+fversion \- initialize 9P connection and negotiate version
+.SH SYNOPSIS
+.EX
+include "sys.m";
+sys := load Sys Sys->PATH;
+
+fversion: fn(fd: ref FD, bufsize: int, version: string): (int, string);
+.EE
+.SH DESCRIPTION
+.B Fversion
+initialises the 9P connection represented by
+.I fd
+and negotiates the maximum message size and the version of the protocol to be used.
+.PP
+The
+.I bufsize
+determines the size of the I/O buffer used to stage 9P requests to the server,
+subject to the constraints of the server itself.
+The
+.I version
+is a text string that represents the highest version level the protocol will support.
+.PP
+.B Fversion
+returns a tuple
+.RI ( n , useversion )
+where
+.I n
+is -1 if the request was rejected, and otherwise
+.I n
+is
+.I bufsize
+and
+.I useversion
+is a string representing the negotiated, possibly lower, version of the protocol.
+.PP
+Default values of zero for
+.I bufsize
+and the empty string for
+.I version
+will negotiate sensible defaults for the connection.
+.PP
+The interpretation of the version strings is defined in
+.IR version (5).
+.PP
+It is rare to use
+.BR fversion
+directly; usually the default negotiation performed
+by the kernel during
+.B mount
+(see
+.IR sys-bind (2))
+is sufficient.
+.SH SEE ALSO
+.IR sys-fauth (2),
+.IR intro (5),
+.IR version (5).
+.SH DIAGNOSTICS
+.B Fversion
+returns a value of -1 for
+.I n
+on error, including failure to negotiate acceptable values,
+and sets the system error string.
+.SH BUGS
+The returned value of
+.I n
+when no error occurs should be the negotiated message size
+but is currently the original
+.I bufsize
+parameter.
--- /dev/null
+++ b/man/2/sys-iounit
@@ -1,0 +1,35 @@
+.TH SYS-IOUNIT 2
+.SH NAME
+iounit \- return size of atomic I/O unit for file descriptor
+.SH SYNOPSIS
+.EX
+include "sys.m";
+sys := load Sys Sys->PATH;
+
+iounit:	fn(fd: ref FD): int;
+.EE
+.SH DESCRIPTION
+Reads and writes of files are transmitted using the 9P protocol (see
+.IR intro (5))
+and in general, operations involving large amounts of data must be
+broken into smaller pieces by the operating system.
+The `I/O unit' associated with each file descriptor records the maximum
+size, in bytes, that may be read or written without breaking up the transfer.
+.PP
+The
+.B iounit
+system call returns the I/O unit size for the open file
+.IR fd .
+Certain file descriptors, particularly those associated with devices, may
+have no specific I/O unit, in which case
+.B iounit
+will return zero.
+.SH SOURCE
+.B /emu/port/inferno.c
+.br
+.B /os/port/inferno.c
+.SH SEE ALSO
+.IR sys-dup (2),
+.IR read (5)
+.SH DIAGNOSTICS
+Returns zero if any error occurs or if the I/O unit size of the fd is unspecified or unknown.
--- /dev/null
+++ b/man/2/sys-millisec
@@ -1,0 +1,24 @@
+.TH SYS-MILLISEC 2
+.SH NAME
+millisec \- millisecond timer
+.SH SYNOPSIS
+.EX
+include "sys.m";
+sys := load Sys Sys->PATH;
+
+millisec:	fn(): int;
+.EE
+.SH DESCRIPTION
+.B Millisec
+returns the value of an internal system clock, in milliseconds.
+The actual resolution of the clock is a property of the underlying
+system and may be coarser than a millisecond.
+The epoch is arbitrary, so
+.B millisec
+is useful for measuring durations but not absolute time.
+If the system operates continuously, the millisecond counter overflows
+every few months;
+a reboot resets the epoch.
+.SH SEE ALSO
+.IR sys-intro (2),
+.IR sys-sleep (2)
--- /dev/null
+++ b/man/2/sys-open
@@ -1,0 +1,135 @@
+.TH SYS-OPEN 2
+.SH NAME
+open, create \- open a file for reading or writing, create file
+.SH SYNOPSIS
+.EX
+include "sys.m";
+sys := load Sys Sys->PATH;
+
+create: fn(file: string, omode, perm: int): ref FD;
+open:   fn(file: string, omode: int):       ref FD;
+.EE
+.SH DESCRIPTION
+.B Open
+opens the
+.I file
+for I/O and returns an associated file descriptor.
+.I Omode
+is one of
+.BR Sys->OREAD ,
+.BR Sys->OWRITE ,
+or
+.BR Sys->ORDWR ,
+asking for permission to read, write, or read and write, respectively.
+There are two values that can be OR'd with those to form
+.IR omode :
+.B Sys->OTRUNC
+says to truncate the file before opening it, which requires write permission
+even if
+.I omode
+is
+.BR Sys->OREAD ;
+and
+.B Sys->ORCLOSE
+says to remove the file when it is closed (ie, when the last reference
+to this file descriptor goes away).
+.PP
+.B Open
+returns
+.B nil
+if the file does not exist, if the file name is unacceptable, if the user does not have
+permission to open it as requested
+(see
+.IR sys-stat (2)
+for a description of permissions),
+or if any other error occurs.
+.PP
+.B Create
+creates a new
+.I file
+or prepares to rewrite an existing
+.IR file ,
+opens it according to
+.I omode
+(as described for
+.BR open ),
+and returns an associated file descriptor.
+.PP
+If the file is new,
+the owner is set to the
+.I "user id
+of the creating process group,
+the group to that of the containing directory,
+and the permissions to
+.I perm
+ANDed with the permissions of the containing directory.
+The bits in 
+.I perm
+are the same as those
+in the file mode returned by 
+.IR sys-stat (2).
+.PP
+If the file already exists,
+it is truncated to 0 length,
+but the permissions, owner, and group remain unchanged.
+.PP
+The created file will be a directory if the
+.B Sys->DMDIR
+bit is set in
+.IR perm ,
+and
+.I omode
+is
+.BR Sys->OREAD .
+The file will be exclusive-use if the
+.B Sys->DMEXCL
+bit is set in
+.I perm
+and the underlying file server supports it;
+see
+.IR open (5)
+for details.
+It will be append-only if the
+.B Sys->DMAPPEND
+bit is set, and the underlying file server supports it.
+.PP
+.B Create
+returns
+.B nil
+if the path up to the last element of
+.I file
+cannot be evaluated, if the file name is unacceptable,
+if the user does not have write permission in the final directory,
+if the file already exists and does not permit the access defined by
+.IR omode,
+or if any other error occurs.
+.PP
+If the file is new and the directory in which it is created is
+a union directory (see
+.IR sys-intro (2))
+then the constituent directory where the file is created
+depends on the structure of the union: see
+.IR sys-bind (2).
+.PP
+Since create may succeed even if the file exists, a special mechanism
+is necessary for applications that require an atomic create operation.
+If the
+.B Sys->OEXCL
+bit is set in the mode for a create, the call succeeds only if
+the file does not already exist; see
+.IR open (5)
+for details.
+.PP
+There is no explicit ``close'' routine:
+when the last reference to the file descriptor is released,
+the system closes the associated file.
+For devices and network protocols where shutdown must be guaranteed,
+write a
+.B hangup
+message to the associated control file and use the return value of the
+.B write
+to verify closure.
+.SH SEE ALSO
+.IR sys-intro (2),
+.IR sys-bind (2),
+.IR sys-stat (2)
--- /dev/null
+++ b/man/2/sys-pctl
@@ -1,0 +1,141 @@
+.TH SYS-PCTL 2
+.SH NAME
+pctl \- process control
+.SH SYNOPSIS
+.EX
+include "sys.m";
+sys := load Sys Sys->PATH;
+
+pctl: fn(flags: int, movefd: list of int): int;
+.EE
+.SH DESCRIPTION
+A newly spawned Limbo process (also known as a thread)
+shares with its parent process a number of resources and properties,
+such as file name space, open file descriptors, current working directory,
+and so on.
+.B Pctl
+controls this sharing, allowing a process to gain a copy of a resource
+rather than to share it, to start with a null resource, and so on.
+.PP
+The set of processes sharing a property are called a group;
+for example the set of processes sharing a name space are called a name space group.
+Each process is a member of a
+.IR "process group" ,
+typically the set of threads functioning as a single program.
+All the members of a process group
+may be terminated at once using the
+.B killgrp
+control message in the
+.IR prog (3)
+device.
+.PP
+A call to
+.B pctl
+affects the calling process and, indirectly according to
+.IR flags ,
+any other processes sharing properties with it.
+The
+.I flags
+to
+.B pctl
+are:
+.TF FORKENV
+.PD
+.TP
+.B NEWFD
+Give the process a new file descriptor group.
+All file descriptors are closed except those listed in
+.IR movefd ,
+which are preserved in the new group.
+After this call, changes the process makes to its set of open file descriptors
+will not be visible to other processes.
+.TP
+.B FORKFD
+Place the process in a new file descriptor group
+containing a copy
+of the current set of file descriptors.
+The file descriptors listed in
+.I movefd
+are closed in the
+.I old
+group.
+After this call, changes the process makes to its set of open file descriptors
+will not be visible to other processes.
+.TP
+.B NEWNS
+Place the process in a new file name space group in which the current directory
+is made the root directory,
+.LR / ,
+of the new name space.
+The current directory is unaffected by this call.
+.TP
+.B FORKNS
+Place the process in a new file name space group
+containing a copy
+of the current name space.
+After this call, changes the process makes to its name space, including
+.B chdir
+calls,
+will not affect other processes.
+.TP
+.B NODEVS
+Set the current file name space group to prevent subsequent access to
+the roots of file trees implemented by a device driver (ie, the use of path
+names beginning with
+.BR # ,
+as described by
+.IR intro (3)).
+Even after
+.B NODEVS
+the following devices can be attached, which are either
+private or can be separately controlled:
+.IR pipe (3) ,
+.IR env (3),
+.IR srv (3)
+and
+.IR ssl (3).
+.TP 
+.B NEWENV
+Place the process in a new empty environment group containing
+no environment variables.
+After this call, changes the process makes to its environment will
+not affect other processes.
+.TP 
+.B FORKENV
+Place the process in a new environment group containing
+a copy of the current environment variables.
+After this call, changes the process makes to its environment will
+not affect other processes.
+.TP
+.B NEWPGRP
+Establish a new process group with a group id equal to that of the pid
+of the calling process.
+.PP
+The Inferno shell
+.IR sh (1)
+uses
+.B FORKFD
+when starting a command;
+its
+.I pctl
+built-in (see
+.IR sh-std (1))
+can invoke the other effects when needed.
+The window manager
+.IR wm (1)
+uses
+.B NEWPGRP|FORKFD
+when starting a window manager application.
+A network server might use
+.B NEWPGRP|FORKNS|FORKFD|FORKENV
+to insulate itself completely from services it starts.
+.PP
+The return value of
+.B pctl
+is the numerical process id of  the calling process, which can be used for example to
+access its
+.IR prog (3)
+files.
+.SH SEE ALSO
+.IR sh-std (1),
+.IR sys-intro (2)
--- /dev/null
+++ b/man/2/sys-pipe
@@ -1,0 +1,45 @@
+.TH SYS-PIPE 2
+.SH NAME
+pipe \- create an interprocess channel
+.SH SYNOPSIS
+.EX
+include "sys.m";
+sys := load Sys Sys->PATH;
+
+pipe: fn(fds: array of ref FD): int;
+.EE
+.SH DESCRIPTION
+.B Pipe
+creates a buffered channel for interprocess I/O via file descriptors.
+It allocates a pipe from the pipe device and returns in the array
+.B fds
+file descriptors for the two pipe ends. Both returned file descriptors are opened for both reading and writing
+.RB ( Sys->ORDWR ).
+Data written on one file descriptor can be read from the other.
+The details of flow control and buffering are given in
+.IR pipe (3).
+When no references remain to the file descriptor representing
+one end of a pipe, and all remaining data has been read at the other end,
+subsequent reads at that end will return 0 bytes.
+Writes to a pipe with no reader produce an exception.
+.PP
+The array
+.B fds
+passed to the system call must have a length of at least 2;
+only entries 0 and 1 are updated.
+.PP
+Limbo applications typically use typed Limbo channels, not pipes, for efficient
+communication by cooperating processes.
+Pipes are still useful, however,
+to connect applications that do not (or cannot) share such channels,
+or when a system interface requires a file descriptor.
+For instance, a process that serves the 9P protocol can
+pass the file descriptor for one end of a pipe to
+.B Sys->mount
+(see
+.IR sys-bind (2)),
+and read and write 9P messages on the other end of the pipe.
+.SH DIAGNOSTICS
+Returns 0 on success; -1 on failure.
+.SH "SEE ALSO"
+.IR pipe (3)
--- /dev/null
+++ b/man/2/sys-print
@@ -1,0 +1,276 @@
+.TH SYS-PRINT 2
+.SH NAME
+print, aprint, fprint, sprint \- print formatted output
+.SH SYNOPSIS
+.EX
+include "sys.m";
+sys := load Sys Sys->PATH;
+
+aprint: fn(format: string, *): array of byte;
+fprint: fn(fd: ref FD, format: string, *): int;
+print:  fn(format: string, *): int;
+sprint: fn(format: string, *): string;
+.EE
+.SH DESCRIPTION
+These functions format and print their arguments as
+.SM UTF
+text.
+.B Print
+writes text to the standard output.
+.B Fprint
+writes to the named output
+file descriptor.
+.B Sprint
+places text
+in a string, which it returns.
+.B Aprint
+is similar but returns the text in
+.IR utf (6)
+representation as an array of bytes.
+.B Print
+and
+.B fprint
+return the number of bytes transmitted
+or
+a negative value if an  error was encountered when writing the output.
+.PP
+Each of these functions
+converts, formats, and prints its
+trailing arguments
+under control of a
+.IR format 
+string.
+The
+format
+contains two types of objects:
+plain characters, which are simply copied to the
+output stream,
+and conversion specifications,
+each of which results in fetching of
+zero or more
+arguments.
+The Limbo compiler recognizes calls to these functions
+and checks that the arguments match the format specifications in number and type.
+.PP
+Each conversion specification has the following format:
+.IP
+.BI "%" " \fR[\fPflags\fR]\fP verb"
+.PP
+The verb is a single character and each flag is a single character or a
+(decimal) numeric string.
+Up to two numeric strings may be used;
+the first is called
+.IR f1 ,
+the second
+.IR f2 .
+They can be separated by
+.RB ` . ',
+and if one is present, then
+.I f1
+and
+.I f2
+are taken to be zero if missing, otherwise they are considered `omitted'.
+Either or both of the numbers may be replaced with the character
+.BR * ,
+meaning that the actual number will be obtained from the argument list
+as an integer.
+The flags and numbers are arguments to
+the
+.I verb
+described below.
+.PP
+.TP
+.BR d ", " o ", " x ", " X
+The numeric verbs
+.BR d ,
+.BR o ,
+and
+.B x
+format their
+.B int
+arguments in decimal, octal, and hexadecimal (with hex digits in lower-case).
+The flag
+.B b
+is required when the corresponding value is a Limbo
+.BR  big ,
+not an
+.BR int .
+Arguments are taken to be signed, unless the
+.B u
+flag is given, to force them to be treated as unsigned.
+Each interprets the flags
+.B \- ,
+.B , ,
+and
+.B #
+to mean left justified, commas every three digits, and alternative format.
+If
+.I f2
+is not omitted, the number is padded on the left with zeros
+until at least
+.I f2
+digits appear.
+Then, if alternative format is specified
+for
+.B x
+conversion, the number is preceded by
+.BR 0x .
+Finally, if
+.I f1
+is not omitted, the number is padded on the left (or right, if
+left justification is specified) with enough blanks to
+make the field at least
+.I f1
+characters long.
+The verb
+.B X
+is similar to
+.BR x ,
+except that the hexadecimal digits are displayed in upper-case,
+and in alternative format, the number is preceded by
+.BR 0X .
+.PP
+.TP
+.BR e ", " f ", " g
+The floating point verbs
+.BR e ,
+.BR f ,
+and
+.BR g
+take a
+.B real
+argument.
+Each interprets the flags
+.BR + ,
+.BR \- ,
+and
+.B #
+to mean
+always print a sign,
+left justified,
+and
+alternative format.
+.I F1
+is the minimum field width and,
+if the converted value takes up less than
+.I f1
+characters, it is padded on the left (or right, if `left justified')
+with spaces.
+.I F2
+is the number of digits that are converted after the decimal place for
+.BR e
+and
+.B f
+conversions,
+and
+.I f2
+is the maximum number of significant digits for
+.B g
+conversions.
+The 
+.B f
+verb produces output of the form
+.RB [ \- ]\c
+.IR digits [\c
+.BI \&. digits\fR].
+The
+.B e
+conversion appends an exponent
+.BR e [ \- ]\c
+.IR digits .
+The
+.B g
+verb will output the argument in either
+.B e
+or
+.B f
+with the goal of producing the smallest output.
+Also, trailing zeros are omitted from the fraction part of
+the output, and a trailing decimal point appears only if it is followed
+by a digit.
+When alternative format is specified, the result will always contain a decimal point,
+and for
+.B g
+conversions, trailing zeros are not removed.
+.TP
+.BR E ", " G
+These are the same as
+.B e
+and
+.B g
+respectively, but use
+.B E
+not
+.B e
+to specify an exponent when one appears.
+.TP
+.B c
+The
+.B c
+verb converts a single Unicode character
+from an
+.B int
+argument to a UTF encoding,
+justified within a field of
+.I f1
+characters as described above.
+.TP
+.B r
+The
+.B r
+verb takes no arguments; it prints the error string
+associated with the most recent system error.
+.TP
+.B s
+The
+.B s
+verb copies a
+.B string
+to the output.
+The number of characters copied
+.RI ( n )
+is the minimum
+of the size of the string and
+.IR f2 .
+These
+.I n
+characters are justified within a field of
+.I f1
+characters as described above.
+.TP
+.B q
+The
+.B q
+verb copies a
+.B string
+to the output as for
+.BR s ,
+but quotes the string in the style of
+.IR sh (1)
+only if necessary to avoid ambiguity (for instance
+if the string contains quotes or spaces).
+If the format string, however, includes the specifier
+.B #
+(for example
+.BR %#q ),
+the printed string will always be quoted.
+.SH SOURCE
+.B /libinterp/runt.c:/^xprint
+.br
+.B /os/port/print.c
+.br
+.B /lib9/print.c
+.SH SEE ALSO
+.IR sys-intro (2),
+.IR sys-open (2)
+.SH BUGS
+The
+.B x
+verb does not apply the
+.B 0x
+prefix when
+.I f2
+is present.
+The prefix should probably be
+.B 16r
+anyway.
--- /dev/null
+++ b/man/2/sys-read
@@ -1,0 +1,108 @@
+.TH SYS-READ 2
+.SH NAME
+read, write, pread, pwrite, stream \- read or write file
+.SH SYNOPSIS
+.EX
+include "sys.m";
+sys := load Sys Sys->PATH;
+
+read:   fn(fd: ref FD, buf: array of byte, nbytes: int): int;
+readn:  fn(fd: ref FD, buf: array of byte, nbytes: int): int;
+write:  fn(fd: ref FD, buf: array of byte, nbytes: int): int;
+
+pread:  fn(fd: ref FD, buf: array of byte, nbytes: int,
+           offset: big): int;
+pwrite: fn(fd: ref FD, buf: array of byte, nbytes: int,
+           offset: big): int;
+
+stream: fn(src, dst: ref FD, bufsiz: int): int;
+.EE
+.SH DESCRIPTION
+.B Read
+reads
+.I nbytes
+bytes of data from the offset in the file
+associated with
+.I fd
+into memory at
+.IR buf .
+The file offset is advanced by the number of bytes read.
+It is not guaranteed
+that all
+.I nbytes
+bytes will be read; for example
+if the file refers to the console, at most one line
+will be returned.
+In any event the number of bytes read is returned.
+A return value of
+0 is conventionally interpreted as end of file.
+.PP
+.B Readn
+continues to
+.B read
+from
+.I fd
+sequentially into
+.IR buf ,
+until
+.I nbytes
+have been read, or
+.B read
+returns a non-positive count.
+.PP
+.B Write
+writes
+.I nbytes
+bytes of data starting at
+.I buf
+to the file associated with
+.I fd
+at the file offset.
+The offset is advanced by the number of bytes written.
+The number of bytes actually written is returned.
+It should be regarded as an error
+if this is not the same as requested.
+.PP
+.B Pread
+and
+.B pwrite
+take an explicit file
+.I offset
+as a parameter, leaving
+.IR fd 's
+current offset untouched;
+they are otherwise identical in behaviour to
+.B read
+and
+.BR write .
+They are particulary useful when several processes must access the same
+.I fd
+concurrently and it is inconvenient or undesirable to synchronise their activity
+to avoid interference.
+.PP
+.B Stream
+continually reads data from
+.IR src ,
+using a buffer of
+.I bufsiz
+bytes, and writes the data to
+.IR dst .
+It copies data
+until a read fails (returning
+zero bytes or an error) or a write fails.
+.B Stream
+returns the number of bytes actually copied.
+The implementation may be more efficient than a
+.BR read / write
+loop in the application, but is otherwise
+equivalent to calling
+.B read
+and
+.B write
+directly.
+.SH SEE ALSO
+.IR bufio (2),
+.IR sys-intro (2),
+.IR sys-dup (2),
+.IR sys-open (2),
+.IR read (5)
--- /dev/null
+++ b/man/2/sys-remove
@@ -1,0 +1,23 @@
+.TH SYS-REMOVE 2
+.SH NAME
+remove \- remove a file
+.SH SYNOPSIS
+.EX
+include "sys.m";
+sys := load Sys Sys->PATH;
+
+remove:	fn(file: string): int;
+.EE
+.SH DESCRIPTION
+.B Remove
+removes
+.I file
+from the directory containing it and discards the contents of the file.
+The user must have write permission in the containing directory.
+If
+.I file
+is a directory, it must be empty.
+.B Remove
+returns zero if it deletes the file, \-1 otherwise.
+.SH SEE ALSO
+.IR sys-intro (2)
--- /dev/null
+++ b/man/2/sys-seek
@@ -1,0 +1,48 @@
+.TH SYS-SEEK 2
+.SH NAME
+seek \- change file offset
+.SH SYNOPSIS
+.EX
+include "sys.m";
+sys := load Sys Sys->PATH;
+
+seek:  fn(fd: ref FD, off: big, start: int): big;
+.EE
+.SH DESCRIPTION
+.B Seek
+sets the 64-bit offset for the file
+associated with
+.I fd
+as follows:
+.IP
+If
+.I start
+is
+.BR Sys->SEEKSTART ,
+the offset is set to
+.I off
+bytes.
+.IP
+If
+.I start
+is
+.BR Sys->SEEKRELA ,
+the pointer is set to its current location plus
+.IR off .
+.IP
+If
+.I start
+is
+.BR Sys->SEEKEND ,
+the pointer is set to the size of the
+file plus
+.IR off .
+.PP
+The new file offset value is returned.
+.PP
+Seeking in a pipe is not allowed.
+Seeking in a directory is allowed only if the new offset is zero.
+.SH SEE ALSO
+.IR sys-intro (2),
+.IR sys-open (2),
+.IR bufio (2),
--- /dev/null
+++ b/man/2/sys-self
@@ -1,0 +1,83 @@
+.TH SYS-SELF 2
+.SH NAME
+SELF \- reference self as a compatible module type
+.SH SYNOPSIS
+.B
+include "sys.m";
+.br
+.BI "me := load" " Module " SELF;
+.SH DESCRIPTION
+An instance of
+a module of one type can acquire a reference to itself as any compatible module type,
+using the Limbo
+.B load
+operator with the special built-in module name
+.BR $self .
+Normally, applications use a synonym, the constant
+.BR SELF ,
+which is defined by
+.B sys.m
+.I outside
+the declaration of the
+.B Sys
+module
+(so that it need not be imported).
+Note that the result of the
+.B load
+refers to the same instance
+that is currently executing (ie, the same module data).
+.PP
+This mechanism is most often used to obtain a reference to the current module
+instance with a
+.I restriction
+of its module type to a compatible subtype (eg, containing a subset
+of the current module's declarations).
+For example, given modules of the following types:
+.IP
+.EX
+T: module
+{
+    init:    fn(nil: ref Draw->Context, nil: list of string);
+    special: fn(a, b: int);
+};
+S: module
+{
+    special: fn(x, y: int);
+};
+G: module
+{
+    init:    fn(v: S);
+};
+.EE
+.PP
+an instance of module
+.B T
+can execute both the following:
+.IP
+.EX
+t := load T SELF;
+s := load S SELF;
+.EE
+.PP
+but a module of type
+.B S
+could not load itself as type
+.BR T .
+.PP
+The result might typically be assigned to a module variable of that type
+(including passing as a parameter or storing in an adt), as in:
+.IP
+.EX
+g := load G "g.dis";
+g->init(s);
+.EE
+.PP
+See the definition and use of
+.B BufioFill
+in
+.IR bufio (2)
+and
+.IR bufio-chanfill (2)
+for a practical example.
+.SH SEE ALSO
+``The Limbo Programming Language'', Volume 2.
--- /dev/null
+++ b/man/2/sys-sleep
@@ -1,0 +1,29 @@
+.TH SYS-SLEEP 2
+.SH NAME
+sleep \- delay
+.SH SYNOPSIS
+.EX
+include "sys.m";
+sys := load Sys Sys->PATH;
+
+sleep:	fn(period: int): int;
+.EE
+.SH DESCRIPTION
+.B Sleep
+suspends the current thread for
+.I period
+milliseconds.
+The actual suspension time may be a little more or less than
+the requested time.
+If
+.I period
+is 0, the process
+gives up the CPU if another process is waiting to run, returning
+immediately if not.
+.PP
+.B Sleep
+normally returns 0. In hosted Inferno it may return -1 to indicate that
+it was not possible to sleep (eg if no more threads are available).
+.SH SEE ALSO
+.IR sys-intro (2),
+.IR sys-millisec (2)
--- /dev/null
+++ b/man/2/sys-stat
@@ -1,0 +1,283 @@
+.TH SYS-STAT 2
+.SH NAME
+fstat, fwstat, stat, wstat \- get and put file status
+.SH SYNOPSIS
+.EX
+include "sys.m";
+sys := load Sys Sys->PATH;
+
+fstat:  fn(fd: ref FD): (int, Dir);
+fwstat: fn(fd: ref FD; d: Dir): int;
+stat:   fn(name: string): (int, Dir);
+wstat:  fn(name: string, d: Dir): int;
+
+nulldir: con Dir(\fIspecial don't care values\fP);
+zerodir: con Dir(\fIall elements set to zero\fP);
+.EE
+.SH DESCRIPTION
+Given a file's
+.IR name ,
+or an open file descriptor
+.IR fd ,
+these routines retrieve or modify file status information.
+.B Stat
+and
+.B fstat
+retrieve information about
+.I name
+or
+.I fd
+into the
+.B Dir
+member of the return tuple.
+The
+.B int
+member will be zero for success and \-1 for failure.
+.B wstat
+and
+.B fwstat
+write information back, thus changing file attributes according to
+.IR d .
+Both functions return zero for success and \-1 for failure.
+.PP
+File status is recorded as a
+.B Dir
+type:
+.IP
+.PP
+.EX
+Qid: adt
+{
+    path:   big;     # unique id for file on server
+    vers:   int;     # write version number
+    qtype:  int;     # file type (see below)
+};
+
+Dir: adt
+{
+    name:   string;  # last element of path
+    uid:    string;  # owner name
+    gid:    string;  # group name
+    muid:   string;  # last modifier name
+    qid:    Qid;     # unique id from server
+    mode:   int;     # permissions
+    atime:  int;     # last read time
+    mtime:  int;     # last write time
+    length: big;     # file length
+    dtype:  int;     # server type
+    dev:    int;     # server subtype
+};
+.EE
+.PP
+If the file resides on permanent storage and is not a directory,
+the
+.B length
+field returned in
+.B Dir
+by
+.B stat
+is the number of bytes in the file.
+For directories, the length returned is zero.
+Some devices, in particular files that are
+streams such as pipes and network connections,
+report a length that is the number of bytes that
+may be read from the device without blocking.
+.PP
+Each file is the responsibility of some
+.IR server :
+it could be a file server, a kernel device, or a user process.
+.B Dtype
+identifies the server type, and
+.B dev
+says which of a group of servers of the same type is the one
+responsible for this file.
+.B Qid
+is a type containing
+.BR path ,
+.B vers
+and
+.B qtype
+members, each an integer:
+.B path
+is guaranteed to be
+unique among all path names currently on the file server;
+.B vers
+changes each time the file is modified;
+and
+.B qtype
+gives the file's characteristics (eg, directory or file).
+The
+.B path
+is 64 bits
+.RB ( big ),
+and the
+.B vers
+is 32 bits
+.RB ( int ).
+Thus, if two files have the same
+.BR dtype ,
+.BR dev ,
+and
+.BR qid ,
+they are the same file.
+(Except when checking that the contents
+are the same, as in a file cache, the version is often considered irrelevant in that comparison.)
+The bits in
+.B qtype
+are defined by
+.IP
+.EX
+16r80 # directory (Sys->QTDIR)
+16r40 # append-only (Sys->QTAPPEND)
+16r20 # exclusive-use (Sys->QTEXCL)
+16r08 # authentication file (Sys->QTAUTH)
+16r00 # any other file (Sys->QTFILE)
+.EE
+.PP
+(They are the top 8 bits of
+.B Dir.mode
+for the file, as discussed below.)
+.B Sys
+defines constants for the bits:
+.BR Sys->QTDIR ,
+.BR Sys->QTAPPEND ,
+and so on, as shown above.
+The value
+.B Sys->QTFILE
+is not a particular bit; it is defined to be zero, to allow
+a symbolic name to be used when creating
+.B Qid
+values for ordinary files.
+.PP
+The bits in
+.B mode
+are defined by
+.IP
+.EX
+16r80000000 #directory (Sys->DMDIR)
+16r40000000 #append-only (Sys->DMAPPEND)
+16r20000000 #exclusive-use (Sys->DMEXCL)
+16r08000000 #authentication file (Sys->DMAUTH)
+      8r400 #read    permission by owner
+      8r200 #write   permission by owner
+      8r100 #execute permission (search on directory) by owner
+      8r070 #read, write, execute (search) by group
+      8r007 #read, write, execute (search) by others
+.EE
+.PP
+There are constants defined in
+.B Sys
+for the first four bits:
+.BR Sys\->DMDIR ,
+.B Sys\->DMAPPEND
+and
+.B Sys\->DMEXCL
+for normal files, and
+.B Sys\->DMAUTH
+only for the special authentication file opened by
+.IR sys-fauth (2).
+.PP
+The two time fields are measured in seconds since the epoch
+(Jan 1 00:00 1970 GMT).
+.B Mtime
+is the time of the last change of content.
+Similarly,
+.B atime
+is set whenever the contents are accessed;
+also, it is set whenever
+.B mtime
+is set.
+.PP
+.B Uid
+and
+.B gid
+are the names of the owner and group (of owners) of the file;
+.B muid
+is the name of the user that last modified the file (setting
+.BR mtime ).
+Groups are also users, but each server is free to associate
+a list of users with any user name
+.IR g ,
+and that list is the
+set of users in the group
+.IR g .
+When an initial attachment is made to a server,
+the user string in the process group is communicated to the server.
+Thus, the server knows, for any given file access, whether the accessing
+process is the owner of, or in the group of, the file.
+This selects which sets of three bits in
+.B mode
+is used to check permissions.
+.PP
+Only some of the fields may be changed by
+.B wstat
+calls.
+The
+.B name
+can be changed by anyone with write permission in the parent directory.
+The
+.B mode
+and
+.B mtime
+can be changed by the owner or the group leader of the file's current
+group.
+The
+.B gid
+can be changed by the owner if he or she is a member of the new group.
+The
+.B gid
+can be changed by the group leader of the file's current group
+if he or she is the leader of the new group.
+The
+.B length
+can be changed by anyone with write permission, provided the operation
+is implemented by the server.
+(See
+.IR intro (5)
+and
+.IR stat (5)
+for more information about permissions, and
+.IR users (6)
+for how to configure users and groups
+when using
+.IR kfs (4)).
+.PP
+Special values in the fields of the
+.B Dir
+passed to
+.I wstat
+indicate that the field is not intended to be changed by the call.
+The values are the maximum unsigned integer of appropriate size
+for integral values (usually
+.BR ~0 ,
+but beware of conversions and size mismatches
+when comparing values) and the empty or nil string for string values.
+The constant
+.B nulldir
+in
+.B Sys
+has all its elements initialised to these ``don't care'' values.
+Thus one may change the mode, for example, by assigning
+.B sys->nulldir
+to initialize a
+.BR Dir ,
+then setting the mode, and then doing
+.IR wstat ;
+it is not necessary to use
+.I stat
+to retrieve the initial values first.
+.PP
+The constant
+.B zerodir
+has all its elements initialised to zero.
+It can be used to initialise a
+.B Dir
+structure, for use with
+.IR styx (2)
+or
+.IR styxservers-nametree (2),
+for instance.
+.SH SEE ALSO
+.IR sys-intro (2),
+.IR sys-dirread (2),
+.IR sys-open (2)
--- /dev/null
+++ b/man/2/sys-tokenize
@@ -1,0 +1,56 @@
+.TH SYS-TOKENIZE 2
+.SH NAME
+tokenize \- split string into words
+.SH SYNOPSIS
+.EX
+include "sys.m";
+sys := load Sys Sys->PATH;
+
+tokenize: fn(s, delim: string): (int, list of string);
+.EE
+.SH DESCRIPTION
+.B Tokenize
+breaks
+.I s
+into words separated by characters in
+.IR delim .
+The returned tuple holds the number of words
+and an ordered list of those words (whose
+.B hd
+gives the leftmost word from
+.IR s ).
+.PP
+Words are delimited by the maximal sequences of any
+character from the
+.I delim
+string.
+.B Tokenize
+skips delimiter characters at the beginning and end of
+.IR s ,
+so each element in the returned list has non-zero length.
+.PP
+If
+.I s
+is
+.B nil
+or contains no words,
+.B tokenize
+returns a count of zero and a
+.B nil
+list.
+.PP
+.I Delim
+may be
+.B nil
+or the empty string, specifying no delimiter characters.
+The resulting word list will be
+.B nil
+(if
+.I s
+is
+.B nil
+or the empty string)
+or a single-item list with a copy of
+.IR s .
+.SH SEE ALSO
+.IR sys-intro (2)
--- /dev/null
+++ b/man/2/sys-utfbytes
@@ -1,0 +1,28 @@
+.TH SYS-UTFBYTES 2
+.SH NAME
+utfbytes \- compute UTF length of complete Unicode characters in a UTF byte sequence
+.SH SYNOPSIS
+.EX
+include "sys.m";
+sys := load Sys Sys->PATH;
+
+utfbytes: fn(buf: array of byte, n: int): int;
+.EE
+.SH DESCRIPTION
+.B Utfbytes
+examines the 
+.IR n -byte
+UTF sequence in
+.IR buf
+and returns the number of bytes representing complete Unicode characters.
+The value will be less than
+.I n
+when the region inspected ends with an incomplete UTF sequence.
+.SH DIAGNOSTIC
+A bounds check error results if
+.I n
+exceeds the length of the array.
+.SH SEE ALSO
+.IR sys-intro (2),
+.IR sys-byte2char (2),
+.IR utf (6)
--- /dev/null
+++ b/man/2/sys-werrstr
@@ -1,0 +1,26 @@
+.TH SYS-WERRSTR 2
+.SH NAME
+werrstr \- set the system error string
+.SH SYNOPSIS
+.EX
+include "sys.m";
+sys := load Sys Sys->PATH;
+
+werrstr(s: string): int;
+.EE
+.SH DESCRIPTION
+When a system call fails, it returns an error value (often -1)
+and records a string describing the error in a per-process location.
+The verb
+.B r
+in
+.IR sys-print (2)
+outputs the error string.
+.B Werrstr
+sets the process's error string to
+.IR s ,
+to allow a function in a module to mimic the error reporting
+interface of a system call.
+.SH SEE ALSO
+.IR sys-intro (2),
+.IR sys-print (2)
--- /dev/null
+++ b/man/2/tabs
@@ -1,0 +1,74 @@
+.TH TABS 2
+.SH NAME
+tabs: mktabs, tabsctl \-
+tabbed notebook pseudo-widget
+.SH SYNOPSIS
+.EX
+include "tabs.m";
+tabs := load Tabs Tabs->PATH;
+
+init:        fn();
+
+mktabs:      fn(p: ref Tk->Toplevel, book: string,
+               tabs: array of (string, string), dflt: int):
+               chan of string;
+
+tabsctl:     fn(p: ref Tk->Toplevel, book: string,
+               tabs: array of (string, string), curid: int,
+               newid: string): int;
+.EE
+.SH DESCRIPTION
+.B Tabs
+implements a Tk extension:
+a user-interface device that looks like a tabbed notebook.
+.PP
+.B Init
+should be called once to initialise the internal state.
+.PP
+.B Mktabs
+creates a tabbed notebook pseudo widget,
+.IR book ,
+for insertion into Tk
+widget
+.IR p .
+Once created,
+.I book
+can be packed like any other Tk widget.
+Information for specific tab pages is contained in the
+.I tabs
+array.
+For each page, 
+.I tabs
+contains the name displayed in the tab and a Tk widget name.
+Whenever a page is selected, its widget is
+packed in 
+.I book
+and displayed.
+The notebook will initially display the page
+indexed by
+.IR dflt .
+.B Mktabs
+returns a Tk event channel.
+Messages received on this channel should be passed as the
+.I newid
+argument to
+.BR tabsctl .
+.PP
+.B Tabsctl
+controls a tabbed notebook.
+.I Curid
+is the index of the page currently selected
+in the notebook.
+.I Newid
+is a string containing the index of the new
+page to be displayed; this is usually the information received on
+the tabs channel.
+The index of the newly selected page is returned. 
+.SH SOURCE
+.B /appl/lib/tabs.b
+.SH SEE ALSO
+.IR dividers (2),
+.IR draw-context (2),
+.IR tk (2),
+.IR wmlib (2)
+
--- /dev/null
+++ b/man/2/tftp
@@ -1,0 +1,54 @@
+.TH TFTP 2
+.SH NAME
+tftp \- Trivial File Transfer Protocol
+.SH SYNOPSIS
+.EX
+tftp := load Tftp Tftp->PATH;
+Tftp: module
+{
+   init:    fn(progress: int);
+   receive: fn(host: string, filename: string,
+               fd: ref Sys->FD): string;
+};
+.EE
+.SH DESCRIPTION
+.B Tftp
+fetches files from an Internet TFTP server.
+It is typically used only to fetch kernels or configuration files when booting.
+Only one transfer can be active at any given time.
+.PP
+.B Init
+must be called once before using any other function of the module.
+If
+.I progress
+is non-zero,
+.B receive
+will periodically print a character as blocks are received:
+.RB ` . '
+for every 25 blocks,
+.RB ` S '
+for a sequence error,
+.RB ` T '
+for a timeout.
+.PP
+.B Receive
+attempts to fetch the contents of
+.I filename
+from
+.I host
+and writes the blocks of data to
+.I fd
+as they are received.
+It returns when the file transfer has completed,
+returning a nil string on success or a diagnostic string otherwise.
+When booting,
+.I fd
+is typically open on the
+.B kexec
+file of
+.IR boot (3).
+.SH SOURCE
+.B /appl/lib/tftp.b
+.SH SEE ALSO
+.IR ip (2),
+.IR bootpd (8)
--- /dev/null
+++ b/man/2/timers
@@ -1,0 +1,89 @@
+.TH TIMERS 2
+.SH NAME
+timers \- interval timers
+.SH SYNOPSIS
+.EX
+include "timers.m";
+timers := load Timers Timers->PATH;
+
+Timer: adt
+{
+   timeout: chan of int;
+   start:   fn(msec: int): ref Timer;
+   stop:    fn(t: self ref Timer);
+};
+
+init:     fn(minms: int): int;
+shutdown: fn();
+.EE
+.SH DESCRIPTION
+.B Timers
+provides simple interval timing.
+Timeouts are notified by a message sent on a channel,
+allowing them to provide timeouts in
+.B alt
+statements.
+.PP
+The module must first be initialised by calling
+.BR init ,
+which starts a process to manage the interval timers and returns its process ID.
+Before exit, the caller must shut the timing process down either by calling
+.BR shutdown ,
+which stops it synchronously; by using the process ID returned by
+.B init
+to kill it;
+or by killing the process group of the process that
+called
+.BR init
+(since the timing processes remain in that group).
+.I Minms
+gives the minimum granularity of timing requests in milliseconds.
+.TP
+.BI Timer.start( msec )
+Returns a
+.B Timer
+that will expire in
+.I msec
+milliseconds,
+measured with the granularity of either
+.IR sys-sleep (2)
+or the granularity set by
+.BR init ,
+whichever is greater.
+.TP
+.IB t .timeout
+An arbitrary integer value is sent on this channel when the timer
+.I t
+expires.
+.TP
+.IB t .stop()
+The timer
+.I t
+is stopped
+and removed from the interval timing queue,
+if it has not already expired.
+.PP
+Each
+.B Timer
+value times a single interval.
+When a timer
+.I t
+expires, the timing process attempts, at that and each subsequent timing interval, to send on
+.IB t .timeout
+until the expiry message is delivered or the timer is stopped.
+.SH EXAMPLE
+Wait for data to be sent on an input channel, but give up if it does not arrive within 600 milliseconds:
+.IP
+.EX
+t := Timer.start(600);
+alt {
+data := <-input =>
+	t.stop();
+	# process the data
+<-t.timeout =>
+	# request timed out
+}
+.EE
+.SH SEE ALSO
+.IR sys-millisec (2),
+.IR sys-sleep (2)
--- /dev/null
+++ b/man/2/tk
@@ -1,0 +1,270 @@
+.TH TK 2
+.SH NAME
+Tk: toplevel, namechan, cmd, pointer, keyboard, imageget, imageput, quote, rect \- graphics toolkit
+.SH SYNOPSIS
+.EX
+include "draw.m";
+include "tk.m";
+tk := load Tk Tk->PATH;
+Image:    import Draw;
+
+Toplevel: adt
+{
+    display:    ref Draw->Display;
+    wreq:       chan of string;
+    image:      ref Image;
+    ctxt:       ref Draw->Wmcontext;
+    screenr:    Draw->Rect;
+};
+
+toplevel: fn(display: ref Draw->Display, arg: string): ref Toplevel;
+namechan: fn(top: ref Toplevel, c: chan of string, n: string): string;
+cmd:      fn(top: ref Toplevel, arg: string): string;
+pointer:    fn(top: ref Toplevel, p: Draw->Pointer);
+keyboard: fn(top: ref Toplevel, key: int);
+getimage: fn(top: ref Toplevel, name: string):
+             (ref Image, ref Image, string);
+putimage: fn(top: ref Toplevel, name: string, i, m: ref Image): string;
+rect:     fn(top: ref Toplevel, name: string, flags: int): Draw->Rect;
+quote:    fn(s: string): string;
+color:    fn(s: string): int;
+.EE
+.SH DESCRIPTION
+The
+.B Tk
+module provides primitives for building user interfaces, based on
+Ousterhout's Tcl/TK.
+The interface to the toolkit itself is primarily the passing of strings
+to and from the elements of the toolkit using the
+.B cmd
+function; see section 9 of this manual for more information
+about the syntax of those strings.
+.IR Tkclient (2)
+is conventionally used to create tk windows
+that interact correctly with a running window manager.
+.PP
+.B Toplevel
+creates a new window
+called a
+.BR Toplevel ,
+which is under the control of the
+.B Tk
+toolkit,
+on an existing
+.IR display ,
+usually one inherited from the graphics
+.B Context
+(see
+.IR draw-context (2)).
+The
+.B Toplevel
+is passed to
+.B cmd
+and
+.B namechan
+.RI ( q.v. )
+to drive the widgets in the window.
+.I Arg
+is a string containing creation options (such as
+.BR "-borderwidth 2" )
+that are applied when creating the toplevel window.
+.PP
+.B Cmd
+passes command strings to the widgets in the
+.B Toplevel
+.I t
+and returns the string resulting from their execution.
+For example, given a canvas
+.B .c
+in the
+.B Toplevel
+.BR t ,
+.EX
+    x := int tk->cmd(t, ".c cget -actx");
+.EE
+returns the integer
+.I x
+coordinate of the canvas.
+.PP
+Bindings can be created in a
+.B Toplevel
+that trigger strings to be sent on Limbo channels.
+Such channels must be declared to the
+.B Tk
+module using
+.BR namechan .
+For example, to create a button that sends the word
+.B Ouch
+when it is pressed:
+.EX
+    hitchannel := chan of string;
+    tk->namechan(t, hitchannel, "channel");
+    tk->cmd(t,
+         "button .b.Hit -text Hit -command {send channel Ouch}");
+    expl := <-hitchannel;	# will see Ouch when button pressed
+.EE
+.PP
+.B Pointer
+and
+.B keyboard
+pass mouse and keyboard events to a
+.BR Tk
+window
+for delivery to widgets; they must be called by each application,
+which usually receives them via a
+.B Wmcontext
+structure (see
+.IR draw-context (2))
+obtained from the window manager, often via
+.IR tkclient (2).
+.PP
+.B Putimage
+passes an image and a mask into Tk.
+If
+.I name
+is the name of a Tk widget, it must be either a
+.IR panel (9)
+widget, or a top level widget (ie,
+.RB `` . '')
+.BR "" "`"` . "'')"
+or a menu widget,
+in which case the associated image
+or window image is set to
+.IR i .
+.RI ( m
+is ignored for menu and top-level widgets.)
+Otherwise,
+.I name
+must be the name of an existing
+.IR image (9)
+which has its image and mask
+set to copies of
+.I i
+and
+.I m
+respectively.
+.PP
+Initially, a Tk toplevel has no image to draw on.
+Tk uses
+.B wreq
+to request new images of an external authority, and to inform
+said authority when the images are to be deleted.
+The requests are formatted as per
+.B quoted
+in
+.IR string (2),
+and hold one of the following:
+.TP
+.B !reshape \fIname\fP \fIreqid\fP \fIminx miny maxx maxy\fP
+A new image for
+.I name
+is requested
+.RI ( name
+is either the toplevel widget or a menu).
+The desired rectangle for the new image is given
+by
+.RI [ "minx miny maxx maxy" ],
+and the application should respond by creating a new
+image and using
+.B putimage
+to pass it to Tk.
+.I Reqid
+is used by Tk to filter out responses to out-of-date
+requests; when responding to a reshape
+request, the
+.I name
+passed to
+.B putimage
+should have a space and
+.I reqid
+appended.
+.IR Tkclient (2)
+usually deals with the details of this.
+.TP
+.B delete \fIname\fP
+The image
+.I name
+has been deleted. This is generated for
+.IR menu (9)
+widgets when they are unmapped.
+.TP
+.B raise \fIname\fP
+Tk widget
+.I name
+should be raised above other windows on the same screen.
+.TP
+.B lower \fIname\fP
+Tk widget
+.I name
+should be lowered beneath other windows on the same screen.
+.PP
+.B Wreq
+may be set to nil if an application is not prepared to
+read requests sent on this channel.
+.PP
+.B Rect
+returns the bounding rectangle of
+widget
+.I name
+in
+.IR top .
+.I Flags
+determines the form of rectangle returned.
+If
+.I flags
+is zero, the actual rectangle of
+.I name
+in screen coordinates, not including its border,
+is returned. The bitmask flags that can change this are:
+.TP
+.B Border
+Include the widget's border.
+.TP
+.B Required
+Return the rectangle required by the widget, rather
+than the rectangle that has been actually allocated to it.
+.TP
+.B Local
+Return the rectangle in coordinates relative
+to the logical origin of the actual top level image.
+.PP
+.B Quote
+returns a string that is the same as its arguments, but enclosed
+in curly braces and with internal curly braces escaped.
+This can be used to make an arbitrary string into a
+.I word
+suitable as an argument to a Tk function.
+.PP
+.B Color
+returns a colour in 32-bit RGBA format corresponding to the
+tk colour name
+.IR s .
+(see
+.IR types (9)
+for details).
+.PP
+.B Screenr
+gives the rectangle of the screen containing the toplevel window.
+Tk has no
+.I "a priori"
+way of knowing what this is; it is initially set to the rectangle of the
+display image, and may be set by the application if it knows better
+(e.g. from the
+.B wmrect
+file served by
+.IR wm (1)).
+.SH SOURCE
+.B /libinterp/tk.c
+.br
+.B /libtk/*.c
+.SH SEE ALSO
+.IR intro (9),
+.IR image (9),
+.IR panel (9),
+.IR tkcmd (1),
+.IR sh-tk (1),
+.IR draw-context (2),
+.IR tkclient (2),
+.IR wmlib (2)
+.br
+`An Overview of Limbo/Tk', this manual, Volume 2.
--- /dev/null
+++ b/man/2/tkclient
@@ -1,0 +1,229 @@
+.TH TKCLIENT 2
+.SH NAME
+tkclient: makedrawcontext, toplevel, onscreen, startinput,
+wmctl, settitle, handler, snarfput, snarfget \-
+window manager interface for Tk applications.
+.SH SYNOPSIS
+.EX
+include "tkclient.m";
+tkclient := load Tkclient Tkclient->PATH;
+
+Resize,
+Hide,
+Help,
+OK,
+Plain:     con 1 << iota;
+
+Appl:   con Resize | Hide;
+
+init:       fn();
+makedrawcontext: fn():  ref Draw->Context;
+toplevel:   fn(ctxt: ref Draw->Context, topconfig: string,
+                title: string, buts: int): (ref Tk->Toplevel, chan of string);
+onscreen:   fn(top: ref Tk->Toplevel, how: string);
+startinput: fn(top: ref Tk->Toplevel, devs: list of string);
+wmctl:      fn(top: ref Tk->Toplevel, request: string): string;
+settitle:   fn(top: ref Tk->Toplevel, name: string): string;
+handler:    fn(top: ref Tk->Toplevel, stop: chan of int);
+
+snarfput:   fn(buf: string);
+snarfget:   fn(): string;
+.EE
+.SH DESCRIPTION
+The
+.B Tkclient
+module provides routines for making windows controlled by
+.IR wm (1)
+containing
+.IR tk (2)
+widgets.
+.PP
+.B Init
+should be called once to initialise the internal state of
+.BR tkclient .
+.PP
+.B Makedrawcontext
+establishes an initial connection with the window manager,
+creating a new
+.B Draw
+context suitable for creating new windows. It is only
+necessary to call this if the application has not already
+been provided with a context.
+.PP
+.B Toplevel
+creates a new window through
+.IR ctxt .
+.I Topconfig
+gives a list of
+.IR frame (9)
+options that are applied to the
+new tk window, as described in
+.IR tk (2).
+.I Title
+gives a label that will be displayed in the title bar
+of the window;
+.I buts
+determines which buttons are created in the titlebar,
+a bitwise combination of the constants
+.BR Resize ,
+.BR Help ,
+.BR OK,
+and
+.BR Hide .
+If
+.B Plain
+is given, the window is given no decoration at all.
+.B Toplevel
+returns a tuple, say
+.RI ( top ,\  ctl ),
+where
+.I top
+is the newly created top level tk window,
+and
+.I ctl
+is a channel down which requests from the
+title bar are sent.
+Messages received on
+.I ctl
+should be processed
+by the application or passed to the
+.B wmctl
+function. Requests are formatted
+as with
+.B quoted
+in
+.IR string (2).
+The messages include:
+.TP
+.B exit
+The window should be closed.
+.B Wmctl
+will kill all processes in the current
+process group.
+.TP
+.B !move \fIx\fP \fIy\fP
+The user has started to try to drag the window.
+.I X
+and
+.I y
+give the location of the initial pointer click.
+.TP
+.B !size
+The user wishes to resize the window.
+.TP
+.B help
+The help button has been clicked.
+.TP
+.B ok
+The OK  button has been clicked.
+.TP
+.B hide
+The Hide button has been clicked.
+The window will be deleted, and an entry
+shown on the toolbar.
+.PP
+In order to function correctly, an application
+should process not only events from the
+title bar channel, but also events from
+the Tk toplevel
+.I wreq
+channel, those received from the window
+manager itself (via
+.IB top .ctxt.ctl\fR),\fP
+and pointer and keyboard events received from
+the window manager (via
+.IB top .ctxt.ptr
+and
+.IB top .ctxt.kbd
+respectively).
+Control events can be passed to
+.BR wmctl ;
+pointer and keyboard events should be
+passed to their respective functions
+in
+.IR tk (2).
+.PP
+When created, the window is not visible
+and will not receive pointer or keyboard events.
+.B Onscreen
+makes it visible, and possibly chooses a
+position and a size for it.
+.I How
+specifies what sort of placement is required
+for the window; it can be one of
+.TP
+.B place
+tries to choose a suitable place on the screen
+with respect to other windows; it may size the
+window as it feels appropriate. This the default
+(if
+.I how
+is nil).
+.TP
+.B onscreen
+tries to keep the position and size the same
+as specified on the window, adjusting them only
+to bring the window fully on screen, and making sure
+that the window is no bigger than the entire display.
+.TP
+.B exact
+does not change the specified size or position
+of the window unless absolutely necessary.
+.PP
+.B Startinput
+informs the window manager that the window is
+ready to the event types specified in
+.IR devs .
+Currently understood are
+.B kbd
+for keyboard events, and
+.B ptr
+for pointer events.
+.PP
+The simplest well-behaved
+.I wm (1)
+client will therefore contain:
+.PP
+.EX
+	(top, ctl) := tkclient->toplevel(ctxt, nil, "My Program", Tkclient->Appl);
+	# ... populate the window with tk widgets
+	tkclient->startinput(top, "ptr" :: "kbd" :: nil);
+	tkclient->onscreen(top, nil);
+	for(;;){
+		alt{
+		s := <-ctl or
+		s = <-top.ctxt.ctl or
+		s = <-top.wreq =>
+			tkclient->wmctl(top, s);
+		p := <-top.ctxt.ptr =>
+			tk->pointer(top, *p);
+		c := <-top.ctxt.kbd =>
+			tk->keyboard(top, c);
+		}
+	}
+.EE
+.PP
+.B Settitle
+changes the name displayed in the title bar
+and the window's name when it is in the task bar.
+.PP
+.B Snarfget
+and
+.B snarfput
+retrieve and replace the contents of the window
+manager's snarf buffer.
+.SH FILES
+.TF /chan/snarf
+.TP
+.B /chan/snarf
+snarf buffer maintained by
+.IR wm (1)
+.TP
+.B /chan/wm
+channel for interaction with
+.IR wm (1)
+.SH SOURCE
+.B /appl/lib/tkclient.b
+.SH SEE ALSO
+.IR wm (1),
+.IR tk (2)
--- /dev/null
+++ b/man/2/translate
@@ -1,0 +1,135 @@
+.TH TRANSLATE 2
+.SH NAME
+translate: opendict, opendicts, mkdictname \- translation dictionaries
+.SH SYNOPSIS
+.EX
+include "translate.m";
+translate := load Translate Translate->PATH;
+
+Dict: adt {
+    new:    fn(): ref Dict;
+    add:    fn(d: self ref Dict, file: string): string;
+    xlate:  fn(d: self ref Dict, s: string): string;
+    xlaten: fn(d: self ref Dict, s: string, note: string): string;
+};
+
+init:       fn();
+opendict:   fn(file: string): (ref Dict, string);
+opendicts:  fn(files: list of string): (ref Dict, string);
+mkdictname: fn(locale, app: string): string;
+.EE
+.SH DESCRIPTION
+The
+.B Translate
+module provides access to the translation dictionaries
+defined by
+.IR translate (6),
+intended for the translation of
+text from one natural language to another.
+.PP
+.B Init
+should be called before using any of these functions.
+.PP
+.B Opendict
+opens a dictionary
+.I file
+(of the format defined below) and returns a tuple:
+a reference to a
+.B Dict
+that represents it and a diagnostic string (which is nil if no error occurred).
+.B Opendicts
+is similar, but loads each of the
+.I files
+in turn into the same
+.BR Dict ,
+producing a composite dictionary in which translations in later files can override
+translations in earlier ones;
+the diagnostic string summarises all errors (if any).
+.PP
+.B Mkdictname
+returns the conventional name of a dictionary file given
+locale and application names.
+The
+.I locale
+is normally
+.B nil
+to use the current locale, which is formed by
+binding the desired locale directory (or directories) onto
+.BR /lib/locale .
+.PP
+.B Dict.new
+returns an empty dictionary.
+.B Dict.add
+loads the given dictionary
+.I file
+into an existing dictionary, returning a non-nil diagnostic string on error.
+Translations are made by
+.B Dict.xlate
+and
+.BR Dict.xlaten :
+they look for a string
+.I s
+(eg, text in one language),
+optionally qualified by a
+.IR note ,
+and return the corresponding translation text from the dictionary.
+If no such translation exists, they return the original text
+.IR s .
+.SH EXAMPLE
+The following shows one possible style of use:
+.PP
+.EX
+.ta 4n 8n 12n 16n 20n
+include "translate.m";
+	translate: Translate;
+	Dict: import translate;
+
+dict: ref Dict;
+
+X(s: string): string
+{
+	if(dict == nil)
+		return s;
+	return dict.xlate(s);
+}
+
+init(ctxt: ref Draw->Context, args: list of string)
+{
+	...
+	translate = load Translate Translate->PATH;
+	if(translate != nil){
+		translate->init();
+		(dict, nil) = translate->opendict(
+					translate->mkdictname("", "vmail"));
+	}
+	...
+	optioncfg := array [] of {
+		"frame .op -relief flat -borderwidth 8",
+		"frame .op.lbs",
+		"label .op.lbs.a -text {" +
+			X("Voice Mail Active") + ":}",
+		"label .op.lbs.g -text {" +
+			X("Answer Calls With") + ":}",
+		"label .op.lbs.r -text {" +
+			X("Rings before Answering") + ":}",
+		"label .op.lbs.l -text {" +
+			X("Length of Incoming Messages") + ":}}",
+		...
+	};
+	...
+	wmlib->tkcmds(top, optioncfg);
+}
+.EE
+.PP
+The intermediate function
+.B X
+is defined to allow the program to be used (albeit with text in English) even
+when the
+.B Translate
+module cannot be loaded.
+.SH FILES
+.BI /locale/ locale /dict/ app
+.SH SOURCE
+.B /appl/lib/translate.b
+.SH SEE ALSO
+.IR translate (6)
--- /dev/null
+++ b/man/2/ubfa
@@ -1,0 +1,273 @@
+.TH UBFA 2
+.SH NAME
+ubfa: readubf, writeubf, UValue \- read, write and represent values in a UBF(A) data transport encoding
+.SH SYNOPSIS
+.EX
+include "ubfa.m";
+ubfa := load UBFa UBFa->PATH;
+
+UValue: adt {
+    pick{
+    Atom =>
+        name:  string;
+    Int =>
+        value: int;
+    String =>
+        s:     string;
+    Binary =>
+        a:     array of byte;
+    Tuple =>
+        a:     cyclic array of ref UValue;  # tree
+    List =>
+        l:     cyclic list of ref UValue;   # tree
+    Tag =>
+        name:  string;
+        o:     cyclic ref UValue;
+    }
+    isatom:    fn(o: self ref UValue): int;
+    isstring:  fn(o: self ref UValue): int;
+    isint:     fn(o: self ref UValue): int;
+    istuple:   fn(o: self ref UValue): int;
+    isop:      fn(o: self ref UValue, op: string, arity: int): int;
+    islist:    fn(o: self ref UValue): int;
+    isbinary:  fn(o: self ref UValue): int;
+    istag:     fn(o: self ref UValue): int;
+    eq:        fn(o: self ref UValue, v: ref UValue): int;
+    op:        fn(o: self ref UValue, arity: int): string;
+    args:      fn(o: self ref UValue, arity: int):
+                array of ref UValue;
+    els:       fn(o: self ref UValue): list of ref UValue;
+    val:       fn(o: self ref UValue): int;
+    binary:    fn(o: self ref UValue): array of byte;
+    objtag:    fn(o: self ref UValue): string;
+    obj:       fn(o: self ref UValue): ref UValue;
+    text:      fn(o: self ref UValue): string;
+};
+
+init:     fn(bufio: Bufio);
+readubf:  fn(input: ref Iobuf): (ref UValue, string);
+writeubf: fn(output: ref Iobuf, v: ref UValue): int;
+uniq:     fn(s: string): string;
+
+uvatom:   fn(s: string): ref UValue.Atom;
+uvint:    fn(i: int): ref UValue.Int;
+uvstring: fn(s: string): ref UValue.String;
+uvbinary: fn(a: array of byte): ref UValue.Binary;
+uvtuple:  fn(a: array of ref UValue): ref UValue.Tuple;
+uvlist:   fn(l: list of ref UValue): ref UValue.List;
+uvtag:    fn(name: string, o: ref UValue): ref UValue.Tag;
+.EE
+.SH DESCRIPTION
+.B UBFa
+provides value representations, and encoding and decoding operations for Armstrong's UBF(A) data transport
+format, defined by
+.IR ubfa (6).
+.PP
+.B Init
+must be called before invoking any other operation of the module.
+The
+.I bufio
+parameter must refer to the instance of
+.IR bufio (2)
+that provides the
+.B Iobuf
+parameters used for input and output.
+.PP
+.B UValue
+is the internal representation of values that can be transmitted by the UBF(A) encoding.
+The various sorts of values are distinguished in a pick adt:
+.TP
+.B UValue.Atom
+Represents an
+.IR atom :
+a symbolic constant, for example the name of an operation or an enumeration literal.
+The string
+.B name
+gives the spelling of the constant's name.
+.TP
+.B UValue.Int
+Represents an integer value (eg, a Limbo
+.BR int )
+with the given
+.BR value .
+.TP
+.B UValue.String
+Represents a character string (eg, a Limbo
+.BR string )
+with the value
+.BR s .
+.TP
+.B UValue.Binary
+Represents binary data as a sequence of bytes in the array
+.BR a .
+.TP
+.B UValue.Tuple
+Represents a compound value that contains a fixed number of component values,
+given by successive elements of the array
+.BR a .
+UBF tuples correspond to tuples or non-pick
+.B adt
+values in Limbo.
+.TP
+.B UValue.List
+Represents a compound value containing a variable number of component values,
+given by successive elements of the list
+.BR l .
+.TP
+.B UValue.Tag
+Associates an application-specific
+.B tag
+with another
+.B UValue
+referenced by
+.BR o .
+.PP
+.B Readubf
+reads a single value in
+.IR ubfa (6)
+format from the
+.I input
+stream and returns a tuple
+.BI ( val,\ err ).
+On success,
+.I val
+is a
+.B UValue
+that represents that value.
+If an error occurs,
+.I val
+is nil and
+.I err
+contains a diagnostic.
+.PP
+.B Writeubf
+writes a
+.IR ubfa (6)
+representation of the value
+.I v
+to the
+.I output
+stream.
+It returns 0 on success and -1 on error (setting the system error string).
+.PP
+The easiest way to create a new
+.B UValue
+for subsequent output is with one of the module-level functions
+.BR uvatom ,
+.BR uvint ,
+.BR uvstring ,
+and so on.
+As values of a pick adt, a
+.B UValue
+can be inspected using Limbo's
+.B tagof
+operator and the appropriate variant accessed using a
+.B pick
+statement.
+.B UValue
+also supports several groups of common operations, for smaller, tidier code.
+First, the set of enquiry functions
+.IB u .is X ()
+return true if the value
+.I u
+is an instance of the UBF type
+.I X
+.RI ( atom ,
+.IR int ,
+.IR string ,
+.IR binary ,
+.IR tuple ,
+etc).
+The other operations are:
+.TP
+.IB u .eq( v )
+Return true if the values of
+.I u
+and
+.I v
+are equal, including the values of corresponding subcomponents, recursively
+.TP
+.IB u .isop( op,\ n )
+Return true if
+.I u
+is a tuple having
+.I n
+components, and its first component is an atom or string with the value
+.IR op .
+.TP
+.IB u .op( n )
+If
+.I u
+is a tuple with
+.I n
+components, and the first component is an atom or string, return its value.
+Otherwise, return nil.
+.TP
+.IB u .args( n )
+If
+.I u
+is a tuple with
+.I n
+components, return an array containing the values of all but the first component.
+Otherwise, return  nil.
+.TP
+.IB u .els()
+If
+.I u
+is a list, return a Limbo list of its elements (ie,
+.IB u .l\fR)\fP.
+Otherwise, return nil.
+.TP
+.IB u .val()
+If
+.I u
+is an integer, return its value.
+Otherwise return zero.
+.TP
+.IB u .binary()
+If
+.I u
+is a binary value, return the corresponding array of bytes; if
+.I u
+is an atom or string, return an array of bytes containing its value;
+otherwise, return nil.
+.TP
+.IB u .objtag()
+If
+.I u
+is a tag, return the name of the tag.
+Otherwise, return nil.
+.TP
+.IB u .obj()
+If
+.I u
+is a tag, return the tagged value.
+Otherwise, return
+.I u
+itself.
+.TP
+.IB u .text()
+Return a printable representation of the value
+.IR u ,
+mainly intended for debugging and tracing.
+.PP
+One difference between atoms and strings is that
+all atoms with identical spellings refer to the same string in the implementation's storage.
+Given an atom name,
+.B uniq
+returns the corresponding string, stored in an internal dictionary.
+It is used by
+.B UBFa
+to create the strings
+.BR UValue.Atom.s ,
+and can be put to similar use directly by applications.
+It should only be applied to values that are small in number (as with symbolic constants).
+.SH SOURCE
+.B /appl/lib/ubfa.b
+.SH SEE ALSO
+.IR sexprs (2),
+.IR ubfa (6)
+.br
+J L Armstrong, ``Getting Erlang to talk to the outside world'',
+.I "ACM SIGPLAN Erlang workshop 2002" ,
+Pittsburg, PA USA
--- /dev/null
+++ b/man/2/venti
@@ -1,0 +1,103 @@
+.TH VENTI 2
+.SH NAME
+Venti \- access to Venti content-addressed filestore.
+.SH SYNOPSIS
+.EX
+include "venti.m";
+venti := load Venti Venti->PATH;
+Session: import venti;
+
+init:			fn();
+
+Session: adt {
+	new:		fn(fd: ref Sys->FD): ref Session;
+	read:		fn(s: self ref Session, score: Venti->Score, etype: int, maxn: int): array of byte;
+	write:	fn(s: self ref Session, etype: int, buf: array of byte): (int, Venti->Score);
+	sync:	fn(s: self ref Session): int;
+};
+
+Score: adt {
+	a: array of byte;
+	eq:		fn(a: self Score, b: Score): int;
+	text:		fn(a: self Score): string;
+	parse:	fn(s: string): (int, Score);
+	zero:		fn(): Score;
+};
+
+.EE
+.SH DESCRIPTION
+.I Venti
+is a block storage server intended for archival applications.
+The
+.I Venti
+module provides low-level access to a Venti server.
+The module assumes that the physical connection
+to the server has already been established
+(for example, by
+.IR dial (2)).
+On a Venti server, a block is addressed by the SHA1 hash of
+the contents of that block, known as a
+.IR score ,
+and represented as a
+.B Score
+adt.
+Blocks are additionally tagged with a
+.IR type ,
+facilitating recovery in the event of corruption.
+A
+.B Session
+represents a session with a Venti server.
+.TP
+.IB s .new(\fIfd\fP)
+.B New
+performs the initial handshake with the Venti server,
+returning established
+.BR Session .
+.TP
+.IB s .read(\fIscore\fP,\ \fIetype\fP,\ \fImaxn\fP)
+.B Read
+tries to retrieve the block
+corresponding to
+.IR score ,
+and of type
+.IR etype .
+The block must be no longer than
+.I maxn
+bytes.
+.I Etype
+is conventionally one of the constants
+.BR Roottype ,
+.BR Dirtype ,
+.BR Datatype
+or
+.BR Pointertype [0-9],
+where the different
+.BR Pointertype s
+represent different depth levels within a Venti tree.
+.TP
+.IB s .write(\fIetype\fP,\ \fIbuf\fP)
+.B Write
+writes the data in
+.I buf
+to the Venti server.
+The block will be tagged with type
+.IR etype .
+It returns a tuple, say
+.RI ( ok ,\  score );
+on error,
+.I ok
+is -1, otherwise
+.I ok
+is 0 and
+.I score
+contains the Venti score for the block that has been written.
+.TP
+.IB s .sync()
+.B Sync
+tells the Venti server to make sure that all data is committed to
+active storage.
+.SH SOURCE
+.B /module/venti.m
+.br
+.B /appl/lib/venti.b
+.SH BUGS
--- /dev/null
+++ b/man/2/virgil
@@ -1,0 +1,54 @@
+.TH VIRGIL 2
+.SH NAME
+virgil \- pose question to name resolver
+.SH SYNOPSIS
+.EX
+include "security.m";
+virgil := load Virgil Virgil->PATH;
+
+virgil:	fn(args: list of string): string;
+.EE
+.SH DESCRIPTION
+.B Virgil
+provides a client side interface for interactions with the
+name resolution service
+.IR virgild (8).
+.PP
+.I Args
+is a command line of the form:
+.IP
+.BR virgil " [" -v
+.IR address ]
+.RI [ name ]
+.PP
+.I Name
+is the host name to be resolved to a network address.
+Normally the request is broadcast to any and all
+.I virgild
+servers on the network, which
+.B virgil
+expects to find on UDP/IP port 2202.
+The
+.B -v
+option instead directs the request to the given numeric IP
+.IR address .
+.PP
+.B Virgil
+waits up to 5 seconds for a reply, retransmitting the request once a second.
+It accepts and returns the first answer it receives.
+.SH SOURCE
+.B /appl/lib/virgil.b
+.SH SEE ALSO
+.IR dial (2),
+.IR cs (8),
+.IR register (8),
+.IR virgild (8)
+.SH DIAGNOSTICS
+.B Virgil
+returns
+.L nil
+for invalid arguments and if the name was not resolved.
+.SH BUGS
+The
+.I virgild
+port is hard coded.
--- /dev/null
+++ b/man/2/volume
@@ -1,0 +1,51 @@
+.TH VOLUME 2 mux
+.SH NAME
+volume \- volume control for an infrared interface
+.SH SYNOPSIS
+.EX
+include "volume.m";
+vctl := load Volumectl Volumectl->PATH;
+
+volumectl: fn(ctxt: ref Draw->Context, ch: chan of int,
+			device: string);
+.EE
+.SH DESCRIPTION
+.B Volumectl
+should be spawned as a separate process from any process that desires volume control via an infrared interface.
+.I Ctxt
+provides
+.B volumectl
+with access to the display,
+on which it displays a slider widget.
+The slider automatically disappears after several seconds of inactivity.
+.B Volumectl
+receives input from the infrared controller on channel
+.IR ch .
+The values recognized are:
+.TF Enter
+.PD
+.TP
+.B VolUP
+increase volume
+.TP
+.B VolDN
+decrease volume
+.TP
+.B Enter
+exit
+.PP
+.I Device
+is a string used as a prefix to commands to the device;
+for example
+.B
+"audio out"
+.ft P
+for
+.IR audio (3).
+.SH FILES
+.B /dev/volume
+.SH SOURCE
+.B /appl/lib/volume.b
+.SH "SEE ALSO"
+.IR ir (2),
+.IR prefab-intro (2)
--- /dev/null
+++ b/man/2/w3c-css
@@ -1,0 +1,349 @@
+.TH W3C-CSS 2
+.SH NAME
+w3c-css \- cascading style sheet parser
+.SH SYNOPSIS
+.EX
+include "css.m";
+
+css := load CSS CSS->PATH;
+
+Stylesheet: adt {
+   charset:    string;
+   imports:    list of ref Import;
+   statements: list of ref Statement;
+};
+
+Import: adt {
+   name:   string;
+   media:  list of string;
+};
+
+Statement: adt {
+   pick{
+   Media =>
+       media:  list of string;
+       rules:  list of ref Statement.Ruleset;
+   Page =>
+       pseudo: string;
+       decls:  list of ref Decl;
+   Ruleset =>
+       selectors: list of Selector;
+       decls:     list of ref Decl;
+   }
+};
+
+Decl: adt {
+   property:   string;
+   values:     list of ref Value;
+   important:  int;
+};
+
+Selector:   type list of (int, Simplesel);   # (combinator, simplesel)
+Simplesel:  type list of ref Select;
+
+Select: adt {
+   name:   string;
+   pick{
+   Element or ID or Any or Class or Pseudo =>
+       # empty
+   Attrib =>
+       op:     string; # "=" "~=" "|="
+       value:  ref Value;  # optional Ident or String
+   Pseudofn =>
+       arg:    string;
+   }
+};
+
+Value: adt {
+   sep:    int;    # operator preceding this term
+   pick{
+   String or
+   Number or
+   Percentage or
+   Url or
+   Unicoderange =>
+       value:  string;
+   Hexcolour =>
+       value:  string;             # as given
+       rgb:    (int, int, int);    # converted
+   RGB =>
+       args:   cyclic list of ref Value;  # as given
+       rgb:    (int, int, int);           # converted
+   Ident =>
+       name:   string;
+   Unit =>
+       value:  string; # int or float
+       units:  string; # suffix giving units
+   Function =>
+       name:   string;
+       args:   cyclic list of ref Value;
+   }
+};
+
+init:       fn(diag: int);
+parse:      fn(s: string): (ref Stylesheet, string);
+parsedecl:  fn(s: string): (list of ref Decl, string);
+.EE
+.SH DESCRIPTION
+.B Css
+implements a parser for the World-Wide Web Consortium's Cascading
+Style Sheet, specification 2.1.
+.PP
+.B Init
+must be called before any other operation in the module.
+If
+.I diag
+is non-zero, the module will print diagnostics on standard output for
+malformed or unrecognised items that are ignored during parsing (as
+required by the specification).
+.PP
+.B Parse
+takes a complete stylesheet in string
+.IR s ,
+parses it, and returns a tuple
+.BI ( sheet,\ err )
+where
+.I sheet
+refers to a
+.B Stylesheet
+value containing the logical content of
+.IR s ,
+as described below.
+On a fatal error,
+.I sheet
+is nil and
+.I err
+is a diagnostic.
+Most syntactic errors are ignored, as the specification requires.
+.PP
+In some applications there can be auxiliary declarations outside a stylesheet.
+.B Parsedecl
+takes a string
+.I s
+containing a sequence of declarations, and returns a tuple
+.BI ( decls,\ err )
+where
+.I decls
+is a list of references to
+.B Decl
+values, each representing a single
+.I declaration
+in
+.IR s .
+On a fatal error,
+.I decls
+is nil, and
+.I err
+is a diagnostic.
+.PP
+The adts represent an abstract syntax of the CSS grammar.
+The concrete syntax is presented below in an extended BNF,
+derived from the reference grammar,
+with each section labelled by the name of the corresponding adts.
+(Compared to the reference grammar in the
+specification, it abstracts away from the complex rules about where whitespace can appear.)
+.TP
+.B Stylesheet
+.EX
+.ft R
+\f2stylesheet\fP ::= [ '\f5@charset\fP' STRING '\f5;\fP' ] \f2import\fP* \f2statement\fP*
+.EE
+.IP
+Limbo lists represent lists of items in the grammar.
+Nil values denote optional components that are missing.
+Upper-case names such as
+IDENT,
+STRING
+and
+NUMBER
+are terminals; see the CSS specification for their
+often subtle definitions.
+They are usually represented
+by Limbo string values in the adts.
+.TP
+.B Import
+.EX
+.ft R
+\f2import\fP ::= '\f5@import\fP' (\f2STRING\fP|\f2uri\fP) [\f2medium\fP ('\f5,\fP' \f2medium\fP)*] '\f5;\fP'
+\f2uri\fP ::= '\f5url(\fP' STRING '\f5)\fP'
+.EE
+.IP
+.B Import.name
+holds the text of the
+STRING
+or
+.IR uri .
+.TP
+.B Statement
+.EX
+.ft R
+\f2statement\fP ::= \f2ruleset\fP | \f2media\fP | \f2page\fP
+\f2media\fP ::= '\f5@media\fP' \f2medium\fP ('\f5,\fP' \f2medium\fP)* '\f5{\fP' \f2ruleset\fP* '\f5}\fP'
+\f2medium\fP ::= IDENT
+\f2page\fP ::= '\f5@page\fP' [\f2pseudo_page\fP] '\f5{\fP' \f2declaration\fP ('\f5;\fP' \f2declaration\fP)* '\f5}\fP'
+\f2pseudo_page\fP ::= '\f5:\fP' IDENT
+\f2ruleset\fP ::= \f2selector\fP ('\f5,\fP' \f2selector\fP)* '\f5{\fP' \f2declaration\fP ('\f5;\fP' \f2declaration\fP)* '\f5}\fP'
+.EE
+.IP
+.B Statement
+is not in the reference grammar, but is introduced here to give a name corresponding
+to the pick adt.
+.TP
+.B Decl
+.EX
+.ft R
+\f2declaration\fP ::= \f2property\fP '\f5:\fP' \f2expr\fP ['\f5!\fP' '\f5important\fP'] | /* \f2empty\fP */
+\f2property\fP ::= IDENT
+.EE
+.B Decl.values
+is a list representing the terms of the
+.I expr
+(see below for details).
+.BR Decl 's
+field
+.B important
+is non-zero if the optional `important' priority is given.
+.TP
+.B "list of ref Value"
+.EX
+.ft R
+\f2expr\fP ::= \f2term\fP (\f2operator\fP \f2term\fP)*
+\f2operator\fP ::= '\f5/\fP' | '\f5,\fP' | /* \f2empty\fP */
+.EE
+.IP
+An
+.I expr
+is always represented as a list of references to
+.B Value
+in some containing structure
+(where
+.B Value
+represents a
+.IR term ,
+see below).
+The
+.I operator
+preceding each
+.I term
+appears as the field
+.B sep
+of the corresponding
+.BR Value ,
+where a space character represents `empty' (concatenation).
+.TP
+.BR Selector
+.EX
+.ft R
+\f2selector\fP ::= \f2simple_selector\fP (\f2combinator\fP \f2simple_selector\fP)*
+\f2combinator\fP ::= '\f5+\fP' | '\f5>\fP' | /* \f2empty\fP */
+.EE
+.IP
+.B Selector
+is just a type synonym for a list of tuples, say
+.BI ( com,\ simplesel )
+where the
+.I simplesel
+value represents
+.I simple_selector
+(see below), and the integer
+.I com
+is one of the characters space (representing `empty'),
+.RB ` > '
+or
+.RB ` + ',
+giving the combinator that preceded the simple selector.
+(The first in the list is always space.)
+.TP
+.BR Simplesel ", " Select
+.EX
+.ft R
+\f2simple_selector\fP ::= \f2element_name\fP (\f2hash\fP | \f2class\fP | \f2attrib\fP | \f2pseudo\fP)*
+	| (\f2hash\fP | \f2class\fP | \f2attrib\fP | \f2pseudo\fP)+
+\f2hash\fP ::= '\f5#\fP' NAME
+\f2class\fP ::= '\f5.\fP' IDENT
+\f2element_name\fP ::= IDENT | '\f5*\fP'
+\f2attrib\fP ::= '\f5[\fP' IDENT [('\f5=\fP' | '\f5|=\fP' | '\f5~=\fP') (IDENT | STRING)] '\f5]\fP'
+\f2pseudo\fP ::= '\f5:\fP' ( IDENT | IDENT '\f5(\fP' [IDENT] '\f5)\fP' )
+.EE
+.IP
+A
+.I simple_selector
+is represented by
+.BR Simplesel ,
+a list of references to
+.B Select
+values, each representing one
+.I element_name
+or qualifier.
+An
+.I element_name
+is represented by
+.B Select.Element
+for an
+IDENT,
+or
+.B Select.Any
+for
+.RB ` * '.
+The qualifiers are
+.I hash
+.RB ( Select.ID ),
+.I class
+.RB ( Select.Class ),
+.I attrib
+.RB ( Select.Attrib ,
+where  the comparison operator is the string
+.BR op ),
+.I pseudo
+(either
+.B Select.Pseudo
+if a plain identifier, or
+.B Select.Pseudofn
+for a function with optional parameter).
+.TP
+.B Value
+.EX
+.ft R
+\f2term\fP ::= ['\f5+\fP' | '\f5-\fP'] (NUMBER | \f2percent\fP | \f2unit\fP) | STRING | IDENT | \f2uri\fP | \f2function\fP | \f2hexcolour\fP | \f2rgb\fP
+\f2function\fP ::= IDENT '\f5(\fP' \f2expr\fP '\f5)\fP'
+\f2hash\fP ::= '\f5#\fP' NAME
+\f2hexcolour\fP ::= '\f5#\fP' HEXDIGIT+
+\f2percent\fP ::= NUMBER '\f5%\fP'
+\f2unit\fP ::= NUMBER STRING
+\f2rgb\fP ::= '\f5rgb(\fP' \f2term\fP '\f5,\fP' \f2term\fP '\f5,\fP' \f2term\fP '\f5)\fP'
+\f2uri\fP ::= '\f5url(\fP' STRING '\f5)\fP'
+.EE
+.IP
+Any sign before a
+.BR Number ,
+.B Percentage
+or
+.B Unit
+appears as the first character of
+.BR value .
+All the dimensional units (LENGTH, EMS, EXS, ANGLE, TIME, FREQ and others)
+in the reference grammar are mapped to
+.BR Value.Unit ,
+with the field
+.B units
+containing the name of the relevant unit (eg,
+.LR cm ,
+.LR in ,
+etc.) in lower case.
+Values and names appear shorn of the surrounding punctuation.
+.B Value.Hexcolour
+includes the original sequence of hex digits as a string,
+and a decoding of it as an
+.B rgb
+triple.
+The arguments to the CSS
+.B rgb
+function are similarly presented in original and decoded forms, in
+.BR Value.RGB .
+Other function references are returned uninterpreted in
+.BR Value.Function .
+.SH SOURCE
+.B /appl/lib/w3c/css.b
+.SH SEE ALSO
+``Cascading Style Sheets, level 2 revision 1'',
+.B http://www.w3.org/TR/CSS21
--- /dev/null
+++ b/man/2/w3c-uris
@@ -1,0 +1,243 @@
+.TH W3C-URIS 2
+.SH NAME
+w3c-uris \- uniform resource identifiers
+.SH SYNOPSIS
+.EX
+include "uris.m";
+
+uris := load URIs URIs->PATH;
+URI: import uris;
+
+URI: adt
+{
+   scheme:     string;
+   userinfo:   string;  # authority, part I
+   host:       string;  # authority, part II
+   port:       string;  # authority, part III
+   path:       string;  # starts with / if absolute
+   query:      string;  # includes ? if not nil
+   fragment:   string;  # includes # if not nil
+
+   parse:      fn(s: string): ref URI;
+   text:       fn(u: self ref URI): string;
+   addbase:    fn(base: self ref URI, rel: ref URI): ref URI;
+   authority:  fn(u: self ref URI): string;
+   copy:       fn(u: self ref URI): ref URI;
+   eq:         fn(u: self ref URI, v: ref URI): int;
+   hasauthority: fn(u: self ref URI): int;
+   isabsolute: fn(u: self ref URI): int;
+   nodots:     fn(u: self ref URI): ref URI;
+   userpw:     fn(u: self ref URI): (string, string);
+};
+
+init: fn();
+dec:  fn(s: string): string;
+enc:  fn(s: string, safe: string): string;
+.EE
+.SH DESCRIPTION
+.B URIs
+supports the `generic syntax' for `Uniform' Resource Identifiers (URIs), defined by RFC3986.
+Each URI can have up to five components in the general syntax:
+.IP
+.IB scheme :
+.BI // authority / path
+.BI ? query
+.BI # fragment
+.PP
+where each component is optional, and can have scheme-specific substructure.
+For instance, in the
+.BR ftp ,
+.B http
+schemes, and perhaps others, the
+.I authority
+component has the further syntax:
+.IP
+.IB userinfo @ host : port
+.PP
+The set of characters allowed in most components is also scheme-specific, as is their interpretation, and indeed the
+interpretation of the component itself.
+.PP
+.B Init
+must be called before any other operation in the module.
+.PP
+.B URI
+represents a parse of a URI into its components, where the
+.I authority
+has been further split into the scheme-specific but common triple of
+.IR userinfo ,
+.I host
+and
+.IR port .
+(The function
+.B URI.authority
+will reproduce the original
+.I authority
+component if required.)
+The
+.B query
+field starts with the
+.RB ` ? '
+character that introduces the
+.I query
+component, so that an empty query is represented by the string
+\f5"?"\fP, and the absence of a query component is represented by a nil value.
+The
+.B fragment
+field is handled in a similar way with its delimiting
+.RB ` # '.
+The fields representing the other components do not include the delimiters in the syntax,
+and all but
+.B query
+have percent-encoded characters decoded.
+(The query string is an exception because the set of characters to escape is application-specific.
+See below for decoding and encoding functions.)
+.B URI
+provides the following operations:
+.TP
+.BI parse( s )
+Return a
+.B URI
+value representing the results of parsing string
+.I s
+as a URI.
+There is no error return.
+The component values have percent-escapes decoded as discussed above.
+The scheme name is converted to lower case.
+.TP
+.IB u .text()
+Return the textual representation of
+.I u
+in the generic syntax,
+adding percent-encoding as required to prevent characters
+being misinterpreted as delimiters.
+.TP
+.IB u .addbase( b )
+Resolves URI reference
+.I u
+with respect to a base URI
+.IR b ,
+including resolving all
+.RB ` . '
+and
+.RB ` .. '
+segments in the URI's path,
+and returns the resulting
+.B URI
+value.
+If
+.I u
+is an absolute URI reference or
+.I b
+is nil, the result is the same as
+.I u
+except that all
+.RB ` . '
+and
+.RB ` .. '
+segments have been resolved in the resulting path, and leading
+instances of them removed.
+.TP
+.IB u .authority()
+Returns the text of the
+.I authority
+component of
+.IR u ,
+in the generic syntax,
+created from its
+.BR userinfo ,
+.B host
+and
+.B port
+components.
+.TP
+.IB u .copy()
+Return a reference to an independent copy of
+.IR u .
+.TP
+.IB u .eq( v )
+Returns true if
+.I u
+and
+.I v
+are textually equal in all components except
+.BR fragment .
+Note that
+.I u
+and
+.I v
+are assumed to be in a canonical form for the scheme and application.
+.TP
+.IB u .eqf( v )
+Returns true if
+.I u
+and
+.I v
+are textually equal in all components including
+.BR fragment .
+.TP
+.IB u .hasauthority()
+Returns true if any of the authority subcomponents of
+.I u
+are not nil; returns false otherwise.
+.TP
+.IB u .isabsolute()
+Returns true if
+.I u
+has a
+.I scheme
+component; returns false otherwise.
+.TP
+.IB u .nodots()
+Returns a new
+.B URI
+value in which all
+.RB ` . '
+and
+.RB ` .. '
+segments have been resolved (equivalent to
+.IB u .addbase(nil)\c
+).
+.TP
+.IB u .userpw()
+Returns a tuple
+.BI ( username,\ password )
+derived from parsing the
+.I userinfo
+subcomponent of
+.I authority
+using the deprecated but depressingly still common convention that
+.I userinfo
+has the syntax ``\fIusername\fP\f5:\fP\fIpassword\fP''.
+.PP
+A reserved or otherwise special character that appears in a URI component must be
+encoded using a sequence of one or more strings of the form
+.BI % xx
+where
+.I xx
+is the hexadecimal value of one byte of the character's encoding in
+.IR utf (6).
+A string
+.I s
+containing such encodings can be decoded by the function
+.BR dec .
+A string
+.I s
+can be encoded by
+.BR enc ,
+where the parameter
+.I safe
+lists the characters that need not be escaped (where
+.I safe
+may be nil or empty).
+These functions are normally only needed to decode and encode the values of
+.BR URI.query ,
+because
+.B URI.parse
+and
+.B URI.text
+above decode and encode the other fields.
+.SH SOURCE
+.B /appl/lib/w3c/uris.b
+.SH SEE ALSO
+.IR charon (1),
+.IR httpd (8)
--- /dev/null
+++ b/man/2/w3c-xpointers
@@ -1,0 +1,382 @@
+.TH W3C-XPOINTERS 2
+.SH NAME
+w3c-xpointers \- parser for XPointers framework including XPath
+.SH SYNOPSIS
+.EX
+include "xpointers.m";
+
+xpointers := load Xpointers Xpointers->PATH;
+Xpath, Xstep: import xpointers;
+
+# special operators ('+', '-', etc represent themselves)
+One, Ole, Oge, Omul, Odiv, Omod, Oand, Oor, Oneg,
+Onodetype, Onametest, Ofilter, Opath: con ...;
+
+# axis types
+Aancestor,
+Aancestor_or_self,
+Aattribute,
+Achild,
+Adescendant,
+Adescendant_or_self,
+Afollowing,
+Afollowing_sibling,
+Anamespace,
+Aparent,
+Apreceding,
+Apreceding_sibling,
+Aself: con iota;
+
+Xstep: adt {
+   axis:  int;  # Aancestor, ... (above)
+   op:    int;  # Onametest or Onodetype
+   ns:    string;
+   name:  string;
+   arg:   string;  # optional parameter to processing-instruction
+   preds: cyclic list of ref Xpath;
+
+   text:  fn(nil: self ref Xstep): string;
+   axisname:  fn(i: int): string;
+};
+
+Xpath: adt {
+   pick{
+   E =>
+      op:    int;
+      l, r:  cyclic ref Xpath;
+   Fn =>
+      ns:    string;
+      name:  string;
+      args:  cyclic list of ref Xpath;
+   Var =>
+      ns:    string;
+      name:  string;
+   Path =>
+      abs:   int;
+      steps: list of ref Xstep;
+   Int =>
+      val:   big;
+   Real =>
+      val:   real;
+   Str =>
+      s:     string;
+   }
+   text: fn(nil: self ref Xpath): string;
+};
+
+framework:  fn(s: string):
+               (string, list of (string, string, string), string);
+
+# predefined schemes
+element:    fn(s: string): (string, list of int, string);
+xmlns:      fn(s: string): (string, string, string);
+xpointer:   fn(s: string): (ref Xpath, string);
+.EE
+.SH DESCRIPTION
+.B Xpointers
+implements a parser for the World-Wide Web Consortium's XPointers framework,
+including a parser for XPath expressions.
+.PP
+.B Init
+must be called before any other operation in the module.
+.PP
+.B Framework
+parses a string
+.I s
+according to the grammar for the XPointers framework,
+and returns a tuple
+.BI ( short,\ pointers,\ err ) .
+On an error, the string
+.I err
+gives a diagnostic and the other two values are nil.
+Otherwise, if
+.I short
+is non-nil, the XPointer was a `shorthand pointer', with the given value;
+.I pointers
+will be nil.
+If a scheme-based pointer is used,
+.I short
+is nil and
+.I pointers
+has a list of tuples
+.BI ( ns,\ scheme,\ data ) ,
+each representing one pointer value.
+.I Ns
+is the XML name space for the given
+.IR scheme ;
+the default name space is represented by nil.
+.I Scheme
+is the XPointer scheme name within that name space; and
+.I data
+is the actual pointer value following the rules of that scheme.
+(They all have completely different syntax.)
+.PP
+Three common schemes are directly supported by the module,
+by functions named after the scheme.
+All of them follow the convention of returning a tuple in which
+the last element is a diagnostic string.
+On an error, all but that last element of the tuple will be nil,
+and the last element will be a non-nil string with a diagnostic.
+.PP
+.B Xmlns
+parses an XML name space definition of the form
+.IB ns = uri,
+and returns its components.
+.PP
+.B Element
+parses a value of the XPointer
+.B element
+scheme, given by the grammar:
+.IP
+.EX
+.ft I
+selector \f1::=\fP name child*  \f1|\fP  child+
+child \f1::=\fP '\f5/\fP' \f5[1-9][0-9]\fP*
+.EE
+.PP
+The optional
+.I name
+is an XPointer `shorthand pointer'.
+Each
+.I child
+number selects the child with that index (origin 1) at the corresponding level of the XML tree
+beneath the node selected by the
+.IR name ,
+or starting at the root of the XML tree.
+.B Element
+returns a tuple
+.BI (( name,\ path ) ,\ err )
+where
+.I name
+is the top element name or nil if none was specified,
+and
+.I path
+is a
+.B "list of int"
+giving the path of child indices.
+.PP
+The most complex scheme is
+.BR xpointer ,
+because its syntax is that of XML's elaborate XPath expression.
+.B Xpointer
+parses such an expression and returns a tuple
+.BI ( e,\ err )
+where
+.I e
+refers to an
+.B Xpath
+value that represents the abstract syntax of the XPath
+.BR Expr .
+.B Xpointer
+checks only the syntax of
+.IR s ,
+and does not check that functions are limited to those specified by the
+.B xpointer
+scheme (that is consistent with it being a parse of
+.IR s ,
+rather than an XPointer or XPath evaluator).
+.PP
+.B Xpath
+and
+.B Xstep
+together represent an abstract syntax of the XPath grammar.
+.PP
+.B Xstep
+represents the XPath
+.B Step
+grammar rule, with all abbreviations expanded to their full form:
+.IP
+.EX
+.ft I
+Step \f1::=\fP AxisName '\f5::\fP' NodeTest Predicate*
+NodeTest \f1::=\fP NameTest \f1|\fP NodeType '\f5(\fP' '\f5)\fP'
+NameTest \f1::=\fP '\f5*\fP' \f1|\fP NCName '\f5:\fP' '\f5*\fP' \f1|\fP (NCName '\f5:\fP')? NCName
+Predicate \f1::=\fP '\f5[\fP' Expr '\f5]\fP'
+.EE
+.PP
+The correspondence is as follows:
+.TF s.text()
+.PD
+.TP
+.IB s .axis
+Represents the
+.B AxisName
+by one of the constants
+.B Aancestor
+to
+.BR Aself .
+.TP
+.IB s .op
+.B Onametest
+or
+.B Onodetype
+to say which rule is represented
+.TP
+.IB s .ns
+For a
+.IR NameTest ,
+gives the XML name space;
+can be
+.L *
+for `any name space' or nil for the default name space.
+For a
+.IR NodeType ,
+gives the type:
+.BR comment ,
+.BR node ,
+.BR processing-instruction ,
+or
+.BR text .
+.TP
+.IB s .name
+Gives the
+.I name
+for a
+.IR NameTest ;
+can be
+.L *
+for `any name'.
+.TP
+.IB s .arg
+The optional literal parameter to a
+.I NodeType
+that is a
+.BR processing-instruction .
+.TP
+.IB s .preds
+A list of
+.B Xpath
+values representing the optional sequence of
+.I Predicate
+expressions
+.TP
+.IB s .text()
+Returns a string representing the
+.B Xstep
+in textual form.
+.TP
+.IB s .axisname( a )
+Returns the printable text for axis code
+.I a
+(ie,
+one of
+.B Aancestor
+to
+.BR Aself )
+.PP
+.B Xpath
+values represent an abstract syntax for an XPath expression.
+Briefly, an expression follows the grammar below (see the XPath specification for the full concrete syntax).
+.IP
+.EX
+.ft I
+.ta \w'e ::=  'u
+e ::=	e '\f5or\fP' e
+   |	e '\f5and\fP' e
+   |	e \f1(\fP'\f5=\fP' \f1|\fP '\f5!=\fP'\f1)\fP e
+   |	e \f1(\fP'\f5<\fP' \f1|\fP '\f5<=\fP' \f1|\fP '\f5>=\fP' \f1|\fP '\f5>\fP'\f1)\fP e
+   |	e \f1(\fP'\f5+\fP' \f1|\fP '\f5-\fP'\f1)\fP e
+   |	e \f1(\fP'\f5*\fP' \f1|\fP '\f5div\fP' \f1|\fP '\f5mod\fP'\f1)\fP e
+   |	'\f5-\fP' e
+   |	e '\f5|\fP' e
+   |	filter
+   |	path
+filter ::= primary predicate* \f1(\fP\f1(\fP'\f5/\fP' \f1|\fP '\f5//\fP'\f1)\fP relpath\f1)\fP?
+primary ::= '\f5$\fP' QName \f1|\fP '\f5(\fP' e '\f5)\fP' \f1|\fP Literal \f1|\fP Number \f1|\fP FunctionName '\f5(\fP' \f1(\fPe \f1(\fP'\f5,\fP' e\f1)\fP*\f1)\fP '\f5)\fP'
+path ::= '\f5/\fP' relpath \f1|\fP relpath
+relpath ::= relpath '\f5/\fP' relpath \f1|\fP relpath '\f5//\fP' relpath \f1|\fP Step
+.EE
+.PP
+Most of
+.I e
+is represented by a binary tree using the pick
+.BI Xpath.E( op,\ l,\ r )
+where
+.I op
+is an operator symbol (either the character itself or one of the constants
+.BR One ,
+.BR Odiv ,
+etc. for compound symbols),
+and
+.I l
+and
+.I r
+represent the operands.
+The only unary operator
+.B Oneg
+has its operand in
+.IR l .
+A
+.I filter
+uses the binary operator
+.BI Xpath.E(Ofilter ,\ e,\ pred )
+to apply each
+.I predicate
+to the preceding
+.I primary
+or
+.IR predicate .
+A
+.I filter
+also uses
+.BI Xpath.E(Opath ,\ e,\ relpath )
+to apply the optional
+.I relpath
+(represented by a value of
+.BR Xpath.Path )
+to the preceding part of the filter expression.
+.PP
+The other cases in the pick adt correspond to the various choices of
+.I path
+and
+.IR primary .
+Integer and real numbers are distinguished.
+.I Literal
+is represented by
+.BR Xpath.Str ;
+variable references (ie,
+.BI $ QName\c
+)
+are represented by
+.BR XPath.Var ,
+where
+.I ns
+gives the optional XML name space of the
+.IR name .
+.I Path
+is represented by
+.BI Xpath.Path( abs,\ steps )
+where
+.I abs is non-zero if and only if the path is absolute (starts with `/' or `//'),
+and
+.I steps
+lists the
+.B Xstep
+values corresponding to the slash-separated
+.I Steps
+in the grammar.
+Abbreviated forms such as
+.RB ` // '
+are converted by
+.B xpointer
+to their full internal form in terms of
+.RB ` / ',
+as defined by the specification,
+so there is no need to distinguish the delimiters in this representation.
+.SH SOURCE
+.B /appl/lib/w3c/xpointers.b
+.SH SEE ALSO
+``XML Path Language (XPath) Version 1.0'',
+.B http://www.w3.org/TR/xpath
+.br
+``XPointer framework'',
+.B http://www.w3.org/TR/xptr-framework/
+.br
+``XPointer element() scheme'',
+.B http://www.w3.org/TR/xptr-element/
+.br
+``XPointer xmlns() scheme'',
+.B http://www.w3.org/TR/xptr-xmlns/
+.br
+``XPointer xpointer() scheme'',
+.B http://www.w3.org/TR/xptr-xpointer/
--- /dev/null
+++ b/man/2/wait
@@ -1,0 +1,100 @@
+.TH WAIT 2
+.SH NAME
+wait \- wait for child process to exit
+.SH SYNOPSIS
+.EX
+wait := load Wait Wait->PATH;
+
+Wait: module
+{
+   init:    fn();
+   read:    fn(fd: ref Sys->FD): (int, string, string);
+   monitor: fn(fd: ref Sys->FD): (int, chan of (int, string, string));
+   parse:   fn(status: string): (int, string, string);
+};
+.EE
+.SH DESCRIPTION
+.B Wait
+helps use the
+.B wait
+file of
+.IR prog (3).
+.PP
+.B Init
+must be called to initialise the module before invoking any other function.
+.PP
+.B Read
+reads a single wait record from file descriptor
+.IR fd ,
+which must be open on some process
+.IR p 's
+.B wait
+file,
+and returns a tuple
+.BI ( pid\f5,\fP\ module\f5,\fP\ status )
+where
+.I pid
+is the process ID of a child of
+.I p
+that has exited,
+.I module
+is the name of the module that caused
+.I p
+to exit,
+and
+.I status
+is nil if
+.I pid
+ended without error or a status message otherwise.
+If reading the
+.B wait
+file resulted in end of file or error,
+.I pid
+is 0 (for end of file) or
+.B -1
+on error (and
+.I status
+is the system error string for the error).
+.PP
+.B Monitor
+provides a channel interface to the
+.B wait
+file open on
+.IR fd ;
+it allows, for instance,
+a process to use
+.B alt
+to exchange data with a process but also see it exit (for good or ill).
+It starts a monitor process that applies
+.B read
+to
+.I fd
+and sends each resulting tuple on a channel.
+It returns a tuple
+.BI ( pid\f5,\fP\ c )
+where
+.I pid
+is the process ID of the monitor process (which can be used to kill it when done with it),
+and
+.I c
+is the channel on which the process sends each value it reads.
+The tuple has the format described above for
+.BR read .
+The monitor process exits when the wait file
+.I fd
+yields end of file or error, after sending the corresponding tuple on
+.IR c .
+.PP
+.B Parse
+takes a complete
+.I status
+string as read from a
+.B wait
+file and returns a tuple
+.BI ( pid\f5,\fP\ module\f5,\fP\ status )
+as described for
+.B read
+above.
+.SH SEE ALSO
+.IR sh (1),
+.IR prog (3)
--- /dev/null
+++ b/man/2/wmclient
@@ -1,0 +1,235 @@
+.TH WMCLIENT 2
+.SH NAME
+wmclient: makedrawcontext, window, snarfput, snarfget \-
+window manager interface for Draw-based applications.
+.SH SYNOPSIS
+.EX
+include "tk.m";
+include "wmclient.m";
+wmclient := load Wmclient Wmclient->PATH;
+
+Resize,
+Hide,
+Help,
+OK,
+Plain:     con 1 << iota;
+
+Appl:   con Resize | Hide;
+
+init:       fn();
+makedrawcontext: fn():  ref Draw->Context;
+window:	fn(ctxt: ref Draw->Context, title: string, buts: int): ref Window;
+snarfput:   fn(buf: string);
+snarfget:   fn(): string;
+Window: adt{
+	display:	ref Draw->Display;
+	r: Draw->Rect;		# full rectangle of window, including titlebar.
+	image: ref Draw->Image;
+	screenr: Draw->Rect;
+	ctxt: ref Draw->Wmcontext;
+	focused:	int;
+	ctl:		chan of string;
+
+	startinput:	fn(w: self ref Window, devs: list of string);
+	wmctl:	fn(w: self ref Window, request: string): string;
+	settitle:	fn(w: self ref Window, name: string): string;
+	reshape:	fn(w: self ref Window, r: Draw->Rect);
+	onscreen:	fn(w: self ref Window, how: string);
+	screenr2imager:	fn(w: self ref Window, sr: Draw->Rect): Draw->Rect;
+	imager2screenr:	fn(w: self ref Window, ir: Draw->Rect): Draw->Rect;
+	pointer:	fn(w: self ref Window, p: Draw->Pointer): int;
+};
+
+.EE
+.SH DESCRIPTION
+The
+.B Wmclient
+module provides routines for making windows controlled by
+.IR wm (1)
+containing an image that can be drawn on with the
+routines described in
+.IR draw-image (2).
+.PP
+.B Init
+should be called once to initialise the internal state of
+.BR wmclient .
+.PP
+.B Makedrawcontext
+establishes an initial connection with the window manager,
+creating a new
+.B Draw
+context suitable for creating new windows. It is only
+necessary to call this if the application has not already
+been provided with a context.
+.PP
+.B Window
+creates a new window through
+.IR ctxt
+and returns it.
+.I Title
+gives a label that will be displayed in the title bar
+of the window;
+.I buts
+determines which buttons are created in the titlebar,
+a bitwise combination of the constants
+.BR Resize ,
+.BR Help ,
+.BR OK,
+and
+.BR Hide .
+If
+.B Plain
+is given, the window is given no decoration at all.
+.PP
+When a window, say
+.IR w ,
+is first created, its size has not been determined
+and its image is not yet allocated.
+.IB W .reshape
+sets the requested rectangle for the window,
+(and requests a new image for the window
+if it has already been made visible),
+where
+.I r
+gives the requested rectangle of the new
+image, excluding window decoration, such as the
+title bar and the window border.
+An application can use
+.IB w .screenr2imager
+to find out the usable rectangle within screen
+rectangle
+.I sr
+when window decorations are taken into account.
+.IB W .imager2screenr
+converts in the other direction.
+.IB W .screenr
+contains the current rectangle of the screen containing
+the window.
+.PP
+.IB W .image
+holds the window's image when it has been successfully created;
+.IB w .ctxt
+holds the window manager context for the window, from which
+keyboard and mouse events can be received.
+No such events will be received until
+.IB w .startinput
+is called, with a list of devices (e.g.
+.BR ptr ,
+.BR kbd )
+to start input for.
+.IB W .settitle
+sets the title that is shown on the window's title bar;
+it can make the window's size (and therefore the window's image)
+change.
+.PP
+.IB W .ctl
+is a channel down which requests from the titlebar are sent.
+Messages received on it should be processed by
+the application or passed to
+.IB w \.wmctl\fR.
+Requests are formatted
+as with
+.B quoted
+in
+.IR string (2).
+The messages include:
+.TP
+.B exit
+The window should be closed.
+.IB W .wmctl
+will kill all processes in the current
+process group.
+.TP
+.B !move \fIx\fP \fIy\fP
+The user has started to try to drag the window.
+.I X
+and
+.I y
+give the location of the initial pointer click.
+.TP
+.B !size \fImindx\fP \fImindy\fP
+The user wishes to resize the window.
+.I Mindx
+and
+.I mindy
+give the minimum size acceptable for the window.
+.TP
+.B help
+The help button has been clicked.
+.TP
+.B ok
+The OK  button has been clicked.
+.TP
+.B hide
+The Hide button has been clicked.
+.IB W .wmctl
+will delete the window, and an entry
+will be shown on the toolbar.
+.PP
+In order to function correctly, an application
+should process not only events from the
+title bar channel, but also events received from the window
+manager itself (via
+.IB w .ctxt.ctl\fR),\fP
+and pointer and keyboard events received from
+the window manager (via
+.IB top .ctxt.ptr
+and
+.IB top .ctxt.kbd
+respectively).
+Control events can be passed to
+.IB w .wmctl \fR;
+keyboard events can be processed by the application;
+pointer events should be passed to
+.IB w .pointer
+for processing; if this returns zero, the
+application should process the pointer event,
+otherwise the event has been consumed by the
+titlebar.
+.PP
+When created, the window is not visible;
+.IB w .onscreen
+makes it visible, and possibly chooses a
+position and a size for it.
+.I How
+specifies what sort of placement is required
+for the window; it can be one of
+.TP
+.B place
+tries to choose a suitable place on the screen
+with respect to other windows; it may size the
+window as it feels appropriate. This the default
+(if
+.I how
+is nil).
+.TP
+.B onscreen
+tries to keep the position and size the same
+as specified on the window, adjusting them only
+to bring the window fully on screen, and making sure
+that the window is no bigger than the entire display.
+.TP
+.B exact
+does not change the specified size or position
+of the window unless absolutely necessary.
+.PP
+.B Snarfget
+and
+.B snarfput
+retrieve and replace the contents of the window
+manager's snarf buffer.
+.SH FILES
+.TF /chan/snarf
+.TP
+.B /chan/snarf
+snarf buffer maintained by
+.IR wm (1)
+.TP
+.B /chan/wm
+channel for interaction with
+.IR wm (1)
+.SH SOURCE
+.B /appl/lib/wmclient.b
+.SH SEE ALSO
+.IR wm (1),
+.IR tk (2)
--- /dev/null
+++ b/man/2/wmlib
@@ -1,0 +1,90 @@
+.TH WMLIB 2
+.SH NAME
+wmlib \-
+low level access to window manager
+.SH SYNOPSIS
+.EX
+include "sys.m";
+include "draw.m";
+include "wmlib.m";
+wmlib := load Wmlib Wmlib->PATH;
+
+init:       fn();
+connect:	fn(ctxt: ref Draw->Context): ref Draw->Wmcontext;
+startinput:	fn(w: ref Draw->Wmcontext, devs: list of string): string;
+wmctl:	fn(w: ref Draw->Wmcontext, request: string): (string, ref Draw->Image, string);
+snarfput:	fn(buf: string);
+snarfget:	fn(): string;
+
+.EE
+.SH DESCRIPTION
+.B Wmlib
+provides basic routines to access the window manager,
+It is used by higher level modules such as
+.IR tkclient (2)
+and
+.IR wmclient (2)
+to provide window manager access to applications.
+.PP
+.B Init
+should be called once to initialise the internal state of
+.BR Wmlib .
+.B Connect
+makes a connection to the window manager through
+.I ctxt
+(see
+.IR draw-context (2)).
+.B Startinput
+tells the window manager to start queuing events on the input
+streams named in
+.I devs
+(e.g.
+.BR ptr ,
+.BR kbd )
+so that they can be received by the application.
+.PP
+.B Wmctl
+sends
+.I request
+to the window manager.
+It returns a tuple, say (\fIname\fP, \fIimg\fP, \fIerr\fP).
+If a request fails, then
+.I name
+and
+.I img
+will be nil and the non-empty
+.I err
+gives the reason.
+If the request has resized or created an image,
+.I name
+gives the tag name of the image and
+.I img
+holds the new image.
+.B Wmlib
+interprets the
+.B exit
+request itself, by killing all processes in the current
+process group, and exiting.
+.PP
+.B Snarfget
+and
+.B snarfput
+retrieve and replace the contents of the window
+manager's snarf buffer.
+.SH FILES
+.TF /chan/snarf
+.TP
+.B /chan/snarf
+snarf buffer maintained by
+.IR wm (1)
+.TP
+.B /chan/wm
+channel for interaction with
+.IR wm (1)
+.SH SOURCE
+.B /appl/lib/wmclient.b
+.SH SEE ALSO
+.IR wm (1),
+.IR wmclient (2),
+.IR tkclient (2),
+.IR tk (2)
--- /dev/null
+++ b/man/2/wmsrv
@@ -1,0 +1,295 @@
+.TH WMSRV 2
+.SH NAME
+Wmsrv \- core window-manager functionality and helper functions
+.SH SYNOPSIS
+.EX
+.ps -1
+.vs -1
+include "sys.m";
+include "draw.m";
+include "wmsrv.m";
+wmsrv := load Wmsrv Wmsrv->PATH;
+Client, Window: import wmsrv;
+
+init:   fn(): 
+    (chan of (string, chan of (string, ref Draw->Wmcontext)),
+    chan of (ref Client, chan of string),
+    chan of (ref Client, array of byte, Sys->Rwrite));
+find:   fn(p: Draw->Point): ref Client;
+top:    fn(): ref Client;
+
+Window: adt {
+    tag:    string;
+    r:  Rect;
+    img:    ref Image;
+};
+
+Client: adt {
+    kbd:    chan of int;
+    ptr:    chan of ref Draw->Pointer;
+    ctl:    chan of string;
+    stop:   chan of int;
+    images: chan of (ref Draw->Point, ref Draw->Image, chan of int);
+    flags:  int;
+    wins:   list of ref Window;
+    znext:  cyclic ref Client;
+    id:     int;
+
+    window:   fn(c: self ref Client, tag: string): ref Window;
+    contains: fn(c: self ref Client, p: Draw->Point): int;
+    setimage: fn(c: self ref Client, tag: string, i: ref Draw->Image): int;
+    setorigin:fn(c: self ref Client, tag: string, o: Draw->Point): int;
+    top:      fn(c: self ref Client);
+    bottom:   fn(c: self ref Client);
+    remove:   fn(w: self ref Client);
+};
+.ps +1
+.vs +1
+.EE
+.SH DESCRIPTION
+.B Wmsrv
+acts as a kind of ``buffer'' module between an actual window-manager
+implementation and possibly misbehaving clients.
+It provides notification when clients arrive, make window-manager requests,
+and leave. For each client, it provides a set of channels that mirror those
+found in
+.BR Draw->Wmcontext ,
+(see
+.IR draw-context (2)),
+except that writing to the
+.BR Client 's
+channels is guaranteed not to block.
+Each client holds zero or more
+.BR Window s,
+each of which is tagged with an identifying string
+and which can hold the image of that window.
+A given client's windows are layered in strict order,
+most recently created at the top. Most clients will have
+only one window; others are generally used only for
+ephemeral purposes, such as pop-up menus.
+.PP
+A
+.BR Client ,
+say
+.IR c ,
+holds some channels directly equivalent to their
+.B Wmcontext
+namesakes:
+.IB c \.kbd\fR
+.IB c \.ptr\fR,
+and
+.IB c \.ctl\fR.
+The behaviour of
+.IB c \.images
+is described below.
+.B Wmsrv
+starts a new process to mediate interaction
+between the window manager and its clients;
+sending a value on
+.IB c \.stop
+causes this process to exit.
+.IB C \.wins
+gives the list of all the windows
+associated with this client;
+.IB c \.flags
+is not used by
+.IR wmsrv :
+it may be used to store arbitrary information;
+.IB c \.id
+holds a unique identifier for the client;
+it will be no larger than the largest
+number of clients that have simultaneously existed;
+.IB c \.znext
+links clients together by window depth (see
+.BR top ,
+below).
+.PP
+.B Init
+must be called before any other
+.I wmsrv
+function to initialise the
+.I wmsrv
+module. It creates the virtual file
+.BR /chan/wm ,
+and returns a tuple of channels, say (\fIwm\fP, \fIjoin\fP, \fIrq\fP).
+.I Wm
+is the channel that should be passed to prospective clients
+in the
+.B Draw->Context
+structure; communication on this channel is used
+to establish a new client connection.
+.I Join
+is used to receive notifications
+of new clients arriving. The tuple received on this channel,
+say (\fIc\fP, \fIrc\fP)
+holds the new client, and a channel on which a reply
+should be sent acknowledging the new client.
+If the string sent is non-empty, it represents an error message
+that will be returned to the client, and the client will not
+be allowed to join.
+.IB c \.ptr\fR,
+.IB c \.kbd\fR,
+and
+.IB c \.ctl
+are all direct equivalents of their
+.B Wmcontext
+namesakes; the
+behaviour of
+.IB c \.images
+is described below.
+.I Rq
+is used to receive requests made by clients to the window
+manager by writing to the file
+.B /chan/wm.
+The tuple received on
+.IR rq ,
+say (\fIc\fP, \fIdata\fP, \fIreply\fP)
+holds the client that is making the request, the
+data that has been sent, and a channel that can be used
+(as described in
+.IR sys-file2chan (2))
+to return a reply to the request,
+The request is conventionally formatted as a utf8-encoded
+string, holding a list of tokens quoted as described in
+.B quoted
+in
+.IR string (2).
+.PP
+If the first character of a window-manager request is an exclamation mark
+.RB ( ! ),
+it should be a request to change the image of a client's window
+(or create a new window).
+In this case, the first three tokens should be
+the name of the command (starting with an exclamation mark),
+the tag of the window to which the request refers, and a tag
+used by clients to match requests to replies.
+If such a request is allowed to succeed, then clients expect that
+a new image will be sent to them.
+The
+.I images
+channel in a client is used to do this (normally accessed through the
+.I setimage
+and
+.I setorigin
+methods, see below). Sending a tuple, say (\fIo\fP, \fIi\fP, \fIrc\fP)
+on
+.I images
+buffers an image to be returned to the client.
+If
+.I o
+is non-nil, the request will change the physical origin of
+.I i
+to
+.IR o ,
+otherwise
+.I i
+gives a new image (its logical origin must match its physical origin).
+Only one such request is allowed to be outstanding
+at any one time; the channel passed in
+.I rc
+will yield the value
+.B -1
+if the image from a previous request has not yet been consumed,
+in which case the current request should be caused to fail.
+.PP
+.B Wmsrv
+can maintain a record of the current
+windows and their stacking order relative to one other.
+.B Top
+returns a pointer to the client at the top of the stack;
+the other clients can be accessed, in stacking order,
+via their
+.B znext
+references.
+.B Find
+finds the top client that has a window containing
+the point
+.IR p .
+.B Wmsrv
+provides various
+.B Client
+methods that may be used to help
+implement a window manager's interface:
+.TP 10
+.IB c .window(\fItag\fP)
+Yield the
+.BR Window ,
+.IR w ,
+corresponding to
+.IR tag ,
+or
+.B nil
+if there is none.
+Note that
+.IB w \.r
+holds the actual screen rectangle of the image;
+the client is free to modify the image's logical
+coordinate system, so
+.IB w \.img.r
+cannot be relied upon to contain a value with a meaningful origin.
+.TP
+.IB c .contains(\fIp\fP)
+Return non-zero if any of the client's windows
+contain the point
+.IR p .
+.TP
+.IB c .setimage(\fItag\fP,\ \fIi\fP)
+Set the image associated with window
+.I tag
+to
+.IR i .
+If this is in response to a window manager request,
+.I i
+must be non-nil, and
+.I wmsrv
+will arrange that the new image is sent to the client.
+If this is not possible, then
+.B setimage
+will return
+.BR -1 .
+If
+.I i
+is nil, no image will be sent to the client
+and the window will be deleted.
+.TP
+.IB c .setorigin(\fItag\fP,\ \fIo\fP)
+Similar to
+.BR setimage ,
+except that only the origin of the window is changed.
+In order to enable clients to maintain their own logical
+coordinate system,
+.I wmsrv
+first sends
+.B nil
+on the
+.B Wmcontext.images
+channel, allowing the client to suspend operations
+on the image momentarily; it then sends to same
+channel, with its origin set to its actual screen origin.
+The client is then free to set the logical origin again.
+.TP
+.IB c .top()
+Raise the client's windows above the other clients' windows.
+.TP
+.IB c .bottom()
+Send the client's windows below the other clients' windows.
+.TP
+.IB c .remove()
+Remove the client and its windows from wmsrv's window stack.
+.SH FILES
+.TP 10
+.B /chan/wm
+Created by
+.I wmsrv
+using
+.IR file2chan (2)
+to serve connection requests.
+.SH SOURCE
+.B /appl/lib/wmsrv.b
+.SH SEE ALSO
+.IR wm (1),
+.IR draw-screen (2),
+.IR wmlib (2),
+.IR wmexport (1),
+.IR wmclient (2),
+.IR tkclient (2),
--- /dev/null
+++ b/man/2/workdir
@@ -1,0 +1,25 @@
+.TH WORKDIR 2
+.SH NAME
+workdir \- get the current working directory
+.SH SYNOPSIS
+.EX
+include "workdir.m";
+workdir := load Workdir Workdir->PATH;
+
+init:  fn(): string;
+.EE
+.SH DESCRIPTION
+.B Workdir
+returns a string representation of the working directory
+of the invoking process.
+.PP
+When Inferno boots,
+the initial process has
+.B /
+for its working directory.
+.SH SOURCE
+.B /appl/lib/workdir.b
+.SH SEE ALSO
+.IR sys-chdir (2),
+.IR sys-fd2path (2),
+.IR sys-stat (2)
--- /dev/null
+++ b/man/2/xml
@@ -1,0 +1,277 @@
+.TH XML 2
+.SH NAME
+xml \- XML navigation
+.SH SYNOPSIS
+.EX
+include "xml.m";
+
+xml := load Xml Xml->PATH;
+Parser, Item, Locator, Attributes, Mark: import xml;
+
+init:   fn(): string;
+open: fn(f: string, warning: chan of (Locator, string),
+                preelem: string): (ref Parser, string);
+fopen: fn(iob: ref Bufio->Iobuf, f: string, warning: chan of (Locator, string),
+                preelem: string): (ref Parser, string);
+
+Parser: adt {
+    fileoffset: int;
+
+    next:       fn(p: self ref Parser): ref Item;
+    down:       fn(p: self ref Parser);
+    up:         fn(p: self ref Parser);
+    mark:       fn(p: self ref Parser): ref Mark;
+    atmark:     fn(p: self ref Parser, m: ref Mark): int;
+    goto:       fn(p: self ref Parser, m: ref Mark);
+    str2mark:   fn(p: self ref Parser, s: string): ref Mark;
+};
+
+Item: adt {
+    fileoffset: int;
+    pick {
+    Tag =>
+        name:   string;
+        attrs:  Attributes;
+    Text =>
+        ch:     string;
+        ws1:	int;
+		ws2:    int;
+    Process =>
+        target: string;
+        data:   string;
+    Doctype =>
+        name:   string;
+        public: int;
+        params: list of string;
+    Stylesheet =>
+        attrs:  Attributes;
+    Error =>
+        loc:    Locator;
+        msg:    string;
+    }
+};
+
+Locator: adt {
+    line:       int;
+    systemid:   string;
+    publicid:   string;
+};
+
+Attribute: adt {
+    name:       string;
+    value:      string;
+};
+
+Attributes: adt {
+    all:        fn(a: self Attributes): list of Attribute;
+    get:        fn(a: self Attributes, name: string): string;
+};
+
+Mark: adt {
+    offset:     int;
+    str:        fn(m: self ref Mark): string;   
+};
+.EE
+.SH DESCRIPTION
+.B Xml
+provides an interface for navigating XML files (`documents'). Once loaded, the module
+must first be initialised by calling
+.BR init .
+A new parser instance is created by calling
+.BR open(\fIf\fP,\ \fIwarning\fP,\ \fIpreelem\fP) ,
+which opens the file
+.I f
+for parsing as an XML document,
+or
+.BR fopen(\fIiob\fP,\ \fIname\fP,\ \fIwarning\fP,\ \fIpreelem\fP) ,
+which does the same for an already open
+.B Iobuf
+(the string
+.I name
+will be used in diagnostics).
+Both functions return a tuple
+.RI ( p ,\  err ).
+If there is an error opening the document,
+.I p
+is nil, and
+.I err
+contains a description of the error; otherwise
+.I p
+can be used to examine the contents of the document.
+If
+.I warning
+is not nil, non-fatal errors encountered when parsing
+will be sent on this channel - a separate process will
+be needed to received them. Each error is represented
+by a tuple
+.RI ( loc ,\  msg ),
+containing the location
+.IR loc ,
+and the description,
+.IR msg ,
+of the error encountered. One XML tag,
+.IR preelem ,
+may be marked for special treatment by the XML parser:
+within this tag all white space will be passed through as-is.
+.PP
+Once an XML document has been opened, the following
+.B Parser
+methods may be used to examine the items contained within:
+.TP 10
+.IB p .next()
+An XML document is represented by a tree-structure.
+.B Next
+returns the next item in the document at the current level of the tree
+within the current parent element. If there are no more such
+items, it returns
+.BR nil .
+.TP
+.IB p .down()
+.B Down
+descends into the element that has just been returned by
+.BR next ,
+which should be a
+.B Tag
+item. Subsequent items returned by
+.B next
+will be those within that tag.
+.TP
+.IB p .up()
+.B Up
+moves up one level in the XML tree.
+.TP
+.IB p .mark()
+.B Mark
+returns a mark that can be used to return later to the current
+position in the document. The underlying file must
+be seekable for this to work.
+.TP
+.IB p .goto(\fIm\fP)
+Goes back to a previously marked position,
+.IR m ,
+in the document.
+.TP
+.IB p .atmark(\fIm\fP)
+.B Atmark
+returns non-zero if the current
+position in the document is the same as that marked by
+.IR m .
+The current tree level is ignored in the comparison.
+.TP
+.IB p .str2mark(\fIs\fP)
+.B Str2mark
+turns a string as created by
+.B Mark.str
+back into a mark as returned by
+.BR Parser.mark .
+.SS Items
+Various species of items live in XML documents; they are encapsulated
+in the
+.B Item
+adt. This contains one member in common to all its subtypes:
+.BR fileoffset ,
+the position in the XML document of the start of the item.
+The various kinds of item are as follows:
+.TP
+.B Tag
+A generic XML tag.
+.B Name
+names the tag, and
+.B attrs
+holds its attributes, if any.
+.TP
+.B Text
+.B Text
+represents inline text in the XML document.
+With the exception of text inside the tag named by
+.I preelem
+in
+.BR open ,
+any runs of white space are compressed to a single space,
+and white space at the start or end of the text is elided.
+.B Ch
+contains the resulting text;
+.B ws1
+and
+.B ws2
+are non-zero if there was originally white space at the start
+or end of the text respectively.
+.TP
+.B Process
+.B Process
+represents an XML document processing directive.
+.B Target
+is the processing instruction's target, and
+.B data
+holds the rest of the text inside the directive.
+XML stylesheet directives are recognised directly and have
+their own item type.
+.TP
+.B Doctype
+.B Doctype
+should only occur at the start of an xml document,
+and represents the type of the XML document.
+.TP
+.B Stylesheet
+.B Stylesheet
+represents an XML stylesheet processing request. The
+data of the processing request is parsed as per the RFC
+into attribute-value pairs.
+.TP
+.B Error
+If an unrecoverable error occurs processing the document,
+an
+.B Error
+item is returned holding the location
+.RB ( loc ),
+and description
+.RB ( msg )
+of the error.
+This will be the last item returned by the parser.
+.PP
+The attribute-value pairs in
+.B Tag
+and
+.B Stylesheet
+items are held in an
+.B Atttributes
+adt, say
+.IR a .
+.IB A .all()
+yields a list holding all the attributes;
+.IB a .get( name )
+yields the value of the attribute
+.IR name .
+.PP
+The location returned when an error is reported is held
+inside a
+.B Locator
+adt, which holds the line number on which the error occurred,
+the ``system id'' of the document (in this implementation, its file name),
+and the "public id" of the document (not currently used).
+.PP
+A
+.B Mark
+.I m
+may be converted to a string with
+.IB m .str()\fR;\fP
+this enables marks to be written out to external storage, to index
+a large XML document, for example.
+Note that if the XML document changes, any stored marks will
+no longer be valid.
+.SH SOURCE
+.B /appl/lib/xml.b
+.SH SEE ALSO
+``Extensible Markup Language (XML) 1.0 (Second Edition)'',
+.B http://www.w3.org/TR/REC-xml
+.PP
+``Navigating Large XML Documents on Small Devices'' in Volume 2.
+.SH BUGS
+XML's definition makes it tricky to handle leading and trailing white space
+efficiently;
+.B ws1
+and
+.B ws2
+in
+.B Item.Text
+is the current compromise.
--- /dev/null
+++ b/man/3/0intro
@@ -1,0 +1,69 @@
+.TH INTRO 3 
+.SH NAME
+intro \- introduction to the Inferno devices
+.SH DESCRIPTION
+An Inferno
+.I device
+implements a file tree for client processes.
+A file name beginning with a hash (number) symbol, such as
+.LR #c ,
+names the root of a file tree implemented by
+a particular
+.IR "kernel device driver"
+identified by the character after the hash.
+Such names are usually bound to conventional locations
+in the name space.
+For example, after
+.IP
+.EX
+sys->bind("#c", "/dev", sys->MREPL)
+.EE
+.LP
+an
+.IR ls (1)
+of
+.B /dev
+will list the files provided by the
+.I console
+device.
+.PP
+A kernel device driver is a
+.I server
+in the sense of the Inferno File Protocol, 9P (see Section 5),
+but with the messages implemented by local
+rather than remote procedure calls.
+Also, several of the messages
+.RI ( Nop ,
+.IR Flush ,
+and
+.IR Error )
+have no subroutine equivalents.
+.PP
+When a system call is passed a file name beginning with
+.L "#"
+it looks at the next character, and if that is a valid
+.I device character
+it performs an
+.IR attach (5)
+on the corresponding device to get a channel representing the
+root of that device's file tree.
+If there are any characters after the device character but
+before the next
+.L "/"
+or end of string, those characters are passed as parameter
+.I aname
+to the attach.
+.PP
+Each kernel device has a conventional place at which to be bound
+to the name space.
+The
+.I SYNOPSIS
+sections of the following pages includes a shell
+.I bind
+command to put the device in the conventional place.
+Most of these binds are done automatically by the system when it initializes;
+see
+.IR init (8).
+.SH SEE ALSO
+.IR intro (5),
+.IR intro (2)
--- /dev/null
+++ b/man/3/INDEX
@@ -1,0 +1,54 @@
+intro 0intro
+arch arch
+audio audio
+boot boot
+cap cap
+cmd cmd
+cons cons
+dbg dbg
+draw draw
+ds ds
+dup dup
+dynld dynld
+eia eia
+env env
+ether ether
+flash flash
+floppy floppy
+fpga fpga
+fs fs
+ftl ftl
+gpio gpio
+i2c i2c
+i82365 i82365
+indir indir
+ip ip
+kprof kprof
+logfs logfs
+lpt lpt
+mnt mnt
+mpeg mpeg
+pbus pbus
+pipe pipe
+plap plap
+pci pnp
+pnp pnp
+pointer pointer
+prof prof
+prog prog
+root root
+rtc rtc
+sd sd
+sign sign
+snarf snarf
+srv srv
+srv9 srv9
+ssl ssl
+switch switch
+tinyfs tinyfs
+tls tls
+touch touch
+tv tv
+usb usb
+vga vga
+vid vid
--- /dev/null
+++ b/man/3/arch
@@ -1,0 +1,63 @@
+.TH ARCH 3 x86
+.SH NAME
+arch \- x86 architecture-specific information and control
+.SH SYNOPSIS
+.nf
+.B bind -a #P /dev
+
+.B /dev/cputype
+.B /dev/ioalloc
+.B /dev/iob
+.B /dev/iol
+.B /dev/iow
+.B /dev/irqalloc
+.SH DESCRIPTION
+This device presents textual information about PC hardware and allows
+user-level control of the I/O ports on x86-class machines.
+.PP
+Reads from
+.I cputype
+recover the processor type and clock rate.
+.PP
+Reads from
+.I ioalloc
+return I/O ranges used by each device, one line
+per range.  Each line contains three fields separated by white space: first address
+in hexadecimal,
+last address, name of device.
+.PP
+Reads from
+.I irqalloc
+return the enabled interrupts, one line per
+interrupt.  Each line contains three fields separated by white space:
+the trap number, the IRQ it is assigned to, and the name of
+the device using it.
+.PP
+Reads and writes to
+.IR iob ,
+.IR iow ,
+and
+.I iol
+cause 8-bit wide, 16-bit wide, and 32-bit wide requests to
+I/O ports.
+The port accessed is determined by the byte offset of the
+file descriptor.
+.SH EXAMPLE
+The following Limbo code reads from an x86 byte I/O port.
+.IP
+.EX
+inportb(port: int): byte
+{
+    data := array[1] of byte;
+
+    if(iobfd == nil)
+        iobfd = sys->open("#P/iob", Sys->ORDWR);
+
+    sys->seek(iobfd, port, 0);
+    if(sys->read(iobfd, data, len data) != len data)
+        fatal(sys->sprint("inportb(16r%4.4x): %r\en", port));
+    return data[0];
+}
+.EE
+.SH SOURCE
+.B /os/pc/devarch.c
--- /dev/null
+++ b/man/3/audio
@@ -1,0 +1,169 @@
+.TH AUDIO 3
+.SH NAME
+audio \- digital audio input and output
+.SH SYNOPSIS
+.B bind -a '#A' /dev
+.PP
+.B /dev/audio
+.br
+.B /dev/audioctl
+.SH DESCRIPTION
+The
+.I audio
+device serves a one-level directory containing two files,
+giving access to the host or native audio device in a platform-independent way.
+.PP
+.B Audio
+is the data file, which can be read or written to exchange data with the device. Audio
+data is a sequence of stereo samples, left sample first, each of which is a 16 bit little-endian
+two's complement integer.
+.PP
+.B Audioctl
+is the associated control file, which sets the characteristics of the device.
+Reads return lines of the form
+.TP
+.B "\fIsource\fP [in out] \fIvalue\fP"
+.TP
+.B "\fIsource\fP in \fIvalue\fP out \fIvalue\fP"
+.TP
+.B "\fIsource\fP [in out] left \fIvalue\fP right \fIvalue\fP"
+.TP
+.B "\fIsource\fP in left \fIvalue\fP right \fIvalue\fP out left \fIvalue\fP right \fIvalue\fP"
+The qualifiers
+.BR in ,
+.B out
+and
+.BR left ,
+.B right
+are omitted if they
+are not applicable or if the input/output and/or left/right values are
+the same.
+The
+.I source
+is one of
+.BR audio ,
+.BR aux1 ,
+.BR aux2 ,
+.BR line ,
+.BR mono ,
+.BR mic ,
+.BR rate ,
+.B chans
+or
+.BR bits .
+If
+.I source
+is one of the ports
+.BR audio ,
+.BR aux1 ,
+.BR aux2 ,
+.BR line ,
+.B mono
+or
+.BR mic ,
+.I value
+represents
+the volume
+on a scale of 0 (quiet) to 100 (loud). The default volume is 50 for the
+.B audio
+port and 0 for the
+remainder. If the
+.I source
+is
+.BR rate ,
+.I value
+represents the
+rate at which the data was recorded in Hz. The default is 22050.
+If the
+.I source
+is
+.BR chans ,
+.I value
+is the number of channels of audio data - 2 (the default) for stereo, otherwise mono. If the
+.I source
+is
+.BR bits ,
+.I value
+is one of 4, 8 or 16, the sample size in bits.
+.TP
+.B [in out] enc \fIformat\fP
+Outputs the recording format on the input and/or output channels.
+.I Format
+is one of
+.BR ulaw ,
+.BR alaw ,
+.BR pcm ,
+.B pcm16
+or
+.B adpcm
+.TP
+.B loop \fIvalue\fP
+.I Value
+is 0 or 1 to disable or enable loopback respectively.
+.PP
+Writes control the device with strings of a similar format as that for reads.
+.TP
+.B "\fIsource\fP [in out] \fIvalue\fP"
+.TP
+.B "\fIsource\fP in \fIvalue\fP out \fIvalue\fP"
+.TP
+.B "\fIsource\fP [in out] left \fIvalue\fP right \fIvalue\fP"
+.TP
+.B "\fIsource\fP in left \fIvalue\fP right \fIvalue\fP out left \fIvalue\fP right \fIvalue\fP"
+Set the corresponding value/volume for the particular
+.I source.
+The same conventions apply
+as in the read case above. The default value of
+.I source
+is
+.BR audio .
+.TP
+.B reset
+Set all attributes to their default value.
+.TP
+.B loop \fIvalue\fP
+Enable (1) or disable (0) loopback mode.
+.TP
+.B [in out] enc \fIformat\fP
+Set the recording format on the input and/or output channels to
+.I format.
+.I Format
+is one of
+.BR ulaw ,
+.BR alaw ,
+.BR pcm ,
+.BR adpcm .
+.TP
+.B [left right] dev \fIinput\fP
+Set the input source of the left and/or right ADCs.
+.I Input
+is one of
+.BR mic ,
+.BR line ,
+.BR aux1 ,
+.BR loop .
+.SH SOURCE
+.B /emu/port/devaudio*.c
+.br
+.B /emu/*/devaudio.c
+.br
+.B /os/*/devaudio.c
+.br
+.B /os/*/devcs4231.c
+.SH SEE ALSO
+.IR auplay (1)
+.br
+.IR audio (6)
+.SH BUGS
+The device has not been implemented on every platform that could provide it,
+particularly hosted implementations.
+.br
+.B /os/port/devaudio.c
+(a driver for the SB16) implements an older interface of broadly similar form,
+but using
+.B volume
+not
+.BR audioctl ,
+and supports a subset of the commands described above.
+.br
+The device is exclusive-use and cannot mix streams from several clients.
--- /dev/null
+++ b/man/3/boot
@@ -1,0 +1,41 @@
+.TH BOOT 3
+.SH NAME
+boot \- reboot under program control
+.SH SYNOPSIS
+.B bind -a #B /dev
+.PP
+.B /dev/boot
+.br
+.B /dev/kexec
+.br
+.B /dev/mem
+.SH DESCRIPTION
+.I Boot
+serves a directory containing three files that control a reboot of the system.
+The files can only be opened by the host owner
+(see
+.IR eve (10.2)).
+.PP
+.B Boot
+is a write-only file that accepts 4 bytes representing a kernel address, high-order byte first.
+The system will transfer control to that address, which is assumed to be the start of low-level reboot code.
+There is no return from the write on success or failure.
+.PP
+.B Kexec
+is a write-only file to which an uncompressed kernel image can be written, a chunk at a time.
+When the file is closed, the system will transfer control to the start of the image, which again
+is assumed to be low-level reboot code.
+.PP
+.B Mem
+copies data to and from kernel memory, taking the current file offset as an address therein.
+.PP
+.I Boot
+flushes caches as necessary to ensure that writes to kernel memory through any
+of these files will be visible when subsequently executed as instructions.
+.SH SOURCE
+.B /os/port/devboot.c
+.SH SEE ALSO
+.IR tftp (2),
+.IR cons (3)
+.SH DIAGNOSTICS
+Invalid addresses are diagnosed, but otherwise there is no error return.
--- /dev/null
+++ b/man/3/cap
@@ -1,0 +1,92 @@
+.TH CAP 3
+.SH NAME
+cap \- capability for changing user name
+.SH SYNOPSIS
+.BI "bind #¤ " dir
+.PP
+.IB dir /caphash
+.br
+.IB dir /capuse
+.SH DESCRIPTION
+.I Cap
+allows a process owned by the host owner (see
+.IR eve (10.2))
+to give another process on the same machine
+a capability to set its user name to a specified user.
+The capability is a string of the form:
+.IP
+[
+.IB fromuser @
+]
+.IB touser @ key
+.PP
+where
+.I fromuser
+is a process's current user name,
+.I touser
+is its new user name, and
+.I key
+is a string of random characters
+(eg, produced by
+.IR security-random (2)).
+.PP
+.B Caphash
+is a write-only file that can only be opened by the host owner.
+A process enables the use of a capability by writing the keyed hash of
+.IB fromuser @ touser
+to
+.BR caphash .
+The hash is computed using
+.B Keyring->hmac_sha1
+as follows:
+.IP
+.EX
+kr := load Keyring Keyring->PATH;
+IPint: import kr;
+users := sys->sprint("%s@%s", fromuser, touser);
+cap := sys->sprint("%s@%s", users, key);
+digest := array[Keyring->SHA1dlen] of byte;
+ausers := array of byte users;
+kr->hmac_sha1(ausers, len ausers, array of byte key, digest, nil);
+if(sys->write(caphashfd, digest, len digest) < 0)
+	error();
+.EE
+.PP
+The capability (eg,
+.B cap
+in the example)
+can then be passed to another process.
+.PP
+.B Capuse
+is a write-only file that can be opened by any process.
+It can then write a capability string to change its user name,
+provided that capability has previously been enabled by the host owner via
+.BR caphash ,
+and if the capability included a
+.IR fromuser ,
+the writing process currently has that user name.
+After a successful write, the writing process will be owned by
+.IR touser .
+Any capability can be used at most once.
+.PP
+A capability enabled by
+.B caphash
+has a limited lifetime, on the order of 30 seconds.
+.B Caphash
+can be removed by the host owner to prevent its further use.
+.SH SOURCE
+.B /emu/port/devcap.c
+.br
+.B /os/port/devcap.c
+.SH SEE ALSO
+.IR keyring-sha1 (2),
+.IR cons (3),
+.IR intro (5),
+.IR eve (10.2)
+.SH DIAGNOSTICS
+A write to
+.B capuse
+without a previous write to
+.B caphash
+sets the error string to
+.RB `` "invalid capability" ''.
--- /dev/null
+++ b/man/3/cmd
@@ -1,0 +1,282 @@
+.TH CMD 3 hosted
+.SH NAME
+cmd \- interface to host operating system commands
+.SH SYNOPSIS
+.B bind -a '#C' /
+.PP
+.B /cmd/clone
+.br
+.BI /cmd/ n /ctl
+.br
+.BI /cmd/ n /data
+.br
+.BI /cmd/ n /stderr
+.br
+.BI /cmd/ n /status
+.br
+.BI /cmd/ n /wait
+.SH DESCRIPTION
+.I Cmd
+provides a way to run commands in the underlying operating system's
+command interpreter when Inferno is running hosted, in
+.IR emu (1).
+It serves a three-level directory that is conventionally bound
+behind the root directory.
+The top of the hierarchy is a directory
+.BR cmd ,
+that contains a
+.B clone
+file and zero or more numbered directories.
+Each directory represents a distinct connection to the host's command interpreter.
+The directory contains five files:
+.BR ctl ,
+.BR data ,
+.BR stderr ,
+.B status
+and
+.BR wait ,
+used as described below.
+Opening the
+.B clone
+file reserves a connection: it is equivalent to opening the
+.B ctl
+file of an unused connection directory, creating a new one if necessary.
+.PP
+The file
+.B ctl
+controls a connection.
+When read, it returns the decimal number
+.I n
+of its connection directory.
+Thus, opening and reading
+.B clone
+allocates a connection directory and reveals the number of the allocated directory,
+allowing the other files to be named (eg,
+.BI /cmd/ n /data\fR).
+.PP
+.B Ctl
+accepts the following textual commands, allowing quoting as interpreted by
+.IR parsecmd (10.2):
+.TP
+.BI "dir " wdir
+Run the host command in directory
+.IR wdir ,
+which is a directory
+.I "on the host system" .
+Issue this request before starting the command.
+By default, commands are run in the Inferno root directory on the host system.
+.TP
+.BI "exec " "command args ..."
+Spawn a host process to run the
+.I command
+with arguments as given.
+The write returns with an error, setting the error string, if anything prevents
+starting the command.
+If write returns successfully, the command has started, and its standard input and
+output may be accessed through
+.BR data ,
+and its error output accessed through
+.B stderr
+(see below).
+If arguments containing white space are quoted (following the conventions of
+.IR sh (1)
+or
+.IR parsecmd (10.2)),
+they are requoted by
+.I cmd
+using the host command interpreter's conventions so that
+.I command
+sees exactly the same arguments as were written to
+.BR ctl .
+.TP
+.B kill
+Kill the host command immediately.
+.TP
+.B killonclose
+Set the device to kill the host command when the
+.B ctl
+file is closed (normally all files must be closed, see below).
+.TP
+.BI nice " \fR[\fPn\fR]\fP"
+Run the host command at less than normal scheduling priority.
+Issue this request before starting the command.
+The optional value
+.IR n ,
+in the range 1 to 3,
+indicates the degree of `niceness' (default: 1).
+.PP
+The
+.B data
+file provides a connection to the input and output of a previously-started
+host command.
+It must be opened separately for reading and for writing.
+When opened for reading, it returns data that the command writes to its standard output; when closed, further writes by the command will receive the host
+equivalent of `write to closed pipe'.
+When opened for writing, data written to the file
+can be read by the command on its standard input; when closed, further reads by
+the command will see the host equivalent of `end of file'.
+(Unfortunately there is no way to know when the command needs input.)
+.PP
+The
+.B stderr
+file provides a similar read-only connection to the error output from the command.
+If the
+.B stderr
+file is not opened, the error output will be discarded.
+.PP
+Once started, a host command runs until it terminates or until it is killed,
+by using the
+.B kill
+or
+.B killonclose
+requests above, or by closing all
+.BR ctl ,
+.B data
+and
+.B wait
+files for a connection.
+.PP
+The read-only
+.B status
+file provides a single line giving the status of the connection (not the command), of the form:
+.IP
+.BI cmd/ "n opens state wdir arg0"
+.PP
+where the fields are separated by white space. The meaning of each field is:
+.TP
+.I n
+The
+.B cmd
+directory number.
+.TP
+.I opens
+The decimal number of open file descriptors for
+.BR ctl ,
+.B data
+and
+.BR wait .
+.TP
+.I state
+The status of the interface in directory
+.IR n :
+.RS
+.TF Execute
+.TP
+.B Open
+Allocated for use but not yet running a command.
+.TP
+.B Execute
+Running a command.
+.TP
+.B Done
+Command terminated: status available in the
+.B status
+file (or via
+.BR wait ).
+.TP
+.B Close
+Command completed. Available for reallocation via
+.BR clone .
+.RE
+.PD
+.TP
+.I wdir
+The command's initial working directory on the host.
+.TP
+.I arg0
+The host command name (without arguments).
+.PP
+The read-only
+.B wait
+file must be opened before starting a command via
+.BR ctl .
+When read, it blocks until the command terminates.
+The read then returns with a single status line, to be
+parsed using
+.B String->unquote
+(see
+.IR string (2)).
+There are five fields:
+host process ID (or 0 if unknown);
+time the command spent in user code in milliseconds (or 0);
+time spent in system code in milliseconds (or 0);
+real time in milliseconds (or 0);
+and a string giving the exit status of the command.
+The exit status is host-dependent, except that an empty string
+means success, and a non-empty string contains a diagnostic.
+.PP
+.SS "Command execution"
+In all cases, the command runs in the host operating system's
+own file name space
+in which
+.IR emu (1)
+was started.
+All file names will be interpreted in that space, not Inferno's.
+For example, on Unix and Plan 9,
+.B /
+refers to the host's file system root, not Inferno's;
+the effects of mounts and binds will not be visible;
+nor will Inferno services be available except by network connection.
+.PP
+On Unix systems,
+the command is run by the
+.B execvp
+system call, 
+using the Unix user and group ID of
+the user that started
+.IR emu (1),
+unless it was started by the super-user, in which case
+.I cmd
+attempts to set the Unix user ID and group ID to those of a Unix user corresponding to
+the current Inferno user's name, and failing that, to user and group
+.BR nobody .
+.PP
+On Plan 9,
+the command is run with the system call
+.BR exec ,
+first trying the
+.I command
+name as-is;
+if that fails and the name does not start with
+.BR # ,
+.BR / ,
+.B ./
+or
+.BR ../ ,
+.I cmd
+attempts to exec
+.BI /bin/ command.
+The command runs using the Plan 9 identity of
+the user that started
+.IR emu (1).
+Each connection has its own name space.
+.PP
+On Windows systems,
+the command must be a binary executable (not built into the command interpreter) in the current path.
+It is always run with the same Windows user identity as started
+.IR emu (1).
+The arguments given to the
+.B exec
+request are requoted as described above so as to present the same
+arguments to the command via the Windows command interpreter.
+The arguments are otherwise unmodified.
+In particular, no attempt is made to convert slashes to backslashes in a vain
+attempt to convert file name syntax to Windows conventions.
+(In fact, most Windows applications will accept
+.B /
+as a separate in file names, provided the drive letter precedes the whole
+name to prevent its interpretation as a command option.)
+.SH SOURCE
+.B /emu/port/devcmd.c
+.br
+.B /emu/*/cmd.c
+.SH "SEE ALSO"
+.IR emu (1),
+.IR os (1)
+.SH DIAGNOSTICS
+A
+.B write
+to
+.B ctl
+returns with an error and sets the error string if
+a command cannot be started or killed successfully.
--- /dev/null
+++ b/man/3/cons
@@ -1,0 +1,301 @@
+.TH CONS 3 
+.SH NAME
+cons \- console device
+.SH SYNOPSIS
+.nf
+.B bind #c /dev
+
+.B /dev/cons
+.B /dev/consctl
+.B /dev/drivers
+.B /dev/jit
+.B /dev/keyboard
+.B /dev/klog
+.B /dev/kprint
+.B /dev/memory
+.B /dev/msec
+.B /dev/null
+.B /dev/notquiterandom
+.B /dev/pointer
+.B /dev/random
+.B /dev/scancode
+.B /dev/sysctl
+.B /dev/sysname
+.B /dev/time
+.B /dev/user
+.fi
+.SH DESCRIPTION
+The console device serves a one-level directory
+giving access to the console and
+miscellaneous information.
+.PP
+Reading the
+.B cons
+file returns characters typed on the keyboard.
+Normally, characters are buffered to enable erase and kill processing.
+A control-U,
+.LR ^U ,
+typed at the keyboard
+.I kills
+the current input line (removes all characters
+from the buffer of characters
+not yet read via
+.BR cons ),
+and a backspace
+.I erases
+the previous non-kill, non-erase character from the input buffer.
+Killing and erasing only delete characters back to, but not including,
+the last newline.
+Typed keystrokes produce 21-bit runes
+that are translated into the variable-length
+.SM UTF
+encoding (see
+.IR utf (6))
+before putting them into the buffer.
+A
+.B read
+of length greater than zero causes the process to wait until a
+newline or a
+.L ^D
+ends the buffer, and then returns as much of the buffer as the argument
+to
+.B read
+allows, but only up to one complete line.
+A terminating
+.L ^D
+is not put into the buffer.
+If part of the line remains, the next
+.B read
+will return bytes from that remainder and not part of any new line
+that has been typed since. A single line containing a
+.L ^D
+can be used as an end of file indication to programs that take interactive input.
+.PP
+If
+the string
+.B rawon
+has been written to the
+.B consctl
+file and the file is still open,
+.B cons
+is in
+.IR "raw mode" :
+characters are not echoed as they are typed,
+backspace,
+.L ^U
+and
+.L ^D
+are not treated specially,
+and characters are available to
+.B read
+as soon as they are typed.
+Ordinary mode is reentered when
+.B rawoff
+is written to
+.B consctl
+or this file is closed.
+.PP
+A
+.B write
+to
+.B cons
+causes the characters to be printed on the console screen.
+.PP
+The
+.B keyboard
+file returns the underlying tokens produced by the keyboard hardware as they
+are produced; in the emulation environment, it is like an always-raw
+.B cons
+file.
+.PP
+The
+.B null
+file throws away anything written to it
+and always returns zero bytes when read.
+.PP
+The
+.B klog
+file
+returns the tail of messages written by the native kernel debugging function
+.B kprint
+(mainly used when debugging interrupt handlers in device drivers).
+It is available only in native kernel implementations.
+.PP
+The
+.B kprint
+file
+returns console output: messages written by kernel print statements and messages
+written by processes to this driver's
+.B cons
+file.
+Until
+.B kprint
+is opened, system console output is handled normally.
+Once
+.B kprint
+has been opened,
+if the machine's console is a serial line, the data is sent both to
+the serial console and to
+.BR kprint ;
+if the console is a graphics screen, the data is sent only to
+.BR kprint .
+.PP
+A read of the
+.B pointer
+file returns the status of the mouse or other pointing device:
+its position and button state.
+The read blocks until the state has changed since the last read.
+The read returns 49 bytes: the letter
+.B m
+followed by four fields containing decimal integers, each 11 characters wide followed by a blank.
+The integers are: x and y,
+coordinates of the pointer on the screen; a bit mask with the
+1, 2, and 4 bits when the pointer's left, middle, and right
+buttons, respectively, are down; and a time stamp in units of milliseconds.
+.PP
+Writing to the
+.B pointer
+file, using the same format,
+causes the pointer to move to the specified x, y position
+(the button and millisecond fields are ignored, and optional).
+If there is a visible image representing the pointer's position,
+that will move too.
+.PP
+The
+.B random
+device returns as many bytes of random data as are requested in the
+.BR read .
+.PP
+The
+.B notquiterandom
+device returns as many bytes of pseudo-random data as are requested in the
+.BR read ;
+this is typically faster than
+.B random
+but the results are more predictable.
+.PP
+The
+.B scancode
+device provides access to the raw scan codes of the primary
+.B keyboard .
+While it is open, key strokes are diverted from
+.B cons
+and
+.B keyboard .
+The first
+.IR read (2)
+after opening returns an identifier string which defines the format of data delivered
+by subsequent
+reads. Known ones are defined in
+.IR scancode (6).
+The most common format is a single byte per scan code, where the top bit is 1 for
+up and 0 for down, and the bottom 7 bits are the scan code. Some input devices
+have a larger scan code space; in this case scan codes are often delivered as two byte
+little endian quantities, where the top bit is the up/down signifier, and the bottom
+15 bits are the scan code. In all cases the meaning of the individual scan codes is
+device specific.
+.PP
+The rest of the files contain (mostly) read-only strings.
+Each string has a fixed length: a
+.I read
+(see
+.IR sys-read (2))
+of more than that gives a result of that fixed length (the result does not
+include a terminating zero byte);
+a
+.I read
+of less than that length leaves the file offset so the
+rest of the string (but no more) will be read the next time.
+To reread the file without closing it,
+.I seek
+must be used to reset the offset.
+When the file contains numeric data, each number is formatted
+in decimal as an 11-digit number with leading blanks and
+one trailing blank: twelve bytes total.
+.PP
+The
+.B sysctl
+file can be read to return the current Inferno version. Writing the string
+.B reboot
+to it attempts to reboot the system, writing
+.B halt
+attempts to halt the system. Writing
+.B nobroken
+ensures that broken processes have all associated memory freed before
+being destroyed,
+writing
+.B broken
+ensures that they are left in this state to allow debugging (the default).
+Only the privileged user is allowed to write to this file.
+.PP
+The
+.B sysname
+file holds the textual name of the machine.
+It can only be written by the privileged user.
+.PP
+The
+.B user
+file contains the name of the user associated with the current process.
+It can only be written by the privileged user. In the emulation environment,
+writing to this file also attempts to set the user id in the host operating
+system to the specified value.
+.PP
+The
+.B memory
+file returns a formatted presentation of the state of the memory
+allocation pools in the system.
+Each line of output returned reports, for a single pool,
+the amount of memory in use,
+the upper size limit,
+the high water mark,
+the number of allocations done,
+the number of frees done,
+the number of extensions done,
+the largest chunk available
+and the name of the pool.
+.PP
+The
+.B drivers
+file returns a list of the device drivers loaded in the system.
+Each line gives the name of the device for
+.IR bind (1),
+such as
+.BR #c ,
+followed by the name of the driver as used in the system configuration file.
+.PP
+The other files served by the
+.I cons
+device are all single numbers:
+.TP
+.B jit
+non-zero if `just in time' compilation is configured (can be written to change the state). Writing 0 turns off JIT compilation, 1 turns it on and larger values give increasingly
+detailed traces of the compilation for debugging purposes.
+.TP
+.B msec
+the value of a millisecond counter
+.TP
+.B time
+number of microseconds since the epoch 00:00:00 GMT, Jan. 1, 1970.
+(Can be written once by the privileged user, to set at boot time.)
+.SH SOURCE
+.B /emu/port/devcons.c
+.br
+.B /os/port/devcons.c
+.SH SEE ALSO
+.IR draw (3),
+.IR keyboard (6),
+.IR utf (6),
+.IR eve (10.2)
+.SH BUGS
+For debugging, on native systems only,
+two control-T's followed by a letter
+generate console output:
+.L ^T^Tp
+prints data about kernel processes,
+.L ^T^Ts
+prints the kernel stack,
+.L ^T^Tx
+prints data about memory allocation.
+.PP
+The system can be rebooted by typing
+.LR ^T^Tr .
--- /dev/null
+++ b/man/3/dbg
@@ -1,0 +1,384 @@
+.TH DBG 3
+.SH NAME
+dbg \- remote kernel debugging
+.SH SYNOPSIS
+.B "bind -b '#b' /dev"
+.PP
+.B /dev/dbgctl
+.br
+.B /dev/dbglog
+.PP
+.BI "echo r >/dev/dbgctl"
+.SH DESCRIPTION
+.I Dbg
+allows a native kernel to be debugged remotely,
+by means of a simple protocol, typically run on a serial port
+(see
+.IR eia (3)).
+The
+.IR acid (10.1)
+debugger uses the protocol for instance; see its
+.B -R
+option.
+.PP
+.I Dbg
+uses the values of several global variables set by the kernel configuration file (see
+.IR conf (10.6)),
+all of which default values.
+The variables and default values are listed below:
+.IP
+.EX
+int dbgstart = 0;
+char	*dbgdata = "#t/eia0";
+char	*dbgctl = "#t/eia0ctl";
+char	*dbgctlstart = "b19200";
+char	*dbgctlstop = "h";
+char	*dbgctlflush = "f";
+.EE
+.PP
+Different values can be set by including similar declarations,
+with values as desired, in the
+.B code
+section of the configuration file.
+.I Dbg
+uses the values as follows:
+.TP \w'\f5dbgctlflushxx\fP'u
+.PD 0
+.B dbgstart
+if non-zero, start the debugger protocol on the
+configured connection during driver initialisation (system startup);
+otherwise it must be started explicitly by the
+.B r
+control request (see below)
+.TP
+.B dbgdata
+data file for the debugging connection
+.TP
+.B dbgctl
+control file for the debugging connection
+.TP
+.B dbgctlstart
+control request to initialise link (eg, baud rate)
+.TP
+.B dbgctlstop
+control request to hang up link
+.TP
+.B dbgctlflush
+control request to write to flush input and output on link
+.PD
+.PP
+.I Dbg
+serves two files that control and monitor its operation.
+.PP
+.B Dbgctl
+accepts several textual commands; normally only
+.B r
+is needed:
+.TP
+.BI d " dbgdata"
+set the value of
+.B dbgdata
+to the value given as an argument
+.TP
+.BI c " dbgctl"
+.PD 0
+.TP
+.BI i " dbgctlstart"
+.TP
+.BI h " dbgctlstop"
+.TP
+.BI f " dbgctlflush"
+set the value of the corresponding control variable to the value of the first argument
+.PD
+.TP
+.B r
+start running the debugger protocol (not needed if
+.B dbgstart
+was non-zero at boot)
+.TP
+.B s
+stop running the debugger protocol; stop and flush the link
+.PP
+When read,
+.B dbgctl
+yields a single line showing the status of the device
+.RB (` running '
+or
+.RB ` stopped ')
+and the current values of the debugger control
+variables listed above.
+.PP
+.B Dbglog
+is a read-only text file containing lines representing debugger events,
+one per line.
+It is mainly useful for checking the operation of the device, or debugging new
+.IR acid (10.1)
+functions.
+.SS Debug protocol
+The protocol is subject to change.
+The host running the debugger and the target to be debugged
+exchange 10-byte messages containing a one byte message type
+and 9 bytes of data.
+Bytes unused by a given type are set to zero.
+Normally the host sends one of the T-messages below and
+receives the corresponding R-message, or
+.BR Rerr .
+(These are unrelated to the T-messages and R-messages of
+.IR intro (5).)
+Exceptionally, the target sends the first message, an
+.B Rerr
+to reset the protocol, and thus the debugger is notified if the target
+is rebooted during a debugging session and can reset its own state.
+Values, including addresses, are sometimes represented textually
+in hexadecimal, but are usually in binary as a single byte, or an array of 4 bytes,
+high-order byte first (big endian).
+.PP
+The term
+.I process
+here refers exclusively to those created directly or indirectly by
+.IR kproc (10.2),
+not to Limbo processes, which are not visible directly through
+the protocol (although it is possible to write
+.IR acid (10.1)
+functions that interact through
+.I dbg
+with the Inferno data structures representing
+the state of the Dis virtual machine).
+Many requests read or write the memory or state of the
+.IR "current process"
+set by the
+.B Tproc
+message (see below).
+Addresses are always 32 bits.
+An address below the size of
+.B Ureg
+(saved register state) for the target is interpreted as an offset within the saved
+state for the current process.
+Otherwise it refers to an address in kernel virtual memory.
+Currently in native Inferno all processes share the same address space.
+.PP
+The message type names used below
+are assigned values by declarations in
+.BR /include/rdbg.h .
+The following messages are currently implemented:
+.TP \w'Tstart'u
+.PD 0
+.B Terr
+unused
+.TP
+.BI Rerr " reason\fR[9]\fR"
+The last message failed for the given
+.IR reason ,
+a text string:
+.BR reset ,
+the target or debug driver was restarted;
+.BR count ,
+bad count;
+.BR unk ,
+unknown command;
+.BR inval ,
+invalid parameter;
+.BR pid ,
+no such process;
+.BR unsup ,
+unsupported action;
+.BR notstop ,
+action requires process to be stopped first.
+.TP
+.BI Tmget " addr\fR[4]\fP n\fR[1]\fP"
+Request
+.I n
+bytes of memory from
+.IR addr ;
+.I n
+must be no greater than 9
+.TP
+.BI Rmget " data\fR[9]\fP"
+Return
+.I data
+requested by
+.B Tmget
+.TP
+.BI Tmput " addr\fR[4]\fP n\fR[1]\fP data\fR[4]\fP"
+Write the first
+.I n
+bytes of
+.I data
+to memory at
+.IR addr ,
+and flush the data and instruction caches for that region;
+.I n
+must be no greater than 4
+.TP
+.BI Rmput
+Reply to a successful
+.B Tmput
+.TP
+.BI Tproc " pid\fR[4]\fP"
+Set the current process to the one with integer process ID
+.I pid
+for subsequent requests.
+.TP
+.BI Rproc " addr\fR[8]\fP"
+.I Addr
+is the address in hexadecimal text of the
+.B Proc
+structure for process
+.I pid
+in the corresponding
+.BR Tproc .
+.TP
+.BI Tstatus " pid\fR[4]\fP"
+Request the status of process
+.I pid
+leaving the current process ID unchanged.
+.TP
+.BI Rstatus " status\fR[9]\fP"
+Return the textual status of the process
+as a text string, currently one of:
+.BR Dead ,
+.BR Moribund ,
+.BR Ready ,
+.BR Scheding ,
+.BR Running ,
+.BR Queueing ,
+.BR Wakeme ,
+.BR Broken ,
+.BR Stopped ,
+.BR Rendezvous ,
+or if invalid, the state value as a hexadecimal number.
+.TP
+.BI Trnote " pid\fR[4]\fP"
+Retrieve the note (trap status) for the given
+.I pid
+.TP
+.BI Rrnote " status\fR[9]\fP"
+Provide the textual trap
+.I status
+for the requested process (currently always returns null status)
+.TP
+.BI Tstop " pid\fR[4]\fP"
+Tell the kernel to stop running process
+.I pid
+in debugging state
+.B Stopped
+when it next appears in the scheduler.
+.TP
+.BI Rstop
+Reply to successful
+.B Tstop
+.TP
+.BI Tstart
+Cancel a previous
+.BR Tstop ;
+if the process has already stopped, make it ready to run.
+.TP
+.BI Rstart
+Reply to successful
+.B Tstart
+.TP
+.BI Tcondbreak " val\fR[4]\fP op\fR[4]\fP"
+If
+.I op
+is
+.BR d ,
+remove and delete the breakpoint with ID
+.IR val .
+All other operations help
+create a conditional breakpoint, providing a possibly empty list of operations
+representing a conditional expression in Reverse Polish is followed
+by a breakpoint request,
+each expression element represented by a single
+.B Tcondbreak
+message.
+.I Op
+is a single character representing an operation, with
+.I val
+(integer, address, process ID) as a parameter.
+The operator
+.B n
+should appear first; it assigns the
+breakpoint an ID number
+.I val
+(no greater than 255).
+Expression primaries are:
+.BI k " val,"
+true if process
+.I val
+is at this breakpoint;
+.BI b " val,"
+true if program counter is
+.IR val ;
+and
+.BI p " val,"
+.I val
+as a 32-bit literal.
+Expression operators are:
+unary
+.B *
+(indirect, yielding 32-bit value);
+.B &
+(bit-wise AND);
+.B =
+(values equal);
+.B !
+(values not equal);
+.B a
+(logical AND);
+.B o
+(logical OR).
+Although the expression is interpreted following Reverse Polish notation,
+when transmitted, the
+.B b
+operation is sent last (to mark the end of the sequence and create the breakpoint),
+but is moved to the start of the expression before evaluation.
+.TP
+.BI Rcondbreak
+Reply to successful
+.BR Tcondbreak .
+.TP
+.BI Tstartstop " pid\fR[4]\fP"
+If the process
+.I pid
+is not stopped, return
+.B Rerr
+.BR notstop .
+Otherwise, if the process is not stopped at
+a breakpoint, start it, and wait for it to reach a breakpoint
+that evaluates `true'
+.TP
+.BI Rstartstop " id\fR[1]\fP"
+Process has stopped at breakpoint with the given
+.I id
+.TP
+.BI Twaitstop
+Unimplemented. See
+.BR Tstartstop .
+.TP
+.BI Rwaitstop
+Unused.
+.TP
+.BI Tkill " pid\fR[4]\fP note\fR[5]\fP"
+Kill process
+.I pid
+with the given textual
+.IR note .
+Unimplemented.
+.TP
+.BI Rkill
+Reply to successful
+.BR Tkill .
+Unused.
+.PP
+.SH SOURCE
+.B /os/port/devdbg.c
+.br
+.B /os/*/*break.c
+.br
+.B /os/*/trap.c
+.SH SEE ALSO
+.IR acid (10.1)
+.SH BUGS
+The protocol is not itself error-detecting let alone error-correcting, although that normally does not
+matter for debugging even
+over a serial line, provided the connection is reasonably sound.
--- /dev/null
+++ b/man/3/draw
@@ -1,0 +1,816 @@
+.TH DRAW 3 
+.SH NAME
+draw \- screen graphics
+.SH SYNOPSIS
+.EX
+.B bind -a #i /dev
+
+.B /dev/draw/new
+
+.BI /dev/draw/ n /ctl
+.BI /dev/draw/ n /data
+.ig
+.BI /dev/draw/ n /colormap
+..
+.BI /dev/draw/ n /refresh
+
+.EE
+.SH DESCRIPTION
+The
+.I draw
+device serves a three-level file system 
+providing an interface to the graphics facilities of the system.
+The Limbo Draw module (see
+.IR draw-intro (2))
+implements its functions using this device.
+Each client of the device connects by opening
+.B /dev/draw/new
+and reading 12 strings, each 11 characters wide followed by a blank:
+the connection number
+.RI ( n ),
+the image id
+.RI ( q.v. )
+of the display image (always zero),
+the
+channel format
+of the image,
+the
+.BR min.x ,
+.BR min.y ,
+.BR max.x ,
+and
+.B max.y
+of the display image,
+and the
+.BR min.x ,
+.BR min.y ,
+.BR max.x ,
+and
+.B max.y 
+of the clipping rectangle. 
+The channel format string is described in 
+.IR image (6),
+and the other fields are decimal numbers.
+.PP
+The client can then open the directory
+.BI /dev/draw/ n /
+to access the
+.BR ctl ,
+.BR data ,
+.ig
+.BR colormap ,
+..
+and
+.B refresh
+files associated with the connection.
+.PP
+Via the 
+.B ctl
+and
+.B draw 
+files, the
+.I draw
+device provides access to 
+images and font caches
+in its private storage,
+as described in 
+.IR draw-intro (2).
+Each image is identified by a 4-byte integer, its
+.IR id .
+.PP
+Reading the
+.B ctl 
+file yields 12 strings formatted as in
+.BR /dev/draw/new ,
+but for the current image rather
+than the display image.
+The current image may be set by writing a
+binary image id to the
+.B ctl
+file.
+.PP
+A process can write messages to 
+.B data
+to allocate and free images, fonts, and subfonts;
+read or write portions of the images;
+and draw line segments and character
+strings in the images.
+All graphics requests are clipped to their images.
+Some messages return a response to be recovered by 
+reading the
+.B data 
+file.
+.PP
+The
+.B draw
+device provides three types of graphical resource: Images, Screens and Fonts.
+Resource instances have an identification number.
+Screen identifiers are global to the device.
+All other identifiers are local to each client.
+.PP
+Image is the fundamental resource type on which all drawing primitives
+are performed.
+At client connection the physical display is represented by Image 0.
+.PP
+A Screen manages a set of (overlapping) Images, handling
+Z-order and position manipulation and the refreshing of regions
+uncovered by such operations.
+When a Screen is created it is associated with an Image on which
+to render itself.
+New images can be associated with a screen when they are created; they
+are then treated as windows on that screen.
+Screens can be marked as
+.IR public ,
+meaning that other clients can import their ID and create new windows
+on them.
+.PP
+A Font is an image with associated character data.
+The Font image provides the bitmap of all the characters in the
+Font; the character data is used by the string command to render
+characters from the image.
+.SS "Command messages"
+.PP
+The format of messages written to
+.B data
+is a single letter
+followed by binary parameters;
+multibyte integers are transmitted with the low order byte first.
+Points are two four-byte numbers:
+.IR x ,
+.IR y .
+Rectangles are four four-byte numbers: min 
+.IR x ,
+min
+.IR y ,
+max
+.IR x ,
+and max
+.IR y .
+Images, screens, and fonts have 32-bit identifiers.
+In the discussion of the protocol below,
+the distinction between identifier and actual image, screen, or font
+is not made, so that
+``the object
+.IR id ''
+should be interpreted as 
+``the object with identifier
+.IR id ''.
+The definitions of constants used in the description below can be found in
+.BR /module/draw.m
+or
+.BR /include/draw.h .
+.PP
+The following requests are accepted by the
+.B data
+file.
+The numbers in brackets give the length in bytes of the parameters.
+.HP .5i
+.B A
+.IR id [4]
+.IR imageid [4]
+.IR fillid [4]
+.IR public [1]
+.br
+Allocate a new
+.B Screen
+(see
+.IR draw-display (2))
+with
+screen identifier
+.I id
+using
+backing store image
+.IR imageid ,
+filling it initially
+with data from image
+.IR fillid .
+If the
+.I public
+byte is non-zero, the screen can 
+be accessed from other processes
+using the
+.B publicscreen
+interface.
+.HP
+.B b 
+.IR id [4]
+.IR screenid [4]
+.IR refresh [1]
+.IR chan [4]
+.IR repl [1]
+.IR r [4*4]
+.IR clipr [4*4]
+.IR color [4]
+.br
+Allocate an image with a given 
+.I id 
+on the
+screen named by
+.IR screenid .
+The image will have rectangle
+.I r 
+and clipping rectangle
+.IR clipr .
+If 
+.I repl
+is non-zero, the image's replicate 
+bit will be set (see
+.IR draw (2)).
+.IP
+.I Refresh
+specifies the method to be used to draw the window
+when it is uncovered.
+.B Refbackup
+causes the server to maintain a backing store,
+.B Refnone 
+does not refresh the image,
+and
+.B Refmesg
+causes a message to be sent via
+the
+.B refresh 
+file
+.RI ( q.v. ).
+.IP
+The image format is described by
+.IR chan ,
+a binary version of the channel format string.
+Specifically, the image format is the catenation of up to four
+8-bit numbers, each describing a particular image channel.
+Each of these 8-bit numbers contains a channel type in its
+high nibble and a bit count in its low nibble.
+The channel type is one of
+.BR CRed ,
+.BR CGreen ,
+.BR CBlue ,
+.BR CGrey ,
+.BR CAlpha ,
+.BR CMap ,
+and
+.BR CIgnore .
+See
+.IR image (6).
+.IP
+.I Color
+is the catenation of four 8-bit numbers
+specifying the red, green, blue, and alpha
+channels of the color that the new image should
+be initially filled with.
+The red channel is in the highest 8 bits, and
+the alpha in the lowest.
+Note that color is always in this format, independent of 
+the image format.
+.HP
+.B c
+.IR dstid [4]
+.IR repl [1]
+.IR clipr [4*4]
+.br
+Change the replicate bit and clipping rectangle of the 
+image
+.IR dstid .
+This overrides whatever settings were specified in
+the allocate message.
+.HP
+.B d
+.IR dstid [4]
+.IR srcid [4]
+.IR maskid [4]
+.IR dstr [4*4]
+.IR srcp [2*4]
+.IR maskp [2*4]
+.br
+Use the 
+.B draw
+operator to combine the rectangle 
+.I dstr
+of 
+image
+.I dstid 
+with a 
+rectangle of image
+.IR srcid ,
+using a rectangle of image
+.IR maskid
+as an alpha mask to further control blending.
+The three rectangles are congruent and aligned such that
+the upper left corner
+.I dstr
+in image
+.I dstid
+corresponds to
+the point 
+.I srcp
+in image
+.I srcid
+and
+the point
+.I maskp
+in image
+.IR maskid .
+See
+.IR draw-image (2).
+.HP
+.B D
+.IR debugon [1]
+.br
+If
+.I debugon
+is non-zero, enable debugging output.
+If zero, disable it.
+The meaning of ``debugging output'' is implementation dependent.
+.HP
+.B e
+.IR dstid [4]
+.IR srcid [4]
+.IR c [2*4]
+.IR a [4]
+.IR b [4]
+.IR thick [4]
+.IR sp [2*4]
+.IR alpha [4]
+.IR phi [4]
+.br
+Draw an ellipse in image
+.I dst
+centered on the point
+.I c
+with horizontal and vertical semiaxes
+.I a
+and
+.IR b .
+The ellipse is drawn using the image
+.IR src ,
+with
+the point
+.I sp
+in 
+.I src
+aligned with
+.I c
+in
+.IR dst .
+The ellipse is drawn with thickness
+.RI 1+2× thick .
+.IP
+If the high bit of
+.I alpha
+is set, 
+only the arc of the ellipse from degree angles
+.I alpha
+to
+.I phi 
+is drawn.
+For the purposes of drawing the arc,
+.I alpha
+is treated as a signed 31-bit number
+by ignoring its high bit.
+.HP
+.B E
+.IR dstid [4]
+.IR srcid [4]
+.IR center [2*4]
+.IR a [4]
+.IR b [4]
+.IR thick [4]
+.IR sp [2*4]
+.IR alpha [4]
+.IR phi [4]
+.br
+Draws an ellipse or arc as the
+.B e
+message, but rather than outlining it, fills
+the corresponding sector using the image
+.IR srcid .
+The 
+.I thick 
+field is ignored, but must be non-negative.
+.HP
+.B f
+.IR id [4]
+.br
+Free the resources associated with the image
+.IR id .
+.HP
+.B F
+.IR id [4]
+.br
+Free the the screen with the specified
+.IR id .
+Windows on the screen must be freed separately.
+.HP
+.B i
+.IR id [4]
+.IR n [4]
+.IR ascent [1]
+.br
+Treat the image
+.I id
+as a font cache of 
+.I n
+character cells, each with
+ascent
+.IR ascent .
+.HP
+.B l
+.IR cacheid [4]
+.IR srcid [4]
+.IR index [2]
+.IR r [4*4]
+.IR sp [2*4]
+.IR left [1]
+.IR width [1]
+.br
+Load a character into the font cache associated with image
+.I cacheid
+at cache position
+.IR index .
+The character data is drawn in rectangle
+.I r
+of the font cache image
+and is fetched from
+the congruent rectangle in image
+.I srcid
+with upper left corner
+.IR sp .
+.I Width
+specifies the width of the character\(emthe spacing from this character to the next\(emwhile
+.I left
+specifies
+the horizontal distance from the left side
+of the character to the left side of the cache image.
+The dimensions of the image of the character are defined by
+.IR r .
+.HP
+.B L
+.IR dstid [4]
+.IR p0 [2*4]
+.IR p1 [2*4]
+.IR end0 [4]
+.IR end1 [4]
+.IR thick [4]
+.IR srcid [4]
+.IR sp [2*4]
+.br
+Draw a line of thickness
+.RI 1+2× thick
+in image
+.I dstid
+from point
+.I p0
+to
+.IR p1 .
+The line is drawn using the image
+.IR srcid ,
+translated so that point
+.I sp
+in
+.I srcid
+aligns with
+.I p0
+in 
+.IR dstid .
+The 
+.I end0
+and
+.I end1
+fields specify whether the corresponding
+line end should be a square, a disc,
+or an arrow head.
+See 
+.I line
+in
+.IR draw-image (2)
+for more details.
+.HP
+.B N
+.IR id [4]
+.IR in [1]
+.IR j [1]
+.IR name [ j ]
+.br
+If 
+.I in
+is non-zero, associate the image
+.I id
+with the string
+.IR name .
+If 
+.I in
+is zero and 
+.I name
+already corresponds to the
+image
+.IR id ,
+the association is deleted.
+.HP
+.B n
+.IR id [4]
+.IR j [1]
+.IR name [ j ]
+.br
+Introduce the identifier 
+.I id
+to correspond to the image named
+by the string
+.IR name .
+.HP
+.B o
+.IR id [4]
+.IR r.min [2*4]
+.IR scr [2*4]
+.br
+Position the window 
+.I id
+so that its upper left corner is at the
+point
+.I scr
+on its screen.
+Simultaneously change its internal (logical) coordinate system
+so that the point
+.I log
+corresponds to the upper left corner of the window.
+.HP
+.B p
+.IR dstid [4]
+.IR n [2]
+.IR end0 [4]
+.IR end1 [4]
+.IR thick [4]
+.IR srcid [4]
+.IR sp [2*4]
+.IR dp [2*2*(n+1)]
+.br
+Draw a polygon of thickness
+.RI 1+2× thick .
+It is conceptually equivalent to a series of
+.I n
+line-drawing messages (see
+.B L
+above)
+joining adjacent points in the list of points
+.IR dp .
+The source image
+.I srcid
+is translated so that the point
+.I sp
+in
+.I srcid
+aligns with the first point
+in the list
+.IR dp .
+The polygon need not be closed: 
+.I end0
+and
+.I end1
+specify the line endings for the first and
+last point on the polygon.
+All interior lines have rounded ends
+to make smooth joins.
+.HP
+.B P
+.IR dstid [4]
+.IR n [2]
+.IR wind [4]
+.IR ignore [2*4]
+.IR srcid [4]
+.IR sp [2*4]
+.IR dp [2*2*(n+1)]
+.br
+Draw a polygon as the
+.B p
+message, but
+fill it rather than outlining it.
+The winding rule parameter
+.I wind
+resolves ambiguities about what to fill if the polygon is self-intersecting.
+If
+.I wind
+is
+.BR ~0 ,
+a pixel is inside the polygon if the polygon's winding number about the point
+is non-zero.
+If
+.I wind
+is
+.BR 1 ,
+a pixel is inside if the winding number is odd.
+Complementary values (0 or ~1) cause outside pixels to be filled.
+The meaning of other values is undefined.
+The polygon is closed with a line if necessary.
+.HP
+.B r
+.IR id [4]
+.IR r [4*4]
+.br
+Cause the next read of the
+.B data
+file to return the image pixel data corresponding to the
+rectangle
+.I r
+in image
+.IR id .
+.HP
+.B s
+.IR dstid [4]
+.IR srcid [4]
+.IR fontid [4]
+.IR p [2*4]
+.IR clipr [4*4]
+.IR sp [2*4]
+.IR n [2]
+.IR n*(index [2])
+.br
+Draw in the image
+.I dstid
+the text string specified by the 
+.I n 
+cache
+.I indices
+into font
+.IR fontid ,
+starting with the upper left corner at point
+.I p
+in image
+.IR dstid .
+The image drawn is taken from image
+.IR srcid ,
+translated to align
+.I sp
+in
+.I srcid
+with
+.I dp
+in
+.IR dstid.
+All drawing is confined to the clipping rectangle
+.I clipr 
+in
+.IR dstid .
+.HP
+.B x
+.IR dstid [4]
+.IR srcid [4]
+.IR fontid [4]
+.IR dp [2*4]
+.IR clipr [4*4]
+.IR sp [2*4]
+.IR n [2]
+.IR bgid [4]
+.IR bp [2*4]
+.IR n*(index [2])
+.br
+Like the string drawing 
+.B s
+command, but fill the background of each character
+with pixels from image
+.IR bgid .
+The image
+.I bgid
+is translated so that the point
+.I bp
+aligns with the
+point 
+.I dp
+in 
+.IR dstid .
+.HP
+.B S
+.IR id [4]
+.IR chan [4]
+Attach to the public screen with the specified
+.IR id .
+It is an error if the screen does not exist, is not public, or does not
+have the channel descriptor
+.I chan
+for its associated image.
+.HP
+.B t
+.IR top [1]
+.IR n [2]
+.IR n*id [4]
+.br
+Send 
+.I n
+windows to the top (if
+.I t
+is non-zero) or bottom (if
+.I t
+is zero) of the window stack.
+The window is specified by the list of
+.I n
+image
+.IR id s
+are moved as a group, maintaining their own order within the stack.
+.HP
+.B v
+.br
+Flush changes from a soft screen, if any, to the display buffer.
+.HP
+.B y
+.IR id [4]
+.IR r [4*4]
+.IR buf [x*1]
+.br
+.ti -0.5i
+.B Y
+.IR id [4]
+.IR r [4*4]
+.IR buf [x*1]
+.br
+Replace the rectangle 
+.I r
+of pixels in image
+.I id
+with the pixel data in 
+.IR buf .
+The pixel data must be in the format dictated by
+.IR id 's
+image channel descriptor (see
+.IR image (6)).
+The 
+.B y
+message uses uncompressed data,
+while the
+.B Y
+message uses compressed data. 
+In either case, 
+it is an error to include more data than necessary.
+.ig
+.PP
+Reading the
+.B colormap
+returns the system color map used on 8-bit displays.
+Each color map entry consists of a single line containing
+four space-separated decimal strings.
+The first is an index into the map, and the remaining three are
+the red, green, and blue values associated with that index.
+The color map can be changed by writing entries in the
+above format to
+the 
+.B colormap
+file.
+Note that changing the system color map 
+does not change the color map used for
+calculations involving
+.B m8
+images, which is immutable.
+..
+.PP
+The
+.B refresh
+file is read-only.
+As windows owned by the client are uncovered,
+if they cannot be refreshed by the server (such as when they have
+refresh functions associated with them), a message is made available
+on the
+.B refresh
+file reporting what needs to be repainted by the client.
+The message has five decimal integers formatted as in the
+.B ctl
+message: the image id of the window and the coordinates of the rectangle
+that should be refreshed.
+.SH SOURCE
+.B /emu/port/devdraw.c
+.br
+.B /emu/*/win.c
+.br
+.B /os/port/devdraw.c
+.br
+.B /os/*/screen.c
+.br
+.B /libmemdraw
+.SH SEE ALSO
+.IR draw-intro (2),
+.IR colour (6),
+.IR image (6),
+.IR font (6)
+.SH DIAGNOSTICS
+Most messages to
+.B draw
+can return errors;
+these can be detected by a system call error
+on the
+.IR write (see
+.IR sys-read (2))
+of the data containing the erroneous message.
+The most common error is a failure to allocate
+because of insufficient free resources.  Most other errors occur
+only when the protocol is mishandled by the application.
+The error string (see
+.IR sys-intro (2))
+will report details.
+.SH BUGS
+The 
+.B Refmesg
+refresh method is not fully implemented.
+.ig
+.br
+The
+.B colormap
+files only reference the system color map, and as 
+such should be called
+.B /dev/colormap
+rather than
+.BI /dev/draw/ n /colormap\f1.
+..
--- /dev/null
+++ b/man/3/ds
@@ -1,0 +1,145 @@
+.TH DS 3
+.SH NAME
+ds \- compound device subsystems
+.SH SYNOPSIS
+.nf
+.B bind -b #k /dev
+
+.B /dev/ds
+.B /dev/ds/ctl
+.B /dev/ds/...
+.fi
+.SH DESCRIPTION
+.I Ds
+builds complex block storage subsystems out of simpler ones.
+Inspired by the Plan 9 file server kernel's configuration strings,
+it provides device mirroring, partitioning, interleaving, and catenation
+for block-oriented file systems.
+.PP
+It serves a two-level directory.
+The top level contains a single directory
+.BR ds ,
+which contains a control file
+.B ctl
+and one file per configured device.
+.PP
+The control messages each introduce a new device, here named
+.IR new .
+The
+.I file
+arguments are interpreted in the name space of the writer.
+.TP
+.BI mirror " new files" \fR...
+The device
+.I new
+corresponds to a RAID 1 mirroring of
+.IR files .
+Writes to
+.BI new
+are handled by sequentially writing to the
+.I files
+from right to left (the reverse of
+of the order in the control message).
+If any write fails, the write is aborted.
+Reads from
+.BI new
+are handled by sequentially reading from the
+.I files
+from left to right until one succeeds.
+The length of the mirror device is the minimum of the lengths of the
+.IR files .
+.TP
+.BI part " new file offset length"
+The device
+.I new
+corresponds to the
+.I length
+bytes starting at
+.I offset
+in
+.IR file .
+If
+.IR offset + length
+reaches past the end of
+.IR file ,
+.I length
+is silently reduced to fit.
+.TP
+.BI inter " new files" \fR...
+The device
+.I new
+corresponds to the block interleaving of
+.IR files ;
+an 8192-byte block size is assumed.
+.TP
+.BI cat " name files" \fR...
+The device
+.I new
+corresponds to the catenation of
+.IR files .
+.PD
+.LP
+If the variable
+.B fsconfig
+is set in
+.IR plan9.ini (8)
+then
+.I ds
+will read its configuration from the file
+.B $fsconfig
+on the first attach.
+This is useful when the machine boots
+from a local file server that uses
+.IR fs .
+.SH EXAMPLE
+Mirror the two disks
+.B /dev/sdC0/data
+and
+.B /dev/sdD0/data
+as
+.BR /dev/ds/m0 ;
+similarly, mirror
+.B /dev/sdC1/data
+and
+.B /dev/sdD1/data
+as
+.BR /dev/ds/m1 :
+.IP
+.EX
+echo mirror m0 /dev/sdC0/data /dev/sdD0/data >/dev/ds/ctl
+echo mirror m1 /dev/sdC1/data /dev/sdD1/data >/dev/ds/ctl
+.EE
+.LP
+Interleave the two mirrored disks to create
+.BR /dev/ds/data :
+.IP
+.EX
+echo inter data /dev/ds/m0 /dev/ds/m1 >/dev/ds/ctl
+.EE
+.LP
+Run
+.IR kfs (4)
+on the interleaved device:
+.IP
+.EX
+disk/kfs -f /dev/ds/data
+.EE
+.LP
+Save the configuration:
+.IP
+.EX
+cp /dev/ds/ctl /dev/fd0disk
+.EE
+To load the configuration automatically at boot time,
+add this to
+.IR plan9.ini :
+.IP
+.EX
+fsconfig=/dev/fd0disk
+.EE
+.SH "SEE ALSO"
+.IR dossrv (4)
+.SH SOURCE
+.B /os/port/devds.c
+.SH BUGS
+Should be able to select block size for interleaved devices.
--- /dev/null
+++ b/man/3/dup
@@ -1,0 +1,60 @@
+.TH DUP 3 
+.SH NAME
+dup \- dups of open files
+.SH SYNOPSIS
+.nf
+.B bind #d /fd
+
+.B /fd/0
+.B /fd/0ctl
+.B /fd/1
+.B /fd/1ctl
+\&...
+.fi
+.SH DESCRIPTION
+The
+.I dup
+device serves a one-level directory containing files whose
+names are decimal numbers.
+Each such file also has an associated control file.
+A file of name
+.I n
+corresponds to open file descriptor
+.I n
+in the current process.
+.PP
+A
+.IR sys-open (2)
+of file
+.I n
+results in a file descriptor identical to
+what would be returned from a system call
+.BI sys->dup( n,
+.BR -1) .
+Note that the result is no longer a file in the
+.I dup
+device.
+.PP
+The
+.I stat
+operation returns information about the device file, not the open file it points to.
+A stat of
+.BI #d/ n
+will contain
+.I n
+for the name, 0 for the length, and 400, 200, or 600 (octal)
+for the mode, depending on whether the dup target is open
+for reading, writing, or both.
+.PP
+A file of name
+.IB n ctl
+may be read to discover the properties of the associated file descriptor, in format identical to that of the
+.B fd
+file in
+.IR prog (3).
+.SH SEE ALSO
+.IR sys-dup (2)
+.SH SOURCE
+.B /usr/inferno/emu/port/devdup.c
+.br
+.B /usr/inferno/os/port/devdup.c
--- /dev/null
+++ b/man/3/dynld
@@ -1,0 +1,75 @@
+.TH DYNLD 3
+.SH NAME
+dynld \- load kernel module dynamically
+.SH SYNOPSIS
+.B bind -a #L /dev
+.PP
+.B /dev/dynld
+.br
+.B /dev/dynsyms
+.SH DESCRIPTION
+.I Dynld
+serves a directory containing two files that control dynamic loading of modules.
+When a module is loaded,
+.IR dynld (10.2)),
+checks the module's type against a type provided by the driver;
+the types must match.
+Currently the only supported module type is that of a device driver,
+.B Dev
+defined in
+.IR dev (10.2)).
+.PP
+The control file
+.B dynld
+can only be opened by the host owner
+(see
+.IR eve (10.2)).
+It accepts the following commands:
+.TP
+.BI "load dev" " name file \fR[\fP \fR[\fP tag \fR] dep ... ]\fP"
+Load a module for device driver
+.I name
+from the given
+.IR file .
+The optional
+.I tag
+is typically the MD5 or SHA1 hash of the file's contents, to identify a particular version of the driver.
+.TP
+.BI "unload dev" " name \fR[\fP file \fR[\fP tag \fR] ]\fP"
+Unload the device driver identified by the given
+.IR name ,
+optionally qualified by the other parameters.
+.PP
+When read,
+.B dynld
+returns a list of the currently-loaded drivers, one per line, most recent first.
+Each line contain: the module's device type
+.BI # x
+used in
+.IR bind (1);
+its kernel address;
+its total text and data size in bytes;
+its
+.I name
+when loaded;
+the name of the
+.I file
+from which it was loaded;
+and its
+.IR tag .
+.PP
+.B Dynsyms
+is a read-only text file that lists, one per line,
+the symbols exported by the running kernel for use by modules it loads.
+Each line contains:
+the symbol's kernel address,
+its type signature,
+and its name.
+.SH SOURCE
+.B /os/port/devdynld.c
+.SH SEE ALSO
+.IR dynld (10.2),
+.IR a.out (10.6),
+.SH BUGS
+.I Dynld
+does not currently check that a driver is no longer in use before unloading it.
--- /dev/null
+++ b/man/3/eia
@@ -1,0 +1,136 @@
+.TH EIA 3 
+.SH NAME
+eia \- serial communication control
+.SH SYNOPSIS
+.nf
+.B bind -a '#t' /dev
+
+.B /dev/eia0
+.B /dev/eia0ctl
+.B /dev/eia0status
+.B /dev/eia1
+.B /dev/eia1ctl
+.B /dev/eia1status
+\&...
+.fi
+.SH DESCRIPTION
+.PP
+The serial line device serves a one-level directory that gives access to the serial ports.
+Each file set
+.BI eia "n ..."
+represents one serial port
+and includes a data file
+(eg,
+.BR eia0 ),
+a control file
+(eg,
+.BR eia0ctl ),
+and a status file
+(eg,
+.BR eia0status ).
+.PP
+The
+.B data
+file can be read and written to access the corresponding serial port.
+Reads will block until at least one character is available.
+.PP
+The
+.B ctl
+file accepts textual commands, via
+.I write
+(see
+.IR sys-read (2)),
+that control or condition
+the serial port for subsequent IO:
+.TP
+.BI b n
+Set the baud rate to
+.IR n .
+.TP
+.BI c n
+If
+.I n
+is non-zero, cause a hangup if the remote drops DCD.
+.TP
+.BI d n
+Set DTR if
+.I n
+is non-zero;
+otherwise clear it.
+.TP
+.BI e n
+If
+.I n
+is non-zero,
+cause a hangup if the remote drops DSR.
+.TP
+.B f 
+Flush the output queue.
+.TP
+.B h
+Hangup both input and output queues.
+.TP
+.BI k n
+Send a break lasting
+.I n
+milliseconds.
+.TP
+.BI l n
+Set number of bits per byte to
+.IR n .
+Legal values are 5, 6, 7, or 8.
+.TP
+.BI m n
+Obey modem CTS signal if
+.I n
+is non-zero; otherwise clear it.
+.TP
+.BI n n
+Set the output queue to non-blocking, if
+.I n
+is non-zero; otherwise mark it as blocking (the default).
+In non-blocking mode, if the output queue is full, new output is discarded
+rather than block the writer until the queue drains.
+.TP
+.BI p c
+Set parity to odd if
+.I c
+is
+.BR o ,
+to even if
+.I c
+is
+.BR e ;
+otherwise set no parity.
+.TP
+.BI q n
+Set the limit on input and output queues to
+.IR n ;
+both queues are initially set to 4k bytes.
+.TP
+.BI r n
+Set RTS if
+.I n
+is non-zero; otherwise clear it.
+.TP
+.BI s n
+Set number of stop bits to
+.IR n .
+Legal values are 1 or 2.
+.TP
+.BI x n
+Enable XON/XOFF flow control if
+.I n
+is non-zero; otherwise disable it (the default).
+.PP
+Changes to port settings persist between a subsequent close and reopen.
+.PP
+The
+.B status
+file can be read for a report of the serial port status.
+.SH SOURCE
+.B /os/port/devns16552.c
+.br
+.B /os/*/devuart.c
+.br
+.B /os/*/devscc.c
--- /dev/null
+++ b/man/3/env
@@ -1,0 +1,48 @@
+.TH ENV 3
+.SH NAME
+env \- environment device
+.SH SYNOPSIS
+.nf
+.B bind #e /env
+
+.BI /env/ name
+.fi
+.SH DESCRIPTION
+The environment device serves a one-level directory giving access to environment variables
+and their values.
+It is conventionally bound to
+.BR /env .
+The value of an environment variable 
+.I name
+may be obtained by reading the file
+.BI /env/ name.
+If the file does not exist, the variable is unset and has the value nil. The
+maximum length of a variable name is 127 bytes.
+.PP
+New environment variables are set by creating the corresponding file
+in
+.B /env
+and writing the required value to that file. Similarly environment variables are destroyed (unset) by
+removing the corresponding file.
+.PP
+Processes sharing an `environment group' see the same files and contents;
+changes made by one process are seen by the others.
+A process can insulate itself from further changes using the
+.L FORKENV
+option to
+.IR sys-pctl (2),
+which creates a new environment group that is a copy of the old,
+but further changes in each are independent.
+A new empty environment group is created by the
+.L NEWENV
+option to
+.IR sys-pctl (2).
+.SH SOURCE
+.B /os/port/devenv.c
+.br
+.B /emu/port/devenv.c
+.SH SEE ALSO
+.IR env (1),
+.IR sh (1),
+.IR env (2),
+.IR sys-pctl (2)
--- /dev/null
+++ b/man/3/ether
@@ -1,0 +1,134 @@
+.TH ETHER 3
+.SH NAME
+ether \- Ethernet device
+.SH SYNOPSIS
+.nf
+.BI "bind -a #l" n " /net"
+
+.BI /net/ether n /clone
+.BI /net/ether n /[0-7]
+.BI /net/ether n /[0-7]/data
+.BI /net/ether n /[0-7]/ctl
+.BI /net/ether n /[0-7]/stats
+.BI /net/ether n /[0-7]/type
+.BI /net/ether n /[0-7]/ifstats
+.fi
+.SH DESCRIPTION
+The Ethernet device
+.BI #l n
+serves a three-level directory representing a physical Ethernet interface:
+AMD LANCE, 3Com 3C509, 3Com 3C905, Intel 82557, and others.
+If
+.I n
+is not given it is taken to be 0.
+.PP
+The top level directory
+has a single directory named
+.BI ether n,
+where
+.I n
+is the interface number, starting from 0,
+assigned in some platform-dependent way.
+That directory contains a conversation directory for each of 8 Ethernet packet types,
+and a
+.B clone
+file.
+.PP
+Opening the
+.B clone
+file returns a file descriptor open on the
+.B ctl
+file of an unused conversation directory.
+Reading the
+.B ctl
+file returns a text
+string representing the number of the
+connection.
+The connection is controlled by writing textual commands to the associated
+.B ctl
+file:
+.TP
+.BI connect " type"
+Set the
+.I type
+of Ethernet packets received on the connection; the type is
+expressed
+as an integer constant
+(in hexadecimal if it has a leading
+.BR 0x ,
+octal if it has a leading
+.BR 0 ,
+and otherwise decimal).
+For instance, Ethernet packets carrying IP version 4 use type
+.BR 0x800 .
+The value
+.B \-1
+stands for all types.
+The value
+.B \-2
+causes at most the first 64 bytes of all types of packets to be copied
+to the conversation.
+If several conversations are assigned the same packet type,
+a copy of the packet is given to each.
+.TP
+.B promiscuous
+Set the interface to capture all packets regardless of destination address.
+An interface normally receives only packets whose 
+destination address is that of the interface or is the
+broadcast address,
+.BR ffffffffffff .
+The interface remains promiscuous until the control file is
+closed.
+The extra packets are received only by conversations of the same type
+as the incoming packet (or of type -1).
+.TP
+.BI addmulti " address"
+Add the given MAC multicast
+.I address
+to the set of multicast addresses accepted by the interface.
+.I Address
+is a twelve digit MAC address in hexadecimal.
+.TP
+.BI remmulti " address"
+Remove
+.I address
+from the set of multicast addresses accepted by the interface.
+.PP
+Incoming Ethernet packets are demultiplexed by destination address and
+packet type and queued
+for reading by the corresponding open connection(s).
+Each read of the
+.B data
+file returns each packet in turn, including the Ethernet header.
+A read will terminate at packet boundaries.
+Each write to the
+.B data
+file causes a packet to be sent.
+The Ethernet address of the interface is inserted into
+the packet header as the source address.
+.PP
+Reading the
+.B type
+file returns the decimal value of the assigned Ethernet packet type.
+.PP
+Reading the
+.B stats
+file returns status information, the value
+of counters, the `promiscuous' state,
+and the Ethernet MAC address of the
+interface.
+.PP
+Reading the
+.B ifstats
+file returns statistics and status information specific to a given
+hardware interface.
+.SH SOURCE
+.B /os/port/devlance.c
+.br
+.B /os/port/netif.c
+.br
+.B /os/*/devether.c
+.br
+.B /os/*/ether*.c
+.SH SEE ALSO
+.IR ip (3)
--- /dev/null
+++ b/man/3/flash
@@ -1,0 +1,157 @@
+.TH FLASH 3
+.SH NAME
+flash \- flash memory
+.SH SYNOPSYS
+.nf
+.BI "bind -a #F" \fR[\fPn\fR]\fP " /dev"
+
+.B /dev/flash
+.BI /dev/flash/ part
+.BI /dev/flash/ part ctl
+.fi
+.SH DESCRIPTION
+The flash memory device serves a two-level directory,
+giving access to files representing part or all of a bank of flash memory.
+A platform might have more than one bank of flash, numbered starting from 0.
+The attach specifier
+.I n
+is a decimal integer that selects a particular bank of flash (default: 0).
+Both NOR and NAND flash is supported.
+For both types of flash, the driver gives a read/write/erase interface to the raw flash device,
+which can impose constraints on operations beyond those imposed by the driver.
+Other drivers such as
+.IR ftl (3)
+or
+.IR logfs (3)
+implement any higher-level format required, including ECC for NAND flash, for instance.
+.PP
+The top level directory contains a single directory named
+.B flash
+for bank 0, and
+.BI flash n
+for each other bank
+.IR n .
+It contains two files for each partition:
+a data file
+.I part
+and an associated control file
+.IB part ctl ,
+where
+.I part
+is the name of the partition.
+Each partition represents a region of flash memory that starts and ends on a flash segment (erase unit) boundary.
+The system initially creates a single standard partition
+.B flash
+representing the whole of flash memory, and the corresponding control file
+.BR flashctl .
+Other partitions can be created by writing to
+.B flashctl
+as described below.
+.PP
+The data file
+.I part
+provides read and write access to the bytes on the system's flash memory.
+Bytes can be read and written on any byte boundary:
+the interface hides any alignment restrictions.
+A read returns the value of the bytes at the current file offset, where zero is the start of the partition.
+A write reprograms the flash to the given byte values, at the current
+file offset (relative to the start of the partition), using the physical
+device's reprogramming algorithm.
+An erased flash byte is logically
+.B 16rFF
+(regardless of the conventions of the physical flash device).
+A write can change a bit with value 1 to a 0,
+but cannot change a 0 bit to 1;
+that can only be done by erasing one or more flash segments.
+NAND flash typically has restrictions on the number of writes allowed to a page before requiring a block erase.
+Reads and writes are unbuffered.
+.PP
+The control file
+.BI part ctl
+can be read and written.
+A read returns several lines containing decimal and hexadecimal numbers
+(separated by white space)
+revealing the characteristics of memory within the partition.
+The first line gives the 
+the manufacturer ID,  the flash device ID, the memory width in bytes, and a string giving the flash type
+(currently either
+.B nor
+or
+.BR nand ).
+Subsequent lines give characteristics of each group of erase units within the partition,
+where the erase units within a group have the same properties.
+Each line gives the start and end (as byte addresses) of the erase units in the region
+that lie within the partition, followed by the size in bytes of each erase unit, which is followed
+for NAND flash by the size in bytes of a page.
+The sizes for NAND flash include the extra bytes per page typically used to hold an ECC and block status.
+A write contains one of the following textual commands:
+.TF erasexx
+.TP
+.BI add " name start end"
+Create a new partition that ranges from
+.I start
+to
+.I end
+within the current partition.
+Each value must be numeric (decimal, octal or hexadecimal) and a multiple of the erase unit size.
+.I Name
+must not be the name of an existing partition.
+On success, new files
+.I name
+and
+.IB name ctl
+will appear in the parent
+.B flash
+directory.
+.TP
+.B erase all
+Erase the whole flash partition, setting all bytes to
+.BR 16rFF ,
+except those that are hardware write-protected.
+.TP
+.BI erase " offset"
+Erase the segment that begins at the given
+.I offset
+within the partition,
+setting all bytes to
+.BR 16rFF ,
+except those that are hardware write-protected.
+The
+.I offset
+is given in bytes, but must be a multiple
+of the segment (erase unit) size.
+.TP
+.BR protectboot [ " off " ]
+By default the system prevents erase unit 0 of the flash from being
+erased or written, assuming it
+contains the primary bootstrap.
+Writing this command with parameter
+.B off
+removes that protection.
+Writing
+.B protectboot
+with any other parameter (or none) restores the protection.
+Note that a manufacturer might also have locked the flash in hardware,
+and that protection must be removed in a device-dependent way.
+.TP
+.B sync
+If the underlying device must buffer or cache (current devices do not), flush the buffer(s).
+.PD
+.PP
+The syntax of all numbers is that of
+.BR strtoul (10.2) ;
+the default base is 10.
+.SH SOURCE
+.B /os/*/devflash.c
+.br
+.B /os/*/flash*.c
+.SH SEE ALSO
+.IR ftl (3)
+.SH DIAGNOSTICS
+A write will return an error if
+an attempt is made to change a 0 bit to 1,
+or if the flash memory fails to be programmed correctly.
+.SH BUGS
+The flash cannot be written if the kernel is executing directly from flash,
+because the physical flash cannot be read during programming,
+and the driver does not copy the programming code to DRAM.
--- /dev/null
+++ b/man/3/floppy
@@ -1,0 +1,52 @@
+.TH FLOPPY 3 
+.SH NAME
+floppy \- floppy disk interface
+.SH SYNOPSIS
+.nf
+.B bind -a '#f' /dev
+
+.B /dev/fd0disk
+.B /dev/fd0ctl
+.B /dev/fd1disk
+.B /dev/fd1ctl
+.B /dev/fd2disk
+.B /dev/fd2ctl
+.B /dev/fd3disk
+.B /dev/fd3ctl
+.fi
+.SH DESCRIPTION
+.PP
+The floppy disk interface serves a one-level directory giving access to up
+to four floppy disk drives.
+Each drive is represented by a data and control file.
+There are no partitions.
+.PP
+Messages accepted by the
+.B ctl
+file include:
+.TF format
+.TP
+.B eject
+Eject the floppy, if possible.
+.TP
+.B reset
+Reset the drive.
+.TP
+.BI format " type
+Format the floppy.  The
+.I type
+sets the density and
+type of disk to be formatted; see
+.IR format (8).
+.PD
+.PP
+A read of the
+.B ctl
+file returns a string describing the form factor of the disk, one of
+.BR 3½DD ,
+.BR 3½HD ,
+.BR 5¼DD ,
+or
+.BR 5¼HD .
+.SH SOURCE
+.B /os/pc/devfloppy.c
--- /dev/null
+++ b/man/3/fpga
@@ -1,0 +1,97 @@
+.TH FPGA 3
+.SH NAME
+fpga \- interface to on-board FPGA
+.SH SYNOPSIS
+.B "bind -a #F /dev"
+.PP
+.B /dev/fpgaclk
+.br
+.B /dev/fpgactl
+.br
+.B /dev/fpgamemb
+.br
+.B /dev/fpgamemw
+.br
+.B /dev/fpgaprog
+.br
+.B /dev/fpgastatus
+.SH DESCRIPTION
+.I Fpga
+allows configuration of an on-board FPGA (eg, the Altera Flex6000 on the Bright Star Engineering ip-Engine),
+control of its clocks and environment, and raw access to any devices presented in the FPGA address space.
+On booting, the FPGA subsystem is normally left powered down and the system's external clocks are not directed to
+the processor's output pins, to conserve power if the subsystem is unused.
+.PP
+The control file
+.B fpgactl
+accepts the following requests:
+.TF "power off"
+.TP
+.BI bclk " n"
+Set BCLK output to
+.I n
+MHz,
+where
+.I n
+must be a divisor of the system clock frequency (eg, of 48 if the system is running at 48 MHz).
+.TP
+.BI power
+Power up the FPGA subsystem (that is automatically done when
+.B fpgaprog
+is accessed).
+.TP
+.B "power off"
+Power down the FPGA subsystem.
+.TP
+.B reset
+Reset the FPGA, forcing it into configuration mode (automatically done when
+.B fpgaprog
+is accessed).
+.TP
+.BI vclk " n m v r"
+Program the clock synthesiser with the given parameters, and enable its output as VCLK.
+.TP
+.B "vclk off"
+Disable output from the clock synthesiser.
+.PD
+.PP
+The files
+.B fpgamemb
+and
+.B fpgamemw
+give 8-bit and 16-bit access respectively to the address space interpreted by the FPGA.
+The file offset is the address read or written within that space;
+offset and count must both be even for
+.BR fpgamemw .
+The interpretation of the data, including the data format and
+whether byte or word access is required, is completely determined
+by the program configured into the FPGA.
+.PP
+The write-only file
+.B fpgaprog
+configures the device.
+A single write provides an FPGA configuration in raw binary format.
+The FPGA subsystem is given power, the processor's clocks are made visible externally, and the
+device is configured with the data written.
+The write returns an error if configuration fails.
+.PP
+The read-only file
+.B fpgastatus
+contains a single line of text giving the state of the two status bits in the FPGA interface.
+The word
+.B config
+appears if
+.B CONFIG_DONE
+is set, and
+.B !config
+otherwise; the word
+.B status
+appears if
+.B nSTATUS
+is set, and
+.B !status
+otherwise.
+.SH SOURCE
+.B /os/ipengine/devfpga.c
+.SH SEE ALSO
+.IR fpgaload (8)
--- /dev/null
+++ b/man/3/fs
@@ -1,0 +1,120 @@
+.TH FS 3 emu
+.SH NAME
+fs \- host file system interface
+.SH SYNOPSIS
+.EX
+bind #U\fIspec\fP /
+
+sys->bind("#U\fIspec\fP", "/", Sys->MAFTER|Sys->MCREATE);
+.EE
+.SH DESCRIPTION
+.I Fs
+provides an interface to the host file system when running
+.IR emu (1).
+The device is automatically bound to the root of the Inferno namespace when
+.I emu
+starts.
+.I Fs
+maps filenames from the Inferno namespace to the real host filesystem
+namespace. The mapping is determined by the host system directory that
+.I emu
+serves (see the description of the
+.B -r
+option in
+.IR emu (1)).
+.PP
+The plain name
+.B #U
+always refers to the root of the Inferno tree within the host file system.
+An optional
+.I spec
+string can select a different tree using conventions peculiar to the host system type.
+Currently, on Unix and Plan 9, a
+.I spec
+of
+.B *
+(ie,
+.BR #U* )
+refers to the root of the host system;
+on Windows, the
+.I spec
+can give a device letter (eg,
+.BR #Uc: )
+to refer to the Windows tree on that device.
+.PP
+When there is nothing but
+.IR root (3)
+in the name space, the device can only be bound from Limbo using
+.IR sys-bind (2)
+as shown above, since there is nothing in the name space
+to support either
+.IR bind (1)
+or
+.IR newns (2).
+It can later be rebound elsewhere in the name space using
+.IR bind (1).
+.PP
+.I Fs
+provides only an approximation to the
+file ownership and permissions checking defined in
+.IR intro (5)
+and other entries in this manual.
+.IR Emu (1)
+typically runs as the user that starts it, and that user will own all newly-created
+files and directories, and that user's host system access rights control access
+to any file.
+Other details are specific to the host system, as given below.
+.PP
+The set of characters visible in file names within
+.I fs
+is also determined by the host operating system, not Inferno.
+For instance, names might contain control characters, or even `/',
+which might render them unusable in Inferno.
+.SS Unix
+When checking owner, group and other permissions, the intent in Inferno is that
+each is tried in turn, in that order.
+Unix checks just the first that applies, so that if group
+permission disallows access but other allows it, the access is still denied.
+If
+.IR emu (1)
+is run as the super-user, Inferno's rules for ownership and permission checking are enforced by
+.I fs
+itself; otherwise, Unix's permission checking prevails.
+.PP
+On MacOS X, file names might be case insensitive, depending on file system settings.
+.SS FAT file system (Windows9x and Windows/NT)
+The values of
+.B uid
+and
+.B gid
+are
+.BR Everyone .
+.PP
+Files and directories always have read and execute permission,
+which cannot be changed.
+Files without write permission cannot be removed.
+.SS NTFS file system (Windows/NT, 2000, XP)
+Permissions for read, write and execute operates as described in
+.IR sys-stat (2).
+.IR Emu (1)
+attempts to maintain a limited
+but consistent map between Inferno and NT worlds, specifically between Inferno names
+and NT security IDs.
+Special NT group
+.B Everyone
+represents `other' for file permissions.
+The Inferno
+.B uid
+is the file owner under NT;
+the Inferno
+.B gid
+reported is the first user in the file's ACL
+that is neither the owner nor
+.BR Everyone ;
+failing that, the
+.B gid
+is the file's owner.
+.SH SOURCE
+.B /emu/*/devfs.c
+.br
+.B /emu/port/devfs-posix.c
--- /dev/null
+++ b/man/3/ftl
@@ -1,0 +1,185 @@
+.TH FTL 3
+.SH NAME
+ftl \- flash translation layer
+.SH SYNOPSIS
+.EX
+.B bind -a '#X' /dev
+
+.B /dev/ftlctl
+.B /dev/ftldata
+.EE
+.SH DESCRIPTION
+.PP
+The flash translation layer device
+provides the interface for management of
+rewritable blocks on a flash memory device.
+Flash memory differs from normal disc
+or memory in that it is organised in large blocks (erase units),
+typically 64k bytes or more in size,
+and although writes can reset bits they cannot set them;
+instead an entire erase unit must be erased at once.
+These properties make it unsuitable for direct use by
+a conventional block-oriented file system.
+The flash translation layer compensates by
+implementing a logical to physical
+mapping that allows 512-byte blocks to be read or written
+in the same way as rewritable disc blocks.
+The translation layer manages the details of
+block remapping, copying erase units to reclaim obsolete physical versions
+of rewritten logical blocks, erase unit load wearing, etc.
+.PP
+The flash translation device serves a one-level directory,
+giving access to two files.
+The control file
+.B ftlctl
+receives commands to format a flash device
+or initialise access to an already formatted device.
+.B Ftldata
+is the data file, giving access to the logical blocks on the formatted flash.
+For example, it can be given to
+.IR kfs (3)
+for use as a file system.
+The length of the
+.B ftldata
+file as returned by
+.B Sys->stat
+shows the total logical (formatted) space available for
+use by the driver's clients.
+.PP
+The target flash device is identified to this driver
+by name
+(eg,
+.BR #F/flash )
+in a control message defined below.
+The flash device must have the following properties:
+.IP 1.
+It must have a corresponding control file
+.IB device ctl
+(eg,
+.BR #F/flashctl ),
+which must be writable.
+.IP 2.
+The flash control file must accept a command of the
+form
+.DS
+.BI erase " offset"
+.DE
+which must cause the flash erase unit
+starting at the given byte
+.I offset
+to be erased.
+.IP 3.
+The device
+must allow reads and writes of any number of bytes
+on arbitrary byte boundaries (file offset).
+(In other words, the flash driver must hide alignment restrictions.)
+.IP 4.
+A write request
+must allow previously-written regions to be updated provided
+the new data does not change any 0 bit to 1
+(ie, writes can clear bits to 0 but will not change any 0 bits to 1).
+.PP
+The following control messages can be written to
+.BR ftlctl :
+.TP
+.BI format " device [ offset [ n [ erasesize ] ] ]"
+.br
+Erase
+.I n
+bytes of
+the given flash
+.I device
+starting at the given byte
+.IR offset ,
+and format the erased region
+for use by the flash translation layer. Omitting the
+optional parameters is equivalent to setting them to
+.BR 0xffffffff .
+.I Erasesize
+is the number of bytes in the flash device's erase unit; setting
+to
+.B 0xffffffff
+takes the value from the underlying device.
+If
+.I offset
+is
+.BR 0xffffffff ,
+then the underlying device is searched from the
+start for an existing flash translation layer header, and the remaining
+parameters are taken from there. If
+.I n
+is
+.BR 0xffffffff ,
+then everywhere from
+.I offset
+to the end of the
+underlying device is erased. Otherwise,
+.I offset
+and
+.I n
+must be multiples of
+.IR erasesize .
+Make the newly formatted device's contents available on
+.BR ftldata .
+.TP
+.BI init " device [ offset [ n [ erasesize ] ] ]"
+.br
+Make available on
+.B ftldata
+the logical blocks (with existing content) of a previously-formatted
+.IR device .
+The parameters are as defined for the
+.B format
+command, above.
+.TP
+.BI part " name start [ limit ]"
+.br
+Add a partition. This creates a new data file
+.BI ftl name
+with similar properties to
+.BR ftldata ,
+but which constrains the range of the formatted data accessed to
+begin at
+.IR start ,
+and end at
+.IR limit-1 ,
+or the last byte of the formatted data if
+.I limit
+is omitted.
+.TP
+.BI delpart " name"
+.br
+Removes a partition. 
+.TP
+.B detach
+Stop flash translation on
+the corresponding flash device,
+and close it.
+An error results instead if
+.B ftldata
+is open.
+.TP
+.B scavenge
+Force scavenging of reusable blocks (mainly intended to be used when
+testing flash or debugging the driver).
+.TP
+.BI trace " n"
+.br
+Trace the actions of the flash translation driver.
+No tracing is done if
+.I n
+is zero.
+Larger values of
+.I n
+increase the level of detail.
+.SH SOURCE
+.B /os/*/devftl.c
+.SH SEE ALSO
+.IR flash (3)
+.SH BUGS
+Reads and writes of
+.B ftldata
+must be multiples of 512 bytes in length
+and start on a 512-byte block boundary.
+.br
+Only one flash device can be active.
--- /dev/null
+++ b/man/3/gpio
@@ -1,0 +1,94 @@
+.TH GPIO 3 SA1100
+.SH NAME
+gpio \- access to GPIO registers
+.SH SYNOPSIS
+.B bind -a
+.B #G
+.B /dev
+.PP
+.nf
+.B /dev/gpioclear
+.B /dev/gpioctl
+.B /dev/gpioedge
+.B /dev/gpioset
+.B /dev/gpiostatus
+.fi
+.SH DESCRIPTION
+.PP
+The GPIO interface
+serves a one-level directory with five files
+that give access to the GPIO registers in the SA1100.
+See the SA1100 handbook for details of the function
+of the various registers.
+.PP
+The control file
+.B gpioctl
+accepts commands to set individual bits in the edge detect
+registers.
+Each control message has three space-separated fields:
+.IP
+.EX
+.I "reg pin value"
+.EE
+.LP
+where
+.I reg
+is a single character
+denoting a register,
+.I pin
+a bit within it, and
+.I val
+the value (0 or 1) for that bit.
+Valid choices for
+.I reg
+are:
+.B d
+(GPDR),
+.B r
+(GRER),
+.B f
+(GFER),
+and
+.B a
+(GAFR).
+For example, the control message
+.IP
+.EX
+d 10 1
+.EE
+.LP
+sets bit 10 (following the handbook's bit-numbering conventions) in the GPIO pin
+direction register GPDR.
+.PP
+The read-only file
+.B gpiostatus
+shows the names and values (in hexadecimal) of all GPIO registers.
+.PP
+The remaining data files
+allow bits to be read by
+.B Sys->read
+requests
+and set by
+.B Sys->write
+requests.
+When read, each file returns the value of a given
+register as a single 8 digit hexadecimal number:
+.B gpioset
+and
+.B gpioclear
+both give the value of the level register, GPLR;
+and
+.B gpioedge
+gives the value of the edge dectect register, GEDR.
+Each write request
+should present a single number in textual form, which
+is assumed to be hexadecimal by default.
+The value is written to a GPIO register:
+.B gpioset
+corresponds to GPSR,
+.B gpioclear
+corresponds to GPCR, and
+.B gpioedge
+corresponds to GEDR.
+.SH SOURCE
+.B /os/sa1100/devgpio.c
--- /dev/null
+++ b/man/3/i2c
@@ -1,0 +1,87 @@
+.TH I2C 3
+.SH NAME
+i2c \- basic I2C interface
+.SH SYNOPSIS
+.B bind -a
+.BI #J n
+.B /dev
+.PP
+.BI /dev/i2c. n .ctl
+. br
+.BI /dev/i2c. n .data
+.fi
+.SH DESCRIPTION
+.I I2c
+serves a one-level directory with two files
+that give access to the target device with address
+.I n
+(given in hexadecimal)
+on the system's I2C bus.
+.I N
+is usually determined by the I2C device manufacturer.
+I2C gives address 0 special meaning as the `general call' address.
+See an I2C specification for details.
+.PP
+The control file
+.BI i2c. n .ctl
+accepts commands to set the valid address range and
+subaddressing mode for the corresponding data file.
+The following control messages can be written to it:
+.TP
+.B a10
+Force 10-bit addressing instead of 7-bit addressing.
+Otherwise 10-bit addressing is used only if the device address
+.I n
+is bigger than 255.
+.TP
+.BI size " nbytes"
+.br
+Set the logical size of the target device to
+.IR nbytes .
+(By default when opened, it is 256 bytes, enough for most small I2C devices.)
+IO requests will be kept within this limit.
+This value is also returned by
+.B Sys->stat
+as the length of the data file.
+.TP
+.BI subaddress " \fR[\fP n \fR]\fP"
+.br
+Cause subsequent reads and writes
+on the data file to use I2C subaddressing
+with
+.I n
+byte subaddresses (default: 1 byte).
+.I N
+must be no larger than 4.
+The target device must support subaddressing.
+By default, the device is not subaddressed.
+Setting
+.I n
+to zero switches off subaddressing.
+.PP
+When read,
+the control file displays the current settings.
+.PP
+The data file
+.BI i2c. n .data
+can be read or written to
+exchange data with the slave device with address
+.I n
+(where
+.I n
+is given in hexadecimal).
+Each write request transmits the given data
+to the device.
+Each read request sends a receive
+request to the device and returns the resulting data.
+If the I2C target is subaddressed, the current file offset
+is used as the subaddress;
+otherwise the file offset is ignored, and the device typically starts at 0 for each transfer request.
+Read and write requests are trimmed to the declared
+size of the device.
+.SH SOURCE
+.B /os/port/devi2c.c
+.br
+.B /os/*/i2c.c
+.br
+.B /os/cerf405/iic.c
--- /dev/null
+++ b/man/3/i82365
@@ -1,0 +1,41 @@
+.TH I82365 3 
+.SH NAME
+i82365 \- Personal Computer Memory Card Interface Association (PCMCIA) device
+.SH SYNOPSIS
+.nf
+.B bind -a #y /dev
+
+.B /dev/pcm0attr
+.B /dev/pcm0ctl
+.B /dev/pcm0mem
+.B /dev/pcm1attr
+.B /dev/pcm1ctl
+.B /dev/pcm1mem
+.fi
+.SH DESCRIPTION
+The
+.I i82365
+driver provides an interface to an Intel
+82365-compatible PCMCIA interface chip.
+This chip supports up to 2 PCMCIA slots, 0
+and 1.
+Reading
+.B pcm[01]attr
+returns the contents of attribute memory.
+Reading or writing
+.B pcm[01]mem
+reads or writes RAM on the card.
+Reading
+.B pcm[01]ctl
+returns the card's status.
+.PP
+This driver must be included to use PCMCIA
+devices such as the NE4100 Ethernet card.
+The individual card drivers make calls to routines
+in the PCMCIA driver.
+.SH SOURCE
+.B /os/pc/devi82365.c
+.SH "SEE ALSO"
+.IR plan9.ini (10.8)
+.SH BUGS
+There is no driver for the Databook PCMCIA interface chip.
--- /dev/null
+++ b/man/3/indir
@@ -1,0 +1,81 @@
+.TH INDIR 3
+.SH NAME
+indir \- attach to device indirectly by name
+.SH SYNOPSIS
+.BI "bind  #*" name
+[\c
+.BI ! spec\c
+]
+.I " dir"
+.SH DESCRIPTION
+.I Indir
+allows any other device to be referred to by its name instead of its perhaps arbitrary single character type;
+.I indir
+itself has the type character
+.RB ` * '.
+It has no name space of its own.
+On attach (see
+.IR attach (5))
+.I indir
+interprets its device specifier string as the
+.I name
+of a device to which it should attach, optionally followed by specifier
+.I spec
+for that device, separated from the
+.I name
+by an exclamation mark.
+Attaching to
+.I indir
+(eg, by
+.IR sys-bind (2)),
+effectively attaches to the device with the given
+.I name
+and
+.IR spec ,
+and all subsequent operations in the resulting name space access that
+device, not
+.I indir
+itself.
+.PP
+For example, to access
+.IR cap (3),
+one could write:
+.IP
+.B "bind -a '#*cap' /dev"
+.PP
+The following commands both list the second instance of
+.IR ether (3),
+first directly, then using
+.IR indir :
+.IP
+.B "ls '#l1'"
+.br
+.B "ls '#*ether!1'"
+.PP
+The file
+.B /dev/drivers
+(see
+.IR cons (3))
+lists the names of currently configured devices.
+.SS Credit
+Invented by Bruce Ellis for Lucent's internal Research Inferno to help name dynamically-loaded device drivers.
+This is a re-implementation.
+.SH SOURCE
+.B /emu/port/devindir.c
+.br
+.B /os/port/devindir.c
+.SH SEE ALSO
+.IR bind (1),
+.IR sys-bind (2),
+.IR cons (3)
+.SH DIAGNOSTICS
+If
+.I name
+is not configured,
+.I indir
+returns a suitable diagnostic in the error string.
+.SH BUGS
+Arguably the kernel
+could simply look up the
+.I name
+itself.
--- /dev/null
+++ b/man/3/ip
@@ -1,0 +1,927 @@
+.TH IP 3
+.SH NAME
+ip \- network protocols over IP
+.SH SYNOPSIS
+.nf
+.B bind -a #I\f1[\f5\f2ifn\f1]\f5 /net
+
+.B /net/arp
+.B /net/bootp
+.B /net/iproute
+.B /net/ipselftab
+.B /net/iprouter
+.B /net/log
+
+.B  /net/ipifc/clone
+.B /net/ipifc/stats
+.BI /net/ipifc/ n 
+.BI /net/ipifc/ n /data
+.BI /net/ipifc/ n /ctl
+.BI /net/ipifc/ n /local
+.BI /net/ipifc/ n /status
+
+.BI  /net/ proto /clone
+.BI /net/ proto /stats
+.BI /net/ proto / n 
+.BI /net/ proto / n /ctl
+.BI /net/ proto / n /data
+.BI /net/ proto / n /err
+.BI /net/ proto / n /local
+.BI /net/ proto / n /remote
+.BI /net/ proto / n /status
+.BI /net/ proto / n /listen
+\&...
+.fi
+.SH DESCRIPTION
+The IP device serves a directory representing a self-contained
+collection of IP interfaces.
+There may be several instances, identified by the decimal interface number
+.IR ifn ,
+that follows the
+.B #I
+device name;
+.B #I0
+is assumed by default.
+Each instance
+has a disjoint collection of IP interfaces, routes and address resolution maps.
+A physical or virtual device, or
+.IR medium ,
+that produces IP packets is associated
+with a logical IP network using the mechanisms described under
+.I "Physical and logical interfaces"
+below.
+Commonly all IP media on a host are assigned to a single
+instance of
+.BR #I ,
+which is conventionally bound to
+.BR /net ,
+but other configurations are possible: interfaces might be assigned
+to different device instances forming separate
+logical IP networks
+to partition networks in firewall or
+gateway applications.
+.PP
+Hosted Inferno provides a subset of the interface described here that gives
+to  the TCP/IP and UDP/IP of the host system's own IP subsystem.
+See
+.IR "Hosted interfaces"
+below for a summary of the differences.
+.SS Protocols
+Within each instance,
+the IP device provides
+an interface to each IP protocol configured into the system, such as TCP/IP or UDP/IP.
+.PP
+Each of the protocols is served by the IP device, which represents a
+connection by a set of device files.
+The top level directory,
+.I proto
+in the
+.SM SYNOPSIS
+above,
+is named after a protocol (eg,
+.BR tcp ,
+.BR il ,
+.BR udp )
+and contains a
+.B clone
+file, a
+.B stats
+file,
+and subdirectories numbered from zero to the number of connections
+configured for this protocol.
+.PP
+The read-only
+.B stats
+file contains protocol-specific statistics as one or more lines of text.
+There is no particular format, but the values are often a superset
+of those required by the SNMP MIB.
+.PP
+Opening the
+.B clone
+file reserves a connection, represented by
+one of the numbered subdirectories.  The resulting file descriptor
+will be open on the control file,
+.BR ctl ,
+of the newly allocated connection.
+Reading the
+.B ctl
+file returns a text
+string representing the number of the
+connection.
+Connections may be used either to listen for incoming calls
+or to initiate calls to other machines.
+.PP
+A connection is controlled by writing text strings to the associated
+.B ctl
+file.
+After a connection has been established data may be read from
+and written to the data file.
+.PP
+Before sending data, remote and local addresses must be set for the connection.
+For outgoing calls the local port number will be allocated randomly if none is set.
+Addresses are set by writing control messages to the
+.B ctl
+file of the connection.
+The connection is not established until the data file is opened.
+There are two models depending on the nature of the protocol.
+For connection-oriented protocols, the process will block on open
+until the remote host has acknowledged the connection,
+either accepting it, causing a successful return from open,
+or rejecting it, causing open to return an appropriate error.
+For connectionless protocols, the open always succeeds;
+the `connect' request sets local parameters for the source and destination fields
+for use by subsequent read and write requests.
+.PP
+The following control messages are provided by this interface
+to all protocols.
+A particular protocol can provide additional commands, or
+change the interpretation or even syntax of those below,
+as described in the manual page for that protocol.
+The description below shows
+the standard commands with the default argument syntax and interpretation:
+.TP
+.BI connect\  ipaddress ! port "[!r]\ [\f2lport\f5]"
+Set the remote IP address and port number for the connection.
+If the
+.B r
+flag
+is supplied and the optional local port
+.I lport
+has not been specified the system will allocate
+a restricted port number (between 600 and 1024) for the connection to allow communication
+with Unix machines'
+.B login
+and
+.B exec
+services.
+.TP
+.BI "announce\ [" ipaddress !] port
+Set the local port
+number to
+.I port
+and accept calls to that port.
+.I Port
+is a decimal port number or
+.LR * .
+If
+.I port
+is zero, assign a port number
+(the one assigned can be read from the
+.B local
+address file).
+If
+.I port
+is
+.LR * ,
+accept
+calls for any port that no process has explicitly announced.
+If the optional
+.I ipaddress
+is given, set the local IP address for the connection
+to that address, and accept only those incoming calls to
+.I port
+that are addressed to
+.IR ipaddress .
+.B Announce
+fails if the connection is already announced or connected.
+.TP
+.BI bind\  port
+.I Port
+is a decimal port number or
+.LR * .
+Set the local port number to
+.IR port .
+This request exists to support emulation of
+of BSD sockets and is otherwise neither needed nor used in Inferno.
+.TP
+.BI tos " \f1[\f2 n \f1]\f2"
+Set the type-of-service value in outgooing packets to
+.I n
+(default: 0).
+.TP
+.BI ttl " \f1[\f2 n \f1]\f2"
+Set the time-to-live (TTL) value in packets transmitted on this conversation
+to
+.I n
+(default: 255).
+.PP
+Port numbers must be in the range 1 to 32767.
+.PP
+Several read-only files report the status of a
+connection.
+The
+.B remote
+and
+.B local
+files contain the IP address and port number for the remote and local side of the
+connection.
+The
+.B status
+file contains protocol-dependent information to help debug network connections.
+The first word on the first line gives the status of the
+connection.
+.PP
+Having announced, a process may accept incoming connections by calling
+.B open
+on the
+.B listen
+file.
+The
+.B open
+will block until a new connection request arrives;
+it will then
+return an open file descriptor that points to the control file of the
+newly accepted connection.
+Repeating this procedure will accept all calls for the
+given protocol.
+.PP
+In general it should not be necessary to use the file system interface to the
+networks.
+The
+.BR dial ,
+.BR announce ,
+and
+.BR listen
+functions described in
+.IR dial (2)
+perform the necessary I/O to establish and
+manipulate network connections.
+.SS TCP protocol
+The TCP protocol is the standard Internet
+protocol for reliable stream communication; it does not preserve
+read/write
+boundaries.
+.PP
+A connection is controlled by writing text strings to the associated
+.B ctl
+file.
+After a connection has been established data may be read from
+and written to the data file.
+The TCP protocol provides a stream connection that does not preserve
+read/write
+boundaries.
+.PP
+For outgoing calls the local port number will be allocated randomly if none is set.
+Addresses are set by writing control messages to the
+.B ctl
+file of the connection.
+The connection is not established until the data file is opened.
+For TCP the
+process will block until the remote host has acknowledged the connection.
+.PP
+As well as the standard control messages above,
+TCP accepts the following:
+.TP
+.BI hangup
+Send a TCP reset (RST) to the remote side and end the conversation,
+without waiting for untransmitted data to be acknowledged,
+unlike a normal close of the device.
+.TP
+.BI keepalive\ [ "n" ]
+Enable `keep alive'
+mode:
+if no traffic crosses the link within a given period, send a
+packet to check that the remote party is still there, and remind
+it that the local connection is still live.
+The optional value
+.I n
+gives the keep-alive time in milliseconds (default: 120000).
+.PP
+The
+.B status
+file has many lines, each containing a labelled number, giving the values
+of parameters and statistics such as:
+maximum allowed connections, outgoing calls, incoming calls, established but later reset,
+active calls, input segments, output segments, retransmitted segments, retransmitted timeouts,
+input errors, transmitted reset.
+.SS UDP protocol
+.PP
+UDP provides the standard Internet
+protocol for unreliable datagram
+communication.
+.PP
+UDP opens always succeed.
+Before sending data, remote and local addresses must be set for the connection.
+Alternatively, the following special control requests can be used:
+.TP
+.B headers
+Set the connection to use an address header with IPv6 addressing
+on reads and writes of the data file,
+allowing a single connection to send datagrams to converse with
+many different destination addresses and ports.
+The 52 byte binary header appears before the data
+read or written.
+It contains: remote IP address, local IP address, interface IP address, remote port, and local port.
+The IP addresses are 16 bytes each in IPv6 format, and
+the port addresses are 2 bytes each, all written in network (big-endian) order.
+On reads, the header gives the values from the incoming datagram,
+except that if the remote used a multicast destination address, the IP address
+of the receiving interface is substituted.
+On writes, the header provides the destination for the resulting datagram,
+and if the local IP address corresponds to a valid local unicast interface,
+that address is used, otherwise the IP address of the transmitting interface
+is substituted.
+.TP
+.B headers4
+Set the connection to use an address header with IPv4 addresses
+on reads and writes of the data file,
+allowing a single connection to send datagrams to converse with
+many different destination addresses and ports.
+The 12 byte binary header appears before the data
+read or written.
+It contains: remote IP address, local IP address, remote port, and local port.
+The IP addresses are 4 bytes each,
+the port addresses are 2 bytes each, all written in network (big-endian) order.
+On reads, the header gives the values from the incoming datagram.
+On writes, the header provides the destination for the resulting datagram.
+This mode is obsolete and destined for oblivion.
+.PP
+A read of less than
+the size of the datagram will cause the entire datagram to be consumed.
+Each write to the data file will send a single datagram on the network.
+.PP
+In replies, in connection-oriented mode, if the remote address
+has not been set, the first arriving packet sets the following
+based on the source of the incoming datagram:
+the remote address and port for the conversation,
+and the local address is set to the destination address in the
+datagram unless that is a multicast address, and then the address
+of the receiving interface is used.
+.PP
+If a conversation is in
+.B headers
+mode, only the local port is relevant.
+.PP
+Connection-oriented UDP is hungup if an ICMP error (eg, host or port unreachable,
+or time exceeded) arrives with matching port.
+.PP
+The
+.I udp
+.B status
+file contains four lines, each containing a labelled number counting an event:
+input datagrams, datagrams on unannounced ports, datagrams with wrong checksum, and output datagrams.
+.SS IL Protocol
+IL provides a reliable point-to-point datagram service for communication between Plan 9 and
+native Inferno machines.
+Each read and write transfers a single datagram, as for UDP.
+The datagrams are delivered reliably and in order.
+Conversations are addressed and established as for TCP.
+.SS Routing
+.PP
+The
+.B iproute
+file can be read and written.
+When read, it returns the contents of the IP routing tables,
+one line per entry,
+with six fields giving the
+destination host or network address, address mask,
+gateway address, route type, tag (see below), and the number of the
+.B ipifc
+interface owning the route
+(or
+.RB ` - '
+if none).
+The route type is up to four characters:
+.B 4
+or
+.B 6
+(IPv4 or IPv6 route);
+.B i
+(route is interface);
+one of
+.B u
+(unicast),
+.B b
+(broadcast),
+or
+.B m
+(multicast);
+and lastly
+.B p
+if the route is point-to-point.
+.PP
+Commands can also be written to control the routing:
+.TP
+.BI add " ip mask gw \f1[\f2 tag \f1]\f2"
+Add a route via the gateway identified by IP address
+.I gw
+to the address specified by
+.I ip
+and subnet mask
+.IR mask .
+Tag the resulting table entry with the
+.I tag
+provided, or the current
+.I tag
+(see
+.B tag
+below),
+or the tag
+.BR none .
+.TP
+.BI flush " \f1[\f2 tag \f1]\f2"
+Remove all routes with the given
+.I tag
+that do not correspond to a local interface.
+If
+.I tag
+is not given, flush all routes.
+.TP
+.BI remove " ip mask"
+Remove routes to the given address.
+.TP
+.BI tag " tag"
+Tag the routes generated by writes on the current file descriptor with
+the given
+.IR tag
+of up to 4 characters.
+The default is
+.BR none ,
+set when
+.B iproute
+is opened.
+.PP
+The
+.B ipselftab
+file summarises the addresses and routes that refer to the local host.
+It gives an address, the number of logical interfaces, and the interface type
+in the same form as the route type of
+.BR iproute .
+.PP
+The
+.B iprouter
+file is provided for use by a user-level application acting as an IP gateway.
+It is effective only when the kernel-level gateway is not enabled
+(see the
+.B iprouting
+interface control request below).
+Once opened, packets that are not addressed to a
+local address can be read from this device.
+The packet contents are preceded by a 16 byte binary header that
+gives the IPv6 address of the local interface that received the packet.
+.SS Bootstrap
+.PP
+The read-only
+.B bootp
+file contains the results of the last BOOTP
+request transmitted on any interface (see
+.I "Physical and logical interfaces"
+below)
+as several lines of text,
+with two fields each.
+The first field names an entity and the second field gives its value in IPv4 address format.
+The current entities are:
+.IP
+.RS
+.TF ipaddr
+.TP
+.B auip
+Authentication server address
+.TP
+.B fsip
+File server address
+.TP
+.B gwip
+Address of an IP gateway out of this (sub)net.
+.TP
+.B ipaddr
+Local IP address
+.TP
+.B ipmask
+Subnet mask for the local IP address
+.RE
+.PD
+.PP
+If any value is unknown (no reply to BOOTP, or value unspecified),
+the value will be zero, represented as
+.BR 0.0.0.0 .
+.SS Address resolution
+The
+.B arp
+file can be read and written.
+When read,
+it returns the contents of the current ARP cache as a sequence of lines,
+one per map entry, giving
+type, state, IP address and corresponding MAC address.
+Several textual commands can be written to it:
+.TP
+.BI add " \f1[\f2 medium \f1]\f2 ip mac"
+Add a mapping from IP address
+.I ip
+to the given
+.I mac
+address (a sequence of bytes in hexadecimal)
+on the given
+.IR medium .
+It must support address resolution (eg, Ethernet).
+If the
+.I medium
+is not specified, find the one associated with a route to
+.I ip
+(which must be IPv4).
+.TP
+.B flush
+Clear the cache.
+.SS Logging
+.PP
+The
+.B log
+file provides protocol tracing and debugging data.
+While the file is held open, the system
+saves, in a small circular buffer, error messages logged by selected protocols.
+When read, it returns data not previously read,
+blocking until there is data to read.
+The following commands can be written to determine what is logged:
+.TP
+.BI set " proto ..."
+Enable logging of messages from each source
+.IR proto ,
+one or more of:
+.BR ppp ,
+.BR ip ,
+.BR fs ,
+.BR tcp ,
+.BR il ,
+.BR icmp ,
+.BR udp ,
+.BR compress ,
+.BR ilmsg ,
+.BR gre ,
+.BR tcpmsg ,
+.BR udpmsg ,
+.BR ipmsg
+and
+.BR esp .
+.TP
+.BI clear " proto ..."
+Disable logging of messages from the given sources.
+.SS Physical and logical interfaces
+The configuration of the physical and logical IP interfaces
+in a given instance of
+.B #I
+uses
+a virtual protocol
+.B ipifc
+within that instance,
+that adds, controls and removes
+IP interfaces.
+It is represented by the protocol directory
+.BR ipifc .
+Each connection corresponds to an interface to a physical or virtual medium on
+which IP packets can be sent and received.
+It has a set of associated values:
+minimum and maximum transfer unit,
+MAC address, and a set of logical IP interfaces.
+Each logical IP interface has local and remote addresses and an address mask.
+.PP
+Opening the
+.B clone
+file returns a file descriptor open on the
+.B ctl
+file for a new connection.
+A medium is then attached using a
+.B bind
+request;
+logical interfaces are associated by
+.B connect
+or
+.BR add ;
+they are removed by
+.BR remove ;
+and finally
+.B unbind
+detaches the medium from the connection.
+For certain types of media, the
+.B unbind
+is automatic when the connection itself is closed.
+With most media, including Ethernet,
+the
+.B ipifc
+connection files can be closed after configuration, and later
+reopened if need be to add or remove logical interfaces,
+or set other parameters.
+.PP
+The
+.B ctl
+file responds to the following text commands, including interface-specific variants
+of standard
+IP device
+requests:
+.TP
+.BI bind " medium " "[ \f5\f2name\f5 [ \f2arg ...\f5 ]"
+Attach device
+.I medium
+to the interface, which must not already be bound to a device.
+The
+.I name
+and subsequent arguments are interpreted by the driver for the
+.IR medium .
+The device name associated with the interface is
+.IR name ,
+if given, or a generated name otherwise.
+.TP
+.BR connect " \f2ip\f5 [\f2mask \f5[\f2remote \f5[\f2mtu \f5]]]"
+Remove all existing logical interfaces and create a new one as if by
+.B add
+(see below).
+The connection must be bound to a medium.
+.TP
+.BR add " \f2ip\f5 [\f2 mask \f5[\f2 remote \f5[\f2 mtu \f5] ] ]"
+Add a logical interface with local IP address
+.IR ip .
+The default for
+.I mask
+is the mask for
+.IR ip 's
+address class;
+for the
+.IR remote
+address,
+.IR ip 's
+network; and for
+.IR mtu ,
+the largest MTU allowed by the medium.
+The new interface is registered in the IP routing tables.
+.TP
+.B bootp
+Broadcast a BOOTP packet (using
+.BR udp ).
+If a valid response is received, set the interface's IP address and mask,
+and the IP stack's default gateway to the results obtained from BOOTP.
+The results are also available to applications by reading
+the
+.B bootp
+file above.
+Note that this mechanism is now deprecated in favour of
+.IR dhcpclient (2).
+.TP
+.BI remove " ip mask"
+Remove the logical interface determined by
+.I ip
+and
+.IR mask .
+.TP
+.BI iprouting\ [ "n" ]
+Control the use of IP routing on this
+.IR ip (3)
+instance.
+If
+.I n
+is missing or non-zero, allow use as a gateway,
+rerouting via one interface packets received on another.
+By default,
+or if
+.I n
+is zero, use as a gateway is not allowed: if a packet received
+is not addressed to any local interface, either pass it to
+a gateway application if active (see
+.B iprouter
+in
+.IR ip (3)),
+and otherwise drop the packet.
+.TP
+.BI mtu " n"
+.br
+Set the maximum transmit unit (MTU) on this interface to
+.I n
+bytes, which must be valid for the medium.
+.TP
+.BI addmulti " multi"
+Add the multicast address
+.I multi
+to the interface.
+.TP
+.BI remmulti " multi"
+Remove the multicast address
+.I multi
+from the interface.
+.TP
+.BI unbind
+Remove any association between
+the current medium (device) and the connection:
+remove all routes using this interface, detach the device,
+stop packet transport, and
+remove all logical interfaces.
+The connection is ready for re-use.
+.PP
+The
+.B local
+file contains one line for each logical interface, of the form:
+.IP
+.IB local -> self ...
+.PP
+where
+.I local
+is the local address associated with the interface and each
+.I self
+is a broadcast or multicast address that can address that interface,
+including subnet addresses, if any.
+.PP
+The
+.B status
+file contains many fields:
+the first two give the device name and the value of the current MTU,
+followed by 7 fields per line for each logical interface:
+local address, address mask, remote address, packets in, packets out, input errors, and output errors.
+.PP
+The following sections describe the media drivers available.
+Each is separately configurable into a kernel.
+.SS Ethernet medium
+Ethernet devices as described in
+.IR ether (3)
+can be bound to an IP interface.
+The bind request has the form:
+.IP
+.BI "bind ether " device
+.PP
+The interface opens two conversations on the given Ethernet
+.IR device ,
+for instance
+.BR ether0 ,
+using an internal version of
+.BR dial ,
+with the addresses
+.IB device !0x800
+(IPv4)
+and
+.IB device !0x806
+(ARP).
+See
+.IR dial (2)
+for the interpretation of such addresses.
+The interface runs until a process does an explicit
+.BR unbind .
+Multicast settings made on the interface are propagated to the
+.IR device .
+.SS Point-to-point medium
+An asynchronous serial device as described in
+.IR eia (3)
+can be bound to an interface as a Point-to-Point protocol (PPP) device.
+The bind request has the form:
+.IP
+.BI "bind ppp " "serial ip remote mtu framing username secret"
+.PP
+All parameters except
+.I serial
+are optional.
+The character
+.RB ` - '
+can appear as a placeholder for any parameter.
+Except for authentication data, an attempt is made to negotiate
+suitable values for any missing parameter values, including network addresses.
+The parameters are interpreted as follows:
+.IP
+.RS
+.TF username
+.TP
+.I serial
+Name of the device that will run PPP.
+.TP
+.I ip
+Local IP address for the interface.
+.TP
+.I remote
+IP address of the other end of the link.
+.TP
+.I mtu
+Initial MTU value for negotiation (default: 1450)
+.TP
+.I framing
+If
+.I framing
+is zero, do not provide asynch. framing (on by default).
+Unimplemented.
+.TP
+.I username
+Identification string used in PAP or CHAP authentication.
+.TP
+.I secret
+Secret used in authentication; with CHAP it never crosses the link.
+.PD
+.RE
+.PP
+If the name
+.I serial
+contains
+.RB ` ! '
+a connection will be opened using
+.B dial
+(see
+.IR dial (2)).
+Otherwise the name will be opened as-is;
+usually it is the name of a serial device
+(eg,
+.BR "#t/eia0" ).
+In the latter case, a companion
+.B ctl
+file will also be opened if possible, to set serial characteristics for PPP
+(flow control, 64kbyte queue size, nonblocking writes).
+An attempt is made to start the PPP link immediately.
+The write of the
+.B bind
+control message returns with an error if the link cannot be started,
+or if negotiation fails.
+The PPP link is automatically unbound if the line hangs up (eg, modem drops carrier),
+or an unrecoverable error occurs when reading or writing the connection.
+.PP
+The PPP implementation can use either PAP and CHAP authentication,
+as negotiated, provided an appropriate
+.I username
+and
+.I secret
+is given in the
+.B bind
+request.
+It does not yet support the Microsoft authentication scheme.
+.SS Packet medium
+The packet medium allows an application to be source and sink
+for IP packets.
+It is bound to an interface by the simple request:
+.IP
+.B "bind pkt"
+.PP
+All other interface parameters including its IP address are
+set using the standard
+.I ipifc
+requests described above.
+Once that has been done, the application reads the
+.B data
+file of the interface to receive packets addressed to the interface,
+and it writes to the file to inject packets into the IP network.
+The interface is automatically unbound when all interface files are closed.
+.SS Hosted interfaces
+Native Inferno and Plan 9 have related IP implementations.
+Plan 9
+.I emu
+therefore simply imports Plan 9's
+.BR /net ,
+and in the absence of version-specific differences, what is described
+above still applies.
+.PP
+On all other hosted platforms,
+the IP device gives applications
+within
+.IR emu (1)
+a portable interface to TCP/IP and UDP/IP, even through it
+is ultimately using the host system's own TCP/IP and UDP/IP implementations
+(usually but not always socket based).
+The interface remains the same: for instance by
+.B /net/tcp
+and
+.BR /net/udp ,
+but is currently more limited in the set of services and control requests.
+Both IPv4 and IPv6 address syntax may be used, but the IPv6 form must
+still map to the IPv4 address space if the IPv6 support is not configured into
+.IR emu .
+Only TCP and UDP are generally available, and a limited interface to ARP on some platforms (see below).
+The set of TCP/UDP control requests is limited to:
+.BR connect ,
+.BR announce ,
+.BR bind ,
+.BR ttl ,
+.BR tos ,
+.BR ignoreadvice ,
+.BR headers4 ,
+.BR oldheaders ,
+.BR headers ,
+.BR hangup
+and
+.BR keepalive .
+.PP
+The write-only
+.B arp
+file is implemented only on some Unix systems, and
+is intended to allow the implementation of
+the BOOTP protocol
+using Inferno, on hosted systems.
+It accepts a single textual control request:
+.TP
+.BI add " ip ether"
+Add a new ARP map entry, or replace an existing one, for IP address
+.IR ip ,
+associating it with the given
+.I ether
+MAC address.
+The
+.I ip
+address is expressed in the usual dotted address notation;
+.I ether
+is a 12 digit hexadecimal number.
+.PP
+An error results if the host system does not allow the ARP map
+to be set, or the current user lacks the privileges to set it.
+.SH SOURCE
+.B /emu/port/devip.c
+.br
+.B /os/ip/devip.c
+.br
+.BI /os/ip/ proto .c
+.br
+.B /os/ip/ipifc.c
+.br
+.br
+.B /os/ip/*medium.c
+.SH "SEE ALSO"
+.IR dial (2)
+.\" joinmulti and leavemulti are unimplemented
+.\" many media are only partly implemented
--- /dev/null
+++ b/man/3/kprof
@@ -1,0 +1,88 @@
+.TH KPROF 3 
+.SH NAME
+kprof \- kernel profiling
+.SH SYNOPSIS
+.nf
+.B bind -a #T /dev
+.sp
+.B /dev/kpctl
+.B /dev/kpdata
+.fi
+.SH DESCRIPTION
+The
+.I kprof
+device provides simple profiling
+data for the operating system kernel.   The data accumulates by
+recording the program counter of the kernel at each `tick' of a
+profiling clock. Often this is just the system clock, but may be an
+independent higher priority timer which allows profiling of interrupt
+handlers, dependent on implementation.
+.PP
+The file
+.B kpdata
+holds the accumulated counts in big-endian
+byte order.
+The size of the file depends on the size of kernel text, and the
+size of the individual counts.
+The first six counts are 4 bytes in size, and are special. The
+first holds the total number of ticks, the second the number of
+ticks which fell outside the kernel text, the third the number
+of microseconds per tick, the fourth the number of sample buckets,
+the fifth the size in bytes of each sample bucket, and sixth the
+log base 2 of the sample bucket size. Typically the sample size
+is 4, and the log base 2 of the bucket size 3 (8 bytes). The remainder
+of the file holds the sample buckets. The entire file has size
+.I "sample bucket size"
+times
+.IR "number of sample buckets" .
+That is, the first six sample buckets are replaced with the special
+numbers.
+.PP
+The file
+.B kpctl
+controls profiling.
+Writing the string
+.B start
+to
+.B kpctl
+begins profiling;
+.B stop
+terminates it.  The message
+.B startclr
+restarts profiling after zeroing the array of counts.
+.PP
+.IR Kprof (10.1)
+formats the data for presentation.
+.SH EXAMPLE
+The following
+.IR sh (1)
+commands define and invoke a
+function
+.B sample
+that runs a given test program with kernel profiling enabled,
+and copies the result to a server presumed to be mounted on
+.BR /n/remote .
+.sp
+.EX
+fn sample {
+	echo start >/dev/kpctl
+	$1
+	echo stop >/dev/kpctl
+	cp /dev/kpdata /n/remote/tmp/kpdata
+}
+bind -a '#T' /dev
+sample {cp sound /dev/audio}
+.EE
+.PP
+On the server, the
+.IR kprof (10.1)
+command is used to analyse the data:
+.EX
+	kprof /usr/inferno/os/mpc/impc /tmp/kpdata
+.EE
+.SH SOURCE
+.B /os/port/devkprof.c
+.SH SEE ALSO
+.IR kprof (10.1)
+.SH BUGS
+It cannot provide times for each node in the dynamic call graph (dynamic profiling).
--- /dev/null
+++ b/man/3/logfs
@@ -1,0 +1,260 @@
+.TH LOGFS 3
+.SH NAME
+logfs \- log-structured file system for flash devices
+.SH SYNOPSIS
+.B bind -b '#ʟ' /dev
+.PP
+.B /dev/logfsctl
+.br
+.B /dev/logfsusers
+.br
+.BI /dev/logfs name
+.br
+.BI /dev/logfs name boot
+.PP
+.B echo fsys
+.I name
+.B config
+.I flash-device
+.B "> /dev/logfsctl"
+.br
+.B echo fsys
+.I name
+.B format
+.I boot-area-size
+.B "> /dev/logfsctl"
+.br
+.B echo fsys
+.I name
+.B open
+.B "> /dev/logfsctl"
+.br
+.BI "mount -Ac /dev/logfs" name
+.I dir
+.SH DESCRIPTION
+.I Logfs
+is a driver level implementation of the Inferno log structured
+filesystem, a file system designed with modern flash devices
+(such as NAND flash) in mind.
+.I Logfs
+is itself hardware independent, requiring other
+devices to provide the physical medium. Currently only
+.IR flash (3)
+devices are supported.
+.PP
+The file system maintains two storage regions on the same medium:
+a log-structured hierarchical file system that implements all the
+functionality communicable by the 9P protocol
+(see
+.IR intro (5)),
+and a boot
+partition that offers a fixed amount of flat storage suitable for
+holding such things as a kernel boot image, boot parameters etc.  The
+boot partition can be accessed and updated without understanding all
+but the simplest facts about the hierarchical file system, making it
+easy to implement boot loaders with small footprints.
+.PP
+The physical layout of the file system varies from medium to medium,
+so that the specialised features of the medium can be accounted for.
+.PP
+The user table is maintained in memory; there is one table, shared between
+all file systems supported by this driver. There is no distinction between
+users and groups: a user is a group with one member.
+The user table records a mapping
+between uids
+and
+unames,
+as well as recording the leader and members of each group. A
+.I uid
+is a string naming a user or group that is stored in the file system. A
+.I uname
+is the string naming a user or group that is used in
+file system protocol messages (see
+.IR intro (5)).
+There is a distinction so that
+unames
+can be safely reused, even though
+uids
+cannot.
+.PP
+Configuration and control of the file systems is by writing commands to
+.BR /dev/logfsctl :
+.TP
+.BI fsys " name"
+.br
+Sets the current file system to
+.IR name ,
+which must be configured. Subsequent commands, not prefixed with
+.BI fsys name\fR,
+will by applied to the name file system.
+.TP
+.BI [fsys " name\f5] config\fI flash-device"
+.br
+Configures file system
+.I name
+to be written to
+.BI flash (3)
+device
+.IR flash-device .
+This does not initialise or format the filesystem, but simply bind
+.I name
+to
+.IR flash-device .
+For each configured
+.IR name ,
+two files appear in the device name space,
+.BI /dev/logfs name
+and
+.BI /dev/logfs name\f5boot\fI.
+The former serves the 9P protocol giving access to the hierarchical file system;
+the latter is a fixed size file that represents the boot partition.
+.TP
+.BI [fsys " name\f5] format \fIbootsize"
+.br
+Formats the underlying medium as a
+.I logfs
+format file system. Sufficent storage is set aside to create a boot partition of at
+least 
+.I bootsize
+bytes. Some medium implementations (eg, for NAND flash) store file system
+parameters (eg, location, size and boot partition size) to enable automatic
+location of file systems and improve bad block detection. These must
+be part of the boot partition so that they are easily accessible to boot loaders,
+so such medium implementations may also enforce a minimum size for the
+boot partition.
+.TP
+.BI [fsys " name\f5] open [-P] [-W]"
+.br
+Initialises the specified file system, and makes it available for use. The file
+system structure is verified, bad blocks are repaired and, in the case of the
+log structured file system, and the log replayed to regenerate the directory structure.
+The
+.B -W
+option reduces the permission control on
+.IR wstat (5)
+requests. Specifically it allows the
+.BR uid ,
+.BR gid ,
+.BR mtime ,
+and
+.B perm
+to be changed by anyone.
+The
+.B -P
+option removes all file access controls, allowing anyone to open any file in
+any mode.
+.TP
+.BI [fsys " name\f5] sweep"
+.br
+Forces the log to be swept, if it has been written to since last being swept.
+The log is normally swept automatically when space is low.
+.TP
+.BI [fsys " name\f5] sync"
+.br
+Flush any buffered log or data to the underlying device.
+Use before shutting down the system if the device is not unmounted.
+(See notes below.)
+.TP
+.BI [fsys " name\f5] trace [\fIlevel\f5]"
+.br
+If
+.I
+level
+is non-zero, internal diagnostics for the log file system are enabled at the
+given level. If
+.I level
+is zero, or missing, tracing is disabled.
+.TP
+.BI [fsys " name\f5] unconfig"
+.br
+removes the configuration. The file system must not be mounted, or the
+boot partition open, before doing this.
+.TP
+.BI uname " uname uid"
+.TP
+.BI uname " uname \f5:\fPuid"
+.br
+adds the user with uname
+.I uname
+and uid
+.I uid
+to the in-memory table
+.TP
+.BI uname " uname \f5%\fPnewname"
+.br
+renames
+.I uname
+to
+.IR newname ,
+throughout the user table
+.TP
+.BI uname " uname \f5=\fPleader"
+.br
+sets the group leader to the uname
+.I leader
+.TP
+.BI uname " uname \f5="
+.br
+removes the group leader; then all members are leaders
+.TP
+.BI uname " uname \f5+\fPmember"
+.br
+add the uname
+.I
+member
+to the group
+.TP
+.BI uname " uname \f5-\fPmember"
+.br
+removes the uname
+.I
+member
+from the group
+.SS Notes
+The file system log may be subject to a small amount of buffering for efficiency
+purposes; therefore, it is necessary to unmount the file system before disconnecting
+the power to avoid losing recent updates. Failure to do this does not result in
+inconsistencies in the file system, but some recent changes will be lost.
+Equivalently, a
+.IR wstat (5)
+of any file or directory, with all fields set to
+.I no change
+(also known as a
+.I wstat
+.IR flush)
+will cause the log to be written to disk. Note that during a dismount, and also a
+.I wstat
+.IR flush ,
+a
+.I wstat
+flush
+is also applied to the underlying
+.IR flash (3)
+device. Furthermore, since some buffering is used on the log,
+needless use of
+.I wstat flush
+will consume log space more rapidly than normal, although it will be recovered during
+the next sweep.
+.PP
+The log is automatically swept when space is low, so there is not normally any
+need to use the
+.B sweep
+command. 
+.SH SOURCE
+.B /liblogfs
+.br
+.B /libnandfs
+.br
+.B /emu/port/devlogfs.c
+.br
+.B /os/port/devlogfs.c
+.SH SEE ALSO
+.IR flash (3),
+.IR ftl (3),
+.IR kfs (4)
+.SH BUGS
+The only medium currently supported is NAND flash. This is detected by
+recognising the manufacturer and device ids supplied by the status file of
+the
+.IR flash (3)
+device.
--- /dev/null
+++ b/man/3/lpt
@@ -1,0 +1,51 @@
+.TH LPT 3 
+.SH NAME
+lpt \- parallel port interface
+.SH SYNOPSIS
+.nf
+.B bind -a #L[123] /dev
+
+.B /dev/lpt[123]data
+.B /dev/lpt[123]dlr
+.B /dev/lpt[123]pcr
+.B /dev/lpt[123]psr
+.fi
+.SH DESCRIPTION
+The
+.I lpt
+driver provides an interface to the parallel
+interface normally used for printers.
+The specifiers
+.BR 1 ,
+.BR 2 ,
+and
+.BR 3
+correspond to
+the parallel interfaces at PC ports 0x378, 0x3bc, and 0x278
+respectively.
+.PP
+.B Lpt?data
+is write only.
+Writing to it sends data to the interface.
+This file is sufficient for communicating with most printers.
+.PP
+.BR Lpt?dlr ,
+.BR lpt?pcr ,
+and
+.B lpt?psr
+are used for fine control of the parallel port.
+Reading or writing these files corresponds to
+reading and writing the data latch register,
+printer control register, and printer status
+register.
+These are used by programs to drive special devices.
+The data is written as a hexadecimal
+(leading
+.BR 0x ),
+octal
+(leading
+.BR 0 )
+or decimal number.
+Reads return a hexadecimal number representing the value of the register.
+.SH SOURCE
+.B /os/*/devlpt.c
--- /dev/null
+++ b/man/3/mnt
@@ -1,0 +1,84 @@
+.TH MNT 3 
+.SH NAME
+mnt \- attach to 9P servers
+.SH SYNOPSIS
+.nf
+.B #M
+.fi
+.SH DESCRIPTION
+The
+.I mount driver
+is used by
+.B Sys->mount
+(but not
+.BR Sys->bind ;
+see
+.IR sys-bind (2))
+to connect the name space of a process to
+the service provided by a 9P server over a communications channel.
+After the
+.BR mount ,
+file operations in that portion of the name space will
+be converted by the mount driver into the appropriate
+9P messages to the server, and its 9P replies returned
+as the status of the system calls.
+.PP
+Any authentication must precede
+.IR mount ,
+which does none itself;
+see
+.IR security-intro (2)
+for a discussion of the authentication and security
+mechanisms provided `ready made' by Inferno, but
+any other scheme can be agreed between client and server.
+The
+.I mount
+system call does, however, issue an
+.IR attach (5)
+message to the server to identify the user of the connection.
+Each distinct user of a connection must mount it separately;
+the mount driver multiplexes the access of the various users and their
+processes to the service.
+.PP
+File-oriented system calls are converted by the kernel into messages in the 9P protocol.
+Within the kernel, 9P is implemented by procedure calls to the
+various kernel device drivers, as described in
+.IR intro (10)
+and
+.IR dev (10.2).
+The mount driver translates these procedure calls into remote procedure calls
+to be transmitted as messages over the communication channel to the server.
+Each message is implemented by a write
+of the corresponding protocol message to the server channel
+followed by a read on the server channel to get the reply.
+Errors in the reply message are turned into system call error returns.
+.PP
+A
+.IR sys-read (2)
+or
+.I Sys->write
+system call on a file served by the mount driver
+may be translated
+into more than one
+message,
+since there is a maximum data size for a 9P message.
+The system call
+will return when the specified number of bytes have been transferred
+or a short reply is returned.
+.PP
+The string
+.L #M
+is an illegal file name,
+so this device can be accessed directly only by the kernel.
+.SH "SEE ALSO"
+.IR bind (1),
+.IR sys-bind (2)
+.SH SOURCE
+.B /emu/port/devmnt.c
+.br
+.B /os/port/devmnt.c
+.SH BUGS
+When mounting a service through the mount driver,
+that is, when the channel being multiplexed is itself
+a file being served by the mount driver,
+large messages may be broken in two.
--- /dev/null
+++ b/man/3/mpeg
@@ -1,0 +1,102 @@
+.TH MPEG 3
+.SH NAME
+mpeg \- Boffin mpeg decoder
+.SH SYNOPSIS
+.B
+bind -b #E /dev
+.PP
+.B /dev/mpeg
+.br
+.B /dev/mpegctl
+.SH DESCRIPTION
+The
+.I mpeg
+device serves two files giving access to a Boffin MPEG-1 decoder.
+.PP
+The control file
+.B mpegctl
+when read returns lines of text giving the device's properties.
+Each line starts
+.B video
+or
+.B audio
+followed by a list of capabilities.
+The current device returns a fixed set:
+.IP
+.EX
+video mpeg1,sif
+.br
+audio musicam,I musicam,II
+.EE
+.PP
+The control file accepts the following commands:
+.TF pause
+.PD
+.TP
+.B init
+Reinitialise the device's video and audio subsystems;
+cancel the effect of
+.B stop
+or
+.B pause
+.TP
+.B stop
+Stop the display immediately.
+.TP
+.B pause
+Suspend display.
+.TP
+.B video iso11172 mpeg1,sif
+Set the output video mode and formats; only this precise setting is valid.
+.TP
+.BI audio " format rate"
+There are two choices for
+.IR format :
+.B musicam,I
+(MPEG audio layer 1),
+with
+.I rate
+from 64000 to 448000 Hz,
+and
+.B musicam,II
+(MPEG audio layer 2)
+with
+.I rate
+from 48000 to 384000.
+.TP
+.BI window " minx miny maxx maxy"
+Sets the overlay window on the screen, in screen coordinates.
+If the window size exceeds 320x240, the image is scaled accordingly,
+separately in each dimension.
+.PP
+The data file
+.B mpeg
+is an exclusive-use device.
+Once the device has been initialised and an output window
+set using the
+.B init
+and
+.B window
+commands to the control file,
+and an audio format selected by the
+.B audio
+command,
+MPEG data can be written in the agreed format to the
+.B mpeg
+file,
+for display in the output window.
+The driver buffers the data written and implements flow control between application
+and the hardware.
+When closed, the device waits for the last block of data to display
+before returning to the application.
+.SH SOURCE
+.B /os/pc/devmpeg.c
+.SH CONFIGURATION
+.B "mpeg0=port=0x1e0 irq=15 dma=6
+.SH SEE ALSO
+.IR mpeg (2),
+.I stream
+in
+.IR sys-read (2)
+.SH DIAGNOSTICS
+An error results on a write to the device once stopped.
--- /dev/null
+++ b/man/3/pbus
@@ -1,0 +1,60 @@
+.TH PBUS 3
+.SH NAME
+pbus \- USR/3Com Edgeserver packet bus
+.SH SYNOPSIS
+.B bind -a '#Y' /dev
+.PP
+.B /dev/pbus
+.br
+.B /dev/pbusctl
+.SH DESCRIPTION
+The packet bus driver serves a directory containing two files,
+giving MAC-level access to the EPB version of the packet bus
+of the US Robotics (3Com) Edgeserver chassis.
+.PP
+The read-only file
+.B pbusctl
+contains a single integer that gives the chassis slot occupied by the Edge server
+(its address on the packet bus).
+.PP
+The file
+.B pbus
+is read and written to communicate on the bus.
+Each write
+transmits a single packet on the bus.
+The driver expects a four byte header in each write:
+.IP
+.IB "slot chan" " 0 0"
+.PP
+where
+.I slot
+is the destination chassis slot number and
+.I chan
+identifies a logical receiver in that slot.
+Including the header, the largest write accepted is 128 bytes.
+The driver completes the header as the bus firmware requires;
+it also pads each packet with zero bytes to a hardware packet boundary
+as it puts it on the bus.
+It flashes green in the `Wan TX' LED as packets are transmitted.
+.PP
+Each read of the
+.B pbus
+file returns the contents of a single packet received from the bus.
+The read will block if necessary until a packet arrives.
+The data includes a four byte header
+as above, and padding to a hardware packet boundary.
+Normally a higher-level protocol will provide its own headers including
+an actual length.
+.PP
+At reset, the driver downloads firmware to the controller if required.
+All invalid packets received are quietly discarded.
+Packets are also dropped whenever an internal input queue overflows
+because the reader has not kept up with arriving traffic.
+.SH SOURCE
+.B /os/pc/devpbus.c
+.SH SEE ALSO
+.IR plap (3)
+.SH BUGS
+The driver detects but does not recover from bus clock loss
+.br
+The driver cannot be included in the public distribution.
--- /dev/null
+++ b/man/3/pipe
@@ -1,0 +1,69 @@
+.TH PIPE 3
+.SH NAME
+pipe \- two-way interprocess communication
+.SH SYNOPSIS
+.nf
+.BI "bind '#|'" dir
+.IB dir /data
+.IB dir /data1
+.fi
+.SH DESCRIPTION
+A pipe provides a mechanism for interprocess I/O by
+reading and writing file descriptors (see
+.IR sys-read (2)).
+An
+.IR attach (5),
+typically via
+.IR sys-pipe (2)
+or
+.IR sys-bind (2),
+allocates two files that are cross-connected:
+data written to one can be read back from the other, in the same order.
+.PP
+Write boundaries are preserved: each read terminates when the read buffer
+is full or after reading the last byte of a write, whichever comes first.
+In particular, a write of zero bytes will result in a zero-length read,
+which is usually interpreted by readers as end-of-file, but could be used
+to delimit the data stream for other purposes.
+.PP
+Written data is buffered by the kernel and stored on internal queues
+(see
+.IR qio (10.2)).
+The maximum block size is 128k bytes;
+larger writes will be split across several blocks, which are queued separately.
+Each read will return data from at most one block.
+Concurrent writers are therefore guaranteed that their data will not be interleaved
+with data from other writers (ie, will be written atomically) only when
+each write is less than the maximum buffer size.
+Writers to pipe interfaces on remotely mounted portions of the namespace have their guarantee of atomicity lowered to
+.BR Sys->ATOMICIO
+bytes by
+.IR mnt (3).
+.PP
+The system mediates between producer and consumer.
+Writers will block when buffered data reaches a high-water mark, currently 32k bytes,
+until a reader has reduced it by half.
+The length returned by
+.IR sys-stat (2)
+on each name
+gives the number of bytes waiting to be read on the corresponding end of the pipe.
+.PP
+When all file descriptors on
+one side of the pipe have been closed, and after any remaining data has
+been read,
+a reader on the other side sees end-of-file (count of zero) on a subsequent read.
+Once both ends are closed, the pipe can be reused.
+.PP
+A pipe persists until it is unmounted and no processes have either end open.
+.SH SOURCE
+.B /emu/port/devpipe.c
+.br
+.B /os/port/devpipe.c
+.SH "SEE ALSO"
+.IR sys-file2chan (2),
+.IR sys-pipe (2)
+.SH DIAGNOSTICS
+Writes to a closed pipe generate an exception
+.RB ` "write on closed pipe" '.
+Persistently reading a closed pipe after reading end-of-file
+will result in a read error.
--- /dev/null
+++ b/man/3/plap
@@ -1,0 +1,136 @@
+.TH PLAP 3
+.SH NAME
+plap \- USR/3Com Edgeserver packet bus link access protocol
+.SH SYNOPSIS
+.B bind -a '#X' /net
+.PP
+.nf
+.B /net/plap/clone
+.BI /net/plap/ n /ctl
+.BI /net/plap/ n /data
+.BI /net/plap/ n /local
+.BI /net/plap/ n /remote
+.BI /net/plap/ n /status
+.BI /net/plap/ n /listen
+.SH DESCRIPTION
+The
+.I plap
+driver serves a three-level directory giving access to the link level
+protocol used by an Edgeserver card to communicate with
+other cards in a USR/3Com chassis.
+On first
+.I attach
+(usually by a
+.IR bind (1)
+to
+.BR /net )
+the driver opens a connection to the packet bus device
+.B #Y
+described in
+.IR pbus (3).
+It multiplexes that connection using a specialised member of the LAP family
+of protocols.
+.PP
+The top level directory contains a single directory naming the protocol,
+.BR plap .
+It contains a
+.B clone
+file and a set of conversation directories numbered from 0 to 127;
+only active conversation directories are present.
+.PP
+Opening the
+.B clone
+file allocates a new conversation directory
+.IR n .
+The file descriptor will point to the control file,
+.BR ctl ,
+of that directory.
+Reading the file returns a text string giving the conversation number.
+.PP
+Each conversation directory contains six files.
+The connection is controlled by writing text strings to the
+.B ctl
+file.
+Once a connection has been established, data is exchanged with the
+remote peer by reading and writing the
+.B data
+file.
+.PP
+A
+.I plap
+address has the form:
+.IP
+[
+.IB slot .
+]
+.IB chan ! sap
+.PP
+where
+.I slot
+is a chassis slot number,
+.I chan
+is a logical channel (eg, a modem number),
+and
+.I sap
+is a number representing a `service access point'.
+.PP
+The following control messages are supported:
+.TP
+.BI connect " remoteaddr \fP\fR[\fP \fIlocaladdr\fP ]\fI "
+Make a connection to the given
+remote address (of the form given above).
+If a local address is not specified, the
+system will assign an unused address dynamically
+(the local chassis slot number is of course fixed).
+Connect fails if the connection is already announced or connected,
+or
+.I localaddr
+is already in use.
+.TP
+.BI announce address
+Set the local address to
+.I address
+and accept incoming calls to that address.
+If no address is specified, all incoming calls to addresses not explicitly announced
+will be accepted by this connection.
+Announce fails if the connection is already announced or connected,
+or the local
+.I address
+specified is already in use.
+.PP
+The
+.B remote
+and
+.B local
+files contain the remote and local addresses for the connection
+in the full address form
+.IB slot . chan ! sap.
+The
+.B status
+file returns a line giving the directory name, number of active opens, and the state
+of the connection.
+.PP
+A process listens for incoming calls by opening the
+.B listen
+file using
+.IR sys-open (2).
+The open will block until a call arrives, when it will return a file descriptor
+open on the
+.B ctl
+file of the newly accepted connection, which can be read to find the directory
+number and thus the other files.
+.PP
+The device follows the conventions that allow
+.BR dial ,
+.BR announce
+and
+.B listen
+of
+.IR dial (2)
+to do the work needed to establish and manipulate network connections.
+.SH SOURCE
+.B /os/pc/devplap.c
+.SH SEE ALSO
+.IR pbus (3)
+.SH BUGS
+The driver cannot be included in the public distribution.
--- /dev/null
+++ b/man/3/pnp
@@ -1,0 +1,150 @@
+.TH PNP 3
+.SH NAME
+pnp, pci \- Plug 'n' Play ISA and PCI Interfaces
+.SH SYNOPSIS
+.nf
+.B bind -a '#$' /dev
+
+.BI /dev/pci/ bus\fL.\fPdev\fL.\fPfn ctl
+.BI /dev/pci/ bus\fL.\fPdev\fL.\fPfn raw
+
+.BI /dev/pnp/ctl
+.BI /dev/pnp/csn n ctl
+.BI /dev/pnp/csn n raw
+\&...
+
+.fi
+.SH DESCRIPTION
+This device provides a limited interface to the PCI bus and
+Plug 'n' Play ISA devices.
+Both are present on most PC platforms; on others, only the
+.B pci
+interface is present (because there is no ISA bus).
+.SS PCI Interface
+.PP
+PCI devices are addressed logically by a bus number,
+a device number on that bus, and a function number
+within the device.
+The set of all such device functions may be enumerated
+by traversing the
+.B /dev/pci
+directory; the driver serves two files for each function.
+These are a control file
+.RL ( /dev/pci/\fIbus\fP.\fIdev\fP.\fIfn\fPctl )
+which may be read for a textual summary of the device function,
+and a `raw' file
+.RL ( /dev/pci/\fIbus\fP.\fIdev\fP.\fIfn\fPraw )
+which may be read to obtain the raw contents of PCI configuration space.
+.PP
+The first field of a PCI control file contains the class, sub-class and
+programming interface values for the device function, expressed
+as 2-digit hexadecimal values, and separated by periods.
+The second field yields the vendor ID and device ID, each as 4-digit
+hex numbers, separated by a slash.
+The third field is the associated interrupt line in decimal.
+The remainder of the line enumerates any valid base address
+registers for the function, using two fields for each.
+In the first field, the index of the register is followed by
+a colon, and then the value of the register itself.
+The following field gives the associated size of the memory
+(or I/O space) that is mapped by the register.
+.SS Plug 'n' Play
+.PP
+Plug 'n' Play ISA devices are discovered by sending a fixed `unlock' sequence
+over an I/O port, and then reading back data from another port.
+An arbitration algorithm is used to separate out the individual
+cards and enumerate them in turn.
+Each card is assigned a unique number, called a CSN, in the range 1-255 as a
+result of enumeration.
+Cards also have a fixed 64 bit identification number, set by the
+manufacturer, which is used by the arbitration algorithm to
+resolve conflicts.
+The first 32 bits describe the type of the card, and the second
+32 bits form a serial number for the particular instance of that card type.
+When formatted textually, it appears as 3 upper-case letters
+(typically representing the manufacturer),
+followed by 4 hex digits, then a period, then 8 hex digits.
+The substring before the period is the card type, and the substring
+after the period is the serial number.
+.PP
+The enumeration algorithm needs to be enabled by specifying the
+port number to write the unlock sequence out on.
+This can be configured to take place at boot time by adding a line
+like the following to
+.IR plan9.ini :
+.IP
+.EX
+pnp0=port=0x203
+.EE
+.PP
+Here
+.B port
+should be chosen to not conflict with any existing devices.
+It must be in the range
+.BR 0x203-0x3ff .
+Alternatively, one can use the following command:
+.IP
+.EX
+echo port 0x203 >/dev/pnp/ctl
+.EE
+.PP
+Note that a side-effect of PnP enumeration is to reset the configuration
+state of all such cards; any settings made by a Plug and Play BIOS will be lost.
+Reading the file
+.B /dev/pnp/ctl
+returns one of the strings
+.B "enabled\fI port\fP"
+or
+.BR "disabled" .
+.PP
+For each enumerated card, two files are served in
+.BR /dev/pnp .
+A control file
+.RL ( /dev/pnp/csn\fIn\fPctl )
+may be read to determine the ID of the card, and a raw file
+.RL ( /dev/pnp/csn\fIn\fPraw )
+may be read to obtain the configuration data associated with the card.
+It is intended that the control file should take commands which set the
+various configurable resources of the card, but this has not been
+implemented yet.
+.PP
+A mechanism is provided for configuring cards via
+.IR plan9.ini (10.6).
+A line of the form
+.BI pnp n = "idstring ..."
+will cause the driver to look for the card named by
+.I idstring
+and, if found, assign it the CSN
+.IR n .
+The intention is that
+any additional text after the idstring is interpreted as if it
+was written to the card's
+.B ctl
+file, but this is not yet implemented.
+.SH EXAMPLES
+.PP
+To list all PCI functions:
+.IP
+.EX
+cat /dev/pci/*ctl
+.EE
+.PP
+To find just the PCI video card (class 3):
+.IP
+.EX
+grep '^03' /dev/pci/*ctl
+.EE
+.SH SOURCE
+.B /os/pc/devpnp.c
+.br
+.B /os/port/devpci.c
+.SH BUGS
+There is currently no way to write to PCI configuration space,
+or to perform reads of less than 32 bits.
+Access to the I/O and memory regions of a PCI device is not provided.
+.PP
+The ability to set a Plug 'n' Play card's configurable settings has not been
+implemented.
+.PP
+There should be a user program for identifying and configuring
+Plug 'n' Play cards.
--- /dev/null
+++ b/man/3/pointer
@@ -1,0 +1,80 @@
+.TH POINTER 3
+.SH NAME
+pointer \- mouse and stylus interface
+.SH SYNOPSIS
+.B "bind -a #m /dev"
+.PP
+.B /dev/cursor
+.br
+.B /dev/pointer
+.SH DESCRIPTION
+The
+.I pointer
+device is the interface to input from a mouse, stylus, or some other device
+for pointing at positions on a display.
+On some platforms, the pointer is associated with a cursor
+on the display, that is displayed at the current pointer position.
+There often are buttons associated with the pointer, whether on
+the pointing device or elsewhere.
+For instance, some of the buttons on a PDA might be associated
+with the stylus not a keypad, to allow modification of stylus actions
+when there are are no buttons on the stylus itself.
+In the hosted environment of
+.IR emu (1),
+the pointer is associated with the host system's own pointing device.
+.PP
+Reading the
+.B pointer
+file returns the current pointer position and the state of any buttons associated with the pointer.
+The read blocks until the state has changed since the last read.
+Each read returns a 49 byte record:
+.IP
+.BI m "x y buttons msec"
+.PP
+Each record has the letter
+.B m
+immedlately followed by four 11-character decimal numbers, each number ended by a blank.
+.I X
+and
+.I y
+give the coordinates of the pointer in display coordinates;
+the value
+.I buttons
+has bit
+.RI "(1<<" b )
+set when button
+.I b
+is down; and
+.I msec
+is a time stamp in milliseconds.
+.PP
+Writing a record of the above form to
+.B pointer
+moves the displayed cursor to the given
+.I x
+and
+.I y
+position;
+.I buttons
+and
+.I msec
+are ignored.
+.PP
+Writing an
+.IR image (6)
+file to the
+.B cursor
+file sets the image of the cursor on the display.
+Reading
+.B cursor
+returns the current cursor image, again in
+.IR image (6)
+format.
+.SH SOURCE
+.B /emu/port/devpointer.c
+.br
+.B /os/port/devpointer.c
+.SH SEE ALSO
+.IR wm (1),
+.IR draw-intro (2),
+.IR image (6)
--- /dev/null
+++ b/man/3/prof
@@ -1,0 +1,93 @@
+.TH PROF 3
+.SH NAME
+prof \- profiling device
+.SH SYNOPSIS
+.nf
+.B bind #P /prof
+
+.BI /prof/ctl
+.BI /prof/ n /name
+.BI /prof/ n /path
+.BI /prof/ n /histogram
+.BI /prof/ n /pctl
+.fi
+.SH DESCRIPTION
+The
+.B prof
+device serves a two-level directory structure. The first level contains a control file
+.B ctl
+and zero or more numbered directories each corresponding to a module being
+profiled. Inside each of these numbered directories are further files which describe
+the particular module being profiled.
+.PP
+The write-only control file
+.B ctl
+provides the facilities to profile a module. Messages written to this file initiate and
+control the profiling.
+.TP 10n
+.BI module " name"
+Add the module or the module whose path is
+.I name
+to the list of modules to be profiled.
+.TP 10n
+.B start
+Start profiling all modules in the above list or, if the list is empty, start profiling all
+modules loaded by the kernel. The profiling is done by sampling.
+.TP 10n
+.B startcp
+As above but do coverage profiling. All instructions executed in the wanted modules
+are counted.
+.TP 10n
+.B startmp
+As above but do memory profiling. All heap memory allocations are associated
+with a line of limbo source and all deallocations with the line that did the allocation.
+.TP 10n
+.B stop
+Stop all profiling.
+.TP 10n
+.B end
+Stop all profiling and free all memory associated with the modules being profiled.
+The profiler returns to it's initial state with no modules under profile.
+.TP 10n
+.BI interval " i"
+Change the sampling interval to
+.I i
+ms. The default sampling rate is 100 ms.
+.PP
+The second level directories contain information about each module under profile.
+.PP
+The read-only file 
+.B name
+contains the name of the module in the form it appears in it's module source file.
+.PP
+The read-only file
+.B path
+contains the path of the dis file that implements the module.
+.PP
+The write-only control file
+.B pctl
+allows finer control of the profiling of a module. It is not used at present.
+.PP
+The read-only file
+.B histogram
+contains statistics about the profiled module. The file contains a list of pairs
+of integers seperated by a space. The first number of the pair is a program counter
+value representing the address of a dis instruction. Addresses start from 0 and the
+list is in order of increasing address. The
+second number is the frequency with which this address was sampled. Each read
+of this file returns the next pair in the list. The frequency is guaranteed to be non-zero.
+.PP
+When coverage profiling the second number of each pair is the number of times
+that dis instruction was executed, when memory profiling it's the amount of memory in bytes.
+.SH SOURCE
+.B /emu/port/devprof.c
+.SH BUGS
+The device profiles on a global basis and therefore does not distinguish between multiple profilers running at once. 
+.PP
+The coverage profiling can only be done on dis instructions.
+.PP
+The device can do only one of time, coverage or memory profiling at once.
+
+
+
+
--- /dev/null
+++ b/man/3/prog
@@ -1,0 +1,310 @@
+.TH PROG 3 
+.SH NAME
+prog \- running programs
+.SH SYNOPSIS
+.nf
+.B bind #p /prog
+
+.BI /prog/ n /ctl
+.BI /prog/ n /dbgctl
+.BI /prog/ n /exception
+.BI /prog/ n /fd
+.BI /prog/ n /heap
+.BI /prog/ n /ns
+.BI /prog/ n /nsgrp
+.BI /prog/ n /pgrp
+.BI /prog/ n /stack
+.BI /prog/ n /status
+.BI /prog/ n /text
+.BI /prog/ n /wait
+\&...
+.fi
+.SH DESCRIPTION
+The
+.B prog
+device serves a two-level directory structure.
+The first level contains numbered directories
+corresponding to pids of live Limbo processes;
+each such directory contains a set of files
+representing the corresponding process.
+All files operate on
+.I UTF
+(see
+.IR utf (6))
+strings.
+.PP
+The read-only
+.B status
+file contains seven space-separated fields.
+The fields are: the process and process group identifiers, each 8 characters right justified;
+the user name, at least 10 characters left justified;
+cpu time in minutes, seconds and tenths of seconds;
+the process state, 10 characters left justified;
+the amount of memory used by the process in units of 1024 bytes,
+at least 5 characters, right justified, followed by a
+.BR K ;
+and the name of the current program module,
+up to 28 characters, left justified.
+.PP
+The read-only
+.B pgrp
+file contains the process group identifier, in decimal.
+.PP
+The read-only
+.B nsgrp
+file contains the namespace group identifier, in decimal.
+.PP
+The read-only
+.B ns
+file contains a set of mount and bind commands
+which describe the
+.IR sys-bind (2)
+and
+.B mount
+operations used to construct the name space, in
+the format of
+.IR namespace (6).
+The last
+line of the file is a
+.B cd
+command indicating the working directory.
+.PP
+The read-only
+.B wait
+file may be read to recover
+information about the exiting children of the process.
+A read of
+.B wait
+will block until a child of the process, created after
+.B wait
+was opened, exits.
+When such a child exits, it produces a string with three fields:
+the pid of the exiting process, a space,
+module name enclosed in
+\ \f5"\fP's,
+a colon,
+and a possibly empty error message.
+The error message will contain at most 64 characters.
+.PP
+The read-only
+.B fd
+file
+describes the open file descriptors in the
+file descriptor group of the process.
+Each line describes an open file.
+The fields are: the file descriptor index, the open mode
+.RB ( r ,
+.BR w ,
+.BR rw );
+the type and number of the device;
+the path, version and type of the file's
+.I qid
+(see
+.IR intro (5));
+the file's atomic I/O unit, as defined in
+.IR sys-iounit (2));
+the file I/O offset in bytes;
+and the name with which it was opened.
+.PP
+The read-only
+.B exception
+file gives details of the last exception to occur in the process, if any. The fields
+are the program counter value when the exception occurred, the module it
+occurred in and the exception itself, each separated by a space. If none, the
+result is the empty string.
+.PP
+Messages written to the
+.B ctl
+file control the execution of the process.
+.TF killgrp
+.PD
+.TP
+.B kill
+Kill the process.
+.TP
+.B killgrp
+Kill all processes in the same group as the process.
+A process writing to its own ctl file does not kill itself.
+.TP
+.B exceptions propagate
+Applies to process group leaders only (ie any process that executes a system
+call
+.I sys->pctl(Sys->NEWPGRP, ...
+).
+If any process in the same group as the leader incurs an exception which it
+does not handle, atomically raise the exception in all processes in the group.
+In this case exceptions are generated for killed processes as well. This mechanism allows
+all processes in a process group to perfom error recovery when one of them
+faults.
+.TP
+.B exceptions notifyleader
+Applies to process group leaders only.
+If any process in the same group as the leader incurs an exception which it
+does not handle, atomically destroy all processes in the group except for the
+leader and raise the exception in the leader. This error recovery mechanism might
+be appropriate when a fault occurs amongst a group of processes and the group
+leader takes sole responsibilty for recovery.
+.TP
+.B restricted
+Mark all processes that the process spawns in future as restricted. A restricted
+process is one which can run out of memory when a configured limit has been
+reached and before total memory is exhausted. An unrestricted process can
+allocate memory until memory is truly exhausted. Typically a window manager
+or server might be unrestricted as they are processes fundamental to the
+running of a system.
+.PP
+The
+.B dbgctl
+file provides facilities for debugging a process.
+Messages written to the file control the execution of the process.
+.TP 10n
+.BI step " n"
+Step the interpreter for at most
+.I n
+instructions, or until a breakpoint is reached.
+.TP 10n
+.B toret
+Step the interpreter until a return from the current activation frame
+or a breakpoint is reached.
+.TP 10n
+.B cont
+Step the interpreter until a breakpoint is reached.
+.TP 10n
+.B stop
+Stop the process as soon as possible.
+Do not allow the process to execute again until an
+.B unstop
+message is received.
+.TP 10n
+.B unstop
+Cancel the effect of any previous
+.BR stop .
+.TP 10n
+.BI "bpt set " "path pc"
+Set a breakpoint at
+.I pc
+for the module given by
+.IR path .
+.TP 10n
+.BI "bpt del " "path pc"
+Clear a breakpoint if one exists.
+.PP
+Reading 
+.B
+dbgctl
+gives updates for some state transitions while the process is being debugged.
+Each update is terminated by a newline.
+.TP 10n
+.B exited
+The process exited without error.
+.TP 10n
+.BI broken: " error"
+The process died due to
+.IR error ,
+a string with up to 64 characters.
+.TP 10n
+.B send
+The process is blocked sending on a channel.
+.TP 10n
+.B recv
+The process is blocked receiving on a channel.
+.TP 10n
+.B alt
+The process is blocked in an
+.B alt
+statement.
+.TP 10n
+.B run
+The process is unblocked and now ready to run.
+.TP 10n
+.BI new " pid"
+The process has spawned a new process identified by
+.IR pid .
+.PP
+The read-only
+.B stack
+file contains the dynamic call stack trace.
+Each activation frame is described by one line with six fields, separated by a space:
+the frame pointer, program counter,
+module data pointer, and module code pointer,
+each 8 hexadecimal digits;
+the execution method for the module (0 means interpreted, 1 compiled);
+and the path name of the module.
+The top activation frame starts at offset 0.
+.PP
+The
+.B heap
+file may be queried to examine the state of the process.
+A data query contains an address, a period, a format character,
+and a count.
+An instruction query contains a pc, a plus, a mode address, a period,
+the format
+.BR I ,
+and a count.
+The addresses in the query may be decimal,
+hexadecimal preceded by
+.B 0x
+or
+.BR 0X ,
+or octal preceded by
+.BR 0 .
+Count gives the number of consecutive
+data items retrieved by reading
+.B heap
+starting at offset 0;
+the format varies according to the format character.
+All data items other than strings are terminated by a newline.
+.TP 10n
+.B W
+32-bit decimal ints.
+.TP 10n
+.B B
+8-bit unsigned decimal bytes.
+.TP 10n
+.B V
+64-bit decimal bytes.
+.TP 10n
+.B R
+64-bit reals.
+.TP 10n
+.B I
+Disassembled Dis instructions.
+.TP 10n
+.B P
+32-bit hexadecimal address, or
+.BR nil .
+.PP
+The following formats examine properties of specific 32-bit pointers.
+.TP 10n
+.B L
+Examine a list, yielding
+a pair of hexadecimal addresses separated by a period,
+giving the address of the head and tail of a list.
+It is an error to use
+.B L
+on
+.BR nil .
+.TP 10n
+.B A
+Examine an array, yielding
+a decimal length, a period, and the address of the 0th element of an array,
+or
+.BR nil .
+.TP 10n
+.B C
+Examine a string, yielding
+the decimal length in characters, a period, and the
+.IR utf (6)
+representation of the string.
+.TP 10n
+.B M
+Examine a module reference, yielding the address of its global data or
+.BR nil .
+.PP
+The
+.B text
+file is currently unimplemented.
+.SH SOURCE
+.B /emu/port/devprog.c
+.br
+.B /os/port/devprog.c
--- /dev/null
+++ b/man/3/root
@@ -1,0 +1,54 @@
+.TH ROOT 3 
+.SH NAME
+root \- the root file system
+.SH SYNOPSIS
+.nf
+.B /
+.B /chan
+.B /dev
+.B /env
+.B /fd
+.B /net
+.B /net.alt
+.B /nvfs
+.B /prog
+.B /root
+.B /srv
+.B /osinit.dis
+.fi
+.SH DESCRIPTION
+This device is set up by the kernel to be the initial root of
+the name space.
+In the emulation environment,
+the names in the one-level tree shown above are mostly just place-holders,
+to allow a place on which to
+.I bind
+(see
+.IR sys-bind (2)).
+.PP
+In the native environment, an arbitrary initial directory structure and
+file content can be built-in
+when the kernel is configured, but it will typically include at least
+the names above, to act as mount points.
+(See the discussion of the `root section' in
+.IR conf (10.6).)
+The required file
+.B /osinit.dis
+(not present in the emulation environment)
+provides an executable Dis binary when read,
+and is the first program invoked when initialising the system.
+Other files needed for bootstrap
+might also reside in this device.
+.PP
+Its local name
+.B #/
+can be used by programs outside the kernel, for instance to implement
+.B newns
+in
+.IR newns (2).
+.SH SOURCE
+.B /emu/port/devroot.c
+.br
+.B /os/port/devroot.c
+.SH SEE ALSO
+.IR init (8)
--- /dev/null
+++ b/man/3/rtc
@@ -1,0 +1,60 @@
+.TH RTC 3
+.SH NAME
+rtc \- real-time clock and non-volatile memory
+.SH SYNOPSIS
+.B bind -b '#r' /dev
+.PP
+.B /dev/rtc
+.br
+.B /dev/rtcid
+.br
+.B /dev/nvram
+.SH DESCRIPTION
+The
+.I rtc
+device provides access to the real-time clock and any associated non-volatile memory.
+Physical devices supported include the Mostek MK48T12-15 Zeropower/Timekeeper,
+the Dallas Semiconductor DS1687 real-time clock,
+and the IBM PC real-time clock on various platforms.
+.PP
+The
+.B rtc
+file when read returns the time as a decimal number, expressed as the number of seconds since the epoch,
+1 January 1970 00:00 GMT.
+The clock is set by writing the desired number of seconds since the epoch to the file.
+Setting the time in this device has no effect on system time returned by
+.B /dev/time
+(see
+.IR cons (3)),
+which must be set separately if desired.
+For example, when
+.I rtc
+is available
+.I osinit
+(see
+.IR init (8))
+uses it to set
+.BR /dev/time .
+.PP
+The
+.B nvram
+file provides access to the non-volatile memory commonly implemented
+by these clock chips.
+There are no cross-platform standards for the range of addresses that
+can be safely used by Inferno, or for the content and format of the data.
+.PP
+The
+.B rtcid
+file holds a decimal number giving the serial number of the device,
+set during manufacturing.
+The file exists only if a particular device provides such a number.
+.SH SOURCE
+.B /os/*/devrtc.c
+.SH SEE ALSO
+.IR cons (3)
+.SH BUGS
+The
+.B rtc
+file returns seconds but
+.B /dev/time
+returns microseconds: it's history.
--- /dev/null
+++ b/man/3/sd
@@ -1,0 +1,194 @@
+.TH SD 3 
+.SH NAME
+sd \- storage device interface
+.SH SYNOPSIS
+.nf
+.B bind #S /dev
+
+.BI /dev/sd Cu /ctl
+.BI /dev/sd Cu /raw
+.BI /dev/sd Cu /data
+\&...
+.fi
+.SH DESCRIPTION
+The storage device interface serves a two-level directory
+giving access to multiple storage units,
+typically ATA(PI) or SCSI discs.
+Each unit
+is accessed via files in the directory named by the controller
+to which it is attached,
+.IR C  ,
+and by its unit number
+.IR u .
+The controller naming convention for ATA(PI) units starts
+with the first controller being named
+.LR C ,
+the second
+.LR D ,
+etc. up to a maximum of 4 controllers
+.RB ([ C-F ]);
+legacy controllers are always 'C' and 'D'.
+There can be a maximum of 2 units per ATA(PI) controller
+.RB ([ 01 ]).
+The controller naming convention for SCSI units starts with
+the first controller being named
+.LR 0 ,
+the second
+.LR 1 ,
+etc. up to a maximum of 16 controllers
+.RB ([ 0-9a-f ]).
+There can be a maximum of 16 units per SCSI controller
+.RB ([ 0-9a-f ]).
+.PP
+Units are not accessed before the first attach.
+Units may be individually attached using the attach specifier,
+for example
+.IP
+.EX
+bind -a '#SsdD0' /dev
+.EE
+.PP
+An attach without a specifier will cause the driver to scan for all possible
+units before processing the rest of the name.
+.PP
+The subdirectory for each unit contains two files,
+.I ctl
+and
+.IR raw .
+In addition,
+if the unit is a direct-access disc of some type
+it may be split into partitions and
+the subdirectory may contain a file per partition.
+By default,
+the partition
+.I data
+will exist for such media.
+.PP
+Partitions are added and deleted by writing to the
+.I ctl
+file
+.IP
+.EX
+part \f2name start-sector end-sector\fP
+delpart \f2name\fP
+.EE
+.PP
+The default
+.I data
+partition may be deleted.
+A partition cannot be deleted if a process has it open.
+If a change of removable media is detected,
+the new media cannot be opened until all open partitions
+on the old media are closed.
+.PP
+Reading the
+.I ctl
+file returns at least one line of textual information about
+the unit.
+The first line will always be prefixed by
+.B inquiry
+and will give a manufacturer and model number if possible.
+A line prefixed by
+.B config
+will be returned for appropriate media,
+e.g. for ATA(PI) units the remainder of the line contains
+configuration information from the device's
+.I identify
+command (config and capabilities)
+and also the available I/O transfer options;
+this is a diagnostic aid.
+A line prefixed by
+.B geometry
+will be returned for appropriate media;
+at least two numbers will follow,
+the first being the number of sectors contained in the unit
+and the second the sector size in bytes.
+Any remaining information on the
+.B geometry
+line is unit-dependent,
+for instance, head,
+cylinder and sector counts for ATA discs.
+If any partitions are defined for the media,
+their name, start-sector and end-sector will be returned,
+prefixed by
+.BR part .
+.IP
+.EX
+% cat /dev/sdD0/ctl
+inquiry KENWOOD CD-ROM UCR-421  208E10/20/99  7.39  2 M0
+config 85C0 capabilities 0F00 dma 00550004 dmactl 00000000
+geometry 242725 2352
+part data 0 242725
+% 
+.EE
+.PP
+The use of DMA and multi-sector read/write commands may be
+enabled and disabled on ATA(PI) units by writing to the
+.B ctl
+file
+.B dma
+and
+.B rwm
+respectively followed by
+.B on
+or
+.BR off .
+For example, to enable DMA on a unit that supports it:
+.IP
+.EX
+% echo 'dma on'>/dev/sd00/ctl
+.EE
+.PP
+If supported by the unit,
+the standby timer may be enabled:
+.IP
+.EX
+% echo 'standby \f2T\fP'>/dev/sdC0/ctl
+.EE
+.PP
+where
+.I T
+is the standby timer period in seconds.
+.I T
+must be between 30 and 1200,
+or can be 0 to disable the timer.
+.PP
+The
+.B raw
+file is used to execute an arbitrary command on the unit at
+a low level.
+This is used by programs
+to manipulate devices that do not fit the simple storage model
+or using special commands for maintenance purposes.
+The following steps may be used to execute a command
+.IP
+\- Write the command to the
+.I raw
+file;
+.IP
+\- Read or write data associated with the command, according to the direction of the transfer.
+.IP
+\- Read the
+.I raw
+file to retrieve the status of the command,
+returned as a text integer.
+.SH SOURCE
+.B /os/port/devsd.c
+.br
+.B /os/*/sd*.[hc]
+.SH SEE ALSO
+.IR ds (3),
+.IR floppy (3)
+.SH BUGS
+.PP
+Still in development.
+.PP
+No LUNs.
+.PP
+The 4 controller limit for ATA(PI) is not enforced.
+.PP
+No account is taken of some buggy ATA PCI controllers
+such as the CMD640.
+.PP
+ATA(PI) units come up with DMA and multi-sector read/write
+capability disabled.
--- /dev/null
+++ b/man/3/sign
@@ -1,0 +1,92 @@
+.TH SIGN 3
+.SH NAME
+sign \- control use of signed modules
+.SH SYNOPSIS
+.B "bind -a #Σ /dev"
+.PP
+.B /dev/signerkey
+.br
+.B /dev/signctl
+.SH DESCRIPTION
+.I Sign
+is a device, still experimental, to
+control the use of signed Dis modules.
+After system initialisation, any process can load any module.
+Using
+.IR sign ,
+.B load
+operations can subsequently be restricted for a process and its descendents.
+.PP
+.B Signerkey
+is a file that can be opened for writing only  by the host owner
+(see
+.IR eve (10.2))
+(but any process holding the file descriptor can write to it).
+The data written must be the textual representation of a public key in the form produced by
+.B Keyring->pktostr
+(see
+.IR keyring-certtostr (2)).
+After a successful write, subsequent
+.B load
+operations will be limited to Dis modules in any of the following sets:
+.IP •
+built-in modules
+.IP •
+unsigned modules found in the kernel's
+.IR root (3)
+file system
+.IP •
+currently loaded unsigned modules
+.IP •
+acceptable signed modules
+.PP
+A signed Dis module contains a signature in its header, as specified by
+.IR dis (6).
+The signature contains the result of signing the
+remaining data in the file (or more precisely, a cryptographically secure hash of it), using
+a configured public key algorithm and the signer's secret key
+(for instance using
+.BR Keyring->sign ,
+see
+.IR keyring-sha1 (2)).
+.PP
+A signed module is `acceptable' if it was signed by the secret key corresponding to one
+of the public keys written to
+.BR signerkey .
+There can be up to 8 such keys.
+The set of keys and the secured status is shared across
+.BR spawn .
+.PP
+.B Signerkey
+is generally readable, and when read yields a list of the keys installed, one per line,
+showing
+.BR owner ,
+.BR alg ,
+and other attributes in
+.IB attr = value
+format, space separated.
+The actual key value is not currently shown.
+.PP
+.B Signctl
+can be read or written only by the host owner.
+Each write contains a textual control request.
+(Currently there are none such.)
+If security is not enabled, it is empty when read.
+Otherwise, it contains the number of keys loaded, as a decimal integer.
+.SH SOURCE
+.B /emu/port/devsign.c
+.br
+.B /os/port/devsign.c
+.SH SEE ALSO
+.B wm/rt
+in
+.IR wm-misc (2),
+.IR sys-pctl (2),
+.IR dis (6),
+.IR createsignerkey (8),
+.IR eve (10.2)
+.SH DIAGNOSTICS
+If the text is not a valid public key or uses an algorithm that is not configured,
+a write to
+.B signer
+fails and sets the error string.
--- /dev/null
+++ b/man/3/snarf
@@ -1,0 +1,29 @@
+.TH SNARF 3 hosted
+.SH NAME
+snarf \- host window system clipboard
+.SH SYNOPSIS
+.B bind -a #^ /dev
+.br
+.B bind -b #^ /chan
+.PP
+.B /dev/snarf
+.br
+.B /chan/snarf
+.SH DESCRIPTION
+.I Snarf
+serves a single file,
+.BR snarf .
+Reading the file returns the current
+contents of the host window system's own clipboard (equivalent to
+the Inferno window system's `snarf buffer').
+If the file is opened for writing, subsequent writes append to an internal buffer;
+when the file is closed, that buffer's contents are written to the host window system's clipboard.
+.PP
+.IR Emu (1)
+automatically binds the device to both
+.B /dev
+and
+.B /chan
+when it starts up, if a window system is configured.
+.SH SEE ALSO
+.IR wm (1)
--- /dev/null
+++ b/man/3/srv
@@ -1,0 +1,86 @@
+.TH SRV 3
+.SH NAME
+srv \- server registry
+.SH SYNOPSIS
+.BI "bind #s"
+[
+.I spec
+]
+.B /chan
+.PP
+.EX
+sys->bind("#s\fIspec\fP", "/chan", Sys->MREPL);
+.EE
+.SH DESCRIPTION
+.I Srv
+converts between file system operations by applications
+and messages on channels, as described in
+.IR sys-file2chan (2).
+Each attach that does not include a
+.I spec
+produces a new instance: an empty directory owned
+by the current user in which only files
+may be created, and then only by
+.IR sys-file2chan ,
+using a special internal interface.
+Each such file initially has the same owner as the
+directory, and permissions
+.B 8r600
+(read-write for the owner only),
+but the permissions can be changed by
+.BR Sys->wstat
+(see
+.IR sys-stat (2))
+and thus
+.IR chmod (1).
+If mode
+.B Sys->DMEXCL
+is thereby set, the file becomes exclusive-use and can be opened by
+only one process at a time.
+The file length and
+mode bit
+.B Sys->DMAPPEND
+may also be set by
+.B wstat
+but are not interpreted by the system.
+.PP
+Files may be removed, directly using
+.IR sys-remove (2)
+(and thus
+.IR rm (1)),
+or indirectly by opening them with mode
+.B Sys->ORCLOSE
+(see
+.IR sys-open (2)).
+.I File2chan
+also removes the corresponding file when the last references
+to the server's read and write channels have gone
+(eg, on server exit).
+.PP
+A
+.I spec
+may be given to name specific instances of
+.IR srv ,
+so that a new name space can bind in a service created in another.
+Only the owner (original creator) of the instance may attach to it,
+unless the mode of the service directory is changed
+using
+.B Sys->wstat
+(see
+.IR sys-stat (2))
+to have general access.
+.SH FILES
+.TF /chan
+.TP
+.B /chan
+directory to which
+.I srv
+is conventionally bound by various applications
+.SH SOURCE
+.B /emu/port/devsrv.c
+.br
+.B /os/port/devsrv.c
+.SH SEE ALSO
+.IR plumb (1),
+.IR wm (1),
+.IR sys-file2chan (2)
--- /dev/null
+++ b/man/3/srv9
@@ -1,0 +1,108 @@
+.TH SRV9 3 "Plan 9"
+.SH NAME
+srv9 \- plan 9 server registry
+.SH SYNOPSIS
+.nf
+.B bind -c #₪ /srv
+
+.BI /srv/ service1
+.BI /srv/ service2
+ ...
+.fi
+.SH DESCRIPTION
+On Inferno hosted on Plan 9,
+.I srv9
+serves a one-level directory that gives Inferno applications
+direct access to Plan 9 services posted in its
+.B #s
+server registry,
+and allows Inferno applications to post services for access by Plan 9 applications.
+A service is represented by a file descriptor, usually serving the common file service
+protocol described in
+.IR intro (5),
+allowing it to be mounted (see
+.IR bind (1)
+and
+.IR sys-bind (2))
+in the name space of an application in the other system.
+.PP
+To access a Plan 9 service in Inferno, open the desired service file;
+the resulting file descriptor is connected to the associated Plan 9 service.
+When that service is a shared Plan 9 file service,
+.I srv9
+automatically starts Plan 9's
+.I exportfs
+with appropriate options to make the service accessible from Inferno.
+It can safely be shared with Plan 9 applications, but note that any path names
+mentioned in the attach specifier (see
+.IR mount (1)
+and
+.IR attach (5))
+will be in the Plan 9 application's name space, not the Inferno application's.
+(Indeed, a similar caveat applies in Plan 9 itself.)
+.PP
+To export an Inferno service to Plan 9, create a new file such as
+.BR /srv/myserv
+using
+.B Sys->create
+with mode
+.B Sys->ORDWR
+(see
+.IR sys-open (2))
+and any desired permissions for the new service file.
+(Note that the
+.B #₪
+device must have been bound in with the
+.B -c
+option, as shown above, to allow file creation.)
+If the create is successful, Plan 9's service registry will have a new entry
+.BR myserv ,
+and the file descriptor returned from
+.B create
+acts as a pipe to any Plan 9 application
+that opens the associated service file in Plan 9.
+Typically on the Inferno side the file descriptor is passed to
+.IR sys-export (2)
+or made the standard input of an Inferno file service
+(see
+.IR intro (4)).
+The Plan 9 service file is automatically removed when the Inferno file descriptor is no longer referenced.
+.SH EXAMPLES
+To make Plan 9's factotum available in Inferno:
+.IP
+.EX
+mount -Aa /srv/factotum /mnt
+.EE
+.PP
+(Note the
+.B -A
+option to suppress Inferno authentication.)
+.PP
+To mount the Plan 9 file service from which a Plan 9 machine booted:
+.IP
+.EX
+mount -9 /srv/boot /n/local
+.EE
+.PP
+The
+.B -9
+option to
+.IR mount (1)
+requests Plan 9 authentication; that could also have been used instead of
+.B -A
+in the previous example.
+.PP
+To make the environment variables of the current Inferno name space available to Plan 9:
+.IP
+.EX
+fd := sys->create("/srv/infenv", Sys->ORDWR, 8r600);
+sys->export(fd, "/env", Sys->EXPWAIT);
+.EE
+.SH SOURCE
+.B /emu/Plan9/devsrv9.c
+.SH SEE ALSO
+.IR sys-bind (2),
+.IR sys-open (2),
+.IR sys-export (2),
+.IR import (4),
+.IR 9srvfs (4)
--- /dev/null
+++ b/man/3/ssl
@@ -1,0 +1,145 @@
+.TH SSL 3
+.SH NAME
+ssl \- secure sockets layer device
+.SH SYNOPSIS
+.B #D/clone
+.br
+.BI #D/ n
+.br
+.BI #D/ n /data
+.br
+.BI #D/ n /ctl
+.br
+.BI #D/ n /secretin
+.br
+.BI #D/ n /secretout
+.br
+.BI #D/ n /encalgs
+.br
+.BI #D/ n /hashalgs
+.SH DESCRIPTION
+The
+.I ssl
+device provides access to a Secure Socket Layer that implements the record layer protocol
+of SSLv2
+(but not its handshaking).
+The device provides encrypting and digesting for many independent connections.
+Once associated with a network connection, the
+.I ssl
+device can be thought of as a filter for the connection.
+.I Ssl
+can send data in the clear, digested or encrypted. In all cases, if
+.I ssl
+is associated with both ends of a connection, all messages are delimited.
+As long as reads always specify buffers that are of equal or greater lengths than the writes at the other end of the connection, one write will correspond to one read.
+The device is unusual because it is not bound into the name space but named directly by its local name,
+.BR #D .
+That is because the interface described below requires writing a local file descriptor number to a file,
+which will not work remotely.
+.PP
+The top-level directory contains a
+.B clone
+file and numbered directories, each representing a connection.
+Opening the
+.B clone
+file reserves a connection; the file descriptor resulting from the
+.IR \%sys-open (2)
+will be open on the control file,
+.BR ctl ,
+in the directory that represents the new connection.
+Reading the control file will return a text string giving the connection number
+.IR n ,
+and thus the directory name.
+.PP
+Writing to
+.B ctl
+controls the corresponding connection.
+The following control messages are possible:
+.TP
+.BI fd " m"
+Associate the network connection on existing file descriptor
+.I m
+with the
+.I ssl
+device.
+.TP
+.B alg clear
+Allow data to pass in the clear with only message delimiters added. The device starts in this mode.
+.TP
+.B alg sha
+Append a SHA digest to each buffer written to
+.BR data .
+The digest covers the outgoing secret (written to
+.BR secretout ),
+the message, and a message number which starts at 0 and increments by one for each message.
+Messages read have their appended digests compared to a digest computed using the incoming secret (written to
+.BR secretin ).
+If the comparison fails, so will the read.
+.TP
+.B alg md4
+Like
+.B sha
+but using the MD4 message digest algorithm.
+.TP
+.B alg md5
+Like
+.B sha
+but using the MD5 message digest algorithm.
+.TP
+.B alg rc4
+.PD0
+.TP
+.B alg rc4_40
+.TP
+.B alg rc4_128
+.TP
+.B alg rc4_256
+RC4 encrypt each message written to
+.B data
+with the key written to
+.BR secretout ,
+using the key length as indicated (40-bit keys by default).
+.PD
+.TP
+.B alg des_56_cbc
+Encrypt the stream using DES and Cipher Block Chaining (CBC)
+.TP
+.B alg des_56_ecb
+Encrypt the stream using DES and Electronic Code Book (ECB)
+.TP
+.B alg ideacbc
+Encrypt the stream using IDEA and CBC
+.TP
+.B alg ideaecb
+Encrypt the stream using IDEA and ECB
+.TP
+.BI alg " digest" / crypt
+Combine the use of the given
+.I digest
+algorithm and the stream encryption algorithm
+.IR crypt
+.PP
+Files
+.B secretin
+and
+.B secretout
+must be written before digesting or encryption is turned on. If only one is written, they are both assumed to be the same.
+.PP
+The mode may be changed at any time during a connection.
+.PP
+The list of algorithms supported by a given implementation of
+.I ssl
+may be read from the read-only text files
+.B encalgs
+(encryption algorithms)
+and
+.B hashalgs
+(hashing algorithms for digests).
+Each contains a space-separated list of algorithm names.
+.PP
+.SH "SEE ALSO"
+.IR security-ssl (2)
+.br
+B. Schneier,
+.IR "Applied Cryptography ,
+1996, J. Wiley & Sons, Inc.
--- /dev/null
+++ b/man/3/switch
@@ -1,0 +1,28 @@
+.TH SWITCH 3 MPC8xx
+.SH NAME
+switch \- hardware option switch
+.SH SYNOPSYS
+.nf
+.B bind -a '#r' /dev
+
+.B /dev/switch
+.fi
+.SH DESCRIPTION
+.PP
+The switch device serves a one-level directory,
+giving access to one read-only file,
+.BR switch .
+A read returns the value of the development board's
+option switch (DS1), represented as a decimal number
+from 0 to 15, representing four bits,
+each corresponding to one of the four toggles.
+Each toggle produces the value 0 when it
+is set `ON' and 1 when it is off.
+Toggle 1 corresponds to bit
+.B 1<<3
+in the number,
+toggle 2 corresponds to bit
+.BR 1<<2 ,
+and so on
+.SH SOURCE
+.B /os/mpc/devrtc.c
--- /dev/null
+++ b/man/3/tinyfs
@@ -1,0 +1,45 @@
+.TH TINYFS 3
+.SH NAME
+tinyfs \- file system for miniscule devices
+.SH SYNOPSIS
+.EX
+bind -c #F\fIname\fP /nvfs
+.EE
+.SH DESCRIPTION
+.I Tinyfs
+provides file system access to the contents of low-capacity devices,
+ranging from several hundred bytes (at least 144 bytes) to at most the order of a kilobyte.
+It is provided to support file system access to small non-volatile memories,
+as for instance are found in some real-time clock chips,
+where IDs, keys, PINs, certificates and the like might be stored by either client or server.
+.PP
+The file system has only one directory, its root, which can contain only files.
+Once created, a write can only append to a file; random updates are not allowed,
+although the file could be truncated and rewritten.
+.PP
+The device specifier following the
+.B #F
+device name is the
+.I name
+of a file in
+.B /dev
+on which the tiny file system will live.
+For instance,
+.B #Fnvram
+refers to
+.BR /dev/nvram .
+The device must allow seek and write.
+During the
+.IR attach ,
+the system scans the device, checking the file system structure
+by building a table of files, and checking a checksum stored
+in each block; inconsistent structure is reinitialised, and thus
+a previously unused device will emerge correctly formatted as an empty tiny file system.
+.SH FILES
+.TF /nvfs
+.TP
+.B /nvfs
+conventional mount point used by
+.IR init (8)
+.SH SOURCE
+.B /os/port/devtinyfs.c
--- /dev/null
+++ b/man/3/tls
@@ -1,0 +1,276 @@
+.TH TLS 3 
+.SH NAME
+tls \- TLS1 and SSL3 record layer
+.SH SYNOPSIS
+.nf
+.B bind -a #a /net
+
+.B /net/tls/clone
+.B /net/tls/encalgs
+.B /net/tls/hashalgs
+.BI /net/tls/ n 
+.BI /net/tls/ n /ctl
+.BI /net/tls/ n /data
+.BI /net/tls/ n /hand
+.BI /net/tls/ n /stats
+.BI /net/tls/ n /status
+.fi
+.SH DESCRIPTION
+The TLS device implements the record layer protocols
+of Transport Layer Security version 1.0 and Secure Sockets Layer version 3.0.
+It does not implement the handshake protocols, which are responsible for
+mutual authentication and key exchange.
+The
+.I tls
+device can be thought of as filters providing optional encryption and anti-tampering.
+.PP
+The top level directory contains a
+.B clone
+file and subdirectories numbered from zero through at least the last active filter.
+Opening the
+.B clone
+file reserves a filter.
+The file descriptor returned from the
+.IR sys-open (2)
+will point to the control file,
+.BR ctl ,
+of the newly allocated filter.
+Reading the
+.B ctl
+file returns a text string containing the number of the filter directory.
+.PP
+The filter initially cannot be used to pass messages
+and will not encrypt or digest messages.
+It is configured and controlled by writing commands to
+.BR ctl .
+.PP
+The following commands are supported:
+.TP
+.BI fd \ open-fd\ vers
+Pass record messages over the communications channel
+.IR open-fd .
+Initially, outgoing messages use version
+.I vers
+format records, but incoming messages of either version are accepted.
+Valid versions are
+.B 0x300
+for SSLv3.0 and
+.B 0x301
+for TLSv1.0 (which could be known as SSLv3.01.)
+This command must be issued before any other command
+and before reading or writing any messages;
+it may only be executed once.
+.TP
+.BI version \ vers
+Use
+.I vers
+format records for all future records,
+both outgoing and incoming.
+This command may only be executed once.
+.TP
+.BI secret \ hashalg\ encalg\ isclient\ secretdata
+Set up the digesting and encryption algorithms and secrets.
+.I Hashalg
+and
+.I encalg
+must be algorithm names returned by the corresponding files.
+.I Secretdata
+is the base-64 encoded (see
+.IR encode (2))
+secret data used for the algorithms.
+It must contain at least enough data to populate the
+secrets for digesting and encrypting.
+These secrets are divided into three categories: digest secrets, keys, and initialization vectors.
+The secrets are packed in this order, with no extra padding.
+Within each category, the secret for data traveling from the client to the server comes first.
+The incoming and outgoing secrets are automatically selected by devtls based on the
+.I isclient
+argument, which must be non-zero for the client of the TLS handshake,
+and zero for the server.
+.br
+This command must be issued after
+.BR version ,
+and may be issued more than once.
+At least one new
+.I secret
+command must be issued before each
+.I changecipher
+command; similarly, at least one new
+.I secret command
+must precede each incoming changecipher message.
+.TP
+.BI changecipher
+Enable outgoing encryption and digesting as configured by the previous
+.I secret
+command.
+This command sends a
+.I changecipher
+message.
+.TP
+.BI opened
+Enable data messages.
+This command may be issued any number of times,
+although only the first is significant.
+It must follow at least one successful
+.I changecipher
+command.
+.TP
+.BI alert \ alertno
+Send an alert message.
+.I Alertno
+may be a valid alert code for either SSLv3.0 or TLSv1.0,
+and is mapped to an appropriate code for the protocol in use.
+If it is a fatal alert, the filter is set into an error state.
+.PP
+Application messages and handshake messages are communicated using
+.I data
+and
+.IR hand ,
+respectively.
+Only one
+.IR sys-open (2)
+of
+.I hand
+is allowed at a time.
+.PP
+Any record layer headers and trailers are inserted and
+stripped automatically, and are not visible from the outside.
+The device tries to synchronize record boundaries with reads and writes.
+Each read will return data from exactly one record,
+and will return all of the data from the record as long as
+the buffer is big enough.
+Each write will be converted into an integral number of records,
+with all but potentially the last being maximal size.
+The maximum record length supported is 16384 bytes.
+This behavior is not specified in the protocols,
+and may not be followed by other implementations.
+.PP
+If a fatal alert message is received, or a fatal
+.I alert
+command issued, the filter is set into an error state.
+All further correspondence is halted,
+although some pending operations may not be terminated.
+Operations on
+.I data
+will fail with a
+.BR "'tls error'" ,
+and operations on
+.I hand
+will fail with a textual decoding of the alert.
+The current non-fatal alert messages are
+.BR "'close notify'" ,
+.BR "'no renegotiation'" ,
+and
+.BR "'handshake canceled by user'" .
+Receipt of one of these alerts cause the next read on
+.I hand
+to terminate with an error.
+If the alert is
+.BR "'close notify'",
+all future reads will terminate with a
+.B "tls hungup"
+error.
+A
+.B "'close notify'"
+.I alert
+command will terminate all future writes or reads from
+.I data
+with a
+.B "'tls hungup'"
+error.
+.PP
+If an error is encountered while reading or writing
+the underlying communications channel, the error is returned
+to the offending operation.
+If the error is not
+.BR "'interrupted'" ,
+the filter is set into the error state.
+In this case, all future operations on
+.I hand
+will fail with a
+.BR "'channel error'" .
+.PP
+When all file descriptors for a filter have been closed,
+the session is terminated and the filter reclaimed for future use.
+A
+.B "'close notify'"
+alert will be sent on the underlying communications channel
+unless one has already been sent or the filter is in the error state.
+.PP
+Reading
+.I stats
+or
+.I status
+returns information about the filter.
+Each datum is returned on a single line of the form
+.IB tag ": " data .
+.I Stats
+returns the number of bytes communicated by the
+.B data
+and
+.B hand
+channels.
+The four lines returned are tagged by, in order,
+.BR DataIn ,
+.BR DataOut ,
+.BR HandIn ,
+and
+.BR HandOut .
+.I Status
+returns lines following tags:
+.BR State ,
+.BR Version ,
+.BR EncIn ,
+.BR HashIn ,
+.BR NewEncIn ,
+.BR NewHashIn ,
+.BR EncOut ,
+.BR HashOut ,
+.BR NewEncOut ,
+and
+.BR NewHashOut .
+.BR State 's
+value is a string describing the status of the connection,
+and is one of the following:
+.BR 'Handshaking' ,
+.BR 'Established' ,
+.BR 'RemoteClosed' ,
+.BR 'LocalClosed' ,
+.BR 'Alerting' ,
+.BR 'Errored' ,
+or
+.BR 'Closed' .
+.BR Version 's
+give the hexadecimal record layer version in use.
+The
+.B Enc
+and
+.B Hash
+fields return name of the current algorithms in use
+or ready to be used, if any.
+.PP
+Reading
+.I encalgs
+and 
+.I hashalgs
+will give the space-separated list of algorithms implemented.
+This will always include
+.BR clear ,
+meaning no encryption or digesting.
+Currently implemented encryption algorithms are
+.B 'rc4_128'
+and
+.BR '3des_ede_cbc' .
+Currently implemented hashing algorithms are
+.B 'md5'
+and
+.BR 'sha1' .
+.SH "SEE ALSO"
+.IR listen (1),
+.IR dial (2),
+.IR ssl (3)
+\".IR pushtls (2)
+.SH SOURCE
+.B /emu/port/devtls.c
+.br
+.B /os/port/devtls.c
--- /dev/null
+++ b/man/3/touch
@@ -1,0 +1,138 @@
+.TH TOUCH 3
+.SH NAME
+touch \- touch screen
+.SH SYNOPSIS
+.EX
+bind -a '#T' /dev
+
+/dev/touchcal
+/dev/touchctl
+/dev/touchstat
+/dev/touch
+.EE
+.SH DESCRIPTION
+The touch screen driver
+serves a one-level directory giving access to a touch panel.
+The driver attempts to compensate
+for the warped nature of the panels we have used:
+amongst other things,
+it repeatedly samples the panel to obtain a stable reading,
+and applies a transformation to the
+.RI ( "x, y" )
+coordinates obtained to account for the peculiar distortions of individual panels.
+A panel should be calibrated by
+.IR touchcal (8)
+before use.
+.PP
+There are two variants of the driver.
+One drives a
+DynaPro touch panel
+on a York Electronics Centre BRD/98/024 interface (Version A)
+accessed via the Motorola MPC8xx SPI interface.
+The other drives a different pressure-sensitive touch panel on a webphone reference design.
+.PP
+The driver initialisation starts a kernel process to read samples from the
+touch screen and provide them to the system through the interface used
+for mouse events.
+The events generated mark
+`mouse button' 1 as down as long as the panel is touched and up otherwise.
+Other `mouse buttons' are always up.
+This is adequate for many applications (but not
+.IR acme (1)).
+.PP
+The data file
+.B touch
+is read-only.
+Each read samples the touch panel
+and returns two decimal numbers, separated by a space,
+giving the
+.I x
+and
+.I y
+coordinates read.
+The values are both -1 if a consistent reading could not be made.
+This file is used only for debugging; the window system should not be running.
+.PP
+The control file
+.B touchctl
+accepts commands to set driver parameters:
+.TF "X a b c"
+.PD
+.TP
+.BI X " a b c"
+(MPC8xx only) Set the parameters for a transformation of the X-coordinates received from the
+panel:
+.RS
+.IP
+.IR "x' = a*x+b*y+c"
+.PP
+The values are fixed-point: 1.0 is represented by 2\u\s-216\s0\d.
+.RE
+.TP
+.BI Y " d e f"
+(MPC8xx only) Set the parameters for a similar transformation of Y-coordinates:
+.RS
+.IP
+.IR "y' = d*x+e*y+f"
+.RE
+.TP
+.BI s n
+Sample every
+.I n
+milliseconds.
+.TP
+.BI c "p sx sy"
+(SA1100) Wait for the screen to be touched and sample parameters for point
+.IR p ,
+0\(<=\fIp\fP\(<=3,
+corresponding to one of the four corners of the screen.
+Save the sample parameters for calibration.
+.TP
+.B C
+(SA1100) Compute calibration parameters based on previously stored samples.
+.TP
+.BI r n
+Sets delay between touch reads.
+.TP
+.BI R n
+Average
+.RI 2 ^n
+readings each sample.
+.TP
+.BI f n
+Set the filter level (-1024 \(<=\fIn\fP\(<=1024).
+.TP
+.BI e "x y"
+Set error term for
+.I x
+and
+.IR y ;
+normally calculated by
+.BR C .
+.TP
+.BI t "p r"
+Set pressure threshold for press and release
+.PP
+Reads of
+.B touchctl
+return all current parameters in the same form as the commands
+(allowing settings to be read from the file and later restored by simply writing them back).
+.PP
+The read-only file
+.B touchstat
+contains four decimal values separated by spaces or newlines:
+the number of raw samples, the number of valid samples,
+microseconds waiting for samples, and microseconds spent processing samples.
+.PP
+The SA1100 driver
+provides a file
+.B /dev/touchcal
+that holds the calibration results as a set of
+decimal numbers for each of the four sample points, one set per line.
+The results can be saved and written back to restore the same calibration.
+.SH SOURCE
+.B /os/mpc/devtouch.c
+.br
+.B /os/sa1100/devtouch.c
+.SH SEE ALSO
+.IR touchcal (8)
--- /dev/null
+++ b/man/3/tv
@@ -1,0 +1,179 @@
+.TH TV 3
+.SH NAME
+tv \- Hauppage TV device
+.SH SYNOPSIS
+.B
+bind -b #V /dev
+.PP
+.B /dev/tv
+.br
+.B /dev/tvctl
+.SH DESCRIPTION
+The
+.I tv
+device serves two files giving access to a Hauppage television card
+in NTSC mode (only).
+.PP
+The control device
+.B tvctl
+accepts the following commands to control
+tuning and image processing:
+.TP
+.B init
+Reset the device to the default settings.
+.TP
+.BI window " minx miny maxx maxy"
+Set the display window size (default: none).
+.TP
+.BI colorkey " rl rh gl gh bl bh"
+Set the key limits for chromakey display to
+the given `low' and `high' values for
+each of red, green and blue
+(default: 15 63 255 15 15 63), and enable
+use of the colour key.
+The value 255 for a lower limit disables the limit.
+.TP
+.BI capture " minx miny maxx maxy"
+Set the capture window (default: none);
+see the description of reading the
+.B tv
+file, below.
+.TP
+.BI capbrightness " n"
+Set the brightness of a captured frame to
+.IR n ,
+on a scale from 0 to 100
+(default: 13).
+.TP
+.BI capcontrast " n"
+Set the contrast of a captured frame to
+.IR n ,
+on a scale from 0 to 100
+(default: 57).
+.TP
+.BI capsaturation " n"
+Set the saturation of a captured frame to
+.IR n ,
+on a scale from 0 to 100
+(default: 51).
+.TP
+.BI caphue " n"
+Set the hue of a captured frame to
+.IR n ,
+on a scale from 0 to 100
+(default: 0).
+.TP
+.BI capbw " n"
+Capture colour
+.RI ( n=0 )
+or monochrome
+.RI ( n=1 );
+(default: 0).
+.TP
+.BI brightness " n"
+Set the image brightness to
+.IR n ,
+on a scale from 0 to 100
+(default: 0)
+.TP
+.BI contrast " n"
+Set the image contrast to
+.IR n ,
+on a scale from 0 to 100
+(default: 54)
+.TP
+.BI saturation " n"
+Set image saturation to
+.IR n ,
+on a scale from 0 to 100
+(default: 54)
+.TP
+.BI source " n"
+Set the input source to
+.I n
+(default: video input 0).
+.TP
+.BI svideo " n"
+If
+.I n
+is non-zero, set the input capture format to
+S-Video.
+.TP
+.BI format " n"
+Set the input format to
+.I n
+(0, NTSC-M; 1, NTSC-443; 2, external control).
+Default: 0.
+.TP
+.BI channel " c f"
+Sets the right frequency for HRC CATV channel
+.IR c ,
+with fine tuning by signed offset
+.I f
+(in MHz).
+.TP
+.BI volume " m" [ " n " ]
+Set the volume of left and right channels to
+.I m
+and
+.IR n ,
+on a scale from 0 to 100
+(default: 80).
+If only
+.I m
+is given, set both left and right volumes to
+.IR m .
+.TP
+.BI bass " n"
+Set the bass to
+.IR n ,
+on a scale from 0 to 100 (default: 50).
+.TP
+.BI treble " n"
+Set the treble to
+.IR n ,
+on a scale from 0 to 100 (default: 50).
+.TP
+.BI freeze " n"
+If
+.I n
+is non-zero, freeze the current frame;
+if
+.I n
+is zero, unfreeze the video.
+.PP
+The data file
+.B tv
+is read-only.
+Reading is valid only after a capture window has been set
+(see
+.B capture
+above).
+Each read at file offset 0 causes the contents of the input video image
+within the previously-set capture window to be
+captured.
+The frame data is successively returned
+in this and subsequent reads until a short read signals that
+all frame data has been transferred.
+The data is returned in Plan 9
+.I picfile
+format, with a two line textual header followed by an empty
+line, followed by the image data in binary format.
+The header has the form:
+.IP
+.B "TYPE=ccir601"
+.br
+.BI "WINDOW=" "minx miny maxx maxy"
+.PP
+The
+.B WINDOW
+coordinates are those of the capture window.
+The image data is organised as interleaved scan lines from top to bottom of the window,
+with UY0 and VY1 values alternating, with two bytes per value, stored high-order byte
+first, encoded according to the IEEE digital component video standard.
+.SH SOURCE
+.B /os/port/devtv.c
+.br
+.B /os/pc/tv.h
+.SH SEE ALSO
+.IR audio (3)
--- /dev/null
+++ b/man/3/usb
@@ -1,0 +1,143 @@
+.TH USB 3 MPC823
+.SH NAME
+usb \- USB device interface
+.SH SYNOPSYS
+.nf
+.B bind -a '#U' /dev
+
+.B /dev/usbctl
+.B /dev/usbdata
+.B /dev/usbstat
+.B /dev/usbaddr
+.B /dev/usbframe
+.B /dev/usbsetup
+.fi
+.SH DESCRIPTION
+.PP
+The USB device serves a one-level directory,
+giving access to six files.
+They provide an interface to the device (slave) mode of the 823/850 USB controller,
+allowing an Inferno system to provide services to the USB host.
+The controller's endpoints are configured as follows.
+Endpoint 0 is the default USB control endpoint, as required by the USB standard.
+Endpoint 1 responds to bulk output requests from the host;
+endpoint 2 responds to bulk input requests from the host;
+endpoint 3 is unused.
+.PP
+The data file
+.B usbdata
+provides read and write access to endpoints 1 and 2.
+When the file is read, it returns data written to endpoint 1 by the USB host.
+Data written to the file is provided in response to the host's read requests
+on endpoint 2.
+Output data is converted to packets limited to the maximum packet
+length for the endpoint (see the
+.B maxpkt
+control request, below), which is 1023 bytes by default.
+Note that zero length records can be transmitted and
+received on USB.
+.PP
+The file
+.B usbsetup
+provides read and write access to endpoint 0.
+When the file is read, it returns
+SETUP
+requests from the host.
+If a given request requires a reply, the reply must be written to the file
+(host requests will automatically be NAKd until then).
+Requests and replies are in binary form as defined in the USB specification,
+except for the reset message described below.
+The driver itself interprets all
+SET_ADDRESS
+requests from the host, and sets the controller's address accordingly.
+That request will therefore not be visible on the
+.B usbsetup
+file.
+.PP
+A USB bus or device reset sets the controller's USB address  to zero,
+the USB `default address' for configuration.
+It also resets all data toggles to DATA0.
+A subsequent read of
+.B usbsetup
+will return the following special 8-byte reset message:
+.IP
+.EX
+byte 16rFF, byte 16rFF, byte 'r', byte 'e',
+.br
+byte 's', byte 'e', byte 't', byte '\en'
+.EE
+.PP
+which does not resemble a normal SETUP message.
+Further reads and writes on
+.B usbdata
+will return errors until that file has been closed and reopened.
+.PP
+The control file
+.B usbctl
+receives textual commands to set device parameters and responses:
+.TP
+.BI maxpkt "n nb"
+Set the maximum packet size for endpoint
+.I n
+to
+.I nb
+bytes.
+.I Nb
+must be between 8 and 64 bytes for the control endpoint 0,
+and between 1 and 1023 bytes for the other endpoints.
+The default is 8 bytes for endpoint 0 and 1023 bytes for the others.
+.TP
+.BI rdtog "n t"
+Set the data toggle for input in endpoint
+.I n
+to
+.IR t ,
+which must be either 0 (DATA0) or 1 (DATA1).
+.TP
+.BI stall "n"
+Cause endpoint
+.I n
+to respond to host IN and OUT tokens by stalling.
+.TP
+.BI unstall "n"
+Cancel the effect of a previous
+.B stall
+request on endpoint
+.IR n .
+.TP
+.BI wrtog "n t"
+Set the data toggle for the next packet output by endpoint
+.I n
+to
+.IR t ,
+which must be either 0 (DATA0) or 1 (DATA1).
+.PP
+Stalling an endpoint causes IN and OUT
+transactions from the host to return a `stalled' error status.
+On the control endpoint (endpoint 0), however,
+SETUP transactions will still arrive on
+.BR usbsetup ,
+and the arrival of a SETUP will automatically clear the `stalled' status
+for the endpoint,
+allowing an application to reject a
+control request (by stalling the subsequent status transaction)
+without stalling the control endpoint by rejecting subsequent
+SETUP transactions.
+.PP
+The status file
+.B usbstat
+is a read-only file,
+containing one line of statistics for each endpoint, giving:
+endpoint number; data toggles; maximum packet size;
+count of input and output bytes and packets; and counts
+of various error conditions.
+.PP
+The read-only file
+.B usbaddr
+contains a single number giving the current device address.
+.PP
+The read-only file
+.B usbframe
+contains a single line giving the current USB frame number.
+.SH SOURCE
+.B /os/mpc/devusb.c
--- /dev/null
+++ b/man/3/vga
@@ -1,0 +1,247 @@
+.TH VGA 3
+.SH NAME
+vga \- VGA controller device
+.SH SYNOPSIS
+.B bind #v /dev
+.PP
+.B /dev/vgactl
+.br
+.B /dev/vgaovlctl
+.br
+.B /dev/vgaovl
+.SH DESCRIPTION
+The VGA device allows configuration of a graphics controller
+on a PC (and any other platform with VGA devices).
+.B Vgactl
+allows control over higher-level settings such as display height, width, depth,
+controller and hardware-cursor type.
+Along with the I/O-port registers
+provided by
+.IR arch (3),
+it is used to implement configuration and setup of VGA controller cards.
+....This is usually performed by
+.....IR vga (8).
+.PP
+Writing strings to 
+.B vgactl
+configures the VGA device.
+The following are valid commands.
+.TP
+.BI size " X" x Y x "Z chan"
+Set the size of the screen image to be
+.I X 
+pixels wide
+and
+.I Y 
+pixels high.
+Each pixel is 
+.I Z 
+bits as specified by 
+.IR chan ,
+whose format is described in
+.IR image (6).
+.TP
+.BI actualsize " X" x Y
+Set the physical size of the display to be
+.I X
+pixels wide by
+.I Y 
+pixels high.
+This message is optional;
+it is used to implement panning and to accommodate
+displays that require the in-memory screen image
+to have certain alignment properties.
+For example, a 1400x1050 screen with a 1408x1050 in-memory image
+will use 
+.B "size 1408x1050
+but
+.BR "actualsize 1400x1050" .
+.TP
+.BI panning " mode"
+Depending on whether
+.I mode
+is 
+.B on
+or
+.BR off ,
+enable or disable panning in a virtual screen.
+If panning is on and the screen's
+.B size
+is larger than its
+.BR actualsize ,
+the displayed portion of the screen will pan to follow the mouse.
+Setting the panning mode after the first attach of the
+.B #i
+driver has no effect.
+.TP
+.BI type " ctlr"
+Set the type of VGA controller being used.
+.I Ctlr
+is one of 
+.BR ark200pv ,
+.BR clgd542x ,
+.BR clgd546x ,
+.BR ct65545 ,
+.BR cyber938x ,
+.BR hiqvideo ,
+.BR mach64xx ,
+.BR mga2164w ,
+.BR neomagic ,
+.BR s3 ,
+and
+.BR t2r4 .
+.IP
+Note that this list does not indicate the full set of VGA chips
+supported. For example,
+.B s3
+includes the 86C801/5, 86C928, Vision864, and Vision964.
+It is the job of a user-level program
+....IR vga (8)
+to recognise which particular chip is being used and to initialize it
+appropriately.
+.TP
+.BI hwgc " gc"
+Set the type of hardware graphics cursor being used.
+.I Gc
+is one of
+.BR ark200pvhwgc ,
+.BR bt485hwgc ,
+.BR clgd542xhwgc ,
+.BR clgd546xhwgc ,
+.BR ct65545hwgc ,
+.BR cyber938xhwgc ,
+.BR hiqvideohwgc ,
+.BR mach64xxhwgc ,
+.BR mga2164whwgc ,
+.BR neomagichwgc ,
+.BR rgb524hwgc ,
+.BR s3hwgc ,
+.BR t2r4hwgc ,
+.BR tvp3020hwgc ,
+and
+.BR tvp3026hwgc .
+A value of
+.B off
+disables the cursor.
+There is no software cursor.
+.TP
+.BI palettedepth " d"
+Set the number of bits of precision used by the 
+VGA palette to 
+.IR d ,
+which must be either 
+.B 6
+or
+.BR 8 .
+.TP
+.B blank
+Blank the screen.
+This consists of setting the hardware
+color map to all black as well as, on some controllers, setting the
+VGA hsync and vsync signals so as to turn off
+VESA DPMS-compliant monitors.
+The screen also blanks after 30 minutes of inactivity.
+The screen can be unblanked by moving the mouse.
+.TP
+.BI blanktime " minutes"
+Set the timeout before the
+screen blanks; the default is 30 minutes.
+If
+.I minutes
+is zero, blanking is disabled.
+.TP
+.BI hwaccel " mode"
+Depending on whether
+.I mode
+is 
+.B on
+or
+.BR off ,
+enable or disable whether hardware acceleration
+(currently for rectangle filling and moving)
+used by the graphics engine.
+The default setting is
+.BR on .
+.TP
+.BI hwblank " mode"
+Depending on whether
+.I mode
+is
+.B on
+or
+.BR off ,
+enable or disable the use of DPMS blanking
+(see
+.B blank
+above).
+.TP
+.BI linear " size align"
+Use a linear screen aperture of size
+.I size
+aligned on an
+.IR align -byte
+boundary.
+.TP
+.B drawinit
+Initialize the graphics hardware.
+This must be sent after setting the
+.BR type .
+.PP
+Reading
+.B vgactl
+returns the current settings, one per line.
+.PP
+Some VGA cards support overlay graphics.
+Writing strings to
+.B vgaovlctl
+configures such cards.
+The following are valid overlay control commands:
+.TP
+.BI openctl
+opens the overlay device.
+.TP
+.BI configure " w h format"
+allocates resources inside the driver to support an overlay area
+of width
+.I w
+and height
+.I h
+pixels.  Currently, the only supported
+.I format
+is
+.B YUYV
+packed.
+In
+.B YUYV
+two pixels are encoded by their separate Y values
+and their combined U and V values.
+The size of the two pixels is 32 bits.
+.TP
+.BI enable " x y w h"
+enables drawing data on the display through the overlay mode.  The data
+is drawn at position
+.IR x , y
+and has a width and height of
+.IR w , h
+respectively.
+.TP
+.BI closectl
+terminates overlay control.
+.PP
+Overlay data can be written to
+.BR vgaovl .
+.SH EXAMPLES
+The following disables hardware acceleration.
+.IP
+.EX
+echo hwaccel off > /dev/vgactl
+.EE
+.SH SOURCE
+.B /os/pc/devvga.c
+.SH SEE ALSO
+.IR arch (3)
+....IR vga (8)
+.SH BUGS
+The hardware graphics cursor on the
+.B et4000
+does not work in 2x8-bit mode.
--- /dev/null
+++ b/man/3/vid
@@ -1,0 +1,64 @@
+.TH VID 3 MPC823
+.SH NAME
+vid \- Motorola 823 video output
+.SH SYNOPSIS
+.B bind -b #v /dev
+.PP
+.B /dev/viddata
+.br
+.B /dev/vidctl
+.br
+.B /dev/vidcram
+.SH DESCRIPTION
+The
+.I vid
+device serves two
+files giving basic access
+to the on-chip video output of the Motorola 823.
+It assumes a video hardware configuration exactly like that of the 823FADS
+board, with an I²C interface to an off-chip ADV7176 NTSC/PAL converter
+at I²C address 0x54.
+.PP
+The control device
+.B vidctl
+accepts the following commands:
+.TP
+.B on
+Disable LCD panel (if present) and enable output from the video frame buffer.
+.TP
+.B off
+Stop display from the video frame buffer and re-enable the LCD panel (if present).
+.TP
+.BI mode " m"
+Set the video output format to
+.IR m ,
+which is one of:
+.BR ntsc ,
+.BR pal-i ,
+.B pal-m
+or
+.BR pal-n .
+This command is only effective when video output is off.
+When next switched on, the both the video controller RAM and
+ADV7176 are reprogrammed to the new format.
+.PP
+Video data can be written to the video frame buffer
+by writes of the
+.B viddata
+file.
+The file offset selects the starting byte within the frame buffer.
+Reads return the corresponding data from the frame buffer.
+The application is responsible for formatting the data as the 823's video
+encoder requires, for the selected video format.
+.PP
+The file
+.B vidcram
+gives read and write access to the contents of the
+video control RAM.
+The file offset addresses bytes in the RAM; values can be read from or
+written to the RAM in groups of 4-byte big-endian words.
+.SH SOURCE
+.B /os/mpc/devvid.c
+.SH BUGS
+Square pixel mode cannot be used on the FADS board:
+no suitable video clock source is available.
--- /dev/null
+++ b/man/4/0intro
@@ -1,0 +1,16 @@
+.TH INTRO 4
+.SH NAME
+intro \- introduction to file servers
+.SH DESCRIPTION
+This section describes programs that serve 9P
+(see
+.IR intro (5)),
+and can therefore
+be mounted, generating a new part of the name space.
+Some, such as
+.IR dossrv ,
+thereby make the contents of a foreign file format visible directly
+to Inferno applications;
+others, such as
+.IR acme ,
+provide their own services through operations on files in a name space.
--- /dev/null
+++ b/man/4/9srvfs
@@ -1,0 +1,73 @@
+.TH 9SRVFS 4 "Plan 9"
+.SH NAME
+9srvfs \- add Inferno service to Plan 9 service registry
+.SH SYNOPSIS
+.B 9srvfs
+[
+.BI -p " perm"
+]
+.I srvname
+.I source
+.SH DESCRIPTION
+.I 9srvfs
+is only usable (or indeed of interest) on Inferno hosted under Plan 9.
+It uses
+.IR srv9 (3)
+to make an Inferno service
+.I source
+available to Plan 9 applications via the Plan 9 service registry.
+.I Srv9 (3)
+must previously have been bound to
+.B /srv
+in the current name space, with
+.B -c
+to allow file creation (see
+.IR bind (1)).
+.PP
+.I Source
+may be either a command or the name of a directory.
+If
+.I source
+is surrounded by braces
+.RB ( {
+and
+.BR } ),
+it is invoked as a
+.IR sh (1)
+command, and its standard input (sic) is posted as Plan 9 service
+.BI /srv/ srvname
+with permissions
+.I perm
+(default: mode 600).
+Otherwise,
+.I source
+is taken to be a directory that is the root of a name space to export to Plan 9,
+an exporting file service is started (see
+.IR sys-export (2)),
+and again posted as Plan 9 service
+.BI /srv/ srvname,
+and the export terminates when the Plan 9 service file
+has been removed and the last mounted instance goes away in Plan 9.
+.SH EXAMPLE
+Make the current Inferno environment variables available to Plan 9 applications:
+.IP
+.EX
+bind -c '#₪' /srv   # if not already done
+9srvfs infenv /env
+.EE
+.PP
+The name space can then be mounted in Plan 9,
+allowing variables to be read and written in that Inferno environment:
+.IP
+.EX
+mount -c /srv/infenv /n/ftp
+ls /n/ftp
+cat /n/ftp/emuargs
+echo masked man >/n/ftp/zorro
+.EE
+.SH SOURCE
+.B /appl/cmd/9srvfs.b
+.SH SEE ALSO
+.IR bind (1),
+.IR srv9 (3),
+.IR import (4)
--- /dev/null
+++ b/man/4/INDEX
@@ -1,0 +1,34 @@
+intro 0intro
+9srvfs 9srvfs
+acme acme
+archfs archfs
+dbfs dbfs
+rawdbfs dbfs
+9660srv dossrv
+dossrv dossrv
+export export
+factotum factotum
+feedkey factotum
+ftpfs ftpfs
+cpu grid-cpu
+grid grid-cpu
+grid-cpu grid-cpu
+9export import
+import import
+iostats iostats
+keyfs keyfs
+keysrv keysrv
+kfs kfs
+lockfs lockfs
+logfile logfile
+memfs memfs
+mntgen mntgen
+namespace namespace
+palmsrv palmsrv
+ramfile ramfile
+registry registry
+regquery registry
+spree spree
+tarfs tarfs
+trfs trfs
+vacfs vacfs
--- /dev/null
+++ b/man/4/acme
@@ -1,0 +1,408 @@
+.TH ACME 4
+.SH NAME
+acme \- control files for text windows
+.SH SYNOPSIS
+.B acme
+[
+.B -f
+.I varfont
+] [
+.B -F
+.I fixfont
+]
+[
+.I file
+\&... ]
+.SH DESCRIPTION
+The text window system
+.IR acme (1)
+serves a variety of files for reading, writing, and controlling
+windows.
+Some of them are virtual versions of system files for dealing
+with the virtual console; others control operations
+of 
+.I acme
+itself.
+When a command is run under
+.IR acme ,
+a directory holding these files is mounted on
+.B /mnt/acme
+(also bound to
+.BR /chan )
+and also
+.BR /dev/acme ;
+the files mentioned here
+appear in both these directories.
+.PP
+Some of these files supply virtual versions of services available from the underlying
+environment, in particular the character terminal files
+.IR cons (3).
+Other files are unique to
+.IR acme .
+.TP
+.B acme
+is a subdirectory used by
+.B win
+(see
+.IR acme (1))
+as a mount point for the
+.I acme
+files associated with the window in which
+.B win
+is running.
+It has no specific function under
+.I acme
+itself.
+.TP
+.B cons
+is the standard and diagnostic output file for all commands
+run under
+.IR acme .
+(Input for commands is redirected to
+.BR /dev/null .)
+Text written to
+.B cons
+appears in a window labelled
+.IB dir /+Errors\f1,
+where
+.I dir
+is the directory in which the command
+was run.
+The window is created if necessary, but not until text is actually written.
+.TP
+.B consctl
+Is an empty unwritable file present only for compatibility.
+.TP
+.B index
+holds a sequence of lines of text, one per window.  Each line has 5 decimal numbers,
+each formatted in 11 characters plus a blank\(emthe window ID;
+number of characters (runes) in the tag;
+number of characters in the body;
+a 1 if the window is a directory, 0 otherwise;
+and a 1 if the window is modified, 0
+otherwise\(emfollowed by the tag up to a newline if present.
+Thus at character position 5×12 starts the name of the window.
+If a file has multiple zeroxed windows open,
+only the most recently used will appear in the
+.B index
+file.
+.TP
+.B label
+is an empty file, writable without effect, present only for compatibility.
+.TP
+.B new
+A directory analogous to the numbered directories
+.RI ( q.v. ).
+Accessing any
+file in
+.B new
+creates a new window.  Thus to cause text to appear in a new window,
+write it to
+.BR /dev/new/body .
+For more control, open
+.BR /dev/new/ctl
+and use the interface described below.
+.LP
+.PP
+Each
+.I acme
+window has associated a directory numbered by its ID.
+Window IDs are chosen sequentially and may be discovered by the
+.B ID
+command, by
+reading the
+.B ctl
+file, or
+indirectly through the
+.B index
+file.  The files in the numbered directories are as follows.
+.TP
+.B addr
+may be written with any textual address (line number, regular expression, etc.),
+in the format understood by button 3 but without the initial colon, including compound addresses,
+to set the address for text accessed through the
+.B data
+file.
+When read, it returns the value of the address that would next be read
+or written through the
+.B data
+file, in the format
+.BI # m ,# n
+where
+.I m
+and
+.I n
+are character (not byte) offsets.  If
+.I m
+and
+.I n
+are identical, the format is just
+.BI # m\f1.
+Thus a regular expression may be evaluated by writing it to
+.B addr
+and reading it back.
+The
+.B addr
+address has no effect on the user's selection of text.
+.TP
+.B body
+holds contents of the window body.  It may be read at any byte offset.
+Text written to
+.B body
+is always appended; the file offset is ignored.
+.TP
+.B ctl
+may be read to recover the five numbers as held in the
+.B index
+file, described above, plus three more fields: the width of the
+window in pixels, the name of the font used in the window,
+and the width of a tab character in pixels.
+Text messages may be written to
+.B ctl
+to affect the window.
+Each message is terminated by a newline and multiple
+messages may be sent in a single write.
+.RS .5i
+.TF "dumpdir\ directory"
+.TP
+.B addr=dot
+Set the
+.B addr
+address to that of the user's selected text in the window.
+.TP
+.B clean
+Mark the window clean as though it has just been written.
+.TP
+.B cleartag
+Remove all text in the tag after the vertical bar.
+.TP
+.B del
+Equivalent to the
+.B Del
+interactive command.
+.TP
+.B delete
+Equivalent to the
+.B Delete
+interactive command.
+.TP
+.B dot=addr
+Set the user's selected text in the window to the text addressed by the
+.B addr
+address.
+.TP
+.BI dump " command
+Set the command string to recreate the window from a dump file.
+.TP
+.BI dumpdir " directory
+Set the directory in which to run the command to recreate the window from a dump file.
+.TP
+.B get
+Equivalent to the
+.B Get
+interactive command with no arguments; accepts no arguments.
+.TP
+.B limit=addr
+When the
+.B ctl
+file is first opened, regular expression context searches in
+.B addr
+addresses examine the whole file; this message restricts subsequent
+searches to the current
+.B addr
+address.
+.TP
+.B mark
+Cancel
+.BR nomark ,
+returning the window to the usual state wherein each modification to the
+body must be undone individually.
+.TP
+.BI name " name
+Set the name of the window to
+.IR name .
+.TP
+.B nomark
+Turn off automatic `marking' of changes, so a set of related changes
+may be undone in a single
+.B Undo
+interactive command.
+.TP
+.B noscroll
+Turn off automatic `scrolling' of the window to show text written to the body.
+.TP
+.B put
+Equivalent to the
+.B Put
+interactive command with no arguments; accepts no arguments.
+.TP
+.B scroll
+Cancel a
+.B noscroll
+message, returning the window to the default state wherein each write
+to the
+.B body
+file causes the window to `scroll' to display the new text.
+.TP
+.B show
+Guarantee at least some of the selected text is visible on the display.
+.TP
+.B noecho
+Stop echoing characters sent to the window but indicate their presence by
+printing an asterisk.
+.TP
+.B echo
+Turns
+.BR noecho
+off.
+.RE
+.PD
+.TP
+.B data
+is used in conjunction with
+.B addr
+for random access to the contents of the body.
+The file offset is ignored when writing the
+.B body
+file, but the character (not byte) offset may be set with
+.B addr
+and then read from the
+.B data
+file.
+Text, which must contain only whole characters (no `partial runes'),
+written to
+.B data
+replaces the characters addressed by the
+.B addr
+file and sets the address to the null string at the end of the written text.
+A read from
+.B data
+returns as many whole characters as the read count will permit starting
+at the beginning of the
+.B addr
+address (the end of the address has no effect)
+and sets the address to the null string at the end of the returned
+characters.
+.TP
+.B event
+When a window's
+.B event
+file is open, changes to the window occur as always but the
+actions are also reported as
+messages to the reader of the file.  Also, user actions with buttons 2 and 3
+(other than chorded
+.B Cut
+and
+.BR Paste ,
+which behave normally) have no immediate effect on the window;
+it is expected that the program reading the
+.B event
+file will interpret them.
+The messages have a fixed format:
+a character indicating the origin or cause of the action,
+a character indicating the type of the action,
+four free-format blank-terminated decimal numbers,
+optional text, and a newline.
+The first and second numbers are the character addresses of the action,
+the third is a flag,
+and the final is a count of the characters in the optional text, which
+may itself contain newlines.
+The origin characters are
+.B E
+for writes to the
+.B body
+or
+.B tag
+file,
+.B F
+for actions through the window's other files,
+.B K
+for the keyboard, and
+.B M
+for the mouse.
+The type characters are
+.B D
+for text deleted from the body,
+.B d
+for text deleted from the tag,
+.B I
+for text inserted to the body,
+.B i
+for text inserted to the tag,
+.B L
+for a button 3 action in the body,
+.B l
+for a button 3 action in the tag,
+.B X
+for a button 2 action in the body, and
+.B x
+for a button 2 action in the tag.
+.IP
+If the relevant text has less than 256 characters, it is included in the message;
+otherwise it is elided, the fourth number is 0, and the program must read
+it from the
+.B data
+file if needed.  No text is sent on a
+.B D
+or
+.B d
+message.
+.IP
+For
+.BR D ,
+.BR d ,
+.BR I ,
+and
+.BR i
+the flag is always zero.
+For
+.BR X
+and
+.BR x ,
+the flag is a bitwise OR (reported decimally) of the following:
+1 if the text indicated is recognized as an
+.I acme
+built-in command;
+2 if the text indicated is a null string that has a non-null expansion;
+if so, another complete message will follow describing the expansion
+exactly as if it had been indicated explicitly (its flag will always be 0);
+8 if the command has an extra (chorded) argument; if so,
+two more complete messages will follow reporting the argument (with
+all numbers 0 except the character count) and where it originated, in the form of
+a fully-qualified button 3 style address.
+.IP
+For
+.B L
+and
+.BR l ,
+the flag is the bitwise OR of the following:
+1 if
+.I acme
+can interpret the action without loading a new file;
+2 if a second (post-expansion) message follows, analogous to that with
+.B X
+messages;
+4 if the text is a file or window name (perhaps with address) rather than
+plain literal text.
+.IP
+For messages with the 1 bit on in the flag,
+writing the message back to the
+.B event
+file, but with the flag, count, and text omitted,
+will cause the action to be applied to the file exactly as it would
+have been if the
+.B event
+file had not been open.
+.TP
+.B tag
+holds contents of the window tag.  It may be read at any byte offset.
+Text written to
+.B tag
+is always appended; the file offset is ignored.
+.SH SOURCE
+.B /appl/acme
+.br
+.B /appl/acme/acme
+.SH SEE ALSO
+.IR acme (1),
+.IR cons (3)
--- /dev/null
+++ b/man/4/archfs
@@ -1,0 +1,64 @@
+.TH ARCHFS 4
+.SH NAME
+archfs \- mount a mkfs archive
+.SH SYNOPSIS
+.B archfs
+[
+.B -ab
+] [
+.B -s
+] [
+.B -m mountpoint
+]
+.I archfile 
+[
+.I prefix ...
+]
+.SH DESCRIPTION
+.I Archfs
+mounts at
+.IR mountpoint
+(default:
+.BR /mnt/arch ),
+a read-only file system representing the contents of 
+.IR archfile ,
+which is a file of the format produced by
+.IR mkfs (8)
+with the
+.B -a
+option.
+Typically the archive file will have been created by 
+.IR mkfs (8)
+or
+.IR create (8).
+Any
+.I prefix
+names limit the file system's contents to files and directories in the
+archive whose path names start with one of the prefixes.
+.PP
+The
+.B -a
+and
+.B -b
+options cause the archive contents to be mounted after or before the
+mount point's existing contents, in a union dirctory (see
+.IR bind (1)
+for details).
+Normally the archive contents replace the contents of
+.IR mountpoint .
+The
+.B -s
+option causes the rest of the archive to be skipped once a file is found in the archive which
+does not start with any of the given prefixes. This is useful an archive is known to start
+with a description of its contents (eg, the
+.B /wrap
+directory provided by
+.IR create (8)),
+and that is all that is needed under
+.IR mountpoint .
+.SH SOURCE
+.B /appl/cmd/archfs.b
+.SH SEE ALSO
+.IR tarfs (4),
+.IR mkfs (8),
+.IR create (8)
--- /dev/null
+++ b/man/4/dbfs
@@ -1,0 +1,126 @@
+.TH DBFS 4
+.SH NAME
+dbfs, rawdbfs \- simple database file system
+.SH SYNOPSIS
+.B dbfs
+[
+.B -abcer
+]
+.I file mountpoint
+.br
+.B rawdbfs
+[
+.B -abcelx
+]
+[
+.B -u
+.I cmd
+]
+.I file mountpoint
+.PP
+.IB mountpoint /new
+.br
+.IB mountpoint /0
+.br
+.IB mountpoint /1
+.br
+.IB mountpoint /\fR...\fP
+.SH DESCRIPTION
+.B Dbfs
+and
+.B rawdbfs
+both expose a simple, record-based filesystem stored
+in
+.IR file .
+The
+.BR -a ,
+.B -b
+and
+.B -c
+options have the same meaning as the options accepted by
+.IR bind (1).
+If the
+.B -e
+option is given, then
+.I file
+will be created if it does not already exist.
+The filesystem provided by both
+.B dbfs
+and
+.B rawdbfs
+is substantially the same:
+when started,
+.I mountpoint
+is populated with numbered files, one for each record found in
+.IR file .
+A read of one of these files yields the data in the record;
+a write stores data in the record. A new record can be created
+by opening the
+.B new
+file; writes to this file write to the new record. The name
+of the new file can be discovered by using
+.I fstat
+in
+.IR sys-stat (2).
+Records can be removed by removing the appropriate
+record file.
+.PP
+.B Rawdbfs
+has additional features for database use: 
+.BR -x
+causes two additional files ("index" and "stats") to  appear
+in the mounted directory,
+.BR -l
+specifies that a record can only be opened for writing
+by one process at once, and 
+.BR -u
+.I cmd
+specifies a 
+.IR sh (1)
+command to be run whenever the contents of the database 
+change.
+The index file is provided as a convenience for database
+client applications. Once initialised (by writing an integer value to it),
+subsequent reads return an ever-increasing integer value.
+Reading the stats file returns counts of database
+read, write, create and delete operations as a string of four
+integers.
+.PP
+.B Dbfs
+and
+.B rawdbfs
+differ in the way that they store their data.
+.B Dbfs
+stores its records in
+.I file
+in a simple text format: the end of a record is indicated
+by an empty line. The file is completely rewritten every time
+a record is written. Storing records containing blank lines
+will lead to confusion when the database is re-read.
+.B Rawdbfs
+can store arbitrary data, but the format of the data
+storage is known only to itself. It does not rewrite
+the whole file on every record change, so can be more
+suitable for flash-based storage, where it is important
+to minimise the number of writes.
+.PP
+Note that the record numbers always get their initial
+numbering from the order of the records in
+.IR file .
+You cannot assume that the filename given to a record
+will remain the same between runs of
+.BR dbfs .
+.SH SOURCE
+.B /appl/cmd/dbfs.b
+.br
+.B /appl/cmd/rawdbfs.b
+.SH SEE ALSO
+.IR memfs (4),
+.IR ramfile (4),
+.IR calendar (1)
+.SH BUGS
+Write offsets are ignored, so the maximum amount of data
+that can be stored in a record is ATOMICIO bytes (i.e. 8K).
+.PP
+There is no way to compact a file maintained by
+.BR dbfs .
--- /dev/null
+++ b/man/4/dossrv
@@ -1,0 +1,76 @@
+.TH DOSSRV 4
+.SH NAME
+dossrv, 9660srv \- DOS/Windows and ISO 9660 file systems
+.SH SYNOPSIS
+.B dossrv
+.RB [ \-v ]
+.RB [ \-s ]
+.RB [ \-F ]
+.RB [ \-c ]
+[
+.BI -f " devicefile"
+]
+[
+.BI -m " mountpoint"
+]
+[
+.BI -S " sectors/track"
+]
+.PP
+.B 9660srv
+.RB [ \-rab ]
+.I cdfile
+.I dir
+.SH DESCRIPTION
+.I Dossrv
+makes the contents of a DOS/Windows file system visible in the Inferno name space.
+The options are:
+.TP
+.B -v
+print a debugging trace, including 9P messages, on standard error
+.TP
+.B -F
+display FAT information
+.TP
+.B -c
+display cluster data and I/O activity
+.TP
+.B -s
+standard ouput is connected to a client; serve that
+.TP
+.BI -f " devicefile"
+the DOS/Windows file system image; defaults to
+.BR /dev/hd0disk .
+.TP
+.BI -m " mountpoint"
+The directory where the contents of the file system should appear; defaults to
+.BR /n/dos .
+.TP
+.BI -S  " sectors/track"
+The number of sectors per track. The default is 9.
+.PP
+.I 9660srv
+performs a similar function for files (notably CDROMs) in ISO9660 format,
+including the Rock Ridge extension and its Plan 9 variant.
+It makes the contents of
+.I cdfile
+visible in the name space at
+.IR dir .
+The options are those of
+.IR bind (1):
+.BR \-r ,
+to replace the contents of
+.IR dir ;
+.BR \-a ,
+to make a union directory with the CD contents after the contents of
+.IR dir ;
+.BR \-b ,
+to make a union directory with the CD contents before the contents of
+.IR dir .
+.SH SOURCE
+.B /appl/cmd/dossrv.b
+.br
+.B /appl/cmd/9660srv.b
+.SH BUGS
+.I Dossrv
+cannot handle all the FAT32 extensions.
--- /dev/null
+++ b/man/4/export
@@ -1,0 +1,56 @@
+.TH EXPORT 4
+.SH NAME
+export \- export name space on a connection
+.SH SYNOPSIS
+.B export
+[
+.B \-a
+]
+.I dir
+[
+.I file
+]
+.SH DESCRIPTION
+.I Export
+calls
+.IR sys-export (2)
+to serve the name space rooted at
+.I dir
+over a connection to a 9P (Styx) client.
+.I Export
+opens the connection on the given
+.IR file ,
+or uses the standard input by default.
+It returns when the client closes the connection.
+If the
+.B \-a
+option is given, however,
+.I export
+starts an Inferno kernel process
+to serve the name space and returns immediately.
+.I Export
+serves the current name space without forking it; changes made to it
+will be visible to clients.
+.PP
+.I Export
+starts serving 9P immediately; it assumes that the connection
+has been authenticated if required.
+.I Export
+is typically called via
+.IR listen (1),
+which can authenticate the connection.
+.SH EXAMPLE
+Start a background process to serve the name space on the serial port.
+.IP
+.B "export / /dev/eia0 &"
+.PP
+Although useful when bootstrapping a system, this is unsafe
+unless a transport protocol is run on the connection.
+.SH SOURCE
+.B /appl/cmd/export.b
+.SH SEE ALSO
+.IR cpu (1),
+.IR listen (1),
+.IR dial (2),
+.IR sys-export (2),
+.IR intro (5)
--- /dev/null
+++ b/man/4/factotum
@@ -1,0 +1,717 @@
+.TH FACTOTUM 4
+.SH NAME
+factotum, feedkey \- authentication agent
+.SH SYNOPSIS
+.B auth/factotum
+[
+.B -d
+.\" ] [
+.\" .B -a authaddr
+] [
+.B -s
+.I srvname
+] [
+.B -m
+.I mtpt
+]
+.B ...
+.IB attribute ?
+.B ...
+.PP
+.B auth/feedkey
+.SH DESCRIPTION
+.I Factotum
+is a user-level file system that
+acts as the authentication agent for a user.
+It does so by managing a set of
+.IR keys .
+A key is a collection of information used to authenticate a particular action.
+Stored as a list of
+.IB attribute = value
+pairs, a key typically contains a user, an authentication domain, a protocol, and
+some secret data.
+.PP
+.I Factotum
+serves
+.IR srv (3)
+directory
+.BR #sfactotum ,
+which it binds to
+.BR /mnt/factotum .
+It serves the following files:
+.TF needkey
+.TP
+.B rpc
+each open represents a new private channel to
+.I factotum
+.TP
+.B proto
+when read lists the protocols available
+.\" .TP
+.\" .B confirm
+.\" for confiming the use of key
+.TP
+.B needkey
+allows external programs to control the addition of new keys
+.TP
+.B log
+a log of actions
+.TP
+.B ctl
+for maintaining keys; when read, it returns a list of keys.
+For secret attributes, only the attribute name follow by a
+.L ?
+is returned.
+.PD
+.PP
+In any authentication, the caller typically acts as a client
+and the callee as a server.  The server determines
+the authentication domain, sometimes after a negotiation with
+the client.  Authentication always requires the client to
+prove its identity to the server.  Under some protocols, including the one normally
+used by Inferno, the
+authentication is mutual.
+Proof is accomplished using secret information kept by
+.I factotum
+in conjunction with a cryptographic protocol.
+.PP
+.I Factotum
+can act in the role of client for any process possessing the
+same user id as it.
+.\" For select protocols such as
+.\" .B p9sk1
+.\" it can also act as a client for other processes provided
+.\" its user id may speak for the other process' user id (see
+.\" .IR authsrv (6)).
+.I Factotum
+can act in the role of server for any process.
+.PP
+.IR Factotum 's
+structure is independent of
+any particular authentication protocol.
+.I Factotum
+currently supports the following protocols:
+.TF mschap
+.TP
+.B infauth
+Inferno's authentication protocol
+.IR auth (6)
+.TP
+.B p9any
+a metaprotocol used to negotiate which actual protocol to use.
+.TP
+.B p9sk1
+a Plan 9 shared key protocol described in
+.I authsrv
+in section 6 of Plan 9's Programmer's Manual
+.\" .TP
+.\" .B p9sk2
+.\" a variant of
+.\" .B p9sk1
+.\" described in
+.\" .IR authsrv (6)'s
+.\" ``Remote Execution'' section.
+.\" .TP
+.\" .B p9cr
+.\" a Plan 9 protocol that can use either
+.\" .B p9sk1
+.\" keys or SecureID tokens.
+.\" .TP
+.\" .B apop
+.\" the challenge/response protocol used by POP3 mail servers.
+.\" .TP
+.\" .B cram
+.\" the challenge/response protocol also used by POP3 mail servers.
+.\" .TP
+.\" .B chap
+.\" the challenge/response protocols used by PPP and PPTP.
+.\" .TP
+.\" .B mschap
+.\" a proprietary Microsoft protocol also used by PPP and PPTP.
+.\" .TP
+.\" .B rsa
+.\" RSA public key decryption, used by SSH and TLS.
+.TP
+.B pass
+passwords in the clear.
+.\" .TP
+.\" .B vnc
+.\" .IR vnc (1)'s
+.\" challenge/response.
+.\" .TP
+.\" .B wep
+.\" WEP passwords for wireless ethernet cards.
+.PD
+.PP
+The options are:
+.\".TP
+.\" .B \-a
+.\" supplies the address of the authentication server to use.
+.\" Without this option, it will attempt to find an authentication server by
+.\" querying the connection server
+.\" .IR cs (8),
+.\" the file
+.\" .IB net /ndb ,
+.\" and finally the network database
+.\" .IR ndb (6).
+.TP
+.B \-m
+specifies the mount point to use, by default
+.BR /mnt/factotum .
+.TP
+.B \-s
+specifies the service name to use,
+by default it is
+.BR factotum .
+.TP
+.B \-d
+turns on debugging, written to standard error.
+.PD
+.PP
+.I Feedkey
+is a
+.IR wm (1)
+user interface for
+.\" confirming key usage and
+entering new keys.  It puts its window in the
+.IR wm (1)
+toolbar,
+and waits, reading requests from
+.\" .B confirm
+.\" and
+.BR needkey .
+For each request, it pops open a window containing suitable prompts and waits for
+user input.
+See the sections on key confirmation and key prompting below.
+.SS "Key Tuples
+.PP
+A
+.I "key tuple
+is a space-delimited list of 
+.IB attribute = value
+pairs.  Values containing spaces must be quoted following
+the conventions of
+.IR sh (1).
+An attribute whose name begins with an exclamation point
+.RB ( ! )
+is `secret' and
+does not appear when reading the
+.B ctl
+file.
+See the `Protocols' section below.
+Here are some examples:
+.PP
+.EX
+    proto=p9sk1 dom=avayalabs.com user=presotto !password=lucent
+    proto=pass user=tb !password=does.it.matter
+.EE
+.PP
+The required attributes depend on the authentication protocol.
+The `Protocols' section below describes the attributes specific
+to each supported protocol.
+.PP
+All keys can have additional attributes that act either as comments
+or as selectors to distinguish them in the
+.IR factotum (2)
+and other
+library calls.
+.PP
+The factotum owner can use any key stored by factotum.
+Any key may have one or more
+.B owner
+attributes listing the users who can use the key
+as though they were the owner.
+For example, the TLS and SSH host keys on a server
+often have an attribute
+.B owner=*
+to allow any user (and in particular,
+.L none )
+to run the TLS or SSH server-side protocol.
+.PP
+Any key may have a
+.B role
+attribute for restricting how it can be used.
+If this attribute is missing, the key can be used in any role.
+The possible values are:
+.TP
+.B client
+for authenticating outbound calls
+.TP
+.B server
+for authenticating inbound calls
+.TP
+.B speaksfor
+for authenticating processes whose
+user id does not match
+.IR factotum 's.
+.PP
+If a key has a
+.B disabled
+attribute (with any value), the key is not used
+during any protocols.
+.\" Factotum automatically marks
+.\" keys with
+.\" .B disabled=by.factotum
+.\" when they fail during certain authentication
+.\" protocols (in particular, the Plan 9 ones).
+.PD
+.\" .PP
+.\" Whenever
+.\" .I factotum
+.\" runs as a server, it must have a
+.\" .B p9sk1
+.\" key in order to communicate with the authentication
+.\" server for validating passwords and challenge/responses of
+.\" other users.
+.SS "Key Templates
+Key templates are used by routines that interface to
+.IR factotum ,
+such as those in
+.IR factotum (2),
+to specify which key and protocol to use for an authentication.
+Like a key tuple, a key template is also a list of 
+.IB attribute = value
+pairs.
+It must specify at least the protocol and enough
+other attributes to uniquely identify a key, or set of keys, to use.
+The keys chosen are those that match all the attributes specified
+in the template.  The possible attribute/value formats are:
+.TP 1i
+.IB attr = val
+The attribute
+.I attr
+must exist in the key and its value must exactly
+match
+.I val
+.TP 1i
+.IB attr ?
+The attribute
+.I attr
+must exist in the key but its value doesn't matter.
+.TP 1i
+.I attr
+The attribute
+.I attr
+must exist in the key with a null value
+.PD
+.PP
+Key templates are also used by
+.I factotum
+to request a key either via
+an RPC error or via the
+.B needkey
+interface.
+The possible attribute/value formats are:
+.TP 1i
+.IB attr = val
+This pair must remain unchanged
+.TP 1i
+.IB attr ?
+This attribute needs a value
+.TP 1i
+.I attr
+The pair must remain unchanged
+.PD
+.SS "Control and Key Management
+.PP
+A number of messages can be written to the control file.
+The messages are:
+.TP
+.B "key \fIattribute-value-list\fP
+add a new key.  This will replace any old key whose
+public attributes match (ie, non
+.B !
+attributes).
+.TP
+.B "delkey \fIattribute-value-list\fP
+delete a key whose attributes match those given.
+.TP
+.B debug
+toggle debugging on and off, i.e., the debugging also
+turned on by the
+.B \-d
+option.
+.\" .PP
+.\" By default when factotum starts it looks for a
+.\" .IR secstore (1)
+.\" account on $auth for the user and, if one exists,
+.\" prompts for a secstore password in order to fetch
+.\" the file
+.\" .IR factotum ,
+.\" which should contain control file commands.
+.\" An example would be
+.\" .EX
+.\"  key dom=x.com proto=p9sk1 user=boyd !hex=26E522ADE2BBB2A229
+.\"  key proto=rsa service=ssh size=1024 ek=3B !dk=...
+.\" .EE
+.\" where the first line sets a password for
+.\" challenge/response authentication, strong against dictionary
+.\" attack by being a long random string, and the second line
+.\" sets a public/private keypair for ssh authentication,
+.\" generated by
+.\" .B ssh_genkey
+.\" (see
+.\" .IR ssh (1)).
+.\" .PD
+.\" .SS "Confirming key use
+.\" .PP
+.\" The 
+.\" .B confirm
+.\" file provides a connection from
+.\" .I factotum
+.\" to a confirmation server, normally the program
+.\" .IR auth/fgui .
+.\" Whenever a key with the
+.\" .B confirm
+.\" attribute is used, 
+.\" .I factotum
+.\" requires confirmation of its use.  If no process has
+.\" .B confirm
+.\" opened, use of the key will be denied.
+.\" However, if the file is opened a request can be read from it
+.\" with the following format:
+.\" .PP
+.\" .B confirm
+.\" .BI tag= tagno
+.\" .I "<key template>
+.\" .PP
+.\" The reply, written back to
+.\" .BR confirm ,
+.\" consists of string:
+.\" .PP
+.\" .BI tag= tagno
+.\" .BI answer= xxx
+.\" .PP
+.\" If
+.\" .I xxx
+.\" is the string
+.\" .B yes
+.\" then the use is confirmed and the authentication will proceed.
+.\" Otherwise, it fails.
+.\" .PP
+.\" .B Confirm
+.\" is exclusive open and can only be opened by a process with
+.\" the same user id as
+.\" .IR factotum .
+.SS "Prompting for keys
+.PP
+The 
+.B needkey
+file provides a connection from
+.I factotum
+to a key server, normally the program
+.IR auth/fgui .
+Whenever
+.I factotum
+needs a new key, it first checks to see if
+.B needkey
+is opened.  If it isn't, it returns a error to its client.
+If the file is opened a request can be read from it
+with the following format:
+.PP
+.B needkey
+.BI tag= tagno
+.I "<key template>
+.PP
+It is up to the reader to then query the user for any missing fields,
+write the key tuple into the
+.B ctl
+file, and then reply by writing into the
+.B needkey
+file the string:
+.PP
+.BI tag= tagno
+.PP
+.B Needkey
+is exclusive open and can only be opened by a process with
+the same user id as
+.IR factotum .
+.SS "The RPC Protocol
+Authentication is performed by
+.IP 1)
+opening
+.BR rpc
+.IP 2)
+setting up the protocol and key to be used (see the
+.B start
+RPC below),
+.IP 3)
+shuttling messages back and forth between
+.IR factotum
+and the other party (see the
+.B read
+and
+.B write
+RPC's) until done
+.IP 4)
+if successful, reading back an
+.I AuthInfo
+structure (see
+.IR factotum (2)).
+.PP
+The RPC protocol is normally embodied by one of the
+routines in
+.IR factotum (2).
+We describe it here should anyone want to extend
+that module.
+.PP
+An RPC consists of writing a request message to
+.B rpc
+followed by reading a reply message back.
+RPC's are strictly ordered; requests and replies of
+different RPC's cannot be interleaved.
+Messages consist of a verb, a single space, and data.
+The data format depends on the verb.  The request verbs are:
+.TP
+.B "start \fIattribute-value-list\fP
+start a new authentication.
+.I Attribute-value-pair-list
+must include a
+.B proto
+attribute, a
+.B role
+attribute with value
+.B client
+or
+.BR server ,
+and enough other attibutes to uniquely identify a key to use.
+A
+.B start
+RPC is required before any others.    The possible replies are:
+.RS
+.TP
+.B ok
+start succeeded.
+.TP
+.B "error \fIstring\fP
+where
+.I string
+is the reason.
+.RE
+.PD
+.TP
+.B read
+get data from
+.I factotum
+to send to the other party.  The possible replies are:
+.RS
+.TP
+.B ok
+read succeeded, this is zero length message.
+.TP
+.B "ok \fIdata\fP
+read succeeded, the data follows the space and is
+unformatted.
+.TP
+.B "done
+authentication has succeeded, no further RPC's are
+necessary
+.TP
+.B "done haveai
+authentication has succeeded, an
+.B AuthInfo
+structure (see
+.IR factotum (2))
+can be retrieved with an
+.B authinfo
+RPC
+.TP
+.B "phase \fIstring\fP
+its not your turn to read, get some data from
+the other party and return it with a write RPC.
+.TP
+.B "error \fIstring\fP
+authentication failed,
+.I string
+is the reason.
+.TP
+.B "protocol not started
+a
+.B start
+RPC needs to precede reads and writes
+.TP
+.B "needkey \fIattribute-value-list\fP
+a key matching the argument is needed.
+This will not appear if the
+.B needkey
+file is in use.
+Otherwise, a suitable key can be written to
+.B ctl
+and after that,
+authentication may proceed (ie, the read restarted).
+.PD
+.RE
+.TP
+.B "write \fIdata\fP
+send data from the other party to
+.IR factotum .
+The possible replies are:
+.RS
+.TP
+.B "ok
+the write succeeded
+.TP
+.B "needkey \fIattribute-value-list\fP
+see above
+.TP
+.B "toosmall \fIn\fP
+the write is too short, get more data from the
+other party and retry the write.
+.I n
+specifies the maximun total number of bytes.
+.TP
+.B "phase \fIstring\fP
+its not your turn to write, get some data from
+.I factotum
+first.
+.TP
+.B "done
+see above
+.TP
+.B "done haveai
+see above
+.RE
+.TP
+.B authinfo
+retrieve the AuthInfo structure.  
+The possible replies are:
+.RS
+.TP
+.B "ok \fIdata\fP
+.I data
+is a marshaled form of the AuthInfo structure.
+.TP
+.B "error \fIstring\fP
+where
+.I string
+is the reason for the error.
+.PD
+.RE
+.TP
+.B attr
+retrieve the attributes used in the
+.B start
+RPC.
+The possible replies are:
+.RS
+.TP
+.B "ok \fIattribute-value-list\fP
+.TP
+.B "error \fIstring\fP
+where
+.I string
+is the reason for the error.
+.PD
+.RE
+.SS Protocols
+.I Factotum
+can support many authentication protocols, each implemented by a separate
+module in the directory
+.BR /dis/auth/proto .
+Currently only a few are implemented in Inferno:
+.PP
+.B Infauth
+is the Inferno public-key authentication protocol described by
+.IR auth (6).
+It requires a key with
+.BR proto = infauth ,
+and a
+.B !authinfo
+attribute providing Inferno authentication data as an S-expression (see
+.IR sexprs (6)).
+The S-expression has five string elements:
+the signer's public key, the certificate for the user's public key,
+the user's secret key, and the values for parameters
+.I alpha
+and
+.IR p ,
+selected by the signer when the key was generated.
+The keys and certificates are represented as strings of the form produced by
+.IR keyring-certtostr (2);
+the parameter values are represented as binary in the form produced by
+.B IPint.iptobytes
+(see
+.IR keyring-ipint (2)).
+Normally
+.B infauth
+checks that the other party's key was signed by the signer in the
+.B !authinfo
+data, but
+if the key has the attribute
+.B anysigner
+with non-zero integer value,
+.B infauth
+will accept keys signed by any signer.
+The actual signer can be determined by inspecting the data
+returned by the
+.B authinfo
+request;
+the option is intended for use by services that support calls from many domains,
+each with its own signer.
+.PP
+.BR P9sk1
+is the shared-secret protocol used to authenticate to various Plan 9 services.
+It requires a key with
+.BR proto = p9sk1 ,
+a
+.B dom
+attribute identifying the authentication domain, a
+.B user
+name valid in that domain, and either a
+.B !password
+or
+.B !hex
+attribute specifying the password or hexadecimal secret
+to be used.
+.B P9sk1
+normally is invoked by Plan 9's general authentication protocol,
+.BR p9any ,
+which is supported by Inferno's
+.IR factotum .
+.PP
+.B Pass
+requires a key with
+.B proto=pass
+in addition to
+.B user
+and
+.B !password
+attributes.
+.\" .PP
+.\" .B Rsa
+.\" requires a key with
+.\" .B proto=rsa
+.\" in addition to all the hex attributes defining an RSA key:
+.\" .BR ek ,
+.\" .BR n ,
+.\" .BR !p ,
+.\" .BR !q ,
+.\" .BR !kp ,
+.\" .BR !kq ,
+.\" .BR !c2 ,
+.\" and
+.\" .BR !dk .
+.\" By convention, programs using the RSA protocol also require a
+.\" .B service
+.\" attribute set to
+.\" .BR ssh ,
+.\" .BR sshserve ,
+.\" or
+.\" .BR tls .
+.\" .PP
+.\" .B Wep
+.\" requires a
+.\" .BR key1 ,
+.\" .BR key2 ,
+.\" or
+.\" .BR key3
+.\" set to the password to be used.
+.\" Starting the protocol causes
+.\" .I factotum
+.\" to configure the wireless ethernet card
+.\" .B #l/ether0
+.\" for WEP encryption with the given password.
+.SH SOURCE
+.B /appl/cmd/auth/factotum
+.SH SEE ALSO
+.IR factotum (2)
--- /dev/null
+++ b/man/4/ftpfs
@@ -1,0 +1,117 @@
+.TH FTPFS 4
+.SH NAME
+ftpfs \- file transfer protocol (FTP) file server
+.SH SYNOPSIS
+.B ftpfs
+[
+.B -/dpq
+] [
+.BI -m " mountpoint"
+] [
+.BI -a " password"
+]
+.I ftphost
+.SH DESCRIPTION
+.I Ftpfs
+connects to
+.I ftphost
+using the Internet's File Transfer Protocol,
+and makes the remote files
+visible at
+.I mountpoint
+(default:
+.BR /n/ftp )
+in the current name space,
+for access by ordinary Inferno file operations.
+In other words, it is a protocol translator between FTP and 9P.
+The connection is shut down by unmounting the mount point;
+see
+.IR bind (1).
+The
+.I ftphost
+can be any address acceptable to
+.IR cs (8);
+the default network is
+.BR tcp ,
+and the default service is
+.B ftp
+(port 21).
+.PP
+.I Ftpfs
+will prompt for a user name and password as
+.I ftphost
+demands.
+If the FTP host supports `anonymous FTP' (ie, guest access),
+it will accept the user name
+.L anonymous
+and a conventional password (notionally the user's e-mail address).
+Given the
+.B -a
+option,
+.I ftpfs
+automatically logs in as
+.L anonymous
+with the
+.I password
+supplied,
+avoiding prompting.
+.PP
+By default,
+.I ftpfs
+uses a `passive' connection for file transfer: the remote system gives the local
+system a new address to call to fetch the data.
+This often works best for local systems behind firewalls that block incoming calls
+to arbitrary ports,
+but can fail for instance if the remote system does not support passive mode,
+or is itself behind such a firewall.
+The
+.B \-p
+option forces `active' connection: the local system gives the remote machine
+an address on which to call it back to transfer the data.
+.PP
+Other options are:
+.TP
+.B \-/
+Mount the remote machine's root directory on
+.IR mountpoint ,
+not the user's home directory (which is used by default).
+.TP
+.B \-q
+Suppress the printing of chatter (`informational messages') from
+the remote server.
+.TP
+.B \-d
+Display all FTP protocol transactions to allow a failure to connect to be debugged.
+.\" -/?
+.PP
+.I Ftpfs
+keeps a limited local cache of remote files and directories.
+The cache is kept consistent with file and directory operations by the local
+user through the current connection, but not with changes made by others on the remote site.
+Cached entries for a given directory can be flushed explicitly by
+accessing the name
+.B .flush.ftpfs
+in that directory.
+.SH EXAMPLE
+Mount a remote machine and copy files from there to here.
+.IP
+.EX
+mkdir here
+ftpfs -a 'bloggs@' ftp.vitanuova.com
+cp /n/ftp/package.tgz here
+unmount /n/ftp
+.EE
+.SH FILES
+.TF /tmp/ftp.*
+.TP
+.B /tmp/ftp.*
+Temporarily cached files
+.SH SOURCE
+.B /appl/cmd/ftpfs.b
+.SH BUGS
+Symbolic links are not supported.
+.br
+.I Ftpfs
+does not implement `rename', forcing
+.IR mv (1)
+to rename by copying.
--- /dev/null
+++ b/man/4/grid-cpu
@@ -1,0 +1,141 @@
+.TH GRID-CPU 4
+.SH NAME
+grid: cpu \- cpu server resource
+.SH SYNOPSIS
+.B grid/srv/cpu
+.br
+.B grid/runcpu
+.SH DESCRIPTION
+.I Cpu
+is a file server that provides a processing resource. This resource imports a namespace from one or more external sources and executes selected commands within it. 
+.I Cpu
+creates its own namespace containing three files
+.BR ctl ,
+.B cmd
+and
+.B ns
+which are used to configure the processing task prior to execution, this namespace is exported and served on 
+.I stdin.
+In order to configure a processing task, a client must first mount the
+.I cpu
+namespace. Once this is done, reading the 
+.B cmd
+and
+.B ns
+files will display the current configuration of commands to be executed or directories to be imported respectively. The 
+.B ctl
+file is used to modify the current configuration and accepts the following input:
+.IP
+.B add cmd 
+.I command
+.IP
+.B add ns 
+.I path address
+.IP
+.B del [cmd|ns]
+.I index
+.PP
+Where
+.I command
+is the name (including full path and parameters) of a command to be executed.
+.I Path
+is the name of a directory to import from the exported namespace available at 
+.I address
+and 
+.I index
+is the index (as shown when reading the
+.B cmd
+and
+.B ns
+files) of the command or directory to be removed from the configuration.
+.PP
+For example, to import
+.B /dis
+and
+.B /dev
+from a resource exporting a 9P (see
+.IR intro(5))
+namespace at address
+.B tcp!200.1.5.53!7003
+and then run 
+.B ls -l 
+and
+.BR du ,
+the following commands would be used:
+.IP
+.B "echo 'add cmd /dis/ls -l' > ctl"
+.br
+.B "echo 'add cmd /dis/du' > ctl"
+.br
+.B "echo 'add ns /dis tcp!200.1.5.53!7003' > ctl"
+.br
+.B "echo 'add ns /dev tcp!200.1.5.53!7003' > ctl"
+.PP
+Reading the
+.B cmd
+and 
+.B ns
+files will give the following output:
+.IP
+.B cmd
+.br
+.B 1: /dis/ls -l
+.br
+.B 2: /dis/du
+.IP
+.B ns
+.br
+.B 1: /dis (200.1.5.53!7003)
+.br
+.B 2: /dev (200.1.5.53!7003)
+.PP
+Once the client disconnects from (unmounts) the cpu, the selected namespaces will be imported and the commands executed. Note: A 
+.I cpu
+does not have
+.I any
+namespace of its own. All required namespaces
+.I must
+be imported, for a simple command such as
+.BR ls ,
+the minimum recommended namespace to import would be:
+.B /chan /dev /dis /module
+.PP
+.IR Register (1)
+may be used in conjunction with 
+.I cpu
+to register it with a 
+.IR registry (4)
+and to export and serve its namespace across
+.IR dial(2) 
+network connections. Incoming connections may also be displayed visually using
+.IR grid-monitor (1).
+For example:
+.PP
+.BI "grid/register [" " options ..." " ] { grid/srv/cpu } | grid/srv/monitor 2 'CPU resource'
+.PP
+This set of commands is encapsulated within the shell script
+.I runcpu
+which will automatically register 
+.I cpu
+with a
+.IR registry(4)
+if possible and start up the graphical display to show connections to the resource. There is no need for the user to execute
+.I cpu
+outside of
+.I runcpu
+unless the namespace it provides is required to be accessible in a different way to that provided by
+.IR grid-register (1).
+.SH SOURCE
+.B /appl/grid/srv/cpu.b
+
+.SH BUGS
+At the moment,
+.I Cpu
+will only import namespaces across an unauthenticated connection as provided by
+.IR ns (1).
+
+.SH "SEE ALSO"
+.IR grid-monitor (1),
+.IR grid-ns (1),
+.IR grid-query (1),
+.IR grid-register (1)
--- /dev/null
+++ b/man/4/import
@@ -1,0 +1,145 @@
+.TH IMPORT 4
+.SH NAME
+import, 9export \- exchange name spaces with a Plan 9 system
+.SH SYNOPSIS
+.B import
+[
+.B -ab
+] [
+.B -c
+] [
+.BI -k " keyspec"
+] [
+.BI -e " crypt hash"
+]
+.I host
+.I file
+[
+.I localfile
+]
+.PP
+.B 9export
+[
+.B -A
+] [
+.BI -k " keyspec"
+] [
+.BI -e " crypt hash"
+]
+.SH DESCRIPTION
+.I Import
+dials the Plan 9
+.I host
+(which might be the current host) and makes the given
+.I file
+on that system visible in the local name space as
+.I localfile
+(if
+.I localfile
+is not given,
+.I file
+is used as the local name).
+The authentication agent
+.IR factotum (4)
+must be active.
+(When Inferno is running on Plan 9, Plan 9's own authentication
+agent can be used, by binding Plan 9's
+.B /mnt/factotum
+to the same name in Inferno's own name space. See
+.IR fs (3).)
+The remote files are accessed with the permissions of the user authenticated
+to the remote system by
+.IR factotum .
+.PP
+If the remote
+.I file
+is a directory, the whole file tree rooted at that
+directory is accessible, the local mount point must also be a directory,
+and the
+.BR -a ,
+.BR -b ,
+and
+.B -c
+options control the creation of union directories just as for
+.IR bind (1).
+By default, the remote file replaces the local one in the name space.
+.PP
+If the
+.B -e
+option is given, the network connection can be encrypted, or provided
+with digests to authenticate the contents, or both.
+.I Crypt
+is an encryption algorithm accepted by
+.IR ssl (3);
+.I hash
+is one of its digest algorithms.
+Plan 9's
+.I exportfs
+normally requires
+.B
+\&'rc4_256 sha1'
+on such connections;
+it currently provides no negotiation of algorithms.
+.PP
+The
+.B -k
+option gives a string of space separated
+.IB attr = value
+pairs that control
+.IR factotum 's
+selection of a suitable key for the remote system.
+.PP
+.I 9export
+serves the Plan 9
+.I exportfs
+protocol on its standard input,
+allowing a Plan 9 system to import parts of the Inferno name space in which
+.I 9export
+is run.
+If the
+.B -A
+option is given, the caller is not authenticated, line encryption and message authentication
+are not done,
+and the Plan 9
+.I exportfs
+protocol starts immediately.
+Otherwise,
+.IR factotum (4)
+must be accessible via
+.B /mnt/factotum
+as for
+.IR import ,
+and it is used to run the Plan 9 authentication protocol.
+The
+.B -e
+and
+.B -k
+options are as for
+.IR import .
+Normally
+.I 9export
+returns when the client closes the connection; the
+.B -a
+option causes
+.I 9export
+to return immediately, leaving a kernel process to serve the name space.
+.I 9export
+might be used as follows:
+.IP
+.EX
+listen -Av 'tcp!*!exportfs' {9export&}
+.EE
+.PP
+Beware that currently the name space is served with the permissions of the
+person running
+.IR 9export ,
+not those of the caller.
+.SH SOURCE
+.B /appl/cmd/9export.b
+.br
+.B /appl/cmd/import.b
+.SH SEE ALSO
+.IR bind (1),
+.IR factotum (2),
+.IR export (4),
+.IR namespace (6)
--- /dev/null
+++ b/man/4/iostats
@@ -1,0 +1,81 @@
+.TH IOSTATS 4
+.SH NAME
+iostats \- file system to measure I/O
+.SH SYNOPSIS
+.B iostats
+[
+.B -d
+]
+[
+.B -f
+.I dbfile
+]
+.I cmd
+[
+.I args...
+]
+.SH DESCRIPTION
+.I Iostats
+is a file server that interposes itself between a program and a copy of the current name space,
+which allows it to gather statistics of file system
+use at the level of the file system protocol of
+.IR intro (5).
+When the program exits,
+a report is printed on standard error.
+.PP
+The report consists of three sections.
+The first section reports the amount
+of user data in
+.B read
+and
+.B write
+messages sent by the program and the average rate at
+which the data was transferred.
+The
+.B protocol
+line reports the amount
+of data sent as message headers, that is,
+protocol overhead.
+The
+.B rpc
+line reports the
+total number of file system transactions.
+.PP
+The second section gives
+the number of messages, the fastest, slowest, and average turn around
+time and the amount of data involved with each 9P
+message type.
+The final section gives an I/O summary for each file used
+by the program in terms of opens, reads and writes.
+.PP
+The
+.B -d
+option causes
+.I iostats
+to write a debugging log to
+.I dbfile
+(default:
+.BR iostats.out )
+that records all protocol messages.
+.SH EXAMPLE
+Display summary of file I/O incurred by
+.IR ls (1):
+.IP
+.EX
+iostats ls
+.EE
+.PP
+Start a new shell, displaying all 9P traffic caused by the shell or its children:
+.IP
+.EX
+iostats -df /fd/1 sh
+.EE
+.SH SOURCE
+.B /appl/cmd/iostats.b
+.SH BUGS
+Poor clock resolution means that large amounts of I/O must be done to
+get accurate rate figures.
+.PP
+Can be fooled by programs that do fresh mounts outside its purview.
+.PP
+Cannot monitor standard input or output.
--- /dev/null
+++ b/man/4/keyfs
@@ -1,0 +1,126 @@
+.TH KEYFS 4
+.SH NAME
+keyfs \- encrypted key storage
+.SH SYNOPSIS
+.B auth/keyfs
+[
+.B -D
+]
+[
+.BI -m " mountpoint"
+] [
+.BI -n " nvram"
+] [
+.I keyfile
+]
+.SH DESCRIPTION
+.I Keyfs
+serves a two-level name space for storing authentication data, specifically
+the status and secrets of each user to whom
+.IR logind (8)
+can issue a certificate.
+The data is stored in
+.I keyfile
+(default:
+.BR /keydb/keys ),
+encrypted by a master key using AES
+(see
+.IR keyring-crypt (2)).
+.I Keyfs
+should be started only on the machine acting as authentication server (signer),
+before a listener is started for
+.IR signer (8).
+Note that
+.I signer
+and
+.I keyfs
+must share the name space.
+Furthermore, no other application except the console should see that name space.
+.PP
+.I Keyfs
+prompts for the master key, reads and decrypts
+.IR keyfile ,
+and serves files representing the contents at
+.I mountpoint
+in the name space (default:
+.BR /mnt/keys ).
+.PP
+Each
+.I user
+in
+.I keyfile
+is represented by a directory
+.IB mountpoint / user.
+Each such directory has the following files:
+.TF status
+.TP
+.B log
+A count of the number of failed authentications.
+Writing
+.B bad
+to the file increments the count; writing
+.B good
+resets it to 0.
+When the count reaches some implementation-defined limit,
+the account status is set to
+.B disabled
+(see the
+.B status
+file below).
+.TP
+.B expire
+The time in seconds since the epoch when the account will expire,
+or the text
+.B never
+if it has no expiration time.
+The string
+.B never
+or a number can be written to the file to set a new expiry time.
+.TP
+.B secret
+The secret (supposedly) known only to the user and the authentication service.
+A secret is any sequence of bytes between 0 and 255 bytes long;
+it is initially empty.
+The length of the file returned by
+.IR sys-stat (2)
+is the length of the secret.
+If the account has expired or is disabled, an attempt to read the file
+will give an error.
+.TP
+.B status
+The current status of the user's account, either
+.B ok
+or
+.BR disabled .
+Either string can be written to the file to change the state accordingly.
+.PD
+.PP
+To add a new account, make a directory with that name in
+.IR mountpoint .
+It must not already exist.
+To remove an account, remove the corresponding directory;
+to rename an account, rename the directory.
+.PP
+All changes made via file system operations in
+.I mountpoint
+result in appropriate changes to
+.IR keyfile .
+.PD
+.PP
+If the
+.B -n
+option is given, instead of prompting for the master key,
+.I keyfs
+will read it from the file
+.IR nvram .
+Obviously that file should be well-protected from ordinary observers.
+.PP
+The
+.B -D
+option enables tracing of the file service protocol, for debugging.
+.SH SOURCE
+.B /appl/cmd/auth/keyfs.b
+.SH SEE ALSO
+.IR changelogin (8),
+.IR logind (8),
+.IR signer (8)
--- /dev/null
+++ b/man/4/keysrv
@@ -1,0 +1,116 @@
+.TH KEYSRV 4
+.SH NAME
+keysrv \- secret key server
+.SH SYNOPSIS
+.B auth/keysrv
+.SH DESCRIPTION
+.I Keysrv
+is a file service run on a connection to an authentication server.
+It allows a remote user
+to change a secret stored on the server by
+.IR keyfs (4),
+which must have been started before
+.IR keysrv ,
+in a name space with the authentication data available under
+.BR /mnt/keys .
+.PP
+.I Keysrv
+serves a single file,
+.BR secret ,
+on a connection accessed through file descriptor 0 (ie, the standard `input').
+When invoked, it
+first authenticates the connection using
+.IR security-auth (2),
+requiring the client to use
+.B sha1
+and
+.BR rc4_256 .
+If authentication succeeds,
+.I keysrv
+exports a name space containing a file
+.BR secret .
+The authentication ensures that only a user that possesses a valid certificate can connect to the service.
+.PP
+If the authenticated user (ie, the user name in the verified certificate)
+has an entry in
+.BR /mnt/keys ,
+as served by
+.IR keyfs (4),
+and that user has a non-empty secret,
+then the file
+.B secret
+will accept reads and writes.
+(Otherwise, every read or write returns an appropriate error.)
+Every successful read returns 0 bytes; thus a read can be used to check that the user is known and has a secret key.
+Each write contains data of the following form:
+.IP
+.EX
+.fi
+.I oldhash
+[
+.I newsecret
+]
+.EE
+.PP
+.I Oldhash
+is the SHA1 hash
+(see
+.IR keyring-sha1 (2))
+of the user's existing secret, as 20 hexadecimal digits.
+If the value of
+.I oldhash
+does not match that of the stored secret, the write returns an error and suitable diagnostic.
+.I Oldhash
+is optionally followed by a
+.IR newsecret ,
+in clear text as a sequence of bytes (typically the secret as
+.IR utf (6)),
+separated from
+.I oldhash
+by a single space.
+If the
+.I oldhash
+matches that of the secret currently stored,
+.I newsecret
+replaces it.
+The write returns an error if
+.I oldhash
+does not match the stored value, or if something else goes wrong.
+.PP
+.I Keysrv
+can be invoked via
+.IR listen (1):
+.IP
+.EX
+listen -t -A 'tcp!*!infkey' {auth/keysrv}
+.EE
+.PP
+Normally that is done automatically when
+starting an authentication service using
+.B svc/auth
+(see
+.IR svc (8)).
+.PP
+.IR Passwd (1)
+dials the service, authenticates, and mounts the resulting connection on
+.BR /mnt/keysrv ,
+where it accesses the
+.B secret
+file to change the secret.
+.SH FILES
+.TF /mnt/keysrv
+.TP
+.B /mnt/keys
+mount point for
+.IR keyfs (4)
+.TP
+.B /mnt/keysrv
+exported mount point for
+.I keysrv
+.SH SOURCE
+.B /appl/cmd/auth/keysrv.b
+.SH SEE ALSO
+.IR listen (1),
+.IR passwd (1),
+.IR keyfs (4),
+.IR logind (8)
--- /dev/null
+++ b/man/4/kfs
@@ -1,0 +1,175 @@
+.TH KFS 4
+.SH NAME
+kfs \- disk file system
+.SH SYNOPSIS
+.BI "mount {disk/kfs"
+.RB [ -r
+[
+.BI -b " bsize"
+] ]
+.RB [ -c ]
+.RB [ -A ]
+.RB [ -P ]
+.RB [ -R ]
+.RB [ -W ]
+[
+.BI -n " name"
+]
+.IB " file" "} " dir
+.SH DESCRIPTION
+.I Kfs
+implements a hierarchical Inferno file system within an existing
+.IR file ,
+which is typically a disk or flash memory partition.
+It gives access to it through the 9P protocol on its standard input,
+and the contents can be mounted directly on a given
+.I dir
+as shown above.
+The file system format is the same as that used by the
+.I kfs
+command of Plan 9, except that the modification user ID is implemented.
+.PP
+The
+.B -r
+option causes the file system to be reset to an initially empty state
+(`reamed').
+Permission checking is turned off, to allow any desired permissions and
+file ownership to be set.
+(In other words, the
+.B -W
+and
+.B -P
+options are also set by default.)
+The file system block size is set to the
+.IR bsize
+given by the
+.B -b
+option
+(default: 1024 bytes),
+which must be a multiple of 512 and not greater than 16k bytes.
+The block size is stored in the
+.I file
+and need not be given again.
+The storage representation is always little-endian.
+.PP
+Otherwise, the file system is checked if required, unless the
+.B -c
+option is given.
+.PP
+The contents of the file system can be provided by using commands
+such as
+.IR mkdir (1),
+.IR cp (1)
+and
+.IR rm (1)
+in
+.IR dir ,
+or built from a description using
+.IR mkfs (8).
+.PP
+The mapping between user names and
+internal IDs within the file system is established by the file
+.BR adm/users
+(within the file system itself)
+as described by
+.IR users (6),
+which
+.I kfs
+reads when it starts.
+If no such file exists, as for instance when the file system is initially empty,
+.I kfs
+uses a minimal set corresponding to the following
+.IR users (6)
+file:
+.IP
+.EX
+-1:adm:adm:
+0:none:adm:
+9999:noworld::
+10000:sys::
+10001:upas:upas:
+10002:bootes:bootes:
+10006:inferno::
+.EE
+.PP
+Any
+.IR users (6)
+file used with
+.I kfs
+should include entries for at least
+.BR adm ,
+.BR none ,
+and
+.BR noworld
+as above (although group membership can vary).
+.PP
+.I Kfs
+can optionally serve a control file, for use by
+.IR kfscmd (8).
+If the
+.B -n
+option is given,
+.I kfs
+creates a channel
+.BI /chan/kfs. name .cmd
+and accepts commands on it from the user that started
+.IR kfs .
+.PP
+Other options are:
+.TP
+.B -A
+do not update access times; useful when running a file system in flash over
+.IR ftl (3),
+to avoid excessive wear
+.TP
+.B -P
+suppress permission checking
+.TP
+.B -R
+file system is read only
+.TP
+.B -W
+allow
+.B wstat
+(see
+.IR sys-stat (2)
+or
+.IR stat (5))
+to make arbitrary changes to user and group fields
+.SH EXAMPLES
+Create an empty file system
+in the file
+.BR kfs.file .
+Because the file system will be no larger than the existing
+file's size, and the file is assumed not to be a device file, use
+.IR zeros (1)
+to prepare a file with 2048 blocks of 1024 bytes each:
+.IP
+.EX
+zeros 1024 2048 >kfs.file
+mount -c {disk/kfs -r kfs.file} /n/local
+.EE
+.PP
+The
+.B -c
+option to
+.I mount
+allows files to be created in
+.BR /n/local .
+.SH SOURCE
+.B /appl/cmd/disk/kfs.b
+.SH SEE ALSO
+.IR dd (1),
+.IR zeros (1),
+.IR flash (3),
+.IR ftl (3),
+.IR logfs (3),
+.IR sd (3),
+.IR users (6),
+.IR kfscmd (8),
+.IR mkfs (8)
+.SH BUGS
+Because the file system format is the same as Plan 9's
+.IR kfs ,
+this one also does not support file names longer than 27 bytes.
+It likewise cannot cope with files bigger than 2⁳ⁱ-1 bytes.
--- /dev/null
+++ b/man/4/lockfs
@@ -1,0 +1,89 @@
+.TH LOCKFS 4
+.SH NAME
+lockfs \- exclusive access file server
+.SH SYNOPSIS
+.B lockfs
+[
+.B -A
+] [
+.B -a
+.I alg
+]... [
+.B -p
+.I addr
+]
+.I dir
+[
+.I mountpoint
+]
+.SH DESCRIPTION
+.I Lockfs
+acts as a filesystem layer above an existing namespace,
+allowing multiple-reader, exclusive writer access to the
+files therein. Opening a file served by
+.I lockfs
+obtains a lock on the file, or blocks until a lock can
+be obtained.
+.I Lockfs
+serves a single-level directory that initially contains the
+files in
+.IR dir .
+If the
+.B -p
+option is provided,
+.I lockfs
+will listen for incoming connections on
+.IR addr ,
+authenticating them as required.
+Each
+.B -a
+argument provides an acceptable
+algorithm to run on the connection.
+The list of all
+.IR alg s
+is passed to
+.B server (see
+.IR security-auth (2)).
+If no
+.B -a
+arguments are given,
+.B "-a none"
+is assumed.
+If the
+.B -A
+option is given, then no authentication
+will be performed.
+.PP
+If the
+.B -p
+option is not given, the lockfs file system
+will be mounted on
+.IR mountpoint ,
+or
+.I dir
+if
+.I mountpoint
+is not given.
+.SH EXAMPLE
+Run a lock server guarding access to
+.BR /lib/datafiles :
+.IP
+.EX
+lockfs -p 'tcp!*!32454' /lib/datafiles
+.EE
+.PP
+Mount the above server (where
+.I locksrv
+was originally run on a server named
+.IR machine .
+.IP
+.EX
+mount -c tcp!\fImachine\fP!32454 /n/remote
+.EE
+.SH SOURCE
+.B /appl/cmd/lockfs.b
+.SH BUGS
+There's no way to break a lock held by a
+malingering process.
+.PP
+Should probably support multi-level directories.
--- /dev/null
+++ b/man/4/logfile
@@ -1,0 +1,45 @@
+.TH LOGFILE 4
+.SH NAME
+logfile \- memory-based append-only circular storage buffer
+.SH SYNOPSIS
+.B logfile
+.RB [ \-\fIsize\fP ]
+.I \fIfile\fP
+.SH DESCRIPTION
+.I Logfile
+creates a name at
+.I file
+(the containing directory must already exist).
+Each write to
+.I file
+appends the written data up to a maximum of
+.I size
+bytes; subsequent writes erase the existing
+data in a circular fashion. A read from
+.I file
+will return any currently stored data, if any;
+if there is none, then it will block until more
+data becomes available.
+Multiple readers will receive the data independently
+of one another. If a writing process is consistently
+producing data faster than a reading process, then
+the data read will have some bytes elided; a write
+is never blocked.
+.SH SOURCE
+.B /appl/cmd/logfile.b
+.SH SEE ALSO
+.IR wm-logwindow (1),
+.IR sys-file2chan (2)
+.SH BUGS
+Restrictions on
+.IR sys-file2chan (2)
+mean that the
+.I logfile
+process will not terminate automatically
+when its file is removed or unmounted.
+.PP
+There is no indication to a reader of any
+missing data.
+.PP
+It is not possible to do a non-blocking
+read of the log file.
--- /dev/null
+++ b/man/4/memfs
@@ -1,0 +1,62 @@
+.TH MEMFS 4
+.SH NAME
+memfs \- mount a heap based filesystem
+.SH SYNOPSIS
+.B memfs
+.RB [ \-s ]
+.RB [ \-rab ]
+.RB [ \-m
+.IR size ]
+.RI [ mountpoint ]
+.SH DESCRIPTION
+.I Memfs
+mounts a newly created heap-based filesystem
+on the given
+.I mountpoint
+directory (default
+.BR /tmp ).
+.PP
+The filesystem is entirely maintained in memory, no external storage is used.
+File data is allocated in 512 byte blocks.
+If a maximum
+.I size
+is specified, the actual maximum is rounded down to the nearest
+whole number of blocks:
+.IP
+.EX
+actualsize := (size / 512) * 512;
+.EE
+.PP
+The root of the filesystem is owned by the user who invoked memfs and
+is created with Read, Write and Execute permissions for the owner and Read and Execute
+permissions for everyone else (8r755).
+.PP
+.SH OPTIONS
+.TP
+.B \-s
+Serve styx on file descriptor 0.
+The
+.I mountpoint
+and other mount options are ignored.
+.TP
+.B \-r
+The default option.
+The old
+.I mountpoint
+directory becomes a union directory consisting of just the root of the new filesystem.
+.TP
+.B \-a
+Add the root of the new filesystem to the end of the union directory
+represented by the
+.I mountpoint
+directory.
+.TP
+.B \-b
+Add the root of the new filesystem to the beginning of the union directory
+represented by the
+.I mountpoint
+directory.
+.SH SOURCE
+.B /appl/cmd/memfs.b
+.SH SEE ALSO
+.IR ramfile (4)
--- /dev/null
+++ b/man/4/mntgen
@@ -1,0 +1,30 @@
+.TH MNTGEN 4
+.SH NAME
+mntgen \- dynamically generate mount points
+.SH SYNOPSIS
+.BI "mount {mntgen} " dir
+.SH DESCRIPTION
+.I Mntgen
+serves 9P on its standard input, providing a name space containing a
+single directory that is initially empty, but in which a subdirectory name appears when
+is first referenced, and remains only as long as it is needed (referenced).
+.I Mntgen
+is typically mounted on a directory
+.I dir
+using
+.I mount
+(see
+.IR bind (1))
+as above.
+.I Dir
+is commonly
+.B /n
+or
+.BR /mnt ,
+to generate mount points on demand for file systems or services.
+.SH SOURCE
+.B /appl/cmd/mntgen.b
+.SH SEE ALSO
+.IR intro (1),
+.IR bind (1),
+.IR intro (5)
--- /dev/null
+++ b/man/4/namespace
@@ -1,0 +1,332 @@
+.TH NAMESPACE 4
+.SH NAME
+namespace \- structure of conventional Inferno name space
+.SH SYNOPSIS
+none
+.SH DESCRIPTION
+The list below gives an overview of the Inferno distribution
+file tree, organised into related categories.
+.TF /appl/cmd
+.TP
+.B /
+The root directory. To programs running outside Inferno,
+this corresponds to the directory in which Inferno has been
+installed (e.g.
+.B C:\einferno
+under Windows).
+.SS Mount points
+The following are all placeholders for filesystems
+that are mounted when Inferno is running. They
+contain no data files. Although an Inferno namespace
+is a dynamic entity, and devices can be mounted anywhere
+therein, many programs assume that devices have been
+mounted in the standard places, as suggested by the skeleton
+directories listed below.
+.TF /appl/cmd
+.TP
+.B /dev
+The standard mount point
+for devices (e.g.
+.IR cons (3))
+.TP
+.B /env
+The standard mount point for the
+.IR env (3)
+device.
+.TP
+.B /mnt
+A directory containing mount points for applications.
+.TP
+.B /chan
+An empty directory, used for holding
+files created with
+.IR sys-file2chan (2).
+.TP
+.B /net
+The standard mount point directory for network interfaces.
+.TP
+.B /n
+A directory containing mount points for file trees on local devices or imported from
+remote systems.
+.TP
+.B /prog
+An empty directory, the mount point for the
+.IR prog (3)
+device.
+.TP
+.B /nvfs
+An empty directory, the mount point for a non-volatile RAM
+filesystem on devices that have one.
+.TP
+.B /tmp
+Mount point for private
+directory of temporary files (eg,
+.BI /usr/ user /tmp\c
+).
+.TP
+.B /mail
+Conventional place to mount mailboxes.
+.SS Limbo applications
+.TF /appl/cmd
+.TP
+.B /dis
+Dis executables (commands)
+.TP
+.B /dis/lib
+Dis libraries
+.TP
+.B /dis/wm
+Dis commands that run under
+.IR wm (1).
+.TP
+.B /man
+Manual pages.
+.TP
+.B /doc
+Documentation other than manual pages.
+.TP
+.B /appl
+Source to Limbo applications.
+.TP
+.B /appl/cmd
+Source to the commands in
+.BR /dis
+(as documented in Section 1).
+.TP
+.B /appl/wm
+Source to the commands in
+.B /dis/wm
+.TP
+.B /appl/lib
+Source to the modules in
+.B /dis/lib
+(as documented in Section 2).
+.TP
+.B /module
+Limbo module declarations
+.SS Supporting data
+.TF /appl/cmd
+.TP
+.B /acme
+Programs and guide files specific to
+.IR acme (1).
+.TP
+.B /fonts
+Font definitions
+.TP
+.B /locale
+Timezone and locale information
+.TP
+.B /icons
+Contains
+.IR image (6)
+files used by programs.
+.TP
+.B /icons/tk
+Default directory searched by tk's
+.B -bitmap
+option (see
+.IR options (9)).
+.TP
+.B /lib
+Static program-specific data.
+.TP
+.B /lib/ndb
+Network configuration files
+used by
+.IR cs (8),
+.IR dns (8)
+and others.
+.SS Administration
+.TF /appl/cmd
+.TP
+.B /keydb
+Storage of secrets and certificates on signers (authentication servers).
+.TP
+.B /services
+A jungle of program-specific configuration files.
+.SS Platform specific
+.TF /appl/cmd
+.TP
+.BI / Platform
+Binaries specific to
+.IR Platform .
+Current platforms include
+.B Inferno
+(native binaries),
+.BR FreeBSD ,
+.BR Hp ,
+.BR Irix ,
+.BR Linux ,
+.BR Nt ,
+.BR Plan9 ,
+.B Solaris
+and
+.BR Unixware .
+.TP
+.BI / Platform / arch /bin
+.TP
+.BI / Platform / arch /lib
+.TP
+.BI / Platform / arch /include
+.I Platform
+specific binaries, libraries and include files
+respectively.
+.I Arch
+is the architecture type, as defined
+in
+.IR 2c (10.1)
+and held in the
+.B $objtype
+environment variable.
+.TP
+.B /usr
+A directory containing user directories.
+.SS Inferno source code
+.TF /libmemlayerxx
+.TP
+.B /emu
+Directory containing source specific to 
+.IR emu (1).
+.TP
+.B /emu/port
+Cross-platform source for
+.IR emu (1).
+.BI /emu/ Platform
+.IR Platform -specific
+source for
+.IR emu (1).
+.TP
+.B /libkfs
+The emu version of
+.IR kfs (3).
+.TP
+.B /libbio
+.TP
+.B /libregexp
+Source to libraries used by hosted commands.
+.TP
+.B /lib9
+Source to the Plan 9 emulation library, used by
+emu and the hosted commands.
+.TP
+.B /libmemdraw
+.TP
+.B /libmemlayer
+.TP
+.B /libprefab
+.TP
+.B /libkern
+.TP
+.B /libkeyring
+.TP
+.B /libdraw
+.TP
+.B /libinterp
+.TP
+.B /libtk
+Inferno source used by both native and hosted versions of
+Inferno.
+.TP
+.B /asm
+.TP
+.B /limbo
+Source to the two hosted Inferno commands of
+the same name.
+.TP
+.B /utils
+Source to hosted utilities run from
+.IR emu (1)
+via the
+.IR cmd (3)
+interface.
+.TP
+.B /tools
+A directory containing source directories
+for hosted tools used in building Inferno
+(e.g.
+.IR mk (10.1)).
+.TP
+.B /os
+A directory holding source directories for the native versions of Inferno.
+.TP
+.B /os/init
+Limbo source for platform-specific initialisation procedures.
+.TP
+.B /os/port
+Portable native kernel source.
+.TP
+.BI /os/ arch
+.IR Arch -specific
+native kernel source.
+.TP
+.B /os/kfs
+The native kernel version of
+.IR kfs (3).
+.SS "Minimal name space"
+The above is all very well on a system with lots of storage,
+but what is actually necessary for the running of Inferno?
+The following gives a quick summary of the structure that
+must be provided for Inferno to function correctly.
+.TF /appl/cmd
+.TP
+.B /dis
+This must contain Dis modules for all the applications
+you plan to run, and the modules they depend on.
+.IR Disdep (1)
+can be useful when trying to determine this set.
+.TP
+.B /dev
+.TP
+.B /env
+.TP
+.B /chan
+.TP
+.B /net
+.TP
+.B /prog
+.TP
+.B /tmp
+All empty unwritable directories, place holders for mounted services and
+applications.
+Often these are provided by the built-in
+.IR root (3).
+.TP
+.B /mnt
+A directory containing mount points for applications.
+.TP
+.B /n
+A directory containing mount points for remote file systems.
+.SS "Files needed to run as a server"
+.TF /appl/cmd
+.TP
+.B /keydb/keys
+See
+.IR keyfs (4),
+.IR logind (8)
+and
+.IR signer (8).
+.TP
+.B /keydb/signerkey
+See
+.IR createsignerkey (8)
+and
+.IR logind (8).
+.SS "Files needed to run the window manager"
+.TF /appl/cmd
+.TP
+.B /fonts
+At least one font must be provided - a default font for Tk
+to use.
+.TP
+.B /icons/tk
+This should contain icons used by applications that run within Tk.
+.TP
+.BI /user/ user
+At least one user directory must exist if
+.IR logon (1)
+is to function correctly.
+.SH SEE ALSO
+.IR intro (1),
+.IR root (3),
+.IR namespace (6)
--- /dev/null
+++ b/man/4/palmsrv
@@ -1,0 +1,49 @@
+.TH PALMSRV 4
+.SH NAME
+palmsrv \- packet link to Palm device on serial connection
+.SH SYNOPSIS
+.B palm/palmsrv
+[
+.B -D
+] [
+.BI -d " device"
+] [
+.BI -s " speed"
+]
+.SH DESCRIPTION
+.I Palmsrv
+serves packet link connections to a Palm device connected on the given
+.I device
+(default:
+.BR "/dev/eia0" )
+at the given
+.I speed
+(default:
+57600 baud).
+Client applications open
+.B /chan/palmsrv
+to obtain a connection.
+Each write must contain the data portion of a single Packet Assembly-Disassembly Protocol message
+containing a Desklink protocol request.
+If the device rejects the request, the write returns a diagnostic; otherwise,
+a subsequent read will retrieve the reply.
+.PP
+The server can be shut down by writing the text
+.B exit
+to the file
+.BR /chan/palmsrv :
+.IP
+.EX
+echo -n exit >/chan/palmsrv
+.EE
+.PP
+.SH FILES
+.B /chan/palmsrv
+.SH SOURCE
+.B /appl/cmd/palm/palmsrv.b
+.SH SEE ALSO
+.IR palmfile (2)
+.SH BUGS
+It does not yet support USB.
+.br
+It does not tickle the Palm when there are long delays.
--- /dev/null
+++ b/man/4/ramfile
@@ -1,0 +1,63 @@
+.TH RAMFILE 4
+.SH NAME
+ramfile \- synthesise file
+.SH SYNOPSIS
+.B ramfile
+.I file
+[
+.I data
+]
+.SH DESCRIPTION
+.I Ramfile
+puts a virtual file in the name space at
+.IR file ,
+and services file requests on it.
+Data written to the file is stored in
+.IR ramfile 's
+memory; read requests return the previously stored data.
+If the optional
+.I data
+argument is provided, the file will initially contain that text.
+The parent directory of
+.I file
+must exist;
+.I ramfile
+creates a union mount on that directory, and if
+.I file
+previously existed, the synthesised file will hide it.
+.PP
+.I Ramfile
+uses
+.IR sys-file2chan (2),
+which simplifies implementation but prevents it from knowing
+when the file has been truncated.
+Instead, it truncates the file on any write to the start of the file
+(offset 0),
+allowing
+.IR cp (1)
+to copy in new contents, and
+text editors to edit it.
+Indeed, the file will be usable by all applications that do
+not rely on updating parts of the file in place, or if they do,
+do not update the start of the file.
+.SH SOURCE
+.B /appl/cmd/ramfile.b
+.SH SEE ALSO
+.IR bind (1),
+.IR sys-file2chan (2),
+.IR srv (3),
+.IR memfs (4)
+.SH BUGS
+.I Ramfile
+cannot tell when the
+.I path
+has been unmounted;
+the only option is to
+.IR kill (1)
+it.
+.br
+The
+.I file
+always appears to have zero length to
+.IR sys-stat (2),
+which prevents loading a Dis file from it.
--- /dev/null
+++ b/man/4/registry
@@ -1,0 +1,205 @@
+.TH REGISTRY 4
+.SH NAME
+registry, regquery \- registration of services dynamically
+.SH SYNOPSIS
+.B ndb/registry
+[
+.BI -f " dbfile"
+]
+.PP
+.B ndb/regquery
+[
+.BI -m " mntpt"
+]
+[
+.B -n
+]
+[
+.I "attr value"
+] ...
+.SH DESCRIPTION
+.I Registry
+is a file server that
+provides registration and location of services by their attributes
+where services come and go.
+It was originally intended to register Grid resources
+but has much wider use.
+.I Registry
+serves 9P on its standard input.
+That can be mounted in the local name space (see below for an example),
+or served over a connection
+(eg, via
+.IR listen (1)).
+The conventional local mount point is
+.BR /mnt/registry ,
+which is where
+.IR registries (2)
+expects to find the local registry by default.
+There can, however, be any number of registries active,
+each supporting a different group of users and services,
+in different authentication domains.
+.PP
+.I Registry
+serves a one-level directory containing a few control files
+and one file per registered service.
+All the files are text files with similar format:
+zero or more lines of one or more words, separated by white space (blank or tab).
+A word can be quoted, using single quotes in the style of
+.IR sh (1),
+allowing it to contain white space
+or represent the empty string
+.BR \&'' .
+Within a quoted word, a single quote character is represented by two adjacent ones.
+(In programs, use
+.B String->quoted
+from
+.IR string (2),
+or more simply the
+.B %q
+string format of
+.IR sys-print (2)
+to quote strings appropriately.)
+.PP
+The control files are:
+.TF index
+.PD
+.TP
+.B new
+A text file that is opened and written to register a new service.
+Opening
+.B new
+returns a file descriptor to a new but anonymous service file.
+It must be written to set its name and initial description.
+The first write contains one or more words:
+.RS
+.IP
+.IR svcname " [" " attr value " ] ..."
+.PP
+The first word,
+.IR svcname ,
+is the proposed service name.
+The write returns an error if the name duplicates any existing name.
+Otherwise, a new file
+.I svcname
+will appear in the directory.
+The optional attribute value pairs form the initial service description.
+The service is removed from the register by default when the file is closed.
+To advertise a service without having to hold open a file descriptor, include an
+.I attr
+named
+.B persist
+with non-zero
+.IR value ;
+the service will then remain registered until explicitly removed (see below).
+.RE
+.TP
+.B index
+A read-only text file containing a list of all the currently-registered services, one per line.
+Each line contains a sequence of words.
+The first word is the service name and the remaining ones are paired: attribute name then value.
+.TP
+.B find
+A text file which should be opened for reading and writing.
+Each write provides a set of attribute/value pairs that acts as a filter, selecting only those
+services that possess all the given attributes with the given values;
+the value
+.B \&"*"
+matches any value.
+Subsequent reads return a list of services, in the same format
+as
+.BR index ,
+but listing only the selected services.
+The selection is made from those available at the time of the write.
+There is a separate filter for each open instance of
+.BR find ,
+so that several processes can set different filters simultaneously.
+(Note that after the write, the file offset must be set to zero using
+.IR sys-seek (2)
+before reading.)
+.TP
+.B event
+A read-only text file that can be used to detect when changes are made to the registry.
+Reading from it blocks until a change is made. At that point, the read completes and returns a
+string with a newline terminated decimal version number representing
+the number of changes made to the registry, which matches
+.B Qid.vers
+for the registry directory.
+Multiple changes are coalesced into one report.
+.PP
+A service file created by
+.B new
+may be read by anyone, but may be written only by its owner.
+Each write must contain a set of attribute/value pairs, formatted as above, which adds new attributes to the service
+description and changes the value of
+existing attributes.
+The file remains until the service is struck off the register.
+The owner of a service can remove its registration at any time by removing the corresponding
+service file (eg, using
+.IR rm (1)
+or
+.IR sys-remove (2)).
+.PP
+Typically, most services register themselves dynamically, perhaps using their locations (eg, network address)
+as their service names.
+.RI ( Registry
+itself does not interpret the name.)
+The
+.B -f
+option causes
+.I registry
+to load initially a set of static service descriptions from
+.IR dbfile ,
+which is in
+.IR attrdb (6)
+format.
+The database entry for each service contains the pair
+.BI service= svcname,
+causing
+.I registry
+to make an entry for the given
+.I svcname
+with attribute/value pairs initialised from the rest of that database entry.
+For example:
+.IP
+.EX
+service=net!click!1234
+	description='snapshot service' auth=none
+.EE
+.PP
+Static entries are regarded as persistent but can be removed explicitly by the registry owner.
+.PP
+.I Regquery
+looks for services in the registry mounted at
+.I mntpt
+(default:
+.BR /mnt/registry )
+that match all the given attribute/value pairs.
+It prints the resulting list of services, one per line.
+If the
+.B -n
+option is given,
+.I regquery
+prints only the service name(s); otherwise it prints the service description as well.
+If no attributes are given on the command line,
+.I regquery
+prompts for successive queries, one per line, and prints each result.
+.SH EXAMPLE
+Start a registry that appears at the standard location for local registries,
+.BR /mnt/registry :
+.IP
+.EX
+mount {ndb/registry} /mnt/registry
+.EE
+.PP
+Make that registry available to the network on authenticated connections:
+.IP
+.EX
+listen -v 'tcp!*!registry' {export /mnt/registry&}
+.EE
+.SH SOURCE
+.B /appl/cmd/ndb/registry.b
+.SH SEE ALSO
+.IR attrdb (2),
+.IR registries (2)
+.SH BUGS
+It is not currently possible to have two values with the same attribute name in the same service description.
--- /dev/null
+++ b/man/4/spree
@@ -1,0 +1,195 @@
+.TH SPREE 4
+.SH NAME
+spree \- distributed interactive sessions.
+.SH SYNOPSIS
+.B mount {spree/spree} /n/remote
+.PP
+.B /n/remote/name
+.br
+.BI /n/remote/ n
+.br
+.BI /n/remote/ n /ctl
+.SH DESCRIPTION
+.B Spree
+serves a file system that allows clients to interact
+through various types of
+.I engine
+(see
+.IR spree (2)
+for an explanation of engines).
+It serves styx messages through file descriptor 0;
+thus it can be mounted directly with
+.IR mount (1),
+or made available across the network
+with
+.I styxlisten
+(see
+.IR listen (1)).
+.PP
+Spree serves a name-space consisting of a directory
+for each clique that is currently active, and
+a file,
+.BR name ,
+that holds the authenticated name of the user that has
+mounted the spree namespace.
+A clique's directory holds at least one file,
+.BR ctl ;
+if a client opens this file, it can write to it
+communicate with the clique's engine, and read from
+it to find updates to the state of the clique.
+Messages written to the file are formed as text
+and the write yields an error if there is an error
+executing the command.
+The first message written to the file is the
+initial request to join the clique; conventionally it
+is the string
+.BR join ,
+but some engines accept other kinds of message
+(e.g.
+.BR watch ).
+If the initial request succeeds, the client will be
+informed of the current state of the clique by means
+of update messages read from the same file.
+Reading from the file will block until an update
+is available, whereupon the read request will return
+as many updates are available, separated by newline characters.
+If there are more updates available than can fit
+in the read request, the last two bytes of the buffer
+read will be a newline character and an asterisk
+.RB ( * )
+respectively, indicating that there are more updates
+to come.
+.PP
+When
+.I spree
+is first started, it creates one clique, a ``lobby''
+(see
+.IR spree-lobby (4))
+that allows other cliques to be created; this
+is named
+.BR 0 ).
+.PP
+A client cannot join a particular clique
+more than once.
+.PP
+A zero-length write to the
+.B ctl
+file causes any reads
+of that file from the same file descriptor to yield
+EOF (no bytes).
+This is necessary to force a hangup under
+systems such as Windows, where it is not possible
+to interrupt a kproc blocked on a network read.
+.PP
+The update messages generated by
+.I spree
+are as follows:
+.RS
+.TP
+.BI create " objid parentid visibility objtype"
+Create an object, identified by
+.IR objid ,
+at the end of
+.IR parentid 's
+children
+.RI ( parentid
+is
+.B -1
+for the root object).
+.I Visibility
+is non-zero if the object's children are visible
+to the member reading the update.
+.I Objtype
+is the object's type (engine dependent).
+.TP
+.BI tx " srcid dstid start end index"
+Transfer objects from
+.I srcid
+to
+.IR dstid.
+Take the objects from the range
+.RI [ start ,\  end )
+in the children of
+.IR srcid ,
+and insert them just before
+.I index
+in
+.IR dstid .
+When objects are transferred
+to an object that conceals its children,
+and the object is itself visible,
+the objects will first be transferred to the
+destination and then deleted; objects transferred
+out of such an object will first be created and
+.I then
+transferred to their destination.
+This enables a client to maintain some knowledge
+of where an object has been transferred to, even
+if the object is no longer visible, and means
+that a client is unable to keep track of objects
+that are concealed from it.
+.TP
+.BI del " parentid start end " "" objid\fR...
+Delete the range
+.RI [ start ,\  end )
+of children from the object identified by
+.IR parentid .
+.I Spree
+guarantees that those objects will themselves
+not have any children.
+The list of
+.IR objid s
+gives the actual identifiers of the objects deleted,
+for the benefit of clients that do not wish to keep lists of objects' children.
+.TP
+.BI set " objid attr val"
+Set the attribute named
+.I attr
+on object
+.I objid
+to
+.IR val .
+.TP
+.BI vis " objid visibility"
+The visibility of object to the reading member
+.I objid
+has changed to
+.I visibility
+(non-zero if visible).
+.TP
+.I action
+Game engines can generate arbitrary messages
+of their own devising; such messages are specific
+to particular engine types.
+.PP
+Note that a given client does not have to interpret
+all the above messages \- different client types
+have their own conventions. The
+.B card
+client type uses most of the above functionality,
+for example, whereas a client for the
+.B chat
+engine listed in
+.IR spree (2)
+can get away with interpreting only one message, the custom action
+.BR chat .
+.PP
+Writes to the opened clique file
+are interpreted as clique actions by
+the clique that has been loaded, and acted on accordingly.
+Invalid actions will draw a write error.
+.RE
+.SH EXAMPLE
+The simplest client!
+.PP
+.EX
+mount tcp!somehost.com!3242 /n/remote
+{
+	echo create chat >[1=0]
+	cat &
+	cat  >[1=0] < /dev/cons
+} <> /n/remote/new
+.SH SOURCE
+.B /appl/cmd/cliques/spree.b
+.SH SEE ALSO
+.IR spree (2)
--- /dev/null
+++ b/man/4/tarfs
@@ -1,0 +1,45 @@
+.TH TARFS 4
+.SH NAME
+tarfs \- mount tar archive
+.SH SYNOPSIS
+.B tarfs
+[
+.B -ab
+] [
+.B -p
+]
+.I tarfile
+.I mountpt
+.SH DESCRIPTION
+.I Tarfs
+makes the contents of the directories and files archived in the given Unix/POSIX
+.I tar
+archive
+.I tarfile
+visible in the name space under
+.IR mountpt .
+They can then be accessed in an obvious way by other commands
+such as
+.IR ls (1)
+and
+.IR cp (1).
+Only read access is supported.
+User and group names are taken from the ``ustar'' headers when present.  Otherwise, the numeric user and group IDs are used.
+The
+.B -a
+and
+.B -b
+options cause the
+.IR tarfile 's
+name space to be placed after or before the current contents of
+.IR mountpt ;
+the default is to replace that directory's contents.
+Option
+.B -p
+causes permissions in the tar file to be discarded.
+.SH SOURCE
+.B /appl/cmd/tarfs.b
+.SH SEE ALSO
+.IR gettar (1),
+.IR archfs (4),
+.IR intro (5)
--- /dev/null
+++ b/man/4/trfs
@@ -1,0 +1,22 @@
+.TH TRFS 4
+.SH NAME
+trfs \- translate spaces and other runes in names in a file system
+.SH SYNOPSIS
+.B trfs
+.I olddir
+.I mountdir
+.SH DESCRIPTION
+.I Trfs
+makes the contents of directory
+.I olddir
+appear on the directory
+.IR mountdir ,
+but every space character in file and directory names viewed through
+.I mountdir
+is made visible as a special `no-break space' character (U+00A0).
+Other Inferno applications will not mistake it for the end of a file name,
+and it has some visible representation in most fonts.
+.SH SOURCE
+.B /appl/cmd/trfs.b
+.SH BUGS
+There can be error or ambiguity if the existing tree also uses no-break space in its names.
--- /dev/null
+++ b/man/4/vacfs
@@ -1,0 +1,47 @@
+.TH VACFS 4
+.SH NAME
+vacfs \- mount venti archive
+.SH SYNOPSIS
+.B vacfs
+[
+.B -Ddp
+] [
+.B -a
+.I addr
+]
+.I [vac:score]
+.SH DESCRIPTION
+.I Vacfs
+makes the contents of a venti archive available over styx.  Standard input is used for reading and writing styx messages.
+If
+.I score
+is not specified, vacfs
+serves multiple venti archives.  In this case the root directory lists no files, but walking to a directory
+.I score
+opens the venti archive with that score.  Note that the vacfs does not support writing.
+.TP
+.B -D
+Print styx trace messages.
+.TP
+.B -d
+Print debug messages.
+.TP
+.B -p
+Disable permission checking.
+.TP
+.BI -a " address"
+Dial
+.I address
+instead of the default venti server.
+.SH SOURCE
+.B /appl/cmd/vacfs.b
+.SH SEE ALSO
+.IR vacget (1),
+.IR vacput (1),
+.IR vcache (1),
+.IR venti (2),
+.IR ventisrv (8)
+.SH BUGS
+Vacfs needs more testing.
+.br
+When the venti connection is broken, directories appear empty.
--- /dev/null
+++ b/man/5/0intro
@@ -1,0 +1,647 @@
+.TH INTRO 5 
+.SH NAME
+intro \- introduction to the Plan 9 File Protocol 9P in Inferno
+.SH SYNOPSIS
+.B #include <lib9.h>
+.br
+.B #include <styx.h>
+.SH DESCRIPTION
+Inferno uses the Plan 9 protocol 9P to make resources visible as file hierarchies.
+There have been two versions of 9P, the most recent being 9P2000.
+Inferno originally used its own protocol called
+.IR Styx ,
+which was a simplified version of the first 9P protocol.
+When 9P was subsequently revised as 9P2000, Inferno adopted that, but still using the name
+.IR Styx .
+To reduce confusion and to emphasise that Inferno and Plan 9 use the same protocol,
+the name
+.I Styx
+is being replaced by 9P.
+References to `Styx' remain in the programs and documentation,
+but will eventually be eliminated.
+.PP
+An Inferno
+.I server
+is an agent that provides one or more hierarchical file systems
+\(em file trees \(em
+that may be accessed by Inferno processes.
+A server responds to requests by
+.I clients
+to navigate the hierarchy,
+and to create, remove, read, and write files.
+The prototypical server is a separate machine that stores
+large numbers of user files on permanent media.
+Another possibility for a server is to synthesize
+files on demand, perhaps based on information on data structures
+inside the kernel; the
+.IR prog (3)
+.I kernel device
+is a part of the Inferno kernel that does this.
+User programs can also act as servers.
+One easy way is to serve a set of files
+using the
+.IR sys-file2chan (2)
+interface.
+More complex Limbo file service applications can use
+.IR styx (2)
+to handle the protocol messages directly,
+or use
+.IR styxservers (2)
+to provide a higher-level framework for file serving.
+.PP
+A
+.I connection
+to a server is a bidirectional communication path from the client to the server.
+There may be a single client or
+multiple clients sharing the same connection.
+A server's file tree is attached to a process
+group's name space by
+.IR bind
+and
+.I mount
+calls;
+see
+.IR intro (2)
+and
+.IR sys-bind (2).
+Processes in the group are then clients
+of the server:
+system calls operating on files are translated into requests
+and responses transmitted on the connection to the appropriate service.
+.PP
+The
+.IR "Plan 9 File Protocol" ,
+9P, is used for messages between
+.I clients
+and
+.IR servers .
+A client transmits
+.I requests
+.RI ( T-messages )
+to a server, which
+subsequently returns
+.I replies
+.RI ( R-messages )
+to the client.
+The combined acts of transmitting (receiving) a request of a particular type,
+and receiving (transmitting)
+its reply is called a
+.I transaction
+of that type.
+.PP
+Each message consists of a sequence of bytes.
+Two-, four-, and eight-byte fields hold unsigned
+integers represented in little-endian order
+(least significant byte first).
+Data items of larger or variable lengths are represented
+by a two-byte field specifying a count,
+.IR n ,
+followed by
+.I n
+bytes of data.
+Text strings are represented this way,
+with the text itself stored as a UTF-8
+encoded sequence of Unicode characters (see
+.IR utf (6)).
+Text strings in 9P messages are not
+.SM NUL\c
+-terminated:
+.I n
+counts the bytes of UTF-8 data, which include no final zero byte.
+The
+.SM NUL
+character is illegal in all text strings in 9P, and is therefore
+excluded from file names, user names, and so on.
+.PP
+Each 9P message begins with a four-byte size field
+specifying the length in bytes of the complete message including
+the four bytes of the size field itself.
+The next byte is the message type, one of the constants
+in the module
+.IR styx (2),
+and in the C include file
+.BR <styx.h>
+(see
+.IR styx (10.2)).
+The next two bytes are an identifying
+.IR tag ,
+described below.
+The remaining bytes are parameters of different sizes.
+In the message descriptions, the number of bytes in a field
+is given in brackets after the field name.
+The notation
+.IR parameter [ n ]
+where
+.I n
+is not a constant represents a variable-length parameter:
+.IR n [2]
+followed by
+.I n
+bytes of data forming the
+.IR parameter .
+The notation
+.IR string [ s ]
+(using a literal
+.I s
+character)
+is shorthand for
+.IR s [2]
+followed by
+.I s
+bytes of UTF-8 text.
+(Systems may choose to reduce the set of legal characters
+to reduce syntactic problems,
+for example to remove slashes from name components,
+but the protocol has no such restriction.
+Inferno names may contain any printable character (that is, any character
+outside hexadecimal 00-1F and 80-9F)
+except slash.)
+Messages are transported in byte form to allow for machine independence;
+.IR styx (10.2)
+describes routines that convert to and from this form into a machine-dependent
+C structure.
+.SH MESSAGES
+.ta \w'\fLTsession 'u
+.IP
+.ne 2v
+.IR size [4]
+.B Tversion
+.IR tag [2]
+.IR msize [4]
+.IR version [ s ]
+.br
+.IR size [4]
+.B Rversion
+.IR tag [2]
+.IR msize [4]
+.IR version [ s ]
+.IP
+.ne 2v
+.IR size [4]
+.B Tauth
+.IR tag [2]
+.IR afid [4]
+.IR uname [ s ]
+.IR aname [ s ]
+.br
+.br
+.IR size [4]
+.B Rauth
+.IR tag [2]
+.IR aqid [13]
+.IP
+.ne 2v
+.IR size [4]
+.B Rerror
+.IR tag [2]
+.IR ename [ s ]
+.IP
+.ne 2v
+.IR size [4]
+.B Tflush
+.IR tag [2]
+.IR oldtag [2]
+.br
+.IR size [4]
+.B Rflush
+.IR tag [2]
+.IP
+.ne 2v
+.IR size [4]
+.B Tattach
+.IR tag [2]
+.IR fid [4]
+.IR afid [4]
+.IR uname [ s ]
+.IR aname [ s ]
+.br
+.IR size [4]
+.B Rattach
+.IR tag [2]
+.IR qid [13]
+.IP
+.ne 2v
+.IR size [4]
+.B Twalk
+.IR tag [2]
+.IR fid [4]
+.IR newfid [4]
+.IR nwname [2]
+.IR nwname *( wname [ s ])
+.br
+.IR size [4]
+.B Rwalk
+.IR tag [2]
+.IR nwqid [2]
+.IR nwqid *( wqid [13])
+.IP
+.ne 2v
+.IR size [4]
+.B Topen
+.IR tag [2]
+.IR fid [4]
+.IR mode [1]
+.br
+.IR size [4]
+.B Ropen
+.IR tag [2]
+.IR qid [13]
+.IR iounit [4]
+.IP
+.ne 2v
+.IR size [4]
+.B Tcreate
+.IR tag [2]
+.IR fid [4]
+.IR name [ s ]
+.IR perm [4]
+.IR mode [1]
+.br
+.IR size [4]
+.B Rcreate
+.IR tag [2]
+.IR qid [13]
+.IR iounit [4]
+.IP
+.ne 2v
+.IR size [4]
+.B Tread
+.IR tag [2]
+.IR fid [4]
+.IR offset [8]
+.IR count [4]
+.br
+.IR size [4]
+.B Rread
+.IR tag [2]
+.IR count [4]
+.IR data [ count ]
+.IP
+.ne 2v
+.IR size [4]
+.B Twrite
+.IR tag [2]
+.IR fid [4]
+.IR offset [8]
+.IR count [4]
+.IR data [ count ]
+.br
+.IR size [4]
+.B Rwrite
+.IR tag [2]
+.IR count [4]
+.IP
+.ne 2v
+.IR size [4]
+.B Tclunk
+.IR tag [2]
+.IR fid [4]
+.br
+.IR size [4]
+.B Rclunk
+.IR tag [2]
+.IP
+.ne 2v
+.IR size [4]
+.B Tremove
+.IR tag [2]
+.IR fid [4]
+.br
+.IR size [4]
+.B Rremove
+.IR tag [2]
+.IP
+.ne 2v
+.IR size [4]
+.B Tstat
+.IR tag [2]
+.IR fid [4]
+.br
+.IR size [4]
+.B Rstat
+.IR tag [2]
+.IR stat [ n ]
+.IP
+.ne 2v
+.IR size [4]
+.B Twstat
+.IR tag [2]
+.IR fid [4]
+.IR stat [ n ]
+.br
+.IR size [4]
+.B Rwstat
+.IR tag [2]
+.PP
+Each T-message has a
+.I tag
+field, chosen and used by the client to identify the message.
+The reply to the message will have the same tag.
+Clients must arrange that no two outstanding messages
+on the same connection have the same tag.
+An exception is the tag
+.BR NOTAG ,
+defined as
+.B 16rFFFF
+in
+.IR styx (2)
+and
+.IR styx (10.2):
+the client can use it, when establishing a connection,
+to
+override tag matching in
+.B version
+messages.
+.PP
+The type of an R-message will either be one greater than the type
+of the corresponding T-message or
+.BR Rerror ,
+indicating that the request failed.
+In the latter case, the
+.I ename
+field contains a string describing the reason for failure.
+.PP
+The
+.B version
+message identifies the version of the protocol and indicates
+the maximum message size the system is prepared to handle.
+It also initializes the connection and
+aborts all outstanding I/O on the connection.
+The set of messages between
+.B version
+requests is called a
+.IR session .
+.PP
+Most T-messages contain a
+.IR fid ,
+a 32-bit unsigned integer that the client uses to identify
+a ``current file'' on the server.
+Fids are somewhat like file descriptors in a user process,
+but they are not restricted to files open for I/O:
+directories being examined, files being accessed by
+.IR sys-stat (2)
+calls, and so on \(em all files being manipulated by the operating
+system \(em are identified by fids.
+Fids are chosen by the client.
+All requests on a connection share the same fid space;
+when several clients share a connection,
+the agent managing the sharing must arrange
+that no two clients choose the same fid.
+.PP
+The fid supplied in an
+.B attach
+message
+will be taken by the server to refer to the root of the served file tree.
+The
+.B attach
+identifies the user
+to the server and may specify a particular file tree served
+by the server (for those that supply more than one).
+.PP
+Permission to attach to the service is proven by providing a special fid, called
+.BR afid ,
+in the
+.B attach
+message.  This
+.B afid
+is established by exchanging
+.B auth
+messages and subsequently manipulated using
+.B read
+and
+.B write
+messages to exchange authentication information not defined explicitly by 9P.
+Once the authentication protocol is complete, the
+.B afid
+is presented in the
+.B attach
+to permit the user to access the service.
+.PP
+A
+.B walk
+message causes the server to change the current file associated
+with a fid to be a file in the directory that is the old current file, or one of
+its subdirectories.
+.B Walk
+returns a new fid that refers to the resulting file.
+Usually, a client maintains a fid for the root,
+and navigates by
+.B walks
+from the root fid.
+.PP
+A client can send multiple T-messages without waiting for the corresponding
+R-messages, but all outstanding T-messages must specify different tags.
+The server may delay the response to a request
+and respond to later ones;
+this is sometimes necessary, for example when the client reads
+from a file that the server synthesizes from external events
+such as keyboard characters.
+.PP
+Replies (R-messages) to
+.BR auth ,
+.BR attach ,
+.BR walk ,
+.BR open ,
+and
+.B create
+requests convey a
+.I qid
+field back to the client.
+The qid represents the server's unique identification for the
+file being accessed:
+two files on the same server hierarchy are the same if and only if their qids
+are the same.
+(The client may have multiple fids pointing to a single file on a server
+and hence having a single qid.)
+The thirteen-byte qid fields hold a one-byte type,
+specifying whether the file is a directory, append-only file, etc.,
+and two unsigned integers:
+first the four-byte qid
+.IR version ,
+then the eight-byte qid
+.IR path .
+The path is an integer unique among all files in the hierarchy.
+If a file is deleted and recreated with the
+same name in the same directory, the old and new path components of the qids
+should be different.
+The version is a version number for a file;
+typically, it is incremented every time the file is modified.
+.PP
+An existing file can be
+.BR opened ,
+or a new file may be
+.B created
+in the current (directory) file.
+I/O of a given number of bytes
+at a given offset
+on an open file is done by
+.B read
+and
+.BR write .
+.PP
+A client should
+.B clunk
+any fid that is no longer needed.
+The
+.B remove
+transaction deletes files.
+.PP
+The
+.B stat
+transaction retrieves information about the file.
+The
+.I stat
+field in the reply includes the file's
+name,
+access permissions (read, write and execute for owner, group and public),
+access and modification times, and
+owner and group identifications
+(see
+.IR sys-stat (2)).
+The owner and group identifications are textual names.
+The
+.B wstat
+transaction allows some of a file's properties
+to be changed.
+.PP
+A request can be aborted with a
+flush
+request.
+When a server receives a
+.BR Tflush ,
+it should not reply to the message with tag
+.I oldtag
+(unless it has already replied),
+and it should immediately send an
+.BR Rflush .
+The client must wait
+until it gets the
+.B Rflush
+(even if the reply to the original message arrives in the interim),
+at which point
+.I oldtag
+may be reused.
+.PP
+Because the message size is negotiable and some elements of the
+protocol are variable length, it is possible (although unlikely) to have
+a situation where a valid message is too large to fit within the negotiated size.
+For example, a very long file name may cause a
+.B Rstat
+of the file or
+.B Rread
+of its directory entry to be too large to send.
+In most such cases, the server should generate an error rather than
+modify the data to fit, such as by truncating the file name.
+The exception is that a long error string in an
+.B Rerror
+message should be truncated if necessary, since the string is only
+advisory and in some sense arbitrary.
+.PP
+Most programs do not see the 9P protocol directly; instead calls to library
+routines that access files are
+translated by the mount driver,
+.IR mnt (3),
+into 9P messages.
+.SH DIRECTORIES
+Directories are created by
+.B create
+with
+.B DMDIR
+set in the permissions argument (see
+.IR stat (5)).
+The members of a directory can be found with
+.IR read (5).
+All directories must support
+.B walks
+to the directory
+.B ..
+(dot-dot)
+meaning parent directory, although by convention directories
+contain no explicit entry for
+.B ..
+or
+.B .
+(dot).
+The parent of the root directory of a server's tree is itself.
+.SH "ACCESS PERMISSIONS"
+Each file server maintains a set of user and group names.
+Each user can be a member of any number of groups.
+Each group has a
+.I group leader
+who has special privileges (see
+.IR stat (5)).
+Every file request has an implicit user id (copied from the original
+.BR attach )
+and an implicit set of groups (every group of which the user is a member).
+.PP
+Each file has an associated
+.I owner
+and
+.I group
+id and
+three sets of permissions:
+those of the owner, those of the group, and those of ``other'' users.
+When the owner attempts to do something to a file, the owner, group,
+and other permissions are consulted, and if any of them grant
+the requested permission, the operation is allowed.
+For someone who is not the owner, but is a member of the file's group,
+the group and other permissions are consulted.
+For everyone else, the other permissions are used.
+Each set of permissions says whether reading is allowed,
+whether writing is allowed, and whether executing is allowed.
+A
+.B walk
+in a directory is regarded as executing the directory,
+not reading it.
+Permissions are kept in the low-order bits of the file
+.IR mode :
+owner read/write/execute permission represented as 1 in bits 8, 7, and 6
+respectively (using 0 to number the low order).
+The group permissions are in bits 5, 4, and 3,
+and the other permissions are in bits 2, 1, and 0.
+.PP
+The file
+.I mode
+contains some additional attributes besides the permissions.
+If bit 31
+.RB ( DMDIR )
+is set, the file is a directory;
+if bit 30
+.RB ( DMAPPEND )
+is set, the file is append-only (offset is ignored in writes);
+if bit 29
+.RB ( DMEXCL )
+is set, the file is exclusive-use (only one client may have it
+open at a time);
+if bit 27
+.RB ( DMAUTH )
+is set, the file is an authentication file established by
+.B auth
+messages;
+if bit 26
+.RB ( DMTMP )
+is set, the contents of the file (or directory) are not included in nightly archives.
+(Bit 28 is skipped for historical reasons.)
+These bits are reproduced, from the top bit down, in the type byte of the Qid:
+.BR QTDIR ,
+.BR QTAPPEND ,
+.BR QTEXCL ,
+(skipping one bit)
+.BR QTAUTH ,
+and
+.BR QTTMP .
+The name
+.BR QTFILE ,
+defined to be zero,
+identifies the value of the type for a plain file.
+.SH SEE ALSO
+.IR intro (2),
+.IR styx (2),
+.IR styxservers (2),
+.IR sys-bind (2),
+.IR sys-stat (2),
+.IR mnt (3),
+.IR prog (3),
+.IR read (5),
+.IR stat (5),
+.IR styx (10.2)
--- /dev/null
+++ b/man/5/INDEX
@@ -1,0 +1,15 @@
+intro 0intro
+attach attach
+auth attach
+clunk clunk
+error error
+flush flush
+create open
+open open
+read read
+write read
+remove remove
+stat stat
+wstat stat
+version version
+walk walk
--- /dev/null
+++ b/man/5/attach
@@ -1,0 +1,159 @@
+.TH ATTACH 5 
+.SH NAME
+attach, auth \- messages to establish a connection
+.SH SYNOPSIS
+.ta \w'\fLTauth 'u
+.IR size [4]
+.B Tauth
+.IR tag [2]
+.IR afid [4]
+.IR uname [ s ]
+.IR aname [ s ]
+.br
+.IR size [4]
+.B Rauth
+.IR tag [2]
+.IR aqid [13]
+.PP
+.IR size [4]
+.B Tattach
+.IR tag [2]
+.IR fid [4]
+.IR afid [4]
+.IR uname [ s ]
+.IR aname [ s ]
+.br
+.IR size [4]
+.B Rattach
+.IR tag [2]
+.IR qid [13]
+.SH DESCRIPTION
+.PP
+The
+.B attach
+message serves as a fresh introduction from a user on
+the client machine to the server.
+The message identifies the user
+.RI ( uname )
+and may select
+the file tree to access
+.RI ( aname ).
+The
+.I afid
+argument specifies a fid previously established by an
+.B auth
+message, as described below.
+.PP
+As a result of the
+.B attach
+transaction, the client will have a connection to the root
+directory of the desired file tree,
+represented by
+.IR fid .
+An error is returned if
+.I fid
+is already in use.
+The server's idea of the root of the file tree is represented by the returned
+.IR qid .
+.PP
+If the client does not wish to authenticate the connection, or knows that
+authentication is not required, the
+.I afid
+field in the
+.B attach
+message should be set to
+.BR NOFID ,
+defined as
+.B (u32int)~0
+in
+.BR <fcall.h> .
+If the client does wish to authenticate, it must acquire and validate an
+.I afid
+using an
+.B auth
+message before doing the
+.BR attach .
+.PP
+The
+.B auth
+message contains
+.IR afid ,
+a new fid to be established for authentication, and the
+.I uname
+and
+.I aname
+that will be those of the following
+.B attach
+message.
+If the server does not require authentication, it returns
+.B Rerror
+to the
+.B Tauth
+message.
+.PP
+If the server does require authentication, it returns
+.I aqid
+defining a file of type
+.B QTAUTH
+(see
+.IR intro (5))
+that may be read and written (using
+.B read
+and
+.B write
+messages in the usual way) to execute an authentication protocol.
+That protocol's definition is not part of 9P itself.
+.PP
+Once the protocol is complete, the same
+.I afid
+is presented in the
+.B attach
+message for the user, granting entry.
+The same validated
+.I afid
+may be used for multiple
+.B attach
+messages with the same
+.I uname
+and
+.IR aname .
+.SH ENTRY POINTS
+An
+.B attach
+transaction will be generated for kernel devices
+(see
+.IR intro (3))
+when a system call evaluates a file name
+beginning with
+.LR # .
+.IR Sys-pipe (2)
+generates an attach on the kernel device
+.IR pipe (3).
+The
+.B mount
+call (see
+.IR sys-bind (2))
+generates an
+.B attach
+message to the remote file server.
+When the kernel boots, an
+.I attach
+is made to the root device,
+.IR root (3),
+and then an
+.B attach
+is made to the requested file server machine.
+.PP
+An
+.B auth
+transaction is generated by the
+.IR sys-fauth (2)
+system call or by the first
+.B mount
+system call on an uninitialized connection.
+.SH SEE ALSO
+.IR sys-bind (2),
+.IR sys-fauth (2),
+.IR intro (3),
+.IR root (3),
+.IR version (5)
--- /dev/null
+++ b/man/5/clunk
@@ -1,0 +1,44 @@
+.TH CLUNK 5 
+.SH NAME
+clunk \- forget about a fid
+.SH SYNOPSIS
+.ta \w'\fLTclunk 'u
+.IR size [4]
+.B Tclunk
+.IR tag [2]
+.IR fid [4]
+.br
+.IR size [4]
+.B Rclunk
+.IR tag [2]
+.SH DESCRIPTION
+The
+.B clunk
+request informs the file server
+that the current file represented by
+.I fid 
+is no longer needed by the client.
+The actual file is not removed on the server unless the fid had been opened with
+.BR ORCLOSE .
+.PP
+Once a fid has been clunked,
+the same fid can be reused in a new
+.B walk
+or
+.B attach
+request.
+.PP
+Even if the
+.B clunk
+returns an error, the
+.I fid
+is no longer valid.
+.SH ENTRY POINTS
+A
+.B clunk
+message is generated when a file descriptor is closed (usually by the garbage collector)
+and indirectly by other actions such as failed
+.I open
+calls.
+.SH SEE ALSO
+.IR sys-open (2)
--- /dev/null
+++ b/man/5/error
@@ -1,0 +1,25 @@
+.TH ERROR 5 
+.SH NAME
+error \- return an error
+.SH SYNOPSIS
+.ta \w'\fLRerror 'u
+.IR size [4]
+.B Rerror
+.IR tag [2]
+.IR ename [ s ]
+.SH DESCRIPTION
+The
+.B Rerror
+message
+(there is no
+.BR Terror )
+is used to return an error string
+describing the
+failure of a transaction.
+It replaces the corresponding reply message
+that would accompany a successful call; its tag is that
+of the failing request.
+.PP
+By convention, clients may truncate error messages after
+.B Sys->ERRMAX-1
+bytes.
--- /dev/null
+++ b/man/5/flush
@@ -1,0 +1,101 @@
+.TH FLUSH 5
+.SH NAME
+flush \- abort a message
+.SH SYNOPSIS
+.ta \w'\fLTflush 'u
+.IR size [4]
+.B Tflush
+.IR tag [2]
+.IR oldtag [2]
+.br
+.IR size [4]
+.B Rflush
+.IR tag [2]
+.SH DESCRIPTION
+When the response to a request is no longer needed, such as when
+a user interrupts a process doing a
+.IR sys-read (2),
+a
+.B Tflush
+request is sent to the server to purge the pending response.
+The message being flushed is identified by
+.IR oldtag .
+The semantics of
+.B flush
+depends on messages arriving in order.
+.PP
+The server should answer the
+.B flush
+message immediately.
+If it recognizes
+.I oldtag
+as the tag of a pending transaction, it should abort any pending response
+and discard that tag.
+In either case, it should respond with an
+.B Rflush
+echoing the
+.I tag
+(not
+.IR oldtag )
+of the
+.B Tflush
+message.
+A
+.B Tflush
+can never be responded to by an
+.B Rerror
+message.
+.PP
+The server may respond to the pending request before
+responding to the
+.BR Tflush .
+It is possible for a client to send multiple
+.B Tflush
+messages for a particular pending request.  Each
+subsequent
+.B Tflush
+must contain as
+.I oldtag
+the tag of the pending request (not a previous
+.BR Tflush ).
+Should multiple
+.BR Tflush es
+be received for a pending request, they must be answered in
+order.  An
+.B Rflush
+for any of the multiple
+.BR Tflush es
+implies an answer for all previous ones.  Therefore, should
+a server receive a request and then multiple flushes for that
+request, it need respond only to the last flush.
+.PP
+When the client sends a 
+.BR Tflush ,
+it must wait to receive the corresponding
+.B Rflush
+before reusing
+.I oldtag
+for subsequent messages.
+If a response to the flushed request is received before the
+.BR Rflush ,
+the client must honor the response
+as if it had not been flushed,
+since the completed request may signify a state change in the server.
+For instance,
+.B Tcreate
+may have created a file and
+.B Twalk
+may have allocated a fid.
+If no response is received before the
+.BR Rflush ,
+the flushed transaction is considered to have been cancelled,
+and should be treated as though it had never been sent.
+.PP
+Several exceptional conditions are handled correctly by the above specification:
+sending multiple flushes for a single tag,
+flushing after a transaction is completed,
+flushing a
+.BR Tflush ,
+and flushing an invalid tag.
+.SH SEE ALSO
+.IR sys-read (2)
--- /dev/null
+++ b/man/5/open
@@ -1,0 +1,250 @@
+.TH OPEN 5 
+.SH NAME
+open, create \- prepare a fid for I/O on an existing or new file
+.SH SYNOPSIS
+.ta \w'\fLTcreate 'u
+.IR size [4]
+.B Topen
+.IR tag [2]
+.IR fid [4]
+.IR mode [1]
+.br
+.IR size [4]
+.B Ropen
+.IR tag [2]
+.IR qid [13]
+.IR iounit [4]
+.PP
+.IR size [4]
+.B Tcreate
+.IR tag [2]
+.IR fid [4]
+.IR name [ s ]
+.IR perm [4]
+.IR mode [1]
+.br
+.IR size [4]
+.B Rcreate
+.IR tag [2]
+.IR qid [13]
+.IR iounit [4]
+.SH DESCRIPTION
+The
+.B open
+request asks the file server to check permissions and
+prepare a fid for I/O with subsequent
+.B read
+and
+.B write
+messages.
+The
+.I mode
+field determines the type of I/O:
+0
+(called
+.BR OREAD
+in
+.BR Sys ),
+1
+.RB ( OWRITE ),
+2
+.RB ( ORDWR ),
+and 3
+.RB ( OEXEC )
+mean
+.I
+read access, write access, read and write access,
+and
+.I
+execute access,
+to be checked against the permissions for the file.
+In addition, if
+.I mode
+has the
+.B OTRUNC
+.RB ( 16r10 )
+bit set,
+the file is to be truncated, which requires write permission
+(if
+the file is append-only, and permission is granted, the
+.B open
+succeeds but the file will not be truncated);
+if the
+.I mode
+has the
+.B ORCLOSE
+.RB ( 16r40 )
+bit set, the file is to be removed when the fid
+is clunked, which requires permission to remove the file from its directory.
+All other bits in
+.I mode
+should be zero.
+It is illegal to write a directory, truncate it, or attempt to remove it on close.
+If the file is marked for exclusive use (see
+.IR stat (5)),
+only one client can have the file open at any time.
+That is, after such a file has been opened,
+further opens will fail until
+.I fid
+has been clunked.
+All these permissions are checked at the time of the
+.B open
+request; subsequent changes to the permissions of files do not affect
+the ability to read, write, or remove an open file.
+.PP
+The 
+.B create
+request asks the file server to create a new file
+with the 
+.I name
+supplied, in the directory
+.RI ( dir )
+represented by
+.IR fid ,
+and requires write permission in the directory.
+The owner of the file is the implied user id of the request,
+the group of the file is the same as
+.IR dir ,
+and the permissions are the value of
+.ce
+.B "perm & (~8r666 | (dir.perm & 8r666))"
+if a regular file is being created and
+.ce
+.B "perm & (~8r777 | (dir.perm & 8r777))"
+if a directory is being created.
+This means, for example, that if the
+.B create
+allows read permission to others, but the containing directory
+does not, then the created file will not allow others to read the file.
+.PP
+Finally, the newly created file is opened according to
+.IR mode ,
+and
+.I fid
+will represent the newly opened file.
+.I Mode
+is not checked against the permissions in
+.IR perm .
+The
+.I qid
+for the new file is returned with the
+.B create
+reply message.
+.PP
+Directories are created by setting the
+.B DMDIR
+bit
+.RB ( 16r80000000 )
+in the
+.IR perm .
+.PP
+The names
+.B .
+and
+.B ..
+are special; it is illegal to create files with these names.
+.PP
+It is an error for either of these messages if the fid
+is already the product of a successful
+.B open
+or
+.B create
+message.
+.PP
+An attempt to
+.B create
+a file in a directory where the given
+.I name
+already exists will be rejected;
+in this case, the
+.I create
+system call
+(see
+.IR sys-open (2))
+uses
+.B open
+with truncation.
+The algorithm used by the
+.IR create
+system call
+is:
+first walk to the directory to contain the file.
+If that fails, return an error.
+Next
+.B walk
+to the specified file.
+If the
+.B walk
+succeeds, send a request to
+.B open
+and truncate the file and return the result, successful or not.
+If the
+.B walk
+fails, send a create message.
+If that fails, it may be because the file was created by another
+process after the previous walk failed, so (once) try the
+.B walk
+and
+.B open
+again.
+.PP
+For the behavior of
+.I create
+on a union directory, see
+.IR sys-bind (2).
+.PP
+The
+.B iounit
+field returned by
+.B open
+and
+.B create
+may be zero.
+If it is not, it is the maximum number of bytes that are guaranteed to
+be read from or written to the file without breaking the I/O transfer
+into multiple 9P messages; see
+.IR read (5).
+.SH ENTRY POINTS
+.I Open
+and
+.I create
+both generate
+.B open
+messages; only
+.I create
+generates a
+.B create
+message.
+The
+.B iounit
+associated with an open file may be discovered by calling
+.IR sys-iounit (2).
+.PP
+For programs that need atomic file creation, without the race
+that exists in the
+.B open-create
+sequence described above,
+the kernel does the following.
+If the
+.B OEXCL
+.RB ( 16r1000 )
+bit is set in the
+.I mode
+for a
+.B create
+system call,
+the
+.B open
+message is not sent; the kernel issues only the
+.BR create .
+Thus, if the file exists,
+.B create
+will draw an error, but if it doesn't and the
+.B create
+system call succeeds, the process issuing the
+.B create
+is guaranteed to be the one that created the file.
+.SH SEE ALSO
+.IR sys-bind (2),
+.IR sys-open (2),
+.IR stat (5)
--- /dev/null
+++ b/man/5/read
@@ -1,0 +1,124 @@
+.TH READ 5 
+.SH NAME
+read, write \- transfer data from and to a file
+.SH SYNOPSIS
+.ta \w'\fLTwrite 'u
+.IR size [4]
+.B Tread
+.IR tag [2]
+.IR fid [4]
+.IR offset [8]
+.IR count [4]
+.br
+.IR size [4]
+.B Rread
+.IR tag [2]
+.IR count [4]
+.IR data [ count ]
+.PP
+.IR size [4]
+.B Twrite
+.IR tag [2]
+.IR fid [4]
+.IR offset [8]
+.IR count [4]
+.IR data [ count ]
+.br
+.IR size [4]
+.B Rwrite
+.IR tag [2]
+.IR count [4]
+.SH DESCRIPTION
+The
+.B read
+request
+asks for
+.I count
+bytes of data
+from the file identified by 
+.IR fid ,
+which must be opened for reading,
+starting 
+.I offset
+bytes after the beginning of the file.
+The bytes are returned with the
+.B read
+reply message.
+.PP
+The
+.I count
+field in the reply indicates the number of bytes returned.
+This may be less than the requested amount.
+If the
+.I offset
+field is greater than or equal to the number of bytes in the file,
+a count of zero will be returned.
+.PP
+For directories,
+.B read
+returns an integral number of
+directory entries exactly as in
+.B stat
+(see
+.IR stat (5)),
+one for each member of the directory.
+The
+.B read
+request message must have
+.B offset
+equal to zero or the value of
+.B offset
+in the previous
+.B read
+on the directory, plus the number of bytes
+returned in the previous
+.BR read .
+In other words, seeking other than to the beginning
+is illegal in a directory (see
+.IR sys-seek (2)).
+.PP
+The
+.B write
+request asks that
+.I count
+bytes of data be recorded in the file identified by
+.IR fid ,
+which must be opened for writing, starting
+.I offset
+bytes after the beginning of the file.
+If the file is append-only,
+the data will be placed at the end of the file regardless of
+.IR offset .
+Directories may not be written.
+.PP
+The 
+.B write
+reply records the number of bytes actually written.
+It is usually an error
+if this is not the same as requested.
+.PP
+Because 9P implementations may limit the size of individual
+messages,
+more than one message may be produced by a single
+.I read
+or
+.I write
+call.
+The
+.I iounit
+field returned by
+.IR open (5),
+if non-zero, reports the maximum size that is guaranteed
+to be transferred atomically.
+.SH ENTRY POINTS
+.B Read
+and
+.B write
+messages are generated by the corresponding calls in
+.IR sys-read (2).
+Although
+.IR sys-seek (2)
+affects the offset, it does not generate a message.
+.SH SEE ALSO
+.IR sys-read (2),
+.IR stat (5)
--- /dev/null
+++ b/man/5/remove
@@ -1,0 +1,39 @@
+.TH REMOVE 5 
+.SH NAME
+remove \- remove a file from a server
+.SH SYNOPSIS
+.ta \w'\fLTremove 'u
+.IR size [4]
+.B Tremove
+.IR tag [2]
+.IR fid [4]
+.br
+.IR size [4]
+.B Rremove
+.IR tag [2]
+.SH DESCRIPTION
+The
+.B remove
+request asks the file server both to remove the file represented by
+.I fid
+and to
+.B clunk
+the
+.IR fid ,
+even if the remove fails.
+This request will fail if the client does not have write permission
+in the parent directory.
+.PP
+It is correct to consider
+.B remove
+to be a
+.B clunk
+with the side effect of removing the file if permissions allow.
+.SH ENTRY POINTS
+.B Remove
+messages are generated by
+.B remove
+(see
+.IR sys-remove (2)).
+.SH SEE ALSO
+.IR sys-remove (2)
--- /dev/null
+++ b/man/5/stat
@@ -1,0 +1,309 @@
+.TH STAT 5 
+.SH NAME
+stat, wstat \- inquire or change file attributes
+.SH SYNOPSIS
+.ta \w'\fLTwstat 'u
+.IR size [4]
+.B Tstat
+.IR tag [2]
+.IR fid [4]
+.br
+.IR size [4]
+.B Rstat
+.IR tag [2]
+.IR stat [ n ]
+.PP
+.IR size [4]
+.B Twstat
+.IR tag [2]
+.IR fid [4]
+.IR stat [ n ]
+.br
+.IR size [4]
+.B Rwstat
+.IR tag [2]
+.SH DESCRIPTION
+The
+.B stat
+transaction inquires about the file
+identified by
+.IR fid .
+The reply will contain a
+machine-independent
+.I directory
+.IR entry ,
+.IR stat ,
+laid out as follows:
+.TP
+.I size\f1[2]\fP
+total byte count of the following data
+.TP
+.I type\f1[2]\fP
+for kernel use
+.TP
+.I dev\f1[4]\fP
+for kernel use
+.TP
+.I qid.type\f1[1]\fP
+the type of the file (directory, etc.), represented as a bit vector
+corresponding to the high 8 bits of the file's mode word.
+.TP
+.I qid.vers\f1[4]\fP
+version number for given path
+.TP
+.I qid.path\f1[8]\fP
+the file server's unique identification for the file
+.TP
+.I mode\f1[4]\fP
+permissions and flags
+.TP
+.I atime\f1[4]\fP
+last access time
+.TP
+.I mtime\f1[4]\fP
+last modification time
+.TP
+.I length\f1[8]\fP
+length of file in bytes
+.TP
+.I name\f1[ s ]\fP
+file name; must be
+.B /
+if the file is the root directory of the server
+.TP
+.I uid\f1[ s ]\fP
+owner name
+.TP
+.I gid\f1[ s ]\fP
+group name
+.TP
+.I muid\f1[ s ]\fP
+name of the user who last modified the file
+.PD
+.PP
+Integers in this encoding are in little-endian order (least
+significant byte first).
+The
+.B unpackdir
+and
+.B packdir
+functions of
+.IR styx (2)
+convert between directory entries and the Limbo adt
+.BR Sys->Dir .
+For C implementations, the
+.I convM2D
+and
+.I convD2M
+routines (see
+.IR styx (10.2))
+convert between directory entries and a C structure called
+.BR Dir .
+.PP
+The
+.I mode
+contains permission bits as described in
+.IR intro (5)
+and the following:
+.B 16r80000000
+.RB ( DMDIR ,
+this file is a directory),
+.B 16r40000000
+.RB ( DMAPPEND ,
+append only),
+.B 16r20000000
+.RB ( DMEXCL ,
+exclusive use),
+.B 16r04000000
+.RB ( DMTMP ,
+temporary);
+these are echoed in
+.BR Qid.type .
+Writes to append-only files always place their data at the
+end of the file; the
+.I offset
+in the
+.B write
+message is ignored, as is the
+.B OTRUNC
+bit in an open.
+Exclusive use files may be open for I/O by only one fid at a time
+across all clients of the server.  If a second open is attempted,
+it draws an error.  Servers may implement a timeout on the lock
+on an exclusive use file: if the fid holding the file open has
+been unused for an extended period (of order at least minutes),
+it is reasonable to break the lock and deny the initial fid
+further I/O.
+Temporary files are not included in any automatic archives or dumps a server might create.
+.PP
+The two time fields are measured in seconds since the epoch
+(Jan 1 00:00 1970 GMT).
+The
+.I mtime
+field reflects the time of the last change of content (except when later changed by
+.BR wstat ).
+For a plain file,
+.I mtime
+is the time of the most recent
+.BR create ,
+.B open
+with truncation,
+or
+.BR write ;
+for a directory it is the time of the most recent
+.BR remove ,
+.BR create ,
+or
+.B wstat
+of a file in the directory.
+Similarly, the
+.I atime
+field records the last
+.B read
+of the contents;
+also it is set whenever
+.I mtime
+is set.
+In addition, for a directory, it is set by
+an
+.BR attach ,
+.BR walk ,
+or
+.BR create ,
+all whether successful or not.
+.PP
+The
+.I muid
+field names the user whose actions most recently changed the
+.I mtime
+of the file.
+.PP
+The
+.I length
+records the number of bytes in the file.
+Directories and most files representing devices have a conventional
+length of 0.
+.PP
+The
+.B stat
+request requires no special permissions.
+.PP
+The
+.B wstat
+request can change some of the file status information.
+The
+.I name
+can be changed by anyone with write permission in the parent directory;
+it is an error to change the name to that of an existing file.
+The
+.I length
+can be changed (affecting the actual length of the file) by anyone with
+write permission on the file.
+It is an error to attempt to set the length of a directory to a non-zero value,
+and servers may decide to reject length changes for other reasons.
+The
+.I mode
+and
+.I mtime
+can be changed by the owner of the file or the group leader of the file's current
+group.
+The directory bit cannot be changed by a
+.BR wstat ;
+the other defined permission and mode bits can.
+The
+.I gid
+can be changed: by the owner if also a member of the new group; or
+by the group leader of the file's current group
+if also leader of the new group
+(see
+.IR intro (5)
+for more information about permissions and
+.IR users (6)
+for users and groups).
+None of the other data can be altered by a
+.B wstat
+and attempts to change them will trigger an error.
+In particular,
+it is illegal to attempt to change the owner of a file.
+(These conditions may be
+relaxed when establishing the initial state of a file server.)
+.PP
+Either all the changes in
+.B wstat
+request happen, or none of them does: if the request succeeds,
+all changes were made; if it fails, none were.
+.PP
+A
+.B wstat
+request can avoid modifying some properties of the file
+by providing explicit ``don't touch'' values in the
+.B stat
+data that is sent: zero-length strings for text values and
+the maximum unsigned value of appropriate size
+for integral values.
+As a special case, if
+.I all
+the elements of the directory entry in a
+.B Twstat
+message are ``don't touch'' values, the server may interpret it
+as a request to guarantee that the contents of the associated
+file are committed to stable storage before the
+.B Rwstat
+message is returned.
+(Consider the message to mean, ``make the state of the file
+exactly what it claims to be.'')
+.PP
+A
+.I read
+of a directory yields an integral number of directory entries in
+the machine independent encoding given above
+(see
+.IR read (5)).
+.PP
+Note that since the
+.B stat
+information is sent as a 9P variable-length datum, it is limited to a maximum of
+65535 bytes.
+.SH ENTRY POINTS
+.B Stat
+messages are generated by
+.I fstat
+and
+.IR stat .
+.PP
+.B Wstat
+messages are generated by
+.I fwstat
+and
+.IR wstat .
+.SH SEE ALSO
+.IR sys-stat (2),
+.IR intro (5),
+.IR read (5),
+.IR intro (10),
+.IR styx (10.2)
+.SH BUGS
+To make the contents of a directory, such as returned by
+.IR read (5),
+easy to parse, each
+directory entry begins with a size field.
+For consistency, the entries in
+.B Twstat
+and
+.B Rstat
+messages also contain
+their size, which means the size appears twice.
+For example, the
+.B Rstat
+message is formatted as
+.RI ``(4+1+2+2+ n )[4]
+.B Rstat
+.IR tag [2]
+.IR n [2]
+.RI ( n -2)[2]
+.IR type [2]
+.IR dev [4]...,''
+where 
+.I n
+is the length of the array returned by
+.BR Styx->packdir .
--- /dev/null
+++ b/man/5/version
@@ -1,0 +1,103 @@
+.TH VERSION 5 
+.SH NAME
+version \- negotiate protocol version
+.SH SYNOPSIS
+.ta \w'\fLTversion 'u
+.IR size [4]
+.B Tversion
+.IR tag [2]
+.IR msize [4]
+.IR version [ s ]
+.br
+.IR size [4]
+.B Rversion
+.IR tag [2]
+.IR msize [4]
+.IR version [ s ]
+.SH DESCRIPTION
+The 
+.B version
+request negotiates the protocol version and message size
+to be used on the connection and initializes the connection for I/O.
+.B Tversion
+must be the first message sent on the 9P connection,
+and the client cannot issue any further requests until it has received the
+.B Rversion
+reply.
+The
+.I tag
+should be
+.B NOTAG
+(value
+.BR (ushort)~0 )
+for a
+.B version
+message.
+.PP
+The client suggests a maximum message size,
+.BR msize ,
+that is the maximum length, in bytes,
+it will ever generate or expect to receive in a single 9P message.
+This count includes all 9P protocol data, starting from the
+.B size
+field and extending through the message,
+but excludes enveloping transport protocols.
+The server responds with its own maximum,
+.BR msize ,
+which must be less than or equal to the client's value.
+Thenceforth, both sides of the connection must honor this limit.
+.PP
+The
+.B version
+string identifies the level of the protocol.
+The string must always begin with the two characters
+.RB `` 9P ''.
+If the server does not understand the client's version string,
+it should respond with an
+.B Rversion
+message (not
+.BR Rerror )
+with the
+.B version
+string the 7 characters
+.RB `` unknown ''.
+.PP
+The server may respond with the client's version string,
+or a version string identifying
+an earlier defined protocol version.
+Currently, the only defined version is the 6 characters
+.RB `` 9P2000 ''.
+Version strings are defined such that, if the client string contains
+one or more period characters, the initial substring up to but not including
+any single period in the version string defines a version of the protocol.
+After stripping any such period-separated suffix, the server is allowed to respond
+with a string of the form
+.BI 9P nnnn\f1,
+where
+.I nnnn
+is less than or equal to the digits sent by the client.
+.PP
+The client and server will use the protocol version defined by the
+server's response for all subsequent communication on the connection.
+.PP
+A successful
+.B version
+request initializes the connection.
+All outstanding I/O on the connection is aborted; all active fids are freed (`clunked') automatically.
+The set of messages between
+.B version
+requests is called a
+.IR session .
+.SH ENTRY POINTS
+The
+.B version
+message is generated by
+.I sys-fversion (2).
+It is also generated automatically, if required, by a
+.B mount
+or
+.B fauth
+system call on an uninitialized connection.
+.SH SEE ALSO
+.IR sys-bind (2),
+.IR sys-fversion (2)
--- /dev/null
+++ b/man/5/walk
@@ -1,0 +1,186 @@
+.TH WALK 5 
+.SH NAME
+walk \- descend a directory hierarchy
+.SH SYNOPSIS
+.ta \w'\fLTwalk 'u
+.IR size [4]
+.B Twalk
+.IR tag [2]
+.IR fid [4]
+.IR newfid [4]
+.IR nwname [2]
+.IR nwname *( wname [ s ])
+.br
+.IR size [4]
+.B Rwalk
+.IR tag [2]
+.IR nwqid [2]
+.IR nwqid *( qid [13])
+.SH DESCRIPTION
+The 
+.B walk
+request carries as arguments an existing 
+.IR fid
+and a proposed
+.I newfid
+(which must not be in use unless it is the same as
+.IR fid )
+that the client wishes to associate with
+the result of traversing the directory hierarchy
+by `walking' the hierarchy using the successive path name
+elements
+.BR wname .
+The
+.I fid
+must represent a directory unless zero path name elements are specified.
+.PP
+The
+.I fid
+must be valid in the current session and must not have been opened for I/O
+by an
+.B open
+or
+.B create
+message.
+If the full sequence of
+.B nwname
+elements is walked successfully,
+.I newfid
+will represent the file that results.
+If not,
+.I newfid
+(and
+.BR fid )
+will be unaffected.
+However, if
+.I newfid
+is in use or otherwise illegal, an
+.B Rerror
+is returned.
+.PP
+The name
+.RB `` .. ''
+(dot-dot) represents the parent directory.
+The name
+.RB `` . ''
+(dot), meaning the current directory, is not used in the protocol.
+.PP
+It is legal for
+.B nwname
+to be zero, in which case
+.I newfid
+will represent the same file as
+.I fid
+and the
+.B walk
+will usually succeed; this is equivalent to walking to dot.
+The rest of this discussion assumes
+.B nwname
+is greater than zero.
+.PP
+The
+.B nwname
+path name elements
+.B wname
+are walked in order, ``elementwise''.
+For the first elementwise walk
+to succeed, the file identified by
+.I fid
+must be a directory,
+and the implied user of the request must have permission
+to search the directory (see
+.IR intro (5)).
+Subsequent elementwise walks have equivalent restrictions
+applied to the implicit fid that results from the preceding elementwise walk.
+.PP
+If the first element cannot be walked for any reason,
+.B Rerror
+is returned.
+Otherwise, the walk will return an
+.B Rwalk
+message containing
+.I nwqid
+qids corresponding, in order, to the files that are visited by the
+.I nwqid
+successful elementwise walks;
+.I nwqid
+is therefore either
+.B nwname
+or the index of the first elementwise walk that failed.
+The value of
+.I nwqid
+cannot be zero unless
+.B nwname
+is zero.
+Also,
+.I nwqid
+will always be less than or equal to
+.BR nwname .
+Only if it is equal, however, will
+.I newfid
+be affected, in which case
+.I newfid
+will represent the file
+reached by the final elementwise walk requested in the message.
+.PP
+A
+.B walk
+of the name
+.RB `` .. ''
+in the root directory of a server is equivalent to a walk with no name elements.
+.PP
+If
+.I newfid
+is the same as
+.IR fid ,
+the above discussion applies, with the obvious difference
+that if the walk changes the state of
+.IR newfid ,
+it also changes the state of
+.IR fid ;
+and if
+.I newfid
+is unaffected, then
+.I fid
+is also unaffected.
+.PP
+To simplify the implementation of the servers, a maximum of sixteen name elements or qids
+may be packed in a single message.
+This constant is called
+.B MAXWELEM
+in
+.IR styx (2)
+and
+.IR styx (10.2).
+Despite this restriction, the system imposes no limit on the number of elements in a file name,
+only the number that may be transmitted in a single message.
+.SH ENTRY POINTS
+A call to
+.IR sys-chdir (2)
+causes a
+.BR walk .
+One or more
+.B walk
+messages may be generated by
+any of the following calls, which evaluate file names:
+.BR bind ,
+.BR create ,
+.BR mount ,
+.BR open ,
+.BR remove ,
+.BR stat ,
+.BR unmount ,
+.BR wstat .
+The file name element
+.B .
+(dot) is interpreted locally and
+is not transmitted in
+.B walk
+messages.
+.SH SEE ALSO
+.IR sys-bind (2),
+.IR sys-chdir (2),
+.IR sys-open (2),
+.IR sys-remove (2),
+.IR sys-stat (2),
+.IR intro (5)
--- /dev/null
+++ b/man/6/0intro
@@ -1,0 +1,7 @@
+.TH INTRO 6
+.SH NAME
+intro \- introduction to file formats
+.SH DESCRIPTION
+This section describes Inferno file formats that are meant to be public
+(as opposed to formats that are for a program's internal use).
+It also describes a few system conventions.
--- /dev/null
+++ b/man/6/INDEX
@@ -1,0 +1,30 @@
+intro 0intro
+attrdb attrdb
+audio audio
+auth auth
+colour colour
+dis dis
+font font
+subfont font
+image image
+json json
+keyboard keyboard
+keys keys
+keytext keytext
+login login
+man man
+namespace namespace
+ndb ndb
+plumbing plumbing
+proto proto
+regex regexp
+regexp regexp
+sbl sbl
+scancode scancode
+sexprs sexprs
+translate translate
+ubfa ubfa
+users users
+ascii utf
+unicode utf
+utf utf
--- /dev/null
+++ b/man/6/attrdb
@@ -1,0 +1,101 @@
+.TH ATTRDB 6
+.SH NAME
+attrdb \- data base of attribute-value pairs
+.SH DESCRIPTION
+An attribute data base is formed from one or more text files,
+each containing groups of related attribute-value pairs.
+A given data base typically stores data relating to a single logical domain.
+For instance,
+Inferno uses an attribute data base
+.IR ndb (6)
+to organise network configuration data.
+.I Attrdb (2)
+is typically used by applications to search the data.
+.PP
+Each entry in the data base consists of one or more lines containing
+attribute/value pairs, separated by white space.
+The first line of an entry starts a line; subsequent lines in the entry
+are preceded by white space (spaces or tabs).
+Pairs on the same line bind tighter together than others in the same entry.
+(This is important when one known pair is used to find another by attribute name.)
+The character
+.RB ` # '
+causes the rest of the line to be ignored (eg, as commentary).
+Empty lines are also ignored.
+.PP
+Attribute/value pairs are written as
+.IB attribute = value,
+which can be abbreviated to
+.IB attribute =
+or simply
+.I attribute
+if the attribute has no value.
+A
+.I value
+that contains white space, or any of the characters \f5#'"=\fP
+must be quoted, using either the single or double quote character.
+Within a quoted string, two adjacent quotes can be written to enter a quote in the string.
+For example:
+.IP
+.EX
+name='Paul Wilson' business company='Fruitbat Villas Ltd'
+   title='Key Grip'
+   address='39 Willis Street, York' tel='+44 1904 20927'
+   address='On the road' tel='+44 7904 169 704'
+name='James Mason' personal=
+   address='10 St James''s, East Grinstead' tel='01342 7674'
+.EE
+.PP
+Note that the binding rule associates each
+.B address
+in the first entry most closely with the
+.B tel
+on the same line.
+The attributes
+.B business
+(first entry)
+and
+.B personal
+(second entry)
+are both attributes without value.
+.PP
+A single logical data base can be formed by concatenating several physical files,
+each having the format above.
+One of the files must contain an entry (typically first in the file) containing a
+.B database
+attribute and a sequence of attributes of the form
+.BI file= filename.
+For example:
+.IP
+.EX
+database=
+   file=file1
+   file=file2
+   file=file3
+.EE
+.PP
+The logical database is formed by (logically) concatenating the contents
+of each
+.I filename
+in the order listed
+(ie,
+.BR file1 ,
+.BR file2 ,
+and
+.BR file3
+above).
+Typically the file containing the
+.B database
+attribute
+appears in the list as well, at the point where its contents should go;
+if it does not appear at all, it is assumed to be first.
+The name of that file is also used as the name of the logical database,
+for instance when opened by
+.IR attrdb (2).
+.SH SEE ALSO
+.IR cfg (2),
+.IR attrdb (2),
+.IR ipattr (2),
+.IR ndb (6),
+.IR cs (8),
+.IR dns (8)
--- /dev/null
+++ b/man/6/audio
@@ -1,0 +1,79 @@
+.TH AUDIO 6
+.SH NAME
+audio \- Inferno audio file format
+.SH DESCRIPTION
+Inferno audio files accepted by commands such as
+.IR auplay (1)
+have a simple format.
+The file contains a header consisting of
+non-empty lines of text specifying the data format and characteristics,
+followed by an empty line,
+followed by the audio data in binary form.
+The data can be copied
+directly to a suitable audio device (see
+.IR audio (3))
+correctly configured,
+using
+.BR read ,
+.B write
+or
+.B stream
+(see
+.IR sys-read (2)).
+.PP
+Each header line corresponds to a command that can be written to
+the control file
+.B audioctl
+of the
+.IR audio (3)
+device.
+The first line of the header must be a
+.BR rate
+command;
+other commands can appear in any order.
+The commands available are:
+.TP
+.BI rate " n"
+The data was recorded at
+.I n
+Hz, typically one of
+.BR 44100 ,
+.BR 22050 ,
+.BR 11025
+or
+.BR 8000
+but other values are legal.
+.TP
+.BI chans " n"
+There are
+.I n
+channels of audio data (eg, 1 for mono and 2 for stereo).
+.TP
+.BI bits " n"
+Each sample is
+.I n
+bits (typically 8 or 16).
+.TP
+.BI enc " e"
+.br
+The string
+.I e
+gives the recording format, and
+will usually be one of the following:
+.BR ulaw ,
+.BR alaw ,
+.BR pcm
+or
+.BR adpcm .
+.PP
+The audio data is stored with a sample from each channel in turn
+(`left' sample first if stereo, regardless of encoding or data size).
+Data encoded as
+.B pcm
+is little-endian, 2's complement  if 16-bit, but unsigned if 8 bit.
+The
+.B adpcm
+encoding uses 4-bit samples (two mono samples or one stereo sample per byte, again little-endian).
+.SH SEE ALSO
+.IR auplay (1),
+.IR audio (3)
--- /dev/null
+++ b/man/6/auth
@@ -1,0 +1,74 @@
+.TH AUTH 6
+.SH NAME
+auth \- authentication using station-to-station protocol
+.SH DESCRIPTION
+The following protocol,
+based on the Station-to-Station
+protocol, is used for mutual authentication of two parties,
+each possessing a certificate from the same certifying authority (CA).
+.PP
+In the description below:
+.TF CERTx
+.PD
+.TP
+.I alpha
+is a Diffie-Hellman base used system wide
+.TP
+.I p
+is a Diffie-Hellman modulus used system wide
+.TP
+.I Rx
+is a random number of the same order as
+.IR p .
+.TP
+.BI PK x
+the public key of
+.I x
+.TP
+.BI SK x
+the private key of
+.I x
+.TP
+.BI CERT x
+the public key
+of
+.I x
+signed by the certifying authority
+.TP
+.BI sig n ( x )
+represents
+.I x
+signed with
+.IR n 's
+private key
+.PP
+In the following, the parties are labelled 0 and 1.
+.PP
+Each sends its public key and certificate to the other together with
+a computation
+.IR "alpha**r0 mod p"
+.RI ( "alpha**r1 mod p" )
+based on the Diffie-Hellman parameters contained in the certificate:
+.IP
+.EX
+0 → 1  alpha**r0 mod p, CERTu0, PKu0
+1 → 0  alpha**r1 mod p, CERTu1, PKu1
+.EE
+.PP
+Each can now use the CA's public key and the certificate received to check
+that each has the other's public key.
+.PP
+Finally, each user signs values known to both that each can then verify:
+.IP
+.EX
+0 → 1  sig0(alpha**r0 mod p, alpha**r1 mod p)
+1 → 0  sig1(alpha**r0 mod p, alpha**r1 mod p)
+.EE
+.PP
+At this point 0 and 1 can calculate the shared secret
+.BR "alpha**(r0*r1)" ,
+and can use it to encrypt later communications.
+.SH SEE ALSO
+.IR keyring-auth (2),
+.IR keytext (6),
+.IR login (6)
--- /dev/null
+++ b/man/6/colour
@@ -1,0 +1,187 @@
+.TH COLOUR 6
+.SH NAME
+colour \- representation of pixels and colours
+.SH DESCRIPTION
+An image manipulated by
+.IR draw (2)
+(via the device
+.IR draw (3)),
+including the image corresponding to a physical display,
+contains a set of pixels.
+Each pixel has one or more components:
+colour components (red, green, blue); greyscale value; colour-map index; alpha value; and
+``don't care'' (for padding).
+Each component takes a given number of bits; the sum of the sizes of all components
+determines the size of the pixel.
+The implementation supports only pixel sizes that are either divisors or multiples of 8 bits.
+All pixels in an image have the same structure, corresponding to the
+.I channels
+of that image (see
+.IR image (6)).
+.PP
+The values of the red, green and blue components are chosen so 0 represents
+no intensity (black) and the maximum value (all ones, 255 for an 8-bit component)
+represents full intensity (eg, full red).
+Common colour physical display depths are 24 bits per pixel, with 8 bits per colour in order
+red, green, blue, and 16 bits per pixel, with 5 bits of red, 6 bits of green, and 5 bits of blue.
+.PP
+Colors may also be created with an opacity factor called
+.IR alpha ,
+which is scaled so 0 represents fully transparent and the maximum value (eg, 255 for
+an 8-bit alpha component) represents opaque colour.
+The alpha is
+.I premultiplied
+into the other channels, as described in the paper by Porter and Duff cited in
+.IR draw-image (2).
+The function
+.B Draw->setalpha
+(see
+.IR draw-intro (2))
+aids the initialisation of colour values with non-trivial alpha.
+.PP
+Because images are stored in memory managed by
+.IR draw (3)
+and operated through
+.IR draw-image (2),
+the details of pixel representation internally can be ignored by many applications.
+The representation is visible, however, when using the operations
+.B Image.readpixels
+and
+.B Image.writepixels
+(see
+.IR draw-image (2)).
+The bits representing a pixel's channel components are packed contiguously, and
+pixels are stored in contiguous bytes.
+The packing of pixels into bytes and words is odd.
+For compatibility with VGA frame buffers, the bits within a
+pixel byte are in big-endian order (leftmost pixel is most
+significant bits in byte), while bytes within a pixel are packed in little-endian order.
+This results in unintuitive pixel formats. For example, for the RGB24 format,
+the byte ordering is blue, green, red.
+.PP
+To maintain a constant external representation, the
+.IR draw (3)
+interface
+as well as the
+various graphics libraries represent colours 
+by 32-bit integers, containing red, blue, green and alpha components
+as 8-bit values, in that order from most to least significant byte.
+The color component values range from 0 (no colour) to 255 (saturated);
+alpha ranges from 0 (fully transparent) to 255 (fully opaque).
+.PP
+On displays with 8 bits per pixel or less,
+to address problems of consistency and portability amongst Inferno applications,
+Inferno uses a fixed colour map, called
+.BR rgbv .
+Although this avoids problems caused by multiplexing colour maps between
+applications, it requires that the colour map chosen be suitable for most purposes
+and usable for all.
+Other systems that use fixed colour maps tend to sample the colour cube
+uniformly, which has advantages\(emmapping from a (red, green, blue) triple
+to the colour map and back again is easy\(embut ignores an important property
+of the human visual system: eyes are
+much more sensitive to small changes in intensity than
+to changes in hue.
+Sampling the colour cube uniformly gives a colour map with many different
+hues, but only a few shades of each.
+Continuous tone images converted into such maps demonstrate conspicuous
+artifacts.
+.PP
+Rather than dice the colour cube into subregions of
+size 6\(mu6\(mu6 (as in Netscape Navigator) or 8\(mu8\(mu4
+picking 1 colour in each,
+the
+.B rgbv
+colour map uses a 4\(mu4\(mu4 subdivision, with
+4 shades in each subcube.
+The idea is to reduce the colour resolution by dicing
+the colour cube into fewer cells, and to use the extra space to increase the intensity
+resolution.
+This results in 16 grey shades (4 grey subcubes with
+4 samples in each), 13 shades of each primary and secondary colour (3 subcubes
+with 4 samples plus black) and a reasonable selection of colours covering the
+rest of the colour cube.
+The advantage is better representation of
+continuous tones.
+.PP
+The following function computes the 256 3-byte entries in the colour map:
+.IP
+.EX
+.ta 6n +6n +6n +6n
+void
+setmaprgbv(uchar cmap[256][3])
+{
+    uchar *c;
+    int r, g, b, v;
+    int num, den;
+    int i, j;
+
+    for(r=0,i=0; r!=4; r++)
+      for(v=0; v!=4; v++,i+=16)
+        for(g=0,j=v-r; g!=4; g++)
+          for(b=0; b!=4; b++,j++){
+            c = cmap[i+(j&15)];
+            den = r;
+            if(g > den)
+                den = g;
+            if(b > den)
+                den = b;
+            if(den == 0) /* would divide check; pick grey shades */
+                c[0] = c[1] = c[2] = 17*v;
+            else{
+                num = 17*(4*den+v);
+                c[0] = r*num/den;
+                c[1] = g*num/den;
+                c[2] = b*num/den;
+            }
+          }
+}
+.EE
+.PP
+There are 4 nested loops to pick the (red,green,blue) coordinates of the subcube,
+and the value (intensity) within the subcube, indexed by
+.BR r ,
+.BR g ,
+.BR b ,
+and
+.BR v ,
+whence
+the name
+.IR rgbv .
+The peculiar order in which the colour map is indexed is designed to distribute the
+grey shades uniformly through the map\(emthe
+.IR i 'th
+grey shade,
+.RI 0<= i <=15
+has index
+.IR i ×17,
+with black going to 0 and white to 255.
+Therefore, when a call to
+.B Image.draw
+(see
+.IR draw-image (2))
+converts a 1, 2 or 4 bit-per-pixel picture to 8 bits per pixel (which it does
+by replicating the pixels' bits), the converted pixel values are the appropriate
+grey shades.
+.PP
+The
+.B rgbv
+map is not gamma-corrected, for many reasons.  First, photographic
+film and television are both normally under-corrected, the former by an
+accident of physics and the latter by NTSC's design.
+Second, we require extra colour resolution at low intensities because of the
+non-linear response and adaptation of the human visual system.
+Properly
+gamma-corrected displays with adequate low-intensity resolution pack the
+high-intensity parts of the colour cube with colours whose differences are
+almost imperceptible.
+Either of these reasons suggests concentrating
+the available intensities at the low end of the range.
+Third, the compositing computations underlying the graphics operations in
+.IR draw-image (2)
+assume a linear colour space.
+Finally, the right value for gamma correction is determined in part by the
+characteristics of the physical display device, and correction should be done on output.
+.SH "SEE ALSO"
+.IR draw-intro (2),
+.IR draw-image (2)
--- /dev/null
+++ b/man/6/dis
@@ -1,0 +1,487 @@
+.TH DIS 6
+.SH NAME
+dis \- Dis object file
+.SH DESCRIPTION
+.ds Os "\v'0.2m'\s-3\|opt\s+3\^\v'-0.2m'
+A Dis object file contains the executable form of a single module,
+and conventionally uses the file suffix
+.BR .dis .
+.PP
+The following names are used in the description of the file encoding.
+.PP
+.TP 10n
+.I byte
+An unsigned 8-bit byte.
+.TP
+.I word
+A 32-bit integer value represented in exactly 4 bytes.
+.TP
+.I long
+A 64-bit integer value represented in exactly 8 bytes.
+.TP
+.I operand
+An integer stored in a compact variable-length
+encoding selected by the two most significant bits as follows:
+.IP
+.nf
+0x	signed 7 bits, 1 byte
+.br
+10	signed 14 bits, 2 bytes
+.br
+11	signed 30 bits, 4 bytes
+.fi
+.TP
+.I string
+A variable length sequence of bytes terminated by a zero byte.
+Names thus represented are in
+.IR utf (6)
+format.
+.PP
+All integers are encoded in two's complement format, most significant byte first.
+.PP
+Every object file has a header followed by five sections containing code, data, and several sorts of descriptors:
+.IP
+.I "header code-section type-section data-section module-name link-section"
+.PP
+Each section is described in turn below.
+.SS Header
+The header contains a magic number,
+a digital signature, a flag word,
+sizes of the other sections, and a description of the entry point.
+It has the following format:
+.IP
+.EX
+.ft I
+header:
+	magic signature\*(Os runflag stack-extent
+		code-size data-size type-size link-size entry-pc entry-type
+magic, runflag:
+	operand
+stack-extent, code-size, data-size, type-size, link-size:
+	operand
+entry-pc, entry-type:
+	operand
+.EE
+.PP
+The magic number is defined as 819248
+(symbolically
+.BR  XMAGIC ),
+for modules that have not been signed cryptographically, and 923426
+(symbolically
+.BR  "SMAGIC" ),
+for modules that contain a signature.
+The symbolic names
+.B  "XMAGIC"
+and
+.B  SMAGIC
+are defined by the C include file
+.B  "/include/isa.h"
+and by the Limbo module
+.IR dis (2).
+.PP
+The
+.I signature
+is present only if the magic number is
+.BR  "SMAGIC" .
+It has the form:
+.IP
+.EX
+.ft I
+signature:
+	length signature-data
+length:
+	operand
+signature-data:
+	byte ...
+.EE
+.PP
+A digital signature is defined by a length, followed by an array of bytes of that
+length that contain the signature in some unspecified format.
+Data within the signature should identify the signing authority, algorithm, and data to be signed.
+.PP
+.I Runflag
+is a bit mask that
+selects various execution options for a Dis module. The flags currently defined are:
+.IP
+.RS
+.TP
+.BR MUSTCOMPILE " (1<<0)"
+The module must be compiled into native instructions for execution (using a just-in-time compiler);
+if the implementation cannot do that,
+the
+.B load
+instruction should given an error.
+.TP
+.BR DONTCOMPILE " (1<<1)"
+The module should not be compiled into native instructions, when that is the default for the runtime environment, but
+should be interpreted.
+This flag may be set to allow debugging or to save memory.
+.TP
+.BR  SHAREMP " (1<<2)"
+Each instance of the module should use the same module data for all instances of the module. There is no implicit synchronisation between threads using the shared data.
+.TP
+.BR  HASLDT " (1<<4)"
+The dis file contains a separate import section. On older versions of the system,
+this section was within the data section.
+.TP
+.BR  HASEXCEPT " (1<<5)"
+The dis file contains an exception handler section.
+.RE
+.PP
+.IR Stack-extent ,
+if non-zero, gives the number of bytes by which the thread stack of this module should be extended in the event that procedure calls exhaust the allocated stack.
+While stack extension is transparent to programs, increasing this value may improve the efficiency of execution at the expense of using more memory.
+.PP
+.IR Code-size ,
+.I type-size
+and
+.I link-size
+give the number of entries (instructions, type descriptors, linkage directives)
+in the corresponding sections.
+.PP
+.I Data-size
+is the size in bytes of the module's global data area
+(not the number of items in
+.IR data-section ).
+.PP
+.I Entry-pc
+is an integer index into the instruction stream that is the default entry point for this module.
+It should point to the first instruction of a function.
+Instructions are numbered from a program counter value of zero.
+.PP
+.I Entry-type
+is the index of the type descriptor in the type section that corresponds to the function entry point set by
+.IR entry-pc .
+.SS Code Section
+.PP
+The code section describes a sequence of instructions for the virtual machine.
+There are
+.I code-size
+instructions.
+An instruction is encoded as follows:
+.IP
+.EX
+.ft I
+instruction:
+	opcode address-mode middle-data\*(Os source-data\*(Os dest-data\*(Os
+opcode, address-mode:
+	byte
+middle-data:
+	operand
+source-data, dest-data:
+	operand operand\*(Os
+.ft R
+.EE
+.PP
+The one byte
+.I opcode
+specifies the instruction to execute; opcodes are
+defined by the virtual machine specification.
+.PP
+The
+.I address-mode
+byte specifies the addressing mode of each of the three operands: middle, source and destination. The source and destination operands are encoded by three bits and the middle operand by two bits. The bits are packed as follows:
+.IP
+.EX
+bit  7  6  5  4  3  2  1  0
+    m1 m0 s2 s1 s0 d2 d1 d0
+.EE
+.PP
+The following definitions are used in the description of addressing modes:
+.IP
+.RS
+.TP
+.B OP
+30 bit integer operand
+.PD0
+.TP
+.B SO
+16 bit unsigned small offset from register
+.TP
+.B SI
+16 bit signed immediate value
+.TP
+.B LO
+30 bit signed large offset from register
+.PD
+.RE
+.PP
+The middle operand is encoded as follows:
+.IP
+.RS
+.TF "00    SO(MP)"
+.TP
+.BI "00    " "none"
+no middle operand	
+.TP
+.B "01    $SI"
+small immediate
+.TP
+.B "10    SO(FP)"
+small offset indirect from FP
+.TP
+.B "11    SO(MP)"
+small offset indirect from MP
+.RE
+.PD
+.PP
+The
+.I middle-data
+field is present only if the middle operand specifier of the
+.I address-mode
+is not `none'.
+If the field is present it is encoded as an 
+.IR operand .
+.PP
+The source and destination operands are encoded as follows:
+.IP
+.RS
+.TF "000  SO(SO(MP))"
+.TP
+.B "000   LO(MP)"
+offset indirect from MP
+.TP
+.B "001   LO(FP)"
+offset indirect from FP
+.TP
+.B "010   $OP"
+30 bit immediate
+.TP
+.BI "011   " "\fInone\fP"
+no operand
+.TP
+.B "100   SO(SO(MP))"
+double indirect from MP
+.TP
+.B "101   SO(SO(FP))"
+double indirect from FP
+.TP
+.B 110
+\fIreserved\fP
+.TP
+.B 111
+\fIreserved\fP
+.RE
+.PD
+.PP
+The
+.I source-data
+and
+.I dest-data
+fields are present only when the corresponding
+.I address-mode
+field is not `none'.
+For offset indirect and immediate modes the field contains a single 
+.I operand
+value.
+For double indirect modes the values are encoded as two 
+.IR operands :
+the first is the register indirect offset, and the second is the final indirect offset.
+The offsets for double indirect addressing cannot be larger than 16 bits.
+.SS Type Section
+The type section contains
+.I type-size
+type descriptors describing the layout of pointers within data types. The format of each descriptor is:
+.IP
+.EX
+.ft I
+type-descriptor:
+	desc-number memsize mapsize map
+desc-number, memsize, mapsize:
+	operand
+map:
+	byte ...
+.ft R
+.EE
+.PP
+The
+.I  desc-number
+is a small integer index used to identify the descriptor to instructions such as 
+.BR  "new" .
+.I Memsize
+is the size in bytes of the memory described by this type.
+.PP
+The
+.I  mapsize
+field gives the size in bytes of the following
+.I "map"
+array.
+.I Map
+is an array of bytes representing
+a bit map where each bit corresponds to a word in memory.
+The most significant bit corresponds to the lowest address.
+For each bit in the map,
+the word at the corresponding offset in the type is a pointer iff the bit is set to 1.
+.SS Data Section
+.PP
+The data section encodes the contents of the
+data segment for the module, addressed by
+.B MP
+at run-time.
+The section contains a sequence of items of the following form:
+.IP
+.EX
+.ft I
+data-item:
+	code count\*(Os offset data-value ...
+code:
+	byte
+count, offset:
+	operand
+.EE
+.PP
+Each item contains an
+.I offset
+into the section, followed by one or more data values in
+a machine-independent encoding.
+As each value is placed in the data segment, the offset is incremented by the size of the datum.
+.PP
+The
+.I code
+byte has two 4-bit fields.
+The bottom 4 bits of
+.I code
+gives the number of
+.I data-values
+if there are between 1 and 15;
+if there are more than 15,
+the low-order field is zero, and a following
+.I operand
+gives the count.
+.PP
+The top 4 bits of
+.I code
+encode the type of each
+.I data-value
+in the item, which determines its encoding.
+The defined values are:
+.IP
+.RS
+.TF 0000
+.TP
+.B 0001
+8 bit bytes
+.TP
+.B 0010
+32 bit integers, one
+.I word
+each
+.TP
+.B 0011
+string value encoded by
+.IR utf (6)
+in
+.I count
+bytes
+.TP
+.B 0100
+real values in IEEE754 canonical representation, 8 bytes each
+.TP
+.B 0101
+Array, represented by two
+.I words
+giving type and length
+.TP
+.B 0110
+Set base for data items: one
+.I word
+giving an array index
+.TP
+.B 0111
+Restore base for data items:
+no operands
+.TP
+.B 1000
+64 bit big, 8 bytes each
+.RE
+.PD
+.PP
+The loader maintains a current base address and a stack of addresses.
+Each item's value is stored at the address formed by adding the current offset
+to the current base address.
+That address initially is the base of the module's data segment.
+The `set base' operation immediately follows
+an `array'
+.IR data-item .
+It stacks the current base address and sets the current base address to the
+address of the array element selected by its operand.
+The `restore base' operation sets the current base address to the
+address on the top of the stack, and pops the stack.
+.SS Module name
+The module name immediately follows the data section.
+It contains the name of the module implemented by the object file,
+as a sequence of bytes in
+UTF encoding, terminated by a zero byte.
+.SS Link Section
+The link section contains an array of
+.I link-size
+external linkage items,
+listing the functions exported by this module.
+Each variable-length item contains the following:
+.IP
+.EX
+.ft I
+link-item:
+	pc desc sig fn-name
+pc, desc:
+	operand
+sig:
+	word
+fn-name:
+	string
+.ft R
+.EE
+.PP
+.I Fn-name
+is the name of an exported function.
+Adt member functions appear with their
+full names: the member name qualified by the adt name, in the form
+.IB adt-name . member-name,
+for instance
+.BR Iobuf.gets .
+.PP
+.I Pc
+is the instruction number of its entry point.
+.I Desc
+is an index value that selects a type descriptor in the type section,
+which gives the type of the function's stack frame.
+.I Sig
+is an integer hash of the type signature of the function, used in type checking.
+.SS Import Section
+The optional import section lists all those functions imported from other
+modules. This allows type checking at load time. The size of the section in
+bytes is given at the start in operand form. For each module imported there
+is a list of functions imported from that module. For each function, its type signature (a word) is followed by
+a 0 terminated list of bytes representing its name.
+.SS Handler Section
+The final optional section lists all exception handlers declared in the module. The number
+of such handlers is given at the start of the section in operand form. For each one, its format
+is:
+.IP
+.EX
+.ft 1
+handler:
+	offset pc1 pc2 desc nlab exc-tab
+offset, pc1, pc2, desc, nlab:
+	operand
+exc-tab:
+	exc-name pc ... exc-name pc pc
+exc-name:
+	string
+pc:
+	operand
+.ft R
+.EE
+.PP
+Each handler specifies the frame offset of its exception structure, the range of pc values it covers (from pc1 up to but not including pc2), the type descriptor of any
+memory that needs destroying by the handler (or -1 if none), the number of
+exceptions in the handler and then the exception table itself. The latter consists
+of a list of exception names and the corresponding pc to jump to when this
+exception is raised. This is then followed by the pc to jump to in any wildcard (*) case
+or -1 if this is not applicable.
+.SH SEE ALSO
+.IR asm (1),
+.IR dis (2),
+.IR sbl (6)
+.br
+``The Dis Virtual Machine Specification'', Volume 2
--- /dev/null
+++ b/man/6/font
@@ -1,0 +1,102 @@
+.TH FONT 6
+.SH NAME
+font, subfont \- external format for character fonts and subfonts
+.SH DESCRIPTION
+Fonts are constructed as a list defining a range of Unicode characters
+and a subfont containing the character images for that range.
+Subfonts are not directly accessible from Limbo.
+.PP
+External fonts are described by a plain text file that can be read using
+.BR Font.open ;
+.B Font.build
+reads the same format from a string rather than a file.
+(see
+.IR draw-font (2)).
+.PP
+The format is a header followed by any number of
+subfont range specifications.
+The header contains two numbers: the height and the ascent, both in pixels.
+The height is the inter-line spacing and the ascent is the distance
+from the top of the line to the baseline.  These numbers should be chosen
+to display consistently all the subfonts of the font.
+A subfont range specification contains two or three numbers and a file name.
+The numbers are the inclusive range of characters covered by the subfont,
+with an optional starting position within the subfont,
+and the file name names an external file holding the subfont data.
+The minimum number of a covered range is mapped to the specified starting position
+(default zero) of the
+corresponding subfont.
+If the subfont file name does not begin with a slash, it is taken relative to the
+directory containing the font file.
+Each field must be followed by some white space.
+Each numeric field may be C-format decimal, octal, or hexadecimal.
+.PP
+External subfonts are represented in a more rigid format:
+an image containing character images,
+followed by a subfont header, followed by character information.
+The image has the format for external image files described in
+.IR image (6).
+The subfont header has 3
+decimal strings:
+.BR n ,
+.BR height ,
+and
+.BR ascent .
+Each number is right-justified and blank padded in 11 characters, followed by a blank.
+The character
+.B info
+consists of
+.BR n +1
+6-byte entries, each giving values called
+.B x
+(2 bytes, low order byte first),
+.BR top ,
+.BR bottom ,
+.BR left ,
+and
+.BR width
+for the successive characters from left to right (in increasing Unicode order) in the
+subfont.
+The rectangle holding the character is
+.B (x,
+.B top,
+.B xn,
+.BR bottom) ,
+where
+.B xn
+is the
+.B x
+field of the next character.
+When the character is to be drawn in an image at point
+.BR p ,
+the rectangle is placed at
+.B (p.x+left ,
+.B p.y)
+and the next character to be drawn is placed at
+.B (p.x+width ,
+.B p.y)
+The
+.B x
+field of the last
+entry
+is used to calculate the image width
+of the previous character; the other fields in the last
+entry
+are irrelevant.
+.PP
+Note that the convention of using the character with value zero (NUL) to represent
+characters of zero width (see the description of
+.B Image.text
+in
+.IR draw-image (2))
+means that fonts should have, as their zeroth character,
+one with non-zero width.
+.SH FILES
+.TF /fonts/*
+.TP
+.B /fonts/*
+font directories
+.SH "SEE ALSO"
+.IR draw-intro (2),
+.IR draw-font (2),
+.IR draw (3)
--- /dev/null
+++ b/man/6/image
@@ -1,0 +1,178 @@
+.TH IMAGE 6
+.SH NAME
+image \- external format for images
+.SH DESCRIPTION
+Images are described in
+.IR draw-image (2),
+and the definition of pixel values is in
+.IR colour (6).
+Fonts and images are stored in external files
+in machine-independent formats.
+.PP
+Image files are read and written using
+.B Display.readimage
+and
+.B Display.writeimage
+(see
+.IR draw-display (2)).
+An image is a rectangular array of pixels,
+where each pixel is organised as one or more channels,
+as determined by the image.
+.PP
+An uncompressed image file starts with 5
+strings:
+.BR chan ,
+.BR r.min.x ,
+.BR r.min.y ,
+.BR r.max.x ,
+and
+.BR r.max.y .
+Each is right-justified and blank padded in 11 characters, followed by a blank.
+The
+.B chan
+value is a textual string describing the pixel format
+(see below for a discussion of channel descriptors),
+and the rectangle coordinates are decimal strings.
+The rest of the file contains the
+.B r.max.y-r.min.y
+rows of pixel data.
+A
+.I row
+consists of the byte containing pixel
+.B r.min.x
+and all the bytes up to and including the byte containing pixel
+.BR r.max.x -1.
+For images with depth 
+.I d
+less than eight, a pixel with x-coordinate =
+.I x
+will appear as
+.I d
+contiguous bits in a byte, with the pixel's high order bit
+starting at the byte's bit number
+.if t \fIw\fP\(mu(\fIx\fP mod (8/\fIw\fP)),
+.if n w*(x mod (8/w)),
+where bits within a byte are numbered 0 to 7 from the
+high order to the low order bit.
+Rows contain integral number of bytes, so there may be some unused
+pixels at either end of a row.
+If
+.I d
+is greater than 8, the definition of images requires that it be a multiple of 8, so
+pixel values take up an integral number of bytes.
+.PP
+The
+.B Image.readpixels
+and
+.B Image.writepixel
+operations described in
+.IR draw-image (2)
+also deal with rows in this format, stored in Limbo arrays of bytes.
+.PP
+The channel format string is a sequence of two-character channel descriptions,
+each comprising a letter 
+.RB ( r
+for red,
+.B g
+for green,
+.B b
+for blue,
+.B a
+for alpha,
+.B m
+for colour-mapped,
+.B k
+for greyscale,
+and
+.B x
+for ``don't care'')
+followed by a number of bits per pixel.
+The sum of the channel bits per pixel is the
+depth of the image, which must be either 
+a divisor or a multiple of eight.
+It is an error to have more than
+one of any channel but 
+.BR x .
+An image must have either a greyscale channel; a colour-mapped channel;
+or red, green, and blue channels.
+If the alpha channel is present, it must be at least as deep as any other channel.
+.PP
+The channel string defines the format of the pixels in the file,
+and should not be confused with ordering of bytes in the file,
+which is little-endian.
+In particular
+.B 'r8g8b8'
+pixels have byte ordering blue, green, and red within the file.
+See
+.IR colour (6)
+for more details of the pixel format.
+.PP
+A previous Inferno image format replaces the channel string
+with a decimal
+.IR ldepth ,
+which is the base two logarithm of the number 
+of bits per pixel in the image.
+In this case,
+.IR ldepth s
+0, 1, 2, and 3
+correspond to channel descriptors
+.BR k1 ,
+.BR k2 ,
+.BR k4 ,
+and
+.BR m8 ,
+respectively.
+Furthermore, the pixel values are inverted compared to the current colour maps;
+in particular, an all-zero pixel is white and all-ones is black.
+That format is still readable but cannot be written; older files should be converted to the
+newer one.
+The image file reading operations automatically invert the pixel values to produce correct results.
+.PP
+A compressed image file begins with the 11 bytes "compressed\en",
+immediately followed by a header as described above, followed by the
+image data.
+(The pixel data once uncompressed has the format described above.)
+The rest of the file is a string of compression blocks, each encoding
+a number of rows of the image's pixel data.  Compression blocks
+are at most 6024 bytes long, so that they fit comfortably in a
+single 9P message.  Since a compression block must encode a
+whole number of rows, there is a limit (about 5825 bytes) to the width of images
+that may be encoded.  Most wide images are in subfonts,
+which, at 1 bit per pixel (the usual case for fonts), can be 46600 pixels wide.
+.PP
+A compression block begins with two decimal strings of twelve bytes each.
+The first number is one more than the
+.B y
+coordinate of the last row in the block.  The second is the number
+of bytes of compressed data in the block, not including the two decimal strings.
+This number must not be larger than 6000.
+.PP
+Pixels are encoded using a version of Lempel & Ziv's sliding window scheme LZ77,
+best described in J A Storer & T G Szymanski
+`Data Compression via Textual Substitution',
+JACM 29#4, pp. 928-951.
+.PP
+The compression block is a string of variable-length
+code words encoding substrings of the pixel data.  A code word either gives the
+substring directly or indicates that it is a copy of data occurring
+previously in the pixel stream.
+.PP
+In a code word whose first byte has the high-order bit set, the rest of the
+byte indicates the length of a substring encoded directly.
+Values from 0 to 127 encode lengths from 1 to 128 bytes.
+Subsequent bytes are the literal pixel data.
+.PP
+If the high-order bit is zero, the next 5 bits encode
+the length of a substring copied from previous pixels.  Values from 0 to 31
+encode lengths from 3 to 34 bytes.  The bottom two bits of the first byte and
+the 8 bits of the next byte encode an offset backward from the current
+position in the pixel data at which the copy is to be found.  Values from
+0 to 1023 encode offsets from 1 to 1024.  The encoding may be `prescient',
+with the length larger than the offset, which works just fine: the new data
+is identical to the data at the given offset, even though the two strings overlap.
+.SH "SEE ALSO"
+.IR draw-intro (2),
+.IR draw-image (2),
+.IR draw (3),
+.IR colour (6),
+.IR font (6)
--- /dev/null
+++ b/man/6/json
@@ -1,0 +1,68 @@
+.TH JSON 6
+.SH NAME
+json \- javascript object notation
+.SH DESCRIPTION
+.I JSON
+is a textual data transport encoding for a small collection of basic and structured values:
+.BR null ,
+booleans, numbers, strings, arrays, and objects (property/value list).
+It is a subset of JavaScript (ECMAScript).
+.IR Json (2)
+describes a Limbo module that can read and write streams of JSON-encoded data.
+.PP
+The encoding syntax and its interpretation is defined by Internet RFC4627, but is briefly summarised here:
+.IP
+.EX
+.ft R
+.ta \w'\f2simple-xxx\f1'u +\w'\ ::=\ 'u
+\f2text\fP	::=	\f2array\fP | \f2object\fP
+
+\f2value\fP	::=	 \f5null\fP | \f5true\fP | \f5false\fP | \f2number\fP | \f2string\fP | \f2array\fP | \f2object\fP
+
+\f2object\fP	::=	\f5'{'\fP [\f2pair\fP (\f5','\fP \f2pair\fP)*] \f5'}'\fP
+\f2pair\fP	::=	\f2string\fP \f5':'\fP \f2value\fP
+
+\f2array\fP	::=	\f5'['\fP [\f2value\fP (\f5','\fP \f2value\fP)*] \f5']'\fP
+
+\f2number\fP	::=	\f2int\fP \f2frac\fP? \f2exp\fP?
+\f2int\fP	::=	\f5'-'\fP? \f5[0-9]\fP | \f5[1-9][0-9]\fP+
+\f2frac\fP	::=	\f5'.'\fP \f5[0-9]\fP+
+\f2exp\fP	::=	\f5[eE][-+]\fP? \f5[0-9]\fP+
+
+\f2string\fP	::=	\f5'"'\fP \f2char\fP* \f5'"'\fP
+\f2char\fP	::=	\f5[^\ex00-\ex1F"\e\e]\fP |
+		 \f5'\e"'\fP | \f5'\e/'\fP | \f5'\eb'\fP | \f5'\ef'\fP | \f5'\en'\fP | \f5'\er'\fP | \f5'\et'\fP |
+		\f5'\eu'\fP \f2hex\fP \f2hex\fP \f2hex\fP \f2hex\fP
+\f2hex\fP	::=	\f5[0-9a-fA-F]\fP
+.EE
+.PD
+.DT
+.PP
+A sequence of blank, tab, newline or carriage-return characters (`white space') can appear
+before or after opening and closing brackets and braces, colons and commas,
+and is ignored.
+The
+.B null
+represents a null value of any type.
+The
+.I strings
+in the
+.I pairs
+of an
+.I object
+are intended to represent member names, and should be unique within that object.
+Note that array and object denotations can be empty.
+Also note that the RFC wants applications to exchange a
+.I text
+(ie, object or array) not an arbitrary
+.IR value .
+.SH SEE ALSO
+.IR json (2),
+.IR sexprs (6),
+.IR ubfa (6)
+.br
+.br
+D Crockford, ``The application/json Media Type for JavaScript Object Notation (JSON)'',
+.IR RFC4627 .
+.br
+.B "http://www.json.org/"
--- /dev/null
+++ b/man/6/keyboard
@@ -1,0 +1,170 @@
+.TH KEYBOARD 6
+.SH NAME
+keyboard \- how to type characters
+.SH DESCRIPTION
+Keyboards are idiosyncratic.
+The differing conventions of host operating systems
+make them more so for Inferno.
+In all implementations,
+it should be obvious how to type ordinary
+.SM ASCII
+characters,
+backspace, tab, escape, and newline.
+When typing into the Inferno environment, the key labelled
+.B Return
+or
+.B Enter
+generates a newline
+.RB ( 0x0A );
+if there is a key labelled
+.B Line
+.BR Feed ,
+it generates a carriage return
+.RB ( 0x0D );
+Inferno eschews CRLFs.
+All control characters are typed in the usual way;
+in particular, control-J is a line feed and control-M a carriage return.
+.PP
+In native mode,
+on the PC and some other machines, the following
+extra conventions might also be used.
+The key labelled
+.B Caps
+.B Lock
+acts as an additional control key.
+The character erase key generates backspace.
+The key labelled
+.B Del
+or
+.B Delete
+generates the delete character
+.RB ( 0x7F ).
+The plethora of function keys generate values in the Unicode
+user-defined space, as defined by
+.B /include/keyboard.h 
+and
+.BR /module/keyboard.m .
+They are fitfully supported by applications, but
+.B "Page\ Up"
+and
+.B "Page\ Down"
+are often understood by Tk applications.
+.PP
+Characters in Inferno are runes (see
+.IR utf (6)).
+Any rune can be typed using a compose key followed by several
+other keys.
+The compose key is implementation-dependent, and
+is also generally near the lower right of the main key area:
+either
+.B Alt
+key on the PC,
+and in X11 implementations, whatever X11 regards
+as
+.B Alt
+or
+.BR Meta .
+After typing the compose key, type a capital
+.L X
+and exactly four hexadecimal characters (digits and
+.L a
+to
+.LR f )
+to type a single rune with the value represented by
+the typed number.
+There are shorthands for many characters, comprising
+the compose key followed by a two- or three-character sequence.
+There are several rules guiding the design of the sequences, as
+illustrated by the following examples.
+The full list is too long to repeat here, but is contained in the file
+.L /lib/keyboard
+in a format suitable for
+.IR grep (1).
+.IP
+A repeated symbol gives a variant of that symbol, e.g.,
+.B ??
+yields ¿\|.
+.IP
+ASCII digraphs for mathematical operators give the corresponding operator, e.g.,
+.B <=
+yields ≤.
+.IP
+Two letters give the corresponding ligature, e.g.,
+.B AE
+yields Æ.
+.IP
+Mathematical and other symbols are given by abbreviations for their names, e.g.,
+.B pg
+yields ¶.
+.IP
+Chess pieces are given by a
+.B w
+or
+.B b
+followed by a letter for the piece
+.RB ( k
+for king,
+.B q
+for queen,
+.B r
+for rook,
+.B n
+for knight,
+.B b
+for bishop, or
+.B p
+for pawn),
+e.g.,
+.B wk
+for a white king.
+.IP
+Greek letters are given by an asterisk followed by a corresponding latin letter,
+e.g.,
+.B *d
+yields δ.
+.IP
+Cyrillic letters are given by an at sign followed by a corresponding latin letter or letters,
+e.g.,
+.B @ya
+yields я.
+.IP
+Script letters are given by a dollar sign followed by the corresponding regular letter,
+e.g.,
+.B $F
+yields ℱ.
+.IP
+A digraph of a symbol followed by a letter gives the letter with an accent that looks like the symbol, e.g.,
+.B ,c
+yields ç.
+.IP
+Two digits give the fraction with that numerator and denominator, e.g.,
+.B 12
+yields ½.
+.IP
+The letter s followed by a character gives that character as a superscript, e.g.,
+.B s1
+yields ¹.
+.IP
+Sometimes a pair of characters give a symbol related to the superimposition of the characters, e.g.,
+.B cO
+yields ©.
+.IP
+A mnemonic letter followed by $ gives a currency symbol, e.g.,
+.B l$
+yields £.
+.PP
+Note the difference between ß (ss) and µ (micron) and
+the Greek β and μ.
+.SH FILES
+.TF "/lib/keyboard "
+.TP
+.B /lib/keyboard
+sorted table of characters and keyboard sequences
+.SH "SEE ALSO"
+.IR acme (1),
+.IR wm-brutus (1),
+.IR intro (1),
+.IR unicode (1),
+.IR cons (3),
+.IR utf (6),
+.IR kbdputc (10.2)
--- /dev/null
+++ b/man/6/keys
@@ -1,0 +1,52 @@
+.TH KEYS 6
+.SH NAME
+keys \- secrets shared with signer
+.SH DESCRIPTION
+The file
+.B /keydb/keys
+exists only on a host acting as a `signer' (authentication server, Certifying Authority).
+It
+holds a password entry
+for each user registered with an Inferno server.
+Each entry contains a user name, a password, the time at which the entry
+expires, and the entry's status.
+The password is the secret shared between the user
+and signer (authentication server),
+allowing the signer to sign a certificate to authenticate a
+user's public key to others, using the secret to check the user's identity.
+The actual secret is not stored, but rather its SHA-1 hash.
+.PP
+The file is encrypted with a secret provided by the signer's administrator;
+normally that secret is entered once when
+authentication services are started by
+.B svc/auth
+on the host acting as signer
+(see
+.IR svc (8)).
+The file should also be readable and writable only by the user identity
+that runs the signing service
+(ie,
+mode
+.BR 600 ,
+see
+.IR chmod (1)).
+Entries are usually accessed only through
+the name space provided by
+.IR keyfs (4),
+which decrypts the file into internal data structures given the administrative key,
+and makes each entry visible as a separate directory.
+Using that name space,
+entries are added and updated by an administrator using
+.IR changelogin (8),
+a user can change a secret using
+.IR passwd (1)
+via
+.IR keysrv (4),
+and it is accessed for signing by
+.IR logind (8)
+to obtain the secret used to verify the identity
+of a client requesting a certificate
+(typically via
+.IR security-login (2)).
+.SH FILES
+.B /keydb/keys
--- /dev/null
+++ b/man/6/keytext
@@ -1,0 +1,124 @@
+.TH KEYTEXT 6
+.SH NAME
+keytext \- textual form of Inferno public/private keys
+.SH DESCRIPTION
+.IR Keyring-certtostr (2)
+defines a set of functions that convert between textual forms of the elements of the
+Inferno public-key authentication system and their internal data types.
+The textual form is used for key storage and as the transport format for the authentication protocol
+.IR auth (6).
+In storage and transport each encoded value is encapsulated by the record-oriented
+encoding defined in
+.IR keyring-getmsg (2).
+The format represents public and private keys, and signer's certificates.
+In this context a
+.I certificate
+is a time-limited cryptographically signed hash of some other value
+(usually a public key) and contains neither that value nor the signer's key, which is assumed to be
+available elsewhere.
+.PP
+All values are represented by a sequence of newline-separated text fields.
+The type of any given value is determined by its context.
+Each type of value has a common prefix that includes an algorithm identifier, followed by a sequence of algorithm-dependent fields:
+.IP
+.EX
+.ft R
+.ta \w'\f2elgamal-public-keyxx\f1'u +\w'\ ::=\ 'u
+.fi
+.IR "authinfo" "	::=	" "signer-public-key certificate !private-key big-alpha big-p"
+.br
+.IR certificate "	::=	" "sigalg hashalg signer-name exp-time *-sig"
+.br
+.IR sigalg "	::=	"
+.BR "rsa" " | "
+.BR "dsa" " | "
+.BR "elgamal"
+.br
+.IR hashalg "	::=	"
+.BR sha1 " | "
+.B md5
+.br
+.IR "*-key" "	::=	" "sigalg owner-name ..."
+.sp
+.IR "rsa-public-key" "	::=	"
+.B rsa
+.I owner-name
+.I "big-n big-ek"
+.br
+.IR "rsa-private-key" "	::=	"
+.B rsa
+.I owner-name
+.I "big-n big-ek"
+.br
+.I "		!big-dk  !big-p !big-q !big-kp !big-kq !big-c2"
+.br
+.IR "dsa-public-key" "	::=	"
+.B dsa
+.I owner-name
+.I "big-p big-q big-alpha big-key"
+.br
+.IR "dsa-private-key" "	::=	"
+.B dsa
+.I owner-name
+.I "big-p big-q big-alpha big-key !big-secret"
+.sp
+.IR rsa-sig "	::=	" "big-val"
+.br
+.IR dsa-sig "	::=	" "big-r big-s"
+.br
+.IR elgamal-sig "	::=	" "big-r big-s"
+.EE
+.PP
+Each value labelled as
+.RI ` big- '
+is an unsigned multiple-precision integer
+from
+.IR keyring-ipint (2),
+represented as a sequence of bytes with
+in big-endian order,
+as produced by
+.BR IPint->iptobytes
+with an extra leading zero byte added if the top bit of the first byte is set,
+and then encoded in base-64 (as by
+.IR encoding (2)).
+Each value labelled
+.RI ` -name '
+is
+.I utf (6)
+text not containing a newline;
+it is interpreted by an application and need not be a name.
+The expiry time
+.I exp-time
+is represented in decimal as seconds from the Epoch (1 January 1970 00:00 GMT);
+if it is zero, no expiry time is set.
+A label prefixed by
+.RB ` ! '
+marks a value that should be considered secret.
+.PP
+The hash of a key is computed over its textual encoding according to the syntax above.
+A certificate's signature value is produced by digitally signing using
+.I sigalg
+the hash (using
+.IR hashalg )
+of the concatenation of the value to be authenticated, the
+.I signer-name
+in
+.IR utf (6),
+a single space, and the
+.I exp-time
+in decimal (with no leading zeroes).
+When checking a signature, comparisons are done with values in internal multiple-precision form
+(ie, as
+.BR IPint s),
+not in base-64 form.
+.SH SEE ALSO
+.IR keyring-certtostr (2),
+.IR keyring-getmsg (2),
+.IR factotum (4),
+.IR keys (6),
+.IR getauthinfo (8)
+.SH BUGS
+The byte-array encoding of
+.B IPint
+should not require the leading zero;
+it does so for compatibility with old keys.
--- /dev/null
+++ b/man/6/login
@@ -1,0 +1,109 @@
+.TH LOGIN 6
+.SH NAME
+login \- key exchange protocol
+.SH DESCRIPTION
+The following encrypted key exchange protocol is used between a client such as
+.B login
+in
+.IR security-login (2),
+and a certificate signing process such as
+.IR logind (8),
+to justify the latter's issuing a certificate that can
+later be presented to an Inferno service
+to establish credentials.
+.PP
+A shared secret must previously be agreed between
+user and certifying authority (CA).
+It is used by the protocol to establish a secure channel between user and CA.
+.PP
+In the description below:
+.TF key(m)
+.PD
+.TP
+.I ivec
+is an 8 byte random number (`initialisation vector') chosen for
+this conversation.
+.TP
+.I sha
+is the 20 byte secure hash (SHA-1) of the password
+.TP
+.I key
+is an 8 byte secret formed as follows:
+.EX
+.br
+key[0] = ivec[0]^sha[0]^sha[8]^sha[16]
+key[1] = ivec[1]^sha[1]^sha[9]^sha[17]
+.EE
+\&...
+.EX
+key[5] = ivec[5]^sha[5]^sha[13];
+key[6] = ivec[6]^sha[6]^sha[14];
+key[7] = ivec[7]^sha[7]^sha[15];
+.EE
+.TP
+.I alpha
+is a Diffie-Hellman base used system wide
+.TP
+.I p
+is a Diffie-Hellman modulus used system wide
+.TP
+.I "key(m)"
+is
+.I m
+encrypted using the RC4 algorithm with
+.IR key .
+.TP
+.I Rx
+is a random number of the same order as
+.IR p .
+.TP
+.I "secret"
+is the Diffie-Hellman secret
+.IR "alpha**(r0*r1) mod p" .
+.PP
+The protocol follows.  ``user→CA xxx'' means that the user
+sends the message ``xxx'' to the certifying authority.
+Any party can send an error instead of a message at any
+point to terminate the protocol.
+.IP
+.EX
+user→CA	name
+CA→user	ACK
+.sp 1v
+user→CA	ivec
+CA→user	key(alpha**r0 mod p), alpha, p
+.sp 1v
+user→CA	alpha**r1 mod p
+CA→user	CA's public key, SHA(CA's public key + secret)
+.sp 1v
+user→CA	user's public key, SHA(user's public key + secret)
+CA→user	user's public key certificate
+.EE
+.PP
+The complexity of this protocol is intended to shield the password.
+To start a clear text attack against the password, one
+needs to first attack the Diffie-Hellman exponential
+to determine
+.IR "alpha**r0 mod p" .
+A possible weakness is that the encrypted quantity
+is base64 encoded, constraining
+the possible values of each byte.
+This could aid a brute force attack.
+.PP
+.I Alpha
+and
+.I p
+are sent unprotected, though the user code does a few sanity checks
+on the values it receives.
+This is another likely point of attack.
+We should like to know about any.
+.PP
+The role of
+.I ivec
+is to foil any replay attacks by someone spoofing the CA
+though this is probably overkill.
+.SH SEE ALSO
+.IR security-intro (2),
+.IR security-login (2),
+.IR logind (8),
+.IR signer (8)
--- /dev/null
+++ b/man/6/man
@@ -1,0 +1,248 @@
+.TH MAN 6
+.SH NAME
+man \- manual page format
+.SH DESCRIPTION
+The Inferno manual pages are stored as text files in a format originally defined for use with the Unix and Plan 9
+.I troff
+command, when used with the
+.I man
+macro package.
+Within Inferno, the format is interpreted by the programs of
+.IR man (1).
+.PP
+Except in
+.L .LR
+and
+.L .RL
+requests, any text argument denoted
+.I t
+in the request summary may be zero to six words.
+Quotes
+\fL"\fP ... \fL"\fP
+may be used to include blanks in a `word'.
+If
+.I t
+is empty,
+the special treatment is applied to
+the next text input line (the next line that doesn't begin with dot).
+In this way, for example,
+.B .I
+may be used to italicize a line of more than 6 words, or
+.B .SM
+followed by
+.B .B
+to make small letters in `bold' font.
+.PP
+A prevailing indent distance is remembered between
+successive indented paragraphs,
+and is reset to default value upon reaching a non-indented paragraph.
+Default units for indents
+.I i
+are ens.
+.PP
+The fonts are
+.TP
+.B R
+roman, the main font, preferred for diagnostics
+.PD 0
+.TP
+.B I
+italic, preferred for parameters, short names of commands,
+names of manual pages,
+and naked function names
+.TP
+.B B
+`bold', actually the constant width font,
+preferred for examples, file names, declarations, keywords, names of
+.B struct
+members, and literals
+(numbers are rarely literals)
+.TP
+.B L
+also the constant width font.
+For graphical display and printing,
+.BR L = B ;
+when converted to plain text, the
+arguments of the macros
+.BR .L ,
+.BR .LR ,
+and
+.B .RL
+are printed in quotes;
+preferred only where quotes really help (e.g. lower-case literals and
+punctuation).
+.PD
+.LP
+Type font and size are reset to default values
+before each paragraph, and after processing
+font- or size-setting macros.
+.PP
+The
+.I man
+format
+admits equations and tables in the style of Unix
+.I eqn
+(equation setting)
+and
+.I tbl
+(table layout)
+preprocessors,
+but do not support arguments on
+.B .EQ
+and
+.B .TS
+macros.
+.PP
+These strings are predefined by
+.IR man :
+.TP
+.B \e*R
+Trademark symbol
+.br
+.ns
+.TP
+.B \e*S
+Change to default type size.
+.SH FILES
+.TF /man/1/INDEX
+.TP
+.B /man/*/*
+Source files of manual pages.
+.SH SEE ALSO
+.IR man (1)
+.SH REQUESTS
+.ta \w'.TH n c x 'u +\w'Cause 'u +\w'Argument\ 'u
+.if 1 .di xx
+.if 1 			\ka
+.if 1 .br
+.if 1 .di
+.if 1 .in \nau
+.ti0
+Request	Cause	If no	Explanation
+.ti0
+	Break	Argument
+.ti0
+\&\fL.B\fR \fIt\fR	no	\fIt\fR=n.t.l.*	Text
+.I t
+is `bold'.
+.ti0
+\&\fL.BI\fR \fIt\fR	no	\fIt\fR=n.t.l.	Join
+words of
+.I t
+alternating bold and italic.
+.ti0
+\&\fL.BR\fR \fIt\fR	no	\fIt\fR=n.t.l.	Join
+words of
+.I t
+alternating bold and Roman.
+.ti0
+\&\fL.DT\fR	no		Restore default tabs.
+.ti0
+\&\fL.EE\fR	yes		End displayed example
+.ti0
+\&\fL.EX\fR	yes		Begin displayed example
+.ti0
+\&\fL.HP\fR \fIi\fR	yes	\fIi\fR=p.i.*	Set prevailing indent to
+.IR i .
+Begin paragraph with hanging indent.
+.ti0
+\&\fL.I\fR \fIt\fR	no	\fIt\fR=n.t.l.	Text
+.I t
+is italic.
+.ti0
+\&\fL.IB\fR \fIt\fR	no	\fIt\fR=n.t.l.	Join
+words of
+.I t
+alternating italic and bold.
+.ti0
+\&\fL.IP\fR \fIx i\fR	yes	\fIx\fR=""	Same as \fL.TP\fP with tag
+.IR x .
+.ti0
+\&\fL.IR\fR \fIt\fR	no	\fIt\fR=n.t.l.	Join
+words of
+.I t
+alternating italic and Roman.
+.ti0
+\&\fL.L\fR \fIt\fR	no	\fIt\fR=n.t.l.	Text
+.I t
+is literal.
+.ti0
+\&\fL.LP\fR	yes		Same as \fL.PP\fP.
+.ti0
+\&\fL.LR\fR \fIt\fR	no		Join 2
+words of
+.I t
+alternating literal and Roman.
+.ti0
+\&\fL.PD\fR \fId\fR	no	\fId\fR=\fL.4v\fP	Interparagraph distance is
+.IR d .
+.ti0
+\&\fL.PP\fR	yes		Begin paragraph.
+Set prevailing indent to default.
+.ti0
+\&\fL.RE\fR	yes		End of relative indent.
+Set prevailing indent to amount of starting \fL.RS\fP.
+.ti0
+\&\fL.RI\fR \fIt\fR	no	\fIt\fR=n.t.l.	Join
+words of
+.I t
+alternating Roman and italic.
+.ti0
+\&\fL.RL\fR \fIt\fR	no		Join 2 or 3
+words of
+.I t
+alternating Roman and literal.
+.ti0
+\&\fL.RS\fR \fIi\fR	yes	\fIi\fR=p.i.	Start relative indent,
+move left margin in distance
+.IR i .
+Set prevailing indent to default for nested indents.
+.ti0
+\&\fL.SH\fR \fIt\fR	yes	\fIt\fR=""	Subhead; reset paragraph distance.
+.ti0
+\&\fL.SM\fR \fIt\fR	no	\fIt\fR=n.t.l.	Text
+.I t
+is small.
+.ti0
+\&\fL.SS\fR \fIt\fR	no	\fIt\fR=""	Secondary subhead.
+.ti0
+\&\fL.TF\fR \fIs\fR	yes		Prevailing indent is wide as
+string
+.I s
+in font 
+.BR L ;
+paragraph distance is 0.
+.ti0
+\&\fL.TH\fR \fIn c x\fR	yes		Begin page named
+.I n
+of chapter
+.IR c;
+.I x
+is extra commentary, e.g. `local', for page head.
+Set prevailing indent and tabs to default.
+.ti0
+\&\fL.TP\fR \fIi\fR	yes	\fIi\fR=p.i.	Set prevailing indent to
+.IR i .
+Restore default indent if
+.IR i =0.
+Begin indented paragraph
+with hanging tag given by next text line.
+If tag doesn't fit, place it on separate line.
+.ti0
+\&\fL.1C\fR	yes		Equalize columns and return to 1-column output
+.ti0
+\&\fL.2C\fR	yes		Start 2-column nofill output
+.PP
+.ti0
+* n.t.l. = next text line; p.i. = prevailing indent
+.SH BUGS
+There's no way to include literal double quote marks
+.B \&"
+in font-alternation macros, such as
+.LR .BI .
+.br
+There is no direct way to suppress column widows in 2-column
+output; the column lengths may be adjusted by inserting
+.L .sp
+requests before the closing
+.LR .1C .
--- /dev/null
+++ b/man/6/namespace
@@ -1,0 +1,162 @@
+.TH NAMESPACE 6
+.SH NAME
+namespace \- name space description file
+.SH DESCRIPTION
+Namespace files describe how to construct a name space from scratch,
+or add to an existing name space.
+The files are interpreted by
+.IR newns (2),
+invoked by commands such as
+.IR logon (1)
+and by other system services.
+.PP
+The name space description file contains one or more
+lines each of which specifies one name space operation.
+Empty lines and lines with
+.B #
+as the first non-space character are ignored.
+Environment variables of the form
+.BI $ name
+are expanded within arguments, where
+.I name
+is a string terminated by white space, or one of the characters
+.LR / ,
+.LR . ,
+or
+.LR $ .
+.PP
+The known operations and their arguments are:
+.TP
+.B "bind [-abci] \f2old\fP \f2new\fP"
+Use 
+.I new
+as an alias for file or directory
+.IR old .
+Options
+.BR a ,
+.BR b
+and
+.BR c
+translate to flag values
+.BR Sys\->MAFTER ,
+.B Sys\->MBEFORE
+and
+.B Sys\->MCREATE
+of
+.IR sys-bind (2).
+If neither
+.B a
+nor
+.B b
+are given, the default is
+.BR Sys\->MREPL .
+Option
+.B i
+means to ignore errors.
+.TP
+.B cd \f2directory\fP
+Change working directory to
+.IR directory .
+.TP
+.B fork
+Split the name space before modification.
+This is equivalent to passing a value of
+.B Sys->FORKNS
+to
+.IR sys-pctl (2).
+.TP
+.B "mount [-abc9i] [-k \f2keyfile\fP] [-C \f2alg\fP] [\f2net\fP!]\f2machine\fP[!\f2svc\fP] \f2dir\fP [\f2spec\fP]"
+Mount the file tree of
+.I machine
+upon directory
+.IR dir .
+The default service is
+.BR styx .
+Options
+.BR a ,
+.BR b ,
+.B c
+and
+. B i
+are the same as for
+.BR bind .
+The tokens
+.I dir
+and
+.I spec
+translate to
+.I old
+and
+.I aname
+of
+.B mount
+as described under
+.IR sys-bind (2).
+The
+.BR -k ,
+.B -C
+and
+.B -9
+options are the same as those for the
+.I mount
+command (see
+.IR bind (1)).
+.TP
+.B "import [-abc9i] [\f2net\fP!]\f2machine\fP[!\f2svc\fP] [\f2remotedir\fP] \f2dir\fP"
+Import a directory from a remote Plan 9
+.I machine
+and make it appear on directory
+.IR dir .
+Options
+.BR a ,
+.BR b ,
+.BR c
+and
+.B i
+are the same as for
+.BR mount .
+By default,
+.I remotedir
+is assumed to be the same name as
+.IR dir .
+The default service is
+.BR exportfs .
+(Currently this operation works only under native Inferno and Plan 9.)
+.TP
+.B new
+Create a new name space in which the current directory
+becomes the root directory;
+typically used after
+.BR cd .
+This is equivalent to passing a value of
+.B Sys->NEWNS
+to
+.IR sys-pctl (2).
+.TP
+.B nodev
+Disallow device attaches.
+This is equivalent to passing a value of
+.B Sys->NODEVS
+to
+.IR sys-pctl (2).
+.TP
+.B unmount [-i] [\f2name\fP] \f2from\fP
+If two arguments are given, undo a
+.B bind
+or
+.B mount
+with the same arguments.
+If only one argument is given, everything bound to or mounted on
+.I from
+is unmounted.
+The
+.B -i
+option means ignore errors.
+.TP
+.BI "\&." " path"
+Include the name space description file
+.IR path .
+.SH SEE ALSO
+.IR nsbuild (1),
+.IR wm (1),
+.IR newns (2)
--- /dev/null
+++ b/man/6/ndb
@@ -1,0 +1,219 @@
+.TH NDB 6
+.SH NAME
+ndb \- network data base
+.SH SYNOPSIS
+.B /lib/ndb/local
+.br
+.B /lib/ndb/inferno
+.br
+.B /lib/ndb/dns
+.br
+.B /lib/ndb/common
+.SH DESCRIPTION
+Network configuration data is stored in one or more files in the directory
+.BR /lib/ndb ,
+in the attribute data base format defined by
+.IR attrdb (6).
+Most applications that use it start with
+.B /lib/ndb/local
+by default.
+That can refer (using a
+.B database
+entry) to other files that form part of the same logical database.
+They are stored in distinct files to allow different configurations
+to avoid duplicating data by sharing some common content.
+For example,
+.B /lib/ndb/common
+associates service names with port numbers for Internet protocols,
+and
+.B /lib/ndb/inferno
+provides the Inferno-specific port mapping.
+A database is automatically reread if any component file has
+changed since it was last read (based on its modification time).
+.PP
+A network database gives particular meaning to the following attributes:
+.TF dnsdomainxx
+.TP
+.B auth
+name or address of authentication server
+.TP
+.B bootf
+name of the file to send to a device on request when booting
+.TP
+.B dns
+name or address of DNS resolver (see also the
+.B ns
+attribute below)
+.TP
+.B dnsdomain
+domain in which the local host lives (used to qualify unqualified names)
+.TP
+.B dom
+Internet domain name (host or DNS zone)
+.TP
+.B ether
+Ethernet address in the form accepted by
+.IR ether (2)
+.TP
+.B fs
+name or address of file server
+.TP
+.B il
+IL service name
+.TP
+.B infernosite
+empty-valued attribute that labels default site parameters
+.TP
+.B ip
+Internet address
+.TP
+.B ipgw
+name or address of Internet gateway
+.TP
+.B ipmask
+Internet network mask
+.TP
+.B ipnet
+network or subnetwork name
+.TP
+.B ipv4proto
+IPv4 protocol number (see also
+.BR protocol )
+.TP
+.B mx
+mail exchanger
+.TP
+.B ns
+name of a DNS name server for an associated
+.B dom
+.TP
+.B port
+port number for a service
+.TP
+.B protocol
+protocol name
+.TP
+.B soa
+start of area (value is empty if locally authoritative, or
+.RB ` delegated '
+if another server is authoritative)
+.TP
+.B sys
+system name (short local name for a host)
+.TP
+.B tcp
+TCP service name
+.TP
+.B udp
+UDP service name
+.PD
+.PP
+Internet addresses and masks can be written in any form
+accepted by
+.IR Ip (2).
+Network applications might use other attributes of their own,
+which they document on their own pages.
+.PP
+.I Cs (8)
+interprets a host name of the form
+.BI $ server
+as referring to the set of names and addresses that are values
+of the attribute
+.I server
+in the network data base.
+Attributes in general use include some of those above, and the following:
+.TF FILESERVER
+.TP
+.B FILESERVER
+older name for file server
+.B fs
+file server
+.TP
+.B pop3
+mail reading service using the POP3 protocol
+.TP
+.B PROXY
+firewall proxy for
+.IR ftpfs (4)
+.TP
+.B SIGNER
+Inferno authentication server running
+.IR logind (8)
+and/or
+.IR signer (8)
+.TP
+.B smtp
+mail delivery gateway using the SMTP protocol
+.PD
+.PP
+Other such symbolic server names are mentioned in the manual pages for associated applications.
+.PP
+.IR Dns (8)
+interprets
+.BR dns
+and
+.BR dnsdomain
+values in any
+.B infernosite
+entry, and
+.BR dom ,
+.BR ip ,
+and
+.BR ns
+values more generally.
+When resolving a domain name,
+.I dns
+looks first in this data base to see if there is a
+.B dom
+entry for the name, and if so, whether there is either an associated
+.B ns
+attribute giving a name server to ask, or an
+.B ip
+attribute giving the name's IP address.
+Failing that, it works up the name hierarchy looking for name servers to ask.
+Failing that, it looks for a
+.B dnsdomain
+entry with
+.B dns
+attributes listing other resolvers to ask.
+Bootstrap data is commonly stored in
+.BR /lib/ndb/dns ;
+by default it defines the root name servers:
+.IP
+.EX
+dom=    # root
+	ns=a.root-servers.net
+	ns=b.root-servers.net
+	\f1...\fP
+dom=a.root-servers.net ip=198.41.0.4
+dom=b.root-servers.net ip=128.9.0.107
+	\f1...\fP
+.EE
+.SH EXAMPLE
+Specify the site's default DNS domain name and resolvers for
+.IR dns (8),
+and a collection of symbolic service names for
+.IR cs (8):
+.IP
+.EX
+infernosite=
+	dnsdomain=vitanuova.com
+	dns=200.1.1.11
+	dns=158.152.1.58
+	dns=158.152.1.43
+	SIGNER=doppio
+	FILESERVER=doppio
+	smtp=doppio
+	pop3=doppio
+	PROXY=doppio
+	GAMES=vivido
+	IRMSERVER=vivido
+.EE
+.SH FILES
+.B /lib/ndb/*
+.SH SEE ALSO
+.IR attrdb (2),
+.IR attrdb (6),
+.IR bootpd (8),
+.IR cs (8),
+.IR dns (8)
--- /dev/null
+++ b/man/6/plumbing
@@ -1,0 +1,163 @@
+.TH PLUMBING 6
+.SH NAME
+plumbing \- plumbing rules
+.SH DESCRIPTION
+Plumbing rules tell
+.IR plumber (8)
+how to route plumbing messages
+generated by applications using
+.IR plumbmsg (2).
+.PP
+The file is a set of rules separated by blank lines.
+Each
+rule
+is a set of patterns followed by a set of actions.
+The rules are interpreted in order.
+The first rule whose patterns all match is applied, and no further
+rules are examined.
+Comments start with
+.B #
+and continue to end of line.
+Single quotes protect special characters (use
+.B ''
+to get a single quote).
+.PP
+A
+pattern
+has the general form:
+.IP
+.I "field verb arg"
+.PP
+Each
+.I field
+corresponds to a field in the incoming plumbing message:
+.RS
+.TP
+.B src
+Source application
+.TP
+.B dst
+Destination port
+.TP
+.B dir
+Working directory
+.TP
+.B kind
+Format of the data (eg,
+.B text
+or
+.BR image )
+.TP
+.B attr
+A line of
+.IB name = value
+pairs
+.TP
+.B data
+The message data (an array of bytes)
+.RE
+.PP
+The verbs are:
+.RS
+.TF matches
+.PD
+.TP
+.B is
+Exact string comparison with
+.I arg
+.TP
+.B matches
+Regular expression comparison with
+.I arg
+.TP
+.B isdir
+.I Arg
+must name an existing directory
+.TP
+.B isfile
+.I Arg
+must name an existing file
+.TP
+.B set
+Set the value of the
+.I field
+to
+.IR arg .
+This verb operates in place, so put it after all other patterns in the rule.
+.RE
+.PP
+The
+.I arg
+can refer to one of the following variables:
+.RS
+.TF "$0 to $9 "
+.PD
+.TP
+.BR $0 " to " $9
+Substrings resulting from the most recent regular expression match:
+.B $0
+is the entire substring;
+.B $1
+the first parenthesised substring, etc.
+.TP
+.B $file
+The file name examined by the last
+.B isfile
+verb.
+.TP
+.B $dir
+The directory name examined by the last
+.B isdir
+verb.
+.RE
+.PP
+The following actions are provided:
+.RS
+.TP
+.BI "plumb to" " port"
+Route the message to the given plumbing
+.IR port .
+.TP
+.BI "plumb start" " command arg ..."
+If no program is currently listening on the current rule's
+.IR port ,
+start the
+.I command
+with the given arguments.
+The
+.RB ` $ '
+variables listed above can be used, to include part
+of the message in the command line arguments to the program.
+They are replaced in the command string by their actual values.
+.RE
+.PP
+For example, the following rule sends the names of module files\-
+file names ending with suffix
+.RB ` .m '\-
+to
+.IR wm-brutus (1),
+starting it if it is not already running:
+.IP
+.EX
+kind is text
+data matches '([a-zA-Z0-9]+\.m)(:[0-9]+)?'
+data isfile     /module/$1
+data set        /module/$0
+plumb to edit
+plumb start /dis/wm/brutus.dis $file$2
+.EE
+.PP
+Note the use of
+.B $2
+in the
+.B start
+action to pass
+.B brutus
+the line number selected by the second parenthesised
+expression in the pattern.
+.SH FILES
+.BI /usr/ user /lib/plumbing
+.SH SEE ALSO
+.IR plumb (1),
+.IR plumbmsg (2),
+.IR plumber (8)
--- /dev/null
+++ b/man/6/proto
@@ -1,0 +1,153 @@
+.TH PROTO 6
+.SH NAME
+proto \- file system prototype
+.SH DESCRIPTION
+A
+.I proto
+file defines a file system hierarchy, for
+programs that create, copy or operate on them,
+such as
+.IR fs (1)
+or
+.IR mkfs (8).
+The
+.I proto
+file defines the hierarchy as a set of names relative to an
+existing hierarchy, for instance in an existing file system or a list of path names
+in an archive.
+Files in the existing hierarchy that are not specified in the
+.I proto
+file
+are ignored.
+.IR Fsproto (2)
+provides functions to read a prototype file and enumerate the names it selects in an
+existing hierarchy.
+.PP
+Each line of the
+.I proto
+file specifies a file (where the term `file' includes directories).
+Indentation is significant,
+with each level of indentation corresponding to a level in the file tree.
+Each line contains up to five fields separated by white space:
+.IP
+.EX
+.I "name perm uid gid source"
+.EE
+.PP
+.I Name
+is the last path element in the resulting file tree.
+.I Perm
+specifies the permissions, as described below.
+.I Uid
+is the owner of the file,
+and
+.I gid
+is the group owning the file.
+.I Source
+is the name of a file in the current name space
+(not the source file tree) from which to copy
+.IR name 's
+content.
+All fields except
+.I name
+are optional.
+If a field such as
+.I perm
+or
+.I uid
+is not given, or is given as
+.LR - ,
+its value is taken from the existing file.
+.PP
+A
+.I name
+starting with
+.L $
+is taken as a reference to an environment variable (see
+.IR sh (1)
+and
+.IR env (3))
+and replaced by the value of that variable.
+If the first
+.I name
+in a directory is
+.LR + ,
+all of the files are represented, and all subdirectories recursively.
+If the first
+.I name
+is
+.LR * ,
+all of the names in the corresponding existing directory are represented,
+but only the names of subdirectories, not their content.
+If the first
+.I name
+is
+.LR % ,
+only non-directory names are represented excluding both the names and content
+of subdirectories.
+.PP
+.I Perm
+has the form:
+.IP
+.RB [ d ]
+.RB [ a ]
+.RB [ l ]
+.I oct
+.PP
+where the optional letters set file attributes
+.RL ( d
+directory,
+.L a
+append-only, and
+.L l
+exclusive-use),
+and
+.I oct
+is an octal number giving the permissions for user, group and others
+(see
+.IR chmod (1)).
+.SH EXAMPLES
+.PP
+Denote all files in a given file system:
+.IP
+.EX
++
+.EE
+.PP
+Denote all files in the current user's home directory:
+.IP
+.EX
+usr
+	$user
+.EE
+.PP
+Specify a subset of files in
+.BR /dis :
+.IP
+.EX
+dis
+	*
+	install
+		*
+	lib
+		arg.dis
+		names.dis
+.EE
+.SH FILES
+.TF /lib/proto/portproto
+.TP
+.B /lib/proto
+directory of prototype files
+.TP
+.B /lib/proto/all
+prototype for whole hierarchy (ie, line containing
+.LR + )
+.TP
+.B /lib/proto/portproto
+generic prototype file
+.SH "SEE ALSO"
+.IR fs (1),
+.IR fsproto (2),
+.IR kfs (4),
+.IR mkfs (8)
+
--- /dev/null
+++ b/man/6/regexp
@@ -1,0 +1,126 @@
+.TH REGEXP 6
+.SH NAME
+regexp, regex \- regular expression notation
+.SH DESCRIPTION
+A 
+.I "regular expression"
+specifies
+a set of strings of characters.
+A member of this set of strings is said to be
+.I matched
+by the regular expression.  In many applications
+a delimiter character, commonly
+.LR / ,
+bounds a regular expression.
+In the following specification for regular expressions
+the word `character' means any character (rune) but newline.
+.PP
+The syntax for a regular expression
+.B e0
+is
+.IP
+.EX
+e3:  literal | charclass | '.' | '^' | '$' | '(' e0 ')'
+
+e2:  e3
+  |  e2 REP
+
+REP: '*' | '+' | '?'
+
+e1:  e2
+  |  e1 e2
+
+e0:  e1
+  |  e0 '|' e1
+.EE
+.PP
+A
+.B literal
+is any non-metacharacter, or a metacharacter
+(one of
+.BR .*+?[]()|\e^$ ),
+or the delimiter
+preceded by 
+.LR \e .
+.PP
+A
+.B charclass
+is a nonempty string
+.I s
+bracketed
+.BI [ \|s\| ]
+(or
+.BI [^ s\| ]\fR);
+it matches any character in (or not in)
+.IR s .
+A negated character class never
+matches newline.
+A substring 
+.IB a - b\f1,
+with
+.I a
+and
+.I b
+in ascending
+order, stands for the inclusive
+range of
+characters between
+.I a
+and
+.IR b .
+In 
+.IR s ,
+the metacharacters
+.LR - ,
+.LR ] ,
+an initial
+.LR ^ ,
+and the regular expression delimiter
+must be preceded by a
+.LR \e ;
+other metacharacters 
+have no special meaning and
+may appear unescaped.
+.PP
+A 
+.L .
+matches any character.
+.PP
+A
+.L ^
+matches the beginning of a line;
+.L $
+matches the end of the line.
+.PP
+The 
+.B REP
+operators match zero or more
+.RB ( * ),
+one or more
+.RB ( + ),
+zero or one
+.RB ( ? ),
+instances respectively of the preceding regular expression 
+.BR e2 .
+.PP
+A concatenated regular expression,
+.BR "e1\|e2" ,
+matches a match to 
+.B e1
+followed by a match to
+.BR e2 .
+.PP
+An alternative regular expression,
+.BR "e0\||\|e1" ,
+matches either a match to
+.B e0
+or a match to
+.BR e1 .
+.PP
+A match to any part of a regular expression
+extends as far as possible without preventing
+a match to the remainder of the regular expression.
+.SH "SEE ALSO"
+.IR acme (1),
+.IR sh-regex (1),
+.IR regex (2)
--- /dev/null
+++ b/man/6/sbl
@@ -1,0 +1,392 @@
+.TH SBL 6
+.SH NAME
+sbl \- symbol table file
+.SH DESCRIPTION
+A Limbo symbol table file provides type information for the module
+in an associated Dis executable file,
+for use by debuggers and similar applications.
+They are written by the Limbo compiler
+when given the
+.B -g
+option.
+The files conventionally have a
+.B .sbl
+suffix; they need not be stored in the same directory as
+the corresponding Dis file.
+.PP
+The file consists of a header followed by five tables:
+.IP
+.I "header file-table pc-table type-table fn-table data-table"
+.PP
+Each table starts with a line containing a decimal
+count of the items in that table.
+The count can be zero. The following
+sections describe the format of table items in each table.
+In the description, the following terminals are used.
+.PP
+A
+.I "string"
+is a sequence of letters, digits, and the characters _, ., -, and >. Letters are the Unicode characters a through z and A through Z, and all Unicode characters with encoded values greater than A0 hexadecimal.
+.PP
+An
+.I int
+is an optional minus sign followed by a sequence of digits, 0 to 9.
+.PP
+In the following description,
+the presence of a space is represented by
+.B •
+and a newline by
+.BR \en .
+There are no other spaces between syntactic elements in the file format.
+Other special characters represent themselves.
+.SS Header
+.PP
+The header consists of two items.
+.ds Os "\v'0.2m'\s-3\|opt\s+3\^\v'-0.2m'
+.IP
+.EX
+.ft I
+.nf
+header:
+	magic\f5\en\fP
+	module\f5\en\fP
+magic:
+	\f5limbo .sbl 2.0\fP
+module:
+	string
+.ft R
+.EE
+.PP
+There have been two previous versions of symbol table format, distinguished
+by the number in
+.IR magic .
+Version
+.B 1.
+was the original; version
+.B 1.1
+added more references back to the source; and version
+.B 2.0
+replaced the original
+adt table by a type table and added support for Limbo's
+.B pick
+construction.
+Only version 2.0 is described here; the others are obsolete.
+.PP
+.I Module
+is the name of the module implemented in the Dis file.
+Symbol file references to identifiers declared by the implementation module are unqualified.
+A name referenced that is imported from any other module is preceded by
+.IB m ->
+where
+.I m
+is that module's identifier.
+.SS File table
+The file table is a list of file names referenced by the rest of the tables. Each file name is terminated by a newline character; within the name, any character other than a newline is valid.
+.SS PC table
+The PC table relates every instruction in the Dis file to the source from which it was compiled.
+The table is indexed by Dis PC to obtain a reference to the corresponding source.
+Each item consists of a source description
+and a statement reference:
+.IP
+.EX
+.ft I
+pc-item:
+	src stmt \f5\en\fP
+src:
+	pos\f5,\fPpos•
+pos:
+	file\f5:\fP\*(Os line\f5.\fP\*(Os char
+file, line, char, stmt:
+	int
+.ft R
+.EE
+.PP
+A source description
+.I src
+selects source text as a range of characters within lines of a source file.
+.I File
+is an index into the file table (origin 0);
+.I "line"
+and
+.I "char"
+are positions within that source file, with line numbers starting at 1 and character positions at 0. If
+.I "file"
+or
+.I "line"
+is omitted, it is assumed to have the previous value, or 0 if there is no previous value.
+.PP
+.I Stmt
+is the `statement number' of the instruction.
+Despite its name, the scope of a statement number is smaller than a Limbo statement:
+it identifies a region marked by the compiler for treatment as a unit when debugging.
+For instance,
+in a
+.B for
+statement, the initial expression, increment, test, and body all have unique
+statement numbers.
+All instructions compiled from the same region in a Limbo program have the same statement number.
+The number is that of the innermost region that contains the instruction.
+.SS Type table
+The type table describes the Limbo adts used in the Dis file,
+both locally declared and imported.
+Each entry describes a
+.IR type :
+.IP
+.EX
+.I "type:"
+.ft 5
+	@ \fItype-index\fP \en
+	a \fIadt-item\fP
+	p \fIadt-item\fP \fItag-table\fP
+	t \fIsize\fP . \fIid-table\fP
+	m \fImodule\fP \en \fIsrc\fP
+	A \fItype\fP
+	C \fItype\fP
+	F \fIfn-name\fP \fItype\fP
+	L \fItype\fP
+	R \fItype\fP
+	n
+	N
+	B
+	b
+	i
+	f
+	s
+.ft I
+type-index:
+	int
+.EE
+.PP
+Each leading character specifies a different Limbo type:
+.IP
+.RS
+.TF N
+.TP 5n
+.B @
+existing type referenced by type table index (not self-referential)
+.TP
+.B A
+.B array of
+.I type
+.TP
+.B a
+.B adt
+without
+.B pick
+.TP
+.B B
+.B big
+.TP
+.B b
+.B byte
+.TP
+.B
+C
+.B chan of
+.I type
+.TP
+.B
+F
+.B fn
+returning
+.I type
+.TP
+.B f
+.B real
+.TP
+.B i
+.B int
+.TP
+.B L
+.B list of
+.I type
+.TP
+.B m
+.B module
+.TP
+.B N
+.BR nil 's
+type
+.TP
+.B n
+no type (eg, function with no return type)
+.TP
+.B p
+.B adt
+with
+.B pick
+.TP
+.B R
+.B ref
+.I type
+.TP
+.B s
+.B string
+.TP
+.B t
+tuple
+.PD
+.RE
+.PP
+A
+.I type-index
+is an offset in the type table, representing the corresponding type.
+.I Size
+is the size in bytes of a value of the given type.
+The size of basic types is known and does not appear explicitly:
+.B big
+and
+.B real
+are 64 bits; all others including strings and reference types are 4 bytes.
+(Strings are represented internally by a pointer.)
+.PP
+Each
+.I "id-table"
+is preceded by a count of the number of entries, followed by
+an
+.I id-item
+for each entry in the table:
+.IP
+.EX
+.ft I
+id-table:
+	count \f5\en\fP id-items
+id-item:
+	id-offset \f5:\fP id-name \f5:\fP src type \f5\en\fP
+id-offset:
+	int
+id-name:
+	string
+count:
+	int
+.ft R
+.EE
+.PP
+.I Id-offset
+is the number of bytes from the start of the enclosing object to the value of the
+object identified by
+.IR "id-name" .
+.PP
+An adt without a
+.B pick
+clause is described using an
+.IR adt-item :
+.IP
+.EX
+.ft I
+adt-item:
+	adt-name • src size \f5\en\fP id-table
+adt-name:
+	string
+size:
+	int
+.ft R
+.EE
+.PP
+The
+.I id-table
+has an entry for every data element of
+.IR "adt-name" .
+.PP
+If an adt has a
+.B pick
+clause, the adt's type table entry uses the
+.B p
+type character.
+The invariant part of the adt is described by an
+.I adt-item
+(with zero
+.IR size )
+and the variant clause is described by a
+.I tag-table
+of the following form:
+.IP
+.EX
+.ft I
+tag-table:
+	count \f5\en\fP tag-items
+tag-item:
+	name \f5:\fP src size \f5\en\fP id-table
+  \f1|\fP	name \f5:\fP src \f5\en\fP
+.ft R
+.EE
+.PP
+The
+.I tag-table
+describes the variants of a Limbo adt
+that includes a
+.B pick
+clause.
+The
+.I name
+is the
+.B pick
+tag for the associated alternative.
+If the
+.I size
+and
+.I id-table
+are missing, the given variant has the same description
+as the previous one.
+.PP
+A tuple's
+.I id-table
+contains
+.I id-names
+of the form
+.BI t n,
+where
+.I n
+is the 0-origin index of the item in the tuple.
+.PP
+.I Src
+is as previously defined above in the PC table section.
+.SS Fn table
+The function table describes each function implemented by the Dis file.
+.IP
+.EX
+.ft I
+fn-item:
+	fn-pc \f5:\fP fn-name \f5\en\fP args locals return
+fn-pc:
+	int
+fn-name:
+	string
+args, locals:
+	id-table
+return:
+	type
+.ft R
+.EE
+.PP
+.I Fn-pc
+is the starting pc for the Dis instructions for
+.IR "fn-name" .
+If the function is a member of an adt,
+the member name is qualified by the adt name, so that
+.I fn-name
+has the form
+.IB `adt-name . member-name'.
+Within the
+.I id-tables
+for
+.I args
+and
+.IR locals ,
+the
+.I "id-offset"
+fields give offsets relative to the function's activation frame.
+Furthermore, no table entries are made for
+.I args
+that are declared
+.B nil
+(unused).
+.SS Data table
+The data table describes the global variables in the Dis file. It is an
+.IR "id-table" ,
+with one entry for each global variable.
+.SH SEE ALSO
+.IR limbo (1),
+.IR wm-deb (1),
+.IR debug (2),
+.IR dis (6)
--- /dev/null
+++ b/man/6/scancode
@@ -1,0 +1,37 @@
+.TH SCANCODE 6
+.SH NAME
+scancode \- known formats of scan codes
+.SH DESCRIPTION
+The
+.B scancode
+interface of
+.IR cons (3)
+provides access to scan codes of the primary keyboard. There
+are as many scan code formats as keyboards. Here are some
+common ones.
+.SH Microsoft Windows™ Virtual Key
+.PP
+The identifier is
+.BR emu_win32vk ,
+and each scan code is encoded in the two byte format specified in
+.IR cons(3) .
+This format is used by the Microsoft Windows™ implementation of
+.IR emu (1E),
+and each scan code is a
+.IR "Virtual Key" .
+See the appropriate Microsoft documentation for the meaning of the
+codes.
+.SH X11
+.PP
+The identifier is
+.BR emu_x11 ,
+and each scan code is encoded in the single byte format specified in
+.IR cons(3) .
+This format is used by the various Unix/X11 implementations of
+.IR emu (1E),
+and each scan code is an X11
+.IR keycode .
+See the appropriate X11 documentation for the meaning of the
+codes.
+.SH "SEE ALSO"
+.IR cons (3)
--- /dev/null
+++ b/man/6/sexprs
@@ -1,0 +1,238 @@
+.TH SEXPRS 6
+.SH NAME
+sexprs \- symbolic expressions
+.SH DESCRIPTION
+S-expressions (`symbolic expressions') provide a way for programs to store and
+exchange tree-structured text and binary data.
+The Limbo module
+.IR sexprs (2)
+provides the variant defined by
+Rivest in Internet Draft
+.L draft-rivest-sexp-00.txt
+(4 May 1997),
+as used for instance by the Simple Public Key Infrastructure (SPKI).
+It provides a `canonical' form of S-expression,
+and an `advanced' form for display.
+They can convey binary data directly and efficiently, unlike some
+other schemes such as XML.
+The two forms are closely related and all can be read or written by
+.IR sexprs (2),
+including a variant sometimes used for transport on links that are not 8-bit safe.
+.PP
+An S-expression is either a sequence of bytes (a byte
+.IR string ),
+or a parenthesised list of smaller S-expressions.
+All forms start with the fundamental rules below, in extended BNF:
+.IP
+.EX
+.ft R
+.ta \w'\f2simple-stringxxxxx\f1'u +\w'\ ::=\ 'u
+\f2sexpr\fP	::=	\f2string\fP | \f2list\fP
+\f2list\fP	::=	'(' \f2sexpr\fP* ')'
+.EE
+.DT
+.PD
+.PP
+They give the recursive structure.
+The various representations ultimately differ only in how the byte string is represented
+and whether white space such as blanks or newlines can appear.
+.PP
+Furthermore, the definition of
+.I string
+is also common to all forms:
+.IP
+.EX
+.ft R
+.ta \w'\f2simple-stringxxxxx\f1'u +\w'\ ::=\ 'u
+\f2string\fP	::=	\f2display\fP? \f2simple-string\fP
+\f2display\fP	::=	'[' \f2simple-string\fP ']'
+.EE
+.DT
+.PD
+.PP
+The optional bracketed
+.I display
+string provides information on how to present the associated byte string to a user.
+(``It has no other function.  Many of the MIME types work here.'')
+Although supported by
+.IR sexprs (2),
+it is largely unused by Inferno applications and is usually left out.
+The canonical and advanced forms differ in their definitions of
+.IR simple-string .
+They always denote sequences of 8-bit bytes, but with different syntax (encodings).
+Two
+.I strings
+are equal iff their
+.I simple-strings
+encode the same byte strings (for both data and
+.IR display ).
+.PP
+.I Canonical
+form must be used when exchanging S-expressions between computers,
+and when digitally signing an expression.
+It is defined by the complete set of rules below:
+.IP
+.EX
+.ft R
+.ta \w'\f2simple-stringxxxxx\f1'u +\w'\ ::=\ 'u
+\f2sexpr\fP	::=	\f2string\fP | \f2list\fP
+\f2list\fP	::=	'(' \f2sexpr\fP* ')'
+\f2string\fP	::=	\f2display\fP? \f2simple-string\fP
+\f2display\fP	::=	'[' \f2simple-string\fP ']'
+\f2simple-string\fP	::=	\f2raw\fP
+\f2raw\fP	::=	\f2nbytes\fP ':' \f2byte*\fP
+\f2nbytes\fP	::=	\f5[1-9][0-9]\fP+ |  \f50\fP
+.EE
+.DT
+.PD
+.PP
+Its
+.I simple-string
+is a raw byte string.
+The primitive
+.I byte
+represents an 8-bit byte.
+The length of every byte string is given explicitly by a preceding decimal value
+.I nbytes
+(with no leading zeroes).
+There is no white space.
+It is `canonical' because it is uniquely defined for each S-expression.
+It is efficient to parse even on small computers.
+.PP
+.I Advanced
+form is more elaborate, and has two main differences:
+not all byte strings need an explicit length, and binary
+data can be represented in printable form, either using hexadecimal or base 64 encodings,
+or using quoted strings (with escape sequences similar to those of Limbo or C).
+Unquoted text is called a
+.IR token ,
+and is restricted by the standard to a specific alphabet:
+it must contain only letters, digits, or characters from the set
+.LR "-./_:*+=" ,
+and must not start with a digit.
+The latter restriction is imposed to allow byte counts to be distinguished from tokens without
+lookahead, but has the consequence that decimal numbers must be quoted,
+as must non-ASCII characters in
+.IR utf (6)
+encoding.
+Upper- and lower-case letters are distinct.
+The advanced transport syntax is defined by the complete set of rules below:
+.IP
+.EX
+.ft R
+.ta \w'\f2simple-stringxxxxx\f1'u +\w'\ ::=\ 'u
+\f2sexpr\fP	::=	\f2string\fP | \f2list\fP
+\f2list\fP	::=	'(' ( \f2sexpr\fP | \f2whitespace\fP )* ')'
+\f2string\fP	::=	\f2display\fP? \f2simple-string\fP
+\f2display\fP	::=	'[' \f2simple-string\fP ']'
+\f2simple-string\fP	::=	\f2raw\fP | \f2token\fP | \f2base-64\fP | \f2hexadecimal\fP |  \f2quoted-string\fP
+\f2raw\fP	::=	\f2nbytes\fP ':' \f2byte*\fP
+\f2nbytes\fP	::=	\f5[1-9][0-9]\fP+ |  \f50\fP
+\f2token\fP	::=	\f2token-start\fP \f2token-char*\fP
+\f2base-64\fP	::=	\f2decimal\fP? '|' ( \f2base-64-char\fP | \f2whitespace\fP )* '|'
+\f2hexadecimal\fP	::=	'#' ( \f2hex-digit\fP | \f2whitespace\fP )* '#'
+\f2quoted-string\fP	::=	\f2nbytes\fP? \f2quoted-string-body\fP  
+\f2quoted-string-body\fP	::=	'"' \f2byte*\fP '"'
+\f2token-start\fP	::=	\f5[-./_:*+=a-zA-Z]\fP
+\f2token-char\fP	::=	\f2token-start\fP | \f5[0-9]\fP
+\f2hex-digit\fP	::=	\f5[0-9a-fA-F]\fP
+\f2base-64-char\fP	::=	\f5[a-zA-Z0-9+/=]\fP
+.EE
+.PD
+.DT
+.PP
+.I Whitespace
+is any sequence of blank, tab, newline or carriage-return characters;
+note that it can appear only at the places shown.
+The
+.I bytes
+in a
+.I quoted-string-body
+are interpreted according to the quoting rules for Limbo (or C).
+That is, the bytes are enclosed in quotes, and may contain the
+escape sequences for the following characters:
+backspace
+.RB ( \eb ),
+form-feed
+.RB ( \ef ),
+newline
+.RB ( \en ),
+carriage-return
+.RB ( \er ),
+tab
+.RB ( \et ),
+and vertical tab
+.RB ( \ev ),
+octal escape
+.BI \e ooo
+(all three digits must be given),
+hexadecimal escape
+.BI \ex hh
+(both digits must be given),
+.B \e\e
+for backslash,
+.B \e'
+for single quote, and
+and \f5\e"\fP to include a quote in a string.
+Note that a quoted string can have an optional
+.IR nbytes ,
+but it gives the length of the byte string resulting
+.I after
+interpreting character escapes.
+.PP
+Both canonical and advanced forms can contain binary data verbatim.
+Sometimes that is troublesome for storage or transport.
+At the lexical level any
+.I sexpr
+can therefore be replaced by the following:
+.IP
+.EX
+.ft R
+\&'{' ( \f2base-64-char\fP | \f2whitespace\fP )* '}'
+.EE
+.PP
+where the text between the braces is the base-64 encoding of the
+.I sexpr
+expressed in canonical or advanced form.
+The S-expression parser will replace the sequence by its decoded, and resume
+parsing at the start of that byte string.
+Note the difference in syntax and interpretation from rule
+.IR base-64
+above, which encodes a
+.IR simple-string ,
+not an
+.IR sexpr .
+.SH EXAMPLES
+The following S-expression is in canonical form:
+.IP
+.EX
+(12:hello world!(5:inner0:))
+.EE
+.PP
+It is a list of two elements: the string
+.BR "hello world!" ,
+and another list also with two elements,
+the string
+.BR inner
+and an empty string.
+All the bytes in the example are printable characters, but they could have been arbitrary binary values.
+.PP
+The following is an S-expression in advanced form:
+.IP
+.EX
+(hello-world
+    (* "3" "5.6")
+    (best-of-3 (5:inner0:)))
+.EE
+.PP
+Note that advanced form contains canonical form as a subset;
+here it is used for the innermost list.
+.SH SEE ALSO
+.IR sexprs (2),
+.IR json (6),
+.IR ubfa (6)
+.PP
+R. Rivest, ``S-expressions'', Network Working Group Internet Draft
+(4 May 1997),
+reproduced in
+.BR /lib/sexp .
--- /dev/null
+++ b/man/6/translate
@@ -1,0 +1,55 @@
+.TH TRANSLATE 6
+.SH NAME
+translate \- translation dictionary
+.SH SYNOPSIS
+.BI /locale/dict/ app
+.br
+.BI /locale/ locale /dict/ app
+.SH DESCRIPTION
+A dictionary file provides translation text that applications can access using
+.IR translate (2).
+Each is a Unicode file containing a set of translations, one per line,
+each line having the following syntax:
+.IP
+.EX
+"\fIsource-text\fP"\fR [\fP (\fInote-text\fP)\fR ] [ \fP="\fItarget-text\fP" \fR ] \fP
+.EE
+.PP
+which defines
+.I target-text
+as the translation for
+.I source-text .
+A missing translation clause defines the identity translation.
+The optional
+.I note-text
+qualifies the context of the
+.IR source-text ,
+when the same phrase in the source language might
+translate to different phrases in the target language,
+or to distinguish a particular context in the program (eg, particular menus):
+For example:
+.IP
+.EX
+"Times"(newspaper) = "La Republicca"
+"Times"(timetable) = "L'orario"
+"ABC"(keypad) = "abc"
+.EE
+.PP
+Empty lines and lines beginning with
+.B #
+are ignored.
+The quoted strings can contain the Limbo escape sequences
+.B "\en"
+(newline),
+.B "\et"
+(tab),
+.B "\er"
+(carriage return),
+and
+.B "\e\e"
+(backslash).
+.PP
+The default locale is set by binding one or more specific locales onto
+.BR /locale/dict .
+.SH FILES
+.B /locale/*/dict/*
--- /dev/null
+++ b/man/6/ubfa
@@ -1,0 +1,129 @@
+.TH UBFA 6
+.SH NAME
+ubfa \- universal binary format for data transport
+.SH DESCRIPTION
+.I UBF(A)
+is the data transport encoding for Armstrong's
+Universal Binary Format.
+It provides four primitive types: atoms (symbolic constants), integers, strings, and binary data.
+There are two compound types: fixed-length tuples and variable-length lists.
+.IR Ubfa (2)
+provides basic support in Limbo for reading and writing streams of UBF(A)-encoded data.
+.PP
+The
+.I input
+syntax is defined by the following rules:
+.IP
+.EX
+.ft R
+.ta \w'\f2simple-xxx\f1'u +\w'\ ::=\ 'u
+\f2input\fP	::=	\f2item\fP* '$'
+\f2item\fP	::=	\f2integer\fP | \f2atom\fP | \f2string\fP | \f2binary\fP | \f2tuple\fP | \f2list\fP | \f2store\fP | \f2push\fP | \f2comment\fP | \f2tag\fP
+
+\f2integer\fP	::=	\f5'-'\fP?\f5[0-9]\fP+
+\f2atom\fP	::=	"'" (\f5[^\e']\fP | '\e\e' | "\e'")* "'"
+\f2string\fP	::=	'"' (\f5[^\e"]\fP | '\e\e' | '\e"')* '"'
+\f2binary\fP	::=	'~' \f2byte\fP* '~'      # preceded by \f2integer\fP byte count
+
+\f2tuple\fP	::=	'{' \f2item\fP* '}'
+\f2list\fP	::=	'#' (\f2item\fP '&')*
+
+\f2store\fP	::=	'>' \f2reg\fP
+\f2push\fP	::=	\f2reg\fP
+\f2reg\fP	::=	\f5[^-%"~'`{}#& \en\er\et,0-9]\fP
+\f2comment\fP	::=	'%' (\f5[^\e%]\fP | '\e\e' | '\e%')* '%'
+\f2tag\fP	::=	'`' (\f5[^\e`]\fP | '\e\e' | '\e`')* '`'
+.EE
+.PD
+.DT
+.PP
+White space is any sequence of blank, tab, newline or carriage-return characters, and can appear
+before or after any instance of
+.I item
+in the grammar.
+.PP
+The
+.I input
+data is interpreted by a simple virtual machine.
+The machine contains a stack of values of primitive and compound types, and a set of registers also containing
+values of those types.
+White space and comments are ignored.
+Primitive
+.IR integer ,
+.I atom
+and
+.IR string
+values are pushed onto the stack as they are recognised.
+Certain input bytes outside any value act as operators:
+.TP
+.B {
+Note the current stack depth.
+.TP
+.B }
+Pop stack values to restore the most recently noted stack depth.
+Push a single value
+representing a tuple of those items; the left-most value in the tuple is the last one popped
+(the first in the original input stream).
+.TP
+.B ~
+Pop an integer value
+.I n
+from the stack.
+Read
+.I n
+bytes from the input stream and push a value onto the stack that represents them.
+The next byte must be the character
+.BR ~ ,
+which is discarded.
+.TP
+.B #
+Push a value representing an empty list onto the stack.
+.TP
+.B &
+Pop a value
+.IR v .
+Pop another value
+.IR l ,
+which must represent a list.
+Push a value that represents
+the list
+.IB v :: l .
+(Note that the items in a
+.I list
+therefore appear in reverse order in the input stream.)
+.TP
+.BI > reg
+Pop the top value from the stack and store it in a register labelled by the byte
+.IR reg .
+.TP
+.I reg
+Push the value of register
+.I reg
+(which must be non-null) onto the stack.
+.TP
+.I tag
+Associate the tag string with the value on top of the stack.
+The
+.IR ubfa (2)
+implementation does so by replacing it by a special
+.B Tag
+tuple.
+.TP
+.B $
+End-of-input: there must be exactly one value on the stack,
+which is the result.
+.PP
+Applications using UBF(A) typically take turns to exchange
+.I input
+values on a communication channel.
+.SH SEE ALSO
+.IR ubfa (2),
+.IR json (6),
+.IR sexprs (6)
+.br
+J L Armstrong, ``Getting Erlang to talk to the outside world'',
+.I "ACM SIGPLAN Erlang workshop 2002" ,
+Pittsburg, PA USA
+.br
+UBF web page,
+.B "http://www.sics.se/~joe/ubf/"
--- /dev/null
+++ b/man/6/users
@@ -1,0 +1,73 @@
+.TH USERS 6
+.SH NAME
+users \- kfs file server user list format
+.SH DESCRIPTION
+The permanent file server
+.IR kfs (4)
+maintains a private list of users
+and groups, in
+.B /adm/users
+by convention.
+Each line in the file has the format
+.IP
+.IB num : name : leader :\fImembers\fP
+.PP
+where
+.I num
+is a decimal integer,
+.I name
+and
+.I leader
+are printable strings excluding the characters
+.LR ? ,
+.LR = ,
+.LR + ,
+.LR - ,
+.LR / ,
+and
+.LR : ,
+and
+.I members
+is a comma-separated list of such strings.
+Such a line defines a user and a group with the given
+.IR name ;
+the group has a group leader given by
+.I leader
+and group members given by the user names in
+.IR members .
+The
+.I leader
+field may be empty,
+in which case any group member is a group leader.
+The
+.I members
+field may be empty.
+.PP
+Lines beginning with
+.L #
+are ignored.
+.PP
+The
+.I num
+in a line is a number used internally by a file server;
+there should be no duplicate
+.IR num s
+in the file.
+A negative
+.I num
+is special: a user with a negative
+.I num
+cannot attach to the file server.
+The file
+.B /adm/users
+itself is owned by user
+.IR adm ,
+having a negative
+.IR num ,
+and write protected to others,
+so it can only be changed via console commands.
+.SH "SEE ALSO"
+.IR kfs (4),
+.IR intro (5),
+.IR stat (5),
+.IR kfscmd (8)
--- /dev/null
+++ b/man/6/utf
@@ -1,0 +1,108 @@
+.TH UTF 6
+.SH NAME
+UTF, Unicode, ASCII, rune \- character set and format
+.SH DESCRIPTION
+The Inferno character set and representation are
+based on the Unicode Standard and on the ISO multibyte
+.SM UTF-8
+encoding (Universal Character
+Set Transformation Format, 8 bits wide).
+The Unicode Standard represents its characters in 21
+bits;
+.SM UTF-8
+represents such
+values in an 8-bit byte stream.
+Throughout this manual,
+.SM UTF-8
+is shortened to
+.SM UTF.
+.PP
+Internally, programs store individual Unicode characters as 32-bit integers,
+of which only 21 bits are currently used.
+Documentation often refers to them as `runes', following Plan 9.
+However, any external manifestation of textual information,
+in files or at the interface between programs, uses the
+machine-independent, byte-stream encoding called
+.SM UTF.
+.PP
+.SM UTF
+is designed so the 7-bit
+.SM ASCII
+set (values hexadecimal 00 to 7F),
+appear only as themselves
+in the encoding.
+Characters with values above 7F appear as sequences of two or more
+bytes with values only from 80 to FF.
+.PP
+The
+.SM UTF
+encoding of the Unicode Standard is backward compatible with
+.SM ASCII\c
+: programs presented only with
+.SM ASCII
+work on Inferno
+even if not written to deal with
+.SM UTF\c
+,
+as do
+programs that deal with uninterpreted byte streams.
+However, programs that perform semantic processing on
+characters must convert from
+.SM UTF
+to runes
+in order to work properly with non-\c
+.SM ASCII
+input.
+Normally, all necessary conversions are done by the Limbo compiler
+and execution envirnoment, when converting between
+.B "array of byte"
+and
+.B "string" ,
+but sometimes more is needed, such
+as when a program receives
+.SM UTF
+input one byte at a time;
+see
+.IR sys-byte2char (2)
+for routines to handle such processing.
+.PP
+Letting numbers be binary,
+a rune x is converted to a multibyte
+.SM UTF
+sequence
+as follows:
+.PP
+01.   x in [000000.00000000.0bbbbbbb] → 0bbbbbbb
+.br
+10.   x in [000000.00000bbb.bbbbbbbb] → 110bbbbb, 10bbbbbb
+.br
+11.   x in [000000.bbbbbbbb.bbbbbbbb] → 1110bbbb, 10bbbbbb, 10bbbbbb
+.br
+100. x in [bbbbbb.bbbbbbbb.bbbbbbbb] → 1110bbbb, 10bbbbbb, 10bbbbbb, 10bbbbbb
+.br
+.PP
+.PP
+Conversion 01 provides a one-byte sequence that spans the
+.SM ASCII
+character set in a compatible way.
+Conversions 10, 11 and 100 represent higher-valued characters
+as sequences of two, three or four bytes with the high bit set.
+Inferno does not support the 5 and 6 byte sequences proposed by X-Open.
+When there are multiple ways to encode a value, for example rune 0,
+the shortest encoding is used.
+.PP
+In the inverse mapping,
+any sequence except those described above
+is incorrect and is converted to the rune hexadecimal FFFD.
+.SH FILES
+.TF "/lib/unicode "
+.TP
+.B /lib/unicode
+table of characters and descriptions, suitable for
+.IR look (1).
+.SH "SEE ALSO"
+.IR ascii (1),
+.IR tcs (1),
+.IR sys-byte2char (2),
+.IR keyboard (6), 
+.IR "The Unicode Standard" .
--- /dev/null
+++ b/man/7/0intro
@@ -1,0 +1,6 @@
+.TH INTRO 7
+.SH NAME
+intro \- introduction to databases
+.SH DESCRIPTION
+This manual section describes databases available on Inferno
+and the commands and modules that access them.
--- /dev/null
+++ b/man/7/INDEX
@@ -1,0 +1,4 @@
+intro 0intro
+cddb cddb
+db db
+dbsrv dbsrv
--- /dev/null
+++ b/man/7/cddb
@@ -1,0 +1,56 @@
+.TH CDDB 7
+.SH NAME
+cddb \- CD database
+.SH SYNOPSIS
+.B cddb
+[
+.B -DTt
+]
+[
+.B -s
+.I server
+]
+.B query
+.I diskid
+.I ntracks
+.I track0id
+.I ...
+.SH DESCRIPTION
+.I Cddb
+queries an Internet database to get a table of
+contents and other details for audio CDs.
+The CD has
+.I ntracks
+tracks, and
+.I trackNid
+is a hash value derived from the
+.IR N 'th
+track's characteristics;
+.I diskid
+is a hash of all the track IDs.
+.PP
+.I Cddb
+takes 4 optional arguments.
+The
+.B -s server
+option causes
+.I cddb
+to use a different server for the query
+(default is
+.IR freedb.freedb.org ).
+The
+.B -D
+option causes the raw database response from the server to be dumped
+to standard output.
+The
+.I -t
+option causes the time of each track to be appended to the normal output.
+.I -T
+prints track times and
+adds a line at the end with the total time.
+.SH SOURCE
+.B /appl/cmd/cddb.b
+.SH SEE ALSO
+.I 9660srv
+(in
+.IR dossrv (4))
--- /dev/null
+++ b/man/7/db
@@ -1,0 +1,187 @@
+.TH DB 7
+.SH NAME
+DB \- database support
+.SH SYNOPSIS
+.EX
+include "security.m";   # need Auth for algorithm names
+include "db.m";
+db := load DB DB->PATH;
+
+DB_Handle: adt {
+   SQLOpen:     fn(oldh: self ref DB_Handle): (int, ref DB_Handle);
+   SQLClose:    fn(dbh: self ref DB_Handle): int;
+   SQL:         fn(handle: self ref DB_Handle, command: string):
+                  (int, string);
+   columns:     fn(handle: self ref DB_Handle): int;
+   nextRow:     fn(handle: self ref DB_Handle): int;
+   read:        fn(handle: self ref DB_Handle, column: int):
+                  (int, array of byte);
+   write:       fn(handle: self ref DB_Handle, param: int,
+                  fld: array of byte): int
+   columnTitle: fn(handle: self ref DB_Handle,
+                    column: int): string;
+   errmsg:      fn(handle: self ref DB_Handle): string;
+
+   datafd:    ref Sys->FD;
+   sqlconn:   int;
+   sqlstream: int;
+   lock:      chan of int;
+};
+
+connect:  fn(addr, alg: string): (ref Sys->FD, string);
+dbopen:   fn(fd: ref Sys->FD, username, password, dbname: string):
+            (ref DB_Handle, list of string);
+open:     fn(addr, username, password, dbname: string):
+            (ref DB_Handle, list of string);
+.EE
+.SH DESCRIPTION
+.B DB
+allows Limbo programs to connect to data base management systems
+that support an ODBC interface.
+.PP
+.IR Dbsrv (7)
+must be running (usually in a hosted
+.IR emu (1))
+to service database requests.
+.PP
+If security features will be used in conjunction with
+.BR DB ,
+the
+.B Auth
+module definitions (see
+.IR security-auth (2))
+from
+.B security.m
+must be included.
+.PP
+If authentication is in use,
+.B DB
+will use the certificate in the file
+.IP
+.BI /usr/ user /keyring/ net ! machine
+.PP
+if that file exists. Otherwise,
+.I db
+will attempt to find a certificate in the file
+.IP
+.BI /usr/ user /keyring/default .
+.PP
+.B Connect
+establishes a connection to the
+.I dbsrv
+at
+.IR addr .
+.I Addr
+has the form
+.IB machine ! service.
+.I Machine
+is a symbolic or numeric network address, and
+.I service
+is a service or port on that machine.
+.I Dbsrv
+starts a corresponding
+.I infdb
+process. After some negotiation,
+.I infdb
+will take the appropriate authentication, digesting, and decryption actions, based on the
+.I alg
+requested by the client.
+If successful,
+.B connect
+will return a file descriptor required by
+.BR dbopen .
+.PP
+.B Dbopen
+initiates a session with a database
+.IR dbname ,
+passing
+.I username
+and
+.I password
+down the
+.I fd
+returned by
+.BR connect .
+.B Dbopen
+returns a reference to an instance of
+.B DB_Handle
+and an error string, which is nil on success.
+On error, the reference is nil, but the string contains a diagnostic.
+.B Dbopen
+implicitly opens an initial SQL stream via
+.BR SQLOpen .
+.PP
+.B Open
+returns the result of a
+.B connect
+followed (if successful) by a
+.BR dbopen .
+.TP
+.IB oh .SQLOpen
+Creates a new
+.B DB_Handle
+and opens a new SQL stream with which requests can be associated. The new handle shares the connection and transaction context with
+.IR oh .
+Other characteristics such as the current SQL command, the result set, and cursor position are independent,
+which allows the manipulation and interaction of many SQL statements within a transaction.
+It returns zero on success, non-zero on error.
+.TP
+.IB h .SQLClose
+Closes the SQL stream opened by
+.BR SQLOpen .
+Closing all SQL streams associated with a connection causes the connection to be closed.
+.TP
+.IB h .SQL( command )
+Sends the SQL statement
+.I command
+to the database. If the call fails, the first element of the returned tuple is non-zero and the second is an error message.
+.TP
+.IB h .columns()
+Returns the number of columns in the result set of the previous SQL select command sent to the database. A returned value of 0 indicates there was a problem with the previous command, or there was no previous select command.
+.TP
+.IB h .nextRow()
+Advances the current row, then returns the current row number of the selection results. A return value of 0 indicates there are no more rows; a negative value is returned in case of an error. The initial current row is 0 following a select, so
+.B nextRow
+must be called before any data is read.
+.TP
+.IB h .read( c )
+Returns the data of column
+.I c
+of the current row. If
+.I c
+is out of range, there is no current row, or some other error occurred, the first element of the returned tuple will be negative, and the byte array (sic) will contain an error message. Otherwise, it will be the number of bytes in the field requested. This could be greater than the length of the returned array, if the DB module could not allocate enough memory to contain the entire field. In this case the returned array contains the initial portion of the field.
+.TP
+.IB h .write( p , data )
+.B Write
+sends data for a binary field to the server,
+in anticipation of a subsequent SQL `update request with placeholders'.
+.I Data
+is an array of bytes containing the data for placeholder
+.I p
+(counting from 1).
+All binary fields should be set by
+.B write
+before the SQL request is sent to the server
+with
+.BR SQL .
+It returns the number of bytes saved for the field, or -1 on failure.
+.TP
+.IB h .columnTitle( n )
+.B ColumnTitle
+returns the title of column
+.IR n .
+It returns nil if
+.I n
+is out of range.
+.TP
+.IB h .errmsg()
+Returns the error message associated with the failure of a previous
+.BR columns ,
+.BR nextRow ,
+.B columnTitle
+or
+.BR read .
+.SH SOURCE
+.B /appl/lib/db.b
+.SH "SEE ALSO"
+.IR svc (8)
--- /dev/null
+++ b/man/7/dbsrv
@@ -1,0 +1,67 @@
+.TH DBSRV 7 emu
+.SH NAME
+dbsrv \- ODBC database server
+.SH SYNOPSIS
+.B lib/dbsrv
+.I alg
+\&...
+.SH DESCRIPTION
+.I Dbsrv
+is normally configured to be run by
+.IR svc (8)
+on a machine that acts as database server,
+for access through the module
+.IR db (7).
+.PP
+The incoming call is authenticated by
+.IR security-auth (2).
+After successful authorisation, the client can request that a digest and/or encryption
+algorithm be applied to protect the data exchanged with the server.
+Each
+.I alg
+names a digest or encryption algorithm that the server will allow
+the client to use,
+in any form accepted by
+.IR ssl (3);
+the special name
+.B none
+means that
+.I ssl
+need not be used.
+.PP
+The implementation of
+.I dbsrv
+relies on a program external to Inferno,
+.BR infdb ,
+which translates between the
+.IR db (7)
+internal format for requests, and data base access calls on the host.
+.I Dbsrv
+invokes
+.B infdb
+using the
+.IR cmd (3)
+device.
+Currently
+.B infdb
+is implemented only under Windows/NT, giving
+access via ODBC to a Microsoft Access data base.
+.SH FILES
+.TF /usr/user/keyring/default
+.TP
+.B /cmd
+host command execution
+.TP
+.B /Nt/386/bin/infdb.exe
+Windows executable for ODBC interface
+.TP
+.BI /usr/ user /keyring/default
+server's authentication data when
+.IR svc (8)
+run as
+.I user
+.SH SOURCE
+.B /appl/lib/dbsrv.b
+.SH SEE ALSO
+.IR db (7),
+.IR svc (8)
--- /dev/null
+++ b/man/8/0intro
@@ -1,0 +1,13 @@
+.TH INTRO 8
+.SH NAME
+intro \- introduction to system configuration and administration, and system utilities
+.SH DESCRIPTION
+This section of the manual describes commands that provide
+system configuration and support system administration.
+There are commands to start and shut down native and hosted environments,
+administer authentication,
+build file system images,
+initialise disks and flash memory, and
+configure devices.
+It also documents the commands and interfaces to service
+programs, including those providing services on the network.
--- /dev/null
+++ b/man/8/INDEX
@@ -1,0 +1,60 @@
+intro 0intro
+ai2key ai2key
+dsagen ai2key
+rsagen ai2key
+applylog applylog
+updatelog applylog
+bootpd bootpd
+tftpd bootpd
+changelogin changelogin
+convpasswd changelogin
+collabsrv collabsrv
+create create
+info create
+inst create
+createsignerkey createsignerkey
+cs cs
+csquery cs
+dhcp dhcp
+dns dns
+dnsquery dns
+fpgaload fpgaload
+ftl ftl
+getauthinfo getauthinfo
+echo httpd
+httpd httpd
+stats httpd
+emuinit init
+init init
+osinit init
+kfscmd kfscmd
+logind logind
+mangaload mangaload
+manufacture manufacture
+mkext mkfs
+mkfs mkfs
+ping ping
+plumber plumber
+fdisk prep
+format prep
+mbr prep
+prep prep
+rdbgsrv rdbgsrv
+register register
+rip rip
+rstyxd rstyxd
+shutdown shutdown
+countersigner signer
+signer signer
+verify signer
+sntp sntp
+styxchat styxchat
+styxmon styxmon
+auth svc
+net svc
+registry svc
+rstyx svc
+styx svc
+svc svc
+touchcal touchcal
+virgild virgild
--- /dev/null
+++ b/man/8/ai2key
@@ -1,0 +1,262 @@
+.TH AI2KEY 8
+.SH NAME
+ai2key, dsagen, rsagen \- generate and reformat public keys
+.SH SYNOPSIS
+.B ai2key
+[
+.BI -t " tag"
+]
+.I keyfile
+\&...
+.PP
+.B dsagen
+[
+.BI -t " tag"
+]
+.PP
+.B rsagen
+[
+.BI -b " nbits"
+] [
+.BI -t " tag"
+]
+.SH DESCRIPTION
+.IR Factotum (4)
+represents public keys as lists of attribute-value pairs, each key on a single line prefixed with the string
+.BR key .
+.PP
+.I Ai2key
+converts the original Inferno representation of authentication data,
+in the format defined for
+.B authinfo
+by
+.IR keytext (6),
+to an attribute-value format accepted by
+.IR factotum (4)
+for the
+.B infauth
+authentication protocol.
+For each
+.I keyfile
+it writes a single line on standard output, containing
+the following fields:
+.IP
+.EX
+.fi
+.ti -3n
+key proto=infauth
+[
+.I tag
+]
+.BI "sigalg=" pkalg - hashalg
+[
+.BI dom= host
+]
+.BI server= host
+[
+.BI service= svc
+]
+.BI "user=" name
+.BI "signer=" name
+.BI "pk=" pk
+.BI "!sk=" sk
+.BI spk= pk
+.BI cert= cert
+.BI dh-alpha= hex
+.BI dh-p= hex
+.EE
+.PP
+where
+.RS
+.TP 15n
+.I pkalg
+is
+.BR dsa ,
+.B elgamal
+or
+.BR rsa
+.PD 0
+.ns
+.TP
+.I hashalg
+is
+.B md5
+or
+.BR sha1
+.br
+.ns
+.TP
+.B user
+is the user name associated with the key, as vouched for by the supporting
+certificate
+.BR cert
+.br
+.ns
+.TP
+.B signer
+is the user name associated with the key that signed the certificate
+.br
+.ns
+.TP
+.B pk
+is the user's public key
+.br
+.ns
+.TP
+.B !sk
+is the user's private (secret) key
+.br
+.ns
+.TP
+.B spk
+is the signer's public key
+.br
+.ns
+.TP
+.B cert
+is the
+.I certificate
+.br
+.ns
+.TP
+.BR dh-alpha ,\  dh-p
+are the Diffie-Hellman parameters shared by the user and file servers.
+.RE
+.PD
+.PP
+The key is tagged by one or more of
+.BR dom ,
+.B server
+and
+.BR service ,
+derived from the file name
+.IR keyfile .
+The server is
+.RB ` * '
+if
+.I keyfile
+is
+.BR default .
+Otherwise
+.I keyfile
+has the form
+.IP
+[
+.IB net !
+]
+.I host
+[
+.BI ! srv
+]
+.PP
+and
+.B server
+and
+.B service
+are set accordingly;
+.B dom
+is set if
+.I host
+looks like a domain name.
+Key and certificate values have the form defined in
+.IR keytext (6);
+.I hex
+is a large number in hexadecimal.
+.PP
+.I Dsagen
+prints a randomly-generated DSA private key using the NIST-recommended algorithm.
+If
+.I tag
+text is specified, it is printed after the
+.B proto
+attribute-value pair.
+Typically,
+.I tag
+is a sequence of attribute-value comments describing the key.
+A DSA key has the following attributes
+.RS
+.TP 8n
+.B p
+prime public modulus
+.PD 0
+.TP
+.B q
+prime group order; divides
+.BR p -1
+.TP
+.B alpha
+group generator
+.TP
+.B key
+.BR alpha ^ !secret
+mod
+.B p
+.TP
+.B !secret
+the secret exponent
+.RE
+.PD
+.PP
+.I Rsagen
+prints a randomly generated RSA private key
+whose
+.B n
+has exactly
+.I nbits
+(default 1024)
+significant bits.
+The key has the following attributes:
+.RS
+.TP
+.B size
+the number of significant bits in
+.B n
+.PD 0
+.TP
+.B ek
+the encryption exponent
+.TP
+.B n
+the product of
+.B !p
+and
+.B !q
+.TP
+.B !dk
+the decryption exponent
+.TP
+.B !p
+a large prime
+.TP
+.B !q
+another large prime
+.TP
+.B "!kp\fR, \fL!kq\fR, \fL!c2
+parameters derived from the other attributes, cached to speed decryption
+.RE
+.PD
+.PP
+All the numbers in
+.I dsagen
+and
+.I rsagen
+output are in hexadecimal except RSA's
+.BR size ,
+which is decimal.
+A public key omits the attributes beginning with
+.L ! .
+A key may have other attributes as well, for example a
+.B service
+attribute identifying how this key is typically used,
+but to these utilities such attributes are merely comments.
+They can be provided in a
+.I tag
+argument.
+.SH SOURCE
+.B /appl/cmd/auth/ai2key.b
+.br
+.B /appl/cmd/auth/dsagen.b
+.br
+.B /appl/cmd/auth/rsagen.b
+.SH "SEE ALSO"
+.IR factotum (4)
--- /dev/null
+++ b/man/8/applylog
@@ -1,0 +1,237 @@
+.TH APPLYLOG 8
+.SH NAME
+applylog, updatelog \- log-based updates
+.SH SYNOPSIS
+.B install/applylog
+[
+.B -c
+] [
+.B -e
+] [
+.B -n
+] [
+.B -s
+] [
+.B -u
+] [
+.B -g
+] [
+.B -v
+] [
+.BI -T " timefile"
+]
+.I clientlog
+.I clientroot
+.I serverroot
+[
+.I path
+\&...
+]
+.PP
+.B install/updatelog
+[
+.BI -p " proto"
+] [
+.BI -r " root"
+] [
+.BI -t " now gen"
+] [
+.B -c
+] [
+.BI -x " path"
+]
+.I log
+[
+.I path
+\&...
+]
+.SH DESCRIPTION
+These two commands allow distribution of updates (eg, to the Inferno tree)
+based on a log of changes since a previous update.
+Notionally, one
+.I server
+system is the primary for a set of files, and one or more
+.I client
+systems maintain replicas of that set,
+although in some applications server and client might be the same machine.
+.PP
+.I Applylog
+is run on a client, to update the file tree rooted at
+.IR clientroot .
+The server's version of the tree is rooted at
+.I serverroot
+on the client,
+typically by being mounted there (see
+.IR bind (1)).
+.I Applylog
+takes the current state of the replica from the entries in
+.IR clientlog ,
+and applies a set of changes represented by log entries read from its standard input.
+Those entries are provided by the server.
+Each change is examined to see whether the file to which it applies is in the expected state.
+If so, the change is applied without comment; otherwise, there is a conflict caused by
+a local change to the replica tree independently from the primary.
+By default,
+.I applylog
+diagnoses the conflict and does not apply the change.
+It accepts the following options:
+.TP
+.B -c
+Resolve inconsistencies in favour of the client: leave the replica as is.
+.TP
+.B -e
+Exit with an error status on any error, including inconsistency between client and server.
+.TP
+.B -n
+Print on standard output a list of changes that would be made, and list any conflicts,
+but do not change the tree or update the log.
+.TP
+.B -s
+Resolve inconsistencies in favour of the server: make the replica match the server's state.
+.TP
+.B -u
+Make file ownership in the replica match that on the server.
+.TP
+.B -g
+Make group ownership in the replica match that on the server.
+.TP
+.B -v
+Print a summary of each log entry as it is examined.
+.TP
+.BI -T " timefile"
+Read a time and sequence number from
+.I timefile
+and apply only log entries with stamps greater than that.
+On successful completion, if the
+.B -n
+option is not given, update the
+.I timefile
+with the stamp of the last log entry processed successfully.
+.PP
+The scope of an update in a tree can be restricted to a particular set of
+.I paths
+listed on the command line.
+They should all be relative path names.
+.PP
+.I Updatelog
+is run on a server to produce a sequence of log entries representing changes
+to the primary tree since a previous log was produced.
+It can also be run on a client to see how its replica state differs from that recorded in a log.
+It accepts the following options:
+.TP
+.BI -p " proto"
+Use
+.I proto
+as the prototype for the file system, as described by
+.IR proto (6)
+(default:
+.BR /lib/proto/all ).
+.TP
+.BI -r " root"
+The replica is rooted at
+.I root
+(default:
+the current directory,
+.BR . ).
+.TP
+.BI -t " now gen"
+Make log entries use time
+.I now
+and initial sequence number
+.IR gen .
+The defaults are the current time and 0.
+.TP
+.B -c
+Produce output only for content and metadata changes, not additions or deletions.
+.TP
+.BI -x " path"
+Exclude
+.I path
+and its subtrees from consideration.
+.PP
+By default,
+.I updatelog
+produces log entries describing changes, additions and deletions to all files in
+.I root
+but the scope can be limited by giving a different
+.IR proto ,
+explicitly listing trees to consider as
+.I paths
+on the command line, and
+by giving one or more
+.B -x
+options to exclude particular paths,
+in any desired combination.
+.SS Log file format
+.PP
+A log file is a text file with one line representing each change to the tree.
+Each line has the form:
+.IP
+.I "time gen verb path serverpath mode uid gid mtime length"
+[
+.I sum
+\&...
+]
+.PP
+where:
+.RS
+.TP
+.I "time, gen"
+are decimal numbers that order the sequence of requests:
+.I time
+is typically the time in seconds of the epoch at which the entry was made;
+.I gen
+is a monotonically increasing sequence number
+.PD
+.TP
+.I verb
+is an action:
+.RS
+.PD0
+.TP
+.B a
+add file
+.I path
+.TP
+.B c
+change the contents of file
+.I path
+.TP
+.B d
+delete
+.I path
+.TP
+.B m
+change the metadata (permissions, ownership) for
+.I path
+.RE
+.PD
+.TP
+.I path
+the name of the file on the client
+.TP
+.I serverpath
+the name of the file on the server with the contents for
+.IR path ,
+or simply
+.L -
+when the server and client file names are the same
+.TP
+.I "mode, uid, gid, length, mtime"
+the resulting metadata (except for
+.B d
+where the metadata is that for the file to be deleted)
+.TP
+.I sum
+is the MD5 checksum of the file's contents
+.RE
+.SH SOURCE
+.B /appl/cmd/install/applylog.b
+.br
+.B /appl/cmd/install/logs.b
+.br
+.B /appl/cmd/install/updatelog.b
+.SH SEE ALSO
+.IR fs (1),
+.IR kfs (4),
+.IR proto (6)
--- /dev/null
+++ b/man/8/bootpd
@@ -1,0 +1,163 @@
+.TH BOOTPD 8
+.SH NAME
+bootpd, tftpd \- Internet booting
+.SH SYNOPSIS
+.B ip/bootpd
+[
+.B -dsq
+] [
+.BI -f " dbfile"
+] [
+.BI -x " network"
+]
+.PP
+.B ip/tftpd
+.RB [ -dr ]
+[
+.BI -p " port"
+] [
+.BI -h " homedir"
+] [
+.BI -x " network"
+]
+.SH DESCRIPTION
+.I Bootpd
+listens for Internet BOOTP requests and broadcasts a suitable reply
+to each request that matches an entry
+in the network database
+.I dbfile
+(default:
+.BR /lib/ndb/local ).
+The BOOTP protocol is typically used by a remote system as it boots, to obtain its Internet address
+and other configuration data such as the addresses of servers
+(see for instance the
+.B bootp
+file in
+.IR ip (3)).
+.PP
+.I Dbfile
+is in
+.IR ndb (6)
+format, as interpreted by
+.IR attrdb (2).
+.I Bootpd
+uses the following attributes:
+.TF ipmask
+.TP
+.B auth
+authentication server name or address
+.TP
+.B bootf
+name of the client's boot file
+.TP
+.B dom
+fully-qualified domain name
+.TP
+.B ether
+hardware (MAC) address; only Ethernet is supported
+.TP
+.B fs
+file server name or address
+.TP
+.B ip
+client's Internet address
+.TP
+.B ipgw
+gateway from client's subnet (IP address)
+.TP
+.B ipmask
+subnet mask
+.TP
+.B ipnet
+network name
+.TP
+.B sys
+system name (client identifier)
+.PD
+.PP
+.I Bootpd
+replies to an incoming request only if its hardware address matches the value of the
+.B ether
+attribute of an entry in
+.I dbfile .
+If found, the reply contains all the other requested data that is contained in the entry;
+if an item is missing, it is sought in the entries for successively higher networks (described by
+.B ipnet
+entries) that contain the requesting system's address.
+The `vendor specific' part of the reply conveys the file server and authentication server addresses
+to Inferno clients.
+Before answering a request,
+.I bootpd
+rereads
+.I dbfile
+if it has changed since last read.
+.PP
+The
+.B -s
+option causes
+.I bootpd
+to sniff the network for BOOTP traffic and print it, but not reply.
+The
+.B -d
+option prints debugging information; giving it twice prints even more.
+The
+.B -x
+option tells
+.I bootpd
+to use a
+.I network
+other than
+.BR /net .
+Currently
+.I bootpd
+prints a message to standard output each time it replies; the
+.B -q
+option keeps it quiet.
+.PP
+.I Tftpd
+is mainly used to send kernels and configuration files to machines booting from the network.
+It listens for incoming TFTP file transfer requests on the given UDP
+.I port
+(default: 69) and responds by sending or receiving a file as requested.
+.I Homedir
+is the current directory for transfers,
+.B /services/tftpd
+by default,
+and requests that use a relative path name refer to files in or below that directory.
+If the
+.B -r
+option is given, absolute path names are also restricted to
+.IR homedir .
+.I Tftpd
+runs as
+.B none
+(the least privileged user) and can send only files with general read permission, or write files
+that are generally writable.
+Normally
+.I tftpd
+uses the network directory
+.BR /net ,
+but another can be specified with the
+.B -x
+option.
+The
+.B -d
+option prints a debugging trace on standard output.
+.SH FILES
+.TF /services/tftpd/xxxxx
+.TP
+.B /lib/ndb/local
+network configuration file
+.TP
+.B /services/tftpd
+default directory for relative pathnames
+.SH SOURCE
+.B /appl/cmd/ip/bootpd.b
+.br
+.B /appl/cmd/ip/tftpd.b
+.SH SEE ALSO
+.IR attrdb (2),
+.IR ip (3),
+.IR ndb (6),
+.IR cs (8),
+.IR dns (8)
--- /dev/null
+++ b/man/8/changelogin
@@ -1,0 +1,113 @@
+.TH CHANGELOGIN 8
+.SH NAME
+changelogin, convpasswd \- create/update the password file
+.SH SYNOPSIS
+.BI auth/changelogin " name"
+.PP
+.B auth/convpasswd
+[
+.B -f
+] [
+.B -v
+] [
+.BI -m " keydir"
+]
+[
+.I pwfile
+]
+.SH DESCRIPTION
+.I Changelogin
+helps to administer a password file
+.B /keydb/keys
+(see
+.IR keys (6)),
+a requirement of `signer' servers
+(see
+.IR logind (8),
+.IR signer (8),
+and
+.B svc/auth
+in
+.IR svc (8)).
+.I Changelogin
+can only be used on the signing host itself,
+after
+.IR keyfs (4)
+has been started (eg, by
+.BR svc/auth )
+to make the entries visible in the name space.
+.PP
+If a password file entry for
+.I name
+exists, it is modified; otherwise, a new entry is created.
+The user is prompted for the following items:
+.TP
+password
+A string at least 8 characters in length. The SHA-1 digest of the entered string is stored in the password file. See
+.IR keyring-sha1 (2).
+By default, the password for an existing entry is unchanged.
+.TP
+expiration date
+An expiration time in the format
+.IR DDMMYYYY .
+The user input is checked for valid day, month, and year; moreover, the new date must be later than the current time.
+By default, the current value, if any, is unchanged for an existing entry and one year from the current time for a new entry.
+.ig
+.TP
+free form info
+Arbitrary administrative information.
+..
+.PP
+Note that the
+password expiration date is also used as the default expiration date
+for any certificate later produced;
+see
+.IR getauthinfo (8).
+.PP
+.I Convpasswd
+converts a Third Edition
+password file
+to the
+.IR keys (6)
+file used by the current Edition of the system.
+It reads password entries from
+.IR pwfile ,
+.B /keydb/password
+by default,
+and writes corresponding entries into
+a name space served by
+.IR keyfs (4),
+mounted at
+.IR keydir ,
+.B /mnt/keys
+by default.
+It copies passwords and expiry times.
+The `free form' administrative data is discarded.
+If
+.I keydir
+already has got an entry for a user, it is left as-is, unless
+the
+.B -f
+option is given to force
+.I convpasswd
+to copy across the entry in
+.I pwfile .
+The
+.B -v
+option causes
+.I convpasswd
+to print each user name after it successfully installs it.
+.SH FILES
+.TF /keydb/keys
+.TP
+.B /keydb/keys
+.SH SOURCE
+.B /appl/cmd/auth/changelogin.b
+.SH "SEE ALSO"
+.IR passwd (1),
+.IR keyfs (4),
+.IR keysrv (4),
+.IR keys (6),
+.IR logind (8),
+.IR signer (8),
+.IR svc (8)
--- /dev/null
+++ b/man/8/collabsrv
@@ -1,0 +1,334 @@
+.TH COLLABSRV 8
+.SH NAME
+collabsrv \- multi-user collaboration
+.SH SYNOPSIS
+.B collab/collabsrv
+[
+.BI "-f " keyfile
+] [
+.BI "-n " netaddress
+] [
+.I dir
+]
+.PP
+.B collab/servers/chatsrv
+.PP
+.B collab/servers/mpx
+.PP
+.B collab/servers/wbsrv
+.SH DESCRIPTION
+.I Collabsrv
+listens on network address
+.I netaddress
+(default:
+.BR tcp!*!9999 )
+for incoming requests to attach to services
+it offers.
+The services are defined by the contents of directory
+.IR dir
+(default:
+.BR /services/collab ).
+.I Collabsrv
+serves an authenticated 9P connection that exports the contents of
+.IB dir /export
+(default:
+.BR /services/collab/export ).
+The exported name space can contain directories from a shared file server,
+but it will also contain a directory
+.B services
+giving access to any collaborative activity services that have been configured.
+.PP
+The
+.B services
+directory contains a single
+.B ctl
+file and a set of subdirectories, numbered
+.BR 0 ,
+.BR 1
+and so on.
+The
+.B ctl
+is used to activate and access services.
+Each service instance is identified by a name;
+clients connect to a given instance by presenting its name.
+Each directory represents one instance of a service.
+Each service instance corresponds to a name space; the clients
+all share that name space.
+The name space is determined by the service.
+.PP
+A client wishing to make use of the services must first
+.IR dial (2)
+to connect to
+.IR collabsrv,
+and authenticate and mount the resulting 9P connection.
+It can do so using the
+.I mount
+command
+(see
+.IR bind (1)),
+or by
+using
+.IR security-auth (2)
+and
+.B Sys->mount
+(see
+.IR sys-bind (2)).
+Normally, this is done by
+.IR collab (1).
+.PP
+A new connection to a particular service is requested by opening the
+.B ctl
+file for reading and writing,
+writing a service request to it, and reading back
+the number of the directory corresponding to the requested service.
+A service request written to
+.B ctl
+is text of the following form:
+.IP
+.DS
+.I "service id"
+.DE
+.PP
+where
+.I service
+is a string specifying the type of service (eg,
+.BR chat )
+and
+.I id
+is a string identifying the instance.
+The server does not interpret
+.IR id ;
+it is up to the clients to agree a naming convention (often
+using the name of a shared file as an
+.IR id ).
+.I Collabsrv
+will connect to instance
+.I id
+of the requested
+.I service
+type
+if one is already running, or start one if necessary;
+the write request returns an error if the service cannot be started.
+Following a successful write to
+.BR ctl ,
+a read will return the number of the service
+directory containing the client's private connection to that service instance.
+Closing the
+.B ctl
+file disconnects from the service.
+.PP
+Available services are defined by a configuration file
+.BR /services/collab/services.cfg ,
+which contains a sequence of configuration
+entries of the following form:
+.IP
+.EX
+.I service
+.br
+\f5	path=\fIdisfile\fP
+.EE
+.PP
+where
+.I service
+is the name presented in a service request, and
+.I disfile
+names a Dis file implementing that service;
+path names are interpreted relative to
+.BR /services/collab ,
+but usually refer to files in
+.BR /dis/collab/servers .
+.PP
+Each
+.I collabsrv
+service is represented by a name space peculiar to that service.
+The interface to the service is therefore implemented by a service-specific 9P server
+.RI ( disfile
+in the configuration file).
+Current services are described below.
+.SS "Chat"
+.I Chatsrv
+provides a simple service for the exchange of text messages.
+It serves a name space containing two files.
+The files together represent a single messaging group:
+.TF users
+.TP
+.B users
+A read-only file that lists the user names of the current members of the group, one per line.
+The version number of the file's Qid
+(see
+.IR sys-stat (2))
+is incremented each time a client arrives or leaves.
+.TP
+.B msgs
+.RS
+A client connects to the messaging group by opening this file.
+A message is sent to the group by writing to the file.
+Each read returns the next unread message, prefixed by the name of the sender,
+or
+.B <you>
+for a message sent by the current client.
+A client sees no messages sent before it connects.
+Messages are delivered in the same order to all clients; clients receive their own messages.
+Two special messages are generated by the server:
+.IP
+.EX
++++ \fIname\fP has arrived
+--- \fIname\fP has left
+.EE
+.PP
+as clients come and go.
+.RE
+.SS "Multiplexor"
+.I Mpx
+offers a general fan-out/fan-in multiplexing service for a tree of processes,
+with one controlling or root process at the root of the tree,
+and a set of client processes at the leaves of the tree.
+It serves three files:
+.TF users
+.TP
+.B root
+An exclusive-use file read and written by the root process
+to communicate with the leaf processes.
+.TP
+.B leaf
+Client processes read and write this file to communicate with the
+root process (each
+.B open
+of
+.B leaf
+is independent).
+It cannot be opened until a process has opened
+.BR root .
+After
+.B root
+has been closed, and any remaining messages on
+.B leaf
+have been read,
+subsequent reads will return zero bytes (end-of-file).
+.TP
+.B users
+A read-only text file that lists the user names of processes that currently have
+.B leaf
+open.
+There is one line per leaf, containing a unique numeric ID for the leaf, a space, and then the users's name.
+.PP
+A message written to
+.B root
+is replicated on all instances of
+.B leaf
+that are currently open.
+A message written to any instance of
+.B leaf
+will be read by the process reading
+.BR root .
+Data written to both
+.B root
+and
+.B leaf
+has a prefix added to identify the sender, causing messages
+to have the following format:
+.IP
+.EX
+.I "seq clientid op name data"
+.EE
+.PP
+where
+.I seq
+is a unique message sequence number;
+.I clientid
+is a unique number identifying the process amongst currently connected clients,
+with 0 identifying the root process;
+.I op
+is a single character giving the message type (see below);
+.I name
+is the sending process's user name; and
+.I data
+is the data written by the process, which can be text or binary (the message header is always text).
+.I Mpx
+also generates messages as root and leaf processes arrive and leave.
+These are identified by
+.IR op .
+The various
+.I op
+values and the direction in which they can occur are listed below:
+.TP
+.B a
+New leaf process has arrived (leaf to root)
+.TP
+.B M
+Message from root process (root to leaf)
+.TP
+.B m
+Message from leaf process (leaf to root)
+.TP
+.B L
+Root process has left (root to leaf)
+.TP
+.B l
+Leaf process has left (leaf to root)
+.PP
+Messages are only ever sent from the root to all leaves, or from a leaf to the root,
+never from leaf to leaf; the root process could of course rebroadcast a message from a leaf.
+The multiplexor service is used to implement
+a real-time poll
+(see
+.B poll
+and
+.B poller
+in
+.IR collab-clients (1)),
+but could be used for several other services, such as auctions.
+.SS "Whiteboard"
+.I Wbsrv
+offers a service for sharing a simple line drawing.
+It serves two files:
+.TP
+.B wb.bit
+A read-only file containing an uncompressed
+.IR image (6)
+with the current state of the drawing.
+.TP
+.B strokes
+This file is read and written to exchange strokes with other clients.
+A stroke has the following representation:
+.RS
+.IP
+.IR "colour width x0 y0 x1 y1 " ...
+.PP
+where all values are space-separated decimal numbers:
+.I colour
+is an index into the
+.IR rgbv (6)
+colour map;
+.I width
+is the width of the line in pixels, and
+the sequence of coordinate pairs defines the connected line segments to draw.
+A stroke is transmitted from one client to all others by writing a stroke description to the file in a single write.
+Each read returns a description of a stroke made by another client.
+.RE
+.PP
+A whiteboard client should read the
+.B wb.bit
+file to obtain its image, then read the
+.B strokes
+file for instructions to keep it up to date.
+.SH FILES
+.TF /services/collab/export/services
+.TP
+.B /services/collab/export/services
+active service directory
+.TP
+.B /services/collab/services.cfg
+maps service names to modules
+.TP
+.B /dis/collab/servers
+service implementation modules
+.SH SOURCE
+.B /appl/collabsrv
+.br
+.B /appl/collab/servers
+.br
+.B /appl/collab/lib
+.SH SEE ALSO
+.IR collab (1),
+.IR collab-clients (1)
--- /dev/null
+++ b/man/8/create
@@ -1,0 +1,204 @@
+.TH CREATE 8
+.SH NAME
+create, inst, info \- archive or update a file system
+.SH SYNOPSIS
+.B install/create
+.RB [ -u ]
+.RB [ -U ]
+.RB [ -v ]
+.RB [ -x ]
+.RB [ -o ]
+.RB [ -p
+.IR proto ]
+.RB [ -r
+.IR root ]
+.RB [ -s
+.IR source ]
+.RB [ -N
+.IR uid ]
+.RB [ -G
+.IR gid ]
+.RB [ -d
+.IR description ]
+.I name
+.PP
+.B install/inst
+.RB [ -c ]
+.RB [ -h ]
+.RB [ -t ]
+.RB [ -u ]
+.RB [ -v ]
+.RB [ -F ]
+.RB [ -r
+.IR root ]
+.I name
+.RI [ prefix
+.IR ... ]
+.PP
+.B install/info
+.RB [ -r ]
+.I package
+.SH DESCRIPTION
+.I Create
+copies files from the file tree
+.I source
+(default
+.BR / )
+to an output file in archive format appropriate for a software distribution. The name of the output
+file is the time that the archive was made. The latter includes wrap headers that describe
+the distribution.
+.I Name
+is typically the name of the product or software package. The
+.B -p
+option specifies the prototype file
+.I proto
+to use to build the archive.
+.PP
+Each line of the
+.I proto
+file specifies a file to copy.
+Indentation is significant,
+with each level of indentation corresponding to a level in the file tree.
+Fields within a line are separated by white space.
+The first field is the last path element in the destination file tree.
+The second field specifies the permissions.
+The third field is the owner of the file,
+and the fourth is the group owning the file.
+The fifth field is the name of the file from which to copy;
+this file is read from the current name space,
+not the source file tree.
+All fields except the first are optional.
+.PP
+Names beginning with a
+.L $
+are expanded as environment variables.
+If the first file specified in a directory is
+.LR * ,
+all of the files in that directory are copied.
+If the first file is
+.LR % ,
+all of the non-directory files in that directory are copied.
+If the first file is
+.LR + ,
+all of the files are copied, and all subdirectories
+are recursively copied.
+.PP
+Files in the source tree that are not specified in the
+.I proto
+file
+are not placed in the archive.
+.PP
+The remaining options to
+.I create
+are:
+.TP 10
+.B -u
+Build an update distribution rather than a base distribution. In this case 
+.I name
+should be the name of the previous archive file built for this product. Only files
+that are out of date with respect to the latter are included in the archive. Files which
+no longer exist will be marked for removal.
+.TP
+.B -U
+Build an update package distribution instead. This is a hybrid of a base
+distribution and an update distribution.
+.TP
+.B -v
+Print out the files as they go into the archive.
+.TP
+.B -x
+Print out the files that would go into the archive but do not actually archive them.
+.TP
+.B -o
+Copy the archive file to the standard output rather than putting it in a date stamped output file.
+.TP
+.B -r root
+Specifies the location of any previous archives for this product.
+.TP
+.B -N uid
+Give all the files in the archive the user id specified.
+.TP
+.B -G gid
+Give all the files in the archive the group id specified,
+.TP
+.B -d description
+Give a description of the distribution. This is placed in the wrap header files.
+.PD
+.PP
+.I Inst
+installs archive files made by
+.I create .
+.I Name
+is the name of the archive file to install. Any further names after this are treated as path
+prefixes and only files in the archive that have one of the given prefixes are actually installed.
+The option to
+.I inst
+are :
+.TP 10
+.B -c
+Carry on regardless when errors occur. The default behaviour is to exit on encountering an error.
+.TP
+.B -h
+Only print the names of the files in the archive.
+.TP
+.B -t
+Give each installed file the same date stamp as indicated by that file's entry in the archive.
+.TP
+.B -u
+Give each installed file the same date stamp, user id and group id as shown in the archive.
+.TP
+.B -v
+Print out the names of directories as they are installed.
+.TP
+.B -F
+Force the installation of the files in the archive even when the corresponding local file
+has apparently been locally updated or already exists.
+.TP
+.B -r root
+Specifies the root of destination tree where the files will be copied to.
+.PD
+.PP
+.I Info
+prints information about either a specific file produced by
+.I create
+or about all files making up a package in the
+.B /wrap
+tree. In particular base packages,
+full updates and partial updates are distinguished. The 
+.B -r
+option
+specifies the the root of the tree to look in. This defaults to
+.BR / .
+.SH EXAMPLES
+.PP
+Make an archive to establish a new base package for an Inferno distribution:
+.IP
+.EX
+install/create -o -N inferno -G inf -d InfernoOS -p PROTO Inferno.1.0 > inferno.arch
+.EE
+.PP
+Here the name of the product is Inferno.1.0.
+.PP
+Install that archive on another machine:
+.IP
+.EX
+install/inst -r / inferno.arch
+.EE
+.PP
+Here the product is placed in / with the user and group ids being set to those of the
+person doing the installation.
+.SH SOURCE
+.B /appl/cmd/install/arch.b
+.br
+.B /appl/cmd/install/create.b
+.br
+.B /appl/cmd/install/info.b
+.br
+.B /appl/cmd/install/inst.b
+.br
+.B /appl/cmd/install/proto.b
+.br
+.B /appl/cmd/install/wrap.b
+.SH "SEE ALSO"
+.IR archfs (4)
+
--- /dev/null
+++ b/man/8/createsignerkey
@@ -1,0 +1,67 @@
+.TH CREATESIGNERKEY 8
+.SH NAME
+createsignerkey \- create signer key on authentication server
+.SH SYNOPSIS
+.B auth/createsignerkey
+[
+.BI -a " alg"
+] [
+.BI -f " keyfile"
+] [
+.BI -e " expiry"
+] [
+.BI -b " bitsize"
+]
+.I name
+.SH DESCRIPTION
+.I Createsignerkey
+creates public and private keys that are used by a server acting as `signer' to generate certificates for users.
+.I Name
+appears as signer in each certificate.
+The
+.I expiry
+date has the form
+.IR ddmmyyyy ,
+is converted to seconds since the epoch
+(see
+.IR daytime (2))
+and stored in the
+.IR keyfile ;
+by default the server's certificate never expires.
+.PP
+The key will be
+.I bitsize
+long (default: 512 bits) with a minimum of 32 bits and a maximum of 4096 bits.
+.I Keyfile
+is the file in which the server stores its keys;
+the default is
+.BR /keydb/signerkey ,
+and many authentication programs such as
+.IR logind (8)
+by default expect to find their server key there.
+Creating a signer's default key afresh typically invalidates all certificates previously issued by that signer,
+because their signatures will not verify.
+The mode of the
+.I keyfile
+should be set to be readable only by the user running
+those programs.
+.PP
+The
+.B -a
+option specifies the signature algorithm.
+Currently
+.I alg
+can be either
+.B elgamal
+or
+.BR rsa .
+RSA keys are now used by default.
+.SH FILES
+.B /keydb/signerkey
+.SH SOURCE
+.B /appl/cmd/auth/createsignerkey.b
+.SH SEE ALSO
+.IR security-auth (2),
+.IR keyring-gensk (2),
+.IR logind (8),
+.IR signer (8)
--- /dev/null
+++ b/man/8/cs
@@ -1,0 +1,248 @@
+.TH CS 8
+.SH NAME
+cs, csquery \- connection server
+.SH SYNOPSYS
+.B ndb/cs
+[
+.BI -f " database"
+] [
+.B -v
+] [
+.BI -x " net"
+]
+.PP
+.B ndb/csquery
+[
+.B -x
+.I net
+] [
+.B -s
+.I server
+] [
+.I address
+\&... ]
+.SH DESCRIPTION
+.I Cs
+spawns a process that
+serves a single file
+.BR /net/cs ,
+in the current name space,
+answering requests by client processes
+to translate symbolic network and service names into
+instructions for connecting to the given service.
+It is normally accessed indirectly by calls to
+.IR dial (2).
+.PP
+The network data is taken from the network database files,
+described in
+.IR ndb (6).
+By default, it is
+.B /lib/ndb/local
+but the
+.B -f
+option can specify a different one.
+.PP
+Each write to
+.B /net/cs
+makes a query, expressed in one of two forms.
+The first form is a network address of the same form
+as the
+.I addr
+parameter to
+.IR dial :
+.IB network ! netaddr ! service
+where
+.I service
+and
+.I network
+are optional for some networks.
+The write returns an error if the address cannot be translated.
+Otherwise, the file offset should be reset to 0 using
+.IR sys-seek (2)
+and each subsequent read will return either
+end-of-file (if there are no further translations),
+or a single line containing a translation of the form:
+.IP
+.BI /net/ proto /clone " address" ! port
+.PP
+The first field is the name of the
+.I clone
+file for a network protocol or interface.
+To make a connection or announce a service, open that file, and write the
+text in the second field
+preceded by
+.B connect
+or
+.B announce
+as required.
+(All this activity is normally encapsulated in a call to
+.IR dial (2).)
+.I Cs
+produces a translation for each network and for each network address on which a symbolic
+.I netaddr
+is found.
+When announcing a service,
+.I netaddr
+can be
+.B *
+to represent any local interface, and the resulting recipes read
+from
+.B /net/cs
+will not include an
+.IB address !
+part.
+.PP
+.I Cs
+interprets a
+.I netaddr
+of the form
+.BI $ server
+specially: it looks for an attribute
+.I server
+in the database in the entry for the current host, then in the entry for
+each network that contains it (if specified), and finally in a site-wide
+entry labelled with the attribute
+.BR infernosite .
+If found, the value of the attribute replaces the
+.I netaddr
+before further translation.
+.PP
+In the second form of query, the text written contains space-separated attribute/value pairs following
+an initial
+.LR ! :
+.IP
+.B !
+.IB attr1 = val1
+[
+.IB attr2 = val2
+\& ...
+]
+.PP
+.I Cs
+looks for an
+.IR ndb (6)
+entry that contains attribute/value pairs matching those in the query.
+Any value but
+.I val1
+may be
+.RB ` * ',
+to signify that the entry must contain the given attribute but with any value.
+As before, the write returns an error if no entry matches.
+Otherwise, each subsequent read returns the whole of the next matching entry, in
+.IR ndb (6)
+form.
+.PP
+The file
+.B /net/cs
+persists until it is removed or unmounted from
+.BR /net ,
+or the
+.I cs
+process is killed
+(see
+.IR kill (1)).
+The
+.B \-v
+option causes
+.I cs
+to print each translation request and results (if any) on standard error.
+The
+.B -x
+option gives an alternative mount point for
+.IR cs ,
+when there is more than one network stack
+(see
+.IR ip (3)).
+It causes it
+.I cs
+to serve
+.IB net /cs
+instead of
+.BR /net/cs .
+.PP
+.I Cs
+is normally started  once,
+after
+.IR dns (8)
+if used, but
+before most other applications including
+the various listeners described in
+.IR svc (8).
+If another instance of
+.IR cs (8)
+is started on the same mount point, the file it serves replaces the
+earlier one if permissions allow.
+(On Plan 9, Plan 9's native connection service will be used by default if Inferno's
+.I cs
+is not started.)
+.PP
+.I Csquery
+queries the given
+.I server
+(default:
+.BR /net/cs )
+for a translation of each
+.I address
+and prints the results, one per line.
+If no
+.I address
+is given,
+.I csquery
+prompts for address(es) to translate which it reads from the standard input,
+printing the results of each translation on the standard output.
+The
+.B -x
+option gives an alternative mount point for
+.IR cs ,
+when there is more than one network stack
+(see
+.IR ip (3)).
+.PP
+.I Cs
+uses
+.IR ndb (6)
+to map protocol and service names to Internet port numbers.
+When running hosted,
+if entries are not in
+.IR ndb (6),
+.I cs
+applies the built-in
+.IR srv (2),
+if available,
+to have the host system try the translation.
+Consequently, entries in
+.IR ndb (6)
+take precedence over the host's system-wide configuration.
+(This is helpful for adding symbolic names for Inferno services
+without requiring administrative privileges on the host system.)
+.SH EXAMPLE
+Check the translation of the symbolic name
+.BR $signer :
+.IP
+.EX
+ndb/csquery
+> net!$signer!inflogin
+/net/tcp/clone 200.1.1.67!6673
+.EE
+.SH FILES
+.TF /lib/ndb/inferno
+.TP
+.B #scs*
+service directory
+.TP
+.B /net/cs
+connection service
+.TP
+.B /net/dns
+domain name service
+.TP
+.B /lib/ndb/local
+map from symbolic service names to servers
+.SH SOURCE
+.B /appl/cmd/ndb/cs.b
+.br
+.B /appl/cmd/ndb/csquery.b
+.SH "SEE ALSO"
+.IR dial (2),
+.IR ndb (6),
+.IR dns (8)
--- /dev/null
+++ b/man/8/dhcp
@@ -1,0 +1,117 @@
+.TH DHCP 8
+.SH NAME
+dhcp \- configure network interface details using DHCP
+.SH SYNOPSIS
+.B ip/dhcp
+[
+.B -bdmpr
+] [
+.BI -g " gateway"
+] [
+.BI -h " hostname"
+] [
+.BI -x " net
+]
+.I ifcdir
+[
+.I localip
+[
+.I localmask
+]]
+.SH DESCRIPTION
+.I Dhcp
+uses the Dynamic Host Configuration Protocol (DHCP) to configure the
+.IR ip (3)
+interface represented by
+.I ifcdir
+(eg,
+.BR /net/ipifc/1 ).
+The interface must have a device already bound to it.
+.I Dhcp
+uses the MAC address of that device in its requests.
+.PP
+.I Dhcp
+broadcasts a DHCP request for an address and various network parameters.
+It takes the first acceptable offer, sets the interface to that address, and writes the address and
+parameter values, in
+.IR ndb (6)
+format, to
+.BR /net/ndb ,
+where
+.IR cs (8),
+.IR dns (8)
+and others will find them.
+If the address is provided with a limited lease,
+.I dhcp
+itself returns, but it leaves
+a process in the background that periodically renews the lease (or requests a new address if the lease is not renewed).
+.PP
+If
+.I localip
+is given,
+.I dhcp
+attempts to reacquire that address.
+If successful, it configures the interface with that address (and mask if supplied),
+maintaining any lease as before.
+If it cannot reacquire the address, it broadcasts a request for a new address, as above.
+.PP
+The options are:
+.TP
+.B -b
+Use plain BOOTP without the DHCP options
+.TP
+.B -d
+Enable debugging output on standard output
+.TP
+.BI -g " gateway"
+Suggest
+.I gateway
+as the default gateway (the server might change it)
+.TP
+.BI -h " hostname"
+Use
+.I hostname
+as the current host's name in DHCP messages
+.TP
+.B -m
+Monitor the DHCP status and print a summary on standard output whenever it changes
+.TP
+.B -n
+Do not configure the interface
+.TP
+.B -p
+Print the resulting configuration on standard output
+.TP
+.B -r
+Retry DHCP periodically until it succeeds
+.TP
+.BI -x " net"
+Use mount point
+.I net
+to access the network,
+and write the results to
+.IB net /ndb
+(default:
+.BR /net )
+.PD
+.SH EXAMPLE
+Allocate a new interface, bind an ether device to it, and configure it with
+.IR dhcp :
+.IP
+.EX
+x=`{cat /net/ipifc/clone}
+echo bind ether /net/ether0 >/net/ipifc/$x/ctl &&
+ip/dhcp /net/ipifc/$x
+.EE
+.SH SOURCE
+.B /appl/cmd/ip/dhcp.b
+.SH SEE ALSO
+.IR ip (3),
+.IR ndb (6),
+.IR cs (8),
+.IR dns (8)
+.SH DIAGNOSTICS
+.I Dhcp
+returns an error status if it receives no acceptable reply, unless the
+.B -r
+option is given to force retries.
--- /dev/null
+++ b/man/8/dns
@@ -1,0 +1,158 @@
+.TH DNS 8
+.SH NAME
+dns, dnsquery \- domain name service
+.SH SYNOPSIS
+.B ndb/dns
+[
+.BI -f " dnsfile"
+] [
+.B -h
+] [
+.B -r
+] [
+.BI -x " net"
+]
+.PP
+.B ndb/dnsquery
+[
+.BI -x " net"
+] [
+.BI -s " server"
+] [
+.I "address ..."
+]
+.SH DESCRIPTION
+.I Dns
+is an Internet Domain Name Service (DNS) resolver.
+By default it serves a file
+.BR /net/dns ,
+that clients such as
+.IR cs (8)
+write and read to retrieve network data associated with domain names and Internet addresses.
+The
+.B -f
+option specifies the network database that contains the local DNS data (default:
+.BR /lib/ndb/local ).
+The
+.B -x
+option specifies an alternative mount point for the network (default:
+.BR /net ).
+When Inferno is running hosted,
+.I dns
+normally uses the host's own DNS resolver first (via
+.IR srv (2)),
+before searching the DNS itself; that way domain names can be
+used in Inferno with minimal configuration.
+The
+.B -h
+option stops
+.I dns
+from using the host data.
+In the absence of local data, by default
+.I dns
+consults the external DNS directly using some bootstrap data, but if
+.B -r
+is specified and local resolvers are given in the configuration file,
+.IR dns (6),
+.I dns
+will query them first for all addresses, before resorting to external DNS servers.
+.PP
+.I Dnsquery
+queries the given
+.I server
+(default:
+.BR /net/dns )
+for a translation of each
+.I address
+and prints the results, one per line.
+If no
+.I address
+is given,
+.I dnsquery
+prompts for something to find in the DNS, one per line on the standard input,
+of the form:
+.IP
+.EX
+.IR "name" " [" attribute "]"
+.EE
+.PP
+where
+.I name
+is the label of something in the DNS, and
+.I attribute
+is one of its attributes from the list below:
+.TF hinfox
+.TP
+.B all
+all data currently known locally for
+.I name
+.TP
+cname
+name for which
+.I name
+is an alias
+.TP
+hinfo
+host and operating system type
+.TP
+.B ip
+for an IP address when
+.I name
+is a domain name
+.TP
+.B mx
+mail exchanger
+.TP
+.B ns
+for a list of name servers
+.TP
+.B ptr
+for the domain name when
+.I name
+is an Internet address
+.TP
+.B soa
+statement-of-authority
+.PD
+.PP
+.I Dnsquery
+queries the
+.I server
+for that name/attribute combination and prints the results, one per line.
+If an
+.I attribute
+is not given,
+.I dnsquery
+uses
+.B ip
+if
+.I name
+looks like a domain name,
+and
+.B ptr
+if it looks like an Internet address.
+.SH FILES
+.TF /lib/ndb/local
+.TP
+.B #sdns*
+service directory
+.TP
+.B /net/dns
+domain name service
+.TP
+.B /lib/ndb/local
+network database
+.TP
+.B /lib/ndb/dns
+DNS bootstrap data
+.SH SOURCE
+.B /appl/cmd/ndb/dns.b
+.br
+.B /appl/cmd/ndb/dnsquery.b
+.SH "SEE ALSO"
+.IR dns (6),
+.IR cs (8)
+.SH BUGS
+.I Dns
+does not yet offer an external DNS server, mainly
+for lack of a suitable database for local zone data.
--- /dev/null
+++ b/man/8/fpgaload
@@ -1,0 +1,24 @@
+.TH FPGALOAD 8
+.SH NAME
+fpgaload \- configure FPGA
+.SH SYNOPSIS
+.B auxi/fpgaload
+[
+.BI -c " clk"
+]
+.I file.rbf
+.SH DESCRIPTION
+.I Fpgaload
+configures the directly-attached Altera Flex6000 FPGA on the Bright Star Engineering ip-Engine.
+It enables the FPGA and output of the external system clocks, then loads the FPGA with the contents of
+.IR file.rbf
+which should be in the `raw binary format' produced for example by the Altera tools.
+After successful configuration, the BCLK is set to
+.I clk
+MHz;
+.I clk
+must be a divisor of the ip-Engine's system clock (currently 48 MHz).
+.SH SOURCE
+.B /appl/cmd/auxi/fpgaload.b
+.SH SEE ALSO
+.IR fpga (3)
--- /dev/null
+++ b/man/8/ftl
@@ -1,0 +1,63 @@
+.TH FTL 8
+.SH NAME
+ftl \- Flash Translation Layer formatter
+.SH SYNOPSIS
+.B disk/ftl
+.I flashsize
+.I secsize
+.I kfsfile
+.I output
+.SH DESCRIPTION
+.I Ftl
+reads a file system image in
+.IR kfs (3)
+format from
+.I kfsfile
+and adds the data structures needed to make it a valid image for
+the Flash Translation Layer driver
+.IR ftl (3).
+The result is written to the
+.I output
+file, which can be copied to initialise the
+flash memory of a suitable device
+(see
+.IR flash (3)).
+.PP
+The other arguments describe the characteristics of the flash memory:
+.TF \fIflashsize\fP
+.PD
+.TP
+.I flashsize
+The size in bytes of the flash memory to which
+.I output
+will be copied; exactly
+.I flashsize
+bytes will be written to
+.IR output .
+.TP
+.I secsize
+The effective erase unit (sector) size in bytes of the flash memory, as seen
+by the processor, having
+allowed for bus width.
+For example, a bank of flash
+formed from byte-wide flash chips, each with 16kbyte sectors,
+wired across a 4 byte bus, might have an effective erase unit size of
+64kbytes.
+.PP
+The
+.I kfsfile
+must not be larger than the size (length) of the
+.B ftldata
+file provided by
+.IR ftl (3)
+for the target flash device or partition.
+(That size is invariably less than the size of the raw flash,
+owing to the overhead of FTL data structures
+and a reserve pool of 5% to reduce the number of erase cycles; see
+.IR ftl (3)).
+.SH SOURCE
+.B /appl/cmd/disk/ftl.b
+.SH SEE ALSO
+.IR flash (3),
+.IR ftl (3),
+.IR kfs (3)
--- /dev/null
+++ b/man/8/getauthinfo
@@ -1,0 +1,127 @@
+.TH GETAUTHINFO 8
+.SH NAME
+getauthinfo \- obtain a certificate for authentication
+.SH SYNOPSIS
+.BI getauthinfo " keyname"
+.PP
+.B wm/getauthinfo
+.SH DESCRIPTION
+.I Getauthinfo
+makes contact with
+.IR logind (8)
+on a `signer', or certifying authority, with which the user
+has previously been registered using
+.IR changelogin (8),
+to obtain a certificate that
+can later be presented to other Inferno services to authenticate the user.
+If
+.I keyname
+starts with a `/', the certificate is stored there; otherwise, it is stored in the file
+.BI /usr/ user /keyring/ keyname,
+where
+.I user
+is the name in
+.B /dev/user
+(see
+.IR cons (3)).
+The directory
+.BI /usr/ user /keyring
+must exist.
+.PP
+The user is prompted for the following:
+.TP
+signer
+The name of the signing server, for example
+.BR signer.froop.com .
+The default is the default signer for the site:
+the value of
+.B SIGNER
+in the local network configuration database
+(see
+.IR ndb (6)).
+.TP
+remote user name
+The name of the user for whom a certificate is to be obtained. The default is the current user name in
+.BR /dev/user .
+.TP
+password
+The user's password. The password entered on the client must match the password
+previously stored on the server using
+.IR changelogin (8),
+or a certificate will be refused.
+.TP
+save in file?
+The default is `no'. If the user responds `yes', the certificate is written directly to the file.
+Otherwise,
+.I getauthinfo
+becomes a file server, serving
+a secure temporary file bound over
+the file name above (because that is where applications look for it).
+The temporary will disappear if the name is unmounted, or Inferno is rebooted.
+.PP
+Note that the certificate will expire at or before expiry of the password entry
+on the signer.
+.PP
+The signer needs its own key to endorse the certificates that it gives to clients.
+If a user requests a certificate with
+.IR getauthinfo (8)
+before the signer's key is created on the signer (eg,
+using
+.IR createsignerkey (8)),
+then the request will be rejected with a suitable diagnostic
+by
+.IR logind (8).
+.SS "File servers"
+.PP
+Machines that will be file servers must obtain a certificate and save the certificate in a key file named
+.BR default ,
+thus:
+.IP
+.B "getauthinfo default"
+.PP
+The user invoking
+.I getauthinfo
+must be the same user who later runs
+.IR svc (8)
+to start the machine's services.
+.SS "File server clients"
+Machines that wish to be authenticated clients of file servers must obtain a certificate and store the certificate in a file named
+.IB net ! machine.
+The file name must match exactly the
+server address given to
+.I mount
+(see
+.IR bind (1)).
+To set the key, use
+.IP
+.BI getauthinfo " net" ! host
+.SS Window system interface
+.I Getauthinfo
+has a visual counterpart
+.B wm/getauthinfo
+for use under
+.IR wm (1).
+It takes no arguments.
+It displays a window prompting for all the information it needs,
+and offering apparently sensible defaults.
+Apart from the different interface, its function is otherwise
+the same as the command line version.
+.SH FILES
+.TF /usr/username/keyring/net!machine
+.TP
+.BI /usr/ user /keyring/ net ! machine
+where a certificate is stored on a client machine
+.TP
+.BI /usr/ user /keyring/default
+where a certificate is stored on a file server
+.TP
+.B /lib/ndb/local
+contains the default host name of the signer
+.SH SOURCE
+.B /appl/cmd/getauthinfo.b
+.br
+.B /appl/wm/getauthinfo.b
+.SH "SEE ALSO"
+.IR bind (1),
+.IR changelogin (8),
+.IR createsignerkey (8)
--- /dev/null
+++ b/man/8/httpd
@@ -1,0 +1,115 @@
+.TH HTTPD 8
+.SH NAME
+httpd, echo, stats \- HTTP server
+.SH SYNOPSIS
+.B svc/httpd/httpd
+[
+.BI -a " addr"
+] [
+.BI -c " cachesize"
+] [
+.B -D
+]
+.PP
+.BI svc/httpd/echo " meth vers uri search"
+.PP
+.BI svc/httpd/stats " meth vers uri search"
+.SH DESCRIPTION
+.I Httpd
+is a simple HTTP daemon, serving version 1.0 of the HTTP protocol.
+It listens for incoming calls on a given
+.I address
+(default:
+.BR tcp!*!80 ).
+It serves content rooted at
+.L /services/httpd/root
+in its name space.
+.PP
+The
+.I httpd
+program supports only the
+.L GET
+and
+.L HEAD
+methods of the HTTP protocol. The
+.L Content-type
+(default
+.LR application/octet-stream )
+and
+.L Content-encoding
+(default
+.LR binary )
+of a file are determined by looking for suffixes of the file name in
+.BR /services/http/http.suff .
+.PP
+If the requested URI begins with
+.BR /magic/ ,
+.I httpd
+loads the module associated with the remaining part of the URI.
+Take care to configure the name space sensibly.
+Simple servers
+.I echo
+and
+.I stats
+are provided (see below).
+.PP
+.I Httpd
+has the following options:
+.TP
+.BI -a " address"
+Listen for calls on the given
+.IR address ,
+expressed using the syntax of
+.IR dial (2).
+.TP
+.BI -c " cachesize"
+Set the size of the daemon's cache to
+.I cachesize
+kilobytes. The default is a five megabyte cache.
+.TP
+.B -D
+Debugging information is written to the file
+.BR /services/httpd/httpd.debug .
+.PP
+.I Echo
+is a trivial server that just returns the method, URI, any search, and the headers sent by the client.
+.PP
+.I Stats
+is an equally simple server that queries the cache and returns information to the user about pages stored in the cache.
+.PP
+More complex services can be written to
+.IR httpd 's
+private interface.
+The file
+.B httpd.m
+(in
+.BR /appl/svc/httpd )
+defines constants and adts used by
+.IR httpd .
+The file
+.B cgi.m
+defines the
+module
+.L Cgi
+which is the interface for programs called using the URI
+.BR /magic/ .
+.SH FILES
+.TF "/services/httpd/httpd.rewrite  "
+.TP
+.B /services/httpd/root
+Root of the served web content.
+.TP
+.B /services/httpd/httpd.debug
+Logfile for debugging information.
+.TP
+.B /services/httpd/httpd.log
+.I httpd
+logfile.
+.TP
+.B /services/httpd/httpd.rewrite
+File to redirect specific URI requests.
+.TP
+.B /services/httpd/httpd.suff
+File of recognizable suffixes and their content type.
+.SH SOURCE
+.B /appl/svc/httpd
--- /dev/null
+++ b/man/8/init
@@ -1,0 +1,99 @@
+.TH INIT 8
+.SH NAME
+init: emuinit, osinit \- Inferno initialisation
+.SH SYNOPSIS
+.EX
+Init: module
+{
+	init: fn();
+};
+.EE
+.PP
+.B /dis/emuinit.dis
+.PP
+.B #/./osinit.dis
+.SH DESCRIPTION
+Both
+.IR emu (1)
+and the native kernels run a Dis program to initialise the system.
+.PP
+.I Emuinit
+is the default initialisation
+program for
+.IR emu (1).
+.I Emu
+sets the environment variable
+(see
+.IR env (3))
+.B /env/emuargs
+to the command line originally given to
+.IR emu ,
+which has the following form:
+.IP
+.B emu
+.RB [ \-d ]
+[
+.I command
+.RI [ " arg ..." ]
+]
+.PP
+.I Emuinit
+uses the value of
+.B emuargs
+to decide which command to start and its arguments.
+The default
+.I command
+is
+.BR /dis/sh.dis ,
+unless the
+.B \-d
+option is given, in which case
+.B /dis/lib/srv.dis
+is used by default instead, to cause
+.I emu
+to run on the host system as a server (`daemon' mode).
+.PP
+.I Osinit
+is built-in to the
+.IR root (3)
+of native kernels.
+Although the kernel uses the fixed name
+.B #/./osinit.dis
+the contents are taken from one of the files in
+.B /os/init
+selected by the
+.B init
+section of the kernel configuration file.
+.IR Osinit 's
+action is platform-specific in detail, but might include:
+building an initial
+.B /dev
+by mounting device drivers;
+binding the physical network driver (eg,
+.IR ether (3))
+into
+.B /net
+and initialising
+.IR ip (3),
+usually setting addresses and routes using
+.BR bootp ;
+attaching to a remote file system;
+setting up flash translation using
+.IR ftl (3);
+starting
+.I dossrv
+or
+.I 9660srv
+(see
+.IR dossrv (4)),
+or
+.IR kfs (3)
+to serve local files from disk or flash memory.
+.SH FILES
+.B /env/emuargs
+.SH SOURCE
+.B /appl/cmd/emuinit.b
+.br
+.B /os/init/*.b
+.SH SEE ALSO
+.IR emu (1)
--- /dev/null
+++ b/man/8/kfscmd
@@ -1,0 +1,142 @@
+.TH KFSCMD 8
+.SH NAME
+kfscmd \- kfs administration
+.SH SYNOPSIS
+.B disk/kfscmd
+.RB [ -n
+.IR name ]
+.IR cmd " ..."
+.SH DESCRIPTION
+.I Kfscmd
+issues commands to a
+.IR kfs (4)
+server that was started with the
+.B -n
+option to create a control file.
+.IR Kfscmd 's
+own
+.B -n
+option names the file system to which the
+.I cmd
+applies; it is
+.B main
+by default.
+.PP
+The known commands are described below.
+Note that some commands are multiple words (eg,
+.B check
+and its flags) and
+should be quoted to appear as a single argument to
+.IR sh (1).
+.TP \w'\fLallowoff\ \fIn'u
+.B allow
+Turn permission checking off (to simplify administration).
+Equivalent to the
+.B -P
+and
+.B -W
+options to
+.IR kfs (4).
+.TP
+.B allowoff
+.PD 0
+.TP
+.B disallow
+Turn permission checking on (again).
+.PD
+.ig
+.TP
+.B halt
+write all changed blocks and stop the file system.
+.TP
+.B help
+print the list of commands.
+.TP
+.BI "rename " "file name"
+Change the name of
+.I file
+to
+.IR name .
+.TP
+.BI "newuser " user
+Add
+.I user
+to
+.B /adm/users
+and make the standard directories needed for booting.
+.TP
+.BI "remove " file
+Remove
+.I file
+and place its blocks on the free list.
+.TP
+.BI "clri " file
+Remove 
+.I file
+but do not place the blocks on the free list.
+This command can be used to remove files that have duplicated blocks.
+The non-duplicate blocks can be retrieved by checking the file system
+with option
+.B f
+(see below).
+.TP
+.BI create \ file\ owner\ group\ mode\  [adl]
+Create the file.  Owner and group are users in
+.B /adm/users
+and mode is an octal number.
+If present,
+.L a
+creates an append only file,
+.L d
+creates a directory, and
+.L l
+creates a file that is exclusive-use.
+..
+.TP
+.B sync
+write to disk all of the dirty blocks in the memory cache.
+.TP
+.B users
+Reinitialise authentication information by reading
+.BR /adm/users .
+.TP
+.B check [cdfpPqrtw]
+Check the file system and print summary information.
+The options are
+.PD 0
+.RS
+.TP
+.B c
+fix bad tags and clear the contents of the block.
+.TP
+.B d
+delete redundant references to a block.
+.TP
+.B f
+rebuild the list of free blocks.
+.TP
+.B p
+print the names of directories as they are checked.
+.TP
+.B P
+print the names of all files as they are checked.
+.TP
+.B q
+quiet mode: report errors, but suppress summary information.
+.TP
+.B r
+read all of the data blocks and check the tags.
+.TP
+.B t
+fix bad tags.
+.TP
+.B w
+write all of the blocks that are touched.
+.RE
+.PD
+.SH SOURCE
+.B /appl/cmd/disk/kfscmd.b
+.SH "SEE ALSO"
+.IR sd (3),
+.IR kfs (4),
+.IR mkfs (8)
--- /dev/null
+++ b/man/8/logind
@@ -1,0 +1,52 @@
+.TH LOGIND 8
+.SH NAME
+logind \- login daemon
+.SH SYNOPSIS
+.B auth/logind
+.SH DESCRIPTION
+.I Logind
+is normally started by
+.IR svc (8)
+to service requests on the
+.B inflogin
+TCP/IP port,
+to provide a `signing' service (identity authentication) for a network.
+Ultimately, the client receives a certificate that can be used to establish its identity
+with any host that is willing to honour certificates from the certificate's signer.
+.PP
+The signer constructs the certificate from the contents
+of the signer's key file
+.BR /keydb/signerkey ,
+typically created by
+.IR createsignerkey (8),
+and
+the ID string and password supplied by the client (which are used to access
+.IR keyfs (4)
+to check identity).
+.PP
+The protocol involves an exchange of information between the client and server,
+as summarised in
+.IR login (6).
+The client side of this exchange can be managed by the
+.B Login
+module; see
+.IR security-login (2).
+.PP
+Client and server communicate over the Secure Socket Layer device
+.IR ssl (3).
+.SH FILES
+.TF /keydb/signerkey
+.TP
+.B /keydb/keys
+.TP
+.B /keydb/signerkey
+.SH SOURCE
+.B /appl/cmd/auth/logind.b
+.SH "SEE ALSO"
+.IR security-login (2),
+.IR ssl (3),
+.IR keyfs (4),
+.IR keys (6),
+.IR changelogin (8),
+.IR createsignerkey (8),
+.IR svc (8)
--- /dev/null
+++ b/man/8/mangaload
@@ -1,0 +1,49 @@
+.TH MANGALOAD 8
+.SH NAME
+mangaload \- send new kernel to MANGA bootstrap
+.SH SYNOPSIS
+.B auxi/mangaload
+[
+.B -48dr
+]
+.I host
+.I image
+.SH DESCRIPTION
+.I Mangaload
+uses an ICMP protocol unique to the Peplink MANGA™ firewall to send a new kernel
+.I image
+to the MANGA bootstrap monitor to burn into flash, replacing
+the existing kernel image.
+To start the loading process, connect to the device's console on its serial port
+(38400 baud, 8 bits, no parity, 1 stop bit, no flow control),
+power the device off then on again, and when the
+.B MANGA
+prompt appears, quickly type the letter
+.BR f .
+Then run
+.I mangaload
+to load the kernel; the device should give status updates as the kernel loads,
+and as it burns the image into flash.
+When it has finished, power the device off and on again to start the new kernel.
+.PP
+By default,
+.I mangaload
+assumes a 4 Mbyte flash on the device; the
+.B -8
+option sets it to 8 Mbytes.
+The
+.B -r
+option causes the
+.I image
+to be loaded into another, larger flash region, used by Linux for its initial root,
+which can be used by Inferno for general storage.
+The
+.B -d
+option prints a trace of the protocol, for debugging.
+.SH SOURCE
+.B /appl/cmd/auxi/mangaload.b
+.br
+.B /os/manga
+.SH SEE ALSO
+.IR bootpd (8),
+.IR tftpd (8)
--- /dev/null
+++ b/man/8/manufacture
@@ -1,0 +1,31 @@
+.TH MANUFACTURE 8 mux
+.SH NAME
+manufacture \- command to emulate set-top-box-id in ROM
+.SH SYNOPSIS
+.BI manufacture box-id
+.SH DESCRIPTION
+.I Manufacture
+initialises the file
+.B /nvfs/ID
+with the string
+.IR box-id .
+The file emulates the serial number that the manufacturer of a real set top box would
+normally burn into ROM
+(see also
+.B rtcid
+in
+.IR rtc (3)).
+The
+.I box-id
+is used by
+.IR register (8).
+.SH FILES
+.TF /nvfs/ID
+.TP
+.B /nvfs/ID
+.TP
+.B /nvfs/default
+.SH SOURCE
+.B /appl/cmd/manufacture.b
+.SH "SEE ALSO"
+.IR register (8)
--- /dev/null
+++ b/man/8/mkfs
@@ -1,0 +1,223 @@
+.TH MKFS 8
+.SH NAME
+mkfs, mkext \- archive or update a file system
+.SH SYNOPSIS
+.B disk/mkfs
+.RB [ -aprvxS ]
+.RB [ -d
+.IR dest ]
+.RB [ -n
+.IR name ]
+.RB [ -s
+.IR source ]
+.RB [ -u
+.IR users ]
+.RB [ -z
+.IR n ]
+.RB [ -G
+.IR group ]
+.RB [ -U
+.IR user ]
+.I proto ...
+.PP
+.B disk/mkext
+.RB [ -d
+.IR name ]
+.RB [ -f ]
+.RB [ -h ]
+.RB [ -T ]
+.RB [ -u ]
+.RB [ -v ]
+[
+.I file ...
+]
+.SH DESCRIPTION
+.I Mkfs
+copies files from the file tree
+.I source
+(default
+.BR / )
+to a
+.B kfs
+file system (see
+.IR kfs (4))
+assumed to be mounted on
+.I dest
+(default:
+.BR /n/kfs ).
+The
+.I proto
+files are read,
+and any files specified in them that are out of date are copied to
+.IR dest .
+See
+.IR proto (6)
+for the description of file system prototype files.
+.PP
+.I Mkfs
+by default copies only those files that are out of date.
+Such a file is first copied into a temporary
+file in the appropriate destination directory
+and then moved to the destination file.
+Files in the
+.I kfs
+file system that are not specified in the
+.I proto
+file
+are not updated and not removed.
+.PP
+The options to
+.I mkfs
+are:
+.TF "-s source"
+.TP
+.B -a
+Instead of writing to a
+.B kfs
+file system, write an archive file to standard output, suitable for
+.IR mkext .
+All files in
+.I proto
+are archived,
+not just those out of date.
+.TP
+.B -x
+For use with
+.BR -a ,
+this option writes a list of file names, dates, and sizes to standard output
+rather than producing an archive file.
+.TP
+.BI -n " name"
+Use
+.BI kfs. name .cmd
+as the name of the command file for the
+.IR kfs (4)
+assumed to be mounted on
+.IR dest .
+.TP
+.B -p
+Update the permissions of a file even if it is up to date.
+.TP
+.B -r
+Copy all files.
+.TP
+.BI -s " source"
+Copy from files rooted at the tree
+.IR source .
+.TP
+.BI -u " users"
+Before copying any other file, copy file
+.I users
+into
+.IB dest /adm/users ,
+and if the
+.B -n
+option was given, also issue the
+.IR kfs (4)
+command
+.B users
+to update
+.IR kfs 's
+own user list.
+.TP
+.B -v
+Print the names of all of the files as they are copied.
+.TP
+.BI -z " n"
+Copy files assuming
+.I kfs
+is using a block size of
+.I n
+bytes
+(default 1024).
+If a block contains only 0-valued bytes, it is not copied.
+.TP
+.B -S
+For use with the
+.B -d
+option,
+make owner and group of each file copied match the original.
+.TP
+.BI -G " group"
+Force all files copied to have the given
+.IR group .
+.TP
+.BI -U " user"
+Force all files copied to be owned by the given
+.IR user .
+.PD
+.PP
+.I Mkext
+unpacks an archive created by the
+.B -a
+option of
+.IR mkfs ,
+read from the standard input.
+If one or more files are specified on the command line,
+only those files are unpacked.
+If a
+.I file
+is a directory,
+all files and subdirectories of that directory are also unpacked.
+When a file is unpacked, the entire path is created if it
+does not exist.
+If no files are specified, the entire archive is unpacked;
+in this case, missing intermediate directories are not created.
+The options are:
+.TP
+.BI -d " dir"
+Treat
+.I dir
+(default
+.LR / )
+as the root directory when unpacking the archive.
+.TP
+.B -h
+Print headers for the files on standard output
+instead of unpacking the files.
+.TP
+.B -T
+Restore the modification times of the files.
+.TP
+.B -u
+Set the owners of the files created to correspond to
+those in the archive and restore the modification times of the files.
+.TP
+.B -v
+Print the names and sizes of files as they are extracted.
+.SH EXAMPLES
+.PP
+Make an archive to establish a new file system
+(assuming that the output file
+.B arch
+is not referenced by
+.BR proto ):
+.IP
+.EX
+bind '#U' /n/local
+disk/mkfs -a -s /n/local proto > arch
+.EE
+.PP
+Unpack that archive on another machine:
+.IP
+.EX
+mount tcp!server /n/remote
+disk/mkext -u -d /n/remote < arch
+.EE
+.SH FILES
+.TF /lib/proto/portproto
+.TP
+.B /lib/proto
+directory of prototype files.
+.TP
+.B /lib/proto/all
+contains a single + (archive any directory structure)
+.SH SOURCE
+.B /appl/cmd/disk/mkfs.b
+.br
+.B /appl/cmd/disk/mkext.b
+.SH "SEE ALSO"
+.IR fs (1),
+.IR kfs (4),
+.IR proto (6),
+.IR kfscmd (8)
--- /dev/null
+++ b/man/8/ping
@@ -1,0 +1,64 @@
+.TH 8 PING
+.SH NAME
+ping \- probe the Internet
+.SH SYNOPSIS
+.B ip/ping
+.RB [ -alq ]
+[
+.BI -i " interval"
+] [
+.BI -s " size"
+] [
+.BI -n " nping"
+]
+.I destination
+.SH DESCRIPTION
+.I Ping
+sends ICMP echo requests to a network
+.I destination
+(which has the syntax accepted by
+.IR dial (2)).
+The target host, if up, should send a corresponding reply.
+By default, one line is printed for each reply,
+containing the sequence number (starting at 0) of the message it answers,
+the round trip time for that reply, the average round trip time so far,
+and the `time to live' value from the reply packet.
+.PP
+The options are:
+.TP
+.B -a
+include source and destination IP addresses in the output
+.TP
+.BI -i " interval"
+send requests with the given
+.I interval
+between messages,
+in milliseconds (default: 1 second)
+.TP
+.B -l
+list only lost messages
+.TP
+.BI -n " nping"
+send
+.I nping
+messages in all (default: 32)
+.TP
+.B -q
+suppress per-packet output, giving summary data only
+.TP
+.BI -s " size"
+send request packets of the given
+.I size
+in bytes
+(default: 64, minimum 32)
+.SH SOURCE
+.B /appl/cmd/ip/ping.b
+.SH SEE ALSO
+.IR dial (2),
+.IR ip (3)
+.SH DIAGNOSTICS
+.I Ping
+yields an error status if any request had no corresponding reply.
+.SH BUGS
+Works only on native Inferno and when hosted on Plan 9, owing to the lack of
+access to ICMP on other hosted systems.
--- /dev/null
+++ b/man/8/plumber
@@ -1,0 +1,94 @@
+.TH PLUMBER 8
+.SH NAME
+plumber \- plumber for interapplication message routing
+.SH SYNOPSIS
+.B plumber
+[
+.B -v
+] [
+.B -w
+] [
+.BI -c " wmchan"
+] [
+.I rulefile
+\&...
+]
+.SH DESCRIPTION
+.I Plumber
+provides high-level message-passing between applications.
+In a plumbed environment,
+applications can receive messages on an input port, which is given a logical name.
+Messages are not sent directly between applications but are routed via
+the plumber, following user-specified rules.
+.PP
+.I Plumber
+is typically started by
+.IR wm (1)'s
+startup script.
+It reads each
+.I rulefile
+(default:
+.BI /usr/ user /lib/plumbing )
+in turn.
+Each file has the form described in
+.IR plumbing (6);
+the rules direct the routing of each message
+.I plumber
+receives.
+.I Plumber
+then lurks in the background with its mate,
+awaiting plumbing requests sent by
+.IR plumbmsg (2),
+by windowing applications in response to events such as button clicks or drag-and-drop,
+or by
+.IR plumb (1).
+.PP
+When a message arrives,
+.I plumber
+applies the rules to decide how to route it.
+It forwards the message to the selected application's input port, starting it if necessary.
+If no rule applies (or some other error occurs),
+.I plumber
+returns an error to the message's sender.
+The
+.B -v
+option causes
+.I plumber
+to log the contents of messages it receives, to help debug plumbing rules and applications.
+.PP
+.I Plumber
+normally starts applications directly.
+For use on devices that have specialised
+window managers, not
+.IR wm (1),
+the
+.B -w
+option causes
+.I plumber
+to start applications indirectly, by sending a message to
+a window manager listening on
+.BR /chan/wm ,
+allowing
+the window manager to track every application started.
+The
+.B -c
+option can select an alternative
+.I wmchan
+to
+.BR /chan/wm .
+
+.SH FILES
+.TF /usr/user/lib/plumbing
+.TP
+.BI /usr/ user /lib/plumbing
+default plumbing rules for
+.I user
+.SH SOURCE
+.B /appl/cmd/plumber.b
+.br
+.B /appl/lib/plumbing.b
+.SH SEE ALSO
+.IR plumb (1),
+.IR wm (1),
+.IR plumbmsg (2),
+.IR plumbing (6)
--- /dev/null
+++ b/man/8/prep
@@ -1,0 +1,710 @@
+.TH PREP 8
+.SH NAME
+prep, fdisk, format, mbr \- prepare hard and floppy diskettes, flashes
+.SH SYNOPSIS
+.B disk/prep
+[
+.B -bcfnprw
+]
+[
+.B -a
+.I name
+]...
+[
+.B -s 
+.I sectorsize
+]
+.I plan9partition
+.PP
+.B disk/fdisk
+[
+.B -abfprw
+]
+[
+.B -s
+.I sectorsize
+]
+.I disk
+.PP
+.B disk/format
+[
+.B -dfvx
+]
+[
+.B -b 
+.I bootblock
+]
+[
+.B -c
+.I csize
+]
+[
+.B -l
+.I label
+]
+[
+.B -r
+.I nresrv
+]
+[
+.B -t
+.I type
+]
+.I disk
+[
+.IR file ...
+]
+.PP
+.B disk/mbr
+[
+.B -9
+]
+[
+.B -m
+.I mbrfile
+]
+.SH DESCRIPTION
+A partition table is stored on a hard disk to specify the division of
+the physical disk into a set of logical units.
+On PCs, the partition table is stored at the end of the master boot record
+of the disk.
+Partitions of type
+.B 0x39
+are Plan 9 partitions.
+Inferno uses the same type and follows other Plan 9 conventions described here.
+The names of PC partitions are chosen by convention from the type:
+.BR dos ,
+.BR plan9 ,
+etc.
+Second and subsequent partitions of the same type on a given disk are given
+unique names by appending a number (or a period and a number if the name
+already ends in a number).
+.PP
+Plan 9 partitions (and Plan 9 disks on non-PCs) are
+themselves divided, using a textual partition table, called the Plan 9 partition table, in the second
+sector of the partition (the first is left for architecture-specific boot data, such as PC boot blocks).
+Inferno again uses the same conventions.
+The table is a sequence of lines of the format
+.BI part " name start end" \fR,
+where
+.I start
+and
+.I end
+name the starting and ending sector.
+Sector 0 is the first sector of the Plan 9 partition or disk,
+regardless of its position in a larger disk.
+Partition extents do not contain the ending sector,
+so a partition from 0 to 5 and a partition from 5 to 10
+do not overlap.
+.PP
+The Plan 9 partition often contains a number of
+conventionally named subpartitions.
+Only
+.BR 9fat ,
+.BR fs
+and
+.BR nvram
+are currently used by Inferno, but the others are included for reference.
+They include:
+.TF arenas
+.TP 
+.B 9fat
+A small FAT file system used to hold
+configuration information 
+(such as
+.B plan9.ini
+and
+.BR plan9.nvr )
+and kernels.
+This typically begins in the first sector
+of the partition, and contains the partition
+table as a ``reserved'' sector.
+See the discussion of the
+.B -r
+option to
+.IR format .
+.TP
+.B arenas
+A Plan 9
+.IR venti
+arenas partition.
+.TP
+.B cache
+A Plan 9
+.IR cfs
+file system cache.
+.TP
+.B fossil
+A Plan 9
+.IR fossil
+file system.
+.TP
+.B fs
+A
+.IR kfs (4)
+file system.
+.TP
+.B fscfg
+A one-sector partition used to store a
+.IR ds (3)
+configuration.
+.TP
+.B isect
+A Plan 9
+.IR venti
+index section.
+.TP
+.B nvram
+A one-sector partition used to simulate non-volatile RAM on PCs.
+.TP
+.B other
+A non-archived Plan 9
+.IR fossil
+file system.
+.TP
+.B swap
+A Plan 9
+swap partition.
+.PD
+.PP
+.I Fdisk
+edits the PC partition table and is usually
+invoked with a disk like
+.B /dev/sdC0/data 
+as its argument, while
+.I prep
+edits the Plan 9 partition table
+and is usually invoked with a disk partition
+like
+.B /dev/sdC0/plan9
+as its argument.
+.I Fdisk
+works in units of disk ``cylinders'': the cylinder
+size in bytes is printed when
+.I fdisk
+starts.
+.I Prep
+works in units of disk sectors, which are almost always 512 bytes.
+.I Fdisk
+and
+.I prep
+share most of their options:
+.TP
+.B -a
+Automatically partition the disk.
+.I Fdisk
+will create a Plan 9
+partition in the largest unused area on the disk,
+doing nothing if a
+Plan 9 partition already exists.
+If no other partition on the disk is marked active (i.e. marked as the boot partition),
+.I fdisk
+will mark the new partition active.
+.IR Prep 's
+.B -a
+flag takes the name of a partition to create.
+(See the list above for partition names.)
+It can be repeated to specify a list of partitions to create.
+If the disk is currently unpartitioned, 
+.I prep
+will create the named partitions on the disk,
+attempting to use the entire disk in a sensible manner.
+The partition names must be from the list given above.
+.TP
+.B -b
+Start with a blank disk, ignoring any extant partition table.
+.TP
+.B -p
+Print a sequence of commands that when sent to the disk device's
+.B ctl 
+file 
+will bring the partition
+table information kept by
+the 
+.IR sd (3) 
+driver up to date.
+Then exit.
+.I Prep
+will check to see if it is being called with a disk partition
+(rather than an entire disk) as its argument; if so, it
+will translate the printed sectors by the partition's offset
+within the disk.
+Since 
+.I fdisk
+operates on a table of unnamed partitions,
+it assigns names based on the partition type
+(e.g.,
+.BR plan9 ,
+.BR dos ,
+.BR ntfs ,
+.BR linux ,
+.BR linuxswap )
+and resolves collisions by appending a numbered suffix.
+(e.g.,
+.BR dos ,
+.BR dos1 ,
+.BR dos2 ).
+.TP
+.B -r
+In the absence of the 
+.B -p
+and
+.B -w
+flags, 
+.I prep
+and
+.I fdisk
+enter an interactive partition editor;
+the 
+.B -r
+flag runs the editor in read-only mode.
+.TP
+.BI -s " sectorsize"
+Specify the disk's sector size.
+In the absence of this flag,
+.I prep
+and
+.I fdisk
+look for a disk
+.B ctl
+file and read it to find the disk's sector size.
+If the
+.B ctl
+file cannot be found, a message is printed and
+a sector size of 512 bytes is assumed.
+.TP
+.B -w
+Write the partition table to the disk and exit.
+This is useful when used in conjunction with
+.B -a
+or
+.BR -b .
+.PP
+If neither the
+.B -p
+flag nor the
+.B -w
+flag is given,
+.I prep
+and
+.I fdisk
+enter an interactive partition editor that
+operates on named partitions.
+The PC partition table distinguishes between
+primary partitions, which can be listed in the boot
+sector at the beginning of the disk,
+and secondary (or extended) partitions, arbitrarily
+many of which may be chained together in place
+of a primary partition.
+Primary partitions are named
+.BR p \fIn\fR,
+secondary partitions
+.BR s \fIn\fR.
+The number of primary partitions plus number of contiguous chains of
+secondary partitions cannot exceed four.
+.PP
+The commands are as follows.
+In the descriptions, read ``sector'' as ``cylinder'' when using
+.IR fdisk .
+.TP
+.B "a\fR \fIname\fR [ \fIstart\fR [ \fIend\fR ] ]"
+Create a partition named
+.I name
+starting at sector offset
+.I start
+and ending at offset
+.IR end .
+The new partition will not be created if
+it overlaps an extant partition.
+If
+.I start
+or
+.I end
+are omitted,
+.I prep
+and
+.I fdisk
+will prompt for them.
+In
+.IR fdisk ,
+the newly created partition has type
+.RB `` PLAN9 ;''
+to set a different type, use the
+.B t
+command (q.v.).
+.I Start
+and
+.I end
+may be expressions using the operators
+.BR + ,
+.BR - ,
+.BR * ,
+and
+.BR / ,
+numeric constants, and the
+pseudovariables
+.B .
+and
+.BR $ .
+At the start of the program,
+.B .
+is set to zero; each time a partition is
+created, it is set to the end sector
+of the new partition.
+It can also be explicitly set using the
+.B .
+command.
+When evaluating
+.IR start ,
+.B $
+is set to one past the last disk sector.
+When evaluating
+.IR end ,
+.B $
+is set to the maximum value that 
+.I end
+can take on without running off the disk
+or into another partition.
+Finally, the expression
+.IB n %
+evaluates to 
+.RI ( n × disksize )/100.
+As an example, 
+.B a
+.B .
+.B .+20%
+creates a new partition starting at
+.B .
+that takes up a fifth of the disk,
+and 
+.B a
+.B 1000
+.B $
+creates a new partition starting at
+sector 1000 and
+extending as far as possible.
+.TP
+.B ".\fR \fInewdot"
+Set the value of the variable
+.B .
+to
+.IR newdot ,
+which is an arithmetic expression as described
+in the discussion of the
+.B a
+command.
+.TP
+.BI d " name"
+Delete the named partition.
+.TP
+.B h
+Print a help message listing command synopses.
+.TP
+.B p
+Print the disk partition table.
+Unpartitioned regions are also listed.
+The table consists of a number of lines containing
+partition name, beginning and ending sectors,
+and total size.
+A 
+.B '
+is prefixed to the names of partitions
+whose entries have been modified but not written to disk.
+.I Fdisk
+adds to the end of each line a textual partition type,
+and places a 
+.B *
+next to the name of the active partition
+(see the
+.B A
+command below).
+.TP
+.B P
+Print the partition table in the format accepted by the disk's
+.B ctl
+file, which is also the format of the output of the
+.B -p
+option.
+.TP
+.B w
+Write the partition table to disk.
+.I Prep
+will also inform the kernel of the changed
+partition table.
+The write will fail if any programs have any
+of the disk's partitions open.
+If the write fails (for this or any other reason),
+.I prep
+and
+.I fdisk
+will attempt to restore the partition table to
+its former state.
+.TP
+.B q
+Quit the program.
+If the partition table has been modified but not written,
+a warning is printed.
+Typing
+.B q
+again will quit the program.
+.PP
+.I Fdisk
+also has the following commands.
+.TP
+.BI A " name
+Set the named partition active.
+The active partition is the one whose boot block is used
+when booting a PC from disk.
+.TP
+.B e
+Print the names of empty slots in the partition table, i.e., the
+valid names to use when creating a new partition.
+.TP
+.BI t " \fR[\fI type \fR]
+Set the partition type.  If it is not given, 
+.I fdisk
+will display a list of choices and then prompt for it.
+.PD
+.PP
+.I Format
+prepares for use the floppy diskette or hard disk partition in the file named
+.IR disk ,
+for example
+.B /dev/fd0disk 
+or
+.BR /dev/sdC0/9fat .
+The options are:
+.TP
+.B -f
+Do not physically format the disc. Used
+to install a FAT file system on a
+previously formatted disc. If
+.I disk
+is not a floppy device, this flag is a no-op.
+.TP
+.B -t
+specify a density and type of disk to be prepared.
+The possible
+.I types
+are:
+.RS
+.TP
+.B 3½DD
+3½" double density, 737280 bytes
+.TP
+.B 3½HD
+3½" high density, 1474560 bytes
+.TP
+.B 5¼DD
+5¼" double density, 368640 bytes
+.TP
+.B 5¼HD
+5¼"  high density, 1146880 bytes
+.TP
+.B hard
+fixed disk
+.PD
+.PP
+The default when
+.I disk
+is a floppy drive is the highest possible on the device.
+When 
+.I disk
+is a regular file, the default is
+.BR 3½HD .
+When 
+.I disk
+is an
+.IR sd (3)
+device, the default is 
+.BR hard .
+.RE
+.TP
+.B -d
+initialize a FAT file system on the
+.IR disk .
+.TP
+.B -b
+use the contents of
+.I bootblock
+as a bootstrap block
+to be installed in sector 0.
+.PD
+.PP
+The remaining options have effect only when
+.B -d
+is specified:
+.TP
+.B -c
+use a FAT cluster size of
+.I csize
+sectors when creating the FAT.
+.TP
+.B -l
+add a
+.I label
+when creating the FAT file system.
+.TP
+.BI -r
+mark the first 
+.I nresrv
+sectors of the partition as ``reserved''.
+Since the first sector always contains the
+FAT parameter block, this really marks
+the
+.IR nresrv -1
+sectors starting at sector 1 as ``reserved''.
+When formatting the
+.B 9fat
+partition, 
+.B -r
+.B 2
+should be used to jump over the partition table sector.
+.PD
+.PP
+Again under
+.BR -d ,
+any
+.I files
+listed are added, in order,
+to the root
+directory of the FAT file system.  The files are
+contiguously allocated.
+If a file is named
+.BR 9load ,
+it will be created with the
+.B SYSTEM
+attribute set so that
+.IR dossrv (4) 
+keeps it contiguous when modifying it.
+.PP
+.I Format
+checks for a number of common mistakes; in particular,
+it will refuse to format a 
+.B 9fat
+partition unless 
+.B -r
+is specified with 
+.I nresrv 
+larger than two.
+It also refuses to format a raw
+.IR sd (3)
+partition that begins at offset zero in the disk.
+(The beginning of the disk should contain an
+.I fdisk
+partition table with master boot record,
+not a FAT file system or boot block.)
+Both checks are disabled by the
+.B -x
+option.
+The
+.B -v
+option prints debugging information.
+.PP
+The file
+.B /Inferno/386/pbs
+is an example of a suitable
+.I bfile
+to make the disk a boot disk.
+It gets loaded by the BIOS at 0x7C00,
+reads the root directory into address 0x7E00, and looks at
+the first root directory entry.
+If that file is called
+.BR 9LOAD ,
+it uses
+single sector reads to load the file into address 0x10000 and then
+jumps to the loaded file image.
+The file
+.B /Inferno/386/pbslba
+is similar, but because it uses LBA addressing (not supported
+by all BIOSes), it can access more than the first 8.5GB of the disk.
+.PP
+.I Mbr
+installs a new boot block in sector 0 (the master boot record)
+of a disk such as
+.BR /dev/sdC0/data .
+This boot block should not be confused with the
+boot block used by 
+.IR format ,
+which goes in sector 0 of a partition.
+Typically, the boot block in the master boot record
+scans the PC partition table to find an active
+partition and then executes the boot block for
+that partition.
+The partition boot block then loads a bootstrap
+program such as
+.IR 9load (10.8),
+which then loads the operating system.
+If MS-DOS or Windows 9[58] is already installed
+on your hard disk, the master boot record
+already has a suitable boot block.
+Otherwise, 
+.B /Inferno/386/mbr
+is an appropriate 
+.IR mbrfile .
+It detects and uses LBA addressing when available
+from the BIOS (the same could not
+be done in the case of
+.B pbs
+due to space considerations).
+If the
+.I mbrfile
+is not specified, a boot block is installed that
+prints a message explaining that the disk is not bootable.
+The
+.B -9
+option initialises the partition table to consist of one
+.BR plan9
+partition which spans the entire disc starting at the end of the
+first track.
+.SH EXAMPLES
+Initialize the kernel disk driver with the partition information
+from the FAT boot sectors.
+If Plan 9 partitions exist, pass that partition information as well.
+.IP
+.EX
+for(disk in /dev/sd??) {
+	if(ftest -f $disk/data && ftest -f $disk/ctl){
+		disk/fdisk -p $disk/data >$disk/ctl
+	}
+	for(part in $disk/plan9*){
+		if(ftest -f $part){
+			disk/prep -p $part >$disk/ctl
+		}
+	}
+}
+.EE
+.PP
+Create a boot floppy on a previously formatted diskette:
+.IP
+.EX
+disk/format -b /Inferno/386/pbs -df /dev/fd0disk /Inferno/386/9load /tmp/plan9.ini
+.EE
+.PP
+Initialize the blank hard disk
+.BR /dev/sdC0/data .
+.IP
+.EX
+disk/mbr -m /Inferno/386/mbr /dev/sdC0/data
+disk/fdisk -baw /dev/sdC0/data
+disk/prep -bw -a^(9fat fs) /dev/sdC0/plan9
+disk/format -b /Inferno/386/pbslba -d -r 2 /dev/sdC0/9fat 9load 9pcdisk plan9.ini
+.EE
+.PP
+.SH SOURCE
+.B /appl/cmd/disk/prep
+.br
+.B /appl/cmd/disk/format.b
+.br
+.B /os/boot/pc
+.SH SEE ALSO
+.IR floppy (3),
+.IR sd (3),
+.IR 9load (10.8),
+.IR plan9.ini (10.8)
+.SH BUGS
+.I Format
+can create FAT12 and FAT16
+file systems, but not FAT32 file systems.
+The boot block can only read from
+FAT12 and FAT16 file systems.
--- /dev/null
+++ b/man/8/rdbgsrv
@@ -1,0 +1,58 @@
+.TH RDBGSRV 8
+.SH NAME
+rdbgsrv \- remote debug server
+.SH SYNOPSIS
+.B "bind -b '#t' /dev"
+.PP
+.B auxi/rdbgsrv
+[
+.BI \-d n
+] [
+.BI \-s baud
+] [
+.BI \-f dev
+]
+.I mountpoint
+.SH DESCRIPTION
+.I Rdbgsrv
+interposes itself between
+.I dev
+(default:
+.BR /dev/eia0 )
+and
+.I mountpoint
+to convey 9P messages via the serial port to and from a 9P server program
+running on a board running native Inferno.
+The
+.B \-f
+option specifies the serial device; the default is
+.BR /dev/eia0 .
+The
+.B \-s
+option sets the line speed; the default is 38400 baud.
+The
+.B \-d
+option selects debugging options by a bit mask:
+1, print trace of 9P message types;
+2, print actual 9P message contents.
+.PP
+The monitor program on the board must be started first.
+.I Rdbgsrv
+writes the two byte message
+.BR go ,
+and keeps reading the device until it sees the reply
+.BR ok .
+It then attempts to mount the exported name space, and
+copies 9P messages to and from the device.
+.PP
+Once
+.I rdbgsrv
+is running, any device files provided by the program
+will be visible at
+.IR mountpoint .
+.SH SOURCE
+.B /appl/auxi/rdbgsrv.b
+.SH SEE ALSO
+.IR styxchat (8)
+.SH BUGS
+No error recovery is applied, let alone error correction.
--- /dev/null
+++ b/man/8/register
@@ -1,0 +1,85 @@
+.TH REGISTER 8 mux
+.SH NAME
+register \- command to register set-top-box identity with signer
+.SH SYNOPSIS
+.B mux/register
+[
+.I signer
+]
+.SH DESCRIPTION
+.I Register
+is intended for use on a set top box (or similar device).
+It connects to
+.IR signer ,
+a machine configured to sign certificates,
+and obtains an authenticated certificate based on the contents of
+.L /nvfs/ID
+(the set top box ID in non-volatile memory).
+The certificate is saved in the file
+.L /nvfs/default
+for later use.
+If no
+.I signer
+is named explicitly, the
+.B $SIGNER
+named in
+.IR db (6)
+is used instead.
+.PP
+There are several phases to obtaining the certificate.
+.IP 1.
+The register command interacts with
+.IR signer (8)
+on the signing host
+to construct the certificate. This certificate is `blinded' by a random bit mask, sent back to
+.I register
+which displays it in textual or graphical form to
+the user.
+.IP 2.
+The user running
+.I register
+must use an independent,
+secure mechanism (for example, an untapped telephone call)
+to communicate with a human agent at the
+site acting as
+.IR signer .
+That agent runs
+.I verify
+(see
+.IR signer (8))
+to display the same `blinded' certificate that was
+shown to
+.IR register 's
+user at the client.
+Once the agent is convinced that the `blinded' certificate has been delivered to the correct party, the agent tells
+.I verify
+to accept the identity of the caller.
+.IP 3.
+.I Register
+then connects to the
+.I countersigner
+process (see
+.IR signer (8))
+to obtain the bitmask needed to `unblind' the previously received certificate.
+This step can only validly be performed after the successful
+completion of
+.I verify
+on the
+.I signer.
+.SH FILES
+.TF /services/cs/db
+.TP
+.B /nvfs/ID
+File emulating set top box-id in ROM.
+.TP
+.B /nvfs/default
+Repository of authenticated certificate.
+.TP
+.B /services/cs/db
+Default definition of `signer' host.
+.SH SOURCE
+.B /appl/mux/register.b
+.SH "SEE ALSO"
+.IR db (6),
+.IR manufacture (8),
+.IR signer (8)
--- /dev/null
+++ b/man/8/rip
@@ -1,0 +1,98 @@
+.TH RIP 8
+.SH NAME
+rip \- routing information protocol
+.SH SYNOPSIS
+.B ip/rip
+.RB [ -2 ]
+.RB [ -b ]
+.RB [ -d ]
+.RB [ -n ]
+[
+.BI -x " mntpt"
+] [
+.I net
+\&...
+]
+.B &
+.SH DESCRIPTION
+.I Rip
+implements the Internet RIP routing protocol
+described by RFC1058 and RFC2453.
+It watches the network and makes appropriate changes
+to the machine's Internet routing table
+(see
+.B iproute
+in
+.IR ip (3)),
+based on routing packets
+broadcast by gateways on the network.
+.I Rip
+is only used when a single default gateway is inadequate,
+typically because a machine sits on a network directly connected to
+several others,
+having no common gateway or router.
+On networks where there is just one gateway, it is usually simpler and more efficient
+to configure that statically using
+.IR ndb (6)
+or dynamically using DHCP/BOOTP,
+rather than running
+.IR rip .
+.PP
+.I Rip
+serves the network on
+.I mntpt
+(default:
+.BR /net ).
+When it starts,
+.I rip
+learns its own interfaces and directly attached networks by reading
+.IB mntpt /ipifc ,
+and notes any routes currently in
+.IB mntpt /iproute .
+.PP
+By default,
+.I rip
+neither broadcasts routes nor replies to requests for its route table.
+If the
+.B \-b
+option is given,
+.I rip
+periodically broadcasts changes to its routing table to each of its interfaces.
+If at least one explicit
+.I net
+address is given, the broadcasts are restricted to just the interfaces listed
+(and
+.B \-b
+is implied).
+.PP
+The
+.B \-d
+option causes
+.I routed
+to record changes it makes to the routing tables.
+This can be helpful when locating misleading announcements
+from rogue gateways.
+A second
+.B \-d
+will include detailed information about every packet.
+The
+.B \-n
+option tells
+.I rip
+not to change the local routing table, but only say what changes it would have made.
+.PP
+.I Rip
+understands both version1 and version 2 of the protocol,
+and interprets updates from gateways appropriately.
+By default, it transmits updates using version 1; if the
+.B -2
+option is given, it uses version 2 instead, which is preferable when
+the network has subnets.
+.\".SH FILES
+.\".LR /sys/log/iproute "	debugging information"
+.SH SOURCE
+.B /appl/cmd/ip/rip.b
+.SH "SEE ALSO"
+.IR ip (3),
+.IR ndb (6)
+.\" .IR ipconfig (8)
--- /dev/null
+++ b/man/8/rstyxd
@@ -1,0 +1,88 @@
+.TH RSTYXD 8
+.SH NAME
+rstyxd \- Styx-based remote execution and file service
+.SH SYNOPSIS
+.B auxi/rstyxd
+.I alg
+\&...
+.SH DESCRIPTION
+.PP
+.I Rstyxd
+provides a remote-execution service.
+Having authorised the client and optionally established
+.IR ssl ,
+as described above,
+it reads a single line from its standard input.
+The line contains a decimal value that is the count of the number
+of bytes that follow,
+which
+.I rstyxd
+reads as a
+.IR utf (6)-encoded
+string.
+The string contains a command, which is parsed into arguments,
+following the quoting
+conventions of
+.IR sh (1).
+The first argument is the command name.
+.I Rstyxd
+prepares a modified name space in which
+to run the command.
+It mounts the connection (standard input) on
+.BR /n/client ,
+binds
+.BR /n/client/dev
+onto
+.BR /dev ,
+and opens the new
+.B /dev/cons
+(ie, the remote client's
+.BR /dev/cons )
+on file descriptors 0, 1 and 2.
+Finally, it executes the command.
+.PP
+.I Rstyxd
+is normally started by
+.IR svc (8)
+in response to incoming network calls.
+It expects the standard input to be connected to the client;
+unusually, it is both read and written.
+It
+first authenticates the incoming call using
+.IR keyring-auth (2)
+via
+.IR security-auth (2).
+On successful authorisation, the server sets
+its user identity to that of the caller,
+and the client can request that a digest and/or encryption
+algorithm be applied using
+.IR ssl (3)
+to protect the data exchanged with the server.
+Each
+.I alg
+names a digest or encryption algorithm that the server will allow
+the client to use,
+in any form accepted by
+.IR ssl ;
+the special name
+.B none
+is usually listed, to allow the client to choose not to use
+.IR ssl .
+.SH FILES
+.TF /usr/user/keyring/default
+.TP
+.B /n/client
+mount point used by
+.I rstyxd
+.TP
+.BI /usr/ user /keyring/default
+server's authentication data when
+.IR svc (8)
+run as given
+.I user
+.SH SOURCE
+.B /appl/cmd/auxi/rstyxd.b
+.SH SEE ALSO
+.IR keyring-auth (2),
+.IR security-auth (2),
+.IR getauthinfo (8)
--- /dev/null
+++ b/man/8/shutdown
@@ -1,0 +1,30 @@
+.TH SHUTDOWN 8
+.SH NAME
+shutdown \- shut down system/emulator
+.SH SYNOPSIS
+.B shutdown
+.B -h
+.PP
+.B shutdown
+.B -r
+.SH DESCRIPTION
+.I Shutdown
+halts
+.RB ( -h )
+or restarts
+.RB ( -r )
+.IR emu (1)
+or a native kernel.
+If restarted,
+.IR emu (1)
+will be given the same options as were given it when it started.
+.SH FILES
+.B /dev/sysctl
+.SH SOURCE
+.TF /dis/shutdown
+.TP
+.B /dis/shutdown
+.IR sh (1)
+script
+.SH "SEE ALSO"
+.IR cons (3)
--- /dev/null
+++ b/man/8/signer
@@ -1,0 +1,117 @@
+.TH SIGNER 8
+.SH NAME
+signer, verify, countersigner \- set-top box authentication
+.SH SYNOPSIS
+.B auth/signer
+.PP
+.BI auth/verify " set-top-box-id"
+.PP
+.B auth/countersigner
+.SH DESCRIPTION
+.I Signer
+and
+.I countersigner
+listen for requests on the service ports
+.B infsigner
+and
+.BR infcsigner ,
+respectively.
+They are typically run via
+.IR svc (8)
+on a machine acting as authentication server for a network.
+.I Verify
+is invoked on the same server, after
+.I signer
+but before
+.IR countersigner ,
+following an independent check of a caller's credentials.
+.PP
+.I Signer
+constructs an authentication certificate from the signer's key (in
+.BR /keydb/signerkey )
+and information from the requesting client, including
+the set top box ID.
+The signer's key can be created using
+.IR createsignerkey (8),
+but if the key does not yet exist,
+.I signer
+creates and initialises
+.B /keydb/signerkey
+itself, with an owner name of
+.LR * .
+.PP
+.I Signer
+`blinds'
+the certificate by XOR-ing it with a random bit mask, then sends the result to the requesting client.
+The client machine's user uses that information to establish identity with a human agent on the
+signing machine.
+.I Signer
+also saves the both the `blinded' and `unblinded' result from the input in
+.BI /keydb/signed/ set-top-box-id
+for
+.I verify
+(see below).
+.PP
+.I Verify
+is run on the signing server
+by the agency running the authentication server, in response to
+a call from a remote user who has invoked
+.IR register (8)
+or an equivalent.
+.I Verify
+checks a caller's identity using information from the file
+.BI /keydb/signed/ set-top-box-id
+created by
+.IR signer .
+The file contains the previously crafted authentication certificate and the `blinded' version of the certificate that was sent to the requesting client.
+.PP
+.I Verify
+displays the `blinded' version textually or graphically, as appropriate, so that it can be compared to that reported by the set-top-box owner over a secure independent mechanism (for example, telephone). If the operator of
+.I verify
+is convinced of the identity of the caller, the operator should accept when prompted by
+.IR verify .
+.I Verify
+then writes the authentication certificate to
+.BI /keydb/countersigned/ set-top-box-id,
+as input for
+.I countersigner
+(see
+.IR signer (8)).
+.PP
+.I Note:
+if the operator of
+.I verify
+accepts the identity, the set-top-box owner should be requested
+to answer `yes' to the prompt displayed by
+.IR register (8).
+The order of
+acceptance\-first on the signer, then on the client\-is essential,
+to produce the countersigned certificate before invoking
+.I countersigner
+to read it.
+.PP
+.I Countersigner
+sends the blinding data in
+.BI /keydb/countersigned/ set-top-box-id
+to the requesting client.
+.SH FILES
+.TF /keydb/countersigned/set-top-box-id
+.TP
+.B /keydb/signerkey
+Secret key of the `signer' host.
+.TP
+.BI /keydb/signed/ set-top-box-id
+Repository of `blinded' and clear certificates.
+.TP
+.BI /keydb/countersigned/ set-top-box-id
+Repository of `unblinded' certificates.
+.SH SOURCE
+.B /appl/cmd/auth/signer.b
+.br
+.B /appl/cmd/auth/verify.b
+.br
+.B /appl/cmd/auth/countersigner.b
+.SH SEE ALSO
+.IR createsignerkey (8),
+.IR register (8),
+.IR svc (8)
--- /dev/null
+++ b/man/8/sntp
@@ -1,0 +1,41 @@
+.TH SNTP 8
+.SH NAME
+sntp \- simple network time protocol client
+.SH SYNOPSIS
+.B ip/sntp
+[
+.B -di
+]
+[
+.I server
+]
+.SH DESCRIPTION
+.I Sntp
+is a simple client for the Simple Network Time Protocol (RFC1361).
+It requests the time from the SNTP service on
+.I server
+(default:
+.BR udp!$ntp!ntp ),
+and if it receives a plausible reply,
+sets the local time accordingly, in both
+.IR rtc (3)
+and
+.BR /dev/time .
+The
+.B -d
+option prints debugging text, including the time received;
+the
+.B -i
+option stops
+.I sntp
+from actually updating the local time.
+.SH FILES
+.B #r/rtc
+.br
+.B /dev/time
+.SH SOURCE
+.B /appl/cmd/ip/sntp.b
+.SH SEE ALSO
+.IR date (1),
+.IR cons (3)
+
--- /dev/null
+++ b/man/8/styxchat
@@ -1,0 +1,245 @@
+.TH STYXCHAT 8
+.SH NAME
+styxchat \- exchange 9P (Styx) messages with a server or client
+.SH SYNOPSIS
+.B styxchat
+[
+.RI -m " messagesize"
+] [
+.B -s
+] [
+.B -v
+] [
+.B -n
+] [
+.I destination
+]
+.SH DESCRIPTION
+.I Styxchat
+exchanges messages with a 9P service.
+(9P was previously called `Styx' when used by Inferno, hence the command's name.)
+See
+.IR intro (5)
+for the protocol definition.
+It makes a connection to a given
+.IR destination ,
+(or waits for a connection on
+.IR destination,
+if the
+.B -s
+option is specified),
+then reads a textual representation of 9P T-messages from the standard
+input and writes them on the connection, with a copy on standard output,
+simultaneously reading 9P R-messages from the connection and printing a representation of them
+on standard output.
+Each message is represented by one line
+on the standard output in the form of a literal of either
+.B Tmsg
+or
+.B Rmsg
+types defined in
+.IR styx (2).
+The
+.B -v
+option causes a second line to be written for
+.B Rmsg.Read
+and
+.B Tmsg.Write
+that shows the data transmitted, as text or binary as appropriate;
+if
+.B -v
+appears a second time, a third line is written that
+shows the text equivalent of apparently binary data (useful to see text that is surrounded by binary data).
+.PP
+By default,
+.I destination
+is the name of a file, typically one end of a named pipe.
+The
+.B \-n
+option causes
+.I destination
+to be interpreted as a network address, as accepted by
+.IR dial (2)
+(or
+.I listen
+with
+.BR -s ).
+If
+.I destination
+is not provided,
+.B styxchat
+reads and writes 9P messages on its standard input,
+using
+.B /dev/cons
+where it would usually use its standard input and output.
+.PP
+Each line of standard input has the form:
+.IP
+.br
+.BI  Tversion " messagesize version"
+.br
+.BI Tauth " afid uname aname"
+.br
+.BI Tflush " oldtag"
+.br
+.BI Tattach " fid afid uname aname"
+.br
+.BI Twalk " fid newfid \f1[\fP name \f1... ]\fP"
+.br
+.BI Topen " fid mode"
+.br
+.BI Tcreate " fid name perm mode"
+.br
+.BI Tread " fid offset count"
+.br
+.BI Twrite " fid offset data"
+.br
+.BI Tclunk " fid"
+.br
+.BI Tremove " fid"
+.br
+.BI Tstat " fid"
+.br
+.BI Twstat " fid name uid gid mode mtime length"
+.br
+.BI nexttag " \f1[\fP tag \f1]\fP"
+.br
+.B dump
+.PD
+.PP
+The input is interpreted as space-separated fields
+using the quoting conventions of
+.IR sh (1),
+allowing fields to contain spaces.
+Empty lines and lines beginning with
+.B #
+are ignored.
+The first field on each line is normally the name of a T-message.
+Subsequent fields provide parameter values for
+the corresponding message.
+Integers are given in the format accepted for integers
+by the Limbo compiler (e.g.
+.BR 16rffff ):
+a
+.I tag
+is 16 bits,
+.I offset
+and
+.I length
+are 64 bits, and all others are 32-bit integers.
+If the an integer parameter field contains
+.BR ~0 ,
+it is taken to be the `all ones' value of appropriate size for that parameter;
+this is particularly useful with
+.BR Twstat ,
+where that value represents `no change'.
+In the ``mode'' field of a qid, letters can be given, representing
+mode bits:
+.B d
+for
+.BR QTDIR ,
+.B l
+for
+.BR QTEXCL ,
+.B a
+for
+.BR QTAPPEND ,
+and
+.B u
+for
+.BR QTAUTH .
+In an
+.B Rstat
+message, the qid mode bits are copied into the
+.B Rstat
+mode field in the appropriate place.
+.PP
+Following the
+.IR sh (1)
+quoting rules,
+an empty string is represented by a field containing \f5''\f1.
+The
+.I data
+field is sent as its UTF-8 representation as an array of bytes.
+The value for
+.I fid
+can be
+.B nofid
+(or
+.BR NOFID )
+to represent the `no fid' value in the protocol.
+The
+.I tag
+for each message is automatically supplied by
+.IR styxchat ,
+starting from 1, and incremented with each successful message transmission.
+The
+.B nexttag
+command will cause subsequent tags to start from
+.IR tag ;
+if none is given, it will print the next tag value.
+The
+.I tag
+may be
+.B notag
+to represent the `no tag' value
+.RB ( 16rFFFF ).
+.PP
+The
+.B dump
+command has the same effect as a
+.B -v
+option, allowing data display to be enabled later.
+.PP
+By default,
+.I styxchat
+sends a 9P client's T-messages and prints a server's R-messages.
+The
+.B -s
+option causes it to present a server's view: it prints the T-messages from 9P clients, and sends R-messages
+as it reads a textual representation of them from standard input:
+.IP
+.br
+.BI Rversion " tag messagesize version"
+.br
+.BI Rauth " tag aqid"
+.br
+.BI Rflush " tag"
+.br
+.BI Rerror " tag ename"
+.br
+.BI Rattach " tag qid"
+.br
+.BI Rwalk " tag qid ..."
+.br
+.BI Ropen " tag qid iounit"
+.br
+.BI Rcreate " tag qid iounit"
+.br
+.BI Rread " tag data"
+.br
+.BI Rwrite " tag count"
+.br
+.BI Rclunk " tag"
+.br
+.BI Rremove " tag"
+.br
+.BI Rstat " tag qid mode atime mtime length name uid gid muid"
+.br
+.BI Rwstat " tag"
+.br
+.B dump
+.PD
+.PP
+The input conventions are as above, except that tags
+are required.
+A
+.I qid
+is a single field of the form \fIpath\f1\f5.\f1\fIvers\f1[\f5.\f1\fItype\f1],
+where the three values are decimal integers.
+.SH SOURCE
+.B /appl/cmd/styxchat.b
+.SH SEE ALSO
+.IR styx (2),
+.IR intro (5),
+.IR styxmon (8)
--- /dev/null
+++ b/man/8/styxmon
@@ -1,0 +1,50 @@
+.TH STYXMON 8
+.SH NAME
+styxmon \- monitor a 9P (Styx) conversation
+.SH SYNOPSIS
+.B styxmon
+[
+.B -r
+] [
+.B -t
+]
+.I cmd
+[
+.IR arg ...
+]
+.SH DESCRIPTION
+.I Styxmon
+allows the monitoring of 9P messages sent and received
+by
+.IR cmd ,
+which should serve 9P through its standard input.
+.I Styxmon
+in its turn serves 9P through its standard input,
+and writes information on the 9P messages that
+it sees to the standard error.
+The
+.B -r
+and
+.B -t
+options restrict the messages printed to R-messages
+and T-messages respectively.
+.SH EXAMPLE
+Mount an instance of
+.IR export (4)
+of the current name space through
+.I styxmon
+on
+.B /n/remote
+to monitor all access to it through that name:
+.IP
+.EX
+mount {styxmon {export /}} /n/remote
+ls /n/remote
+cp /n/remote/lib/unicode /n/remote/dev/null
+.EE
+.SH SOURCE
+.B /appl/cmd/styxmon.b
+.SH SEE ALSO
+.IR styx (2),
+.IR intro (5),
+.IR styxchat (8)
--- /dev/null
+++ b/man/8/svc
@@ -1,0 +1,145 @@
+.TH SVC 8
+.SH NAME
+svc: auth, net, registry, rstyx, styx \- start Inferno network services
+.SH SYNOPSIS
+.B svc/net
+.br
+.B svc/auth
+[
+.B -n
+]
+.br
+.B svc/registry
+.br
+.B svc/rstyx
+.br
+.B svc/styx
+.SH DESCRIPTION
+The directory
+.B /dis/svc
+contains several
+.IR sh (1)
+scripts to start network listeners (see
+.IR listen (1))
+that give remote hosts access to specific Inferno services on the current host.
+The scripts can be edited to suit (or configure themselves to suit) the
+requirements of a particular site.
+.PP
+A host that is not an authentication server and wishes to start the usual network services
+can simply invoke
+.BR svc/net ,
+which runs all the others
+.I except
+authentication.
+Authentication servers should normally run
+.B svc/auth
+instead, to start local name and authentication services, and a listener
+for each authentication service but
+.I not
+file service or remote execution.
+.PP
+.I Auth
+must be run (only) on a host that is to act as an authentication server,
+providing signing and other authentication services to itself and the network.
+The
+.B -n
+flag tells it not to start
+.IR keyfs (4),
+perhaps because it has been started already.
+The files
+.BR /keydb/signerkey ,
+created by
+.IR createsignerkey (8),
+and
+.BR /keydb/keys ,
+managed by
+.IR changelogin (8),
+must exist.
+If so,
+.I auth
+starts
+.IR keyfs (4),
+which prompts for the password that protects
+.BR /keydb/keys ,
+the file of secrets shared
+with registered users.
+If the key file is empty, the confirmed password will be used in future to encrypt and decrypt the file;
+otherwise the password must match the one used to encrypt the key file.
+If the password is valid, listeners are started for
+.IR keysrv (4),
+to allow passwords to be changed remotely,
+.IR logind (8),
+to provide signed certificates,
+and
+.IR signer (8).
+Note that although an authentication server must be present to run
+.IR getauthinfo (8)
+to obtain credentials to access another service, once those have been
+issued, the recipient can subsequently present them (if still valid) to
+access that service without further involvement by the service (ie, it
+need not then be running).
+See
+.IR changelogin (8)
+for the user registration program, which can be used once
+.I auth
+has started.
+.PP
+.I Registry
+starts the dynamic service registry (see
+.IR registry (4))
+if it is not already running,
+putting it at the conventional location for the local registry,
+.BR /mnt/registry .
+Initial (static) service descriptions are taken from
+.B /lib/ndb/registry
+if it exists.
+It then starts a listener to give other hosts access to the registry as a 9P
+service at
+.BR tcp!*!registry ,
+normally port 6675.
+.PP
+.I Rstyx
+listens for incoming calls to the
+.B rstyx
+service, and invokes
+.IR rstyxd (8)
+to deal with each one.
+.PP
+.I Styx
+listens for incoming calls to the
+.B styx
+service,
+and for each one, authenticates the caller, then calls
+.IR export (4)
+to export the current root.
+.SH FILES
+.TF /keydb/signerkey
+.TP
+.B /keydb/keys
+encrypted file containing user secrets
+.TP
+.B /keydb/signerkey
+private key of authentication server
+.SH SOURCE
+.B /appl/svc/auth.sh
+.br
+.B /appl/svc/net.sh
+.br
+.B /appl/svc/registry.sh
+.br
+.B /appl/svc/rstyx.sh
+.br
+.B /appl/svc/styx.sh
+.SH SEE ALSO
+.IR listen (1),
+.IR export (4),
+.IR keyfs (4),
+.IR keysrv (4),
+.IR registry (4),
+.IR changelogin (8),
+.IR createsignerkey (8),
+.IR cs (8),
+.IR dns (8),
+.IR logind (8),
+.IR rstyxd (8),
+.IR signer (8)
--- /dev/null
+++ b/man/8/touchcal
@@ -1,0 +1,39 @@
+.TH TOUCHCAL 8
+.SH NAME
+touchcal \- touch screen calibration
+.SH SYNOPSIS
+.B touchcal
+.SH DESCRIPTION
+.I Touchcal
+draws a cross-hair in each corner of the screen in turn (clockwise from the lower left hand corner),
+and waits each time for the user to touch the centre of the cross-hair with the stylus.
+It then prompts with a final cross-hair in the centre of the screen, and waits once
+more for the user to touch its centre with the stylus.
+The process is repeated until
+.I touchcal
+can calculate a transformation matrix that consistently maps the touch panel to screen
+coordinates.
+It then writes corresponding calibration commands for
+.IR touch (3)
+on its standard output, which
+can be saved in a file on the device, perhaps
+provided in NVRAM by
+.IR tinyfs (3)
+or a file system in
+.IR ftl (3).
+.PP
+.I Touchcal
+can be used both inside and outside the
+.IR wm (1)
+environment,
+allowing calibration when the system is initialised, and whilst
+the window system is running.
+.SH FILES
+.TF "/dev/touchctl    "
+.TP
+.B /dev/touchctl
+reset or read existing settings
+.SH SOURCE
+.B /appl/cmd/touchcal.b
+.SH SEE ALSO
+.IR touch (3)
--- /dev/null
+++ b/man/8/virgild
@@ -1,0 +1,55 @@
+.TH VIRGILD 8
+.SH NAME
+virgild \- connection service for remote clients
+.SH SYNOPSIS
+.B ndb/cs
+.br
+.B ip/virgild
+.SH DESCRIPTION
+.I Virgild
+receives requests for name service on UDP/IP
+port
+.BR virgil ,
+defined as
+2202 by
+.IR services (6),
+and hard-coded in
+.IR virgil (2).
+Each request has the form:
+.IP
+.IB userid ? machine-name
+.PP
+.I Virgild
+translates the
+.I machine-name
+using the local connection server
+(see
+.IR cs (8)),
+and sends a response of the following form to the requesting client:
+.IP
+.IB userid ? machine-name = network-address
+.PP
+If the
+.I machine-name
+cannot be translated,
+.I virgild
+makes no response;
+unless another server replies, the client's request will time out.
+.PP
+.I Virgild
+requires that
+.IR cs (8)
+be running before it is started.
+.SH SOURCE
+.B /appl/cmd/ip/virgild.b
+.SH "SEE ALSO"
+.IR rcmd (1),
+.IR cs (8)
+.SH BUGS
+The
+.I userid
+part is currently unused but must still be included.
+.br
+.I Virgild
+is single threaded: a delay in translating a name for one client will delay response
+to any subsequent clients.
--- /dev/null
+++ b/man/9/0intro
@@ -1,0 +1,134 @@
+.TH INTRO 9
+.SH NAME
+intro \- introduction to Inferno Tk
+.SH DESCRIPTION
+This section of the manual provides a reference for the Inferno
+Tk implementation,
+which is accessed by Limbo programs via
+.IR tk (2),
+and from
+.IR sh (1)
+via
+.IR sh-tk (1).
+.PP
+The following pages were derived by Vita Nuova from documentation that is
+.IP
+Copyright © 1990 The Regents of the University of California
+.br
+Copyright © 1994-1996 Sun Microsystems, Inc.
+.br
+See
+.IR copyright (9)
+for the full copyright notice.
+.PP
+The format of the pages has changed to follow the format of the rest of
+this manual, but more important, the content has been changed
+(typically in small ways) to reflect the variant of Tk implemented by
+Inferno.
+.SS Programming Interface
+The interface to Inferno Tk is exclusively through the
+.IR tk (2)
+module; all the Tk commands described in this section of
+the manual are excecuted by passing them as strings to the
+.B cmd
+function in that module.
+The Inferno Tk implementation is based on the Tk 4.0 documentation,
+but there are many differences, probably the greatest of which is that
+there is no associated Tcl implementation, so almost every
+Inferno application using Tk will need to have some Limbo code
+associated with it (the
+.IR sh-tk (1)
+shell module can also fulful this rôle). See ``An Overview of Limbo/Tk''
+in Volume 2 for a tutorial-style introduction to the use of Inferno Tk
+which summarises the differences from Tk 4.0.
+.SS Tk Commands
+The command string passed to
+.B tk->cmd
+may contain one or more Tk commands, separated by semicolons.
+A semicolon is not a command separator when it is nested in braces
+.RB ( {} )
+or brackets
+.RB ( [] )
+or it is escaped by a backslash
+.RB ( \e ).
+Each command is divided into
+.IR words :
+sequences of characters separated by one or more
+blanks and tabs.
+.PP
+There is also a `super quote' convention: at any point in the command
+string a single quote mark
+.RB ( ' )
+means that the entire rest of the string should be treated as one word.
+.PP
+A word beginning with an opening brace
+.RB ( { )
+continues until the balancing closing brace
+.RB ( } )
+is reached. The outer brace characters are stripped. A backslash
+can be used to escape a brace in this context. Backslash
+characters not used to escape braces are left unchanged.
+.PP
+A word beginning with an opening bracket
+.RB ( [ )
+continues until the balancing closing bracket
+.RB ( ] )
+is reached. The enclosed string is then evaluated as if it
+were a command string, and the resulting value
+is used as the contents of the word.
+.PP
+Single commands are executed in order until they are all done
+or an error is encountered. By convention, an error is signaled by a
+return value starting with an exclamation mark
+.RB ( ! ).
+The return value from
+.B tk->cmd
+is the return value of the first error-producing
+command or else the return value of the final single command.
+.PP
+To execute a single command, the first word is examined. It
+must either begin with dot
+.RB ( . )
+in which case it must name an existing widget, which will
+interpret the rest of the command according to its type,
+or one of the following words, each of which is
+documented in a manual page of that name in this section:
+.EX
+
+bind         focus        lower        scrollbar
+button       frame        menu         see
+canvas       grab         menubutton   send
+checkbutton  grid         pack         text
+cursor       image        radiobutton  update
+destroy      label        raise        variable
+entry        listbox      scale
+.EE
+.SS Widget Options
+Each manual page in this section documents the options
+that a particular command will accept. A number of options
+are common to several of the widgets and are named as
+``standard options'' near the beginning of the manual page
+for each widget. These options are documented in
+.IR options (9).
+The types of value required as arguments to options within
+Inferno Tk are documented under
+.IR types (9).
+.SH SEE ALSO
+.IR options (9),
+.IR types (9),
+.IR tk (2),
+.IR sh-tk (1),
+.IR tkcmd (1),
+.IR wmlib (2),
+.IR draw-intro (2),
+``An Overview of Limbo/Tk'' in Volume 2.
+.SH BUGS
+The bracket
+.RB ( [] )
+command interpretation is not applied consistently throughout
+the Inferno Tk commands (notably, the argument to
+the
+.IR send (9)
+command will not interpret this correctly).
+Moreover, if the string to be substituted is significantly bigger
+than the command it was substituting, then it will be truncated.
--- /dev/null
+++ b/man/9/1copyright
@@ -1,0 +1,55 @@
+.TH COPYRIGHT 9
+.SH NAME
+copyright \- copyright notice for Tk documentation
+.SH DESCRIPTION
+The following pages were derived by Vita Nuova from documentation that is
+.IP
+Copyright © 1990 The Regents of the University of California
+.br
+Copyright © 1994-1996 Sun Microsystems, Inc.
+.PP
+and was made available on the following terms:
+.IP
+.ps -1
+.vs -1
+This software is copyrighted by the Regents of the University of
+California, Sun Microsystems, Inc., and other parties.  The following
+terms apply to all files associated with the software unless explicitly
+disclaimed in individual files.
+
+The authors hereby grant permission to use, copy, modify, distribute,
+and license this software and its documentation for any purpose, provided
+that existing copyright notices are retained in all copies and that this
+notice is included verbatim in any distributions. No written agreement,
+license, or royalty fee is required for any of the authorized uses.
+Modifications to this software may be copyrighted by their authors
+and need not follow the licensing terms described here, provided that
+the new terms are clearly indicated on the first page of each file where
+they apply.
+
+IN NO EVENT SHALL THE AUTHORS OR DISTRIBUTORS BE LIABLE TO ANY PARTY
+FOR DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES
+ARISING OUT OF THE USE OF THIS SOFTWARE, ITS DOCUMENTATION, OR ANY
+DERIVATIVES THEREOF, EVEN IF THE AUTHORS HAVE BEEN ADVISED OF THE
+POSSIBILITY OF SUCH DAMAGE.
+
+THE AUTHORS AND DISTRIBUTORS SPECIFICALLY DISCLAIM ANY WARRANTIES,
+INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE, AND NON-INFRINGEMENT.  THIS SOFTWARE
+IS PROVIDED ON AN "AS IS" BASIS, AND THE AUTHORS AND DISTRIBUTORS HAVE
+NO OBLIGATION TO PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR
+MODIFICATIONS.
+
+GOVERNMENT USE: If you are acquiring this software on behalf of the
+U.S. government, the Government shall have only "Restricted Rights"
+in the software and related documentation as defined in the Federal 
+Acquisition Regulations (FARs) in Clause 52.227.19 (c) (2).  If you
+are acquiring the software on behalf of the Department of Defense, the
+software shall be classified as "Commercial Computer Software" and the
+Government shall have only "Restricted Rights" as defined in Clause
+252.227-7013 (c) (1) of DFARs.  Notwithstanding the foregoing, the
+authors grant the U.S. Government and others acting in its behalf
+permission to use and distribute the software in accordance with the
+terms specified in this license.
+.ps +1
+.vs +1
--- /dev/null
+++ b/man/9/INDEX
@@ -1,0 +1,34 @@
+intro 0intro
+1copyright 1copyright
+copyright 1copyright
+bind bind
+button button
+canvas canvas
+checkbutton checkbutton
+choicebutton choicebutton
+cursor cursor
+destroy destroy
+entry entry
+focus focus
+frame frame
+grab grab
+grid grid
+image image
+label label
+listbox listbox
+lower lower
+menu menu
+menubutton menubutton
+options options
+pack pack
+panel panel
+radiobutton radiobutton
+raise raise
+scale scale
+scrollbar scrollbar
+see see
+send send
+text text
+types types
+update update
+variable variable
--- /dev/null
+++ b/man/9/bind
@@ -1,0 +1,194 @@
+.TH BIND 9
+.SH NAME
+bind \- Arrange for events to invoke Tk scripts
+.SH SYNOPSIS
+\f5bind\fI tag sequence script\fR
+.sp
+\f5bind\fI tag sequence \f5+\fIscript\fR
+.SH DESCRIPTION
+The \f5bind\fR command associates Tk scripts with window events.
+If all three arguments are specified, \f5bind\fR will
+arrange for \fIscript\fR (a Tk script) to be evaluated whenever
+the event(s) given by \fIsequence\fR occur in the window(s)
+identified by \fItag\fR.
+If \fIscript\fR is prefixed with a ``+'', then it is appended to
+any existing binding for \fIsequence\fR;  otherwise \fIscript\fR replaces
+any existing binding.
+If \fIscript\fR is an empty string then the current binding for
+\fIsequence\fR is destroyed, leaving \fIsequence\fR unbound.
+In all of the cases where a \fIscript\fR argument is provided,
+\f5bind\fR returns an empty string.
+If \fIscript\fR is prefixed with a ``-'', then any existing binding for
+\fIsequence\fR, whose script is exactly the same as \fIscript\fR, is removed.
+The \fItag\fR argument gives the pathname of the window
+to which \fIsequence\fR is bound.
+.SS Event Patterns
+The \fIsequence\fR argument specifies a sequence of one or more
+event patterns, with optional white space between the patterns.  Each
+event pattern may
+take one of two forms.  In the simplest case it is a single
+printing ASCII character, such as \f5a\fR or \f5[\fR.  The character
+may not be a space character or the character \f5<\fR.  This form of
+pattern matches a \f5KeyPress\fR event for the particular
+character.  The second form of pattern is longer but more general.
+It has the following syntax:
+.IP
+.EX
+\f5<\fIevent\f5-\fIevent\f5-\fR...\f5-\fIevent\f5>\fR
+.EE
+.PP
+The following events are accepted:
+.TP
+.BR Key \ or\  Keypress
+This represents the press of the character in the following
+.IR event .
+If there is no following event, this represents the press of
+any key. The key letter may be escaped with a backslash
+.RB ( \e )
+to prevent any character (e.g.
+.BR > )
+from being treated specially.
+.TP
+.B Configure
+This event occurs whenever the widget is configured
+such that its requested size changes.
+.TP
+.B Control
+This represents the press of the character in the following
+.I
+event
+with the Control key pressed. The character may be quoted
+as for
+.BR Key .
+.TP
+.BR ButtonPress \ or\  Button
+This represents the pressed state of the mouse button given by
+the following event, which should be 1, 2, or 3. If there is no
+following event, this represents the press of any button.
+If the mouse is moved with a button pressed, the
+Button event is delivered in combination with a Motion event
+so long as the button remains pressed.
+.TP
+.B ButtonRelease
+Like
+.BR ButtonPress ,
+but represents the release of any button.
+.TP
+.B Motion
+Mouse movement.
+.TP
+.B Double
+Any events in the sequence representing button presses must
+be double-clicked for the sequence to match.
+.TP
+.B Map
+The event that a toplevel widget is mapped onto the screen.
+.TP
+.B Unmap
+The event that a toplevel widget is unmapped from the screen.
+.TP
+.B Enter
+The event of the mouse entering the widget from outside.
+.TP
+.B Leave
+The event of the mouse leaving the boundaries of the widget.
+.TP
+.B FocusIn
+The event of the widget getting the keyboard focus.
+.TP
+.B FocusOut
+The event of the widget losing the keyboard focus.
+.TP
+.B Destroy
+The event of the widget being destroyed.
+See
+.IR destroy  (9)
+for details of widget destruction.
+.RE
+.PP
+The event sequence can contain any combination of the above
+events. They are treated independently, and if any of the
+events occur, the sequence matches. You cannot combine
+key presses of more than one key. Events will not be combined
+on delivery, except that
+.B Motion
+events may be combined with button presses (possibly doubled).
+.SS Binding Scripts and Substitutions
+The \fIscript\fR argument to \f5bind\fR is a Tk script,
+which will be executed whenever the given event sequence occurs.
+If \fIscript\fR contains
+any \f5%\fR characters, then the script will not be
+executed directly.  Instead, a new script will be
+generated by replacing each \f5%\fR, and the character following
+it, with information from the current event.  The replacement
+depends on the character following the \f5%\fR, as defined in the
+list below.  Unless otherwise indicated, the
+replacement string is the decimal value of the given field from
+the current event.
+Some of the substitutions are only valid for
+certain types of events;  if they are used for other types of events
+the value substituted is undefined.
+.IP \f5%%\fR 5
+Replaced with a single percent.
+.IP \f5%b\fR 5
+The number of the button that was pressed or released.  Valid only
+for \f5ButtonPress\fR and \f5ButtonRelease\fR events.
+.IP \f5%h\fR 5
+For
+.B Configure
+events, this is
+the old requested height of the widget.
+.IP \f5%s\fR 5
+For mouse events, this is the logical OR of the mouse buttons;
+for keyboard events, it is the decimal value of the pressed character.
+.IP \f5%w\fR 5
+For
+.B Configure
+events, this is the old requested width of the widget.
+.IP \f5%x\fR 5
+The x coordinate from the event, relative to the origin
+of the toplevel window. Valid only for
+.BR Enter ,
+.BR Leave ,
+and mouse events.
+.IP \f5%y\fR 5
+The y coordinate from the event, relative to the origin
+of the toplevel window. Valid only for
+.BR Enter ,
+.BR Leave ,
+and mouse events.
+.IP \f5%A\fR 5
+The ASCII character corresponding to a \f5Key\fP event.
+.IP \f5%K\fR 5
+The pressed character for the event, as four hexadecimal digits.
+Valid only for \f5Key\fP events.
+.IP \f5%W\fR 5
+The path name of the window to which the event was reported (the
+\fIwindow\fR field from the event).  Valid for all event types.
+.IP \f5%X\fR 5
+Same as
+.B %x
+except that the screen coordinate system is used.
+.IP \f5%Y\fR 5
+Same as
+.B %y
+except that the screen coordinate system is used.
+.LP
+The replacement string for a %-replacement is formatted as a proper
+Tk list element.
+This means that it will be surrounded with braces
+if it contains spaces, or special characters such as \f5$\fR and
+\f5{\fR may be preceded by backslashes.
+This guarantees that the string will be passed through the Tk
+parser when the binding script is evaluated.
+.SH BUGS
+The above scheme is not ideal, and will probably be fixed
+in the future.
+Quoting is a mess - in particular, the quoting provided for the
+.B %A
+substitution does not work correctly when a quoted character
+is re-interpreted by Tk.
+.LP
+The length of binding scripts is limited to
+128 characters.
+
--- /dev/null
+++ b/man/9/button
@@ -1,0 +1,133 @@
+.TH BUTTON 9
+.SH NAME
+button \- Create and manipulate button widgets
+.SH SYNOPSIS
+\f5button\fI \fIpathName \fR?\fIoptions\fR?
+.SH STANDARD OPTIONS
+.EX
+-activebackground -disabledcolor      -justify
+-activeforeground -font               -relief
+-anchor           -foreground         -takefocus
+-background       -highlightcolor     -text
+-bitmap           -highlightthickness -underline
+-borderwidth      -image
+.EE
+.SH "WIDGET-SPECIFIC OPTIONS"
+.TP
+.B -command \fItkcmd\fP
+Specifies a Tk command to associate with the button.  This command
+is typically invoked when mouse button 1 is released over the button
+window.
+.TP
+.B -height \fIdist\fP
+Specifies a desired height for the button.
+If this option isn't specified, the button's desired height is computed
+from the size of the image or bitmap or text being displayed in it.
+.TP
+.B -state \fIstate\fP
+Specifies one of three states for the button:  \f5normal\fR, \f5active\fR,
+or \f5disabled\fR.  In normal state the button is displayed using the
+\f5foreground\fR and \f5background\fR options.  The active state is
+typically used when the pointer is over the button.  In active state
+the button is displayed using the \f5activeForeground\fR and
+\f5activeBackground\fR options.  Disabled state means that the button
+should be insensitive:  the default bindings will refuse to activate
+the widget and will ignore mouse button presses.
+In this state the \f5background\fR option determines how the button is
+displayed.
+.TP
+.B -width \fIdist\fP
+Specifies a desired width for the button.
+If this option isn't specified, the button's desired width is computed
+from the size of the image or bitmap or text being displayed in it.
+.SH DESCRIPTION
+The \f5button\fR command creates a new window (given by the
+\fIpathName\fR argument) and makes it into a button widget.
+Additional
+options, described above, may be specified on the command line.
+to configure aspects of the button such as its colours, font,
+text, and initial relief.  The \f5button\fR command returns its
+\fIpathName\fR argument.  At the time this command is invoked,
+there must not exist a window named \fIpathName\fR.
+.PP
+A button is a widget that displays a textual string, bitmap or image.
+If text is displayed, it must all be in a single font, but it
+can occupy multiple lines on the screen (if it contains newlines) and
+one of the characters may optionally be underlined using the
+\f5underline\fR option.
+It can display itself in either of three different ways, according
+to
+the \f5state\fR option; 
+it can be made to appear raised, sunken, or flat.  When a user invokes the
+button (by pressing mouse button 1 with the cursor over the
+button), then the Tk command specified in the \f5-command\fR
+option is invoked.
+.SH "WIDGET COMMAND"
+The \f5button\fR command creates a new Tk command whose
+name is \fIpathName\fR.  This
+command may be used to invoke various
+operations on the widget.  It has the following general form:
+.RS
+.EX
+\fIpathName option \fR?\fIarg arg ...\fR?
+.EE
+.RE
+\fIOption\fR and the \fIarg\fRs
+determine the exact behaviour of the command.  The following
+commands are possible for button widgets:
+.TP
+\fIpathName \f5cget\fR \fIoption\fR
+Returns the current value of the configuration option given
+by \fIoption\fR.
+\fIOption\fR may have any of the values accepted by the \f5button\fR
+command.
+.TP
+\fIpathName \f5configure\fR ?\fIoption\fR? ?\fIvalue option value ...\fR?
+Query or modify the configuration options of the widget.
+If no \fIoption\fR is specified, returns a list of all of
+the available options for \fIpathName\fR. If
+one or more \fIoption-value\fR pairs are specified, then the command
+modifies the given widget option(s) to have the given value(s);  in
+this case the command returns an empty string.
+\fIOption\fR may have any of the values accepted by the \f5button\fR
+command.
+.TP
+\fIpathName \f5invoke\fR
+Invoke the Tk command associated with the button, if there is one.
+The return value is the return value from the Tk command, or an
+empty string if there is no command associated with the button.
+This command is ignored if the button's state is \f5disabled\fR.
+
+.SH "DEFAULT BINDINGS"
+Tk automatically creates bindings for buttons that give them
+the following default behaviour:
+.IP [1]
+A button activates whenever the mouse passes over it and deactivates
+whenever the mouse leaves the button.
+.IP [2]
+A button's relief is changed to sunken whenever mouse button 1 is
+pressed over the button, and the relief is restored to its original
+value when button 1 is later released.
+.IP [3]
+If mouse button 1 is pressed over a button and later released over
+the button, the button is invoked.  However, if the mouse is not
+over the button when button 1 is released, then no invocation occurs.
+.IP [4]
+If the
+.B Enter
+or
+.B Space
+key is pressed when the button has keyboard focus,
+the button is invoked.
+.PP
+If the button's state is \f5disabled\fR then none of the above
+actions occur:  the button is completely non-responsive.
+.PP
+The behaviour of buttons can be changed by defining new bindings for
+individual widgets.
+.SH SEE ALSO
+.IR checkbutton (9),
+.IR choicebutton (9),
+.IR options (9),
+.IR radiobutton (9),
+.IR types (9)
--- /dev/null
+++ b/man/9/canvas
@@ -1,0 +1,1359 @@
+.TH CANVAS 9
+.SH NAME
+canvas \- Create and manipulate canvas widgets
+.SH SYNOPSIS
+\f5canvas\fI \fIpathName \fR?\fIoptions\fR?
+.SH STANDARD OPTIONS
+.EX
+-background        -selectbackground  -takefocus
+-borderwidth       -selectborderwidth -xscrollcommand
+-relief            -selectforeground  -yscrollcommand
+.EE
+.SH "WIDGET-SPECIFIC OPTIONS"
+.TP
+.B -closeenough \fIdist\fP
+Specifies a floating-point value indicating how close the mouse cursor
+must be to an item before it is considered to be ``inside'' the item.
+Defaults to 1.0.
+.TP
+.B -confine \fIboolean\fP
+Specifies a boolean value that indicates whether or not it should be
+allowable to set the canvas's view outside the region defined by the
+\f5scrollregion\fR option.
+Defaults to true, which means that the view will
+be constrained within the scroll region.
+.TP
+.B -height \fIdist\fP
+Specifies a desired window height that the canvas widget should request from
+its geometry manager.  The value may be specified in any
+of the forms described in the COORDINATES section below.
+.TP
+.B -scrollregion \fIlist\fP
+Specifies a list with four \fIdist\fP coordinates describing the left, top, right, and
+bottom coordinates of a rectangular region.
+This region is used for scrolling purposes and is considered to be
+the boundary of the information in the canvas.
+Each of the coordinates may be specified
+in any of the forms given in the COORDINATES section below.
+.TP
+.B -width \fIdist\fP
+Specifies a desired window width that the canvas widget should request from
+its geometry manager.  The value may be specified in any
+of the forms described in the COORDINATES section below.
+.br
+.TP
+.B -xscrollincrement \fIdist\fP
+Specifies an increment for horizontal scrolling, in any of the usual forms
+permitted for screen distances.  If the value of this option is greater
+than zero, the horizontal view in the window will be constrained so that
+the canvas x coordinate at the left edge of the window is always an even
+multiple of \f5xscrollicrement\fR;  furthermore, the units for scrolling
+(e.g., the change in view when the left and right arrows of a scrollbar
+are selected) will also be \f5xscrollicrement\fR.  If the value of
+this option is less than or equal to zero, then horizontal scrolling
+is unconstrained.
+.TP
+.B -yscrollincrement \fIdist\fP
+Specifies an increment for vertical scrolling, in any of the usual forms
+permitted for screen distances.  If the value of this option is greater
+than zero, the vertical view in the window will be constrained so that
+the canvas y coordinate at the top edge of the window is always an even
+multiple of \f5yscrollicrement\fR;  furthermore, the units for scrolling
+(e.g., the change in view when the top and bottom arrows of a scrollbar
+are selected) will also be \f5yscrollicrement\fR.  If the value of
+this option is less than or equal to zero, then vertical scrolling
+is unconstrained.
+.TP
+.B -buffer \fIwhat\fP
+Specifies how much of the canvas region will be
+backed by an offscreen bitmap buffer.
+\fIWhat\fP can be one of \f5all\fP (the entire scroll region
+will be buffered), \f5visible\fP (only the visible area), \f5none\fP
+(no buffering) or \f5auto\fP (equivalent to either \f5none\fP
+or \f5visible\fP depending on whether the canvas is packed inside
+another canvas or not).
+
+.SH INTRODUCTION
+The \f5canvas\fR command creates a new window (given
+by the \fIpathName\fR argument) and makes it into a canvas widget.
+Additional options, described above, may be specified on the
+command line
+to configure aspects of the canvas such as its colours and 3-D relief.
+The \f5canvas\fR command returns its
+\fIpathName\fR argument.  At the time this command is invoked,
+there must not exist a window named \fIpathName\fR.
+.PP
+Canvas widgets implement structured graphics.
+A canvas displays any number of \fIitems\fR, which may be things like
+rectangles, circles, lines, and text.
+Items may be manipulated (e.g. moved or re-coloured) and commands may
+be associated with items in much the same way that the \f5bind\fR
+command allows commands to be bound to widgets.  For example,
+a particular command may be associated with the <Button-1> event
+so that the command is invoked whenever button 1 is pressed with
+the mouse cursor over an item.
+This means that items in a canvas can have behaviours defined by
+the Tk scripts bound to them.
+
+.SH "DISPLAY LIST"
+The items in a canvas are ordered for purposes of display,
+with the first item in the display list being displayed
+first, followed by the next item in the list, and so on.
+Items later in the display list obscure those that are
+earlier in the display list and are sometimes referred to
+as being ``on top'' of earlier items.
+When a new item is created it is placed at the end of the
+display list, on top of everything else.
+Widget commands may be used to re-arrange the order of the
+display list.
+
+.SH "ITEM IDS AND TAGS"
+Items in a canvas widget may be named in either of two ways:
+by id or by tag.
+Each item has a unique identifying number which is assigned to
+that item when it is created.  The id of an item never changes
+and id numbers are never re-used within the lifetime of a
+canvas widget.
+.PP
+Each item may also have any number of \fItags\fR associated
+with it.  A tag is just a string of characters, and it may
+take any form except that of an integer.
+For example, ``x123'' is OK but ``123'' isn't.
+The same tag may be associated with many different items.
+This is commonly done to group items in various interesting
+ways;  for example, all selected items might be given the
+tag ``selected''.
+.PP
+The tag \f5all\fR is implicitly associated with every item
+in the canvas;  it may be used to invoke operations on
+all the items in the canvas.
+.PP
+The tag \f5current\fR is managed automatically by Tk;
+it applies to the \fIcurrent item\fR, which is the
+topmost item whose drawn area covers the position of
+the mouse cursor.
+If the mouse is not in the canvas widget or is not over
+an item, then no item has the \f5current\fR tag.
+.PP
+When specifying items in canvas widget commands, if the
+specifier is an integer then it is assumed to refer to
+the single item with that id.
+If the specifier is not an integer, then it is assumed to
+refer to all of the items in the canvas that have a tag
+matching the specifier.
+The symbol \fItagOrId\fR is used below to indicate that
+an argument specifies either an id that selects a single
+item or a tag that selects zero or more items.
+Some widget commands only operate on a single item at a
+time;  if \fItagOrId\fR is specified in a way that
+names multiple items, then the normal behaviour is for
+the command to use the first (lowest) of these items in
+the display list that is suitable for the command.
+Exceptions are noted in the widget command descriptions
+below.
+.SH COORDINATES
+All coordinates related to canvases are stored as fixed-point
+numbers.
+Coordinates and distances are specified as documented
+in the
+.I dist
+section of 
+.IR types (9).
+.SH TRANSFORMATIONS
+Normally the origin of the canvas coordinate system is at the
+upper-left corner of the window containing the canvas.
+It is possible to adjust the origin of the canvas
+coordinate system relative to the origin of the window using the
+\f5xview\fR and \f5yview\fR widget commands;  this is typically used
+for scrolling.
+Canvases do not support scaling or rotation of the canvas coordinate
+system relative to the window coordinate system.
+.PP
+Individual items may be moved or scaled using widget commands
+described below, but they may not be rotated.
+
+.SH INDICES
+Text items support the notion of an \fIindex\fR for identifying
+particular positions within the item.
+Indices are used for commands such as inserting text, deleting
+a range of characters, and setting the insertion cursor position.
+An index may be specified in any of a number of ways, and
+different types of items may support different forms for
+specifying indices.
+Text items support the following forms for an index.
+Note that it is possible to refer to the character just after
+the last one in the text item;  this is necessary for such
+tasks as inserting new text at the end of the item.
+.TP 10
+\fInumber\fR
+A decimal number giving the position of the desired character
+within the text item.
+0 refers to the first character, 1 to the next character, and
+so on.
+A number less than 0 is treated as if it were zero, and a
+number greater than the length of the text item is treated
+as if it were equal to the length of the text item.
+.TP 10
+\f5end\fR
+Refers to the character just after the last one in the item
+(same as the number of characters in the item).
+.TP 10
+\f5insert\fR
+Refers to the character just before which the insertion cursor
+is drawn in this item.
+.TP 10
+\f5sel.first\fR
+Refers to the first selected character in the item.
+If the selection isn't in this item then this form is illegal.
+.TP 10
+\f5sel.last\fR
+Refers to the last selected character in the item.
+If the selection isn't in this item then this form is illegal.
+.TP 10
+\f5@\fIx,y\fR
+Refers to the character at the point given by \fIx\fR and
+\fIy\fR, where \fIx\fR and \fIy\fR are specified in the coordinate
+system of the canvas.
+If \fIx\fR and \fIy\fR lie outside the coordinates covered by the
+text item, then they refer to the first or last character in the
+line that is closest to the given point.
+
+.SH "WIDGET COMMAND"
+The \f5canvas\fR command creates a new Tk command whose
+name is \fIpathName\fR.  This
+command may be used to invoke various
+operations on the widget.  It has the following general form:
+.RS
+.EX
+\fIpathName option \fR?\fIarg arg ...\fR?
+.EE
+.RE
+\fIOption\fR and the \fIarg\fRs
+determine the exact behaviour of the command.
+The following widget commands are possible for canvas widgets:
+.TP
+\fIpathName \f5addtag \fItag searchSpec \fR?\fIarg arg ...\fR?
+For each item that meets the constraints specified by
+\fIsearchSpec\fR and the \fIarg\fRs, add
+\fItag\fR to the list of tags associated with the item if it
+isn't already present on that list.
+It is possible that no items will satisfy the constraints
+given by \fIsearchSpec and \fIarg\fRs, in which case the
+command has no effect.
+This command returns an empty string as result.
+\fISearchSpec\fR and \fIarg\fR's may take any of the following
+forms:
+.RS
+.TP
+\f5above \fItagOrId\fR
+Selects the item just after (above) the one given by \fItagOrId\fR
+in the display list.
+If \fItagOrId\fR denotes more than one item, then the last (topmost)
+of these items in the display list is used.
+.TP
+\f5all\fR
+Selects all the items in the canvas.
+.TP
+\f5below \fItagOrId\fR
+Selects the item just before (below) the one given by \fItagOrId\fR
+in the display list.
+If \fItagOrId\fR denotes more than one item, then the first (lowest)
+of these items in the display list is used.
+.TP
+\f5closest \fIx y \fR?\fIhalo\fR? ?\fIstart\fR?
+Selects the item closest to the point given by \fIx\fR and \fIy\fR.
+If more than one item is at the same closest distance (e.g. two
+items overlap the point), then the top-most of these items (the
+last one in the display list) is used.
+If \fIhalo\fR is specified, then it must be a non-negative
+value.
+Any item closer than \fIhalo\fR to the point is considered to
+overlap it.
+The \fIstart\fR argument may be used to step circularly through
+all the closest items.
+If \fIstart\fR is specified, it names an item using a tag or id
+(if by tag, it selects the first item in the display list with
+the given tag).
+Instead of selecting the topmost closest item, this form will
+select the topmost closest item that is below \fIstart\fR in
+the display list;  if no such item exists, then the selection
+behaves as if the \fIstart\fR argument had not been specified.
+.TP
+\f5enclosed\fR \fIx1\fR \fIy1\fR \fIx2\fR \fIy2\fR
+Selects all the items completely enclosed within the rectangular
+region given by \fIx1\fR, \fIy1\fR, \fIx2\fR, and \fIy2\fR.
+\fIX1\fR must be no greater then \fIx2\fR and \fIy1\fR must be
+no greater than \fIy2\fR.
+.TP
+\f5overlapping\fR \fIx1\fR \fIy1\fR \fIx2\fR \fIy2\fR
+Selects all the items that overlap or are enclosed within the
+rectangular region given by \fIx1\fR, \fIy1\fR, \fIx2\fR,
+and \fIy2\fR.
+\fIX1\fR must be no greater then \fIx2\fR and \fIy1\fR must be
+no greater than \fIy2\fR.
+.TP
+\f5withtag \fItagOrId\fR
+Selects all the items given by \fItagOrId\fR.
+.RE
+.TP
+\fIpathName \f5bbox \fItagOrId\fR ?\fItagOrId tagOrId ...\fR?
+Returns a list with four elements giving an approximate bounding box
+for all the items named by the \fItagOrId\fR arguments.
+The list has the form ``\fIx1 y1 x2 y2\fR'' such that the drawn
+areas of all the named elements are within the region bounded by
+\fIx1\fR on the left, \fIx2\fR on the right, \fIy1\fR on the top,
+and \fIy2\fR on the bottom.
+The return value may overestimate the actual bounding box by
+a few pixels.
+If no items match any of the \fItagOrId\fR arguments or if the
+matching items have empty bounding boxes (i.e. they have nothing
+to display)
+then an empty string is returned.
+.TP
+\fIpathName \f5bind \fItagOrId\fR ?\fIsequence\fR? ?\fIcommand\fR?
+This command associates \fIcommand\fR with all the items given by
+\fItagOrId\fR such that whenever the event sequence given by
+\fIsequence\fR occurs for one of the items the command will
+be invoked.
+This widget command is similar to the \f5bind\fR command except that
+it operates on items in a canvas rather than entire widgets.
+See the \f5bind\fR manual entry for complete details
+on the syntax of \fIsequence\fR and the substitutions performed
+on \fIcommand\fR before invoking it.
+If all arguments are specified then a new binding is created, replacing
+any existing binding for the same \fIsequence\fR and \fItagOrId\fR
+(if the first character of \fIcommand\fR is ``+'' then \fIcommand\fR
+augments an existing binding rather than replacing it).
+In this case the return value is an empty string.
+If \fIcommand\fR is omitted then the command returns the \fIcommand\fR
+associated with \fItagOrId\fR and \fIsequence\fR (an error occurs
+if there is no such binding).
+If both \fIcommand\fR and \fIsequence\fR are omitted then the command
+returns a list of all the sequences for which bindings have been
+defined for \fItagOrId\fR.
+.RS
+.PP
+The only events for which bindings may be specified are those related
+to the mouse and keyboard, such as \f5Enter\fR, \f5Leave\fR,
+\f5ButtonPress\fR, \f5Motion\fR, and \f5KeyPress\fR.
+The handling of events in canvases uses the current item defined
+in ITEM IDS AND TAGS above.
+\f5Enter\fR and \f5Leave\fR events trigger for an item when it
+becomes the current item or ceases to be the current item;  note
+that these events are different than \f5Enter\fR and \f5Leave\fR
+events for windows.
+Mouse-related events are directed to the current item, if any.
+Keyboard-related events are directed to the focus item, if any
+(see the \f5focus\fR widget command below for more on this).
+.PP
+It is possible for multiple bindings to match a particular event.
+This could occur, for example, if one binding is associated with the
+item's id and another is associated with one of the item's tags.
+When this occurs, all of the matching bindings are invoked.
+A binding associated with the \f5all\fR tag is invoked first,
+followed by one binding for each of the item's tags (in order),
+followed by a binding associated with the item's id.
+If there are multiple matching bindings for a single tag,
+then only the most specific binding is invoked.
+A \f5continue\fR command in a binding script terminates that
+script, and a \f5break\fR command terminates that script
+and skips any remaining scripts for the event, just as for the
+\f5bind\fR command.
+.PP
+If bindings have been created for a canvas window using the \f5bind\fR
+command, then they are invoked in addition to bindings created for
+the canvas's items using the \f5bind\fR widget command.
+The bindings for items will be invoked before any of the bindings
+for the window as a whole.
+.RE
+.TP
+\fIpathName \f5canvasx \fIscreenx\fR ?\fIgridspacing\fR?
+Given a window x-coordinate in the canvas \fIscreenx\fR, this command returns
+the canvas x-coordinate that is displayed at that location.
+If \fIgridspacing\fR is specified, then the canvas coordinate is
+rounded to the nearest multiple of \fIgridspacing\fR units.
+.TP
+\fIpathName \f5canvasy \fIscreeny\fR ?\fIgridspacing\fR?
+Given a window y-coordinate in the canvas \fIscreeny\fR this command returns
+the canvas y-coordinate that is displayed at that location.
+If \fIgridspacing\fR is specified, then the canvas coordinate is
+rounded to the nearest multiple of \fIgridspacing\fR units.
+.TP
+\fIpathName \f5cget\fR \fIoption\fR
+Returns the current value of the configuration option given
+by \fIoption\fR.
+\fIOption\fR may have any of the values accepted by the \f5canvas\fR
+command.
+.TP
+\fIpathName \f5configure ?\fIoption\fR? ?\fIvalue\fR? ?\fIoption value ...\fR?
+Query or modify the configuration options of the widget.
+If no \fIoption\fR is specified, returns a list of all of
+the available options for \fIpathName\fR.  If
+one or more \fIoption-value\fR pairs are specified, then the command
+modifies the given widget option(s) to have the given value(s);  in
+this case the command returns an empty string.
+\fIOption\fR may have any of the values accepted by the \f5canvas\fR
+command.
+.TP
+\fIpathName\fR \f5coords \fItagOrId \fR?\fIx0 y0 ...\fR?
+Query or modify the coordinates that define an item.
+If no coordinates are specified, this command returns a list
+whose elements are the coordinates of the item named by
+\fItagOrId\fR.
+If coordinates are specified, then they replace the current
+coordinates for the named item.
+If \fItagOrId\fR refers to multiple items, then
+the first one in the display list is used.
+.TP
+\fIpathName \f5create \fItype x y \fR?\fIx y ...\fR? ?\fIoption value ...\fR?
+Create a new item in \fIpathName\fR of type \fItype\fR.
+The exact format of the arguments after \f5type\fR depends
+on \f5type\fR, but usually they consist of the coordinates for
+one or more points, followed by specifications for zero or
+more item options.
+See the subsections on individual item types below for more
+on the syntax of this command.
+This command returns the id for the new item.
+.TP
+\fIpathName \f5dchars \fItagOrId first \fR?\fIlast\fR?
+For each item given by \fItagOrId\fR, delete the characters
+in the range given by \fIfirst\fR and \fIlast\fR,
+inclusive.
+If some of the items given by \fItagOrId\fR don't support
+text operations, then they are ignored.
+\fIFirst\fR and \fIlast\fR are indices of characters
+within the item(s) as described in INDICES above.
+If \fIlast\fR is omitted, it defaults to \fIfirst\fR.
+This command returns an empty string.
+.TP
+\fIpathName \f5delete \fR?\fItagOrId tagOrId ...\fR?
+Delete each of the items given by each \fItagOrId\fR, and return
+an empty string.
+.TP
+\fIpathName \f5dtag \fItagOrId \fR?\fItagToDelete\fR?
+For each of the items given by \fItagOrId\fR, delete the
+tag given by \fItagToDelete\fR from the list of those
+associated with the item.
+If an item doesn't have the tag \fItagToDelete\fR then
+the item is unaffected by the command.
+If \fItagToDelete\fR is omitted then it defaults to \fItagOrId\fR.
+This command returns an empty string.
+.TP
+\fIpathName \f5find \fIsearchCommand \fR?\fIarg arg ...\fR?
+This command returns a list consisting of all the items that
+meet the constraints specified by \fIsearchCommand\fR and
+\fIarg\fR's.
+\fISearchCommand\fR and \fIargs\fR have any of the forms
+accepted by the \f5addtag\fR command.
+If \fIsearchCommand\fR is \f5enclosed\fR, \f5overlapping\fR,
+or \f5all\fR, the items are returned in display-list order,
+i.e. bottommost first.
+.TP
+\fIpathName \f5focus \fR?\fItagOrId\fR?
+Set the keyboard focus for the canvas widget to the item given by
+\fItagOrId\fR.
+If \fItagOrId\fR refers to several items, then the focus is set
+to the first such item in the display list that supports the
+insertion cursor.
+If \fItagOrId\fR doesn't refer to any items, or if none of them
+support the insertion cursor, then the focus isn't changed.
+If \fItagOrId\fR is an empty
+string, then the focus item is reset so that no item has the focus.
+If \fItagOrId\fR is not specified then the command returns the
+id for the item that currently has the focus, or an empty string
+if no item has the focus.
+.RS
+.PP
+Once the focus has been set to an item, the item will display
+the insertion cursor and all keyboard events will be directed
+to that item.
+The focus item within a canvas and the focus window on the
+screen (set with the \f5focus\fR command) are totally independent:
+a given item doesn't actually have the input focus unless (a)
+its canvas is the focus window and (b) the item is the focus item
+within the canvas.
+In most cases it is advisable to follow the \f5focus\fR widget
+command with the \f5focus\fR command to set the focus window to
+the canvas (if it wasn't there already).
+.RE
+.TP
+\fIpathName \f5gettags\fR \fItagOrId\fR
+Return a list whose elements are the tags associated with the
+item given by \fItagOrId\fR.
+If \fItagOrId\fR refers to more than one item, then the tags
+are returned from the first such item in the display list.
+If \fItagOrId\fR doesn't refer to any items, then an error is returned. If the item
+contains no tags, then an empty string is returned.
+.TP
+\fIpathName \f5grab\fR \fIwhat\fR \fItagOrId\fR
+Does for canvas widgets what \fIgrab\fR(9) does for
+normal tk widgets: mouse events will only be delivered
+to \fItagOrId\fR. If \fItagOrId\fR refers to more than one item,
+then the first such item in the display list is grabbed.
+\fIWhat\fR is as described in
+.IR grab (9).
+.PP
+Note that the canvas grab item, as set by this command,
+and the tk grab item, as set by
+.IR grab (9)
+are totally independent; a canvas item doesn't actually grab
+the mouse unless a) the canvas itself has grabbed the mouse
+or b) the mouse events are being delivered to the canvas as
+a matter of course.
+.TP
+\fIpathName \f5icursor \fItagOrId index\fR
+Set the position of the insertion cursor for the item(s)
+given by \fItagOrId\fR
+to just before the character whose position is given by \fIindex\fR.
+If some or all of the items given by \fItagOrId\fR don't support
+an insertion cursor then this command has no effect on them.
+See INDICES above for a description of the
+legal forms for \fIindex\fR.
+Note:  the insertion cursor is only displayed in an item if
+that item currently has the keyboard focus (see the widget
+command \f5focus\fR, below), but the cursor position may
+be set even when the item doesn't have the focus.
+This command returns an empty string.
+.TP
+\fIpathName \f5index \fItagOrId index\fR
+This command returns a decimal string giving the numerical index
+within \fItagOrId\fR corresponding to \fIindex\fR.
+\fIIndex\fR gives a textual description of the desired position
+as described in INDICES above.
+The return value is guaranteed to lie between 0 and the number
+of characters within the item, inclusive.
+If \fItagOrId\fR refers to multiple items, then the index
+is processed in the first of these items that supports indexing
+operations (in display list order).
+.TP
+\fIpathName \f5insert \fItagOrId beforeThis string\fR
+For each of the items given by \fItagOrId\fR, if the item supports
+text insertion then \fIstring\fR is inserted into the item's
+text just before the character whose index is \fIbeforeThis\fR.
+See INDICES above for information about the forms allowed
+for \fIbeforeThis\fR.
+This command returns an empty string.
+.TP
+\fIpathName \f5itemcget\fR \fItagOrId\fR \fIoption\fR
+Returns the current value of the configuration option for the
+item given by \fItagOrId\fR whose name is \fIoption\fR.
+This command is similar to the \f5cget\fR widget command except that
+it applies to a particular item rather than the widget as a whole.
+\fIOption\fR may have any of the values accepted by the \f5create\fR
+widget command when the item was created.
+If \fItagOrId\fR is a tag that refers to more than one item,
+the first (lowest) such item is used.
+.TP
+\fIpathName \f5itemconfigure \fItagOrId\fR ?\fIoption\fR? ?\fIvalue\fR? ?\fIoption value ...\fR?
+This command is similar to the \f5configure\fR widget command except
+that it modifies item-specific options for the items given by
+\fItagOrId\fR instead of modifying options for the overall
+canvas widget.
+If
+one or more \fIoption-value\fR pairs are specified, then the command
+modifies the given widget option(s) to have the given value(s) in
+each of the items given by \fItagOrId\fR;  in
+this case the command returns an empty string.
+The \fIoption\fRs and \fIvalue\fRs are the same as those permissible
+in the \f5create\fR widget command when the item(s) were created;
+see the sections describing individual item types below for details
+on the legal options.
+.TP
+\fIpathName \f5lower \fItagOrId \fR?\fIbelowThis\fR?
+Move all of the items given by \fItagOrId\fR to a new position
+in the display list just before the item given by \fIbelowThis\fR.
+If \fItagOrId\fR refers to more than one item then all are moved
+but the relative order of the moved items will not be changed.
+\fIBelowThis\fR is a tag or id;  if it refers to more than one
+item then the first (lowest) of these items in the display list is used
+as the destination location for the moved items.
+This command returns an empty string.
+.TP
+\fIpathName \f5move \fItagOrId xAmount yAmount\fR
+Move each of the items given by \fItagOrId\fR in the canvas coordinate
+space by adding \fIxAmount\fR to the x-coordinate of each point
+associated with the item and \fIyAmount\fR to the y-coordinate of
+each point associated with the item.
+This command returns an empty string.
+.TP
+\fIpathName \f5raise \fItagOrId \fR?\fIaboveThis\fR?
+Move all of the items given by \fItagOrId\fR to a new position
+in the display list just after the item given by \fIaboveThis\fR.
+If \fItagOrId\fR refers to more than one item then all are moved
+but the relative order of the moved items will not be changed.
+\fIAboveThis\fR is a tag or id;  if it refers to more than one
+item then the last (topmost) of these items in the display list is used
+as the destination location for the moved items.
+This command returns an empty string.
+.TP
+\fIpathName \f5scale \fItagOrId xOrigin yOrigin xScale yScale\fR
+Rescale all of the items given by \fItagOrId\fR in canvas coordinate
+space.
+\fIXOrigin\fR and \fIyOrigin\fR identify the origin for the scaling
+operation and \fIxScale\fR and \fIyScale\fR identify the scale
+factors for x- and y-coordinates, respectively (a scale factor of
+1.0 implies no change to that coordinate).
+For each of the points defining each item, the x-coordinate is
+adjusted to change the distance from \fIxOrigin\fR by a factor
+of \fIxScale\fR.
+Similarly, each y-coordinate is adjusted to change the distance
+from \fIyOrigin\fR by a factor of \fIyScale\fR.
+This command returns an empty string.
+.TP
+\fIpathName \f5screenx \fIcanvasx\fR
+Given an x-coordinate \fIcanvasx\fR in the canvas, this command returns
+the equivalent screen x-coordinate.
+.TP
+\fIpathName \f5screeny \fIcanvasy\fR
+Given an x-coordinate \fIcanvasy\fR in the canvas, this command returns
+the equivalent screen y-coordinate.
+.TP
+\fIpathName \f5see \fIx1 y1\fR ?\fIx2 y2\fR?
+Adjusts the view in the window such that, if possible
+the point [\fIx1\fR, \fIy1\fR] (and, if given, the point
+[\fIx2\fR, \fIy2\fR]) are made visible.
+.TP
+\fIpathName \f5select \fIoption\fR ?\fItagOrId arg\fR?
+Manipulates the selection in one of several ways, depending on
+\fIoption\fR.
+The command may take any of the forms described below.
+In all of the descriptions below, \fItagOrId\fR must refer to
+an item that supports indexing and selection;  if it refers to
+multiple items then the first of
+these that supports indexing and the selection is used.
+\fIIndex\fR gives a textual description of a position
+within \fItagOrId\fR, as described in INDICES above.
+.RS
+.TP
+\fIpathName \f5select adjust \fItagOrId index\fR
+Locate the end of the selection in \fItagOrId\fR nearest
+to the character given by \fIindex\fR, and adjust that
+end of the selection to be at \fIindex\fR (i.e. including
+but not going beyond \fIindex\fR).
+The other end of the selection is made the anchor point
+for future \f5select to\fR commands.
+If the selection isn't currently in \fItagOrId\fR then
+this command behaves the same as the \f5select to\fR widget
+command.
+Returns an empty string.
+.TP
+\fIpathName \f5select clear\fR
+Clear the selection if it is in this widget.
+If the selection isn't in this widget then the command
+has no effect.
+Returns an empty string.
+.TP
+\fIpathName \f5select from \fItagOrId index\fR
+Set the selection anchor point for the widget to be just
+before the character
+given by \fIindex\fR in the item given by \fItagOrId\fR.
+This command doesn't change the selection;  it just sets
+the fixed end of the selection for future \f5select to\fR
+commands.
+Returns an empty string.
+.TP
+\fIpathName \f5select item\fR
+Returns the id of the selected item, if the selection is in an
+item in this canvas.
+If the selection is not in this canvas then an empty string
+is returned.
+.TP
+\fIpathName \f5select to \fItagOrId index\fR
+Set the selection to consist of those characters of \fItagOrId\fR
+between the selection anchor point and
+\fIindex\fR.
+The new selection will include the character given by \fIindex\fR;
+it will include the character given by the anchor point only if
+\fIindex\fR is greater than or equal to the anchor point.
+The anchor point is determined by the most recent \f5select adjust\fR
+or \f5select from\fR command for this widget.
+If the selection anchor point for the widget isn't currently in
+\fItagOrId\fR, then it is set to the same character given
+by \fIindex\fR.
+Returns an empty string.
+.RE
+.TP
+\fIpathName \f5type\fI tagOrId\fR
+Returns the type of the item given by \fItagOrId\fR, such as
+\f5rectangle\fR or \f5text\fR.
+If \fItagOrId\fR refers to more than one item, then the type
+of the first item in the display list is returned.
+If \fItagOrId\fR doesn't refer to any items at all then
+an empty string is returned.
+.TP
+\fIpathName \f5xview  \fR?\fIargs\fR?
+This command is used to query and change the horizontal position of the
+information displayed in the canvas's window.
+It can take any of the following forms:
+.RS
+.TP
+\fIpathName \f5xview\fR
+Returns a list containing two elements.
+Each element is a real fraction between 0 and 1;  together they describe
+the horizontal span that is visible in the window.
+For example, if the first element is .2 and the second element is .6,
+20% of the canvas's area (as defined by the \f5-scrollregion\fR option)
+is off-screen to the left, the middle 40% is visible
+in the window, and 40% of the canvas is off-screen to the right.
+These are the same values passed to scrollbars via the \f5-xscrollcommand\fR
+option.
+.TP
+\fIpathName \f5xview moveto\fI fraction\fR
+Adjusts the view in the window so that \fIfraction\fR of the
+total width of the canvas is off-screen to the left.
+\fIFraction\fR must be a fraction between 0 and 1.
+.TP
+\fIpathName \f5xview scroll \fInumber what\fR
+This command shifts the view in the window left or right according to
+\fInumber\fR and \fIwhat\fR.
+\fINumber\fR must be an integer.
+\fIWhat\fR must be either \f5units\fR or \f5pages\fR.
+If \fIwhat\fR is \f5units\fR, the view adjusts left or right in units
+of the \f5xscrollicrement\fR option, if it is greater than zero,
+or in units of one-tenth the window's width otherwise.
+If \fIwhat is \f5pages\fR then the view
+adjusts in units of nine-tenths the window's width.
+If \fInumber\fR is negative then information farther to the left
+becomes visible;  if it is positive then information farther to the right
+becomes visible.
+.RE
+.TP
+\fIpathName \f5yview \fI?args\fR?
+This command is used to query and change the vertical position of the
+information displayed in the canvas's window.
+It can take any of the following forms:
+.RS
+.TP
+\fIpathName \f5yview\fR
+Returns a list containing two elements.
+Each element is a real fraction between 0 and 1;  together they describe
+the vertical span that is visible in the window.
+For example, if the first element is .6 and the second element is 1.0,
+the lowest 40% of the canvas's area (as defined by the \f5-scrollregion\fR
+option) is visible in the window.
+These are the same values passed to scrollbars via the \f5-yscrollcommand\fR
+option.
+.TP
+\fIpathName \f5yview moveto\fI fraction\fR
+Adjusts the view in the window so that \fIfraction\fR of the canvas's
+area is off-screen to the top.
+\fIFraction\fR is a fraction between 0 and 1.
+.TP
+\fIpathName \f5yview scroll \fInumber what\fR
+This command adjusts the view in the window up or down according to
+\fInumber\fR and \fIwhat\fR.
+\fINumber\fR must be an integer.
+\fIWhat\fR must be either \f5units\fR or \f5pages\fR.
+If \fIwhat\fR is \f5units\fR, the view adjusts up or down in units
+of the \f5yscrollicrement\fR option, if it is greater than zero,
+or in units of one-tenth the window's height otherwise.
+If \fIwhat\fR is \f5pages\fR then
+the view adjusts in units of nine-tenths the window's height.
+If \fInumber\fR is negative then higher information becomes
+visible;  if it is positive then lower information
+becomes visible.
+.RE
+
+.SH "OVERVIEW OF ITEM TYPES"
+The sections below describe the various types of items supported
+by canvas widgets.  Each item type is characterized by two things:
+first, the form of the \f5create\fR command used to create
+instances of the type;  and second, a set of configuration options
+for items of that type, which may be used in the
+\f5create\fR and \f5itemconfigure\fR widget commands.
+Most items don't support indexing or selection or the commands
+related to them, such as \f5index\fR and \f5insert\fR.
+Where items do support these facilities, it is noted explicitly
+in the descriptions below (at present, only text items provide
+this support).
+
+.SH "ARC ITEMS"
+Items of type \f5arc\fR appear on the display as arc-shaped regions.
+An arc is a section of an oval delimited by two angles (specified
+by the \f5-start\fR and \f5-extent\fR options) and displayed in
+one of several ways (specified by the \f5-style\fR option).
+Arcs are created with widget commands of the following form:
+.RS
+.EX
+\fIpathName \f5create arc \fIx1 y1 x2 y2 \fR?\fIoption value option value ...\fR?
+.EE
+.RE
+The arguments \fIx1\fR, \fIy1\fR, \fIx2\fR, and \fIy2\fR give
+the coordinates of two diagonally opposite corners of a
+rectangular region enclosing the oval that defines the arc.
+After the coordinates there may be any number of \fIoption\fR-\fIvalue\fR
+pairs, each of which sets one of the configuration options
+for the item.  These same \fIoption\fR-\fIvalue\fR pairs may be
+used in \f5itemconfigure\fR widget commands to change the item's
+configuration.
+The following options are supported for arcs:
+.TP
+\f5-extent \fIdegrees\fR
+Specifies the size of the angular range occupied by the arc.
+The arc's range extends for \fIdegrees\fR degrees counter-clockwise
+from the starting angle given by the \f5-start\fR option.
+\fIDegrees\fR may be negative.
+If it is greater than 360 or less than -360, then \fIdegrees\fR
+modulo 360 is used as the extent.
+.TP
+\f5-fill \fIcolour\fR
+Fill the region of the arc with \fIcolour\fR.
+If \fIcolour\fR is an empty string (the default), then
+the arc will not be filled.
+.TP
+\f5-outline \fIcolour\fR
+\fIColour\fR specifies a colour to use for drawing the arc's
+outline.
+This option defaults to \f5black\fR.  If \fIcolour\fR is specified
+as an empty string then no outline is drawn for the arc.
+.TP
+\f5-start \fIdegrees\fR
+Specifies the beginning of the angular range occupied by the
+arc.
+\fIDegrees\fR is given in units of degrees measured counter-clockwise
+from the 3-o'clock position;  it may be either positive or negative.
+.TP
+\f5-stipple \fIbitmap\fR
+Indicates that the arc should be filled in a stipple pattern;
+\fIbitmap\fR specifies the stipple pattern to use.
+If the \f5-fill\fR option hasn't been specified then this option
+has no effect.
+If \fIbitmap\fR is an empty string (the default), then filling is done
+in a solid fashion.
+The results are undefined if \fIbitmap\fR is not
+a 1-bit image.
+.TP
+\f5-style \fItype\fR
+Specifies how to draw the arc.  If \fItype\fR is \f5pieslice\fR
+(the default) then the arc's region is defined by a section
+of the oval's perimeter plus two line segments, one between the center
+of the oval and each end of the perimeter section.
+If \fItype\fR is \f5chord\fR then the arc's region is defined
+by a section of the oval's perimeter plus a single line segment
+connecting the two end points of the perimeter section.
+This type is not implemented at the moment. It behaves as \f5arc\fR.
+If \fItype\fR is \f5arc\fR then the arc's region consists of
+a section of the perimeter alone.
+In this last case the \f5-fill\fR option is ignored.
+.TP
+\f5-tags \fItagList\fR
+Specifies a set of tags to apply to the item.
+\fITagList\fR consists of a list of tag names, which replace any
+existing tags for the item.
+\fITagList\fR may be an empty list.
+.TP
+\f5-width \fIoutlineWidth\fR
+Specifies the width of the outline to be drawn around
+the arc's region, in any of the forms described in the COORDINATES
+section above.
+If the \f5-outline\fR option has been specified as an empty string
+then this option has no effect.
+Wide outlines will be drawn centered on the edges of the arc's region.
+This option defaults to 1.0.
+
+.SH "BITMAP ITEMS"
+Items of type \f5bitmap\fR appear on the display as images with
+two colours, foreground and background.
+Bitmaps are created with widget commands of the following form:
+.RS
+.EX
+\fIpathName \f5create bitmap \fIx y \fR?\fIoption value option value ...\fR?
+.EE
+.RE
+The arguments \fIx\fR and \fIy\fR specify the coordinates of a
+point used to position the bitmap on the display (see the \f5-anchor\fR
+option below for more information on how bitmaps are displayed).
+After the coordinates there may be any number of \fIoption\fR-\fIvalue\fR
+pairs, each of which sets one of the configuration options
+for the item.  These same \fIoption\fR-\fIvalue\fR pairs may be
+used in \f5itemconfigure\fR widget commands to change the item's
+configuration.
+The following options are supported for bitmaps:
+.TP
+\f5-anchor \fIanchorPos\fR
+\fIAnchorPos\fR tells how to position the bitmap relative to the
+positioning point for the item.  For example, if \fIanchorPos\fR
+is \f5center\fR then the bitmap is centered on the point;  if
+\fIanchorPos\fR is \f5n\fR then the bitmap will be drawn so that
+its top center point is at the positioning point.
+This option defaults to \f5center\fR.
+.TP
+\f5-bitmap \fIbitmap\fR
+Specifies the bitmap to display in the item.
+.TP
+\f5-tags \fItagList\fR
+Specifies a set of tags to apply to the item.
+\fITagList\fR consists of a list of tag names, which replace any
+existing tags for the item.
+\fITagList\fR may be an empty list.
+
+.SH "IMAGE ITEMS"
+Items of type \f5image\fR are used to display images on a
+canvas.
+Images are created with widget commands of the following form:
+.RS
+.EX
+\fIpathName \f5create image \fIx y \fR?\fIoption value option value ...\fR?
+.EE
+.RE
+The arguments \fIx\fR and \fIy\fR specify the coordinates of a
+point used to position the image on the display (see the \f5-anchor\fR
+option below for more information).
+After the coordinates there may be any number of \fIoption\fR-\fIvalue\fR
+pairs, each of which sets one of the configuration options
+for the item.  These same \fIoption\fR-\fIvalue\fR pairs may be
+used in \f5itemconfigure\fR widget commands to change the item's
+configuration.
+The following options are supported for images:
+.TP
+\f5-anchor \fIanchorPos\fR
+\fIAnchorPos\fR tells how to position the image relative to the
+positioning point for the item.  For example, if \fIanchorPos\fR
+is \f5center\fR then the image is centered on the point;  if
+\fIanchorPos\fR is \f5n\fR then the image will be drawn so that
+its top center point is at the positioning point.
+This option defaults to \f5center\fR.
+.TP
+\f5-image \fIname\fR
+Specifies the name of the image to display in the item.
+This image must have been created previously with the
+\f5image create\fR command.
+.TP
+\f5-tags \fItagList\fR
+Specifies a set of tags to apply to the item.
+\fITagList\fR consists of a list of tag names, which replace any
+existing tags for the item;  it may be an empty list.
+
+.SH "LINE ITEMS"
+Items of type \f5line\fR appear on the display as one or more connected
+line segments or curves.
+Lines are created with widget commands of the following form:
+.RS
+.EX
+\fIpathName \f5create line \fIx1 y1... xn yn \fR?\fIoption value option value ...\fR?
+.EE
+.RE
+The arguments \fIx1\fR through \fIyn\fR give
+the coordinates for a series of two or more points that describe
+a series of connected line segments.
+After the coordinates there may be any number of \fIoption\fR-\fIvalue\fR
+pairs, each of which sets one of the configuration options
+for the item.  These same \fIoption\fR-\fIvalue\fR pairs may be
+used in \f5itemconfigure\fR widget commands to change the item's
+configuration.
+The following options are supported for lines:
+.TP
+\f5-arrow \fIwhere\fR
+Indicates whether or not arrowheads are to be drawn at one or both
+ends of the line.
+\fIWhere\fR must have one of the values \f5none\fR (for no arrowheads),
+\f5first\fR (for an arrowhead at the first point of the line),
+\f5last\fR (for an arrowhead at the last point of the line), or
+\f5both\fR (for arrowheads at both ends).
+This option defaults to \f5none\fR.
+.TP
+\f5-arrowshape \fIshape\fR
+This option indicates how to draw arrowheads.
+The \fIshape\fR argument must be a list with three elements, each
+specifying a distance in any of the forms described in
+the COORDINATES section above.
+The first element of the list gives the distance along the line
+from the neck of the arrowhead to its tip.
+The second element gives the distance along the line from the
+trailing points of the arrowhead to the tip, and the third
+element gives the distance from the outside edge of the line to the
+trailing points.
+If this option isn't specified then Tk picks a ``reasonable'' shape.
+.TP
+\f5-capstyle \fIstyle\fR
+Specifies the ways in which caps are to be drawn at the endpoints
+of the line.
+\fIStyle\fR may one of
+\f5butt\fR, \f5projecting\fR, or \f5round\fR.
+If this option isn't specified then it defaults to \f5butt\fR.
+Where arrowheads are drawn the cap style is ignored. Note that the first two options
+currently have the same effect.
+.TP
+\f5-fill \fIcolour\fR
+\fIColour\fR specifies a colour to use for drawing the line.  It may also be an
+empty string, in which case the line will be transparent.
+This option defaults to \f5black\fR.
+.TP
+\f5-smooth \fIboolean\fR
+\fIBoolean\fR indicates whether or not the line should be drawn as a curve.
+If so, the line is rendered as a set of Bezier splines: one spline
+is drawn for the first and second line segments, one for the second
+and third, and so on.  Straight-line segments can be generated within
+a curve by duplicating the end-points of the desired line segment.
+.TP
+\f5-stipple \fIbitmap\fR
+Indicates that the line should be filled in a stipple pattern;
+\fIbitmap\fR specifies the stipple pattern to use.
+If \fIbitmap\fR is an empty string (the default), then filling is
+done in a solid fashion.
+The results are undefined if \fIbitmap\fR is not
+a 1-bit image.
+.TP
+\f5-tags \fItagList\fR
+Specifies a set of tags to apply to the item.
+\fITagList\fR consists of a list of tag names, which replace any
+existing tags for the item.
+\fITagList\fR may be an empty list.
+.TP
+\f5-width \fIlineWidth\fR
+\fILineWidth\fR specifies the width of the line, in any of the forms
+described in the COORDINATES section above.
+Wide lines will be drawn centered on the path specified by the
+points.
+If this option isn't specified then it defaults to 1.0.
+
+.SH "OVAL ITEMS"
+Items of type \f5oval\fR appear as circular or oval regions on
+the display.  Each oval may have an outline, a fill, or
+both.  Ovals are created with widget commands of the
+following form:
+.RS
+.EX
+\fIpathName \f5create oval \fIx1 y1 x2 y2 \fR?\fIoption value option value ...\fR?
+.EE
+.RE
+The arguments \fIx1\fR, \fIy1\fR, \fIx2\fR, and \fIy2\fR give
+the coordinates of two diagonally opposite corners of a
+rectangular region enclosing the oval.
+The oval will include the top and left edges of the rectangle
+not the lower or right edges.
+If the region is square then the resulting oval is circular;
+otherwise it is elongated in shape.
+After the coordinates there may be any number of \fIoption\fR-\fIvalue\fR
+pairs, each of which sets one of the configuration options
+for the item.  These same \fIoption\fR-\fIvalue\fR pairs may be
+used in \f5itemconfigure\fR widget commands to change the item's
+configuration.
+The following options are supported for ovals:
+.TP
+\f5-fill \fIcolour\fR
+Fill the area of the oval with \fIcolour\fR.
+If \fIcolour\fR is an empty string (the default), then
+then the oval will not be filled.
+.TP
+\f5-outline \fIcolour\fR
+\fIColour\fR specifies a colour to use for drawing the oval's
+outline.
+This option defaults to \f5black\fR.
+If \fIcolour\fR is an empty string then no outline will be
+drawn for the oval.
+.TP
+\f5-stipple \fIbitmap\fR
+Indicates that the oval should be filled in a stipple pattern;
+\fIbitmap\fR specifies the stipple pattern to use.
+If the \f5-fill\fR option hasn't been specified then this option
+has no effect.
+If \fIbitmap\fR is an empty string (the default), then filling is done
+in a solid fashion.
+The results are undefined if \fIbitmap\fR is not
+a 1-bit image.
+.TP
+\f5-tags \fItagList\fR
+Specifies a set of tags to apply to the item.
+\fITagList\fR consists of a list of tag names, which replace any
+existing tags for the item.
+\fITagList\fR may be an empty list.
+.TP
+\f5-width \fIoutlineWidth\fR
+\fIoutlineWidth\fR specifies the width of the outline to be drawn around
+the oval, in any of the forms described in the COORDINATES section above.
+If the \f5-outline\fR option hasn't been specified then this option
+has no effect.
+Wide outlines are drawn centered on the oval path defined by
+\fIx1\fR, \fIy1\fR, \fIx2\fR, and \fIy2\fR.
+This option defaults to 1.0.
+
+.SH "POLYGON ITEMS"
+Items of type \f5polygon\fR appear as polygonal or curved filled regions
+on the display.
+Polygons are created with widget commands of the following form:
+.RS
+.EX
+\fIpathName \f5create polygon \fIx1 y1 ... xn yn \fR?\fIoption value option value ...\fR?
+.EE
+.RE
+The arguments \fIx1\fR through \fIyn\fR specify the coordinates for
+three or more points that define a closed polygon.
+The first and last points may be the same;  whether they are or not,
+Tk will draw the polygon as a closed polygon.
+After the coordinates there may be any number of \fIoption\fR-\fIvalue\fR
+pairs, each of which sets one of the configuration options
+for the item.  These same \fIoption\fR-\fIvalue\fR pairs may be
+used in \f5itemconfigure\fR widget commands to change the item's
+configuration.
+The following options are supported for polygons:
+.TP
+\f5-fill \fIcolour\fR
+\fIColour\fR specifies a colour to use for filling the area of the
+polygon.
+If \fIcolour\fR is an empty string then the polygon will be
+transparent.
+This option defaults to the empty string (transparent).
+.TP
+\f5-outline \fIcolour\fR
+\fIColour\fR specifies a colour to use for drawing the polygon's
+outline.
+If \fIcolour\fR is an empty string then no outline will be
+drawn for the polygon.
+This option defaults to black.
+.TP
+\f5-smooth \fIboolean\fR
+\fIBoolean\fP indicates whether or not the polygon should be drawn with a
+curved perimeter.
+If so, the outline of the polygon becomes a set of Bezier splines,
+one spline for the first and second line segments, one for the second
+and third, and so on.  Straight-line segments can be generated in a
+smoothed polygon by duplicating the end-points of the desired line segment.
+.TP
+\f5-stipple \fIbitmap\fR
+Indicates that the polygon should be filled in a stipple pattern;
+\fIbitmap\fR specifies the stipple pattern to use.
+If \fIbitmap\fR is an empty string (the default), then filling is
+done in a solid fashion.
+The results are undefined if \fIbitmap\fR is not
+a 1-bit image.
+.TP
+\f5-tags \fItagList\fR
+Specifies a set of tags to apply to the item.
+\fITagList\fR consists of a list of tag names, which replace any
+existing tags for the item.
+\fITagList\fR may be an empty list.
+.TP
+\f5-winding \fItype\fR
+Specifies the winding rule to use when filling the polygon.
+\fIType\fR can be either \f5nonzero\fR (the default)
+or \f5odd\fR
+See \f5fillpoly\fR in
+.IR draw-image(2)
+for an explanation.
+.TP
+\f5-width \fIoutlineWidth\fR
+\fIOutlineWidth\fR specifies the width of the outline to be drawn around
+the polygon, in any of the forms described in the COORDINATES section above.
+If the \f5-outline\fR option hasn't been specified then this option
+has no effect.  This option defaults to 1.0.
+.PP
+Polygon items are different from other items such as rectangles, ovals
+and arcs in that interior points are considered to be ``inside'' a
+polygon (e.g. for purposes of the \f5find closest\fR and
+\f5find overlapping\fR widget commands) even if it is not filled.
+For most other item types, an
+interior point is considered to be inside the item only if the item
+is filled or if it has neither a fill nor an outline.  If you would
+like an unfilled polygon whose interior points are not considered
+to be inside the polygon, use a line item instead.
+
+.SH "RECTANGLE ITEMS"
+Items of type \f5rectangle\fR appear as rectangular regions on
+the display.  Each rectangle may have an outline, a fill, or
+both.  Rectangles are created with widget commands of the
+following form:
+.RS
+.EX
+\fIpathName \f5create rectangle \fIx1 y1 x2 y2 \fR?\fIoption value option value ...\fR?
+.EE
+.RE
+The arguments \fIx1\fR, \fIy1\fR, \fIx2\fR, and \fIy2\fR give
+the coordinates of two diagonally opposite corners of the rectangle
+(the rectangle will include its upper and left edges but not
+its lower or right edges).
+After the coordinates there may be any number of \fIoption\fR-\fIvalue\fR
+pairs, each of which sets one of the configuration options
+for the item.  These same \fIoption\fR-\fIvalue\fR pairs may be
+used in \f5itemconfigure\fR widget commands to change the item's
+configuration.
+The following options are supported for rectangles:
+.TP
+\f5-fill \fIcolour\fR
+Fill the area of the rectangle with \fIcolour\fR.
+If \fIcolour\fR is an empty string (the default),
+then the rectangle will not be filled.
+.TP
+\f5-outline \fIcolour\fR
+Draw an outline around the edge of the rectangle in \fIcolour\fR.
+This option defaults to \f5black\fR.
+If \fIcolour\fR is an empty string then no outline will be
+drawn for the rectangle.
+.TP
+\f5-stipple \fIbitmap\fR
+Indicates that the rectangle should be filled in a stipple pattern;
+\fIbitmap\fR specifies the stipple pattern to use.
+If the \f5-fill\fR option hasn't been specified then this option
+has no effect.
+If \fIbitmap\fR is an empty string (the default), then filling
+is done in a solid fashion.
+The results are undefined if \fIbitmap\fR is not
+a 1-bit image.
+.TP
+\f5-tags \fItagList\fR
+Specifies a set of tags to apply to the item.
+\fITagList\fR consists of a list of tag names, which replace any
+existing tags for the item.
+\fITagList\fR may be an empty list.
+.TP
+\f5-width \fIoutlineWidth\fR
+\fIOutlineWidth\fR specifies the width of the outline to be drawn around
+the rectangle, in any of the forms described in the COORDINATES section above.
+If the \f5-outline\fR option hasn't been specified then this option
+has no effect.
+Wide outlines are drawn centered on the rectangular path
+defined by \fIx1\fR, \fIy1\fR, \fIx2\fR, and \fIy2\fR.
+This option defaults to 1.0.
+
+.SH "TEXT ITEMS"
+A text item displays a string of characters on the screen in one
+or more lines.
+Text items support indexing and selection, along with the
+following text-related canvas widget commands:  \f5dchars\fR,
+\f5focus\fR, \f5icursor\fR, \f5index\fR, \f5insert\fR,
+\f5select\fR.
+Text items are created with widget commands of the following
+form:
+.RS
+.EX
+\fIpathName \f5create text \fIx y \fR?\fIoption value option value ...\fR?
+.EE
+.RE
+The arguments \fIx\fR and \fIy\fR specify the coordinates of a
+point used to position the text on the display (see the options
+below for more information on how text is displayed).
+After the coordinates there may be any number of \fIoption\fR-\fIvalue\fR
+pairs, each of which sets one of the configuration options
+for the item.  These same \fIoption\fR-\fIvalue\fR pairs may be
+used in \f5itemconfigure\fR widget commands to change the item's
+configuration.
+The following options are supported for text items:
+.TP
+\f5-anchor \fIanchorPos\fR
+\fIAnchorPos\fR tells how to position the text relative to the
+positioning point for the text. For example, if \fIanchorPos\fR
+is \f5center\fR then the text is centered on the point;  if
+\fIanchorPos\fR is \f5n\fR then the text will be drawn such that
+the top center point of the rectangular region occupied by the
+text will be at the positioning point.
+This option defaults to \f5center\fR.
+.TP
+\f5-fill \fIcolour\fR
+\fIColour\fR specifies a colour to use for filling the text characters.
+If this option isn't specified then it defaults to \f5black\fR.
+.TP
+\f5-font \fIfont\fR
+Specifies the font to use for the text item.
+If this option isn't specified, it defaults to a system-dependent
+font.
+.TP
+\f5-justify \fIhow\fR
+Specifies how to justify the text within its bounding region.
+\fIHow\fR must be one of the values \f5left\fR, \f5right\fR,
+or \f5center\fR.
+This option will only matter if the text is displayed as multiple
+lines.
+If the option is omitted, it defaults to \f5left\fR.
+.TP
+\f5-stipple \fIbitmap\fR
+Indicates that the text should be drawn in a stippled pattern
+rather than solid;
+\fIbitmap\fR specifies the stipple pattern to use.
+If \fIbitmap\fR is an empty string (the default) then the text
+is drawn in a solid fashion.
+The results are undefined if \fIbitmap\fR is not
+a 1-bit image.
+.TP
+\f5-tags \fItagList\fR
+Specifies a set of tags to apply to the item.
+\fITagList\fR consists of a list of tag names, which replace any
+existing tags for the item.
+\fITagList\fR may be an empty list.
+.TP
+\f5-text \fIstring\fR
+\fIString\fR specifies the characters to be displayed in the text item.
+Newline characters cause line breaks.
+The characters in the item may also be changed with the
+\f5insert\fR and \f5delete\fR widget commands.
+This option defaults to an empty string.
+.TP
+\f5-width \fIlineLength\fR
+Specifies a maximum line length for the text, in any of the forms
+described in the COORDINATES section above.
+If this option is zero (the default) the text is broken into
+lines only at newline characters.
+However, if this option is non-zero then any line that would
+be longer than \fIlineLength\fR is broken just before a space
+character to make the line shorter than \fIlineLength\fR;  the
+space character is treated as if it were a newline
+character.
+
+.SH "WINDOW ITEMS"
+Items of type \f5window\fR cause a particular window to be displayed
+at a given position on the canvas.
+Window items are created with widget commands of the following form:
+.RS
+.EX
+\fIpathName \f5create window \fIx y \fR?\fIoption value option value ...\fR?
+.EE
+.RE
+The arguments \fIx\fR and \fIy\fR specify the coordinates of a
+point used to position the window on the display (see the \f5-anchor\fR
+option below for more information on how bitmaps are displayed).
+After the coordinates there may be any number of \fIoption\fR-\fIvalue\fR
+pairs, each of which sets one of the configuration options
+for the item.  These same \fIoption\fR-\fIvalue\fR pairs may be
+used in \f5itemconfigure\fR widget commands to change the item's
+configuration.
+The following options are supported for window items:
+.TP
+\f5-anchor \fIanchorPos\fR
+\fIAnchorPos\fR tells how to position the window relative to the
+positioning point for the item.  For example, if \fIanchorPos\fR
+is \f5center\fR then the window is centered on the point;  if
+\fIanchorPos\fR is \f5n\fR then the window will be drawn so that
+its top center point is at the positioning point.
+This option defaults to \f5center\fR.
+.TP
+\f5-height \fIdist\fR
+Specifies the height to assign to the item's window.
+\fIDist\fR may have any of the
+forms described in the COORDINATES section above.
+If this option isn't specified, or if it is specified as an empty
+string, then the window is given whatever height it requests internally.
+.TP
+\f5-tags \fItagList\fR
+Specifies a set of tags to apply to the item.
+\fITagList\fR consists of a list of tag names, which replace any
+existing tags for the item.
+\fITagList\fR may be an empty list.
+.TP
+\f5-width \fIdist\fR
+Specifies the width to assign to the item's window.
+\fIDist\fR may have any of the
+forms described in the COORDINATES section above.
+If this option isn't specified, or if it is specified as an empty
+string, then the window is given whatever width it requests internally.
+.TP
+\f5-window \fIpathName\fR
+Specifies the window to associate with this item.
+The window specified by \fIpathName\fR must either be a child of
+the canvas widget or a child of some ancestor of the canvas widget.
+\fIPathName\fR may not refer to a top-level window.
+
+.SH BINDINGS
+New canvases are not given any
+default behaviour.
+Use
+.IR bind (2)
+commands to give the canvas its behaviour.
+.SH CREDITS
+Tk's canvas widget is a blatant ripoff of ideas from Joel Bartlett's
+\fIezd\fR program.  \fIEzd\fR provides structured graphics in a Scheme
+environment and preceded canvases by a year or two.  Its simple
+mechanisms for placing and animating graphical objects inspired the
+functions of canvases.
+
+.SH SEE ALSO
+.IR options (9),
+.IR types (9)
--- /dev/null
+++ b/man/9/checkbutton
@@ -1,0 +1,193 @@
+.TH CHECKBUTTON 9
+.SH NAME
+checkbutton \- Create and manipulate checkbutton widgets
+.SH SYNOPSIS
+\f5checkbutton\fI pathname \fR?\fIoptions\fR?
+.SH STANDARD OPTIONS
+.EX
+-activebackground -disabledcolor      -justify
+-activeforeground -font               -relief
+-anchor           -foreground         -takefocus
+-background       -highlightcolor     -text
+-bitmap           -highlightthickness -underline
+-borderwidth      -image
+.EE
+.SH "WIDGET-SPECIFIC OPTIONS"
+.TP
+.B -command \fIcommand\fP
+Specifies a Tk command to associate with the button.  This command
+is typically invoked when mouse button 1 is released over the button
+window.  The button's global variable (\f5-variable\fR option) will
+be updated before the command is invoked.
+.TP
+.B -height \fIdist\fP
+Specifies a desired height for the button.
+If this option isn't specified, the button's desired height is computed
+from the size of the image or bitmap or text being displayed in it.
+.TP
+.B -indicatoron \fIboolean\fP
+Specifies whether or not the indicator should be drawn.
+If false, the \f5relief\fP option is ignored and the widget's
+relief is always \f5sunken\fP if the widget is selected
+and \f5raised\fP otherwise.
+.TP
+.B -offvalue \fIstring\fP
+Specifies value to store in the button's associated variable whenever
+this button is deselected.  Defaults to ``0''.
+.TP
+.B -onvalue \fIstring\fP
+Specifies value to store in the button's associated variable whenever
+this button is selected.  Defaults to ``1''.
+.TP
+.B -selectcolor \fIcolour\fP
+Specifies a background colour to use when the button is selected.
+If \f5indicatoron\fR is true then the colour applies to the indicator.
+If \f5indicatoron\fR is false, this colour is used as the background
+for the entire widget, in place of \f5background\fR or \f5activebackground\fR,
+whenever the widget is selected.
+If specified as an empty string then no special colour is used for
+displaying when the widget is selected.
+.ig
+.TP
+.B -selectimage \fIimage\fP
+Specifies an image to display (in place of the \f5image\fR option)
+when the checkbutton is selected.
+This option is ignored unless the \f5image\fR option has been
+specified.
+..
+.TP
+.B -state \fIstate\fP
+Specifies one of three states for the checkbutton:  \f5normal\fR, \f5active\fR,
+or \f5disabled\fR.  In normal state the checkbutton is displayed using the
+\f5foreground\fR and \f5background\fR options.  The active state is
+typically used when the pointer is over the checkbutton.  In active state
+the checkbutton is displayed using the \f5activeforeground\fR and
+\f5activebackground\fR options.  Disabled state means that the checkbutton
+should be insensitive:  the default bindings will refuse to activate
+the widget and will ignore mouse button presses.
+In this state the \f5disabledcolor\fR and
+\f5background\fR options determine how the checkbutton is displayed.
+.TP
+.B -variable \fIstring\fP
+Specifies name of global variable to set whenever this button is
+selected.  Changes in this variable also cause the button to select
+or deselect itself.
+.TP
+.B -width \fIdist\fP
+Specifies a desired width for the button.
+If this option isn't specified, the button's desired width is computed
+from the size of the image or bitmap or text being displayed in it.
+.SH DESCRIPTION
+The \f5checkbutton\fR command creates a new window (given by the
+\fIpathname\fR argument) and makes it into a checkbutton widget.
+Additional
+options, described above, may be specified on the command line
+to configure aspects of the checkbutton such as its colours, font,
+text, and initial relief.  The \f5checkbutton\fR command returns its
+\fIpathname\fR argument.  At the time this command is invoked,
+there must not exist a window named \fIpathname\fR.
+.PP
+A checkbutton is a widget
+that displays a textual string, bitmap or image
+and a square called an \fIindicator\fR.
+If text is displayed, it must all be in a single font, but it
+can occupy multiple lines on the screen (if it contains newlines) and
+one of the characters may optionally be underlined using the
+\f5underline\fR option.
+A checkbutton has
+all of the behaviour of a simple button, including the
+following: it can display itself in either of three different
+ways, according to the \f5state\fR option;
+it can be made to appear
+raised, sunken, or flat; and it invokes
+a Tk command whenever mouse button 1 is clicked over the
+checkbutton.
+.PP
+In addition, checkbuttons can be \fIselected\fR.
+If a checkbutton is selected then the indicator is normally
+drawn with a sunken relief and a tick (check) mark, and
+a Tk variable associated with the checkbutton is set to the
+.B onvalue
+(normally 1).
+If the checkbutton is not selected, then the indicator is drawn as an empty box with
+raised relief, and the associated variable is
+set to the
+.B offvalue
+(normally 0).
+The variable name
+may be modified with options on the command line (\f5-variable\fR option).
+Configuration options may also be used to modify the way the
+indicator is displayed (or whether it is displayed at all).
+By default a checkbutton is configured to select and deselect
+itself on alternate button clicks.
+.SH "WIDGET COMMAND"
+The \f5checkbutton\fR command creates a new Tk command whose
+name is \fIpathname\fR.  This
+command may be used to invoke various
+operations on the widget.  It has the following general form:
+.RS
+.EX
+\fIpathname option \fR?\fIarg arg ...\fR?
+.EE
+.RE
+\fIOption\fR and the \fIarg\fRs
+determine the exact behaviour of the command.  The following
+commands are possible for checkbutton widgets:
+.TP
+\fIpathname \f5cget\fR \fIoption\fR
+Returns the current value of the configuration option given
+by \fIoption\fR.
+\fIOption\fR may have any of the values accepted by the \f5checkbutton\fR
+command.
+.TP
+\fIpathname \f5configure\fR ?\fIoption\fR? ?\fIvalue option value ...\fR?
+Query or modify the configuration options of the widget.
+If no \fIoption\fR is specified, returns a list of all of
+the available options for \fIpathname\fR.  If
+one or more \fIoption-value\fR pairs are specified, then the command
+modifies the given widget option(s) to have the given value(s);  in
+this case the command returns an empty string.
+\fIOption\fR may have any of the values accepted by the \f5checkbutton\fR
+command.
+.TP
+\fIpathname \f5deselect\fR
+Deselects the checkbutton and sets the associated variable to its ``off''
+value.
+.TP
+\fIpathname \f5invoke\fR
+Does just what would have happened if the user invoked the checkbutton
+with the mouse: toggle the selection state of the button and invoke
+the Tk command associated with the checkbutton, if there is one.
+The return value is the return value from the Tk command, or an
+empty string if there is no command associated with the checkbutton.
+This command is ignored if the checkbutton's state is \f5disabled\fR.
+.TP
+\fIpathname \f5select\fR
+Selects the checkbutton and sets the associated variable to 1.
+.TP
+\fIpathname \f5toggle\fR
+Toggles the selection state of the button, redisplaying it and
+modifying its associated variable to reflect the new state.
+
+.SH BINDINGS
+Tk automatically creates bindings for checkbuttons that give them
+the following default behaviour:
+.IP [1]
+A checkbutton activates whenever the mouse passes over it and deactivates
+whenever the mouse leaves the checkbutton.
+.IP [2]
+When mouse button 1 is pressed over a checkbutton it is invoked (its
+selection state toggles and the command associated with the button is
+invoked, if there is one).
+.PP
+If the checkbutton's state is \f5disabled\fR then none of the above
+actions occur:  the checkbutton is completely non-responsive.
+.PP
+The behaviour of checkbuttons can be changed by defining new bindings for
+individual widgets.
+.SH SEE ALSO
+.IR button (9),
+.IR choicebutton (9),
+.IR options (9),
+.IR radiobutton (9),
+.IR types (9)
--- /dev/null
+++ b/man/9/choicebutton
@@ -1,0 +1,148 @@
+.TH CHOICEBUTTON 9
+.SH NAME
+choicebutton \- create and manipulate choicebutton widgets
+.SH SYNOPSIS
+\f5choicebutton\fI \fIpathName \fR?\fIoptions\fR?
+.SH STANDARD OPTIONS
+.EX
+-activebackground -borderwidth      -relief
+-activeforeground -font             -text
+-anchor           -foreground
+-background       -image
+-bitmap           -justify
+.EE
+.SH "WIDGET-SPECIFIC OPTIONS"
+.TP
+.B -command \fIcommand\fP
+Specifies a Tk command to associate with the button.  This command
+is typically invoked when mouse button 1 is released over the button
+window.  The button's global variable (\f5-variable\fR option) will
+be updated before the command is invoked.
+.TP
+.B -height \fIdist\fP
+Specifies a desired height for the button.
+If this option isn't specified, the button's desired height is computed
+from the size of the highest item in the
+.B -values
+list.
+.TP
+.B -selectcolor \fIcolour\fP
+Specifies a background colour to use when the button is selected.
+If specified as an empty string, no special colour is used for
+displaying when the widget is selected.
+.TP
+.B -values \fIlist\fP
+Specifies a list of all the values the choicebutton can choose from.
+.TP
+.B -variable \fIstring\fP
+Specifies name of global variable to set whenever this button is
+selected. The variable will be set to the index of the currently
+selected item. Changes in this variable also cause the button to
+choose the appropriate item (provided it is an integer and within
+range).
+.TP
+.B -width \fIdist\fP
+Specifies a desired width for the button.
+If this option isn't specified, the button's desired width is computed
+from the size of the widest item in the
+.B -values
+list.
+.SH DESCRIPTION
+The \f5choicebutton\fR command creates a new window (given by the
+\fIpathName\fR argument) and makes it into a choicebutton widget.
+Additional
+options, described above, may be specified on the command line
+to configure aspects of the choicebutton such as its colours, font,
+text, and initial relief.  The \f5choicebutton\fR command returns its
+\fIpathName\fR argument.  At the time this command is invoked,
+there must not exist a window named \fIpathName\fR.
+.PP
+A choicebutton is a widget that displays a textual string,
+one of the items given in the
+.I list
+argument to the
+.B -values
+option.
+When clicked on, the choicebutton displays a menu showing
+all the available choices, allowing the user to change
+the choice by selecting an item.
+In the description below, an
+.I index
+is a zero-based index into the set of values specified with
+the
+.B -values
+option.
+.SH "WIDGET COMMAND"
+The \f5choicebutton\fR command creates a new Tk command whose
+name is \fIpathName\fR.  This
+command may be used to invoke various
+operations on the widget.  It has the following general form:
+.RS
+.EX
+\fIpathName option \fR?\fIarg arg ...\fR?
+.EE
+.RE
+\fIOption\fR and the \fIarg\fRs
+determine the exact behaviour of the command.  The following
+commands are possible for choicebutton widgets:
+.TP
+\fIpathName \f5cget\fR \fIoption\fR
+Returns the current value of the configuration option given
+by \fIoption\fR.
+\fIOption\fR may have any of the values accepted by the \f5choicebutton\fR
+command.
+.TP
+\fIpathName \f5configure\fR ?\fIoption\fR? ?\fIvalue option value ...\fR?
+Query or modify the configuration options of the widget.
+If no \fIoption\fR is specified, returns a list of all of
+the available options for \fIpathName\fR.  If
+one or more \fIoption-value\fR pairs are specified, the command
+modifies the given widget option(s) to have the given value(s);  in
+this case the command returns an empty string.
+\fIOption\fR may have any of the values accepted by the \f5choicebutton\fR
+command.
+.TP
+\fIpathName \f5get\fR ?\fIvalue\fR?
+Get the index of the first item with
+.IR value .
+If
+.I value
+is not given, get the index of the currently selected item.
+.TP
+\fIpathName \f5set\fR \fIindex\fR
+Sets the current item to the
+.IR index th
+value.
+.TP
+\fIpathName \f5getvalue\fR ?\fIindex\fR?
+Get the value of the
+.IR index th
+item. If
+.I index
+is not given, get the value of the currently selected item.
+.TP
+\fIpathName \f5setvalue\fR \fIvalue\fR
+Set the current item to the first item having
+.IR value .
+If no item has that value, no change is made,
+and an error is returned.
+.TP
+\fIpathName \f5invoke\fR \fIindex\fR
+Does just what would have happened if the user invoked the choicebutton
+with the mouse and selected the item at
+.IR index :
+sets the current item to
+.I index
+and runs
+its associated Tk command, if there is one.
+The return value is that from invoking the Tk command, or an
+empty string if there is no associated command.
+.TP
+\fIpathName \f5valuecount\fR
+Returns the number of values configured in the choicebutton.
+.SH SEE ALSO
+.IR button (9),
+.IR checkbutton (9),
+.IR options (9),
+.IR radiobutton (9),
+.IR types (9)
--- /dev/null
+++ b/man/9/cursor
@@ -1,0 +1,47 @@
+.TH CURSOR 9
+.SH NAME
+cursor \- change the current mouse cursor
+.SH SYNOPSIS
+.B cursor
+?\fIoptions\fP?
+.SH INTRODUCTION
+.B Cursor
+changes the image displayed at the current mouse position.
+The following options are supported:
+.TP
+\f5-image \fIimage\fR
+.I Image
+gives a name created using the
+.IR image (9)
+command; the image must conform to the rules
+given for
+.B Display.cursor
+(see
+.IR draw-display (2)).
+.TP
+\f5-bitmap \fIbitmap\fR
+Similar to
+.B -image
+but using the
+.I bitmap
+syntax (see
+.IR types (9)).
+.TP
+\f5-x \fIdist\fP
+The cursor will be drawn
+.I dist
+to the right of the actual position of the mouse.
+.TP
+\f5-y \fIdist\fP
+The cursor will be drawn
+.I dist
+below the actual position of the mouse.
+.TP
+.B -default
+Revert the cursor to its default image.
+.SH BUGS
+A cursor image should have some inherent association with
+its hot-spot offset.
+.SH SEE ALSO
+.IR options (9),
+.IR types (9)
--- /dev/null
+++ b/man/9/destroy
@@ -1,0 +1,24 @@
+.TH DESTROY 9
+.SH NAME
+destroy \- Destroy one or more windows
+.SH SYNOPSIS
+\f5destroy \fR?\fIwindow window ...\fR?
+
+.SH DESCRIPTION
+This command deletes the windows given by the
+\fIwindow\fR arguments, plus all of their descendants.
+The \fIwindow\fRs are destroyed in order, and if an error occurs
+in destroying a window the command aborts without destroying the
+remaining windows.
+.PP
+The
+.B Destroy
+event is fired for each window destroyed, including descendants.
+.PP
+If the window ``.'' is specified, it is unmapped rather than destroyed
+and the
+.B Destroy
+event is not fired on it.
+However, all of its decendants are destroyed as normal.
+.SH SEE ALSO
+.IR bind (9)
--- /dev/null
+++ b/man/9/entry
@@ -1,0 +1,316 @@
+.TH ENTRY 9
+.SH NAME
+entry \- Create and manipulate entry widgets
+.SH SYNOPSIS
+\f5entry\fI \fIpathName \fR?\fIoptions\fR?
+.SH STANDARD OPTIONS
+.EX
+-background  -highlightcolor     -selectbackground
+-borderwidth -highlightthickness -selectforeground
+-font        -justify            -takefocus
+-foreground  -relief             -xscrollcommand
+.EE
+.SH "WIDGET-SPECIFIC OPTIONS"
+.TP
+.B -show \fIstring\fP
+If this option is specified, then the true contents of the entry
+are not displayed in the window.
+Instead, each character in the entry's value will be displayed as
+the first character in the value of this option, such as ``*''.
+This is useful, for example, if the entry is to be used to enter
+a password.
+.TP
+.B -state \fIstate\fP
+Specifies one of two states for the entry:  \f5normal\fR or \f5disabled\fR.
+If the entry is disabled then the value may not be changed using widget
+commands and no insertion cursor will be displayed, even if the input focus is
+in the widget.
+.TP
+.B -width \fIdist\fP
+Specifies an integer value indicating the desired width of the entry window.
+
+.SH DESCRIPTION
+The \f5entry\fR command creates a new window (given by the
+\fIpathName\fR argument) and makes it into an entry widget.
+Additional options, described above, may be specified on the
+command line
+to configure aspects of the entry such as its colours, font,
+and relief.  The \f5entry\fR command returns its
+\fIpathName\fR argument.  At the time this command is invoked,
+there must not exist a window named \fIpathName\fR.
+.PP
+An entry is a widget that displays a one-line text string and
+allows that string to be edited using widget commands described below, which
+are typically bound to keystrokes and mouse actions.
+When first created, an entry's string is empty.
+A portion of the entry may be selected as described below.
+Entries observe the standard Tk rules for dealing with the
+input focus.  When an entry has the input focus it displays an
+\fIinsertion cursor\fR to indicate where new characters will be
+inserted.
+.PP
+Entries are capable of displaying strings that are too long to
+fit entirely within the widget's window.  In this case, only a
+portion of the string will be displayed;  commands described below
+may be used to change the view in the window.  Entries use
+the standard \f5xscrollcommand\fR mechanism for interacting with
+scrollbars (see the description of the \f5xscrollcommand\fR option
+for details).
+
+.SH "WIDGET COMMAND"
+The \f5entry\fR command creates a new Tk command whose
+name is \fIpathName\fR.  This
+command may be used to invoke various
+operations on the widget.  It has the following general form:
+.RS
+.EX
+\fIpathName option \fR?\fIarg arg ...\fR?
+.EE
+.RE
+\fIOption\fR and the \fIarg\fRs
+determine the exact behaviour of the command.
+.PP
+Many of the widget commands for entries take one or more indices as
+arguments.  An index specifies a particular character in the entry's
+string, in any of the following ways:
+.TP 12
+\fInumber\fR
+Specifies the character as a numerical index, where 0 corresponds
+to the first character in the string.
+.TP 12
+\f5anchor\fR
+Indicates the anchor point for the selection, which is set with the
+\f5select from\fR and \f5select adjust\fR widget commands.
+.TP 12
+\f5end\fR
+Indicates the character just after the last one in the entry's string.
+This is equivalent to specifying a numerical index equal to the length
+of the entry's string.
+.TP 12
+\f5insert\fR
+Indicates the character adjacent to and immediately following the
+insertion cursor.
+.TP 12
+\f5sel.first\fR
+Indicates the first character in the selection.  It is an error to
+use this form if the selection isn't in the entry window.
+.TP 12
+\f5sel.last\fR
+Indicates the character just after the last one in the selection.
+It is an error to use this form if the selection isn't in the
+entry window.
+.TP 12
+\f5@\fInumber\fR
+In this form, \fInumber\fR is treated as an x-coordinate in the
+entry's window;  the character spanning that x-coordinate is used.
+For example, ``\f5@0\fR'' indicates the left-most character in the
+window.
+.PP
+The following commands are possible for entry widgets:
+.TP
+\fIpathName \f5bbox \fIindex\fR
+Returns a list of four numbers describing the bounding box of the
+character given by \fIindex\fR.
+The first two elements of the list give the x and y coordinates of
+the upper-left corner of the screen area covered by the character
+(in pixels relative to the widget) and the last two elements give
+the width and height of the character, in pixels.
+The bounding box may refer to a region outside the visible area
+of the window.
+.TP
+\fIpathName \f5cget\fR \fIoption\fR
+Returns the current value of the configuration option given
+by \fIoption\fR.
+\fIOption\fR may have any of the values accepted by the \f5entry\fR
+command.
+.TP
+\fIpathName \f5configure\fR ?\fIoption\fR? ?\fIvalue option value ...\fR?
+Query or modify the configuration options of the widget.
+If no \fIoption\fR is specified, returns a list of all of
+the available options for \fIpathName\fR.  If
+one or more \fIoption-value\fR pairs are specified, then the command
+modifies the given widget option(s) to have the given value(s);  in
+this case the command returns an empty string.
+\fIOption\fR may have any of the values accepted by the \f5entry\fR
+command.
+.TP
+\fIpathName \f5delete \fIfirst \fR?\fIlast\fR?
+Delete one or more elements of the entry.
+\fIFirst\fR is the index of the first character to delete, and
+\fIlast\fR is the index of the character just after the last
+one to delete.
+If \fIlast\fR isn't specified it defaults to \fIfirst\fR+1,
+i.e. a single character is deleted.
+This command returns an empty string.
+.TP
+\fIpathName \f5get\fR
+Returns the entry's string.
+.TP
+\fIpathName \f5icursor \fIindex\fR
+Arrange for the insertion cursor to be displayed just before the character
+given by \fIindex\fR.  Returns an empty string.
+.TP
+\fIpathName \f5index\fI index\fR
+Returns the numerical index corresponding to \fIindex\fR.
+.TP
+\fIpathName \f5insert \fIindex string\fR
+Insert the characters of \fIstring\fR just before the character
+indicated by \fIindex\fR.  Returns an empty string.
+.TP
+\fIpathName \f5see \fIindex\fR
+Adjusts the view in the window so that the character given by \fIindex\fR
+is completely visible.
+.TP
+\fIpathName \f5selection \fIoption arg\fR
+This command is used to adjust the selection within an entry.  It
+has several forms, depending on \fIoption\fR:
+.RS
+.TP
+\fIpathName \f5selection adjust \fIindex\fR
+Locate the end of the selection nearest to the character given by
+\fIindex\fR, and adjust that end of the selection to be at \fIindex\fR
+(i.e including but not going beyond \fIindex\fR).  The other
+end of the selection is made the anchor point for future
+\f5select to\fR commands.  If the selection
+isn't currently in the entry, then a new selection is created to
+include the characters between \fIindex\fR and the most recent
+selection anchor point, inclusive.
+Returns an empty string.
+.TP
+\fIpathName \f5selection clear\fR
+Clear the selection if it is currently in this widget.  If the
+selection isn't in this widget then the command has no effect.
+Returns an empty string.
+.TP
+\fIpathName \f5selection from \fIindex\fR
+Set the selection anchor point to just before the character
+given by \fIindex\fR.  Doesn't change the selection.
+Returns an empty string.
+.TP
+\fIpathName \f5selection present\fR
+Returns 1 if there are characters selected in the entry,
+0 if nothing is selected.
+.TP
+\fIpathName \f5selection range \fIstart\fR \fIend\fR
+Sets the selection to include the characters starting with
+the one indexed by \fIstart\fR and ending with the one just
+before \fIend\fR.
+If \fIend\fR refers to the same character as \fIstart\fR or an
+earlier one, then the entry's selection is cleared.
+.TP
+\fIpathName \f5selection to \fIindex\fR
+If \fIindex\fR is before the anchor point, set the selection
+to the characters from \fIindex\fR up to but not including
+the anchor point.
+If \fIindex\fR is the same as the anchor point, do nothing.
+If \fIindex\fR is after the anchor point, set the selection
+to the characters from the anchor point up to but not including
+\fIindex\fR.
+The anchor point is determined by the most recent \f5select from\fR
+or \f5select adjust\fR command in this widget.
+If the selection isn't in this widget then a new selection is
+created using the most recent anchor point specified for the widget.
+Returns an empty string.
+.RE
+.TP
+\fIpathName \f5xview \fIargs\fR
+This command is used to query and change the horizontal position of the
+text in the widget's window.  It can take any of the following
+forms:
+.RS
+.TP
+\fIpathName \f5xview\fR
+Returns a list containing two elements.
+Each element is a real fraction between 0 and 1;  together they describe
+the horizontal span that is visible in the window.
+For example, if the first element is .2 and the second element is .6,
+20% of the entry's text is off-screen to the left, the middle 40% is visible
+in the window, and 40% of the text is off-screen to the right.
+These are the same values passed to scrollbars via the \f5-xscrollcommand\fR
+option.
+.TP
+\fIpathName \f5xview\fR \fIindex\fR
+Adjusts the view in the window so that the character given by \fIindex\fR
+is displayed at the left edge of the window.
+.TP
+\fIpathName \f5xview moveto\fI fraction\fR
+Adjusts the view in the window so that the character \fIfraction\fR of the
+way through the text appears at the left edge of the window.
+\fIFraction\fR must be a fraction between 0 and 1.
+.TP
+\fIpathName \f5xview scroll \fInumber what\fR
+This command shifts the view in the window left or right according to
+\fInumber\fR and \fIwhat\fR.
+\fINumber\fR must be an integer.
+\fIWhat\fR must be either \f5units\fR or \f5pages\fR.
+If \fIwhat\fR is \f5units\fR, the view adjusts left or right by
+\fInumber\fR average-width characters on the display;  if it is
+\f5pages\fR then the view adjusts by \fInumber\fR screenfuls.
+If \fInumber\fR is negative then characters farther to the left
+become visible;  if it is positive then characters farther to the right
+become visible.
+.RE
+
+.SH "DEFAULT BINDINGS"
+Tk automatically creates bindings for entries that give them
+the following default behaviour.
+In the descriptions below, ``word'' refers to a contiguous group
+of letters, digits, or ``_'' characters, or any single character
+other than these.
+.IP [1]
+Clicking mouse button 1 positions the insertion cursor
+just before the character underneath the mouse cursor, sets the
+input focus to this widget, and clears any selection in the widget.
+Dragging with mouse button 1 strokes out a selection between
+the insertion cursor and the character under the mouse.
+.IP [2]
+Double-clicking with mouse button 1 selects the word under the mouse
+and positions the insertion cursor at the beginning of the word.
+.IP [3]
+If any normal printing characters are typed in an entry, the current
+selection is deleted, and they are
+inserted at the point of the insertion cursor
+.IP [4]
+The view in the entry can be adjusted by dragging with mouse button 2.
+.IP [5]
+The Left and Right keys move the insertion cursor one character to the
+left or right;  they also clear any selection in the entry and set
+the selection anchor.
+Control-b and Control-f behave the same as Left and Right, respectively.
+.IP [6]
+The Home key, or Control-a, will move the insertion cursor to the
+beginning of the entry and clear any selection in the entry.
+.IP [7]
+The End key, or Control-e, will move the insertion cursor to the
+end of the entry and clear any selection in the entry.
+.IP [8]
+Control-/ selects all the text in the entry.
+.IP [9]
+Control-\e clears any selection in the entry.
+.IP [10]
+The Delete key deletes the selection, if there is one in the entry.
+If there is no selection, it deletes the character to the right of
+the insertion cursor.
+.IP [11]
+The BackSpace key and Control-h delete the selection, if there is one
+in the entry.
+If there is no selection, it deletes the character to the left of
+the insertion cursor.
+.IP [12]
+Control-d deletes the character to the right of the insertion cursor.
+.IP [13]
+Control-k deletes all the characters to the right of the insertion
+cursor.
+.IP [14]
+Control-w deletes the word to the left of the insertion cursor.
+.PP
+If the entry is disabled using the \f5-state\fR option, then the entry's
+view can still be adjusted and text in the entry can still be selected,
+but no insertion cursor will be displayed and no text modifications will
+take place.
+.PP
+The behaviour of entries can be changed by defining new bindings for
+individual widgets.
+.SH SEE ALSO
+.IR options (9),
+.IR types (9)
--- /dev/null
+++ b/man/9/focus
@@ -1,0 +1,21 @@
+.TH FOCUS 9
+.SH NAME
+focus \- change or query keyboard focus
+.SH SYNOPSIS
+\f5focus \fR?\fIwhat\fR?
+.SH DESCRIPTION
+With no arguments, \f5focus\fP returns the
+name of the widget to which keyboard events
+are currently delivered, known as the
+.IR "keyboard focus" .
+When given an argument,
+\f5focus\fP changes the keyboard focus.
+If \fIwhat\fP is the name of a widget, that
+widget will be made the keyboard focus.
+If \fIwhat\fP is \f5next\fP, the keyboard focus
+will be changed to the next item in the
+focus order for that window. If \fIwhat\fP is
+\f5previous\fP, the keyboard focus will be
+changed to the previous item in the focus order
+for that window. The focus order is determined
+automatically by Tk.
--- /dev/null
+++ b/man/9/frame
@@ -1,0 +1,79 @@
+.TH FRAME 9
+.SH NAME
+frame \- Create and manipulate frame widgets
+.SH SYNOPSIS
+\f5frame\fI \fIpathName ?\fIoptions\fR?
+.SH STANDARD OPTIONS
+.EX
+-borderwidth -relief
+.EE
+.SH "WIDGET-SPECIFIC OPTIONS"
+.TP
+.B -background \fIcolour\fP
+This option is the same as the standard \f5background\fR option
+except that its value may also be specified as an empty string.
+In this case, the widget will display no background or border, and
+no colours will be consumed from its colourmap for its background
+and border.
+.TP
+.B -height \fIdist\fP
+Specifies the desired height for the window.
+If this option is less than or equal to zero then the window will
+not request any size at all.
+.TP
+.B -width \fIdist\fP
+Specifies the desired width for the window.
+If this option is less than or equal to zero then the window will
+not request any size at all.
+
+.SH DESCRIPTION
+The \f5frame\fR command creates a new window (given by the
+\fIpathName\fR argument) and makes it into a frame widget.
+Additional
+options, described above, may be specified on the command line
+to configure aspects of the frame such as its background colour
+and relief.  The \f5frame\fR command returns the
+path name of the new window.
+.PP
+A frame is a simple widget.  Its primary purpose is to act as a
+spacer or container for complex window layouts.  The only features
+of a frame are its background colour and an optional 3-D border to make the
+frame appear raised or sunken.
+
+.SH "WIDGET COMMAND"
+The \f5frame\fR command creates a new Tk command whose
+name is \fIpathName\fR.. This
+command may be used to invoke various
+operations on the widget.  It has the following general form:
+.RS
+.EX
+\fIpathName option \fR?\fIarg arg ...\fR?
+.EE
+.RE
+\fIPathName\fR is the name of the command, which is the same as
+the frame widget's path name.  \fIOption\fR and the \fIarg\fRs
+determine the exact behaviour of the command.  The following
+commands are possible for frame widgets:
+.TP
+\fIpathName \f5cget\fR \fIoption\fR
+Returns the current value of the configuration option given
+by \fIoption\fR.
+\fIOption\fR may have any of the values accepted by the \f5frame\fR
+command.
+.TP
+\fIpathName \f5configure\fR ?\fIoption\fR? \fI?value option value ...\fR?
+Query or modify the configuration options of the widget.
+If no \fIoption\fR is specified, returns a list of all of
+the available options for \fIpathName\fR.  If
+one or more \fIoption-value\fR pairs are specified, then the command
+modifies the given widget option(s) to have the given value(s);  in
+this case the command returns an empty string.
+\fIOption\fR may have any of the values accepted by the \f5frame\fR
+command.
+
+.SH BINDINGS
+When a new frame is created, it has no default event bindings:
+frames are not intended to be interactive.
+.SH SEE ALSO
+.IR options (9),
+.IR types (9)
--- /dev/null
+++ b/man/9/grab
@@ -1,0 +1,58 @@
+.TH GRAB 9
+.SH NAME
+grab \- Confine pointer and keyboard events to a window sub-tree
+.SH SYNOPSIS
+\f5grab \fIwhat \fR?\fIarg arg \fR...?
+.SH DESCRIPTION
+This command implements simple pointer grabbing for Tk.
+When a grab is set for a particular window, Tk restricts all pointer
+events to the grab window and its descendants in Tk's window hierarchy.
+Whenever the pointer is within the grab window's subtree, the pointer
+will behave exactly the same as if there had been no grab at all
+and all events will be reported in the normal fashion.
+When the pointer is outside \fIwindow\fR's tree, button presses and
+releases and
+mouse motion events are reported to \fIwindow\fR, and window entry
+and window exit events are ignored.
+The grab subtree ``owns'' the pointer:
+windows outside the grab subtree will be visible on the screen
+but they will be insensitive until the grab is released.
+The tree of windows underneath the grab window can include top-level
+windows, in which case all of those top-level windows
+and their descendants will continue to receive mouse events
+during the grab.
+.PP
+A grab locks out all applications on the screen,
+so that only the given subtree of the grabbing application will be
+sensitive to pointer events (mouse button presses, mouse button releases,
+pointer motions, window entries, and window exits).
+During grabs the window manager will not receive pointer
+events either (which means that if the grab is not released
+the whole GUI will freeze, so be careful!).
+.PP
+The \f5grab\fR command can take any of the following forms:
+.TP
+\f5grab release \fIwindow\fR
+Releases the grab on \fIwindow\fR if there is one, otherwise does
+nothing.  Returns an empty string.
+.TP
+\f5grab set \fIwindow\fR
+If a grab was already in effect for this application on
+\fIwindow\fR's display then it is automatically released.
+If there is already a grab on \fIwindow\fR then the command
+does nothing.  Returns an empty string.
+.TP
+\f5grab ifunset \fIwindow\fR
+Same as
+.B grab set
+except that \fIwindow\fR is only grabbed if there is no
+other grab currently in effect.
+.TP
+\f5grab status \fIwindow\fR
+Returns \f5none\fR if no grab is currently set on \fIwindow\fR,
+or
+\fIwindow\fR if it has the grab.
+.SH BUGS
+The current application-exclusive nature of
+.B grab
+is a problem which will be adressed in the future.
--- /dev/null
+++ b/man/9/grid
@@ -1,0 +1,332 @@
+.TH GRID 9
+'\" Note:  do not modify the .SH NAME line immediately below!
+.SH NAME
+grid \- Geometry manager that arranges widgets in a grid
+.SH SYNOPSIS
+\f5grid \fIoption arg \fR?\fIarg ...\fR?
+
+.SH DESCRIPTION
+.PP
+The \f5grid\fR command is used to communicate with the grid
+geometry manager that arranges widgets in rows and columns inside
+of another window, called the geometry master (or master window).
+The \f5grid\fR command can have any of several forms, depending
+on the \fIoption\fR argument:
+.TP
+\f5grid bbox \fImaster\fR ?\fIcolumn row\fR? ?\fIcolumn2 row2\fR?
+With no arguments, 
+the bounding box (in pixels) of the grid is returned.
+The return value consists of 4 integers.  The first two are the pixel
+offset from the master window (x then y) of the top-left corner of the
+grid, and the second two integers are the bottom-right corner of the grid,
+also in pixels.  If a single \fIcolumn\fP and \fIrow\fP is specified on 
+the command line, then the bounding box for that cell is returned, where the
+top left cell is numbered from zero.  If both \fIcolumn\fP and \fIrow\fP
+arguments are specified, then the bounding box spanning the rows and columns
+indicated is returned.
+.TP
+\f5grid cellinfo \fImaster column row\fR
+Returns a list giving information about the slave
+currently held in the grid cell specified by
+\fIcolumn\fP and \fIrow\fP.
+The first element of the list is the name of the slave,
+and the rest have the same option-value form that
+might be specified to \f5grid configure\fR
+(the same form as returned by \f5grid info\fR).
+.TP
+\f5grid columnconfigure \fImaster index \fR?\fI-option value...\fR?
+Set the column properties of the \fIindex\fP column of the 
+geometry master, \fImaster\fP.
+The valid options are \f5-minsize\fP, \f5-weight\fP, \f5-pad\fP and \f5-name\fP.
+If one or more options are provided, then \fIindex\fP may be given as 
+a list of column indices to which the configuration options will operate on.
+The \f5-minsize\fP option sets the minimum size, in screen units,
+that will be permitted for this column.
+The \f5-weight\fP option (an integer value)
+sets the relative weight for apportioning
+any extra spaces among
+columns.
+A weight of zero (0) indicates the column will not deviate from its requested
+size.  A column whose weight is two will grow at twice the rate as a column
+of weight one when extra space is allocated to the layout.
+The \f5-pad\fP option specifies a pad distance that will be
+added either side of the column; adjacent columns merge their padding,
+creating a pad sized to the larger of the pads on either column.
+The \f5-name\fP option associates a name with the column, which can
+then be used as an index to refer to that column.
+.TP
+\f5grid \fIslave \fR?\fIslave ...\fR? ?\fIoptions\fR?
+The arguments consist of the names of one or more slave windows
+followed by pairs of arguments that specify how
+to manage the slaves.
+The characters \f5-\fP,  \f5x\fP and \f5^\fP, 
+can be specified instead of a window name to alter the default
+location of a \fIslave\fP, as described in the ``RELATIVE PLACEMENT''
+section, below.
+The following options are supported:
+.RS
+.TP
+\f5-column \fIindex\fR
+Insert the slave so that it occupies the \fIindex\fPth column in the grid.
+Column numbers start with 0.  If this option is not supplied, then the
+slave is arranged just to the right of previous slave specified on this
+call to \fIgrid\fP, or column "0" if it is the first slave.  For each
+\f5x\fP that immediately precedes the \fIslave\fP, the column position
+is incremented by one.  Thus the \f5x\fP represents a blank column
+for this row in the grid.
+.TP
+\f5-columnspan \fIn\fR
+Insert the slave so that it occupies \fIn\fP columns in the grid.
+The default is one column, unless the window name is followed by a
+\f5-\fP, in which case the columnspan is incremented once for each immediately
+following \f5-\fP.
+.TP
+\f5-in \fIother\fR
+Insert the slave(s) in the master
+window given by \fIother\fR.  The default is the first slave's
+parent window.
+.TP
+\f5-ipadx \fIamount\fR
+The \fIamount\fR specifies how much horizontal internal padding to
+leave on each side of the slave(s).  This is space is added
+inside the slave(s) border.
+The \fIamount\fR must be a valid screen distance, such as \f52\fR or \f5.5c\fR.
+It defaults to 0.
+.TP
+\f5-ipady \fIamount\fR
+The \fIamount\fR specifies how much vertical internal padding to
+leave on on the top and bottom of the slave(s).
+This space is added inside the slave(s) border.
+The \fIamount\fR  defaults to 0.
+.TP
+\f5-padx \fIamount\fR
+The \fIamount\fR specifies how much horizontal external padding to
+leave on each side of the slave(s), in screen units.
+The \fIamount\fR defaults to 0.
+This space is added outside the slave(s) border.
+.TP
+\f5-pady \fIamount\fR
+The \fIamount\fR specifies how much vertical external padding to
+leave on the top and bottom of the slave(s), in screen units.
+The \fIamount\fR defaults to 0.
+This space is added outside the slave(s) border.
+.TP
+\f5-row \fIindex\fR
+Insert the slave so that it occupies the \fIindex\fPth row in the grid.
+Row numbers start with 0.  If this option is not supplied, then the
+slave is arranged on the same row as the previous slave specified on this
+call to \f5grid\fP, or the first unoccupied row if this is the first slave.
+.TP
+\f5-rowspan \fIn\fR
+Insert the slave so that it occupies \fIn\fP rows in the grid.
+The default is one row.  If the next \f5grid\fP command contains
+\f5^\fP characters instead of \fIslaves\fP that line up with the columns
+of this \fIslave\fP, then the \f5rowspan\fP of this \fIslave\fP is
+extended by one.
+.TP
+\f5-sticky \fIstyle\fR
+If a slave's cell is larger than its requested dimensions, this
+option may be used to position (or stretch) the slave within its cell.
+\fIStyle\fR  is a string that contains zero or more of the characters
+\f5n\fP, \f5s\fP, \f5e\fP or \f5w\fP.
+The string can optionally contains spaces or
+commas, but they are ignored.  Each letter refers to a side (north, south,
+east, or west) that the slave will "stick" to.  If both \f5n\fP and \f5s\fP (or
+\f5e\fP and \f5w\fP) are specified, the slave will be stretched to fill the entire
+height (or width) of its cavity.  The \f5sticky\fP option subsumes the
+combination of \f5-anchor\fP and \f5-fill\fP that is used by \f5pack\fP.
+The default is \f5{}\fP, which causes the slave to be centered in its cavity,
+at its requested size.
+.LP
+If any of the slaves are already managed by the geometry manager
+then any unspecified options for them retain their previous values rather
+than receiving default values.
+.LP
+It is an error if any \fIslave\fP covers an area already covered by an
+existing slave of the grid.
+.RE
+.TP
+\f5grid columndelete \fImaster index0 \fR?\fIindex1\fR?
+Deletes columns from \fIindex0\fP up to but not including \fIindex1\fP,
+If \fIindex1\fP is not given, it defaults to \fIindex0\fP+1.
+The deleted columns may not contain cells spanned by
+slaves outside the deleted columns.
+.TP
+\f5grid columnindex \fImaster index\fP
+Returns \fIindex\fP as a numerical index into the column.
+.TP
+\f5grid columninsert \fImaster index \fR?\fIcount\fR?
+Inserts \fIcount\fP (default 1) new columns just before \fIindex\fP.
+The inserted rows may not split a spanning cell.
+.TP
+\f5grid forget \fIslave \fR?\fIslave ...\fR?
+Removes each of the \fIslave\fRs from grid for its
+master and unmaps their windows.
+The slaves will no longer be managed by the grid geometry manager.
+The configuration options for that window are forgotten, so that if the
+slave is managed once more by the grid geometry manager, the initial
+default settings are used.
+.TP
+\f5grid info \fIslave\fR
+Returns a list whose elements are the current configuration state of
+the slave given by \fIslave\fR in the same option-value form that
+might be specified to \f5grid configure\fR.
+The first two elements of the list are ``\f5-in \fImaster\fR'' where
+\fImaster\fR is the slave's master.
+.TP
+\f5grid location \fImaster x y\fR
+Given  \fIx\fP and \fIy\fP values in screen units relative to the master window, 
+the column and row number at that \fIx\fP and \fIy\fP location is returned.
+For locations that are above or to the left of the grid, \f5-1\fP is returned.
+.TP
+\f5grid propagate \fImaster\fR ?\fIboolean\fR?
+If \fIboolean\fR has a true boolean value such as \f51\fR or \f5on\fR
+then propagation is enabled for \fImaster\fR, which must be a window
+name (see ``GEOMETRY PROPAGATION'' below).
+If \fIboolean\fR has a false boolean value then propagation is
+disabled for \fImaster\fR.
+In either of these cases an empty string is returned.
+If \fIboolean\fR is omitted then the command returns \f50\fR or
+\f51\fR to indicate whether propagation is currently enabled
+for \fImaster\fR.
+Propagation is enabled by default.
+.TP
+\f5grid rowconfigure \fImaster index \fR?\fI-option value...\fR?
+Set the row properties of the \fIindex\fP row of the 
+geometry master, \fImaster\fP.
+The valid options are \f5-minsize\fP, \f5-weight\fP, \f5-pad\fP and \f5-name\fP.
+If one or more options are provided, then \fIindex\fP may be given as 
+a list of row indices to which the configuration options will operate on.
+The \f5-minsize\fP option sets the minimum size, in screen units,
+that will be permitted for this row.
+The \f5-weight\fP option (an integer value)
+sets the relative weight for apportioning
+any extra spaces among
+rows.
+A weight of zero (0) indicates the row will not deviate from its requested
+size.  A row whose weight is two will grow at twice the rate as a row
+of weight one when extra space is allocated to the layout.
+The \f5-pad\fP option specifies a pad distance that will be
+added either side of the row; adjacent rows merge their padding,
+creating a pad sized to the larger of the pads on either row.
+The \f5-name\fP option associates a name with the row, which can
+then be used as an index to refer to that row.
+.TP
+\f5grid rowdelete \fImaster index0 \fR?\fIindex1\fR?
+Deletes rows from \fIindex0\fP up to but not including \fIindex1\fP,
+If \fIindex1\fP is not given, it defaults to \fIindex0\fP+1.
+The deleted rows may not contain cells spanned by
+slaves outside the deleted columns.
+.TP
+\f5grid rowindex \fImaster index\fP
+Returns \fIindex\fP as a numerical index into the row.
+.TP
+\f5grid rowinsert \fImaster index \fR?\fIcount\fR?
+Inserts \fIcount\fP (default 1) new rows just before \fIindex\fP.
+The inserted rows may not split a spanning cell.
+.TP
+\f5grid size \fImaster\fR
+Returns the size of the grid (in columns then rows) for \fImaster\fP.
+The size is determined either by the largest row or column that has
+been occupied by a slave, or the largest column or row that has been explicitly
+configured.
+.TP
+\f5grid slaves \fImaster\fR ?\fI-option value\fR?
+If no options are supplied, a list of all of the slaves in \fImaster\fR
+are returned, most recently manages first.
+\fIOption\fP can be either \f5-row\fP or \f5-column\fP which
+causes only the slaves in the row (or column) specified by \fIvalue\fP
+to be returned.
+.SS INDICES
+.PP
+An index is either a non-negative integer value, the string
+\f5end\fP, (one beyond the last index in the row or column in question),
+or a name identifying a row or column previously named with
+the \f5-name\fP option to \f5rowconfigure\fP or \f5columnconfigure\fP.
+.SS "RELATIVE PLACEMENT"
+.PP
+The \f5grid\fP command contains a limited set of capabilities that
+permit layouts to be created without specifying the row and column 
+information for each slave.  This permits slaves to be rearranged, 
+added, or removed without the need to explicitly specify row and
+column information.
+When no column or row information is specified for a \fIslave\fP, 
+default values are chosen for
+\f5column\fP, \f5row\fP, \f5columnspan\fP and \f5rowspan\fP
+at the time the \fIslave\fP is managed. The values are chosen
+based upon the current layout of the grid, the position of the \fIslave\fP
+relative to other \fIslave\fPs in the same grid command, and the presence
+of the characters \f5-\fP, \f5^\fP, and \f5^\fP in \f5grid\fP
+command where \fIslave\fP names are normally expected.
+.RS
+.TP
+\f5-\fP
+This increases the columnspan of the \fIslave\fP to the left.  Several
+\f5-\fP's in a row will successively increase the columnspan. A \f5-\fP
+may not follow a \f5^\fP or a \f5x\fP.
+.TP
+\f5x\fP
+This leaves an empty column between the \fIslave\fP on the left and
+the \fIslave\fP on the right.
+.TP
+\f5^\fP
+This extends the \f5rowspan\fP of the \fIslave\fP above the \f5^\fP's
+in the grid.  The number of \f5^\fP's in a row must match the number of
+columns spanned by the \fIslave\fP above it.
+.RE
+.SS "THE GRID ALGORITHM"
+.PP
+The grid geometry manager lays out its slaves in three steps.
+In the first step, the minimum size needed to fit all of the slaves
+is computed, then (if propagation is turned on), a request is made
+of the master window to become that size.
+In the second step, the requested size is compared against the actual size
+of the master.  If the sizes are different, then spaces is added to or taken
+away from the layout as needed.
+For the final step, each slave is positioned in its row(s) and column(s)
+based on the setting of its \fIsticky\fP flag.
+.PP
+To compute the minimum size of a layout, the grid geometry manager
+first looks at all slaves whose columnspan and rowspan values are one,
+and computes the nominal size of each row or column to be either the
+\fIminsize\fP for that row or column, or the sum of the \fIpad\fPding
+plus the size of the largest slave, whichever is greater.  Then the
+slaves whose rowspans or columnspans are greater than one are
+examined.  If a group of rows or columns need to be increased in size
+in order to accommodate these slaves, then extra space is added to each
+row or column in the group according to its \fIweight\fP.  For each
+group whose weights are all zero, the additional space is apportioned
+equally.
+.PP
+For masters whose size is larger than the requested layout, the additional
+space is apportioned according to the row and column weights.  If all of
+the weights are zero, the layout is centered within its master.
+For masters whose size is smaller than the requested layout, space is taken
+away from columns and rows according to their weights.  However, once a 
+column or row shrinks to its minsize, its weight is taken to be zero.
+If more space needs to be removed from a layout than would be permitted, as
+when all the rows or columns are at their minimum sizes, the layout is
+clipped on the bottom and right.
+.SS "GEOMETRY PROPAGATION"
+.PP
+The grid geometry manager normally computes how large a master must be to
+just exactly meet the needs of its slaves, and it sets the
+requested width and height of the master to these dimensions.
+This causes geometry information to propagate up through a
+window hierarchy to a top-level window so that the entire
+sub-tree sizes itself to fit the needs of the leaf windows.
+However, the \f5grid propagate\fR command may be used to
+turn off propagation for one or more masters.
+If propagation is disabled then grid will not set
+the requested width and height of the master window.
+This may be useful if, for example, you wish for a master
+window to have a fixed size that you specify.
+.SS CREDITS
+.PP
+The \f5grid\fP command is based on ideas taken from the \fIGridBag\fP
+geometry manager written by Doug. Stein, and the \f5blt_table\fR geometry
+manager, written by George Howlett.
+.SH "SEE ALSO"
+.IR pack (9),
+.IR types (9),
+.IR options (9)
--- /dev/null
+++ b/man/9/image
@@ -1,0 +1,75 @@
+.TH IMAGE 9
+.SH NAME
+image \- Create and manipulate images
+.SH SYNOPSIS
+\f5image\fR \fIoption \fR?\fIarg arg ...\fR?
+.SH DESCRIPTION
+The \f5image\fR command is used to create, delete, and query images.
+It can take several different forms, depending on the
+\fIoption\fR argument.  The legal forms are:
+.TP
+\f5image create bitmap \fR?\fIname\fR? ?\fIoption value ...\fR?
+Creates a new image and returns its name.
+\fIname\fR specifies the name for the image;  if it is omitted then
+Tk picks a name of the form \f5image\fIx\fR, where \fIx\fR is
+an integer.
+The following options are supported:
+.RS
+.TP
+\f5-file \fIname\fR
+\fIname\fR gives the name of a file whose contents define the
+source bitmap.
+The file must be in
+.IR image (6)
+format.
+.TP
+\f5-maskfile \fIname\fR
+\fIname\fR gives the name of a file whose contents define the
+mask. The file must be in
+.IR image (6)
+format.
+.RE
+.PP
+If an image already exists by the given name then it is replaced
+with the new image.
+.PP
+Transparency effects can be achieved by either the source image including an alpha channel
+or by specifying a mask image.
+If a mask is specified then the resultant image is generated by rendering the source
+through the mask onto a transparent destination image.
+.PP
+The following commands are posible for images:
+.TP
+\f5image delete \fR?\fIname name\fR ...?
+Deletes each of the named images and returns an empty string.
+Each image name is invalidated.
+If there are instances of the images displayed in widgets,
+the images won't actually be deleted until all of the instances
+are released.
+.TP
+\f5image height \fIname\fR
+Returns a decimal string giving the height of image \fIname\fR
+in pixels.
+.TP
+\f5image names\fR
+Returns a list containing the names of all existing images.
+.TP
+\f5image type \fIname\fR
+Returns ``bitmap'' if
+.I name
+is an existing image, or an error otherwise.
+.TP
+\f5image width \fIname\fR
+Returns a decimal string giving the width of image \fIname\fR
+in pixels.
+.SH BUGS
+When an image is reconfigured the widgets that refer to it
+will continue to render the original image until their
+.B -image
+option is reconfigured.
+.SH SEE ALSO
+.IR options (9),
+.IR types (9),
+.B imageput
+in
+.IR tk (2),
--- /dev/null
+++ b/man/9/label
@@ -1,0 +1,79 @@
+.TH LABEL 9
+.SH NAME
+label \- Create and manipulate label widgets
+.SH SYNOPSIS
+\f5label\fI \fIpathName \fR?\fIoptions\fR?
+.SH STANDARD OPTIONS
+.EX
+-anchor        -font               -justify
+-background    -foreground         -relief
+-bitmap        -highlightcolor     -takefocus
+-borderwidth   -highlightthickness -text
+-disabledcolor -image              -underline
+.EE
+.SH "WIDGET-SPECIFIC OPTIONS"
+.TP
+.B -height \fIdist\fP
+Specifies a desired height for the label.
+If this option isn't specified, the label's desired height is computed
+from the size of the image or bitmap or text being displayed in it.
+.TP
+.B -width \fIdist\fP
+Specifies a desired width for the label.
+If this option isn't specified, the label's desired width is computed
+from the size of the image or bitmap or text being displayed in it.
+
+.SH DESCRIPTION
+The \f5label\fR command creates a new window (given by the
+\fIpathName\fR argument) and makes it into a label widget.
+Additional
+options, described above, may be specified on the command line
+to configure aspects of the label such as its colours, font,
+text, and initial relief.  The \f5label\fR command returns its
+\fIpathName\fR argument.  At the time this command is invoked,
+there must not exist a window named \fIpathName\fR.
+.PP
+A label is a widget that displays a textual string, bitmap or image.
+If text is displayed, it must all be in a single font, but it
+can occupy multiple lines on the screen (if it contains newlines) and
+one of the characters may optionally be underlined using the
+\f5underline\fR option.
+The label can be manipulated in a few simple ways, such as
+changing its relief or text, using the commands described below.
+
+.SH "WIDGET COMMAND"
+The \f5label\fR command creates a new Tk command whose
+name is \fIpathName\fR.  This
+command may be used to invoke various
+operations on the widget.  It has the following general form:
+.RS
+.EX
+\fIpathName option \fR?\fIarg arg ...\fR?
+.EE
+.RE
+\fIOption\fR and the \fIarg\fRs
+determine the exact behaviour of the command.  The following
+commands are possible for label widgets:
+.TP
+\fIpathName \f5cget\fR \fIoption\fR
+Returns the current value of the configuration option given
+by \fIoption\fR.
+\fIOption\fR may have any of the values accepted by the \f5label\fR
+command.
+.TP
+\fIpathName \f5configure\fR ?\fIoption\fR? ?\fIvalue option value ...\fR?
+Query or modify the configuration options of the widget.
+If no \fIoption\fR is specified, returns a list of all of
+the available options for \fIpathName\fR.  If
+one or more \fIoption-value\fR pairs are specified, then the command
+modifies the given widget option(s) to have the given value(s);  in
+this case the command returns an empty string.
+\fIOption\fR may have any of the values accepted by the \f5label\fR
+command.
+
+.SH BINDINGS
+When a new label is created, it has no default event bindings:
+labels are not intended to be interactive.
+.SH SEE ALSO
+.IR options (9),
+.IR types (9)
--- /dev/null
+++ b/man/9/listbox
@@ -1,0 +1,304 @@
+.TH LISTBOX 9
+.SH NAME
+listbox \- Create and manipulate listbox widgets
+.SH SYNOPSIS
+\f5listbox\fI \fIpathName \fR?\fIoptions\fR?
+.SH STANDARD OPTIONS
+.EX
+-background  -highlightcolor     -selectforeground
+-borderwidth -highlightthickness -takefocus
+-font        -relief             -width
+-foreground  -selectbackground   -xscrollcommand
+-height      -selectborderwidth  -yscrollcommand
+.EE
+.SH "WIDGET-SPECIFIC OPTIONS"
+.TP
+.B -height \fIdist\fP
+Specifies the desired height for the window.
+.TP
+.B -selectmode \fIval\fP
+Specifies one of several styles for manipulating the selection.
+The value of the option may be arbitrary, but the default bindings
+expect it to be either \f5single\fR, \f5browse\fR, \f5multiple\fR,
+or \f5extended\fR;  the default value is \f5single\fR.
+.TP
+.B -width \fIdist\fP
+Specifies the desired width for the window.
+.SH DESCRIPTION
+The \f5listbox\fR command creates a new window (given by the
+\fIpathName\fR argument) and makes it into a listbox widget.
+Additional
+options, described above, may be specified on the command line
+to configure aspects of the listbox such as its colours, font,
+text, and relief.  The \f5listbox\fR command returns its
+\fIpathName\fR argument.  At the time this command is invoked,
+there must not exist a window named \fIpathName\fR.
+.PP
+A listbox is a widget that displays a list of strings, one per line.
+When first created, a new listbox has no elements.
+Elements may be added or deleted using widget commands described
+below.  In addition, one or more elements may be selected as described
+below.
+.PP
+It is not necessary for all the elements to be
+displayed in the listbox window at once;  commands described below
+may be used to change the view in the window.  Listboxes allow
+scrolling in both directions using the standard \f5-xscrollcommand\fR
+and \f5-yscrollcommand\fR options.
+.SH INDICES
+Many of the widget commands for listboxes take one or more indices
+as arguments.
+An index specifies a particular element of the listbox, in any of
+the following ways:
+.TP 12
+\fInumber\fR
+Specifies the element as a numerical index, where 0 corresponds
+to the first element in the listbox.
+.TP 12
+\f5active\fR
+Indicates the element that has the location cursor.  This element
+will be displayed with a highlight rectangle when the listbox has the
+keyboard focus, and it is specified with the \f5activate\fR
+widget command.
+.TP 12
+\f5anchor\fR
+Indicates the anchor point for the selection, which is set with the
+\f5selection anchor\fR widget command.
+.TP 12
+\f5end\fR
+Indicates the end of the listbox.
+For some commands this means just after the last element;
+for other commands it means the last element.
+.TP 12
+\f5@\fIx\f5,\fIy\fR
+Indicates the element that covers the point in the listbox window
+specified by \fIx\fR and \fIy\fR (in pixel coordinates).  If no
+element covers that point, then the closest element to that
+point is used.
+.LP
+In the widget command descriptions below, arguments named \fIindex\fR,
+\fIfirst\fR, and \fIlast\fR always contain text indices in one of
+the above forms.
+
+.SH "WIDGET COMMAND"
+The \f5listbox\fR command creates a new Tk command whose
+name is \fIpathName\fR.  This
+command may be used to invoke various
+operations on the widget.  It has the following general form:
+.RS
+.EX
+\fIpathName option \fR?\fIarg arg ...\fR?
+.EE
+.RE
+\fIOption\fR and the \fIarg\fRs
+determine the exact behaviour of the command.  The following
+commands are possible for listbox widgets:
+.TP
+\fIpathName \f5activate\fR \fIindex\fR
+Sets the active element to the one indicated by \fIindex\fR.
+The active element is drawn with an underline when the widget
+has the input focus, and its index may be retrieved with the
+index \f5active\fR.
+.TP
+\fIpathName \f5cget\fR \fIoption\fR
+Returns the current value of the configuration option given
+by \fIoption\fR.
+\fIOption\fR may have any of the values accepted by the \f5listbox\fR
+command.
+.TP
+\fIpathName \f5configure\fR ?\fIoption\fR? ?\fIvalue option value ...\fR?
+Query or modify the configuration options of the widget.
+If no \fIoption\fR is specified, returns a list of all of
+the available options for \fIpathName\fR.  If
+one or more \fIoption-value\fR pairs are specified, then the command
+modifies the given widget option(s) to have the given value(s);  in
+this case the command returns an empty string.
+\fIOption\fR may have any of the values accepted by the \f5listbox\fR
+command.
+.TP
+\fIpathName \f5curselection\fR
+Returns a list containing the numerical indices of
+all of the elements in the listbox that are currently selected.
+If there are no elements selected in the listbox then an empty
+string is returned.
+.TP
+\fIpathName \f5delete \fIfirst \fR?\fIlast\fR?
+Deletes one or more elements of the listbox.  \fIFirst\fR and \fIlast\fR
+are indices specifying the first and last elements in the range
+to delete.  If \fIlast\fR isn't specified it defaults to
+\fIfirst\fR, i.e. a single element is deleted.
+.TP
+\fIpathName \f5get \fIfirst\fR ?\fIlast\fR?
+If \fIlast\fR is omitted, returns the contents of the listbox
+element indicated by \fIfirst\fR.
+If \fIlast\fR is specified, the command returns a list whose elements
+are all of the listbox elements between \fIfirst\fR and \fIlast\fR,
+inclusive.
+Both \fIfirst\fR and \fIlast\fR may have any of the standard
+forms for indices.
+.TP
+\fIpathName \f5index \fIindex\fR
+Returns a decimal string giving the integer index value that
+corresponds to \fIindex\fR.
+.TP
+\fIpathName \f5insert \fIindex \fR?\fIelement element ...\fR?
+Inserts zero or more new elements in the list just before the
+element given by \fIindex\fR.  If \fIindex\fR is specified as
+\f5end\fR then the new elements are added to the end of the
+list.  Returns an empty string.
+.TP
+\fIpathName \f5nearest \fIy\fR
+Given a y-coordinate within the listbox window, this command returns
+the index of the (visible) listbox element nearest to that y-coordinate.
+.TP
+\fIpathName \f5see \fIindex\fR
+Adjust the view in the listbox so that the element given by \fIindex\fR
+is visible.
+If the element is already visible then the command has no effect;
+if the element is near one edge of the window then the listbox
+scrolls to bring the element into view at the edge;  otherwise
+the listbox scrolls to center the element.
+.TP
+\fIpathName \f5selection \fIoption arg\fR
+This command is used to adjust the selection within a listbox.  It
+has several forms, depending on \fIoption\fR:
+.RS
+.TP
+\fIpathName \f5selection anchor \fIindex\fR
+Sets the selection anchor to the element given by \fIindex\fR.
+The selection anchor is the end of the selection that is fixed
+while dragging out a selection with the mouse.
+The index \f5anchor\fR may be used to refer to the anchor
+element.
+.TP
+\fIpathName \f5selection clear \fIfirst \fR?\fIlast\fR?
+If any of the elements between \fIfirst\fR and \fIlast\fR
+(inclusive) are selected, they are deselected.
+The selection state is not changed for elements outside
+this range.
+.TP
+\fIpathName \f5selection includes \fIindex\fR
+Returns 1 if the element indicated by \fIindex\fR is currently
+selected, 0 if it isn't.
+.TP
+\fIpathName \f5selection set \fIfirst \fR?\fIlast\fR?
+Selects all of the elements in the range between
+\fIfirst\fR and \fIlast\fR, inclusive, without affecting
+the selection state of elements outside that range.
+.RE
+.TP
+\fIpathName \f5size\fR
+Returns a decimal string indicating the total number of elements
+in the listbox.
+.TP
+\fIpathName \f5xview \fIargs\fR
+This command is used to query and change the horizontal position of the
+information in the widget's window.  It can take any of the following
+forms:
+.RS
+.TP
+\fIpathName \f5xview\fR
+Returns a list containing two elements.
+Each element is a real fraction between 0 and 1;  together they describe
+the horizontal span that is visible in the window.
+For example, if the first element is .2 and the second element is .6,
+20% of the listbox's text is off-screen to the left, the middle 40% is visible
+in the window, and 40% of the text is off-screen to the right.
+These are the same values passed to scrollbars via the \f5-xscrollcommand\fR
+option.
+.TP
+\fIpathName \f5xview\fR \fIindex\fR
+Adjusts the view in the window so that the character position given by
+\fIindex\fR is displayed at the left edge of the window.
+Character positions are defined by the width of the character \f50\fR.
+.TP
+\fIpathName \f5xview moveto\fI fraction\fR
+Adjusts the view in the window so that \fIfraction\fR of the
+total width of the listbox text is off-screen to the left.
+\fIfraction\fR must be a fraction between 0 and 1.
+.TP
+\fIpathName \f5xview scroll \fInumber what\fR
+This command shifts the view in the window left or right according to
+\fInumber\fR and \fIwhat\fR.
+\fINumber\fR must be an integer.
+\fIWhat\fR must be either \f5units\fR or \f5pages\fR.
+If \fIwhat\fR is \f5units\fR, the view adjusts left or right by
+\fInumber\fR character units (the width of the \f50\fR character)
+on the display;  if it is \f5pages\fR then the view adjusts by
+\fInumber\fR screenfuls.
+If \fInumber\fR is negative then characters farther to the left
+become visible;  if it is positive then characters farther to the right
+become visible.
+.RE
+.TP
+\fIpathName \f5yview \fI?args\fR?
+This command is used to query and change the vertical position of the
+text in the widget's window.
+It can take any of the following forms:
+.RS
+.TP
+\fIpathName \f5yview\fR
+Returns a list containing two elements, both of which are real fractions
+between 0 and 1.
+The first element gives the position of the listbox element at the
+top of the window, relative to the listbox as a whole (0.5 means
+it is halfway through the listbox, for example).
+The second element gives the position of the listbox element just after
+the last one in the window, relative to the listbox as a whole.
+These are the same values passed to scrollbars via the \f5-yscrollcommand\fR
+option.
+.TP
+\fIpathName \f5yview\fR \fIindex\fR
+Adjusts the view in the window so that the element given by
+\fIindex\fR is displayed at the top of the window.
+.TP
+\fIpathName \f5yview moveto\fI fraction\fR
+Adjusts the view in the window so that the element given by \fIfraction\fR
+appears at the top of the window.
+\fIFraction\fR is a fraction between 0 and 1;  0 indicates the first
+element in the listbox, 0.33 indicates the element one-third the
+way through the listbox, and so on.
+.TP
+\fIpathName \f5yview scroll \fInumber what\fR
+This command adjusts the view in the window up or down according to
+\fInumber\fR and \fIwhat\fR.
+\fINumber\fR must be an integer.
+\fIWhat\fR must be either \f5units\fR or \f5pages\fR.
+If \fIwhat\fR is \f5units\fR, the view adjusts up or down by
+\fInumber\fR lines;  if it is \f5pages\fR then
+the view adjusts by \fInumber\fR screenfuls.
+If \fInumber\fR is negative then earlier elements
+become visible;  if it is positive then later elements
+become visible.
+.RE
+.SH "DEFAULT BINDINGS"
+If the selection mode is \f5single\fR or \f5browse\fR, at most one
+element can be selected in the listbox at once.
+In both modes, clicking button 1 on an element selects
+it and deselects any other selected item.
+In \f5browse\fR mode it is also possible to drag the selection
+with button 1.
+.PP
+If the selection mode is \f5multiple\fR or \f5extended\fR,
+any number of elements may be selected at once, including discontiguous
+ranges.  In \f5multiple\fR mode, clicking button 1 on an element
+toggles its selection state without affecting any other elements.
+In \f5extended\fR mode, pressing button 1 on an element selects
+it, deselects everything else, and sets the anchor to the element
+under the mouse;  dragging the mouse with button 1
+down extends the selection to include all the elements between
+the anchor and the element under the mouse, inclusive.
+.PP
+Most people will probably want to use \f5browse\fR mode for
+single selections and \f5extended\fR mode for multiple selections;
+the other modes appear to be useful only in special situations.
+.PP
+The behaviour of listboxes can be changed by defining new bindings for
+individual widgets. The default bindings do a grab set when button 1 is pressed
+and a grab release when button 1 is released. Care must be taken when overriding
+either or both of these defaults to ensure that grabbing is consistent.
+.SH BUGS
+At least one entry is required for the widget to indicate that it has keyboard focus.
+.SH SEE ALSO
+.IR options (9),
+.IR types (9)
--- /dev/null
+++ b/man/9/lower
@@ -1,0 +1,12 @@
+.TH LOWER 9
+.SH NAME
+lower \- Change a window's position in the stacking order
+.SH SYNOPSIS
+\f5lower \fIwindow
+.SH DESCRIPTION
+\f5Lower\fR lowers
+\fIwindow\fR so that it is below all of its siblings in the stacking
+order (it will be obscured by any siblings that overlap it and
+will not obscure any siblings).
+.SH "SEE ALSO"
+.IR raise (9)
--- /dev/null
+++ b/man/9/menu
@@ -1,0 +1,461 @@
+.TH MENU 9
+.SH NAME
+menu \- Create and manipulate menu widgets
+.SH SYNOPSIS
+\f5menu\fI \fIpathName \fR?\fIoptions\fR?
+.SH STANDARD OPTIONS
+.EX
+-activebackground -borderwidth   -foreground
+-activeforeground -disabledcolor -relief
+-background       -font
+.EE
+.SH "WIDGET-SPECIFIC OPTIONS"
+.TP
+.B -postcommand \fIcommand\fP
+If this option is specified then it provides a Tk command to execute
+each time the menu is posted.  The command is invoked by the \f5post\fR
+widget command before posting the menu.
+.TP
+.B -selectcolor \fIcolour\fP
+For menu entries that are check buttons or radio buttons, this option
+specifies the colour to display in the indicator when the check button
+or radio button is selected.
+.SH INTRODUCTION
+The \f5menu\fR command creates a new top-level window (given
+by the \fIpathName\fR argument) and makes it into a menu widget.
+Additional
+options, described above, may be specified on the command line
+to configure aspects of the menu such as its colours and font.
+The \f5menu\fR command returns its
+\fIpathName\fR argument.  At the time this command is invoked,
+there must not exist a window named \fIpathName\fR.
+.PP
+A menu is a widget that displays a collection of one-line entries arranged
+in a column.  There exist several different types of entries,
+each with different properties.  Entries of different types may be
+combined in a single menu.  Menu entries are not the same as
+entry widgets.  In fact, menu entries are not even distinct widgets;
+the entire menu is one widget.
+.PP
+Menu entries are displayed with up to three separate fields.
+The main field is a label in the form of a text string,
+a bitmap, or an image, controlled by the \f5-label\fR,
+\f5-bitmap\fR, and \f5-image\fR options for the entry.
+The second field is a marker for cascade entries,
+showing that the entry will post a cascade menu.
+It is displayed at the right-hand edge of the entry.
+The third field is an \fIindicator\fR.  The indicator is present only for
+checkbutton or radiobutton entries.  It indicates whether the entry
+is selected or not, and is displayed to the left of the entry's
+string.
+.PP
+In normal use, an entry becomes active (displays itself differently)
+whenever the mouse pointer is over the entry.  If a mouse
+button is released over the entry then the entry is \fIinvoked\fR.
+The effect of invocation is different for each type of entry;
+these effects are described below in the sections on individual
+entries.
+.PP
+Entries may be \fIdisabled\fR, which causes their labels
+and accelerators to be displayed
+with dimmer colours.
+The default menu bindings will not allow
+a disabled entry to be activated or invoked.
+Disabled entries may be re-enabled, at which point it becomes
+possible to activate and invoke them again.
+
+.SH "COMMAND ENTRIES"
+The most common kind of menu entry is a command entry, which
+behaves much like a button widget.  When a command entry is
+invoked, a Tk command is executed.  The Tk
+command is specified with the \f5-command\fR option.
+
+.SH "SEPARATOR ENTRIES"
+A separator is an entry that is displayed as a horizontal dividing
+line.  A separator may not be activated or invoked, and it has
+no behaviour other than its display appearance.
+
+.SH "CHECKBUTTON ENTRIES"
+A checkbutton menu entry behaves much like a checkbutton widget.
+When it is invoked it toggles back and forth between the selected
+and deselected states.  When the entry is selected, the value
+``1'' is stored in a particular global variable (as determined by
+\f5-variable\fR option for the entry);  when
+the entry is deselected the value ``0'' is stored in the global variable.
+An indicator box is displayed to the left of the label in a checkbutton
+entry.  If the entry is selected then the indicator's center is displayed
+in the colour given by the \f5-selectcolor\fR option for the entry;
+otherwise the indicator's center is displayed in the background colour for
+the menu.  If a \f5-command\fR option is specified for a checkbutton
+entry, then its value is evaluated as a Tk command each time the entry
+is invoked;  this happens after toggling the entry's
+selected state.
+
+.SH "RADIOBUTTON ENTRIES"
+A radiobutton menu entry behaves much like a radiobutton widget.
+Radiobutton entries are organized in groups of which only one
+entry may be selected at a time.  Whenever a particular entry
+becomes selected it stores a particular value into a particular
+global variable (as determined by the \f5-value\fR and
+\f5-variable\fR options for the entry).  This action
+causes any previously-selected entry in the same group
+to deselect itself.
+Once an entry has become selected, any change to the entry's
+associated variable will cause the entry to deselect itself.
+Grouping of radiobutton entries is determined by their
+associated variables:  if two entries have the same associated
+variable then they are in the same group.
+An indicator diamond is displayed to the left of the label in each
+radiobutton entry.  If the entry is selected then the indicator's
+center is displayed in the colour given by the \f5-selectcolor\fR option
+for the entry;
+otherwise the indicator's center is displayed in the background colour for
+the menu.  If a \f5-command\fR option is specified for a radiobutton
+entry, then its value is evaluated as a Tk command each time the entry
+is invoked;  this happens after selecting the entry.
+
+.SH "CASCADE ENTRIES"
+A cascade entry is one with an associated menu (determined
+by the \f5-menu\fR option).  Cascade entries allow the construction
+of cascading menus.
+The \f5postcascade\fR widget command can be used to post and unpost
+the associated menu just to the right of the cascade entry.
+The associated menu must be a child of the menu containing
+the cascade entry (this is needed in order for menu traversal to
+work correctly).
+.PP
+A cascade entry posts its associated menu by invoking a
+Tk command of the form
+.RS
+.EX
+\fImenu\f5 post \fIx y\fR
+.EE
+.RE
+where \fImenu\fR is the path name of the associated menu, and \fIx\fR
+and \fIy\fR are the screen coordinates of the upper-right
+corner of the cascade entry.
+The lower-level menu is unposted by executing a Tk command with
+the form
+.RS
+.EX
+\fImenu\f5 unpost\fR
+.EE
+.RE
+where \fImenu\fR is the name of the associated menu.
+.PP
+If a \f5-command\fR option is specified for a cascade entry then it is
+evaluated as a Tk command whenever the entry is invoked.
+
+.SH "WIDGET COMMAND"
+The \f5menu\fR command creates a new Tk command whose
+name is \fIpathName\fR.  This
+command may be used to invoke various
+operations on the widget.  It has the following general form:
+.RS
+.EX
+\fIpathName option \fR?\fIarg arg ...\fR?
+.EE
+.RE
+\fIOption\fR and the \fIarg\fRs
+determine the exact behaviour of the command.
+.PP
+Many of the widget commands for a menu take as one argument an
+indicator of which entry of the menu to operate on.  These
+indicators are called \fIindex\fRes and may be specified in
+any of the following forms:
+.TP 12
+\fInumber\fR
+Specifies the entry numerically, where 0 corresponds
+to the top-most entry of the menu, 1 to the entry below it, and
+so on.
+.TP 12
+\f5active\fR
+Indicates the entry that is currently active.  If no entry is
+active then this form is equivalent to \f5none\fR.  This form may
+not be abbreviated.
+.TP 12
+\f5end\fR
+Indicates the bottommost entry in the menu.  If there are no
+entries in the menu then this form is equivalent to \f5none\fR.
+This form may not be abbreviated.
+.TP 12
+\f5none\fR
+Indicates ``no entry at all'';  this is used most commonly with
+the \f5activate\fR option to deactivate all the entries in the
+menu.  In most cases the specification of \f5none\fR causes
+nothing to happen in the widget command.
+This form may not be abbreviated.
+.TP 12
+\f5@\fInumber\fR
+In this form, \fInumber\fR is treated as a y-coordinate in the
+menu's window;  the entry closest to that y-coordinate is used.
+For example, ``\f5@0\fR'' indicates the top-most entry in the
+window.
+.PP
+The following widget commands are possible for menu widgets:
+.TP
+\fIpathName \f5activate \fIindex\fR
+Change the state of the entry indicated by \fIindex\fR to \f5active\fR
+and redisplay it using its active colours.
+Any previously-active entry is deactivated.  If \fIindex\fR
+is specified as \f5none\fR, or if the specified entry is
+disabled, then the menu ends up with no active entry.
+Returns an empty string.
+.TP
+\fIpathName \f5add \fItype \fR?\fIoption value option value ...\fR?
+Add a new entry to the bottom of the menu.  The new entry's type
+is given by \fItype\fR and must be one of \f5cascade\fR,
+\f5checkbutton\fR, \f5command\fR, \f5radiobutton\fR, or \f5separator\fR.
+If additional arguments
+are present, they specify any of the following options:
+.RS
+.TP
+\f5-activebackground \fIvalue\fR
+Specifies a background colour to use for displaying this entry when it
+is active.
+If this option is not specified then the
+\f5activebackground\fR option for the overall menu is used.
+This option is not available for separator entries.
+.TP
+\f5-activeforeground \fIvalue\fR
+Specifies a foreground colour to use for displaying this entry when it
+is active.
+If this option is not specified then the
+\f5activeforeground\fR option for the overall menu is used.
+This option is not available for separator entries.
+.TP
+\f5-background \fIvalue\fR
+Specifies a background colour to use for displaying this entry when it
+is in the normal state (neither active nor disabled).
+If this option is not specified then the
+\f5background\fR option for the overall menu is used.
+This option is not available for separator entries.
+.TP
+\f5-bitmap \fIbitmap\fR
+Specifies a bitmap to display in the menu instead of a textual
+label.
+This option overrides the \f5-label\fR option but may be reset
+to an empty string to enable a textual label to be displayed.
+If a \f5-image\fR option has been specified, it overrides
+\f5-bitmap\fR.
+This option is not available for separator entries.
+.TP
+\f5-command \fIvalue\fR
+Specifies a Tk command to execute when the menu entry is invoked.
+Not available for separator entries.
+.TP
+\f5-font \fIvalue\fR
+Specifies the font to use when drawing the label or accelerator
+string in this entry.
+If this option is not specified then
+the \f5font\fR option for the overall menu is used.
+This option is not available for separator entries.
+.TP
+\f5-foreground \fIvalue\fR
+Specifies a foreground colour to use for displaying this entry when it
+is in the normal state (neither active nor disabled).
+If this option is not specified then the
+\f5foreground\fR option for the overall menu is used.
+This option is not available for separator entries.
+.TP
+\f5-image \fIvalue\fR
+Specifies an image to display in the menu instead of a text string
+or bitmap
+The image must have been created by some previous invocation of
+\f5image create\fR.
+This option overrides the \f5-label\fR and \f5-bitmap\fR options
+but may be reset to an empty string to enable a textual or
+bitmap label to be displayed.
+This option is not available for separator entries.
+.TP
+\f5-label \fIvalue\fR
+Specifies a string to display as an identifying label in the menu
+entry.  Not available for separator entries.
+.TP
+\f5-menu \fIvalue\fR
+Available only for cascade entries.  Specifies the path name of
+the submenu associated with this entry.
+The submenu must be a child of the menu.
+.TP
+\f5-selectcolor \fIvalue\fR
+Available only for checkbutton and radiobutton entries.
+Specifies the colour to display in the indicator when the entry is
+selected.
+If this option is not specified then the \f5selectcolor\fR
+option for the menu determines the indicator colour.
+.TP
+\f5-selectimage \fIvalue\fR
+Available only for checkbutton and radiobutton entries.
+Specifies an image to display in the entry (in place of
+the \f5-image\fR option) when it is selected.
+\fIValue\fR is the name of an image, which must have been created
+by some previous invocation of \f5image create\fR.
+This option is ignored unless the \f5-image\fR option has
+been specified.
+.TP
+\f5-state \fIvalue\fR
+Specifies one of three states for the entry:  \f5normal\fR, \f5active\fR,
+or \f5disabled\fR.  In normal state the entry is displayed using the
+\f5foreground\fR and \f5background\fR
+colours.
+The active state is typically used when the pointer is over the entry.
+In active state the entry is displayed using the \f5activeforeground\fR
+and \f5activebackground\fR colours.
+Disabled state means that the entry
+should be insensitive:  the default bindings will refuse to activate
+or invoke the entry.
+In this state the entry is displayed according to the 
+\f5disabledcolor\fR and \f5background\fR colours.
+This option is not available for separator entries.
+.TP
+\f5-underline \fIvalue\fR
+Specifies the integer index of a character to underline in the entry.
+This option is also queried by the default bindings and used to
+implement keyboard traversal.
+0 corresponds to the first character of the text displayed in the entry,
+1 to the next character, and so on.
+If a bitmap or image is displayed in the entry then this option is ignored.
+This option is not available for separator entries.
+.TP
+\f5-value \fIvalue\fR
+Available only for radiobutton entries.  Specifies the value to
+store in the entry's associated variable when the entry is selected.
+If an empty string is specified, then the \f5-label\fR option
+for the entry as the value to store in the variable.
+.TP
+\f5-variable \fIvalue\fR
+Available only for checkbutton and radiobutton entries.  Specifies
+the name of a global value to set when the entry is selected.
+For checkbutton entries the variable is also set when the entry
+is deselected.  For radiobutton entries, changing the variable
+causes the currently-selected entry to deselect itself.
+.LP
+The \f5add\fR widget command returns an empty string.
+.RE
+.TP
+\fIpathName \f5cget\fR \fIoption\fR
+Returns the current value of the configuration option given
+by \fIoption\fR.
+\fIOption\fR may have any of the values accepted by the \f5menu\fR
+command.
+.TP
+\fIpathName \f5configure\fR ?\fIoption\fR? ?\fIvalue option value ...\fR?
+Query or modify the configuration options of the widget.
+If no \fIoption\fR is specified, returns a list of all of
+the available options for \fIpathName\fR.  If
+one or more \fIoption-value\fR pairs are specified, then the command
+modifies the given widget option(s) to have the given value(s);  in
+this case the command returns an empty string.
+\fIOption\fR may have any of the values accepted by the \f5menu\fR
+command.
+.TP
+\fIpathName \f5delete \fIindex1\fR ?\fIindex2\fR?
+Delete all of the menu entries between \fIindex1\fR and
+\fIindex2\fR inclusive.
+If \fIindex2\fR is omitted then it defaults to \fIindex1\fR.
+.TP
+\fIpathName \f5entrycget\fR \fIindex option\fR
+Returns the current value of a configuration option for
+the entry given by \fIindex\fR.
+\fIOption\fR may have any of the values accepted by the \f5add\fR
+widget command.
+.TP
+\fIpathName \f5entryconfigure \fIindex \fR?\fIoptions\fR?
+This command is similar to the \f5configure\fR command, except that
+it applies to the options for an individual entry, whereas \f5configure\fR
+applies to the options for the menu as a whole.
+\fIOptions\fR may have any of the values accepted by the \f5add\fR
+widget command.  If \fIoptions\fR are specified, options are modified
+as indicated
+in the command and the command returns an empty string.
+.TP
+\fIpathName \f5index \fIindex\fR
+Returns the numerical index corresponding to \fIindex\fR, or
+\f5none\fR if \fIindex\fR was specified as \f5none\fR.
+.TP
+\fIpathName \f5insert \fIindex\fR \fItype \fR?\fIoption value option value ...\fR?
+Same as the \f5add\fR widget command except that it inserts the new
+entry just before the entry given by \fIindex\fR, instead of appending
+to the end of the menu.  The \fItype\fR, \fIoption\fR, and \fIvalue\fR
+arguments have the same interpretation as for the \f5add\fR widget
+command.
+.TP
+\fIpathName \f5invoke \fIindex\fR
+Invoke the action of the menu entry.  See the sections on the
+individual entries above for details on what happens.  If the
+menu entry is disabled then nothing happens.  If the
+entry has a command associated with it then the result of that
+command is returned as the result of the \f5invoke\fR widget
+command.  Otherwise the result is an empty string.  Note:  invoking
+a menu entry does not automatically unpost the menu;  the default
+bindings normally take care of this before invoking the \f5invoke\fR
+widget command.
+.TP
+\fIpathName \f5post \fIx y\fR
+Arrange for the menu to be displayed on the screen at the screen
+coordinates given by \fIx\fR and \fIy\fR.  These coordinates are
+adjusted if necessary to guarantee that the entire menu is visible on
+the screen.  This command normally returns an empty string.
+If the \f5postcommand\fR option has been specified, then its value is
+executed as a Tk script before posting the menu and the result of
+that script is returned as the result of the \f5post\fR widget
+command.
+If an error returns while executing the command, then the error is
+returned without posting the menu.
+.TP
+\fIpathName \f5postcascade \fIindex\fR
+Posts the submenu associated with the cascade entry given by
+\fIindex\fR, and unposts any previously posted submenu.
+If \fIindex\fR doesn't correspond to a cascade entry,
+or if \fIpathName\fR isn't posted,
+the command has no effect except to unpost any currently posted
+submenu.
+.TP
+\fIpathName \f5type \fIindex\fR
+Returns the type of the menu entry given by \fIindex\fR.
+This is the \fItype\fR argument passed to the \f5add\fR widget
+command when the entry was created, such as \f5command\fR
+or \f5separator\fR.
+.TP
+\fIpathName \f5unpost\fR
+Unmap the window so that it is no longer displayed.  If a
+lower-level cascaded menu is posted, unpost that menu.  Returns an
+empty string.
+.TP
+\fIpathName \f5yposition \fIindex\fR
+Returns a decimal string giving the y-coordinate within the menu
+window of the topmost pixel in the entry specified by \fIindex\fR.
+
+.SH "DEFAULT BINDINGS"
+Tk automatically creates bindings for menus that give them
+the following default behaviour:
+.IP [1]
+When the mouse enters a menu, the entry underneath the mouse
+cursor activates;  as the mouse moves around the menu, the active
+entry changes to track the mouse.
+.IP [2]
+When the mouse leaves a menu all of the entries in the menu
+deactivate, except in the special case where the mouse moves from
+a menu to a cascaded submenu.
+.IP [3]
+When a button is released over a menu, the active entry (if any) is invoked.
+The menu also unposts unless it is a torn-off menu.
+.IP [4]
+If any of the entries in a menu have letters underlined with
+with \f5-underline\fR option, then pressing one of the underlined
+letters (or its upper-case or lower-case equivalent) invokes that
+entry and unposts the menu.
+.PP
+Disabled menu entries are non-responsive:  they don't activate and
+they ignore mouse button presses and releases.
+.PP
+The behaviour of menus can be changed by defining new bindings for
+individual widgets.
+.SH BUGS
+The first time any colour option of an entry is configured,
+all of the menu colour option values are captured and set in the entry.
+Any subsequent changes to the menu's colour options
+will not be reflected in the entry.
+.SH SEE ALSO
+.IR options (9),
+.IR types (9)
--- /dev/null
+++ b/man/9/menubutton
@@ -1,0 +1,134 @@
+.TH MENUBUTTON 9
+.SH NAME
+menubutton \- Create and manipulate menubutton widgets
+.SH SYNOPSIS
+\f5menubutton\fI \fIpathName \fR?\fIoptions\fR?
+.SH STANDARD OPTIONS
+.EX
+-activebackground -disabledcolor      -justify
+-activeforeground -font               -relief
+-anchor           -foreground         -takefocus
+-background       -highlightcolor     -text
+-bitmap           -highlightthickness -underline
+-borderwidth      -image
+.EE
+.SH "WIDGET-SPECIFIC OPTIONS"
+.TP
+.BI -height \ state
+Specifies a desired height for the menubutton.
+If this option is not specified, the menubutton's desired height is computed
+from the size of the image or bitmap or text being displayed in it.
+.TP
+.BI -menu \ widgetname
+Specifies the path name of the menu associated with this menubutton.
+The menu must be a child of the menubutton.
+.TP
+.BI -state \ state
+Specifies one of three states for the menubutton:  \f5normal\fR, \f5active\fR,
+or \f5disabled\fR.  In normal state the menubutton is displayed using the
+\f5foreground\fR and \f5background\fR options.  The active state is
+typically used when the pointer is over the menubutton.  In active state
+the menubutton is displayed using the \f5activeforeground\fR and
+\f5activebackground\fR options.  Disabled state means that the menubutton
+should be insensitive:  the default bindings will refuse to activate
+the widget and will ignore mouse button presses.
+In this state the \f5disabledcolor\fR and
+\f5background\fR options determine how the button is displayed.
+.TP
+.BI -width \ dist
+Specifies a desired width for the menubutton.
+If this option is not specified, the menubutton's desired width is computed
+from the size of the image or bitmap or text being displayed in it.
+.SH INTRODUCTION
+.PP
+The \f5menubutton\fR command creates a new window (given by the
+\fIpathName\fR argument) and makes it into a menubutton widget.
+Additional
+options, described above, may be specified to configure aspects of the
+menubutton such as its colors, font, text, and initial relief.
+The \f5menubutton\fR command returns its
+\fIpathName\fR argument.  At the time this command is invoked,
+there must not exist a window named \fIpathName\fR.
+.PP
+A menubutton is a widget that displays a textual string, bitmap, or image
+and is associated with a menu widget.
+If text is displayed, it must all be in a single font, but it
+can occupy multiple lines on the screen (if it contains newlines) and
+one of the characters may optionally be underlined using the
+\f5underline\fR option.
+In normal usage, pressing
+mouse button 1 over the menubutton causes the associated menu to
+be posted just underneath the menubutton.
+If the mouse button is released over the menubutton then the menu
+remains posted; clicking on the menubutton again unposts the menu.
+Releasing the mouse button over a menu entry invokes the entry, while
+releasing mouse button elsewhere unposts the menu.
+.PP
+Menubuttons are typically organized into groups called menu bars
+that allow scanning:
+if the mouse button is pressed over one menubutton (causing it
+to post its menu) and the mouse is moved over another menubutton
+without releasing the mouse button, then the
+menu of the first menubutton is unposted and the menu of the
+new menubutton is posted instead.
+.SH "WIDGET COMMAND"
+.PP
+The \f5menubutton\fR command creates a new Tk command whose
+name is \fIpathName\fR.  This
+command may be used to invoke various
+operations on the widget.  It has the following general form:
+.RS
+.EX
+\fIpathName option \fR?\fIarg arg ...\fR?
+.EE
+.RE
+\fIOption\fR and the \fIarg\fRs
+determine the exact behaviour of the command.  The following
+commands are possible for menubutton widgets:
+.TP
+\fIpathName \f5cget\fR \fIoption\fR
+Returns the current value of the configuration option given
+by \fIoption\fR.
+\fIOption\fR may have any of the values accepted by the \f5menubutton\fR
+command.
+.TP
+\fIpathName \f5configure\fR ?\fIoption\fR? ?\fIvalue option value ...\fR?
+Query or modify the configuration options of the widget.
+If no \fIoption\fR is specified, returns a list of all of
+the available options for \fIpathName\fR.
+If one or more \fIoption-value\fR pairs are specified, then the command
+modifies the given widget option(s) to have the given value(s);  in
+this case the command returns an empty string.
+\fIOption\fR may have any of the values accepted by the \f5menubutton\fR
+command.
+.SH "DEFAULT BINDINGS"
+Tk automatically creates class bindings for menubuttons that give them
+the following default behaviour:
+.IP [1]
+A menubutton activates whenever the mouse passes over it and deactivates
+whenever the mouse leaves it.
+.IP [2]
+Pressing mouse button 1 over a menubutton posts the menu specified by the
+.B -menu
+option.
+If the menu is already posted then it is unposted.
+.IP [3]
+Releasing mouse button 1 over the menubutton leaves the menu posted.
+Releasing the button over a menu entry, if the menu is posted, invokes that entry.
+Releaseing the button anywhere else unposts the menu.
+.IP [4]
+When a menubutton is posted, its associated menu claims the input
+focus to allow keyboard traversal of the menu and its submenus.
+See the
+.IR menu (9)
+manual entry for details on these bindings.
+.PP
+If the menubutton's state is \f5disabled\fR then none of the above
+actions occur:  the menubutton is completely non-responsive.
+.PP
+The behaviour of menubuttons can be changed by defining new bindings for
+individual widgets or by redefining the class bindings.
+.SH SEE ALSO
+.IR menu (9),
+.IR options (9),
+.IR types (9)
--- /dev/null
+++ b/man/9/options
@@ -1,0 +1,244 @@
+.TH OPTIONS 9
+.SH NAME
+options \- Standard options supported by widgets
+.SH DESCRIPTION
+This manual entry describes the common configuration options supported
+by widgets in the Tk toolkit.  Every widget does not necessarily support
+every option (see the manual entries for individual widgets for a list
+of the standard options supported by that widget), but if a widget does
+support an option with one of the names listed below, then the option
+has exactly the effect described below.
+For a description of kinds of values that can passed to the various
+options, see
+.IR types (9).
+.PP
+In the descriptions below, the name refers to the
+switch used in class commands and \f5configure\fR widget commands to
+set this value.  For example, if an option's command-line switch is set to
+\f5-foreground\fR and there exists a widget \f5.a.b.c\fR, then the
+command
+.RS
+.EX
+\&.a.b.c configure -foreground black
+.EE
+.RE
+may be used to specify the value \f5black\fR for the option in the
+the widget \f5.a.b.c\fR.
+.TP
+.BI -activebackground " colour"
+Specifies background colour to use when drawing active elements.
+An element (a widget or portion of a widget) is active if the
+mouse cursor is positioned over the element and pressing a mouse button
+will cause some action to occur.
+.TP
+.BI -activeforeground " colour"
+Specifies foreground colour to use when drawing active elements.
+See above for definition of active elements.
+.TP
+.BI -actx
+Returns the current x position of the widget in screen coordinates.
+.TP
+.BI -acty
+Returns the current y position of the widget in screen coordinates.
+.TP
+.BI -actwidth
+Returns the current allocated width of the widget.
+.TP
+.BI -actheight
+Returns the current allocated height of the widget.
+.TP
+.BI -anchor " val"
+Specifies how the information in a widget (e.g. text or a bitmap)
+is to be displayed in the widget.
+.I Val
+must be one of the values \f5n\fR, \f5ne\fR, \f5e\fR, \f5se\fR,
+\f5s\fR, \f5sw\fR, \f5w\fR, \f5nw\fR, or \f5center\fR.
+For example, \f5nw\fR means display the information such that its
+top-left corner is at the top-left corner of the widget.
+.TP
+.B -background  \fIcolour\fP \fRor\fP -bg \fIcolour\fP
+Specifies the normal background colour to use when displaying the
+widget.
+.TP
+.BI -bitmap " bitmap"
+Specifies a bitmap to display in the widget.
+The exact way in which the bitmap is displayed may be affected by
+other options such as \f5anchor\fR or \f5justify\fR.
+Typically, if this option is specified then it overrides other
+options that specify a textual value to display in the widget;
+the \f5bitmap\fR option may be reset to an empty string to re-enable
+a text display.
+In widgets that support both \f5bitmap\fR and \f5image\fR options,
+\f5image\fR will usually override \f5bitmap\fR.
+For those widgets that support the \f5foreground\fR option, if the bitmap is
+monochrome it is displayed using the foreground colour.
+.TP
+.B -borderwidth \fIdist\fP \fRor\fP -bd \fIdist\fP
+Specifies a non-negative value indicating the width
+of the 3-D border to draw around the outside of the widget (if such a
+border is being drawn;  the \f5relief\fR option typically determines
+this).  The value may also be used when drawing 3-D effects in the
+interior of the widget.
+.TP
+.B -disabledcolor  \fIcolour\fP
+Specifies the foreground colour to use when drawing disabled widgets.
+Certain widgets can be disabled by use of their
+.B -state
+option.
+.TP
+.B -font \fIfont\fP
+Specifies the font to use when drawing text inside the widget.
+.TP
+.B -foreground \fIcolour\fP or -fg \fIcolour\fP
+Specifies the normal foreground colour to use when displaying the widget.
+.TP
+.B -highlightcolor \fIcolour\fP
+Specifies the colour to use for the traversal highlight rectangle that is
+drawn around the widget when it has the input focus.
+.TP
+.B -highlightthickness \fIdist\fP
+Specifies a non-negative value indicating the width of the highlight
+rectangle to draw around the outside of the widget when it has the
+input focus.
+If the value is zero, no focus highlight is drawn around the widget.
+.TP
+.B -image \fIimage\fP
+Specifies an image to display in the widget, which must have been
+created with the \f5image create\fR command.
+Typically, if the \f5image\fR option is specified then it overrides other
+options that specify a bitmap or textual value to display in the widget;
+the \f5image\fR option may be reset to an empty string to re-enable
+a bitmap or text display.
+For those widgets that support the \f5foreground\fR option, if the image is
+monochrome it is displayed using the foreground colour.
+.TP
+.B -jump \fIboolean\fP
+For widgets with a slider that can be dragged to adjust a value,
+such as scrollbars, this option determines when
+notifications are made about changes in the value.
+If the value is false, updates are made continuously as the
+slider is dragged.
+If the value is true, updates are delayed until the mouse button
+is released to end the drag;  at that point a single notification
+is made (the value ``jumps'' rather than changing smoothly).
+.TP
+.B -justify \fIval\fP
+When there are multiple lines of text displayed in a widget, this
+option determines how the lines line up with each other.
+.I Val
+must be one of \f5left\fR, \f5center\fR, or \f5right\fR.
+\f5Left\fR means that the lines' left edges all line up, \f5center\fR
+means that the lines' centers are aligned, and \f5right\fR means
+that the lines' right edges line up.
+.TP
+.B -orient \fIorientation\fP
+For widgets that can lay themselves out with either a horizontal
+or vertical orientation, such as scrollbars, this option specifies
+which orientation should be used.
+.I Orientation
+must be either \f5horizontal\fR
+or \f5vertical\fR.
+.TP
+.B -padx \fIdist\fP
+Specifies a non-negative value indicating how much extra space
+to request for the widget in the X-direction.
+When computing how large a window it needs, the widget will
+add this amount to the width it would normally need (as determined
+by the width of the things displayed in the widget);  if the geometry
+manager can satisfy this request, the widget will end up with extra
+internal space to the left and/or right of what it displays inside.
+Most widgets only use this option for padding text:  if they are
+displaying a bitmap or image, then they usually ignore padding
+options.
+.TP
+.B -pady \fIdist\fP
+Specifies a non-negative value indicating how much extra space
+to request for the widget in the Y-direction.
+When computing how large a window it needs, the widget will add
+this amount to the height it would normally need (as determined by
+the height of the things displayed in the widget);  if the geometry
+manager can satisfy this request, the widget will end up with extra
+internal space above and/or below what it displays inside.
+Most widgets only use this option for padding text:  if they are
+displaying a bitmap or image, then they usually ignore padding
+options.
+.TP
+.B -relief \fIval\fP
+Specifies the 3-D effect desired for the widget.  Acceptable
+values for
+.I val are \f5raised\fR, \f5sunken\fR, \f5flat\fR, \f5ridge\fR,
+and \f5groove\fR.
+The value
+indicates how the interior of the widget should appear relative
+to its exterior;  for example, \f5raised\fR means the interior of
+the widget should appear to protrude from the screen, relative to
+the exterior of the widget.
+.TP
+.B -selectbackground \fIcolour\fP
+Specifies the background colour to use when displaying selected
+items.
+.TP
+.B -selectborderwidth \fIdist\fP
+Specifies a non-negative value indicating the width
+of the 3-D border to draw around selected items.
+.TP
+.B -selectforeground \fIcolour\fP
+Specifies the foreground colour to use when displaying selected
+items.
+.TP
+.B -takefocus \fIboolean\fP
+Determines whether clicking on the widget will automatically
+give it the keyboard focus, and also whether the widget
+will become part of the keyboard focus list and made accessible
+by keyboard navigation.
+.TP
+.B -text \fIval\fP
+Specifies a string,
+.IR val ,
+to be displayed inside the widget.  The way in which
+the string is displayed depends on the particular widget and may be
+determined by other options, such as \f5anchor\fR or \f5justify\fR.
+.TP
+.B -underline \fIinteger\fP
+Specifies the integer index of a character to underline in the widget.
+This option is used by the default bindings to implement keyboard
+traversal for menu buttons and menu entries.
+0 corresponds to the first character of the text displayed in the
+widget, 1 to the next character, and so on.
+.TP
+.B -xscrollcommand \fIcommand\fP
+Specifies the prefix for a command used to communicate with horizontal
+scrollbars.
+When the view in the widget's window changes (or
+whenever anything else occurs that could change the display in a
+scrollbar, such as a change in the total size of the widget's
+contents), the widget will
+generate a Tk command by concatenating
+.I command
+and
+two numbers.
+Each of the numbers is a fraction between 0 and 1, which indicates
+a position in the document.  0 indicates the beginning of the document,
+1 indicates the end, .333 indicates a position one third the way through
+the document, and so on.
+The first fraction indicates the first information in the document
+that is visible in the window, and the second fraction indicates
+the information just after the last portion that is visible.
+The command is
+then passed to the Tk interpreter for execution.  Typically the
+\f5-xscrollcommand\fR option consists of the path name of a scrollbar
+widget followed by
+.RB `` set '',
+e.g.
+.RB `` ".x.scrollbar set" '':
+this will cause
+the scrollbar to be updated whenever the view in the window changes.
+If this option is not specified, then no command will be executed.
+.TP
+.B -yscrollcommand \fIcommand\fP
+Specifies the prefix for a command used to communicate with vertical
+scrollbars.  This option is treated in the same way as the
+\f5-xscrollcommand\fR option, except that it is used for vertical
+scrollbars and is provided by widgets that support vertical scrolling.
+See the description of \f5-xscrollcommand\fR for details
+on how this option is used.
--- /dev/null
+++ b/man/9/pack
@@ -1,0 +1,227 @@
+.TH PACK 9
+.SH NAME
+pack \- Geometry manager that packs around edges of cavity
+.SH SYNOPSIS
+\f5pack \fIoption arg \fR?\fIarg ...\fR?
+
+.SH DESCRIPTION
+The \f5pack\fR command is used to communicate with the packer,
+a geometry manager that arranges the children of a parent by
+packing them in order around the edges of the parent.
+The \f5pack\fR command can have any of several forms, depending
+on the \fIoption\fR argument:
+.TP
+\f5pack \fIslave \fR?\fIslave ...\fR? ?\fIoptions\fR?
+If the first argument to \f5pack\fR is a window name (any value
+starting with ``.''), then the command is processed in the same
+way as \f5pack configure\fR.
+.TP
+\f5pack configure \fIslave \fR?\fIslave ...\fR? ?\fIoptions\fR?
+The arguments consist of the names of one or more slave windows
+followed by pairs of arguments that specify how
+to manage the slaves.
+See ``THE PACKER ALGORITHM'' below for details on how the options
+are used by the packer.
+The following options are supported:
+.RS
+.TP
+\f5-after \fIother\fR
+\fIOther\fR must be the name of another window.
+Use its master as the master for the slaves, and insert
+the slaves just after \fIother\fR in the packing order.
+.TP
+\f5-anchor \fIanchor\fR
+\fIAnchor\fR must be a valid anchor position such as \f5n\fR
+or \f5sw\fR; it specifies where to position each slave in its
+parcel.
+Defaults to \f5center\fR.
+.TP
+\f5-before \fIother\fR
+\fIOther\fR must be the name of another window.
+Use its master as the master for the slaves, and insert
+the slaves just before \fIother\fR in the packing order.
+.TP
+\f5-expand \fIboolean\fR
+Specifies whether the slaves should be expanded to consume
+extra space in their master.
+\fIBoolean\fR may have any proper boolean value, such as \f51\fR
+or \f5no\fR.
+Defaults to 0.
+.TP
+\f5-fill \fIstyle\fR
+If a slave's parcel is larger than its requested dimensions, this
+option may be used to stretch the slave.
+\fIStyle\fR must have one of the following values:
+.RS
+.TP
+\f5none\fR
+Give the slave its requested dimensions plus any internal padding
+requested with \f5-ipadx\fR or \f5-ipady\fR.  This is the default.
+.TP
+\f5x\fR
+Stretch the slave horizontally to fill the entire width of its
+parcel (except leave external padding as specified by \f5-padx\fR).
+.TP
+\f5y\fR
+Stretch the slave vertically to fill the entire height of its
+parcel (except leave external padding as specified by \f5-pady\fR).
+.TP
+\f5both\fR
+Stretch the slave both horizontally and vertically.
+.RE
+.TP
+\f5-in \fIother\fR
+Insert the slave(s) at the end of the packing order for the master
+window given by \fIother\fR.
+.TP
+\f5-ipadx \fIdist\fR
+\fIDist\fR specifies how much horizontal internal padding to
+leave on each side of the slave(s).
+\fIDist\fR must be a valid screen distance, such as \f52\fR or \f5.5c\fR.
+It defaults to 0.
+.TP
+\f5-ipady \fIdist\fR
+\fIDist\fR specifies how much vertical internal padding to
+leave on each side of the slave(s).
+\fIDist\fR  defaults to 0.
+.TP
+\f5-padx \fIdist\fR
+\fIDist\fR specifies how much horizontal external padding to
+leave on each side of the slave(s).
+\fIDist\fR defaults to 0.
+.TP
+\f5-pady \fIdist\fR
+\fIDist\fR specifies how much vertical external padding to
+leave on each side of the slave(s).
+\fIDist\fR defaults to 0.
+.TP
+\f5-side \fIside\fR
+Specifies which side of the master the slave(s) will be packed against.
+Must be \f5left\fR, \f5right\fR, \f5top\fR, or \f5bottom\fR.
+Defaults to \f5top\fR.
+.LP
+If no \f5-in\fR, \f5-after\fR or \f5-before\fR option is specified
+then each of the slaves will be inserted at the end of the packing list
+for its parent unless it is already managed by the packer (in which
+case it will be left where it is).
+If one of these options is specified then all the slaves will be
+inserted at the specified point.
+If any of the slaves are already managed by the geometry manager
+then any unspecified options for them retain their previous values rather
+than receiving default values.
+.RE
+.TP
+\f5pack forget \fIslave \fR?\fIslave ...\fR?
+Removes each of the \fIslave\fRs from the packing order for its
+master and unmaps their windows.
+The slaves will no longer be managed by the packer.
+.TP
+\f5pack propagate \fImaster\fR ?\fIboolean\fR?
+If \fIboolean\fR has a true boolean value such as \f51\fR or \f5on\fR
+then propagation is enabled for \fImaster\fR, which must be a window
+name (see ``GEOMETRY PROPAGATION'' below).
+If \fIboolean\fR has a false boolean value then propagation is
+disabled for \fImaster\fR.
+In either of these cases an empty string is returned.
+If \fIboolean\fR is omitted then the command returns \f50\fR or
+\f51\fR to indicate whether propagation is currently enabled
+for \fImaster\fR.
+Propagation is enabled by default.
+.TP
+\f5pack slaves \fImaster\fR
+Returns a list of all of the slaves in the packing order for \fImaster\fR.
+The order of the slaves in the list is the same as their order in
+the packing order.
+If \fImaster\fR has no slaves then an empty string is returned.
+
+.SH "THE PACKER ALGORITHM"
+For each master the packer maintains an ordered list of slaves
+called the \fIpacking list\fR.
+The \f5-in\fR, \f5-after\fR, and \f5-before\fR configuration
+options are used to specify the master for each slave and the slave's
+position in the packing list.
+If none of these options is given for a slave then the slave
+is added to the end of the packing list for its parent.
+.PP
+The packer arranges the slaves for a master by scanning the
+packing list in order.
+At the time it processes each slave, a rectangular area within
+the master is still unallocated.
+This area is called the \fIcavity\fR;  for the first slave it
+is the entire area of the master.
+.PP
+For each slave the packer carries out the following steps:
+.IP [1]
+The packer allocates a rectangular \fIparcel\fR for the slave
+along the side of the cavity given by the slave's \f5-side\fR option.
+If the side is top or bottom then the width of the parcel is
+the width of the cavity and its height is the requested height
+of the slave plus the \f5-ipady\fR and \f5-pady\fR options.
+For the left or right side the height of the parcel is
+the height of the cavity and the width is the requested width
+of the slave plus the \f5-ipadx\fR and \f5-padx\fR options.
+The parcel may be enlarged further because of the \f5-expand\fR
+option (see ``EXPANSION'' below)
+.IP [2]
+The packer chooses the dimensions of the slave.
+The width will normally be the slave's requested width plus
+twice its \f5-ipadx\fR option and the height will normally be
+the slave's requested height plus twice its \f5-ipady\fR
+option.
+However, if the \f5-fill\fR option is \f5x\fR or \f5both\fR
+then the width of the slave is expanded to fill the width of the parcel,
+minus twice the \f5-padx\fR option.
+If the \f5-fill\fR option is \f5y\fR or \f5both\fR
+then the height of the slave is expanded to fill the width of the parcel,
+minus twice the \f5-pady\fR option.
+.IP [3]
+The packer positions the slave over its parcel.
+If the slave is smaller than the parcel then the \f5-anchor\fR
+option determines where in the parcel the slave will be placed.
+If \f5-padx\fR or \f5-pady\fR is non-zero, then the given
+amount of external padding will always be left between the
+slave and the edges of the parcel.
+.PP
+Once a given slave has been packed, the area of its parcel
+is subtracted from the cavity, leaving a smaller rectangular
+cavity for the next slave.
+If a slave doesn't use all of its parcel, the unused space
+in the parcel will not be used by subsequent slaves.
+If the cavity should become too small to meet the needs of
+a slave then the slave will be given whatever space is
+left in the cavity.
+If the cavity shrinks to zero size, then all remaining slaves
+on the packing list will be unmapped from the screen until
+the master window becomes large enough to hold them again.
+
+.SH EXPANSION
+If a master window is so large that there will be extra space
+left over after all of its slaves have been packed, then the
+extra space is distributed uniformly among all of the slaves
+for which the \f5-expand\fR option is set.
+Extra horizontal space is distributed among the expandable
+slaves whose \f5-side\fR is \f5left\fR or \f5right\fR,
+and extra vertical space is distributed among the expandable
+slaves whose \f5-side\fR is \f5top\fR or \f5bottom\fR.
+
+.SH "GEOMETRY PROPAGATION"
+The packer normally computes how large a master must be to
+just exactly meet the needs of its slaves, and it sets the
+requested width and height of the master to these dimensions.
+This causes geometry information to propagate up through a
+window hierarchy to a top-level window so that the entire
+sub-tree sizes itself to fit the needs of the leaf windows.
+However, the \f5pack propagate\fR command may be used to
+turn off propagation for one or more masters.
+If propagation is disabled then the packer will not set
+the requested width and height of the packer.
+This may be useful if, for example, you wish for a master
+window to have a fixed size that you specify.
+
+.SH "RESTRICTIONS ON MASTER WINDOWS"
+The master for each slave must be a frame widget or the top-level window (``.'').
+Widgets of other types can be specifed as the master window
+but will give rise to unpredictable results.
+
+.SH SEE ALSO
+.IR types (9)
--- /dev/null
+++ b/man/9/panel
@@ -1,0 +1,121 @@
+.TH PANEL 9
+.SH NAME
+panel \- embedded graphics
+.SH SYNOPSIS
+\f5panel\fI \fIpathName \fR?\fIoptions\fR?
+.SH STANDARD OPTIONS
+.EX
+-anchor       -borderwidth  -background   -relief
+.EE
+.SH "WIDGET-SPECIFIC OPTIONS"
+.TP
+.B -height \fIdist\fP
+Specifies a desired height for the panel.
+If this option isn't specified, the panel's desired height is computed
+from the image and mask images. If these are replicated,
+the default is zero.
+.TP
+.B -width \fIdist\fP
+Specifies a desired width for the panel.
+If this option isn't specified, the panel's desired height is computed
+from the image and mask images. If these are replicated,
+the default is zero.
+.SH DESCRIPTION
+The \f5panel\fR command creates a new window (given by the \fIpathName\fR
+argument) and makes it into a panel widget.
+Additional options, described above, may be specified on the
+command line
+to configure aspects of the panel such as its background colour and 3-D relief.
+The \f5panel\fR command returns its
+\fIpathName\fR argument.  At the time this command is invoked,
+there must not exist a window named \fIpathName\fR.
+.PP
+A panel is a widget that displays arbitrary graphics drawn outside
+of Tk. An application is free to use any image or mask that it
+chooses to create; the panel will display the contents of the image
+through the mask (see
+.IR draw (2)).
+.PP
+A panel widget will not display anything until its image has been
+set using
+.B putimage
+(see
+.IR tk (2)).
+
+.SH "WIDGET COMMAND"
+The \f5panel\fR command creates a new Tk command whose name
+is \fIpathName\fR. This command may be used to invoke various
+operations on the widget: It has the following general form:
+.RS
+.EX
+\fIpathName option \fR?\fIarg arg ...\fR?
+.EE
+.RE
+\fIOption\fR and the \fIarg\fRs
+determine the exact behaviour of the command.  The following
+commands are possible for panel widgets:
+.TP
+\fIpathName \f5cget\fR \fIoption\fR
+Returns the current value of the configuration option given
+by \fIoption\fR.
+\fIOption\fR may have any of the values accepted by the \f5panel\fR
+function.
+.TP
+\fIpathName \f5configure\fR ?\fIoption\fR? ?\fIvalue option value ...\fR?
+Query or modify the configuration options of the widget.
+If no \fIoption\fR is specified, returns a list of all of
+the available options for \fIpathName\fR.  If
+one or more \fIoption-value\fR pairs are specified, then the command
+modifies the given widget option(s) to have the given value(s);  in
+this case the command returns an empty string.
+\fIOption\fR may have any of the values accepted by the \f5panel\fR
+function
+.TP
+\fIpathName \f5dirty\fR ?\fIminx miny maxx maxy\fR?
+Mark the area inside the given rectangle
+((\fIminx\fP, \fIminy\fP), (\fImaxx\fP, \fImaxy\fP))
+to be flushed to the screen. If the rectangle is not
+given, the whole area will be marked.
+The coordinates are relative to the image's origin.
+.TP
+\fIpathName \f5origin\fR ?\fIx y\fR?
+Set the point within the image that will be displayed
+at the top left of the panel's rectangle.
+If the area allocated to the panel is more than
+the area drawn by the image, the ``top left'' will
+positioned with respect to the value of the
+.B -anchor
+option.
+If the point is not given, the current origin
+is returned.
+.TP
+\fIpathName \f5panelx\fR ?\fIscreenx\fR?
+Given a screen x-coordinate,
+.IR screenx ,
+this command returns the image x-coordinate displayed
+at that location.
+.TP
+\fIpathName \f5panely\fR ?\fIscreeny\fR?
+Given a screen y-coordinate,
+.IR screeny ,
+this command returns the image y-coordinate displayed
+at that location.
+.TP
+\fIpathName \f5screenx\fR ?\fIpanelx\fR?
+Given an image x-coordinate,
+.IR panelx ,
+this command returns the
+equivalent screen x-coordinate.
+.TP
+\fIpathName \f5screeny\fR ?\fIpanely\fR?
+Given an image y-coordinate,
+.IR panely ,
+this command returns the
+equivalent screen y-coordinate.
+.SH BINDINGS
+When a new panel is created, it has no default event bindings.
+.SH SEE ALSO
+.IR draw (2),
+.IR tk (2),
+.IR options (9),
+.IR types (9)
--- /dev/null
+++ b/man/9/radiobutton
@@ -1,0 +1,192 @@
+.TH RADIOBUTTON 9
+.SH NAME
+radiobutton \- Create and manipulate radiobutton widgets
+.SH SYNOPSIS
+\f5radiobutton\fI \fIpathName \fR?\fIoptions\fR?
+.SH STANDARD OPTIONS
+.EX
+-activebackground -disabledcolor      -justify
+-activeforeground -font               -relief
+-anchor           -foreground         -takefocus
+-background       -highlightcolor     -text
+-bitmap           -highlightthickness -underline
+-borderwidth      -image
+.EE
+.SH "WIDGET-SPECIFIC OPTIONS"
+.TP
+.B -command \fIcommand\fP
+Specifies a Tk command to associate with the button.  This command
+is typically invoked when mouse button 1 is released over the button
+window.  The button's global variable (\f5-variable\fR option) will
+be updated before the command is invoked.
+.TP
+.B -height \fIdist\fP
+Specifies a desired height for the button.
+If this option isn't specified, the button's desired height is computed
+from the size of the image or bitmap or text being displayed in it.
+.TP
+.B -indicatoron \fIboolean\fP
+Specifies whether or not the indicator should be drawn.
+If false, the \f5relief\fP option is ignored and the widget's
+relief is always \f5sunken\fP if the widget is selected
+and \f5raised\fP otherwise.
+.TP
+.B -selectcolor \fIcolour\fP
+Specifies a background colour to use when the button is selected.
+If \f5indicatoron\fR is true, the colour applies to the indicator.
+If \f5indicatoron\fR is false, this colour is used as the background
+for the entire widget, in place of \f5background\fR or \f5activebackground\fR,
+whenever the widget is selected.
+If specified as an empty string, no special colour is used for
+displaying when the widget is selected.
+.ig
+.TP
+.B -selectimage \fIimage\fP
+Specifies an image to display (in place of the \f5image\fR option)
+when the radiobutton is selected.
+This option is ignored unless the \f5image\fR option has been
+specified.
+..
+.TP
+.B -state \fIstate\fP
+Specifies one of three states for the radiobutton:  \f5normal\fR, \f5active\fR,
+or \f5disabled\fR.  In normal state the radiobutton is displayed using the
+\f5foreground\fR and \f5background\fR options.  The active state is
+typically used when the pointer is over the radiobutton.  In active state
+the radiobutton is displayed using the \f5activeforeground\fR and
+\f5activebackground\fR options.  Disabled state means that the radiobutton
+should be insensitive:  the default bindings will refuse to activate
+the widget and will ignore mouse button presses.
+In this state the \f5disabledcolor\fR and
+\f5background\fR options determine how the radiobutton is displayed.
+.TP
+.B -value \fIstring\fP
+Specifies value to store in the button's associated variable whenever
+this button is selected.
+.TP
+.B -variable \fIstring\fP
+Specifies name of global variable to set whenever this button is
+selected.  Changes in this variable also cause the button to select
+or deselect itself.
+Defaults to the value \f5selectedButton\fR.
+.TP
+.B -width \fIdist\fP
+Specifies a desired width for the button.
+If this option isn't specified, the button's desired width is computed
+from the size of the image or bitmap or text being displayed in it.
+.SH DESCRIPTION
+The \f5radiobutton\fR command creates a new window (given by the
+\fIpathName\fR argument) and makes it into a radiobutton widget.
+Additional
+options, described above, may be specified on the command line
+to configure aspects of the radiobutton such as its colours, font,
+text, and initial relief.  The \f5radiobutton\fR command returns its
+\fIpathName\fR argument.  At the time this command is invoked,
+there must not exist a window named \fIpathName\fR.
+.PP
+A radiobutton is a widget that displays a textual string, bitmap or image
+and a circle called an \fIindicator\fR.
+If text is displayed, it must all be in a single font, but it
+can occupy multiple lines on the screen (if it contains newlines) and
+one of the characters may optionally be underlined using the
+\f5underline\fR option.  A radiobutton has
+all of the behaviour of a simple button: it can display itself in either
+of three different ways, according to the \f5state\fR option;
+it can be made to appear
+raised, sunken, or flat; and it invokes
+a Tk command whenever mouse button 1 is clicked over the
+check button.
+.PP
+In addition, radiobuttons can be \fIselected\fR.
+If a radiobutton is selected, the indicator is normally
+drawn as a circle containing a disc (possibly in a special colour), and
+a Tk variable associated with the radiobutton is set to a particular
+value.
+If the radiobutton is not selected, the indicator is drawn as an
+empty circle.
+Typically, several radiobuttons share a single variable and the
+value of the variable indicates which radiobutton is to be selected.
+When a radiobutton is selected it sets the value of the variable to
+indicate that fact;  each radiobutton also monitors the value of
+the variable and automatically selects and deselects itself when the
+variable's value changes.
+By default the variable \f5selectedButton\fR
+is used;  its contents give the name of the button that is
+selected, or the empty string if no button associated with that
+variable is selected.
+The name of the variable for a radiobutton,
+plus the variable to be stored into it, may be modified with options
+on the command line.
+Configuration options may also be used to modify the way the
+indicator is displayed (or whether it is displayed at all).
+By default a radiobutton is configured to select itself on button clicks.
+.SH "WIDGET COMMAND"
+The \f5radiobutton\fR command creates a new Tk command whose
+name is \fIpathName\fR.  This
+command may be used to invoke various
+operations on the widget.  It has the following general form:
+.RS
+.EX
+\fIpathName option \fR?\fIarg arg ...\fR?
+.EE
+.RE
+\fIOption\fR and the \fIarg\fRs
+determine the exact behaviour of the command.  The following
+commands are possible for radiobutton widgets:
+.TP
+\fIpathName \f5cget\fR \fIoption\fR
+Returns the current value of the configuration option given
+by \fIoption\fR.
+\fIOption\fR may have any of the values accepted by the \f5radiobutton\fR
+command.
+.TP
+\fIpathName \f5configure\fR ?\fIoption\fR? ?\fIvalue option value ...\fR?
+Query or modify the configuration options of the widget.
+If no \fIoption\fR is specified, returns a list of all of
+the available options for \fIpathName\fR.  If
+one or more \fIoption-value\fR pairs are specified, the command
+modifies the given widget option(s) to have the given value(s);  in
+this case the command returns an empty string.
+\fIOption\fR may have any of the values accepted by the \f5radiobutton\fR
+command.
+.TP
+\fIpathName \f5deselect\fR
+Deselects the radiobutton and sets the associated variable to an
+empty string.
+If this radiobutton was not currently selected, the command has
+no effect.
+.TP
+\fIpathName \f5invoke\fR
+Does just what would have happened if the user invoked the radiobutton
+with the mouse: selects the button and invokes
+its associated Tk command, if there is one.
+The return value is the return value from the Tk command, or an
+empty string if there is no command associated with the radiobutton.
+This command is ignored if the radiobutton's state is \f5disabled\fR.
+.TP
+\fIpathName \f5select\fR
+Selects the radiobutton and sets the associated variable to the
+value corresponding to this widget.
+
+.SH BINDINGS
+Tk automatically creates bindings for radiobuttons that give them
+the following default behaviour:
+.IP [1]
+The radiobutton activates whenever the mouse passes over it and deactivates
+whenever the mouse leaves the radiobutton.
+.IP [2]
+When mouse button 1 is pressed over a radiobutton it is invoked (it
+becomes selected and the command associated with the button is
+invoked, if there is one).
+.PP
+If the radiobutton's state is \f5disabled\fR then none of the above
+actions occur:  the radiobutton is completely non-responsive.
+.PP
+The behaviour of radiobuttons can be changed by defining new bindings for
+individual widgets.
+.SH SEE ALSO
+.IR button (9),
+.IR checkbutton (9),
+.IR choicebutton (9),
+.IR options (9),
+.IR types (9)
--- /dev/null
+++ b/man/9/raise
@@ -1,0 +1,13 @@
+.TH RAISE 9
+.SH NAME
+raise \- Change a window's position in the stacking order
+.SH SYNOPSIS
+\f5raise \fIwindow
+.SH DESCRIPTION
+.B Raise
+raises
+\fIwindow\fR so that it is above all of its siblings in the stacking
+order (it will not be obscured by any siblings and will obscure
+any siblings that overlap it).
+.SH "SEE ALSO"
+.IR lower (9)
--- /dev/null
+++ b/man/9/scale
@@ -1,0 +1,198 @@
+.TH SCALE 9
+.SH NAME
+scale \- Create and manipulate scale widgets
+.SH SYNOPSIS
+\f5scale\fI \fIpathName \fR?\fIoptions\fR?
+.SH STANDARD OPTIONS
+.EX
+-activebackground -font               -orient
+-background       -foreground         -relief
+-borderwidth      -highlightcolor     -takefocus
+-disabledcolor    -highlightthickness
+.EE
+.SH "WIDGET-SPECIFIC OPTIONS"
+.TP
+.B -bigincrement \fIdist\fR
+Some interactions with the scale cause its value to change by
+``large'' increments;  this option specifies the size of the
+large increments.  If specified as 0, the large increments default
+to 1/10 the range of the scale.
+.TP
+.B -command \fIcommand\fR
+Specifies the prefix of a Tk command to invoke whenever the scale's
+value is changed via a widget command.
+The actual command consists
+of this option followed by a space and a real number indicating the
+new value of the scale.
+.TP
+.B -from \fIfrac\fR
+A real value corresponding to the left or top end of the scale.
+.TP
+.B -height \fIdist\fP
+Specifies a desired height for the scale.
+If this option isn't specified, the scale is given a default height.
+.TP
+.B -label \fIstring\fR
+A string to display as a label for the scale. If the option is specified
+as an empty string, no label is displayed.
+.TP
+.B -resolution \fIfrac\fR
+A real value specifying the resolution for the scale.
+If this value is greater than zero then the scale's value will always be
+rounded to an even multiple of this value, as will tick marks and
+the endpoints of the scale.  If the value is less than zero then no
+rounding occurs.  Defaults to 1 (i.e., the value will be integral).
+.TP
+.B -showvalue \fIboolean\fR
+Specifies a boolean value indicating whether or not the current
+value of the scale is to be displayed.
+.TP
+.B -sliderlength \fIdist\fR
+Specfies the size of the slider along the slider's
+long dimension..
+.TP
+.B -sliderrelief \fIrelief\fI
+Specifies the relief to use when drawing the slider.
+.TP
+.B -state \fIstate\fR
+Specifies one of three states for the scale:  \f5normal\fR,
+\f5active\fR, or \f5disabled\fR.
+If the scale is disabled then the value may not be changed and the scale
+won't activate.
+If the scale is active, the slider is displayed using the colour
+specified by the \f5activebackground\fR option.
+.TP
+.B -tickinterval \fIfrac\fR
+Must be a real value.
+Determines the spacing between numerical
+tick marks displayed below or to the left of the slider.
+If 0, no tick marks will be displayed.
+.TP
+.B -to \fIfrac\fR
+Specifies a real value corresponding
+to the right or bottom end of the scale.
+This value may be either less than or greater than the \f5from\fR option.
+.TP
+.B -width \fIdist\fP
+Specifies a desired width for the scale.
+If this option isn't specified, the scale is given a default width.
+.SH DESCRIPTION
+The \f5scale\fR command creates a new window (given by the
+\fIpathName\fR argument) and makes it into a scale widget.
+Additional
+options, described above, may be specified on the command line
+to configure aspects of the scale such as its colours, orientation,
+and relief.  The \f5scale\fR command returns its
+\fIpathName\fR argument.  At the time this command is invoked,
+there must not exist a window named \fIpathName\fR.
+.PP
+A scale is a widget that displays a rectangular \fItrough\fR and a
+small \fIslider\fR.  The trough corresponds to a range
+of real values (determined by the \f5from\fR, \f5to\fR, and
+\f5resolution\fR options),
+and the position of the slider selects a particular real value.
+The slider's position (and hence the scale's value) may be adjusted
+with the mouse or keyboard as described in the BINDINGS
+section below.  Whenever the scale's value is changed, a Tk
+command is invoked (using the \f5command\fR option) to notify
+other interested widgets of the change.
+.PP
+Three annotations may be displayed in a scale widget:  a label
+appearing at the top right of the widget (top left for horizontal
+scales), a number displayed just to the left of the slider
+(just above the slider for horizontal scales), and a collection
+of numerical tick marks just to the right of the trough
+(just below the trough for horizontal scales).  Each of these three
+annotations may be enabled or disabled using the
+configuration options.
+
+.SH "WIDGET COMMAND"
+The \f5scale\fR command creates a new Tk command whose
+name is \fIpathName\fR.  This
+command may be used to invoke various
+operations on the widget.  It has the following general form:
+.RS
+.EX
+\fIpathName option \fR?\fIarg arg ...\fR?
+.EE
+.RE
+\fIOption\fR and the \fIarg\fRs
+determine the exact behaviour of the command.  The following
+commands are possible for scale widgets:
+.TP
+\fIpathName \f5cget\fR \fIoption\fR
+Returns the current value of the configuration option given
+by \fIoption\fR.
+\fIOption\fR may have any of the values accepted by the \f5scale\fR
+command.
+.TP
+\fIpathName \f5configure\fR ?\fIoption\fR? ?\fIvalue option value ...\fR?
+Query or modify the configuration options of the widget.
+If no \fIoption\fR is specified, returns a list of all of
+the available options for \fIpathName\fR.  If
+one or more \fIoption-value\fR pairs are specified, then the command
+modifies the given widget option(s) to have the given value(s);  in
+this case the command returns an empty string.
+\fIOption\fR may have any of the values accepted by the \f5scale\fR
+command.
+.TP
+\fIpathName \f5coords \fR?\fIvalue\fR?
+Returns a list whose elements are the x and y coordinates of
+the point along the centreline of the trough that corresponds
+to \fIvalue\fR.
+If \fIvalue\fR is omitted then the scale's current value is used.
+.TP
+\fIpathName \f5get\fR ?\fIx y\fR?
+If \fIx\fR and \fIy\fR are omitted, returns the current value
+of the scale.  If \fIx\fR and \fIy\fR are specified, they give
+pixel coordinates within the widget;  the command returns
+the scale value corresponding to the given pixel.
+Only one of \fIx\fR or \fIy\fR is used:  for horizontal scales
+\fIy\fR is ignored, and for vertical scales \fIx\fR is ignored.
+.TP
+\fIpathName \f5identify\fR \fIx y\fR
+Returns a string indicating what part of the scale lies under
+the coordinates given by \fIx\fR and \fIy\fR.
+A return value of \f5slider\fR means that the point is over
+the slider;  \f5trough1\fR means that the point is over the
+portion of the slider above  or to the left of the slider;
+and \f5trough2\fR means that the point is over the portion
+of the slider below or to the right of the slider.
+If the point isn't over one of these elements, an empty string
+is returned.
+.TP
+\fIpathName \f5set\fR \fIvalue\fR
+This command is invoked to change the current value of the scale,
+and hence the position at which the slider is displayed.  \fIValue\fR
+gives the new value for the scale.
+The command has no effect if the scale is disabled.
+.SH BINDINGS
+Tk automatically creates bindings for scales that give them
+the following default behaviour.
+Where the behaviour is different for vertical and horizontal scales,
+the horizontal behaviour is described in parentheses.
+.IP [1]
+If button 1 is pressed in the trough, the scale's value will
+be incremented or decremented by the value of the \f5bigincrement\fR
+option so that the slider moves in the direction of the cursor. 
+If the button is held down, the action auto-repeats.
+.IP [2]
+If button 1 is pressed over the slider, the slider can be dragged
+with the mouse.
+.IP [3]
+If the widget receives Up-arrow or Left-arrow key events, the scale's value
+will be incremented or decremented so as to adjust the value towards the
+.BR from value .
+.IP [4]
+If the widget receives Down-arrow or Right-arrow key events, the scale's value
+will be incremented or decremented so as to adjust the value towards the
+.BR to value .
+.PP
+If the scale is disabled using the \f5state\fR option then
+none of the above bindings have any effect.
+.PP
+The behaviour of scales can be changed by defining new bindings for
+individual widgets.
+.SH SEE ALSO
+.IR options (9),
+.IR types (9)
--- /dev/null
+++ b/man/9/scrollbar
@@ -1,0 +1,243 @@
+.TH SCROLLBAR 9
+.SH NAME
+scrollbar \- Create and manipulate scrollbar widgets
+.SH SYNOPSIS
+\f5scrollbar\fI pathName \fR?\fIoptions\fR?
+.SH STANDARD OPTIONS
+.EX
+-activebackground -borderwidth      -orient
+-background       -jump             -relief
+.EE
+.SH "WIDGET-SPECIFIC OPTIONS"
+.TP
+.B -activerelief \fIrelief\fP
+Specifies the relief to use when displaying the element that is
+active, if any.
+Elements other than the active element are always displayed with
+a raised relief.
+.TP
+.B -command \fIcommand\fP
+Specifies the prefix of a Tk command to invoke to change the view
+in the widget associated with the scrollbar.  When a user requests
+a view change by manipulating the scrollbar, a Tk command is
+invoked.  The actual command consists of this option followed by
+additional information as described later.  This option almost always has
+a value such as \f5.t xview\fR or \f5.t yview\fR, consisting of the
+name of a widget and either \f5xview\fR (if the scrollbar is for
+horizontal scrolling) or \f5yview\fR (for vertical scrolling).
+All scrollable widgets have \f5xview\fR and \f5yview\fR commands
+that take exactly the additional arguments appended by the scrollbar
+as described in SCROLLING COMMANDS below.
+.TP
+.B -height \fIdist\fP
+Specifies a desired height for the scrollbar.
+If this option isn't specified, a suitable default height is chosen.
+.TP
+.B -width \fIdist\fP
+Specifies a desired width for the scrollbar.
+If this option isn't specified, a suitable default width is chosen.
+.SH DESCRIPTION
+The \f5scrollbar\fR command creates a new window (given by the
+\fIpathName\fR argument) and makes it into a scrollbar widget.
+Additional options, described above, may be specified on the command
+line to configure aspects of the scrollbar
+such as its colours, orientation, and relief.
+The \f5scrollbar\fR command returns its \fIpathName\fR argument.
+At the time this command is invoked, there must not exist a window
+named \fIpathName\fR.
+.PP
+A scrollbar is a widget that displays two arrows, one at each end of
+the scrollbar, and a \fIslider\fR in the middle portion of the
+scrollbar.
+It provides information about what is visible in an \fIassociated window\fR
+that displays a document of some sort (such as a file being edited or
+a drawing).
+The position and size of the slider indicate which portion of the
+document is visible in the associated window.  For example, if the
+slider in a vertical scrollbar covers the top third of the area
+between the two arrows, it means that the associated window displays
+the top third of its document.
+.PP
+Scrollbars can be used to adjust the view in the associated window
+by clicking or dragging with the mouse.  See the BINDINGS section
+below for details.
+
+.SH ELEMENTS
+A scrollbar displays five elements, which are referred to in the
+widget commands for the scrollbar:
+.TP 12
+\f5arrow1\fR
+The top or left arrow in the scrollbar.
+.TP 12
+\f5trough1\fR
+The region between the slider and \f5arrow1\fR.
+.TP 12
+\f5slider\fR
+The rectangle that indicates what is visible in the associated widget.
+.TP 12
+\f5trough2\fR
+The region between the slider and \f5arrow2\fR.
+.TP 12
+\f5arrow2\fR
+The bottom or right arrow in the scrollbar.
+
+.SH "WIDGET COMMAND"
+The \f5scrollbar\fR command creates a new Tk command whose
+name is \fIpathName\fR.  This
+command may be used to invoke various
+operations on the widget.  It has the following general form:
+.RS
+.EX
+\fIpathName option \fR?\fIarg arg ...\fR?
+.EE
+.RE
+\fIOption\fR and the \fIarg\fRs
+determine the exact behaviour of the command.  The following
+commands are possible for scrollbar widgets:
+.TP
+\fIpathName \f5activate \fR?\fIelement\fR?
+Marks the element indicated by \fIelement\fR as active, which
+causes it to be displayed as specified by the \f5activebackground\fR option.
+The only element values understood by this command are \f5arrow1\fR,
+\f5slider\fR, or \f5arrow2\fR.
+If any other value is specified then no element of the scrollbar
+will be active.
+If \fIelement\fR is not specified, the command returns
+the name of the element that is currently active, or an empty string
+if no element is active.
+.TP
+\fIpathName \f5cget\fR \fIoption\fR
+Returns the current value of the configuration option given
+by \fIoption\fR.
+\fIOption\fR may have any of the values accepted by the \f5scrollbar\fR
+command.
+.TP
+\fIpathName \f5configure\fR ?\fIoption\fR? ?\fIvalue option value ...\fR?
+Query or modify the configuration options of the widget.
+If no \fIoption\fR is specified, returns a list of all of
+the available options for \fIpathName\fR.  If
+one or more \fIoption-value\fR pairs are specified, then the command
+modifies the given widget option(s) to have the given value(s);  in
+this case the command returns an empty string.
+\fIOption\fR may have any of the values accepted by the \f5scrollbar\fR
+command.
+.TP
+\fIpathName \f5delta \fIdeltaX deltaY\fR
+Returns a real number indicating the fractional change in
+the scrollbar setting that corresponds to a given change
+in slider position.  For example, if the scrollbar is horizontal,
+the result indicates how much the scrollbar setting must change
+to move the slider \fIdeltaX\fR pixels to the right (\fIdeltaY\fR is
+ignored in this case).
+If the scrollbar is vertical, the result indicates how much the
+scrollbar setting must change to move the slider \fIdeltaY\fR pixels
+down.  The arguments and the result may be zero or negative.
+.TP
+\fIpathName \f5fraction \fIx y\fR
+Returns a real number between 0 and 1 indicating where the point
+given by \fIx\fR and \fIy\fR lies in the trough area of the scrollbar.
+The value 0 corresponds to the top or left of the trough, the
+value 1 corresponds to the bottom or right, 0.5 corresponds to
+the middle, and so on.
+\fIX\fR and \fIy\fR must be pixel coordinates relative to the scrollbar
+widget.
+If \fIx\fR and \fIy\fR refer to a point outside the trough, the closest
+point in the trough is used.
+.TP
+\fIpathName \f5get\fR
+Returns the scrollbar settings in the form of a list whose
+elements are the arguments to the most recent \f5set\fR widget command.
+.TP
+\fIpathName \f5identify\fR \fIx y\fR
+Returns the name of the element under the point given by \fIx\fR and
+\fIy\fR (such as \f5arrow1\fR), or an empty string if the point does
+not lie in any element of the scrollbar.
+\fIX\fR and \fIy\fR must be pixel coordinates relative to the scrollbar
+widget.
+.TP
+\fIpathName \f5set\fR \fIfirst last\fR
+This command is invoked by the scrollbar's associated widget to
+tell the scrollbar about the current view in the widget.
+The command takes two arguments, each of which is a real fraction
+between 0 and 1.
+The fractions describe the range of the document that is visible in
+the associated widget.
+For example, if \fIfirst\fR is 0.2 and \fIlast\fR is 0.4, it means
+that the first part of the document visible in the window is 20%
+of the way through the document, and the last visible part is 40%
+of the way through.
+
+.SH "SCROLLING COMMANDS"
+When the user interacts with the scrollbar, for example by dragging
+the slider, the scrollbar notifies the associated widget that it
+must change its view.
+The scrollbar makes the notification by evaluating a Tk command
+generated from the scrollbar's \f5-command\fR option.
+The command may take any of the following forms.
+In each case, \fIprefix\fR is the contents of the
+\f5-command\fR option, which usually has a form like \f5.t yview\fR
+.TP
+\fIprefix \f5moveto \fIfraction\fR
+\fIFraction\fR is a real number between 0 and 1.
+The widget should adjust its view so that the point given
+by \fIfraction\fR appears at the beginning of the widget.
+If \fIfraction\fR is 0 it refers to the beginning of the
+document.  1.0 refers to the end of the document, 0.333
+refers to a point one-third of the way through the document,
+and so on.
+.TP
+\fIprefix \f5scroll \fInumber \f5unit\fR
+The widget should adjust its view by \fInumber\fR units.
+The units are defined in whatever way makes sense for the widget,
+such as characters or lines in a text widget.
+\fINumber\fR is either 1, which means one unit should scroll off
+the top or left of the window, or -1, which means that one unit
+should scroll off the bottom or right of the window.
+.TP
+\fIprefix \f5scroll \fInumber \f5page\fR
+The widget should adjust its view by \fInumber\fR pages.
+It is up to the widget to define the meaning of a page;  typically
+it is slightly less than what fits in the window, so that there
+is a slight overlap between the old and new views.
+\fINumber\fR is either 1, which means the next page should
+become visible, or -1, which means that the previous page should
+become visible.
+
+.SH BINDINGS
+Tk automatically creates bindings for scrollbars that give them
+the following default behaviour.
+If the behaviour is different for vertical and horizontal scrollbars,
+the horizontal behaviour is described in parentheses.
+
+.IP [1]
+Pressing button 1 over \f5arrow1\fR causes the view in the
+associated widget to shift up (left) by one unit so that the
+document appears to move down (right) one unit.
+If the button is held down, the action auto-repeats.
+.IP [2]
+Pressing button 1 over \f5trough1\fR causes the view in the
+associated widget to shift up (left) by one screenful so that the
+document appears to move down (right) one screenful.
+.IP [3]
+Pressing button 1 over the slider and dragging causes the view
+to drag with the slider.
+If the \f5jump\fR option is true, then the view doesn't drag along
+with the slider;  it changes only when the mouse button is released.
+.IP [4]
+Pressing button 1 over \f5trough2\fR causes the view in the
+associated widget to shift down (right) by one screenful so that the
+document appears to move up (left) one screenful.
+.IP [5]
+Pressing button 1 over \f5arrow2\fR causes the view in the
+associated widget to shift down (right) by one unit so that the
+document appears to move up (left) one unit.
+If the button is held down, the action auto-repeats.
+.IP [6]
+If button 2 is pressed over the trough or the slider, it sets
+the view to correspond to the mouse position;  dragging the
+mouse with button 2 down causes the view to drag with the mouse.
+If button 2 is pressed over one of the arrows, it causes the
+same behaviour as pressing button 1.
+.SH SEE ALSO
+.IR options (9),
+.IR types (9)
--- /dev/null
+++ b/man/9/see
@@ -1,0 +1,37 @@
+.TH SEE 9
+.SH NAME
+see \- Make a portion of a widget visible.
+.SH SYNOPSIS
+\f5see \fIoption\fP ?\fIpathName\fR?
+.SH DESCRIPTION
+\f5See\fP makes sure that a portion of a widget
+is visible in its containing view; for instance, it will
+scroll a canvas or text widget to ensure that the
+specified portion is in view. The following options
+are supported:
+.TP
+\f5-rectangle\fP  \fIlist\fP
+Specifies a list with four \fIdist\fP coordinates describing the left, top,
+right, and bottom coordinates of the rectangular region,
+in coordinates relative to the widget origin, of the area
+that should be made visible.
+.TP
+\f5-point\fP \fIlist\P
+Specifies a list with two \fIdist\fP coordinates describing
+a point within the widget that should be made visible
+by preference if the entirety of the above-specified
+rectangle cannot be shown. Defaults to the top left of
+the specified rectangle.
+.TP
+\f5-where\fP
+If this option is given, \f5see\fP returns a list containing
+the left, top, right and bottom coordinates of
+the currently visible portion of
+.IR pathName .
+.PP
+If neither the \f5-rectangle\fP or the \f5-point\fP
+options are given, then the entire area of \fIpathName\fP
+will be made visible.
+.SH SEE ALSO
+.IR options (9),
+.IR types (9)
--- /dev/null
+++ b/man/9/send
@@ -1,0 +1,38 @@
+.TH SEND 9
+.SH NAME
+send \- send a value down a Tk channel
+.SH SYNOPSIS
+\f5send \fIchan\fP \fIstring\fP
+.SH DESCRIPTION
+.B Send
+is the gateway from the Tk world into the Limbo world.
+It sends
+.I string
+down
+.IR chan ,
+which should previously have been named with a call to
+.B namechan
+(see
+.IR tk (2)).
+Tk channels are non blocking, so the call will return immediately,
+whether the message has been sent or not. If too many messages
+have been queued on the
+.IR chan ,
+then the message will be
+discarded, and a warning printed on the console.
+.I String
+is not subject to interpretation by the usual Tk quoting rules.
+.SH BUGS
+.I String
+is not subject to interpretation by the usual Tk quoting rules.
+This means, for example that:
+.EX
+	radiobutton .x -text X -variable x -value x
+	radiobutton .y -text Y -variable y -value y
+	button .z -text Submit {send chan submit [variable X]}
+.EE
+will not work as expected. Instead, one must interrogate values
+directly in Limbo.
+.PP
+The specialised queued channels will be replaced by buffered channels in Limbo in
+a future edition of Inferno.
--- /dev/null
+++ b/man/9/text
@@ -1,0 +1,1188 @@
+.TH TEXT 9
+.SH NAME
+text \- Create and manipulate text widgets
+.SH SYNOPSIS
+\f5text\fI \fIpathName \fR?\fIoptions\fR?
+.SH STANDARD OPTIONS
+.EX
+-background  -pady              -takefocus
+-borderwidth -relief            -xscrollcommand
+-font        -selectbackground  -yscrollcommand
+-foreground  -selectborderwidth
+-padx        -selectforeground
+.EE
+.SH "WIDGET-SPECIFIC OPTIONS"
+.TP
+.B -height \fIdist\fP
+Specifies the desired height for the window.
+.TP
+.B -spacing1 \fIdist\fP
+Requests additional space above each text line in the widget,
+using any of the standard forms for screen distances.
+If a line wraps, this option only applies to the first line
+on the display.
+This option may be overriden with \f5-spacing1\fR options in
+tags.
+.TP
+.B -spacing2 \fIdist\fP
+For lines that wrap (so that they cover more than one line on the
+display) this option specifies additional space to provide between
+the display lines that represent a single line of text.
+The value may have any of the standard forms for screen distances.
+This option may be overriden with \f5-spacing2\fR options in
+tags.
+.TP
+.B -spacing3 \fIdist\fP
+Requests additional space below each text line in the widget,
+using any of the standard forms for screen distances.
+If a line wraps, this option only applies to the last line
+on the display.
+This option may be overriden with \f5-spacing3\fR options in
+tags.
+.TP
+.B -state \fPstate\fP
+Specifies one of two states for the text:  \f5normal\fR or \f5disabled\fR.
+If the text is disabled then characters may not be inserted or deleted
+and no insertion cursor will be displayed, even if the input focus is
+in the widget.
+.TP
+.B -tabs \fIdist\fP
+Specifies a set of tab stops for the window.  The option's value consists
+of a list of \fIdist\fP values giving the positions of the tab stops.  Each
+\fIdist\fP may optionally be followed in the next list element
+by one of the keywords \f5left\fR, \f5right\fR, \f5center\fR,
+or \f5numeric\fR, which specifies how to justify
+text relative to the tab stop.  \f5Left\fR is the default; it causes
+the text following the tab character to be positioned with its left edge
+at the tab position.  \f5Right\fR means that the right edge of the text
+following the tab character is positioned at the tab position, and
+\f5center\fR means that the text is centered at the tab position.
+\f5Numeric\fR means that the decimal point in the text is positioned
+at the tab position;  if there is no decimal point then the least
+significant digit of the number is positioned just to the left of the
+tab position;  if there is no number in the text then the text is
+right-justified at the tab position.
+For example, \f5-tabs {2c left 4c 6c center}\fR creates three
+tab stops at two-centimeter intervals;  the first two use left
+justification and the third uses center justification.
+If the list of tab stops does not have enough elements to cover all
+of the tabs in a text line, then Tk extrapolates new tab stops using
+the spacing and alignment from the last tab stop in the list.
+The value of the \f5tabs\fR option may be overridden by \f5-tabs\fR
+options in tags.
+If no \f5-tabs\fR option is specified, or if it is specified as
+an empty list, then Tk uses default tabs spaced every eight
+(average size) characters.
+.TP
+.B -width \fIdist\fP
+Specifies the desired width for the window.
+.TP
+.B -wrap \fIval\fP
+Specifies how to handle lines in the text that are too long to be
+displayed in a single line of the text's window.
+The value must be \f5none\fR or \f5char\fR or \f5word\fR.
+A wrap mode of \f5none\fR means that each line of text appears as
+exactly one line on the screen;  extra characters that don't fit
+on the screen are not displayed.
+In the other modes each line of text will be broken up into several
+screen lines if necessary to keep all the characters visible.
+In \f5char\fR mode a screen line break may occur after any character;
+in \f5word\fR mode a line break will only be made at word boundaries.
+
+.SH DESCRIPTION
+The \f5text\fR command creates a new window (given by the
+\fIpathName\fR argument) and makes it into a text widget.
+Additional
+options, described above, may be specified on the command line
+to configure aspects of the text such as its default background colour
+and relief.  The \f5text\fR command returns the
+path name of the new window.
+.PP
+A text widget displays one or more lines of text and allows that
+text to be edited.
+Text widgets support three different kinds of annotations on the
+text, called tags, marks, and embedded windows.
+Tags allow different portions of the text
+to be displayed with different fonts and colours.
+In addition, Tk commands can be associated with tags so
+that scripts are invoked when particular actions such as keystrokes
+and mouse button presses occur in particular ranges of the text.
+See TAGS below for more details.
+.PP
+The second form of annotation consists of marks, which are floating
+markers in the text.
+Marks are used to keep track of various interesting positions in the
+text as it is edited.
+See MARKS below for more details.
+.PP
+The third form of annotation allows arbitrary windows to be
+embedded in a text widget.
+See EMBEDDED WINDOWS below for more details.
+
+.SH INDICES
+Many of the widget commands for texts take one or more indices
+as arguments.
+An index is a string used to indicate a particular place within
+a text, such as a place to insert characters or one endpoint of a
+range of characters to delete.
+Indices have the syntax
+.RS
+.EX
+\fIbase modifier modifier modifier ...\fR
+.EE
+.RE
+Where \fIbase\fR gives a starting point and the \fImodifier\fRs
+adjust the index from the starting point (e.g. move forward or
+backward one character).  Every index must contain a \fIbase\fR,
+but the \fImodifier\fRs are optional.
+.PP
+The \fIbase\fR for an index must have one of the following forms:
+.TP 12
+\fIline\f5.\fIchar\fR
+Indicates \fIchar\fR'th character on line \fIline\fR.
+Lines are numbered from 1 for consistency with other UNIX programs
+that use this numbering scheme.
+Within a line, characters are numbered from 0.
+If \fIchar\fR is \f5end\fR then it refers to the newline character
+that ends the line.
+.TP 12
+\f5@\fIx\f5,\fIy\fR
+Indicates the character that covers the pixel whose x and y coordinates
+within the text's window are \fIx\fR and \fIy\fR.
+.TP 12
+\f5end\fR
+Indicates the end of the text (the character just after the last
+newline).
+.TP 12
+\fImark\fR
+Indicates the character just after the mark whose name is \fImark\fR.
+.TP 12
+\fItag\f5.first\fR
+Indicates the first character in the text that has been tagged with
+\fItag\fR.
+This form generates an error if no characters are currently tagged
+with \fItag\fR.
+.TP 12
+\fItag\f5.last\fR
+Indicates the character just after the last one in the text that has
+been tagged with \fItag\fR.
+This form generates an error if no characters are currently tagged
+with \fItag\fR.
+.TP 12
+\fIpathName\fR
+Indicates the position of the embedded window whose name is
+\fIpathName\fR.
+This form generates an error if there is no embedded window
+by the given name.
+.PP
+If modifiers follow the base index, each one of them must have one
+of the forms listed below.
+.TP
+\f5+ \fIcount\f5 chars\fR
+Adjust the index forward by \fIcount\fR characters, moving to later
+lines in the text if necessary.  If there are fewer than \fIcount\fR
+characters in the text after the current index, then set the index
+to the last character in the text.
+Spaces on either side of \fIcount\fR are optional.
+.TP
+\f5\- \fIcount\f5 chars\fR
+Adjust the index backward by \fIcount\fR characters, moving to earlier
+lines in the text if necessary.  If there are fewer than \fIcount\fR
+characters in the text before the current index, then set the index
+to the first character in the text.
+Spaces on either side of \fIcount\fR are optional.
+.TP
+\f5+ \fIcount\f5 lines\fR
+Adjust the index forward by \fIcount\fR lines, retaining the same
+character position within the line.  If there are fewer than \fIcount\fR
+lines after the line containing the current index, then set the index
+to refer to the same character position on the last line of the text.
+Then, if the line is not long enough to contain a character at the indicated
+character position, adjust the character position to refer to the last
+character of the line (the newline).
+Spaces on either side of \fIcount\fR are optional.
+.TP
+\f5\- \fIcount\f5 lines\fR
+Adjust the index backward by \fIcount\fR lines, retaining the same
+character position within the line.  If there are fewer than \fIcount\fR
+lines before the line containing the current index, then set the index
+to refer to the same character position on the first line of the text.
+Then, if the line is not long enough to contain a character at the indicated
+character position, adjust the character position to refer to the last
+character of the line (the newline).
+Spaces on either side of \fIcount\fR are optional.
+.TP
+\f5linestart\fR
+Adjust the index to refer to the first character on the line.
+.TP
+\f5lineend\fR
+Adjust the index to refer to the last character on the line (the newline).
+.TP
+\f5wordstart\fR
+Adjust the index to refer to the first character of the word containing
+the current index.  A word consists of any number of adjacent characters
+that are letters, digits, or underscores, or a single character that
+is not one of these.
+.TP
+\f5wordend\fR
+Adjust the index to refer to the character just after the last one of the
+word containing the current index.  If the current index refers to the last
+character of the text then it is not modified.
+.PP
+If more than one modifier is present then they are applied in
+left-to-right order.  For example, the index ``\f5end \- 1 chars\fR''
+refers to the next-to-last character in the text and the index
+``\f5insert wordstart \- 1 c\fR'' refers to the character just before
+the first one in the word containing the insertion cursor.
+
+.SH TAGS
+The first form of annotation in text widgets is a tag.
+A tag is a textual string that is associated with some of the characters
+in a text.
+Tags may contain arbitrary characters, but it is probably best to
+avoid using the the characters `` '' (space), \f5+\fR, or \f5-\fR:
+these characters have special meaning in indices, so tags containing
+them can't be used as indices. The tag name may not begin with a digit.
+There may be any number of tags associated with characters in a
+text.
+Each tag may refer to a single character, a range of characters, or
+several ranges of characters.
+An individual character may have any number of tags associated with it.
+.PP
+A priority order is defined among tags, and this order is used in
+implementing some of the tag-related functions described below.
+When a tag is defined (by associating it with characters or setting
+its display options or binding commands to it), it is given
+a priority higher than any existing tag.
+The priority order of tags may be redefined using the
+``\fIpathName \f5tag raise\fR'' and ``\fIpathName \f5tag lower\fR''
+widget commands.
+.PP
+Tags serve three purposes in text widgets.
+First, they control the way information is displayed on the screen.
+By default, characters are displayed as determined by the
+\f5background\fR, \f5font\fR, and \f5foreground\fR options for the
+text widget.
+However, display options may be associated with individual tags
+using the ``\fIpathName \f5tag configure\fR'' widget command.
+If a character has been tagged, then the display options associated
+with the tag override the default display style.
+The following options are currently supported for tags:
+.TP
+\f5-background \fIcolour\fR
+\fIColor\fR specifies the background colour to use for characters
+associated with the tag.
+.TP
+\f5-borderwidth \fIdist\fR
+\fIDist\fR specifies the width of a 3-D border to draw around
+the background.
+This option is used in conjunction with the \f5-relief\fR
+option to give a 3-D appearance to the background for characters;
+it is ignored unless the \f5-background\fR option
+has been set for the tag.
+.TP
+\f5-font \fIfont\fR
+\fIFont\fR is the name of a font to use for drawing characters.
+.TP
+\f5-foreground \fIcolour\fR
+\fIColor\fR specifies the colour to use when drawing text and other
+foreground information such as underlines.
+.TP
+\f5-justify \fIjustify\fR
+If the first character of a display line has a tag for which this
+option has been specified, then \fIjustify\fR determines how to
+justify the line.
+It must be one of \f5left\fR, \f5right\fR, or \f5center\fR.
+If a line wraps, then the justification for each line on the
+display is determined by the first character of that display line.
+.TP
+\f5-lmargin1 \fIdist\fR
+If the first character of a text line has a tag for which this
+option has been specified, then \fIdist\fR specifies how
+much the line should be indented from the left edge of the
+window.
+\fIDist\fR may have any of the standard forms for screen
+distances.
+If a line of text wraps, this option only applies to the
+first line on the display;  the \f5-lmargin2\fR option controls
+the indentation for subsequent lines.
+.TP
+\f5-lmargin2 \fIdist\fR
+If the first character of a display line has a tag for which this
+option has been specified, and if the display line is not the
+first for its text line (i.e., the text line has wrapped), then
+\fIdist\fR specifies how much the line should be indented from
+the left edge of the window.
+\fIDist\fR may have any of the standard forms for screen
+distances.
+This option is only used when wrapping is enabled, and it only
+applies to the second and later display lines for a text line.
+.TP
+\f5-offset \fIdist\fR
+\fIDist\fR specifies an amount by which the text's baseline
+should be offset vertically from the baseline of the overall
+line, in pixels.
+For example, a positive offset can be used for superscripts
+and a negative offset can be used for subscripts.
+\fIDist\fR may have any of the standard forms for screen
+distances.
+.TP
+\f5-overstrike \fIboolean\fR
+Specifies whether or not to draw a horizontal rule through
+the middle of characters.
+.TP
+\f5-relief \fIrelief\fR
+\fIRelief\fR specifies the 3-D relief to use for drawing backgrounds.
+This option is used in conjunction with the \f5-borderwidth\fR
+option to give a 3-D appearance to the background for characters;
+it is ignored unless the \f5-background\fR option
+has been set for the tag.
+.TP
+\f5-rmargin \fIdist\fR
+If the first character of a display line has a tag for which this
+option has been specified, then \fIdist\fR specifies how wide
+a margin to leave between the end of the line and the right
+edge of the window.
+This option is only used when wrapping is enabled.
+If a text line wraps, the right margin for each line on the
+display is determined by the first character of that display
+line.
+.TP
+\f5-spacing1 \fIdist\fR
+\fIDist\fR specifies how much additional space should be
+left above each text line, using any of the standard forms for
+screen distances.
+If a line wraps, this option only applies to the first
+line on the display.
+.TP
+\f5-spacing2 \fIdist\fR
+For lines that wrap, this option specifies how much additional
+space to leave between the display lines for a single text line.
+\fIDist\fR may have any of the standard forms for screen
+distances.
+.TP
+\f5-spacing3 \fIdist\fR
+\fIDist\fR specifies how much additional space should be
+left below each text line, using any of the standard forms for
+screen distances.
+If a line wraps, this option only applies to the last
+line on the display.
+.TP
+\f5-tabs \fItabList\fR
+\fITabList\fR specifies a set of tab stops in the same form
+as for the \f5-tabs\fR option for the text widget.  This
+option only applies to a display line if it applies to the
+first character on that display line.
+If this option is specified as an empty string, it cancels
+the option, leaving it unspecified for the tag (the default).
+If the option is specified as a non-empty string that is
+an empty list, such as \f5-tabs\0{\0}\fR, then it requests
+default 8-character tabs as described for the \f5tabs\fR
+widget option.
+.TP
+\f5-underline \fIboolean\fR
+\fIBoolean\fR specifies whether or not to draw an underline underneath
+characters.
+.TP
+\f5-wrap \fImode\fR
+\fIMode\fR specifies how to handle lines that are wider than the
+text's window.
+It has the same legal values as the \f5-wrap\fR option
+for the text widget:  \f5none\fR, \f5char\fR, or \f5word\fR.
+If this tag option is specified, it overrides the \f5-wrap\fR option
+for the text widget.
+.PP
+If a character has several tags associated with it, and if their
+display options conflict, then the options of the highest priority
+tag are used.
+If a particular display option hasn't been specified for a
+particular tag, or if it is specified as an empty string, then
+that option will never be used;  the next-highest-priority
+tag's option will be used instead.
+If no tag specifies a particular display option, then the default
+style for the widget will be used.
+.PP
+The second purpose for tags is event bindings.
+You can associate bindings with a tag in much the same way you can
+associate bindings with a widget class:  whenever particular
+events occur on characters with the given tag, a given
+Tk command will be executed.
+Tag bindings can be used to give behaviours to ranges of characters;
+among other things, this allows hypertext-like
+features to be implemented.
+For details, see the description of the \f5tag bind\fR widget
+command below.
+.PP
+The third use for tags is in managing the selection.
+See THE SELECTION below.
+
+.SH MARKS
+The second form of annotation in text widgets is a mark.
+Marks are used for remembering particular places in a text.
+They are something like tags, in that they have names and
+they refer to places in the file, but a mark isn't associated
+with particular characters.
+Instead, a mark is associated with the gap between two characters.
+Only a single position may be associated with a mark at any given
+time.
+If the characters around a mark are deleted the mark will still
+remain;  it will just have new neighbour characters.
+In contrast, if the characters containing a tag are deleted then
+the tag will no longer have an association with characters in
+the file.
+Marks may be manipulated with the ``\fIpathName \f5mark\fR'' widget
+command, and their current locations may be determined by using the
+mark name as an index in widget commands.
+.PP
+Each mark also has a \fIgravity\fR, which is either \f5left\fR or
+\f5right\fR.
+The gravity for a mark specifies what happens to the mark when
+text is inserted at the point of the mark.
+If a mark has left gravity, then the mark is treated as if it
+were attached to the character on its left, so the mark will
+remain to the left of any text inserted at the mark position.
+If the mark has right gravity, new text inserted at the mark
+position will appear to the right of the mark.  The gravity
+for a mark defaults to \f5right\fR.
+.PP
+The name space for marks is different from that for tags:  the
+same name may be used for both a mark and a tag, but they will refer
+to different things.
+.PP
+Two marks have special significance.
+First, the mark \f5insert\fR is associated with the insertion cursor,
+as described under THE INSERTION CURSOR below.
+Second, the mark \f5current\fR is associated with the character
+closest to the mouse and is adjusted automatically to track the
+mouse position and any changes to the text in the widget (one
+exception:  \f5current\fR is not updated in response to mouse
+motions if a mouse button is down;  the update will be deferred
+until all mouse buttons have been released).
+Neither of these special marks may be deleted.
+
+.SH EMBEDDED WINDOWS
+The third form of annotation in text widgets is an embedded window.
+Each embedded window annotation causes a window to be displayed
+at a particular point in  the text.
+There may be any number of embedded windows in a text widget,
+and any widget may be used as an embedded window.
+The embedded window's position on the screen will be updated as the
+text is modified or scrolled.
+Each embedded window occupies one character's worth of index space
+in the text widget, and it may be referred to either by the name
+of its embedded window or by its position in the widget's
+index space.
+If the range of text containing the embedded window is deleted and the
+window is a child of the text widget then the window is destroyed.
+.PP
+When an embedded window is added to a text widget with the
+\f5window create\fR widget command, several configuration
+options may be associated with it.
+These options may be  modified later with the \f5window configure\fR
+widget command.
+The following options are currently supported:
+.TP
+\f5-align \fIwhere\fR
+If the window is not as tall as the line in which it is displayed,
+this option determines where the window is displayed in the line.
+\fIWhere\fR must have one of the values \f5top\fR (align the top of the window
+with the top of the line), \f5center\fR (center the window
+within the range of the line), \f5bottom\fR (align the bottom of the
+window with the bottom of the line's area),
+or \f5baseline\fR (align the bottom of the window with the baseline
+of the line).
+.TP
+\f5-padx \fIdist\fR
+\fIDist\fR specifies the amount of extra space to leave on
+each side of the embedded window.
+It may have any of the usual forms defined for a screen distance.
+.TP
+\f5-pady \fIdist\fR
+\fIDist\fR specifies the amount of extra space to leave on
+the top and on the bottom of the embedded window.
+It may have any of the usual forms defined for a screen distance.
+.TP
+\f5-stretch \fIboolean\fR
+If the requested height of the embedded window is less than the
+height of the line in which it is displayed, this option can be
+used to specify whether the window should be stretched vertically
+to fill its line.
+If the \f5-pady\fR option has been specified as well, then the
+requested padding will be retained even if the window is
+stretched.
+.TP
+\f5-window \fIpathName\fR
+Specifies the name of a window to display in the annotation.
+
+.SH THE SELECTION
+Selection support is implemented via tags.
+The \f5sel\fR tag is automatically defined when a text widget is
+created, and it may not be deleted with the ``\fIpathName \f5tag delete\fR''
+widget command.  Furthermore, the \f5selectbackground\fR,
+\f5selectborderwidth\fR, and \f5selectforeground\fR options for
+the text widget are tied to the \f5background\fR,
+\f5borderwidth\fR, and \f5foreground\fR options for the \f5sel\fR
+tag:  changes in either will automatically be reflected in the
+other.
+
+.SH THE INSERTION CURSOR
+The mark named \f5insert\fR has special significance in text widgets.
+It is defined automatically when a text widget is created and it
+may not be unset with the ``\fIpathName \f5mark unset\fR'' widget
+command.
+The \f5insert\fR mark represents the position of the insertion
+cursor, and the insertion cursor will automatically be drawn at
+this point whenever the text widget has the input focus.
+
+.SH "WIDGET COMMAND"
+The \f5text\fR command creates a new Tk command whose
+name is the same as the path name of the text's window.  This
+command may be used to invoke various
+operations on the widget.  It has the following general form:
+.RS
+.EX
+\fIpathName option \fR?\fIarg arg ...\fR?
+.EE
+.RE
+\fIPathName\fR is the name of the command, which is the same as
+the text widget's path name.  \fIOption\fR and the \fIarg\fRs
+determine the exact behaviour of the command.  The following
+commands are possible for text widgets:
+.TP
+\fIpathName \f5bbox \fIindex\fR
+Returns a list of four elements describing the screen area
+of the character given by \fIindex\fR.
+The first two elements of the list give the x and y coordinates
+of the upper-left corner of the area occupied by the
+character, and the last two elements give the width and height
+of the area.
+If the character is only partially visible on the screen, then
+the return value reflects just the visible part.
+If the character is not visible on the screen then the return
+value is an empty list.
+.TP
+\fIpathName \f5cget\fR \fIoption\fR
+Returns the current value of the configuration option given
+by \fIoption\fR.
+\fIOption\fR may have any of the values accepted by the \f5text\fR
+command.
+.TP
+\fIpathName \f5compare\fR \fIindex1 op index2\fR
+Compares the indices given by \fIindex1\fR and \fIindex2\fR according
+to the relational operator given by \fIop\fR, and returns 1 if
+the relationship is satisfied and 0 if it isn't.
+\fIOp\fR must be one of the operators <, <=, ==, >=, >, or !=.
+If \fIop\fR is == then 1 is returned if the two indices refer to
+the same character, if \fIop\fR is < then 1 is returned if \fIindex1\fR
+refers to an earlier character in the text than \fIindex2\fR, and
+so on.
+.TP
+\fIpathName \f5configure\fR ?\fIoption\fR? \fI?value option value ...\fR?
+Query or modify the configuration options of the widget.
+If no \fIoption\fR is specified, returns a list of all of
+the available options for \fIpathName\fR.  If
+one or more \fIoption-value\fR pairs are specified, then the command
+modifies the given widget option(s) to have the given value(s);  in
+this case the command returns an empty string.
+\fIOption\fR may have any of the values accepted by the \f5text\fR
+command.
+.TP
+\fIpathName \f5debug \fR?\fIboolean\fR?
+If the value is a true one then internal consistency checks will be
+turned on in the code associated with text widgets.
+If \fIboolean\fR has a false value then the debugging checks will
+be turned off.
+In either case the command returns an empty string.
+If \fIboolean\fR is not specified then the command returns \f5on\fR
+or \f5off\fR to indicate whether or not debugging is turned on.
+There is a single debugging switch shared by all text widgets:  turning
+debugging on or off in any widget turns it on or off for all widgets.
+For widgets with large amounts of text, the consistency checks may
+cause a noticeable slow-down.
+.TP
+\fIpathName \f5delete \fIindex1 \fR?\fIindex2\fR?
+Delete a range of characters from the text.
+If both \fIindex1\fR and \fIindex2\fR are specified, then delete
+all the characters starting with the one given by \fIindex1\fR
+and stopping just before \fIindex2\fR (i.e. the character at
+\fIindex2\fR is not deleted).
+If \fIindex2\fR doesn't specify a position later in the text
+than \fIindex1\fR then no characters are deleted.
+If \fIindex2\fR isn't specified then the single character at
+\fIindex1\fR is deleted.
+It is not allowable to delete characters in a way that would leave
+the text without a newline as the last character.
+The command returns an empty string.
+.TP
+\fIpathName \f5dlineinfo \fIindex\fR
+Returns a list with five elements describing the area occupied
+by the display line containing \fIindex\fR.
+The first two elements of the list give the x and y coordinates
+of the upper-left corner of the area occupied by the
+line, the third and fourth elements give the width and height
+of the area, and the fifth element gives the position of the baseline
+for the line, measured down from the top of the area.
+All of this information is measured in pixels.
+If the current wrap mode is \f5none\fR and the line extends beyond
+the boundaries of the window,
+the area returned reflects the entire area of the line, including the
+portions that are out of the window.
+If the line is shorter than the full width of the window then the
+area returned reflects just the portion of the line that is occupied
+by characters and embedded windows.
+If the display line containing \fIindex\fR is not visible on
+the screen then the return value is an empty list.
+.TP
+\fIpathName \f5get \fIindex1 \fR?\fIindex2\fR?
+Return a range of characters from the text.
+The return value will be all the characters in the text starting
+with the one whose index is \fIindex1\fR and ending just before
+the one whose index is \fIindex2\fR (the character at \fIindex2\fR
+will not be returned).
+If \fIindex2\fR is omitted then the single character at \fIindex1\fR
+is returned.
+If there are no characters in the specified range (e.g. \fIindex1\fR
+is past the end of the file or \fIindex2\fR is less than or equal
+to \fIindex1\fR) then an empty string is returned.
+If the specified range contains embedded windows, no information
+about them is included in the returned string.
+.TP
+\fIpathName \f5index \fIindex\fR
+Returns the position corresponding to \fIindex\fR in the form
+\fIline.char\fR where \fIline\fR is the line number and \fIchar\fR
+is the character number.
+\fIIndex\fR may have any of the forms described under INDICES above.
+.TP
+\fIpathName \f5insert \fIindex chars \fR?\fItagList chars tagList ...\fR?
+Inserts all of the \fIchars\fR arguments just before the character at
+\fIindex\fR.
+If \fIindex\fR refers to the end of the text (the character after
+the last newline) then the new text is inserted just before the
+last newline instead.
+If there is a single \fIchars\fR argument and no \fItagList\fR, then
+the new text will receive any tags that are present on both the
+character before and the character after the insertion point; if a tag
+is present on only one of these characters then it will not be
+applied to the new text.
+If \fItagList\fR is specified then it consists of a list of
+tag names;  the new characters will receive all of the tags in
+this list and no others, regardless of the tags present around
+the insertion point.
+If multiple \fIchars\fR-\fItagList\fR argument pairs are present,
+they produce the same effect as if a separate \f5insert\fR widget
+command had been issued for each pair, in order.
+The last \fItagList\fR argument may be omitted.
+.TP
+\fIpathName \f5mark \fIoption \fR?\fIarg arg ...\fR?
+This command is used to manipulate marks.  The exact behaviour of
+the command depends on the \fIoption\fR argument that follows
+the \f5mark\fR argument.  The following forms of the command
+are currently supported:
+.RS
+.TP
+\fIpathName \f5mark gravity \fImarkName\fR ?\fIdirection\fR?
+If \fIdirection\fR is not specified, returns \f5left\fR or \f5right\fR
+to indicate which of its adjacent characters \fImarkName\fR is attached
+to.
+If \fIdirection\fR is specified, it must be \f5left\fR or \f5right\fR;
+the gravity of \fImarkName\fR is set to the given value.
+.TP
+\fIpathName \f5mark names\fR
+Returns a list whose elements are the names of all the marks that
+are currently set.
+.TP
+\fIpathName \f5mark next \fIindex\fR
+Returns the name of the next mark at or after \fIindex\fR.
+If \fIindex\fR is specified in numerical form, then the search for
+the next mark begins at that index.
+If \fIindex\fR is the name of a mark, then the search for
+the next mark begins immediately after that mark.
+This can still return a mark at the same position if
+there are multiple marks at the same index.
+If a mark has been set to the special \f5end\fP index,
+then it appears to be \fIafter\fP \f5end\fP with respect to the \f5mark next\fP operation.
+An empty string is returned if there are no marks after \fIindex\fP.
+.TP
+\fIpathName \f5mark previous \fIindex\fR
+Returns the name of the mark at or before \fIindex\fR.
+If \fIindex\fR is specified in numerical form, then the search for
+the previous mark begins with the character just before that index.
+If \fIindex\fR is the name of a mark, then the search for
+the next mark begins immediately before that mark.
+This can still return a mark at the same position if
+there are multiple marks at the same index.
+An empty string is returned if there are no marks before \fIindex\fR.
+.TP
+\fIpathName \f5mark set \fImarkName index\fR
+Sets the mark named \fImarkName\fR to a position just before the
+character at \fIindex\fR.
+If \fImarkName\fR already exists, it is moved from its old position;
+if it doesn't exist, a new mark is created.
+This command returns an empty string.
+.TP
+\fIpathName \f5mark unset \fImarkName \fR?\fImarkName markName ...\fR?
+Remove the mark corresponding to each of the \fImarkName\fR arguments.
+The removed marks will not be usable in indices and will not be
+returned by future calls to ``\fIpathName \f5mark names\fR''.
+This command returns an empty string.
+.RE
+.TP
+\fIpathName \f5scan\fR \fIoption args\fR
+This command is used to implement scanning on texts.  It has
+two forms, depending on \fIoption\fR:
+.RS
+.TP
+\fIpathName \f5scan mark \fIx y\fR
+Records \fIx\fR and \fIy\fR and the current view in the text window,
+for use in conjunction with later \f5scan dragto\fR commands.
+Typically this command is associated with a mouse button press in
+the widget.  It returns an empty string.
+.TP
+\fIpathName \f5scan dragto \fIx y\fR
+This command computes the difference between its \fIx\fR and \fIy\fR
+arguments and the \fIx\fR and \fIy\fR arguments to the last
+\f5scan mark\fR command for the widget.
+It then adjusts the view by 10 times the difference in coordinates.
+This command is typically associated
+with mouse motion events in the widget, to produce the effect of
+dragging the text at high speed through the window.  The return
+value is an empty string.
+.RE
+.TP
+\fIpathName \f5search \fR?\fIswitches\fR? \fIpattern index \fR?\fIstopIndex\fR?
+Searches the text in \fIpathName\fR starting at \fIindex\fR for a range
+of characters that matches \fIpattern\fR.
+If a match is found, the index of the first character in the match is
+returned as result;  otherwise an empty string is returned.
+One or more of the following switches
+may be specified to control the search:
+.RS
+.TP
+\f5-backwards\fR
+The search will proceed backward through the text, finding the
+matching range closest to \fIindex\fR whose first character
+is before \fIindex\fR.
+.TP
+\f5-nocase\fR
+Ignore case differences between the pattern and the text.
+.TP
+\f5-\|-\fR
+This switch has no effect except to terminate the list of switches:
+the next argument will be treated as \fIpattern\fR even if it starts
+with \f5-\fR.
+.LP
+The matching range must be entirely within a single line of text.
+If \fIstopIndex\fR is specified, the search stops at that index:
+for forward searches, no match at or after \fIstopIndex\fR will
+be considered;  for backward searches, no match earlier in the
+text than \fIstopIndex\fR will be considered.
+If \fIstopIndex\fR is omitted, the entire text will be searched:
+when the beginning or end of the text is reached, the search
+continues at the other end until the starting location is reached
+again;  if \fIstopIndex\fR is specified, no wrap-around will occur.
+.RE
+.TP
+\fIpathName \f5see \fIindex\fR
+Adjusts the view in the window so that the character given by \fIindex\fR
+is completely visible.
+If \fIindex\fR is already visible then the command does nothing.
+If \fIindex\fR is a short distance out of view, the command
+adjusts the view just enough to make \fIindex\fR visible at the
+edge of the window.
+If \fIindex\fR is far out of view, then the command centers
+\fIindex\fR in the window.
+.TP
+\fIpathName \f5tag \fIoption \fR?\fIarg arg ...\fR?
+This command is used to manipulate tags.  The exact behaviour of the
+command depends on the \fIoption\fR argument that follows the
+\f5tag\fR argument.  The following forms of the command are currently
+supported:
+.RS
+.TP
+\fIpathName \f5tag add \fItagName index1 \fR?\fIindex2 index1 index2 ...\fR?
+Associate the tag \fItagName\fR with all of the characters starting
+with \fIindex1\fR and ending just before
+\fIindex2\fR (the character at \fIindex2\fR isn't tagged).
+A single command may contain any number of \fIindex1\fR-\fIindex2\fR
+pairs.
+If the last \fIindex2\fR is omitted then the single character at
+\fIindex1\fR is tagged.
+If there are no characters in the specified range (e.g. \fIindex1\fR
+is past the end of the file or \fIindex2\fR is less than or equal
+to \fIindex1\fR) then the command has no effect.
+.TP
+\fIpathName \f5tag bind \fItagName\fR ?\fIsequence\fR? ?\fIscript\fR?
+This command associates \fIscript\fR with the tag given by
+\fItagName\fR.
+Whenever the event sequence given by \fIsequence\fR occurs for a
+character that has been tagged with \fItagName\fR,
+the script will be invoked.
+This widget command is similar to the \f5bind\fR command except that
+it operates on characters in a text rather than entire widgets.
+See the \f5bind\fR manual entry for complete details
+on the syntax of \fIsequence\fR and the substitutions performed
+on \fIscript\fR before invoking it.
+If all arguments are specified then a new binding is created, replacing
+any existing binding for the same \fIsequence\fR and \fItagName\fR
+(if the first character of \fIscript\fR is ``+'' then \fIscript\fR
+augments an existing binding rather than replacing it).
+In this case the return value is an empty string.
+.RS
+.PP
+The only events for which bindings may be specified are those related
+to the mouse and keyboard, such as \f5Enter\fR, \f5Leave\fR,
+\f5ButtonPress\fR, \f5Motion\fR, and \f5KeyPress\fR.
+Event bindings for a text widget use the \f5current\fR mark
+described under MARKS above.
+An \f5Enter\fR event triggers for a tag when the tag first
+becomes present on the current character, and a \f5Leave\fR
+event triggers for a tag when it ceases to be present on
+the current character.
+\f5Enter\fR and \f5Leave\fR events can happen either because the
+\f5current\fR mark moved or because the character at that
+position changed.
+Note that these events are different than \f5Enter\fR and \f5Leave\fR
+events for windows.
+Mouse and keyboard events are directed to the current character.
+.PP
+It is possible for the current character to have multiple tags,
+and for each of them to have a binding for a particular event
+sequence.
+When this occurs, one binding is invoked for each tag, in order
+from lowest-priority to highest priority.
+If there are multiple matching bindings for a single tag, then
+the most specific binding is chosen (see the manual entry for
+the \f5bind\fR command for details).
+.PP
+If bindings are created for the widget as a whole using the
+\f5bind\fR command, then those bindings will supplement the
+tag bindings.
+The tag bindings will be invoked first, followed by bindings
+for the window as a whole.
+.RE
+.TP
+\fIpathName \f5tag cget\fR \fItagName option\fR
+This command returns the current value of the option named \fIoption\fR
+associated with the tag given by \fItagName\fR.
+\fIOption\fR may have any of the values accepted by the \f5tag configure\fR
+widget command.
+.TP
+\fIpathName \f5tag configure \fItagName\fR ?\fIoption\fR? ?\fIvalue\fR? ?\fIoption value ...\fR?
+This command is similar to the \f5configure\fR widget command except
+that it modifies options associated with the tag given by \fItagName\fR
+instead of modifying options for the overall text widget.
+If one or more \fIoption-value\fR pairs are specified, then the command
+modifies the given option(s) to have the given value(s) in \fItagName\fR.
+See TAGS above for details on the options available for tags.
+.TP
+\fIpathName \f5tag delete \fItagName \fR?\fItagName ...\fR?
+Deletes all tag information for each of the \fItagName\fR
+arguments.
+The command removes the tags from all characters in the file
+and also deletes any other information associated with the tags,
+such as bindings and display information.
+The command returns an empty string.
+.TP
+\fIpathName\f5 tag lower \fItagName \fR?\fIbelowThis\fR?
+Changes the priority of tag \fItagName\fR so that it is just lower
+in priority than the tag whose name is \fIbelowThis\fR.
+If \fIbelowThis\fR is omitted, then \fItagName\fR's priority
+is changed to make it lowest priority of all tags.
+.TP
+\fIpathName \f5tag names \fR?\fIindex\fR?
+Returns a list whose elements are the names of all the tags that
+are active at the character position given by \fIindex\fR.
+If \fIindex\fR is omitted, then the return value will describe
+all of the tags that exist for the text (this includes all tags
+that have been named in a ``\fIpathName \f5tag\fR'' widget
+command but haven't been deleted by a ``\fIpathName \f5tag delete\fR''
+widget command, even if no characters are currently marked with
+the tag).
+The list will be sorted in order from highest priority to lowest
+priority.
+.TP
+\fIpathName \f5tag nextrange \fItagName index1 \fR?\fIindex2\fR?
+This command searches the text for a range of characters tagged
+with \fItagName\fR where the first character of the range is
+no earlier than the character at \fIindex1\fR and no later than
+the character just before \fIindex2\fR (a range starting at
+\fIindex2\fR will not be considered).
+If several matching ranges exist, the first one is chosen.
+The command's return value is a list containing
+two elements, which are the index of the first character of the
+range and the index of the character just after the last one in
+the range.
+If no matching range is found then the return value is an
+empty string.
+If \fIindex2\fR is not given then it defaults to the end of the text.
+.TP
+\fIpathName \f5tag prevrange \fItagName index1 \fR?\fIindex2\fR?
+This command searches the text for a range of characters tagged
+with \fItagName\fR where the first character of the range is
+before the character at \fIindex1\fR and no earlier than
+the character at \fIindex2\fR (a range starting at
+\fIindex2\fR will be considered).
+If several matching ranges exist, the one closest to \fIindex1\fR is chosen.
+The command's return value is a list containing
+two elements, which are the index of the first character of the
+range and the index of the character just after the last one in
+the range.
+If no matching range is found then the return value is an
+empty string.
+If \fIindex2\fR is not given then it defaults to the beginning of the text.
+.TP
+\fIpathName\f5 tag raise \fItagName \fR?\fIaboveThis\fR?
+Changes the priority of tag \fItagName\fR so that it is just higher
+in priority than the tag whose name is \fIaboveThis\fR.
+If \fIaboveThis\fR is omitted, then \fItagName\fR's priority
+is changed to make it highest priority of all tags.
+.TP
+\fIpathName \f5tag ranges \fItagName\fR
+Returns a list describing all of the ranges of text that have been
+tagged with \fItagName\fR.
+The first two elements of the list describe the first tagged range
+in the text, the next two elements describe the second range, and
+so on.
+The first element of each pair contains the index of the first
+character of the range, and the second element of the pair contains
+the index of the character just after the last one in the
+range.
+If there are no characters tagged with \fItag\fR then an
+empty string is returned.
+.TP
+\fIpathName \f5tag remove \fItagName index1 \fR?\fIindex2 index1 index2 ...\fR?
+Remove the tag \fItagName\fR from all of the characters starting
+at \fIindex1\fR and ending just before
+\fIindex2\fR (the character at \fIindex2\fR isn't affected).
+A single command may contain any number of \fIindex1\fR-\fIindex2\fR
+pairs.
+If the last \fIindex2\fR is omitted then the single character at
+\fIindex1\fR is tagged.
+If there are no characters in the specified range (e.g. \fIindex1\fR
+is past the end of the file or \fIindex2\fR is less than or equal
+to \fIindex1\fR) then the command has no effect.
+This command returns an empty string.
+.RE
+.TP
+\fIpathName \f5window \fIoption \fR?\fIarg arg ...\fR?
+This command is used to manipulate embedded windows.
+The behaviour of the command depends on the \fIoption\fR argument
+that follows the \f5window\fR argument.
+The following forms of the command are currently supported:
+.RS
+.TP
+\fIpathName \f5window cget\fR \fIindex option\fR
+Returns the value of a configuration option for an embedded window.
+\fIIndex\fR identifies the embedded window, and \fIoption\fR
+specifies a particular configuration option, which must be one of
+the ones listed in the section EMBEDDED WINDOWS.
+.TP
+\fIpathName \f5window configure \fIindex\fR ?\fIoption value ...\fR?
+Query or modify the configuration options for an embedded window.
+If one or more \fIoption-value\fR pairs are specified, then the command
+modifies the given option(s) to have the given value(s).
+See EMBEDDED WINDOWS for information on the options that
+are supported.
+.TP
+\fIpathName \f5window create \fIindex\fR ?\fIoption value ...\fR?
+This command creates a new window annotation, which will appear
+in the text at the position given by \fIindex\fR.
+Any number of \fIoption-value\fR pairs may be specified to
+configure the annotation.
+See EMBEDDED WINDOWS for information on the options that
+are supported.
+Returns an empty string.
+.TP
+\fIpathName \f5window names\fR
+Returns a list whose elements are the names of all windows currently
+embedded in \fIwindow\fR.
+.RE
+.TP
+\fIpathName \f5xview \fIoption args\fR
+This command is used to query and change the horizontal position of the
+text in the widget's window.  It can take any of the following
+forms:
+.RS
+.TP
+\fIpathName \f5xview\fR
+Returns a list containing two elements.
+Each element is a real fraction between 0 and 1;  together they describe
+the portion of the document's horizontal span that is visible in
+the window.
+For example, if the first element is .2 and the second element is .6,
+20% of the text is off-screen to the left, the middle 40% is visible
+in the window, and 40% of the text is off-screen to the right.
+The fractions refer only to the lines that are actually visible in the
+window:  if the lines in the window are all very short, so that they
+are entirely visible, the returned fractions will be 0 and 1,
+even if there are other lines in the text that are
+much wider than the window.
+These are the same values passed to scrollbars via the \f5-xscrollcommand\fR
+option.
+.TP
+\fIpathName \f5xview moveto\fI fraction\fR
+Adjusts the view in the window so that \fIfraction\fR of the horizontal
+span of the text is off-screen to the left.
+\fIFraction\fR is a fraction between 0 and 1.
+.TP
+\fIpathName \f5xview scroll \fInumber what\fR
+This command shifts the view in the window left or right according to
+\fInumber\fR and \fIwhat\fR.
+\fINumber\fR must be an integer.
+\fIWhat\fR must be either \f5units\fR or \f5pages\fR.
+If \fIwhat\fR is \f5units\fR, the view adjusts left or right by
+\fInumber\fR average-width characters on the display;  if it is
+\f5pages\fR then the view adjusts by \fInumber\fR screenfuls.
+If \fInumber\fR is negative then characters farther to the left
+become visible;  if it is positive then characters farther to the right
+become visible.
+.RE
+.TP
+\fIpathName \f5yview \fI?args\fR?
+This command is used to query and change the vertical position of the
+text in the widget's window.
+It can take any of the following forms:
+.RS
+.TP
+\fIpathName \f5yview\fR
+Returns a list containing two elements, both of which are real fractions
+between 0 and 1.
+The first element gives the position of the first character in the
+top line in the window, relative to the text as a whole (0.5 means
+it is halfway through the text, for example).
+The second element gives the position of the character just after
+the last one in the bottom line of the window,
+relative to the text as a whole.
+These are the same values passed to scrollbars via the \f5-yscrollcommand\fR
+option.
+.TP
+\fIpathName \f5yview moveto\fI fraction\fR
+Adjusts the view in the window so that the character given by \fIfraction\fR
+appears on the top line of the window.
+\fIFraction\fR is a fraction between 0 and 1;  0 indicates the first
+character in the text, 0.33 indicates the character one-third the
+way through the text, and so on.
+.TP
+\fIpathName \f5yview scroll \fInumber what\fR
+This command adjust the view in the window up or down according to
+\fInumber\fR and \fIwhat\fR.
+\fINumber\fR must be an integer.
+\fIWhat\fR must be either \f5units\fR or \f5pages\fR.
+If \fIwhat\fR is \f5units\fR, the view adjusts up or down by
+\fInumber\fR lines on the display;  if it is \f5pages\fR then
+the view adjusts by \fInumber\fR screenfuls.
+If \fInumber\fR is negative then earlier positions in the text
+become visible;  if it is positive then later positions in the text
+become visible.
+.TP
+\fIpathName \f5yview \fR?\f5-pickplace\fR? \fIindex\fR
+Changes the view in the widget's window to make \fIindex\fR visible.
+If the \f5-pickplace\fR option isn't specified then \fIindex\fR will
+appear at the top of the window.
+If \f5-pickplace\fR is specified then the widget chooses where
+\fIindex\fR appears in the window:
+.RS
+.IP [1]
+If \fIindex\fR is already visible somewhere in the window then the
+command does nothing.
+.IP [2]
+If \fIindex\fR is only a few lines off-screen above the window then
+it will be positioned at the top of the window.
+.IP [3]
+If \fIindex\fR is only a few lines off-screen below the window then
+it will be positioned at the bottom of the window.
+.IP [4]
+Otherwise, \fIindex\fR will be centered in the window.
+.LP
+The \f5-pickplace\fR option has been made obsolete by the \f5see\fR widget
+command (\f5see\fR handles both x- and y-motion to make a location
+visible, whereas \f5-pickplace\fR only handles motion in y).
+.RE
+.RE
+
+.SH BINDINGS
+Tk automatically creates bindings for texts that give them
+the following default behaviour.
+In the descriptions below, ``word'' refers to a contiguous group
+of letters, digits, or ``_'' characters, or any single character
+other than these.
+.IP [1]
+Clicking mouse button 1 positions the insertion cursor
+just before the character underneath the mouse cursor, sets the
+input focus to this widget, and clears any selection in the widget.
+Dragging with mouse button 1 strokes out a selection between
+the insertion cursor and the character under the mouse.
+.IP [2]
+Double-clicking with mouse button 1 selects the word under the mouse
+and positions the insertion cursor at the beginning of the word.
+Dragging after a double click is ignored.
+.IP [3]
+If any normal printing characters are typed, they are
+inserted at the point of the insertion cursor, replacing the
+current selection.
+.IP [4]
+If the mouse is dragged out of the widget
+while button 1 is pressed, the entry will automatically scroll to
+make more text visible (if there is more text off-screen on the side
+where the mouse left the window).
+.IP [5]
+The Left and Right keys move the insertion cursor one character to the
+left or right;  they also clear any selection in the text.
+Control-b and Control-f behave the same as Left and Right, respectively.
+.IP [6]
+The Up and Down keys move the insertion cursor one line up or
+down and clear any selection in the text.
+Control-p and Control-n behave the same as Up and Down, respectively.
+.IP [7]
+The Page-up and Page-down keys move the view up or down
+one screenful without moving the insertion cursor or adjusting the selection.
+IControl-v behaves the same as Page-down.
+.IP [8]
+Home, Control-a and Control-< move the insertion cursor to the
+beginning of its line and clear any selection in the widget.
+.IP [9]
+End, Control-e and Control-> move the insertion cursor to the
+end of the line and clear any selection in the widget.
+.IP [10]
+The Delete key deletes the selection, if there is one in the widget.
+If there is no selection, it deletes the character to the right of
+the insertion cursor.
+.IP [11]
+Backspace and Control-h delete the selection, if there is one
+in the widget.
+If there is no selection, they delete the character to the left of
+the insertion cursor.
+.IP [12]
+Control-d deletes the character to the right of the insertion cursor.
+.IP [13]
+Control-k deletes from the insertion cursor to the end of its line;
+if the insertion cursor is already at the end of a line, then
+Control-k deletes all of the next line.
+.IP [14]
+Control-o opens a new line by inserting a newline character in
+front of the insertion cursor without moving the insertion cursor.
+.IP [15]
+Control-u deletes from the insertion cursor to the start of its line;
+if the insertion cursor is already at the start of the line, then
+the current line is joined with the previous one.
+.IP [16]
+Control-w deletes from the insertion cursor to the start of the word
+that contains it;
+if the insertion cursor is at the start of the line, then
+the current line is joined with the previous one.
+.PP
+If the widget is disabled using the \f5-state\fR option, then its
+view can still be adjusted and text can still be selected,
+but no insertion cursor will be displayed and no text modifications will
+take place.
+.PP
+The behaviour of texts can be changed by defining new bindings for
+individual widgets.
+.SH BUGS
+Tab alignment doesn't work correctly.
+.PP
+The \f5-stretch\fR option on embedded windows is not implemented.
+.SH SEE ALSO
+.IR entry (9),
+.IR options (9),
+.IR types (9)
--- /dev/null
+++ b/man/9/types
@@ -1,0 +1,147 @@
+.TH TYPES 9
+.SH NAME
+types \- Standard types required by widget options.
+.SH DESCRIPTION
+This manual entry describes the the standard types
+that can be given as arguments to Inferno Tk widget
+options. When an option is documented, the type
+of argument that it accepts is either documented
+there, or the name of the argument refers to one of the
+names documented below.
+.TP
+.I anchorPos
+One of the values \f5n\fR, \f5ne\fR, \f5e\fR, \f5se\fR,
+\f5s\fR, \f5sw\fR, \f5w\fR, \f5nw\fR, or \f5center\fR.
+See
+.B -anchor
+in
+.IR options (9).
+.TP
+.I boolean
+A true or false value, one of the following:
+\f50\fP, \f5no\fP, \f5off\fP, 
+\f5false\fP (false),
+\f51\fP, \f5yes\fP, \f5on\fP, 
+\f5true\fP (true).
+.TP
+.I bitmap
+Identifies an image which can be drawn, or used as a mask
+through which something else is drawn. If
+.I bitmap
+begins with a
+.RB ` @ ',
+the remaining characters must be the path name of
+an Inferno image file. If
+.I bitmap
+begins with the character
+.RB ` < ',
+the remaining characters must be a decimal integer
+giving the file descriptor number of an open file (see
+.IR sys-open (2))
+from which the bitmap can be loaded. Otherwise,
+.I bitmap
+should be the name of a bitmap file in the directory
+.BR /icons/tk .
+.TP
+.I color
+A
+.I color
+parameter can be a colour name or an RGB (red, green and blue
+luminance) value. The colour names recognized are:
+.EX
+    aqua     yellow   red      teal     white
+    fuchsia  black    blue     darkblue transparent
+    maroon   gray     green    lime
+    purple   navy     olive    orange
+.EE
+For RGB values, either
+.BI # rgb
+or
+.BI # rrggbb
+can be used, where
+.IR r ,
+.IR rr ,
+etc. are hexadecimal values for the corresponding colour
+components.
+The notation
+.BI #rrggbbaa
+can be used to describe a colour with alpha (transparency) component
+.IR aa ;
+the
+.IR rr ,
+.IR gg ,
+and
+.IR bb
+values should be pre-multiplied appropriately. This is the
+canonical form for colour values. Alternatively, transparency
+can be applied to any other colour using a suffix
+.BI * alpha
+where
+.I alpha
+is either
+.BI #aa
+(two hex digits representing an alpha value between 0 and 255)
+or
+.I f
+(a decimal fixed point number between 0 and 1).
+For example,
+.B red*0.5
+gives half transparent red;
+.B #007f007f*#7f
+gives 0.75 transparent green (canonically
+.B #003f003f).
+.TP
+.I dist
+.I Dist
+specifies a distance on the screen, in the following form:
+an optional minus sign
+.RB ( - ),
+then one or more decimal digits (with possible
+embedded decimal point), then an optional units
+specifier. The unit specifiers are the following:
+.RS
+.TP
+.B c
+centimetres
+.TP
+.B m
+millimetres
+.TP
+.B i
+inches
+.TP
+.B p
+points (1/72th inch)
+.TP
+.B h
+height of widget's font (only applicable if the widget
+has an associated font, and if the font has previously been set).
+.TP
+.B w
+width of the zero
+.RB ( 0 )
+character in widget's font. (see above).
+.TP
+Measurements are converted into pixels assuming 100 dots
+per inch on an average CRT display.
+.RE
+.TP
+.I font
+A
+.I font
+parameter gives the full path name of an Inferno font file;
+for example,
+.BR /fonts/pelm/unicode.9.font .
+.TP
+.I frac
+A numeric, possibly fractional, value.
+.TP
+.I relief
+One of \f5raised\fR, \f5sunken\fR, \f5flat\fR, \f5ridge\fR,
+or \f5groove\fR. See
+.B -relief
+in
+.IR options (9).
+.SH SEE ALSO
+.IR intro (9),
+.IR options (9)
--- /dev/null
+++ b/man/9/update
@@ -1,0 +1,24 @@
+.TH UPDATE 9
+.SH NAME
+update \- flush graphics
+.SH SYNOPSIS
+\f5update\fP ?\fIoptions\fP?
+.SH DESCRIPTION
+Changes made via Tk commands do not become
+visible until a call to
+.BR update .
+.B Update
+flushes any pending changes to the screen.
+The following options are supported:
+.TP
+\f5-disable\fP
+If this option is given, all updates are disabled.
+This is useful to enable a program to
+stop mouse movement causing
+automatic updates while it is making changes to the window.
+.TP
+\f5-enable\fP
+This reverses a previous \f5update -enable\fP invocation.
+.PP
+.SH BUGS
+Changes that affect window sizes are visible immediately.
--- /dev/null
+++ b/man/9/variable
@@ -1,0 +1,25 @@
+.TH VARIABLE 9
+.SH NAME
+variable \- interrogate a button variable
+.SH SYNOPSIS
+\f5variable \fIvar\fR ?\fIvalue\fR?
+.SH DESCRIPTION
+.B Variable
+queries or sets the value of a Tk radiobutton, checkbutton or choicebutton variable.
+An error diagnostic will be returned if
+the variable is of the wrong type (if for instance
+it is the name of a named channel).
+.PP
+If \fIvalue\fR is given, the value of the variable is set
+to that value, and any associated buttons
+will subsequently reflect this in their appearance.
+Omitting the \fIvalue\fR argument returns the current value of the variable.
+.PP
+The special variable
+.B lasterror
+holds the text of the last Tk error encountered.
+It resets when queried.
+.SH SEE ALSO
+.IR checkbutton (9),
+.IR choicebutton (9),
+.IR radiobutton (9)
--- /dev/null
+++ b/man/fonts
@@ -1,0 +1,10 @@
+# mkfile rules to get fonts in Lucida Sans.
+# if you want to use Times, change these next lines to
+# MAN=mantimes
+# FONTS=''
+MAN=mani
+FONTS='.fp 1 R LucidaSans
+.fp 2 I LucidaSansI
+.fp 3 B LucidaSansB
+.fp 5 L LucidaCW
+'
--- /dev/null
+++ b/man/index
@@ -1,0 +1,64850 @@
+0,0,qtdir	/man/10/devattach
+0,nil	/man/2/sys-dirread
+00.txt	/man/2/sexprs
+00.txt	/man/6/sexprs
+00000000.0bbbbbbb	/man/6/utf
+00000bbb.bbbbbbbb	/man/6/utf
+003f003f	/man/9/types
+007f007f	/man/9/types
+0:0:0:0:0:ffff	/man/2/ip
+0:0:0:0:0:ffff:127.0.0.1	/man/2/ip
+0:none:adm	/man/4/kfs
+0bbbbbbb	/man/6/utf
+0ctl	/man/3/dup
+0th	/man/3/prog
+0x0a	/man/6/keyboard
+0x0d	/man/6/keyboard
+0x14,r29	/man/10/acid
+0x16c8,r29	/man/10/acid
+0x1e0	/man/3/mpeg
+0x2f8	/man/10/plan9.ini
+0x337,0x430	/man/10/plan9.ini
+0x3bc	/man/3/lpt
+0x3e8	/man/10/plan9.ini
+0x3f8	/man/10/plan9.ini
+0x3ff	/man/3/pnp
+0x43f	/man/10/plan9.ini
+0x7c00	/man/10/9load
+0x7c00	/man/8/prep
+0x7e00	/man/8/prep
+0x7f	/man/6/keyboard
+0xa00	/man/1/fc
+0xd0000	/man/10/plan9.ini
+0xffffffff	/man/3/ftl
+1,yy	/man/2/draw-example
+1.dis	/man/1/mash-make
+10000:sys	/man/4/kfs
+10001:upas:upas	/man/4/kfs
+10002:bootes:bootes	/man/4/kfs
+10006:inferno	/man/4/kfs
+100base	/man/10/plan9.ini
+100basefx	/man/10/plan9.ini
+100basetx	/man/10/plan9.ini
+100mbit	/man/10/plan9.ini
+100ms	/man/2/prof
+1024x768x8	/man/10/plan9.ini
+10base	/man/10/plan9.ini
+10base2	/man/10/plan9.ini
+10baset	/man/10/plan9.ini
+10bbbbbb	/man/6/utf
+110bbbbb	/man/6/utf
+1110bbbb	/man/6/utf
+11:certificate	/man/2/sexprs
+12:hello	/man/6/sexprs
+1600x1200x8	/man/10/plan9.ini
+16kbyte	/man/8/ftl
+16mb	/man/10/9load
+16mb	/man/10/plan9.ini
+16r000000ff	/man/2/draw-display
+16r000055ff	/man/2/draw-display
+16r000099ff	/man/2/draw-display
+16r0000bbff	/man/2/draw-display
+16r0000ffff	/man/2/draw-display
+16r005dbbff	/man/2/draw-display
+16r008888ff	/man/2/draw-display
+16r00ff00ff	/man/2/draw-display
+16r00ffffff	/man/2/draw-display
+16r3fff	/man/1/fc
+16r448844ff	/man/2/draw-display
+16r4993ddff	/man/2/draw-display
+16r55aaaaff	/man/2/draw-display
+16r7f	/man/2/draw-0intro
+16r7f00007f	/man/2/draw-0intro
+16r8888ccff	/man/2/draw-display
+16r88cc88ff	/man/2/draw-display
+16r99994cff	/man/2/draw-display
+16r9eeeeeff	/man/2/draw-display
+16raaffaaff	/man/2/draw-display
+16raaffffff	/man/2/draw-display
+16redb88320	/man/2/crc
+16reeee9eff	/man/2/draw-display
+16reeeeeeff	/man/2/draw-display
+16rff	/man/3/flash
+16rff	/man/3/usb
+16rff00007f	/man/2/draw-0intro
+16rff0000ff	/man/2/draw-display
+16rff00ffff	/man/2/draw-display
+16rfffd	/man/2/convcs
+16rffff	/man/2/dis
+16rffff	/man/2/string
+16rffff	/man/2/styx
+16rffff	/man/5/0intro
+16rffff	/man/8/styxchat
+16rffff00ff	/man/2/draw-display
+16rffffaaff	/man/2/draw-display
+16rffffff00	/man/2/draw-display
+16rffffffff	/man/2/crc
+16rffffffff	/man/2/draw-display
+1::2::5::nil	/man/2/sets
+1:adm:adm	/man/4/kfs
+1ctl	/man/3/dup
+1immediately	/man/10/qio
+1pp	/man/1/alphabet-fs
+1pp	/man/1/fs
+2000ad	/man/2/daytime
+20th	/man/1/cal
+2fd	/man/1/alphabet-main
+2gb	/man/10/9load
+2nd	/man/1/charon
+3com	/man/10/plan9.ini
+3com	/man/3/ether
+3com	/man/3/pbus
+3com	/man/3/plap
+3des	/man/3/tls
+3½dd	/man/3/floppy
+3½dd	/man/8/prep
+3½hd	/man/3/floppy
+3½hd	/man/8/prep
+48dr	/man/8/mangaload
+53c8xx	/man/10/plan9.ini
+5:inner0	/man/6/sexprs
+5coff	/man/10/5coff
+5cv	/man/10/5coff
+5cv	/man/10/5cv
+5cv	/man/10/ms2
+5¼dd	/man/3/floppy
+5¼dd	/man/8/prep
+5¼hd	/man/3/floppy
+5¼hd	/man/8/prep
+64kbyte	/man/3/ip
+64kbytes	/man/8/ftl
+6:issuer3:bob	/man/2/sexprs
+72th	/man/9/types
+7:subject7:alice	/man/2/sexprs
+8.5gb	/man/10/9load
+8.5gb	/man/8/prep
+8.out	/man/10/ksize
+823fads	/man/3/vid
+82543gc	/man/10/plan9.ini
+91cxx	/man/10/plan9.ini
+9660srv	/man/4/dossrv
+9660srv	/man/7/cddb
+9660srv	/man/8/init
+9660srv.b	/man/4/dossrv
+9999:noworld	/man/4/kfs
+9cpu	/man/1/9win
+9export	/man/4/import
+9export.b	/man/4/import
+9fat	/man/10/9load
+9fat	/man/8/prep
+9load	/man/10/9load
+9load	/man/10/plan9.ini
+9load	/man/8/prep
+9pcdisk	/man/8/prep
+9pxeload	/man/10/9load
+9srvfs	/man/3/srv9
+9srvfs	/man/4/9srvfs
+9srvfs.b	/man/4/9srvfs
+9win	/man/1/9win
+9win.b	/man/1/9win
+a&b	/man/2/sets
+a'b	/man/1/tiny
+a,b,c	/man/2/draw-image
+a,b,c,d	/man/2/draw-image
+a.b	/man/1/mk
+a.b.c	/man/9/options
+a.b.c.d	/man/10/plan9.ini
+a.c	/man/10/mk
+a.out	/man/10/5coff
+a.out	/man/10/5cv
+a.out	/man/10/a.out
+a.out	/man/10/dynld
+a.out	/man/10/inm
+a.out	/man/10/ksize
+a.out	/man/10/kstrip
+a.out	/man/10/srclist
+a.out	/man/3/dynld
+a.out.h	/man/10/a.out
+a.out.h	/man/10/dynld
+a.root	/man/6/ndb
+a10076795.gz	/man/1/idea
+a10076795.gz.id	/man/1/idea
+aa	/man/10/2c
+aa	/man/3/srv9
+aa	/man/9/types
+aabbccdd	/man/2/regex
+aaccess	/man/10/newchan
+aancestor	/man/2/w3c-xpointers
+aatopb	/man/1/alphabet-fs
+aatopb	/man/1/fs
+aattr	/man/2/factotum
+aattribute	/man/2/w3c-xpointers
+ab	/man/1/tiny
+ab	/man/4/archfs
+ab	/man/4/import
+ab	/man/4/tarfs
+abandon	/man/10/panic
+abbccd	/man/2/regex
+abbreviated	/man/1/diff
+abbreviated	/man/2/w3c-xpointers
+abbreviated	/man/6/attrdb
+abbreviated	/man/9/menu
+abbreviations	/man/1/m4
+abbreviations	/man/1/miniterm
+abbreviations	/man/1/units
+abbreviations	/man/2/w3c-xpointers
+abbreviations	/man/6/keyboard
+abc	/man/1/alphabet-abc
+abc	/man/1/alphabet-main
+abc	/man/1/asm
+abc	/man/10/5coff
+abc	/man/2/alphabet-intro
+abc	/man/2/sexprs
+abc	/man/6/translate
+abc.coff	/man/10/5coff
+abc.dis	/man/1/asm
+abc9i	/man/6/namespace
+abcb	/man/2/regex
+abcd2345	/man/1/idea
+abcdefghijklmn	/man/10/styxserver
+abcdeghijklmn	/man/10/styxserver
+abcelx	/man/4/dbfs
+abcer	/man/4/dbfs
+abci	/man/6/namespace
+abfprw	/man/8/prep
+ability	/man/1/sh
+ability	/man/10/plan9.ini
+ability	/man/3/pnp
+ability	/man/5/open
+able	/man/1/blur
+able	/man/1/mash-make
+able	/man/1/wm
+able	/man/10/plan9.ini
+able	/man/2/asn1
+able	/man/2/format
+able	/man/2/ida
+able	/man/2/security-0intro
+able	/man/2/spree-objstore
+able	/man/3/ds
+abort	/man/10/memory
+abort	/man/5/flush
+aborted	/man/2/spree
+aborted	/man/3/ds
+aborted	/man/5/0intro
+aborted	/man/5/version
+aborting	/man/2/styxflush
+aborts	/man/5/0intro
+aborts	/man/9/destroy
+about.b	/man/1/wm-misc
+abovethis	/man/9/canvas
+abovethis	/man/9/text
+abreviation	/man/1/tktester
+abs	/man/1/calc
+abs	/man/10/mk
+abs	/man/2/w3c-xpointers
+absence	/man/10/strcat
+absence	/man/2/ir
+absence	/man/2/prof
+absence	/man/2/w3c-uris
+absence	/man/3/ip
+absence	/man/8/dns
+absence	/man/8/prep
+absent	/man/1/acme
+absent	/man/1/alphabet-fs
+absent	/man/1/fs
+absent	/man/2/sets
+absolute	/man/1/acme
+absolute	/man/1/calc
+absolute	/man/1/gettar
+absolute	/man/1/passwd
+absolute	/man/1/tiny
+absolute	/man/10/a.out
+absolute	/man/10/conf
+absolute	/man/2/math-fp
+absolute	/man/2/sys-0intro
+absolute	/man/2/sys-millisec
+absolute	/man/2/w3c-uris
+absolute	/man/2/w3c-xpointers
+absolute	/man/8/bootpd
+absolutely	/man/1/chmod
+absolutely	/man/2/tkclient
+absolutely	/man/2/wmclient
+abstract	/man/2/0intro
+abstract	/man/2/asn1
+abstract	/man/2/draw-0intro
+abstract	/man/2/sys-dup
+abstract	/man/2/w3c-css
+abstract	/man/2/w3c-xpointers
+abstraction	/man/1/emu
+abstracts	/man/2/w3c-css
+abut	/man/2/prefab-element
+abv	/man/1/tktester
+ac	/man/2/dial
+ac	/man/3/logfs
+acceleration	/man/1/units
+acceleration	/man/3/vga
+accelerator	/man/9/menu
+accelerators	/man/9/menu
+accent	/man/6/keyboard
+accents	/man/1/m4
+accept	/man/1/dmview
+accept	/man/1/sh-file2chan
+accept	/man/1/tiny
+accept	/man/10/2c
+accept	/man/10/allocb
+accept	/man/2/arg
+accept	/man/2/dhcpclient
+accept	/man/2/dial
+accept	/man/2/ip
+accept	/man/2/rfc822
+accept	/man/2/sexprs
+accept	/man/2/spree-gather
+accept	/man/3/cmd
+accept	/man/3/ftl
+accept	/man/3/ip
+accept	/man/3/plap
+accept	/man/4/factotum
+accept	/man/4/ftpfs
+accept	/man/4/keysrv
+accept	/man/4/spree
+accept	/man/8/register
+accept	/man/8/signer
+accept	/man/9/0intro
+acceptable	/man/1/alphabet-main
+acceptable	/man/1/miniterm
+acceptable	/man/1/sh-arg
+acceptable	/man/1/sh-expr
+acceptable	/man/1/telnet
+acceptable	/man/2/factotum
+acceptable	/man/2/rfc822
+acceptable	/man/2/secstore
+acceptable	/man/2/security-auth
+acceptable	/man/2/spki-verifier
+acceptable	/man/2/styx
+acceptable	/man/2/sys-fversion
+acceptable	/man/2/wmclient
+acceptable	/man/3/sign
+acceptable	/man/4/ftpfs
+acceptable	/man/4/lockfs
+acceptable	/man/8/dhcp
+acceptable	/man/9/options
+acceptance	/man/8/signer
+accepted	/man/1/charon
+accepted	/man/1/collab-clients
+accepted	/man/1/listen
+accepted	/man/1/sh-std
+accepted	/man/10/2c
+accepted	/man/2/dial
+accepted	/man/2/ip
+accepted	/man/2/security-login
+accepted	/man/2/spree-gather
+accepted	/man/3/draw
+accepted	/man/3/ether
+accepted	/man/3/floppy
+accepted	/man/3/ip
+accepted	/man/3/pbus
+accepted	/man/3/plap
+accepted	/man/3/tls
+accepted	/man/4/dbfs
+accepted	/man/4/import
+accepted	/man/6/audio
+accepted	/man/6/ndb
+accepted	/man/7/dbsrv
+accepted	/man/8/ai2key
+accepted	/man/8/ping
+accepted	/man/8/prep
+accepted	/man/8/rstyxd
+accepted	/man/8/styxchat
+accepted	/man/9/bind
+accepted	/man/9/button
+accepted	/man/9/canvas
+accepted	/man/9/checkbutton
+accepted	/man/9/choicebutton
+accepted	/man/9/entry
+accepted	/man/9/frame
+accepted	/man/9/label
+accepted	/man/9/listbox
+accepted	/man/9/menu
+accepted	/man/9/menubutton
+accepted	/man/9/panel
+accepted	/man/9/radiobutton
+accepted	/man/9/scale
+accepted	/man/9/scrollbar
+accepted	/man/9/text
+accepting	/man/3/ip
+accepts	/man/1/acme
+accepts	/man/1/collab
+accepts	/man/1/crypt
+accepts	/man/1/dmview
+accepts	/man/1/du
+accepts	/man/1/grid-monitor
+accepts	/man/1/man
+accepts	/man/1/mash
+accepts	/man/1/mdb
+accepts	/man/1/sh
+accepts	/man/1/sh-alphabet
+accepts	/man/1/sh-arg
+accepts	/man/1/tkcmd
+accepts	/man/1/wm
+accepts	/man/1/wm-sh
+accepts	/man/2/dbm
+accepts	/man/2/spki
+accepts	/man/2/virgil
+accepts	/man/3/boot
+accepts	/man/3/cmd
+accepts	/man/3/dbg
+accepts	/man/3/dynld
+accepts	/man/3/eia
+accepts	/man/3/fpga
+accepts	/man/3/gpio
+accepts	/man/3/i2c
+accepts	/man/3/ip
+accepts	/man/3/mpeg
+accepts	/man/3/touch
+accepts	/man/3/tv
+accepts	/man/3/vid
+accepts	/man/4/acme
+accepts	/man/4/grid-cpu
+accepts	/man/4/kfs
+accepts	/man/8/applylog
+accepts	/man/8/rdbgsrv
+accepts	/man/8/signer
+accepts	/man/9/types
+access	/man/1/0intro
+access	/man/1/alphabet-abc
+access	/man/1/alphabet-fs
+access	/man/1/alphabet-grid
+access	/man/1/ar
+access	/man/1/chmod
+access	/man/1/collab
+access	/man/1/ebook
+access	/man/1/fs
+access	/man/1/listen
+access	/man/1/ls
+access	/man/1/sh
+access	/man/1/sh-regex
+access	/man/1/sh-std
+access	/man/1/sh-tk
+access	/man/1/tktester
+access	/man/1/yacc
+access	/man/10/devattach
+access	/man/10/dmainit
+access	/man/10/dynld
+access	/man/10/iar
+access	/man/10/inb
+access	/man/10/lock
+access	/man/10/mk
+access	/man/10/newchan
+access	/man/10/plan9.ini
+access	/man/10/qio
+access	/man/10/qlock
+access	/man/10/sleep
+access	/man/10/styxserver
+access	/man/2/alphabet-intro
+access	/man/2/bufio
+access	/man/2/daytime
+access	/man/2/dbm
+access	/man/2/dhcpclient
+access	/man/2/diskblocks
+access	/man/2/draw-image
+access	/man/2/draw-screen
+access	/man/2/factotum
+access	/man/2/hash
+access	/man/2/lock
+access	/man/2/math-linalg
+access	/man/2/palmfile
+access	/man/2/readdir
+access	/man/2/registries
+access	/man/2/security-0intro
+access	/man/2/sh
+access	/man/2/spree
+access	/man/2/srv
+access	/man/2/styxpersist
+access	/man/2/styxservers
+access	/man/2/styxservers-nametree
+access	/man/2/sys-0intro
+access	/man/2/sys-dup
+access	/man/2/sys-fauth
+access	/man/2/sys-fd2path
+access	/man/2/sys-open
+access	/man/2/sys-pctl
+access	/man/2/sys-read
+access	/man/2/sys-stat
+access	/man/2/translate
+access	/man/2/venti
+access	/man/2/volume
+access	/man/2/wmlib
+access	/man/3/audio
+access	/man/3/cons
+access	/man/3/draw
+access	/man/3/eia
+access	/man/3/env
+access	/man/3/flash
+access	/man/3/floppy
+access	/man/3/fpga
+access	/man/3/fs
+access	/man/3/ftl
+access	/man/3/gpio
+access	/man/3/i2c
+access	/man/3/indir
+access	/man/3/logfs
+access	/man/3/mnt
+access	/man/3/mpeg
+access	/man/3/pbus
+access	/man/3/plap
+access	/man/3/pnp
+access	/man/3/rtc
+access	/man/3/sd
+access	/man/3/srv
+access	/man/3/srv9
+access	/man/3/ssl
+access	/man/3/switch
+access	/man/3/tinyfs
+access	/man/3/touch
+access	/man/3/tv
+access	/man/3/usb
+access	/man/3/vid
+access	/man/4/acme
+access	/man/4/ftpfs
+access	/man/4/kfs
+access	/man/4/lockfs
+access	/man/4/tarfs
+access	/man/5/0intro
+access	/man/5/attach
+access	/man/5/open
+access	/man/5/stat
+access	/man/6/scancode
+access	/man/6/translate
+access	/man/7/0intro
+access	/man/7/dbsrv
+access	/man/8/collabsrv
+access	/man/8/dhcp
+access	/man/8/logind
+access	/man/8/ping
+access	/man/8/prep
+access	/man/8/styxmon
+access	/man/8/svc
+accessed	/man/1/0intro
+accessed	/man/1/du
+accessed	/man/10/acid
+accessed	/man/10/allocb
+accessed	/man/10/newchan
+accessed	/man/10/qlock
+accessed	/man/10/styxserver
+accessed	/man/2/0intro
+accessed	/man/2/bufio
+accessed	/man/2/command
+accessed	/man/2/crc
+accessed	/man/2/json
+accessed	/man/2/readdir
+accessed	/man/2/security-auth
+accessed	/man/2/srv
+accessed	/man/2/styxservers
+accessed	/man/2/sys-0intro
+accessed	/man/2/sys-stat
+accessed	/man/2/ubfa
+accessed	/man/2/wmsrv
+accessed	/man/3/arch
+accessed	/man/3/cmd
+accessed	/man/3/draw
+accessed	/man/3/fpga
+accessed	/man/3/ftl
+accessed	/man/3/logfs
+accessed	/man/3/mnt
+accessed	/man/3/sd
+accessed	/man/3/touch
+accessed	/man/4/acme
+accessed	/man/4/import
+accessed	/man/4/keysrv
+accessed	/man/4/tarfs
+accessed	/man/5/0intro
+accessed	/man/6/keys
+accessed	/man/8/cs
+accessed	/man/9/0intro
+accesses	/man/10/dev
+accesses	/man/2/dbm
+accesses	/man/2/dial
+accesses	/man/2/sys-dial
+accesses	/man/4/keysrv
+accessible	/man/1/alphabet-main
+accessible	/man/1/grid-ns
+accessible	/man/1/sh-mload
+accessible	/man/2/0intro
+accessible	/man/2/draw-screen
+accessible	/man/2/pop3
+accessible	/man/3/logfs
+accessible	/man/3/srv9
+accessible	/man/4/grid-cpu
+accessible	/man/4/import
+accessible	/man/6/font
+accessible	/man/9/options
+accessing	/man/2/dial
+accessing	/man/2/sh
+accessing	/man/2/sys-dial
+accessing	/man/2/sys-stat
+accessing	/man/4/acme
+accessing	/man/4/ftpfs
+accident	/man/6/colour
+accidents	/man/2/disks
+accommodate	/man/2/prefab-element
+accommodate	/man/3/vga
+accommodate	/man/9/grid
+accomodate	/man/10/ms2
+accompany	/man/5/error
+accompanying	/man/2/filter-deflate
+accomplished	/man/2/security-0intro
+accomplished	/man/4/factotum
+accordance	/man/9/1copyright
+accordingly	/man/3/mpeg
+accordingly	/man/3/usb
+accordingly	/man/4/keyfs
+accordingly	/man/4/spree
+accordingly	/man/8/ai2key
+accordingly	/man/8/sntp
+account	/man/10/memory
+account	/man/10/qio
+account	/man/2/bufio
+account	/man/2/keyring-rc4
+account	/man/2/palmfile
+account	/man/2/secstore
+account	/man/2/styxservers
+account	/man/2/wmclient
+account	/man/3/sd
+account	/man/3/touch
+account	/man/4/keyfs
+accounted	/man/3/logfs
+accounts	/man/2/scsiio
+accton	/man/10/plan9.ini
+accumulate	/man/1/cprof
+accumulate	/man/1/tail
+accumulated	/man/10/kprof
+accumulated	/man/2/math-fp
+accumulated	/man/3/kprof
+accumulates	/man/3/kprof
+accumulation	/man/1/mprof
+accurate	/man/1/alphabet-fs
+accurate	/man/1/fs
+accurate	/man/1/timestamp
+accurate	/man/1/units
+accurate	/man/1/wm-misc
+accurate	/man/2/srv
+accurate	/man/4/iostats
+accurately	/man/1/deb
+accurately	/man/1/ps
+ace	/man/2/spree-cardlib
+acentre	/man/2/spree-cardlib
+acentreleft	/man/2/spree-cardlib
+acentreright	/man/2/spree-cardlib
+acg	/man/2/string
+achieved	/man/1/charon
+achieved	/man/9/image
+achieves	/man/10/5cv
+achild	/man/2/w3c-xpointers
+acid	/man/10/2c
+acid	/man/10/a.out
+acid	/man/10/acid
+acid	/man/10/inm
+acid	/man/3/dbg
+acidinit	/man/10/acid
+ack	/man/1/calc
+ack	/man/1/math-misc
+ack	/man/6/login
+ack'd	/man/10/allocb
+ack.b	/man/1/math-misc
+ackermann's	/man/1/math-misc
+acknowledge	/man/10/intrenable
+acknowledge	/man/2/dialog
+acknowledged	/man/1/sh-file2chan
+acknowledged	/man/10/qio
+acknowledged	/man/2/filter
+acknowledged	/man/3/ip
+acknowledgement	/man/10/intrenable
+acknowledgement	/man/2/draw-context
+acknowledging	/man/2/wmsrv
+acl	/man/3/fs
+acm	/man/2/ubfa
+acm	/man/6/ubfa
+acme	/man/1/0intro
+acme	/man/1/acme
+acme	/man/1/brutus
+acme	/man/1/env
+acme	/man/1/mc
+acme	/man/1/wm-misc
+acme	/man/1/wm-sh
+acme	/man/2/pop3
+acme	/man/2/prof
+acme	/man/2/smtp
+acme	/man/3/touch
+acme	/man/4/0intro
+acme	/man/4/acme
+acme	/man/4/namespace
+acme	/man/6/keyboard
+acme	/man/6/regexp
+acme's	/man/1/acme
+acme.dump	/man/1/acme
+acmeaddr	/man/1/acme
+acos	/man/1/calc
+acos	/man/2/math-elem
+acosh	/man/1/calc
+acosh	/man/2/math-elem
+acquire	/man/1/acme
+acquire	/man/10/lock
+acquire	/man/2/draw-screen
+acquire	/man/2/hash
+acquire	/man/2/sys-0intro
+acquire	/man/2/sys-self
+acquire	/man/5/attach
+acquired	/man/10/lock
+acquired	/man/10/malloc
+acquired	/man/10/qlock
+acquired	/man/2/prefab-compound
+acquires	/man/10/kproc
+acquires	/man/10/qlock
+acquiring	/man/10/lock
+acquiring	/man/9/1copyright
+acquisition	/man/10/error
+acquisition	/man/9/1copyright
+acreate	/man/10/newchan
+acronym	/man/10/c2l
+acted	/man/4/spree
+actheight	/man/9/options
+acting	/man/1/bind
+acting	/man/10/dev
+acting	/man/2/security-0intro
+acting	/man/3/ip
+acting	/man/4/keyfs
+acting	/man/6/keys
+acting	/man/8/createsignerkey
+acting	/man/8/register
+acting	/man/8/signer
+acting	/man/9/1copyright
+actions	/man/1/acme
+actions	/man/1/collab
+actions	/man/1/ebook
+actions	/man/1/mash-tk
+actions	/man/1/mk
+actions	/man/1/sh-tk
+actions	/man/10/devattach
+actions	/man/10/error
+actions	/man/10/eve
+actions	/man/10/mk
+actions	/man/10/plan9.ini
+actions	/man/10/styxserver
+actions	/man/2/ir
+actions	/man/2/secstore
+actions	/man/2/spree-allow
+actions	/man/2/styxservers
+actions	/man/2/sys-0intro
+actions	/man/3/ftl
+actions	/man/3/pointer
+actions	/man/4/acme
+actions	/man/4/factotum
+actions	/man/4/spree
+actions	/man/5/clunk
+actions	/man/5/stat
+actions	/man/6/plumbing
+actions	/man/7/db
+actions	/man/9/button
+actions	/man/9/checkbutton
+actions	/man/9/entry
+actions	/man/9/menubutton
+actions	/man/9/radiobutton
+actions	/man/9/text
+activate	/man/2/prefab-0intro
+activate	/man/8/collabsrv
+activate	/man/9/button
+activate	/man/9/checkbutton
+activate	/man/9/listbox
+activate	/man/9/menu
+activate	/man/9/menubutton
+activate	/man/9/radiobutton
+activate	/man/9/scale
+activate	/man/9/scrollbar
+activated	/man/1/miniterm
+activated	/man/1/wm-sh
+activated	/man/2/spree-cardlib
+activated	/man/9/menu
+activates	/man/1/collab-clients
+activates	/man/9/button
+activates	/man/9/checkbutton
+activates	/man/9/menu
+activates	/man/9/menubutton
+activates	/man/9/radiobutton
+activation	/man/1/wm-sh
+activation	/man/10/error
+activation	/man/3/prog
+activation	/man/6/sbl
+active	/man/10/9load
+active	/man/10/acid
+active	/man/10/dev
+active	/man/10/intrenable
+active	/man/10/odbc
+active	/man/10/xalloc
+active	/man/2/disks
+active	/man/2/math-linalg
+active	/man/2/plumbmsg
+active	/man/2/popup
+active	/man/2/registries
+active	/man/2/styxservers
+active	/man/2/sys-file2chan
+active	/man/2/tftp
+active	/man/2/translate
+active	/man/2/venti
+active	/man/3/ftl
+active	/man/3/ip
+active	/man/3/plap
+active	/man/3/tls
+active	/man/4/ftpfs
+active	/man/4/registry
+active	/man/4/spree
+active	/man/5/version
+active	/man/8/collabsrv
+active	/man/8/prep
+active	/man/9/button
+active	/man/9/checkbutton
+active	/man/9/listbox
+active	/man/9/menu
+active	/man/9/menubutton
+active	/man/9/options
+active	/man/9/radiobutton
+active	/man/9/scale
+active	/man/9/scrollbar
+active	/man/9/text
+activebackground	/man/9/button
+activebackground	/man/9/checkbutton
+activebackground	/man/9/choicebutton
+activebackground	/man/9/menu
+activebackground	/man/9/menubutton
+activebackground	/man/9/options
+activebackground	/man/9/radiobutton
+activebackground	/man/9/scale
+activebackground	/man/9/scrollbar
+activeforeground	/man/9/button
+activeforeground	/man/9/checkbutton
+activeforeground	/man/9/choicebutton
+activeforeground	/man/9/menu
+activeforeground	/man/9/menubutton
+activeforeground	/man/9/options
+activeforeground	/man/9/radiobutton
+activerelief	/man/9/scrollbar
+activities	/man/1/collab
+activities	/man/1/collab-clients
+activities	/man/10/2l
+activity	/man/1/collab-clients
+activity	/man/2/sys-read
+activity	/man/4/dossrv
+activity	/man/8/collabsrv
+activity	/man/8/cs
+acts	/man/1/miniterm
+acts	/man/1/mk
+acts	/man/10/9load
+acts	/man/10/mk
+acts	/man/2/spree
+acts	/man/2/spree-gather
+acts	/man/2/styx
+acts	/man/2/sys-dial
+acts	/man/2/wmsrv
+acts	/man/3/srv9
+acts	/man/4/factotum
+acts	/man/4/lockfs
+acts	/man/4/registry
+acts	/man/5/0intro
+acts	/man/6/keyboard
+acts	/man/7/dbsrv
+actual	/man/1/cprof
+actual	/man/1/listen
+actual	/man/1/math-misc
+actual	/man/1/mprof
+actual	/man/1/prof
+actual	/man/1/sh-tk
+actual	/man/10/allocb
+actual	/man/10/ar
+actual	/man/10/print
+actual	/man/10/styxserver
+actual	/man/2/alphabet-intro
+actual	/man/2/asn1
+actual	/man/2/convcs
+actual	/man/2/dbm
+actual	/man/2/dial
+actual	/man/2/format
+actual	/man/2/palmfile
+actual	/man/2/spki-verifier
+actual	/man/2/spree-cardlib
+actual	/man/2/spree-gather
+actual	/man/2/sys-dial
+actual	/man/2/sys-millisec
+actual	/man/2/sys-print
+actual	/man/2/sys-sleep
+actual	/man/2/tk
+actual	/man/2/w3c-xpointers
+actual	/man/2/wmsrv
+actual	/man/3/draw
+actual	/man/3/pbus
+actual	/man/3/sign
+actual	/man/4/factotum
+actual	/man/4/memfs
+actual	/man/4/spree
+actual	/man/5/clunk
+actual	/man/5/stat
+actual	/man/6/keys
+actual	/man/6/plumbing
+actual	/man/8/rdbgsrv
+actual	/man/9/canvas
+actual	/man/9/cursor
+actual	/man/9/grid
+actual	/man/9/scale
+actual	/man/9/scrollbar
+actually	/man/1/deb
+actually	/man/1/filename
+actually	/man/1/sh-alphabet
+actually	/man/1/sum
+actually	/man/10/dev
+actually	/man/10/devattach
+actually	/man/10/dynld
+actually	/man/10/qio
+actually	/man/2/alphabet-intro
+actually	/man/2/command
+actually	/man/2/convcs
+actually	/man/2/filter
+actually	/man/2/prefab-compound
+actually	/man/2/secstore
+actually	/man/2/spree-gather
+actually	/man/2/styxflush
+actually	/man/2/sys-dirread
+actually	/man/2/sys-read
+actually	/man/2/tk
+actually	/man/4/acme
+actually	/man/4/namespace
+actually	/man/5/read
+actually	/man/6/man
+actually	/man/8/create
+actually	/man/8/sntp
+actually	/man/9/canvas
+actually	/man/9/image
+actually	/man/9/text
+actualsize	/man/3/vga
+actualsize	/man/4/memfs
+actwidth	/man/9/options
+actx	/man/2/tk
+actx	/man/9/options
+acty	/man/9/options
+acute	/man/1/m4
+ad	/man/10/plan9.ini
+adaptation	/man/6/colour
+adaptec	/man/10/plan9.ini
+adapter	/man/10/plan9.ini
+adapters	/man/10/plan9.ini
+adcs	/man/3/audio
+add	/man/1/ar
+add	/man/1/bind
+add	/man/1/calc
+add	/man/1/chmod
+add	/man/1/grid-monitor
+add	/man/1/ls
+add	/man/1/plumb
+add	/man/1/units
+add	/man/10/acid
+add	/man/10/iar
+add	/man/10/ntsrv
+add	/man/10/plan9.ini
+add	/man/10/styxserver
+add	/man/2/dict
+add	/man/2/draw-point
+add	/man/2/filter-deflate
+add	/man/2/keyring-ipint
+add	/man/2/math-0intro
+add	/man/2/popup
+add	/man/2/security-0intro
+add	/man/2/sets
+add	/man/2/sh
+add	/man/2/spree-allow
+add	/man/2/sys-bind
+add	/man/2/translate
+add	/man/3/ds
+add	/man/3/ether
+add	/man/3/flash
+add	/man/3/ftl
+add	/man/3/ip
+add	/man/3/logfs
+add	/man/3/prof
+add	/man/4/9srvfs
+add	/man/4/factotum
+add	/man/4/grid-cpu
+add	/man/4/keyfs
+add	/man/4/memfs
+add	/man/6/namespace
+add	/man/8/applylog
+add	/man/8/kfscmd
+add	/man/8/prep
+add	/man/9/canvas
+add	/man/9/menu
+add	/man/9/options
+add	/man/9/text
+addbase	/man/2/w3c-uris
+addbuiltin	/man/2/sh
+addclock0link	/man/10/delay
+addclock0link	/man/10/kbdputc
+added	/man/1/acme
+added	/man/1/cprof
+added	/man/1/dd
+added	/man/1/grid-query
+added	/man/1/grid-session
+added	/man/1/mash
+added	/man/1/mk
+added	/man/1/mprof
+added	/man/1/prof
+added	/man/1/sh
+added	/man/1/sh-alphabet
+added	/man/1/sh-file2chan
+added	/man/1/stack
+added	/man/1/tiny
+added	/man/1/tktester
+added	/man/10/0intro
+added	/man/10/a.out
+added	/man/10/master
+added	/man/10/mk
+added	/man/10/ntsrv
+added	/man/10/plan9.ini
+added	/man/10/styxserver
+added	/man/2/draw-display
+added	/man/2/draw-image
+added	/man/2/factotum
+added	/man/2/rfc822
+added	/man/2/sets
+added	/man/2/spree-cardlib
+added	/man/3/sd
+added	/man/3/ssl
+added	/man/6/keys
+added	/man/6/keytext
+added	/man/6/sbl
+added	/man/8/collabsrv
+added	/man/8/prep
+added	/man/9/grid
+added	/man/9/listbox
+added	/man/9/pack
+added	/man/9/text
+addethercard	/man/10/conf
+addictive	/man/1/wm-misc
+addindex	/man/2/spree-cardlib
+adding	/man/1/mash
+adding	/man/1/sh-alphabet
+adding	/man/10/devattach
+adding	/man/10/plan9.ini
+adding	/man/10/strcat
+adding	/man/2/w3c-uris
+adding	/man/3/pnp
+adding	/man/6/dis
+adding	/man/8/cs
+adding	/man/9/canvas
+addition	/man/1/acme
+addition	/man/1/bind
+addition	/man/1/fc
+addition	/man/1/mdb
+addition	/man/1/sh-expr
+addition	/man/1/wm-sh
+addition	/man/1/xd
+addition	/man/2/itslib
+addition	/man/2/sys-bind
+addition	/man/3/sd
+addition	/man/4/factotum
+addition	/man/5/open
+addition	/man/5/stat
+addition	/man/9/canvas
+addition	/man/9/checkbutton
+addition	/man/9/listbox
+addition	/man/9/radiobutton
+addition	/man/9/text
+additional	/man/1/avr
+additional	/man/1/mk
+additional	/man/1/sh-alphabet
+additional	/man/1/wc
+additional	/man/1/wm-sh
+additional	/man/10/mk
+additional	/man/10/ntsrv
+additional	/man/3/ip
+additional	/man/3/pnp
+additional	/man/4/dbfs
+additional	/man/4/factotum
+additional	/man/5/0intro
+additional	/man/6/keyboard
+additional	/man/9/button
+additional	/man/9/canvas
+additional	/man/9/checkbutton
+additional	/man/9/choicebutton
+additional	/man/9/entry
+additional	/man/9/frame
+additional	/man/9/grid
+additional	/man/9/label
+additional	/man/9/listbox
+additional	/man/9/menu
+additional	/man/9/menubutton
+additional	/man/9/panel
+additional	/man/9/radiobutton
+additional	/man/9/scale
+additional	/man/9/scrollbar
+additional	/man/9/text
+additionally	/man/1/charon
+additionally	/man/1/mash
+additionally	/man/1/sh-alphabet
+additionally	/man/2/venti
+additions	/man/1/tktester
+additions	/man/8/applylog
+addlayframe	/man/2/spree-cardlib
+addlayobj	/man/2/spree-cardlib
+addlist	/man/2/sets
+addmodule	/man/2/sh
+addmulti	/man/3/ether
+addmulti	/man/3/ip
+addpt	/man/2/draw-rect
+addr	/man/1/alphabet-abc
+addr	/man/1/alphabet-grid
+addr	/man/1/alphabet-main
+addr	/man/1/grid-monitor
+addr	/man/1/listen
+addr	/man/1/vacget
+addr	/man/10/dynld
+addr	/man/10/ms2
+addr	/man/2/bufio
+addr	/man/2/dial
+addr	/man/2/dis
+addr	/man/2/diskblocks
+addr	/man/2/ether
+addr	/man/2/registries
+addr	/man/2/secstore
+addr	/man/2/security-login
+addr	/man/2/srv
+addr	/man/2/sys-dial
+addr	/man/3/dbg
+addr	/man/4/acme
+addr	/man/4/lockfs
+addr	/man/4/vacfs
+addr	/man/7/db
+addr	/man/8/cs
+addr	/man/8/httpd
+addr&amask	/man/2/dis
+address	/man/1/acme
+address	/man/1/alphabet-abc
+address	/man/1/alphabet-grid
+address	/man/1/alphabet-main
+address	/man/1/bind
+address	/man/1/charon
+address	/man/1/collab
+address	/man/1/deb
+address	/man/1/dmview
+address	/man/1/grid-monitor
+address	/man/1/grid-register
+address	/man/1/listen
+address	/man/1/mdb
+address	/man/1/miniterm
+address	/man/1/netstat
+address	/man/1/ns
+address	/man/1/secstore
+address	/man/1/vacget
+address	/man/1/webgrab
+address	/man/10/2l
+address	/man/10/5coff
+address	/man/10/5cv
+address	/man/10/9load
+address	/man/10/a.out
+address	/man/10/acid
+address	/man/10/c2l
+address	/man/10/conf
+address	/man/10/dmainit
+address	/man/10/dynld
+address	/man/10/inb
+address	/man/10/inm
+address	/man/10/lock
+address	/man/10/ms2
+address	/man/10/plan9.ini
+address	/man/10/readnum
+address	/man/10/styxserver
+address	/man/10/xalloc
+address	/man/2/debug
+address	/man/2/dhcpclient
+address	/man/2/dial
+address	/man/2/dis
+address	/man/2/diskblocks
+address	/man/2/disks
+address	/man/2/ether
+address	/man/2/format
+address	/man/2/ip
+address	/man/2/registries
+address	/man/2/secstore
+address	/man/2/security-login
+address	/man/2/srv
+address	/man/2/sys-0intro
+address	/man/2/sys-dial
+address	/man/2/virgil
+address	/man/3/arch
+address	/man/3/boot
+address	/man/3/dbg
+address	/man/3/dynld
+address	/man/3/ether
+address	/man/3/fpga
+address	/man/3/i2c
+address	/man/3/ip
+address	/man/3/pbus
+address	/man/3/plap
+address	/man/3/pnp
+address	/man/3/prof
+address	/man/3/prog
+address	/man/3/usb
+address	/man/3/vid
+address	/man/4/acme
+address	/man/4/ftpfs
+address	/man/4/grid-cpu
+address	/man/4/registry
+address	/man/4/vacfs
+address	/man/6/attrdb
+address	/man/6/colour
+address	/man/6/dis
+address	/man/6/ndb
+address	/man/7/db
+address	/man/8/bootpd
+address	/man/8/collabsrv
+address	/man/8/cs
+address	/man/8/dhcp
+address	/man/8/dns
+address	/man/8/getauthinfo
+address	/man/8/httpd
+address	/man/8/prep
+address	/man/8/rip
+address	/man/8/styxchat
+address	/man/8/virgild
+addressable	/man/2/draw-display
+addressed	/man/1/mdb
+addressed	/man/1/telnet
+addressed	/man/10/c2l
+addressed	/man/2/venti
+addressed	/man/3/ip
+addressed	/man/3/pnp
+addressed	/man/4/acme
+addressed	/man/6/dis
+addresses	/man/1/acme
+addresses	/man/1/charon
+addresses	/man/1/xd
+addresses	/man/10/2l
+addresses	/man/10/9load
+addresses	/man/10/acid
+addresses	/man/10/allocb
+addresses	/man/10/plan9.ini
+addresses	/man/2/dhcpclient
+addresses	/man/2/dial
+addresses	/man/2/ether
+addresses	/man/2/ip
+addresses	/man/2/srv
+addresses	/man/2/sys-dial
+addresses	/man/3/boot
+addresses	/man/3/dbg
+addresses	/man/3/ether
+addresses	/man/3/flash
+addresses	/man/3/ip
+addresses	/man/3/plap
+addresses	/man/3/prof
+addresses	/man/3/prog
+addresses	/man/3/rtc
+addresses	/man/3/vid
+addresses	/man/4/acme
+addresses	/man/6/dis
+addresses	/man/6/ndb
+addresses	/man/8/bootpd
+addresses	/man/8/dns
+addresses	/man/8/init
+addresses	/man/8/ping
+addressing	/man/10/9load
+addressing	/man/10/dmainit
+addressing	/man/2/dis
+addressing	/man/2/ip
+addressing	/man/3/i2c
+addressing	/man/3/ip
+addressing	/man/6/dis
+addressing	/man/8/prep
+addressof	/man/2/ether
+addrs	/man/2/ip
+adds	/man/1/acme
+adds	/man/1/alphabet-main
+adds	/man/1/auplay
+adds	/man/1/man
+adds	/man/1/sh-alphabet
+adds	/man/1/stack
+adds	/man/1/webgrab
+adds	/man/10/allocb
+adds	/man/10/ar
+adds	/man/10/delay
+adds	/man/10/panic
+adds	/man/10/srclist
+adds	/man/2/dial
+adds	/man/2/dict
+adds	/man/2/filter-slip
+adds	/man/2/hash
+adds	/man/2/json
+adds	/man/2/popup
+adds	/man/2/sh
+adds	/man/2/spree-allow
+adds	/man/2/spree-cardlib
+adds	/man/3/ip
+adds	/man/3/logfs
+adds	/man/4/registry
+adds	/man/7/cddb
+adds	/man/8/ftl
+adds	/man/8/prep
+addsbuiltin	/man/2/sh
+addsrcdir	/man/10/acid
+addsym	/man/2/debug
+addtag	/man/9/canvas
+adequate	/man/10/sleep
+adequate	/man/3/touch
+adequate	/man/6/colour
+adescendant	/man/2/w3c-xpointers
+adhere	/man/1/tktester
+adhere	/man/10/error
+adhere	/man/2/command
+adhered	/man/1/0intro
+adheres	/man/2/sh
+adhering	/man/2/sh
+adhoc	/man/10/plan9.ini
+adiff	/man/1/acme
+adir	/man/10/styxserver
+adjacent	/man/1/uniq
+adjacent	/man/10/acid
+adjacent	/man/10/getfields
+adjacent	/man/2/draw-image
+adjacent	/man/3/draw
+adjacent	/man/4/registry
+adjacent	/man/6/attrdb
+adjacent	/man/9/entry
+adjacent	/man/9/grid
+adjacent	/man/9/text
+adjcenter	/man/2/prefab-element
+adjdown	/man/2/prefab-element
+adjequal	/man/2/prefab-element
+adjfill	/man/2/prefab-element
+adjleft	/man/2/prefab-element
+adjpack	/man/2/prefab-element
+adjright	/man/2/prefab-element
+adjup	/man/2/prefab-element
+adjust	/man/1/tktester
+adjust	/man/2/prefab-element
+adjust	/man/9/canvas
+adjust	/man/9/entry
+adjust	/man/9/listbox
+adjust	/man/9/options
+adjust	/man/9/scale
+adjust	/man/9/scrollbar
+adjust	/man/9/text
+adjustable	/man/10/qio
+adjustblock	/man/10/allocb
+adjusted	/man/1/acme
+adjusted	/man/10/ar
+adjusted	/man/10/dynld
+adjusted	/man/2/sys-export
+adjusted	/man/6/man
+adjusted	/man/9/canvas
+adjusted	/man/9/entry
+adjusted	/man/9/menu
+adjusted	/man/9/scale
+adjusted	/man/9/text
+adjusting	/man/1/collab
+adjusting	/man/2/tkclient
+adjusting	/man/2/wmclient
+adjusting	/man/9/text
+adjustment	/man/10/a.out
+adjustment	/man/2/sys-0intro
+adjustments	/man/1/date
+adjustments	/man/2/sys-0intro
+adjusts	/man/10/2c
+adjusts	/man/9/canvas
+adjusts	/man/9/entry
+adjusts	/man/9/listbox
+adjusts	/man/9/text
+adl	/man/8/kfscmd
+adm	/man/1/acme
+adm	/man/4/kfs
+adm	/man/6/users
+adm	/man/8/kfscmd
+adm	/man/8/mkfs
+adm.users	/man/8/mkfs
+administer	/man/8/0intro
+administer	/man/8/changelogin
+administration	/man/4/namespace
+administration	/man/8/0intro
+administration	/man/8/kfscmd
+administrative	/man/1/0intro
+administrative	/man/1/cp
+administrative	/man/6/keys
+administrative	/man/8/changelogin
+administrative	/man/8/cs
+administrator	/man/6/keys
+admit	/man/10/acid
+admits	/man/6/man
+adopted	/man/1/charon
+adopted	/man/2/security-0intro
+adpcm	/man/3/audio
+adpcm	/man/6/audio
+adressed	/man/9/grab
+adt	/man/1/deb
+adt	/man/1/wm
+adt	/man/1/yacc
+adt	/man/10/c2l
+adt	/man/10/devattach
+adt	/man/2/0intro
+adt	/man/2/alphabet-intro
+adt	/man/2/asn1
+adt	/man/2/attrdb
+adt	/man/2/bufio
+adt	/man/2/cfg
+adt	/man/2/complete
+adt	/man/2/crc
+adt	/man/2/daytime
+adt	/man/2/dbm
+adt	/man/2/debug
+adt	/man/2/dhcpclient
+adt	/man/2/dial
+adt	/man/2/dict
+adt	/man/2/dis
+adt	/man/2/diskblocks
+adt	/man/2/disks
+adt	/man/2/dividers
+adt	/man/2/draw-context
+adt	/man/2/draw-display
+adt	/man/2/draw-font
+adt	/man/2/draw-image
+adt	/man/2/draw-point
+adt	/man/2/draw-pointer
+adt	/man/2/draw-rect
+adt	/man/2/draw-screen
+adt	/man/2/factotum
+adt	/man/2/filter
+adt	/man/2/format
+adt	/man/2/geodesy
+adt	/man/2/hash
+adt	/man/2/ida
+adt	/man/2/imagefile
+adt	/man/2/ip
+adt	/man/2/itslib
+adt	/man/2/json
+adt	/man/2/keyring-0intro
+adt	/man/2/keyring-crypt
+adt	/man/2/keyring-ipint
+adt	/man/2/lists
+adt	/man/2/lock
+adt	/man/2/palmfile
+adt	/man/2/plumbmsg
+adt	/man/2/prefab-compound
+adt	/man/2/prefab-element
+adt	/man/2/prefab-environ
+adt	/man/2/prefab-style
+adt	/man/2/print
+adt	/man/2/prof
+adt	/man/2/registries
+adt	/man/2/rfc822
+adt	/man/2/scsiio
+adt	/man/2/security-login
+adt	/man/2/security-ssl
+adt	/man/2/sets
+adt	/man/2/sexprs
+adt	/man/2/sh
+adt	/man/2/spki
+adt	/man/2/spki-verifier
+adt	/man/2/spree
+adt	/man/2/spree-cardlib
+adt	/man/2/stringinttab
+adt	/man/2/styx
+adt	/man/2/styxservers
+adt	/man/2/styxservers-nametree
+adt	/man/2/sys-dial
+adt	/man/2/sys-file2chan
+adt	/man/2/sys-self
+adt	/man/2/sys-stat
+adt	/man/2/timers
+adt	/man/2/tk
+adt	/man/2/translate
+adt	/man/2/ubfa
+adt	/man/2/venti
+adt	/man/2/w3c-css
+adt	/man/2/w3c-uris
+adt	/man/2/w3c-xpointers
+adt	/man/2/wmclient
+adt	/man/2/wmsrv
+adt	/man/2/xml
+adt	/man/5/stat
+adt	/man/6/dis
+adt	/man/6/sbl
+adt	/man/7/db
+adt's	/man/6/sbl
+adts	/man/2/debug
+adts	/man/2/devpointer
+adts	/man/2/keyring-0intro
+adts	/man/2/keyring-certtostr
+adts	/man/2/plumbmsg
+adts	/man/2/w3c-css
+adts	/man/6/sbl
+adts	/man/8/httpd
+adv7176	/man/3/vid
+advance	/man/2/complete
+advance	/man/2/sys-0intro
+advanced	/man/1/deb
+advanced	/man/10/plan9.ini
+advanced	/man/2/keyring-crypt
+advanced	/man/2/sexprs
+advanced	/man/2/sys-dirread
+advanced	/man/2/sys-read
+advanced	/man/6/sexprs
+advances	/man/1/mdb
+advances	/man/2/security-0intro
+advances	/man/7/db
+advancing	/man/1/mdb
+advantage	/man/1/0intro
+advantage	/man/1/cpu
+advantage	/man/1/dd
+advantage	/man/6/colour
+advantages	/man/6/colour
+advert	/man/2/ip
+advertise	/man/4/registry
+adverts	/man/2/ip
+advisable	/man/9/canvas
+advised	/man/2/styx
+advised	/man/9/1copyright
+advisory	/man/5/0intro
+ae	/man/6/keyboard
+aes	/man/1/crypt
+aes	/man/2/keyring-0intro
+aes	/man/2/keyring-crypt
+aes	/man/4/keyfs
+aesbsize	/man/2/keyring-crypt
+aescbc	/man/1/crypt
+aescbc	/man/2/keyring-crypt
+aescbc.b	/man/1/crypt
+aessetup	/man/2/keyring-crypt
+aesstate	/man/2/keyring-crypt
+afd	/man/2/factotum
+afd	/man/2/sys-bind
+afd	/man/2/sys-fauth
+affect	/man/1/emu
+affect	/man/2/draw-screen
+affect	/man/2/env
+affect	/man/2/sys-pctl
+affect	/man/4/acme
+affect	/man/5/open
+affect	/man/9/update
+affected	/man/1/alphabet-fs
+affected	/man/1/charon
+affected	/man/1/diff
+affected	/man/1/fs
+affected	/man/1/sort
+affected	/man/2/command
+affected	/man/2/draw-image
+affected	/man/5/walk
+affected	/man/9/options
+affected	/man/9/text
+affecting	/man/2/spree
+affecting	/man/2/sys-chdir
+affecting	/man/5/stat
+affecting	/man/9/listbox
+affects	/man/1/emu
+affects	/man/2/draw-0intro
+affects	/man/2/draw-image
+affects	/man/2/sys-0intro
+affects	/man/2/sys-pctl
+affects	/man/5/read
+affixes	/man/1/basename
+afid	/man/10/styx
+afid	/man/2/styx
+afid	/man/2/styxservers
+afid	/man/5/0intro
+afid	/man/5/attach
+afid	/man/8/styxchat
+afile	/man/1/ar
+afile	/man/10/iar
+afollowing	/man/2/w3c-xpointers
+afp	/man/2/dis
+afresh	/man/8/createsignerkey
+ag	/man/2/keyring-crypt
+agency	/man/8/signer
+agent	/man/1/bind
+agent	/man/2/factotum
+agent	/man/4/factotum
+agent	/man/4/import
+agent	/man/5/0intro
+agent	/man/8/register
+agent	/man/8/signer
+aggregate	/man/1/mk
+aggregate	/man/10/mk
+aggregates	/man/1/mk
+aggregates	/man/10/mk
+aghnsu	/man/10/inm
+agp	/man/10/plan9.ini
+agree	/man/2/disks
+agree	/man/2/ida
+agree	/man/8/collabsrv
+agreed	/man/1/collab-clients
+agreed	/man/2/factotum
+agreed	/man/2/spki-verifier
+agreed	/man/2/styx
+agreed	/man/3/mnt
+agreed	/man/3/mpeg
+agreed	/man/6/login
+agreement	/man/1/diff
+agreement	/man/9/1copyright
+agrees	/man/10/qlock
+agrep	/man/1/acme
+aha1542	/man/10/plan9.ini
+ai	/man/2/factotum
+ai	/man/2/security-auth
+ai2key	/man/8/ai2key
+ai2key.b	/man/8/ai2key
+aid	/man/10/plan9.ini
+aid	/man/3/sd
+aid	/man/6/login
+aids	/man/6/colour
+aif	/man/10/5cv
+aimm	/man/2/dis
+ainb	/man/1/alphabet-fs
+ainb	/man/1/fs
+aind	/man/2/dis
+aindex	/man/2/dis
+al	/man/10/ar
+alarm	/man/10/mk
+alaw	/man/3/audio
+alaw	/man/6/audio
+albeit	/man/2/translate
+alef	/man/10/mk
+alen	/man/1/sh-string
+alert	/man/3/tls
+alerting	/man/3/tls
+alertno	/man/3/tls
+alerts	/man/3/tls
+alg	/man/1/alphabet-main
+alg	/man/1/bind
+alg	/man/1/collab
+alg	/man/1/cpu
+alg	/man/1/crypt
+alg	/man/1/listen
+alg	/man/1/math-misc
+alg	/man/2/keyring-crypt
+alg	/man/2/security-auth
+alg	/man/2/spki
+alg	/man/3/sign
+alg	/man/3/ssl
+alg	/man/4/lockfs
+alg	/man/6/namespace
+alg	/man/7/db
+alg	/man/7/dbsrv
+alg	/man/8/createsignerkey
+alg	/man/8/rstyxd
+algebra	/man/2/draw-image
+algebra	/man/2/math-0intro
+algebra	/man/2/math-linalg
+algname	/man/2/keyring-gensk
+algorithm	/man/1/alphabet-main
+algorithm	/man/1/bind
+algorithm	/man/1/charon
+algorithm	/man/1/collab
+algorithm	/man/1/cpu
+algorithm	/man/1/crypt
+algorithm	/man/1/diff
+algorithm	/man/1/gzip
+algorithm	/man/1/idea
+algorithm	/man/1/listen
+algorithm	/man/1/math-misc
+algorithm	/man/1/netkey
+algorithm	/man/1/rcmd
+algorithm	/man/1/sum
+algorithm	/man/1/wm-misc
+algorithm	/man/10/2l
+algorithm	/man/10/a.out
+algorithm	/man/2/crc
+algorithm	/man/2/filter
+algorithm	/man/2/ida
+algorithm	/man/2/keyring-0intro
+algorithm	/man/2/keyring-certtostr
+algorithm	/man/2/keyring-crypt
+algorithm	/man/2/keyring-gensk
+algorithm	/man/2/keyring-rc4
+algorithm	/man/2/keyring-sha1
+algorithm	/man/2/rand
+algorithm	/man/2/security-0intro
+algorithm	/man/2/security-auth
+algorithm	/man/2/security-login
+algorithm	/man/2/spki
+algorithm	/man/2/spree-cardlib
+algorithm	/man/2/sys-0intro
+algorithm	/man/3/flash
+algorithm	/man/3/pnp
+algorithm	/man/3/sign
+algorithm	/man/3/ssl
+algorithm	/man/3/tls
+algorithm	/man/4/import
+algorithm	/man/4/lockfs
+algorithm	/man/5/open
+algorithm	/man/6/dis
+algorithm	/man/6/keytext
+algorithm	/man/6/login
+algorithm	/man/7/db
+algorithm	/man/7/dbsrv
+algorithm	/man/8/ai2key
+algorithm	/man/8/createsignerkey
+algorithm	/man/8/rstyxd
+algorithm	/man/9/grid
+algorithm	/man/9/pack
+algorithms	/man/1/bind
+algorithms	/man/1/collab
+algorithms	/man/1/cpu
+algorithms	/man/1/crypt
+algorithms	/man/1/rcmd
+algorithms	/man/2/keyring-0intro
+algorithms	/man/2/keyring-crypt
+algorithms	/man/2/security-0intro
+algorithms	/man/2/spki
+algorithms	/man/3/ssl
+algorithms	/man/3/tls
+algorithms	/man/4/import
+algs	/man/2/security-auth
+algs	/man/2/spki
+alias	/man/1/bind
+alias	/man/2/convcs
+alias	/man/2/sys-bind
+alias	/man/6/namespace
+alias	/man/8/dns
+aliased	/man/2/draw-image
+aliases	/man/1/tcs
+aliases	/man/2/convcs
+aliases	/man/2/print
+aliasing	/man/2/convcs
+aliasing	/man/2/draw-image
+alice	/man/2/sexprs
+align	/man/10/xalloc
+align	/man/3/draw
+align	/man/3/vga
+align	/man/9/text
+aligned	/man/1/mc
+aligned	/man/10/allocb
+aligned	/man/10/malloc
+aligned	/man/10/xalloc
+aligned	/man/2/draw-image
+aligned	/man/3/draw
+aligned	/man/3/vga
+aligned	/man/9/options
+alignment	/man/10/dmainit
+alignment	/man/10/xalloc
+alignment	/man/2/draw-image
+alignment	/man/3/flash
+alignment	/man/3/ftl
+alignment	/man/3/vga
+alignment	/man/9/text
+aligns	/man/2/draw-image
+aligns	/man/3/draw
+alive	/man/3/ip
+allbits	/man/2/ip
+allfids	/man/2/styxservers
+alloc	/man/1/mk
+alloc	/man/10/mk
+allocate	/man/10/allocb
+allocate	/man/10/devattach
+allocate	/man/10/malloc
+allocate	/man/10/newchan
+allocate	/man/10/styxserver
+allocate	/man/10/xalloc
+allocate	/man/2/diskblocks
+allocate	/man/2/dividers
+allocate	/man/2/draw-0intro
+allocate	/man/2/draw-display
+allocate	/man/2/draw-screen
+allocate	/man/2/prefab-0intro
+allocate	/man/3/draw
+allocate	/man/3/ip
+allocate	/man/3/prog
+allocate	/man/7/db
+allocate	/man/8/dhcp
+allocated	/man/1/mprof
+allocated	/man/10/9load
+allocated	/man/10/allocb
+allocated	/man/10/dev
+allocated	/man/10/devattach
+allocated	/man/10/dmainit
+allocated	/man/10/malloc
+allocated	/man/10/newchan
+allocated	/man/10/odbc
+allocated	/man/10/parsecmd
+allocated	/man/10/print
+allocated	/man/10/qlock
+allocated	/man/2/bufio
+allocated	/man/2/bufio-chanfill
+allocated	/man/2/diskblocks
+allocated	/man/2/draw-0intro
+allocated	/man/2/draw-context
+allocated	/man/2/draw-display
+allocated	/man/2/draw-image
+allocated	/man/2/draw-screen
+allocated	/man/2/keyring-sha1
+allocated	/man/2/prefab-element
+allocated	/man/2/prefab-style
+allocated	/man/2/spree
+allocated	/man/2/tk
+allocated	/man/2/wmclient
+allocated	/man/3/cmd
+allocated	/man/3/ip
+allocated	/man/3/tls
+allocated	/man/4/memfs
+allocated	/man/5/flush
+allocated	/man/6/dis
+allocated	/man/8/prep
+allocated	/man/9/grid
+allocated	/man/9/options
+allocated	/man/9/panel
+allocates	/man/10/allocb
+allocates	/man/10/dmainit
+allocates	/man/10/xalloc
+allocates	/man/2/diskblocks
+allocates	/man/2/disks
+allocates	/man/2/draw-screen
+allocates	/man/2/security-ssl
+allocates	/man/2/sys-0intro
+allocates	/man/2/sys-pipe
+allocates	/man/3/cmd
+allocates	/man/3/pipe
+allocates	/man/3/plap
+allocates	/man/3/vga
+allocates	/man/9/pack
+allocating	/man/1/mprof
+allocating	/man/10/xalloc
+allocating	/man/2/draw-display
+allocating	/man/2/prefab-compound
+allocation	/man/1/emu
+allocation	/man/10/allocb
+allocation	/man/10/dev
+allocation	/man/10/malloc
+allocation	/man/10/plan9.ini
+allocation	/man/10/xalloc
+allocation	/man/2/draw-display
+allocation	/man/2/draw-font
+allocation	/man/2/draw-screen
+allocation	/man/2/prefab-style
+allocation	/man/3/cons
+allocation	/man/3/prof
+allocations	/man/2/0intro
+allocations	/man/3/cons
+allocations	/man/3/prof
+allocators	/man/10/malloc
+allocators	/man/10/xalloc
+allocb	/man/10/allocb
+allocb	/man/10/dev
+allocb	/man/10/devattach
+allocb	/man/10/qio
+allotted	/man/2/prefab-element
+allow	/man/1/0intro
+allow	/man/1/acme
+allow	/man/1/alphabet-fs
+allow	/man/1/blur
+allow	/man/1/deb
+allow	/man/1/dmview
+allow	/man/1/emu
+allow	/man/1/fs
+allow	/man/1/grid-query
+allow	/man/1/kill
+allow	/man/1/listen
+allow	/man/1/miniterm
+allow	/man/1/secstore
+allow	/man/1/tiny
+allow	/man/1/tktester
+allow	/man/1/wm-misc
+allow	/man/10/acid
+allow	/man/10/dev
+allow	/man/10/devattach
+allow	/man/10/dynld
+allow	/man/10/inb
+allow	/man/10/lock
+allow	/man/10/master
+allow	/man/10/qio
+allow	/man/10/styxserver
+allow	/man/2/alphabet-intro
+allow	/man/2/arg
+allow	/man/2/command
+allow	/man/2/dividers
+allow	/man/2/draw-0intro
+allow	/man/2/draw-display
+allow	/man/2/draw-image
+allow	/man/2/keyring-0intro
+allow	/man/2/keyring-getmsg
+allow	/man/2/keyring-sha1
+allow	/man/2/palmfile
+allow	/man/2/plumbmsg
+allow	/man/2/prof
+allow	/man/2/sets
+allow	/man/2/sh
+allow	/man/2/spki
+allow	/man/2/spki-verifier
+allow	/man/2/spree
+allow	/man/2/spree-allow
+allow	/man/2/spree-cardlib
+allow	/man/2/spree-objstore
+allow	/man/2/styx
+allow	/man/2/styxservers
+allow	/man/2/sys-stat
+allow	/man/2/sys-werrstr
+allow	/man/2/translate
+allow	/man/3/cons
+allow	/man/3/ftl
+allow	/man/3/gpio
+allow	/man/3/ip
+allow	/man/3/plap
+allow	/man/3/pointer
+allow	/man/3/prog
+allow	/man/3/root
+allow	/man/3/srv9
+allow	/man/3/ssl
+allow	/man/3/tinyfs
+allow	/man/4/9srvfs
+allow	/man/4/factotum
+allow	/man/4/ftpfs
+allow	/man/4/kfs
+allow	/man/5/0intro
+allow	/man/5/open
+allow	/man/5/remove
+allow	/man/6/dis
+allow	/man/6/ndb
+allow	/man/6/sexprs
+allow	/man/7/dbsrv
+allow	/man/8/applylog
+allow	/man/8/cs
+allow	/man/8/kfscmd
+allow	/man/8/rstyxd
+allow	/man/8/svc
+allow	/man/9/listbox
+allow	/man/9/menu
+allow	/man/9/menubutton
+allow	/man/9/text
+allow.b	/man/2/spree-allow
+allow.m	/man/2/spree-allow
+allowable	/man/2/keyring-0intro
+allowable	/man/2/rfc822
+allowable	/man/9/canvas
+allowable	/man/9/text
+allowance	/man/2/prefab-compound
+allowing	/man/1/avr
+allowing	/man/1/collab-clients
+allowing	/man/1/grid-monitor
+allowing	/man/1/listen
+allowing	/man/1/logon
+allowing	/man/1/sh
+allowing	/man/1/toolbar
+allowing	/man/10/devattach
+allowing	/man/2/alphabet-intro
+allowing	/man/2/dbm
+allowing	/man/2/disks
+allowing	/man/2/dividers
+allowing	/man/2/exception
+allowing	/man/2/ip
+allowing	/man/2/math-export
+allowing	/man/2/readdir
+allowing	/man/2/regex
+allowing	/man/2/spki
+allowing	/man/2/spree
+allowing	/man/2/sys-pctl
+allowing	/man/2/timers
+allowing	/man/2/wmsrv
+allowing	/man/3/cmd
+allowing	/man/3/ip
+allowing	/man/3/logfs
+allowing	/man/3/srv9
+allowing	/man/3/touch
+allowing	/man/3/usb
+allowing	/man/4/9srvfs
+allowing	/man/4/import
+allowing	/man/4/lockfs
+allowing	/man/4/ramfile
+allowing	/man/4/registry
+allowing	/man/6/keys
+allowing	/man/8/plumber
+allowing	/man/8/styxchat
+allowing	/man/8/touchcal
+allowing	/man/9/choicebutton
+allowoff	/man/8/kfscmd
+allows	/man/1/0intro
+allows	/man/1/acme
+allows	/man/1/alphabet-abc
+allows	/man/1/alphabet-grid
+allows	/man/1/collab-clients
+allows	/man/1/cprof
+allows	/man/1/deb
+allows	/man/1/ebook
+allows	/man/1/filename
+allows	/man/1/grid-query
+allows	/man/1/grid-register
+allows	/man/1/grid-session
+allows	/man/1/keyboard
+allows	/man/1/mash
+allows	/man/1/mdb
+allows	/man/1/sh
+allows	/man/1/sh-expr
+allows	/man/1/sh-mload
+allows	/man/1/sh-tk
+allows	/man/1/tkcmd
+allows	/man/1/wm-misc
+allows	/man/1/wm-sh
+allows	/man/10/2c
+allows	/man/10/5cv
+allows	/man/10/eve
+allows	/man/10/master
+allows	/man/10/ntsrv
+allows	/man/10/qio
+allows	/man/10/qlock
+allows	/man/10/styxserver
+allows	/man/2/alphabet-intro
+allows	/man/2/asn1
+allows	/man/2/bufio
+allows	/man/2/debug
+allows	/man/2/dhcpclient
+allows	/man/2/dial
+allows	/man/2/diskblocks
+allows	/man/2/draw-0intro
+allows	/man/2/draw-context
+allows	/man/2/draw-display
+allows	/man/2/draw-image
+allows	/man/2/ip
+allows	/man/2/keyring-0intro
+allows	/man/2/keyring-crypt
+allows	/man/2/lock
+allows	/man/2/msgio
+allows	/man/2/palmfile
+allows	/man/2/plumbmsg
+allows	/man/2/prefab-element
+allows	/man/2/regex
+allows	/man/2/selectfile
+allows	/man/2/spree
+allows	/man/2/styxservers-nametree
+allows	/man/2/sys-dial
+allows	/man/2/wait
+allows	/man/3/arch
+allows	/man/3/cap
+allows	/man/3/cons
+allows	/man/3/dbg
+allows	/man/3/fpga
+allows	/man/3/fs
+allows	/man/3/ftl
+allows	/man/3/indir
+allows	/man/3/ip
+allows	/man/3/kprof
+allows	/man/3/logfs
+allows	/man/3/prof
+allows	/man/3/prog
+allows	/man/3/srv9
+allows	/man/3/vga
+allows	/man/4/factotum
+allows	/man/4/iostats
+allows	/man/4/keysrv
+allows	/man/4/kfs
+allows	/man/4/spree
+allows	/man/5/0intro
+allows	/man/5/open
+allows	/man/6/dis
+allows	/man/7/db
+allows	/man/8/styxmon
+allows	/man/9/canvas
+allows	/man/9/entry
+allows	/man/9/text
+allsat	/man/2/lists
+alltarget	/man/1/mk
+alltarget	/man/10/mk
+alone	/man/2/prefab-element
+alone	/man/3/dbg
+alone	/man/8/rdbgsrv
+alone	/man/9/canvas
+alongside	/man/1/charon
+alongside	/man/1/mux
+alowercentre	/man/2/spree-cardlib
+alowerleft	/man/2/spree-cardlib
+alowerright	/man/2/spree-cardlib
+alpha	/man/10/2c
+alpha	/man/10/a.out
+alpha	/man/2/draw-0intro
+alpha	/man/2/draw-display
+alpha	/man/2/draw-image
+alpha	/man/2/keyring-0intro
+alpha	/man/2/keyring-auth
+alpha	/man/2/math-linalg
+alpha	/man/3/draw
+alpha	/man/4/factotum
+alpha	/man/6/auth
+alpha	/man/6/colour
+alpha	/man/6/image
+alpha	/man/6/keytext
+alpha	/man/6/login
+alpha	/man/8/ai2key
+alpha	/man/9/image
+alpha	/man/9/types
+alphabet	/man/1/alphabet-abc
+alphabet	/man/1/alphabet-fs
+alphabet	/man/1/alphabet-grid
+alphabet	/man/1/alphabet-main
+alphabet	/man/1/sh-alphabet
+alphabet	/man/2/alphabet-intro
+alphabet	/man/2/draw-font
+alphabet	/man/2/sexprs
+alphabet	/man/6/sexprs
+alphabet.b	/man/1/alphabet-main
+alphabetic	/man/1/m4
+alphabetical	/man/2/filepat
+alphabetically	/man/10/inm
+alphabetically	/man/2/readdir
+alphabetics	/man/1/dd
+alphabetics	/man/1/tr
+alphanumeric	/man/1/mash
+alphanumeric	/man/1/sh
+alphanumerics	/man/1/acme
+alq	/man/8/ping
+alt	/man/1/limbo
+alt	/man/1/sh-tk
+alt	/man/10/9load
+alt	/man/2/0intro
+alt	/man/2/popup
+alt	/man/2/styxflush
+alt	/man/2/timers
+alt	/man/2/tkclient
+alt	/man/2/wait
+alt	/man/3/prog
+alt	/man/6/keyboard
+alter	/man/1/ar
+alter	/man/1/mash
+alter	/man/1/wm-misc
+alter	/man/10/iar
+alter	/man/10/strcat
+alter	/man/2/spree
+alter	/man/9/grid
+altera	/man/3/fpga
+altera	/man/8/fpgaload
+altered	/man/5/stat
+altering	/man/1/wm-misc
+alternate	/man/10/print
+alternate	/man/2/security-0intro
+alternate	/man/9/checkbutton
+alternating	/man/3/tv
+alternating	/man/6/man
+alternation	/man/6/man
+alternative	/man/1/collab-clients
+alternative	/man/1/mash
+alternative	/man/1/sh-expr
+alternative	/man/10/5cv
+alternative	/man/10/delay
+alternative	/man/2/daytime
+alternative	/man/2/dis
+alternative	/man/2/encoding
+alternative	/man/2/print
+alternative	/man/2/styx
+alternative	/man/2/sys-print
+alternative	/man/6/regexp
+alternative	/man/6/sbl
+alternative	/man/8/cs
+alternative	/man/8/dns
+alternative	/man/8/plumber
+alternatively	/man/1/collab-clients
+alternatively	/man/2/prefab-element
+alternatively	/man/2/string
+alternatively	/man/3/ip
+alternatively	/man/3/pnp
+alternatively	/man/9/types
+alternatives	/man/1/tiny
+alternatives	/man/2/dialog
+alternatives	/man/2/dis
+alternatives	/man/2/styx
+alters	/man/2/geodesy
+altmodule	/man/1/mash-make
+altogether	/man/2/prof
+am79c970	/man/10/plan9.ini
+amask	/man/2/dis
+ambiguities	/man/1/yacc
+ambiguities	/man/2/draw-image
+ambiguities	/man/3/draw
+ambiguity	/man/1/unicode
+ambiguity	/man/2/0intro
+ambiguity	/man/2/sys-print
+ambiguity	/man/4/trfs
+ambiguous	/man/1/calc
+ambiguous	/man/1/mk
+ambiguous	/man/1/sh-alphabet
+ambiguous	/man/1/sh-csv
+ambiguous	/man/1/yacc
+ambiguous	/man/10/mk
+amd	/man/10/a.out
+amd	/man/10/plan9.ini
+amd	/man/3/ether
+amd64	/man/10/2c
+amd79c970	/man/10/plan9.ini
+amexit	/man/2/ir
+amode	/man/10/newchan
+amongst	/man/1/bind
+amongst	/man/1/chgrp
+amongst	/man/1/collab-clients
+amongst	/man/10/conf
+amongst	/man/10/dev
+amongst	/man/10/dmainit
+amongst	/man/2/keyring-auth
+amongst	/man/2/spree-cardlib
+amongst	/man/2/sys-0intro
+amongst	/man/3/prog
+amongst	/man/3/touch
+amongst	/man/6/colour
+amongst	/man/8/collabsrv
+amount	/man/1/charon
+amount	/man/1/grid-register
+amount	/man/1/mdb
+amount	/man/1/mprof
+amount	/man/1/prof
+amount	/man/1/ps
+amount	/man/1/sh-file2chan
+amount	/man/1/wm-misc
+amount	/man/1/yacc
+amount	/man/10/allocb
+amount	/man/10/newchan
+amount	/man/10/styx
+amount	/man/2/draw-rect
+amount	/man/2/prof
+amount	/man/2/scsiio
+amount	/man/2/styx
+amount	/man/2/sys-fauth
+amount	/man/3/cons
+amount	/man/3/logfs
+amount	/man/3/prof
+amount	/man/3/prog
+amount	/man/4/dbfs
+amount	/man/4/iostats
+amount	/man/5/read
+amount	/man/6/man
+amount	/man/9/grid
+amount	/man/9/options
+amount	/man/9/pack
+amount	/man/9/text
+amounts	/man/1/deb
+amounts	/man/2/sys-iounit
+amounts	/man/4/iostats
+amounts	/man/9/text
+amp	/man/2/dis
+ampersand	/man/1/mash
+ampersand	/man/1/mk
+ampersand	/man/1/tiny
+ampersand	/man/10/mk
+ampersand	/man/2/spree-allow
+ampersands	/man/1/sh
+amstartinput	/man/2/ir
+analogous	/man/1/acme
+analogous	/man/10/rune
+analogous	/man/10/styx
+analogous	/man/2/draw-display
+analogous	/man/4/acme
+analogue	/man/1/wm-misc
+analogues	/man/2/cfg
+analyse	/man/1/yacc
+analyse	/man/3/kprof
+analyser	/man/1/yacc
+analysis	/man/1/blur
+analysis	/man/2/complete
+analysis	/man/2/math-0intro
+aname	/man/1/alphabet-main
+aname	/man/1/listen
+aname	/man/10/styx
+aname	/man/10/styxserver
+aname	/man/2/factotum
+aname	/man/2/styx
+aname	/man/2/styxservers
+aname	/man/2/sys-bind
+aname	/man/2/sys-fauth
+aname	/man/3/0intro
+aname	/man/5/0intro
+aname	/man/5/attach
+aname	/man/6/namespace
+aname	/man/8/styxchat
+anamespace	/man/2/w3c-xpointers
+ancestor	/man/1/alphabet-fs
+ancestor	/man/1/fs
+ancestor	/man/9/canvas
+ancestors	/man/2/spree
+anchor	/man/2/spree-cardlib
+anchor	/man/9/button
+anchor	/man/9/canvas
+anchor	/man/9/checkbutton
+anchor	/man/9/choicebutton
+anchor	/man/9/entry
+anchor	/man/9/grid
+anchor	/man/9/label
+anchor	/man/9/listbox
+anchor	/man/9/menubutton
+anchor	/man/9/options
+anchor	/man/9/pack
+anchor	/man/9/panel
+anchor	/man/9/radiobutton
+anchor	/man/9/types
+anchored	/man/2/spree-cardlib
+anchorpos	/man/9/canvas
+anchorpos	/man/9/types
+anded	/man/2/sys-open
+angle	/man/1/yacc
+angle	/man/2/draw-image
+angle	/man/2/w3c-css
+angle	/man/9/canvas
+angles	/man/1/calc
+angles	/man/2/draw-image
+angles	/man/3/draw
+angles	/man/9/canvas
+angular	/man/9/canvas
+animate	/man/1/charon
+animated	/man/1/charon
+animating	/man/9/canvas
+anmd	/man/1/mk
+annot	/man/1/ebook
+annotate	/man/1/limbo
+annotates	/man/10/2l
+annotation	/man/1/ebook
+annotation	/man/9/text
+annotations	/man/1/ebook
+annotations	/man/9/scale
+annotations	/man/9/text
+announce	/man/1/grid-register
+announce	/man/1/listen
+announce	/man/10/styxserver
+announce	/man/2/dial
+announce	/man/2/sys-dial
+announce	/man/3/ip
+announce	/man/3/plap
+announce	/man/8/cs
+announced	/man/1/listen
+announced	/man/1/secstore
+announced	/man/3/ip
+announced	/man/3/plap
+announcement	/man/1/listen
+announcement	/man/2/dial
+announcement	/man/2/sys-dial
+announcements	/man/8/rip
+announces	/man/1/collab-clients
+announces	/man/10/qlock
+announcing	/man/1/listen
+announcing	/man/8/cs
+annoyances	/man/1/charon
+annoying	/man/1/charon
+annoyingly	/man/1/wm-misc
+anon	/man/10/c2l
+anonymous	/man/10/c2l
+anonymous	/man/4/ftpfs
+anonymous	/man/4/registry
+ansi	/man/10/0intro
+ansi	/man/10/2c
+ansi	/man/10/c2l
+ansi	/man/10/error
+ansi	/man/10/memory
+ansi	/man/10/print
+ansi	/man/10/strcat
+ansi	/man/2/security-0intro
+anstu	/man/1/du
+answer	/man/2/factotum
+answer	/man/2/styxservers
+answer	/man/2/translate
+answer	/man/2/virgil
+answer	/man/5/flush
+answer	/man/8/signer
+answering	/man/2/translate
+answering	/man/8/bootpd
+answering	/man/8/cs
+answers	/man/1/collab-clients
+answers	/man/8/ping
+antecedent	/man/1/mdb
+anti	/man/1/wm-misc
+anti	/man/2/draw-image
+anti	/man/3/tls
+anticipation	/man/7/db
+anybody	/man/1/ls
+anymore	/man/2/disks
+anyone	/man/10/styxserver
+anyone	/man/2/security-0intro
+anyone	/man/2/sys-stat
+anyone	/man/3/logfs
+anyone	/man/4/factotum
+anyone	/man/4/registry
+anyone	/man/5/stat
+anysat	/man/2/lists
+anysigner	/man/4/factotum
+anyway	/man/2/sys-print
+anywhere	/man/1/acme
+anywhere	/man/1/sh
+anywhere	/man/4/namespace
+anywhere	/man/9/menubutton
+aopen	/man/10/newchan
+aopen	/man/2/bufio
+aoutb	/man/1/alphabet-fs
+aoutb	/man/1/fs
+aoverb	/man/1/alphabet-fs
+aoverb	/man/1/fs
+ap	/man/10/styx
+aparent	/man/2/w3c-xpointers
+apart	/man/1/ftest
+apart	/man/1/grid-ns
+apart	/man/2/convcs
+apart	/man/2/prof
+apart	/man/8/getauthinfo
+aperture	/man/3/vga
+aphorism	/man/1/fortune
+api	/man/10/odbc
+apid	/man/1/sh
+apid	/man/1/sh-file2chan
+apm	/man/10/plan9.ini
+apm0	/man/10/plan9.ini
+apostrophes	/man/2/math-linalg
+app	/man/1/mux
+app	/man/2/translate
+app	/man/6/translate
+apparent	/man/2/dbm
+apparently	/man/1/wm-misc
+apparently	/man/2/dbm
+apparently	/man/2/keyset
+apparently	/man/2/palmfile
+apparently	/man/8/create
+apparently	/man/8/getauthinfo
+apparently	/man/8/styxchat
+appdata	/man/2/palmfile
+appearance	/man/1/brutus
+appearance	/man/1/unicode
+appearance	/man/2/draw-font
+appearance	/man/2/prefab-0intro
+appearance	/man/2/prefab-compound
+appearance	/man/2/prefab-style
+appearance	/man/2/spree-cardlib
+appearance	/man/9/menu
+appearance	/man/9/text
+appearance	/man/9/variable
+appearing	/man/1/wm-sh
+appearing	/man/2/cfg
+appearing	/man/9/scale
+append	/man/1/acme
+append	/man/1/ar
+append	/man/1/chmod
+append	/man/1/limbo
+append	/man/1/ls
+append	/man/1/tiny
+append	/man/10/2a
+append	/man/10/2c
+append	/man/10/acid
+append	/man/10/iar
+append	/man/10/styxserver
+append	/man/2/attrdb
+append	/man/2/lists
+append	/man/2/palmfile
+append	/man/2/prefab-element
+append	/man/2/string
+append	/man/2/styx
+append	/man/2/styxpersist
+append	/man/2/sys-open
+append	/man/2/sys-stat
+append	/man/3/snarf
+append	/man/3/ssl
+append	/man/3/tinyfs
+append	/man/4/logfile
+append	/man/5/0intro
+append	/man/5/open
+append	/man/5/read
+append	/man/5/stat
+append	/man/6/proto
+append	/man/8/kfscmd
+appended	/man/1/acme
+appended	/man/1/alphabet-main
+appended	/man/1/gzip
+appended	/man/1/sh
+appended	/man/1/tail
+appended	/man/1/tee
+appended	/man/1/wm-sh
+appended	/man/10/9load
+appended	/man/10/c2l
+appended	/man/2/complete
+appended	/man/2/security-0intro
+appended	/man/2/tk
+appended	/man/3/ssl
+appended	/man/4/acme
+appended	/man/7/cddb
+appended	/man/9/bind
+appended	/man/9/scrollbar
+appending	/man/1/asm
+appending	/man/1/limbo
+appending	/man/1/sh
+appending	/man/2/attrdb
+appending	/man/2/rfc822
+appending	/man/2/security-0intro
+appending	/man/8/prep
+appending	/man/9/menu
+appends	/man/1/acme
+appends	/man/1/mash
+appends	/man/1/sendmail
+appends	/man/1/sh
+appends	/man/10/print
+appends	/man/10/strcat
+appends	/man/2/string
+appends	/man/2/sys-print
+appends	/man/4/logfile
+appi	/man/2/palmfile
+appinfo	/man/2/palmfile
+appl	/man/1/9win
+appl	/man/1/acme
+appl	/man/1/alphabet-abc
+appl	/man/1/alphabet-fs
+appl	/man/1/alphabet-grid
+appl	/man/1/alphabet-main
+appl	/man/1/ar
+appl	/man/1/asm
+appl	/man/1/auplay
+appl	/man/1/avr
+appl	/man/1/basename
+appl	/man/1/bind
+appl	/man/1/blur
+appl	/man/1/brutus
+appl	/man/1/cal
+appl	/man/1/calc
+appl	/man/1/calendar
+appl	/man/1/cat
+appl	/man/1/cd
+appl	/man/1/charon
+appl	/man/1/chgrp
+appl	/man/1/chmod
+appl	/man/1/cleanname
+appl	/man/1/cmp
+appl	/man/1/collab
+appl	/man/1/collab-clients
+appl	/man/1/comm
+appl	/man/1/cook
+appl	/man/1/cp
+appl	/man/1/cprof
+appl	/man/1/cpu
+appl	/man/1/crypt
+appl	/man/1/date
+appl	/man/1/dd
+appl	/man/1/deb
+appl	/man/1/diff
+appl	/man/1/disdep
+appl	/man/1/dmview
+appl	/man/1/du
+appl	/man/1/ebook
+appl	/man/1/echo
+appl	/man/1/env
+appl	/man/1/filename
+appl	/man/1/fmt
+appl	/man/1/fortune
+appl	/man/1/freq
+appl	/man/1/fs
+appl	/man/1/ftest
+appl	/man/1/ftree
+appl	/man/1/gettar
+appl	/man/1/grep
+appl	/man/1/grid-monitor
+appl	/man/1/grid-ns
+appl	/man/1/grid-query
+appl	/man/1/grid-register
+appl	/man/1/grid-session
+appl	/man/1/gzip
+appl	/man/1/idea
+appl	/man/1/itest
+appl	/man/1/keyboard
+appl	/man/1/kill
+appl	/man/1/limbo
+appl	/man/1/listen
+appl	/man/1/logon
+appl	/man/1/logwindow
+appl	/man/1/look
+appl	/man/1/ls
+appl	/man/1/man
+appl	/man/1/mash
+appl	/man/1/mash-make
+appl	/man/1/mash-tk
+appl	/man/1/math-misc
+appl	/man/1/mc
+appl	/man/1/mdb
+appl	/man/1/miniterm
+appl	/man/1/mk
+appl	/man/1/mkdir
+appl	/man/1/mprof
+appl	/man/1/mux
+appl	/man/1/mv
+appl	/man/1/netkey
+appl	/man/1/netstat
+appl	/man/1/ns
+appl	/man/1/nsbuild
+appl	/man/1/os
+appl	/man/1/p
+appl	/man/1/passwd
+appl	/man/1/plumb
+appl	/man/1/prof
+appl	/man/1/ps
+appl	/man/1/pwd
+appl	/man/1/rcmd
+appl	/man/1/read
+appl	/man/1/rm
+appl	/man/1/runas
+appl	/man/1/secstore
+appl	/man/1/sendmail
+appl	/man/1/sh
+appl	/man/1/sh-alphabet
+appl	/man/1/sh-arg
+appl	/man/1/sh-csv
+appl	/man/1/sh-expr
+appl	/man/1/sh-file2chan
+appl	/man/1/sh-mload
+appl	/man/1/sh-regex
+appl	/man/1/sh-sexprs
+appl	/man/1/sh-std
+appl	/man/1/sh-string
+appl	/man/1/sh-tk
+appl	/man/1/sleep
+appl	/man/1/spree-join
+appl	/man/1/stack
+appl	/man/1/stream
+appl	/man/1/strings
+appl	/man/1/sum
+appl	/man/1/tcs
+appl	/man/1/tee
+appl	/man/1/telnet
+appl	/man/1/time
+appl	/man/1/timestamp
+appl	/man/1/tiny
+appl	/man/1/tkcmd
+appl	/man/1/tktester
+appl	/man/1/toolbar
+appl	/man/1/touch
+appl	/man/1/tr
+appl	/man/1/tsort
+appl	/man/1/unicode
+appl	/man/1/uniq
+appl	/man/1/units
+appl	/man/1/uuencode
+appl	/man/1/vacget
+appl	/man/1/wc
+appl	/man/1/webgrab
+appl	/man/1/wish
+appl	/man/1/wm
+appl	/man/1/wm-misc
+appl	/man/1/wm-sh
+appl	/man/1/xd
+appl	/man/1/yacc
+appl	/man/1/zeros
+appl	/man/10/c2l
+appl	/man/2/alphabet-intro
+appl	/man/2/arg
+appl	/man/2/asn1
+appl	/man/2/attrdb
+appl	/man/2/bloomfilter
+appl	/man/2/bufio
+appl	/man/2/cfg
+appl	/man/2/complete
+appl	/man/2/convcs
+appl	/man/2/crc
+appl	/man/2/csv
+appl	/man/2/daytime
+appl	/man/2/dbm
+appl	/man/2/debug
+appl	/man/2/dhcpclient
+appl	/man/2/dial
+appl	/man/2/dialog
+appl	/man/2/dict
+appl	/man/2/dis
+appl	/man/2/diskblocks
+appl	/man/2/disks
+appl	/man/2/dividers
+appl	/man/2/encoding
+appl	/man/2/env
+appl	/man/2/ether
+appl	/man/2/exception
+appl	/man/2/factotum
+appl	/man/2/filepat
+appl	/man/2/filter-deflate
+appl	/man/2/filter-slip
+appl	/man/2/format
+appl	/man/2/fsproto
+appl	/man/2/geodesy
+appl	/man/2/hash
+appl	/man/2/ida
+appl	/man/2/imagefile
+appl	/man/2/ip
+appl	/man/2/ir
+appl	/man/2/json
+appl	/man/2/keyset
+appl	/man/2/lists
+appl	/man/2/lock
+appl	/man/2/mpeg
+appl	/man/2/msgio
+appl	/man/2/names
+appl	/man/2/newns
+appl	/man/2/palmfile
+appl	/man/2/plumbmsg
+appl	/man/2/pop3
+appl	/man/2/popup
+appl	/man/2/print
+appl	/man/2/prof
+appl	/man/2/pslib
+appl	/man/2/rand
+appl	/man/2/readdir
+appl	/man/2/regex
+appl	/man/2/rfc822
+appl	/man/2/scsiio
+appl	/man/2/secstore
+appl	/man/2/security-auth
+appl	/man/2/security-login
+appl	/man/2/security-random
+appl	/man/2/security-ssl
+appl	/man/2/selectfile
+appl	/man/2/sexprs
+appl	/man/2/sh
+appl	/man/2/smtp
+appl	/man/2/spki
+appl	/man/2/spki-verifier
+appl	/man/2/spree
+appl	/man/2/spree-allow
+appl	/man/2/spree-cardlib
+appl	/man/2/string
+appl	/man/2/stringinttab
+appl	/man/2/styx
+appl	/man/2/styxconv
+appl	/man/2/styxflush
+appl	/man/2/styxpersist
+appl	/man/2/styxservers
+appl	/man/2/styxservers-nametree
+appl	/man/2/tabs
+appl	/man/2/tftp
+appl	/man/2/tkclient
+appl	/man/2/translate
+appl	/man/2/ubfa
+appl	/man/2/venti
+appl	/man/2/virgil
+appl	/man/2/volume
+appl	/man/2/w3c-css
+appl	/man/2/w3c-uris
+appl	/man/2/w3c-xpointers
+appl	/man/2/wmclient
+appl	/man/2/wmlib
+appl	/man/2/wmsrv
+appl	/man/2/workdir
+appl	/man/2/xml
+appl	/man/4/9srvfs
+appl	/man/4/acme
+appl	/man/4/archfs
+appl	/man/4/dbfs
+appl	/man/4/dossrv
+appl	/man/4/export
+appl	/man/4/factotum
+appl	/man/4/ftpfs
+appl	/man/4/grid-cpu
+appl	/man/4/import
+appl	/man/4/iostats
+appl	/man/4/keyfs
+appl	/man/4/keysrv
+appl	/man/4/kfs
+appl	/man/4/lockfs
+appl	/man/4/logfile
+appl	/man/4/memfs
+appl	/man/4/mntgen
+appl	/man/4/namespace
+appl	/man/4/palmsrv
+appl	/man/4/ramfile
+appl	/man/4/registry
+appl	/man/4/spree
+appl	/man/4/tarfs
+appl	/man/4/trfs
+appl	/man/4/vacfs
+appl	/man/7/cddb
+appl	/man/7/db
+appl	/man/7/dbsrv
+appl	/man/8/ai2key
+appl	/man/8/applylog
+appl	/man/8/bootpd
+appl	/man/8/changelogin
+appl	/man/8/collabsrv
+appl	/man/8/create
+appl	/man/8/createsignerkey
+appl	/man/8/cs
+appl	/man/8/dhcp
+appl	/man/8/dns
+appl	/man/8/fpgaload
+appl	/man/8/ftl
+appl	/man/8/getauthinfo
+appl	/man/8/httpd
+appl	/man/8/init
+appl	/man/8/kfscmd
+appl	/man/8/logind
+appl	/man/8/mangaload
+appl	/man/8/manufacture
+appl	/man/8/mkfs
+appl	/man/8/ping
+appl	/man/8/plumber
+appl	/man/8/prep
+appl	/man/8/rdbgsrv
+appl	/man/8/register
+appl	/man/8/rip
+appl	/man/8/rstyxd
+appl	/man/8/signer
+appl	/man/8/sntp
+appl	/man/8/styxchat
+appl	/man/8/styxmon
+appl	/man/8/svc
+appl	/man/8/touchcal
+appl	/man/8/virgild
+applets	/man/1/charon
+applicable	/man/1/ls
+applicable	/man/2/dis
+applicable	/man/2/styxservers
+applicable	/man/3/audio
+applicable	/man/6/dis
+applicable	/man/9/types
+application	/man/1/0intro
+application	/man/1/avr
+application	/man/1/brutus
+application	/man/1/disdep
+application	/man/1/emu
+application	/man/1/logon
+application	/man/1/mux
+application	/man/1/tktester
+application	/man/1/toolbar
+application	/man/1/wm
+application	/man/10/error
+application	/man/2/0intro
+application	/man/2/asn1
+application	/man/2/attrdb
+application	/man/2/bufio
+application	/man/2/cfg
+application	/man/2/command
+application	/man/2/dbm
+application	/man/2/dividers
+application	/man/2/draw-0intro
+application	/man/2/draw-context
+application	/man/2/draw-display
+application	/man/2/drawmux
+application	/man/2/factotum
+application	/man/2/ir
+application	/man/2/json
+application	/man/2/palmfile
+application	/man/2/plumbmsg
+application	/man/2/popup
+application	/man/2/prefab-element
+application	/man/2/prefab-environ
+application	/man/2/prefab-style
+application	/man/2/rfc822
+application	/man/2/security-0intro
+application	/man/2/selectfile
+application	/man/2/styxservers
+application	/man/2/sys-0intro
+application	/man/2/sys-pctl
+application	/man/2/sys-read
+application	/man/2/tk
+application	/man/2/tkclient
+application	/man/2/translate
+application	/man/2/ubfa
+application	/man/2/w3c-uris
+application	/man/2/wmclient
+application	/man/2/wmlib
+application	/man/3/draw
+application	/man/3/ip
+application	/man/3/mpeg
+application	/man/3/srv9
+application	/man/3/tls
+application	/man/3/usb
+application	/man/3/vid
+application	/man/4/keyfs
+application	/man/6/json
+application	/man/6/keytext
+application	/man/6/plumbing
+application	/man/8/httpd
+application	/man/8/plumber
+application	/man/9/0intro
+application	/man/9/grab
+application	/man/9/panel
+application's	/man/2/plumbmsg
+application's	/man/2/styxservers
+application's	/man/3/srv9
+application's	/man/8/plumber
+applications	/man/1/acme
+applications	/man/1/cpu
+applications	/man/1/emu
+applications	/man/1/mash-tk
+applications	/man/1/math-misc
+applications	/man/1/mux
+applications	/man/1/tiny
+applications	/man/1/wish
+applications	/man/1/wm
+applications	/man/1/wm-misc
+applications	/man/10/dev
+applications	/man/10/kproc
+applications	/man/10/splhi
+applications	/man/2/bufio
+applications	/man/2/command
+applications	/man/2/dialog
+applications	/man/2/draw-pointer
+applications	/man/2/ether
+applications	/man/2/imagefile
+applications	/man/2/ir
+applications	/man/2/keyring-certtostr
+applications	/man/2/palmfile
+applications	/man/2/plumbmsg
+applications	/man/2/prefab-0intro
+applications	/man/2/security-0intro
+applications	/man/2/srv
+applications	/man/2/styx
+applications	/man/2/styxservers
+applications	/man/2/sys-chdir
+applications	/man/2/sys-open
+applications	/man/2/sys-pipe
+applications	/man/2/sys-self
+applications	/man/2/tkclient
+applications	/man/2/ubfa
+applications	/man/2/venti
+applications	/man/2/w3c-css
+applications	/man/2/wmclient
+applications	/man/2/wmlib
+applications	/man/3/cmd
+applications	/man/3/ip
+applications	/man/3/srv
+applications	/man/3/srv9
+applications	/man/3/touch
+applications	/man/4/0intro
+applications	/man/4/9srvfs
+applications	/man/4/dbfs
+applications	/man/4/namespace
+applications	/man/4/palmsrv
+applications	/man/4/ramfile
+applications	/man/4/trfs
+applications	/man/5/0intro
+applications	/man/6/attrdb
+applications	/man/6/colour
+applications	/man/6/json
+applications	/man/6/keyboard
+applications	/man/6/ndb
+applications	/man/6/plumbing
+applications	/man/6/regexp
+applications	/man/6/sbl
+applications	/man/6/sexprs
+applications	/man/6/translate
+applications	/man/6/ubfa
+applications	/man/8/applylog
+applications	/man/8/cs
+applications	/man/8/getauthinfo
+applications	/man/8/plumber
+applications	/man/9/grab
+applied	/man/1/acme
+applied	/man/1/alphabet-main
+applied	/man/1/fs
+applied	/man/1/sh
+applied	/man/1/sh-alphabet
+applied	/man/1/tiny
+applied	/man/10/dev
+applied	/man/10/dynld
+applied	/man/2/asn1
+applied	/man/2/keyring-0intro
+applied	/man/2/math-0intro
+applied	/man/2/math-elem
+applied	/man/2/security-0intro
+applied	/man/2/sexprs
+applied	/man/2/tk
+applied	/man/2/tkclient
+applied	/man/2/ubfa
+applied	/man/3/logfs
+applied	/man/3/ssl
+applied	/man/4/acme
+applied	/man/5/walk
+applied	/man/6/man
+applied	/man/6/plumbing
+applied	/man/7/dbsrv
+applied	/man/8/applylog
+applied	/man/8/rdbgsrv
+applied	/man/8/rstyxd
+applied	/man/9/0intro
+applied	/man/9/text
+applied	/man/9/types
+applies	/man/1/cp
+applies	/man/1/sh-regex
+applies	/man/10/kproc
+applies	/man/2/daytime
+applies	/man/2/draw-image
+applies	/man/2/lists
+applies	/man/2/security-0intro
+applies	/man/2/spree
+applies	/man/2/wait
+applies	/man/3/fs
+applies	/man/3/ip
+applies	/man/3/prog
+applies	/man/3/srv9
+applies	/man/3/touch
+applies	/man/5/walk
+applies	/man/8/applylog
+applies	/man/8/cs
+applies	/man/8/kfscmd
+applies	/man/8/plumber
+applies	/man/9/canvas
+applies	/man/9/checkbutton
+applies	/man/9/menu
+applies	/man/9/radiobutton
+applies	/man/9/text
+apply	/man/1/acme
+apply	/man/1/calc
+apply	/man/1/mash-make
+apply	/man/1/plumb
+apply	/man/1/read
+apply	/man/1/sh-std
+apply	/man/1/wm-misc
+apply	/man/10/dev
+apply	/man/10/inb
+apply	/man/2/dbm
+apply	/man/2/draw-display
+apply	/man/2/imagefile
+apply	/man/2/math-linalg
+apply	/man/2/print
+apply	/man/2/spki
+apply	/man/2/styxconv
+apply	/man/2/sys-print
+apply	/man/2/w3c-xpointers
+apply	/man/3/audio
+apply	/man/8/applylog
+apply	/man/9/1copyright
+apply	/man/9/canvas
+applycfg	/man/2/dhcpclient
+applying	/man/1/acme
+applying	/man/1/alphabet-main
+applying	/man/1/diff
+applying	/man/1/fs
+applying	/man/1/mash-make
+applying	/man/2/plumbmsg
+applying	/man/2/readdir
+applying	/man/2/secstore
+applying	/man/2/spki
+applylog	/man/8/applylog
+applylog.b	/man/8/applylog
+apportioned	/man/9/grid
+apportioning	/man/9/grid
+appreciate	/man/10/devattach
+approach	/man/2/sh
+appropriately	/man/1/ftree
+appropriately	/man/1/sh-tk
+appropriately	/man/10/2c
+appropriately	/man/10/9load
+appropriately	/man/10/kbdputc
+appropriately	/man/10/plan9.ini
+appropriately	/man/2/bufio-chanfill
+appropriately	/man/2/dhcpclient
+appropriately	/man/2/dividers
+appropriately	/man/2/draw-context
+appropriately	/man/2/prefab-compound
+appropriately	/man/2/spree
+appropriately	/man/2/spree-cardlib
+appropriately	/man/2/styxservers
+appropriately	/man/3/vga
+appropriately	/man/4/registry
+appropriately	/man/8/rip
+appropriately	/man/9/types
+approximate	/man/1/sh-file2chan
+approximate	/man/2/geodesy
+approximate	/man/9/canvas
+approximately	/man/2/bloomfilter
+approximation	/man/1/man
+approximation	/man/3/fs
+apreceding	/man/2/w3c-xpointers
+april	/man/2/ida
+aprint	/man/2/sys-print
+aprvx	/man/8/mkfs
+apx	/man/10/2c
+aqid	/man/10/styx
+aqid	/man/2/styx
+aqid	/man/5/0intro
+aqid	/man/5/attach
+aqid	/man/8/styxchat
+aqua	/man/9/types
+aquery	/man/2/factotum
+ar	/man/1/alphabet-fs
+ar	/man/1/ar
+ar	/man/1/fs
+ar	/man/10/ar
+ar	/man/10/iar
+ar	/man/10/mk
+ar.b	/man/1/ar
+ar.h	/man/10/ar
+arbitrarily	/man/1/acme
+arbitrarily	/man/2/dict
+arbitrarily	/man/2/draw-display
+arbitrarily	/man/2/sys-0intro
+arbitrarily	/man/8/prep
+arbitrary	/man/1/mk
+arbitrary	/man/1/sh-expr
+arbitrary	/man/10/dynld
+arbitrary	/man/10/master
+arbitrary	/man/10/mk
+arbitrary	/man/2/alphabet-intro
+arbitrary	/man/2/bufio
+arbitrary	/man/2/dbm
+arbitrary	/man/2/draw-screen
+arbitrary	/man/2/filter
+arbitrary	/man/2/format
+arbitrary	/man/2/keyring-getmsg
+arbitrary	/man/2/keyring-getstring
+arbitrary	/man/2/keyring-ipint
+arbitrary	/man/2/msgio
+arbitrary	/man/2/prefab-element
+arbitrary	/man/2/sets
+arbitrary	/man/2/spki
+arbitrary	/man/2/spree-allow
+arbitrary	/man/2/spree-cardlib
+arbitrary	/man/2/styxservers
+arbitrary	/man/2/sys-0intro
+arbitrary	/man/2/sys-millisec
+arbitrary	/man/2/timers
+arbitrary	/man/2/tk
+arbitrary	/man/2/wmsrv
+arbitrary	/man/3/ftl
+arbitrary	/man/3/indir
+arbitrary	/man/3/root
+arbitrary	/man/3/sd
+arbitrary	/man/4/dbfs
+arbitrary	/man/4/ftpfs
+arbitrary	/man/4/kfs
+arbitrary	/man/4/spree
+arbitrary	/man/5/0intro
+arbitrary	/man/6/json
+arbitrary	/man/6/sexprs
+arbitrary	/man/8/changelogin
+arbitrary	/man/9/listbox
+arbitrary	/man/9/panel
+arbitrary	/man/9/text
+arbitration	/man/3/pnp
+arc	/man/1/mk
+arc	/man/10/mk
+arc	/man/2/draw-image
+arc	/man/3/draw
+arc	/man/9/canvas
+arc's	/man/9/canvas
+arcane	/man/2/disks
+arch	/man/10/ar
+arch	/man/3/arch
+arch	/man/3/vga
+arch	/man/4/archfs
+arch	/man/4/namespace
+arch	/man/8/mkfs
+arch.a	/man/1/ar
+arch.b	/man/8/create
+archfile	/man/4/archfs
+archfs	/man/4/archfs
+archfs	/man/4/tarfs
+archfs	/man/8/create
+archfs.b	/man/4/archfs
+architecture	/man/10/2a
+architecture	/man/10/2c
+architecture	/man/10/2l
+architecture	/man/10/5cv
+architecture	/man/10/acid
+architecture	/man/10/ar
+architecture	/man/10/conf
+architecture	/man/10/dynld
+architecture	/man/10/iar
+architecture	/man/10/kbdputc
+architecture	/man/10/mk
+architecture	/man/3/arch
+architecture	/man/4/namespace
+architecture	/man/8/prep
+architectures	/man/1/0intro
+architectures	/man/10/2a
+architectures	/man/10/2l
+architectures	/man/10/a.out
+architectures	/man/10/inb
+archival	/man/1/alphabet-fs
+archival	/man/1/fs
+archival	/man/2/spree-cardlib
+archival	/man/2/spree-gather
+archival	/man/2/spree-objstore
+archival	/man/2/venti
+archive	/man/1/alphabet-fs
+archive	/man/1/ar
+archive	/man/1/fs
+archive	/man/1/gettar
+archive	/man/1/vacget
+archive	/man/10/0intro
+archive	/man/10/ar
+archive	/man/10/iar
+archive	/man/10/inm
+archive	/man/2/palmfile
+archive	/man/2/spree
+archive	/man/2/spree-allow
+archive	/man/2/spree-cardlib
+archive	/man/2/spree-gather
+archive	/man/2/spree-objstore
+archive	/man/4/archfs
+archive	/man/4/tarfs
+archive	/man/4/vacfs
+archive	/man/6/proto
+archive	/man/8/create
+archive	/man/8/mkfs
+archivearray	/man/2/spree-cardlib
+archived	/man/1/chmod
+archived	/man/2/spree
+archived	/man/2/spree-cardlib
+archived	/man/2/spree-gather
+archived	/man/4/tarfs
+archived	/man/8/mkfs
+archived	/man/8/prep
+archiveobj	/man/2/spree-allow
+archives	/man/1/ar
+archives	/man/1/gettar
+archives	/man/10/2l
+archives	/man/10/ar
+archives	/man/10/mk
+archives	/man/2/spree
+archives	/man/2/spree-objstore
+archives	/man/4/vacfs
+archives	/man/5/0intro
+archives	/man/5/stat
+archives	/man/8/create
+archiving	/man/2/spree-cardlib
+archiving	/man/2/spree-objstore
+arcop	/man/2/draw-image
+arcs	/man/1/tsort
+arcs	/man/9/canvas
+arctan	/man/2/math-elem
+aren't	/man/1/alphabet-fs
+aren't	/man/1/fs
+aren't	/man/2/sets
+arena	/man/1/emu
+arenas	/man/8/prep
+arestore	/man/2/dis
+arfmag	/man/10/ar
+arg	/man/1/9win
+arg	/man/1/alphabet-abc
+arg	/man/1/alphabet-grid
+arg	/man/1/alphabet-main
+arg	/man/1/cprof
+arg	/man/1/cpu
+arg	/man/1/echo
+arg	/man/1/emu
+arg	/man/1/fs
+arg	/man/1/ftest
+arg	/man/1/listen
+arg	/man/1/mash
+arg	/man/1/mprof
+arg	/man/1/os
+arg	/man/1/prof
+arg	/man/1/rcmd
+arg	/man/1/runas
+arg	/man/1/sh
+arg	/man/1/sh-alphabet
+arg	/man/1/sh-arg
+arg	/man/1/sh-expr
+arg	/man/1/sh-regex
+arg	/man/1/sh-std
+arg	/man/1/stack
+arg	/man/1/time
+arg	/man/1/wm
+arg	/man/10/kproc
+arg	/man/10/ntsrv
+arg	/man/10/print
+arg	/man/10/qio
+arg	/man/10/sleep
+arg	/man/2/arg
+arg	/man/2/convcs
+arg	/man/2/factotum
+arg	/man/2/prefab-element
+arg	/man/2/tk
+arg	/man/2/w3c-css
+arg	/man/2/w3c-xpointers
+arg	/man/3/ip
+arg	/man/6/plumbing
+arg	/man/8/init
+arg	/man/8/styxmon
+arg	/man/9/button
+arg	/man/9/canvas
+arg	/man/9/checkbutton
+arg	/man/9/choicebutton
+arg	/man/9/entry
+arg	/man/9/frame
+arg	/man/9/grab
+arg	/man/9/grid
+arg	/man/9/image
+arg	/man/9/label
+arg	/man/9/listbox
+arg	/man/9/menu
+arg	/man/9/menubutton
+arg	/man/9/pack
+arg	/man/9/panel
+arg	/man/9/radiobutton
+arg	/man/9/scale
+arg	/man/9/scrollbar
+arg	/man/9/text
+arg.b	/man/1/sh-arg
+arg.b	/man/2/arg
+arg.dis	/man/6/proto
+arg.m	/man/2/arg
+arg0	/man/3/cmd
+arg1	/man/1/m4
+arg1	/man/1/mk
+arg1	/man/10/mk
+arg2	/man/1/m4
+arg2	/man/1/mk
+arg2	/man/10/mk
+argc	/man/10/styxserver
+argn	/man/1/m4
+argpos	/man/10/2c
+args	/man/1/deb
+args	/man/1/grid-session
+args	/man/1/mash
+args	/man/1/mk
+args	/man/1/sh-arg
+args	/man/1/sh-tk
+args	/man/1/wm-sh
+args	/man/1/yacc
+args	/man/10/acid
+args	/man/10/getfields
+args	/man/10/mk
+args	/man/2/alphabet-intro
+args	/man/2/arg
+args	/man/2/command
+args	/man/2/debug
+args	/man/2/sexprs
+args	/man/2/sh
+args	/man/2/spki
+args	/man/2/string
+args	/man/2/styxservers-nametree
+args	/man/2/translate
+args	/man/2/ubfa
+args	/man/2/virgil
+args	/man/2/w3c-css
+args	/man/2/w3c-xpointers
+args	/man/3/cmd
+args	/man/4/iostats
+args	/man/6/sbl
+args	/man/9/canvas
+args	/man/9/entry
+args	/man/9/listbox
+args	/man/9/text
+argtype	/man/1/sh-alphabet
+arguably	/man/1/wish
+arguably	/man/3/indir
+argv	/man/1/stack
+argv	/man/10/acid
+argv	/man/10/styxserver
+argv	/man/2/arg
+argv	/man/2/debug
+argv	/man/2/draw-example
+argv	/man/2/ir
+argv	/man/2/itslib
+argv	/man/2/security-auth
+argv	/man/2/sh
+argv	/man/2/spree
+argv	/man/2/spree-gather
+argv	/man/2/styxservers-nametree
+argv0	/man/10/acid
+arising	/man/1/yacc
+arising	/man/9/1copyright
+arithmetic	/man/1/calc
+arithmetic	/man/1/m4
+arithmetic	/man/1/sh-expr
+arithmetic	/man/10/2c
+arithmetic	/man/2/keyring-0intro
+arithmetic	/man/2/math-0intro
+arithmetic	/man/2/math-fp
+arithmetic	/man/8/prep
+arity	/man/2/ubfa
+ark200pv	/man/3/vga
+ark200pvhwgc	/man/3/vga
+arm	/man/10/2c
+arm	/man/10/5cv
+arm	/man/10/a.out
+arm	/man/10/acid
+arm	/man/2/alphabet-intro
+arm	/man/2/dis
+armag	/man/10/ar
+armpit	/man/10/plan9.ini
+armstrong	/man/2/ubfa
+armstrong	/man/6/ubfa
+armstrong's	/man/2/ubfa
+armstrong's	/man/6/ubfa
+arp	/man/3/ip
+arrange	/man/1/acme
+arrange	/man/2/dividers
+arrange	/man/2/styxpersist
+arrange	/man/2/wmsrv
+arrange	/man/5/0intro
+arrange	/man/9/bind
+arrange	/man/9/canvas
+arrange	/man/9/entry
+arrange	/man/9/menu
+arranged	/man/1/acme
+arranged	/man/10/kproc
+arranged	/man/2/dividers
+arranged	/man/9/grid
+arranged	/man/9/menu
+arrangement	/man/2/draw-image
+arrangement	/man/6/utf
+arranges	/man/1/alphabet-abc
+arranges	/man/1/alphabet-grid
+arranges	/man/1/tiny
+arranges	/man/2/spree
+arranges	/man/9/grid
+arranges	/man/9/pack
+array	/man/1/collab-clients
+array	/man/1/kill
+array	/man/1/miniterm
+array	/man/1/sh-string
+array	/man/1/tktester
+array	/man/10/2c
+array	/man/10/a.out
+array	/man/10/acid
+array	/man/10/c2l
+array	/man/10/dev
+array	/man/10/devattach
+array	/man/10/dynld
+array	/man/10/getfields
+array	/man/10/newchan
+array	/man/10/parsecmd
+array	/man/10/print
+array	/man/10/strcat
+array	/man/2/0intro
+array	/man/2/asn1
+array	/man/2/bloomfilter
+array	/man/2/bufio
+array	/man/2/bufio-chanfill
+array	/man/2/complete
+array	/man/2/convcs
+array	/man/2/crc
+array	/man/2/dbm
+array	/man/2/debug
+array	/man/2/devpointer
+array	/man/2/dhcpclient
+array	/man/2/dial
+array	/man/2/dis
+array	/man/2/diskblocks
+array	/man/2/disks
+array	/man/2/draw-image
+array	/man/2/draw-screen
+array	/man/2/encoding
+array	/man/2/ether
+array	/man/2/factotum
+array	/man/2/filter
+array	/man/2/format
+array	/man/2/ida
+array	/man/2/imagefile
+array	/man/2/ip
+array	/man/2/json
+array	/man/2/keyring-0intro
+array	/man/2/keyring-auth
+array	/man/2/keyring-crypt
+array	/man/2/keyring-getmsg
+array	/man/2/keyring-getstring
+array	/man/2/keyring-ipint
+array	/man/2/keyring-rc4
+array	/man/2/keyring-sha1
+array	/man/2/lists
+array	/man/2/math-export
+array	/man/2/math-linalg
+array	/man/2/msgio
+array	/man/2/palmfile
+array	/man/2/plumbmsg
+array	/man/2/popup
+array	/man/2/prof
+array	/man/2/readdir
+array	/man/2/regex
+array	/man/2/rfc822
+array	/man/2/scsiio
+array	/man/2/secstore
+array	/man/2/security-random
+array	/man/2/security-ssl
+array	/man/2/sets
+array	/man/2/sexprs
+array	/man/2/spki
+array	/man/2/spree
+array	/man/2/spree-cardlib
+array	/man/2/spree-gather
+array	/man/2/stringinttab
+array	/man/2/styx
+array	/man/2/styxconv
+array	/man/2/styxservers
+array	/man/2/sys-byte2char
+array	/man/2/sys-dirread
+array	/man/2/sys-file2chan
+array	/man/2/sys-pipe
+array	/man/2/sys-print
+array	/man/2/sys-read
+array	/man/2/sys-utfbytes
+array	/man/2/tabs
+array	/man/2/translate
+array	/man/2/ubfa
+array	/man/2/venti
+array	/man/2/wmsrv
+array	/man/3/arch
+array	/man/3/cap
+array	/man/3/dbg
+array	/man/3/kprof
+array	/man/3/prog
+array	/man/6/dis
+array	/man/6/image
+array	/man/6/json
+array	/man/6/keytext
+array	/man/6/plumbing
+array	/man/6/sbl
+array	/man/7/db
+array	/man/8/styxchat
+array's	/man/2/dis
+arrays	/man/1/charon
+arrays	/man/1/deb
+arrays	/man/1/limbo
+arrays	/man/10/c2l
+arrays	/man/10/memory
+arrays	/man/2/crc
+arrays	/man/2/dbm
+arrays	/man/2/dis
+arrays	/man/2/draw-image
+arrays	/man/2/encoding
+arrays	/man/2/hash
+arrays	/man/2/keyring-0intro
+arrays	/man/2/keyring-getmsg
+arrays	/man/2/keyring-getstring
+arrays	/man/2/math-export
+arrays	/man/2/math-linalg
+arrays	/man/2/msgio
+arrays	/man/2/palmfile
+arrays	/man/2/secstore
+arrays	/man/2/styx
+arrays	/man/2/sys-0intro
+arrays	/man/6/image
+arrays	/man/6/json
+arrival	/man/1/collab-clients
+arrival	/man/3/usb
+arrive	/man/1/sh-tk
+arrive	/man/2/timers
+arrive	/man/2/wmsrv
+arrive	/man/3/usb
+arrive	/man/8/collabsrv
+arrived	/man/10/rune
+arrived	/man/2/pop3
+arrived	/man/8/collabsrv
+arrives	/man/1/sh-file2chan
+arrives	/man/10/qio
+arrives	/man/2/styxflush
+arrives	/man/3/ip
+arrives	/man/3/pbus
+arrives	/man/3/plap
+arrives	/man/5/0intro
+arrives	/man/8/collabsrv
+arrives	/man/8/plumber
+arriving	/man/1/sh-tk
+arriving	/man/2/wmsrv
+arriving	/man/3/ip
+arriving	/man/3/pbus
+arriving	/man/5/flush
+arrow	/man/1/cprof
+arrow	/man/2/draw-image
+arrow	/man/2/prefab-compound
+arrow	/man/3/draw
+arrow	/man/9/canvas
+arrow	/man/9/scale
+arrow	/man/9/scrollbar
+arrow1	/man/9/scrollbar
+arrow2	/man/9/scrollbar
+arrowhead	/man/2/draw-image
+arrowhead	/man/9/canvas
+arrowheads	/man/2/draw-image
+arrowheads	/man/9/canvas
+arrows	/man/1/ebook
+arrows	/man/9/canvas
+arrows	/man/9/scrollbar
+arrowshape	/man/9/canvas
+art	/man/1/ar
+artifacts	/man/6/colour
+artist	/man/1/collab-clients
+artists	/man/1/collab-clients
+asc	/man/2/scsiio
+ascending	/man/10/dynld
+ascending	/man/6/regexp
+ascent	/man/2/draw-font
+ascent	/man/3/draw
+ascent	/man/6/font
+ascertain	/man/1/diff
+ascii	/man/1/charon
+ascii	/man/1/dd
+ascii	/man/1/sh-string
+ascii	/man/1/strings
+ascii	/man/1/tr
+ascii	/man/1/uuencode
+ascii	/man/1/xd
+ascii	/man/10/print
+ascii	/man/2/convcs
+ascii	/man/2/draw-0intro
+ascii	/man/2/draw-font
+ascii	/man/2/sexprs
+ascii	/man/2/sys-0intro
+ascii	/man/6/keyboard
+ascii	/man/6/sexprs
+ascii	/man/6/utf
+ascii	/man/9/bind
+ascom	/man/2/keyring-crypt
+ascq	/man/2/scsiio
+asdata	/man/2/sexprs
+aself	/man/2/w3c-xpointers
+aside	/man/3/logfs
+asin	/man/1/calc
+asin	/man/2/math-elem
+asinh	/man/1/calc
+asinh	/man/2/math-elem
+ask	/man/1/blur
+ask	/man/6/ndb
+asking	/man/2/registries
+asking	/man/2/sys-open
+asks	/man/1/tktester
+asks	/man/2/dhcpclient
+asks	/man/2/sys-file2chan
+asks	/man/5/open
+asks	/man/5/read
+asks	/man/5/remove
+asm	/man/1/asm
+asm	/man/1/limbo
+asm	/man/10/acid
+asm	/man/2/dis
+asm	/man/4/namespace
+asm	/man/6/dis
+asn.1	/man/2/asn1
+asn1	/man/2/asn1
+asn1.b	/man/2/asn1
+asn1.m	/man/2/asn1
+aspects	/man/1/bind
+aspects	/man/9/button
+aspects	/man/9/canvas
+aspects	/man/9/checkbutton
+aspects	/man/9/choicebutton
+aspects	/man/9/entry
+aspects	/man/9/frame
+aspects	/man/9/label
+aspects	/man/9/listbox
+aspects	/man/9/menu
+aspects	/man/9/menubutton
+aspects	/man/9/panel
+aspects	/man/9/radiobutton
+aspects	/man/9/scale
+aspects	/man/9/scrollbar
+aspects	/man/9/text
+assemble	/man/10/2a
+assembled	/man/2/spree-gather
+assembler	/man/1/asm
+assembler	/man/10/2a
+assembler	/man/10/2c
+assembler's	/man/10/2a
+assemblers	/man/10/2a
+assembly	/man/1/asm
+assembly	/man/1/limbo
+assembly	/man/10/2c
+assembly	/man/10/2l
+assembly	/man/10/conf
+assembly	/man/2/dis
+assembly	/man/4/palmsrv
+assertion	/man/2/0intro
+assign	/man/1/calc
+assign	/man/1/chmod
+assign	/man/2/dhcpclient
+assign	/man/2/palmfile
+assign	/man/3/ip
+assign	/man/3/plap
+assign	/man/3/pnp
+assign	/man/9/canvas
+assigned	/man/1/charon
+assigned	/man/1/mash
+assigned	/man/1/sh
+assigned	/man/1/sh-file2chan
+assigned	/man/1/sh-tk
+assigned	/man/1/yacc
+assigned	/man/10/styxserver
+assigned	/man/2/dhcpclient
+assigned	/man/2/dial
+assigned	/man/2/palmfile
+assigned	/man/2/rfc822
+assigned	/man/2/sys-0intro
+assigned	/man/2/sys-dial
+assigned	/man/2/sys-self
+assigned	/man/3/arch
+assigned	/man/3/dbg
+assigned	/man/3/ether
+assigned	/man/3/ip
+assigned	/man/3/pnp
+assigned	/man/9/canvas
+assigning	/man/2/bufio
+assigning	/man/2/draw-0intro
+assigning	/man/2/palmfile
+assigning	/man/2/sys-stat
+assignment	/man/1/calc
+assignment	/man/1/env
+assignment	/man/1/mash
+assignment	/man/1/mk
+assignment	/man/1/sh
+assignment	/man/1/sh-std
+assignment	/man/10/master
+assignment	/man/10/mk
+assignment	/man/2/dhcpclient
+assignments	/man/1/calc
+assignments	/man/1/mk
+assignments	/man/1/sh
+assignments	/man/10/acid
+assignments	/man/10/mk
+assigns	/man/1/sh
+assigns	/man/10/devattach
+assigns	/man/2/asn1
+assigns	/man/2/draw-screen
+assigns	/man/2/palmfile
+assigns	/man/3/dbg
+assigns	/man/8/prep
+assistance	/man/2/styxservers
+assists	/man/2/styxservers
+associate	/man/1/mash
+associate	/man/1/yacc
+associate	/man/2/prefab-environ
+associate	/man/2/styxservers
+associate	/man/2/sys-stat
+associate	/man/3/draw
+associate	/man/3/ssl
+associate	/man/5/walk
+associate	/man/6/ubfa
+associate	/man/9/button
+associate	/man/9/canvas
+associate	/man/9/checkbutton
+associate	/man/9/choicebutton
+associate	/man/9/radiobutton
+associate	/man/9/text
+associates	/man/1/mash
+associates	/man/10/2c
+associates	/man/10/a.out
+associates	/man/2/bufio
+associates	/man/2/spki
+associates	/man/2/spree-cardlib
+associates	/man/2/ubfa
+associates	/man/6/attrdb
+associates	/man/6/ndb
+associates	/man/9/bind
+associates	/man/9/canvas
+associates	/man/9/grid
+associates	/man/9/text
+associating	/man/3/ip
+associating	/man/9/text
+association	/man/1/deb
+association	/man/1/stack
+association	/man/2/asn1
+association	/man/2/dict
+association	/man/3/draw
+association	/man/3/i82365
+association	/man/3/ip
+association	/man/9/cursor
+association	/man/9/text
+associations	/man/1/stack
+associative	/man/1/calc
+associative	/man/2/json
+assorted	/man/10/devattach
+asssumed	/man/1/os
+assume	/man/1/alphabet-fs
+assume	/man/1/mk
+assume	/man/10/acid
+assume	/man/10/mk
+assume	/man/10/qlock
+assume	/man/2/0intro
+assume	/man/2/math-0intro
+assume	/man/2/math-fp
+assume	/man/2/math-linalg
+assume	/man/2/pop3
+assume	/man/2/security-0intro
+assume	/man/4/dbfs
+assume	/man/4/namespace
+assume	/man/6/colour
+assumed	/man/1/acme
+assumed	/man/1/alphabet-abc
+assumed	/man/1/alphabet-grid
+assumed	/man/1/bind
+assumed	/man/1/brutus
+assumed	/man/1/collab-clients
+assumed	/man/1/cprof
+assumed	/man/1/du
+assumed	/man/1/look
+assumed	/man/1/man
+assumed	/man/1/mash
+assumed	/man/1/mdb
+assumed	/man/1/mprof
+assumed	/man/1/prof
+assumed	/man/1/rcmd
+assumed	/man/1/sendmail
+assumed	/man/1/sh-file2chan
+assumed	/man/1/sort
+assumed	/man/1/wc
+assumed	/man/10/a.out
+assumed	/man/10/devattach
+assumed	/man/10/qio
+assumed	/man/2/0intro
+assumed	/man/2/daytime
+assumed	/man/2/disks
+assumed	/man/2/filter-deflate
+assumed	/man/2/palmfile
+assumed	/man/2/sexprs
+assumed	/man/2/spree
+assumed	/man/2/spree-cardlib
+assumed	/man/2/styx
+assumed	/man/2/styxservers
+assumed	/man/2/styxservers-nametree
+assumed	/man/2/w3c-uris
+assumed	/man/3/boot
+assumed	/man/3/ds
+assumed	/man/3/gpio
+assumed	/man/3/ip
+assumed	/man/3/ssl
+assumed	/man/4/kfs
+assumed	/man/4/lockfs
+assumed	/man/6/attrdb
+assumed	/man/6/keytext
+assumed	/man/6/namespace
+assumed	/man/6/sbl
+assumed	/man/8/prep
+assumed	/man/9/canvas
+assumes	/man/1/cook
+assumes	/man/1/listen
+assumes	/man/10/qlock
+assumes	/man/10/styx
+assumes	/man/2/ida
+assumes	/man/2/security-login
+assumes	/man/2/styxflush
+assumes	/man/2/venti
+assumes	/man/3/vid
+assumes	/man/4/export
+assumes	/man/5/walk
+assumes	/man/8/mangaload
+assuming	/man/2/styxservers
+assuming	/man/3/flash
+assuming	/man/8/mkfs
+assuming	/man/9/types
+assumption	/man/1/disdep
+assumption	/man/1/mdb
+assumption	/man/10/2c
+assumptions	/man/1/ar
+assumptions	/man/10/iar
+asterisk	/man/1/sh-arg
+asterisk	/man/1/xd
+asterisk	/man/10/plan9.ini
+asterisk	/man/2/alphabet-intro
+asterisk	/man/2/filepat
+asterisk	/man/2/spree-allow
+asterisk	/man/4/acme
+asterisk	/man/4/spree
+asterisk	/man/6/keyboard
+astext	/man/2/sexprs
+astronomical	/man/1/units
+async	/man/10/plan9.ini
+asynch	/man/3/ip
+asynchronous	/man/1/stream
+asynchronous	/man/2/mpeg
+asynchronous	/man/3/ip
+asynchronously	/man/1/mash
+asynchronously	/man/1/sh
+asynchronously	/man/1/sh-std
+asynchronously	/man/2/draw-0intro
+asynchronously	/man/2/sh
+ata	/man/2/disks
+ata	/man/3/sd
+atactlrwait	/man/10/seconds
+atan	/man/1/calc
+atan	/man/2/math-elem
+atan2	/man/1/calc
+atan2	/man/2/math-elem
+atanh	/man/1/calc
+atanh	/man/2/math-elem
+atapi	/man/2/scsiio
+atime	/man/10/devattach
+atime	/man/2/readdir
+atime	/man/2/styxservers
+atime	/man/2/sys-stat
+atime	/man/5/stat
+atime	/man/8/styxchat
+atm	/man/1/units
+atmark	/man/2/xml
+atmega128	/man/1/avr
+atmel	/man/1/avr
+atodir	/man/10/newchan
+atoi	/man/10/atoi
+atol	/man/10/atoi
+atol.c	/man/10/atoi
+atom	/man/2/sexprs
+atom	/man/2/ubfa
+atom	/man/6/ubfa
+atomic	/man/1/sh-alphabet
+atomic	/man/2/sys-iounit
+atomic	/man/2/sys-open
+atomic	/man/3/prog
+atomic	/man/5/open
+atomically	/man/10/ref
+atomically	/man/2/exception
+atomically	/man/3/pipe
+atomically	/man/3/prog
+atomically	/man/5/read
+atomicio	/man/1/stream
+atomicio	/man/2/dial
+atomicio	/man/3/pipe
+atomicio	/man/4/dbfs
+atomicity	/man/2/sys-0intro
+atomicity	/man/3/pipe
+atoms	/man/2/sexprs
+atoms	/man/2/ubfa
+atoms	/man/6/ubfa
+atop	/man/1/sh-alphabet
+ats	/man/1/listen
+att	/man/10/a.out
+attach	/man/1/0intro
+attach	/man/1/alphabet-main
+attach	/man/1/bind
+attach	/man/1/deb
+attach	/man/1/listen
+attach	/man/1/wm-sh
+attach	/man/10/dev
+attach	/man/10/devattach
+attach	/man/10/plan9.ini
+attach	/man/10/styxserver
+attach	/man/2/draw-display
+attach	/man/2/factotum
+attach	/man/2/registries
+attach	/man/2/styx
+attach	/man/2/styxservers
+attach	/man/2/sys-fauth
+attach	/man/3/0intro
+attach	/man/3/draw
+attach	/man/3/ds
+attach	/man/3/flash
+attach	/man/3/indir
+attach	/man/3/ip
+attach	/man/3/mnt
+attach	/man/3/pipe
+attach	/man/3/plap
+attach	/man/3/sd
+attach	/man/3/srv
+attach	/man/3/srv9
+attach	/man/3/tinyfs
+attach	/man/3/vga
+attach	/man/5/0intro
+attach	/man/5/attach
+attach	/man/5/clunk
+attach	/man/5/stat
+attach	/man/6/users
+attach	/man/8/collabsrv
+attached	/man/1/0intro
+attached	/man/1/ebook
+attached	/man/10/acid
+attached	/man/10/plan9.ini
+attached	/man/2/draw-screen
+attached	/man/2/keyset
+attached	/man/2/registries
+attached	/man/2/sys-bind
+attached	/man/2/sys-pctl
+attached	/man/3/ip
+attached	/man/3/sd
+attached	/man/5/0intro
+attached	/man/8/fpgaload
+attached	/man/8/rip
+attached	/man/9/text
+attaches	/man/1/0intro
+attaches	/man/1/listen
+attaches	/man/1/logon
+attaches	/man/10/newchan
+attaches	/man/2/debug
+attaches	/man/3/indir
+attaches	/man/6/namespace
+attaching	/man/1/deb
+attaching	/man/2/styxservers
+attaching	/man/3/indir
+attaching	/man/8/init
+attachment	/man/1/0intro
+attachment	/man/2/sys-stat
+attack	/man/2/security-0intro
+attack	/man/6/login
+attacking	/man/2/security-0intro
+attacks	/man/2/security-0intro
+attacks	/man/6/login
+attain	/man/1/alphabet-main
+attempt	/man/1/charon
+attempt	/man/1/dmview
+attempt	/man/1/listen
+attempt	/man/1/miniterm
+attempt	/man/1/os
+attempt	/man/1/tiny
+attempt	/man/1/touch
+attempt	/man/1/wm
+attempt	/man/10/9load
+attempt	/man/10/c2l
+attempt	/man/10/devattach
+attempt	/man/10/lock
+attempt	/man/2/0intro
+attempt	/man/2/debug
+attempt	/man/2/dhcpclient
+attempt	/man/2/dial
+attempt	/man/2/dict
+attempt	/man/2/plumbmsg
+attempt	/man/2/prefab-element
+attempt	/man/2/registries
+attempt	/man/2/scsiio
+attempt	/man/2/security-0intro
+attempt	/man/2/sh
+attempt	/man/2/styxservers-nametree
+attempt	/man/2/sys-bind
+attempt	/man/2/sys-dial
+attempt	/man/3/cmd
+attempt	/man/3/flash
+attempt	/man/3/ip
+attempt	/man/4/keyfs
+attempt	/man/5/open
+attempt	/man/5/stat
+attempt	/man/7/db
+attempt	/man/8/prep
+attempted	/man/2/styxservers
+attempted	/man/5/stat
+attempting	/man/10/lock
+attempting	/man/2/sys-0intro
+attempting	/man/8/prep
+attempts	/man/1/blur
+attempts	/man/1/cd
+attempts	/man/1/charon
+attempts	/man/1/collab-clients
+attempts	/man/1/deb
+attempts	/man/1/grid-ns
+attempts	/man/1/tiny
+attempts	/man/1/touch
+attempts	/man/10/9load
+attempts	/man/10/dev
+attempts	/man/10/lock
+attempts	/man/10/qio
+attempts	/man/2/dhcpclient
+attempts	/man/2/disks
+attempts	/man/2/keyring-auth
+attempts	/man/2/newns
+attempts	/man/2/registries
+attempts	/man/2/scsiio
+attempts	/man/2/security-0intro
+attempts	/man/2/styx
+attempts	/man/2/sys-0intro
+attempts	/man/2/sys-bind
+attempts	/man/2/tftp
+attempts	/man/2/timers
+attempts	/man/3/cmd
+attempts	/man/3/cons
+attempts	/man/3/fs
+attempts	/man/3/touch
+attempts	/man/5/0intro
+attempts	/man/5/stat
+attempts	/man/8/dhcp
+attempts	/man/8/rdbgsrv
+attention	/man/1/acme
+attention	/man/1/cprof
+attention	/man/1/mprof
+attention	/man/1/prof
+attery	/man/10/plan9.ini
+attibutes	/man/4/factotum
+attr	/man/1/mk
+attr	/man/10/mk
+attr	/man/2/attrdb
+attr	/man/2/cfg
+attr	/man/2/factotum
+attr	/man/2/palmfile
+attr	/man/2/plumbmsg
+attr	/man/2/registries
+attr	/man/2/rfc822
+attr	/man/3/i82365
+attr	/man/3/sign
+attr	/man/4/factotum
+attr	/man/4/import
+attr	/man/4/registry
+attr	/man/4/spree
+attr	/man/6/plumbing
+attr1	/man/8/cs
+attr2	/man/8/cs
+attr2string	/man/2/plumbmsg
+attrdb	/man/2/attrdb
+attrdb	/man/2/registries
+attrdb	/man/4/registry
+attrdb	/man/6/attrdb
+attrdb	/man/6/ndb
+attrdb	/man/8/bootpd
+attrdb.b	/man/2/attrdb
+attrdb.m	/man/2/attrdb
+attrib	/man/2/w3c-css
+attribute	/man/1/ftest
+attribute	/man/1/grid-query
+attribute	/man/1/grid-register
+attribute	/man/1/mk
+attribute	/man/1/plumb
+attribute	/man/10/mk
+attribute	/man/10/plan9.ini
+attribute	/man/2/attrdb
+attribute	/man/2/cfg
+attribute	/man/2/convcs
+attribute	/man/2/dial
+attribute	/man/2/factotum
+attribute	/man/2/plumbmsg
+attribute	/man/2/registries
+attribute	/man/2/rfc822
+attribute	/man/2/spree
+attribute	/man/2/spree-cardlib
+attribute	/man/2/spree-objstore
+attribute	/man/2/styxpersist
+attribute	/man/2/sys-dial
+attribute	/man/2/xml
+attribute	/man/3/i82365
+attribute	/man/4/factotum
+attribute	/man/4/registry
+attribute	/man/4/spree
+attribute	/man/6/attrdb
+attribute	/man/6/ndb
+attribute	/man/8/ai2key
+attribute	/man/8/bootpd
+attribute	/man/8/cs
+attribute	/man/8/dns
+attribute	/man/8/prep
+attributed	/man/10/acid
+attributed	/man/2/styxservers
+attributes	/man/1/ftest
+attributes	/man/1/grid-query
+attributes	/man/1/grid-register
+attributes	/man/1/grid-session
+attributes	/man/1/mk
+attributes	/man/1/wm-misc
+attributes	/man/10/5cv
+attributes	/man/10/dev
+attributes	/man/10/kproc
+attributes	/man/10/mk
+attributes	/man/10/ntsrv
+attributes	/man/2/attrdb
+attributes	/man/2/cfg
+attributes	/man/2/convcs
+attributes	/man/2/dhcpclient
+attributes	/man/2/factotum
+attributes	/man/2/ip
+attributes	/man/2/palmfile
+attributes	/man/2/plumbmsg
+attributes	/man/2/registries
+attributes	/man/2/spree
+attributes	/man/2/spree-allow
+attributes	/man/2/spree-cardlib
+attributes	/man/2/styxservers
+attributes	/man/2/sys-0intro
+attributes	/man/2/sys-stat
+attributes	/man/2/xml
+attributes	/man/3/audio
+attributes	/man/3/sign
+attributes	/man/4/factotum
+attributes	/man/4/registry
+attributes	/man/5/0intro
+attributes	/man/5/stat
+attributes	/man/6/attrdb
+attributes	/man/6/ndb
+attributes	/man/6/proto
+attributes	/man/8/ai2key
+attributes	/man/8/bootpd
+attributes	/man/8/dns
+attributes.new	/man/2/registries
+attrs	/man/2/cfg
+attrs	/man/2/palmfile
+attrs	/man/2/plumbmsg
+attrs	/man/2/registries
+attrs	/man/2/xml
+attrs2string	/man/2/plumbmsg
+attrtext	/man/2/factotum
+atttributes	/man/2/xml
+au	/man/1/units
+au	/man/2/security-auth
+auctions	/man/8/collabsrv
+audience	/man/1/collab-clients
+audio	/man/1/auplay
+audio	/man/1/mux
+audio	/man/10/kproc
+audio	/man/2/volume
+audio	/man/3/audio
+audio	/man/3/kprof
+audio	/man/3/mpeg
+audio	/man/3/tv
+audio	/man/6/audio
+audio	/man/7/cddb
+audio0	/man/10/plan9.ini
+audioctl	/man/1/auplay
+audioctl	/man/1/mux
+audioctl	/man/3/audio
+audioctl	/man/6/audio
+audiox	/man/10/plan9.ini
+augmented	/man/1/mk
+augmented	/man/1/yacc
+augmented	/man/10/mk
+augments	/man/9/canvas
+augments	/man/9/text
+august	/man/2/palmfile
+auhdr	/man/1/auplay
+aui	/man/10/plan9.ini
+auip	/man/3/ip
+auplay	/man/1/auplay
+auplay	/man/3/audio
+auplay	/man/6/audio
+auplay.b	/man/1/auplay
+auppercentre	/man/2/spree-cardlib
+aupperleft	/man/2/spree-cardlib
+aupperright	/man/2/spree-cardlib
+ausers	/man/3/cap
+auth	/man/1/alphabet-fs
+auth	/man/1/alphabet-main
+auth	/man/1/bind
+auth	/man/1/collab
+auth	/man/1/cpu
+auth	/man/1/crypt
+auth	/man/1/fs
+auth	/man/1/listen
+auth	/man/1/passwd
+auth	/man/1/rcmd
+auth	/man/1/secstore
+auth	/man/10/odbc
+auth	/man/10/plan9.ini
+auth	/man/2/factotum
+auth	/man/2/keyring-0intro
+auth	/man/2/keyring-auth
+auth	/man/2/keyring-getmsg
+auth	/man/2/keyset
+auth	/man/2/msgio
+auth	/man/2/secstore
+auth	/man/2/security-0intro
+auth	/man/2/security-auth
+auth	/man/2/security-login
+auth	/man/2/security-ssl
+auth	/man/2/spki
+auth	/man/2/styx
+auth	/man/2/styxservers
+auth	/man/2/sys-export
+auth	/man/2/sys-fauth
+auth	/man/4/factotum
+auth	/man/4/keyfs
+auth	/man/4/keysrv
+auth	/man/4/lockfs
+auth	/man/4/registry
+auth	/man/5/0intro
+auth	/man/5/attach
+auth	/man/6/auth
+auth	/man/6/keys
+auth	/man/6/keytext
+auth	/man/6/ndb
+auth	/man/7/db
+auth	/man/7/dbsrv
+auth	/man/8/ai2key
+auth	/man/8/bootpd
+auth	/man/8/changelogin
+auth	/man/8/collabsrv
+auth	/man/8/createsignerkey
+auth	/man/8/logind
+auth	/man/8/rstyxd
+auth	/man/8/signer
+auth	/man/8/svc
+auth.b	/man/2/security-auth
+auth.sh	/man/8/svc
+authenticate	/man/1/bind
+authenticate	/man/1/listen
+authenticate	/man/1/netkey
+authenticate	/man/1/secstore
+authenticate	/man/10/odbc
+authenticate	/man/2/factotum
+authenticate	/man/2/keyring-0intro
+authenticate	/man/2/keyring-auth
+authenticate	/man/2/secstore
+authenticate	/man/2/styxpersist
+authenticate	/man/2/sys-fauth
+authenticate	/man/4/export
+authenticate	/man/4/factotum
+authenticate	/man/4/import
+authenticate	/man/5/attach
+authenticate	/man/6/keys
+authenticate	/man/8/collabsrv
+authenticate	/man/8/getauthinfo
+authenticated	/man/1/alphabet-main
+authenticated	/man/1/bind
+authenticated	/man/1/listen
+authenticated	/man/2/secstore
+authenticated	/man/2/security-0intro
+authenticated	/man/2/security-auth
+authenticated	/man/2/spree
+authenticated	/man/2/styxservers
+authenticated	/man/2/sys-export
+authenticated	/man/4/export
+authenticated	/man/4/import
+authenticated	/man/4/keysrv
+authenticated	/man/4/registry
+authenticated	/man/4/spree
+authenticated	/man/6/keytext
+authenticated	/man/7/dbsrv
+authenticated	/man/8/collabsrv
+authenticated	/man/8/getauthinfo
+authenticated	/man/8/register
+authenticates	/man/1/alphabet-main
+authenticates	/man/2/keyring-0intro
+authenticates	/man/2/secstore
+authenticates	/man/2/security-auth
+authenticates	/man/4/keysrv
+authenticates	/man/8/rstyxd
+authenticates	/man/8/svc
+authenticating	/man/1/bind
+authenticating	/man/1/collab
+authenticating	/man/1/passwd
+authenticating	/man/2/factotum
+authenticating	/man/2/security-0intro
+authenticating	/man/4/factotum
+authenticating	/man/4/lockfs
+authentication	/man/1/bind
+authentication	/man/1/collab
+authentication	/man/1/cpu
+authentication	/man/1/listen
+authentication	/man/1/ls
+authentication	/man/1/netkey
+authentication	/man/1/passwd
+authentication	/man/1/rcmd
+authentication	/man/1/secstore
+authentication	/man/10/odbc
+authentication	/man/10/plan9.ini
+authentication	/man/10/styxserver
+authentication	/man/2/dhcpclient
+authentication	/man/2/factotum
+authentication	/man/2/keyring-0intro
+authentication	/man/2/keyring-auth
+authentication	/man/2/keyring-getmsg
+authentication	/man/2/keyset
+authentication	/man/2/msgio
+authentication	/man/2/registries
+authentication	/man/2/secstore
+authentication	/man/2/security-0intro
+authentication	/man/2/security-auth
+authentication	/man/2/spki-verifier
+authentication	/man/2/styx
+authentication	/man/2/styxpersist
+authentication	/man/2/styxservers
+authentication	/man/2/sys-bind
+authentication	/man/2/sys-fauth
+authentication	/man/2/sys-stat
+authentication	/man/3/cons
+authentication	/man/3/ip
+authentication	/man/3/mnt
+authentication	/man/3/srv9
+authentication	/man/3/tls
+authentication	/man/4/factotum
+authentication	/man/4/import
+authentication	/man/4/keyfs
+authentication	/man/4/keysrv
+authentication	/man/4/lockfs
+authentication	/man/4/namespace
+authentication	/man/4/registry
+authentication	/man/5/0intro
+authentication	/man/5/attach
+authentication	/man/6/auth
+authentication	/man/6/keys
+authentication	/man/6/keytext
+authentication	/man/6/ndb
+authentication	/man/7/db
+authentication	/man/7/dbsrv
+authentication	/man/8/0intro
+authentication	/man/8/ai2key
+authentication	/man/8/bootpd
+authentication	/man/8/createsignerkey
+authentication	/man/8/getauthinfo
+authentication	/man/8/kfscmd
+authentication	/man/8/logind
+authentication	/man/8/rstyxd
+authentication	/man/8/signer
+authentication	/man/8/svc
+authentications	/man/4/keyfs
+authenticity	/man/2/security-0intro
+authinfo	/man/2/factotum
+authinfo	/man/2/keyring-0intro
+authinfo	/man/2/keyring-auth
+authinfo	/man/2/security-auth
+authinfo	/man/2/security-login
+authinfo	/man/4/factotum
+authinfo	/man/6/keytext
+authinfo	/man/8/ai2key
+author	/man/1/charon
+author	/man/1/cook
+authorisation	/man/2/spki
+authorisation	/man/7/dbsrv
+authorisation	/man/8/rstyxd
+authorised	/man/2/spki
+authorised	/man/8/rstyxd
+authoritative	/man/6/ndb
+authorities	/man/2/keyring-0intro
+authority	/man/2/keyring-0intro
+authority	/man/2/keyring-auth
+authority	/man/2/keyring-certtostr
+authority	/man/2/security-0intro
+authority	/man/2/security-login
+authority	/man/2/spki
+authority	/man/2/tk
+authority	/man/2/w3c-uris
+authority	/man/6/auth
+authority	/man/6/dis
+authority	/man/6/keys
+authority	/man/6/login
+authority	/man/8/dns
+authority	/man/8/getauthinfo
+authorized	/man/9/1copyright
+authors	/man/1/charon
+authors	/man/10/0intro
+authors	/man/9/1copyright
+authrpcmax	/man/2/factotum
+authsrv	/man/2/sys-fauth
+authsrv	/man/4/factotum
+auto	/man/1/alphabet-main
+auto	/man/1/sh-alphabet
+auto	/man/10/9load
+auto	/man/10/odbc
+auto	/man/9/canvas
+auto	/man/9/scale
+auto	/man/9/scrollbar
+autoconvert	/man/1/alphabet-abc
+autoconvert	/man/1/alphabet-fs
+autoconvert	/man/1/alphabet-grid
+autoconvert	/man/1/sh-alphabet
+autodeclare	/man/1/sh-alphabet
+autodetected	/man/10/plan9.ini
+autoexec.bat	/man/10/plan9.ini
+autoload	/man/1/sh
+automate	/man/1/wm-sh
+automate	/man/2/styxservers
+automatic	/man/1/acme
+automatic	/man/1/fs
+automatic	/man/1/sh-alphabet
+automatic	/man/1/sh-file2chan
+automatic	/man/1/tkcmd
+automatic	/man/10/2c
+automatic	/man/10/a.out
+automatic	/man/10/inm
+automatic	/man/10/plan9.ini
+automatic	/man/2/draw-image
+automatic	/man/2/plumbmsg
+automatic	/man/2/spree-gather
+automatic	/man/3/ip
+automatic	/man/3/logfs
+automatic	/man/4/acme
+automatic	/man/5/stat
+automatic	/man/9/update
+automatically	/man/1/acme
+automatically	/man/1/cprof
+automatically	/man/1/fortune
+automatically	/man/1/grid-ns
+automatically	/man/1/grid-session
+automatically	/man/1/mash
+automatically	/man/1/mk
+automatically	/man/1/mprof
+automatically	/man/1/mux
+automatically	/man/1/prof
+automatically	/man/1/sh
+automatically	/man/1/sh-alphabet
+automatically	/man/1/sh-file2chan
+automatically	/man/1/stack
+automatically	/man/1/tktester
+automatically	/man/1/webgrab
+automatically	/man/1/wm-misc
+automatically	/man/1/wm-sh
+automatically	/man/10/2l
+automatically	/man/10/9load
+automatically	/man/10/a.out
+automatically	/man/10/acid
+automatically	/man/10/ar
+automatically	/man/10/c2l
+automatically	/man/10/conf
+automatically	/man/10/master
+automatically	/man/10/mk
+automatically	/man/10/ntsrv
+automatically	/man/10/odbc
+automatically	/man/10/plan9.ini
+automatically	/man/10/styxserver
+automatically	/man/2/alphabet-intro
+automatically	/man/2/asn1
+automatically	/man/2/dhcpclient
+automatically	/man/2/diskblocks
+automatically	/man/2/draw-0intro
+automatically	/man/2/srv
+automatically	/man/2/styxservers
+automatically	/man/2/sys-0intro
+automatically	/man/2/sys-export
+automatically	/man/2/volume
+automatically	/man/3/0intro
+automatically	/man/3/ds
+automatically	/man/3/fpga
+automatically	/man/3/fs
+automatically	/man/3/ip
+automatically	/man/3/logfs
+automatically	/man/3/snarf
+automatically	/man/3/srv9
+automatically	/man/3/tls
+automatically	/man/3/usb
+automatically	/man/4/ftpfs
+automatically	/man/4/grid-cpu
+automatically	/man/4/keysrv
+automatically	/man/4/logfile
+automatically	/man/5/version
+automatically	/man/6/image
+automatically	/man/6/ndb
+automatically	/man/8/prep
+automatically	/man/8/styxchat
+automatically	/man/9/button
+automatically	/man/9/canvas
+automatically	/man/9/checkbutton
+automatically	/man/9/entry
+automatically	/man/9/focus
+automatically	/man/9/grab
+automatically	/man/9/menu
+automatically	/man/9/menubutton
+automatically	/man/9/options
+automatically	/man/9/radiobutton
+automatically	/man/9/scale
+automatically	/man/9/scrollbar
+automatically	/man/9/text
+automation	/man/1/charon
+autoprobing	/man/10/plan9.ini
+autosense	/man/10/5cv
+aux1	/man/3/audio
+aux2	/man/3/audio
+auxi	/man/1/alphabet-fs
+auxi	/man/8/fpgaload
+auxi	/man/8/mangaload
+auxi	/man/8/rdbgsrv
+auxi	/man/8/rstyxd
+auxiliary	/man/10/conf
+auxiliary	/man/10/devattach
+auxiliary	/man/2/crc
+auxiliary	/man/2/w3c-css
+auxilliary	/man/1/wm
+av	/man/4/import
+availability	/man/1/wm-sh
+aval	/man/2/factotum
+avanstar	/man/10/plan9.ini
+avayalabs.com	/man/4/factotum
+average	/man/3/touch
+average	/man/4/iostats
+average	/man/8/ping
+average	/man/9/entry
+average	/man/9/text
+average	/man/9/types
+avogadro's	/man/1/units
+avoid	/man/1/fc
+avoid	/man/1/sh-arg
+avoid	/man/1/sh-expr
+avoid	/man/1/unicode
+avoid	/man/10/dev
+avoid	/man/2/dbm
+avoid	/man/2/sh
+avoid	/man/2/sys-print
+avoid	/man/2/sys-read
+avoid	/man/3/logfs
+avoid	/man/4/kfs
+avoid	/man/5/stat
+avoid	/man/6/ndb
+avoid	/man/9/text
+avoiding	/man/4/ftpfs
+avoids	/man/1/ar
+avoids	/man/1/sh-file2chan
+avoids	/man/10/iar
+avoids	/man/2/crc
+avoids	/man/6/colour
+avr	/man/1/avr
+avrs	/man/1/avr
+await	/man/1/sh-std
+awaiting	/man/10/sleep
+awaiting	/man/8/plumber
+aware	/man/1/charon
+aware	/man/1/sh-std
+aware	/man/10/qio
+aware	/man/2/spree
+awk	/man/1/mk
+awk	/man/10/mk
+aximm	/man/2/dis
+axinf	/man/2/dis
+axinm	/man/2/dis
+axis	/man/1/wm-misc
+axis	/man/2/draw-image
+axis	/man/2/w3c-xpointers
+axisname	/man/2/w3c-xpointers
+axnon	/man/2/dis
+axorb	/man/1/alphabet-fs
+axorb	/man/1/fs
+axxx	/man/2/dis
+azerty	/man/1/miniterm
+b.b	/man/1/mk
+b.c	/man/10/mk
+b.hit	/man/2/tk
+b.root	/man/6/ndb
+b40f04	/man/1/stack
+b419a4	/man/1/stack
+b64text	/man/2/sexprs
+b64toip	/man/2/keyring-ipint
+backed	/man/2/palmfile
+backed	/man/9/canvas
+background	/man/1/cprof
+background	/man/1/os
+background	/man/1/sh
+background	/man/1/tiny
+background	/man/1/tktester
+background	/man/2/draw-display
+background	/man/2/draw-image
+background	/man/2/draw-screen
+background	/man/2/prefab-style
+background	/man/2/sys-export
+background	/man/3/draw
+background	/man/4/export
+background	/man/8/dhcp
+background	/man/8/plumber
+background	/man/9/button
+background	/man/9/canvas
+background	/man/9/checkbutton
+background	/man/9/choicebutton
+background	/man/9/entry
+background	/man/9/frame
+background	/man/9/label
+background	/man/9/listbox
+background	/man/9/menu
+background	/man/9/menubutton
+background	/man/9/options
+background	/man/9/panel
+background	/man/9/radiobutton
+background	/man/9/scale
+background	/man/9/scrollbar
+background	/man/9/text
+backgrounds	/man/1/ebook
+backgrounds	/man/1/listen
+backgrounds	/man/2/prefab-0intro
+backgrounds	/man/9/text
+backing	/man/2/draw-screen
+backing	/man/3/draw
+backing:int	/man/2/draw-screen
+backquote	/man/1/sh
+backquoted	/man/1/mk
+backquoted	/man/10/mk
+backslash	/man/1/acme
+backslash	/man/1/mash
+backslash	/man/1/mdb
+backslash	/man/1/mk
+backslash	/man/1/sh-regex
+backslash	/man/1/wish
+backslash	/man/10/mk
+backslash	/man/6/sexprs
+backslash	/man/6/translate
+backslash	/man/9/0intro
+backslash	/man/9/bind
+backslashes	/man/1/acme
+backslashes	/man/3/cmd
+backslashes	/man/9/bind
+backspace	/man/1/wish
+backspace	/man/3/cons
+backspace	/man/6/keyboard
+backspace	/man/6/sexprs
+backspace	/man/9/entry
+backspace	/man/9/text
+backspacing	/man/1/acme
+backup	/man/2/palmfile
+backward	/man/1/acme
+backward	/man/1/diff
+backward	/man/6/image
+backward	/man/6/utf
+backward	/man/9/text
+backwards	/man/1/acme
+backwards	/man/1/brutus
+backwards	/man/1/charon
+backwards	/man/1/ebook
+backwards	/man/2/keyring-rc4
+backwards	/man/9/text
+bad	/man/1/sh
+bad	/man/1/sh-std
+bad	/man/1/sh-tk
+bad	/man/10/error
+bad	/man/10/styxserver
+bad	/man/2/format
+bad	/man/3/dbg
+bad	/man/3/logfs
+bad	/man/4/keyfs
+bad	/man/8/kfscmd
+badel	/man/2/spki-verifier
+balancing	/man/2/ida
+balancing	/man/9/0intro
+band	/man/2/keyring-0intro
+bang	/man/1/sh-tk
+bank	/man/3/flash
+bank	/man/8/ftl
+banking	/man/2/security-0intro
+bar	/man/1/acme
+bar	/man/1/charon
+bar	/man/1/collab-clients
+bar	/man/1/cprof
+bar	/man/1/deb
+bar	/man/1/keyboard
+bar	/man/1/mash-tk
+bar	/man/1/mk
+bar	/man/1/sh-alphabet
+bar	/man/1/tiny
+bar	/man/1/tktester
+bar	/man/1/wm-misc
+bar	/man/1/wm-sh
+bar	/man/10/mk
+bar	/man/2/ir
+bar	/man/2/tkclient
+bar	/man/2/wmclient
+bar	/man/4/acme
+barb	/man/2/draw-image
+bare	/man/1/ls
+bare	/man/10/2l
+bars	/man/1/wm-misc
+bars	/man/2/dividers
+bars	/man/2/palmfile
+bars	/man/9/menubutton
+bartlett's	/man/9/canvas
+base	/man/1/calc
+base	/man/1/fc
+base	/man/1/sh-expr
+base	/man/1/wm-misc
+base	/man/10/2l
+base	/man/10/a.out
+base	/man/10/allocb
+base	/man/10/atoi
+base	/man/10/dynld
+base	/man/10/plan9.ini
+base	/man/2/dbm
+base	/man/2/dial
+base	/man/2/dis
+base	/man/2/disks
+base	/man/2/draw-screen
+base	/man/2/drawmux
+base	/man/2/encoding
+base	/man/2/keyring-0intro
+base	/man/2/keyring-ipint
+base	/man/2/math-elem
+base	/man/2/math-fp
+base	/man/2/palmfile
+base	/man/2/prefab-style
+base	/man/2/print
+base	/man/2/sexprs
+base	/man/2/string
+base	/man/2/sys-dial
+base	/man/2/w3c-uris
+base	/man/3/flash
+base	/man/3/kprof
+base	/man/3/pnp
+base	/man/3/tls
+base	/man/6/attrdb
+base	/man/6/auth
+base	/man/6/dis
+base	/man/6/image
+base	/man/6/keytext
+base	/man/6/login
+base	/man/6/ndb
+base	/man/6/sexprs
+base	/man/7/db
+base	/man/7/dbsrv
+base	/man/8/create
+base	/man/9/text
+base16	/man/2/encoding
+base16path	/man/2/encoding
+base32	/man/2/encoding
+base32a	/man/2/encoding
+base32apath	/man/2/encoding
+base32path	/man/2/encoding
+base64	/man/1/sh-expr
+base64	/man/2/encoding
+base64	/man/6/login
+base64path	/man/2/encoding
+based	/man/1/0intro
+based	/man/1/acme
+based	/man/1/charon
+based	/man/1/ftree
+based	/man/1/netkey
+based	/man/1/ns
+based	/man/1/sh-file2chan
+based	/man/1/uuencode
+based	/man/10/2c
+based	/man/10/5cv
+based	/man/10/plan9.ini
+based	/man/10/styxserver
+based	/man/2/dividers
+based	/man/2/draw-font
+based	/man/2/format
+based	/man/2/keyring-auth
+based	/man/2/lock
+based	/man/2/newns
+based	/man/2/prefab-compound
+based	/man/2/readdir
+based	/man/2/styxconv
+based	/man/2/styxservers
+based	/man/2/tk
+based	/man/2/w3c-xpointers
+based	/man/2/wmclient
+based	/man/3/ip
+based	/man/3/tls
+based	/man/3/touch
+based	/man/4/dbfs
+based	/man/4/logfile
+based	/man/4/memfs
+based	/man/5/0intro
+based	/man/6/auth
+based	/man/6/ndb
+based	/man/6/utf
+based	/man/7/db
+based	/man/8/applylog
+based	/man/8/prep
+based	/man/8/register
+based	/man/8/rip
+based	/man/8/rstyxd
+based	/man/9/0intro
+based	/man/9/choicebutton
+based	/man/9/grid
+baseline	/man/2/draw-font
+baseline	/man/2/imagefile
+baseline	/man/6/font
+baseline	/man/9/text
+basename	/man/1/basename
+basename	/man/1/cleanname
+basename	/man/1/diff
+basename	/man/2/names
+basename.b	/man/1/basename
+bases	/man/2/keyring-ipint
+basic	/man/1/alphabet-main
+basic	/man/1/auplay
+basic	/man/1/mux
+basic	/man/1/sh-alphabet
+basic	/man/10/xalloc
+basic	/man/2/0intro
+basic	/man/2/alphabet-intro
+basic	/man/2/asn1
+basic	/man/2/dhcpclient
+basic	/man/2/dialog
+basic	/man/2/draw-0intro
+basic	/man/2/draw-display
+basic	/man/2/draw-example
+basic	/man/2/factotum
+basic	/man/2/math-0intro
+basic	/man/2/math-elem
+basic	/man/2/math-linalg
+basic	/man/2/security-0intro
+basic	/man/2/sexprs
+basic	/man/2/spki
+basic	/man/2/spki-verifier
+basic	/man/2/spree
+basic	/man/2/sys-0intro
+basic	/man/2/wmlib
+basic	/man/3/i2c
+basic	/man/3/vid
+basic	/man/6/json
+basic	/man/6/sbl
+basic	/man/6/ubfa
+basis	/man/2/convcs
+basis	/man/3/prof
+basis	/man/9/1copyright
+bass	/man/3/tv
+batopa	/man/1/alphabet-fs
+batopa	/man/1/fs
+battery	/man/10/plan9.ini
+baud	/man/10/9load
+baud	/man/10/plan9.ini
+baud	/man/3/dbg
+baud	/man/3/eia
+baud	/man/4/palmsrv
+baud	/man/8/mangaload
+baud	/man/8/rdbgsrv
+baw	/man/8/prep
+bb	/man/9/types
+bbaabb	/man/2/regex
+bbbbbbbb.bbbbbbbb	/man/6/utf
+bbox	/man/9/canvas
+bbox	/man/9/entry
+bbox	/man/9/grid
+bbox	/man/9/text
+bc	/man/1/sh-arg
+bc	/man/2/arg
+bc	/man/2/dhcpclient
+bcfnprw	/man/8/prep
+bclk	/man/3/fpga
+bclk	/man/8/fpgaload
+bcmflnve123	/man/1/mprof
+bd	/man/9/options
+bdata	/man/10/2l
+bdmpr	/man/8/dhcp
+beamed	/man/2/palmfile
+bear	/man/2/disks
+bears	/man/2/asn1
+bebytestoip	/man/2/keyring-ipint
+becomes	/man/1/bind
+becomes	/man/1/logwindow
+becomes	/man/1/sh
+becomes	/man/1/tktester
+becomes	/man/10/c2l
+becomes	/man/10/devattach
+becomes	/man/10/qio
+becomes	/man/2/bufio-chanfill
+becomes	/man/2/sys-bind
+becomes	/man/3/srv
+becomes	/man/4/logfile
+becomes	/man/4/memfs
+becomes	/man/6/namespace
+becomes	/man/8/getauthinfo
+becomes	/man/9/canvas
+becomes	/man/9/menu
+becomes	/man/9/pack
+becomes	/man/9/radiobutton
+becomes	/man/9/text
+becoming	/man/2/spree
+bed	/man/10/plan9.ini
+beforehand	/man/2/styx
+beforethis	/man/9/canvas
+beg	/man/2/regex
+beg:end	/man/2/regex
+begun	/man/1/acme
+behalf	/man/2/spki-verifier
+behalf	/man/2/sys-file2chan
+behalf	/man/9/1copyright
+behave	/man/1/calc
+behave	/man/10/atoi
+behave	/man/2/draw-0intro
+behave	/man/2/filter
+behave	/man/2/math-elem
+behave	/man/2/styx
+behave	/man/4/acme
+behave	/man/9/entry
+behave	/man/9/grab
+behave	/man/9/text
+behaved	/man/2/tkclient
+behaves	/man/1/0intro
+behaves	/man/1/acme
+behaves	/man/1/cp
+behaves	/man/2/draw-image
+behaves	/man/2/prefab-compound
+behaves	/man/9/canvas
+behaves	/man/9/menu
+behaves	/man/9/text
+behavior	/man/1/ar
+behavior	/man/10/iar
+behavior	/man/10/plan9.ini
+behavior	/man/2/draw-image
+behavior	/man/3/tls
+behavior	/man/5/open
+behaviour	/man/1/acme
+behaviour	/man/1/emu
+behaviour	/man/1/ftree
+behaviour	/man/1/os
+behaviour	/man/1/sh
+behaviour	/man/1/sh-alphabet
+behaviour	/man/1/wm-sh
+behaviour	/man/1/yacc
+behaviour	/man/2/draw-image
+behaviour	/man/2/sets
+behaviour	/man/2/sh
+behaviour	/man/2/sys-read
+behaviour	/man/2/wmsrv
+behaviour	/man/8/create
+behaviour	/man/9/button
+behaviour	/man/9/canvas
+behaviour	/man/9/checkbutton
+behaviour	/man/9/choicebutton
+behaviour	/man/9/entry
+behaviour	/man/9/frame
+behaviour	/man/9/label
+behaviour	/man/9/listbox
+behaviour	/man/9/menu
+behaviour	/man/9/menubutton
+behaviour	/man/9/panel
+behaviour	/man/9/radiobutton
+behaviour	/man/9/scale
+behaviour	/man/9/scrollbar
+behaviour	/man/9/text
+behaviours	/man/9/canvas
+behaviours	/man/9/text
+belgiumfranc	/man/1/units
+believes	/man/1/acme
+believes	/man/1/blur
+bell	/man/10/0intro
+belong	/man/2/prefab-element
+belonging	/man/1/grid-register
+belonging	/man/10/conf
+belonging	/man/2/secstore
+belongs	/man/2/asn1
+belongs	/man/2/security-0intro
+belowthis	/man/9/canvas
+belowthis	/man/9/text
+beneath	/man/1/ftree
+beneath	/man/2/tk
+beneath	/man/2/w3c-xpointers
+benefit	/man/1/deb
+benefit	/man/4/spree
+ber	/man/2/asn1
+besides	/man/2/draw-0intro
+besides	/man/5/0intro
+bessel	/man/2/math-elem
+beta	/man/2/math-linalg
+beware	/man/1/cal
+beware	/man/1/cat
+beware	/man/2/sys-stat
+beware	/man/4/import
+beyond	/man/1/wm-sh
+beyond	/man/10/devattach
+beyond	/man/10/odbc
+beyond	/man/10/qio
+beyond	/man/2/ether
+beyond	/man/2/regex
+beyond	/man/2/styxservers
+beyond	/man/3/flash
+beyond	/man/9/canvas
+beyond	/man/9/entry
+beyond	/man/9/grid
+beyond	/man/9/text
+bezier	/man/2/draw-image
+bezier	/man/9/canvas
+bezierop	/man/2/draw-image
+bezspline	/man/2/draw-image
+bezsplineop	/man/2/draw-image
+bfbsize	/man/2/keyring-crypt
+bfile	/man/8/prep
+bflag	/man/1/sh-arg
+bflag	/man/2/arg
+bflnve	/man/1/prof
+bfstate	/man/2/keyring-crypt
+bg	/man/2/draw-image
+bg	/man/9/options
+bgid	/man/3/draw
+bgp	/man/2/draw-image
+bh	/man/3/tv
+bi	/man/6/man
+biased	/man/2/geodesy
+bidirectional	/man/1/sh-std
+bidirectional	/man/5/0intro
+bigger	/man/2/spree-cardlib
+bigger	/man/2/tkclient
+bigger	/man/2/wmclient
+bigger	/man/3/i2c
+bigger	/man/4/kfs
+bigger	/man/9/0intro
+bigincrement	/man/9/scale
+bigint	/man/2/asn1
+bigrand	/man/2/rand
+bigs	/man/2/dis
+bigs	/man/2/rand
+bill	/man/10/styxserver
+billion	/man/2/dbm
+bin	/man/1/acme
+bin	/man/1/grid-ns
+bin	/man/10/2c
+bin	/man/10/acid
+bin	/man/10/mk
+bin	/man/10/ntsrv
+bin	/man/10/plan9.ini
+bin	/man/3/cmd
+bin	/man/4/namespace
+bin	/man/7/dbsrv
+bina	/man/1/alphabet-fs
+bina	/man/1/fs
+binaries	/man/1/mash-make
+binaries	/man/4/namespace
+binary	/man/1/fc
+binary	/man/1/look
+binary	/man/1/m4
+binary	/man/1/mdb
+binary	/man/1/sh-file2chan
+binary	/man/1/sh-sexprs
+binary	/man/1/strings
+binary	/man/10/2l
+binary	/man/10/a.out
+binary	/man/10/ms2
+binary	/man/10/print
+binary	/man/2/dbm
+binary	/man/2/encoding
+binary	/man/2/factotum
+binary	/man/2/math-0intro
+binary	/man/2/math-export
+binary	/man/2/math-fp
+binary	/man/2/palmfile
+binary	/man/2/sexprs
+binary	/man/2/spki
+binary	/man/2/stringinttab
+binary	/man/2/ubfa
+binary	/man/2/w3c-xpointers
+binary	/man/3/cmd
+binary	/man/3/dbg
+binary	/man/3/draw
+binary	/man/3/fpga
+binary	/man/3/ip
+binary	/man/3/root
+binary	/man/3/tv
+binary	/man/3/usb
+binary	/man/4/factotum
+binary	/man/6/audio
+binary	/man/6/sexprs
+binary	/man/6/ubfa
+binary	/man/6/utf
+binary	/man/7/db
+binary	/man/8/collabsrv
+binary	/man/8/fpgaload
+binary	/man/8/httpd
+binary	/man/8/styxchat
+bind	/man/1/0intro
+bind	/man/1/acme
+bind	/man/1/bind
+bind	/man/1/collab-clients
+bind	/man/1/deb
+bind	/man/1/ns
+bind	/man/1/nsbuild
+bind	/man/1/os
+bind	/man/1/ps
+bind	/man/1/pwd
+bind	/man/1/stack
+bind	/man/1/tiny
+bind	/man/10/dev
+bind	/man/10/devattach
+bind	/man/10/master
+bind	/man/10/odbc
+bind	/man/2/factotum
+bind	/man/2/keyset
+bind	/man/2/newns
+bind	/man/2/styxconv
+bind	/man/2/sys-0intro
+bind	/man/2/sys-bind
+bind	/man/2/sys-export
+bind	/man/2/sys-fauth
+bind	/man/2/sys-fd2path
+bind	/man/2/sys-file2chan
+bind	/man/2/sys-fversion
+bind	/man/2/sys-open
+bind	/man/2/sys-pipe
+bind	/man/3/0intro
+bind	/man/3/arch
+bind	/man/3/audio
+bind	/man/3/boot
+bind	/man/3/cap
+bind	/man/3/cmd
+bind	/man/3/cons
+bind	/man/3/dbg
+bind	/man/3/draw
+bind	/man/3/ds
+bind	/man/3/dup
+bind	/man/3/dynld
+bind	/man/3/eia
+bind	/man/3/env
+bind	/man/3/ether
+bind	/man/3/flash
+bind	/man/3/floppy
+bind	/man/3/fpga
+bind	/man/3/fs
+bind	/man/3/ftl
+bind	/man/3/gpio
+bind	/man/3/i2c
+bind	/man/3/i82365
+bind	/man/3/indir
+bind	/man/3/ip
+bind	/man/3/kprof
+bind	/man/3/logfs
+bind	/man/3/lpt
+bind	/man/3/mnt
+bind	/man/3/mpeg
+bind	/man/3/pbus
+bind	/man/3/pipe
+bind	/man/3/plap
+bind	/man/3/pnp
+bind	/man/3/pointer
+bind	/man/3/prof
+bind	/man/3/prog
+bind	/man/3/root
+bind	/man/3/rtc
+bind	/man/3/sd
+bind	/man/3/sign
+bind	/man/3/snarf
+bind	/man/3/srv
+bind	/man/3/srv9
+bind	/man/3/switch
+bind	/man/3/tinyfs
+bind	/man/3/tls
+bind	/man/3/touch
+bind	/man/3/tv
+bind	/man/3/usb
+bind	/man/3/vga
+bind	/man/3/vid
+bind	/man/4/9srvfs
+bind	/man/4/archfs
+bind	/man/4/dbfs
+bind	/man/4/dossrv
+bind	/man/4/ftpfs
+bind	/man/4/import
+bind	/man/4/mntgen
+bind	/man/4/ramfile
+bind	/man/5/0intro
+bind	/man/5/attach
+bind	/man/5/open
+bind	/man/5/version
+bind	/man/5/walk
+bind	/man/6/attrdb
+bind	/man/6/namespace
+bind	/man/8/applylog
+bind	/man/8/collabsrv
+bind	/man/8/dhcp
+bind	/man/8/getauthinfo
+bind	/man/8/mkfs
+bind	/man/8/rdbgsrv
+bind	/man/9/0intro
+bind	/man/9/bind
+bind	/man/9/canvas
+bind	/man/9/destroy
+bind	/man/9/text
+bind.b	/man/1/bind
+binding	/man/1/bind
+binding	/man/1/deb
+binding	/man/2/dial
+binding	/man/2/spki
+binding	/man/2/sys-bind
+binding	/man/2/sys-file2chan
+binding	/man/2/translate
+binding	/man/6/attrdb
+binding	/man/6/translate
+binding	/man/8/init
+binding	/man/9/bind
+binding	/man/9/canvas
+binding	/man/9/text
+bindings	/man/1/tktester
+bindings	/man/2/tk
+bindings	/man/9/button
+bindings	/man/9/canvas
+bindings	/man/9/checkbutton
+bindings	/man/9/entry
+bindings	/man/9/frame
+bindings	/man/9/label
+bindings	/man/9/listbox
+bindings	/man/9/menu
+bindings	/man/9/menubutton
+bindings	/man/9/options
+bindings	/man/9/panel
+bindings	/man/9/radiobutton
+bindings	/man/9/scale
+bindings	/man/9/scrollbar
+bindings	/man/9/text
+binds	/man/1/acme
+binds	/man/1/collab
+binds	/man/2/prof
+binds	/man/3/0intro
+binds	/man/3/cmd
+binds	/man/3/snarf
+binds	/man/4/factotum
+binds	/man/8/rstyxd
+bio	/man/10/print
+bios	/man/10/9load
+bios	/man/10/plan9.ini
+bios	/man/2/disks
+bios	/man/3/pnp
+bios	/man/8/prep
+bioses	/man/10/9load
+bioses	/man/2/disks
+bioses	/man/8/prep
+bioss	/man/10/9load
+bishop	/man/6/keyboard
+bit	/man/1/charon
+bit	/man/1/emu
+bit	/man/1/fc
+bit	/man/1/ls
+bit	/man/1/m4
+bit	/man/1/mux
+bit	/man/1/sh-expr
+bit	/man/1/sh-std
+bit	/man/1/sum
+bit	/man/1/tr
+bit	/man/1/vacget
+bit	/man/10/2c
+bit	/man/10/9load
+bit	/man/10/a.out
+bit	/man/10/c2l
+bit	/man/10/devattach
+bit	/man/10/dmainit
+bit	/man/10/kbdputc
+bit	/man/10/malloc
+bit	/man/10/newchan
+bit	/man/10/plan9.ini
+bit	/man/10/readnum
+bit	/man/10/styx
+bit	/man/2/asn1
+bit	/man/2/bloomfilter
+bit	/man/2/crc
+bit	/man/2/daytime
+bit	/man/2/dbm
+bit	/man/2/dis
+bit	/man/2/draw-0intro
+bit	/man/2/draw-display
+bit	/man/2/draw-image
+bit	/man/2/draw-pointer
+bit	/man/2/draw-screen
+bit	/man/2/keyring-0intro
+bit	/man/2/keyring-crypt
+bit	/man/2/math-export
+bit	/man/2/math-fp
+bit	/man/2/palmfile
+bit	/man/2/scsiio
+bit	/man/2/security-0intro
+bit	/man/2/sets
+bit	/man/2/sexprs
+bit	/man/2/string
+bit	/man/2/styxservers
+bit	/man/2/styxservers-nametree
+bit	/man/2/sys-open
+bit	/man/2/sys-seek
+bit	/man/2/sys-stat
+bit	/man/2/tk
+bit	/man/3/arch
+bit	/man/3/audio
+bit	/man/3/cons
+bit	/man/3/dbg
+bit	/man/3/draw
+bit	/man/3/flash
+bit	/man/3/fpga
+bit	/man/3/ftl
+bit	/man/3/gpio
+bit	/man/3/i2c
+bit	/man/3/pnp
+bit	/man/3/pointer
+bit	/man/3/prog
+bit	/man/3/srv
+bit	/man/3/ssl
+bit	/man/3/switch
+bit	/man/3/vga
+bit	/man/4/acme
+bit	/man/5/0intro
+bit	/man/5/open
+bit	/man/5/stat
+bit	/man/6/audio
+bit	/man/6/colour
+bit	/man/6/dis
+bit	/man/6/image
+bit	/man/6/keyboard
+bit	/man/6/keytext
+bit	/man/6/sexprs
+bit	/man/6/utf
+bit	/man/8/mangaload
+bit	/man/8/rdbgsrv
+bit	/man/8/register
+bit	/man/8/signer
+bit	/man/8/styxchat
+bit	/man/9/canvas
+bit16sz	/man/10/styx
+bit32sz	/man/10/styx
+bit64sz	/man/10/styx
+bit8sz	/man/10/styx
+bitmap	/man/1/mash-tk
+bitmap	/man/1/mux
+bitmap	/man/1/wm-misc
+bitmap	/man/2/convcs
+bitmap	/man/2/dis
+bitmap	/man/3/draw
+bitmap	/man/4/namespace
+bitmap	/man/9/button
+bitmap	/man/9/canvas
+bitmap	/man/9/checkbutton
+bitmap	/man/9/choicebutton
+bitmap	/man/9/cursor
+bitmap	/man/9/image
+bitmap	/man/9/label
+bitmap	/man/9/menu
+bitmap	/man/9/menubutton
+bitmap	/man/9/options
+bitmap	/man/9/radiobutton
+bitmap	/man/9/types
+bitmaps	/man/9/canvas
+bitmask	/man/1/sh-std
+bitmask	/man/2/sh
+bitmask	/man/2/spree-cardlib
+bitmask	/man/2/tk
+bitmask	/man/8/register
+bits	/man/1/auplay
+bits	/man/1/chmod
+bits	/man/1/sh-expr
+bits	/man/10/ar
+bits	/man/10/devattach
+bits	/man/2/asn1
+bits	/man/2/bloomfilter
+bits	/man/2/convcs
+bits	/man/2/dis
+bits	/man/2/disks
+bits	/man/2/draw-0intro
+bits	/man/2/draw-display
+bits	/man/2/draw-image
+bits	/man/2/draw-pointer
+bits	/man/2/drawmux
+bits	/man/2/encoding
+bits	/man/2/ip
+bits	/man/2/keyring-crypt
+bits	/man/2/keyring-gensk
+bits	/man/2/keyring-ipint
+bits	/man/2/math-0intro
+bits	/man/2/math-fp
+bits	/man/2/palmfile
+bits	/man/2/sets
+bits	/man/2/sh
+bits	/man/2/sys-open
+bits	/man/2/sys-stat
+bits	/man/3/audio
+bits	/man/3/cons
+bits	/man/3/dbg
+bits	/man/3/draw
+bits	/man/3/eia
+bits	/man/3/fpga
+bits	/man/3/ftl
+bits	/man/3/gpio
+bits	/man/3/pnp
+bits	/man/3/switch
+bits	/man/3/vga
+bits	/man/5/0intro
+bits	/man/5/open
+bits	/man/5/stat
+bits	/man/6/audio
+bits	/man/6/colour
+bits	/man/6/dis
+bits	/man/6/image
+bits	/man/6/sbl
+bits	/man/6/utf
+bits	/man/8/ai2key
+bits	/man/8/createsignerkey
+bits	/man/8/mangaload
+bits	/man/8/styxchat
+bitsize	/man/8/createsignerkey
+bitstring	/man/2/asn1
+bitwise	/man/1/fc
+bitwise	/man/1/mdb
+bitwise	/man/1/sh-expr
+bitwise	/man/2/keyring-ipint
+bitwise	/man/2/sets
+bitwise	/man/2/tkclient
+bitwise	/man/2/wmclient
+bitwise	/man/4/acme
+bl	/man/3/tv
+black	/man/1/acme
+black	/man/1/wm-misc
+black	/man/2/draw-display
+black	/man/2/draw-example
+black	/man/2/draw-image
+black	/man/2/print
+black	/man/3/vga
+black	/man/6/colour
+black	/man/6/image
+black	/man/9/canvas
+black	/man/9/options
+black	/man/9/types
+blackdepth	/man/2/print
+blackresmult	/man/2/print
+blanc	/man/10/odbc
+blank	/man/1/acme
+blank	/man/1/mash
+blank	/man/1/mk
+blank	/man/1/sh
+blank	/man/1/strings
+blank	/man/1/tiny
+blank	/man/1/xd
+blank	/man/10/ar
+blank	/man/10/conf
+blank	/man/10/mk
+blank	/man/10/parsecmd
+blank	/man/10/readnum
+blank	/man/2/complete
+blank	/man/2/string
+blank	/man/3/cons
+blank	/man/3/draw
+blank	/man/3/pointer
+blank	/man/3/vga
+blank	/man/4/acme
+blank	/man/4/dbfs
+blank	/man/4/registry
+blank	/man/6/font
+blank	/man/6/image
+blank	/man/6/json
+blank	/man/6/plumbing
+blank	/man/6/sexprs
+blank	/man/6/ubfa
+blank	/man/8/prep
+blank	/man/9/grid
+blanking	/man/10/plan9.ini
+blanking	/man/3/vga
+blanks	/man/1/dd
+blanks	/man/1/diff
+blanks	/man/1/echo
+blanks	/man/1/look
+blanks	/man/1/m4
+blanks	/man/1/mash
+blanks	/man/1/mk
+blanks	/man/1/sh
+blanks	/man/1/wc
+blanks	/man/10/inm
+blanks	/man/10/mk
+blanks	/man/10/print
+blanks	/man/2/sys-print
+blanks	/man/3/cons
+blanks	/man/3/vga
+blanks	/man/6/man
+blanks	/man/6/sexprs
+blanks	/man/9/0intro
+blanktime	/man/3/vga
+blas	/man/2/math-0intro
+blas	/man/2/math-linalg
+blas2	/man/2/math-linalg
+blas3	/man/2/math-linalg
+blaster	/man/10/plan9.ini
+blatant	/man/9/canvas
+blen	/man/10/allocb
+blending	/man/3/draw
+blinded	/man/2/keyring-0intro
+blinded	/man/2/security-0intro
+blinded	/man/8/register
+blinded	/man/8/signer
+blinding	/man/2/keyring-0intro
+blinding	/man/8/signer
+blinds	/man/8/signer
+block	/man/1/blur
+block	/man/1/charon
+block	/man/1/crypt
+block	/man/1/dd
+block	/man/1/fs
+block	/man/1/sh
+block	/man/1/sh-alphabet
+block	/man/1/sh-file2chan
+block	/man/1/sh-std
+block	/man/1/sh-tk
+block	/man/1/tee
+block	/man/1/vacget
+block	/man/1/wm-sh
+block	/man/1/zeros
+block	/man/10/9load
+block	/man/10/acid
+block	/man/10/allocb
+block	/man/10/dev
+block	/man/10/devattach
+block	/man/10/error
+block	/man/10/malloc
+block	/man/10/odbc
+block	/man/10/plan9.ini
+block	/man/10/qio
+block	/man/10/xalloc
+block	/man/2/dbm
+block	/man/2/diskblocks
+block	/man/2/disks
+block	/man/2/draw-image
+block	/man/2/filter-slip
+block	/man/2/ida
+block	/man/2/keyring-crypt
+block	/man/2/lock
+block	/man/2/security-0intro
+block	/man/2/sh
+block	/man/2/venti
+block	/man/2/wmsrv
+block	/man/3/ds
+block	/man/3/eia
+block	/man/3/flash
+block	/man/3/ftl
+block	/man/3/ip
+block	/man/3/logfs
+block	/man/3/mpeg
+block	/man/3/pbus
+block	/man/3/pipe
+block	/man/3/plap
+block	/man/3/prog
+block	/man/3/ssl
+block	/man/3/tinyfs
+block	/man/4/ftpfs
+block	/man/4/kfs
+block	/man/4/logfile
+block	/man/4/spree
+block	/man/6/image
+block	/man/8/kfscmd
+block	/man/8/mkfs
+block	/man/8/prep
+block's	/man/10/qio
+block.0.a	/man/1/blur
+block.1.a	/man/1/blur
+block.b	/man/1/blur
+blocked	/man/1/deb
+blocked	/man/10/qlock
+blocked	/man/2/command
+blocked	/man/2/styxpersist
+blocked	/man/3/prog
+blocked	/man/3/srv
+blocked	/man/4/logfile
+blocked	/man/4/spree
+blocking	/man/1/sh-file2chan
+blocking	/man/1/sh-tk
+blocking	/man/10/lock
+blocking	/man/10/qio
+blocking	/man/10/qlock
+blocking	/man/2/sys-stat
+blocking	/man/3/eia
+blocking	/man/3/ip
+blocking	/man/4/logfile
+blocking	/man/9/send
+blocklen	/man/10/allocb
+blocks	/man/1/blur
+blocks	/man/1/cat
+blocks	/man/1/cp
+blocks	/man/1/dd
+blocks	/man/1/sh
+blocks	/man/1/sh-file2chan
+blocks	/man/1/tail
+blocks	/man/1/vacget
+blocks	/man/1/wm-misc
+blocks	/man/1/zeros
+blocks	/man/10/acid
+blocks	/man/10/allocb
+blocks	/man/10/dev
+blocks	/man/10/plan9.ini
+blocks	/man/10/qio
+blocks	/man/10/sleep
+blocks	/man/2/crc
+blocks	/man/2/dbm
+blocks	/man/2/diskblocks
+blocks	/man/2/draw-0intro
+blocks	/man/2/keyring-crypt
+blocks	/man/2/keyring-sha1
+blocks	/man/2/prefab-element
+blocks	/man/2/sys-export
+blocks	/man/2/sys-file2chan
+blocks	/man/2/tftp
+blocks	/man/2/venti
+blocks	/man/3/cmd
+blocks	/man/3/cons
+blocks	/man/3/ftl
+blocks	/man/3/logfs
+blocks	/man/3/pipe
+blocks	/man/3/pointer
+blocks	/man/4/kfs
+blocks	/man/4/lockfs
+blocks	/man/4/memfs
+blocks	/man/4/registry
+blocks	/man/6/image
+blocks	/man/8/kfscmd
+blocks	/man/8/prep
+blocksize	/man/1/du
+blocksize	/man/1/vacget
+blocksize	/man/1/wm-misc
+blocksize	/man/1/zeros
+bloggs	/man/4/ftpfs
+blood	/man/1/charon
+bloom	/man/2/bloomfilter
+bloomfilter	/man/2/bloomfilter
+bloomfilter.b	/man/2/bloomfilter
+bloomfilter.m	/man/2/bloomfilter
+blowfish	/man/2/keyring-crypt
+blowfishcbc	/man/2/keyring-crypt
+blowfishsetup	/man/2/keyring-crypt
+blt	/man/9/grid
+blue	/man/1/wm-sh
+blue	/man/2/draw-0intro
+blue	/man/2/draw-display
+blue	/man/2/draw-example
+blue	/man/2/draw-image
+blue	/man/3/draw
+blue	/man/3/tv
+blue	/man/6/colour
+blue	/man/6/image
+blue	/man/9/types
+bluegreen	/man/2/draw-display
+blunders	/man/10/lock
+blur	/man/1/blur
+blur.b	/man/1/blur
+blurs	/man/1/blur
+bmpstring	/man/2/asn1
+bnf	/man/2/w3c-css
+bnf	/man/6/sexprs
+board's	/man/3/switch
+boards	/man/10/plan9.ini
+bob	/man/1/mk
+bob	/man/10/mk
+bob	/man/2/sexprs
+boffin	/man/3/mpeg
+bol	/man/2/regex
+bold	/man/1/brutus
+bold	/man/1/cook
+bold	/man/6/man
+book	/man/1/ebook
+book	/man/2/keyring-crypt
+book	/man/2/palmfile
+book	/man/2/security-0intro
+book	/man/2/tabs
+book	/man/3/ssl
+bookmark	/man/1/charon
+books	/man/2/palmfile
+bool	/man/2/asn1
+boolean	/man/1/sh-expr
+boolean	/man/2/asn1
+boolean	/man/2/draw-0intro
+boolean	/man/2/draw-image
+boolean	/man/2/lists
+boolean	/man/2/prof
+boolean	/man/2/sets
+boolean	/man/9/canvas
+boolean	/man/9/checkbutton
+boolean	/man/9/grid
+boolean	/man/9/options
+boolean	/man/9/pack
+boolean	/man/9/radiobutton
+boolean	/man/9/scale
+boolean	/man/9/text
+boolean	/man/9/types
+booleans	/man/1/limbo
+booleans	/man/6/json
+boost	/man/10/lock
+boot	/man/1/sh
+boot	/man/10/2l
+boot	/man/10/5cv
+boot	/man/10/9load
+boot	/man/10/plan9.ini
+boot	/man/10/seconds
+boot	/man/2/disks
+boot	/man/2/tftp
+boot	/man/3/boot
+boot	/man/3/cons
+boot	/man/3/dbg
+boot	/man/3/ds
+boot	/man/3/logfs
+boot	/man/3/pnp
+boot	/man/3/srv9
+boot	/man/8/bootpd
+boot	/man/8/prep
+boot	/man/8/rdbgsrv
+bootable	/man/10/conf
+bootable	/man/2/disks
+bootable	/man/8/prep
+bootblock	/man/8/prep
+bootconf	/man/2/dhcpclient
+bootconf.bootf	/man/2/dhcpclient
+bootconf.bootip	/man/2/dhcpclient
+bootconf.dom	/man/2/dhcpclient
+bootconf.ipgw	/man/2/dhcpclient
+bootconf.ipmask	/man/2/dhcpclient
+bootconf.lease	/man/2/dhcpclient
+bootconf.sys	/man/2/dhcpclient
+booted	/man/10/9load
+booted	/man/3/srv9
+bootf	/man/2/dhcpclient
+bootf	/man/6/ndb
+bootf	/man/8/bootpd
+bootfile	/man/10/9load
+bootfile	/man/10/plan9.ini
+booting	/man/10/9load
+booting	/man/10/plan9.ini
+booting	/man/2/tftp
+booting	/man/3/fpga
+booting	/man/6/ndb
+booting	/man/8/bootpd
+booting	/man/8/kfscmd
+booting	/man/8/prep
+bootip	/man/2/dhcpclient
+bootp	/man/10/9load
+bootp	/man/2/dhcpclient
+bootp	/man/3/ip
+bootp	/man/8/bootpd
+bootp	/man/8/dhcp
+bootp	/man/8/init
+bootp	/man/8/rip
+bootpd	/man/2/dhcpclient
+bootpd	/man/2/ether
+bootpd	/man/2/tftp
+bootpd	/man/6/ndb
+bootpd	/man/8/bootpd
+bootpd	/man/8/mangaload
+bootpd.b	/man/8/bootpd
+boots	/man/2/sys-chdir
+boots	/man/2/workdir
+boots	/man/3/ds
+boots	/man/5/attach
+boots	/man/8/bootpd
+bootsize	/man/3/logfs
+bootstrap	/man/10/0intro
+bootstrap	/man/10/5cv
+bootstrap	/man/10/9load
+bootstrap	/man/10/plan9.ini
+bootstrap	/man/3/flash
+bootstrap	/man/3/ip
+bootstrap	/man/3/root
+bootstrap	/man/6/ndb
+bootstrap	/man/8/dns
+bootstrap	/man/8/mangaload
+bootstrap	/man/8/prep
+bootstrapping	/man/4/export
+border	/man/2/draw-0intro
+border	/man/2/draw-image
+border	/man/2/prefab-compound
+border	/man/2/prefab-element
+border	/man/2/tk
+border	/man/2/wmclient
+border	/man/9/frame
+border	/man/9/grid
+border	/man/9/options
+border	/man/9/text
+borderop	/man/2/draw-image
+borders	/man/1/ebook
+borders	/man/2/prefab-style
+borders	/man/9/scrollbar
+borderwidth	/man/2/tk
+borderwidth	/man/2/translate
+borderwidth	/man/9/button
+borderwidth	/man/9/canvas
+borderwidth	/man/9/checkbutton
+borderwidth	/man/9/choicebutton
+borderwidth	/man/9/entry
+borderwidth	/man/9/frame
+borderwidth	/man/9/label
+borderwidth	/man/9/listbox
+borderwidth	/man/9/menu
+borderwidth	/man/9/menubutton
+borderwidth	/man/9/options
+borderwidth	/man/9/panel
+borderwidth	/man/9/radiobutton
+borderwidth	/man/9/scale
+borderwidth	/man/9/scrollbar
+borderwidth	/man/9/text
+bottom	/man/1/acme
+bottom	/man/1/charon
+bottom	/man/1/collab-clients
+bottom	/man/1/grid-session
+bottom	/man/1/miniterm
+bottom	/man/1/mux
+bottom	/man/1/tktester
+bottom	/man/1/wm
+bottom	/man/1/wm-misc
+bottom	/man/2/draw-display
+bottom	/man/2/draw-image
+bottom	/man/2/prefab-compound
+bottom	/man/2/prefab-element
+bottom	/man/2/wmsrv
+bottom	/man/3/cons
+bottom	/man/3/draw
+bottom	/man/3/tv
+bottom	/man/6/dis
+bottom	/man/6/font
+bottom	/man/6/image
+bottom	/man/9/canvas
+bottom	/man/9/grid
+bottom	/man/9/menu
+bottom	/man/9/pack
+bottom	/man/9/scale
+bottom	/man/9/scrollbar
+bottom	/man/9/see
+bottom	/man/9/text
+bottommost	/man/9/canvas
+bottommost	/man/9/menu
+bound	/man/1/acme
+bound	/man/1/bind
+bound	/man/1/cpu
+bound	/man/1/deb
+bound	/man/1/logwindow
+bound	/man/1/netstat
+bound	/man/1/os
+bound	/man/1/ps
+bound	/man/1/tiny
+bound	/man/10/plan9.ini
+bound	/man/2/ip
+bound	/man/2/sys-0intro
+bound	/man/2/sys-bind
+bound	/man/2/sys-file2chan
+bound	/man/3/0intro
+bound	/man/3/cmd
+bound	/man/3/env
+bound	/man/3/fs
+bound	/man/3/ip
+bound	/man/3/srv
+bound	/man/3/srv9
+bound	/man/3/ssl
+bound	/man/4/9srvfs
+bound	/man/4/acme
+bound	/man/6/namespace
+bound	/man/8/dhcp
+bound	/man/8/getauthinfo
+bound	/man/9/bind
+bound	/man/9/canvas
+bound	/man/9/entry
+boundaries	/man/1/acme
+boundaries	/man/1/brutus
+boundaries	/man/2/disks
+boundaries	/man/2/keyring-getmsg
+boundaries	/man/2/msgio
+boundaries	/man/2/sexprs
+boundaries	/man/3/ether
+boundaries	/man/3/ftl
+boundaries	/man/3/ip
+boundaries	/man/3/pipe
+boundaries	/man/3/tls
+boundaries	/man/9/bind
+boundaries	/man/9/text
+boundary	/man/10/allocb
+boundary	/man/10/ar
+boundary	/man/10/dmainit
+boundary	/man/10/malloc
+boundary	/man/10/readnum
+boundary	/man/10/xalloc
+boundary	/man/3/flash
+boundary	/man/3/ftl
+boundary	/man/3/pbus
+boundary	/man/3/vga
+boundary	/man/9/canvas
+bounded	/man/1/brutus
+bounded	/man/1/ebook
+bounded	/man/1/wm-sh
+bounded	/man/10/memory
+bounded	/man/9/canvas
+bounding	/man/1/wm
+bounding	/man/2/tk
+bounding	/man/9/canvas
+bounding	/man/9/entry
+bounding	/man/9/grid
+bounds	/man/1/charon
+bounds	/man/1/kill
+bounds	/man/1/sh-string
+bounds	/man/10/allocb
+bounds	/man/2/0intro
+bounds	/man/2/sys-byte2char
+bounds	/man/2/sys-utfbytes
+bounds	/man/6/regexp
+bouta	/man/1/alphabet-fs
+bouta	/man/1/fs
+bovera	/man/1/alphabet-fs
+bovera	/man/1/fs
+box	/man/1/acme
+box	/man/1/grid-session
+box	/man/1/mash-tk
+box	/man/1/tktester
+box	/man/1/wm
+box	/man/1/wm-misc
+box	/man/2/dialog
+box	/man/2/keyring-0intro
+box	/man/2/pop3
+box	/man/2/popup
+box	/man/2/prefab-compound
+box	/man/2/security-0intro
+box	/man/8/manufacture
+box	/man/8/register
+box	/man/8/signer
+box	/man/9/canvas
+box	/man/9/entry
+box	/man/9/grid
+box	/man/9/menu
+boxed	/man/2/sys-byte2char
+boxes	/man/1/wm-misc
+boxes	/man/2/dialog
+boxes	/man/2/popup
+boxes	/man/2/prefab-compound
+boxes	/man/2/security-0intro
+boxes	/man/9/canvas
+bp	/man/10/devattach
+bp	/man/3/draw
+bpdel	/man/10/acid
+bph	/man/10/allocb
+bpset	/man/10/acid
+bpt	/man/3/prog
+bptab	/man/10/acid
+bquote	/man/1/mk
+bquote	/man/1/sh
+bquote	/man/10/mk
+br	/man/1/fs
+br	/man/6/man
+brace	/man/1/acme
+brace	/man/1/bind
+brace	/man/1/brutus
+brace	/man/1/sh
+brace	/man/1/wm-sh
+brace	/man/9/0intro
+braced	/man/1/fs
+braced	/man/1/sh
+braced	/man/1/sh-std
+braced	/man/10/odbc
+braced	/man/2/sh
+braces	/man/1/acme
+braces	/man/1/brutus
+braces	/man/1/calc
+braces	/man/1/mash
+braces	/man/1/sh
+braces	/man/1/sh-std
+braces	/man/2/sexprs
+braces	/man/2/tk
+braces	/man/4/9srvfs
+braces	/man/6/json
+braces	/man/6/sexprs
+braces	/man/9/0intro
+braces	/man/9/bind
+bracket	/man/1/acme
+bracket	/man/1/wm-sh
+bracket	/man/1/yacc
+bracket	/man/9/0intro
+bracketed	/man/1/brutus
+bracketed	/man/6/regexp
+bracketed	/man/6/sexprs
+brackets	/man/1/acme
+brackets	/man/1/calc
+brackets	/man/1/math-misc
+brackets	/man/1/sh
+brackets	/man/1/sh-alphabet
+brackets	/man/1/sh-std
+brackets	/man/1/yacc
+brackets	/man/10/2c
+brackets	/man/3/draw
+brackets	/man/5/0intro
+brackets	/man/6/json
+brackets	/man/9/0intro
+branch	/man/2/asn1
+brd	/man/3/touch
+bread	/man/10/dev
+break	/man/1/0intro
+break	/man/1/calc
+break	/man/1/deb
+break	/man/1/mk
+break	/man/1/sh-csv
+break	/man/1/sh-regex
+break	/man/1/sh-sexprs
+break	/man/1/sh-std
+break	/man/1/yacc
+break	/man/10/getfields
+break	/man/10/mk
+break	/man/10/parsecmd
+break	/man/2/debug
+break	/man/2/ir
+break	/man/2/security-0intro
+break	/man/3/eia
+break	/man/4/lockfs
+break	/man/4/trfs
+break	/man/5/stat
+break	/man/6/man
+break	/man/9/canvas
+break	/man/9/text
+break.c	/man/3/dbg
+breaking	/man/2/prefab-element
+breaking	/man/2/sys-iounit
+breaking	/man/5/open
+breakmsg	/man/2/spree
+breakpoint	/man/1/deb
+breakpoint	/man/10/acid
+breakpoint	/man/2/debug
+breakpoint	/man/3/dbg
+breakpoint	/man/3/prog
+breakpoints	/man/1/deb
+breakpoints	/man/10/acid
+breakpoints	/man/2/debug
+breaks	/man/1/deb
+breaks	/man/10/getfields
+breaks	/man/2/ida
+breaks	/man/2/math-fp
+breaks	/man/2/prefab-element
+breaks	/man/2/sys-tokenize
+breaks	/man/9/canvas
+brethren	/man/10/styx
+brgallon	/man/1/units
+bridging	/man/10/plan9.ini
+brief	/man/2/asn1
+briefly	/man/2/ip
+briefly	/man/2/w3c-xpointers
+briefly	/man/6/json
+bright	/man/3/fpga
+bright	/man/8/fpgaload
+brightness	/man/3/tv
+bring	/man/1/diff
+bring	/man/1/grid-session
+bring	/man/1/tktester
+bring	/man/1/wm
+bring	/man/2/scsiio
+bring	/man/2/tkclient
+bring	/man/2/wmclient
+bring	/man/8/prep
+bring	/man/9/listbox
+brings	/man/1/deb
+brings	/man/1/grid-query
+britain	/man/2/geodesy
+britainpound	/man/1/units
+british	/man/1/units
+broad	/man/1/0intro
+broadcast	/man/2/ip
+broadcast	/man/2/virgil
+broadcast	/man/3/ether
+broadcast	/man/3/ip
+broadcast	/man/8/rip
+broadcasts	/man/8/bootpd
+broadcasts	/man/8/dhcp
+broadcasts	/man/8/rip
+broadly	/man/3/audio
+broke	/man/1/kill
+broke.b	/man/1/kill
+broken	/man/1/deb
+broken	/man/1/emu
+broken	/man/1/kill
+broken	/man/2/draw-image
+broken	/man/2/exception
+broken	/man/2/keyring-sha1
+broken	/man/2/sh
+broken	/man/2/sys-iounit
+broken	/man/3/cons
+broken	/man/3/dbg
+broken	/man/3/mnt
+broken	/man/3/prog
+broken	/man/4/vacfs
+broken	/man/9/canvas
+broken	/man/9/text
+browse	/man/1/grid-query
+browse	/man/2/selectfile
+browse	/man/9/listbox
+browse.b	/man/1/grid-query
+browse.b	/man/1/grid-session
+browsed	/man/1/charon
+browser	/man/1/charon
+browser	/man/1/ebook
+browser	/man/1/filename
+browser	/man/1/ftree
+browser	/man/1/mux
+browser	/man/1/wm-misc
+browser	/man/2/selectfile
+browsers	/man/1/charon
+browsing	/man/1/deb
+browsing	/man/1/wm-misc
+bruce	/man/2/keyring-crypt
+bruce	/man/2/security-0intro
+bruce	/man/3/indir
+brush	/man/1/collab-clients
+brush	/man/2/draw-example
+brush.fillellipse	/man/2/draw-example
+brute	/man/6/login
+brutus	/man/1/brutus
+brutus	/man/1/cook
+brutus	/man/6/keyboard
+brutus	/man/6/plumbing
+brutus.b	/man/1/brutus
+brutus.dis	/man/6/plumbing
+bs	/man/1/dd
+bsd	/man/3/ip
+bsize	/man/2/keyring-crypt
+bsize	/man/4/kfs
+bsrc	/man/10/acid
+bss	/man/10/2l
+bss	/man/10/a.out
+bss	/man/10/dynld
+bss	/man/10/inm
+bsssize	/man/10/ksize
+bst	/man/2/daytime
+bt	/man/10/plan9.ini
+bt485hwgc	/man/3/vga
+btime	/man/2/palmfile
+btos	/man/2/convcs
+btos.b	/man/2/convcs
+btos.dis	/man/2/convcs
+bucket	/man/3/kprof
+buckets	/man/3/kprof
+buf	/man/1/stack
+buf	/man/10/dev
+buf	/man/10/dynld
+buf	/man/10/parsecmd
+buf	/man/10/print
+buf	/man/10/qio
+buf	/man/10/readnum
+buf	/man/10/styx
+buf	/man/10/styxserver
+buf	/man/2/crc
+buf	/man/2/devpointer
+buf	/man/2/dial
+buf	/man/2/disks
+buf	/man/2/filter
+buf	/man/2/keyring-crypt
+buf	/man/2/keyring-getmsg
+buf	/man/2/keyring-ipint
+buf	/man/2/keyring-rc4
+buf	/man/2/keyring-sha1
+buf	/man/2/msgio
+buf	/man/2/sys-byte2char
+buf	/man/2/sys-read
+buf	/man/2/sys-utfbytes
+buf	/man/2/tkclient
+buf	/man/2/venti
+buf	/man/2/wmclient
+buf	/man/2/wmlib
+buf	/man/3/draw
+buffer	/man/1/acme
+buffer	/man/1/brutus
+buffer	/man/1/dd
+buffer	/man/1/logwindow
+buffer	/man/1/mash-tk
+buffer	/man/1/wm-misc
+buffer	/man/1/wm-sh
+buffer	/man/1/xd
+buffer	/man/10/allocb
+buffer	/man/10/dev
+buffer	/man/10/devattach
+buffer	/man/10/dmainit
+buffer	/man/10/parsecmd
+buffer	/man/10/readnum
+buffer	/man/10/styx
+buffer	/man/10/styxserver
+buffer	/man/2/bufio
+buffer	/man/2/bufio-chanfill
+buffer	/man/2/draw-context
+buffer	/man/2/draw-image
+buffer	/man/2/filter
+buffer	/man/2/keyring-crypt
+buffer	/man/2/styx
+buffer	/man/2/sys-byte2char
+buffer	/man/2/sys-fversion
+buffer	/man/2/sys-read
+buffer	/man/2/tkclient
+buffer	/man/2/wmclient
+buffer	/man/2/wmlib
+buffer	/man/2/wmsrv
+buffer	/man/3/cons
+buffer	/man/3/draw
+buffer	/man/3/flash
+buffer	/man/3/ip
+buffer	/man/3/pipe
+buffer	/man/3/snarf
+buffer	/man/3/ssl
+buffer	/man/3/tls
+buffer	/man/3/vid
+buffer	/man/4/logfile
+buffer	/man/4/spree
+buffer	/man/8/rdbgsrv
+buffer	/man/9/canvas
+buffer's	/man/3/snarf
+buffered	/man/1/cat
+buffered	/man/1/math-misc
+buffered	/man/10/print
+buffered	/man/2/bufio
+buffered	/man/2/bufio-chanfill
+buffered	/man/2/dhcpclient
+buffered	/man/2/sexprs
+buffered	/man/2/sys-pipe
+buffered	/man/3/cons
+buffered	/man/3/logfs
+buffered	/man/3/pipe
+buffered	/man/9/canvas
+buffered	/man/9/send
+buffering	/man/10/allocb
+buffering	/man/10/qio
+buffering	/man/10/readnum
+buffering	/man/2/sys-pipe
+buffering	/man/3/logfs
+buffering	/man/9/canvas
+buffers	/man/1/stream
+buffers	/man/10/styx
+buffers	/man/10/xalloc
+buffers	/man/2/bufio
+buffers	/man/2/draw-image
+buffers	/man/2/wmsrv
+buffers	/man/3/mpeg
+buffers	/man/3/ssl
+buffers	/man/6/colour
+bufio	/man/1/yacc
+bufio	/man/2/bufio
+bufio	/man/2/bufio-chanfill
+bufio	/man/2/csv
+bufio	/man/2/format
+bufio	/man/2/imagefile
+bufio	/man/2/json
+bufio	/man/2/pslib
+bufio	/man/2/rfc822
+bufio	/man/2/sexprs
+bufio	/man/2/spki
+bufio	/man/2/sys-read
+bufio	/man/2/sys-seek
+bufio	/man/2/sys-self
+bufio	/man/2/ubfa
+bufio	/man/2/xml
+bufio.b	/man/2/bufio
+bufio.m	/man/1/yacc
+bufio.m	/man/2/attrdb
+bufio.m	/man/2/bufio
+bufio.m	/man/2/bufio-chanfill
+bufio.m	/man/2/csv
+bufio.m	/man/2/format
+bufio.m	/man/2/palmfile
+bufio.m	/man/2/rfc822
+bufio.m	/man/2/sexprs
+bufio.m	/man/2/spki
+bufio.m	/man/2/spki-verifier
+bufiofill	/man/2/bufio
+bufiofill	/man/2/bufio-chanfill
+bufiofill	/man/2/sys-self
+bufsiz	/man/2/sys-read
+bufsize	/man/1/stream
+bufsize	/man/2/sys-fversion
+bug	/man/10/plan9.ini
+bug	/man/10/print
+bug	/man/10/ref
+buggy	/man/3/sd
+bugs	/man/1/9win
+bugs	/man/1/acme
+bugs	/man/1/ar
+bugs	/man/1/asm
+bugs	/man/1/cal
+bugs	/man/1/cat
+bugs	/man/1/charon
+bugs	/man/1/chmod
+bugs	/man/1/cprof
+bugs	/man/1/cpu
+bugs	/man/1/deb
+bugs	/man/1/diff
+bugs	/man/1/disdep
+bugs	/man/1/ebook
+bugs	/man/1/env
+bugs	/man/1/fc
+bugs	/man/1/filename
+bugs	/man/1/ftest
+bugs	/man/1/grid-query
+bugs	/man/1/listen
+bugs	/man/1/logwindow
+bugs	/man/1/man
+bugs	/man/1/mc
+bugs	/man/1/mdb
+bugs	/man/1/miniterm
+bugs	/man/1/mk
+bugs	/man/1/mprof
+bugs	/man/1/mux
+bugs	/man/1/nsbuild
+bugs	/man/1/prof
+bugs	/man/1/ps
+bugs	/man/1/secstore
+bugs	/man/1/sh
+bugs	/man/1/sh-alphabet
+bugs	/man/1/sh-csv
+bugs	/man/1/sh-expr
+bugs	/man/1/sh-mload
+bugs	/man/1/sort
+bugs	/man/1/time
+bugs	/man/1/tktester
+bugs	/man/1/units
+bugs	/man/1/uuencode
+bugs	/man/1/vacget
+bugs	/man/1/webgrab
+bugs	/man/1/wish
+bugs	/man/1/xd
+bugs	/man/1/yacc
+bugs	/man/10/2c
+bugs	/man/10/9load
+bugs	/man/10/a.out
+bugs	/man/10/acid
+bugs	/man/10/allocb
+bugs	/man/10/ar
+bugs	/man/10/c2l
+bugs	/man/10/iar
+bugs	/man/10/mk
+bugs	/man/10/plan9.ini
+bugs	/man/10/print
+bugs	/man/10/strcat
+bugs	/man/10/styxserver
+bugs	/man/2/asn1
+bugs	/man/2/bufio
+bugs	/man/2/daytime
+bugs	/man/2/dbm
+bugs	/man/2/debug
+bugs	/man/2/dict
+bugs	/man/2/dividers
+bugs	/man/2/draw-display
+bugs	/man/2/draw-image
+bugs	/man/2/geodesy
+bugs	/man/2/hash
+bugs	/man/2/ip
+bugs	/man/2/keyring-sha1
+bugs	/man/2/lists
+bugs	/man/2/msgio
+bugs	/man/2/plumbmsg
+bugs	/man/2/pslib
+bugs	/man/2/rand
+bugs	/man/2/smtp
+bugs	/man/2/spree
+bugs	/man/2/spree-cardlib
+bugs	/man/2/stringinttab
+bugs	/man/2/styxpersist
+bugs	/man/2/sys-bind
+bugs	/man/2/sys-dial
+bugs	/man/2/sys-file2chan
+bugs	/man/2/sys-fversion
+bugs	/man/2/sys-print
+bugs	/man/2/venti
+bugs	/man/2/virgil
+bugs	/man/2/xml
+bugs	/man/3/audio
+bugs	/man/3/cons
+bugs	/man/3/dbg
+bugs	/man/3/draw
+bugs	/man/3/ds
+bugs	/man/3/dynld
+bugs	/man/3/flash
+bugs	/man/3/ftl
+bugs	/man/3/i82365
+bugs	/man/3/indir
+bugs	/man/3/kprof
+bugs	/man/3/logfs
+bugs	/man/3/mnt
+bugs	/man/3/pbus
+bugs	/man/3/plap
+bugs	/man/3/pnp
+bugs	/man/3/prof
+bugs	/man/3/rtc
+bugs	/man/3/sd
+bugs	/man/3/srv
+bugs	/man/3/vga
+bugs	/man/3/vid
+bugs	/man/4/dbfs
+bugs	/man/4/dossrv
+bugs	/man/4/ftpfs
+bugs	/man/4/grid-cpu
+bugs	/man/4/import
+bugs	/man/4/iostats
+bugs	/man/4/kfs
+bugs	/man/4/lockfs
+bugs	/man/4/logfile
+bugs	/man/4/palmsrv
+bugs	/man/4/ramfile
+bugs	/man/4/registry
+bugs	/man/4/trfs
+bugs	/man/4/vacfs
+bugs	/man/5/stat
+bugs	/man/6/keytext
+bugs	/man/6/man
+bugs	/man/8/dns
+bugs	/man/8/ping
+bugs	/man/8/prep
+bugs	/man/8/rdbgsrv
+bugs	/man/8/virgild
+bugs	/man/9/0intro
+bugs	/man/9/bind
+bugs	/man/9/cursor
+bugs	/man/9/grab
+bugs	/man/9/image
+bugs	/man/9/listbox
+bugs	/man/9/menu
+bugs	/man/9/send
+bugs	/man/9/text
+bugs	/man/9/update
+build	/man/1/0intro
+build	/man/1/grid-session
+build	/man/1/mash-make
+build	/man/1/mk
+build	/man/1/nsbuild
+build	/man/1/webgrab
+build	/man/1/yacc
+build	/man/10/lock
+build	/man/10/mk
+build	/man/10/srclist
+build	/man/2/draw-font
+build	/man/2/newns
+build	/man/2/prefab-0intro
+build	/man/2/prefab-compound
+build	/man/2/prefab-element
+build	/man/2/spki
+build	/man/2/styx
+build	/man/8/0intro
+build	/man/8/create
+building	/man/1/mash-tk
+building	/man/1/mk
+building	/man/10/mk
+building	/man/2/draw-0intro
+building	/man/2/draw-image
+building	/man/2/prefab-0intro
+building	/man/2/prefab-compound
+building	/man/2/prefab-element
+building	/man/2/tk
+building	/man/3/tinyfs
+building	/man/4/namespace
+building	/man/8/init
+builds	/man/1/nsbuild
+builds	/man/10/devattach
+builds	/man/2/filepat
+builds	/man/2/newns
+builds	/man/2/prefab-element
+builds	/man/3/ds
+built	/man/1/acme
+built	/man/1/calc
+built	/man/1/charon
+built	/man/1/emu
+built	/man/1/m4
+built	/man/1/mash
+built	/man/1/mash-make
+built	/man/1/sh
+built	/man/1/sh-std
+built	/man/1/stack
+built	/man/1/toolbar
+built	/man/10/5coff
+built	/man/10/conf
+built	/man/10/devattach
+built	/man/10/plan9.ini
+built	/man/2/draw-0intro
+built	/man/2/draw-font
+built	/man/2/draw-image
+built	/man/2/factotum
+built	/man/2/keyring-0intro
+built	/man/2/math-linalg
+built	/man/2/prefab-compound
+built	/man/2/spree
+built	/man/2/srv
+built	/man/2/sys-0intro
+built	/man/2/sys-pctl
+built	/man/2/sys-self
+built	/man/3/cmd
+built	/man/3/root
+built	/man/3/sign
+built	/man/4/acme
+built	/man/4/kfs
+built	/man/4/namespace
+built	/man/8/create
+built	/man/8/cs
+built	/man/8/init
+builtin	/man/1/0intro
+builtin	/man/1/mash
+builtin	/man/1/mash-make
+builtin	/man/1/mash-tk
+builtin	/man/1/sh
+builtin	/man/1/sh-mload
+builtin	/man/1/sh-regex
+builtin	/man/1/sh-std
+builtin	/man/1/sh-string
+builtin	/man/1/sh-tk
+builtin	/man/1/wm-sh
+builtin	/man/10/conf
+builtin	/man/2/sh
+builtins	/man/1/mash
+builtins	/man/1/mk
+builtins	/man/1/sh
+builtins	/man/1/sh-mload
+builtins	/man/1/sh-std
+builtins	/man/1/sh-string
+builtins	/man/10/mk
+builtins	/man/2/sh
+builtins.b	/man/1/mash
+bulk	/man/3/usb
+bundle	/man/1/alphabet-fs
+bundle	/man/1/fs
+bundled	/man/1/alphabet-fs
+bundled	/man/1/fs
+bundled	/man/2/styxservers
+burden	/man/1/charon
+burgundy	/man/10/odbc
+burn	/man/1/avr
+burn	/man/8/mangaload
+burn	/man/8/manufacture
+burn.b	/man/1/avr
+burns	/man/8/mangaload
+bus	/man/10/dmainit
+bus	/man/10/intrenable
+bus	/man/10/plan9.ini
+bus	/man/3/i2c
+bus	/man/3/pbus
+bus	/man/3/plap
+bus	/man/3/pnp
+bus	/man/3/usb
+bus	/man/8/ftl
+buslogic	/man/10/plan9.ini
+busy	/man/1/charon
+busy	/man/10/delay
+buts	/man/2/tkclient
+buts	/man/2/wmclient
+butt	/man/9/canvas
+button	/man/1/0intro
+button	/man/1/acme
+button	/man/1/brutus
+button	/man/1/charon
+button	/man/1/collab-clients
+button	/man/1/deb
+button	/man/1/emu
+button	/man/1/ftree
+button	/man/1/grid-monitor
+button	/man/1/grid-query
+button	/man/1/grid-session
+button	/man/1/keyboard
+button	/man/1/man
+button	/man/1/mash-tk
+button	/man/1/miniterm
+button	/man/1/tktester
+button	/man/1/wm-misc
+button	/man/1/wm-sh
+button	/man/2/dialog
+button	/man/2/draw-pointer
+button	/man/2/ir
+button	/man/2/popup
+button	/man/2/spree-cardlib
+button	/man/2/tk
+button	/man/2/tkclient
+button	/man/2/wmclient
+button	/man/3/cons
+button	/man/3/pointer
+button	/man/3/touch
+button	/man/4/acme
+button	/man/8/plumber
+button	/man/9/0intro
+button	/man/9/bind
+button	/man/9/button
+button	/man/9/canvas
+button	/man/9/checkbutton
+button	/man/9/choicebutton
+button	/man/9/entry
+button	/man/9/grab
+button	/man/9/listbox
+button	/man/9/menu
+button	/man/9/menubutton
+button	/man/9/options
+button	/man/9/radiobutton
+button	/man/9/scale
+button	/man/9/scrollbar
+button	/man/9/send
+button	/man/9/text
+button	/man/9/variable
+button's	/man/2/popup
+button's	/man/9/button
+button's	/man/9/checkbutton
+button's	/man/9/choicebutton
+button's	/man/9/radiobutton
+buttonpress	/man/9/bind
+buttonpress	/man/9/canvas
+buttonpress	/man/9/text
+buttonrelease	/man/9/bind
+buttons	/man/1/0intro
+buttons	/man/1/acme
+buttons	/man/1/charon
+buttons	/man/1/collab-clients
+buttons	/man/1/deb
+buttons	/man/1/ebook
+buttons	/man/1/grid-monitor
+buttons	/man/1/grid-session
+buttons	/man/1/mash-tk
+buttons	/man/1/miniterm
+buttons	/man/1/tktester
+buttons	/man/1/wm-misc
+buttons	/man/1/wm-sh
+buttons	/man/2/dialog
+buttons	/man/2/draw-pointer
+buttons	/man/2/ir
+buttons	/man/2/tkclient
+buttons	/man/2/wmclient
+buttons	/man/3/cons
+buttons	/man/3/pointer
+buttons	/man/3/touch
+buttons	/man/4/acme
+buttons	/man/9/bind
+buttons	/man/9/button
+buttons	/man/9/menu
+buttons	/man/9/options
+buttons	/man/9/text
+buttons	/man/9/variable
+bw	/man/8/prep
+bwrite	/man/10/dev
+by2v	/man/10/allocb
+by2v	/man/10/xalloc
+bye	/man/2/secstore
+bypass	/man/1/alphabet-fs
+bypass	/man/1/fs
+byte	/man/1/auplay
+byte	/man/1/cmp
+byte	/man/1/freq
+byte	/man/1/mdb
+byte	/man/1/sh-string
+byte	/man/1/vacget
+byte	/man/1/xd
+byte	/man/1/zeros
+byte	/man/10/a.out
+byte	/man/10/allocb
+byte	/man/10/c2l
+byte	/man/10/dev
+byte	/man/10/devattach
+byte	/man/10/dmainit
+byte	/man/10/inb
+byte	/man/10/memory
+byte	/man/10/parsecmd
+byte	/man/10/print
+byte	/man/10/rune
+byte	/man/10/strcat
+byte	/man/10/styx
+byte	/man/10/xalloc
+byte	/man/2/asn1
+byte	/man/2/bloomfilter
+byte	/man/2/bufio
+byte	/man/2/bufio-chanfill
+byte	/man/2/convcs
+byte	/man/2/crc
+byte	/man/2/dbm
+byte	/man/2/devpointer
+byte	/man/2/dhcpclient
+byte	/man/2/dial
+byte	/man/2/dis
+byte	/man/2/diskblocks
+byte	/man/2/disks
+byte	/man/2/draw-0intro
+byte	/man/2/draw-display
+byte	/man/2/draw-image
+byte	/man/2/encoding
+byte	/man/2/ether
+byte	/man/2/factotum
+byte	/man/2/filter
+byte	/man/2/format
+byte	/man/2/ida
+byte	/man/2/imagefile
+byte	/man/2/ip
+byte	/man/2/keyring-0intro
+byte	/man/2/keyring-auth
+byte	/man/2/keyring-crypt
+byte	/man/2/keyring-getmsg
+byte	/man/2/keyring-getstring
+byte	/man/2/keyring-ipint
+byte	/man/2/keyring-rc4
+byte	/man/2/keyring-sha1
+byte	/man/2/math-export
+byte	/man/2/msgio
+byte	/man/2/palmfile
+byte	/man/2/plumbmsg
+byte	/man/2/prof
+byte	/man/2/rfc822
+byte	/man/2/scsiio
+byte	/man/2/secstore
+byte	/man/2/security-random
+byte	/man/2/security-ssl
+byte	/man/2/sets
+byte	/man/2/sexprs
+byte	/man/2/spki
+byte	/man/2/spree
+byte	/man/2/spree-gather
+byte	/man/2/styx
+byte	/man/2/styxservers
+byte	/man/2/sys-0intro
+byte	/man/2/sys-byte2char
+byte	/man/2/sys-file2chan
+byte	/man/2/sys-print
+byte	/man/2/sys-read
+byte	/man/2/sys-utfbytes
+byte	/man/2/ubfa
+byte	/man/2/venti
+byte	/man/2/w3c-uris
+byte	/man/2/wmsrv
+byte	/man/3/arch
+byte	/man/3/boot
+byte	/man/3/cap
+byte	/man/3/cons
+byte	/man/3/dbg
+byte	/man/3/draw
+byte	/man/3/ds
+byte	/man/3/eia
+byte	/man/3/flash
+byte	/man/3/fpga
+byte	/man/3/ftl
+byte	/man/3/i2c
+byte	/man/3/ip
+byte	/man/3/kprof
+byte	/man/3/pbus
+byte	/man/3/pipe
+byte	/man/3/pointer
+byte	/man/3/tv
+byte	/man/3/usb
+byte	/man/3/vga
+byte	/man/3/vid
+byte	/man/4/acme
+byte	/man/4/memfs
+byte	/man/5/0intro
+byte	/man/5/stat
+byte	/man/6/audio
+byte	/man/6/colour
+byte	/man/6/dis
+byte	/man/6/font
+byte	/man/6/image
+byte	/man/6/keytext
+byte	/man/6/login
+byte	/man/6/sbl
+byte	/man/6/scancode
+byte	/man/6/sexprs
+byte	/man/6/ubfa
+byte	/man/6/utf
+byte	/man/7/db
+byte	/man/8/ftl
+byte	/man/8/rdbgsrv
+byte's	/man/6/image
+byte2char	/man/2/sys-byte2char
+byte2char	/man/2/sys-utfbytes
+byte2char	/man/6/utf
+bytes	/man/1/alphabet-fs
+bytes	/man/1/auplay
+bytes	/man/1/charon
+bytes	/man/1/cmp
+bytes	/man/1/dd
+bytes	/man/1/du
+bytes	/man/1/emu
+bytes	/man/1/fs
+bytes	/man/1/gettar
+bytes	/man/1/ls
+bytes	/man/1/mprof
+bytes	/man/1/read
+bytes	/man/1/secstore
+bytes	/man/1/sh-file2chan
+bytes	/man/1/sh-std
+bytes	/man/1/stream
+bytes	/man/1/sum
+bytes	/man/1/vacget
+bytes	/man/1/wc
+bytes	/man/1/wm-misc
+bytes	/man/1/xd
+bytes	/man/1/zeros
+bytes	/man/10/a.out
+bytes	/man/10/acid
+bytes	/man/10/allocb
+bytes	/man/10/c2l
+bytes	/man/10/dev
+bytes	/man/10/devattach
+bytes	/man/10/dmainit
+bytes	/man/10/dynld
+bytes	/man/10/error
+bytes	/man/10/getfields
+bytes	/man/10/inb
+bytes	/man/10/kbdputc
+bytes	/man/10/ksize
+bytes	/man/10/malloc
+bytes	/man/10/memory
+bytes	/man/10/odbc
+bytes	/man/10/plan9.ini
+bytes	/man/10/print
+bytes	/man/10/qio
+bytes	/man/10/readnum
+bytes	/man/10/rune
+bytes	/man/10/strcat
+bytes	/man/10/styx
+bytes	/man/10/styxserver
+bytes	/man/10/xalloc
+bytes	/man/2/asn1
+bytes	/man/2/bufio
+bytes	/man/2/convcs
+bytes	/man/2/crc
+bytes	/man/2/dbm
+bytes	/man/2/devpointer
+bytes	/man/2/dhcpclient
+bytes	/man/2/dis
+bytes	/man/2/diskblocks
+bytes	/man/2/disks
+bytes	/man/2/draw-image
+bytes	/man/2/drawmux
+bytes	/man/2/encoding
+bytes	/man/2/ether
+bytes	/man/2/factotum
+bytes	/man/2/filter
+bytes	/man/2/filter-slip
+bytes	/man/2/ida
+bytes	/man/2/imagefile
+bytes	/man/2/ip
+bytes	/man/2/keyring-0intro
+bytes	/man/2/keyring-crypt
+bytes	/man/2/keyring-getmsg
+bytes	/man/2/keyring-getstring
+bytes	/man/2/keyring-ipint
+bytes	/man/2/keyring-rc4
+bytes	/man/2/keyring-sha1
+bytes	/man/2/math-export
+bytes	/man/2/msgio
+bytes	/man/2/palmfile
+bytes	/man/2/plumbmsg
+bytes	/man/2/pop3
+bytes	/man/2/print
+bytes	/man/2/rfc822
+bytes	/man/2/scsiio
+bytes	/man/2/secstore
+bytes	/man/2/security-random
+bytes	/man/2/sets
+bytes	/man/2/sexprs
+bytes	/man/2/sh
+bytes	/man/2/spki
+bytes	/man/2/styx
+bytes	/man/2/sys-byte2char
+bytes	/man/2/sys-dirread
+bytes	/man/2/sys-file2chan
+bytes	/man/2/sys-iounit
+bytes	/man/2/sys-pipe
+bytes	/man/2/sys-print
+bytes	/man/2/sys-read
+bytes	/man/2/sys-seek
+bytes	/man/2/sys-stat
+bytes	/man/2/sys-utfbytes
+bytes	/man/2/ubfa
+bytes	/man/2/venti
+bytes	/man/3/boot
+bytes	/man/3/cons
+bytes	/man/3/dbg
+bytes	/man/3/draw
+bytes	/man/3/ds
+bytes	/man/3/dynld
+bytes	/man/3/eia
+bytes	/man/3/env
+bytes	/man/3/ether
+bytes	/man/3/flash
+bytes	/man/3/ftl
+bytes	/man/3/i2c
+bytes	/man/3/ip
+bytes	/man/3/kprof
+bytes	/man/3/logfs
+bytes	/man/3/mnt
+bytes	/man/3/pbus
+bytes	/man/3/pipe
+bytes	/man/3/prof
+bytes	/man/3/prog
+bytes	/man/3/sd
+bytes	/man/3/tinyfs
+bytes	/man/3/tls
+bytes	/man/3/tv
+bytes	/man/3/usb
+bytes	/man/3/vid
+bytes	/man/4/dbfs
+bytes	/man/4/factotum
+bytes	/man/4/keyfs
+bytes	/man/4/keysrv
+bytes	/man/4/kfs
+bytes	/man/4/logfile
+bytes	/man/4/spree
+bytes	/man/5/0intro
+bytes	/man/5/error
+bytes	/man/5/open
+bytes	/man/5/read
+bytes	/man/5/stat
+bytes	/man/5/version
+bytes	/man/6/colour
+bytes	/man/6/dis
+bytes	/man/6/font
+bytes	/man/6/image
+bytes	/man/6/keytext
+bytes	/man/6/plumbing
+bytes	/man/6/sbl
+bytes	/man/6/sexprs
+bytes	/man/6/ubfa
+bytes	/man/6/utf
+bytes	/man/7/db
+bytes	/man/8/collabsrv
+bytes	/man/8/ftl
+bytes	/man/8/mkfs
+bytes	/man/8/ping
+bytes	/man/8/prep
+bytes	/man/8/rstyxd
+bytes	/man/8/styxchat
+bytes2ptr	/man/2/devpointer
+bytes2set	/man/2/sets
+bytestoip	/man/2/keyring-ipint
+bytewise	/man/10/memory
+c's	/man/10/strcat
+c's	/man/2/daytime
+c.dfd	/man/2/dial
+c.dfd	/man/2/security-auth
+c.dir	/man/2/dial
+c2l	/man/10/c2l
+ca	/man/2/keyring-0intro
+ca	/man/2/security-0intro
+ca	/man/2/security-login
+ca	/man/6/auth
+ca	/man/6/login
+ca's	/man/2/keyring-0intro
+ca's	/man/2/security-0intro
+ca's	/man/6/auth
+ca's	/man/6/login
+cab	/man/10/odbc
+cache	/man/1/charon
+cache	/man/2/dbm
+cache	/man/2/sys-stat
+cache	/man/3/draw
+cache	/man/3/flash
+cache	/man/3/ip
+cache	/man/4/ftpfs
+cache	/man/8/httpd
+cache	/man/8/kfscmd
+cache	/man/8/prep
+cached	/man/2/attrdb
+cached	/man/2/dbm
+cached	/man/2/draw-font
+cached	/man/4/ftpfs
+cached	/man/8/ai2key
+cacheid	/man/3/draw
+caches	/man/3/boot
+caches	/man/3/dbg
+caches	/man/3/draw
+cachesize	/man/8/httpd
+cal	/man/1/cal
+cal	/man/1/calendar
+cal.b	/man/1/cal
+calc	/man/1/calc
+calc	/man/1/fc
+calc	/man/1/yacc
+calc.b	/man/1/calc
+calculate	/man/1/netkey
+calculate	/man/1/sum
+calculate	/man/10/seconds
+calculate	/man/2/crc
+calculate	/man/2/factotum
+calculate	/man/6/auth
+calculate	/man/6/font
+calculate	/man/8/touchcal
+calculated	/man/1/calc
+calculated	/man/1/m4
+calculated	/man/10/2c
+calculated	/man/2/disks
+calculated	/man/2/styx
+calculated	/man/3/touch
+calculates	/man/1/disdep
+calculates	/man/1/fc
+calculates	/man/1/math-misc
+calculates	/man/1/mprof
+calculates	/man/1/netkey
+calculates	/man/1/prof
+calculates	/man/2/crc
+calculating	/man/2/crc
+calculating	/man/2/popup
+calculation	/man/2/crc
+calculation	/man/2/draw-image
+calculation	/man/2/sets
+calculations	/man/3/draw
+calculator	/man/1/calc
+calculator	/man/1/fc
+calculator	/man/1/yacc
+calendar	/man/1/cal
+calendar	/man/1/calendar
+calendar	/man/4/dbfs
+calendar.b	/man/1/calendar
+calibrated	/man/3/touch
+calibration	/man/3/touch
+calibration	/man/8/touchcal
+california	/man/9/0intro
+california	/man/9/1copyright
+callback	/man/10/styxserver
+callee	/man/4/factotum
+caller	/man/10/dynld
+caller	/man/10/kproc
+caller	/man/10/lock
+caller	/man/10/parsecmd
+caller	/man/10/qio
+caller	/man/10/qlock
+caller	/man/10/sleep
+caller	/man/2/command
+caller	/man/2/complete
+caller	/man/2/dial
+caller	/man/2/factotum
+caller	/man/2/ir
+caller	/man/2/math-fp
+caller	/man/2/spree-allow
+caller	/man/2/styx
+caller	/man/2/styxconv
+caller	/man/2/styxflush
+caller	/man/2/styxpersist
+caller	/man/2/styxservers
+caller	/man/2/sys-dial
+caller	/man/2/timers
+caller	/man/4/factotum
+caller	/man/4/import
+caller	/man/8/register
+caller	/man/8/rstyxd
+caller	/man/8/signer
+caller	/man/8/svc
+caller's	/man/10/dynld
+caller's	/man/10/eve
+caller's	/man/10/kproc
+caller's	/man/8/signer
+callers	/man/10/allocb
+calling	/man/10/atoi
+calling	/man/10/conf
+calling	/man/10/devattach
+calling	/man/10/dynld
+calling	/man/10/error
+calling	/man/10/intrenable
+calling	/man/10/kbdputc
+calling	/man/10/kproc
+calling	/man/10/lock
+calling	/man/10/odbc
+calling	/man/2/0intro
+calling	/man/2/asn1
+calling	/man/2/debug
+calling	/man/2/factotum
+calling	/man/2/imagefile
+calling	/man/2/itslib
+calling	/man/2/keyring-crypt
+calling	/man/2/keyring-sha1
+calling	/man/2/msgio
+calling	/man/2/prefab-compound
+calling	/man/2/print
+calling	/man/2/pslib
+calling	/man/2/secstore
+calling	/man/2/security-0intro
+calling	/man/2/sets
+calling	/man/2/sh
+calling	/man/2/spree
+calling	/man/2/spree-cardlib
+calling	/man/2/styxservers
+calling	/man/2/sys-export
+calling	/man/2/sys-pctl
+calling	/man/2/sys-read
+calling	/man/2/timers
+calling	/man/2/xml
+calling	/man/3/ip
+calling	/man/5/open
+callkremvax	/man/2/dial
+callkremvax	/man/2/sys-dial
+calloc	/man/10/malloc
+calpha	/man/3/draw
+can't	/man/10/styxserver
+can't	/man/2/command
+can't	/man/2/dial
+can't	/man/2/ir
+can't	/man/9/text
+cancel	/man/1/mash-tk
+cancel	/man/3/dbg
+cancel	/man/3/mpeg
+cancel	/man/3/prog
+cancel	/man/3/usb
+cancel	/man/4/acme
+canceled	/man/3/tls
+cancelled	/man/5/flush
+cancelling	/man/10/qio
+cancels	/man/9/text
+cancreate	/man/2/styxservers
+candidates	/man/1/limbo
+canlock	/man/10/lock
+canon	/man/2/draw-rect
+canonical	/man/1/alphabet-main
+canonical	/man/1/keyboard
+canonical	/man/1/sh-alphabet
+canonical	/man/2/draw-rect
+canonical	/man/2/keyset
+canonical	/man/2/sexprs
+canonical	/man/2/spki
+canonical	/man/2/w3c-uris
+canonical	/man/6/dis
+canonical	/man/6/sexprs
+canonical	/man/9/types
+canonically	/man/9/types
+canopen	/man/2/styxservers
+canqlock	/man/10/qlock
+canread	/man/2/styxservers
+canremove	/man/2/styxservers
+canrlock	/man/10/qlock
+cansecstore	/man/2/secstore
+canvas	/man/1/collab-clients
+canvas	/man/1/tktester
+canvas	/man/2/tk
+canvas	/man/9/0intro
+canvas	/man/9/canvas
+canvas	/man/9/see
+canvas's	/man/9/canvas
+canvases	/man/9/canvas
+canvasx	/man/9/canvas
+canvasy	/man/9/canvas
+canwrite	/man/2/styxservers
+cap	/man/2/factotum
+cap	/man/2/newns
+cap	/man/3/cap
+cap	/man/3/indir
+cap	/man/9/canvas
+capabilities	/man/1/ftest
+capabilities	/man/2/styxpersist
+capabilities	/man/3/mpeg
+capabilities	/man/3/sd
+capabilities	/man/9/grid
+capability	/man/1/0intro
+capability	/man/2/factotum
+capability	/man/2/newns
+capability	/man/3/cap
+capability	/man/3/sd
+capable	/man/1/tiny
+capable	/man/10/9load
+capable	/man/10/plan9.ini
+capable	/man/2/format
+capable	/man/9/entry
+capacity	/man/3/tinyfs
+capbrightness	/man/3/tv
+capbw	/man/3/tv
+capcontrast	/man/3/tv
+caphash	/man/3/cap
+caphashfd	/man/3/cap
+caphue	/man/3/tv
+capital	/man/1/acme
+capital	/man/1/miniterm
+capital	/man/6/keyboard
+capping	/man/10/lock
+caps	/man/6/keyboard
+caps	/man/9/canvas
+capsaturation	/man/3/tv
+capstyle	/man/9/canvas
+caption	/man/1/cook
+capture	/man/3/ether
+capture	/man/3/tv
+captured	/man/3/tv
+captured	/man/9/menu
+capuse	/man/3/cap
+card	/man/10/9load
+card	/man/10/plan9.ini
+card	/man/2/spree-allow
+card	/man/2/spree-cardlib
+card	/man/2/spree-gather
+card	/man/3/i82365
+card	/man/3/plap
+card	/man/3/pnp
+card	/man/3/tv
+card	/man/4/spree
+card's	/man/2/spree-cardlib
+card's	/man/3/i82365
+card's	/man/3/pnp
+cardlib	/man/2/spree
+cardlib	/man/2/spree-allow
+cardlib	/man/2/spree-cardlib
+cardlib	/man/2/spree-gather
+cardlib.b	/man/2/spree-cardlib
+cardlib.m	/man/2/spree-cardlib
+cards	/man/10/plan9.ini
+cards	/man/2/spree-cardlib
+cards	/man/2/spree-gather
+cards	/man/3/plap
+cards	/man/3/pnp
+cards	/man/3/vga
+care	/man/1/calendar
+care	/man/1/ebook
+care	/man/1/mash-make
+care	/man/10/qio
+care	/man/2/alphabet-intro
+care	/man/2/command
+care	/man/2/draw-example
+care	/man/2/geodesy
+care	/man/2/keyset
+care	/man/2/sh
+care	/man/2/spree
+care	/man/2/styx
+care	/man/2/sys-stat
+care	/man/6/colour
+care	/man/6/image
+care	/man/8/httpd
+care	/man/9/listbox
+care	/man/9/menu
+careful	/man/9/grab
+carefully	/man/1/0intro
+carefully	/man/1/crypt
+carets	/man/1/mash
+carets	/man/1/sh
+carriage	/man/1/deb
+carriage	/man/1/mash
+carriage	/man/2/csv
+carriage	/man/6/json
+carriage	/man/6/keyboard
+carriage	/man/6/sexprs
+carriage	/man/6/translate
+carriage	/man/6/ubfa
+carrier	/man/3/ip
+carries	/man/1/fs
+carries	/man/2/spki
+carries	/man/2/styxservers
+carries	/man/5/walk
+carries	/man/9/pack
+carry	/man/10/devattach
+carry	/man/10/intrenable
+carry	/man/8/create
+carrying	/man/3/ether
+cas	/man/2/keyring-0intro
+cascade	/man/9/menu
+cascaded	/man/9/menu
+cascading	/man/1/mash-tk
+cascading	/man/2/w3c-css
+cascading	/man/9/menu
+cases	/man/1/0intro
+cases	/man/1/collab-clients
+cases	/man/1/dd
+cases	/man/1/mash
+cases	/man/1/read
+cases	/man/1/sh
+cases	/man/1/stream
+cases	/man/1/tiny
+cases	/man/1/wm-sh
+cases	/man/10/allocb
+cases	/man/10/c2l
+cases	/man/10/dev
+cases	/man/10/dmainit
+cases	/man/10/newchan
+cases	/man/10/plan9.ini
+cases	/man/10/readnum
+cases	/man/2/0intro
+cases	/man/2/dial
+cases	/man/2/dis
+cases	/man/2/draw-0intro
+cases	/man/2/draw-image
+cases	/man/2/filter-deflate
+cases	/man/2/rfc822
+cases	/man/2/security-auth
+cases	/man/2/sys-dirread
+cases	/man/2/w3c-xpointers
+cases	/man/3/cmd
+cases	/man/3/cons
+cases	/man/3/ssl
+cases	/man/5/0intro
+cases	/man/9/bind
+cases	/man/9/canvas
+cases	/man/9/grid
+cases	/man/9/menu
+cases	/man/9/pack
+casts	/man/10/acid
+cat	/man/1/alphabet-main
+cat	/man/1/cat
+cat	/man/1/cp
+cat	/man/1/dd
+cat	/man/1/gettar
+cat	/man/1/listen
+cat	/man/1/read
+cat	/man/1/sh-alphabet
+cat	/man/1/stream
+cat	/man/1/tee
+cat	/man/1/uuencode
+cat	/man/10/odbc
+cat	/man/10/plan9.ini
+cat	/man/2/dbm
+cat	/man/2/palmfile
+cat	/man/3/ds
+cat	/man/3/pnp
+cat	/man/3/sd
+cat	/man/4/9srvfs
+cat	/man/4/spree
+cat	/man/8/dhcp
+cat	/man/8/rdbgsrv
+cat.b	/man/1/cat
+catch	/man/1/sh-std
+catch	/man/2/0intro
+catches	/man/1/sh
+catches	/man/1/sh-std
+catches	/man/2/sh
+catching	/man/2/sh
+categories	/man/1/grid-query
+categories	/man/1/mux
+categories	/man/2/palmfile
+categories	/man/3/tls
+categories	/man/4/namespace
+category	/man/1/grid-query
+category	/man/10/conf
+category	/man/2/palmfile
+category	/man/3/tls
+catenate	/man/1/dd
+catenated	/man/1/acme
+catenation	/man/1/acme
+catenation	/man/1/units
+catenation	/man/3/draw
+catenation	/man/3/ds
+catv	/man/3/tv
+caught	/man/1/sh-std
+caught	/man/2/sh
+caused	/man/1/mk
+caused	/man/1/wm
+caused	/man/10/lock
+caused	/man/10/mk
+caused	/man/2/draw-image
+caused	/man/2/wait
+caused	/man/2/wmsrv
+caused	/man/4/iostats
+caused	/man/6/colour
+caused	/man/8/applylog
+causing	/man/1/charon
+causing	/man/10/kproc
+causing	/man/10/sleep
+causing	/man/2/cfg
+causing	/man/2/prefab-element
+causing	/man/3/ip
+causing	/man/4/registry
+causing	/man/8/collabsrv
+causing	/man/9/menubutton
+causing	/man/9/update
+caveat	/man/3/srv9
+caveats	/man/1/bind
+caveats	/man/10/error
+cavity	/man/2/spree-cardlib
+cavity	/man/9/grid
+cavity	/man/9/pack
+ca→user	/man/6/login
+cbc	/man/1/crypt
+cbc	/man/2/keyring-crypt
+cbc	/man/2/security-0intro
+cbc	/man/3/ssl
+cbc	/man/3/tls
+cblue	/man/3/draw
+cbrt	/man/1/calc
+cbrt	/man/2/math-elem
+cbs	/man/1/dd
+cc	/man/1/sendmail
+cc	/man/10/2c
+cc	/man/10/mk
+cc	/man/2/smtp
+cc's	/man/2/smtp
+ccir601	/man/3/tv
+cclose	/man/10/newchan
+cd	/man/1/acme
+cd	/man/1/cd
+cd	/man/1/mk
+cd	/man/1/mkdir
+cd	/man/1/nsbuild
+cd	/man/1/pwd
+cd	/man/1/wm-sh
+cd	/man/10/mk
+cd	/man/10/styxserver
+cd	/man/3/prog
+cd	/man/3/sd
+cd	/man/4/dossrv
+cd	/man/6/namespace
+cd	/man/7/cddb
+cd.b	/man/1/cd
+cddb	/man/7/cddb
+cddb.b	/man/7/cddb
+cdfile	/man/4/dossrv
+cdfppqrtw	/man/8/kfscmd
+cdir	/man/10/styxserver
+cdroms	/man/4/dossrv
+cds	/man/1/tr
+cds	/man/7/cddb
+ce	/man/10/srclist
+ceases	/man/9/canvas
+ceases	/man/9/text
+ceil	/man/2/bloomfilter
+ceil	/man/2/ida
+ceil	/man/2/math-fp
+ceiling	/man/1/calc
+cell	/man/1/tktester
+cell	/man/9/grid
+cellar	/man/10/odbc
+cellinfo	/man/9/grid
+cells	/man/3/draw
+cells	/man/6/colour
+cells	/man/9/grid
+center	/man/1/acme
+center	/man/3/draw
+center	/man/9/canvas
+center	/man/9/listbox
+center	/man/9/menu
+center	/man/9/options
+center	/man/9/pack
+center	/man/9/text
+center	/man/9/types
+centered	/man/2/draw-0intro
+centered	/man/2/draw-image
+centered	/man/3/draw
+centered	/man/9/canvas
+centered	/man/9/grid
+centered	/man/9/text
+centers	/man/9/options
+centers	/man/9/text
+centigrade	/man/1/units
+centimeter	/man/9/text
+centimetres	/man/9/types
+central	/man/1/sh
+central	/man/2/styxflush
+centralloop	/man/2/styxflush
+centre	/man/3/touch
+centre	/man/8/touchcal
+centred	/man/2/dialog
+centred	/man/2/print
+centred	/man/2/selectfile
+centreline	/man/9/scale
+century	/man/1/cal
+cerf405	/man/3/i2c
+cert	/man/2/keyring-0intro
+cert	/man/2/keyring-sha1
+cert	/man/2/spki
+cert	/man/6/auth
+cert	/man/8/ai2key
+cert.a	/man/2/spki
+cert.kh	/man/2/spki
+cert.n	/man/2/spki
+cert.o	/man/2/spki
+certainly	/man/10/c2l
+certainly	/man/10/kbdputc
+certainly	/man/10/qio
+certainly	/man/2/pop3
+certificate	/man/1/listen
+certificate	/man/1/passwd
+certificate	/man/1/rcmd
+certificate	/man/2/dial
+certificate	/man/2/keyring-0intro
+certificate	/man/2/keyring-certtostr
+certificate	/man/2/keyring-sha1
+certificate	/man/2/keyset
+certificate	/man/2/registries
+certificate	/man/2/security-0intro
+certificate	/man/2/security-login
+certificate	/man/2/sexprs
+certificate	/man/2/spki
+certificate	/man/2/sys-dial
+certificate	/man/4/factotum
+certificate	/man/4/keyfs
+certificate	/man/4/keysrv
+certificate	/man/6/auth
+certificate	/man/6/keys
+certificate	/man/6/keytext
+certificate	/man/6/login
+certificate	/man/7/db
+certificate	/man/8/ai2key
+certificate	/man/8/changelogin
+certificate	/man/8/createsignerkey
+certificate	/man/8/getauthinfo
+certificate	/man/8/logind
+certificate	/man/8/register
+certificate	/man/8/signer
+certificate's	/man/2/spki
+certificate's	/man/6/keytext
+certificate's	/man/8/logind
+certificates	/man/1/charon
+certificates	/man/2/keyring-0intro
+certificates	/man/2/keyring-certtostr
+certificates	/man/2/security-0intro
+certificates	/man/2/spki
+certificates	/man/2/spki-verifier
+certificates	/man/3/tinyfs
+certificates	/man/4/factotum
+certificates	/man/4/namespace
+certificates	/man/6/keytext
+certificates	/man/8/createsignerkey
+certificates	/man/8/getauthinfo
+certificates	/man/8/logind
+certificates	/man/8/register
+certificates	/man/8/signer
+certificates	/man/8/svc
+certification	/man/2/keyring-0intro
+certified	/man/2/keyset
+certified	/man/2/registries
+certifying	/man/2/keyring-0intro
+certifying	/man/2/keyring-auth
+certifying	/man/2/keyring-certtostr
+certifying	/man/2/security-0intro
+certifying	/man/2/security-login
+certifying	/man/6/auth
+certifying	/man/6/keys
+certifying	/man/6/login
+certifying	/man/8/getauthinfo
+certtostr	/man/2/keyring-0intro
+certtostr	/man/2/keyring-certtostr
+certtostr	/man/2/keyset
+certtostr	/man/3/sign
+certtostr	/man/4/factotum
+certtostr	/man/6/keytext
+certu	/man/2/keyring-auth
+certu0	/man/2/keyring-auth
+certu0	/man/6/auth
+certu1	/man/2/keyring-auth
+certu1	/man/6/auth
+certx	/man/6/auth
+cf	/man/10/dev
+cf	/man/2/plumbmsg
+cfd	/man/2/dial
+cfd	/man/2/security-ssl
+cfd	/man/2/sys-dial
+cfg	/man/2/attrdb
+cfg	/man/2/cfg
+cfg	/man/2/convcs
+cfg	/man/6/attrdb
+cfg.b	/man/2/cfg
+cfg.m	/man/2/cfg
+cfile	/man/1/itest
+cfile	/man/10/styxserver
+cflag	/man/1/itest
+cflag	/man/1/sh-arg
+cflag	/man/2/arg
+cflags	/man/10/mk
+cfs	/man/8/prep
+cg	/man/1/mash-make
+cga	/man/10/plan9.ini
+cget	/man/2/popup
+cget	/man/2/tk
+cget	/man/9/button
+cget	/man/9/canvas
+cget	/man/9/checkbutton
+cget	/man/9/choicebutton
+cget	/man/9/entry
+cget	/man/9/frame
+cget	/man/9/label
+cget	/man/9/listbox
+cget	/man/9/menu
+cget	/man/9/menubutton
+cget	/man/9/panel
+cget	/man/9/radiobutton
+cget	/man/9/scale
+cget	/man/9/scrollbar
+cget	/man/9/text
+cgi	/man/8/httpd
+cgi.m	/man/8/httpd
+cgreen	/man/3/draw
+cgrey	/man/3/draw
+ch	/man/2/volume
+ch	/man/2/xml
+chain	/man/2/disks
+chain	/man/2/security-0intro
+chained	/man/10/allocb
+chained	/man/8/prep
+chaining	/man/1/crypt
+chaining	/man/2/keyring-crypt
+chaining	/man/3/ssl
+chains	/man/8/prep
+chal	/man/2/factotum
+challenge	/man/1/netkey
+challenge	/man/2/factotum
+chan	/man/1/plumb
+chan	/man/1/sh-file2chan
+chan	/man/1/sh-tk
+chan	/man/1/wm
+chan	/man/1/wm-sh
+chan	/man/10/dev
+chan	/man/10/devattach
+chan	/man/10/dmainit
+chan	/man/10/newchan
+chan	/man/2/0intro
+chan	/man/2/command
+chan	/man/2/devpointer
+chan	/man/2/dhcpclient
+chan	/man/2/dividers
+chan	/man/2/draw-context
+chan	/man/2/filter
+chan	/man/2/filter-deflate
+chan	/man/2/filter-slip
+chan	/man/2/fsproto
+chan	/man/2/ir
+chan	/man/2/lists
+chan	/man/2/lock
+chan	/man/2/mpeg
+chan	/man/2/plumbmsg
+chan	/man/2/popup
+chan	/man/2/prefab-compound
+chan	/man/2/styxconv
+chan	/man/2/styxflush
+chan	/man/2/styxpersist
+chan	/man/2/styxservers
+chan	/man/2/styxservers-nametree
+chan	/man/2/sys-export
+chan	/man/2/sys-file2chan
+chan	/man/2/tabs
+chan	/man/2/timers
+chan	/man/2/tk
+chan	/man/2/tkclient
+chan	/man/2/volume
+chan	/man/2/wait
+chan	/man/2/wmclient
+chan	/man/2/wmlib
+chan	/man/2/wmsrv
+chan	/man/2/xml
+chan	/man/3/draw
+chan	/man/3/pbus
+chan	/man/3/plap
+chan	/man/3/root
+chan	/man/3/snarf
+chan	/man/3/srv
+chan	/man/3/vga
+chan	/man/4/acme
+chan	/man/4/grid-cpu
+chan	/man/4/kfs
+chan	/man/4/namespace
+chan	/man/4/palmsrv
+chan	/man/6/image
+chan	/man/6/sbl
+chan	/man/7/db
+chan	/man/8/plumber
+chan	/man/9/send
+chan.c	/man/10/newchan
+chan.c	/man/10/ref
+chan.flag	/man/10/devattach
+chance	/man/2/draw-context
+chance	/man/2/sh
+chandesc	/man/2/imagefile
+chandn	/man/2/ir
+chanfill	/man/2/bufio
+chanfill	/man/2/bufio-chanfill
+chanfill	/man/2/sys-self
+chanfree	/man/10/newchan
+changebutton	/man/2/popup
+changecipher	/man/3/tls
+changecom	/man/1/m4
+changelogin	/man/1/passwd
+changelogin	/man/2/security-0intro
+changelogin	/man/2/security-login
+changelogin	/man/4/keyfs
+changelogin	/man/6/keys
+changelogin	/man/8/changelogin
+changelogin	/man/8/getauthinfo
+changelogin	/man/8/logind
+changelogin	/man/8/svc
+changelogin.b	/man/8/changelogin
+changequote	/man/1/m4
+changetime	/man/2/scsiio
+channel	/man/1/auplay
+channel	/man/1/emu
+channel	/man/1/mux
+channel	/man/1/plumb
+channel	/man/1/sh-tk
+channel	/man/1/tkcmd
+channel	/man/10/dev
+channel	/man/10/devattach
+channel	/man/10/dmainit
+channel	/man/10/newchan
+channel	/man/10/plan9.ini
+channel	/man/10/styxserver
+channel	/man/2/0intro
+channel	/man/2/alphabet-intro
+channel	/man/2/bufio-chanfill
+channel	/man/2/command
+channel	/man/2/devpointer
+channel	/man/2/dhcpclient
+channel	/man/2/dividers
+channel	/man/2/draw-0intro
+channel	/man/2/draw-context
+channel	/man/2/draw-display
+channel	/man/2/draw-image
+channel	/man/2/filter
+channel	/man/2/filter-slip
+channel	/man/2/fsproto
+channel	/man/2/imagefile
+channel	/man/2/ir
+channel	/man/2/keyring-0intro
+channel	/man/2/mpeg
+channel	/man/2/plumbmsg
+channel	/man/2/popup
+channel	/man/2/prefab-compound
+channel	/man/2/security-login
+channel	/man/2/styxconv
+channel	/man/2/styxflush
+channel	/man/2/styxpersist
+channel	/man/2/styxservers
+channel	/man/2/styxservers-nametree
+channel	/man/2/sys-file2chan
+channel	/man/2/sys-pipe
+channel	/man/2/tabs
+channel	/man/2/timers
+channel	/man/2/tk
+channel	/man/2/tkclient
+channel	/man/2/volume
+channel	/man/2/wait
+channel	/man/2/wmclient
+channel	/man/2/wmlib
+channel	/man/2/wmsrv
+channel	/man/2/xml
+channel	/man/3/0intro
+channel	/man/3/draw
+channel	/man/3/mnt
+channel	/man/3/plap
+channel	/man/3/prog
+channel	/man/3/tls
+channel	/man/3/tv
+channel	/man/4/factotum
+channel	/man/4/kfs
+channel	/man/6/audio
+channel	/man/6/colour
+channel	/man/6/image
+channel	/man/6/login
+channel	/man/6/ubfa
+channel	/man/9/image
+channel	/man/9/send
+channel	/man/9/variable
+channel's	/man/10/newchan
+channels	/man/1/auplay
+channels	/man/1/math-misc
+channels	/man/1/sh-tk
+channels	/man/10/dmainit
+channels	/man/10/newchan
+channels	/man/2/alphabet-intro
+channels	/man/2/draw-0intro
+channels	/man/2/draw-context
+channels	/man/2/draw-image
+channels	/man/2/imagefile
+channels	/man/2/ir
+channels	/man/2/popup
+channels	/man/2/sexprs
+channels	/man/2/spki-verifier
+channels	/man/2/sys-file2chan
+channels	/man/2/sys-pipe
+channels	/man/2/tk
+channels	/man/2/wmsrv
+channels	/man/3/audio
+channels	/man/3/draw
+channels	/man/3/srv
+channels	/man/3/tls
+channels	/man/3/tv
+channels	/man/6/audio
+channels	/man/6/colour
+channels	/man/6/image
+channels	/man/9/send
+chans	/man/2/draw-0intro
+chans	/man/2/draw-display
+chans	/man/2/draw-image
+chans	/man/2/imagefile
+chans	/man/3/audio
+chans	/man/6/audio
+chans.depth	/man/2/draw-image
+chans.mk	/man/2/draw-display
+chanup	/man/2/ir
+chap	/man/3/ip
+chapter	/man/6/man
+char	/man/1/unicode
+char	/man/10/a.out
+char	/man/10/acid
+char	/man/10/allocb
+char	/man/10/ar
+char	/man/10/atoi
+char	/man/10/c2l
+char	/man/10/dev
+char	/man/10/devattach
+char	/man/10/dynld
+char	/man/10/error
+char	/man/10/eve
+char	/man/10/getfields
+char	/man/10/intrenable
+char	/man/10/kproc
+char	/man/10/newchan
+char	/man/10/panic
+char	/man/10/parsecmd
+char	/man/10/print
+char	/man/10/qio
+char	/man/10/readnum
+char	/man/10/rune
+char	/man/10/strcat
+char	/man/10/styx
+char	/man/10/styxserver
+char	/man/3/dbg
+char	/man/6/json
+char	/man/6/sbl
+char	/man/6/sexprs
+char	/man/9/text
+char2byte	/man/2/sys-byte2char
+character	/man/1/acme
+character	/man/1/ar
+character	/man/1/bind
+character	/man/1/brutus
+character	/man/1/charon
+character	/man/1/cook
+character	/man/1/dd
+character	/man/1/fc
+character	/man/1/freq
+character	/man/1/fs
+character	/man/1/idea
+character	/man/1/keyboard
+character	/man/1/look
+character	/man/1/ls
+character	/man/1/m4
+character	/man/1/mash
+character	/man/1/mc
+character	/man/1/mdb
+character	/man/1/miniterm
+character	/man/1/mk
+character	/man/1/sh
+character	/man/1/sh-arg
+character	/man/1/sh-regex
+character	/man/1/sh-std
+character	/man/1/sh-tk
+character	/man/1/tcs
+character	/man/1/tiny
+character	/man/1/tr
+character	/man/1/unicode
+character	/man/1/wm
+character	/man/1/wm-misc
+character	/man/1/wm-sh
+character	/man/1/xd
+character	/man/10/2a
+character	/man/10/2c
+character	/man/10/2l
+character	/man/10/9load
+character	/man/10/acid
+character	/man/10/atoi
+character	/man/10/dev
+character	/man/10/devattach
+character	/man/10/iar
+character	/man/10/kbdputc
+character	/man/10/master
+character	/man/10/mk
+character	/man/10/odbc
+character	/man/10/print
+character	/man/2/alphabet-intro
+character	/man/2/asn1
+character	/man/2/bufio
+character	/man/2/cfg
+character	/man/2/complete
+character	/man/2/convcs
+character	/man/2/csv
+character	/man/2/debug
+character	/man/2/draw-0intro
+character	/man/2/draw-font
+character	/man/2/draw-image
+character	/man/2/encoding
+character	/man/2/filepat
+character	/man/2/filter-deflate
+character	/man/2/keyring-getmsg
+character	/man/2/msgio
+character	/man/2/palmfile
+character	/man/2/prof
+character	/man/2/regex
+character	/man/2/rfc822
+character	/man/2/sexprs
+character	/man/2/spree-cardlib
+character	/man/2/string
+character	/man/2/sys-0intro
+character	/man/2/sys-byte2char
+character	/man/2/sys-print
+character	/man/2/sys-tokenize
+character	/man/2/tftp
+character	/man/2/ubfa
+character	/man/2/w3c-css
+character	/man/2/w3c-uris
+character	/man/2/w3c-xpointers
+character	/man/2/wmsrv
+character	/man/3/0intro
+character	/man/3/cons
+character	/man/3/dbg
+character	/man/3/draw
+character	/man/3/eia
+character	/man/3/gpio
+character	/man/3/indir
+character	/man/3/ip
+character	/man/3/pointer
+character	/man/3/prog
+character	/man/4/acme
+character	/man/4/registry
+character	/man/4/spree
+character	/man/4/trfs
+character	/man/5/0intro
+character	/man/6/attrdb
+character	/man/6/font
+character	/man/6/image
+character	/man/6/keyboard
+character	/man/6/namespace
+character	/man/6/regexp
+character	/man/6/sbl
+character	/man/6/sexprs
+character	/man/6/ubfa
+character	/man/6/utf
+character	/man/8/collabsrv
+character	/man/9/bind
+character	/man/9/canvas
+character	/man/9/entry
+character	/man/9/listbox
+character	/man/9/menu
+character	/man/9/options
+character	/man/9/text
+character	/man/9/types
+character's	/man/9/text
+characteristics	/man/1/auplay
+characteristics	/man/1/listen
+characteristics	/man/2/sys-stat
+characteristics	/man/3/audio
+characteristics	/man/3/flash
+characteristics	/man/3/ip
+characteristics	/man/6/audio
+characteristics	/man/6/colour
+characteristics	/man/7/cddb
+characteristics	/man/7/db
+characteristics	/man/8/ftl
+characterized	/man/9/canvas
+characters	/man/1/acme
+characters	/man/1/alphabet-fs
+characters	/man/1/ar
+characters	/man/1/bind
+characters	/man/1/charon
+characters	/man/1/dd
+characters	/man/1/deb
+characters	/man/1/fs
+characters	/man/1/keyboard
+characters	/man/1/ls
+characters	/man/1/m4
+characters	/man/1/man
+characters	/man/1/mash
+characters	/man/1/mash-tk
+characters	/man/1/mdb
+characters	/man/1/miniterm
+characters	/man/1/mk
+characters	/man/1/passwd
+characters	/man/1/sh
+characters	/man/1/sh-arg
+characters	/man/1/sh-expr
+characters	/man/1/sh-regex
+characters	/man/1/sh-std
+characters	/man/1/sh-string
+characters	/man/1/sort
+characters	/man/1/stack
+characters	/man/1/strings
+characters	/man/1/tail
+characters	/man/1/tiny
+characters	/man/1/tktester
+characters	/man/1/tr
+characters	/man/1/tsort
+characters	/man/1/unicode
+characters	/man/1/wc
+characters	/man/1/wm-misc
+characters	/man/1/xd
+characters	/man/10/2c
+characters	/man/10/a.out
+characters	/man/10/atoi
+characters	/man/10/devattach
+characters	/man/10/getfields
+characters	/man/10/iar
+characters	/man/10/kbdputc
+characters	/man/10/master
+characters	/man/10/mk
+characters	/man/10/print
+characters	/man/2/alphabet-intro
+characters	/man/2/arg
+characters	/man/2/bufio
+characters	/man/2/cfg
+characters	/man/2/convcs
+characters	/man/2/draw-context
+characters	/man/2/draw-example
+characters	/man/2/draw-font
+characters	/man/2/draw-image
+characters	/man/2/encoding
+characters	/man/2/factotum
+characters	/man/2/filepat
+characters	/man/2/filter-deflate
+characters	/man/2/filter-slip
+characters	/man/2/json
+characters	/man/2/palmfile
+characters	/man/2/rfc822
+characters	/man/2/sexprs
+characters	/man/2/sh
+characters	/man/2/spree
+characters	/man/2/string
+characters	/man/2/stringinttab
+characters	/man/2/sys-0intro
+characters	/man/2/sys-byte2char
+characters	/man/2/sys-print
+characters	/man/2/sys-tokenize
+characters	/man/2/sys-utfbytes
+characters	/man/2/w3c-css
+characters	/man/2/w3c-uris
+characters	/man/3/0intro
+characters	/man/3/cap
+characters	/man/3/cons
+characters	/man/3/draw
+characters	/man/3/fs
+characters	/man/3/ip
+characters	/man/3/prog
+characters	/man/4/acme
+characters	/man/4/spree
+characters	/man/5/0intro
+characters	/man/5/version
+characters	/man/6/attrdb
+characters	/man/6/font
+characters	/man/6/image
+characters	/man/6/json
+characters	/man/6/keyboard
+characters	/man/6/namespace
+characters	/man/6/plumbing
+characters	/man/6/regexp
+characters	/man/6/sbl
+characters	/man/6/sexprs
+characters	/man/6/ubfa
+characters	/man/6/users
+characters	/man/6/utf
+characters	/man/8/changelogin
+characters	/man/9/0intro
+characters	/man/9/bind
+characters	/man/9/button
+characters	/man/9/canvas
+characters	/man/9/checkbutton
+characters	/man/9/entry
+characters	/man/9/grid
+characters	/man/9/label
+characters	/man/9/listbox
+characters	/man/9/menubutton
+characters	/man/9/radiobutton
+characters	/man/9/scrollbar
+characters	/man/9/text
+characters	/man/9/types
+charclass	/man/6/regexp
+chardonnay	/man/10/odbc
+charge	/man/1/miniterm
+charge	/man/1/units
+charon	/man/1/charon
+charon	/man/1/miniterm
+charon	/man/1/webgrab
+charon	/man/1/wm-misc
+charon	/man/2/w3c-uris
+charon.cfg	/man/1/charon
+chars	/man/9/text
+charset	/man/2/convcs
+charset	/man/2/w3c-css
+charsets	/man/1/tcs
+charsets	/man/2/convcs
+charstod	/man/10/atoi
+charstod.c	/man/10/atoi
+chart	/man/1/collab-clients
+chartorune	/man/10/rune
+charval	/man/1/wm
+chassis	/man/3/pbus
+chassis	/man/3/plap
+chat	/man/1/collab
+chat	/man/1/collab-clients
+chat	/man/2/spree
+chat	/man/4/spree
+chat	/man/8/collabsrv
+chat.b	/man/1/collab-clients
+chatroom	/man/1/collab-clients
+chatsrv	/man/8/collabsrv
+chatter	/man/4/ftpfs
+chatty	/man/1/webgrab
+chdir	/man/1/cd
+chdir	/man/10/newchan
+chdir	/man/2/newns
+chdir	/man/2/sys-chdir
+chdir	/man/2/sys-pctl
+chdir	/man/2/workdir
+chdir	/man/5/walk
+check	/man/1/ftest
+check	/man/1/grid-session
+check	/man/1/kill
+check	/man/1/passwd
+check	/man/10/0intro
+check	/man/10/dev
+check	/man/10/devattach
+check	/man/10/eve
+check	/man/10/memory
+check	/man/10/newchan
+check	/man/10/plan9.ini
+check	/man/10/sleep
+check	/man/10/strcat
+check	/man/10/styxserver
+check	/man/2/crc
+check	/man/2/format
+check	/man/2/keyring-crypt
+check	/man/2/rfc822
+check	/man/2/security-0intro
+check	/man/2/styxservers
+check	/man/2/styxservers-nametree
+check	/man/2/sys-stat
+check	/man/2/sys-utfbytes
+check	/man/2/w3c-xpointers
+check	/man/3/dynld
+check	/man/3/ip
+check	/man/4/keysrv
+check	/man/5/open
+check	/man/6/auth
+check	/man/6/colour
+check	/man/6/keys
+check	/man/8/cs
+check	/man/8/kfscmd
+check	/man/8/logind
+check	/man/8/prep
+check	/man/8/signer
+check	/man/9/menu
+check	/man/9/radiobutton
+checkb	/man/10/allocb
+checkbox	/man/1/tktester
+checkbutton	/man/9/0intro
+checkbutton	/man/9/button
+checkbutton	/man/9/checkbutton
+checkbutton	/man/9/choicebutton
+checkbutton	/man/9/menu
+checkbutton	/man/9/radiobutton
+checkbutton	/man/9/variable
+checkbutton's	/man/9/checkbutton
+checkbuttons	/man/9/checkbutton
+checked	/man/10/devattach
+checked	/man/2/filter-deflate
+checked	/man/2/spki
+checked	/man/4/kfs
+checked	/man/5/open
+checked	/man/8/changelogin
+checked	/man/8/kfscmd
+checking	/man/1/acme
+checking	/man/1/alphabet-main
+checking	/man/1/ar
+checking	/man/1/ftest
+checking	/man/1/limbo
+checking	/man/1/listen
+checking	/man/1/secstore
+checking	/man/1/sh-alphabet
+checking	/man/10/2c
+checking	/man/10/allocb
+checking	/man/10/error
+checking	/man/10/iar
+checking	/man/10/styxserver
+checking	/man/2/format
+checking	/man/2/spki
+checking	/man/2/spree
+checking	/man/2/styx
+checking	/man/2/styxservers
+checking	/man/2/sys-stat
+checking	/man/3/dbg
+checking	/man/3/fs
+checking	/man/3/tinyfs
+checking	/man/4/kfs
+checking	/man/4/vacfs
+checking	/man/6/dis
+checking	/man/6/keytext
+checking	/man/8/kfscmd
+checks	/man/1/charon
+checks	/man/1/ftest
+checks	/man/1/passwd
+checks	/man/10/newchan
+checks	/man/10/styx
+checks	/man/10/styxserver
+checks	/man/2/ida
+checks	/man/2/math-fp
+checks	/man/2/spki-verifier
+checks	/man/2/styxservers
+checks	/man/2/sys-print
+checks	/man/2/w3c-xpointers
+checks	/man/3/dynld
+checks	/man/3/fs
+checks	/man/4/factotum
+checks	/man/6/login
+checks	/man/8/prep
+checks	/man/8/signer
+checks	/man/9/text
+checksig	/man/2/spki
+checksum	/man/1/du
+checksum	/man/1/gettar
+checksum	/man/1/sum
+checksum	/man/2/filter-deflate
+checksum	/man/3/ip
+checksum	/man/3/tinyfs
+checksum	/man/8/applylog
+chess	/man/6/keyboard
+chgrp	/man/1/chgrp
+chgrp	/man/1/chmod
+chgrp.b	/man/1/chgrp
+chicanery	/man/2/sys-0intro
+child	/man/10/styxserver
+child	/man/2/command
+child	/man/2/sh
+child	/man/2/spree
+child	/man/2/w3c-xpointers
+child	/man/2/wait
+child	/man/3/prog
+child	/man/9/canvas
+child	/man/9/menu
+child	/man/9/menubutton
+child	/man/9/text
+child's	/man/2/command
+chip	/man/1/avr
+chip	/man/10/plan9.ini
+chip	/man/3/i82365
+chip	/man/3/vga
+chip	/man/3/vid
+chips	/man/10/plan9.ini
+chips	/man/3/rtc
+chips	/man/3/tinyfs
+chips	/man/3/vga
+chips	/man/8/ftl
+chipset	/man/10/plan9.ini
+chkmnt	/man/10/newchan
+chmod	/man/1/alphabet-fs
+chmod	/man/1/chgrp
+chmod	/man/1/chmod
+chmod	/man/1/fs
+chmod	/man/1/mkdir
+chmod	/man/1/touch
+chmod	/man/3/srv
+chmod	/man/6/keys
+chmod	/man/6/proto
+chmod.b	/man/1/chmod
+chocolate	/man/2/regex
+choice	/man/1/collab-clients
+choice	/man/1/ftree
+choice	/man/1/mash-tk
+choice	/man/10/qlock
+choice	/man/10/styxserver
+choice	/man/2/popup
+choice	/man/9/choicebutton
+choicebutton	/man/2/popup
+choicebutton	/man/9/button
+choicebutton	/man/9/checkbutton
+choicebutton	/man/9/choicebutton
+choicebutton	/man/9/radiobutton
+choicebutton	/man/9/variable
+choices	/man/10/9load
+choices	/man/2/w3c-xpointers
+choices	/man/3/gpio
+choices	/man/3/mpeg
+choices	/man/8/prep
+choices	/man/9/choicebutton
+choose	/man/1/cprof
+choose	/man/1/wm
+choose	/man/10/styxserver
+choose	/man/2/dialog
+choose	/man/2/plumbmsg
+choose	/man/2/prefab-element
+choose	/man/2/tkclient
+choose	/man/2/wmclient
+choose	/man/5/0intro
+choose	/man/8/rstyxd
+choose	/man/9/choicebutton
+chooses	/man/10/9load
+chooses	/man/2/sys-bind
+chooses	/man/2/sys-dup
+chooses	/man/2/tkclient
+chooses	/man/2/wmclient
+chooses	/man/9/pack
+chooses	/man/9/panel
+chooses	/man/9/text
+choosing	/man/1/miniterm
+choosing	/man/1/uuencode
+choosing	/man/2/security-0intro
+chord	/man/1/acme
+chord	/man/9/canvas
+chorded	/man/4/acme
+chording	/man/1/brutus
+chording	/man/1/wm-sh
+chords	/man/1/0intro
+chords	/man/1/acme
+chosen	/man/1/0intro
+chosen	/man/1/alphabet-fs
+chosen	/man/1/filename
+chosen	/man/1/fortune
+chosen	/man/1/fs
+chosen	/man/1/wm-misc
+chosen	/man/10/conf
+chosen	/man/2/keyring-0intro
+chosen	/man/2/keyring-sha1
+chosen	/man/2/palmfile
+chosen	/man/2/security-auth
+chosen	/man/2/spree-cardlib
+chosen	/man/3/pnp
+chosen	/man/4/acme
+chosen	/man/4/factotum
+chosen	/man/5/0intro
+chosen	/man/6/colour
+chosen	/man/6/font
+chosen	/man/6/login
+chosen	/man/8/prep
+chosen	/man/9/grid
+chosen	/man/9/scrollbar
+chosen	/man/9/text
+chr	/man/10/rune
+christian	/man/1/cal
+chroma	/man/2/mpeg
+chromakey	/man/3/tv
+chs	/man/10/9load
+chssrc	/man/2/disks
+chstext	/man/2/disks
+chunk	/man/1/xd
+chunk	/man/2/readdir
+chunk	/man/3/boot
+chunk	/man/3/cons
+chunks	/man/1/stream
+chunks	/man/1/xd
+cidr	/man/2/ip
+cignore	/man/3/draw
+cipher	/man/1/idea
+cipher	/man/2/keyring-crypt
+cipher	/man/3/ssl
+ciphertext	/man/2/security-0intro
+circle	/man/1/ftree
+circle	/man/2/spree-cardlib
+circles	/man/2/security-0intro
+circles	/man/9/canvas
+circuit	/man/10/devattach
+circular	/man/3/ip
+circular	/man/4/logfile
+circular	/man/9/canvas
+circularly	/man/9/canvas
+circumference	/man/1/units
+circumstances	/man/1/diff
+circumstances	/man/1/mash
+circumstances	/man/1/sh
+circumstances	/man/10/styxserver
+circumvent	/man/10/dmainit
+cirrus	/man/10/5cv
+cited	/man/6/colour
+cl	/man/1/keyboard
+cl	/man/1/sh-string
+cl	/man/2/string
+claim	/man/2/spki-verifier
+claimed	/man/2/keyring-rc4
+claims	/man/5/stat
+claims	/man/9/menubutton
+clash	/man/10/c2l
+clash	/man/10/styx
+clashes	/man/1/fc
+clashing	/man/1/sh-mload
+class	/man/1/acme
+class	/man/1/mash
+class	/man/1/sh
+class	/man/1/sh-string
+class	/man/2/0intro
+class	/man/2/asn1
+class	/man/2/ip
+class	/man/2/rfc822
+class	/man/2/string
+class	/man/2/w3c-css
+class	/man/3/arch
+class	/man/3/ip
+class	/man/3/pnp
+class	/man/6/regexp
+class	/man/9/menubutton
+class	/man/9/options
+class	/man/9/text
+classes	/man/1/charon
+classes	/man/1/sh
+classes	/man/2/0intro
+classes	/man/2/asn1
+classes	/man/2/palmfile
+classes	/man/2/string
+classical	/man/2/math-0intro
+classified	/man/9/1copyright
+classless	/man/2/ip
+classmask	/man/2/ip
+clause	/man/1/limbo
+clause	/man/1/mk
+clause	/man/10/mk
+clause	/man/2/exception
+clause	/man/6/sbl
+clause	/man/6/translate
+clause	/man/9/1copyright
+clavier	/man/1/miniterm
+clean	/man/1/0intro
+clean	/man/1/cleanname
+clean	/man/1/sh-alphabet
+clean	/man/10/error
+clean	/man/2/prof
+clean	/man/4/acme
+cleaner	/man/2/names
+cleanname	/man/1/basename
+cleanname	/man/1/cleanname
+cleanname	/man/2/names
+cleanname.b	/man/1/cleanname
+cleanup	/man/2/styxflush
+clear	/man/1/crypt
+clear	/man/1/grid-monitor
+clear	/man/1/mash-make
+clear	/man/1/mash-tk
+clear	/man/1/secstore
+clear	/man/1/sh-alphabet
+clear	/man/1/sh-std
+clear	/man/1/wm-misc
+clear	/man/1/wm-sh
+clear	/man/10/plan9.ini
+clear	/man/2/asn1
+clear	/man/2/dis
+clear	/man/2/draw-image
+clear	/man/2/security-auth
+clear	/man/3/eia
+clear	/man/3/ftl
+clear	/man/3/ip
+clear	/man/3/prog
+clear	/man/3/ssl
+clear	/man/3/tls
+clear	/man/3/usb
+clear	/man/4/factotum
+clear	/man/4/keysrv
+clear	/man/6/login
+clear	/man/8/kfscmd
+clear	/man/8/signer
+clear	/man/9/canvas
+clear	/man/9/entry
+clear	/man/9/listbox
+clear	/man/9/text
+cleared	/man/1/wm-misc
+cleared	/man/1/wm-sh
+cleared	/man/2/draw-display
+cleared	/man/2/spree-cardlib
+cleared	/man/2/spree-objstore
+cleared	/man/9/entry
+clearly	/man/1/tktester
+clearly	/man/2/math-fp
+clearly	/man/9/1copyright
+clears	/man/1/mprof
+clears	/man/1/sh-alphabet
+clears	/man/10/malloc
+clears	/man/2/math-fp
+clears	/man/2/secstore
+clears	/man/9/entry
+clears	/man/9/text
+cleartag	/man/4/acme
+clgd542x	/man/3/vga
+clgd542xhwgc	/man/3/vga
+clgd546x	/man/3/vga
+clgd546xhwgc	/man/3/vga
+click	/man/1/acme
+click	/man/1/brutus
+click	/man/1/grid-session
+click	/man/1/tktester
+click	/man/1/wm-misc
+click	/man/1/wm-sh
+click	/man/2/plumbmsg
+click	/man/2/tkclient
+click	/man/2/wmclient
+click	/man/4/registry
+click	/man/9/text
+clicked	/man/1/acme
+clicked	/man/1/charon
+clicked	/man/1/mash-tk
+clicked	/man/2/tkclient
+clicked	/man/2/wmclient
+clicked	/man/9/bind
+clicked	/man/9/checkbutton
+clicked	/man/9/choicebutton
+clicked	/man/9/radiobutton
+clicking	/man/1/acme
+clicking	/man/1/brutus
+clicking	/man/1/charon
+clicking	/man/1/deb
+clicking	/man/1/ebook
+clicking	/man/1/ftree
+clicking	/man/1/grid-monitor
+clicking	/man/1/grid-query
+clicking	/man/1/grid-session
+clicking	/man/1/mash-tk
+clicking	/man/1/miniterm
+clicking	/man/1/tktester
+clicking	/man/1/wm-misc
+clicking	/man/1/wm-sh
+clicking	/man/9/entry
+clicking	/man/9/listbox
+clicking	/man/9/menubutton
+clicking	/man/9/options
+clicking	/man/9/scrollbar
+clicking	/man/9/text
+clicks	/man/1/acme
+clicks	/man/8/plumber
+clicks	/man/9/checkbutton
+clicks	/man/9/radiobutton
+client	/man/1/collab
+client	/man/1/cpu
+client	/man/1/listen
+client	/man/1/sh-file2chan
+client	/man/1/spree-join
+client	/man/1/wm
+client	/man/10/dev
+client	/man/10/styxserver
+client	/man/2/asn1
+client	/man/2/dhcpclient
+client	/man/2/dial
+client	/man/2/draw-0intro
+client	/man/2/draw-context
+client	/man/2/draw-display
+client	/man/2/draw-image
+client	/man/2/factotum
+client	/man/2/format
+client	/man/2/keyring-auth
+client	/man/2/plumbmsg
+client	/man/2/rfc822
+client	/man/2/security-0intro
+client	/man/2/security-auth
+client	/man/2/security-login
+client	/man/2/spree
+client	/man/2/spree-allow
+client	/man/2/spree-cardlib
+client	/man/2/spree-gather
+client	/man/2/styx
+client	/man/2/styxflush
+client	/man/2/styxpersist
+client	/man/2/styxservers
+client	/man/2/sys-export
+client	/man/2/sys-file2chan
+client	/man/2/tkclient
+client	/man/2/virgil
+client	/man/2/wmsrv
+client	/man/3/0intro
+client	/man/3/draw
+client	/man/3/mnt
+client	/man/3/srv
+client	/man/3/tinyfs
+client	/man/3/tls
+client	/man/4/dbfs
+client	/man/4/dossrv
+client	/man/4/export
+client	/man/4/factotum
+client	/man/4/grid-cpu
+client	/man/4/import
+client	/man/4/keysrv
+client	/man/4/palmsrv
+client	/man/4/spree
+client	/man/5/0intro
+client	/man/5/attach
+client	/man/5/clunk
+client	/man/5/flush
+client	/man/5/open
+client	/man/5/remove
+client	/man/5/version
+client	/man/5/walk
+client	/man/6/keys
+client	/man/6/login
+client	/man/7/db
+client	/man/7/dbsrv
+client	/man/8/applylog
+client	/man/8/bootpd
+client	/man/8/collabsrv
+client	/man/8/cs
+client	/man/8/getauthinfo
+client	/man/8/httpd
+client	/man/8/logind
+client	/man/8/register
+client	/man/8/rstyxd
+client	/man/8/signer
+client	/man/8/sntp
+client	/man/8/styxchat
+client	/man/8/virgild
+client's	/man/1/collab-clients
+client's	/man/1/sh-file2chan
+client's	/man/2/dhcpclient
+client's	/man/2/security-auth
+client's	/man/2/security-login
+client's	/man/2/spree-allow
+client's	/man/2/styxservers
+client's	/man/2/sys-export
+client's	/man/2/wmsrv
+client's	/man/5/version
+client's	/man/8/bootpd
+client's	/man/8/collabsrv
+client's	/man/8/rstyxd
+client's	/man/8/styxchat
+client's	/man/8/virgild
+clientfd	/man/2/styxpersist
+clientid	/man/8/collabsrv
+clientlog	/man/8/applylog
+clientroot	/man/8/applylog
+clients	/man/1/collab-clients
+clients	/man/1/listen
+clients	/man/10/odbc
+clients	/man/10/styxserver
+clients	/man/2/dhcpclient
+clients	/man/2/draw-screen
+clients	/man/2/keyring-auth
+clients	/man/2/spree
+clients	/man/2/spree-allow
+clients	/man/2/spree-cardlib
+clients	/man/2/styxservers
+clients	/man/2/sys-file2chan
+clients	/man/2/wmsrv
+clients	/man/3/audio
+clients	/man/3/draw
+clients	/man/3/ftl
+clients	/man/4/export
+clients	/man/4/spree
+clients	/man/5/0intro
+clients	/man/5/error
+clients	/man/5/stat
+clients	/man/8/bootpd
+clients	/man/8/collabsrv
+clients	/man/8/dns
+clients	/man/8/getauthinfo
+clients	/man/8/styxchat
+clients	/man/8/virgild
+clienttype	/man/2/spree
+clienttype	/man/2/spree-gather
+clip	/man/2/draw-image
+clip	/man/2/draw-rect
+clip	/man/2/prefab-element
+clipboard	/man/3/snarf
+clipped	/man/3/draw
+clipped	/man/9/grid
+clipping	/man/2/draw-0intro
+clipping	/man/2/draw-image
+clipping	/man/3/draw
+clipr	/man/2/draw-display
+clipr	/man/2/draw-image
+clipr	/man/3/draw
+clique	/man/1/spree-join
+clique	/man/2/spree
+clique	/man/2/spree-allow
+clique	/man/2/spree-cardlib
+clique	/man/2/spree-gather
+clique	/man/2/spree-objstore
+clique	/man/4/spree
+clique's	/man/1/spree-join
+clique's	/man/2/spree
+clique's	/man/4/spree
+clique.action	/man/2/spree
+cliqueid	/man/2/spree
+cliquemodule	/man/2/spree
+cliques	/man/1/spree-join
+cliques	/man/2/spree
+cliques	/man/4/spree
+clk	/man/8/fpgaload
+clock	/man/1/wm-misc
+clock	/man/10/delay
+clock	/man/10/kbdputc
+clock	/man/10/seconds
+clock	/man/2/sys-millisec
+clock	/man/3/arch
+clock	/man/3/fpga
+clock	/man/3/kprof
+clock	/man/3/pbus
+clock	/man/3/rtc
+clock	/man/3/tinyfs
+clock	/man/3/vid
+clock	/man/4/iostats
+clock	/man/8/fpgaload
+clockf	/man/10/delay
+clocks	/man/2/keyring-ipint
+clocks	/man/3/fpga
+clocks	/man/8/fpgaload
+clockwise	/man/1/wm-misc
+clockwise	/man/2/spree-cardlib
+clockwise	/man/8/touchcal
+clockwise	/man/9/canvas
+clone	/man/1/os
+clone	/man/10/devattach
+clone	/man/10/plan9.ini
+clone	/man/2/env
+clone	/man/2/styxservers
+clone	/man/3/cmd
+clone	/man/3/ether
+clone	/man/3/ip
+clone	/man/3/plap
+clone	/man/3/ssl
+clone	/man/3/tls
+clone	/man/8/cs
+clone	/man/8/dhcp
+cloned	/man/10/newchan
+clones	/man/10/conf
+clones	/man/10/plan9.ini
+closecmd	/man/1/sh-file2chan
+closectl	/man/3/vga
+closed	/man/1/sh-file2chan
+closed	/man/1/tkcmd
+closed	/man/1/tktester
+closed	/man/10/qio
+closed	/man/2/bufio
+closed	/man/2/diskblocks
+closed	/man/2/draw-image
+closed	/man/2/registries
+closed	/man/2/styxservers
+closed	/man/2/sys-0intro
+closed	/man/2/sys-open
+closed	/man/2/sys-pctl
+closed	/man/2/tkclient
+closed	/man/2/wmclient
+closed	/man/3/boot
+closed	/man/3/cmd
+closed	/man/3/cons
+closed	/man/3/draw
+closed	/man/3/ether
+closed	/man/3/ip
+closed	/man/3/mpeg
+closed	/man/3/pipe
+closed	/man/3/sd
+closed	/man/3/snarf
+closed	/man/3/tls
+closed	/man/3/usb
+closed	/man/4/registry
+closed	/man/5/clunk
+closed	/man/7/db
+closed	/man/8/collabsrv
+closed	/man/9/canvas
+closeenough	/man/9/canvas
+closely	/man/1/cook
+closely	/man/1/m4
+closely	/man/1/sh-tk
+closely	/man/10/0intro
+closely	/man/6/attrdb
+closely	/man/6/sexprs
+closer	/man/2/math-fp
+closer	/man/9/canvas
+closes	/man/1/collab-clients
+closes	/man/1/grid-monitor
+closes	/man/10/qio
+closes	/man/2/draw-context
+closes	/man/2/pop3
+closes	/man/2/secstore
+closes	/man/2/smtp
+closes	/man/2/sys-file2chan
+closes	/man/2/sys-open
+closes	/man/4/export
+closes	/man/4/import
+closes	/man/7/db
+closest	/man/9/canvas
+closest	/man/9/listbox
+closest	/man/9/menu
+closest	/man/9/scrollbar
+closest	/man/9/text
+closing	/man/1/wm
+closing	/man/2/sys-dup
+closing	/man/3/cmd
+closing	/man/3/cons
+closing	/man/6/json
+closing	/man/6/man
+closing	/man/7/db
+closing	/man/8/collabsrv
+closing	/man/9/0intro
+closure	/man/2/sys-open
+clr	/man/10/malloc
+clri	/man/8/kfscmd
+clubs	/man/2/spree-cardlib
+clunk	/man/2/styx
+clunk	/man/2/styxservers
+clunk	/man/5/0intro
+clunk	/man/5/clunk
+clunk	/man/5/remove
+clunked	/man/5/clunk
+clunked	/man/5/open
+clunked	/man/5/version
+clunking	/man/2/styxservers
+clunks	/man/2/styxservers
+cluster	/man/4/dossrv
+cluster	/man/8/prep
+clustered	/man/10/a.out
+clutter	/man/2/0intro
+clx	/man/1/keyboard
+cm	/man/1/units
+cm	/man/2/w3c-css
+cmap	/man/2/imagefile
+cmap	/man/3/draw
+cmap	/man/6/colour
+cmap2rgb	/man/2/draw-display
+cmap2rgba	/man/2/draw-display
+cmap8	/man/2/draw-display
+cmd	/man/1/9win
+cmd	/man/1/acme
+cmd	/man/1/alphabet-abc
+cmd	/man/1/alphabet-fs
+cmd	/man/1/alphabet-grid
+cmd	/man/1/alphabet-main
+cmd	/man/1/ar
+cmd	/man/1/asm
+cmd	/man/1/auplay
+cmd	/man/1/avr
+cmd	/man/1/basename
+cmd	/man/1/bind
+cmd	/man/1/cal
+cmd	/man/1/calc
+cmd	/man/1/cat
+cmd	/man/1/cd
+cmd	/man/1/chgrp
+cmd	/man/1/chmod
+cmd	/man/1/cleanname
+cmd	/man/1/cmp
+cmd	/man/1/comm
+cmd	/man/1/cook
+cmd	/man/1/cp
+cmd	/man/1/cprof
+cmd	/man/1/cpu
+cmd	/man/1/crypt
+cmd	/man/1/date
+cmd	/man/1/dd
+cmd	/man/1/diff
+cmd	/man/1/disdep
+cmd	/man/1/du
+cmd	/man/1/echo
+cmd	/man/1/emu
+cmd	/man/1/env
+cmd	/man/1/fmt
+cmd	/man/1/fortune
+cmd	/man/1/freq
+cmd	/man/1/fs
+cmd	/man/1/ftest
+cmd	/man/1/gettar
+cmd	/man/1/grep
+cmd	/man/1/grid-session
+cmd	/man/1/gzip
+cmd	/man/1/idea
+cmd	/man/1/itest
+cmd	/man/1/kill
+cmd	/man/1/listen
+cmd	/man/1/look
+cmd	/man/1/ls
+cmd	/man/1/man
+cmd	/man/1/mash
+cmd	/man/1/mash-make
+cmd	/man/1/mash-tk
+cmd	/man/1/mc
+cmd	/man/1/mdb
+cmd	/man/1/mk
+cmd	/man/1/mkdir
+cmd	/man/1/mprof
+cmd	/man/1/mv
+cmd	/man/1/netkey
+cmd	/man/1/netstat
+cmd	/man/1/ns
+cmd	/man/1/nsbuild
+cmd	/man/1/os
+cmd	/man/1/p
+cmd	/man/1/passwd
+cmd	/man/1/plumb
+cmd	/man/1/prof
+cmd	/man/1/ps
+cmd	/man/1/pwd
+cmd	/man/1/rcmd
+cmd	/man/1/read
+cmd	/man/1/rm
+cmd	/man/1/runas
+cmd	/man/1/secstore
+cmd	/man/1/sendmail
+cmd	/man/1/sh
+cmd	/man/1/sh-alphabet
+cmd	/man/1/sh-arg
+cmd	/man/1/sh-csv
+cmd	/man/1/sh-expr
+cmd	/man/1/sh-file2chan
+cmd	/man/1/sh-mload
+cmd	/man/1/sh-regex
+cmd	/man/1/sh-sexprs
+cmd	/man/1/sh-std
+cmd	/man/1/sh-string
+cmd	/man/1/sh-tk
+cmd	/man/1/sleep
+cmd	/man/1/stack
+cmd	/man/1/stream
+cmd	/man/1/strings
+cmd	/man/1/sum
+cmd	/man/1/tcs
+cmd	/man/1/tee
+cmd	/man/1/telnet
+cmd	/man/1/time
+cmd	/man/1/timestamp
+cmd	/man/1/tkcmd
+cmd	/man/1/touch
+cmd	/man/1/tr
+cmd	/man/1/tsort
+cmd	/man/1/unicode
+cmd	/man/1/uniq
+cmd	/man/1/units
+cmd	/man/1/uuencode
+cmd	/man/1/vacget
+cmd	/man/1/wc
+cmd	/man/1/webgrab
+cmd	/man/1/wish
+cmd	/man/1/xd
+cmd	/man/1/yacc
+cmd	/man/1/zeros
+cmd	/man/10/ntsrv
+cmd	/man/10/odbc
+cmd	/man/2/command
+cmd	/man/2/scsiio
+cmd	/man/2/security-auth
+cmd	/man/2/sh
+cmd	/man/2/spree
+cmd	/man/2/spree-allow
+cmd	/man/2/tk
+cmd	/man/3/cmd
+cmd	/man/4/9srvfs
+cmd	/man/4/archfs
+cmd	/man/4/dbfs
+cmd	/man/4/dossrv
+cmd	/man/4/export
+cmd	/man/4/factotum
+cmd	/man/4/ftpfs
+cmd	/man/4/grid-cpu
+cmd	/man/4/import
+cmd	/man/4/iostats
+cmd	/man/4/keyfs
+cmd	/man/4/keysrv
+cmd	/man/4/kfs
+cmd	/man/4/lockfs
+cmd	/man/4/logfile
+cmd	/man/4/memfs
+cmd	/man/4/mntgen
+cmd	/man/4/namespace
+cmd	/man/4/palmsrv
+cmd	/man/4/ramfile
+cmd	/man/4/registry
+cmd	/man/4/spree
+cmd	/man/4/tarfs
+cmd	/man/4/trfs
+cmd	/man/4/vacfs
+cmd	/man/7/cddb
+cmd	/man/7/dbsrv
+cmd	/man/8/ai2key
+cmd	/man/8/applylog
+cmd	/man/8/bootpd
+cmd	/man/8/changelogin
+cmd	/man/8/create
+cmd	/man/8/createsignerkey
+cmd	/man/8/cs
+cmd	/man/8/dhcp
+cmd	/man/8/dns
+cmd	/man/8/fpgaload
+cmd	/man/8/ftl
+cmd	/man/8/getauthinfo
+cmd	/man/8/init
+cmd	/man/8/kfscmd
+cmd	/man/8/logind
+cmd	/man/8/mangaload
+cmd	/man/8/manufacture
+cmd	/man/8/mkfs
+cmd	/man/8/ping
+cmd	/man/8/plumber
+cmd	/man/8/prep
+cmd	/man/8/rip
+cmd	/man/8/rstyxd
+cmd	/man/8/signer
+cmd	/man/8/sntp
+cmd	/man/8/styxchat
+cmd	/man/8/styxmon
+cmd	/man/8/touchcal
+cmd	/man/8/virgild
+cmd	/man/9/0intro
+cmd.c	/man/3/cmd
+cmd2string	/man/2/sh
+cmd640	/man/3/sd
+cmdbuf	/man/10/parsecmd
+cmdchan	/man/1/sh-file2chan
+cmember	/man/2/spree-cardlib
+cmp	/man/1/cmp
+cmp	/man/1/comm
+cmp	/man/1/diff
+cmp	/man/1/mash
+cmp	/man/1/sh
+cmp	/man/1/sh-test
+cmp	/man/1/sum
+cmp	/man/10/mk
+cmp	/man/2/keyring-ipint
+cmp.b	/man/1/cmp
+cn	/man/10/9load
+cname	/man/10/newchan
+cname	/man/8/dns
+co	/man/6/keyboard
+coalesced	/man/4/registry
+coarse	/man/1/wm-misc
+coarser	/man/2/sys-millisec
+code	/man/1/0intro
+code	/man/1/asm
+code	/man/1/avr
+code	/man/1/cprof
+code	/man/1/deb
+code	/man/1/limbo
+code	/man/1/miniterm
+code	/man/1/mprof
+code	/man/1/secstore
+code	/man/1/sh-file2chan
+code	/man/1/sh-std
+code	/man/1/sh-tk
+code	/man/1/tr
+code	/man/1/yacc
+code	/man/10/0intro
+code	/man/10/2c
+code	/man/10/2l
+code	/man/10/9load
+code	/man/10/c2l
+code	/man/10/conf
+code	/man/10/dmainit
+code	/man/10/dynld
+code	/man/10/inb
+code	/man/10/intrenable
+code	/man/10/kbdputc
+code	/man/10/lock
+code	/man/2/0intro
+code	/man/2/asn1
+code	/man/2/convcs
+code	/man/2/dbm
+code	/man/2/debug
+code	/man/2/dhcpclient
+code	/man/2/dis
+code	/man/2/draw-0intro
+code	/man/2/draw-example
+code	/man/2/imagefile
+code	/man/2/json
+code	/man/2/keyring-crypt
+code	/man/2/math-0intro
+code	/man/2/palmfile
+code	/man/2/prefab-compound
+code	/man/2/print
+code	/man/2/prof
+code	/man/2/security-0intro
+code	/man/2/ubfa
+code	/man/2/w3c-xpointers
+code	/man/3/arch
+code	/man/3/boot
+code	/man/3/cmd
+code	/man/3/cons
+code	/man/3/dbg
+code	/man/3/flash
+code	/man/3/prog
+code	/man/3/ssl
+code	/man/3/tls
+code	/man/4/namespace
+code	/man/6/dis
+code	/man/6/image
+code	/man/6/login
+code	/man/6/scancode
+code	/man/9/0intro
+code	/man/9/text
+codec	/man/10/kproc
+coded	/man/2/convcs
+coded	/man/2/virgil
+coded	/man/8/virgild
+codepage	/man/2/convcs
+codes	/man/1/tr
+codes	/man/1/xd
+codes	/man/1/yacc
+codes	/man/10/acid
+codes	/man/10/error
+codes	/man/2/asn1
+codes	/man/2/convcs
+codes	/man/2/dis
+codes	/man/2/ir
+codes	/man/2/scsiio
+codes	/man/3/cons
+codes	/man/6/scancode
+coding	/man/2/rfc822
+coding	/man/2/security-0intro
+coff	/man/10/5coff
+coffee	/man/1/wm-misc
+coffee.b	/man/1/wm-misc
+coherently	/man/2/sys-0intro
+coldepth	/man/2/print
+collab	/man/1/collab
+collab	/man/1/collab-clients
+collab	/man/8/collabsrv
+collaboration	/man/8/collabsrv
+collaborative	/man/1/collab
+collaborative	/man/1/collab-clients
+collaborative	/man/8/collabsrv
+collabsrv	/man/1/collab
+collabsrv	/man/1/collab-clients
+collabsrv	/man/8/collabsrv
+collapses	/man/1/ftree
+collating	/man/1/look
+collected	/man/2/sys-0intro
+collected	/man/2/sys-dup
+collecting	/man/1/m4
+collecting	/man/10/kbdputc
+collection	/man/1/0intro
+collection	/man/1/m4
+collection	/man/1/math-misc
+collection	/man/1/secstore
+collection	/man/1/wm
+collection	/man/1/wm-misc
+collection	/man/10/0intro
+collection	/man/2/convcs
+collection	/man/2/draw-0intro
+collection	/man/2/keyring-0intro
+collection	/man/2/keyring-certtostr
+collection	/man/2/keyset
+collection	/man/2/prefab-environ
+collection	/man/2/sexprs
+collection	/man/2/spki-verifier
+collection	/man/2/sys-0intro
+collection	/man/3/ip
+collection	/man/4/factotum
+collection	/man/6/json
+collection	/man/6/ndb
+collection	/man/9/menu
+collection	/man/9/scale
+collector	/man/2/registries
+collector	/man/5/clunk
+collects	/man/1/m4
+collects	/man/2/prefab-environ
+collects	/man/2/prefab-style
+collide	/man/10/sleep
+collisions	/man/8/prep
+colon	/man/1/acme
+colon	/man/1/calc
+colon	/man/1/charon
+colon	/man/1/mk
+colon	/man/1/tiny
+colon	/man/10/mk
+colon	/man/10/odbc
+colon	/man/2/ether
+colon	/man/2/ip
+colon	/man/3/pnp
+colon	/man/3/prog
+colon	/man/4/acme
+colonies	/man/1/cal
+colons	/man/6/json
+color	/man/2/draw-0intro
+color	/man/2/draw-display
+color	/man/2/draw-image
+color	/man/2/prefab-element
+color	/man/2/prefab-style
+color	/man/2/print
+color	/man/2/tk
+color	/man/3/draw
+color	/man/3/vga
+color	/man/6/colour
+color	/man/9/text
+color	/man/9/types
+colorkey	/man/3/tv
+colormap	/man/3/draw
+colormix	/man/2/draw-display
+colors	/man/1/wm-misc
+colors	/man/2/prefab-environ
+colors	/man/6/colour
+colors	/man/9/menubutton
+colors.b	/man/1/wm-misc
+colour	/man/1/collab-clients
+colour	/man/1/cprof
+colour	/man/1/emu
+colour	/man/1/mprof
+colour	/man/1/prof
+colour	/man/1/wm-misc
+colour	/man/2/draw-0intro
+colour	/man/2/draw-display
+colour	/man/2/draw-example
+colour	/man/2/draw-image
+colour	/man/2/draw-screen
+colour	/man/2/imagefile
+colour	/man/2/mpeg
+colour	/man/2/prefab-0intro
+colour	/man/2/prefab-element
+colour	/man/2/prefab-style
+colour	/man/2/tk
+colour	/man/3/draw
+colour	/man/3/tv
+colour	/man/6/colour
+colour	/man/6/image
+colour	/man/8/collabsrv
+colour	/man/9/canvas
+colour	/man/9/checkbutton
+colour	/man/9/choicebutton
+colour	/man/9/frame
+colour	/man/9/menu
+colour	/man/9/options
+colour	/man/9/panel
+colour	/man/9/radiobutton
+colour	/man/9/scale
+colour	/man/9/text
+colour	/man/9/types
+coloured	/man/1/charon
+coloured	/man/1/collab-clients
+coloured	/man/1/cprof
+coloured	/man/2/draw-0intro
+coloured	/man/2/draw-display
+coloured	/man/9/canvas
+colourmap	/man/9/frame
+colours	/man/1/mprof
+colours	/man/1/prof
+colours	/man/2/draw-display
+colours	/man/2/draw-example
+colours	/man/2/imagefile
+colours	/man/2/prefab-0intro
+colours	/man/2/prefab-style
+colours	/man/6/colour
+colours	/man/9/button
+colours	/man/9/canvas
+colours	/man/9/checkbutton
+colours	/man/9/choicebutton
+colours	/man/9/entry
+colours	/man/9/frame
+colours	/man/9/label
+colours	/man/9/listbox
+colours	/man/9/menu
+colours	/man/9/radiobutton
+colours	/man/9/scale
+colours	/man/9/scrollbar
+colours	/man/9/text
+column	/man/1/acme
+column	/man/1/comm
+column	/man/1/ls
+column	/man/1/miniterm
+column	/man/1/tktester
+column	/man/10/odbc
+column	/man/6/man
+column	/man/7/db
+column	/man/9/grid
+column	/man/9/menu
+column2	/man/9/grid
+columnated	/man/1/acme
+columnconfigure	/man/9/grid
+columndelete	/man/9/grid
+columnindex	/man/9/grid
+columninsert	/man/9/grid
+columns	/man/1/acme
+columns	/man/1/mc
+columns	/man/1/ps
+columns	/man/1/tktester
+columns	/man/10/odbc
+columns	/man/6/man
+columns	/man/7/db
+columns	/man/9/grid
+columnspan	/man/1/tktester
+columnspan	/man/9/grid
+columnspans	/man/9/grid
+columntitle	/man/7/db
+com	/man/1/webgrab
+com	/man/10/9load
+com	/man/10/plan9.ini
+com	/man/2/w3c-css
+com1	/man/10/plan9.ini
+com2	/man/10/plan9.ini
+com2:96,n,8,1,p	/man/10/plan9.ini
+comand	/man/1/charon
+combination	/man/1/charon
+combination	/man/1/chmod
+combination	/man/1/tr
+combination	/man/1/units
+combination	/man/10/kbdputc
+combination	/man/2/draw-0intro
+combination	/man/2/hash
+combination	/man/2/ip
+combination	/man/2/keyring-0intro
+combination	/man/2/prof
+combination	/man/2/security-0intro
+combination	/man/2/sets
+combination	/man/2/spki
+combination	/man/2/tkclient
+combination	/man/2/wmclient
+combination	/man/8/applylog
+combination	/man/8/dns
+combination	/man/9/bind
+combination	/man/9/grid
+combinations	/man/10/0intro
+combinations	/man/2/dis
+combinations	/man/2/draw-example
+combinations	/man/2/filepat
+combinator	/man/2/w3c-css
+combine	/man/1/acme
+combine	/man/10/ar
+combine	/man/2/draw-image
+combine	/man/2/draw-rect
+combine	/man/2/lists
+combine	/man/3/draw
+combine	/man/3/ssl
+combine	/man/9/bind
+combined	/man/1/ar
+combined	/man/10/iar
+combined	/man/2/bloomfilter
+combined	/man/2/draw-0intro
+combined	/man/3/vga
+combined	/man/5/0intro
+combined	/man/9/bind
+combined	/man/9/menu
+combines	/man/2/secstore
+combining	/man/2/sets
+combo	/man/10/plan9.ini
+comes	/man/10/memory
+comes	/man/2/asn1
+comes	/man/3/pipe
+comes	/man/3/tls
+comfortably	/man/6/image
+comm	/man/1/comm
+comm	/man/1/uniq
+comm.b	/man/1/comm
+comma	/man/1/calc
+comma	/man/1/charon
+comma	/man/1/m4
+comma	/man/1/sh-csv
+comma	/man/2/csv
+comma	/man/2/rfc822
+comma	/man/6/users
+command's	/man/1/mash
+command's	/man/1/mash-tk
+command's	/man/1/os
+command's	/man/1/time
+command's	/man/3/cmd
+command's	/man/9/text
+command.com	/man/10/plan9.ini
+command1	/man/1/mash
+command2	/man/1/mash
+commas	/man/1/m4
+commas	/man/10/acid
+commas	/man/10/plan9.ini
+commas	/man/10/print
+commas	/man/2/csv
+commas	/man/2/sys-print
+commas	/man/6/json
+commas	/man/9/grid
+commence	/man/1/grid-session
+comment	/man/1/bind
+comment	/man/1/charon
+comment	/man/1/m4
+comment	/man/1/tiny
+comment	/man/1/webgrab
+comment	/man/1/yacc
+comment	/man/10/2c
+comment	/man/10/conf
+comment	/man/2/w3c-xpointers
+comment	/man/6/ubfa
+comment	/man/8/applylog
+commentary	/man/6/attrdb
+commentary	/man/6/man
+comments	/man/1/calc
+comments	/man/1/charon
+comments	/man/1/m4
+comments	/man/1/mk
+comments	/man/1/yacc
+comments	/man/10/dynld
+comments	/man/10/mk
+comments	/man/10/styx
+comments	/man/2/cfg
+comments	/man/2/draw-example
+comments	/man/4/factotum
+comments	/man/6/plumbing
+comments	/man/6/ubfa
+comments	/man/8/ai2key
+commercial	/man/1/crypt
+commercial	/man/10/9load
+commercial	/man/2/keyring-crypt
+commercial	/man/9/1copyright
+commit	/man/10/odbc
+commited	/man/10/odbc
+commits	/man/2/spree-cardlib
+committed	/man/2/venti
+committed	/man/5/stat
+committing	/man/2/spree
+commmand	/man/2/scsiio
+common	/man/1/acme
+common	/man/1/alphabet-fs
+common	/man/1/blur
+common	/man/1/comm
+common	/man/1/fs
+common	/man/1/grid-monitor
+common	/man/1/itest
+common	/man/1/mux
+common	/man/10/2a
+common	/man/10/9load
+common	/man/10/devattach
+common	/man/10/plan9.ini
+common	/man/2/0intro
+common	/man/2/dial
+common	/man/2/draw-0intro
+common	/man/2/draw-display
+common	/man/2/draw-image
+common	/man/2/encoding
+common	/man/2/ip
+common	/man/2/json
+common	/man/2/keyring-0intro
+common	/man/2/lists
+common	/man/2/spki
+common	/man/2/spree-gather
+common	/man/2/styxservers
+common	/man/2/sys-dial
+common	/man/2/ubfa
+common	/man/2/w3c-uris
+common	/man/2/w3c-xpointers
+common	/man/2/xml
+common	/man/3/cons
+common	/man/3/draw
+common	/man/3/srv9
+common	/man/6/colour
+common	/man/6/keytext
+common	/man/6/ndb
+common	/man/6/scancode
+common	/man/6/sexprs
+common	/man/8/prep
+common	/man/8/rip
+common	/man/9/0intro
+common	/man/9/menu
+common	/man/9/options
+commonly	/man/1/0intro
+commonly	/man/1/mash-make
+commonly	/man/10/odbc
+commonly	/man/10/sleep
+commonly	/man/2/crc
+commonly	/man/2/dhcpclient
+commonly	/man/2/keyring-0intro
+commonly	/man/2/scsiio
+commonly	/man/2/styxservers
+commonly	/man/2/sys-export
+commonly	/man/2/sys-fauth
+commonly	/man/3/ip
+commonly	/man/3/rtc
+commonly	/man/4/mntgen
+commonly	/man/6/ndb
+commonly	/man/6/regexp
+commonly	/man/9/canvas
+commonly	/man/9/menu
+communicable	/man/3/logfs
+communicate	/man/1/sh-tk
+communicate	/man/2/dhcpclient
+communicate	/man/2/dial
+communicate	/man/2/security-0intro
+communicate	/man/2/spree
+communicate	/man/2/sys-dial
+communicate	/man/2/sys-file2chan
+communicate	/man/3/pbus
+communicate	/man/3/plap
+communicate	/man/4/spree
+communicate	/man/8/collabsrv
+communicate	/man/8/logind
+communicate	/man/8/register
+communicate	/man/9/grid
+communicate	/man/9/options
+communicate	/man/9/pack
+communicated	/man/10/styxserver
+communicated	/man/2/sys-stat
+communicated	/man/3/tls
+communicates	/man/2/security-login
+communicating	/man/1/charon
+communicating	/man/2/keyring-0intro
+communicating	/man/2/keyring-auth
+communicating	/man/2/security-0intro
+communicating	/man/2/spree
+communicating	/man/3/lpt
+communication	/man/1/blur
+communication	/man/1/sh-tk
+communication	/man/10/styxserver
+communication	/man/2/factotum
+communication	/man/2/ir
+communication	/man/2/keyring-certtostr
+communication	/man/2/keyring-getstring
+communication	/man/2/keyring-sha1
+communication	/man/2/msgio
+communication	/man/2/plumbmsg
+communication	/man/2/security-0intro
+communication	/man/2/styxservers
+communication	/man/2/sys-pipe
+communication	/man/2/wmsrv
+communication	/man/3/eia
+communication	/man/3/ip
+communication	/man/3/mnt
+communication	/man/3/pipe
+communication	/man/5/0intro
+communication	/man/5/version
+communication	/man/6/ubfa
+communications	/man/10/devattach
+communications	/man/2/dial
+communications	/man/2/keyring-0intro
+communications	/man/2/security-0intro
+communications	/man/2/sys-dial
+communications	/man/3/mnt
+communications	/man/3/tls
+communications	/man/6/auth
+commutative	/man/1/fc
+comp	/man/2/prefab-compound
+compact	/man/1/keyboard
+compact	/man/2/readdir
+compact	/man/2/spki
+compact	/man/4/dbfs
+compact	/man/6/dis
+companion	/man/3/ip
+comparator	/man/1/alphabet-fs
+comparator	/man/1/diff
+comparator	/man/1/fs
+compare	/man/1/cmp
+compare	/man/1/diff
+compare	/man/1/look
+compare	/man/1/mash
+compare	/man/1/sh
+compare	/man/1/sort
+compare	/man/2/keyring-0intro
+compare	/man/2/styx
+compare	/man/9/text
+compared	/man/1/cmp
+compared	/man/1/diff
+compared	/man/2/daytime
+compared	/man/2/w3c-css
+compared	/man/3/ssl
+compared	/man/6/image
+compared	/man/8/signer
+compared	/man/9/grid
+compares	/man/1/alphabet-fs
+compares	/man/1/avr
+compares	/man/1/fs
+compares	/man/10/memory
+compares	/man/10/newchan
+compares	/man/10/strcat
+compares	/man/2/keyring-ipint
+compares	/man/2/security-0intro
+compares	/man/9/text
+comparing	/man/1/uniq
+comparing	/man/2/sys-stat
+comparison	/man/1/cmp
+comparison	/man/1/look
+comparison	/man/10/eve
+comparison	/man/10/memory
+comparison	/man/10/newchan
+comparison	/man/10/strcat
+comparison	/man/2/asn1
+comparison	/man/2/convcs
+comparison	/man/2/stringinttab
+comparison	/man/2/sys-0intro
+comparison	/man/2/sys-stat
+comparison	/man/2/w3c-css
+comparison	/man/2/xml
+comparison	/man/3/ssl
+comparison	/man/6/plumbing
+comparisons	/man/1/look
+comparisons	/man/1/sort
+comparisons	/man/10/strcat
+comparisons	/man/6/keytext
+compatibility	/man/10/9load
+compatibility	/man/10/ar
+compatibility	/man/10/kproc
+compatibility	/man/2/popup
+compatibility	/man/4/acme
+compatibility	/man/6/colour
+compatibility	/man/6/keytext
+compatible	/man/1/alphabet-abc
+compatible	/man/1/alphabet-grid
+compatible	/man/1/alphabet-main
+compatible	/man/1/crypt
+compatible	/man/1/gzip
+compatible	/man/1/sh-alphabet
+compatible	/man/10/2c
+compatible	/man/10/devattach
+compatible	/man/10/plan9.ini
+compatible	/man/10/styxserver
+compatible	/man/2/asn1
+compatible	/man/2/bufio
+compatible	/man/2/debug
+compatible	/man/2/filter-deflate
+compatible	/man/2/format
+compatible	/man/2/ip
+compatible	/man/2/sets
+compatible	/man/2/styx
+compatible	/man/2/sys-self
+compatible	/man/3/i82365
+compatible	/man/6/utf
+compensate	/man/10/dmainit
+compensate	/man/3/touch
+compensates	/man/3/ftl
+competing	/man/1/tiny
+compilation	/man/1/deb
+compilation	/man/1/limbo
+compilation	/man/1/mk
+compilation	/man/10/0intro
+compilation	/man/10/2c
+compilation	/man/10/2l
+compilation	/man/10/mk
+compilation	/man/2/0intro
+compilation	/man/2/regex
+compilation	/man/3/cons
+compile	/man/1/0intro
+compile	/man/1/emu
+compile	/man/1/mash-make
+compile	/man/1/mk
+compile	/man/10/2c
+compile	/man/10/mk
+compile	/man/10/styxserver
+compile	/man/2/math-0intro
+compile	/man/2/regex
+compiled	/man/1/0intro
+compiled	/man/1/cprof
+compiled	/man/1/itest
+compiled	/man/1/mprof
+compiled	/man/1/prof
+compiled	/man/1/stack
+compiled	/man/10/9load
+compiled	/man/10/a.out
+compiled	/man/10/conf
+compiled	/man/2/regex
+compiled	/man/3/prog
+compiled	/man/6/dis
+compiled	/man/6/sbl
+compiler	/man/1/emu
+compiler	/man/1/limbo
+compiler	/man/1/yacc
+compiler	/man/10/0intro
+compiler	/man/10/2c
+compiler	/man/10/2l
+compiler	/man/10/a.out
+compiler	/man/10/c2l
+compiler	/man/2/hash
+compiler	/man/2/math-0intro
+compiler	/man/2/sys-print
+compiler	/man/6/dis
+compiler	/man/6/sbl
+compiler	/man/6/utf
+compiler	/man/8/styxchat
+compiler's	/man/10/dynld
+compilers	/man/1/acme
+compilers	/man/10/2c
+compilers	/man/10/a.out
+compiles	/man/1/0intro
+compiles	/man/1/filename
+compiles	/man/1/limbo
+compiling	/man/10/0intro
+compiling	/man/10/2c
+compiling	/man/10/conf
+complain	/man/1/mkdir
+complement	/man/1/acme
+complement	/man/1/listen
+complement	/man/1/sh-expr
+complement	/man/1/tr
+complement	/man/2/bufio
+complement	/man/2/draw-display
+complement	/man/2/keyring-ipint
+complement	/man/3/audio
+complement	/man/6/audio
+complement	/man/6/dis
+complementary	/man/2/draw-image
+complementary	/man/2/msgio
+complementary	/man/3/draw
+complemented	/man/1/sh
+complements	/man/2/dial
+complements	/man/2/sys-dial
+complete	/man/1/acme
+complete	/man/1/alphabet-fs
+complete	/man/1/charon
+complete	/man/1/fs
+complete	/man/1/mash-tk
+complete	/man/1/sh
+complete	/man/1/sh-alphabet
+complete	/man/1/units
+complete	/man/1/yacc
+complete	/man/10/5cv
+complete	/man/10/acid
+complete	/man/10/devattach
+complete	/man/10/kbdputc
+complete	/man/10/odbc
+complete	/man/10/print
+complete	/man/10/styx
+complete	/man/2/asn1
+complete	/man/2/complete
+complete	/man/2/registries
+complete	/man/2/spree-cardlib
+complete	/man/2/styx
+complete	/man/2/styxservers-nametree
+complete	/man/2/sys-export
+complete	/man/2/sys-utfbytes
+complete	/man/2/w3c-css
+complete	/man/2/wait
+complete	/man/3/cons
+complete	/man/4/acme
+complete	/man/5/0intro
+complete	/man/5/attach
+complete	/man/6/sexprs
+complete	/man/9/canvas
+complete	/man/9/text
+complete.b	/man/2/complete
+complete.m	/man/2/complete
+completed	/man/1/alphabet-main
+completed	/man/1/blur
+completed	/man/1/sh
+completed	/man/1/sh-alphabet
+completed	/man/1/wm-sh
+completed	/man/2/itslib
+completed	/man/2/keyring-0intro
+completed	/man/2/keyring-sha1
+completed	/man/2/tftp
+completed	/man/3/cmd
+completed	/man/5/flush
+completely	/man/1/0intro
+completely	/man/1/acme
+completely	/man/10/2c
+completely	/man/10/dev
+completely	/man/10/plan9.ini
+completely	/man/2/draw-rect
+completely	/man/2/security-auth
+completely	/man/2/sys-pctl
+completely	/man/2/w3c-xpointers
+completely	/man/3/fpga
+completely	/man/4/dbfs
+completely	/man/9/button
+completely	/man/9/canvas
+completely	/man/9/checkbutton
+completely	/man/9/entry
+completely	/man/9/menubutton
+completely	/man/9/radiobutton
+completely	/man/9/text
+completeness	/man/1/calc
+completeness	/man/2/plumbmsg
+completes	/man/1/mash
+completes	/man/2/mpeg
+completes	/man/3/pbus
+completes	/man/4/registry
+completion	/man/1/cprof
+completion	/man/1/mprof
+completion	/man/1/prof
+completion	/man/1/sh
+completion	/man/2/complete
+completion	/man/8/applylog
+completion	/man/8/register
+complex	/man/1/acme
+complex	/man/1/grid-monitor
+complex	/man/1/mash-tk
+complex	/man/10/acid
+complex	/man/10/devattach
+complex	/man/10/error
+complex	/man/10/intrenable
+complex	/man/2/prefab-compound
+complex	/man/2/security-0intro
+complex	/man/2/sexprs
+complex	/man/2/w3c-css
+complex	/man/2/w3c-xpointers
+complex	/man/3/ds
+complex	/man/5/0intro
+complex	/man/8/httpd
+complex	/man/9/frame
+complexity	/man/2/keyring-gensk
+complexity	/man/6/login
+compliant	/man/1/charon
+compliant	/man/3/vga
+complicated	/man/1/grid-query
+complicated	/man/10/styxserver
+component	/man/1/0intro
+component	/man/1/basename
+component	/man/1/charon
+component	/man/1/secstore
+component	/man/1/sh
+component	/man/1/webgrab
+component	/man/10/5cv
+component	/man/10/a.out
+component	/man/10/conf
+component	/man/10/devattach
+component	/man/10/dynld
+component	/man/2/asn1
+component	/man/2/debug
+component	/man/2/dhcpclient
+component	/man/2/draw-0intro
+component	/man/2/draw-display
+component	/man/2/format
+component	/man/2/names
+component	/man/2/palmfile
+component	/man/2/prefab-element
+component	/man/2/print
+component	/man/2/rfc822
+component	/man/2/secstore
+component	/man/2/security-0intro
+component	/man/2/security-login
+component	/man/2/spki
+component	/man/2/styxservers-nametree
+component	/man/2/sys-dial
+component	/man/2/sys-fd2path
+component	/man/2/ubfa
+component	/man/2/w3c-uris
+component	/man/3/tv
+component	/man/6/colour
+component	/man/6/ndb
+component	/man/9/types
+components	/man/1/0intro
+components	/man/1/acme
+components	/man/1/alphabet-abc
+components	/man/1/alphabet-grid
+components	/man/1/charon
+components	/man/1/mash
+components	/man/1/mash-make
+components	/man/1/mash-tk
+components	/man/1/plumb
+components	/man/1/sh
+components	/man/10/a.out
+components	/man/10/conf
+components	/man/10/devattach
+components	/man/10/dynld
+components	/man/10/inm
+components	/man/10/kproc
+components	/man/10/odbc
+components	/man/2/0intro
+components	/man/2/asn1
+components	/man/2/draw-0intro
+components	/man/2/draw-display
+components	/man/2/draw-image
+components	/man/2/fsproto
+components	/man/2/ida
+components	/man/2/plumbmsg
+components	/man/2/prefab-0intro
+components	/man/2/prefab-element
+components	/man/2/print
+components	/man/2/spki
+components	/man/2/ubfa
+components	/man/2/w3c-css
+components	/man/2/w3c-uris
+components	/man/2/w3c-xpointers
+components	/man/5/0intro
+components	/man/6/colour
+components	/man/9/types
+compose	/man/1/alphabet-fs
+compose	/man/1/fs
+compose	/man/2/math-fp
+compose	/man/6/keyboard
+composed	/man/1/acme
+composed	/man/2/asn1
+composite	/man/2/translate
+compositing	/man/1/alphabet-fs
+compositing	/man/1/fs
+compositing	/man/2/draw-image
+compositing	/man/6/colour
+composition	/man/2/asn1
+compound	/man/1/acme
+compound	/man/1/mash
+compound	/man/1/sh
+compound	/man/1/units
+compound	/man/2/prefab-0intro
+compound	/man/2/prefab-compound
+compound	/man/2/prefab-element
+compound	/man/2/prefab-environ
+compound	/man/2/prefab-style
+compound	/man/2/spki
+compound	/man/2/ubfa
+compound	/man/2/w3c-xpointers
+compound	/man/3/ds
+compound	/man/4/acme
+compound	/man/6/ubfa
+compound.box	/man/2/prefab-compound
+compound.contents	/man/2/prefab-compound
+compound.draw	/man/2/prefab-compound
+compound.draw	/man/2/prefab-element
+compound.highlight	/man/2/prefab-compound
+compound.iconbox	/man/2/prefab-compound
+compound.image	/man/2/prefab-compound
+compound.layoutbox	/man/2/prefab-compound
+compound.r	/man/2/prefab-compound
+compound.redraw	/man/2/prefab-compound
+compound.scroll	/man/2/prefab-compound
+compound.select	/man/2/prefab-compound
+compound.show	/man/2/prefab-compound
+compound.tagselect	/man/2/prefab-compound
+compound.tagselect	/man/2/prefab-element
+compound.textbox	/man/2/prefab-compound
+compounds	/man/2/prefab-0intro
+compounds	/man/2/prefab-compound
+compounds	/man/2/prefab-element
+compounds	/man/2/prefab-style
+compress	/man/10/5cv
+compress	/man/3/ip
+compressed	/man/10/5cv
+compressed	/man/10/9load
+compressed	/man/2/palmfile
+compressed	/man/2/spree
+compressed	/man/2/xml
+compressed	/man/3/draw
+compressed	/man/6/image
+compressed	/man/8/rdbgsrv
+compresses	/man/1/gzip
+compresses	/man/10/5cv
+compression	/man/1/gzip
+compression	/man/10/5cv
+compression	/man/2/filter
+compression	/man/2/filter-deflate
+compression	/man/6/image
+comprised	/man/1/deb
+comprised	/man/2/asn1
+comprised	/man/2/cfg
+comprised	/man/2/convcs
+comprises	/man/1/look
+comprises	/man/2/draw-0intro
+comprising	/man/1/sh
+comprising	/man/2/asn1
+comprising	/man/2/pop3
+comprising	/man/6/image
+comprising	/man/6/keyboard
+compromise	/man/2/xml
+computation	/man/2/draw-0intro
+computation	/man/2/prefab-compound
+computation	/man/2/sys-0intro
+computation	/man/6/auth
+computational	/man/2/dict
+computations	/man/6/colour
+compute	/man/2/hash
+compute	/man/2/prefab-element
+compute	/man/2/sys-utfbytes
+compute	/man/3/touch
+compute	/man/9/grid
+computed	/man/1/acme
+computed	/man/1/du
+computed	/man/1/mk
+computed	/man/10/a.out
+computed	/man/10/mk
+computed	/man/2/math-elem
+computed	/man/2/spki
+computed	/man/3/cap
+computed	/man/3/ssl
+computed	/man/6/keytext
+computed	/man/9/button
+computed	/man/9/checkbutton
+computed	/man/9/choicebutton
+computed	/man/9/grid
+computed	/man/9/label
+computed	/man/9/menubutton
+computed	/man/9/panel
+computed	/man/9/radiobutton
+computer	/man/2/draw-image
+computer	/man/2/security-0intro
+computer	/man/3/i82365
+computer	/man/9/1copyright
+computers	/man/2/security-0intro
+computers	/man/2/sexprs
+computers	/man/6/sexprs
+computes	/man/2/draw-rect
+computes	/man/2/keyring-sha1
+computes	/man/2/math-elem
+computes	/man/2/math-linalg
+computes	/man/6/colour
+computes	/man/9/grid
+computes	/man/9/pack
+computes	/man/9/text
+computing	/man/2/math-0intro
+computing	/man/2/math-fp
+computing	/man/9/options
+con	/man/1/yacc
+con	/man/2/bufio
+con	/man/2/command
+con	/man/2/convcs
+con	/man/2/draw-display
+con	/man/2/draw-example
+con	/man/2/draw-image
+con	/man/2/ether
+con	/man/2/exception
+con	/man/2/factotum
+con	/man/2/format
+con	/man/2/geodesy
+con	/man/2/itslib
+con	/man/2/keyring-crypt
+con	/man/2/prefab-element
+con	/man/2/print
+con	/man/2/prof
+con	/man/2/readdir
+con	/man/2/rfc822
+con	/man/2/scsiio
+con	/man/2/secstore
+con	/man/2/styx
+con	/man/2/styxflush
+con	/man/2/styxservers-nametree
+con	/man/2/sys-stat
+con	/man/2/tkclient
+con	/man/2/w3c-xpointers
+con	/man/2/wmclient
+concat	/man/2/lists
+concatblock	/man/10/allocb
+concatenate	/man/1/cat
+concatenate	/man/1/mash
+concatenated	/man/1/ar
+concatenated	/man/1/mash
+concatenated	/man/1/sh
+concatenated	/man/1/sh-string
+concatenated	/man/10/iar
+concatenated	/man/6/regexp
+concatenates	/man/1/cat
+concatenates	/man/1/mash
+concatenates	/man/1/sh
+concatenating	/man/2/attrdb
+concatenating	/man/2/spree
+concatenating	/man/6/attrdb
+concatenating	/man/9/options
+concatenation	/man/1/m4
+concatenation	/man/1/mash
+concatenation	/man/1/sh
+concatenation	/man/1/sh-std
+concatenation	/man/2/keyring-0intro
+concatenation	/man/2/keyring-sha1
+concatenation	/man/2/w3c-css
+concatenation	/man/6/keytext
+concatenations	/man/2/sys-0intro
+conceal	/man/2/spree-cardlib
+concealed	/man/2/spree
+concealed	/man/4/spree
+conceals	/man/2/spree
+conceals	/man/4/spree
+concentrate	/man/10/devattach
+concentrating	/man/6/colour
+concentrators	/man/10/plan9.ini
+concepts	/man/1/0intro
+conceptually	/man/3/draw
+concerns	/man/1/tiny
+conclusion	/man/2/draw-image
+concrete	/man/2/w3c-css
+concrete	/man/2/w3c-xpointers
+concurrent	/man/1/0intro
+concurrent	/man/1/charon
+concurrent	/man/1/sh
+concurrent	/man/1/sh-file2chan
+concurrent	/man/10/qio
+concurrent	/man/10/qlock
+concurrent	/man/10/ref
+concurrent	/man/2/dbm
+concurrent	/man/3/pipe
+concurrently	/man/1/blur
+concurrently	/man/1/charon
+concurrently	/man/2/bufio
+concurrently	/man/2/styxflush
+concurrently	/man/2/sys-0intro
+concurrently	/man/2/sys-read
+condition	/man/1/alphabet-fs
+condition	/man/1/bind
+condition	/man/1/cprof
+condition	/man/1/fs
+condition	/man/1/sh
+condition	/man/1/sh-std
+condition	/man/10/error
+condition	/man/10/sleep
+condition	/man/2/lists
+condition	/man/2/sys-bind
+condition	/man/3/eia
+conditional	/man/1/alphabet-fs
+conditional	/man/1/calc
+conditional	/man/1/fs
+conditional	/man/1/sh
+conditional	/man/3/dbg
+conditionals	/man/1/acme
+conditions	/man/1/mk
+conditions	/man/10/error
+conditions	/man/10/mk
+conditions	/man/2/draw-image
+conditions	/man/2/keyset
+conditions	/man/2/styxservers
+conditions	/man/3/usb
+conditions	/man/5/flush
+conditions	/man/5/stat
+conduit	/man/2/palmfile
+conf	/man/10/conf
+conf	/man/2/dhcpclient
+conf	/man/3/dbg
+conf	/man/3/root
+confidential	/man/2/security-0intro
+confidentiality	/man/2/security-0intro
+config	/man/1/charon
+config	/man/1/tktester
+config	/man/1/webgrab
+config	/man/10/dev
+config	/man/2/print
+config	/man/3/fpga
+config	/man/3/logfs
+config	/man/3/sd
+config.sys	/man/10/plan9.ini
+configfile	/man/1/itest
+configs	/man/2/dhcpclient
+configurability	/man/10/plan9.ini
+configurable	/man/10/plan9.ini
+configurable	/man/3/ip
+configurable	/man/3/pnp
+configuration	/man/1/charon
+configuration	/man/1/collab
+configuration	/man/1/deb
+configuration	/man/1/itest
+configuration	/man/1/mk
+configuration	/man/1/mux
+configuration	/man/1/tiny
+configuration	/man/1/toolbar
+configuration	/man/1/webgrab
+configuration	/man/10/0intro
+configuration	/man/10/9load
+configuration	/man/10/conf
+configuration	/man/10/dev
+configuration	/man/10/mk
+configuration	/man/10/panic
+configuration	/man/10/plan9.ini
+configuration	/man/2/cfg
+configuration	/man/2/dhcpclient
+configuration	/man/2/print
+configuration	/man/2/tftp
+configuration	/man/3/cons
+configuration	/man/3/dbg
+configuration	/man/3/ds
+configuration	/man/3/fpga
+configuration	/man/3/ip
+configuration	/man/3/logfs
+configuration	/man/3/mpeg
+configuration	/man/3/pnp
+configuration	/man/3/sd
+configuration	/man/3/usb
+configuration	/man/3/vga
+configuration	/man/3/vid
+configuration	/man/4/grid-cpu
+configuration	/man/4/namespace
+configuration	/man/6/attrdb
+configuration	/man/6/ndb
+configuration	/man/8/0intro
+configuration	/man/8/bootpd
+configuration	/man/8/collabsrv
+configuration	/man/8/cs
+configuration	/man/8/dhcp
+configuration	/man/8/dns
+configuration	/man/8/fpgaload
+configuration	/man/8/getauthinfo
+configuration	/man/8/init
+configuration	/man/8/prep
+configuration	/man/9/button
+configuration	/man/9/canvas
+configuration	/man/9/checkbutton
+configuration	/man/9/choicebutton
+configuration	/man/9/entry
+configuration	/man/9/frame
+configuration	/man/9/grid
+configuration	/man/9/label
+configuration	/man/9/listbox
+configuration	/man/9/menu
+configuration	/man/9/menubutton
+configuration	/man/9/options
+configuration	/man/9/pack
+configuration	/man/9/panel
+configuration	/man/9/radiobutton
+configuration	/man/9/scale
+configuration	/man/9/scrollbar
+configuration	/man/9/text
+configurations	/man/1/wish
+configurations	/man/10/conf
+configurations	/man/10/master
+configurations	/man/10/plan9.ini
+configurations	/man/3/ip
+configurations	/man/6/ndb
+configure	/man/1/emu
+configure	/man/1/grid-session
+configure	/man/1/tktester
+configure	/man/10/plan9.ini
+configure	/man/2/dhcpclient
+configure	/man/2/sys-stat
+configure	/man/3/vga
+configure	/man/4/grid-cpu
+configure	/man/8/0intro
+configure	/man/8/dhcp
+configure	/man/8/fpgaload
+configure	/man/8/httpd
+configure	/man/8/rip
+configure	/man/8/svc
+configure	/man/9/bind
+configure	/man/9/button
+configure	/man/9/canvas
+configure	/man/9/checkbutton
+configure	/man/9/choicebutton
+configure	/man/9/entry
+configure	/man/9/frame
+configure	/man/9/grid
+configure	/man/9/label
+configure	/man/9/listbox
+configure	/man/9/menu
+configure	/man/9/menubutton
+configure	/man/9/options
+configure	/man/9/pack
+configure	/man/9/panel
+configure	/man/9/radiobutton
+configure	/man/9/scale
+configure	/man/9/scrollbar
+configure	/man/9/text
+configured	/man/1/charon
+configured	/man/1/collab-clients
+configured	/man/1/grid-session
+configured	/man/1/secstore
+configured	/man/10/dev
+configured	/man/10/dmainit
+configured	/man/10/intrenable
+configured	/man/10/master
+configured	/man/10/odbc
+configured	/man/10/plan9.ini
+configured	/man/2/dhcpclient
+configured	/man/2/secstore
+configured	/man/3/cons
+configured	/man/3/dbg
+configured	/man/3/ds
+configured	/man/3/fpga
+configured	/man/3/indir
+configured	/man/3/ip
+configured	/man/3/logfs
+configured	/man/3/pnp
+configured	/man/3/prog
+configured	/man/3/root
+configured	/man/3/sign
+configured	/man/3/snarf
+configured	/man/3/tls
+configured	/man/3/usb
+configured	/man/6/audio
+configured	/man/7/dbsrv
+configured	/man/8/collabsrv
+configured	/man/8/register
+configured	/man/9/bind
+configured	/man/9/checkbutton
+configured	/man/9/choicebutton
+configured	/man/9/grid
+configured	/man/9/menu
+configured	/man/9/radiobutton
+configures	/man/1/toolbar
+configures	/man/10/plan9.ini
+configures	/man/3/fpga
+configures	/man/3/logfs
+configures	/man/3/vga
+configures	/man/8/dhcp
+configures	/man/8/fpgaload
+configuring	/man/1/grid-session
+configuring	/man/10/qio
+configuring	/man/2/dividers
+configuring	/man/3/pnp
+confine	/man/9/canvas
+confine	/man/9/grab
+confined	/man/3/draw
+confirmation	/man/4/factotum
+confirmed	/man/1/passwd
+confirmed	/man/8/svc
+conflict	/man/10/2c
+conflict	/man/10/acid
+conflict	/man/3/pnp
+conflict	/man/8/applylog
+conflict	/man/9/text
+conflicts	/man/1/yacc
+conflicts	/man/3/pnp
+conflicts	/man/8/applylog
+conform	/man/1/bind
+conform	/man/1/sh
+conform	/man/1/sh-alphabet
+conform	/man/2/alphabet-intro
+conform	/man/2/filter-deflate
+conform	/man/9/cursor
+conformance	/man/10/2c
+conforming	/man/2/format
+conforms	/man/2/convcs
+confound	/man/2/sys-0intro
+confused	/man/1/0intro
+confused	/man/1/charon
+confused	/man/2/sys-0intro
+confused	/man/6/image
+confused	/man/8/prep
+confusing	/man/1/0intro
+confusing	/man/1/sh-expr
+confusion	/man/4/dbfs
+congruent	/man/2/draw-display
+congruent	/man/3/draw
+conjunction	/man/1/grid-ns
+conjunction	/man/1/mdb
+conjunction	/man/1/mk
+conjunction	/man/10/mk
+conjunction	/man/2/styxservers-nametree
+conjunction	/man/4/acme
+conjunction	/man/4/factotum
+conjunction	/man/4/grid-cpu
+conjunction	/man/7/db
+conjunction	/man/8/prep
+conjunction	/man/9/text
+conn	/man/2/secstore
+connect	/man/1/acme
+connect	/man/1/alphabet-abc
+connect	/man/1/alphabet-grid
+connect	/man/1/collab
+connect	/man/1/collab-clients
+connect	/man/1/miniterm
+connect	/man/1/secstore
+connect	/man/1/spree-join
+connect	/man/10/odbc
+connect	/man/2/registries
+connect	/man/2/secstore
+connect	/man/2/security-0intro
+connect	/man/2/security-ssl
+connect	/man/2/sys-pipe
+connect	/man/2/wmlib
+connect	/man/3/ether
+connect	/man/3/ip
+connect	/man/3/mnt
+connect	/man/3/plap
+connect	/man/4/ftpfs
+connect	/man/4/keysrv
+connect	/man/7/db
+connect	/man/8/collabsrv
+connect	/man/8/cs
+connect	/man/8/mangaload
+connect.b	/man/1/collab
+connected	/man/1/alphabet-main
+connected	/man/1/avr
+connected	/man/1/mash
+connected	/man/1/miniterm
+connected	/man/1/sh
+connected	/man/1/sh-std
+connected	/man/1/stream
+connected	/man/1/wm
+connected	/man/2/draw-context
+connected	/man/2/prefab-compound
+connected	/man/2/styxconv
+connected	/man/2/sys-bind
+connected	/man/2/sys-file2chan
+connected	/man/3/ip
+connected	/man/3/pipe
+connected	/man/3/plap
+connected	/man/3/srv9
+connected	/man/4/dossrv
+connected	/man/4/palmsrv
+connected	/man/8/collabsrv
+connected	/man/8/rip
+connected	/man/8/rstyxd
+connected	/man/9/canvas
+connecting	/man/1/miniterm
+connecting	/man/10/devattach
+connecting	/man/10/plan9.ini
+connecting	/man/8/cs
+connecting	/man/9/canvas
+connection	/man/1/alphabet-main
+connection	/man/1/bind
+connection	/man/1/collab
+connection	/man/1/collab-clients
+connection	/man/1/dmview
+connection	/man/1/grid-monitor
+connection	/man/1/grid-register
+connection	/man/1/listen
+connection	/man/1/ls
+connection	/man/1/miniterm
+connection	/man/1/netstat
+connection	/man/1/ns
+connection	/man/1/passwd
+connection	/man/1/secstore
+connection	/man/1/telnet
+connection	/man/1/webgrab
+connection	/man/10/odbc
+connection	/man/2/dial
+connection	/man/2/draw-0intro
+connection	/man/2/draw-context
+connection	/man/2/draw-display
+connection	/man/2/draw-example
+connection	/man/2/draw-image
+connection	/man/2/drawmux
+connection	/man/2/factotum
+connection	/man/2/keyring-0intro
+connection	/man/2/keyring-auth
+connection	/man/2/keyring-getmsg
+connection	/man/2/keyring-ipint
+connection	/man/2/msgio
+connection	/man/2/plumbmsg
+connection	/man/2/pop3
+connection	/man/2/registries
+connection	/man/2/scsiio
+connection	/man/2/secstore
+connection	/man/2/security-0intro
+connection	/man/2/security-auth
+connection	/man/2/security-ssl
+connection	/man/2/sexprs
+connection	/man/2/smtp
+connection	/man/2/styxconv
+connection	/man/2/styxpersist
+connection	/man/2/styxservers
+connection	/man/2/sys-bind
+connection	/man/2/sys-dial
+connection	/man/2/sys-export
+connection	/man/2/sys-fauth
+connection	/man/2/sys-fversion
+connection	/man/2/tkclient
+connection	/man/2/venti
+connection	/man/2/wmclient
+connection	/man/2/wmlib
+connection	/man/2/wmsrv
+connection	/man/3/cmd
+connection	/man/3/dbg
+connection	/man/3/draw
+connection	/man/3/ether
+connection	/man/3/ip
+connection	/man/3/mnt
+connection	/man/3/plap
+connection	/man/3/ssl
+connection	/man/3/tls
+connection	/man/4/export
+connection	/man/4/factotum
+connection	/man/4/ftpfs
+connection	/man/4/grid-cpu
+connection	/man/4/import
+connection	/man/4/keysrv
+connection	/man/4/lockfs
+connection	/man/4/mntgen
+connection	/man/4/palmsrv
+connection	/man/4/registry
+connection	/man/4/vacfs
+connection	/man/5/0intro
+connection	/man/5/attach
+connection	/man/5/version
+connection	/man/7/db
+connection	/man/8/collabsrv
+connection	/man/8/cs
+connection	/man/8/rstyxd
+connection	/man/8/styxchat
+connection	/man/8/virgild
+connection's	/man/1/listen
+connection's	/man/2/security-ssl
+connectionless	/man/3/ip
+connections	/man/1/bind
+connections	/man/1/grid-monitor
+connections	/man/1/grid-ns
+connections	/man/1/grid-register
+connections	/man/1/listen
+connections	/man/1/netstat
+connections	/man/10/odbc
+connections	/man/2/dial
+connections	/man/2/keyring-getmsg
+connections	/man/2/keyring-getstring
+connections	/man/2/msgio
+connections	/man/2/plumbmsg
+connections	/man/2/registries
+connections	/man/2/security-auth
+connections	/man/2/sys-dial
+connections	/man/2/sys-stat
+connections	/man/3/ip
+connections	/man/3/plap
+connections	/man/3/ssl
+connections	/man/4/grid-cpu
+connections	/man/4/import
+connections	/man/4/lockfs
+connections	/man/4/palmsrv
+connections	/man/4/registry
+connects	/man/1/alphabet-abc
+connects	/man/1/alphabet-grid
+connects	/man/1/miniterm
+connects	/man/1/passwd
+connects	/man/1/secstore
+connects	/man/1/sendmail
+connects	/man/1/sh
+connects	/man/1/telnet
+connects	/man/1/webgrab
+connects	/man/10/styxserver
+connects	/man/2/dial
+connects	/man/2/security-0intro
+connects	/man/2/security-login
+connects	/man/3/draw
+connects	/man/4/ftpfs
+connects	/man/8/collabsrv
+connects	/man/8/register
+connfd	/man/10/styxserver
+connfd	/man/2/draw-context
+conninfo	/man/2/dial
+cons	/man/1/9win
+cons	/man/1/cd
+cons	/man/1/charon
+cons	/man/1/crypt
+cons	/man/1/date
+cons	/man/1/emu
+cons	/man/1/ftest
+cons	/man/1/logon
+cons	/man/1/mash
+cons	/man/1/netkey
+cons	/man/1/runas
+cons	/man/1/tiny
+cons	/man/1/tkcmd
+cons	/man/1/wm-sh
+cons	/man/10/dev
+cons	/man/10/kbdputc
+cons	/man/10/kproc
+cons	/man/10/master
+cons	/man/2/daytime
+cons	/man/2/keyring-auth
+cons	/man/2/security-random
+cons	/man/3/boot
+cons	/man/3/cap
+cons	/man/3/cons
+cons	/man/3/indir
+cons	/man/3/rtc
+cons	/man/4/acme
+cons	/man/4/namespace
+cons	/man/4/spree
+cons	/man/6/keyboard
+cons	/man/6/scancode
+cons	/man/8/getauthinfo
+cons	/man/8/rstyxd
+cons	/man/8/shutdown
+cons	/man/8/sntp
+cons	/man/8/styxchat
+consattach	/man/10/dev
+consclose	/man/10/dev
+consctl	/man/1/9win
+consctl	/man/1/wm-sh
+consctl	/man/3/cons
+consctl	/man/4/acme
+consdevtab	/man/10/dev
+consecutive	/man/1/sh-file2chan
+consecutive	/man/1/strings
+consecutive	/man/10/acid
+consecutive	/man/10/print
+consecutive	/man/3/prog
+consecutively	/man/1/sh-regex
+consequence	/man/2/bufio
+consequence	/man/6/sexprs
+consequences	/man/10/error
+consequential	/man/9/1copyright
+consequently	/man/10/dev
+consequently	/man/10/devattach
+consequently	/man/10/xalloc
+consequently	/man/2/asn1
+consequently	/man/2/palmfile
+consequently	/man/8/cs
+conserve	/man/3/fpga
+consider	/man/1/math-misc
+consider	/man/2/0intro
+consider	/man/2/asn1
+consider	/man/5/remove
+consider	/man/5/stat
+consider	/man/8/applylog
+consideration	/man/8/applylog
+considerations	/man/1/sh-std
+considerations	/man/10/9load
+considerations	/man/2/keyring-0intro
+considerations	/man/2/security-0intro
+considerations	/man/8/prep
+considered	/man/1/cal
+considered	/man/1/mk
+considered	/man/1/sh-file2chan
+considered	/man/10/mk
+considered	/man/10/rune
+considered	/man/10/strcat
+considered	/man/2/draw-rect
+considered	/man/2/factotum
+considered	/man/2/geodesy
+considered	/man/2/keyset
+considered	/man/2/lists
+considered	/man/2/sys-print
+considered	/man/2/sys-stat
+considered	/man/5/flush
+considered	/man/6/keytext
+considered	/man/9/canvas
+considered	/man/9/text
+considers	/man/10/styxserver
+consinit	/man/10/dev
+consist	/man/1/m4
+consist	/man/1/yacc
+consist	/man/10/plan9.ini
+consist	/man/2/devpointer
+consist	/man/2/spree-cardlib
+consist	/man/2/sys-0intro
+consist	/man/4/factotum
+consist	/man/8/prep
+consist	/man/9/canvas
+consist	/man/9/grid
+consist	/man/9/pack
+consistency	/man/10/allocb
+consistency	/man/2/ida
+consistency	/man/2/sys-0intro
+consistency	/man/5/stat
+consistency	/man/6/colour
+consistency	/man/9/text
+consistent	/man/1/0intro
+consistent	/man/10/0intro
+consistent	/man/2/draw-example
+consistent	/man/2/ida
+consistent	/man/2/w3c-xpointers
+consistent	/man/3/fs
+consistent	/man/3/touch
+consistent	/man/4/ftpfs
+consistent	/man/9/listbox
+consistently	/man/4/logfile
+consistently	/man/6/font
+consistently	/man/8/touchcal
+consistently	/man/9/0intro
+consisting	/man/1/bind
+consisting	/man/1/cprof
+consisting	/man/1/diff
+consisting	/man/1/sh-alphabet
+consisting	/man/1/sh-regex
+consisting	/man/1/tiny
+consisting	/man/2/spki
+consisting	/man/2/spree-allow
+consisting	/man/2/spree-gather
+consisting	/man/2/sys-bind
+consisting	/man/4/memfs
+consisting	/man/4/spree
+consisting	/man/6/audio
+consisting	/man/9/canvas
+consisting	/man/9/scrollbar
+consists	/man/1/calc
+consists	/man/1/mdb
+consists	/man/1/mk
+consists	/man/1/ps
+consists	/man/1/sh-alphabet
+consists	/man/1/sh-tk
+consists	/man/1/tiny
+consists	/man/10/a.out
+consists	/man/10/mk
+consists	/man/10/odbc
+consists	/man/2/convcs
+consists	/man/2/dis
+consists	/man/2/format
+consists	/man/2/prof
+consists	/man/2/spree-cardlib
+consists	/man/2/string
+consists	/man/3/draw
+consists	/man/3/vga
+consists	/man/4/factotum
+consists	/man/4/iostats
+consists	/man/5/0intro
+consists	/man/6/attrdb
+consists	/man/6/dis
+consists	/man/6/font
+consists	/man/6/image
+consists	/man/6/sbl
+consists	/man/8/prep
+consists	/man/9/canvas
+consists	/man/9/grid
+consists	/man/9/options
+consists	/man/9/scale
+consists	/man/9/scrollbar
+consists	/man/9/text
+console	/man/1/brutus
+console	/man/1/grid-monitor
+console	/man/1/mux
+console	/man/1/tiny
+console	/man/1/wish
+console	/man/1/wm-sh
+console	/man/10/9load
+console	/man/10/devattach
+console	/man/10/eve
+console	/man/10/kproc
+console	/man/10/lock
+console	/man/10/panic
+console	/man/10/plan9.ini
+console	/man/10/xalloc
+console	/man/2/security-0intro
+console	/man/2/sys-read
+console	/man/3/0intro
+console	/man/3/cons
+console	/man/4/acme
+console	/man/4/keyfs
+console	/man/6/users
+console	/man/8/mangaload
+console	/man/8/rdbgsrv
+console	/man/9/send
+consopen	/man/10/dev
+consortium's	/man/2/w3c-css
+consortium's	/man/2/w3c-xpointers
+conspicuous	/man/6/colour
+consread	/man/10/dev
+consstat	/man/10/dev
+constant	/man/1/brutus
+constant	/man/1/charon
+constant	/man/1/gettar
+constant	/man/1/limbo
+constant	/man/10/a.out
+constant	/man/10/atoi
+constant	/man/10/c2l
+constant	/man/10/readnum
+constant	/man/10/styx
+constant	/man/2/dis
+constant	/man/2/draw-0intro
+constant	/man/2/factotum
+constant	/man/2/format
+constant	/man/2/keyring-crypt
+constant	/man/2/math-0intro
+constant	/man/2/math-linalg
+constant	/man/2/prefab-element
+constant	/man/2/security-0intro
+constant	/man/2/sexprs
+constant	/man/2/styx
+constant	/man/2/sys-self
+constant	/man/2/sys-stat
+constant	/man/2/ubfa
+constant	/man/3/ether
+constant	/man/5/0intro
+constant	/man/5/error
+constant	/man/5/walk
+constant	/man/6/colour
+constant	/man/6/man
+constant's	/man/2/ubfa
+constants	/man/1/calc
+constants	/man/1/units
+constants	/man/1/yacc
+constants	/man/10/c2l
+constants	/man/10/newchan
+constants	/man/10/seconds
+constants	/man/2/0intro
+constants	/man/2/debug
+constants	/man/2/dhcpclient
+constants	/man/2/dis
+constants	/man/2/draw-0intro
+constants	/man/2/draw-display
+constants	/man/2/format
+constants	/man/2/ir
+constants	/man/2/math-0intro
+constants	/man/2/math-fp
+constants	/man/2/sets
+constants	/man/2/sh
+constants	/man/2/spree-cardlib
+constants	/man/2/styx
+constants	/man/2/styxservers
+constants	/man/2/sys-stat
+constants	/man/2/tkclient
+constants	/man/2/ubfa
+constants	/man/2/venti
+constants	/man/2/w3c-xpointers
+constants	/man/2/wmclient
+constants	/man/3/draw
+constants	/man/5/0intro
+constants	/man/6/ubfa
+constants	/man/8/httpd
+constants	/man/8/prep
+constituent	/man/1/ar
+constituent	/man/1/sh-regex
+constituent	/man/10/ar
+constituent	/man/10/iar
+constituent	/man/2/prefab-element
+constituent	/man/2/sys-bind
+constituent	/man/2/sys-open
+constituents	/man/1/ar
+constituents	/man/10/iar
+constr	/man/2/asn1
+constrained	/man/2/asn1
+constrained	/man/9/canvas
+constraining	/man/6/login
+constrains	/man/3/ftl
+constraints	/man/1/tiny
+constraints	/man/10/9load
+constraints	/man/10/allocb
+constraints	/man/10/kproc
+constraints	/man/10/xalloc
+constraints	/man/2/asn1
+constraints	/man/2/disks
+constraints	/man/2/sys-fversion
+constraints	/man/3/flash
+constraints	/man/9/canvas
+construct	/man/2/keyring-0intro
+construct	/man/3/prog
+construct	/man/6/namespace
+construct	/man/8/register
+constructed	/man/1/listen
+constructed	/man/2/asn1
+constructed	/man/2/stringinttab
+constructed	/man/6/font
+constructing	/man/1/wm-sh
+construction	/man/1/ftree
+construction	/man/1/mash
+construction	/man/1/ns
+construction	/man/1/sh
+construction	/man/1/yacc
+construction	/man/2/palmfile
+construction	/man/2/prefab-element
+construction	/man/6/sbl
+construction	/man/9/menu
+construction's	/man/1/mash
+construction's	/man/1/sh
+constructions	/man/1/mash
+constructions	/man/1/sh
+constructor	/man/2/0intro
+constructs	/man/1/ebook
+constructs	/man/1/sh-std
+constructs	/man/10/c2l
+constructs	/man/2/asn1
+constructs	/man/2/cfg
+constructs	/man/8/logind
+constructs	/man/8/signer
+consulted	/man/1/alphabet-fs
+consulted	/man/1/fs
+consulted	/man/10/plan9.ini
+consulted	/man/2/scsiio
+consulted	/man/5/0intro
+consulting	/man/2/scsiio
+consults	/man/1/look
+consults	/man/10/plan9.ini
+consults	/man/8/dns
+consume	/man/3/logfs
+consume	/man/9/pack
+consumed	/man/10/styxserver
+consumed	/man/2/convcs
+consumed	/man/2/filter
+consumed	/man/2/rfc822
+consumed	/man/2/sys-byte2char
+consumed	/man/2/wmclient
+consumed	/man/2/wmsrv
+consumed	/man/3/ip
+consumed	/man/9/frame
+consumer	/man/3/pipe
+consumers	/man/10/qio
+consumes	/man/2/sys-byte2char
+conswalk	/man/10/dev
+conswrite	/man/10/dev
+cont	/man/10/acid
+cont	/man/2/debug
+cont	/man/3/prog
+contact	/man/8/getauthinfo
+contained	/man/1/acme
+contained	/man/1/alphabet-fs
+contained	/man/1/fs
+contained	/man/1/gettar
+contained	/man/1/grid-ns
+contained	/man/1/secstore
+contained	/man/1/stack
+contained	/man/10/allocb
+contained	/man/10/dev
+contained	/man/10/plan9.ini
+contained	/man/2/0intro
+contained	/man/2/draw-example
+contained	/man/2/prefab-compound
+contained	/man/2/sets
+contained	/man/2/sh
+contained	/man/2/spree-cardlib
+contained	/man/2/tabs
+contained	/man/2/xml
+contained	/man/3/ip
+contained	/man/3/sd
+contained	/man/6/auth
+contained	/man/6/keyboard
+contained	/man/8/bootpd
+container	/man/1/deb
+container	/man/9/frame
+contains	/man/1/0intro
+contains	/man/1/acme
+contains	/man/1/basename
+contains	/man/1/bind
+contains	/man/1/blur
+contains	/man/1/collab
+contains	/man/1/collab-clients
+contains	/man/1/deb
+contains	/man/1/diff
+contains	/man/1/dmview
+contains	/man/1/ftree
+contains	/man/1/gettar
+contains	/man/1/grep
+contains	/man/1/grid-session
+contains	/man/1/itest
+contains	/man/1/limbo
+contains	/man/1/mash
+contains	/man/1/mash-tk
+contains	/man/1/mc
+contains	/man/1/mk
+contains	/man/1/secstore
+contains	/man/1/sh
+contains	/man/1/sh-csv
+contains	/man/1/sh-regex
+contains	/man/1/sh-sexprs
+contains	/man/1/sh-string
+contains	/man/1/tktester
+contains	/man/1/tsort
+contains	/man/1/webgrab
+contains	/man/10/9load
+contains	/man/10/a.out
+contains	/man/10/ar
+contains	/man/10/dev
+contains	/man/10/dynld
+contains	/man/10/getfields
+contains	/man/10/intrenable
+contains	/man/10/mk
+contains	/man/10/odbc
+contains	/man/10/plan9.ini
+contains	/man/10/print
+contains	/man/10/qio
+contains	/man/10/rune
+contains	/man/10/styxserver
+contains	/man/2/alphabet-intro
+contains	/man/2/asn1
+contains	/man/2/attrdb
+contains	/man/2/csv
+contains	/man/2/dbm
+contains	/man/2/debug
+contains	/man/2/dhcpclient
+contains	/man/2/dial
+contains	/man/2/dialog
+contains	/man/2/dis
+contains	/man/2/draw-0intro
+contains	/man/2/draw-context
+contains	/man/2/draw-display
+contains	/man/2/draw-rect
+contains	/man/2/exception
+contains	/man/2/factotum
+contains	/man/2/filter
+contains	/man/2/filter-deflate
+contains	/man/2/format
+contains	/man/2/hash
+contains	/man/2/ip
+contains	/man/2/json
+contains	/man/2/keyring-0intro
+contains	/man/2/keyring-gensk
+contains	/man/2/keyring-getmsg
+contains	/man/2/lists
+contains	/man/2/msgio
+contains	/man/2/newns
+contains	/man/2/palmfile
+contains	/man/2/plumbmsg
+contains	/man/2/prefab-0intro
+contains	/man/2/prefab-element
+contains	/man/2/print
+contains	/man/2/prof
+contains	/man/2/registries
+contains	/man/2/rfc822
+contains	/man/2/secstore
+contains	/man/2/security-login
+contains	/man/2/security-ssl
+contains	/man/2/sexprs
+contains	/man/2/spki
+contains	/man/2/spree
+contains	/man/2/spree-cardlib
+contains	/man/2/string
+contains	/man/2/styx
+contains	/man/2/styxservers
+contains	/man/2/sys-0intro
+contains	/man/2/sys-dial
+contains	/man/2/sys-file2chan
+contains	/man/2/sys-print
+contains	/man/2/sys-tokenize
+contains	/man/2/tabs
+contains	/man/2/ubfa
+contains	/man/2/venti
+contains	/man/2/wmclient
+contains	/man/2/wmsrv
+contains	/man/2/xml
+contains	/man/3/arch
+contains	/man/3/cmd
+contains	/man/3/cons
+contains	/man/3/draw
+contains	/man/3/ds
+contains	/man/3/dynld
+contains	/man/3/ether
+contains	/man/3/flash
+contains	/man/3/fpga
+contains	/man/3/ip
+contains	/man/3/pbus
+contains	/man/3/plap
+contains	/man/3/pnp
+contains	/man/3/prof
+contains	/man/3/prog
+contains	/man/3/sd
+contains	/man/3/sign
+contains	/man/3/ssl
+contains	/man/3/tls
+contains	/man/3/touch
+contains	/man/3/usb
+contains	/man/4/factotum
+contains	/man/4/keysrv
+contains	/man/4/lockfs
+contains	/man/4/namespace
+contains	/man/4/registry
+contains	/man/5/0intro
+contains	/man/5/attach
+contains	/man/5/stat
+contains	/man/5/version
+contains	/man/6/attrdb
+contains	/man/6/audio
+contains	/man/6/colour
+contains	/man/6/dis
+contains	/man/6/font
+contains	/man/6/image
+contains	/man/6/keys
+contains	/man/6/keytext
+contains	/man/6/namespace
+contains	/man/6/proto
+contains	/man/6/sbl
+contains	/man/6/sexprs
+contains	/man/6/ubfa
+contains	/man/7/db
+contains	/man/8/bootpd
+contains	/man/8/collabsrv
+contains	/man/8/cs
+contains	/man/8/dns
+contains	/man/8/getauthinfo
+contains	/man/8/mkfs
+contains	/man/8/prep
+contains	/man/8/rstyxd
+contains	/man/8/signer
+contains	/man/8/styxchat
+contains	/man/8/svc
+contains	/man/9/bind
+contains	/man/9/button
+contains	/man/9/canvas
+contains	/man/9/checkbutton
+contains	/man/9/grid
+contains	/man/9/label
+contains	/man/9/menubutton
+contains	/man/9/radiobutton
+contains	/man/9/text
+contemporaries	/man/1/0intro
+content	/man/1/alphabet-fs
+content	/man/1/fs
+content	/man/1/mux
+content	/man/1/webgrab
+content	/man/10/mk
+content	/man/2/dbm
+content	/man/2/format
+content	/man/2/keyring-getmsg
+content	/man/2/msgio
+content	/man/2/palmfile
+content	/man/2/rfc822
+content	/man/2/sys-stat
+content	/man/2/venti
+content	/man/2/w3c-css
+content	/man/3/ftl
+content	/man/3/root
+content	/man/3/rtc
+content	/man/5/stat
+content	/man/6/ndb
+content	/man/6/proto
+content	/man/8/applylog
+content	/man/8/httpd
+content	/man/9/0intro
+context	/man/1/acme
+context	/man/1/alphabet-fs
+context	/man/1/fs
+context	/man/1/listen
+context	/man/1/sh
+context	/man/1/sh-std
+context	/man/1/sh-tk
+context	/man/1/tkcmd
+context	/man/1/wm
+context	/man/1/yacc
+context	/man/10/2c
+context	/man/10/dev
+context	/man/2/arg
+context	/man/2/asn1
+context	/man/2/command
+context	/man/2/debug
+context	/man/2/dialog
+context	/man/2/draw-0intro
+context	/man/2/draw-context
+context	/man/2/draw-example
+context	/man/2/draw-pointer
+context	/man/2/ir
+context	/man/2/itslib
+context	/man/2/prefab-compound
+context	/man/2/selectfile
+context	/man/2/sh
+context	/man/2/styxservers-nametree
+context	/man/2/sys-dup
+context	/man/2/sys-self
+context	/man/2/tabs
+context	/man/2/tk
+context	/man/2/tkclient
+context	/man/2/translate
+context	/man/2/volume
+context	/man/2/wmclient
+context	/man/2/wmlib
+context	/man/2/wmsrv
+context	/man/4/acme
+context	/man/6/keytext
+context	/man/6/translate
+context	/man/7/db
+context	/man/9/0intro
+context.cinput	/man/2/ir
+context.ctomux	/man/2/ir
+context.new	/man/2/sh
+contexts	/man/2/draw-0intro
+contexts	/man/2/draw-context
+contiguous	/man/10/allocb
+contiguous	/man/2/draw-font
+contiguous	/man/2/math-linalg
+contiguous	/man/6/colour
+contiguous	/man/6/image
+contiguous	/man/8/prep
+contiguous	/man/9/entry
+contiguous	/man/9/text
+contiguously	/man/10/9load
+contiguously	/man/6/colour
+contiguously	/man/8/prep
+contin	/man/1/mash
+continually	/man/2/devpointer
+continually	/man/2/sys-read
+continuation	/man/1/mash
+continuation	/man/1/strings
+continuation	/man/10/atoi
+continuation	/man/2/rfc822
+continue	/man/1/blur
+continue	/man/1/calc
+continue	/man/1/deb
+continue	/man/1/mk
+continue	/man/1/sh-csv
+continue	/man/1/sh-sexprs
+continue	/man/1/sh-std
+continue	/man/1/tail
+continue	/man/10/acid
+continue	/man/10/mk
+continue	/man/2/styxservers
+continue	/man/6/plumbing
+continue	/man/9/canvas
+continue	/man/9/grab
+continue	/man/9/image
+continued	/man/1/deb
+continued	/man/1/mash
+continues	/man/1/acme
+continues	/man/1/deb
+continues	/man/1/mkdir
+continues	/man/1/tiny
+continues	/man/1/wm-misc
+continues	/man/2/dhcpclient
+continues	/man/2/fsproto
+continues	/man/2/sys-0intro
+continues	/man/2/sys-read
+continues	/man/9/0intro
+continues	/man/9/text
+continuing	/man/1/acme
+continuing	/man/1/tiny
+continuing	/man/2/csv
+continuous	/man/6/colour
+continuously	/man/2/sys-millisec
+continuously	/man/9/options
+contracted	/man/2/math-0intro
+contrary	/man/2/dividers
+contrast	/man/1/blur
+contrast	/man/10/allocb
+contrast	/man/10/master
+contrast	/man/10/memory
+contrast	/man/10/qio
+contrast	/man/3/tv
+contrast	/man/9/text
+control	/man/1/0intro
+control	/man/1/acme
+control	/man/1/bind
+control	/man/1/brutus
+control	/man/1/calc
+control	/man/1/charon
+control	/man/1/kill
+control	/man/1/mash
+control	/man/1/mash-tk
+control	/man/1/miniterm
+control	/man/1/mk
+control	/man/1/mux
+control	/man/1/os
+control	/man/1/sh
+control	/man/1/sh-std
+control	/man/1/tktester
+control	/man/1/wm
+control	/man/10/9load
+control	/man/10/conf
+control	/man/10/devattach
+control	/man/10/error
+control	/man/10/lock
+control	/man/10/mk
+control	/man/10/odbc
+control	/man/10/print
+control	/man/10/qio
+control	/man/10/splhi
+control	/man/10/xalloc
+control	/man/2/dbm
+control	/man/2/debug
+control	/man/2/dhcpclient
+control	/man/2/dial
+control	/man/2/disks
+control	/man/2/draw-0intro
+control	/man/2/draw-context
+control	/man/2/draw-image
+control	/man/2/imagefile
+control	/man/2/ir
+control	/man/2/math-0intro
+control	/man/2/math-fp
+control	/man/2/mpeg
+control	/man/2/prefab-compound
+control	/man/2/prefab-element
+control	/man/2/prefab-style
+control	/man/2/secstore
+control	/man/2/security-ssl
+control	/man/2/sexprs
+control	/man/2/sys-0intro
+control	/man/2/sys-dial
+control	/man/2/sys-open
+control	/man/2/sys-pctl
+control	/man/2/sys-pipe
+control	/man/2/sys-print
+control	/man/2/tk
+control	/man/2/tkclient
+control	/man/2/volume
+control	/man/2/wmclient
+control	/man/3/arch
+control	/man/3/audio
+control	/man/3/boot
+control	/man/3/cons
+control	/man/3/dbg
+control	/man/3/draw
+control	/man/3/ds
+control	/man/3/dup
+control	/man/3/dynld
+control	/man/3/eia
+control	/man/3/ether
+control	/man/3/flash
+control	/man/3/floppy
+control	/man/3/fpga
+control	/man/3/fs
+control	/man/3/ftl
+control	/man/3/gpio
+control	/man/3/i2c
+control	/man/3/ip
+control	/man/3/logfs
+control	/man/3/lpt
+control	/man/3/mpeg
+control	/man/3/plap
+control	/man/3/pnp
+control	/man/3/prof
+control	/man/3/prog
+control	/man/3/sign
+control	/man/3/ssl
+control	/man/3/tls
+control	/man/3/touch
+control	/man/3/tv
+control	/man/3/usb
+control	/man/3/vga
+control	/man/3/vid
+control	/man/4/acme
+control	/man/4/factotum
+control	/man/4/import
+control	/man/4/kfs
+control	/man/4/registry
+control	/man/6/audio
+control	/man/6/keyboard
+control	/man/8/kfscmd
+control	/man/8/mangaload
+control	/man/9/bind
+control	/man/9/entry
+control	/man/9/text
+controlled	/man/1/ftree
+controlled	/man/1/mux
+controlled	/man/10/5cv
+controlled	/man/10/ntsrv
+controlled	/man/10/odbc
+controlled	/man/10/qio
+controlled	/man/10/qlock
+controlled	/man/2/0intro
+controlled	/man/2/ir
+controlled	/man/2/print
+controlled	/man/2/prof
+controlled	/man/2/spree
+controlled	/man/2/sys-pctl
+controlled	/man/2/tkclient
+controlled	/man/2/wmclient
+controlled	/man/3/ether
+controlled	/man/3/ip
+controlled	/man/3/plap
+controlled	/man/3/tls
+controlled	/man/9/menu
+controller	/man/1/wm
+controller	/man/10/intrenable
+controller	/man/10/plan9.ini
+controller	/man/2/volume
+controller	/man/3/pbus
+controller	/man/3/sd
+controller	/man/3/usb
+controller	/man/3/vga
+controller	/man/3/vid
+controller's	/man/3/usb
+controllers	/man/10/dmainit
+controllers	/man/10/plan9.ini
+controllers	/man/3/sd
+controllers	/man/3/vga
+controlling	/man/1/cook
+controlling	/man/1/toolbar
+controlling	/man/1/wm
+controlling	/man/2/debug
+controlling	/man/2/ir
+controlling	/man/2/spree
+controlling	/man/4/acme
+controlling	/man/8/collabsrv
+controls	/man/1/charon
+controls	/man/1/collab-clients
+controls	/man/1/ebook
+controls	/man/1/ftree
+controls	/man/1/mux
+controls	/man/2/prefab-0intro
+controls	/man/2/prefab-element
+controls	/man/2/spree-cardlib
+controls	/man/2/sys-bind
+controls	/man/2/sys-pctl
+controls	/man/2/tabs
+controls	/man/3/cmd
+controls	/man/3/ip
+controls	/man/3/kprof
+controls	/man/3/logfs
+controls	/man/3/ssl
+controls	/man/9/text
+conv	/man/1/dd
+convcs	/man/1/tcs
+convcs	/man/10/rune
+convcs	/man/2/convcs
+convcs	/man/2/encoding
+convcs.b	/man/2/convcs
+convcs.m	/man/2/convcs
+convd2m	/man/10/dev
+convd2m	/man/10/devattach
+convd2m	/man/10/styx
+convd2m	/man/5/stat
+convenience	/man/1/fs
+convenience	/man/1/sh-alphabet
+convenience	/man/1/tiny
+convenience	/man/2/convcs
+convenience	/man/2/disks
+convenience	/man/2/draw-image
+convenience	/man/2/format
+convenience	/man/2/ip
+convenience	/man/2/sexprs
+convenience	/man/2/styxservers
+convenience	/man/4/dbfs
+convenient	/man/1/collab-clients
+convenient	/man/10/styx
+convenient	/man/2/alphabet-intro
+convenient	/man/2/draw-0intro
+convenient	/man/2/keyring-ipint
+convenient	/man/2/spree-cardlib
+convenient	/man/2/styxservers
+convention	/man/1/acme
+convention	/man/1/yacc
+convention	/man/10/2c
+convention	/man/10/9load
+convention	/man/10/devattach
+convention	/man/2/command
+convention	/man/2/math-linalg
+convention	/man/2/sh
+convention	/man/2/sys-0intro
+convention	/man/2/sys-dial
+convention	/man/2/w3c-uris
+convention	/man/2/w3c-xpointers
+convention	/man/3/sd
+convention	/man/5/0intro
+convention	/man/5/error
+convention	/man/6/font
+convention	/man/6/users
+convention	/man/8/collabsrv
+convention	/man/8/prep
+convention	/man/9/0intro
+conventional	/man/1/0intro
+conventional	/man/1/ebook
+conventional	/man/1/limbo
+conventional	/man/10/0intro
+conventional	/man/10/qio
+conventional	/man/2/0intro
+conventional	/man/2/alphabet-intro
+conventional	/man/2/keyring-auth
+conventional	/man/2/palmfile
+conventional	/man/2/sh
+conventional	/man/2/spree-cardlib
+conventional	/man/2/styxpersist
+conventional	/man/2/translate
+conventional	/man/3/0intro
+conventional	/man/3/ftl
+conventional	/man/3/tinyfs
+conventional	/man/4/ftpfs
+conventional	/man/4/namespace
+conventional	/man/4/registry
+conventional	/man/5/stat
+conventional	/man/8/svc
+conventionally	/man/1/acme
+conventionally	/man/1/read
+conventionally	/man/10/2c
+conventionally	/man/10/conf
+conventionally	/man/2/alphabet-intro
+conventionally	/man/2/draw-context
+conventionally	/man/2/spree-allow
+conventionally	/man/2/spree-cardlib
+conventionally	/man/2/sys-fauth
+conventionally	/man/2/sys-read
+conventionally	/man/2/tk
+conventionally	/man/2/venti
+conventionally	/man/2/wmsrv
+conventionally	/man/3/cmd
+conventionally	/man/3/env
+conventionally	/man/3/ip
+conventionally	/man/3/srv
+conventionally	/man/4/spree
+conventionally	/man/6/dis
+conventionally	/man/6/sbl
+conventionally	/man/8/prep
+conventions	/man/1/0intro
+conventions	/man/1/acme
+conventions	/man/1/bind
+conventions	/man/1/crypt
+conventions	/man/2/daytime
+conventions	/man/2/factotum
+conventions	/man/2/keyring-sha1
+conventions	/man/2/msgio
+conventions	/man/2/palmfile
+conventions	/man/2/spree
+conventions	/man/2/string
+conventions	/man/2/sys-0intro
+conventions	/man/3/audio
+conventions	/man/3/cmd
+conventions	/man/3/flash
+conventions	/man/3/fs
+conventions	/man/3/gpio
+conventions	/man/3/plap
+conventions	/man/4/factotum
+conventions	/man/4/spree
+conventions	/man/6/0intro
+conventions	/man/6/keyboard
+conventions	/man/8/prep
+conventions	/man/8/rstyxd
+conventions	/man/8/styxchat
+conversation	/man/1/collab-clients
+conversation	/man/1/netstat
+conversation	/man/10/odbc
+conversation	/man/2/dial
+conversation	/man/2/factotum
+conversation	/man/2/keyring-0intro
+conversation	/man/2/keyring-auth
+conversation	/man/3/ether
+conversation	/man/3/ip
+conversation	/man/3/plap
+conversation	/man/6/login
+conversation	/man/8/styxmon
+conversations	/man/10/odbc
+conversations	/man/3/ether
+conversations	/man/3/ip
+converse	/man/1/sh-file2chan
+converse	/man/10/odbc
+converse	/man/3/ip
+conversely	/man/10/qio
+conversely	/man/2/dhcpclient
+conversing	/man/2/keyring-0intro
+conversion	/man/1/auplay
+conversion	/man/1/date
+conversion	/man/1/dd
+conversion	/man/1/man
+conversion	/man/1/sh-alphabet
+conversion	/man/1/sh-sexprs
+conversion	/man/1/units
+conversion	/man/10/5cv
+conversion	/man/10/a.out
+conversion	/man/10/print
+conversion	/man/10/rune
+conversion	/man/10/styx
+conversion	/man/2/asn1
+conversion	/man/2/convcs
+conversion	/man/2/daytime
+conversion	/man/2/keyring-auth
+conversion	/man/2/keyring-certtostr
+conversion	/man/2/math-0intro
+conversion	/man/2/math-export
+conversion	/man/2/sh
+conversion	/man/2/sys-print
+conversion	/man/6/utf
+conversions	/man/1/alphabet-main
+conversions	/man/1/dd
+conversions	/man/1/fs
+conversions	/man/1/sh-alphabet
+conversions	/man/1/units
+conversions	/man/10/c2l
+conversions	/man/10/print
+conversions	/man/10/seconds
+conversions	/man/2/convcs
+conversions	/man/2/daytime
+conversions	/man/2/math-0intro
+conversions	/man/2/sys-print
+conversions	/man/2/sys-stat
+conversions	/man/6/utf
+convert	/man/1/date
+convert	/man/1/dd
+convert	/man/1/diff
+convert	/man/1/fc
+convert	/man/1/sh-alphabet
+convert	/man/1/sh-regex
+convert	/man/1/units
+convert	/man/10/5cv
+convert	/man/10/acid
+convert	/man/10/atoi
+convert	/man/10/c2l
+convert	/man/10/dev
+convert	/man/10/ms2
+convert	/man/10/rune
+convert	/man/10/seconds
+convert	/man/10/styx
+convert	/man/2/asn1
+convert	/man/2/convcs
+convert	/man/2/draw-0intro
+convert	/man/2/geodesy
+convert	/man/2/ip
+convert	/man/2/keyring-certtostr
+convert	/man/2/math-export
+convert	/man/2/palmfile
+convert	/man/2/security-0intro
+convert	/man/2/sh
+convert	/man/2/spki
+convert	/man/2/spree
+convert	/man/2/styxconv
+convert	/man/2/sys-byte2char
+convert	/man/2/sys-dup
+convert	/man/3/cmd
+convert	/man/5/0intro
+convert	/man/5/stat
+convert	/man/6/keytext
+convert	/man/6/utf
+converted	/man/1/fc
+converted	/man/1/fs
+converted	/man/1/man
+converted	/man/1/sh
+converted	/man/1/sh-alphabet
+converted	/man/1/sh-sexprs
+converted	/man/1/sh-string
+converted	/man/1/tiny
+converted	/man/10/0intro
+converted	/man/10/5coff
+converted	/man/10/atoi
+converted	/man/10/devattach
+converted	/man/10/plan9.ini
+converted	/man/10/print
+converted	/man/2/asn1
+converted	/man/2/convcs
+converted	/man/2/imagefile
+converted	/man/2/keyring-0intro
+converted	/man/2/sexprs
+converted	/man/2/spki
+converted	/man/2/spree
+converted	/man/2/sys-print
+converted	/man/2/w3c-css
+converted	/man/2/w3c-uris
+converted	/man/2/w3c-xpointers
+converted	/man/2/xml
+converted	/man/3/mnt
+converted	/man/3/tls
+converted	/man/3/usb
+converted	/man/6/colour
+converted	/man/6/image
+converted	/man/6/man
+converted	/man/6/utf
+converted	/man/8/createsignerkey
+converted	/man/9/types
+converter	/man/1/cook
+converter	/man/10/5coff
+converter	/man/2/convcs
+converter	/man/3/vid
+converters	/man/1/tcs
+converters	/man/2/convcs
+convertible	/man/1/sh-alphabet
+converting	/man/1/mdb
+converting	/man/1/unicode
+converting	/man/2/convcs
+converting	/man/2/devpointer
+converting	/man/2/math-export
+converting	/man/2/styxconv
+converts	/man/1/alphabet-fs
+converts	/man/1/alphabet-main
+converts	/man/1/auplay
+converts	/man/1/fs
+converts	/man/1/gettar
+converts	/man/1/man
+converts	/man/1/tcs
+converts	/man/1/unicode
+converts	/man/1/units
+converts	/man/1/uuencode
+converts	/man/1/yacc
+converts	/man/10/5coff
+converts	/man/10/5cv
+converts	/man/10/dev
+converts	/man/10/ms2
+converts	/man/10/parsecmd
+converts	/man/10/print
+converts	/man/10/readnum
+converts	/man/10/styx
+converts	/man/2/convcs
+converts	/man/2/daytime
+converts	/man/2/debug
+converts	/man/2/devpointer
+converts	/man/2/format
+converts	/man/2/geodesy
+converts	/man/2/ir
+converts	/man/2/keyring-ipint
+converts	/man/2/math-export
+converts	/man/2/plumbmsg
+converts	/man/2/sets
+converts	/man/2/sh
+converts	/man/2/spree
+converts	/man/2/string
+converts	/man/2/styxconv
+converts	/man/2/sys-byte2char
+converts	/man/2/sys-print
+converts	/man/2/wmclient
+converts	/man/3/srv
+converts	/man/6/colour
+converts	/man/8/ai2key
+converts	/man/8/changelogin
+convex	/man/1/wm-misc
+convey	/man/5/0intro
+convey	/man/6/sexprs
+convey	/man/8/rdbgsrv
+conveyed	/man/2/keyring-0intro
+conveyed	/man/2/plumbmsg
+conveys	/man/2/draw-0intro
+conveys	/man/2/sexprs
+conveys	/man/8/bootpd
+convinced	/man/8/register
+convinced	/man/8/signer
+convm2d	/man/10/dev
+convm2d	/man/10/styx
+convm2d	/man/5/stat
+convm2d.c	/man/10/styx
+convm2s	/man/10/styx
+convm2s	/man/10/styxserver
+convm2s.c	/man/10/styx
+convpasswd	/man/8/changelogin
+convs2m	/man/10/styx
+convs2m.c	/man/10/styx
+cook	/man/1/brutus
+cook	/man/1/cook
+cook.b	/man/1/cook
+cookie	/man/1/charon
+cookies	/man/1/charon
+cooperating	/man/2/sys-pipe
+coordinate	/man/10/qio
+coordinate	/man/2/bufio
+coordinate	/man/2/draw-0intro
+coordinate	/man/2/draw-example
+coordinate	/man/2/draw-image
+coordinate	/man/2/draw-point
+coordinate	/man/2/geodesy
+coordinate	/man/2/sys-0intro
+coordinate	/man/2/tk
+coordinate	/man/2/wmsrv
+coordinate	/man/3/draw
+coordinate	/man/6/image
+coordinate	/man/8/collabsrv
+coordinate	/man/9/bind
+coordinate	/man/9/canvas
+coordinate	/man/9/entry
+coordinate	/man/9/listbox
+coordinate	/man/9/menu
+coordinate	/man/9/panel
+coordinates	/man/2/draw-0intro
+coordinates	/man/2/draw-image
+coordinates	/man/2/draw-point
+coordinates	/man/2/draw-pointer
+coordinates	/man/2/draw-rect
+coordinates	/man/2/geodesy
+coordinates	/man/2/prefab-element
+coordinates	/man/2/tk
+coordinates	/man/3/cons
+coordinates	/man/3/draw
+coordinates	/man/3/mpeg
+coordinates	/man/3/pointer
+coordinates	/man/3/touch
+coordinates	/man/3/tv
+coordinates	/man/6/colour
+coordinates	/man/6/image
+coordinates	/man/8/touchcal
+coordinates	/man/9/canvas
+coordinates	/man/9/entry
+coordinates	/man/9/listbox
+coordinates	/man/9/menu
+coordinates	/man/9/options
+coordinates	/man/9/panel
+coordinates	/man/9/scale
+coordinates	/man/9/scrollbar
+coordinates	/man/9/see
+coordinates	/man/9/text
+coords	/man/9/canvas
+coords	/man/9/scale
+cope	/man/4/kfs
+copen	/man/10/devattach
+copen	/man/10/newchan
+copied	/man/1/acme
+copied	/man/1/dd
+copied	/man/1/ftree
+copied	/man/1/m4
+copied	/man/1/tail
+copied	/man/1/wm-sh
+copied	/man/10/dev
+copied	/man/10/kproc
+copied	/man/10/memory
+copied	/man/10/print
+copied	/man/10/qio
+copied	/man/10/rune
+copied	/man/10/strcat
+copied	/man/10/styx
+copied	/man/2/daytime
+copied	/man/2/dbm
+copied	/man/2/draw-image
+copied	/man/2/keyring-sha1
+copied	/man/2/sh
+copied	/man/2/styxservers
+copied	/man/2/sys-print
+copied	/man/2/sys-read
+copied	/man/3/ether
+copied	/man/5/0intro
+copied	/man/6/audio
+copied	/man/6/image
+copied	/man/8/create
+copied	/man/8/ftl
+copied	/man/8/mkfs
+copied	/man/8/rdbgsrv
+copied	/man/8/styxchat
+copies	/man/1/cp
+copies	/man/1/dd
+copies	/man/1/fmt
+copies	/man/1/m4
+copies	/man/1/mv
+copies	/man/1/os
+copies	/man/1/p
+copies	/man/1/tail
+copies	/man/1/telnet
+copies	/man/1/tr
+copies	/man/1/uniq
+copies	/man/1/wm-sh
+copies	/man/10/9load
+copies	/man/10/error
+copies	/man/10/memory
+copies	/man/10/print
+copies	/man/10/qio
+copies	/man/10/rune
+copies	/man/10/strcat
+copies	/man/2/draw-0intro
+copies	/man/2/draw-image
+copies	/man/2/env
+copies	/man/2/styxconv
+copies	/man/2/sys-print
+copies	/man/2/sys-read
+copies	/man/2/tk
+copies	/man/3/boot
+copies	/man/3/kprof
+copies	/man/8/changelogin
+copies	/man/8/create
+copies	/man/8/mkfs
+copies	/man/8/rdbgsrv
+copies	/man/9/1copyright
+copy	/man/1/acme
+copy	/man/1/alphabet-fs
+copy	/man/1/cp
+copy	/man/1/dd
+copy	/man/1/fs
+copy	/man/1/ftree
+copy	/man/1/gettar
+copy	/man/1/mash-tk
+copy	/man/1/mk
+copy	/man/1/sh
+copy	/man/1/tee
+copy	/man/1/tiny
+copy	/man/1/uniq
+copy	/man/1/wm-misc
+copy	/man/1/wm-sh
+copy	/man/10/9load
+copy	/man/10/allocb
+copy	/man/10/dev
+copy	/man/10/devattach
+copy	/man/10/inb
+copy	/man/10/kprof
+copy	/man/10/memory
+copy	/man/10/mk
+copy	/man/10/parsecmd
+copy	/man/10/qio
+copy	/man/10/strcat
+copy	/man/10/styx
+copy	/man/2/factotum
+copy	/man/2/ip
+copy	/man/2/json
+copy	/man/2/keyring-0intro
+copy	/man/2/keyring-ipint
+copy	/man/2/math-fp
+copy	/man/2/palmfile
+copy	/man/2/plumbmsg
+copy	/man/2/registries
+copy	/man/2/security-0intro
+copy	/man/2/sets
+copy	/man/2/sexprs
+copy	/man/2/sh
+copy	/man/2/styxservers
+copy	/man/2/sys-pctl
+copy	/man/2/sys-tokenize
+copy	/man/2/w3c-uris
+copy	/man/3/env
+copy	/man/3/ether
+copy	/man/3/flash
+copy	/man/4/ftpfs
+copy	/man/4/iostats
+copy	/man/4/ramfile
+copy	/man/6/image
+copy	/man/6/proto
+copy	/man/8/changelogin
+copy	/man/8/create
+copy	/man/8/mkfs
+copy	/man/8/rdbgsrv
+copy	/man/8/styxchat
+copy	/man/9/1copyright
+copyattrs	/man/2/factotum
+copyblock	/man/10/allocb
+copydef	/man/1/m4
+copyenv	/man/2/sh
+copying	/man/1/dd
+copying	/man/1/tail
+copying	/man/10/dev
+copying	/man/10/readnum
+copying	/man/2/json
+copying	/man/2/smtp
+copying	/man/3/ftl
+copying	/man/4/ftpfs
+copying	/man/8/rdbgsrv
+copyright	/man/1/mux
+copyright	/man/1/wm-misc
+copyright	/man/9/0intro
+copyright	/man/9/1copyright
+copyrighted	/man/9/1copyright
+copysign	/man/2/math-fp
+core	/man/1/dd
+core	/man/10/a.out
+core	/man/10/acid
+core	/man/2/wmsrv
+corner	/man/1/acme
+corner	/man/1/collab-clients
+corner	/man/1/mash-tk
+corner	/man/1/miniterm
+corner	/man/2/dialog
+corner	/man/2/draw-0intro
+corner	/man/2/draw-image
+corner	/man/2/prefab-compound
+corner	/man/2/selectfile
+corner	/man/3/draw
+corner	/man/8/touchcal
+corner	/man/9/canvas
+corner	/man/9/entry
+corner	/man/9/grid
+corner	/man/9/menu
+corner	/man/9/options
+corner	/man/9/text
+corners	/man/3/touch
+corners	/man/9/canvas
+correct	/man/1/mash-make
+correct	/man/1/sh-arg
+correct	/man/1/tktester
+correct	/man/10/c2l
+correct	/man/10/mk
+correct	/man/10/plan9.ini
+correct	/man/10/splhi
+correct	/man/10/styx
+correct	/man/2/attrdb
+correct	/man/2/dis
+correct	/man/2/factotum
+correct	/man/2/security-0intro
+correct	/man/2/styxflush
+correct	/man/5/remove
+correct	/man/6/image
+correct	/man/8/register
+corrected	/man/6/colour
+correcting	/man/3/dbg
+correction	/man/1/miniterm
+correction	/man/6/colour
+correction	/man/8/rdbgsrv
+correctly	/man/1/9win
+correctly	/man/1/brutus
+correctly	/man/1/man
+correctly	/man/1/secstore
+correctly	/man/1/sh
+correctly	/man/10/dev
+correctly	/man/10/devattach
+correctly	/man/10/plan9.ini
+correctly	/man/10/strcat
+correctly	/man/2/daytime
+correctly	/man/2/debug
+correctly	/man/2/plumbmsg
+correctly	/man/2/sh
+correctly	/man/2/spki
+correctly	/man/2/spree-objstore
+correctly	/man/2/styxservers
+correctly	/man/2/tk
+correctly	/man/2/tkclient
+correctly	/man/2/wmclient
+correctly	/man/3/flash
+correctly	/man/3/tinyfs
+correctly	/man/4/namespace
+correctly	/man/5/flush
+correctly	/man/6/audio
+correctly	/man/9/0intro
+correctly	/man/9/bind
+correctly	/man/9/menu
+correctly	/man/9/text
+correlated	/man/10/kprof
+correspond	/man/1/deb
+correspond	/man/10/2c
+correspond	/man/10/a.out
+correspond	/man/10/newchan
+correspond	/man/2/dis
+correspond	/man/2/ubfa
+correspond	/man/2/w3c-xpointers
+correspond	/man/3/draw
+correspond	/man/3/ip
+correspond	/man/3/lpt
+correspond	/man/3/ssl
+correspond	/man/6/image
+correspond	/man/8/mkfs
+correspond	/man/9/menu
+correspond	/man/9/scrollbar
+correspondence	/man/10/2a
+correspondence	/man/10/2l
+correspondence	/man/2/w3c-xpointers
+correspondence	/man/3/tls
+corresponds	/man/10/conf
+corresponds	/man/10/devattach
+corresponds	/man/2/dial
+corresponds	/man/2/draw-image
+corresponds	/man/2/draw-pointer
+corresponds	/man/2/ida
+corresponds	/man/2/styxservers
+corresponds	/man/2/sys-dial
+corresponds	/man/3/draw
+corresponds	/man/3/ds
+corresponds	/man/3/dup
+corresponds	/man/3/gpio
+corresponds	/man/3/ip
+corresponds	/man/3/lpt
+corresponds	/man/3/switch
+corresponds	/man/4/namespace
+corresponds	/man/6/audio
+corresponds	/man/6/dis
+corresponds	/man/6/plumbing
+corresponds	/man/8/collabsrv
+corresponds	/man/9/entry
+corresponds	/man/9/listbox
+corresponds	/man/9/menu
+corresponds	/man/9/options
+corresponds	/man/9/scale
+corresponds	/man/9/scrollbar
+corrupt	/man/2/palmfile
+corrupted	/man/1/calendar
+corrupted	/man/1/charon
+corrupted	/man/1/miniterm
+corrupted	/man/1/sh-file2chan
+corrupted	/man/1/sh-sexprs
+corruption	/man/2/venti
+cos	/man/1/calc
+cos	/man/2/math-elem
+cosh	/man/1/calc
+cosh	/man/2/math-elem
+cost	/man/10/error
+cost	/man/2/imagefile
+count	/man/1/dd
+count	/man/1/itest
+count	/man/1/mdb
+count	/man/1/read
+count	/man/1/sh
+count	/man/1/sh-file2chan
+count	/man/1/wc
+count	/man/10/dev
+count	/man/10/dmainit
+count	/man/10/inb
+count	/man/10/memory
+count	/man/10/newchan
+count	/man/10/parsecmd
+count	/man/10/print
+count	/man/10/qio
+count	/man/10/ref
+count	/man/10/styx
+count	/man/2/alphabet-intro
+count	/man/2/keyring-getmsg
+count	/man/2/msgio
+count	/man/2/prof
+count	/man/2/sexprs
+count	/man/2/spree
+count	/man/2/styx
+count	/man/2/styxservers
+count	/man/2/sys-file2chan
+count	/man/2/sys-read
+count	/man/2/sys-tokenize
+count	/man/3/dbg
+count	/man/3/draw
+count	/man/3/fpga
+count	/man/3/pipe
+count	/man/3/prog
+count	/man/3/usb
+count	/man/4/acme
+count	/man/4/keyfs
+count	/man/5/0intro
+count	/man/5/read
+count	/man/5/stat
+count	/man/5/version
+count	/man/6/dis
+count	/man/6/sbl
+count	/man/6/ubfa
+count	/man/8/rstyxd
+count	/man/8/styxchat
+count	/man/9/grid
+count	/man/9/text
+counte	/man/2/prof
+counted	/man/1/tail
+counted	/man/1/wc
+counted	/man/10/acid
+counted	/man/2/sys-dup
+counted	/man/3/prof
+counter	/man/1/limbo
+counter	/man/10/acid
+counter	/man/2/security-0intro
+counter	/man/2/sys-millisec
+counter	/man/3/cons
+counter	/man/3/dbg
+counter	/man/3/kprof
+counter	/man/3/prof
+counter	/man/3/prog
+counter	/man/6/dis
+counter	/man/9/canvas
+counterclockwise	/man/2/draw-image
+counterpart	/man/1/sh-tk
+counterpart	/man/8/getauthinfo
+counterparts	/man/1/sh-string
+counterparts	/man/1/units
+counters	/man/3/ether
+countersigned	/man/8/signer
+countersigner	/man/8/register
+countersigner	/man/8/signer
+countersigner.b	/man/8/signer
+counting	/man/1/fc
+counting	/man/1/sh-std
+counting	/man/10/dynld
+counting	/man/10/ref
+counting	/man/2/asn1
+counting	/man/2/regex
+counting	/man/3/ip
+counting	/man/7/db
+counts	/man/1/freq
+counts	/man/1/tail
+counts	/man/1/wc
+counts	/man/10/a.out
+counts	/man/10/memory
+counts	/man/10/ref
+counts	/man/10/styxserver
+counts	/man/2/spree-cardlib
+counts	/man/2/styx
+counts	/man/2/styxservers
+counts	/man/2/sys-0intro
+counts	/man/3/kprof
+counts	/man/3/sd
+counts	/man/3/usb
+counts	/man/4/dbfs
+counts	/man/5/0intro
+counts	/man/6/sexprs
+couple	/man/1/sh-string
+couples	/man/2/asn1
+cover	/man/2/draw-image
+cover	/man/2/draw-rect
+cover	/man/2/keyring-sha1
+cover	/man/9/text
+coverage	/man/1/cprof
+coverage	/man/2/prof
+coverage	/man/3/prof
+covered	/man/1/sh
+covered	/man/2/draw-image
+covered	/man/2/prof
+covered	/man/6/font
+covered	/man/9/canvas
+covered	/man/9/entry
+covered	/man/9/grid
+covering	/man/1/wish
+covering	/man/6/colour
+covers	/man/1/mprof
+covers	/man/2/dis
+covers	/man/3/ssl
+covers	/man/6/dis
+covers	/man/9/canvas
+covers	/man/9/grid
+covers	/man/9/listbox
+covers	/man/9/scrollbar
+covers	/man/9/text
+cp	/man/1/cat
+cp	/man/1/cp
+cp	/man/1/dd
+cp	/man/1/mash-make
+cp	/man/1/mk
+cp	/man/1/mv
+cp	/man/10/mk
+cp	/man/10/seconds
+cp	/man/2/convcs
+cp	/man/2/dbm
+cp	/man/2/spree-cardlib
+cp	/man/3/ds
+cp	/man/3/kprof
+cp	/man/4/ftpfs
+cp	/man/4/kfs
+cp	/man/4/ramfile
+cp	/man/4/tarfs
+cp	/man/8/rdbgsrv
+cp	/man/8/styxmon
+cp.b	/man/1/cp
+cp866	/man/2/convcs
+cpfstats	/man/2/prof
+cprof	/man/1/cprof
+cprof	/man/1/mprof
+cprof	/man/1/prof
+cprof.b	/man/1/cprof
+cpshow	/man/2/prof
+cpstart	/man/2/prof
+cpstats	/man/2/prof
+cptr	/man/2/draw-pointer
+cpu	/man/1/cpu
+cpu	/man/1/grid-monitor
+cpu	/man/1/grid-ns
+cpu	/man/1/grid-session
+cpu	/man/1/os
+cpu	/man/1/ps
+cpu	/man/1/telnet
+cpu	/man/1/time
+cpu	/man/10/acid
+cpu	/man/10/plan9.ini
+cpu	/man/2/sys-sleep
+cpu	/man/3/prog
+cpu	/man/4/export
+cpu	/man/4/grid-cpu
+cpu.b	/man/1/cpu
+cpu.b	/man/4/grid-cpu
+cpus	/man/10/mk
+cputype	/man/3/arch
+cr	/man/1/ar
+cr	/man/10/iar
+crackerbarrel	/man/1/math-misc
+crackerbarrel.b	/man/1/math-misc
+cracks	/man/2/format
+crafted	/man/8/signer
+crag	/man/2/format
+crash	/man/1/tktester
+crash	/man/10/c2l
+crashed	/man/1/blur
+crc	/man/1/sum
+crc	/man/2/crc
+crc.b	/man/2/crc
+crc.m	/man/2/crc
+crc32	/man/2/crc
+crcstate	/man/2/crc
+crctab	/man/2/crc
+create	/man/1/0intro
+create	/man/1/9win
+create	/man/1/acme
+create	/man/1/alphabet-abc
+create	/man/1/alphabet-fs
+create	/man/1/alphabet-grid
+create	/man/1/alphabet-main
+create	/man/1/ar
+create	/man/1/bind
+create	/man/1/blur
+create	/man/1/fs
+create	/man/1/gettar
+create	/man/1/grid-session
+create	/man/1/limbo
+create	/man/1/m4
+create	/man/1/mkdir
+create	/man/1/sh-file2chan
+create	/man/1/sh-tk
+create	/man/1/tiny
+create	/man/1/touch
+create	/man/1/tr
+create	/man/1/wm
+create	/man/1/wm-sh
+create	/man/1/yacc
+create	/man/1/zeros
+create	/man/10/dev
+create	/man/10/devattach
+create	/man/10/iar
+create	/man/10/styxserver
+create	/man/2/alphabet-intro
+create	/man/2/bloomfilter
+create	/man/2/bufio
+create	/man/2/dbm
+create	/man/2/draw-0intro
+create	/man/2/draw-context
+create	/man/2/draw-display
+create	/man/2/draw-screen
+create	/man/2/factotum
+create	/man/2/imagefile
+create	/man/2/json
+create	/man/2/keyring-0intro
+create	/man/2/mpeg
+create	/man/2/palmfile
+create	/man/2/security-0intro
+create	/man/2/security-login
+create	/man/2/styx
+create	/man/2/styxconv
+create	/man/2/styxpersist
+create	/man/2/styxservers
+create	/man/2/styxservers-nametree
+create	/man/2/sys-0intro
+create	/man/2/sys-bind
+create	/man/2/sys-dup
+create	/man/2/sys-file2chan
+create	/man/2/sys-open
+create	/man/2/sys-pipe
+create	/man/2/tk
+create	/man/2/ubfa
+create	/man/2/wmsrv
+create	/man/3/dbg
+create	/man/3/draw
+create	/man/3/ds
+create	/man/3/flash
+create	/man/3/ip
+create	/man/3/logfs
+create	/man/3/srv9
+create	/man/4/archfs
+create	/man/4/dbfs
+create	/man/4/kfs
+create	/man/4/spree
+create	/man/5/0intro
+create	/man/5/open
+create	/man/5/stat
+create	/man/5/walk
+create	/man/6/namespace
+create	/man/6/proto
+create	/man/8/changelogin
+create	/man/8/create
+create	/man/8/createsignerkey
+create	/man/8/kfscmd
+create	/man/8/prep
+create	/man/9/button
+create	/man/9/canvas
+create	/man/9/checkbutton
+create	/man/9/choicebutton
+create	/man/9/entry
+create	/man/9/frame
+create	/man/9/image
+create	/man/9/label
+create	/man/9/listbox
+create	/man/9/menu
+create	/man/9/menubutton
+create	/man/9/options
+create	/man/9/panel
+create	/man/9/radiobutton
+create	/man/9/scale
+create	/man/9/scrollbar
+create	/man/9/text
+create.b	/man/8/create
+created	/man/1/0intro
+created	/man/1/acme
+created	/man/1/alphabet-abc
+created	/man/1/alphabet-grid
+created	/man/1/asm
+created	/man/1/bind
+created	/man/1/blur
+created	/man/1/deb
+created	/man/1/ebook
+created	/man/1/ftree
+created	/man/1/mkdir
+created	/man/1/sh
+created	/man/1/sh-tk
+created	/man/1/tiny
+created	/man/1/tkcmd
+created	/man/1/tktester
+created	/man/1/wish
+created	/man/1/wm-sh
+created	/man/1/yacc
+created	/man/10/dev
+created	/man/10/eve
+created	/man/10/newchan
+created	/man/10/qio
+created	/man/10/styxserver
+created	/man/2/bufio
+created	/man/2/bufio-chanfill
+created	/man/2/dbm
+created	/man/2/debug
+created	/man/2/dialog
+created	/man/2/diskblocks
+created	/man/2/draw-0intro
+created	/man/2/draw-display
+created	/man/2/draw-font
+created	/man/2/draw-image
+created	/man/2/drawmux
+created	/man/2/keyring-0intro
+created	/man/2/plumbmsg
+created	/man/2/popup
+created	/man/2/prefab-0intro
+created	/man/2/prefab-element
+created	/man/2/prefab-style
+created	/man/2/security-0intro
+created	/man/2/selectfile
+created	/man/2/sets
+created	/man/2/sh
+created	/man/2/spki
+created	/man/2/spree
+created	/man/2/spree-cardlib
+created	/man/2/styxservers
+created	/man/2/styxservers-nametree
+created	/man/2/sys-0intro
+created	/man/2/sys-bind
+created	/man/2/sys-open
+created	/man/2/tabs
+created	/man/2/tk
+created	/man/2/tkclient
+created	/man/2/w3c-uris
+created	/man/2/wmclient
+created	/man/2/wmlib
+created	/man/2/wmsrv
+created	/man/2/xml
+created	/man/3/dbg
+created	/man/3/draw
+created	/man/3/env
+created	/man/3/flash
+created	/man/3/fs
+created	/man/3/prog
+created	/man/3/srv
+created	/man/3/tinyfs
+created	/man/4/acme
+created	/man/4/archfs
+created	/man/4/dbfs
+created	/man/4/kfs
+created	/man/4/memfs
+created	/man/4/namespace
+created	/man/4/registry
+created	/man/4/spree
+created	/man/5/0intro
+created	/man/5/flush
+created	/man/5/open
+created	/man/6/colour
+created	/man/8/changelogin
+created	/man/8/getauthinfo
+created	/man/8/logind
+created	/man/8/mkfs
+created	/man/8/prep
+created	/man/8/signer
+created	/man/8/svc
+created	/man/9/canvas
+created	/man/9/cursor
+created	/man/9/entry
+created	/man/9/frame
+created	/man/9/grid
+created	/man/9/label
+created	/man/9/listbox
+created	/man/9/menu
+created	/man/9/options
+created	/man/9/panel
+created	/man/9/text
+creates	/man/1/9win
+creates	/man/1/acme
+creates	/man/1/alphabet-main
+creates	/man/1/blur
+creates	/man/1/charon
+creates	/man/1/itest
+creates	/man/1/logon
+creates	/man/1/mash-tk
+creates	/man/1/mkdir
+creates	/man/1/sh
+creates	/man/1/sh-alphabet
+creates	/man/1/sh-file2chan
+creates	/man/1/sh-mload
+creates	/man/1/sh-std
+creates	/man/1/sh-tk
+creates	/man/1/spree-join
+creates	/man/1/stream
+creates	/man/1/tiny
+creates	/man/1/tkcmd
+creates	/man/1/uuencode
+creates	/man/1/wm-misc
+creates	/man/10/2l
+creates	/man/10/dynld
+creates	/man/10/kproc
+creates	/man/10/ntsrv
+creates	/man/10/styxserver
+creates	/man/2/bufio
+creates	/man/2/dbm
+creates	/man/2/dhcpclient
+creates	/man/2/dividers
+creates	/man/2/draw-0intro
+creates	/man/2/draw-display
+creates	/man/2/draw-font
+creates	/man/2/hash
+creates	/man/2/ir
+creates	/man/2/keyring-0intro
+creates	/man/2/keyring-gensk
+creates	/man/2/keyring-ipint
+creates	/man/2/keyring-sha1
+creates	/man/2/lock
+creates	/man/2/popup
+creates	/man/2/prefab-compound
+creates	/man/2/prefab-element
+creates	/man/2/security-0intro
+creates	/man/2/sh
+creates	/man/2/spree
+creates	/man/2/spree-cardlib
+creates	/man/2/sys-open
+creates	/man/2/sys-pipe
+creates	/man/2/tabs
+creates	/man/2/tk
+creates	/man/2/tkclient
+creates	/man/2/wmclient
+creates	/man/2/wmsrv
+creates	/man/3/env
+creates	/man/3/flash
+creates	/man/3/ftl
+creates	/man/4/acme
+creates	/man/4/grid-cpu
+creates	/man/4/kfs
+creates	/man/4/logfile
+creates	/man/4/ramfile
+creates	/man/4/spree
+creates	/man/7/db
+creates	/man/8/createsignerkey
+creates	/man/8/kfscmd
+creates	/man/8/prep
+creates	/man/8/signer
+creates	/man/9/button
+creates	/man/9/canvas
+creates	/man/9/checkbutton
+creates	/man/9/choicebutton
+creates	/man/9/entry
+creates	/man/9/frame
+creates	/man/9/image
+creates	/man/9/label
+creates	/man/9/listbox
+creates	/man/9/menu
+creates	/man/9/menubutton
+creates	/man/9/panel
+creates	/man/9/radiobutton
+creates	/man/9/scale
+creates	/man/9/scrollbar
+creates	/man/9/text
+createsignerkey	/man/2/security-0intro
+createsignerkey	/man/2/security-login
+createsignerkey	/man/3/sign
+createsignerkey	/man/4/namespace
+createsignerkey	/man/8/createsignerkey
+createsignerkey	/man/8/getauthinfo
+createsignerkey	/man/8/logind
+createsignerkey	/man/8/signer
+createsignerkey	/man/8/svc
+createsignerkey.b	/man/8/createsignerkey
+creating	/man/1/diff
+creating	/man/1/grid-ns
+creating	/man/1/mash
+creating	/man/1/mash-tk
+creating	/man/1/tiny
+creating	/man/1/tktester
+creating	/man/1/wm
+creating	/man/10/acid
+creating	/man/10/kproc
+creating	/man/2/dis
+creating	/man/2/lock
+creating	/man/2/palmfile
+creating	/man/2/security-0intro
+creating	/man/2/spree-cardlib
+creating	/man/2/styxservers
+creating	/man/2/sys-open
+creating	/man/2/sys-stat
+creating	/man/2/tk
+creating	/man/2/tkclient
+creating	/man/2/wmclient
+creating	/man/3/cmd
+creating	/man/3/env
+creating	/man/8/createsignerkey
+creating	/man/8/prep
+creating	/man/9/grid
+creation	/man/1/bind
+creation	/man/1/sh-file2chan
+creation	/man/1/sh-tk
+creation	/man/10/dev
+creation	/man/10/devattach
+creation	/man/10/kproc
+creation	/man/10/styxserver
+creation	/man/2/draw-font
+creation	/man/2/palmfile
+creation	/man/2/prefab-element
+creation	/man/2/spree
+creation	/man/2/styxservers
+creation	/man/2/styxservers-nametree
+creation	/man/2/tk
+creation	/man/3/srv9
+creation	/man/4/9srvfs
+creation	/man/4/import
+creation	/man/5/open
+creator	/man/1/tiny
+creator	/man/2/palmfile
+creator	/man/2/sys-0intro
+creator	/man/3/srv
+cred	/man/3/draw
+credentials	/man/1/secstore
+credentials	/man/2/security-login
+credentials	/man/2/spki-verifier
+credentials	/man/6/login
+credentials	/man/8/signer
+credentials	/man/8/svc
+credit	/man/3/indir
+credits	/man/9/canvas
+credits	/man/9/grid
+crgb	/man/2/imagefile
+crgb1	/man/2/imagefile
+criteria	/man/1/alphabet-fs
+criteria	/man/1/fs
+criteria	/man/2/keyset
+criteria	/man/2/readdir
+critical	/man/10/kproc
+critical	/man/10/lock
+critical	/man/10/qlock
+crlfs	/man/6/keyboard
+crockford	/man/2/json
+crockford	/man/6/json
+cross	/man/1/0intro
+cross	/man/1/stream
+cross	/man/10/0intro
+cross	/man/10/2c
+cross	/man/2/sexprs
+cross	/man/3/pipe
+cross	/man/3/rtc
+cross	/man/4/namespace
+cross	/man/8/touchcal
+crossbow	/man/1/avr
+crosses	/man/10/dmainit
+crosses	/man/2/sys-0intro
+crosses	/man/3/ip
+crt	/man/2/draw-display
+crt	/man/9/types
+crypt	/man/1/crypt
+crypt	/man/1/secstore
+crypt	/man/10/plan9.ini
+crypt	/man/2/keyring-0intro
+crypt	/man/2/keyring-crypt
+crypt	/man/2/keyring-rc4
+crypt	/man/2/secstore
+crypt	/man/3/ssl
+crypt	/man/4/import
+crypt	/man/4/keyfs
+crypt.b	/man/1/crypt
+cryptanalysis	/man/2/security-0intro
+cryptoalg	/man/1/rcmd
+cryptographic	/man/2/keyring-0intro
+cryptographic	/man/2/keyring-ipint
+cryptographic	/man/2/keyring-sha1
+cryptographic	/man/2/security-0intro
+cryptographic	/man/4/factotum
+cryptographically	/man/2/keyring-0intro
+cryptographically	/man/2/keyring-crypt
+cryptographically	/man/2/keyring-sha1
+cryptographically	/man/3/sign
+cryptographically	/man/6/dis
+cryptographically	/man/6/keytext
+cryptography	/man/2/keyring-0intro
+cryptography	/man/2/security-0intro
+cryptography	/man/3/ssl
+cs	/man/1/secstore
+cs	/man/1/tr
+cs	/man/1/webgrab
+cs	/man/2/convcs
+cs	/man/2/dial
+cs	/man/2/secstore
+cs	/man/2/security-login
+cs	/man/2/srv
+cs	/man/2/sys-0intro
+cs	/man/2/sys-dial
+cs	/man/2/virgil
+cs	/man/4/ftpfs
+cs	/man/4/namespace
+cs	/man/6/attrdb
+cs	/man/6/ndb
+cs	/man/8/bootpd
+cs	/man/8/cs
+cs	/man/8/dhcp
+cs	/man/8/dns
+cs	/man/8/register
+cs	/man/8/svc
+cs	/man/8/virgild
+cs.b	/man/8/cs
+csfile	/man/2/convcs
+csize	/man/8/prep
+csn	/man/3/pnp
+csname	/man/1/tcs
+csname	/man/2/convcs
+csquery	/man/8/cs
+csquery.b	/man/8/cs
+css	/man/2/w3c-css
+css.b	/man/2/w3c-css
+css.m	/man/2/w3c-css
+css21	/man/2/w3c-css
+csv	/man/1/sh
+csv	/man/1/sh-csv
+csv	/man/2/csv
+csv.b	/man/1/sh-csv
+csv.b	/man/2/csv
+csv.m	/man/2/csv
+ct65545	/man/3/vga
+ct65545hwgc	/man/3/vga
+ctext	/man/2/dial
+ctime	/man/2/daytime
+ctime	/man/2/palmfile
+ctl	/man/1/kill
+ctl	/man/1/secstore
+ctl	/man/1/wm
+ctl	/man/10/acid
+ctl	/man/10/odbc
+ctl	/man/10/parsecmd
+ctl	/man/10/plan9.ini
+ctl	/man/2/command
+ctl	/man/2/dial
+ctl	/man/2/disks
+ctl	/man/2/draw-context
+ctl	/man/2/mpeg
+ctl	/man/2/styxservers-nametree
+ctl	/man/2/sys-dial
+ctl	/man/2/tkclient
+ctl	/man/2/wmclient
+ctl	/man/2/wmsrv
+ctl	/man/3/cmd
+ctl	/man/3/draw
+ctl	/man/3/ds
+ctl	/man/3/dup
+ctl	/man/3/eia
+ctl	/man/3/ether
+ctl	/man/3/flash
+ctl	/man/3/floppy
+ctl	/man/3/ftl
+ctl	/man/3/i2c
+ctl	/man/3/i82365
+ctl	/man/3/ip
+ctl	/man/3/plap
+ctl	/man/3/pnp
+ctl	/man/3/prof
+ctl	/man/3/prog
+ctl	/man/3/sd
+ctl	/man/3/ssl
+ctl	/man/3/tls
+ctl	/man/4/acme
+ctl	/man/4/factotum
+ctl	/man/4/grid-cpu
+ctl	/man/4/spree
+ctl	/man/8/collabsrv
+ctl	/man/8/dhcp
+ctl	/man/8/prep
+ctlfd	/man/2/disks
+ctlifc	/man/2/dhcpclient
+ctlr	/man/3/vga
+ctrl	/man/10/9load
+cts	/man/3/eia
+ctx	/man/1/stack
+ctxt	/man/1/stack
+ctxt	/man/1/yacc
+ctxt	/man/2/command
+ctxt	/man/2/debug
+ctxt	/man/2/dialog
+ctxt	/man/2/draw-context
+ctxt	/man/2/draw-example
+ctxt	/man/2/ir
+ctxt	/man/2/itslib
+ctxt	/man/2/selectfile
+ctxt	/man/2/sh
+ctxt	/man/2/tk
+ctxt	/man/2/tkclient
+ctxt	/man/2/translate
+ctxt	/man/2/volume
+ctxt	/man/2/wmclient
+ctxt	/man/2/wmlib
+ctxt.cinput	/man/2/ir
+ctxt.ctl	/man/2/tkclient
+ctxt.ctl	/man/2/wmclient
+ctxt.ctomux	/man/2/ir
+ctxt.kbd	/man/2/tkclient
+ctxt.kbd	/man/2/wmclient
+ctxt.ptr	/man/2/tkclient
+ctxt.ptr	/man/2/wmclient
+cu	/man/3/sd
+cube	/man/2/math-elem
+cube	/man/6/colour
+cubic	/man/2/draw-image
+cue	/man/1/deb
+cuid	/man/2/factotum
+cumulative	/man/1/cprof
+curc	/man/10/styxserver
+curid	/man/2/tabs
+curly	/man/1/acme
+curly	/man/2/tk
+currency	/man/1/units
+currency	/man/6/keyboard
+current	/man/1/acme
+current	/man/1/alphabet-fs
+current	/man/1/ar
+current	/man/1/basename
+current	/man/1/bind
+current	/man/1/blur
+current	/man/1/brutus
+current	/man/1/cal
+current	/man/1/calc
+current	/man/1/charon
+current	/man/1/chgrp
+current	/man/1/collab-clients
+current	/man/1/date
+current	/man/1/deb
+current	/man/1/du
+current	/man/1/env
+current	/man/1/fc
+current	/man/1/fs
+current	/man/1/gettar
+current	/man/1/grid-monitor
+current	/man/1/grid-query
+current	/man/1/grid-session
+current	/man/1/kill
+current	/man/1/listen
+current	/man/1/logon
+current	/man/1/m4
+current	/man/1/mash
+current	/man/1/mash-tk
+current	/man/1/mdb
+current	/man/1/mk
+current	/man/1/mprof
+current	/man/1/ns
+current	/man/1/nsbuild
+current	/man/1/passwd
+current	/man/1/plumb
+current	/man/1/ps
+current	/man/1/pwd
+current	/man/1/read
+current	/man/1/secstore
+current	/man/1/sendmail
+current	/man/1/sh
+current	/man/1/sh-alphabet
+current	/man/1/sh-regex
+current	/man/1/sh-std
+current	/man/1/sh-test
+current	/man/1/tiny
+current	/man/1/tktester
+current	/man/1/touch
+current	/man/1/unicode
+current	/man/1/vacget
+current	/man/1/wm
+current	/man/1/wm-misc
+current	/man/1/wm-sh
+current	/man/10/2c
+current	/man/10/a.out
+current	/man/10/acid
+current	/man/10/conf
+current	/man/10/dev
+current	/man/10/devattach
+current	/man/10/dynld
+current	/man/10/error
+current	/man/10/eve
+current	/man/10/iar
+current	/man/10/intrenable
+current	/man/10/kproc
+current	/man/10/lock
+current	/man/10/mk
+current	/man/10/newchan
+current	/man/10/odbc
+current	/man/10/panic
+current	/man/10/qio
+current	/man/10/seconds
+current	/man/10/splhi
+current	/man/10/styxserver
+current	/man/2/0intro
+current	/man/2/arg
+current	/man/2/bufio
+current	/man/2/command
+current	/man/2/crc
+current	/man/2/debug
+current	/man/2/dhcpclient
+current	/man/2/dial
+current	/man/2/dis
+current	/man/2/diskblocks
+current	/man/2/draw-context
+current	/man/2/draw-display
+current	/man/2/drawmux
+current	/man/2/env
+current	/man/2/exception
+current	/man/2/fsproto
+current	/man/2/geodesy
+current	/man/2/itslib
+current	/man/2/keyring-crypt
+current	/man/2/keyring-gensk
+current	/man/2/keyring-ipint
+current	/man/2/keyring-sha1
+current	/man/2/lists
+current	/man/2/newns
+current	/man/2/popup
+current	/man/2/registries
+current	/man/2/rfc822
+current	/man/2/scsiio
+current	/man/2/security-auth
+current	/man/2/sh
+current	/man/2/spki
+current	/man/2/spree
+current	/man/2/spree-allow
+current	/man/2/spree-cardlib
+current	/man/2/spree-objstore
+current	/man/2/styx
+current	/man/2/styxconv
+current	/man/2/styxservers
+current	/man/2/sys-0intro
+current	/man/2/sys-bind
+current	/man/2/sys-export
+current	/man/2/sys-fauth
+current	/man/2/sys-pctl
+current	/man/2/sys-read
+current	/man/2/sys-seek
+current	/man/2/sys-self
+current	/man/2/sys-sleep
+current	/man/2/sys-stat
+current	/man/2/tkclient
+current	/man/2/translate
+current	/man/2/wmclient
+current	/man/2/wmlib
+current	/man/2/wmsrv
+current	/man/2/workdir
+current	/man/2/xml
+current	/man/3/boot
+current	/man/3/cap
+current	/man/3/cmd
+current	/man/3/cons
+current	/man/3/dbg
+current	/man/3/draw
+current	/man/3/dup
+current	/man/3/flash
+current	/man/3/i2c
+current	/man/3/ip
+current	/man/3/logfs
+current	/man/3/mpeg
+current	/man/3/pointer
+current	/man/3/prog
+current	/man/3/snarf
+current	/man/3/srv
+current	/man/3/srv9
+current	/man/3/tls
+current	/man/3/touch
+current	/man/3/tv
+current	/man/3/usb
+current	/man/3/vga
+current	/man/4/9srvfs
+current	/man/4/acme
+current	/man/4/export
+current	/man/4/ftpfs
+current	/man/4/grid-cpu
+current	/man/4/import
+current	/man/4/iostats
+current	/man/4/keyfs
+current	/man/4/namespace
+current	/man/4/spree
+current	/man/4/tarfs
+current	/man/5/0intro
+current	/man/5/clunk
+current	/man/5/stat
+current	/man/5/walk
+current	/man/6/dis
+current	/man/6/image
+current	/man/6/namespace
+current	/man/6/plumbing
+current	/man/6/proto
+current	/man/6/ubfa
+current	/man/7/db
+current	/man/8/applylog
+current	/man/8/bootpd
+current	/man/8/changelogin
+current	/man/8/collabsrv
+current	/man/8/create
+current	/man/8/cs
+current	/man/8/dhcp
+current	/man/8/getauthinfo
+current	/man/8/styxmon
+current	/man/8/svc
+current	/man/9/bind
+current	/man/9/button
+current	/man/9/canvas
+current	/man/9/checkbutton
+current	/man/9/choicebutton
+current	/man/9/cursor
+current	/man/9/entry
+current	/man/9/frame
+current	/man/9/grab
+current	/man/9/grid
+current	/man/9/label
+current	/man/9/listbox
+current	/man/9/menu
+current	/man/9/menubutton
+current	/man/9/options
+current	/man/9/panel
+current	/man/9/radiobutton
+current	/man/9/scale
+current	/man/9/scrollbar
+current	/man/9/text
+current	/man/9/variable
+curselection	/man/9/listbox
+cursor	/man/1/acme
+cursor	/man/1/mux
+cursor	/man/1/wm-sh
+cursor	/man/2/draw-context
+cursor	/man/2/draw-display
+cursor	/man/3/pointer
+cursor	/man/3/vga
+cursor	/man/7/db
+cursor	/man/9/0intro
+cursor	/man/9/button
+cursor	/man/9/canvas
+cursor	/man/9/cursor
+cursor	/man/9/entry
+cursor	/man/9/listbox
+cursor	/man/9/menu
+cursor	/man/9/options
+cursor	/man/9/scale
+cursor	/man/9/text
+cursors	/man/2/draw-display
+cursorset	/man/2/draw-display
+curve	/man/2/draw-image
+curve	/man/9/canvas
+curved	/man/9/canvas
+curves	/man/9/canvas
+custom	/man/1/listen
+custom	/man/1/tail
+custom	/man/10/devattach
+custom	/man/4/spree
+customary	/man/2/math-fp
+customer	/man/2/keyring-0intro
+customizing	/man/1/mash-tk
+cut	/man/1/acme
+cut	/man/1/brutus
+cut	/man/1/wm-misc
+cut	/man/1/wm-sh
+cut	/man/4/acme
+cuts	/man/1/brutus
+cuts	/man/1/wm-sh
+cvstyx	/man/2/styxconv
+cwd	/man/1/wm-sh
+cx	/man/1/miniterm
+cy	/man/2/imagefile
+cyan	/man/2/draw-display
+cyber938x	/man/3/vga
+cyber938xhwgc	/man/3/vga
+cycle	/man/1/tsort
+cycle	/man/2/keyring-rc4
+cycles	/man/1/tsort
+cycles	/man/2/sexprs
+cycles	/man/8/ftl
+cyclic	/man/10/c2l
+cyclic	/man/2/crc
+cyclic	/man/2/format
+cyclic	/man/2/json
+cyclic	/man/2/prefab-element
+cyclic	/man/2/prof
+cyclic	/man/2/sexprs
+cyclic	/man/2/spki
+cyclic	/man/2/ubfa
+cyclic	/man/2/w3c-css
+cyclic	/man/2/w3c-xpointers
+cyclic	/man/2/wmsrv
+cyl	/man/2/disks
+cylinder	/man/10/9load
+cylinder	/man/2/disks
+cylinder	/man/3/sd
+cylinder	/man/8/prep
+cylinders	/man/2/disks
+cylinders	/man/8/prep
+cypher	/man/1/crypt
+cyrillic	/man/6/keyboard
+d.gid	/man/2/styxservers-nametree
+d.mode	/man/2/styxservers-nametree
+d.name	/man/2/styxservers-nametree
+d.qid	/man/10/styxserver
+d.qid.path	/man/2/styxservers-nametree
+d.qid.qtype	/man/2/styxservers-nametree
+d.qid.type&qtdir	/man/10/styxserver
+d.uid	/man/2/styxservers-nametree
+daemon	/man/1/emu
+daemon	/man/1/listen
+daemon	/man/8/httpd
+daemon	/man/8/init
+daemon	/man/8/logind
+daemon's	/man/8/httpd
+dallas	/man/3/rtc
+damage	/man/9/1copyright
+damages	/man/9/1copyright
+dance	/man/10/plan9.ini
+dapop	/man/2/dis
+darkblue	/man/2/draw-display
+darkblue	/man/9/types
+darker	/man/1/cprof
+darker	/man/1/mprof
+darker	/man/1/prof
+darkgreen	/man/2/draw-display
+darkyellow	/man/2/draw-display
+dash	/man/2/arg
+dashes	/man/2/spki
+dat.h	/man/10/0intro
+data	/man/1/0intro
+data	/man/1/acme
+data	/man/1/alphabet-abc
+data	/man/1/alphabet-fs
+data	/man/1/alphabet-grid
+data	/man/1/alphabet-main
+data	/man/1/auplay
+data	/man/1/avr
+data	/man/1/calendar
+data	/man/1/charon
+data	/man/1/crypt
+data	/man/1/deb
+data	/man/1/emu
+data	/man/1/fs
+data	/man/1/grid-monitor
+data	/man/1/gzip
+data	/man/1/idea
+data	/man/1/keyboard
+data	/man/1/listen
+data	/man/1/logwindow
+data	/man/1/mdb
+data	/man/1/mprof
+data	/man/1/ns
+data	/man/1/plumb
+data	/man/1/sh-alphabet
+data	/man/1/sh-csv
+data	/man/1/sh-file2chan
+data	/man/1/sh-sexprs
+data	/man/1/stack
+data	/man/1/stream
+data	/man/1/tail
+data	/man/1/tcs
+data	/man/1/tee
+data	/man/1/telnet
+data	/man/1/tktester
+data	/man/1/uuencode
+data	/man/1/webgrab
+data	/man/1/wm-misc
+data	/man/1/xd
+data	/man/10/2l
+data	/man/10/5coff
+data	/man/10/5cv
+data	/man/10/9load
+data	/man/10/a.out
+data	/man/10/acid
+data	/man/10/allocb
+data	/man/10/ar
+data	/man/10/c2l
+data	/man/10/conf
+data	/man/10/dev
+data	/man/10/devattach
+data	/man/10/dmainit
+data	/man/10/dynld
+data	/man/10/inm
+data	/man/10/intrenable
+data	/man/10/kprof
+data	/man/10/lock
+data	/man/10/malloc
+data	/man/10/ms2
+data	/man/10/odbc
+data	/man/10/qio
+data	/man/10/readnum
+data	/man/10/ref
+data	/man/10/styx
+data	/man/10/styxserver
+data	/man/10/xalloc
+data	/man/2/0intro
+data	/man/2/alphabet-intro
+data	/man/2/asn1
+data	/man/2/attrdb
+data	/man/2/bufio
+data	/man/2/bufio-chanfill
+data	/man/2/convcs
+data	/man/2/crc
+data	/man/2/csv
+data	/man/2/dbm
+data	/man/2/debug
+data	/man/2/devpointer
+data	/man/2/dhcpclient
+data	/man/2/dial
+data	/man/2/dis
+data	/man/2/disks
+data	/man/2/draw-0intro
+data	/man/2/draw-context
+data	/man/2/draw-image
+data	/man/2/draw-point
+data	/man/2/draw-screen
+data	/man/2/encoding
+data	/man/2/exception
+data	/man/2/factotum
+data	/man/2/filter
+data	/man/2/filter-deflate
+data	/man/2/filter-slip
+data	/man/2/format
+data	/man/2/hash
+data	/man/2/ida
+data	/man/2/imagefile
+data	/man/2/ip
+data	/man/2/keyring-0intro
+data	/man/2/keyring-crypt
+data	/man/2/keyring-getmsg
+data	/man/2/keyring-getstring
+data	/man/2/keyring-sha1
+data	/man/2/math-0intro
+data	/man/2/msgio
+data	/man/2/palmfile
+data	/man/2/plumbmsg
+data	/man/2/prefab-0intro
+data	/man/2/prefab-compound
+data	/man/2/print
+data	/man/2/prof
+data	/man/2/pslib
+data	/man/2/registries
+data	/man/2/scsiio
+data	/man/2/secstore
+data	/man/2/security-0intro
+data	/man/2/security-auth
+data	/man/2/security-random
+data	/man/2/security-ssl
+data	/man/2/sets
+data	/man/2/sexprs
+data	/man/2/sh
+data	/man/2/spki
+data	/man/2/spree
+data	/man/2/srv
+data	/man/2/styx
+data	/man/2/styxflush
+data	/man/2/styxservers
+data	/man/2/styxservers-nametree
+data	/man/2/sys-0intro
+data	/man/2/sys-dial
+data	/man/2/sys-dup
+data	/man/2/sys-export
+data	/man/2/sys-file2chan
+data	/man/2/sys-iounit
+data	/man/2/sys-pipe
+data	/man/2/sys-read
+data	/man/2/sys-self
+data	/man/2/tftp
+data	/man/2/timers
+data	/man/2/ubfa
+data	/man/2/venti
+data	/man/2/w3c-xpointers
+data	/man/2/wait
+data	/man/2/wmsrv
+data	/man/2/xml
+data	/man/3/arch
+data	/man/3/audio
+data	/man/3/boot
+data	/man/3/cmd
+data	/man/3/cons
+data	/man/3/dbg
+data	/man/3/draw
+data	/man/3/ds
+data	/man/3/dynld
+data	/man/3/eia
+data	/man/3/ether
+data	/man/3/flash
+data	/man/3/floppy
+data	/man/3/fpga
+data	/man/3/ftl
+data	/man/3/gpio
+data	/man/3/i2c
+data	/man/3/ip
+data	/man/3/kprof
+data	/man/3/logfs
+data	/man/3/lpt
+data	/man/3/mnt
+data	/man/3/mpeg
+data	/man/3/pbus
+data	/man/3/pipe
+data	/man/3/plap
+data	/man/3/pnp
+data	/man/3/prog
+data	/man/3/rtc
+data	/man/3/sd
+data	/man/3/sign
+data	/man/3/ssl
+data	/man/3/tls
+data	/man/3/touch
+data	/man/3/tv
+data	/man/3/usb
+data	/man/3/vga
+data	/man/3/vid
+data	/man/4/acme
+data	/man/4/dbfs
+data	/man/4/dossrv
+data	/man/4/factotum
+data	/man/4/ftpfs
+data	/man/4/iostats
+data	/man/4/keyfs
+data	/man/4/keysrv
+data	/man/4/logfile
+data	/man/4/memfs
+data	/man/4/namespace
+data	/man/4/palmsrv
+data	/man/4/ramfile
+data	/man/5/0intro
+data	/man/5/read
+data	/man/5/stat
+data	/man/5/version
+data	/man/6/attrdb
+data	/man/6/audio
+data	/man/6/dis
+data	/man/6/font
+data	/man/6/image
+data	/man/6/json
+data	/man/6/keys
+data	/man/6/keytext
+data	/man/6/ndb
+data	/man/6/plumbing
+data	/man/6/sbl
+data	/man/6/sexprs
+data	/man/6/ubfa
+data	/man/7/db
+data	/man/7/dbsrv
+data	/man/8/ai2key
+data	/man/8/bootpd
+data	/man/8/changelogin
+data	/man/8/collabsrv
+data	/man/8/cs
+data	/man/8/dns
+data	/man/8/ftl
+data	/man/8/kfscmd
+data	/man/8/ping
+data	/man/8/prep
+data	/man/8/rdbgsrv
+data	/man/8/rstyxd
+data	/man/8/signer
+data	/man/8/styxchat
+data.array	/man/2/dis
+data.dat	/man/1/blur
+data0	/man/3/usb
+data1	/man/2/asn1
+data1	/man/3/pipe
+data1	/man/3/usb
+database	/man/1/0intro
+database	/man/1/units
+database	/man/10/odbc
+database	/man/2/attrdb
+database	/man/2/dbm
+database	/man/2/palmfile
+database	/man/2/spree
+database	/man/4/dbfs
+database	/man/4/registry
+database	/man/6/attrdb
+database	/man/6/ndb
+database	/man/7/cddb
+database	/man/7/db
+database	/man/7/dbsrv
+database	/man/8/bootpd
+database	/man/8/cs
+database	/man/8/dns
+database	/man/8/getauthinfo
+databases	/man/1/0intro
+databases	/man/1/mux
+databases	/man/10/odbc
+databases	/man/2/attrdb
+databases	/man/2/dbm
+databases	/man/7/0intro
+databook	/man/3/i82365
+dataclass	/man/2/rfc822
+datafd	/man/7/db
+datafile	/man/1/calendar
+datafile	/man/1/idea
+datafile.id	/man/1/idea
+datafiles	/man/4/lockfs
+datagram	/man/3/ip
+datagrams	/man/3/ip
+datain	/man/3/tls
+dataout	/man/3/tls
+dataprefix	/man/2/print
+datasize	/man/10/ksize
+datasource	/man/10/odbc
+datatype	/man/2/venti
+dataxx	/man/2/plumbmsg
+date	/man/1/acme
+date	/man/1/ar
+date	/man/1/date
+date	/man/1/deb
+date	/man/1/emu
+date	/man/1/ls
+date	/man/1/mash-make
+date	/man/1/mk
+date	/man/1/secstore
+date	/man/1/wm-misc
+date	/man/1/wm-sh
+date	/man/10/ar
+date	/man/10/devattach
+date	/man/10/iar
+date	/man/10/mk
+date	/man/2/command
+date	/man/2/daytime
+date	/man/2/keyring-0intro
+date	/man/2/rfc822
+date	/man/2/tk
+date	/man/8/changelogin
+date	/man/8/collabsrv
+date	/man/8/create
+date	/man/8/createsignerkey
+date	/man/8/mkfs
+date	/man/8/prep
+date	/man/8/sntp
+date.b	/man/1/acme
+date.b	/man/1/date
+date.b	/man/1/wm-misc
+date.dis	/man/2/command
+date2epoch	/man/2/spki
+date2sec	/man/2/rfc822
+dates	/man/1/ar
+dates	/man/10/iar
+dates	/man/2/rfc822
+dates	/man/2/sys-0intro
+dates	/man/8/mkfs
+datops	/man/2/draw-image
+datum	/man/2/dbm
+datum	/man/2/geodesy
+datum	/man/3/tls
+datum	/man/5/stat
+datum	/man/6/dis
+datum2datum	/man/2/geodesy
+datums	/man/2/geodesy
+dawes	/man/2/format
+daylight	/man/2/daytime
+daytime	/man/1/date
+daytime	/man/2/daytime
+daytime	/man/2/palmfile
+daytime	/man/2/rfc822
+daytime	/man/8/createsignerkey
+daytime.b	/man/2/daytime
+daytime.m	/man/2/daytime
+db	/man/1/mdb
+db	/man/1/sendmail
+db	/man/10/acid
+db	/man/10/dev
+db	/man/10/devattach
+db	/man/10/odbc
+db	/man/2/attrdb
+db	/man/2/dbm
+db	/man/2/dial
+db	/man/2/sys-dial
+db	/man/7/db
+db	/man/7/dbsrv
+db	/man/8/register
+db.b	/man/7/db
+db.fetch	/man/2/dbm
+db.firstkey	/man/2/dbm
+db.m	/man/7/db
+db.nextkey	/man/2/dbm
+db.open	/man/2/attrdb
+db.sopen	/man/2/attrdb
+db1	/man/2/attrdb
+db2	/man/2/attrdb
+dbentry	/man/2/attrdb
+dbf	/man/2/dbm
+dbf.create	/man/2/dbm
+dbf.firstkey	/man/2/dbm
+dbf.nextkey	/man/2/dbm
+dbf.open	/man/2/dbm
+dbf.store	/man/2/dbm
+dbfile	/man/4/iostats
+dbfile	/man/4/registry
+dbfile	/man/8/bootpd
+dbfs	/man/1/calendar
+dbfs	/man/4/dbfs
+dbfs.b	/man/4/dbfs
+dbg	/man/3/dbg
+dbgctl	/man/2/debug
+dbgctl	/man/3/dbg
+dbgctl	/man/3/prog
+dbgctlflush	/man/3/dbg
+dbgctlstart	/man/3/dbg
+dbgctlstop	/man/3/dbg
+dbgdata	/man/3/dbg
+dbglog	/man/3/dbg
+dbgstart	/man/3/dbg
+dbh	/man/7/db
+dbinfo	/man/2/palmfile
+dbinfo.name	/man/2/palmfile
+dbm	/man/2/dbm
+dbm.b	/man/2/dbm
+dbm.m	/man/2/dbm
+dbname	/man/2/palmfile
+dbname	/man/7/db
+dbopen	/man/7/db
+dbottom	/man/2/spree-cardlib
+dbptr	/man/2/attrdb
+dbsize	/man/10/dev
+dbsrv	/man/10/odbc
+dbsrv	/man/7/db
+dbsrv	/man/7/dbsrv
+dbsrv.b	/man/7/dbsrv
+dc	/man/10/dev
+dcd	/man/3/eia
+dchars	/man/9/canvas
+dd	/man/1/dd
+dd	/man/2/spki
+dd	/man/4/kfs
+dd.b	/man/1/dd
+dd:1	/man/2/sets
+ddmmyyyy	/man/8/changelogin
+ddmmyyyy	/man/8/createsignerkey
+ddp	/man/4/vacfs
+de	/man/10/9load
+de	/man/2/asn1
+deactivate	/man/9/menu
+deactivated	/man/9/menu
+deactivates	/man/9/button
+deactivates	/man/9/checkbutton
+deactivates	/man/9/menubutton
+deactivates	/man/9/radiobutton
+dead	/man/2/sh
+dead	/man/3/dbg
+deadlocks	/man/10/lock
+deal	/man/1/grid-monitor
+deal	/man/10/intrenable
+deal	/man/10/mk
+deal	/man/10/readnum
+deal	/man/2/geodesy
+deal	/man/2/spree-cardlib
+deal	/man/2/styxflush
+deal	/man/6/image
+deal	/man/8/svc
+dealing	/man/1/mash-tk
+dealing	/man/2/spree-cardlib
+dealing	/man/4/acme
+dealing	/man/9/entry
+deallocate	/man/10/allocb
+deallocating	/man/10/error
+deallocations	/man/3/prof
+deals	/man/1/emu
+deals	/man/10/styxserver
+deals	/man/2/spree-cardlib
+deals	/man/2/styxflush
+deals	/man/2/tk
+dealt	/man/1/cprof
+dealt	/man/10/conf
+dealt	/man/2/styxflush
+deb	/man/1/0intro
+deb	/man/1/deb
+deb	/man/1/ps
+deb	/man/1/stack
+deb	/man/1/wm-misc
+deb	/man/2/debug
+deb	/man/6/sbl
+deb.b	/man/1/deb
+debate	/man/1/diff
+debdata.b	/man/1/deb
+debian	/man/1/ar
+debsrc.b	/man/1/deb
+debug	/man/1/0intro
+debug	/man/1/deb
+debug	/man/1/stack
+debug	/man/1/vacget
+debug	/man/1/wm-misc
+debug	/man/10/5cv
+debug	/man/10/acid
+debug	/man/2/debug
+debug	/man/2/dhcpclient
+debug	/man/2/print
+debug	/man/3/dbg
+debug	/man/3/ip
+debug	/man/4/factotum
+debug	/man/4/vacfs
+debug	/man/6/sbl
+debug	/man/8/plumber
+debug	/man/8/rdbgsrv
+debug	/man/9/text
+debug.b	/man/2/debug
+debug.m	/man/2/debug
+debugged	/man/1/deb
+debugged	/man/10/acid
+debugged	/man/3/dbg
+debugged	/man/3/prog
+debugged	/man/4/ftpfs
+debugger	/man/1/0intro
+debugger	/man/1/deb
+debugger	/man/10/acid
+debugger	/man/3/dbg
+debuggers	/man/6/sbl
+debugging	/man/1/0intro
+debugging	/man/1/collab-clients
+debugging	/man/1/deb
+debugging	/man/1/emu
+debugging	/man/1/kill
+debugging	/man/1/limbo
+debugging	/man/1/mk
+debugging	/man/1/tiny
+debugging	/man/10/2l
+debugging	/man/10/5coff
+debugging	/man/10/mk
+debugging	/man/10/odbc
+debugging	/man/10/plan9.ini
+debugging	/man/10/xalloc
+debugging	/man/2/alphabet-intro
+debugging	/man/2/debug
+debugging	/man/2/exception
+debugging	/man/2/filter-deflate
+debugging	/man/2/scsiio
+debugging	/man/2/styx
+debugging	/man/2/ubfa
+debugging	/man/3/cons
+debugging	/man/3/dbg
+debugging	/man/3/draw
+debugging	/man/3/ftl
+debugging	/man/3/ip
+debugging	/man/3/prog
+debugging	/man/3/touch
+debugging	/man/4/dossrv
+debugging	/man/4/factotum
+debugging	/man/4/iostats
+debugging	/man/4/keyfs
+debugging	/man/6/dis
+debugging	/man/6/sbl
+debugging	/man/8/bootpd
+debugging	/man/8/dhcp
+debugging	/man/8/httpd
+debugging	/man/8/mangaload
+debugging	/man/8/prep
+debugging	/man/8/rdbgsrv
+debugging	/man/8/sntp
+debugging	/man/9/text
+debugon	/man/3/draw
+dec	/man/10/a.out
+dec	/man/2/encoding
+dec	/man/2/w3c-uris
+decakilometre	/man/2/geodesy
+decametre	/man/2/geodesy
+decendants	/man/9/destroy
+decide	/man/1/plumb
+decide	/man/10/dev
+decide	/man/10/dynld
+decide	/man/10/sleep
+decide	/man/2/prefab-element
+decide	/man/2/styx
+decide	/man/5/stat
+decide	/man/8/init
+decide	/man/8/plumber
+decided	/man/10/plan9.ini
+decides	/man/2/spree
+decimal	/man/1/cmp
+decimal	/man/1/fc
+decimal	/man/1/freq
+decimal	/man/1/gzip
+decimal	/man/1/look
+decimal	/man/1/m4
+decimal	/man/1/math-misc
+decimal	/man/1/mdb
+decimal	/man/1/sh-regex
+decimal	/man/1/sh-std
+decimal	/man/1/strings
+decimal	/man/1/timestamp
+decimal	/man/1/wm
+decimal	/man/1/xd
+decimal	/man/10/2l
+decimal	/man/10/5cv
+decimal	/man/10/ar
+decimal	/man/10/atoi
+decimal	/man/10/print
+decimal	/man/10/readnum
+decimal	/man/2/disks
+decimal	/man/2/drawmux
+decimal	/man/2/geodesy
+decimal	/man/2/ip
+decimal	/man/2/keyring-getmsg
+decimal	/man/2/math-0intro
+decimal	/man/2/msgio
+decimal	/man/2/plumbmsg
+decimal	/man/2/sexprs
+decimal	/man/2/spree-allow
+decimal	/man/2/spree-cardlib
+decimal	/man/2/string
+decimal	/man/2/sys-print
+decimal	/man/3/cmd
+decimal	/man/3/cons
+decimal	/man/3/draw
+decimal	/man/3/dup
+decimal	/man/3/ether
+decimal	/man/3/flash
+decimal	/man/3/ip
+decimal	/man/3/lpt
+decimal	/man/3/pnp
+decimal	/man/3/pointer
+decimal	/man/3/prog
+decimal	/man/3/rtc
+decimal	/man/3/sign
+decimal	/man/3/switch
+decimal	/man/3/touch
+decimal	/man/4/acme
+decimal	/man/4/registry
+decimal	/man/6/font
+decimal	/man/6/image
+decimal	/man/6/keytext
+decimal	/man/6/sbl
+decimal	/man/6/sexprs
+decimal	/man/6/users
+decimal	/man/8/ai2key
+decimal	/man/8/applylog
+decimal	/man/8/collabsrv
+decimal	/man/8/rstyxd
+decimal	/man/8/styxchat
+decimal	/man/9/bind
+decimal	/man/9/canvas
+decimal	/man/9/image
+decimal	/man/9/listbox
+decimal	/man/9/menu
+decimal	/man/9/text
+decimal	/man/9/types
+decimally	/man/4/acme
+deck	/man/2/spree-cardlib
+decl	/man/2/w3c-css
+decl.values	/man/2/w3c-css
+declaration	/man/1/calc
+declaration	/man/1/deb
+declaration	/man/1/sh-alphabet
+declaration	/man/1/yacc
+declaration	/man/2/0intro
+declaration	/man/2/alphabet-intro
+declaration	/man/2/sys-self
+declaration	/man/2/w3c-css
+declarations	/man/1/alphabet-abc
+declarations	/man/1/alphabet-fs
+declarations	/man/1/alphabet-grid
+declarations	/man/1/limbo
+declarations	/man/1/sh-alphabet
+declarations	/man/10/0intro
+declarations	/man/10/c2l
+declarations	/man/10/conf
+declarations	/man/2/sys-self
+declarations	/man/2/w3c-css
+declarations	/man/3/dbg
+declarations	/man/4/namespace
+declarations	/man/6/man
+declare	/man/1/sh-alphabet
+declare	/man/2/0intro
+declared	/man/1/calc
+declared	/man/1/deb
+declared	/man/1/sh-alphabet
+declared	/man/1/yacc
+declared	/man/10/2c
+declared	/man/10/acid
+declared	/man/10/dev
+declared	/man/2/0intro
+declared	/man/2/alphabet-intro
+declared	/man/2/dis
+declared	/man/2/draw-screen
+declared	/man/2/sets
+declared	/man/2/sh
+declared	/man/2/sys-0intro
+declared	/man/2/tk
+declared	/man/3/i2c
+declared	/man/6/dis
+declared	/man/6/sbl
+declares	/man/1/listen
+declares	/man/1/sh-alphabet
+declares	/man/2/alphabet-intro
+declares	/man/2/disks
+declaring	/man/1/alphabet-main
+declaring	/man/10/2c
+decls	/man/2/w3c-css
+decode	/man/1/uuencode
+decode	/man/2/asn1
+decode	/man/2/filter-slip
+decode	/man/2/palmfile
+decode	/man/2/w3c-uris
+decoded	/man/1/uuencode
+decoded	/man/10/rune
+decoded	/man/2/asn1
+decoded	/man/2/dis
+decoded	/man/2/w3c-css
+decoded	/man/2/w3c-uris
+decoded	/man/6/sexprs
+decoder	/man/3/mpeg
+decoders	/man/10/ms2
+decoders	/man/2/encoding
+decodes	/man/2/asn1
+decodes	/man/2/dis
+decoding	/man/1/stack
+decoding	/man/1/uuencode
+decoding	/man/2/asn1
+decoding	/man/2/encoding
+decoding	/man/2/filter-slip
+decoding	/man/2/json
+decoding	/man/2/ubfa
+decoding	/man/2/w3c-css
+decoding	/man/2/w3c-uris
+decoding	/man/3/tls
+decompress	/man/10/5cv
+decompresses	/man/10/5cv
+decompression	/man/1/gzip
+decorated	/man/1/sh
+decoration	/man/2/tkclient
+decoration	/man/2/wmclient
+decorations	/man/2/wmclient
+decrease	/man/2/volume
+decreasing	/man/1/calc
+decreasing	/man/10/kprof
+decref	/man/10/ref
+decrement	/man/10/dev
+decremented	/man/9/scale
+decrements	/man/1/mdb
+decrements	/man/10/newchan
+decrements	/man/10/ref
+decryped	/man/1/idea
+decrypt	/man/1/crypt
+decrypt	/man/1/idea
+decrypt	/man/2/keyring-crypt
+decrypt	/man/2/keyring-rc4
+decrypt	/man/2/secstore
+decrypt	/man/2/security-0intro
+decrypt	/man/8/svc
+decrypted	/man/1/idea
+decrypted	/man/1/secstore
+decrypted	/man/2/keyring-crypt
+decrypted	/man/2/secstore
+decrypted	/man/2/security-0intro
+decrypting	/man/1/crypt
+decryption	/man/1/idea
+decryption	/man/2/keyring-crypt
+decryption	/man/2/security-0intro
+decryption	/man/7/db
+decryption	/man/8/ai2key
+decryptor	/man/2/keyring-crypt
+decrypts	/man/1/crypt
+decrypts	/man/1/idea
+decrypts	/man/2/secstore
+decrypts	/man/4/keyfs
+decrypts	/man/6/keys
+dectect	/man/3/gpio
+dedicated	/man/10/2c
+dedicating	/man/10/a.out
+deduced	/man/10/5cv
+deemed	/man/1/m4
+deemed	/man/10/devattach
+deep	/man/10/error
+deep	/man/2/draw-image
+deep	/man/6/image
+deeply	/man/1/acme
+def	/man/1/mash-tk
+def	/man/10/2c
+def	/man/10/c2l
+def.dis	/man/1/asm
+def.s	/man/1/asm
+defa	/man/2/dis
+default	/man/1/9win
+default	/man/1/acme
+default	/man/1/alphabet-fs
+default	/man/1/alphabet-main
+default	/man/1/avr
+default	/man/1/bind
+default	/man/1/calc
+default	/man/1/calendar
+default	/man/1/charon
+default	/man/1/chmod
+default	/man/1/cmp
+default	/man/1/collab
+default	/man/1/collab-clients
+default	/man/1/cp
+default	/man/1/cpu
+default	/man/1/crypt
+default	/man/1/dd
+default	/man/1/deb
+default	/man/1/dmview
+default	/man/1/du
+default	/man/1/emu
+default	/man/1/fmt
+default	/man/1/freq
+default	/man/1/fs
+default	/man/1/ftree
+default	/man/1/gettar
+default	/man/1/grid-session
+default	/man/1/gzip
+default	/man/1/itest
+default	/man/1/limbo
+default	/man/1/listen
+default	/man/1/logon
+default	/man/1/look
+default	/man/1/m4
+default	/man/1/man
+default	/man/1/mash
+default	/man/1/mash-tk
+default	/man/1/math-misc
+default	/man/1/mc
+default	/man/1/mdb
+default	/man/1/miniterm
+default	/man/1/mk
+default	/man/1/mprof
+default	/man/1/ns
+default	/man/1/nsbuild
+default	/man/1/p
+default	/man/1/passwd
+default	/man/1/plumb
+default	/man/1/prof
+default	/man/1/rcmd
+default	/man/1/read
+default	/man/1/secstore
+default	/man/1/sh-arg
+default	/man/1/sh-file2chan
+default	/man/1/sh-std
+default	/man/1/sh-tk
+default	/man/1/sort
+default	/man/1/spree-join
+default	/man/1/stream
+default	/man/1/strings
+default	/man/1/tail
+default	/man/1/tcs
+default	/man/1/telnet
+default	/man/1/tiny
+default	/man/1/tkcmd
+default	/man/1/tktester
+default	/man/1/touch
+default	/man/1/vacget
+default	/man/1/wm
+default	/man/1/wm-misc
+default	/man/1/xd
+default	/man/1/yacc
+default	/man/1/zeros
+default	/man/10/2a
+default	/man/10/2c
+default	/man/10/2l
+default	/man/10/5cv
+default	/man/10/9load
+default	/man/10/acid
+default	/man/10/dev
+default	/man/10/devattach
+default	/man/10/dynld
+default	/man/10/eve
+default	/man/10/ksize
+default	/man/10/mk
+default	/man/10/ntsrv
+default	/man/10/odbc
+default	/man/10/plan9.ini
+default	/man/10/qio
+default	/man/10/srclist
+default	/man/10/styxserver
+default	/man/2/convcs
+default	/man/2/crc
+default	/man/2/devpointer
+default	/man/2/dhcpclient
+default	/man/2/dial
+default	/man/2/dis
+default	/man/2/draw-display
+default	/man/2/draw-example
+default	/man/2/draw-font
+default	/man/2/draw-image
+default	/man/2/exception
+default	/man/2/geodesy
+default	/man/2/keyring-auth
+default	/man/2/keyset
+default	/man/2/math-fp
+default	/man/2/prefab-compound
+default	/man/2/print
+default	/man/2/prof
+default	/man/2/registries
+default	/man/2/secstore
+default	/man/2/security-0intro
+default	/man/2/security-auth
+default	/man/2/security-login
+default	/man/2/sh
+default	/man/2/smtp
+default	/man/2/spree-cardlib
+default	/man/2/styxservers
+default	/man/2/sys-bind
+default	/man/2/sys-fversion
+default	/man/2/tkclient
+default	/man/2/w3c-xpointers
+default	/man/2/wmclient
+default	/man/3/audio
+default	/man/3/cmd
+default	/man/3/cons
+default	/man/3/dbg
+default	/man/3/eia
+default	/man/3/flash
+default	/man/3/gpio
+default	/man/3/i2c
+default	/man/3/ip
+default	/man/3/prof
+default	/man/3/sd
+default	/man/3/ssl
+default	/man/3/tv
+default	/man/3/usb
+default	/man/3/vga
+default	/man/4/9srvfs
+default	/man/4/acme
+default	/man/4/archfs
+default	/man/4/dossrv
+default	/man/4/export
+default	/man/4/factotum
+default	/man/4/ftpfs
+default	/man/4/import
+default	/man/4/iostats
+default	/man/4/keyfs
+default	/man/4/kfs
+default	/man/4/memfs
+default	/man/4/namespace
+default	/man/4/palmsrv
+default	/man/4/registry
+default	/man/4/tarfs
+default	/man/4/vacfs
+default	/man/6/dis
+default	/man/6/font
+default	/man/6/man
+default	/man/6/namespace
+default	/man/6/ndb
+default	/man/6/translate
+default	/man/7/cddb
+default	/man/7/db
+default	/man/7/dbsrv
+default	/man/8/ai2key
+default	/man/8/applylog
+default	/man/8/bootpd
+default	/man/8/changelogin
+default	/man/8/collabsrv
+default	/man/8/create
+default	/man/8/createsignerkey
+default	/man/8/cs
+default	/man/8/dhcp
+default	/man/8/dns
+default	/man/8/getauthinfo
+default	/man/8/httpd
+default	/man/8/init
+default	/man/8/kfscmd
+default	/man/8/mangaload
+default	/man/8/manufacture
+default	/man/8/mkfs
+default	/man/8/ping
+default	/man/8/plumber
+default	/man/8/prep
+default	/man/8/rdbgsrv
+default	/man/8/register
+default	/man/8/rip
+default	/man/8/rstyxd
+default	/man/8/sntp
+default	/man/8/styxchat
+default	/man/9/button
+default	/man/9/canvas
+default	/man/9/checkbutton
+default	/man/9/cursor
+default	/man/9/entry
+default	/man/9/frame
+default	/man/9/grid
+default	/man/9/label
+default	/man/9/listbox
+default	/man/9/menu
+default	/man/9/menubutton
+default	/man/9/options
+default	/man/9/pack
+default	/man/9/panel
+default	/man/9/radiobutton
+default	/man/9/scale
+default	/man/9/scrollbar
+default	/man/9/text
+default.css	/man/1/ebook
+defaultheight	/man/1/charon
+defaulting	/man/10/9load
+defaults	/man/1/acme
+defaults	/man/1/math-misc
+defaults	/man/1/rcmd
+defaults	/man/10/2c
+defaults	/man/10/2l
+defaults	/man/10/plan9.ini
+defaults	/man/10/styxserver
+defaults	/man/2/keyring-ipint
+defaults	/man/2/registries
+defaults	/man/2/secstore
+defaults	/man/2/sys-fversion
+defaults	/man/4/dossrv
+defaults	/man/8/applylog
+defaults	/man/8/create
+defaults	/man/8/getauthinfo
+defaults	/man/9/canvas
+defaults	/man/9/checkbutton
+defaults	/man/9/entry
+defaults	/man/9/grid
+defaults	/man/9/listbox
+defaults	/man/9/menu
+defaults	/man/9/pack
+defaults	/man/9/radiobutton
+defaults	/man/9/scale
+defaults	/man/9/see
+defaults	/man/9/text
+defaultwidth	/man/1/charon
+defaultxxx	/man/2/keyring-auth
+defb	/man/2/dis
+defense	/man/9/1copyright
+deferred	/man/9/text
+deferring	/man/1/wm-sh
+deff	/man/2/dis
+define	/man/1/charon
+define	/man/1/emu
+define	/man/1/m4
+define	/man/1/mash
+define	/man/1/mash-tk
+define	/man/1/mk
+define	/man/1/sh-alphabet
+define	/man/1/sh-regex
+define	/man/1/wm-sh
+define	/man/1/yacc
+define	/man/10/2c
+define	/man/10/a.out
+define	/man/10/ar
+define	/man/10/c2l
+define	/man/10/mk
+define	/man/10/seconds
+define	/man/10/styx
+define	/man/10/styxserver
+define	/man/2/alphabet-intro
+define	/man/2/draw-0intro
+define	/man/2/draw-font
+define	/man/2/draw-rect
+define	/man/2/format
+define	/man/2/sh
+define	/man/2/spree-cardlib
+define	/man/2/styxservers
+define	/man/3/kprof
+define	/man/9/canvas
+define	/man/9/image
+define	/man/9/scrollbar
+defined	/man/1/acme
+defined	/man/1/alphabet-abc
+defined	/man/1/alphabet-fs
+defined	/man/1/alphabet-grid
+defined	/man/1/alphabet-main
+defined	/man/1/auplay
+defined	/man/1/grep
+defined	/man/1/m4
+defined	/man/1/mash
+defined	/man/1/mash-make
+defined	/man/1/mash-tk
+defined	/man/1/math-misc
+defined	/man/1/mk
+defined	/man/1/secstore
+defined	/man/1/sh
+defined	/man/1/sh-mload
+defined	/man/1/sh-std
+defined	/man/1/yacc
+defined	/man/10/2c
+defined	/man/10/a.out
+defined	/man/10/acid
+defined	/man/10/ar
+defined	/man/10/c2l
+defined	/man/10/dynld
+defined	/man/10/inm
+defined	/man/10/intrenable
+defined	/man/10/kbdputc
+defined	/man/10/mk
+defined	/man/10/parsecmd
+defined	/man/10/plan9.ini
+defined	/man/10/rune
+defined	/man/10/seconds
+defined	/man/10/styx
+defined	/man/10/styxserver
+defined	/man/2/alphabet-intro
+defined	/man/2/attrdb
+defined	/man/2/bufio
+defined	/man/2/cfg
+defined	/man/2/convcs
+defined	/man/2/dis
+defined	/man/2/disks
+defined	/man/2/draw-0intro
+defined	/man/2/draw-context
+defined	/man/2/draw-font
+defined	/man/2/draw-image
+defined	/man/2/encoding
+defined	/man/2/format
+defined	/man/2/fsproto
+defined	/man/2/geodesy
+defined	/man/2/imagefile
+defined	/man/2/ip
+defined	/man/2/ir
+defined	/man/2/keyring-gensk
+defined	/man/2/lists
+defined	/man/2/math-fp
+defined	/man/2/math-linalg
+defined	/man/2/newns
+defined	/man/2/palmfile
+defined	/man/2/pop3
+defined	/man/2/prefab-compound
+defined	/man/2/prefab-element
+defined	/man/2/prefab-style
+defined	/man/2/print
+defined	/man/2/readdir
+defined	/man/2/regex
+defined	/man/2/secstore
+defined	/man/2/sets
+defined	/man/2/sexprs
+defined	/man/2/sh
+defined	/man/2/smtp
+defined	/man/2/spki
+defined	/man/2/spki-verifier
+defined	/man/2/styx
+defined	/man/2/styxconv
+defined	/man/2/styxflush
+defined	/man/2/styxservers
+defined	/man/2/sys-bind
+defined	/man/2/sys-fversion
+defined	/man/2/sys-open
+defined	/man/2/sys-self
+defined	/man/2/sys-stat
+defined	/man/2/translate
+defined	/man/2/ubfa
+defined	/man/2/w3c-uris
+defined	/man/2/w3c-xpointers
+defined	/man/3/cons
+defined	/man/3/draw
+defined	/man/3/dynld
+defined	/man/3/fs
+defined	/man/3/ftl
+defined	/man/3/prog
+defined	/man/3/sd
+defined	/man/3/usb
+defined	/man/4/keyfs
+defined	/man/4/namespace
+defined	/man/5/0intro
+defined	/man/5/attach
+defined	/man/5/error
+defined	/man/5/stat
+defined	/man/5/version
+defined	/man/6/dis
+defined	/man/6/json
+defined	/man/6/keyboard
+defined	/man/6/keytext
+defined	/man/6/man
+defined	/man/6/ndb
+defined	/man/6/sbl
+defined	/man/6/sexprs
+defined	/man/6/ubfa
+defined	/man/8/ai2key
+defined	/man/8/collabsrv
+defined	/man/8/styxchat
+defined	/man/8/virgild
+defined	/man/9/1copyright
+defined	/man/9/bind
+defined	/man/9/canvas
+defined	/man/9/listbox
+defined	/man/9/scrollbar
+defined	/man/9/text
+defines	/man/1/alphabet-abc
+defines	/man/1/alphabet-fs
+defines	/man/1/alphabet-grid
+defines	/man/1/m4
+defines	/man/1/mash
+defines	/man/1/sh-alphabet
+defines	/man/1/sh-file2chan
+defines	/man/1/sh-regex
+defines	/man/1/sh-std
+defines	/man/1/sh-tk
+defines	/man/1/toolbar
+defines	/man/1/xd
+defines	/man/1/yacc
+defines	/man/10/dev
+defines	/man/10/dynld
+defines	/man/10/plan9.ini
+defines	/man/2/alphabet-intro
+defines	/man/2/command
+defines	/man/2/convcs
+defines	/man/2/dis
+defines	/man/2/disks
+defines	/man/2/draw-0intro
+defines	/man/2/draw-display
+defines	/man/2/draw-font
+defines	/man/2/draw-image
+defines	/man/2/draw-rect
+defines	/man/2/filter
+defines	/man/2/format
+defines	/man/2/imagefile
+defines	/man/2/ir
+defines	/man/2/prefab-0intro
+defines	/man/2/prefab-element
+defines	/man/2/sh
+defines	/man/2/spki
+defines	/man/2/spree-cardlib
+defines	/man/2/styx
+defines	/man/2/styxservers
+defines	/man/2/sys-stat
+defines	/man/3/cons
+defines	/man/5/version
+defines	/man/6/image
+defines	/man/6/keytext
+defines	/man/6/ndb
+defines	/man/6/proto
+defines	/man/6/translate
+defines	/man/6/users
+defines	/man/8/collabsrv
+defines	/man/8/httpd
+defines	/man/9/canvas
+defining	/man/10/error
+defining	/man/10/getfields
+defining	/man/2/alphabet-intro
+defining	/man/2/asn1
+defining	/man/2/draw-0intro
+defining	/man/2/draw-image
+defining	/man/2/ip
+defining	/man/2/prefab-compound
+defining	/man/2/spki
+defining	/man/2/styxservers
+defining	/man/5/attach
+defining	/man/6/font
+defining	/man/9/button
+defining	/man/9/canvas
+defining	/man/9/checkbutton
+defining	/man/9/entry
+defining	/man/9/listbox
+defining	/man/9/menu
+defining	/man/9/menubutton
+defining	/man/9/radiobutton
+defining	/man/9/scale
+defining	/man/9/text
+definite	/man/1/calc
+definitely	/man/1/grid-session
+definition	/man/1/bind
+definition	/man/1/m4
+definition	/man/1/mash
+definition	/man/1/sh
+definition	/man/1/sh-alphabet
+definition	/man/1/yacc
+definition	/man/10/2c
+definition	/man/10/c2l
+definition	/man/10/strcat
+definition	/man/2/0intro
+definition	/man/2/draw-0intro
+definition	/man/2/format
+definition	/man/2/sh
+definition	/man/2/sys-bind
+definition	/man/2/sys-self
+definition	/man/2/w3c-xpointers
+definition	/man/2/xml
+definition	/man/5/attach
+definition	/man/6/image
+definition	/man/6/sexprs
+definition	/man/8/register
+definition	/man/8/styxchat
+definition	/man/9/options
+definitions	/man/1/alphabet-main
+definitions	/man/1/calc
+definitions	/man/1/limbo
+definitions	/man/1/m4
+definitions	/man/1/sh
+definitions	/man/1/sh-alphabet
+definitions	/man/1/yacc
+definitions	/man/10/2c
+definitions	/man/10/acid
+definitions	/man/10/c2l
+definitions	/man/2/asn1
+definitions	/man/2/dial
+definitions	/man/2/prefab-0intro
+definitions	/man/2/prefab-style
+definitions	/man/2/sys-dial
+definitions	/man/2/w3c-css
+definitions	/man/3/draw
+definitions	/man/4/namespace
+definitions	/man/6/dis
+definitions	/man/6/sexprs
+definitions	/man/7/db
+defl	/man/2/dis
+deflate	/man/1/gzip
+deflate	/man/2/filter
+deflate	/man/2/filter-deflate
+deflate.b	/man/2/filter-deflate
+deflatepath	/man/2/filter-deflate
+defn	/man/10/acid
+defnet	/man/2/dial
+defprinter	/man/2/print
+defs	/man/2/dis
+defsvc	/man/2/dial
+defunct	/man/10/acid
+defw	/man/2/dis
+defz	/man/2/dis
+deg	/man/1/fc
+deg	/man/1/math-misc
+deg	/man/2/geodesy
+deg:min	/man/2/geodesy
+deg:min:sec	/man/2/geodesy
+degenerate	/man/2/prefab-element
+degree	/man/1/math-misc
+degree	/man/2/math-fp
+degree	/man/2/security-0intro
+degree	/man/3/cmd
+degree	/man/3/draw
+degrees	/man/1/calc
+degrees	/man/10/plan9.ini
+degrees	/man/2/draw-image
+degrees	/man/2/geodesy
+degrees	/man/9/canvas
+del	/man/1/acme
+del	/man/1/grid-monitor
+del	/man/1/sh-tk
+del	/man/10/9load
+del	/man/10/ntsrv
+del	/man/2/sets
+del	/man/2/spree
+del	/man/2/spree-allow
+del	/man/3/prog
+del	/man/4/acme
+del	/man/4/grid-cpu
+del	/man/4/spree
+del	/man/6/keyboard
+delattr	/man/2/factotum
+delay	/man/10/delay
+delay	/man/10/plan9.ini
+delay	/man/10/sleep
+delay	/man/2/sys-sleep
+delay	/man/3/touch
+delay	/man/5/0intro
+delay	/man/8/virgild
+delayed	/man/2/spki
+delayed	/man/2/styxflush
+delayed	/man/9/options
+delays	/man/10/delay
+delays	/man/10/seconds
+delays	/man/4/palmsrv
+delbpt	/man/2/debug
+delclient	/man/1/wm
+delcol	/man/1/acme
+delegate	/man/2/spki
+delegated	/man/6/ndb
+delegation	/man/2/spki
+delete	/man/1/acme
+delete	/man/1/ar
+delete	/man/1/brutus
+delete	/man/1/secstore
+delete	/man/1/tktester
+delete	/man/1/tr
+delete	/man/1/wm
+delete	/man/1/wm-sh
+delete	/man/10/acid
+delete	/man/10/iar
+delete	/man/10/ntsrv
+delete	/man/10/odbc
+delete	/man/2/dbm
+delete	/man/2/dict
+delete	/man/2/hash
+delete	/man/2/ir
+delete	/man/2/lists
+delete	/man/2/palmfile
+delete	/man/2/pop3
+delete	/man/2/spree
+delete	/man/2/styxservers
+delete	/man/2/tk
+delete	/man/2/wmclient
+delete	/man/3/cons
+delete	/man/3/dbg
+delete	/man/4/acme
+delete	/man/4/dbfs
+delete	/man/4/factotum
+delete	/man/4/spree
+delete	/man/6/keyboard
+delete	/man/8/applylog
+delete	/man/8/kfscmd
+delete	/man/8/prep
+delete	/man/9/canvas
+delete	/man/9/entry
+delete	/man/9/image
+delete	/man/9/listbox
+delete	/man/9/menu
+delete	/man/9/text
+deletechildren	/man/2/spree
+deleted	/man/1/0intro
+deleted	/man/1/acme
+deleted	/man/1/mk
+deleted	/man/1/sh-tk
+deleted	/man/1/wm-sh
+deleted	/man/10/iar
+deleted	/man/10/mk
+deleted	/man/10/styxserver
+deleted	/man/2/draw-screen
+deleted	/man/2/pop3
+deleted	/man/2/spree
+deleted	/man/2/spree-allow
+deleted	/man/2/spree-cardlib
+deleted	/man/2/sys-0intro
+deleted	/man/2/tk
+deleted	/man/2/tkclient
+deleted	/man/2/wmsrv
+deleted	/man/3/draw
+deleted	/man/3/sd
+deleted	/man/4/acme
+deleted	/man/4/spree
+deleted	/man/5/0intro
+deleted	/man/8/applylog
+deleted	/man/9/entry
+deleted	/man/9/grid
+deleted	/man/9/image
+deleted	/man/9/listbox
+deleted	/man/9/text
+deletes	/man/1/basename
+deletes	/man/1/sh-tk
+deletes	/man/1/tktester
+deletes	/man/2/debug
+deletes	/man/2/dict
+deletes	/man/2/pop3
+deletes	/man/2/secstore
+deletes	/man/2/spree
+deletes	/man/2/spree-allow
+deletes	/man/2/spree-cardlib
+deletes	/man/2/sys-remove
+deletes	/man/5/0intro
+deletes	/man/9/destroy
+deletes	/man/9/entry
+deletes	/man/9/grid
+deletes	/man/9/image
+deletes	/man/9/listbox
+deletes	/man/9/text
+deleting	/man/1/wm-misc
+deleting	/man/9/canvas
+deletion	/man/1/acme
+deletion	/man/1/tr
+deletion	/man/2/dict
+deletions	/man/8/applylog
+delfid	/man/2/styxservers
+delight	/man/2/draw-example
+delight.bit	/man/2/draw-example
+delight.r.max	/man/2/draw-example
+delight.r.max.x	/man/2/draw-example
+delight.r.min.add	/man/2/draw-example
+delight.r.min.x	/man/2/draw-example
+delim	/man/2/sys-tokenize
+delimit	/man/1/idea
+delimit	/man/10/qio
+delimit	/man/3/pipe
+delimited	/man/1/acme
+delimited	/man/2/cfg
+delimited	/man/2/keyring-getstring
+delimited	/man/2/msgio
+delimited	/man/2/sys-tokenize
+delimited	/man/3/ssl
+delimited	/man/4/factotum
+delimited	/man/9/canvas
+delimiter	/man/1/acme
+delimiter	/man/10/getfields
+delimiter	/man/2/filepat
+delimiter	/man/2/sexprs
+delimiter	/man/2/sys-0intro
+delimiter	/man/2/sys-tokenize
+delimiter	/man/6/regexp
+delimiters	/man/1/acme
+delimiters	/man/1/m4
+delimiters	/man/10/getfields
+delimiters	/man/2/msgio
+delimiters	/man/2/w3c-uris
+delimiters	/man/2/w3c-xpointers
+delimiters	/man/3/ssl
+delimiting	/man/2/w3c-uris
+delims	/man/10/getfields
+delindex	/man/2/spree-cardlib
+deliver	/man/1/tail
+deliver	/man/2/sys-file2chan
+delivered	/man/1/acme
+delivered	/man/2/timers
+delivered	/man/3/cons
+delivered	/man/3/ip
+delivered	/man/8/collabsrv
+delivered	/man/8/register
+delivered	/man/9/bind
+delivered	/man/9/canvas
+delivered	/man/9/focus
+delivers	/man/1/sendmail
+delivers	/man/2/draw-context
+delivers	/man/2/popup
+delivery	/man/2/dhcpclient
+delivery	/man/2/smtp
+delivery	/man/2/tk
+delivery	/man/6/ndb
+delivery	/man/9/bind
+delkey	/man/4/factotum
+dell	/man/10/plan9.ini
+dellay	/man/2/spree-cardlib
+delmenu	/man/1/toolbar
+delpart	/man/3/ftl
+delpart	/man/3/sd
+delta	/man/9/scrollbar
+deltax	/man/9/scrollbar
+deltay	/man/9/scrollbar
+demand	/man/1/0intro
+demand	/man/1/secstore
+demand	/man/2/registries
+demand	/man/2/secstore
+demand	/man/4/mntgen
+demand	/man/5/0intro
+demands	/man/1/secstore
+demands	/man/4/ftpfs
+demarcated	/man/1/acme
+demftd	/man/1/miniterm
+demise	/man/2/command
+demo	/man/1/blur
+demo	/man/1/mux
+demonstrate	/man/1/blur
+demonstrate	/man/6/colour
+demonstrated	/man/1/mux
+demonstration	/man/1/miniterm
+demonstration	/man/1/mux
+demonstrations	/man/1/mux
+demultiplexed	/man/3/ether
+den	/man/6/colour
+denied	/man/10/styxserver
+denied	/man/2/styxservers
+denied	/man/3/fs
+denies	/man/2/spree
+denominator	/man/6/keyboard
+denormalized	/man/2/math-0intro
+denotations	/man/6/json
+denote	/man/1/math-misc
+denote	/man/1/xd
+denote	/man/10/intrenable
+denote	/man/2/math-linalg
+denote	/man/2/w3c-css
+denote	/man/6/proto
+denote	/man/6/sexprs
+denoted	/man/1/units
+denoted	/man/10/intrenable
+denoted	/man/2/scsiio
+denoted	/man/6/man
+denotes	/man/1/blur
+denotes	/man/1/math-misc
+denotes	/man/2/asn1
+denotes	/man/9/canvas
+denoting	/man/3/gpio
+density	/man/3/floppy
+density	/man/8/prep
+denx	/man/1/mash
+deny	/man/5/stat
+dep	/man/3/dynld
+department	/man/9/1copyright
+departure	/man/1/collab-clients
+depend	/man/1/mk
+depend	/man/10/2l
+depend	/man/10/conf
+depend	/man/10/mk
+depend	/man/2/security-0intro
+depend	/man/4/factotum
+depend	/man/4/namespace
+dependencies	/man/1/disdep
+dependencies	/man/1/mash
+dependencies	/man/1/mash-make
+dependencies	/man/10/conf
+dependencies	/man/2/0intro
+dependency	/man/1/disdep
+dependency	/man/1/mash
+dependency	/man/1/mash-make
+dependency	/man/1/mk
+dependency	/man/1/tsort
+dependency	/man/10/mk
+dependent	/man/1/mash-make
+dependent	/man/1/math-misc
+dependent	/man/10/2a
+dependent	/man/10/2c
+dependent	/man/10/2l
+dependent	/man/10/a.out
+dependent	/man/10/acid
+dependent	/man/10/ar
+dependent	/man/10/inb
+dependent	/man/10/intrenable
+dependent	/man/10/kbdputc
+dependent	/man/10/panic
+dependent	/man/10/print
+dependent	/man/10/seconds
+dependent	/man/2/disks
+dependent	/man/2/draw-display
+dependent	/man/2/styxpersist
+dependent	/man/3/cmd
+dependent	/man/3/draw
+dependent	/man/3/ether
+dependent	/man/3/flash
+dependent	/man/3/ip
+dependent	/man/3/kprof
+dependent	/man/3/sd
+dependent	/man/4/spree
+dependent	/man/5/0intro
+dependent	/man/6/keyboard
+dependent	/man/6/keytext
+dependent	/man/9/canvas
+dependents	/man/1/mash-make
+depends	/man/1/date
+depends	/man/1/disdep
+depends	/man/1/fc
+depends	/man/1/mash-make
+depends	/man/1/mk
+depends	/man/1/sh-file2chan
+depends	/man/10/mk
+depends	/man/2/alphabet-intro
+depends	/man/2/dbm
+depends	/man/2/palmfile
+depends	/man/2/prefab-element
+depends	/man/2/registries
+depends	/man/2/security-0intro
+depends	/man/2/sys-0intro
+depends	/man/2/sys-open
+depends	/man/3/kprof
+depends	/man/4/factotum
+depends	/man/5/flush
+depends	/man/9/bind
+depends	/man/9/canvas
+depends	/man/9/options
+depends	/man/9/text
+deprecated	/man/1/sh-std
+deprecated	/man/10/ms2
+deprecated	/man/10/plan9.ini
+deprecated	/man/10/print
+deprecated	/man/2/w3c-uris
+deprecated	/man/3/ip
+deprecated	/man/9/scrollbar
+depressingly	/man/2/w3c-uris
+depth	/man/1/alphabet-fs
+depth	/man/1/fs
+depth	/man/1/wm-misc
+depth	/man/2/draw-0intro
+depth	/man/2/draw-display
+depth	/man/2/draw-image
+depth	/man/2/print
+depth	/man/2/venti
+depth	/man/2/wmsrv
+depth	/man/3/vga
+depth	/man/6/image
+depth	/man/6/ubfa
+depths	/man/2/draw-0intro
+depths	/man/2/draw-display
+depths	/man/6/colour
+dereference	/man/2/0intro
+derivative	/man/1/calc
+derivatives	/man/9/1copyright
+derive	/man/1/mk
+derive	/man/10/mk
+derive	/man/2/security-0intro
+derived	/man/1/mux
+derived	/man/1/webgrab
+derived	/man/10/0intro
+derived	/man/10/c2l
+derived	/man/2/styxservers
+derived	/man/2/w3c-css
+derived	/man/2/w3c-uris
+derived	/man/7/cddb
+derived	/man/8/ai2key
+derived	/man/9/0intro
+derived	/man/9/1copyright
+des	/man/2/keyring-0intro
+des	/man/2/keyring-crypt
+des	/man/2/security-0intro
+des	/man/3/ssl
+desbsize	/man/2/keyring-crypt
+desc	/man/2/convcs
+desc	/man/2/dis
+desc	/man/2/draw-font
+desc	/man/2/print
+desc	/man/6/dis
+descbc	/man/2/keyring-crypt
+descend	/man/1/sh-expr
+descend	/man/5/walk
+descendants	/man/2/styxservers-nametree
+descendants	/man/9/destroy
+descendants	/man/9/grab
+descendent	/man/1/alphabet-fs
+descendent	/man/1/fs
+descendents	/man/1/alphabet-fs
+descendents	/man/1/fs
+descendents	/man/3/sign
+descending	/man/1/grid-session
+descending	/man/2/readdir
+descends	/man/2/xml
+descent	/man/1/limbo
+descr	/man/2/format
+describe	/man/1/auplay
+describe	/man/10/a.out
+describe	/man/10/plan9.ini
+describe	/man/10/styxserver
+describe	/man/2/0intro
+describe	/man/2/alphabet-intro
+describe	/man/2/diskblocks
+describe	/man/2/draw-image
+describe	/man/2/spree-gather
+describe	/man/3/ip
+describe	/man/3/pnp
+describe	/man/3/prof
+describe	/man/3/prog
+describe	/man/4/factotum
+describe	/man/6/namespace
+describe	/man/6/sbl
+describe	/man/8/create
+describe	/man/8/ftl
+describe	/man/9/canvas
+describe	/man/9/entry
+describe	/man/9/listbox
+describe	/man/9/scrollbar
+describe	/man/9/text
+describe	/man/9/types
+described	/man/1/acme
+described	/man/1/alphabet-fs
+described	/man/1/alphabet-main
+described	/man/1/bind
+described	/man/1/charon
+described	/man/1/collab
+described	/man/1/emu
+described	/man/1/fs
+described	/man/1/listen
+described	/man/1/logon
+described	/man/1/m4
+described	/man/1/mash
+described	/man/1/mk
+described	/man/1/mux
+described	/man/1/sh
+described	/man/1/sh-alphabet
+described	/man/1/tcs
+described	/man/1/tiny
+described	/man/10/9load
+described	/man/10/a.out
+described	/man/10/allocb
+described	/man/10/ar
+described	/man/10/dev
+described	/man/10/devattach
+described	/man/10/dynld
+described	/man/10/lock
+described	/man/10/mk
+described	/man/10/plan9.ini
+described	/man/10/print
+described	/man/10/qio
+described	/man/10/qlock
+described	/man/10/styx
+described	/man/10/styxserver
+described	/man/2/0intro
+described	/man/2/asn1
+described	/man/2/convcs
+described	/man/2/dbm
+described	/man/2/draw-0intro
+described	/man/2/draw-context
+described	/man/2/draw-display
+described	/man/2/draw-image
+described	/man/2/factotum
+described	/man/2/filter-slip
+described	/man/2/imagefile
+described	/man/2/json
+described	/man/2/keyring-0intro
+described	/man/2/math-0intro
+described	/man/2/plumbmsg
+described	/man/2/prefab-0intro
+described	/man/2/prefab-element
+described	/man/2/readdir
+described	/man/2/regex
+described	/man/2/registries
+described	/man/2/secstore
+described	/man/2/security-0intro
+described	/man/2/security-auth
+described	/man/2/security-login
+described	/man/2/sh
+described	/man/2/spree
+described	/man/2/spree-cardlib
+described	/man/2/styx
+described	/man/2/styxservers
+described	/man/2/sys-0intro
+described	/man/2/sys-bind
+described	/man/2/sys-fd2path
+described	/man/2/sys-open
+described	/man/2/sys-pctl
+described	/man/2/sys-print
+described	/man/2/tkclient
+described	/man/2/w3c-css
+described	/man/2/wait
+described	/man/2/wmclient
+described	/man/2/wmsrv
+described	/man/3/audio
+described	/man/3/cmd
+described	/man/3/draw
+described	/man/3/flash
+described	/man/3/fs
+described	/man/3/ip
+described	/man/3/mnt
+described	/man/3/plap
+described	/man/3/prog
+described	/man/3/srv
+described	/man/3/srv9
+described	/man/3/ssl
+described	/man/3/usb
+described	/man/3/vga
+described	/man/4/acme
+described	/man/4/factotum
+described	/man/4/kfs
+described	/man/5/0intro
+described	/man/5/attach
+described	/man/5/open
+described	/man/5/stat
+described	/man/6/colour
+described	/man/6/dis
+described	/man/6/font
+described	/man/6/image
+described	/man/6/namespace
+described	/man/6/proto
+described	/man/6/sbl
+described	/man/6/utf
+described	/man/8/applylog
+described	/man/8/bootpd
+described	/man/8/collabsrv
+described	/man/8/cs
+described	/man/8/kfscmd
+described	/man/8/plumber
+described	/man/8/prep
+described	/man/8/rip
+described	/man/8/rstyxd
+described	/man/9/0intro
+described	/man/9/1copyright
+described	/man/9/button
+described	/man/9/canvas
+described	/man/9/checkbutton
+described	/man/9/choicebutton
+described	/man/9/entry
+described	/man/9/frame
+described	/man/9/grid
+described	/man/9/label
+described	/man/9/listbox
+described	/man/9/menu
+described	/man/9/menubutton
+described	/man/9/options
+described	/man/9/panel
+described	/man/9/radiobutton
+described	/man/9/scale
+described	/man/9/scrollbar
+described	/man/9/text
+describes	/man/1/0intro
+describes	/man/1/alphabet-fs
+describes	/man/1/alphabet-main
+describes	/man/1/auplay
+describes	/man/1/fs
+describes	/man/10/0intro
+describes	/man/10/devattach
+describes	/man/2/alphabet-intro
+describes	/man/2/draw-font
+describes	/man/2/exception
+describes	/man/2/format
+describes	/man/2/imagefile
+describes	/man/2/security-ssl
+describes	/man/2/spree
+describes	/man/2/styx
+describes	/man/2/styxpersist
+describes	/man/3/prog
+describes	/man/4/0intro
+describes	/man/4/factotum
+describes	/man/5/0intro
+describes	/man/6/0intro
+describes	/man/6/dis
+describes	/man/6/json
+describes	/man/6/sbl
+describes	/man/7/0intro
+describes	/man/8/0intro
+describes	/man/9/options
+describes	/man/9/types
+describing	/man/1/yacc
+describing	/man/10/dev
+describing	/man/10/styxserver
+describing	/man/2/convcs
+describing	/man/2/dbm
+describing	/man/2/dis
+describing	/man/2/filter
+describing	/man/2/rfc822
+describing	/man/2/security-ssl
+describing	/man/2/spree-allow
+describing	/man/2/styxservers-nametree
+describing	/man/2/sys-werrstr
+describing	/man/3/draw
+describing	/man/3/floppy
+describing	/man/3/tls
+describing	/man/4/acme
+describing	/man/5/0intro
+describing	/man/5/error
+describing	/man/6/dis
+describing	/man/6/image
+describing	/man/8/ai2key
+describing	/man/8/applylog
+describing	/man/9/canvas
+describing	/man/9/entry
+describing	/man/9/see
+describing	/man/9/text
+descriptions	/man/1/alphabet-abc
+descriptions	/man/1/alphabet-grid
+descriptions	/man/1/mash-tk
+descriptions	/man/1/sh-std
+descriptions	/man/1/unicode
+descriptions	/man/10/qio
+descriptions	/man/2/draw-font
+descriptions	/man/2/rfc822
+descriptions	/man/2/security-0intro
+descriptions	/man/4/registry
+descriptions	/man/5/0intro
+descriptions	/man/6/image
+descriptions	/man/8/prep
+descriptions	/man/8/svc
+descriptions	/man/9/canvas
+descriptions	/man/9/entry
+descriptions	/man/9/listbox
+descriptions	/man/9/options
+descriptions	/man/9/text
+descriptive	/man/2/convcs
+descriptor	/man/1/alphabet-main
+descriptor	/man/1/ftest
+descriptor	/man/1/listen
+descriptor	/man/1/mash
+descriptor	/man/1/mdb
+descriptor	/man/1/sh
+descriptor	/man/1/sh-std
+descriptor	/man/10/devattach
+descriptor	/man/10/dynld
+descriptor	/man/10/kproc
+descriptor	/man/10/newchan
+descriptor	/man/10/odbc
+descriptor	/man/10/print
+descriptor	/man/2/bufio
+descriptor	/man/2/dhcpclient
+descriptor	/man/2/dial
+descriptor	/man/2/dis
+descriptor	/man/2/diskblocks
+descriptor	/man/2/disks
+descriptor	/man/2/draw-context
+descriptor	/man/2/draw-display
+descriptor	/man/2/drawmux
+descriptor	/man/2/factotum
+descriptor	/man/2/itslib
+descriptor	/man/2/msgio
+descriptor	/man/2/print
+descriptor	/man/2/readdir
+descriptor	/man/2/registries
+descriptor	/man/2/scsiio
+descriptor	/man/2/security-auth
+descriptor	/man/2/security-ssl
+descriptor	/man/2/styx
+descriptor	/man/2/styxconv
+descriptor	/man/2/styxpersist
+descriptor	/man/2/styxservers
+descriptor	/man/2/sys-0intro
+descriptor	/man/2/sys-bind
+descriptor	/man/2/sys-dial
+descriptor	/man/2/sys-dirread
+descriptor	/man/2/sys-dup
+descriptor	/man/2/sys-export
+descriptor	/man/2/sys-fauth
+descriptor	/man/2/sys-fd2path
+descriptor	/man/2/sys-iounit
+descriptor	/man/2/sys-open
+descriptor	/man/2/sys-pctl
+descriptor	/man/2/sys-pipe
+descriptor	/man/2/sys-print
+descriptor	/man/2/sys-stat
+descriptor	/man/2/wait
+descriptor	/man/3/arch
+descriptor	/man/3/draw
+descriptor	/man/3/dup
+descriptor	/man/3/ether
+descriptor	/man/3/ip
+descriptor	/man/3/plap
+descriptor	/man/3/prog
+descriptor	/man/3/sign
+descriptor	/man/3/srv9
+descriptor	/man/3/ssl
+descriptor	/man/3/tls
+descriptor	/man/4/keysrv
+descriptor	/man/4/memfs
+descriptor	/man/4/registry
+descriptor	/man/4/spree
+descriptor	/man/5/clunk
+descriptor	/man/6/dis
+descriptor	/man/7/db
+descriptor	/man/9/types
+descriptors	/man/1/emu
+descriptors	/man/1/limbo
+descriptors	/man/1/sh
+descriptors	/man/1/sh-alphabet
+descriptors	/man/1/sh-std
+descriptors	/man/10/dev
+descriptors	/man/10/newchan
+descriptors	/man/2/dis
+descriptors	/man/2/disks
+descriptors	/man/2/draw-context
+descriptors	/man/2/secstore
+descriptors	/man/2/styxconv
+descriptors	/man/2/styxservers
+descriptors	/man/2/sys-0intro
+descriptors	/man/2/sys-dup
+descriptors	/man/2/sys-iounit
+descriptors	/man/2/sys-pctl
+descriptors	/man/2/sys-pipe
+descriptors	/man/3/cmd
+descriptors	/man/3/pipe
+descriptors	/man/3/prog
+descriptors	/man/3/tls
+descriptors	/man/5/0intro
+descriptors	/man/6/dis
+descriptors	/man/6/image
+descriptors	/man/8/rstyxd
+desecb	/man/2/keyring-crypt
+deselect	/man/9/checkbutton
+deselect	/man/9/menu
+deselect	/man/9/radiobutton
+deselected	/man/9/checkbutton
+deselected	/man/9/listbox
+deselected	/man/9/menu
+deselecting	/man/1/wm-misc
+deselects	/man/9/checkbutton
+deselects	/man/9/listbox
+deselects	/man/9/radiobutton
+design	/man/1/tktester
+design	/man/2/security-0intro
+design	/man/3/touch
+design	/man/6/colour
+design	/man/6/keyboard
+designated	/man/1/cmp
+designated	/man/1/tail
+designed	/man/1/0intro
+designed	/man/1/cook
+designed	/man/1/grid-monitor
+designed	/man/1/tiny
+designed	/man/1/toolbar
+designed	/man/2/format
+designed	/man/2/ir
+designed	/man/2/styxflush
+designed	/man/3/logfs
+designed	/man/6/colour
+designed	/man/6/utf
+designing	/man/1/tktester
+desired	/man/1/9win
+desired	/man/1/avr
+desired	/man/1/collab-clients
+desired	/man/1/cprof
+desired	/man/1/wm
+desired	/man/1/wm-misc
+desired	/man/1/wm-sh
+desired	/man/1/zeros
+desired	/man/10/devattach
+desired	/man/10/plan9.ini
+desired	/man/2/command
+desired	/man/2/ida
+desired	/man/2/plumbmsg
+desired	/man/2/prof
+desired	/man/2/security-login
+desired	/man/2/sh
+desired	/man/2/sys-0intro
+desired	/man/2/tk
+desired	/man/2/translate
+desired	/man/3/dbg
+desired	/man/3/rtc
+desired	/man/3/srv9
+desired	/man/4/kfs
+desired	/man/5/attach
+desired	/man/8/applylog
+desired	/man/9/button
+desired	/man/9/canvas
+desired	/man/9/checkbutton
+desired	/man/9/choicebutton
+desired	/man/9/entry
+desired	/man/9/frame
+desired	/man/9/label
+desired	/man/9/listbox
+desired	/man/9/menubutton
+desired	/man/9/options
+desired	/man/9/panel
+desired	/man/9/radiobutton
+desired	/man/9/scale
+desired	/man/9/scrollbar
+desired	/man/9/text
+desires	/man/2/volume
+desklink	/man/4/palmsrv
+desktop	/man/2/palmfile
+despite	/man/2/asn1
+despite	/man/2/draw-image
+despite	/man/5/walk
+despite	/man/6/sbl
+dessetup	/man/2/keyring-crypt
+desstate	/man/2/keyring-crypt
+dest	/man/1/plumb
+dest	/man/6/dis
+destination	/man/1/ftree
+destination	/man/1/plumb
+destination	/man/1/stream
+destination	/man/1/tiny
+destination	/man/1/tktester
+destination	/man/2/dial
+destination	/man/2/dis
+destination	/man/2/draw-0intro
+destination	/man/2/draw-example
+destination	/man/2/draw-image
+destination	/man/2/plumbmsg
+destination	/man/2/pslib
+destination	/man/2/sys-dial
+destination	/man/3/ether
+destination	/man/3/ip
+destination	/man/3/pbus
+destination	/man/4/spree
+destination	/man/6/dis
+destination	/man/6/plumbing
+destination	/man/8/create
+destination	/man/8/mkfs
+destination	/man/8/ping
+destination	/man/8/styxchat
+destination	/man/9/canvas
+destination	/man/9/image
+destined	/man/2/dhcpclient
+destined	/man/3/ip
+destroy	/man/1/cat
+destroy	/man/1/tktester
+destroy	/man/2/exception
+destroy	/man/3/prog
+destroy	/man/9/0intro
+destroy	/man/9/bind
+destroy	/man/9/destroy
+destroyed	/man/1/sh
+destroyed	/man/1/sh-tk
+destroyed	/man/10/ntsrv
+destroyed	/man/3/cons
+destroyed	/man/3/env
+destroyed	/man/9/bind
+destroyed	/man/9/destroy
+destroyed	/man/9/text
+destroying	/man/6/dis
+destroying	/man/9/destroy
+destruction	/man/9/bind
+detach	/man/3/ftl
+detach	/man/3/ip
+detached	/man/1/deb
+detaches	/man/3/ip
+detaching	/man/1/deb
+detail	/man/1/itest
+detail	/man/1/man
+detail	/man/1/mash
+detail	/man/2/convcs
+detail	/man/2/keyring-0intro
+detail	/man/3/ftl
+detail	/man/8/init
+detailed	/man/1/emu
+detailed	/man/1/fc
+detailed	/man/1/sh-regex
+detailed	/man/1/tcs
+detailed	/man/2/dbm
+detailed	/man/2/debug
+detailed	/man/2/security-0intro
+detailed	/man/2/sys-0intro
+detailed	/man/3/cons
+detailed	/man/8/rip
+details	/man/1/0intro
+details	/man/1/bind
+details	/man/1/collab
+details	/man/1/crypt
+details	/man/1/math-misc
+details	/man/1/nsbuild
+details	/man/1/sh
+details	/man/1/sh-regex
+details	/man/1/sh-std
+details	/man/10/9load
+details	/man/10/conf
+details	/man/10/devattach
+details	/man/10/dynld
+details	/man/2/asn1
+details	/man/2/attrdb
+details	/man/2/convcs
+details	/man/2/dhcpclient
+details	/man/2/dial
+details	/man/2/exception
+details	/man/2/factotum
+details	/man/2/filter
+details	/man/2/filter-deflate
+details	/man/2/filter-slip
+details	/man/2/names
+details	/man/2/plumbmsg
+details	/man/2/sh
+details	/man/2/spki
+details	/man/2/spree
+details	/man/2/spree-cardlib
+details	/man/2/sys-bind
+details	/man/2/sys-file2chan
+details	/man/2/sys-open
+details	/man/2/sys-pipe
+details	/man/2/tk
+details	/man/2/w3c-css
+details	/man/3/draw
+details	/man/3/fs
+details	/man/3/ftl
+details	/man/3/gpio
+details	/man/3/i2c
+details	/man/3/prog
+details	/man/4/archfs
+details	/man/6/colour
+details	/man/6/image
+details	/man/7/cddb
+details	/man/8/dhcp
+details	/man/9/bind
+details	/man/9/canvas
+details	/man/9/entry
+details	/man/9/menu
+details	/man/9/menubutton
+details	/man/9/options
+details	/man/9/pack
+details	/man/9/scrollbar
+details	/man/9/text
+detect	/man/2/spki-verifier
+detect	/man/3/gpio
+detect	/man/4/registry
+detected	/man/10/c2l
+detected	/man/10/plan9.ini
+detected	/man/2/0intro
+detected	/man/2/scsiio
+detected	/man/2/sys-0intro
+detected	/man/3/draw
+detected	/man/3/logfs
+detected	/man/3/sd
+detecting	/man/3/dbg
+detection	/man/1/blur
+detection	/man/3/logfs
+detects	/man/10/9load
+detects	/man/10/newchan
+detects	/man/2/ida
+detects	/man/3/pbus
+detects	/man/8/prep
+determine	/man/1/alphabet-fs
+determine	/man/1/charon
+determine	/man/1/fs
+determine	/man/1/mash-make
+determine	/man/1/mk
+determine	/man/1/prof
+determine	/man/1/wm
+determine	/man/1/wm-misc
+determine	/man/10/9load
+determine	/man/10/mk
+determine	/man/10/odbc
+determine	/man/2/asn1
+determine	/man/2/bloomfilter
+determine	/man/2/dividers
+determine	/man/2/prefab-element
+determine	/man/2/registries
+determine	/man/2/security-0intro
+determine	/man/2/sys-byte2char
+determine	/man/2/sys-file2chan
+determine	/man/3/ip
+determine	/man/3/pnp
+determine	/man/4/namespace
+determine	/man/6/login
+determine	/man/9/button
+determine	/man/9/canvas
+determine	/man/9/checkbutton
+determine	/man/9/choicebutton
+determine	/man/9/entry
+determine	/man/9/frame
+determine	/man/9/label
+determine	/man/9/listbox
+determine	/man/9/menu
+determine	/man/9/menubutton
+determine	/man/9/panel
+determine	/man/9/radiobutton
+determine	/man/9/scale
+determine	/man/9/scrollbar
+determine	/man/9/text
+determined	/man/1/collab
+determined	/man/1/ls
+determined	/man/1/sh-file2chan
+determined	/man/1/tiny
+determined	/man/1/webgrab
+determined	/man/10/9load
+determined	/man/10/acid
+determined	/man/10/conf
+determined	/man/10/dev
+determined	/man/10/kproc
+determined	/man/10/odbc
+determined	/man/10/plan9.ini
+determined	/man/2/dbm
+determined	/man/2/draw-display
+determined	/man/2/draw-image
+determined	/man/2/filter-deflate
+determined	/man/2/plumbmsg
+determined	/man/2/prefab-compound
+determined	/man/2/prefab-element
+determined	/man/2/registries
+determined	/man/2/stringinttab
+determined	/man/2/wmclient
+determined	/man/3/arch
+determined	/man/3/fpga
+determined	/man/3/fs
+determined	/man/3/i2c
+determined	/man/3/ip
+determined	/man/4/factotum
+determined	/man/6/colour
+determined	/man/6/image
+determined	/man/6/keytext
+determined	/man/8/collabsrv
+determined	/man/8/httpd
+determined	/man/9/canvas
+determined	/man/9/entry
+determined	/man/9/focus
+determined	/man/9/grid
+determined	/man/9/menu
+determined	/man/9/options
+determined	/man/9/scale
+determined	/man/9/text
+determines	/man/1/brutus
+determines	/man/1/mk
+determines	/man/1/sh-std
+determines	/man/1/wm-misc
+determines	/man/10/mk
+determines	/man/10/newchan
+determines	/man/2/dis
+determines	/man/2/draw-0intro
+determines	/man/2/registries
+determines	/man/2/sys-fversion
+determines	/man/2/tk
+determines	/man/2/tkclient
+determines	/man/2/wmclient
+determines	/man/4/factotum
+determines	/man/5/open
+determines	/man/6/colour
+determines	/man/6/dis
+determines	/man/9/button
+determines	/man/9/menu
+determines	/man/9/options
+determines	/man/9/pack
+determines	/man/9/scale
+determines	/man/9/text
+determining	/man/1/ls
+determining	/man/10/9load
+determining	/man/2/prefab-element
+dev	/man/1/9win
+dev	/man/1/auplay
+dev	/man/1/avr
+dev	/man/1/cd
+dev	/man/1/charon
+dev	/man/1/crypt
+dev	/man/1/date
+dev	/man/1/emu
+dev	/man/1/ftest
+dev	/man/1/itest
+dev	/man/1/logon
+dev	/man/1/mash
+dev	/man/1/miniterm
+dev	/man/1/netkey
+dev	/man/1/os
+dev	/man/1/passwd
+dev	/man/1/runas
+dev	/man/1/secstore
+dev	/man/1/sh
+dev	/man/1/timestamp
+dev	/man/1/tiny
+dev	/man/1/tkcmd
+dev	/man/1/wish
+dev	/man/1/wm-misc
+dev	/man/1/wm-sh
+dev	/man/10/c2l
+dev	/man/10/conf
+dev	/man/10/dev
+dev	/man/10/devattach
+dev	/man/10/dynld
+dev	/man/10/eve
+dev	/man/10/intrenable
+dev	/man/10/kbdputc
+dev	/man/10/kproc
+dev	/man/10/kprof
+dev	/man/10/master
+dev	/man/10/newchan
+dev	/man/10/plan9.ini
+dev	/man/2/daytime
+dev	/man/2/devpointer
+dev	/man/2/draw-display
+dev	/man/2/drawmux
+dev	/man/2/ether
+dev	/man/2/ip
+dev	/man/2/keyring-auth
+dev	/man/2/keyset
+dev	/man/2/mpeg
+dev	/man/2/print
+dev	/man/2/registries
+dev	/man/2/security-random
+dev	/man/2/styxservers
+dev	/man/2/sys-stat
+dev	/man/2/volume
+dev	/man/3/0intro
+dev	/man/3/arch
+dev	/man/3/audio
+dev	/man/3/boot
+dev	/man/3/cons
+dev	/man/3/dbg
+dev	/man/3/draw
+dev	/man/3/ds
+dev	/man/3/dynld
+dev	/man/3/eia
+dev	/man/3/flash
+dev	/man/3/floppy
+dev	/man/3/fpga
+dev	/man/3/ftl
+dev	/man/3/gpio
+dev	/man/3/i2c
+dev	/man/3/i82365
+dev	/man/3/indir
+dev	/man/3/kprof
+dev	/man/3/logfs
+dev	/man/3/lpt
+dev	/man/3/mnt
+dev	/man/3/mpeg
+dev	/man/3/pbus
+dev	/man/3/pnp
+dev	/man/3/pointer
+dev	/man/3/root
+dev	/man/3/rtc
+dev	/man/3/sd
+dev	/man/3/sign
+dev	/man/3/snarf
+dev	/man/3/switch
+dev	/man/3/tinyfs
+dev	/man/3/touch
+dev	/man/3/tv
+dev	/man/3/usb
+dev	/man/3/vga
+dev	/man/3/vid
+dev	/man/4/acme
+dev	/man/4/dossrv
+dev	/man/4/export
+dev	/man/4/grid-cpu
+dev	/man/4/namespace
+dev	/man/4/palmsrv
+dev	/man/4/spree
+dev	/man/5/stat
+dev	/man/8/getauthinfo
+dev	/man/8/init
+dev	/man/8/prep
+dev	/man/8/rdbgsrv
+dev	/man/8/rstyxd
+dev	/man/8/shutdown
+dev	/man/8/sntp
+dev	/man/8/styxchat
+dev	/man/8/styxmon
+dev	/man/8/touchcal
+dev.c	/man/10/devattach
+dev.c	/man/10/parsecmd
+dev.close	/man/10/newchan
+devarch.c	/man/3/arch
+devattach	/man/10/dev
+devattach	/man/10/devattach
+devattach	/man/10/newchan
+devaudio	/man/3/audio
+devaudio.c	/man/3/audio
+devboot.c	/man/3/boot
+devbread	/man/10/dev
+devbread	/man/10/devattach
+devbwrite	/man/10/dev
+devbwrite	/man/10/devattach
+devcap.c	/man/3/cap
+devclone	/man/10/devattach
+devclone	/man/10/newchan
+devcmd.c	/man/3/cmd
+devcon.c	/man/10/readnum
+devconf	/man/10/dev
+devcons.c	/man/10/dev
+devcons.c	/man/10/readnum
+devcons.c	/man/3/cons
+devcreate	/man/10/dev
+devcreate	/man/10/devattach
+devcs4231.c	/man/3/audio
+devdbg.c	/man/3/dbg
+devdir	/man/10/devattach
+devdir	/man/2/scsiio
+devdirread	/man/10/dev
+devdirread	/man/10/devattach
+devdotdot	/man/10/devattach
+devdraw.c	/man/3/draw
+devds.c	/man/3/ds
+devdup.c	/man/3/dup
+devdynld.c	/man/3/dynld
+developed	/man/2/security-0intro
+developer	/man/1/0intro
+devenv.c	/man/3/env
+devether.c	/man/10/conf
+devether.c	/man/3/ether
+devflash.c	/man/3/flash
+devfloppy.c	/man/3/floppy
+devfpga.c	/man/3/fpga
+devfs	/man/3/fs
+devfs.c	/man/3/fs
+devftl.c	/man/3/ftl
+devgen	/man/10/devattach
+devgpio.c	/man/3/gpio
+devi2c.c	/man/3/i2c
+devi82365.c	/man/3/i82365
+deviate	/man/9/grid
+device	/man/1/0intro
+device	/man/1/auplay
+device	/man/1/avr
+device	/man/1/bind
+device	/man/1/charon
+device	/man/1/chgrp
+device	/man/1/collab
+device	/man/1/cpu
+device	/man/1/dd
+device	/man/1/deb
+device	/man/1/ebook
+device	/man/1/listen
+device	/man/1/ls
+device	/man/1/mprof
+device	/man/1/mux
+device	/man/1/netkey
+device	/man/1/netstat
+device	/man/1/os
+device	/man/1/ps
+device	/man/1/tiny
+device	/man/10/0intro
+device	/man/10/9load
+device	/man/10/conf
+device	/man/10/dev
+device	/man/10/devattach
+device	/man/10/dmainit
+device	/man/10/inb
+device	/man/10/intrenable
+device	/man/10/kprof
+device	/man/10/master
+device	/man/10/newchan
+device	/man/10/parsecmd
+device	/man/10/plan9.ini
+device	/man/10/qio
+device	/man/10/readnum
+device	/man/10/sleep
+device	/man/10/splhi
+device	/man/2/devpointer
+device	/man/2/dhcpclient
+device	/man/2/disks
+device	/man/2/draw-0intro
+device	/man/2/draw-context
+device	/man/2/draw-display
+device	/man/2/draw-pointer
+device	/man/2/drawmux
+device	/man/2/ether
+device	/man/2/ip
+device	/man/2/ir
+device	/man/2/keyring-0intro
+device	/man/2/mpeg
+device	/man/2/palmfile
+device	/man/2/print
+device	/man/2/prof
+device	/man/2/scsiio
+device	/man/2/security-0intro
+device	/man/2/security-ssl
+device	/man/2/sys-bind
+device	/man/2/sys-file2chan
+device	/man/2/sys-pctl
+device	/man/2/sys-pipe
+device	/man/2/sys-stat
+device	/man/2/tabs
+device	/man/2/volume
+device	/man/3/0intro
+device	/man/3/arch
+device	/man/3/audio
+device	/man/3/cmd
+device	/man/3/cons
+device	/man/3/dbg
+device	/man/3/draw
+device	/man/3/ds
+device	/man/3/dup
+device	/man/3/dynld
+device	/man/3/eia
+device	/man/3/env
+device	/man/3/ether
+device	/man/3/flash
+device	/man/3/fpga
+device	/man/3/fs
+device	/man/3/ftl
+device	/man/3/i2c
+device	/man/3/i82365
+device	/man/3/indir
+device	/man/3/ip
+device	/man/3/kprof
+device	/man/3/logfs
+device	/man/3/mnt
+device	/man/3/mpeg
+device	/man/3/plap
+device	/man/3/pnp
+device	/man/3/pointer
+device	/man/3/prof
+device	/man/3/prog
+device	/man/3/root
+device	/man/3/rtc
+device	/man/3/sd
+device	/man/3/sign
+device	/man/3/snarf
+device	/man/3/srv9
+device	/man/3/ssl
+device	/man/3/switch
+device	/man/3/tinyfs
+device	/man/3/tls
+device	/man/3/tv
+device	/man/3/usb
+device	/man/3/vga
+device	/man/3/vid
+device	/man/4/kfs
+device	/man/4/namespace
+device	/man/4/palmsrv
+device	/man/5/0intro
+device	/man/5/attach
+device	/man/6/audio
+device	/man/6/colour
+device	/man/6/namespace
+device	/man/6/ndb
+device	/man/7/dbsrv
+device	/man/8/dhcp
+device	/man/8/ftl
+device	/man/8/init
+device	/man/8/logind
+device	/man/8/mangaload
+device	/man/8/prep
+device	/man/8/rdbgsrv
+device	/man/8/register
+device	/man/8/touchcal
+device's	/man/1/auplay
+device's	/man/1/chgrp
+device's	/man/10/dev
+device's	/man/10/devattach
+device's	/man/10/newchan
+device's	/man/10/parsecmd
+device's	/man/10/plan9.ini
+device's	/man/2/disks
+device's	/man/2/ir
+device's	/man/2/scsiio
+device's	/man/2/sys-0intro
+device's	/man/3/0intro
+device's	/man/3/flash
+device's	/man/3/ftl
+device's	/man/3/mpeg
+device's	/man/3/sd
+device's	/man/8/mangaload
+device's	/man/8/prep
+device's	/man/8/rdbgsrv
+devicefile	/man/4/dossrv
+devices	/man/1/0intro
+devices	/man/1/charon
+devices	/man/1/chgrp
+devices	/man/1/cp
+devices	/man/1/ftree
+devices	/man/1/keyboard
+devices	/man/1/ls
+devices	/man/1/mux
+devices	/man/1/tiny
+devices	/man/10/9load
+devices	/man/10/allocb
+devices	/man/10/dev
+devices	/man/10/devattach
+devices	/man/10/inb
+devices	/man/10/master
+devices	/man/10/plan9.ini
+devices	/man/10/qio
+devices	/man/10/sleep
+devices	/man/10/styxserver
+devices	/man/2/devpointer
+devices	/man/2/draw-0intro
+devices	/man/2/sys-0intro
+devices	/man/2/sys-iounit
+devices	/man/2/sys-open
+devices	/man/2/sys-pctl
+devices	/man/2/sys-stat
+devices	/man/2/wmclient
+devices	/man/3/0intro
+devices	/man/3/cons
+devices	/man/3/ds
+devices	/man/3/flash
+devices	/man/3/fpga
+devices	/man/3/i2c
+devices	/man/3/i82365
+devices	/man/3/indir
+devices	/man/3/ip
+devices	/man/3/logfs
+devices	/man/3/lpt
+devices	/man/3/pnp
+devices	/man/3/rtc
+devices	/man/3/sd
+devices	/man/3/tinyfs
+devices	/man/3/vga
+devices	/man/4/namespace
+devices	/man/5/attach
+devices	/man/5/stat
+devices	/man/8/0intro
+devices	/man/8/plumber
+devies	/man/10/dev
+devindir.c	/man/3/indir
+devinit	/man/10/devattach
+devip.c	/man/3/ip
+devised	/man/10/dmainit
+devising	/man/4/spree
+devkprof.c	/man/3/kprof
+devlance.c	/man/3/ether
+devlogfs.c	/man/3/logfs
+devlpt.c	/man/3/lpt
+devmnt.c	/man/3/mnt
+devmpeg.c	/man/3/mpeg
+devns16552.c	/man/3/eia
+devopen	/man/10/dev
+devopen	/man/10/devattach
+devpbus.c	/man/3/pbus
+devpci.c	/man/3/pnp
+devpipe.c	/man/3/pipe
+devplap.c	/man/3/plap
+devpnp.c	/man/3/pnp
+devpointer	/man/2/devpointer
+devpointer	/man/2/draw-context
+devpointer	/man/2/draw-pointer
+devpointer.c	/man/3/pointer
+devpointer.m	/man/2/devpointer
+devprof.c	/man/3/prof
+devprog.c	/man/3/prog
+devremove	/man/10/dev
+devremove	/man/10/devattach
+devreset	/man/10/dev
+devreset	/man/10/devattach
+devroot.c	/man/3/root
+devrtc.c	/man/3/rtc
+devrtc.c	/man/3/switch
+devs	/man/2/tkclient
+devs	/man/2/wmclient
+devs	/man/2/wmlib
+devscc.c	/man/3/eia
+devsd.c	/man/3/sd
+devshutdown	/man/10/dev
+devshutdown	/man/10/devattach
+devsign.c	/man/3/sign
+devsrv.c	/man/2/sys-file2chan
+devsrv.c	/man/3/srv
+devsrv9.c	/man/3/srv9
+devstat	/man/10/dev
+devstat	/man/10/devattach
+devtab	/man/10/dev
+devtab	/man/10/devattach
+devtinyfs.c	/man/3/tinyfs
+devtls	/man/3/tls
+devtls.c	/man/3/tls
+devtouch.c	/man/3/touch
+devtv.c	/man/3/tv
+devuart.c	/man/3/eia
+devusb.c	/man/3/usb
+devvga.c	/man/3/vga
+devvid.c	/man/3/vid
+devwalk	/man/10/dev
+devwalk	/man/10/devattach
+devwstat	/man/10/dev
+devwstat	/man/10/devattach
+df	/man/1/look
+df	/man/4/iostats
+df	/man/8/prep
+dfars	/man/9/1copyright
+dfd	/man/2/dial
+dfd	/man/2/security-ssl
+dfd	/man/2/sys-dial
+dflt	/man/2/dialog
+dflt	/man/2/tabs
+dfnix	/man/1/look
+dfvx	/man/8/prep
+dh	/man/8/ai2key
+dhcp	/man/10/plan9.ini
+dhcp	/man/2/dhcpclient
+dhcp	/man/8/dhcp
+dhcp	/man/8/rip
+dhcp.b	/man/8/dhcp
+dhcp.m	/man/2/dhcpclient
+dhcpclient	/man/2/dhcpclient
+dhcpclient	/man/3/ip
+dhcpclient.b	/man/2/dhcpclient
+dhcpip	/man/2/dhcpclient
+dhmagic	/man/10/seconds
+dhparams	/man/2/keyring-gensk
+dhz	/man/10/mk
+di	/man/6/man
+di	/man/8/sntp
+diag	/man/2/dhcpclient
+diag	/man/2/fsproto
+diag	/man/2/regex
+diag	/man/2/secstore
+diag	/man/2/w3c-css
+diagnose	/man/2/styxservers
+diagnosed	/man/3/boot
+diagnoses	/man/2/palmfile
+diagnoses	/man/8/applylog
+diagnostic	/man/1/alphabet-main
+diagnostic	/man/1/bind
+diagnostic	/man/1/cmp
+diagnostic	/man/1/logon
+diagnostic	/man/1/m4
+diagnostic	/man/1/read
+diagnostic	/man/1/sh
+diagnostic	/man/1/sh-alphabet
+diagnostic	/man/1/tsort
+diagnostic	/man/1/yacc
+diagnostic	/man/2/alphabet-intro
+diagnostic	/man/2/attrdb
+diagnostic	/man/2/complete
+diagnostic	/man/2/dbm
+diagnostic	/man/2/debug
+diagnostic	/man/2/dhcpclient
+diagnostic	/man/2/dis
+diagnostic	/man/2/draw-context
+diagnostic	/man/2/factotum
+diagnostic	/man/2/fsproto
+diagnostic	/man/2/ida
+diagnostic	/man/2/ip
+diagnostic	/man/2/json
+diagnostic	/man/2/keyring-getmsg
+diagnostic	/man/2/keyset
+diagnostic	/man/2/msgio
+diagnostic	/man/2/palmfile
+diagnostic	/man/2/regex
+diagnostic	/man/2/registries
+diagnostic	/man/2/secstore
+diagnostic	/man/2/security-auth
+diagnostic	/man/2/security-login
+diagnostic	/man/2/security-ssl
+diagnostic	/man/2/sexprs
+diagnostic	/man/2/spki
+diagnostic	/man/2/styx
+diagnostic	/man/2/styxservers
+diagnostic	/man/2/sys-file2chan
+diagnostic	/man/2/sys-utfbytes
+diagnostic	/man/2/tftp
+diagnostic	/man/2/translate
+diagnostic	/man/2/ubfa
+diagnostic	/man/2/w3c-css
+diagnostic	/man/2/w3c-xpointers
+diagnostic	/man/3/cmd
+diagnostic	/man/3/indir
+diagnostic	/man/3/sd
+diagnostic	/man/4/acme
+diagnostic	/man/4/keysrv
+diagnostic	/man/4/palmsrv
+diagnostic	/man/7/db
+diagnostic	/man/8/getauthinfo
+diagnostic	/man/9/variable
+diagnostics	/man/1/0intro
+diagnostics	/man/1/acme
+diagnostics	/man/1/cook
+diagnostics	/man/1/cp
+diagnostics	/man/1/dd
+diagnostics	/man/1/diff
+diagnostics	/man/1/fc
+diagnostics	/man/1/grep
+diagnostics	/man/1/look
+diagnostics	/man/1/mkdir
+diagnostics	/man/1/os
+diagnostics	/man/1/read
+diagnostics	/man/1/rm
+diagnostics	/man/1/tsort
+diagnostics	/man/10/acid
+diagnostics	/man/10/allocb
+diagnostics	/man/10/atoi
+diagnostics	/man/10/dynld
+diagnostics	/man/10/lock
+diagnostics	/man/10/malloc
+diagnostics	/man/10/memory
+diagnostics	/man/10/newchan
+diagnostics	/man/10/print
+diagnostics	/man/10/ref
+diagnostics	/man/10/sleep
+diagnostics	/man/2/alphabet-intro
+diagnostics	/man/2/bufio
+diagnostics	/man/2/complete
+diagnostics	/man/2/dbm
+diagnostics	/man/2/dial
+diagnostics	/man/2/diskblocks
+diagnostics	/man/2/draw-image
+diagnostics	/man/2/exception
+diagnostics	/man/2/factotum
+diagnostics	/man/2/keyring-getmsg
+diagnostics	/man/2/keyring-getstring
+diagnostics	/man/2/msgio
+diagnostics	/man/2/pop3
+diagnostics	/man/2/secstore
+diagnostics	/man/2/security-login
+diagnostics	/man/2/security-ssl
+diagnostics	/man/2/sys-0intro
+diagnostics	/man/2/sys-bind
+diagnostics	/man/2/sys-byte2char
+diagnostics	/man/2/sys-chdir
+diagnostics	/man/2/sys-dial
+diagnostics	/man/2/sys-dirread
+diagnostics	/man/2/sys-export
+diagnostics	/man/2/sys-fauth
+diagnostics	/man/2/sys-fd2path
+diagnostics	/man/2/sys-fversion
+diagnostics	/man/2/sys-iounit
+diagnostics	/man/2/sys-pipe
+diagnostics	/man/2/virgil
+diagnostics	/man/2/w3c-css
+diagnostics	/man/2/xml
+diagnostics	/man/3/boot
+diagnostics	/man/3/cap
+diagnostics	/man/3/cmd
+diagnostics	/man/3/draw
+diagnostics	/man/3/flash
+diagnostics	/man/3/indir
+diagnostics	/man/3/logfs
+diagnostics	/man/3/mpeg
+diagnostics	/man/3/pipe
+diagnostics	/man/3/sign
+diagnostics	/man/6/man
+diagnostics	/man/8/dhcp
+diagnostics	/man/8/ping
+diagonally	/man/9/canvas
+dial	/man/1/alphabet-main
+dial	/man/1/bind
+dial	/man/1/cpu
+dial	/man/1/grid-ns
+dial	/man/1/listen
+dial	/man/1/miniterm
+dial	/man/1/ns
+dial	/man/1/secstore
+dial	/man/1/telnet
+dial	/man/1/vacget
+dial	/man/10/devattach
+dial	/man/2/dial
+dial	/man/2/drawmux
+dial	/man/2/factotum
+dial	/man/2/registries
+dial	/man/2/secstore
+dial	/man/2/security-login
+dial	/man/2/srv
+dial	/man/2/styxpersist
+dial	/man/2/sys-0intro
+dial	/man/2/sys-dial
+dial	/man/2/sys-export
+dial	/man/2/venti
+dial	/man/2/virgil
+dial	/man/3/ip
+dial	/man/3/plap
+dial	/man/3/tls
+dial	/man/4/export
+dial	/man/4/grid-cpu
+dial	/man/4/vacfs
+dial	/man/8/collabsrv
+dial	/man/8/cs
+dial	/man/8/httpd
+dial	/man/8/ping
+dial	/man/8/styxchat
+dial.b	/man/1/listen
+dial.b	/man/2/dial
+dial.c	/man/2/sys-dial
+dial.m	/man/2/dial
+dialing	/man/1/miniterm
+dialog	/man/1/mash-tk
+dialog	/man/2/dialog
+dialog	/man/2/selectfile
+dialog.b	/man/2/dialog
+dialog.m	/man/2/dialog
+dials	/man/1/collab
+dials	/man/1/cpu
+dials	/man/1/miniterm
+dials	/man/2/keyring-0intro
+dials	/man/2/secstore
+dials	/man/2/security-0intro
+dials	/man/4/import
+dials	/man/4/keysrv
+dialsigner	/man/2/dial
+dialsigner	/man/2/sys-dial
+diameter	/man/1/units
+diameter	/man/2/draw-image
+diamond	/man/9/menu
+diamond	/man/9/radiobutton
+diamonds	/man/2/spree-cardlib
+diary	/man/1/calendar
+dice	/man/6/colour
+dicing	/man/6/colour
+dict	/man/2/dict
+dict	/man/2/translate
+dict	/man/6/translate
+dict.add	/man/2/translate
+dict.b	/man/2/dict
+dict.m	/man/2/dict
+dict.new	/man/2/translate
+dict.xlate	/man/2/translate
+dict.xlaten	/man/2/translate
+dictated	/man/3/draw
+dictionaries	/man/2/translate
+dictionary	/man/1/look
+dictionary	/man/2/dict
+dictionary	/man/2/translate
+dictionary	/man/2/ubfa
+dictionary	/man/6/translate
+died	/man/3/prog
+dies	/man/1/acme
+dies	/man/2/styxpersist
+diff	/man/1/acme
+diff	/man/1/cmp
+diff	/man/1/comm
+diff	/man/1/diff
+diff.b	/man/1/diff
+differ	/man/1/cmp
+differ	/man/1/units
+differ	/man/2/diskblocks
+differ	/man/2/draw-0intro
+differ	/man/2/draw-image
+differ	/man/4/dbfs
+differ	/man/6/sexprs
+difference	/man/1/acme
+difference	/man/1/cmp
+difference	/man/1/diff
+difference	/man/1/sh
+difference	/man/10/print
+difference	/man/2/asn1
+difference	/man/2/daytime
+difference	/man/2/ubfa
+difference	/man/5/walk
+difference	/man/6/keyboard
+difference	/man/6/sexprs
+difference	/man/9/text
+differences	/man/1/charon
+differences	/man/1/diff
+differences	/man/1/yacc
+differences	/man/2/palmfile
+differences	/man/3/ip
+differences	/man/6/colour
+differences	/man/6/sexprs
+differences	/man/9/0intro
+differences	/man/9/text
+differential	/man/1/calc
+differential	/man/1/diff
+differently	/man/1/charon
+differently	/man/1/mk
+differently	/man/10/mk
+differently	/man/2/draw-image
+differently	/man/9/menu
+differing	/man/1/cmp
+differing	/man/10/plan9.ini
+differing	/man/2/palmfile
+differing	/man/6/keyboard
+differs	/man/2/readdir
+differs	/man/3/ftl
+differs	/man/8/applylog
+difficult	/man/2/keyring-0intro
+difficult	/man/2/security-0intro
+diffie	/man/2/keyring-0intro
+diffie	/man/2/keyring-auth
+diffie	/man/2/keyring-gensk
+diffie	/man/2/security-0intro
+diffie	/man/6/auth
+diffie	/man/6/login
+diffie	/man/8/ai2key
+diffusion	/man/2/imagefile
+digest	/man/1/crypt
+digest	/man/1/listen
+digest	/man/1/sum
+digest	/man/2/keyring-0intro
+digest	/man/2/keyring-auth
+digest	/man/2/keyring-sha1
+digest	/man/2/security-0intro
+digest	/man/2/security-auth
+digest	/man/3/cap
+digest	/man/3/ssl
+digest	/man/3/tls
+digest	/man/4/import
+digest	/man/7/dbsrv
+digest	/man/8/changelogin
+digest	/man/8/rstyxd
+digested	/man/3/ssl
+digesting	/man/1/bind
+digesting	/man/1/collab
+digesting	/man/1/cpu
+digesting	/man/1/rcmd
+digesting	/man/2/security-0intro
+digesting	/man/2/sys-export
+digesting	/man/3/ssl
+digesting	/man/3/tls
+digesting	/man/7/db
+digests	/man/1/crypt
+digests	/man/2/keyring-0intro
+digests	/man/2/keyring-sha1
+digests	/man/2/security-0intro
+digests	/man/3/ssl
+digests	/man/4/import
+digeststate	/man/2/keyring-0intro
+digeststate	/man/2/keyring-sha1
+digit	/man/1/acme
+digit	/man/1/comm
+digit	/man/1/gzip
+digit	/man/1/m4
+digit	/man/1/sh-regex
+digit	/man/10/atoi
+digit	/man/10/print
+digit	/man/2/geodesy
+digit	/man/2/ir
+digit	/man/2/spree-cardlib
+digit	/man/2/sys-print
+digit	/man/3/cons
+digit	/man/3/ether
+digit	/man/3/gpio
+digit	/man/3/ip
+digit	/man/3/pnp
+digit	/man/6/sexprs
+digit	/man/9/text
+digital	/man/10/2c
+digital	/man/10/plan9.ini
+digital	/man/2/draw-image
+digital	/man/2/keyring-0intro
+digital	/man/2/keyring-sha1
+digital	/man/2/security-0intro
+digital	/man/3/audio
+digital	/man/3/tv
+digital	/man/6/dis
+digitally	/man/2/keyring-0intro
+digitally	/man/2/sexprs
+digitally	/man/6/keytext
+digitally	/man/6/sexprs
+digits	/man/1/keyboard
+digits	/man/1/look
+digits	/man/1/m4
+digits	/man/1/sh-tk
+digits	/man/1/sum
+digits	/man/1/tr
+digits	/man/10/atoi
+digits	/man/10/print
+digits	/man/2/drawmux
+digits	/man/2/encoding
+digits	/man/2/ether
+digits	/man/2/sexprs
+digits	/man/2/string
+digits	/man/2/sys-print
+digits	/man/2/w3c-css
+digits	/man/3/pnp
+digits	/man/3/prog
+digits	/man/4/keysrv
+digits	/man/5/version
+digits	/man/6/keyboard
+digits	/man/6/sbl
+digits	/man/6/sexprs
+digits	/man/9/bind
+digits	/man/9/entry
+digits	/man/9/text
+digits	/man/9/types
+digraph	/man/6/keyboard
+digraphs	/man/6/keyboard
+dimension	/man/2/draw-rect
+dimension	/man/2/math-linalg
+dimension	/man/2/prefab-element
+dimension	/man/3/mpeg
+dimension	/man/9/scale
+dimensional	/man/2/prefab-element
+dimensional	/man/2/w3c-css
+dimensions	/man/2/print
+dimensions	/man/3/draw
+dimensions	/man/9/grid
+dimensions	/man/9/pack
+dimmer	/man/9/menu
+dind	/man/2/dis
+ding	/man/9/grid
+dins	/man/2/draw-image
+dir	/man/1/alphabet-fs
+dir	/man/1/alphabet-main
+dir	/man/1/asm
+dir	/man/1/cleanname
+dir	/man/1/cp
+dir	/man/1/fs
+dir	/man/1/limbo
+dir	/man/1/mash-tk
+dir	/man/1/os
+dir	/man/1/wm-sh
+dir	/man/10/2c
+dir	/man/10/c2l
+dir	/man/10/dev
+dir	/man/10/devattach
+dir	/man/10/styx
+dir	/man/10/styxserver
+dir	/man/2/complete
+dir	/man/2/dbm
+dir	/man/2/debug
+dir	/man/2/dial
+dir	/man/2/dividers
+dir	/man/2/fsproto
+dir	/man/2/keyset
+dir	/man/2/plumbmsg
+dir	/man/2/prefab-element
+dir	/man/2/readdir
+dir	/man/2/registries
+dir	/man/2/security-auth
+dir	/man/2/security-ssl
+dir	/man/2/selectfile
+dir	/man/2/styx
+dir	/man/2/styxservers
+dir	/man/2/styxservers-nametree
+dir	/man/2/sys-0intro
+dir	/man/2/sys-dial
+dir	/man/2/sys-dirread
+dir	/man/2/sys-export
+dir	/man/2/sys-file2chan
+dir	/man/2/sys-stat
+dir	/man/3/cap
+dir	/man/3/cmd
+dir	/man/3/indir
+dir	/man/3/logfs
+dir	/man/3/pipe
+dir	/man/4/acme
+dir	/man/4/dossrv
+dir	/man/4/export
+dir	/man/4/kfs
+dir	/man/4/lockfs
+dir	/man/4/mntgen
+dir	/man/5/open
+dir	/man/5/stat
+dir	/man/6/namespace
+dir	/man/6/plumbing
+dir	/man/8/collabsrv
+dir.dtype	/man/10/dev
+dir.mode	/man/10/devattach
+dir.mode	/man/2/sys-stat
+dir.name	/man/10/devattach
+dir.perm	/man/10/devattach
+dir.perm	/man/5/open
+dir.qid.type	/man/10/devattach
+dir2text	/man/2/styx
+dirctory	/man/4/archfs
+direct	/man/1/alphabet-abc
+direct	/man/1/alphabet-grid
+direct	/man/1/miniterm
+direct	/man/1/tiny
+direct	/man/1/yacc
+direct	/man/10/plan9.ini
+direct	/man/2/alphabet-intro
+direct	/man/2/cfg
+direct	/man/2/wmsrv
+direct	/man/3/ftl
+direct	/man/3/sd
+direct	/man/3/srv9
+direct	/man/6/man
+direct	/man/8/plumber
+direct	/man/9/1copyright
+directed	/man/1/tkcmd
+directed	/man/1/tsort
+directed	/man/10/9load
+directed	/man/10/plan9.ini
+directed	/man/2/math-fp
+directed	/man/3/fpga
+directed	/man/9/canvas
+directed	/man/9/text
+direction	/man/1/acme
+direction	/man/1/tiny
+direction	/man/1/wm-misc
+direction	/man/2/draw-image
+direction	/man/2/factotum
+direction	/man/2/keyring-crypt
+direction	/man/2/print
+direction	/man/2/security-ssl
+direction	/man/2/spree-cardlib
+direction	/man/2/wmclient
+direction	/man/3/gpio
+direction	/man/3/sd
+direction	/man/8/collabsrv
+direction	/man/9/options
+direction	/man/9/scale
+direction	/man/9/text
+directions	/man/1/tiny
+directions	/man/9/listbox
+directive	/man/1/yacc
+directive	/man/10/error
+directive	/man/2/xml
+directives	/man/1/yacc
+directives	/man/10/2a
+directives	/man/10/2c
+directives	/man/10/a.out
+directives	/man/2/xml
+directives	/man/6/dis
+directly	/man/1/acme
+directly	/man/1/auplay
+directly	/man/1/charon
+directly	/man/1/diff
+directly	/man/1/emu
+directly	/man/1/miniterm
+directly	/man/1/mux
+directly	/man/1/sh
+directly	/man/1/tkcmd
+directly	/man/10/9load
+directly	/man/10/allocb
+directly	/man/10/c2l
+directly	/man/10/devattach
+directly	/man/10/intrenable
+directly	/man/10/newchan
+directly	/man/10/print
+directly	/man/10/readnum
+directly	/man/10/xalloc
+directly	/man/2/alphabet-intro
+directly	/man/2/asn1
+directly	/man/2/command
+directly	/man/2/convcs
+directly	/man/2/crc
+directly	/man/2/dhcpclient
+directly	/man/2/draw-example
+directly	/man/2/ir
+directly	/man/2/palmfile
+directly	/man/2/plumbmsg
+directly	/man/2/prefab-0intro
+directly	/man/2/secstore
+directly	/man/2/security-auth
+directly	/man/2/sexprs
+directly	/man/2/spki-verifier
+directly	/man/2/spree
+directly	/man/2/spree-cardlib
+directly	/man/2/spree-gather
+directly	/man/2/styxservers
+directly	/man/2/sys-0intro
+directly	/man/2/sys-fauth
+directly	/man/2/sys-fversion
+directly	/man/2/sys-read
+directly	/man/2/ubfa
+directly	/man/2/w3c-xpointers
+directly	/man/2/wmsrv
+directly	/man/2/xml
+directly	/man/3/dbg
+directly	/man/3/flash
+directly	/man/3/indir
+directly	/man/3/mnt
+directly	/man/3/srv
+directly	/man/3/ssl
+directly	/man/4/0intro
+directly	/man/4/kfs
+directly	/man/4/spree
+directly	/man/5/0intro
+directly	/man/6/audio
+directly	/man/6/font
+directly	/man/6/image
+directly	/man/6/sexprs
+directly	/man/8/dns
+directly	/man/8/fpgaload
+directly	/man/8/getauthinfo
+directly	/man/8/plumber
+directly	/man/8/rdbgsrv
+directly	/man/8/rip
+directly	/man/9/bind
+directly	/man/9/send
+directories	/man/1/0intro
+directories	/man/1/acme
+directories	/man/1/alphabet-fs
+directories	/man/1/bind
+directories	/man/1/cp
+directories	/man/1/diff
+directories	/man/1/du
+directories	/man/1/fs
+directories	/man/1/grid-ns
+directories	/man/1/grid-session
+directories	/man/1/itest
+directories	/man/1/limbo
+directories	/man/1/mkdir
+directories	/man/1/mv
+directories	/man/1/netstat
+directories	/man/1/pwd
+directories	/man/1/rm
+directories	/man/1/sh-alphabet
+directories	/man/1/stack
+directories	/man/1/sum
+directories	/man/1/tiny
+directories	/man/1/vacget
+directories	/man/10/2c
+directories	/man/10/acid
+directories	/man/10/c2l
+directories	/man/10/conf
+directories	/man/10/odbc
+directories	/man/10/srclist
+directories	/man/10/styxserver
+directories	/man/2/styxservers
+directories	/man/2/sys-0intro
+directories	/man/2/sys-bind
+directories	/man/2/sys-dirread
+directories	/man/2/sys-stat
+directories	/man/2/translate
+directories	/man/3/cmd
+directories	/man/3/fs
+directories	/man/3/plap
+directories	/man/3/prof
+directories	/man/3/prog
+directories	/man/3/ssl
+directories	/man/4/acme
+directories	/man/4/archfs
+directories	/man/4/ftpfs
+directories	/man/4/grid-cpu
+directories	/man/4/import
+directories	/man/4/lockfs
+directories	/man/4/namespace
+directories	/man/4/tarfs
+directories	/man/4/vacfs
+directories	/man/5/0intro
+directories	/man/5/open
+directories	/man/5/read
+directories	/man/5/stat
+directories	/man/6/font
+directories	/man/6/proto
+directories	/man/8/collabsrv
+directories	/man/8/create
+directories	/man/8/kfscmd
+directories	/man/8/mkfs
+directory	/man/1/0intro
+directory	/man/1/acme
+directory	/man/1/alphabet-fs
+directory	/man/1/ar
+directory	/man/1/basename
+directory	/man/1/bind
+directory	/man/1/blur
+directory	/man/1/calendar
+directory	/man/1/cd
+directory	/man/1/charon
+directory	/man/1/chmod
+directory	/man/1/cleanname
+directory	/man/1/collab
+directory	/man/1/collab-clients
+directory	/man/1/cp
+directory	/man/1/cpu
+directory	/man/1/date
+directory	/man/1/deb
+directory	/man/1/diff
+directory	/man/1/du
+directory	/man/1/emu
+directory	/man/1/filename
+directory	/man/1/fs
+directory	/man/1/ftest
+directory	/man/1/ftree
+directory	/man/1/gettar
+directory	/man/1/grid-ns
+directory	/man/1/grid-session
+directory	/man/1/itest
+directory	/man/1/limbo
+directory	/man/1/listen
+directory	/man/1/logon
+directory	/man/1/look
+directory	/man/1/ls
+directory	/man/1/mash
+directory	/man/1/mash-tk
+directory	/man/1/miniterm
+directory	/man/1/mkdir
+directory	/man/1/mv
+directory	/man/1/netstat
+directory	/man/1/nsbuild
+directory	/man/1/os
+directory	/man/1/plumb
+directory	/man/1/pwd
+directory	/man/1/rm
+directory	/man/1/secstore
+directory	/man/1/sh
+directory	/man/1/sh-regex
+directory	/man/1/sh-std
+directory	/man/1/spree-join
+directory	/man/1/tiny
+directory	/man/1/tktester
+directory	/man/1/vacget
+directory	/man/1/wm-misc
+directory	/man/1/wm-sh
+directory	/man/10/2a
+directory	/man/10/2c
+directory	/man/10/9load
+directory	/man/10/acid
+directory	/man/10/c2l
+directory	/man/10/conf
+directory	/man/10/dev
+directory	/man/10/devattach
+directory	/man/10/iar
+directory	/man/10/kproc
+directory	/man/10/kstrip
+directory	/man/10/newchan
+directory	/man/10/ntsrv
+directory	/man/10/odbc
+directory	/man/10/srclist
+directory	/man/10/styx
+directory	/man/10/styxserver
+directory	/man/2/complete
+directory	/man/2/dbm
+directory	/man/2/debug
+directory	/man/2/dhcpclient
+directory	/man/2/dial
+directory	/man/2/disks
+directory	/man/2/draw-display
+directory	/man/2/fsproto
+directory	/man/2/ip
+directory	/man/2/keyring-auth
+directory	/man/2/keyset
+directory	/man/2/names
+directory	/man/2/plumbmsg
+directory	/man/2/prof
+directory	/man/2/readdir
+directory	/man/2/registries
+directory	/man/2/security-ssl
+directory	/man/2/selectfile
+directory	/man/2/spree
+directory	/man/2/styx
+directory	/man/2/styxservers
+directory	/man/2/styxservers-nametree
+directory	/man/2/sys-0intro
+directory	/man/2/sys-bind
+directory	/man/2/sys-chdir
+directory	/man/2/sys-dial
+directory	/man/2/sys-dirread
+directory	/man/2/sys-export
+directory	/man/2/sys-fd2path
+directory	/man/2/sys-file2chan
+directory	/man/2/sys-open
+directory	/man/2/sys-pctl
+directory	/man/2/sys-remove
+directory	/man/2/sys-seek
+directory	/man/2/sys-stat
+directory	/man/2/translate
+directory	/man/2/workdir
+directory	/man/3/audio
+directory	/man/3/boot
+directory	/man/3/cmd
+directory	/man/3/cons
+directory	/man/3/draw
+directory	/man/3/ds
+directory	/man/3/dup
+directory	/man/3/dynld
+directory	/man/3/eia
+directory	/man/3/env
+directory	/man/3/ether
+directory	/man/3/flash
+directory	/man/3/floppy
+directory	/man/3/fs
+directory	/man/3/ftl
+directory	/man/3/gpio
+directory	/man/3/i2c
+directory	/man/3/ip
+directory	/man/3/logfs
+directory	/man/3/pbus
+directory	/man/3/plap
+directory	/man/3/pnp
+directory	/man/3/prof
+directory	/man/3/prog
+directory	/man/3/root
+directory	/man/3/sd
+directory	/man/3/srv
+directory	/man/3/srv9
+directory	/man/3/ssl
+directory	/man/3/switch
+directory	/man/3/tinyfs
+directory	/man/3/tls
+directory	/man/3/touch
+directory	/man/3/usb
+directory	/man/4/9srvfs
+directory	/man/4/acme
+directory	/man/4/archfs
+directory	/man/4/dbfs
+directory	/man/4/dossrv
+directory	/man/4/factotum
+directory	/man/4/ftpfs
+directory	/man/4/grid-cpu
+directory	/man/4/import
+directory	/man/4/keyfs
+directory	/man/4/lockfs
+directory	/man/4/logfile
+directory	/man/4/memfs
+directory	/man/4/mntgen
+directory	/man/4/namespace
+directory	/man/4/ramfile
+directory	/man/4/registry
+directory	/man/4/spree
+directory	/man/4/trfs
+directory	/man/4/vacfs
+directory	/man/5/0intro
+directory	/man/5/attach
+directory	/man/5/open
+directory	/man/5/read
+directory	/man/5/remove
+directory	/man/5/stat
+directory	/man/5/walk
+directory	/man/6/font
+directory	/man/6/keys
+directory	/man/6/namespace
+directory	/man/6/ndb
+directory	/man/6/plumbing
+directory	/man/6/proto
+directory	/man/6/sbl
+directory	/man/8/applylog
+directory	/man/8/bootpd
+directory	/man/8/collabsrv
+directory	/man/8/create
+directory	/man/8/cs
+directory	/man/8/dns
+directory	/man/8/getauthinfo
+directory	/man/8/kfscmd
+directory	/man/8/mkfs
+directory	/man/8/prep
+directory	/man/8/svc
+directory	/man/9/types
+directory's	/man/1/rm
+directory's	/man/2/dial
+directory's	/man/2/styxservers
+directory's	/man/4/tarfs
+directory.the	/man/2/styxservers
+directs	/man/1/cp
+directs	/man/1/sh
+directs	/man/2/virgil
+direntry	/man/2/fsproto
+dirfmt	/man/10/styx
+dirlen	/man/10/devattach
+dirmodefmt	/man/10/styx
+dirname	/man/1/mkdir
+dirname	/man/2/names
+dirread	/man/2/readdir
+dirread	/man/2/sys-0intro
+dirread	/man/2/sys-dirread
+dirread	/man/2/sys-stat
+dirstat	/man/10/devattach
+dirtab	/man/10/devattach
+dirtiness	/man/1/acme
+dirty	/man/1/acme
+dirty	/man/8/kfscmd
+dirty	/man/9/panel
+dirtype	/man/2/venti
+dis	/man/1/0intro
+dis	/man/1/acme
+dis	/man/1/alphabet-fs
+dis	/man/1/asm
+dis	/man/1/cook
+dis	/man/1/cprof
+dis	/man/1/cpu
+dis	/man/1/deb
+dis	/man/1/disdep
+dis	/man/1/emu
+dis	/man/1/fs
+dis	/man/1/grid-ns
+dis	/man/1/limbo
+dis	/man/1/ls
+dis	/man/1/man
+dis	/man/1/mash
+dis	/man/1/mash-make
+dis	/man/1/mdb
+dis	/man/1/mk
+dis	/man/1/mprof
+dis	/man/1/mux
+dis	/man/1/prof
+dis	/man/1/sh
+dis	/man/1/sh-alphabet
+dis	/man/1/sh-arg
+dis	/man/1/sh-regex
+dis	/man/1/sh-test
+dis	/man/1/stack
+dis	/man/1/tiny
+dis	/man/1/uuencode
+dis	/man/1/wm-misc
+dis	/man/10/conf
+dis	/man/10/kproc
+dis	/man/10/styx
+dis	/man/2/command
+dis	/man/2/convcs
+dis	/man/2/debug
+dis	/man/2/dis
+dis	/man/2/filter
+dis	/man/2/print
+dis	/man/2/prof
+dis	/man/2/spree
+dis	/man/3/dbg
+dis	/man/3/prof
+dis	/man/3/prog
+dis	/man/3/root
+dis	/man/3/sign
+dis	/man/4/factotum
+dis	/man/4/grid-cpu
+dis	/man/4/namespace
+dis	/man/4/ramfile
+dis	/man/6/dis
+dis	/man/6/plumbing
+dis	/man/6/proto
+dis	/man/6/sbl
+dis	/man/8/collabsrv
+dis	/man/8/init
+dis	/man/8/shutdown
+dis	/man/8/svc
+dis.b	/man/2/dis
+dis.m	/man/2/dis
+dis:r	/man/1/mk
+disable	/man/1/charon
+disable	/man/1/grid-session
+disable	/man/1/limbo
+disable	/man/10/intrenable
+disable	/man/10/kbdputc
+disable	/man/10/plan9.ini
+disable	/man/10/splhi
+disable	/man/2/plumbmsg
+disable	/man/3/audio
+disable	/man/3/draw
+disable	/man/3/eia
+disable	/man/3/fpga
+disable	/man/3/ip
+disable	/man/3/sd
+disable	/man/3/vga
+disable	/man/3/vid
+disable	/man/4/vacfs
+disable	/man/9/update
+disabled	/man/1/charon
+disabled	/man/1/collab-clients
+disabled	/man/1/grid-session
+disabled	/man/1/sh
+disabled	/man/1/tktester
+disabled	/man/10/dev
+disabled	/man/10/lock
+disabled	/man/10/plan9.ini
+disabled	/man/10/splhi
+disabled	/man/3/logfs
+disabled	/man/3/pnp
+disabled	/man/3/sd
+disabled	/man/3/vga
+disabled	/man/4/factotum
+disabled	/man/4/keyfs
+disabled	/man/8/prep
+disabled	/man/9/button
+disabled	/man/9/checkbutton
+disabled	/man/9/entry
+disabled	/man/9/grid
+disabled	/man/9/menu
+disabled	/man/9/menubutton
+disabled	/man/9/options
+disabled	/man/9/pack
+disabled	/man/9/radiobutton
+disabled	/man/9/scale
+disabled	/man/9/text
+disabled	/man/9/update
+disabledcolor	/man/9/button
+disabledcolor	/man/9/checkbutton
+disabledcolor	/man/9/label
+disabledcolor	/man/9/menu
+disabledcolor	/man/9/menubutton
+disabledcolor	/man/9/options
+disabledcolor	/man/9/radiobutton
+disabledcolor	/man/9/scale
+disables	/man/1/collab-clients
+disables	/man/10/dmainit
+disables	/man/10/lock
+disables	/man/10/splhi
+disables	/man/3/tv
+disables	/man/3/vga
+disabling	/man/1/emu
+disabling	/man/10/kbdputc
+disabling	/man/10/lock
+disagree	/man/10/2c
+disagree	/man/2/ida
+disallow	/man/10/styxserver
+disallow	/man/6/namespace
+disallow	/man/8/kfscmd
+disallows	/man/1/ftree
+disallows	/man/10/styxserver
+disallows	/man/3/fs
+disambiguating	/man/2/asn1
+disappear	/man/8/getauthinfo
+disappearing	/man/1/sh-tk
+disappears	/man/2/sys-0intro
+disappears	/man/2/volume
+disassemble	/man/1/mdb
+disassemble	/man/10/acid
+disassembled	/man/3/prog
+disassembler	/man/1/asm
+disassembly	/man/2/dis
+disassembly	/man/4/palmsrv
+disc	/man/2/draw-image
+disc	/man/3/draw
+disc	/man/3/ftl
+disc	/man/3/sd
+disc	/man/8/prep
+discard	/man/10/allocb
+discard	/man/10/qio
+discard	/man/2/attrdb
+discard	/man/2/bufio
+discard	/man/2/dbm
+discard	/man/2/spree-cardlib
+discard	/man/5/flush
+discarded	/man/1/m4
+discarded	/man/1/tiny
+discarded	/man/10/allocb
+discarded	/man/10/qio
+discarded	/man/2/bufio
+discarded	/man/2/spree-gather
+discarded	/man/2/styxflush
+discarded	/man/3/cmd
+discarded	/man/3/eia
+discarded	/man/3/pbus
+discarded	/man/6/ubfa
+discarded	/man/8/changelogin
+discarded	/man/9/send
+discarding	/man/1/charon
+discarding	/man/2/bufio
+discards	/man/1/ar
+discards	/man/1/m4
+discards	/man/10/allocb
+discards	/man/10/iar
+discards	/man/10/plan9.ini
+discards	/man/10/qio
+discards	/man/2/dhcpclient
+discards	/man/2/sh
+discards	/man/2/sys-remove
+discipline	/man/10/error
+disclaim	/man/9/1copyright
+disclaimed	/man/9/1copyright
+disconcerting	/man/10/plan9.ini
+disconnect	/man/1/miniterm
+disconnect	/man/10/odbc
+disconnecting	/man/3/logfs
+disconnects	/man/10/styxserver
+disconnects	/man/4/grid-cpu
+disconnects	/man/8/collabsrv
+discontiguous	/man/9/listbox
+discover	/man/10/a.out
+discover	/man/2/disks
+discover	/man/2/security-0intro
+discover	/man/3/dup
+discovered	/man/3/pnp
+discovered	/man/4/acme
+discovered	/man/4/dbfs
+discovered	/man/5/open
+discriminated	/man/2/alphabet-intro
+discriminates	/man/1/cprof
+discs	/man/3/sd
+discussed	/man/1/acme
+discussed	/man/10/conf
+discussed	/man/10/qio
+discussed	/man/2/bufio
+discussed	/man/2/keyring-0intro
+discussed	/man/2/sys-stat
+discussed	/man/2/w3c-uris
+discussion	/man/10/2c
+discussion	/man/2/draw-image
+discussion	/man/3/draw
+discussion	/man/3/mnt
+discussion	/man/3/root
+discussion	/man/5/walk
+discussion	/man/6/image
+discussion	/man/8/prep
+disdep	/man/1/disdep
+disdep	/man/2/dis
+disdep	/man/4/namespace
+disdep.b	/man/1/disdep
+disdump	/man/1/asm
+disdump.b	/man/1/asm
+disfile	/man/2/command
+disfile	/man/8/collabsrv
+disjoint	/man/2/draw-0intro
+disjoint	/man/3/ip
+disjunction	/man/1/mdb
+disk	/man/1/0intro
+disk	/man/1/du
+disk	/man/1/zeros
+disk	/man/10/9load
+disk	/man/10/plan9.ini
+disk	/man/2/diskblocks
+disk	/man/2/disks
+disk	/man/3/ds
+disk	/man/3/floppy
+disk	/man/3/logfs
+disk	/man/4/kfs
+disk	/man/8/ftl
+disk	/man/8/init
+disk	/man/8/kfscmd
+disk	/man/8/mkfs
+disk	/man/8/prep
+disk's	/man/8/prep
+disk.init	/man/2/diskblocks
+disk.open	/man/2/disks
+diskblocks	/man/2/diskblocks
+diskblocks.b	/man/2/diskblocks
+diskblocks.m	/man/2/diskblocks
+diskette	/man/8/prep
+diskettes	/man/8/prep
+diskid	/man/7/cddb
+disks	/man/10/9load
+disks	/man/2/disks
+disks	/man/2/scsiio
+disks	/man/3/ds
+disks	/man/8/0intro
+disks	/man/8/prep
+disks.b	/man/2/disks
+disks.m	/man/2/disks
+disksize	/man/8/prep
+dismiss	/man/1/mash-tk
+dismiss	/man/10/intrenable
+dismount	/man/3/logfs
+disp	/man/2/draw-example
+disp.chans	/man/2/draw-example
+disp.draw	/man/2/draw-example
+disp.ellipse	/man/2/draw-example
+disp.fillellipse	/man/2/draw-example
+disp.gendraw	/man/2/draw-example
+disp.r	/man/2/draw-example
+disp.r.min	/man/2/draw-example
+disp.text	/man/2/draw-example
+dispath	/man/1/stack
+dispersal	/man/2/ida
+display	/man/1/0intro
+display	/man/1/acme
+display	/man/1/charon
+display	/man/1/collab-clients
+display	/man/1/deb
+display	/man/1/dmview
+display	/man/1/du
+display	/man/1/ebook
+display	/man/1/emu
+display	/man/1/env
+display	/man/1/ftree
+display	/man/1/grid-monitor
+display	/man/1/grid-ns
+display	/man/1/grid-query
+display	/man/1/grid-session
+display	/man/1/itest
+display	/man/1/logon
+display	/man/1/man
+display	/man/1/mash-make
+display	/man/1/mash-tk
+display	/man/1/miniterm
+display	/man/1/ns
+display	/man/1/secstore
+display	/man/1/spree-join
+display	/man/1/tiny
+display	/man/1/tktester
+display	/man/1/wm-misc
+display	/man/10/9load
+display	/man/10/acid
+display	/man/10/kprof
+display	/man/10/plan9.ini
+display	/man/2/draw-0intro
+display	/man/2/draw-context
+display	/man/2/draw-display
+display	/man/2/draw-example
+display	/man/2/draw-font
+display	/man/2/draw-image
+display	/man/2/draw-screen
+display	/man/2/drawmux
+display	/man/2/imagefile
+display	/man/2/mpeg
+display	/man/2/prefab-0intro
+display	/man/2/prefab-compound
+display	/man/2/prefab-element
+display	/man/2/print
+display	/man/2/prof
+display	/man/2/sexprs
+display	/man/2/sh
+display	/man/2/spree-cardlib
+display	/man/2/tabs
+display	/man/2/tk
+display	/man/2/tkclient
+display	/man/2/volume
+display	/man/2/wmclient
+display	/man/3/draw
+display	/man/3/mpeg
+display	/man/3/pointer
+display	/man/3/tv
+display	/man/3/vga
+display	/man/3/vid
+display	/man/4/acme
+display	/man/4/dossrv
+display	/man/4/ftpfs
+display	/man/4/grid-cpu
+display	/man/4/iostats
+display	/man/6/colour
+display	/man/6/font
+display	/man/6/image
+display	/man/6/man
+display	/man/6/sexprs
+display	/man/8/prep
+display	/man/8/register
+display	/man/8/styxchat
+display	/man/9/button
+display	/man/9/canvas
+display	/man/9/checkbutton
+display	/man/9/cursor
+display	/man/9/entry
+display	/man/9/frame
+display	/man/9/grab
+display	/man/9/listbox
+display	/man/9/menu
+display	/man/9/options
+display	/man/9/panel
+display	/man/9/radiobutton
+display	/man/9/scale
+display	/man/9/text
+display	/man/9/types
+display's	/man/1/dmview
+display.allocate	/man/2/draw-example
+display.color	/man/2/draw-example
+display.cursor	/man/9/cursor
+display.image	/man/2/draw-example
+display.namedimage	/man/2/draw-image
+display.newimage	/man/2/draw-display
+display.newimage	/man/2/draw-example
+display.opaque	/man/2/draw-example
+display.open	/man/2/draw-example
+display.publicscreen	/man/2/draw-screen
+display.readimage	/man/6/image
+display.white	/man/2/draw-example
+display.writeimage	/man/6/image
+displayed	/man/1/acme
+displayed	/man/1/brutus
+displayed	/man/1/charon
+displayed	/man/1/collab
+displayed	/man/1/deb
+displayed	/man/1/fc
+displayed	/man/1/ftree
+displayed	/man/1/grid-monitor
+displayed	/man/1/grid-ns
+displayed	/man/1/grid-query
+displayed	/man/1/grid-session
+displayed	/man/1/itest
+displayed	/man/1/ls
+displayed	/man/1/man
+displayed	/man/1/mash
+displayed	/man/1/mash-make
+displayed	/man/1/mash-tk
+displayed	/man/1/miniterm
+displayed	/man/1/mux
+displayed	/man/1/sh-test
+displayed	/man/1/tktester
+displayed	/man/1/toolbar
+displayed	/man/1/wm-misc
+displayed	/man/1/wm-sh
+displayed	/man/2/draw-0intro
+displayed	/man/2/draw-image
+displayed	/man/2/itslib
+displayed	/man/2/keyring-0intro
+displayed	/man/2/popup
+displayed	/man/2/prefab-0intro
+displayed	/man/2/prefab-environ
+displayed	/man/2/security-0intro
+displayed	/man/2/selectfile
+displayed	/man/2/spree-cardlib
+displayed	/man/2/sys-print
+displayed	/man/2/tabs
+displayed	/man/2/tkclient
+displayed	/man/2/wmclient
+displayed	/man/3/pointer
+displayed	/man/3/vga
+displayed	/man/4/grid-cpu
+displayed	/man/6/man
+displayed	/man/8/signer
+displayed	/man/9/button
+displayed	/man/9/canvas
+displayed	/man/9/checkbutton
+displayed	/man/9/cursor
+displayed	/man/9/entry
+displayed	/man/9/image
+displayed	/man/9/label
+displayed	/man/9/listbox
+displayed	/man/9/menu
+displayed	/man/9/menubutton
+displayed	/man/9/options
+displayed	/man/9/panel
+displayed	/man/9/radiobutton
+displayed	/man/9/scale
+displayed	/man/9/scrollbar
+displayed	/man/9/text
+displaying	/man/1/blur
+displaying	/man/1/deb
+displaying	/man/1/grid-monitor
+displaying	/man/1/grid-session
+displaying	/man/1/mash-tk
+displaying	/man/1/tktester
+displaying	/man/4/iostats
+displaying	/man/9/checkbutton
+displaying	/man/9/choicebutton
+displaying	/man/9/entry
+displaying	/man/9/menu
+displaying	/man/9/options
+displaying	/man/9/radiobutton
+displaying	/man/9/scrollbar
+displays	/man/1/blur
+displays	/man/1/brutus
+displays	/man/1/charon
+displays	/man/1/collab-clients
+displays	/man/1/cprof
+displays	/man/1/deb
+displays	/man/1/dmview
+displays	/man/1/ftree
+displays	/man/1/grid-monitor
+displays	/man/1/grid-query
+displays	/man/1/grid-session
+displays	/man/1/logon
+displays	/man/1/man
+displays	/man/1/miniterm
+displays	/man/1/mprof
+displays	/man/1/ns
+displays	/man/1/prof
+displays	/man/1/secstore
+displays	/man/1/wm-misc
+displays	/man/1/wm-sh
+displays	/man/2/dialog
+displays	/man/2/draw-0intro
+displays	/man/2/draw-display
+displays	/man/2/volume
+displays	/man/3/draw
+displays	/man/3/i2c
+displays	/man/3/vga
+displays	/man/6/colour
+displays	/man/8/getauthinfo
+displays	/man/8/register
+displays	/man/8/signer
+displays	/man/9/button
+displays	/man/9/canvas
+displays	/man/9/checkbutton
+displays	/man/9/choicebutton
+displays	/man/9/entry
+displays	/man/9/label
+displays	/man/9/listbox
+displays	/man/9/menu
+displays	/man/9/menubutton
+displays	/man/9/options
+displays	/man/9/panel
+displays	/man/9/radiobutton
+displays	/man/9/scale
+displays	/man/9/scrollbar
+displays	/man/9/text
+disposal	/man/2/alphabet-intro
+dispose	/man/2/alphabet-intro
+dispose	/man/2/command
+disrupt	/man/2/security-0intro
+dist	/man/9/button
+dist	/man/9/canvas
+dist	/man/9/checkbutton
+dist	/man/9/choicebutton
+dist	/man/9/cursor
+dist	/man/9/entry
+dist	/man/9/frame
+dist	/man/9/label
+dist	/man/9/listbox
+dist	/man/9/menubutton
+dist	/man/9/options
+dist	/man/9/pack
+dist	/man/9/panel
+dist	/man/9/radiobutton
+dist	/man/9/scale
+dist	/man/9/scrollbar
+dist	/man/9/see
+dist	/man/9/text
+dist	/man/9/types
+distance	/man/2/draw-font
+distance	/man/2/draw-image
+distance	/man/3/draw
+distance	/man/6/font
+distance	/man/6/man
+distance	/man/9/canvas
+distance	/man/9/grid
+distance	/man/9/pack
+distance	/man/9/text
+distance	/man/9/types
+distances	/man/9/canvas
+distances	/man/9/text
+distant	/man/1/collab-clients
+distinct	/man/1/0intro
+distinct	/man/1/acme
+distinct	/man/1/m4
+distinct	/man/1/mk
+distinct	/man/10/mk
+distinct	/man/10/ntsrv
+distinct	/man/10/qlock
+distinct	/man/10/strcat
+distinct	/man/2/asn1
+distinct	/man/2/ida
+distinct	/man/2/math-fp
+distinct	/man/2/registries
+distinct	/man/2/sexprs
+distinct	/man/2/spree-cardlib
+distinct	/man/2/styxservers-nametree
+distinct	/man/3/cmd
+distinct	/man/3/mnt
+distinct	/man/6/ndb
+distinct	/man/6/sexprs
+distinct	/man/9/menu
+distinction	/man/10/qio
+distinction	/man/2/asn1
+distinction	/man/2/ip
+distinction	/man/3/draw
+distinction	/man/3/logfs
+distinctly	/man/10/print
+distinguish	/man/2/w3c-xpointers
+distinguish	/man/3/prof
+distinguish	/man/4/factotum
+distinguish	/man/6/translate
+distinguishable	/man/1/acme
+distinguished	/man/1/mk
+distinguished	/man/10/mk
+distinguished	/man/2/json
+distinguished	/man/2/ubfa
+distinguished	/man/2/w3c-xpointers
+distinguished	/man/6/sbl
+distinguished	/man/6/sexprs
+distinguished	/man/8/create
+distinguishes	/man/1/ls
+distinguishes	/man/2/sexprs
+distinguishes	/man/8/prep
+distinguishing	/man/10/acid
+distortions	/man/3/touch
+distribute	/man/2/keyring-0intro
+distribute	/man/6/colour
+distribute	/man/9/1copyright
+distributed	/man/1/mux
+distributed	/man/10/master
+distributed	/man/2/security-0intro
+distributed	/man/2/spree
+distributed	/man/4/spree
+distributed	/man/9/pack
+distributes	/man/2/spree
+distribution	/man/1/alphabet-grid
+distribution	/man/10/master
+distribution	/man/3/pbus
+distribution	/man/3/plap
+distribution	/man/4/namespace
+distribution	/man/8/applylog
+distribution	/man/8/create
+distributions	/man/9/1copyright
+distributive	/man/1/mash
+distributive	/man/1/sh
+distributors	/man/9/1copyright
+div	/man/2/draw-example
+div	/man/2/draw-point
+div	/man/2/keyring-ipint
+div	/man/2/w3c-xpointers
+diversion	/man/1/m4
+diversions	/man/1/m4
+divert	/man/1/m4
+diverted	/man/1/m4
+diverted	/man/3/cons
+divide	/man/1/sh-expr
+divide	/man/1/units
+divide	/man/10/2l
+divide	/man/2/0intro
+divide	/man/2/dividers
+divide	/man/2/math-fp
+divide	/man/6/colour
+divided	/man/10/0intro
+divided	/man/2/dividers
+divided	/man/3/tls
+divided	/man/8/prep
+divided	/man/9/0intro
+divider	/man/2/dividers
+divider.new	/man/2/dividers
+dividers	/man/2/dividers
+dividers	/man/2/selectfile
+dividers	/man/2/tabs
+dividers.b	/man/2/dividers
+dividers.m	/man/2/dividers
+divides	/man/8/ai2key
+dividing	/man/2/dividers
+dividing	/man/9/menu
+division	/man/1/calc
+division	/man/1/mdb
+division	/man/1/sh-expr
+division	/man/2/0intro
+division	/man/2/math-fp
+division	/man/2/palmfile
+division	/man/8/prep
+divisor	/man/3/fpga
+divisor	/man/6/image
+divisor	/man/8/fpgaload
+divisors	/man/6/colour
+divnum	/man/1/m4
+divorce	/man/1/0intro
+dk	/man/6/keytext
+dk	/man/8/ai2key
+dleft	/man/2/spree-cardlib
+dlen	/man/2/ida
+dlineinfo	/man/9/text
+dlr	/man/3/lpt
+dma	/man/10/allocb
+dma	/man/10/dmainit
+dma	/man/10/inb
+dma	/man/10/plan9.ini
+dma	/man/3/mpeg
+dma	/man/3/sd
+dma.c	/man/10/dmainit
+dmacount	/man/10/dmainit
+dmactl	/man/3/sd
+dmadone	/man/10/dmainit
+dmaend	/man/10/dmainit
+dmainit	/man/10/dmainit
+dmappend	/man/2/sys-open
+dmappend	/man/2/sys-stat
+dmappend	/man/3/srv
+dmappend	/man/5/0intro
+dmappend	/man/5/stat
+dmasetup	/man/10/dmainit
+dmauth	/man/2/sys-stat
+dmauth	/man/5/0intro
+dmdir	/man/2/styxservers-nametree
+dmdir	/man/2/sys-open
+dmdir	/man/2/sys-stat
+dmdir	/man/5/0intro
+dmdir	/man/5/open
+dmdir	/man/5/stat
+dmexcl	/man/2/sys-open
+dmexcl	/man/2/sys-stat
+dmexcl	/man/3/srv
+dmexcl	/man/5/0intro
+dmexcl	/man/5/stat
+dmmount	/man/10/devattach
+dmtmp	/man/1/vacget
+dmtmp	/man/5/0intro
+dmtmp	/man/5/stat
+dmview	/man/1/dmview
+dmview	/man/2/drawmux
+dmview.b	/man/1/dmview
+dmwm	/man/1/dmview
+dmwm.b	/man/1/dmview
+dn	/man/2/ir
+dnl	/man/1/m4
+dns	/man/2/dhcpclient
+dns	/man/2/srv
+dns	/man/4/namespace
+dns	/man/6/attrdb
+dns	/man/6/ndb
+dns	/man/8/bootpd
+dns	/man/8/cs
+dns	/man/8/dhcp
+dns	/man/8/dns
+dns	/man/8/svc
+dns.b	/man/8/dns
+dnsdomain	/man/6/ndb
+dnsdomainxx	/man/6/ndb
+dnsfile	/man/8/dns
+dnsquery	/man/8/dns
+dnsquery.b	/man/8/dns
+doc	/man/2/palmfile
+doc	/man/4/namespace
+doc.length	/man/2/palmfile
+doc.open	/man/2/palmfile
+doc.read	/man/2/palmfile
+docookies	/man/1/charon
+doctype	/man/2/xml
+document	/man/1/charon
+document	/man/1/cook
+document	/man/1/ebook
+document	/man/1/man
+document	/man/1/sh
+document	/man/2/math-fp
+document	/man/2/palmfile
+document	/man/2/xml
+document	/man/6/ndb
+document	/man/9/options
+document	/man/9/scrollbar
+document's	/man/2/palmfile
+document's	/man/9/text
+document.applets	/man/1/charon
+document.embeds	/man/1/charon
+document.onunload	/man/1/charon
+document.plugins	/man/1/charon
+documentation	/man/1/0intro
+documentation	/man/1/acme
+documentation	/man/2/0intro
+documentation	/man/4/namespace
+documentation	/man/6/scancode
+documentation	/man/9/0intro
+documentation	/man/9/1copyright
+documented	/man/1/0intro
+documented	/man/1/chgrp
+documented	/man/2/alphabet-intro
+documented	/man/2/plumbmsg
+documented	/man/2/spree
+documented	/man/4/namespace
+documented	/man/9/0intro
+documented	/man/9/canvas
+documented	/man/9/types
+documents	/man/1/charon
+documents	/man/1/ebook
+documents	/man/2/filter
+documents	/man/2/palmfile
+documents	/man/2/xml
+documents	/man/8/0intro
+documents	/man/9/0intro
+does.it.matter	/man/4/factotum
+doesn't	/man/1/bind
+doesn't	/man/1/mc
+doesn't	/man/1/sh
+doesn't	/man/10/print
+doesn't	/man/4/factotum
+doesn't	/man/5/open
+doesn't	/man/6/man
+doesn't	/man/9/canvas
+doesn't	/man/9/entry
+doesn't	/man/9/menu
+doesn't	/man/9/pack
+doesn't	/man/9/scrollbar
+doesn't	/man/9/text
+doing	/man/1/alphabet-abc
+doing	/man/1/alphabet-grid
+doing	/man/10/dev
+doing	/man/10/styxserver
+doing	/man/2/dividers
+doing	/man/2/sh
+doing	/man/2/sys-bind
+doing	/man/2/sys-file2chan
+doing	/man/2/sys-stat
+doing	/man/3/logfs
+doing	/man/5/attach
+doing	/man/5/flush
+doing	/man/8/create
+doing	/man/8/prep
+dollar	/man/1/mash
+dollar	/man/6/keyboard
+dom	/man/2/dhcpclient
+dom	/man/4/factotum
+dom	/man/6/ndb
+dom	/man/8/ai2key
+dom	/man/8/bootpd
+domain	/man/1/sendmail
+domain	/man/1/webgrab
+domain	/man/2/dhcpclient
+domain	/man/2/dial
+domain	/man/2/ip
+domain	/man/2/sys-dial
+domain	/man/4/factotum
+domain	/man/6/attrdb
+domain	/man/6/ndb
+domain	/man/8/ai2key
+domain	/man/8/bootpd
+domain	/man/8/cs
+domain	/man/8/dns
+domains	/man/1/charon
+domains	/man/1/webgrab
+domains	/man/4/factotum
+domains	/man/4/registry
+don''t	/man/1/tiny
+don't	/man/1/mk
+don't	/man/1/tiny
+don't	/man/10/acid
+don't	/man/10/c2l
+don't	/man/10/inm
+don't	/man/10/mk
+don't	/man/2/keyset
+don't	/man/2/palmfile
+don't	/man/2/sys-stat
+don't	/man/5/stat
+don't	/man/6/colour
+don't	/man/6/image
+don't	/man/9/canvas
+don't	/man/9/menu
+don't	/man/9/text
+dontcompile	/man/2/dis
+dontcompile	/man/6/dis
+dopaint	/man/2/mpeg
+doppio	/man/6/ndb
+dos	/man/10/9load
+dos	/man/10/plan9.ini
+dos	/man/2/convcs
+dos	/man/4/dossrv
+dos	/man/8/prep
+dos1	/man/8/prep
+dos2	/man/8/prep
+dos;c	/man/10/plan9.ini
+doscripts	/man/1/charon
+dossrv	/man/10/9load
+dossrv	/man/3/ds
+dossrv	/man/4/0intro
+dossrv	/man/4/dossrv
+dossrv	/man/7/cddb
+dossrv	/man/8/init
+dossrv	/man/8/prep
+dossrv.b	/man/4/dossrv
+dot	/man/1/acme
+dot	/man/1/mdb
+dot	/man/1/sh
+dot	/man/1/tiny
+dot	/man/1/webgrab
+dot	/man/2/ip
+dot	/man/2/math-linalg
+dot	/man/2/sys-0intro
+dot	/man/4/acme
+dot	/man/5/0intro
+dot	/man/5/walk
+dot	/man/6/man
+dot	/man/9/0intro
+dots	/man/1/strings
+dots	/man/2/print
+dots	/man/2/pslib
+dots	/man/9/types
+dotted	/man/3/ip
+double	/man/1/acme
+double	/man/1/brutus
+double	/man/1/fc
+double	/man/1/mash-tk
+double	/man/1/passwd
+double	/man/1/sh-csv
+double	/man/1/wm-sh
+double	/man/10/2c
+double	/man/10/9load
+double	/man/10/atoi
+double	/man/10/c2l
+double	/man/10/print
+double	/man/10/sleep
+double	/man/2/cfg
+double	/man/2/csv
+double	/man/2/math-export
+double	/man/2/math-fp
+double	/man/6/attrdb
+double	/man/6/dis
+double	/man/6/man
+double	/man/8/prep
+double	/man/9/bind
+double	/man/9/entry
+double	/man/9/text
+doubled	/man/2/csv
+doubled	/man/2/string
+doubled	/man/9/bind
+doublequote	/man/1/sh
+doug	/man/9/grid
+douts	/man/2/draw-image
+dovers	/man/2/draw-image
+download	/man/1/charon
+download	/man/10/9load
+downloaded	/man/1/charon
+downloaders	/man/10/5cv
+downloading	/man/1/charon
+downloads	/man/1/charon
+downloads	/man/3/pbus
+downside	/man/1/charon
+downwards	/man/10/a.out
+downwards	/man/2/spree-cardlib
+dozen	/man/2/draw-display
+dp	/man/1/math-misc
+dp	/man/10/devattach
+dp	/man/10/seconds
+dp	/man/3/draw
+dp83815	/man/10/plan9.ini
+dpi	/man/2/print
+dpi	/man/2/pslib
+dpms	/man/10/plan9.ini
+dpms	/man/3/vga
+dpq	/man/4/ftpfs
+dr	/man/8/bootpd
+draft	/man/2/sexprs
+draft	/man/6/sexprs
+drag	/man/1/wm
+drag	/man/1/wm-misc
+drag	/man/2/tkclient
+drag	/man/2/wmclient
+drag	/man/8/plumber
+drag	/man/9/listbox
+drag	/man/9/options
+drag	/man/9/scrollbar
+draggable	/man/2/dividers
+dragged	/man/1/wm-misc
+dragged	/man/2/dividers
+dragged	/man/9/options
+dragged	/man/9/scale
+dragged	/man/9/text
+dragging	/man/1/brutus
+dragging	/man/1/grid-session
+dragging	/man/1/wm-sh
+dragging	/man/9/entry
+dragging	/man/9/listbox
+dragging	/man/9/scrollbar
+dragging	/man/9/text
+drags	/man/1/acme
+drags	/man/1/wm-misc
+dragto	/man/9/text
+drain	/man/2/styxservers
+drains	/man/10/qio
+drains	/man/3/eia
+dram	/man/3/flash
+draw	/man/1/charon
+draw	/man/1/collab-clients
+draw	/man/1/cpu
+draw	/man/1/mux
+draw	/man/1/wm
+draw	/man/1/yacc
+draw	/man/10/conf
+draw	/man/10/plan9.ini
+draw	/man/2/0intro
+draw	/man/2/arg
+draw	/man/2/command
+draw	/man/2/debug
+draw	/man/2/devpointer
+draw	/man/2/dialog
+draw	/man/2/draw-0intro
+draw	/man/2/draw-context
+draw	/man/2/draw-display
+draw	/man/2/draw-example
+draw	/man/2/draw-font
+draw	/man/2/draw-image
+draw	/man/2/draw-point
+draw	/man/2/draw-pointer
+draw	/man/2/draw-rect
+draw	/man/2/draw-screen
+draw	/man/2/drawmux
+draw	/man/2/imagefile
+draw	/man/2/ir
+draw	/man/2/itslib
+draw	/man/2/prefab-0intro
+draw	/man/2/prefab-compound
+draw	/man/2/prefab-element
+draw	/man/2/prefab-environ
+draw	/man/2/prefab-style
+draw	/man/2/print
+draw	/man/2/pslib
+draw	/man/2/selectfile
+draw	/man/2/sh
+draw	/man/2/styxservers-nametree
+draw	/man/2/sys-self
+draw	/man/2/tabs
+draw	/man/2/tk
+draw	/man/2/tkclient
+draw	/man/2/translate
+draw	/man/2/volume
+draw	/man/2/wmclient
+draw	/man/2/wmlib
+draw	/man/2/wmsrv
+draw	/man/3/cons
+draw	/man/3/draw
+draw	/man/3/pointer
+draw	/man/4/spree
+draw	/man/5/open
+draw	/man/6/colour
+draw	/man/6/font
+draw	/man/6/image
+draw	/man/8/collabsrv
+draw	/man/9/0intro
+draw	/man/9/canvas
+draw	/man/9/cursor
+draw	/man/9/options
+draw	/man/9/panel
+draw	/man/9/text
+draw.c	/man/2/draw-0intro
+draw.h	/man/3/draw
+draw.m	/man/1/acme
+draw.m	/man/1/yacc
+draw.m	/man/2/0intro
+draw.m	/man/2/arg
+draw.m	/man/2/devpointer
+draw.m	/man/2/draw-0intro
+draw.m	/man/2/draw-context
+draw.m	/man/2/draw-display
+draw.m	/man/2/draw-example
+draw.m	/man/2/draw-font
+draw.m	/man/2/draw-image
+draw.m	/man/2/draw-point
+draw.m	/man/2/draw-pointer
+draw.m	/man/2/draw-rect
+draw.m	/man/2/draw-screen
+draw.m	/man/2/ir
+draw.m	/man/2/itslib
+draw.m	/man/2/mpeg
+draw.m	/man/2/prefab-0intro
+draw.m	/man/2/prefab-compound
+draw.m	/man/2/prefab-element
+draw.m	/man/2/prefab-environ
+draw.m	/man/2/prefab-style
+draw.m	/man/2/spree
+draw.m	/man/2/spree-cardlib
+draw.m	/man/2/styxservers-nametree
+draw.m	/man/2/tk
+draw.m	/man/2/wmlib
+draw.m	/man/2/wmsrv
+draw.m	/man/3/draw
+drawcontext	/man/2/sh
+drawctxt	/man/2/sh
+drawing	/man/1/collab-clients
+drawing	/man/2/draw-0intro
+drawing	/man/2/draw-display
+drawing	/man/2/draw-example
+drawing	/man/2/draw-image
+drawing	/man/2/prefab-compound
+drawing	/man/2/prefab-element
+drawing	/man/3/draw
+drawing	/man/3/vga
+drawing	/man/8/collabsrv
+drawing	/man/9/canvas
+drawing	/man/9/menu
+drawing	/man/9/options
+drawing	/man/9/scale
+drawing	/man/9/scrollbar
+drawing	/man/9/text
+drawinit	/man/3/vga
+drawmux	/man/1/dmview
+drawmux	/man/2/drawmux
+drawmux.b	/man/1/dmview
+drawmux.m	/man/2/drawmux
+drawn	/man/1/collab-clients
+drawn	/man/2/draw-display
+drawn	/man/2/draw-font
+drawn	/man/2/draw-image
+drawn	/man/2/prefab-0intro
+drawn	/man/2/prefab-compound
+drawn	/man/2/prefab-element
+drawn	/man/2/wmclient
+drawn	/man/3/draw
+drawn	/man/3/vga
+drawn	/man/6/font
+drawn	/man/9/canvas
+drawn	/man/9/checkbutton
+drawn	/man/9/cursor
+drawn	/man/9/listbox
+drawn	/man/9/options
+drawn	/man/9/panel
+drawn	/man/9/radiobutton
+drawn	/man/9/scrollbar
+drawn	/man/9/text
+drawn	/man/9/types
+drawop	/man/2/draw-image
+draws	/man/1/mk
+draws	/man/10/mk
+draws	/man/2/draw-image
+draws	/man/3/draw
+draws	/man/5/stat
+draws	/man/8/touchcal
+dright	/man/2/spree-cardlib
+drive	/man/10/9load
+drive	/man/10/ntsrv
+drive	/man/2/ir
+drive	/man/2/tk
+drive	/man/3/cmd
+drive	/man/3/floppy
+drive	/man/3/lpt
+drive	/man/8/prep
+driven	/man/2/prefab-0intro
+driver	/man/1/0intro
+driver	/man/10/conf
+driver	/man/10/dev
+driver	/man/10/devattach
+driver	/man/10/dynld
+driver	/man/10/eve
+driver	/man/10/intrenable
+driver	/man/10/kbdputc
+driver	/man/10/master
+driver	/man/10/newchan
+driver	/man/10/plan9.ini
+driver	/man/10/qio
+driver	/man/2/mpeg
+driver	/man/2/print
+driver	/man/2/sys-pctl
+driver	/man/3/0intro
+driver	/man/3/audio
+driver	/man/3/cons
+driver	/man/3/dbg
+driver	/man/3/dynld
+driver	/man/3/flash
+driver	/man/3/ftl
+driver	/man/3/i82365
+driver	/man/3/ip
+driver	/man/3/logfs
+driver	/man/3/lpt
+driver	/man/3/mnt
+driver	/man/3/mpeg
+driver	/man/3/pbus
+driver	/man/3/plap
+driver	/man/3/pnp
+driver	/man/3/sd
+driver	/man/3/touch
+driver	/man/3/usb
+driver	/man/3/vga
+driver	/man/5/0intro
+driver	/man/8/ftl
+driver	/man/8/init
+driver	/man/8/prep
+driver's	/man/10/dev
+driver's	/man/10/devattach
+driver's	/man/10/dmainit
+driver's	/man/10/intrenable
+driver's	/man/10/kbdputc
+driver's	/man/10/parsecmd
+driver's	/man/3/cons
+driver's	/man/3/ftl
+driver.b	/man/2/print
+drivers	/man/10/0intro
+drivers	/man/10/9load
+drivers	/man/10/conf
+drivers	/man/10/dev
+drivers	/man/10/devattach
+drivers	/man/10/eve
+drivers	/man/10/inb
+drivers	/man/10/intrenable
+drivers	/man/10/kbdputc
+drivers	/man/10/master
+drivers	/man/10/newchan
+drivers	/man/10/plan9.ini
+drivers	/man/10/qio
+drivers	/man/10/sleep
+drivers	/man/10/splhi
+drivers	/man/3/cons
+drivers	/man/3/dynld
+drivers	/man/3/flash
+drivers	/man/3/i82365
+drivers	/man/3/indir
+drivers	/man/3/ip
+drivers	/man/3/mnt
+drivers	/man/8/init
+drives	/man/1/collab-clients
+drives	/man/3/floppy
+drives	/man/3/touch
+drop	/man/1/sh-string
+drop	/man/1/wm-misc
+drop	/man/2/string
+drop	/man/3/ip
+drop	/man/8/plumber
+dropped	/man/3/pbus
+dropping	/man/1/wm-misc
+drops	/man/3/eia
+drops	/man/3/ip
+drqtpmx	/man/1/ar
+drqtpmx	/man/10/iar
+ds	/man/1/units
+ds	/man/3/ds
+ds	/man/3/sd
+ds	/man/8/prep
+ds1	/man/3/switch
+ds1687	/man/3/rtc
+dsa	/man/2/spki
+dsa	/man/6/keytext
+dsa	/man/8/ai2key
+dsagen	/man/8/ai2key
+dsagen.b	/man/8/ai2key
+dsize	/man/2/dis
+dsp	/man/10/a.out
+dsq	/man/8/bootpd
+dsr	/man/3/eia
+dss	/man/2/security-0intro
+dst	/man/2/dis
+dst	/man/2/draw-image
+dst	/man/2/plumbmsg
+dst	/man/2/spree
+dst	/man/2/spree-cardlib
+dst	/man/2/sys-read
+dst	/man/3/draw
+dst	/man/6/plumbing
+dstid	/man/3/draw
+dstid	/man/4/spree
+dstr	/man/3/draw
+dsttype	/man/1/alphabet-main
+dsttype	/man/1/sh-alphabet
+dt	/man/6/man
+dtag	/man/9/canvas
+dtop	/man/2/spree-cardlib
+dtr	/man/3/eia
+dtt	/man/7/cddb
+dtype	/man/2/disks
+dtype	/man/2/palmfile
+dtype	/man/2/styxservers
+dtype	/man/2/sys-stat
+du	/man/1/du
+du	/man/1/gettar
+du	/man/4/grid-cpu
+du.b	/man/1/du
+dual	/man/1/charon
+dual	/man/1/wm-misc
+due	/man/1/sh
+due	/man/1/wm
+due	/man/10/9load
+due	/man/3/prog
+due	/man/8/prep
+duelling	/man/2/keyring-ipint
+duff	/man/1/alphabet-fs
+duff	/man/1/fs
+duff	/man/1/mash
+duff	/man/2/draw-image
+duff	/man/6/colour
+dummy	/man/10/plan9.ini
+dummyrr	/man/10/plan9.ini
+dump	/man/1/acme
+dump	/man/1/mash
+dump	/man/1/mash-tk
+dump	/man/1/xd
+dump	/man/10/acid
+dump	/man/4/acme
+dump	/man/8/styxchat
+dumpdef	/man/1/m4
+dumpdir	/man/4/acme
+dumped	/man/7/cddb
+dumping	/man/1/xd
+dumps	/man/1/xd
+dumps	/man/10/panic
+dumps	/man/5/stat
+dup	/man/1/fc
+dup	/man/1/sh
+dup	/man/2/sys-0intro
+dup	/man/2/sys-dup
+dup	/man/2/sys-iounit
+dup	/man/2/sys-read
+dup	/man/3/dup
+duplex	/man/10/plan9.ini
+duplex	/man/2/print
+duplicate	/man/1/fc
+duplicate	/man/2/cfg
+duplicate	/man/2/readdir
+duplicate	/man/2/styxservers
+duplicate	/man/2/sys-dup
+duplicate	/man/6/users
+duplicate	/man/8/kfscmd
+duplicated	/man/1/uniq
+duplicated	/man/10/9load
+duplicated	/man/2/readdir
+duplicated	/man/8/kfscmd
+duplicates	/man/1/ar
+duplicates	/man/1/disdep
+duplicates	/man/10/iar
+duplicates	/man/4/registry
+duplicating	/man/1/tr
+duplicating	/man/6/ndb
+duplicating	/man/9/canvas
+dups	/man/3/dup
+duration	/man/1/charon
+duration	/man/1/sh
+duration	/man/1/sh-arg
+duration	/man/1/sh-file2chan
+duration	/man/1/sh-std
+duration	/man/10/odbc
+durations	/man/2/sys-millisec
+dx	/man/2/draw-example
+dx	/man/2/draw-rect
+dx,dy	/man/2/draw-example
+dxocr	/man/1/freq
+dxors	/man/2/draw-image
+dy	/man/2/draw-example
+dy	/man/2/draw-rect
+dynamic	/man/1/0intro
+dynamic	/man/1/mk
+dynamic	/man/1/mprof
+dynamic	/man/1/wm
+dynamic	/man/10/2l
+dynamic	/man/10/mk
+dynamic	/man/2/dhcpclient
+dynamic	/man/3/dynld
+dynamic	/man/3/kprof
+dynamic	/man/3/prog
+dynamic	/man/4/namespace
+dynamic	/man/8/dhcp
+dynamic	/man/8/svc
+dynamically	/man/1/0intro
+dynamically	/man/10/dev
+dynamically	/man/10/devattach
+dynamically	/man/10/dynld
+dynamically	/man/10/intrenable
+dynamically	/man/10/parsecmd
+dynamically	/man/2/draw-image
+dynamically	/man/2/popup
+dynamically	/man/3/dynld
+dynamically	/man/3/indir
+dynamically	/man/3/plap
+dynamically	/man/4/mntgen
+dynamically	/man/4/registry
+dynamically	/man/8/rip
+dynapro	/man/3/touch
+dynfindsym	/man/10/dynld
+dynfindysm	/man/10/dynld
+dynfreeimport	/man/10/dynld
+dynimport	/man/10/dynld
+dynld	/man/10/2c
+dynld	/man/10/2l
+dynld	/man/10/dynld
+dynld	/man/3/dynld
+dynld.h	/man/10/dynld
+dynloadfd	/man/10/dynld
+dynloadgen	/man/10/dynld
+dynobj	/man/10/dynld
+dynobjfree	/man/10/dynld
+dynsym	/man/10/dynld
+dynsyms	/man/3/dynld
+dyntabsize	/man/10/dynld
+e.clip	/man/2/prefab-element
+e.g	/man/1/acme
+e.g	/man/1/alphabet-main
+e.g	/man/1/blur
+e.g	/man/1/fc
+e.g	/man/1/grid-session
+e.g	/man/1/limbo
+e.g	/man/1/sh-expr
+e.g	/man/1/sh-regex
+e.g	/man/1/sh-std
+e.g	/man/1/stack
+e.g	/man/1/tktester
+e.g	/man/1/units
+e.g	/man/10/2c
+e.g	/man/10/a.out
+e.g	/man/10/acid
+e.g	/man/10/plan9.ini
+e.g	/man/2/spree
+e.g	/man/2/spree-cardlib
+e.g	/man/2/spree-gather
+e.g	/man/2/spree-objstore
+e.g	/man/2/string
+e.g	/man/2/tk
+e.g	/man/2/wmclient
+e.g	/man/2/wmlib
+e.g	/man/3/sd
+e.g	/man/4/namespace
+e.g	/man/4/spree
+e.g	/man/6/keyboard
+e.g	/man/6/man
+e.g	/man/8/prep
+e.g	/man/8/styxchat
+e.g	/man/9/bind
+e.g	/man/9/canvas
+e.g	/man/9/options
+e.g	/man/9/text
+e.r.inset	/man/2/prefab-element
+e.tag.constr	/man/2/asn1
+e.val	/man/2/asn1
+ea	/man/10/plan9.ini
+eaddrlen	/man/2/ether
+eano	/man/2/geodesy
+earg	/man/2/arg
+earlier	/man/1/mv
+earlier	/man/1/sh-alphabet
+earlier	/man/1/tsort
+earlier	/man/2/readdir
+earlier	/man/2/styxconv
+earlier	/man/2/translate
+earlier	/man/5/version
+earlier	/man/8/cs
+earlier	/man/9/canvas
+earlier	/man/9/entry
+earlier	/man/9/listbox
+earlier	/man/9/text
+easier	/man/1/acme
+easier	/man/1/alphabet-main
+easier	/man/10/c2l
+easier	/man/2/alphabet-intro
+easiest	/man/10/0intro
+easiest	/man/2/json
+easiest	/man/2/ubfa
+easily	/man/1/tktester
+easily	/man/2/keyring-sha1
+easily	/man/3/logfs
+east	/man/6/attrdb
+east	/man/9/grid
+easting	/man/2/geodesy
+eastings	/man/2/geodesy
+easy	/man/10/plan9.ini
+easy	/man/2/security-0intro
+easy	/man/3/logfs
+easy	/man/5/0intro
+easy	/man/5/stat
+easy	/man/6/colour
+eavesdropper	/man/2/security-0intro
+ebadarg	/man/10/devattach
+ebadarg	/man/10/styxserver
+ebadarg	/man/2/styxservers
+ebadcmd	/man/10/styxserver
+ebadfid	/man/2/styxservers
+ebc	/man/2/keyring-crypt
+ebcdic	/man/1/dd
+ebcdic,ucase	/man/1/dd
+ebook	/man/1/ebook
+ebooks	/man/1/ebook
+ec2t	/man/10/plan9.ini
+ecb	/man/2/security-0intro
+ecb	/man/3/ssl
+ecc	/man/3/flash
+echo	/man/1/acme
+echo	/man/1/alphabet-fs
+echo	/man/1/alphabet-main
+echo	/man/1/echo
+echo	/man/1/fs
+echo	/man/1/listen
+echo	/man/1/mash
+echo	/man/1/netkey
+echo	/man/1/sh
+echo	/man/1/sh-alphabet
+echo	/man/1/sh-arg
+echo	/man/1/sh-file2chan
+echo	/man/1/sh-mload
+echo	/man/1/sh-std
+echo	/man/1/sh-test
+echo	/man/1/timestamp
+echo	/man/1/tiny
+echo	/man/1/wm-sh
+echo	/man/10/odbc
+echo	/man/10/plan9.ini
+echo	/man/3/dbg
+echo	/man/3/ds
+echo	/man/3/kprof
+echo	/man/3/logfs
+echo	/man/3/pnp
+echo	/man/3/sd
+echo	/man/3/vga
+echo	/man/4/9srvfs
+echo	/man/4/acme
+echo	/man/4/grid-cpu
+echo	/man/4/palmsrv
+echo	/man/4/spree
+echo	/man/8/dhcp
+echo	/man/8/httpd
+echo	/man/8/ping
+echo	/man/8/rdbgsrv
+echo.b	/man/1/echo
+echoed	/man/1/wm-sh
+echoed	/man/3/cons
+echoed	/man/5/stat
+echoing	/man/4/acme
+echoing	/man/5/flush
+ecma	/man/1/charon
+ecmascript	/man/1/charon
+ecmascript	/man/6/json
+ed	/man/1/diff
+ed50	/man/2/geodesy
+edata	/man/10/2l
+ede	/man/3/tls
+edge	/man/1/blur
+edge	/man/10/intrenable
+edge	/man/2/draw-0intro
+edge	/man/2/draw-image
+edge	/man/3/gpio
+edge	/man/3/pbus
+edge	/man/9/canvas
+edge	/man/9/entry
+edge	/man/9/listbox
+edge	/man/9/menu
+edge	/man/9/text
+edgecolor	/man/2/prefab-style
+edges	/man/1/wm-misc
+edges	/man/2/prefab-style
+edges	/man/9/canvas
+edges	/man/9/options
+edges	/man/9/pack
+edgeserver	/man/3/pbus
+edgeserver	/man/3/plap
+edit	/man/1/acme
+edit	/man/1/brutus
+edit	/man/1/miniterm
+edit	/man/1/wm-misc
+edit	/man/2/plumbmsg
+edit	/man/4/ramfile
+edit	/man/6/plumbing
+edit.b	/man/1/wm-misc
+editable	/man/1/collab-clients
+edited	/man/1/acme
+edited	/man/1/tktester
+edited	/man/1/wm-sh
+edited	/man/8/svc
+edited	/man/9/entry
+edited	/man/9/scrollbar
+edited	/man/9/text
+editing	/man/1/acme
+editing	/man/1/brutus
+editing	/man/1/charon
+editing	/man/1/diff
+editing	/man/1/miniterm
+editing	/man/1/wm-misc
+editing	/man/1/wm-sh
+editing	/man/10/c2l
+edition	/man/1/charon
+edition	/man/1/emu
+edition	/man/1/m4
+edition	/man/1/mash
+edition	/man/1/secstore
+edition	/man/1/yacc
+edition	/man/2/styxconv
+edition	/man/2/xml
+edition	/man/8/changelogin
+edition	/man/9/send
+editions	/man/1/charon
+editor	/man/1/0intro
+editor	/man/1/brutus
+editor	/man/1/cook
+editor	/man/1/diff
+editor	/man/1/ftree
+editor	/man/1/man
+editor	/man/1/mdb
+editor	/man/1/wm-misc
+editor	/man/10/acid
+editor	/man/8/prep
+editors	/man/1/deb
+editors	/man/4/ramfile
+edits	/man/8/prep
+edt	/man/2/daytime
+ee	/man/6/json
+ee	/man/6/man
+eeeeeennnnnn	/man/2/geodesy
+eeeeennnnn	/man/2/geodesy
+eeeennnn	/man/2/geodesy
+eeennn	/man/2/geodesy
+eenn	/man/2/geodesy
+eeprom	/man/1/avr
+eexist	/man/10/styxserver
+eexists	/man/2/styxservers
+efbwr	/man/1/diff
+effective	/man/1/m4
+effective	/man/10/styx
+effective	/man/2/ida
+effective	/man/3/ip
+effective	/man/3/vid
+effective	/man/8/ftl
+effectively	/man/3/indir
+effects	/man/1/bind
+effects	/man/1/sh
+effects	/man/1/wm-misc
+effects	/man/2/dhcpclient
+effects	/man/2/sh
+effects	/man/2/sys-bind
+effects	/man/2/sys-export
+effects	/man/2/sys-pctl
+effects	/man/3/cmd
+effects	/man/9/image
+effects	/man/9/menu
+effects	/man/9/options
+efficiency	/man/3/logfs
+efficiency	/man/6/dis
+efficient	/man/1/acme
+efficient	/man/1/dd
+efficient	/man/2/draw-0intro
+efficient	/man/2/format
+efficient	/man/2/ida
+efficient	/man/2/math-export
+efficient	/man/2/sets
+efficient	/man/2/sys-pipe
+efficient	/man/2/sys-read
+efficient	/man/6/sexprs
+efficient	/man/8/rip
+efficiently	/man/10/memory
+efficiently	/man/2/math-linalg
+efficiently	/man/2/sexprs
+efficiently	/man/2/xml
+efficiently	/man/6/sexprs
+efr	/man/1/cprof
+eg	/man/1/0intro
+eg	/man/1/keyboard
+eg	/man/1/kill
+eg	/man/1/netstat
+eg	/man/1/os
+eg	/man/1/timestamp
+eg	/man/1/tiny
+eg	/man/1/webgrab
+eg	/man/1/wm-misc
+eg	/man/10/2c
+eg	/man/10/allocb
+eg	/man/10/dev
+eg	/man/10/devattach
+eg	/man/10/dmainit
+eg	/man/10/intrenable
+eg	/man/10/lock
+eg	/man/10/master
+eg	/man/10/ntsrv
+eg	/man/10/xalloc
+eg	/man/2/arg
+eg	/man/2/attrdb
+eg	/man/2/bufio
+eg	/man/2/command
+eg	/man/2/daytime
+eg	/man/2/dbm
+eg	/man/2/dhcpclient
+eg	/man/2/dial
+eg	/man/2/draw-context
+eg	/man/2/draw-display
+eg	/man/2/ether
+eg	/man/2/filter
+eg	/man/2/fsproto
+eg	/man/2/imagefile
+eg	/man/2/ip
+eg	/man/2/keyring-getmsg
+eg	/man/2/keyring-rc4
+eg	/man/2/palmfile
+eg	/man/2/plumbmsg
+eg	/man/2/print
+eg	/man/2/registries
+eg	/man/2/rfc822
+eg	/man/2/secstore
+eg	/man/2/security-login
+eg	/man/2/spki
+eg	/man/2/srv
+eg	/man/2/styxservers
+eg	/man/2/sys-0intro
+eg	/man/2/sys-dial
+eg	/man/2/sys-export
+eg	/man/2/sys-self
+eg	/man/2/sys-sleep
+eg	/man/2/sys-stat
+eg	/man/2/translate
+eg	/man/2/ubfa
+eg	/man/2/w3c-css
+eg	/man/3/cap
+eg	/man/3/cmd
+eg	/man/3/dbg
+eg	/man/3/eia
+eg	/man/3/fpga
+eg	/man/3/fs
+eg	/man/3/ftl
+eg	/man/3/indir
+eg	/man/3/ip
+eg	/man/3/logfs
+eg	/man/3/plap
+eg	/man/3/srv
+eg	/man/4/archfs
+eg	/man/4/namespace
+eg	/man/4/registry
+eg	/man/6/attrdb
+eg	/man/6/audio
+eg	/man/6/colour
+eg	/man/6/plumbing
+eg	/man/6/sbl
+eg	/man/6/translate
+eg	/man/8/applylog
+eg	/man/8/changelogin
+eg	/man/8/collabsrv
+eg	/man/8/dhcp
+eg	/man/8/getauthinfo
+eg	/man/8/init
+eg	/man/8/kfscmd
+egp	/man/1/mk
+egp	/man/10/mk
+egreg	/man/10/error
+ehorizontal	/man/2/prefab-element
+ehorizontal:con	/man/2/prefab-element
+ehungup	/man/10/qio
+ehungup	/man/10/styxserver
+eia	/man/1/avr
+eia	/man/10/plan9.ini
+eia	/man/3/dbg
+eia	/man/3/eia
+eia	/man/3/ip
+eia0	/man/1/avr
+eia0	/man/10/plan9.ini
+eia0	/man/3/dbg
+eia0	/man/3/eia
+eia0	/man/3/ip
+eia0	/man/4/export
+eia0	/man/4/palmsrv
+eia0	/man/8/rdbgsrv
+eia0ctl	/man/3/dbg
+eia0ctl	/man/3/eia
+eia0status	/man/3/eia
+eia1	/man/10/plan9.ini
+eia1	/man/3/eia
+eia1ctl	/man/3/eia
+eia1status	/man/3/eia
+eicon	/man/2/prefab-element
+eight	/man/1/passwd
+eight	/man/5/0intro
+eight	/man/6/image
+eight	/man/9/text
+eighth	/man/2/keyring-crypt
+einterrupted	/man/2/styxflush
+einuse	/man/2/styxservers
+eject	/man/3/floppy
+ek	/man/6/keytext
+ek	/man/8/ai2key
+eke	/man/2/security-0intro
+elaborate	/man/1/m4
+elaborate	/man/2/sexprs
+elaborate	/man/2/w3c-xpointers
+elaborate	/man/6/sexprs
+elapsed	/man/1/itest
+electron	/man/1/units
+electronic	/man/2/keyring-crypt
+electronic	/man/2/security-0intro
+electronic	/man/3/ssl
+electronics	/man/3/touch
+elem	/man/1/cook
+elem	/man/1/fc
+elem	/man/10/devattach
+elem	/man/2/0intro
+elem	/man/2/asn1
+elem	/man/2/math-0intro
+elem	/man/2/math-elem
+elem	/man/2/prefab-compound
+elem	/man/2/prefab-element
+elem.is	/man/2/asn1
+elem.tag	/man/2/asn1
+elem.tag.class	/man/2/asn1
+elem.tag.num	/man/2/asn1
+elem.tostring	/man/2/asn1
+elem.val	/man/2/asn1
+elem1	/man/2/asn1
+elem2	/man/2/asn1
+elem:self	/man/2/prefab-element
+elemcolor	/man/2/prefab-style
+element's	/man/2/prefab-element
+element.adjust	/man/2/prefab-element
+element.append	/man/2/prefab-element
+element.icon	/man/2/prefab-compound
+element.layout	/man/2/prefab-compound
+element.layout	/man/2/prefab-element
+element.r	/man/2/prefab-element
+element.scroll	/man/2/prefab-compound
+element.scroll	/man/2/prefab-element
+element.show	/man/2/prefab-compound
+element.text	/man/2/prefab-compound
+element.text	/man/2/prefab-element
+elementary	/man/2/math-0intro
+elementary	/man/2/math-elem
+elementborderwidth	/man/9/scrollbar
+elementwise	/man/5/walk
+elems	/man/2/bloomfilter
+eleven	/man/10/2c
+elgamal	/man/2/keyring-gensk
+elgamal	/man/2/security-0intro
+elgamal	/man/6/keytext
+elgamal	/man/8/ai2key
+elgamal	/man/8/createsignerkey
+elided	/man/1/acme
+elided	/man/1/alphabet-fs
+elided	/man/1/fs
+elided	/man/1/mk
+elided	/man/10/mk
+elided	/man/2/xml
+elided	/man/4/acme
+elided	/man/4/logfile
+elif	/man/10/2c
+eliminate	/man/2/bloomfilter
+eliminate	/man/2/draw-0intro
+eliminating	/man/10/error
+elimination	/man/2/math-0intro
+elisions	/man/1/acme
+elist	/man/2/prefab-compound
+elist	/man/2/prefab-element
+elist.r	/man/2/prefab-element
+elists	/man/2/prefab-element
+elite	/man/10/plan9.ini
+ell	/man/1/tail
+ellipse	/man/2/draw-image
+ellipse	/man/3/draw
+ellipseop	/man/2/draw-image
+elliptical	/man/2/draw-example
+ellis	/man/3/indir
+elnk3	/man/10/plan9.ini
+elongated	/man/9/canvas
+els	/man/1/sh-sexprs
+els	/man/2/names
+els	/man/2/sexprs
+els	/man/2/ubfa
+elseaction	/man/1/sh-std
+elsewhere	/man/1/ar
+elsewhere	/man/1/dmview
+elsewhere	/man/1/grid-session
+elsewhere	/man/1/sh
+elsewhere	/man/1/tktester
+elsewhere	/man/10/allocb
+elsewhere	/man/10/iar
+elsewhere	/man/2/disks
+elsewhere	/man/2/drawmux
+elsewhere	/man/2/sys-export
+elsewhere	/man/3/fs
+elsewhere	/man/3/pointer
+elsewhere	/man/6/keytext
+elsewhere	/man/9/menubutton
+email	/man/1/mux
+emanating	/man/1/sh-file2chan
+embedded	/man/1/ebook
+embedded	/man/2/asn1
+embedded	/man/2/csv
+embedded	/man/2/factotum
+embedded	/man/2/ip
+embedded	/man/2/math-0intro
+embedded	/man/9/panel
+embedded	/man/9/text
+embedded	/man/9/types
+embodied	/man/4/factotum
+emboldened	/man/1/brutus
+embryonic	/man/2/complete
+emerge	/man/3/tinyfs
+emitted	/man/1/yacc
+employ	/man/1/acme
+employ	/man/1/charon
+employ	/man/2/security-0intro
+employs	/man/2/disks
+empties	/man/10/allocb
+empties	/man/10/qio
+ems	/man/2/w3c-css
+emtpy	/man/2/convcs
+emu	/man/1/asm
+emu	/man/1/emu
+emu	/man/1/limbo
+emu	/man/1/mux
+emu	/man/10/allocb
+emu	/man/10/conf
+emu	/man/10/devattach
+emu	/man/10/error
+emu	/man/10/eve
+emu	/man/10/kproc
+emu	/man/10/lock
+emu	/man/10/master
+emu	/man/10/newchan
+emu	/man/10/ntsrv
+emu	/man/10/parsecmd
+emu	/man/10/qio
+emu	/man/10/qlock
+emu	/man/10/readnum
+emu	/man/10/ref
+emu	/man/10/sleep
+emu	/man/2/srv
+emu	/man/2/sys-0intro
+emu	/man/2/sys-dial
+emu	/man/2/sys-export
+emu	/man/2/sys-file2chan
+emu	/man/2/sys-iounit
+emu	/man/3/audio
+emu	/man/3/cap
+emu	/man/3/cmd
+emu	/man/3/cons
+emu	/man/3/draw
+emu	/man/3/dup
+emu	/man/3/env
+emu	/man/3/fs
+emu	/man/3/indir
+emu	/man/3/ip
+emu	/man/3/logfs
+emu	/man/3/mnt
+emu	/man/3/pipe
+emu	/man/3/pointer
+emu	/man/3/prof
+emu	/man/3/prog
+emu	/man/3/root
+emu	/man/3/sign
+emu	/man/3/snarf
+emu	/man/3/srv
+emu	/man/3/srv9
+emu	/man/3/tls
+emu	/man/4/namespace
+emu	/man/6/scancode
+emu	/man/7/db
+emu	/man/7/dbsrv
+emu	/man/8/init
+emu	/man/8/shutdown
+emu.exe	/man/10/ntsrv
+emuargs	/man/4/9srvfs
+emuargs	/man/8/init
+emuinit	/man/10/conf
+emuinit	/man/8/init
+emuinit.b	/man/8/init
+emuinit.dis	/man/1/emu
+emuinit.dis	/man/10/conf
+emuinit.dis	/man/8/init
+emulate	/man/8/manufacture
+emulated	/man/1/mux
+emulated	/man/10/2c
+emulated	/man/10/print
+emulates	/man/8/manufacture
+emulating	/man/8/register
+emulation	/man/1/emu
+emulation	/man/10/2l
+emulation	/man/3/cons
+emulation	/man/3/ip
+emulation	/man/3/root
+emulation	/man/4/namespace
+emulator	/man/1/emu
+emulator	/man/1/miniterm
+emulator	/man/8/shutdown
+en	/man/1/acme
+en	/man/2/geodesy
+en2216	/man/10/plan9.ini
+en2lalo	/man/2/geodesy
+en2os	/man/2/geodesy
+enable	/man/1/charon
+enable	/man/1/collab-clients
+enable	/man/1/ebook
+enable	/man/1/miniterm
+enable	/man/1/timestamp
+enable	/man/10/intrenable
+enable	/man/10/kbdputc
+enable	/man/10/plan9.ini
+enable	/man/10/splhi
+enable	/man/10/styxserver
+enable	/man/2/filter-deflate
+enable	/man/2/format
+enable	/man/2/security-0intro
+enable	/man/2/spree
+enable	/man/2/spree-objstore
+enable	/man/2/styxservers
+enable	/man/2/wmsrv
+enable	/man/3/audio
+enable	/man/3/cons
+enable	/man/3/draw
+enable	/man/3/eia
+enable	/man/3/fpga
+enable	/man/3/ip
+enable	/man/3/logfs
+enable	/man/3/sd
+enable	/man/3/tls
+enable	/man/3/tv
+enable	/man/3/vga
+enable	/man/3/vid
+enable	/man/8/dhcp
+enable	/man/9/menu
+enable	/man/9/options
+enable	/man/9/update
+enabled	/man/1/0intro
+enabled	/man/1/charon
+enabled	/man/1/collab-clients
+enabled	/man/1/miniterm
+enabled	/man/1/rcmd
+enabled	/man/10/2l
+enabled	/man/10/dev
+enabled	/man/10/plan9.ini
+enabled	/man/10/splhi
+enabled	/man/2/sh
+enabled	/man/3/arch
+enabled	/man/3/cap
+enabled	/man/3/ip
+enabled	/man/3/kprof
+enabled	/man/3/logfs
+enabled	/man/3/pnp
+enabled	/man/3/sd
+enabled	/man/3/sign
+enabled	/man/8/styxchat
+enabled	/man/9/grid
+enabled	/man/9/menu
+enabled	/man/9/pack
+enabled	/man/9/scale
+enabled	/man/9/text
+enables	/man/1/alphabet-abc
+enables	/man/1/alphabet-fs
+enables	/man/1/alphabet-grid
+enables	/man/1/charon
+enables	/man/1/collab-clients
+enables	/man/1/emu
+enables	/man/1/sh
+enables	/man/10/5cv
+enables	/man/10/error
+enables	/man/10/panic
+enables	/man/10/plan9.ini
+enables	/man/10/qlock
+enables	/man/10/splhi
+enables	/man/2/0intro
+enables	/man/2/security-0intro
+enables	/man/2/security-auth
+enables	/man/2/spree-allow
+enables	/man/2/xml
+enables	/man/3/cap
+enables	/man/3/vga
+enables	/man/4/keyfs
+enables	/man/4/spree
+enables	/man/8/fpgaload
+enabling	/man/1/charon
+enabling	/man/1/toolbar
+enabling	/man/10/kbdputc
+enabling	/man/2/draw-screen
+ename	/man/10/styx
+ename	/man/2/sh
+ename	/man/2/styx
+ename	/man/5/0intro
+ename	/man/5/error
+ename	/man/8/styxchat
+enc	/man/2/encoding
+enc	/man/2/ida
+enc	/man/2/rfc822
+enc	/man/2/w3c-uris
+enc	/man/3/audio
+enc	/man/3/tls
+enc	/man/6/audio
+encalg	/man/3/tls
+encalgs	/man/3/ssl
+encalgs	/man/3/tls
+encapsulate	/man/2/plumbmsg
+encapsulated	/man/1/grid-ns
+encapsulated	/man/2/plumbmsg
+encapsulated	/man/2/pslib
+encapsulated	/man/2/xml
+encapsulated	/man/4/grid-cpu
+encapsulated	/man/6/keytext
+encapsulated	/man/8/cs
+encapsulates	/man/2/draw-context
+encapsulates	/man/2/exception
+encapsulates	/man/2/plumbmsg
+encapsulates	/man/2/security-auth
+encapsulates	/man/2/styxservers
+encin	/man/3/tls
+enclosed	/man/1/sh
+enclosed	/man/10/2c
+enclosed	/man/10/c2l
+enclosed	/man/2/tk
+enclosed	/man/3/prog
+enclosed	/man/6/sexprs
+enclosed	/man/9/0intro
+enclosed	/man/9/canvas
+enclosing	/man/1/acme
+enclosing	/man/1/tiny
+enclosing	/man/2/0intro
+enclosing	/man/6/sbl
+enclosing	/man/9/canvas
+encode	/man/1/uuencode
+encode	/man/10/ms2
+encode	/man/2/asn1
+encode	/man/2/encoding
+encode	/man/2/filter-slip
+encode	/man/2/keyring-0intro
+encode	/man/2/palmfile
+encode	/man/2/w3c-uris
+encode	/man/3/tls
+encode	/man/6/dis
+encode	/man/6/image
+encode	/man/6/sexprs
+encode	/man/6/utf
+encoded	/man/1/auplay
+encoded	/man/1/strings
+encoded	/man/1/uuencode
+encoded	/man/1/wc
+encoded	/man/10/a.out
+encoded	/man/10/devattach
+encoded	/man/2/asn1
+encoded	/man/2/bufio
+encoded	/man/2/dis
+encoded	/man/2/encoding
+encoded	/man/2/ida
+encoded	/man/2/imagefile
+encoded	/man/2/keyring-crypt
+encoded	/man/2/keyring-ipint
+encoded	/man/2/math-export
+encoded	/man/2/palmfile
+encoded	/man/2/rfc822
+encoded	/man/2/spki
+encoded	/man/2/w3c-uris
+encoded	/man/2/wmsrv
+encoded	/man/3/tls
+encoded	/man/3/tv
+encoded	/man/3/vga
+encoded	/man/5/0intro
+encoded	/man/6/audio
+encoded	/man/6/dis
+encoded	/man/6/image
+encoded	/man/6/json
+encoded	/man/6/keytext
+encoded	/man/6/login
+encoded	/man/6/sbl
+encoded	/man/6/scancode
+encoded	/man/6/ubfa
+encoded	/man/8/rstyxd
+encodedfile	/man/1/uuencode
+encoder	/man/3/vid
+encodes	/man/1/uuencode
+encodes	/man/2/math-elem
+encodes	/man/2/math-export
+encodes	/man/6/dis
+encodes	/man/6/sexprs
+encoding	/man/1/tcs
+encoding	/man/1/uuencode
+encoding	/man/10/a.out
+encoding	/man/10/intrenable
+encoding	/man/10/kbdputc
+encoding	/man/10/rune
+encoding	/man/10/styx
+encoding	/man/2/asn1
+encoding	/man/2/convcs
+encoding	/man/2/draw-0intro
+encoding	/man/2/encoding
+encoding	/man/2/filter
+encoding	/man/2/filter-slip
+encoding	/man/2/ida
+encoding	/man/2/json
+encoding	/man/2/keyring-0intro
+encoding	/man/2/keyring-getmsg
+encoding	/man/2/msgio
+encoding	/man/2/palmfile
+encoding	/man/2/plumbmsg
+encoding	/man/2/rfc822
+encoding	/man/2/secstore
+encoding	/man/2/sexprs
+encoding	/man/2/spki
+encoding	/man/2/sys-print
+encoding	/man/2/ubfa
+encoding	/man/2/w3c-uris
+encoding	/man/3/cons
+encoding	/man/5/stat
+encoding	/man/6/audio
+encoding	/man/6/dis
+encoding	/man/6/image
+encoding	/man/6/json
+encoding	/man/6/keytext
+encoding	/man/6/sexprs
+encoding	/man/6/ubfa
+encoding	/man/6/utf
+encoding	/man/8/httpd
+encoding.m	/man/2/encoding
+encodings	/man/1/charon
+encodings	/man/2/asn1
+encodings	/man/2/encoding
+encodings	/man/2/rfc822
+encodings	/man/2/sexprs
+encodings	/man/2/w3c-uris
+encodings	/man/6/sexprs
+encountering	/man/10/9load
+encountering	/man/2/bufio
+encountering	/man/8/create
+encounters	/man/1/sh
+encounters	/man/2/sh
+encourage	/man/10/inb
+encourage	/man/2/dhcpclient
+encout	/man/3/tls
+encrypt	/man/1/bind
+encrypt	/man/1/crypt
+encrypt	/man/1/idea
+encrypt	/man/1/listen
+encrypt	/man/2/keyring-0intro
+encrypt	/man/2/keyring-auth
+encrypt	/man/2/keyring-crypt
+encrypt	/man/2/keyring-rc4
+encrypt	/man/2/security-0intro
+encrypt	/man/2/security-auth
+encrypt	/man/3/ssl
+encrypt	/man/3/tls
+encrypt	/man/6/auth
+encrypt	/man/8/svc
+encrypted	/man/1/crypt
+encrypted	/man/1/idea
+encrypted	/man/2/keyring-crypt
+encrypted	/man/2/secstore
+encrypted	/man/2/security-0intro
+encrypted	/man/2/security-login
+encrypted	/man/3/ssl
+encrypted	/man/4/import
+encrypted	/man/4/keyfs
+encrypted	/man/6/keys
+encrypted	/man/6/login
+encrypted	/man/8/svc
+encrypting	/man/1/secstore
+encrypting	/man/2/security-0intro
+encrypting	/man/3/ssl
+encrypting	/man/3/tls
+encryption	/man/1/alphabet-main
+encryption	/man/1/bind
+encryption	/man/1/collab
+encryption	/man/1/cpu
+encryption	/man/1/crypt
+encryption	/man/1/idea
+encryption	/man/1/listen
+encryption	/man/1/rcmd
+encryption	/man/10/plan9.ini
+encryption	/man/2/factotum
+encryption	/man/2/keyring-certtostr
+encryption	/man/2/keyring-crypt
+encryption	/man/2/keyring-rc4
+encryption	/man/2/secstore
+encryption	/man/2/security-0intro
+encryption	/man/2/security-auth
+encryption	/man/2/sys-export
+encryption	/man/3/ssl
+encryption	/man/3/tls
+encryption	/man/4/import
+encryption	/man/7/dbsrv
+encryption	/man/8/ai2key
+encryption	/man/8/rstyxd
+encryptor	/man/2/keyring-crypt
+encrypts	/man/1/crypt
+encrypts	/man/1/idea
+end0	/man/2/draw-image
+end0	/man/3/draw
+end0,end1,thick	/man/2/draw-image
+end1	/man/2/draw-image
+end1	/man/3/draw
+endarrow	/man/2/draw-image
+endcontrol	/man/1/wm
+enddisc	/man/2/draw-image
+ended	/man/1/m4
+ended	/man/10/error
+ended	/man/2/rfc822
+ended	/man/2/wait
+ended	/man/3/pointer
+endian	/man/1/auplay
+endian	/man/1/charon
+endian	/man/1/mdb
+endian	/man/1/xd
+endian	/man/10/2c
+endian	/man/10/a.out
+endian	/man/2/keyring-ipint
+endian	/man/2/math-export
+endian	/man/2/palmfile
+endian	/man/2/sets
+endian	/man/3/audio
+endian	/man/3/cons
+endian	/man/3/dbg
+endian	/man/3/ip
+endian	/man/3/kprof
+endian	/man/3/vid
+endian	/man/4/kfs
+endian	/man/5/0intro
+endian	/man/5/stat
+endian	/man/6/audio
+endian	/man/6/colour
+endian	/man/6/image
+endian	/man/6/keytext
+ending	/man/1/alphabet-fs
+ending	/man/1/basename
+ending	/man/1/fs
+ending	/man/1/m4
+ending	/man/1/sh-string
+ending	/man/10/allocb
+ending	/man/10/srclist
+ending	/man/6/plumbing
+ending	/man/8/prep
+ending	/man/9/entry
+ending	/man/9/text
+endings	/man/3/draw
+endkey	/man/1/look
+endorse	/man/8/getauthinfo
+endpoint	/man/1/alphabet-abc
+endpoint	/man/1/alphabet-grid
+endpoint	/man/2/draw-image
+endpoint	/man/3/usb
+endpoint	/man/9/text
+endpoints	/man/3/usb
+endpoints	/man/9/canvas
+endpoints	/man/9/scale
+ends	/man/1/acme
+ends	/man/1/ns
+ends	/man/1/sh
+ends	/man/1/sh-std
+ends	/man/10/atoi
+ends	/man/10/plan9.ini
+ends	/man/2/arg
+ends	/man/2/draw-image
+ends	/man/2/prof
+ends	/man/2/sys-pipe
+ends	/man/2/sys-utfbytes
+ends	/man/3/cons
+ends	/man/3/draw
+ends	/man/3/flash
+ends	/man/3/pipe
+ends	/man/3/ssl
+ends	/man/8/prep
+ends	/man/9/canvas
+ends	/man/9/menu
+ends	/man/9/text
+endsquare	/man/2/draw-image
+enforce	/man/2/asn1
+enforce	/man/2/security-0intro
+enforce	/man/3/logfs
+enforced	/man/1/calc
+enforced	/man/3/fs
+enforced	/man/3/sd
+engine	/man/1/spree-join
+engine	/man/2/spree
+engine	/man/2/spree-allow
+engine	/man/2/spree-cardlib
+engine	/man/2/spree-gather
+engine	/man/2/spree-objstore
+engine	/man/3/fpga
+engine	/man/3/vga
+engine	/man/4/spree
+engine	/man/8/fpgaload
+engine's	/man/2/spree-objstore
+engine's	/man/8/fpgaload
+engineering	/man/3/fpga
+engineering	/man/8/fpgaload
+engines	/man/2/spree
+engines	/man/2/spree-allow
+engines	/man/2/spree-cardlib
+engines	/man/2/spree-gather
+engines	/man/2/spree-objstore
+engines	/man/4/spree
+england	/man/1/cal
+english	/man/2/translate
+enhance	/man/2/security-0intro
+enhancements	/man/9/1copyright
+enlarged	/man/9/pack
+enodev	/man/10/styxserver
+enomem	/man/10/error
+enomem	/man/10/styxserver
+enonexist	/man/10/devattach
+enonexist	/man/10/styxserver
+enotdir	/man/10/devattach
+enotdir	/man/10/newchan
+enotdir	/man/2/styxservers
+enotfound	/man/2/styxservers
+enquiries	/man/10/odbc
+enquiry	/man/2/json
+enquiry	/man/2/ubfa
+ens	/man/6/man
+ensure	/man/1/charon
+ensure	/man/1/ebook
+ensure	/man/10/allocb
+ensure	/man/10/print
+ensure	/man/10/qio
+ensure	/man/2/geodesy
+ensure	/man/2/security-0intro
+ensure	/man/2/sh
+ensure	/man/2/styxservers
+ensure	/man/2/styxservers-nametree
+ensure	/man/3/boot
+ensure	/man/8/rdbgsrv
+ensure	/man/9/listbox
+ensure	/man/9/see
+ensures	/man/10/allocb
+ensures	/man/10/devattach
+ensures	/man/10/odbc
+ensures	/man/2/secstore
+ensures	/man/2/security-0intro
+ensures	/man/3/cons
+ensures	/man/4/keysrv
+ensuring	/man/1/ftree
+ensuring	/man/1/zeros
+ensuring	/man/10/9load
+ensuring	/man/2/security-0intro
+enter	/man/1/charon
+enter	/man/1/collab-clients
+enter	/man/1/mash-tk
+enter	/man/1/pwd
+enter	/man/1/tkcmd
+enter	/man/1/tktester
+enter	/man/10/9load
+enter	/man/10/odbc
+enter	/man/2/ir
+enter	/man/2/security-0intro
+enter	/man/2/volume
+enter	/man/6/attrdb
+enter	/man/6/keyboard
+enter	/man/8/prep
+enter	/man/9/bind
+enter	/man/9/button
+enter	/man/9/canvas
+enter	/man/9/entry
+enter	/man/9/text
+entered	/man/1/acme
+entered	/man/1/grid-session
+entered	/man/1/sh
+entered	/man/1/tkcmd
+entered	/man/10/9load
+entered	/man/6/keys
+entered	/man/8/changelogin
+entered	/man/8/getauthinfo
+entering	/man/1/acme
+entering	/man/10/kproc
+entering	/man/10/lock
+entering	/man/2/pop3
+entering	/man/2/security-0intro
+entering	/man/4/factotum
+entering	/man/9/bind
+enters	/man/10/9load
+enters	/man/9/menu
+entire	/man/1/acme
+entire	/man/1/brutus
+entire	/man/1/charon
+entire	/man/1/look
+entire	/man/1/mash
+entire	/man/1/sh-regex
+entire	/man/1/sort
+entire	/man/1/units
+entire	/man/2/asn1
+entire	/man/2/draw-0intro
+entire	/man/2/prefab-element
+entire	/man/2/security-0intro
+entire	/man/2/tkclient
+entire	/man/2/wmclient
+entire	/man/3/ftl
+entire	/man/3/ip
+entire	/man/3/kprof
+entire	/man/6/plumbing
+entire	/man/7/db
+entire	/man/8/mkfs
+entire	/man/8/prep
+entire	/man/9/0intro
+entire	/man/9/canvas
+entire	/man/9/checkbutton
+entire	/man/9/grid
+entire	/man/9/menu
+entire	/man/9/pack
+entire	/man/9/radiobutton
+entire	/man/9/see
+entire	/man/9/text
+entirely	/man/1/sh
+entirely	/man/1/sh-tk
+entirely	/man/2/string
+entirely	/man/2/sys-0intro
+entirely	/man/4/memfs
+entirely	/man/9/entry
+entirely	/man/9/text
+entirety	/man/9/see
+entities	/man/10/2c
+entities	/man/2/spki
+entities	/man/3/ip
+entitled	/man/2/math-fp
+entity	/man/2/spki
+entity	/man/2/spki-verifier
+entity	/man/3/ip
+entity	/man/4/namespace
+entrant	/man/1/yacc
+entrant	/man/2/alphabet-intro
+entries	/man/1/0intro
+entries	/man/1/alphabet-fs
+entries	/man/1/calendar
+entries	/man/1/fs
+entries	/man/1/man
+entries	/man/1/sh-alphabet
+entries	/man/10/9load
+entries	/man/10/a.out
+entries	/man/10/dev
+entries	/man/10/devattach
+entries	/man/10/plan9.ini
+entries	/man/2/disks
+entries	/man/2/fsproto
+entries	/man/2/palmfile
+entries	/man/2/pop3
+entries	/man/2/readdir
+entries	/man/2/spree-cardlib
+entries	/man/2/stringinttab
+entries	/man/2/styx
+entries	/man/2/styxservers
+entries	/man/2/sys-0intro
+entries	/man/2/sys-dirread
+entries	/man/2/sys-pipe
+entries	/man/3/draw
+entries	/man/3/fs
+entries	/man/4/ftpfs
+entries	/man/4/kfs
+entries	/man/4/registry
+entries	/man/5/read
+entries	/man/5/stat
+entries	/man/6/colour
+entries	/man/6/dis
+entries	/man/6/font
+entries	/man/6/keys
+entries	/man/6/sbl
+entries	/man/8/applylog
+entries	/man/8/bootpd
+entries	/man/8/changelogin
+entries	/man/8/collabsrv
+entries	/man/8/cs
+entries	/man/8/prep
+entries	/man/9/entry
+entries	/man/9/grab
+entries	/man/9/menu
+entries	/man/9/options
+entry	/man/1/0intro
+entry	/man/1/acme
+entry	/man/1/alphabet-fs
+entry	/man/1/alphabet-main
+entry	/man/1/charon
+entry	/man/1/collab-clients
+entry	/man/1/freq
+entry	/man/1/fs
+entry	/man/1/grid-session
+entry	/man/1/netstat
+entry	/man/1/sh
+entry	/man/1/tkcmd
+entry	/man/1/tktester
+entry	/man/1/webgrab
+entry	/man/10/2l
+entry	/man/10/5coff
+entry	/man/10/5cv
+entry	/man/10/9load
+entry	/man/10/a.out
+entry	/man/10/dev
+entry	/man/10/devattach
+entry	/man/10/dynld
+entry	/man/10/ms2
+entry	/man/10/plan9.ini
+entry	/man/10/styx
+entry	/man/2/attrdb
+entry	/man/2/convcs
+entry	/man/2/dialog
+entry	/man/2/dis
+entry	/man/2/disks
+entry	/man/2/factotum
+entry	/man/2/math-linalg
+entry	/man/2/palmfile
+entry	/man/2/readdir
+entry	/man/2/registries
+entry	/man/2/spree-cardlib
+entry	/man/2/stringinttab
+entry	/man/2/styx
+entry	/man/2/styxservers
+entry	/man/2/sys-0intro
+entry	/man/2/sys-dirread
+entry	/man/2/sys-fauth
+entry	/man/2/tkclient
+entry	/man/2/venti
+entry	/man/2/wmclient
+entry	/man/3/draw
+entry	/man/3/ip
+entry	/man/3/srv9
+entry	/man/4/keysrv
+entry	/man/4/registry
+entry	/man/5/0intro
+entry	/man/5/attach
+entry	/man/5/clunk
+entry	/man/5/open
+entry	/man/5/read
+entry	/man/5/remove
+entry	/man/5/stat
+entry	/man/5/version
+entry	/man/5/walk
+entry	/man/6/attrdb
+entry	/man/6/dis
+entry	/man/6/font
+entry	/man/6/keys
+entry	/man/6/ndb
+entry	/man/6/sbl
+entry	/man/8/applylog
+entry	/man/8/bootpd
+entry	/man/8/changelogin
+entry	/man/8/create
+entry	/man/8/cs
+entry	/man/8/getauthinfo
+entry	/man/8/prep
+entry	/man/9/0intro
+entry	/man/9/canvas
+entry	/man/9/entry
+entry	/man/9/grab
+entry	/man/9/listbox
+entry	/man/9/menu
+entry	/man/9/menubutton
+entry	/man/9/options
+entry	/man/9/text
+entry	/man/9/types
+entry's	/man/1/alphabet-fs
+entry's	/man/1/fs
+entry's	/man/6/keys
+entry's	/man/9/entry
+entry's	/man/9/menu
+entry.attrs	/man/2/palmfile
+entrybox	/man/1/tktester
+entrycget	/man/9/menu
+entryconfigure	/man/9/menu
+entryt	/man/2/dis
+enumcs	/man/2/convcs
+enumerate	/man/3/pnp
+enumerate	/man/6/proto
+enumerated	/man/10/styx
+enumerated	/man/2/asn1
+enumerated	/man/3/pnp
+enumerates	/man/3/pnp
+enumeration	/man/2/ubfa
+enumeration	/man/3/pnp
+env	/man/1/0intro
+env	/man/1/env
+env	/man/1/mash
+env	/man/1/mash-tk
+env	/man/1/sh
+env	/man/1/sh-std
+env	/man/10/error
+env	/man/10/newchan
+env	/man/2/env
+env	/man/2/itslib
+env	/man/2/prefab-compound
+env	/man/2/prefab-element
+env	/man/2/sys-pctl
+env	/man/3/env
+env	/man/3/root
+env	/man/3/srv9
+env	/man/4/9srvfs
+env	/man/4/namespace
+env	/man/6/proto
+env	/man/8/init
+env.b	/man/1/env
+env.b	/man/2/env
+env.defbuiltin	/man/1/mash
+env.m	/man/2/env
+env:ref	/man/2/prefab-element
+enveloping	/man/5/version
+environ	/man/2/prefab-0intro
+environ	/man/2/prefab-compound
+environ	/man/2/prefab-element
+environ	/man/2/prefab-environ
+environ	/man/2/prefab-style
+environ:ref	/man/2/prefab-element
+environent	/man/1/alphabet-fs
+environent	/man/1/fs
+environment	/man/1/0intro
+environment	/man/1/9win
+environment	/man/1/acme
+environment	/man/1/alphabet-fs
+environment	/man/1/diff
+environment	/man/1/emu
+environment	/man/1/env
+environment	/man/1/fs
+environment	/man/1/logon
+environment	/man/1/mash-tk
+environment	/man/1/mk
+environment	/man/1/mux
+environment	/man/1/sendmail
+environment	/man/1/sh
+environment	/man/1/sh-arg
+environment	/man/1/sh-file2chan
+environment	/man/1/sh-std
+environment	/man/1/stack
+environment	/man/1/yacc
+environment	/man/10/0intro
+environment	/man/10/2c
+environment	/man/10/conf
+environment	/man/10/dev
+environment	/man/10/kproc
+environment	/man/10/mk
+environment	/man/10/plan9.ini
+environment	/man/10/sleep
+environment	/man/2/command
+environment	/man/2/daytime
+environment	/man/2/draw-context
+environment	/man/2/env
+environment	/man/2/itslib
+environment	/man/2/keyring-0intro
+environment	/man/2/math-0intro
+environment	/man/2/plumbmsg
+environment	/man/2/prefab-element
+environment	/man/2/prefab-environ
+environment	/man/2/sh
+environment	/man/2/styxservers
+environment	/man/2/sys-chdir
+environment	/man/2/sys-pctl
+environment	/man/3/cons
+environment	/man/3/env
+environment	/man/3/fpga
+environment	/man/3/pointer
+environment	/man/3/root
+environment	/man/3/srv9
+environment	/man/4/9srvfs
+environment	/man/4/acme
+environment	/man/4/import
+environment	/man/4/namespace
+environment	/man/6/dis
+environment	/man/6/keyboard
+environment	/man/6/namespace
+environment	/man/6/proto
+environment	/man/6/utf
+environment	/man/8/create
+environment	/man/8/init
+environment	/man/8/plumber
+environment	/man/8/touchcal
+environment	/man/9/canvas
+environment's	/man/10/print
+environments	/man/8/0intro
+envlist	/man/2/sh
+envoi	/man/1/miniterm
+eo	/man/1/itest
+eoc	/man/2/asn1
+eof	/man/2/bufio
+eof	/man/2/ir
+eof	/man/2/rfc822
+eof	/man/4/spree
+eoff	/man/2/dis
+eol	/man/2/regex
+eopen	/man/2/styxservers
+eor	/man/1/read
+ep	/man/10/5cv
+ep	/man/2/keyring-crypt
+ep72xx	/man/10/5cv
+epb	/man/3/pbus
+eperm	/man/10/dev
+eperm	/man/10/devattach
+eperm	/man/10/styxserver
+eperm	/man/2/styxservers
+ephemeral	/man/2/wmsrv
+epilogue	/man/10/c2l
+epoc	/man/10/5cv
+epoch	/man/1/date
+epoch	/man/1/du
+epoch	/man/1/gettar
+epoch	/man/1/ls
+epoch	/man/1/timestamp
+epoch	/man/10/seconds
+epoch	/man/2/daytime
+epoch	/man/2/palmfile
+epoch	/man/2/rfc822
+epoch	/man/2/sys-millisec
+epoch	/man/2/sys-stat
+epoch	/man/3/cons
+epoch	/man/3/rtc
+epoch	/man/4/keyfs
+epoch	/man/5/stat
+epoch	/man/6/keytext
+epoch	/man/8/applylog
+epoch	/man/8/createsignerkey
+epoch2date	/man/2/spki
+eponymous	/man/1/secstore
+eponymously	/man/2/alphabet-intro
+eq	/man/1/sh-expr
+eq	/man/2/bloomfilter
+eq	/man/2/draw-display
+eq	/man/2/draw-point
+eq	/man/2/draw-rect
+eq	/man/2/ip
+eq	/man/2/json
+eq	/man/2/keyring-ipint
+eq	/man/2/lists
+eq	/man/2/sets
+eq	/man/2/sexprs
+eq	/man/2/spki
+eq	/man/2/ubfa
+eq	/man/2/venti
+eq	/man/2/w3c-uris
+eq	/man/6/man
+eqaddr	/man/2/ether
+eqchan	/man/10/newchan
+eqf	/man/2/w3c-uris
+eqn	/man/6/man
+eqqid	/man/10/newchan
+equal	/man/1/diff
+equal	/man/1/look
+equal	/man/1/sh-expr
+equal	/man/1/sh-test
+equal	/man/1/sort
+equal	/man/10/2c
+equal	/man/10/dynld
+equal	/man/10/memory
+equal	/man/10/newchan
+equal	/man/10/strcat
+equal	/man/10/styx
+equal	/man/2/draw-0intro
+equal	/man/2/draw-image
+equal	/man/2/draw-point
+equal	/man/2/draw-rect
+equal	/man/2/itslib
+equal	/man/2/json
+equal	/man/2/keyring-ipint
+equal	/man/2/lists
+equal	/man/2/math-fp
+equal	/man/2/prefab-element
+equal	/man/2/sexprs
+equal	/man/2/spki
+equal	/man/2/stringinttab
+equal	/man/2/styxservers-nametree
+equal	/man/2/sys-pctl
+equal	/man/2/ubfa
+equal	/man/2/w3c-uris
+equal	/man/3/dbg
+equal	/man/3/ssl
+equal	/man/5/read
+equal	/man/5/version
+equal	/man/5/walk
+equal	/man/6/sexprs
+equal	/man/9/canvas
+equal	/man/9/entry
+equal	/man/9/frame
+equal	/man/9/text
+equality	/man/1/mash
+equality	/man/1/sh-expr
+equality	/man/10/2c
+equalize	/man/6/man
+equally	/man/1/diff
+equally	/man/8/httpd
+equally	/man/9/grid
+equals	/man/2/asn1
+equals	/man/2/draw-image
+equation	/man/6/man
+equations	/man/1/calc
+equations	/man/6/man
+equipment	/man/10/plan9.ini
+equivalence	/man/1/calc
+equivalence	/man/10/c2l
+equivalent	/man/1/acme
+equivalent	/man/1/alphabet-fs
+equivalent	/man/1/charon
+equivalent	/man/1/chgrp
+equivalent	/man/1/cp
+equivalent	/man/1/fs
+equivalent	/man/1/mash
+equivalent	/man/1/sh
+equivalent	/man/1/sh-alphabet
+equivalent	/man/1/sh-regex
+equivalent	/man/1/sh-std
+equivalent	/man/1/sh-string
+equivalent	/man/1/tail
+equivalent	/man/1/tiny
+equivalent	/man/10/devattach
+equivalent	/man/10/kproc
+equivalent	/man/10/malloc
+equivalent	/man/10/memory
+equivalent	/man/10/odbc
+equivalent	/man/10/styx
+equivalent	/man/2/0intro
+equivalent	/man/2/draw-image
+equivalent	/man/2/factotum
+equivalent	/man/2/format
+equivalent	/man/2/geodesy
+equivalent	/man/2/ir
+equivalent	/man/2/keyring-rc4
+equivalent	/man/2/math-export
+equivalent	/man/2/rfc822
+equivalent	/man/2/secstore
+equivalent	/man/2/sexprs
+equivalent	/man/2/sys-dirread
+equivalent	/man/2/sys-read
+equivalent	/man/2/w3c-uris
+equivalent	/man/2/wmsrv
+equivalent	/man/3/cmd
+equivalent	/man/3/draw
+equivalent	/man/3/ftl
+equivalent	/man/3/snarf
+equivalent	/man/4/acme
+equivalent	/man/4/import
+equivalent	/man/5/walk
+equivalent	/man/6/namespace
+equivalent	/man/8/kfscmd
+equivalent	/man/8/signer
+equivalent	/man/8/styxchat
+equivalent	/man/9/canvas
+equivalent	/man/9/entry
+equivalent	/man/9/menu
+equivalent	/man/9/panel
+equivalently	/man/3/logfs
+equivalents	/man/1/units
+equivalents	/man/2/ir
+equivalents	/man/2/wmsrv
+equivalents	/man/3/0intro
+equivelant	/man/1/fc
+era	/man/1/cal
+erase	/man/3/cons
+erase	/man/3/flash
+erase	/man/3/ftl
+erase	/man/4/logfile
+erase	/man/6/keyboard
+erase	/man/8/ftl
+erased	/man/1/avr
+erased	/man/3/flash
+erased	/man/3/ftl
+erasekey	/man/2/secstore
+erases	/man/1/avr
+erases	/man/3/cons
+erasesize	/man/3/ftl
+erasexx	/man/3/flash
+erasing	/man/1/avr
+erasing	/man/1/collab-clients
+erasing	/man/3/cons
+erasing	/man/3/flash
+erf	/man/1/calc
+erf	/man/2/math-elem
+erfc	/man/2/math-elem
+erlang	/man/2/ubfa
+erlang	/man/6/ubfa
+err	/man/1/sh-test
+err	/man/1/yacc
+err	/man/10/dynld
+err	/man/2/asn1
+err	/man/2/attrdb
+err	/man/2/complete
+err	/man/2/debug
+err	/man/2/dhcpclient
+err	/man/2/format
+err	/man/2/ida
+err	/man/2/json
+err	/man/2/keyset
+err	/man/2/palmfile
+err	/man/2/registries
+err	/man/2/security-auth
+err	/man/2/sexprs
+err	/man/2/spki
+err	/man/2/spki-verifier
+err	/man/2/spree-allow
+err	/man/2/styxpersist
+err	/man/2/styxservers
+err	/man/2/ubfa
+err	/man/2/w3c-css
+err	/man/2/w3c-xpointers
+err	/man/2/wmlib
+err	/man/2/xml
+err	/man/3/ip
+err1	/man/2/asn1
+err2	/man/2/asn1
+errdiff	/man/2/imagefile
+errin	/man/2/ip
+errmax	/man/10/error
+errmax	/man/2/keyring-getstring
+errmax	/man/2/msgio
+errmax	/man/5/error
+errmsg	/man/1/sh-file2chan
+errmsg	/man/7/db
+erroneous	/man/10/mk
+erroneous	/man/2/styxservers
+erroneous	/man/3/draw
+erroneously	/man/1/mk
+erroneously	/man/1/wc
+erroneously	/man/10/mk
+error	/man/1/0intro
+error	/man/1/acme
+error	/man/1/charon
+error	/man/1/dd
+error	/man/1/diff
+error	/man/1/fc
+error	/man/1/ftest
+error	/man/1/gettar
+error	/man/1/grep
+error	/man/1/grid-monitor
+error	/man/1/itest
+error	/man/1/listen
+error	/man/1/mash
+error	/man/1/mash-tk
+error	/man/1/miniterm
+error	/man/1/mk
+error	/man/1/mkdir
+error	/man/1/os
+error	/man/1/read
+error	/man/1/sh
+error	/man/1/sh-alphabet
+error	/man/1/sh-arg
+error	/man/1/sh-file2chan
+error	/man/1/sh-sexprs
+error	/man/1/sh-std
+error	/man/1/sh-test
+error	/man/1/time
+error	/man/1/tiny
+error	/man/1/tktester
+error	/man/1/tsort
+error	/man/1/wm-misc
+error	/man/1/yacc
+error	/man/10/5cv
+error	/man/10/9load
+error	/man/10/allocb
+error	/man/10/c2l
+error	/man/10/dev
+error	/man/10/devattach
+error	/man/10/dynld
+error	/man/10/error
+error	/man/10/kproc
+error	/man/10/mk
+error	/man/10/newchan
+error	/man/10/odbc
+error	/man/10/plan9.ini
+error	/man/10/print
+error	/man/10/qio
+error	/man/10/sleep
+error	/man/10/styxserver
+error	/man/2/0intro
+error	/man/2/alphabet-intro
+error	/man/2/arg
+error	/man/2/asn1
+error	/man/2/attrdb
+error	/man/2/bloomfilter
+error	/man/2/bufio
+error	/man/2/cfg
+error	/man/2/complete
+error	/man/2/convcs
+error	/man/2/csv
+error	/man/2/dbm
+error	/man/2/debug
+error	/man/2/dhcpclient
+error	/man/2/dial
+error	/man/2/dis
+error	/man/2/diskblocks
+error	/man/2/draw-0intro
+error	/man/2/draw-image
+error	/man/2/ether
+error	/man/2/exception
+error	/man/2/factotum
+error	/man/2/filter
+error	/man/2/format
+error	/man/2/fsproto
+error	/man/2/ida
+error	/man/2/imagefile
+error	/man/2/ip
+error	/man/2/ir
+error	/man/2/itslib
+error	/man/2/json
+error	/man/2/keyring-auth
+error	/man/2/keyring-getmsg
+error	/man/2/keyring-getstring
+error	/man/2/keyring-ipint
+error	/man/2/keyset
+error	/man/2/lists
+error	/man/2/math-elem
+error	/man/2/mpeg
+error	/man/2/msgio
+error	/man/2/palmfile
+error	/man/2/pop3
+error	/man/2/prof
+error	/man/2/readdir
+error	/man/2/registries
+error	/man/2/scsiio
+error	/man/2/secstore
+error	/man/2/security-auth
+error	/man/2/security-login
+error	/man/2/security-ssl
+error	/man/2/sexprs
+error	/man/2/sh
+error	/man/2/smtp
+error	/man/2/spki
+error	/man/2/spree
+error	/man/2/spree-gather
+error	/man/2/styx
+error	/man/2/styxflush
+error	/man/2/styxpersist
+error	/man/2/styxservers
+error	/man/2/styxservers-nametree
+error	/man/2/sys-0intro
+error	/man/2/sys-byte2char
+error	/man/2/sys-dial
+error	/man/2/sys-dirread
+error	/man/2/sys-export
+error	/man/2/sys-fauth
+error	/man/2/sys-fd2path
+error	/man/2/sys-file2chan
+error	/man/2/sys-fversion
+error	/man/2/sys-iounit
+error	/man/2/sys-open
+error	/man/2/sys-print
+error	/man/2/sys-read
+error	/man/2/sys-utfbytes
+error	/man/2/sys-werrstr
+error	/man/2/tftp
+error	/man/2/translate
+error	/man/2/ubfa
+error	/man/2/venti
+error	/man/2/w3c-css
+error	/man/2/w3c-uris
+error	/man/2/w3c-xpointers
+error	/man/2/wait
+error	/man/2/wmsrv
+error	/man/2/xml
+error	/man/3/0intro
+error	/man/3/boot
+error	/man/3/cap
+error	/man/3/cmd
+error	/man/3/dbg
+error	/man/3/draw
+error	/man/3/flash
+error	/man/3/fpga
+error	/man/3/ftl
+error	/man/3/indir
+error	/man/3/ip
+error	/man/3/mnt
+error	/man/3/mpeg
+error	/man/3/pipe
+error	/man/3/prog
+error	/man/3/sign
+error	/man/3/tls
+error	/man/3/touch
+error	/man/3/usb
+error	/man/4/dossrv
+error	/man/4/factotum
+error	/man/4/iostats
+error	/man/4/keyfs
+error	/man/4/keysrv
+error	/man/4/registry
+error	/man/4/spree
+error	/man/4/trfs
+error	/man/5/0intro
+error	/man/5/attach
+error	/man/5/clunk
+error	/man/5/error
+error	/man/5/open
+error	/man/5/read
+error	/man/5/stat
+error	/man/6/dis
+error	/man/6/image
+error	/man/6/login
+error	/man/7/db
+error	/man/8/applylog
+error	/man/8/collabsrv
+error	/man/8/create
+error	/man/8/cs
+error	/man/8/dhcp
+error	/man/8/ping
+error	/man/8/plumber
+error	/man/8/rdbgsrv
+error	/man/8/styxmon
+error	/man/9/0intro
+error	/man/9/canvas
+error	/man/9/choicebutton
+error	/man/9/destroy
+error	/man/9/entry
+error	/man/9/grid
+error	/man/9/image
+error	/man/9/menu
+error	/man/9/text
+error	/man/9/variable
+error.h	/man/10/0intro
+error.h	/man/10/error
+errorc	/man/2/alphabet-intro
+errored	/man/3/tls
+errorexit	/man/2/sh
+errors	/man/1/acme
+errors	/man/1/charon
+errors	/man/1/limbo
+errors	/man/1/mk
+errors	/man/1/sh-test
+errors	/man/1/tktester
+errors	/man/1/yacc
+errors	/man/10/mk
+errors	/man/2/0intro
+errors	/man/2/asn1
+errors	/man/2/bufio
+errors	/man/2/cfg
+errors	/man/2/convcs
+errors	/man/2/dbm
+errors	/man/2/draw-0intro
+errors	/man/2/geodesy
+errors	/man/2/ip
+errors	/man/2/itslib
+errors	/man/2/mpeg
+errors	/man/2/prefab-style
+errors	/man/2/security-ssl
+errors	/man/2/styx
+errors	/man/2/translate
+errors	/man/2/w3c-css
+errors	/man/2/xml
+errors	/man/3/draw
+errors	/man/3/ip
+errors	/man/3/mnt
+errors	/man/3/usb
+errors	/man/4/acme
+errors	/man/6/namespace
+errors	/man/8/create
+errors	/man/8/kfscmd
+errout	/man/2/ip
+errprint	/man/1/m4
+errstr	/man/1/grid-monitor
+errstr	/man/10/dynld
+errstr	/man/10/print
+es	/man/1/kill
+es	/man/2/dhcpclient
+es	/man/2/names
+es	/man/8/cs
+es	/man/9/menu
+esc	/man/1/acme
+esc	/man/1/wm-sh
+escape	/man/1/xd
+escape	/man/10/acid
+escape	/man/2/filter-slip
+escape	/man/2/sexprs
+escape	/man/2/w3c-uris
+escape	/man/6/keyboard
+escape	/man/6/sexprs
+escape	/man/6/translate
+escape	/man/9/0intro
+escaped	/man/1/mash
+escaped	/man/1/mdb
+escaped	/man/2/tk
+escaped	/man/2/w3c-uris
+escaped	/man/9/0intro
+escaped	/man/9/bind
+escapes	/man/10/atoi
+escapes	/man/2/w3c-uris
+escapes	/man/6/sexprs
+escaping	/man/1/mash
+eschews	/man/6/keyboard
+eseparator	/man/2/prefab-element
+esp	/man/3/ip
+especially	/man/1/charon
+especially	/man/1/tktester
+especially	/man/2/keyring-crypt
+especially	/man/2/security-0intro
+especially	/man/2/smtp
+ess1688	/man/10/plan9.ini
+essential	/man/10/devattach
+essential	/man/2/palmfile
+essential	/man/8/signer
+essentially	/man/2/encoding
+essid	/man/10/plan9.ini
+est	/man/2/daytime
+establish	/man/2/disks
+establish	/man/2/security-0intro
+establish	/man/2/security-login
+establish	/man/2/sys-dial
+establish	/man/2/sys-pctl
+establish	/man/2/wmsrv
+establish	/man/3/ip
+establish	/man/3/plap
+establish	/man/5/attach
+establish	/man/6/login
+establish	/man/8/create
+establish	/man/8/logind
+establish	/man/8/mkfs
+establish	/man/8/signer
+established	/man/10/dev
+established	/man/10/odbc
+established	/man/2/keyring-0intro
+established	/man/2/security-0intro
+established	/man/2/security-auth
+established	/man/2/security-login
+established	/man/2/venti
+established	/man/3/ip
+established	/man/3/plap
+established	/man/3/tls
+established	/man/4/kfs
+established	/man/5/0intro
+established	/man/5/attach
+established	/man/8/rstyxd
+establishes	/man/2/dial
+establishes	/man/2/draw-context
+establishes	/man/2/draw-example
+establishes	/man/2/ir
+establishes	/man/2/keyring-0intro
+establishes	/man/2/plumbmsg
+establishes	/man/2/secstore
+establishes	/man/2/security-auth
+establishes	/man/2/sys-dial
+establishes	/man/2/tkclient
+establishes	/man/2/wmclient
+establishes	/man/7/db
+establishing	/man/2/security-0intro
+establishing	/man/5/0intro
+establishing	/man/5/stat
+et	/man/10/ar
+et4000	/man/3/vga
+etab	/man/2/dis
+etc	/man/1/acme
+etc	/man/1/blur
+etc	/man/1/limbo
+etc	/man/1/sh-alphabet
+etc	/man/1/spree-join
+etc	/man/1/tktester
+etc	/man/1/units
+etc	/man/1/webgrab
+etc	/man/10/2a
+etc	/man/10/2c
+etc	/man/10/2l
+etc	/man/10/dev
+etc	/man/10/iar
+etc	/man/10/print
+etc	/man/10/styx
+etc	/man/10/styxserver
+etc	/man/2/0intro
+etc	/man/2/bufio
+etc	/man/2/debug
+etc	/man/2/draw-0intro
+etc	/man/2/draw-image
+etc	/man/2/ir
+etc	/man/2/json
+etc	/man/2/math-elem
+etc	/man/2/prefab-element
+etc	/man/2/rfc822
+etc	/man/2/spree
+etc	/man/2/styx
+etc	/man/2/styxflush
+etc	/man/2/styxservers
+etc	/man/2/ubfa
+etc	/man/2/w3c-css
+etc	/man/2/w3c-xpointers
+etc	/man/3/ftl
+etc	/man/3/logfs
+etc	/man/3/sd
+etc	/man/4/acme
+etc	/man/5/0intro
+etc	/man/5/stat
+etc	/man/6/plumbing
+etc	/man/8/prep
+etc	/man/9/types
+etext	/man/10/2l
+etext	/man/2/prefab-element
+ether	/man/10/0intro
+ether	/man/10/9load
+ether	/man/10/plan9.ini
+ether	/man/2/ether
+ether	/man/2/ip
+ether	/man/3/ether
+ether	/man/3/indir
+ether	/man/3/ip
+ether	/man/6/ndb
+ether	/man/8/bootpd
+ether	/man/8/dhcp
+ether	/man/8/init
+ether.b	/man/2/ether
+ether.m	/man/2/ether
+ether0	/man/10/plan9.ini
+ether0	/man/2/ether
+ether0	/man/3/ip
+ether0	/man/8/dhcp
+etherexpress	/man/10/plan9.ini
+etherez	/man/10/plan9.ini
+etherfast	/man/10/plan9.ini
+etherlink	/man/10/plan9.ini
+ethern	/man/10/9load
+ethern	/man/2/ether
+ethernet	/man/10/9load
+ethernet	/man/10/plan9.ini
+ethernet	/man/2/ether
+ethernet	/man/3/ether
+ethernet	/man/3/i82365
+ethernet	/man/3/ip
+ethernet	/man/6/ndb
+ethernet	/man/8/bootpd
+ethernetcard	/man/10/plan9.ini
+ethernets	/man/10/plan9.ini
+etherpair	/man/10/plan9.ini
+ethersmc	/man/10/conf
+ethersmc.c	/man/10/conf
+ethersmclink	/man/10/conf
+etherx	/man/10/plan9.ini
+etherxx	/man/10/conf
+etime	/man/2/itslib
+etitle	/man/2/prefab-element
+etrs89	/man/2/geodesy
+etype	/man/2/venti
+euro.8.font	/man/1/acme
+european	/man/2/geodesy
+eval	/man/1/fs
+eval	/man/1/m4
+eval	/man/1/mash
+evaluate	/man/1/alphabet-fs
+evaluate	/man/1/fs
+evaluate	/man/1/mash
+evaluate	/man/1/sh-alphabet
+evaluate	/man/5/walk
+evaluated	/man/1/acme
+evaluated	/man/1/alphabet-fs
+evaluated	/man/1/calc
+evaluated	/man/1/fs
+evaluated	/man/1/mash
+evaluated	/man/1/sh-alphabet
+evaluated	/man/1/sh-expr
+evaluated	/man/1/sh-std
+evaluated	/man/2/math-0intro
+evaluated	/man/2/sys-0intro
+evaluated	/man/2/sys-bind
+evaluated	/man/2/sys-open
+evaluated	/man/4/acme
+evaluated	/man/9/0intro
+evaluated	/man/9/bind
+evaluated	/man/9/menu
+evaluates	/man/1/acme
+evaluates	/man/1/fs
+evaluates	/man/1/m4
+evaluates	/man/1/mash
+evaluates	/man/1/sh-alphabet
+evaluates	/man/1/sh-expr
+evaluates	/man/1/sh-std
+evaluates	/man/10/sleep
+evaluates	/man/3/dbg
+evaluates	/man/5/attach
+evaluates	/man/8/prep
+evaluating	/man/1/fs
+evaluating	/man/1/mash
+evaluating	/man/1/mk
+evaluating	/man/10/mk
+evaluating	/man/2/sys-bind
+evaluating	/man/2/sys-chdir
+evaluating	/man/8/prep
+evaluating	/man/9/scrollbar
+evaluation	/man/1/bind
+evaluation	/man/1/m4
+evaluation	/man/1/sh-alphabet
+evaluation	/man/2/debug
+evaluation	/man/2/math-0intro
+evaluation	/man/2/sys-0intro
+evaluation	/man/2/sys-bind
+evaluation	/man/3/dbg
+evaluator	/man/2/w3c-xpointers
+eve	/man/10/devattach
+eve	/man/10/eve
+eve	/man/3/boot
+eve	/man/3/cap
+eve	/man/3/cons
+eve	/man/3/dynld
+eve	/man/3/sign
+evenly	/man/2/spree-cardlib
+event	/man/1/charon
+event	/man/1/sh-tk
+event	/man/1/timestamp
+event	/man/1/wm
+event	/man/10/intrenable
+event	/man/10/sleep
+event	/man/2/dbm
+event	/man/2/debug
+event	/man/2/dividers
+event	/man/2/popup
+event	/man/2/sys-read
+event	/man/2/tabs
+event	/man/2/tkclient
+event	/man/2/venti
+event	/man/2/wmclient
+event	/man/3/ip
+event	/man/4/acme
+event	/man/4/registry
+event	/man/6/dis
+event	/man/9/1copyright
+event	/man/9/bind
+event	/man/9/canvas
+event	/man/9/destroy
+event	/man/9/frame
+event	/man/9/label
+event	/man/9/panel
+event	/man/9/text
+events	/man/1/cpu
+events	/man/1/emu
+events	/man/1/sh-tk
+events	/man/1/timestamp
+events	/man/1/wm
+events	/man/10/error
+events	/man/2/dividers
+events	/man/2/draw-context
+events	/man/2/draw-display
+events	/man/2/draw-pointer
+events	/man/2/popup
+events	/man/2/spree
+events	/man/2/tk
+events	/man/2/tkclient
+events	/man/2/wmclient
+events	/man/2/wmlib
+events	/man/3/dbg
+events	/man/3/touch
+events	/man/5/0intro
+events	/man/8/plumber
+events	/man/9/bind
+events	/man/9/canvas
+events	/man/9/focus
+events	/man/9/grab
+events	/man/9/scale
+events	/man/9/text
+eventually	/man/10/0intro
+eventually	/man/2/palmfile
+evertical	/man/2/prefab-element
+everybody	/man/1/sh
+everyone	/man/3/fs
+everyone	/man/4/memfs
+everyone	/man/5/0intro
+everything	/man/1/alphabet-abc
+everything	/man/1/alphabet-grid
+everything	/man/1/bind
+everything	/man/1/mk
+everything	/man/1/secstore
+everything	/man/1/sh-alphabet
+everything	/man/10/mk
+everything	/man/2/spki-verifier
+everything	/man/2/sys-bind
+everything	/man/6/namespace
+everything	/man/9/canvas
+everything	/man/9/listbox
+everywhere	/man/3/ftl
+evidence	/man/2/spki-verifier
+ew	/man/2/dividers
+ex	/man/6/man
+exact	/man/1/0intro
+exact	/man/1/look
+exact	/man/1/sh-tk
+exact	/man/1/wm
+exact	/man/2/asn1
+exact	/man/2/tkclient
+exact	/man/2/wmclient
+exact	/man/6/plumbing
+exact	/man/9/button
+exact	/man/9/canvas
+exact	/man/9/checkbutton
+exact	/man/9/choicebutton
+exact	/man/9/entry
+exact	/man/9/frame
+exact	/man/9/label
+exact	/man/9/listbox
+exact	/man/9/menu
+exact	/man/9/menubutton
+exact	/man/9/options
+exact	/man/9/panel
+exact	/man/9/radiobutton
+exact	/man/9/scale
+exact	/man/9/scrollbar
+exact	/man/9/text
+exactly	/man/1/alphabet-fs
+exactly	/man/1/brutus
+exactly	/man/1/fc
+exactly	/man/1/fs
+exactly	/man/1/grid-query
+exactly	/man/1/idea
+exactly	/man/1/look
+exactly	/man/1/m4
+exactly	/man/1/mk
+exactly	/man/1/sh-alphabet
+exactly	/man/1/sh-std
+exactly	/man/1/sh-string
+exactly	/man/1/wm
+exactly	/man/1/wm-misc
+exactly	/man/10/getfields
+exactly	/man/10/mk
+exactly	/man/10/rune
+exactly	/man/10/strcat
+exactly	/man/10/styx
+exactly	/man/2/asn1
+exactly	/man/2/devpointer
+exactly	/man/2/disks
+exactly	/man/2/filter-deflate
+exactly	/man/2/sh
+exactly	/man/2/spki
+exactly	/man/2/styx
+exactly	/man/3/cmd
+exactly	/man/3/tls
+exactly	/man/3/vid
+exactly	/man/4/acme
+exactly	/man/4/factotum
+exactly	/man/5/read
+exactly	/man/5/stat
+exactly	/man/6/dis
+exactly	/man/6/keyboard
+exactly	/man/6/ubfa
+exactly	/man/8/ai2key
+exactly	/man/8/ftl
+exactly	/man/8/getauthinfo
+exactly	/man/9/bind
+exactly	/man/9/grab
+exactly	/man/9/grid
+exactly	/man/9/options
+exactly	/man/9/pack
+exactly	/man/9/scrollbar
+exactly	/man/9/text
+examination	/man/1/charon
+examine	/man/1/mash-make
+examine	/man/1/stack
+examine	/man/10/plan9.ini
+examine	/man/2/sys-byte2char
+examine	/man/2/xml
+examine	/man/3/prog
+examine	/man/4/acme
+examined	/man/1/acme
+examined	/man/2/fsproto
+examined	/man/5/0intro
+examined	/man/6/plumbing
+examined	/man/8/applylog
+examined	/man/9/0intro
+examined	/man/9/grid
+examines	/man/10/allocb
+examines	/man/10/strcat
+examines	/man/2/sys-utfbytes
+examining	/man/10/2c
+examining	/man/10/acid
+examining	/man/10/error
+exc	/man/2/exception
+exc	/man/6/dis
+excecuted	/man/9/0intro
+exceed	/man/2/dbm
+exceed	/man/2/diskblocks
+exceed	/man/8/prep
+exceeded	/man/1/charon
+exceeded	/man/10/qio
+exceeded	/man/2/rfc822
+exceeded	/man/3/ip
+exceeds	/man/1/charon
+exceeds	/man/10/dmainit
+exceeds	/man/10/qio
+exceeds	/man/2/diskblocks
+exceeds	/man/2/sys-byte2char
+exceeds	/man/2/sys-utfbytes
+exceeds	/man/3/mpeg
+except	/man/1/acme
+except	/man/1/alphabet-fs
+except	/man/1/cp
+except	/man/1/diff
+except	/man/1/fs
+except	/man/1/ftree
+except	/man/1/listen
+except	/man/1/m4
+except	/man/1/mash
+except	/man/1/mk
+except	/man/1/sh
+except	/man/1/sh-file2chan
+except	/man/1/sh-regex
+except	/man/1/sh-std
+except	/man/1/sh-tk
+except	/man/1/tiny
+except	/man/1/tktester
+except	/man/1/units
+except	/man/1/wm-sh
+except	/man/1/xd
+except	/man/10/2c
+except	/man/10/acid
+except	/man/10/allocb
+except	/man/10/ar
+except	/man/10/devattach
+except	/man/10/getfields
+except	/man/10/iar
+except	/man/10/malloc
+except	/man/10/master
+except	/man/10/mk
+except	/man/10/print
+except	/man/10/sleep
+except	/man/10/styx
+except	/man/10/styxserver
+except	/man/2/arg
+except	/man/2/asn1
+except	/man/2/dbm
+except	/man/2/debug
+except	/man/2/dis
+except	/man/2/draw-image
+except	/man/2/encoding
+except	/man/2/exception
+except	/man/2/ip
+except	/man/2/keyring-getmsg
+except	/man/2/math-0intro
+except	/man/2/math-linalg
+except	/man/2/msgio
+except	/man/2/prefab-0intro
+except	/man/2/prefab-style
+except	/man/2/readdir
+except	/man/2/sets
+except	/man/2/sh
+except	/man/2/spree
+except	/man/2/spree-cardlib
+except	/man/2/string
+except	/man/2/styx
+except	/man/2/styxservers
+except	/man/2/sys-bind
+except	/man/2/sys-dial
+except	/man/2/sys-pctl
+except	/man/2/sys-print
+except	/man/2/sys-stat
+except	/man/2/w3c-uris
+except	/man/2/wmsrv
+except	/man/3/cmd
+except	/man/3/flash
+except	/man/3/ip
+except	/man/3/prog
+except	/man/3/usb
+except	/man/4/acme
+except	/man/4/keyfs
+except	/man/4/kfs
+except	/man/5/0intro
+except	/man/5/stat
+except	/man/6/man
+except	/man/6/proto
+except	/man/6/utf
+except	/man/8/ai2key
+except	/man/8/applylog
+except	/man/8/create
+except	/man/8/styxchat
+except	/man/8/svc
+except	/man/9/bind
+except	/man/9/canvas
+except	/man/9/frame
+except	/man/9/grab
+except	/man/9/menu
+except	/man/9/options
+except	/man/9/pack
+except	/man/9/text
+exception	/man/1/0intro
+exception	/man/1/acme
+exception	/man/1/kill
+exception	/man/1/mash
+exception	/man/1/mdb
+exception	/man/1/sh
+exception	/man/1/sh-arg
+exception	/man/1/sh-expr
+exception	/man/1/sh-std
+exception	/man/1/sh-string
+exception	/man/1/sh-tk
+exception	/man/10/error
+exception	/man/10/styx
+exception	/man/2/0intro
+exception	/man/2/alphabet-intro
+exception	/man/2/arg
+exception	/man/2/command
+exception	/man/2/dis
+exception	/man/2/exception
+exception	/man/2/json
+exception	/man/2/math-0intro
+exception	/man/2/math-fp
+exception	/man/2/sh
+exception	/man/2/sys-pipe
+exception	/man/2/w3c-uris
+exception	/man/2/xml
+exception	/man/3/pipe
+exception	/man/3/prog
+exception	/man/5/0intro
+exception	/man/6/dis
+exception	/man/9/text
+exception's	/man/1/0intro
+exception.b	/man/2/exception
+exception.m	/man/2/exception
+exceptional	/man/5/flush
+exceptionally	/man/10/devattach
+exceptionally	/man/2/factotum
+exceptionally	/man/2/keyring-crypt
+exceptionally	/man/3/dbg
+exceptions	/man/1/math-misc
+exceptions	/man/1/sh
+exceptions	/man/1/sh-csv
+exceptions	/man/1/sh-sexprs
+exceptions	/man/1/sh-std
+exceptions	/man/1/tiny
+exceptions	/man/10/plan9.ini
+exceptions	/man/10/xalloc
+exceptions	/man/2/0intro
+exceptions	/man/2/dis
+exceptions	/man/2/draw-image
+exceptions	/man/2/exception
+exceptions	/man/2/math-0intro
+exceptions	/man/2/math-fp
+exceptions	/man/2/sh
+exceptions	/man/2/spree-allow
+exceptions	/man/2/styx
+exceptions	/man/2/styxservers
+exceptions	/man/2/sys-0intro
+exceptions	/man/3/prog
+exceptions	/man/6/dis
+exceptions	/man/9/canvas
+excess	/man/1/sh-file2chan
+excess	/man/10/print
+excess	/man/10/qio
+excessive	/man/1/deb
+excessive	/man/4/kfs
+exchange	/man/1/alphabet-abc
+exchange	/man/1/alphabet-grid
+exchange	/man/1/sh-csv
+exchange	/man/10/styx
+exchange	/man/2/draw-context
+exchange	/man/2/factotum
+exchange	/man/2/imagefile
+exchange	/man/2/keyring-0intro
+exchange	/man/2/keyring-getstring
+exchange	/man/2/msgio
+exchange	/man/2/registries
+exchange	/man/2/security-0intro
+exchange	/man/2/security-login
+exchange	/man/2/security-ssl
+exchange	/man/2/styx
+exchange	/man/2/wait
+exchange	/man/3/audio
+exchange	/man/3/dbg
+exchange	/man/3/i2c
+exchange	/man/3/tls
+exchange	/man/4/import
+exchange	/man/5/0intro
+exchange	/man/6/json
+exchange	/man/6/login
+exchange	/man/6/sexprs
+exchange	/man/6/ubfa
+exchange	/man/8/collabsrv
+exchange	/man/8/logind
+exchange	/man/8/styxchat
+exchanged	/man/2/keyring-getmsg
+exchanged	/man/2/msgio
+exchanged	/man/2/plumbmsg
+exchanged	/man/2/security-0intro
+exchanged	/man/3/plap
+exchanged	/man/7/dbsrv
+exchanged	/man/8/rstyxd
+exchanger	/man/6/ndb
+exchanger	/man/8/dns
+exchanges	/man/8/styxchat
+exchanging	/man/1/diff
+exchanging	/man/2/keyring-0intro
+exchanging	/man/2/security-0intro
+exchanging	/man/2/sexprs
+exchanging	/man/5/0intro
+exchanging	/man/6/sexprs
+exclamation	/man/1/bind
+exclamation	/man/2/draw-context
+exclamation	/man/2/factotum
+exclamation	/man/2/keyring-getmsg
+exclamation	/man/2/msgio
+exclamation	/man/2/wmsrv
+exclamation	/man/3/indir
+exclamation	/man/4/factotum
+exclamation	/man/9/0intro
+exclude	/man/1/alphabet-fs
+exclude	/man/1/fs
+exclude	/man/10/lock
+exclude	/man/10/plan9.ini
+exclude	/man/8/applylog
+excluded	/man/1/alphabet-fs
+excluded	/man/1/fs
+excluded	/man/5/0intro
+excludes	/man/1/cp
+excludes	/man/10/2c
+excludes	/man/10/lock
+excludes	/man/2/secstore
+excludes	/man/5/version
+excluding	/man/1/webgrab
+excluding	/man/2/asn1
+excluding	/man/2/wmclient
+excluding	/man/6/proto
+excluding	/man/6/users
+exclusion	/man/10/qlock
+exclusion	/man/2/lock
+exclusive	/man/1/chmod
+exclusive	/man/1/ls
+exclusive	/man/10/ar
+exclusive	/man/10/dev
+exclusive	/man/10/master
+exclusive	/man/10/qlock
+exclusive	/man/2/keyring-ipint
+exclusive	/man/2/lock
+exclusive	/man/2/spree-cardlib
+exclusive	/man/2/sys-0intro
+exclusive	/man/2/sys-export
+exclusive	/man/2/sys-open
+exclusive	/man/2/sys-stat
+exclusive	/man/3/audio
+exclusive	/man/3/mpeg
+exclusive	/man/3/srv
+exclusive	/man/4/factotum
+exclusive	/man/4/lockfs
+exclusive	/man/5/0intro
+exclusive	/man/5/open
+exclusive	/man/5/stat
+exclusive	/man/6/proto
+exclusive	/man/8/collabsrv
+exclusive	/man/8/kfscmd
+exclusive	/man/9/grab
+exclusively	/man/10/qio
+exclusively	/man/3/dbg
+exclusively	/man/9/0intro
+exe	/man/10/conf
+exec	/man/1/alphabet-fs
+exec	/man/1/fs
+exec	/man/10/a.out
+exec	/man/2/sh
+exec	/man/3/cmd
+exec	/man/3/ip
+execprint	/man/2/sh
+executable	/man/1/deb
+executable	/man/1/emu
+executable	/man/1/ftest
+executable	/man/1/ls
+executable	/man/1/sh
+executable	/man/1/sh-file2chan
+executable	/man/1/tiny
+executable	/man/10/2l
+executable	/man/10/5coff
+executable	/man/10/5cv
+executable	/man/10/9load
+executable	/man/10/a.out
+executable	/man/10/acid
+executable	/man/10/conf
+executable	/man/10/dynld
+executable	/man/10/inm
+executable	/man/10/kprof
+executable	/man/10/ksize
+executable	/man/10/kstrip
+executable	/man/10/ms2
+executable	/man/10/srclist
+executable	/man/3/cmd
+executable	/man/3/root
+executable	/man/6/dis
+executable	/man/6/sbl
+executable	/man/7/dbsrv
+executable's	/man/10/5cv
+executables	/man/10/5cv
+executables	/man/10/ntsrv
+executables	/man/4/namespace
+execute	/man/1/0intro
+execute	/man/1/acme
+execute	/man/1/alphabet-abc
+execute	/man/1/alphabet-grid
+execute	/man/1/chmod
+execute	/man/1/cpu
+execute	/man/1/emu
+execute	/man/1/fc
+execute	/man/1/ftest
+execute	/man/1/grid-ns
+execute	/man/1/ls
+execute	/man/1/mash
+execute	/man/1/mash-tk
+execute	/man/1/mdb
+execute	/man/1/mk
+execute	/man/1/os
+execute	/man/1/sh
+execute	/man/1/tiny
+execute	/man/10/acid
+execute	/man/10/devattach
+execute	/man/10/mk
+execute	/man/10/styxserver
+execute	/man/2/draw-0intro
+execute	/man/2/regex
+execute	/man/2/scsiio
+execute	/man/2/sys-fauth
+execute	/man/2/sys-self
+execute	/man/2/sys-stat
+execute	/man/3/cmd
+execute	/man/3/fs
+execute	/man/3/prog
+execute	/man/3/sd
+execute	/man/4/grid-cpu
+execute	/man/4/memfs
+execute	/man/5/0intro
+execute	/man/5/attach
+execute	/man/5/open
+execute	/man/6/dis
+execute	/man/9/0intro
+execute	/man/9/canvas
+execute	/man/9/menu
+executed	/man/1/acme
+executed	/man/1/alphabet-fs
+executed	/man/1/cprof
+executed	/man/1/deb
+executed	/man/1/fs
+executed	/man/1/grid-session
+executed	/man/1/listen
+executed	/man/1/mash
+executed	/man/1/mash-tk
+executed	/man/1/mk
+executed	/man/1/ns
+executed	/man/1/nsbuild
+executed	/man/1/sh
+executed	/man/1/sh-std
+executed	/man/1/tiny
+executed	/man/1/tkcmd
+executed	/man/1/toolbar
+executed	/man/10/a.out
+executed	/man/10/delay
+executed	/man/10/mk
+executed	/man/10/odbc
+executed	/man/2/prof
+executed	/man/2/sh
+executed	/man/3/boot
+executed	/man/3/prof
+executed	/man/3/tls
+executed	/man/4/grid-cpu
+executed	/man/9/0intro
+executed	/man/9/bind
+executed	/man/9/menu
+executed	/man/9/options
+executed	/man/9/text
+executes	/man/1/acme
+executes	/man/1/cpu
+executes	/man/1/mash
+executes	/man/1/rcmd
+executes	/man/1/sh
+executes	/man/1/sh-std
+executes	/man/1/time
+executes	/man/1/tiny
+executes	/man/1/toolbar
+executes	/man/2/command
+executes	/man/2/sh
+executes	/man/3/prog
+executes	/man/4/grid-cpu
+executes	/man/8/prep
+executes	/man/8/rstyxd
+executese	/man/2/regex
+executing	/man/1/acme
+executing	/man/1/mash
+executing	/man/1/mk
+executing	/man/1/mprof
+executing	/man/1/sh
+executing	/man/1/sh-alphabet
+executing	/man/1/sh-std
+executing	/man/1/stack
+executing	/man/1/toolbar
+executing	/man/10/dynld
+executing	/man/10/kprof
+executing	/man/10/lock
+executing	/man/10/mk
+executing	/man/2/debug
+executing	/man/2/format
+executing	/man/2/sys-fd2path
+executing	/man/2/sys-self
+executing	/man/3/flash
+executing	/man/4/spree
+executing	/man/5/0intro
+executing	/man/9/menu
+execution	/man/1/0intro
+execution	/man/1/alphabet-abc
+execution	/man/1/alphabet-fs
+execution	/man/1/alphabet-grid
+execution	/man/1/cprof
+execution	/man/1/deb
+execution	/man/1/emu
+execution	/man/1/fs
+execution	/man/1/grid-session
+execution	/man/1/mash
+execution	/man/1/mk
+execution	/man/1/rcmd
+execution	/man/1/sh
+execution	/man/1/sleep
+execution	/man/1/time
+execution	/man/1/tiny
+execution	/man/10/9load
+execution	/man/10/acid
+execution	/man/10/mk
+execution	/man/10/odbc
+execution	/man/2/0intro
+execution	/man/2/debug
+execution	/man/2/draw-context
+execution	/man/2/prof
+execution	/man/2/sh
+execution	/man/2/sys-0intro
+execution	/man/2/tk
+execution	/man/3/cmd
+execution	/man/3/prog
+execution	/man/4/grid-cpu
+execution	/man/6/dis
+execution	/man/6/utf
+execution	/man/7/dbsrv
+execution	/man/8/rstyxd
+execution	/man/8/svc
+execution	/man/9/options
+execvp	/man/3/cmd
+exercise	/man/1/cook
+exercises	/man/2/draw-example
+exhaust	/man/6/dis
+exhausted	/man/1/fc
+exhausted	/man/1/sh-expr
+exhausted	/man/10/print
+exhausted	/man/2/0intro
+exhausted	/man/3/prog
+exhaustively	/man/2/asn1
+exist	/man/1/0intro
+exist	/man/1/acme
+exist	/man/1/alphabet-fs
+exist	/man/1/ar
+exist	/man/1/calendar
+exist	/man/1/charon
+exist	/man/1/cprof
+exist	/man/1/fs
+exist	/man/1/ftest
+exist	/man/1/mash-make
+exist	/man/1/mk
+exist	/man/1/mprof
+exist	/man/1/os
+exist	/man/1/prof
+exist	/man/1/sh
+exist	/man/1/sh-alphabet
+exist	/man/1/tiny
+exist	/man/1/tktester
+exist	/man/1/touch
+exist	/man/1/wm-misc
+exist	/man/10/conf
+exist	/man/10/devattach
+exist	/man/10/iar
+exist	/man/10/master
+exist	/man/10/mk
+exist	/man/10/srclist
+exist	/man/10/styxserver
+exist	/man/2/0intro
+exist	/man/2/draw-0intro
+exist	/man/2/draw-image
+exist	/man/2/ether
+exist	/man/2/format
+exist	/man/2/palmfile
+exist	/man/2/styxservers
+exist	/man/2/styxservers-nametree
+exist	/man/2/sys-bind
+exist	/man/2/sys-open
+exist	/man/3/draw
+exist	/man/3/env
+exist	/man/3/sd
+exist	/man/4/dbfs
+exist	/man/4/factotum
+exist	/man/4/keyfs
+exist	/man/4/logfile
+exist	/man/4/namespace
+exist	/man/4/ramfile
+exist	/man/8/create
+exist	/man/8/getauthinfo
+exist	/man/8/mkfs
+exist	/man/8/prep
+exist	/man/8/signer
+exist	/man/8/svc
+exist	/man/9/button
+exist	/man/9/canvas
+exist	/man/9/checkbutton
+exist	/man/9/choicebutton
+exist	/man/9/entry
+exist	/man/9/label
+exist	/man/9/listbox
+exist	/man/9/menu
+exist	/man/9/menubutton
+exist	/man/9/panel
+exist	/man/9/radiobutton
+exist	/man/9/scale
+exist	/man/9/scrollbar
+exist	/man/9/text
+existed	/man/1/cprof
+existed	/man/2/wmsrv
+existed	/man/4/ramfile
+existence	/man/10/print
+existent	/man/1/acme
+existent	/man/1/sh-tk
+existent	/man/2/draw-pointer
+existing	/man/1/acme
+existing	/man/1/bind
+existing	/man/1/brutus
+existing	/man/1/chmod
+existing	/man/1/cp
+existing	/man/1/cprof
+existing	/man/1/gettar
+existing	/man/1/itest
+existing	/man/1/mk
+existing	/man/1/mv
+existing	/man/1/passwd
+existing	/man/1/sh-alphabet
+existing	/man/1/sh-tk
+existing	/man/1/tiny
+existing	/man/1/uuencode
+existing	/man/10/conf
+existing	/man/10/dev
+existing	/man/10/mk
+existing	/man/10/newchan
+existing	/man/10/ntsrv
+existing	/man/2/alphabet-intro
+existing	/man/2/bufio
+existing	/man/2/dbm
+existing	/man/2/dhcpclient
+existing	/man/2/draw-font
+existing	/man/2/filter
+existing	/man/2/format
+existing	/man/2/geodesy
+existing	/man/2/popup
+existing	/man/2/prefab-compound
+existing	/man/2/prefab-element
+existing	/man/2/security-ssl
+existing	/man/2/sets
+existing	/man/2/sh
+existing	/man/2/spree-cardlib
+existing	/man/2/string
+existing	/man/2/styxservers
+existing	/man/2/sys-0intro
+existing	/man/2/sys-bind
+existing	/man/2/sys-open
+existing	/man/2/tk
+existing	/man/2/translate
+existing	/man/3/flash
+existing	/man/3/ftl
+existing	/man/3/ip
+existing	/man/3/pnp
+existing	/man/3/ssl
+existing	/man/4/archfs
+existing	/man/4/keysrv
+existing	/man/4/kfs
+existing	/man/4/lockfs
+existing	/man/4/logfile
+existing	/man/4/registry
+existing	/man/4/trfs
+existing	/man/5/0intro
+existing	/man/5/open
+existing	/man/5/stat
+existing	/man/5/walk
+existing	/man/6/namespace
+existing	/man/6/plumbing
+existing	/man/6/proto
+existing	/man/6/sbl
+existing	/man/8/changelogin
+existing	/man/8/mangaload
+existing	/man/8/touchcal
+existing	/man/9/0intro
+existing	/man/9/1copyright
+existing	/man/9/bind
+existing	/man/9/canvas
+existing	/man/9/grid
+existing	/man/9/image
+existing	/man/9/text
+exists	/man/1/blur
+exists	/man/1/charon
+exists	/man/1/collab
+exists	/man/1/cp
+exists	/man/1/disdep
+exists	/man/1/ftest
+exists	/man/1/listen
+exists	/man/1/mkdir
+exists	/man/1/rcmd
+exists	/man/1/sh
+exists	/man/1/sh-alphabet
+exists	/man/1/tiny
+exists	/man/1/webgrab
+exists	/man/10/styxserver
+exists	/man/2/dial
+exists	/man/2/dict
+exists	/man/2/hash
+exists	/man/2/palmfile
+exists	/man/2/spree
+exists	/man/2/styxservers
+exists	/man/2/sys-dial
+exists	/man/2/sys-open
+exists	/man/2/translate
+exists	/man/3/ip
+exists	/man/3/prog
+exists	/man/3/rtc
+exists	/man/4/kfs
+exists	/man/5/open
+exists	/man/6/keys
+exists	/man/7/db
+exists	/man/8/changelogin
+exists	/man/8/create
+exists	/man/8/mkfs
+exists	/man/8/prep
+exists	/man/8/svc
+exists	/man/9/canvas
+exists	/man/9/image
+exists	/man/9/options
+exists	/man/9/text
+exit	/man/1/0intro
+exit	/man/1/acme
+exit	/man/1/alphabet-fs
+exit	/man/1/bind
+exit	/man/1/blur
+exit	/man/1/calc
+exit	/man/1/cmp
+exit	/man/1/collab-clients
+exit	/man/1/diff
+exit	/man/1/fc
+exit	/man/1/fs
+exit	/man/1/ftest
+exit	/man/1/ftree
+exit	/man/1/grep
+exit	/man/1/grid-monitor
+exit	/man/1/grid-register
+exit	/man/1/keyboard
+exit	/man/1/look
+exit	/man/1/mash
+exit	/man/1/mash-tk
+exit	/man/1/mk
+exit	/man/1/mkdir
+exit	/man/1/mux
+exit	/man/1/os
+exit	/man/1/read
+exit	/man/1/sh
+exit	/man/1/sh-regex
+exit	/man/1/sh-std
+exit	/man/1/sh-tk
+exit	/man/1/tktester
+exit	/man/1/wm
+exit	/man/10/mk
+exit	/man/10/panic
+exit	/man/2/0intro
+exit	/man/2/command
+exit	/man/2/dial
+exit	/man/2/security-auth
+exit	/man/2/sh
+exit	/man/2/timers
+exit	/man/2/tkclient
+exit	/man/2/volume
+exit	/man/2/wait
+exit	/man/2/wmclient
+exit	/man/2/wmlib
+exit	/man/2/wmsrv
+exit	/man/3/cmd
+exit	/man/3/srv
+exit	/man/4/palmsrv
+exit	/man/8/applylog
+exit	/man/8/create
+exit	/man/8/prep
+exit	/man/9/grab
+exited	/man/2/wait
+exited	/man/3/prog
+exiting	/man/1/limbo
+exiting	/man/2/wmlib
+exiting	/man/3/prog
+exits	/man/1/listen
+exits	/man/1/mk
+exits	/man/1/netkey
+exits	/man/1/os
+exits	/man/1/sh
+exits	/man/1/wm-sh
+exits	/man/10/mk
+exits	/man/10/print
+exits	/man/2/dbm
+exits	/man/2/debug
+exits	/man/2/draw-context
+exits	/man/2/itslib
+exits	/man/2/plumbmsg
+exits	/man/2/wait
+exits	/man/3/prog
+exits	/man/3/srv
+exits	/man/4/iostats
+exits	/man/9/grab
+exotica	/man/1/units
+exp	/man/1/calc
+exp	/man/1/sh-expr
+exp	/man/1/yacc
+exp	/man/2/debug
+exp	/man/2/keyring-0intro
+exp	/man/2/keyring-ipint
+exp	/man/2/keyring-sha1
+exp	/man/2/math-elem
+exp	/man/2/spki
+exp	/man/6/json
+exp	/man/6/keytext
+expand	/man/1/deb
+expand	/man/1/mk
+expand	/man/10/mk
+expand	/man/2/debug
+expand	/man/2/filepat
+expand	/man/2/spree-cardlib
+expand	/man/9/pack
+expandable	/man/9/pack
+expanded	/man/1/deb
+expanded	/man/1/m4
+expanded	/man/1/mk
+expanded	/man/1/sh-alphabet
+expanded	/man/1/tiny
+expanded	/man/1/uuencode
+expanded	/man/10/0intro
+expanded	/man/10/mk
+expanded	/man/2/debug
+expanded	/man/2/w3c-xpointers
+expanded	/man/6/namespace
+expanded	/man/8/create
+expanded	/man/9/pack
+expanding	/man/1/alphabet-main
+expanding	/man/1/mk
+expanding	/man/10/mk
+expanding	/man/2/prefab-element
+expands	/man/1/acme
+expands	/man/2/debug
+expansion	/man/1/acme
+expansion	/man/1/m4
+expansion	/man/1/tiny
+expansion	/man/4/acme
+expansion	/man/9/pack
+expasync	/man/2/security-auth
+expasync	/man/2/sys-export
+expect	/man/1/acme
+expect	/man/10/devattach
+expect	/man/2/math-0intro
+expect	/man/2/wmsrv
+expect	/man/5/version
+expect	/man/8/createsignerkey
+expect	/man/9/listbox
+expected	/man/1/acme
+expected	/man/1/dd
+expected	/man/1/math-misc
+expected	/man/1/sh
+expected	/man/1/sh-alphabet
+expected	/man/2/math-elem
+expected	/man/2/sh
+expected	/man/2/spree-gather
+expected	/man/4/acme
+expected	/man/8/applylog
+expected	/man/9/grid
+expected	/man/9/send
+expects	/man/1/acme
+expects	/man/1/charon
+expects	/man/1/logon
+expects	/man/1/sh-alphabet
+expects	/man/2/drawmux
+expects	/man/2/string
+expects	/man/2/virgil
+expects	/man/3/pbus
+expects	/man/4/registry
+expects	/man/8/rstyxd
+expense	/man/6/dis
+experiment	/man/10/plan9.ini
+experimental	/man/1/acme
+experimental	/man/1/sh-alphabet
+experimental	/man/10/plan9.ini
+experimental	/man/2/alphabet-intro
+experimental	/man/3/sign
+expid	/man/2/sys-export
+expiration	/man/2/keyring-0intro
+expiration	/man/2/keyring-certtostr
+expiration	/man/2/keyring-sha1
+expiration	/man/2/security-0intro
+expiration	/man/4/keyfs
+expiration	/man/8/changelogin
+expire	/man/2/timers
+expire	/man/4/keyfs
+expire	/man/8/getauthinfo
+expired	/man/2/spki-verifier
+expired	/man/2/timers
+expired	/man/4/keyfs
+expires	/man/2/timers
+expires	/man/6/keys
+expires	/man/8/createsignerkey
+expiry	/man/2/timers
+expiry	/man/4/keyfs
+expiry	/man/6/keytext
+expiry	/man/8/changelogin
+expiry	/man/8/createsignerkey
+expiry	/man/8/getauthinfo
+expl	/man/2/tk
+explain	/man/1/mk
+explain	/man/10/mk
+explained	/man/2/0intro
+explained	/man/2/sys-chdir
+explaining	/man/8/prep
+explains	/man/1/acme
+explains	/man/2/dhcpclient
+explanation	/man/2/convcs
+explanation	/man/2/sys-file2chan
+explanation	/man/4/spree
+explanation	/man/6/man
+explanation	/man/9/canvas
+explanatory	/man/2/sh
+explicit	/man/1/acme
+explicit	/man/1/alphabet-fs
+explicit	/man/1/fs
+explicit	/man/1/mash
+explicit	/man/1/tiny
+explicit	/man/2/0intro
+explicit	/man/2/asn1
+explicit	/man/2/dial
+explicit	/man/2/draw-image
+explicit	/man/2/sys-dial
+explicit	/man/2/sys-open
+explicit	/man/2/sys-read
+explicit	/man/3/ip
+explicit	/man/5/0intro
+explicit	/man/5/stat
+explicit	/man/6/sexprs
+explicit	/man/8/rip
+explicit	/man/9/canvas
+explicitly	/man/1/acme
+explicitly	/man/1/calc
+explicitly	/man/1/grid-session
+explicitly	/man/1/mash
+explicitly	/man/1/sh
+explicitly	/man/10/2c
+explicitly	/man/10/2l
+explicitly	/man/10/5cv
+explicitly	/man/10/iar
+explicitly	/man/10/qio
+explicitly	/man/2/0intro
+explicitly	/man/2/asn1
+explicitly	/man/2/cfg
+explicitly	/man/2/prefab-element
+explicitly	/man/2/sets
+explicitly	/man/2/styxservers
+explicitly	/man/2/styxservers-nametree
+explicitly	/man/3/dbg
+explicitly	/man/3/ip
+explicitly	/man/3/plap
+explicitly	/man/4/acme
+explicitly	/man/4/ftpfs
+explicitly	/man/4/registry
+explicitly	/man/5/0intro
+explicitly	/man/6/sbl
+explicitly	/man/6/sexprs
+explicitly	/man/8/applylog
+explicitly	/man/8/prep
+explicitly	/man/8/register
+explicitly	/man/9/1copyright
+explicitly	/man/9/canvas
+explicitly	/man/9/grid
+explicity	/man/1/tktester
+explore	/man/1/wm-misc
+explorer	/man/1/miniterm
+expm1	/man/2/math-elem
+expmod	/man/1/sh-expr
+expmod	/man/2/keyring-ipint
+exponent	/man/10/print
+exponent	/man/2/math-elem
+exponent	/man/2/string
+exponent	/man/2/sys-print
+exponent	/man/8/ai2key
+exponential	/man/2/math-elem
+exponential	/man/6/login
+exponentiation	/man/1/sh-expr
+exponentiation	/man/1/units
+export	/man/1/9win
+export	/man/1/alphabet-main
+export	/man/1/grid-ns
+export	/man/1/grid-session
+export	/man/1/listen
+export	/man/10/2l
+export	/man/10/devattach
+export	/man/10/dynld
+export	/man/2/drawmux
+export	/man/2/math-export
+export	/man/2/security-0intro
+export	/man/2/security-auth
+export	/man/2/sys-export
+export	/man/3/srv9
+export	/man/4/9srvfs
+export	/man/4/export
+export	/man/4/grid-cpu
+export	/man/4/import
+export	/man/4/registry
+export	/man/8/collabsrv
+export	/man/8/styxmon
+export	/man/8/svc
+export.b	/man/4/export
+exportdir	/man/2/sys-export
+exported	/man/1/grid-ns
+exported	/man/1/mk
+exported	/man/1/rcmd
+exported	/man/1/sh
+exported	/man/10/2l
+exported	/man/10/dynld
+exported	/man/10/mk
+exported	/man/2/dis
+exported	/man/2/drawmux
+exported	/man/2/sys-export
+exported	/man/3/dynld
+exported	/man/4/grid-cpu
+exported	/man/4/keysrv
+exported	/man/6/dis
+exported	/man/8/collabsrv
+exported	/man/8/rdbgsrv
+exportfs	/man/3/srv9
+exportfs	/man/4/import
+exportfs	/man/6/namespace
+exportfs.c	/man/2/sys-export
+exporting	/man/1/grid-ns
+exporting	/man/1/grid-register
+exporting	/man/4/9srvfs
+exporting	/man/4/grid-cpu
+exportpath	/man/1/rcmd
+exports	/man/1/9win
+exports	/man/1/alphabet-main
+exports	/man/1/cpu
+exports	/man/1/grid-ns
+exports	/man/1/grid-session
+exports	/man/10/dev
+exports	/man/10/dynld
+exports	/man/10/odbc
+exports	/man/4/keysrv
+exports	/man/8/collabsrv
+exporttab	/man/10/dynld
+expose	/man/2/command
+expose	/man/4/dbfs
+expr	/man/1/fc
+expr	/man/1/fs
+expr	/man/1/mash
+expr	/man/1/mdb
+expr	/man/1/sh
+expr	/man/1/sh-alphabet
+expr	/man/1/sh-expr
+expr	/man/1/sh-mload
+expr	/man/1/sh-std
+expr	/man/1/sh-string
+expr	/man/1/sh-tk
+expr	/man/10/acid
+expr	/man/2/sh
+expr	/man/2/w3c-css
+expr	/man/2/w3c-xpointers
+expr.b	/man/1/sh-expr
+express	/man/2/draw-display
+expressed	/man/1/acme
+expressed	/man/1/prof
+expressed	/man/1/units
+expressed	/man/10/a.out
+expressed	/man/2/draw-display
+expressed	/man/2/draw-screen
+expressed	/man/2/styxservers
+expressed	/man/3/ether
+expressed	/man/3/ip
+expressed	/man/3/pnp
+expressed	/man/3/rtc
+expressed	/man/6/sexprs
+expressed	/man/8/cs
+expressed	/man/8/httpd
+expresses	/man/2/spki
+expression	/man/1/acme
+expression	/man/1/alphabet-abc
+expression	/man/1/alphabet-fs
+expression	/man/1/alphabet-grid
+expression	/man/1/alphabet-main
+expression	/man/1/calc
+expression	/man/1/deb
+expression	/man/1/fc
+expression	/man/1/fs
+expression	/man/1/grep
+expression	/man/1/m4
+expression	/man/1/mash
+expression	/man/1/mdb
+expression	/man/1/mk
+expression	/man/1/sh-alphabet
+expression	/man/1/sh-expr
+expression	/man/1/sh-regex
+expression	/man/1/sh-sexprs
+expression	/man/1/tiny
+expression	/man/10/2c
+expression	/man/10/acid
+expression	/man/10/c2l
+expression	/man/10/mk
+expression	/man/2/debug
+expression	/man/2/format
+expression	/man/2/regex
+expression	/man/2/sexprs
+expression	/man/2/spki
+expression	/man/2/spki-verifier
+expression	/man/2/w3c-xpointers
+expression	/man/3/dbg
+expression	/man/4/acme
+expression	/man/4/factotum
+expression	/man/6/plumbing
+expression	/man/6/regexp
+expression	/man/6/sbl
+expression	/man/6/sexprs
+expression	/man/8/prep
+expression's	/man/1/sh-alphabet
+expressions	/man/1/acme
+expressions	/man/1/calc
+expressions	/man/1/limbo
+expressions	/man/1/mash
+expressions	/man/1/mdb
+expressions	/man/1/mk
+expressions	/man/1/sh-alphabet
+expressions	/man/1/sh-regex
+expressions	/man/1/sh-sexprs
+expressions	/man/10/acid
+expressions	/man/10/mk
+expressions	/man/2/format
+expressions	/man/2/math-0intro
+expressions	/man/2/regex
+expressions	/man/2/sexprs
+expressions	/man/2/spki
+expressions	/man/2/w3c-xpointers
+expressions	/man/6/regexp
+expressions	/man/6/sexprs
+expressions	/man/8/prep
+exps	/man/2/debug
+expwait	/man/2/sys-export
+expwait	/man/3/srv9
+exs	/man/2/w3c-css
+ext	/man/10/2l
+extant	/man/10/acid
+extant	/man/2/draw-image
+extant	/man/8/prep
+extend	/man/1/calc
+extend	/man/1/disdep
+extend	/man/1/m4
+extend	/man/10/eve
+extend	/man/2/complete
+extend	/man/2/dhcpclient
+extend	/man/4/factotum
+extended	/man/1/0intro
+extended	/man/1/acme
+extended	/man/1/limbo
+extended	/man/10/9load
+extended	/man/2/complete
+extended	/man/2/disks
+extended	/man/2/spki
+extended	/man/2/w3c-css
+extended	/man/5/stat
+extended	/man/6/dis
+extended	/man/6/sexprs
+extended	/man/8/prep
+extended	/man/9/grid
+extended	/man/9/listbox
+extending	/man/2/draw-image
+extending	/man/5/version
+extending	/man/8/prep
+extends	/man/1/acme
+extends	/man/1/charon
+extends	/man/2/keyring-sha1
+extends	/man/6/regexp
+extends	/man/9/canvas
+extends	/man/9/grid
+extends	/man/9/listbox
+extends	/man/9/text
+extensible	/man/2/xml
+extension	/man/1/asm
+extension	/man/1/charon
+extension	/man/1/cook
+extension	/man/1/idea
+extension	/man/1/mash
+extension	/man/10/2c
+extension	/man/10/a.out
+extension	/man/2/complete
+extension	/man/2/spki
+extension	/man/2/spree
+extension	/man/2/tabs
+extension	/man/4/dossrv
+extension	/man/6/dis
+extensions	/man/1/limbo
+extensions	/man/10/2c
+extensions	/man/2/rfc822
+extensions	/man/3/cons
+extensions	/man/4/dossrv
+extensive	/man/10/9load
+extent	/man/2/dis
+extent	/man/2/draw-0intro
+extent	/man/6/dis
+extent	/man/9/canvas
+extents	/man/8/prep
+exterior	/man/9/options
+extern	/man/10/2c
+extern	/man/10/dynld
+extern	/man/10/kbdputc
+extern	/man/10/styxserver
+external	/man/1/acme
+external	/man/1/ebook
+external	/man/1/sh
+external	/man/1/sh-alphabet
+external	/man/1/sh-file2chan
+external	/man/1/tktester
+external	/man/1/yacc
+external	/man/10/2c
+external	/man/10/dynld
+external	/man/2/alphabet-intro
+external	/man/2/asn1
+external	/man/2/bufio
+external	/man/2/dis
+external	/man/2/draw-display
+external	/man/2/imagefile
+external	/man/2/keyring-0intro
+external	/man/2/math-export
+external	/man/2/sh
+external	/man/2/spree
+external	/man/2/spree-allow
+external	/man/2/styxpersist
+external	/man/2/styxservers
+external	/man/2/tk
+external	/man/2/xml
+external	/man/3/fpga
+external	/man/3/tv
+external	/man/4/factotum
+external	/man/4/grid-cpu
+external	/man/4/memfs
+external	/man/5/0intro
+external	/man/6/colour
+external	/man/6/dis
+external	/man/6/font
+external	/man/6/image
+external	/man/6/utf
+external	/man/7/dbsrv
+external	/man/8/dns
+external	/man/8/fpgaload
+external	/man/9/grid
+external	/man/9/pack
+externally	/man/1/sh
+externally	/man/10/ar
+externally	/man/2/alphabet-intro
+externally	/man/2/draw-display
+externally	/man/2/spki
+externally	/man/3/fpga
+extra	/man/1/acme
+extra	/man/1/calc
+extra	/man/1/fc
+extra	/man/1/grid-monitor
+extra	/man/1/secstore
+extra	/man/10/ar
+extra	/man/10/dynld
+extra	/man/2/secstore
+extra	/man/2/spree-cardlib
+extra	/man/3/ether
+extra	/man/3/flash
+extra	/man/3/tls
+extra	/man/4/acme
+extra	/man/6/colour
+extra	/man/6/keyboard
+extra	/man/6/keytext
+extra	/man/6/man
+extra	/man/9/grid
+extra	/man/9/options
+extra	/man/9/pack
+extra	/man/9/text
+extract	/man/1/ar
+extract	/man/1/gettar
+extract	/man/1/secstore
+extract	/man/1/sh-arg
+extract	/man/1/strings
+extract	/man/10/iar
+extract	/man/10/odbc
+extract	/man/10/styx
+extract	/man/2/convcs
+extract	/man/2/disks
+extract	/man/2/print
+extract	/man/2/sexprs
+extracted	/man/1/ar
+extracted	/man/1/gettar
+extracted	/man/1/sh-arg
+extracted	/man/10/iar
+extracted	/man/2/debug
+extracted	/man/2/dis
+extracted	/man/2/draw-image
+extracted	/man/2/math-fp
+extracted	/man/8/mkfs
+extracting	/man/2/styxservers
+extracts	/man/1/brutus
+extracts	/man/1/gettar
+extracts	/man/2/disks
+extracts	/man/2/keyring-gensk
+extrapolates	/man/9/text
+ezd	/man/9/canvas
+f.f1.b2	/man/1/tktester
+f.fmenu	/man/1/tktester
+f.x	/man/2/bloomfilter
+fa	/man/6/json
+fa	/man/6/sexprs
+fa310	/man/10/plan9.ini
+fa311	/man/10/plan9.ini
+fa312	/man/10/plan9.ini
+fa410tx	/man/10/plan9.ini
+fabs	/man/2/math-fp
+fabs	/man/2/math-linalg
+facedown	/man/2/spree-cardlib
+faces	/man/1/wm-misc
+faces	/man/2/spree-cardlib
+facfd	/man/2/factotum
+facilitate	/man/10/a.out
+facilitating	/man/2/venti
+facilities	/man/1/deb
+facilities	/man/1/sh-file2chan
+facilities	/man/1/sh-string
+facilities	/man/1/wm-sh
+facilities	/man/2/debug
+facilities	/man/2/draw-0intro
+facilities	/man/2/prefab-0intro
+facilities	/man/2/spree-cardlib
+facilities	/man/2/spree-gather
+facilities	/man/3/draw
+facilities	/man/3/prof
+facilities	/man/3/prog
+facilities	/man/9/canvas
+facility	/man/1/sh-csv
+facility	/man/1/sh-sexprs
+facility	/man/1/sh-test
+facility	/man/1/wm
+facility	/man/2/asn1
+facility	/man/2/itslib
+facility	/man/2/spree
+facing	/man/2/spree-cardlib
+factor	/man/1/math-misc
+factor	/man/1/wm-misc
+factor	/man/2/draw-image
+factor	/man/3/floppy
+factor	/man/6/colour
+factor	/man/9/canvas
+factor.b	/man/1/math-misc
+factorial	/man/1/calc
+factorial	/man/1/fc
+factoring	/man/2/math-0intro
+factors	/man/1/math-misc
+factors	/man/9/canvas
+factory	/man/10/plan9.ini
+factotum	/man/1/bind
+factotum	/man/1/secstore
+factotum	/man/2/factotum
+factotum	/man/2/newns
+factotum	/man/2/secstore
+factotum	/man/2/styxpersist
+factotum	/man/3/srv9
+factotum	/man/4/factotum
+factotum	/man/4/import
+factotum	/man/6/keytext
+factotum	/man/8/ai2key
+factotum.b	/man/2/factotum
+factotum.m	/man/2/factotum
+facts	/man/3/logfs
+fads	/man/3/vid
+fahrenheit	/man/1/units
+fail	/man/1/0intro
+fail	/man/1/mash
+fail	/man/1/sh-tk
+fail	/man/1/tiny
+fail	/man/10/odbc
+fail	/man/2/0intro
+fail	/man/2/alphabet-intro
+fail	/man/2/asn1
+fail	/man/2/debug
+fail	/man/2/keyring-getstring
+fail	/man/2/msgio
+fail	/man/2/scsiio
+fail	/man/2/sh
+fail	/man/2/spree-cardlib
+fail	/man/2/wmsrv
+fail	/man/3/tls
+fail	/man/4/ftpfs
+fail	/man/5/open
+fail	/man/5/remove
+fail	/man/8/prep
+fail:eof	/man/1/read
+fail:error	/man/1/read
+fail:usage	/man/2/arg
+failed	/man/1/alphabet-abc
+failed	/man/1/alphabet-grid
+failed	/man/1/sh
+failed	/man/1/sh-test
+failed	/man/2/factotum
+failed	/man/2/format
+failed	/man/2/keyring-auth
+failed	/man/2/palmfile
+failed	/man/2/security-auth
+failed	/man/2/spki-verifier
+failed	/man/2/styxpersist
+failed	/man/2/styxservers
+failed	/man/3/dbg
+failed	/man/4/factotum
+failed	/man/4/keyfs
+failed	/man/5/0intro
+failed	/man/5/clunk
+failed	/man/5/open
+failed	/man/5/walk
+failing	/man/1/listen
+failing	/man/1/sh
+failing	/man/10/lock
+failing	/man/2/arg
+failing	/man/2/dhcpclient
+failing	/man/3/cmd
+failing	/man/3/fs
+failing	/man/5/error
+failing	/man/6/ndb
+fails	/man/1/bind
+fails	/man/1/limbo
+fails	/man/1/sh
+fails	/man/1/sh-regex
+fails	/man/1/sh-std
+fails	/man/1/tiny
+fails	/man/1/webgrab
+fails	/man/10/2c
+fails	/man/10/c2l
+fails	/man/2/alphabet-intro
+fails	/man/2/disks
+fails	/man/2/draw-display
+fails	/man/2/env
+fails	/man/2/factotum
+fails	/man/2/keyring-auth
+fails	/man/2/plumbmsg
+fails	/man/2/prof
+fails	/man/2/scsiio
+fails	/man/2/smtp
+fails	/man/2/spree-cardlib
+fails	/man/2/spree-gather
+fails	/man/2/styx
+fails	/man/2/sys-bind
+fails	/man/2/sys-read
+fails	/man/2/sys-werrstr
+fails	/man/2/wmlib
+fails	/man/3/cmd
+fails	/man/3/ds
+fails	/man/3/flash
+fails	/man/3/fpga
+fails	/man/3/ip
+fails	/man/3/plap
+fails	/man/3/sign
+fails	/man/3/ssl
+fails	/man/5/open
+fails	/man/5/remove
+fails	/man/5/stat
+fails	/man/7/db
+fails	/man/8/prep
+failure	/man/1/charon
+failure	/man/2/alphabet-intro
+failure	/man/2/asn1
+failure	/man/2/dbm
+failure	/man/2/draw-0intro
+failure	/man/2/factotum
+failure	/man/2/keyset
+failure	/man/2/pop3
+failure	/man/2/scsiio
+failure	/man/2/secstore
+failure	/man/2/smtp
+failure	/man/2/spki-verifier
+failure	/man/2/spree-allow
+failure	/man/2/styxservers-nametree
+failure	/man/2/sys-bind
+failure	/man/2/sys-chdir
+failure	/man/2/sys-fversion
+failure	/man/2/sys-pipe
+failure	/man/2/sys-stat
+failure	/man/3/boot
+failure	/man/3/draw
+failure	/man/3/logfs
+failure	/man/4/ftpfs
+failure	/man/5/0intro
+failure	/man/5/error
+failure	/man/7/db
+fair	/man/1/charon
+fair	/man/2/scsiio
+fairly	/man/1/mash-tk
+faithful	/man/1/man
+fall	/man/10/c2l
+fallbacks	/man/1/ebook
+falls	/man/10/qio
+false	/man/1/sh-expr
+false	/man/1/sh-regex
+false	/man/1/sh-std
+false	/man/1/sh-tk
+false	/man/10/acid
+false	/man/10/dmainit
+false	/man/10/sleep
+false	/man/2/asn1
+false	/man/2/bloomfilter
+false	/man/2/draw-display
+false	/man/2/draw-image
+false	/man/2/ip
+false	/man/2/json
+false	/man/2/keyring-sha1
+false	/man/2/palmfile
+false	/man/2/secstore
+false	/man/2/sh
+false	/man/2/styxservers
+false	/man/2/w3c-uris
+false	/man/6/json
+false	/man/9/checkbutton
+false	/man/9/grid
+false	/man/9/options
+false	/man/9/pack
+false	/man/9/radiobutton
+false	/man/9/text
+false	/man/9/types
+familiar	/man/1/0intro
+familiar	/man/1/charon
+familiar	/man/1/units
+familiarity	/man/2/styxservers
+fan	/man/8/collabsrv
+fappinfodirty	/man/2/palmfile
+farm	/man/1/alphabet-abc
+farm	/man/1/alphabet-grid
+fars	/man/9/1copyright
+farther	/man/9/canvas
+farther	/man/9/entry
+farther	/man/9/listbox
+farther	/man/9/text
+fashion	/man/1/mk
+fashion	/man/1/units
+fashion	/man/1/wm-misc
+fashion	/man/10/mk
+fashion	/man/2/asn1
+fashion	/man/4/logfile
+fashion	/man/9/canvas
+fashion	/man/9/grab
+fast	/man/1/fortune
+fast	/man/1/mux
+fast	/man/10/plan9.ini
+fast	/man/2/ir
+fast16	/man/10/plan9.ini
+fast4	/man/10/plan9.ini
+fast8	/man/10/plan9.ini
+faster	/man/1/charon
+faster	/man/1/cp
+faster	/man/1/echo
+faster	/man/1/emu
+faster	/man/10/5cv
+faster	/man/10/c2l
+faster	/man/2/math-0intro
+faster	/man/2/security-0intro
+faster	/man/3/cons
+faster	/man/4/logfile
+fastest	/man/4/iostats
+fat	/man/10/9load
+fat	/man/2/disks
+fat	/man/3/fs
+fat	/man/4/dossrv
+fat	/man/8/prep
+fat12	/man/8/prep
+fat16	/man/10/9load
+fat16	/man/8/prep
+fat32	/man/4/dossrv
+fat32	/man/8/prep
+fatal	/man/1/sh-test
+fatal	/man/10/print
+fatal	/man/2/0intro
+fatal	/man/2/itslib
+fatal	/man/2/math-fp
+fatal	/man/2/w3c-css
+fatal	/man/2/xml
+fatal	/man/3/arch
+fatal	/man/3/tls
+fault	/man/2/ida
+fault	/man/3/prog
+faults	/man/3/prog
+fauth	/man/2/factotum
+fauth	/man/2/sys-fauth
+fauth	/man/2/sys-fversion
+fauth	/man/2/sys-stat
+fauth	/man/5/attach
+fauth	/man/5/version
+favour	/man/1/alphabet-fs
+favour	/man/1/fs
+favour	/man/1/sh
+favour	/man/1/sh-std
+favour	/man/3/ip
+favour	/man/8/applylog
+fbackup	/man/2/palmfile
+fc	/man/1/calc
+fc	/man/1/fc
+fcall	/man/10/styx
+fcall.h	/man/5/attach
+fcallfmt	/man/10/styx
+fcallfmt.c	/man/10/styx
+fcp	/man/1/cp
+fcp.b	/man/1/cp
+fd	/man/1/alphabet-abc
+fd	/man/1/alphabet-fs
+fd	/man/1/alphabet-grid
+fd	/man/1/alphabet-main
+fd	/man/1/sh
+fd	/man/1/sh-alphabet
+fd	/man/1/yacc
+fd	/man/10/9load
+fd	/man/10/dynld
+fd	/man/10/newchan
+fd	/man/10/plan9.ini
+fd	/man/10/print
+fd	/man/10/styxserver
+fd	/man/2/bufio
+fd	/man/2/csv
+fd	/man/2/dhcpclient
+fd	/man/2/dial
+fd	/man/2/diskblocks
+fd	/man/2/disks
+fd	/man/2/draw-context
+fd	/man/2/draw-display
+fd	/man/2/drawmux
+fd	/man/2/factotum
+fd	/man/2/imagefile
+fd	/man/2/itslib
+fd	/man/2/keyring-auth
+fd	/man/2/keyring-getmsg
+fd	/man/2/keyring-getstring
+fd	/man/2/keyring-sha1
+fd	/man/2/msgio
+fd	/man/2/print
+fd	/man/2/readdir
+fd	/man/2/registries
+fd	/man/2/rfc822
+fd	/man/2/scsiio
+fd	/man/2/security-auth
+fd	/man/2/security-ssl
+fd	/man/2/styx
+fd	/man/2/styxconv
+fd	/man/2/styxflush
+fd	/man/2/styxpersist
+fd	/man/2/styxservers
+fd	/man/2/sys-0intro
+fd	/man/2/sys-bind
+fd	/man/2/sys-dial
+fd	/man/2/sys-dirread
+fd	/man/2/sys-dup
+fd	/man/2/sys-export
+fd	/man/2/sys-fauth
+fd	/man/2/sys-fd2path
+fd	/man/2/sys-fversion
+fd	/man/2/sys-iounit
+fd	/man/2/sys-open
+fd	/man/2/sys-pipe
+fd	/man/2/sys-print
+fd	/man/2/sys-read
+fd	/man/2/sys-seek
+fd	/man/2/sys-stat
+fd	/man/2/tftp
+fd	/man/2/venti
+fd	/man/2/wait
+fd	/man/3/dup
+fd	/man/3/prog
+fd	/man/3/root
+fd	/man/3/srv9
+fd	/man/3/ssl
+fd	/man/3/tls
+fd	/man/4/iostats
+fd	/man/7/db
+fd.fd	/man/2/sys-export
+fd0	/man/1/sh
+fd0	/man/10/9load
+fd0ctl	/man/3/floppy
+fd0disk	/man/3/ds
+fd0disk	/man/3/floppy
+fd0disk	/man/8/prep
+fd1	/man/1/sh
+fd1ctl	/man/3/floppy
+fd1disk	/man/3/floppy
+fd2ctl	/man/3/floppy
+fd2disk	/man/3/floppy
+fd2path	/man/1/pwd
+fd2path	/man/2/names
+fd2path	/man/2/sys-0intro
+fd2path	/man/2/sys-fd2path
+fd2path	/man/2/workdir
+fd3ctl	/man/3/floppy
+fd3disk	/man/3/floppy
+fdim	/man/2/math-fp
+fdisk	/man/8/prep
+fdnum	/man/1/sh-std
+fds	/man/2/sys-pipe
+fdtochan	/man/10/newchan
+feasible	/man/10/styxserver
+feasible	/man/2/math-0intro
+feature	/man/1/acme
+feature	/man/10/plan9.ini
+feature	/man/2/draw-example
+feature	/man/2/print
+feature	/man/2/sh
+feature	/man/2/spree-cardlib
+features	/man/1/mdb
+features	/man/1/tiny
+features	/man/1/tkcmd
+features	/man/2/math-0intro
+features	/man/3/logfs
+features	/man/4/dbfs
+features	/man/7/db
+features	/man/9/frame
+features	/man/9/text
+fee	/man/2/keyring-crypt
+fee	/man/9/1copyright
+feed	/man/1/secstore
+feed	/man/6/keyboard
+feed	/man/6/sexprs
+feedkey	/man/4/factotum
+feels	/man/1/0intro
+feels	/man/2/tkclient
+feels	/man/2/wmclient
+fell	/man/3/kprof
+ferries	/man/2/factotum
+fetch	/man/1/webgrab
+fetch	/man/10/inb
+fetch	/man/2/dbm
+fetch	/man/2/dhcpclient
+fetch	/man/2/exception
+fetch	/man/2/popup
+fetch	/man/2/secstore
+fetch	/man/2/security-auth
+fetch	/man/2/tftp
+fetch	/man/4/ftpfs
+fetched	/man/1/mdb
+fetched	/man/2/secstore
+fetched	/man/2/styxservers
+fetched	/man/3/draw
+fetches	/man/1/mdb
+fetches	/man/1/webgrab
+fetches	/man/2/attrdb
+fetches	/man/2/tftp
+fetching	/man/10/allocb
+fetching	/man/10/print
+fetching	/man/2/sexprs
+fetching	/man/2/sys-print
+fetchwdata	/man/1/sh-file2chan
+fewer	/man/2/palmfile
+fewer	/man/6/colour
+fewer	/man/9/text
+ff	/man/2/ir
+ff	/man/6/utf
+ffff	/man/1/strings
+ffff	/man/2/ip
+ffffffffffff	/man/3/ether
+ffile	/man/1/sh-arg
+ffile	/man/2/arg
+fg	/man/9/options
+fgid	/man/2/styxservers
+fgname	/man/2/styxservers
+fgrp	/man/10/newchan
+fgui	/man/4/factotum
+fhtml	/man/2/stringinttab
+fi	/man/2/dis
+fibonacci	/man/1/math-misc
+fibonacci.b	/man/1/math-misc
+fid	/man/1/sh-file2chan
+fid	/man/10/styx
+fid	/man/10/styxserver
+fid	/man/2/bufio-chanfill
+fid	/man/2/styx
+fid	/man/2/styxservers
+fid	/man/2/sys-file2chan
+fid	/man/5/0intro
+fid	/man/5/attach
+fid	/man/5/clunk
+fid	/man/5/flush
+fid	/man/5/open
+fid	/man/5/read
+fid	/man/5/remove
+fid	/man/5/stat
+fid	/man/5/walk
+fid	/man/8/styxchat
+fid's	/man/2/styxservers
+fiddly	/man/2/styxservers
+fids	/man/10/styxserver
+fids	/man/2/styx
+fids	/man/2/styxservers
+fids	/man/5/0intro
+fids	/man/5/version
+field's	/man/2/format
+fieldname	/man/2/rfc822
+fields	/man/1/alphabet-fs
+fields	/man/1/fs
+fields	/man/1/gettar
+fields	/man/1/ls
+fields	/man/1/mux
+fields	/man/1/sh-string
+fields	/man/1/tiny
+fields	/man/1/tktester
+fields	/man/10/2c
+fields	/man/10/ar
+fields	/man/10/c2l
+fields	/man/10/devattach
+fields	/man/10/dynld
+fields	/man/10/getfields
+fields	/man/10/odbc
+fields	/man/10/parsecmd
+fields	/man/10/styx
+fields	/man/10/styxserver
+fields	/man/2/crc
+fields	/man/2/csv
+fields	/man/2/daytime
+fields	/man/2/dhcpclient
+fields	/man/2/draw-screen
+fields	/man/2/factotum
+fields	/man/2/format
+fields	/man/2/keyring-certtostr
+fields	/man/2/palmfile
+fields	/man/2/plumbmsg
+fields	/man/2/prefab-element
+fields	/man/2/print
+fields	/man/2/prof
+fields	/man/2/rfc822
+fields	/man/2/spki
+fields	/man/2/sys-stat
+fields	/man/2/w3c-uris
+fields	/man/3/arch
+fields	/man/3/cmd
+fields	/man/3/cons
+fields	/man/3/draw
+fields	/man/3/gpio
+fields	/man/3/ip
+fields	/man/3/logfs
+fields	/man/3/pnp
+fields	/man/3/prog
+fields	/man/3/tls
+fields	/man/4/acme
+fields	/man/4/factotum
+fields	/man/4/kfs
+fields	/man/5/0intro
+fields	/man/5/stat
+fields	/man/6/dis
+fields	/man/6/font
+fields	/man/6/keytext
+fields	/man/6/proto
+fields	/man/6/sbl
+fields	/man/7/db
+fields	/man/8/ai2key
+fields	/man/8/create
+fields	/man/8/styxchat
+fields	/man/9/menu
+fifo	/man/10/qlock
+fifth	/man/3/kprof
+fifth	/man/8/create
+fifth	/man/8/prep
+fifth	/man/9/text
+figure	/man/2/security-0intro
+figures	/man/4/iostats
+fildes	/man/1/yacc
+fildes	/man/2/styxservers-nametree
+fildes	/man/2/sys-dup
+file	/man/1/0intro
+file	/man/1/acme
+file	/man/1/alphabet-fs
+file	/man/1/alphabet-main
+file	/man/1/ar
+file	/man/1/asm
+file	/man/1/auplay
+file	/man/1/avr
+file	/man/1/basename
+file	/man/1/bind
+file	/man/1/blur
+file	/man/1/brutus
+file	/man/1/calc
+file	/man/1/calendar
+file	/man/1/cat
+file	/man/1/charon
+file	/man/1/chgrp
+file	/man/1/chmod
+file	/man/1/cleanname
+file	/man/1/cmp
+file	/man/1/collab
+file	/man/1/collab-clients
+file	/man/1/comm
+file	/man/1/cook
+file	/man/1/cp
+file	/man/1/cprof
+file	/man/1/crypt
+file	/man/1/dd
+file	/man/1/deb
+file	/man/1/diff
+file	/man/1/disdep
+file	/man/1/du
+file	/man/1/ebook
+file	/man/1/emu
+file	/man/1/filename
+file	/man/1/fmt
+file	/man/1/fortune
+file	/man/1/freq
+file	/man/1/fs
+file	/man/1/ftest
+file	/man/1/ftree
+file	/man/1/gettar
+file	/man/1/grep
+file	/man/1/grid-session
+file	/man/1/gzip
+file	/man/1/idea
+file	/man/1/itest
+file	/man/1/kill
+file	/man/1/limbo
+file	/man/1/listen
+file	/man/1/logon
+file	/man/1/logwindow
+file	/man/1/look
+file	/man/1/ls
+file	/man/1/m4
+file	/man/1/man
+file	/man/1/mash
+file	/man/1/mash-make
+file	/man/1/mash-tk
+file	/man/1/math-misc
+file	/man/1/mc
+file	/man/1/mdb
+file	/man/1/mk
+file	/man/1/mprof
+file	/man/1/mux
+file	/man/1/mv
+file	/man/1/netkey
+file	/man/1/netstat
+file	/man/1/ns
+file	/man/1/nsbuild
+file	/man/1/p
+file	/man/1/passwd
+file	/man/1/prof
+file	/man/1/rcmd
+file	/man/1/read
+file	/man/1/rm
+file	/man/1/secstore
+file	/man/1/sh
+file	/man/1/sh-alphabet
+file	/man/1/sh-arg
+file	/man/1/sh-file2chan
+file	/man/1/sh-std
+file	/man/1/sort
+file	/man/1/stack
+file	/man/1/stream
+file	/man/1/strings
+file	/man/1/sum
+file	/man/1/tail
+file	/man/1/tcs
+file	/man/1/tee
+file	/man/1/timestamp
+file	/man/1/tiny
+file	/man/1/tktester
+file	/man/1/touch
+file	/man/1/uniq
+file	/man/1/units
+file	/man/1/uuencode
+file	/man/1/vacget
+file	/man/1/wc
+file	/man/1/webgrab
+file	/man/1/wish
+file	/man/1/wm
+file	/man/1/wm-misc
+file	/man/1/wm-sh
+file	/man/1/xd
+file	/man/1/yacc
+file	/man/1/zeros
+file	/man/10/0intro
+file	/man/10/2a
+file	/man/10/2c
+file	/man/10/2l
+file	/man/10/5coff
+file	/man/10/5cv
+file	/man/10/9load
+file	/man/10/a.out
+file	/man/10/acid
+file	/man/10/ar
+file	/man/10/c2l
+file	/man/10/conf
+file	/man/10/dev
+file	/man/10/devattach
+file	/man/10/dynld
+file	/man/10/eve
+file	/man/10/iar
+file	/man/10/inm
+file	/man/10/intrenable
+file	/man/10/kproc
+file	/man/10/kprof
+file	/man/10/ksize
+file	/man/10/kstrip
+file	/man/10/master
+file	/man/10/mk
+file	/man/10/ms2
+file	/man/10/newchan
+file	/man/10/odbc
+file	/man/10/parsecmd
+file	/man/10/plan9.ini
+file	/man/10/print
+file	/man/10/qio
+file	/man/10/readnum
+file	/man/10/seconds
+file	/man/10/srclist
+file	/man/10/styx
+file	/man/10/styxserver
+file	/man/2/0intro
+file	/man/2/arg
+file	/man/2/asn1
+file	/man/2/attrdb
+file	/man/2/bufio
+file	/man/2/bufio-chanfill
+file	/man/2/cfg
+file	/man/2/command
+file	/man/2/complete
+file	/man/2/convcs
+file	/man/2/crc
+file	/man/2/csv
+file	/man/2/daytime
+file	/man/2/dbm
+file	/man/2/debug
+file	/man/2/devpointer
+file	/man/2/dhcpclient
+file	/man/2/dial
+file	/man/2/dis
+file	/man/2/diskblocks
+file	/man/2/disks
+file	/man/2/draw-context
+file	/man/2/draw-display
+file	/man/2/draw-font
+file	/man/2/drawmux
+file	/man/2/ether
+file	/man/2/factotum
+file	/man/2/filepat
+file	/man/2/filter
+file	/man/2/filter-deflate
+file	/man/2/format
+file	/man/2/fsproto
+file	/man/2/ida
+file	/man/2/imagefile
+file	/man/2/ir
+file	/man/2/itslib
+file	/man/2/keyring-0intro
+file	/man/2/keyring-auth
+file	/man/2/keyring-sha1
+file	/man/2/keyset
+file	/man/2/mpeg
+file	/man/2/msgio
+file	/man/2/names
+file	/man/2/newns
+file	/man/2/palmfile
+file	/man/2/plumbmsg
+file	/man/2/print
+file	/man/2/prof
+file	/man/2/pslib
+file	/man/2/readdir
+file	/man/2/registries
+file	/man/2/rfc822
+file	/man/2/scsiio
+file	/man/2/secstore
+file	/man/2/security-auth
+file	/man/2/security-ssl
+file	/man/2/selectfile
+file	/man/2/sexprs
+file	/man/2/sh
+file	/man/2/spree
+file	/man/2/styx
+file	/man/2/styxconv
+file	/man/2/styxpersist
+file	/man/2/styxservers
+file	/man/2/styxservers-nametree
+file	/man/2/sys-0intro
+file	/man/2/sys-bind
+file	/man/2/sys-chdir
+file	/man/2/sys-dial
+file	/man/2/sys-dirread
+file	/man/2/sys-dup
+file	/man/2/sys-export
+file	/man/2/sys-fauth
+file	/man/2/sys-fd2path
+file	/man/2/sys-file2chan
+file	/man/2/sys-iounit
+file	/man/2/sys-open
+file	/man/2/sys-pctl
+file	/man/2/sys-pipe
+file	/man/2/sys-print
+file	/man/2/sys-read
+file	/man/2/sys-remove
+file	/man/2/sys-seek
+file	/man/2/sys-stat
+file	/man/2/tftp
+file	/man/2/tk
+file	/man/2/translate
+file	/man/2/wait
+file	/man/2/wmsrv
+file	/man/2/xml
+file	/man/3/0intro
+file	/man/3/arch
+file	/man/3/audio
+file	/man/3/boot
+file	/man/3/cap
+file	/man/3/cmd
+file	/man/3/cons
+file	/man/3/dbg
+file	/man/3/draw
+file	/man/3/ds
+file	/man/3/dup
+file	/man/3/dynld
+file	/man/3/eia
+file	/man/3/env
+file	/man/3/ether
+file	/man/3/flash
+file	/man/3/floppy
+file	/man/3/fpga
+file	/man/3/fs
+file	/man/3/ftl
+file	/man/3/gpio
+file	/man/3/i2c
+file	/man/3/indir
+file	/man/3/ip
+file	/man/3/kprof
+file	/man/3/logfs
+file	/man/3/lpt
+file	/man/3/mnt
+file	/man/3/mpeg
+file	/man/3/pbus
+file	/man/3/pipe
+file	/man/3/plap
+file	/man/3/pnp
+file	/man/3/pointer
+file	/man/3/prof
+file	/man/3/prog
+file	/man/3/root
+file	/man/3/rtc
+file	/man/3/sd
+file	/man/3/sign
+file	/man/3/snarf
+file	/man/3/srv
+file	/man/3/srv9
+file	/man/3/ssl
+file	/man/3/switch
+file	/man/3/tinyfs
+file	/man/3/tls
+file	/man/3/touch
+file	/man/3/tv
+file	/man/3/usb
+file	/man/3/vid
+file	/man/4/0intro
+file	/man/4/9srvfs
+file	/man/4/acme
+file	/man/4/archfs
+file	/man/4/dbfs
+file	/man/4/dossrv
+file	/man/4/export
+file	/man/4/factotum
+file	/man/4/ftpfs
+file	/man/4/grid-cpu
+file	/man/4/import
+file	/man/4/iostats
+file	/man/4/keyfs
+file	/man/4/keysrv
+file	/man/4/kfs
+file	/man/4/lockfs
+file	/man/4/logfile
+file	/man/4/memfs
+file	/man/4/mntgen
+file	/man/4/namespace
+file	/man/4/palmsrv
+file	/man/4/ramfile
+file	/man/4/registry
+file	/man/4/spree
+file	/man/4/tarfs
+file	/man/4/trfs
+file	/man/5/0intro
+file	/man/5/attach
+file	/man/5/clunk
+file	/man/5/flush
+file	/man/5/open
+file	/man/5/read
+file	/man/5/remove
+file	/man/5/stat
+file	/man/5/walk
+file	/man/6/0intro
+file	/man/6/attrdb
+file	/man/6/audio
+file	/man/6/dis
+file	/man/6/font
+file	/man/6/image
+file	/man/6/keyboard
+file	/man/6/keys
+file	/man/6/man
+file	/man/6/namespace
+file	/man/6/ndb
+file	/man/6/plumbing
+file	/man/6/proto
+file	/man/6/sbl
+file	/man/6/translate
+file	/man/6/users
+file	/man/7/db
+file	/man/8/0intro
+file	/man/8/ai2key
+file	/man/8/applylog
+file	/man/8/bootpd
+file	/man/8/changelogin
+file	/man/8/collabsrv
+file	/man/8/create
+file	/man/8/createsignerkey
+file	/man/8/cs
+file	/man/8/dns
+file	/man/8/ftl
+file	/man/8/getauthinfo
+file	/man/8/httpd
+file	/man/8/init
+file	/man/8/kfscmd
+file	/man/8/logind
+file	/man/8/manufacture
+file	/man/8/mkfs
+file	/man/8/plumber
+file	/man/8/prep
+file	/man/8/rdbgsrv
+file	/man/8/register
+file	/man/8/rstyxd
+file	/man/8/signer
+file	/man/8/styxchat
+file	/man/8/svc
+file	/man/8/touchcal
+file	/man/9/1copyright
+file	/man/9/image
+file	/man/9/scrollbar
+file	/man/9/text
+file	/man/9/types
+file's	/man/1/acme
+file's	/man/1/chgrp
+file's	/man/1/chmod
+file's	/man/1/ls
+file's	/man/1/mash-tk
+file's	/man/1/mk
+file's	/man/1/secstore
+file's	/man/1/sum
+file's	/man/1/uuencode
+file's	/man/1/webgrab
+file's	/man/10/conf
+file's	/man/10/mk
+file's	/man/2/dis
+file's	/man/2/disks
+file's	/man/2/fsproto
+file's	/man/2/palmfile
+file's	/man/2/styxservers
+file's	/man/2/styxservers-nametree
+file's	/man/2/sys-stat
+file's	/man/3/dynld
+file's	/man/3/fs
+file's	/man/3/prog
+file's	/man/4/kfs
+file's	/man/5/0intro
+file's	/man/5/stat
+file's	/man/8/applylog
+file's	/man/8/collabsrv
+file's	/man/8/create
+file.c	/man/1/acme
+file.c:27	/man/1/acme
+file.rbf	/man/8/fpgaload
+file.srec	/man/1/avr
+file1	/man/1/cat
+file1	/man/1/cmp
+file1	/man/1/comm
+file1	/man/1/cp
+file1	/man/1/diff
+file1	/man/1/ftest
+file1	/man/1/stream
+file1	/man/1/tr
+file1	/man/6/attrdb
+file2	/man/1/cat
+file2	/man/1/cmp
+file2	/man/1/comm
+file2	/man/1/cp
+file2	/man/1/diff
+file2	/man/1/ftest
+file2	/man/1/stream
+file2	/man/1/tr
+file2	/man/6/attrdb
+file2chan	/man/1/0intro
+file2chan	/man/1/sh
+file2chan	/man/1/sh-file2chan
+file2chan	/man/2/bufio-chanfill
+file2chan	/man/2/draw-context
+file2chan	/man/2/srv
+file2chan	/man/2/sys-file2chan
+file2chan	/man/2/wmsrv
+file2chan	/man/3/pipe
+file2chan	/man/3/srv
+file2chan	/man/4/logfile
+file2chan	/man/4/namespace
+file2chan	/man/4/ramfile
+file2chan	/man/5/0intro
+file2chan.b	/man/1/sh-file2chan
+file3	/man/1/cat
+file3	/man/6/attrdb
+file:lineno	/man/2/prof
+fileio	/man/2/bufio-chanfill
+fileio	/man/2/sys-file2chan
+filekey	/man/2/secstore
+filename	/man/1/acme
+filename	/man/1/alphabet-fs
+filename	/man/1/alphabet-main
+filename	/man/1/charon
+filename	/man/1/du
+filename	/man/1/filename
+filename	/man/1/fs
+filename	/man/1/ftree
+filename	/man/1/ls
+filename	/man/1/sh-file2chan
+filename	/man/1/sh-std
+filename	/man/1/tiny
+filename	/man/1/tktester
+filename	/man/1/uuencode
+filename	/man/1/wm-misc
+filename	/man/2/attrdb
+filename	/man/2/bufio
+filename	/man/2/complete
+filename	/man/2/debug
+filename	/man/2/filter-deflate
+filename	/man/2/keyring-auth
+filename	/man/2/palmfile
+filename	/man/2/selectfile
+filename	/man/2/tftp
+filename	/man/4/dbfs
+filename	/man/6/attrdb
+filename.b	/man/1/filename
+filename:linenumber	/man/1/acme
+filenames	/man/1/du
+filenames	/man/1/man
+filenames	/man/1/tiny
+filenames	/man/2/sys-0intro
+filenames	/man/3/fs
+fileoffset	/man/2/xml
+filepat	/man/1/alphabet-fs
+filepat	/man/1/filename
+filepat	/man/1/fs
+filepat	/man/1/sh-std
+filepat	/man/1/tiny
+filepat	/man/2/filepat
+filepat.b	/man/2/filepat
+filepat.m	/man/2/filepat
+filepc	/man/10/acid
+files	/man/1/0intro
+files	/man/1/9win
+files	/man/1/acme
+files	/man/1/alphabet-fs
+files	/man/1/ar
+files	/man/1/auplay
+files	/man/1/bind
+files	/man/1/blur
+files	/man/1/brutus
+files	/man/1/calc
+files	/man/1/cat
+files	/man/1/cd
+files	/man/1/charon
+files	/man/1/cmp
+files	/man/1/collab
+files	/man/1/collab-clients
+files	/man/1/comm
+files	/man/1/cook
+files	/man/1/cp
+files	/man/1/cprof
+files	/man/1/cpu
+files	/man/1/date
+files	/man/1/dd
+files	/man/1/deb
+files	/man/1/diff
+files	/man/1/disdep
+files	/man/1/du
+files	/man/1/ebook
+files	/man/1/emu
+files	/man/1/filename
+files	/man/1/fmt
+files	/man/1/fortune
+files	/man/1/freq
+files	/man/1/fs
+files	/man/1/ftree
+files	/man/1/gettar
+files	/man/1/grep
+files	/man/1/grid-query
+files	/man/1/grid-session
+files	/man/1/gzip
+files	/man/1/itest
+files	/man/1/keyboard
+files	/man/1/kill
+files	/man/1/limbo
+files	/man/1/logon
+files	/man/1/look
+files	/man/1/ls
+files	/man/1/m4
+files	/man/1/man
+files	/man/1/mash
+files	/man/1/mash-tk
+files	/man/1/mc
+files	/man/1/miniterm
+files	/man/1/mk
+files	/man/1/mprof
+files	/man/1/mux
+files	/man/1/mv
+files	/man/1/netstat
+files	/man/1/nsbuild
+files	/man/1/os
+files	/man/1/p
+files	/man/1/passwd
+files	/man/1/plumb
+files	/man/1/prof
+files	/man/1/ps
+files	/man/1/rm
+files	/man/1/secstore
+files	/man/1/sh
+files	/man/1/sh-regex
+files	/man/1/sh-std
+files	/man/1/sh-test
+files	/man/1/stack
+files	/man/1/stream
+files	/man/1/sum
+files	/man/1/tcs
+files	/man/1/tiny
+files	/man/1/tktester
+files	/man/1/toolbar
+files	/man/1/touch
+files	/man/1/unicode
+files	/man/1/units
+files	/man/1/uuencode
+files	/man/1/vacget
+files	/man/1/wc
+files	/man/1/webgrab
+files	/man/1/wish
+files	/man/1/wm
+files	/man/1/wm-misc
+files	/man/1/wm-sh
+files	/man/1/yacc
+files	/man/10/0intro
+files	/man/10/2a
+files	/man/10/2c
+files	/man/10/2l
+files	/man/10/9load
+files	/man/10/a.out
+files	/man/10/acid
+files	/man/10/ar
+files	/man/10/c2l
+files	/man/10/conf
+files	/man/10/dev
+files	/man/10/devattach
+files	/man/10/error
+files	/man/10/iar
+files	/man/10/kproc
+files	/man/10/ksize
+files	/man/10/kstrip
+files	/man/10/master
+files	/man/10/mk
+files	/man/10/newchan
+files	/man/10/ntsrv
+files	/man/10/odbc
+files	/man/10/plan9.ini
+files	/man/10/srclist
+files	/man/10/styxserver
+files	/man/2/0intro
+files	/man/2/alphabet-intro
+files	/man/2/attrdb
+files	/man/2/bufio
+files	/man/2/complete
+files	/man/2/convcs
+files	/man/2/csv
+files	/man/2/daytime
+files	/man/2/dbm
+files	/man/2/debug
+files	/man/2/devpointer
+files	/man/2/dhcpclient
+files	/man/2/dial
+files	/man/2/dis
+files	/man/2/disks
+files	/man/2/draw-display
+files	/man/2/draw-font
+files	/man/2/ether
+files	/man/2/exception
+files	/man/2/format
+files	/man/2/fsproto
+files	/man/2/imagefile
+files	/man/2/ip
+files	/man/2/keyring-0intro
+files	/man/2/keyring-auth
+files	/man/2/mpeg
+files	/man/2/palmfile
+files	/man/2/plumbmsg
+files	/man/2/print
+files	/man/2/prof
+files	/man/2/readdir
+files	/man/2/rfc822
+files	/man/2/scsiio
+files	/man/2/secstore
+files	/man/2/security-0intro
+files	/man/2/security-random
+files	/man/2/selectfile
+files	/man/2/sh
+files	/man/2/styxpersist
+files	/man/2/styxservers
+files	/man/2/styxservers-nametree
+files	/man/2/sys-0intro
+files	/man/2/sys-bind
+files	/man/2/sys-dial
+files	/man/2/sys-file2chan
+files	/man/2/sys-iounit
+files	/man/2/sys-pctl
+files	/man/2/sys-stat
+files	/man/2/tftp
+files	/man/2/tkclient
+files	/man/2/translate
+files	/man/2/volume
+files	/man/2/wmclient
+files	/man/2/wmlib
+files	/man/2/wmsrv
+files	/man/2/xml
+files	/man/3/0intro
+files	/man/3/audio
+files	/man/3/boot
+files	/man/3/cmd
+files	/man/3/cons
+files	/man/3/dbg
+files	/man/3/draw
+files	/man/3/ds
+files	/man/3/dup
+files	/man/3/dynld
+files	/man/3/env
+files	/man/3/flash
+files	/man/3/fpga
+files	/man/3/fs
+files	/man/3/ftl
+files	/man/3/gpio
+files	/man/3/i2c
+files	/man/3/ip
+files	/man/3/logfs
+files	/man/3/lpt
+files	/man/3/mpeg
+files	/man/3/pbus
+files	/man/3/pipe
+files	/man/3/plap
+files	/man/3/pnp
+files	/man/3/prof
+files	/man/3/prog
+files	/man/3/root
+files	/man/3/sd
+files	/man/3/srv
+files	/man/3/ssl
+files	/man/3/tinyfs
+files	/man/3/tls
+files	/man/3/tv
+files	/man/3/usb
+files	/man/3/vid
+files	/man/4/0intro
+files	/man/4/acme
+files	/man/4/archfs
+files	/man/4/dbfs
+files	/man/4/dossrv
+files	/man/4/factotum
+files	/man/4/ftpfs
+files	/man/4/grid-cpu
+files	/man/4/import
+files	/man/4/keyfs
+files	/man/4/keysrv
+files	/man/4/kfs
+files	/man/4/lockfs
+files	/man/4/namespace
+files	/man/4/palmsrv
+files	/man/4/registry
+files	/man/4/tarfs
+files	/man/4/vacfs
+files	/man/5/0intro
+files	/man/5/open
+files	/man/5/stat
+files	/man/5/walk
+files	/man/6/attrdb
+files	/man/6/audio
+files	/man/6/font
+files	/man/6/image
+files	/man/6/keyboard
+files	/man/6/keys
+files	/man/6/man
+files	/man/6/namespace
+files	/man/6/ndb
+files	/man/6/plumbing
+files	/man/6/proto
+files	/man/6/sbl
+files	/man/6/translate
+files	/man/6/utf
+files	/man/7/dbsrv
+files	/man/8/applylog
+files	/man/8/bootpd
+files	/man/8/changelogin
+files	/man/8/collabsrv
+files	/man/8/create
+files	/man/8/createsignerkey
+files	/man/8/cs
+files	/man/8/dns
+files	/man/8/getauthinfo
+files	/man/8/httpd
+files	/man/8/init
+files	/man/8/kfscmd
+files	/man/8/logind
+files	/man/8/manufacture
+files	/man/8/mkfs
+files	/man/8/plumber
+files	/man/8/prep
+files	/man/8/rdbgsrv
+files	/man/8/register
+files	/man/8/rstyxd
+files	/man/8/shutdown
+files	/man/8/signer
+files	/man/8/sntp
+files	/man/8/svc
+files	/man/8/touchcal
+files	/man/9/1copyright
+fileserver	/man/2/styxpersist
+fileserver	/man/6/ndb
+filestore	/man/1/0intro
+filestore	/man/2/venti
+filesystem	/man/1/alphabet-fs
+filesystem	/man/1/calendar
+filesystem	/man/1/fs
+filesystem	/man/10/9load
+filesystem	/man/2/dbm
+filesystem	/man/3/fs
+filesystem	/man/3/logfs
+filesystem	/man/4/dbfs
+filesystem	/man/4/lockfs
+filesystem	/man/4/memfs
+filesystem	/man/4/namespace
+filesystems	/man/1/alphabet-fs
+filesystems	/man/1/fs
+filesystems	/man/4/namespace
+filet	/man/2/daytime
+fill	/man/1/cook
+fill	/man/1/wm-misc
+fill	/man/10/dev
+fill	/man/10/styx
+fill	/man/2/bufio
+fill	/man/2/bufio-chanfill
+fill	/man/2/draw-image
+fill	/man/2/draw-screen
+fill	/man/2/filter
+fill	/man/2/prefab-element
+fill	/man/2/print
+fill	/man/2/regex
+fill	/man/2/spree-cardlib
+fill	/man/2/styx
+fill	/man/3/draw
+fill	/man/9/canvas
+fill	/man/9/grid
+fill	/man/9/pack
+fill	/man/9/text
+fillarc	/man/2/draw-image
+fillarcop	/man/2/draw-image
+fillbezier	/man/2/draw-image
+fillbezierop	/man/2/draw-image
+fillbezspline	/man/2/draw-image
+fillbezsplineop	/man/2/draw-image
+filled	/man/1/charon
+filled	/man/10/xalloc
+filled	/man/2/complete
+filled	/man/2/draw-image
+filled	/man/2/prefab-element
+filled	/man/2/security-random
+filled	/man/3/draw
+filled	/man/9/canvas
+fillellipse	/man/2/draw-image
+fillellipse:fn	/man/2/draw-image
+fillellipseop	/man/2/draw-image
+fillellipseop:fn	/man/2/draw-image
+fillid	/man/3/draw
+filling	/man/1/fmt
+filling	/man/1/wm-misc
+filling	/man/10/allocb
+filling	/man/10/devattach
+filling	/man/2/dbm
+filling	/man/2/prefab-element
+filling	/man/3/draw
+filling	/man/3/vga
+filling	/man/9/canvas
+fillpoly	/man/2/draw-image
+fillpoly	/man/9/canvas
+fillpolyop	/man/2/draw-image
+fills	/man/10/devattach
+fills	/man/2/bufio-chanfill
+fills	/man/2/dbm
+fills	/man/2/draw-image
+fills	/man/2/sets
+fills	/man/3/draw
+fillx	/man/2/spree-cardlib
+filly	/man/2/spree-cardlib
+film	/man/1/mux
+film	/man/6/colour
+filter	/man/1/alphabet-abc
+filter	/man/1/alphabet-fs
+filter	/man/1/alphabet-grid
+filter	/man/1/alphabet-main
+filter	/man/1/fs
+filter	/man/1/gzip
+filter	/man/1/sh-alphabet
+filter	/man/2/bloomfilter
+filter	/man/2/filter
+filter	/man/2/filter-deflate
+filter	/man/2/filter-slip
+filter	/man/2/lists
+filter	/man/2/spree-allow
+filter	/man/2/tk
+filter	/man/2/w3c-xpointers
+filter	/man/3/ssl
+filter	/man/3/tls
+filter	/man/3/touch
+filter	/man/4/registry
+filter.m	/man/2/filter
+filter.m	/man/2/filter-deflate
+filter.m	/man/2/filter-slip
+filtered	/man/1/alphabet-fs
+filtered	/man/1/fs
+filtering	/man/1/alphabet-fs
+filtering	/man/2/filter-deflate
+filterpath	/man/2/filter
+filters	/man/2/bloomfilter
+filters	/man/2/filter-deflate
+filters	/man/3/tls
+filters	/man/4/registry
+fin	/man/1/miniterm
+final	/man/1/acme
+final	/man/1/alphabet-main
+final	/man/1/basename
+final	/man/1/collab-clients
+final	/man/1/m4
+final	/man/1/mash
+final	/man/1/mash-tk
+final	/man/1/secstore
+final	/man/1/sh-alphabet
+final	/man/2/crc
+final	/man/2/dhcpclient
+final	/man/2/dis
+final	/man/2/names
+final	/man/2/print
+final	/man/2/spree
+final	/man/2/sys-bind
+final	/man/2/sys-open
+final	/man/4/acme
+final	/man/4/iostats
+final	/man/5/0intro
+final	/man/5/walk
+final	/man/6/dis
+final	/man/8/touchcal
+final	/man/9/0intro
+final	/man/9/grid
+finally	/man/1/collab-clients
+finally	/man/1/gettar
+finally	/man/1/math-misc
+finally	/man/1/sh-alphabet
+finally	/man/1/wm-misc
+finally	/man/10/2c
+finally	/man/10/9load
+finally	/man/10/a.out
+finally	/man/10/c2l
+finally	/man/10/error
+finally	/man/10/print
+finally	/man/10/styxserver
+finally	/man/2/arg
+finally	/man/2/dial
+finally	/man/2/dis
+finally	/man/2/itslib
+finally	/man/2/math-fp
+finally	/man/2/sys-dial
+finally	/man/2/sys-print
+finally	/man/3/ip
+finally	/man/5/open
+finally	/man/6/auth
+finally	/man/6/colour
+finally	/man/8/cs
+finally	/man/8/prep
+finally	/man/8/rstyxd
+finally,temporary	/man/10/c2l
+financial	/man/1/mux
+financial	/man/2/security-0intro
+findattr	/man/2/factotum
+findattrval	/man/2/factotum
+findbyattr	/man/2/attrdb
+findfirst	/man/2/attrdb
+findid	/man/2/spree-cardlib
+finding	/man/1/acme
+finding	/man/1/tiny
+finding	/man/1/wm-misc
+finding	/man/9/text
+findpair	/man/2/attrdb
+finds	/man/1/acme
+finds	/man/1/diff
+finds	/man/1/disdep
+finds	/man/1/emu
+finds	/man/1/man
+finds	/man/1/sh-alphabet
+finds	/man/1/strings
+finds	/man/10/9load
+finds	/man/2/disks
+finds	/man/2/spree
+finds	/man/2/wmsrv
+findsym	/man/2/debug
+findsym:fn	/man/2/debug
+fine	/man/2/0intro
+fine	/man/2/prefab-element
+fine	/man/3/lpt
+fine	/man/3/tv
+fine	/man/6/image
+finer	/man/1/0intro
+finer	/man/2/draw-image
+finer	/man/3/prof
+finish	/man/1/mash
+finish	/man/1/sh
+finished	/man/1/grid-monitor
+finished	/man/1/sh-arg
+finished	/man/1/sh-file2chan
+finished	/man/2/filter
+finished	/man/2/itslib
+finished	/man/8/mangaload
+finishes	/man/2/ir
+finishing	/man/2/spree
+finishing	/man/2/styxservers
+finite	/man/2/math-fp
+fio	/man/2/bufio-chanfill
+fire	/man/1/spree-join
+fired	/man/9/destroy
+firewall	/man/3/ip
+firewall	/man/4/ftpfs
+firewall	/man/6/ndb
+firewall	/man/8/mangaload
+firewalls	/man/4/ftpfs
+firmware	/man/3/pbus
+firstkey	/man/2/dbm
+fishcalledraawaru	/man/10/plan9.ini
+fit	/man/1/charon
+fit	/man/1/grid-monitor
+fit	/man/1/math-misc
+fit	/man/1/mc
+fit	/man/10/dev
+fit	/man/10/print
+fit	/man/2/asn1
+fit	/man/2/dbm
+fit	/man/2/disks
+fit	/man/2/prefab-element
+fit	/man/2/styx
+fit	/man/3/ds
+fit	/man/3/sd
+fit	/man/4/spree
+fit	/man/5/0intro
+fit	/man/6/image
+fit	/man/6/man
+fit	/man/9/entry
+fit	/man/9/grid
+fit	/man/9/pack
+fit	/man/9/text
+fit.b	/man/1/math-misc
+fitfully	/man/6/keyboard
+fitness	/man/9/1copyright
+fits	/man/1/math-misc
+fits	/man/10/9load
+fits	/man/9/scrollbar
+fitting	/man/1/tee
+fix	/man/1/acme
+fix	/man/8/kfscmd
+fixed	/man/1/acme
+fixed	/man/1/cpu
+fixed	/man/1/dd
+fixed	/man/1/fc
+fixed	/man/10/odbc
+fixed	/man/2/draw-0intro
+fixed	/man/2/draw-image
+fixed	/man/2/palmfile
+fixed	/man/2/plumbmsg
+fixed	/man/2/prefab-element
+fixed	/man/2/security-0intro
+fixed	/man/2/ubfa
+fixed	/man/3/cons
+fixed	/man/3/logfs
+fixed	/man/3/mpeg
+fixed	/man/3/plap
+fixed	/man/3/pnp
+fixed	/man/3/touch
+fixed	/man/4/acme
+fixed	/man/6/colour
+fixed	/man/6/ubfa
+fixed	/man/8/init
+fixed	/man/8/prep
+fixed	/man/9/bind
+fixed	/man/9/canvas
+fixed	/man/9/grid
+fixed	/man/9/listbox
+fixed	/man/9/pack
+fixed	/man/9/types
+fixedorigin	/man/1/wm
+fixfont	/man/1/acme
+fixfont	/man/4/acme
+flag	/man/1/alphabet-fs
+flag	/man/1/alphabet-main
+flag	/man/1/calc
+flag	/man/1/cprof
+flag	/man/1/fs
+flag	/man/1/ls
+flag	/man/1/mash
+flag	/man/1/mk
+flag	/man/1/mprof
+flag	/man/1/prof
+flag	/man/1/sh
+flag	/man/1/sh-arg
+flag	/man/1/sh-std
+flag	/man/1/tail
+flag	/man/1/units
+flag	/man/1/uuencode
+flag	/man/10/2c
+flag	/man/10/allocb
+flag	/man/10/devattach
+flag	/man/10/kstrip
+flag	/man/10/mk
+flag	/man/10/newchan
+flag	/man/10/plan9.ini
+flag	/man/10/print
+flag	/man/10/splhi
+flag	/man/2/alphabet-intro
+flag	/man/2/asn1
+flag	/man/2/complete
+flag	/man/2/debug
+flag	/man/2/dhcpclient
+flag	/man/2/draw-image
+flag	/man/2/filter-deflate
+flag	/man/2/imagefile
+flag	/man/2/math-fp
+flag	/man/2/mpeg
+flag	/man/2/regex
+flag	/man/2/sys-bind
+flag	/man/2/sys-export
+flag	/man/2/sys-print
+flag	/man/3/ip
+flag	/man/4/acme
+flag	/man/6/dis
+flag	/man/6/namespace
+flag	/man/8/prep
+flag	/man/9/grid
+flagged	/man/1/diff
+flags	/man/1/alphabet-main
+flags	/man/1/freq
+flags	/man/1/limbo
+flags	/man/1/mash
+flags	/man/1/sh
+flags	/man/1/sh-std
+flags	/man/10/2c
+flags	/man/10/kproc
+flags	/man/10/plan9.ini
+flags	/man/10/print
+flags	/man/2/alphabet-intro
+flags	/man/2/dbm
+flags	/man/2/dis
+flags	/man/2/factotum
+flags	/man/2/math-fp
+flags	/man/2/security-auth
+flags	/man/2/sh
+flags	/man/2/styxservers
+flags	/man/2/sys-export
+flags	/man/2/sys-pctl
+flags	/man/2/sys-print
+flags	/man/2/tk
+flags	/man/2/wmsrv
+flags	/man/5/stat
+flags	/man/6/dis
+flags	/man/8/kfscmd
+flags	/man/8/prep
+flandrena	/man/1/mk
+flandrena	/man/10/mk
+flash	/man/1/avr
+flash	/man/3/flash
+flash	/man/3/ftl
+flash	/man/3/logfs
+flash	/man/4/dbfs
+flash	/man/4/kfs
+flash	/man/8/0intro
+flash	/man/8/ftl
+flash	/man/8/init
+flash	/man/8/mangaload
+flash	/man/8/rdbgsrv
+flashctl	/man/3/flash
+flashctl	/man/3/ftl
+flashes	/man/3/pbus
+flashes	/man/8/prep
+flashsize	/man/8/ftl
+flat	/man/2/translate
+flat	/man/3/logfs
+flat	/man/9/button
+flat	/man/9/checkbutton
+flat	/man/9/options
+flat	/man/9/radiobutton
+flat	/man/9/types
+flatex	/man/2/stringinttab
+flatexbook	/man/2/stringinttab
+flatexpart	/man/2/stringinttab
+flatexproc	/man/2/stringinttab
+flatexslides	/man/2/stringinttab
+fld	/man/7/db
+fleshier	/man/1/tiny
+flex6000	/man/3/fpga
+flex6000	/man/8/fpgaload
+flill	/man/1/tktester
+flip	/man/2/spree-cardlib
+float	/man/1/cook
+float	/man/10/acid
+float	/man/10/c2l
+float	/man/10/odbc
+float	/man/2/w3c-css
+floating	/man/1/calc
+floating	/man/1/fc
+floating	/man/1/units
+floating	/man/10/acid
+floating	/man/10/atoi
+floating	/man/10/kproc
+floating	/man/10/print
+floating	/man/2/math-0intro
+floating	/man/2/math-export
+floating	/man/2/math-fp
+floating	/man/2/rfc822
+floating	/man/2/string
+floating	/man/2/sys-print
+floating	/man/9/canvas
+floating	/man/9/text
+floats	/man/1/ebook
+floats	/man/10/acid
+floor	/man/1/calc
+floor	/man/2/math-fp
+floppy	/man/10/9load
+floppy	/man/2/disks
+floppy	/man/3/floppy
+floppy	/man/3/sd
+floppy	/man/8/prep
+flow	/man/1/calc
+flow	/man/1/sh
+flow	/man/1/sh-std
+flow	/man/10/qio
+flow	/man/2/sys-pipe
+flow	/man/3/eia
+flow	/man/3/ip
+flow	/man/3/mpeg
+flow	/man/8/mangaload
+floyd	/man/2/imagefile
+flush	/man/10/qio
+flush	/man/2/bufio
+flush	/man/2/convcs
+flush	/man/2/dbm
+flush	/man/2/draw-image
+flush	/man/2/math-0intro
+flush	/man/2/styx
+flush	/man/2/styxflush
+flush	/man/3/0intro
+flush	/man/3/dbg
+flush	/man/3/draw
+flush	/man/3/eia
+flush	/man/3/flash
+flush	/man/3/ip
+flush	/man/3/logfs
+flush	/man/5/0intro
+flush	/man/5/flush
+flush	/man/9/update
+flush.ftpfs	/man/4/ftpfs
+flushc	/man/2/styxflush
+flushed	/man/1/xd
+flushed	/man/10/qio
+flushed	/man/2/bufio
+flushed	/man/2/convcs
+flushed	/man/2/draw-image
+flushed	/man/2/styxflush
+flushed	/man/4/ftpfs
+flushed	/man/5/flush
+flushed	/man/9/panel
+flushes	/man/2/draw-image
+flushes	/man/2/styxflush
+flushes	/man/3/boot
+flushes	/man/5/flush
+flushes	/man/9/update
+flushing	/man/2/draw-image
+flushing	/man/2/styxflush
+flushing	/man/5/flush
+flushnow	/man/2/draw-image
+flushoff	/man/2/draw-image
+flushon	/man/2/draw-image
+fly	/man/1/0intro
+fmag	/man/10/ar
+fmax	/man/2/math-fp
+fmin	/man/2/math-fp
+fmod	/man/2/math-fp
+fmt	/man/1/fmt
+fmt	/man/10/acid
+fmt	/man/10/panic
+fmt	/man/10/print
+fmt	/man/10/styx
+fmt	/man/2/format
+fmt	/man/2/stringinttab
+fmt.b	/man/1/fmt
+fmtfile	/man/2/format
+fmtfile.new	/man/2/format
+fmtinstall	/man/10/print
+fmtinstall	/man/10/styx
+fmtspec	/man/2/format
+fmtstring	/man/2/stringinttab
+fmtstringtab	/man/2/stringinttab
+fmtval	/man/2/format
+fn	/man/1/calc
+fn	/man/1/mash
+fn	/man/1/sh-file2chan
+fn	/man/1/sh-std
+fn	/man/1/stack
+fn	/man/1/wm-sh
+fn	/man/1/yacc
+fn	/man/2/arg
+fn	/man/2/asn1
+fn	/man/2/attrdb
+fn	/man/2/bloomfilter
+fn	/man/2/bufio
+fn	/man/2/bufio-chanfill
+fn	/man/2/cfg
+fn	/man/2/command
+fn	/man/2/complete
+fn	/man/2/convcs
+fn	/man/2/crc
+fn	/man/2/csv
+fn	/man/2/daytime
+fn	/man/2/dbm
+fn	/man/2/debug
+fn	/man/2/devpointer
+fn	/man/2/dhcpclient
+fn	/man/2/dial
+fn	/man/2/dialog
+fn	/man/2/dict
+fn	/man/2/dis
+fn	/man/2/diskblocks
+fn	/man/2/disks
+fn	/man/2/dividers
+fn	/man/2/draw-0intro
+fn	/man/2/draw-context
+fn	/man/2/draw-display
+fn	/man/2/draw-example
+fn	/man/2/draw-font
+fn	/man/2/draw-image
+fn	/man/2/draw-point
+fn	/man/2/draw-rect
+fn	/man/2/draw-screen
+fn	/man/2/drawmux
+fn	/man/2/encoding
+fn	/man/2/env
+fn	/man/2/ether
+fn	/man/2/exception
+fn	/man/2/factotum
+fn	/man/2/filepat
+fn	/man/2/filter
+fn	/man/2/filter-deflate
+fn	/man/2/filter-slip
+fn	/man/2/format
+fn	/man/2/fsproto
+fn	/man/2/geodesy
+fn	/man/2/hash
+fn	/man/2/ida
+fn	/man/2/imagefile
+fn	/man/2/ip
+fn	/man/2/ir
+fn	/man/2/itslib
+fn	/man/2/json
+fn	/man/2/keyring-0intro
+fn	/man/2/keyring-auth
+fn	/man/2/keyring-certtostr
+fn	/man/2/keyring-crypt
+fn	/man/2/keyring-gensk
+fn	/man/2/keyring-getmsg
+fn	/man/2/keyring-getstring
+fn	/man/2/keyring-ipint
+fn	/man/2/keyring-rc4
+fn	/man/2/keyring-sha1
+fn	/man/2/keyset
+fn	/man/2/lists
+fn	/man/2/lock
+fn	/man/2/math-elem
+fn	/man/2/math-export
+fn	/man/2/math-fp
+fn	/man/2/math-linalg
+fn	/man/2/mpeg
+fn	/man/2/msgio
+fn	/man/2/names
+fn	/man/2/newns
+fn	/man/2/palmfile
+fn	/man/2/plumbmsg
+fn	/man/2/pop3
+fn	/man/2/popup
+fn	/man/2/prefab-compound
+fn	/man/2/prefab-element
+fn	/man/2/print
+fn	/man/2/prof
+fn	/man/2/pslib
+fn	/man/2/rand
+fn	/man/2/readdir
+fn	/man/2/regex
+fn	/man/2/registries
+fn	/man/2/rfc822
+fn	/man/2/scsiio
+fn	/man/2/secstore
+fn	/man/2/security-auth
+fn	/man/2/security-login
+fn	/man/2/security-random
+fn	/man/2/security-ssl
+fn	/man/2/selectfile
+fn	/man/2/sets
+fn	/man/2/sexprs
+fn	/man/2/sh
+fn	/man/2/smtp
+fn	/man/2/spki
+fn	/man/2/spki-verifier
+fn	/man/2/spree
+fn	/man/2/spree-allow
+fn	/man/2/spree-cardlib
+fn	/man/2/spree-gather
+fn	/man/2/spree-objstore
+fn	/man/2/srv
+fn	/man/2/string
+fn	/man/2/stringinttab
+fn	/man/2/styx
+fn	/man/2/styxconv
+fn	/man/2/styxflush
+fn	/man/2/styxpersist
+fn	/man/2/styxservers
+fn	/man/2/styxservers-nametree
+fn	/man/2/sys-bind
+fn	/man/2/sys-byte2char
+fn	/man/2/sys-chdir
+fn	/man/2/sys-dial
+fn	/man/2/sys-dirread
+fn	/man/2/sys-dup
+fn	/man/2/sys-export
+fn	/man/2/sys-fauth
+fn	/man/2/sys-file2chan
+fn	/man/2/sys-fversion
+fn	/man/2/sys-iounit
+fn	/man/2/sys-millisec
+fn	/man/2/sys-open
+fn	/man/2/sys-pctl
+fn	/man/2/sys-pipe
+fn	/man/2/sys-print
+fn	/man/2/sys-read
+fn	/man/2/sys-remove
+fn	/man/2/sys-seek
+fn	/man/2/sys-self
+fn	/man/2/sys-sleep
+fn	/man/2/sys-stat
+fn	/man/2/sys-tokenize
+fn	/man/2/sys-utfbytes
+fn	/man/2/tabs
+fn	/man/2/tftp
+fn	/man/2/timers
+fn	/man/2/tk
+fn	/man/2/tkclient
+fn	/man/2/translate
+fn	/man/2/ubfa
+fn	/man/2/venti
+fn	/man/2/virgil
+fn	/man/2/volume
+fn	/man/2/w3c-css
+fn	/man/2/w3c-uris
+fn	/man/2/w3c-xpointers
+fn	/man/2/wait
+fn	/man/2/wmclient
+fn	/man/2/wmlib
+fn	/man/2/wmsrv
+fn	/man/2/workdir
+fn	/man/2/xml
+fn	/man/3/kprof
+fn	/man/3/pnp
+fn	/man/6/dis
+fn	/man/6/sbl
+fn	/man/7/db
+fn	/man/8/init
+fname	/man/2/palmfile
+fnd	/man/2/stringinttab
+fnn	/man/1/mux
+fns.h	/man/10/0intro
+fnvram	/man/3/tinyfs
+focus	/man/1/wm
+focus	/man/2/rfc822
+focus	/man/9/0intro
+focus	/man/9/bind
+focus	/man/9/button
+focus	/man/9/canvas
+focus	/man/9/entry
+focus	/man/9/focus
+focus	/man/9/listbox
+focus	/man/9/menubutton
+focus	/man/9/options
+focus	/man/9/text
+focused	/man/1/acme
+focused	/man/2/wmclient
+focusin	/man/9/bind
+focusout	/man/9/bind
+foil	/man/6/login
+fold	/man/1/fmt
+fold	/man/1/look
+folded	/man/1/mk
+folded	/man/10/mk
+folded	/man/2/prefab-element
+folding	/man/1/limbo
+folding	/man/2/prefab-element
+follow	/man/1/calc
+follow	/man/1/m4
+follow	/man/1/sh-arg
+follow	/man/1/tail
+follow	/man/1/wm-sh
+follow	/man/1/yacc
+follow	/man/10/9load
+follow	/man/10/error
+follow	/man/2/sys-0intro
+follow	/man/2/w3c-xpointers
+follow	/man/3/sd
+follow	/man/3/tls
+follow	/man/3/vga
+follow	/man/4/acme
+follow	/man/4/factotum
+follow	/man/8/rstyxd
+follow	/man/9/0intro
+follow	/man/9/1copyright
+follow	/man/9/canvas
+follow	/man/9/grid
+follow	/man/9/text
+font	/man/1/acme
+font	/man/1/brutus
+font	/man/1/emu
+font	/man/1/unicode
+font	/man/1/wm-misc
+font	/man/1/wm-sh
+font	/man/2/draw-0intro
+font	/man/2/draw-example
+font	/man/2/draw-font
+font	/man/2/draw-image
+font	/man/2/prefab-0intro
+font	/man/2/prefab-element
+font	/man/2/prefab-style
+font	/man/2/print
+font	/man/3/draw
+font	/man/4/acme
+font	/man/4/namespace
+font	/man/6/font
+font	/man/6/image
+font	/man/6/man
+font	/man/9/button
+font	/man/9/canvas
+font	/man/9/checkbutton
+font	/man/9/choicebutton
+font	/man/9/entry
+font	/man/9/label
+font	/man/9/listbox
+font	/man/9/menu
+font	/man/9/menubutton
+font	/man/9/options
+font	/man/9/radiobutton
+font	/man/9/scale
+font	/man/9/text
+font	/man/9/types
+font.build	/man/6/font
+font.open	/man/2/draw-example
+font.open	/man/6/font
+fontid	/man/3/draw
+fonts	/man/1/acme
+fonts	/man/1/emu
+fonts	/man/1/fc
+fonts	/man/1/miniterm
+fonts	/man/1/wm-misc
+fonts	/man/2/draw-0intro
+fonts	/man/2/draw-font
+fonts	/man/2/draw-image
+fonts	/man/2/prefab-0intro
+fonts	/man/2/prefab-environ
+fonts	/man/2/prefab-style
+fonts	/man/2/sys-byte2char
+fonts	/man/3/draw
+fonts	/man/4/namespace
+fonts	/man/4/trfs
+fonts	/man/6/font
+fonts	/man/6/image
+fonts	/man/6/man
+fonts	/man/9/text
+fonts	/man/9/types
+foo	/man/1/alphabet-fs
+foo	/man/1/fs
+foo	/man/1/mk
+foo	/man/1/sh-alphabet
+foo	/man/10/mk
+foo	/man/2/alphabet-intro
+foo.b	/man/2/alphabet-intro
+foo.m	/man/2/alphabet-intro
+fooled	/man/4/iostats
+foot	/man/2/keyring-0intro
+footer	/man/2/filter-deflate
+footprints	/man/3/logfs
+footypes.b	/man/2/alphabet-intro
+footypes.m	/man/2/alphabet-intro
+fopen	/man/1/yacc
+fopen	/man/2/bufio
+fopen	/man/2/xml
+force100	/man/10/plan9.ini
+forced	/man/10/delay
+forces	/man/1/calc
+forces	/man/1/charon
+forces	/man/1/unicode
+forces	/man/2/asn1
+forces	/man/3/logfs
+forces	/man/4/ftpfs
+forcing	/man/2/keyring-getstring
+forcing	/man/2/msgio
+forcing	/man/3/fpga
+forcing	/man/4/ftpfs
+foregoing	/man/9/1copyright
+foreground	/man/9/button
+foreground	/man/9/canvas
+foreground	/man/9/checkbutton
+foreground	/man/9/choicebutton
+foreground	/man/9/entry
+foreground	/man/9/label
+foreground	/man/9/listbox
+foreground	/man/9/menu
+foreground	/man/9/menubutton
+foreground	/man/9/options
+foreground	/man/9/radiobutton
+foreground	/man/9/scale
+foreground	/man/9/text
+foreign	/man/4/0intro
+forge	/man/2/security-0intro
+forged	/man/2/keyring-0intro
+forget	/man/1/tktester
+forget	/man/1/toolbar
+forget	/man/5/clunk
+forget	/man/9/grid
+forget	/man/9/pack
+forgotten	/man/1/tktester
+forgotten	/man/2/spree
+forgotten	/man/9/grid
+fork	/man/1/nsbuild
+fork	/man/1/tiny
+fork	/man/6/namespace
+forked	/man/1/sh
+forkenv	/man/2/sys-export
+forkenv	/man/2/sys-pctl
+forkenv	/man/3/env
+forkfd	/man/1/sh-std
+forkfd	/man/2/sys-pctl
+forking	/man/4/export
+forkns	/man/1/sh-std
+forkns	/man/2/command
+forkns	/man/2/security-auth
+forkns	/man/2/styxservers-nametree
+forkns	/man/2/sys-chdir
+forkns	/man/2/sys-export
+forkns	/man/2/sys-pctl
+forkns	/man/6/namespace
+forks	/man/1/sh
+forks	/man/1/tiny
+forks	/man/10/acid
+forks	/man/2/sys-export
+formally	/man/1/acme
+format	/man/1/0intro
+format	/man/1/acme
+format	/man/1/alphabet-fs
+format	/man/1/alphabet-main
+format	/man/1/ar
+format	/man/1/auplay
+format	/man/1/avr
+format	/man/1/brutus
+format	/man/1/charon
+format	/man/1/cook
+format	/man/1/crypt
+format	/man/1/date
+format	/man/1/disdep
+format	/man/1/ebook
+format	/man/1/fc
+format	/man/1/fs
+format	/man/1/gettar
+format	/man/1/gzip
+format	/man/1/itest
+format	/man/1/ls
+format	/man/1/man
+format	/man/1/mash
+format	/man/1/mash-make
+format	/man/1/mdb
+format	/man/1/mprof
+format	/man/1/nsbuild
+format	/man/1/prof
+format	/man/1/sh-csv
+format	/man/1/sh-sexprs
+format	/man/1/tktester
+format	/man/1/wc
+format	/man/1/wish
+format	/man/1/xd
+format	/man/10/0intro
+format	/man/10/2c
+format	/man/10/2l
+format	/man/10/5coff
+format	/man/10/5cv
+format	/man/10/9load
+format	/man/10/a.out
+format	/man/10/acid
+format	/man/10/ar
+format	/man/10/dev
+format	/man/10/devattach
+format	/man/10/dynld
+format	/man/10/iar
+format	/man/10/ksize
+format	/man/10/ms2
+format	/man/10/odbc
+format	/man/10/panic
+format	/man/10/plan9.ini
+format	/man/10/print
+format	/man/10/rune
+format	/man/10/srclist
+format	/man/10/styx
+format	/man/2/attrdb
+format	/man/2/bufio
+format	/man/2/cfg
+format	/man/2/convcs
+format	/man/2/csv
+format	/man/2/daytime
+format	/man/2/devpointer
+format	/man/2/dis
+format	/man/2/disks
+format	/man/2/draw-0intro
+format	/man/2/draw-context
+format	/man/2/draw-display
+format	/man/2/draw-font
+format	/man/2/draw-image
+format	/man/2/draw-screen
+format	/man/2/filter-deflate
+format	/man/2/format
+format	/man/2/geodesy
+format	/man/2/imagefile
+format	/man/2/ip
+format	/man/2/json
+format	/man/2/keyring-0intro
+format	/man/2/keyring-certtostr
+format	/man/2/keyring-ipint
+format	/man/2/newns
+format	/man/2/palmfile
+format	/man/2/plumbmsg
+format	/man/2/prefab-element
+format	/man/2/print
+format	/man/2/rfc822
+format	/man/2/sets
+format	/man/2/smtp
+format	/man/2/styx
+format	/man/2/sys-0intro
+format	/man/2/sys-print
+format	/man/2/tk
+format	/man/2/translate
+format	/man/2/ubfa
+format	/man/2/wait
+format	/man/3/audio
+format	/man/3/cons
+format	/man/3/draw
+format	/man/3/dup
+format	/man/3/flash
+format	/man/3/floppy
+format	/man/3/fpga
+format	/man/3/ftl
+format	/man/3/ip
+format	/man/3/logfs
+format	/man/3/mpeg
+format	/man/3/pointer
+format	/man/3/prog
+format	/man/3/rtc
+format	/man/3/sign
+format	/man/3/tls
+format	/man/3/tv
+format	/man/3/vga
+format	/man/3/vid
+format	/man/4/0intro
+format	/man/4/acme
+format	/man/4/archfs
+format	/man/4/dbfs
+format	/man/4/dossrv
+format	/man/4/factotum
+format	/man/4/kfs
+format	/man/4/registry
+format	/man/6/attrdb
+format	/man/6/audio
+format	/man/6/colour
+format	/man/6/dis
+format	/man/6/font
+format	/man/6/image
+format	/man/6/keyboard
+format	/man/6/keytext
+format	/man/6/man
+format	/man/6/ndb
+format	/man/6/plumbing
+format	/man/6/sbl
+format	/man/6/scancode
+format	/man/6/ubfa
+format	/man/6/users
+format	/man/6/utf
+format	/man/7/dbsrv
+format	/man/8/ai2key
+format	/man/8/applylog
+format	/man/8/bootpd
+format	/man/8/changelogin
+format	/man/8/collabsrv
+format	/man/8/create
+format	/man/8/dhcp
+format	/man/8/fpgaload
+format	/man/8/ftl
+format	/man/8/prep
+format	/man/8/styxchat
+format	/man/9/0intro
+format	/man/9/canvas
+format	/man/9/image
+format.b	/man/2/format
+format.b	/man/8/prep
+format.m	/man/2/format
+formats	/man/1/0intro
+formats	/man/1/crypt
+formats	/man/1/fc
+formats	/man/1/freq
+formats	/man/1/mc
+formats	/man/1/xd
+formats	/man/10/0intro
+formats	/man/10/2c
+formats	/man/10/5cv
+formats	/man/10/acid
+formats	/man/10/odbc
+formats	/man/10/print
+formats	/man/2/draw-image
+formats	/man/2/geodesy
+formats	/man/2/imagefile
+formats	/man/2/keyring-0intro
+formats	/man/2/palmfile
+formats	/man/2/sys-print
+formats	/man/3/kprof
+formats	/man/3/logfs
+formats	/man/3/mpeg
+formats	/man/3/prog
+formats	/man/4/factotum
+formats	/man/6/0intro
+formats	/man/6/colour
+formats	/man/6/image
+formats	/man/6/scancode
+formatted	/man/1/fmt
+formatted	/man/1/ls
+formatted	/man/1/man
+formatted	/man/1/mc
+formatted	/man/1/sh
+formatted	/man/10/print
+formatted	/man/10/styx
+formatted	/man/2/draw-context
+formatted	/man/2/spree-allow
+formatted	/man/2/styx
+formatted	/man/2/sys-0intro
+formatted	/man/2/sys-print
+formatted	/man/2/tk
+formatted	/man/2/tkclient
+formatted	/man/2/wmclient
+formatted	/man/2/wmsrv
+formatted	/man/3/cons
+formatted	/man/3/draw
+formatted	/man/3/floppy
+formatted	/man/3/ftl
+formatted	/man/3/pnp
+formatted	/man/3/tinyfs
+formatted	/man/4/acme
+formatted	/man/4/registry
+formatted	/man/5/stat
+formatted	/man/8/prep
+formatted	/man/9/bind
+formatter	/man/1/fmt
+formatter	/man/8/ftl
+formatting	/man/1/brutus
+formatting	/man/1/man
+formatting	/man/1/tktester
+formatting	/man/10/2c
+formatting	/man/10/print
+formatting	/man/10/styx
+formatting	/man/2/disks
+formatting	/man/3/vid
+formatting	/man/8/prep
+formed	/man/1/calc
+formed	/man/1/chmod
+formed	/man/1/mk
+formed	/man/1/sh
+formed	/man/1/sh-sexprs
+formed	/man/1/sh-std
+formed	/man/10/2c
+formed	/man/10/mk
+formed	/man/2/asn1
+formed	/man/2/attrdb
+formed	/man/2/daytime
+formed	/man/2/names
+formed	/man/2/sexprs
+formed	/man/2/spki
+formed	/man/2/styxservers
+formed	/man/2/translate
+formed	/man/4/spree
+formed	/man/6/attrdb
+formed	/man/6/dis
+formed	/man/6/login
+formed	/man/8/ftl
+former	/man/1/acme
+former	/man/1/calc
+former	/man/2/prof
+former	/man/2/spki
+former	/man/3/logfs
+former	/man/6/colour
+former	/man/8/prep
+forming	/man/10/a.out
+forming	/man/10/qlock
+forming	/man/2/asn1
+forming	/man/2/disks
+forming	/man/2/draw-display
+forming	/man/2/string
+forming	/man/3/ip
+forming	/man/5/0intro
+forms	/man/1/acme
+forms	/man/1/brutus
+forms	/man/1/collab-clients
+forms	/man/1/diff
+forms	/man/1/mash
+forms	/man/1/mdb
+forms	/man/1/sh
+forms	/man/10/2c
+forms	/man/10/conf
+forms	/man/2/daytime
+forms	/man/2/dhcpclient
+forms	/man/2/dial
+forms	/man/2/ip
+forms	/man/2/math-0intro
+forms	/man/2/rfc822
+forms	/man/2/sexprs
+forms	/man/2/spki
+forms	/man/2/styxservers-nametree
+forms	/man/2/sys-dial
+forms	/man/2/w3c-css
+forms	/man/2/w3c-xpointers
+forms	/man/6/keytext
+forms	/man/6/sexprs
+forms	/man/8/cs
+forms	/man/9/bind
+forms	/man/9/canvas
+forms	/man/9/entry
+forms	/man/9/grab
+forms	/man/9/grid
+forms	/man/9/image
+forms	/man/9/listbox
+forms	/man/9/menu
+forms	/man/9/pack
+forms	/man/9/scrollbar
+forms	/man/9/text
+formula	/man/2/bloomfilter
+forth	/man/4/factotum
+forth	/man/9/menu
+fortran	/man/2/math-0intro
+fortran	/man/2/math-linalg
+fortunately	/man/10/devattach
+fortunately	/man/2/asn1
+fortunately	/man/2/security-0intro
+fortune	/man/1/fortune
+fortune.b	/man/1/fortune
+fortunes	/man/1/fortune
+fortunes.index	/man/1/fortune
+forward	/man/1/charon
+forward	/man/1/dd
+forward	/man/1/ebook
+forward	/man/1/mux
+forward	/man/1/wm-misc
+forward	/man/2/complete
+forward	/man/2/debug
+forward	/man/2/ir
+forward	/man/2/stringinttab
+forward	/man/9/text
+forwarded	/man/2/drawmux
+forwarding	/man/1/wm-sh
+forwards	/man/1/acme
+forwards	/man/1/brutus
+forwards	/man/1/charon
+forwards	/man/1/ebook
+forwards	/man/8/plumber
+fossil	/man/8/prep
+fourth	/man/1/secstore
+fourth	/man/2/pop3
+fourth	/man/3/kprof
+fourth	/man/4/acme
+fourth	/man/8/create
+fourth	/man/9/text
+foverwrite	/man/2/palmfile
+fp	/man/1/fc
+fp	/man/2/0intro
+fp	/man/2/dis
+fp	/man/2/math-0intro
+fp	/man/2/math-fp
+fp	/man/6/dis
+fpcontrol	/man/2/math-fp
+fpga	/man/3/fpga
+fpga	/man/8/fpgaload
+fpgaclk	/man/3/fpga
+fpgactl	/man/3/fpga
+fpgaload	/man/3/fpga
+fpgaload	/man/8/fpgaload
+fpgaload.b	/man/8/fpgaload
+fpgamemb	/man/3/fpga
+fpgamemw	/man/3/fpga
+fpgaprog	/man/3/fpga
+fpgastatus	/man/3/fpga
+fpr	/man/10/acid
+fprint	/man/1/yacc
+fprint	/man/10/print
+fprint	/man/2/security-auth
+fprint	/man/2/sys-print
+fprintf	/man/10/print
+fprivate	/man/2/palmfile
+fpstatus	/man/2/math-fp
+fr	/man/1/rm
+fr	/man/1/tail
+frac	/man/1/calc
+frac	/man/6/json
+frac	/man/9/scale
+frac	/man/9/types
+fractal	/man/1/wm-misc
+fraction	/man/10/print
+fraction	/man/2/sys-print
+fraction	/man/6/keyboard
+fraction	/man/9/canvas
+fraction	/man/9/entry
+fraction	/man/9/listbox
+fraction	/man/9/options
+fraction	/man/9/scrollbar
+fraction	/man/9/text
+fractional	/man/2/json
+fractional	/man/2/math-fp
+fractional	/man/9/scrollbar
+fractional	/man/9/types
+fractions	/man/9/listbox
+fractions	/man/9/scrollbar
+fractions	/man/9/text
+frag	/man/2/ida
+fragment	/man/2/ida
+fragment	/man/2/plumbmsg
+fragment	/man/2/w3c-uris
+fragments	/man/2/ida
+frags	/man/2/ida
+frame	/man/1/charon
+frame	/man/1/deb
+frame	/man/1/tktester
+frame	/man/10/a.out
+frame	/man/2/debug
+frame	/man/2/dis
+frame	/man/2/draw-context
+frame	/man/2/draw-example
+frame	/man/2/spree-cardlib
+frame	/man/2/tkclient
+frame	/man/2/translate
+frame	/man/3/prog
+frame	/man/3/tv
+frame	/man/3/usb
+frame	/man/3/vid
+frame	/man/6/colour
+frame	/man/6/dis
+frame	/man/6/sbl
+frame	/man/9/0intro
+frame	/man/9/frame
+frame	/man/9/pack
+frame.m	/man/1/mash-make
+framed	/man/2/filter-slip
+frames	/man/1/charon
+frames	/man/1/deb
+frames	/man/1/limbo
+frames	/man/1/tktester
+frames	/man/1/webgrab
+frames	/man/1/wm-sh
+frames	/man/2/spree-cardlib
+frames	/man/9/frame
+frameset	/man/1/charon
+framesets	/man/1/charon
+framework	/man/1/wm-sh
+framework	/man/2/prefab-environ
+framework	/man/2/styxservers
+framework	/man/2/w3c-xpointers
+framework	/man/5/0intro
+framing	/man/2/filter-slip
+framing	/man/3/ip
+france	/man/1/miniterm
+fred	/man/10/styxserver
+freeb	/man/10/allocb
+freeblist	/man/10/allocb
+freebsd	/man/4/namespace
+freeclient	/man/10/styxserver
+freed	/man/10/allocb
+freed	/man/10/dev
+freed	/man/10/devattach
+freed	/man/10/qio
+freed	/man/2/alphabet-intro
+freed	/man/2/registries
+freed	/man/3/cons
+freed	/man/3/draw
+freed	/man/5/version
+freedb.freedb.org	/man/7/cddb
+freedom	/man/1/sh
+freeing	/man/1/mprof
+freeing	/man/10/allocb
+freeing	/man/10/parsecmd
+freely	/man/2/prefab-element
+frees	/man/10/allocb
+frees	/man/10/devattach
+frees	/man/10/dynld
+frees	/man/10/newchan
+frees	/man/10/qio
+frees	/man/10/xalloc
+frees	/man/2/draw-0intro
+frees	/man/2/registries
+frees	/man/3/cons
+freeze	/man/3/tv
+freeze	/man/9/grab
+french	/man/1/miniterm
+freq	/man/1/freq
+freq	/man/2/w3c-css
+freq.b	/man/1/freq
+frequencies	/man/1/freq
+frequency	/man/10/seconds
+frequency	/man/2/prof
+frequency	/man/3/fpga
+frequency	/man/3/prof
+frequency	/man/3/tv
+frequently	/man/2/draw-image
+freset	/man/2/palmfile
+fresh	/man/2/secstore
+fresh	/man/2/styxflush
+fresh	/man/4/iostats
+fresh	/man/5/attach
+fresource	/man/2/palmfile
+fri	/man/2/daytime
+friendly	/man/2/convcs
+friends	/man/10/print
+fromdir	/man/1/cp
+fromfile	/man/1/cp
+fromfile	/man/1/mv
+fromfiles	/man/1/cp
+fromid	/man/2/spree
+fromuser	/man/3/cap
+fromwhom	/man/2/smtp
+fronly	/man/2/palmfile
+front	/man/1/acme
+front	/man/1/deb
+front	/man/10/iar
+front	/man/10/qio
+front	/man/10/styxserver
+front	/man/2/draw-screen
+front	/man/9/text
+fruitbat	/man/6/attrdb
+fs	/man/1/alphabet-fs
+fs	/man/1/fs
+fs	/man/1/sh-alphabet
+fs	/man/10/odbc
+fs	/man/10/plan9.ini
+fs	/man/2/fsproto
+fs	/man/2/sys-0intro
+fs	/man/3/ds
+fs	/man/3/fs
+fs	/man/3/ip
+fs	/man/6/ndb
+fs	/man/6/proto
+fs	/man/8/applylog
+fs	/man/8/bootpd
+fs	/man/8/mkfs
+fs	/man/8/prep
+fs	/man/8/rdbgsrv
+fs.b	/man/1/alphabet-fs
+fs.b	/man/1/fs
+fs.tgz	/man/8/rdbgsrv
+fscfg	/man/8/prep
+fsconfig	/man/3/ds
+fsfilter.b	/man/1/alphabet-fs
+fsip	/man/3/ip
+fslib.b	/man/1/fs
+fsproto	/man/2/fsproto
+fsproto	/man/6/proto
+fsproto.b	/man/2/fsproto
+fsproto.m	/man/2/fsproto
+fstat	/man/2/sys-0intro
+fstat	/man/2/sys-stat
+fstat	/man/4/dbfs
+fstat	/man/5/stat
+fstypes.b	/man/1/alphabet-fs
+fsys	/man/3/logfs
+ft	/man/1/miniterm
+ft	/man/10/plan9.ini
+ftab	/man/10/styxserver
+ftest	/man/1/ftest
+ftest	/man/8/prep
+ftest.b	/man/1/ftest
+ftl	/man/1/sh-test
+ftl	/man/3/flash
+ftl	/man/3/ftl
+ftl	/man/3/logfs
+ftl	/man/4/kfs
+ftl	/man/8/ftl
+ftl	/man/8/init
+ftl	/man/8/touchcal
+ftl.b	/man/8/ftl
+ftlctl	/man/3/ftl
+ftldata	/man/3/ftl
+ftldata	/man/8/ftl
+ftp	/man/1/charon
+ftp	/man/2/factotum
+ftp	/man/2/w3c-uris
+ftp	/man/4/9srvfs
+ftp	/man/4/ftpfs
+ftp.vitanuova.com	/man/4/ftpfs
+ftpfs	/man/1/ns
+ftpfs	/man/4/ftpfs
+ftpfs	/man/6/ndb
+ftpfs.b	/man/4/ftpfs
+ftphost	/man/4/ftpfs
+ftree	/man/1/collab
+ftree	/man/1/ftree
+fuchsia	/man/9/types
+fuid	/man/2/styxservers
+fulful	/man/9/0intro
+full	/man/1/0intro
+full	/man/1/9win
+full	/man/1/acme
+full	/man/1/alphabet-fs
+full	/man/1/charon
+full	/man/1/cp
+full	/man/1/dd
+full	/man/1/deb
+full	/man/1/emu
+full	/man/1/fs
+full	/man/1/ftree
+full	/man/1/mash-tk
+full	/man/1/stream
+full	/man/1/yacc
+full	/man/10/2c
+full	/man/10/dev
+full	/man/10/odbc
+full	/man/10/plan9.ini
+full	/man/10/qio
+full	/man/10/rune
+full	/man/10/styx
+full	/man/2/asn1
+full	/man/2/complete
+full	/man/2/debug
+full	/man/2/dhcpclient
+full	/man/2/dial
+full	/man/2/ip
+full	/man/2/keyset
+full	/man/2/pop3
+full	/man/2/prof
+full	/man/2/styxservers-nametree
+full	/man/2/sys-dial
+full	/man/2/w3c-xpointers
+full	/man/2/wmclient
+full	/man/3/eia
+full	/man/3/pipe
+full	/man/3/plap
+full	/man/3/vga
+full	/man/4/grid-cpu
+full	/man/5/walk
+full	/man/6/colour
+full	/man/6/dis
+full	/man/6/keyboard
+full	/man/8/create
+full	/man/9/0intro
+full	/man/9/text
+full	/man/9/types
+fullduplex	/man/10/plan9.ini
+fullhdr	/man/2/prof
+fullrune	/man/10/rune
+fully	/man/1/acme
+fully	/man/1/charon
+fully	/man/1/sh-alphabet
+fully	/man/1/yacc
+fully	/man/10/2c
+fully	/man/2/draw-display
+fully	/man/2/draw-image
+fully	/man/2/tkclient
+fully	/man/2/wmclient
+fully	/man/3/draw
+fully	/man/4/acme
+fully	/man/6/colour
+fully	/man/8/bootpd
+fume	/man/10/odbc
+fun1	/man/2/hash
+fun2	/man/2/hash
+funame	/man/2/styxservers
+func	/man/10/acid
+func	/man/10/kproc
+func	/man/2/draw-image
+function's	/man/2/debug
+function's	/man/2/dis
+function's	/man/6/dis
+function's	/man/6/sbl
+functionality	/man/1/0intro
+functionality	/man/1/mash
+functionality	/man/1/mash-make
+functionality	/man/1/sh
+functionality	/man/1/sh-string
+functionality	/man/1/tiny
+functionality	/man/2/math-linalg
+functionality	/man/2/sh
+functionality	/man/2/spree-gather
+functionality	/man/2/styxservers
+functionality	/man/2/wmsrv
+functionality	/man/3/logfs
+functionality	/man/4/spree
+functionally	/man/1/sh
+functionally	/man/1/sh-std
+functioning	/man/2/sys-pctl
+functionname	/man/2/w3c-xpointers
+fundamental	/man/10/intrenable
+fundamental	/man/10/master
+fundamental	/man/2/ida
+fundamental	/man/2/math-0intro
+fundamental	/man/2/sys-0intro
+fundamental	/man/3/draw
+fundamental	/man/3/prog
+fundamental	/man/6/sexprs
+funprof	/man/2/prof
+funtab	/man/2/prof
+further	/man/1/acme
+further	/man/1/collab-clients
+further	/man/1/miniterm
+further	/man/1/tail
+further	/man/1/wish
+further	/man/10/9load
+further	/man/10/acid
+further	/man/10/dmainit
+further	/man/10/dynld
+further	/man/10/error
+further	/man/10/intrenable
+further	/man/10/master
+further	/man/10/newchan
+further	/man/10/plan9.ini
+further	/man/10/styxserver
+further	/man/2/bufio
+further	/man/2/exception
+further	/man/2/palmfile
+further	/man/2/prefab-0intro
+further	/man/2/prof
+further	/man/2/sexprs
+further	/man/2/spki
+further	/man/2/spki-verifier
+further	/man/2/styxservers
+further	/man/2/sys-0intro
+further	/man/2/w3c-uris
+further	/man/3/cap
+further	/man/3/cmd
+further	/man/3/draw
+further	/man/3/env
+further	/man/3/prof
+further	/man/3/tls
+further	/man/3/usb
+further	/man/4/factotum
+further	/man/5/open
+further	/man/5/stat
+further	/man/5/version
+further	/man/6/plumbing
+further	/man/8/create
+further	/man/8/cs
+further	/man/8/svc
+further	/man/9/pack
+furthermore	/man/1/collab-clients
+furthermore	/man/1/ns
+furthermore	/man/10/0intro
+furthermore	/man/2/command
+furthermore	/man/2/security-auth
+furthermore	/man/2/sexprs
+furthermore	/man/3/logfs
+furthermore	/man/4/keyfs
+furthermore	/man/6/image
+furthermore	/man/6/sbl
+furthermore	/man/6/sexprs
+furthermore	/man/9/canvas
+furthermore	/man/9/text
+futher	/man/1/grid-session
+future	/man/1/acme
+future	/man/3/prog
+future	/man/3/tls
+future	/man/8/svc
+future	/man/9/bind
+future	/man/9/canvas
+future	/man/9/entry
+future	/man/9/grab
+future	/man/9/send
+future	/man/9/text
+fversion	/man/2/sys-fversion
+fversion	/man/5/version
+fvw	/man/10/2c
+fwd	/man/2/spree-cardlib
+fwstat	/man/2/sys-0intro
+fwstat	/man/2/sys-stat
+fwstat	/man/5/stat
+fx	/man/10/plan9.ini
+fxfd	/man/10/plan9.ini
+g.dis	/man/2/sys-self
+g800x600	/man/1/emu
+gafr	/man/3/gpio
+gain	/man/2/sys-pctl
+gamay	/man/10/odbc
+game	/man/1/wm-misc
+game	/man/2/spree
+game	/man/2/spree-allow
+game	/man/2/spree-cardlib
+game	/man/2/spree-gather
+game	/man/4/spree
+games	/man/1/fortune
+games	/man/2/spree-cardlib
+games	/man/6/ndb
+gamma	/man/1/calc
+gamma	/man/2/math-elem
+gamma	/man/6/colour
+gap	/man/1/grid-session
+gap	/man/9/text
+garbage	/man/2/draw-0intro
+garbage	/man/2/registries
+garbage	/man/2/sys-dup
+garbage	/man/5/clunk
+garbled	/man/2/factotum
+gary	/man/2/palmfile
+gate	/man/1/alphabet-fs
+gate	/man/1/fs
+gate	/man/10/plan9.ini
+gateway	/man/1/miniterm
+gateway	/man/2/dhcpclient
+gateway	/man/3/ip
+gateway	/man/6/ndb
+gateway	/man/8/bootpd
+gateway	/man/8/dhcp
+gateway	/man/8/rip
+gateway	/man/9/send
+gateways	/man/8/rip
+gather	/man/1/alphabet-fs
+gather	/man/1/fs
+gather	/man/2/disks
+gather	/man/2/draw-image
+gather	/man/2/prof
+gather	/man/2/spree-gather
+gather	/man/4/iostats
+gather.m	/man/2/spree-gather
+gathered	/man/1/alphabet-fs
+gathered	/man/1/fs
+gathered	/man/2/prof
+gatherengine	/man/2/spree-gather
+gathering	/man/1/sh-std
+gbit16	/man/10/styx
+gbit32	/man/10/styx
+gbit64	/man/10/styx
+gbit8	/man/10/styx
+gc	/man/3/vga
+ge	/man/1/sh-expr
+gedr	/man/3/gpio
+gemm	/man/2/math-linalg
+gen	/man/10/devattach
+gen	/man/8/applylog
+genbuf	/man/10/devattach
+gendraw	/man/2/draw-image
+gendrawop	/man/2/draw-image
+generalizedtime	/man/2/asn1
+generally	/man/1/0intro
+generally	/man/1/charon
+generally	/man/10/splhi
+generally	/man/2/draw-0intro
+generally	/man/2/styx
+generally	/man/2/styxservers
+generally	/man/2/wmsrv
+generally	/man/3/ip
+generally	/man/3/sign
+generally	/man/6/keyboard
+generally	/man/6/ndb
+generally	/man/8/bootpd
+generalstring	/man/2/asn1
+generate	/man/1/asm
+generate	/man/1/limbo
+generate	/man/1/sh-csv
+generate	/man/1/sh-sexprs
+generate	/man/10/2c
+generate	/man/10/2l
+generate	/man/10/c2l
+generate	/man/10/print
+generate	/man/10/styxserver
+generate	/man/2/draw-0intro
+generate	/man/2/keyring-0intro
+generate	/man/2/keyring-gensk
+generate	/man/2/styxservers
+generate	/man/3/cons
+generate	/man/3/pipe
+generate	/man/4/mntgen
+generate	/man/4/spree
+generate	/man/5/0intro
+generate	/man/5/open
+generate	/man/5/read
+generate	/man/5/version
+generate	/man/6/keyboard
+generate	/man/8/ai2key
+generate	/man/8/createsignerkey
+generate	/man/9/options
+generated	/man/1/acme
+generated	/man/1/asm
+generated	/man/1/charon
+generated	/man/1/cook
+generated	/man/1/itest
+generated	/man/1/mux
+generated	/man/1/sh-string
+generated	/man/1/wm
+generated	/man/1/wm-sh
+generated	/man/1/xd
+generated	/man/1/zeros
+generated	/man/10/c2l
+generated	/man/10/conf
+generated	/man/10/devattach
+generated	/man/10/master
+generated	/man/2/draw-image
+generated	/man/2/ida
+generated	/man/2/keyring-0intro
+generated	/man/2/keyring-auth
+generated	/man/2/math-0intro
+generated	/man/2/security-login
+generated	/man/2/sh
+generated	/man/2/tk
+generated	/man/3/ip
+generated	/man/3/prog
+generated	/man/3/touch
+generated	/man/4/factotum
+generated	/man/4/spree
+generated	/man/5/attach
+generated	/man/5/clunk
+generated	/man/5/read
+generated	/man/5/remove
+generated	/man/5/stat
+generated	/man/5/version
+generated	/man/5/walk
+generated	/man/6/plumbing
+generated	/man/8/ai2key
+generated	/man/8/collabsrv
+generated	/man/9/bind
+generated	/man/9/canvas
+generated	/man/9/image
+generated	/man/9/scrollbar
+generates	/man/1/math-misc
+generates	/man/1/wm
+generates	/man/10/newchan
+generates	/man/10/plan9.ini
+generates	/man/2/keyring-gensk
+generates	/man/2/sys-byte2char
+generates	/man/5/attach
+generates	/man/5/open
+generates	/man/6/keyboard
+generates	/man/8/collabsrv
+generates	/man/9/text
+generating	/man/10/2c
+generating	/man/2/keyring-0intro
+generating	/man/2/math-0intro
+generating	/man/2/palmfile
+generating	/man/2/plumbmsg
+generating	/man/2/sh
+generating	/man/2/styx
+generating	/man/4/0intro
+generation	/man/1/cook
+generation	/man/1/limbo
+generation	/man/10/2c
+generation	/man/2/pslib
+generation	/man/2/rand
+generation	/man/2/security-random
+generator	/man/1/yacc
+generator	/man/2/keyring-ipint
+generator	/man/2/keyring-rc4
+generator	/man/2/rand
+generator	/man/8/ai2key
+generators	/man/1/disdep
+generic	/man/10/plan9.ini
+generic	/man/2/alphabet-intro
+generic	/man/2/convcs
+generic	/man/2/disks
+generic	/man/2/rfc822
+generic	/man/2/w3c-uris
+generic	/man/2/xml
+generic	/man/6/proto
+generic	/man/8/mkfs
+generically	/man/2/0intro
+generous	/man/1/units
+genprimes	/man/1/math-misc
+genprimes.b	/man/1/math-misc
+gensk	/man/2/keyring-0intro
+gensk	/man/2/keyring-gensk
+gensk	/man/2/keyset
+gensk	/man/2/security-login
+gensk	/man/8/createsignerkey
+genskfrompk	/man/2/keyring-gensk
+geodesy	/man/2/geodesy
+geodesy.b	/man/2/geodesy
+geodesy.m	/man/2/geodesy
+geodetic	/man/2/geodesy
+geom	/man/1/filename
+geom	/man/1/mash-tk
+geometric	/man/2/draw-0intro
+geometric	/man/2/draw-image
+geometrical	/man/2/draw-image
+geometries	/man/2/disks
+geometry	/man/2/disks
+geometry	/man/2/prefab-element
+geometry	/man/3/sd
+geometry	/man/9/canvas
+geometry	/man/9/grid
+geometry	/man/9/options
+geometry	/man/9/pack
+george	/man/9/grid
+gestures	/man/1/keyboard
+get2	/man/2/palmfile
+get3	/man/2/palmfile
+get4	/man/2/palmfile
+getall	/man/2/env
+getarchivearray	/man/2/spree-cardlib
+getarchiveobj	/man/2/spree-cardlib
+getattr	/man/2/spree
+getauthinfo	/man/1/bind
+getauthinfo	/man/1/rcmd
+getauthinfo	/man/2/keyset
+getauthinfo	/man/2/registries
+getauthinfo	/man/2/security-login
+getauthinfo	/man/6/keytext
+getauthinfo	/man/8/changelogin
+getauthinfo	/man/8/getauthinfo
+getauthinfo	/man/8/rstyxd
+getauthinfo	/man/8/svc
+getauthinfo.b	/man/8/getauthinfo
+getb	/man/2/bufio
+getbtos	/man/2/convcs
+getbytearray	/man/2/keyring-getstring
+getbytearray	/man/2/msgio
+getc	/man/2/bufio
+getc	/man/2/rfc822
+getcard	/man/2/spree-cardlib
+getcards	/man/2/spree-cardlib
+getcsv	/man/1/sh-csv
+getenv	/man/2/env
+getexc	/man/2/exception
+getfid	/man/2/styxservers
+getfields	/man/10/getfields
+getfields	/man/10/parsecmd
+getfields.c	/man/10/getfields
+getfile	/man/2/secstore
+getfpcontrol	/man/2/math-fp
+getfpstatus	/man/2/math-fp
+gethostbyname	/man/2/srv
+getimage	/man/2/tk
+getint	/man/2/dhcpclient
+getip	/man/2/dhcpclient
+getips	/man/2/dhcpclient
+getkeys	/man/2/cfg
+getline	/man/1/read
+getline	/man/2/csv
+getlines	/man/1/sh-csv
+getlines	/man/1/sh-sexprs
+getlines	/man/1/sh-std
+getmsg	/man/2/keyring-getmsg
+getmsg	/man/2/msgio
+getmsg	/man/6/keytext
+gets	/man/1/sh
+gets	/man/1/sh-expr
+gets	/man/10/atoi
+gets	/man/2/bufio
+gets	/man/2/dhcpclient
+gets	/man/2/palmfile
+gets	/man/2/spree-cardlib
+gets	/man/5/0intro
+gets	/man/8/prep
+getself	/man/2/sh
+getsexprs	/man/1/sh-sexprs
+getstob	/man/2/convcs
+getstring	/man/2/dialog
+getstring	/man/2/keyring-0intro
+getstring	/man/2/keyring-getstring
+getstring	/man/2/msgio
+gett	/man/2/bufio
+gettags	/man/9/canvas
+gettar	/man/1/gettar
+gettar	/man/4/tarfs
+gettar.b	/man/1/gettar
+getting	/man/2/styxflush
+getting	/man/2/ubfa
+getting	/man/6/ubfa
+getting	/man/9/bind
+getuserpassd	/man/2/factotum
+getuserpasswd	/man/2/factotum
+getvalue	/man/9/choicebutton
+gewurztraminer	/man/10/odbc
+gfer	/man/3/gpio
+gg	/man/9/types
+gh	/man/3/tv
+ghost	/man/10/panic
+gid	/man/1/ar
+gid	/man/10/ar
+gid	/man/10/devattach
+gid	/man/10/iar
+gid	/man/2/styxservers
+gid	/man/2/sys-0intro
+gid	/man/2/sys-stat
+gid	/man/3/fs
+gid	/man/3/logfs
+gid	/man/5/stat
+gid	/man/6/proto
+gid	/man/8/applylog
+gid	/man/8/create
+gid	/man/8/styxchat
+gif	/man/1/charon
+gif	/man/1/wm-misc
+gif	/man/2/imagefile
+gif87a	/man/1/charon
+gif89a	/man/1/charon
+gifreader	/man/2/imagefile
+gifs	/man/1/charon
+gigabit	/man/10/plan9.ini
+gigabytes	/man/10/plan9.ini
+gl	/man/3/tv
+glean	/man/2/debug
+global	/man/1/deb
+global	/man/1/mash
+global	/man/1/sh-mload
+global	/man/1/yacc
+global	/man/10/2c
+global	/man/10/dev
+global	/man/10/dynld
+global	/man/10/inm
+global	/man/10/kbdputc
+global	/man/10/qlock
+global	/man/10/readnum
+global	/man/2/alphabet-intro
+global	/man/2/debug
+global	/man/2/dis
+global	/man/2/spree-objstore
+global	/man/2/sys-0intro
+global	/man/3/dbg
+global	/man/3/draw
+global	/man/3/prof
+global	/man/3/prog
+global	/man/6/dis
+global	/man/6/sbl
+global	/man/9/checkbutton
+global	/man/9/choicebutton
+global	/man/9/menu
+global	/man/9/radiobutton
+globally	/man/1/0intro
+globals	/man/10/styxserver
+gm	/man/2/styxservers
+gm	/man/2/styxservers-nametree
+gmt	/man/1/date
+gmt	/man/10/seconds
+gmt	/man/2/daytime
+gmt	/man/2/sys-stat
+gmt	/man/3/cons
+gmt	/man/3/rtc
+gmt	/man/5/stat
+gmt	/man/6/keytext
+goal	/man/10/print
+goal	/man/2/sys-print
+goals	/man/2/security-0intro
+goes	/man/10/ref
+goes	/man/2/draw-image
+goes	/man/2/styxconv
+goes	/man/2/styxpersist
+goes	/man/2/sys-bind
+goes	/man/2/sys-open
+goes	/man/2/xml
+goes	/man/4/9srvfs
+goes	/man/4/keysrv
+goes	/man/8/prep
+gone	/man/1/blur
+gone	/man/1/grid-register
+gone	/man/3/srv
+goo	/man/1/0intro
+goodbye	/man/1/timestamp
+goto	/man/10/sleep
+goto	/man/2/xml
+gotos	/man/10/c2l
+gotos	/man/10/error
+govern	/man/1/sh-std
+governing	/man/2/math-fp
+gpcr	/man/3/gpio
+gpdr	/man/3/gpio
+gpio	/man/3/gpio
+gpioclear	/man/3/gpio
+gpioctl	/man/3/gpio
+gpioedge	/man/3/gpio
+gpioset	/man/3/gpio
+gpiostatus	/man/3/gpio
+gplr	/man/3/gpio
+gpr	/man/10/acid
+gpsr	/man/3/gpio
+grab	/man/1/wm-misc
+grab	/man/2/debug
+grab	/man/9/0intro
+grab	/man/9/canvas
+grab	/man/9/grab
+grab	/man/9/listbox
+grabbed	/man/9/canvas
+grabbed	/man/9/grab
+grabbing	/man/9/grab
+grabbing	/man/9/listbox
+grabs	/man/9/grab
+gradually	/man/1/charon
+gradually	/man/10/panic
+grained	/man/1/wm-misc
+gram.y	/man/10/mk
+grammar	/man/1/yacc
+grammar	/man/2/rfc822
+grammar	/man/2/w3c-css
+grammar	/man/2/w3c-xpointers
+grammar	/man/6/ubfa
+grammars	/man/10/mk
+gran	/man/2/diskblocks
+grant	/man/5/0intro
+grant	/man/9/1copyright
+granted	/man/1/ls
+granted	/man/2/spki
+granted	/man/2/styxservers
+granted	/man/5/open
+granting	/man/10/eve
+granting	/man/5/attach
+granularity	/man/2/diskblocks
+granularity	/man/2/timers
+graph	/man/1/mk
+graph	/man/1/tsort
+graph	/man/10/mk
+graph	/man/3/kprof
+graphic	/man/10/plan9.ini
+graphical	/man/1/0intro
+graphical	/man/1/9win
+graphical	/man/1/alphabet-fs
+graphical	/man/1/charon
+graphical	/man/1/cprof
+graphical	/man/1/deb
+graphical	/man/1/ebook
+graphical	/man/1/fs
+graphical	/man/1/ftree
+graphical	/man/1/grid-monitor
+graphical	/man/1/grid-ns
+graphical	/man/1/grid-query
+graphical	/man/1/grid-session
+graphical	/man/1/man
+graphical	/man/1/mash-tk
+graphical	/man/1/wm-misc
+graphical	/man/1/wm-sh
+graphical	/man/2/draw-display
+graphical	/man/3/draw
+graphical	/man/4/grid-cpu
+graphical	/man/6/man
+graphical	/man/8/register
+graphical	/man/9/canvas
+graphically	/man/1/cprof
+graphically	/man/8/signer
+graphics	/man/1/emu
+graphics	/man/1/mux
+graphics	/man/1/sh-tk
+graphics	/man/1/wish
+graphics	/man/2/command
+graphics	/man/2/dialog
+graphics	/man/2/draw-0intro
+graphics	/man/2/draw-context
+graphics	/man/2/draw-display
+graphics	/man/2/draw-image
+graphics	/man/2/ir
+graphics	/man/2/prefab-0intro
+graphics	/man/2/prefab-environ
+graphics	/man/2/selectfile
+graphics	/man/2/sh
+graphics	/man/2/tk
+graphics	/man/3/cons
+graphics	/man/3/draw
+graphics	/man/3/vga
+graphics	/man/6/colour
+graphics	/man/9/canvas
+graphics	/man/9/panel
+graphics	/man/9/update
+graphicstring	/man/2/asn1
+graphing	/man/1/disdep
+grave	/man/1/m4
+gravity	/man/1/units
+gravity	/man/9/text
+gray	/man/9/types
+gre	/man/3/ip
+greater	/man/1/emu
+greater	/man/1/itest
+greater	/man/1/sh-expr
+greater	/man/1/sh-test
+greater	/man/1/yacc
+greater	/man/10/dev
+greater	/man/10/memory
+greater	/man/10/strcat
+greater	/man/10/styx
+greater	/man/2/bloomfilter
+greater	/man/2/draw-0intro
+greater	/man/2/itslib
+greater	/man/2/math-fp
+greater	/man/2/palmfile
+greater	/man/2/spree-cardlib
+greater	/man/2/timers
+greater	/man/3/cons
+greater	/man/3/dbg
+greater	/man/3/ssl
+greater	/man/4/kfs
+greater	/man/5/0intro
+greater	/man/5/read
+greater	/man/5/walk
+greater	/man/6/image
+greater	/man/6/sbl
+greater	/man/7/db
+greater	/man/8/applylog
+greater	/man/9/canvas
+greater	/man/9/grid
+greater	/man/9/scale
+greatest	/man/9/0intro
+greek	/man/2/draw-0intro
+greek	/man/2/draw-font
+greek	/man/6/keyboard
+green	/man/1/tktester
+green	/man/2/draw-0intro
+green	/man/2/draw-display
+green	/man/3/draw
+green	/man/3/pbus
+green	/man/3/tv
+green	/man/6/colour
+green	/man/6/image
+green	/man/9/types
+greenwich	/man/1/date
+greenwich	/man/2/daytime
+grep	/man/1/acme
+grep	/man/1/grep
+grep	/man/1/look
+grep	/man/1/time
+grep	/man/2/arg
+grep	/man/3/pnp
+grep	/man/6/keyboard
+grep.b	/man/1/grep
+grer	/man/3/gpio
+grey	/man/1/tktester
+grey	/man/2/draw-display
+grey	/man/2/imagefile
+grey	/man/6/colour
+grey1	/man/2/draw-display
+grey2	/man/2/draw-display
+grey8	/man/2/draw-display
+greyblue	/man/2/draw-display
+greygreen	/man/2/draw-display
+greyscale	/man/1/emu
+greyscale	/man/2/draw-0intro
+greyscale	/man/2/draw-display
+greyscale	/man/2/draw-image
+greyscale	/man/6/colour
+greyscale	/man/6/image
+grid	/man/1/alphabet-abc
+grid	/man/1/alphabet-grid
+grid	/man/1/blur
+grid	/man/1/grid-monitor
+grid	/man/1/grid-ns
+grid	/man/1/grid-query
+grid	/man/1/grid-register
+grid	/man/1/grid-session
+grid	/man/1/sh-alphabet
+grid	/man/1/tktester
+grid	/man/2/alphabet-intro
+grid	/man/2/draw-0intro
+grid	/man/2/draw-point
+grid	/man/2/draw-rect
+grid	/man/2/geodesy
+grid	/man/4/grid-cpu
+grid	/man/4/registry
+grid	/man/9/0intro
+grid	/man/9/grid
+grid.b	/man/1/alphabet-abc
+grid.b	/man/1/alphabet-grid
+grid.m	/man/2/alphabet-intro
+gridbag	/man/9/grid
+gridspacing	/man/9/canvas
+gridtypes.b	/man/1/alphabet-abc
+gridtypes.b	/man/1/alphabet-grid
+grim	/man/10/allocb
+grinstead	/man/6/attrdb
+grip	/man/6/attrdb
+groove	/man/9/options
+groove	/man/9/types
+group's	/man/5/0intro
+grouped	/man/1/acme
+grouped	/man/1/sh
+grouped	/man/2/prefab-element
+grouped	/man/2/spree-gather
+grouping	/man/1/acme
+grouping	/man/1/calc
+grouping	/man/1/mash
+grouping	/man/1/units
+grouping	/man/9/menu
+groups	/man/1/ar
+groups	/man/1/ls
+groups	/man/1/m4
+groups	/man/10/iar
+groups	/man/2/attrdb
+groups	/man/2/dividers
+groups	/man/2/draw-0intro
+groups	/man/2/json
+groups	/man/2/prefab-0intro
+groups	/man/2/spree-gather
+groups	/man/2/sys-0intro
+groups	/man/2/sys-stat
+groups	/man/2/ubfa
+groups	/man/3/logfs
+groups	/man/3/vid
+groups	/man/5/0intro
+groups	/man/5/stat
+groups	/man/6/attrdb
+groups	/man/6/users
+groups	/man/9/menu
+groups	/man/9/menubutton
+grow	/man/1/ebook
+grow	/man/9/grid
+growing	/man/10/a.out
+grows	/man/1/acme
+grows	/man/1/logwindow
+grows	/man/1/tail
+gt	/man/1/sh-expr
+guarantee	/man/1/limbo
+guarantee	/man/10/qlock
+guarantee	/man/10/rune
+guarantee	/man/2/keyring-0intro
+guarantee	/man/2/security-0intro
+guarantee	/man/2/sys-0intro
+guarantee	/man/3/pipe
+guarantee	/man/4/acme
+guarantee	/man/5/stat
+guarantee	/man/9/menu
+guaranteed	/man/1/pwd
+guaranteed	/man/10/memory
+guaranteed	/man/10/qlock
+guaranteed	/man/10/xalloc
+guaranteed	/man/2/alphabet-intro
+guaranteed	/man/2/math-0intro
+guaranteed	/man/2/sys-0intro
+guaranteed	/man/2/sys-open
+guaranteed	/man/2/sys-read
+guaranteed	/man/2/sys-stat
+guaranteed	/man/2/wmsrv
+guaranteed	/man/3/pipe
+guaranteed	/man/3/prof
+guaranteed	/man/5/open
+guaranteed	/man/5/read
+guaranteed	/man/9/canvas
+guarantees	/man/1/blur
+guarantees	/man/10/qio
+guarantees	/man/10/strcat
+guarantees	/man/4/spree
+guarantees	/man/9/bind
+guard	/man/10/lock
+guarding	/man/4/lockfs
+guess	/man/1/passwd
+guess	/man/2/disks
+guess	/man/2/keyring-0intro
+guest	/man/4/ftpfs
+gui	/man/1/0intro
+gui	/man/1/ebook
+gui	/man/9/grab
+guide	/man/1/acme
+guide	/man/1/ebook
+guide	/man/1/wm-misc
+guide	/man/2/palmfile
+guide	/man/4/namespace
+guiding	/man/6/keyboard
+gunzip	/man/1/gzip
+gunzip.b	/man/1/gzip
+gux	/man/1/cp
+gw	/man/3/ip
+gwip	/man/3/ip
+gz	/man/1/gzip
+gzip	/man/1/ar
+gzip	/man/1/gzip
+gzip	/man/10/5cv
+gzip	/man/10/9load
+gzip	/man/2/filter
+gzip	/man/2/filter-deflate
+gzip.b	/man/1/gzip
+h:self	/man/2/hash
+ha	/man/2/keyring-0intro
+ha	/man/2/keyring-sha1
+hair	/man/8/touchcal
+halfway	/man/9/listbox
+halfway	/man/9/text
+halg	/man/2/spki
+hall	/man/1/yacc
+halo	/man/9/canvas
+halt	/man/1/deb
+halt	/man/10/acid
+halt	/man/10/panic
+halt	/man/3/cons
+halt	/man/8/kfscmd
+halted	/man/1/deb
+halted	/man/3/tls
+halts	/man/10/panic
+halts	/man/8/shutdown
+handbook	/man/3/gpio
+handbook's	/man/3/gpio
+handed	/man/10/memory
+handed	/man/10/readnum
+handheld	/man/1/ftree
+handin	/man/3/tls
+handle	/man/1/charon
+handle	/man/10/2a
+handle	/man/10/2c
+handle	/man/10/odbc
+handle	/man/10/qio
+handle	/man/2/asn1
+handle	/man/2/dbm
+handle	/man/2/draw-screen
+handle	/man/2/print
+handle	/man/2/sh
+handle	/man/2/styxflush
+handle	/man/2/styxservers
+handle	/man/2/xml
+handle	/man/3/prog
+handle	/man/4/dossrv
+handle	/man/5/0intro
+handle	/man/6/utf
+handle	/man/7/db
+handle	/man/9/text
+handled	/man/1/dd
+handled	/man/1/units
+handled	/man/10/conf
+handled	/man/10/qio
+handled	/man/2/cfg
+handled	/man/2/exception
+handled	/man/2/styxflush
+handled	/man/2/sys-export
+handled	/man/2/w3c-uris
+handled	/man/3/cons
+handled	/man/3/ds
+handled	/man/5/flush
+handler	/man/1/mdb
+handler	/man/1/sh-std
+handler	/man/10/allocb
+handler	/man/10/intrenable
+handler	/man/10/lock
+handler	/man/10/qio
+handler	/man/10/sleep
+handler	/man/2/0intro
+handler	/man/2/dis
+handler	/man/2/styxflush
+handler	/man/2/tkclient
+handler	/man/6/dis
+handlers	/man/10/allocb
+handlers	/man/10/error
+handlers	/man/10/malloc
+handlers	/man/10/qio
+handlers	/man/10/splhi
+handlers	/man/2/dis
+handlers	/man/3/cons
+handlers	/man/3/kprof
+handlers	/man/6/dis
+handles	/man/1/tkcmd
+handles	/man/10/2c
+handles	/man/10/error
+handles	/man/2/asn1
+handles	/man/2/exception
+handles	/man/2/imagefile
+handles	/man/2/palmfile
+handles	/man/2/styxflush
+handles	/man/9/text
+handling	/man/1/brutus
+handling	/man/1/charon
+handling	/man/1/sh-regex
+handling	/man/1/sh-std
+handling	/man/10/error
+handling	/man/10/styx
+handling	/man/2/0intro
+handling	/man/2/draw-0intro
+handling	/man/2/exception
+handling	/man/2/styxflush
+handling	/man/2/styxservers
+handling	/man/3/draw
+handling	/man/9/canvas
+handout	/man/3/tls
+handshake	/man/2/venti
+handshake	/man/3/tls
+handshaking	/man/3/ssl
+handshaking	/man/3/tls
+handy	/man/1/wm-misc
+hang	/man/3/dbg
+hanging	/man/6/man
+hangs	/man/2/sys-export
+hangs	/man/3/ip
+hangup	/man/10/qio
+hangup	/man/2/spree
+hangup	/man/2/sys-open
+hangup	/man/3/eia
+hangup	/man/3/ip
+hangup	/man/4/spree
+happen	/man/2/0intro
+happen	/man/5/stat
+happen	/man/9/menu
+happen	/man/9/text
+happened	/man/1/wm
+happened	/man/10/sleep
+happened	/man/9/checkbutton
+happened	/man/9/choicebutton
+happened	/man/9/radiobutton
+happens	/man/1/bind
+happens	/man/1/sh
+happens	/man/1/sh-string
+happens	/man/10/master
+happens	/man/2/prof
+happens	/man/2/sys-bind
+happens	/man/9/menu
+happens	/man/9/text
+hard	/man/1/mdb
+hard	/man/1/passwd
+hard	/man/1/sh
+hard	/man/10/9load
+hard	/man/10/acid
+hard	/man/10/plan9.ini
+hard	/man/2/disks
+hard	/man/2/palmfile
+hard	/man/2/styxflush
+hard	/man/2/virgil
+hard	/man/8/prep
+hard	/man/8/virgild
+harder	/man/2/scsiio
+hardly	/man/1/asm
+hardware	/man/1/miniterm
+hardware	/man/1/mux
+hardware	/man/10/0intro
+hardware	/man/10/9load
+hardware	/man/10/conf
+hardware	/man/10/dev
+hardware	/man/10/dmainit
+hardware	/man/10/inb
+hardware	/man/10/intrenable
+hardware	/man/10/plan9.ini
+hardware	/man/10/xalloc
+hardware	/man/2/ether
+hardware	/man/2/math-0intro
+hardware	/man/3/arch
+hardware	/man/3/cons
+hardware	/man/3/ether
+hardware	/man/3/flash
+hardware	/man/3/logfs
+hardware	/man/3/mpeg
+hardware	/man/3/pbus
+hardware	/man/3/switch
+hardware	/man/3/vga
+hardware	/man/3/vid
+hardware	/man/8/bootpd
+hasattr	/man/2/attrdb
+hasauthority	/man/2/w3c-uris
+hasexcept	/man/6/dis
+hash	/man/1/alphabet-main
+hash	/man/1/secstore
+hash	/man/1/sum
+hash	/man/2/bloomfilter
+hash	/man/2/dbm
+hash	/man/2/dict
+hash	/man/2/dis
+hash	/man/2/hash
+hash	/man/2/ida
+hash	/man/2/keyring-0intro
+hash	/man/2/keyring-certtostr
+hash	/man/2/keyring-sha1
+hash	/man/2/keyset
+hash	/man/2/registries
+hash	/man/2/secstore
+hash	/man/2/security-0intro
+hash	/man/2/spki
+hash	/man/2/stringinttab
+hash	/man/2/venti
+hash	/man/2/w3c-css
+hash	/man/3/0intro
+hash	/man/3/cap
+hash	/man/3/dynld
+hash	/man/3/sign
+hash	/man/3/tls
+hash	/man/4/import
+hash	/man/4/keysrv
+hash	/man/6/dis
+hash	/man/6/keys
+hash	/man/6/keytext
+hash	/man/6/login
+hash	/man/7/cddb
+hash.b	/man/2/hash
+hash.m	/man/2/hash
+hashalg	/man/3/tls
+hashalg	/man/6/keytext
+hashalg	/man/8/ai2key
+hashalgs	/man/3/ssl
+hashalgs	/man/3/tls
+hashbytes	/man/2/spki
+hashed	/man/2/dbm
+hashed	/man/2/keyring-0intro
+hashed	/man/2/security-0intro
+hashed	/man/2/spki
+hashes	/man/2/secstore
+hashes	/man/2/spki
+hashexp	/man/2/spki
+hashin	/man/3/tls
+hashing	/man/2/dbm
+hashing	/man/2/factotum
+hashing	/man/2/hash
+hashing	/man/2/keyring-sha1
+hashing	/man/2/security-0intro
+hashing	/man/2/spki
+hashing	/man/3/ssl
+hashing	/man/3/tls
+hashnode	/man/2/hash
+hashout	/man/3/tls
+hashtable	/man/2/hash
+hashval	/man/2/hash
+haskbdfocus	/man/1/wm
+hasldt	/man/6/dis
+hasn't	/man/10/styxserver
+hasn't	/man/9/canvas
+hasn't	/man/9/text
+haspair	/man/2/attrdb
+hauppage	/man/3/tv
+haveai	/man/4/factotum
+haven't	/man/9/text
+hc	/man/3/sd
+hd	/man/1/mash
+hd	/man/1/sh-std
+hd	/man/10/plan9.ini
+hd	/man/2/arg
+hd	/man/2/debug
+hd	/man/2/dial
+hd	/man/2/security-auth
+hd	/man/2/sh
+hd	/man/2/sys-tokenize
+hd0disk	/man/4/dossrv
+hdr	/man/10/ar
+headed	/man/10/allocb
+headed	/man/10/qio
+headed	/man/2/spki-verifier
+header	/man/1/auplay
+header	/man/1/crypt
+header	/man/1/man
+header	/man/1/mdb
+header	/man/1/sendmail
+header	/man/1/sh
+header	/man/1/uuencode
+header	/man/1/wm-misc
+header	/man/10/0intro
+header	/man/10/2c
+header	/man/10/2l
+header	/man/10/5cv
+header	/man/10/9load
+header	/man/10/a.out
+header	/man/10/ar
+header	/man/10/c2l
+header	/man/10/ms2
+header	/man/10/styx
+header	/man/2/alphabet-intro
+header	/man/2/asn1
+header	/man/2/dis
+header	/man/2/filter-deflate
+header	/man/2/keyring-getmsg
+header	/man/2/msgio
+header	/man/2/rfc822
+header	/man/2/spree
+header	/man/3/ether
+header	/man/3/ftl
+header	/man/3/ip
+header	/man/3/pbus
+header	/man/3/sign
+header	/man/3/tv
+header	/man/6/audio
+header	/man/6/dis
+header	/man/6/font
+header	/man/6/image
+header	/man/6/sbl
+header	/man/8/collabsrv
+header	/man/8/create
+headerless	/man/10/5cv
+headers	/man/10/allocb
+headers	/man/10/inm
+headers	/man/2/rfc822
+headers	/man/2/secstore
+headers	/man/3/ip
+headers	/man/3/pbus
+headers	/man/3/tls
+headers	/man/4/iostats
+headers	/man/8/create
+headers	/man/8/httpd
+headers	/man/8/mkfs
+headers4	/man/3/ip
+heading	/man/1/acme
+heading	/man/1/cook
+heads	/man/2/command
+heads	/man/2/disks
+headstr	/man/10/acid
+heap	/man/1/emu
+heap	/man/1/mprof
+heap	/man/1/ps
+heap	/man/1/wm-misc
+heap	/man/2/0intro
+heap	/man/2/sys-0intro
+heap	/man/3/prof
+heap	/man/3/prog
+heap	/man/4/memfs
+heart	/man/1/0intro
+hearts	/man/2/spree-cardlib
+heavily	/man/2/geodesy
+hectometre	/man/2/geodesy
+height	/man/1/9win
+height	/man/1/charon
+height	/man/1/emu
+height	/man/1/units
+height	/man/1/wm-sh
+height	/man/2/dividers
+height	/man/2/draw-display
+height	/man/2/draw-font
+height	/man/2/prefab-element
+height	/man/2/print
+height	/man/2/pslib
+height	/man/3/vga
+height	/man/6/font
+height	/man/9/bind
+height	/man/9/button
+height	/man/9/canvas
+height	/man/9/checkbutton
+height	/man/9/choicebutton
+height	/man/9/entry
+height	/man/9/frame
+height	/man/9/grid
+height	/man/9/image
+height	/man/9/label
+height	/man/9/listbox
+height	/man/9/menubutton
+height	/man/9/options
+height	/man/9/pack
+height	/man/9/panel
+height	/man/9/radiobutton
+height	/man/9/scale
+height	/man/9/scrollbar
+height	/man/9/text
+height	/man/9/types
+heights	/man/1/acme
+heirarchical	/man/1/grid-session
+hellman	/man/2/keyring-0intro
+hellman	/man/2/keyring-auth
+hellman	/man/2/keyring-gensk
+hellman	/man/2/security-0intro
+hellman	/man/6/auth
+hellman	/man/6/login
+hellman	/man/8/ai2key
+hello	/man/1/calc
+hello	/man/1/listen
+hello	/man/1/sh
+hello	/man/1/sh-alphabet
+hello	/man/1/timestamp
+hello	/man/1/tiny
+hello	/man/2/draw-example
+hello	/man/6/sexprs
+helmert	/man/2/geodesy
+help.html	/man/1/charon
+helper	/man/2/alphabet-intro
+helper	/man/2/wmsrv
+helpers	/man/2/rfc822
+helpful	/man/1/unicode
+helpful	/man/8/cs
+helpful	/man/8/rip
+helps	/man/2/disks
+helps	/man/2/registries
+helps	/man/2/styxservers
+helps	/man/2/wait
+helps	/man/8/changelogin
+helpurl	/man/1/charon
+henc	/man/2/spki
+hence	/man/10/9load
+hence	/man/10/intrenable
+hence	/man/10/plan9.ini
+hence	/man/2/draw-0intro
+hence	/man/2/msgio
+hence	/man/2/sets
+hence	/man/5/0intro
+hence	/man/9/scale
+henceforth	/man/1/bind
+henceforth	/man/1/sh-tk
+henceforth	/man/2/sys-bind
+hereby	/man/9/1copyright
+heuristics	/man/2/disks
+hex	/man/1/freq
+hex	/man/1/mdb
+hex	/man/1/unicode
+hex	/man/2/sys-print
+hex	/man/2/w3c-css
+hex	/man/3/pnp
+hex	/man/4/factotum
+hex	/man/6/json
+hex	/man/6/sexprs
+hex	/man/8/ai2key
+hex	/man/9/types
+hexadecimal	/man/1/cmp
+hexadecimal	/man/1/fc
+hexadecimal	/man/1/mdb
+hexadecimal	/man/1/strings
+hexadecimal	/man/1/sum
+hexadecimal	/man/1/tr
+hexadecimal	/man/1/unicode
+hexadecimal	/man/1/xd
+hexadecimal	/man/10/2l
+hexadecimal	/man/10/atoi
+hexadecimal	/man/10/inm
+hexadecimal	/man/10/print
+hexadecimal	/man/2/encoding
+hexadecimal	/man/2/ether
+hexadecimal	/man/2/ip
+hexadecimal	/man/2/keyset
+hexadecimal	/man/2/registries
+hexadecimal	/man/2/sets
+hexadecimal	/man/2/sexprs
+hexadecimal	/man/2/sys-print
+hexadecimal	/man/2/w3c-uris
+hexadecimal	/man/3/arch
+hexadecimal	/man/3/dbg
+hexadecimal	/man/3/ether
+hexadecimal	/man/3/flash
+hexadecimal	/man/3/gpio
+hexadecimal	/man/3/i2c
+hexadecimal	/man/3/ip
+hexadecimal	/man/3/lpt
+hexadecimal	/man/3/pnp
+hexadecimal	/man/3/prog
+hexadecimal	/man/3/tls
+hexadecimal	/man/4/factotum
+hexadecimal	/man/4/keysrv
+hexadecimal	/man/5/0intro
+hexadecimal	/man/6/font
+hexadecimal	/man/6/keyboard
+hexadecimal	/man/6/sbl
+hexadecimal	/man/6/sexprs
+hexadecimal	/man/6/utf
+hexadecimal	/man/8/ai2key
+hexadecimal	/man/9/bind
+hexadecimal	/man/9/types
+hexcolour	/man/2/w3c-css
+hexdigit	/man/2/w3c-css
+hexdigits	/man/2/sets
+hexmax	/man/1/unicode
+hexmin	/man/1/unicode
+hh	/man/10/print
+hh	/man/6/sexprs
+hh:mm:ss	/man/2/spki
+hi	/man/1/sh
+hidden	/man/1/deb
+hidden	/man/1/logwindow
+hidden	/man/1/tktester
+hidden	/man/1/toolbar
+hidden	/man/2/factotum
+hidden	/man/2/keyring-0intro
+hide	/man/1/bind
+hide	/man/1/charon
+hide	/man/1/mash-make
+hide	/man/1/tktester
+hide	/man/2/sh
+hide	/man/2/sys-bind
+hide	/man/2/tkclient
+hide	/man/2/wmclient
+hide	/man/3/ftl
+hide	/man/4/ramfile
+hides	/man/3/flash
+hiding	/man/1/acme
+hierarchical	/man/1/0intro
+hierarchical	/man/1/alphabet-fs
+hierarchical	/man/1/deb
+hierarchical	/man/1/fs
+hierarchical	/man/1/sh-alphabet
+hierarchical	/man/2/debug
+hierarchical	/man/2/spree
+hierarchical	/man/2/styxservers-nametree
+hierarchical	/man/2/sys-0intro
+hierarchical	/man/3/logfs
+hierarchical	/man/4/kfs
+hierarchical	/man/5/0intro
+hierarchically	/man/2/draw-0intro
+hierarchically	/man/2/prefab-0intro
+hierarchies	/man/2/styxservers-nametree
+hierarchy	/man/1/alphabet-fs
+hierarchy	/man/1/collab
+hierarchy	/man/1/fs
+hierarchy	/man/10/devattach
+hierarchy	/man/10/master
+hierarchy	/man/2/drawmux
+hierarchy	/man/2/keyring-0intro
+hierarchy	/man/2/spree
+hierarchy	/man/2/spree-cardlib
+hierarchy	/man/2/spree-gather
+hierarchy	/man/2/spree-objstore
+hierarchy	/man/2/styxservers
+hierarchy	/man/2/styxservers-nametree
+hierarchy	/man/2/sys-0intro
+hierarchy	/man/3/cmd
+hierarchy	/man/5/0intro
+hierarchy	/man/5/walk
+hierarchy	/man/6/ndb
+hierarchy	/man/6/proto
+hierarchy	/man/9/grab
+hierarchy	/man/9/grid
+hierarchy	/man/9/pack
+higher	/man/1/charon
+higher	/man/1/gzip
+higher	/man/1/itest
+higher	/man/1/sh-alphabet
+higher	/man/10/kproc
+higher	/man/10/lock
+higher	/man/10/xalloc
+higher	/man/2/draw-0intro
+higher	/man/2/draw-image
+higher	/man/2/rfc822
+higher	/man/2/sets
+higher	/man/2/wmlib
+higher	/man/3/flash
+higher	/man/3/kprof
+higher	/man/3/pbus
+higher	/man/3/vga
+higher	/man/5/0intro
+higher	/man/6/utf
+higher	/man/8/bootpd
+higher	/man/9/canvas
+higher	/man/9/text
+highest	/man/1/calc
+highest	/man/1/itest
+highest	/man/1/mash
+highest	/man/10/a.out
+highest	/man/10/kproc
+highest	/man/10/lock
+highest	/man/2/filter-deflate
+highest	/man/2/keyring-0intro
+highest	/man/2/sys-fversion
+highest	/man/3/draw
+highest	/man/8/prep
+highest	/man/9/choicebutton
+highest	/man/9/text
+highlight	/man/1/acme
+highlight	/man/1/deb
+highlight	/man/2/prefab-compound
+highlight	/man/2/prefab-style
+highlight	/man/9/listbox
+highlight	/man/9/options
+highlightcolor	/man/2/prefab-style
+highlightcolor	/man/9/button
+highlightcolor	/man/9/checkbutton
+highlightcolor	/man/9/entry
+highlightcolor	/man/9/label
+highlightcolor	/man/9/listbox
+highlightcolor	/man/9/menubutton
+highlightcolor	/man/9/options
+highlightcolor	/man/9/radiobutton
+highlightcolor	/man/9/scale
+highlighted	/man/1/acme
+highlighted	/man/2/prefab-compound
+highlighting	/man/2/prefab-compound
+highlighting	/man/2/prefab-element
+highlights	/man/1/acme
+highlights	/man/1/deb
+highlightthickness	/man/9/button
+highlightthickness	/man/9/checkbutton
+highlightthickness	/man/9/entry
+highlightthickness	/man/9/label
+highlightthickness	/man/9/listbox
+highlightthickness	/man/9/menubutton
+highlightthickness	/man/9/options
+highlightthickness	/man/9/radiobutton
+highlightthickness	/man/9/scale
+highwater	/man/1/wm-misc
+hillerson	/man/2/palmfile
+hinfo	/man/8/dns
+hinfox	/man/8/dns
+hint	/man/2/sexprs
+hints	/man/2/registries
+hiqvideo	/man/3/vga
+hiqvideohwgc	/man/3/vga
+histogram	/man/1/freq
+histogram	/man/3/prof
+histograms	/man/1/freq
+historical	/man/5/0intro
+historically	/man/1/cal
+hit	/man/1/deb
+hit	/man/2/prefab-compound
+hit	/man/2/tk
+hitchannel	/man/2/tk
+hits	/man/1/collab-clients
+hits	/man/1/deb
+hits	/man/2/debug
+hitting	/man/1/miniterm
+hmac	/man/2/keyring-sha1
+hmac	/man/3/cap
+hmac.c	/man/2/keyring-sha1
+hoc	/man/10/plan9.ini
+hold	/man/1/acme
+hold	/man/1/alphabet-fs
+hold	/man/1/fs
+hold	/man/1/kill
+hold	/man/1/limbo
+hold	/man/1/wm-sh
+hold	/man/1/yacc
+hold	/man/1/zeros
+hold	/man/10/a.out
+hold	/man/2/draw-0intro
+hold	/man/2/draw-image
+hold	/man/2/format
+hold	/man/2/palmfile
+hold	/man/2/secstore
+hold	/man/2/sh
+hold	/man/2/spree
+hold	/man/2/styx
+hold	/man/2/styxflush
+hold	/man/2/styxservers-nametree
+hold	/man/2/sys-byte2char
+hold	/man/2/tk
+hold	/man/2/wmsrv
+hold	/man/3/flash
+hold	/man/4/registry
+hold	/man/5/0intro
+hold	/man/8/prep
+hold	/man/9/pack
+holder	/man/1/du
+holder	/man/1/gettar
+holder	/man/10/qlock
+holder	/man/2/spki
+holders	/man/3/root
+holders	/man/4/namespace
+holding	/man/1/0intro
+holding	/man/1/acme
+holding	/man/1/limbo
+holding	/man/1/wm
+holding	/man/1/wm-misc
+holding	/man/10/plan9.ini
+holding	/man/2/alphabet-intro
+holding	/man/2/convcs
+holding	/man/2/daytime
+holding	/man/2/draw-display
+holding	/man/2/format
+holding	/man/2/keyset
+holding	/man/2/sets
+holding	/man/2/spree
+holding	/man/2/sys-file2chan
+holding	/man/2/wmsrv
+holding	/man/2/xml
+holding	/man/3/logfs
+holding	/man/3/sign
+holding	/man/4/acme
+holding	/man/4/namespace
+holding	/man/5/stat
+holding	/man/6/font
+holds	/man/1/acme
+holds	/man/1/secstore
+holds	/man/1/sh
+holds	/man/10/parsecmd
+holds	/man/10/print
+holds	/man/10/ref
+holds	/man/10/styx
+holds	/man/2/alphabet-intro
+holds	/man/2/crc
+holds	/man/2/debug
+holds	/man/2/format
+holds	/man/2/imagefile
+holds	/man/2/keyring-auth
+holds	/man/2/keyring-crypt
+holds	/man/2/keyset
+holds	/man/2/palmfile
+holds	/man/2/prefab-compound
+holds	/man/2/print
+holds	/man/2/registries
+holds	/man/2/sets
+holds	/man/2/sh
+holds	/man/2/spree
+holds	/man/2/spree-allow
+holds	/man/2/spree-cardlib
+holds	/man/2/spree-objstore
+holds	/man/2/styxpersist
+holds	/man/2/styxservers
+holds	/man/2/sys-dup
+holds	/man/2/sys-tokenize
+holds	/man/2/w3c-css
+holds	/man/2/wmclient
+holds	/man/2/wmlib
+holds	/man/2/wmsrv
+holds	/man/2/xml
+holds	/man/3/cons
+holds	/man/3/kprof
+holds	/man/3/rtc
+holds	/man/3/touch
+holds	/man/4/acme
+holds	/man/4/spree
+holds	/man/6/keys
+holds	/man/9/variable
+holes	/man/10/xalloc
+holes	/man/2/dbm
+homedir	/man/8/bootpd
+homeurl	/man/1/charon
+honor	/man/5/flush
+honor	/man/5/version
+honour	/man/8/logind
+honoured	/man/2/sys-0intro
+honours	/man/1/sh-file2chan
+hope	/man/10/panic
+horizontal	/man/1/deb
+horizontal	/man/2/draw-display
+horizontal	/man/2/draw-image
+horizontal	/man/2/draw-rect
+horizontal	/man/2/prefab-compound
+horizontal	/man/2/prefab-element
+horizontal	/man/3/draw
+horizontal	/man/9/canvas
+horizontal	/man/9/entry
+horizontal	/man/9/grid
+horizontal	/man/9/listbox
+horizontal	/man/9/menu
+horizontal	/man/9/options
+horizontal	/man/9/pack
+horizontal	/man/9/scale
+horizontal	/man/9/scrollbar
+horizontal	/man/9/text
+horizontally	/man/2/dividers
+horizontally	/man/2/print
+horizontally	/man/9/pack
+horizontaly	/man/1/tktester
+host	/man/1/0intro
+host	/man/1/asm
+host	/man/1/bind
+host	/man/1/charon
+host	/man/1/chgrp
+host	/man/1/chmod
+host	/man/1/cpu
+host	/man/1/emu
+host	/man/1/limbo
+host	/man/1/m4
+host	/man/1/netstat
+host	/man/1/os
+host	/man/1/rcmd
+host	/man/1/sendmail
+host	/man/1/telnet
+host	/man/10/0intro
+host	/man/10/2c
+host	/man/10/9load
+host	/man/10/conf
+host	/man/10/eve
+host	/man/10/kproc
+host	/man/10/ntsrv
+host	/man/2/dhcpclient
+host	/man/2/dial
+host	/man/2/registries
+host	/man/2/secstore
+host	/man/2/srv
+host	/man/2/sys-0intro
+host	/man/2/sys-dial
+host	/man/2/tftp
+host	/man/2/virgil
+host	/man/2/w3c-uris
+host	/man/3/audio
+host	/man/3/boot
+host	/man/3/cap
+host	/man/3/cmd
+host	/man/3/cons
+host	/man/3/dbg
+host	/man/3/dynld
+host	/man/3/fs
+host	/man/3/ip
+host	/man/3/pointer
+host	/man/3/sign
+host	/man/3/snarf
+host	/man/3/usb
+host	/man/4/factotum
+host	/man/4/ftpfs
+host	/man/4/import
+host	/man/6/keyboard
+host	/man/6/keys
+host	/man/6/ndb
+host	/man/7/dbsrv
+host	/man/8/ai2key
+host	/man/8/changelogin
+host	/man/8/cs
+host	/man/8/dhcp
+host	/man/8/dns
+host	/man/8/getauthinfo
+host	/man/8/init
+host	/man/8/logind
+host	/man/8/mangaload
+host	/man/8/ping
+host	/man/8/rdbgsrv
+host	/man/8/register
+host	/man/8/signer
+host	/man/8/svc
+host's	/man/1/os
+host's	/man/2/dial
+host's	/man/2/ip
+host's	/man/2/sys-dial
+host's	/man/3/cmd
+host's	/man/3/usb
+host's	/man/8/cs
+host's	/man/8/dhcp
+host's	/man/8/dns
+hosted	/man/1/0intro
+hosted	/man/1/bind
+hosted	/man/1/os
+hosted	/man/1/ps
+hosted	/man/10/0intro
+hosted	/man/10/conf
+hosted	/man/10/eve
+hosted	/man/10/ntsrv
+hosted	/man/2/0intro
+hosted	/man/2/srv
+hosted	/man/2/sys-sleep
+hosted	/man/3/audio
+hosted	/man/3/cmd
+hosted	/man/3/ip
+hosted	/man/3/pointer
+hosted	/man/3/snarf
+hosted	/man/3/srv9
+hosted	/man/4/9srvfs
+hosted	/man/4/import
+hosted	/man/4/namespace
+hosted	/man/7/db
+hosted	/man/8/0intro
+hosted	/man/8/cs
+hosted	/man/8/dns
+hosted	/man/8/ping
+hostname	/man/8/dhcp
+hosts	/man/2/ip
+hosts	/man/8/svc
+hot	/man/9/cursor
+hour	/man/2/daytime
+hours	/man/2/daytime
+howlett	/man/9/grid
+hp	/man/2/print
+hp	/man/4/namespace
+hp	/man/6/man
+hpcode	/man/2/print
+hpmapfile	/man/2/print
+hrc	/man/3/tv
+hsync	/man/3/vga
+ht	/man/2/hash
+html	/man/1/charon
+html	/man/1/cook
+html	/man/1/ebook
+html	/man/1/man
+html	/man/1/webgrab
+html	/man/2/stringinttab
+http	/man/1/charon
+http	/man/1/webgrab
+http	/man/2/dhcpclient
+http	/man/2/rfc822
+http	/man/2/sexprs
+http	/man/2/w3c-css
+http	/man/2/w3c-uris
+http	/man/2/w3c-xpointers
+http	/man/2/xml
+http	/man/6/json
+http	/man/6/ubfa
+http	/man/8/httpd
+http.suff	/man/8/httpd
+http1.0	/man/1/charon
+httpd	/man/2/w3c-uris
+httpd	/man/8/httpd
+httpd.debug	/man/8/httpd
+httpd.log	/man/8/httpd
+httpd.m	/man/8/httpd
+httpd.rewrite	/man/8/httpd
+httpd.suff	/man/8/httpd
+httpproxy	/man/1/charon
+httpproxy	/man/1/webgrab
+https	/man/1/charon
+https	/man/1/webgrab
+hue	/man/3/tv
+hue	/man/6/colour
+hues	/man/6/colour
+huffman	/man/1/gzip
+huge	/man/2/math-linalg
+hume	/man/1/mk
+hume	/man/10/mk
+hundred	/man/3/tinyfs
+hung	/man/10/qio
+hungup	/man/10/qio
+hungup	/man/10/styxserver
+hungup	/man/3/ip
+hungup	/man/3/tls
+hwaccel	/man/3/vga
+hwblank	/man/3/vga
+hwgc	/man/3/vga
+hybrid	/man/1/gzip
+hybrid	/man/8/create
+hyperbolic	/man/2/math-elem
+hypertext	/man/9/text
+hyphen	/man/2/arg
+hypot	/man/2/math-elem
+hz	/man/1/auplay
+hz	/man/10/seconds
+hz	/man/3/audio
+hz	/man/3/mpeg
+hz	/man/6/audio
+i.e	/man/1/alphabet-fs
+i.e	/man/1/fs
+i.e	/man/1/sh-alphabet
+i.e	/man/1/vacget
+i.e	/man/1/yacc
+i.e	/man/10/ms2
+i.e	/man/10/plan9.ini
+i.e	/man/2/debug
+i.e	/man/2/format
+i.e	/man/2/security-login
+i.e	/man/2/sh
+i.e	/man/2/spree
+i.e	/man/2/styxservers-nametree
+i.e	/man/4/dbfs
+i.e	/man/4/factotum
+i.e	/man/8/prep
+i.e	/man/9/canvas
+i.e	/man/9/entry
+i.e	/man/9/listbox
+i.e	/man/9/scale
+i.e	/man/9/text
+i2c	/man/3/i2c
+i2c.c	/man/3/i2c
+ia5string	/man/2/asn1
+iallocb	/man/10/allocb
+ialt	/man/2/dis
+iamax	/man/2/math-linalg
+iar	/man/1/ar
+iar	/man/10/2l
+iar	/man/10/ar
+iar	/man/10/iar
+iar	/man/10/inm
+ib	/man/6/man
+ibm	/man/1/dd
+ibm	/man/2/disks
+ibm	/man/3/rtc
+ibm866	/man/2/convcs
+ibm866.cp	/man/2/convcs
+ibs	/man/1/dd
+ibutton	/man/1/mash-tk
+ibuttons	/man/1/mash-tk
+ic	/man/1/miniterm
+ic	/man/1/wm-sh
+icmp	/man/3/ip
+icmp	/man/8/mangaload
+icmp	/man/8/ping
+icon	/man/1/cprof
+icon	/man/1/deb
+icon	/man/1/mash-tk
+icon	/man/1/mux
+icon	/man/2/dialog
+icon	/man/2/prefab-compound
+icon	/man/2/prefab-element
+iconbox	/man/2/prefab-compound
+iconising	/man/1/wm-misc
+icons	/man/1/cprof
+icons	/man/1/mash-tk
+icons	/man/1/mux
+icons	/man/1/toolbar
+icons	/man/2/draw-example
+icons	/man/2/prefab-element
+icons	/man/4/namespace
+icons	/man/9/types
+icontrol	/man/9/text
+ics	/man/1/tcs
+icursor	/man/9/canvas
+icursor	/man/9/entry
+id	/man/1/acme
+id	/man/1/blur
+id	/man/1/chgrp
+id	/man/1/collab-clients
+id	/man/1/cp
+id	/man/1/grid-monitor
+id	/man/1/idea
+id	/man/1/kill
+id	/man/1/m4
+id	/man/1/miniterm
+id	/man/1/mk
+id	/man/1/ps
+id	/man/1/sh
+id	/man/1/sh-file2chan
+id	/man/1/sh-std
+id	/man/1/sh-tk
+id	/man/1/stack
+id	/man/1/wm
+id	/man/10/acid
+id	/man/10/devattach
+id	/man/10/kproc
+id	/man/10/mk
+id	/man/10/newchan
+id	/man/10/plan9.ini
+id	/man/10/styxserver
+id	/man/2/asn1
+id	/man/2/command
+id	/man/2/debug
+id	/man/2/draw-display
+id	/man/2/draw-screen
+id	/man/2/exception
+id	/man/2/factotum
+id	/man/2/filter
+id	/man/2/ir
+id	/man/2/palmfile
+id	/man/2/spree
+id	/man/2/spree-allow
+id	/man/2/spree-cardlib
+id	/man/2/styxconv
+id	/man/2/styxservers
+id	/man/2/sys-open
+id	/man/2/sys-pctl
+id	/man/2/sys-stat
+id	/man/2/timers
+id	/man/2/w3c-css
+id	/man/2/wait
+id	/man/2/wmsrv
+id	/man/2/xml
+id	/man/3/cmd
+id	/man/3/cons
+id	/man/3/dbg
+id	/man/3/draw
+id	/man/3/flash
+id	/man/3/pnp
+id	/man/4/acme
+id	/man/4/factotum
+id	/man/4/kfs
+id	/man/5/0intro
+id	/man/5/open
+id	/man/6/sbl
+id	/man/8/collabsrv
+id	/man/8/create
+id	/man/8/logind
+id	/man/8/manufacture
+id	/man/8/register
+id	/man/8/signer
+id	/man/9/canvas
+ida	/man/2/ida
+ida.b	/man/2/ida
+ida.m	/man/2/ida
+idatab.b	/man/2/ida
+idea	/man/1/acme
+idea	/man/1/idea
+idea	/man/10/c2l
+idea	/man/10/plan9.ini
+idea	/man/10/seconds
+idea	/man/2/keyring-0intro
+idea	/man/2/keyring-crypt
+idea	/man/3/ssl
+idea	/man/5/attach
+idea	/man/6/colour
+idea.b	/man/1/idea
+idea.c	/man/1/idea
+ideabsize	/man/2/keyring-crypt
+ideacbc	/man/1/crypt
+ideacbc	/man/2/keyring-crypt
+ideacbc	/man/3/ssl
+ideaecb	/man/2/keyring-crypt
+ideaecb	/man/3/ssl
+ideal	/man/9/bind
+ideally	/man/10/dev
+ideas	/man/1/0intro
+ideas	/man/9/canvas
+ideas	/man/9/grid
+ideasetup	/man/2/keyring-crypt
+ideastate	/man/2/keyring-crypt
+idea™	/man/2/keyring-crypt
+idempotent	/man/1/fmt
+idempotent	/man/2/styxpersist
+ident	/man/2/w3c-css
+identical	/man/1/acme
+identical	/man/1/diff
+identical	/man/1/mk
+identical	/man/1/sh
+identical	/man/1/xd
+identical	/man/1/yacc
+identical	/man/10/mk
+identical	/man/10/ntsrv
+identical	/man/2/0intro
+identical	/man/2/draw-image
+identical	/man/2/math-0intro
+identical	/man/2/sets
+identical	/man/2/sexprs
+identical	/man/2/sys-read
+identical	/man/2/ubfa
+identical	/man/3/dup
+identical	/man/4/acme
+identical	/man/5/0intro
+identical	/man/6/image
+identically	/man/2/draw-image
+identifers	/man/2/asn1
+identification	/man/3/draw
+identification	/man/3/ip
+identification	/man/3/pnp
+identification	/man/5/0intro
+identification	/man/5/stat
+identifications	/man/5/0intro
+identified	/man/1/acme
+identified	/man/1/kill
+identified	/man/1/wm
+identified	/man/10/plan9.ini
+identified	/man/10/styxserver
+identified	/man/2/debug
+identified	/man/2/spree
+identified	/man/2/styxservers-nametree
+identified	/man/3/0intro
+identified	/man/3/draw
+identified	/man/3/dynld
+identified	/man/3/ftl
+identified	/man/3/ip
+identified	/man/3/prog
+identified	/man/4/spree
+identified	/man/5/0intro
+identified	/man/5/flush
+identified	/man/5/read
+identified	/man/5/stat
+identified	/man/5/walk
+identified	/man/6/sbl
+identified	/man/8/collabsrv
+identified	/man/9/bind
+identifier	/man/1/mash
+identifier	/man/1/sh-file2chan
+identifier	/man/10/c2l
+identifier	/man/10/plan9.ini
+identifier	/man/2/asn1
+identifier	/man/2/drawmux
+identifier	/man/2/spree
+identifier	/man/2/spree-cardlib
+identifier	/man/2/sys-0intro
+identifier	/man/2/w3c-css
+identifier	/man/2/wmsrv
+identifier	/man/3/cons
+identifier	/man/3/draw
+identifier	/man/3/prog
+identifier	/man/6/keytext
+identifier	/man/6/sbl
+identifier	/man/6/scancode
+identifier	/man/8/bootpd
+identifiers	/man/2/0intro
+identifiers	/man/2/asn1
+identifiers	/man/2/spree
+identifiers	/man/2/w3c-uris
+identifiers	/man/3/draw
+identifiers	/man/3/prog
+identifiers	/man/4/spree
+identifiers	/man/6/sbl
+identifies	/man/1/acme
+identifies	/man/1/mk
+identifies	/man/1/passwd
+identifies	/man/1/tiny
+identifies	/man/10/dev
+identifies	/man/10/devattach
+identifies	/man/10/mk
+identifies	/man/10/plan9.ini
+identifies	/man/2/asn1
+identifies	/man/2/complete
+identifies	/man/2/draw-font
+identifies	/man/2/factotum
+identifies	/man/2/palmfile
+identifies	/man/2/prefab-0intro
+identifies	/man/2/spki
+identifies	/man/2/styxservers
+identifies	/man/2/sys-0intro
+identifies	/man/2/sys-stat
+identifies	/man/3/pbus
+identifies	/man/5/0intro
+identifies	/man/5/attach
+identifies	/man/5/version
+identifies	/man/6/sbl
+identifies	/man/9/text
+identifies	/man/9/types
+identify	/man/1/0intro
+identify	/man/10/2c
+identify	/man/10/dev
+identify	/man/10/intrenable
+identify	/man/10/plan9.ini
+identify	/man/10/styxserver
+identify	/man/2/dhcpclient
+identify	/man/2/registries
+identify	/man/2/spree
+identify	/man/2/spree-allow
+identify	/man/3/dynld
+identify	/man/3/mnt
+identify	/man/3/sd
+identify	/man/4/factotum
+identify	/man/5/0intro
+identify	/man/6/dis
+identify	/man/8/collabsrv
+identify	/man/9/canvas
+identify	/man/9/scale
+identify	/man/9/scrollbar
+identifying	/man/1/collab-clients
+identifying	/man/10/2c
+identifying	/man/10/plan9.ini
+identifying	/man/2/draw-0intro
+identifying	/man/2/palmfile
+identifying	/man/2/spree
+identifying	/man/2/wmsrv
+identifying	/man/3/pnp
+identifying	/man/4/factotum
+identifying	/man/5/0intro
+identifying	/man/5/version
+identifying	/man/8/ai2key
+identifying	/man/8/collabsrv
+identifying	/man/9/canvas
+identifying	/man/9/grid
+identifying	/man/9/menu
+identities	/man/2/security-0intro
+identities	/man/2/sys-0intro
+identity	/man/1/passwd
+identity	/man/1/sh-alphabet
+identity	/man/10/eve
+identity	/man/10/ntsrv
+identity	/man/2/keyring-0intro
+identity	/man/2/registries
+identity	/man/2/security-0intro
+identity	/man/2/sys-0intro
+identity	/man/3/cmd
+identity	/man/4/factotum
+identity	/man/6/keys
+identity	/man/6/translate
+identity	/man/8/logind
+identity	/man/8/register
+identity	/man/8/rstyxd
+identity	/man/8/signer
+idiomatic	/man/2/command
+idioms	/man/1/acme
+idiosyncratic	/man/6/keyboard
+idle	/man/10/dmainit
+ids	/man/1/ar
+ids	/man/1/deb
+ids	/man/1/kill
+ids	/man/10/iar
+ids	/man/10/kproc
+ids	/man/2/factotum
+ids	/man/2/palmfile
+ids	/man/2/spree
+ids	/man/2/spree-allow
+ids	/man/3/fs
+ids	/man/3/logfs
+ids	/man/3/tinyfs
+ids	/man/4/acme
+ids	/man/4/kfs
+ids	/man/4/tarfs
+ids	/man/7/cddb
+ids	/man/8/create
+ids	/man/9/canvas
+idstring	/man/3/pnp
+idxl	/man/2/spree-cardlib
+ie	/man/1/m4
+ie	/man/1/math-misc
+ie	/man/1/secstore
+ie	/man/1/tail
+ie	/man/10/c2l
+ie	/man/10/conf
+ie	/man/10/devattach
+ie	/man/10/kbdputc
+ie	/man/10/kproc
+ie	/man/10/newchan
+ie	/man/10/ntsrv
+ie	/man/10/styxserver
+ie	/man/2/dbm
+ie	/man/2/dhcpclient
+ie	/man/2/dis
+ie	/man/2/draw-image
+ie	/man/2/exception
+ie	/man/2/ip
+ie	/man/2/lists
+ie	/man/2/palmfile
+ie	/man/2/plumbmsg
+ie	/man/2/registries
+ie	/man/2/rfc822
+ie	/man/2/secstore
+ie	/man/2/sexprs
+ie	/man/2/spki
+ie	/man/2/styxservers
+ie	/man/2/sys-open
+ie	/man/2/sys-pctl
+ie	/man/2/sys-self
+ie	/man/2/tk
+ie	/man/2/ubfa
+ie	/man/2/w3c-xpointers
+ie	/man/3/fs
+ie	/man/3/ftl
+ie	/man/3/pipe
+ie	/man/3/prog
+ie	/man/4/factotum
+ie	/man/4/ftpfs
+ie	/man/4/keysrv
+ie	/man/6/attrdb
+ie	/man/6/json
+ie	/man/6/keys
+ie	/man/6/keytext
+ie	/man/6/proto
+ie	/man/8/rstyxd
+ie	/man/8/svc
+ieee	/man/10/plan9.ini
+ieee	/man/2/math-0intro
+ieee	/man/2/math-export
+ieee	/man/2/math-fp
+ieee	/man/2/string
+ieee	/man/3/tv
+ieee754	/man/6/dis
+ifcaddr	/man/2/ip
+ifcdir	/man/8/dhcp
+ifdef	/man/1/m4
+ifdef	/man/10/2c
+ifelse	/man/1/m4
+iff	/man/2/attrdb
+iff	/man/2/ether
+iff	/man/2/names
+iff	/man/2/rfc822
+iff	/man/2/sexprs
+iff	/man/2/spki
+iff	/man/6/dis
+iff	/man/6/sexprs
+ifile	/man/10/5coff
+ifn	/man/3/ip
+ifndef	/man/10/2c
+ifs	/man/1/sh
+ifs	/man/1/sh-std
+ifstats	/man/3/ether
+ifunset	/man/9/grab
+ignore	/man/1/ftree
+ignore	/man/10/5cv
+ignore	/man/10/dev
+ignore	/man/10/devattach
+ignore	/man/2/dhcpclient
+ignore	/man/2/prof
+ignore	/man/2/spree
+ignore	/man/3/draw
+ignore	/man/6/namespace
+ignore	/man/9/button
+ignore	/man/9/checkbutton
+ignore	/man/9/menu
+ignore	/man/9/menubutton
+ignore	/man/9/options
+ignore	/man/9/radiobutton
+ignore	/man/9/text
+ignoreadvice	/man/3/ip
+ignores	/man/1/sum
+ignores	/man/10/ms2
+ignores	/man/10/qio
+ignores	/man/10/sleep
+ignores	/man/2/draw-image
+ignores	/man/2/ether
+ignores	/man/6/colour
+ignoring	/man/1/uuencode
+ignoring	/man/10/2c
+ignoring	/man/10/newchan
+ignoring	/man/2/draw-0intro
+ignoring	/man/2/prefab-compound
+ignoring	/man/2/rfc822
+ignoring	/man/3/draw
+ignoring	/man/8/prep
+ii	/man/2/w3c-uris
+iic.c	/man/3/i2c
+iii	/man/10/plan9.ini
+iii	/man/2/w3c-uris
+il	/man/1/netstat
+il	/man/3/ip
+il	/man/6/ndb
+ill	/man/2/dhcpclient
+ill	/man/2/spki
+ill	/man/2/wait
+illegal	/man/1/sh-tk
+illegal	/man/2/0intro
+illegal	/man/2/encoding
+illegal	/man/2/styxservers
+illegal	/man/3/mnt
+illegal	/man/5/0intro
+illegal	/man/5/open
+illegal	/man/5/read
+illegal	/man/5/stat
+illegal	/man/5/walk
+illegal	/man/9/canvas
+illegally	/man/2/styx
+illumination	/man/2/draw-display
+illustrate	/man/2/0intro
+illustrated	/man/10/styxserver
+illustrated	/man/6/keyboard
+illustrates	/man/2/draw-example
+illustrates	/man/2/security-auth
+illustrating	/man/2/draw-example
+ilmsg	/man/3/ip
+ilock	/man/10/lock
+ilock	/man/10/splhi
+ilogb	/man/2/math-fp
+ilxvn	/man/1/sh
+image	/man/1/acme
+image	/man/1/blur
+image	/man/1/charon
+image	/man/1/emu
+image	/man/1/mash-tk
+image	/man/1/mprof
+image	/man/1/wm
+image	/man/1/wm-misc
+image	/man/1/zeros
+image	/man/10/2l
+image	/man/10/5cv
+image	/man/10/a.out
+image	/man/10/acid
+image	/man/10/conf
+image	/man/10/dynld
+image	/man/2/0intro
+image	/man/2/dialog
+image	/man/2/draw-0intro
+image	/man/2/draw-context
+image	/man/2/draw-display
+image	/man/2/draw-example
+image	/man/2/draw-font
+image	/man/2/draw-image
+image	/man/2/draw-screen
+image	/man/2/imagefile
+image	/man/2/mpeg
+image	/man/2/prefab-compound
+image	/man/2/prefab-element
+image	/man/2/prefab-style
+image	/man/2/print
+image	/man/2/pslib
+image	/man/2/rfc822
+image	/man/2/selectfile
+image	/man/2/tk
+image	/man/2/wmclient
+image	/man/2/wmlib
+image	/man/2/wmsrv
+image	/man/3/boot
+image	/man/3/cons
+image	/man/3/draw
+image	/man/3/logfs
+image	/man/3/mpeg
+image	/man/3/pointer
+image	/man/3/tv
+image	/man/3/vga
+image	/man/4/dossrv
+image	/man/4/namespace
+image	/man/6/colour
+image	/man/6/font
+image	/man/6/image
+image	/man/6/plumbing
+image	/man/8/collabsrv
+image	/man/8/ftl
+image	/man/8/mangaload
+image	/man/8/prep
+image	/man/9/0intro
+image	/man/9/button
+image	/man/9/canvas
+image	/man/9/checkbutton
+image	/man/9/choicebutton
+image	/man/9/cursor
+image	/man/9/image
+image	/man/9/label
+image	/man/9/menu
+image	/man/9/menubutton
+image	/man/9/options
+image	/man/9/panel
+image	/man/9/radiobutton
+image	/man/9/types
+image's	/man/2/draw-0intro
+image's	/man/2/draw-display
+image's	/man/2/wmsrv
+image's	/man/3/draw
+image's	/man/6/image
+image's	/man/9/panel
+image.bit	/man/1/blur
+image.chans	/man/2/draw-0intro
+image.clipr	/man/2/draw-0intro
+image.clipr	/man/2/draw-image
+image.depth	/man/2/draw-0intro
+image.draw	/man/2/prefab-element
+image.draw	/man/6/colour
+image.nameimage	/man/2/draw-display
+image.r	/man/2/draw-0intro
+image.r	/man/2/draw-image
+image.readpixels	/man/6/colour
+image.readpixels	/man/6/image
+image.repl	/man/2/draw-0intro
+image.repl	/man/2/draw-image
+image.text	/man/2/draw-font
+image.text	/man/6/font
+image.writepixel	/man/6/image
+image.writepixels	/man/6/colour
+imagecachemem	/man/1/charon
+imagecachenum	/man/1/charon
+imagefile	/man/1/blur
+imagefile	/man/2/imagefile
+imagefile.m	/man/2/imagefile
+imageget	/man/2/tk
+imageid	/man/3/draw
+imagelvl	/man/1/charon
+imageput	/man/2/tk
+imageput	/man/9/image
+imager2screenr	/man/2/wmclient
+imageremap	/man/2/imagefile
+images	/man/1/charon
+images	/man/1/ftree
+images	/man/1/webgrab
+images	/man/10/ksize
+images	/man/10/kstrip
+images	/man/10/plan9.ini
+images	/man/2/draw-0intro
+images	/man/2/draw-context
+images	/man/2/draw-display
+images	/man/2/draw-example
+images	/man/2/draw-font
+images	/man/2/draw-image
+images	/man/2/draw-screen
+images	/man/2/imagefile
+images	/man/2/prefab-0intro
+images	/man/2/prefab-element
+images	/man/2/tk
+images	/man/2/wmsrv
+images	/man/3/draw
+images	/man/6/colour
+images	/man/6/font
+images	/man/6/image
+images	/man/8/0intro
+images	/man/9/canvas
+images	/man/9/image
+images	/man/9/panel
+imap	/man/2/factotum
+img	/man/10/5cv
+img	/man/2/pslib
+img	/man/2/wmlib
+img	/man/2/wmsrv
+img.bit	/man/1/blur
+img.r	/man/2/wmsrv
+immediate	/man/1/disdep
+immediate	/man/1/m4
+immediate	/man/2/dis
+immediate	/man/4/acme
+immediate	/man/6/dis
+immedlately	/man/3/pointer
+immutable	/man/3/draw
+impc	/man/3/kprof
+imperceptible	/man/6/colour
+implement	/man/1/tiny
+implement	/man/1/wm
+implement	/man/10/a.out
+implement	/man/10/c2l
+implement	/man/10/dev
+implement	/man/10/error
+implement	/man/10/kbdputc
+implement	/man/10/qio
+implement	/man/10/qlock
+implement	/man/2/alphabet-intro
+implement	/man/2/arg
+implement	/man/2/draw-example
+implement	/man/2/ir
+implement	/man/2/itslib
+implement	/man/2/keyring-getmsg
+implement	/man/2/keyring-rc4
+implement	/man/2/math-elem
+implement	/man/2/math-linalg
+implement	/man/2/msgio
+implement	/man/2/spree
+implement	/man/2/spree-cardlib
+implement	/man/2/spree-gather
+implement	/man/2/styxservers
+implement	/man/2/styxservers-nametree
+implement	/man/2/wmsrv
+implement	/man/3/flash
+implement	/man/3/logfs
+implement	/man/3/root
+implement	/man/3/tls
+implement	/man/3/vga
+implement	/man/4/ftpfs
+implement	/man/5/stat
+implement	/man/8/collabsrv
+implement	/man/9/canvas
+implement	/man/9/menu
+implement	/man/9/options
+implement	/man/9/text
+implementation	/man/1/0intro
+implementation	/man/1/charon
+implementation	/man/1/cpu
+implementation	/man/1/deb
+implementation	/man/1/tcs
+implementation	/man/1/tktester
+implementation	/man/1/wm-misc
+implementation	/man/1/yacc
+implementation	/man/10/0intro
+implementation	/man/10/conf
+implementation	/man/10/dev
+implementation	/man/10/inb
+implementation	/man/10/qio
+implementation	/man/2/0intro
+implementation	/man/2/alphabet-intro
+implementation	/man/2/bufio
+implementation	/man/2/bufio-chanfill
+implementation	/man/2/convcs
+implementation	/man/2/debug
+implementation	/man/2/dis
+implementation	/man/2/draw-image
+implementation	/man/2/filter
+implementation	/man/2/filter-deflate
+implementation	/man/2/filter-slip
+implementation	/man/2/imagefile
+implementation	/man/2/ir
+implementation	/man/2/keyring-0intro
+implementation	/man/2/keyring-gensk
+implementation	/man/2/keyring-ipint
+implementation	/man/2/lists
+implementation	/man/2/msgio
+implementation	/man/2/print
+implementation	/man/2/registries
+implementation	/man/2/sets
+implementation	/man/2/sexprs
+implementation	/man/2/spki-verifier
+implementation	/man/2/spree-cardlib
+implementation	/man/2/srv
+implementation	/man/2/styx
+implementation	/man/2/styxservers
+implementation	/man/2/sys-read
+implementation	/man/2/wmsrv
+implementation	/man/2/xml
+implementation	/man/3/draw
+implementation	/man/3/indir
+implementation	/man/3/ip
+implementation	/man/3/kprof
+implementation	/man/3/logfs
+implementation	/man/3/ssl
+implementation	/man/4/keyfs
+implementation	/man/4/ramfile
+implementation	/man/5/walk
+implementation	/man/6/colour
+implementation	/man/6/dis
+implementation	/man/6/keyboard
+implementation	/man/6/sbl
+implementation	/man/6/scancode
+implementation	/man/6/ubfa
+implementation	/man/7/dbsrv
+implementation	/man/8/collabsrv
+implementation	/man/9/0intro
+implementation	/man/9/canvas
+implementation's	/man/2/ubfa
+implementations	/man/1/limbo
+implementations	/man/1/ps
+implementations	/man/10/devattach
+implementations	/man/10/dmainit
+implementations	/man/10/strcat
+implementations	/man/2/filter
+implementations	/man/2/sets
+implementations	/man/2/spki
+implementations	/man/2/styxservers
+implementations	/man/3/audio
+implementations	/man/3/cons
+implementations	/man/3/ip
+implementations	/man/3/logfs
+implementations	/man/3/tls
+implementations	/man/5/read
+implementations	/man/5/stat
+implementations	/man/6/keyboard
+implementations	/man/6/scancode
+implementer	/man/10/styxserver
+implementing	/man/1/limbo
+implementing	/man/1/tktester
+implementing	/man/2/keyring-certtostr
+implementing	/man/2/spree-gather
+implementing	/man/3/ftl
+implementing	/man/8/collabsrv
+implementing	/man/9/text
+implements	/man/1/0intro
+implements	/man/1/alphabet-fs
+implements	/man/1/charon
+implements	/man/1/fs
+implements	/man/1/sh
+implements	/man/1/sh-alphabet
+implements	/man/1/sh-file2chan
+implements	/man/1/sh-string
+implements	/man/2/complete
+implements	/man/2/debug
+implements	/man/2/dhcpclient
+implements	/man/2/filter-deflate
+implements	/man/2/ida
+implements	/man/2/popup
+implements	/man/2/styxservers
+implements	/man/2/tabs
+implements	/man/2/w3c-css
+implements	/man/2/w3c-xpointers
+implements	/man/3/0intro
+implements	/man/3/audio
+implements	/man/3/draw
+implements	/man/3/logfs
+implements	/man/3/mpeg
+implements	/man/3/prof
+implements	/man/3/ssl
+implements	/man/3/tls
+implements	/man/4/kfs
+implements	/man/8/rip
+implements	/man/9/grab
+implication	/man/1/calc
+implication	/man/2/filter
+implicit	/man/1/mash
+implicit	/man/1/mash-make
+implicit	/man/1/mk
+implicit	/man/10/mk
+implicit	/man/2/asn1
+implicit	/man/2/crc
+implicit	/man/5/0intro
+implicit	/man/5/walk
+implicit	/man/6/dis
+implicitly	/man/1/acme
+implicitly	/man/2/asn1
+implicitly	/man/2/bufio
+implicitly	/man/7/db
+implicitly	/man/9/canvas
+implied	/man/1/mash
+implied	/man/2/keyring-sha1
+implied	/man/5/open
+implied	/man/5/walk
+implied	/man/8/rip
+implied	/man/9/1copyright
+implies	/man/1/du
+implies	/man/1/sh
+implies	/man/10/styxserver
+implies	/man/2/math-0intro
+implies	/man/2/spki
+implies	/man/2/styxservers
+implies	/man/9/canvas
+imply	/man/1/charon
+import	/man/1/9win
+import	/man/1/alphabet-abc
+import	/man/1/alphabet-fs
+import	/man/1/alphabet-grid
+import	/man/1/grid-session
+import	/man/1/mdb
+import	/man/1/ps
+import	/man/1/sh-alphabet
+import	/man/1/tktester
+import	/man/1/yacc
+import	/man/10/2l
+import	/man/10/dynld
+import	/man/2/0intro
+import	/man/2/bloomfilter
+import	/man/2/bufio
+import	/man/2/dbm
+import	/man/2/dhcpclient
+import	/man/2/dis
+import	/man/2/dividers
+import	/man/2/draw-example
+import	/man/2/ip
+import	/man/2/itslib
+import	/man/2/math-export
+import	/man/2/plumbmsg
+import	/man/2/rfc822
+import	/man/2/sets
+import	/man/2/sh
+import	/man/2/spki-verifier
+import	/man/2/spree
+import	/man/2/spree-allow
+import	/man/2/spree-cardlib
+import	/man/2/spree-gather
+import	/man/2/spree-objstore
+import	/man/2/stringinttab
+import	/man/2/styxservers
+import	/man/2/styxservers-nametree
+import	/man/2/tk
+import	/man/2/translate
+import	/man/2/venti
+import	/man/2/w3c-css
+import	/man/2/w3c-uris
+import	/man/2/w3c-xpointers
+import	/man/2/wmsrv
+import	/man/2/xml
+import	/man/3/cap
+import	/man/3/draw
+import	/man/3/srv9
+import	/man/4/9srvfs
+import	/man/4/grid-cpu
+import	/man/4/import
+import	/man/6/dis
+import	/man/6/namespace
+import.b	/man/4/import
+import.name	/man/2/w3c-css
+importance	/man/2/readdir
+importantly	/man/2/draw-0intro
+imported	/man/1/grid-query
+imported	/man/1/grid-session
+imported	/man/1/sh-alphabet
+imported	/man/1/tktester
+imported	/man/10/2c
+imported	/man/10/2l
+imported	/man/10/dynld
+imported	/man/2/dis
+imported	/man/2/sys-self
+imported	/man/4/grid-cpu
+imported	/man/4/namespace
+imported	/man/6/dis
+imported	/man/6/sbl
+importing	/man/1/grid-session
+imports	/man/1/sh-alphabet
+imports	/man/2/dis
+imports	/man/2/w3c-css
+imports	/man/3/ip
+imports	/man/4/grid-cpu
+impose	/man/2/sys-0intro
+impose	/man/3/flash
+imposed	/man/1/sh
+imposed	/man/10/dmainit
+imposed	/man/2/0intro
+imposed	/man/3/flash
+imposed	/man/6/sexprs
+imposes	/man/5/walk
+impossible	/man/1/tktester
+impossible	/man/10/c2l
+imprecise	/man/10/sleep
+improper	/man/2/keyring-certtostr
+improve	/man/1/blur
+improve	/man/3/logfs
+improve	/man/6/dis
+in.getc	/man/1/yacc
+in.ungetc	/man/1/yacc
+inaccessible	/man/1/m4
+inaccessible	/man/1/ns
+inaccessible	/man/1/os
+inaccurate	/man/2/prof
+inactivity	/man/2/volume
+inactivity	/man/3/vga
+inadequate	/man/8/rip
+inappropriate	/man/10/2c
+inappropriate	/man/2/convcs
+inb	/man/10/inb
+inbalt	/man/2/dis
+inbound	/man/4/factotum
+inc	/man/1/avr
+inc	/man/2/keyring-0intro
+inc	/man/2/palmfile
+inc	/man/2/security-0intro
+inc	/man/3/ssl
+inc	/man/9/0intro
+inc	/man/9/1copyright
+inch	/man/1/units
+inch	/man/2/print
+inch	/man/2/pslib
+inch	/man/9/types
+inches	/man/2/print
+inches	/man/9/types
+incidental	/man/9/1copyright
+incl	/man/1/acme
+include	/man/1/acme
+include	/man/1/alphabet-abc
+include	/man/1/alphabet-fs
+include	/man/1/alphabet-grid
+include	/man/1/alphabet-main
+include	/man/1/calc
+include	/man/1/charon
+include	/man/1/disdep
+include	/man/1/fc
+include	/man/1/fs
+include	/man/1/limbo
+include	/man/1/look
+include	/man/1/m4
+include	/man/1/mash-tk
+include	/man/1/mdb
+include	/man/1/nsbuild
+include	/man/1/plumb
+include	/man/1/sh
+include	/man/1/sh-std
+include	/man/1/sh-tk
+include	/man/1/wm
+include	/man/1/yacc
+include	/man/10/0intro
+include	/man/10/2a
+include	/man/10/2c
+include	/man/10/a.out
+include	/man/10/ar
+include	/man/10/c2l
+include	/man/10/conf
+include	/man/10/dynld
+include	/man/10/error
+include	/man/10/intrenable
+include	/man/10/kbdputc
+include	/man/10/master
+include	/man/10/plan9.ini
+include	/man/10/styx
+include	/man/10/styxserver
+include	/man/2/0intro
+include	/man/2/arg
+include	/man/2/asn1
+include	/man/2/attrdb
+include	/man/2/bloomfilter
+include	/man/2/bufio
+include	/man/2/bufio-chanfill
+include	/man/2/cfg
+include	/man/2/command
+include	/man/2/complete
+include	/man/2/convcs
+include	/man/2/crc
+include	/man/2/csv
+include	/man/2/daytime
+include	/man/2/dbm
+include	/man/2/debug
+include	/man/2/devpointer
+include	/man/2/dhcpclient
+include	/man/2/dial
+include	/man/2/dialog
+include	/man/2/dict
+include	/man/2/dis
+include	/man/2/diskblocks
+include	/man/2/disks
+include	/man/2/dividers
+include	/man/2/draw-0intro
+include	/man/2/draw-context
+include	/man/2/draw-display
+include	/man/2/draw-example
+include	/man/2/draw-font
+include	/man/2/draw-image
+include	/man/2/draw-point
+include	/man/2/draw-pointer
+include	/man/2/draw-rect
+include	/man/2/draw-screen
+include	/man/2/drawmux
+include	/man/2/encoding
+include	/man/2/env
+include	/man/2/ether
+include	/man/2/exception
+include	/man/2/factotum
+include	/man/2/filepat
+include	/man/2/filter
+include	/man/2/filter-deflate
+include	/man/2/filter-slip
+include	/man/2/format
+include	/man/2/fsproto
+include	/man/2/geodesy
+include	/man/2/hash
+include	/man/2/ida
+include	/man/2/imagefile
+include	/man/2/ip
+include	/man/2/ir
+include	/man/2/itslib
+include	/man/2/json
+include	/man/2/keyring-0intro
+include	/man/2/keyring-auth
+include	/man/2/keyring-certtostr
+include	/man/2/keyring-crypt
+include	/man/2/keyring-gensk
+include	/man/2/keyring-getmsg
+include	/man/2/keyring-getstring
+include	/man/2/keyring-ipint
+include	/man/2/keyring-rc4
+include	/man/2/keyring-sha1
+include	/man/2/keyset
+include	/man/2/lists
+include	/man/2/lock
+include	/man/2/math-0intro
+include	/man/2/math-elem
+include	/man/2/math-export
+include	/man/2/math-fp
+include	/man/2/math-linalg
+include	/man/2/mpeg
+include	/man/2/msgio
+include	/man/2/names
+include	/man/2/newns
+include	/man/2/palmfile
+include	/man/2/plumbmsg
+include	/man/2/pop3
+include	/man/2/popup
+include	/man/2/prefab-0intro
+include	/man/2/prefab-compound
+include	/man/2/prefab-element
+include	/man/2/prefab-environ
+include	/man/2/prefab-style
+include	/man/2/print
+include	/man/2/prof
+include	/man/2/pslib
+include	/man/2/rand
+include	/man/2/readdir
+include	/man/2/regex
+include	/man/2/registries
+include	/man/2/rfc822
+include	/man/2/scsiio
+include	/man/2/secstore
+include	/man/2/security-0intro
+include	/man/2/security-auth
+include	/man/2/security-login
+include	/man/2/security-random
+include	/man/2/security-ssl
+include	/man/2/selectfile
+include	/man/2/sets
+include	/man/2/sexprs
+include	/man/2/sh
+include	/man/2/smtp
+include	/man/2/spki
+include	/man/2/spki-verifier
+include	/man/2/spree
+include	/man/2/spree-allow
+include	/man/2/spree-cardlib
+include	/man/2/spree-gather
+include	/man/2/spree-objstore
+include	/man/2/srv
+include	/man/2/string
+include	/man/2/stringinttab
+include	/man/2/styx
+include	/man/2/styxconv
+include	/man/2/styxflush
+include	/man/2/styxpersist
+include	/man/2/styxservers
+include	/man/2/styxservers-nametree
+include	/man/2/sys-0intro
+include	/man/2/sys-bind
+include	/man/2/sys-byte2char
+include	/man/2/sys-chdir
+include	/man/2/sys-dial
+include	/man/2/sys-dirread
+include	/man/2/sys-dup
+include	/man/2/sys-export
+include	/man/2/sys-fauth
+include	/man/2/sys-fd2path
+include	/man/2/sys-file2chan
+include	/man/2/sys-fversion
+include	/man/2/sys-iounit
+include	/man/2/sys-millisec
+include	/man/2/sys-open
+include	/man/2/sys-pctl
+include	/man/2/sys-pipe
+include	/man/2/sys-print
+include	/man/2/sys-read
+include	/man/2/sys-remove
+include	/man/2/sys-seek
+include	/man/2/sys-self
+include	/man/2/sys-sleep
+include	/man/2/sys-stat
+include	/man/2/sys-tokenize
+include	/man/2/sys-utfbytes
+include	/man/2/sys-werrstr
+include	/man/2/tabs
+include	/man/2/timers
+include	/man/2/tk
+include	/man/2/tkclient
+include	/man/2/translate
+include	/man/2/ubfa
+include	/man/2/venti
+include	/man/2/virgil
+include	/man/2/volume
+include	/man/2/w3c-css
+include	/man/2/w3c-uris
+include	/man/2/w3c-xpointers
+include	/man/2/wmclient
+include	/man/2/wmlib
+include	/man/2/wmsrv
+include	/man/2/workdir
+include	/man/2/xml
+include	/man/3/cons
+include	/man/3/dbg
+include	/man/3/draw
+include	/man/3/flash
+include	/man/3/floppy
+include	/man/3/root
+include	/man/3/rtc
+include	/man/3/srv
+include	/man/3/tls
+include	/man/4/factotum
+include	/man/4/kfs
+include	/man/4/namespace
+include	/man/4/registry
+include	/man/5/0intro
+include	/man/6/dis
+include	/man/6/keyboard
+include	/man/6/man
+include	/man/6/namespace
+include	/man/6/ndb
+include	/man/6/plumbing
+include	/man/6/sexprs
+include	/man/7/db
+include	/man/8/cs
+include	/man/8/init
+include	/man/8/ping
+include	/man/8/prep
+include	/man/8/rdbgsrv
+include	/man/8/rip
+include	/man/9/canvas
+include	/man/9/entry
+include	/man/9/grab
+include	/man/9/listbox
+included	/man/1/alphabet-fs
+included	/man/1/cook
+included	/man/1/fs
+included	/man/1/m4
+included	/man/1/mk
+included	/man/1/mux
+included	/man/1/sh
+included	/man/1/time
+included	/man/1/uuencode
+included	/man/10/2c
+included	/man/10/2l
+included	/man/10/5cv
+included	/man/10/a.out
+included	/man/10/c2l
+included	/man/10/conf
+included	/man/10/kproc
+included	/man/10/mk
+included	/man/10/plan9.ini
+included	/man/2/cfg
+included	/man/2/csv
+included	/man/2/encoding
+included	/man/2/keyring-sha1
+included	/man/2/prefab-compound
+included	/man/2/security-0intro
+included	/man/3/cap
+included	/man/3/i82365
+included	/man/3/pbus
+included	/man/3/plap
+included	/man/4/acme
+included	/man/5/0intro
+included	/man/5/stat
+included	/man/7/db
+included	/man/8/create
+included	/man/8/prep
+included	/man/8/virgild
+included	/man/9/1copyright
+included	/man/9/text
+includes	/man/1/acme
+includes	/man/1/collab
+includes	/man/1/gettar
+includes	/man/1/grid-register
+includes	/man/1/uuencode
+includes	/man/10/2c
+includes	/man/10/2l
+includes	/man/10/kproc
+includes	/man/10/mk
+includes	/man/10/odbc
+includes	/man/10/plan9.ini
+includes	/man/10/styx
+includes	/man/10/xalloc
+includes	/man/2/alphabet-intro
+includes	/man/2/bufio
+includes	/man/2/command
+includes	/man/2/ir
+includes	/man/2/keyring-getstring
+includes	/man/2/keyring-ipint
+includes	/man/2/msgio
+includes	/man/2/security-0intro
+includes	/man/2/sys-print
+includes	/man/2/w3c-css
+includes	/man/2/w3c-uris
+includes	/man/3/0intro
+includes	/man/3/eia
+includes	/man/3/pbus
+includes	/man/3/vga
+includes	/man/5/0intro
+includes	/man/5/version
+includes	/man/6/keytext
+includes	/man/6/proto
+includes	/man/6/sbl
+includes	/man/8/create
+includes	/man/9/listbox
+includes	/man/9/text
+inclusion	/man/10/plan9.ini
+inclusion	/man/2/prefab-element
+inclusive	/man/1/calc
+inclusive	/man/1/strings
+inclusive	/man/10/plan9.ini
+inclusive	/man/2/keyring-ipint
+inclusive	/man/2/sets
+inclusive	/man/2/spree-cardlib
+inclusive	/man/2/string
+inclusive	/man/6/font
+inclusive	/man/6/regexp
+inclusive	/man/9/canvas
+inclusive	/man/9/entry
+inclusive	/man/9/listbox
+inclusive	/man/9/menu
+incoming	/man/1/dmview
+incoming	/man/1/grid-monitor
+incoming	/man/1/grid-ns
+incoming	/man/1/grid-register
+incoming	/man/1/listen
+incoming	/man/2/dial
+incoming	/man/2/draw-context
+incoming	/man/2/security-login
+incoming	/man/2/styx
+incoming	/man/2/sys-dial
+incoming	/man/2/translate
+incoming	/man/3/ether
+incoming	/man/3/ip
+incoming	/man/3/plap
+incoming	/man/3/ssl
+incoming	/man/3/tls
+incoming	/man/4/ftpfs
+incoming	/man/4/grid-cpu
+incoming	/man/4/lockfs
+incoming	/man/6/plumbing
+incoming	/man/7/dbsrv
+incoming	/man/8/bootpd
+incoming	/man/8/collabsrv
+incoming	/man/8/httpd
+incoming	/man/8/rstyxd
+incoming	/man/8/svc
+incompatible	/man/1/sh-alphabet
+incompatible	/man/10/c2l
+incomplete	/man/1/charon
+incomplete	/man/10/2c
+incomplete	/man/10/devattach
+incomplete	/man/2/asn1
+incomplete	/man/2/sys-utfbytes
+inconsistencies	/man/3/logfs
+inconsistencies	/man/8/applylog
+inconsistency	/man/2/ida
+inconsistency	/man/8/applylog
+inconsistent	/man/10/lock
+inconsistent	/man/2/0intro
+inconsistent	/man/2/asn1
+inconsistent	/man/2/scsiio
+inconsistent	/man/2/string
+inconsistent	/man/3/tinyfs
+inconvenient	/man/2/sys-read
+incorporate	/man/1/yacc
+incorporated	/man/2/prefab-element
+incorporates	/man/2/cfg
+incorporates	/man/2/draw-font
+incorporating	/man/1/tkcmd
+incorrect	/man/2/geodesy
+incorrect	/man/6/utf
+incorrectly	/man/10/styx
+incorrectly	/man/2/asn1
+incorrectly	/man/2/imagefile
+incr	/man/1/m4
+increase	/man/1/limbo
+increase	/man/2/draw-0intro
+increase	/man/2/draw-point
+increase	/man/2/volume
+increase	/man/3/ftl
+increase	/man/6/colour
+increase	/man/9/grid
+increased	/man/1/wm-misc
+increased	/man/9/grid
+increases	/man/2/draw-image
+increases	/man/9/grid
+increasing	/man/1/mk
+increasing	/man/1/sort
+increasing	/man/10/mk
+increasing	/man/10/plan9.ini
+increasing	/man/2/stringinttab
+increasing	/man/3/prof
+increasing	/man/4/dbfs
+increasing	/man/6/dis
+increasing	/man/6/font
+increasing	/man/8/applylog
+increasingly	/man/1/emu
+increasingly	/man/3/cons
+incref	/man/10/ref
+increment	/man/1/calc
+increment	/man/10/dev
+increment	/man/6/sbl
+increment	/man/9/canvas
+incremented	/man/1/m4
+incremented	/man/10/allocb
+incremented	/man/10/newchan
+incremented	/man/2/alphabet-intro
+incremented	/man/5/0intro
+incremented	/man/6/dis
+incremented	/man/8/collabsrv
+incremented	/man/8/styxchat
+incremented	/man/9/grid
+incremented	/man/9/scale
+increments	/man/1/mdb
+increments	/man/10/ref
+increments	/man/3/ssl
+increments	/man/4/keyfs
+increments	/man/9/scale
+incurred	/man/4/iostats
+incurs	/man/1/kill
+incurs	/man/2/exception
+incurs	/man/3/prog
+indecipherable	/man/2/security-0intro
+indeed	/man/1/0intro
+indeed	/man/10/intrenable
+indeed	/man/2/security-0intro
+indeed	/man/2/sys-fd2path
+indeed	/man/2/w3c-uris
+indeed	/man/3/srv9
+indeed	/man/4/9srvfs
+indeed	/man/4/ramfile
+indefinite	/man/2/asn1
+indefinite	/man/2/spree
+indefinitely	/man/1/itest
+indent	/man/1/disdep
+indent	/man/1/fmt
+indent	/man/6/man
+indentation	/man/6/proto
+indentation	/man/8/create
+indentation	/man/9/text
+indented	/man/1/deb
+indented	/man/6/man
+indented	/man/9/text
+indenting	/man/1/fmt
+indents	/man/6/man
+independence	/man/5/0intro
+independent	/man/1/0intro
+independent	/man/1/limbo
+independent	/man/1/math-misc
+independent	/man/1/tiny
+independent	/man/10/2c
+independent	/man/10/conf
+independent	/man/10/dev
+independent	/man/10/styx
+independent	/man/2/bloomfilter
+independent	/man/2/draw-display
+independent	/man/2/draw-image
+independent	/man/2/styx
+independent	/man/2/sys-0intro
+independent	/man/2/w3c-uris
+independent	/man/3/audio
+independent	/man/3/draw
+independent	/man/3/env
+independent	/man/3/kprof
+independent	/man/3/logfs
+independent	/man/3/ssl
+independent	/man/4/factotum
+independent	/man/5/stat
+independent	/man/6/dis
+independent	/man/6/image
+independent	/man/6/utf
+independent	/man/7/db
+independent	/man/8/collabsrv
+independent	/man/8/register
+independent	/man/8/signer
+independent	/man/9/canvas
+independently	/man/1/sh
+independently	/man/10/allocb
+independently	/man/10/ar
+independently	/man/2/styxservers
+independently	/man/2/sys-0intro
+independently	/man/4/logfile
+independently	/man/8/applylog
+independently	/man/9/bind
+index	/man/1/0intro
+index	/man/1/cook
+index	/man/1/ebook
+index	/man/1/m4
+index	/man/1/man
+index	/man/1/sh-std
+index	/man/1/sh-string
+index	/man/1/webgrab
+index	/man/10/ar
+index	/man/10/devattach
+index	/man/2/0intro
+index	/man/2/alphabet-intro
+index	/man/2/asn1
+index	/man/2/convcs
+index	/man/2/dialog
+index	/man/2/dis
+index	/man/2/draw-display
+index	/man/2/format
+index	/man/2/ip
+index	/man/2/palmfile
+index	/man/2/popup
+index	/man/2/prefab-compound
+index	/man/2/regex
+index	/man/2/spree
+index	/man/2/spree-cardlib
+index	/man/2/styxservers
+index	/man/2/sys-byte2char
+index	/man/2/tabs
+index	/man/2/w3c-xpointers
+index	/man/2/xml
+index	/man/3/draw
+index	/man/3/pnp
+index	/man/3/prog
+index	/man/4/acme
+index	/man/4/dbfs
+index	/man/4/grid-cpu
+index	/man/4/registry
+index	/man/4/spree
+index	/man/5/walk
+index	/man/6/colour
+index	/man/6/dis
+index	/man/6/man
+index	/man/6/sbl
+index	/man/8/collabsrv
+index	/man/8/prep
+index	/man/9/canvas
+index	/man/9/choicebutton
+index	/man/9/entry
+index	/man/9/grid
+index	/man/9/listbox
+index	/man/9/menu
+index	/man/9/options
+index	/man/9/text
+index0	/man/9/grid
+index1	/man/9/grid
+index1	/man/9/menu
+index1	/man/9/text
+index2	/man/9/menu
+index2	/man/9/text
+indexed	/man/1/sh-std
+indexed	/man/10/newchan
+indexed	/man/2/hash
+indexed	/man/2/json
+indexed	/man/2/tabs
+indexed	/man/6/colour
+indexed	/man/6/sbl
+indexed	/man/9/entry
+indexing	/man/10/intrenable
+indexing	/man/2/dbm
+indexing	/man/2/math-linalg
+indexing	/man/2/prof
+indexing	/man/9/canvas
+indicate	/man/1/acme
+indicate	/man/1/blur
+indicate	/man/1/charon
+indicate	/man/1/dd
+indicate	/man/1/mash
+indicate	/man/1/mash-tk
+indicate	/man/10/styxserver
+indicate	/man/2/alphabet-intro
+indicate	/man/2/asn1
+indicate	/man/2/dbm
+indicate	/man/2/draw-pointer
+indicate	/man/2/math-linalg
+indicate	/man/2/prefab-compound
+indicate	/man/2/sys-sleep
+indicate	/man/2/sys-stat
+indicate	/man/3/vga
+indicate	/man/4/acme
+indicate	/man/9/canvas
+indicate	/man/9/entry
+indicate	/man/9/grid
+indicate	/man/9/listbox
+indicate	/man/9/pack
+indicate	/man/9/radiobutton
+indicate	/man/9/scrollbar
+indicate	/man/9/text
+indicated	/man/1/acme
+indicated	/man/1/fc
+indicated	/man/1/math-misc
+indicated	/man/10/print
+indicated	/man/2/alphabet-intro
+indicated	/man/2/geodesy
+indicated	/man/2/prefab-0intro
+indicated	/man/2/prefab-element
+indicated	/man/3/ssl
+indicated	/man/4/acme
+indicated	/man/4/dbfs
+indicated	/man/8/create
+indicated	/man/9/1copyright
+indicated	/man/9/bind
+indicated	/man/9/entry
+indicated	/man/9/grid
+indicated	/man/9/listbox
+indicated	/man/9/menu
+indicated	/man/9/scrollbar
+indicated	/man/9/text
+indicates	/man/1/acme
+indicates	/man/1/cprof
+indicates	/man/1/m4
+indicates	/man/1/math-misc
+indicates	/man/1/sh
+indicates	/man/1/tiny
+indicates	/man/10/c2l
+indicates	/man/10/styxserver
+indicates	/man/2/alphabet-intro
+indicates	/man/2/asn1
+indicates	/man/2/convcs
+indicates	/man/2/dbm
+indicates	/man/2/draw-context
+indicates	/man/2/prof
+indicates	/man/2/string
+indicates	/man/2/sys-dirread
+indicates	/man/3/cmd
+indicates	/man/5/0intro
+indicates	/man/5/read
+indicates	/man/6/image
+indicates	/man/7/db
+indicates	/man/9/canvas
+indicates	/man/9/entry
+indicates	/man/9/grid
+indicates	/man/9/listbox
+indicates	/man/9/menu
+indicates	/man/9/options
+indicates	/man/9/radiobutton
+indicates	/man/9/scrollbar
+indicates	/man/9/text
+indicating	/man/1/acme
+indicating	/man/1/cprof
+indicating	/man/1/listen
+indicating	/man/10/odbc
+indicating	/man/2/asn1
+indicating	/man/2/debug
+indicating	/man/2/keyring-getstring
+indicating	/man/2/msgio
+indicating	/man/2/palmfile
+indicating	/man/2/prof
+indicating	/man/3/prog
+indicating	/man/4/acme
+indicating	/man/4/spree
+indicating	/man/5/0intro
+indicating	/man/9/canvas
+indicating	/man/9/entry
+indicating	/man/9/listbox
+indicating	/man/9/options
+indicating	/man/9/scale
+indicating	/man/9/scrollbar
+indication	/man/1/cprof
+indication	/man/3/cons
+indication	/man/4/logfile
+indicator	/man/2/prof
+indicator	/man/9/checkbutton
+indicator	/man/9/menu
+indicator	/man/9/radiobutton
+indicator's	/man/9/menu
+indicatoron	/man/9/checkbutton
+indicatoron	/man/9/radiobutton
+indicators	/man/1/tktester
+indicators	/man/10/atoi
+indicators	/man/9/menu
+indices	/man/1/man
+indices	/man/1/sh-string
+indices	/man/10/2c
+indices	/man/10/c2l
+indices	/man/2/draw-0intro
+indices	/man/2/sys-0intro
+indices	/man/2/w3c-xpointers
+indices	/man/3/draw
+indices	/man/9/canvas
+indices	/man/9/entry
+indices	/man/9/grid
+indices	/man/9/listbox
+indices	/man/9/text
+indir	/man/10/master
+indir	/man/3/indir
+indirect	/man/3/dbg
+indirect	/man/6/dis
+indirect	/man/9/1copyright
+indirection	/man/1/sh
+indirection	/man/10/acid
+indirection	/man/2/dis
+indirectly	/man/10/allocb
+indirectly	/man/10/sleep
+indirectly	/man/2/sexprs
+indirectly	/man/2/sys-pctl
+indirectly	/man/3/dbg
+indirectly	/man/3/indir
+indirectly	/man/3/srv
+indirectly	/man/4/acme
+indirectly	/man/5/clunk
+indirectly	/man/8/cs
+indirectly	/man/8/plumber
+individually	/man/2/bufio
+individually	/man/3/sd
+individually	/man/4/acme
+industry	/man/2/security-0intro
+ineffective	/man/1/acme
+ineffective	/man/1/nsbuild
+inequalities	/man/1/tsort
+inequality	/man/1/mash
+inequality	/man/1/sh-expr
+inewaz	/man/2/dis
+inewz	/man/2/dis
+inex	/man/2/math-fp
+inexact	/man/2/math-fp
+inf	/man/1/sh-test
+inf	/man/8/create
+infauth	/man/4/factotum
+infauth	/man/8/ai2key
+infcsigner	/man/8/signer
+infdb	/man/7/db
+infdb	/man/7/dbsrv
+infdb.exe	/man/7/dbsrv
+infenv	/man/3/srv9
+infenv	/man/4/9srvfs
+infer	/man/1/mash-make
+infer	/man/2/asn1
+infer	/man/2/ir
+inferface	/man/1/wm
+inferno	/man/1/0intro
+inferno	/man/1/9win
+inferno	/man/1/ar
+inferno	/man/1/auplay
+inferno	/man/1/avr
+inferno	/man/1/bind
+inferno	/man/1/charon
+inferno	/man/1/diff
+inferno	/man/1/dmview
+inferno	/man/1/emu
+inferno	/man/1/ftest
+inferno	/man/1/gettar
+inferno	/man/1/grid-ns
+inferno	/man/1/grid-session
+inferno	/man/1/limbo
+inferno	/man/1/listen
+inferno	/man/1/logon
+inferno	/man/1/m4
+inferno	/man/1/mash
+inferno	/man/1/mash-tk
+inferno	/man/1/mdb
+inferno	/man/1/miniterm
+inferno	/man/1/mk
+inferno	/man/1/mux
+inferno	/man/1/nsbuild
+inferno	/man/1/os
+inferno	/man/1/ps
+inferno	/man/1/secstore
+inferno	/man/1/sh
+inferno	/man/1/sh-regex
+inferno	/man/1/sh-std
+inferno	/man/1/sh-tk
+inferno	/man/1/tcs
+inferno	/man/1/tiny
+inferno	/man/1/webgrab
+inferno	/man/1/wish
+inferno	/man/1/wm
+inferno	/man/1/wm-misc
+inferno	/man/1/wm-sh
+inferno	/man/10/0intro
+inferno	/man/10/2a
+inferno	/man/10/2c
+inferno	/man/10/5cv
+inferno	/man/10/9load
+inferno	/man/10/conf
+inferno	/man/10/eve
+inferno	/man/10/inm
+inferno	/man/10/kproc
+inferno	/man/10/memory
+inferno	/man/10/mk
+inferno	/man/10/ntsrv
+inferno	/man/10/odbc
+inferno	/man/10/plan9.ini
+inferno	/man/10/styx
+inferno	/man/10/styxserver
+inferno	/man/2/0intro
+inferno	/man/2/command
+inferno	/man/2/convcs
+inferno	/man/2/dhcpclient
+inferno	/man/2/dis
+inferno	/man/2/disks
+inferno	/man/2/draw-0intro
+inferno	/man/2/draw-example
+inferno	/man/2/ip
+inferno	/man/2/keyring-0intro
+inferno	/man/2/math-0intro
+inferno	/man/2/math-linalg
+inferno	/man/2/msgio
+inferno	/man/2/palmfile
+inferno	/man/2/plumbmsg
+inferno	/man/2/security-0intro
+inferno	/man/2/sh
+inferno	/man/2/srv
+inferno	/man/2/sys-0intro
+inferno	/man/2/sys-chdir
+inferno	/man/2/sys-pctl
+inferno	/man/2/sys-sleep
+inferno	/man/2/workdir
+inferno	/man/3/0intro
+inferno	/man/3/cmd
+inferno	/man/3/cons
+inferno	/man/3/dbg
+inferno	/man/3/dup
+inferno	/man/3/fs
+inferno	/man/3/indir
+inferno	/man/3/ip
+inferno	/man/3/kprof
+inferno	/man/3/logfs
+inferno	/man/3/mnt
+inferno	/man/3/rtc
+inferno	/man/3/snarf
+inferno	/man/3/srv9
+inferno	/man/3/usb
+inferno	/man/4/0intro
+inferno	/man/4/9srvfs
+inferno	/man/4/dossrv
+inferno	/man/4/export
+inferno	/man/4/factotum
+inferno	/man/4/ftpfs
+inferno	/man/4/import
+inferno	/man/4/kfs
+inferno	/man/4/namespace
+inferno	/man/4/trfs
+inferno	/man/5/0intro
+inferno	/man/6/0intro
+inferno	/man/6/attrdb
+inferno	/man/6/audio
+inferno	/man/6/colour
+inferno	/man/6/image
+inferno	/man/6/keyboard
+inferno	/man/6/keys
+inferno	/man/6/keytext
+inferno	/man/6/login
+inferno	/man/6/man
+inferno	/man/6/namespace
+inferno	/man/6/ndb
+inferno	/man/6/sexprs
+inferno	/man/6/utf
+inferno	/man/7/0intro
+inferno	/man/7/dbsrv
+inferno	/man/8/ai2key
+inferno	/man/8/applylog
+inferno	/man/8/bootpd
+inferno	/man/8/create
+inferno	/man/8/cs
+inferno	/man/8/dns
+inferno	/man/8/getauthinfo
+inferno	/man/8/init
+inferno	/man/8/mangaload
+inferno	/man/8/ping
+inferno	/man/8/prep
+inferno	/man/8/rdbgsrv
+inferno	/man/8/svc
+inferno	/man/9/0intro
+inferno	/man/9/send
+inferno	/man/9/types
+inferno's	/man/1/0intro
+inferno's	/man/2/draw-0intro
+inferno's	/man/2/math-0intro
+inferno's	/man/2/styxconv
+inferno's	/man/3/cmd
+inferno's	/man/3/fs
+inferno's	/man/4/factotum
+inferno's	/man/8/cs
+inferno.1.0	/man/8/create
+inferno.arch	/man/8/create
+inferno.c	/man/2/sys-0intro
+inferno.c	/man/2/sys-dial
+inferno.c	/man/2/sys-export
+inferno.c	/man/2/sys-iounit
+infernoos	/man/8/create
+infernosite	/man/6/ndb
+infernosite	/man/8/cs
+inferred	/man/1/sh-alphabet
+inferred	/man/10/acid
+infile	/man/1/cook
+infile	/man/10/ms2
+infinite	/man/2/keyring-0intro
+infinite	/man/2/keyring-ipint
+infinitely	/man/2/math-fp
+infinitely	/man/2/sets
+infinity	/man/1/calc
+infinity	/man/2/math-fp
+infinity	/man/2/math-linalg
+infinity	/man/2/string
+infinity,sqrt	/man/2/math-fp
+infkey	/man/4/keysrv
+inflate	/man/2/filter-deflate
+inflate.b	/man/2/filter-deflate
+inflatepath	/man/2/filter-deflate
+inflogin	/man/2/dial
+inflogin	/man/2/sys-dial
+inflogin	/man/8/cs
+inflogin	/man/8/logind
+influences	/man/2/spree-cardlib
+info	/man/1/sh-alphabet
+info	/man/2/filter
+info	/man/2/itslib
+info	/man/2/keyring-auth
+info	/man/2/security-auth
+info	/man/2/spree
+info	/man/6/font
+info	/man/8/changelogin
+info	/man/8/create
+info	/man/9/grid
+info.b	/man/8/create
+inform	/man/1/wm
+inform	/man/2/dividers
+inform	/man/2/styxflush
+inform	/man/2/tk
+inform	/man/8/prep
+informal	/man/1/acme
+informational	/man/1/sh
+informational	/man/2/spree
+informational	/man/4/ftpfs
+informatory	/man/1/sh-test
+informatory	/man/2/itslib
+informed	/man/2/dividers
+informed	/man/4/spree
+informs	/man/1/wm
+informs	/man/2/spree
+informs	/man/2/tkclient
+informs	/man/5/clunk
+infrared	/man/1/mux
+infrared	/man/2/ir
+infrared	/man/2/prefab-0intro
+infrared	/man/2/prefab-element
+infrared	/man/2/volume
+infrastructure	/man/2/sexprs
+infrastructure	/man/2/spki
+infrastructure	/man/6/sexprs
+infringement	/man/9/1copyright
+infsigner	/man/8/signer
+ing	/man/2/regex
+ing	/man/8/signer
+inherent	/man/9/cursor
+inherit	/man/2/draw-context
+inherited	/man/1/acme
+inherited	/man/1/ns
+inherited	/man/2/msgio
+inherited	/man/2/styxservers
+inherited	/man/2/sys-0intro
+inherited	/man/2/tk
+inherits	/man/1/0intro
+inherits	/man/2/sys-0intro
+init	/man/1/mux
+init	/man/1/stack
+init	/man/1/tiny
+init	/man/1/yacc
+init	/man/10/c2l
+init	/man/10/conf
+init	/man/10/dev
+init	/man/2/alphabet-intro
+init	/man/2/arg
+init	/man/2/asn1
+init	/man/2/attrdb
+init	/man/2/bloomfilter
+init	/man/2/bufio-chanfill
+init	/man/2/cfg
+init	/man/2/command
+init	/man/2/complete
+init	/man/2/convcs
+init	/man/2/crc
+init	/man/2/csv
+init	/man/2/dbm
+init	/man/2/debug
+init	/man/2/devpointer
+init	/man/2/dhcpclient
+init	/man/2/dialog
+init	/man/2/dis
+init	/man/2/diskblocks
+init	/man/2/disks
+init	/man/2/dividers
+init	/man/2/draw-context
+init	/man/2/draw-example
+init	/man/2/drawmux
+init	/man/2/ether
+init	/man/2/factotum
+init	/man/2/filter
+init	/man/2/filter-deflate
+init	/man/2/filter-slip
+init	/man/2/format
+init	/man/2/fsproto
+init	/man/2/geodesy
+init	/man/2/ida
+init	/man/2/imagefile
+init	/man/2/ip
+init	/man/2/ir
+init	/man/2/itslib
+init	/man/2/json
+init	/man/2/keyset
+init	/man/2/lock
+init	/man/2/msgio
+init	/man/2/palmfile
+init	/man/2/plumbmsg
+init	/man/2/popup
+init	/man/2/print
+init	/man/2/prof
+init	/man/2/pslib
+init	/man/2/rand
+init	/man/2/readdir
+init	/man/2/registries
+init	/man/2/rfc822
+init	/man/2/scsiio
+init	/man/2/secstore
+init	/man/2/security-auth
+init	/man/2/selectfile
+init	/man/2/sets
+init	/man/2/sexprs
+init	/man/2/spki
+init	/man/2/spki-verifier
+init	/man/2/spree
+init	/man/2/spree-allow
+init	/man/2/spree-cardlib
+init	/man/2/spree-gather
+init	/man/2/spree-objstore
+init	/man/2/srv
+init	/man/2/styx
+init	/man/2/styxconv
+init	/man/2/styxflush
+init	/man/2/styxpersist
+init	/man/2/styxservers
+init	/man/2/styxservers-nametree
+init	/man/2/sys-self
+init	/man/2/tabs
+init	/man/2/tftp
+init	/man/2/timers
+init	/man/2/tkclient
+init	/man/2/translate
+init	/man/2/ubfa
+init	/man/2/venti
+init	/man/2/w3c-css
+init	/man/2/w3c-uris
+init	/man/2/w3c-xpointers
+init	/man/2/wait
+init	/man/2/wmclient
+init	/man/2/wmlib
+init	/man/2/wmsrv
+init	/man/2/workdir
+init	/man/2/xml
+init	/man/3/0intro
+init	/man/3/ftl
+init	/man/3/mpeg
+init	/man/3/root
+init	/man/3/rtc
+init	/man/3/tinyfs
+init	/man/3/tv
+init	/man/4/namespace
+init	/man/8/init
+initbuiltin	/man/2/sh
+initialisation	/man/1/emu
+initialisation	/man/1/sh
+initialisation	/man/1/toolbar
+initialisation	/man/10/9load
+initialisation	/man/10/allocb
+initialisation	/man/10/conf
+initialisation	/man/10/dev
+initialisation	/man/10/devattach
+initialisation	/man/10/dmainit
+initialisation	/man/10/eve
+initialisation	/man/10/intrenable
+initialisation	/man/10/kbdputc
+initialisation	/man/10/plan9.ini
+initialisation	/man/2/keyring-crypt
+initialisation	/man/2/plumbmsg
+initialisation	/man/2/sh
+initialisation	/man/3/dbg
+initialisation	/man/3/sign
+initialisation	/man/3/touch
+initialisation	/man/4/namespace
+initialisation	/man/6/colour
+initialisation	/man/6/login
+initialisation	/man/8/init
+initialise	/man/1/mash
+initialise	/man/1/miniterm
+initialise	/man/10/conf
+initialise	/man/10/kbdputc
+initialise	/man/2/alphabet-intro
+initialise	/man/2/bloomfilter
+initialise	/man/2/convcs
+initialise	/man/2/debug
+initialise	/man/2/dialog
+initialise	/man/2/dis
+initialise	/man/2/dividers
+initialise	/man/2/draw-0intro
+initialise	/man/2/ir
+initialise	/man/2/palmfile
+initialise	/man/2/popup
+initialise	/man/2/print
+initialise	/man/2/selectfile
+initialise	/man/2/sets
+initialise	/man/2/spree-allow
+initialise	/man/2/spree-cardlib
+initialise	/man/2/styxservers
+initialise	/man/2/styxservers-nametree
+initialise	/man/2/sys-stat
+initialise	/man/2/tabs
+initialise	/man/2/tkclient
+initialise	/man/2/wait
+initialise	/man/2/wmclient
+initialise	/man/2/wmlib
+initialise	/man/2/wmsrv
+initialise	/man/3/dbg
+initialise	/man/3/ftl
+initialise	/man/3/logfs
+initialise	/man/8/0intro
+initialise	/man/8/ftl
+initialise	/man/8/init
+initialised	/man/10/5cv
+initialised	/man/10/dev
+initialised	/man/10/devattach
+initialised	/man/10/kbdputc
+initialised	/man/10/malloc
+initialised	/man/10/qlock
+initialised	/man/2/asn1
+initialised	/man/2/convcs
+initialised	/man/2/dhcpclient
+initialised	/man/2/draw-context
+initialised	/man/2/draw-display
+initialised	/man/2/itslib
+initialised	/man/2/pslib
+initialised	/man/2/rfc822
+initialised	/man/2/sh
+initialised	/man/2/spree
+initialised	/man/2/sys-stat
+initialised	/man/2/timers
+initialised	/man/2/xml
+initialised	/man/3/mpeg
+initialised	/man/4/dbfs
+initialised	/man/4/registry
+initialised	/man/8/touchcal
+initialiser	/man/10/dev
+initialises	/man/1/avr
+initialises	/man/10/devattach
+initialises	/man/10/qio
+initialises	/man/2/cfg
+initialises	/man/2/diskblocks
+initialises	/man/2/rand
+initialises	/man/2/spree
+initialises	/man/2/sys-fversion
+initialises	/man/3/logfs
+initialises	/man/8/manufacture
+initialises	/man/8/prep
+initialises	/man/8/signer
+initialising	/man/10/dev
+initialising	/man/2/convcs
+initialising	/man/2/dis
+initialising	/man/3/root
+initialising	/man/8/init
+initialization	/man/1/cprof
+initialization	/man/1/limbo
+initialization	/man/1/sh
+initialization	/man/10/plan9.ini
+initialization	/man/10/styxserver
+initialization	/man/3/tls
+initialize	/man/1/zeros
+initialize	/man/10/9load
+initialize	/man/2/bufio
+initialize	/man/2/crc
+initialize	/man/2/draw-example
+initialize	/man/2/imagefile
+initialize	/man/2/ir
+initialize	/man/2/print
+initialize	/man/2/sys-fversion
+initialize	/man/2/sys-stat
+initialize	/man/3/vga
+initialize	/man/8/prep
+initialized	/man/1/calc
+initialized	/man/1/sh
+initialized	/man/10/a.out
+initialized	/man/2/disks
+initialized	/man/2/draw-display
+initialized	/man/2/geodesy
+initializers	/man/10/2c
+initializes	/man/10/2c
+initializes	/man/10/styxserver
+initializes	/man/2/crc
+initializes	/man/2/prof
+initializes	/man/3/0intro
+initializes	/man/5/0intro
+initializes	/man/5/version
+initializing	/man/8/mkfs
+initially	/man/1/calc
+initially	/man/1/charon
+initially	/man/1/collab-clients
+initially	/man/1/filename
+initially	/man/1/ftree
+initially	/man/1/logwindow
+initially	/man/1/m4
+initially	/man/1/mash
+initially	/man/1/mdb
+initially	/man/1/mk
+initially	/man/1/sh-alphabet
+initially	/man/1/wish
+initially	/man/10/allocb
+initially	/man/10/devattach
+initially	/man/10/mk
+initially	/man/10/plan9.ini
+initially	/man/10/qio
+initially	/man/2/attrdb
+initially	/man/2/keyring-crypt
+initially	/man/2/security-0intro
+initially	/man/2/styxservers
+initially	/man/2/tabs
+initially	/man/2/tk
+initially	/man/3/draw
+initially	/man/3/eia
+initially	/man/3/flash
+initially	/man/3/srv
+initially	/man/3/tls
+initially	/man/4/keyfs
+initially	/man/4/kfs
+initially	/man/4/lockfs
+initially	/man/4/mntgen
+initially	/man/4/ramfile
+initially	/man/4/registry
+initially	/man/6/dis
+initiate	/man/2/exception
+initiate	/man/3/ip
+initiate	/man/3/prof
+initiated	/man/1/acme
+initiated	/man/1/wm
+initiates	/man/7/db
+initscript	/man/1/listen
+inject	/man/3/ip
+inl	/man/10/inb
+inline	/man/2/xml
+inlining	/man/1/limbo
+inm	/man/10/2c
+inm	/man/10/2l
+inm	/man/10/a.out
+inm	/man/10/ar
+inm	/man/10/inm
+inm	/man/10/srclist
+inner	/man/10/error
+inner	/man/2/keyring-sha1
+inner	/man/2/math-linalg
+inner	/man/2/spree-cardlib
+inner	/man/6/sexprs
+innermost	/man/1/sh
+innermost	/man/1/sh-std
+innermost	/man/2/sh
+innermost	/man/6/sbl
+innermost	/man/6/sexprs
+innovative	/man/1/0intro
+inop	/man/2/dis
+inportb	/man/3/arch
+input	/man/1/9win
+input	/man/1/acme
+input	/man/1/alphabet-abc
+input	/man/1/alphabet-grid
+input	/man/1/asm
+input	/man/1/auplay
+input	/man/1/bind
+input	/man/1/calc
+input	/man/1/cat
+input	/man/1/charon
+input	/man/1/comm
+input	/man/1/cook
+input	/man/1/crypt
+input	/man/1/dd
+input	/man/1/diff
+input	/man/1/disdep
+input	/man/1/emu
+input	/man/1/fmt
+input	/man/1/freq
+input	/man/1/fs
+input	/man/1/gettar
+input	/man/1/grep
+input	/man/1/grid-monitor
+input	/man/1/grid-ns
+input	/man/1/gzip
+input	/man/1/idea
+input	/man/1/keyboard
+input	/man/1/limbo
+input	/man/1/listen
+input	/man/1/logon
+input	/man/1/logwindow
+input	/man/1/look
+input	/man/1/m4
+input	/man/1/man
+input	/man/1/mash
+input	/man/1/math-misc
+input	/man/1/mc
+input	/man/1/mdb
+input	/man/1/mk
+input	/man/1/os
+input	/man/1/p
+input	/man/1/plumb
+input	/man/1/read
+input	/man/1/secstore
+input	/man/1/sendmail
+input	/man/1/sh
+input	/man/1/sh-csv
+input	/man/1/sh-file2chan
+input	/man/1/sh-sexprs
+input	/man/1/sh-std
+input	/man/1/sleep
+input	/man/1/sort
+input	/man/1/strings
+input	/man/1/sum
+input	/man/1/tail
+input	/man/1/tcs
+input	/man/1/tee
+input	/man/1/telnet
+input	/man/1/timestamp
+input	/man/1/tiny
+input	/man/1/tkcmd
+input	/man/1/tr
+input	/man/1/tsort
+input	/man/1/uniq
+input	/man/1/uuencode
+input	/man/1/wc
+input	/man/1/wish
+input	/man/1/wm
+input	/man/1/wm-sh
+input	/man/1/xd
+input	/man/10/2a
+input	/man/10/2c
+input	/man/10/5cv
+input	/man/10/9load
+input	/man/10/acid
+input	/man/10/atoi
+input	/man/10/dmainit
+input	/man/10/kstrip
+input	/man/10/mk
+input	/man/10/plan9.ini
+input	/man/10/rune
+input	/man/2/arg
+input	/man/2/bufio
+input	/man/2/cfg
+input	/man/2/convcs
+input	/man/2/draw-context
+input	/man/2/draw-rect
+input	/man/2/filter
+input	/man/2/filter-deflate
+input	/man/2/ida
+input	/man/2/ip
+input	/man/2/ir
+input	/man/2/json
+input	/man/2/keyring-getstring
+input	/man/2/math-export
+input	/man/2/msgio
+input	/man/2/plumbmsg
+input	/man/2/rfc822
+input	/man/2/sexprs
+input	/man/2/styx
+input	/man/2/sys-0intro
+input	/man/2/sys-byte2char
+input	/man/2/sys-dup
+input	/man/2/timers
+input	/man/2/ubfa
+input	/man/2/volume
+input	/man/2/wmclient
+input	/man/2/wmlib
+input	/man/3/audio
+input	/man/3/cmd
+input	/man/3/cons
+input	/man/3/dbg
+input	/man/3/eia
+input	/man/3/ip
+input	/man/3/pbus
+input	/man/3/pointer
+input	/man/3/srv9
+input	/man/3/tv
+input	/man/3/usb
+input	/man/4/9srvfs
+input	/man/4/acme
+input	/man/4/export
+input	/man/4/factotum
+input	/man/4/grid-cpu
+input	/man/4/import
+input	/man/4/iostats
+input	/man/4/keysrv
+input	/man/4/kfs
+input	/man/4/mntgen
+input	/man/4/registry
+input	/man/4/vacfs
+input	/man/6/man
+input	/man/6/ubfa
+input	/man/6/utf
+input	/man/8/applylog
+input	/man/8/changelogin
+input	/man/8/cs
+input	/man/8/dns
+input	/man/8/plumber
+input	/man/8/rstyxd
+input	/man/8/signer
+input	/man/8/styxchat
+input	/man/8/styxmon
+input	/man/9/canvas
+input	/man/9/entry
+input	/man/9/listbox
+input	/man/9/menubutton
+input	/man/9/options
+input	/man/9/text
+input's	/man/1/read
+inquire	/man/2/scsiio
+inquire	/man/5/stat
+inquires	/man/5/stat
+inquiry	/man/2/scsiio
+inquiry	/man/3/sd
+inrect	/man/2/draw-rect
+ins	/man/1/acme
+ins	/man/10/inb
+insb	/man/10/inb
+insensitive	/man/1/grep
+insensitive	/man/10/eve
+insensitive	/man/2/encoding
+insensitive	/man/3/fs
+insensitive	/man/9/button
+insensitive	/man/9/checkbutton
+insensitive	/man/9/grab
+insensitive	/man/9/menu
+insensitive	/man/9/menubutton
+insensitive	/man/9/radiobutton
+inseparable	/man/2/dbm
+insert	/man/1/acme
+insert	/man/1/mash
+insert	/man/1/sh
+insert	/man/1/tktester
+insert	/man/1/toolbar
+insert	/man/10/2l
+insert	/man/10/odbc
+insert	/man/2/hash
+insert	/man/4/spree
+insert	/man/9/canvas
+insert	/man/9/entry
+insert	/man/9/grid
+insert	/man/9/listbox
+insert	/man/9/menu
+insert	/man/9/pack
+insert	/man/9/text
+inserted	/man/1/fmt
+inserted	/man/1/mash
+inserted	/man/1/sh
+inserted	/man/1/wm-sh
+inserted	/man/10/2l
+inserted	/man/10/ar
+inserted	/man/2/dbm
+inserted	/man/2/scsiio
+inserted	/man/2/security-0intro
+inserted	/man/3/ether
+inserted	/man/3/tls
+inserted	/man/4/acme
+inserted	/man/9/canvas
+inserted	/man/9/entry
+inserted	/man/9/grid
+inserted	/man/9/pack
+inserted	/man/9/text
+inserting	/man/10/mk
+inserting	/man/6/man
+inserting	/man/9/canvas
+inserting	/man/9/text
+insertion	/man/1/acme
+insertion	/man/1/brutus
+insertion	/man/10/ar
+insertion	/man/2/tabs
+insertion	/man/9/canvas
+insertion	/man/9/entry
+insertion	/man/9/text
+insertions	/man/1/acme
+inserts	/man/1/acme
+inserts	/man/1/bind
+inserts	/man/1/tktester
+inserts	/man/10/allocb
+inserts	/man/10/iar
+inserts	/man/9/grid
+inserts	/man/9/listbox
+inserts	/man/9/menu
+inserts	/man/9/text
+inset	/man/2/draw-rect
+inside	/man/1/9win
+inside	/man/1/brutus
+inside	/man/1/sh-string
+inside	/man/1/tiny
+inside	/man/1/wm
+inside	/man/1/wm-sh
+inside	/man/1/yacc
+inside	/man/10/devattach
+inside	/man/2/alphabet-intro
+inside	/man/2/draw-image
+inside	/man/2/draw-rect
+inside	/man/2/spree
+inside	/man/2/spree-cardlib
+inside	/man/2/styxservers
+inside	/man/2/styxservers-nametree
+inside	/man/2/xml
+inside	/man/3/draw
+inside	/man/3/prof
+inside	/man/3/vga
+inside	/man/5/0intro
+inside	/man/8/touchcal
+inside	/man/9/canvas
+inside	/man/9/grid
+inside	/man/9/options
+inside	/man/9/panel
+insl	/man/10/inb
+inspect	/man/1/deb
+inspect	/man/10/acid
+inspected	/man/2/json
+inspected	/man/2/sys-utfbytes
+inspected	/man/2/ubfa
+inspecting	/man/2/debug
+inspecting	/man/4/factotum
+inspection	/man/1/0intro
+inspection	/man/1/mdb
+inspection	/man/2/debug
+inspector	/man/1/wm-misc
+inspired	/man/3/ds
+inspired	/man/9/canvas
+inss	/man/10/inb
+inst	/man/2/dis
+inst	/man/8/create
+inst.b	/man/8/create
+inst2s	/man/2/dis
+install	/man/1/mk
+install	/man/10/9load
+install	/man/6/proto
+install	/man/8/applylog
+install	/man/8/create
+install	/man/8/prep
+installation	/man/1/0intro
+installation	/man/10/master
+installation	/man/8/create
+installations	/man/10/master
+installed	/man/1/m4
+installed	/man/1/sh-alphabet
+installed	/man/1/sh-std
+installed	/man/10/9load
+installed	/man/10/print
+installed	/man/10/styx
+installed	/man/2/daytime
+installed	/man/3/sign
+installed	/man/4/namespace
+installed	/man/8/create
+installed	/man/8/prep
+installing	/man/1/m4
+installing	/man/2/palmfile
+installs	/man/1/sh-std
+installs	/man/8/changelogin
+installs	/man/8/create
+installs	/man/8/prep
+instance	/man/1/acme
+instance	/man/1/alphabet-fs
+instance	/man/1/ar
+instance	/man/1/calendar
+instance	/man/1/collab
+instance	/man/1/collab-clients
+instance	/man/1/dmview
+instance	/man/1/emu
+instance	/man/1/fs
+instance	/man/1/ftree
+instance	/man/1/grid-ns
+instance	/man/1/listen
+instance	/man/1/ls
+instance	/man/1/mk
+instance	/man/1/sh
+instance	/man/1/sh-alphabet
+instance	/man/1/sh-expr
+instance	/man/1/sh-mload
+instance	/man/1/sh-string
+instance	/man/1/spree-join
+instance	/man/1/toolbar
+instance	/man/1/tsort
+instance	/man/1/wm
+instance	/man/1/wm-sh
+instance	/man/1/yacc
+instance	/man/10/conf
+instance	/man/10/dev
+instance	/man/10/iar
+instance	/man/10/intrenable
+instance	/man/10/kbdputc
+instance	/man/10/mk
+instance	/man/10/newchan
+instance	/man/10/seconds
+instance	/man/10/sleep
+instance	/man/10/styxserver
+instance	/man/2/0intro
+instance	/man/2/alphabet-intro
+instance	/man/2/asn1
+instance	/man/2/bufio
+instance	/man/2/bufio-chanfill
+instance	/man/2/command
+instance	/man/2/csv
+instance	/man/2/dhcpclient
+instance	/man/2/dial
+instance	/man/2/dialog
+instance	/man/2/draw-0intro
+instance	/man/2/draw-context
+instance	/man/2/draw-font
+instance	/man/2/factotum
+instance	/man/2/ida
+instance	/man/2/imagefile
+instance	/man/2/json
+instance	/man/2/keyring-ipint
+instance	/man/2/keyset
+instance	/man/2/palmfile
+instance	/man/2/print
+instance	/man/2/registries
+instance	/man/2/rfc822
+instance	/man/2/secstore
+instance	/man/2/security-auth
+instance	/man/2/selectfile
+instance	/man/2/sexprs
+instance	/man/2/spki
+instance	/man/2/spki-verifier
+instance	/man/2/spree
+instance	/man/2/spree-cardlib
+instance	/man/2/spree-gather
+instance	/man/2/styxconv
+instance	/man/2/styxflush
+instance	/man/2/styxservers
+instance	/man/2/styxservers-nametree
+instance	/man/2/sys-0intro
+instance	/man/2/sys-dial
+instance	/man/2/sys-pipe
+instance	/man/2/sys-print
+instance	/man/2/sys-self
+instance	/man/2/sys-stat
+instance	/man/2/ubfa
+instance	/man/2/w3c-uris
+instance	/man/2/wait
+instance	/man/2/xml
+instance	/man/3/dbg
+instance	/man/3/ether
+instance	/man/3/flash
+instance	/man/3/fs
+instance	/man/3/indir
+instance	/man/3/ip
+instance	/man/3/pnp
+instance	/man/3/pointer
+instance	/man/3/root
+instance	/man/3/sd
+instance	/man/3/sign
+instance	/man/3/srv
+instance	/man/3/tinyfs
+instance	/man/4/9srvfs
+instance	/man/4/ftpfs
+instance	/man/4/kfs
+instance	/man/4/registry
+instance	/man/5/flush
+instance	/man/6/attrdb
+instance	/man/6/dis
+instance	/man/6/proto
+instance	/man/6/sbl
+instance	/man/6/sexprs
+instance	/man/6/ubfa
+instance	/man/7/db
+instance	/man/8/bootpd
+instance	/man/8/collabsrv
+instance	/man/8/cs
+instance	/man/8/styxmon
+instance	/man/9/see
+instance	/man/9/variable
+instances	/man/1/calendar
+instances	/man/1/ls
+instances	/man/10/error
+instances	/man/2/filter
+instances	/man/2/styxflush
+instances	/man/2/styxservers
+instances	/man/2/sys-dup
+instances	/man/2/w3c-uris
+instances	/man/3/draw
+instances	/man/3/ip
+instances	/man/3/srv
+instances	/man/6/dis
+instances	/man/6/regexp
+instances	/man/8/collabsrv
+instances	/man/9/canvas
+instances	/man/9/image
+instant	/man/10/plan9.ini
+institute	/man/1/sum
+instituted	/man/2/keyring-0intro
+instruction	/man/10/a.out
+instruction	/man/10/acid
+instruction	/man/10/inb
+instruction	/man/2/debug
+instruction	/man/2/dis
+instruction	/man/2/w3c-xpointers
+instruction	/man/3/dbg
+instruction	/man/3/prof
+instruction	/man/3/prog
+instruction	/man/6/dis
+instruction	/man/6/sbl
+instruction's	/man/2/xml
+instructions	/man/1/asm
+instructions	/man/1/cprof
+instructions	/man/1/emu
+instructions	/man/1/mdb
+instructions	/man/1/wm-misc
+instructions	/man/10/2l
+instructions	/man/10/acid
+instructions	/man/10/styx
+instructions	/man/2/dis
+instructions	/man/2/math-0intro
+instructions	/man/2/prof
+instructions	/man/3/boot
+instructions	/man/3/prof
+instructions	/man/3/prog
+instructions	/man/6/dis
+instructions	/man/6/sbl
+instructions	/man/8/collabsrv
+instructions	/man/8/cs
+instructs	/man/1/acme
+insufficient	/man/2/draw-image
+insufficient	/man/3/draw
+insulate	/man/2/command
+insulate	/man/2/disks
+insulate	/man/2/sys-pctl
+insulate	/man/3/env
+insulating	/man/2/styxservers
+int	/man/1/calc
+int	/man/1/yacc
+int	/man/10/2c
+int	/man/10/allocb
+int	/man/10/atoi
+int	/man/10/c2l
+int	/man/10/delay
+int	/man/10/dev
+int	/man/10/devattach
+int	/man/10/dmainit
+int	/man/10/dynld
+int	/man/10/error
+int	/man/10/eve
+int	/man/10/getfields
+int	/man/10/inb
+int	/man/10/intrenable
+int	/man/10/kbdputc
+int	/man/10/kproc
+int	/man/10/lock
+int	/man/10/malloc
+int	/man/10/memory
+int	/man/10/newchan
+int	/man/10/parsecmd
+int	/man/10/print
+int	/man/10/qio
+int	/man/10/qlock
+int	/man/10/readnum
+int	/man/10/ref
+int	/man/10/rune
+int	/man/10/sleep
+int	/man/10/splhi
+int	/man/10/strcat
+int	/man/10/styx
+int	/man/10/styxserver
+int	/man/10/xalloc
+int	/man/2/arg
+int	/man/2/asn1
+int	/man/2/attrdb
+int	/man/2/bloomfilter
+int	/man/2/bufio
+int	/man/2/bufio-chanfill
+int	/man/2/cfg
+int	/man/2/command
+int	/man/2/complete
+int	/man/2/convcs
+int	/man/2/crc
+int	/man/2/daytime
+int	/man/2/dbm
+int	/man/2/debug
+int	/man/2/devpointer
+int	/man/2/dhcpclient
+int	/man/2/dialog
+int	/man/2/dis
+int	/man/2/diskblocks
+int	/man/2/disks
+int	/man/2/dividers
+int	/man/2/draw-0intro
+int	/man/2/draw-context
+int	/man/2/draw-display
+int	/man/2/draw-font
+int	/man/2/draw-image
+int	/man/2/draw-point
+int	/man/2/draw-pointer
+int	/man/2/draw-rect
+int	/man/2/draw-screen
+int	/man/2/env
+int	/man/2/ether
+int	/man/2/exception
+int	/man/2/factotum
+int	/man/2/filepat
+int	/man/2/filter
+int	/man/2/format
+int	/man/2/geodesy
+int	/man/2/hash
+int	/man/2/ida
+int	/man/2/imagefile
+int	/man/2/ip
+int	/man/2/ir
+int	/man/2/itslib
+int	/man/2/json
+int	/man/2/keyring-0intro
+int	/man/2/keyring-auth
+int	/man/2/keyring-crypt
+int	/man/2/keyring-gensk
+int	/man/2/keyring-getmsg
+int	/man/2/keyring-getstring
+int	/man/2/keyring-ipint
+int	/man/2/keyring-rc4
+int	/man/2/keyring-sha1
+int	/man/2/lists
+int	/man/2/lock
+int	/man/2/math-elem
+int	/man/2/math-export
+int	/man/2/math-fp
+int	/man/2/math-linalg
+int	/man/2/mpeg
+int	/man/2/msgio
+int	/man/2/names
+int	/man/2/palmfile
+int	/man/2/plumbmsg
+int	/man/2/pop3
+int	/man/2/popup
+int	/man/2/prefab-compound
+int	/man/2/prefab-element
+int	/man/2/print
+int	/man/2/prof
+int	/man/2/pslib
+int	/man/2/rand
+int	/man/2/readdir
+int	/man/2/regex
+int	/man/2/registries
+int	/man/2/rfc822
+int	/man/2/scsiio
+int	/man/2/secstore
+int	/man/2/security-auth
+int	/man/2/security-random
+int	/man/2/sets
+int	/man/2/sexprs
+int	/man/2/sh
+int	/man/2/smtp
+int	/man/2/spki
+int	/man/2/spree
+int	/man/2/spree-allow
+int	/man/2/spree-cardlib
+int	/man/2/spree-gather
+int	/man/2/string
+int	/man/2/stringinttab
+int	/man/2/styx
+int	/man/2/styxconv
+int	/man/2/styxflush
+int	/man/2/styxpersist
+int	/man/2/styxservers
+int	/man/2/styxservers-nametree
+int	/man/2/sys-bind
+int	/man/2/sys-byte2char
+int	/man/2/sys-chdir
+int	/man/2/sys-dial
+int	/man/2/sys-dirread
+int	/man/2/sys-dup
+int	/man/2/sys-export
+int	/man/2/sys-file2chan
+int	/man/2/sys-fversion
+int	/man/2/sys-iounit
+int	/man/2/sys-millisec
+int	/man/2/sys-open
+int	/man/2/sys-pctl
+int	/man/2/sys-pipe
+int	/man/2/sys-print
+int	/man/2/sys-read
+int	/man/2/sys-remove
+int	/man/2/sys-seek
+int	/man/2/sys-self
+int	/man/2/sys-sleep
+int	/man/2/sys-stat
+int	/man/2/sys-tokenize
+int	/man/2/sys-utfbytes
+int	/man/2/sys-werrstr
+int	/man/2/tabs
+int	/man/2/tftp
+int	/man/2/timers
+int	/man/2/tk
+int	/man/2/tkclient
+int	/man/2/ubfa
+int	/man/2/venti
+int	/man/2/volume
+int	/man/2/w3c-css
+int	/man/2/w3c-uris
+int	/man/2/w3c-xpointers
+int	/man/2/wait
+int	/man/2/wmclient
+int	/man/2/wmsrv
+int	/man/2/xml
+int	/man/3/arch
+int	/man/3/dbg
+int	/man/6/colour
+int	/man/6/json
+int	/man/6/sbl
+int	/man/7/db
+int,int	/man/2/regex
+int,real	/man/2/math-elem
+int,real	/man/2/math-fp
+intact	/man/2/draw-image
+intact	/man/2/prefab-element
+integer	/man/1/calc
+integer	/man/1/charon
+integer	/man/1/fc
+integer	/man/1/m4
+integer	/man/1/math-misc
+integer	/man/1/mc
+integer	/man/1/mdb
+integer	/man/1/sh-expr
+integer	/man/1/sh-std
+integer	/man/1/sh-test
+integer	/man/1/sort
+integer	/man/1/wm
+integer	/man/10/a.out
+integer	/man/10/acid
+integer	/man/10/atoi
+integer	/man/10/memory
+integer	/man/10/newchan
+integer	/man/10/print
+integer	/man/10/readnum
+integer	/man/10/strcat
+integer	/man/2/0intro
+integer	/man/2/asn1
+integer	/man/2/daytime
+integer	/man/2/dhcpclient
+integer	/man/2/dial
+integer	/man/2/dis
+integer	/man/2/diskblocks
+integer	/man/2/draw-0intro
+integer	/man/2/draw-point
+integer	/man/2/draw-rect
+integer	/man/2/draw-screen
+integer	/man/2/factotum
+integer	/man/2/geodesy
+integer	/man/2/ida
+integer	/man/2/itslib
+integer	/man/2/keyring-0intro
+integer	/man/2/keyring-gensk
+integer	/man/2/keyring-ipint
+integer	/man/2/math-elem
+integer	/man/2/math-export
+integer	/man/2/math-fp
+integer	/man/2/math-linalg
+integer	/man/2/palmfile
+integer	/man/2/prof
+integer	/man/2/rand
+integer	/man/2/readdir
+integer	/man/2/security-random
+integer	/man/2/sets
+integer	/man/2/spree
+integer	/man/2/spree-allow
+integer	/man/2/spree-cardlib
+integer	/man/2/string
+integer	/man/2/styx
+integer	/man/2/styxservers
+integer	/man/2/sys-0intro
+integer	/man/2/sys-bind
+integer	/man/2/sys-dial
+integer	/man/2/sys-dirread
+integer	/man/2/sys-dup
+integer	/man/2/sys-print
+integer	/man/2/sys-stat
+integer	/man/2/timers
+integer	/man/2/tk
+integer	/man/2/ubfa
+integer	/man/2/w3c-css
+integer	/man/2/w3c-xpointers
+integer	/man/3/audio
+integer	/man/3/dbg
+integer	/man/3/draw
+integer	/man/3/ether
+integer	/man/3/flash
+integer	/man/3/pbus
+integer	/man/3/sd
+integer	/man/3/sign
+integer	/man/4/dbfs
+integer	/man/4/factotum
+integer	/man/5/0intro
+integer	/man/6/dis
+integer	/man/6/keytext
+integer	/man/6/ubfa
+integer	/man/6/users
+integer	/man/8/styxchat
+integer	/man/9/canvas
+integer	/man/9/choicebutton
+integer	/man/9/entry
+integer	/man/9/grid
+integer	/man/9/image
+integer	/man/9/listbox
+integer	/man/9/menu
+integer	/man/9/options
+integer	/man/9/text
+integer	/man/9/types
+integers	/man/1/fc
+integers	/man/1/itest
+integers	/man/1/m4
+integers	/man/1/sh-expr
+integers	/man/1/tail
+integers	/man/10/a.out
+integers	/man/10/acid
+integers	/man/10/print
+integers	/man/10/readnum
+integers	/man/2/asn1
+integers	/man/2/dhcpclient
+integers	/man/2/draw-0intro
+integers	/man/2/draw-display
+integers	/man/2/keyring-ipint
+integers	/man/2/rand
+integers	/man/2/sets
+integers	/man/2/string
+integers	/man/2/styx
+integers	/man/3/cons
+integers	/man/3/draw
+integers	/man/3/prof
+integers	/man/4/dbfs
+integers	/man/5/0intro
+integers	/man/5/stat
+integers	/man/6/colour
+integers	/man/6/dis
+integers	/man/6/ubfa
+integers	/man/8/styxchat
+integers	/man/9/grid
+integral	/man/1/calc
+integral	/man/10/atoi
+integral	/man/2/math-fp
+integral	/man/2/sys-0intro
+integral	/man/2/sys-stat
+integral	/man/3/tls
+integral	/man/5/read
+integral	/man/5/stat
+integral	/man/6/image
+integral	/man/9/scale
+integrated	/man/10/plan9.ini
+integration	/man/1/calc
+integrity	/man/2/filter-deflate
+intel	/man/10/2c
+intel	/man/10/a.out
+intel	/man/10/plan9.ini
+intel	/man/3/ether
+intel	/man/3/i82365
+intelligent	/man/10/plan9.ini
+intellimouse	/man/10/plan9.ini
+intended	/man/1/alphabet-main
+intended	/man/1/charon
+intended	/man/1/emu
+intended	/man/1/man
+intended	/man/1/sh-std
+intended	/man/1/tiny
+intended	/man/10/allocb
+intended	/man/10/kproc
+intended	/man/10/plan9.ini
+intended	/man/10/xalloc
+intended	/man/2/keyring-0intro
+intended	/man/2/math-0intro
+intended	/man/2/msgio
+intended	/man/2/prefab-element
+intended	/man/2/security-0intro
+intended	/man/2/sexprs
+intended	/man/2/sys-stat
+intended	/man/2/translate
+intended	/man/2/ubfa
+intended	/man/2/venti
+intended	/man/3/ftl
+intended	/man/3/ip
+intended	/man/3/pnp
+intended	/man/4/factotum
+intended	/man/4/registry
+intended	/man/6/json
+intended	/man/6/login
+intended	/man/8/rdbgsrv
+intended	/man/8/register
+intended	/man/9/frame
+intended	/man/9/label
+intends	/man/10/kproc
+intensities	/man/2/draw-display
+intensities	/man/6/colour
+intensity	/man/6/colour
+intensive	/man/1/sh
+intent	/man/3/fs
+intention	/man/3/pnp
+intentions	/man/10/qlock
+inter	/man/2/ip
+inter	/man/3/ds
+inter	/man/6/font
+interact	/man/1/mprof
+interact	/man/2/draw-context
+interact	/man/2/factotum
+interact	/man/2/spree
+interact	/man/2/tk
+interact	/man/3/dbg
+interact	/man/4/spree
+interacting	/man/9/entry
+interaction	/man/1/collab-clients
+interaction	/man/1/ftree
+interaction	/man/1/mux
+interaction	/man/10/splhi
+interaction	/man/2/spree
+interaction	/man/2/tkclient
+interaction	/man/2/wmclient
+interaction	/man/2/wmlib
+interaction	/man/2/wmsrv
+interaction	/man/7/db
+interactions	/man/1/tkcmd
+interactions	/man/2/virgil
+interactions	/man/9/scale
+interactive	/man/1/acme
+interactive	/man/1/look
+interactive	/man/1/mux
+interactive	/man/1/sh
+interactive	/man/1/sh-tk
+interactive	/man/1/tkcmd
+interactive	/man/2/draw-0intro
+interactive	/man/2/draw-context
+interactive	/man/2/itslib
+interactive	/man/2/prefab-0intro
+interactive	/man/2/selectfile
+interactive	/man/2/sh
+interactive	/man/2/spree
+interactive	/man/3/cons
+interactive	/man/4/acme
+interactive	/man/4/spree
+interactive	/man/8/prep
+interactive	/man/9/frame
+interactive	/man/9/label
+interactively	/man/1/acme
+interactively	/man/1/alphabet-fs
+interactively	/man/1/calc
+interactively	/man/1/filename
+interactively	/man/1/fs
+interactively	/man/1/mprof
+interactively	/man/1/prof
+interactively	/man/1/sh
+interactively	/man/1/tkcmd
+interactively	/man/1/units
+interactively	/man/1/wm
+interactively	/man/10/plan9.ini
+interacts	/man/2/factotum
+interacts	/man/8/register
+interacts	/man/9/scrollbar
+interapplication	/man/8/plumber
+intercepted	/man/2/keyring-0intro
+intercepts	/man/2/ir
+interchange	/man/2/format
+interconnection	/man/1/alphabet-abc
+interconnection	/man/1/alphabet-grid
+interested	/man/9/scale
+interesting	/man/1/0intro
+interesting	/man/10/plan9.ini
+interesting	/man/2/dbm
+interesting	/man/9/canvas
+interesting	/man/9/text
+interface	/man/1/0intro
+interface	/man/1/acme
+interface	/man/1/ftree
+interface	/man/1/grid-monitor
+interface	/man/1/grid-query
+interface	/man/1/grid-session
+interface	/man/1/mash
+interface	/man/1/os
+interface	/man/1/sh
+interface	/man/1/sh-alphabet
+interface	/man/1/sh-expr
+interface	/man/1/sh-file2chan
+interface	/man/1/sh-std
+interface	/man/1/sh-string
+interface	/man/1/tiny
+interface	/man/1/wish
+interface	/man/1/wm
+interface	/man/10/conf
+interface	/man/10/dev
+interface	/man/10/devattach
+interface	/man/10/dmainit
+interface	/man/10/inb
+interface	/man/10/kbdputc
+interface	/man/10/ntsrv
+interface	/man/10/parsecmd
+interface	/man/10/plan9.ini
+interface	/man/10/styx
+interface	/man/10/styxserver
+interface	/man/2/0intro
+interface	/man/2/alphabet-intro
+interface	/man/2/bufio
+interface	/man/2/bufio-chanfill
+interface	/man/2/command
+interface	/man/2/debug
+interface	/man/2/devpointer
+interface	/man/2/dhcpclient
+interface	/man/2/dial
+interface	/man/2/disks
+interface	/man/2/dividers
+interface	/man/2/draw-0intro
+interface	/man/2/draw-context
+interface	/man/2/draw-display
+interface	/man/2/encoding
+interface	/man/2/env
+interface	/man/2/factotum
+interface	/man/2/filter
+interface	/man/2/filter-deflate
+interface	/man/2/filter-slip
+interface	/man/2/fsproto
+interface	/man/2/ip
+interface	/man/2/keyring-0intro
+interface	/man/2/mpeg
+interface	/man/2/palmfile
+interface	/man/2/plumbmsg
+interface	/man/2/pop3
+interface	/man/2/prefab-0intro
+interface	/man/2/prefab-compound
+interface	/man/2/print
+interface	/man/2/prof
+interface	/man/2/scsiio
+interface	/man/2/security-ssl
+interface	/man/2/sh
+interface	/man/2/smtp
+interface	/man/2/spree
+interface	/man/2/spree-cardlib
+interface	/man/2/spree-gather
+interface	/man/2/styx
+interface	/man/2/styxservers-nametree
+interface	/man/2/sys-dial
+interface	/man/2/sys-file2chan
+interface	/man/2/sys-pipe
+interface	/man/2/sys-werrstr
+interface	/man/2/tabs
+interface	/man/2/tk
+interface	/man/2/tkclient
+interface	/man/2/virgil
+interface	/man/2/volume
+interface	/man/2/wait
+interface	/man/2/wmclient
+interface	/man/2/wmsrv
+interface	/man/2/xml
+interface	/man/3/audio
+interface	/man/3/cmd
+interface	/man/3/draw
+interface	/man/3/ether
+interface	/man/3/flash
+interface	/man/3/floppy
+interface	/man/3/fpga
+interface	/man/3/fs
+interface	/man/3/ftl
+interface	/man/3/gpio
+interface	/man/3/i2c
+interface	/man/3/i82365
+interface	/man/3/ip
+interface	/man/3/lpt
+interface	/man/3/pnp
+interface	/man/3/pointer
+interface	/man/3/sd
+interface	/man/3/srv
+interface	/man/3/ssl
+interface	/man/3/touch
+interface	/man/3/usb
+interface	/man/3/vid
+interface	/man/4/acme
+interface	/man/4/factotum
+interface	/man/4/namespace
+interface	/man/5/0intro
+interface	/man/6/colour
+interface	/man/6/scancode
+interface	/man/6/utf
+interface	/man/7/db
+interface	/man/7/dbsrv
+interface	/man/8/collabsrv
+interface	/man/8/cs
+interface	/man/8/dhcp
+interface	/man/8/getauthinfo
+interface	/man/8/httpd
+interface	/man/9/0intro
+interface's	/man/2/dhcpclient
+interface's	/man/3/ip
+interfaces	/man/10/0intro
+interfaces	/man/10/9load
+interfaces	/man/10/conf
+interfaces	/man/10/error
+interfaces	/man/2/0intro
+interfaces	/man/2/dial
+interfaces	/man/2/factotum
+interfaces	/man/2/ip
+interfaces	/man/2/keyring-crypt
+interfaces	/man/2/sys-0intro
+interfaces	/man/2/sys-dial
+interfaces	/man/2/tk
+interfaces	/man/3/ip
+interfaces	/man/3/lpt
+interfaces	/man/3/pipe
+interfaces	/man/3/pnp
+interfaces	/man/4/namespace
+interfaces	/man/8/0intro
+interfaces	/man/8/rip
+interference	/man/2/sys-read
+interim	/man/5/0intro
+interior	/man/2/draw-image
+interior	/man/3/draw
+interior	/man/9/canvas
+interior	/man/9/options
+interleave	/man/3/ds
+interleaved	/man/3/ds
+interleaved	/man/3/pipe
+interleaved	/man/3/tv
+interleaved	/man/4/factotum
+interleaving	/man/3/ds
+interline	/man/2/draw-font
+interlock	/man/10/lock
+interlocked	/man/1/wm-misc
+interloper	/man/2/security-0intro
+intermediate	/man/1/acme
+intermediate	/man/1/alphabet-abc
+intermediate	/man/1/alphabet-grid
+intermediate	/man/1/mk
+intermediate	/man/10/mk
+intermediate	/man/2/smtp
+intermediate	/man/2/spree-gather
+intermediate	/man/2/translate
+intermediate	/man/8/mkfs
+intermingled	/man/10/qio
+internal	/man/1/sh
+internal	/man/1/tktester
+internal	/man/10/allocb
+internal	/man/10/c2l
+internal	/man/10/dynld
+internal	/man/10/error
+internal	/man/10/kbdputc
+internal	/man/10/srclist
+internal	/man/10/styx
+internal	/man/2/0intro
+internal	/man/2/alphabet-intro
+internal	/man/2/convcs
+internal	/man/2/crc
+internal	/man/2/dbm
+internal	/man/2/dialog
+internal	/man/2/dis
+internal	/man/2/dividers
+internal	/man/2/draw-image
+internal	/man/2/ether
+internal	/man/2/imagefile
+internal	/man/2/ip
+internal	/man/2/json
+internal	/man/2/math-export
+internal	/man/2/prefab-element
+internal	/man/2/print
+internal	/man/2/registries
+internal	/man/2/selectfile
+internal	/man/2/sets
+internal	/man/2/sexprs
+internal	/man/2/spki
+internal	/man/2/spree-allow
+internal	/man/2/spree-cardlib
+internal	/man/2/spree-gather
+internal	/man/2/spree-objstore
+internal	/man/2/styxflush
+internal	/man/2/styxservers
+internal	/man/2/styxservers-nametree
+internal	/man/2/sys-millisec
+internal	/man/2/tabs
+internal	/man/2/tk
+internal	/man/2/tkclient
+internal	/man/2/ubfa
+internal	/man/2/w3c-xpointers
+internal	/man/2/wmclient
+internal	/man/2/wmlib
+internal	/man/3/draw
+internal	/man/3/indir
+internal	/man/3/ip
+internal	/man/3/logfs
+internal	/man/3/pbus
+internal	/man/3/pipe
+internal	/man/3/snarf
+internal	/man/3/srv
+internal	/man/4/kfs
+internal	/man/6/0intro
+internal	/man/6/keys
+internal	/man/6/keytext
+internal	/man/7/dbsrv
+internal	/man/9/grid
+internal	/man/9/options
+internal	/man/9/pack
+internal	/man/9/scrollbar
+internal	/man/9/text
+internally	/man/1/sh
+internally	/man/10/dev
+internally	/man/2/alphabet-intro
+internally	/man/2/bufio
+internally	/man/2/draw-display
+internally	/man/2/hash
+internally	/man/2/palmfile
+internally	/man/2/spki
+internally	/man/2/srv
+internally	/man/2/styxservers
+internally	/man/6/colour
+internally	/man/6/sbl
+internally	/man/6/users
+internally	/man/6/utf
+internally	/man/9/canvas
+international	/man/1/idea
+international	/man/2/geodesy
+international	/man/2/keyring-crypt
+internet	/man/1/miniterm
+internet	/man/1/mux
+internet	/man/2/dhcpclient
+internet	/man/2/filter-deflate
+internet	/man/2/ip
+internet	/man/2/json
+internet	/man/2/keyring-sha1
+internet	/man/2/rfc822
+internet	/man/2/sexprs
+internet	/man/2/srv
+internet	/man/2/tftp
+internet	/man/3/ip
+internet	/man/6/json
+internet	/man/6/ndb
+internet	/man/6/sexprs
+internet	/man/7/cddb
+internet	/man/8/bootpd
+internet	/man/8/cs
+internet	/man/8/dns
+internet	/man/8/ping
+internet	/man/8/rip
+internet's	/man/4/ftpfs
+interp	/man/10/kproc
+interparagraph	/man/6/man
+interposes	/man/4/iostats
+interposes	/man/8/rdbgsrv
+interpret	/man/1/0intro
+interpret	/man/1/mash
+interpret	/man/1/unicode
+interpret	/man/10/a.out
+interpret	/man/10/atoi
+interpret	/man/10/dev
+interpret	/man/2/plumbmsg
+interpret	/man/2/security-0intro
+interpret	/man/4/acme
+interpret	/man/4/registry
+interpret	/man/4/spree
+interpret	/man/5/stat
+interpret	/man/8/collabsrv
+interpret	/man/9/0intro
+interpretable	/man/10/atoi
+interpretation	/man/1/calc
+interpretation	/man/1/chmod
+interpretation	/man/1/cook
+interpretation	/man/1/plumb
+interpretation	/man/1/sh
+interpretation	/man/1/sh-arg
+interpretation	/man/1/sh-expr
+interpretation	/man/10/dev
+interpretation	/man/2/0intro
+interpretation	/man/2/dis
+interpretation	/man/2/draw-0intro
+interpretation	/man/2/names
+interpretation	/man/2/registries
+interpretation	/man/2/styxservers-nametree
+interpretation	/man/2/sys-fversion
+interpretation	/man/2/w3c-uris
+interpretation	/man/3/cmd
+interpretation	/man/3/fpga
+interpretation	/man/3/ip
+interpretation	/man/6/json
+interpretation	/man/6/sexprs
+interpretation	/man/9/0intro
+interpretation	/man/9/menu
+interpretation	/man/9/send
+interpreter	/man/1/deb
+interpreter	/man/1/emu
+interpreter	/man/1/limbo
+interpreter	/man/1/sleep
+interpreter	/man/10/getfields
+interpreter	/man/10/kproc
+interpreter	/man/2/sh
+interpreter	/man/3/cmd
+interpreter	/man/3/prog
+interpreter	/man/9/options
+interpreter's	/man/3/cmd
+interpreters	/man/1/0intro
+interpreters	/man/2/sexprs
+interpreting	/man/1/acme
+interpreting	/man/1/filename
+interpreting	/man/1/m4
+interpreting	/man/1/mk
+interpreting	/man/1/sh-std
+interpreting	/man/10/a.out
+interpreting	/man/10/mk
+interpreting	/man/10/parsecmd
+interpreting	/man/2/plumbmsg
+interpreting	/man/4/spree
+interpreting	/man/6/sexprs
+interprets	/man/1/0intro
+interprets	/man/1/calc
+interprets	/man/1/cleanname
+interprets	/man/1/diff
+interprets	/man/1/ftree
+interprets	/man/1/m4
+interprets	/man/1/nsbuild
+interprets	/man/1/p
+interprets	/man/1/telnet
+interprets	/man/10/atoi
+interprets	/man/10/print
+interprets	/man/2/styxservers-nametree
+interprets	/man/2/sys-print
+interprets	/man/2/wmlib
+interprets	/man/3/indir
+interprets	/man/3/usb
+interprets	/man/6/ndb
+interprets	/man/8/cs
+interprets	/man/8/rip
+interprocess	/man/2/sys-pipe
+interprocess	/man/3/pipe
+interrogate	/man/2/debug
+interrogate	/man/9/send
+interrogate	/man/9/variable
+interrogated	/man/2/draw-0intro
+interrupt	/man/10/allocb
+interrupt	/man/10/delay
+interrupt	/man/10/intrenable
+interrupt	/man/10/kproc
+interrupt	/man/10/lock
+interrupt	/man/10/malloc
+interrupt	/man/10/plan9.ini
+interrupt	/man/10/qio
+interrupt	/man/10/sleep
+interrupt	/man/10/splhi
+interrupt	/man/3/arch
+interrupt	/man/3/cons
+interrupt	/man/3/kprof
+interrupt	/man/3/pnp
+interrupt	/man/4/spree
+interrupted	/man/10/kproc
+interrupted	/man/10/sleep
+interrupted	/man/2/styxflush
+interrupted	/man/3/tls
+interrupts	/man/10/delay
+interrupts	/man/10/dev
+interrupts	/man/10/error
+interrupts	/man/10/intrenable
+interrupts	/man/10/lock
+interrupts	/man/10/panic
+interrupts	/man/10/sleep
+interrupts	/man/10/splhi
+interrupts	/man/3/arch
+interrupts	/man/5/flush
+intersect	/man/2/draw-image
+intersect	/man/2/draw-rect
+intersect	/man/2/spki
+intersecting	/man/2/draw-image
+intersecting	/man/3/draw
+intersection	/man/1/alphabet-fs
+intersection	/man/1/fs
+intersection	/man/2/draw-image
+intersection	/man/2/draw-rect
+intersection	/man/2/sets
+intersection	/man/2/spree
+intersil	/man/10/plan9.ini
+interspersed	/man/1/mash
+interspersed	/man/1/sh
+interval	/man/1/calc
+interval	/man/1/prof
+interval	/man/1/sleep
+interval	/man/2/dhcpclient
+interval	/man/2/ida
+interval	/man/2/ip
+interval	/man/2/prof
+interval	/man/2/timers
+interval	/man/3/prof
+interval	/man/8/ping
+intervals	/man/1/acme
+intervals	/man/9/text
+intervening	/man/1/mash
+intervening	/man/1/sh
+intialise	/man/2/styxflush
+intrdisable	/man/10/intrenable
+intrenable	/man/10/intrenable
+intrenable	/man/10/splhi
+intro	/man/1/0intro
+intro	/man/1/bind
+intro	/man/1/calc
+intro	/man/1/grid-register
+intro	/man/1/grid-session
+intro	/man/1/limbo
+intro	/man/1/listen
+intro	/man/1/mux
+intro	/man/1/rcmd
+intro	/man/1/sh-file2chan
+intro	/man/1/tkcmd
+intro	/man/1/wish
+intro	/man/10/0intro
+intro	/man/10/dev
+intro	/man/10/devattach
+intro	/man/10/master
+intro	/man/10/odbc
+intro	/man/10/styx
+intro	/man/2/0intro
+intro	/man/2/alphabet-intro
+intro	/man/2/bufio
+intro	/man/2/devpointer
+intro	/man/2/draw-0intro
+intro	/man/2/draw-context
+intro	/man/2/draw-image
+intro	/man/2/draw-point
+intro	/man/2/drawmux
+intro	/man/2/ir
+intro	/man/2/keyring-0intro
+intro	/man/2/keyring-crypt
+intro	/man/2/keyring-rc4
+intro	/man/2/math-0intro
+intro	/man/2/math-elem
+intro	/man/2/math-export
+intro	/man/2/math-fp
+intro	/man/2/math-linalg
+intro	/man/2/names
+intro	/man/2/prefab-0intro
+intro	/man/2/prefab-element
+intro	/man/2/prefab-environ
+intro	/man/2/prefab-style
+intro	/man/2/security-0intro
+intro	/man/2/security-login
+intro	/man/2/spki
+intro	/man/2/styx
+intro	/man/2/styxconv
+intro	/man/2/styxservers
+intro	/man/2/styxservers-nametree
+intro	/man/2/sys-0intro
+intro	/man/2/sys-bind
+intro	/man/2/sys-byte2char
+intro	/man/2/sys-chdir
+intro	/man/2/sys-dirread
+intro	/man/2/sys-dup
+intro	/man/2/sys-fd2path
+intro	/man/2/sys-file2chan
+intro	/man/2/sys-fversion
+intro	/man/2/sys-iounit
+intro	/man/2/sys-millisec
+intro	/man/2/sys-open
+intro	/man/2/sys-pctl
+intro	/man/2/sys-print
+intro	/man/2/sys-read
+intro	/man/2/sys-remove
+intro	/man/2/sys-seek
+intro	/man/2/sys-sleep
+intro	/man/2/sys-stat
+intro	/man/2/sys-tokenize
+intro	/man/2/sys-utfbytes
+intro	/man/2/sys-werrstr
+intro	/man/2/tk
+intro	/man/2/volume
+intro	/man/3/0intro
+intro	/man/3/cap
+intro	/man/3/dbg
+intro	/man/3/draw
+intro	/man/3/fs
+intro	/man/3/logfs
+intro	/man/3/mnt
+intro	/man/3/pointer
+intro	/man/3/prog
+intro	/man/3/srv9
+intro	/man/4/0intro
+intro	/man/4/export
+intro	/man/4/grid-cpu
+intro	/man/4/iostats
+intro	/man/4/mntgen
+intro	/man/4/namespace
+intro	/man/4/tarfs
+intro	/man/5/0intro
+intro	/man/5/attach
+intro	/man/5/stat
+intro	/man/5/walk
+intro	/man/6/0intro
+intro	/man/6/colour
+intro	/man/6/font
+intro	/man/6/image
+intro	/man/6/keyboard
+intro	/man/6/login
+intro	/man/6/users
+intro	/man/7/0intro
+intro	/man/8/0intro
+intro	/man/8/styxchat
+intro	/man/8/styxmon
+intro	/man/9/0intro
+intro	/man/9/types
+introduce	/man/2/draw-example
+introduce	/man/3/draw
+introduce	/man/3/ds
+introduced	/man/1/calc
+introduced	/man/2/cfg
+introduced	/man/2/w3c-css
+introduces	/man/2/0intro
+introduces	/man/2/w3c-uris
+introduction	/man/1/0intro
+introduction	/man/10/0intro
+introduction	/man/2/0intro
+introduction	/man/2/keyring-0intro
+introduction	/man/2/security-0intro
+introduction	/man/2/sys-0intro
+introduction	/man/3/0intro
+introduction	/man/4/0intro
+introduction	/man/5/0intro
+introduction	/man/5/attach
+introduction	/man/6/0intro
+introduction	/man/7/0intro
+introduction	/man/8/0intro
+introduction	/man/9/0intro
+introduction	/man/9/bind
+introduction	/man/9/canvas
+introduction	/man/9/cursor
+introduction	/man/9/menu
+introduction	/man/9/menubutton
+intruder	/man/2/security-0intro
+ints	/man/3/prog
+inttoip	/man/2/keyring-ipint
+inval	/man/2/math-fp
+inval	/man/3/dbg
+invalid	/man/1/ar
+invalid	/man/1/sh
+invalid	/man/1/sh-std
+invalid	/man/10/c2l
+invalid	/man/10/devattach
+invalid	/man/10/iar
+invalid	/man/10/newchan
+invalid	/man/10/styxserver
+invalid	/man/2/0intro
+invalid	/man/2/draw-display
+invalid	/man/2/ether
+invalid	/man/2/ip
+invalid	/man/2/ir
+invalid	/man/2/keyring-certtostr
+invalid	/man/2/keyring-sha1
+invalid	/man/2/math-fp
+invalid	/man/2/sh
+invalid	/man/2/spki
+invalid	/man/2/styx
+invalid	/man/2/styxservers
+invalid	/man/2/sys-byte2char
+invalid	/man/2/virgil
+invalid	/man/3/boot
+invalid	/man/3/cap
+invalid	/man/3/dbg
+invalid	/man/3/pbus
+invalid	/man/4/spree
+invalid	/man/5/flush
+invalidated	/man/9/image
+invalidates	/man/8/createsignerkey
+invariably	/man/10/intrenable
+invariably	/man/10/newchan
+invariably	/man/2/ida
+invariably	/man/8/ftl
+invariant	/man/10/conf
+invariant	/man/6/sbl
+invented	/man/3/indir
+inverse	/man/1/alphabet-abc
+inverse	/man/1/alphabet-grid
+inverse	/man/1/alphabet-main
+inverse	/man/2/devpointer
+inverse	/man/2/filter-deflate
+inverse	/man/2/keyring-certtostr
+inverse	/man/2/keyring-ipint
+inverse	/man/2/math-elem
+inverse	/man/2/sys-byte2char
+inverse	/man/6/utf
+invert	/man/1/sh-expr
+invert	/man/2/sets
+invert	/man/6/image
+inverted	/man/6/image
+inverts	/man/1/sh-std
+investigate	/man/1/acme
+investigates	/man/1/math-misc
+invisible	/man/1/tiny
+invisible	/man/2/draw-display
+invisible	/man/2/prefab-element
+invisible	/man/2/spree
+invisible	/man/2/spree-cardlib
+invocation	/man/1/alphabet-fs
+invocation	/man/1/fs
+invocation	/man/1/mash
+invocation	/man/1/sh
+invocation	/man/2/sh
+invocation	/man/9/button
+invocation	/man/9/menu
+invocation	/man/9/update
+invoke	/man/1/acme
+invoke	/man/1/yacc
+invoke	/man/10/devattach
+invoke	/man/10/ntsrv
+invoke	/man/2/sys-chdir
+invoke	/man/2/sys-pctl
+invoke	/man/3/kprof
+invoke	/man/8/svc
+invoke	/man/9/bind
+invoke	/man/9/button
+invoke	/man/9/canvas
+invoke	/man/9/checkbutton
+invoke	/man/9/choicebutton
+invoke	/man/9/entry
+invoke	/man/9/frame
+invoke	/man/9/label
+invoke	/man/9/listbox
+invoke	/man/9/menu
+invoke	/man/9/menubutton
+invoke	/man/9/panel
+invoke	/man/9/radiobutton
+invoke	/man/9/scale
+invoke	/man/9/scrollbar
+invoke	/man/9/text
+invoked	/man/1/bind
+invoked	/man/1/mash
+invoked	/man/1/mash-tk
+invoked	/man/1/mk
+invoked	/man/1/runas
+invoked	/man/1/sh
+invoked	/man/1/sh-alphabet
+invoked	/man/1/sh-arg
+invoked	/man/1/sh-file2chan
+invoked	/man/1/sh-std
+invoked	/man/1/wm-sh
+invoked	/man/10/acid
+invoked	/man/10/dynld
+invoked	/man/10/intrenable
+invoked	/man/10/kproc
+invoked	/man/10/mk
+invoked	/man/10/qio
+invoked	/man/2/arg
+invoked	/man/2/command
+invoked	/man/2/ir
+invoked	/man/2/sh
+invoked	/man/3/root
+invoked	/man/4/9srvfs
+invoked	/man/4/factotum
+invoked	/man/4/keysrv
+invoked	/man/4/memfs
+invoked	/man/6/namespace
+invoked	/man/8/prep
+invoked	/man/8/signer
+invoked	/man/9/button
+invoked	/man/9/canvas
+invoked	/man/9/checkbutton
+invoked	/man/9/choicebutton
+invoked	/man/9/entry
+invoked	/man/9/label
+invoked	/man/9/listbox
+invoked	/man/9/menu
+invoked	/man/9/menubutton
+invoked	/man/9/panel
+invoked	/man/9/radiobutton
+invoked	/man/9/scale
+invoked	/man/9/scrollbar
+invoked	/man/9/text
+invoker	/man/1/passwd
+invokes	/man/1/emu
+invokes	/man/1/listen
+invokes	/man/1/runas
+invokes	/man/1/sh-csv
+invokes	/man/1/sh-sexprs
+invokes	/man/1/sh-std
+invokes	/man/1/sh-tk
+invokes	/man/1/wm-sh
+invokes	/man/10/error
+invokes	/man/2/sys-file2chan
+invokes	/man/7/dbsrv
+invokes	/man/8/svc
+invokes	/man/9/button
+invokes	/man/9/checkbutton
+invokes	/man/9/menu
+invokes	/man/9/menubutton
+invokes	/man/9/radiobutton
+invoking	/man/1/alphabet-fs
+invoking	/man/1/emu
+invoking	/man/1/fs
+invoking	/man/1/ftree
+invoking	/man/1/passwd
+invoking	/man/10/error
+invoking	/man/10/parsecmd
+invoking	/man/2/command
+invoking	/man/2/dhcpclient
+invoking	/man/2/disks
+invoking	/man/2/drawmux
+invoking	/man/2/exception
+invoking	/man/2/ida
+invoking	/man/2/json
+invoking	/man/2/secstore
+invoking	/man/2/sexprs
+invoking	/man/2/sh
+invoking	/man/2/spki
+invoking	/man/2/sys-chdir
+invoking	/man/2/ubfa
+invoking	/man/2/wait
+invoking	/man/2/workdir
+invoking	/man/8/getauthinfo
+invoking	/man/8/signer
+invoking	/man/9/canvas
+invoking	/man/9/choicebutton
+invoking	/man/9/menu
+invoking	/man/9/text
+involve	/man/1/mprof
+involved	/man/1/mv
+involved	/man/1/ns
+involved	/man/1/webgrab
+involved	/man/2/draw-image
+involved	/man/2/spree
+involved	/man/4/iostats
+involvement	/man/8/svc
+involves	/man/8/logind
+involving	/man/1/sh-alphabet
+involving	/man/2/math-0intro
+involving	/man/2/sys-iounit
+involving	/man/3/draw
+in²	/man/1/units
+io	/man/2/diskblocks
+io	/man/2/disks
+io	/man/2/scsiio
+io	/man/3/eia
+io	/man/3/i2c
+io.h	/man/10/0intro
+ioalloc	/man/3/arch
+iob	/man/2/format
+iob	/man/2/xml
+iob	/man/3/arch
+iobfd	/man/3/arch
+iobuf	/man/1/yacc
+iobuf	/man/2/bufio
+iobuf	/man/2/bufio-chanfill
+iobuf	/man/2/csv
+iobuf	/man/2/format
+iobuf	/man/2/imagefile
+iobuf	/man/2/json
+iobuf	/man/2/pslib
+iobuf	/man/2/rfc822
+iobuf	/man/2/sexprs
+iobuf	/man/2/ubfa
+iobuf	/man/2/xml
+iobuf.gets	/man/6/dis
+ioexclude	/man/10/plan9.ini
+iohdrsz	/man/10/styx
+iohdrsz	/man/2/styx
+iol	/man/3/arch
+iostats	/man/4/iostats
+iostats.b	/man/4/iostats
+iostats.out	/man/4/iostats
+iota	/man/2/exception
+iota	/man/2/format
+iota	/man/2/geodesy
+iota	/man/2/readdir
+iota	/man/2/scsiio
+iota	/man/2/styxservers-nametree
+iota	/man/2/tkclient
+iota	/man/2/w3c-xpointers
+iota	/man/2/wmclient
+iounit	/man/10/styx
+iounit	/man/2/styx
+iounit	/man/2/styxservers
+iounit	/man/2/sys-iounit
+iounit	/man/3/prog
+iounit	/man/5/0intro
+iounit	/man/5/open
+iounit	/man/5/read
+iounit	/man/8/styxchat
+iow	/man/3/arch
+ip	/man/1/dmview
+ip	/man/1/netstat
+ip	/man/10/0intro
+ip	/man/10/conf
+ip	/man/10/dev
+ip	/man/10/plan9.ini
+ip	/man/2/dhcpclient
+ip	/man/2/dial
+ip	/man/2/ether
+ip	/man/2/ip
+ip	/man/2/keyring-getmsg
+ip	/man/2/msgio
+ip	/man/2/sys-dial
+ip	/man/2/tftp
+ip	/man/2/virgil
+ip	/man/3/ether
+ip	/man/3/fpga
+ip	/man/3/ip
+ip	/man/6/man
+ip	/man/6/ndb
+ip	/man/8/bootpd
+ip	/man/8/cs
+ip	/man/8/dhcp
+ip	/man/8/dns
+ip	/man/8/fpgaload
+ip	/man/8/init
+ip	/man/8/logind
+ip	/man/8/ping
+ip	/man/8/rip
+ip	/man/8/sntp
+ip	/man/8/virgild
+ip.b	/man/2/ip
+ip.h	/man/10/0intro
+ip.m	/man/2/ip
+ipa2h	/man/2/srv
+ipaddr	/man/2/ip
+ipaddr	/man/3/ip
+ipaddr.newv4	/man/2/ip
+ipaddr.newv6	/man/2/ip
+ipaddr.parse	/man/2/ip
+ipaddr.parsecidr	/man/2/ip
+ipaddr.parsemask	/man/2/ip
+ipaddress	/man/3/ip
+ipadx	/man/9/grid
+ipadx	/man/9/pack
+ipady	/man/9/grid
+ipady	/man/9/pack
+ipaq	/man/10/conf
+ipattr	/man/6/attrdb
+ipengine	/man/3/fpga
+ipgw	/man/2/dhcpclient
+ipgw	/man/6/ndb
+ipgw	/man/8/bootpd
+iph2a	/man/2/srv
+ipifc	/man/2/dhcpclient
+ipifc	/man/2/ip
+ipifc	/man/3/ip
+ipifc	/man/8/dhcp
+ipifc	/man/8/rip
+ipifc.c	/man/3/ip
+ipint	/man/1/sh-expr
+ipint	/man/2/keyring-0intro
+ipint	/man/2/keyring-gensk
+ipint	/man/2/keyring-ipint
+ipint	/man/3/cap
+ipint	/man/4/factotum
+ipint	/man/6/keytext
+ipint.iptobytes	/man/4/factotum
+iplifc	/man/2/ip
+ipmask	/man/2/dhcpclient
+ipmask	/man/3/ip
+ipmask	/man/6/ndb
+ipmask	/man/8/bootpd
+ipmsg	/man/3/ip
+ipn2p	/man/2/srv
+ipnet	/man/6/ndb
+ipnet	/man/8/bootpd
+iproute	/man/3/ip
+iproute	/man/8/rip
+iprouter	/man/3/ip
+iprouting	/man/3/ip
+ips	/man/2/dhcpclient
+ipselftab	/man/3/ip
+iptob64	/man/2/keyring-ipint
+iptobebytes	/man/2/keyring-ipint
+iptobytes	/man/2/keyring-ipint
+iptobytes	/man/6/keytext
+iptoint	/man/2/keyring-ipint
+iptostr	/man/2/keyring-ipint
+ipv4	/man/2/dhcpclient
+ipv4	/man/2/ip
+ipv4	/man/3/ip
+ipv4	/man/6/ndb
+ipv4proto	/man/6/ndb
+ipv6	/man/2/ip
+ipv6	/man/3/ip
+ipv6rp	/man/2/ip
+ir	/man/1/mux
+ir	/man/2/draw-0intro
+ir	/man/2/draw-context
+ir	/man/2/ir
+ir	/man/2/prefab-0intro
+ir	/man/2/prefab-compound
+ir	/man/2/volume
+ir	/man/2/wmclient
+ir	/man/6/man
+ir.b	/man/2/ir
+ir.m	/man/2/ir
+iraise	/man/2/dis
+irc	/man/2/ir
+iref	/man/10/newchan
+ireland	/man/2/geodesy
+ireland65	/man/2/geodesy
+irish	/man/2/geodesy
+irishnatgrid	/man/2/geodesy
+irix	/man/4/namespace
+irmpath.b	/man/2/ir
+irmserver	/man/6/ndb
+irq	/man/10/plan9.ini
+irq	/man/3/arch
+irq	/man/3/mpeg
+irq3	/man/10/plan9.ini
+irq4	/man/10/plan9.ini
+irqalloc	/man/3/arch
+irraw	/man/2/ir
+irregular	/man/2/msgio
+irrelevant	/man/2/sys-stat
+irrelevant	/man/6/font
+irrespective	/man/1/charon
+irritating	/man/2/asn1
+irsim.b	/man/2/ir
+irtest	/man/2/ir
+irval	/man/2/ir
+isa	/man/10/dmainit
+isa	/man/10/plan9.ini
+isa	/man/3/pnp
+isa.h	/man/6/dis
+isabsolute	/man/2/w3c-uris
+isaconfig	/man/10/inb
+isarray	/man/2/json
+isatom	/man/2/ubfa
+isbinary	/man/2/ubfa
+isclient	/man/3/tls
+iscompressed	/man/2/palmfile
+isdir	/man/10/newchan
+isdir	/man/10/styxserver
+isdir	/man/6/plumbing
+isect	/man/8/prep
+iseek	/man/1/dd
+isempty	/man/2/sets
+isempty	/man/2/spree-cardlib
+iseve	/man/10/eve
+isfalse	/man/2/json
+isfile	/man/6/plumbing
+ishash	/man/2/spki
+isint	/man/2/json
+isint	/man/2/ubfa
+isize	/man/2/dis
+islist	/man/1/sh-sexprs
+islist	/man/2/sexprs
+islist	/man/2/ubfa
+islo	/man/10/splhi
+islocal	/man/2/spki
+ismember	/man/2/lists
+ismulticast	/man/2/ip
+isn't	/man/1/asm
+isn't	/man/4/factotum
+isn't	/man/9/button
+isn't	/man/9/canvas
+isn't	/man/9/checkbutton
+isn't	/man/9/choicebutton
+isn't	/man/9/entry
+isn't	/man/9/label
+isn't	/man/9/listbox
+isn't	/man/9/menu
+isn't	/man/9/panel
+isn't	/man/9/radiobutton
+isn't	/man/9/scale
+isn't	/man/9/scrollbar
+isn't	/man/9/text
+isnan	/man/2/math-fp
+isnull	/man/2/json
+isnumber	/man/2/json
+iso	/man/1/charon
+iso	/man/2/palmfile
+iso	/man/2/rfc822
+iso	/man/4/dossrv
+iso	/man/6/utf
+iso11172	/man/3/mpeg
+iso9660	/man/4/dossrv
+isobject	/man/2/json
+isomorphic	/man/2/sexprs
+isop	/man/2/ubfa
+isopen	/man/2/styxservers
+isprefix	/man/2/names
+isprefix	/man/2/spki
+isprincipal	/man/2/spki
+isrange	/man/2/spree-cardlib
+isrdonly	/man/2/dbm
+isread	/man/10/dmainit
+isreal	/man/2/json
+isset	/man/2/spree-cardlib
+isstring	/man/2/json
+isstring	/man/2/ubfa
+issue	/man/1/mash-make
+issue	/man/10/styxserver
+issue	/man/3/cmd
+issue	/man/3/mnt
+issue	/man/4/keyfs
+issue	/man/5/version
+issued	/man/1/tkcmd
+issued	/man/10/c2l
+issued	/man/2/scsiio
+issued	/man/2/spki
+issued	/man/3/tls
+issued	/man/8/createsignerkey
+issued	/man/8/svc
+issued	/man/9/text
+issuer	/man/2/sexprs
+issuer	/man/2/spki
+issues	/man/2/scsiio
+issues	/man/5/open
+issues	/man/8/kfscmd
+issuing	/man/10/dmainit
+issuing	/man/2/security-login
+issuing	/man/5/open
+issuing	/man/6/login
+istag	/man/2/ubfa
+istmsg	/man/2/styx
+istrue	/man/2/json
+istuple	/man/2/ubfa
+isv4	/man/2/ip
+isvalid	/man/2/ip
+isword.p9.gz	/man/8/rdbgsrv
+it's	/man/1/mprof
+it's	/man/1/uuencode
+it's	/man/1/wm-misc
+it's	/man/10/c2l
+it's	/man/10/styxserver
+it's	/man/3/prof
+it's	/man/3/rtc
+italic	/man/1/brutus
+italic	/man/1/cook
+italic	/man/6/man
+italicised	/man/1/brutus
+italicize	/man/6/man
+italics	/man/1/0intro
+item	/man/1/deb
+item	/man/1/fc
+item	/man/1/mash-tk
+item	/man/1/mux
+item	/man/1/sh-csv
+item	/man/1/toolbar
+item	/man/10/conf
+item	/man/10/dynld
+item	/man/10/plan9.ini
+item	/man/2/asn1
+item	/man/2/dbm
+item	/man/2/dis
+item	/man/2/popup
+item	/man/2/prefab-element
+item	/man/2/smtp
+item	/man/2/spki
+item	/man/2/sys-tokenize
+item	/man/2/xml
+item	/man/6/dis
+item	/man/6/sbl
+item	/man/6/ubfa
+item	/man/8/bootpd
+item	/man/9/canvas
+item	/man/9/choicebutton
+item	/man/9/focus
+item	/man/9/listbox
+item's	/man/2/prefab-style
+item's	/man/6/dis
+item's	/man/9/canvas
+item.text	/man/2/xml
+itemcget	/man/9/canvas
+itemconfigure	/man/9/canvas
+items	/man/1/charon
+items	/man/1/deb
+items	/man/1/fc
+items	/man/1/grid-session
+items	/man/1/m4
+items	/man/1/mash-tk
+items	/man/1/sh
+items	/man/1/sh-csv
+items	/man/1/sh-std
+items	/man/1/sh-string
+items	/man/1/stack
+items	/man/1/tktester
+items	/man/1/toolbar
+items	/man/10/conf
+items	/man/10/devattach
+items	/man/2/asn1
+items	/man/2/dis
+items	/man/2/dividers
+items	/man/2/format
+items	/man/2/itslib
+items	/man/2/popup
+items	/man/2/prefab-0intro
+items	/man/2/prefab-environ
+items	/man/2/prefab-style
+items	/man/2/w3c-css
+items	/man/2/xml
+items	/man/3/prog
+items	/man/5/0intro
+items	/man/6/dis
+items	/man/6/sbl
+items	/man/6/ubfa
+items	/man/8/changelogin
+items	/man/9/canvas
+items	/man/9/choicebutton
+items	/man/9/options
+iteration	/man/1/wm-misc
+iteration	/man/10/plan9.ini
+iterations	/man/1/wm-misc
+iteratively	/man/2/keyring-sha1
+itest	/man/1/itest
+itest	/man/1/sh-test
+itest	/man/2/itslib
+itest.b	/man/1/itest
+itreplay	/man/1/itest
+itreplay.b	/man/1/itest
+itrs2000	/man/2/geodesy
+itslib	/man/1/itest
+itslib	/man/2/itslib
+itslib.m	/man/2/itslib
+itu	/man/2/asn1
+itv	/man/2/prefab-0intro
+itv	/man/2/prefab-compound
+itv	/man/2/prefab-element
+itv	/man/2/prefab-environ
+itv	/man/2/prefab-style
+iu	/man/1/tkcmd
+iunlock	/man/10/lock
+iv	/man/1/secstore
+ivec	/man/2/keyring-crypt
+ivec	/man/6/login
+i²c	/man/3/vid
+jacm	/man/2/ida
+jacm	/man/6/image
+james	/man/1/idea
+james	/man/6/attrdb
+james''s	/man/6/attrdb
+jan	/man/1/date
+jan	/man/2/daytime
+jan	/man/2/sys-stat
+jan	/man/3/cons
+jan	/man/5/stat
+january	/man/1/cal
+january	/man/1/date
+january	/man/10/seconds
+january	/man/3/rtc
+january	/man/6/keytext
+java	/man/1/charon
+javascript	/man/1/charon
+javascript	/man/2/json
+javascript	/man/6/json
+javascript1.1	/man/1/charon
+jfif	/man/2/imagefile
+jit	/man/1/itest
+jit	/man/1/mprof
+jit	/man/3/cons
+jited	/man/1/deb
+jn	/man/2/math-elem
+jo	/man/10/odbc
+joe	/man/1/filename
+joe	/man/10/styxserver
+joe	/man/6/ubfa
+joe's	/man/1/filename
+joel	/man/9/canvas
+johnson	/man/1/yacc
+join	/man/1/collab-clients
+join	/man/1/fmt
+join	/man/1/sh-std
+join	/man/1/spree-join
+join	/man/2/spree
+join	/man/2/spree-cardlib
+join	/man/2/wmsrv
+join	/man/4/spree
+join	/man/6/man
+join.b	/man/1/spree-join
+joined	/man/1/sh-tk
+joined	/man/9/text
+joining	/man/2/asn1
+joining	/man/2/draw-image
+joining	/man/2/spree-cardlib
+joining	/man/3/draw
+joinrequest	/man/1/spree-join
+joins	/man/2/draw-image
+joins	/man/2/spree
+joins	/man/3/draw
+joker	/man/2/spree-cardlib
+jonny	/man/2/format
+jpeg	/man/1/charon
+jpeg	/man/1/wm-misc
+jpeg	/man/2/imagefile
+jpgreader	/man/2/imagefile
+json	/man/2/json
+json	/man/6/json
+json	/man/6/sexprs
+json	/man/6/ubfa
+json.b	/man/2/json
+json.m	/man/2/json
+judging	/man/2/styx
+julia	/man/1/wm-misc
+jump	/man/1/charon
+jump	/man/2/dis
+jump	/man/6/dis
+jump	/man/8/prep
+jump	/man/9/options
+jump	/man/9/scrollbar
+jumps	/man/8/prep
+jumps	/man/9/options
+jungle	/man/4/namespace
+junk	/man/1/sh
+junk.b	/man/1/sh
+justification	/man/10/print
+justification	/man/2/sys-print
+justification	/man/9/text
+justified	/man/10/print
+justified	/man/10/readnum
+justified	/man/2/sys-print
+justified	/man/3/prog
+justified	/man/6/font
+justified	/man/6/image
+justified	/man/9/text
+justify	/man/1/mdb
+justify	/man/2/security-login
+justify	/man/6/login
+justify	/man/9/button
+justify	/man/9/canvas
+justify	/man/9/checkbutton
+justify	/man/9/choicebutton
+justify	/man/9/entry
+justify	/man/9/label
+justify	/man/9/menubutton
+justify	/man/9/options
+justify	/man/9/radiobutton
+justify	/man/9/text
+jvalue	/man/2/json
+jvalue.array	/man/2/json
+jvalue.false	/man/2/json
+jvalue.int	/man/2/json
+jvalue.null	/man/2/json
+jvalue.object	/man/2/json
+jvalue.real	/man/2/json
+jvalue.string	/man/2/json
+jvalue.true	/man/2/json
+jvarray	/man/2/json
+jvbig	/man/2/json
+jvfalse	/man/2/json
+jvint	/man/2/json
+jvnull	/man/2/json
+jvobject	/man/2/json
+jvreal	/man/2/json
+jvstring	/man/2/json
+jvtrue	/man/2/json
+ka	/man/10/2a
+kagstrom	/man/2/math-linalg
+kbd	/man/1/wm
+kbd	/man/10/kbdputc
+kbd	/man/2/draw-context
+kbd	/man/2/tkclient
+kbd	/man/2/wmclient
+kbd	/man/2/wmlib
+kbd	/man/2/wmsrv
+kbdclock	/man/10/kbdputc
+kbdfocus	/man/1/wm
+kbdinit	/man/10/kbdputc
+kbdputc	/man/10/kbdputc
+kbdputc	/man/6/keyboard
+kbdq	/man/10/kbdputc
+kbdrepeat	/man/10/kbdputc
+kc	/man/10/2c
+keen	/man/10/kproc
+keepalive	/man/3/ip
+keeps	/man/1/blur
+keeps	/man/1/spree-join
+keeps	/man/2/spree-cardlib
+keeps	/man/2/styxservers
+keeps	/man/4/ftpfs
+keeps	/man/8/bootpd
+keeps	/man/8/prep
+keeps	/man/8/rdbgsrv
+kelvin	/man/1/units
+kenwood	/man/3/sd
+kept	/man/1/alphabet-abc
+kept	/man/1/alphabet-grid
+kept	/man/1/mash-make
+kept	/man/10/9load
+kept	/man/2/disks
+kept	/man/2/keyring-0intro
+kept	/man/2/keyring-sha1
+kept	/man/2/scsiio
+kept	/man/2/security-0intro
+kept	/man/3/i2c
+kept	/man/3/pbus
+kept	/man/4/factotum
+kept	/man/4/ftpfs
+kept	/man/5/0intro
+kept	/man/8/prep
+kern	/man/8/rdbgsrv
+kernel	/man/1/0intro
+kernel	/man/1/bind
+kernel	/man/1/cprof
+kernel	/man/1/mprof
+kernel	/man/1/prof
+kernel	/man/10/0intro
+kernel	/man/10/2c
+kernel	/man/10/5cv
+kernel	/man/10/9load
+kernel	/man/10/a.out
+kernel	/man/10/conf
+kernel	/man/10/dev
+kernel	/man/10/devattach
+kernel	/man/10/dmainit
+kernel	/man/10/dynld
+kernel	/man/10/error
+kernel	/man/10/inb
+kernel	/man/10/intrenable
+kernel	/man/10/kproc
+kernel	/man/10/kprof
+kernel	/man/10/ksize
+kernel	/man/10/kstrip
+kernel	/man/10/malloc
+kernel	/man/10/memory
+kernel	/man/10/newchan
+kernel	/man/10/panic
+kernel	/man/10/plan9.ini
+kernel	/man/10/qio
+kernel	/man/10/seconds
+kernel	/man/10/sleep
+kernel	/man/10/xalloc
+kernel	/man/2/0intro
+kernel	/man/2/dhcpclient
+kernel	/man/2/prof
+kernel	/man/2/sys-0intro
+kernel	/man/2/sys-bind
+kernel	/man/2/sys-export
+kernel	/man/2/sys-fd2path
+kernel	/man/2/sys-fversion
+kernel	/man/2/sys-stat
+kernel	/man/3/0intro
+kernel	/man/3/boot
+kernel	/man/3/cons
+kernel	/man/3/dbg
+kernel	/man/3/dynld
+kernel	/man/3/flash
+kernel	/man/3/indir
+kernel	/man/3/ip
+kernel	/man/3/kprof
+kernel	/man/3/logfs
+kernel	/man/3/mnt
+kernel	/man/3/pipe
+kernel	/man/3/prof
+kernel	/man/3/root
+kernel	/man/3/touch
+kernel	/man/4/export
+kernel	/man/4/import
+kernel	/man/4/namespace
+kernel	/man/5/0intro
+kernel	/man/5/attach
+kernel	/man/5/open
+kernel	/man/5/stat
+kernel	/man/8/init
+kernel	/man/8/mangaload
+kernel	/man/8/prep
+kernel	/man/8/rdbgsrv
+kernel	/man/8/shutdown
+kernel's	/man/10/conf
+kernel's	/man/10/dev
+kernel's	/man/10/intrenable
+kernel's	/man/10/plan9.ini
+kernel's	/man/3/ds
+kernel's	/man/3/sign
+kernelpercent	/man/10/plan9.ini
+kernels	/man/10/0intro
+kernels	/man/10/conf
+kernels	/man/10/dev
+kernels	/man/10/plan9.ini
+kernels	/man/10/print
+kernels	/man/10/styx
+kernels	/man/2/tftp
+kernels	/man/8/bootpd
+kernels	/man/8/init
+kernels	/man/8/prep
+kernighan	/man/1/m4
+kernighan	/man/1/yacc
+kexec	/man/2/tftp
+kexec	/man/3/boot
+key	/man/1/acme
+key	/man/1/alphabet-main
+key	/man/1/ar
+key	/man/1/charon
+key	/man/1/crypt
+key	/man/1/idea
+key	/man/1/look
+key	/man/1/miniterm
+key	/man/1/secstore
+key	/man/1/wm
+key	/man/10/iar
+key	/man/10/lock
+key	/man/10/plan9.ini
+key	/man/2/cfg
+key	/man/2/dbm
+key	/man/2/dict
+key	/man/2/factotum
+key	/man/2/hash
+key	/man/2/ir
+key	/man/2/keyring-0intro
+key	/man/2/keyring-auth
+key	/man/2/keyring-certtostr
+key	/man/2/keyring-crypt
+key	/man/2/keyring-gensk
+key	/man/2/keyring-sha1
+key	/man/2/keyset
+key	/man/2/mpeg
+key	/man/2/prefab-compound
+key	/man/2/readdir
+key	/man/2/registries
+key	/man/2/secstore
+key	/man/2/security-0intro
+key	/man/2/security-login
+key	/man/2/sexprs
+key	/man/2/spki
+key	/man/2/stringinttab
+key	/man/2/tk
+key	/man/3/cap
+key	/man/3/cons
+key	/man/3/sign
+key	/man/3/ssl
+key	/man/3/tls
+key	/man/3/tv
+key	/man/4/factotum
+key	/man/4/import
+key	/man/4/keyfs
+key	/man/4/keysrv
+key	/man/6/attrdb
+key	/man/6/auth
+key	/man/6/keyboard
+key	/man/6/keys
+key	/man/6/keytext
+key	/man/6/login
+key	/man/6/scancode
+key	/man/6/sexprs
+key	/man/8/ai2key
+key	/man/8/createsignerkey
+key	/man/8/getauthinfo
+key	/man/8/logind
+key	/man/8/signer
+key	/man/8/svc
+key	/man/9/bind
+key	/man/9/button
+key	/man/9/entry
+key	/man/9/scale
+key	/man/9/text
+key's	/man/2/keyset
+key's	/man/2/spki
+key2	/man/10/plan9.ini
+key:string	/man/2/hash
+keyboard	/man/1/acme
+keyboard	/man/1/ebook
+keyboard	/man/1/keyboard
+keyboard	/man/1/logon
+keyboard	/man/1/miniterm
+keyboard	/man/1/mux
+keyboard	/man/1/tiny
+keyboard	/man/1/wish
+keyboard	/man/1/wm
+keyboard	/man/10/9load
+keyboard	/man/10/kbdputc
+keyboard	/man/10/plan9.ini
+keyboard	/man/2/draw-context
+keyboard	/man/2/ir
+keyboard	/man/2/prefab-0intro
+keyboard	/man/2/security-0intro
+keyboard	/man/2/tk
+keyboard	/man/2/tkclient
+keyboard	/man/2/wmclient
+keyboard	/man/3/cons
+keyboard	/man/4/acme
+keyboard	/man/5/0intro
+keyboard	/man/6/keyboard
+keyboard	/man/6/scancode
+keyboard	/man/9/bind
+keyboard	/man/9/button
+keyboard	/man/9/canvas
+keyboard	/man/9/focus
+keyboard	/man/9/grab
+keyboard	/man/9/listbox
+keyboard	/man/9/menu
+keyboard	/man/9/menubutton
+keyboard	/man/9/options
+keyboard	/man/9/scale
+keyboard	/man/9/text
+keyboard.b	/man/1/keyboard
+keyboard.h	/man/10/kbdputc
+keyboard.h	/man/6/keyboard
+keyboard.m	/man/6/keyboard
+keyboards	/man/6/keyboard
+keyboards	/man/6/scancode
+keycode	/man/6/scancode
+keycolor	/man/2/mpeg
+keydb	/man/4/keyfs
+keydb	/man/4/namespace
+keydb	/man/6/keys
+keydb	/man/8/changelogin
+keydb	/man/8/createsignerkey
+keydb	/man/8/logind
+keydb	/man/8/signer
+keydb	/man/8/svc
+keydir	/man/2/registries
+keydir	/man/8/changelogin
+keyed	/man/2/dbm
+keyed	/man/2/keyring-sha1
+keyed	/man/3/cap
+keyfile	/man/1/alphabet-main
+keyfile	/man/1/bind
+keyfile	/man/1/collab
+keyfile	/man/1/crypt
+keyfile	/man/1/listen
+keyfile	/man/1/passwd
+keyfile	/man/2/keyset
+keyfile	/man/4/keyfs
+keyfile	/man/6/namespace
+keyfile	/man/8/ai2key
+keyfile	/man/8/collabsrv
+keyfile	/man/8/createsignerkey
+keyfs	/man/1/crypt
+keyfs	/man/1/passwd
+keyfs	/man/2/security-login
+keyfs	/man/4/keyfs
+keyfs	/man/4/keysrv
+keyfs	/man/4/namespace
+keyfs	/man/6/keys
+keyfs	/man/8/changelogin
+keyfs	/man/8/logind
+keyfs	/man/8/svc
+keyfs.b	/man/4/keyfs
+keyholder	/man/2/spki
+keyname	/man/8/getauthinfo
+keypad	/man/3/pointer
+keypad	/man/6/translate
+keypress	/man/9/bind
+keypress	/man/9/canvas
+keypress	/man/9/text
+keypresses	/man/10/kbdputc
+keyring	/man/1/alphabet-main
+keyring	/man/1/bind
+keyring	/man/1/collab
+keyring	/man/1/cpu
+keyring	/man/1/crypt
+keyring	/man/1/listen
+keyring	/man/1/passwd
+keyring	/man/1/rcmd
+keyring	/man/1/sh-expr
+keyring	/man/1/sum
+keyring	/man/2/0intro
+keyring	/man/2/keyring-0intro
+keyring	/man/2/keyring-auth
+keyring	/man/2/keyring-certtostr
+keyring	/man/2/keyring-crypt
+keyring	/man/2/keyring-gensk
+keyring	/man/2/keyring-getmsg
+keyring	/man/2/keyring-getstring
+keyring	/man/2/keyring-ipint
+keyring	/man/2/keyring-rc4
+keyring	/man/2/keyring-sha1
+keyring	/man/2/keyset
+keyring	/man/2/msgio
+keyring	/man/2/registries
+keyring	/man/2/security-auth
+keyring	/man/2/security-login
+keyring	/man/2/spki
+keyring	/man/3/cap
+keyring	/man/3/sign
+keyring	/man/4/factotum
+keyring	/man/4/keyfs
+keyring	/man/4/keysrv
+keyring	/man/6/auth
+keyring	/man/6/keytext
+keyring	/man/7/db
+keyring	/man/7/dbsrv
+keyring	/man/8/changelogin
+keyring	/man/8/createsignerkey
+keyring	/man/8/getauthinfo
+keyring	/man/8/rstyxd
+keyring.c	/man/2/keyring-0intro
+keyring.c	/man/2/keyring-auth
+keyring.c	/man/2/keyring-certtostr
+keyring.c	/man/2/keyring-getmsg
+keyring.c	/man/2/keyring-getstring
+keyring.c	/man/2/keyring-sha1
+keyring.m	/man/2/keyring-0intro
+keyring.m	/man/2/keyring-auth
+keyring.m	/man/2/keyring-certtostr
+keyring.m	/man/2/keyring-crypt
+keyring.m	/man/2/keyring-gensk
+keyring.m	/man/2/keyring-getmsg
+keyring.m	/man/2/keyring-getstring
+keyring.m	/man/2/keyring-ipint
+keyring.m	/man/2/keyring-rc4
+keyring.m	/man/2/keyring-sha1
+keyring.m	/man/2/security-0intro
+keyring.m	/man/2/security-auth
+keyring.m	/man/2/security-login
+keyring.m	/man/2/spki
+keys	/man/1/listen
+keys	/man/1/miniterm
+keys	/man/1/secstore
+keys	/man/1/wm-misc
+keys	/man/10/kbdputc
+keys	/man/2/cfg
+keys	/man/2/dbm
+keys	/man/2/dict
+keys	/man/2/factotum
+keys	/man/2/hash
+keys	/man/2/ir
+keys	/man/2/keyring-0intro
+keys	/man/2/keyring-certtostr
+keys	/man/2/keyring-gensk
+keys	/man/2/keyset
+keys	/man/2/prefab-compound
+keys	/man/2/registries
+keys	/man/2/secstore
+keys	/man/2/security-0intro
+keys	/man/2/spki
+keys	/man/2/stringinttab
+keys	/man/2/styxpersist
+keys	/man/3/sign
+keys	/man/3/ssl
+keys	/man/3/tinyfs
+keys	/man/3/tls
+keys	/man/4/factotum
+keys	/man/4/keyfs
+keys	/man/4/keysrv
+keys	/man/4/namespace
+keys	/man/6/keyboard
+keys	/man/6/keys
+keys	/man/6/keytext
+keys	/man/8/ai2key
+keys	/man/8/changelogin
+keys	/man/8/createsignerkey
+keys	/man/8/logind
+keys	/man/8/svc
+keys	/man/9/entry
+keys	/man/9/text
+keyset	/man/2/keyset
+keyset.b	/man/2/keyset
+keyset.m	/man/2/keyset
+keysforsigner	/man/2/keyset
+keyspec	/man/2/factotum
+keyspec	/man/2/styxpersist
+keyspec	/man/4/import
+keysrv	/man/1/passwd
+keysrv	/man/4/keysrv
+keysrv	/man/6/keys
+keysrv	/man/8/changelogin
+keysrv	/man/8/svc
+keysrv.b	/man/4/keysrv
+keystrokes	/man/2/draw-context
+keystrokes	/man/3/cons
+keystrokes	/man/9/entry
+keystrokes	/man/9/text
+keytext	/man/6/auth
+keytext	/man/6/keytext
+keytext	/man/8/ai2key
+keyword	/man/1/calc
+keyword	/man/1/man
+keyword	/man/2/keyring-ipint
+keywords	/man/1/man
+keywords	/man/1/mash
+keywords	/man/10/c2l
+keywords	/man/6/man
+keywords	/man/9/text
+kfile	/man/1/bind
+kfile	/man/1/collab
+kfs	/man/1/zeros
+kfs	/man/2/sys-stat
+kfs	/man/3/ds
+kfs	/man/3/ftl
+kfs	/man/3/logfs
+kfs	/man/4/kfs
+kfs	/man/4/namespace
+kfs	/man/6/proto
+kfs	/man/6/users
+kfs	/man/8/applylog
+kfs	/man/8/ftl
+kfs	/man/8/init
+kfs	/man/8/kfscmd
+kfs	/man/8/mkfs
+kfs	/man/8/prep
+kfs.b	/man/4/kfs
+kfs.file	/man/1/zeros
+kfs.file	/man/4/kfs
+kfscmd	/man/4/kfs
+kfscmd	/man/6/users
+kfscmd	/man/8/kfscmd
+kfscmd	/man/8/mkfs
+kfscmd.b	/man/8/kfscmd
+kfsfile	/man/8/ftl
+kh	/man/2/spki
+kick	/man/10/qio
+kids	/man/2/prefab-element
+kill	/man/1/acme
+kill	/man/1/grid-monitor
+kill	/man/1/kill
+kill	/man/1/os
+kill	/man/1/ps
+kill	/man/1/wm-misc
+kill	/man/10/acid
+kill	/man/2/debug
+kill	/man/2/ir
+kill	/man/2/styxflush
+kill	/man/2/sys-0intro
+kill	/man/2/timers
+kill	/man/2/tkclient
+kill	/man/2/wait
+kill	/man/2/wmclient
+kill	/man/3/cmd
+kill	/man/3/cons
+kill	/man/3/dbg
+kill	/man/3/prog
+kill	/man/4/ramfile
+kill	/man/8/cs
+kill.b	/man/1/kill
+killed	/man/1/deb
+killed	/man/1/grid-monitor
+killed	/man/1/os
+killed	/man/10/kproc
+killed	/man/3/cmd
+killed	/man/3/prog
+killed	/man/8/cs
+killgrp	/man/1/kill
+killgrp	/man/10/kproc
+killgrp	/man/2/sys-pctl
+killgrp	/man/3/prog
+killing	/man/1/kill
+killing	/man/2/sys-export
+killing	/man/2/timers
+killing	/man/2/wmlib
+killing	/man/3/cons
+killonclose	/man/3/cmd
+kills	/man/1/kill
+kills	/man/2/debug
+kills	/man/3/cons
+kilobyte	/man/3/tinyfs
+kilobytes	/man/1/du
+kilobytes	/man/8/httpd
+kilometre	/man/2/geodesy
+kinds	/man/1/sh-alphabet
+kinds	/man/2/prefab-element
+kinds	/man/2/spree-cardlib
+kinds	/man/2/xml
+kinds	/man/4/spree
+kinds	/man/9/options
+kinds	/man/9/text
+king	/man/2/spree-cardlib
+king	/man/6/keyboard
+kl	/man/10/2l
+klog	/man/3/cons
+kn	/man/2/bloomfilter
+knamelen	/man/10/devattach
+knight	/man/6/keyboard
+knowing	/man/2/draw-screen
+knowing	/man/2/security-0intro
+knowing	/man/2/tk
+knowing	/man/4/ramfile
+knowledge	/man/1/mash-make
+knowledge	/man/4/spree
+knows	/man/1/man
+knows	/man/10/9load
+knows	/man/2/dhcpclient
+knows	/man/2/spree-cardlib
+knows	/man/2/sys-stat
+knows	/man/2/tk
+knows	/man/5/attach
+kp	/man/6/keytext
+kp	/man/8/ai2key
+kpctl	/man/3/kprof
+kpdata	/man/10/kprof
+kpdata	/man/3/kprof
+kpdup	/man/10/kproc
+kpdupenvg	/man/10/kproc
+kpdupfdg	/man/10/kproc
+kpduppg	/man/10/kproc
+kprint	/man/3/cons
+kproc	/man/10/error
+kproc	/man/10/intrenable
+kproc	/man/10/kproc
+kproc	/man/10/sleep
+kproc	/man/3/dbg
+kproc	/man/4/spree
+kprof	/man/10/kprof
+kprof	/man/3/kprof
+kq	/man/6/keytext
+kq	/man/8/ai2key
+kr	/man/2/keyring-sha1
+kr	/man/2/security-auth
+kr	/man/3/cap
+kremvax	/man/1/wm-sh
+kremvax	/man/2/dial
+kremvax	/man/2/sys-dial
+ksize	/man/10/ksize
+ksize	/man/10/kstrip
+kstrip	/man/10/ksize
+kstrip	/man/10/kstrip
+l'orario	/man/6/translate
+l.s	/man/10/inb
+l.s	/man/10/lock
+l2a1157	/man/10/plan9.ini
+la	/man/2/geodesy
+la	/man/6/translate
+label	/man/1/cook
+label	/man/1/ebook
+label	/man/1/mash-tk
+label	/man/1/mux
+label	/man/1/tktester
+label	/man/1/tsort
+label	/man/10/conf
+label	/man/10/error
+label	/man/2/tkclient
+label	/man/2/translate
+label	/man/2/wmclient
+label	/man/4/acme
+label	/man/6/keytext
+label	/man/8/dns
+label	/man/8/prep
+label	/man/9/0intro
+label	/man/9/label
+label	/man/9/menu
+label	/man/9/scale
+label's	/man/9/label
+labeled	/man/1/wm-sh
+labelled	/man/1/acme
+labelled	/man/1/deb
+labelled	/man/1/mash-tk
+labelled	/man/10/5cv
+labelled	/man/2/dialog
+labelled	/man/2/keyring-auth
+labelled	/man/2/prefab-element
+labelled	/man/2/styx
+labelled	/man/2/w3c-css
+labelled	/man/3/ip
+labelled	/man/4/acme
+labelled	/man/6/auth
+labelled	/man/6/keyboard
+labelled	/man/6/keytext
+labelled	/man/6/ubfa
+labelled	/man/8/cs
+labels	/man/1/collab-clients
+labels	/man/1/tktester
+labels	/man/1/tsort
+labels	/man/10/c2l
+labels	/man/10/conf
+labels	/man/10/error
+labels	/man/2/palmfile
+labels	/man/6/ndb
+labels	/man/9/label
+labels	/man/9/menu
+labour	/man/1/alphabet-abc
+labour	/man/1/alphabet-grid
+labs	/man/10/0intro
+labs	/man/2/dialog
+lack	/man/1/os
+lack	/man/1/sh
+lack	/man/8/dns
+lack	/man/8/ping
+lacks	/man/1/mkdir
+lacks	/man/2/factotum
+lacks	/man/3/ip
+laddr	/man/2/dial
+lai	/man/1/idea
+laid	/man/1/charon
+laid	/man/1/mash-tk
+laid	/man/10/a.out
+laid	/man/10/c2l
+laid	/man/2/spree-cardlib
+laid	/man/5/stat
+lalo	/man/2/geodesy
+lalo2en	/man/2/geodesy
+lalo2str	/man/2/geodesy
+lan	/man/10/plan9.ini
+lance	/man/3/ether
+landscape	/man/2/print
+lane	/man/2/format
+language	/man/1/0intro
+language	/man/1/acme
+language	/man/1/asm
+language	/man/1/calc
+language	/man/1/charon
+language	/man/1/limbo
+language	/man/1/sh
+language	/man/10/2c
+language	/man/10/2l
+language	/man/10/acid
+language	/man/10/conf
+language	/man/2/0intro
+language	/man/2/dis
+language	/man/2/sh
+language	/man/2/sys-dup
+language	/man/2/sys-self
+language	/man/2/translate
+language	/man/2/w3c-xpointers
+language	/man/2/xml
+language	/man/6/translate
+lap	/man/3/plap
+laptops	/man/10/plan9.ini
+largely	/man/6/sexprs
+larger	/man/1/cal
+larger	/man/1/emu
+larger	/man/1/look
+larger	/man/1/tiny
+larger	/man/10/9load
+larger	/man/2/0intro
+larger	/man/2/command
+larger	/man/2/disks
+larger	/man/2/keyring-ipint
+larger	/man/2/prefab-element
+larger	/man/2/styx
+larger	/man/2/sys-file2chan
+larger	/man/2/wmsrv
+larger	/man/3/cons
+larger	/man/3/ftl
+larger	/man/3/i2c
+larger	/man/3/pipe
+larger	/man/3/vga
+larger	/man/4/kfs
+larger	/man/5/0intro
+larger	/man/6/dis
+larger	/man/6/image
+larger	/man/8/ftl
+larger	/man/8/mangaload
+larger	/man/8/prep
+larger	/man/9/grid
+larger	/man/9/pack
+largest	/man/1/acme
+largest	/man/1/calc
+largest	/man/1/deb
+largest	/man/1/math-misc
+largest	/man/10/readnum
+largest	/man/2/diskblocks
+largest	/man/2/keyring-ipint
+largest	/man/2/plumbmsg
+largest	/man/2/prefab-element
+largest	/man/2/readdir
+largest	/man/2/secstore
+largest	/man/2/sets
+largest	/man/2/wmsrv
+largest	/man/3/cons
+largest	/man/3/ip
+largest	/man/3/pbus
+largest	/man/8/prep
+largest	/man/9/grid
+lasterror	/man/2/prof
+lasterror	/man/9/variable
+lasting	/man/3/eia
+lastly	/man/3/ip
+lastuid	/man/2/palmfile
+latch	/man/3/lpt
+late	/man/10/c2l
+latency	/man/2/spree
+latex	/man/1/cook
+latex	/man/2/stringinttab
+latexbook	/man/1/cook
+latexbook	/man/2/stringinttab
+latexpart	/man/1/cook
+latexpart	/man/2/stringinttab
+latexproc	/man/1/cook
+latexproc	/man/2/stringinttab
+latexslides	/man/1/cook
+latexslides	/man/2/stringinttab
+latin	/man/10/kbdputc
+latin	/man/2/palmfile
+latin	/man/2/rfc822
+latin	/man/2/sys-0intro
+latin	/man/6/keyboard
+latin1	/man/2/convcs
+latitude	/man/2/geodesy
+latter	/man/1/acme
+latter	/man/1/calc
+latter	/man/1/calendar
+latter	/man/1/dd
+latter	/man/1/sh
+latter	/man/1/units
+latter	/man/10/plan9.ini
+latter	/man/10/styxserver
+latter	/man/2/dhcpclient
+latter	/man/2/dis
+latter	/man/2/factotum
+latter	/man/2/geodesy
+latter	/man/2/palmfile
+latter	/man/2/prof
+latter	/man/2/smtp
+latter	/man/2/spki
+latter	/man/3/ip
+latter	/man/3/logfs
+latter	/man/5/0intro
+latter	/man/6/colour
+latter	/man/6/dis
+latter	/man/6/sexprs
+latter	/man/8/create
+latter's	/man/6/login
+launched	/man/1/deb
+lax	/man/1/sh-alphabet
+lax	/man/10/2c
+lay	/man/2/prefab-compound
+lay	/man/2/prefab-element
+lay	/man/2/spree-cardlib
+lay	/man/9/options
+layer	/man/2/keyring-0intro
+layer	/man/2/security-0intro
+layer	/man/2/security-auth
+layer	/man/2/security-ssl
+layer	/man/2/spree-gather
+layer	/man/3/ftl
+layer	/man/3/mpeg
+layer	/man/3/ssl
+layer	/man/3/tls
+layer	/man/4/lockfs
+layer	/man/8/ftl
+layer	/man/8/logind
+layered	/man/2/wmsrv
+layout	/man/1/acme
+layout	/man/1/charon
+layout	/man/1/deb
+layout	/man/1/man
+layout	/man/1/mash-tk
+layout	/man/1/sh
+layout	/man/10/ar
+layout	/man/2/palmfile
+layout	/man/2/prefab-compound
+layout	/man/2/prefab-element
+layout	/man/2/prefab-environ
+layout	/man/2/prefab-style
+layout	/man/2/spree-cardlib
+layout	/man/2/sys-0intro
+layout	/man/3/logfs
+layout	/man/6/dis
+layout	/man/6/man
+layout	/man/9/grid
+layoutbox	/man/2/prefab-compound
+layoutlist	/man/2/prefab-compound
+layouts	/man/1/tktester
+layouts	/man/2/spree-cardlib
+layouts	/man/9/frame
+layouts	/man/9/grid
+lays	/man/9/grid
+lb	/man/1/tktester
+lba	/man/10/9load
+lba	/man/8/prep
+lbc	/man/1/tail
+lc	/man/1/ls
+lc	/man/2/bufio
+lc	/man/2/dial
+lc.gets	/man/2/bufio
+lcase	/man/1/dd
+lcd	/man/3/vid
+ld	/man/10/9load
+ld	/man/10/mk
+ld.com	/man/10/9load
+lda	/man/2/math-linalg
+ldb	/man/2/math-linalg
+ldc	/man/2/math-linalg
+ldepth	/man/2/draw-display
+ldepth	/man/6/image
+le	/man/1/sh-expr
+le	/man/10/a.out
+lead	/man/2/spree
+lead	/man/4/dbfs
+leader	/man/1/chgrp
+leader	/man/2/exception
+leader	/man/2/sys-stat
+leader	/man/3/logfs
+leader	/man/3/prog
+leader	/man/5/0intro
+leader	/man/5/stat
+leader	/man/6/users
+leaders	/man/3/logfs
+leaders	/man/3/prog
+leading	/man/1/chmod
+leading	/man/1/m4
+leading	/man/1/sh
+leading	/man/1/uuencode
+leading	/man/10/atoi
+leading	/man/2/ip
+leading	/man/2/math-linalg
+leading	/man/2/rfc822
+leading	/man/2/string
+leading	/man/2/w3c-uris
+leading	/man/2/xml
+leading	/man/3/cons
+leading	/man/3/ether
+leading	/man/3/lpt
+leading	/man/6/keytext
+leading	/man/6/sbl
+leading	/man/6/sexprs
+leaf	/man/10/a.out
+leaf	/man/10/inm
+leaf	/man/8/collabsrv
+leaf	/man/9/grid
+leaf	/man/9/pack
+leaks	/man/10/allocb
+learn	/man/1/mk
+learn	/man/10/mk
+learns	/man/8/rip
+lease	/man/2/dhcpclient
+lease	/man/8/dhcp
+lease.release	/man/2/dhcpclient
+leased	/man/2/dhcpclient
+leave	/man/10/dev
+leave	/man/2/prefab-element
+leave	/man/2/spree
+leave	/man/2/spree-cardlib
+leave	/man/2/styxservers
+leave	/man/2/wmsrv
+leave	/man/8/applylog
+leave	/man/8/collabsrv
+leave	/man/9/bind
+leave	/man/9/canvas
+leave	/man/9/grid
+leave	/man/9/pack
+leave	/man/9/text
+leavening	/man/1/units
+leaves	/man/1/acme
+leaves	/man/1/collab-clients
+leaves	/man/1/fmt
+leaves	/man/10/devattach
+leaves	/man/2/prefab-element
+leaves	/man/2/sys-byte2char
+leaves	/man/3/cons
+leaves	/man/8/collabsrv
+leaves	/man/8/dhcp
+leaves	/man/9/button
+leaves	/man/9/checkbutton
+leaves	/man/9/grid
+leaves	/man/9/menu
+leaves	/man/9/menubutton
+leaves	/man/9/radiobutton
+leaving	/man/1/acme
+leaving	/man/1/charon
+leaving	/man/1/mux
+leaving	/man/1/wm
+leaving	/man/1/yacc
+leaving	/man/10/5cv
+leaving	/man/10/9load
+leaving	/man/10/allocb
+leaving	/man/10/devattach
+leaving	/man/10/qio
+leaving	/man/2/draw-display
+leaving	/man/2/draw-image
+leaving	/man/2/math-linalg
+leaving	/man/2/palmfile
+leaving	/man/2/prefab-element
+leaving	/man/2/sys-read
+leaving	/man/3/dbg
+leaving	/man/4/import
+leaving	/man/9/bind
+leaving	/man/9/pack
+leaving	/man/9/text
+led	/man/3/pbus
+leftmost	/man/1/acme
+leftmost	/man/1/mash-tk
+leftmost	/man/1/tiny
+leftmost	/man/2/draw-image
+leftmost	/man/2/ip
+leftmost	/man/2/regex
+leftmost	/man/2/string
+leftmost	/man/2/sys-tokenize
+leftmost	/man/6/colour
+legacy	/man/3/sd
+legal	/man/1/mk
+legal	/man/10/2c
+legal	/man/10/atoi
+legal	/man/10/mk
+legal	/man/10/rune
+legal	/man/2/sexprs
+legal	/man/2/spki
+legal	/man/2/styxservers
+legal	/man/2/sys-dup
+legal	/man/3/eia
+legal	/man/5/0intro
+legal	/man/5/walk
+legal	/man/6/audio
+legal	/man/9/canvas
+legal	/man/9/image
+legal	/man/9/text
+legitimate	/man/2/security-0intro
+lempel	/man/1/gzip
+lempel	/man/6/image
+len	/man/1/m4
+len	/man/1/mash
+len	/man/1/sh-string
+len	/man/10/dmainit
+len	/man/10/print
+len	/man/10/qio
+len	/man/2/bloomfilter
+len	/man/2/dial
+len	/man/2/keyring-sha1
+len	/man/2/styxflush
+len	/man/3/arch
+len	/man/3/cap
+length	/man/1/dd
+length	/man/1/fmt
+length	/man/1/m4
+length	/man/1/mash
+length	/man/1/mk
+length	/man/1/sh-string
+length	/man/1/sum
+length	/man/1/tr
+length	/man/10/5cv
+length	/man/10/a.out
+length	/man/10/ar
+length	/man/10/devattach
+length	/man/10/dmainit
+length	/man/10/dynld
+length	/man/10/mk
+length	/man/10/plan9.ini
+length	/man/10/print
+length	/man/10/qio
+length	/man/10/readnum
+length	/man/10/rune
+length	/man/10/strcat
+length	/man/10/styx
+length	/man/2/asn1
+length	/man/2/convcs
+length	/man/2/dis
+length	/man/2/disks
+length	/man/2/ether
+length	/man/2/ida
+length	/man/2/ip
+length	/man/2/keyring-crypt
+length	/man/2/keyring-gensk
+length	/man/2/keyring-ipint
+length	/man/2/keyring-rc4
+length	/man/2/lists
+length	/man/2/palmfile
+length	/man/2/plumbmsg
+length	/man/2/regex
+length	/man/2/scsiio
+length	/man/2/security-0intro
+length	/man/2/security-login
+length	/man/2/security-random
+length	/man/2/styx
+length	/man/2/sys-byte2char
+length	/man/2/sys-dirread
+length	/man/2/sys-open
+length	/man/2/sys-pipe
+length	/man/2/sys-stat
+length	/man/2/sys-tokenize
+length	/man/2/sys-utfbytes
+length	/man/2/translate
+length	/man/2/w3c-css
+length	/man/3/cons
+length	/man/3/draw
+length	/man/3/ds
+length	/man/3/dup
+length	/man/3/env
+length	/man/3/ftl
+length	/man/3/i2c
+length	/man/3/pbus
+length	/man/3/pipe
+length	/man/3/prog
+length	/man/3/srv
+length	/man/3/ssl
+length	/man/3/tls
+length	/man/3/usb
+length	/man/4/factotum
+length	/man/4/keyfs
+length	/man/4/ramfile
+length	/man/4/spree
+length	/man/5/0intro
+length	/man/5/stat
+length	/man/5/version
+length	/man/6/dis
+length	/man/6/image
+length	/man/6/sexprs
+length	/man/6/ubfa
+length	/man/7/db
+length	/man/8/applylog
+length	/man/8/changelogin
+length	/man/8/ftl
+length	/man/8/styxchat
+length	/man/9/bind
+length	/man/9/canvas
+length	/man/9/entry
+lengths	/man/3/ds
+lengths	/man/3/ssl
+lengths	/man/5/0intro
+lengths	/man/6/image
+lengths	/man/6/man
+lesser	/man/10/malloc
+lesser	/man/2/styx
+lets	/man/1/alphabet-fs
+lets	/man/1/fs
+lets	/man/1/mash-tk
+letter	/man/1/acme
+letter	/man/1/cal
+letter	/man/1/chmod
+letter	/man/1/ls
+letter	/man/1/m4
+letter	/man/1/sh-arg
+letter	/man/10/2a
+letter	/man/10/2c
+letter	/man/10/2l
+letter	/man/10/c2l
+letter	/man/10/ntsrv
+letter	/man/10/styx
+letter	/man/2/alphabet-intro
+letter	/man/2/security-0intro
+letter	/man/2/sexprs
+letter	/man/3/cmd
+letter	/man/3/cons
+letter	/man/3/draw
+letter	/man/3/fs
+letter	/man/3/pointer
+letter	/man/6/image
+letter	/man/6/keyboard
+letter	/man/8/mangaload
+letter	/man/9/bind
+letter	/man/9/grid
+letters	/man/1/chmod
+letters	/man/1/diff
+letters	/man/1/grep
+letters	/man/1/keyboard
+letters	/man/1/look
+letters	/man/1/m4
+letters	/man/1/tr
+letters	/man/1/wc
+letters	/man/10/atoi
+letters	/man/10/inm
+letters	/man/2/arg
+letters	/man/2/encoding
+letters	/man/2/geodesy
+letters	/man/2/sexprs
+letters	/man/2/string
+letters	/man/3/pnp
+letters	/man/6/keyboard
+letters	/man/6/man
+letters	/man/6/proto
+letters	/man/6/sbl
+letters	/man/6/sexprs
+letters	/man/8/styxchat
+letters	/man/9/entry
+letters	/man/9/menu
+letters	/man/9/text
+letting	/man/6/utf
+level	/man/1/0intro
+level	/man/1/acme
+level	/man/1/alphabet-fs
+level	/man/1/ebook
+level	/man/1/fs
+level	/man/1/grid-query
+level	/man/1/grid-session
+level	/man/1/gzip
+level	/man/1/itest
+level	/man/1/mash
+level	/man/1/math-misc
+level	/man/1/mprof
+level	/man/1/os
+level	/man/1/sh
+level	/man/1/sh-alphabet
+level	/man/1/sh-test
+level	/man/1/sh-tk
+level	/man/1/tiny
+level	/man/1/tkcmd
+level	/man/1/wm-misc
+level	/man/10/allocb
+level	/man/10/dev
+level	/man/10/devattach
+level	/man/10/error
+level	/man/10/intrenable
+level	/man/10/kproc
+level	/man/10/lock
+level	/man/10/odbc
+level	/man/10/plan9.ini
+level	/man/10/splhi
+level	/man/10/styxserver
+level	/man/10/xalloc
+level	/man/2/alphabet-intro
+level	/man/2/convcs
+level	/man/2/debug
+level	/man/2/draw-0intro
+level	/man/2/draw-example
+level	/man/2/draw-image
+level	/man/2/factotum
+level	/man/2/filter-deflate
+level	/man/2/ida
+level	/man/2/itslib
+level	/man/2/json
+level	/man/2/prefab-compound
+level	/man/2/rfc822
+level	/man/2/scsiio
+level	/man/2/security-0intro
+level	/man/2/sh
+level	/man/2/styx
+level	/man/2/styxservers
+level	/man/2/sys-fversion
+level	/man/2/tk
+level	/man/2/tkclient
+level	/man/2/ubfa
+level	/man/2/venti
+level	/man/2/w3c-css
+level	/man/2/w3c-xpointers
+level	/man/2/wmlib
+level	/man/2/xml
+level	/man/3/arch
+level	/man/3/audio
+level	/man/3/boot
+level	/man/3/cmd
+level	/man/3/cons
+level	/man/3/draw
+level	/man/3/ds
+level	/man/3/dup
+level	/man/3/eia
+level	/man/3/env
+level	/man/3/ether
+level	/man/3/flash
+level	/man/3/floppy
+level	/man/3/ftl
+level	/man/3/gpio
+level	/man/3/i2c
+level	/man/3/ip
+level	/man/3/logfs
+level	/man/3/pbus
+level	/man/3/plap
+level	/man/3/prof
+level	/man/3/prog
+level	/man/3/root
+level	/man/3/sd
+level	/man/3/srv9
+level	/man/3/ssl
+level	/man/3/switch
+level	/man/3/tls
+level	/man/3/touch
+level	/man/3/usb
+level	/man/3/vga
+level	/man/4/factotum
+level	/man/4/iostats
+level	/man/4/keyfs
+level	/man/4/lockfs
+level	/man/4/registry
+level	/man/5/0intro
+level	/man/5/version
+level	/man/6/proto
+level	/man/6/sexprs
+level	/man/8/create
+level	/man/8/plumber
+level	/man/9/canvas
+level	/man/9/grab
+level	/man/9/grid
+level	/man/9/menu
+level	/man/9/pack
+levels	/man/1/alphabet-fs
+levels	/man/1/fs
+levels	/man/1/sh
+levels	/man/10/intrenable
+levels	/man/10/kproc
+levels	/man/10/plan9.ini
+levels	/man/2/keyring-0intro
+levels	/man/2/security-0intro
+levels	/man/2/venti
+lex	/man/1/yacc
+lex	/man/2/rfc822
+lex.c	/man/10/mk
+lex.lval.v	/man/1/yacc
+lex.o	/man/10/mk
+lexical	/man/1/yacc
+lexical	/man/2/sys-0intro
+lexical	/man/6/sexprs
+lexically	/man/1/cleanname
+lexically	/man/1/mash
+lexically	/man/1/sh
+lexically	/man/2/names
+lexicographic	/man/1/sort
+lexicographical	/man/1/acme
+lexicographical	/man/1/comm
+lexicographically	/man/1/tr
+lexicographically	/man/10/memory
+lexicographically	/man/10/strcat
+lflags	/man/1/mash-make
+lg	/man/2/math-elem
+lgamma	/man/2/math-elem
+liable	/man/10/c2l
+liable	/man/2/spree-cardlib
+liable	/man/9/1copyright
+lib	/man/1/charon
+lib	/man/1/deb
+lib	/man/1/ebook
+lib	/man/1/fortune
+lib	/man/1/fs
+lib	/man/1/grid-ns
+lib	/man/1/grid-query
+lib	/man/1/grid-session
+lib	/man/1/keyboard
+lib	/man/1/look
+lib	/man/1/man
+lib	/man/1/mash
+lib	/man/1/sh
+lib	/man/1/sh-alphabet
+lib	/man/1/tcs
+lib	/man/1/toolbar
+lib	/man/1/unicode
+lib	/man/1/units
+lib	/man/1/webgrab
+lib	/man/1/wm-misc
+lib	/man/1/yacc
+lib	/man/10/2c
+lib	/man/10/2l
+lib	/man/10/9load
+lib	/man/10/acid
+lib	/man/10/c2l
+lib	/man/10/conf
+lib	/man/2/arg
+lib	/man/2/asn1
+lib	/man/2/attrdb
+lib	/man/2/bloomfilter
+lib	/man/2/bufio
+lib	/man/2/cfg
+lib	/man/2/complete
+lib	/man/2/convcs
+lib	/man/2/crc
+lib	/man/2/csv
+lib	/man/2/daytime
+lib	/man/2/dbm
+lib	/man/2/debug
+lib	/man/2/dhcpclient
+lib	/man/2/dial
+lib	/man/2/dialog
+lib	/man/2/dict
+lib	/man/2/dis
+lib	/man/2/diskblocks
+lib	/man/2/disks
+lib	/man/2/dividers
+lib	/man/2/encoding
+lib	/man/2/env
+lib	/man/2/ether
+lib	/man/2/exception
+lib	/man/2/factotum
+lib	/man/2/filepat
+lib	/man/2/filter-deflate
+lib	/man/2/filter-slip
+lib	/man/2/format
+lib	/man/2/fsproto
+lib	/man/2/hash
+lib	/man/2/ida
+lib	/man/2/imagefile
+lib	/man/2/ip
+lib	/man/2/ir
+lib	/man/2/json
+lib	/man/2/keyset
+lib	/man/2/lists
+lib	/man/2/lock
+lib	/man/2/mpeg
+lib	/man/2/msgio
+lib	/man/2/names
+lib	/man/2/newns
+lib	/man/2/palmfile
+lib	/man/2/plumbmsg
+lib	/man/2/pop3
+lib	/man/2/popup
+lib	/man/2/print
+lib	/man/2/prof
+lib	/man/2/pslib
+lib	/man/2/rand
+lib	/man/2/readdir
+lib	/man/2/regex
+lib	/man/2/rfc822
+lib	/man/2/scsiio
+lib	/man/2/secstore
+lib	/man/2/security-auth
+lib	/man/2/security-login
+lib	/man/2/security-random
+lib	/man/2/security-ssl
+lib	/man/2/selectfile
+lib	/man/2/sexprs
+lib	/man/2/smtp
+lib	/man/2/spki
+lib	/man/2/spki-verifier
+lib	/man/2/spree-allow
+lib	/man/2/spree-cardlib
+lib	/man/2/string
+lib	/man/2/stringinttab
+lib	/man/2/styx
+lib	/man/2/styxconv
+lib	/man/2/styxflush
+lib	/man/2/styxpersist
+lib	/man/2/styxservers
+lib	/man/2/styxservers-nametree
+lib	/man/2/tabs
+lib	/man/2/tftp
+lib	/man/2/tkclient
+lib	/man/2/translate
+lib	/man/2/ubfa
+lib	/man/2/venti
+lib	/man/2/virgil
+lib	/man/2/volume
+lib	/man/2/w3c-css
+lib	/man/2/w3c-uris
+lib	/man/2/w3c-xpointers
+lib	/man/2/wmclient
+lib	/man/2/wmlib
+lib	/man/2/wmsrv
+lib	/man/2/workdir
+lib	/man/2/xml
+lib	/man/4/lockfs
+lib	/man/4/namespace
+lib	/man/6/keyboard
+lib	/man/6/ndb
+lib	/man/6/plumbing
+lib	/man/6/proto
+lib	/man/6/sexprs
+lib	/man/7/db
+lib	/man/7/dbsrv
+lib	/man/8/applylog
+lib	/man/8/bootpd
+lib	/man/8/collabsrv
+lib	/man/8/cs
+lib	/man/8/dns
+lib	/man/8/getauthinfo
+lib	/man/8/init
+lib	/man/8/mkfs
+lib	/man/8/plumber
+lib	/man/8/styxmon
+lib	/man/8/svc
+lib.a	/man/1/ar
+lib.a	/man/10/iar
+lib.h	/man/10/0intro
+lib9	/man/10/getfields
+lib9	/man/10/print
+lib9	/man/10/strcat
+lib9	/man/10/styx
+lib9	/man/2/sys-print
+lib9	/man/4/namespace
+lib9.h	/man/10/dynld
+lib9.h	/man/10/styx
+lib9.h	/man/10/styxserver
+lib9.h	/man/5/0intro
+libbio	/man/4/namespace
+libbio.a	/man/10/2c
+libc	/man/10/c2l
+libc.a	/man/10/2l
+libc.a	/man/10/mk
+libc.b	/man/10/c2l
+libc.h	/man/10/2l
+libc.h	/man/10/rune
+libc.m	/man/10/c2l
+libc0.b	/man/10/c2l
+libc0.m	/man/10/c2l
+libcrypt	/man/2/keyring-0intro
+libcrypt	/man/2/keyring-sha1
+libdraw	/man/2/draw-0intro
+libdraw	/man/2/draw-image
+libdraw	/man/4/namespace
+libfile	/man/10/acid
+libinterp	/man/2/draw-0intro
+libinterp	/man/2/keyring-0intro
+libinterp	/man/2/keyring-auth
+libinterp	/man/2/keyring-certtostr
+libinterp	/man/2/keyring-getmsg
+libinterp	/man/2/keyring-getstring
+libinterp	/man/2/keyring-sha1
+libinterp	/man/2/math-0intro
+libinterp	/man/2/math-elem
+libinterp	/man/2/math-export
+libinterp	/man/2/math-fp
+libinterp	/man/2/math-linalg
+libinterp	/man/2/prefab-0intro
+libinterp	/man/2/prefab-compound
+libinterp	/man/2/prefab-element
+libinterp	/man/2/prefab-environ
+libinterp	/man/2/prefab-style
+libinterp	/man/2/sys-byte2char
+libinterp	/man/2/sys-print
+libinterp	/man/2/tk
+libinterp	/man/4/namespace
+libkern	/man/10/atoi
+libkern	/man/10/getfields
+libkern	/man/10/memory
+libkern	/man/10/print
+libkern	/man/10/rune
+libkern	/man/10/strcat
+libkern	/man/10/styx
+libkern	/man/4/namespace
+libkeyring	/man/2/keyring-0intro
+libkeyring	/man/4/namespace
+libkfs	/man/4/namespace
+liblogfs	/man/3/logfs
+libmemdraw	/man/3/draw
+libmemdraw	/man/4/namespace
+libmemlayer	/man/4/namespace
+libmemlayerxx	/man/4/namespace
+libnandfs	/man/3/logfs
+libprefab	/man/2/prefab-0intro
+libprefab	/man/2/prefab-compound
+libprefab	/man/2/prefab-element
+libprefab	/man/2/prefab-environ
+libprefab	/man/2/prefab-style
+libprefab	/man/4/namespace
+libraries	/man/1/ar
+libraries	/man/10/0intro
+libraries	/man/10/2c
+libraries	/man/10/2l
+libraries	/man/10/ar
+libraries	/man/10/conf
+libraries	/man/2/math-0intro
+libraries	/man/2/sys-dup
+libraries	/man/4/namespace
+libraries	/man/6/colour
+library	/man/1/sh-std
+library	/man/10/0intro
+library	/man/10/2c
+library	/man/10/2l
+library	/man/10/acid
+library	/man/10/ar
+library	/man/10/c2l
+library	/man/10/conf
+library	/man/10/iar
+library	/man/10/mk
+library	/man/10/styxserver
+library	/man/2/draw-example
+library	/man/2/draw-image
+library	/man/2/itslib
+library	/man/2/prof
+library	/man/2/security-0intro
+library	/man/4/factotum
+library	/man/4/namespace
+library	/man/5/0intro
+libregexp	/man/4/namespace
+libstyx	/man/10/styxserver
+libtk	/man/2/tk
+libtk	/man/4/namespace
+licence	/man/2/keyring-crypt
+license	/man/9/1copyright
+licensing	/man/2/keyring-crypt
+licensing	/man/9/1copyright
+lie	/man/3/flash
+lie	/man/9/canvas
+lie	/man/9/scrollbar
+lies	/man/1/acme
+lies	/man/10/dmainit
+lies	/man/10/plan9.ini
+lies	/man/2/draw-point
+lies	/man/9/scale
+lies	/man/9/scrollbar
+lieu	/man/1/tiny
+lifetime	/man/3/cap
+lifetime	/man/9/canvas
+ligature	/man/6/keyboard
+lightyear	/man/1/units
+likely	/man/1/0intro
+likely	/man/1/charon
+likely	/man/1/sh-sexprs
+likely	/man/1/webgrab
+likely	/man/10/0intro
+likely	/man/10/dev
+likely	/man/10/master
+likely	/man/2/draw-display
+likely	/man/6/login
+likewise	/man/1/mash-tk
+likewise	/man/2/spree
+likewise	/man/4/kfs
+lim	/man/1/math-misc
+lim	/man/10/allocb
+limbo	/man/1/0intro
+limbo	/man/1/acme
+limbo	/man/1/asm
+limbo	/man/1/calc
+limbo	/man/1/cprof
+limbo	/man/1/deb
+limbo	/man/1/disdep
+limbo	/man/1/emu
+limbo	/man/1/fc
+limbo	/man/1/filename
+limbo	/man/1/itest
+limbo	/man/1/limbo
+limbo	/man/1/m4
+limbo	/man/1/mash
+limbo	/man/1/mash-make
+limbo	/man/1/mk
+limbo	/man/1/mprof
+limbo	/man/1/prof
+limbo	/man/1/sh
+limbo	/man/1/sh-expr
+limbo	/man/1/sh-std
+limbo	/man/1/sh-string
+limbo	/man/1/sh-tk
+limbo	/man/1/stack
+limbo	/man/1/tkcmd
+limbo	/man/1/wish
+limbo	/man/1/xd
+limbo	/man/1/yacc
+limbo	/man/10/c2l
+limbo	/man/10/conf
+limbo	/man/10/devattach
+limbo	/man/10/kproc
+limbo	/man/10/styx
+limbo	/man/2/0intro
+limbo	/man/2/alphabet-intro
+limbo	/man/2/arg
+limbo	/man/2/asn1
+limbo	/man/2/command
+limbo	/man/2/convcs
+limbo	/man/2/daytime
+limbo	/man/2/debug
+limbo	/man/2/dis
+limbo	/man/2/dividers
+limbo	/man/2/draw-0intro
+limbo	/man/2/draw-font
+limbo	/man/2/hash
+limbo	/man/2/ir
+limbo	/man/2/json
+limbo	/man/2/keyring-0intro
+limbo	/man/2/keyring-ipint
+limbo	/man/2/math-0intro
+limbo	/man/2/math-fp
+limbo	/man/2/palmfile
+limbo	/man/2/prefab-0intro
+limbo	/man/2/prefab-element
+limbo	/man/2/prefab-style
+limbo	/man/2/prof
+limbo	/man/2/sh
+limbo	/man/2/spree
+limbo	/man/2/string
+limbo	/man/2/styx
+limbo	/man/2/styxservers
+limbo	/man/2/sys-0intro
+limbo	/man/2/sys-dup
+limbo	/man/2/sys-file2chan
+limbo	/man/2/sys-pctl
+limbo	/man/2/sys-pipe
+limbo	/man/2/sys-print
+limbo	/man/2/sys-self
+limbo	/man/2/tk
+limbo	/man/2/ubfa
+limbo	/man/2/w3c-css
+limbo	/man/3/arch
+limbo	/man/3/dbg
+limbo	/man/3/draw
+limbo	/man/3/fs
+limbo	/man/3/prof
+limbo	/man/3/prog
+limbo	/man/4/namespace
+limbo	/man/5/0intro
+limbo	/man/5/stat
+limbo	/man/6/dis
+limbo	/man/6/font
+limbo	/man/6/image
+limbo	/man/6/json
+limbo	/man/6/sbl
+limbo	/man/6/sexprs
+limbo	/man/6/translate
+limbo	/man/6/ubfa
+limbo	/man/6/utf
+limbo	/man/7/db
+limbo	/man/8/styxchat
+limbo	/man/9/0intro
+limbo	/man/9/send
+limbo's	/man/1/sh-std
+limbo's	/man/2/draw-0intro
+limbo's	/man/2/exception
+limbo's	/man/2/json
+limbo's	/man/2/math-export
+limbo's	/man/2/plumbmsg
+limbo's	/man/2/ubfa
+limbo's	/man/6/sbl
+limbo.dis	/man/1/uuencode
+lime	/man/9/types
+limit	/man/1/look
+limit	/man/1/sh-file2chan
+limit	/man/1/tiny
+limit	/man/10/dmainit
+limit	/man/10/lock
+limit	/man/10/plan9.ini
+limit	/man/10/qio
+limit	/man/2/0intro
+limit	/man/2/rfc822
+limit	/man/2/sets
+limit	/man/2/styx
+limit	/man/3/cons
+limit	/man/3/eia
+limit	/man/3/ftl
+limit	/man/3/i2c
+limit	/man/3/prog
+limit	/man/3/sd
+limit	/man/3/tv
+limit	/man/4/acme
+limit	/man/4/archfs
+limit	/man/4/keyfs
+limit	/man/5/read
+limit	/man/5/version
+limit	/man/5/walk
+limit	/man/6/image
+limit,int	/man/10/qio
+limitations	/man/10/dmainit
+limited	/man/1/calc
+limited	/man/1/chmod
+limited	/man/1/mux
+limited	/man/1/sh-file2chan
+limited	/man/10/dynld
+limited	/man/10/error
+limited	/man/10/readnum
+limited	/man/2/lists
+limited	/man/2/math-0intro
+limited	/man/2/spki-verifier
+limited	/man/2/w3c-xpointers
+limited	/man/3/cap
+limited	/man/3/fs
+limited	/man/3/ip
+limited	/man/3/pnp
+limited	/man/3/sign
+limited	/man/3/usb
+limited	/man/4/ftpfs
+limited	/man/4/import
+limited	/man/5/stat
+limited	/man/6/keytext
+limited	/man/8/applylog
+limited	/man/8/dhcp
+limited	/man/9/1copyright
+limited	/man/9/bind
+limited	/man/9/grid
+limiting	/man/2/styx
+limits	/man/1/charon
+limits	/man/10/allocb
+limits	/man/10/qio
+limits	/man/2/bufio
+limits	/man/2/draw-0intro
+limits	/man/2/keyring-0intro
+limits	/man/2/lists
+limits	/man/3/tv
+linalg	/man/2/0intro
+linalg	/man/2/math-0intro
+linalg	/man/2/math-linalg
+line's	/man/2/attrdb
+line's	/man/2/rfc822
+line's	/man/9/text
+line.char	/man/9/text
+linear	/man/1/mash
+linear	/man/1/sh
+linear	/man/10/9load
+linear	/man/2/dbm
+linear	/man/2/math-0intro
+linear	/man/2/math-linalg
+linear	/man/3/vga
+linear	/man/6/colour
+lineend	/man/9/text
+linelength	/man/9/canvas
+lineno	/man/1/acme
+linenumber	/man/1/brutus
+linenumber	/man/10/acid
+lineop	/man/2/draw-image
+lines	/man/1/0intro
+lines	/man/1/acme
+lines	/man/1/brutus
+lines	/man/1/charon
+lines	/man/1/comm
+lines	/man/1/cprof
+lines	/man/1/deb
+lines	/man/1/diff
+lines	/man/1/fc
+lines	/man/1/fmt
+lines	/man/1/fortune
+lines	/man/1/grep
+lines	/man/1/look
+lines	/man/1/mash
+lines	/man/1/mk
+lines	/man/1/mprof
+lines	/man/1/p
+lines	/man/1/prof
+lines	/man/1/secstore
+lines	/man/1/sendmail
+lines	/man/1/sh
+lines	/man/1/sh-csv
+lines	/man/1/sh-std
+lines	/man/1/sort
+lines	/man/1/tail
+lines	/man/1/timestamp
+lines	/man/1/tiny
+lines	/man/1/uniq
+lines	/man/1/uuencode
+lines	/man/1/wc
+lines	/man/1/wm-sh
+lines	/man/10/2c
+lines	/man/10/acid
+lines	/man/10/conf
+lines	/man/10/inm
+lines	/man/10/mk
+lines	/man/10/plan9.ini
+lines	/man/2/attrdb
+lines	/man/2/cfg
+lines	/man/2/daytime
+lines	/man/2/draw-image
+lines	/man/2/plumbmsg
+lines	/man/2/prefab-compound
+lines	/man/2/print
+lines	/man/2/prof
+lines	/man/2/rfc822
+lines	/man/2/secstore
+lines	/man/2/smtp
+lines	/man/3/audio
+lines	/man/3/dbg
+lines	/man/3/draw
+lines	/man/3/flash
+lines	/man/3/ip
+lines	/man/3/mpeg
+lines	/man/3/tls
+lines	/man/3/tv
+lines	/man/4/acme
+lines	/man/4/dbfs
+lines	/man/4/registry
+lines	/man/6/attrdb
+lines	/man/6/audio
+lines	/man/6/namespace
+lines	/man/6/plumbing
+lines	/man/6/sbl
+lines	/man/6/translate
+lines	/man/6/users
+lines	/man/8/prep
+lines	/man/8/styxchat
+lines	/man/9/button
+lines	/man/9/canvas
+lines	/man/9/checkbutton
+lines	/man/9/label
+lines	/man/9/listbox
+lines	/man/9/menubutton
+lines	/man/9/options
+lines	/man/9/radiobutton
+lines	/man/9/scrollbar
+lines	/man/9/text
+linestart	/man/9/text
+linetab	/man/2/prof
+linewidth	/man/9/canvas
+ling	/man/2/math-linalg
+link	/man/1/charon
+link	/man/1/ebook
+link	/man/1/limbo
+link	/man/10/conf
+link	/man/10/styxserver
+link	/man/2/dis
+link	/man/2/format
+link	/man/2/styxservers
+link	/man/3/dbg
+link	/man/3/ip
+link	/man/3/plap
+link	/man/4/palmsrv
+link	/man/6/dis
+linkage	/man/1/limbo
+linkage	/man/10/2l
+linkage	/man/10/conf
+linkage	/man/10/dynld
+linkage	/man/2/dis
+linkage	/man/6/dis
+linked	/man/1/cook
+linked	/man/10/acid
+linked	/man/10/dynld
+linker	/man/10/ms2
+linking	/man/10/conf
+linkmtu	/man/2/ip
+links	/man/1/charon
+links	/man/1/ebook
+links	/man/1/mdb
+links	/man/2/dis
+links	/man/2/factotum
+links	/man/2/keyring-0intro
+links	/man/2/wmsrv
+links	/man/4/ftpfs
+links	/man/6/sexprs
+linksys	/man/10/plan9.ini
+linux	/man/1/ar
+linux	/man/10/conf
+linux	/man/10/styxserver
+linux	/man/4/namespace
+linux	/man/8/mangaload
+linux	/man/8/prep
+linuxswap	/man/8/prep
+lisp	/man/2/sexprs
+lisp's	/man/2/sexprs
+list	/man/1/acme
+list	/man/1/ar
+list	/man/1/blur
+list	/man/1/calc
+list	/man/1/charon
+list	/man/1/cook
+list	/man/1/cprof
+list	/man/1/crypt
+list	/man/1/deb
+list	/man/1/disdep
+list	/man/1/fc
+list	/man/1/filename
+list	/man/1/gettar
+list	/man/1/grid-monitor
+list	/man/1/grid-query
+list	/man/1/grid-register
+list	/man/1/grid-session
+list	/man/1/itest
+list	/man/1/logon
+list	/man/1/look
+list	/man/1/ls
+list	/man/1/man
+list	/man/1/mash
+list	/man/1/mash-make
+list	/man/1/mash-tk
+list	/man/1/mk
+list	/man/1/secstore
+list	/man/1/sendmail
+list	/man/1/sh
+list	/man/1/sh-alphabet
+list	/man/1/sh-arg
+list	/man/1/sh-csv
+list	/man/1/sh-file2chan
+list	/man/1/sh-regex
+list	/man/1/sh-sexprs
+list	/man/1/sh-std
+list	/man/1/sh-string
+list	/man/1/sh-tk
+list	/man/1/stack
+list	/man/1/tcs
+list	/man/1/tiny
+list	/man/1/tktester
+list	/man/1/tr
+list	/man/1/units
+list	/man/1/vacget
+list	/man/1/webgrab
+list	/man/1/wm-misc
+list	/man/1/yacc
+list	/man/10/2c
+list	/man/10/9load
+list	/man/10/acid
+list	/man/10/allocb
+list	/man/10/conf
+list	/man/10/delay
+list	/man/10/devattach
+list	/man/10/dynld
+list	/man/10/iar
+list	/man/10/inm
+list	/man/10/master
+list	/man/10/mk
+list	/man/10/odbc
+list	/man/10/plan9.ini
+list	/man/10/print
+list	/man/10/qio
+list	/man/10/qlock
+list	/man/10/srclist
+list	/man/2/0intro
+list	/man/2/alphabet-intro
+list	/man/2/arg
+list	/man/2/asn1
+list	/man/2/attrdb
+list	/man/2/cfg
+list	/man/2/command
+list	/man/2/convcs
+list	/man/2/csv
+list	/man/2/daytime
+list	/man/2/debug
+list	/man/2/dhcpclient
+list	/man/2/dialog
+list	/man/2/dict
+list	/man/2/dis
+list	/man/2/dividers
+list	/man/2/draw-context
+list	/man/2/draw-example
+list	/man/2/env
+list	/man/2/factotum
+list	/man/2/filepat
+list	/man/2/format
+list	/man/2/geodesy
+list	/man/2/hash
+list	/man/2/ip
+list	/man/2/ir
+list	/man/2/itslib
+list	/man/2/json
+list	/man/2/keyset
+list	/man/2/lists
+list	/man/2/names
+list	/man/2/plumbmsg
+list	/man/2/pop3
+list	/man/2/popup
+list	/man/2/prefab-0intro
+list	/man/2/prefab-compound
+list	/man/2/prefab-element
+list	/man/2/print
+list	/man/2/prof
+list	/man/2/registries
+list	/man/2/rfc822
+list	/man/2/scsiio
+list	/man/2/secstore
+list	/man/2/security-auth
+list	/man/2/selectfile
+list	/man/2/sets
+list	/man/2/sexprs
+list	/man/2/sh
+list	/man/2/smtp
+list	/man/2/spki
+list	/man/2/spki-verifier
+list	/man/2/spree
+list	/man/2/spree-allow
+list	/man/2/spree-cardlib
+list	/man/2/spree-gather
+list	/man/2/srv
+list	/man/2/string
+list	/man/2/styxservers
+list	/man/2/styxservers-nametree
+list	/man/2/sys-pctl
+list	/man/2/sys-print
+list	/man/2/sys-self
+list	/man/2/sys-stat
+list	/man/2/sys-tokenize
+list	/man/2/tkclient
+list	/man/2/translate
+list	/man/2/ubfa
+list	/man/2/virgil
+list	/man/2/w3c-css
+list	/man/2/w3c-xpointers
+list	/man/2/wmclient
+list	/man/2/wmlib
+list	/man/2/wmsrv
+list	/man/2/xml
+list	/man/3/0intro
+list	/man/3/cons
+list	/man/3/dbg
+list	/man/3/draw
+list	/man/3/dynld
+list	/man/3/indir
+list	/man/3/mpeg
+list	/man/3/pnp
+list	/man/3/prof
+list	/man/3/prog
+list	/man/3/sign
+list	/man/3/ssl
+list	/man/3/tls
+list	/man/3/vga
+list	/man/4/factotum
+list	/man/4/lockfs
+list	/man/4/namespace
+list	/man/4/registry
+list	/man/4/spree
+list	/man/6/attrdb
+list	/man/6/dis
+list	/man/6/font
+list	/man/6/json
+list	/man/6/keyboard
+list	/man/6/proto
+list	/man/6/sbl
+list	/man/6/sexprs
+list	/man/6/ubfa
+list	/man/6/users
+list	/man/7/db
+list	/man/8/applylog
+list	/man/8/dns
+list	/man/8/kfscmd
+list	/man/8/mkfs
+list	/man/8/ping
+list	/man/8/prep
+list	/man/9/bind
+list	/man/9/button
+list	/man/9/canvas
+list	/man/9/checkbutton
+list	/man/9/choicebutton
+list	/man/9/entry
+list	/man/9/frame
+list	/man/9/grid
+list	/man/9/image
+list	/man/9/label
+list	/man/9/listbox
+list	/man/9/menu
+list	/man/9/menubutton
+list	/man/9/options
+list	/man/9/pack
+list	/man/9/panel
+list	/man/9/radiobutton
+list	/man/9/scale
+list	/man/9/scrollbar
+list	/man/9/see
+list	/man/9/text
+list's	/man/2/prefab-element
+list2stringlist	/man/2/sh
+listbox	/man/1/tktester
+listbox	/man/9/0intro
+listbox	/man/9/listbox
+listbox's	/man/9/listbox
+listboxes	/man/1/tktester
+listboxes	/man/9/listbox
+listed	/man/1/ar
+listed	/man/1/calc
+listed	/man/1/ftree
+listed	/man/1/grid-ns
+listed	/man/1/grid-register
+listed	/man/1/grid-session
+listed	/man/1/ls
+listed	/man/1/secstore
+listed	/man/1/sh
+listed	/man/10/2l
+listed	/man/10/conf
+listed	/man/10/devattach
+listed	/man/10/iar
+listed	/man/10/plan9.ini
+listed	/man/10/srclist
+listed	/man/2/attrdb
+listed	/man/2/dial
+listed	/man/2/factotum
+listed	/man/2/registries
+listed	/man/2/security-auth
+listed	/man/2/spki
+listed	/man/2/styxservers
+listed	/man/2/sys-dial
+listed	/man/2/sys-pctl
+listed	/man/3/dbg
+listed	/man/4/namespace
+listed	/man/4/spree
+listed	/man/6/attrdb
+listed	/man/6/plumbing
+listed	/man/8/applylog
+listed	/man/8/collabsrv
+listed	/man/8/prep
+listed	/man/8/rip
+listed	/man/8/rstyxd
+listed	/man/9/options
+listed	/man/9/text
+listen	/man/1/listen
+listen	/man/1/wm-misc
+listen	/man/10/odbc
+listen	/man/2/dial
+listen	/man/2/security-0intro
+listen	/man/2/sys-dial
+listen	/man/2/sys-export
+listen	/man/3/ip
+listen	/man/3/plap
+listen	/man/3/tls
+listen	/man/4/export
+listen	/man/4/import
+listen	/man/4/keysrv
+listen	/man/4/lockfs
+listen	/man/4/registry
+listen	/man/4/spree
+listen	/man/8/httpd
+listen	/man/8/signer
+listen	/man/8/styxchat
+listen	/man/8/svc
+listen.b	/man/1/listen
+listener	/man/1/listen
+listener	/man/2/dial
+listener	/man/4/keyfs
+listener	/man/8/svc
+listeners	/man/8/cs
+listeners	/man/8/svc
+listening	/man/1/grid-register
+listening	/man/1/listen
+listening	/man/10/odbc
+listening	/man/2/security-0intro
+listening	/man/6/plumbing
+listening	/man/8/plumber
+listens	/man/1/brutus
+listens	/man/3/plap
+listens	/man/8/bootpd
+listens	/man/8/collabsrv
+listens	/man/8/httpd
+listens	/man/8/svc
+listing	/man/1/acme
+listing	/man/1/ar
+listing	/man/1/asm
+listing	/man/1/env
+listing	/man/1/gettar
+listing	/man/1/tcs
+listing	/man/1/tsort
+listing	/man/1/wc
+listing	/man/10/dev
+listing	/man/10/dynld
+listing	/man/10/iar
+listing	/man/2/sys-0intro
+listing	/man/4/factotum
+listing	/man/4/registry
+listing	/man/6/dis
+listing	/man/6/ndb
+listing	/man/8/applylog
+listing	/man/8/prep
+listings	/man/1/charon
+listings	/man/1/ls
+listnode	/man/2/sh
+lists	/man/1/acme
+lists	/man/1/charon
+lists	/man/1/cprof
+lists	/man/1/deb
+lists	/man/1/env
+lists	/man/1/gettar
+lists	/man/1/grid-session
+lists	/man/1/ls
+lists	/man/1/mash
+lists	/man/1/mprof
+lists	/man/1/prof
+lists	/man/1/sh
+lists	/man/1/sh-csv
+lists	/man/1/tktester
+lists	/man/1/tsort
+lists	/man/1/wm-misc
+lists	/man/10/acid
+lists	/man/10/allocb
+lists	/man/10/conf
+lists	/man/10/dev
+lists	/man/10/dynld
+lists	/man/2/0intro
+lists	/man/2/arg
+lists	/man/2/asn1
+lists	/man/2/convcs
+lists	/man/2/lists
+lists	/man/2/pop3
+lists	/man/2/prefab-element
+lists	/man/2/registries
+lists	/man/2/sexprs
+lists	/man/2/sh
+lists	/man/2/w3c-css
+lists	/man/2/w3c-uris
+lists	/man/2/w3c-xpointers
+lists	/man/3/dynld
+lists	/man/3/indir
+lists	/man/4/factotum
+lists	/man/4/spree
+lists	/man/4/vacfs
+lists	/man/6/dis
+lists	/man/6/ubfa
+lists	/man/8/ai2key
+lists	/man/8/collabsrv
+lists.b	/man/2/lists
+lists.m	/man/2/lists
+lit	/man/1/grid-monitor
+literal	/man/1/acme
+literal	/man/1/fs
+literal	/man/1/mk
+literal	/man/1/sh-alphabet
+literal	/man/1/sh-regex
+literal	/man/1/sh-string
+literal	/man/10/mk
+literal	/man/2/dis
+literal	/man/2/math-0intro
+literal	/man/2/prefab-style
+literal	/man/2/ubfa
+literal	/man/2/w3c-xpointers
+literal	/man/3/dbg
+literal	/man/4/acme
+literal	/man/5/0intro
+literal	/man/6/image
+literal	/man/6/man
+literal	/man/6/regexp
+literal	/man/8/styxchat
+literally	/man/1/acme
+literally	/man/1/sh-std
+literally	/man/1/tiny
+literally	/man/2/filepat
+literally	/man/2/spree-allow
+literals	/man/1/m4
+literals	/man/1/tiny
+literals	/man/6/man
+live	/man/1/acme
+live	/man/1/tiny
+live	/man/10/plan9.ini
+live	/man/2/xml
+live	/man/3/ip
+live	/man/3/prog
+live	/man/3/tinyfs
+live	/man/8/ping
+lives	/man/6/ndb
+lld	/man/10/2c
+lmargin	/man/2/print
+lmargin1	/man/9/text
+lmargin2	/man/9/text
+lmno	/man/1/idea
+lnkava	/man/1/alphabet-abc
+lnkava	/man/1/alphabet-grid
+lno	/man/2/attrdb
+lnum	/man/2/cfg
+lnvils	/man/1/grep
+lo	/man/2/geodesy
+lo	/man/6/dis
+load	/man/1/acme
+load	/man/1/alphabet-abc
+load	/man/1/alphabet-fs
+load	/man/1/alphabet-grid
+load	/man/1/alphabet-main
+load	/man/1/deb
+load	/man/1/disdep
+load	/man/1/echo
+load	/man/1/mash
+load	/man/1/mash-make
+load	/man/1/mash-tk
+load	/man/1/sh
+load	/man/1/sh-alphabet
+load	/man/1/sh-arg
+load	/man/1/sh-csv
+load	/man/1/sh-expr
+load	/man/1/sh-file2chan
+load	/man/1/sh-mload
+load	/man/1/sh-regex
+load	/man/1/sh-sexprs
+load	/man/1/sh-std
+load	/man/1/sh-string
+load	/man/1/sh-test
+load	/man/1/sh-tk
+load	/man/1/time
+load	/man/1/tiny
+load	/man/1/tktester
+load	/man/1/yacc
+load	/man/10/2l
+load	/man/10/5cv
+load	/man/10/9load
+load	/man/10/acid
+load	/man/10/dynld
+load	/man/10/plan9.ini
+load	/man/2/0intro
+load	/man/2/arg
+load	/man/2/asn1
+load	/man/2/attrdb
+load	/man/2/bloomfilter
+load	/man/2/bufio
+load	/man/2/bufio-chanfill
+load	/man/2/cfg
+load	/man/2/command
+load	/man/2/complete
+load	/man/2/convcs
+load	/man/2/crc
+load	/man/2/csv
+load	/man/2/daytime
+load	/man/2/dbm
+load	/man/2/debug
+load	/man/2/devpointer
+load	/man/2/dhcpclient
+load	/man/2/dial
+load	/man/2/dialog
+load	/man/2/dict
+load	/man/2/dis
+load	/man/2/diskblocks
+load	/man/2/disks
+load	/man/2/dividers
+load	/man/2/draw-0intro
+load	/man/2/draw-context
+load	/man/2/draw-display
+load	/man/2/draw-example
+load	/man/2/draw-font
+load	/man/2/draw-image
+load	/man/2/draw-point
+load	/man/2/draw-pointer
+load	/man/2/draw-rect
+load	/man/2/draw-screen
+load	/man/2/drawmux
+load	/man/2/encoding
+load	/man/2/env
+load	/man/2/ether
+load	/man/2/exception
+load	/man/2/factotum
+load	/man/2/filepat
+load	/man/2/filter
+load	/man/2/filter-deflate
+load	/man/2/filter-slip
+load	/man/2/format
+load	/man/2/fsproto
+load	/man/2/geodesy
+load	/man/2/hash
+load	/man/2/ida
+load	/man/2/imagefile
+load	/man/2/ip
+load	/man/2/ir
+load	/man/2/itslib
+load	/man/2/json
+load	/man/2/keyring-0intro
+load	/man/2/keyring-auth
+load	/man/2/keyring-certtostr
+load	/man/2/keyring-crypt
+load	/man/2/keyring-gensk
+load	/man/2/keyring-getmsg
+load	/man/2/keyring-getstring
+load	/man/2/keyring-ipint
+load	/man/2/keyring-rc4
+load	/man/2/keyring-sha1
+load	/man/2/keyset
+load	/man/2/lists
+load	/man/2/lock
+load	/man/2/math-0intro
+load	/man/2/math-elem
+load	/man/2/math-export
+load	/man/2/math-fp
+load	/man/2/math-linalg
+load	/man/2/mpeg
+load	/man/2/msgio
+load	/man/2/names
+load	/man/2/newns
+load	/man/2/palmfile
+load	/man/2/plumbmsg
+load	/man/2/pop3
+load	/man/2/popup
+load	/man/2/prefab-0intro
+load	/man/2/prefab-compound
+load	/man/2/prefab-element
+load	/man/2/prefab-environ
+load	/man/2/prefab-style
+load	/man/2/print
+load	/man/2/prof
+load	/man/2/pslib
+load	/man/2/rand
+load	/man/2/readdir
+load	/man/2/regex
+load	/man/2/registries
+load	/man/2/rfc822
+load	/man/2/scsiio
+load	/man/2/secstore
+load	/man/2/security-auth
+load	/man/2/security-login
+load	/man/2/security-random
+load	/man/2/security-ssl
+load	/man/2/selectfile
+load	/man/2/sets
+load	/man/2/sexprs
+load	/man/2/sh
+load	/man/2/smtp
+load	/man/2/spki
+load	/man/2/spki-verifier
+load	/man/2/spree
+load	/man/2/spree-allow
+load	/man/2/spree-cardlib
+load	/man/2/spree-objstore
+load	/man/2/srv
+load	/man/2/string
+load	/man/2/stringinttab
+load	/man/2/styx
+load	/man/2/styxconv
+load	/man/2/styxflush
+load	/man/2/styxpersist
+load	/man/2/styxservers
+load	/man/2/styxservers-nametree
+load	/man/2/sys-0intro
+load	/man/2/sys-bind
+load	/man/2/sys-byte2char
+load	/man/2/sys-chdir
+load	/man/2/sys-dial
+load	/man/2/sys-dirread
+load	/man/2/sys-dup
+load	/man/2/sys-export
+load	/man/2/sys-fauth
+load	/man/2/sys-fd2path
+load	/man/2/sys-file2chan
+load	/man/2/sys-fversion
+load	/man/2/sys-iounit
+load	/man/2/sys-millisec
+load	/man/2/sys-open
+load	/man/2/sys-pctl
+load	/man/2/sys-pipe
+load	/man/2/sys-print
+load	/man/2/sys-read
+load	/man/2/sys-remove
+load	/man/2/sys-seek
+load	/man/2/sys-self
+load	/man/2/sys-sleep
+load	/man/2/sys-stat
+load	/man/2/sys-tokenize
+load	/man/2/sys-utfbytes
+load	/man/2/sys-werrstr
+load	/man/2/tabs
+load	/man/2/tftp
+load	/man/2/timers
+load	/man/2/tk
+load	/man/2/tkclient
+load	/man/2/translate
+load	/man/2/ubfa
+load	/man/2/venti
+load	/man/2/virgil
+load	/man/2/volume
+load	/man/2/w3c-css
+load	/man/2/w3c-uris
+load	/man/2/w3c-xpointers
+load	/man/2/wait
+load	/man/2/wmclient
+load	/man/2/wmlib
+load	/man/2/wmsrv
+load	/man/2/workdir
+load	/man/2/xml
+load	/man/3/cap
+load	/man/3/draw
+load	/man/3/ds
+load	/man/3/dynld
+load	/man/3/ftl
+load	/man/3/sign
+load	/man/4/registry
+load	/man/6/dis
+load	/man/7/db
+load	/man/8/mangaload
+load	/man/8/mkfs
+load	/man/8/prep
+loadable	/man/1/mash
+loadable	/man/1/mash-make
+loadable	/man/1/mash-tk
+loadable	/man/1/sh
+loadable	/man/1/sh-alphabet
+loadable	/man/1/sh-arg
+loadable	/man/1/sh-csv
+loadable	/man/1/sh-expr
+loadable	/man/1/sh-file2chan
+loadable	/man/1/sh-mload
+loadable	/man/1/sh-regex
+loadable	/man/1/sh-sexprs
+loadable	/man/1/sh-std
+loadable	/man/1/sh-string
+loadable	/man/1/sh-test
+loadable	/man/1/sh-tk
+loadable	/man/10/dynld
+loadable	/man/2/sh
+loadaddr	/man/10/5cv
+loaded	/man/1/0intro
+loaded	/man/1/acme
+loaded	/man/1/avr
+loaded	/man/1/charon
+loaded	/man/1/cprof
+loaded	/man/1/disdep
+loaded	/man/1/echo
+loaded	/man/1/emu
+loaded	/man/1/mash
+loaded	/man/1/mprof
+loaded	/man/1/prof
+loaded	/man/1/secstore
+loaded	/man/1/sh
+loaded	/man/1/sh-alphabet
+loaded	/man/1/sh-std
+loaded	/man/1/tiny
+loaded	/man/1/tktester
+loaded	/man/10/2c
+loaded	/man/10/2l
+loaded	/man/10/5cv
+loaded	/man/10/9load
+loaded	/man/10/acid
+loaded	/man/10/dynld
+loaded	/man/2/0intro
+loaded	/man/2/filter
+loaded	/man/2/ir
+loaded	/man/2/prof
+loaded	/man/2/pslib
+loaded	/man/2/sh
+loaded	/man/2/spree
+loaded	/man/2/styxflush
+loaded	/man/2/styxservers
+loaded	/man/2/styxservers-nametree
+loaded	/man/2/translate
+loaded	/man/2/xml
+loaded	/man/3/cons
+loaded	/man/3/dynld
+loaded	/man/3/indir
+loaded	/man/3/prof
+loaded	/man/3/sign
+loaded	/man/4/spree
+loaded	/man/8/mangaload
+loaded	/man/8/prep
+loaded	/man/9/types
+loader	/man/10/2c
+loader	/man/10/2l
+loader	/man/10/5cv
+loader	/man/10/dynld
+loader	/man/2/dis
+loader	/man/6/dis
+loaders	/man/1/ar
+loaders	/man/10/2c
+loaders	/man/10/2l
+loaders	/man/10/9load
+loaders	/man/10/ar
+loaders	/man/10/dynld
+loaders	/man/10/iar
+loaders	/man/3/logfs
+loading	/man/1/0intro
+loading	/man/1/sh-alphabet
+loading	/man/1/tktester
+loading	/man/10/2l
+loading	/man/10/9load
+loading	/man/10/dynld
+loading	/man/2/convcs
+loading	/man/2/dis
+loading	/man/2/ir
+loading	/man/3/dynld
+loading	/man/4/acme
+loading	/man/4/ramfile
+loading	/man/8/mangaload
+loadmodule	/man/2/sh
+loadobj	/man/2/dis
+loads	/man/1/acme
+loads	/man/1/avr
+loads	/man/1/sh-alphabet
+loads	/man/1/sh-mload
+loads	/man/1/time
+loads	/man/1/toolbar
+loads	/man/10/5cv
+loads	/man/10/9load
+loads	/man/10/dynld
+loads	/man/2/sh
+loads	/man/2/translate
+loads	/man/3/dynld
+loads	/man/8/fpgaload
+loads	/man/8/httpd
+loads	/man/8/mangaload
+loads	/man/8/prep
+loan	/man/2/math-linalg
+lobby	/man/1/spree-join
+lobby	/man/4/spree
+loc	/man/2/xml
+localaddr	/man/3/plap
+localclosed	/man/3/tls
+localdir	/man/1/collab
+locale	/man/1/date
+locale	/man/2/daytime
+locale	/man/2/translate
+locale	/man/4/namespace
+locale	/man/6/translate
+locales	/man/6/translate
+localfile	/man/4/import
+localhost	/man/1/charon
+localhost	/man/10/odbc
+localip	/man/8/dhcp
+locally	/man/1/webgrab
+locally	/man/10/master
+locally	/man/2/draw-display
+locally	/man/2/keyring-0intro
+locally	/man/2/sys-export
+locally	/man/5/walk
+locally	/man/6/ndb
+locally	/man/6/sbl
+locally	/man/8/create
+locally	/man/8/dns
+localmask	/man/8/dhcp
+localreg	/man/1/grid-query
+localreg	/man/1/grid-register
+locals	/man/1/deb
+locals	/man/2/debug
+locals	/man/6/sbl
+localuser	/man/2/registries
+locate	/man/1/acme
+locate	/man/1/man
+locate	/man/10/ar
+locate	/man/10/plan9.ini
+locate	/man/2/debug
+locate	/man/2/prof
+locate	/man/9/canvas
+locate	/man/9/entry
+located	/man/1/grid-session
+located	/man/1/tktester
+located	/man/10/allocb
+located	/man/2/debug
+located	/man/2/registries
+locates	/man/1/acme
+locating	/man/2/convcs
+locating	/man/8/rip
+location	/man/1/0intro
+location	/man/1/deb
+location	/man/1/mdb
+location	/man/1/wm
+location	/man/10/9load
+location	/man/10/a.out
+location	/man/10/error
+location	/man/2/dis
+location	/man/2/draw-image
+location	/man/2/registries
+location	/man/2/sys-seek
+location	/man/2/sys-werrstr
+location	/man/2/tkclient
+location	/man/2/wmclient
+location	/man/2/xml
+location	/man/3/logfs
+location	/man/4/registry
+location	/man/8/create
+location	/man/8/svc
+location	/man/9/canvas
+location	/man/9/grid
+location	/man/9/listbox
+location	/man/9/panel
+location	/man/9/text
+locations	/man/1/mdb
+locations	/man/10/a.out
+locations	/man/10/styx
+locations	/man/2/dial
+locations	/man/3/0intro
+locations	/man/4/registry
+locations	/man/9/grid
+locations	/man/9/text
+locator	/man/2/xml
+lock	/man/10/kproc
+lock	/man/10/lock
+lock	/man/10/qlock
+lock	/man/10/ref
+lock	/man/10/sleep
+lock	/man/10/splhi
+lock	/man/2/bufio
+lock	/man/2/lock
+lock	/man/4/lockfs
+lock	/man/5/stat
+lock	/man/6/keyboard
+lock	/man/7/db
+lock.b	/man/2/lock
+lock.c	/man/10/lock
+lock.c	/man/10/qlock
+lock.m	/man/2/lock
+locked	/man/10/qio
+locked	/man/3/flash
+lockfs	/man/4/lockfs
+lockfs.b	/man/4/lockfs
+locking	/man/10/lock
+locking	/man/10/qlock
+locking	/man/2/lock
+locking	/man/2/sys-0intro
+locks	/man/10/lock
+locks	/man/10/qlock
+locks	/man/2/lock
+locks	/man/9/grab
+locksrv	/man/4/lockfs
+log	/man/1/calc
+log	/man/1/logon
+log	/man/1/sh-file2chan
+log	/man/1/timestamp
+log	/man/2/draw-image
+log	/man/2/drawmux
+log	/man/2/math-elem
+log	/man/3/draw
+log	/man/3/ip
+log	/man/3/kprof
+log	/man/3/logfs
+log	/man/4/factotum
+log	/man/4/iostats
+log	/man/4/keyfs
+log	/man/4/logfile
+log	/man/8/applylog
+log	/man/8/plumber
+log10	/man/1/calc
+log10	/man/2/math-elem
+log1p	/man/2/math-elem
+logarithm	/man/2/math-elem
+logarithm	/man/2/math-fp
+logarithm	/man/6/image
+logfile	/man/1/logwindow
+logfile	/man/1/sh-file2chan
+logfile	/man/4/logfile
+logfile	/man/8/httpd
+logfile.b	/man/4/logfile
+logfs	/man/3/flash
+logfs	/man/3/logfs
+logfs	/man/4/kfs
+logfsctl	/man/3/logfs
+logfsusers	/man/3/logfs
+logged	/man/3/ip
+logging	/man/1/timestamp
+logging	/man/2/pop3
+logging	/man/3/ip
+logic	/man/1/0intro
+logic	/man/10/plan9.ini
+logical	/man/1/calc
+logical	/man/1/sh-expr
+logical	/man/10/kproc
+logical	/man/2/attrdb
+logical	/man/2/dial
+logical	/man/2/draw-image
+logical	/man/2/plumbmsg
+logical	/man/2/sys-dial
+logical	/man/2/tk
+logical	/man/2/w3c-css
+logical	/man/2/wmsrv
+logical	/man/3/dbg
+logical	/man/3/draw
+logical	/man/3/ftl
+logical	/man/3/i2c
+logical	/man/3/ip
+logical	/man/3/pbus
+logical	/man/3/plap
+logical	/man/6/attrdb
+logical	/man/6/ndb
+logical	/man/8/plumber
+logical	/man/8/prep
+logical	/man/9/bind
+logically	/man/2/attrdb
+logically	/man/3/flash
+logically	/man/3/pnp
+logically	/man/6/attrdb
+login	/man/1/listen
+login	/man/1/logon
+login	/man/1/rcmd
+login	/man/1/telnet
+login	/man/2/keyring-0intro
+login	/man/2/security-0intro
+login	/man/2/security-login
+login	/man/3/ip
+login	/man/6/auth
+login	/man/6/keys
+login	/man/6/login
+login	/man/8/logind
+login.b	/man/2/security-login
+logind	/man/1/passwd
+logind	/man/2/keyset
+logind	/man/2/security-login
+logind	/man/4/keyfs
+logind	/man/4/keysrv
+logind	/man/4/namespace
+logind	/man/6/keys
+logind	/man/6/login
+logind	/man/6/ndb
+logind	/man/8/changelogin
+logind	/man/8/createsignerkey
+logind	/man/8/getauthinfo
+logind	/man/8/logind
+logind	/man/8/svc
+logind.b	/man/8/logind
+logm	/man/2/bloomfilter
+logon	/man/1/emu
+logon	/man/1/logon
+logon	/man/1/toolbar
+logon	/man/1/wm
+logon	/man/4/namespace
+logon	/man/6/namespace
+logon.b	/man/1/logon
+logs	/man/1/listen
+logs	/man/1/logon
+logs	/man/2/sys-0intro
+logs	/man/4/ftpfs
+logs.b	/man/8/applylog
+logwindow	/man/1/logwindow
+logwindow	/man/4/logfile
+logwindow.b	/man/1/logwindow
+log₂	/man/2/bloomfilter
+lone	/man/2/csv
+longer	/man/1/acme
+longer	/man/1/miniterm
+longer	/man/1/prof
+longer	/man/1/pwd
+longer	/man/1/sh-file2chan
+longer	/man/1/sh-std
+longer	/man/1/strings
+longer	/man/10/dynld
+longer	/man/10/master
+longer	/man/10/plan9.ini
+longer	/man/10/qio
+longer	/man/2/alphabet-intro
+longer	/man/2/debug
+longer	/man/2/draw-image
+longer	/man/2/keyring-getstring
+longer	/man/2/msgio
+longer	/man/2/sys-fd2path
+longer	/man/2/venti
+longer	/man/2/xml
+longer	/man/3/dup
+longer	/man/3/dynld
+longer	/man/3/srv9
+longer	/man/4/kfs
+longer	/man/4/spree
+longer	/man/5/0intro
+longer	/man/5/clunk
+longer	/man/5/flush
+longer	/man/8/create
+longer	/man/9/bind
+longer	/man/9/canvas
+longer	/man/9/grid
+longer	/man/9/menu
+longer	/man/9/pack
+longer	/man/9/text
+longest	/man/1/acme
+longest	/man/2/regex
+longest	/man/2/sys-byte2char
+longitude	/man/2/geodesy
+longjmp	/man/10/error
+longs	/man/10/inb
+look.b	/man/1/look
+lookahead	/man/1/wm-misc
+lookahead	/man/6/sexprs
+looking	/man/1/acme
+looking	/man/10/0intro
+looking	/man/10/kbdputc
+looking	/man/10/memory
+looking	/man/2/convcs
+looking	/man/2/debug
+looking	/man/2/spree-cardlib
+looking	/man/2/spree-objstore
+looking	/man/6/ndb
+looking	/man/8/httpd
+lookman	/man/1/man
+looks	/man/1/0intro
+looks	/man/1/ps
+looks	/man/1/sh
+looks	/man/10/dynld
+looks	/man/10/newchan
+looks	/man/10/plan9.ini
+looks	/man/2/disks
+looks	/man/2/keyset
+looks	/man/2/spree-cardlib
+looks	/man/2/styx
+looks	/man/2/tabs
+looks	/man/3/0intro
+looks	/man/4/registry
+looks	/man/6/keyboard
+looks	/man/6/ndb
+looks	/man/8/ai2key
+looks	/man/8/cs
+looks	/man/8/dns
+looks	/man/8/prep
+looks	/man/9/grid
+lookup	/man/1/fortune
+lookup	/man/1/man
+lookup	/man/2/asn1
+lookup	/man/2/cfg
+lookup	/man/2/dict
+lookup	/man/2/plumbmsg
+lookup	/man/2/stringinttab
+loop	/man/1/charon
+loop	/man/1/sh
+loop	/man/1/sh-csv
+loop	/man/1/sh-sexprs
+loop	/man/1/sh-std
+loop	/man/10/acid
+loop	/man/10/lock
+loop	/man/10/styxserver
+loop	/man/2/keyring-sha1
+loop	/man/2/math-linalg
+loop	/man/2/styxflush
+loop	/man/2/sys-read
+loop	/man/3/audio
+loopback	/man/10/styxserver
+loopback	/man/2/ip
+loopback	/man/3/audio
+looping	/man/1/sh-std
+loops	/man/1/acme
+loops	/man/10/lock
+loops	/man/2/math-linalg
+loops	/man/6/colour
+loosely	/man/1/sh
+loosely	/man/2/rfc822
+lose	/man/1/wm
+loses	/man/2/filepat
+losing	/man/2/sh
+losing	/man/2/spree
+losing	/man/3/logfs
+losing	/man/9/bind
+loss	/man/3/pbus
+lost	/man/1/acme
+lost	/man/1/m4
+lost	/man/1/man
+lost	/man/1/sh
+lost	/man/1/sh-file2chan
+lost	/man/1/tiny
+lost	/man/1/tktester
+lost	/man/10/plan9.ini
+lost	/man/10/sleep
+lost	/man/2/keyring-rc4
+lost	/man/2/sh
+lost	/man/3/logfs
+lost	/man/3/pnp
+lost	/man/8/ping
+lot	/man/1/0intro
+lots	/man/4/namespace
+loud	/man/3/audio
+low	/man/1/acme
+low	/man/1/os
+low	/man/10/ar
+low	/man/10/dmainit
+low	/man/10/qio
+low	/man/2/factotum
+low	/man/2/scsiio
+low	/man/2/spree-cardlib
+low	/man/2/venti
+low	/man/2/wmlib
+low	/man/3/boot
+low	/man/3/draw
+low	/man/3/logfs
+low	/man/3/sd
+low	/man/3/tinyfs
+low	/man/3/tv
+low	/man/5/0intro
+low	/man/6/colour
+low	/man/6/dis
+low	/man/6/font
+low	/man/6/image
+lower	/man/1/cal
+lower	/man/1/collab-clients
+lower	/man/1/dd
+lower	/man/1/look
+lower	/man/1/tr
+lower	/man/1/wm
+lower	/man/10/error
+lower	/man/2/convcs
+lower	/man/2/draw-rect
+lower	/man/2/encoding
+lower	/man/2/rfc822
+lower	/man/2/security-0intro
+lower	/man/2/sexprs
+lower	/man/2/sh
+lower	/man/2/string
+lower	/man/2/styx
+lower	/man/2/sys-fversion
+lower	/man/2/sys-print
+lower	/man/2/tk
+lower	/man/2/w3c-css
+lower	/man/2/w3c-uris
+lower	/man/3/tv
+lower	/man/6/keyboard
+lower	/man/6/man
+lower	/man/6/sexprs
+lower	/man/8/touchcal
+lower	/man/9/0intro
+lower	/man/9/canvas
+lower	/man/9/lower
+lower	/man/9/menu
+lower	/man/9/raise
+lower	/man/9/text
+lowered	/man/2/tk
+lowered	/man/3/pipe
+lowering	/man/1/wm-misc
+lowers	/man/9/lower
+lowest	/man/1/grid-query
+lowest	/man/10/kproc
+lowest	/man/2/sys-0intro
+lowest	/man/2/sys-dup
+lowest	/man/3/draw
+lowest	/man/6/dis
+lowest	/man/9/canvas
+lowest	/man/9/text
+lp	/man/6/man
+lpmnqduntscrft	/man/1/ls
+lport	/man/3/ip
+lpt	/man/3/lpt
+lpt1data	/man/2/print
+lpt?data	/man/3/lpt
+lpt?dlr	/man/3/lpt
+lpt?pcr	/man/3/lpt
+lpt?psr	/man/3/lpt
+lr	/man/1/yacc
+lr	/man/6/man
+ls	/man/1/alphabet-fs
+ls	/man/1/ar
+ls	/man/1/chgrp
+ls	/man/1/chmod
+ls	/man/1/fs
+ls	/man/1/ls
+ls	/man/1/sh
+ls	/man/1/sh-regex
+ls	/man/1/touch
+ls	/man/10/acid
+ls	/man/10/iar
+ls	/man/10/styxserver
+ls	/man/3/0intro
+ls	/man/3/indir
+ls	/man/4/9srvfs
+ls	/man/4/grid-cpu
+ls	/man/4/iostats
+ls	/man/4/tarfs
+ls	/man/8/rdbgsrv
+ls	/man/8/styxmon
+ls.b	/man/1/ls
+ls.dis	/man/1/sh
+ls.dis	/man/1/stack
+ls.sbl	/man/1/stack
+lsb	/man/2/asn1
+lserv	/man/2/dial
+lsi	/man/10/plan9.ini
+lsize	/man/2/dis
+lsl	/man/1/cmp
+lstar	/man/1/gettar
+lstar.b	/man/1/gettar
+lstk	/man/10/acid
+lsys	/man/2/dial
+lt	/man/1/sh-expr
+ltd	/man/2/keyring-crypt
+ltd	/man/6/attrdb
+lucas	/man/2/palmfile
+lucent	/man/10/plan9.ini
+lucent	/man/4/factotum
+lucent's	/man/3/indir
+lucidasans	/man/1/acme
+lucm	/man/1/acme
+lucm	/man/1/emu
+luminance	/man/2/imagefile
+luminance	/man/9/types
+luns	/man/3/sd
+lurks	/man/8/plumber
+lval	/man/1/yacc
+lwceb	/man/1/wc
+lx	/man/10/2c
+lz77	/man/6/image
+m.count	/man/2/styxservers
+m.tag	/man/2/styxflush
+m1args	/man/1/sh-alphabet
+m2args	/man/1/sh-alphabet
+mac	/man/2/ether
+mac	/man/3/ether
+mac	/man/3/ip
+mac	/man/3/pbus
+mac	/man/8/bootpd
+mac	/man/8/dhcp
+mach64xx	/man/3/vga
+mach64xxhwgc	/man/3/vga
+macheps	/man/1/fc
+macheps	/man/2/math-fp
+machine	/man/1/0intro
+machine	/man/1/asm
+machine	/man/1/bind
+machine	/man/1/cpu
+machine	/man/1/deb
+machine	/man/1/emu
+machine	/man/1/grid-register
+machine	/man/1/grid-session
+machine	/man/1/limbo
+machine	/man/1/ps
+machine	/man/1/rcmd
+machine	/man/1/telnet
+machine	/man/1/wm-misc
+machine	/man/10/2a
+machine	/man/10/2c
+machine	/man/10/9load
+machine	/man/10/a.out
+machine	/man/10/acid
+machine	/man/10/dev
+machine	/man/10/mk
+machine	/man/10/odbc
+machine	/man/10/plan9.ini
+machine	/man/10/styx
+machine	/man/10/styxserver
+machine	/man/2/0intro
+machine	/man/2/dial
+machine	/man/2/dis
+machine	/man/2/draw-screen
+machine	/man/2/keyring-0intro
+machine	/man/2/math-0intro
+machine	/man/2/math-fp
+machine	/man/2/styx
+machine	/man/2/sys-0intro
+machine	/man/2/sys-dial
+machine	/man/3/cap
+machine	/man/3/cons
+machine	/man/3/dbg
+machine	/man/3/ds
+machine	/man/3/srv9
+machine	/man/4/ftpfs
+machine	/man/4/keyfs
+machine	/man/4/lockfs
+machine	/man/5/0intro
+machine	/man/5/attach
+machine	/man/5/stat
+machine	/man/6/dis
+machine	/man/6/image
+machine	/man/6/namespace
+machine	/man/6/ubfa
+machine	/man/6/utf
+machine	/man/7/db
+machine	/man/7/dbsrv
+machine	/man/8/applylog
+machine	/man/8/create
+machine	/man/8/getauthinfo
+machine	/man/8/mkfs
+machine	/man/8/register
+machine	/man/8/rip
+machine	/man/8/signer
+machine	/man/8/virgild
+machine's	/man/1/emu
+machine's	/man/10/2c
+machine's	/man/10/9load
+machine's	/man/2/security-0intro
+machine's	/man/3/cons
+machine's	/man/4/ftpfs
+machine's	/man/8/getauthinfo
+machine's	/man/8/rip
+machine's	/man/8/signer
+machines	/man/1/0intro
+machines	/man/1/blur
+machines	/man/1/grid-session
+machines	/man/1/mux
+machines	/man/10/plan9.ini
+machines	/man/2/draw-screen
+machines	/man/3/arch
+machines	/man/3/ip
+machines	/man/6/keyboard
+machines	/man/8/bootpd
+machines	/man/8/getauthinfo
+macos	/man/3/fs
+macro	/man/1/m4
+macro	/man/1/man
+macro	/man/10/allocb
+macro	/man/10/seconds
+macro	/man/10/styx
+macro	/man/6/man
+macros	/man/1/m4
+macros	/man/1/man
+macros	/man/10/styx
+macros	/man/6/man
+mafter	/man/2/sys-bind
+mafter	/man/3/fs
+mafter	/man/6/namespace
+magenta	/man/2/draw-display
+magic	/man/10/a.out
+magic	/man/10/acid
+magic	/man/10/ar
+magic	/man/2/dis
+magic	/man/6/dis
+magic	/man/6/sbl
+magic	/man/8/httpd
+magic0	/man/2/disks
+magic1	/man/2/disks
+magnetic	/man/1/dd
+magnitude	/man/2/keyring-ipint
+magnitude	/man/2/math-fp
+mail	/man/1/acme
+mail	/man/1/mux
+mail	/man/1/sendmail
+mail	/man/1/uuencode
+mail	/man/2/dhcpclient
+mail	/man/2/encoding
+mail	/man/2/keyring-0intro
+mail	/man/2/pop3
+mail	/man/2/rfc822
+mail	/man/2/smtp
+mail	/man/2/translate
+mail	/man/4/ftpfs
+mail	/man/4/namespace
+mail	/man/6/ndb
+mail	/man/8/dns
+mailbox	/man/2/pop3
+mailboxes	/man/4/namespace
+mailpop3	/man/1/acme
+mailserver	/man/1/sendmail
+main	/man/1/alphabet-abc
+main	/man/1/alphabet-fs
+main	/man/1/alphabet-grid
+main	/man/1/alphabet-main
+main	/man/1/ar
+main	/man/1/charon
+main	/man/1/deb
+main	/man/1/emu
+main	/man/1/grid-monitor
+main	/man/1/grid-query
+main	/man/1/grid-register
+main	/man/1/mk
+main	/man/1/mprof
+main	/man/1/mux
+main	/man/1/sh
+main	/man/1/sh-alphabet
+main	/man/1/tktester
+main	/man/1/toolbar
+main	/man/1/wm-misc
+main	/man/10/0intro
+main	/man/10/2l
+main	/man/10/a.out
+main	/man/10/acid
+main	/man/10/c2l
+main	/man/10/dev
+main	/man/10/error
+main	/man/10/iar
+main	/man/10/mk
+main	/man/10/plan9.ini
+main	/man/10/print
+main	/man/10/styxserver
+main	/man/10/xalloc
+main	/man/2/0intro
+main	/man/2/alphabet-intro
+main	/man/2/sys-0intro
+main	/man/6/keyboard
+main	/man/6/man
+main	/man/6/sexprs
+main	/man/8/kfscmd
+main	/man/9/menu
+main.2	/man/10/2c
+main.c	/man/10/2c
+main.c	/man/10/error
+main:argv	/man/10/acid
+mainly	/man/1/acme
+mainly	/man/10/ar
+mainly	/man/10/styxserver
+mainly	/man/2/ip
+mainly	/man/2/palmfile
+mainly	/man/2/ubfa
+mainly	/man/3/cons
+mainly	/man/3/dbg
+mainly	/man/3/ftl
+mainly	/man/8/bootpd
+mainly	/man/8/dns
+mainmem	/man/10/malloc
+mainp	/man/10/2l
+mainstream	/man/1/tiny
+maintain	/man/1/grid-ns
+maintain	/man/1/mk
+maintain	/man/10/mk
+maintain	/man/2/draw-example
+maintain	/man/2/styxpersist
+maintain	/man/2/styxservers
+maintain	/man/2/wmsrv
+maintain	/man/3/draw
+maintain	/man/3/fs
+maintain	/man/4/spree
+maintain	/man/6/colour
+maintain	/man/8/applylog
+maintained	/man/1/acme
+maintained	/man/1/charon
+maintained	/man/1/fortune
+maintained	/man/1/mash-tk
+maintained	/man/1/sh-file2chan
+maintained	/man/2/scsiio
+maintained	/man/2/sys-0intro
+maintained	/man/2/tkclient
+maintained	/man/2/wmclient
+maintained	/man/2/wmlib
+maintained	/man/3/logfs
+maintained	/man/4/dbfs
+maintained	/man/4/memfs
+maintainer	/man/1/ar
+maintainer	/man/10/iar
+maintaining	/man/2/convcs
+maintaining	/man/3/draw
+maintaining	/man/4/factotum
+maintaining	/man/8/dhcp
+maintains	/man/1/acme
+maintains	/man/1/ar
+maintains	/man/1/m4
+maintains	/man/10/devattach
+maintains	/man/10/iar
+maintains	/man/10/plan9.ini
+maintains	/man/2/dbm
+maintains	/man/2/keyring-crypt
+maintains	/man/2/sys-0intro
+maintains	/man/3/logfs
+maintains	/man/5/0intro
+maintains	/man/6/dis
+maintains	/man/6/users
+maintains	/man/9/pack
+maintenance	/man/1/tiny
+maintenance	/man/3/sd
+maintenance	/man/9/1copyright
+majority	/man/2/ida
+make.b	/man/1/mash
+make.b	/man/1/mash-make
+makecard	/man/2/spree-cardlib
+makecards	/man/2/spree-cardlib
+makedrawcontext	/man/2/tkclient
+makedrawcontext	/man/2/wmclient
+maketable	/man/2/spree-cardlib
+maketag	/man/2/spki
+maketemp	/man/1/m4
+malformed	/man/1/acme
+malformed	/man/2/w3c-css
+malicious	/man/2/styxservers
+malingering	/man/4/lockfs
+malloc	/man/1/emu
+malloc	/man/10/allocb
+malloc	/man/10/c2l
+malloc	/man/10/intrenable
+malloc	/man/10/malloc
+malloc	/man/10/parsecmd
+malloc	/man/10/print
+malloc	/man/10/qlock
+malloc	/man/10/strcat
+malloc	/man/10/xalloc
+mallocz	/man/10/error
+mallocz	/man/10/malloc
+man.b	/man/1/man
+man2html	/man/1/man
+man2txt	/man/1/man
+man2txt.b	/man/1/man
+manage	/man/1/0intro
+manage	/man/1/ar
+manage	/man/1/gettar
+manage	/man/1/wm
+manage	/man/1/wm-sh
+manage	/man/10/devattach
+manage	/man/10/dmainit
+manage	/man/2/draw-0intro
+manage	/man/2/ir
+manage	/man/2/styxservers
+manage	/man/2/sys-dup
+manage	/man/2/sys-file2chan
+manage	/man/2/timers
+manage	/man/9/grid
+manage	/man/9/pack
+managed	/man/1/blur
+managed	/man/1/charon
+managed	/man/1/wm-misc
+managed	/man/10/odbc
+managed	/man/10/plan9.ini
+managed	/man/2/draw-0intro
+managed	/man/2/security-login
+managed	/man/6/colour
+managed	/man/8/logind
+managed	/man/8/svc
+managed	/man/9/canvas
+managed	/man/9/grid
+managed	/man/9/pack
+management	/man/1/wm-misc
+management	/man/10/allocb
+management	/man/10/devattach
+management	/man/10/plan9.ini
+management	/man/10/xalloc
+management	/man/2/draw-0intro
+management	/man/2/draw-context
+management	/man/2/draw-image
+management	/man/2/draw-screen
+management	/man/2/prefab-element
+management	/man/2/styxservers
+management	/man/3/ftl
+management	/man/4/factotum
+management	/man/7/db
+manager	/man/1/0intro
+manager	/man/1/charon
+manager	/man/1/dmview
+manager	/man/1/mash-tk
+manager	/man/1/sh-tk
+manager	/man/1/tkcmd
+manager	/man/1/toolbar
+manager	/man/1/wm
+manager	/man/1/wm-misc
+manager	/man/10/9load
+manager	/man/10/ntsrv
+manager	/man/2/command
+manager	/man/2/draw-context
+manager	/man/2/draw-display
+manager	/man/2/draw-screen
+manager	/man/2/sys-pctl
+manager	/man/2/tk
+manager	/man/2/tkclient
+manager	/man/2/wmclient
+manager	/man/2/wmlib
+manager	/man/2/wmsrv
+manager	/man/3/prog
+manager	/man/4/namespace
+manager	/man/8/plumber
+manager	/man/9/canvas
+manager	/man/9/grab
+manager	/man/9/grid
+manager	/man/9/options
+manager	/man/9/pack
+manager's	/man/2/tkclient
+manager's	/man/2/wmclient
+manager's	/man/2/wmlib
+manager's	/man/2/wmsrv
+managers	/man/8/plumber
+manages	/man/1/acme
+manages	/man/1/secstore
+manages	/man/2/diskblocks
+manages	/man/3/draw
+manages	/man/3/ftl
+manages	/man/9/grid
+managing	/man/2/styxflush
+managing	/man/4/factotum
+managing	/man/5/0intro
+managing	/man/9/text
+mand	/man/1/wm-misc
+mand.b	/man/1/wm-misc
+mandated	/man/2/math-0intro
+mandatory	/man/1/sh-alphabet
+mandelbrot	/man/1/wm-misc
+manga	/man/8/mangaload
+mangaload	/man/8/mangaload
+mangaload.b	/man/8/mangaload
+manga™	/man/8/mangaload
+manifest	/man/1/ar
+manifestation	/man/6/utf
+manipulate	/man/1/ar
+manipulate	/man/1/blur
+manipulate	/man/10/dev
+manipulate	/man/10/odbc
+manipulate	/man/2/env
+manipulate	/man/2/ether
+manipulate	/man/2/ip
+manipulate	/man/3/ip
+manipulate	/man/3/plap
+manipulate	/man/3/sd
+manipulate	/man/9/button
+manipulate	/man/9/canvas
+manipulate	/man/9/checkbutton
+manipulate	/man/9/choicebutton
+manipulate	/man/9/entry
+manipulate	/man/9/frame
+manipulate	/man/9/image
+manipulate	/man/9/label
+manipulate	/man/9/listbox
+manipulate	/man/9/menu
+manipulate	/man/9/menubutton
+manipulate	/man/9/radiobutton
+manipulate	/man/9/scale
+manipulate	/man/9/scrollbar
+manipulate	/man/9/text
+manipulated	/man/2/bufio
+manipulated	/man/2/styxservers
+manipulated	/man/5/0intro
+manipulated	/man/6/colour
+manipulated	/man/9/canvas
+manipulated	/man/9/label
+manipulated	/man/9/text
+manipulates	/man/10/2c
+manipulates	/man/9/canvas
+manipulating	/man/2/draw-0intro
+manipulating	/man/2/sets
+manipulating	/man/2/styx
+manipulating	/man/9/listbox
+manipulating	/man/9/scrollbar
+manipulation	/man/1/sh-string
+manipulation	/man/2/alphabet-intro
+manipulation	/man/2/ether
+manipulation	/man/2/keyring-ipint
+manipulation	/man/2/names
+manipulation	/man/2/spree-cardlib
+manipulation	/man/2/sys-0intro
+manipulation	/man/3/draw
+manipulation	/man/7/db
+manipulations	/man/1/limbo
+manner	/man/1/deb
+manner	/man/10/ar
+manner	/man/10/getfields
+manner	/man/10/qio
+manner	/man/2/asn1
+manner	/man/2/debug
+manner	/man/2/itslib
+manner	/man/2/spree
+manner	/man/8/prep
+manual	/man/1/0intro
+manual	/man/1/charon
+manual	/man/1/chgrp
+manual	/man/1/man
+manual	/man/1/mash
+manual	/man/1/secstore
+manual	/man/1/sh-tk
+manual	/man/1/wm-misc
+manual	/man/1/yacc
+manual	/man/10/0intro
+manual	/man/10/2a
+manual	/man/10/9load
+manual	/man/10/acid
+manual	/man/10/intrenable
+manual	/man/10/odbc
+manual	/man/10/sleep
+manual	/man/2/0intro
+manual	/man/2/convcs
+manual	/man/2/draw-example
+manual	/man/2/filter
+manual	/man/2/prefab-0intro
+manual	/man/2/spree
+manual	/man/2/styx
+manual	/man/2/styxservers
+manual	/man/2/tk
+manual	/man/3/fs
+manual	/man/3/ip
+manual	/man/4/factotum
+manual	/man/4/namespace
+manual	/man/6/man
+manual	/man/6/ndb
+manual	/man/6/utf
+manual	/man/7/0intro
+manual	/man/8/0intro
+manual	/man/9/0intro
+manual	/man/9/canvas
+manual	/man/9/menubutton
+manual	/man/9/options
+manual	/man/9/text
+manual	/man/9/types
+manufacture	/man/1/mux
+manufacture	/man/8/manufacture
+manufacture	/man/8/register
+manufacture.b	/man/8/manufacture
+manufacturer	/man/3/flash
+manufacturer	/man/3/i2c
+manufacturer	/man/3/logfs
+manufacturer	/man/3/pnp
+manufacturer	/man/3/sd
+manufacturer	/man/8/manufacture
+manufacturing	/man/3/rtc
+map	/man/1/dd
+map	/man/1/emu
+map	/man/1/sh-tk
+map	/man/1/stack
+map	/man/10/c2l
+map	/man/10/kbdputc
+map	/man/2/asn1
+map	/man/2/dbm
+map	/man/2/debug
+map	/man/2/dis
+map	/man/2/draw-0intro
+map	/man/2/draw-display
+map	/man/2/imagefile
+map	/man/2/lists
+map	/man/2/print
+map	/man/2/rfc822
+map	/man/3/draw
+map	/man/3/fs
+map	/man/3/ip
+map	/man/3/vga
+map	/man/6/colour
+map	/man/6/dis
+map	/man/8/collabsrv
+map	/man/8/cs
+map	/man/9/bind
+mapped	/man/1/tr
+mapped	/man/10/c2l
+mapped	/man/10/inb
+mapped	/man/10/plan9.ini
+mapped	/man/2/convcs
+mapped	/man/2/draw-0intro
+mapped	/man/2/draw-display
+mapped	/man/2/ip
+mapped	/man/2/sys-0intro
+mapped	/man/2/w3c-css
+mapped	/man/3/pnp
+mapped	/man/3/tls
+mapped	/man/6/font
+mapped	/man/6/image
+mapped	/man/9/bind
+mapping	/man/1/cook
+mapping	/man/1/dd
+mapping	/man/1/miniterm
+mapping	/man/2/alphabet-intro
+mapping	/man/2/convcs
+mapping	/man/2/draw-0intro
+mapping	/man/2/spree-cardlib
+mapping	/man/2/styxservers
+mapping	/man/3/fs
+mapping	/man/3/ftl
+mapping	/man/3/ip
+mapping	/man/3/logfs
+mapping	/man/4/kfs
+mapping	/man/6/colour
+mapping	/man/6/ndb
+mapping	/man/6/utf
+mappings	/man/1/miniterm
+maps	/man/10/9load
+maps	/man/2/palmfile
+maps	/man/2/print
+maps	/man/3/fs
+maps	/man/3/ip
+maps	/man/6/colour
+maps	/man/6/image
+maps	/man/8/collabsrv
+maps	/man/8/touchcal
+mapsize	/man/6/dis
+margin	/man/6/man
+margin	/man/9/text
+marginally	/man/10/plan9.ini
+mark	/man/1/acme
+mark	/man/1/bind
+mark	/man/1/cprof
+mark	/man/1/limbo
+mark	/man/1/wm-misc
+mark	/man/1/xd
+mark	/man/10/devattach
+mark	/man/10/qio
+mark	/man/2/asn1
+mark	/man/2/dhcpclient
+mark	/man/2/draw-context
+mark	/man/2/factotum
+mark	/man/2/keyring-getmsg
+mark	/man/2/msgio
+mark	/man/2/sexprs
+mark	/man/2/styxservers
+mark	/man/2/sys-byte2char
+mark	/man/2/wmsrv
+mark	/man/2/xml
+mark	/man/3/cons
+mark	/man/3/dbg
+mark	/man/3/eia
+mark	/man/3/indir
+mark	/man/3/pipe
+mark	/man/3/prog
+mark	/man/3/touch
+mark	/man/4/acme
+mark	/man/8/prep
+mark	/man/9/0intro
+mark	/man/9/panel
+mark	/man/9/text
+mark.str	/man/2/xml
+marker	/man/2/asn1
+marker	/man/9/menu
+markers	/man/9/text
+marking	/man/4/acme
+markname	/man/9/text
+marks	/man/1/mash
+marks	/man/1/sh
+marks	/man/1/sh-file2chan
+marks	/man/10/dynld
+marks	/man/10/qio
+marks	/man/2/dis
+marks	/man/2/string
+marks	/man/2/xml
+marks	/man/6/keytext
+marks	/man/6/man
+marks	/man/8/prep
+marks	/man/9/scale
+marks	/man/9/scrollbar
+marks	/man/9/text
+markup	/man/1/charon
+markup	/man/1/man
+markup	/man/2/xml
+maroon	/man/9/types
+marshal	/man/2/format
+marshaled	/man/4/factotum
+marshals	/man/1/grid-register
+mash	/man/1/0intro
+mash	/man/1/mash
+mash	/man/1/mash-make
+mash	/man/1/mash-tk
+mash	/man/1/wm-sh
+mash	/man/2/arg
+mash	/man/2/command
+mash.b	/man/1/wm-sh
+mash.dis	/man/1/acme
+mash.m	/man/1/mash
+mashbuiltin	/man/1/mash
+mashcmd	/man/1/mash
+mashfile	/man/1/mash-make
+mashinit	/man/1/mash
+mask	/man/2/dhcpclient
+mask	/man/2/dis
+mask	/man/2/draw-example
+mask	/man/2/draw-image
+mask	/man/2/ip
+mask	/man/2/math-fp
+mask	/man/2/palmfile
+mask	/man/2/prefab-compound
+mask	/man/2/prefab-element
+mask	/man/2/tk
+mask	/man/3/cons
+mask	/man/3/draw
+mask	/man/3/ip
+mask	/man/6/dis
+mask	/man/6/ndb
+mask	/man/8/bootpd
+mask	/man/8/dhcp
+mask	/man/8/rdbgsrv
+mask	/man/8/register
+mask	/man/8/signer
+mask	/man/9/image
+mask	/man/9/panel
+mask	/man/9/types
+maskable	/man/10/splhi
+masked	/man/1/mkdir
+masked	/man/2/ip
+masked	/man/4/9srvfs
+maskfile	/man/9/image
+maskid	/man/3/draw
+masking	/man/2/draw-0intro
+maskp	/man/3/draw
+masks	/man/10/intrenable
+masks	/man/2/dis
+masks	/man/2/draw-display
+masks	/man/2/draw-image
+masks	/man/2/ip
+masks	/man/2/math-fp
+masks	/man/6/ndb
+masktext	/man/2/ip
+mason	/man/6/attrdb
+mass	/man/1/units
+masse	/man/1/acme
+massey	/man/1/idea
+master	/man/1/blur
+master	/man/1/mk
+master	/man/10/9load
+master	/man/10/master
+master	/man/10/mk
+master	/man/2/disks
+master	/man/4/keyfs
+master	/man/8/prep
+master	/man/9/grid
+master	/man/9/pack
+master.local	/man/10/master
+masters	/man/9/grid
+masters	/man/9/pack
+match	/man/1/acme
+match	/man/1/alphabet-fs
+match	/man/1/auplay
+match	/man/1/fs
+match	/man/1/grep
+match	/man/1/grid-query
+match	/man/1/look
+match	/man/1/m4
+match	/man/1/man
+match	/man/1/mash
+match	/man/1/mash-make
+match	/man/1/mk
+match	/man/1/sh-regex
+match	/man/1/sh-std
+match	/man/1/wm-sh
+match	/man/10/dynld
+match	/man/10/mk
+match	/man/10/plan9.ini
+match	/man/2/asn1
+match	/man/2/complete
+match	/man/2/filepat
+match	/man/2/filter
+match	/man/2/keyring-0intro
+match	/man/2/popup
+match	/man/2/regex
+match	/man/2/rfc822
+match	/man/2/security-0intro
+match	/man/2/selectfile
+match	/man/2/spree-allow
+match	/man/2/stringinttab
+match	/man/2/sys-print
+match	/man/2/wmsrv
+match	/man/3/dynld
+match	/man/4/factotum
+match	/man/4/keysrv
+match	/man/4/registry
+match	/man/6/plumbing
+match	/man/6/regexp
+match	/man/8/applylog
+match	/man/8/getauthinfo
+match	/man/8/svc
+match	/man/9/bind
+match	/man/9/canvas
+match	/man/9/grid
+match	/man/9/text
+matched	/man/1/acme
+matched	/man/1/mash
+matched	/man/1/mash-make
+matched	/man/1/mk
+matched	/man/1/sh
+matched	/man/1/sh-regex
+matched	/man/10/mk
+matched	/man/2/complete
+matched	/man/2/keyring-0intro
+matched	/man/2/keyset
+matched	/man/2/regex
+matched	/man/2/registries
+matched	/man/2/sh
+matched	/man/2/spree-allow
+matched	/man/6/regexp
+matches	/man/1/acme
+matches	/man/1/alphabet-fs
+matches	/man/1/fs
+matches	/man/1/grep
+matches	/man/1/look
+matches	/man/1/mash
+matches	/man/1/mash-make
+matches	/man/1/mk
+matches	/man/1/sh
+matches	/man/1/sh-arg
+matches	/man/1/sh-regex
+matches	/man/1/sh-std
+matches	/man/1/stack
+matches	/man/10/mk
+matches	/man/2/cfg
+matches	/man/2/draw-display
+matches	/man/2/exception
+matches	/man/2/factotum
+matches	/man/2/filepat
+matches	/man/2/prefab-element
+matches	/man/2/regex
+matches	/man/2/registries
+matches	/man/2/spree-allow
+matches	/man/4/keysrv
+matches	/man/4/registry
+matches	/man/6/plumbing
+matches	/man/6/regexp
+matches	/man/8/bootpd
+matches	/man/8/cs
+matches	/man/9/bind
+matches	/man/9/text
+matching	/man/1/acme
+matching	/man/1/brutus
+matching	/man/1/cat
+matching	/man/1/filename
+matching	/man/1/grep
+matching	/man/1/look
+matching	/man/1/m4
+matching	/man/1/man
+matching	/man/1/mash
+matching	/man/1/mash-make
+matching	/man/1/sh
+matching	/man/1/sh-arg
+matching	/man/1/sh-regex
+matching	/man/1/sh-std
+matching	/man/1/sh-string
+matching	/man/1/wm-sh
+matching	/man/10/devattach
+matching	/man/10/intrenable
+matching	/man/2/0intro
+matching	/man/2/cfg
+matching	/man/2/filepat
+matching	/man/2/keyset
+matching	/man/2/spree-allow
+matching	/man/2/stringinttab
+matching	/man/2/styx
+matching	/man/3/ip
+matching	/man/4/factotum
+matching	/man/5/0intro
+matching	/man/8/cs
+matching	/man/9/canvas
+matching	/man/9/text
+mate	/man/8/plumber
+math	/man/1/calc
+math	/man/1/cprof
+math	/man/1/fc
+math	/man/1/math-misc
+math	/man/1/mprof
+math	/man/1/prof
+math	/man/2/0intro
+math	/man/2/geodesy
+math	/man/2/math-0intro
+math	/man/2/math-elem
+math	/man/2/math-export
+math	/man/2/math-fp
+math	/man/2/math-linalg
+math.c	/man/2/math-0intro
+math.c	/man/2/math-elem
+math.c	/man/2/math-export
+math.c	/man/2/math-fp
+math.c	/man/2/math-linalg
+math.m	/man/2/math-0intro
+math.m	/man/2/math-elem
+math.m	/man/2/math-export
+math.m	/man/2/math-fp
+math.m	/man/2/math-linalg
+mathematical	/man/1/math-misc
+mathematical	/man/1/unicode
+mathematical	/man/2/security-0intro
+mathematical	/man/6/keyboard
+mathematics	/man/2/math-0intro
+mathematics	/man/2/math-elem
+maths	/man/10/c2l
+matrices	/man/2/math-linalg
+matrix	/man/2/ida
+matrix	/man/2/math-linalg
+matrix	/man/8/touchcal
+matte	/man/2/draw-display
+matte	/man/2/draw-image
+mattes	/man/2/draw-display
+max	/man/1/calc
+max	/man/1/wm
+max	/man/2/draw-0intro
+max	/man/2/draw-rect
+max	/man/2/ip
+max	/man/3/draw
+max.add	/man/2/draw-rect
+max.sub	/man/2/draw-rect
+max.x	/man/2/draw-rect
+max.x	/man/3/draw
+max.y	/man/2/draw-rect
+max.y	/man/3/draw
+maxargs	/man/10/getfields
+maxbits	/man/2/keyring-ipint
+maxblock	/man/2/diskblocks
+maxcards	/man/2/spree-cardlib
+maxdata	/man/2/plumbmsg
+maxdis	/man/2/dis
+maxfilesize	/man/2/secstore
+maximal	/man/1/mk
+maximal	/man/1/tr
+maximal	/man/1/wc
+maximal	/man/10/mk
+maximal	/man/10/print
+maximal	/man/2/convcs
+maximal	/man/2/math-linalg
+maximal	/man/2/string
+maximal	/man/2/sys-tokenize
+maximal	/man/3/tls
+maximise	/man/1/0intro
+maximum	/man/1/calc
+maximum	/man/1/charon
+maximum	/man/1/emu
+maximum	/man/1/grid-monitor
+maximum	/man/1/grid-register
+maximum	/man/1/wm
+maximum	/man/1/wm-misc
+maximum	/man/10/odbc
+maximum	/man/10/plan9.ini
+maximum	/man/10/print
+maximum	/man/10/qio
+maximum	/man/10/rune
+maximum	/man/2/dhcpclient
+maximum	/man/2/factotum
+maximum	/man/2/keyring-0intro
+maximum	/man/2/math-fp
+maximum	/man/2/palmfile
+maximum	/man/2/rfc822
+maximum	/man/2/spree-gather
+maximum	/man/2/styx
+maximum	/man/2/sys-fversion
+maximum	/man/2/sys-iounit
+maximum	/man/2/sys-print
+maximum	/man/2/sys-stat
+maximum	/man/3/env
+maximum	/man/3/ip
+maximum	/man/3/mnt
+maximum	/man/3/pipe
+maximum	/man/3/sd
+maximum	/man/3/tls
+maximum	/man/3/usb
+maximum	/man/4/dbfs
+maximum	/man/4/logfile
+maximum	/man/4/memfs
+maximum	/man/5/0intro
+maximum	/man/5/open
+maximum	/man/5/read
+maximum	/man/5/stat
+maximum	/man/5/version
+maximum	/man/5/walk
+maximum	/man/6/colour
+maximum	/man/8/createsignerkey
+maximum	/man/8/prep
+maximum	/man/9/canvas
+maximun	/man/4/factotum
+maxmem	/man/10/plan9.ini
+maxmembers	/man/2/spree-gather
+maxn	/man/2/venti
+maxpkt	/man/3/usb
+maxraint	/man/2/ip
+maxrequest	/man/2/rfc822
+maxrpc	/man/2/styx
+maxsd53c8xx	/man/10/plan9.ini
+maxsize	/man/1/emu
+maxsize	/man/10/dynld
+maxsize	/man/2/secstore
+maxusers	/man/1/grid-monitor
+maxusers	/man/1/grid-register
+maxwelem	/man/10/styx
+maxwelem	/man/2/styx
+maxwelem	/man/5/walk
+maxx	/man/1/wm
+maxx	/man/2/tk
+maxx	/man/3/mpeg
+maxx	/man/3/tv
+maxx	/man/9/panel
+maxy	/man/1/wm
+maxy	/man/2/tk
+maxy	/man/3/mpeg
+maxy	/man/3/tv
+maxy	/man/9/panel
+maybe	/man/1/tktester
+mb	/man/1/tktester
+mbefore	/man/2/sys-bind
+mbefore	/man/6/namespace
+mbr	/man/10/9load
+mbr	/man/8/prep
+mbrfile	/man/8/prep
+mbrs	/man/10/9load
+mbyte	/man/10/dmainit
+mbyte	/man/8/mangaload
+mbytes	/man/8/mangaload
+mc	/man/1/ls
+mc	/man/1/mc
+mc.b	/man/1/mc
+mc68000	/man/10/2c
+mc68020	/man/10/2c
+mc680x0	/man/10/a.out
+mcreate	/man/2/sys-bind
+mcreate	/man/3/fs
+mcreate	/man/6/namespace
+md4	/man/2/keyring-0intro
+md4	/man/2/keyring-sha1
+md4	/man/3/ssl
+md4.c	/man/2/keyring-sha1
+md4len	/man/2/keyring-sha1
+md5	/man/1/alphabet-main
+md5	/man/1/crypt
+md5	/man/1/sum
+md5	/man/2/keyring-0intro
+md5	/man/2/keyring-sha1
+md5	/man/2/security-0intro
+md5	/man/2/spki
+md5	/man/3/dynld
+md5	/man/3/ssl
+md5	/man/3/tls
+md5	/man/6/keytext
+md5	/man/8/ai2key
+md5	/man/8/applylog
+md5.c	/man/2/keyring-sha1
+md5len	/man/2/keyring-sha1
+md5sum	/man/1/sum
+md5sum.b	/man/1/sum
+mday	/man/2/daytime
+mdb	/man/1/mdb
+mdb.b	/man/1/mdb
+mean	/man/1/date
+mean	/man/1/grid-ns
+mean	/man/1/tiny
+mean	/man/10/print
+mean	/man/2/daytime
+mean	/man/2/exception
+mean	/man/2/sys-print
+mean	/man/4/logfile
+mean	/man/5/open
+mean	/man/5/stat
+meaningful	/man/1/charon
+meaningful	/man/1/tktester
+meaningful	/man/2/wmsrv
+meaningless	/man/1/acme
+meanings	/man/1/ar
+meanings	/man/10/iar
+meant	/man/1/cpu
+meant	/man/6/0intro
+meantime	/man/2/styxpersist
+meanwhile	/man/1/pwd
+meanwhile	/man/10/qio
+measure	/man/1/sh-string
+measure	/man/1/time
+measure	/man/4/iostats
+measured	/man/1/tail
+measured	/man/2/disks
+measured	/man/2/draw-image
+measured	/man/2/geodesy
+measured	/man/2/sys-stat
+measured	/man/2/timers
+measured	/man/5/stat
+measured	/man/9/canvas
+measured	/man/9/text
+measurements	/man/9/types
+measuring	/man/2/sys-millisec
+mechanism	/man/1/tiny
+mechanism	/man/1/wm
+mechanism	/man/10/2c
+mechanism	/man/10/kproc
+mechanism	/man/10/qio
+mechanism	/man/10/sleep
+mechanism	/man/10/styxserver
+mechanism	/man/2/asn1
+mechanism	/man/2/bufio
+mechanism	/man/2/dividers
+mechanism	/man/2/prefab-element
+mechanism	/man/2/sys-open
+mechanism	/man/2/sys-self
+mechanism	/man/3/ip
+mechanism	/man/3/pipe
+mechanism	/man/3/pnp
+mechanism	/man/3/prog
+mechanism	/man/8/register
+mechanism	/man/8/signer
+mechanism	/man/9/entry
+mechanisms	/man/1/tiny
+mechanisms	/man/10/lock
+mechanisms	/man/2/0intro
+mechanisms	/man/2/sys-0intro
+mechanisms	/man/3/ip
+mechanisms	/man/3/mnt
+mechanisms	/man/9/canvas
+medblue	/man/2/draw-display
+medgreen	/man/2/draw-display
+media	/man/10/plan9.ini
+media	/man/2/json
+media	/man/2/rfc822
+media	/man/2/scsiio
+media	/man/2/w3c-css
+media	/man/3/ip
+media	/man/3/sd
+media	/man/5/0intro
+media	/man/6/json
+mediate	/man/2/wmsrv
+mediated	/man/2/spree
+mediates	/man/3/pipe
+medium	/man/10/9load
+medium	/man/2/spree-objstore
+medium	/man/2/w3c-css
+medium	/man/3/ip
+medium	/man/3/logfs
+medium.c	/man/3/ip
+mediums	/man/1/uuencode
+meet	/man/9/canvas
+meet	/man/9/grid
+meet	/man/9/pack
+meets	/man/9/canvas
+megabyte	/man/8/httpd
+megabytes	/man/1/wm-misc
+megabytes	/man/10/plan9.ini
+mem	/man/10/acid
+mem	/man/10/memory
+mem	/man/10/plan9.ini
+mem	/man/2/json
+mem	/man/3/boot
+mem	/man/3/i82365
+mem.h	/man/10/0intro
+mem.h	/man/10/seconds
+member	/man/1/acme
+member	/man/1/chgrp
+member	/man/1/mk
+member	/man/1/spree-join
+member	/man/1/yacc
+member	/man/10/ar
+member	/man/10/c2l
+member	/man/10/mk
+member	/man/10/styxserver
+member	/man/2/alphabet-intro
+member	/man/2/asn1
+member	/man/2/bloomfilter
+member	/man/2/daytime
+member	/man/2/dial
+member	/man/2/dis
+member	/man/2/draw-0intro
+member	/man/2/draw-display
+member	/man/2/draw-pointer
+member	/man/2/format
+member	/man/2/json
+member	/man/2/plumbmsg
+member	/man/2/prefab-0intro
+member	/man/2/prefab-compound
+member	/man/2/prefab-element
+member	/man/2/sets
+member	/man/2/spree
+member	/man/2/spree-allow
+member	/man/2/spree-cardlib
+member	/man/2/spree-gather
+member	/man/2/styx
+member	/man/2/styxservers
+member	/man/2/sys-dial
+member	/man/2/sys-pctl
+member	/man/2/sys-stat
+member	/man/2/xml
+member	/man/3/logfs
+member	/man/3/plap
+member	/man/4/spree
+member	/man/5/0intro
+member	/man/5/read
+member	/man/5/stat
+member	/man/6/dis
+member	/man/6/json
+member	/man/6/regexp
+member	/man/6/sbl
+member	/man/6/users
+member's	/man/2/spree
+member's	/man/2/spree-allow
+member.id	/man/2/spree
+membername	/man/10/mk
+membernamed	/man/2/spree
+membership	/man/2/bloomfilter
+membership	/man/2/prefab-element
+membership	/man/2/sets
+membership	/man/4/kfs
+memccpy	/man/10/memory
+memchr	/man/10/memory
+memcmp	/man/10/memory
+memcpy	/man/10/memory
+memfile	/man/1/sh-file2chan
+memfs	/man/4/dbfs
+memfs	/man/4/memfs
+memfs	/man/4/ramfile
+memfs.b	/man/4/memfs
+memmove	/man/10/memory
+memories	/man/10/plan9.ini
+memories	/man/3/tinyfs
+memory	/man/1/0intro
+memory	/man/1/charon
+memory	/man/1/deb
+memory	/man/1/ebook
+memory	/man/1/emu
+memory	/man/1/grid-register
+memory	/man/1/mprof
+memory	/man/1/ps
+memory	/man/1/sh-file2chan
+memory	/man/1/sort
+memory	/man/1/wm-misc
+memory	/man/10/9load
+memory	/man/10/a.out
+memory	/man/10/acid
+memory	/man/10/allocb
+memory	/man/10/dev
+memory	/man/10/dmainit
+memory	/man/10/error
+memory	/man/10/inb
+memory	/man/10/malloc
+memory	/man/10/memory
+memory	/man/10/newchan
+memory	/man/10/plan9.ini
+memory	/man/10/sleep
+memory	/man/10/strcat
+memory	/man/10/styxserver
+memory	/man/10/xalloc
+memory	/man/2/0intro
+memory	/man/2/dhcpclient
+memory	/man/2/dis
+memory	/man/2/draw-display
+memory	/man/2/draw-image
+memory	/man/2/imagefile
+memory	/man/2/math-0intro
+memory	/man/2/palmfile
+memory	/man/2/prof
+memory	/man/2/secstore
+memory	/man/2/sys-0intro
+memory	/man/2/sys-read
+memory	/man/3/boot
+memory	/man/3/cons
+memory	/man/3/dbg
+memory	/man/3/flash
+memory	/man/3/ftl
+memory	/man/3/i82365
+memory	/man/3/logfs
+memory	/man/3/pnp
+memory	/man/3/prof
+memory	/man/3/prog
+memory	/man/3/rtc
+memory	/man/3/vga
+memory	/man/4/kfs
+memory	/man/4/logfile
+memory	/man/4/memfs
+memory	/man/4/ramfile
+memory	/man/6/colour
+memory	/man/6/dis
+memory	/man/7/db
+memory	/man/8/0intro
+memory	/man/8/ftl
+memory	/man/8/init
+memory	/man/8/kfscmd
+memory	/man/8/rdbgsrv
+memory	/man/8/register
+memory.b	/man/1/wm-misc
+memset	/man/10/memory
+memshow	/man/2/prof
+memsize	/man/6/dis
+memstart	/man/2/prof
+memstats	/man/2/prof
+mention	/man/10/2l
+mentioned	/man/1/ar
+mentioned	/man/1/sendmail
+mentioned	/man/1/sh
+mentioned	/man/10/2l
+mentioned	/man/10/conf
+mentioned	/man/10/iar
+mentioned	/man/2/draw-image
+mentioned	/man/2/fsproto
+mentioned	/man/2/spki
+mentioned	/man/2/styx
+mentioned	/man/3/srv9
+mentioned	/man/4/acme
+mentioned	/man/6/ndb
+menu	/man/1/acme
+menu	/man/1/brutus
+menu	/man/1/collab-clients
+menu	/man/1/cprof
+menu	/man/1/deb
+menu	/man/1/ebook
+menu	/man/1/ftree
+menu	/man/1/grid-session
+menu	/man/1/mash-tk
+menu	/man/1/mux
+menu	/man/1/tktester
+menu	/man/1/toolbar
+menu	/man/1/wm-misc
+menu	/man/1/wm-sh
+menu	/man/10/9load
+menu	/man/10/plan9.ini
+menu	/man/2/draw-0intro
+menu	/man/2/ir
+menu	/man/2/popup
+menu	/man/2/prefab-element
+menu	/man/2/spree-cardlib
+menu	/man/2/tk
+menu	/man/9/0intro
+menu	/man/9/choicebutton
+menu	/man/9/menu
+menu	/man/9/menubutton
+menu	/man/9/options
+menu's	/man/9/menu
+menubutton	/man/1/tktester
+menubutton	/man/9/0intro
+menubutton	/man/9/menubutton
+menubutton's	/man/9/menubutton
+menubuttons	/man/9/menubutton
+menuconsole	/man/10/plan9.ini
+menudefault	/man/10/plan9.ini
+menuentry	/man/2/spree-cardlib
+menuitem	/man/10/plan9.ini
+menus	/man/1/deb
+menus	/man/1/mash-tk
+menus	/man/1/tktester
+menus	/man/1/wm-misc
+menus	/man/2/prefab-element
+menus	/man/2/wmsrv
+menus	/man/6/translate
+menus	/man/9/menu
+mercator	/man/2/geodesy
+merchantability	/man/9/1copyright
+merely	/man/2/spree
+merely	/man/8/ai2key
+merge	/man/1/alphabet-fs
+merge	/man/1/fs
+merge	/man/1/sh-string
+merge	/man/9/grid
+merged	/man/2/spree
+merging	/man/1/alphabet-fs
+merging	/man/1/fs
+mersenne	/man/1/math-misc
+mersenne.b	/man/1/math-misc
+mesg	/man/1/mash-tk
+mess	/man/9/bind
+message	/man/1/bind
+message	/man/1/fc
+message	/man/1/kill
+message	/man/1/listen
+message	/man/1/mash-tk
+message	/man/1/plumb
+message	/man/1/sendmail
+message	/man/1/sh
+message	/man/1/sh-arg
+message	/man/1/sh-file2chan
+message	/man/1/sh-std
+message	/man/1/sh-test
+message	/man/1/sum
+message	/man/1/tiny
+message	/man/1/uuencode
+message	/man/1/wm
+message	/man/10/error
+message	/man/10/lock
+message	/man/10/odbc
+message	/man/10/panic
+message	/man/10/print
+message	/man/10/qio
+message	/man/10/styx
+message	/man/10/styxserver
+message	/man/2/arg
+message	/man/2/debug
+message	/man/2/dhcpclient
+message	/man/2/dialog
+message	/man/2/factotum
+message	/man/2/filter
+message	/man/2/filter-deflate
+message	/man/2/filter-slip
+message	/man/2/ida
+message	/man/2/itslib
+message	/man/2/keyring-0intro
+message	/man/2/keyring-auth
+message	/man/2/keyring-getmsg
+message	/man/2/keyring-sha1
+message	/man/2/msgio
+message	/man/2/plumbmsg
+message	/man/2/pop3
+message	/man/2/prof
+message	/man/2/security-0intro
+message	/man/2/security-auth
+message	/man/2/sh
+message	/man/2/smtp
+message	/man/2/spree
+message	/man/2/styx
+message	/man/2/styxconv
+message	/man/2/styxflush
+message	/man/2/styxservers
+message	/man/2/sys-0intro
+message	/man/2/sys-file2chan
+message	/man/2/sys-fversion
+message	/man/2/sys-open
+message	/man/2/sys-pctl
+message	/man/2/timers
+message	/man/2/wait
+message	/man/2/wmsrv
+message	/man/3/dbg
+message	/man/3/draw
+message	/man/3/ds
+message	/man/3/ftl
+message	/man/3/gpio
+message	/man/3/ip
+message	/man/3/kprof
+message	/man/3/mnt
+message	/man/3/prog
+message	/man/3/ssl
+message	/man/3/tls
+message	/man/3/usb
+message	/man/3/vga
+message	/man/4/acme
+message	/man/4/factotum
+message	/man/4/import
+message	/man/4/iostats
+message	/man/4/palmsrv
+message	/man/4/spree
+message	/man/5/0intro
+message	/man/5/attach
+message	/man/5/clunk
+message	/man/5/error
+message	/man/5/flush
+message	/man/5/open
+message	/man/5/read
+message	/man/5/stat
+message	/man/5/version
+message	/man/5/walk
+message	/man/6/image
+message	/man/6/login
+message	/man/6/plumbing
+message	/man/7/db
+message	/man/8/bootpd
+message	/man/8/collabsrv
+message	/man/8/ping
+message	/man/8/plumber
+message	/man/8/prep
+message	/man/8/rdbgsrv
+message	/man/8/styxchat
+message	/man/9/send
+message's	/man/2/keyring-getmsg
+message's	/man/2/msgio
+message's	/man/8/plumber
+messages	/man/1/brutus
+messages	/man/1/collab-clients
+messages	/man/1/grid-monitor
+messages	/man/1/grid-register
+messages	/man/1/itest
+messages	/man/1/limbo
+messages	/man/1/listen
+messages	/man/1/sendmail
+messages	/man/1/sh
+messages	/man/1/sh-alphabet
+messages	/man/1/sh-test
+messages	/man/1/tktester
+messages	/man/1/vacget
+messages	/man/1/wm
+messages	/man/1/wm-misc
+messages	/man/1/yacc
+messages	/man/10/2c
+messages	/man/10/c2l
+messages	/man/10/error
+messages	/man/10/kproc
+messages	/man/10/plan9.ini
+messages	/man/10/styx
+messages	/man/2/devpointer
+messages	/man/2/dial
+messages	/man/2/disks
+messages	/man/2/draw-context
+messages	/man/2/factotum
+messages	/man/2/filter
+messages	/man/2/filter-slip
+messages	/man/2/itslib
+messages	/man/2/keyring-0intro
+messages	/man/2/keyring-getmsg
+messages	/man/2/keyring-getstring
+messages	/man/2/msgio
+messages	/man/2/plumbmsg
+messages	/man/2/pop3
+messages	/man/2/scsiio
+messages	/man/2/security-0intro
+messages	/man/2/smtp
+messages	/man/2/spree
+messages	/man/2/styx
+messages	/man/2/styxconv
+messages	/man/2/styxflush
+messages	/man/2/styxservers
+messages	/man/2/sys-bind
+messages	/man/2/sys-dial
+messages	/man/2/sys-file2chan
+messages	/man/2/sys-pipe
+messages	/man/2/tabs
+messages	/man/2/tkclient
+messages	/man/2/translate
+messages	/man/2/wmclient
+messages	/man/3/0intro
+messages	/man/3/cons
+messages	/man/3/dbg
+messages	/man/3/draw
+messages	/man/3/ds
+messages	/man/3/floppy
+messages	/man/3/ftl
+messages	/man/3/i2c
+messages	/man/3/ip
+messages	/man/3/logfs
+messages	/man/3/mnt
+messages	/man/3/plap
+messages	/man/3/prof
+messages	/man/3/prog
+messages	/man/3/srv
+messages	/man/3/ssl
+messages	/man/3/tls
+messages	/man/4/acme
+messages	/man/4/dossrv
+messages	/man/4/factotum
+messages	/man/4/ftpfs
+messages	/man/4/iostats
+messages	/man/4/spree
+messages	/man/4/vacfs
+messages	/man/5/0intro
+messages	/man/5/attach
+messages	/man/5/error
+messages	/man/5/flush
+messages	/man/5/open
+messages	/man/5/read
+messages	/man/5/remove
+messages	/man/5/stat
+messages	/man/5/version
+messages	/man/5/walk
+messages	/man/6/plumbing
+messages	/man/8/collabsrv
+messages	/man/8/dhcp
+messages	/man/8/ping
+messages	/man/8/plumber
+messages	/man/8/rdbgsrv
+messages	/man/8/styxchat
+messages	/man/8/styxmon
+messages	/man/9/send
+messagesize	/man/8/styxchat
+messaging	/man/8/collabsrv
+meta	/man/1/mash
+meta	/man/1/mk
+meta	/man/10/mk
+meta	/man/2/dial
+meta	/man/2/sys-dial
+meta	/man/6/keyboard
+metacharacter	/man/6/regexp
+metacharacters	/man/1/fc
+metacharacters	/man/1/sh-expr
+metacharacters	/man/1/sh-regex
+metacharacters	/man/6/regexp
+metadata	/man/8/applylog
+metaprotocol	/man/4/factotum
+meth	/man/8/httpd
+method	/man/1/deb
+method	/man/1/diff
+method	/man/1/fs
+method	/man/10/0intro
+method	/man/10/5cv
+method	/man/2/bloomfilter
+method	/man/2/bufio
+method	/man/2/draw-font
+method	/man/2/registries
+method	/man/2/security-0intro
+method	/man/2/sys-0intro
+method	/man/3/draw
+method	/man/3/prog
+method	/man/8/httpd
+methods	/man/2/draw-image
+methods	/man/2/registries
+methods	/man/2/security-0intro
+methods	/man/2/styxservers
+methods	/man/2/wmsrv
+methods	/man/2/xml
+methods	/man/8/httpd
+metre	/man/2/geodesy
+metres	/man/2/geodesy
+metric	/man/1/units
+mfd	/man/2/itslib
+mflag	/man/2/ip
+mg	/man/1/sh-regex
+mga2164w	/man/3/vga
+mga2164whwgc	/man/3/vga
+mga4xx	/man/10/plan9.ini
+mhz	/man/3/fpga
+mhz	/man/3/tv
+mhz	/man/8/fpgaload
+mib	/man/3/ip
+mib510	/man/1/avr
+mic	/man/3/audio
+mice	/man/2/draw-0intro
+microdelay	/man/10/delay
+micron	/man/6/keyboard
+microseconds	/man/1/date
+microseconds	/man/10/delay
+microseconds	/man/3/cons
+microseconds	/man/3/kprof
+microseconds	/man/3/rtc
+microseconds	/man/3/touch
+microsoft	/man/3/ip
+microsoft	/man/6/scancode
+microsoft	/man/7/dbsrv
+microsystems	/man/9/0intro
+microsystems	/man/9/1copyright
+mid	/man/2/dis
+middle	/man/2/dis
+middle	/man/2/prefab-element
+middle	/man/2/security-0intro
+middle	/man/3/cons
+middle	/man/6/dis
+middle	/man/9/canvas
+middle	/man/9/entry
+middle	/man/9/listbox
+middle	/man/9/scrollbar
+middle	/man/9/text
+mii	/man/10/plan9.ini
+millimetres	/man/9/types
+millisec	/man/1/time
+millisec	/man/2/daytime
+millisec	/man/2/sys-millisec
+millisec	/man/2/sys-sleep
+millisec	/man/2/timers
+millisecond	/man/10/delay
+millisecond	/man/2/sys-millisec
+millisecond	/man/3/cons
+milliseconds	/man/1/timestamp
+milliseconds	/man/10/delay
+milliseconds	/man/10/kprof
+milliseconds	/man/10/malloc
+milliseconds	/man/10/seconds
+milliseconds	/man/2/sys-millisec
+milliseconds	/man/2/sys-sleep
+milliseconds	/man/2/timers
+milliseconds	/man/3/cmd
+milliseconds	/man/3/cons
+milliseconds	/man/3/eia
+milliseconds	/man/3/ip
+milliseconds	/man/3/pointer
+milliseconds	/man/3/touch
+milliseconds	/man/8/ping
+millliseconds	/man/10/sleep
+mime	/man/1/charon
+mime	/man/2/encoding
+mime	/man/2/keyring-0intro
+mime	/man/2/rfc822
+mime	/man/6/sexprs
+mimefields	/man/2/rfc822
+mimetype	/man/2/rfc822
+mimic	/man/1/ebook
+mimic	/man/2/sys-werrstr
+mimicked	/man/1/tiny
+mimics	/man/1/miniterm
+min	/man/1/calc
+min	/man/2/daytime
+min	/man/2/draw-0intro
+min	/man/2/draw-image
+min	/man/2/draw-rect
+min	/man/2/ip
+min	/man/3/draw
+min.add	/man/2/draw-rect
+min.sub	/man/2/draw-rect
+min.x	/man/2/draw-rect
+min.x	/man/3/draw
+min.y	/man/2/draw-rect
+min.y	/man/3/draw
+minbits	/man/2/keyring-ipint
+minded	/man/1/wm-misc
+mindx	/man/2/wmclient
+mindy	/man/2/wmclient
+mine	/man/1/wm-misc
+mini	/man/1/ebook
+miniaturise	/man/1/sh-tk
+minimal	/man/4/kfs
+minimal	/man/4/namespace
+minimal	/man/8/dns
+minimise	/man/4/dbfs
+minimum	/man/1/emu
+minimum	/man/1/math-misc
+minimum	/man/10/plan9.ini
+minimum	/man/10/print
+minimum	/man/2/dhcpclient
+minimum	/man/2/ida
+minimum	/man/2/math-fp
+minimum	/man/2/sys-print
+minimum	/man/2/timers
+minimum	/man/2/wmclient
+minimum	/man/3/ds
+minimum	/man/3/ip
+minimum	/man/3/logfs
+minimum	/man/4/grid-cpu
+minimum	/man/6/font
+minimum	/man/8/createsignerkey
+minimum	/man/8/ping
+minimum	/man/9/grid
+miniscule	/man/3/tinyfs
+minitel	/man/1/miniterm
+minitel®	/man/1/miniterm
+miniterm	/man/1/miniterm
+minms	/man/2/timers
+minraint	/man/2/ip
+minsize	/man/9/grid
+minus	/man/1/cprof
+minus	/man/1/fc
+minus	/man/1/look
+minus	/man/1/sh-arg
+minus	/man/1/sh-expr
+minus	/man/1/sort
+minus	/man/10/styx
+minus	/man/2/alphabet-intro
+minus	/man/2/arg
+minus	/man/6/sbl
+minus	/man/9/pack
+minus	/man/9/types
+minutes	/man/2/daytime
+minutes	/man/2/geodesy
+minutes	/man/3/prog
+minutes	/man/3/vga
+minutes	/man/5/stat
+minx	/man/1/wm
+minx	/man/2/tk
+minx	/man/3/mpeg
+minx	/man/3/tv
+minx	/man/9/panel
+minx..maxy	/man/1/wm
+miny	/man/1/wm
+miny	/man/2/tk
+miny	/man/3/mpeg
+miny	/man/3/tv
+miny	/man/9/panel
+mips	/man/10/2c
+mips	/man/10/2l
+mips	/man/10/a.out
+mips	/man/10/acid
+mipsco	/man/10/acid
+mirror	/man/2/wmsrv
+mirror	/man/3/ds
+mirrored	/man/3/ds
+mirroring	/man/2/format
+mirroring	/man/3/ds
+misbehaving	/man/2/wmsrv
+misc	/man/1/emu
+misc	/man/1/math-misc
+misc	/man/1/ps
+misc	/man/1/wm-misc
+misc	/man/10/conf
+misc	/man/2/dis
+misc	/man/3/sign
+miscellaneous	/man/1/mash-tk
+miscellaneous	/man/1/math-misc
+miscellaneous	/man/1/sh-std
+miscellaneous	/man/1/unicode
+miscellaneous	/man/1/wm-misc
+miscellaneous	/man/3/cons
+miscellany	/man/1/acme
+mishandled	/man/3/draw
+misinterpreted	/man/2/w3c-uris
+misleading	/man/8/rip
+mismatch	/man/10/2c
+mismatches	/man/2/sys-stat
+missing	/man/1/acme
+missing	/man/1/ar
+missing	/man/1/look
+missing	/man/1/m4
+missing	/man/1/mk
+missing	/man/1/mkdir
+missing	/man/1/tiny
+missing	/man/1/yacc
+missing	/man/10/iar
+missing	/man/10/mk
+missing	/man/10/print
+missing	/man/2/daytime
+missing	/man/2/sys-print
+missing	/man/2/w3c-css
+missing	/man/3/ip
+missing	/man/3/logfs
+missing	/man/4/factotum
+missing	/man/4/logfile
+missing	/man/6/sbl
+missing	/man/6/translate
+missing	/man/8/bootpd
+missing	/man/8/mkfs
+mistake	/man/4/trfs
+mistakes	/man/8/prep
+mix	/man/2/draw-display
+mix	/man/3/audio
+mixed	/man/1/tr
+mixed	/man/2/asn1
+mixed	/man/2/keyring-0intro
+mixing	/man/2/draw-display
+mixture	/man/2/sexprs
+mk	/man/1/disdep
+mk	/man/1/limbo
+mk	/man/1/mk
+mk	/man/10/2c
+mk	/man/10/acid
+mk	/man/10/conf
+mk	/man/10/mk
+mk	/man/2/draw-display
+mk	/man/2/rfc822
+mk	/man/4/namespace
+mk's	/man/1/mk
+mk's	/man/10/mk
+mk48t12	/man/3/rtc
+mkargs	/man/1/mk
+mkargs	/man/10/mk
+mkbutton	/man/2/popup
+mkdevc	/man/10/conf
+mkdevlist	/man/10/conf
+mkdictname	/man/2/translate
+mkdir	/man/1/mkdir
+mkdir	/man/4/ftpfs
+mkdir	/man/4/kfs
+mkdir.b	/man/1/mkdir
+mkext	/man/8/mkfs
+mkext.b	/man/8/mkfs
+mkfile	/man/1/mk
+mkfile	/man/10/2c
+mkfile	/man/10/conf
+mkfile	/man/10/master
+mkfile	/man/10/mk
+mkfile	/man/10/styxserver
+mkfilekey	/man/2/secstore
+mkfiles	/man/1/mk
+mkfiles	/man/10/2c
+mkfiles	/man/10/mk
+mkflags	/man/1/mk
+mkflags	/man/10/mk
+mkfs	/man/1/alphabet-fs
+mkfs	/man/1/fs
+mkfs	/man/2/fsproto
+mkfs	/man/4/archfs
+mkfs	/man/4/kfs
+mkfs	/man/6/proto
+mkfs	/man/8/kfscmd
+mkfs	/man/8/mkfs
+mkfs.b	/man/8/mkfs
+mklist	/man/1/sh-sexprs
+mkppcimage	/man/10/5cv
+mkroot	/man/10/conf
+mkseckey	/man/2/secstore
+mktabs	/man/2/tabs
+mktext	/man/1/sh-sexprs
+mktextlist	/man/1/sh-sexprs
+mload	/man/1/sh-mload
+mload.b	/man/1/sh-mload
+mm	/man/2/spki
+mmu	/man/10/dev
+mnemonic	/man/6/keyboard
+mnt	/man/1/calendar
+mnt	/man/1/listen
+mnt	/man/1/passwd
+mnt	/man/1/secstore
+mnt	/man/10/newchan
+mnt	/man/2/factotum
+mnt	/man/2/registries
+mnt	/man/2/sys-bind
+mnt	/man/3/mnt
+mnt	/man/3/pipe
+mnt	/man/3/srv9
+mnt	/man/4/acme
+mnt	/man/4/archfs
+mnt	/man/4/factotum
+mnt	/man/4/import
+mnt	/man/4/keyfs
+mnt	/man/4/keysrv
+mnt	/man/4/mntgen
+mnt	/man/4/namespace
+mnt	/man/4/registry
+mnt	/man/5/0intro
+mnt	/man/8/changelogin
+mnt	/man/8/svc
+mntdir	/man/1/calendar
+mntdir	/man/1/spree-join
+mntgen	/man/4/mntgen
+mntgen.b	/man/4/mntgen
+mntpt	/man/4/registry
+mntpt	/man/8/rip
+mod	/man/10/ar
+mod	/man/10/conf
+mod	/man/10/xalloc
+mod	/man/2/0intro
+mod	/man/2/dis
+mod	/man/2/draw-image
+mod	/man/2/keyring-auth
+mod	/man/2/keyring-ipint
+mod	/man/2/sh
+mod	/man/2/spree
+mod	/man/2/w3c-xpointers
+mod	/man/6/auth
+mod	/man/6/image
+mod	/man/6/login
+mod	/man/8/ai2key
+mod.b	/man/1/limbo
+mod.code	/man/2/dis
+mod.dis	/man/1/limbo
+mod.types	/man/2/dis
+mode	/man/1/9win
+mode	/man/1/alphabet-fs
+mode	/man/1/ar
+mode	/man/1/chmod
+mode	/man/1/cp
+mode	/man/1/crypt
+mode	/man/1/fs
+mode	/man/1/itest
+mode	/man/1/ls
+mode	/man/1/miniterm
+mode	/man/1/sh-tk
+mode	/man/1/tkcmd
+mode	/man/1/uuencode
+mode	/man/1/wm-sh
+mode	/man/10/9load
+mode	/man/10/ar
+mode	/man/10/dev
+mode	/man/10/devattach
+mode	/man/10/iar
+mode	/man/10/newchan
+mode	/man/10/odbc
+mode	/man/10/plan9.ini
+mode	/man/10/qio
+mode	/man/10/styx
+mode	/man/10/styxserver
+mode	/man/2/bufio
+mode	/man/2/convcs
+mode	/man/2/dbm
+mode	/man/2/dis
+mode	/man/2/disks
+mode	/man/2/exception
+mode	/man/2/filter-deflate
+mode	/man/2/math-0intro
+mode	/man/2/math-fp
+mode	/man/2/palmfile
+mode	/man/2/print
+mode	/man/2/styx
+mode	/man/2/styxservers
+mode	/man/2/sys-open
+mode	/man/2/sys-stat
+mode	/man/3/audio
+mode	/man/3/cons
+mode	/man/3/dup
+mode	/man/3/eia
+mode	/man/3/fpga
+mode	/man/3/i2c
+mode	/man/3/ip
+mode	/man/3/logfs
+mode	/man/3/mpeg
+mode	/man/3/prog
+mode	/man/3/srv
+mode	/man/3/srv9
+mode	/man/3/ssl
+mode	/man/3/tv
+mode	/man/3/usb
+mode	/man/3/vga
+mode	/man/3/vid
+mode	/man/4/9srvfs
+mode	/man/4/ftpfs
+mode	/man/5/0intro
+mode	/man/5/open
+mode	/man/5/stat
+mode	/man/6/dis
+mode	/man/6/keyboard
+mode	/man/6/keys
+mode	/man/8/applylog
+mode	/man/8/createsignerkey
+mode	/man/8/init
+mode	/man/8/kfscmd
+mode	/man/8/prep
+mode	/man/8/styxchat
+mode	/man/9/listbox
+mode	/man/9/text
+model	/man/1/charon
+model	/man/2/ir
+model	/man/2/prefab-element
+model	/man/3/sd
+models	/man/10/plan9.ini
+models	/man/3/ip
+modem	/man/1/miniterm
+modem	/man/10/plan9.ini
+modem	/man/3/eia
+modem	/man/3/ip
+modem	/man/3/plap
+modemctl	/man/1/miniterm
+modeminit	/man/1/miniterm
+modemport	/man/10/plan9.ini
+modern	/man/2/disks
+modern	/man/3/logfs
+modes	/man/1/alphabet-fs
+modes	/man/1/chmod
+modes	/man/1/fs
+modes	/man/1/uuencode
+modes	/man/2/dis
+modes	/man/2/exception
+modes	/man/2/math-fp
+modes	/man/2/print
+modes	/man/2/styxservers
+modes	/man/6/dis
+modes	/man/9/listbox
+modes	/man/9/text
+modf	/man/2/math-fp
+modification	/man/1/alphabet-fs
+modification	/man/1/ar
+modification	/man/1/bind
+modification	/man/1/du
+modification	/man/1/fs
+modification	/man/1/gettar
+modification	/man/1/ls
+modification	/man/1/mash-make
+modification	/man/1/mdb
+modification	/man/1/mk
+modification	/man/1/tktester
+modification	/man/1/touch
+modification	/man/10/ar
+modification	/man/10/devattach
+modification	/man/10/iar
+modification	/man/10/mk
+modification	/man/2/daytime
+modification	/man/2/filter-deflate
+modification	/man/2/palmfile
+modification	/man/2/readdir
+modification	/man/2/sys-bind
+modification	/man/3/pointer
+modification	/man/4/acme
+modification	/man/4/kfs
+modification	/man/5/0intro
+modification	/man/5/stat
+modification	/man/6/namespace
+modification	/man/6/ndb
+modification	/man/8/mkfs
+modifications	/man/1/tiny
+modifications	/man/9/1copyright
+modifications	/man/9/entry
+modifications	/man/9/text
+modified	/man/1/acme
+modified	/man/1/alphabet-fs
+modified	/man/1/ar
+modified	/man/1/charon
+modified	/man/1/cp
+modified	/man/1/du
+modified	/man/1/fs
+modified	/man/1/ls
+modified	/man/1/mk
+modified	/man/1/tktester
+modified	/man/10/acid
+modified	/man/10/devattach
+modified	/man/10/iar
+modified	/man/10/mk
+modified	/man/10/parsecmd
+modified	/man/2/draw-image
+modified	/man/2/palmfile
+modified	/man/2/prefab-element
+modified	/man/2/readdir
+modified	/man/2/spree-cardlib
+modified	/man/2/sys-0intro
+modified	/man/2/sys-stat
+modified	/man/4/acme
+modified	/man/5/0intro
+modified	/man/5/stat
+modified	/man/8/changelogin
+modified	/man/8/prep
+modified	/man/8/rstyxd
+modified	/man/9/checkbutton
+modified	/man/9/menu
+modified	/man/9/radiobutton
+modified	/man/9/text
+modifier	/man/1/mdb
+modifier	/man/10/devattach
+modifier	/man/2/sys-stat
+modifier	/man/9/text
+modifiers	/man/1/ar
+modifiers	/man/10/iar
+modifiers	/man/9/text
+modifies	/man/2/command
+modifies	/man/9/button
+modifies	/man/9/canvas
+modifies	/man/9/checkbutton
+modifies	/man/9/choicebutton
+modifies	/man/9/entry
+modifies	/man/9/frame
+modifies	/man/9/label
+modifies	/man/9/listbox
+modifies	/man/9/menu
+modifies	/man/9/menubutton
+modifies	/man/9/panel
+modifies	/man/9/radiobutton
+modifies	/man/9/scale
+modifies	/man/9/scrollbar
+modifies	/man/9/text
+modify	/man/1/bind
+modify	/man/1/charon
+modify	/man/1/mk
+modify	/man/1/tiny
+modify	/man/1/tktester
+modify	/man/1/wm
+modify	/man/10/mk
+modify	/man/10/qlock
+modify	/man/2/prefab-0intro
+modify	/man/2/prefab-style
+modify	/man/2/security-0intro
+modify	/man/2/sys-bind
+modify	/man/2/sys-stat
+modify	/man/2/wmsrv
+modify	/man/4/grid-cpu
+modify	/man/5/0intro
+modify	/man/9/1copyright
+modify	/man/9/button
+modify	/man/9/canvas
+modify	/man/9/checkbutton
+modify	/man/9/choicebutton
+modify	/man/9/entry
+modify	/man/9/frame
+modify	/man/9/label
+modify	/man/9/listbox
+modify	/man/9/menu
+modify	/man/9/menubutton
+modify	/man/9/panel
+modify	/man/9/radiobutton
+modify	/man/9/scale
+modify	/man/9/scrollbar
+modify	/man/9/text
+modifying	/man/1/mk
+modifying	/man/10/mk
+modifying	/man/2/secstore
+modifying	/man/5/stat
+modifying	/man/8/prep
+modifying	/man/9/canvas
+modifying	/man/9/checkbutton
+modifying	/man/9/text
+modname	/man/1/cprof
+modname	/man/1/mprof
+modname	/man/1/prof
+modname	/man/1/yacc
+modno	/man/2/palmfile
+modprof	/man/2/prof
+mods	/man/2/prof
+modular	/man/1/0intro
+modular	/man/2/keyring-0intro
+module	/man/1/0intro
+module	/man/1/acme
+module	/man/1/cprof
+module	/man/1/deb
+module	/man/1/emu
+module	/man/1/kill
+module	/man/1/limbo
+module	/man/1/mash
+module	/man/1/mash-make
+module	/man/1/mprof
+module	/man/1/mux
+module	/man/1/prof
+module	/man/1/ps
+module	/man/1/sendmail
+module	/man/1/sh
+module	/man/1/sh-alphabet
+module	/man/1/sh-arg
+module	/man/1/sh-csv
+module	/man/1/sh-expr
+module	/man/1/sh-file2chan
+module	/man/1/sh-mload
+module	/man/1/sh-regex
+module	/man/1/sh-sexprs
+module	/man/1/sh-std
+module	/man/1/sh-string
+module	/man/1/sh-test
+module	/man/1/sh-tk
+module	/man/1/spree-join
+module	/man/1/stack
+module	/man/1/tiny
+module	/man/1/wm-misc
+module	/man/1/yacc
+module	/man/10/c2l
+module	/man/10/conf
+module	/man/10/dynld
+module	/man/2/0intro
+module	/man/2/alphabet-intro
+module	/man/2/arg
+module	/man/2/asn1
+module	/man/2/attrdb
+module	/man/2/bloomfilter
+module	/man/2/bufio
+module	/man/2/cfg
+module	/man/2/command
+module	/man/2/complete
+module	/man/2/convcs
+module	/man/2/crc
+module	/man/2/csv
+module	/man/2/dbm
+module	/man/2/debug
+module	/man/2/dhcpclient
+module	/man/2/dialog
+module	/man/2/dis
+module	/man/2/diskblocks
+module	/man/2/disks
+module	/man/2/draw-0intro
+module	/man/2/draw-context
+module	/man/2/draw-display
+module	/man/2/draw-example
+module	/man/2/env
+module	/man/2/ether
+module	/man/2/exception
+module	/man/2/filter
+module	/man/2/filter-deflate
+module	/man/2/filter-slip
+module	/man/2/fsproto
+module	/man/2/geodesy
+module	/man/2/hash
+module	/man/2/ida
+module	/man/2/imagefile
+module	/man/2/ip
+module	/man/2/ir
+module	/man/2/itslib
+module	/man/2/json
+module	/man/2/keyring-0intro
+module	/man/2/keyset
+module	/man/2/lists
+module	/man/2/math-0intro
+module	/man/2/msgio
+module	/man/2/palmfile
+module	/man/2/plumbmsg
+module	/man/2/popup
+module	/man/2/prefab-0intro
+module	/man/2/prefab-element
+module	/man/2/print
+module	/man/2/prof
+module	/man/2/pslib
+module	/man/2/regex
+module	/man/2/registries
+module	/man/2/rfc822
+module	/man/2/scsiio
+module	/man/2/secstore
+module	/man/2/security-0intro
+module	/man/2/security-login
+module	/man/2/sets
+module	/man/2/sexprs
+module	/man/2/sh
+module	/man/2/spki
+module	/man/2/spki-verifier
+module	/man/2/spree
+module	/man/2/spree-allow
+module	/man/2/spree-cardlib
+module	/man/2/spree-gather
+module	/man/2/spree-objstore
+module	/man/2/srv
+module	/man/2/stringinttab
+module	/man/2/styx
+module	/man/2/styxconv
+module	/man/2/styxservers
+module	/man/2/styxservers-nametree
+module	/man/2/sys-0intro
+module	/man/2/sys-dup
+module	/man/2/sys-self
+module	/man/2/sys-werrstr
+module	/man/2/tftp
+module	/man/2/timers
+module	/man/2/tk
+module	/man/2/tkclient
+module	/man/2/translate
+module	/man/2/ubfa
+module	/man/2/venti
+module	/man/2/w3c-css
+module	/man/2/w3c-uris
+module	/man/2/w3c-xpointers
+module	/man/2/wait
+module	/man/2/wmclient
+module	/man/2/wmsrv
+module	/man/2/xml
+module	/man/3/draw
+module	/man/3/dynld
+module	/man/3/prof
+module	/man/3/prog
+module	/man/3/sign
+module	/man/4/factotum
+module	/man/4/grid-cpu
+module	/man/4/namespace
+module	/man/5/0intro
+module	/man/6/dis
+module	/man/6/json
+module	/man/6/keyboard
+module	/man/6/plumbing
+module	/man/6/sbl
+module	/man/6/sexprs
+module	/man/7/db
+module	/man/7/dbsrv
+module	/man/8/httpd
+module	/man/8/init
+module	/man/8/logind
+module	/man/9/0intro
+module's	/man/1/mash
+module's	/man/1/sh-tk
+module's	/man/1/tiny
+module's	/man/1/wm-misc
+module's	/man/1/yacc
+module's	/man/10/dynld
+module's	/man/2/0intro
+module's	/man/2/alphabet-intro
+module's	/man/2/bufio
+module's	/man/2/dis
+module's	/man/2/disks
+module's	/man/2/prefab-0intro
+module's	/man/2/prof
+module's	/man/2/secstore
+module's	/man/2/selectfile
+module's	/man/2/spree-allow
+module's	/man/2/srv
+module's	/man/2/styxservers-nametree
+module's	/man/2/sys-self
+module's	/man/3/dynld
+module's	/man/6/dis
+module's	/man/6/sbl
+modules	/man/1/0intro
+modules	/man/1/alphabet-abc
+modules	/man/1/alphabet-fs
+modules	/man/1/alphabet-grid
+modules	/man/1/alphabet-main
+modules	/man/1/cprof
+modules	/man/1/deb
+modules	/man/1/disdep
+modules	/man/1/limbo
+modules	/man/1/mash
+modules	/man/1/mprof
+modules	/man/1/prof
+modules	/man/1/sh
+modules	/man/1/sh-alphabet
+modules	/man/1/sh-expr
+modules	/man/1/sh-mload
+modules	/man/10/2c
+modules	/man/10/acid
+modules	/man/10/c2l
+modules	/man/10/conf
+modules	/man/10/dynld
+modules	/man/2/0intro
+modules	/man/2/alphabet-intro
+modules	/man/2/convcs
+modules	/man/2/filter-deflate
+modules	/man/2/imagefile
+modules	/man/2/ir
+modules	/man/2/math-fp
+modules	/man/2/print
+modules	/man/2/prof
+modules	/man/2/sh
+modules	/man/2/spree
+modules	/man/2/sys-self
+modules	/man/2/wmlib
+modules	/man/3/dynld
+modules	/man/3/prof
+modules	/man/3/sign
+modules	/man/4/namespace
+modules	/man/6/dis
+modules	/man/7/0intro
+modules	/man/8/collabsrv
+modulo	/man/10/9load
+modulo	/man/9/canvas
+modulus	/man/1/mdb
+modulus	/man/1/sh-expr
+modulus	/man/2/keyring-0intro
+modulus	/man/2/keyring-gensk
+modulus	/man/2/math-fp
+modulus	/man/2/rand
+modulus	/man/6/auth
+modulus	/man/6/login
+modulus	/man/8/ai2key
+mole	/man/1/units
+momentarily	/man/2/wmsrv
+mon	/man/2/daytime
+monitor	/man/1/grid-monitor
+monitor	/man/1/grid-ns
+monitor	/man/1/grid-register
+monitor	/man/10/plan9.ini
+monitor	/man/2/dbm
+monitor	/man/2/wait
+monitor	/man/3/dbg
+monitor	/man/4/grid-cpu
+monitor	/man/4/iostats
+monitor	/man/8/dhcp
+monitor	/man/8/mangaload
+monitor	/man/8/rdbgsrv
+monitor	/man/8/styxmon
+monitor.b	/man/1/grid-monitor
+monitor.b	/man/1/grid-session
+monitoring	/man/8/styxmon
+monitors	/man/3/vga
+monitors	/man/9/radiobutton
+mono	/man/1/auplay
+mono	/man/3/audio
+mono	/man/6/audio
+monochrome	/man/3/tv
+monochrome	/man/9/options
+monotone	/man/2/imagefile
+monotonically	/man/10/plan9.ini
+monotonically	/man/8/applylog
+month	/man/1/cal
+month	/man/2/daytime
+month	/man/8/changelogin
+months	/man/1/cal
+months	/man/2/daytime
+months	/man/2/sys-millisec
+moreover	/man/10/a.out
+moreover	/man/2/dbm
+moreover	/man/2/prefab-0intro
+moreover	/man/2/prefab-compound
+moreover	/man/8/changelogin
+moreover	/man/9/0intro
+moribund	/man/3/dbg
+mostek	/man/3/rtc
+mostly	/man/3/cons
+mostly	/man/3/root
+motherboard	/man/10/plan9.ini
+motion	/man/1/acme
+motion	/man/1/emu
+motion	/man/2/math-0intro
+motion	/man/9/bind
+motion	/man/9/canvas
+motion	/man/9/grab
+motion	/man/9/text
+motions	/man/9/grab
+motions	/man/9/text
+motorola	/man/1/avr
+motorola	/man/10/2c
+motorola	/man/10/ms2
+motorola	/man/3/touch
+motorola	/man/3/vid
+mount	/man/1/0intro
+mount	/man/1/acme
+mount	/man/1/alphabet-main
+mount	/man/1/bind
+mount	/man/1/collab-clients
+mount	/man/1/grid-query
+mount	/man/1/grid-session
+mount	/man/1/listen
+mount	/man/1/ns
+mount	/man/1/nsbuild
+mount	/man/1/passwd
+mount	/man/1/sh-std
+mount	/man/1/wm-sh
+mount	/man/1/zeros
+mount	/man/10/devattach
+mount	/man/10/newchan
+mount	/man/10/odbc
+mount	/man/10/styxserver
+mount	/man/2/alphabet-intro
+mount	/man/2/dial
+mount	/man/2/factotum
+mount	/man/2/readdir
+mount	/man/2/security-auth
+mount	/man/2/styxconv
+mount	/man/2/styxpersist
+mount	/man/2/sys-0intro
+mount	/man/2/sys-bind
+mount	/man/2/sys-export
+mount	/man/2/sys-fauth
+mount	/man/2/sys-fversion
+mount	/man/2/sys-pipe
+mount	/man/3/logfs
+mount	/man/3/mnt
+mount	/man/3/prog
+mount	/man/3/root
+mount	/man/3/srv9
+mount	/man/3/tinyfs
+mount	/man/4/9srvfs
+mount	/man/4/acme
+mount	/man/4/archfs
+mount	/man/4/factotum
+mount	/man/4/ftpfs
+mount	/man/4/grid-cpu
+mount	/man/4/import
+mount	/man/4/keysrv
+mount	/man/4/kfs
+mount	/man/4/lockfs
+mount	/man/4/memfs
+mount	/man/4/mntgen
+mount	/man/4/namespace
+mount	/man/4/ramfile
+mount	/man/4/registry
+mount	/man/4/spree
+mount	/man/4/tarfs
+mount	/man/4/vacfs
+mount	/man/5/0intro
+mount	/man/5/attach
+mount	/man/5/version
+mount	/man/5/walk
+mount	/man/6/namespace
+mount	/man/8/collabsrv
+mount	/man/8/cs
+mount	/man/8/dhcp
+mount	/man/8/dns
+mount	/man/8/getauthinfo
+mount	/man/8/mkfs
+mount	/man/8/rdbgsrv
+mount	/man/8/rstyxd
+mount	/man/8/styxmon
+mount.b	/man/1/bind
+mount.b	/man/2/security-auth
+mountdir	/man/4/trfs
+mounted	/man/1/0intro
+mounted	/man/1/bind
+mounted	/man/1/calendar
+mounted	/man/1/ls
+mounted	/man/1/spree-join
+mounted	/man/10/9load
+mounted	/man/10/newchan
+mounted	/man/2/draw-display
+mounted	/man/2/sys-bind
+mounted	/man/2/sys-export
+mounted	/man/3/kprof
+mounted	/man/3/logfs
+mounted	/man/3/pipe
+mounted	/man/3/srv9
+mounted	/man/4/0intro
+mounted	/man/4/9srvfs
+mounted	/man/4/acme
+mounted	/man/4/archfs
+mounted	/man/4/dbfs
+mounted	/man/4/kfs
+mounted	/man/4/lockfs
+mounted	/man/4/mntgen
+mounted	/man/4/namespace
+mounted	/man/4/registry
+mounted	/man/4/spree
+mounted	/man/6/namespace
+mounted	/man/8/applylog
+mounted	/man/8/changelogin
+mounted	/man/8/mkfs
+mounting	/man/1/collab
+mounting	/man/10/plan9.ini
+mounting	/man/2/factotum
+mounting	/man/2/sys-bind
+mounting	/man/3/mnt
+mounting	/man/8/init
+mountpoint	/man/1/os
+mountpoint	/man/4/archfs
+mountpoint	/man/4/dbfs
+mountpoint	/man/4/dossrv
+mountpoint	/man/4/ftpfs
+mountpoint	/man/4/keyfs
+mountpoint	/man/4/lockfs
+mountpoint	/man/4/memfs
+mountpoint	/man/8/rdbgsrv
+mountpt	/man/4/tarfs
+mounts	/man/1/alphabet-main
+mounts	/man/1/collab
+mounts	/man/1/ns
+mounts	/man/10/kproc
+mounts	/man/10/odbc
+mounts	/man/10/styxserver
+mounts	/man/2/sys-export
+mounts	/man/3/cmd
+mounts	/man/4/archfs
+mounts	/man/4/iostats
+mounts	/man/4/keysrv
+mounts	/man/4/memfs
+mounts	/man/8/rdbgsrv
+mounts	/man/8/rstyxd
+mouse	/man/1/0intro
+mouse	/man/1/9win
+mouse	/man/1/acme
+mouse	/man/1/brutus
+mouse	/man/1/charon
+mouse	/man/1/collab-clients
+mouse	/man/1/emu
+mouse	/man/1/grid-session
+mouse	/man/1/keyboard
+mouse	/man/1/logon
+mouse	/man/1/man
+mouse	/man/1/mash-tk
+mouse	/man/1/miniterm
+mouse	/man/1/tktester
+mouse	/man/1/wm-misc
+mouse	/man/1/wm-sh
+mouse	/man/10/plan9.ini
+mouse	/man/2/draw-context
+mouse	/man/2/draw-display
+mouse	/man/2/draw-pointer
+mouse	/man/2/prefab-0intro
+mouse	/man/2/tk
+mouse	/man/2/wmclient
+mouse	/man/3/cons
+mouse	/man/3/pointer
+mouse	/man/3/touch
+mouse	/man/3/vga
+mouse	/man/4/acme
+mouse	/man/9/bind
+mouse	/man/9/button
+mouse	/man/9/canvas
+mouse	/man/9/checkbutton
+mouse	/man/9/choicebutton
+mouse	/man/9/cursor
+mouse	/man/9/entry
+mouse	/man/9/grab
+mouse	/man/9/listbox
+mouse	/man/9/menu
+mouse	/man/9/menubutton
+mouse	/man/9/options
+mouse	/man/9/radiobutton
+mouse	/man/9/scale
+mouse	/man/9/scrollbar
+mouse	/man/9/text
+mouse	/man/9/update
+mouseport	/man/10/plan9.ini
+move	/man/1/acme
+move	/man/1/ar
+move	/man/1/cprof
+move	/man/1/ebook
+move	/man/1/ftree
+move	/man/1/mv
+move	/man/1/sh-tk
+move	/man/1/tktester
+move	/man/1/wm
+move	/man/1/wm-misc
+move	/man/10/iar
+move	/man/2/draw-image
+move	/man/2/prefab-element
+move	/man/2/tkclient
+move	/man/2/wmclient
+move	/man/3/cons
+move	/man/6/man
+move	/man/9/canvas
+move	/man/9/entry
+move	/man/9/scrollbar
+move	/man/9/text
+moved	/man/1/acme
+moved	/man/1/mv
+moved	/man/1/tktester
+moved	/man/1/wm-sh
+moved	/man/10/iar
+moved	/man/2/prefab-compound
+moved	/man/2/spree
+moved	/man/3/dbg
+moved	/man/3/draw
+moved	/man/8/mkfs
+moved	/man/9/bind
+moved	/man/9/canvas
+moved	/man/9/menubutton
+moved	/man/9/text
+movefd	/man/2/sys-pctl
+movement	/man/1/acme
+movement	/man/9/bind
+movement	/man/9/update
+moves	/man/1/acme
+moves	/man/1/brutus
+moves	/man/1/mv
+moves	/man/10/strcat
+moves	/man/2/draw-image
+moves	/man/2/prefab-element
+moves	/man/2/spree-cardlib
+moves	/man/2/xml
+moves	/man/3/pointer
+moves	/man/9/menu
+moves	/man/9/scale
+moveto	/man/9/canvas
+moveto	/man/9/entry
+moveto	/man/9/listbox
+moveto	/man/9/scrollbar
+moveto	/man/9/text
+movie	/man/1/mux
+movie	/man/2/mpeg
+movies	/man/1/mux
+moving	/man/1/acme
+moving	/man/1/ebook
+moving	/man/1/tktester
+moving	/man/1/wm-misc
+moving	/man/10/malloc
+moving	/man/3/vga
+moving	/man/9/text
+movw	/man/10/acid
+mp	/man/1/sh-mload
+mp	/man/2/dis
+mp	/man/6/dis
+mpc	/man/3/kprof
+mpc	/man/3/switch
+mpc	/man/3/touch
+mpc	/man/3/usb
+mpc	/man/3/vid
+mpc823	/man/3/usb
+mpc823	/man/3/vid
+mpc8xx	/man/3/switch
+mpc8xx	/man/3/touch
+mpeg	/man/10/kproc
+mpeg	/man/2/mpeg
+mpeg	/man/3/mpeg
+mpeg.b	/man/2/mpeg
+mpeg.m	/man/2/mpeg
+mpeg0	/man/3/mpeg
+mpeg1,sif	/man/3/mpeg
+mpegctl	/man/2/mpeg
+mpegctl	/man/3/mpeg
+mpexpr	/man/1/sh-expr
+mpexpr	/man/1/sh-mload
+mprof	/man/1/mprof
+mprof	/man/1/prof
+mprof.b	/man/1/mprof
+mpx	/man/8/collabsrv
+mrepl	/man/2/sys-bind
+mrepl	/man/3/0intro
+mrepl	/man/3/srv
+mrepl	/man/6/namespace
+ms	/man/1/itest
+ms	/man/1/prof
+ms	/man/10/9load
+ms	/man/10/plan9.ini
+ms	/man/10/sleep
+ms	/man/2/convcs
+ms	/man/2/prof
+ms	/man/3/prof
+ms	/man/8/prep
+ms2	/man/1/avr
+ms2	/man/10/5cv
+ms2	/man/10/ms2
+ms2hz	/man/10/seconds
+ms2tk	/man/10/seconds
+msb	/man/2/asn1
+msb	/man/2/sets
+mschap	/man/4/factotum
+msec	/man/2/timers
+msec	/man/3/cons
+msec	/man/3/pointer
+msg	/man/1/uuencode
+msg	/man/1/wm
+msg	/man/1/yacc
+msg	/man/10/allocb
+msg	/man/10/print
+msg	/man/10/qio
+msg	/man/10/styxserver
+msg	/man/2/dialog
+msg	/man/2/filter
+msg	/man/2/filter-deflate
+msg	/man/2/itslib
+msg	/man/2/mpeg
+msg	/man/2/plumbmsg
+msg	/man/2/sh
+msg	/man/2/smtp
+msg	/man/2/spree
+msg	/man/2/xml
+msg.recv	/man/2/plumbmsg
+msg.unpack	/man/2/plumbmsg
+msgio	/man/2/msgio
+msgio.b	/man/2/msgio
+msgio.m	/man/2/msgio
+msglist	/man/2/pop3
+msgmax	/man/10/styxserver
+msgnolist	/man/2/pop3
+msgs	/man/1/itest
+msgs	/man/8/collabsrv
+msize	/man/10/styx
+msize	/man/2/styx
+msize	/man/2/styxservers
+msize	/man/5/0intro
+msize	/man/5/version
+mtime	/man/10/devattach
+mtime	/man/2/filter-deflate
+mtime	/man/2/palmfile
+mtime	/man/2/readdir
+mtime	/man/2/styxservers
+mtime	/man/2/sys-stat
+mtime	/man/3/logfs
+mtime	/man/5/stat
+mtime	/man/8/applylog
+mtime	/man/8/styxchat
+mtpt	/man/4/factotum
+mtu	/man/2/ip
+mtu	/man/3/ip
+mtype	/man/2/styx
+muid	/man/10/devattach
+muid	/man/2/styxservers
+muid	/man/2/sys-stat
+muid	/man/5/stat
+muid	/man/8/styxchat
+mul	/man/2/draw-point
+mul	/man/2/keyring-ipint
+multi	/man/1/acme
+multi	/man/1/brutus
+multi	/man/1/collab-clients
+multi	/man/1/wm-sh
+multi	/man/3/ip
+multi	/man/3/sd
+multi	/man/4/lockfs
+multi	/man/8/collabsrv
+multibyte	/man/3/draw
+multibyte	/man/6/utf
+multicast	/man/2/ip
+multicast	/man/3/ether
+multicast	/man/3/ip
+multicolumn	/man/1/mc
+multiflag	/man/10/getfields
+multimaster	/man/10/plan9.ini
+multipart	/man/2/rfc822
+multiples	/man/10/devattach
+multiples	/man/2/diskblocks
+multiples	/man/2/spree-cardlib
+multiples	/man/3/ftl
+multiples	/man/6/colour
+multiplex	/man/2/drawmux
+multiplexed	/man/2/dial
+multiplexed	/man/2/ir
+multiplexed	/man/2/sys-dial
+multiplexed	/man/3/mnt
+multiplexes	/man/1/listen
+multiplexes	/man/3/mnt
+multiplexes	/man/3/plap
+multiplexing	/man/2/sys-file2chan
+multiplexing	/man/6/colour
+multiplexing	/man/8/collabsrv
+multiplexor	/man/2/drawmux
+multiplexor	/man/8/collabsrv
+multiplexors	/man/10/dev
+multiplication	/man/1/dd
+multiplication	/man/1/mdb
+multiplication	/man/1/sh-expr
+multiplicative	/man/1/units
+multiplied	/man/10/a.out
+multiplied	/man/9/types
+multiplier	/man/2/print
+multiply	/man/1/units
+multiply	/man/10/2l
+multiply	/man/2/math-0intro
+multiply	/man/2/math-linalg
+multiplying	/man/2/draw-0intro
+multiprocessor	/man/10/lock
+multiprocessor	/man/10/plan9.ini
+multiprocessor	/man/10/sleep
+multiprocessors	/man/10/splhi
+multipurpose	/man/2/rfc822
+multisync135	/man/10/plan9.ini
+munload	/man/1/sh-mload
+musicam,i	/man/3/mpeg
+musicam,ii	/man/3/mpeg
+mustcompile	/man/2/dis
+mustcompile	/man/6/dis
+mute	/man/2/ir
+mutual	/man/10/qlock
+mutual	/man/2/keyring-auth
+mutual	/man/2/lock
+mutual	/man/2/registries
+mutual	/man/2/security-0intro
+mutual	/man/3/tls
+mutual	/man/4/factotum
+mutual	/man/6/auth
+mutually	/man/10/master
+mutually	/man/2/secstore
+mutually	/man/2/security-0intro
+mutually	/man/2/spree-cardlib
+mutually	/man/2/sys-export
+mux	/man/1/mux
+mux	/man/1/wish
+mux	/man/2/devpointer
+mux	/man/2/draw-context
+mux	/man/2/draw-pointer
+mux	/man/2/ir
+mux	/man/2/keyring-0intro
+mux	/man/2/volume
+mux	/man/8/manufacture
+mux	/man/8/register
+mv	/man/1/cp
+mv	/man/1/mv
+mv	/man/4/ftpfs
+mv.b	/man/1/mv
+mx	/man/6/ndb
+mx	/man/8/dns
+mya4	/man/2/print
+mycreate	/man/10/styxserver
+myinit	/man/10/styxserver
+mylex	/man/10/plan9.ini
+mypk	/man/2/keyring-0intro
+myread	/man/10/styxserver
+myremove	/man/10/styxserver
+myserv	/man/3/srv9
+mysk	/man/2/keyring-0intro
+n.b	/man/1/filename
+n.b	/man/1/mdb
+n.b	/man/2/sh
+n.t.l	/man/6/man
+n1,n2	/man/1/diff
+n3,n4	/man/1/diff
+n:int	/man/2/hash
+nad	/man/2/asn1
+naive	/man/1/cal
+naive	/man/1/diff
+naive	/man/1/webgrab
+nakd	/man/3/usb
+naked	/man/6/man
+name's	/man/6/ndb
+namec	/man/10/newchan
+namechan	/man/1/sh-tk
+namechan	/man/2/tk
+namechan	/man/9/send
+namedimage	/man/2/draw-display
+namelen	/man/10/eve
+namely	/man/1/sh
+namely	/man/2/styxpersist
+names	/man/1/0intro
+names	/man/1/acme
+names	/man/1/alphabet-fs
+names	/man/1/alphabet-main
+names	/man/1/ar
+names	/man/1/basename
+names	/man/1/calc
+names	/man/1/charon
+names	/man/1/cleanname
+names	/man/1/collab-clients
+names	/man/1/cp
+names	/man/1/cprof
+names	/man/1/diff
+names	/man/1/disdep
+names	/man/1/ebook
+names	/man/1/fc
+names	/man/1/fs
+names	/man/1/gettar
+names	/man/1/grid-query
+names	/man/1/logon
+names	/man/1/ls
+names	/man/1/m4
+names	/man/1/man
+names	/man/1/mash
+names	/man/1/mk
+names	/man/1/mprof
+names	/man/1/ns
+names	/man/1/prof
+names	/man/1/secstore
+names	/man/1/sendmail
+names	/man/1/sh
+names	/man/1/sh-alphabet
+names	/man/1/sh-expr
+names	/man/1/sh-std
+names	/man/1/tcs
+names	/man/1/tktester
+names	/man/1/units
+names	/man/1/webgrab
+names	/man/1/yacc
+names	/man/10/2c
+names	/man/10/a.out
+names	/man/10/c2l
+names	/man/10/conf
+names	/man/10/devattach
+names	/man/10/dynld
+names	/man/10/iar
+names	/man/10/master
+names	/man/10/mk
+names	/man/10/odbc
+names	/man/10/plan9.ini
+names	/man/10/srclist
+names	/man/2/0intro
+names	/man/2/arg
+names	/man/2/cfg
+names	/man/2/command
+names	/man/2/complete
+names	/man/2/convcs
+names	/man/2/dhcpclient
+names	/man/2/dial
+names	/man/2/dis
+names	/man/2/disks
+names	/man/2/factotum
+names	/man/2/filepat
+names	/man/2/format
+names	/man/2/json
+names	/man/2/keyring-auth
+names	/man/2/names
+names	/man/2/palmfile
+names	/man/2/plumbmsg
+names	/man/2/readdir
+names	/man/2/secstore
+names	/man/2/security-ssl
+names	/man/2/sh
+names	/man/2/smtp
+names	/man/2/spki
+names	/man/2/spree
+names	/man/2/spree-gather
+names	/man/2/spree-objstore
+names	/man/2/srv
+names	/man/2/styx
+names	/man/2/sys-0intro
+names	/man/2/sys-bind
+names	/man/2/sys-chdir
+names	/man/2/sys-export
+names	/man/2/sys-pctl
+names	/man/2/sys-stat
+names	/man/2/translate
+names	/man/2/w3c-css
+names	/man/2/xml
+names	/man/3/0intro
+names	/man/3/cmd
+names	/man/3/dbg
+names	/man/3/dup
+names	/man/3/fs
+names	/man/3/gpio
+names	/man/3/indir
+names	/man/3/ip
+names	/man/3/root
+names	/man/3/srv9
+names	/man/3/ssl
+names	/man/3/tls
+names	/man/4/archfs
+names	/man/4/kfs
+names	/man/4/registry
+names	/man/4/tarfs
+names	/man/4/trfs
+names	/man/5/0intro
+names	/man/5/open
+names	/man/5/stat
+names	/man/5/walk
+names	/man/6/dis
+names	/man/6/font
+names	/man/6/json
+names	/man/6/keyboard
+names	/man/6/man
+names	/man/6/ndb
+names	/man/6/plumbing
+names	/man/6/proto
+names	/man/6/sbl
+names	/man/6/users
+names	/man/7/db
+names	/man/7/dbsrv
+names	/man/8/applylog
+names	/man/8/bootpd
+names	/man/8/collabsrv
+names	/man/8/create
+names	/man/8/cs
+names	/man/8/dns
+names	/man/8/kfscmd
+names	/man/8/mkfs
+names	/man/8/prep
+names	/man/8/rstyxd
+names	/man/9/canvas
+names	/man/9/grid
+names	/man/9/image
+names	/man/9/options
+names	/man/9/pack
+names	/man/9/text
+names	/man/9/types
+names.b	/man/2/names
+names.dis	/man/6/proto
+names.m	/man/2/names
+namesakes	/man/2/wmsrv
+namespace	/man/1/0intro
+namespace	/man/1/9win
+namespace	/man/1/alphabet-main
+namespace	/man/1/cpu
+namespace	/man/1/ftree
+namespace	/man/1/grid-ns
+namespace	/man/1/grid-query
+namespace	/man/1/grid-register
+namespace	/man/1/grid-session
+namespace	/man/1/logon
+namespace	/man/1/ns
+namespace	/man/1/nsbuild
+namespace	/man/1/os
+namespace	/man/1/sh
+namespace	/man/1/sh-file2chan
+namespace	/man/1/sh-mload
+namespace	/man/1/tiny
+namespace	/man/1/wm-sh
+namespace	/man/10/master
+namespace	/man/10/odbc
+namespace	/man/2/newns
+namespace	/man/2/styxpersist
+namespace	/man/2/styxservers
+namespace	/man/2/styxservers-nametree
+namespace	/man/2/sys-0intro
+namespace	/man/3/fs
+namespace	/man/3/pipe
+namespace	/man/3/prog
+namespace	/man/4/grid-cpu
+namespace	/man/4/import
+namespace	/man/4/lockfs
+namespace	/man/4/namespace
+namespace	/man/4/spree
+namespace	/man/6/namespace
+namespaces	/man/1/grid-query
+namespaces	/man/1/sh-mload
+namespaces	/man/4/grid-cpu
+nametest	/man/2/w3c-xpointers
+nametree	/man/2/styxservers
+nametree	/man/2/styxservers-nametree
+nametree	/man/2/sys-stat
+nametree.b	/man/2/styxservers-nametree
+naming	/man/1/collab-clients
+naming	/man/1/tiny
+naming	/man/10/9load
+naming	/man/10/plan9.ini
+naming	/man/2/fsproto
+naming	/man/2/msgio
+naming	/man/2/spree-objstore
+naming	/man/3/logfs
+naming	/man/3/plap
+naming	/man/3/sd
+naming	/man/8/collabsrv
+nan	/man/1/calc
+nan	/man/2/math-fp
+nan	/man/2/string
+nan,infinity	/man/2/math-fp
+nand	/man/1/calc
+nand	/man/3/flash
+nand	/man/3/logfs
+nap	/man/10/styx
+natgrid	/man/2/geodesy
+native	/man/1/0intro
+native	/man/1/emu
+native	/man/1/mdb
+native	/man/1/miniterm
+native	/man/1/mux
+native	/man/10/0intro
+native	/man/10/2c
+native	/man/10/a.out
+native	/man/10/conf
+native	/man/10/dev
+native	/man/10/error
+native	/man/10/eve
+native	/man/2/0intro
+native	/man/2/ip
+native	/man/2/math-0intro
+native	/man/3/audio
+native	/man/3/cons
+native	/man/3/dbg
+native	/man/3/ip
+native	/man/3/root
+native	/man/4/namespace
+native	/man/6/dis
+native	/man/6/keyboard
+native	/man/6/namespace
+native	/man/8/0intro
+native	/man/8/cs
+native	/man/8/init
+native	/man/8/ping
+native	/man/8/rdbgsrv
+native	/man/8/shutdown
+natively	/man/1/0intro
+natural	/man/2/math-elem
+natural	/man/2/prefab-element
+natural	/man/2/translate
+nature	/man/1/units
+nature	/man/1/zeros
+nature	/man/2/security-0intro
+nature	/man/2/spree-allow
+nature	/man/3/ip
+nature	/man/3/touch
+nature	/man/9/grab
+navigate	/man/1/charon
+navigate	/man/5/0intro
+navigates	/man/5/0intro
+navigating	/man/2/styxservers
+navigating	/man/2/xml
+navigation	/man/1/charon
+navigation	/man/2/xml
+navigation	/man/9/options
+navigator	/man/1/charon
+navigator	/man/2/styxservers
+navigator	/man/2/styxservers-nametree
+navigator	/man/6/colour
+navigator.new	/man/2/styxservers
+navigator.new	/man/2/styxservers-nametree
+navop	/man/2/styxservers
+navop	/man/2/styxservers-nametree
+navy	/man/9/types
+nb	/man/2/crc
+nb	/man/2/print
+nb	/man/3/usb
+nbits	/man/2/ip
+nbits	/man/2/keyring-gensk
+nbits	/man/2/spki
+nbits	/man/8/ai2key
+nbuf	/man/10/styx
+nbytes	/man/10/dev
+nbytes	/man/10/dynld
+nbytes	/man/2/convcs
+nbytes	/man/2/sys-read
+nbytes	/man/3/i2c
+nbytes	/man/6/sexprs
+nc	/man/10/dev
+nc	/man/10/devattach
+nc	/man/10/styxserver
+nchange	/man/2/scsiio
+nchans	/man/2/imagefile
+ncharacters	/man/1/tail
+nchars	/man/2/convcs
+nclients	/man/10/odbc
+ncname	/man/2/w3c-xpointers
+ncol	/man/1/acme
+ncpu	/man/10/plan9.ini
+ncr	/man/10/plan9.ini
+nd	/man/10/plan9.ini
+ndb	/man/10/9load
+ndb	/man/2/attrdb
+ndb	/man/2/dhcpclient
+ndb	/man/2/pop3
+ndb	/man/2/smtp
+ndb	/man/4/namespace
+ndb	/man/4/registry
+ndb	/man/6/attrdb
+ndb	/man/6/ndb
+ndb	/man/8/bootpd
+ndb	/man/8/cs
+ndb	/man/8/dhcp
+ndb	/man/8/dns
+ndb	/man/8/getauthinfo
+ndb	/man/8/rip
+ndb	/man/8/svc
+ndb	/man/8/virgild
+ne	/man/2/dis
+ne	/man/9/options
+ne	/man/9/types
+ne2000	/man/10/plan9.ini
+ne4100	/man/3/i82365
+near	/man/1/acme
+near	/man/10/acid
+near	/man/10/plan9.ini
+near	/man/2/dialog
+near	/man/2/selectfile
+near	/man/6/keyboard
+near	/man/9/0intro
+near	/man/9/listbox
+nearby	/man/10/0intro
+nearest	/man/1/du
+nearest	/man/10/acid
+nearest	/man/2/geodesy
+nearest	/man/2/math-fp
+nearest	/man/4/memfs
+nearest	/man/9/canvas
+nearest	/man/9/entry
+nearest	/man/9/listbox
+nearly	/man/10/dev
+nearly	/man/10/devattach
+necessarily	/man/1/acme
+necessarily	/man/2/draw-0intro
+necessarily	/man/2/draw-image
+necessarily	/man/2/spree
+necessarily	/man/9/options
+necessary	/man/1/acme
+necessary	/man/1/alphabet-abc
+necessary	/man/1/alphabet-grid
+necessary	/man/1/crypt
+necessary	/man/1/grid-session
+necessary	/man/1/mdb
+necessary	/man/1/os
+necessary	/man/1/sh-alphabet
+necessary	/man/1/sh-csv
+necessary	/man/1/sh-string
+necessary	/man/10/2c
+necessary	/man/10/2l
+necessary	/man/10/allocb
+necessary	/man/10/ar
+necessary	/man/10/atoi
+necessary	/man/10/dev
+necessary	/man/10/dynld
+necessary	/man/10/newchan
+necessary	/man/10/plan9.ini
+necessary	/man/10/strcat
+necessary	/man/2/0intro
+necessary	/man/2/bufio
+necessary	/man/2/dbm
+necessary	/man/2/debug
+necessary	/man/2/dividers
+necessary	/man/2/draw-image
+necessary	/man/2/draw-screen
+necessary	/man/2/ir
+necessary	/man/2/keyring-0intro
+necessary	/man/2/palmfile
+necessary	/man/2/prefab-element
+necessary	/man/2/prefab-environ
+necessary	/man/2/rfc822
+necessary	/man/2/security-0intro
+necessary	/man/2/spki
+necessary	/man/2/styxpersist
+necessary	/man/2/styxservers
+necessary	/man/2/sys-open
+necessary	/man/2/sys-print
+necessary	/man/2/sys-stat
+necessary	/man/2/tkclient
+necessary	/man/2/wmclient
+necessary	/man/3/boot
+necessary	/man/3/cmd
+necessary	/man/3/draw
+necessary	/man/3/ip
+necessary	/man/3/logfs
+necessary	/man/3/pbus
+necessary	/man/4/acme
+necessary	/man/4/factotum
+necessary	/man/4/namespace
+necessary	/man/4/spree
+necessary	/man/5/0intro
+necessary	/man/6/utf
+necessary	/man/8/collabsrv
+necessary	/man/8/plumber
+necessary	/man/9/canvas
+necessary	/man/9/listbox
+necessary	/man/9/menu
+necessary	/man/9/text
+necessity	/man/1/sh-expr
+neck	/man/9/canvas
+needed	/man/1/acme
+needed	/man/1/charon
+needed	/man/1/grid-session
+needed	/man/1/mash-tk
+needed	/man/1/mk
+needed	/man/10/0intro
+needed	/man/10/2l
+needed	/man/10/conf
+needed	/man/10/devattach
+needed	/man/10/dynld
+needed	/man/10/error
+needed	/man/10/mk
+needed	/man/10/plan9.ini
+needed	/man/10/styx
+needed	/man/2/alphabet-intro
+needed	/man/2/draw-image
+needed	/man/2/draw-screen
+needed	/man/2/factotum
+needed	/man/2/keyring-0intro
+needed	/man/2/palmfile
+needed	/man/2/plumbmsg
+needed	/man/2/pop3
+needed	/man/2/print
+needed	/man/2/registries
+needed	/man/2/security-auth
+needed	/man/2/sh
+needed	/man/2/spree
+needed	/man/2/styxservers
+needed	/man/2/sys-dirread
+needed	/man/2/sys-pctl
+needed	/man/2/w3c-uris
+needed	/man/2/xml
+needed	/man/3/dbg
+needed	/man/3/ip
+needed	/man/3/plap
+needed	/man/3/root
+needed	/man/4/acme
+needed	/man/4/archfs
+needed	/man/4/factotum
+needed	/man/4/mntgen
+needed	/man/4/namespace
+needed	/man/5/0intro
+needed	/man/5/clunk
+needed	/man/5/flush
+needed	/man/8/ftl
+needed	/man/8/kfscmd
+needed	/man/8/register
+needed	/man/9/grid
+needed	/man/9/menu
+needfile	/man/10/styxserver
+needing	/man/2/spree
+needkey	/man/4/factotum
+needless	/man/3/logfs
+needlessly	/man/10/dev
+needs	/man/1/acme
+needs	/man/1/mash-make
+needs	/man/1/webgrab
+needs	/man/10/2c
+needs	/man/10/2l
+needs	/man/10/dynld
+needs	/man/10/plan9.ini
+needs	/man/2/alphabet-intro
+needs	/man/2/cfg
+needs	/man/2/debug
+needs	/man/2/prefab-element
+needs	/man/2/security-0intro
+needs	/man/2/styxflush
+needs	/man/2/styxservers
+needs	/man/3/cmd
+needs	/man/3/draw
+needs	/man/3/pnp
+needs	/man/4/factotum
+needs	/man/4/vacfs
+needs	/man/6/dis
+needs	/man/6/login
+needs	/man/8/getauthinfo
+needs	/man/9/grid
+needs	/man/9/options
+needs	/man/9/pack
+neg	/man/2/keyring-ipint
+negated	/man/6/regexp
+negation	/man/1/sh-expr
+negative	/man/1/acme
+negative	/man/1/tail
+negative	/man/10/allocb
+negative	/man/10/c2l
+negative	/man/10/memory
+negative	/man/10/print
+negative	/man/10/ref
+negative	/man/2/0intro
+negative	/man/2/dbm
+negative	/man/2/draw-image
+negative	/man/2/env
+negative	/man/2/factotum
+negative	/man/2/geodesy
+negative	/man/2/ip
+negative	/man/2/keyring-getstring
+negative	/man/2/keyring-ipint
+negative	/man/2/math-fp
+negative	/man/2/msgio
+negative	/man/2/plumbmsg
+negative	/man/2/prof
+negative	/man/2/rand
+negative	/man/2/rfc822
+negative	/man/2/secstore
+negative	/man/2/sets
+negative	/man/2/styxservers
+negative	/man/2/sys-0intro
+negative	/man/2/sys-export
+negative	/man/2/sys-print
+negative	/man/3/draw
+negative	/man/6/users
+negative	/man/7/db
+negative	/man/9/canvas
+negative	/man/9/entry
+negative	/man/9/grid
+negative	/man/9/listbox
+negative	/man/9/options
+negative	/man/9/scrollbar
+negative	/man/9/text
+negotiable	/man/5/0intro
+negotiate	/man/10/plan9.ini
+negotiate	/man/2/factotum
+negotiate	/man/2/sys-fversion
+negotiate	/man/3/ip
+negotiate	/man/4/factotum
+negotiate	/man/5/version
+negotiated	/man/10/plan9.ini
+negotiated	/man/10/styx
+negotiated	/man/2/factotum
+negotiated	/man/2/styx
+negotiated	/man/2/styxservers
+negotiated	/man/2/sys-fversion
+negotiated	/man/3/ip
+negotiated	/man/5/0intro
+negotiates	/man/2/sys-fversion
+negotiates	/man/5/version
+negotiation	/man/2/sys-fversion
+negotiation	/man/3/ip
+negotiation	/man/4/factotum
+negotiation	/man/4/import
+negotiation	/man/7/db
+neighbour	/man/9/text
+neither	/man/1/alphabet-fs
+neither	/man/1/ar
+neither	/man/1/charon
+neither	/man/1/cprof
+neither	/man/1/fs
+neither	/man/1/ftest
+neither	/man/1/listen
+neither	/man/1/m4
+neither	/man/1/mash
+neither	/man/1/mprof
+neither	/man/1/prof
+neither	/man/1/rm
+neither	/man/1/sh-std
+neither	/man/10/iar
+neither	/man/10/print
+neither	/man/2/convcs
+neither	/man/2/security-auth
+neither	/man/2/sets
+neither	/man/2/sys-export
+neither	/man/3/fs
+neither	/man/3/ip
+neither	/man/6/keytext
+neither	/man/6/namespace
+neither	/man/8/prep
+neither	/man/8/rip
+neither	/man/9/canvas
+neither	/man/9/menu
+neither	/man/9/see
+neither	/man/9/text
+nelem	/man/10/devattach
+neomagic	/man/3/vga
+neomagichwgc	/man/3/vga
+neq	/man/1/sh-expr
+nest	/man/1/m4
+nested	/man/1/acme
+nested	/man/1/brutus
+nested	/man/1/m4
+nested	/man/10/error
+nested	/man/10/lock
+nested	/man/2/asn1
+nested	/man/2/dividers
+nested	/man/6/colour
+nested	/man/6/man
+nested	/man/9/0intro
+nesting	/man/2/sexprs
+net	/man/1/collab
+net	/man/1/cpu
+net	/man/1/listen
+net	/man/1/netstat
+net	/man/1/rcmd
+net	/man/1/secstore
+net	/man/1/telnet
+net	/man/10/plan9.ini
+net	/man/2/bufio
+net	/man/2/dhcpclient
+net	/man/2/dial
+net	/man/2/ether
+net	/man/2/ip
+net	/man/2/keyring-auth
+net	/man/2/registries
+net	/man/2/secstore
+net	/man/2/sys-dial
+net	/man/3/ether
+net	/man/3/ip
+net	/man/3/plap
+net	/man/3/root
+net	/man/3/tls
+net	/man/4/namespace
+net	/man/4/registry
+net	/man/6/namespace
+net	/man/7/db
+net	/man/8/ai2key
+net	/man/8/bootpd
+net	/man/8/cs
+net	/man/8/dhcp
+net	/man/8/dns
+net	/man/8/getauthinfo
+net	/man/8/init
+net	/man/8/rip
+net	/man/8/svc
+net.alt	/man/2/dial
+net.alt	/man/2/sys-dial
+net.alt	/man/3/root
+net.sh	/man/8/svc
+netaddr	/man/1/collab
+netaddr	/man/2/dial
+netaddr	/man/2/sys-dial
+netaddr	/man/8/cs
+netaddress	/man/1/collab
+netaddress	/man/8/collabsrv
+netbios	/man/2/dhcpclient
+netbsd	/man/10/5cv
+netgear	/man/10/plan9.ini
+netif.c	/man/3/ether
+netif.h	/man/10/0intro
+netinfo	/man/2/dial
+netkey	/man/1/netkey
+netkey.b	/man/1/netkey
+netmkaddr	/man/2/dial
+netscape	/man/1/charon
+netscape	/man/6/colour
+netstat	/man/1/netstat
+netstat.b	/man/1/netstat
+network	/man/1/alphabet-abc
+network	/man/1/alphabet-grid
+network	/man/1/alphabet-main
+network	/man/1/bind
+network	/man/1/charon
+network	/man/1/collab
+network	/man/1/collab-clients
+network	/man/1/cpu
+network	/man/1/dmview
+network	/man/1/emu
+network	/man/1/grid-ns
+network	/man/1/listen
+network	/man/1/miniterm
+network	/man/1/netstat
+network	/man/1/ns
+network	/man/1/secstore
+network	/man/1/spree-join
+network	/man/1/webgrab
+network	/man/10/0intro
+network	/man/10/9load
+network	/man/10/eve
+network	/man/10/odbc
+network	/man/10/plan9.ini
+network	/man/2/dhcpclient
+network	/man/2/dial
+network	/man/2/ip
+network	/man/2/keyring-0intro
+network	/man/2/keyring-auth
+network	/man/2/keyring-certtostr
+network	/man/2/keyring-getmsg
+network	/man/2/keyring-getstring
+network	/man/2/keyring-ipint
+network	/man/2/msgio
+network	/man/2/secstore
+network	/man/2/security-login
+network	/man/2/sexprs
+network	/man/2/spree
+network	/man/2/srv
+network	/man/2/sys-0intro
+network	/man/2/sys-bind
+network	/man/2/sys-dial
+network	/man/2/sys-open
+network	/man/2/sys-pctl
+network	/man/2/sys-stat
+network	/man/2/virgil
+network	/man/3/cmd
+network	/man/3/ip
+network	/man/3/plap
+network	/man/3/ssl
+network	/man/4/ftpfs
+network	/man/4/grid-cpu
+network	/man/4/import
+network	/man/4/namespace
+network	/man/4/registry
+network	/man/4/spree
+network	/man/6/attrdb
+network	/man/6/ndb
+network	/man/6/sexprs
+network	/man/7/db
+network	/man/8/0intro
+network	/man/8/bootpd
+network	/man/8/collabsrv
+network	/man/8/cs
+network	/man/8/dhcp
+network	/man/8/dns
+network	/man/8/getauthinfo
+network	/man/8/init
+network	/man/8/logind
+network	/man/8/ping
+network	/man/8/rip
+network	/man/8/rstyxd
+network	/man/8/signer
+network	/man/8/sntp
+network	/man/8/styxchat
+network	/man/8/svc
+network	/man/8/virgild
+networks	/man/10/9load
+networks	/man/2/dial
+networks	/man/2/keyring-0intro
+networks	/man/2/keyring-certtostr
+networks	/man/2/keyring-getstring
+networks	/man/2/msgio
+networks	/man/2/sys-dial
+networks	/man/3/ip
+networks	/man/8/bootpd
+networks	/man/8/cs
+networks	/man/8/rip
+nevertheless	/man/1/tiny
+nevertheless	/man/10/ar
+new.bundle	/man/1/alphabet-fs
+new.bundle	/man/1/fs
+newchan	/man/10/dev
+newchan	/man/10/newchan
+newclient	/man/1/wm
+newclient	/man/10/styxserver
+newcol	/man/1/acme
+newdot	/man/8/prep
+newencin	/man/3/tls
+newencout	/man/3/tls
+newenv	/man/2/sys-pctl
+newenv	/man/3/env
+newer	/man/1/ftest
+newer	/man/1/mk
+newer	/man/10/mk
+newer	/man/10/plan9.ini
+newer	/man/6/image
+newer.b	/man/1/ftest
+newfd	/man/1/sh-std
+newfd	/man/2/command
+newfd	/man/2/sys-dup
+newfd	/man/2/sys-export
+newfd	/man/2/sys-pctl
+newfgrp	/man/10/kproc
+newfid	/man/10/styx
+newfid	/man/2/styx
+newfid	/man/2/styxservers
+newfid	/man/5/0intro
+newfid	/man/5/walk
+newfid	/man/8/styxchat
+newhashin	/man/3/tls
+newhashout	/man/3/tls
+newid	/man/2/tabs
+newimage	/man/2/draw-display
+newline	/man/1/acme
+newline	/man/1/alphabet-abc
+newline	/man/1/alphabet-grid
+newline	/man/1/alphabet-main
+newline	/man/1/calc
+newline	/man/1/echo
+newline	/man/1/m4
+newline	/man/1/mash
+newline	/man/1/mdb
+newline	/man/1/mk
+newline	/man/1/mux
+newline	/man/1/p
+newline	/man/1/sh
+newline	/man/1/sh-csv
+newline	/man/1/sh-std
+newline	/man/1/tiny
+newline	/man/1/tr
+newline	/man/1/wish
+newline	/man/1/wm-sh
+newline	/man/10/ar
+newline	/man/10/mk
+newline	/man/10/odbc
+newline	/man/10/panic
+newline	/man/2/arg
+newline	/man/2/asn1
+newline	/man/2/bufio
+newline	/man/2/csv
+newline	/man/2/daytime
+newline	/man/2/dialog
+newline	/man/2/keyring-certtostr
+newline	/man/2/keyring-getmsg
+newline	/man/2/msgio
+newline	/man/2/secstore
+newline	/man/2/string
+newline	/man/3/cons
+newline	/man/3/prog
+newline	/man/4/acme
+newline	/man/4/registry
+newline	/man/4/spree
+newline	/man/6/json
+newline	/man/6/keyboard
+newline	/man/6/keytext
+newline	/man/6/regexp
+newline	/man/6/sbl
+newline	/man/6/sexprs
+newline	/man/6/translate
+newline	/man/6/ubfa
+newline	/man/9/canvas
+newline	/man/9/text
+newlines	/man/1/acme
+newlines	/man/1/crypt
+newlines	/man/1/deb
+newlines	/man/1/m4
+newlines	/man/1/sh
+newlines	/man/1/sh-std
+newlines	/man/1/wc
+newlines	/man/10/a.out
+newlines	/man/2/prefab-element
+newlines	/man/2/sexprs
+newlines	/man/3/touch
+newlines	/man/4/acme
+newlines	/man/6/sexprs
+newlines	/man/9/button
+newlines	/man/9/checkbutton
+newlines	/man/9/label
+newlines	/man/9/menubutton
+newlines	/man/9/radiobutton
+newly	/man/1/acme
+newly	/man/1/alphabet-abc
+newly	/man/1/alphabet-grid
+newly	/man/1/mkdir
+newly	/man/1/sh-tk
+newly	/man/10/dev
+newly	/man/10/newchan
+newly	/man/10/odbc
+newly	/man/10/styxserver
+newly	/man/2/diskblocks
+newly	/man/2/keyring-sha1
+newly	/man/2/pop3
+newly	/man/2/prefab-element
+newly	/man/2/security-auth
+newly	/man/2/sh
+newly	/man/2/sys-0intro
+newly	/man/2/sys-pctl
+newly	/man/2/tabs
+newly	/man/2/tkclient
+newly	/man/3/fs
+newly	/man/3/ftl
+newly	/man/3/ip
+newly	/man/3/plap
+newly	/man/3/tls
+newly	/man/4/memfs
+newly	/man/5/open
+newly	/man/8/prep
+newname	/man/3/logfs
+newns	/man/1/nsbuild
+newns	/man/1/sh-std
+newns	/man/2/newns
+newns	/man/2/sys-pctl
+newns	/man/3/fs
+newns	/man/3/root
+newns	/man/6/namespace
+newns.b	/man/2/newns
+newns.m	/man/2/newns
+newobject	/man/2/spree
+newpgrp	/man/1/sh-std
+newpgrp	/man/1/sh-tk
+newpgrp	/man/2/command
+newpgrp	/man/2/sys-pctl
+newpgrp	/man/3/prog
+newprereq	/man/1/mk
+newprereq	/man/10/mk
+newproc	/man/10/acid
+news	/man/1/mux
+newsecret	/man/4/keysrv
+newspaper	/man/1/mux
+newspaper	/man/6/translate
+newspapers	/man/1/mux
+newstack	/man/2/spree-cardlib
+newuser	/man/2/newns
+newuser	/man/8/kfscmd
+newv4	/man/2/ip
+newv6	/man/2/ip
+newviewer	/man/2/drawmux
+newwindow	/man/2/draw-screen
+nexport	/man/10/dynld
+nextafter	/man/2/math-fp
+nexterror	/man/10/error
+nextkey	/man/2/dbm
+nextrange	/man/9/text
+nextrow	/man/7/db
+nexttag	/man/8/styxchat
+nf	/man/10/parsecmd
+nf	/man/2/styxservers
+nfer	/man/1/cprof
+nibble	/man/3/draw
+nice	/man/1/os
+nice	/man/3/cmd
+niceness	/man/3/cmd
+nightly	/man/5/0intro
+nil	/man/1/grep
+nil	/man/1/limbo
+nil	/man/1/sh
+nil	/man/1/sh-file2chan
+nil	/man/1/sh-sexprs
+nil	/man/1/sh-std
+nil	/man/1/stack
+nil	/man/1/yacc
+nil	/man/10/devattach
+nil	/man/10/dynld
+nil	/man/10/error
+nil	/man/10/intrenable
+nil	/man/10/kproc
+nil	/man/10/sleep
+nil	/man/10/styx
+nil	/man/10/styxserver
+nil	/man/2/0intro
+nil	/man/2/alphabet-intro
+nil	/man/2/arg
+nil	/man/2/asn1
+nil	/man/2/attrdb
+nil	/man/2/bufio
+nil	/man/2/cfg
+nil	/man/2/command
+nil	/man/2/complete
+nil	/man/2/convcs
+nil	/man/2/csv
+nil	/man/2/daytime
+nil	/man/2/dbm
+nil	/man/2/debug
+nil	/man/2/devpointer
+nil	/man/2/dhcpclient
+nil	/man/2/dial
+nil	/man/2/dialog
+nil	/man/2/dict
+nil	/man/2/dis
+nil	/man/2/diskblocks
+nil	/man/2/disks
+nil	/man/2/draw-0intro
+nil	/man/2/draw-context
+nil	/man/2/draw-display
+nil	/man/2/draw-example
+nil	/man/2/draw-image
+nil	/man/2/draw-screen
+nil	/man/2/env
+nil	/man/2/ether
+nil	/man/2/exception
+nil	/man/2/factotum
+nil	/man/2/format
+nil	/man/2/fsproto
+nil	/man/2/hash
+nil	/man/2/ida
+nil	/man/2/imagefile
+nil	/man/2/ip
+nil	/man/2/ir
+nil	/man/2/itslib
+nil	/man/2/json
+nil	/man/2/keyring-auth
+nil	/man/2/keyring-certtostr
+nil	/man/2/keyring-crypt
+nil	/man/2/keyring-gensk
+nil	/man/2/keyring-getmsg
+nil	/man/2/keyring-getstring
+nil	/man/2/keyring-ipint
+nil	/man/2/keyring-sha1
+nil	/man/2/keyset
+nil	/man/2/lists
+nil	/man/2/mpeg
+nil	/man/2/msgio
+nil	/man/2/names
+nil	/man/2/newns
+nil	/man/2/palmfile
+nil	/man/2/plumbmsg
+nil	/man/2/pop3
+nil	/man/2/popup
+nil	/man/2/prefab-compound
+nil	/man/2/prefab-element
+nil	/man/2/regex
+nil	/man/2/registries
+nil	/man/2/rfc822
+nil	/man/2/scsiio
+nil	/man/2/secstore
+nil	/man/2/security-auth
+nil	/man/2/security-login
+nil	/man/2/security-ssl
+nil	/man/2/selectfile
+nil	/man/2/sexprs
+nil	/man/2/sh
+nil	/man/2/smtp
+nil	/man/2/spki
+nil	/man/2/spki-verifier
+nil	/man/2/spree
+nil	/man/2/spree-allow
+nil	/man/2/spree-cardlib
+nil	/man/2/spree-gather
+nil	/man/2/spree-objstore
+nil	/man/2/string
+nil	/man/2/styx
+nil	/man/2/styxconv
+nil	/man/2/styxflush
+nil	/man/2/styxpersist
+nil	/man/2/styxservers
+nil	/man/2/styxservers-nametree
+nil	/man/2/sys-bind
+nil	/man/2/sys-dial
+nil	/man/2/sys-dirread
+nil	/man/2/sys-dup
+nil	/man/2/sys-export
+nil	/man/2/sys-fauth
+nil	/man/2/sys-fd2path
+nil	/man/2/sys-file2chan
+nil	/man/2/sys-open
+nil	/man/2/sys-self
+nil	/man/2/sys-stat
+nil	/man/2/sys-tokenize
+nil	/man/2/tftp
+nil	/man/2/tk
+nil	/man/2/tkclient
+nil	/man/2/translate
+nil	/man/2/ubfa
+nil	/man/2/virgil
+nil	/man/2/w3c-css
+nil	/man/2/w3c-uris
+nil	/man/2/w3c-xpointers
+nil	/man/2/wait
+nil	/man/2/wmclient
+nil	/man/2/wmlib
+nil	/man/2/wmsrv
+nil	/man/2/xml
+nil	/man/3/arch
+nil	/man/3/cap
+nil	/man/3/env
+nil	/man/3/prog
+nil	/man/6/sbl
+nil	/man/7/db
+nil,nil	/man/2/styxservers
+nimport	/man/10/dynld
+nine	/man/9/canvas
+ninf	/man/2/math-fp
+nist	/man/8/ai2key
+nk	/man/2/bloomfilter
+nl	/man/2/sh
+nlab	/man/6/dis
+nlines	/man/1/tail
+nm	/man/10/inm
+nmatch	/man/2/complete
+nmembers	/man/2/spree-cardlib
+nname	/man/10/dev
+nname	/man/10/devattach
+nnnn	/man/5/version
+noaddr	/man/2/ip
+nobody	/man/2/keyring-auth
+nobody	/man/3/cmd
+nobroken	/man/3/cons
+nocase	/man/9/text
+noctl	/man/2/disks
+node	/man/1/alphabet-abc
+node	/man/1/alphabet-grid
+node	/man/2/ip
+node	/man/2/w3c-xpointers
+node	/man/3/kprof
+node1	/man/1/alphabet-abc
+node1	/man/1/alphabet-grid
+node2	/man/1/alphabet-abc
+node2	/man/1/alphabet-grid
+nodes	/man/2/ip
+nodetest	/man/2/w3c-xpointers
+nodetype	/man/2/w3c-xpointers
+nodev	/man/6/namespace
+nodevs	/man/1/sh-std
+nodevs	/man/2/sys-pctl
+nodevs	/man/6/namespace
+nodots	/man/2/w3c-uris
+nodummyrr	/man/10/plan9.ini
+noecho	/man/4/acme
+noerror	/man/1/dd
+noetherprobe	/man/10/plan9.ini
+nofid	/man/2/styx
+nofid	/man/5/attach
+nofid	/man/8/styxchat
+nofill	/man/2/draw-display
+nofill	/man/6/man
+noir	/man/10/odbc
+noise	/man/2/imagefile
+nomark	/man/4/acme
+nomce	/man/10/plan9.ini
+nominal	/man/9/grid
+nominally	/man/10/styxserver
+nomp	/man/10/plan9.ini
+non	/man/1/acme
+non	/man/1/calc
+non	/man/1/charon
+non	/man/1/cp
+non	/man/1/fc
+non	/man/1/freq
+non	/man/1/ftest
+non	/man/1/grep
+non	/man/1/kill
+non	/man/1/mash
+non	/man/1/mash-tk
+non	/man/1/mk
+non	/man/1/read
+non	/man/1/sh
+non	/man/1/sh-expr
+non	/man/1/sh-file2chan
+non	/man/1/sh-std
+non	/man/1/sh-string
+non	/man/1/sh-tk
+non	/man/1/tiny
+non	/man/1/tsort
+non	/man/1/wm
+non	/man/10/2c
+non	/man/10/5coff
+non	/man/10/9load
+non	/man/10/c2l
+non	/man/10/dev
+non	/man/10/devattach
+non	/man/10/dmainit
+non	/man/10/dynld
+non	/man/10/error
+non	/man/10/getfields
+non	/man/10/kbdputc
+non	/man/10/kproc
+non	/man/10/lock
+non	/man/10/malloc
+non	/man/10/mk
+non	/man/10/newchan
+non	/man/10/plan9.ini
+non	/man/10/print
+non	/man/10/qio
+non	/man/10/qlock
+non	/man/10/sleep
+non	/man/10/splhi
+non	/man/10/styxserver
+non	/man/2/asn1
+non	/man/2/convcs
+non	/man/2/dbm
+non	/man/2/debug
+non	/man/2/dhcpclient
+non	/man/2/dial
+non	/man/2/disks
+non	/man/2/draw-display
+non	/man/2/draw-image
+non	/man/2/draw-point
+non	/man/2/draw-pointer
+non	/man/2/draw-rect
+non	/man/2/draw-screen
+non	/man/2/factotum
+non	/man/2/ip
+non	/man/2/ir
+non	/man/2/json
+non	/man/2/keyring-auth
+non	/man/2/keyring-crypt
+non	/man/2/keyring-getstring
+non	/man/2/keyring-ipint
+non	/man/2/keyring-rc4
+non	/man/2/keyring-sha1
+non	/man/2/keyset
+non	/man/2/lists
+non	/man/2/math-fp
+non	/man/2/msgio
+non	/man/2/palmfile
+non	/man/2/plumbmsg
+non	/man/2/prefab-element
+non	/man/2/print
+non	/man/2/rand
+non	/man/2/regex
+non	/man/2/registries
+non	/man/2/rfc822
+non	/man/2/scsiio
+non	/man/2/security-auth
+non	/man/2/sets
+non	/man/2/sexprs
+non	/man/2/sh
+non	/man/2/spree
+non	/man/2/spree-allow
+non	/man/2/spree-cardlib
+non	/man/2/spree-gather
+non	/man/2/stringinttab
+non	/man/2/styxflush
+non	/man/2/styxpersist
+non	/man/2/styxservers
+non	/man/2/sys-0intro
+non	/man/2/sys-byte2char
+non	/man/2/sys-dial
+non	/man/2/sys-export
+non	/man/2/sys-read
+non	/man/2/sys-tokenize
+non	/man/2/tftp
+non	/man/2/translate
+non	/man/2/ubfa
+non	/man/2/w3c-css
+non	/man/2/w3c-xpointers
+non	/man/2/wmlib
+non	/man/2/wmsrv
+non	/man/2/xml
+non	/man/3/cmd
+non	/man/3/cons
+non	/man/3/dbg
+non	/man/3/draw
+non	/man/3/eia
+non	/man/3/ip
+non	/man/3/logfs
+non	/man/3/prof
+non	/man/3/rtc
+non	/man/3/tinyfs
+non	/man/3/tls
+non	/man/3/tv
+non	/man/4/acme
+non	/man/4/factotum
+non	/man/4/keysrv
+non	/man/4/logfile
+non	/man/4/namespace
+non	/man/4/registry
+non	/man/4/spree
+non	/man/5/read
+non	/man/5/stat
+non	/man/6/audio
+non	/man/6/colour
+non	/man/6/dis
+non	/man/6/font
+non	/man/6/man
+non	/man/6/namespace
+non	/man/6/proto
+non	/man/6/regexp
+non	/man/6/sexprs
+non	/man/6/ubfa
+non	/man/6/utf
+non	/man/7/db
+non	/man/8/create
+non	/man/8/kfscmd
+non	/man/8/prep
+non	/man/8/register
+non	/man/9/1copyright
+non	/man/9/button
+non	/man/9/canvas
+non	/man/9/checkbutton
+non	/man/9/grid
+non	/man/9/menu
+non	/man/9/menubutton
+non	/man/9/options
+non	/man/9/pack
+non	/man/9/radiobutton
+non	/man/9/send
+non	/man/9/text
+nonassoc	/man/1/yacc
+nonblock	/man/10/qio
+nonblocking	/man/3/ip
+noncommercial	/man/2/keyring-crypt
+none	/man/1/acme
+none	/man/1/bind
+none	/man/1/collab
+none	/man/1/cprof
+none	/man/1/cpu
+none	/man/1/ftest
+none	/man/1/m4
+none	/man/1/mash
+none	/man/1/math-misc
+none	/man/1/mprof
+none	/man/1/prof
+none	/man/1/rcmd
+none	/man/1/sh
+none	/man/1/sh-std
+none	/man/1/uuencode
+none	/man/10/plan9.ini
+none	/man/10/rune
+none	/man/10/strcat
+none	/man/2/bloomfilter
+none	/man/2/bufio
+none	/man/2/dbm
+none	/man/2/hash
+none	/man/2/json
+none	/man/2/keyring-0intro
+none	/man/2/lists
+none	/man/2/readdir
+none	/man/2/security-auth
+none	/man/2/sets
+none	/man/2/spree
+none	/man/2/spree-objstore
+none	/man/2/styxservers
+none	/man/2/w3c-xpointers
+none	/man/2/wmsrv
+none	/man/3/flash
+none	/man/3/ip
+none	/man/3/mnt
+none	/man/3/prog
+none	/man/3/sign
+none	/man/3/tv
+none	/man/4/factotum
+none	/man/4/kfs
+none	/man/4/lockfs
+none	/man/4/logfile
+none	/man/4/namespace
+none	/man/4/registry
+none	/man/5/stat
+none	/man/6/dis
+none	/man/7/dbsrv
+none	/man/8/bootpd
+none	/man/8/rstyxd
+none	/man/8/styxchat
+none	/man/9/button
+none	/man/9/canvas
+none	/man/9/checkbutton
+none	/man/9/grab
+none	/man/9/menu
+none	/man/9/menubutton
+none	/man/9/pack
+none	/man/9/radiobutton
+none	/man/9/scale
+none	/man/9/text
+nonempty	/man/10/acid
+nonempty	/man/2/filepat
+nonempty	/man/6/regexp
+nonetheless	/man/2/spree-cardlib
+nonexistent	/man/1/mk
+nonexistent	/man/10/mk
+noninitial	/man/1/tr
+nop	/man/3/0intro
+nopcirouting	/man/10/plan9.ini
+noproxy	/man/1/charon
+noproxy	/man/1/webgrab
+noproxydoms	/man/1/charon
+noproxydoms	/man/1/webgrab
+nor	/man/1/calc
+nor	/man/1/charon
+nor	/man/1/m4
+nor	/man/1/mprof
+nor	/man/1/prof
+nor	/man/1/rm
+nor	/man/10/lock
+nor	/man/10/print
+nor	/man/2/convcs
+nor	/man/2/prefab-element
+nor	/man/2/security-auth
+nor	/man/2/sets
+nor	/man/2/sys-export
+nor	/man/3/cmd
+nor	/man/3/flash
+nor	/man/3/fs
+nor	/man/3/ip
+nor	/man/6/keytext
+nor	/man/6/namespace
+nor	/man/8/prep
+nor	/man/8/rip
+nor	/man/9/canvas
+nor	/man/9/menu
+norm	/man/2/math-linalg
+norm1	/man/2/math-linalg
+norm2	/man/2/math-linalg
+normalised	/man/2/convcs
+norms	/man/2/math-linalg
+north	/man/9/grid
+northeast	/man/2/selectfile
+northing	/man/2/geodesy
+northings	/man/2/geodesy
+northwest	/man/2/dialog
+noscroll	/man/4/acme
+noscsireset	/man/10/plan9.ini
+notably	/man/10/plan9.ini
+notably	/man/2/dbm
+notably	/man/2/sys-0intro
+notably	/man/4/dossrv
+notably	/man/9/0intro
+notacolor	/man/2/draw-display
+notafter	/man/2/spki
+notag	/man/2/styx
+notag	/man/5/0intro
+notag	/man/5/version
+notag	/man/8/styxchat
+notation	/man/1/0intro
+notation	/man/1/alphabet-main
+notation	/man/1/fc
+notation	/man/1/sh-expr
+notation	/man/10/2c
+notation	/man/10/acid
+notation	/man/2/asn1
+notation	/man/2/filepat
+notation	/man/2/json
+notation	/man/3/dbg
+notation	/man/3/ip
+notation	/man/5/0intro
+notation	/man/6/json
+notation	/man/6/regexp
+notation	/man/9/types
+notbefore	/man/2/spki
+note	/man/1/acme
+note	/man/1/alphabet-fs
+note	/man/1/ar
+note	/man/1/bind
+note	/man/1/fc
+note	/man/1/fs
+note	/man/1/idea
+note	/man/1/m4
+note	/man/1/mprof
+note	/man/1/prof
+note	/man/1/pwd
+note	/man/1/sh
+note	/man/1/sh-alphabet
+note	/man/1/sh-expr
+note	/man/1/sh-file2chan
+note	/man/1/sh-sexprs
+note	/man/1/sh-std
+note	/man/1/tail
+note	/man/1/timestamp
+note	/man/1/tktester
+note	/man/1/wm-sh
+note	/man/10/2c
+note	/man/10/acid
+note	/man/10/dev
+note	/man/10/devattach
+note	/man/10/dynld
+note	/man/10/eve
+note	/man/10/intrenable
+note	/man/10/qio
+note	/man/10/strcat
+note	/man/10/styxserver
+note	/man/2/0intro
+note	/man/2/alphabet-intro
+note	/man/2/asn1
+note	/man/2/bufio
+note	/man/2/cfg
+note	/man/2/command
+note	/man/2/convcs
+note	/man/2/csv
+note	/man/2/daytime
+note	/man/2/geodesy
+note	/man/2/palmfile
+note	/man/2/plumbmsg
+note	/man/2/pop3
+note	/man/2/prefab-style
+note	/man/2/security-auth
+note	/man/2/sets
+note	/man/2/spree
+note	/man/2/styx
+note	/man/2/styxpersist
+note	/man/2/styxservers
+note	/man/2/styxservers-nametree
+note	/man/2/sys-dial
+note	/man/2/sys-self
+note	/man/2/translate
+note	/man/2/w3c-uris
+note	/man/2/wmsrv
+note	/man/2/xml
+note	/man/3/dbg
+note	/man/3/draw
+note	/man/3/dup
+note	/man/3/flash
+note	/man/3/ip
+note	/man/3/logfs
+note	/man/3/pnp
+note	/man/3/srv9
+note	/man/3/usb
+note	/man/3/vga
+note	/man/4/dbfs
+note	/man/4/grid-cpu
+note	/man/4/keyfs
+note	/man/4/registry
+note	/man/4/spree
+note	/man/4/vacfs
+note	/man/5/stat
+note	/man/6/attrdb
+note	/man/6/font
+note	/man/6/json
+note	/man/6/keyboard
+note	/man/6/plumbing
+note	/man/6/sexprs
+note	/man/6/translate
+note	/man/6/ubfa
+note	/man/8/changelogin
+note	/man/8/getauthinfo
+note	/man/8/kfscmd
+note	/man/8/signer
+note	/man/8/svc
+note	/man/9/canvas
+note	/man/9/menu
+note	/man/9/text
+notebook	/man/2/tabs
+noted	/man/1/crypt
+noted	/man/10/dynld
+noted	/man/2/sys-0intro
+noted	/man/6/ubfa
+noted	/man/9/canvas
+notes	/man/1/collab-clients
+notes	/man/2/imagefile
+notes	/man/3/logfs
+notes	/man/8/rip
+notice	/man/1/mash-tk
+notice	/man/10/plan9.ini
+notice	/man/10/qio
+notice	/man/9/0intro
+notice	/man/9/1copyright
+noticeable	/man/9/text
+notices	/man/9/1copyright
+notification	/man/2/spree
+notification	/man/2/wmsrv
+notification	/man/9/options
+notification	/man/9/scrollbar
+notifications	/man/2/dhcpclient
+notifications	/man/2/wmsrv
+notifications	/man/9/options
+notified	/man/2/draw-image
+notified	/man/2/spree
+notified	/man/2/timers
+notified	/man/3/dbg
+notifies	/man/2/styxflush
+notifies	/man/9/scrollbar
+notify	/man/10/qio
+notify	/man/2/mpeg
+notify	/man/2/spree
+notify	/man/3/tls
+notify	/man/9/scale
+notifyleader	/man/2/exception
+notifyleader	/man/3/prog
+notion	/man/1/diff
+notion	/man/2/security-0intro
+notion	/man/9/canvas
+notional	/man/10/devattach
+notionally	/man/10/master
+notionally	/man/10/styxserver
+notionally	/man/2/sets
+notionally	/man/4/ftpfs
+notionally	/man/8/applylog
+notquiterandom	/man/2/security-random
+notquiterandom	/man/3/cons
+notstop	/man/3/dbg
+notwithstanding	/man/9/1copyright
+nousbprobe	/man/10/plan9.ini
+nov	/man/2/daytime
+novel	/man/1/0intro
+novelties	/man/1/webgrab
+novga	/man/10/plan9.ini
+noworld	/man/4/kfs
+nping	/man/8/ping
+nproc	/man/1/mk
+nproc	/man/10/mk
+nptr	/man/10/atoi
+nq	/man/10/styxserver
+nqid	/man/10/devattach
+nr	/man/1/cp
+nr	/man/1/sort
+nr	/man/2/ida
+nr	/man/2/math-fp
+nread	/man/10/styxserver
+nrec	/man/2/palmfile
+nresrv	/man/8/prep
+ns	/man/1/grid-monitor
+ns	/man/1/grid-ns
+ns	/man/1/grid-session
+ns	/man/1/ns
+ns	/man/2/dividers
+ns	/man/2/newns
+ns	/man/2/sets
+ns	/man/2/sys-0intro
+ns	/man/2/sys-fd2path
+ns	/man/2/w3c-xpointers
+ns	/man/3/prog
+ns	/man/4/grid-cpu
+ns	/man/6/ndb
+ns	/man/8/dns
+ns.b	/man/1/grid-ns
+ns.b	/man/1/grid-session
+ns.b	/man/1/ns
+nsbuild	/man/1/nsbuild
+nsbuild	/man/6/namespace
+nsbuild.b	/man/1/nsbuild
+nsfile	/man/1/logon
+nsfile	/man/2/newns
+nsgrp	/man/3/prog
+nstat	/man/10/styx
+nstatus	/man/3/fpga
+nsym	/man/10/dynld
+nt	/man/1/unicode
+nt	/man/10/5cv
+nt	/man/10/conf
+nt	/man/10/ntsrv
+nt	/man/10/styxserver
+nt	/man/3/fs
+nt	/man/4/namespace
+nt	/man/7/dbsrv
+ntab	/man/10/devattach
+ntentry	/man/2/disks
+ntest	/man/1/sh-expr
+ntfs	/man/3/fs
+ntfs	/man/8/prep
+nthreads	/man/1/charon
+ntp	/man/8/sntp
+ntracks	/man/7/cddb
+ntsc	/man/2/draw-image
+ntsc	/man/3/tv
+ntsc	/man/3/vid
+ntsc's	/man/6/colour
+ntsrv	/man/10/ntsrv
+ntsrv.exe	/man/10/ntsrv
+ntsrv4	/man/10/ntsrv
+nuances	/man/10/devattach
+nul	/man/10/a.out
+nul	/man/10/c2l
+nul	/man/10/print
+nul	/man/10/readnum
+nul	/man/10/rune
+nul	/man/2/draw-image
+nul	/man/5/0intro
+nul	/man/6/font
+null	/man/1/acme
+null	/man/1/charon
+null	/man/1/fc
+null	/man/1/m4
+null	/man/1/mash
+null	/man/1/mk
+null	/man/1/os
+null	/man/1/sh
+null	/man/1/sh-regex
+null	/man/1/sh-std
+null	/man/10/a.out
+null	/man/10/allocb
+null	/man/10/c2l
+null	/man/10/dev
+null	/man/10/eve
+null	/man/10/getfields
+null	/man/10/malloc
+null	/man/10/mk
+null	/man/10/parsecmd
+null	/man/10/plan9.ini
+null	/man/10/qio
+null	/man/10/rune
+null	/man/10/strcat
+null	/man/10/xalloc
+null	/man/2/asn1
+null	/man/2/debug
+null	/man/2/json
+null	/man/2/names
+null	/man/2/sys-pctl
+null	/man/3/cons
+null	/man/3/dbg
+null	/man/4/acme
+null	/man/4/factotum
+null	/man/6/json
+null	/man/6/ubfa
+null	/man/8/styxmon
+nulldir	/man/2/sys-stat
+num	/man/1/math-misc
+num	/man/1/sh-expr
+num	/man/10/c2l
+num	/man/2/asn1
+num	/man/6/colour
+num	/man/6/users
+numbered	/man/1/itest
+numbered	/man/1/m4
+numbered	/man/1/mash-tk
+numbered	/man/10/0intro
+numbered	/man/10/odbc
+numbered	/man/2/spree-cardlib
+numbered	/man/3/cmd
+numbered	/man/3/flash
+numbered	/man/3/ip
+numbered	/man/3/plap
+numbered	/man/3/prof
+numbered	/man/3/prog
+numbered	/man/3/ssl
+numbered	/man/3/tls
+numbered	/man/4/acme
+numbered	/man/4/dbfs
+numbered	/man/6/dis
+numbered	/man/6/image
+numbered	/man/8/collabsrv
+numbered	/man/8/prep
+numbered	/man/9/grid
+numbered	/man/9/text
+numbering	/man/3/gpio
+numbering	/man/4/dbfs
+numbering	/man/9/text
+numblocks	/man/1/zeros
+numeral	/man/1/acme
+numeral	/man/2/ir
+numerator	/man/6/keyboard
+numeric	/man/1/ar
+numeric	/man/1/charon
+numeric	/man/1/kill
+numeric	/man/1/look
+numeric	/man/1/sh-tk
+numeric	/man/1/sort
+numeric	/man/1/unicode
+numeric	/man/10/iar
+numeric	/man/10/plan9.ini
+numeric	/man/10/print
+numeric	/man/2/json
+numeric	/man/2/styxservers
+numeric	/man/2/sys-print
+numeric	/man/2/virgil
+numeric	/man/3/cons
+numeric	/man/3/flash
+numeric	/man/4/tarfs
+numeric	/man/6/font
+numeric	/man/7/db
+numeric	/man/8/collabsrv
+numeric	/man/8/prep
+numeric	/man/9/text
+numeric	/man/9/types
+numerical	/man/1/m4
+numerical	/man/10/9load
+numerical	/man/2/sys-pctl
+numerical	/man/9/canvas
+numerical	/man/9/entry
+numerical	/man/9/grid
+numerical	/man/9/listbox
+numerical	/man/9/menu
+numerical	/man/9/scale
+numerical	/man/9/text
+numerically	/man/1/chmod
+numerically	/man/1/sh-expr
+numerically	/man/9/menu
+numerics	/man/2/math-0intro
+numericstring	/man/2/asn1
+nums	/man/2/asn1
+numsize	/man/10/readnum
+nuova	/man/9/0intro
+nuova	/man/9/1copyright
+nvfs	/man/3/root
+nvfs	/man/3/tinyfs
+nvfs	/man/4/namespace
+nvfs	/man/8/manufacture
+nvfs	/man/8/register
+nvr	/man/10/plan9.ini
+nvram	/man/3/rtc
+nvram	/man/3/tinyfs
+nvram	/man/8/prep
+nvram	/man/8/touchcal
+nw	/man/1/cp
+nw	/man/9/options
+nw	/man/9/types
+nwname	/man/10/styx
+nwname	/man/5/0intro
+nwname	/man/5/walk
+nwqid	/man/10/styx
+nwqid	/man/5/0intro
+nwqid	/man/5/walk
+o'clock	/man/9/canvas
+o:r	/man/10/mk
+oand	/man/2/w3c-xpointers
+obey	/man/3/eia
+obj	/man/1/limbo
+obj	/man/1/mk
+obj	/man/10/2a
+obj	/man/10/2c
+obj	/man/10/dynld
+obj	/man/10/mk
+obj	/man/2/spree
+obj	/man/2/spree-allow
+obj	/man/2/spree-cardlib
+obj	/man/2/ubfa
+object	/man/1/ar
+object	/man/1/asm
+object	/man/1/bind
+object	/man/1/disdep
+object	/man/1/limbo
+object	/man/1/mash
+object	/man/1/tiny
+object	/man/10/0intro
+object	/man/10/2a
+object	/man/10/2c
+object	/man/10/2l
+object	/man/10/a.out
+object	/man/10/ar
+object	/man/10/conf
+object	/man/10/dynld
+object	/man/10/iar
+object	/man/10/inm
+object	/man/2/asn1
+object	/man/2/dis
+object	/man/2/draw-display
+object	/man/2/draw-font
+object	/man/2/draw-screen
+object	/man/2/json
+object	/man/2/prefab-0intro
+object	/man/2/prefab-element
+object	/man/2/spki
+object	/man/2/spree
+object	/man/2/spree-allow
+object	/man/2/spree-cardlib
+object	/man/2/spree-gather
+object	/man/2/spree-objstore
+object	/man/2/sys-0intro
+object	/man/2/sys-bind
+object	/man/3/draw
+object	/man/4/spree
+object	/man/6/dis
+object	/man/6/json
+object	/man/6/sbl
+object's	/man/2/spree
+object's	/man/4/spree
+objectdescriptor	/man/2/asn1
+objective	/man/2/security-0intro
+objects	/man/1/charon
+objects	/man/10/print
+objects	/man/2/draw-0intro
+objects	/man/2/draw-display
+objects	/man/2/draw-image
+objects	/man/2/prefab-0intro
+objects	/man/2/prefab-element
+objects	/man/2/prefab-environ
+objects	/man/2/spree
+objects	/man/2/spree-cardlib
+objects	/man/2/spree-objstore
+objects	/man/2/sys-print
+objects	/man/4/spree
+objects	/man/6/json
+objects	/man/9/canvas
+objid	/man/2/asn1
+objid	/man/4/spree
+objs	/man/2/spree
+objstore	/man/2/spree
+objstore	/man/2/spree-allow
+objstore	/man/2/spree-cardlib
+objstore	/man/2/spree-objstore
+objstore.m	/man/2/spree-objstore
+objtag	/man/2/ubfa
+objtype	/man/10/2a
+objtype	/man/10/2c
+objtype	/man/10/2l
+objtype	/man/10/acid
+objtype	/man/10/c2l
+objtype	/man/10/conf
+objtype	/man/10/memory
+objtype	/man/10/mk
+objtype	/man/10/strcat
+objtype	/man/2/spree
+objtype	/man/4/namespace
+objtype	/man/4/spree
+obligation	/man/9/1copyright
+oblivion	/man/3/ip
+obootfile	/man/2/dhcpclient
+obs	/man/1/dd
+obscure	/man/9/canvas
+obscure	/man/9/lower
+obscure	/man/9/raise
+obscured	/man/2/draw-screen
+obscured	/man/9/lower
+obscured	/man/9/raise
+obscuring	/man/2/draw-image
+observe	/man/9/entry
+obsolete	/man/10/error
+obsolete	/man/3/ftl
+obsolete	/man/3/ip
+obsolete	/man/6/sbl
+obsolete	/man/9/text
+obtain	/man/1/sh-file2chan
+obtain	/man/10/devattach
+obtain	/man/10/kproc
+obtain	/man/10/qlock
+obtain	/man/10/rune
+obtain	/man/10/sleep
+obtain	/man/2/dhcpclient
+obtain	/man/2/dial
+obtain	/man/2/exception
+obtain	/man/2/ida
+obtain	/man/2/lock
+obtain	/man/2/scsiio
+obtain	/man/2/security-0intro
+obtain	/man/2/styx
+obtain	/man/2/styxservers
+obtain	/man/2/sys-self
+obtain	/man/3/pnp
+obtain	/man/3/touch
+obtain	/man/4/palmsrv
+obtain	/man/6/keys
+obtain	/man/6/sbl
+obtain	/man/8/bootpd
+obtain	/man/8/collabsrv
+obtain	/man/8/getauthinfo
+obtain	/man/8/register
+obtain	/man/8/svc
+obtained	/man/1/charon
+obtained	/man/1/date
+obtained	/man/1/netstat
+obtained	/man/1/passwd
+obtained	/man/1/wm
+obtained	/man/10/acid
+obtained	/man/10/error
+obtained	/man/10/odbc
+obtained	/man/10/print
+obtained	/man/10/strcat
+obtained	/man/2/asn1
+obtained	/man/2/bufio
+obtained	/man/2/daytime
+obtained	/man/2/draw-screen
+obtained	/man/2/filter-slip
+obtained	/man/2/keyring-ipint
+obtained	/man/2/keyset
+obtained	/man/2/newns
+obtained	/man/2/security-random
+obtained	/man/2/sh
+obtained	/man/2/styxservers
+obtained	/man/2/sys-0intro
+obtained	/man/2/sys-print
+obtained	/man/2/tk
+obtained	/man/3/env
+obtained	/man/3/ip
+obtained	/man/3/touch
+obtained	/man/4/lockfs
+obtained	/man/8/getauthinfo
+obtaining	/man/10/odbc
+obtaining	/man/2/security-0intro
+obtaining	/man/8/register
+obtains	/man/10/acid
+obtains	/man/2/factotum
+obtains	/man/2/lock
+obtains	/man/2/security-0intro
+obtains	/man/2/styxservers
+obtains	/man/4/lockfs
+obtains	/man/8/register
+obviate	/man/10/2c
+obvious	/man/1/charon
+obvious	/man/1/miniterm
+obvious	/man/2/dis
+obvious	/man/2/security-0intro
+obvious	/man/4/tarfs
+obvious	/man/5/walk
+obvious	/man/6/keyboard
+obviously	/man/1/crypt
+occasion	/man/10/error
+occasionally	/man/1/miniterm
+occasions	/man/2/sys-dup
+occupied	/man/10/styx
+occupied	/man/2/prefab-element
+occupied	/man/3/pbus
+occupied	/man/9/canvas
+occupied	/man/9/grid
+occupied	/man/9/text
+occupies	/man/10/a.out
+occupies	/man/2/draw-0intro
+occupies	/man/2/prefab-0intro
+occupies	/man/2/prefab-compound
+occupies	/man/2/prefab-element
+occupies	/man/9/grid
+occupies	/man/9/text
+occupy	/man/2/draw-font
+occupy	/man/2/prefab-element
+occupy	/man/9/button
+occupy	/man/9/checkbutton
+occupy	/man/9/label
+occupy	/man/9/menubutton
+occupy	/man/9/radiobutton
+occupying	/man/10/a.out
+occupying	/man/2/prefab-element
+occur	/man/1/acme
+occur	/man/1/m4
+occur	/man/1/sh
+occur	/man/1/tr
+occur	/man/10/memory
+occur	/man/10/rune
+occur	/man/10/strcat
+occur	/man/2/string
+occur	/man/2/xml
+occur	/man/3/draw
+occur	/man/3/prog
+occur	/man/4/acme
+occur	/man/8/collabsrv
+occur	/man/8/create
+occur	/man/9/bind
+occur	/man/9/button
+occur	/man/9/canvas
+occur	/man/9/checkbutton
+occur	/man/9/menubutton
+occur	/man/9/options
+occur	/man/9/radiobutton
+occur	/man/9/text
+occurred	/man/1/grep
+occurred	/man/1/sh
+occurred	/man/10/sleep
+occurred	/man/2/dhcpclient
+occurred	/man/2/exception
+occurred	/man/2/keyring-getstring
+occurred	/man/2/msgio
+occurred	/man/2/prof
+occurred	/man/2/registries
+occurred	/man/2/sh
+occurred	/man/2/sys-0intro
+occurred	/man/2/sys-file2chan
+occurred	/man/2/translate
+occurred	/man/2/xml
+occurred	/man/3/prog
+occurred	/man/7/db
+occurrence	/man/1/acme
+occurrence	/man/1/brutus
+occurrence	/man/1/m4
+occurrence	/man/1/mk
+occurrence	/man/1/sh-regex
+occurrence	/man/10/intrenable
+occurrence	/man/10/memory
+occurrence	/man/10/mk
+occurrence	/man/10/rune
+occurrence	/man/10/strcat
+occurrences	/man/1/acme
+occurrences	/man/1/mk
+occurrences	/man/1/xd
+occurrences	/man/10/mk
+occurring	/man/1/ls
+occurring	/man/6/image
+occurs	/man/1/acme
+occurs	/man/1/charon
+occurs	/man/1/emu
+occurs	/man/1/os
+occurs	/man/1/sh-alphabet
+occurs	/man/1/yacc
+occurs	/man/10/intrenable
+occurs	/man/2/asn1
+occurs	/man/2/bufio
+occurs	/man/2/debug
+occurs	/man/2/dis
+occurs	/man/2/fsproto
+occurs	/man/2/ida
+occurs	/man/2/json
+occurs	/man/2/lists
+occurs	/man/2/palmfile
+occurs	/man/2/registries
+occurs	/man/2/sh
+occurs	/man/2/styxpersist
+occurs	/man/2/sys-byte2char
+occurs	/man/2/sys-fd2path
+occurs	/man/2/sys-file2chan
+occurs	/man/2/sys-fversion
+occurs	/man/2/sys-iounit
+occurs	/man/2/sys-open
+occurs	/man/2/ubfa
+occurs	/man/2/xml
+occurs	/man/3/ip
+occurs	/man/3/prog
+occurs	/man/8/plumber
+occurs	/man/9/bind
+occurs	/man/9/button
+occurs	/man/9/canvas
+occurs	/man/9/destroy
+occurs	/man/9/options
+occurs	/man/9/scale
+occurs	/man/9/text
+ocs	/man/1/tcs
+oct	/man/6/proto
+octal	/man/1/chmod
+octal	/man/1/cmp
+octal	/man/1/fc
+octal	/man/1/freq
+octal	/man/1/mdb
+octal	/man/1/tr
+octal	/man/1/xd
+octal	/man/10/2l
+octal	/man/10/ar
+octal	/man/10/atoi
+octal	/man/10/print
+octal	/man/2/sys-print
+octal	/man/3/dup
+octal	/man/3/ether
+octal	/man/3/flash
+octal	/man/3/lpt
+octal	/man/3/prog
+octal	/man/6/font
+octal	/man/6/proto
+octal	/man/6/sexprs
+octal	/man/8/kfscmd
+octet	/man/2/asn1
+octet	/man/2/rfc822
+octet	/man/8/httpd
+octets	/man/2/asn1
+octetstring	/man/2/asn1
+odbc	/man/10/odbc
+odbc	/man/7/db
+odbc	/man/7/dbsrv
+odbc.c	/man/10/odbc
+odbc.exe	/man/10/odbc
+odd	/man/10/plan9.ini
+odd	/man/2/draw-image
+odd	/man/3/draw
+odd	/man/3/eia
+odd	/man/6/colour
+odd	/man/9/canvas
+odiv	/man/2/w3c-xpointers
+odnsserver	/man/2/dhcpclient
+odomainname	/man/2/dhcpclient
+odown	/man/2/spree-cardlib
+oeb	/man/1/ebook
+oexcl	/man/2/sys-open
+oexcl	/man/5/open
+oexec	/man/10/devattach
+oexec	/man/5/open
+offending	/man/1/emu
+offending	/man/3/tls
+offer	/man/1/ftree
+offer	/man/1/miniterm
+offer	/man/1/passwd
+offer	/man/10/error
+offer	/man/8/dhcp
+offer	/man/8/dns
+offered	/man/1/bind
+offered	/man/1/collab
+offered	/man/1/webgrab
+offered	/man/2/security-0intro
+offering	/man/2/popup
+offering	/man/8/getauthinfo
+offers	/man/1/miniterm
+offers	/man/1/wm-misc
+offers	/man/2/security-0intro
+offers	/man/2/sexprs
+offers	/man/3/logfs
+offers	/man/8/collabsrv
+offscreen	/man/2/prefab-compound
+offscreen	/man/9/canvas
+offset	/man/1/read
+offset	/man/1/sh-file2chan
+offset	/man/1/strings
+offset	/man/1/xd
+offset	/man/10/a.out
+offset	/man/10/allocb
+offset	/man/10/dev
+offset	/man/10/devattach
+offset	/man/10/dynld
+offset	/man/10/inm
+offset	/man/10/newchan
+offset	/man/10/qio
+offset	/man/10/readnum
+offset	/man/10/styx
+offset	/man/10/styxserver
+offset	/man/2/bufio
+offset	/man/2/daytime
+offset	/man/2/dis
+offset	/man/2/disks
+offset	/man/2/draw-display
+offset	/man/2/draw-image
+offset	/man/2/palmfile
+offset	/man/2/spree
+offset	/man/2/spree-gather
+offset	/man/2/styx
+offset	/man/2/styxservers
+offset	/man/2/sys-0intro
+offset	/man/2/sys-dirread
+offset	/man/2/sys-file2chan
+offset	/man/2/sys-read
+offset	/man/2/sys-seek
+offset	/man/2/xml
+offset	/man/3/arch
+offset	/man/3/boot
+offset	/man/3/cons
+offset	/man/3/dbg
+offset	/man/3/ds
+offset	/man/3/flash
+offset	/man/3/fpga
+offset	/man/3/ftl
+offset	/man/3/i2c
+offset	/man/3/prog
+offset	/man/3/tv
+offset	/man/3/vid
+offset	/man/4/acme
+offset	/man/4/ramfile
+offset	/man/4/registry
+offset	/man/5/0intro
+offset	/man/5/read
+offset	/man/5/stat
+offset	/man/6/dis
+offset	/man/6/image
+offset	/man/6/sbl
+offset	/man/8/cs
+offset	/man/8/prep
+offset	/man/8/styxchat
+offset	/man/9/cursor
+offset	/man/9/grid
+offset	/man/9/text
+offset1	/man/1/cmp
+offset2	/man/1/cmp
+offsets	/man/1/cmp
+offsets	/man/1/cp
+offsets	/man/2/disks
+offsets	/man/2/styx
+offsets	/man/2/styxservers
+offsets	/man/4/acme
+offsets	/man/4/dbfs
+offsets	/man/6/dis
+offsets	/man/6/image
+offsets	/man/6/sbl
+offvalue	/man/9/checkbutton
+ofile	/man/10/5coff
+ofile	/man/10/kstrip
+ofilter	/man/2/w3c-xpointers
+oflag	/man/2/ip
+oge	/man/2/w3c-xpointers
+oh	/man/7/db
+ohostname	/man/2/dhcpclient
+oid	/man/2/asn1
+oid.nums	/man/2/asn1
+ok	/man/1/grid-session
+ok	/man/1/listen
+ok	/man/1/sh-test
+ok	/man/1/tktester
+ok	/man/2/ip
+ok	/man/2/itslib
+ok	/man/2/rfc822
+ok	/man/2/security-auth
+ok	/man/2/tkclient
+ok	/man/2/venti
+ok	/man/2/wmclient
+ok	/man/4/factotum
+ok	/man/4/keyfs
+ok	/man/8/rdbgsrv
+ok	/man/9/canvas
+oks	/man/2/rfc822
+old.bundle	/man/1/alphabet-fs
+old.bundle	/man/1/fs
+old.index.html	/man/1/webgrab
+olddir	/man/4/trfs
+older	/man/1/0intro
+older	/man/1/ftest
+older	/man/1/mash
+older	/man/1/mk
+older	/man/10/dmainit
+older	/man/10/mk
+older	/man/10/ms2
+older	/man/10/plan9.ini
+older	/man/2/0intro
+older	/man/2/daytime
+older	/man/2/keyring-crypt
+older	/man/2/palmfile
+older	/man/2/styxconv
+older	/man/3/audio
+older	/man/6/dis
+older	/man/6/image
+older	/man/6/ndb
+oldfd	/man/2/sys-dup
+oldh	/man/7/db
+oldhash	/man/4/keysrv
+oldheaders	/man/3/ip
+oldtag	/man/10/styx
+oldtag	/man/2/styx
+oldtag	/man/5/0intro
+oldtag	/man/5/flush
+oldtag	/man/8/styxchat
+ole	/man/2/w3c-xpointers
+olease	/man/2/dhcpclient
+oleft	/man/2/spree-cardlib
+olive	/man/9/types
+omask	/man/2/dhcpclient
+omaxmsg	/man/2/dhcpclient
+omit	/man/10/ms2
+omit	/man/10/styxserver
+omits	/man/2/pop3
+omits	/man/8/ai2key
+omitted	/man/1/acme
+omitted	/man/1/cal
+omitted	/man/1/chmod
+omitted	/man/1/mash-make
+omitted	/man/1/mk
+omitted	/man/1/sh-alphabet
+omitted	/man/1/sh-file2chan
+omitted	/man/1/sh-tk
+omitted	/man/10/9load
+omitted	/man/10/mk
+omitted	/man/10/plan9.ini
+omitted	/man/10/print
+omitted	/man/2/ip
+omitted	/man/2/spree
+omitted	/man/2/sys-print
+omitted	/man/3/audio
+omitted	/man/3/ftl
+omitted	/man/4/acme
+omitted	/man/6/sbl
+omitted	/man/8/prep
+omitted	/man/9/canvas
+omitted	/man/9/grid
+omitted	/man/9/image
+omitted	/man/9/listbox
+omitted	/man/9/menu
+omitted	/man/9/pack
+omitted	/man/9/scale
+omitted	/man/9/text
+omitting	/man/3/ftl
+omitting	/man/9/variable
+omod	/man/2/w3c-xpointers
+omode	/man/10/devattach
+omode	/man/10/newchan
+omode	/man/2/styxservers
+omode	/man/2/sys-open
+omul	/man/2/w3c-xpointers
+onametest	/man/2/w3c-xpointers
+one's	/man/2/keyring-0intro
+oneg	/man/2/w3c-xpointers
+ones	/man/10/intrenable
+ones	/man/10/plan9.ini
+ones	/man/2/0intro
+ones	/man/2/draw-display
+ones	/man/2/ip
+ones	/man/2/keyring-ipint
+ones	/man/2/translate
+ones	/man/3/cons
+ones	/man/3/ds
+ones	/man/4/registry
+ones	/man/5/0intro
+ones	/man/6/colour
+ones	/man/6/image
+ones	/man/6/scancode
+ones	/man/8/styxchat
+ones	/man/9/text
+onetbiosns	/man/2/dhcpclient
+onodetype	/man/2/w3c-xpointers
+onscreen	/man/1/sh-tk
+onscreen	/man/1/wm
+onscreen	/man/2/tkclient
+onscreen	/man/2/wmclient
+onto	/man/1/alphabet-main
+onto	/man/1/cat
+onto	/man/1/cp
+onto	/man/1/m4
+onto	/man/1/sh-expr
+onto	/man/10/error
+onto	/man/10/ms2
+onto	/man/10/styxserver
+onto	/man/2/prof
+onto	/man/2/spree-cardlib
+onto	/man/2/translate
+onto	/man/6/translate
+onto	/man/6/ubfa
+onto	/man/8/rstyxd
+onto	/man/9/bind
+onto	/man/9/image
+ontpserver	/man/2/dhcpclient
+onvalue	/man/9/checkbutton
+ooo	/man/6/sexprs
+oor	/man/2/w3c-xpointers
+op	/man/1/acme
+op	/man/1/alphabet-fs
+op	/man/1/chmod
+op	/man/1/fs
+op	/man/1/limbo
+op	/man/1/secstore
+op	/man/1/sh-alphabet
+op	/man/1/sh-regex
+op	/man/10/dev
+op	/man/2/dis
+op	/man/2/draw-image
+op	/man/2/sets
+op	/man/2/sexprs
+op	/man/2/spki
+op	/man/2/spree-cardlib
+op	/man/2/translate
+op	/man/2/ubfa
+op	/man/2/w3c-css
+op	/man/2/w3c-xpointers
+op	/man/3/dbg
+op	/man/6/dis
+op	/man/8/collabsrv
+op	/man/8/prep
+op	/man/9/text
+op.lbs	/man/2/translate
+op.lbs.a	/man/2/translate
+op.lbs.g	/man/2/translate
+op.lbs.l	/man/2/translate
+op.lbs.r	/man/2/translate
+op2s	/man/2/dis
+op9auth	/man/2/dhcpclient
+op9fs	/man/2/dhcpclient
+opacity	/man/2/draw-image
+opacity	/man/6/colour
+opaque	/man/10/2c
+opaque	/man/2/draw-display
+opaque	/man/2/draw-image
+opaque	/man/2/sets
+opaque	/man/6/colour
+opath	/man/2/w3c-xpointers
+opcode	/man/6/dis
+opcodes	/man/6/dis
+openctl	/man/3/vga
+opendict	/man/2/translate
+opendicts	/man/2/translate
+opendisk	/man/2/disks
+opened	/man/1/bind
+opened	/man/1/brutus
+opened	/man/1/charon
+opened	/man/1/ftree
+opened	/man/1/grid-query
+opened	/man/1/look
+opened	/man/1/sh
+opened	/man/1/sh-std
+opened	/man/1/tktester
+opened	/man/10/dev
+opened	/man/10/devattach
+opened	/man/10/newchan
+opened	/man/10/styxserver
+opened	/man/2/bufio
+opened	/man/2/dbm
+opened	/man/2/dial
+opened	/man/2/disks
+opened	/man/2/plumbmsg
+opened	/man/2/pop3
+opened	/man/2/pslib
+opened	/man/2/styxservers
+opened	/man/2/sys-0intro
+opened	/man/2/sys-dial
+opened	/man/2/sys-pipe
+opened	/man/2/sys-stat
+opened	/man/2/xml
+opened	/man/3/boot
+opened	/man/3/cap
+opened	/man/3/cmd
+opened	/man/3/cons
+opened	/man/3/dynld
+opened	/man/3/i2c
+opened	/man/3/ip
+opened	/man/3/prog
+opened	/man/3/sd
+opened	/man/3/sign
+opened	/man/3/snarf
+opened	/man/3/srv
+opened	/man/3/tls
+opened	/man/4/acme
+opened	/man/4/dbfs
+opened	/man/4/factotum
+opened	/man/4/registry
+opened	/man/4/spree
+opened	/man/5/0intro
+opened	/man/5/clunk
+opened	/man/5/open
+opened	/man/5/read
+opened	/man/5/walk
+opened	/man/6/attrdb
+opened	/man/7/db
+opened	/man/8/collabsrv
+opening	/man/1/acme
+opening	/man/1/grid-query
+opening	/man/10/odbc
+opening	/man/10/styxserver
+opening	/man/2/bufio
+opening	/man/2/sys-fd2path
+opening	/man/2/sys-open
+opening	/man/2/xml
+opening	/man/3/cmd
+opening	/man/3/cons
+opening	/man/3/draw
+opening	/man/3/ether
+opening	/man/3/ip
+opening	/man/3/plap
+opening	/man/3/srv
+opening	/man/3/ssl
+opening	/man/3/tls
+opening	/man/4/dbfs
+opening	/man/4/factotum
+opening	/man/4/lockfs
+opening	/man/4/registry
+opening	/man/6/json
+opening	/man/8/collabsrv
+opening	/man/9/0intro
+openmode	/man/10/devattach
+openmode	/man/2/styxservers
+openok	/man/2/styxservers
+opens	/man/1/dmview
+opens	/man/1/mash
+opens	/man/1/miniterm
+opens	/man/1/sh
+opens	/man/1/tktester
+opens	/man/10/styxserver
+opens	/man/2/attrdb
+opens	/man/2/dbm
+opens	/man/2/disks
+opens	/man/2/factotum
+opens	/man/2/format
+opens	/man/2/palmfile
+opens	/man/2/pop3
+opens	/man/2/smtp
+opens	/man/2/spree
+opens	/man/2/sys-open
+opens	/man/2/translate
+opens	/man/2/xml
+opens	/man/3/cmd
+opens	/man/3/ip
+opens	/man/3/plap
+opens	/man/3/srv9
+opens	/man/3/vga
+opens	/man/4/export
+opens	/man/4/iostats
+opens	/man/4/spree
+opens	/man/4/vacfs
+opens	/man/5/open
+opens	/man/7/db
+opens	/man/8/rstyxd
+opens	/man/9/text
+operand	/man/1/disdep
+operand	/man/1/fc
+operand	/man/1/mash
+operand	/man/1/sh
+operand	/man/1/sh-expr
+operand	/man/10/acid
+operand	/man/2/dis
+operand	/man/2/w3c-xpointers
+operand	/man/6/dis
+operand's	/man/2/dis
+operands	/man/1/fc
+operands	/man/1/mash
+operands	/man/1/sh
+operands	/man/1/sh-expr
+operands	/man/2/dis
+operands	/man/2/w3c-xpointers
+operands	/man/6/dis
+operate	/man/1/cook
+operate	/man/1/wish
+operate	/man/1/wm-misc
+operate	/man/10/9load
+operate	/man/10/memory
+operate	/man/2/draw-0intro
+operate	/man/2/spree-cardlib
+operate	/man/2/styx
+operate	/man/3/prog
+operate	/man/6/proto
+operate	/man/9/canvas
+operate	/man/9/grid
+operate	/man/9/menu
+operated	/man/6/colour
+operates	/man/1/du
+operates	/man/2/filter-slip
+operates	/man/2/sys-millisec
+operates	/man/3/fs
+operates	/man/6/plumbing
+operates	/man/8/prep
+operates	/man/9/canvas
+operates	/man/9/text
+operating	/man/1/0intro
+operating	/man/1/chgrp
+operating	/man/1/chmod
+operating	/man/1/emu
+operating	/man/10/9load
+operating	/man/10/kprof
+operating	/man/10/plan9.ini
+operating	/man/2/disks
+operating	/man/2/srv
+operating	/man/2/sys-0intro
+operating	/man/2/sys-dup
+operating	/man/2/sys-iounit
+operating	/man/3/cmd
+operating	/man/3/cons
+operating	/man/3/fs
+operating	/man/3/kprof
+operating	/man/5/0intro
+operating	/man/6/keyboard
+operating	/man/8/dns
+operating	/man/8/prep
+operation	/man/1/acme
+operation	/man/1/alphabet-main
+operation	/man/1/chgrp
+operation	/man/1/deb
+operation	/man/1/fc
+operation	/man/1/gzip
+operation	/man/1/sh
+operation	/man/1/sh-expr
+operation	/man/1/sh-file2chan
+operation	/man/1/sh-tk
+operation	/man/10/2c
+operation	/man/10/dmainit
+operation	/man/10/plan9.ini
+operation	/man/10/qio
+operation	/man/10/styxserver
+operation	/man/2/complete
+operation	/man/2/csv
+operation	/man/2/dbm
+operation	/man/2/dhcpclient
+operation	/man/2/dis
+operation	/man/2/draw-image
+operation	/man/2/filter
+operation	/man/2/filter-deflate
+operation	/man/2/filter-slip
+operation	/man/2/fsproto
+operation	/man/2/ida
+operation	/man/2/json
+operation	/man/2/keyring-0intro
+operation	/man/2/keyring-auth
+operation	/man/2/keyring-certtostr
+operation	/man/2/keyring-ipint
+operation	/man/2/lists
+operation	/man/2/math-export
+operation	/man/2/math-fp
+operation	/man/2/msgio
+operation	/man/2/prefab-element
+operation	/man/2/rfc822
+operation	/man/2/scsiio
+operation	/man/2/secstore
+operation	/man/2/sexprs
+operation	/man/2/spki
+operation	/man/2/spki-verifier
+operation	/man/2/styx
+operation	/man/2/styxservers
+operation	/man/2/sys-0intro
+operation	/man/2/sys-open
+operation	/man/2/sys-stat
+operation	/man/2/ubfa
+operation	/man/2/w3c-css
+operation	/man/2/w3c-uris
+operation	/man/2/w3c-xpointers
+operation	/man/3/dbg
+operation	/man/3/dup
+operation	/man/3/tls
+operation	/man/5/0intro
+operation	/man/6/dis
+operation	/man/6/namespace
+operation	/man/9/canvas
+operation	/man/9/text
+operations	/man/1/acme
+operations	/man/1/fc
+operations	/man/1/ftree
+operations	/man/1/mash
+operations	/man/1/mash-make
+operations	/man/1/mprof
+operations	/man/1/nsbuild
+operations	/man/1/os
+operations	/man/1/secstore
+operations	/man/1/sh
+operations	/man/1/sh-alphabet
+operations	/man/1/sh-regex
+operations	/man/1/sh-tk
+operations	/man/1/tktester
+operations	/man/10/a.out
+operations	/man/10/allocb
+operations	/man/10/dev
+operations	/man/10/master
+operations	/man/10/memory
+operations	/man/10/newchan
+operations	/man/10/qio
+operations	/man/10/strcat
+operations	/man/10/styxserver
+operations	/man/2/alphabet-intro
+operations	/man/2/bufio
+operations	/man/2/dbm
+operations	/man/2/dhcpclient
+operations	/man/2/diskblocks
+operations	/man/2/disks
+operations	/man/2/draw-0intro
+operations	/man/2/draw-context
+operations	/man/2/draw-display
+operations	/man/2/draw-image
+operations	/man/2/drawmux
+operations	/man/2/exception
+operations	/man/2/factotum
+operations	/man/2/format
+operations	/man/2/ida
+operations	/man/2/ip
+operations	/man/2/json
+operations	/man/2/keyring-certtostr
+operations	/man/2/lists
+operations	/man/2/math-0intro
+operations	/man/2/names
+operations	/man/2/prefab-0intro
+operations	/man/2/registries
+operations	/man/2/rfc822
+operations	/man/2/scsiio
+operations	/man/2/sets
+operations	/man/2/sexprs
+operations	/man/2/spki
+operations	/man/2/spki-verifier
+operations	/man/2/string
+operations	/man/2/styxpersist
+operations	/man/2/styxservers
+operations	/man/2/styxservers-nametree
+operations	/man/2/sys-0intro
+operations	/man/2/sys-bind
+operations	/man/2/sys-export
+operations	/man/2/sys-iounit
+operations	/man/2/ubfa
+operations	/man/2/w3c-uris
+operations	/man/2/wmsrv
+operations	/man/3/dbg
+operations	/man/3/draw
+operations	/man/3/flash
+operations	/man/3/indir
+operations	/man/3/mnt
+operations	/man/3/prog
+operations	/man/3/sign
+operations	/man/3/srv
+operations	/man/3/tls
+operations	/man/4/0intro
+operations	/man/4/acme
+operations	/man/4/dbfs
+operations	/man/4/ftpfs
+operations	/man/4/keyfs
+operations	/man/6/colour
+operations	/man/6/image
+operations	/man/6/namespace
+operations	/man/9/button
+operations	/man/9/canvas
+operations	/man/9/checkbutton
+operations	/man/9/choicebutton
+operations	/man/9/entry
+operations	/man/9/frame
+operations	/man/9/label
+operations	/man/9/listbox
+operations	/man/9/menu
+operations	/man/9/menubutton
+operations	/man/9/panel
+operations	/man/9/radiobutton
+operations	/man/9/scale
+operations	/man/9/scrollbar
+operations	/man/9/text
+operator	/man/1/alphabet-fs
+operator	/man/1/calc
+operator	/man/1/disdep
+operator	/man/1/fc
+operator	/man/1/fs
+operator	/man/1/m4
+operator	/man/1/mash
+operator	/man/1/sh
+operator	/man/1/sh-expr
+operator	/man/1/sh-regex
+operator	/man/1/sh-std
+operator	/man/1/sh-string
+operator	/man/10/2c
+operator	/man/10/acid
+operator	/man/10/c2l
+operator	/man/10/dynld
+operator	/man/2/command
+operator	/man/2/draw-0intro
+operator	/man/2/draw-image
+operator	/man/2/json
+operator	/man/2/plumbmsg
+operator	/man/2/regex
+operator	/man/2/sets
+operator	/man/2/sh
+operator	/man/2/sys-self
+operator	/man/2/ubfa
+operator	/man/2/w3c-css
+operator	/man/2/w3c-xpointers
+operator	/man/3/dbg
+operator	/man/3/draw
+operator	/man/6/keyboard
+operator	/man/8/signer
+operator	/man/9/text
+operators	/man/1/acme
+operators	/man/1/alphabet-fs
+operators	/man/1/alphabet-main
+operators	/man/1/calc
+operators	/man/1/fc
+operators	/man/1/fs
+operators	/man/1/m4
+operators	/man/1/mash
+operators	/man/1/mdb
+operators	/man/1/sh-expr
+operators	/man/1/units
+operators	/man/10/acid
+operators	/man/2/draw-image
+operators	/man/2/sh
+operators	/man/2/w3c-xpointers
+operators	/man/3/dbg
+operators	/man/6/keyboard
+operators	/man/6/regexp
+operators	/man/6/ubfa
+operators	/man/8/prep
+operators	/man/9/text
+opf	/man/1/ebook
+opop3server	/man/2/dhcpclient
+opposed	/man/2/dis
+opposed	/man/6/0intro
+opposite	/man/1/diff
+opposite	/man/1/gzip
+opposite	/man/1/tktester
+opposite	/man/2/sh
+opposite	/man/2/spree-cardlib
+opposite	/man/9/canvas
+ops	/man/10/styxserver
+opt	/man/1/sh-arg
+opt	/man/2/arg
+opt	/man/6/dis
+opt	/man/6/sbl
+optargs	/man/2/alphabet-intro
+optimization	/man/10/2c
+option	/man/1/9win
+option	/man/1/acme
+option	/man/1/alphabet-fs
+option	/man/1/alphabet-main
+option	/man/1/ar
+option	/man/1/asm
+option	/man/1/auplay
+option	/man/1/avr
+option	/man/1/basename
+option	/man/1/bind
+option	/man/1/charon
+option	/man/1/cleanname
+option	/man/1/collab-clients
+option	/man/1/comm
+option	/man/1/cp
+option	/man/1/cprof
+option	/man/1/cpu
+option	/man/1/crypt
+option	/man/1/dd
+option	/man/1/deb
+option	/man/1/diff
+option	/man/1/du
+option	/man/1/ebook
+option	/man/1/echo
+option	/man/1/emu
+option	/man/1/fc
+option	/man/1/freq
+option	/man/1/fs
+option	/man/1/ftree
+option	/man/1/gettar
+option	/man/1/grid-register
+option	/man/1/grid-session
+option	/man/1/gzip
+option	/man/1/idea
+option	/man/1/keyboard
+option	/man/1/kill
+option	/man/1/limbo
+option	/man/1/listen
+option	/man/1/logon
+option	/man/1/ls
+option	/man/1/m4
+option	/man/1/man
+option	/man/1/math-misc
+option	/man/1/mdb
+option	/man/1/mk
+option	/man/1/mkdir
+option	/man/1/mprof
+option	/man/1/ns
+option	/man/1/os
+option	/man/1/p
+option	/man/1/passwd
+option	/man/1/prof
+option	/man/1/rcmd
+option	/man/1/rm
+option	/man/1/secstore
+option	/man/1/sh
+option	/man/1/sh-arg
+option	/man/1/sh-expr
+option	/man/1/stack
+option	/man/1/stream
+option	/man/1/tail
+option	/man/1/tcs
+option	/man/1/tee
+option	/man/1/time
+option	/man/1/tktester
+option	/man/1/touch
+option	/man/1/wc
+option	/man/1/wish
+option	/man/1/wm-misc
+option	/man/1/xd
+option	/man/1/yacc
+option	/man/1/zeros
+option	/man/10/2a
+option	/man/10/2c
+option	/man/10/2l
+option	/man/10/5cv
+option	/man/10/9load
+option	/man/10/acid
+option	/man/10/c2l
+option	/man/10/dynld
+option	/man/10/iar
+option	/man/10/kproc
+option	/man/10/mk
+option	/man/10/ms2
+option	/man/10/odbc
+option	/man/10/plan9.ini
+option	/man/10/srclist
+option	/man/10/styxserver
+option	/man/2/alphabet-intro
+option	/man/2/arg
+option	/man/2/dhcpclient
+option	/man/2/filter-deflate
+option	/man/2/prof
+option	/man/2/readdir
+option	/man/2/styxservers
+option	/man/2/sys-chdir
+option	/man/2/virgil
+option	/man/3/cmd
+option	/man/3/dbg
+option	/man/3/env
+option	/man/3/fs
+option	/man/3/logfs
+option	/man/3/srv9
+option	/man/3/switch
+option	/man/4/archfs
+option	/man/4/dbfs
+option	/man/4/export
+option	/man/4/factotum
+option	/man/4/ftpfs
+option	/man/4/import
+option	/man/4/iostats
+option	/man/4/keyfs
+option	/man/4/kfs
+option	/man/4/lockfs
+option	/man/4/memfs
+option	/man/4/namespace
+option	/man/4/ramfile
+option	/man/4/registry
+option	/man/6/namespace
+option	/man/6/sbl
+option	/man/7/cddb
+option	/man/8/applylog
+option	/man/8/bootpd
+option	/man/8/changelogin
+option	/man/8/create
+option	/man/8/createsignerkey
+option	/man/8/cs
+option	/man/8/dhcp
+option	/man/8/dns
+option	/man/8/init
+option	/man/8/kfscmd
+option	/man/8/mangaload
+option	/man/8/mkfs
+option	/man/8/plumber
+option	/man/8/prep
+option	/man/8/rdbgsrv
+option	/man/8/rip
+option	/man/8/sntp
+option	/man/8/styxchat
+option	/man/9/button
+option	/man/9/canvas
+option	/man/9/checkbutton
+option	/man/9/choicebutton
+option	/man/9/entry
+option	/man/9/frame
+option	/man/9/grid
+option	/man/9/image
+option	/man/9/label
+option	/man/9/listbox
+option	/man/9/menu
+option	/man/9/menubutton
+option	/man/9/options
+option	/man/9/pack
+option	/man/9/panel
+option	/man/9/radiobutton
+option	/man/9/scale
+option	/man/9/scrollbar
+option	/man/9/see
+option	/man/9/text
+option	/man/9/types
+option	/man/9/update
+option's	/man/1/sh-arg
+option's	/man/9/options
+option's	/man/9/text
+optional	/man/1/acme
+optional	/man/1/ar
+optional	/man/1/bind
+optional	/man/1/calc
+optional	/man/1/calendar
+optional	/man/1/charon
+optional	/man/1/collab-clients
+optional	/man/1/date
+optional	/man/1/emu
+optional	/man/1/fc
+optional	/man/1/filename
+optional	/man/1/listen
+optional	/man/1/look
+optional	/man/1/man
+optional	/man/1/mash-make
+optional	/man/1/mk
+optional	/man/1/read
+optional	/man/1/secstore
+optional	/man/1/sh-file2chan
+optional	/man/1/sort
+optional	/man/1/tktester
+optional	/man/1/webgrab
+optional	/man/1/wish
+optional	/man/10/2l
+optional	/man/10/atoi
+optional	/man/10/iar
+optional	/man/10/mk
+optional	/man/10/plan9.ini
+optional	/man/10/qio
+optional	/man/2/cfg
+optional	/man/2/convcs
+optional	/man/2/dialog
+optional	/man/2/draw-image
+optional	/man/2/factotum
+optional	/man/2/geodesy
+optional	/man/2/math-linalg
+optional	/man/2/palmfile
+optional	/man/2/rfc822
+optional	/man/2/spki
+optional	/man/2/string
+optional	/man/2/w3c-css
+optional	/man/2/w3c-uris
+optional	/man/2/w3c-xpointers
+optional	/man/3/cmd
+optional	/man/3/cons
+optional	/man/3/dynld
+optional	/man/3/fs
+optional	/man/3/ftl
+optional	/man/3/ip
+optional	/man/3/tls
+optional	/man/3/vga
+optional	/man/4/acme
+optional	/man/4/ramfile
+optional	/man/4/registry
+optional	/man/6/dis
+optional	/man/6/font
+optional	/man/6/proto
+optional	/man/6/sbl
+optional	/man/6/sexprs
+optional	/man/6/translate
+optional	/man/7/cddb
+optional	/man/8/create
+optional	/man/8/cs
+optional	/man/9/bind
+optional	/man/9/frame
+optional	/man/9/text
+optional	/man/9/types
+optionally	/man/1/alphabet-main
+optionally	/man/1/ar
+optionally	/man/1/cprof
+optionally	/man/1/du
+optionally	/man/1/echo
+optionally	/man/1/grid-monitor
+optionally	/man/1/itest
+optionally	/man/10/atoi
+optionally	/man/10/conf
+optionally	/man/10/iar
+optionally	/man/2/ether
+optionally	/man/2/spki-verifier
+optionally	/man/2/string
+optionally	/man/2/sys-dirread
+optionally	/man/2/sys-export
+optionally	/man/2/translate
+optionally	/man/3/dynld
+optionally	/man/3/indir
+optionally	/man/4/keysrv
+optionally	/man/4/kfs
+optionally	/man/8/rstyxd
+optionally	/man/9/button
+optionally	/man/9/checkbutton
+optionally	/man/9/grid
+optionally	/man/9/label
+optionally	/man/9/menubutton
+optionally	/man/9/radiobutton
+optionally	/man/9/text
+optioncfg	/man/2/translate
+options	/man/1/alphabet-abc
+options	/man/1/alphabet-fs
+options	/man/1/alphabet-grid
+options	/man/1/auplay
+options	/man/1/bind
+options	/man/1/charon
+options	/man/1/chgrp
+options	/man/1/cmp
+options	/man/1/collab
+options	/man/1/cook
+options	/man/1/cp
+options	/man/1/cprof
+options	/man/1/crypt
+options	/man/1/date
+options	/man/1/dd
+options	/man/1/deb
+options	/man/1/disdep
+options	/man/1/du
+options	/man/1/emu
+options	/man/1/filename
+options	/man/1/fmt
+options	/man/1/freq
+options	/man/1/fs
+options	/man/1/ftree
+options	/man/1/grep
+options	/man/1/grid-ns
+options	/man/1/grid-register
+options	/man/1/itest
+options	/man/1/keyboard
+options	/man/1/limbo
+options	/man/1/look
+options	/man/1/ls
+options	/man/1/m4
+options	/man/1/man
+options	/man/1/math-misc
+options	/man/1/mk
+options	/man/1/mprof
+options	/man/1/plumb
+options	/man/1/prof
+options	/man/1/rm
+options	/man/1/secstore
+options	/man/1/sh-alphabet
+options	/man/1/sh-arg
+options	/man/1/sh-tk
+options	/man/1/sort
+options	/man/1/stack
+options	/man/1/tiny
+options	/man/1/tktester
+options	/man/1/tr
+options	/man/1/unicode
+options	/man/1/wc
+options	/man/1/webgrab
+options	/man/1/wm-misc
+options	/man/1/wm-sh
+options	/man/1/xd
+options	/man/1/yacc
+options	/man/10/2a
+options	/man/10/2c
+options	/man/10/2l
+options	/man/10/5coff
+options	/man/10/5cv
+options	/man/10/9load
+options	/man/10/acid
+options	/man/10/c2l
+options	/man/10/devattach
+options	/man/10/inm
+options	/man/10/mk
+options	/man/10/ms2
+options	/man/10/plan9.ini
+options	/man/2/alphabet-intro
+options	/man/2/arg
+options	/man/2/dhcpclient
+options	/man/2/dis
+options	/man/2/print
+options	/man/2/sh
+options	/man/2/spree-cardlib
+options	/man/2/tk
+options	/man/2/tkclient
+options	/man/3/sd
+options	/man/3/srv9
+options	/man/4/archfs
+options	/man/4/dbfs
+options	/man/4/dossrv
+options	/man/4/factotum
+options	/man/4/ftpfs
+options	/man/4/grid-cpu
+options	/man/4/import
+options	/man/4/kfs
+options	/man/4/memfs
+options	/man/4/namespace
+options	/man/4/tarfs
+options	/man/6/dis
+options	/man/6/namespace
+options	/man/8/applylog
+options	/man/8/create
+options	/man/8/dhcp
+options	/man/8/httpd
+options	/man/8/kfscmd
+options	/man/8/mkfs
+options	/man/8/ping
+options	/man/8/prep
+options	/man/8/rdbgsrv
+options	/man/8/shutdown
+options	/man/8/styxmon
+options	/man/9/0intro
+options	/man/9/button
+options	/man/9/canvas
+options	/man/9/checkbutton
+options	/man/9/choicebutton
+options	/man/9/cursor
+options	/man/9/entry
+options	/man/9/frame
+options	/man/9/grid
+options	/man/9/image
+options	/man/9/label
+options	/man/9/listbox
+options	/man/9/menu
+options	/man/9/menubutton
+options	/man/9/options
+options	/man/9/pack
+options	/man/9/panel
+options	/man/9/radiobutton
+options	/man/9/scale
+options	/man/9/scrollbar
+options	/man/9/see
+options	/man/9/text
+options	/man/9/types
+options	/man/9/update
+opts	/man/1/sh-arg
+opts	/man/2/alphabet-intro
+or'd	/man/2/dhcpclient
+or'd	/man/2/readdir
+or'd	/man/2/sys-bind
+or'd	/man/2/sys-open
+orange	/man/9/types
+orclose	/man/2/styxservers
+orclose	/man/2/sys-open
+orclose	/man/3/srv
+orclose	/man/5/clunk
+orclose	/man/5/open
+ord	/man/2/spree-cardlib
+ordered	/man/1/tr
+ordered	/man/2/asn1
+ordered	/man/2/stringinttab
+ordered	/man/2/sys-tokenize
+ordered	/man/4/factotum
+ordered	/man/9/canvas
+ordered	/man/9/pack
+ordering	/man/1/ls
+ordering	/man/1/sort
+ordering	/man/2/stringinttab
+ordering	/man/6/colour
+ordering	/man/6/image
+orderly	/man/10/qlock
+ordinal	/man/2/spree-cardlib
+ordinary	/man/10/styxserver
+ordinary	/man/2/draw-screen
+ordinary	/man/2/sys-0intro
+ordinary	/man/2/sys-stat
+ordinary	/man/3/cons
+ordinary	/man/4/ftpfs
+ordinary	/man/6/keyboard
+ordnance	/man/2/geodesy
+ordwr	/man/10/devattach
+ordwr	/man/10/newchan
+ordwr	/man/10/styxserver
+ordwr	/man/2/bufio
+ordwr	/man/2/dbm
+ordwr	/man/2/disks
+ordwr	/man/2/factotum
+ordwr	/man/2/styxservers
+ordwr	/man/2/sys-open
+ordwr	/man/2/sys-pipe
+ordwr	/man/3/arch
+ordwr	/man/3/srv9
+ordwr	/man/5/open
+oread	/man/1/yacc
+oread	/man/10/devattach
+oread	/man/10/newchan
+oread	/man/10/styxserver
+oread	/man/2/bufio
+oread	/man/2/dbm
+oread	/man/2/disks
+oread	/man/2/palmfile
+oread	/man/2/styxservers
+oread	/man/2/sys-open
+oread	/man/5/open
+orebindingtime	/man/2/dhcpclient
+ored	/man/2/prof
+ored	/man/2/styxservers
+orenewaltime	/man/2/dhcpclient
+organise	/man/6/attrdb
+organised	/man/3/ftl
+organised	/man/3/tv
+organised	/man/4/namespace
+organised	/man/6/image
+organization	/man/2/sys-0intro
+organized	/man/2/sys-0intro
+organized	/man/9/menu
+organized	/man/9/menubutton
+organizes	/man/2/draw-screen
+ori	/man/2/keyring-ipint
+orient	/man/9/options
+orient	/man/9/scale
+orient	/man/9/scrollbar
+orientation	/man/2/prefab-element
+orientation	/man/2/print
+orientation	/man/2/spree-cardlib
+orientation	/man/9/options
+orientation	/man/9/scale
+orientation	/man/9/scrollbar
+oriented	/man/1/diff
+oriented	/man/10/dmainit
+oriented	/man/2/keyring-getmsg
+oriented	/man/2/msgio
+oriented	/man/2/plumbmsg
+oriented	/man/3/ds
+oriented	/man/3/ftl
+oriented	/man/3/ip
+oriented	/man/3/mnt
+oriented	/man/6/keytext
+oright	/man/2/spree-cardlib
+origin	/man/1/m4
+origin	/man/1/webgrab
+origin	/man/1/wm
+origin	/man/2/debug
+origin	/man/2/draw-0intro
+origin	/man/2/draw-context
+origin	/man/2/draw-image
+origin	/man/2/math-linalg
+origin	/man/2/palmfile
+origin	/man/2/styxservers
+origin	/man/2/tk
+origin	/man/2/w3c-xpointers
+origin	/man/2/wmsrv
+origin	/man/4/acme
+origin	/man/6/sbl
+origin	/man/9/bind
+origin	/man/9/canvas
+origin	/man/9/panel
+origin	/man/9/see
+original	/man/1/acme
+original	/man/1/bind
+original	/man/1/cp
+original	/man/1/cpu
+original	/man/1/m4
+original	/man/1/mash
+original	/man/1/ns
+original	/man/1/sh
+original	/man/1/sh-alphabet
+original	/man/1/sort
+original	/man/1/uuencode
+original	/man/1/webgrab
+original	/man/1/wm-misc
+original	/man/10/allocb
+original	/man/10/lock
+original	/man/2/asn1
+original	/man/2/disks
+original	/man/2/draw-context
+original	/man/2/draw-screen
+original	/man/2/filter-deflate
+original	/man/2/filter-slip
+original	/man/2/format
+original	/man/2/geodesy
+original	/man/2/ida
+original	/man/2/msgio
+original	/man/2/plumbmsg
+original	/man/2/security-0intro
+original	/man/2/security-ssl
+original	/man/2/sh
+original	/man/2/spki
+original	/man/2/styxconv
+original	/man/2/styxflush
+original	/man/2/styxservers
+original	/man/2/sys-bind
+original	/man/2/sys-fd2path
+original	/man/2/sys-fversion
+original	/man/2/translate
+original	/man/2/w3c-css
+original	/man/2/w3c-uris
+original	/man/3/srv
+original	/man/5/0intro
+original	/man/6/sbl
+original	/man/6/ubfa
+original	/man/8/ai2key
+original	/man/9/button
+original	/man/9/image
+originally	/man/1/alphabet-fs
+originally	/man/1/bind
+originally	/man/1/fs
+originally	/man/1/sh
+originally	/man/1/sh-std
+originally	/man/10/dmainit
+originally	/man/10/newchan
+originally	/man/10/styxserver
+originally	/man/2/ida
+originally	/man/2/ir
+originally	/man/2/spki
+originally	/man/2/sys-bind
+originally	/man/2/xml
+originally	/man/4/lockfs
+originally	/man/4/registry
+originally	/man/6/man
+originally	/man/8/init
+originals	/man/1/wm-sh
+originated	/man/1/stack
+originated	/man/4/acme
+origins	/man/10/plan9.ini
+orinoco	/man/10/plan9.ini
+orouter	/man/2/dhcpclient
+os	/man/1/alphabet-abc
+os	/man/1/alphabet-grid
+os	/man/1/mk
+os	/man/1/os
+os	/man/10/5cv
+os	/man/10/9load
+os	/man/10/allocb
+os	/man/10/conf
+os	/man/10/devattach
+os	/man/10/dmainit
+os	/man/10/error
+os	/man/10/inb
+os	/man/10/intrenable
+os	/man/10/kbdputc
+os	/man/10/lock
+os	/man/10/master
+os	/man/10/mk
+os	/man/10/newchan
+os	/man/10/parsecmd
+os	/man/10/qio
+os	/man/10/qlock
+os	/man/10/readnum
+os	/man/10/ref
+os	/man/10/sleep
+os	/man/2/geodesy
+os	/man/2/sys-0intro
+os	/man/2/sys-dial
+os	/man/2/sys-export
+os	/man/2/sys-file2chan
+os	/man/2/sys-iounit
+os	/man/2/sys-print
+os	/man/3/arch
+os	/man/3/audio
+os	/man/3/boot
+os	/man/3/cap
+os	/man/3/cmd
+os	/man/3/cons
+os	/man/3/dbg
+os	/man/3/draw
+os	/man/3/ds
+os	/man/3/dup
+os	/man/3/dynld
+os	/man/3/eia
+os	/man/3/env
+os	/man/3/ether
+os	/man/3/flash
+os	/man/3/floppy
+os	/man/3/fpga
+os	/man/3/ftl
+os	/man/3/gpio
+os	/man/3/i2c
+os	/man/3/i82365
+os	/man/3/indir
+os	/man/3/ip
+os	/man/3/kprof
+os	/man/3/logfs
+os	/man/3/lpt
+os	/man/3/mnt
+os	/man/3/mpeg
+os	/man/3/pbus
+os	/man/3/pipe
+os	/man/3/plap
+os	/man/3/pnp
+os	/man/3/pointer
+os	/man/3/prog
+os	/man/3/root
+os	/man/3/rtc
+os	/man/3/sd
+os	/man/3/sign
+os	/man/3/srv
+os	/man/3/switch
+os	/man/3/tinyfs
+os	/man/3/tls
+os	/man/3/touch
+os	/man/3/tv
+os	/man/3/usb
+os	/man/3/vga
+os	/man/3/vid
+os	/man/4/namespace
+os	/man/6/dis
+os	/man/6/sbl
+os	/man/8/init
+os	/man/8/mangaload
+os	/man/8/prep
+os	/man/8/rdbgsrv
+os.b	/man/1/os
+os.c	/man/10/lock
+os2en	/man/2/geodesy
+oseek	/man/1/dd
+osenv.errstr	/man/10/error
+osgb36	/man/2/geodesy
+osinit	/man/3/rtc
+osinit	/man/8/init
+osinit.dis	/man/10/9load
+osinit.dis	/man/10/plan9.ini
+osinit.dis	/man/3/root
+osinit.dis	/man/8/init
+osmtpserver	/man/2/dhcpclient
+otftpserver	/man/2/dhcpclient
+other's	/man/2/keyring-0intro
+other's	/man/6/auth
+otherhand	/man/10/devattach
+otrunc	/man/10/devattach
+otrunc	/man/2/styxservers
+otrunc	/man/2/sys-open
+otrunc	/man/5/open
+otrunc	/man/5/stat
+ouch	/man/2/tk
+oup	/man/2/spree-cardlib
+ouput	/man/4/dossrv
+ousterhout's	/man/2/tk
+outb	/man/10/inb
+outbound	/man/4/factotum
+outcome	/man/10/strcat
+outer	/man/10/2c
+outer	/man/10/error
+outer	/man/9/0intro
+outermost	/man/1/disdep
+outermost	/man/1/m4
+outermost	/man/1/sh-alphabet
+outermost	/man/10/error
+outermost	/man/2/asn1
+outermost	/man/2/readdir
+outermost	/man/2/sh
+outfile	/man/1/cook
+outfile	/man/10/5cv
+outgoing	/man/3/ip
+outgoing	/man/3/ssl
+outgoing	/man/3/tls
+outgooing	/man/3/ip
+outl	/man/10/inb
+outline	/man/2/draw-image
+outline	/man/9/canvas
+outlined	/man/2/dialog
+outlined	/man/2/draw-image
+outlines	/man/9/canvas
+outlinewidth	/man/9/canvas
+outlining	/man/2/draw-image
+outlining	/man/3/draw
+output	/man/1/acme
+output	/man/1/alphabet-abc
+output	/man/1/alphabet-fs
+output	/man/1/alphabet-grid
+output	/man/1/asm
+output	/man/1/auplay
+output	/man/1/basename
+output	/man/1/calc
+output	/man/1/cat
+output	/man/1/cleanname
+output	/man/1/cmp
+output	/man/1/collab
+output	/man/1/comm
+output	/man/1/cook
+output	/man/1/crypt
+output	/man/1/date
+output	/man/1/dd
+output	/man/1/diff
+output	/man/1/disdep
+output	/man/1/du
+output	/man/1/echo
+output	/man/1/env
+output	/man/1/fc
+output	/man/1/filename
+output	/man/1/fmt
+output	/man/1/fs
+output	/man/1/gettar
+output	/man/1/grep
+output	/man/1/gzip
+output	/man/1/idea
+output	/man/1/itest
+output	/man/1/kill
+output	/man/1/limbo
+output	/man/1/listen
+output	/man/1/ls
+output	/man/1/m4
+output	/man/1/man
+output	/man/1/mash
+output	/man/1/mash-tk
+output	/man/1/mc
+output	/man/1/mk
+output	/man/1/mprof
+output	/man/1/ns
+output	/man/1/os
+output	/man/1/p
+output	/man/1/prof
+output	/man/1/ps
+output	/man/1/read
+output	/man/1/secstore
+output	/man/1/sh
+output	/man/1/sh-arg
+output	/man/1/sh-expr
+output	/man/1/sh-file2chan
+output	/man/1/sh-std
+output	/man/1/sort
+output	/man/1/stack
+output	/man/1/stream
+output	/man/1/tail
+output	/man/1/tcs
+output	/man/1/tee
+output	/man/1/telnet
+output	/man/1/timestamp
+output	/man/1/tiny
+output	/man/1/tkcmd
+output	/man/1/tktester
+output	/man/1/tr
+output	/man/1/tsort
+output	/man/1/unicode
+output	/man/1/uniq
+output	/man/1/uuencode
+output	/man/1/wc
+output	/man/1/webgrab
+output	/man/1/wm-sh
+output	/man/1/xd
+output	/man/1/yacc
+output	/man/1/zeros
+output	/man/10/2a
+output	/man/10/2c
+output	/man/10/2l
+output	/man/10/5cv
+output	/man/10/acid
+output	/man/10/c2l
+output	/man/10/inb
+output	/man/10/inm
+output	/man/10/mk
+output	/man/10/ms2
+output	/man/10/odbc
+output	/man/10/print
+output	/man/10/qio
+output	/man/10/srclist
+output	/man/10/xalloc
+output	/man/2/arg
+output	/man/2/asn1
+output	/man/2/bufio
+output	/man/2/daytime
+output	/man/2/draw-context
+output	/man/2/filter-deflate
+output	/man/2/imagefile
+output	/man/2/ip
+output	/man/2/json
+output	/man/2/keyring-getstring
+output	/man/2/keyring-rc4
+output	/man/2/keyring-sha1
+output	/man/2/math-export
+output	/man/2/msgio
+output	/man/2/plumbmsg
+output	/man/2/print
+output	/man/2/prof
+output	/man/2/scsiio
+output	/man/2/security-0intro
+output	/man/2/sexprs
+output	/man/2/sys-0intro
+output	/man/2/sys-dup
+output	/man/2/sys-print
+output	/man/2/ubfa
+output	/man/2/w3c-css
+output	/man/3/audio
+output	/man/3/cmd
+output	/man/3/cons
+output	/man/3/dbg
+output	/man/3/draw
+output	/man/3/eia
+output	/man/3/fpga
+output	/man/3/ip
+output	/man/3/mpeg
+output	/man/3/usb
+output	/man/3/vid
+output	/man/4/acme
+output	/man/4/grid-cpu
+output	/man/4/iostats
+output	/man/6/colour
+output	/man/6/man
+output	/man/7/cddb
+output	/man/8/ai2key
+output	/man/8/applylog
+output	/man/8/bootpd
+output	/man/8/create
+output	/man/8/cs
+output	/man/8/dhcp
+output	/man/8/fpgaload
+output	/man/8/ftl
+output	/man/8/mkfs
+output	/man/8/ping
+output	/man/8/prep
+output	/man/8/rdbgsrv
+output	/man/8/styxchat
+output	/man/8/touchcal
+outputs	/man/1/acme
+outputs	/man/1/calc
+outputs	/man/1/du
+outputs	/man/1/mash
+outputs	/man/1/math-misc
+outputs	/man/1/mprof
+outputs	/man/1/prof
+outputs	/man/1/sh
+outputs	/man/1/tcs
+outputs	/man/10/c2l
+outputs	/man/2/sys-werrstr
+outputs	/man/3/audio
+outs	/man/10/inb
+outsb	/man/10/inb
+outside	/man/1/grid-ns
+outside	/man/1/tktester
+outside	/man/1/tsort
+outside	/man/10/acid
+outside	/man/10/conf
+outside	/man/10/devattach
+outside	/man/10/dmainit
+outside	/man/10/intrenable
+outside	/man/2/draw-image
+outside	/man/2/plumbmsg
+outside	/man/2/sexprs
+outside	/man/2/styxflush
+outside	/man/2/sys-0intro
+outside	/man/2/sys-self
+outside	/man/2/ubfa
+outside	/man/2/w3c-css
+outside	/man/3/draw
+outside	/man/3/kprof
+outside	/man/3/root
+outside	/man/3/tls
+outside	/man/4/grid-cpu
+outside	/man/4/iostats
+outside	/man/4/namespace
+outside	/man/5/0intro
+outside	/man/6/ubfa
+outside	/man/8/touchcal
+outside	/man/9/bind
+outside	/man/9/canvas
+outside	/man/9/entry
+outside	/man/9/grab
+outside	/man/9/grid
+outside	/man/9/listbox
+outside	/man/9/options
+outside	/man/9/panel
+outside	/man/9/scrollbar
+outsl	/man/10/inb
+outss	/man/10/inb
+outstanding	/man/1/sh-file2chan
+outstanding	/man/2/styxflush
+outstanding	/man/2/styxpersist
+outstanding	/man/2/wmsrv
+outstanding	/man/5/0intro
+outstanding	/man/5/version
+oval	/man/9/canvas
+oval's	/man/9/canvas
+ovals	/man/9/canvas
+ovendor	/man/2/dhcpclient
+ovendorclass	/man/2/dhcpclient
+ovendorinfo	/man/2/dhcpclient
+overall	/man/2/draw-image
+overall	/man/2/regex
+overall	/man/9/canvas
+overall	/man/9/menu
+overall	/man/9/text
+overestimate	/man/9/canvas
+overflow	/man/10/memory
+overflow	/man/10/strcat
+overflow	/man/2/math-fp
+overflow	/man/2/print
+overflows	/man/2/sys-millisec
+overflows	/man/3/pbus
+overhead	/man/1/charon
+overhead	/man/2/dict
+overhead	/man/2/styx
+overhead	/man/4/iostats
+overhead	/man/8/ftl
+overkill	/man/6/login
+overlap	/man/10/memory
+overlap	/man/6/image
+overlap	/man/8/prep
+overlap	/man/9/canvas
+overlap	/man/9/lower
+overlap	/man/9/raise
+overlap	/man/9/scrollbar
+overlapping	/man/10/memory
+overlapping	/man/10/strcat
+overlapping	/man/2/math-fp
+overlapping	/man/3/draw
+overlapping	/man/9/canvas
+overlaps	/man/8/prep
+overlay	/man/3/mpeg
+overlay	/man/3/vga
+overlays	/man/1/blur
+overridden	/man/1/acme
+overridden	/man/1/emu
+overridden	/man/10/9load
+overridden	/man/9/text
+override	/man/1/charon
+override	/man/1/mk
+override	/man/10/acid
+override	/man/10/mk
+override	/man/10/plan9.ini
+override	/man/2/styxservers
+override	/man/2/translate
+override	/man/5/0intro
+override	/man/9/options
+override	/man/9/text
+overriden	/man/9/text
+overrides	/man/1/mk
+overrides	/man/10/mk
+overrides	/man/3/draw
+overrides	/man/9/menu
+overrides	/man/9/options
+overrides	/man/9/text
+overriding	/man/9/listbox
+overstrike	/man/9/text
+overview	/man/1/0intro
+overview	/man/2/tk
+overview	/man/4/namespace
+overview	/man/9/0intro
+overview	/man/9/canvas
+overwrite	/man/1/grid-session
+overwrite	/man/1/uuencode
+overwrite	/man/2/palmfile
+overwriting	/man/1/gettar
+overwritten	/man/1/mv
+overwritten	/man/1/tiny
+overwritten	/man/10/getfields
+ovfl	/man/2/math-fp
+ovid	/man/1/mux
+owing	/man/8/ftl
+owing	/man/8/ping
+owned	/man/1/kill
+owned	/man/10/styxserver
+owned	/man/2/spree-cardlib
+owned	/man/2/styxservers
+owned	/man/3/cap
+owned	/man/3/draw
+owned	/man/3/srv
+owned	/man/4/memfs
+owned	/man/6/users
+owner	/man/1/alphabet-fs
+owner	/man/1/chgrp
+owner	/man/1/chmod
+owner	/man/1/cp
+owner	/man/1/fs
+owner	/man/1/ls
+owner	/man/1/ps
+owner	/man/1/vacget
+owner	/man/10/devattach
+owner	/man/10/eve
+owner	/man/10/kproc
+owner	/man/10/styxserver
+owner	/man/2/debug
+owner	/man/2/keyring-0intro
+owner	/man/2/keyring-gensk
+owner	/man/2/keyset
+owner	/man/2/registries
+owner	/man/2/security-0intro
+owner	/man/2/spree
+owner	/man/2/spree-cardlib
+owner	/man/2/sys-open
+owner	/man/2/sys-stat
+owner	/man/3/boot
+owner	/man/3/cap
+owner	/man/3/dynld
+owner	/man/3/fs
+owner	/man/3/sign
+owner	/man/3/srv
+owner	/man/4/factotum
+owner	/man/4/memfs
+owner	/man/4/registry
+owner	/man/5/0intro
+owner	/man/5/open
+owner	/man/5/stat
+owner	/man/6/keytext
+owner	/man/6/proto
+owner	/man/8/create
+owner	/man/8/kfscmd
+owner	/man/8/signer
+ownerid	/man/2/spree-cardlib
+owners	/man/2/sys-stat
+owners	/man/8/mkfs
+ownership	/man/1/chgrp
+ownership	/man/2/security-0intro
+ownership	/man/2/sys-0intro
+ownership	/man/3/fs
+ownership	/man/4/kfs
+ownership	/man/8/applylog
+owning	/man/3/ip
+owning	/man/6/proto
+owning	/man/8/create
+owns	/man/2/keyset
+owns	/man/2/security-0intro
+owns	/man/2/spree-cardlib
+owns	/man/9/grab
+owrite	/man/10/devattach
+owrite	/man/10/newchan
+owrite	/man/10/styxserver
+owrite	/man/2/bufio
+owrite	/man/2/styxservers
+owrite	/man/2/sys-open
+owrite	/man/5/open
+owwwserver	/man/2/dhcpclient
+p.b	/man/1/p
+p.i	/man/6/man
+p.x	/man/6/font
+p.y	/man/6/font
+p0,p1	/man/2/draw-image
+p9any	/man/4/factotum
+p9sk1	/man/4/factotum
+pa	/man/2/ubfa
+pa	/man/6/ubfa
+pack	/man/1/tktester
+pack	/man/2/ida
+pack	/man/2/palmfile
+pack	/man/2/plumbmsg
+pack	/man/2/prefab-element
+pack	/man/2/sexprs
+pack	/man/2/spki
+pack	/man/2/spree-cardlib
+pack	/man/2/styx
+pack	/man/6/colour
+pack	/man/9/0intro
+pack	/man/9/grid
+pack	/man/9/pack
+package	/man/1/ebook
+package	/man/2/keyring-certtostr
+package	/man/6/man
+package	/man/8/create
+package.tgz	/man/4/ftpfs
+packaged	/man/2/prefab-element
+packages	/man/1/ar
+packages	/man/1/man
+packages	/man/8/create
+packblock	/man/10/allocb
+packdir	/man/2/styx
+packdir	/man/5/stat
+packdirsize	/man/2/styx
+packed	/man/1/tktester
+packed	/man/2/asn1
+packed	/man/2/disks
+packed	/man/2/draw-image
+packed	/man/2/keyring-getmsg
+packed	/man/2/msgio
+packed	/man/2/plumbmsg
+packed	/man/2/popup
+packed	/man/2/sets
+packed	/man/2/spree-cardlib
+packed	/man/2/styx
+packed	/man/2/tabs
+packed	/man/3/tls
+packed	/man/3/vga
+packed	/man/5/walk
+packed	/man/6/colour
+packed	/man/6/dis
+packed	/man/9/canvas
+packed	/man/9/pack
+packedsize	/man/2/sexprs
+packedsize	/man/2/styx
+packer	/man/9/pack
+packet	/man/10/qio
+packet	/man/3/ether
+packet	/man/3/ip
+packet	/man/3/pbus
+packet	/man/3/plap
+packet	/man/3/usb
+packet	/man/4/palmsrv
+packet	/man/8/ping
+packet	/man/8/rip
+packets	/man/10/plan9.ini
+packets	/man/2/ether
+packets	/man/2/ip
+packets	/man/3/ether
+packets	/man/3/ip
+packets	/man/3/pbus
+packets	/man/3/usb
+packets	/man/8/ping
+packets	/man/8/rip
+packing	/man/1/dd
+packing	/man/1/tktester
+packing	/man/10/styx
+packing	/man/2/spree-cardlib
+packing	/man/2/venti
+packing	/man/6/colour
+packing	/man/9/pack
+packopts	/man/2/spree-cardlib
+packs	/man/2/devpointer
+packs	/man/2/plumbmsg
+packs	/man/2/prefab-element
+packs	/man/9/pack
+pad	/man/1/dd
+pad	/man/10/allocb
+pad	/man/10/print
+pad	/man/9/grid
+padblock	/man/10/allocb
+padded	/man/1/tr
+padded	/man/1/xd
+padded	/man/10/5cv
+padded	/man/10/ar
+padded	/man/10/print
+padded	/man/2/encoding
+padded	/man/2/keyring-getmsg
+padded	/man/2/msgio
+padded	/man/2/sys-print
+padded	/man/6/font
+padded	/man/6/image
+padding	/man/1/sh-string
+padding	/man/1/tktester
+padding	/man/10/allocb
+padding	/man/10/ar
+padding	/man/2/sets
+padding	/man/3/pbus
+padding	/man/3/tls
+padding	/man/6/colour
+padding	/man/9/grid
+padding	/man/9/options
+padding	/man/9/pack
+padding	/man/9/text
+padl	/man/1/sh-string
+padr	/man/1/sh-string
+pads	/man/3/pbus
+pads	/man/9/grid
+padx	/man/9/grid
+padx	/man/9/options
+padx	/man/9/pack
+padx	/man/9/text
+pady	/man/9/grid
+pady	/man/9/options
+pady	/man/9/pack
+pady	/man/9/text
+pag	/man/2/dbm
+page	/man/1/0intro
+page	/man/1/bind
+page	/man/1/charon
+page	/man/1/chgrp
+page	/man/1/man
+page	/man/1/p
+page	/man/1/webgrab
+page	/man/10/a.out
+page	/man/10/dmainit
+page	/man/10/ms2
+page	/man/10/xalloc
+page	/man/2/0intro
+page	/man/2/convcs
+page	/man/2/draw-example
+page	/man/2/filter
+page	/man/2/print
+page	/man/2/spree
+page	/man/2/tabs
+page	/man/2/w3c-css
+page	/man/3/flash
+page	/man/3/ip
+page	/man/6/keyboard
+page	/man/6/man
+page	/man/6/ubfa
+page	/man/9/0intro
+page	/man/9/1copyright
+page	/man/9/scrollbar
+page	/man/9/text
+pages	/man/1/charon
+pages	/man/1/ebook
+pages	/man/1/man
+pages	/man/1/tktester
+pages	/man/1/webgrab
+pages	/man/1/wm-misc
+pages	/man/10/intrenable
+pages	/man/2/0intro
+pages	/man/2/filter
+pages	/man/2/math-0intro
+pages	/man/2/print
+pages	/man/2/tabs
+pages	/man/3/0intro
+pages	/man/4/namespace
+pages	/man/6/man
+pages	/man/6/ndb
+pages	/man/8/httpd
+pages	/man/9/0intro
+pages	/man/9/1copyright
+pages	/man/9/canvas
+pages	/man/9/entry
+pages	/man/9/listbox
+pages	/man/9/scrollbar
+pages	/man/9/text
+pagesm	/man/1/miniterm
+paginate	/man/1/p
+paint	/man/2/draw-display
+paint	/man/2/draw-example
+paint	/man/2/draw-screen
+paint	/man/2/mpeg
+paint	/man/2/prefab-compound
+pair	/man/1/charon
+pair	/man/1/dd
+pair	/man/1/mash
+pair	/man/1/sh
+pair	/man/1/sh-csv
+pair	/man/2/attrdb
+pair	/man/2/dbm
+pair	/man/2/dial
+pair	/man/2/dict
+pair	/man/2/encoding
+pair	/man/2/ether
+pair	/man/2/factotum
+pair	/man/2/hash
+pair	/man/2/keyring-0intro
+pair	/man/2/keyring-gensk
+pair	/man/2/lists
+pair	/man/2/regex
+pair	/man/2/security-0intro
+pair	/man/2/security-login
+pair	/man/2/string
+pair	/man/2/sys-dial
+pair	/man/3/prof
+pair	/man/3/prog
+pair	/man/4/factotum
+pair	/man/4/registry
+pair	/man/6/attrdb
+pair	/man/6/json
+pair	/man/6/keyboard
+pair	/man/8/ai2key
+pair	/man/9/text
+paired	/man/2/lists
+paired	/man/2/string
+paired	/man/4/registry
+pairs	/man/1/acme
+pairs	/man/1/diff
+pairs	/man/1/disdep
+pairs	/man/1/grid-query
+pairs	/man/1/mash
+pairs	/man/1/sh
+pairs	/man/1/sh-arg
+pairs	/man/1/sh-std
+pairs	/man/1/stack
+pairs	/man/2/attrdb
+pairs	/man/2/cfg
+pairs	/man/2/daytime
+pairs	/man/2/dbm
+pairs	/man/2/dict
+pairs	/man/2/dis
+pairs	/man/2/env
+pairs	/man/2/factotum
+pairs	/man/2/hash
+pairs	/man/2/json
+pairs	/man/2/keyring-0intro
+pairs	/man/2/plumbmsg
+pairs	/man/2/pop3
+pairs	/man/2/registries
+pairs	/man/2/rfc822
+pairs	/man/2/spree
+pairs	/man/2/stringinttab
+pairs	/man/2/styxpersist
+pairs	/man/2/xml
+pairs	/man/3/prof
+pairs	/man/4/factotum
+pairs	/man/4/import
+pairs	/man/4/registry
+pairs	/man/6/attrdb
+pairs	/man/6/json
+pairs	/man/6/plumbing
+pairs	/man/8/ai2key
+pairs	/man/8/collabsrv
+pairs	/man/8/cs
+pairs	/man/9/button
+pairs	/man/9/canvas
+pairs	/man/9/checkbutton
+pairs	/man/9/choicebutton
+pairs	/man/9/entry
+pairs	/man/9/frame
+pairs	/man/9/grid
+pairs	/man/9/label
+pairs	/man/9/listbox
+pairs	/man/9/menu
+pairs	/man/9/menubutton
+pairs	/man/9/pack
+pairs	/man/9/panel
+pairs	/man/9/radiobutton
+pairs	/man/9/scale
+pairs	/man/9/scrollbar
+pairs	/man/9/text
+pairwise	/man/1/mash
+pairwise	/man/1/sh
+pal	/man/3/vid
+paleblue	/man/2/draw-display
+palebluegreen	/man/2/draw-display
+palegreen	/man/2/draw-display
+palegreyblue	/man/2/draw-display
+palegreygreen	/man/2/draw-display
+palette	/man/1/collab-clients
+palette	/man/1/wm-misc
+palette	/man/3/vga
+palettedepth	/man/3/vga
+paleyellow	/man/2/draw-display
+palm	/man/2/palmfile
+palm	/man/4/palmsrv
+palmfile	/man/2/palmfile
+palmfile	/man/4/palmsrv
+palmfile.b	/man/2/palmfile
+palmfile.m	/man/2/palmfile
+palmsrv	/man/4/palmsrv
+palmsrv.b	/man/4/palmsrv
+palm®	/man/2/palmfile
+palm™	/man/2/palmfile
+pan	/man/3/vga
+pane	/man/1/grid-session
+panel	/man/1/charon
+panel	/man/1/deb
+panel	/man/1/ftree
+panel	/man/1/logon
+panel	/man/1/wm-misc
+panel	/man/2/selectfile
+panel	/man/2/tk
+panel	/man/3/touch
+panel	/man/3/vid
+panel	/man/8/touchcal
+panel	/man/9/panel
+panel's	/man/9/panel
+panels	/man/1/charon
+panels	/man/1/deb
+panels	/man/3/touch
+panelx	/man/9/panel
+panely	/man/9/panel
+panic	/man/10/allocb
+panic	/man/10/devattach
+panic	/man/10/error
+panic	/man/10/panic
+panic	/man/10/ref
+panic	/man/10/sleep
+panic	/man/10/xalloc
+panning	/man/3/vga
+pap	/man/3/ip
+paper	/man/1/0intro
+paper	/man/2/draw-image
+paper	/man/2/print
+paper	/man/2/pslib
+paper	/man/2/spree
+paper	/man/6/colour
+paper.cfg	/man/2/print
+papers	/man/1/0intro
+papers	/man/2/print
+par	/man/1/cook
+parabolic	/man/2/draw-example
+paragraph	/man/1/acme
+paragraph	/man/6/man
+paragraphs	/man/6/man
+parallel	/man/1/cp
+parallel	/man/1/mk
+parallel	/man/1/sh
+parallel	/man/1/sh-tk
+parallel	/man/10/mk
+parallel	/man/2/sh
+parallel	/man/3/lpt
+param	/man/2/filter
+param	/man/2/filter-deflate
+param	/man/2/filter-slip
+param	/man/2/styxservers
+param	/man/7/db
+paramaterized	/man/2/convcs
+parameter	/man/1/calc
+parameter	/man/1/collab-clients
+parameter	/man/1/mash-tk
+parameter	/man/1/miniterm
+parameter	/man/10/2c
+parameter	/man/10/a.out
+parameter	/man/10/c2l
+parameter	/man/10/devattach
+parameter	/man/10/dynld
+parameter	/man/10/inm
+parameter	/man/10/ntsrv
+parameter	/man/10/odbc
+parameter	/man/10/plan9.ini
+parameter	/man/10/print
+parameter	/man/2/arg
+parameter	/man/2/bufio
+parameter	/man/2/crc
+parameter	/man/2/dbm
+parameter	/man/2/dhcpclient
+parameter	/man/2/draw-image
+parameter	/man/2/draw-screen
+parameter	/man/2/env
+parameter	/man/2/factotum
+parameter	/man/2/geodesy
+parameter	/man/2/ida
+parameter	/man/2/json
+parameter	/man/2/keyring-sha1
+parameter	/man/2/lists
+parameter	/man/2/plumbmsg
+parameter	/man/2/popup
+parameter	/man/2/rfc822
+parameter	/man/2/secstore
+parameter	/man/2/styx
+parameter	/man/2/styxservers
+parameter	/man/2/sys-0intro
+parameter	/man/2/sys-fversion
+parameter	/man/2/sys-read
+parameter	/man/2/sys-self
+parameter	/man/2/ubfa
+parameter	/man/2/w3c-css
+parameter	/man/2/w3c-uris
+parameter	/man/2/w3c-xpointers
+parameter	/man/3/0intro
+parameter	/man/3/dbg
+parameter	/man/3/draw
+parameter	/man/3/flash
+parameter	/man/3/ip
+parameter	/man/4/factotum
+parameter	/man/5/0intro
+parameter	/man/8/cs
+parameter	/man/8/dhcp
+parameter	/man/8/prep
+parameter	/man/8/styxchat
+parameter	/man/9/types
+parameters	/man/1/blur
+parameters	/man/1/mash-tk
+parameters	/man/10/conf
+parameters	/man/10/dev
+parameters	/man/10/devattach
+parameters	/man/10/intrenable
+parameters	/man/10/qio
+parameters	/man/10/readnum
+parameters	/man/2/bloomfilter
+parameters	/man/2/bufio
+parameters	/man/2/dhcpclient
+parameters	/man/2/draw-image
+parameters	/man/2/factotum
+parameters	/man/2/geodesy
+parameters	/man/2/ida
+parameters	/man/2/ip
+parameters	/man/2/json
+parameters	/man/2/keyring-0intro
+parameters	/man/2/keyring-auth
+parameters	/man/2/keyring-gensk
+parameters	/man/2/math-linalg
+parameters	/man/2/rfc822
+parameters	/man/2/security-0intro
+parameters	/man/2/security-login
+parameters	/man/2/sexprs
+parameters	/man/2/ubfa
+parameters	/man/3/draw
+parameters	/man/3/dynld
+parameters	/man/3/fpga
+parameters	/man/3/ftl
+parameters	/man/3/ip
+parameters	/man/3/logfs
+parameters	/man/3/touch
+parameters	/man/3/usb
+parameters	/man/4/factotum
+parameters	/man/4/grid-cpu
+parameters	/man/5/0intro
+parameters	/man/6/auth
+parameters	/man/6/man
+parameters	/man/6/ndb
+parameters	/man/8/ai2key
+parameters	/man/8/dhcp
+params	/man/10/plan9.ini
+params	/man/2/factotum
+params	/man/2/rfc822
+params	/man/2/xml
+parcel	/man/9/pack
+parent	/man/1/0intro
+parent	/man/1/mkdir
+parent	/man/1/mv
+parent	/man/1/sh-alphabet
+parent	/man/1/tktester
+parent	/man/10/2c
+parent	/man/10/devattach
+parent	/man/10/styxserver
+parent	/man/2/alphabet-intro
+parent	/man/2/command
+parent	/man/2/dialog
+parent	/man/2/draw-context
+parent	/man/2/draw-screen
+parent	/man/2/selectfile
+parent	/man/2/spree
+parent	/man/2/spree-cardlib
+parent	/man/2/styxservers
+parent	/man/2/styxservers-nametree
+parent	/man/2/sys-0intro
+parent	/man/2/sys-pctl
+parent	/man/2/sys-stat
+parent	/man/2/xml
+parent	/man/3/flash
+parent	/man/4/ramfile
+parent	/man/5/0intro
+parent	/man/5/remove
+parent	/man/5/stat
+parent	/man/5/walk
+parent	/man/9/grid
+parent	/man/9/pack
+parent's	/man/1/0intro
+parentheses	/man/1/m4
+parentheses	/man/1/mash
+parentheses	/man/1/mk
+parentheses	/man/1/sh
+parentheses	/man/1/sh-expr
+parentheses	/man/10/mk
+parentheses	/man/2/math-0intro
+parentheses	/man/2/regex
+parentheses	/man/2/sexprs
+parentheses	/man/9/scale
+parentheses	/man/9/scrollbar
+parenthesis	/man/1/acme
+parenthesis	/man/1/m4
+parenthesised	/man/1/sh-regex
+parenthesised	/man/6/plumbing
+parenthesised	/man/6/sexprs
+parenthesized	/man/1/sh-regex
+parenthesized	/man/2/regex
+parentid	/man/2/spree
+parentid	/man/4/spree
+parentpath	/man/2/styxservers-nametree
+parents	/man/1/grid-ns
+parity	/man/10/plan9.ini
+parity	/man/2/keyring-crypt
+parity	/man/3/eia
+parity	/man/8/mangaload
+parlance	/man/2/sys-0intro
+parse	/man/1/alphabet-main
+parse	/man/1/mash
+parse	/man/1/sh
+parse	/man/1/sh-csv
+parse	/man/1/sh-sexprs
+parse	/man/1/sh-std
+parse	/man/1/yacc
+parse	/man/10/parsecmd
+parse	/man/2/arg
+parse	/man/2/cfg
+parse	/man/2/dhcpclient
+parse	/man/2/ether
+parse	/man/2/ip
+parse	/man/2/math-0intro
+parse	/man/2/rfc822
+parse	/man/2/sexprs
+parse	/man/2/sh
+parse	/man/2/spki
+parse	/man/2/styxservers
+parse	/man/2/venti
+parse	/man/2/w3c-css
+parse	/man/2/w3c-uris
+parse	/man/2/w3c-xpointers
+parse	/man/2/wait
+parse	/man/5/stat
+parse	/man/6/sexprs
+parse.c	/man/10/parsecmd
+parseattrs	/man/2/factotum
+parsecert	/man/2/spki
+parsecidr	/man/2/ip
+parsecmd	/man/10/parsecmd
+parsecmd	/man/3/cmd
+parsecompound	/man/2/spki
+parsecontent	/man/2/rfc822
+parsed	/man/1/mash
+parsed	/man/1/sh
+parsed	/man/1/sh-arg
+parsed	/man/1/tiny
+parsed	/man/10/mk
+parsed	/man/2/plumbmsg
+parsed	/man/2/sh
+parsed	/man/2/spki
+parsed	/man/2/xml
+parsed	/man/3/cmd
+parsed	/man/8/rstyxd
+parsedecl	/man/2/w3c-css
+parseentry	/man/2/attrdb
+parsefields	/man/10/parsecmd
+parsehash	/man/2/spki
+parsekey	/man/2/spki
+parseline	/man/2/attrdb
+parseman.b	/man/1/man
+parsemask	/man/2/ip
+parsename	/man/2/spki
+parseparams	/man/2/rfc822
+parser	/man/1/yacc
+parser	/man/2/asn1
+parser	/man/2/cfg
+parser	/man/2/w3c-css
+parser	/man/2/w3c-xpointers
+parser	/man/2/xml
+parser	/man/6/sexprs
+parser	/man/9/bind
+parser.mark	/man/2/xml
+parses	/man/1/alphabet-main
+parses	/man/1/sh-arg
+parses	/man/1/sh-std
+parses	/man/10/9load
+parses	/man/10/parsecmd
+parses	/man/2/arg
+parses	/man/2/cfg
+parses	/man/2/disks
+parses	/man/2/factotum
+parses	/man/2/rfc822
+parses	/man/2/spki
+parses	/man/2/spree
+parses	/man/2/w3c-css
+parses	/man/2/w3c-xpointers
+parseseq	/man/2/spki
+parsesig	/man/2/spki
+parsevalid	/man/2/spki
+parsing	/man/1/mash
+parsing	/man/1/mk
+parsing	/man/1/sh
+parsing	/man/1/sh-arg
+parsing	/man/1/webgrab
+parsing	/man/1/yacc
+parsing	/man/10/mk
+parsing	/man/2/spki
+parsing	/man/2/spree
+parsing	/man/2/w3c-css
+parsing	/man/2/w3c-uris
+parsing	/man/2/xml
+parsing	/man/6/sexprs
+partial	/man/1/cprof
+partial	/man/1/dd
+partial	/man/1/tsort
+partial	/man/10/acid
+partial	/man/10/devattach
+partial	/man/2/draw-image
+partial	/man/2/prof
+partial	/man/2/spki
+partial	/man/4/acme
+partial	/man/8/create
+partially	/man/10/qio
+partially	/man/2/draw-image
+partially	/man/2/keyring-0intro
+partially	/man/9/text
+participate	/man/1/look
+participate	/man/2/regex
+participate	/man/2/spree
+participate	/man/2/spree-gather
+particular	/man/1/alphabet-fs
+particular	/man/1/bind
+particular	/man/1/cprof
+particular	/man/1/filename
+particular	/man/1/fs
+particular	/man/1/man
+particular	/man/1/mprof
+particular	/man/1/os
+particular	/man/1/prof
+particular	/man/1/sh-std
+particular	/man/1/tktester
+particular	/man/1/wm-misc
+particular	/man/10/0intro
+particular	/man/10/2c
+particular	/man/10/9load
+particular	/man/10/allocb
+particular	/man/10/conf
+particular	/man/10/devattach
+particular	/man/10/dynld
+particular	/man/10/error
+particular	/man/10/eve
+particular	/man/10/kproc
+particular	/man/10/master
+particular	/man/10/newchan
+particular	/man/10/sleep
+particular	/man/10/styxserver
+particular	/man/2/0intro
+particular	/man/2/asn1
+particular	/man/2/convcs
+particular	/man/2/dial
+particular	/man/2/draw-display
+particular	/man/2/draw-example
+particular	/man/2/draw-image
+particular	/man/2/draw-screen
+particular	/man/2/format
+particular	/man/2/math-0intro
+particular	/man/2/math-fp
+particular	/man/2/palmfile
+particular	/man/2/plumbmsg
+particular	/man/2/prof
+particular	/man/2/readdir
+particular	/man/2/registries
+particular	/man/2/rfc822
+particular	/man/2/security-auth
+particular	/man/2/sh
+particular	/man/2/spki
+particular	/man/2/spki-verifier
+particular	/man/2/spree
+particular	/man/2/spree-cardlib
+particular	/man/2/spree-objstore
+particular	/man/2/srv
+particular	/man/2/styx
+particular	/man/2/styxservers
+particular	/man/2/sys-bind
+particular	/man/2/sys-dial
+particular	/man/2/sys-stat
+particular	/man/3/0intro
+particular	/man/3/audio
+particular	/man/3/cmd
+particular	/man/3/draw
+particular	/man/3/dynld
+particular	/man/3/flash
+particular	/man/3/ip
+particular	/man/3/pipe
+particular	/man/3/pnp
+particular	/man/3/prof
+particular	/man/3/rtc
+particular	/man/3/vga
+particular	/man/4/acme
+particular	/man/4/factotum
+particular	/man/4/spree
+particular	/man/5/0intro
+particular	/man/5/stat
+particular	/man/6/image
+particular	/man/6/keyboard
+particular	/man/6/ndb
+particular	/man/6/translate
+particular	/man/8/applylog
+particular	/man/8/collabsrv
+particular	/man/8/create
+particular	/man/8/prep
+particular	/man/8/svc
+particular	/man/9/0intro
+particular	/man/9/1copyright
+particular	/man/9/bind
+particular	/man/9/canvas
+particular	/man/9/entry
+particular	/man/9/grab
+particular	/man/9/listbox
+particular	/man/9/menu
+particular	/man/9/options
+particular	/man/9/radiobutton
+particular	/man/9/scale
+particular	/man/9/text
+particularly	/man/1/acme
+particularly	/man/1/charon
+particularly	/man/1/chmod
+particularly	/man/1/dd
+particularly	/man/10/0intro
+particularly	/man/2/0intro
+particularly	/man/2/imagefile
+particularly	/man/2/sys-fauth
+particularly	/man/2/sys-iounit
+particularly	/man/3/audio
+particularly	/man/8/styxchat
+particulary	/man/2/sys-read
+parties	/man/1/alphabet-abc
+parties	/man/1/alphabet-grid
+parties	/man/2/keyring-0intro
+parties	/man/2/keyring-auth
+parties	/man/2/security-0intro
+parties	/man/6/auth
+parties	/man/9/1copyright
+partition	/man/10/9load
+partition	/man/10/plan9.ini
+partition	/man/2/disks
+partition	/man/2/lists
+partition	/man/3/flash
+partition	/man/3/ftl
+partition	/man/3/ip
+partition	/man/3/logfs
+partition	/man/3/sd
+partition	/man/4/kfs
+partition	/man/8/ftl
+partition	/man/8/prep
+partition	/man/8/rdbgsrv
+partition's	/man/2/disks
+partition's	/man/8/prep
+partitioning	/man/10/9load
+partitioning	/man/10/plan9.ini
+partitioning	/man/2/disks
+partitioning	/man/3/ds
+partitions	/man/1/cprof
+partitions	/man/1/math-misc
+partitions	/man/1/mprof
+partitions	/man/1/prof
+partitions	/man/10/9load
+partitions	/man/2/disks
+partitions	/man/3/flash
+partitions	/man/3/floppy
+partitions	/man/3/sd
+partitions	/man/8/prep
+partitions	/man/8/rdbgsrv
+parts	/man/1/acme
+parts	/man/1/blur
+parts	/man/1/math-misc
+parts	/man/1/mprof
+parts	/man/1/prof
+parts	/man/10/allocb
+parts	/man/10/conf
+parts	/man/2/draw-screen
+parts	/man/2/keyring-gensk
+parts	/man/2/math-fp
+parts	/man/4/import
+parts	/man/4/ramfile
+parts	/man/6/colour
+parts.b	/man/1/math-misc
+party	/man/1/listen
+party	/man/2/keyring-auth
+party	/man/2/security-0intro
+party	/man/3/ip
+party	/man/4/factotum
+party	/man/6/login
+party	/man/8/register
+party	/man/9/1copyright
+party's	/man/10/5cv
+party's	/man/2/keyring-0intro
+party's	/man/4/factotum
+pass	/man/1/alphabet-fs
+pass	/man/1/fs
+pass	/man/1/mash-tk
+pass	/man/1/p
+pass	/man/1/sh
+pass	/man/10/2c
+pass	/man/10/c2l
+pass	/man/10/dev
+pass	/man/10/qio
+pass	/man/2/dbm
+pass	/man/2/draw-pointer
+pass	/man/2/factotum
+pass	/man/2/filter
+pass	/man/2/imagefile
+pass	/man/2/itslib
+pass	/man/2/keyring-ipint
+pass	/man/2/popup
+pass	/man/2/secstore
+pass	/man/2/security-auth
+pass	/man/2/spree
+pass	/man/2/styxpersist
+pass	/man/2/sys-pipe
+pass	/man/2/tk
+pass	/man/3/ip
+pass	/man/3/ssl
+pass	/man/3/tls
+pass	/man/4/factotum
+pass	/man/6/plumbing
+pass	/man/8/mkfs
+pass	/man/8/prep
+passes	/man/1/alphabet-abc
+passes	/man/1/alphabet-grid
+passes	/man/1/wm
+passes	/man/10/c2l
+passes	/man/10/devattach
+passes	/man/10/intrenable
+passes	/man/2/command
+passes	/man/2/tk
+passes	/man/9/button
+passes	/man/9/checkbutton
+passes	/man/9/menubutton
+passes	/man/9/radiobutton
+passing	/man/1/mash
+passing	/man/1/sh-std
+passing	/man/10/conf
+passing	/man/2/plumbmsg
+passing	/man/2/spree
+passing	/man/2/sys-self
+passing	/man/2/tk
+passing	/man/6/namespace
+passing	/man/7/db
+passing	/man/8/plumber
+passing	/man/9/0intro
+passive	/man/4/ftpfs
+passwd	/man/1/passwd
+passwd	/man/4/keysrv
+passwd	/man/6/keys
+passwd	/man/8/changelogin
+passwd.b	/man/1/passwd
+password	/man/1/acme
+password	/man/1/miniterm
+password	/man/1/netkey
+password	/man/1/passwd
+password	/man/2/factotum
+password	/man/2/keyring-0intro
+password	/man/2/palmfile
+password	/man/2/pop3
+password	/man/2/secstore
+password	/man/2/security-0intro
+password	/man/2/security-login
+password	/man/2/w3c-uris
+password	/man/4/factotum
+password	/man/4/ftpfs
+password	/man/6/keys
+password	/man/6/login
+password	/man/7/db
+password	/man/8/changelogin
+password	/man/8/getauthinfo
+password	/man/8/logind
+password	/man/8/svc
+password	/man/9/entry
+passwords	/man/4/factotum
+passwords	/man/8/changelogin
+passwords	/man/8/svc
+paste	/man/1/acme
+paste	/man/1/brutus
+paste	/man/1/ftree
+paste	/man/1/mash-tk
+paste	/man/1/wm-misc
+paste	/man/1/wm-sh
+paste	/man/4/acme
+pat	/man/2/filepat
+pat	/man/2/selectfile
+pat2regexp	/man/1/sh-regex
+patented	/man/2/keyring-crypt
+path	/man/1/acme
+path	/man/1/alphabet-fs
+path	/man/1/cleanname
+path	/man/1/collab-clients
+path	/man/1/cprof
+path	/man/1/emu
+path	/man/1/fs
+path	/man/1/ftree
+path	/man/1/gettar
+path	/man/1/grid-ns
+path	/man/1/ls
+path	/man/1/mash
+path	/man/1/mash-tk
+path	/man/1/mkdir
+path	/man/1/mprof
+path	/man/1/prof
+path	/man/1/pwd
+path	/man/1/rcmd
+path	/man/1/sh
+path	/man/1/sh-alphabet
+path	/man/1/sh-mload
+path	/man/1/stack
+path	/man/1/tiny
+path	/man/1/vacget
+path	/man/1/webgrab
+path	/man/1/yacc
+path	/man/10/2a
+path	/man/10/9load
+path	/man/10/a.out
+path	/man/10/conf
+path	/man/10/dev
+path	/man/10/devattach
+path	/man/10/newchan
+path	/man/10/plan9.ini
+path	/man/10/styxserver
+path	/man/2/0intro
+path	/man/2/arg
+path	/man/2/asn1
+path	/man/2/attrdb
+path	/man/2/bloomfilter
+path	/man/2/bufio
+path	/man/2/bufio-chanfill
+path	/man/2/cfg
+path	/man/2/command
+path	/man/2/complete
+path	/man/2/convcs
+path	/man/2/crc
+path	/man/2/csv
+path	/man/2/daytime
+path	/man/2/dbm
+path	/man/2/debug
+path	/man/2/devpointer
+path	/man/2/dhcpclient
+path	/man/2/dial
+path	/man/2/dialog
+path	/man/2/dict
+path	/man/2/dis
+path	/man/2/diskblocks
+path	/man/2/disks
+path	/man/2/dividers
+path	/man/2/draw-0intro
+path	/man/2/draw-context
+path	/man/2/draw-display
+path	/man/2/draw-example
+path	/man/2/draw-font
+path	/man/2/draw-image
+path	/man/2/draw-point
+path	/man/2/draw-pointer
+path	/man/2/draw-rect
+path	/man/2/draw-screen
+path	/man/2/drawmux
+path	/man/2/encoding
+path	/man/2/env
+path	/man/2/ether
+path	/man/2/exception
+path	/man/2/factotum
+path	/man/2/filepat
+path	/man/2/format
+path	/man/2/fsproto
+path	/man/2/geodesy
+path	/man/2/hash
+path	/man/2/ida
+path	/man/2/imagefile
+path	/man/2/ip
+path	/man/2/ir
+path	/man/2/itslib
+path	/man/2/json
+path	/man/2/keyring-0intro
+path	/man/2/keyring-auth
+path	/man/2/keyring-certtostr
+path	/man/2/keyring-crypt
+path	/man/2/keyring-gensk
+path	/man/2/keyring-getmsg
+path	/man/2/keyring-getstring
+path	/man/2/keyring-ipint
+path	/man/2/keyring-rc4
+path	/man/2/keyring-sha1
+path	/man/2/keyset
+path	/man/2/lists
+path	/man/2/lock
+path	/man/2/math-0intro
+path	/man/2/math-elem
+path	/man/2/math-export
+path	/man/2/math-fp
+path	/man/2/math-linalg
+path	/man/2/mpeg
+path	/man/2/msgio
+path	/man/2/names
+path	/man/2/newns
+path	/man/2/palmfile
+path	/man/2/plumbmsg
+path	/man/2/pop3
+path	/man/2/popup
+path	/man/2/prefab-0intro
+path	/man/2/prefab-compound
+path	/man/2/prefab-element
+path	/man/2/prefab-environ
+path	/man/2/prefab-style
+path	/man/2/print
+path	/man/2/prof
+path	/man/2/pslib
+path	/man/2/rand
+path	/man/2/readdir
+path	/man/2/regex
+path	/man/2/registries
+path	/man/2/rfc822
+path	/man/2/scsiio
+path	/man/2/secstore
+path	/man/2/security-auth
+path	/man/2/security-login
+path	/man/2/security-random
+path	/man/2/security-ssl
+path	/man/2/selectfile
+path	/man/2/sets
+path	/man/2/sexprs
+path	/man/2/sh
+path	/man/2/smtp
+path	/man/2/spki
+path	/man/2/spki-verifier
+path	/man/2/spree
+path	/man/2/spree-allow
+path	/man/2/spree-cardlib
+path	/man/2/spree-objstore
+path	/man/2/srv
+path	/man/2/string
+path	/man/2/stringinttab
+path	/man/2/styx
+path	/man/2/styxconv
+path	/man/2/styxflush
+path	/man/2/styxpersist
+path	/man/2/styxservers
+path	/man/2/styxservers-nametree
+path	/man/2/sys-0intro
+path	/man/2/sys-bind
+path	/man/2/sys-byte2char
+path	/man/2/sys-chdir
+path	/man/2/sys-dial
+path	/man/2/sys-dirread
+path	/man/2/sys-dup
+path	/man/2/sys-export
+path	/man/2/sys-fauth
+path	/man/2/sys-fd2path
+path	/man/2/sys-file2chan
+path	/man/2/sys-fversion
+path	/man/2/sys-iounit
+path	/man/2/sys-millisec
+path	/man/2/sys-open
+path	/man/2/sys-pctl
+path	/man/2/sys-pipe
+path	/man/2/sys-print
+path	/man/2/sys-read
+path	/man/2/sys-remove
+path	/man/2/sys-seek
+path	/man/2/sys-sleep
+path	/man/2/sys-stat
+path	/man/2/sys-tokenize
+path	/man/2/sys-utfbytes
+path	/man/2/sys-werrstr
+path	/man/2/tabs
+path	/man/2/tftp
+path	/man/2/timers
+path	/man/2/tk
+path	/man/2/tkclient
+path	/man/2/translate
+path	/man/2/ubfa
+path	/man/2/venti
+path	/man/2/virgil
+path	/man/2/volume
+path	/man/2/w3c-css
+path	/man/2/w3c-uris
+path	/man/2/w3c-xpointers
+path	/man/2/wait
+path	/man/2/wmclient
+path	/man/2/wmlib
+path	/man/2/wmsrv
+path	/man/2/workdir
+path	/man/2/xml
+path	/man/3/cap
+path	/man/3/cmd
+path	/man/3/prof
+path	/man/3/prog
+path	/man/3/srv9
+path	/man/4/archfs
+path	/man/4/grid-cpu
+path	/man/4/ramfile
+path	/man/5/0intro
+path	/man/5/stat
+path	/man/5/walk
+path	/man/6/namespace
+path	/man/6/proto
+path	/man/7/db
+path	/man/8/applylog
+path	/man/8/bootpd
+path	/man/8/collabsrv
+path	/man/8/create
+path	/man/8/mkfs
+path	/man/8/styxchat
+path	/man/9/bind
+path	/man/9/canvas
+path	/man/9/frame
+path	/man/9/menu
+path	/man/9/menubutton
+path	/man/9/options
+path	/man/9/text
+path	/man/9/types
+path1	/man/1/grid-ns
+path2...path	/man/1/grid-ns
+pathname	/man/1/alphabet-fs
+pathname	/man/1/bind
+pathname	/man/1/fs
+pathname	/man/1/mash-tk
+pathname	/man/1/passwd
+pathname	/man/1/sh
+pathname	/man/1/tiny
+pathname	/man/10/9load
+pathname	/man/10/newchan
+pathname	/man/2/debug
+pathname	/man/2/dial
+pathname	/man/2/names
+pathname	/man/2/sys-dial
+pathname	/man/9/bind
+pathname	/man/9/button
+pathname	/man/9/canvas
+pathname	/man/9/checkbutton
+pathname	/man/9/choicebutton
+pathname	/man/9/entry
+pathname	/man/9/frame
+pathname	/man/9/label
+pathname	/man/9/listbox
+pathname	/man/9/menu
+pathname	/man/9/menubutton
+pathname	/man/9/panel
+pathname	/man/9/radiobutton
+pathname	/man/9/scale
+pathname	/man/9/scrollbar
+pathname	/man/9/see
+pathname	/man/9/text
+pathnames	/man/1/0intro
+pathnames	/man/8/bootpd
+pathonly	/man/10/newchan
+pathprefix	/man/2/print
+paths	/man/1/alphabet-fs
+paths	/man/1/fs
+paths	/man/1/grid-ns
+paths	/man/1/vacget
+paths	/man/2/debug
+paths	/man/2/styx
+paths	/man/2/styxservers-nametree
+paths	/man/2/sys-0intro
+paths	/man/8/applylog
+pattern	/man/1/acme
+pattern	/man/1/alphabet-fs
+pattern	/man/1/filename
+pattern	/man/1/fs
+pattern	/man/1/grep
+pattern	/man/1/mash
+pattern	/man/1/mash-make
+pattern	/man/1/mash-tk
+pattern	/man/1/mk
+pattern	/man/1/sh
+pattern	/man/1/sh-regex
+pattern	/man/1/sh-std
+pattern	/man/10/mk
+pattern	/man/2/0intro
+pattern	/man/2/arg
+pattern	/man/2/filepat
+pattern	/man/2/security-0intro
+pattern	/man/2/spki
+pattern	/man/2/spree-allow
+pattern	/man/2/spree-cardlib
+pattern	/man/6/plumbing
+pattern	/man/9/bind
+pattern	/man/9/canvas
+pattern	/man/9/text
+pattern's	/man/2/spree-allow
+patterns	/man/1/filename
+patterns	/man/1/sh-regex
+patterns	/man/1/sh-std
+patterns	/man/2/math-fp
+patterns	/man/2/spree-allow
+patterns	/man/6/plumbing
+patterns	/man/9/bind
+paul	/man/2/palmfile
+paul	/man/6/attrdb
+pause	/man/1/sleep
+pause	/man/1/wm-misc
+pause	/man/3/mpeg
+pause.b	/man/1/sleep
+pawn	/man/6/keyboard
+pbit16	/man/10/styx
+pbit32	/man/10/styx
+pbit64	/man/10/styx
+pbit8	/man/10/styx
+pbs	/man/10/9load
+pbs	/man/8/prep
+pbslba	/man/10/9load
+pbslba	/man/8/prep
+pbss	/man/10/9load
+pbus	/man/3/pbus
+pbus	/man/3/plap
+pbusctl	/man/3/pbus
+pc	/man/1/stack
+pc	/man/10/2c
+pc	/man/10/9load
+pc	/man/10/a.out
+pc	/man/10/conf
+pc	/man/10/dmainit
+pc	/man/10/inb
+pc	/man/10/lock
+pc	/man/10/plan9.ini
+pc	/man/2/debug
+pc	/man/2/dis
+pc	/man/2/disks
+pc	/man/2/exception
+pc	/man/3/arch
+pc	/man/3/floppy
+pc	/man/3/i82365
+pc	/man/3/lpt
+pc	/man/3/mpeg
+pc	/man/3/pbus
+pc	/man/3/plap
+pc	/man/3/pnp
+pc	/man/3/prog
+pc	/man/3/rtc
+pc	/man/3/tv
+pc	/man/3/vga
+pc	/man/6/dis
+pc	/man/6/keyboard
+pc	/man/6/sbl
+pc	/man/8/prep
+pc's	/man/2/dis
+pc1	/man/2/dis
+pc1	/man/6/dis
+pc2	/man/2/dis
+pc2	/man/6/dis
+pccard0	/man/10/plan9.ini
+pcfile	/man/10/acid
+pci	/man/10/intrenable
+pci	/man/10/plan9.ini
+pci	/man/3/pnp
+pci	/man/3/sd
+pcimaxbno	/man/10/plan9.ini
+pcimaxdno	/man/10/plan9.ini
+pcl	/man/2/print
+pcline	/man/10/acid
+pcm	/man/1/auplay
+pcm	/man/3/audio
+pcm	/man/3/i82365
+pcm	/man/6/audio
+pcm0attr	/man/3/i82365
+pcm0ctl	/man/3/i82365
+pcm0mem	/man/3/i82365
+pcm100	/man/10/plan9.ini
+pcm16	/man/3/audio
+pcm1attr	/man/3/i82365
+pcm1ctl	/man/3/i82365
+pcm1mem	/man/3/i82365
+pcmcia	/man/10/plan9.ini
+pcmcia	/man/3/i82365
+pcmcia0	/man/10/plan9.ini
+pcmciax	/man/10/plan9.ini
+pcmpc100	/man/10/plan9.ini
+pcnet	/man/10/plan9.ini
+pcpart	/man/2/disks
+pcpart.active	/man/2/disks
+pcr	/man/3/lpt
+pcs	/man/10/plan9.ini
+pcs	/man/8/prep
+pcsz	/man/10/a.out
+pctl	/man/1/0intro
+pctl	/man/1/bind
+pctl	/man/1/cd
+pctl	/man/1/kill
+pctl	/man/1/sh-std
+pctl	/man/1/sh-tk
+pctl	/man/1/tiny
+pctl	/man/2/command
+pctl	/man/2/env
+pctl	/man/2/newns
+pctl	/man/2/security-auth
+pctl	/man/2/styxservers
+pctl	/man/2/styxservers-nametree
+pctl	/man/2/sys-0intro
+pctl	/man/2/sys-chdir
+pctl	/man/2/sys-export
+pctl	/man/2/sys-pctl
+pctl	/man/3/env
+pctl	/man/3/prof
+pctl	/man/3/prog
+pctl	/man/3/sign
+pctl	/man/6/namespace
+pctosrc	/man/2/debug
+pcwidth	/man/2/print
+pd	/man/6/man
+pda	/man/2/palmfile
+pda	/man/3/pointer
+pdb	/man/2/palmfile
+pdc.minitelfr.com	/man/1/miniterm
+pdriver	/man/2/print
+pdv	/man/2/asn1
+peculiar	/man/2/disks
+peculiar	/man/3/fs
+peculiar	/man/3/touch
+peculiar	/man/6/colour
+peculiar	/man/8/collabsrv
+ped	/man/2/sh
+pedantry	/man/1/limbo
+peer	/man/1/alphabet-grid
+peer	/man/3/plap
+peers	/man/2/security-auth
+pelm	/man/9/types
+pen	/man/1/keyboard
+pen.b	/man/1/keyboard
+pending	/man/10/qlock
+pending	/man/2/convcs
+pending	/man/3/srv
+pending	/man/3/tls
+pending	/man/5/flush
+pending	/man/9/update
+pentium	/man/10/2c
+penultimate	/man/1/webgrab
+peplink	/man/8/mangaload
+perceived	/man/1/tiny
+percent	/man/1/mk
+percent	/man/10/mk
+percent	/man/2/w3c-css
+percent	/man/2/w3c-uris
+percent	/man/9/bind
+percentage	/man/1/collab-clients
+percentage	/man/1/prof
+percentage	/man/10/kprof
+percentage	/man/10/plan9.ini
+percentage	/man/2/print
+percentage	/man/2/prof
+percentage	/man/2/w3c-css
+perfom	/man/3/prog
+perform	/man/1/0intro
+perform	/man/1/gzip
+perform	/man/1/mash-make
+perform	/man/1/sh
+perform	/man/1/spree-join
+perform	/man/10/9load
+perform	/man/10/error
+perform	/man/10/eve
+perform	/man/10/styxserver
+perform	/man/2/daytime
+perform	/man/2/keyring-0intro
+perform	/man/2/keyring-certtostr
+perform	/man/2/security-0intro
+perform	/man/2/spree-allow
+perform	/man/2/stringinttab
+perform	/man/3/ip
+perform	/man/3/pnp
+perform	/man/6/utf
+performance	/man/1/blur
+performed	/man/1/acme
+performed	/man/1/deb
+performed	/man/1/grid-query
+performed	/man/1/listen
+performed	/man/1/tiny
+performed	/man/1/wm-misc
+performed	/man/10/eve
+performed	/man/2/asn1
+performed	/man/2/math-0intro
+performed	/man/2/prof
+performed	/man/2/spree-allow
+performed	/man/2/styxservers
+performed	/man/2/sys-fversion
+performed	/man/3/draw
+performed	/man/3/vga
+performed	/man/4/factotum
+performed	/man/4/lockfs
+performed	/man/8/register
+performed	/man/9/canvas
+performed	/man/9/text
+performing	/man/1/tiny
+performs	/man/1/gzip
+performs	/man/1/sh-expr
+performs	/man/1/sh-std
+performs	/man/10/styxserver
+performs	/man/2/draw-0intro
+performs	/man/2/filter-deflate
+performs	/man/2/keyring-auth
+performs	/man/2/keyring-certtostr
+performs	/man/2/security-0intro
+performs	/man/2/sh
+performs	/man/2/stringinttab
+performs	/man/2/sys-byte2char
+performs	/man/2/venti
+performs	/man/3/0intro
+performs	/man/4/dossrv
+perimeter	/man/9/canvas
+periodic	/man/2/draw-image
+periodically	/man/10/kbdputc
+periodically	/man/2/dhcpclient
+periodically	/man/2/tftp
+periodically	/man/8/dhcp
+periodically	/man/8/rip
+periods	/man/2/spki
+periods	/man/3/pnp
+perle	/man/10/plan9.ini
+perm	/man/10/dev
+perm	/man/10/devattach
+perm	/man/10/newchan
+perm	/man/10/styx
+perm	/man/10/styxserver
+perm	/man/2/bufio
+perm	/man/2/dbm
+perm	/man/2/palmfile
+perm	/man/2/styx
+perm	/man/2/styxservers
+perm	/man/2/styxservers-nametree
+perm	/man/2/sys-open
+perm	/man/3/logfs
+perm	/man/4/9srvfs
+perm	/man/5/0intro
+perm	/man/5/open
+perm	/man/6/proto
+perm	/man/8/styxchat
+perm&dmdir	/man/10/styxserver
+permanent	/man/1/bind
+permanent	/man/2/spree
+permanent	/man/2/sys-stat
+permanent	/man/5/0intro
+permanent	/man/6/users
+permanently	/man/10/xalloc
+permanently	/man/2/dhcpclient
+permissible	/man/2/alphabet-intro
+permissible	/man/2/draw-image
+permissible	/man/2/spree
+permissible	/man/2/spree-objstore
+permissible	/man/2/sys-0intro
+permissible	/man/9/canvas
+permission	/man/1/bind
+permission	/man/1/chmod
+permission	/man/1/ls
+permission	/man/1/mkdir
+permission	/man/1/mv
+permission	/man/1/rm
+permission	/man/1/tiny
+permission	/man/1/uuencode
+permission	/man/10/ar
+permission	/man/10/devattach
+permission	/man/10/eve
+permission	/man/10/kstrip
+permission	/man/10/styxserver
+permission	/man/2/dbm
+permission	/man/2/factotum
+permission	/man/2/styxservers
+permission	/man/2/sys-0intro
+permission	/man/2/sys-open
+permission	/man/2/sys-remove
+permission	/man/2/sys-stat
+permission	/man/3/fs
+permission	/man/3/logfs
+permission	/man/4/kfs
+permission	/man/4/vacfs
+permission	/man/5/0intro
+permission	/man/5/open
+permission	/man/5/remove
+permission	/man/5/stat
+permission	/man/5/walk
+permission	/man/8/bootpd
+permission	/man/8/kfscmd
+permission	/man/9/1copyright
+permissions	/man/1/alphabet-fs
+permissions	/man/1/chmod
+permissions	/man/1/cp
+permissions	/man/1/fs
+permissions	/man/1/ftest
+permissions	/man/1/ls
+permissions	/man/1/mkdir
+permissions	/man/1/vacget
+permissions	/man/10/dev
+permissions	/man/10/devattach
+permissions	/man/10/newchan
+permissions	/man/10/styxserver
+permissions	/man/2/bufio
+permissions	/man/2/spki
+permissions	/man/2/styxservers
+permissions	/man/2/sys-open
+permissions	/man/2/sys-stat
+permissions	/man/3/fs
+permissions	/man/3/srv
+permissions	/man/3/srv9
+permissions	/man/4/9srvfs
+permissions	/man/4/import
+permissions	/man/4/kfs
+permissions	/man/4/memfs
+permissions	/man/5/0intro
+permissions	/man/5/open
+permissions	/man/5/remove
+permissions	/man/5/stat
+permissions	/man/6/proto
+permissions	/man/8/applylog
+permissions	/man/8/create
+permissions	/man/8/cs
+permissions	/man/8/mkfs
+permit	/man/1/bind
+permit	/man/1/chgrp
+permit	/man/2/security-0intro
+permit	/man/2/sys-open
+permit	/man/4/acme
+permit	/man/5/0intro
+permit	/man/9/grid
+permits	/man/1/chgrp
+permits	/man/10/2c
+permits	/man/2/sys-0intro
+permits	/man/9/grid
+permitted	/man/1/m4
+permitted	/man/1/wm-misc
+permitted	/man/8/mkfs
+permitted	/man/9/canvas
+permitted	/man/9/grid
+permitting	/man/1/emu
+permitting	/man/2/spree-allow
+perms	/man/1/math-misc
+perms.b	/man/1/math-misc
+permutation	/man/2/math-linalg
+permutations	/man/1/math-misc
+permutations	/man/2/spree-cardlib
+permuted	/man/1/0intro
+perpendicular	/man/2/draw-image
+perpendicularly	/man/2/draw-image
+persist	/man/2/registries
+persist	/man/3/eia
+persist	/man/4/registry
+persistent	/man/1/ebook
+persistent	/man/2/spree-objstore
+persistent	/man/2/styxpersist
+persistent	/man/4/registry
+persistently	/man/3/pipe
+persists	/man/3/pipe
+persists	/man/8/cs
+person	/man/4/import
+person	/man/8/create
+personal	/man/2/palmfile
+personal	/man/3/i82365
+personal	/man/6/attrdb
+pertain	/man/1/diff
+pexit	/man/10/error
+pexit	/man/10/kproc
+pf	/man/2/palmfile
+pfd	/man/2/print
+pfile	/man/2/palmfile
+pfile.open	/man/2/palmfile
+pg	/man/6/keyboard
+pgrp	/man/2/debug
+pgrp	/man/3/prog
+pgrps	/man/2/sys-0intro
+phase	/man/4/factotum
+phases	/man/8/register
+phi	/man/1/calc
+phi	/man/2/draw-image
+phi	/man/3/draw
+phil	/man/10/acid
+phone	/man/1/miniterm
+phone	/man/2/format
+phone	/man/2/security-0intro
+photographic	/man/6/colour
+phrase	/man/2/secstore
+phrase	/man/6/translate
+phrases	/man/6/translate
+physical	/man/1/dd
+physical	/man/10/9load
+physical	/man/10/kbdputc
+physical	/man/10/plan9.ini
+physical	/man/2/attrdb
+physical	/man/2/draw-0intro
+physical	/man/2/draw-display
+physical	/man/2/draw-image
+physical	/man/2/drawmux
+physical	/man/2/venti
+physical	/man/2/wmsrv
+physical	/man/3/draw
+physical	/man/3/ether
+physical	/man/3/flash
+physical	/man/3/ftl
+physical	/man/3/ip
+physical	/man/3/logfs
+physical	/man/3/rtc
+physical	/man/3/vga
+physical	/man/6/attrdb
+physical	/man/6/colour
+physical	/man/8/init
+physical	/man/8/prep
+physically	/man/8/prep
+physics	/man/6/colour
+pi	/man/1/calc
+pi	/man/1/fc
+pi	/man/1/math-misc
+pi	/man/1/units
+pi	/man/2/math-fp
+pi	/man/3/sd
+pi.b	/man/1/math-misc
+pic	/man/1/man
+piccenter	/man/2/draw-example
+piccenter.x	/man/2/draw-example
+piccenter.y	/man/2/draw-example
+picfile	/man/3/tv
+pick	/man/10/acid
+pick	/man/10/c2l
+pick	/man/2/alphabet-intro
+pick	/man/2/asn1
+pick	/man/2/dis
+pick	/man/2/filter
+pick	/man/2/hash
+pick	/man/2/json
+pick	/man/2/sexprs
+pick	/man/2/spki
+pick	/man/2/styx
+pick	/man/2/styxflush
+pick	/man/2/styxservers
+pick	/man/2/styxservers-nametree
+pick	/man/2/ubfa
+pick	/man/2/w3c-css
+pick	/man/2/w3c-xpointers
+pick	/man/2/xml
+pick	/man/6/colour
+pick	/man/6/sbl
+picked	/man/10/2l
+picking	/man/6/colour
+pickplace	/man/9/text
+picks	/man/10/plan9.ini
+picks	/man/9/canvas
+picks	/man/9/image
+picreader	/man/2/imagefile
+picture	/man/2/draw-0intro
+picture	/man/2/draw-display
+picture	/man/2/draw-example
+picture	/man/2/draw-image
+picture	/man/2/imagefile
+picture	/man/2/prefab-element
+picture	/man/6/colour
+pictures	/man/1/wm-misc
+pictures	/man/2/draw-image
+pictures	/man/2/imagefile
+pictures	/man/2/prefab-element
+pid	/man/1/deb
+pid	/man/1/grid-monitor
+pid	/man/1/kill
+pid	/man/1/mk
+pid	/man/1/ns
+pid	/man/1/sh-std
+pid	/man/1/stack
+pid	/man/10/acid
+pid	/man/10/mk
+pid	/man/2/command
+pid	/man/2/debug
+pid	/man/2/exception
+pid	/man/2/filter
+pid	/man/2/ir
+pid	/man/2/prof
+pid	/man/2/sh
+pid	/man/2/styxconv
+pid	/man/2/sys-export
+pid	/man/2/sys-pctl
+pid	/man/2/wait
+pid	/man/3/dbg
+pid	/man/3/prog
+pidc	/man/2/command
+pidc	/man/2/ir
+pidc	/man/2/styxconv
+pids	/man/1/deb
+pids	/man/1/grid-monitor
+pids	/man/3/prog
+piece	/man/6/keyboard
+pieces	/man/1/wm-misc
+pieces	/man/2/ida
+pieces	/man/2/prefab-element
+pieces	/man/2/sys-iounit
+pieces	/man/6/keyboard
+pieslice	/man/9/canvas
+pike	/man/1/acme
+pike	/man/1/yacc
+pike	/man/10/2a
+pike	/man/10/2c
+pike	/man/10/2l
+pile	/man/2/spree-cardlib
+piled	/man/2/spree-cardlib
+pin	/man/1/secstore
+pin	/man/10/kproc
+pin	/man/2/secstore
+pin	/man/3/cons
+pin	/man/3/gpio
+pinf	/man/2/math-fp
+ping	/man/8/ping
+ping.b	/man/8/ping
+pinot	/man/10/odbc
+pins	/man/3/fpga
+pins	/man/3/tinyfs
+pipe	/man/1/alphabet-fs
+pipe	/man/1/alphabet-main
+pipe	/man/1/fs
+pipe	/man/1/mash
+pipe	/man/1/ns
+pipe	/man/1/sh
+pipe	/man/1/sh-alphabet
+pipe	/man/1/sh-std
+pipe	/man/1/tee
+pipe	/man/1/tiny
+pipe	/man/10/odbc
+pipe	/man/2/styxconv
+pipe	/man/2/sys-bind
+pipe	/man/2/sys-pctl
+pipe	/man/2/sys-pipe
+pipe	/man/2/sys-seek
+pipe	/man/3/cmd
+pipe	/man/3/pipe
+pipe	/man/3/srv9
+pipe	/man/5/attach
+pipe	/man/8/styxchat
+piped	/man/1/alphabet-abc
+piped	/man/1/alphabet-fs
+piped	/man/1/alphabet-grid
+piped	/man/1/fs
+pipeline	/man/1/alphabet-abc
+pipeline	/man/1/alphabet-grid
+pipeline	/man/1/du
+pipeline	/man/1/sh
+pipeline	/man/1/stack
+pipeline	/man/1/time
+pipeline	/man/1/tiny
+pipelines	/man/1/mash
+pipelines	/man/1/sh
+pipelines	/man/1/tiny
+pipes	/man/1/alphabet-main
+pipes	/man/1/ls
+pipes	/man/1/mk
+pipes	/man/1/sh
+pipes	/man/1/sh-std
+pipes	/man/1/stack
+pipes	/man/1/tiny
+pipes	/man/10/mk
+pipes	/man/2/sys-pipe
+pipes	/man/2/sys-stat
+pitch	/man/1/acme
+pitfalls	/man/1/sh-std
+pittsburg	/man/2/ubfa
+pittsburg	/man/6/ubfa
+pixel	/man/1/ebook
+pixel	/man/2/draw-0intro
+pixel	/man/2/draw-display
+pixel	/man/2/draw-image
+pixel	/man/2/draw-rect
+pixel	/man/2/drawmux
+pixel	/man/2/imagefile
+pixel	/man/2/mpeg
+pixel	/man/2/prefab-element
+pixel	/man/2/prefab-style
+pixel	/man/2/pslib
+pixel	/man/3/draw
+pixel	/man/3/vga
+pixel	/man/3/vid
+pixel	/man/6/colour
+pixel	/man/6/image
+pixel	/man/9/grid
+pixel	/man/9/listbox
+pixel	/man/9/menu
+pixel	/man/9/scale
+pixel	/man/9/scrollbar
+pixel	/man/9/text
+pixel's	/man/2/draw-image
+pixel's	/man/6/colour
+pixel's	/man/6/image
+pixels	/man/1/emu
+pixels	/man/1/filename
+pixels	/man/1/wm-sh
+pixels	/man/2/draw-0intro
+pixels	/man/2/draw-display
+pixels	/man/2/draw-example
+pixels	/man/2/draw-font
+pixels	/man/2/draw-image
+pixels	/man/2/draw-rect
+pixels	/man/2/print
+pixels	/man/2/pslib
+pixels	/man/3/draw
+pixels	/man/3/vga
+pixels	/man/4/acme
+pixels	/man/6/colour
+pixels	/man/6/font
+pixels	/man/6/image
+pixels	/man/8/collabsrv
+pixels	/man/9/canvas
+pixels	/man/9/entry
+pixels	/man/9/grid
+pixels	/man/9/image
+pixels	/man/9/scrollbar
+pixels	/man/9/text
+pixels	/man/9/types
+pizza	/man/1/mux
+pk	/man/2/keyring-0intro
+pk	/man/2/keyring-certtostr
+pk	/man/2/keyring-gensk
+pk	/man/2/keyring-sha1
+pk	/man/2/keyset
+pk	/man/2/spki
+pk	/man/6/auth
+pk	/man/8/ai2key
+pkalg	/man/8/ai2key
+pkca	/man/2/keyring-auth
+pkcs1	/man/2/spki
+pkhash	/man/2/keyset
+pkt	/man/3/ip
+pktin	/man/2/ip
+pktostr	/man/2/keyring-certtostr
+pktostr	/man/2/keyset
+pktostr	/man/3/sign
+pktout	/man/2/ip
+pku	/man/2/keyring-auth
+pku0	/man/2/keyring-auth
+pku0	/man/6/auth
+pku1	/man/2/keyring-auth
+pku1	/man/6/auth
+placeholder	/man/1/sh-std
+placeholder	/man/3/ip
+placeholder	/man/7/db
+placeholders	/man/4/namespace
+placeholders	/man/7/db
+placement	/man/1/acme
+placement	/man/2/prefab-compound
+placement	/man/2/tkclient
+placement	/man/2/wmclient
+placement	/man/9/grid
+places	/man/1/charon
+places	/man/1/math-misc
+places	/man/1/mk
+places	/man/10/getfields
+places	/man/10/iar
+places	/man/10/mk
+places	/man/10/plan9.ini
+places	/man/10/print
+places	/man/10/qio
+places	/man/10/readnum
+places	/man/2/draw-screen
+places	/man/2/env
+places	/man/2/geodesy
+places	/man/2/sys-print
+places	/man/4/namespace
+places	/man/6/sexprs
+places	/man/8/prep
+places	/man/9/text
+placing	/man/1/filename
+placing	/man/1/tktester
+placing	/man/2/format
+placing	/man/2/spree-cardlib
+placing	/man/9/canvas
+plain	/man/1/acme
+plain	/man/1/charon
+plain	/man/1/collab-clients
+plain	/man/1/cp
+plain	/man/1/man
+plain	/man/1/wish
+plain	/man/10/print
+plain	/man/2/complete
+plain	/man/2/disks
+plain	/man/2/keyring-ipint
+plain	/man/2/rfc822
+plain	/man/2/sys-print
+plain	/man/2/tkclient
+plain	/man/2/w3c-css
+plain	/man/2/wmclient
+plain	/man/3/fs
+plain	/man/4/acme
+plain	/man/5/0intro
+plain	/man/5/stat
+plain	/man/6/font
+plain	/man/6/man
+plain	/man/8/dhcp
+plaintext	/man/2/security-0intro
+plan	/man/1/0intro
+plan	/man/1/9win
+plan	/man/1/ar
+plan	/man/1/bind
+plan	/man/1/crypt
+plan	/man/1/diff
+plan	/man/1/mash
+plan	/man/1/mk
+plan	/man/1/secstore
+plan	/man/10/0intro
+plan	/man/10/2a
+plan	/man/10/2c
+plan	/man/10/2l
+plan	/man/10/5cv
+plan	/man/10/9load
+plan	/man/10/a.out
+plan	/man/10/acid
+plan	/man/10/conf
+plan	/man/10/kproc
+plan	/man/10/mk
+plan	/man/10/plan9.ini
+plan	/man/10/sleep
+plan	/man/10/styx
+plan	/man/2/dbm
+plan	/man/2/dhcpclient
+plan	/man/2/disks
+plan	/man/2/factotum
+plan	/man/2/secstore
+plan	/man/2/styxpersist
+plan	/man/3/cmd
+plan	/man/3/ds
+plan	/man/3/fs
+plan	/man/3/ip
+plan	/man/3/srv9
+plan	/man/3/tv
+plan	/man/4/9srvfs
+plan	/man/4/dossrv
+plan	/man/4/factotum
+plan	/man/4/import
+plan	/man/4/kfs
+plan	/man/4/namespace
+plan	/man/5/0intro
+plan	/man/6/man
+plan	/man/6/namespace
+plan	/man/8/cs
+plan	/man/8/ping
+plan	/man/8/prep
+plan9	/man/1/man
+plan9	/man/10/9load
+plan9	/man/10/conf
+plan9	/man/2/dhcpclient
+plan9	/man/3/srv9
+plan9	/man/4/namespace
+plan9	/man/8/prep
+plan9's	/man/1/acme
+plan9.ini	/man/10/9load
+plan9.ini	/man/10/plan9.ini
+plan9.ini	/man/3/ds
+plan9.ini	/man/3/i82365
+plan9.ini	/man/3/pnp
+plan9.ini	/man/8/prep
+plan9.nvr	/man/10/plan9.ini
+plan9.nvr	/man/8/prep
+plan9partition	/man/8/prep
+plane	/man/2/draw-0intro
+plane	/man/2/draw-image
+plane	/man/2/draw-rect
+plane	/man/2/prefab-element
+plane's	/man/2/draw-0intro
+plap	/man/3/pbus
+plap	/man/3/plap
+platform	/man/1/0intro
+platform	/man/1/mk
+platform	/man/10/0intro
+platform	/man/10/conf
+platform	/man/10/dev
+platform	/man/10/dmainit
+platform	/man/10/inb
+platform	/man/10/intrenable
+platform	/man/10/mk
+platform	/man/10/panic
+platform	/man/10/seconds
+platform	/man/3/audio
+platform	/man/3/ether
+platform	/man/3/flash
+platform	/man/3/rtc
+platform	/man/3/vga
+platform	/man/4/namespace
+platform	/man/8/init
+platform's	/man/10/conf
+platform's	/man/10/intrenable
+platforms	/man/1/0intro
+platforms	/man/10/0intro
+platforms	/man/10/2c
+platforms	/man/10/conf
+platforms	/man/10/dev
+platforms	/man/10/dmainit
+platforms	/man/10/intrenable
+platforms	/man/10/master
+platforms	/man/2/srv
+platforms	/man/3/ip
+platforms	/man/3/pnp
+platforms	/man/3/pointer
+platforms	/man/3/rtc
+platforms	/man/4/namespace
+plausible	/man/8/sntp
+play	/man/2/mpeg
+play	/man/2/security-auth
+play	/man/3/pnp
+played	/man/1/auplay
+player	/man/1/wm-misc
+player	/man/2/spree-allow
+player	/man/2/spree-cardlib
+players	/man/2/spree
+players	/man/2/spree-cardlib
+players	/man/2/spree-gather
+playing	/man/2/mpeg
+playing	/man/2/spree-cardlib
+plays	/man/1/auplay
+plays	/man/2/mpeg
+plaything	/man/1/wm-misc
+please	/man/1/collab-clients
+plethora	/man/6/keyboard
+plot	/man/1/wm-misc
+plotted	/man/1/wm-misc
+plug	/man/1/miniterm
+plug	/man/3/pnp
+plumb	/man/1/man
+plumb	/man/1/plumb
+plumb	/man/1/wm-sh
+plumb	/man/2/plumbmsg
+plumb	/man/3/srv
+plumb	/man/6/plumbing
+plumb	/man/8/plumber
+plumb.b	/man/1/plumb
+plumb.input	/man/1/plumb
+plumb.input	/man/2/plumbmsg
+plumb.rcvport	/man/2/plumbmsg
+plumbed	/man/2/plumbmsg
+plumbed	/man/8/plumber
+plumber	/man/1/brutus
+plumber	/man/1/ftree
+plumber	/man/1/man
+plumber	/man/1/plumb
+plumber	/man/1/wm-misc
+plumber	/man/1/wm-sh
+plumber	/man/2/plumbmsg
+plumber	/man/6/plumbing
+plumber	/man/8/plumber
+plumber.b	/man/8/plumber
+plumbing	/man/1/brutus
+plumbing	/man/1/deb
+plumbing	/man/1/ftree
+plumbing	/man/1/plumb
+plumbing	/man/1/wm-misc
+plumbing	/man/1/wm-sh
+plumbing	/man/2/plumbmsg
+plumbing	/man/6/plumbing
+plumbing	/man/8/plumber
+plumbing.b	/man/8/plumber
+plumbmsg	/man/1/plumb
+plumbmsg	/man/2/plumbmsg
+plumbmsg	/man/6/plumbing
+plumbmsg	/man/8/plumber
+plumbmsg.b	/man/2/plumbmsg
+plumbmsg.m	/man/2/plumbmsg
+plumbs	/man/1/brutus
+plumbs	/man/1/deb
+plumbs	/man/1/ftree
+plumbs	/man/1/wm-sh
+plus	/man/1/cprof
+plus	/man/1/sh-arg
+plus	/man/1/sort
+plus	/man/10/c2l
+plus	/man/10/print
+plus	/man/10/styx
+plus	/man/2/sys-seek
+plus	/man/3/prog
+plus	/man/4/acme
+plus	/man/5/read
+plus	/man/6/colour
+plus	/man/8/prep
+plus	/man/9/canvas
+plus	/man/9/destroy
+plus	/man/9/grid
+plus	/man/9/pack
+plus	/man/9/radiobutton
+pmode	/man/2/print
+pmode.cfg	/man/2/print
+pn	/man/10/plan9.ini
+png	/man/1/wm-misc
+pngreader	/man/2/imagefile
+pnic	/man/10/plan9.ini
+pnic2	/man/10/plan9.ini
+pnp	/man/3/pnp
+pnp0	/man/3/pnp
+po	/man/10/plan9.ini
+pocket	/man/1/mux
+point's	/man/4/archfs
+pointed	/man/1/acme
+pointed	/man/10/atoi
+pointed	/man/10/malloc
+pointed	/man/10/strcat
+pointed	/man/10/styxserver
+pointed	/man/2/sys-dirread
+pointer	/man/1/emu
+pointer	/man/1/wish
+pointer	/man/1/wm
+pointer	/man/10/2c
+pointer	/man/10/a.out
+pointer	/man/10/acid
+pointer	/man/10/allocb
+pointer	/man/10/devattach
+pointer	/man/10/dynld
+pointer	/man/10/intrenable
+pointer	/man/10/malloc
+pointer	/man/10/memory
+pointer	/man/10/newchan
+pointer	/man/10/parsecmd
+pointer	/man/10/print
+pointer	/man/10/qio
+pointer	/man/10/rune
+pointer	/man/10/strcat
+pointer	/man/10/styx
+pointer	/man/10/styxserver
+pointer	/man/10/xalloc
+pointer	/man/2/0intro
+pointer	/man/2/attrdb
+pointer	/man/2/crc
+pointer	/man/2/devpointer
+pointer	/man/2/draw-0intro
+pointer	/man/2/draw-context
+pointer	/man/2/draw-display
+pointer	/man/2/draw-font
+pointer	/man/2/draw-pointer
+pointer	/man/2/sys-seek
+pointer	/man/2/tk
+pointer	/man/2/tkclient
+pointer	/man/2/w3c-xpointers
+pointer	/man/2/wmclient
+pointer	/man/2/wmsrv
+pointer	/man/3/cons
+pointer	/man/3/pointer
+pointer	/man/3/prog
+pointer	/man/6/dis
+pointer	/man/6/sbl
+pointer	/man/9/button
+pointer	/man/9/checkbutton
+pointer	/man/9/grab
+pointer	/man/9/menu
+pointer	/man/9/menubutton
+pointer	/man/9/radiobutton
+pointer's	/man/2/draw-pointer
+pointer's	/man/3/cons
+pointers	/man/10/2c
+pointers	/man/10/allocb
+pointers	/man/10/dynld
+pointers	/man/10/getfields
+pointers	/man/10/parsecmd
+pointers	/man/10/print
+pointers	/man/10/qio
+pointers	/man/10/styx
+pointers	/man/10/styxserver
+pointers	/man/2/dbm
+pointers	/man/2/dis
+pointers	/man/2/w3c-xpointers
+pointers	/man/3/prog
+pointers	/man/6/dis
+pointertype	/man/2/venti
+pointing	/man/1/acme
+pointing	/man/10/allocb
+pointing	/man/2/draw-0intro
+pointing	/man/2/draw-context
+pointing	/man/3/cons
+pointing	/man/3/pointer
+pointing	/man/5/0intro
+points	/man/1/0intro
+points	/man/1/math-misc
+points	/man/1/wm-misc
+points	/man/10/dev
+points	/man/10/devattach
+points	/man/10/dynld
+points	/man/10/intrenable
+points	/man/10/print
+points	/man/2/asn1
+points	/man/2/draw-0intro
+points	/man/2/draw-image
+points	/man/2/draw-point
+points	/man/2/draw-rect
+points	/man/2/math-linalg
+points	/man/2/regex
+points	/man/3/draw
+points	/man/3/dup
+points	/man/3/ip
+points	/man/3/root
+points	/man/3/touch
+points	/man/4/mntgen
+points	/man/4/namespace
+points	/man/5/attach
+points	/man/5/clunk
+points	/man/5/open
+points	/man/5/read
+points	/man/5/remove
+points	/man/5/stat
+points	/man/5/version
+points	/man/5/walk
+points	/man/9/canvas
+points	/man/9/types
+policy	/man/2/keyring-crypt
+polish	/man/1/fc
+polish	/man/1/sh-expr
+polish	/man/3/dbg
+poll	/man/1/collab-clients
+poll	/man/8/collabsrv
+poll.b	/man/1/collab-clients
+polled	/man/1/collab-clients
+poller	/man/1/collab-clients
+poller	/man/8/collabsrv
+poller.b	/man/1/collab-clients
+polling	/man/1/collab-clients
+polls	/man/1/collab-clients
+poly	/man/2/crc
+poly	/man/2/draw-image
+polygon	/man/2/draw-image
+polygon	/man/3/draw
+polygon	/man/9/canvas
+polygon's	/man/2/draw-image
+polygon's	/man/3/draw
+polygon's	/man/9/canvas
+polygonal	/man/9/canvas
+polygons	/man/9/canvas
+polyhedra	/man/1/mprof
+polyhedra	/man/1/sh-alphabet
+polyhedra	/man/1/wm-misc
+polyhedra.b	/man/1/wm-misc
+polymorphism	/man/2/lists
+polynomial	/man/1/math-misc
+polynomial	/man/2/crc
+polyop	/man/2/draw-image
+pool	/man/1/emu
+pool	/man/1/mprof
+pool	/man/1/wm-misc
+pool	/man/10/malloc
+pool	/man/10/plan9.ini
+pool	/man/2/0intro
+pool	/man/3/cons
+pool	/man/8/ftl
+pools	/man/1/emu
+pools	/man/1/wm-misc
+pools	/man/3/cons
+poor	/man/4/iostats
+poorly	/man/10/ms2
+pop	/man/1/collab-clients
+pop	/man/1/fc
+pop	/man/1/wm-misc
+pop	/man/1/wm-sh
+pop	/man/10/a.out
+pop	/man/2/dialog
+pop	/man/2/dis
+pop	/man/2/sh
+pop	/man/2/wmsrv
+pop	/man/6/ubfa
+pop3	/man/1/acme
+pop3	/man/2/dhcpclient
+pop3	/man/2/factotum
+pop3	/man/2/pop3
+pop3	/man/6/ndb
+pop3.b	/man/2/pop3
+pop3.m	/man/2/pop3
+poperror	/man/10/error
+popped	/man/1/ebook
+popped	/man/1/mash-tk
+popped	/man/1/sh-expr
+popped	/man/2/sh
+popped	/man/6/ubfa
+pops	/man/1/collab-clients
+pops	/man/1/cprof
+pops	/man/1/filename
+pops	/man/1/ftree
+pops	/man/1/logwindow
+pops	/man/1/mash-tk
+pops	/man/10/error
+pops	/man/2/dialog
+pops	/man/2/popup
+pops	/man/4/factotum
+pops	/man/6/dis
+popt	/man/2/print
+popts.cfg	/man/2/print
+popular	/man/1/wm-misc
+populate	/man/2/tkclient
+populate	/man/3/tls
+populated	/man/1/netstat
+populated	/man/4/dbfs
+populating	/man/1/grid-ns
+popup	/man/1/charon
+popup	/man/1/mash-tk
+popup	/man/1/tktester
+popup	/man/2/popup
+popup.b	/man/2/popup
+popup.m	/man/2/popup
+port	/man/1/avr
+port	/man/1/charon
+port	/man/1/dmview
+port	/man/1/grid-register
+port	/man/1/listen
+port	/man/1/netstat
+port	/man/1/telnet
+port	/man/10/0intro
+port	/man/10/acid
+port	/man/10/allocb
+port	/man/10/conf
+port	/man/10/devattach
+port	/man/10/error
+port	/man/10/inb
+port	/man/10/lock
+port	/man/10/master
+port	/man/10/newchan
+port	/man/10/odbc
+port	/man/10/parsecmd
+port	/man/10/plan9.ini
+port	/man/10/qio
+port	/man/10/qlock
+port	/man/10/readnum
+port	/man/10/ref
+port	/man/10/sleep
+port	/man/10/styxserver
+port	/man/2/dhcpclient
+port	/man/2/dial
+port	/man/2/plumbmsg
+port	/man/2/srv
+port	/man/2/sys-0intro
+port	/man/2/sys-dial
+port	/man/2/sys-export
+port	/man/2/sys-file2chan
+port	/man/2/sys-iounit
+port	/man/2/sys-print
+port	/man/2/virgil
+port	/man/2/w3c-uris
+port	/man/3/arch
+port	/man/3/audio
+port	/man/3/boot
+port	/man/3/cap
+port	/man/3/cmd
+port	/man/3/cons
+port	/man/3/dbg
+port	/man/3/draw
+port	/man/3/ds
+port	/man/3/dup
+port	/man/3/dynld
+port	/man/3/eia
+port	/man/3/env
+port	/man/3/ether
+port	/man/3/fs
+port	/man/3/i2c
+port	/man/3/indir
+port	/man/3/ip
+port	/man/3/kprof
+port	/man/3/logfs
+port	/man/3/lpt
+port	/man/3/mnt
+port	/man/3/mpeg
+port	/man/3/pipe
+port	/man/3/pnp
+port	/man/3/pointer
+port	/man/3/prof
+port	/man/3/prog
+port	/man/3/root
+port	/man/3/sd
+port	/man/3/sign
+port	/man/3/srv
+port	/man/3/tinyfs
+port	/man/3/tls
+port	/man/3/tv
+port	/man/3/vga
+port	/man/4/export
+port	/man/4/ftpfs
+port	/man/4/namespace
+port	/man/6/ndb
+port	/man/6/plumbing
+port	/man/7/db
+port	/man/8/bootpd
+port	/man/8/cs
+port	/man/8/logind
+port	/man/8/mangaload
+port	/man/8/plumber
+port	/man/8/rdbgsrv
+port	/man/8/svc
+port	/man/8/virgild
+portability	/man/10/inb
+portability	/man/6/colour
+portable	/man/1/0intro
+portable	/man/1/mux
+portable	/man/10/0intro
+portable	/man/10/conf
+portable	/man/2/keyring-certtostr
+portable	/man/3/ip
+portable	/man/4/namespace
+portdat.h	/man/10/devattach
+portdir	/man/10/conf
+porter	/man/1/alphabet-fs
+porter	/man/1/fs
+porter	/man/2/draw-image
+porter	/man/6/colour
+portion	/man/1/0intro
+portion	/man/1/sh-regex
+portion	/man/10/styx
+portion	/man/2/draw-font
+portion	/man/2/draw-image
+portion	/man/2/draw-rect
+portion	/man/2/prefab-element
+portion	/man/2/sexprs
+portion	/man/3/mnt
+portion	/man/3/vga
+portion	/man/4/palmsrv
+portion	/man/7/db
+portion	/man/9/entry
+portion	/man/9/options
+portion	/man/9/scale
+portion	/man/9/scrollbar
+portion	/man/9/see
+portion	/man/9/text
+portions	/man/1/sh-regex
+portions	/man/2/draw-font
+portions	/man/3/draw
+portions	/man/3/pipe
+portions	/man/9/text
+portmkfile	/man/10/conf
+portproto	/man/6/proto
+portproto	/man/8/mkfs
+portrait	/man/2/print
+ports	/man/10/acid
+ports	/man/10/plan9.ini
+ports	/man/3/arch
+ports	/man/3/audio
+ports	/man/3/eia
+ports	/man/3/ip
+ports	/man/3/lpt
+ports	/man/4/ftpfs
+ports	/man/8/signer
+pos	/man/2/debug
+pos	/man/6/sbl
+pose	/man/1/collab-clients
+pose	/man/2/virgil
+posible	/man/9/image
+positioned	/man/2/draw-image
+positioned	/man/2/prefab-compound
+positioned	/man/2/sexprs
+positioned	/man/9/grid
+positioned	/man/9/options
+positioned	/man/9/panel
+positioned	/man/9/text
+positioning	/man/9/canvas
+positions	/man/1/deb
+positions	/man/1/tktester
+positions	/man/2/draw-context
+positions	/man/2/draw-pointer
+positions	/man/2/math-fp
+positions	/man/2/prof
+positions	/man/2/regex
+positions	/man/2/rfc822
+positions	/man/2/sexprs
+positions	/man/3/pointer
+positions	/man/6/sbl
+positions	/man/9/canvas
+positions	/man/9/entry
+positions	/man/9/listbox
+positions	/man/9/pack
+positions	/man/9/text
+positive	/man/10/print
+positive	/man/2/bloomfilter
+positive	/man/2/draw-image
+positive	/man/2/keyring-ipint
+positive	/man/2/math-fp
+positive	/man/2/sys-bind
+positive	/man/2/sys-read
+positive	/man/9/canvas
+positive	/man/9/entry
+positive	/man/9/listbox
+positive	/man/9/text
+positve	/man/2/bloomfilter
+posix	/man/1/m4
+posix	/man/1/tail
+posix	/man/4/tarfs
+posix.1	/man/1/gettar
+posix.c	/man/3/fs
+posn	/man/2/devpointer
+posname	/man/1/ar
+posname	/man/10/iar
+possess	/man/2/keyring-0intro
+possess	/man/4/registry
+possesses	/man/4/keysrv
+possessing	/man/4/factotum
+possessing	/man/6/auth
+possibility	/man/10/lock
+possibility	/man/2/0intro
+possibility	/man/2/sys-0intro
+possibility	/man/5/0intro
+possibility	/man/9/1copyright
+possibly	/man/1/acme
+possibly	/man/1/mash
+possibly	/man/1/mk
+possibly	/man/1/sh
+possibly	/man/1/tr
+possibly	/man/10/a.out
+possibly	/man/10/mk
+possibly	/man/2/format
+possibly	/man/2/palmfile
+possibly	/man/2/prefab-compound
+possibly	/man/2/security-0intro
+possibly	/man/2/styxservers
+possibly	/man/2/sys-bind
+possibly	/man/2/sys-fversion
+possibly	/man/2/tkclient
+possibly	/man/2/wmclient
+possibly	/man/2/wmsrv
+possibly	/man/3/dbg
+possibly	/man/3/prog
+possibly	/man/9/bind
+possibly	/man/9/types
+post	/man/1/diff
+post	/man/1/webgrab
+post	/man/2/pop3
+post	/man/3/srv9
+post	/man/4/acme
+post	/man/9/menu
+post	/man/9/menubutton
+postcascade	/man/9/menu
+postcommand	/man/9/menu
+posted	/man/2/spree
+posted	/man/3/srv9
+posted	/man/4/9srvfs
+posted	/man/9/menu
+posted	/man/9/menubutton
+postfix	/man/1/calc
+postfix	/man/1/sh-expr
+posting	/man/1/webgrab
+posting	/man/2/spree
+posting	/man/9/menu
+posts	/man/9/menu
+posts	/man/9/menubutton
+postscript	/man/2/pslib
+pos•	/man/6/sbl
+potent	/man/1/0intro
+potential	/man/1/m4
+potential	/man/1/mk
+potential	/man/10/mk
+potentially	/man/1/0intro
+potentially	/man/1/acme
+potentially	/man/1/sh-mload
+potentially	/man/10/allocb
+potentially	/man/2/asn1
+potentially	/man/2/spree
+potentially	/man/2/styxservers-nametree
+potentially	/man/3/tls
+pound	/man/1/units
+pounds	/man/1/units
+pow	/man/1/calc
+pow	/man/1/fc
+pow	/man/2/math-elem
+pow10	/man/2/math-elem
+powered	/man/10/dev
+powered	/man/3/fpga
+powerful	/man/1/0intro
+powerpc	/man/10/5cv
+powerpc	/man/10/a.out
+powerpc	/man/10/inb
+powers	/man/1/math-misc
+powers.b	/man/1/math-misc
+pp	/man/1/alphabet-fs
+pp	/man/1/fs
+pp	/man/2/draw-image
+pp	/man/2/ida
+pp	/man/6/image
+pp	/man/6/man
+ppath	/man/10/styxserver
+ppcboot	/man/10/5cv
+ppp	/man/3/ip
+pqid	/man/10/styxserver
+pr	/man/2/print
+practical	/man/2/sys-self
+practice	/man/1/ns
+practice	/man/10/2l
+practice	/man/10/sleep
+practice	/man/2/0intro
+practice	/man/2/command
+practice	/man/2/draw-0intro
+practice	/man/2/plumbmsg
+practice	/man/2/prefab-style
+pragma	/man/10/2c
+pragmas	/man/10/2c
+pragmatic	/man/2/sh
+prc	/man/2/palmfile
+pre	/man/1/acme
+pre	/man/1/sh-string
+pre	/man/10/a.out
+pre	/man/2/spki
+pre	/man/2/spree-gather
+pre	/man/2/string
+pre	/man/9/types
+pread	/man/1/cp
+pread	/man/2/sys-0intro
+pread	/man/2/sys-read
+precede	/man/1/ar
+precede	/man/1/deb
+precede	/man/1/grep
+precede	/man/10/error
+precede	/man/10/iar
+precede	/man/3/mnt
+precede	/man/3/tls
+precede	/man/4/factotum
+precedence	/man/1/acme
+precedence	/man/1/calc
+precedence	/man/1/deb
+precedence	/man/1/m4
+precedence	/man/1/mash
+precedence	/man/1/mk
+precedence	/man/1/stack
+precedence	/man/1/tiny
+precedence	/man/1/units
+precedence	/man/1/yacc
+precedence	/man/10/mk
+precedence	/man/2/spree-cardlib
+precedence	/man/8/cs
+precedes	/man/10/styx
+precedes	/man/3/cmd
+precedes	/man/9/grid
+preceding	/man/1/acme
+preceding	/man/1/mash
+preceding	/man/1/mk
+preceding	/man/10/2c
+preceding	/man/10/a.out
+preceding	/man/10/mk
+preceding	/man/2/alphabet-intro
+preceding	/man/2/w3c-css
+preceding	/man/2/w3c-xpointers
+preceding	/man/5/walk
+preceding	/man/6/regexp
+preceding	/man/6/sexprs
+preceeding	/man/2/cfg
+precise	/man/2/math-fp
+precise	/man/2/sexprs
+precise	/man/3/mpeg
+precisely	/man/1/sh
+precisely	/man/3/sign
+precision	/man/1/fc
+precision	/man/1/sh-expr
+precision	/man/10/print
+precision	/man/2/keyring-0intro
+precision	/man/2/keyring-ipint
+precision	/man/2/math-export
+precision	/man/2/math-fp
+precision	/man/3/vga
+precision	/man/6/keytext
+precludes	/man/10/print
+preconditioning	/man/10/2c
+pred	/man/2/w3c-xpointers
+predecessors	/man/1/tsort
+predefined	/man/1/brutus
+predefined	/man/1/calc
+predefined	/man/1/m4
+predefined	/man/1/tkcmd
+predefined	/man/10/error
+predefined	/man/2/ip
+predefined	/man/2/w3c-xpointers
+predefined	/man/6/man
+predicate	/man/10/sleep
+predicate	/man/2/lists
+predicate	/man/2/styx
+predicate	/man/2/w3c-xpointers
+predictable	/man/3/cons
+preds	/man/2/w3c-xpointers
+preelem	/man/2/xml
+prefab	/man/1/0intro
+prefab	/man/1/mux
+prefab	/man/2/0intro
+prefab	/man/2/draw-0intro
+prefab	/man/2/draw-context
+prefab	/man/2/ir
+prefab	/man/2/prefab-0intro
+prefab	/man/2/prefab-compound
+prefab	/man/2/prefab-element
+prefab	/man/2/prefab-environ
+prefab	/man/2/prefab-style
+prefab	/man/2/volume
+prefab.c	/man/2/prefab-0intro
+prefab.c	/man/2/prefab-compound
+prefab.c	/man/2/prefab-element
+prefab.c	/man/2/prefab-environ
+prefab.c	/man/2/prefab-style
+prefab.m	/man/2/prefab-0intro
+prefab.m	/man/2/prefab-compound
+prefab.m	/man/2/prefab-element
+prefab.m	/man/2/prefab-environ
+prefab.m	/man/2/prefab-style
+preferable	/man/10/qio
+preferable	/man/8/rip
+preference	/man/9/see
+preferred	/man/1/deb
+preferred	/man/1/tcs
+preferred	/man/2/dividers
+preferred	/man/2/ip
+preferred	/man/6/man
+prefix	/man/1/basename
+prefix	/man/1/cal
+prefix	/man/1/mdb
+prefix	/man/1/sh-alphabet
+prefix	/man/1/sh-string
+prefix	/man/1/stack
+prefix	/man/1/webgrab
+prefix	/man/1/yacc
+prefix	/man/10/2c
+prefix	/man/10/panic
+prefix	/man/2/0intro
+prefix	/man/2/alphabet-intro
+prefix	/man/2/asn1
+prefix	/man/2/complete
+prefix	/man/2/disks
+prefix	/man/2/ip
+prefix	/man/2/names
+prefix	/man/2/prof
+prefix	/man/2/sh
+prefix	/man/2/spki
+prefix	/man/2/string
+prefix	/man/2/styx
+prefix	/man/2/sys-print
+prefix	/man/2/volume
+prefix	/man/4/archfs
+prefix	/man/6/keytext
+prefix	/man/8/collabsrv
+prefix	/man/8/create
+prefix	/man/9/options
+prefix	/man/9/scale
+prefix	/man/9/scrollbar
+prefixed	/man/1/acme
+prefixed	/man/1/cleanname
+prefixed	/man/1/fc
+prefixed	/man/1/miniterm
+prefixed	/man/1/sh-alphabet
+prefixed	/man/1/units
+prefixed	/man/1/xd
+prefixed	/man/10/acid
+prefixed	/man/10/plan9.ini
+prefixed	/man/2/sh
+prefixed	/man/3/logfs
+prefixed	/man/3/sd
+prefixed	/man/6/keytext
+prefixed	/man/8/ai2key
+prefixed	/man/8/collabsrv
+prefixed	/man/8/prep
+prefixed	/man/9/bind
+prefixes	/man/1/units
+prefixes	/man/4/archfs
+prefixes	/man/8/create
+prefixing	/man/1/mash
+prefixing	/man/1/sh
+prefixing	/man/1/timestamp
+prefixing	/man/2/sys-0intro
+preflt	/man/2/ip
+premultiplied	/man/2/draw-0intro
+premultiplied	/man/6/colour
+prentice	/man/1/yacc
+prep	/man/10/9load
+prep	/man/2/disks
+prep	/man/8/prep
+prepare	/man/10/dmainit
+prepare	/man/4/kfs
+prepare	/man/5/open
+prepare	/man/8/prep
+prepared	/man/1/acme
+prepared	/man/10/9load
+prepared	/man/2/plumbmsg
+prepared	/man/2/tk
+prepared	/man/5/0intro
+prepared	/man/8/prep
+prepares	/man/10/dmainit
+prepares	/man/2/drawmux
+prepares	/man/2/sys-open
+prepares	/man/8/prep
+prepares	/man/8/rstyxd
+prepended	/man/1/mash
+prepending	/man/1/mash-tk
+preprocessed	/man/10/a.out
+preprocessing	/man/10/2c
+preprocessor	/man/10/2a
+preprocessor	/man/10/2c
+preprocessor	/man/10/c2l
+preprocessors	/man/1/man
+preprocessors	/man/6/man
+prereq	/man/1/mk
+prereq	/man/10/mk
+prereq1	/man/1/mk
+prereq1	/man/10/mk
+prereq2	/man/1/mk
+prereq2	/man/10/mk
+prerequisite	/man/1/mash
+prerequisite	/man/1/mk
+prerequisite	/man/10/mk
+prerequisite	/man/2/styxservers
+prerequisites	/man/1/mash-make
+prerequisites	/man/1/mk
+prerequisites	/man/10/mk
+prescient	/man/6/image
+prescribed	/man/10/dev
+presence	/man/10/2c
+presence	/man/10/ar
+presence	/man/10/strcat
+presence	/man/2/readdir
+presence	/man/4/acme
+presence	/man/6/sbl
+presence	/man/9/grid
+presentation	/man/1/charon
+presentation	/man/3/cons
+presentation	/man/3/kprof
+presentations	/man/1/mux
+presented	/man/1/logon
+presented	/man/1/man
+presented	/man/1/mash-tk
+presented	/man/1/netstat
+presented	/man/10/dev
+presented	/man/10/plan9.ini
+presented	/man/2/bufio-chanfill
+presented	/man/2/dbm
+presented	/man/2/draw-image
+presented	/man/2/factotum
+presented	/man/2/spree
+presented	/man/2/styxservers
+presented	/man/2/sys-export
+presented	/man/2/w3c-css
+presented	/man/3/fpga
+presented	/man/5/0intro
+presented	/man/5/attach
+presented	/man/6/login
+presented	/man/8/collabsrv
+presented	/man/8/getauthinfo
+presenting	/man/8/collabsrv
+presents	/man/1/0intro
+presents	/man/1/grid-session
+presents	/man/10/kprof
+presents	/man/10/odbc
+presents	/man/2/dividers
+presents	/man/2/draw-example
+presents	/man/2/encoding
+presents	/man/2/sh
+presents	/man/2/sys-0intro
+presents	/man/2/sys-file2chan
+presents	/man/3/arch
+preserve	/man/1/ar
+preserve	/man/1/dd
+preserve	/man/1/vacget
+preserve	/man/10/iar
+preserve	/man/2/keyring-getmsg
+preserve	/man/2/msgio
+preserve	/man/3/ip
+preserved	/man/1/cp
+preserved	/man/1/fmt
+preserved	/man/1/uuencode
+preserved	/man/2/sys-0intro
+preserved	/man/2/sys-pctl
+preserved	/man/3/pipe
+preserves	/man/10/c2l
+preserves	/man/2/styxservers
+presotto	/man/4/factotum
+press	/man/1/acme
+press	/man/3/touch
+press	/man/9/bind
+press	/man/9/text
+pressed	/man/1/acme
+pressed	/man/1/deb
+pressed	/man/1/wm
+pressed	/man/2/draw-pointer
+pressed	/man/2/popup
+pressed	/man/2/tk
+pressed	/man/9/bind
+pressed	/man/9/button
+pressed	/man/9/canvas
+pressed	/man/9/checkbutton
+pressed	/man/9/listbox
+pressed	/man/9/menubutton
+pressed	/man/9/radiobutton
+pressed	/man/9/scale
+pressed	/man/9/scrollbar
+pressed	/man/9/text
+presses	/man/9/bind
+presses	/man/9/button
+presses	/man/9/checkbutton
+presses	/man/9/grab
+presses	/man/9/menu
+presses	/man/9/menubutton
+presses	/man/9/radiobutton
+presses	/man/9/text
+pressing	/man/1/acme
+pressing	/man/1/deb
+pressing	/man/9/button
+pressing	/man/9/listbox
+pressing	/man/9/menu
+pressing	/man/9/menubutton
+pressing	/man/9/options
+pressing	/man/9/scrollbar
+pressure	/man/1/units
+pressure	/man/3/touch
+presumably	/man/10/ms2
+presumed	/man/10/ms2
+presumed	/man/3/kprof
+pretend	/man/1/mk
+pretend	/man/10/mk
+pretend	/man/2/security-0intro
+pretends	/man/2/security-0intro
+pretty	/man/1/alphabet-main
+pretty	/man/10/c2l
+prev	/man/1/wm-misc
+prev	/man/2/spree-cardlib
+prevailing	/man/6/man
+prevails	/man/3/fs
+prevent	/man/1/filename
+prevent	/man/1/limbo
+prevent	/man/1/m4
+prevent	/man/1/man
+prevent	/man/1/sh-tk
+prevent	/man/10/allocb
+prevent	/man/10/styxserver
+prevent	/man/2/disks
+prevent	/man/2/security-0intro
+prevent	/man/2/sys-chdir
+prevent	/man/2/sys-pctl
+prevent	/man/2/w3c-uris
+prevent	/man/3/cap
+prevent	/man/3/cmd
+prevent	/man/9/bind
+preventing	/man/10/dmainit
+preventing	/man/2/sys-0intro
+preventing	/man/6/regexp
+prevents	/man/1/charon
+prevents	/man/10/ref
+prevents	/man/2/prof
+prevents	/man/2/sys-export
+prevents	/man/3/cmd
+prevents	/man/3/flash
+prevents	/man/4/ramfile
+previously	/man/1/mv
+previously	/man/1/netstat
+previously	/man/1/ps
+previously	/man/1/sh
+previously	/man/1/sh-alphabet
+previously	/man/1/vacget
+previously	/man/1/wm-sh
+previously	/man/10/acid
+previously	/man/10/dmainit
+previously	/man/10/intrenable
+previously	/man/10/lock
+previously	/man/10/xalloc
+previously	/man/2/bufio
+previously	/man/2/dhcpclient
+previously	/man/2/ida
+previously	/man/2/plumbmsg
+previously	/man/2/secstore
+previously	/man/2/security-0intro
+previously	/man/2/sh
+previously	/man/2/spree
+previously	/man/2/spree-cardlib
+previously	/man/2/styxservers
+previously	/man/2/styxservers-nametree
+previously	/man/2/xml
+previously	/man/3/cap
+previously	/man/3/cmd
+previously	/man/3/ftl
+previously	/man/3/ip
+previously	/man/3/tinyfs
+previously	/man/3/touch
+previously	/man/3/tv
+previously	/man/4/9srvfs
+previously	/man/4/ramfile
+previously	/man/5/attach
+previously	/man/6/image
+previously	/man/6/login
+previously	/man/6/sbl
+previously	/man/8/createsignerkey
+previously	/man/8/getauthinfo
+previously	/man/8/prep
+previously	/man/8/register
+previously	/man/8/signer
+previously	/man/9/canvas
+previously	/man/9/grid
+previously	/man/9/menu
+previously	/man/9/send
+previously	/man/9/types
+prevrange	/man/9/text
+prf	/man/1/cprof
+prf	/man/2/prof
+pri	/man/10/kproc
+pribackground	/man/10/kproc
+prihi	/man/10/kproc
+prihicodec	/man/10/kproc
+prilo	/man/10/kproc
+prilock	/man/10/kproc
+prilock	/man/10/lock
+prilocodec	/man/10/kproc
+primality	/man/1/math-misc
+primaries	/man/3/dbg
+primarily	/man/2/tk
+primary	/man/10/0intro
+primary	/man/10/styxserver
+primary	/man/2/attrdb
+primary	/man/2/cfg
+primary	/man/2/convcs
+primary	/man/2/disks
+primary	/man/2/w3c-xpointers
+primary	/man/3/cons
+primary	/man/3/flash
+primary	/man/6/colour
+primary	/man/6/scancode
+primary	/man/8/applylog
+primary	/man/8/prep
+primary	/man/9/frame
+prime	/man/1/math-misc
+prime	/man/2/hash
+prime	/man/2/keyring-gensk
+prime	/man/8/ai2key
+primes	/man/1/math-misc
+primes.b	/man/1/math-misc
+primitive	/man/10/qlock
+primitive	/man/2/asn1
+primitive	/man/2/draw-0intro
+primitive	/man/2/draw-font
+primitive	/man/2/keyring-gensk
+primitive	/man/2/math-0intro
+primitive	/man/2/math-linalg
+primitive	/man/2/mpeg
+primitive	/man/6/sexprs
+primitive	/man/6/ubfa
+primitives	/man/1/0intro
+primitives	/man/1/mash-tk
+primitives	/man/1/sh-csv
+primitives	/man/1/sh-sexprs
+primitives	/man/1/sh-tk
+primitives	/man/10/lock
+primitives	/man/10/splhi
+primitives	/man/10/xalloc
+primitives	/man/2/alphabet-intro
+primitives	/man/2/draw-example
+primitives	/man/2/draw-image
+primitives	/man/2/math-linalg
+primitives	/man/2/security-auth
+primitives	/man/2/tk
+primitives	/man/3/draw
+principal	/man/1/0intro
+principal	/man/2/asn1
+principal	/man/2/spki
+principals	/man/2/security-0intro
+principals	/man/2/spki
+principles	/man/1/0intro
+principles	/man/2/spki
+prinormal	/man/10/kproc
+print	/man/1/acme
+print	/man/1/alphabet-fs
+print	/man/1/alphabet-main
+print	/man/1/ar
+print	/man/1/cal
+print	/man/1/calc
+print	/man/1/cmp
+print	/man/1/comm
+print	/man/1/crypt
+print	/man/1/date
+print	/man/1/disdep
+print	/man/1/du
+print	/man/1/echo
+print	/man/1/emu
+print	/man/1/fc
+print	/man/1/freq
+print	/man/1/fs
+print	/man/1/gettar
+print	/man/1/grep
+print	/man/1/grid-monitor
+print	/man/1/limbo
+print	/man/1/look
+print	/man/1/ls
+print	/man/1/man
+print	/man/1/mash
+print	/man/1/math-misc
+print	/man/1/mc
+print	/man/1/mdb
+print	/man/1/mk
+print	/man/1/pwd
+print	/man/1/secstore
+print	/man/1/sh
+print	/man/1/sh-alphabet
+print	/man/1/sh-std
+print	/man/1/stack
+print	/man/1/tail
+print	/man/1/unicode
+print	/man/1/uniq
+print	/man/1/units
+print	/man/1/vacget
+print	/man/1/webgrab
+print	/man/1/xd
+print	/man/1/yacc
+print	/man/10/2c
+print	/man/10/2l
+print	/man/10/5coff
+print	/man/10/acid
+print	/man/10/error
+print	/man/10/iar
+print	/man/10/inm
+print	/man/10/ksize
+print	/man/10/mk
+print	/man/10/odbc
+print	/man/10/panic
+print	/man/10/print
+print	/man/10/qio
+print	/man/10/styxserver
+print	/man/2/arg
+print	/man/2/asn1
+print	/man/2/bloomfilter
+print	/man/2/command
+print	/man/2/dial
+print	/man/2/draw-0intro
+print	/man/2/ir
+print	/man/2/itslib
+print	/man/2/keyring-0intro
+print	/man/2/print
+print	/man/2/prof
+print	/man/2/sets
+print	/man/2/sys-0intro
+print	/man/2/sys-print
+print	/man/2/sys-werrstr
+print	/man/2/tftp
+print	/man/2/w3c-css
+print	/man/3/cons
+print	/man/4/dossrv
+print	/man/4/registry
+print	/man/4/vacfs
+print	/man/8/applylog
+print	/man/8/bootpd
+print	/man/8/changelogin
+print	/man/8/create
+print	/man/8/cs
+print	/man/8/dhcp
+print	/man/8/kfscmd
+print	/man/8/mkfs
+print	/man/8/prep
+print	/man/8/rdbgsrv
+print	/man/8/styxchat
+print.b	/man/2/print
+print.c	/man/2/sys-print
+print.dis	/man/2/print
+print.m	/man/2/print
+printable	/man/1/acme
+printable	/man/1/freq
+printable	/man/1/mdb
+printable	/man/1/strings
+printable	/man/1/xd
+printable	/man/2/bufio
+printable	/man/2/encoding
+printable	/man/2/keyring-certtostr
+printable	/man/2/print
+printable	/man/2/sexprs
+printable	/man/2/styx
+printable	/man/2/sys-0intro
+printable	/man/2/ubfa
+printable	/man/2/w3c-xpointers
+printable	/man/5/0intro
+printable	/man/6/sexprs
+printable	/man/6/users
+printablestring	/man/2/asn1
+printbase	/man/1/calc
+printed	/man/1/acme
+printed	/man/1/alphabet-fs
+printed	/man/1/basename
+printed	/man/1/cal
+printed	/man/1/calc
+printed	/man/1/disdep
+printed	/man/1/fc
+printed	/man/1/freq
+printed	/man/1/fs
+printed	/man/1/kill
+printed	/man/1/ls
+printed	/man/1/man
+printed	/man/1/mash
+printed	/man/1/mdb
+printed	/man/1/mk
+printed	/man/1/ps
+printed	/man/1/sh
+printed	/man/1/sh-alphabet
+printed	/man/1/sh-arg
+printed	/man/1/sh-expr
+printed	/man/1/sh-std
+printed	/man/1/sh-tk
+printed	/man/1/tkcmd
+printed	/man/1/unicode
+printed	/man/1/xd
+printed	/man/10/a.out
+printed	/man/10/inm
+printed	/man/10/kprof
+printed	/man/10/mk
+printed	/man/10/plan9.ini
+printed	/man/10/srclist
+printed	/man/2/arg
+printed	/man/2/sh
+printed	/man/2/sys-print
+printed	/man/3/cons
+printed	/man/4/iostats
+printed	/man/6/man
+printed	/man/8/ai2key
+printed	/man/8/ping
+printed	/man/8/prep
+printed	/man/8/styxmon
+printed	/man/9/send
+printer	/man/10/c2l
+printer	/man/2/print
+printer	/man/3/lpt
+printer's	/man/2/print
+printer.cfg	/man/2/print
+printers	/man/2/print
+printers	/man/3/lpt
+printfd	/man/2/print
+printing	/man/1/bind
+printing	/man/1/comm
+printing	/man/1/mdb
+printing	/man/1/mk
+printing	/man/1/sh-tk
+printing	/man/1/strings
+printing	/man/1/tail
+printing	/man/10/mk
+printing	/man/10/qio
+printing	/man/2/math-0intro
+printing	/man/2/print
+printing	/man/2/sh
+printing	/man/4/acme
+printing	/man/4/ftpfs
+printing	/man/6/man
+printing	/man/8/cs
+printing	/man/8/styxchat
+printing	/man/9/bind
+printing	/man/9/entry
+printing	/man/9/text
+prints	/man/1/acme
+prints	/man/1/asm
+prints	/man/1/basename
+prints	/man/1/cal
+prints	/man/1/calc
+prints	/man/1/cat
+prints	/man/1/cleanname
+prints	/man/1/cmp
+prints	/man/1/date
+prints	/man/1/env
+prints	/man/1/fc
+prints	/man/1/filename
+prints	/man/1/fortune
+prints	/man/1/freq
+prints	/man/1/fs
+prints	/man/1/grep
+prints	/man/1/grid-register
+prints	/man/1/kill
+prints	/man/1/look
+prints	/man/1/m4
+prints	/man/1/man
+prints	/man/1/math-misc
+prints	/man/1/mdb
+prints	/man/1/mkdir
+prints	/man/1/netkey
+prints	/man/1/netstat
+prints	/man/1/ns
+prints	/man/1/ps
+prints	/man/1/pwd
+prints	/man/1/read
+prints	/man/1/sh
+prints	/man/1/sh-alphabet
+prints	/man/1/strings
+prints	/man/1/sum
+prints	/man/1/tsort
+prints	/man/1/unicode
+prints	/man/1/vacget
+prints	/man/10/5cv
+prints	/man/10/9load
+prints	/man/10/acid
+prints	/man/10/inm
+prints	/man/10/ksize
+prints	/man/10/print
+prints	/man/10/srclist
+prints	/man/2/print
+prints	/man/2/prof
+prints	/man/2/sh
+prints	/man/2/sys-byte2char
+prints	/man/2/sys-print
+prints	/man/3/cons
+prints	/man/4/registry
+prints	/man/7/cddb
+prints	/man/8/ai2key
+prints	/man/8/bootpd
+prints	/man/8/create
+prints	/man/8/cs
+prints	/man/8/dns
+prints	/man/8/mangaload
+prints	/man/8/mkfs
+prints	/man/8/prep
+prints	/man/8/sntp
+prints	/man/8/styxchat
+prior	/man/1/mk
+prior	/man/1/sh
+prior	/man/1/tkcmd
+prior	/man/10/mk
+prior	/man/2/spree
+prior	/man/2/spree-cardlib
+prior	/man/4/grid-cpu
+priori	/man/2/tk
+priorities	/man/1/mash
+prioritized	/man/10/9load
+priority	/man/1/auplay
+priority	/man/1/os
+priority	/man/10/kproc
+priority	/man/10/lock
+priority	/man/2/srv
+priority	/man/2/w3c-css
+priority	/man/3/cmd
+priority	/man/3/kprof
+priority	/man/9/text
+prirealtime	/man/10/kproc
+prism	/man/10/plan9.ini
+priv	/man/10/styxserver
+privacy	/man/2/secstore
+private	/man/10/styxserver
+private	/man/2/asn1
+private	/man/2/draw-context
+private	/man/2/draw-example
+private	/man/2/draw-image
+private	/man/2/draw-screen
+private	/man/2/keyring-0intro
+private	/man/2/keyring-auth
+private	/man/2/keyring-certtostr
+private	/man/2/keyring-gensk
+private	/man/2/keyring-sha1
+private	/man/2/security-0intro
+private	/man/2/security-login
+private	/man/2/sh
+private	/man/2/spki
+private	/man/2/spree
+private	/man/2/sys-pctl
+private	/man/3/draw
+private	/man/4/factotum
+private	/man/4/namespace
+private	/man/6/auth
+private	/man/6/keytext
+private	/man/6/users
+private	/man/8/ai2key
+private	/man/8/collabsrv
+private	/man/8/createsignerkey
+private	/man/8/httpd
+private	/man/8/svc
+privately	/man/2/asn1
+privilege	/man/1/wm
+privileged	/man/10/eve
+privileged	/man/3/cons
+privileged	/man/8/bootpd
+privileges	/man/10/eve
+privileges	/man/3/ip
+privileges	/man/5/0intro
+privileges	/man/8/cs
+pro	/man/10/plan9.ini
+probabilistic	/man/2/bloomfilter
+probability	/man/2/bloomfilter
+probe	/man/1/charon
+probe	/man/8/ping
+probed	/man/1/charon
+probed	/man/10/plan9.ini
+probes	/man/2/spree
+probing	/man/10/9load
+probing	/man/10/plan9.ini
+proc	/man/10/acid
+proc	/man/10/error
+proc	/man/10/kproc
+proc	/man/10/sleep
+proc	/man/2/draw-image
+proc	/man/3/dbg
+proc.c	/man/10/error
+proc.c	/man/10/sleep
+procedure	/man/1/mkdir
+procedure	/man/10/plan9.ini
+procedure	/man/2/dial
+procedure	/man/2/draw-image
+procedure	/man/2/keyring-0intro
+procedure	/man/2/plumbmsg
+procedure	/man/2/security-login
+procedure	/man/2/sys-dial
+procedure	/man/3/0intro
+procedure	/man/3/ip
+procedure	/man/3/mnt
+procedure	/man/6/dis
+procedures	/man/10/0intro
+procedures	/man/10/9load
+procedures	/man/10/error
+procedures	/man/2/keyring-0intro
+procedures	/man/4/namespace
+proceed	/man/10/qio
+proceed	/man/2/lock
+proceed	/man/4/factotum
+proceed	/man/9/text
+proceeded	/man/1/listen
+proceeding	/man/1/bind
+proceeding	/man/10/9load
+proceeds	/man/1/m4
+proceeds	/man/10/9load
+proceeds	/man/10/qio
+proceeds	/man/2/draw-image
+process	/man/1/0intro
+process	/man/1/bind
+process	/man/1/blur
+process	/man/1/calc
+process	/man/1/charon
+process	/man/1/deb
+process	/man/1/diff
+process	/man/1/emu
+process	/man/1/grid-monitor
+process	/man/1/kill
+process	/man/1/listen
+process	/man/1/m4
+process	/man/1/mk
+process	/man/1/nsbuild
+process	/man/1/os
+process	/man/1/ps
+process	/man/1/sh
+process	/man/1/sh-file2chan
+process	/man/1/sh-std
+process	/man/1/sh-tk
+process	/man/1/stack
+process	/man/1/stream
+process	/man/1/tiny
+process	/man/1/wm-misc
+process	/man/1/wm-sh
+process	/man/10/acid
+process	/man/10/c2l
+process	/man/10/dev
+process	/man/10/dynld
+process	/man/10/error
+process	/man/10/eve
+process	/man/10/intrenable
+process	/man/10/kproc
+process	/man/10/lock
+process	/man/10/mk
+process	/man/10/newchan
+process	/man/10/odbc
+process	/man/10/plan9.ini
+process	/man/10/qio
+process	/man/10/qlock
+process	/man/10/sleep
+process	/man/2/0intro
+process	/man/2/bufio-chanfill
+process	/man/2/command
+process	/man/2/dbm
+process	/man/2/debug
+process	/man/2/devpointer
+process	/man/2/dhcpclient
+process	/man/2/draw-display
+process	/man/2/draw-screen
+process	/man/2/env
+process	/man/2/exception
+process	/man/2/factotum
+process	/man/2/filter
+process	/man/2/imagefile
+process	/man/2/ir
+process	/man/2/keyring-getstring
+process	/man/2/mpeg
+process	/man/2/msgio
+process	/man/2/prof
+process	/man/2/secstore
+process	/man/2/sh
+process	/man/2/styxconv
+process	/man/2/styxflush
+process	/man/2/styxpersist
+process	/man/2/styxservers
+process	/man/2/styxservers-nametree
+process	/man/2/sys-0intro
+process	/man/2/sys-bind
+process	/man/2/sys-chdir
+process	/man/2/sys-export
+process	/man/2/sys-file2chan
+process	/man/2/sys-open
+process	/man/2/sys-pctl
+process	/man/2/sys-pipe
+process	/man/2/sys-sleep
+process	/man/2/sys-stat
+process	/man/2/sys-werrstr
+process	/man/2/timers
+process	/man/2/tkclient
+process	/man/2/volume
+process	/man/2/wait
+process	/man/2/wmclient
+process	/man/2/wmlib
+process	/man/2/wmsrv
+process	/man/2/workdir
+process	/man/2/xml
+process	/man/3/cap
+process	/man/3/cmd
+process	/man/3/cons
+process	/man/3/dbg
+process	/man/3/draw
+process	/man/3/dup
+process	/man/3/env
+process	/man/3/ip
+process	/man/3/mnt
+process	/man/3/plap
+process	/man/3/prog
+process	/man/3/sd
+process	/man/3/sign
+process	/man/3/srv
+process	/man/3/touch
+process	/man/4/dbfs
+process	/man/4/export
+process	/man/4/factotum
+process	/man/4/import
+process	/man/4/lockfs
+process	/man/4/logfile
+process	/man/5/0intro
+process	/man/5/flush
+process	/man/5/open
+process	/man/6/login
+process	/man/7/db
+process	/man/8/collabsrv
+process	/man/8/cs
+process	/man/8/dhcp
+process	/man/8/mangaload
+process	/man/8/register
+process	/man/8/touchcal
+process's	/man/1/0intro
+process's	/man/1/env
+process's	/man/1/kill
+process's	/man/1/sh
+process's	/man/10/master
+process's	/man/2/newns
+process's	/man/2/styxservers
+process's	/man/2/sys-0intro
+process's	/man/2/sys-werrstr
+process's	/man/3/cap
+process's	/man/8/collabsrv
+processed	/man/1/alphabet-abc
+processed	/man/1/alphabet-grid
+processed	/man/1/blur
+processed	/man/1/charon
+processed	/man/1/m4
+processed	/man/1/man
+processed	/man/1/mk
+processed	/man/1/xd
+processed	/man/10/getfields
+processed	/man/10/mk
+processed	/man/10/plan9.ini
+processed	/man/10/styxserver
+processed	/man/2/asn1
+processed	/man/2/filter
+processed	/man/2/filter-slip
+processed	/man/2/tkclient
+processed	/man/2/wmclient
+processed	/man/8/applylog
+processed	/man/9/canvas
+processed	/man/9/pack
+processes	/man/1/bind
+processes	/man/1/blur
+processes	/man/1/cd
+processes	/man/1/grid-monitor
+processes	/man/1/kill
+processes	/man/1/man
+processes	/man/1/math-misc
+processes	/man/1/ps
+processes	/man/1/sh
+processes	/man/1/sh-mload
+processes	/man/1/sh-tk
+processes	/man/1/stream
+processes	/man/1/wm-misc
+processes	/man/10/acid
+processes	/man/10/kproc
+processes	/man/10/newchan
+processes	/man/10/ntsrv
+processes	/man/10/plan9.ini
+processes	/man/10/qlock
+processes	/man/10/sleep
+processes	/man/10/splhi
+processes	/man/10/styxserver
+processes	/man/2/alphabet-intro
+processes	/man/2/bufio
+processes	/man/2/draw-display
+processes	/man/2/draw-screen
+processes	/man/2/env
+processes	/man/2/exception
+processes	/man/2/lock
+processes	/man/2/sh
+processes	/man/2/spki-verifier
+processes	/man/2/styxflush
+processes	/man/2/sys-0intro
+processes	/man/2/sys-pctl
+processes	/man/2/sys-pipe
+processes	/man/2/sys-read
+processes	/man/2/timers
+processes	/man/2/tkclient
+processes	/man/2/wmclient
+processes	/man/2/wmlib
+processes	/man/3/0intro
+processes	/man/3/cons
+processes	/man/3/dbg
+processes	/man/3/draw
+processes	/man/3/env
+processes	/man/3/mnt
+processes	/man/3/pipe
+processes	/man/3/prog
+processes	/man/4/factotum
+processes	/man/4/registry
+processes	/man/5/0intro
+processes	/man/8/collabsrv
+processes	/man/8/cs
+processes	/man/9/pack
+processing	/man/1/alphabet-abc
+processing	/man/1/alphabet-grid
+processing	/man/1/blur
+processing	/man/1/charon
+processing	/man/1/cleanname
+processing	/man/1/dd
+processing	/man/1/sh
+processing	/man/1/xd
+processing	/man/10/styxserver
+processing	/man/2/alphabet-intro
+processing	/man/2/cfg
+processing	/man/2/exception
+processing	/man/2/filter
+processing	/man/2/filter-deflate
+processing	/man/2/filter-slip
+processing	/man/2/fsproto
+processing	/man/2/imagefile
+processing	/man/2/keyring-0intro
+processing	/man/2/sexprs
+processing	/man/2/styxservers
+processing	/man/2/sys-file2chan
+processing	/man/2/w3c-xpointers
+processing	/man/2/wmclient
+processing	/man/2/xml
+processing	/man/3/cons
+processing	/man/3/sd
+processing	/man/3/touch
+processing	/man/3/tv
+processing	/man/4/grid-cpu
+processing	/man/6/man
+processing	/man/6/utf
+processor	/man/1/0intro
+processor	/man/1/diff
+processor	/man/1/m4
+processor	/man/10/2c
+processor	/man/10/lock
+processor	/man/10/panic
+processor	/man/10/plan9.ini
+processor	/man/10/splhi
+processor	/man/2/filter
+processor	/man/3/arch
+processor	/man/8/ftl
+processor's	/man/3/fpga
+processors	/man/10/plan9.ini
+processses	/man/1/blur
+prod	/man/10/qio
+produce	/man/1/acme
+produce	/man/1/alphabet-abc
+produce	/man/1/alphabet-grid
+produce	/man/1/ar
+produce	/man/1/calc
+produce	/man/1/cook
+produce	/man/1/ls
+produce	/man/1/mash-make
+produce	/man/1/mk
+produce	/man/1/sh
+produce	/man/1/stack
+produce	/man/1/wm-misc
+produce	/man/10/2c
+produce	/man/10/2l
+produce	/man/10/mk
+produce	/man/2/keyring-0intro
+produce	/man/2/keyring-certtostr
+produce	/man/2/keyring-sha1
+produce	/man/2/rfc822
+produce	/man/2/scsiio
+produce	/man/2/styx
+produce	/man/2/sys-pipe
+produce	/man/3/cons
+produce	/man/6/image
+produce	/man/8/applylog
+produce	/man/8/signer
+produce	/man/9/text
+produced	/man/1/acme
+produced	/man/1/alphabet-fs
+produced	/man/1/avr
+produced	/man/1/cal
+produced	/man/1/diff
+produced	/man/1/du
+produced	/man/1/fc
+produced	/man/1/fs
+produced	/man/1/gettar
+produced	/man/1/itest
+produced	/man/1/m4
+produced	/man/10/5coff
+produced	/man/10/ar
+produced	/man/10/conf
+produced	/man/10/devattach
+produced	/man/10/dynld
+produced	/man/10/styx
+produced	/man/2/draw-image
+produced	/man/2/fsproto
+produced	/man/2/ida
+produced	/man/2/keyring-crypt
+produced	/man/2/keyset
+produced	/man/2/names
+produced	/man/2/secstore
+produced	/man/2/sexprs
+produced	/man/2/styx
+produced	/man/2/styxflush
+produced	/man/3/cap
+produced	/man/3/cons
+produced	/man/3/sign
+produced	/man/4/archfs
+produced	/man/4/factotum
+produced	/man/5/read
+produced	/man/6/keytext
+produced	/man/8/applylog
+produced	/man/8/changelogin
+produced	/man/8/create
+produced	/man/8/fpgaload
+producer	/man/3/pipe
+producers	/man/10/qio
+produces	/man/1/alphabet-fs
+produces	/man/1/comm
+produces	/man/1/cook
+produces	/man/1/diff
+produces	/man/1/emu
+produces	/man/1/fs
+produces	/man/1/limbo
+produces	/man/1/mux
+produces	/man/10/odbc
+produces	/man/10/print
+produces	/man/2/draw-image
+produces	/man/2/keyring-certtostr
+produces	/man/2/keyring-sha1
+produces	/man/2/rfc822
+produces	/man/2/sets
+produces	/man/2/sys-print
+produces	/man/3/ip
+produces	/man/3/prog
+produces	/man/3/srv
+produces	/man/3/switch
+produces	/man/8/applylog
+produces	/man/8/cs
+producing	/man/1/acme
+producing	/man/10/print
+producing	/man/2/rfc822
+producing	/man/2/sys-print
+producing	/man/2/translate
+producing	/man/4/logfile
+producing	/man/8/mkfs
+producing	/man/9/0intro
+product	/man/1/calc
+product	/man/1/dd
+product	/man/2/math-linalg
+product	/man/5/open
+product	/man/8/ai2key
+product	/man/8/create
+production	/man/1/yacc
+production	/man/10/styxserver
+productive	/man/1/0intro
+productivity	/man/1/0intro
+prof	/man/1/cprof
+prof	/man/1/mprof
+prof	/man/1/prof
+prof	/man/2/prof
+prof	/man/3/prof
+prof.b	/man/1/prof
+profile	/man/1/cprof
+profile	/man/1/mprof
+profile	/man/1/prof
+profile	/man/1/sh
+profile	/man/10/kprof
+profile	/man/2/prof
+profile	/man/3/prof
+profile's	/man/2/prof
+profile.b	/man/2/prof
+profile.m	/man/2/prof
+profiled	/man/1/cprof
+profiled	/man/1/mprof
+profiled	/man/1/prof
+profiled	/man/10/kprof
+profiled	/man/2/prof
+profiled	/man/3/prof
+profiler	/man/1/mprof
+profiler	/man/1/prof
+profiler	/man/2/prof
+profiler	/man/3/prof
+profilers	/man/3/prof
+profiles	/man/1/cprof
+profiles	/man/1/mprof
+profiles	/man/1/prof
+profiles	/man/2/prof
+profiles	/man/3/prof
+profiling	/man/1/cprof
+profiling	/man/1/mprof
+profiling	/man/1/prof
+profiling	/man/10/2l
+profiling	/man/10/kprof
+profiling	/man/2/prof
+profiling	/man/3/kprof
+profiling	/man/3/prof
+profusion	/man/10/9load
+prog	/man/1/deb
+prog	/man/1/kill
+prog	/man/1/mk
+prog	/man/1/ns
+prog	/man/1/ps
+prog	/man/1/sh
+prog	/man/1/stack
+prog	/man/1/tiny
+prog	/man/10/2c
+prog	/man/10/mk
+prog	/man/2/0intro
+prog	/man/2/arg
+prog	/man/2/command
+prog	/man/2/debug
+prog	/man/2/exception
+prog	/man/2/secstore
+prog	/man/2/sh
+prog	/man/2/sys-0intro
+prog	/man/2/sys-fd2path
+prog	/man/2/sys-pctl
+prog	/man/2/wait
+prog	/man/3/dup
+prog	/man/3/prog
+prog	/man/3/root
+prog	/man/4/namespace
+prog	/man/5/0intro
+prog.stack	/man/2/debug
+progargs	/man/10/acid
+progname	/man/2/arg
+program's	/man/10/acid
+program's	/man/2/arg
+program's	/man/2/debug
+program's	/man/2/prefab-compound
+program's	/man/6/0intro
+programmable	/man/1/mash
+programmable	/man/1/sh
+programmable	/man/1/tiny
+programmable	/man/10/acid
+programmed	/man/10/inb
+programmed	/man/2/styxservers
+programmed	/man/3/flash
+programmer	/man/2/0intro
+programmer's	/man/1/0intro
+programmer's	/man/1/mash
+programmer's	/man/1/secstore
+programmer's	/man/1/yacc
+programmer's	/man/10/sleep
+programmer's	/man/4/factotum
+programmers	/man/1/0intro
+programmers	/man/1/acme
+programmers	/man/2/math-0intro
+programming	/man/1/0intro
+programming	/man/1/avr
+programming	/man/1/limbo
+programming	/man/1/sh
+programming	/man/1/yacc
+programming	/man/10/plan9.ini
+programming	/man/2/0intro
+programming	/man/2/sys-0intro
+programming	/man/2/sys-dup
+programming	/man/2/sys-self
+programming	/man/3/flash
+programming	/man/3/pnp
+programming	/man/9/0intro
+programs	/man/1/0intro
+programs	/man/1/9win
+programs	/man/1/acme
+programs	/man/1/calendar
+programs	/man/1/collab-clients
+programs	/man/1/cprof
+programs	/man/1/disdep
+programs	/man/1/itest
+programs	/man/1/mk
+programs	/man/1/mprof
+programs	/man/1/prof
+programs	/man/1/sh-file2chan
+programs	/man/1/tktester
+programs	/man/1/toolbar
+programs	/man/1/wish
+programs	/man/1/wm
+programs	/man/10/0intro
+programs	/man/10/2a
+programs	/man/10/2c
+programs	/man/10/9load
+programs	/man/10/plan9.ini
+programs	/man/10/rune
+programs	/man/2/0intro
+programs	/man/2/arg
+programs	/man/2/command
+programs	/man/2/draw-context
+programs	/man/2/draw-image
+programs	/man/2/format
+programs	/man/2/ir
+programs	/man/2/keyring-0intro
+programs	/man/2/keyring-auth
+programs	/man/2/sh
+programs	/man/2/styx
+programs	/man/2/sys-0intro
+programs	/man/3/cons
+programs	/man/3/lpt
+programs	/man/3/prog
+programs	/man/3/root
+programs	/man/3/sd
+programs	/man/4/0intro
+programs	/man/4/factotum
+programs	/man/4/iostats
+programs	/man/4/namespace
+programs	/man/4/registry
+programs	/man/5/0intro
+programs	/man/5/open
+programs	/man/6/dis
+programs	/man/6/man
+programs	/man/6/proto
+programs	/man/6/sexprs
+programs	/man/6/utf
+programs	/man/7/db
+programs	/man/8/0intro
+programs	/man/8/createsignerkey
+programs	/man/8/httpd
+programs	/man/8/prep
+programs	/man/9/0intro
+programs	/man/9/text
+progress	/man/1/charon
+progress	/man/1/webgrab
+progress	/man/2/complete
+progress	/man/2/tftp
+progresses	/man/1/charon
+projecting	/man/9/canvas
+projection	/man/2/geodesy
+projection.if	/man/2/geodesy
+projections	/man/2/geodesy
+prologue	/man/10/c2l
+promiscuous	/man/3/ether
+promoted	/man/10/print
+promoted	/man/2/draw-image
+promotes	/man/10/2c
+prompt	/man/1/acme
+prompt	/man/1/logon
+prompt	/man/1/mash
+prompt	/man/1/miniterm
+prompt	/man/1/secstore
+prompt	/man/1/sh
+prompt	/man/1/sh-std
+prompt	/man/1/stack
+prompt	/man/1/tiny
+prompt	/man/1/tkcmd
+prompt	/man/1/wm-misc
+prompt	/man/10/9load
+prompt	/man/10/acid
+prompt	/man/10/plan9.ini
+prompt	/man/2/dialog
+prompt	/man/2/draw-example
+prompt	/man/4/ftpfs
+prompt	/man/8/mangaload
+prompt	/man/8/prep
+prompt	/man/8/signer
+prompted	/man/1/sh
+prompted	/man/10/plan9.ini
+prompted	/man/8/changelogin
+prompted	/man/8/getauthinfo
+prompted	/man/8/signer
+prompting	/man/1/miniterm
+prompting	/man/1/tiny
+prompting	/man/4/factotum
+prompting	/man/4/ftpfs
+prompting	/man/8/getauthinfo
+prompts	/man/1/crypt
+prompts	/man/1/dmview
+prompts	/man/1/netkey
+prompts	/man/1/passwd
+prompts	/man/1/secstore
+prompts	/man/1/wish
+prompts	/man/4/factotum
+prompts	/man/4/keyfs
+prompts	/man/4/registry
+prompts	/man/8/cs
+prompts	/man/8/dns
+prompts	/man/8/svc
+prompts	/man/8/touchcal
+promulgated	/man/1/tail
+prone	/man/1/wm-misc
+prone	/man/10/9load
+proof	/man/2/spki
+proof	/man/2/spki-verifier
+proof	/man/4/factotum
+propagate	/man/2/exception
+propagate	/man/2/spki
+propagate	/man/3/prog
+propagate	/man/9/grid
+propagate	/man/9/pack
+propagated	/man/1/mk
+propagated	/man/1/sh
+propagated	/man/10/mk
+propagated	/man/3/ip
+propagation	/man/9/grid
+propagation	/man/9/pack
+proper	/man/10/allocb
+proper	/man/10/ref
+proper	/man/2/0intro
+proper	/man/2/draw-image
+proper	/man/2/itslib
+proper	/man/9/bind
+proper	/man/9/pack
+properly	/man/1/0intro
+properly	/man/1/ebook
+properly	/man/1/mc
+properly	/man/10/plan9.ini
+properly	/man/10/styx
+properly	/man/2/draw-0intro
+properly	/man/2/math-0intro
+properly	/man/2/prefab-0intro
+properly	/man/6/colour
+properly	/man/6/utf
+properties	/man/1/0intro
+properties	/man/1/sh-file2chan
+properties	/man/1/tktester
+properties	/man/10/styxserver
+properties	/man/10/xalloc
+properties	/man/2/asn1
+properties	/man/2/math-fp
+properties	/man/2/sys-0intro
+properties	/man/2/sys-pctl
+properties	/man/3/dup
+properties	/man/3/flash
+properties	/man/3/ftl
+properties	/man/3/mpeg
+properties	/man/3/prog
+properties	/man/3/vga
+properties	/man/5/0intro
+properties	/man/5/stat
+properties	/man/9/grid
+properties	/man/9/menu
+property	/man/1/charon
+property	/man/2/draw-0intro
+property	/man/2/json
+property	/man/2/security-0intro
+property	/man/2/sys-millisec
+property	/man/2/sys-pctl
+property	/man/2/w3c-css
+property	/man/6/colour
+property	/man/6/json
+proportion	/man/2/dividers
+proportional	/man/1/acme
+proportional	/man/2/dict
+proportionally	/man/1/ebook
+proportionally	/man/2/print
+propose	/man/2/spree-gather
+proposed	/man/1/idea
+proposed	/man/1/tktester
+proposed	/man/10/acid
+proposed	/man/10/ntsrv
+proposed	/man/2/spree-gather
+proposed	/man/4/registry
+proposed	/man/5/walk
+proposed	/man/6/utf
+proposes	/man/2/spree-gather
+prospective	/man/2/wmsrv
+protect	/man/10/lock
+protect	/man/2/0intro
+protect	/man/6/plumbing
+protect	/man/7/dbsrv
+protect	/man/8/rstyxd
+protectboot	/man/3/flash
+protected	/man/1/crypt
+protected	/man/2/draw-0intro
+protected	/man/2/draw-screen
+protected	/man/3/flash
+protected	/man/6/users
+protecting	/man/10/sleep
+protecting	/man/2/sys-export
+protection	/man/1/0intro
+protection	/man/3/flash
+protects	/man/2/security-0intro
+protects	/man/8/svc
+proto	/man/1/alphabet-fs
+proto	/man/1/fs
+proto	/man/2/factotum
+proto	/man/2/fsproto
+proto	/man/3/ip
+proto	/man/4/factotum
+proto	/man/6/proto
+proto	/man/8/ai2key
+proto	/man/8/applylog
+proto	/man/8/create
+proto	/man/8/cs
+proto	/man/8/mkfs
+proto.b	/man/8/create
+protocol	/man/1/0intro
+protocol	/man/1/acme
+protocol	/man/1/bind
+protocol	/man/1/charon
+protocol	/man/1/netstat
+protocol	/man/1/sendmail
+protocol	/man/1/telnet
+protocol	/man/10/allocb
+protocol	/man/10/conf
+protocol	/man/10/dev
+protocol	/man/10/qio
+protocol	/man/10/styx
+protocol	/man/10/styxserver
+protocol	/man/2/dhcpclient
+protocol	/man/2/factotum
+protocol	/man/2/filter-slip
+protocol	/man/2/ip
+protocol	/man/2/keyring-0intro
+protocol	/man/2/keyring-auth
+protocol	/man/2/pop3
+protocol	/man/2/registries
+protocol	/man/2/secstore
+protocol	/man/2/security-0intro
+protocol	/man/2/security-auth
+protocol	/man/2/security-login
+protocol	/man/2/smtp
+protocol	/man/2/srv
+protocol	/man/2/styx
+protocol	/man/2/styxconv
+protocol	/man/2/styxflush
+protocol	/man/2/styxservers
+protocol	/man/2/sys-fauth
+protocol	/man/2/sys-fversion
+protocol	/man/2/sys-iounit
+protocol	/man/2/sys-pipe
+protocol	/man/2/tftp
+protocol	/man/3/0intro
+protocol	/man/3/dbg
+protocol	/man/3/draw
+protocol	/man/3/ip
+protocol	/man/3/logfs
+protocol	/man/3/mnt
+protocol	/man/3/pbus
+protocol	/man/3/plap
+protocol	/man/3/srv9
+protocol	/man/3/ssl
+protocol	/man/3/tls
+protocol	/man/4/export
+protocol	/man/4/factotum
+protocol	/man/4/ftpfs
+protocol	/man/4/import
+protocol	/man/4/iostats
+protocol	/man/4/keyfs
+protocol	/man/4/kfs
+protocol	/man/4/palmsrv
+protocol	/man/5/0intro
+protocol	/man/5/attach
+protocol	/man/5/version
+protocol	/man/5/walk
+protocol	/man/6/auth
+protocol	/man/6/keytext
+protocol	/man/6/login
+protocol	/man/6/ndb
+protocol	/man/8/ai2key
+protocol	/man/8/bootpd
+protocol	/man/8/cs
+protocol	/man/8/dhcp
+protocol	/man/8/httpd
+protocol	/man/8/logind
+protocol	/man/8/mangaload
+protocol	/man/8/rip
+protocol	/man/8/sntp
+protocol	/man/8/styxchat
+protocol's	/man/2/styxservers
+protocol's	/man/5/attach
+protocols	/man/1/charon
+protocols	/man/10/allocb
+protocols	/man/10/conf
+protocols	/man/2/factotum
+protocols	/man/2/keyring-getmsg
+protocols	/man/2/msgio
+protocols	/man/2/rfc822
+protocols	/man/2/security-0intro
+protocols	/man/2/sys-open
+protocols	/man/3/ip
+protocols	/man/3/plap
+protocols	/man/3/tls
+protocols	/man/4/factotum
+protocols	/man/5/version
+protocols	/man/6/ndb
+protofile	/man/1/alphabet-fs
+protofile	/man/1/fs
+prototype	/man/1/yacc
+prototype	/man/10/2c
+prototype	/man/2/fsproto
+prototype	/man/6/proto
+prototype	/man/8/applylog
+prototype	/man/8/create
+prototype	/man/8/mkfs
+prototypical	/man/2/styxflush
+prototypical	/man/5/0intro
+protrude	/man/9/options
+prove	/man/2/security-0intro
+prove	/man/4/factotum
+proven	/man/5/0intro
+proves	/man/2/keyring-0intro
+provide	/man/1/0intro
+provide	/man/1/charon
+provide	/man/1/collab-clients
+provide	/man/1/grid-query
+provide	/man/1/logon
+provide	/man/1/mash
+provide	/man/1/mash-make
+provide	/man/1/sh-alphabet
+provide	/man/1/sh-expr
+provide	/man/1/wm
+provide	/man/1/wm-misc
+provide	/man/1/wm-sh
+provide	/man/1/yacc
+provide	/man/10/0intro
+provide	/man/10/allocb
+provide	/man/10/ar
+provide	/man/10/dev
+provide	/man/10/devattach
+provide	/man/10/plan9.ini
+provide	/man/10/qio
+provide	/man/10/sleep
+provide	/man/10/splhi
+provide	/man/2/0intro
+provide	/man/2/alphabet-intro
+provide	/man/2/csv
+provide	/man/2/dhcpclient
+provide	/man/2/draw-0intro
+provide	/man/2/draw-context
+provide	/man/2/format
+provide	/man/2/hash
+provide	/man/2/ida
+provide	/man/2/keyring-0intro
+provide	/man/2/keyring-certtostr
+provide	/man/2/keyring-getstring
+provide	/man/2/math-0intro
+provide	/man/2/math-fp
+provide	/man/2/math-linalg
+provide	/man/2/msgio
+provide	/man/2/prefab-environ
+provide	/man/2/registries
+provide	/man/2/secstore
+provide	/man/2/security-0intro
+provide	/man/2/sexprs
+provide	/man/2/spki
+provide	/man/2/styxservers
+provide	/man/2/timers
+provide	/man/2/wmlib
+provide	/man/3/audio
+provide	/man/3/dbg
+provide	/man/3/ip
+provide	/man/3/kprof
+provide	/man/3/logfs
+provide	/man/3/pbus
+provide	/man/3/touch
+provide	/man/3/usb
+provide	/man/4/0intro
+provide	/man/5/0intro
+provide	/man/6/sexprs
+provide	/man/8/0intro
+provide	/man/8/logind
+provide	/man/8/styxchat
+provide	/man/8/svc
+provide	/man/9/1copyright
+provide	/man/9/canvas
+provide	/man/9/text
+provided	/man/1/9win
+provided	/man/1/alphabet-abc
+provided	/man/1/alphabet-fs
+provided	/man/1/alphabet-grid
+provided	/man/1/alphabet-main
+provided	/man/1/deb
+provided	/man/1/du
+provided	/man/1/emu
+provided	/man/1/fc
+provided	/man/1/fs
+provided	/man/1/grid-ns
+provided	/man/1/listen
+provided	/man/1/mash-tk
+provided	/man/1/mux
+provided	/man/1/sh-csv
+provided	/man/1/sh-file2chan
+provided	/man/1/sh-sexprs
+provided	/man/1/sh-string
+provided	/man/1/stream
+provided	/man/1/tiny
+provided	/man/1/wm
+provided	/man/1/yacc
+provided	/man/10/0intro
+provided	/man/10/c2l
+provided	/man/10/conf
+provided	/man/10/dev
+provided	/man/10/devattach
+provided	/man/10/dynld
+provided	/man/10/plan9.ini
+provided	/man/10/sleep
+provided	/man/10/styxserver
+provided	/man/2/0intro
+provided	/man/2/command
+provided	/man/2/debug
+provided	/man/2/dhcpclient
+provided	/man/2/draw-context
+provided	/man/2/encoding
+provided	/man/2/factotum
+provided	/man/2/filter-deflate
+provided	/man/2/format
+provided	/man/2/geodesy
+provided	/man/2/ida
+provided	/man/2/ip
+provided	/man/2/keyring-0intro
+provided	/man/2/keyring-crypt
+provided	/man/2/keyring-getstring
+provided	/man/2/keyring-sha1
+provided	/man/2/msgio
+provided	/man/2/palmfile
+provided	/man/2/prefab-element
+provided	/man/2/prof
+provided	/man/2/security-0intro
+provided	/man/2/security-login
+provided	/man/2/spree-allow
+provided	/man/2/styx
+provided	/man/2/styxservers
+provided	/man/2/sys-0intro
+provided	/man/2/sys-stat
+provided	/man/2/tkclient
+provided	/man/2/wmclient
+provided	/man/3/0intro
+provided	/man/3/cap
+provided	/man/3/cmd
+provided	/man/3/dbg
+provided	/man/3/dynld
+provided	/man/3/ftl
+provided	/man/3/ip
+provided	/man/3/mnt
+provided	/man/3/pnp
+provided	/man/3/tinyfs
+provided	/man/3/usb
+provided	/man/3/vga
+provided	/man/4/archfs
+provided	/man/4/dbfs
+provided	/man/4/grid-cpu
+provided	/man/4/import
+provided	/man/4/kfs
+provided	/man/4/lockfs
+provided	/man/4/namespace
+provided	/man/4/ramfile
+provided	/man/6/keys
+provided	/man/6/plumbing
+provided	/man/8/ai2key
+provided	/man/8/applylog
+provided	/man/8/dhcp
+provided	/man/8/ftl
+provided	/man/8/httpd
+provided	/man/8/rdbgsrv
+provided	/man/8/styxchat
+provided	/man/8/touchcal
+provided	/man/9/1copyright
+provided	/man/9/bind
+provided	/man/9/choicebutton
+provided	/man/9/grid
+provided	/man/9/options
+provider	/man/1/mux
+provider	/man/2/registries
+provider's	/man/2/keyring-0intro
+provider's	/man/2/security-0intro
+provides	/man/1/0intro
+provides	/man/1/acme
+provides	/man/1/charon
+provides	/man/1/collab-clients
+provides	/man/1/deb
+provides	/man/1/ebook
+provides	/man/1/emu
+provides	/man/1/grid-ns
+provides	/man/1/keyboard
+provides	/man/1/mash
+provides	/man/1/mash-tk
+provides	/man/1/miniterm
+provides	/man/1/sh-alphabet
+provides	/man/1/sh-csv
+provides	/man/1/sh-file2chan
+provides	/man/1/sh-regex
+provides	/man/1/sh-sexprs
+provides	/man/1/sh-std
+provides	/man/1/sh-string
+provides	/man/1/sh-test
+provides	/man/1/sh-tk
+provides	/man/1/tiny
+provides	/man/1/toolbar
+provides	/man/1/wish
+provides	/man/1/wm
+provides	/man/1/wm-misc
+provides	/man/10/0intro
+provides	/man/10/allocb
+provides	/man/10/conf
+provides	/man/10/delay
+provides	/man/10/devattach
+provides	/man/10/dynld
+provides	/man/10/inb
+provides	/man/10/qio
+provides	/man/10/styxserver
+provides	/man/2/0intro
+provides	/man/2/alphabet-intro
+provides	/man/2/asn1
+provides	/man/2/bufio
+provides	/man/2/command
+provides	/man/2/convcs
+provides	/man/2/crc
+provides	/man/2/csv
+provides	/man/2/debug
+provides	/man/2/devpointer
+provides	/man/2/dhcpclient
+provides	/man/2/dialog
+provides	/man/2/dict
+provides	/man/2/disks
+provides	/man/2/draw-0intro
+provides	/man/2/draw-context
+provides	/man/2/draw-image
+provides	/man/2/draw-screen
+provides	/man/2/env
+provides	/man/2/ether
+provides	/man/2/exception
+provides	/man/2/filter-slip
+provides	/man/2/format
+provides	/man/2/fsproto
+provides	/man/2/geodesy
+provides	/man/2/hash
+provides	/man/2/ida
+provides	/man/2/ip
+provides	/man/2/ir
+provides	/man/2/itslib
+provides	/man/2/json
+provides	/man/2/keyring-crypt
+provides	/man/2/keyring-ipint
+provides	/man/2/lists
+provides	/man/2/lock
+provides	/man/2/math-linalg
+provides	/man/2/mpeg
+provides	/man/2/msgio
+provides	/man/2/names
+provides	/man/2/palmfile
+provides	/man/2/plumbmsg
+provides	/man/2/pop3
+provides	/man/2/prefab-0intro
+provides	/man/2/prefab-element
+provides	/man/2/print
+provides	/man/2/prof
+provides	/man/2/readdir
+provides	/man/2/registries
+provides	/man/2/rfc822
+provides	/man/2/scsiio
+provides	/man/2/security-0intro
+provides	/man/2/security-ssl
+provides	/man/2/selectfile
+provides	/man/2/sets
+provides	/man/2/sexprs
+provides	/man/2/smtp
+provides	/man/2/spki
+provides	/man/2/spki-verifier
+provides	/man/2/spree
+provides	/man/2/spree-cardlib
+provides	/man/2/spree-gather
+provides	/man/2/spree-objstore
+provides	/man/2/srv
+provides	/man/2/stringinttab
+provides	/man/2/styx
+provides	/man/2/styxservers
+provides	/man/2/styxservers-nametree
+provides	/man/2/sys-0intro
+provides	/man/2/sys-dirread
+provides	/man/2/sys-fauth
+provides	/man/2/timers
+provides	/man/2/tk
+provides	/man/2/tkclient
+provides	/man/2/translate
+provides	/man/2/ubfa
+provides	/man/2/venti
+provides	/man/2/virgil
+provides	/man/2/volume
+provides	/man/2/w3c-uris
+provides	/man/2/wait
+provides	/man/2/wmclient
+provides	/man/2/wmlib
+provides	/man/2/wmsrv
+provides	/man/2/xml
+provides	/man/3/cmd
+provides	/man/3/cons
+provides	/man/3/draw
+provides	/man/3/ds
+provides	/man/3/flash
+provides	/man/3/fpga
+provides	/man/3/fs
+provides	/man/3/ftl
+provides	/man/3/i82365
+provides	/man/3/ip
+provides	/man/3/kprof
+provides	/man/3/lpt
+provides	/man/3/pipe
+provides	/man/3/pnp
+provides	/man/3/prof
+provides	/man/3/prog
+provides	/man/3/root
+provides	/man/3/rtc
+provides	/man/3/ssl
+provides	/man/3/tinyfs
+provides	/man/3/touch
+provides	/man/3/usb
+provides	/man/4/factotum
+provides	/man/4/grid-cpu
+provides	/man/4/import
+provides	/man/4/lockfs
+provides	/man/4/registry
+provides	/man/5/0intro
+provides	/man/6/ndb
+provides	/man/6/proto
+provides	/man/6/sbl
+provides	/man/6/scancode
+provides	/man/6/sexprs
+provides	/man/6/translate
+provides	/man/6/ubfa
+provides	/man/6/utf
+provides	/man/8/collabsrv
+provides	/man/8/plumber
+provides	/man/8/rstyxd
+provides	/man/9/0intro
+provides	/man/9/canvas
+provides	/man/9/menu
+provides	/man/9/scrollbar
+providing	/man/1/man
+providing	/man/1/sh-alphabet
+providing	/man/1/wm
+providing	/man/1/yacc
+providing	/man/2/diskblocks
+providing	/man/2/filter-slip
+providing	/man/2/format
+providing	/man/2/prefab-element
+providing	/man/2/sys-0intro
+providing	/man/3/dbg
+providing	/man/3/draw
+providing	/man/3/tls
+providing	/man/4/factotum
+providing	/man/4/mntgen
+providing	/man/5/0intro
+providing	/man/5/stat
+providing	/man/8/0intro
+providing	/man/8/svc
+provision	/man/1/mash-tk
+provision	/man/10/ar
+proxies	/man/2/alphabet-intro
+proxy	/man/1/charon
+proxy	/man/1/webgrab
+proxy	/man/2/alphabet-intro
+proxy	/man/2/dhcpclient
+proxy	/man/2/factotum
+proxy	/man/2/styxpersist
+proxy	/man/6/ndb
+prudent	/man/2/command
+ps	/man/1/kill
+ps	/man/1/ps
+ps	/man/1/stack
+ps	/man/1/time
+ps	/man/2/print
+ps	/man/2/rfc822
+ps.b	/man/1/ps
+ps2	/man/10/plan9.ini
+ps2intellimouse	/man/10/plan9.ini
+pseudo	/man/2/keyring-rc4
+pseudo	/man/2/popup
+pseudo	/man/2/rand
+pseudo	/man/2/tabs
+pseudo	/man/2/w3c-css
+pseudo	/man/3/cons
+pseudofn	/man/2/w3c-css
+pseudovariables	/man/8/prep
+pslib	/man/2/pslib
+pslib.b	/man/2/pslib
+pslib.m	/man/2/pslib
+psr	/man/3/lpt
+ptr	/man/1/wm
+ptr	/man/2/attrdb
+ptr	/man/2/devpointer
+ptr	/man/2/draw-context
+ptr	/man/2/tkclient
+ptr	/man/2/wmclient
+ptr	/man/2/wmlib
+ptr	/man/2/wmsrv
+ptr	/man/8/dns
+ptr2bytes	/man/2/devpointer
+ptype	/man/2/disks
+ptype	/man/2/print
+ptype.cfg	/man/2/print
+publication	/man/1/ebook
+publication	/man/2/draw-image
+publicattrs	/man/2/factotum
+publicid	/man/2/xml
+publicly	/man/10/0intro
+publicly	/man/2/keyring-0intro
+publicly	/man/2/security-0intro
+publicscreen	/man/2/draw-display
+publicscreen	/man/3/draw
+publicscreen:fn	/man/2/draw-display
+publish	/man/2/draw-image
+published	/man/1/charon
+published	/man/2/draw-display
+published	/man/2/draw-image
+published	/man/2/security-0intro
+pull	/man/2/spki-verifier
+pullblock	/man/10/allocb
+pulldown	/man/1/mash-tk
+pulls	/man/2/draw-image
+pullupblock	/man/10/allocb
+pulse	/man/1/miniterm
+punctuate	/man/10/qio
+punctuation	/man/2/sexprs
+punctuation	/man/2/w3c-css
+punctuation	/man/6/man
+purely	/man/1/acme
+purely	/man/1/uuencode
+purely	/man/2/sexprs
+purge	/man/5/flush
+purple	/man/9/types
+purpleblue	/man/2/draw-display
+purpose	/man/1/charon
+purpose	/man/1/m4
+purpose	/man/1/sh
+purpose	/man/1/tiny
+purpose	/man/10/iar
+purpose	/man/2/msgio
+purpose	/man/9/1copyright
+purpose	/man/9/frame
+purpose	/man/9/text
+purposes	/man/1/crypt
+purposes	/man/1/mux
+purposes	/man/1/tktester
+purposes	/man/10/plan9.ini
+purposes	/man/2/draw-image
+purposes	/man/2/itslib
+purposes	/man/2/math-linalg
+purposes	/man/2/prefab-element
+purposes	/man/2/wmsrv
+purposes	/man/3/cons
+purposes	/man/3/draw
+purposes	/man/3/logfs
+purposes	/man/3/pipe
+purposes	/man/3/sd
+purposes	/man/6/colour
+purposes	/man/9/canvas
+purposes	/man/9/text
+purview	/man/4/iostats
+push	/man/1/alphabet-main
+push	/man/1/fc
+push	/man/1/sh-expr
+push	/man/2/dialog
+push	/man/2/sh
+push	/man/6/ubfa
+pushbutton	/man/1/mash-tk
+pushbuttons	/man/1/mash-tk
+pushed	/man/1/fc
+pushed	/man/1/m4
+pushed	/man/1/sh-expr
+pushed	/man/2/dialog
+pushed	/man/2/sh
+pushed	/man/6/ubfa
+pushes	/man/1/sh-expr
+pushes	/man/2/security-ssl
+pushing	/man/10/error
+put2	/man/2/palmfile
+put3	/man/2/palmfile
+put4	/man/2/palmfile
+putall	/man/1/acme
+putb	/man/2/bufio
+putbytearray	/man/2/keyring-getstring
+putbytearray	/man/2/msgio
+putc	/man/2/bufio
+puterror	/man/2/keyring-getstring
+puterror	/man/2/msgio
+putimage	/man/2/tk
+putimage	/man/9/panel
+putint	/man/2/dhcpclient
+putips	/man/2/dhcpclient
+putrdata	/man/1/sh-file2chan
+puts	/man/1/mash-tk
+puts	/man/1/sh
+puts	/man/10/c2l
+puts	/man/10/devattach
+puts	/man/10/kbdputc
+puts	/man/10/plan9.ini
+puts	/man/10/qio
+puts	/man/2/bufio
+puts	/man/2/debug
+puts	/man/2/dhcpclient
+puts	/man/2/drawmux
+puts	/man/2/palmfile
+puts	/man/3/pbus
+puts	/man/4/factotum
+puts	/man/4/ramfile
+putstring	/man/2/keyring-getstring
+putstring	/man/2/msgio
+puttar	/man/1/gettar
+puttar.b	/man/1/gettar
+putting	/man/1/sh-regex
+putting	/man/10/c2l
+putting	/man/3/cons
+putting	/man/8/create
+putting	/man/8/svc
+puzzle	/man/1/math-misc
+pwd	/man/1/acme
+pwd	/man/1/basename
+pwd	/man/1/cd
+pwd	/man/1/cleanname
+pwd	/man/1/plumb
+pwd	/man/1/pwd
+pwd	/man/1/wm-sh
+pwd.b	/man/1/pwd
+pwfile	/man/8/changelogin
+pwrite	/man/1/cp
+pwrite	/man/2/sys-0intro
+pwrite	/man/2/sys-read
+pxe	/man/10/9load
+q.v	/man/1/acme
+q.v	/man/2/tk
+q.v	/man/3/draw
+q.v	/man/4/acme
+q.v	/man/8/prep
+qa	/man/10/2a
+qbread	/man/10/qio
+qbwrite	/man/10/qio
+qc	/man/10/2c
+qcanread	/man/10/qio
+qclose	/man/10/qio
+qconsume	/man/10/qio
+qcopy	/man/10/qio
+qctl	/man/2/styxservers-nametree
+qdata	/man/2/styxservers-nametree
+qdiscard	/man/10/qio
+qflush	/man/10/qio
+qfree	/man/10/qio
+qfull	/man/10/qio
+qget	/man/10/qio
+qhangup	/man/10/qio
+qid	/man/1/ls
+qid	/man/10/devattach
+qid	/man/10/newchan
+qid	/man/10/styx
+qid	/man/10/styxserver
+qid	/man/2/styx
+qid	/man/2/styxservers
+qid	/man/2/styxservers-nametree
+qid	/man/2/sys-stat
+qid	/man/3/prog
+qid	/man/5/0intro
+qid	/man/5/attach
+qid	/man/5/open
+qid	/man/5/walk
+qid	/man/8/collabsrv
+qid	/man/8/styxchat
+qid.path	/man/10/dev
+qid.path	/man/10/devattach
+qid.path	/man/10/newchan
+qid.path	/man/10/styxserver
+qid.path	/man/2/styxservers
+qid.path	/man/2/styxservers-nametree
+qid.path	/man/5/stat
+qid.qtype	/man/2/styxservers
+qid.type	/man/10/devattach
+qid.type	/man/5/stat
+qid.vers	/man/4/registry
+qid.vers	/man/5/stat
+qid2text	/man/2/styx
+qidgen	/man/10/styxserver
+qids	/man/10/devattach
+qids	/man/2/styx
+qids	/man/2/styxservers-nametree
+qids	/man/5/0intro
+qids	/man/5/walk
+qio	/man/10/allocb
+qio	/man/10/dev
+qio	/man/10/devattach
+qio	/man/10/intrenable
+qio	/man/10/kbdputc
+qio	/man/10/lock
+qio	/man/10/qio
+qio	/man/10/sleep
+qio	/man/10/splhi
+qio	/man/3/pipe
+qio.c	/man/10/allocb
+qio.c	/man/10/qio
+qiwrite	/man/10/qio
+ql	/man/10/2l
+qlen	/man/10/qio
+qlock	/man/10/error
+qlock	/man/10/lock
+qlock	/man/10/qlock
+qlock	/man/10/sleep
+qlock.c	/man/10/qlock
+qname	/man/1/sh-alphabet
+qname	/man/2/w3c-xpointers
+qnoblock	/man/10/kbdputc
+qnoblock	/man/10/qio
+qopen	/man/10/kbdputc
+qopen	/man/10/qio
+qpass	/man/10/qio
+qpassnolim	/man/10/qio
+qproduce	/man/10/qio
+qread	/man/10/qio
+qreopen	/man/10/qio
+qroot	/man/10/styxserver
+qroot	/man/2/styxservers-nametree
+qsetlimit	/man/10/qio
+qstring	/man/2/rfc822
+qtappend	/man/2/sys-stat
+qtappend	/man/5/0intro
+qtappend	/man/8/styxchat
+qtauth	/man/2/styxservers
+qtauth	/man/2/sys-stat
+qtauth	/man/5/0intro
+qtauth	/man/5/attach
+qtauth	/man/8/styxchat
+qtdir	/man/10/devattach
+qtdir	/man/2/styxservers
+qtdir	/man/2/styxservers-nametree
+qtdir	/man/2/sys-stat
+qtdir	/man/5/0intro
+qtdir	/man/8/styxchat
+qtexcl	/man/2/sys-stat
+qtexcl	/man/5/0intro
+qtexcl	/man/8/styxchat
+qtfile	/man/2/styxservers-nametree
+qtfile	/man/2/sys-stat
+qtfile	/man/5/0intro
+qtmount	/man/10/devattach
+qtype	/man/2/styxservers
+qtype	/man/2/sys-stat
+quadratic	/man/1/ar
+quadratic	/man/10/iar
+quadratic	/man/2/draw-image
+qualified	/man/1/acme
+qualified	/man/1/sh-alphabet
+qualified	/man/10/acid
+qualified	/man/2/translate
+qualified	/man/3/dynld
+qualified	/man/4/acme
+qualified	/man/6/dis
+qualified	/man/6/sbl
+qualified	/man/8/bootpd
+qualifier	/man/2/0intro
+qualifier	/man/2/w3c-css
+qualifiers	/man/2/w3c-css
+qualifiers	/man/3/audio
+qualifies	/man/6/translate
+qualify	/man/10/intrenable
+qualify	/man/2/0intro
+qualify	/man/6/ndb
+qualifying	/man/1/sh
+quality	/man/2/rand
+quality	/man/2/rfc822
+quantisation	/man/1/du
+quantised	/man/1/du
+quantities	/man/1/units
+quantities	/man/10/styx
+quantities	/man/3/cons
+quantity	/man/1/units
+quantity	/man/6/login
+quantum	/man/10/a.out
+quarter	/man/2/draw-example
+queen	/man/6/keyboard
+queried	/man/1/alphabet-fs
+queried	/man/1/fs
+queried	/man/1/sh-alphabet
+queried	/man/3/prog
+queried	/man/9/menu
+queried	/man/9/variable
+queries	/man/2/bloomfilter
+queries	/man/2/styxservers
+queries	/man/4/registry
+queries	/man/7/cddb
+queries	/man/8/cs
+queries	/man/8/dns
+queries	/man/8/httpd
+queries	/man/9/variable
+query	/man/1/alphabet-fs
+query	/man/1/fs
+query	/man/1/grid-query
+query	/man/1/grid-session
+query	/man/2/filepat
+query	/man/2/sh
+query	/man/2/w3c-uris
+query	/man/3/prog
+query	/man/4/factotum
+query	/man/4/grid-cpu
+query	/man/7/cddb
+query	/man/8/cs
+query	/man/8/dns
+query	/man/9/button
+query	/man/9/canvas
+query	/man/9/checkbutton
+query	/man/9/choicebutton
+query	/man/9/entry
+query	/man/9/focus
+query	/man/9/frame
+query	/man/9/image
+query	/man/9/label
+query	/man/9/listbox
+query	/man/9/menu
+query	/man/9/menubutton
+query	/man/9/panel
+query	/man/9/radiobutton
+query	/man/9/scale
+query	/man/9/scrollbar
+query	/man/9/text
+query.b	/man/1/grid-query
+query.b	/man/1/grid-session
+querying	/man/2/styxservers
+questionable	/man/2/rand
+questions	/man/1/collab-clients
+queue	/man/10/allocb
+queue	/man/10/kbdputc
+queue	/man/10/qio
+queue	/man/10/qlock
+queue	/man/2/timers
+queue	/man/3/eia
+queue	/man/3/ip
+queue	/man/3/pbus
+queue's	/man/10/qio
+queued	/man/10/qio
+queued	/man/10/qlock
+queued	/man/3/ether
+queued	/man/3/pipe
+queued	/man/9/send
+queueing	/man/10/qlock
+queueing	/man/3/dbg
+queues	/man/10/kbdputc
+queues	/man/10/qio
+queues	/man/3/eia
+queues	/man/3/pipe
+queuing	/man/2/wmlib
+quick	/man/1/ar
+quick	/man/10/iar
+quick	/man/4/namespace
+quickly	/man/1/charon
+quickly	/man/8/mangaload
+quiet	/man/2/math-fp
+quiet	/man/3/audio
+quiet	/man/8/bootpd
+quiet	/man/8/kfscmd
+quietly	/man/1/bind
+quietly	/man/1/read
+quietly	/man/3/pbus
+quit	/man/1/p
+quit	/man/1/wm-misc
+quit	/man/2/styxservers-nametree
+quit	/man/8/prep
+quits	/man/10/print
+quiz	/man/1/collab-clients
+qunlock	/man/10/error
+qunlock	/man/10/qlock
+quota	/man/10/allocb
+quotable	/man/2/rfc822
+quotation	/man/1/mash
+quotation	/man/1/sh
+quote	/man/1/m4
+quote	/man/1/mash
+quote	/man/1/sh
+quote	/man/1/sh-csv
+quote	/man/1/sh-file2chan
+quote	/man/1/sh-regex
+quote	/man/1/tiny
+quote	/man/1/wm-sh
+quote	/man/2/attrdb
+quote	/man/2/csv
+quote	/man/2/rfc822
+quote	/man/2/string
+quote	/man/2/tk
+quote	/man/4/registry
+quote	/man/6/attrdb
+quote	/man/6/man
+quote	/man/6/plumbing
+quote	/man/6/sexprs
+quote	/man/9/0intro
+quoted	/man/1/bind
+quoted	/man/1/brutus
+quoted	/man/1/filename
+quoted	/man/1/fs
+quoted	/man/1/m4
+quoted	/man/1/mash
+quoted	/man/1/mash-tk
+quoted	/man/1/mk
+quoted	/man/1/sh
+quoted	/man/1/sh-alphabet
+quoted	/man/1/sh-arg
+quoted	/man/1/sh-csv
+quoted	/man/1/sh-expr
+quoted	/man/1/sh-std
+quoted	/man/1/tiny
+quoted	/man/1/tr
+quoted	/man/1/wm-sh
+quoted	/man/10/getfields
+quoted	/man/10/mk
+quoted	/man/10/parsecmd
+quoted	/man/2/cfg
+quoted	/man/2/csv
+quoted	/man/2/draw-context
+quoted	/man/2/factotum
+quoted	/man/2/rfc822
+quoted	/man/2/string
+quoted	/man/2/sys-print
+quoted	/man/2/tk
+quoted	/man/2/tkclient
+quoted	/man/2/wmclient
+quoted	/man/2/wmsrv
+quoted	/man/3/cmd
+quoted	/man/4/factotum
+quoted	/man/4/registry
+quoted	/man/6/attrdb
+quoted	/man/6/sexprs
+quoted	/man/6/translate
+quoted	/man/8/kfscmd
+quoted	/man/9/bind
+quotes	/man/1/acme
+quotes	/man/1/idea
+quotes	/man/1/m4
+quotes	/man/1/mash
+quotes	/man/1/mash-make
+quotes	/man/1/mk
+quotes	/man/1/sh
+quotes	/man/1/sh-csv
+quotes	/man/1/tiny
+quotes	/man/10/2c
+quotes	/man/10/c2l
+quotes	/man/10/getfields
+quotes	/man/10/mk
+quotes	/man/2/cfg
+quotes	/man/2/csv
+quotes	/man/2/rfc822
+quotes	/man/2/string
+quotes	/man/2/sys-print
+quotes	/man/4/registry
+quotes	/man/6/attrdb
+quotes	/man/6/man
+quotes	/man/6/plumbing
+quotes	/man/6/sexprs
+quoting	/man/1/fs
+quoting	/man/1/sh-alphabet
+quoting	/man/1/sh-expr
+quoting	/man/1/tiny
+quoting	/man/1/wm-sh
+quoting	/man/2/cfg
+quoting	/man/2/sexprs
+quoting	/man/3/cmd
+quoting	/man/6/sexprs
+quoting	/man/8/rstyxd
+quoting	/man/8/styxchat
+quoting	/man/9/bind
+quoting	/man/9/send
+qwindow	/man/10/qio
+qwrite	/man/10/qio
+r.max.x	/man/6/image
+r.max.y	/man/6/image
+r.min	/man/2/prefab-element
+r.min	/man/3/draw
+r.min.x	/man/6/image
+r.min.y	/man/6/image
+r31,0x0	/man/10/acid
+r5g6b5	/man/2/draw-display
+r8g8b8	/man/6/image
+rab	/man/4/dossrv
+rab	/man/4/memfs
+rabin	/man/2/ida
+rabin's	/man/2/ida
+race	/man/5/open
+rad	/man/1/fc
+raddr	/man/2/dial
+radians	/man/1/calc
+radians	/man/1/fc
+radians	/man/2/geodesy
+radians	/man/2/math-elem
+radio	/man/1/collab-clients
+radio	/man/9/menu
+radiobutton	/man/9/0intro
+radiobutton	/man/9/button
+radiobutton	/man/9/checkbutton
+radiobutton	/man/9/choicebutton
+radiobutton	/man/9/menu
+radiobutton	/man/9/radiobutton
+radiobutton	/man/9/send
+radiobutton	/man/9/variable
+radiobutton's	/man/9/radiobutton
+radiobuttons	/man/9/radiobutton
+radius	/man/1/blur
+radix	/man/1/fc
+radix	/man/1/sh-expr
+radix	/man/2/string
+raid	/man/3/ds
+raise	/man/1/0intro
+raise	/man/1/mash
+raise	/man/1/sh
+raise	/man/1/sh-std
+raise	/man/1/sh-tk
+raise	/man/1/wm
+raise	/man/10/allocb
+raise	/man/10/dev
+raise	/man/10/devattach
+raise	/man/10/qio
+raise	/man/2/alphabet-intro
+raise	/man/2/draw-image
+raise	/man/2/sys-0intro
+raise	/man/2/tk
+raise	/man/2/wmsrv
+raise	/man/3/prog
+raise	/man/9/0intro
+raise	/man/9/canvas
+raise	/man/9/lower
+raise	/man/9/raise
+raise	/man/9/text
+raised	/man/1/charon
+raised	/man/1/sh
+raised	/man/1/sh-alphabet
+raised	/man/1/sh-arg
+raised	/man/1/sh-expr
+raised	/man/1/sh-std
+raised	/man/1/sh-tk
+raised	/man/10/qio
+raised	/man/2/0intro
+raised	/man/2/arg
+raised	/man/2/exception
+raised	/man/2/math-elem
+raised	/man/2/sh
+raised	/man/2/tk
+raised	/man/6/dis
+raised	/man/9/button
+raised	/man/9/checkbutton
+raised	/man/9/frame
+raised	/man/9/options
+raised	/man/9/radiobutton
+raised	/man/9/scrollbar
+raised	/man/9/types
+raises	/man/1/sh-alphabet
+raises	/man/1/sh-expr
+raises	/man/1/sh-std
+raises	/man/10/allocb
+raises	/man/10/devattach
+raises	/man/10/qio
+raises	/man/2/alphabet-intro
+raises	/man/2/command
+raises	/man/2/json
+raises	/man/2/math-elem
+raises	/man/2/sh
+raises	/man/9/raise
+raising	/man/1/0intro
+raising	/man/1/sh-std
+raising	/man/1/wm-misc
+raising	/man/2/alphabet-intro
+ram	/man/3/i82365
+ram	/man/3/vid
+ram	/man/4/namespace
+ram	/man/8/prep
+ramfile	/man/4/dbfs
+ramfile	/man/4/memfs
+ramfile	/man/4/ramfile
+ramfile.b	/man/4/ramfile
+rand	/man/1/calc
+rand	/man/1/sh-expr
+rand	/man/2/rand
+rand	/man/2/security-random
+rand	/man/2/spree
+rand.b	/man/2/rand
+rand.m	/man/2/rand
+random	/man/1/fortune
+random	/man/1/sh-expr
+random	/man/2/dbm
+random	/man/2/diskblocks
+random	/man/2/keyring-0intro
+random	/man/2/keyring-crypt
+random	/man/2/keyring-ipint
+random	/man/2/keyring-rc4
+random	/man/2/rand
+random	/man/2/security-0intro
+random	/man/2/security-random
+random	/man/3/cap
+random	/man/3/cons
+random	/man/3/tinyfs
+random	/man/4/acme
+random	/man/6/auth
+random	/man/6/login
+random	/man/8/register
+random	/man/8/signer
+random.b	/man/2/security-random
+randombuf	/man/2/security-random
+randomint	/man/2/security-random
+randomly	/man/1/mux
+randomly	/man/1/zeros
+randomly	/man/3/ip
+randomly	/man/8/ai2key
+range	/man/1/0intro
+range	/man/1/acme
+range	/man/1/emu
+range	/man/1/look
+range	/man/1/sh-string
+range	/man/1/tr
+range	/man/1/unicode
+range	/man/10/0intro
+range	/man/10/plan9.ini
+range	/man/10/xalloc
+range	/man/2/bloomfilter
+range	/man/2/daytime
+range	/man/2/debug
+range	/man/2/dis
+range	/man/2/draw-display
+range	/man/2/geodesy
+range	/man/2/palmfile
+range	/man/2/prof
+range	/man/2/spki
+range	/man/2/spree
+range	/man/2/spree-cardlib
+range	/man/2/string
+range	/man/2/sys-0intro
+range	/man/2/sys-dup
+range	/man/3/arch
+range	/man/3/cmd
+range	/man/3/ftl
+range	/man/3/i2c
+range	/man/3/ip
+range	/man/3/pnp
+range	/man/3/rtc
+range	/man/4/spree
+range	/man/6/colour
+range	/man/6/dis
+range	/man/6/font
+range	/man/6/regexp
+range	/man/6/sbl
+range	/man/7/db
+range	/man/9/canvas
+range	/man/9/choicebutton
+range	/man/9/entry
+range	/man/9/listbox
+range	/man/9/scale
+range	/man/9/scrollbar
+range	/man/9/text
+ranges	/man/10/plan9.ini
+ranges	/man/2/math-elem
+ranges	/man/2/prof
+ranges	/man/2/spree-cardlib
+ranges	/man/3/arch
+ranges	/man/3/flash
+ranges	/man/6/colour
+ranges	/man/9/listbox
+ranges	/man/9/text
+ranging	/man/1/sh-expr
+ranging	/man/2/draw-display
+ranging	/man/2/ip
+ranging	/man/3/tinyfs
+rank	/man/2/spree-cardlib
+rankine	/man/1/units
+ranking	/man/2/spree-cardlib
+rapidly	/man/3/logfs
+rarchive	/man/2/palmfile
+rare	/man/1/diff
+rare	/man/2/sys-fauth
+rare	/man/2/sys-fversion
+rarely	/man/1/charon
+rarely	/man/10/2l
+rarely	/man/10/master
+rarely	/man/10/plan9.ini
+rarely	/man/2/asn1
+rarely	/man/6/man
+raspberry	/man/2/regex
+rate	/man/1/acme
+rate	/man/1/auplay
+rate	/man/1/prof
+rate	/man/10/9load
+rate	/man/2/bloomfilter
+rate	/man/3/arch
+rate	/man/3/audio
+rate	/man/3/dbg
+rate	/man/3/eia
+rate	/man/3/mpeg
+rate	/man/3/prof
+rate	/man/4/iostats
+rate	/man/6/audio
+rate	/man/9/grid
+ratio	/man/1/gzip
+ratio	/man/1/units
+ratio	/man/2/draw-display
+rattach	/man/10/styx
+rattach	/man/5/0intro
+rattach	/man/5/attach
+rattach	/man/8/styxchat
+rattr	/man/2/attrdb
+rauth	/man/10/styx
+rauth	/man/2/styxservers
+rauth	/man/5/0intro
+rauth	/man/5/attach
+rauth	/man/8/styxchat
+raw	/man/1/cprof
+raw	/man/1/dd
+raw	/man/1/keyboard
+raw	/man/1/ns
+raw	/man/1/webgrab
+raw	/man/10/ms2
+raw	/man/2/asn1
+raw	/man/2/convcs
+raw	/man/2/dhcpclient
+raw	/man/2/ir
+raw	/man/2/prof
+raw	/man/2/scsiio
+raw	/man/2/spki
+raw	/man/3/cons
+raw	/man/3/flash
+raw	/man/3/fpga
+raw	/man/3/pnp
+raw	/man/3/sd
+raw	/man/3/touch
+raw	/man/6/sexprs
+raw	/man/7/cddb
+raw	/man/8/fpgaload
+raw	/man/8/ftl
+raw	/man/8/prep
+raw2iaf	/man/1/auplay
+raw2iaf.b	/man/1/auplay
+rawcmd	/man/2/scsiio
+rawdbfs	/man/1/calendar
+rawdbfs	/man/4/dbfs
+rawdbfs.b	/man/4/dbfs
+rawfd	/man/2/scsiio
+rawimage	/man/2/imagefile
+rawoff	/man/1/wm-sh
+rawoff	/man/3/cons
+rawon	/man/1/wm-sh
+rawon	/man/3/cons
+rawtab	/man/2/prof
+rblock	/man/1/sh-file2chan
+rc	/man/1/mash
+rc	/man/10/acid
+rc	/man/10/mk
+rc	/man/2/styxflush
+rc	/man/2/sys-file2chan
+rc	/man/2/wmsrv
+rc4	/man/1/alphabet-main
+rc4	/man/2/keyring-crypt
+rc4	/man/2/keyring-rc4
+rc4	/man/2/security-0intro
+rc4	/man/3/ssl
+rc4	/man/3/tls
+rc4	/man/4/import
+rc4	/man/4/keysrv
+rc4	/man/6/login
+rc4back	/man/2/keyring-rc4
+rc4setup	/man/2/keyring-rc4
+rc4skip	/man/2/keyring-rc4
+rc4state	/man/2/keyring-rc4
+rcl	/man/2/ir
+rclunk	/man/5/0intro
+rclunk	/man/5/clunk
+rclunk	/man/8/styxchat
+rcmd	/man/1/os
+rcmd	/man/1/rcmd
+rcmd	/man/8/virgild
+rcmd.b	/man/1/rcmd
+rcondbreak	/man/3/dbg
+rcreate	/man/10/styx
+rcreate	/man/5/0intro
+rcreate	/man/5/open
+rcreate	/man/8/styxchat
+rcvport	/man/2/plumbmsg
+rdata	/man/1/sh-file2chan
+rdbg	/man/8/rdbgsrv
+rdbg.h	/man/3/dbg
+rdbgsrv	/man/8/rdbgsrv
+rdbgsrv.b	/man/8/rdbgsrv
+rdelete	/man/2/palmfile
+rdirty	/man/2/palmfile
+rdonly	/man/2/disks
+rdtog	/man/3/usb
+re	/man/1/acme
+re	/man/1/miniterm
+re	/man/1/sh
+re	/man/1/sh-expr
+re	/man/1/sh-regex
+re	/man/1/tiny
+re	/man/1/wm-misc
+re	/man/1/yacc
+re	/man/2/alphabet-intro
+re	/man/2/regex
+re	/man/2/sh
+re	/man/2/styxpersist
+re	/man/3/indir
+re	/man/3/ip
+re	/man/3/vid
+re	/man/4/dbfs
+re	/man/6/man
+re	/man/9/bind
+re	/man/9/canvas
+re	/man/9/menu
+re	/man/9/options
+reach	/man/3/dbg
+reachable	/man/2/sys-0intro
+reached	/man/1/charon
+reached	/man/1/grid-register
+reached	/man/10/lock
+reached	/man/2/debug
+reached	/man/2/disks
+reached	/man/2/registries
+reached	/man/2/sys-0intro
+reached	/man/3/prog
+reached	/man/5/walk
+reached	/man/9/0intro
+reached	/man/9/text
+reaches	/man/10/allocb
+reaches	/man/3/ds
+reaches	/man/3/pipe
+reaches	/man/4/keyfs
+reaching	/man/10/qio
+reaching	/man/6/man
+reachtime	/man/2/ip
+reacquire	/man/8/dhcp
+reactivates	/man/2/ir
+read	/man/1/0intro
+read	/man/1/acme
+read	/man/1/alphabet-abc
+read	/man/1/alphabet-fs
+read	/man/1/alphabet-grid
+read	/man/1/alphabet-main
+read	/man/1/auplay
+read	/man/1/blur
+read	/man/1/calc
+read	/man/1/cd
+read	/man/1/charon
+read	/man/1/chmod
+read	/man/1/collab-clients
+read	/man/1/cook
+read	/man/1/cp
+read	/man/1/crypt
+read	/man/1/dd
+read	/man/1/deb
+read	/man/1/fs
+read	/man/1/ftest
+read	/man/1/grid-monitor
+read	/man/1/grid-register
+read	/man/1/idea
+read	/man/1/ls
+read	/man/1/mash
+read	/man/1/mash-make
+read	/man/1/math-misc
+read	/man/1/mdb
+read	/man/1/mk
+read	/man/1/passwd
+read	/man/1/read
+read	/man/1/rm
+read	/man/1/secstore
+read	/man/1/sh
+read	/man/1/sh-alphabet
+read	/man/1/sh-csv
+read	/man/1/sh-file2chan
+read	/man/1/sh-sexprs
+read	/man/1/sh-std
+read	/man/1/sort
+read	/man/1/stack
+read	/man/1/stream
+read	/man/1/sum
+read	/man/1/tee
+read	/man/1/tiny
+read	/man/1/wc
+read	/man/1/webgrab
+read	/man/1/wm-misc
+read	/man/1/xd
+read	/man/10/2l
+read	/man/10/9load
+read	/man/10/allocb
+read	/man/10/ar
+read	/man/10/dev
+read	/man/10/devattach
+read	/man/10/dmainit
+read	/man/10/dynld
+read	/man/10/kprof
+read	/man/10/mk
+read	/man/10/odbc
+read	/man/10/plan9.ini
+read	/man/10/qio
+read	/man/10/qlock
+read	/man/10/readnum
+read	/man/10/styxserver
+read	/man/2/attrdb
+read	/man/2/bufio
+read	/man/2/bufio-chanfill
+read	/man/2/csv
+read	/man/2/debug
+read	/man/2/devpointer
+read	/man/2/dial
+read	/man/2/dis
+read	/man/2/diskblocks
+read	/man/2/disks
+read	/man/2/draw-display
+read	/man/2/draw-font
+read	/man/2/draw-image
+read	/man/2/ether
+read	/man/2/format
+read	/man/2/fsproto
+read	/man/2/imagefile
+read	/man/2/ip
+read	/man/2/json
+read	/man/2/keyring-0intro
+read	/man/2/keyring-auth
+read	/man/2/keyring-sha1
+read	/man/2/keyset
+read	/man/2/newns
+read	/man/2/palmfile
+read	/man/2/plumbmsg
+read	/man/2/prefab-0intro
+read	/man/2/prefab-element
+read	/man/2/readdir
+read	/man/2/registries
+read	/man/2/rfc822
+read	/man/2/secstore
+read	/man/2/security-0intro
+read	/man/2/security-ssl
+read	/man/2/sexprs
+read	/man/2/spree
+read	/man/2/styx
+read	/man/2/styxflush
+read	/man/2/styxservers
+read	/man/2/sys-0intro
+read	/man/2/sys-dial
+read	/man/2/sys-dirread
+read	/man/2/sys-file2chan
+read	/man/2/sys-iounit
+read	/man/2/sys-open
+read	/man/2/sys-pipe
+read	/man/2/sys-read
+read	/man/2/sys-stat
+read	/man/2/tk
+read	/man/2/ubfa
+read	/man/2/venti
+read	/man/2/wait
+read	/man/3/arch
+read	/man/3/audio
+read	/man/3/cmd
+read	/man/3/cons
+read	/man/3/dbg
+read	/man/3/draw
+read	/man/3/ds
+read	/man/3/dup
+read	/man/3/dynld
+read	/man/3/eia
+read	/man/3/ether
+read	/man/3/flash
+read	/man/3/floppy
+read	/man/3/fpga
+read	/man/3/fs
+read	/man/3/ftl
+read	/man/3/gpio
+read	/man/3/i2c
+read	/man/3/ip
+read	/man/3/mnt
+read	/man/3/mpeg
+read	/man/3/pbus
+read	/man/3/pipe
+read	/man/3/plap
+read	/man/3/pnp
+read	/man/3/pointer
+read	/man/3/prof
+read	/man/3/prog
+read	/man/3/root
+read	/man/3/rtc
+read	/man/3/sd
+read	/man/3/sign
+read	/man/3/srv
+read	/man/3/ssl
+read	/man/3/switch
+read	/man/3/tls
+read	/man/3/touch
+read	/man/3/tv
+read	/man/3/usb
+read	/man/3/vid
+read	/man/4/9srvfs
+read	/man/4/acme
+read	/man/4/archfs
+read	/man/4/dbfs
+read	/man/4/factotum
+read	/man/4/iostats
+read	/man/4/keyfs
+read	/man/4/keysrv
+read	/man/4/kfs
+read	/man/4/logfile
+read	/man/4/memfs
+read	/man/4/palmsrv
+read	/man/4/ramfile
+read	/man/4/registry
+read	/man/4/spree
+read	/man/4/tarfs
+read	/man/5/0intro
+read	/man/5/attach
+read	/man/5/flush
+read	/man/5/open
+read	/man/5/read
+read	/man/5/stat
+read	/man/6/audio
+read	/man/6/font
+read	/man/6/image
+read	/man/6/json
+read	/man/6/ndb
+read	/man/6/proto
+read	/man/6/sexprs
+read	/man/6/ubfa
+read	/man/7/db
+read	/man/8/applylog
+read	/man/8/bootpd
+read	/man/8/collabsrv
+read	/man/8/create
+read	/man/8/cs
+read	/man/8/dns
+read	/man/8/kfscmd
+read	/man/8/mkfs
+read	/man/8/prep
+read	/man/8/rdbgsrv
+read	/man/8/rstyxd
+read	/man/8/signer
+read	/man/8/touchcal
+read.2	/man/10/mk
+read.b	/man/1/read
+readable	/man/1/ftest
+readable	/man/1/sh
+readable	/man/10/qio
+readable	/man/2/bufio
+readable	/man/2/keyring-0intro
+readable	/man/2/sexprs
+readable	/man/3/sign
+readable	/man/6/image
+readable	/man/6/keys
+readable	/man/8/createsignerkey
+readall	/man/2/readdir
+readauthinfo	/man/2/keyring-0intro
+readauthinfo	/man/2/keyring-auth
+readauthinfo	/man/2/security-auth
+readbytes	/man/2/styxservers
+readc	/man/2/styxflush
+readcmd	/man/1/sh-file2chan
+readdata	/man/1/sh-file2chan
+readdir	/man/1/ls
+readdir	/man/2/filepat
+readdir	/man/2/readdir
+readdir	/man/2/styxservers
+readdir	/man/2/styxservers-nametree
+readdir	/man/2/sys-0intro
+readdir	/man/2/sys-dirread
+readdir.b	/man/2/readdir
+readdir.m	/man/2/readdir
+readdir:fn	/man/2/styxservers
+reader	/man/1/ebook
+reader	/man/10/qio
+reader	/man/10/qlock
+reader	/man/2/imagefile
+reader	/man/2/sys-pipe
+reader	/man/3/pbus
+reader	/man/3/pipe
+reader	/man/4/acme
+reader	/man/4/factotum
+reader	/man/4/lockfs
+reader	/man/4/logfile
+readerror	/man/2/styx
+readers	/man/1/cp
+readers	/man/10/qio
+readers	/man/10/qlock
+readers	/man/2/imagefile
+readers	/man/2/palmfile
+readers	/man/3/pipe
+readers	/man/4/logfile
+readfile	/man/2/spree
+readfile	/man/2/spree-gather
+readgif	/man/2/imagefile
+readgif.b	/man/2/imagefile
+readgifpath	/man/2/imagefile
+readheader	/man/2/spree
+readheaders	/man/2/rfc822
+readid	/man/2/palmfile
+readimage	/man/2/draw-display
+readiness	/man/2/crc
+reading	/man/1/alphabet-main
+reading	/man/1/blur
+reading	/man/1/cat
+reading	/man/1/charon
+reading	/man/1/date
+reading	/man/1/diff
+reading	/man/1/mash
+reading	/man/1/sh
+reading	/man/1/sh-alphabet
+reading	/man/1/sleep
+reading	/man/1/wm-sh
+reading	/man/10/devattach
+reading	/man/10/dynld
+reading	/man/10/odbc
+reading	/man/10/qio
+reading	/man/10/qlock
+reading	/man/2/bufio
+reading	/man/2/daytime
+reading	/man/2/dbm
+reading	/man/2/dial
+reading	/man/2/diskblocks
+reading	/man/2/disks
+reading	/man/2/draw-font
+reading	/man/2/env
+reading	/man/2/format
+reading	/man/2/imagefile
+reading	/man/2/keyring-getmsg
+reading	/man/2/math-0intro
+reading	/man/2/msgio
+reading	/man/2/palmfile
+reading	/man/2/print
+reading	/man/2/readdir
+reading	/man/2/rfc822
+reading	/man/2/security-0intro
+reading	/man/2/styx
+reading	/man/2/styxservers
+reading	/man/2/sys-bind
+reading	/man/2/sys-dial
+reading	/man/2/sys-export
+reading	/man/2/sys-file2chan
+reading	/man/2/sys-open
+reading	/man/2/sys-pipe
+reading	/man/2/wait
+reading	/man/3/cmd
+reading	/man/3/cons
+reading	/man/3/draw
+reading	/man/3/ds
+reading	/man/3/dup
+reading	/man/3/env
+reading	/man/3/ether
+reading	/man/3/i82365
+reading	/man/3/ip
+reading	/man/3/lpt
+reading	/man/3/pipe
+reading	/man/3/plap
+reading	/man/3/pnp
+reading	/man/3/pointer
+reading	/man/3/prog
+reading	/man/3/sd
+reading	/man/3/snarf
+reading	/man/3/ssl
+reading	/man/3/tls
+reading	/man/3/touch
+reading	/man/3/tv
+reading	/man/3/vga
+reading	/man/4/acme
+reading	/man/4/dbfs
+reading	/man/4/factotum
+reading	/man/4/grid-cpu
+reading	/man/4/logfile
+reading	/man/4/registry
+reading	/man/4/spree
+reading	/man/4/vacfs
+reading	/man/5/0intro
+reading	/man/5/read
+reading	/man/6/image
+reading	/man/6/ndb
+reading	/man/6/ubfa
+reading	/man/8/collabsrv
+reading	/man/8/kfscmd
+reading	/man/8/rdbgsrv
+reading	/man/8/rip
+reading	/man/8/styxchat
+readings	/man/3/touch
+readipifc	/man/2/ip
+readjpg	/man/2/imagefile
+readjpg.b	/man/2/imagefile
+readjpgpath	/man/2/imagefile
+readjson	/man/2/json
+readme	/man/1/acme
+readme	/man/1/itest
+readmsg	/man/2/styx
+readmulti	/man/2/imagefile
+readn	/man/2/disks
+readn	/man/2/sys-read
+readnum	/man/10/readnum
+readonly	/man/2/disks
+readpicfile	/man/2/imagefile
+readpicfile.b	/man/2/imagefile
+readpicpath	/man/2/imagefile
+readpixels	/man/2/draw-image
+readpng	/man/2/imagefile
+readpng.b	/man/2/imagefile
+readpngpath	/man/2/imagefile
+readprotofile	/man/2/fsproto
+readprotostring	/man/2/fsproto
+reads	/man/1/0intro
+reads	/man/1/alphabet-abc
+reads	/man/1/alphabet-fs
+reads	/man/1/alphabet-grid
+reads	/man/1/alphabet-main
+reads	/man/1/asm
+reads	/man/1/auplay
+reads	/man/1/blur
+reads	/man/1/calc
+reads	/man/1/cat
+reads	/man/1/comm
+reads	/man/1/cook
+reads	/man/1/crypt
+reads	/man/1/disdep
+reads	/man/1/freq
+reads	/man/1/fs
+reads	/man/1/gettar
+reads	/man/1/grid-monitor
+reads	/man/1/logwindow
+reads	/man/1/m4
+reads	/man/1/mash
+reads	/man/1/netkey
+reads	/man/1/nsbuild
+reads	/man/1/sendmail
+reads	/man/1/sh
+reads	/man/1/sh-csv
+reads	/man/1/sh-file2chan
+reads	/man/1/sh-sexprs
+reads	/man/1/sh-std
+reads	/man/1/sum
+reads	/man/1/tcs
+reads	/man/1/tee
+reads	/man/1/telnet
+reads	/man/1/timestamp
+reads	/man/1/tiny
+reads	/man/1/tsort
+reads	/man/1/uuencode
+reads	/man/1/webgrab
+reads	/man/1/wish
+reads	/man/10/odbc
+reads	/man/10/plan9.ini
+reads	/man/10/qio
+reads	/man/2/csv
+reads	/man/2/daytime
+reads	/man/2/devpointer
+reads	/man/2/dis
+reads	/man/2/disks
+reads	/man/2/ether
+reads	/man/2/format
+reads	/man/2/fsproto
+reads	/man/2/ir
+reads	/man/2/json
+reads	/man/2/keyring-auth
+reads	/man/2/keyring-getmsg
+reads	/man/2/keyring-getstring
+reads	/man/2/msgio
+reads	/man/2/newns
+reads	/man/2/plumbmsg
+reads	/man/2/readdir
+reads	/man/2/rfc822
+reads	/man/2/scsiio
+reads	/man/2/styx
+reads	/man/2/styxflush
+reads	/man/2/styxpersist
+reads	/man/2/sys-dirread
+reads	/man/2/sys-iounit
+reads	/man/2/sys-pipe
+reads	/man/2/sys-read
+reads	/man/2/ubfa
+reads	/man/2/wait
+reads	/man/3/arch
+reads	/man/3/audio
+reads	/man/3/cmd
+reads	/man/3/cons
+reads	/man/3/ds
+reads	/man/3/eia
+reads	/man/3/flash
+reads	/man/3/ftl
+reads	/man/3/i2c
+reads	/man/3/i82365
+reads	/man/3/ip
+reads	/man/3/lpt
+reads	/man/3/pnp
+reads	/man/3/ssl
+reads	/man/3/tls
+reads	/man/3/touch
+reads	/man/3/tv
+reads	/man/3/usb
+reads	/man/3/vid
+reads	/man/4/dbfs
+reads	/man/4/factotum
+reads	/man/4/iostats
+reads	/man/4/keyfs
+reads	/man/4/keysrv
+reads	/man/4/kfs
+reads	/man/4/registry
+reads	/man/4/spree
+reads	/man/5/0intro
+reads	/man/6/font
+reads	/man/8/changelogin
+reads	/man/8/collabsrv
+reads	/man/8/cs
+reads	/man/8/ftl
+reads	/man/8/plumber
+reads	/man/8/prep
+reads	/man/8/rstyxd
+reads	/man/8/styxchat
+readstr	/man/10/error
+readstr	/man/10/readnum
+readstr	/man/2/styxservers
+readubf	/man/2/ubfa
+readxbitmap	/man/2/imagefile
+readxbitmap.b	/man/2/imagefile
+readxbmpath	/man/2/imagefile
+ready	/man/1/secstore
+ready	/man/10/kproc
+ready	/man/2/convcs
+ready	/man/2/format
+ready	/man/2/scsiio
+ready	/man/2/styxpersist
+ready	/man/2/styxservers
+ready	/man/2/sys-bind
+ready	/man/2/tkclient
+ready	/man/3/dbg
+ready	/man/3/ip
+ready	/man/3/mnt
+ready	/man/3/prog
+ready	/man/3/tls
+real	/man/1/acme
+real	/man/1/charon
+real	/man/1/collab-clients
+real	/man/1/date
+real	/man/1/math-misc
+real	/man/1/time
+real	/man/1/yacc
+real	/man/10/0intro
+real	/man/10/c2l
+real	/man/10/kproc
+real	/man/10/splhi
+real	/man/2/asn1
+real	/man/2/convcs
+real	/man/2/dis
+real	/man/2/geodesy
+real	/man/2/hash
+real	/man/2/ir
+real	/man/2/json
+real	/man/2/math-0intro
+real	/man/2/math-elem
+real	/man/2/math-export
+real	/man/2/math-fp
+real	/man/2/math-linalg
+real	/man/2/print
+real	/man/2/string
+real	/man/2/sys-print
+real	/man/2/w3c-xpointers
+real	/man/3/cmd
+real	/man/3/fs
+real	/man/3/rtc
+real	/man/3/tinyfs
+real	/man/6/dis
+real	/man/6/sbl
+real	/man/8/collabsrv
+real	/man/8/manufacture
+real	/man/9/canvas
+real	/man/9/entry
+real	/man/9/listbox
+real	/man/9/scale
+real	/man/9/scrollbar
+real	/man/9/text
+real32	/man/2/math-export
+reality	/man/1/charon
+reality	/man/2/disks
+realloc	/man/10/malloc
+reallocating	/man/10/allocb
+reallocation	/man/2/diskblocks
+reallocation	/man/3/cmd
+reallyrandom	/man/2/security-random
+reals	/man/2/dis
+reals	/man/3/prog
+realtek	/man/10/plan9.ini
+reamed	/man/4/kfs
+reaming	/man/1/zeros
+reappear	/man/1/logwindow
+reappear	/man/1/sh
+rear	/man/2/spree-cardlib
+rearranged	/man/9/grid
+rearranges	/man/10/allocb
+rearranging	/man/2/sys-dup
+reasonable	/man/2/styx
+reasonable	/man/5/stat
+reasonable	/man/6/colour
+reasonable	/man/9/canvas
+reasonably	/man/3/dbg
+reasons	/man/1/mux
+reasons	/man/10/print
+reasons	/man/5/0intro
+reasons	/man/5/stat
+reasons	/man/6/colour
+reassigned	/man/2/sys-0intro
+reboot	/man/1/bind
+reboot	/man/10/dev
+reboot	/man/10/panic
+reboot	/man/2/sys-millisec
+reboot	/man/3/boot
+reboot	/man/3/cons
+rebooted	/man/3/cons
+rebooted	/man/3/dbg
+rebooted	/man/8/getauthinfo
+rebound	/man/2/dhcpclient
+rebound	/man/2/sys-fd2path
+rebound	/man/3/fs
+rebroadcast	/man/8/collabsrv
+rebuild	/man/2/ida
+rebuild	/man/8/kfscmd
+rebuilt	/man/10/iar
+rec	/man/2/format
+rec	/man/2/palmfile
+rec	/man/2/prof
+rec	/man/2/xml
+rec2val	/man/2/format
+recalculate	/man/2/prefab-element
+recall	/man/1/mux
+recall	/man/2/ir
+receipt	/man/10/sleep
+receipt	/man/2/filter
+receipt	/man/3/tls
+receive	/man/1/cpu
+receive	/man/1/sh-tk
+receive	/man/2/0intro
+receive	/man/2/command
+receive	/man/2/draw-display
+receive	/man/2/filter
+receive	/man/2/ip
+receive	/man/2/ir
+receive	/man/2/keyring-getmsg
+receive	/man/2/mpeg
+receive	/man/2/plumbmsg
+receive	/man/2/popup
+receive	/man/2/styx
+receive	/man/2/styxpersist
+receive	/man/2/sys-bind
+receive	/man/2/sys-file2chan
+receive	/man/2/tftp
+receive	/man/2/tkclient
+receive	/man/2/wmsrv
+receive	/man/3/cmd
+receive	/man/3/i2c
+receive	/man/3/ip
+receive	/man/4/logfile
+receive	/man/5/flush
+receive	/man/5/version
+receive	/man/8/collabsrv
+receive	/man/8/plumber
+receive	/man/9/grab
+receive	/man/9/text
+received	/man/1/emu
+received	/man/1/secstore
+received	/man/1/sh-tk
+received	/man/2/arg
+received	/man/2/daytime
+received	/man/2/dhcpclient
+received	/man/2/dial
+received	/man/2/dividers
+received	/man/2/keyring-getmsg
+received	/man/2/msgio
+received	/man/2/security-0intro
+received	/man/2/spree
+received	/man/2/styx
+received	/man/2/styxconv
+received	/man/2/styxpersist
+received	/man/2/sys-dial
+received	/man/2/sys-file2chan
+received	/man/2/tabs
+received	/man/2/tftp
+received	/man/2/tkclient
+received	/man/2/wmclient
+received	/man/2/wmlib
+received	/man/2/wmsrv
+received	/man/2/xml
+received	/man/3/ether
+received	/man/3/ip
+received	/man/3/pbus
+received	/man/3/prog
+received	/man/3/tls
+received	/man/3/touch
+received	/man/3/usb
+received	/man/5/flush
+received	/man/5/version
+received	/man/6/auth
+received	/man/8/register
+received	/man/8/sntp
+received	/man/8/styxmon
+receiver	/man/1/idea
+receiver	/man/2/security-0intro
+receiver	/man/2/styxservers
+receiver	/man/3/pbus
+receives	/man/1/acme
+receives	/man/1/collab-clients
+receives	/man/1/sh
+receives	/man/1/sh-tk
+receives	/man/1/telnet
+receives	/man/1/wm-misc
+receives	/man/10/devattach
+receives	/man/10/plan9.ini
+receives	/man/2/dhcpclient
+receives	/man/2/draw-context
+receives	/man/2/ida
+receives	/man/2/popup
+receives	/man/2/spree
+receives	/man/2/styx
+receives	/man/2/sys-export
+receives	/man/2/sys-file2chan
+receives	/man/2/tk
+receives	/man/2/virgil
+receives	/man/2/volume
+receives	/man/3/dbg
+receives	/man/3/ether
+receives	/man/3/ftl
+receives	/man/3/usb
+receives	/man/5/0intro
+receives	/man/6/login
+receives	/man/6/utf
+receives	/man/8/dhcp
+receives	/man/8/logind
+receives	/man/8/plumber
+receives	/man/8/sntp
+receives	/man/8/virgild
+receives	/man/9/scale
+receiving	/man/10/memory
+receiving	/man/2/keyring-getstring
+receiving	/man/2/msgio
+receiving	/man/2/security-0intro
+receiving	/man/2/spree
+receiving	/man/2/sys-0intro
+receiving	/man/3/ip
+receiving	/man/3/prog
+receiving	/man/5/0intro
+receiving	/man/8/bootpd
+receiving	/man/9/grid
+receiving	/man/9/pack
+recent	/man/1/acme
+recent	/man/1/ls
+recent	/man/1/mk
+recent	/man/1/sh
+recent	/man/10/error
+recent	/man/10/mk
+recent	/man/2/exception
+recent	/man/2/sys-0intro
+recent	/man/2/sys-print
+recent	/man/3/dynld
+recent	/man/3/logfs
+recent	/man/5/stat
+recent	/man/6/plumbing
+recent	/man/8/rdbgsrv
+recent	/man/9/canvas
+recent	/man/9/entry
+recent	/man/9/scrollbar
+recently	/man/1/acme
+recently	/man/1/charon
+recently	/man/1/ls
+recently	/man/2/popup
+recently	/man/2/prefab-compound
+recently	/man/2/readdir
+recently	/man/2/wmsrv
+recently	/man/4/acme
+recently	/man/5/stat
+recently	/man/6/ubfa
+recently	/man/9/grid
+receptacle	/man/10/allocb
+reception	/man/10/plan9.ini
+recipe	/man/1/mk
+recipe	/man/10/mk
+recipes	/man/1/mk
+recipes	/man/10/mk
+recipes	/man/8/cs
+recipient	/man/1/sendmail
+recipient	/man/1/uuencode
+recipient	/man/2/security-0intro
+recipient	/man/8/svc
+recipients	/man/2/smtp
+reclaim	/man/3/ftl
+reclaimed	/man/3/tls
+recognise	/man/3/vga
+recognised	/man/1/brutus
+recognised	/man/1/cook
+recognised	/man/1/fc
+recognised	/man/1/filename
+recognised	/man/1/look
+recognised	/man/1/wish
+recognised	/man/2/xml
+recognised	/man/6/ubfa
+recognises	/man/1/m4
+recognises	/man/1/sh-csv
+recognises	/man/1/sh-sexprs
+recognising	/man/3/logfs
+recognizable	/man/1/mk
+recognizable	/man/10/mk
+recognizable	/man/8/httpd
+recognize	/man/10/atoi
+recognized	/man/1/acme
+recognized	/man/1/units
+recognized	/man/10/9load
+recognized	/man/2/volume
+recognized	/man/4/acme
+recognized	/man/9/types
+recognizer	/man/2/regex
+recognizes	/man/10/atoi
+recognizes	/man/2/sys-print
+recognizes	/man/5/flush
+recommendation	/man/2/asn1
+recommended	/man/1/grid-session
+recommended	/man/10/mk
+recommended	/man/2/prof
+recommended	/man/4/grid-cpu
+recommended	/man/8/ai2key
+reconfiguration	/man/10/plan9.ini
+reconfigured	/man/9/image
+reconnection	/man/2/pop3
+reconnections	/man/2/pop3
+reconstruct	/man/1/mash-make
+reconstruct	/man/1/ns
+reconstruct	/man/2/ida
+reconstructing	/man/1/mash-make
+reconstruction	/man/2/ida
+record	/man/1/alphabet-abc
+record	/man/1/alphabet-grid
+record	/man/1/avr
+record	/man/1/dd
+record	/man/1/itest
+record	/man/1/spree-join
+record	/man/10/9load
+record	/man/10/error
+record	/man/10/ms2
+record	/man/10/odbc
+record	/man/2/cfg
+record	/man/2/convcs
+record	/man/2/disks
+record	/man/2/format
+record	/man/2/ir
+record	/man/2/keyring-getmsg
+record	/man/2/keyring-getstring
+record	/man/2/msgio
+record	/man/2/palmfile
+record	/man/2/spree-cardlib
+record	/man/2/sys-0intro
+record	/man/2/wait
+record	/man/2/wmsrv
+record	/man/3/pointer
+record	/man/3/ssl
+record	/man/3/tls
+record	/man/4/dbfs
+record	/man/6/keytext
+record	/man/8/prep
+record	/man/8/rip
+record's	/man/2/palmfile
+recorddir	/man/1/itest
+recorded	/man/1/cprof
+recorded	/man/1/itest
+recorded	/man/10/a.out
+recorded	/man/2/sys-stat
+recorded	/man/3/audio
+recorded	/man/5/read
+recorded	/man/6/audio
+recorded	/man/8/applylog
+recording	/man/1/cprof
+recording	/man/1/itest
+recording	/man/2/itslib
+recording	/man/3/audio
+recording	/man/3/kprof
+recording	/man/3/logfs
+recording	/man/6/audio
+recordroot	/man/1/itest
+records	/man/1/acme
+records	/man/1/alphabet-abc
+records	/man/1/alphabet-grid
+records	/man/1/dd
+records	/man/10/2c
+records	/man/10/odbc
+records	/man/2/format
+records	/man/2/palmfile
+records	/man/2/sys-0intro
+records	/man/2/sys-iounit
+records	/man/2/sys-werrstr
+records	/man/3/logfs
+records	/man/3/tls
+records	/man/3/usb
+records	/man/4/dbfs
+records	/man/4/iostats
+records	/man/5/read
+records	/man/5/stat
+records	/man/9/text
+recover	/man/10/a.out
+recover	/man/10/error
+recover	/man/2/math-0intro
+recover	/man/3/arch
+recover	/man/3/pbus
+recover	/man/3/prog
+recover	/man/4/acme
+recovered	/man/2/ida
+recovered	/man/3/draw
+recovered	/man/3/logfs
+recovery	/man/10/allocb
+recovery	/man/10/error
+recovery	/man/2/0intro
+recovery	/man/2/exception
+recovery	/man/2/venti
+recovery	/man/3/prog
+recovery	/man/8/rdbgsrv
+recreate	/man/1/diff
+recreate	/man/1/mash-tk
+recreate	/man/1/sh
+recreate	/man/4/acme
+recreated	/man/2/spree
+recreated	/man/5/0intro
+recreates	/man/1/uuencode
+recreation	/man/1/acme
+recroot	/man/1/itest
+recs	/man/2/format
+recsize	/man/2/palmfile
+rect	/man/1/sh-arg
+rect	/man/1/wm
+rect	/man/2/draw-0intro
+rect	/man/2/draw-display
+rect	/man/2/draw-example
+rect	/man/2/draw-image
+rect	/man/2/draw-point
+rect	/man/2/draw-rect
+rect	/man/2/draw-screen
+rect	/man/2/imagefile
+rect	/man/2/mpeg
+rect	/man/2/prefab-compound
+rect	/man/2/prefab-element
+rect	/man/2/tk
+rect	/man/2/wmclient
+rect	/man/2/wmsrv
+rectangle	/man/1/wm
+rectangle	/man/1/wm-misc
+rectangle	/man/2/draw-0intro
+rectangle	/man/2/draw-context
+rectangle	/man/2/draw-image
+rectangle	/man/2/draw-point
+rectangle	/man/2/draw-rect
+rectangle	/man/2/draw-screen
+rectangle	/man/2/imagefile
+rectangle	/man/2/mpeg
+rectangle	/man/2/prefab-compound
+rectangle	/man/2/prefab-element
+rectangle	/man/2/tk
+rectangle	/man/2/wmclient
+rectangle	/man/2/wmsrv
+rectangle	/man/3/draw
+rectangle	/man/3/vga
+rectangle	/man/6/font
+rectangle	/man/6/image
+rectangle	/man/9/canvas
+rectangle	/man/9/listbox
+rectangle	/man/9/options
+rectangle	/man/9/panel
+rectangle	/man/9/scrollbar
+rectangle	/man/9/see
+rectangle's	/man/2/draw-image
+rectangles	/man/2/draw-0intro
+rectangles	/man/2/draw-image
+rectangles	/man/2/draw-rect
+rectangles	/man/2/prefab-element
+rectangles	/man/3/draw
+rectangles	/man/9/canvas
+rectangular	/man/1/charon
+rectangular	/man/2/draw-0intro
+rectangular	/man/2/draw-example
+rectangular	/man/2/draw-image
+rectangular	/man/2/draw-rect
+rectangular	/man/2/prefab-element
+rectangular	/man/6/image
+rectangular	/man/9/canvas
+rectangular	/man/9/pack
+rectangular	/man/9/scale
+rectangular	/man/9/see
+recursion	/man/1/math-misc
+recursion	/man/2/spree
+recursive	/man/1/sh
+recursive	/man/10/lock
+recursive	/man/2/json
+recursive	/man/6/sexprs
+recursively	/man/1/alphabet-fs
+recursively	/man/1/cp
+recursively	/man/1/diff
+recursively	/man/1/disdep
+recursively	/man/1/du
+recursively	/man/1/fs
+recursively	/man/1/rm
+recursively	/man/1/vacget
+recursively	/man/10/styxserver
+recursively	/man/2/draw-0intro
+recursively	/man/2/format
+recursively	/man/2/json
+recursively	/man/2/sexprs
+recursively	/man/2/ubfa
+recursively	/man/6/proto
+recursively	/man/8/create
+recv	/man/1/sh-tk
+recv	/man/2/0intro
+recv	/man/2/plumbmsg
+recv	/man/3/prog
+recvra	/man/2/ip
+red	/man/1/charon
+red	/man/1/cprof
+red	/man/1/deb
+red	/man/1/tktester
+red	/man/1/wm-misc
+red	/man/2/draw-0intro
+red	/man/2/draw-display
+red	/man/2/draw-example
+red	/man/3/draw
+red	/man/3/tv
+red	/man/6/colour
+red	/man/6/image
+red	/man/9/types
+red,green,blue	/man/6/colour
+redefine	/man/2/sh
+redefined	/man/1/m4
+redefined	/man/1/sh
+redefined	/man/9/text
+redefining	/man/1/m4
+redefining	/man/9/menubutton
+redials	/man/2/styxpersist
+redir	/man/1/sh
+redirect	/man/1/os
+redirect	/man/1/tiny
+redirect	/man/10/acid
+redirect	/man/8/httpd
+redirected	/man/1/listen
+redirected	/man/1/m4
+redirected	/man/1/mash
+redirected	/man/1/sh
+redirected	/man/1/sh-alphabet
+redirected	/man/4/acme
+redirecting	/man/1/tiny
+redirection	/man/1/sh
+redirection	/man/1/sh-std
+redirection	/man/1/tiny
+redirection	/man/10/odbc
+redirection	/man/2/sh
+redirections	/man/1/mash
+redirections	/man/1/sh
+redirections	/man/1/webgrab
+redirects	/man/1/mash
+redirects	/man/1/sh
+redisplay	/man/9/menu
+redisplayed	/man/1/miniterm
+redisplaying	/man/9/checkbutton
+redo	/man/1/acme
+redoes	/man/1/acme
+redraw	/man/2/prefab-compound
+redrawn	/man/2/prefab-compound
+reduce	/man/1/charon
+reduce	/man/10/dev
+reduce	/man/5/0intro
+reduce	/man/6/colour
+reduce	/man/8/ftl
+reduced	/man/1/charon
+reduced	/man/1/tiny
+reduced	/man/10/a.out
+reduced	/man/10/devattach
+reduced	/man/10/qio
+reduced	/man/2/sh
+reduced	/man/3/ds
+reduced	/man/3/pipe
+reduces	/man/1/blur
+reduces	/man/10/c2l
+reduces	/man/3/logfs
+reducing	/man/2/0intro
+reductions	/man/1/yacc
+redundancy	/man/2/crc
+redundancy	/man/2/ida
+redundant	/man/1/cleanname
+redundant	/man/2/names
+redundant	/man/8/kfscmd
+reentered	/man/3/cons
+ref	/man/1/cook
+ref	/man/1/yacc
+ref	/man/10/newchan
+ref	/man/10/qio
+ref	/man/10/ref
+ref	/man/10/styxserver
+ref	/man/2/0intro
+ref	/man/2/alphabet-intro
+ref	/man/2/arg
+ref	/man/2/asn1
+ref	/man/2/attrdb
+ref	/man/2/bufio
+ref	/man/2/bufio-chanfill
+ref	/man/2/cfg
+ref	/man/2/command
+ref	/man/2/complete
+ref	/man/2/crc
+ref	/man/2/csv
+ref	/man/2/daytime
+ref	/man/2/dbm
+ref	/man/2/debug
+ref	/man/2/devpointer
+ref	/man/2/dhcpclient
+ref	/man/2/dial
+ref	/man/2/dialog
+ref	/man/2/dict
+ref	/man/2/dis
+ref	/man/2/diskblocks
+ref	/man/2/disks
+ref	/man/2/dividers
+ref	/man/2/draw-context
+ref	/man/2/draw-display
+ref	/man/2/draw-example
+ref	/man/2/draw-font
+ref	/man/2/draw-image
+ref	/man/2/draw-screen
+ref	/man/2/drawmux
+ref	/man/2/factotum
+ref	/man/2/filter
+ref	/man/2/filter-deflate
+ref	/man/2/filter-slip
+ref	/man/2/format
+ref	/man/2/fsproto
+ref	/man/2/hash
+ref	/man/2/ida
+ref	/man/2/imagefile
+ref	/man/2/ip
+ref	/man/2/ir
+ref	/man/2/itslib
+ref	/man/2/json
+ref	/man/2/keyring-0intro
+ref	/man/2/keyring-auth
+ref	/man/2/keyring-certtostr
+ref	/man/2/keyring-crypt
+ref	/man/2/keyring-gensk
+ref	/man/2/keyring-getmsg
+ref	/man/2/keyring-getstring
+ref	/man/2/keyring-ipint
+ref	/man/2/keyring-rc4
+ref	/man/2/keyring-sha1
+ref	/man/2/lists
+ref	/man/2/lock
+ref	/man/2/mpeg
+ref	/man/2/msgio
+ref	/man/2/palmfile
+ref	/man/2/plumbmsg
+ref	/man/2/popup
+ref	/man/2/prefab-compound
+ref	/man/2/prefab-element
+ref	/man/2/prefab-environ
+ref	/man/2/prefab-style
+ref	/man/2/print
+ref	/man/2/prof
+ref	/man/2/pslib
+ref	/man/2/readdir
+ref	/man/2/registries
+ref	/man/2/rfc822
+ref	/man/2/scsiio
+ref	/man/2/secstore
+ref	/man/2/security-auth
+ref	/man/2/security-login
+ref	/man/2/security-ssl
+ref	/man/2/selectfile
+ref	/man/2/sexprs
+ref	/man/2/sh
+ref	/man/2/spki
+ref	/man/2/spki-verifier
+ref	/man/2/spree
+ref	/man/2/spree-allow
+ref	/man/2/spree-cardlib
+ref	/man/2/spree-gather
+ref	/man/2/spree-objstore
+ref	/man/2/styx
+ref	/man/2/styxconv
+ref	/man/2/styxflush
+ref	/man/2/styxpersist
+ref	/man/2/styxservers
+ref	/man/2/styxservers-nametree
+ref	/man/2/sys-bind
+ref	/man/2/sys-dial
+ref	/man/2/sys-dirread
+ref	/man/2/sys-dup
+ref	/man/2/sys-export
+ref	/man/2/sys-fauth
+ref	/man/2/sys-fd2path
+ref	/man/2/sys-file2chan
+ref	/man/2/sys-fversion
+ref	/man/2/sys-iounit
+ref	/man/2/sys-open
+ref	/man/2/sys-pipe
+ref	/man/2/sys-print
+ref	/man/2/sys-read
+ref	/man/2/sys-seek
+ref	/man/2/sys-self
+ref	/man/2/sys-stat
+ref	/man/2/tabs
+ref	/man/2/tftp
+ref	/man/2/timers
+ref	/man/2/tk
+ref	/man/2/tkclient
+ref	/man/2/translate
+ref	/man/2/ubfa
+ref	/man/2/venti
+ref	/man/2/volume
+ref	/man/2/w3c-css
+ref	/man/2/w3c-uris
+ref	/man/2/w3c-xpointers
+ref	/man/2/wait
+ref	/man/2/wmclient
+ref	/man/2/wmlib
+ref	/man/2/wmsrv
+ref	/man/2/xml
+ref	/man/6/sbl
+ref	/man/7/db
+refbackup	/man/2/draw-screen
+refbackup	/man/3/draw
+refer	/man/1/bind
+refer	/man/1/mk
+refer	/man/1/ns
+refer	/man/1/sh
+refer	/man/10/dev
+refer	/man/10/devattach
+refer	/man/10/mk
+refer	/man/2/convcs
+refer	/man/2/dhcpclient
+refer	/man/2/diskblocks
+refer	/man/2/json
+refer	/man/2/prefab-style
+refer	/man/2/print
+refer	/man/2/spki
+refer	/man/2/spree
+refer	/man/2/styxservers
+refer	/man/2/sys-bind
+refer	/man/2/sys-fd2path
+refer	/man/2/ubfa
+refer	/man/3/fs
+refer	/man/3/ip
+refer	/man/5/0intro
+refer	/man/6/ndb
+refer	/man/6/plumbing
+refer	/man/8/bootpd
+refer	/man/8/collabsrv
+refer	/man/9/canvas
+refer	/man/9/entry
+refer	/man/9/grid
+refer	/man/9/image
+refer	/man/9/listbox
+refer	/man/9/scrollbar
+refer	/man/9/text
+reference	/man/1/0intro
+reference	/man/1/charon
+reference	/man/1/limbo
+reference	/man/1/man
+reference	/man/1/mk
+reference	/man/1/sh-tk
+reference	/man/1/yacc
+reference	/man/10/dev
+reference	/man/10/dynld
+reference	/man/10/kproc
+reference	/man/10/mk
+reference	/man/10/newchan
+reference	/man/10/qio
+reference	/man/10/ref
+reference	/man/2/0intro
+reference	/man/2/alphabet-intro
+reference	/man/2/asn1
+reference	/man/2/attrdb
+reference	/man/2/bufio
+reference	/man/2/complete
+reference	/man/2/convcs
+reference	/man/2/daytime
+reference	/man/2/dbm
+reference	/man/2/devpointer
+reference	/man/2/dhcpclient
+reference	/man/2/dial
+reference	/man/2/dis
+reference	/man/2/diskblocks
+reference	/man/2/disks
+reference	/man/2/draw-0intro
+reference	/man/2/draw-context
+reference	/man/2/draw-display
+reference	/man/2/factotum
+reference	/man/2/fsproto
+reference	/man/2/geodesy
+reference	/man/2/ida
+reference	/man/2/json
+reference	/man/2/keyring-0intro
+reference	/man/2/keyring-auth
+reference	/man/2/keyring-certtostr
+reference	/man/2/keyring-ipint
+reference	/man/2/lists
+reference	/man/2/lock
+reference	/man/2/palmfile
+reference	/man/2/plumbmsg
+reference	/man/2/rfc822
+reference	/man/2/scsiio
+reference	/man/2/secstore
+reference	/man/2/security-login
+reference	/man/2/security-ssl
+reference	/man/2/styx
+reference	/man/2/styxservers
+reference	/man/2/sys-0intro
+reference	/man/2/sys-dup
+reference	/man/2/sys-open
+reference	/man/2/sys-self
+reference	/man/2/translate
+reference	/man/2/w3c-css
+reference	/man/2/w3c-uris
+reference	/man/3/draw
+reference	/man/3/prog
+reference	/man/3/touch
+reference	/man/6/proto
+reference	/man/6/sbl
+reference	/man/7/db
+reference	/man/8/prep
+reference	/man/9/0intro
+referenced	/man/1/0intro
+referenced	/man/1/disdep
+referenced	/man/10/2c
+referenced	/man/10/dev
+referenced	/man/10/devattach
+referenced	/man/2/daytime
+referenced	/man/2/keyring-sha1
+referenced	/man/2/spree
+referenced	/man/2/styxservers-nametree
+referenced	/man/2/ubfa
+referenced	/man/3/srv9
+referenced	/man/4/mntgen
+referenced	/man/6/sbl
+referenced	/man/8/mkfs
+references	/man/1/limbo
+references	/man/1/man
+references	/man/1/mash
+references	/man/1/mash-tk
+references	/man/1/mk
+references	/man/10/dynld
+references	/man/10/mk
+references	/man/10/newchan
+references	/man/10/plan9.ini
+references	/man/10/qio
+references	/man/10/styxserver
+references	/man/2/dis
+references	/man/2/draw-0intro
+references	/man/2/factotum
+references	/man/2/plumbmsg
+references	/man/2/readdir
+references	/man/2/sys-dup
+references	/man/2/sys-pipe
+references	/man/2/w3c-css
+references	/man/2/w3c-xpointers
+references	/man/2/wmsrv
+references	/man/3/srv
+references	/man/6/sbl
+references	/man/8/kfscmd
+referential	/man/6/sbl
+referred	/man/1/ps
+referred	/man/1/sh
+referred	/man/1/sh-alphabet
+referred	/man/2/alphabet-intro
+referred	/man/2/draw-0intro
+referred	/man/2/styxservers
+referred	/man/2/styxservers-nametree
+referred	/man/3/indir
+referred	/man/9/canvas
+referred	/man/9/scrollbar
+referred	/man/9/text
+referring	/man/1/mash
+referring	/man/1/sh
+referring	/man/10/conf
+referring	/man/10/master
+referring	/man/2/arg
+referring	/man/2/diskblocks
+referring	/man/2/rfc822
+referring	/man/2/spree-objstore
+referring	/man/2/styxservers
+referring	/man/2/sys-dup
+referring	/man/6/ndb
+refers	/man/1/acme
+refers	/man/1/alphabet-main
+refers	/man/1/cal
+refers	/man/1/yacc
+refers	/man/10/9load
+refers	/man/10/conf
+refers	/man/10/dev
+refers	/man/2/dhcpclient
+refers	/man/2/dial
+refers	/man/2/draw-image
+refers	/man/2/secstore
+refers	/man/2/spki
+refers	/man/2/spki-verifier
+refers	/man/2/spree
+refers	/man/2/styxservers
+refers	/man/2/styxservers-nametree
+refers	/man/2/sys-0intro
+refers	/man/2/sys-bind
+refers	/man/2/sys-read
+refers	/man/2/sys-self
+refers	/man/2/w3c-css
+refers	/man/2/w3c-xpointers
+refers	/man/2/wmsrv
+refers	/man/3/cmd
+refers	/man/3/dbg
+refers	/man/3/fs
+refers	/man/3/tinyfs
+refers	/man/5/0intro
+refers	/man/9/canvas
+refers	/man/9/entry
+refers	/man/9/grid
+refers	/man/9/options
+refers	/man/9/scrollbar
+refers	/man/9/text
+refers	/man/9/types
+refill	/man/2/bufio-chanfill
+refills	/man/2/bufio-chanfill
+reflect	/man/1/ps
+reflect	/man/1/read
+reflect	/man/1/sh-alphabet
+reflect	/man/10/allocb
+reflect	/man/10/devattach
+reflect	/man/10/dynld
+reflect	/man/10/mk
+reflect	/man/2/keyring-getmsg
+reflect	/man/2/msgio
+reflect	/man/2/popup
+reflect	/man/9/0intro
+reflect	/man/9/checkbutton
+reflect	/man/9/variable
+reflected	/man/2/spree-objstore
+reflected	/man/9/menu
+reflected	/man/9/text
+reflecting	/man/1/acme
+reflects	/man/1/mash
+reflects	/man/1/os
+reflects	/man/10/ar
+reflects	/man/5/stat
+reflects	/man/9/text
+refmesg	/man/3/draw
+refnone	/man/2/draw-screen
+refnone	/man/3/draw
+reformat	/man/8/ai2key
+refresh	/man/1/grid-query
+refresh	/man/1/grid-session
+refresh	/man/1/wm-misc
+refresh	/man/2/draw-display
+refresh	/man/2/draw-screen
+refresh	/man/2/spki-verifier
+refresh	/man/3/draw
+refreshed	/man/3/draw
+refreshing	/man/3/draw
+refuse	/man/1/cp
+refuse	/man/8/prep
+refuse	/man/9/button
+refuse	/man/9/checkbutton
+refuse	/man/9/menu
+refuse	/man/9/menubutton
+refuse	/man/9/radiobutton
+refused	/man/1/emu
+refused	/man/2/spree
+refused	/man/8/getauthinfo
+refuses	/man/1/mv
+refuses	/man/8/prep
+reg	/man/2/crc
+reg	/man/2/registries
+reg	/man/3/gpio
+reg	/man/6/ubfa
+regard	/man/10/qio
+regarded	/man/1/m4
+regarded	/man/2/csv
+regarded	/man/2/itslib
+regarded	/man/2/sys-read
+regarded	/man/4/registry
+regarded	/man/5/0intro
+regarding	/man/2/spki-verifier
+regardless	/man/1/limbo
+regardless	/man/1/sh-tk
+regardless	/man/10/qio
+regardless	/man/2/draw-image
+regardless	/man/3/ether
+regardless	/man/3/flash
+regardless	/man/5/read
+regardless	/man/6/audio
+regardless	/man/8/create
+regardless	/man/8/prep
+regardless	/man/9/text
+regards	/man/6/keyboard
+regenerate	/man/3/logfs
+regents	/man/9/0intro
+regents	/man/9/1copyright
+regex	/man/1/grep
+regex	/man/1/mash
+regex	/man/1/sh
+regex	/man/1/sh-regex
+regex	/man/2/0intro
+regex	/man/2/filepat
+regex	/man/2/regex
+regex	/man/6/regexp
+regex.b	/man/1/sh-regex
+regex.b	/man/2/regex
+regex.m	/man/2/regex
+regexp	/man/1/acme
+regexp	/man/1/mk
+regexp	/man/1/sh-regex
+regexp	/man/10/mk
+regexp	/man/2/regex
+regexp	/man/6/regexp
+regime	/man/2/msgio
+region	/man/1/avr
+region	/man/2/draw-0intro
+region	/man/2/draw-example
+region	/man/2/draw-image
+region	/man/2/sys-utfbytes
+region	/man/3/dbg
+region	/man/3/flash
+region	/man/3/ftl
+region	/man/6/sbl
+region	/man/8/mangaload
+region	/man/9/canvas
+region	/man/9/entry
+region	/man/9/scrollbar
+region	/man/9/see
+regions	/man/1/wm-misc
+regions	/man/10/memory
+regions	/man/2/draw-font
+regions	/man/3/draw
+regions	/man/3/ftl
+regions	/man/3/logfs
+regions	/man/3/pnp
+regions	/man/8/prep
+regions	/man/9/canvas
+register	/man/1/grid-monitor
+register	/man/1/grid-ns
+register	/man/1/grid-register
+register	/man/1/mash
+register	/man/1/mux
+register	/man/10/2c
+register	/man/10/a.out
+register	/man/2/crc
+register	/man/2/math-fp
+register	/man/2/registries
+register	/man/2/security-0intro
+register	/man/2/virgil
+register	/man/3/dbg
+register	/man/3/gpio
+register	/man/3/lpt
+register	/man/3/pnp
+register	/man/4/grid-cpu
+register	/man/4/registry
+register	/man/6/dis
+register	/man/6/ubfa
+register	/man/8/manufacture
+register	/man/8/register
+register	/man/8/signer
+register.b	/man/1/grid-register
+register.b	/man/8/register
+registered	/man/1/grid-query
+registered	/man/1/grid-register
+registered	/man/1/grid-session
+registered	/man/1/mash
+registered	/man/1/passwd
+registered	/man/2/registries
+registered	/man/2/secstore
+registered	/man/3/ip
+registered	/man/4/registry
+registered	/man/6/keys
+registered	/man/8/getauthinfo
+registered	/man/8/svc
+registerization	/man/10/2c
+registers	/man/1/grid-register
+registers	/man/10/acid
+registers	/man/10/error
+registers	/man/10/intrenable
+registers	/man/2/registries
+registers	/man/3/gpio
+registers	/man/3/pnp
+registers	/man/3/vga
+registers	/man/6/ubfa
+registration	/man/10/intrenable
+registration	/man/2/keyring-0intro
+registration	/man/2/registries
+registration	/man/4/registry
+registration	/man/8/svc
+registries	/man/1/grid-query
+registries	/man/1/grid-register
+registries	/man/1/grid-session
+registries	/man/2/registries
+registries	/man/4/registry
+registries.m	/man/2/registries
+registry	/man/1/grid-ns
+registry	/man/1/grid-query
+registry	/man/1/grid-register
+registry	/man/1/grid-session
+registry	/man/2/registries
+registry	/man/3/srv
+registry	/man/3/srv9
+registry	/man/4/9srvfs
+registry	/man/4/grid-cpu
+registry	/man/4/registry
+registry	/man/8/svc
+registry.b	/man/4/registry
+registry.connect	/man/2/registries
+registry.new	/man/2/registries
+registry.sh	/man/8/svc
+regquery	/man/4/registry
+regs	/man/10/acid
+regular	/man/1/acme
+regular	/man/1/alphabet-fs
+regular	/man/1/fs
+regular	/man/1/ftest
+regular	/man/1/grep
+regular	/man/1/mash
+regular	/man/1/mk
+regular	/man/1/sh-regex
+regular	/man/1/tiny
+regular	/man/1/wm-misc
+regular	/man/10/2l
+regular	/man/10/mk
+regular	/man/2/draw-0intro
+regular	/man/2/draw-image
+regular	/man/2/imagefile
+regular	/man/2/prefab-0intro
+regular	/man/2/prefab-style
+regular	/man/2/regex
+regular	/man/2/sys-0intro
+regular	/man/4/acme
+regular	/man/5/open
+regular	/man/6/keyboard
+regular	/man/6/plumbing
+regular	/man/6/regexp
+regular	/man/8/prep
+regulated	/man/1/yacc
+regulations	/man/9/1copyright
+reinitialise	/man/3/mpeg
+reinitialise	/man/8/kfscmd
+reinitialised	/man/3/tinyfs
+reject	/man/1/comm
+reject	/man/1/dmview
+reject	/man/10/2c
+reject	/man/2/dial
+reject	/man/3/usb
+reject	/man/5/stat
+rejected	/man/1/tiny
+rejected	/man/2/dial
+rejected	/man/2/sys-fversion
+rejected	/man/5/open
+rejected	/man/8/getauthinfo
+rejecting	/man/3/ip
+rejecting	/man/3/usb
+rejects	/man/4/palmsrv
+rel	/man/2/w3c-uris
+relate	/man/1/ls
+related	/man/1/acme
+related	/man/1/m4
+related	/man/1/mk
+related	/man/1/telnet
+related	/man/10/0intro
+related	/man/10/2c
+related	/man/10/mk
+related	/man/10/styx
+related	/man/2/0intro
+related	/man/2/exception
+related	/man/2/sys-0intro
+related	/man/3/ip
+related	/man/4/acme
+related	/man/4/namespace
+related	/man/6/attrdb
+related	/man/6/keyboard
+related	/man/6/sexprs
+related	/man/9/1copyright
+related	/man/9/canvas
+related	/man/9/text
+relates	/man/6/sbl
+relating	/man/1/ls
+relating	/man/6/attrdb
+relation	/man/1/disdep
+relation	/man/1/tsort
+relation	/man/2/prof
+relational	/man/1/sh-expr
+relational	/man/9/text
+relations	/man/1/tsort
+relationship	/man/9/text
+relationships	/man/1/tsort
+relative	/man/1/alphabet-fs
+relative	/man/1/alphabet-main
+relative	/man/1/gettar
+relative	/man/1/grid-ns
+relative	/man/1/limbo
+relative	/man/1/mash-tk
+relative	/man/1/sh
+relative	/man/1/tiny
+relative	/man/1/vacget
+relative	/man/10/dynld
+relative	/man/2/daytime
+relative	/man/2/dis
+relative	/man/2/disks
+relative	/man/2/fsproto
+relative	/man/2/names
+relative	/man/2/rfc822
+relative	/man/2/spree
+relative	/man/2/sys-0intro
+relative	/man/2/sys-export
+relative	/man/2/tk
+relative	/man/2/wmsrv
+relative	/man/3/flash
+relative	/man/6/font
+relative	/man/6/man
+relative	/man/6/proto
+relative	/man/6/sbl
+relative	/man/8/applylog
+relative	/man/8/bootpd
+relative	/man/8/collabsrv
+relative	/man/9/bind
+relative	/man/9/canvas
+relative	/man/9/entry
+relative	/man/9/grid
+relative	/man/9/listbox
+relative	/man/9/options
+relative	/man/9/panel
+relative	/man/9/scrollbar
+relative	/man/9/see
+relative	/man/9/text
+relatives	/man/1/m4
+relatives	/man/10/print
+relaxed	/man/5/stat
+release	/man/1/acme
+release	/man/1/cpu
+release	/man/1/mux
+release	/man/10/error
+release	/man/10/ref
+release	/man/2/debug
+release	/man/2/dhcpclient
+release	/man/2/diskblocks
+release	/man/2/lock
+release	/man/3/touch
+release	/man/9/bind
+release	/man/9/grab
+release	/man/9/listbox
+released	/man/1/acme
+released	/man/2/draw-pointer
+released	/man/2/sys-0intro
+released	/man/2/sys-open
+released	/man/9/bind
+released	/man/9/button
+released	/man/9/checkbutton
+released	/man/9/choicebutton
+released	/man/9/grab
+released	/man/9/image
+released	/man/9/listbox
+released	/man/9/menu
+released	/man/9/menubutton
+released	/man/9/options
+released	/man/9/radiobutton
+released	/man/9/scrollbar
+released	/man/9/text
+releaseing	/man/9/menubutton
+releases	/man/10/lock
+releases	/man/2/lock
+releases	/man/9/grab
+releases	/man/9/menu
+releasing	/man/1/kill
+releasing	/man/10/error
+releasing	/man/10/lock
+releasing	/man/2/diskblocks
+releasing	/man/9/menubutton
+relevant	/man/1/acme
+relevant	/man/1/blur
+relevant	/man/1/cprof
+relevant	/man/1/mprof
+relevant	/man/1/prof
+relevant	/man/1/tktester
+relevant	/man/10/master
+relevant	/man/10/plan9.ini
+relevant	/man/2/0intro
+relevant	/man/2/disks
+relevant	/man/2/factotum
+relevant	/man/2/print
+relevant	/man/2/prof
+relevant	/man/2/w3c-css
+relevant	/man/3/ip
+relevant	/man/4/acme
+reliable	/man/10/qio
+reliable	/man/3/ip
+reliably	/man/3/ip
+relied	/man/10/devattach
+relied	/man/2/wmsrv
+relief	/man/2/translate
+relief	/man/9/button
+relief	/man/9/canvas
+relief	/man/9/checkbutton
+relief	/man/9/choicebutton
+relief	/man/9/entry
+relief	/man/9/frame
+relief	/man/9/label
+relief	/man/9/listbox
+relief	/man/9/menu
+relief	/man/9/menubutton
+relief	/man/9/options
+relief	/man/9/panel
+relief	/man/9/radiobutton
+relief	/man/9/scale
+relief	/man/9/scrollbar
+relief	/man/9/text
+relief	/man/9/types
+relies	/man/1/netstat
+relies	/man/2/convcs
+relies	/man/2/keyring-0intro
+relies	/man/7/dbsrv
+relinquish	/man/1/wm
+reload	/man/1/charon
+reload	/man/1/cprof
+reloaded	/man/1/charon
+relocation	/man/1/mprof
+relocation	/man/10/dynld
+relpath	/man/1/grid-ns
+relpath	/man/2/w3c-xpointers
+rely	/man/1/mk
+rely	/man/10/mk
+rely	/man/4/ramfile
+remade	/man/1/mk
+remade	/man/10/mk
+remain	/man/1/charon
+remain	/man/1/collab-clients
+remain	/man/1/sh-mload
+remain	/man/1/wm-sh
+remain	/man/10/devattach
+remain	/man/10/newchan
+remain	/man/10/print
+remain	/man/10/qio
+remain	/man/2/lists
+remain	/man/2/styxservers
+remain	/man/2/sys-dial
+remain	/man/2/sys-open
+remain	/man/2/sys-pipe
+remain	/man/2/timers
+remain	/man/3/srv
+remain	/man/4/dbfs
+remain	/man/4/factotum
+remain	/man/4/registry
+remain	/man/9/text
+remainder	/man/1/charon
+remainder	/man/1/mash
+remainder	/man/1/sh
+remainder	/man/10/5cv
+remainder	/man/10/plan9.ini
+remainder	/man/10/qio
+remainder	/man/2/daytime
+remainder	/man/2/math-fp
+remainder	/man/2/rfc822
+remainder	/man/3/audio
+remainder	/man/3/cons
+remainder	/man/3/kprof
+remainder	/man/3/pnp
+remainder	/man/3/sd
+remainder	/man/6/regexp
+remained	/man/2/palmfile
+remaining	/man/1/cprof
+remaining	/man/1/ls
+remaining	/man/1/m4
+remaining	/man/1/man
+remaining	/man/1/mkdir
+remaining	/man/1/mprof
+remaining	/man/1/plumb
+remaining	/man/1/prof
+remaining	/man/1/sh
+remaining	/man/1/sh-arg
+remaining	/man/1/sh-expr
+remaining	/man/1/sh-tk
+remaining	/man/1/tiny
+remaining	/man/10/2l
+remaining	/man/10/a.out
+remaining	/man/10/inb
+remaining	/man/10/plan9.ini
+remaining	/man/2/arg
+remaining	/man/2/bufio
+remaining	/man/2/dbm
+remaining	/man/2/dis
+remaining	/man/2/palmfile
+remaining	/man/2/pop3
+remaining	/man/2/sexprs
+remaining	/man/2/styx
+remaining	/man/2/styxservers
+remaining	/man/2/sys-pipe
+remaining	/man/3/draw
+remaining	/man/3/ftl
+remaining	/man/3/gpio
+remaining	/man/3/pipe
+remaining	/man/3/sd
+remaining	/man/3/sign
+remaining	/man/4/registry
+remaining	/man/5/0intro
+remaining	/man/8/collabsrv
+remaining	/man/8/create
+remaining	/man/8/httpd
+remaining	/man/8/prep
+remaining	/man/9/canvas
+remaining	/man/9/destroy
+remaining	/man/9/pack
+remaining	/man/9/types
+remains	/man/1/blur
+remains	/man/1/collab-clients
+remains	/man/10/allocb
+remains	/man/10/devattach
+remains	/man/10/odbc
+remains	/man/10/qio
+remains	/man/2/dial
+remains	/man/2/popup
+remains	/man/2/security-ssl
+remains	/man/3/cons
+remains	/man/3/ether
+remains	/man/3/ip
+remains	/man/4/mntgen
+remains	/man/4/registry
+remains	/man/9/bind
+remains	/man/9/menubutton
+remap	/man/2/imagefile
+remapping	/man/3/ftl
+remember	/man/1/m4
+remember	/man/2/styxservers
+remembered	/man/1/tktester
+remembered	/man/6/man
+remembering	/man/9/text
+remembers	/man/2/styxservers
+remind	/man/3/ip
+remmulti	/man/3/ether
+remmulti	/man/3/ip
+remote	/man/1/alphabet-abc
+remote	/man/1/alphabet-grid
+remote	/man/1/charon
+remote	/man/1/collab
+remote	/man/1/collab-clients
+remote	/man/1/cpu
+remote	/man/1/deb
+remote	/man/1/dmview
+remote	/man/1/listen
+remote	/man/1/mux
+remote	/man/1/netkey
+remote	/man/1/os
+remote	/man/1/passwd
+remote	/man/1/ps
+remote	/man/1/rcmd
+remote	/man/1/secstore
+remote	/man/1/sh-file2chan
+remote	/man/1/spree-join
+remote	/man/1/telnet
+remote	/man/1/uuencode
+remote	/man/1/wm-sh
+remote	/man/10/odbc
+remote	/man/10/plan9.ini
+remote	/man/10/styxserver
+remote	/man/2/dial
+remote	/man/2/draw-screen
+remote	/man/2/drawmux
+remote	/man/2/ir
+remote	/man/2/prefab-0intro
+remote	/man/2/prefab-compound
+remote	/man/2/prefab-element
+remote	/man/2/registries
+remote	/man/2/secstore
+remote	/man/2/sys-dial
+remote	/man/3/0intro
+remote	/man/3/dbg
+remote	/man/3/eia
+remote	/man/3/ip
+remote	/man/3/kprof
+remote	/man/3/mnt
+remote	/man/3/plap
+remote	/man/4/ftpfs
+remote	/man/4/import
+remote	/man/4/keysrv
+remote	/man/4/lockfs
+remote	/man/4/namespace
+remote	/man/4/spree
+remote	/man/5/attach
+remote	/man/6/namespace
+remote	/man/8/bootpd
+remote	/man/8/getauthinfo
+remote	/man/8/init
+remote	/man/8/mkfs
+remote	/man/8/rdbgsrv
+remote	/man/8/rstyxd
+remote	/man/8/signer
+remote	/man/8/styxmon
+remote	/man/8/svc
+remote	/man/8/virgild
+remoteaddr	/man/3/plap
+remoteclosed	/man/3/tls
+remotedir	/man/6/namespace
+remotefile	/man/1/uuencode
+remotely	/man/1/cpu
+remotely	/man/3/dbg
+remotely	/man/3/pipe
+remotely	/man/3/ssl
+remotely	/man/8/svc
+remotes	/man/2/ir
+remoteuser	/man/2/registries
+removable	/man/3/sd
+removal	/man/1/rm
+removal	/man/10/styxserver
+removal	/man/2/math-0intro
+removal	/man/2/styxservers
+removal	/man/2/styxservers-nametree
+removal	/man/8/create
+removals	/man/2/sys-0intro
+remove	/man/1/alphabet-fs
+remove	/man/1/fs
+remove	/man/1/ftree
+remove	/man/1/grid-monitor
+remove	/man/1/grid-session
+remove	/man/1/rm
+remove	/man/10/dev
+remove	/man/10/devattach
+remove	/man/10/error
+remove	/man/10/kstrip
+remove	/man/10/qio
+remove	/man/10/styxserver
+remove	/man/2/registries
+remove	/man/2/secstore
+remove	/man/2/spree-cardlib
+remove	/man/2/styx
+remove	/man/2/styxpersist
+remove	/man/2/styxservers
+remove	/man/2/styxservers-nametree
+remove	/man/2/sys-0intro
+remove	/man/2/sys-open
+remove	/man/2/sys-remove
+remove	/man/2/wmsrv
+remove	/man/3/dbg
+remove	/man/3/ether
+remove	/man/3/ip
+remove	/man/3/srv
+remove	/man/4/acme
+remove	/man/4/keyfs
+remove	/man/4/registry
+remove	/man/5/0intro
+remove	/man/5/open
+remove	/man/5/remove
+remove	/man/5/stat
+remove	/man/5/walk
+remove	/man/8/kfscmd
+remove	/man/9/text
+removebuiltin	/man/2/sh
+removecfg	/man/2/dhcpclient
+removed	/man/1/alphabet-fs
+removed	/man/1/diff
+removed	/man/1/fs
+removed	/man/1/grid-query
+removed	/man/1/grid-session
+removed	/man/1/gzip
+removed	/man/1/m4
+removed	/man/1/mash-tk
+removed	/man/1/mv
+removed	/man/1/rm
+removed	/man/1/sh-std
+removed	/man/1/tiny
+removed	/man/1/uniq
+removed	/man/10/print
+removed	/man/10/styxserver
+removed	/man/2/dbm
+removed	/man/2/diskblocks
+removed	/man/2/factotum
+removed	/man/2/lists
+removed	/man/2/registries
+removed	/man/2/sets
+removed	/man/2/sh
+removed	/man/2/styxservers
+removed	/man/2/sys-fd2path
+removed	/man/2/sys-print
+removed	/man/2/timers
+removed	/man/2/w3c-uris
+removed	/man/3/cap
+removed	/man/3/flash
+removed	/man/3/fs
+removed	/man/3/ip
+removed	/man/3/srv
+removed	/man/3/srv9
+removed	/man/4/9srvfs
+removed	/man/4/dbfs
+removed	/man/4/grid-cpu
+removed	/man/4/logfile
+removed	/man/4/registry
+removed	/man/5/clunk
+removed	/man/5/open
+removed	/man/8/cs
+removed	/man/8/mkfs
+removed	/man/9/bind
+removed	/man/9/grid
+removed	/man/9/text
+removes	/man/1/blur
+removes	/man/1/cleanname
+removes	/man/1/m4
+removes	/man/1/mash
+removes	/man/1/mash-tk
+removes	/man/1/mv
+removes	/man/1/rm
+removes	/man/1/sh-alphabet
+removes	/man/1/tiny
+removes	/man/1/tktester
+removes	/man/10/intrenable
+removes	/man/10/kstrip
+removes	/man/10/qio
+removes	/man/10/styxserver
+removes	/man/2/dhcpclient
+removes	/man/2/hash
+removes	/man/2/sh
+removes	/man/2/spree
+removes	/man/2/string
+removes	/man/2/sys-remove
+removes	/man/3/cons
+removes	/man/3/flash
+removes	/man/3/ftl
+removes	/man/3/ip
+removes	/man/3/logfs
+removes	/man/3/srv
+removes	/man/9/grid
+removes	/man/9/pack
+removes	/man/9/text
+removesbuiltin	/man/2/sh
+removing	/man/1/rm
+removing	/man/2/factotum
+removing	/man/2/rfc822
+removing	/man/3/env
+removing	/man/4/dbfs
+removing	/man/4/registry
+removing	/man/5/remove
+rename	/man/2/styxpersist
+rename	/man/4/ftpfs
+rename	/man/4/keyfs
+rename	/man/8/kfscmd
+renamed	/man/1/m4
+renamed	/man/1/mv
+renamed	/man/1/ns
+renamed	/man/1/pwd
+renamed	/man/2/palmfile
+renamed	/man/2/sys-fd2path
+renames	/man/3/logfs
+renaming	/man/1/tktester
+renamings	/man/10/acid
+renamings	/man/2/sys-0intro
+render	/man/1/alphabet-abc
+render	/man/1/alphabet-grid
+render	/man/3/draw
+render	/man/3/fs
+render	/man/9/image
+rendered	/man/2/pslib
+rendered	/man/9/canvas
+rendering	/man/1/alphabet-abc
+rendering	/man/1/alphabet-grid
+rendering	/man/2/imagefile
+rendering	/man/9/image
+rendez	/man/10/sleep
+rendezvous	/man/10/sleep
+rendezvous	/man/3/dbg
+renegotiation	/man/3/tls
+renew	/man/2/dhcpclient
+renewed	/man/8/dhcp
+renews	/man/8/dhcp
+renumber	/man/2/pop3
+reopen	/man/2/attrdb
+reopen	/man/3/eia
+reopened	/man/10/qio
+reopened	/man/2/attrdb
+reopened	/man/3/ip
+reopened	/man/3/usb
+rep	/man/1/fc
+rep	/man/1/sh-expr
+rep	/man/6/regexp
+repaint	/man/2/draw-screen
+repainted	/man/3/draw
+repaired	/man/1/acme
+repaired	/man/3/logfs
+reparsed	/man/1/sh
+repeat	/man/1/itest
+repeat	/man/10/kbdputc
+repeat	/man/2/sys-0intro
+repeat	/man/6/keyboard
+repeated	/man/1/m4
+repeated	/man/1/mdb
+repeated	/man/1/tr
+repeated	/man/1/uniq
+repeated	/man/1/xd
+repeated	/man/10/acid
+repeated	/man/10/kbdputc
+repeated	/man/2/ida
+repeated	/man/2/prof
+repeated	/man/6/keyboard
+repeated	/man/8/prep
+repeated	/man/8/touchcal
+repeatedly	/man/1/acme
+repeatedly	/man/1/fc
+repeatedly	/man/1/mash
+repeatedly	/man/1/netkey
+repeatedly	/man/1/sh-std
+repeatedly	/man/10/2l
+repeatedly	/man/10/acid
+repeatedly	/man/10/lock
+repeatedly	/man/2/crc
+repeatedly	/man/2/keyring-crypt
+repeatedly	/man/2/styxpersist
+repeatedly	/man/3/touch
+repeating	/man/3/ip
+repeats	/man/1/sh-expr
+repeats	/man/2/dhcpclient
+repeats	/man/9/scale
+repeats	/man/9/scrollbar
+repetition	/man/2/alphabet-intro
+repetitions	/man/1/mdb
+repl	/man/2/draw-display
+repl	/man/2/draw-image
+repl	/man/3/draw
+replace	/man/1/acme
+replace	/man/1/ar
+replace	/man/1/bind
+replace	/man/1/brutus
+replace	/man/1/secstore
+replace	/man/1/tr
+replace	/man/10/iar
+replace	/man/2/dbm
+replace	/man/2/sys-0intro
+replace	/man/2/sys-bind
+replace	/man/2/tkclient
+replace	/man/2/wmclient
+replace	/man/2/wmlib
+replace	/man/3/draw
+replace	/man/3/ip
+replace	/man/4/archfs
+replace	/man/4/dossrv
+replace	/man/4/factotum
+replace	/man/4/tarfs
+replace	/man/6/sexprs
+replace	/man/9/canvas
+replaceable	/man/10/acid
+replaced	/man/1/calc
+replaced	/man/1/charon
+replaced	/man/1/gzip
+replaced	/man/1/m4
+replaced	/man/1/mash
+replaced	/man/1/mk
+replaced	/man/1/sh
+replaced	/man/1/wm-sh
+replaced	/man/10/c2l
+replaced	/man/10/mk
+replaced	/man/10/print
+replaced	/man/2/asn1
+replaced	/man/2/dial
+replaced	/man/2/ip
+replaced	/man/2/keyring-getmsg
+replaced	/man/2/math-0intro
+replaced	/man/2/msgio
+replaced	/man/2/rfc822
+replaced	/man/2/sh
+replaced	/man/2/sys-dial
+replaced	/man/2/sys-print
+replaced	/man/3/kprof
+replaced	/man/6/plumbing
+replaced	/man/6/proto
+replaced	/man/6/sbl
+replaced	/man/6/sexprs
+replaced	/man/9/bind
+replaced	/man/9/image
+replaced	/man/9/send
+replacement	/man/1/acme
+replacement	/man/1/m4
+replacement	/man/9/bind
+replaces	/man/1/acme
+replaces	/man/1/brutus
+replaces	/man/1/man
+replaces	/man/1/wm-sh
+replaces	/man/1/yacc
+replaces	/man/2/draw-image
+replaces	/man/2/styxservers
+replaces	/man/4/acme
+replaces	/man/4/import
+replaces	/man/4/keysrv
+replaces	/man/5/error
+replaces	/man/6/image
+replaces	/man/8/cs
+replaces	/man/9/bind
+replacing	/man/1/acme
+replacing	/man/1/gzip
+replacing	/man/1/m4
+replacing	/man/2/0intro
+replacing	/man/6/ubfa
+replacing	/man/8/mangaload
+replacing	/man/9/bind
+replacing	/man/9/canvas
+replacing	/man/9/text
+replay	/man/1/itest
+replay	/man/2/security-0intro
+replay	/man/6/login
+replayed	/man/1/itest
+replayed	/man/3/logfs
+replays	/man/1/itest
+replenishes	/man/2/bufio
+replica	/man/1/dmview
+replica	/man/8/applylog
+replicas	/man/8/applylog
+replicate	/man/1/secstore
+replicate	/man/2/draw-image
+replicate	/man/2/drawmux
+replicate	/man/3/draw
+replicated	/man/2/draw-0intro
+replicated	/man/2/draw-display
+replicated	/man/2/draw-image
+replicated	/man/2/drawmux
+replicated	/man/2/mpeg
+replicated	/man/2/prefab-style
+replicated	/man/8/collabsrv
+replicated	/man/9/panel
+replicating	/man/2/draw-image
+replicating	/man/6/colour
+replication	/man/2/draw-0intro
+replication	/man/2/draw-image
+replied	/man/1/sh-file2chan
+replied	/man/2/styxflush
+replied	/man/5/0intro
+replies	/man/1/sh-file2chan
+replies	/man/2/bufio-chanfill
+replies	/man/2/styx
+replies	/man/2/styxflush
+replies	/man/2/styxservers
+replies	/man/2/sys-export
+replies	/man/2/wmsrv
+replies	/man/3/ip
+replies	/man/3/mnt
+replies	/man/3/usb
+replies	/man/4/factotum
+replies	/man/5/0intro
+replies	/man/8/bootpd
+replies	/man/8/rip
+replies	/man/8/virgild
+reply	/man/1/acme
+reply	/man/1/sh-file2chan
+reply	/man/2/dhcpclient
+reply	/man/2/draw-context
+reply	/man/2/filter
+reply	/man/2/styx
+reply	/man/2/styxflush
+reply	/man/2/styxservers
+reply	/man/2/sys-file2chan
+reply	/man/2/virgil
+reply	/man/2/wmsrv
+reply	/man/3/dbg
+reply	/man/3/ip
+reply	/man/3/mnt
+reply	/man/3/srv
+reply	/man/3/usb
+reply	/man/4/factotum
+reply	/man/4/palmsrv
+reply	/man/5/0intro
+reply	/man/5/error
+reply	/man/5/open
+reply	/man/5/read
+reply	/man/5/stat
+reply	/man/5/version
+reply	/man/8/bootpd
+reply	/man/8/dhcp
+reply	/man/8/ping
+reply	/man/8/rdbgsrv
+reply	/man/8/sntp
+replyc	/man/2/styxflush
+replychan	/man/2/styxservers
+replydirect	/man/2/styxservers
+replying	/man/2/styxservers
+report	/man/1/date
+report	/man/1/gzip
+report	/man/1/limbo
+report	/man/1/sh-test
+report	/man/1/uniq
+report	/man/1/webgrab
+report	/man/1/yacc
+report	/man/2/alphabet-intro
+report	/man/2/disks
+report	/man/2/imagefile
+report	/man/2/itslib
+report	/man/2/sys-stat
+report	/man/3/draw
+report	/man/3/eia
+report	/man/3/ip
+report	/man/4/iostats
+report	/man/4/registry
+report	/man/8/kfscmd
+report.start	/man/2/alphabet-intro
+reported	/man/1/emu
+reported	/man/1/mash
+reported	/man/1/plumb
+reported	/man/1/ps
+reported	/man/1/sh
+reported	/man/1/sh-test
+reported	/man/1/timestamp
+reported	/man/1/tktester
+reported	/man/10/acid
+reported	/man/2/sys-0intro
+reported	/man/2/xml
+reported	/man/3/fs
+reported	/man/4/acme
+reported	/man/8/signer
+reported	/man/9/bind
+reported	/man/9/grab
+reporting	/man/1/math-misc
+reporting	/man/1/sh-test
+reporting	/man/2/itslib
+reporting	/man/2/sys-werrstr
+reporting	/man/3/draw
+reporting	/man/4/acme
+reports	/man/1/dd
+reports	/man/1/mk
+reports	/man/1/mux
+reports	/man/1/strings
+reports	/man/1/time
+reports	/man/10/mk
+reports	/man/10/styx
+reports	/man/2/alphabet-intro
+reports	/man/2/complete
+reports	/man/3/cons
+reports	/man/4/iostats
+reports	/man/5/read
+repository	/man/8/register
+repository	/man/8/signer
+represent	/man/1/acme
+represent	/man/1/fs
+represent	/man/1/ls
+represent	/man/1/tsort
+represent	/man/10/a.out
+represent	/man/10/lock
+represent	/man/10/master
+represent	/man/10/newchan
+represent	/man/10/rune
+represent	/man/2/asn1
+represent	/man/2/csv
+represent	/man/2/debug
+represent	/man/2/disks
+represent	/man/2/draw-display
+represent	/man/2/draw-font
+represent	/man/2/draw-pointer
+represent	/man/2/ida
+represent	/man/2/json
+represent	/man/2/prefab-element
+represent	/man/2/registries
+represent	/man/2/rfc822
+represent	/man/2/scsiio
+represent	/man/2/sexprs
+represent	/man/2/sh
+represent	/man/2/styx
+represent	/man/2/styxservers
+represent	/man/2/sys-0intro
+represent	/man/2/ubfa
+represent	/man/2/venti
+represent	/man/2/w3c-css
+represent	/man/2/w3c-xpointers
+represent	/man/4/registry
+represent	/man/5/open
+represent	/man/5/walk
+represent	/man/6/colour
+represent	/man/6/font
+represent	/man/6/json
+represent	/man/6/sbl
+represent	/man/6/ubfa
+represent	/man/6/utf
+represent	/man/8/collabsrv
+represent	/man/8/cs
+represent	/man/8/styxchat
+represent	/man/9/text
+representating	/man/2/sexprs
+representation	/man/1/alphabet-main
+representation	/man/1/sh-sexprs
+representation	/man/1/sh-std
+representation	/man/1/uuencode
+representation	/man/10/atoi
+representation	/man/10/dev
+representation	/man/10/readnum
+representation	/man/10/styx
+representation	/man/2/asn1
+representation	/man/2/debug
+representation	/man/2/dis
+representation	/man/2/disks
+representation	/man/2/draw-0intro
+representation	/man/2/draw-display
+representation	/man/2/ether
+representation	/man/2/factotum
+representation	/man/2/format
+representation	/man/2/imagefile
+representation	/man/2/ip
+representation	/man/2/json
+representation	/man/2/keyring-auth
+representation	/man/2/keyring-certtostr
+representation	/man/2/keyring-ipint
+representation	/man/2/keyring-rc4
+representation	/man/2/keyset
+representation	/man/2/math-export
+representation	/man/2/palmfile
+representation	/man/2/rfc822
+representation	/man/2/sexprs
+representation	/man/2/spki
+representation	/man/2/spree-allow
+representation	/man/2/spree-cardlib
+representation	/man/2/styx
+representation	/man/2/styxservers
+representation	/man/2/sys-0intro
+representation	/man/2/sys-print
+representation	/man/2/ubfa
+representation	/man/2/w3c-uris
+representation	/man/2/w3c-xpointers
+representation	/man/2/workdir
+representation	/man/3/cons
+representation	/man/3/prog
+representation	/man/3/sign
+representation	/man/4/kfs
+representation	/man/4/trfs
+representation	/man/6/colour
+representation	/man/6/dis
+representation	/man/6/utf
+representation	/man/8/ai2key
+representation	/man/8/collabsrv
+representation	/man/8/styxchat
+representations	/man/1/math-misc
+representations	/man/1/unicode
+representations	/man/10/kbdputc
+representations	/man/10/styx
+representations	/man/2/json
+representations	/man/2/math-export
+representations	/man/2/plumbmsg
+representations	/man/2/ubfa
+representations	/man/6/sexprs
+representative	/man/10/plan9.ini
+representative	/man/10/print
+representaton	/man/2/sets
+representing	/man/1/acme
+representing	/man/1/cp
+representing	/man/1/fc
+representing	/man/1/ls
+representing	/man/1/sh-csv
+representing	/man/1/sort
+representing	/man/1/toolbar
+representing	/man/1/xd
+representing	/man/10/dev
+representing	/man/10/devattach
+representing	/man/10/dynld
+representing	/man/10/odbc
+representing	/man/10/splhi
+representing	/man/2/alphabet-intro
+representing	/man/2/asn1
+representing	/man/2/attrdb
+representing	/man/2/bloomfilter
+representing	/man/2/debug
+representing	/man/2/dhcpclient
+representing	/man/2/dial
+representing	/man/2/dict
+representing	/man/2/dis
+representing	/man/2/draw-display
+representing	/man/2/draw-screen
+representing	/man/2/drawmux
+representing	/man/2/encoding
+representing	/man/2/factotum
+representing	/man/2/ida
+representing	/man/2/imagefile
+representing	/man/2/ip
+representing	/man/2/ir
+representing	/man/2/keyring-0intro
+representing	/man/2/keyring-certtostr
+representing	/man/2/keyring-ipint
+representing	/man/2/plumbmsg
+representing	/man/2/prefab-element
+representing	/man/2/registries
+representing	/man/2/rfc822
+representing	/man/2/secstore
+representing	/man/2/sets
+representing	/man/2/sexprs
+representing	/man/2/spki
+representing	/man/2/spree-cardlib
+representing	/man/2/styxservers
+representing	/man/2/sys-dial
+representing	/man/2/sys-file2chan
+representing	/man/2/sys-fversion
+representing	/man/2/sys-pipe
+representing	/man/2/sys-utfbytes
+representing	/man/2/w3c-css
+representing	/man/2/w3c-uris
+representing	/man/2/w3c-xpointers
+representing	/man/3/0intro
+representing	/man/3/boot
+representing	/man/3/cons
+representing	/man/3/dbg
+representing	/man/3/ether
+representing	/man/3/flash
+representing	/man/3/ip
+representing	/man/3/lpt
+representing	/man/3/plap
+representing	/man/3/pnp
+representing	/man/3/prof
+representing	/man/3/prog
+representing	/man/3/ssl
+representing	/man/3/switch
+representing	/man/4/archfs
+representing	/man/4/keyfs
+representing	/man/4/registry
+representing	/man/5/stat
+representing	/man/6/colour
+representing	/man/6/dis
+representing	/man/6/sbl
+representing	/man/6/ubfa
+representing	/man/8/applylog
+representing	/man/8/rdbgsrv
+representing	/man/8/styxchat
+representing	/man/9/bind
+representing	/man/9/types
+represents	/man/1/acme
+represents	/man/1/alphabet-abc
+represents	/man/1/alphabet-grid
+represents	/man/1/collab-clients
+represents	/man/1/ftest
+represents	/man/1/sh-tk
+represents	/man/1/tsort
+represents	/man/10/2c
+represents	/man/10/2l
+represents	/man/10/a.out
+represents	/man/10/conf
+represents	/man/10/dev
+represents	/man/10/devattach
+represents	/man/10/dynld
+represents	/man/10/master
+represents	/man/10/newchan
+represents	/man/10/qlock
+represents	/man/10/styxserver
+represents	/man/2/asn1
+represents	/man/2/attrdb
+represents	/man/2/complete
+represents	/man/2/convcs
+represents	/man/2/debug
+represents	/man/2/dis
+represents	/man/2/disks
+represents	/man/2/draw-0intro
+represents	/man/2/draw-display
+represents	/man/2/factotum
+represents	/man/2/format
+represents	/man/2/ip
+represents	/man/2/json
+represents	/man/2/keyring-auth
+represents	/man/2/keyring-ipint
+represents	/man/2/palmfile
+represents	/man/2/readdir
+represents	/man/2/registries
+represents	/man/2/sexprs
+represents	/man/2/sh
+represents	/man/2/spki
+represents	/man/2/spki-verifier
+represents	/man/2/spree
+represents	/man/2/spree-cardlib
+represents	/man/2/styxservers
+represents	/man/2/sys-fversion
+represents	/man/2/translate
+represents	/man/2/ubfa
+represents	/man/2/venti
+represents	/man/2/w3c-css
+represents	/man/2/w3c-uris
+represents	/man/2/w3c-xpointers
+represents	/man/2/wmsrv
+represents	/man/2/xml
+represents	/man/3/audio
+represents	/man/3/cmd
+represents	/man/3/eia
+represents	/man/3/flash
+represents	/man/3/fs
+represents	/man/3/ip
+represents	/man/3/logfs
+represents	/man/3/ssl
+represents	/man/4/factotum
+represents	/man/5/0intro
+represents	/man/5/walk
+represents	/man/6/auth
+represents	/man/6/colour
+represents	/man/6/json
+represents	/man/6/keytext
+represents	/man/6/sexprs
+represents	/man/6/ubfa
+represents	/man/6/utf
+represents	/man/8/ai2key
+represents	/man/8/collabsrv
+represents	/man/8/rdbgsrv
+represents	/man/8/styxchat
+represents	/man/9/bind
+represents	/man/9/grid
+represents	/man/9/text
+reproduce	/man/2/w3c-uris
+reproduced	/man/2/sexprs
+reproduced	/man/5/0intro
+reproduced	/man/6/sexprs
+reproduction	/man/1/man
+reprogrammed	/man/3/vid
+reprogramming	/man/3/flash
+reprograms	/man/3/flash
+republicca	/man/6/translate
+reqid	/man/1/wm
+reqid	/man/2/tk
+request	/man/1/alphabet-main
+request	/man/1/dmview
+request	/man/1/emu
+request	/man/1/keyboard
+request	/man/1/listen
+request	/man/1/sh-file2chan
+request	/man/1/sh-tk
+request	/man/1/spree-join
+request	/man/1/wm
+request	/man/1/wm-sh
+request	/man/10/dev
+request	/man/10/devattach
+request	/man/10/dmainit
+request	/man/10/kproc
+request	/man/10/styxserver
+request	/man/2/alphabet-intro
+request	/man/2/bufio-chanfill
+request	/man/2/dhcpclient
+request	/man/2/dis
+request	/man/2/draw-context
+request	/man/2/draw-font
+request	/man/2/factotum
+request	/man/2/filter
+request	/man/2/format
+request	/man/2/scsiio
+request	/man/2/spki
+request	/man/2/spree
+request	/man/2/spree-gather
+request	/man/2/styxflush
+request	/man/2/styxpersist
+request	/man/2/styxservers
+request	/man/2/sys-0intro
+request	/man/2/sys-file2chan
+request	/man/2/sys-fversion
+request	/man/2/timers
+request	/man/2/tk
+request	/man/2/tkclient
+request	/man/2/virgil
+request	/man/2/wmclient
+request	/man/2/wmlib
+request	/man/2/wmsrv
+request	/man/2/xml
+request	/man/3/cmd
+request	/man/3/dbg
+request	/man/3/ftl
+request	/man/3/gpio
+request	/man/3/i2c
+request	/man/3/ip
+request	/man/3/sign
+request	/man/3/usb
+request	/man/4/factotum
+request	/man/4/palmsrv
+request	/man/4/spree
+request	/man/5/0intro
+request	/man/5/clunk
+request	/man/5/error
+request	/man/5/flush
+request	/man/5/open
+request	/man/5/read
+request	/man/5/remove
+request	/man/5/stat
+request	/man/5/version
+request	/man/5/walk
+request	/man/6/man
+request	/man/6/ndb
+request	/man/7/db
+request	/man/7/dbsrv
+request	/man/8/bootpd
+request	/man/8/collabsrv
+request	/man/8/cs
+request	/man/8/dhcp
+request	/man/8/getauthinfo
+request	/man/8/ping
+request	/man/8/rstyxd
+request	/man/8/virgild
+request	/man/9/canvas
+request	/man/9/frame
+request	/man/9/grid
+request	/man/9/options
+requested	/man/1/charon
+requested	/man/1/sh-file2chan
+requested	/man/1/wm
+requested	/man/10/dynld
+requested	/man/10/qio
+requested	/man/10/qlock
+requested	/man/10/readnum
+requested	/man/2/asn1
+requested	/man/2/convcs
+requested	/man/2/daytime
+requested	/man/2/dhcpclient
+requested	/man/2/prefab-compound
+requested	/man/2/prefab-element
+requested	/man/2/security-auth
+requested	/man/2/spree-cardlib
+requested	/man/2/styxservers
+requested	/man/2/sys-bind
+requested	/man/2/sys-open
+requested	/man/2/sys-read
+requested	/man/2/sys-sleep
+requested	/man/2/tk
+requested	/man/2/wmclient
+requested	/man/3/cons
+requested	/man/3/dbg
+requested	/man/5/0intro
+requested	/man/5/attach
+requested	/man/5/read
+requested	/man/5/walk
+requested	/man/7/db
+requested	/man/8/bootpd
+requested	/man/8/collabsrv
+requested	/man/8/httpd
+requested	/man/8/signer
+requested	/man/9/bind
+requested	/man/9/grid
+requested	/man/9/pack
+requested	/man/9/text
+requesting	/man/10/qlock
+requesting	/man/2/styxservers
+requesting	/man/6/keys
+requesting	/man/8/bootpd
+requesting	/man/8/signer
+requesting	/man/8/virgild
+requests	/man/1/dmview
+requests	/man/1/miniterm
+requests	/man/1/sh-file2chan
+requests	/man/1/sh-tk
+requests	/man/1/wm
+requests	/man/1/wm-misc
+requests	/man/1/zeros
+requests	/man/10/2c
+requests	/man/10/dev
+requests	/man/10/devattach
+requests	/man/10/qio
+requests	/man/2/diskblocks
+requests	/man/2/draw-context
+requests	/man/2/draw-image
+requests	/man/2/draw-screen
+requests	/man/2/drawmux
+requests	/man/2/filter
+requests	/man/2/spree
+requests	/man/2/spree-cardlib
+requests	/man/2/styx
+requests	/man/2/styxflush
+requests	/man/2/styxservers
+requests	/man/2/sys-export
+requests	/man/2/sys-fversion
+requests	/man/2/timers
+requests	/man/2/tk
+requests	/man/2/tkclient
+requests	/man/2/wmclient
+requests	/man/2/wmsrv
+requests	/man/3/arch
+requests	/man/3/cmd
+requests	/man/3/dbg
+requests	/man/3/draw
+requests	/man/3/fpga
+requests	/man/3/gpio
+requests	/man/3/i2c
+requests	/man/3/ip
+requests	/man/3/logfs
+requests	/man/3/srv9
+requests	/man/3/usb
+requests	/man/4/factotum
+requests	/man/4/ramfile
+requests	/man/5/0intro
+requests	/man/5/version
+requests	/man/6/man
+requests	/man/7/db
+requests	/man/7/dbsrv
+requests	/man/8/applylog
+requests	/man/8/bootpd
+requests	/man/8/collabsrv
+requests	/man/8/cs
+requests	/man/8/dhcp
+requests	/man/8/getauthinfo
+requests	/man/8/httpd
+requests	/man/8/logind
+requests	/man/8/ping
+requests	/man/8/plumber
+requests	/man/8/rip
+requests	/man/8/signer
+requests	/man/8/sntp
+requests	/man/8/virgild
+requests	/man/9/canvas
+requests	/man/9/scrollbar
+requests	/man/9/text
+require	/man/1/grid-session
+require	/man/1/tktester
+require	/man/10/plan9.ini
+require	/man/2/0intro
+require	/man/2/factotum
+require	/man/2/keyring-0intro
+require	/man/2/palmfile
+require	/man/2/security-0intro
+require	/man/2/sys-fauth
+require	/man/2/sys-open
+require	/man/3/vga
+require	/man/5/attach
+require	/man/6/colour
+require	/man/6/keytext
+required	/man/1/0intro
+required	/man/1/acme
+required	/man/1/grid-ns
+required	/man/1/grid-session
+required	/man/1/itest
+required	/man/1/miniterm
+required	/man/1/mprof
+required	/man/1/prof
+required	/man/1/sh
+required	/man/1/sh-expr
+required	/man/1/sh-string
+required	/man/1/tktester
+required	/man/1/wm-misc
+required	/man/1/yacc
+required	/man/10/9load
+required	/man/10/c2l
+required	/man/10/conf
+required	/man/10/devattach
+required	/man/10/dmainit
+required	/man/10/dynld
+required	/man/10/iar
+required	/man/10/intrenable
+required	/man/10/print
+required	/man/10/qio
+required	/man/10/rune
+required	/man/10/styx
+required	/man/10/styxserver
+required	/man/2/alphabet-intro
+required	/man/2/asn1
+required	/man/2/csv
+required	/man/2/dhcpclient
+required	/man/2/dial
+required	/man/2/disks
+required	/man/2/encoding
+required	/man/2/factotum
+required	/man/2/filter-slip
+required	/man/2/geodesy
+required	/man/2/ida
+required	/man/2/keyring-crypt
+required	/man/2/keyring-ipint
+required	/man/2/palmfile
+required	/man/2/print
+required	/man/2/prof
+required	/man/2/registries
+required	/man/2/rfc822
+required	/man/2/sexprs
+required	/man/2/styx
+required	/man/2/styxconv
+required	/man/2/styxservers
+required	/man/2/sys-print
+required	/man/2/tk
+required	/man/2/tkclient
+required	/man/2/w3c-css
+required	/man/2/w3c-uris
+required	/man/2/wmclient
+required	/man/3/env
+required	/man/3/flash
+required	/man/3/fpga
+required	/man/3/ip
+required	/man/3/pbus
+required	/man/3/root
+required	/man/3/usb
+required	/man/4/export
+required	/man/4/factotum
+required	/man/4/grid-cpu
+required	/man/4/kfs
+required	/man/4/lockfs
+required	/man/5/attach
+required	/man/5/version
+required	/man/7/db
+required	/man/8/cs
+required	/man/8/styxchat
+required	/man/9/0intro
+required	/man/9/1copyright
+required	/man/9/listbox
+required	/man/9/types
+requirement	/man/1/blur
+requirement	/man/1/sh-tk
+requirement	/man/2/security-0intro
+requirement	/man/2/spree-gather
+requirement	/man/2/styxflush
+requirement	/man/8/changelogin
+requirements	/man/1/sh-alphabet
+requirements	/man/8/svc
+requires	/man/1/0intro
+requires	/man/1/logon
+requires	/man/1/mash
+requires	/man/1/mkdir
+requires	/man/1/mv
+requires	/man/1/ps
+requires	/man/1/rm
+requires	/man/1/sh
+requires	/man/1/sh-alphabet
+requires	/man/1/sh-tk
+requires	/man/1/tiny
+requires	/man/10/2c
+requires	/man/10/devattach
+requires	/man/10/kstrip
+requires	/man/10/ntsrv
+requires	/man/2/alphabet-intro
+requires	/man/2/draw-image
+requires	/man/2/factotum
+requires	/man/2/keyring-0intro
+requires	/man/2/secstore
+requires	/man/2/security-0intro
+requires	/man/2/styxconv
+requires	/man/2/sys-bind
+requires	/man/2/sys-open
+requires	/man/2/sys-pipe
+requires	/man/2/w3c-css
+requires	/man/3/dbg
+requires	/man/3/pbus
+requires	/man/3/ssl
+requires	/man/3/usb
+requires	/man/3/vid
+requires	/man/4/factotum
+requires	/man/4/import
+requires	/man/5/open
+requires	/man/5/stat
+requires	/man/6/colour
+requires	/man/6/image
+requires	/man/8/virgild
+requiring	/man/10/allocb
+requiring	/man/10/error
+requiring	/man/2/draw-image
+requiring	/man/2/security-0intro
+requiring	/man/2/sys-fauth
+requiring	/man/3/flash
+requiring	/man/3/logfs
+requiring	/man/4/keysrv
+requiring	/man/8/cs
+requoted	/man/3/cmd
+reread	/man/2/bufio
+reread	/man/3/cons
+reread	/man/6/ndb
+rereading	/man/2/draw-font
+rereads	/man/8/bootpd
+rerouting	/man/3/ip
+rerr	/man/3/dbg
+rerror	/man/1/sh-file2chan
+rerror	/man/10/styx
+rerror	/man/5/0intro
+rerror	/man/5/attach
+rerror	/man/5/error
+rerror	/man/5/flush
+rerror	/man/5/version
+rerror	/man/5/walk
+rerror	/man/8/styxchat
+rerun	/man/1/acme
+rescale	/man/9/canvas
+rescanned	/man/1/m4
+reschedule	/man/10/kproc
+reschedule	/man/10/lock
+rescheduled	/man/10/lock
+reschedules	/man/10/lock
+rescue	/man/1/mash
+rescue	/man/1/sh
+rescue	/man/1/sh-std
+rescueblock	/man/1/sh-std
+rescued	/man/1/sh
+research	/man/1/yacc
+research	/man/3/indir
+resemblance	/man/2/disks
+resemble	/man/1/diff
+resemble	/man/3/usb
+resembling	/man/1/mash
+reserve	/man/10/allocb
+reserve	/man/10/odbc
+reserve	/man/10/styx
+reserve	/man/8/ftl
+reserved	/man/10/dev
+reserved	/man/10/devattach
+reserved	/man/10/master
+reserved	/man/10/plan9.ini
+reserved	/man/2/w3c-uris
+reserved	/man/6/dis
+reserved	/man/8/prep
+reserves	/man/10/odbc
+reserves	/man/2/dhcpclient
+reserves	/man/3/cmd
+reserves	/man/3/ip
+reserves	/man/3/ssl
+reserves	/man/3/tls
+reset	/man/1/chmod
+reset	/man/1/mash-make
+reset	/man/10/9load
+reset	/man/10/conf
+reset	/man/10/dev
+reset	/man/10/devattach
+reset	/man/10/intrenable
+reset	/man/10/plan9.ini
+reset	/man/10/qio
+reset	/man/2/crc
+reset	/man/2/palmfile
+reset	/man/2/sh
+reset	/man/2/spree-objstore
+reset	/man/3/audio
+reset	/man/3/cons
+reset	/man/3/dbg
+reset	/man/3/floppy
+reset	/man/3/fpga
+reset	/man/3/ftl
+reset	/man/3/ip
+reset	/man/3/pbus
+reset	/man/3/pnp
+reset	/man/3/tv
+reset	/man/3/usb
+reset	/man/4/kfs
+reset	/man/6/man
+reset	/man/8/cs
+reset	/man/8/touchcal
+reset	/man/9/canvas
+reset	/man/9/menu
+reset	/man/9/options
+resets	/man/10/plan9.ini
+resets	/man/10/qio
+resets	/man/2/sys-millisec
+resets	/man/3/usb
+resets	/man/4/keyfs
+resets	/man/9/variable
+resetting	/man/10/error
+reshape	/man/1/wm
+reshape	/man/2/tk
+reshape	/man/2/wmclient
+reshaped	/man/1/keyboard
+reside	/man/1/acme
+reside	/man/10/9load
+reside	/man/2/draw-context
+reside	/man/3/root
+resident	/man/1/charon
+resident	/man/10/5cv
+resident	/man/2/sys-0intro
+resides	/man/1/charon
+resides	/man/10/9load
+resides	/man/2/draw-0intro
+resides	/man/2/draw-font
+resides	/man/2/draw-image
+resides	/man/2/draw-screen
+resides	/man/2/sys-stat
+resize	/man/1/charon
+resize	/man/1/ftree
+resize	/man/1/sh-tk
+resize	/man/1/wm
+resize	/man/2/tkclient
+resize	/man/2/wmclient
+resized	/man/2/wmlib
+resizing	/man/2/draw-context
+resolution	/man/10/2c
+resolution	/man/2/print
+resolution	/man/2/srv
+resolution	/man/2/sys-millisec
+resolution	/man/2/virgil
+resolution	/man/3/ip
+resolution	/man/4/iostats
+resolution	/man/6/colour
+resolution	/man/9/scale
+resolve	/man/10/2l
+resolve	/man/3/pnp
+resolve	/man/8/applylog
+resolved	/man/1/alphabet-fs
+resolved	/man/1/fs
+resolved	/man/2/virgil
+resolved	/man/2/w3c-uris
+resolver	/man/2/virgil
+resolver	/man/6/ndb
+resolver	/man/8/dns
+resolvers	/man/6/ndb
+resolvers	/man/8/dns
+resolves	/man/2/draw-image
+resolves	/man/2/w3c-uris
+resolves	/man/3/draw
+resolves	/man/8/prep
+resolving	/man/1/tiny
+resolving	/man/2/w3c-uris
+resolving	/man/6/ndb
+resorting	/man/8/dns
+resource	/man/1/charon
+resource	/man/1/grid-monitor
+resource	/man/1/grid-ns
+resource	/man/1/grid-query
+resource	/man/1/grid-register
+resource	/man/1/grid-session
+resource	/man/10/error
+resource	/man/10/kproc
+resource	/man/10/lock
+resource	/man/10/qlock
+resource	/man/2/palmfile
+resource	/man/2/sys-pctl
+resource	/man/2/w3c-uris
+resource	/man/3/draw
+resource	/man/4/grid-cpu
+resources	/man/1/0intro
+resources	/man/1/charon
+resources	/man/1/collab
+resources	/man/1/collab-clients
+resources	/man/1/grid-monitor
+resources	/man/1/grid-query
+resources	/man/1/grid-session
+resources	/man/1/kill
+resources	/man/10/error
+resources	/man/10/kproc
+resources	/man/10/lock
+resources	/man/10/qlock
+resources	/man/2/draw-image
+resources	/man/2/palmfile
+resources	/man/2/sys-fauth
+resources	/man/2/sys-pctl
+resources	/man/3/draw
+resources	/man/3/pnp
+resources	/man/3/vga
+resources	/man/4/registry
+resp	/man/2/factotum
+respect	/man/1/cp
+respect	/man/1/mk
+respect	/man/10/mk
+respect	/man/2/draw-display
+respect	/man/2/format
+respect	/man/2/tkclient
+respect	/man/2/w3c-uris
+respect	/man/2/wmclient
+respect	/man/8/create
+respect	/man/9/panel
+respect	/man/9/text
+respectable	/man/10/5cv
+respective	/man/1/sh
+respective	/man/1/wm-misc
+respective	/man/1/wm-sh
+respective	/man/2/tkclient
+respects	/man/1/mk
+respects	/man/1/yacc
+respects	/man/10/mk
+respond	/man/2/dhcpclient
+respond	/man/2/factotum
+respond	/man/2/styxservers
+respond	/man/2/tk
+respond	/man/3/usb
+respond	/man/5/0intro
+respond	/man/5/flush
+respond	/man/5/version
+responded	/man/1/sh-tk
+responded	/man/2/styxservers
+responded	/man/5/flush
+responding	/man/2/tk
+responds	/man/2/scsiio
+responds	/man/2/spree
+responds	/man/2/styxservers
+responds	/man/3/ip
+responds	/man/3/usb
+responds	/man/5/0intro
+responds	/man/5/version
+responds	/man/8/bootpd
+responds	/man/8/getauthinfo
+response	/man/1/netkey
+response	/man/1/wm
+response	/man/10/9load
+response	/man/10/plan9.ini
+response	/man/10/xalloc
+response	/man/2/factotum
+response	/man/2/spree
+response	/man/2/styxservers
+response	/man/2/sys-file2chan
+response	/man/2/wmsrv
+response	/man/3/draw
+response	/man/3/ip
+response	/man/3/usb
+response	/man/5/0intro
+response	/man/5/flush
+response	/man/5/version
+response	/man/6/colour
+response	/man/7/cddb
+response	/man/8/plumber
+response	/man/8/rstyxd
+response	/man/8/signer
+response	/man/8/virgild
+response	/man/9/text
+responses	/man/2/tk
+responses	/man/3/usb
+responses	/man/5/0intro
+responsibility	/man/10/dynld
+responsibility	/man/10/print
+responsibility	/man/2/sys-stat
+responsibilty	/man/3/prog
+responsible	/man/10/intrenable
+responsible	/man/10/kbdputc
+responsible	/man/10/parsecmd
+responsible	/man/2/alphabet-intro
+responsible	/man/2/spree
+responsible	/man/2/sys-stat
+responsible	/man/3/tls
+responsible	/man/3/vid
+responsive	/man/9/button
+responsive	/man/9/checkbutton
+responsive	/man/9/menu
+responsive	/man/9/menubutton
+responsive	/man/9/radiobutton
+rest	/man/1/mux
+rest	/man/1/p
+rest	/man/1/sh-tk
+rest	/man/2/arg
+rest	/man/2/filter-deflate
+rest	/man/2/prefab-element
+rest	/man/2/rfc822
+rest	/man/2/sets
+rest	/man/2/sexprs
+rest	/man/2/sh
+rest	/man/2/spree
+rest	/man/2/xml
+rest	/man/3/cons
+rest	/man/3/sd
+rest	/man/4/archfs
+rest	/man/4/registry
+rest	/man/5/walk
+rest	/man/6/attrdb
+rest	/man/6/colour
+rest	/man/6/image
+rest	/man/6/sbl
+rest	/man/9/0intro
+rest	/man/9/grid
+restart	/man/10/qio
+restart	/man/2/spree
+restarted	/man/3/dbg
+restarted	/man/4/factotum
+restarted	/man/8/shutdown
+restarts	/man/3/kprof
+restarts	/man/8/shutdown
+restore	/man/1/acme
+restore	/man/2/math-fp
+restore	/man/3/touch
+restore	/man/6/dis
+restore	/man/6/man
+restore	/man/6/ubfa
+restore	/man/8/prep
+restored	/man/1/sh
+restored	/man/1/sh-std
+restored	/man/10/lock
+restored	/man/10/qio
+restored	/man/2/spree-cardlib
+restored	/man/2/spree-gather
+restored	/man/2/spree-objstore
+restored	/man/3/touch
+restored	/man/9/button
+restores	/man/1/m4
+restores	/man/10/splhi
+restores	/man/2/draw-image
+restores	/man/2/prefab-compound
+restores	/man/3/flash
+restores	/man/8/mkfs
+restoring	/man/2/spree-allow
+restoring	/man/2/spree-objstore
+restrict	/man/1/cprof
+restrict	/man/1/mprof
+restrict	/man/1/prof
+restrict	/man/8/styxmon
+restricted	/man/1/0intro
+restricted	/man/1/acme
+restricted	/man/10/qio
+restricted	/man/2/draw-image
+restricted	/man/2/ip
+restricted	/man/2/security-0intro
+restricted	/man/2/sexprs
+restricted	/man/3/ip
+restricted	/man/3/prog
+restricted	/man/3/sign
+restricted	/man/5/0intro
+restricted	/man/6/sexprs
+restricted	/man/8/applylog
+restricted	/man/8/bootpd
+restricted	/man/8/rip
+restricted	/man/9/1copyright
+restricting	/man/4/factotum
+restriction	/man/2/asn1
+restriction	/man/2/spki
+restriction	/man/2/sys-self
+restriction	/man/5/0intro
+restriction	/man/5/walk
+restriction	/man/6/sexprs
+restrictions	/man/1/mk
+restrictions	/man/1/sh
+restrictions	/man/10/dmainit
+restrictions	/man/10/intrenable
+restrictions	/man/10/mk
+restrictions	/man/10/xalloc
+restrictions	/man/2/draw-display
+restrictions	/man/2/rfc822
+restrictions	/man/2/security-0intro
+restrictions	/man/2/sys-0intro
+restrictions	/man/3/flash
+restrictions	/man/3/ftl
+restrictions	/man/4/logfile
+restrictions	/man/5/walk
+restrictions	/man/9/pack
+restricts	/man/1/charon
+restricts	/man/10/2l
+restricts	/man/10/plan9.ini
+restricts	/man/10/srclist
+restricts	/man/2/prefab-compound
+restricts	/man/4/acme
+restricts	/man/9/grab
+resultant	/man/2/asn1
+resultant	/man/9/image
+resulted	/man/2/wait
+resulting	/man/1/acme
+resulting	/man/1/alphabet-abc
+resulting	/man/1/alphabet-fs
+resulting	/man/1/alphabet-grid
+resulting	/man/1/alphabet-main
+resulting	/man/1/collab
+resulting	/man/1/cook
+resulting	/man/1/emu
+resulting	/man/1/fs
+resulting	/man/1/plumb
+resulting	/man/1/wm
+resulting	/man/10/allocb
+resulting	/man/10/c2l
+resulting	/man/10/conf
+resulting	/man/10/devattach
+resulting	/man/10/dynld
+resulting	/man/10/odbc
+resulting	/man/10/parsecmd
+resulting	/man/10/print
+resulting	/man/10/styx
+resulting	/man/2/0intro
+resulting	/man/2/alphabet-intro
+resulting	/man/2/asn1
+resulting	/man/2/bufio
+resulting	/man/2/complete
+resulting	/man/2/dial
+resulting	/man/2/diskblocks
+resulting	/man/2/disks
+resulting	/man/2/draw-image
+resulting	/man/2/draw-rect
+resulting	/man/2/factotum
+resulting	/man/2/filter-slip
+resulting	/man/2/keyring-certtostr
+resulting	/man/2/popup
+resulting	/man/2/prefab-compound
+resulting	/man/2/prefab-element
+resulting	/man/2/pslib
+resulting	/man/2/readdir
+resulting	/man/2/scsiio
+resulting	/man/2/secstore
+resulting	/man/2/security-ssl
+resulting	/man/2/string
+resulting	/man/2/styx
+resulting	/man/2/styxpersist
+resulting	/man/2/sys-byte2char
+resulting	/man/2/sys-dial
+resulting	/man/2/sys-dirread
+resulting	/man/2/sys-fd2path
+resulting	/man/2/sys-tokenize
+resulting	/man/2/tk
+resulting	/man/2/w3c-uris
+resulting	/man/2/wait
+resulting	/man/2/xml
+resulting	/man/3/i2c
+resulting	/man/3/indir
+resulting	/man/3/ip
+resulting	/man/3/srv9
+resulting	/man/3/ssl
+resulting	/man/4/keysrv
+resulting	/man/4/registry
+resulting	/man/5/0intro
+resulting	/man/6/plumbing
+resulting	/man/6/proto
+resulting	/man/6/sexprs
+resulting	/man/8/applylog
+resulting	/man/8/collabsrv
+resulting	/man/8/cs
+resulting	/man/8/dhcp
+resulting	/man/9/0intro
+resulting	/man/9/canvas
+results	/man/1/0intro
+results	/man/1/blur
+results	/man/1/calc
+results	/man/1/charon
+results	/man/1/cmp
+results	/man/1/collab-clients
+results	/man/1/cook
+results	/man/1/cprof
+results	/man/1/deb
+results	/man/1/emu
+results	/man/1/itest
+results	/man/1/miniterm
+results	/man/1/os
+results	/man/10/allocb
+results	/man/10/odbc
+results	/man/10/print
+results	/man/2/dhcpclient
+results	/man/2/dis
+results	/man/2/draw-rect
+results	/man/2/itslib
+results	/man/2/prefab-style
+results	/man/2/prof
+results	/man/2/srv
+results	/man/2/sys-0intro
+results	/man/2/sys-print
+results	/man/2/sys-utfbytes
+results	/man/2/w3c-uris
+results	/man/3/cons
+results	/man/3/dup
+results	/man/3/ftl
+results	/man/3/ip
+results	/man/3/mpeg
+results	/man/3/touch
+results	/man/5/walk
+results	/man/6/colour
+results	/man/6/image
+results	/man/7/db
+results	/man/8/cs
+results	/man/8/dhcp
+results	/man/8/dns
+results	/man/9/canvas
+results	/man/9/pack
+resume	/man/6/sexprs
+resumed	/man/1/strings
+resumes	/man/1/deb
+resumption	/man/2/spree-gather
+resx	/man/2/print
+resy	/man/2/print
+retain	/man/2/dhcpclient
+retain	/man/9/grid
+retain	/man/9/pack
+retained	/man/9/1copyright
+retained	/man/9/text
+retaining	/man/9/text
+retransmission	/man/10/allocb
+retransmission	/man/10/qio
+retransmissions	/man/2/keyring-rc4
+retransmitted	/man/3/ip
+retransmitting	/man/2/virgil
+retried	/man/1/mash
+retries	/man/10/malloc
+retries	/man/8/dhcp
+retrievable	/man/2/spree-objstore
+retrieval	/man/1/charon
+retrieval	/man/2/spree-objstore
+retrieve	/man/1/charon
+retrieve	/man/1/secstore
+retrieve	/man/1/sh-file2chan
+retrieve	/man/10/devattach
+retrieve	/man/10/odbc
+retrieve	/man/10/styx
+retrieve	/man/2/filter-slip
+retrieve	/man/2/spree-objstore
+retrieve	/man/2/sys-stat
+retrieve	/man/2/tkclient
+retrieve	/man/2/venti
+retrieve	/man/2/wmclient
+retrieve	/man/2/wmlib
+retrieve	/man/3/dbg
+retrieve	/man/3/sd
+retrieve	/man/4/factotum
+retrieve	/man/4/palmsrv
+retrieve	/man/8/dns
+retrieved	/man/1/alphabet-fs
+retrieved	/man/1/fs
+retrieved	/man/1/vacget
+retrieved	/man/2/dhcpclient
+retrieved	/man/2/draw-image
+retrieved	/man/2/sh
+retrieved	/man/2/spree-cardlib
+retrieved	/man/2/sys-0intro
+retrieved	/man/3/prog
+retrieved	/man/4/factotum
+retrieved	/man/8/kfscmd
+retrieved	/man/9/listbox
+retrieves	/man/1/mash-tk
+retrieves	/man/1/sh-file2chan
+retrieves	/man/1/vacget
+retrieves	/man/2/palmfile
+retrieves	/man/2/secstore
+retrieves	/man/2/sh
+retrieves	/man/5/0intro
+retrieving	/man/2/spree
+retrofit	/man/2/msgio
+retry	/man/4/factotum
+retry	/man/8/dhcp
+return	/man/1/0intro
+return	/man/1/acme
+return	/man/1/alphabet-main
+return	/man/1/calc
+return	/man/1/collab-clients
+return	/man/1/deb
+return	/man/1/grid-query
+return	/man/1/limbo
+return	/man/1/mash
+return	/man/1/mash-tk
+return	/man/1/miniterm
+return	/man/1/mk
+return	/man/1/mux
+return	/man/1/pwd
+return	/man/1/sh
+return	/man/1/sh-alphabet
+return	/man/1/sh-file2chan
+return	/man/1/sh-sexprs
+return	/man/1/sh-std
+return	/man/1/sh-string
+return	/man/1/sh-tk
+return	/man/1/stream
+return	/man/1/tkcmd
+return	/man/1/yacc
+return	/man/10/acid
+return	/man/10/c2l
+return	/man/10/dev
+return	/man/10/devattach
+return	/man/10/dynld
+return	/man/10/error
+return	/man/10/getfields
+return	/man/10/malloc
+return	/man/10/mk
+return	/man/10/newchan
+return	/man/10/odbc
+return	/man/10/print
+return	/man/10/qio
+return	/man/10/qlock
+return	/man/10/readnum
+return	/man/10/rune
+return	/man/10/seconds
+return	/man/10/styx
+return	/man/10/styxserver
+return	/man/2/0intro
+return	/man/2/alphabet-intro
+return	/man/2/arg
+return	/man/2/asn1
+return	/man/2/attrdb
+return	/man/2/bloomfilter
+return	/man/2/bufio
+return	/man/2/bufio-chanfill
+return	/man/2/cfg
+return	/man/2/command
+return	/man/2/complete
+return	/man/2/convcs
+return	/man/2/csv
+return	/man/2/dbm
+return	/man/2/debug
+return	/man/2/dhcpclient
+return	/man/2/dial
+return	/man/2/diskblocks
+return	/man/2/disks
+return	/man/2/draw-0intro
+return	/man/2/draw-display
+return	/man/2/draw-font
+return	/man/2/draw-image
+return	/man/2/draw-screen
+return	/man/2/exception
+return	/man/2/factotum
+return	/man/2/format
+return	/man/2/hash
+return	/man/2/ip
+return	/man/2/ir
+return	/man/2/json
+return	/man/2/keyring-certtostr
+return	/man/2/keyring-getmsg
+return	/man/2/keyring-getstring
+return	/man/2/lists
+return	/man/2/math-fp
+return	/man/2/msgio
+return	/man/2/names
+return	/man/2/palmfile
+return	/man/2/pop3
+return	/man/2/prefab-compound
+return	/man/2/prof
+return	/man/2/rand
+return	/man/2/registries
+return	/man/2/rfc822
+return	/man/2/scsiio
+return	/man/2/security-random
+return	/man/2/sexprs
+return	/man/2/sh
+return	/man/2/spki
+return	/man/2/spree
+return	/man/2/spree-cardlib
+return	/man/2/spree-gather
+return	/man/2/spree-objstore
+return	/man/2/string
+return	/man/2/styx
+return	/man/2/styxconv
+return	/man/2/styxflush
+return	/man/2/styxservers
+return	/man/2/styxservers-nametree
+return	/man/2/sys-0intro
+return	/man/2/sys-bind
+return	/man/2/sys-dial
+return	/man/2/sys-dirread
+return	/man/2/sys-fauth
+return	/man/2/sys-fd2path
+return	/man/2/sys-file2chan
+return	/man/2/sys-iounit
+return	/man/2/sys-open
+return	/man/2/sys-pctl
+return	/man/2/sys-pipe
+return	/man/2/sys-print
+return	/man/2/sys-read
+return	/man/2/sys-sleep
+return	/man/2/sys-stat
+return	/man/2/tk
+return	/man/2/translate
+return	/man/2/ubfa
+return	/man/2/w3c-uris
+return	/man/2/wmsrv
+return	/man/2/xml
+return	/man/3/arch
+return	/man/3/audio
+return	/man/3/boot
+return	/man/3/cons
+return	/man/3/dbg
+return	/man/3/draw
+return	/man/3/flash
+return	/man/3/ip
+return	/man/3/lpt
+return	/man/3/mnt
+return	/man/3/pipe
+return	/man/3/plap
+return	/man/3/prog
+return	/man/3/ssl
+return	/man/3/tls
+return	/man/3/touch
+return	/man/3/usb
+return	/man/3/vid
+return	/man/4/dbfs
+return	/man/4/factotum
+return	/man/4/import
+return	/man/4/logfile
+return	/man/4/ramfile
+return	/man/4/registry
+return	/man/4/spree
+return	/man/5/error
+return	/man/5/open
+return	/man/5/walk
+return	/man/6/json
+return	/man/6/keyboard
+return	/man/6/man
+return	/man/6/sbl
+return	/man/6/sexprs
+return	/man/6/translate
+return	/man/6/ubfa
+return	/man/7/db
+return	/man/8/collabsrv
+return	/man/8/cs
+return	/man/9/0intro
+return	/man/9/button
+return	/man/9/canvas
+return	/man/9/checkbutton
+return	/man/9/choicebutton
+return	/man/9/grid
+return	/man/9/radiobutton
+return	/man/9/scale
+return	/man/9/send
+return	/man/9/text
+return0	/man/10/sleep
+returned	/man/1/calc
+returned	/man/1/m4
+returned	/man/1/mash-tk
+returned	/man/1/sh-file2chan
+returned	/man/1/sh-tk
+returned	/man/1/tktester
+returned	/man/1/yacc
+returned	/man/10/atoi
+returned	/man/10/devattach
+returned	/man/10/malloc
+returned	/man/10/odbc
+returned	/man/10/print
+returned	/man/10/qio
+returned	/man/10/readnum
+returned	/man/10/splhi
+returned	/man/10/styx
+returned	/man/10/styxserver
+returned	/man/10/xalloc
+returned	/man/2/alphabet-intro
+returned	/man/2/arg
+returned	/man/2/asn1
+returned	/man/2/bufio
+returned	/man/2/cfg
+returned	/man/2/convcs
+returned	/man/2/crc
+returned	/man/2/dbm
+returned	/man/2/debug
+returned	/man/2/devpointer
+returned	/man/2/dial
+returned	/man/2/dialog
+returned	/man/2/diskblocks
+returned	/man/2/disks
+returned	/man/2/exception
+returned	/man/2/geodesy
+returned	/man/2/ida
+returned	/man/2/imagefile
+returned	/man/2/ip
+returned	/man/2/keyring-auth
+returned	/man/2/keyring-gensk
+returned	/man/2/keyring-sha1
+returned	/man/2/math-fp
+returned	/man/2/names
+returned	/man/2/palmfile
+returned	/man/2/popup
+returned	/man/2/prefab-compound
+returned	/man/2/prof
+returned	/man/2/readdir
+returned	/man/2/rfc822
+returned	/man/2/scsiio
+returned	/man/2/secstore
+returned	/man/2/security-login
+returned	/man/2/security-ssl
+returned	/man/2/selectfile
+returned	/man/2/sets
+returned	/man/2/sh
+returned	/man/2/spki
+returned	/man/2/spree-allow
+returned	/man/2/spree-cardlib
+returned	/man/2/string
+returned	/man/2/styx
+returned	/man/2/styxservers
+returned	/man/2/sys-0intro
+returned	/man/2/sys-byte2char
+returned	/man/2/sys-dial
+returned	/man/2/sys-export
+returned	/man/2/sys-fd2path
+returned	/man/2/sys-fversion
+returned	/man/2/sys-open
+returned	/man/2/sys-pipe
+returned	/man/2/sys-read
+returned	/man/2/sys-seek
+returned	/man/2/sys-stat
+returned	/man/2/sys-tokenize
+returned	/man/2/tabs
+returned	/man/2/timers
+returned	/man/2/tk
+returned	/man/2/w3c-css
+returned	/man/2/wmsrv
+returned	/man/2/xml
+returned	/man/3/cons
+returned	/man/3/dup
+returned	/man/3/ftl
+returned	/man/3/i2c
+returned	/man/3/mnt
+returned	/man/3/pipe
+returned	/man/3/rtc
+returned	/man/3/sd
+returned	/man/3/srv9
+returned	/man/3/tls
+returned	/man/3/tv
+returned	/man/4/acme
+returned	/man/4/factotum
+returned	/man/4/keyfs
+returned	/man/5/attach
+returned	/man/5/open
+returned	/man/5/read
+returned	/man/5/stat
+returned	/man/5/walk
+returned	/man/7/db
+returned	/man/9/canvas
+returned	/man/9/choicebutton
+returned	/man/9/grid
+returned	/man/9/listbox
+returned	/man/9/menu
+returned	/man/9/pack
+returned	/man/9/panel
+returned	/man/9/scale
+returned	/man/9/text
+returned	/man/9/variable
+returning	/man/1/deb
+returning	/man/1/mk
+returning	/man/1/sh-std
+returning	/man/1/sleep
+returning	/man/1/stream
+returning	/man/10/devattach
+returning	/man/10/error
+returning	/man/10/mk
+returning	/man/10/odbc
+returning	/man/10/qio
+returning	/man/10/styx
+returning	/man/10/styxserver
+returning	/man/2/alphabet-intro
+returning	/man/2/bufio
+returning	/man/2/dial
+returning	/man/2/diskblocks
+returning	/man/2/draw-0intro
+returning	/man/2/format
+returning	/man/2/keyring-ipint
+returning	/man/2/lists
+returning	/man/2/math-fp
+returning	/man/2/mpeg
+returning	/man/2/palmfile
+returning	/man/2/readdir
+returning	/man/2/scsiio
+returning	/man/2/secstore
+returning	/man/2/sexprs
+returning	/man/2/sh
+returning	/man/2/spree
+returning	/man/2/styxservers
+returning	/man/2/sys-dirread
+returning	/man/2/sys-read
+returning	/man/2/sys-sleep
+returning	/man/2/tftp
+returning	/man/2/translate
+returning	/man/2/venti
+returning	/man/2/w3c-xpointers
+returning	/man/3/mpeg
+returning	/man/4/acme
+returning	/man/6/sbl
+returns	/man/1/alphabet-main
+returns	/man/1/grep
+returns	/man/1/grid-session
+returns	/man/1/m4
+returns	/man/1/mash-tk
+returns	/man/1/mkdir
+returns	/man/1/read
+returns	/man/1/sh-alphabet
+returns	/man/1/sh-expr
+returns	/man/1/sh-sexprs
+returns	/man/1/sh-std
+returns	/man/1/sh-string
+returns	/man/1/sleep
+returns	/man/10/acid
+returns	/man/10/allocb
+returns	/man/10/devattach
+returns	/man/10/dmainit
+returns	/man/10/dynld
+returns	/man/10/error
+returns	/man/10/eve
+returns	/man/10/intrenable
+returns	/man/10/kproc
+returns	/man/10/lock
+returns	/man/10/malloc
+returns	/man/10/memory
+returns	/man/10/newchan
+returns	/man/10/odbc
+returns	/man/10/parsecmd
+returns	/man/10/print
+returns	/man/10/qio
+returns	/man/10/qlock
+returns	/man/10/ref
+returns	/man/10/rune
+returns	/man/10/seconds
+returns	/man/10/sleep
+returns	/man/10/splhi
+returns	/man/10/strcat
+returns	/man/10/styx
+returns	/man/10/styxserver
+returns	/man/10/xalloc
+returns	/man/2/alphabet-intro
+returns	/man/2/asn1
+returns	/man/2/attrdb
+returns	/man/2/bloomfilter
+returns	/man/2/bufio-chanfill
+returns	/man/2/cfg
+returns	/man/2/complete
+returns	/man/2/convcs
+returns	/man/2/crc
+returns	/man/2/csv
+returns	/man/2/daytime
+returns	/man/2/dbm
+returns	/man/2/debug
+returns	/man/2/devpointer
+returns	/man/2/dhcpclient
+returns	/man/2/dial
+returns	/man/2/dialog
+returns	/man/2/dict
+returns	/man/2/dis
+returns	/man/2/diskblocks
+returns	/man/2/disks
+returns	/man/2/dividers
+returns	/man/2/draw-display
+returns	/man/2/draw-font
+returns	/man/2/draw-image
+returns	/man/2/draw-point
+returns	/man/2/draw-rect
+returns	/man/2/drawmux
+returns	/man/2/encoding
+returns	/man/2/env
+returns	/man/2/ether
+returns	/man/2/exception
+returns	/man/2/factotum
+returns	/man/2/filepat
+returns	/man/2/filter
+returns	/man/2/filter-slip
+returns	/man/2/format
+returns	/man/2/hash
+returns	/man/2/ida
+returns	/man/2/imagefile
+returns	/man/2/ip
+returns	/man/2/ir
+returns	/man/2/itslib
+returns	/man/2/json
+returns	/man/2/keyring-0intro
+returns	/man/2/keyring-auth
+returns	/man/2/keyring-certtostr
+returns	/man/2/keyring-gensk
+returns	/man/2/keyring-getmsg
+returns	/man/2/keyring-getstring
+returns	/man/2/keyring-ipint
+returns	/man/2/keyring-rc4
+returns	/man/2/keyring-sha1
+returns	/man/2/keyset
+returns	/man/2/lists
+returns	/man/2/lock
+returns	/man/2/math-elem
+returns	/man/2/math-fp
+returns	/man/2/mpeg
+returns	/man/2/msgio
+returns	/man/2/palmfile
+returns	/man/2/plumbmsg
+returns	/man/2/pop3
+returns	/man/2/popup
+returns	/man/2/prefab-compound
+returns	/man/2/print
+returns	/man/2/prof
+returns	/man/2/readdir
+returns	/man/2/regex
+returns	/man/2/registries
+returns	/man/2/rfc822
+returns	/man/2/scsiio
+returns	/man/2/secstore
+returns	/man/2/security-0intro
+returns	/man/2/security-auth
+returns	/man/2/security-login
+returns	/man/2/security-random
+returns	/man/2/security-ssl
+returns	/man/2/sets
+returns	/man/2/sexprs
+returns	/man/2/sh
+returns	/man/2/smtp
+returns	/man/2/spki
+returns	/man/2/spki-verifier
+returns	/man/2/spree
+returns	/man/2/spree-allow
+returns	/man/2/spree-cardlib
+returns	/man/2/spree-gather
+returns	/man/2/srv
+returns	/man/2/string
+returns	/man/2/stringinttab
+returns	/man/2/styx
+returns	/man/2/styxflush
+returns	/man/2/styxpersist
+returns	/man/2/styxservers
+returns	/man/2/styxservers-nametree
+returns	/man/2/sys-byte2char
+returns	/man/2/sys-chdir
+returns	/man/2/sys-dial
+returns	/man/2/sys-dirread
+returns	/man/2/sys-dup
+returns	/man/2/sys-export
+returns	/man/2/sys-fauth
+returns	/man/2/sys-fd2path
+returns	/man/2/sys-file2chan
+returns	/man/2/sys-fversion
+returns	/man/2/sys-iounit
+returns	/man/2/sys-millisec
+returns	/man/2/sys-open
+returns	/man/2/sys-pipe
+returns	/man/2/sys-print
+returns	/man/2/sys-read
+returns	/man/2/sys-remove
+returns	/man/2/sys-sleep
+returns	/man/2/sys-tokenize
+returns	/man/2/sys-utfbytes
+returns	/man/2/sys-werrstr
+returns	/man/2/tabs
+returns	/man/2/tftp
+returns	/man/2/timers
+returns	/man/2/tk
+returns	/man/2/tkclient
+returns	/man/2/translate
+returns	/man/2/ubfa
+returns	/man/2/venti
+returns	/man/2/virgil
+returns	/man/2/w3c-css
+returns	/man/2/w3c-uris
+returns	/man/2/w3c-xpointers
+returns	/man/2/wait
+returns	/man/2/wmclient
+returns	/man/2/wmlib
+returns	/man/2/wmsrv
+returns	/man/2/workdir
+returns	/man/2/xml
+returns	/man/3/cmd
+returns	/man/3/cons
+returns	/man/3/dbg
+returns	/man/3/draw
+returns	/man/3/dup
+returns	/man/3/dynld
+returns	/man/3/ether
+returns	/man/3/flash
+returns	/man/3/floppy
+returns	/man/3/fpga
+returns	/man/3/gpio
+returns	/man/3/i2c
+returns	/man/3/i82365
+returns	/man/3/indir
+returns	/man/3/ip
+returns	/man/3/mnt
+returns	/man/3/mpeg
+returns	/man/3/pbus
+returns	/man/3/plap
+returns	/man/3/pnp
+returns	/man/3/pointer
+returns	/man/3/prof
+returns	/man/3/rtc
+returns	/man/3/sd
+returns	/man/3/snarf
+returns	/man/3/switch
+returns	/man/3/tls
+returns	/man/3/touch
+returns	/man/3/usb
+returns	/man/3/vga
+returns	/man/4/acme
+returns	/man/4/dbfs
+returns	/man/4/export
+returns	/man/4/factotum
+returns	/man/4/import
+returns	/man/4/keysrv
+returns	/man/4/palmsrv
+returns	/man/4/registry
+returns	/man/5/0intro
+returns	/man/5/attach
+returns	/man/5/clunk
+returns	/man/5/read
+returns	/man/7/db
+returns	/man/8/collabsrv
+returns	/man/8/cs
+returns	/man/8/dhcp
+returns	/man/8/httpd
+returns	/man/8/plumber
+returns	/man/8/rdbgsrv
+returns	/man/9/bind
+returns	/man/9/button
+returns	/man/9/canvas
+returns	/man/9/checkbutton
+returns	/man/9/choicebutton
+returns	/man/9/entry
+returns	/man/9/focus
+returns	/man/9/frame
+returns	/man/9/grab
+returns	/man/9/grid
+returns	/man/9/image
+returns	/man/9/label
+returns	/man/9/listbox
+returns	/man/9/menu
+returns	/man/9/menubutton
+returns	/man/9/options
+returns	/man/9/pack
+returns	/man/9/panel
+returns	/man/9/radiobutton
+returns	/man/9/scale
+returns	/man/9/scrollbar
+returns	/man/9/see
+returns	/man/9/text
+returns	/man/9/variable
+reusable	/man/3/ftl
+reuse	/man/10/newchan
+reuse	/man/10/plan9.ini
+reuse	/man/2/spree
+reused	/man/2/keyring-crypt
+reused	/man/2/spree
+reused	/man/3/logfs
+reused	/man/3/pipe
+reused	/man/5/0intro
+reused	/man/5/clunk
+reuses	/man/2/security-0intro
+reusing	/man/5/flush
+rev	/man/2/lists
+reveal	/man/1/grid-query
+reveal	/man/1/grid-session
+reveal	/man/1/stack
+reveal	/man/2/draw-example
+reveal	/man/2/secstore
+revealing	/man/10/ref
+revealing	/man/2/security-0intro
+revealing	/man/3/flash
+reveals	/man/3/cmd
+reverse	/man/1/acme
+reverse	/man/1/calc
+reverse	/man/1/fc
+reverse	/man/1/ls
+reverse	/man/1/sh-expr
+reverse	/man/1/tail
+reverse	/man/1/xd
+reverse	/man/10/styx
+reverse	/man/2/lists
+reverse	/man/2/stringinttab
+reverse	/man/3/dbg
+reverse	/man/3/ds
+reverse	/man/6/ubfa
+reversed	/man/1/acme
+reversed	/man/10/2l
+reversed	/man/2/readdir
+reverses	/man/1/sh
+reverses	/man/1/sort
+reverses	/man/1/wm-sh
+reverses	/man/2/math-export
+reverses	/man/2/spree-allow
+reverses	/man/2/spree-cardlib
+reverses	/man/9/update
+reversi	/man/1/wm-misc
+reversi.b	/man/1/wm-misc
+reversing	/man/2/spree-cardlib
+revert	/man/9/cursor
+reverts	/man/1/sh-alphabet
+review	/man/1/charon
+revised	/man/1/charon
+revised	/man/10/allocb
+revision	/man/1/emu
+revision	/man/2/w3c-css
+revisiting	/man/1/charon
+revlookup	/man/2/stringinttab
+rew	/man/2/ir
+rewind	/man/1/mux
+rewind	/man/2/ir
+rewritable	/man/3/ftl
+rewrite	/man/1/alphabet-main
+rewrite	/man/1/sh-alphabet
+rewrite	/man/10/c2l
+rewrite	/man/2/sys-open
+rewrite	/man/4/dbfs
+rewrites	/man/1/alphabet-main
+rewrites	/man/1/tee
+rewriting	/man/1/sh-alphabet
+rewriting	/man/10/kstrip
+rewritten	/man/1/alphabet-main
+rewritten	/man/1/sh-alphabet
+rewritten	/man/3/ftl
+rewritten	/man/3/tinyfs
+rewritten	/man/4/dbfs
+rexec	/man/1/alphabet-abc
+rexec	/man/1/alphabet-grid
+rf	/man/1/tail
+rfc	/man/2/xml
+rfc	/man/6/json
+rfc1055	/man/2/filter-slip
+rfc1058	/man/8/rip
+rfc1123	/man/2/daytime
+rfc1361	/man/8/sntp
+rfc1942	/man/1/charon
+rfc1950	/man/2/filter-deflate
+rfc1951	/man/2/filter-deflate
+rfc1952	/man/2/filter-deflate
+rfc2045	/man/2/rfc822
+rfc2104	/man/2/keyring-sha1
+rfc2131	/man/2/dhcpclient
+rfc2373	/man/2/ip
+rfc2453	/man/8/rip
+rfc2822	/man/2/rfc822
+rfc3548	/man/2/encoding
+rfc3986	/man/2/w3c-uris
+rfc4627	/man/2/json
+rfc4627	/man/6/json
+rfc822	/man/2/daytime
+rfc822	/man/2/rfc822
+rfc822's	/man/2/rfc822
+rfc822.b	/man/2/rfc822
+rfc822.m	/man/2/rfc822
+rfc850	/man/2/daytime
+rfc854	/man/1/telnet
+rfclex	/man/2/rfc822
+rfclex.mk	/man/2/rfc822
+rfcs	/man/1/telnet
+rfcs	/man/2/filter-deflate
+rfl	/man/2/rfc822
+rflush	/man/5/0intro
+rflush	/man/5/flush
+rflush	/man/8/styxchat
+rgb	/man/1/wm-misc
+rgb	/man/2/draw-0intro
+rgb	/man/2/draw-display
+rgb	/man/2/imagefile
+rgb	/man/2/w3c-css
+rgb	/man/9/types
+rgb16	/man/2/draw-display
+rgb24	/man/2/draw-display
+rgb24	/man/6/colour
+rgb2cmap	/man/2/draw-display
+rgb524hwgc	/man/3/vga
+rgba	/man/2/draw-0intro
+rgba	/man/2/draw-display
+rgba	/man/2/draw-screen
+rgba	/man/2/tk
+rgba32	/man/2/draw-display
+rgbv	/man/2/draw-display
+rgbv	/man/6/colour
+rgbv	/man/8/collabsrv
+rget	/man/1/sh-file2chan
+rh	/man/3/tv
+ri	/man/6/man
+rich	/man/1/0intro
+ridge	/man/4/dossrv
+ridge	/man/9/options
+ridge	/man/9/types
+riesling	/man/10/odbc
+rightmost	/man/2/string
+rights	/man/2/spki
+rights	/man/3/fs
+rights	/man/9/1copyright
+rigid	/man/6/font
+rigorously	/man/2/security-0intro
+rijndael	/man/1/crypt
+rijndael	/man/2/keyring-crypt
+rimagefile	/man/2/imagefile
+rings	/man/2/translate
+rint	/man/2/math-fp
+rinuse	/man/2/palmfile
+rio	/man/1/9win
+rip	/man/8/rip
+rip.b	/man/8/rip
+ripoff	/man/9/canvas
+riscos	/man/10/5cv
+rise	/man/1/charon
+rise	/man/9/pack
+ritchie	/man/1/m4
+rivest	/man/2/sexprs
+rivest	/man/6/sexprs
+rkill	/man/3/dbg
+rl	/man/3/tv
+rl	/man/6/man
+rlock	/man/10/qlock
+rm	/man/1/alphabet-fs
+rm	/man/1/fs
+rm	/man/1/mkdir
+rm	/man/1/mv
+rm	/man/1/rm
+rm	/man/1/tiny
+rm	/man/10/mk
+rm	/man/2/arg
+rm	/man/2/styxflush
+rm	/man/3/srv
+rm	/man/4/kfs
+rm	/man/4/registry
+rm.b	/man/1/rm
+rm.b	/man/1/tiny
+rm.pack	/man/2/styxflush
+rmargin	/man/9/text
+rmcat	/man/2/palmfile
+rmget	/man/3/dbg
+rmput	/man/3/dbg
+rmsg	/man/2/styx
+rmsg	/man/2/styxflush
+rmsg	/man/2/styxservers
+rmsg	/man/8/styxchat
+rmsg.create	/man/2/styxservers
+rmsg.error	/man/2/styxflush
+rmsg.error	/man/2/styxservers
+rmsg.open	/man/2/styxflush
+rmsg.open	/man/2/styxservers
+rmsg.qids	/man/2/styx
+rmsg.read	/man/2/styx
+rmsg.read	/man/2/styxflush
+rmsg.read	/man/2/styxservers
+rmsg.read	/man/8/styxchat
+rmsg.tag	/man/2/styx
+rmsg.unpack	/man/2/styx
+rmsg.version	/man/2/styx
+rnd	/man/2/math-fp
+rngtab	/man/2/prof
+road	/man/6/attrdb
+rob	/man/1/acme
+rob	/man/1/yacc
+rob	/man/10/2a
+rob	/man/10/2c
+rob	/man/10/2l
+robotics	/man/3/pbus
+rock	/man/4/dossrv
+rogue	/man/8/rip
+role	/man/4/factotum
+role	/man/6/login
+rollback	/man/10/odbc
+rolled	/man/1/0intro
+rom	/man/10/5cv
+rom	/man/3/sd
+rom	/man/8/manufacture
+rom	/man/8/register
+roman	/man/1/brutus
+roman	/man/1/cook
+roman	/man/1/grep
+roman	/man/6/man
+rook	/man/6/keyboard
+root	/man/1/0intro
+root	/man/1/alphabet-abc
+root	/man/1/alphabet-fs
+root	/man/1/alphabet-grid
+root	/man/1/alphabet-main
+root	/man/1/emu
+root	/man/1/fs
+root	/man/1/ftree
+root	/man/1/rcmd
+root	/man/1/sh-alphabet
+root	/man/1/tiny
+root	/man/1/vacget
+root	/man/10/9load
+root	/man/10/conf
+root	/man/10/dev
+root	/man/10/devattach
+root	/man/10/kproc
+root	/man/10/master
+root	/man/10/newchan
+root	/man/10/ntsrv
+root	/man/10/plan9.ini
+root	/man/10/srclist
+root	/man/10/styxserver
+root	/man/2/alphabet-intro
+root	/man/2/dial
+root	/man/2/dis
+root	/man/2/drawmux
+root	/man/2/fsproto
+root	/man/2/keyring-gensk
+root	/man/2/math-elem
+root	/man/2/names
+root	/man/2/spree
+root	/man/2/spree-cardlib
+root	/man/2/styxservers
+root	/man/2/styxservers-nametree
+root	/man/2/sys-0intro
+root	/man/2/sys-bind
+root	/man/2/sys-pctl
+root	/man/2/venti
+root	/man/2/w3c-xpointers
+root	/man/3/0intro
+root	/man/3/cmd
+root	/man/3/fs
+root	/man/3/root
+root	/man/3/sign
+root	/man/3/tinyfs
+root	/man/4/9srvfs
+root	/man/4/ftpfs
+root	/man/4/memfs
+root	/man/4/namespace
+root	/man/4/spree
+root	/man/4/vacfs
+root	/man/5/0intro
+root	/man/5/attach
+root	/man/5/stat
+root	/man/5/walk
+root	/man/6/namespace
+root	/man/6/ndb
+root	/man/8/applylog
+root	/man/8/collabsrv
+root	/man/8/create
+root	/man/8/httpd
+root	/man/8/init
+root	/man/8/mangaload
+root	/man/8/mkfs
+root	/man/8/prep
+root	/man/8/svc
+rootdir	/man/10/ntsrv
+rootdir	/man/10/srclist
+rooted	/man/1/alphabet-fs
+rooted	/man/1/alphabet-main
+rooted	/man/1/fs
+rooted	/man/1/itest
+rooted	/man/10/conf
+rooted	/man/2/disks
+rooted	/man/2/fsproto
+rooted	/man/2/names
+rooted	/man/2/spree
+rooted	/man/2/sys-export
+rooted	/man/2/sys-fd2path
+rooted	/man/4/export
+rooted	/man/4/import
+rooted	/man/8/applylog
+rooted	/man/8/httpd
+rooted	/man/8/mkfs
+rootpath	/man/1/emu
+rootpath	/man/2/styxservers
+roots	/man/1/calc
+roots	/man/2/disks
+roots	/man/2/sys-pctl
+roottype	/man/2/venti
+ropen	/man/10/styx
+ropen	/man/5/0intro
+ropen	/man/5/open
+ropen	/man/8/styxchat
+rotate	/man/1/wm-misc
+rotated	/man/9/canvas
+rotation	/man/1/wm-misc
+rotation	/man/9/canvas
+rotta.b	/man/1/mash-make
+rotta.dis	/man/1/mash-make
+rotta.m	/man/1/mash-make
+roughly	/man/1/charon
+round	/man/1/calc
+round	/man/1/collab-clients
+round	/man/1/sh-std
+round	/man/2/math-fp
+round	/man/8/ping
+round	/man/9/canvas
+rounded	/man/1/du
+rounded	/man/10/2l
+rounded	/man/10/5coff
+rounded	/man/10/a.out
+rounded	/man/2/geodesy
+rounded	/man/2/math-0intro
+rounded	/man/3/draw
+rounded	/man/4/memfs
+rounded	/man/9/canvas
+rounded	/man/9/scale
+rounding	/man/10/seconds
+rounding	/man/2/math-0intro
+rounding	/man/2/math-fp
+rounding	/man/9/scale
+rounds	/man/2/math-fp
+route	/man/1/plumb
+route	/man/2/ip
+route	/man/2/plumbmsg
+route	/man/2/sys-0intro
+route	/man/3/ip
+route	/man/6/plumbing
+route	/man/8/plumber
+route	/man/8/rip
+routed	/man/2/plumbmsg
+routed	/man/8/plumber
+routed	/man/8/rip
+router	/man/2/dhcpclient
+router	/man/2/ip
+router	/man/8/rip
+routerlt	/man/2/ip
+routers	/man/2/ip
+routes	/man/3/ip
+routes	/man/8/init
+routes	/man/8/rip
+routine	/man/1/yacc
+routine	/man/10/2c
+routine	/man/10/devattach
+routine	/man/10/lock
+routine	/man/10/qio
+routine	/man/10/readnum
+routine	/man/10/rune
+routine	/man/10/styx
+routine	/man/2/draw-image
+routine	/man/2/env
+routine	/man/2/prefab-style
+routine	/man/2/prof
+routine	/man/2/scsiio
+routine	/man/2/styxservers
+routine	/man/2/sys-dial
+routine	/man/2/sys-open
+routines	/man/10/0intro
+routines	/man/10/2l
+routines	/man/10/allocb
+routines	/man/10/delay
+routines	/man/10/error
+routines	/man/10/malloc
+routines	/man/10/print
+routines	/man/10/qio
+routines	/man/10/readnum
+routines	/man/10/rune
+routines	/man/10/splhi
+routines	/man/10/strcat
+routines	/man/10/styx
+routines	/man/10/styxserver
+routines	/man/10/xalloc
+routines	/man/2/crc
+routines	/man/2/daytime
+routines	/man/2/dbm
+routines	/man/2/debug
+routines	/man/2/draw-0intro
+routines	/man/2/geodesy
+routines	/man/2/imagefile
+routines	/man/2/keyring-certtostr
+routines	/man/2/math-0intro
+routines	/man/2/math-elem
+routines	/man/2/math-export
+routines	/man/2/math-fp
+routines	/man/2/math-linalg
+routines	/man/2/prefab-style
+routines	/man/2/prof
+routines	/man/2/security-0intro
+routines	/man/2/sets
+routines	/man/2/spree-cardlib
+routines	/man/2/sys-0intro
+routines	/man/2/sys-dial
+routines	/man/2/sys-stat
+routines	/man/2/tkclient
+routines	/man/2/wmclient
+routines	/man/2/wmlib
+routines	/man/3/i82365
+routines	/man/4/factotum
+routines	/man/5/0intro
+routines	/man/5/stat
+routines	/man/6/utf
+routing	/man/10/plan9.ini
+routing	/man/2/ida
+routing	/man/2/ip
+routing	/man/2/plumbmsg
+routing	/man/3/ip
+routing	/man/8/plumber
+routing	/man/8/rip
+row	/man/1/miniterm
+row	/man/1/sh-csv
+row	/man/1/tktester
+row	/man/2/ida
+row	/man/6/image
+row	/man/7/db
+row	/man/9/grid
+row2	/man/9/grid
+rowconfigure	/man/9/grid
+rowdelete	/man/9/grid
+rowindex	/man/9/grid
+rowinsert	/man/9/grid
+rows	/man/1/tktester
+rows	/man/6/image
+rows	/man/7/db
+rows	/man/9/grid
+rowspan	/man/1/tktester
+rowspan	/man/9/grid
+rowspans	/man/9/grid
+royalty	/man/9/1copyright
+rp	/man/10/allocb
+rp	/man/2/ip
+rpc	/man/2/factotum
+rpc	/man/4/factotum
+rpc	/man/4/iostats
+rpc's	/man/4/factotum
+rpcattrs	/man/2/factotum
+rproc	/man/3/dbg
+rptr	/man/10/atoi
+rq	/man/2/filter
+rq	/man/2/filter-deflate
+rq	/man/2/filter-slip
+rq	/man/2/wmsrv
+rq.error	/man/2/filter
+rq.fill	/man/2/filter
+rq.fill	/man/2/filter-slip
+rq.finished	/man/2/filter
+rq.info	/man/2/filter
+rq.info	/man/2/filter-deflate
+rq.result	/man/2/filter
+rq.result	/man/2/filter-slip
+rq.start	/man/2/filter
+rr	/man/9/types
+rread	/man/1/sh-file2chan
+rread	/man/10/styx
+rread	/man/10/styxserver
+rread	/man/2/sys-file2chan
+rread	/man/5/0intro
+rread	/man/5/read
+rread	/man/8/styxchat
+rreadone	/man/1/sh-file2chan
+rremove	/man/5/0intro
+rremove	/man/5/remove
+rremove	/man/8/styxchat
+rrggbb	/man/9/types
+rrggbbaa	/man/9/types
+rrnote	/man/3/dbg
+rs	/man/10/odbc
+rs	/man/10/plan9.ini
+rs	/man/6/man
+rsa	/man/2/keyring-gensk
+rsa	/man/2/keyring-rc4
+rsa	/man/2/security-0intro
+rsa	/man/2/spki
+rsa	/man/6/keytext
+rsa	/man/8/ai2key
+rsa	/man/8/createsignerkey
+rsa's	/man/8/ai2key
+rsagen	/man/8/ai2key
+rsagen.b	/man/8/ai2key
+rsecret	/man/2/palmfile
+rserv	/man/2/dial
+rst	/man/3/ip
+rstart	/man/3/dbg
+rstartstop	/man/3/dbg
+rstat	/man/10/styx
+rstat	/man/5/0intro
+rstat	/man/5/stat
+rstat	/man/8/styxchat
+rstatus	/man/3/dbg
+rstop	/man/3/dbg
+rstyx	/man/1/rcmd
+rstyx	/man/8/svc
+rstyx.sh	/man/8/svc
+rstyxd	/man/1/rcmd
+rstyxd	/man/8/rstyxd
+rstyxd	/man/8/svc
+rstyxd.b	/man/8/rstyxd
+rsys	/man/2/dial
+rt	/man/1/emu
+rt	/man/1/wm-misc
+rt	/man/2/dis
+rt	/man/3/sign
+rt.b	/man/1/wm-misc
+rtc	/man/3/rtc
+rtc	/man/8/manufacture
+rtc	/man/8/sntp
+rtcid	/man/3/rtc
+rtcid	/man/8/manufacture
+rtl8139	/man/10/plan9.ini
+rts	/man/3/eia
+rudimentary	/man/1/tiny
+rule	/man/1/acme
+rule	/man/1/mash
+rule	/man/1/mash-make
+rule	/man/1/mk
+rule	/man/10/mk
+rule	/man/2/draw-image
+rule	/man/2/rfc822
+rule	/man/2/sys-0intro
+rule	/man/2/w3c-xpointers
+rule	/man/3/draw
+rule	/man/6/attrdb
+rule	/man/6/plumbing
+rule	/man/6/sexprs
+rule	/man/8/plumber
+rule	/man/9/canvas
+rule	/man/9/text
+rule's	/man/6/plumbing
+rulefile	/man/8/plumber
+rules	/man/1/acme
+rules	/man/1/brutus
+rules	/man/1/calc
+rules	/man/1/fc
+rules	/man/1/fs
+rules	/man/1/ftree
+rules	/man/1/mash
+rules	/man/1/mash-make
+rules	/man/1/mk
+rules	/man/1/plumb
+rules	/man/1/sh
+rules	/man/1/sh-alphabet
+rules	/man/1/sh-std
+rules	/man/1/tiny
+rules	/man/1/wm-misc
+rules	/man/1/wm-sh
+rules	/man/1/yacc
+rules	/man/10/2c
+rules	/man/10/dev
+rules	/man/10/mk
+rules	/man/2/asn1
+rules	/man/2/command
+rules	/man/2/draw-display
+rules	/man/2/exception
+rules	/man/2/math-0intro
+rules	/man/2/plumbmsg
+rules	/man/2/rfc822
+rules	/man/2/spree
+rules	/man/2/styx
+rules	/man/2/w3c-css
+rules	/man/2/w3c-xpointers
+rules	/man/3/fs
+rules	/man/6/keyboard
+rules	/man/6/plumbing
+rules	/man/6/sexprs
+rules	/man/6/ubfa
+rules	/man/8/plumber
+rules	/man/8/styxchat
+rules	/man/9/cursor
+rules	/man/9/entry
+rules	/man/9/send
+ruleset	/man/2/w3c-css
+run	/man/1/0intro
+run	/man/1/9win
+run	/man/1/acme
+run	/man/1/alphabet-fs
+run	/man/1/calendar
+run	/man/1/cprof
+run	/man/1/cpu
+run	/man/1/deb
+run	/man/1/dmview
+run	/man/1/emu
+run	/man/1/fs
+run	/man/1/itest
+run	/man/1/limbo
+run	/man/1/listen
+run	/man/1/mash
+run	/man/1/mash-make
+run	/man/1/mc
+run	/man/1/mk
+run	/man/1/mprof
+run	/man/1/mux
+run	/man/1/os
+run	/man/1/prof
+run	/man/1/ps
+run	/man/1/runas
+run	/man/1/sh
+run	/man/1/sh-arg
+run	/man/1/sh-std
+run	/man/1/sh-test
+run	/man/1/stack
+run	/man/1/tiny
+run	/man/1/tktester
+run	/man/1/toolbar
+run	/man/1/units
+run	/man/1/wish
+run	/man/1/wm-sh
+run	/man/10/2c
+run	/man/10/9load
+run	/man/10/a.out
+run	/man/10/acid
+run	/man/10/conf
+run	/man/10/kproc
+run	/man/10/mk
+run	/man/10/ntsrv
+run	/man/10/plan9.ini
+run	/man/2/0intro
+run	/man/2/alphabet-intro
+run	/man/2/cfg
+run	/man/2/command
+run	/man/2/debug
+run	/man/2/dis
+run	/man/2/draw-0intro
+run	/man/2/draw-example
+run	/man/2/itslib
+run	/man/2/keyring-0intro
+run	/man/2/lists
+run	/man/2/math-0intro
+run	/man/2/sh
+run	/man/2/styxflush
+run	/man/2/sys-byte2char
+run	/man/2/sys-sleep
+run	/man/3/cmd
+run	/man/3/dbg
+run	/man/3/ds
+run	/man/3/fs
+run	/man/3/ip
+run	/man/3/prog
+run	/man/4/acme
+run	/man/4/dbfs
+run	/man/4/export
+run	/man/4/factotum
+run	/man/4/grid-cpu
+run	/man/4/import
+run	/man/4/keysrv
+run	/man/4/lockfs
+run	/man/4/namespace
+run	/man/6/dis
+run	/man/7/dbsrv
+run	/man/8/applylog
+run	/man/8/init
+run	/man/8/mangaload
+run	/man/8/rstyxd
+run	/man/8/signer
+run	/man/8/svc
+runas	/man/1/runas
+runas.b	/man/1/runas
+runbuiltin	/man/2/sh
+runcmd	/man/1/sh-file2chan
+runcpu	/man/4/grid-cpu
+rune	/man/10/2c
+rune	/man/10/dev
+rune	/man/10/devattach
+rune	/man/10/kbdputc
+rune	/man/10/rune
+rune	/man/10/strcat
+rune	/man/6/keyboard
+rune	/man/6/regexp
+rune	/man/6/utf
+rune.c	/man/10/rune
+runefprint	/man/10/print
+runelen	/man/10/rune
+runeprint	/man/10/print
+runes	/man/1/freq
+runes	/man/1/strings
+runes	/man/1/tcs
+runes	/man/1/tr
+runes	/man/10/print
+runes	/man/10/rune
+runes	/man/2/convcs
+runes	/man/4/acme
+runes	/man/4/trfs
+runes	/man/6/keyboard
+runesprint	/man/10/print
+runestrlen.c	/man/10/rune
+runetochar	/man/10/rune
+runflag	/man/6/dis
+runit	/man/1/stack
+runlock	/man/10/qlock
+runme.dis	/man/1/grid-session
+runnable	/man/10/kproc
+running	/man/1/9win
+running	/man/1/acme
+running	/man/1/blur
+running	/man/1/calendar
+running	/man/1/charon
+running	/man/1/collab
+running	/man/1/collab-clients
+running	/man/1/deb
+running	/man/1/diff
+running	/man/1/emu
+running	/man/1/grid-query
+running	/man/1/kill
+running	/man/1/mux
+running	/man/1/os
+running	/man/1/ps
+running	/man/1/sendmail
+running	/man/1/sh
+running	/man/1/sh-arg
+running	/man/1/sh-tk
+running	/man/1/tr
+running	/man/1/webgrab
+running	/man/1/wm
+running	/man/1/wm-misc
+running	/man/1/wm-sh
+running	/man/10/9load
+running	/man/10/acid
+running	/man/10/c2l
+running	/man/10/eve
+running	/man/10/intrenable
+running	/man/10/kprof
+running	/man/10/lock
+running	/man/10/master
+running	/man/10/sleep
+running	/man/10/styxserver
+running	/man/2/0intro
+running	/man/2/debug
+running	/man/2/ir
+running	/man/2/keyring-sha1
+running	/man/2/plumbmsg
+running	/man/2/sh
+running	/man/2/spree-allow
+running	/man/2/srv
+running	/man/2/tk
+running	/man/3/cmd
+running	/man/3/dbg
+running	/man/3/dynld
+running	/man/3/fpga
+running	/man/3/fs
+running	/man/3/prof
+running	/man/3/prog
+running	/man/3/touch
+running	/man/4/acme
+running	/man/4/import
+running	/man/4/kfs
+running	/man/4/namespace
+running	/man/6/ndb
+running	/man/6/plumbing
+running	/man/7/db
+running	/man/8/collabsrv
+running	/man/8/createsignerkey
+running	/man/8/cs
+running	/man/8/dns
+running	/man/8/prep
+running	/man/8/rdbgsrv
+running	/man/8/register
+running	/man/8/rip
+running	/man/8/signer
+running	/man/8/svc
+running	/man/8/touchcal
+running	/man/8/virgild
+runns	/man/1/grid-ns
+runpipeline	/man/1/stack
+runs	/man/1/acme
+runs	/man/1/alphabet-fs
+runs	/man/1/charon
+runs	/man/1/collab-clients
+runs	/man/1/cprof
+runs	/man/1/emu
+runs	/man/1/fs
+runs	/man/1/itest
+runs	/man/1/m4
+runs	/man/1/mash
+runs	/man/1/mk
+runs	/man/1/mux
+runs	/man/1/nsbuild
+runs	/man/1/sh
+runs	/man/1/sh-std
+runs	/man/1/toolbar
+runs	/man/1/wish
+runs	/man/1/wm
+runs	/man/10/allocb
+runs	/man/10/c2l
+runs	/man/10/mk
+runs	/man/10/odbc
+runs	/man/2/alphabet-intro
+runs	/man/2/command
+runs	/man/2/debug
+runs	/man/2/dhcpclient
+runs	/man/2/keyring-auth
+runs	/man/2/keyring-rc4
+runs	/man/2/mpeg
+runs	/man/2/sh
+runs	/man/2/xml
+runs	/man/3/cmd
+runs	/man/3/fs
+runs	/man/3/ip
+runs	/man/3/kprof
+runs	/man/4/dbfs
+runs	/man/6/keys
+runs	/man/8/bootpd
+runs	/man/8/getauthinfo
+runs	/man/8/prep
+runs	/man/8/register
+runs	/man/8/svc
+runs	/man/9/choicebutton
+runsbuiltin	/man/2/sh
+runt.c	/man/2/sys-byte2char
+runt.c	/man/2/sys-print
+runtime	/man/1/limbo
+runtime	/man/1/mprof
+runtime	/man/10/conf
+runtime	/man/6/dis
+russian	/man/2/convcs
+rversion	/man/10/styx
+rversion	/man/2/styx
+rversion	/man/5/0intro
+rversion	/man/5/version
+rversion	/man/8/styxchat
+rw	/man/3/prog
+rwaitstop	/man/3/dbg
+rwalk	/man/10/styx
+rwalk	/man/5/0intro
+rwalk	/man/5/walk
+rwalk	/man/8/styxchat
+rwlock	/man/10/qlock
+rwm	/man/3/sd
+rwrite	/man/1/sh-file2chan
+rwrite	/man/2/bufio-chanfill
+rwrite	/man/2/sys-file2chan
+rwrite	/man/2/wmsrv
+rwrite	/man/5/0intro
+rwrite	/man/5/read
+rwrite	/man/8/styxchat
+rws	/man/2/alphabet-intro
+rwstat	/man/5/0intro
+rwstat	/man/5/stat
+rwstat	/man/8/styxchat
+rx	/man/6/auth
+rx	/man/6/login
+rxmitra	/man/2/ip
+rôle	/man/1/sh
+rôle	/man/9/0intro
+rôles	/man/2/security-auth
+s.next	/man/10/acid
+s.text	/man/2/w3c-xpointers
+s.val	/man/10/acid
+s3hwgc	/man/3/vga
+s:string	/man/2/hash
+sa	/man/2/keyring-0intro
+sa	/man/2/spki
+sa1100	/man/1/mk
+sa1100	/man/10/mk
+sa1100	/man/3/gpio
+sa1100	/man/3/touch
+sa1100	/man/8/rdbgsrv
+safe	/man/1/0intro
+safe	/man/1/charon
+safe	/man/10/intrenable
+safe	/man/2/styxservers
+safe	/man/2/w3c-uris
+safe	/man/6/sexprs
+safely	/man/10/malloc
+safely	/man/10/parsecmd
+safely	/man/10/qio
+safely	/man/2/bufio
+safely	/man/2/command
+safely	/man/3/logfs
+safely	/man/3/rtc
+safely	/man/3/srv9
+safer	/man/1/sh
+safer	/man/10/9load
+safety	/man/10/print
+sake	/man/1/calc
+sam	/man/1/acme
+sample	/man/1/acme
+sample	/man/1/auplay
+sample	/man/1/fortune
+sample	/man/1/keyboard
+sample	/man/1/prof
+sample	/man/2/prof
+sample	/man/3/audio
+sample	/man/3/kprof
+sample	/man/3/touch
+sample	/man/6/audio
+sample	/man/6/colour
+sampled	/man/3/prof
+samples	/man/2/prof
+samples	/man/3/audio
+samples	/man/3/touch
+samples	/man/6/audio
+samples	/man/6/colour
+sampling	/man/2/prof
+sampling	/man/3/prof
+sampling	/man/6/colour
+sane	/man/10/dev
+sanity	/man/6/login
+sap	/man/3/plap
+sar	/man/10/ar
+sarmag	/man/10/ar
+sat	/man/2/daytime
+satisfied	/man/1/sh-std
+satisfied	/man/9/text
+satisfies	/man/1/mk
+satisfies	/man/10/conf
+satisfies	/man/10/mk
+satisfies	/man/10/readnum
+satisfies	/man/2/lists
+satisfy	/man/1/alphabet-fs
+satisfy	/man/1/fs
+satisfy	/man/1/wm
+satisfy	/man/10/xalloc
+satisfy	/man/2/keyset
+satisfy	/man/2/lists
+satisfy	/man/9/canvas
+satisfy	/man/9/options
+satisfying	/man/1/alphabet-fs
+satisfying	/man/1/fs
+satopd	/man/2/draw-image
+saturated	/man/2/draw-display
+saturated	/man/6/colour
+saturation	/man/3/tv
+sauvignon	/man/10/odbc
+save	/man/1/acme
+save	/man/1/brutus
+save	/man/1/charon
+save	/man/1/tktester
+save	/man/10/dev
+save	/man/2/print
+save	/man/2/spree
+save	/man/3/ds
+save	/man/3/touch
+save	/man/6/dis
+save	/man/8/getauthinfo
+saved	/man/1/deb
+saved	/man/1/sh
+saved	/man/1/tktester
+saved	/man/1/webgrab
+saved	/man/10/intrenable
+saved	/man/2/bufio-chanfill
+saved	/man/2/disks
+saved	/man/2/prof
+saved	/man/2/spree
+saved	/man/3/dbg
+saved	/man/3/touch
+saved	/man/7/db
+saved	/man/8/register
+saved	/man/8/touchcal
+saves	/man/1/tktester
+saves	/man/10/qio
+saves	/man/2/exception
+saves	/man/3/ip
+saves	/man/8/signer
+saving	/man/1/uuencode
+saving	/man/1/webgrab
+saving	/man/10/dev
+saving	/man/10/plan9.ini
+savings	/man/2/daytime
+saying	/man/1/fortune
+says	/man/1/collab-clients
+says	/man/1/m4
+says	/man/2/spki
+says	/man/2/sys-open
+says	/man/2/sys-stat
+says	/man/5/0intro
+sb	/man/1/tktester
+sb16	/man/10/plan9.ini
+sb16	/man/3/audio
+sbl	/man/1/deb
+sbl	/man/1/limbo
+sbl	/man/1/sh-regex
+sbl	/man/1/stack
+sbl	/man/2/debug
+sbl	/man/2/prof
+sbl	/man/6/dis
+sbl	/man/6/sbl
+sblpath	/man/1/stack
+sboot	/man/8/rdbgsrv
+sbootconsole	/man/8/rdbgsrv
+sbuiltin	/man/2/sh
+scalar	/man/10/c2l
+scalbn	/man/2/math-fp
+scale	/man/1/units
+scale	/man/1/wm-misc
+scale	/man/2/imagefile
+scale	/man/3/audio
+scale	/man/3/tv
+scale	/man/9/0intro
+scale	/man/9/canvas
+scale	/man/9/scale
+scale's	/man/9/scale
+scaled	/man/2/math-fp
+scaled	/man/2/print
+scaled	/man/3/mpeg
+scaled	/man/6/colour
+scaled	/man/9/canvas
+scaler	/man/2/print
+scaler.b	/man/2/print
+scales	/man/1/units
+scales	/man/9/scale
+scaling	/man/9/canvas
+scan	/man/10/2l
+scan	/man/10/a.out
+scan	/man/10/atoi
+scan	/man/10/plan9.ini
+scan	/man/2/draw-image
+scan	/man/3/cons
+scan	/man/3/sd
+scan	/man/3/tv
+scan	/man/6/scancode
+scan	/man/9/text
+scancode	/man/3/cons
+scancode	/man/6/scancode
+scanned	/man/1/sendmail
+scanned	/man/2/string
+scanning	/man/10/2l
+scanning	/man/10/plan9.ini
+scanning	/man/9/menubutton
+scanning	/man/9/pack
+scanning	/man/9/text
+scans	/man/2/spree-objstore
+scans	/man/3/tinyfs
+scans	/man/8/prep
+scavenge	/man/3/ftl
+scavenging	/man/3/ftl
+scenes	/man/2/spree
+scheding	/man/3/dbg
+schedule	/man/1/calendar
+scheduled	/man/10/qlock
+scheduled	/man/2/sys-0intro
+scheduler	/man/1/alphabet-abc
+scheduler	/man/1/alphabet-grid
+scheduler	/man/10/kproc
+scheduler	/man/10/lock
+scheduler	/man/3/dbg
+schedules	/man/10/qlock
+scheduling	/man/3/cmd
+scheme	/man/10/intrenable
+scheme	/man/2/0intro
+scheme	/man/2/convcs
+scheme	/man/2/ida
+scheme	/man/2/spree
+scheme	/man/2/spree-cardlib
+scheme	/man/2/w3c-uris
+scheme	/man/2/w3c-xpointers
+scheme	/man/3/ip
+scheme	/man/3/mnt
+scheme	/man/6/image
+scheme	/man/9/bind
+scheme	/man/9/canvas
+scheme	/man/9/text
+schemes	/man/2/convcs
+schemes	/man/2/sexprs
+schemes	/man/2/w3c-uris
+schemes	/man/2/w3c-xpointers
+schemes	/man/6/sexprs
+schneier	/man/2/keyring-0intro
+schneier	/man/2/security-0intro
+schneier	/man/3/ssl
+schneier's	/man/2/keyring-crypt
+scope	/man/1/calc
+scope	/man/1/mash
+scope	/man/1/sh
+scope	/man/2/0intro
+scope	/man/2/sh
+scope	/man/2/sys-0intro
+scope	/man/6/sbl
+scope	/man/8/applylog
+score	/man/1/wm-misc
+score	/man/2/venti
+score	/man/4/vacfs
+scores	/man/1/wm-misc
+scr	/man/2/draw-image
+scr	/man/3/draw
+scrambled	/man/2/security-0intro
+scratch	/man/1/acme
+scratch	/man/6/namespace
+screen	/man/1/blur
+screen	/man/1/brutus
+screen	/man/1/charon
+screen	/man/1/emu
+screen	/man/1/filename
+screen	/man/1/ftree
+screen	/man/1/keyboard
+screen	/man/1/logon
+screen	/man/1/mash-tk
+screen	/man/1/miniterm
+screen	/man/1/mux
+screen	/man/1/tktester
+screen	/man/1/wish
+screen	/man/1/wm
+screen	/man/1/wm-misc
+screen	/man/10/conf
+screen	/man/10/dev
+screen	/man/10/plan9.ini
+screen	/man/2/dialog
+screen	/man/2/draw-0intro
+screen	/man/2/draw-context
+screen	/man/2/draw-display
+screen	/man/2/draw-example
+screen	/man/2/draw-image
+screen	/man/2/draw-pointer
+screen	/man/2/draw-screen
+screen	/man/2/drawmux
+screen	/man/2/keyring-0intro
+screen	/man/2/mpeg
+screen	/man/2/prefab-0intro
+screen	/man/2/prefab-compound
+screen	/man/2/prefab-element
+screen	/man/2/prefab-environ
+screen	/man/2/security-0intro
+screen	/man/2/selectfile
+screen	/man/2/tk
+screen	/man/2/tkclient
+screen	/man/2/wmclient
+screen	/man/2/wmsrv
+screen	/man/3/cons
+screen	/man/3/draw
+screen	/man/3/mpeg
+screen	/man/3/touch
+screen	/man/3/vga
+screen	/man/8/touchcal
+screen	/man/9/bind
+screen	/man/9/button
+screen	/man/9/canvas
+screen	/man/9/checkbutton
+screen	/man/9/entry
+screen	/man/9/grab
+screen	/man/9/grid
+screen	/man/9/label
+screen	/man/9/listbox
+screen	/man/9/menu
+screen	/man/9/menubutton
+screen	/man/9/options
+screen	/man/9/pack
+screen	/man/9/panel
+screen	/man/9/radiobutton
+screen	/man/9/text
+screen	/man/9/types
+screen	/man/9/update
+screen's	/man/2/draw-screen
+screen's	/man/3/vga
+screen.c	/man/10/conf
+screen.c	/man/3/draw
+screen.fill	/man/2/draw-screen
+screenful	/man/9/scrollbar
+screenful	/man/9/text
+screenfuls	/man/9/entry
+screenfuls	/man/9/listbox
+screenfuls	/man/9/text
+screenid	/man/3/draw
+screenr	/man/2/tk
+screenr	/man/2/wmclient
+screenr2imager	/man/2/wmclient
+screens	/man/1/miniterm
+screens	/man/2/draw-0intro
+screens	/man/2/draw-screen
+screens	/man/3/draw
+screenx	/man/9/canvas
+screenx	/man/9/panel
+screeny	/man/9/canvas
+screeny	/man/9/panel
+script	/man/1/0intro
+script	/man/1/charon
+script	/man/1/diff
+script	/man/1/grid-ns
+script	/man/1/itest
+script	/man/1/man
+script	/man/1/mk
+script	/man/1/plumb
+script	/man/1/sh
+script	/man/1/sh-arg
+script	/man/1/sh-file2chan
+script	/man/1/sh-regex
+script	/man/1/sh-string
+script	/man/1/sh-tk
+script	/man/1/toolbar
+script	/man/10/mk
+script	/man/10/plan9.ini
+script	/man/2/sh
+script	/man/4/grid-cpu
+script	/man/6/keyboard
+script	/man/8/plumber
+script	/man/8/shutdown
+script	/man/9/bind
+script	/man/9/canvas
+script	/man/9/menu
+script	/man/9/text
+scripting	/man/1/wish
+scripting	/man/2/sh
+scripts	/man/1/charon
+scripts	/man/1/diff
+scripts	/man/1/echo
+scripts	/man/1/itest
+scripts	/man/10/plan9.ini
+scripts	/man/8/svc
+scripts	/man/9/bind
+scripts	/man/9/canvas
+scripts	/man/9/text
+scroll	/man/1/acme
+scroll	/man/1/deb
+scroll	/man/1/tktester
+scroll	/man/2/palmfile
+scroll	/man/2/prefab-compound
+scroll	/man/2/prefab-element
+scroll	/man/4/acme
+scroll	/man/9/canvas
+scroll	/man/9/entry
+scroll	/man/9/listbox
+scroll	/man/9/scrollbar
+scroll	/man/9/see
+scroll	/man/9/text
+scrollable	/man/1/collab-clients
+scrollable	/man/1/wm-sh
+scrollable	/man/9/scrollbar
+scrollbar	/man/9/0intro
+scrollbar	/man/9/canvas
+scrollbar	/man/9/options
+scrollbar	/man/9/scrollbar
+scrollbar's	/man/9/scrollbar
+scrollbars	/man/1/tktester
+scrollbars	/man/9/canvas
+scrollbars	/man/9/entry
+scrollbars	/man/9/listbox
+scrollbars	/man/9/options
+scrollbars	/man/9/scrollbar
+scrollbars	/man/9/text
+scrolled	/man/1/deb
+scrolled	/man/2/prefab-element
+scrolled	/man/9/text
+scrolling	/man/1/acme
+scrolling	/man/1/mux
+scrolling	/man/1/wm-sh
+scrolling	/man/2/prefab-compound
+scrolling	/man/4/acme
+scrolling	/man/9/canvas
+scrolling	/man/9/listbox
+scrolling	/man/9/options
+scrolling	/man/9/scrollbar
+scrollregion	/man/9/canvas
+scrolls	/man/9/listbox
+scs	/man/8/cs
+scsi	/man/10/conf
+scsi	/man/10/plan9.ini
+scsi	/man/2/scsiio
+scsi	/man/3/sd
+scsi.cmd	/man/2/scsiio
+scsi.m	/man/2/scsiio
+scsi.open	/man/2/scsiio
+scsicodes	/man/2/scsiio
+scsierror	/man/2/scsiio
+scsiio	/man/2/disks
+scsiio	/man/2/scsiio
+scsiio.b	/man/2/scsiio
+scsix	/man/10/plan9.ini
+sd	/man/10/9load
+sd	/man/2/disks
+sd	/man/2/scsiio
+sd	/man/3/sd
+sd	/man/4/kfs
+sd	/man/8/kfscmd
+sd	/man/8/prep
+sd00	/man/3/sd
+sdc0	/man/3/ds
+sdc0	/man/3/sd
+sdc0	/man/8/prep
+sdc1	/man/3/ds
+sdd0	/man/3/ds
+sdd0	/man/3/sd
+sdd1	/man/3/ds
+sdns	/man/8/dns
+se	/man/2/format
+se	/man/2/regex
+se	/man/2/spki
+se	/man/9/options
+se	/man/9/types
+se2fmt	/man/2/format
+search	/man/1/acme
+search	/man/1/brutus
+search	/man/1/calc
+search	/man/1/chmod
+search	/man/1/disdep
+search	/man/1/grid-query
+search	/man/1/look
+search	/man/1/man
+search	/man/1/math-misc
+search	/man/1/sh
+search	/man/1/stack
+search	/man/1/wm-misc
+search	/man/10/2c
+search	/man/10/2l
+search	/man/10/9load
+search	/man/10/devattach
+search	/man/2/hash
+search	/man/2/selectfile
+search	/man/2/stringinttab
+search	/man/2/sys-stat
+search	/man/5/walk
+search	/man/6/attrdb
+search	/man/8/httpd
+search	/man/9/text
+searchcommand	/man/9/canvas
+searched	/man/1/acme
+searched	/man/1/grid-query
+searched	/man/1/mash
+searched	/man/10/2a
+searched	/man/10/ar
+searched	/man/2/sys-0intro
+searched	/man/2/sys-bind
+searched	/man/3/ftl
+searched	/man/4/namespace
+searched	/man/9/text
+searches	/man/1/acme
+searches	/man/1/limbo
+searches	/man/1/sh-alphabet
+searches	/man/10/9load
+searches	/man/10/devattach
+searches	/man/10/plan9.ini
+searches	/man/2/plumbmsg
+searches	/man/2/spree
+searches	/man/2/stringinttab
+searches	/man/4/acme
+searches	/man/9/text
+searching	/man/1/acme
+searching	/man/1/brutus
+searching	/man/1/calc
+searching	/man/1/deb
+searching	/man/1/grid-query
+searching	/man/10/2c
+searching	/man/10/2l
+searching	/man/10/9load
+searching	/man/8/dns
+searchspec	/man/9/canvas
+sec	/man/2/daytime
+sec	/man/2/keyring-crypt
+sec2date	/man/2/rfc822
+seckey	/man/2/secstore
+secondary	/man/6/colour
+secondary	/man/6/man
+secondary	/man/8/prep
+seconds	/man/1/date
+seconds	/man/1/du
+seconds	/man/1/gettar
+seconds	/man/1/itest
+seconds	/man/1/ls
+seconds	/man/1/mash
+seconds	/man/1/sleep
+seconds	/man/1/time
+seconds	/man/10/9load
+seconds	/man/10/plan9.ini
+seconds	/man/10/seconds
+seconds	/man/2/daytime
+seconds	/man/2/dhcpclient
+seconds	/man/2/geodesy
+seconds	/man/2/palmfile
+seconds	/man/2/rfc822
+seconds	/man/2/sys-stat
+seconds	/man/2/virgil
+seconds	/man/2/volume
+seconds	/man/3/cap
+seconds	/man/3/prog
+seconds	/man/3/rtc
+seconds	/man/3/sd
+seconds	/man/4/keyfs
+seconds	/man/5/stat
+seconds	/man/6/keytext
+seconds	/man/8/applylog
+seconds	/man/8/createsignerkey
+secret	/man/1/crypt
+secret	/man/1/netkey
+secret	/man/1/passwd
+secret	/man/1/secstore
+secret	/man/2/factotum
+secret	/man/2/keyring-0intro
+secret	/man/2/keyring-auth
+secret	/man/2/keyring-certtostr
+secret	/man/2/keyring-crypt
+secret	/man/2/palmfile
+secret	/man/2/secstore
+secret	/man/2/security-0intro
+secret	/man/2/security-login
+secret	/man/2/security-ssl
+secret	/man/2/spki
+secret	/man/3/ip
+secret	/man/3/sign
+secret	/man/3/ssl
+secret	/man/3/tls
+secret	/man/4/factotum
+secret	/man/4/keyfs
+secret	/man/4/keysrv
+secret	/man/6/auth
+secret	/man/6/keys
+secret	/man/6/keytext
+secret	/man/6/login
+secret	/man/8/ai2key
+secret	/man/8/signer
+secretdata	/man/3/tls
+secretin	/man/2/security-ssl
+secretin	/man/3/ssl
+secretout	/man/2/security-ssl
+secretout	/man/3/ssl
+secrets	/man/1/passwd
+secrets	/man/2/keyring-0intro
+secrets	/man/2/security-ssl
+secrets	/man/3/tls
+secrets	/man/4/keyfs
+secrets	/man/4/namespace
+secrets	/man/6/keys
+secrets	/man/8/svc
+secs	/man/2/disks
+secs	/man/2/rfc822
+secs2time	/man/2/spki
+secsize	/man/2/disks
+secsize	/man/8/ftl
+secstore	/man/1/crypt
+secstore	/man/1/secstore
+secstore	/man/2/secstore
+secstore.b	/man/1/secstore
+secstore.b	/man/2/secstore
+secstore.m	/man/2/secstore
+section	/man/1/0intro
+section	/man/1/acme
+section	/man/1/charon
+section	/man/1/cprof
+section	/man/1/man
+section	/man/1/mdb
+section	/man/1/sh
+section	/man/1/sh-alphabet
+section	/man/1/sh-string
+section	/man/10/0intro
+section	/man/10/2l
+section	/man/10/a.out
+section	/man/10/conf
+section	/man/10/kproc
+section	/man/10/lock
+section	/man/10/print
+section	/man/10/qlock
+section	/man/10/styx
+section	/man/2/0intro
+section	/man/2/asn1
+section	/man/2/dhcpclient
+section	/man/2/dis
+section	/man/2/security-0intro
+section	/man/2/styx
+section	/man/2/styxservers
+section	/man/2/sys-0intro
+section	/man/2/tk
+section	/man/2/w3c-css
+section	/man/3/0intro
+section	/man/3/dbg
+section	/man/3/root
+section	/man/4/0intro
+section	/man/4/factotum
+section	/man/4/iostats
+section	/man/4/namespace
+section	/man/6/0intro
+section	/man/6/dis
+section	/man/6/sbl
+section	/man/7/0intro
+section	/man/8/0intro
+section	/man/8/init
+section	/man/8/prep
+section	/man/9/0intro
+section	/man/9/canvas
+section	/man/9/grid
+section	/man/9/scale
+section	/man/9/scrollbar
+section	/man/9/text
+sections	/man/1/cprof
+sections	/man/1/man
+sections	/man/10/a.out
+sections	/man/10/conf
+sections	/man/10/dynld
+sections	/man/10/lock
+sections	/man/10/plan9.ini
+sections	/man/2/0intro
+sections	/man/2/dis
+sections	/man/2/palmfile
+sections	/man/2/prefab-0intro
+sections	/man/2/sys-0intro
+sections	/man/3/0intro
+sections	/man/3/ip
+sections	/man/4/factotum
+sections	/man/4/iostats
+sections	/man/6/dis
+sections	/man/6/sbl
+sections	/man/9/canvas
+sections	/man/9/menu
+sector	/man/10/9load
+sector	/man/10/plan9.ini
+sector	/man/2/disks
+sector	/man/2/draw-image
+sector	/man/3/draw
+sector	/man/3/sd
+sector	/man/8/ftl
+sector	/man/8/prep
+sectors	/man/10/9load
+sectors	/man/2/disks
+sectors	/man/3/sd
+sectors	/man/4/dossrv
+sectors	/man/8/ftl
+sectors	/man/8/prep
+sectorsize	/man/8/prep
+secure	/man/1/secstore
+secure	/man/1/sh-expr
+secure	/man/1/sum
+secure	/man/1/webgrab
+secure	/man/2/keyring-0intro
+secure	/man/2/keyring-crypt
+secure	/man/2/keyring-sha1
+secure	/man/2/secstore
+secure	/man/2/security-0intro
+secure	/man/2/security-login
+secure	/man/2/security-ssl
+secure	/man/2/spki
+secure	/man/3/sign
+secure	/man/3/ssl
+secure	/man/3/tls
+secure	/man/6/login
+secure	/man/8/getauthinfo
+secure	/man/8/logind
+secure	/man/8/register
+secure	/man/8/signer
+secured	/man/3/sign
+securely	/man/2/keyring-0intro
+securely	/man/2/security-0intro
+securenet	/man/1/netkey
+security	/man/1/bind
+security	/man/1/charon
+security	/man/1/cpu
+security	/man/1/rcmd
+security	/man/1/secstore
+security	/man/2/0intro
+security	/man/2/ida
+security	/man/2/keyring-0intro
+security	/man/2/keyring-auth
+security	/man/2/keyring-certtostr
+security	/man/2/keyring-crypt
+security	/man/2/keyset
+security	/man/2/rand
+security	/man/2/registries
+security	/man/2/security-0intro
+security	/man/2/security-auth
+security	/man/2/security-login
+security	/man/2/security-random
+security	/man/2/security-ssl
+security	/man/2/styxservers
+security	/man/2/sys-export
+security	/man/2/sys-fauth
+security	/man/3/cap
+security	/man/3/fs
+security	/man/3/mnt
+security	/man/3/sign
+security	/man/3/ssl
+security	/man/3/tls
+security	/man/4/keysrv
+security	/man/4/lockfs
+security	/man/6/keys
+security	/man/6/login
+security	/man/7/db
+security	/man/7/dbsrv
+security	/man/8/collabsrv
+security	/man/8/createsignerkey
+security	/man/8/logind
+security	/man/8/rstyxd
+security's	/man/2/keyring-rc4
+security.m	/man/2/0intro
+security.m	/man/2/security-0intro
+security.m	/man/2/security-auth
+security.m	/man/2/security-login
+security.m	/man/2/security-random
+security.m	/man/2/security-ssl
+security.m	/man/2/virgil
+security.m	/man/7/db
+sed	/man/1/mk
+sed	/man/10/mk
+seed	/man/2/keyring-ipint
+seed	/man/2/keyring-rc4
+seed	/man/2/palmfile
+seed	/man/2/rand
+seeing	/man/1/blur
+seeing	/man/2/sys-export
+seek	/man/1/dd
+seek	/man/1/read
+seek	/man/10/dynld
+seek	/man/2/bufio
+seek	/man/2/sys-0intro
+seek	/man/2/sys-dirread
+seek	/man/2/sys-seek
+seek	/man/3/arch
+seek	/man/3/cons
+seek	/man/3/tinyfs
+seek	/man/4/registry
+seek	/man/5/read
+seek	/man/8/cs
+seekable	/man/2/xml
+seekend	/man/2/bufio
+seekend	/man/2/sys-seek
+seeking	/man/2/sys-seek
+seeking	/man/5/read
+seekrela	/man/2/bufio
+seekrela	/man/2/sys-seek
+seeks	/man/2/sys-dirread
+seekstart	/man/2/bufio
+seekstart	/man/2/sys-seek
+seemingly	/man/10/mk
+sees	/man/1/collab-clients
+sees	/man/2/spree
+sees	/man/3/cmd
+sees	/man/3/pipe
+sees	/man/8/collabsrv
+sees	/man/8/rdbgsrv
+sees	/man/8/styxmon
+segment	/man/10/2l
+segment	/man/10/5coff
+segment	/man/10/a.out
+segment	/man/10/acid
+segment	/man/10/inm
+segment	/man/10/ms2
+segment	/man/2/dis
+segment	/man/2/string
+segment	/man/3/flash
+segment	/man/6/dis
+segment	/man/9/canvas
+segments	/man/10/a.out
+segments	/man/10/ksize
+segments	/man/10/kstrip
+segments	/man/2/w3c-uris
+segments	/man/3/draw
+segments	/man/3/flash
+segments	/man/3/ip
+segments	/man/8/collabsrv
+segments	/man/9/canvas
+segue	/man/1/wm
+sel	/man/1/mash-tk
+sel	/man/2/spree-cardlib
+sel	/man/9/text
+sel.first	/man/9/canvas
+sel.first	/man/9/entry
+sel.last	/man/9/canvas
+sel.last	/man/9/entry
+select	/man/1/acme
+select	/man/1/alphabet-fs
+select	/man/1/brutus
+select	/man/1/comm
+select	/man/1/filename
+select	/man/1/fs
+select	/man/1/grid-session
+select	/man/1/mux
+select	/man/1/tktester
+select	/man/1/wc
+select	/man/1/wm-misc
+select	/man/10/9load
+select	/man/10/acid
+select	/man/10/conf
+select	/man/10/odbc
+select	/man/10/plan9.ini
+select	/man/2/dict
+select	/man/2/dis
+select	/man/2/ir
+select	/man/2/prefab-compound
+select	/man/2/print
+select	/man/2/registries
+select	/man/2/security-random
+select	/man/2/selectfile
+select	/man/2/w3c-css
+select	/man/3/ds
+select	/man/3/fs
+select	/man/5/attach
+select	/man/7/db
+select	/man/8/plumber
+select	/man/9/canvas
+select	/man/9/checkbutton
+select	/man/9/entry
+select	/man/9/radiobutton
+select.any	/man/2/w3c-css
+select.attrib	/man/2/w3c-css
+select.class	/man/2/w3c-css
+select.element	/man/2/w3c-css
+select.id	/man/2/w3c-css
+select.pseudo	/man/2/w3c-css
+select.pseudofn	/man/2/w3c-css
+selectbackground	/man/9/canvas
+selectbackground	/man/9/entry
+selectbackground	/man/9/listbox
+selectbackground	/man/9/options
+selectbackground	/man/9/text
+selectborderwidth	/man/9/canvas
+selectborderwidth	/man/9/listbox
+selectborderwidth	/man/9/options
+selectborderwidth	/man/9/text
+selectcolor	/man/9/checkbutton
+selectcolor	/man/9/choicebutton
+selectcolor	/man/9/menu
+selectcolor	/man/9/radiobutton
+selected	/man/1/acme
+selected	/man/1/brutus
+selected	/man/1/collab-clients
+selected	/man/1/deb
+selected	/man/1/fortune
+selected	/man/1/grid-ns
+selected	/man/1/grid-session
+selected	/man/1/keyboard
+selected	/man/1/logon
+selected	/man/1/ls
+selected	/man/1/man
+selected	/man/1/mash-tk
+selected	/man/1/mux
+selected	/man/1/tktester
+selected	/man/1/toolbar
+selected	/man/1/tr
+selected	/man/1/wm-misc
+selected	/man/1/wm-sh
+selected	/man/10/kproc
+selected	/man/10/plan9.ini
+selected	/man/10/readnum
+selected	/man/2/dis
+selected	/man/2/draw-font
+selected	/man/2/factotum
+selected	/man/2/prefab-element
+selected	/man/2/prefab-style
+selected	/man/2/prof
+selected	/man/2/security-auth
+selected	/man/2/selectfile
+selected	/man/2/spree-cardlib
+selected	/man/2/styxservers
+selected	/man/2/tabs
+selected	/man/2/w3c-xpointers
+selected	/man/3/ip
+selected	/man/3/mpeg
+selected	/man/3/tls
+selected	/man/3/vid
+selected	/man/4/acme
+selected	/man/4/factotum
+selected	/man/4/grid-cpu
+selected	/man/4/registry
+selected	/man/6/dis
+selected	/man/6/plumbing
+selected	/man/8/init
+selected	/man/8/plumber
+selected	/man/9/canvas
+selected	/man/9/checkbutton
+selected	/man/9/choicebutton
+selected	/man/9/entry
+selected	/man/9/listbox
+selected	/man/9/menu
+selected	/man/9/options
+selected	/man/9/radiobutton
+selected	/man/9/text
+selectedbutton	/man/9/radiobutton
+selectfile	/man/2/selectfile
+selectfile.b	/man/2/selectfile
+selectfile.m	/man/2/selectfile
+selectforeground	/man/9/canvas
+selectforeground	/man/9/entry
+selectforeground	/man/9/listbox
+selectforeground	/man/9/options
+selectforeground	/man/9/text
+selectimage	/man/9/checkbutton
+selectimage	/man/9/menu
+selectimage	/man/9/radiobutton
+selecting	/man/1/acme
+selecting	/man/1/ftree
+selecting	/man/1/m4
+selecting	/man/1/mash-tk
+selecting	/man/1/tktester
+selecting	/man/1/wm-misc
+selecting	/man/10/plan9.ini
+selecting	/man/2/prefab-compound
+selecting	/man/2/prefab-element
+selecting	/man/2/selectfile
+selecting	/man/2/sys-0intro
+selecting	/man/4/registry
+selecting	/man/9/canvas
+selecting	/man/9/choicebutton
+selecting	/man/9/menu
+selection	/man/1/acme
+selection	/man/1/brutus
+selection	/man/1/collab-clients
+selection	/man/1/grid-monitor
+selection	/man/1/mash-tk
+selection	/man/1/wm-sh
+selection	/man/10/acid
+selection	/man/10/error
+selection	/man/10/plan9.ini
+selection	/man/2/popup
+selection	/man/2/prefab-compound
+selection	/man/2/security-auth
+selection	/man/2/spree-cardlib
+selection	/man/4/acme
+selection	/man/4/import
+selection	/man/4/registry
+selection	/man/6/colour
+selection	/man/7/db
+selection	/man/9/canvas
+selection	/man/9/checkbutton
+selection	/man/9/entry
+selection	/man/9/listbox
+selection	/man/9/text
+selections	/man/1/tktester
+selections	/man/1/wm-misc
+selections	/man/1/wm-sh
+selections	/man/10/plan9.ini
+selections	/man/2/spree-cardlib
+selections	/man/9/listbox
+selectively	/man/10/styx
+selectively	/man/2/spree
+selectmode	/man/9/listbox
+selector	/man/1/alphabet-fs
+selector	/man/1/fs
+selector	/man/2/dhcpclient
+selector	/man/2/w3c-css
+selector	/man/2/w3c-xpointers
+selectors	/man/2/w3c-css
+selectors	/man/4/factotum
+selects	/man/1/acme
+selects	/man/1/alphabet-fs
+selects	/man/1/bind
+selects	/man/1/brutus
+selects	/man/1/collab-clients
+selects	/man/1/fs
+selects	/man/1/mash-tk
+selects	/man/1/tktester
+selects	/man/1/wm-sh
+selects	/man/10/conf
+selects	/man/10/master
+selects	/man/10/plan9.ini
+selects	/man/2/prefab-compound
+selects	/man/2/sys-bind
+selects	/man/2/sys-stat
+selects	/man/2/w3c-xpointers
+selects	/man/3/flash
+selects	/man/3/vid
+selects	/man/6/dis
+selects	/man/6/proto
+selects	/man/6/sbl
+selects	/man/8/rdbgsrv
+selects	/man/9/canvas
+selects	/man/9/checkbutton
+selects	/man/9/entry
+selects	/man/9/listbox
+selects	/man/9/radiobutton
+selects	/man/9/scale
+selects	/man/9/text
+self	/man/1/yacc
+self	/man/2/0intro
+self	/man/2/asn1
+self	/man/2/attrdb
+self	/man/2/bufio
+self	/man/2/cfg
+self	/man/2/dbm
+self	/man/2/debug
+self	/man/2/dhcpclient
+self	/man/2/dict
+self	/man/2/diskblocks
+self	/man/2/disks
+self	/man/2/dividers
+self	/man/2/draw-display
+self	/man/2/draw-example
+self	/man/2/draw-font
+self	/man/2/draw-image
+self	/man/2/draw-point
+self	/man/2/draw-rect
+self	/man/2/draw-screen
+self	/man/2/factotum
+self	/man/2/format
+self	/man/2/ida
+self	/man/2/ip
+self	/man/2/itslib
+self	/man/2/json
+self	/man/2/keyring-0intro
+self	/man/2/keyring-ipint
+self	/man/2/lock
+self	/man/2/palmfile
+self	/man/2/plumbmsg
+self	/man/2/prefab-compound
+self	/man/2/prefab-element
+self	/man/2/registries
+self	/man/2/rfc822
+self	/man/2/scsiio
+self	/man/2/sets
+self	/man/2/sexprs
+self	/man/2/sh
+self	/man/2/spki
+self	/man/2/spree
+self	/man/2/spree-cardlib
+self	/man/2/styx
+self	/man/2/styxservers
+self	/man/2/styxservers-nametree
+self	/man/2/sys-self
+self	/man/2/timers
+self	/man/2/translate
+self	/man/2/ubfa
+self	/man/2/venti
+self	/man/2/w3c-uris
+self	/man/2/w3c-xpointers
+self	/man/2/wmclient
+self	/man/2/wmsrv
+self	/man/2/xml
+self	/man/3/draw
+self	/man/3/ip
+self	/man/6/sbl
+self	/man/7/db
+selfv4	/man/2/ip
+selfv6	/man/2/ip
+semantic	/man/6/utf
+semantically	/man/1/acme
+semantically	/man/10/c2l
+semantics	/man/1/mash
+semantics	/man/10/c2l
+semantics	/man/2/convcs
+semantics	/man/2/spree-cardlib
+semantics	/man/2/styxflush
+semantics	/man/5/flush
+semaphore	/man/2/lock
+semaphore.new	/man/2/lock
+semi	/man/1/calc
+semi	/man/1/tiny
+semiaxes	/man/2/draw-image
+semiaxes	/man/3/draw
+semicolon	/man/1/charon
+semicolon	/man/1/mash
+semicolon	/man/9/0intro
+semicolons	/man/1/sh
+semicolons	/man/9/0intro
+semiconductor	/man/10/plan9.ini
+semiconductor	/man/3/rtc
+semigraphic	/man/1/miniterm
+send	/man/1/acme
+send	/man/1/collab-clients
+send	/man/1/man
+send	/man/1/plumb
+send	/man/1/sendmail
+send	/man/1/sh-tk
+send	/man/1/uuencode
+send	/man/1/wm
+send	/man/1/wm-sh
+send	/man/10/c2l
+send	/man/10/kproc
+send	/man/10/odbc
+send	/man/2/0intro
+send	/man/2/alphabet-intro
+send	/man/2/devpointer
+send	/man/2/dhcpclient
+send	/man/2/dial
+send	/man/2/draw-context
+send	/man/2/filter
+send	/man/2/fsproto
+send	/man/2/ip
+send	/man/2/keyring-0intro
+send	/man/2/keyring-getmsg
+send	/man/2/plumbmsg
+send	/man/2/prof
+send	/man/2/scsiio
+send	/man/2/spree
+send	/man/2/spree-allow
+send	/man/2/styx
+send	/man/2/styxflush
+send	/man/2/styxpersist
+send	/man/2/styxservers
+send	/man/2/sys-dial
+send	/man/2/timers
+send	/man/2/tk
+send	/man/2/wmsrv
+send	/man/3/draw
+send	/man/3/eia
+send	/man/3/ip
+send	/man/3/prog
+send	/man/3/ssl
+send	/man/3/tls
+send	/man/4/factotum
+send	/man/5/0intro
+send	/man/5/open
+send	/man/6/login
+send	/man/6/ndb
+send	/man/8/bootpd
+send	/man/8/mangaload
+send	/man/8/ping
+send	/man/9/0intro
+send	/man/9/send
+sender	/man/1/sendmail
+sender	/man/2/security-0intro
+sender	/man/2/smtp
+sender	/man/8/collabsrv
+sender	/man/8/plumber
+sender's	/man/1/collab-clients
+sender's	/man/1/sendmail
+senderrmsg	/man/2/keyring-getmsg
+senderrmsg	/man/2/msgio
+sendimage	/man/2/print
+sending	/man/1/collab-clients
+sending	/man/1/dd
+sending	/man/1/miniterm
+sending	/man/1/wm
+sending	/man/2/filter
+sending	/man/2/ir
+sending	/man/2/plumbmsg
+sending	/man/2/security-0intro
+sending	/man/2/styxflush
+sending	/man/2/styxservers
+sending	/man/2/wait
+sending	/man/2/wmsrv
+sending	/man/3/ip
+sending	/man/3/pnp
+sending	/man/3/prog
+sending	/man/5/flush
+sending	/man/8/bootpd
+sending	/man/8/collabsrv
+sending	/man/8/plumber
+sendmail	/man/1/sendmail
+sendmail	/man/2/smtp
+sendmail.b	/man/1/sendmail
+sendmsg	/man/2/keyring-getmsg
+sendmsg	/man/2/msgio
+sendpin	/man/2/secstore
+sendra	/man/2/ip
+sendreply	/man/2/styxflush
+sends	/man/1/acme
+sends	/man/1/collab-clients
+sends	/man/1/miniterm
+sends	/man/1/plumb
+sends	/man/1/sendmail
+sends	/man/1/sh
+sends	/man/1/sh-tk
+sends	/man/1/telnet
+sends	/man/1/tktester
+sends	/man/1/wm
+sends	/man/10/kproc
+sends	/man/2/command
+sends	/man/2/dhcpclient
+sends	/man/2/draw-context
+sends	/man/2/fsproto
+sends	/man/2/keyring-getmsg
+sends	/man/2/msgio
+sends	/man/2/plumbmsg
+sends	/man/2/scsiio
+sends	/man/2/security-login
+sends	/man/2/smtp
+sends	/man/2/spree
+sends	/man/2/spree-gather
+sends	/man/2/styxflush
+sends	/man/2/styxservers
+sends	/man/2/sys-file2chan
+sends	/man/2/tk
+sends	/man/2/wait
+sends	/man/2/wmlib
+sends	/man/2/wmsrv
+sends	/man/3/dbg
+sends	/man/3/i2c
+sends	/man/3/lpt
+sends	/man/3/tls
+sends	/man/5/flush
+sends	/man/6/auth
+sends	/man/6/login
+sends	/man/6/plumbing
+sends	/man/7/db
+sends	/man/8/ping
+sends	/man/8/rdbgsrv
+sends	/man/8/signer
+sends	/man/8/styxchat
+sends	/man/8/virgild
+sends	/man/9/send
+sendtext	/man/1/wm-sh
+sendtextfd	/man/2/print
+sensible	/man/2/prefab-element
+sensible	/man/2/rfc822
+sensible	/man/2/sys-fversion
+sensible	/man/8/getauthinfo
+sensible	/man/8/prep
+sensibly	/man/8/httpd
+sensitive	/man/10/eve
+sensitive	/man/10/plan9.ini
+sensitive	/man/3/touch
+sensitive	/man/6/colour
+sensitive	/man/9/grab
+sent	/man/1/acme
+sent	/man/1/collab-clients
+sent	/man/1/idea
+sent	/man/1/man
+sent	/man/1/mash
+sent	/man/1/miniterm
+sent	/man/1/plumb
+sent	/man/1/sh
+sent	/man/1/sh-file2chan
+sent	/man/1/sh-tk
+sent	/man/1/tkcmd
+sent	/man/1/tktester
+sent	/man/1/uuencode
+sent	/man/1/wm
+sent	/man/1/wm-sh
+sent	/man/10/odbc
+sent	/man/10/plan9.ini
+sent	/man/2/draw-context
+sent	/man/2/encoding
+sent	/man/2/filter
+sent	/man/2/ir
+sent	/man/2/keyring-0intro
+sent	/man/2/print
+sent	/man/2/security-0intro
+sent	/man/2/spree
+sent	/man/2/spree-cardlib
+sent	/man/2/spree-gather
+sent	/man/2/styxconv
+sent	/man/2/styxflush
+sent	/man/2/styxservers
+sent	/man/2/sys-file2chan
+sent	/man/2/timers
+sent	/man/2/tk
+sent	/man/2/tkclient
+sent	/man/2/wmclient
+sent	/man/2/wmsrv
+sent	/man/2/xml
+sent	/man/3/cons
+sent	/man/3/dbg
+sent	/man/3/draw
+sent	/man/3/ether
+sent	/man/3/ip
+sent	/man/3/tls
+sent	/man/3/vga
+sent	/man/4/acme
+sent	/man/4/iostats
+sent	/man/5/flush
+sent	/man/5/open
+sent	/man/5/stat
+sent	/man/5/version
+sent	/man/6/login
+sent	/man/7/db
+sent	/man/8/collabsrv
+sent	/man/8/httpd
+sent	/man/8/plumber
+sent	/man/8/prep
+sent	/man/8/register
+sent	/man/8/signer
+sent	/man/8/styxchat
+sent	/man/8/styxmon
+sent	/man/9/send
+sep	/man/1/cal
+sep	/man/2/w3c-css
+separately	/man/1/ls
+separately	/man/1/man
+separately	/man/1/secstore
+separately	/man/1/tail
+separately	/man/10/qio
+separately	/man/2/secstore
+separately	/man/2/sys-pctl
+separately	/man/3/cmd
+separately	/man/3/draw
+separately	/man/3/ip
+separately	/man/3/mnt
+separately	/man/3/mpeg
+separately	/man/3/pipe
+separately	/man/3/rtc
+separating	/man/1/charon
+separating	/man/1/mk
+separating	/man/10/mk
+separating	/man/2/cfg
+separation	/man/1/alphabet-abc
+separation	/man/1/alphabet-grid
+separation	/man/1/sh-mload
+separator	/man/1/m4
+separator	/man/1/sh-std
+separator	/man/1/sh-string
+separator	/man/1/toolbar
+separator	/man/10/plan9.ini
+separator	/man/2/bufio
+separator	/man/2/prefab-element
+separator	/man/9/0intro
+separator	/man/9/menu
+separators	/man/1/m4
+separators	/man/1/mash
+separators	/man/1/sh
+separators	/man/1/sh-std
+separators	/man/10/parsecmd
+separators	/man/2/dial
+separators	/man/2/prefab-compound
+separators	/man/2/prefab-element
+sepchar	/man/2/bufio
+seperated	/man/1/math-misc
+seperated	/man/3/prof
+seprint	/man/10/print
+sepstring	/man/2/bufio
+seq	/man/1/sh-expr
+seq	/man/2/asn1
+seq	/man/2/spki
+seq	/man/2/spki-verifier
+seq	/man/8/collabsrv
+seqel	/man/2/spki
+seqel	/man/2/spki-verifier
+seqeql	/man/2/spki
+sequences	/man/1/freq
+sequences	/man/1/limbo
+sequences	/man/1/m4
+sequences	/man/1/tsort
+sequences	/man/1/wc
+sequences	/man/1/wish
+sequences	/man/1/xd
+sequences	/man/10/2c
+sequences	/man/10/9load
+sequences	/man/10/acid
+sequences	/man/2/asn1
+sequences	/man/2/convcs
+sequences	/man/2/sexprs
+sequences	/man/2/spki
+sequences	/man/2/spki-verifier
+sequences	/man/2/sys-tokenize
+sequences	/man/6/keyboard
+sequences	/man/6/sexprs
+sequences	/man/6/translate
+sequences	/man/6/utf
+sequences	/man/9/0intro
+sequences	/man/9/canvas
+sequential	/man/2/imagefile
+sequential	/man/2/stringinttab
+sequential	/man/2/sys-0intro
+sequentially	/man/1/acme
+sequentially	/man/1/mk
+sequentially	/man/10/mk
+sequentially	/man/2/sys-0intro
+sequentially	/man/2/sys-read
+sequentially	/man/3/ds
+sequentially	/man/4/acme
+serial	/man/1/avr
+serial	/man/10/5cv
+serial	/man/10/plan9.ini
+serial	/man/10/qio
+serial	/man/10/qlock
+serial	/man/3/cons
+serial	/man/3/dbg
+serial	/man/3/eia
+serial	/man/3/ip
+serial	/man/3/pnp
+serial	/man/3/rtc
+serial	/man/4/export
+serial	/man/4/palmsrv
+serial	/man/8/mangaload
+serial	/man/8/manufacture
+serial	/man/8/rdbgsrv
+serial0	/man/10/plan9.ini
+serialise	/man/10/qlock
+serialised	/man/10/sleep
+series	/man/1/cprof
+series	/man/1/mash
+series	/man/1/math-misc
+series	/man/10/plan9.ini
+series	/man/2/crc
+series	/man/2/draw-font
+series	/man/2/draw-image
+series	/man/3/draw
+series	/man/9/canvas
+serve	/man/1/calendar
+serve	/man/1/emu
+serve	/man/1/grid-ns
+serve	/man/1/listen
+serve	/man/1/mash
+serve	/man/1/sh-file2chan
+serve	/man/1/tiny
+serve	/man/2/prefab-element
+serve	/man/2/styxpersist
+serve	/man/2/styxservers-nametree
+serve	/man/2/wmsrv
+serve	/man/4/0intro
+serve	/man/4/dossrv
+serve	/man/4/export
+serve	/man/4/grid-cpu
+serve	/man/4/import
+serve	/man/4/kfs
+serve	/man/4/memfs
+serve	/man/5/0intro
+serve	/man/8/cs
+serve	/man/8/init
+serve	/man/8/mkfs
+serve	/man/8/rdbgsrv
+serve	/man/8/styxmon
+serve	/man/9/text
+served	/man/1/9win
+served	/man/1/acme
+served	/man/1/collab
+served	/man/1/logwindow
+served	/man/10/dev
+served	/man/10/devattach
+served	/man/10/master
+served	/man/2/styxservers
+served	/man/2/sys-bind
+served	/man/2/tk
+served	/man/3/cons
+served	/man/3/ip
+served	/man/3/mnt
+served	/man/3/pnp
+served	/man/4/grid-cpu
+served	/man/4/import
+served	/man/4/keysrv
+served	/man/4/lockfs
+served	/man/4/registry
+served	/man/5/0intro
+served	/man/8/changelogin
+served	/man/8/httpd
+server	/man/1/9win
+server	/man/1/acme
+server	/man/1/alphabet-abc
+server	/man/1/alphabet-grid
+server	/man/1/alphabet-main
+server	/man/1/bind
+server	/man/1/charon
+server	/man/1/chgrp
+server	/man/1/collab
+server	/man/1/cp
+server	/man/1/emu
+server	/man/1/ftest
+server	/man/1/listen
+server	/man/1/miniterm
+server	/man/1/passwd
+server	/man/1/secstore
+server	/man/1/sendmail
+server	/man/1/vacget
+server	/man/1/webgrab
+server	/man/10/9load
+server	/man/10/devattach
+server	/man/10/odbc
+server	/man/10/plan9.ini
+server	/man/10/styxserver
+server	/man/2/dhcpclient
+server	/man/2/dial
+server	/man/2/factotum
+server	/man/2/format
+server	/man/2/keyring-auth
+server	/man/2/plumbmsg
+server	/man/2/pop3
+server	/man/2/secstore
+server	/man/2/security-0intro
+server	/man/2/security-auth
+server	/man/2/smtp
+server	/man/2/spree
+server	/man/2/styx
+server	/man/2/styxconv
+server	/man/2/styxflush
+server	/man/2/styxpersist
+server	/man/2/styxservers
+server	/man/2/sys-0intro
+server	/man/2/sys-bind
+server	/man/2/sys-dial
+server	/man/2/sys-export
+server	/man/2/sys-fauth
+server	/man/2/sys-file2chan
+server	/man/2/sys-fversion
+server	/man/2/sys-open
+server	/man/2/sys-pctl
+server	/man/2/sys-stat
+server	/man/2/tftp
+server	/man/2/venti
+server	/man/3/0intro
+server	/man/3/draw
+server	/man/3/ds
+server	/man/3/ip
+server	/man/3/kprof
+server	/man/3/mnt
+server	/man/3/pbus
+server	/man/3/prog
+server	/man/3/srv
+server	/man/3/srv9
+server	/man/3/tinyfs
+server	/man/3/tls
+server	/man/4/factotum
+server	/man/4/ftpfs
+server	/man/4/grid-cpu
+server	/man/4/iostats
+server	/man/4/keyfs
+server	/man/4/keysrv
+server	/man/4/lockfs
+server	/man/4/namespace
+server	/man/4/palmsrv
+server	/man/4/registry
+server	/man/4/vacfs
+server	/man/5/0intro
+server	/man/5/attach
+server	/man/5/clunk
+server	/man/5/flush
+server	/man/5/open
+server	/man/5/remove
+server	/man/5/stat
+server	/man/5/version
+server	/man/5/walk
+server	/man/6/keys
+server	/man/6/ndb
+server	/man/6/users
+server	/man/7/cddb
+server	/man/7/db
+server	/man/7/dbsrv
+server	/man/8/ai2key
+server	/man/8/applylog
+server	/man/8/bootpd
+server	/man/8/collabsrv
+server	/man/8/createsignerkey
+server	/man/8/cs
+server	/man/8/dhcp
+server	/man/8/dns
+server	/man/8/getauthinfo
+server	/man/8/httpd
+server	/man/8/init
+server	/man/8/kfscmd
+server	/man/8/logind
+server	/man/8/mkfs
+server	/man/8/rdbgsrv
+server	/man/8/rstyxd
+server	/man/8/signer
+server	/man/8/sntp
+server	/man/8/styxchat
+server	/man/8/svc
+server	/man/8/virgild
+server's	/man/1/listen
+server's	/man/2/security-auth
+server's	/man/2/security-login
+server's	/man/2/styxservers
+server's	/man/2/sys-file2chan
+server's	/man/3/srv
+server's	/man/5/0intro
+server's	/man/5/attach
+server's	/man/5/stat
+server's	/man/5/version
+server's	/man/7/dbsrv
+server's	/man/8/applylog
+server's	/man/8/createsignerkey
+server's	/man/8/rstyxd
+server's	/man/8/styxchat
+serverid	/man/2/dhcpclient
+serverpath	/man/8/applylog
+serverroot	/man/8/applylog
+servers	/man/1/bind
+servers	/man/1/charon
+servers	/man/1/chgrp
+servers	/man/1/logon
+servers	/man/1/miniterm
+servers	/man/1/secstore
+servers	/man/10/eve
+servers	/man/10/plan9.ini
+servers	/man/2/dhcpclient
+servers	/man/2/factotum
+servers	/man/2/format
+servers	/man/2/registries
+servers	/man/2/styx
+servers	/man/2/styxpersist
+servers	/man/2/styxservers
+servers	/man/2/sys-0intro
+servers	/man/2/sys-stat
+servers	/man/2/virgil
+servers	/man/3/mnt
+servers	/man/4/0intro
+servers	/man/4/namespace
+servers	/man/5/0intro
+servers	/man/5/stat
+servers	/man/5/walk
+servers	/man/6/ndb
+servers	/man/8/ai2key
+servers	/man/8/bootpd
+servers	/man/8/changelogin
+servers	/man/8/collabsrv
+servers	/man/8/cs
+servers	/man/8/dns
+servers	/man/8/getauthinfo
+servers	/man/8/httpd
+servers	/man/8/svc
+servers.net	/man/6/ndb
+serves	/man/1/alphabet-main
+serves	/man/1/bind
+serves	/man/1/grid-ns
+serves	/man/1/grid-register
+serves	/man/1/wm-sh
+serves	/man/10/dev
+serves	/man/10/devattach
+serves	/man/2/keyring-gensk
+serves	/man/2/prefab-element
+serves	/man/2/styxconv
+serves	/man/2/sys-pipe
+serves	/man/3/audio
+serves	/man/3/boot
+serves	/man/3/cmd
+serves	/man/3/cons
+serves	/man/3/dbg
+serves	/man/3/draw
+serves	/man/3/ds
+serves	/man/3/dup
+serves	/man/3/dynld
+serves	/man/3/eia
+serves	/man/3/env
+serves	/man/3/ether
+serves	/man/3/flash
+serves	/man/3/floppy
+serves	/man/3/fs
+serves	/man/3/ftl
+serves	/man/3/gpio
+serves	/man/3/i2c
+serves	/man/3/ip
+serves	/man/3/logfs
+serves	/man/3/mpeg
+serves	/man/3/pbus
+serves	/man/3/plap
+serves	/man/3/pnp
+serves	/man/3/prof
+serves	/man/3/prog
+serves	/man/3/sd
+serves	/man/3/snarf
+serves	/man/3/srv9
+serves	/man/3/switch
+serves	/man/3/touch
+serves	/man/3/tv
+serves	/man/3/usb
+serves	/man/3/vid
+serves	/man/4/acme
+serves	/man/4/export
+serves	/man/4/factotum
+serves	/man/4/import
+serves	/man/4/keyfs
+serves	/man/4/keysrv
+serves	/man/4/lockfs
+serves	/man/4/mntgen
+serves	/man/4/palmsrv
+serves	/man/4/registry
+serves	/man/4/spree
+serves	/man/4/vacfs
+serves	/man/5/attach
+serves	/man/8/collabsrv
+serves	/man/8/cs
+serves	/man/8/dns
+serves	/man/8/httpd
+serves	/man/8/rip
+serves	/man/8/styxmon
+service's	/man/2/registries
+service1	/man/3/srv9
+service2	/man/3/srv9
+servicedir	/man/1/collab-clients
+services	/man/1/0intro
+services	/man/1/charon
+services	/man/1/collab
+services	/man/1/collab-clients
+services	/man/1/cp
+services	/man/1/emu
+services	/man/1/miniterm
+services	/man/1/mux
+services	/man/1/ns
+services	/man/1/webgrab
+services	/man/10/ntsrv
+services	/man/2/0intro
+services	/man/2/registries
+services	/man/2/security-0intro
+services	/man/2/srv
+services	/man/2/sys-pctl
+services	/man/3/cmd
+services	/man/3/ip
+services	/man/3/srv9
+services	/man/3/usb
+services	/man/4/0intro
+services	/man/4/acme
+services	/man/4/factotum
+services	/man/4/mntgen
+services	/man/4/namespace
+services	/man/4/ramfile
+services	/man/4/registry
+services	/man/6/keys
+services	/man/6/namespace
+services	/man/8/0intro
+services	/man/8/bootpd
+services	/man/8/collabsrv
+services	/man/8/cs
+services	/man/8/getauthinfo
+services	/man/8/httpd
+services	/man/8/register
+services	/man/8/svc
+services	/man/8/virgild
+services.cfg	/man/8/collabsrv
+serving	/man/1/bind
+serving	/man/2/styxservers
+serving	/man/2/sys-bind
+serving	/man/2/sys-export
+serving	/man/2/sys-file2chan
+serving	/man/3/srv9
+serving	/man/4/export
+serving	/man/5/0intro
+serving	/man/8/getauthinfo
+serving	/man/8/httpd
+session	/man/1/charon
+session	/man/1/grid-ns
+session	/man/1/grid-session
+session	/man/1/spree-join
+session	/man/2/prof
+session	/man/2/spree
+session	/man/2/styxflush
+session	/man/2/venti
+session	/man/3/dbg
+session	/man/3/tls
+session	/man/5/0intro
+session	/man/5/version
+session	/man/5/walk
+session	/man/7/db
+session	/man/8/rdbgsrv
+session.b	/man/1/grid-session
+session.rpc	/man/2/venti
+sessions	/man/2/spree
+sessions	/man/2/styxflush
+sessions	/man/4/spree
+set.bytes	/man/2/sets
+set.str	/man/2/sets
+setalpha	/man/2/draw-0intro
+setalpha	/man/6/colour
+setappinfo	/man/2/palmfile
+setarchivename	/man/2/spree-cardlib
+setattr	/man/2/spree
+setattrvisibility	/man/2/spree
+setbpt	/man/2/debug
+setenv	/man/2/env
+setexcl	/man/2/spree-cardlib
+setexcmode	/man/2/exception
+setface	/man/2/spree-cardlib
+setfill	/man/2/bufio
+sethi	/man/1/yacc
+setid	/man/2/keyring-auth
+setid	/man/2/security-auth
+setimage	/man/2/wmsrv
+setjmp	/man/10/error
+setlocal	/man/2/sh
+setmaprgbv	/man/6/colour
+setname	/man/2/spree-objstore
+setoptions	/man/2/sh
+setorigin	/man/2/wmsrv
+setorigin:fn	/man/2/wmsrv
+setpri	/man/10/kproc
+setproc	/man/10/acid
+setrange	/man/2/spree-cardlib
+setroot	/man/1/alphabet-fs
+setroot	/man/1/fs
+sets.m	/man/2/bloomfilter
+sets.m	/man/2/sets
+sets.m	/man/2/spree
+sets.m	/man/2/spree-cardlib
+sets32.m	/man/2/sets
+setsortinfo	/man/2/palmfile
+setting	/man/1/auplay
+setting	/man/1/charon
+setting	/man/1/deb
+setting	/man/1/emu
+setting	/man/1/runas
+setting	/man/1/sh-std
+setting	/man/1/tktester
+setting	/man/10/a.out
+setting	/man/10/allocb
+setting	/man/10/plan9.ini
+setting	/man/2/dbm
+setting	/man/2/disks
+setting	/man/2/geodesy
+setting	/man/2/itslib
+setting	/man/2/json
+setting	/man/2/prefab-element
+setting	/man/2/print
+setting	/man/2/security-ssl
+setting	/man/2/spree-cardlib
+setting	/man/2/sys-stat
+setting	/man/2/ubfa
+setting	/man/3/cmd
+setting	/man/3/flash
+setting	/man/3/ftl
+setting	/man/3/i2c
+setting	/man/3/mpeg
+setting	/man/3/rtc
+setting	/man/3/vga
+setting	/man/4/factotum
+setting	/man/5/open
+setting	/man/6/man
+setting	/man/8/init
+setting	/man/9/canvas
+setting	/man/9/grid
+setting	/man/9/scrollbar
+setting	/man/9/text
+settings	/man/1/charon
+settings	/man/1/ebook
+settings	/man/10/mk
+settings	/man/10/panic
+settings	/man/10/plan9.ini
+settings	/man/2/geodesy
+settings	/man/2/print
+settings	/man/2/rfc822
+settings	/man/3/draw
+settings	/man/3/eia
+settings	/man/3/fs
+settings	/man/3/i2c
+settings	/man/3/ip
+settings	/man/3/pnp
+settings	/man/3/touch
+settings	/man/3/tv
+settings	/man/3/vga
+settings	/man/8/touchcal
+settings	/man/9/grid
+settings	/man/9/scrollbar
+settitle	/man/2/tkclient
+settitle	/man/2/wmclient
+setup	/man/1/grid-monitor
+setup	/man/10/plan9.ini
+setup	/man/2/keyring-crypt
+setup	/man/3/usb
+setup	/man/3/vga
+setusage	/man/2/arg
+setvalue	/man/9/choicebutton
+setvisibility	/man/2/spree
+sev	/man/2/itslib
+seven	/man/1/ps
+seven	/man/3/prog
+seventh	/man/1/m4
+severity	/man/1/sh-test
+severity	/man/2/itslib
+sexp	/man/1/sh-sexprs
+sexp	/man/2/format
+sexp	/man/2/sexprs
+sexp	/man/2/spki
+sexp	/man/2/spki-verifier
+sexp	/man/6/sexprs
+sexp.binary	/man/2/sexprs
+sexp.list	/man/2/sexprs
+sexp.parse	/man/2/format
+sexp.parse	/man/2/sexprs
+sexp.read	/man/2/sexprs
+sexp.string	/man/2/sexprs
+sexp.txt	/man/2/sexprs
+sexpr	/man/1/sh-sexprs
+sexpr	/man/2/spki
+sexpr	/man/6/sexprs
+sexprs	/man/1/sh-sexprs
+sexprs	/man/2/format
+sexprs	/man/2/json
+sexprs	/man/2/sexprs
+sexprs	/man/2/spki
+sexprs	/man/2/spki-verifier
+sexprs	/man/2/ubfa
+sexprs	/man/4/factotum
+sexprs	/man/6/json
+sexprs	/man/6/sexprs
+sexprs	/man/6/ubfa
+sexprs.b	/man/1/sh-sexprs
+sexprs.b	/man/2/sexprs
+sexprs.m	/man/2/format
+sexprs.m	/man/2/sexprs
+sexprs.m	/man/2/spki
+sexprs.m	/man/2/spki-verifier
+sfactotum	/man/4/factotum
+sfn	/man/1/sh-std
+sg	/man/1/sh-regex
+sget	/man/1/mash-tk
+sgi	/man/10/2l
+sgml	/man/1/brutus
+sgml	/man/1/cook
+sh	/man/1/0intro
+sh	/man/1/alphabet-abc
+sh	/man/1/alphabet-fs
+sh	/man/1/alphabet-grid
+sh	/man/1/alphabet-main
+sh	/man/1/bind
+sh	/man/1/cpu
+sh	/man/1/du
+sh	/man/1/echo
+sh	/man/1/env
+sh	/man/1/fc
+sh	/man/1/filename
+sh	/man/1/fs
+sh	/man/1/ftest
+sh	/man/1/itest
+sh	/man/1/listen
+sh	/man/1/m4
+sh	/man/1/man
+sh	/man/1/mc
+sh	/man/1/mk
+sh	/man/1/rcmd
+sh	/man/1/read
+sh	/man/1/sh
+sh	/man/1/sh-alphabet
+sh	/man/1/sh-arg
+sh	/man/1/sh-csv
+sh	/man/1/sh-expr
+sh	/man/1/sh-file2chan
+sh	/man/1/sh-mload
+sh	/man/1/sh-regex
+sh	/man/1/sh-sexprs
+sh	/man/1/sh-std
+sh	/man/1/sh-string
+sh	/man/1/sh-test
+sh	/man/1/sh-tk
+sh	/man/1/time
+sh	/man/1/tiny
+sh	/man/1/tkcmd
+sh	/man/1/toolbar
+sh	/man/1/wish
+sh	/man/1/wm-sh
+sh	/man/10/mk
+sh	/man/10/parsecmd
+sh	/man/2/0intro
+sh	/man/2/arg
+sh	/man/2/command
+sh	/man/2/csv
+sh	/man/2/factotum
+sh	/man/2/itslib
+sh	/man/2/sh
+sh	/man/2/string
+sh	/man/2/sys-file2chan
+sh	/man/2/sys-pctl
+sh	/man/2/sys-print
+sh	/man/2/tk
+sh	/man/2/wait
+sh	/man/3/cmd
+sh	/man/3/env
+sh	/man/3/kprof
+sh	/man/4/9srvfs
+sh	/man/4/dbfs
+sh	/man/4/factotum
+sh	/man/4/iostats
+sh	/man/4/registry
+sh	/man/6/man
+sh	/man/6/proto
+sh	/man/6/regexp
+sh	/man/8/kfscmd
+sh	/man/8/rstyxd
+sh	/man/8/shutdown
+sh	/man/8/styxchat
+sh	/man/8/svc
+sh	/man/9/0intro
+sh's	/man/1/sh
+sh.b	/man/1/tiny
+sh.b	/man/1/wm-sh
+sh.dis	/man/1/acme
+sh.dis	/man/1/emu
+sh.dis	/man/1/sh-alphabet
+sh.dis	/man/2/command
+sh.dis	/man/8/init
+sh.m	/man/1/tiny
+sh.m	/man/2/command
+sh.m	/man/2/sh
+sh.y	/man/1/sh
+sh.y	/man/2/sh
+sha	/man/1/secstore
+sha	/man/1/sum
+sha	/man/2/keyring-sha1
+sha	/man/2/keyset
+sha	/man/2/registries
+sha	/man/3/ssl
+sha	/man/6/keys
+sha	/man/6/login
+sha	/man/8/changelogin
+sha1	/man/1/sum
+sha1	/man/2/ida
+sha1	/man/2/keyring-0intro
+sha1	/man/2/keyring-sha1
+sha1	/man/2/keyset
+sha1	/man/2/security-0intro
+sha1	/man/2/spki
+sha1	/man/2/venti
+sha1	/man/3/cap
+sha1	/man/3/dynld
+sha1	/man/3/sign
+sha1	/man/3/tls
+sha1	/man/4/import
+sha1	/man/4/keysrv
+sha1	/man/6/keytext
+sha1	/man/8/ai2key
+sha1	/man/8/changelogin
+sha1.c	/man/2/keyring-sha1
+sha1dlen	/man/2/keyring-sha1
+sha1dlen	/man/3/cap
+sha1sum	/man/1/sum
+sha1sum.b	/man/1/sum
+shade	/man/6/colour
+shaded	/man/2/draw-example
+shades	/man/6/colour
+shadlen	/man/2/keyring-sha1
+shape	/man/2/imagefile
+shape	/man/9/canvas
+shaped	/man/2/draw-display
+shaped	/man/2/draw-image
+shaped	/man/9/canvas
+shapes	/man/1/collab-clients
+shapes	/man/2/draw-image
+share	/man/1/charon
+share	/man/1/sh-mload
+share	/man/1/tiny
+share	/man/10/acid
+share	/man/10/sleep
+share	/man/2/0intro
+share	/man/2/bufio
+share	/man/2/draw-0intro
+share	/man/2/draw-display
+share	/man/2/draw-rect
+share	/man/2/keyring-auth
+share	/man/2/styxflush
+share	/man/2/styxservers
+share	/man/2/sys-0intro
+share	/man/2/sys-pctl
+share	/man/2/sys-pipe
+share	/man/3/dbg
+share	/man/4/keyfs
+share	/man/5/0intro
+share	/man/8/collabsrv
+share	/man/8/prep
+share	/man/9/radiobutton
+shared	/man/1/collab-clients
+shared	/man/1/netkey
+shared	/man/1/passwd
+shared	/man/1/ps
+shared	/man/10/kproc
+shared	/man/10/lock
+shared	/man/10/newchan
+shared	/man/10/sleep
+shared	/man/2/command
+shared	/man/2/draw-context
+shared	/man/2/env
+shared	/man/2/factotum
+shared	/man/2/keyring-auth
+shared	/man/2/security-0intro
+shared	/man/2/sys-0intro
+shared	/man/3/logfs
+shared	/man/3/sign
+shared	/man/3/srv9
+shared	/man/4/factotum
+shared	/man/6/auth
+shared	/man/6/dis
+shared	/man/6/keys
+shared	/man/6/login
+shared	/man/8/ai2key
+shared	/man/8/collabsrv
+shared	/man/8/svc
+shared	/man/9/text
+sharemp	/man/2/dis
+sharemp	/man/6/dis
+shares	/man/10/kproc
+shares	/man/2/dhcpclient
+shares	/man/2/security-0intro
+shares	/man/2/sys-0intro
+shares	/man/2/sys-pctl
+shares	/man/7/db
+sharing	/man/10/intrenable
+sharing	/man/2/sexprs
+sharing	/man/2/sys-file2chan
+sharing	/man/2/sys-pctl
+sharing	/man/3/env
+sharing	/man/5/0intro
+sharing	/man/6/ndb
+sharing	/man/8/collabsrv
+shctl	/man/1/wm-sh
+sheet	/man/2/w3c-css
+sheets	/man/2/w3c-css
+shell	/man/1/0intro
+shell	/man/1/acme
+shell	/man/1/alphabet-fs
+shell	/man/1/alphabet-main
+shell	/man/1/bind
+shell	/man/1/cd
+shell	/man/1/echo
+shell	/man/1/emu
+shell	/man/1/fc
+shell	/man/1/filename
+shell	/man/1/fs
+shell	/man/1/grid-ns
+shell	/man/1/grid-query
+shell	/man/1/idea
+shell	/man/1/listen
+shell	/man/1/mash
+shell	/man/1/mash-tk
+shell	/man/1/mk
+shell	/man/1/p
+shell	/man/1/sh
+shell	/man/1/sh-alphabet
+shell	/man/1/sh-arg
+shell	/man/1/sh-expr
+shell	/man/1/sh-file2chan
+shell	/man/1/sh-mload
+shell	/man/1/sh-regex
+shell	/man/1/sh-std
+shell	/man/1/sh-string
+shell	/man/1/sh-test
+shell	/man/1/sh-tk
+shell	/man/1/tiny
+shell	/man/1/toolbar
+shell	/man/1/wish
+shell	/man/1/wm-sh
+shell	/man/10/acid
+shell	/man/10/mk
+shell	/man/10/odbc
+shell	/man/10/plan9.ini
+shell	/man/2/0intro
+shell	/man/2/alphabet-intro
+shell	/man/2/arg
+shell	/man/2/draw-context
+shell	/man/2/draw-example
+shell	/man/2/sh
+shell	/man/2/sys-pctl
+shell	/man/3/0intro
+shell	/man/4/grid-cpu
+shell	/man/4/iostats
+shell	/man/9/0intro
+shell's	/man/1/mash-tk
+shell's	/man/1/tiny
+shell's	/man/1/wm-sh
+shell's	/man/2/sh
+shellbuiltin	/man/2/sh
+shells	/man/1/ftest
+shells	/man/1/sh-std
+shells	/man/1/wm-sh
+shells	/man/2/command
+shells	/man/2/sh
+shield	/man/6/login
+shift	/man/1/fc
+shift	/man/1/sh-expr
+shift	/man/9/scrollbar
+shifts	/man/1/m4
+shifts	/man/9/canvas
+shifts	/man/9/entry
+shifts	/man/9/listbox
+shifts	/man/9/text
+shl	/man/1/fc
+shl	/man/1/sh-expr
+shl	/man/2/keyring-ipint
+shnew.b	/man/1/stack
+shnew.b:105.7	/man/1/stack
+shnew.b:483.2	/man/1/stack
+shnew.b:552.3	/man/1/stack
+shnew.b:83.3	/man/1/stack
+shorn	/man/10/devattach
+shorn	/man/2/names
+shorn	/man/2/w3c-css
+short	/man/1/dd
+short	/man/1/fmt
+short	/man/1/tr
+short	/man/10/c2l
+short	/man/10/devattach
+short	/man/10/inb
+short	/man/10/lock
+short	/man/10/print
+short	/man/10/splhi
+short	/man/10/styx
+short	/man/2/palmfile
+short	/man/2/print
+short	/man/2/w3c-xpointers
+short	/man/3/mnt
+short	/man/3/tv
+short	/man/4/factotum
+short	/man/6/man
+short	/man/6/ndb
+short	/man/9/text
+shortened	/man/6/utf
+shorter	/man/1/cprof
+shorter	/man/1/mprof
+shorter	/man/1/prof
+shorter	/man/10/qio
+shorter	/man/9/canvas
+shorter	/man/9/text
+shortest	/man/6/utf
+shorthand	/man/2/sexprs
+shorthand	/man/2/w3c-xpointers
+shorthand	/man/5/0intro
+shorthands	/man/6/keyboard
+shortly	/man/2/ip
+shorts	/man/10/inb
+showdata	/man/1/plumb
+showing	/man/1/ar
+showing	/man/1/asm
+showing	/man/1/blur
+showing	/man/1/collab-clients
+showing	/man/1/cprof
+showing	/man/1/mprof
+showing	/man/1/prof
+showing	/man/10/iar
+showing	/man/2/styx
+showing	/man/3/dbg
+showing	/man/3/sign
+showing	/man/9/choicebutton
+showing	/man/9/menu
+shown	/man/1/cprof
+shown	/man/1/deb
+shown	/man/1/filename
+shown	/man/1/ns
+shown	/man/1/ps
+shown	/man/1/wm-misc
+shown	/man/1/wm-sh
+shown	/man/10/devattach
+shown	/man/10/error
+shown	/man/10/kproc
+shown	/man/2/command
+shown	/man/2/math-linalg
+shown	/man/2/palmfile
+shown	/man/2/prof
+shown	/man/2/sexprs
+shown	/man/2/sys-stat
+shown	/man/2/tkclient
+shown	/man/2/wmclient
+shown	/man/3/fs
+shown	/man/3/root
+shown	/man/3/sign
+shown	/man/3/srv9
+shown	/man/4/grid-cpu
+shown	/man/4/kfs
+shown	/man/6/sexprs
+shown	/man/8/create
+shown	/man/8/register
+shown	/man/9/see
+showprogress	/man/1/charon
+shows	/man/1/collab-clients
+shows	/man/1/cprof
+shows	/man/1/deb
+shows	/man/1/grid-monitor
+shows	/man/1/grid-session
+shows	/man/1/logwindow
+shows	/man/1/mprof
+shows	/man/1/prof
+shows	/man/1/tktester
+shows	/man/1/toolbar
+shows	/man/1/wm-misc
+shows	/man/10/print
+shows	/man/10/styxserver
+shows	/man/10/xalloc
+shows	/man/2/math-0intro
+shows	/man/2/prof
+shows	/man/2/security-auth
+shows	/man/2/translate
+shows	/man/3/ftl
+shows	/man/3/gpio
+shows	/man/3/ip
+shows	/man/8/styxchat
+showvalue	/man/9/scale
+shr	/man/1/fc
+shr	/man/1/sh-expr
+shr	/man/2/keyring-ipint
+shrinks	/man/9/grid
+shrinks	/man/9/pack
+shuffle	/man/2/spree-cardlib
+shuffles	/man/2/spree-cardlib
+shut	/man/10/dev
+shut	/man/10/devattach
+shut	/man/2/draw-context
+shut	/man/2/styxservers-nametree
+shut	/man/2/timers
+shut	/man/4/ftpfs
+shut	/man/4/palmsrv
+shut	/man/8/0intro
+shut	/man/8/shutdown
+shutdown	/man/10/dev
+shutdown	/man/2/plumbmsg
+shutdown	/man/2/sys-open
+shutdown	/man/2/timers
+shutdown	/man/8/shutdown
+shuts	/man/2/drawmux
+shuts	/man/2/plumbmsg
+shutting	/man/2/drawmux
+shutting	/man/3/logfs
+shuttling	/man/4/factotum
+si	/man/2/dis
+si	/man/6/dis
+siaddr	/man/2/dhcpclient
+sibling	/man/10/styxserver
+sibling	/man/2/w3c-xpointers
+siblings	/man/9/lower
+siblings	/man/9/raise
+sic	/man/10/plan9.ini
+sic	/man/2/dhcpclient
+sic	/man/4/9srvfs
+sic	/man/7/db
+sides	/man/5/version
+sieve	/man/1/cprof
+sieve	/man/1/math-misc
+sieve.b	/man/1/math-misc
+sieve.dis	/man/1/cprof
+sig	/man/10/dynld
+sig	/man/2/dis
+sig	/man/2/spki
+sig	/man/6/auth
+sig	/man/6/dis
+sig	/man/6/keytext
+sig0	/man/2/keyring-auth
+sig0	/man/6/auth
+sig1	/man/2/keyring-auth
+sig1	/man/6/auth
+sig2icert	/man/2/spki
+sigalg	/man/2/keyring-0intro
+sigalg	/man/2/spki
+sigalg	/man/6/keytext
+sigalg	/man/8/ai2key
+sigalgs	/man/2/spki
+siggraph	/man/2/draw-image
+sigma	/man/1/calc
+sign	/man/1/calc
+sign	/man/1/cprof
+sign	/man/1/look
+sign	/man/1/mash
+sign	/man/1/mk
+sign	/man/1/sh
+sign	/man/1/sort
+sign	/man/10/2c
+sign	/man/10/a.out
+sign	/man/10/atoi
+sign	/man/10/mk
+sign	/man/10/plan9.ini
+sign	/man/10/print
+sign	/man/2/alphabet-intro
+sign	/man/2/daytime
+sign	/man/2/dis
+sign	/man/2/keyring-0intro
+sign	/man/2/keyring-sha1
+sign	/man/2/math-fp
+sign	/man/2/spki
+sign	/man/2/string
+sign	/man/2/sys-print
+sign	/man/2/w3c-css
+sign	/man/3/sign
+sign	/man/6/keyboard
+sign	/man/6/keys
+sign	/man/6/sbl
+sign	/man/8/register
+sign	/man/9/types
+signal	/man/10/dev
+signal	/man/10/intrenable
+signal	/man/3/eia
+signaled	/man/9/0intro
+signalled	/man/10/sleep
+signals	/man/10/dev
+signals	/man/2/bufio-chanfill
+signals	/man/2/sys-file2chan
+signals	/man/3/tv
+signals	/man/3/vga
+signature	/man/10/2c
+signature	/man/10/5cv
+signature	/man/10/dynld
+signature	/man/2/alphabet-intro
+signature	/man/2/command
+signature	/man/2/dis
+signature	/man/2/keyring-0intro
+signature	/man/2/keyring-sha1
+signature	/man/2/security-0intro
+signature	/man/2/spki
+signature	/man/3/dynld
+signature	/man/3/sign
+signature	/man/6/dis
+signature	/man/6/keytext
+signature	/man/8/createsignerkey
+signatures	/man/1/limbo
+signatures	/man/10/2c
+signatures	/man/2/alphabet-intro
+signatures	/man/2/keyring-0intro
+signatures	/man/2/keyring-sha1
+signatures	/man/2/security-0intro
+signatures	/man/2/spki
+signatures	/man/2/spki-verifier
+signatures	/man/8/createsignerkey
+signbytes	/man/2/spki
+signcert	/man/2/spki
+signctl	/man/3/sign
+signed	/man/1/m4
+signed	/man/1/passwd
+signed	/man/1/sh-expr
+signed	/man/1/tail
+signed	/man/10/atoi
+signed	/man/2/dis
+signed	/man/2/keyring-0intro
+signed	/man/2/keyring-auth
+signed	/man/2/keyset
+signed	/man/2/security-0intro
+signed	/man/2/security-login
+signed	/man/2/spki
+signed	/man/2/spki-verifier
+signed	/man/2/string
+signed	/man/2/styx
+signed	/man/2/sys-print
+signed	/man/3/draw
+signed	/man/3/sign
+signed	/man/3/tv
+signed	/man/4/factotum
+signed	/man/6/auth
+signed	/man/6/dis
+signed	/man/6/keytext
+signed	/man/8/ai2key
+signed	/man/8/signer
+signed	/man/8/svc
+signer	/man/1/mux
+signer	/man/1/passwd
+signer	/man/2/dial
+signer	/man/2/keyring-0intro
+signer	/man/2/keyring-sha1
+signer	/man/2/keyset
+signer	/man/2/registries
+signer	/man/2/security-0intro
+signer	/man/2/security-login
+signer	/man/2/spki
+signer	/man/2/sys-dial
+signer	/man/3/sign
+signer	/man/4/factotum
+signer	/man/4/keyfs
+signer	/man/4/namespace
+signer	/man/6/keys
+signer	/man/6/keytext
+signer	/man/6/login
+signer	/man/6/ndb
+signer	/man/8/ai2key
+signer	/man/8/changelogin
+signer	/man/8/createsignerkey
+signer	/man/8/cs
+signer	/man/8/getauthinfo
+signer	/man/8/logind
+signer	/man/8/register
+signer	/man/8/signer
+signer	/man/8/svc
+signer's	/man/2/keyring-0intro
+signer's	/man/2/keyring-sha1
+signer's	/man/2/keyset
+signer's	/man/2/registries
+signer's	/man/2/security-login
+signer's	/man/3/sign
+signer's	/man/4/factotum
+signer's	/man/6/keys
+signer's	/man/6/keytext
+signer's	/man/8/ai2key
+signer's	/man/8/createsignerkey
+signer's	/man/8/getauthinfo
+signer's	/man/8/logind
+signer's	/man/8/signer
+signer.b	/man/8/signer
+signer.froop.com	/man/8/getauthinfo
+signerkey	/man/3/sign
+signerkey	/man/4/namespace
+signerkey	/man/8/createsignerkey
+signerkey	/man/8/logind
+signerkey	/man/8/signer
+signerkey	/man/8/svc
+signername	/man/2/keyset
+signerpkhash	/man/2/registries
+signers	/man/4/namespace
+significance	/man/9/text
+significant	/man/1/mdb
+significant	/man/10/memory
+significant	/man/10/print
+significant	/man/2/draw-0intro
+significant	/man/2/draw-display
+significant	/man/2/draw-image
+significant	/man/2/draw-pointer
+significant	/man/2/itslib
+significant	/man/2/math-export
+significant	/man/2/sets
+significant	/man/2/sys-print
+significant	/man/3/tls
+significant	/man/5/0intro
+significant	/man/5/stat
+significant	/man/6/colour
+significant	/man/6/dis
+significant	/man/6/proto
+significant	/man/8/ai2key
+significant	/man/8/create
+significant	/man/9/text
+significantly	/man/9/0intro
+signifier	/man/3/cons
+signifies	/man/1/grid-monitor
+signifies	/man/2/spree
+signify	/man/1/grid-monitor
+signify	/man/10/styxserver
+signify	/man/5/flush
+signify	/man/8/cs
+signifying	/man/1/ftest
+signifying	/man/10/sleep
+signing	/man/2/security-0intro
+signing	/man/2/sexprs
+signing	/man/2/spki
+signing	/man/3/sign
+signing	/man/6/dis
+signing	/man/6/keys
+signing	/man/6/keytext
+signing	/man/6/login
+signing	/man/6/sexprs
+signing	/man/8/changelogin
+signing	/man/8/getauthinfo
+signing	/man/8/logind
+signing	/man/8/register
+signing	/man/8/signer
+signing	/man/8/svc
+signof	/man/10/2c
+signof	/man/10/dynld
+signs	/man/10/acid
+signs	/man/10/atoi
+signs	/man/2/spki
+signs	/man/6/auth
+sigplan	/man/2/ubfa
+sigplan	/man/6/ubfa
+silent	/man/2/math-fp
+silently	/man/1/mv
+silently	/man/3/ds
+similar	/man/1/0intro
+similar	/man/1/acme
+similar	/man/1/alphabet-fs
+similar	/man/1/dd
+similar	/man/1/diff
+similar	/man/1/fs
+similar	/man/1/listen
+similar	/man/1/mash
+similar	/man/1/sh-alphabet
+similar	/man/1/sh-file2chan
+similar	/man/1/sh-regex
+similar	/man/1/sh-std
+similar	/man/1/sh-string
+similar	/man/1/sum
+similar	/man/1/webgrab
+similar	/man/10/a.out
+similar	/man/10/allocb
+similar	/man/10/delay
+similar	/man/10/dev
+similar	/man/10/devattach
+similar	/man/10/dmainit
+similar	/man/10/error
+similar	/man/10/iar
+similar	/man/10/inb
+similar	/man/10/lock
+similar	/man/10/malloc
+similar	/man/10/print
+similar	/man/10/sleep
+similar	/man/2/bufio
+similar	/man/2/debug
+similar	/man/2/dhcpclient
+similar	/man/2/dis
+similar	/man/2/draw-image
+similar	/man/2/factotum
+similar	/man/2/keyring-crypt
+similar	/man/2/keyring-getmsg
+similar	/man/2/msgio
+similar	/man/2/palmfile
+similar	/man/2/print
+similar	/man/2/regex
+similar	/man/2/sexprs
+similar	/man/2/sh
+similar	/man/2/spki
+similar	/man/2/string
+similar	/man/2/sys-0intro
+similar	/man/2/sys-print
+similar	/man/2/translate
+similar	/man/2/ubfa
+similar	/man/2/w3c-uris
+similar	/man/2/wmsrv
+similar	/man/3/audio
+similar	/man/3/cmd
+similar	/man/3/dbg
+similar	/man/3/ftl
+similar	/man/3/srv9
+similar	/man/3/touch
+similar	/man/4/dossrv
+similar	/man/4/registry
+similar	/man/6/sbl
+similar	/man/6/sexprs
+similar	/man/8/prep
+similar	/man/8/register
+similar	/man/9/canvas
+similar	/man/9/cursor
+similar	/man/9/menu
+similar	/man/9/text
+similarly	/man/1/acme
+similarly	/man/1/diff
+similarly	/man/1/sh-tk
+similarly	/man/10/0intro
+similarly	/man/10/atoi
+similarly	/man/10/conf
+similarly	/man/10/plan9.ini
+similarly	/man/2/bufio
+similarly	/man/2/math-linalg
+similarly	/man/2/sexprs
+similarly	/man/2/spree-cardlib
+similarly	/man/2/sys-stat
+similarly	/man/2/w3c-css
+similarly	/man/3/ds
+similarly	/man/3/env
+similarly	/man/3/tls
+similarly	/man/5/stat
+similarly	/man/9/canvas
+similiarly	/man/1/sh-csv
+similiarly	/man/1/sh-sexprs
+simir	/man/2/ir
+simpath	/man/2/ir
+simpler	/man/1/fs
+simpler	/man/1/mux
+simpler	/man/1/tiny
+simpler	/man/10/plan9.ini
+simpler	/man/3/ds
+simpler	/man/8/rip
+simplesel	/man/2/w3c-css
+simplest	/man/1/grid-monitor
+simplest	/man/1/mash
+simplest	/man/1/mprof
+simplest	/man/1/sh
+simplest	/man/2/sh
+simplest	/man/2/tkclient
+simplest	/man/3/logfs
+simplest	/man/4/spree
+simplest	/man/9/bind
+simplicity	/man/1/cprof
+simplicity	/man/2/asn1
+simplification	/man/2/math-0intro
+simplifies	/man/10/error
+simplifies	/man/4/ramfile
+simplify	/man/10/readnum
+simplify	/man/2/keyring-0intro
+simplify	/man/5/walk
+simplify	/man/8/kfscmd
+simplistic	/man/1/mux
+simply	/man/1/0intro
+simply	/man/1/acme
+simply	/man/1/calc
+simply	/man/1/cprof
+simply	/man/1/deb
+simply	/man/1/grid-monitor
+simply	/man/1/idea
+simply	/man/1/man
+simply	/man/1/mv
+simply	/man/1/sh
+simply	/man/1/sh-alphabet
+simply	/man/10/dev
+simply	/man/10/devattach
+simply	/man/10/intrenable
+simply	/man/10/print
+simply	/man/10/styxserver
+simply	/man/2/dbm
+simply	/man/2/draw-image
+simply	/man/2/factotum
+simply	/man/2/prof
+simply	/man/2/scsiio
+simply	/man/2/secstore
+simply	/man/2/smtp
+simply	/man/2/spki
+simply	/man/2/styx
+simply	/man/2/sys-dup
+simply	/man/2/sys-print
+simply	/man/3/indir
+simply	/man/3/ip
+simply	/man/3/logfs
+simply	/man/3/touch
+simply	/man/4/registry
+simply	/man/4/tarfs
+simply	/man/6/attrdb
+simply	/man/8/applylog
+simply	/man/8/svc
+simulate	/man/1/bind
+simulate	/man/1/wm
+simulate	/man/2/ir
+simulate	/man/8/prep
+simulated	/man/2/ir
+simulates	/man/2/draw-display
+simulation	/man/2/ir
+simulator	/man/2/ir
+simultaneous	/man/1/acme
+simultaneous	/man/1/sh-mload
+simultaneously	/man/1/blur
+simultaneously	/man/1/mk
+simultaneously	/man/10/mk
+simultaneously	/man/2/wmsrv
+simultaneously	/man/3/draw
+simultaneously	/man/4/registry
+simultaneously	/man/8/styxchat
+sin	/man/1/calc
+sin	/man/2/math-elem
+sinclude	/man/1/m4
+sind	/man/2/draw-image
+singleton	/man/1/mash
+singleton	/man/2/prefab-compound
+singly	/man/2/math-linalg
+sinh	/man/1/calc
+sinh	/man/2/math-elem
+sink	/man/1/stream
+sink	/man/10/plan9.ini
+sink	/man/3/ip
+sis	/man/10/plan9.ini
+sis900	/man/10/plan9.ini
+sit	/man/2/spree-cardlib
+site	/man/1/collab
+site	/man/4/ftpfs
+site	/man/6/ndb
+site	/man/8/cs
+site	/man/8/getauthinfo
+site	/man/8/register
+site	/man/8/svc
+site's	/man/6/ndb
+sits	/man/8/rip
+sitting	/man/2/spree-cardlib
+situation	/man/1/charon
+situation	/man/5/0intro
+situations	/man/9/listbox
+sitution	/man/2/styxpersist
+six	/man/1/fs
+six	/man/10/a.out
+six	/man/2/ether
+six	/man/2/prefab-element
+six	/man/3/ip
+six	/man/3/kprof
+six	/man/3/plap
+six	/man/3/prog
+six	/man/3/usb
+six	/man/6/man
+sixteen	/man/5/walk
+sixth	/man/3/kprof
+size	/man/1/alphabet-fs
+size	/man/1/ar
+size	/man/1/blur
+size	/man/1/brutus
+size	/man/1/cook
+size	/man/1/dd
+size	/man/1/du
+size	/man/1/ebook
+size	/man/1/emu
+size	/man/1/fs
+size	/man/1/ftest
+size	/man/1/gettar
+size	/man/1/ls
+size	/man/1/mdb
+size	/man/1/secstore
+size	/man/1/sh-expr
+size	/man/1/sh-file2chan
+size	/man/1/sh-tk
+size	/man/1/wm
+size	/man/1/wm-misc
+size	/man/1/yacc
+size	/man/1/zeros
+size	/man/10/2c
+size	/man/10/9load
+size	/man/10/a.out
+size	/man/10/allocb
+size	/man/10/ar
+size	/man/10/dev
+size	/man/10/dynld
+size	/man/10/iar
+size	/man/10/ksize
+size	/man/10/malloc
+size	/man/10/ms2
+size	/man/10/plan9.ini
+size	/man/10/print
+size	/man/10/qio
+size	/man/10/readnum
+size	/man/10/styx
+size	/man/10/xalloc
+size	/man/2/0intro
+size	/man/2/dbm
+size	/man/2/devpointer
+size	/man/2/dhcpclient
+size	/man/2/dis
+size	/man/2/diskblocks
+size	/man/2/disks
+size	/man/2/dividers
+size	/man/2/draw-0intro
+size	/man/2/draw-display
+size	/man/2/draw-font
+size	/man/2/factotum
+size	/man/2/hash
+size	/man/2/keyring-crypt
+size	/man/2/math-linalg
+size	/man/2/palmfile
+size	/man/2/plumbmsg
+size	/man/2/prefab-compound
+size	/man/2/prefab-element
+size	/man/2/print
+size	/man/2/pslib
+size	/man/2/readdir
+size	/man/2/rfc822
+size	/man/2/secstore
+size	/man/2/security-0intro
+size	/man/2/sexprs
+size	/man/2/spree-cardlib
+size	/man/2/styx
+size	/man/2/styxservers
+size	/man/2/sys-0intro
+size	/man/2/sys-fversion
+size	/man/2/sys-iounit
+size	/man/2/sys-print
+size	/man/2/sys-seek
+size	/man/2/sys-stat
+size	/man/2/tkclient
+size	/man/2/wmclient
+size	/man/3/audio
+size	/man/3/cons
+size	/man/3/dbg
+size	/man/3/ds
+size	/man/3/dynld
+size	/man/3/flash
+size	/man/3/ftl
+size	/man/3/i2c
+size	/man/3/ip
+size	/man/3/kprof
+size	/man/3/logfs
+size	/man/3/mnt
+size	/man/3/mpeg
+size	/man/3/pipe
+size	/man/3/pnp
+size	/man/3/sd
+size	/man/3/tls
+size	/man/3/tv
+size	/man/3/usb
+size	/man/3/vga
+size	/man/4/kfs
+size	/man/4/logfile
+size	/man/4/memfs
+size	/man/5/0intro
+size	/man/5/attach
+size	/man/5/clunk
+size	/man/5/error
+size	/man/5/flush
+size	/man/5/open
+size	/man/5/read
+size	/man/5/remove
+size	/man/5/stat
+size	/man/5/version
+size	/man/5/walk
+size	/man/6/audio
+size	/man/6/colour
+size	/man/6/dis
+size	/man/6/man
+size	/man/6/sbl
+size	/man/8/ai2key
+size	/man/8/ftl
+size	/man/8/httpd
+size	/man/8/ping
+size	/man/8/prep
+size	/man/8/styxchat
+size	/man/9/bind
+size	/man/9/button
+size	/man/9/canvas
+size	/man/9/checkbutton
+size	/man/9/choicebutton
+size	/man/9/frame
+size	/man/9/grid
+size	/man/9/label
+size	/man/9/listbox
+size	/man/9/menubutton
+size	/man/9/options
+size	/man/9/pack
+size	/man/9/radiobutton
+size	/man/9/scale
+size	/man/9/scrollbar
+size	/man/9/text
+size:int	/man/2/hash
+sized	/man/10/dmainit
+sized	/man/10/qio
+sized	/man/10/styx
+sized	/man/2/diskblocks
+sized	/man/2/draw-rect
+sized	/man/2/prefab-element
+sized	/man/9/grid
+sized2m	/man/10/styx
+sizeof	/man/10/print
+sizes	/man/1/dd
+sizes	/man/10/a.out
+sizes	/man/10/dynld
+sizes	/man/10/malloc
+sizes	/man/10/print
+sizes	/man/10/styx
+sizes	/man/2/dbm
+sizes	/man/2/draw-font
+sizes	/man/2/draw-image
+sizes	/man/2/keyring-crypt
+sizes	/man/2/math-linalg
+sizes	/man/2/palmfile
+sizes	/man/2/prefab-element
+sizes	/man/2/print
+sizes	/man/3/flash
+sizes	/man/5/0intro
+sizes	/man/6/colour
+sizes	/man/6/dis
+sizes	/man/8/mkfs
+sizes	/man/9/grid
+sizes	/man/9/pack
+sizes	/man/9/update
+sizes2m	/man/10/styx
+sizing	/man/10/plan9.ini
+sk	/man/2/keyring-0intro
+sk	/man/2/keyring-certtostr
+sk	/man/2/keyring-gensk
+sk	/man/2/keyring-sha1
+sk	/man/2/spki
+sk	/man/6/auth
+sk	/man/8/ai2key
+skeleton	/man/2/styxflush
+skeleton	/man/4/namespace
+skip	/man/1/dd
+skip	/man/2/rfc822
+skipped	/man/1/tiny
+skipped	/man/1/vacget
+skipped	/man/4/archfs
+skipped	/man/5/0intro
+skipping	/man/5/0intro
+skips	/man/2/keyring-rc4
+skips	/man/2/sys-tokenize
+skips	/man/9/canvas
+skipws	/man/2/rfc822
+sktopk	/man/2/keyring-gensk
+sktostr	/man/2/keyring-certtostr
+sku	/man/2/keyring-auth
+sl	/man/2/sh
+slash	/man/1/acme
+slash	/man/1/basename
+slash	/man/1/crypt
+slash	/man/1/limbo
+slash	/man/1/mash
+slash	/man/1/mk
+slash	/man/1/sh
+slash	/man/1/tiny
+slash	/man/10/2c
+slash	/man/10/a.out
+slash	/man/10/c2l
+slash	/man/10/mk
+slash	/man/2/complete
+slash	/man/2/names
+slash	/man/2/palmfile
+slash	/man/2/sys-0intro
+slash	/man/2/w3c-xpointers
+slash	/man/3/pnp
+slash	/man/5/0intro
+slash	/man/6/font
+slashes	/man/1/acme
+slashes	/man/1/cleanname
+slashes	/man/10/a.out
+slashes	/man/2/names
+slashes	/man/2/sys-0intro
+slashes	/man/3/cmd
+slashes	/man/5/0intro
+slate	/man/1/sh-alphabet
+slave	/man/1/blur
+slave	/man/3/i2c
+slave	/man/3/usb
+slave	/man/9/grid
+slave	/man/9/pack
+slave's	/man/9/grid
+slave's	/man/9/pack
+slaves	/man/9/grid
+slaves	/man/9/pack
+sleep	/man/1/sleep
+sleep	/man/1/timestamp
+sleep	/man/10/allocb
+sleep	/man/10/delay
+sleep	/man/10/intrenable
+sleep	/man/10/kproc
+sleep	/man/10/lock
+sleep	/man/10/qio
+sleep	/man/10/qlock
+sleep	/man/10/sleep
+sleep	/man/10/splhi
+sleep	/man/2/draw-example
+sleep	/man/2/sys-millisec
+sleep	/man/2/sys-sleep
+sleep	/man/2/timers
+sleep.b	/man/1/sleep
+sleeping	/man/10/newchan
+sleeping	/man/10/qio
+sleeping	/man/10/sleep
+sleeps	/man/10/malloc
+slice	/man/1/sh-string
+slice	/man/2/secstore
+slice	/man/2/sexprs
+slices	/man/2/secstore
+slicing	/man/1/sh-string
+slider	/man/2/volume
+slider	/man/9/options
+slider	/man/9/scale
+slider	/man/9/scrollbar
+slider's	/man/9/scale
+sliderlength	/man/9/scale
+sliderrelief	/man/9/scale
+sliding	/man/6/image
+slight	/man/2/palmfile
+slight	/man/9/scrollbar
+slightly	/man/1/acme
+slightly	/man/1/dd
+slightly	/man/1/grid-monitor
+slightly	/man/2/prof
+slightly	/man/9/scrollbar
+slip	/man/2/filter
+slip	/man/2/filter-slip
+slip.b	/man/2/filter-slip
+slippath	/man/2/filter-slip
+sloppier	/man/2/math-0intro
+slot	/man/1/grid-monitor
+slot	/man/1/mk
+slot	/man/10/mk
+slot	/man/10/plan9.ini
+slot	/man/2/spree-cardlib
+slot	/man/3/pbus
+slot	/man/3/plap
+slots	/man/1/grid-monitor
+slots	/man/10/plan9.ini
+slots	/man/2/hash
+slots	/man/3/i82365
+slots	/man/8/prep
+slow	/man/1/sh
+slow	/man/9/text
+slower	/man/2/security-0intro
+slowest	/man/4/iostats
+slowly	/man/1/acme
+sm	/man/6/man
+smagic	/man/2/dis
+smagic	/man/6/dis
+smaller	/man/1/emu
+smaller	/man/1/tiny
+smaller	/man/2/draw-0intro
+smaller	/man/2/draw-image
+smaller	/man/2/json
+smaller	/man/2/math-0intro
+smaller	/man/2/prefab-element
+smaller	/man/2/sys-0intro
+smaller	/man/2/sys-iounit
+smaller	/man/2/ubfa
+smaller	/man/6/sbl
+smaller	/man/6/sexprs
+smaller	/man/9/grid
+smaller	/man/9/pack
+smallest	/man/1/diff
+smallest	/man/1/ls
+smallest	/man/1/math-misc
+smallest	/man/10/print
+smallest	/man/2/draw-rect
+smallest	/man/2/math-fp
+smallest	/man/2/sys-print
+smalloc	/man/10/malloc
+smalloc	/man/10/parsecmd
+smc	/man/10/plan9.ini
+smc91cxx	/man/10/conf
+smc91cxx	/man/10/plan9.ini
+smooth	/man/2/draw-image
+smooth	/man/3/draw
+smooth	/man/9/canvas
+smoothed	/man/9/canvas
+smoother	/man/2/imagefile
+smoothly	/man/1/wm
+smoothly	/man/9/options
+smprint	/man/10/print
+smtp	/man/1/sendmail
+smtp	/man/2/dhcpclient
+smtp	/man/2/smtp
+smtp	/man/6/ndb
+smtp.b	/man/2/smtp
+smtp.m	/man/2/smtp
+snapshot	/man/4/registry
+snarf	/man/1/acme
+snarf	/man/1/brutus
+snarf	/man/1/mash-tk
+snarf	/man/1/tktester
+snarf	/man/1/wm-misc
+snarf	/man/1/wm-sh
+snarf	/man/2/tkclient
+snarf	/man/2/wmclient
+snarf	/man/2/wmlib
+snarf	/man/3/snarf
+snarfget	/man/2/tkclient
+snarfget	/man/2/wmclient
+snarfget	/man/2/wmlib
+snarfput	/man/2/tkclient
+snarfput	/man/2/wmclient
+snarfput	/man/2/wmlib
+snicker	/man/2/sexprs
+sniff	/man/8/bootpd
+snmp	/man/3/ip
+snone	/man/2/scsiio
+snprint	/man/10/print
+sntp	/man/8/sntp
+sntp.b	/man/8/sntp
+soa	/man/6/ndb
+soa	/man/8/dns
+soap	/man/2/registries
+socket	/man/3/ip
+socket	/man/3/ssl
+socket	/man/8/logind
+sockets	/man/2/keyring-0intro
+sockets	/man/2/security-0intro
+sockets	/man/2/security-ssl
+sockets	/man/3/ip
+sockets	/man/3/ssl
+sockets	/man/3/tls
+soft	/man/1/keyboard
+soft	/man/1/miniterm
+soft	/man/10/dev
+soft	/man/3/draw
+software	/man/1/0intro
+software	/man/1/miniterm
+software	/man/10/2c
+software	/man/10/kbdputc
+software	/man/10/kproc
+software	/man/10/plan9.ini
+software	/man/10/sleep
+software	/man/2/keyring-sha1
+software	/man/2/palmfile
+software	/man/3/vga
+software	/man/8/create
+software	/man/9/1copyright
+solaris	/man/10/conf
+solaris	/man/10/styxserver
+solaris	/man/4/namespace
+sole	/man/1/acme
+sole	/man/3/prog
+solely	/man/10/devattach
+solid	/man/1/wm-misc
+solid	/man/9/canvas
+solution	/man/1/calc
+solution	/man/1/sh
+solve	/man/1/calc
+solve	/man/10/plan9.ini
+solvelimit	/man/1/calc
+solves	/man/1/math-misc
+solves	/man/2/security-0intro
+solvestep	/man/1/calc
+somedata	/man/1/alphabet-abc
+somedata	/man/1/alphabet-grid
+somedata.result	/man/1/alphabet-abc
+somedata.result	/man/1/alphabet-grid
+somehost	/man/1/listen
+somehost.com	/man/4/spree
+someone	/man/1/units
+someone	/man/2/security-0intro
+someone	/man/5/0intro
+someone	/man/6/login
+sometimes	/man/1/0intro
+sometimes	/man/1/charon
+sometimes	/man/1/mk
+sometimes	/man/1/tktester
+sometimes	/man/10/dev
+sometimes	/man/10/devattach
+sometimes	/man/10/mk
+sometimes	/man/10/print
+sometimes	/man/10/xalloc
+sometimes	/man/2/security-0intro
+sometimes	/man/2/sh
+sometimes	/man/3/dbg
+sometimes	/man/4/factotum
+sometimes	/man/5/0intro
+sometimes	/man/6/keyboard
+sometimes	/man/6/sexprs
+sometimes	/man/6/utf
+sometimes	/man/9/canvas
+somewhat	/man/1/ar
+somewhat	/man/1/sh
+somewhat	/man/10/iar
+somewhat	/man/2/dividers
+somewhat	/man/2/security-0intro
+somewhat	/man/2/sets
+somewhat	/man/5/0intro
+somewhere	/man/1/collab-clients
+somewhere	/man/1/spree-join
+somewhere	/man/9/text
+sons	/man/2/keyring-0intro
+sons	/man/2/security-0intro
+sons	/man/3/ssl
+soon	/man/1/wm-sh
+soon	/man/3/cons
+soon	/man/3/prog
+sopen	/man/2/attrdb
+sopen	/man/2/bufio
+sophisticated	/man/1/mash-tk
+sophistication	/man/1/math-misc
+sort	/man/1/acme
+sort	/man/1/comm
+sort	/man/1/look
+sort	/man/1/ls
+sort	/man/1/sort
+sort	/man/1/tsort
+sort	/man/1/uniq
+sort	/man/10/c2l
+sort	/man/10/inm
+sort	/man/10/newchan
+sort	/man/2/math-linalg
+sort	/man/2/palmfile
+sort	/man/2/readdir
+sort	/man/2/spree-cardlib
+sort	/man/2/tkclient
+sort	/man/2/wmclient
+sort	/man/9/scrollbar
+sortdir	/man/2/readdir
+sorted	/man/1/comm
+sorted	/man/1/look
+sorted	/man/1/ls
+sorted	/man/1/sort
+sorted	/man/1/tsort
+sorted	/man/10/dynld
+sorted	/man/10/inm
+sorted	/man/2/readdir
+sorted	/man/2/sys-dirread
+sorted	/man/6/keyboard
+sorted	/man/9/text
+sortinfo	/man/2/palmfile
+sorting	/man/2/draw-rect
+sorting	/man/2/math-linalg
+sorting	/man/2/palmfile
+sorting	/man/2/readdir
+sortkey	/man/2/readdir
+sorts	/man/1/sort
+sorts	/man/2/readdir
+sorts	/man/2/spree-cardlib
+sorts	/man/2/ubfa
+sorts	/man/6/dis
+sought	/man/1/limbo
+sought	/man/10/2c
+sought	/man/10/c2l
+sought	/man/10/conf
+sought	/man/10/devattach
+sought	/man/2/dial
+sought	/man/2/sys-dial
+sought	/man/8/bootpd
+sound	/man/10/plan9.ini
+sound	/man/3/dbg
+sound	/man/3/kprof
+source	/man/1/0intro
+source	/man/1/9win
+source	/man/1/acme
+source	/man/1/alphabet-abc
+source	/man/1/alphabet-fs
+source	/man/1/alphabet-grid
+source	/man/1/alphabet-main
+source	/man/1/ar
+source	/man/1/asm
+source	/man/1/auplay
+source	/man/1/avr
+source	/man/1/basename
+source	/man/1/bind
+source	/man/1/blur
+source	/man/1/brutus
+source	/man/1/cal
+source	/man/1/calc
+source	/man/1/calendar
+source	/man/1/cat
+source	/man/1/cd
+source	/man/1/charon
+source	/man/1/chgrp
+source	/man/1/chmod
+source	/man/1/cleanname
+source	/man/1/cmp
+source	/man/1/collab
+source	/man/1/collab-clients
+source	/man/1/comm
+source	/man/1/cook
+source	/man/1/cp
+source	/man/1/cprof
+source	/man/1/cpu
+source	/man/1/crypt
+source	/man/1/date
+source	/man/1/dd
+source	/man/1/deb
+source	/man/1/diff
+source	/man/1/disdep
+source	/man/1/dmview
+source	/man/1/du
+source	/man/1/ebook
+source	/man/1/echo
+source	/man/1/emu
+source	/man/1/env
+source	/man/1/filename
+source	/man/1/fmt
+source	/man/1/fortune
+source	/man/1/freq
+source	/man/1/fs
+source	/man/1/ftest
+source	/man/1/ftree
+source	/man/1/gettar
+source	/man/1/grep
+source	/man/1/grid-monitor
+source	/man/1/grid-ns
+source	/man/1/grid-query
+source	/man/1/grid-register
+source	/man/1/grid-session
+source	/man/1/gzip
+source	/man/1/idea
+source	/man/1/itest
+source	/man/1/keyboard
+source	/man/1/kill
+source	/man/1/limbo
+source	/man/1/listen
+source	/man/1/logon
+source	/man/1/logwindow
+source	/man/1/look
+source	/man/1/ls
+source	/man/1/man
+source	/man/1/mash
+source	/man/1/mash-make
+source	/man/1/mash-tk
+source	/man/1/math-misc
+source	/man/1/mc
+source	/man/1/mdb
+source	/man/1/miniterm
+source	/man/1/mk
+source	/man/1/mkdir
+source	/man/1/mprof
+source	/man/1/mux
+source	/man/1/mv
+source	/man/1/netkey
+source	/man/1/netstat
+source	/man/1/ns
+source	/man/1/nsbuild
+source	/man/1/os
+source	/man/1/p
+source	/man/1/passwd
+source	/man/1/plumb
+source	/man/1/prof
+source	/man/1/ps
+source	/man/1/pwd
+source	/man/1/rcmd
+source	/man/1/read
+source	/man/1/rm
+source	/man/1/runas
+source	/man/1/secstore
+source	/man/1/sendmail
+source	/man/1/sh
+source	/man/1/sh-alphabet
+source	/man/1/sh-arg
+source	/man/1/sh-csv
+source	/man/1/sh-expr
+source	/man/1/sh-file2chan
+source	/man/1/sh-mload
+source	/man/1/sh-regex
+source	/man/1/sh-sexprs
+source	/man/1/sh-std
+source	/man/1/sh-string
+source	/man/1/sh-tk
+source	/man/1/sleep
+source	/man/1/spree-join
+source	/man/1/stack
+source	/man/1/stream
+source	/man/1/strings
+source	/man/1/sum
+source	/man/1/tcs
+source	/man/1/tee
+source	/man/1/telnet
+source	/man/1/time
+source	/man/1/timestamp
+source	/man/1/tiny
+source	/man/1/tkcmd
+source	/man/1/tktester
+source	/man/1/toolbar
+source	/man/1/touch
+source	/man/1/tr
+source	/man/1/tsort
+source	/man/1/unicode
+source	/man/1/uniq
+source	/man/1/units
+source	/man/1/uuencode
+source	/man/1/vacget
+source	/man/1/wc
+source	/man/1/webgrab
+source	/man/1/wish
+source	/man/1/wm
+source	/man/1/wm-misc
+source	/man/1/wm-sh
+source	/man/1/xd
+source	/man/1/yacc
+source	/man/1/zeros
+source	/man/10/0intro
+source	/man/10/2a
+source	/man/10/2c
+source	/man/10/2l
+source	/man/10/5coff
+source	/man/10/5cv
+source	/man/10/9load
+source	/man/10/a.out
+source	/man/10/acid
+source	/man/10/allocb
+source	/man/10/atoi
+source	/man/10/c2l
+source	/man/10/conf
+source	/man/10/dev
+source	/man/10/devattach
+source	/man/10/dmainit
+source	/man/10/dynld
+source	/man/10/error
+source	/man/10/getfields
+source	/man/10/iar
+source	/man/10/inb
+source	/man/10/inm
+source	/man/10/intrenable
+source	/man/10/kbdputc
+source	/man/10/kprof
+source	/man/10/ksize
+source	/man/10/kstrip
+source	/man/10/lock
+source	/man/10/master
+source	/man/10/memory
+source	/man/10/mk
+source	/man/10/ms2
+source	/man/10/newchan
+source	/man/10/ntsrv
+source	/man/10/odbc
+source	/man/10/parsecmd
+source	/man/10/print
+source	/man/10/qio
+source	/man/10/qlock
+source	/man/10/readnum
+source	/man/10/ref
+source	/man/10/rune
+source	/man/10/sleep
+source	/man/10/srclist
+source	/man/10/strcat
+source	/man/10/styx
+source	/man/10/styxserver
+source	/man/2/arg
+source	/man/2/asn1
+source	/man/2/attrdb
+source	/man/2/bloomfilter
+source	/man/2/bufio
+source	/man/2/cfg
+source	/man/2/complete
+source	/man/2/convcs
+source	/man/2/crc
+source	/man/2/csv
+source	/man/2/daytime
+source	/man/2/dbm
+source	/man/2/debug
+source	/man/2/dhcpclient
+source	/man/2/dial
+source	/man/2/dialog
+source	/man/2/dict
+source	/man/2/dis
+source	/man/2/diskblocks
+source	/man/2/disks
+source	/man/2/dividers
+source	/man/2/draw-0intro
+source	/man/2/draw-example
+source	/man/2/draw-image
+source	/man/2/encoding
+source	/man/2/env
+source	/man/2/ether
+source	/man/2/exception
+source	/man/2/factotum
+source	/man/2/filepat
+source	/man/2/filter
+source	/man/2/filter-deflate
+source	/man/2/filter-slip
+source	/man/2/format
+source	/man/2/fsproto
+source	/man/2/geodesy
+source	/man/2/hash
+source	/man/2/ida
+source	/man/2/imagefile
+source	/man/2/ip
+source	/man/2/ir
+source	/man/2/json
+source	/man/2/keyring-0intro
+source	/man/2/keyring-auth
+source	/man/2/keyring-certtostr
+source	/man/2/keyring-getmsg
+source	/man/2/keyring-getstring
+source	/man/2/keyring-sha1
+source	/man/2/keyset
+source	/man/2/lists
+source	/man/2/lock
+source	/man/2/math-0intro
+source	/man/2/math-elem
+source	/man/2/math-export
+source	/man/2/math-fp
+source	/man/2/math-linalg
+source	/man/2/mpeg
+source	/man/2/msgio
+source	/man/2/names
+source	/man/2/newns
+source	/man/2/palmfile
+source	/man/2/plumbmsg
+source	/man/2/pop3
+source	/man/2/popup
+source	/man/2/prefab-0intro
+source	/man/2/prefab-compound
+source	/man/2/prefab-element
+source	/man/2/prefab-environ
+source	/man/2/prefab-style
+source	/man/2/print
+source	/man/2/prof
+source	/man/2/pslib
+source	/man/2/rand
+source	/man/2/readdir
+source	/man/2/regex
+source	/man/2/rfc822
+source	/man/2/scsiio
+source	/man/2/secstore
+source	/man/2/security-auth
+source	/man/2/security-login
+source	/man/2/security-random
+source	/man/2/security-ssl
+source	/man/2/selectfile
+source	/man/2/sexprs
+source	/man/2/sh
+source	/man/2/smtp
+source	/man/2/spki
+source	/man/2/spki-verifier
+source	/man/2/spree
+source	/man/2/spree-allow
+source	/man/2/spree-cardlib
+source	/man/2/srv
+source	/man/2/string
+source	/man/2/stringinttab
+source	/man/2/styx
+source	/man/2/styxconv
+source	/man/2/styxflush
+source	/man/2/styxpersist
+source	/man/2/styxservers
+source	/man/2/styxservers-nametree
+source	/man/2/sys-0intro
+source	/man/2/sys-byte2char
+source	/man/2/sys-dial
+source	/man/2/sys-export
+source	/man/2/sys-file2chan
+source	/man/2/sys-iounit
+source	/man/2/sys-print
+source	/man/2/tabs
+source	/man/2/tftp
+source	/man/2/tk
+source	/man/2/tkclient
+source	/man/2/translate
+source	/man/2/ubfa
+source	/man/2/venti
+source	/man/2/virgil
+source	/man/2/volume
+source	/man/2/w3c-css
+source	/man/2/w3c-uris
+source	/man/2/w3c-xpointers
+source	/man/2/wmclient
+source	/man/2/wmlib
+source	/man/2/wmsrv
+source	/man/2/workdir
+source	/man/2/xml
+source	/man/3/arch
+source	/man/3/audio
+source	/man/3/boot
+source	/man/3/cap
+source	/man/3/cmd
+source	/man/3/cons
+source	/man/3/dbg
+source	/man/3/draw
+source	/man/3/ds
+source	/man/3/dup
+source	/man/3/dynld
+source	/man/3/eia
+source	/man/3/env
+source	/man/3/ether
+source	/man/3/flash
+source	/man/3/floppy
+source	/man/3/fpga
+source	/man/3/fs
+source	/man/3/ftl
+source	/man/3/gpio
+source	/man/3/i2c
+source	/man/3/i82365
+source	/man/3/indir
+source	/man/3/ip
+source	/man/3/kprof
+source	/man/3/logfs
+source	/man/3/lpt
+source	/man/3/mnt
+source	/man/3/mpeg
+source	/man/3/pbus
+source	/man/3/pipe
+source	/man/3/plap
+source	/man/3/pnp
+source	/man/3/pointer
+source	/man/3/prof
+source	/man/3/prog
+source	/man/3/root
+source	/man/3/rtc
+source	/man/3/sd
+source	/man/3/sign
+source	/man/3/srv
+source	/man/3/srv9
+source	/man/3/switch
+source	/man/3/tinyfs
+source	/man/3/tls
+source	/man/3/touch
+source	/man/3/tv
+source	/man/3/usb
+source	/man/3/vga
+source	/man/3/vid
+source	/man/4/9srvfs
+source	/man/4/acme
+source	/man/4/archfs
+source	/man/4/dbfs
+source	/man/4/dossrv
+source	/man/4/export
+source	/man/4/factotum
+source	/man/4/ftpfs
+source	/man/4/grid-cpu
+source	/man/4/import
+source	/man/4/iostats
+source	/man/4/keyfs
+source	/man/4/keysrv
+source	/man/4/kfs
+source	/man/4/lockfs
+source	/man/4/logfile
+source	/man/4/memfs
+source	/man/4/mntgen
+source	/man/4/namespace
+source	/man/4/palmsrv
+source	/man/4/ramfile
+source	/man/4/registry
+source	/man/4/spree
+source	/man/4/tarfs
+source	/man/4/trfs
+source	/man/4/vacfs
+source	/man/6/dis
+source	/man/6/man
+source	/man/6/plumbing
+source	/man/6/proto
+source	/man/6/sbl
+source	/man/6/translate
+source	/man/7/cddb
+source	/man/7/db
+source	/man/7/dbsrv
+source	/man/8/ai2key
+source	/man/8/applylog
+source	/man/8/bootpd
+source	/man/8/changelogin
+source	/man/8/collabsrv
+source	/man/8/create
+source	/man/8/createsignerkey
+source	/man/8/cs
+source	/man/8/dhcp
+source	/man/8/dns
+source	/man/8/fpgaload
+source	/man/8/ftl
+source	/man/8/getauthinfo
+source	/man/8/httpd
+source	/man/8/init
+source	/man/8/kfscmd
+source	/man/8/logind
+source	/man/8/mangaload
+source	/man/8/manufacture
+source	/man/8/mkfs
+source	/man/8/ping
+source	/man/8/plumber
+source	/man/8/prep
+source	/man/8/rdbgsrv
+source	/man/8/register
+source	/man/8/rip
+source	/man/8/rstyxd
+source	/man/8/shutdown
+source	/man/8/signer
+source	/man/8/sntp
+source	/man/8/styxchat
+source	/man/8/styxmon
+source	/man/8/svc
+source	/man/8/touchcal
+source	/man/8/virgild
+source	/man/9/image
+sourcefile	/man/1/uuencode
+sourcefile	/man/10/acid
+sources	/man/10/odbc
+sources	/man/10/styxserver
+sources	/man/3/ip
+sources	/man/4/grid-cpu
+soutd	/man/2/draw-image
+soverd	/man/2/draw-image
+sp	/man/10/a.out
+sp	/man/2/draw-image
+sp	/man/3/draw
+sp	/man/6/man
+space	/man/1/0intro
+space	/man/1/acme
+space	/man/1/bind
+space	/man/1/calc
+space	/man/1/cd
+space	/man/1/charon
+space	/man/1/collab
+space	/man/1/collab-clients
+space	/man/1/crypt
+space	/man/1/diff
+space	/man/1/disdep
+space	/man/1/emu
+space	/man/1/fmt
+space	/man/1/gettar
+space	/man/1/listen
+space	/man/1/logon
+space	/man/1/mash
+space	/man/1/mash-tk
+space	/man/1/math-misc
+space	/man/1/mk
+space	/man/1/mprof
+space	/man/1/mux
+space	/man/1/ns
+space	/man/1/nsbuild
+space	/man/1/pwd
+space	/man/1/sh
+space	/man/1/tiny
+space	/man/1/tsort
+space	/man/1/wm-misc
+space	/man/1/wm-sh
+space	/man/10/9load
+space	/man/10/acid
+space	/man/10/allocb
+space	/man/10/conf
+space	/man/10/dev
+space	/man/10/devattach
+space	/man/10/dmainit
+space	/man/10/dynld
+space	/man/10/inb
+space	/man/10/kproc
+space	/man/10/malloc
+space	/man/10/master
+space	/man/10/mk
+space	/man/10/newchan
+space	/man/10/odbc
+space	/man/10/plan9.ini
+space	/man/10/print
+space	/man/10/strcat
+space	/man/10/xalloc
+space	/man/2/attrdb
+space	/man/2/bufio
+space	/man/2/daytime
+space	/man/2/dis
+space	/man/2/dividers
+space	/man/2/draw-font
+space	/man/2/drawmux
+space	/man/2/encoding
+space	/man/2/factotum
+space	/man/2/filter-deflate
+space	/man/2/geodesy
+space	/man/2/ip
+space	/man/2/ir
+space	/man/2/keyring-0intro
+space	/man/2/newns
+space	/man/2/palmfile
+space	/man/2/prefab-compound
+space	/man/2/prefab-element
+space	/man/2/registries
+space	/man/2/rfc822
+space	/man/2/secstore
+space	/man/2/sexprs
+space	/man/2/spki
+space	/man/2/spree
+space	/man/2/spree-allow
+space	/man/2/spree-cardlib
+space	/man/2/string
+space	/man/2/styx
+space	/man/2/styxservers
+space	/man/2/styxservers-nametree
+space	/man/2/sys-0intro
+space	/man/2/sys-bind
+space	/man/2/sys-chdir
+space	/man/2/sys-export
+space	/man/2/sys-fd2path
+space	/man/2/sys-file2chan
+space	/man/2/sys-pctl
+space	/man/2/tk
+space	/man/2/w3c-css
+space	/man/2/w3c-xpointers
+space	/man/2/xml
+space	/man/3/0intro
+space	/man/3/arch
+space	/man/3/cmd
+space	/man/3/cons
+space	/man/3/dbg
+space	/man/3/draw
+space	/man/3/ds
+space	/man/3/flash
+space	/man/3/fpga
+space	/man/3/fs
+space	/man/3/ftl
+space	/man/3/gpio
+space	/man/3/indir
+space	/man/3/ip
+space	/man/3/logfs
+space	/man/3/mnt
+space	/man/3/pnp
+space	/man/3/prof
+space	/man/3/prog
+space	/man/3/root
+space	/man/3/sign
+space	/man/3/srv
+space	/man/3/srv9
+space	/man/3/ssl
+space	/man/3/tls
+space	/man/3/touch
+space	/man/4/0intro
+space	/man/4/9srvfs
+space	/man/4/dossrv
+space	/man/4/export
+space	/man/4/factotum
+space	/man/4/ftpfs
+space	/man/4/import
+space	/man/4/iostats
+space	/man/4/keyfs
+space	/man/4/keysrv
+space	/man/4/mntgen
+space	/man/4/namespace
+space	/man/4/ramfile
+space	/man/4/registry
+space	/man/4/spree
+space	/man/4/tarfs
+space	/man/4/trfs
+space	/man/5/0intro
+space	/man/6/attrdb
+space	/man/6/colour
+space	/man/6/font
+space	/man/6/json
+space	/man/6/keyboard
+space	/man/6/keys
+space	/man/6/keytext
+space	/man/6/namespace
+space	/man/6/proto
+space	/man/6/sbl
+space	/man/6/sexprs
+space	/man/6/ubfa
+space	/man/8/changelogin
+space	/man/8/collabsrv
+space	/man/8/create
+space	/man/8/cs
+space	/man/8/httpd
+space	/man/8/prep
+space	/man/8/rdbgsrv
+space	/man/8/rstyxd
+space	/man/8/styxchat
+space	/man/8/styxmon
+space	/man/9/bind
+space	/man/9/button
+space	/man/9/canvas
+space	/man/9/grid
+space	/man/9/options
+space	/man/9/pack
+space	/man/9/scale
+space	/man/9/text
+spaced	/man/1/acme
+spaced	/man/2/print
+spaced	/man/9/text
+spacer	/man/9/frame
+spaces	/man/1/0intro
+spaces	/man/1/diff
+spaces	/man/1/fmt
+spaces	/man/1/mash
+spaces	/man/1/plumb
+spaces	/man/1/sh
+spaces	/man/1/sh-mload
+spaces	/man/1/sh-string
+spaces	/man/1/sh-tk
+spaces	/man/1/tktester
+spaces	/man/10/atoi
+spaces	/man/10/master
+spaces	/man/10/print
+spaces	/man/2/spree
+spaces	/man/2/sys-print
+spaces	/man/3/touch
+spaces	/man/4/factotum
+spaces	/man/4/import
+spaces	/man/4/trfs
+spaces	/man/6/attrdb
+spaces	/man/6/sbl
+spaces	/man/8/styxchat
+spaces	/man/9/bind
+spaces	/man/9/grid
+spaces	/man/9/text
+spacing	/man/2/cfg
+spacing	/man/2/draw-font
+spacing	/man/2/prefab-element
+spacing	/man/3/draw
+spacing	/man/6/font
+spacing	/man/9/scale
+spacing	/man/9/text
+spacing1	/man/9/text
+spacing2	/man/9/text
+spacing3	/man/9/text
+spades	/man/2/spree-cardlib
+span	/man/1/tiny
+span	/man/10/0intro
+span	/man/10/xalloc
+span	/man/2/attrdb
+span	/man/9/canvas
+span	/man/9/entry
+span	/man/9/listbox
+span	/man/9/text
+spanned	/man/9/grid
+spanning	/man/1/tktester
+spanning	/man/10/xalloc
+spanning	/man/9/entry
+spanning	/man/9/grid
+spans	/man/6/utf
+spans	/man/8/prep
+sparc	/man/10/2c
+sparc	/man/10/a.out
+sparc	/man/10/acid
+sparingly	/man/10/xalloc
+spawn	/man/2/command
+spawn	/man/2/dial
+spawn	/man/2/draw-display
+spawn	/man/2/sh
+spawn	/man/2/styxconv
+spawn	/man/2/styxflush
+spawn	/man/2/sys-0intro
+spawn	/man/2/sys-export
+spawn	/man/3/cmd
+spawn	/man/3/sign
+spawned	/man/1/math-misc
+spawned	/man/1/sh-file2chan
+spawned	/man/2/filter
+spawned	/man/2/sh
+spawned	/man/2/styxconv
+spawned	/man/2/sys-0intro
+spawned	/man/2/sys-pctl
+spawned	/man/2/volume
+spawned	/man/3/prog
+spawning	/man/1/stream
+spawns	/man/1/sh-file2chan
+spawns	/man/2/alphabet-intro
+spawns	/man/2/devpointer
+spawns	/man/2/exception
+spawns	/man/2/filter
+spawns	/man/2/ir
+spawns	/man/2/mpeg
+spawns	/man/2/styxservers
+spawns	/man/2/styxservers-nametree
+spawns	/man/2/sys-export
+spawns	/man/3/prog
+spawns	/man/8/cs
+speaks	/man/2/spki-verifier
+speaksfor	/man/2/spki-verifier
+speaksfor	/man/4/factotum
+spec	/man/1/alphabet-fs
+spec	/man/1/bind
+spec	/man/1/fs
+spec	/man/10/dev
+spec	/man/10/devattach
+spec	/man/2/dial
+spec	/man/2/format
+spec	/man/2/spki
+spec	/man/2/spree-cardlib
+spec	/man/3/fs
+spec	/man/3/indir
+spec	/man/3/srv
+spec	/man/6/namespace
+spec2fmt	/man/2/format
+spec2se	/man/2/format
+specfies	/man/9/scale
+specialised	/man/1/zeros
+specialised	/man/10/0intro
+specialised	/man/2/plumbmsg
+specialised	/man/2/styx
+specialised	/man/3/logfs
+specialised	/man/3/plap
+specialised	/man/8/plumber
+specialised	/man/9/send
+specialized	/man/2/security-0intro
+specially	/man/1/mk
+specially	/man/1/sh-std
+specially	/man/1/tiny
+specially	/man/10/allocb
+specially	/man/10/mk
+specially	/man/2/csv
+specially	/man/2/dialog
+specially	/man/3/cons
+specially	/man/8/cs
+specially	/man/9/bind
+species	/man/2/xml
+specifed	/man/9/pack
+specific	/man/1/acme
+specific	/man/1/charon
+specific	/man/1/grid-monitor
+specific	/man/1/grid-query
+specific	/man/1/grid-session
+specific	/man/1/mprof
+specific	/man/1/mux
+specific	/man/1/tktester
+specific	/man/10/0intro
+specific	/man/10/conf
+specific	/man/10/dev
+specific	/man/10/devattach
+specific	/man/10/dmainit
+specific	/man/10/intrenable
+specific	/man/10/xalloc
+specific	/man/2/asn1
+specific	/man/2/convcs
+specific	/man/2/dhcpclient
+specific	/man/2/factotum
+specific	/man/2/filter
+specific	/man/2/palmfile
+specific	/man/2/prefab-element
+specific	/man/2/print
+specific	/man/2/prof
+specific	/man/2/rfc822
+specific	/man/2/sexprs
+specific	/man/2/sh
+specific	/man/2/spree-allow
+specific	/man/2/spree-cardlib
+specific	/man/2/sys-iounit
+specific	/man/2/tabs
+specific	/man/2/ubfa
+specific	/man/2/w3c-uris
+specific	/man/3/arch
+specific	/man/3/cons
+specific	/man/3/ether
+specific	/man/3/fs
+specific	/man/3/ip
+specific	/man/3/prog
+specific	/man/3/srv
+specific	/man/4/acme
+specific	/man/4/factotum
+specific	/man/4/namespace
+specific	/man/4/spree
+specific	/man/6/ndb
+specific	/man/6/sexprs
+specific	/man/6/translate
+specific	/man/8/bootpd
+specific	/man/8/collabsrv
+specific	/man/8/create
+specific	/man/8/httpd
+specific	/man/8/init
+specific	/man/8/prep
+specific	/man/8/svc
+specific	/man/9/button
+specific	/man/9/canvas
+specific	/man/9/checkbutton
+specific	/man/9/choicebutton
+specific	/man/9/entry
+specific	/man/9/frame
+specific	/man/9/label
+specific	/man/9/listbox
+specific	/man/9/menu
+specific	/man/9/menubutton
+specific	/man/9/panel
+specific	/man/9/radiobutton
+specific	/man/9/scale
+specific	/man/9/scrollbar
+specific	/man/9/text
+specifically	/man/1/sh-std
+specifically	/man/10/devattach
+specifically	/man/3/draw
+specifically	/man/3/fs
+specifically	/man/3/logfs
+specifically	/man/4/keyfs
+specifically	/man/9/1copyright
+specification	/man/1/telnet
+specification	/man/10/devattach
+specification	/man/10/print
+specification	/man/2/filter
+specification	/man/2/format
+specification	/man/2/palmfile
+specification	/man/2/print
+specification	/man/2/string
+specification	/man/2/sys-print
+specification	/man/2/w3c-css
+specification	/man/2/w3c-xpointers
+specification	/man/3/i2c
+specification	/man/3/usb
+specification	/man/5/flush
+specification	/man/6/dis
+specification	/man/6/font
+specification	/man/6/regexp
+specification	/man/9/menu
+specifications	/man/1/sh-arg
+specifications	/man/10/print
+specifications	/man/2/format
+specifications	/man/2/spree-cardlib
+specifications	/man/2/sys-print
+specifications	/man/6/font
+specifications	/man/9/canvas
+specifier	/man/10/devattach
+specifier	/man/10/master
+specifier	/man/2/asn1
+specifier	/man/2/string
+specifier	/man/2/sys-print
+specifier	/man/3/flash
+specifier	/man/3/indir
+specifier	/man/3/sd
+specifier	/man/3/srv9
+specifier	/man/3/tinyfs
+specifier	/man/6/dis
+specifier	/man/9/canvas
+specifier	/man/9/types
+specifiers	/man/1/xd
+specifiers	/man/10/9load
+specifiers	/man/10/c2l
+specifiers	/man/10/master
+specifiers	/man/3/lpt
+specifiers	/man/9/types
+specifies	/man/1/alphabet-abc
+specifies	/man/1/alphabet-fs
+specifies	/man/1/alphabet-grid
+specifies	/man/1/alphabet-main
+specifies	/man/1/calc
+specifies	/man/1/calendar
+specifies	/man/1/charon
+specifies	/man/1/crypt
+specifies	/man/1/deb
+specifies	/man/1/filename
+specifies	/man/1/fs
+specifies	/man/1/grid-register
+specifies	/man/1/man
+specifies	/man/1/mash-tk
+specifies	/man/1/miniterm
+specifies	/man/1/sh-alphabet
+specifies	/man/1/sh-expr
+specifies	/man/1/sh-std
+specifies	/man/1/tcs
+specifies	/man/1/units
+specifies	/man/10/2a
+specifies	/man/10/2l
+specifies	/man/10/9load
+specifies	/man/10/conf
+specifies	/man/10/dev
+specifies	/man/10/plan9.ini
+specifies	/man/2/asn1
+specifies	/man/2/convcs
+specifies	/man/2/draw-0intro
+specifies	/man/2/draw-font
+specifies	/man/2/draw-point
+specifies	/man/2/filter-deflate
+specifies	/man/2/format
+specifies	/man/2/imagefile
+specifies	/man/2/keyring-0intro
+specifies	/man/2/mpeg
+specifies	/man/2/prefab-element
+specifies	/man/2/prefab-environ
+specifies	/man/2/prefab-style
+specifies	/man/2/print
+specifies	/man/2/rfc822
+specifies	/man/2/sh
+specifies	/man/2/spree-cardlib
+specifies	/man/2/sys-byte2char
+specifies	/man/2/tkclient
+specifies	/man/2/wmclient
+specifies	/man/3/draw
+specifies	/man/4/dbfs
+specifies	/man/4/factotum
+specifies	/man/5/attach
+specifies	/man/6/dis
+specifies	/man/6/namespace
+specifies	/man/6/proto
+specifies	/man/6/regexp
+specifies	/man/6/sbl
+specifies	/man/8/create
+specifies	/man/8/createsignerkey
+specifies	/man/8/dns
+specifies	/man/8/mkfs
+specifies	/man/8/rdbgsrv
+specifies	/man/9/bind
+specifies	/man/9/button
+specifies	/man/9/canvas
+specifies	/man/9/checkbutton
+specifies	/man/9/choicebutton
+specifies	/man/9/entry
+specifies	/man/9/frame
+specifies	/man/9/grid
+specifies	/man/9/image
+specifies	/man/9/label
+specifies	/man/9/listbox
+specifies	/man/9/menu
+specifies	/man/9/menubutton
+specifies	/man/9/options
+specifies	/man/9/pack
+specifies	/man/9/panel
+specifies	/man/9/radiobutton
+specifies	/man/9/scale
+specifies	/man/9/scrollbar
+specifies	/man/9/see
+specifies	/man/9/text
+specifies	/man/9/types
+specify	/man/1/alphabet-fs
+specify	/man/1/bind
+specify	/man/1/calc
+specify	/man/1/charon
+specify	/man/1/collab
+specify	/man/1/cprof
+specify	/man/1/dd
+specify	/man/1/emu
+specify	/man/1/freq
+specify	/man/1/fs
+specify	/man/1/grid-ns
+specify	/man/1/grid-register
+specify	/man/1/mash-make
+specify	/man/1/mdb
+specify	/man/1/mk
+specify	/man/1/mprof
+specify	/man/1/prof
+specify	/man/1/sh-alphabet
+specify	/man/1/sh-expr
+specify	/man/1/tiny
+specify	/man/1/wm-sh
+specify	/man/1/yacc
+specify	/man/1/zeros
+specify	/man/10/2c
+specify	/man/10/a.out
+specify	/man/10/mk
+specify	/man/10/plan9.ini
+specify	/man/2/draw-image
+specify	/man/2/format
+specify	/man/2/keyring-0intro
+specify	/man/2/prefab-0intro
+specify	/man/2/prefab-environ
+specify	/man/2/rfc822
+specify	/man/2/spree-cardlib
+specify	/man/2/sys-print
+specify	/man/3/draw
+specify	/man/3/ssl
+specify	/man/4/factotum
+specify	/man/5/0intro
+specify	/man/6/ndb
+specify	/man/6/proto
+specify	/man/8/cs
+specify	/man/8/prep
+specify	/man/9/canvas
+specify	/man/9/grid
+specify	/man/9/menu
+specify	/man/9/options
+specify	/man/9/pack
+specify	/man/9/text
+specifying	/man/1/charon
+specifying	/man/1/mc
+specifying	/man/10/dev
+specifying	/man/10/plan9.ini
+specifying	/man/2/draw-image
+specifying	/man/2/keyring-sha1
+specifying	/man/2/prefab-element
+specifying	/man/2/pslib
+specifying	/man/2/spree-cardlib
+specifying	/man/2/sys-tokenize
+specifying	/man/3/draw
+specifying	/man/3/pnp
+specifying	/man/4/factotum
+specifying	/man/5/0intro
+specifying	/man/6/audio
+specifying	/man/8/collabsrv
+specifying	/man/9/canvas
+specifying	/man/9/entry
+specifying	/man/9/grid
+specifying	/man/9/image
+specifying	/man/9/listbox
+specifying	/man/9/scale
+spectrum	/man/1/0intro
+speculative	/man/1/ftest
+speed	/man/1/units
+speed	/man/1/wm-misc
+speed	/man/2/security-0intro
+speed	/man/4/palmsrv
+speed	/man/8/ai2key
+speed	/man/8/rdbgsrv
+speed	/man/9/text
+speeds	/man/1/acme
+spelling	/man/2/ubfa
+spellings	/man/2/ubfa
+spending	/man/1/prof
+spent	/man/1/prof
+spent	/man/10/kprof
+spent	/man/2/prof
+spent	/man/3/cmd
+spent	/man/3/touch
+spi	/man/3/touch
+spim	/man/10/2c
+spin	/man/10/lock
+spin	/man/10/qlock
+spite	/man/1/miniterm
+spk	/man/2/keyring-0intro
+spk	/man/8/ai2key
+spki	/man/2/sexprs
+spki	/man/2/spki
+spki	/man/2/spki-verifier
+spki	/man/6/sexprs
+spki's	/man/2/spki
+spki.b	/man/2/spki
+spki.m	/man/2/spki
+spki.m	/man/2/spki-verifier
+spkthumb	/man/2/keyset
+splhi	/man/10/intrenable
+splhi	/man/10/lock
+splhi	/man/10/qlock
+splhi	/man/10/splhi
+spline	/man/2/draw-image
+spline	/man/9/canvas
+splines	/man/9/canvas
+split	/man/1/alphabet-abc
+split	/man/1/alphabet-grid
+split	/man/1/grid-session
+split	/man/1/sh
+split	/man/1/sh-std
+split	/man/1/sh-string
+split	/man/1/tktester
+split	/man/10/dmainit
+split	/man/10/qio
+split	/man/2/imagefile
+split	/man/2/sys-tokenize
+split	/man/2/w3c-uris
+split	/man/3/pipe
+split	/man/3/sd
+split	/man/6/namespace
+split	/man/9/grid
+splitl	/man/1/sh-string
+splitl	/man/2/string
+splitr	/man/1/sh-string
+splitr	/man/2/string
+splits	/man/1/sh-std
+splits	/man/1/sh-string
+splits	/man/2/string
+splitstrl	/man/1/sh-string
+splitstrl	/man/2/string
+splitstrr	/man/1/sh-string
+splitstrr	/man/2/string
+splitting	/man/1/blur
+splitting	/man/1/mash
+spllo	/man/10/error
+spllo	/man/10/splhi
+splx	/man/10/splhi
+spoof	/man/2/keyring-0intro
+spoofing	/man/6/login
+sports	/man/1/wm-misc
+spot	/man/1/filename
+spot	/man/9/cursor
+spr	/man/10/acid
+spread	/man/2/spree-cardlib
+spree	/man/1/spree-join
+spree	/man/2/spree
+spree	/man/2/spree-allow
+spree	/man/2/spree-cardlib
+spree	/man/2/spree-gather
+spree	/man/2/spree-objstore
+spree	/man/4/spree
+spree.b	/man/2/spree
+spree.b	/man/4/spree
+spree.m	/man/2/spree
+spree.m	/man/2/spree-allow
+spree.m	/man/2/spree-cardlib
+spree.m	/man/2/spree-gather
+spree.m	/man/2/spree-objstore
+sprint	/man/10/2c
+sprint	/man/10/print
+sprint	/man/2/itslib
+sprint	/man/2/sys-print
+sprint	/man/3/arch
+sprint	/man/3/cap
+spsz	/man/10/a.out
+sput	/man/1/mash-tk
+sql	/man/7/db
+sqlclose	/man/7/db
+sqlcolumns	/man/10/odbc
+sqlconn	/man/7/db
+sqlexecdirect	/man/10/odbc
+sqlopen	/man/7/db
+sqlstream	/man/7/db
+sqltables	/man/10/odbc
+sqrt	/man/1/calc
+sqrt	/man/2/math-elem
+sqrt	/man/2/math-linalg
+square	/man/1/math-misc
+square	/man/1/sh
+square	/man/10/2c
+square	/man/2/math-elem
+square	/man/3/draw
+square	/man/3/vid
+square	/man/9/canvas
+square	/man/9/checkbutton
+squeeze	/man/1/tr
+squeezes	/man/10/5cv
+sqz	/man/10/5cv
+sr	/man/2/wmclient
+src	/man/1/acme
+src	/man/1/plumb
+src	/man/10/acid
+src	/man/2/debug
+src	/man/2/dis
+src	/man/2/draw-image
+src	/man/2/plumbmsg
+src	/man/2/sys-read
+src	/man/3/draw
+src	/man/6/plumbing
+src	/man/6/sbl
+src.start	/man/2/debug
+src.stop	/man/2/debug
+srcid	/man/3/draw
+srcid	/man/4/spree
+srclist	/man/10/srclist
+srcp	/man/3/draw
+srcpath	/man/2/dis
+srcpath	/man/2/prof
+srcstr	/man/2/debug
+srctopc	/man/2/debug
+srctype	/man/1/sh-alphabet
+sread	/man/2/scsiio
+srv	/man/1/grid-monitor
+srv	/man/1/grid-ns
+srv	/man/2/srv
+srv	/man/2/styxservers
+srv	/man/2/styxservers-nametree
+srv	/man/2/sys-file2chan
+srv	/man/2/sys-pctl
+srv	/man/3/root
+srv	/man/3/srv
+srv	/man/3/srv9
+srv	/man/4/9srvfs
+srv	/man/4/factotum
+srv	/man/4/grid-cpu
+srv	/man/4/ramfile
+srv	/man/8/ai2key
+srv	/man/8/cs
+srv	/man/8/dns
+srv.b	/man/1/gettar
+srv.c	/man/2/srv
+srv.default	/man/2/styxservers-nametree
+srv.delfid	/man/2/styxservers
+srv.dis	/man/8/init
+srv.m	/man/2/srv
+srv9	/man/3/srv9
+srv9	/man/4/9srvfs
+srvbrowse.b	/man/1/grid-query
+srvbrowse.b	/man/1/grid-session
+srvmod	/man/2/spree
+srvname	/man/2/secstore
+srvname	/man/4/9srvfs
+srvname	/man/4/factotum
+ss	/man/6/keyboard
+ss	/man/6/man
+ssdd0	/man/3/sd
+ssh	/man/4/factotum
+ssize	/man/2/dis
+ssl	/man/1/bind
+ssl	/man/1/charon
+ssl	/man/1/collab
+ssl	/man/1/cpu
+ssl	/man/1/crypt
+ssl	/man/1/listen
+ssl	/man/1/rcmd
+ssl	/man/10/dev
+ssl	/man/2/keyring-0intro
+ssl	/man/2/keyring-auth
+ssl	/man/2/keyring-getmsg
+ssl	/man/2/keyring-getstring
+ssl	/man/2/keyring-ipint
+ssl	/man/2/msgio
+ssl	/man/2/security-0intro
+ssl	/man/2/security-auth
+ssl	/man/2/security-ssl
+ssl	/man/2/sys-pctl
+ssl	/man/3/ssl
+ssl	/man/3/tls
+ssl	/man/4/import
+ssl	/man/7/dbsrv
+ssl	/man/8/logind
+ssl	/man/8/rstyxd
+ssl.b	/man/2/security-ssl
+ssl3	/man/3/tls
+sslv2	/man/3/ssl
+sslv3.0	/man/3/tls
+sslv3.01	/man/3/tls
+st	/man/6/attrdb
+sta	/man/10/plan9.ini
+stable	/man/1/sort
+stable	/man/2/readdir
+stable	/man/3/touch
+stable	/man/5/stat
+stack	/man/1/0intro
+stack	/man/1/deb
+stack	/man/1/fc
+stack	/man/1/ps
+stack	/man/1/sh
+stack	/man/1/sh-expr
+stack	/man/1/stack
+stack	/man/1/wm-misc
+stack	/man/1/yacc
+stack	/man/10/a.out
+stack	/man/10/acid
+stack	/man/10/error
+stack	/man/10/panic
+stack	/man/2/debug
+stack	/man/2/dis
+stack	/man/2/dividers
+stack	/man/2/draw-image
+stack	/man/2/spree-cardlib
+stack	/man/2/wmsrv
+stack	/man/3/cons
+stack	/man/3/draw
+stack	/man/3/prog
+stack	/man/6/dis
+stack	/man/6/ubfa
+stack	/man/8/cs
+stack's	/man/3/ip
+stack.b	/man/1/stack
+stacked	/man/10/error
+stacked	/man/2/dis
+stacked	/man/2/dividers
+stacking	/man/2/wmsrv
+stacking	/man/9/lower
+stacking	/man/9/raise
+stacks	/man/1/mprof
+stacks	/man/2/spree-cardlib
+stacks	/man/6/dis
+stackspec	/man/2/spree-cardlib
+stage	/man/1/tiny
+stage	/man/2/sys-fversion
+stage1	/man/1/alphabet-abc
+stage1	/man/1/alphabet-grid
+stage2	/man/1/alphabet-abc
+stage2	/man/1/alphabet-grid
+stall	/man/3/usb
+stalled	/man/3/usb
+stalling	/man/3/usb
+stamp	/man/1/mk
+stamp	/man/1/timestamp
+stamp	/man/10/mk
+stamp	/man/3/cons
+stamp	/man/3/pointer
+stamp	/man/8/applylog
+stamp	/man/8/create
+stamped	/man/8/create
+stamps	/man/1/mk
+stamps	/man/10/mk
+stamps	/man/8/applylog
+stand	/man/2/math-fp
+standalone	/man/1/mux
+standard	/man/1/9win
+standard	/man/1/acme
+standard	/man/1/alphabet-fs
+standard	/man/1/asm
+standard	/man/1/auplay
+standard	/man/1/basename
+standard	/man/1/bind
+standard	/man/1/calc
+standard	/man/1/cat
+standard	/man/1/charon
+standard	/man/1/cleanname
+standard	/man/1/collab
+standard	/man/1/comm
+standard	/man/1/cook
+standard	/man/1/crypt
+standard	/man/1/date
+standard	/man/1/dd
+standard	/man/1/disdep
+standard	/man/1/du
+standard	/man/1/ebook
+standard	/man/1/echo
+standard	/man/1/env
+standard	/man/1/filename
+standard	/man/1/fmt
+standard	/man/1/freq
+standard	/man/1/fs
+standard	/man/1/gettar
+standard	/man/1/grep
+standard	/man/1/grid-ns
+standard	/man/1/gzip
+standard	/man/1/idea
+standard	/man/1/itest
+standard	/man/1/kill
+standard	/man/1/limbo
+standard	/man/1/listen
+standard	/man/1/logwindow
+standard	/man/1/look
+standard	/man/1/m4
+standard	/man/1/man
+standard	/man/1/mash
+standard	/man/1/mash-tk
+standard	/man/1/math-misc
+standard	/man/1/mc
+standard	/man/1/mdb
+standard	/man/1/mk
+standard	/man/1/ns
+standard	/man/1/os
+standard	/man/1/p
+standard	/man/1/plumb
+standard	/man/1/ps
+standard	/man/1/read
+standard	/man/1/secstore
+standard	/man/1/sendmail
+standard	/man/1/sh
+standard	/man/1/sh-csv
+standard	/man/1/sh-file2chan
+standard	/man/1/sh-sexprs
+standard	/man/1/sh-std
+standard	/man/1/sleep
+standard	/man/1/sort
+standard	/man/1/stack
+standard	/man/1/stream
+standard	/man/1/strings
+standard	/man/1/sum
+standard	/man/1/tail
+standard	/man/1/tcs
+standard	/man/1/tee
+standard	/man/1/telnet
+standard	/man/1/time
+standard	/man/1/timestamp
+standard	/man/1/tiny
+standard	/man/1/tkcmd
+standard	/man/1/toolbar
+standard	/man/1/tr
+standard	/man/1/tsort
+standard	/man/1/unicode
+standard	/man/1/uniq
+standard	/man/1/units
+standard	/man/1/uuencode
+standard	/man/1/wc
+standard	/man/1/wish
+standard	/man/1/wm-sh
+standard	/man/1/xd
+standard	/man/1/zeros
+standard	/man/10/0intro
+standard	/man/10/2c
+standard	/man/10/5cv
+standard	/man/10/acid
+standard	/man/10/c2l
+standard	/man/10/devattach
+standard	/man/10/mk
+standard	/man/10/ms2
+standard	/man/10/plan9.ini
+standard	/man/10/print
+standard	/man/10/srclist
+standard	/man/2/arg
+standard	/man/2/asn1
+standard	/man/2/bufio
+standard	/man/2/convcs
+standard	/man/2/debug
+standard	/man/2/dial
+standard	/man/2/draw-0intro
+standard	/man/2/draw-image
+standard	/man/2/filter
+standard	/man/2/filter-deflate
+standard	/man/2/keyring-crypt
+standard	/man/2/math-0intro
+standard	/man/2/math-fp
+standard	/man/2/math-linalg
+standard	/man/2/palmfile
+standard	/man/2/prof
+standard	/man/2/security-0intro
+standard	/man/2/sexprs
+standard	/man/2/sh
+standard	/man/2/spree-cardlib
+standard	/man/2/styxservers
+standard	/man/2/sys-0intro
+standard	/man/2/sys-dial
+standard	/man/2/sys-dup
+standard	/man/2/sys-print
+standard	/man/2/w3c-css
+standard	/man/3/cmd
+standard	/man/3/flash
+standard	/man/3/ip
+standard	/man/3/srv9
+standard	/man/3/tv
+standard	/man/3/usb
+standard	/man/4/9srvfs
+standard	/man/4/acme
+standard	/man/4/dossrv
+standard	/man/4/export
+standard	/man/4/factotum
+standard	/man/4/import
+standard	/man/4/iostats
+standard	/man/4/keysrv
+standard	/man/4/kfs
+standard	/man/4/mntgen
+standard	/man/4/namespace
+standard	/man/4/registry
+standard	/man/4/vacfs
+standard	/man/6/sexprs
+standard	/man/6/utf
+standard	/man/7/cddb
+standard	/man/8/ai2key
+standard	/man/8/applylog
+standard	/man/8/bootpd
+standard	/man/8/create
+standard	/man/8/cs
+standard	/man/8/dhcp
+standard	/man/8/dns
+standard	/man/8/kfscmd
+standard	/man/8/mkfs
+standard	/man/8/rstyxd
+standard	/man/8/styxchat
+standard	/man/8/styxmon
+standard	/man/8/touchcal
+standard	/man/9/0intro
+standard	/man/9/button
+standard	/man/9/canvas
+standard	/man/9/checkbutton
+standard	/man/9/choicebutton
+standard	/man/9/entry
+standard	/man/9/frame
+standard	/man/9/label
+standard	/man/9/listbox
+standard	/man/9/menu
+standard	/man/9/menubutton
+standard	/man/9/options
+standard	/man/9/panel
+standard	/man/9/radiobutton
+standard	/man/9/scale
+standard	/man/9/scrollbar
+standard	/man/9/text
+standard	/man/9/types
+standardly	/man/10/2c
+standards	/man/1/charon
+standards	/man/1/sum
+standards	/man/3/rtc
+standby	/man/10/plan9.ini
+standby	/man/3/sd
+standing	/man/1/mash
+standing	/man/1/sh
+stands	/man/1/acme
+stands	/man/1/chmod
+stands	/man/1/mash
+stands	/man/1/sh
+stands	/man/1/tr
+stands	/man/2/dial
+stands	/man/2/keyring-0intro
+stands	/man/2/sys-dial
+stands	/man/3/ether
+stands	/man/6/regexp
+star	/man/10/plan9.ini
+star	/man/3/fpga
+star	/man/8/fpgaload
+start.html	/man/1/charon
+startclr	/man/3/kprof
+startcp	/man/3/prof
+startdir	/man/1/filename
+started	/man/1/9win
+started	/man/1/alphabet-fs
+started	/man/1/blur
+started	/man/1/charon
+started	/man/1/fs
+started	/man/1/grid-session
+started	/man/1/listen
+started	/man/1/logon
+started	/man/1/mash
+started	/man/1/mash-tk
+started	/man/1/mux
+started	/man/1/plumb
+started	/man/1/rcmd
+started	/man/1/sh
+started	/man/1/spree-join
+started	/man/1/timestamp
+started	/man/1/tiny
+started	/man/1/tkcmd
+started	/man/1/toolbar
+started	/man/1/wm
+started	/man/10/dev
+started	/man/10/dmainit
+started	/man/10/eve
+started	/man/10/ntsrv
+started	/man/10/odbc
+started	/man/2/command
+started	/man/2/disks
+started	/man/2/spree
+started	/man/2/spree-gather
+started	/man/2/styxpersist
+started	/man/2/styxservers
+started	/man/2/tkclient
+started	/man/2/wmclient
+started	/man/3/cmd
+started	/man/3/dbg
+started	/man/3/ip
+started	/man/4/9srvfs
+started	/man/4/dbfs
+started	/man/4/factotum
+started	/man/4/keyfs
+started	/man/4/keysrv
+started	/man/4/kfs
+started	/man/4/spree
+started	/man/6/keys
+started	/man/8/changelogin
+started	/man/8/collabsrv
+started	/man/8/cs
+started	/man/8/kfscmd
+started	/man/8/logind
+started	/man/8/plumber
+started	/man/8/rdbgsrv
+started	/man/8/rstyxd
+started	/man/8/shutdown
+started	/man/8/svc
+started	/man/8/virgild
+startinput	/man/2/tkclient
+startinput	/man/2/wmclient
+startinput	/man/2/wmlib
+startmp	/man/3/prof
+startprog	/man/2/debug
+startrefresh	/man/2/draw-display
+startrefresh:fn	/man/2/draw-display
+starts	/man/1/acme
+starts	/man/1/alphabet-abc
+starts	/man/1/alphabet-grid
+starts	/man/1/alphabet-main
+starts	/man/1/calendar
+starts	/man/1/charon
+starts	/man/1/cmp
+starts	/man/1/collab-clients
+starts	/man/1/emu
+starts	/man/1/listen
+starts	/man/1/logon
+starts	/man/1/mash
+starts	/man/1/mprof
+starts	/man/1/os
+starts	/man/1/prof
+starts	/man/1/sh
+starts	/man/1/sh-alphabet
+starts	/man/1/strings
+starts	/man/1/tktester
+starts	/man/1/wm
+starts	/man/1/wm-misc
+starts	/man/10/2l
+starts	/man/10/5coff
+starts	/man/10/9load
+starts	/man/10/a.out
+starts	/man/10/conf
+starts	/man/10/dmainit
+starts	/man/10/kbdputc
+starts	/man/10/ntsrv
+starts	/man/2/csv
+starts	/man/2/debug
+starts	/man/2/draw-0intro
+starts	/man/2/draw-image
+starts	/man/2/filter-deflate
+starts	/man/2/ir
+starts	/man/2/names
+starts	/man/2/plumbmsg
+starts	/man/2/prof
+starts	/man/2/styxpersist
+starts	/man/2/sys-pctl
+starts	/man/2/timers
+starts	/man/2/w3c-uris
+starts	/man/2/w3c-xpointers
+starts	/man/2/wait
+starts	/man/2/wmsrv
+starts	/man/3/flash
+starts	/man/3/fs
+starts	/man/3/i2c
+starts	/man/3/mpeg
+starts	/man/3/prog
+starts	/man/3/sd
+starts	/man/3/snarf
+starts	/man/3/srv9
+starts	/man/3/ssl
+starts	/man/3/touch
+starts	/man/4/acme
+starts	/man/4/export
+starts	/man/4/import
+starts	/man/4/kfs
+starts	/man/6/attrdb
+starts	/man/6/image
+starts	/man/6/sbl
+starts	/man/7/db
+starts	/man/8/getauthinfo
+starts	/man/8/plumber
+starts	/man/8/prep
+starts	/man/8/rip
+starts	/man/8/svc
+starts	/man/9/text
+startstate	/man/2/convcs
+startup	/man/1/charon
+startup	/man/1/deb
+startup	/man/1/sh
+startup	/man/10/2l
+startup	/man/10/acid
+startup	/man/10/plan9.ini
+startup	/man/3/dbg
+startup	/man/8/plumber
+starturl	/man/1/charon
+startx	/man/1/wm
+starty	/man/1/wm
+stat	/man/1/alphabet-fs
+stat	/man/1/chgrp
+stat	/man/1/chmod
+stat	/man/1/cp
+stat	/man/1/du
+stat	/man/1/fs
+stat	/man/1/ftest
+stat	/man/1/ls
+stat	/man/1/mv
+stat	/man/1/touch
+stat	/man/10/ar
+stat	/man/10/dev
+stat	/man/10/devattach
+stat	/man/10/newchan
+stat	/man/10/styx
+stat	/man/10/styxserver
+stat	/man/2/disks
+stat	/man/2/fsproto
+stat	/man/2/palmfile
+stat	/man/2/pop3
+stat	/man/2/readdir
+stat	/man/2/styx
+stat	/man/2/styxservers
+stat	/man/2/styxservers-nametree
+stat	/man/2/sys-0intro
+stat	/man/2/sys-dirread
+stat	/man/2/sys-open
+stat	/man/2/sys-stat
+stat	/man/2/workdir
+stat	/man/3/dup
+stat	/man/3/fs
+stat	/man/3/ftl
+stat	/man/3/i2c
+stat	/man/3/pipe
+stat	/man/3/srv
+stat	/man/4/dbfs
+stat	/man/4/keyfs
+stat	/man/4/kfs
+stat	/man/4/ramfile
+stat	/man/5/0intro
+stat	/man/5/open
+stat	/man/5/read
+stat	/man/5/stat
+stat	/man/5/walk
+stat	/man/6/users
+stat	/man/8/collabsrv
+statcheck	/man/10/styx
+stated	/man/1/cprof
+stated	/man/1/m4
+stated	/man/1/sh-std
+stated	/man/1/sh-tk
+stated	/man/2/debug
+statement	/man/1/0intro
+statement	/man/1/calc
+statement	/man/1/cprof
+statement	/man/1/deb
+statement	/man/1/limbo
+statement	/man/1/sh
+statement	/man/1/sh-std
+statement	/man/10/2c
+statement	/man/10/acid
+statement	/man/10/c2l
+statement	/man/10/odbc
+statement	/man/2/debug
+statement	/man/2/json
+statement	/man/2/popup
+statement	/man/2/spki-verifier
+statement	/man/2/ubfa
+statement	/man/2/w3c-css
+statement	/man/3/prog
+statement	/man/6/sbl
+statement	/man/7/db
+statement	/man/8/dns
+statement.ruleset	/man/2/w3c-css
+statements	/man/1/calc
+statements	/man/1/limbo
+statements	/man/10/acid
+statements	/man/2/0intro
+statements	/man/2/spki-verifier
+statements	/man/2/timers
+statements	/man/2/w3c-css
+statements	/man/3/cons
+statements	/man/7/db
+static	/man/1/mk
+static	/man/10/a.out
+static	/man/10/dev
+static	/man/10/devattach
+static	/man/10/inm
+static	/man/10/mk
+static	/man/2/pop3
+static	/man/4/namespace
+static	/man/4/registry
+static	/man/8/svc
+statically	/man/1/disdep
+statically	/man/2/spree
+statically	/man/8/rip
+stating	/man/2/styxservers
+station	/man/1/collab-clients
+station	/man/10/plan9.ini
+station	/man/2/keyring-0intro
+station	/man/2/keyring-auth
+station	/man/2/security-0intro
+station	/man/2/security-auth
+station	/man/6/auth
+stations	/man/2/security-auth
+statisics	/man/2/prof
+statistics	/man/1/cprof
+statistics	/man/1/math-misc
+statistics	/man/1/mprof
+statistics	/man/1/prof
+statistics	/man/1/wm-misc
+statistics	/man/10/5cv
+statistics	/man/10/xalloc
+statistics	/man/2/prof
+statistics	/man/3/ether
+statistics	/man/3/ip
+statistics	/man/3/prof
+statistics	/man/3/usb
+statistics	/man/4/iostats
+stats	/man/10/plan9.ini
+stats	/man/2/prof
+stats	/man/3/ether
+stats	/man/3/ip
+stats	/man/3/tls
+stats	/man/4/dbfs
+stats	/man/8/httpd
+status	/man/1/0intro
+status	/man/1/alphabet-fs
+status	/man/1/alphabet-main
+status	/man/1/charon
+status	/man/1/cmp
+status	/man/1/diff
+status	/man/1/fc
+status	/man/1/fs
+status	/man/1/ftest
+status	/man/1/grep
+status	/man/1/grid-monitor
+status	/man/1/grid-register
+status	/man/1/kill
+status	/man/1/look
+status	/man/1/mk
+status	/man/1/mkdir
+status	/man/1/netstat
+status	/man/1/os
+status	/man/1/ps
+status	/man/1/read
+status	/man/1/sh
+status	/man/1/sh-alphabet
+status	/man/1/sh-file2chan
+status	/man/1/sh-regex
+status	/man/1/sh-sexprs
+status	/man/1/sh-std
+status	/man/1/sh-tk
+status	/man/1/stack
+status	/man/1/wm
+status	/man/10/mk
+status	/man/10/odbc
+status	/man/10/qio
+status	/man/10/xalloc
+status	/man/2/0intro
+status	/man/2/alphabet-intro
+status	/man/2/debug
+status	/man/2/ip
+status	/man/2/math-0intro
+status	/man/2/math-fp
+status	/man/2/mpeg
+status	/man/2/pop3
+status	/man/2/scsiio
+status	/man/2/sets
+status	/man/2/sh
+status	/man/2/styxservers
+status	/man/2/sys-0intro
+status	/man/2/sys-byte2char
+status	/man/2/sys-stat
+status	/man/2/wait
+status	/man/3/cmd
+status	/man/3/cons
+status	/man/3/dbg
+status	/man/3/eia
+status	/man/3/ether
+status	/man/3/flash
+status	/man/3/fpga
+status	/man/3/i82365
+status	/man/3/ip
+status	/man/3/logfs
+status	/man/3/lpt
+status	/man/3/mnt
+status	/man/3/plap
+status	/man/3/prog
+status	/man/3/sd
+status	/man/3/sign
+status	/man/3/tls
+status	/man/3/usb
+status	/man/4/keyfs
+status	/man/5/stat
+status	/man/6/keys
+status	/man/8/applylog
+status	/man/8/dhcp
+status	/man/8/mangaload
+status	/man/8/ping
+status	/man/9/grab
+std	/man/1/read
+std	/man/1/sh
+std	/man/1/sh-arg
+std	/man/1/sh-csv
+std	/man/1/sh-expr
+std	/man/1/sh-file2chan
+std	/man/1/sh-regex
+std	/man/1/sh-sexprs
+std	/man/1/sh-std
+std	/man/1/sh-string
+std	/man/1/sh-test
+std	/man/1/sh-tk
+std	/man/2/sh
+std	/man/2/sys-pctl
+std.b	/man/1/sh-std
+stderr	/man/1/itest
+stderr	/man/1/sh
+stderr	/man/1/sh-std
+stderr	/man/1/yacc
+stderr	/man/2/security-auth
+stderr	/man/3/cmd
+stdin	/man/1/grid-monitor
+stdin	/man/1/grid-ns
+stdin	/man/1/grid-register
+stdin	/man/2/security-auth
+stdin	/man/4/grid-cpu
+stdout	/man/1/grid-monitor
+stdout	/man/1/grid-register
+stdout	/man/1/itest
+stdout	/man/1/tkcmd
+stdsym	/man/2/debug
+stein	/man/9/grid
+steinberg	/man/2/imagefile
+stem	/man/1/mash
+stem	/man/1/mk
+stem	/man/1/sh
+stem	/man/1/webgrab
+stem	/man/1/yacc
+stem	/man/10/mk
+stem.2	/man/10/mk
+stem.b	/man/1/mash
+stem.b	/man/1/mk
+stem.b	/man/1/sh
+stem.c	/man/10/mk
+stem.dis	/man/1/mk
+stem0	/man/1/mk
+stem0	/man/10/mk
+stem1	/man/1/mk
+stem1	/man/10/mk
+stem2.b	/man/1/mk
+stem2.c	/man/10/mk
+stem9	/man/1/mk
+stem9	/man/10/mk
+step	/man/1/cprof
+step	/man/1/deb
+step	/man/10/acid
+step	/man/2/debug
+step	/man/2/draw-example
+step	/man/2/security-0intro
+step	/man/2/sys-0intro
+step	/man/2/w3c-xpointers
+step	/man/3/prog
+step	/man/8/register
+step	/man/9/canvas
+step	/man/9/grid
+stepexp	/man/2/debug
+stepout	/man/2/debug
+stepover	/man/2/debug
+stepped	/man/1/deb
+stepping	/man/1/deb
+stepping	/man/10/acid
+stepping	/man/2/debug
+steps	/man/2/debug
+steps	/man/2/w3c-xpointers
+steps	/man/3/sd
+steps	/man/9/grid
+steps	/man/9/pack
+stepstmt	/man/2/debug
+stereo	/man/1/auplay
+stereo	/man/3/audio
+stereo	/man/6/audio
+stick	/man/9/grid
+sticky	/man/9/grid
+stime	/man/2/itslib
+stipple	/man/2/draw-example
+stipple	/man/9/canvas
+stipple.draw	/man/2/draw-example
+stippled	/man/9/canvas
+stk	/man/10/acid
+stk	/man/2/spree-cardlib
+stmt	/man/6/sbl
+stob	/man/2/convcs
+stob.b	/man/2/convcs
+stob.dis	/man/2/convcs
+stop	/man/1/charon
+stop	/man/1/collab-clients
+stop	/man/1/cprof
+stop	/man/1/dd
+stop	/man/1/deb
+stop	/man/1/mprof
+stop	/man/1/prof
+stop	/man/1/sh-std
+stop	/man/1/sleep
+stop	/man/1/stream
+stop	/man/10/acid
+stop	/man/10/plan9.ini
+stop	/man/2/debug
+stop	/man/2/dhcpclient
+stop	/man/2/prof
+stop	/man/2/sys-export
+stop	/man/2/sys-file2chan
+stop	/man/2/timers
+stop	/man/2/tkclient
+stop	/man/2/wmsrv
+stop	/man/3/dbg
+stop	/man/3/eia
+stop	/man/3/ftl
+stop	/man/3/ip
+stop	/man/3/kprof
+stop	/man/3/mpeg
+stop	/man/3/prof
+stop	/man/3/prog
+stop	/man/3/vid
+stop	/man/4/acme
+stop	/man/8/kfscmd
+stop	/man/8/mangaload
+stop	/man/9/text
+stop	/man/9/update
+stopindex	/man/9/text
+stopped	/man/1/listen
+stopped	/man/10/acid
+stopped	/man/10/devattach
+stopped	/man/10/ntsrv
+stopped	/man/2/debug
+stopped	/man/2/exception
+stopped	/man/2/palmfile
+stopped	/man/2/prof
+stopped	/man/2/sys-export
+stopped	/man/2/timers
+stopped	/man/3/dbg
+stopped	/man/3/mpeg
+stopping	/man/1/p
+stopping	/man/1/sh
+stopping	/man/10/memory
+stopping	/man/10/strcat
+stopping	/man/9/text
+stops	/man/1/avr
+stops	/man/10/9load
+stops	/man/10/acid
+stops	/man/10/plan9.ini
+stops	/man/2/debug
+stops	/man/2/prof
+stops	/man/2/timers
+stops	/man/8/dns
+stops	/man/8/sntp
+stops	/man/9/text
+stopwatch	/man/1/wm-misc
+stopwatch.b	/man/1/wm-misc
+storage	/man/1/du
+storage	/man/1/ebook
+storage	/man/1/emu
+storage	/man/1/secstore
+storage	/man/10/print
+storage	/man/10/styx
+storage	/man/2/dbm
+storage	/man/2/diskblocks
+storage	/man/2/draw-display
+storage	/man/2/ida
+storage	/man/2/math-linalg
+storage	/man/2/secstore
+storage	/man/2/sexprs
+storage	/man/2/spree
+storage	/man/2/spree-objstore
+storage	/man/2/styxservers-nametree
+storage	/man/2/sys-stat
+storage	/man/2/ubfa
+storage	/man/2/venti
+storage	/man/2/xml
+storage	/man/3/draw
+storage	/man/3/ds
+storage	/man/3/logfs
+storage	/man/3/sd
+storage	/man/4/dbfs
+storage	/man/4/keyfs
+storage	/man/4/kfs
+storage	/man/4/logfile
+storage	/man/4/memfs
+storage	/man/4/namespace
+storage	/man/5/stat
+storage	/man/6/keytext
+storage	/man/6/sexprs
+storage	/man/8/mangaload
+storage	/man/8/rdbgsrv
+store	/man/1/acme
+store	/man/1/calendar
+store	/man/1/itest
+store	/man/1/secstore
+store	/man/10/acid
+store	/man/10/dev
+store	/man/10/styx
+store	/man/2/dbm
+store	/man/2/disks
+store	/man/2/draw-context
+store	/man/2/draw-screen
+store	/man/2/palmfile
+store	/man/2/scsiio
+store	/man/2/secstore
+store	/man/2/spree-gather
+store	/man/2/spree-objstore
+store	/man/2/styxservers
+store	/man/2/wmsrv
+store	/man/3/draw
+store	/man/3/logfs
+store	/man/4/dbfs
+store	/man/6/sexprs
+store	/man/6/ubfa
+store	/man/6/utf
+store	/man/8/getauthinfo
+store	/man/8/prep
+store	/man/9/checkbutton
+store	/man/9/menu
+store	/man/9/radiobutton
+stored	/man/1/acme
+stored	/man/1/cprof
+stored	/man/1/ebook
+stored	/man/1/env
+stored	/man/1/secstore
+stored	/man/1/sh
+stored	/man/1/sh-expr
+stored	/man/1/sh-file2chan
+stored	/man/1/sh-std
+stored	/man/1/webgrab
+stored	/man/1/wm-misc
+stored	/man/10/qio
+stored	/man/10/readnum
+stored	/man/10/styx
+stored	/man/2/alphabet-intro
+stored	/man/2/asn1
+stored	/man/2/convcs
+stored	/man/2/dbm
+stored	/man/2/dhcpclient
+stored	/man/2/disks
+stored	/man/2/draw-0intro
+stored	/man/2/hash
+stored	/man/2/ida
+stored	/man/2/imagefile
+stored	/man/2/ip
+stored	/man/2/keyring-0intro
+stored	/man/2/palmfile
+stored	/man/2/prof
+stored	/man/2/scsiio
+stored	/man/2/secstore
+stored	/man/2/security-login
+stored	/man/2/spree
+stored	/man/2/spree-objstore
+stored	/man/2/styxservers-nametree
+stored	/man/2/sys-0intro
+stored	/man/2/sys-fd2path
+stored	/man/2/ubfa
+stored	/man/2/xml
+stored	/man/3/logfs
+stored	/man/3/pipe
+stored	/man/3/tinyfs
+stored	/man/3/touch
+stored	/man/3/tv
+stored	/man/4/dbfs
+stored	/man/4/factotum
+stored	/man/4/keyfs
+stored	/man/4/keysrv
+stored	/man/4/kfs
+stored	/man/4/logfile
+stored	/man/4/ramfile
+stored	/man/5/0intro
+stored	/man/6/audio
+stored	/man/6/colour
+stored	/man/6/dis
+stored	/man/6/image
+stored	/man/6/keys
+stored	/man/6/man
+stored	/man/6/ndb
+stored	/man/6/sbl
+stored	/man/8/changelogin
+stored	/man/8/createsignerkey
+stored	/man/8/getauthinfo
+stored	/man/8/httpd
+stored	/man/8/prep
+stored	/man/9/canvas
+stored	/man/9/menu
+stored	/man/9/radiobutton
+storer	/man/6/image
+stores	/man/1/webgrab
+stores	/man/2/0intro
+stores	/man/2/disks
+stores	/man/2/draw-0intro
+stores	/man/2/palmfile
+stores	/man/2/sets
+stores	/man/2/spree-allow
+stores	/man/2/sys-fd2path
+stores	/man/4/dbfs
+stores	/man/5/0intro
+stores	/man/6/attrdb
+stores	/man/8/createsignerkey
+stores	/man/9/menu
+storing	/man/10/styx
+storing	/man/2/keyring-auth
+storing	/man/2/math-fp
+storing	/man/2/spree-objstore
+storing	/man/2/styx
+storing	/man/2/sys-self
+storing	/man/4/dbfs
+storing	/man/4/keyfs
+str	/man/10/acid
+str	/man/10/getfields
+str	/man/10/readnum
+str	/man/10/rune
+str	/man/10/strcat
+str	/man/10/styxserver
+str	/man/2/complete
+str	/man/2/convcs
+str	/man/2/dis
+str	/man/2/draw-font
+str	/man/2/draw-image
+str	/man/2/keyring-ipint
+str	/man/2/prefab-element
+str	/man/2/sets
+str	/man/2/string
+str	/man/2/w3c-xpointers
+str	/man/2/xml
+str2en	/man/2/geodesy
+str2lalo	/man/2/geodesy
+str2mark	/man/2/xml
+str2set	/man/2/sets
+straddle	/man/1/acme
+straight	/man/2/asn1
+straight	/man/9/canvas
+straightforward	/man/1/0intro
+straightforward	/man/10/qlock
+straightforward	/man/2/dis
+strange	/man/1/wm-misc
+strategies	/man/2/security-0intro
+strcat	/man/10/getfields
+strcat	/man/10/memory
+strcat	/man/10/strcat
+strchr	/man/10/strcat
+strcmp	/man/10/strcat
+strcpy	/man/10/strcat
+strcspn	/man/10/strcat
+strdup	/man/10/strcat
+strdup.c	/man/10/strcat
+stream	/man/1/alphabet-main
+stream	/man/1/auplay
+stream	/man/1/cat
+stream	/man/1/crypt
+stream	/man/1/diff
+stream	/man/1/m4
+stream	/man/1/read
+stream	/man/1/stream
+stream	/man/1/wm-sh
+stream	/man/10/a.out
+stream	/man/10/atoi
+stream	/man/10/print
+stream	/man/10/qio
+stream	/man/10/rune
+stream	/man/2/bufio
+stream	/man/2/convcs
+stream	/man/2/dis
+stream	/man/2/draw-context
+stream	/man/2/drawmux
+stream	/man/2/filter
+stream	/man/2/filter-deflate
+stream	/man/2/filter-slip
+stream	/man/2/imagefile
+stream	/man/2/json
+stream	/man/2/keyring-rc4
+stream	/man/2/rfc822
+stream	/man/2/spree
+stream	/man/2/sys-print
+stream	/man/2/sys-read
+stream	/man/2/ubfa
+stream	/man/3/ip
+stream	/man/3/mpeg
+stream	/man/3/pipe
+stream	/man/3/ssl
+stream	/man/6/audio
+stream	/man/6/dis
+stream	/man/6/image
+stream	/man/6/ubfa
+stream	/man/6/utf
+stream	/man/7/db
+stream	/man/8/httpd
+stream.b	/man/1/stream
+streamed	/man/2/convcs
+streamers	/man/1/stream
+streaming	/man/1/stream
+streamlined	/man/2/rfc822
+streams	/man/1/m4
+streams	/man/1/os
+streams	/man/1/stream
+streams	/man/2/convcs
+streams	/man/2/keyring-getmsg
+streams	/man/2/keyring-getstring
+streams	/man/2/msgio
+streams	/man/2/sys-stat
+streams	/man/2/wmlib
+streams	/man/3/audio
+streams	/man/6/json
+streams	/man/6/ubfa
+streams	/man/6/utf
+streams	/man/7/db
+strengths	/man/1/0intro
+stretch	/man/9/grid
+stretch	/man/9/pack
+stretch	/man/9/text
+stretched	/man/1/tktester
+stretched	/man/9/grid
+stretched	/man/9/text
+strict	/man/1/calc
+strict	/man/10/error
+strict	/man/2/wmsrv
+strictly	/man/10/eve
+strictly	/man/2/draw-0intro
+strictly	/man/4/factotum
+stride	/man/2/math-linalg
+string	/man/1/0intro
+string	/man/1/acme
+string	/man/1/alphabet-abc
+string	/man/1/alphabet-fs
+string	/man/1/alphabet-grid
+string	/man/1/alphabet-main
+string	/man/1/basename
+string	/man/1/brutus
+string	/man/1/calc
+string	/man/1/charon
+string	/man/1/collab-clients
+string	/man/1/diff
+string	/man/1/disdep
+string	/man/1/fs
+string	/man/1/grid-monitor
+string	/man/1/idea
+string	/man/1/look
+string	/man/1/m4
+string	/man/1/man
+string	/man/1/mash
+string	/man/1/mash-tk
+string	/man/1/miniterm
+string	/man/1/mk
+string	/man/1/sh
+string	/man/1/sh-alphabet
+string	/man/1/sh-file2chan
+string	/man/1/sh-regex
+string	/man/1/sh-sexprs
+string	/man/1/sh-std
+string	/man/1/sh-string
+string	/man/1/sh-tk
+string	/man/1/sort
+string	/man/1/strings
+string	/man/1/tiny
+string	/man/1/tr
+string	/man/1/unicode
+string	/man/1/wm-sh
+string	/man/1/yacc
+string	/man/10/2c
+string	/man/10/acid
+string	/man/10/ar
+string	/man/10/atoi
+string	/man/10/c2l
+string	/man/10/dev
+string	/man/10/devattach
+string	/man/10/dynld
+string	/man/10/eve
+string	/man/10/getfields
+string	/man/10/intrenable
+string	/man/10/kproc
+string	/man/10/mk
+string	/man/10/odbc
+string	/man/10/parsecmd
+string	/man/10/plan9.ini
+string	/man/10/print
+string	/man/10/readnum
+string	/man/10/rune
+string	/man/10/strcat
+string	/man/10/styx
+string	/man/10/styxserver
+string	/man/2/0intro
+string	/man/2/alphabet-intro
+string	/man/2/arg
+string	/man/2/asn1
+string	/man/2/attrdb
+string	/man/2/bloomfilter
+string	/man/2/bufio
+string	/man/2/cfg
+string	/man/2/command
+string	/man/2/complete
+string	/man/2/convcs
+string	/man/2/csv
+string	/man/2/daytime
+string	/man/2/dbm
+string	/man/2/debug
+string	/man/2/devpointer
+string	/man/2/dhcpclient
+string	/man/2/dial
+string	/man/2/dialog
+string	/man/2/dict
+string	/man/2/dis
+string	/man/2/diskblocks
+string	/man/2/disks
+string	/man/2/dividers
+string	/man/2/draw-0intro
+string	/man/2/draw-context
+string	/man/2/draw-display
+string	/man/2/draw-example
+string	/man/2/draw-font
+string	/man/2/draw-image
+string	/man/2/drawmux
+string	/man/2/encoding
+string	/man/2/env
+string	/man/2/ether
+string	/man/2/exception
+string	/man/2/factotum
+string	/man/2/filepat
+string	/man/2/filter
+string	/man/2/filter-deflate
+string	/man/2/filter-slip
+string	/man/2/format
+string	/man/2/fsproto
+string	/man/2/geodesy
+string	/man/2/hash
+string	/man/2/ida
+string	/man/2/imagefile
+string	/man/2/ip
+string	/man/2/ir
+string	/man/2/itslib
+string	/man/2/json
+string	/man/2/keyring-0intro
+string	/man/2/keyring-auth
+string	/man/2/keyring-certtostr
+string	/man/2/keyring-gensk
+string	/man/2/keyring-getmsg
+string	/man/2/keyring-getstring
+string	/man/2/keyring-ipint
+string	/man/2/keyring-sha1
+string	/man/2/keyset
+string	/man/2/lists
+string	/man/2/mpeg
+string	/man/2/msgio
+string	/man/2/names
+string	/man/2/newns
+string	/man/2/palmfile
+string	/man/2/plumbmsg
+string	/man/2/pop3
+string	/man/2/popup
+string	/man/2/prefab-compound
+string	/man/2/prefab-element
+string	/man/2/print
+string	/man/2/prof
+string	/man/2/pslib
+string	/man/2/readdir
+string	/man/2/regex
+string	/man/2/registries
+string	/man/2/rfc822
+string	/man/2/scsiio
+string	/man/2/secstore
+string	/man/2/security-0intro
+string	/man/2/security-auth
+string	/man/2/security-login
+string	/man/2/security-ssl
+string	/man/2/selectfile
+string	/man/2/sets
+string	/man/2/sexprs
+string	/man/2/sh
+string	/man/2/smtp
+string	/man/2/spki
+string	/man/2/spki-verifier
+string	/man/2/spree
+string	/man/2/spree-allow
+string	/man/2/spree-cardlib
+string	/man/2/spree-gather
+string	/man/2/spree-objstore
+string	/man/2/srv
+string	/man/2/string
+string	/man/2/stringinttab
+string	/man/2/styx
+string	/man/2/styxpersist
+string	/man/2/styxservers
+string	/man/2/styxservers-nametree
+string	/man/2/sys-0intro
+string	/man/2/sys-bind
+string	/man/2/sys-chdir
+string	/man/2/sys-dial
+string	/man/2/sys-export
+string	/man/2/sys-fauth
+string	/man/2/sys-fd2path
+string	/man/2/sys-file2chan
+string	/man/2/sys-fversion
+string	/man/2/sys-open
+string	/man/2/sys-print
+string	/man/2/sys-remove
+string	/man/2/sys-self
+string	/man/2/sys-stat
+string	/man/2/sys-tokenize
+string	/man/2/sys-werrstr
+string	/man/2/tabs
+string	/man/2/tftp
+string	/man/2/tk
+string	/man/2/tkclient
+string	/man/2/translate
+string	/man/2/ubfa
+string	/man/2/venti
+string	/man/2/virgil
+string	/man/2/volume
+string	/man/2/w3c-css
+string	/man/2/w3c-uris
+string	/man/2/w3c-xpointers
+string	/man/2/wait
+string	/man/2/wmclient
+string	/man/2/wmlib
+string	/man/2/wmsrv
+string	/man/2/workdir
+string	/man/2/xml
+string	/man/3/0intro
+string	/man/3/cap
+string	/man/3/cmd
+string	/man/3/cons
+string	/man/3/dbg
+string	/man/3/draw
+string	/man/3/ether
+string	/man/3/flash
+string	/man/3/floppy
+string	/man/3/fs
+string	/man/3/indir
+string	/man/3/ip
+string	/man/3/kprof
+string	/man/3/logfs
+string	/man/3/mnt
+string	/man/3/plap
+string	/man/3/prog
+string	/man/3/sign
+string	/man/3/ssl
+string	/man/3/tls
+string	/man/4/acme
+string	/man/4/dbfs
+string	/man/4/factotum
+string	/man/4/import
+string	/man/4/keyfs
+string	/man/4/registry
+string	/man/4/spree
+string	/man/5/0intro
+string	/man/5/error
+string	/man/5/version
+string	/man/6/attrdb
+string	/man/6/audio
+string	/man/6/dis
+string	/man/6/font
+string	/man/6/image
+string	/man/6/json
+string	/man/6/man
+string	/man/6/namespace
+string	/man/6/plumbing
+string	/man/6/regexp
+string	/man/6/sbl
+string	/man/6/sexprs
+string	/man/6/ubfa
+string	/man/7/db
+string	/man/8/ai2key
+string	/man/8/changelogin
+string	/man/8/collabsrv
+string	/man/8/logind
+string	/man/8/manufacture
+string	/man/8/rstyxd
+string	/man/8/styxchat
+string	/man/9/0intro
+string	/man/9/bind
+string	/man/9/button
+string	/man/9/canvas
+string	/man/9/checkbutton
+string	/man/9/choicebutton
+string	/man/9/entry
+string	/man/9/frame
+string	/man/9/grab
+string	/man/9/grid
+string	/man/9/image
+string	/man/9/label
+string	/man/9/listbox
+string	/man/9/menu
+string	/man/9/menubutton
+string	/man/9/options
+string	/man/9/pack
+string	/man/9/panel
+string	/man/9/radiobutton
+string	/man/9/scale
+string	/man/9/scrollbar
+string	/man/9/send
+string	/man/9/text
+string.b	/man/1/sh-string
+string.b	/man/2/string
+string.m	/man/2/string
+string1	/man/1/tr
+string2	/man/1/tr
+string2attrs	/man/2/plumbmsg
+string2tm	/man/2/daytime
+stringint	/man/2/stringinttab
+stringinttab	/man/2/stringinttab
+stringlist2list	/man/2/sh
+strings	/man/1/acme
+strings	/man/1/diff
+strings	/man/1/disdep
+strings	/man/1/look
+strings	/man/1/m4
+strings	/man/1/mash
+strings	/man/1/mash-tk
+strings	/man/1/mdb
+strings	/man/1/mk
+strings	/man/1/plumb
+strings	/man/1/sh
+strings	/man/1/strings
+strings	/man/1/tiny
+strings	/man/1/wm-misc
+strings	/man/10/acid
+strings	/man/10/c2l
+strings	/man/10/devattach
+strings	/man/10/mk
+strings	/man/10/odbc
+strings	/man/10/print
+strings	/man/10/readnum
+strings	/man/10/strcat
+strings	/man/10/styx
+strings	/man/2/asn1
+strings	/man/2/command
+strings	/man/2/convcs
+strings	/man/2/dhcpclient
+strings	/man/2/dialog
+strings	/man/2/dis
+strings	/man/2/filter-slip
+strings	/man/2/json
+strings	/man/2/keyring-0intro
+strings	/man/2/keyring-getstring
+strings	/man/2/lists
+strings	/man/2/msgio
+strings	/man/2/palmfile
+strings	/man/2/prefab-element
+strings	/man/2/security-0intro
+strings	/man/2/sexprs
+strings	/man/2/sh
+strings	/man/2/smtp
+strings	/man/2/string
+strings	/man/2/styx
+strings	/man/2/styxservers
+strings	/man/2/sys-0intro
+strings	/man/2/sys-fversion
+strings	/man/2/sys-print
+strings	/man/2/tk
+strings	/man/2/ubfa
+strings	/man/2/w3c-uris
+strings	/man/3/audio
+strings	/man/3/cons
+strings	/man/3/draw
+strings	/man/3/ds
+strings	/man/3/ip
+strings	/man/3/plap
+strings	/man/3/pnp
+strings	/man/3/prog
+strings	/man/3/vga
+strings	/man/4/factotum
+strings	/man/4/registry
+strings	/man/5/0intro
+strings	/man/5/stat
+strings	/man/5/version
+strings	/man/6/font
+strings	/man/6/image
+strings	/man/6/json
+strings	/man/6/man
+strings	/man/6/regexp
+strings	/man/6/sbl
+strings	/man/6/sexprs
+strings	/man/6/translate
+strings	/man/6/ubfa
+strings	/man/6/users
+strings	/man/9/0intro
+strings	/man/9/entry
+strings	/man/9/listbox
+strings.b	/man/1/strings
+strinttab	/man/2/stringinttab
+strinttab.b	/man/2/stringinttab
+strinttab.m	/man/2/stringinttab
+strip	/man/1/basename
+strip	/man/1/deb
+strip	/man/1/limbo
+strip	/man/10/2a
+strip	/man/10/2c
+strip	/man/10/2l
+strip	/man/10/5cv
+stripped	/man/1/m4
+stripped	/man/1/tiny
+stripped	/man/10/5cv
+stripped	/man/10/9load
+stripped	/man/10/kstrip
+stripped	/man/10/ms2
+stripped	/man/2/asn1
+stripped	/man/3/tls
+stripped	/man/9/0intro
+stripping	/man/1/asm
+stripping	/man/1/limbo
+stripping	/man/10/kstrip
+stripping	/man/2/string
+stripping	/man/5/version
+strlen	/man/10/strcat
+strncat	/man/10/strcat
+strncmp	/man/10/strcat
+strncpy	/man/10/strcat
+stroke	/man/1/keyboard
+stroke	/man/2/draw-example
+stroke	/man/8/collabsrv
+strokes	/man/1/collab-clients
+strokes	/man/1/keyboard
+strokes	/man/3/cons
+strokes	/man/8/collabsrv
+strokes	/man/9/entry
+strokes	/man/9/text
+strongarm	/man/10/inb
+strongly	/man/2/keyring-crypt
+strpbrk	/man/10/strcat
+strrchr	/man/10/strcat
+strs	/man/10/styx
+strspn	/man/10/strcat
+strstr	/man/10/strcat
+strtocert	/man/2/keyring-certtostr
+strtod	/man/10/atoi
+strtod.c	/man/10/atoi
+strtoip	/man/2/keyring-ipint
+strtok	/man/10/strcat
+strtol	/man/10/atoi
+strtol.c	/man/10/atoi
+strtol.c	/man/10/strcat
+strtoll	/man/10/atoi
+strtoll.c	/man/10/strcat
+strtopk	/man/2/keyring-certtostr
+strtosk	/man/2/keyring-certtostr
+strtoul	/man/10/atoi
+strtoul	/man/3/flash
+strtoul.c	/man/10/atoi
+strtoul.c	/man/10/strcat
+struck	/man/4/registry
+struct	/man/10/2c
+struct	/man/10/a.out
+struct	/man/10/allocb
+struct	/man/10/ar
+struct	/man/10/dev
+struct	/man/10/devattach
+struct	/man/10/dynld
+struct	/man/10/newchan
+struct	/man/10/parsecmd
+struct	/man/10/ref
+struct	/man/10/styx
+struct	/man/10/styxserver
+struct	/man/6/man
+structurally	/man/2/format
+structure	/man/1/0intro
+structure	/man/1/alphabet-fs
+structure	/man/1/dd
+structure	/man/1/disdep
+structure	/man/1/ebook
+structure	/man/1/fs
+structure	/man/1/ftree
+structure	/man/1/grid-ns
+structure	/man/1/sh
+structure	/man/10/2c
+structure	/man/10/a.out
+structure	/man/10/ar
+structure	/man/10/c2l
+structure	/man/10/conf
+structure	/man/10/dev
+structure	/man/10/devattach
+structure	/man/10/dynld
+structure	/man/10/error
+structure	/man/10/kproc
+structure	/man/10/lock
+structure	/man/10/newchan
+structure	/man/10/parsecmd
+structure	/man/10/qio
+structure	/man/10/qlock
+structure	/man/10/ref
+structure	/man/10/sleep
+structure	/man/10/styx
+structure	/man/10/styxserver
+structure	/man/2/asn1
+structure	/man/2/daytime
+structure	/man/2/debug
+structure	/man/2/dis
+structure	/man/2/draw-0intro
+structure	/man/2/draw-display
+structure	/man/2/draw-image
+structure	/man/2/draw-screen
+structure	/man/2/format
+structure	/man/2/ip
+structure	/man/2/keyring-getmsg
+structure	/man/2/keyring-getstring
+structure	/man/2/msgio
+structure	/man/2/palmfile
+structure	/man/2/plumbmsg
+structure	/man/2/prefab-compound
+structure	/man/2/prefab-element
+structure	/man/2/sexprs
+structure	/man/2/styx
+structure	/man/2/styxflush
+structure	/man/2/styxservers
+structure	/man/2/sys-dial
+structure	/man/2/sys-dirread
+structure	/man/2/sys-dup
+structure	/man/2/sys-open
+structure	/man/2/sys-stat
+structure	/man/2/tk
+structure	/man/2/w3c-css
+structure	/man/2/wmsrv
+structure	/man/2/xml
+structure	/man/3/dbg
+structure	/man/3/logfs
+structure	/man/3/prof
+structure	/man/3/prog
+structure	/man/3/root
+structure	/man/3/tinyfs
+structure	/man/4/factotum
+structure	/man/4/namespace
+structure	/man/5/0intro
+structure	/man/5/stat
+structure	/man/6/colour
+structure	/man/6/dis
+structure	/man/6/sexprs
+structured	/man/2/asn1
+structured	/man/2/format
+structured	/man/2/prefab-0intro
+structured	/man/3/logfs
+structured	/man/6/json
+structured	/man/6/sexprs
+structured	/man/9/canvas
+structures	/man/1/0intro
+structures	/man/1/mprof
+structures	/man/10/2c
+structures	/man/10/acid
+structures	/man/10/c2l
+structures	/man/10/devattach
+structures	/man/10/lock
+structures	/man/10/qlock
+structures	/man/10/xalloc
+structures	/man/2/asn1
+structures	/man/2/debug
+structures	/man/2/dis
+structures	/man/2/draw-0intro
+structures	/man/2/format
+structures	/man/2/prefab-compound
+structures	/man/2/prefab-element
+structures	/man/2/print
+structures	/man/2/prof
+structures	/man/2/spki
+structures	/man/2/styx
+structures	/man/2/styxflush
+structures	/man/2/sys-0intro
+structures	/man/2/sys-dirread
+structures	/man/3/dbg
+structures	/man/5/0intro
+structures	/man/6/keys
+structures	/man/8/ftl
+strucures	/man/10/c2l
+sts	/man/2/keyring-0intro
+sts	/man/2/security-0intro
+stub	/man/1/limbo
+stub	/man/10/5cv
+stubs	/man/10/devattach
+style	/man/1/acme
+style	/man/1/alphabet-fs
+style	/man/1/dd
+style	/man/1/fs
+style	/man/1/grid-monitor
+style	/man/1/mdb
+style	/man/1/mux
+style	/man/1/sh-regex
+style	/man/1/xd
+style	/man/10/2c
+style	/man/10/atoi
+style	/man/10/dmainit
+style	/man/10/parsecmd
+style	/man/2/0intro
+style	/man/2/draw-font
+style	/man/2/prefab-0intro
+style	/man/2/prefab-compound
+style	/man/2/prefab-element
+style	/man/2/prefab-environ
+style	/man/2/prefab-style
+style	/man/2/spree-cardlib
+style	/man/2/string
+style	/man/2/styxpersist
+style	/man/2/sys-print
+style	/man/2/translate
+style	/man/2/w3c-css
+style	/man/4/acme
+style	/man/4/registry
+style	/man/6/man
+style	/man/9/0intro
+style	/man/9/canvas
+style	/man/9/grid
+style	/man/9/pack
+style	/man/9/text
+style.edgecolor	/man/2/prefab-compound
+style.elemcolor	/man/2/prefab-element
+style.highlightcolor	/man/2/prefab-compound
+style.textcolor	/man/2/prefab-element
+style.textfont	/man/2/prefab-element
+style.titlecolor	/man/2/prefab-element
+style.titlefont	/man/2/prefab-element
+styles	/man/1/xd
+styles	/man/2/draw-image
+styles	/man/2/prefab-style
+styles	/man/9/listbox
+stylesheet	/man/1/ebook
+stylesheet	/man/2/w3c-css
+stylesheet	/man/2/xml
+stylised	/man/10/error
+stylus	/man/1/collab-clients
+stylus	/man/1/emu
+stylus	/man/1/keyboard
+stylus	/man/1/wm-misc
+stylus	/man/3/pointer
+stylus	/man/8/touchcal
+styx	/man/1/0intro
+styx	/man/1/alphabet-main
+styx	/man/1/bind
+styx	/man/1/grid-register
+styx	/man/1/grid-session
+styx	/man/1/listen
+styx	/man/1/ls
+styx	/man/1/sh-file2chan
+styx	/man/10/dev
+styx	/man/10/devattach
+styx	/man/10/odbc
+styx	/man/10/styx
+styx	/man/10/styxserver
+styx	/man/2/registries
+styx	/man/2/styx
+styx	/man/2/styxconv
+styx	/man/2/styxflush
+styx	/man/2/styxpersist
+styx	/man/2/styxservers
+styx	/man/2/styxservers-nametree
+styx	/man/2/sys-0intro
+styx	/man/2/sys-bind
+styx	/man/2/sys-export
+styx	/man/2/sys-fauth
+styx	/man/2/sys-file2chan
+styx	/man/2/sys-fversion
+styx	/man/2/sys-iounit
+styx	/man/2/sys-pipe
+styx	/man/2/sys-stat
+styx	/man/3/0intro
+styx	/man/3/logfs
+styx	/man/3/mnt
+styx	/man/4/0intro
+styx	/man/4/dossrv
+styx	/man/4/export
+styx	/man/4/ftpfs
+styx	/man/4/grid-cpu
+styx	/man/4/iostats
+styx	/man/4/kfs
+styx	/man/4/memfs
+styx	/man/4/mntgen
+styx	/man/4/registry
+styx	/man/4/spree
+styx	/man/4/vacfs
+styx	/man/5/0intro
+styx	/man/5/attach
+styx	/man/5/open
+styx	/man/5/read
+styx	/man/5/stat
+styx	/man/5/version
+styx	/man/5/walk
+styx	/man/6/image
+styx	/man/6/namespace
+styx	/man/8/collabsrv
+styx	/man/8/rdbgsrv
+styx	/man/8/rstyxd
+styx	/man/8/styxchat
+styx	/man/8/styxmon
+styx	/man/8/svc
+styx.b	/man/2/styx
+styx.h	/man/10/styx
+styx.h	/man/10/styxserver
+styx.h	/man/5/0intro
+styx.m	/man/2/styx
+styx.m	/man/2/styxflush
+styx.m	/man/2/styxservers
+styx.m	/man/2/styxservers-nametree
+styx.sh	/man/8/svc
+styxadddir	/man/10/styxserver
+styxaddfile	/man/10/styxserver
+styxchat	/man/8/styxchat
+styxchat	/man/8/styxmon
+styxchat.b	/man/8/styxchat
+styxclient	/man/10/styxserver
+styxconv	/man/2/styxconv
+styxconv.m	/man/2/styxconv
+styxd	/man/2/security-auth
+styxd.b	/man/2/security-auth
+styxdebug	/man/10/styxserver
+styxend	/man/10/styxserver
+styxfile	/man/10/styxserver
+styxfindfile	/man/10/styxserver
+styxflush	/man/2/styxflush
+styxflush.b	/man/2/styxflush
+styxflush.m	/man/2/styxflush
+styxfree	/man/10/styxserver
+styxinit	/man/10/styxserver
+styxlisten	/man/1/listen
+styxlisten	/man/4/spree
+styxmalloc	/man/10/styxserver
+styxmon	/man/8/rdbgsrv
+styxmon	/man/8/styxchat
+styxmon	/man/8/styxmon
+styxmon.b	/man/8/styxmon
+styxops	/man/10/styxserver
+styxperm	/man/10/styxserver
+styxpersist	/man/1/bind
+styxpersist	/man/2/styxpersist
+styxpersist.b	/man/2/styxpersist
+styxpersist.m	/man/2/styxpersist
+styxprocess	/man/10/styxserver
+styxqid	/man/10/styxserver
+styxreadstr	/man/10/styxserver
+styxrmfile	/man/10/styxserver
+styxserver	/man/10/styxserver
+styxserver	/man/2/styxservers
+styxserver	/man/2/styxservers-nametree
+styxserver.c	/man/10/styxserver
+styxserver.canopen	/man/2/styxservers
+styxserver.h	/man/10/styxserver
+styxserver.new	/man/2/styxservers
+styxserver.new	/man/2/styxservers-nametree
+styxserver.reply	/man/2/styxservers
+styxserver.walk	/man/2/styxservers
+styxservers	/man/2/styx
+styxservers	/man/2/styxservers
+styxservers	/man/2/styxservers-nametree
+styxservers	/man/2/sys-stat
+styxservers	/man/5/0intro
+styxservers.b	/man/2/styxservers
+styxservers.m	/man/2/styxservers
+styxservers.m	/man/2/styxservers-nametree
+styxsetowner	/man/10/styxserver
+styxtest	/man/10/styxserver
+styxtest.c	/man/10/styxserver
+styxtest0.c	/man/10/styxserver
+styxwait	/man/10/styxserver
+sub	/man/1/acme
+sub	/man/1/charon
+sub	/man/1/deb
+sub	/man/1/toolbar
+sub	/man/1/webgrab
+sub	/man/2/draw-point
+sub	/man/2/format
+sub	/man/2/keyring-ipint
+sub	/man/2/prefab-element
+sub	/man/2/sexprs
+sub	/man/3/ip
+sub	/man/3/pnp
+sub	/man/9/grab
+sub	/man/9/grid
+sub	/man/9/pack
+sub.2	/man/10/2c
+sub.c	/man/10/2c
+subaddress	/man/3/i2c
+subaddressed	/man/3/i2c
+subaddresses	/man/3/i2c
+subaddressing	/man/3/i2c
+subcommand	/man/1/mash-tk
+subcommand	/man/1/tktester
+subcommands	/man/1/mash-tk
+subcommands	/man/1/mdb
+subcommands	/man/1/tktester
+subcomponent	/man/10/conf
+subcomponent	/man/2/w3c-uris
+subcomponents	/man/1/webgrab
+subcomponents	/man/2/json
+subcomponents	/man/2/spki
+subcomponents	/man/2/ubfa
+subcomponents	/man/2/w3c-uris
+subcube	/man/6/colour
+subcubes	/man/6/colour
+subdirectories	/man/1/acme
+subdirectories	/man/1/bind
+subdirectories	/man/1/diff
+subdirectories	/man/1/ftree
+subdirectories	/man/1/grid-ns
+subdirectories	/man/10/odbc
+subdirectories	/man/3/ip
+subdirectories	/man/3/tls
+subdirectories	/man/5/0intro
+subdirectories	/man/6/proto
+subdirectories	/man/8/collabsrv
+subdirectories	/man/8/create
+subdirectories	/man/8/mkfs
+subdirectory	/man/1/acme
+subdirectory	/man/1/bind
+subdirectory	/man/1/du
+subdirectory	/man/1/ftree
+subdirectory	/man/1/grid-ns
+subdirectory	/man/3/sd
+subdirectory	/man/4/acme
+subdirectory	/man/4/mntgen
+subdivision	/man/6/colour
+subexpression	/man/1/acme
+subexpression	/man/1/sh-alphabet
+subexpression	/man/1/sh-regex
+subexpression	/man/2/math-0intro
+subexpression	/man/2/regex
+subexpressions	/man/1/mk
+subexpressions	/man/1/sh-alphabet
+subexpressions	/man/10/mk
+subfn	/man/1/sh-regex
+subfn	/man/1/sh-std
+subfont	/man/6/font
+subfonts	/man/2/draw-font
+subfonts	/man/3/draw
+subfonts	/man/6/font
+subfonts	/man/6/image
+subhead	/man/6/man
+subitem	/man/10/conf
+subitems	/man/10/conf
+subject	/man/1/tiny
+subject	/man/10/master
+subject	/man/2/bufio
+subject	/man/2/draw-display
+subject	/man/2/sexprs
+subject	/man/2/spki
+subject	/man/2/spki-verifier
+subject	/man/2/spree-cardlib
+subject	/man/2/sys-fversion
+subject	/man/3/dbg
+subject	/man/3/logfs
+subject	/man/9/send
+subject.kh	/man/2/spki
+subject.n	/man/2/spki
+subject.o	/man/2/spki
+subject.p	/man/2/spki
+subject.t	/man/2/spki
+subjects	/man/2/spki
+sublists	/man/2/sexprs
+submenu	/man/9/menu
+submenus	/man/1/tktester
+submenus	/man/9/menubutton
+submit	/man/9/send
+subnet	/man/2/dhcpclient
+subnet	/man/2/ip
+subnet	/man/3/ip
+subnet	/man/8/bootpd
+subnets	/man/8/rip
+subnetwork	/man/6/ndb
+subordinate	/man/1/du
+subordinate	/man/1/webgrab
+subpartitions	/man/8/prep
+subprocess	/man/2/sys-dup
+subpt	/man/2/draw-rect
+subregions	/man/6/colour
+subroutine	/man/3/0intro
+subroutines	/man/2/math-linalg
+subs	/man/1/sh-regex
+subs	/man/2/spki
+subscript	/man/10/acid
+subscript	/man/2/0intro
+subscripted	/man/2/math-linalg
+subscripts	/man/10/acid
+subscripts	/man/9/text
+subsection	/man/1/grid-ns
+subsections	/man/10/0intro
+subsections	/man/9/canvas
+subsequently	/man/1/collab-clients
+subsequently	/man/1/mash
+subsequently	/man/1/sh-file2chan
+subsequently	/man/10/plan9.ini
+subsequently	/man/2/exception
+subsequently	/man/2/registries
+subsequently	/man/2/security-0intro
+subsequently	/man/2/sh
+subsequently	/man/2/spki
+subsequently	/man/2/styx
+subsequently	/man/2/styxservers
+subsequently	/man/2/sys-0intro
+subsequently	/man/2/sys-fauth
+subsequently	/man/3/boot
+subsequently	/man/3/sign
+subsequently	/man/5/0intro
+subsequently	/man/8/svc
+subsequently	/man/9/variable
+subset	/man/1/freq
+subset	/man/10/kproc
+subset	/man/2/bloomfilter
+subset	/man/2/factotum
+subset	/man/2/format
+subset	/man/2/ida
+subset	/man/2/sys-self
+subset	/man/3/audio
+subset	/man/3/ip
+subset	/man/6/json
+subset	/man/6/proto
+subset	/man/6/sexprs
+subsets	/man/2/draw-0intro
+subshell	/man/1/sh
+subsidiary	/man/2/spki
+substantially	/man/4/dbfs
+substitute	/man/1/acme
+substitute	/man/1/calc
+substitute	/man/1/mk
+substitute	/man/10/mk
+substituted	/man/1/acme
+substituted	/man/1/mash
+substituted	/man/1/mk
+substituted	/man/1/sh
+substituted	/man/1/sh-regex
+substituted	/man/10/mk
+substituted	/man/10/rune
+substituted	/man/2/convcs
+substituted	/man/2/sh
+substituted	/man/3/ip
+substituted	/man/9/0intro
+substituted	/man/9/bind
+substitutes	/man/1/alphabet-fs
+substitutes	/man/1/fs
+substitutes	/man/1/sh-regex
+substituting	/man/1/mk
+substituting	/man/10/mk
+substituting	/man/9/0intro
+substitution	/man/1/mk
+substitution	/man/1/sh
+substitution	/man/1/sh-expr
+substitution	/man/1/sh-mload
+substitution	/man/1/sh-regex
+substitution	/man/1/sh-std
+substitution	/man/1/sh-string
+substitution	/man/1/tr
+substitution	/man/10/mk
+substitution	/man/2/sh
+substitution	/man/6/image
+substitution	/man/9/bind
+substitutions	/man/1/sh
+substitutions	/man/1/sh-regex
+substitutions	/man/1/tiny
+substitutions	/man/9/bind
+substitutions	/man/9/canvas
+substitutions	/man/9/text
+substr	/man/1/m4
+substring	/man/1/acme
+substring	/man/1/m4
+substring	/man/10/rune
+substring	/man/10/strcat
+substring	/man/2/regex
+substring	/man/3/pnp
+substring	/man/5/version
+substring	/man/6/image
+substring	/man/6/plumbing
+substring	/man/6/regexp
+substrings	/man/1/acme
+substrings	/man/6/image
+substrings	/man/6/plumbing
+substructure	/man/1/gettar
+substructure	/man/1/rm
+substructure	/man/10/2c
+substructure	/man/2/w3c-uris
+substructures	/man/10/2c
+subsumes	/man/9/grid
+subsystem	/man/3/fpga
+subsystem	/man/3/ip
+subsystems	/man/10/dev
+subsystems	/man/3/ds
+subsystems	/man/3/mpeg
+subtle	/man/2/w3c-css
+subtract	/man/1/units
+subtracted	/man/10/a.out
+subtracted	/man/9/pack
+subtraction	/man/1/mdb
+subtraction	/man/1/sh-expr
+subtree	/man/9/grab
+subtrees	/man/8/applylog
+subtype	/man/10/devattach
+subtype	/man/2/rfc822
+subtype	/man/2/sys-self
+subtype	/man/2/sys-stat
+subtypes	/man/2/xml
+subunions	/man/10/2c
+subwindowing	/man/2/draw-0intro
+subwindows	/man/2/draw-screen
+succeed	/man/1/acme
+succeed	/man/1/sh
+succeed	/man/2/keyring-auth
+succeed	/man/2/sys-open
+succeed	/man/2/wmsrv
+succeed	/man/3/ip
+succeed	/man/5/walk
+succeeded	/man/10/devattach
+succeeded	/man/2/factotum
+succeeded	/man/2/styxflush
+succeeded	/man/4/factotum
+succeeding	/man/1/uniq
+succeeds	/man/1/runas
+succeeds	/man/1/sh
+succeeds	/man/1/sh-file2chan
+succeeds	/man/10/lock
+succeeds	/man/2/alphabet-intro
+succeeds	/man/2/asn1
+succeeds	/man/2/debug
+succeeds	/man/2/secstore
+succeeds	/man/2/sys-byte2char
+succeeds	/man/2/sys-open
+succeeds	/man/3/ds
+succeeds	/man/3/ip
+succeeds	/man/4/keysrv
+succeeds	/man/4/spree
+succeeds	/man/5/open
+succeeds	/man/5/stat
+succeeds	/man/8/dhcp
+success	/man/1/sh
+success	/man/10/devattach
+success	/man/10/dynld
+success	/man/10/styxserver
+success	/man/2/asn1
+success	/man/2/attrdb
+success	/man/2/dbm
+success	/man/2/debug
+success	/man/2/dial
+success	/man/2/factotum
+success	/man/2/json
+success	/man/2/keyset
+success	/man/2/registries
+success	/man/2/scsiio
+success	/man/2/secstore
+success	/man/2/security-login
+success	/man/2/security-ssl
+success	/man/2/sexprs
+success	/man/2/spki
+success	/man/2/spki-verifier
+success	/man/2/styxservers
+success	/man/2/sys-0intro
+success	/man/2/sys-bind
+success	/man/2/sys-chdir
+success	/man/2/sys-dial
+success	/man/2/sys-export
+success	/man/2/sys-pipe
+success	/man/2/sys-stat
+success	/man/2/tftp
+success	/man/2/ubfa
+success	/man/3/boot
+success	/man/3/cmd
+success	/man/3/flash
+success	/man/7/db
+successful	/man/1/0intro
+successful	/man/1/bind
+successful	/man/1/collab-clients
+successful	/man/1/sh
+successful	/man/10/conf
+successful	/man/10/dev
+successful	/man/10/devattach
+successful	/man/10/newchan
+successful	/man/10/qio
+successful	/man/10/qlock
+successful	/man/10/styx
+successful	/man/10/styxserver
+successful	/man/2/asn1
+successful	/man/2/dbm
+successful	/man/2/dial
+successful	/man/2/draw-context
+successful	/man/2/factotum
+successful	/man/2/keyring-auth
+successful	/man/2/keyring-crypt
+successful	/man/2/keyring-getstring
+successful	/man/2/msgio
+successful	/man/2/registries
+successful	/man/2/scsiio
+successful	/man/2/security-auth
+successful	/man/2/security-ssl
+successful	/man/2/styx
+successful	/man/2/styxservers
+successful	/man/2/sys-bind
+successful	/man/2/sys-dial
+successful	/man/2/sys-dirread
+successful	/man/2/sys-fauth
+successful	/man/2/sys-file2chan
+successful	/man/3/cap
+successful	/man/3/dbg
+successful	/man/3/ip
+successful	/man/3/sign
+successful	/man/3/srv9
+successful	/man/3/tls
+successful	/man/4/factotum
+successful	/man/4/keysrv
+successful	/man/5/error
+successful	/man/5/open
+successful	/man/5/stat
+successful	/man/5/version
+successful	/man/5/walk
+successful	/man/7/db
+successful	/man/7/dbsrv
+successful	/man/8/applylog
+successful	/man/8/collabsrv
+successful	/man/8/dhcp
+successful	/man/8/fpgaload
+successful	/man/8/register
+successful	/man/8/rstyxd
+successful	/man/8/styxchat
+successfully	/man/1/alphabet-main
+successfully	/man/1/mkdir
+successfully	/man/1/os
+successfully	/man/1/sh-alphabet
+successfully	/man/1/sh-file2chan
+successfully	/man/10/devattach
+successfully	/man/10/lock
+successfully	/man/10/qlock
+successfully	/man/2/dbm
+successfully	/man/2/dhcpclient
+successfully	/man/2/json
+successfully	/man/2/pop3
+successfully	/man/2/spree-gather
+successfully	/man/2/styxpersist
+successfully	/man/2/styxservers
+successfully	/man/2/sys-bind
+successfully	/man/2/wmclient
+successfully	/man/3/cmd
+successfully	/man/5/walk
+successfully	/man/8/applylog
+successfully	/man/8/changelogin
+successive	/man/1/acme
+successive	/man/10/atoi
+successive	/man/10/devattach
+successive	/man/10/styx
+successive	/man/2/arg
+successive	/man/2/sys-byte2char
+successive	/man/2/ubfa
+successive	/man/4/registry
+successive	/man/5/walk
+successive	/man/6/font
+successive	/man/6/man
+successively	/man/3/tv
+successively	/man/8/bootpd
+successively	/man/9/grid
+successor	/man/1/mk
+successor	/man/1/tr
+successor	/man/10/mk
+suffer	/man/1/miniterm
+suffer	/man/10/splhi
+suffice	/man/10/dev
+suffice	/man/10/plan9.ini
+suffice	/man/2/dis
+sufficent	/man/3/logfs
+sufficient	/man/1/acme
+sufficient	/man/1/diff
+sufficient	/man/10/5cv
+sufficient	/man/10/lock
+sufficient	/man/2/alphabet-intro
+sufficient	/man/2/draw-image
+sufficient	/man/2/draw-rect
+sufficient	/man/2/security-0intro
+sufficient	/man/2/styxservers
+sufficient	/man/2/sys-fversion
+sufficient	/man/3/lpt
+suffix	/man/1/acme
+suffix	/man/1/basename
+suffix	/man/1/ebook
+suffix	/man/1/gzip
+suffix	/man/1/tiny
+suffix	/man/1/webgrab
+suffix	/man/2/dbm
+suffix	/man/2/disks
+suffix	/man/2/draw-image
+suffix	/man/2/names
+suffix	/man/2/rfc822
+suffix	/man/2/spki
+suffix	/man/2/w3c-css
+suffix	/man/5/version
+suffix	/man/6/dis
+suffix	/man/6/plumbing
+suffix	/man/6/sbl
+suffix	/man/8/prep
+suffix	/man/9/types
+suffix1	/man/1/webgrab
+suffix2	/man/1/webgrab
+suffixclass	/man/2/rfc822
+suffixed	/man/1/du
+suffixed	/man/1/tiny
+suffixed	/man/10/atoi
+suffixed	/man/2/complete
+suffixes	/man/8/httpd
+suggest	/man/1/sh
+suggest	/man/2/dhcpclient
+suggest	/man/8/dhcp
+suggested	/man/4/namespace
+suggests	/man/5/version
+suggests	/man/6/colour
+suid	/man/2/factotum
+suit	/man/1/acme
+suit	/man/2/spree-cardlib
+suit	/man/8/svc
+suitable	/man/1/9win
+suitable	/man/1/ebook
+suitable	/man/1/ftree
+suitable	/man/1/sh
+suitable	/man/1/sh-expr
+suitable	/man/1/sh-std
+suitable	/man/1/sh-tk
+suitable	/man/10/9load
+suitable	/man/10/devattach
+suitable	/man/10/error
+suitable	/man/10/kbdputc
+suitable	/man/10/newchan
+suitable	/man/10/styx
+suitable	/man/10/styxserver
+suitable	/man/2/convcs
+suitable	/man/2/dhcpclient
+suitable	/man/2/dial
+suitable	/man/2/diskblocks
+suitable	/man/2/factotum
+suitable	/man/2/ida
+suitable	/man/2/keyring-certtostr
+suitable	/man/2/keyset
+suitable	/man/2/newns
+suitable	/man/2/palmfile
+suitable	/man/2/prefab-0intro
+suitable	/man/2/pslib
+suitable	/man/2/registries
+suitable	/man/2/secstore
+suitable	/man/2/spki
+suitable	/man/2/styxservers
+suitable	/man/2/sys-dup
+suitable	/man/2/tk
+suitable	/man/2/tkclient
+suitable	/man/2/wmclient
+suitable	/man/3/indir
+suitable	/man/3/ip
+suitable	/man/3/logfs
+suitable	/man/3/vid
+suitable	/man/4/dbfs
+suitable	/man/4/factotum
+suitable	/man/4/import
+suitable	/man/4/keysrv
+suitable	/man/6/audio
+suitable	/man/6/colour
+suitable	/man/6/keyboard
+suitable	/man/8/bootpd
+suitable	/man/8/dns
+suitable	/man/8/ftl
+suitable	/man/8/getauthinfo
+suitable	/man/8/mkfs
+suitable	/man/8/prep
+suitable	/man/9/canvas
+suitable	/man/9/scrollbar
+suitably	/man/2/prefab-element
+suite	/man/1/miniterm
+suite	/man/10/qio
+suite	/man/10/styxserver
+suite	/man/2/convcs
+suites	/man/1/0intro
+suites	/man/10/0intro
+suitrank	/man/2/spree-cardlib
+suits	/man/2/spree-cardlib
+sum	/man/1/alphabet-fs
+sum	/man/1/calc
+sum	/man/1/fc
+sum	/man/1/fs
+sum	/man/1/math-misc
+sum	/man/1/sum
+sum	/man/10/styx
+sum	/man/2/crc
+sum	/man/2/dbm
+sum	/man/2/math-linalg
+sum	/man/6/colour
+sum	/man/6/image
+sum	/man/8/applylog
+sum	/man/9/grid
+sum.b	/man/1/sum
+summaries	/man/2/spki-verifier
+summarised	/man/1/yacc
+summarised	/man/10/0intro
+summarised	/man/2/json
+summarised	/man/6/json
+summarised	/man/8/logind
+summarises	/man/2/translate
+summarises	/man/3/ip
+summarises	/man/9/0intro
+summarize	/man/1/netstat
+summary	/man/1/du
+summary	/man/1/fc
+summary	/man/1/fs
+summary	/man/1/itest
+summary	/man/1/limbo
+summary	/man/10/9load
+summary	/man/10/xalloc
+summary	/man/3/ip
+summary	/man/3/pnp
+summary	/man/4/iostats
+summary	/man/4/namespace
+summary	/man/6/man
+summary	/man/8/applylog
+summary	/man/8/dhcp
+summary	/man/8/kfscmd
+summary	/man/8/ping
+summer	/man/2/daytime
+sums	/man/1/du
+sun	/man/10/2c
+sun	/man/2/daytime
+sun	/man/9/0intro
+sun	/man/9/1copyright
+sunday	/man/2/daytime
+sunken	/man/9/button
+sunken	/man/9/checkbutton
+sunken	/man/9/frame
+sunken	/man/9/options
+sunken	/man/9/radiobutton
+sunken	/man/9/types
+sunsparc	/man/10/acid
+super	/man/3/cmd
+super	/man/3/fs
+super	/man/9/0intro
+superficially	/man/1/0intro
+superimposition	/man/6/keyboard
+superscript	/man/6/keyboard
+superscripts	/man/9/text
+superseded	/man/1/wish
+superseded	/man/2/popup
+superseding	/man/1/dd
+superset	/man/3/ip
+supplement	/man/9/text
+supplementary	/man/1/acme
+supplies	/man/10/devattach
+supply	/man/1/secstore
+supply	/man/2/dhcpclient
+supply	/man/2/draw-context
+supply	/man/2/draw-display
+supply	/man/2/factotum
+supply	/man/2/styxservers
+supply	/man/4/acme
+supply	/man/5/0intro
+supplying	/man/1/mk
+supplying	/man/10/mk
+support	/man/1/acme
+support	/man/1/avr
+support	/man/1/brutus
+support	/man/1/calc
+support	/man/1/charon
+support	/man/1/sh
+support	/man/1/sh-alphabet
+support	/man/1/sh-expr
+support	/man/1/sh-file2chan
+support	/man/1/uuencode
+support	/man/10/0intro
+support	/man/10/2c
+support	/man/10/conf
+support	/man/10/devattach
+support	/man/10/dmainit
+support	/man/10/plan9.ini
+support	/man/2/asn1
+support	/man/2/bloomfilter
+support	/man/2/disks
+support	/man/2/format
+support	/man/2/hash
+support	/man/2/imagefile
+support	/man/2/keyring-ipint
+support	/man/2/print
+support	/man/2/prof
+support	/man/2/rfc822
+support	/man/2/spki-verifier
+support	/man/2/spree-cardlib
+support	/man/2/spree-gather
+support	/man/2/spree-objstore
+support	/man/2/sys-fversion
+support	/man/3/fs
+support	/man/3/i2c
+support	/man/3/ip
+support	/man/3/tinyfs
+support	/man/3/vga
+support	/man/4/factotum
+support	/man/4/ftpfs
+support	/man/4/kfs
+support	/man/4/lockfs
+support	/man/4/palmsrv
+support	/man/4/vacfs
+support	/man/5/0intro
+support	/man/6/man
+support	/man/6/sbl
+support	/man/6/ubfa
+support	/man/6/utf
+support	/man/7/db
+support	/man/8/0intro
+support	/man/9/1copyright
+support	/man/9/canvas
+support	/man/9/options
+support	/man/9/text
+supported	/man/1/bind
+supported	/man/1/charon
+supported	/man/1/collab
+supported	/man/1/cpu
+supported	/man/1/crypt
+supported	/man/1/miniterm
+supported	/man/1/mk
+supported	/man/1/rcmd
+supported	/man/1/sh-expr
+supported	/man/1/tcs
+supported	/man/10/9load
+supported	/man/10/mk
+supported	/man/10/plan9.ini
+supported	/man/10/styxserver
+supported	/man/2/convcs
+supported	/man/2/geodesy
+supported	/man/2/keyring-ipint
+supported	/man/2/palmfile
+supported	/man/2/print
+supported	/man/2/spki-verifier
+supported	/man/2/sys-0intro
+supported	/man/2/w3c-xpointers
+supported	/man/3/dynld
+supported	/man/3/flash
+supported	/man/3/logfs
+supported	/man/3/plap
+supported	/man/3/rtc
+supported	/man/3/sd
+supported	/man/3/ssl
+supported	/man/3/tls
+supported	/man/3/vga
+supported	/man/4/factotum
+supported	/man/4/ftpfs
+supported	/man/4/tarfs
+supported	/man/6/keyboard
+supported	/man/6/sexprs
+supported	/man/8/bootpd
+supported	/man/8/prep
+supported	/man/9/canvas
+supported	/man/9/cursor
+supported	/man/9/grid
+supported	/man/9/image
+supported	/man/9/options
+supported	/man/9/pack
+supported	/man/9/see
+supported	/man/9/text
+supported	/man/9/update
+supporting	/man/1/charon
+supporting	/man/10/dev
+supporting	/man/10/kproc
+supporting	/man/4/namespace
+supporting	/man/4/registry
+supporting	/man/8/ai2key
+supports	/man/1/avr
+supports	/man/1/charon
+supports	/man/1/emu
+supports	/man/1/mash
+supports	/man/10/9load
+supports	/man/10/intrenable
+supports	/man/2/asn1
+supports	/man/2/factotum
+supports	/man/2/json
+supports	/man/2/security-0intro
+supports	/man/2/spki-verifier
+supports	/man/2/sys-open
+supports	/man/2/ubfa
+supports	/man/2/w3c-uris
+supports	/man/3/audio
+supports	/man/3/i82365
+supports	/man/3/sd
+supports	/man/4/factotum
+supports	/man/4/ftpfs
+supports	/man/6/colour
+supports	/man/8/httpd
+supports	/man/9/canvas
+suppose	/man/2/alphabet-intro
+supposed	/man/2/disks
+supposedly	/man/4/keyfs
+suppress	/man/1/bind
+suppress	/man/1/rm
+suppress	/man/1/tkcmd
+suppress	/man/10/2c
+suppress	/man/10/2l
+suppress	/man/3/srv9
+suppress	/man/4/ftpfs
+suppress	/man/4/kfs
+suppress	/man/6/man
+suppress	/man/8/kfscmd
+suppress	/man/8/ping
+suppressed	/man/1/sh
+suppresses	/man/1/ar
+suppresses	/man/1/comm
+suppresses	/man/1/echo
+suppresses	/man/1/limbo
+suppresses	/man/1/mash-tk
+suppresses	/man/1/os
+suppresses	/man/1/yacc
+suppresses	/man/10/acid
+suppresses	/man/10/iar
+surfer	/man/1/charon
+surprisingly	/man/1/0intro
+surprisingly	/man/2/styxflush
+surrounded	/man/1/bind
+surrounded	/man/1/mash
+surrounded	/man/1/sh
+surrounded	/man/1/sh-csv
+surrounded	/man/1/sh-std
+surrounded	/man/10/acid
+surrounded	/man/2/sexprs
+surrounded	/man/4/9srvfs
+surrounded	/man/8/styxchat
+surrounded	/man/9/bind
+surrounding	/man/1/mk
+surrounding	/man/1/sh
+surrounding	/man/1/wm-sh
+surrounding	/man/10/mk
+surrounding	/man/2/string
+surrounding	/man/2/styxservers
+surrounding	/man/2/sys-chdir
+surrounding	/man/2/w3c-css
+survey	/man/2/geodesy
+survive	/man/2/registries
+survived	/man/1/0intro
+suspect	/man/10/plan9.ini
+suspend	/man/1/emu
+suspend	/man/1/sleep
+suspend	/man/10/plan9.ini
+suspend	/man/2/draw-context
+suspend	/man/2/spree
+suspend	/man/2/wmsrv
+suspend	/man/3/mpeg
+suspended	/man/1/kill
+suspended	/man/2/spree
+suspends	/man/1/sleep
+suspends	/man/2/sys-sleep
+suspension	/man/2/spree-gather
+suspension	/man/2/sys-sleep
+svc	/man/1/listen
+svc	/man/1/rcmd
+svc	/man/10/odbc
+svc	/man/2/registries
+svc	/man/2/security-login
+svc	/man/4/keysrv
+svc	/man/6/keys
+svc	/man/6/namespace
+svc	/man/7/db
+svc	/man/7/dbsrv
+svc	/man/8/ai2key
+svc	/man/8/changelogin
+svc	/man/8/cs
+svc	/man/8/getauthinfo
+svc	/man/8/httpd
+svc	/man/8/logind
+svc	/man/8/rstyxd
+svc	/man/8/signer
+svc	/man/8/svc
+svcname	/man/10/ntsrv
+svcname	/man/4/registry
+svcs	/man/2/registries
+svideo	/man/3/tv
+sw	/man/9/options
+sw	/man/9/pack
+sw	/man/9/types
+swab	/man/1/dd
+swap	/man/1/dd
+swap	/man/1/fc
+swap	/man/8/prep
+swapped	/man/1/miniterm
+sweep	/man/1/acme
+sweep	/man/3/logfs
+sweeper	/man/1/wm-misc
+sweeping	/man/1/acme
+sweeping	/man/1/wm-misc
+swept	/man/3/logfs
+swiproc	/man/10/kproc
+swiproc	/man/10/sleep
+switch	/man/1/grid-session
+switch	/man/10/devattach
+switch	/man/10/plan9.ini
+switch	/man/3/switch
+switch	/man/9/options
+switch	/man/9/text
+switched	/man/3/vid
+switches	/man/10/9load
+switches	/man/3/i2c
+switches	/man/9/text
+swrite	/man/2/scsiio
+sx	/man/3/touch
+sxord	/man/2/draw-image
+sy	/man/3/touch
+sym	/man/2/debug
+symbios	/man/10/plan9.ini
+symbol	/man/1/cprof
+symbol	/man/1/deb
+symbol	/man/1/ftree
+symbol	/man/1/mprof
+symbol	/man/1/prof
+symbol	/man/1/stack
+symbol	/man/1/yacc
+symbol	/man/10/2l
+symbol	/man/10/5cv
+symbol	/man/10/a.out
+symbol	/man/10/dynld
+symbol	/man/10/inm
+symbol	/man/10/kprof
+symbol	/man/10/kstrip
+symbol	/man/10/ms2
+symbol	/man/10/odbc
+symbol	/man/2/debug
+symbol	/man/2/prof
+symbol	/man/2/w3c-xpointers
+symbol	/man/3/0intro
+symbol	/man/6/keyboard
+symbol	/man/6/man
+symbol	/man/6/sbl
+symbol	/man/9/canvas
+symbol's	/man/10/dynld
+symbol's	/man/3/dynld
+symbolic	/man/1/chmod
+symbolic	/man/1/fc
+symbolic	/man/1/limbo
+symbolic	/man/1/sendmail
+symbolic	/man/1/webgrab
+symbolic	/man/10/acid
+symbolic	/man/10/seconds
+symbolic	/man/2/dis
+symbolic	/man/2/format
+symbolic	/man/2/registries
+symbolic	/man/2/sexprs
+symbolic	/man/2/sys-stat
+symbolic	/man/2/ubfa
+symbolic	/man/4/ftpfs
+symbolic	/man/6/dis
+symbolic	/man/6/ndb
+symbolic	/man/6/sexprs
+symbolic	/man/6/ubfa
+symbolic	/man/7/db
+symbolic	/man/8/cs
+symbolically	/man/6/dis
+symbols	/man/1/acme
+symbols	/man/1/unicode
+symbols	/man/10/2l
+symbols	/man/10/5cv
+symbols	/man/10/a.out
+symbols	/man/10/acid
+symbols	/man/10/ar
+symbols	/man/10/dynld
+symbols	/man/10/inm
+symbols	/man/10/kstrip
+symbols	/man/2/w3c-xpointers
+symbols	/man/3/dynld
+symbols	/man/6/keyboard
+symdef	/man/10/ar
+symmetric	/man/2/keyring-auth
+symmetric	/man/2/keyring-crypt
+symmetric	/man/2/keyring-rc4
+symmetric	/man/2/security-0intro
+symmetric	/man/2/security-auth
+symmetry	/man/2/draw-image
+syms	/man/10/a.out
+syms	/man/10/dynld
+sync	/man/1/dd
+sync	/man/2/venti
+sync	/man/3/flash
+sync	/man/3/logfs
+sync	/man/8/kfscmd
+synchronisation	/man/1/blur
+synchronisation	/man/10/lock
+synchronisation	/man/10/qlock
+synchronisation	/man/10/sleep
+synchronisation	/man/10/splhi
+synchronisation	/man/6/dis
+synchronise	/man/10/sleep
+synchronise	/man/2/sh
+synchronise	/man/2/sys-read
+synchronised	/man/2/palmfile
+synchronize	/man/2/sys-0intro
+synchronize	/man/3/tls
+synchronous	/man/2/sys-0intro
+synchronously	/man/1/listen
+synchronously	/man/1/tiny
+synchronously	/man/2/mpeg
+synchronously	/man/2/spree
+synchronously	/man/2/timers
+synonym	/man/1/fmt
+synonym	/man/10/print
+synonym	/man/2/dbm
+synonym	/man/2/sys-self
+synonym	/man/2/w3c-css
+synopses	/man/8/prep
+synopsys	/man/3/flash
+synopsys	/man/3/switch
+synopsys	/man/3/usb
+synopsys	/man/8/cs
+syntactic	/man/1/sh-std
+syntactic	/man/2/w3c-css
+syntactic	/man/5/0intro
+syntactic	/man/6/sbl
+syntactically	/man/10/c2l
+syntax	/man/1/calc
+syntax	/man/1/charon
+syntax	/man/1/mash
+syntax	/man/1/mdb
+syntax	/man/1/sh
+syntax	/man/1/sh-alphabet
+syntax	/man/1/sh-regex
+syntax	/man/1/sh-std
+syntax	/man/1/tail
+syntax	/man/1/tiny
+syntax	/man/10/5cv
+syntax	/man/10/atoi
+syntax	/man/10/plan9.ini
+syntax	/man/2/asn1
+syntax	/man/2/attrdb
+syntax	/man/2/draw-display
+syntax	/man/2/ip
+syntax	/man/2/names
+syntax	/man/2/rfc822
+syntax	/man/2/tk
+syntax	/man/2/w3c-css
+syntax	/man/2/w3c-uris
+syntax	/man/2/w3c-xpointers
+syntax	/man/3/cmd
+syntax	/man/3/flash
+syntax	/man/3/ip
+syntax	/man/6/json
+syntax	/man/6/keytext
+syntax	/man/6/regexp
+syntax	/man/6/sexprs
+syntax	/man/6/translate
+syntax	/man/6/ubfa
+syntax	/man/8/httpd
+syntax	/man/8/ping
+syntax	/man/9/bind
+syntax	/man/9/canvas
+syntax	/man/9/cursor
+syntax	/man/9/text
+synthesise	/man/1/0intro
+synthesise	/man/4/ramfile
+synthesised	/man/4/ramfile
+synthesiser	/man/3/fpga
+synthesize	/man/5/0intro
+synthesizes	/man/5/0intro
+sys	/man/1/0intro
+sys	/man/1/alphabet-fs
+sys	/man/1/auplay
+sys	/man/1/bind
+sys	/man/1/cd
+sys	/man/1/chgrp
+sys	/man/1/chmod
+sys	/man/1/cp
+sys	/man/1/cpu
+sys	/man/1/du
+sys	/man/1/fc
+sys	/man/1/fs
+sys	/man/1/ftest
+sys	/man/1/kill
+sys	/man/1/limbo
+sys	/man/1/listen
+sys	/man/1/ls
+sys	/man/1/mkdir
+sys	/man/1/mv
+sys	/man/1/ns
+sys	/man/1/pwd
+sys	/man/1/read
+sys	/man/1/rm
+sys	/man/1/secstore
+sys	/man/1/sh
+sys	/man/1/sh-file2chan
+sys	/man/1/sh-std
+sys	/man/1/sleep
+sys	/man/1/stack
+sys	/man/1/stream
+sys	/man/1/telnet
+sys	/man/1/time
+sys	/man/1/tiny
+sys	/man/1/touch
+sys	/man/1/yacc
+sys	/man/10/2a
+sys	/man/10/2c
+sys	/man/10/ar
+sys	/man/10/c2l
+sys	/man/10/dev
+sys	/man/10/devattach
+sys	/man/10/error
+sys	/man/10/master
+sys	/man/10/newchan
+sys	/man/10/odbc
+sys	/man/10/styx
+sys	/man/2/0intro
+sys	/man/2/arg
+sys	/man/2/bloomfilter
+sys	/man/2/bufio
+sys	/man/2/bufio-chanfill
+sys	/man/2/command
+sys	/man/2/daytime
+sys	/man/2/dbm
+sys	/man/2/dhcpclient
+sys	/man/2/dial
+sys	/man/2/diskblocks
+sys	/man/2/disks
+sys	/man/2/draw-0intro
+sys	/man/2/draw-context
+sys	/man/2/draw-display
+sys	/man/2/draw-example
+sys	/man/2/drawmux
+sys	/man/2/env
+sys	/man/2/factotum
+sys	/man/2/filepat
+sys	/man/2/fsproto
+sys	/man/2/ir
+sys	/man/2/itslib
+sys	/man/2/keyring-auth
+sys	/man/2/keyring-getmsg
+sys	/man/2/keyring-getstring
+sys	/man/2/keyring-sha1
+sys	/man/2/msgio
+sys	/man/2/names
+sys	/man/2/newns
+sys	/man/2/palmfile
+sys	/man/2/plumbmsg
+sys	/man/2/print
+sys	/man/2/readdir
+sys	/man/2/registries
+sys	/man/2/scsiio
+sys	/man/2/secstore
+sys	/man/2/security-auth
+sys	/man/2/security-login
+sys	/man/2/security-ssl
+sys	/man/2/sets
+sys	/man/2/spree
+sys	/man/2/srv
+sys	/man/2/styx
+sys	/man/2/styxconv
+sys	/man/2/styxflush
+sys	/man/2/styxpersist
+sys	/man/2/styxservers
+sys	/man/2/styxservers-nametree
+sys	/man/2/sys-0intro
+sys	/man/2/sys-bind
+sys	/man/2/sys-byte2char
+sys	/man/2/sys-chdir
+sys	/man/2/sys-dial
+sys	/man/2/sys-dirread
+sys	/man/2/sys-dup
+sys	/man/2/sys-export
+sys	/man/2/sys-fauth
+sys	/man/2/sys-fd2path
+sys	/man/2/sys-file2chan
+sys	/man/2/sys-fversion
+sys	/man/2/sys-iounit
+sys	/man/2/sys-millisec
+sys	/man/2/sys-open
+sys	/man/2/sys-pctl
+sys	/man/2/sys-pipe
+sys	/man/2/sys-print
+sys	/man/2/sys-read
+sys	/man/2/sys-remove
+sys	/man/2/sys-seek
+sys	/man/2/sys-self
+sys	/man/2/sys-sleep
+sys	/man/2/sys-stat
+sys	/man/2/sys-tokenize
+sys	/man/2/sys-utfbytes
+sys	/man/2/sys-werrstr
+sys	/man/2/tftp
+sys	/man/2/timers
+sys	/man/2/venti
+sys	/man/2/virgil
+sys	/man/2/wait
+sys	/man/2/wmsrv
+sys	/man/2/workdir
+sys	/man/3/0intro
+sys	/man/3/arch
+sys	/man/3/cap
+sys	/man/3/cons
+sys	/man/3/draw
+sys	/man/3/dup
+sys	/man/3/eia
+sys	/man/3/env
+sys	/man/3/fs
+sys	/man/3/ftl
+sys	/man/3/gpio
+sys	/man/3/i2c
+sys	/man/3/indir
+sys	/man/3/ip
+sys	/man/3/mnt
+sys	/man/3/mpeg
+sys	/man/3/pipe
+sys	/man/3/plap
+sys	/man/3/prog
+sys	/man/3/root
+sys	/man/3/sign
+sys	/man/3/srv
+sys	/man/3/srv9
+sys	/man/3/ssl
+sys	/man/3/tls
+sys	/man/4/9srvfs
+sys	/man/4/dbfs
+sys	/man/4/export
+sys	/man/4/keyfs
+sys	/man/4/kfs
+sys	/man/4/logfile
+sys	/man/4/namespace
+sys	/man/4/ramfile
+sys	/man/4/registry
+sys	/man/5/0intro
+sys	/man/5/attach
+sys	/man/5/clunk
+sys	/man/5/error
+sys	/man/5/flush
+sys	/man/5/open
+sys	/man/5/read
+sys	/man/5/remove
+sys	/man/5/stat
+sys	/man/5/version
+sys	/man/5/walk
+sys	/man/6/audio
+sys	/man/6/namespace
+sys	/man/6/ndb
+sys	/man/6/utf
+sys	/man/7/db
+sys	/man/8/bootpd
+sys	/man/8/collabsrv
+sys	/man/8/cs
+sys	/man/8/httpd
+sys	/man/8/ping
+sys	/man/8/styxchat
+sys	/man/9/types
+sys.m	/man/1/acme
+sys.m	/man/1/yacc
+sys.m	/man/2/0intro
+sys.m	/man/2/arg
+sys.m	/man/2/draw-example
+sys.m	/man/2/format
+sys.m	/man/2/ir
+sys.m	/man/2/itslib
+sys.m	/man/2/security-ssl
+sys.m	/man/2/spree
+sys.m	/man/2/spree-cardlib
+sys.m	/man/2/styxflush
+sys.m	/man/2/styxpersist
+sys.m	/man/2/styxservers
+sys.m	/man/2/styxservers-nametree
+sys.m	/man/2/sys-0intro
+sys.m	/man/2/sys-bind
+sys.m	/man/2/sys-byte2char
+sys.m	/man/2/sys-chdir
+sys.m	/man/2/sys-dial
+sys.m	/man/2/sys-dirread
+sys.m	/man/2/sys-dup
+sys.m	/man/2/sys-export
+sys.m	/man/2/sys-fauth
+sys.m	/man/2/sys-fd2path
+sys.m	/man/2/sys-file2chan
+sys.m	/man/2/sys-fversion
+sys.m	/man/2/sys-iounit
+sys.m	/man/2/sys-millisec
+sys.m	/man/2/sys-open
+sys.m	/man/2/sys-pctl
+sys.m	/man/2/sys-pipe
+sys.m	/man/2/sys-print
+sys.m	/man/2/sys-read
+sys.m	/man/2/sys-remove
+sys.m	/man/2/sys-seek
+sys.m	/man/2/sys-self
+sys.m	/man/2/sys-sleep
+sys.m	/man/2/sys-stat
+sys.m	/man/2/sys-tokenize
+sys.m	/man/2/sys-utfbytes
+sys.m	/man/2/sys-werrstr
+sys.m	/man/2/wmlib
+sys.m	/man/2/wmsrv
+syscmd	/man/1/m4
+sysctl	/man/3/cons
+sysctl	/man/8/shutdown
+sysname	/man/1/tiny
+sysname	/man/3/cons
+systarg	/man/10/conf
+systec	/man/2/keyring-crypt
+system's	/man/1/emu
+system's	/man/1/netkey
+system's	/man/10/master
+system's	/man/10/seconds
+system's	/man/2/dis
+system's	/man/2/plumbmsg
+system's	/man/2/srv
+system's	/man/2/styxservers-nametree
+system's	/man/3/cmd
+system's	/man/3/flash
+system's	/man/3/fpga
+system's	/man/3/i2c
+system's	/man/3/ip
+system's	/man/3/pointer
+system's	/man/3/snarf
+system's	/man/4/archfs
+system's	/man/8/bootpd
+systemid	/man/2/xml
+systems	/man/1/0intro
+systems	/man/1/chgrp
+systems	/man/1/chmod
+systems	/man/1/emu
+systems	/man/10/0intro
+systems	/man/10/9load
+systems	/man/10/eve
+systems	/man/10/plan9.ini
+systems	/man/2/command
+systems	/man/2/dbm
+systems	/man/2/disks
+systems	/man/2/draw-display
+systems	/man/2/encoding
+systems	/man/2/geodesy
+systems	/man/2/math-0intro
+systems	/man/2/plumbmsg
+systems	/man/2/security-0intro
+systems	/man/2/srv
+systems	/man/2/styx
+systems	/man/3/cmd
+systems	/man/3/cons
+systems	/man/3/ds
+systems	/man/3/ip
+systems	/man/3/logfs
+systems	/man/4/dossrv
+systems	/man/4/ftpfs
+systems	/man/4/mntgen
+systems	/man/4/namespace
+systems	/man/4/spree
+systems	/man/5/0intro
+systems	/man/6/colour
+systems	/man/6/keyboard
+systems	/man/7/db
+systems	/man/8/applylog
+systems	/man/8/ping
+systems	/man/8/prep
+szelem	/man/10/malloc
+szymanski	/man/6/image
+t's	/man/3/cons
+t.dis	/man/1/itest
+t.sh	/man/1/itest
+t.stat	/man/2/styxservers
+t.stop	/man/2/timers
+t.timeout	/man/2/timers
+t0x04010000	/man/10/5coff
+t2r4	/man/3/vga
+t2r4hwgc	/man/3/vga
+ta	/man/10/styx
+tab	/man/1/acme
+tab	/man/1/charon
+tab	/man/1/look
+tab	/man/1/mash
+tab	/man/1/sh
+tab	/man/1/sum
+tab	/man/1/tiny
+tab	/man/1/tsort
+tab	/man/1/wish
+tab	/man/10/conf
+tab	/man/10/devattach
+tab	/man/10/parsecmd
+tab	/man/2/asn1
+tab	/man/2/plumbmsg
+tab	/man/2/string
+tab	/man/2/stringinttab
+tab	/man/2/tabs
+tab	/man/4/acme
+tab	/man/4/registry
+tab	/man/6/dis
+tab	/man/6/json
+tab	/man/6/keyboard
+tab	/man/6/sexprs
+tab	/man/6/translate
+tab	/man/6/ubfa
+tab	/man/9/text
+tabbed	/man/2/tabs
+table	/man/1/ar
+table	/man/1/charon
+table	/man/1/cprof
+table	/man/1/fortune
+table	/man/1/freq
+table	/man/1/limbo
+table	/man/1/math-misc
+table	/man/1/mprof
+table	/man/1/prof
+table	/man/1/secstore
+table	/man/1/sh-csv
+table	/man/1/unicode
+table	/man/1/wm-misc
+table	/man/10/2l
+table	/man/10/5cv
+table	/man/10/a.out
+table	/man/10/conf
+table	/man/10/dev
+table	/man/10/devattach
+table	/man/10/dynld
+table	/man/10/iar
+table	/man/10/inm
+table	/man/10/kprof
+table	/man/10/kstrip
+table	/man/10/ms2
+table	/man/10/plan9.ini
+table	/man/10/styxserver
+table	/man/2/alphabet-intro
+table	/man/2/asn1
+table	/man/2/crc
+table	/man/2/daytime
+table	/man/2/dis
+table	/man/2/disks
+table	/man/2/hash
+table	/man/2/json
+table	/man/2/prof
+table	/man/2/spree-cardlib
+table	/man/2/stringinttab
+table	/man/2/styxservers
+table	/man/2/sys-0intro
+table	/man/3/ip
+table	/man/3/logfs
+table	/man/3/tinyfs
+table	/man/6/dis
+table	/man/6/keyboard
+table	/man/6/man
+table	/man/6/sbl
+table	/man/7/cddb
+table	/man/8/prep
+table	/man/8/rip
+table	/man/9/grid
+table's	/man/10/dynld
+tablename	/man/10/odbc
+tables	/man/1/date
+tables	/man/1/mdb
+tables	/man/1/yacc
+tables	/man/10/2l
+tables	/man/10/9load
+tables	/man/10/dynld
+tables	/man/10/odbc
+tables	/man/10/xalloc
+tables	/man/2/dis
+tables	/man/2/disks
+tables	/man/3/ip
+tables	/man/6/man
+tables	/man/6/sbl
+tables	/man/8/rip
+tablist	/man/9/text
+tabs	/man/1/diff
+tabs	/man/1/look
+tabs	/man/1/m4
+tabs	/man/1/mash
+tabs	/man/1/mc
+tabs	/man/1/sh
+tabs	/man/1/tktester
+tabs	/man/1/wc
+tabs	/man/10/atoi
+tabs	/man/2/plumbmsg
+tabs	/man/2/popup
+tabs	/man/2/selectfile
+tabs	/man/2/styxservers
+tabs	/man/2/tabs
+tabs	/man/6/attrdb
+tabs	/man/6/man
+tabs	/man/9/0intro
+tabs	/man/9/text
+tabs.b	/man/2/tabs
+tabs.m	/man/2/tabs
+tabsctl	/man/2/tabs
+tag	/man/1/acme
+tag	/man/1/cook
+tag	/man/1/sh-file2chan
+tag	/man/1/timestamp
+tag	/man/1/wm
+tag	/man/10/2c
+tag	/man/10/plan9.ini
+tag	/man/10/styx
+tag	/man/10/styxserver
+tag	/man/2/asn1
+tag	/man/2/attrdb
+tag	/man/2/dis
+tag	/man/2/factotum
+tag	/man/2/ida
+tag	/man/2/prefab-element
+tag	/man/2/spki
+tag	/man/2/spki-verifier
+tag	/man/2/spree-allow
+tag	/man/2/styx
+tag	/man/2/styxflush
+tag	/man/2/ubfa
+tag	/man/2/wmlib
+tag	/man/2/wmsrv
+tag	/man/2/xml
+tag	/man/3/dynld
+tag	/man/3/ip
+tag	/man/3/tls
+tag	/man/4/acme
+tag	/man/4/factotum
+tag	/man/5/0intro
+tag	/man/5/attach
+tag	/man/5/clunk
+tag	/man/5/error
+tag	/man/5/flush
+tag	/man/5/open
+tag	/man/5/read
+tag	/man/5/remove
+tag	/man/5/stat
+tag	/man/5/version
+tag	/man/5/walk
+tag	/man/6/man
+tag	/man/6/sbl
+tag	/man/6/ubfa
+tag	/man/8/ai2key
+tag	/man/8/styxchat
+tag	/man/9/bind
+tag	/man/9/canvas
+tag	/man/9/text
+tag's	/man/9/text
+tag.class	/man/2/asn1
+tag.constr	/man/2/asn1
+tag.num	/man/2/asn1
+tag.tostring	/man/2/asn1
+tag1	/man/2/asn1
+tag2	/man/2/asn1
+tagged	/man/1/collab-clients
+tagged	/man/1/sh
+tagged	/man/10/styxserver
+tagged	/man/2/asn1
+tagged	/man/2/spree-allow
+tagged	/man/2/ubfa
+tagged	/man/2/venti
+tagged	/man/2/wmsrv
+tagged	/man/3/tls
+tagged	/man/8/ai2key
+tagged	/man/9/text
+tagging	/man/1/timestamp
+tagging	/man/2/asn1
+tagimplies	/man/2/spki
+tagintersect	/man/2/spki
+taglist	/man/9/canvas
+taglist	/man/9/text
+tagname	/man/10/2c
+tagname	/man/9/text
+tagno	/man/4/factotum
+tagof	/man/2/asn1
+tagof	/man/2/json
+tagof	/man/2/styxflush
+tagof	/man/2/ubfa
+tagorid	/man/9/canvas
+tags	/man/1/acme
+tags	/man/1/brutus
+tags	/man/1/cook
+tags	/man/1/sh-file2chan
+tags	/man/2/asn1
+tags	/man/2/dis
+tags	/man/2/prefab-compound
+tags	/man/2/prefab-element
+tags	/man/2/spki
+tags	/man/3/tls
+tags	/man/5/0intro
+tags	/man/8/kfscmd
+tags	/man/8/styxchat
+tags	/man/9/canvas
+tags	/man/9/text
+tagselect	/man/2/prefab-compound
+tagtodelete	/man/9/canvas
+tail	/man/1/mk
+tail	/man/1/tail
+tail	/man/1/tee
+tail	/man/10/acid
+tail	/man/10/mk
+tail	/man/10/qio
+tail	/man/2/sexprs
+tail	/man/3/cons
+tail	/man/3/prog
+tailored	/man/2/security-0intro
+takeattrs	/man/2/factotum
+takefocus	/man/9/button
+takefocus	/man/9/canvas
+takefocus	/man/9/checkbutton
+takefocus	/man/9/entry
+takefocus	/man/9/label
+takefocus	/man/9/listbox
+takefocus	/man/9/menubutton
+takefocus	/man/9/options
+takefocus	/man/9/radiobutton
+takefocus	/man/9/scale
+takefocus	/man/9/text
+takes	/man/1/0intro
+takes	/man/1/acme
+takes	/man/1/alphabet-fs
+takes	/man/1/alphabet-main
+takes	/man/1/bind
+takes	/man/1/blur
+takes	/man/1/charon
+takes	/man/1/deb
+takes	/man/1/disdep
+takes	/man/1/ebook
+takes	/man/1/fs
+takes	/man/1/grid-register
+takes	/man/1/grid-session
+takes	/man/1/look
+takes	/man/1/mk
+takes	/man/1/sh
+takes	/man/1/sh-alphabet
+takes	/man/1/sh-arg
+takes	/man/1/sh-expr
+takes	/man/1/sh-string
+takes	/man/1/sh-tk
+takes	/man/1/tiny
+takes	/man/1/wish
+takes	/man/1/yacc
+takes	/man/10/devattach
+takes	/man/10/mk
+takes	/man/10/print
+takes	/man/10/styx
+takes	/man/2/arg
+takes	/man/2/attrdb
+takes	/man/2/bufio
+takes	/man/2/dial
+takes	/man/2/disks
+takes	/man/2/draw-image
+takes	/man/2/ether
+takes	/man/2/filter-slip
+takes	/man/2/ida
+takes	/man/2/keyring-certtostr
+takes	/man/2/lists
+takes	/man/2/plumbmsg
+takes	/man/2/prefab-compound
+takes	/man/2/prefab-element
+takes	/man/2/prof
+takes	/man/2/rfc822
+takes	/man/2/secstore
+takes	/man/2/security-0intro
+takes	/man/2/sh
+takes	/man/2/string
+takes	/man/2/styxconv
+takes	/man/2/sys-dial
+takes	/man/2/sys-dup
+takes	/man/2/sys-export
+takes	/man/2/sys-print
+takes	/man/2/w3c-css
+takes	/man/2/wait
+takes	/man/3/ftl
+takes	/man/3/prog
+takes	/man/6/colour
+takes	/man/7/cddb
+takes	/man/8/applylog
+takes	/man/8/dhcp
+takes	/man/8/getauthinfo
+takes	/man/8/prep
+takes	/man/9/scrollbar
+taking	/man/1/alphabet-fs
+taking	/man/1/asm
+taking	/man/1/blur
+taking	/man/1/fs
+taking	/man/1/mash-make
+taking	/man/2/bufio
+taking	/man/2/csv
+taking	/man/2/draw-example
+taking	/man/2/sh
+taking	/man/2/styxservers
+taking	/man/3/boot
+talk	/man/1/telnet
+talk	/man/2/ubfa
+talk	/man/6/ubfa
+tall	/man/2/prefab-element
+tall	/man/9/text
+tallied	/man/1/du
+tally	/man/1/wc
+tampered	/man/2/security-0intro
+tampering	/man/3/tls
+tan	/man/1/calc
+tan	/man/2/math-elem
+tanh	/man/1/calc
+tanh	/man/2/math-elem
+tape	/man/1/dd
+tape	/man/1/mux
+tapping	/man/1/wm-misc
+tar	/man/1/ar
+tar	/man/1/gettar
+tar	/man/4/tarfs
+tar.gz	/man/1/ar
+tarfile	/man/4/tarfs
+tarfs	/man/1/gettar
+tarfs	/man/4/archfs
+tarfs	/man/4/tarfs
+tarfs.b	/man/4/tarfs
+target	/man/1/bind
+target	/man/1/cp
+target	/man/1/mash-make
+target	/man/1/mk
+target	/man/1/wm-misc
+target	/man/10/2c
+target	/man/10/conf
+target	/man/10/dmainit
+target	/man/10/mk
+target	/man/2/xml
+target	/man/3/dbg
+target	/man/3/dup
+target	/man/3/ftl
+target	/man/3/i2c
+target	/man/6/translate
+target	/man/8/ftl
+target	/man/8/ping
+target's	/man/1/mk
+target's	/man/10/mk
+target1	/man/1/mk
+target1	/man/10/mk
+target2	/man/1/mk
+target2	/man/10/mk
+targeted	/man/1/0intro
+targets	/man/1/mash-make
+targets	/man/1/mk
+targets	/man/10/mk
+tarlist	/man/1/gettar
+task	/man/1/blur
+task	/man/1/keyboard
+task	/man/1/ps
+task	/man/1/sh-tk
+task	/man/1/tiny
+task	/man/1/wm
+task	/man/1/wm-misc
+task	/man/10/kproc
+task	/man/2/tkclient
+task	/man/4/grid-cpu
+task.b	/man/1/wm-misc
+taskbar	/man/1/mash-tk
+tasks	/man/1/0intro
+tasks	/man/1/alphabet-abc
+tasks	/man/1/alphabet-grid
+tasks	/man/1/grid-query
+tasks	/man/1/grid-session
+tasks	/man/1/sh
+tasks	/man/1/tiny
+tasks	/man/10/devattach
+tasks	/man/2/styxservers
+tasks	/man/9/canvas
+tasktype	/man/1/alphabet-abc
+tasktype	/man/1/alphabet-grid
+taslock.c	/man/10/lock
+tattach	/man/10/styx
+tattach	/man/2/styxservers
+tattach	/man/5/0intro
+tattach	/man/5/attach
+tattach	/man/8/styxchat
+taught	/man/1/mash-make
+tauth	/man/10/styx
+tauth	/man/2/styxservers
+tauth	/man/5/0intro
+tauth	/man/5/attach
+tauth	/man/8/styxchat
+tb	/man/4/factotum
+tba	/man/10/c2l
+tbdf	/man/10/intrenable
+tbl	/man/1/man
+tbl	/man/6/man
+tc	/man/10/devattach
+tchan	/man/2/styxservers-nametree
+tcl	/man/1/wish
+tcl	/man/2/tk
+tcl	/man/9/0intro
+tclunk	/man/5/0intro
+tclunk	/man/5/clunk
+tclunk	/man/8/styxchat
+tcondbreak	/man/3/dbg
+tconf	/man/2/itslib
+tconf.done	/man/2/itslib
+tconf.report	/man/2/itslib
+tconfig	/man/2/itslib
+tcp	/man/1/alphabet-abc
+tcp	/man/1/alphabet-grid
+tcp	/man/1/cpu
+tcp	/man/1/dmview
+tcp	/man/1/grid-register
+tcp	/man/1/listen
+tcp	/man/1/miniterm
+tcp	/man/1/netstat
+tcp	/man/1/telnet
+tcp	/man/10/odbc
+tcp	/man/10/styxserver
+tcp	/man/2/bufio
+tcp	/man/2/dial
+tcp	/man/2/keyring-getmsg
+tcp	/man/2/msgio
+tcp	/man/2/srv
+tcp	/man/2/sys-dial
+tcp	/man/3/ip
+tcp	/man/4/ftpfs
+tcp	/man/4/grid-cpu
+tcp	/man/4/import
+tcp	/man/4/keysrv
+tcp	/man/4/lockfs
+tcp	/man/4/registry
+tcp	/man/4/spree
+tcp	/man/6/ndb
+tcp	/man/8/collabsrv
+tcp	/man/8/cs
+tcp	/man/8/httpd
+tcp	/man/8/logind
+tcp	/man/8/mkfs
+tcp	/man/8/svc
+tcpmsg	/man/3/ip
+tcreate	/man/10/styx
+tcreate	/man/5/0intro
+tcreate	/man/5/flush
+tcreate	/man/5/open
+tcreate	/man/8/styxchat
+tcs	/man/1/tcs
+tcs	/man/2/convcs
+tcs.b	/man/1/tcs
+teal	/man/9/types
+tech	/man/2/keyring-crypt
+technicality	/man/2/spree
+technique	/man/1/mk
+technique	/man/10/9load
+technique	/man/10/mk
+technique	/man/10/xalloc
+technique	/man/2/security-0intro
+technology	/man/1/avr
+technology's	/man/1/sum
+tedious	/man/2/styxservers
+tee	/man/1/tee
+tee.b	/man/1/tee
+tel	/man/6/attrdb
+telecom	/man/1/miniterm
+telecom's	/man/1/miniterm
+telephone	/man/2/keyring-0intro
+telephone	/man/8/register
+telephone	/man/8/signer
+telephones	/man/1/mux
+telephones	/man/2/security-0intro
+telephony	/man/10/kproc
+teletexstring	/man/2/asn1
+television	/man/1/mux
+television	/man/2/prefab-0intro
+television	/man/3/tv
+television	/man/6/colour
+televisions	/man/1/mux
+tells	/man/1/9win
+tells	/man/1/blur
+tells	/man/1/collab
+tells	/man/1/diff
+tells	/man/1/gettar
+tells	/man/1/grid-register
+tells	/man/1/miniterm
+tells	/man/1/tktester
+tells	/man/1/wm
+tells	/man/10/2c
+tells	/man/10/ntsrv
+tells	/man/2/dis
+tells	/man/2/draw-display
+tells	/man/2/draw-font
+tells	/man/2/draw-image
+tells	/man/2/security-auth
+tells	/man/2/venti
+tells	/man/2/wmlib
+tells	/man/8/bootpd
+tells	/man/8/register
+tells	/man/8/rip
+tells	/man/9/canvas
+telnet	/man/1/miniterm
+telnet	/man/1/telnet
+telnet.b	/man/1/telnet
+temp	/man/10/plan9.ini
+tempfile	/man/2/diskblocks
+template	/man/1/acme
+template	/man/1/tktester
+template	/man/4/factotum
+templates	/man/1/tktester
+templates	/man/4/factotum
+temporaries	/man/1/ar
+temporaries	/man/10/iar
+temporarily	/man/1/acme
+temporarily	/man/10/lock
+temporarily	/man/4/ftpfs
+temporarily	/man/4/import
+temporary	/man/1/chmod
+temporary	/man/1/ls
+temporary	/man/1/m4
+temporary	/man/1/sh-std
+temporary	/man/1/vacget
+temporary	/man/10/iar
+temporary	/man/2/diskblocks
+temporary	/man/4/namespace
+temporary	/man/5/stat
+temporary	/man/8/getauthinfo
+temporary	/man/8/mkfs
+temporary	/man/8/rdbgsrv
+ten	/man/1/tail
+tend	/man/10/intrenable
+tend	/man/2/0intro
+tend	/man/6/colour
+tenth	/man/1/yacc
+tenth	/man/10/2c
+tenth	/man/9/canvas
+tenths	/man/3/prog
+tenths	/man/9/canvas
+tentrysize	/man/2/disks
+term	/man/1/sh
+term	/man/1/yacc
+term	/man/2/w3c-css
+term	/man/3/dbg
+term	/man/3/touch
+term	/man/6/proto
+terminal	/man/1/sh
+terminal	/man/4/acme
+terminals	/man/10/plan9.ini
+terminals	/man/2/w3c-css
+terminals	/man/6/sbl
+terminate	/man/1/kill
+terminate	/man/1/mk
+terminate	/man/1/sh
+terminate	/man/10/error
+terminate	/man/10/mk
+terminate	/man/2/convcs
+terminate	/man/2/filter
+terminate	/man/3/ether
+terminate	/man/3/tls
+terminate	/man/4/logfile
+terminate	/man/6/login
+terminate	/man/9/text
+terminates	/man/1/deb
+terminates	/man/1/kill
+terminates	/man/1/look
+terminates	/man/1/os
+terminates	/man/1/tkcmd
+terminates	/man/10/atoi
+terminates	/man/10/kproc
+terminates	/man/2/draw-image
+terminates	/man/2/filter
+terminates	/man/2/spree
+terminates	/man/2/sys-export
+terminates	/man/3/cmd
+terminates	/man/3/kprof
+terminates	/man/3/pipe
+terminates	/man/3/vga
+terminates	/man/4/9srvfs
+terminates	/man/9/canvas
+termination	/man/1/alphabet-fs
+termination	/man/1/fs
+termination	/man/1/tiny
+termination	/man/10/acid
+termination	/man/10/kproc
+terminator	/man/1/tiny
+terminator	/man/2/csv
+terminators	/man/2/csv
+terminology	/man/2/draw-0intro
+terms	/man/1/charon
+terms	/man/1/math-misc
+terms	/man/1/sh-alphabet
+terms	/man/10/odbc
+terms	/man/2/convcs
+terms	/man/2/draw-image
+terms	/man/2/w3c-css
+terms	/man/2/w3c-xpointers
+terms	/man/4/iostats
+terms	/man/9/1copyright
+ternary	/man/10/c2l
+terr	/man/3/dbg
+terrestrial	/man/2/geodesy
+terror	/man/5/error
+test	/man/1/0intro
+test	/man/1/ftest
+test	/man/1/itest
+test	/man/1/sh
+test	/man/1/sh-expr
+test	/man/1/sh-std
+test	/man/1/sh-test
+test	/man/1/tktester
+test	/man/10/plan9.ini
+test	/man/10/styx
+test	/man/2/asn1
+test	/man/2/bloomfilter
+test	/man/2/draw-example
+test	/man/2/itslib
+test	/man/3/kprof
+test	/man/6/sbl
+test.b	/man/1/gettar
+test.tar	/man/1/gettar
+testdir	/man/1/itest
+tested	/man/1/ftest
+tested	/man/10/error
+tested	/man/10/plan9.ini
+tested	/man/2/0intro
+tested	/man/2/security-0intro
+testifies	/man/2/security-0intro
+testifying	/man/2/security-0intro
+testing	/man/1/sh-std
+testing	/man/1/sh-test
+testing	/man/1/tkcmd
+testing	/man/1/tktester
+testing	/man/1/vacget
+testing	/man/1/wish
+testing	/man/2/itslib
+testing	/man/3/ftl
+testing	/man/4/vacfs
+tests	/man/1/ftest
+tests	/man/1/itest
+tests	/man/1/math-misc
+tests	/man/1/sh-test
+tests	/man/1/tktester
+tests	/man/2/asn1
+tests	/man/2/itslib
+tetris	/man/1/wm-misc
+tetris.b	/man/1/wm-misc
+text	/man/1/0intro
+text	/man/1/acme
+text	/man/1/brutus
+text	/man/1/charon
+text	/man/1/collab-clients
+text	/man/1/cprof
+text	/man/1/crypt
+text	/man/1/deb
+text	/man/1/diff
+text	/man/1/ebook
+text	/man/1/fmt
+text	/man/1/logwindow
+text	/man/1/m4
+text	/man/1/man
+text	/man/1/mash
+text	/man/1/mash-make
+text	/man/1/mash-tk
+text	/man/1/miniterm
+text	/man/1/mk
+text	/man/1/mprof
+text	/man/1/plumb
+text	/man/1/prof
+text	/man/1/secstore
+text	/man/1/sendmail
+text	/man/1/sh-sexprs
+text	/man/1/sh-tk
+text	/man/1/strings
+text	/man/1/tcs
+text	/man/1/tktester
+text	/man/1/toolbar
+text	/man/1/unicode
+text	/man/1/wc
+text	/man/1/wm-misc
+text	/man/1/wm-sh
+text	/man/1/yacc
+text	/man/10/2l
+text	/man/10/5coff
+text	/man/10/5cv
+text	/man/10/a.out
+text	/man/10/acid
+text	/man/10/ar
+text	/man/10/atoi
+text	/man/10/conf
+text	/man/10/dynld
+text	/man/10/inm
+text	/man/10/kproc
+text	/man/10/mk
+text	/man/10/ms2
+text	/man/10/odbc
+text	/man/10/print
+text	/man/2/0intro
+text	/man/2/alphabet-intro
+text	/man/2/asn1
+text	/man/2/bufio
+text	/man/2/convcs
+text	/man/2/csv
+text	/man/2/daytime
+text	/man/2/disks
+text	/man/2/draw-0intro
+text	/man/2/draw-display
+text	/man/2/draw-font
+text	/man/2/draw-image
+text	/man/2/encoding
+text	/man/2/ether
+text	/man/2/factotum
+text	/man/2/format
+text	/man/2/ip
+text	/man/2/json
+text	/man/2/keyring-certtostr
+text	/man/2/names
+text	/man/2/palmfile
+text	/man/2/plumbmsg
+text	/man/2/pop3
+text	/man/2/popup
+text	/man/2/prefab-0intro
+text	/man/2/prefab-compound
+text	/man/2/prefab-element
+text	/man/2/prefab-style
+text	/man/2/print
+text	/man/2/rfc822
+text	/man/2/secstore
+text	/man/2/sexprs
+text	/man/2/smtp
+text	/man/2/spki
+text	/man/2/spree-cardlib
+text	/man/2/string
+text	/man/2/styx
+text	/man/2/sys-fversion
+text	/man/2/sys-print
+text	/man/2/tk
+text	/man/2/translate
+text	/man/2/ubfa
+text	/man/2/venti
+text	/man/2/w3c-css
+text	/man/2/w3c-uris
+text	/man/2/w3c-xpointers
+text	/man/2/xml
+text	/man/3/dbg
+text	/man/3/draw
+text	/man/3/dynld
+text	/man/3/ether
+text	/man/3/fpga
+text	/man/3/ip
+text	/man/3/kprof
+text	/man/3/mpeg
+text	/man/3/plap
+text	/man/3/pnp
+text	/man/3/prog
+text	/man/3/sd
+text	/man/3/sign
+text	/man/3/ssl
+text	/man/3/tls
+text	/man/4/acme
+text	/man/4/dbfs
+text	/man/4/keyfs
+text	/man/4/keysrv
+text	/man/4/palmsrv
+text	/man/4/ramfile
+text	/man/4/registry
+text	/man/4/spree
+text	/man/5/0intro
+text	/man/5/stat
+text	/man/6/attrdb
+text	/man/6/audio
+text	/man/6/font
+text	/man/6/json
+text	/man/6/keytext
+text	/man/6/login
+text	/man/6/man
+text	/man/6/plumbing
+text	/man/6/sbl
+text	/man/6/sexprs
+text	/man/6/translate
+text	/man/6/utf
+text	/man/8/ai2key
+text	/man/8/applylog
+text	/man/8/collabsrv
+text	/man/8/cs
+text	/man/8/sntp
+text	/man/8/styxchat
+text	/man/9/0intro
+text	/man/9/button
+text	/man/9/canvas
+text	/man/9/checkbutton
+text	/man/9/choicebutton
+text	/man/9/entry
+text	/man/9/label
+text	/man/9/listbox
+text	/man/9/menu
+text	/man/9/menubutton
+text	/man/9/options
+text	/man/9/radiobutton
+text	/man/9/scrollbar
+text	/man/9/see
+text	/man/9/send
+text	/man/9/text
+text	/man/9/variable
+text's	/man/9/text
+textbg	/man/2/draw-image
+textbgop	/man/2/draw-image
+textbox	/man/2/prefab-compound
+textcolor	/man/2/prefab-style
+textels	/man/1/sh-sexprs
+textfd	/man/2/print
+textfile	/man/10/acid
+textfont	/man/2/prefab-style
+textlength	/man/2/palmfile
+textop	/man/2/draw-image
+texts	/man/2/security-0intro
+texts	/man/9/text
+textsize	/man/10/ksize
+textual	/man/1/acme
+textual	/man/1/mux
+textual	/man/2/asn1
+textual	/man/2/attrdb
+textual	/man/2/daytime
+textual	/man/2/draw-font
+textual	/man/2/encoding
+textual	/man/2/ether
+textual	/man/2/factotum
+textual	/man/2/format
+textual	/man/2/ip
+textual	/man/2/keyring-0intro
+textual	/man/2/keyring-certtostr
+textual	/man/2/keyset
+textual	/man/2/prefab-element
+textual	/man/2/scsiio
+textual	/man/2/secstore
+textual	/man/2/sexprs
+textual	/man/2/spki
+textual	/man/2/w3c-uris
+textual	/man/2/w3c-xpointers
+textual	/man/3/arch
+textual	/man/3/cmd
+textual	/man/3/cons
+textual	/man/3/dbg
+textual	/man/3/eia
+textual	/man/3/ether
+textual	/man/3/flash
+textual	/man/3/gpio
+textual	/man/3/ip
+textual	/man/3/pnp
+textual	/man/3/sd
+textual	/man/3/sign
+textual	/man/3/tls
+textual	/man/3/tv
+textual	/man/3/usb
+textual	/man/4/acme
+textual	/man/5/0intro
+textual	/man/6/image
+textual	/man/6/json
+textual	/man/6/keytext
+textual	/man/6/utf
+textual	/man/8/prep
+textual	/man/8/register
+textual	/man/8/styxchat
+textual	/man/9/button
+textual	/man/9/canvas
+textual	/man/9/checkbutton
+textual	/man/9/choicebutton
+textual	/man/9/label
+textual	/man/9/menu
+textual	/man/9/menubutton
+textual	/man/9/options
+textual	/man/9/radiobutton
+textual	/man/9/text
+textually	/man/1/acme
+textually	/man/2/ip
+textually	/man/2/keyring-ipint
+textually	/man/2/w3c-uris
+textually	/man/3/dbg
+textually	/man/3/pnp
+textually	/man/8/signer
+texture	/man/2/draw-example
+texture.clipr	/man/2/draw-example
+texture.draw	/man/2/draw-example
+textured	/man/2/draw-example
+tf	/man/6/man
+tfd	/man/10/plan9.ini
+tfd	/man/2/print
+tflush	/man/10/styx
+tflush	/man/5/0intro
+tflush	/man/5/flush
+tflush	/man/8/styxchat
+tftp	/man/2/dhcpclient
+tftp	/man/2/tftp
+tftp	/man/3/boot
+tftp	/man/8/bootpd
+tftp.b	/man/2/tftp
+tftpd	/man/8/bootpd
+tftpd	/man/8/mangaload
+tftpd.b	/man/8/bootpd
+th	/man/1/acme
+th	/man/1/m4
+th	/man/1/sh
+th	/man/1/sh-regex
+th	/man/1/sh-std
+th	/man/10/devattach
+th	/man/2/regex
+th	/man/2/spree-cardlib
+th	/man/6/colour
+th	/man/6/man
+th	/man/7/cddb
+th	/man/9/choicebutton
+th	/man/9/grid
+th	/man/9/text
+that's	/man/1/fs
+that's	/man/2/spree-cardlib
+thenceforth	/man/5/version
+theory.lcs.mit.edu	/man/2/sexprs
+there's	/man/1/sh-file2chan
+there's	/man/2/pslib
+there's	/man/4/lockfs
+there's	/man/6/man
+thereafter	/man/1/mash
+thereafter	/man/1/mash-make
+thereby	/man/1/acme
+thereby	/man/10/kproc
+thereby	/man/3/srv
+thereby	/man/4/0intro
+therefore	/man/1/asm
+therefore	/man/1/collab-clients
+therefore	/man/1/grid-session
+therefore	/man/1/netstat
+therefore	/man/1/sh
+therefore	/man/10/allocb
+therefore	/man/10/atoi
+therefore	/man/10/intrenable
+therefore	/man/10/qio
+therefore	/man/10/sleep
+therefore	/man/10/styx
+therefore	/man/10/styxserver
+therefore	/man/2/draw-image
+therefore	/man/2/geodesy
+therefore	/man/2/math-0intro
+therefore	/man/2/palmfile
+therefore	/man/2/security-0intro
+therefore	/man/2/sh
+therefore	/man/2/styxservers
+therefore	/man/2/sys-fd2path
+therefore	/man/2/tkclient
+therefore	/man/2/wmclient
+therefore	/man/3/ip
+therefore	/man/3/logfs
+therefore	/man/3/pipe
+therefore	/man/3/prof
+therefore	/man/3/usb
+therefore	/man/4/0intro
+therefore	/man/5/0intro
+therefore	/man/5/walk
+therefore	/man/6/colour
+therefore	/man/6/sexprs
+therefore	/man/6/ubfa
+therefore	/man/8/collabsrv
+therein	/man/1/gettar
+therein	/man/1/secstore
+therein	/man/10/allocb
+therein	/man/2/popup
+therein	/man/3/boot
+therein	/man/4/lockfs
+therein	/man/4/namespace
+thereof	/man/1/sh-alphabet
+thereof	/man/9/1copyright
+thick	/man/2/draw-image
+thick	/man/2/regex
+thick	/man/3/draw
+thickness	/man/2/draw-image
+thickness	/man/3/draw
+third	/man/1/cat
+third	/man/1/deb
+third	/man/1/m4
+third	/man/1/mash
+third	/man/1/uuencode
+third	/man/10/2c
+third	/man/10/5cv
+third	/man/10/plan9.ini
+third	/man/2/pop3
+third	/man/2/security-0intro
+third	/man/2/styxconv
+third	/man/3/kprof
+third	/man/3/pnp
+third	/man/4/acme
+third	/man/6/colour
+third	/man/8/changelogin
+third	/man/8/create
+third	/man/8/styxchat
+third	/man/9/canvas
+third	/man/9/listbox
+third	/man/9/menu
+third	/man/9/options
+third	/man/9/scrollbar
+third	/man/9/text
+thirteen	/man/5/0intro
+thorough	/man/2/styx
+thoughtful	/man/1/0intro
+thread	/man/1/deb
+thread	/man/1/emu
+thread	/man/1/ps
+thread	/man/1/sh-file2chan
+thread	/man/2/draw-display
+thread	/man/2/filter
+thread	/man/2/ir
+thread	/man/2/lock
+thread	/man/2/math-fp
+thread	/man/2/styxservers
+thread	/man/2/sys-0intro
+thread	/man/2/sys-bind
+thread	/man/2/sys-pctl
+thread	/man/2/sys-sleep
+thread	/man/6/dis
+threaded	/man/1/sh-file2chan
+threaded	/man/8/virgild
+threads	/man/1/deb
+threads	/man/1/tiny
+threads	/man/10/ntsrv
+threads	/man/2/sys-0intro
+threads	/man/2/sys-pctl
+threads	/man/2/sys-sleep
+threads	/man/6/dis
+threshold	/man/1/grid-register
+threshold	/man/2/spki
+threshold	/man/3/touch
+throughout	/man/1/0intro
+throughout	/man/3/logfs
+throughout	/man/6/utf
+throughout	/man/9/0intro
+thrown	/man/1/sh-string
+throws	/man/3/cons
+thumbprint	/man/2/keyset
+thumbprint	/man/2/registries
+thwart	/man/2/security-0intro
+tick	/man/10/kbdputc
+tick	/man/10/seconds
+tick	/man/3/kprof
+tick	/man/9/scale
+ticker	/man/1/mux
+ticket	/man/2/sys-fauth
+tickinterval	/man/9/scale
+tickle	/man/4/palmsrv
+ticks	/man/10/delay
+ticks	/man/10/seconds
+ticks	/man/3/kprof
+tidier	/man/2/json
+tidier	/man/2/ubfa
+tidies	/man/1/cleanname
+tied	/man/9/text
+tighter	/man/6/attrdb
+tightly	/man/10/plan9.ini
+tightly	/man/2/draw-image
+tightly	/man/2/prefab-element
+tilde	/man/1/strings
+tile	/man/1/wm-misc
+tile	/man/2/draw-image
+tiled	/man/2/draw-image
+tiles	/man/2/draw-example
+tiling	/man/2/draw-0intro
+tiling	/man/2/draw-image
+tim	/man/2/daytime
+time	/man/1/0intro
+time	/man/1/acme
+time	/man/1/alphabet-fs
+time	/man/1/bind
+time	/man/1/blur
+time	/man/1/charon
+time	/man/1/collab-clients
+time	/man/1/cp
+time	/man/1/date
+time	/man/1/deb
+time	/man/1/du
+time	/man/1/emu
+time	/man/1/fs
+time	/man/1/gettar
+time	/man/1/grid-monitor
+time	/man/1/grid-register
+time	/man/1/itest
+time	/man/1/limbo
+time	/man/1/ls
+time	/man/1/man
+time	/man/1/mash
+time	/man/1/math-misc
+time	/man/1/mk
+time	/man/1/mprof
+time	/man/1/prof
+time	/man/1/ps
+time	/man/1/sh
+time	/man/1/sh-file2chan
+time	/man/1/sh-std
+time	/man/1/sh-tk
+time	/man/1/time
+time	/man/1/timestamp
+time	/man/1/touch
+time	/man/1/units
+time	/man/1/webgrab
+time	/man/1/wm-misc
+time	/man/10/2c
+time	/man/10/2l
+time	/man/10/9load
+time	/man/10/allocb
+time	/man/10/ar
+time	/man/10/devattach
+time	/man/10/intrenable
+time	/man/10/kproc
+time	/man/10/kprof
+time	/man/10/mk
+time	/man/10/odbc
+time	/man/10/plan9.ini
+time	/man/10/rune
+time	/man/10/seconds
+time	/man/10/sleep
+time	/man/10/splhi
+time	/man/10/styxserver
+time	/man/2/0intro
+time	/man/2/asn1
+time	/man/2/daytime
+time	/man/2/dhcpclient
+time	/man/2/dis
+time	/man/2/draw-0intro
+time	/man/2/draw-image
+time	/man/2/filter-deflate
+time	/man/2/geodesy
+time	/man/2/ip
+time	/man/2/keyring-certtostr
+time	/man/2/keyring-crypt
+time	/man/2/keyring-sha1
+time	/man/2/lists
+time	/man/2/math-0intro
+time	/man/2/palmfile
+time	/man/2/prof
+time	/man/2/readdir
+time	/man/2/registries
+time	/man/2/rfc822
+time	/man/2/scsiio
+time	/man/2/secstore
+time	/man/2/security-0intro
+time	/man/2/sh
+time	/man/2/spki-verifier
+time	/man/2/spree
+time	/man/2/spree-cardlib
+time	/man/2/styxservers
+time	/man/2/sys-bind
+time	/man/2/sys-byte2char
+time	/man/2/sys-millisec
+time	/man/2/sys-sleep
+time	/man/2/sys-stat
+time	/man/2/tftp
+time	/man/2/w3c-css
+time	/man/2/wmsrv
+time	/man/3/boot
+time	/man/3/cmd
+time	/man/3/cons
+time	/man/3/ds
+time	/man/3/ip
+time	/man/3/pnp
+time	/man/3/pointer
+time	/man/3/prof
+time	/man/3/prog
+time	/man/3/rtc
+time	/man/3/srv
+time	/man/3/ssl
+time	/man/3/tinyfs
+time	/man/3/tls
+time	/man/4/dbfs
+time	/man/4/iostats
+time	/man/4/keyfs
+time	/man/4/registry
+time	/man/5/0intro
+time	/man/5/open
+time	/man/5/stat
+time	/man/6/dis
+time	/man/6/keys
+time	/man/6/keytext
+time	/man/6/ndb
+time	/man/6/utf
+time	/man/7/cddb
+time	/man/8/applylog
+time	/man/8/bootpd
+time	/man/8/changelogin
+time	/man/8/collabsrv
+time	/man/8/create
+time	/man/8/ping
+time	/man/8/prep
+time	/man/8/sntp
+time	/man/8/styxchat
+time	/man/8/touchcal
+time	/man/8/virgild
+time	/man/9/button
+time	/man/9/canvas
+time	/man/9/checkbutton
+time	/man/9/choicebutton
+time	/man/9/entry
+time	/man/9/grid
+time	/man/9/label
+time	/man/9/listbox
+time	/man/9/menu
+time	/man/9/menubutton
+time	/man/9/pack
+time	/man/9/panel
+time	/man/9/radiobutton
+time	/man/9/scale
+time	/man/9/scrollbar
+time	/man/9/text
+time.b	/man/1/time
+time2secs	/man/2/spki
+timed	/man/2/timers
+timefile	/man/8/applylog
+timekeeper	/man/3/rtc
+timeout	/man/10/plan9.ini
+timeout	/man/10/sleep
+timeout	/man/2/tftp
+timeout	/man/2/timers
+timeout	/man/3/vga
+timeout	/man/5/stat
+timeouts	/man/2/timers
+timeouts	/man/3/ip
+timer	/man/2/sys-millisec
+timer	/man/2/timers
+timer	/man/3/kprof
+timer	/man/3/sd
+timer.start	/man/2/timers
+timers	/man/2/timers
+timers.m	/man/2/timers
+times	/man/1/acme
+times	/man/1/ar
+times	/man/1/cprof
+times	/man/1/itest
+times	/man/1/mash-make
+times	/man/1/math-misc
+times	/man/1/mdb
+times	/man/1/timestamp
+times	/man/1/tiny
+times	/man/10/allocb
+times	/man/10/dev
+times	/man/10/iar
+times	/man/10/seconds
+times	/man/2/alphabet-intro
+times	/man/2/daytime
+times	/man/2/dbm
+times	/man/2/scsiio
+times	/man/2/security-0intro
+times	/man/2/timers
+times	/man/3/kprof
+times	/man/3/prof
+times	/man/3/tls
+times	/man/4/kfs
+times	/man/5/0intro
+times	/man/6/translate
+times	/man/7/cddb
+times	/man/8/changelogin
+times	/man/8/mkfs
+times	/man/9/text
+timestamp	/man/1/timestamp
+timestamp.b	/man/1/timestamp
+timestamps	/man/1/timestamp
+timetable	/man/1/mux
+timetable	/man/6/translate
+timezone	/man/1/date
+timezone	/man/2/daytime
+timezone	/man/4/namespace
+timing	/man/10/seconds
+timing	/man/10/sleep
+timing	/man/2/timers
+timings	/man/1/timestamp
+timings	/man/1/wm-misc
+tiny	/man/1/0intro
+tiny	/man/1/mash
+tiny	/man/1/rm
+tiny	/man/1/tiny
+tiny	/man/2/command
+tiny	/man/3/tinyfs
+tinyfs	/man/3/tinyfs
+tinyfs	/man/8/touchcal
+tip	/man/2/draw-image
+tip	/man/9/canvas
+title	/man/1/charon
+title	/man/1/cook
+title	/man/1/filename
+title	/man/1/grid-monitor
+title	/man/1/keyboard
+title	/man/1/logwindow
+title	/man/1/man
+title	/man/1/mash-tk
+title	/man/1/sh-tk
+title	/man/1/tktester
+title	/man/1/wm-sh
+title	/man/2/dialog
+title	/man/2/prefab-compound
+title	/man/2/prefab-element
+title	/man/2/prefab-style
+title	/man/2/selectfile
+title	/man/2/spree-cardlib
+title	/man/2/tkclient
+title	/man/2/wmclient
+title	/man/6/attrdb
+title	/man/7/db
+title1	/man/1/toolbar
+title2	/man/1/toolbar
+titlebar	/man/1/sh-tk
+titlebar	/man/1/tkcmd
+titlebar	/man/1/wish
+titlebar	/man/2/command
+titlebar	/man/2/tkclient
+titlebar	/man/2/wmclient
+titlecolor	/man/2/prefab-style
+titled	/man/1/wish
+titlefont	/man/2/prefab-style
+titles	/man/1/man
+tk	/man/1/0intro
+tk	/man/1/cpu
+tk	/man/1/emu
+tk	/man/1/filename
+tk	/man/1/keyboard
+tk	/man/1/limbo
+tk	/man/1/mash
+tk	/man/1/mash-tk
+tk	/man/1/mprof
+tk	/man/1/mux
+tk	/man/1/prof
+tk	/man/1/sh
+tk	/man/1/sh-expr
+tk	/man/1/sh-std
+tk	/man/1/sh-tk
+tk	/man/1/tkcmd
+tk	/man/1/tktester
+tk	/man/1/wish
+tk	/man/1/wm-sh
+tk	/man/2/0intro
+tk	/man/2/command
+tk	/man/2/dialog
+tk	/man/2/dividers
+tk	/man/2/draw-0intro
+tk	/man/2/draw-context
+tk	/man/2/draw-pointer
+tk	/man/2/keyring-0intro
+tk	/man/2/popup
+tk	/man/2/prefab-0intro
+tk	/man/2/selectfile
+tk	/man/2/sh
+tk	/man/2/spree-cardlib
+tk	/man/2/tabs
+tk	/man/2/tk
+tk	/man/2/tkclient
+tk	/man/2/wmclient
+tk	/man/2/wmlib
+tk	/man/4/namespace
+tk	/man/6/keyboard
+tk	/man/9/0intro
+tk	/man/9/1copyright
+tk	/man/9/bind
+tk	/man/9/button
+tk	/man/9/canvas
+tk	/man/9/checkbutton
+tk	/man/9/choicebutton
+tk	/man/9/entry
+tk	/man/9/focus
+tk	/man/9/frame
+tk	/man/9/grab
+tk	/man/9/image
+tk	/man/9/label
+tk	/man/9/listbox
+tk	/man/9/menu
+tk	/man/9/menubutton
+tk	/man/9/options
+tk	/man/9/panel
+tk	/man/9/radiobutton
+tk	/man/9/scale
+tk	/man/9/scrollbar
+tk	/man/9/send
+tk	/man/9/text
+tk	/man/9/types
+tk	/man/9/update
+tk	/man/9/variable
+tk's	/man/4/namespace
+tk's	/man/9/canvas
+tk's	/man/9/grab
+tk.b	/man/1/mash
+tk.b	/man/1/mash-tk
+tk.b	/man/1/sh-tk
+tk.c	/man/2/tk
+tk.m	/man/2/0intro
+tk.m	/man/2/tk
+tk.m	/man/2/wmclient
+tk2ms	/man/10/seconds
+tk2sec	/man/10/seconds
+tkarg	/man/1/tkcmd
+tkclient	/man/1/sh-tk
+tkclient	/man/1/toolbar
+tkclient	/man/1/wm
+tkclient	/man/2/draw-context
+tkclient	/man/2/tk
+tkclient	/man/2/tkclient
+tkclient	/man/2/wmlib
+tkclient	/man/2/wmsrv
+tkclient.b	/man/2/tkclient
+tkclient.m	/man/2/tkclient
+tkcmd	/man/1/sh-tk
+tkcmd	/man/1/tkcmd
+tkcmd	/man/1/wish
+tkcmd	/man/2/tk
+tkcmd	/man/9/0intro
+tkcmd	/man/9/button
+tkcmd.b	/man/1/tkcmd
+tkcmds	/man/2/translate
+tkill	/man/3/dbg
+tktester	/man/1/tktester
+tkwargs	/man/1/tktester
+tl	/man/1/mash
+tl	/man/1/sh-std
+tl	/man/2/arg
+tl	/man/2/debug
+tls	/man/3/tls
+tls	/man/4/factotum
+tls1	/man/3/tls
+tlsv1.0	/man/3/tls
+tm	/man/2/daytime
+tm	/man/2/styxflush
+tm2epoch	/man/2/daytime
+tmget	/man/3/dbg
+tmp	/man/1/alphabet-abc
+tmp	/man/1/alphabet-fs
+tmp	/man/1/alphabet-grid
+tmp	/man/1/ar
+tmp	/man/1/blur
+tmp	/man/1/diff
+tmp	/man/1/fs
+tmp	/man/1/grid-ns
+tmp	/man/1/mash
+tmp	/man/1/sh
+tmp	/man/1/sh-file2chan
+tmp	/man/1/sh-std
+tmp	/man/1/sh-test
+tmp	/man/1/uuencode
+tmp	/man/10/c2l
+tmp	/man/10/iar
+tmp	/man/10/plan9.ini
+tmp	/man/3/kprof
+tmp	/man/4/ftpfs
+tmp	/man/4/memfs
+tmp	/man/4/namespace
+tmp	/man/8/prep
+tmp	/man/8/rdbgsrv
+tmp.dis	/man/1/uuencode
+tmput	/man/3/dbg
+tmsg	/man/2/styx
+tmsg	/man/2/styxflush
+tmsg	/man/2/styxservers
+tmsg	/man/8/styxchat
+tmsg.attach	/man/2/styxservers
+tmsg.clunk	/man/2/styxservers
+tmsg.create	/man/2/styxservers
+tmsg.names	/man/2/styx
+tmsg.open	/man/2/styxservers
+tmsg.read	/man/2/styx
+tmsg.read	/man/2/styxservers
+tmsg.read	/man/2/styxservers-nametree
+tmsg.readerror	/man/2/styx
+tmsg.readerror	/man/2/styxflush
+tmsg.remove	/man/2/styxservers
+tmsg.stat	/man/2/styxservers
+tmsg.tag	/man/2/styx
+tmsg.unpack	/man/2/styx
+tmsg.version	/man/2/styx
+tmsg.walk	/man/2/styxservers
+tmsg.write	/man/2/styx
+tmsg.write	/man/2/styxservers
+tmsg.write	/man/2/styxservers-nametree
+tmsg.write	/man/8/styxchat
+tobig	/man/2/string
+today's	/man/1/mux
+todir	/man/1/cp
+todir	/man/1/mv
+todo	/man/1/blur
+toffset	/man/2/disks
+tofile	/man/1/cp
+tofile	/man/1/mv
+toggle	/man/1/grid-session
+toggle	/man/3/switch
+toggle	/man/3/usb
+toggle	/man/4/factotum
+toggle	/man/9/checkbutton
+toggled	/man/1/deb
+toggled	/man/1/tktester
+toggles	/man/3/switch
+toggles	/man/3/usb
+toggles	/man/9/checkbutton
+toggles	/man/9/listbox
+toggles	/man/9/menu
+toggling	/man/9/menu
+toint	/man/2/string
+tok	/man/2/rfc822
+token	/man/1/yacc
+token	/man/2/dial
+token	/man/2/rfc822
+token	/man/2/sexprs
+token	/man/2/sys-dial
+token	/man/6/sexprs
+tokenised	/man/2/filepat
+tokenization	/man/1/sh
+tokenize	/man/10/getfields
+tokenize	/man/10/parsecmd
+tokenize	/man/2/filepat
+tokenize	/man/2/sys-tokenize
+tokenize.c	/man/10/getfields
+tokens	/man/1/yacc
+tokens	/man/10/getfields
+tokens	/man/2/rfc822
+tokens	/man/2/sexprs
+tokens	/man/2/spree
+tokens	/man/2/wmsrv
+tokens	/man/3/cons
+tokens	/man/3/usb
+tokens	/man/6/namespace
+tokens	/man/6/sexprs
+toks	/man/2/spree-allow
+tolerance	/man/2/ida
+tolerant	/man/2/ida
+tolerate	/man/1/charon
+tolower	/man/1/sh-string
+tolower	/man/2/string
+tom	/man/1/mash
+tone	/man/1/miniterm
+tone	/man/6/colour
+tones	/man/2/draw-display
+tones	/man/6/colour
+tookit	/man/2/prefab-0intro
+tool	/man/1/cprof
+tool	/man/1/mprof
+tool	/man/1/prof
+tool	/man/1/wm-misc
+toolbar	/man/1/logon
+toolbar	/man/1/toolbar
+toolbar	/man/1/wm
+toolbar	/man/2/tkclient
+toolbar	/man/2/wmclient
+toolbar	/man/4/factotum
+toolbar.b	/man/1/toolbar
+toolkit	/man/1/wish
+toolkit	/man/2/ir
+toolkit	/man/2/prefab-0intro
+toolkit	/man/2/prefab-compound
+toolkit	/man/2/prefab-element
+toolkit	/man/2/prefab-environ
+toolkit	/man/2/prefab-style
+toolkit	/man/2/tk
+toolkit	/man/9/options
+tools	/man/1/0intro
+tools	/man/1/vacget
+tools	/man/10/odbc
+tools	/man/10/styxserver
+tools	/man/2/math-0intro
+tools	/man/4/namespace
+tools	/man/8/fpgaload
+toosmall	/man/4/factotum
+top.ctxt.ctl	/man/2/tkclient
+top.ctxt.kbd	/man/2/tkclient
+top.ctxt.ptr	/man/2/tkclient
+top.wreq	/man/2/tkclient
+topconfig	/man/2/tkclient
+topen	/man/10/styx
+topen	/man/5/0intro
+topen	/man/5/open
+topen	/man/8/styxchat
+topic	/man/1/cook
+toplev	/man/2/spki
+toplevel	/man/1/wish
+toplevel	/man/2/dialog
+toplevel	/man/2/dividers
+toplevel	/man/2/popup
+toplevel	/man/2/selectfile
+toplevel	/man/2/tabs
+toplevel	/man/2/tk
+toplevel	/man/2/tkclient
+toplevel	/man/9/bind
+topmost	/man/2/sets
+topmost	/man/9/canvas
+topmost	/man/9/menu
+topological	/man/1/tsort
+topological	/man/10/2l
+topp	/man/2/regex
+toreal	/man/2/string
+toret	/man/3/prog
+torn	/man/9/menu
+tos	/man/3/ip
+tostring	/man/2/asn1
+total	/man/1/charon
+total	/man/1/du
+total	/man/1/mash
+total	/man/1/time
+total	/man/10/dynld
+total	/man/10/kprof
+total	/man/10/ksize
+total	/man/10/xalloc
+total	/man/2/pop3
+total	/man/2/print
+total	/man/2/prof
+total	/man/3/cons
+total	/man/3/dynld
+total	/man/3/ftl
+total	/man/3/kprof
+total	/man/3/prog
+total	/man/4/factotum
+total	/man/4/iostats
+total	/man/5/stat
+total	/man/7/cddb
+total	/man/8/prep
+total	/man/9/canvas
+total	/man/9/listbox
+total	/man/9/options
+totally	/man/9/canvas
+totals	/man/1/wc
+totals	/man/2/prof
+touch	/man/1/charon
+touch	/man/1/ftree
+touch	/man/1/keyboard
+touch	/man/1/logon
+touch	/man/1/mk
+touch	/man/1/touch
+touch	/man/10/mk
+touch	/man/3/touch
+touch	/man/5/stat
+touch	/man/8/touchcal
+touch.b	/man/1/touch
+touchcal	/man/3/touch
+touchcal	/man/8/touchcal
+touchcal.b	/man/8/touchcal
+touchctl	/man/3/touch
+touchctl	/man/8/touchcal
+touched	/man/3/touch
+touched	/man/8/kfscmd
+touches	/man/1/emu
+touches	/man/2/draw-image
+touchstat	/man/3/touch
+toupper	/man/1/sh-string
+toupper	/man/2/string
+touser	/man/3/cap
+towards	/man/10/dev
+towards	/man/10/devattach
+towards	/man/2/geodesy
+towards	/man/9/scale
+towhom	/man/2/smtp
+tp	/man/3/cons
+tp	/man/6/man
+tproc	/man/3/dbg
+tr	/man/1/tr
+tr	/man/1/unicode
+tr	/man/1/wc
+tr	/man/2/w3c-css
+tr	/man/2/w3c-xpointers
+tr	/man/2/xml
+tr	/man/3/cons
+tr.b	/man/1/tr
+trace	/man/1/deb
+trace	/man/1/stack
+trace	/man/10/acid
+trace	/man/2/styxservers
+trace	/man/3/ftl
+trace	/man/3/logfs
+trace	/man/3/prog
+trace	/man/4/dossrv
+trace	/man/4/vacfs
+trace	/man/8/bootpd
+trace	/man/8/mangaload
+trace	/man/8/rdbgsrv
+traceback	/man/1/stack
+traces	/man/1/emu
+traces	/man/3/cons
+traceset	/man/2/styxservers
+tracing	/man/2/dhcpclient
+tracing	/man/2/styx
+tracing	/man/2/ubfa
+tracing	/man/3/ftl
+tracing	/man/3/ip
+tracing	/man/3/logfs
+tracing	/man/4/keyfs
+track	/man/1/blur
+track	/man/2/disks
+track	/man/4/dossrv
+track	/man/4/spree
+track	/man/7/cddb
+track	/man/8/plumber
+track	/man/8/prep
+track	/man/9/menu
+track	/man/9/text
+track's	/man/7/cddb
+track0id	/man/7/cddb
+trackballs	/man/2/draw-0intro
+tracknid	/man/7/cddb
+tracks	/man/7/cddb
+trademark	/man/6/man
+traditional	/man/10/plan9.ini
+traditional	/man/2/arg
+traditional	/man/2/security-0intro
+traditionally	/man/1/sh
+traditionally	/man/2/arg
+traffic	/man/3/ip
+traffic	/man/3/pbus
+traffic	/man/4/iostats
+traffic	/man/8/bootpd
+trailer	/man/1/man
+trailers	/man/10/allocb
+trailers	/man/2/secstore
+trailers	/man/3/tls
+trans	/man/10/odbc
+transa	/man/2/math-linalg
+transaction	/man/10/odbc
+transaction	/man/3/usb
+transaction	/man/5/0intro
+transaction	/man/5/attach
+transaction	/man/5/error
+transaction	/man/5/flush
+transaction	/man/5/stat
+transaction	/man/7/db
+transactions	/man/10/odbc
+transactions	/man/3/usb
+transactions	/man/4/ftpfs
+transactions	/man/4/iostats
+transation	/man/10/odbc
+transb	/man/2/math-linalg
+transcript	/man/1/collab-clients
+transfer	/man/10/dmainit
+transfer	/man/10/error
+transfer	/man/10/inb
+transfer	/man/2/keyring-0intro
+transfer	/man/2/rfc822
+transfer	/man/2/smtp
+transfer	/man/2/spree
+transfer	/man/2/spree-cardlib
+transfer	/man/2/sys-iounit
+transfer	/man/2/tftp
+transfer	/man/3/boot
+transfer	/man/3/i2c
+transfer	/man/3/ip
+transfer	/man/3/sd
+transfer	/man/4/ftpfs
+transfer	/man/4/spree
+transfer	/man/5/open
+transfer	/man/5/read
+transfer	/man/8/bootpd
+transferred	/man/1/sh
+transferred	/man/1/sh-std
+transferred	/man/10/dmainit
+transferred	/man/10/dynld
+transferred	/man/10/readnum
+transferred	/man/2/spree
+transferred	/man/3/mnt
+transferred	/man/3/tv
+transferred	/man/4/iostats
+transferred	/man/4/spree
+transferred	/man/5/read
+transfers	/man/1/webgrab
+transfers	/man/10/9load
+transfers	/man/10/dmainit
+transfers	/man/2/spree
+transfers	/man/3/ip
+transfers	/man/8/bootpd
+transform	/man/2/keyring-0intro
+transformation	/man/2/devpointer
+transformation	/man/2/geodesy
+transformation	/man/2/math-export
+transformation	/man/2/secstore
+transformation	/man/3/touch
+transformation	/man/6/utf
+transformation	/man/8/touchcal
+transformations	/man/1/sh-alphabet
+transformations	/man/10/c2l
+transformations	/man/2/geodesy
+transformations	/man/9/canvas
+transformed	/man/2/lists
+transient	/man/2/draw-screen
+transient	/man/2/spree
+transition	/man/2/debug
+transitions	/man/3/prog
+translate	/man/1/tcs
+translate	/man/1/tr
+translate	/man/2/dial
+translate	/man/2/draw-image
+translate	/man/2/ir
+translate	/man/2/prefab-element
+translate	/man/2/security-0intro
+translate	/man/2/security-login
+translate	/man/2/styxservers
+translate	/man/2/sys-dial
+translate	/man/2/translate
+translate	/man/4/trfs
+translate	/man/6/namespace
+translate	/man/6/translate
+translate	/man/8/cs
+translate	/man/8/prep
+translate.b	/man/2/translate
+translate.m	/man/2/translate
+translated	/man/1/bind
+translated	/man/1/secstore
+translated	/man/10/c2l
+translated	/man/2/srv
+translated	/man/2/sys-bind
+translated	/man/3/cons
+translated	/man/3/draw
+translated	/man/3/mnt
+translated	/man/5/0intro
+translated	/man/8/cs
+translated	/man/8/virgild
+translates	/man/1/asm
+translates	/man/1/bind
+translates	/man/1/unicode
+translates	/man/10/c2l
+translates	/man/2/alphabet-intro
+translates	/man/2/prefab-element
+translates	/man/2/sys-byte2char
+translates	/man/3/mnt
+translates	/man/7/dbsrv
+translates	/man/8/virgild
+translating	/man/10/c2l
+translating	/man/2/prefab-element
+translating	/man/2/security-0intro
+translating	/man/8/virgild
+translation	/man/1/yacc
+translation	/man/10/c2l
+translation	/man/10/styx
+translation	/man/2/alphabet-intro
+translation	/man/2/dial
+translation	/man/2/draw-display
+translation	/man/2/secstore
+translation	/man/2/srv
+translation	/man/2/sys-0intro
+translation	/man/2/sys-bind
+translation	/man/2/sys-byte2char
+translation	/man/2/sys-dial
+translation	/man/2/translate
+translation	/man/3/ftl
+translation	/man/6/translate
+translation	/man/8/cs
+translation	/man/8/dns
+translation	/man/8/ftl
+translation	/man/8/init
+translations	/man/2/translate
+translations	/man/6/translate
+translations	/man/8/cs
+translator	/man/1/yacc
+translator	/man/10/c2l
+translator	/man/2/alphabet-intro
+translator	/man/4/ftpfs
+translit	/man/1/m4
+transliterates	/man/1/m4
+transmission	/man/1/uuencode
+transmission	/man/10/qio
+transmission	/man/2/keyring-ipint
+transmission	/man/2/plumbmsg
+transmission	/man/8/styxchat
+transmissions	/man/2/keyring-rc4
+transmit	/man/1/uuencode
+transmit	/man/10/plan9.ini
+transmit	/man/2/filter-deflate
+transmit	/man/3/ip
+transmits	/man/2/styx
+transmits	/man/3/i2c
+transmits	/man/3/pbus
+transmits	/man/5/0intro
+transmits	/man/8/rip
+transmitted	/man/1/idea
+transmitted	/man/1/sh-file2chan
+transmitted	/man/10/print
+transmitted	/man/2/ida
+transmitted	/man/2/json
+transmitted	/man/2/keyring-0intro
+transmitted	/man/2/keyring-getmsg
+transmitted	/man/2/msgio
+transmitted	/man/2/plumbmsg
+transmitted	/man/2/scsiio
+transmitted	/man/2/sys-iounit
+transmitted	/man/2/sys-print
+transmitted	/man/2/ubfa
+transmitted	/man/3/dbg
+transmitted	/man/3/draw
+transmitted	/man/3/ip
+transmitted	/man/3/mnt
+transmitted	/man/3/pbus
+transmitted	/man/3/usb
+transmitted	/man/5/0intro
+transmitted	/man/5/walk
+transmitted	/man/8/collabsrv
+transmitted	/man/8/styxchat
+transmitting	/man/2/security-0intro
+transmitting	/man/2/sys-file2chan
+transmitting	/man/3/ip
+transmitting	/man/5/0intro
+transparency	/man/2/draw-image
+transparency	/man/9/image
+transparency	/man/9/types
+transparent	/man/2/draw-display
+transparent	/man/2/draw-example
+transparent	/man/2/draw-image
+transparent	/man/6/colour
+transparent	/man/6/dis
+transparent	/man/9/canvas
+transparent	/man/9/image
+transparent	/man/9/types
+transport	/man/1/sendmail
+transport	/man/10/allocb
+transport	/man/10/qio
+transport	/man/2/keyring-getmsg
+transport	/man/2/msgio
+transport	/man/2/sexprs
+transport	/man/2/smtp
+transport	/man/2/ubfa
+transport	/man/3/ip
+transport	/man/3/tls
+transport	/man/4/export
+transport	/man/5/version
+transport	/man/6/json
+transport	/man/6/keytext
+transport	/man/6/sexprs
+transport	/man/6/ubfa
+transported	/man/5/0intro
+transposition	/man/2/math-linalg
+transverse	/man/2/geodesy
+trap	/man/1/emu
+trap	/man/2/math-fp
+trap	/man/3/arch
+trap	/man/3/dbg
+trap.c	/man/10/intrenable
+trap.c	/man/3/dbg
+traps	/man/1/emu
+traps	/man/10/dev
+traveling	/man/3/tls
+traversal	/man/1/alphabet-fs
+traversal	/man/1/fs
+traversal	/man/9/menu
+traversal	/man/9/menubutton
+traversal	/man/9/options
+traverse	/man/2/dbm
+traversed	/man/10/devattach
+traversed	/man/2/styxservers-nametree
+traverses	/man/2/fsproto
+traversing	/man/1/alphabet-fs
+traversing	/man/1/fs
+traversing	/man/3/pnp
+traversing	/man/5/walk
+tread	/man/10/styx
+tread	/man/10/styxserver
+tread	/man/2/sys-file2chan
+tread	/man/5/0intro
+tread	/man/5/read
+tread	/man/8/styxchat
+treat	/man/1/acme
+treat	/man/1/keyboard
+treat	/man/1/sh-expr
+treat	/man/2/attrdb
+treat	/man/2/prefab-0intro
+treat	/man/3/draw
+treatment	/man/1/mk
+treatment	/man/10/mk
+treatment	/man/2/xml
+treatment	/man/6/man
+treatment	/man/6/sbl
+treats	/man/1/sh-csv
+treble	/man/3/tv
+tree	/man/1/0intro
+tree	/man/1/ftree
+tree	/man/1/gettar
+tree	/man/1/grid-query
+tree	/man/1/grid-session
+tree	/man/1/itest
+tree	/man/1/wm-misc
+tree	/man/10/conf
+tree	/man/10/dev
+tree	/man/10/devattach
+tree	/man/10/master
+tree	/man/10/ntsrv
+tree	/man/10/styxserver
+tree	/man/2/disks
+tree	/man/2/math-0intro
+tree	/man/2/sexprs
+tree	/man/2/spree
+tree	/man/2/styxservers
+tree	/man/2/styxservers-nametree
+tree	/man/2/sys-0intro
+tree	/man/2/sys-bind
+tree	/man/2/ubfa
+tree	/man/2/venti
+tree	/man/2/w3c-xpointers
+tree	/man/2/xml
+tree	/man/3/0intro
+tree	/man/3/fs
+tree	/man/3/root
+tree	/man/4/import
+tree	/man/4/namespace
+tree	/man/4/trfs
+tree	/man/5/0intro
+tree	/man/5/attach
+tree	/man/6/namespace
+tree	/man/6/proto
+tree	/man/6/sexprs
+tree	/man/8/applylog
+tree	/man/8/collabsrv
+tree	/man/8/create
+tree	/man/8/mkfs
+tree	/man/9/grab
+tree	/man/9/grid
+tree	/man/9/pack
+tree.create	/man/2/styxservers-nametree
+tree.quit	/man/2/styxservers-nametree
+treeop	/man/2/styxservers-nametree
+trees	/man/1/bind
+trees	/man/2/sexprs
+trees	/man/2/spki
+trees	/man/2/styxservers
+trees	/man/2/sys-bind
+trees	/man/2/sys-pctl
+trees	/man/4/namespace
+trees	/man/5/0intro
+trees	/man/8/applylog
+tremove	/man/5/0intro
+tremove	/man/5/remove
+tremove	/man/8/styxchat
+trfs	/man/4/trfs
+trfs.b	/man/4/trfs
+tricky	/man/2/xml
+tried	/man/10/plan9.ini
+tried	/man/2/dial
+tried	/man/2/sys-dial
+tried	/man/3/fs
+tries	/man/1/charon
+tries	/man/1/listen
+tries	/man/1/sh
+tries	/man/10/c2l
+tries	/man/10/qlock
+tries	/man/2/dial
+tries	/man/2/dict
+tries	/man/2/scsiio
+tries	/man/2/styxpersist
+tries	/man/2/sys-dial
+tries	/man/2/tkclient
+tries	/man/2/venti
+tries	/man/2/wmclient
+tries	/man/3/tls
+trigger	/man/2/tk
+trigger	/man/5/stat
+trigger	/man/9/canvas
+triggered	/man/1/mk
+triggered	/man/10/intrenable
+triggered	/man/10/mk
+triggered	/man/2/debug
+triggers	/man/2/prefab-compound
+triggers	/man/9/text
+trigonometric	/man/1/calc
+trigonometric	/man/2/math-elem
+trimblock	/man/10/allocb
+trimmed	/man/1/dd
+trimmed	/man/1/sh-string
+trimmed	/man/3/i2c
+trimming	/man/10/parsecmd
+trims	/man/10/allocb
+trip	/man/1/webgrab
+trip	/man/8/ping
+triple	/man/2/draw-display
+triple	/man/2/w3c-css
+triple	/man/2/w3c-uris
+triple	/man/6/colour
+triplets	/man/2/draw-0intro
+trivial	/man/2/tftp
+trivial	/man/6/colour
+trivial	/man/8/httpd
+trnote	/man/3/dbg
+troff	/man/1/man
+troff	/man/6/man
+trouble	/man/1/diff
+troublesome	/man/10/plan9.ini
+troublesome	/man/6/sexprs
+trough	/man/9/scale
+trough	/man/9/scrollbar
+trough1	/man/9/scale
+trough1	/man/9/scrollbar
+trough2	/man/9/scale
+trough2	/man/9/scrollbar
+trtod.c	/man/10/strcat
+truemobile	/man/10/plan9.ini
+truly	/man/3/prog
+truncate	/man/1/tiny
+truncate	/man/2/dbm
+truncate	/man/2/sys-open
+truncate	/man/5/error
+truncate	/man/5/open
+truncated	/man/1/sh-std
+truncated	/man/2/draw-image
+truncated	/man/2/keyring-getstring
+truncated	/man/2/msgio
+truncated	/man/2/sys-open
+truncated	/man/3/tinyfs
+truncated	/man/4/ramfile
+truncated	/man/5/0intro
+truncated	/man/5/open
+truncated	/man/9/0intro
+truncates	/man/2/bufio
+truncates	/man/4/ramfile
+truncating	/man/10/seconds
+truncating	/man/10/strcat
+truncating	/man/2/sh
+truncating	/man/5/0intro
+truncation	/man/5/open
+truncation	/man/5/stat
+trust	/man/1/mk
+trust	/man/10/mk
+trust	/man/2/keyring-0intro
+trust	/man/2/security-0intro
+trusted	/man/1/listen
+trusted	/man/2/keyring-0intro
+trusts	/man/2/security-0intro
+truth	/man/10/acid
+try	/man/1/alphabet-fs
+try	/man/1/bind
+try	/man/1/cal
+try	/man/1/ebook
+try	/man/1/fs
+try	/man/1/passwd
+try	/man/1/sh
+try	/man/1/sh-alphabet
+try	/man/1/vacget
+try	/man/2/debug
+try	/man/2/dhcpclient
+try	/man/2/dial
+try	/man/2/ir
+try	/man/2/registries
+try	/man/2/scsiio
+try	/man/2/styx
+try	/man/2/tkclient
+try	/man/2/wmclient
+try	/man/5/open
+try	/man/8/cs
+trying	/man/1/sh
+trying	/man/2/styxservers
+trying	/man/3/cmd
+trying	/man/4/namespace
+ts	/man/2/attrdb
+ts	/man/3/cons
+ts	/man/6/man
+tsize	/man/2/dis
+tsleep	/man/10/delay
+tsleep	/man/10/sleep
+tsort	/man/1/tsort
+tsort.b	/man/1/tsort
+tst	/man/2/styxservers-nametree
+tstart	/man/3/dbg
+tstartstop	/man/3/dbg
+tstat	/man/5/0intro
+tstat	/man/5/stat
+tstat	/man/8/styxchat
+tstatus	/man/3/dbg
+tstop	/man/3/dbg
+ttc	/man/10/plan9.ini
+ttl	/man/2/ip
+ttl	/man/3/ip
+tune	/man/1/calc
+tuning	/man/3/tv
+tuple	/man/1/sh-string
+tuple	/man/2/alphabet-intro
+tuple	/man/2/asn1
+tuple	/man/2/attrdb
+tuple	/man/2/bufio-chanfill
+tuple	/man/2/cfg
+tuple	/man/2/complete
+tuple	/man/2/convcs
+tuple	/man/2/debug
+tuple	/man/2/dhcpclient
+tuple	/man/2/dict
+tuple	/man/2/dis
+tuple	/man/2/draw-context
+tuple	/man/2/exception
+tuple	/man/2/factotum
+tuple	/man/2/format
+tuple	/man/2/fsproto
+tuple	/man/2/geodesy
+tuple	/man/2/ida
+tuple	/man/2/imagefile
+tuple	/man/2/ip
+tuple	/man/2/json
+tuple	/man/2/keyring-getstring
+tuple	/man/2/keyset
+tuple	/man/2/lists
+tuple	/man/2/math-elem
+tuple	/man/2/math-fp
+tuple	/man/2/msgio
+tuple	/man/2/palmfile
+tuple	/man/2/plumbmsg
+tuple	/man/2/pop3
+tuple	/man/2/prefab-compound
+tuple	/man/2/readdir
+tuple	/man/2/regex
+tuple	/man/2/registries
+tuple	/man/2/rfc822
+tuple	/man/2/secstore
+tuple	/man/2/security-ssl
+tuple	/man/2/sexprs
+tuple	/man/2/sh
+tuple	/man/2/spki
+tuple	/man/2/spki-verifier
+tuple	/man/2/spree-allow
+tuple	/man/2/string
+tuple	/man/2/stringinttab
+tuple	/man/2/styx
+tuple	/man/2/styxflush
+tuple	/man/2/styxpersist
+tuple	/man/2/styxservers
+tuple	/man/2/styxservers-nametree
+tuple	/man/2/sys-byte2char
+tuple	/man/2/sys-dial
+tuple	/man/2/sys-dirread
+tuple	/man/2/sys-file2chan
+tuple	/man/2/sys-fversion
+tuple	/man/2/sys-stat
+tuple	/man/2/sys-tokenize
+tuple	/man/2/tkclient
+tuple	/man/2/translate
+tuple	/man/2/ubfa
+tuple	/man/2/venti
+tuple	/man/2/w3c-css
+tuple	/man/2/w3c-uris
+tuple	/man/2/w3c-xpointers
+tuple	/man/2/wait
+tuple	/man/2/wmlib
+tuple	/man/2/wmsrv
+tuple	/man/2/xml
+tuple	/man/4/factotum
+tuple	/man/6/sbl
+tuple	/man/6/ubfa
+tuple	/man/7/db
+tuple's	/man/6/sbl
+tuples	/man/1/limbo
+tuples	/man/2/attrdb
+tuples	/man/2/cfg
+tuples	/man/2/convcs
+tuples	/man/2/keyset
+tuples	/man/2/lists
+tuples	/man/2/pop3
+tuples	/man/2/registries
+tuples	/man/2/rfc822
+tuples	/man/2/sh
+tuples	/man/2/sys-0intro
+tuples	/man/2/sys-file2chan
+tuples	/man/2/ubfa
+tuples	/man/2/w3c-css
+tuples	/man/2/w3c-xpointers
+tuples	/man/4/factotum
+tuples	/man/6/ubfa
+turning	/man/1/acme
+turning	/man/1/emu
+turning	/man/1/wm-sh
+turning	/man/10/styx
+turning	/man/2/spree-cardlib
+turning	/man/9/text
+turns	/man/1/sh-alphabet
+turns	/man/1/wm-sh
+turns	/man/10/plan9.ini
+turns	/man/2/draw-image
+turns	/man/2/xml
+turns	/man/3/cons
+turns	/man/4/acme
+turns	/man/4/factotum
+turns	/man/6/ubfa
+turns	/man/9/text
+tutorial	/man/9/0intro
+tv	/man/1/mux
+tv	/man/2/prefab-0intro
+tv	/man/3/tv
+tv.h	/man/3/tv
+tvctl	/man/3/tv
+tversion	/man/10/styx
+tversion	/man/2/styx
+tversion	/man/5/0intro
+tversion	/man/5/version
+tversion	/man/8/styxchat
+tvlist	/man/1/mux
+tvp3020hwgc	/man/3/vga
+tvp3026hwgc	/man/3/vga
+twaitstop	/man/3/dbg
+twalk	/man/10/styx
+twalk	/man/5/0intro
+twalk	/man/5/flush
+twalk	/man/5/walk
+twalk	/man/8/styxchat
+twelve	/man/1/cal
+twelve	/man/2/ether
+twelve	/man/3/cons
+twelve	/man/3/ether
+twelve	/man/6/image
+twice	/man/1/ar
+twice	/man/1/charon
+twice	/man/10/iar
+twice	/man/2/cfg
+twice	/man/5/stat
+twice	/man/8/bootpd
+twice	/man/9/grid
+twice	/man/9/pack
+two's	/man/1/sh-expr
+two's	/man/3/audio
+two's	/man/6/dis
+twrite	/man/10/styx
+twrite	/man/2/sys-file2chan
+twrite	/man/5/0intro
+twrite	/man/5/read
+twrite	/man/8/styxchat
+twstat	/man/10/styx
+twstat	/man/5/0intro
+twstat	/man/5/stat
+twstat	/man/8/styxchat
+tx	/man/10/plan9.ini
+tx	/man/3/cons
+tx	/man/3/pbus
+tx	/man/4/spree
+txfd	/man/10/plan9.ini
+txkey	/man/10/plan9.ini
+type	/man/1/acme
+type	/man/1/alphabet-abc
+type	/man/1/alphabet-fs
+type	/man/1/alphabet-grid
+type	/man/1/alphabet-main
+type	/man/1/bind
+type	/man/1/brutus
+type	/man/1/charon
+type	/man/1/cook
+type	/man/1/deb
+type	/man/1/fs
+type	/man/1/grid-session
+type	/man/1/limbo
+type	/man/1/ls
+type	/man/1/mash
+type	/man/1/plumb
+type	/man/1/sh-alphabet
+type	/man/1/tktester
+type	/man/1/yacc
+type	/man/10/2c
+type	/man/10/2l
+type	/man/10/5cv
+type	/man/10/9load
+type	/man/10/a.out
+type	/man/10/acid
+type	/man/10/atoi
+type	/man/10/c2l
+type	/man/10/dev
+type	/man/10/devattach
+type	/man/10/dynld
+type	/man/10/intrenable
+type	/man/10/kproc
+type	/man/10/newchan
+type	/man/10/odbc
+type	/man/10/plan9.ini
+type	/man/10/print
+type	/man/10/styx
+type	/man/10/styxserver
+type	/man/2/alphabet-intro
+type	/man/2/asn1
+type	/man/2/bufio
+type	/man/2/convcs
+type	/man/2/dbm
+type	/man/2/debug
+type	/man/2/dhcpclient
+type	/man/2/dialog
+type	/man/2/dis
+type	/man/2/diskblocks
+type	/man/2/disks
+type	/man/2/draw-0intro
+type	/man/2/draw-context
+type	/man/2/draw-display
+type	/man/2/draw-font
+type	/man/2/draw-image
+type	/man/2/draw-point
+type	/man/2/draw-rect
+type	/man/2/filter
+type	/man/2/filter-deflate
+type	/man/2/filter-slip
+type	/man/2/format
+type	/man/2/fsproto
+type	/man/2/hash
+type	/man/2/imagefile
+type	/man/2/json
+type	/man/2/keyring-sha1
+type	/man/2/lists
+type	/man/2/palmfile
+type	/man/2/plumbmsg
+type	/man/2/prefab-0intro
+type	/man/2/prefab-compound
+type	/man/2/prefab-element
+type	/man/2/prefab-environ
+type	/man/2/prefab-style
+type	/man/2/print
+type	/man/2/prof
+type	/man/2/registries
+type	/man/2/rfc822
+type	/man/2/selectfile
+type	/man/2/sexprs
+type	/man/2/sh
+type	/man/2/spree
+type	/man/2/spree-cardlib
+type	/man/2/styx
+type	/man/2/styxservers
+type	/man/2/sys-0intro
+type	/man/2/sys-dup
+type	/man/2/sys-file2chan
+type	/man/2/sys-print
+type	/man/2/sys-self
+type	/man/2/sys-stat
+type	/man/2/ubfa
+type	/man/2/venti
+type	/man/2/w3c-css
+type	/man/2/w3c-xpointers
+type	/man/2/xml
+type	/man/3/arch
+type	/man/3/dbg
+type	/man/3/draw
+type	/man/3/dynld
+type	/man/3/ether
+type	/man/3/flash
+type	/man/3/floppy
+type	/man/3/fs
+type	/man/3/indir
+type	/man/3/ip
+type	/man/3/pnp
+type	/man/3/prog
+type	/man/3/sd
+type	/man/3/tv
+type	/man/3/vga
+type	/man/4/acme
+type	/man/4/iostats
+type	/man/4/namespace
+type	/man/4/spree
+type	/man/5/0intro
+type	/man/5/attach
+type	/man/5/open
+type	/man/5/stat
+type	/man/6/dis
+type	/man/6/json
+type	/man/6/keyboard
+type	/man/6/keytext
+type	/man/6/man
+type	/man/6/sbl
+type	/man/8/collabsrv
+type	/man/8/dns
+type	/man/8/httpd
+type	/man/8/mangaload
+type	/man/8/prep
+type	/man/8/styxchat
+type	/man/9/0intro
+type	/man/9/canvas
+type	/man/9/image
+type	/man/9/menu
+type	/man/9/types
+type	/man/9/variable
+type1	/man/2/asn1
+type2	/man/2/asn1
+type3	/man/2/asn1
+type9	/man/2/disks
+typeable	/man/10/kbdputc
+typechecking	/man/1/sh-alphabet
+typed	/man/1/acme
+typed	/man/1/ebook
+typed	/man/1/mash-tk
+typed	/man/1/mdb
+typed	/man/1/miniterm
+typed	/man/1/sh
+typed	/man/1/sh-alphabet
+typed	/man/1/tkcmd
+typed	/man/1/wm
+typed	/man/1/wm-sh
+typed	/man/10/9load
+typed	/man/10/c2l
+typed	/man/2/alphabet-intro
+typed	/man/2/dialog
+typed	/man/2/keyring-0intro
+typed	/man/2/sys-pipe
+typed	/man/3/cons
+typed	/man/6/keyboard
+typed	/man/9/entry
+typed	/man/9/text
+typedef	/man/10/2c
+typedef	/man/10/a.out
+typedef	/man/10/allocb
+typedef	/man/10/devattach
+typedef	/man/10/dynld
+typedef	/man/10/newchan
+typedef	/man/10/parsecmd
+typedef	/man/10/ref
+typedef	/man/10/styx
+typedef	/man/10/styxserver
+typedefs	/man/10/2c
+types	/man/1/alphabet-fs
+types	/man/1/alphabet-main
+types	/man/1/charon
+types	/man/1/collab-clients
+types	/man/1/fs
+types	/man/1/limbo
+types	/man/1/mk
+types	/man/1/sh-alphabet
+types	/man/1/wm-misc
+types	/man/1/yacc
+types	/man/10/2c
+types	/man/10/2l
+types	/man/10/a.out
+types	/man/10/acid
+types	/man/10/c2l
+types	/man/10/conf
+types	/man/10/mk
+types	/man/10/plan9.ini
+types	/man/10/print
+types	/man/10/styx
+types	/man/2/0intro
+types	/man/2/alphabet-intro
+types	/man/2/asn1
+types	/man/2/cfg
+types	/man/2/debug
+types	/man/2/dialog
+types	/man/2/dis
+types	/man/2/draw-context
+types	/man/2/draw-display
+types	/man/2/imagefile
+types	/man/2/ip
+types	/man/2/keyring-0intro
+types	/man/2/lists
+types	/man/2/palmfile
+types	/man/2/prefab-0intro
+types	/man/2/prefab-element
+types	/man/2/prefab-environ
+types	/man/2/print
+types	/man/2/rfc822
+types	/man/2/security-0intro
+types	/man/2/spki
+types	/man/2/styx
+types	/man/2/sys-print
+types	/man/2/sys-self
+types	/man/2/tk
+types	/man/2/tkclient
+types	/man/2/w3c-xpointers
+types	/man/3/draw
+types	/man/3/dynld
+types	/man/3/ether
+types	/man/3/flash
+types	/man/3/ip
+types	/man/4/spree
+types	/man/6/dis
+types	/man/6/keytext
+types	/man/6/sbl
+types	/man/6/sexprs
+types	/man/6/ubfa
+types	/man/8/prep
+types	/man/8/rdbgsrv
+types	/man/8/styxchat
+types	/man/9/0intro
+types	/man/9/bind
+types	/man/9/button
+types	/man/9/canvas
+types	/man/9/checkbutton
+types	/man/9/choicebutton
+types	/man/9/cursor
+types	/man/9/entry
+types	/man/9/frame
+types	/man/9/grid
+types	/man/9/image
+types	/man/9/label
+types	/man/9/listbox
+types	/man/9/menu
+types	/man/9/menubutton
+types	/man/9/options
+types	/man/9/pack
+types	/man/9/panel
+types	/man/9/radiobutton
+types	/man/9/scale
+types	/man/9/scrollbar
+types	/man/9/see
+types	/man/9/text
+types	/man/9/types
+typeset	/man/1/alphabet-abc
+typeset	/man/1/alphabet-fs
+typeset	/man/1/alphabet-grid
+typeset	/man/1/alphabet-main
+typeset	/man/1/sh-alphabet
+typeset	/man/2/alphabet-intro
+typeset's	/man/2/alphabet-intro
+typesets	/man/1/sh-alphabet
+typesets	/man/2/alphabet-intro
+typex	/man/10/c2l
+typex	/man/2/dis
+typical	/man/10/error
+typical	/man/10/plan9.ini
+typical	/man/10/styxserver
+typically	/man/1/acme
+typically	/man/1/calc
+typically	/man/1/cprof
+typically	/man/1/disdep
+typically	/man/1/ftree
+typically	/man/1/m4
+typically	/man/1/mash-make
+typically	/man/1/mk
+typically	/man/1/rcmd
+typically	/man/1/sleep
+typically	/man/1/strings
+typically	/man/1/tiny
+typically	/man/1/zeros
+typically	/man/10/2c
+typically	/man/10/2l
+typically	/man/10/9load
+typically	/man/10/conf
+typically	/man/10/dev
+typically	/man/10/devattach
+typically	/man/10/intrenable
+typically	/man/10/kbdputc
+typically	/man/10/mk
+typically	/man/10/newchan
+typically	/man/10/plan9.ini
+typically	/man/10/qio
+typically	/man/10/styxserver
+typically	/man/2/bufio
+typically	/man/2/command
+typically	/man/2/draw-0intro
+typically	/man/2/draw-context
+typically	/man/2/draw-image
+typically	/man/2/drawmux
+typically	/man/2/factotum
+typically	/man/2/keyring-certtostr
+typically	/man/2/newns
+typically	/man/2/palmfile
+typically	/man/2/popup
+typically	/man/2/prefab-compound
+typically	/man/2/registries
+typically	/man/2/sexprs
+typically	/man/2/srv
+typically	/man/2/styx
+typically	/man/2/styxservers
+typically	/man/2/sys-fd2path
+typically	/man/2/sys-pctl
+typically	/man/2/sys-pipe
+typically	/man/2/sys-self
+typically	/man/2/tftp
+typically	/man/3/cons
+typically	/man/3/dbg
+typically	/man/3/dynld
+typically	/man/3/flash
+typically	/man/3/fs
+typically	/man/3/ftl
+typically	/man/3/i2c
+typically	/man/3/kprof
+typically	/man/3/pipe
+typically	/man/3/pnp
+typically	/man/3/prog
+typically	/man/3/root
+typically	/man/3/sd
+typically	/man/3/srv9
+typically	/man/4/archfs
+typically	/man/4/export
+typically	/man/4/factotum
+typically	/man/4/keysrv
+typically	/man/4/kfs
+typically	/man/4/mntgen
+typically	/man/4/registry
+typically	/man/5/0intro
+typically	/man/6/attrdb
+typically	/man/6/audio
+typically	/man/6/keys
+typically	/man/6/namespace
+typically	/man/6/ubfa
+typically	/man/8/ai2key
+typically	/man/8/applylog
+typically	/man/8/bootpd
+typically	/man/8/create
+typically	/man/8/createsignerkey
+typically	/man/8/logind
+typically	/man/8/plumber
+typically	/man/8/prep
+typically	/man/8/rip
+typically	/man/8/signer
+typically	/man/8/styxchat
+typically	/man/9/0intro
+typically	/man/9/button
+typically	/man/9/canvas
+typically	/man/9/checkbutton
+typically	/man/9/choicebutton
+typically	/man/9/entry
+typically	/man/9/menu
+typically	/man/9/menubutton
+typically	/man/9/options
+typically	/man/9/radiobutton
+typically	/man/9/scrollbar
+typically	/man/9/text
+typing	/man/1/acme
+typing	/man/1/brutus
+typing	/man/1/charon
+typing	/man/1/grid-session
+typing	/man/1/mash-tk
+typing	/man/1/miniterm
+typing	/man/1/wm-sh
+typing	/man/2/security-0intro
+typing	/man/3/cons
+typing	/man/6/keyboard
+typing	/man/8/prep
+tzoff	/man/2/daytime
+u'ffff	/man/2/json
+u.h	/man/10/0intro
+u.s	/man/9/1copyright
+u32int	/man/10/styx
+u32int	/man/5/attach
+uart	/man/10/plan9.ini
+uart0	/man/10/intrenable
+uarts	/man/10/plan9.ini
+ubf	/man/2/ubfa
+ubf	/man/6/ubfa
+ubfa	/man/2/json
+ubfa	/man/2/ubfa
+ubfa	/man/6/json
+ubfa	/man/6/sexprs
+ubfa	/man/6/ubfa
+ubfa.b	/man/2/ubfa
+ubfa.m	/man/2/ubfa
+ubiquitous	/man/1/wm-misc
+uboot	/man/10/5cv
+uc	/man/3/fs
+ucase	/man/1/dd
+uchar	/man/10/a.out
+uchar	/man/10/allocb
+uchar	/man/10/dev
+uchar	/man/10/devattach
+uchar	/man/10/dynld
+uchar	/man/10/styx
+uchar	/man/6/colour
+ucr	/man/3/sd
+ud	/man/1/uniq
+udp	/man/1/netstat
+udp	/man/2/dhcpclient
+udp	/man/2/dial
+udp	/man/2/srv
+udp	/man/2/sys-dial
+udp	/man/2/virgil
+udp	/man/3/ip
+udp	/man/6/ndb
+udp	/man/8/bootpd
+udp	/man/8/sntp
+udp	/man/8/virgild
+udpmsg	/man/3/ip
+uexp	/man/1/yacc
+ugo	/man/1/chmod
+uhci	/man/10/plan9.ini
+uid	/man/1/ar
+uid	/man/10/ar
+uid	/man/10/devattach
+uid	/man/10/iar
+uid	/man/10/styxserver
+uid	/man/2/styxservers
+uid	/man/2/sys-0intro
+uid	/man/2/sys-stat
+uid	/man/3/fs
+uid	/man/3/logfs
+uid	/man/5/stat
+uid	/man/6/proto
+uid	/man/8/applylog
+uid	/man/8/create
+uid	/man/8/styxchat
+uids	/man/2/palmfile
+uids	/man/3/logfs
+uidseed	/man/2/palmfile
+uint	/man/10/devattach
+uint	/man/10/styx
+uint	/man/10/styxserver
+ulaw	/man/3/audio
+ulaw	/man/6/audio
+ulong	/man/10/allocb
+ulong	/man/10/atoi
+ulong	/man/10/dev
+ulong	/man/10/devattach
+ulong	/man/10/dynld
+ulong	/man/10/inb
+ulong	/man/10/malloc
+ulong	/man/10/newchan
+ulong	/man/10/qio
+ulong	/man/10/readnum
+ulong	/man/10/seconds
+ulong	/man/10/styx
+ulong	/man/10/styxserver
+ulong	/man/10/xalloc
+ultimately	/man/2/asn1
+ultimately	/man/2/draw-0intro
+ultimately	/man/2/styxservers-nametree
+ultimately	/man/2/sys-0intro
+ultimately	/man/3/ip
+ultimately	/man/6/sexprs
+ultimately	/man/8/logind
+ultra	/man/10/plan9.ini
+um	/man/1/alphabet-fs
+um	/man/1/fs
+uments	/man/1/alphabet-abc
+uments	/man/1/alphabet-grid
+uments	/man/1/alphabet-main
+un	/man/1/grid-session
+un	/man/1/tiny
+unabated	/man/1/deb
+unable	/man/2/fsproto
+unable	/man/2/styxpersist
+unable	/man/4/spree
+unacceptable	/man/2/sys-open
+unacknowledged	/man/10/qio
+unaffected	/man/1/acme
+unaffected	/man/1/listen
+unaffected	/man/1/sh-alphabet
+unaffected	/man/2/sys-pctl
+unaffected	/man/5/walk
+unaffected	/man/9/canvas
+unallocated	/man/9/pack
+unaltered	/man/1/wm-sh
+unambiguously	/man/2/complete
+uname	/man/1/listen
+uname	/man/10/styx
+uname	/man/10/styxserver
+uname	/man/2/styx
+uname	/man/2/styxservers
+uname	/man/3/logfs
+uname	/man/5/0intro
+uname	/man/5/attach
+uname	/man/8/styxchat
+unames	/man/3/logfs
+unannounced	/man/3/ip
+unarchive	/man/2/spree-allow
+unarchive	/man/2/spree-cardlib
+unarchive	/man/2/spree-objstore
+unarchives	/man/2/spree-cardlib
+unarchiving	/man/2/spree-cardlib
+unary	/man/1/calc
+unary	/man/1/fc
+unary	/man/1/m4
+unary	/man/1/sh-expr
+unary	/man/10/acid
+unary	/man/2/w3c-xpointers
+unary	/man/3/dbg
+unassigned	/man/2/dis
+unattractive	/man/1/tiny
+unauthenticated	/man/1/alphabet-abc
+unauthenticated	/man/1/alphabet-grid
+unauthenticated	/man/4/grid-cpu
+unavailable	/man/10/lock
+unavailable	/man/10/malloc
+unavailable	/man/10/qlock
+unaware	/man/2/sys-0intro
+unbind	/man/3/ip
+unblanked	/man/3/vga
+unblind	/man/8/register
+unblinded	/man/8/signer
+unblock	/man/1/dd
+unblocked	/man/3/prog
+unbound	/man/2/sys-bind
+unbound	/man/3/ip
+unbound	/man/9/bind
+unbuffered	/man/1/xd
+unbuffered	/man/2/styxflush
+unbuffered	/man/3/flash
+unbundle	/man/1/alphabet-fs
+unbundle	/man/1/fs
+unchanged	/man/1/acme
+unchanged	/man/1/fmt
+unchanged	/man/1/m4
+unchanged	/man/1/sh-regex
+unchanged	/man/1/sh-string
+unchanged	/man/10/allocb
+unchanged	/man/10/kstrip
+unchanged	/man/10/malloc
+unchanged	/man/2/math-fp
+unchanged	/man/2/math-linalg
+unchanged	/man/2/names
+unchanged	/man/2/security-ssl
+unchanged	/man/2/sys-byte2char
+unchanged	/man/2/sys-open
+unchanged	/man/3/dbg
+unchanged	/man/4/factotum
+unchanged	/man/8/changelogin
+unchanged	/man/9/0intro
+unclassified	/man/2/security-0intro
+unclear	/man/2/styxpersist
+uncompressed	/man/1/gzip
+uncompressed	/man/10/5cv
+uncompressed	/man/2/palmfile
+uncompressed	/man/3/boot
+uncompressed	/man/3/draw
+uncompressed	/man/6/image
+uncompressed	/man/8/collabsrv
+uncompressing	/man/2/palmfile
+unconfig	/man/3/logfs
+unconstrained	/man/9/canvas
+unconsumed	/man/10/allocb
+uncorrupted	/man/1/sh-file2chan
+uncovered	/man/3/draw
+undeclare	/man/1/sh-alphabet
+undeclared	/man/1/calc
+undeclared	/man/1/sh-alphabet
+undef	/man/10/2c
+undefine	/man/1/m4
+undefined	/man/1/sh-mload
+undefined	/man/1/sh-string
+undefined	/man/10/2l
+undefined	/man/10/inm
+undefined	/man/10/print
+undefined	/man/2/draw-image
+undefined	/man/2/draw-rect
+undefined	/man/2/sets
+undefined	/man/3/draw
+undefined	/man/9/bind
+undefined	/man/9/canvas
+undefines	/man/1/m4
+undelimited	/man/2/keyring-getmsg
+undelimited	/man/2/msgio
+underflow	/man/2/math-fp
+underlies	/man/2/styx
+underline	/man/9/button
+underline	/man/9/checkbutton
+underline	/man/9/label
+underline	/man/9/listbox
+underline	/man/9/menu
+underline	/man/9/menubutton
+underline	/man/9/options
+underline	/man/9/radiobutton
+underline	/man/9/text
+underlined	/man/9/button
+underlined	/man/9/checkbutton
+underlined	/man/9/label
+underlined	/man/9/menu
+underlined	/man/9/menubutton
+underlined	/man/9/radiobutton
+underlines	/man/9/text
+underlying	/man/1/0intro
+underlying	/man/1/chgrp
+underlying	/man/10/intrenable
+underlying	/man/2/asn1
+underlying	/man/2/attrdb
+underlying	/man/2/bufio
+underlying	/man/2/disks
+underlying	/man/2/draw-0intro
+underlying	/man/2/drawmux
+underlying	/man/2/security-auth
+underlying	/man/2/sys-0intro
+underlying	/man/2/sys-dup
+underlying	/man/2/sys-fd2path
+underlying	/man/2/sys-millisec
+underlying	/man/2/sys-open
+underlying	/man/2/xml
+underlying	/man/3/cmd
+underlying	/man/3/cons
+underlying	/man/3/flash
+underlying	/man/3/ftl
+underlying	/man/3/logfs
+underlying	/man/3/tls
+underlying	/man/4/acme
+underlying	/man/6/colour
+undermine	/man/2/security-0intro
+underneath	/man/1/alphabet-fs
+underneath	/man/1/fs
+underneath	/man/2/spree-cardlib
+underneath	/man/9/entry
+underneath	/man/9/grab
+underneath	/man/9/menu
+underneath	/man/9/menubutton
+underneath	/man/9/text
+underscore	/man/1/m4
+underscore	/man/1/mash
+underscore	/man/1/sh
+underscores	/man/9/text
+understand	/man/1/env
+understand	/man/1/wm
+understand	/man/10/styxserver
+understand	/man/2/plumbmsg
+understand	/man/5/version
+understanding	/man/3/logfs
+understands	/man/1/miniterm
+understands	/man/1/tcs
+understands	/man/1/wm
+understands	/man/8/rip
+understood	/man/1/0intro
+understood	/man/1/fc
+understood	/man/1/filename
+understood	/man/1/fs
+understood	/man/10/atoi
+understood	/man/10/devattach
+understood	/man/2/sys-dup
+understood	/man/2/tkclient
+understood	/man/4/acme
+understood	/man/6/keyboard
+understood	/man/9/scrollbar
+undesirable	/man/1/listen
+undesirable	/man/2/sys-read
+undirtied	/man/1/acme
+undivert	/man/1/m4
+undiverted	/man/1/m4
+undiverting	/man/1/m4
+undo	/man/1/acme
+undo	/man/1/bind
+undo	/man/4/acme
+undo	/man/6/namespace
+undoes	/man/1/acme
+undoes	/man/1/sh
+undoes	/man/2/bufio
+undoing	/man/1/acme
+undone	/man/1/bind
+undone	/man/2/sys-bind
+undone	/man/4/acme
+unencoded	/man/2/sexprs
+unencrypted	/man/10/plan9.ini
+unencrypted	/man/2/palmfile
+unescaped	/man/6/regexp
+unexecuted	/man/1/cprof
+unexpectedly	/man/1/acme
+unexpectedly	/man/2/draw-context
+unexpired	/man/2/keyset
+unfilled	/man/9/canvas
+unfl	/man/2/math-fp
+unformatted	/man/4/factotum
+unfortunate	/man/10/9load
+unfortunately	/man/1/charon
+unfortunately	/man/1/sh-alphabet
+unfortunately	/man/3/cmd
+unfreeze	/man/3/tv
+ungetb	/man/2/bufio
+ungetc	/man/2/bufio
+ungetc	/man/2/rfc822
+unhandled	/man/2/0intro
+unibrowse	/man/1/wm-misc
+unibrowse.b	/man/1/wm-misc
+unicast	/man/3/ip
+unicode	/man/1/charon
+unicode	/man/1/fc
+unicode	/man/1/freq
+unicode	/man/1/m4
+unicode	/man/1/sort
+unicode	/man/1/tcs
+unicode	/man/1/unicode
+unicode	/man/1/wm-misc
+unicode	/man/10/dev
+unicode	/man/10/devattach
+unicode	/man/10/kbdputc
+unicode	/man/10/master
+unicode	/man/2/asn1
+unicode	/man/2/bufio
+unicode	/man/2/convcs
+unicode	/man/2/draw-0intro
+unicode	/man/2/draw-font
+unicode	/man/2/json
+unicode	/man/2/keyring-certtostr
+unicode	/man/2/stringinttab
+unicode	/man/2/sys-byte2char
+unicode	/man/2/sys-print
+unicode	/man/2/sys-utfbytes
+unicode	/man/5/0intro
+unicode	/man/6/font
+unicode	/man/6/keyboard
+unicode	/man/6/sbl
+unicode	/man/6/translate
+unicode	/man/6/utf
+unicode	/man/8/styxmon
+unicode.9.font	/man/1/acme
+unicode.9.font	/man/1/emu
+unicode.9.font	/man/9/types
+unicode.b	/man/1/unicode
+unicoderange	/man/2/w3c-css
+unidata	/man/1/wm-misc
+uniform	/man/2/w3c-uris
+uniformly	/man/1/acme
+uniformly	/man/6/colour
+uniformly	/man/9/pack
+unimplemented	/man/1/mdb
+unimplemented	/man/2/imagefile
+unimplemented	/man/3/dbg
+unimplemented	/man/3/ip
+unimplemented	/man/3/prog
+uninitialised	/man/2/0intro
+uninitialised	/man/2/draw-display
+uninitialized	/man/10/a.out
+uninitialized	/man/5/attach
+uninitialized	/man/5/version
+unintercepted	/man/2/security-0intro
+uninterpreted	/man/1/sh-alphabet
+uninterpreted	/man/2/rfc822
+uninterpreted	/man/2/w3c-css
+uninterpreted	/man/6/utf
+uninterrupted	/man/2/spree
+unintuitive	/man/6/colour
+union	/man/1/alphabet-fs
+union	/man/1/bind
+union	/man/1/fs
+union	/man/1/ls
+union	/man/1/tiny
+union	/man/1/yacc
+union	/man/10/2c
+union	/man/10/c2l
+union	/man/10/styx
+union	/man/2/alphabet-intro
+union	/man/2/bloomfilter
+union	/man/2/readdir
+union	/man/2/sets
+union	/man/2/sys-0intro
+union	/man/2/sys-bind
+union	/man/2/sys-open
+union	/man/4/archfs
+union	/man/4/dossrv
+union	/man/4/import
+union	/man/4/memfs
+union	/man/4/ramfile
+union	/man/5/open
+unions	/man/10/c2l
+uniprocessor	/man/10/lock
+uniq	/man/1/comm
+uniq	/man/1/uniq
+uniq	/man/2/ubfa
+uniq.b	/man/1/uniq
+unique	/man/1/acme
+unique	/man/1/deb
+unique	/man/1/disdep
+unique	/man/1/m4
+unique	/man/1/sh-file2chan
+unique	/man/1/sh-tk
+unique	/man/1/uniq
+unique	/man/10/a.out
+unique	/man/10/c2l
+unique	/man/10/dev
+unique	/man/10/devattach
+unique	/man/10/newchan
+unique	/man/10/plan9.ini
+unique	/man/10/styxserver
+unique	/man/2/dict
+unique	/man/2/draw-screen
+unique	/man/2/json
+unique	/man/2/palmfile
+unique	/man/2/prefab-0intro
+unique	/man/2/spree
+unique	/man/2/spree-cardlib
+unique	/man/2/styxservers
+unique	/man/2/sys-bind
+unique	/man/2/sys-stat
+unique	/man/2/wmsrv
+unique	/man/3/pnp
+unique	/man/4/acme
+unique	/man/5/0intro
+unique	/man/5/stat
+unique	/man/6/json
+unique	/man/6/sbl
+unique	/man/8/collabsrv
+unique	/man/8/mangaload
+unique	/man/8/prep
+unique	/man/9/canvas
+uniquely	/man/10/intrenable
+uniquely	/man/2/complete
+uniquely	/man/2/registries
+uniquely	/man/2/security-0intro
+uniquely	/man/2/styxservers
+uniquely	/man/4/factotum
+uniquely	/man/6/sexprs
+unit	/man/1/du
+unit	/man/1/units
+unit	/man/10/ar
+unit	/man/10/plan9.ini
+unit	/man/2/math-linalg
+unit	/man/2/scsiio
+unit	/man/2/spree
+unit	/man/2/sys-0intro
+unit	/man/2/sys-iounit
+unit	/man/2/w3c-css
+unit	/man/3/flash
+unit	/man/3/ftl
+unit	/man/3/ip
+unit	/man/3/prog
+unit	/man/3/sd
+unit	/man/6/sbl
+unit	/man/8/ftl
+unit	/man/9/scrollbar
+unit	/man/9/types
+unites	/man/1/bind
+units	/man/1/du
+units	/man/1/units
+units	/man/1/xd
+units	/man/10/9load
+units	/man/10/seconds
+units	/man/2/prefab-0intro
+units	/man/2/styxservers
+units	/man/2/w3c-css
+units	/man/3/cons
+units	/man/3/flash
+units	/man/3/ftl
+units	/man/3/prog
+units	/man/3/sd
+units	/man/6/man
+units	/man/8/prep
+units	/man/9/canvas
+units	/man/9/entry
+units	/man/9/grid
+units	/man/9/listbox
+units	/man/9/scrollbar
+units	/man/9/text
+units	/man/9/types
+units.b	/man/1/units
+units.y	/man/1/units
+universal	/man/2/asn1
+universal	/man/2/geodesy
+universal	/man/6/ubfa
+universal	/man/6/utf
+universalstring	/man/2/asn1
+university	/man/9/0intro
+university	/man/9/1copyright
+unix	/man/1/0intro
+unix	/man/1/ar
+unix	/man/1/diff
+unix	/man/1/gzip
+unix	/man/1/m4
+unix	/man/1/mash
+unix	/man/1/yacc
+unix	/man/10/2l
+unix	/man/10/acid
+unix	/man/10/ar
+unix	/man/10/conf
+unix	/man/10/iar
+unix	/man/2/dbm
+unix	/man/2/srv
+unix	/man/2/sys-0intro
+unix	/man/3/cmd
+unix	/man/3/fs
+unix	/man/3/ip
+unix	/man/4/tarfs
+unix	/man/6/man
+unix	/man/6/scancode
+unix	/man/9/text
+unix's	/man/3/fs
+unixware	/man/4/namespace
+unk	/man/3/dbg
+unknown	/man/1/sh-arg
+unknown	/man/1/sh-tk
+unknown	/man/1/stack
+unknown	/man/2/arg
+unknown	/man/2/asn1
+unknown	/man/2/factotum
+unknown	/man/2/ir
+unknown	/man/2/keyring-gensk
+unknown	/man/2/plumbmsg
+unknown	/man/2/styx
+unknown	/man/2/sys-iounit
+unknown	/man/3/cmd
+unknown	/man/3/dbg
+unknown	/man/3/ip
+unknown	/man/5/version
+unless	/man/1/0intro
+unless	/man/1/9win
+unless	/man/1/acme
+unless	/man/1/alphabet-fs
+unless	/man/1/calc
+unless	/man/1/emu
+unless	/man/1/fs
+unless	/man/1/grid-ns
+unless	/man/1/grid-session
+unless	/man/1/limbo
+unless	/man/1/listen
+unless	/man/1/m4
+unless	/man/1/mash-tk
+unless	/man/1/mk
+unless	/man/1/mprof
+unless	/man/1/prof
+unless	/man/1/sh
+unless	/man/1/sh-std
+unless	/man/1/sh-tk
+unless	/man/1/stream
+unless	/man/1/touch
+unless	/man/1/wish
+unless	/man/1/yacc
+unless	/man/10/9load
+unless	/man/10/kproc
+unless	/man/10/lock
+unless	/man/10/mk
+unless	/man/10/plan9.ini
+unless	/man/2/debug
+unless	/man/2/registries
+unless	/man/2/sys-print
+unless	/man/2/tkclient
+unless	/man/2/wmclient
+unless	/man/3/cmd
+unless	/man/3/ip
+unless	/man/3/srv
+unless	/man/3/tls
+unless	/man/4/export
+unless	/man/4/grid-cpu
+unless	/man/4/kfs
+unless	/man/5/0intro
+unless	/man/5/clunk
+unless	/man/5/walk
+unless	/man/8/changelogin
+unless	/man/8/dhcp
+unless	/man/8/init
+unless	/man/8/prep
+unless	/man/8/virgild
+unless	/man/9/1copyright
+unless	/man/9/bind
+unless	/man/9/canvas
+unless	/man/9/checkbutton
+unless	/man/9/grid
+unless	/man/9/menu
+unless	/man/9/pack
+unless	/man/9/radiobutton
+unless	/man/9/text
+unlex	/man/2/rfc822
+unlike	/man/1/sh-std
+unlike	/man/1/sh-string
+unlike	/man/10/plan9.ini
+unlike	/man/2/sexprs
+unlike	/man/3/ip
+unlike	/man/6/sexprs
+unlikely	/man/2/keyring-crypt
+unlikely	/man/5/0intro
+unlimited	/man/1/grid-monitor
+unlimited	/man/2/alphabet-intro
+unload	/man/1/sh
+unload	/man/3/dynld
+unloaded	/man/1/sh
+unloading	/man/3/dynld
+unloads	/man/1/sh-mload
+unlock	/man/10/lock
+unlock	/man/10/qlock
+unlock	/man/3/pnp
+unlocked	/man/10/lock
+unlocking	/man/10/error
+unlocking	/man/10/lock
+unlocks	/man/10/qlock
+unlocks	/man/2/security-0intro
+unmap	/man/9/bind
+unmap	/man/9/menu
+unmapped	/man/2/tk
+unmapped	/man/9/bind
+unmapped	/man/9/destroy
+unmapped	/man/9/pack
+unmaps	/man/9/grid
+unmaps	/man/9/pack
+unmarshal	/man/2/format
+unmasks	/man/10/intrenable
+unmatched	/man/1/tiny
+unmatched	/man/2/attrdb
+unmodified	/man/3/cmd
+unmount	/man/1/bind
+unmount	/man/2/sys-bind
+unmount	/man/3/logfs
+unmount	/man/4/ftpfs
+unmount	/man/5/walk
+unmount	/man/6/namespace
+unmount.b	/man/1/bind
+unmounted	/man/1/bind
+unmounted	/man/2/sys-bind
+unmounted	/man/3/logfs
+unmounted	/man/3/pipe
+unmounted	/man/4/logfile
+unmounted	/man/4/ramfile
+unmounted	/man/6/namespace
+unmounted	/man/8/cs
+unmounted	/man/8/getauthinfo
+unmounting	/man/4/ftpfs
+unmounts	/man/4/grid-cpu
+unnamed	/man/10/2c
+unnamed	/man/8/prep
+unnecessarily	/man/1/acme
+unneeded	/man/10/allocb
+unoccupied	/man/9/grid
+unopened	/man/2/styxservers
+unordered	/man/2/asn1
+unpack	/man/2/dis
+unpack	/man/2/ida
+unpack	/man/2/palmfile
+unpack	/man/2/plumbmsg
+unpack	/man/2/sexprs
+unpack	/man/2/styx
+unpack	/man/8/mkfs
+unpackdir	/man/2/styx
+unpackdir	/man/5/stat
+unpacked	/man/2/styx
+unpacked	/man/8/mkfs
+unpackentry	/man/2/venti
+unpacking	/man/2/palmfile
+unpacking	/man/2/venti
+unpacking	/man/8/mkfs
+unpackroot	/man/2/venti
+unpacks	/man/1/gettar
+unpacks	/man/2/plumbmsg
+unpacks	/man/8/mkfs
+unpacktext	/man/2/palmfile
+unpaddable	/man/2/palmfile
+unpair	/man/2/lists
+unparse	/man/1/alphabet-main
+unparsed	/man/2/sexprs
+unpartitioned	/man/8/prep
+unpleasant	/man/10/error
+unpost	/man/9/menu
+unposted	/man/9/menu
+unposted	/man/9/menubutton
+unposts	/man/9/menu
+unposts	/man/9/menubutton
+unpredictable	/man/10/error
+unpredictable	/man/2/keyring-crypt
+unpredictable	/man/2/prefab-style
+unpredictable	/man/9/pack
+unprotected	/man/6/login
+unqualified	/man/1/sendmail
+unqualified	/man/1/sh-alphabet
+unqualified	/man/2/0intro
+unqualified	/man/6/ndb
+unqualified	/man/6/sbl
+unquote	/man/1/sh
+unquote	/man/1/sh-file2chan
+unquote	/man/3/cmd
+unquoted	/man/1/m4
+unquoted	/man/1/mash
+unquoted	/man/1/mk
+unquoted	/man/1/sh
+unquoted	/man/1/wm-sh
+unquoted	/man/10/mk
+unquoted	/man/2/rfc822
+unquoted	/man/2/sexprs
+unquoted	/man/2/string
+unquoted	/man/6/sexprs
+unquotes	/man/2/csv
+unreachable	/man/3/ip
+unread	/man/1/wm-sh
+unread	/man/10/allocb
+unread	/man/10/qio
+unread	/man/8/collabsrv
+unreadable	/man/2/complete
+unreadable	/man/2/security-0intro
+unrecognised	/man/2/w3c-css
+unrecognized	/man/10/atoi
+unrecoverable	/man/2/xml
+unrecoverable	/man/3/ip
+unregister	/man/2/registries
+unrelated	/man/2/draw-display
+unrelated	/man/3/dbg
+unreliable	/man/3/ip
+unresponsive	/man/10/sleep
+unrestricted	/man/3/prog
+unsafe	/man/4/export
+unscrambled	/man/2/security-0intro
+unset	/man/2/env
+unset	/man/3/env
+unset	/man/9/text
+unsigned	/man/1/auplay
+unsigned	/man/1/mdb
+unsigned	/man/1/tail
+unsigned	/man/10/c2l
+unsigned	/man/10/memory
+unsigned	/man/10/print
+unsigned	/man/10/readnum
+unsigned	/man/10/strcat
+unsigned	/man/2/dis
+unsigned	/man/2/styx
+unsigned	/man/2/sys-print
+unsigned	/man/2/sys-stat
+unsigned	/man/3/prog
+unsigned	/man/3/sign
+unsigned	/man/5/0intro
+unsigned	/man/5/stat
+unsigned	/man/6/audio
+unsigned	/man/6/dis
+unsigned	/man/6/keytext
+unsorted	/man/2/readdir
+unspecified	/man/1/dd
+unspecified	/man/1/emu
+unspecified	/man/1/plumb
+unspecified	/man/10/2c
+unspecified	/man/10/plan9.ini
+unspecified	/man/2/sys-iounit
+unspecified	/man/3/ip
+unspecified	/man/6/dis
+unspecified	/man/9/grid
+unspecified	/man/9/pack
+unspecified	/man/9/text
+unstall	/man/3/usb
+unstop	/man/2/debug
+unstop	/man/3/prog
+unsuccessful	/man/10/devattach
+unsuccessful	/man/2/styxpersist
+unsuitable	/man/10/allocb
+unsuitable	/man/3/ftl
+unsup	/man/3/dbg
+unsupported	/man/3/dbg
+untapped	/man/2/security-0intro
+untapped	/man/8/register
+untask	/man/1/wm
+untouched	/man/2/styxservers
+untouched	/man/2/sys-read
+untoward	/man/1/sh-string
+untoward	/man/2/command
+untransmitted	/man/3/ip
+unusable	/man/3/fs
+unused	/man/1/limbo
+unused	/man/10/2c
+unused	/man/10/ar
+unused	/man/2/asn1
+unused	/man/2/palmfile
+unused	/man/2/spree-cardlib
+unused	/man/2/styx
+unused	/man/2/styxservers
+unused	/man/2/sys-0intro
+unused	/man/3/cmd
+unused	/man/3/dbg
+unused	/man/3/ether
+unused	/man/3/fpga
+unused	/man/3/plap
+unused	/man/3/tinyfs
+unused	/man/3/usb
+unused	/man/5/stat
+unused	/man/6/image
+unused	/man/6/sbl
+unused	/man/6/sexprs
+unused	/man/8/prep
+unused	/man/8/virgild
+unused	/man/9/pack
+unusedbits	/man/2/asn1
+unusual	/man/3/ssl
+unusually	/man/8/rstyxd
+unwritable	/man/4/acme
+unwritable	/man/4/namespace
+uo	/man/1/chgrp
+update	/man/1/cpu
+update	/man/1/mash
+update	/man/1/mash-make
+update	/man/1/mk
+update	/man/1/sh-tk
+update	/man/1/tkcmd
+update	/man/1/touch
+update	/man/1/wm-sh
+update	/man/10/iar
+update	/man/10/lock
+update	/man/10/mk
+update	/man/10/odbc
+update	/man/10/styxserver
+update	/man/2/registries
+update	/man/2/sys-fd2path
+update	/man/3/prog
+update	/man/4/kfs
+update	/man/4/ramfile
+update	/man/4/spree
+update	/man/7/db
+update	/man/8/applylog
+update	/man/8/changelogin
+update	/man/8/create
+update	/man/8/mkfs
+update	/man/9/0intro
+update	/man/9/text
+update	/man/9/update
+updated	/man/1/collab
+updated	/man/1/cpu
+updated	/man/1/deb
+updated	/man/1/mash
+updated	/man/1/mash-make
+updated	/man/1/mk
+updated	/man/1/units
+updated	/man/10/atoi
+updated	/man/10/master
+updated	/man/10/mk
+updated	/man/2/keyring-crypt
+updated	/man/2/prefab-compound
+updated	/man/2/sys-pipe
+updated	/man/3/ftl
+updated	/man/3/logfs
+updated	/man/6/keys
+updated	/man/8/create
+updated	/man/8/mkfs
+updated	/man/9/checkbutton
+updated	/man/9/choicebutton
+updated	/man/9/options
+updated	/man/9/radiobutton
+updated	/man/9/text
+updatelog	/man/8/applylog
+updatelog.b	/man/8/applylog
+updates	/man/1/grid-query
+updates	/man/1/grid-session
+updates	/man/1/mk
+updates	/man/1/wm-misc
+updates	/man/10/mk
+updates	/man/10/ref
+updates	/man/2/keyring-sha1
+updates	/man/2/math-linalg
+updates	/man/2/spree
+updates	/man/3/logfs
+updates	/man/3/prog
+updates	/man/3/tinyfs
+updates	/man/4/spree
+updates	/man/8/applylog
+updates	/man/8/create
+updates	/man/8/mangaload
+updates	/man/8/rip
+updates	/man/9/1copyright
+updates	/man/9/options
+updates	/man/9/update
+updating	/man/1/mk
+updating	/man/10/mk
+updating	/man/2/keyring-rc4
+updating	/man/4/ramfile
+updating	/man/8/sntp
+updwards	/man/2/spree-cardlib
+upper	/man/1/acme
+upper	/man/1/dd
+upper	/man/1/look
+upper	/man/1/mash-tk
+upper	/man/1/tr
+upper	/man/10/print
+upper	/man/2/asn1
+upper	/man/2/convcs
+upper	/man/2/dis
+upper	/man/2/draw-0intro
+upper	/man/2/draw-image
+upper	/man/2/draw-rect
+upper	/man/2/encoding
+upper	/man/2/geodesy
+upper	/man/2/math-linalg
+upper	/man/2/prefab-compound
+upper	/man/2/sexprs
+upper	/man/2/string
+upper	/man/2/sys-0intro
+upper	/man/2/sys-print
+upper	/man/2/w3c-css
+upper	/man/3/cons
+upper	/man/3/draw
+upper	/man/3/pnp
+upper	/man/6/sexprs
+upper	/man/9/canvas
+upper	/man/9/entry
+upper	/man/9/menu
+upper	/man/9/text
+upwards	/man/10/9load
+upwards	/man/2/spree-cardlib
+ureg	/man/10/intrenable
+ureg	/man/3/dbg
+ureg.h	/man/10/0intro
+ureg.h	/man/10/intrenable
+uri	/man/2/w3c-css
+uri	/man/2/w3c-uris
+uri	/man/2/w3c-xpointers
+uri	/man/8/httpd
+uri's	/man/2/w3c-uris
+uri.authority	/man/2/w3c-uris
+uri.parse	/man/2/w3c-uris
+uri.query	/man/2/w3c-uris
+uri.text	/man/2/w3c-uris
+uris	/man/2/w3c-uris
+uris.b	/man/2/w3c-uris
+uris.m	/man/2/w3c-uris
+url	/man/1/charon
+url	/man/1/webgrab
+url	/man/2/w3c-css
+urls	/man/1/charon
+us005214703	/man/2/keyring-crypt
+usa	/man/1/miniterm
+usa	/man/2/ubfa
+usa	/man/6/ubfa
+usable	/man/1/collab-clients
+usable	/man/10/allocb
+usable	/man/2/imagefile
+usable	/man/2/ip
+usable	/man/2/wmclient
+usable	/man/4/9srvfs
+usable	/man/4/ramfile
+usable	/man/6/colour
+usable	/man/9/text
+usage	/man/1/alphabet-abc
+usage	/man/1/alphabet-grid
+usage	/man/1/du
+usage	/man/1/ebook
+usage	/man/1/fc
+usage	/man/1/sh
+usage	/man/1/sh-alphabet
+usage	/man/1/sh-arg
+usage	/man/1/sh-std
+usage	/man/1/sh-string
+usage	/man/1/wm-misc
+usage	/man/10/print
+usage	/man/2/arg
+usage	/man/2/sh
+usage	/man/9/menubutton
+usb	/man/10/plan9.ini
+usb	/man/3/usb
+usb	/man/4/palmsrv
+usbaddr	/man/3/usb
+usbctl	/man/3/usb
+usbdata	/man/3/usb
+usbframe	/man/3/usb
+usbsetup	/man/3/usb
+usbstat	/man/3/usb
+usbx	/man/10/plan9.ini
+usefac	/man/2/styxpersist
+usefully	/man/1/acme
+usefully	/man/2/draw-0intro
+usefully	/man/2/prefab-compound
+user	/man/1/0intro
+user	/man/1/acme
+user	/man/1/alphabet-fs
+user	/man/1/alphabet-main
+user	/man/1/ar
+user	/man/1/bind
+user	/man/1/cd
+user	/man/1/charon
+user	/man/1/collab
+user	/man/1/collab-clients
+user	/man/1/cp
+user	/man/1/deb
+user	/man/1/dmview
+user	/man/1/ebook
+user	/man/1/filename
+user	/man/1/fs
+user	/man/1/ftree
+user	/man/1/grid-monitor
+user	/man/1/grid-ns
+user	/man/1/grid-query
+user	/man/1/grid-register
+user	/man/1/grid-session
+user	/man/1/kill
+user	/man/1/listen
+user	/man/1/logon
+user	/man/1/ls
+user	/man/1/mash-tk
+user	/man/1/math-misc
+user	/man/1/miniterm
+user	/man/1/netkey
+user	/man/1/netstat
+user	/man/1/p
+user	/man/1/passwd
+user	/man/1/runas
+user	/man/1/secstore
+user	/man/1/sendmail
+user	/man/1/sh
+user	/man/1/sh-std
+user	/man/1/tiny
+user	/man/1/tktester
+user	/man/1/toolbar
+user	/man/1/wm
+user	/man/1/wm-misc
+user	/man/1/wm-sh
+user	/man/1/yacc
+user	/man/10/9load
+user	/man/10/acid
+user	/man/10/devattach
+user	/man/10/eve
+user	/man/10/iar
+user	/man/10/inm
+user	/man/10/kproc
+user	/man/10/odbc
+user	/man/10/plan9.ini
+user	/man/10/styxserver
+user	/man/2/debug
+user	/man/2/dialog
+user	/man/2/dividers
+user	/man/2/factotum
+user	/man/2/ida
+user	/man/2/keyring-0intro
+user	/man/2/keyring-auth
+user	/man/2/keyring-certtostr
+user	/man/2/keyring-gensk
+user	/man/2/keyset
+user	/man/2/newns
+user	/man/2/palmfile
+user	/man/2/plumbmsg
+user	/man/2/pop3
+user	/man/2/prefab-0intro
+user	/man/2/registries
+user	/man/2/secstore
+user	/man/2/security-0intro
+user	/man/2/security-auth
+user	/man/2/security-login
+user	/man/2/selectfile
+user	/man/2/spree
+user	/man/2/styxservers
+user	/man/2/sys-0intro
+user	/man/2/sys-fauth
+user	/man/2/sys-open
+user	/man/2/sys-remove
+user	/man/2/sys-stat
+user	/man/2/tabs
+user	/man/2/tk
+user	/man/2/tkclient
+user	/man/2/wmclient
+user	/man/3/arch
+user	/man/3/cap
+user	/man/3/cmd
+user	/man/3/cons
+user	/man/3/fs
+user	/man/3/ip
+user	/man/3/logfs
+user	/man/3/mnt
+user	/man/3/pnp
+user	/man/3/prog
+user	/man/3/srv
+user	/man/3/tls
+user	/man/3/vga
+user	/man/4/acme
+user	/man/4/factotum
+user	/man/4/ftpfs
+user	/man/4/grid-cpu
+user	/man/4/import
+user	/man/4/iostats
+user	/man/4/keyfs
+user	/man/4/keysrv
+user	/man/4/kfs
+user	/man/4/memfs
+user	/man/4/namespace
+user	/man/4/spree
+user	/man/4/tarfs
+user	/man/5/0intro
+user	/man/5/attach
+user	/man/5/flush
+user	/man/5/open
+user	/man/5/stat
+user	/man/5/walk
+user	/man/6/auth
+user	/man/6/keyboard
+user	/man/6/keys
+user	/man/6/login
+user	/man/6/plumbing
+user	/man/6/proto
+user	/man/6/sexprs
+user	/man/6/users
+user	/man/7/db
+user	/man/7/dbsrv
+user	/man/8/ai2key
+user	/man/8/bootpd
+user	/man/8/changelogin
+user	/man/8/collabsrv
+user	/man/8/create
+user	/man/8/createsignerkey
+user	/man/8/getauthinfo
+user	/man/8/httpd
+user	/man/8/kfscmd
+user	/man/8/plumber
+user	/man/8/register
+user	/man/8/rstyxd
+user	/man/8/signer
+user	/man/8/svc
+user	/man/8/touchcal
+user	/man/9/button
+user	/man/9/checkbutton
+user	/man/9/choicebutton
+user	/man/9/radiobutton
+user	/man/9/scrollbar
+user's	/man/1/acme
+user's	/man/1/chmod
+user's	/man/1/collab-clients
+user's	/man/1/deb
+user's	/man/1/ftree
+user's	/man/1/passwd
+user's	/man/10/print
+user's	/man/10/readnum
+user's	/man/2/factotum
+user's	/man/2/keyring-0intro
+user's	/man/2/keyset
+user's	/man/2/pop3
+user's	/man/2/secstore
+user's	/man/2/security-0intro
+user's	/man/3/cmd
+user's	/man/3/fs
+user's	/man/4/acme
+user's	/man/4/factotum
+user's	/man/4/ftpfs
+user's	/man/4/import
+user's	/man/4/keyfs
+user's	/man/4/keysrv
+user's	/man/6/keys
+user's	/man/6/login
+user's	/man/6/proto
+user's	/man/8/ai2key
+user's	/man/8/getauthinfo
+userdir	/man/1/charon
+userid	/man/8/virgild
+userinfo	/man/2/w3c-uris
+username	/man/1/deb
+username	/man/1/rcmd
+username	/man/1/toolbar
+username	/man/10/odbc
+username	/man/2/w3c-uris
+username	/man/3/ip
+username	/man/7/db
+username	/man/8/getauthinfo
+usernames	/man/10/eve
+userpw	/man/2/w3c-uris
+users	/man/1/0intro
+users	/man/1/collab-clients
+users	/man/1/secstore
+users	/man/1/tktester
+users	/man/2/secstore
+users	/man/2/security-0intro
+users	/man/2/sys-stat
+users	/man/3/cap
+users	/man/3/logfs
+users	/man/3/mnt
+users	/man/4/factotum
+users	/man/4/kfs
+users	/man/4/registry
+users	/man/5/0intro
+users	/man/5/stat
+users	/man/6/users
+users	/man/8/collabsrv
+users	/man/8/createsignerkey
+users	/man/8/kfscmd
+users	/man/8/mkfs
+users	/man/8/svc
+users's	/man/8/collabsrv
+userxx	/man/1/logon
+user→ca	/man/6/login
+usessl	/man/1/charon
+useversion	/man/2/sys-fversion
+ushort	/man/10/devattach
+ushort	/man/10/inb
+ushort	/man/10/newchan
+ushort	/man/10/styx
+ushort	/man/5/version
+using	/man/1/0intro
+using	/man/1/acme
+using	/man/1/auplay
+using	/man/1/bind
+using	/man/1/calc
+using	/man/1/calendar
+using	/man/1/charon
+using	/man/1/collab-clients
+using	/man/1/cprof
+using	/man/1/cpu
+using	/man/1/crypt
+using	/man/1/deb
+using	/man/1/grid-ns
+using	/man/1/grid-session
+using	/man/1/idea
+using	/man/1/keyboard
+using	/man/1/listen
+using	/man/1/logon
+using	/man/1/m4
+using	/man/1/man
+using	/man/1/mash
+using	/man/1/mash-tk
+using	/man/1/math-misc
+using	/man/1/miniterm
+using	/man/1/mk
+using	/man/1/mux
+using	/man/1/netkey
+using	/man/1/passwd
+using	/man/1/sendmail
+using	/man/1/sh
+using	/man/1/sh-tk
+using	/man/1/sort
+using	/man/1/telnet
+using	/man/1/tktester
+using	/man/1/wm-misc
+using	/man/1/wm-sh
+using	/man/10/2c
+using	/man/10/5cv
+using	/man/10/9load
+using	/man/10/a.out
+using	/man/10/acid
+using	/man/10/conf
+using	/man/10/dev
+using	/man/10/devattach
+using	/man/10/dmainit
+using	/man/10/dynld
+using	/man/10/error
+using	/man/10/getfields
+using	/man/10/inb
+using	/man/10/kproc
+using	/man/10/lock
+using	/man/10/master
+using	/man/10/mk
+using	/man/10/newchan
+using	/man/10/ntsrv
+using	/man/10/odbc
+using	/man/10/parsecmd
+using	/man/10/plan9.ini
+using	/man/10/print
+using	/man/10/qio
+using	/man/10/sleep
+using	/man/10/styxserver
+using	/man/2/0intro
+using	/man/2/alphabet-intro
+using	/man/2/asn1
+using	/man/2/bufio
+using	/man/2/cfg
+using	/man/2/command
+using	/man/2/convcs
+using	/man/2/dbm
+using	/man/2/debug
+using	/man/2/dhcpclient
+using	/man/2/dial
+using	/man/2/disks
+using	/man/2/draw-example
+using	/man/2/draw-image
+using	/man/2/draw-screen
+using	/man/2/encoding
+using	/man/2/ether
+using	/man/2/factotum
+using	/man/2/filepat
+using	/man/2/geodesy
+using	/man/2/ida
+using	/man/2/imagefile
+using	/man/2/ip
+using	/man/2/json
+using	/man/2/keyring-0intro
+using	/man/2/keyring-certtostr
+using	/man/2/keyring-crypt
+using	/man/2/keyring-getmsg
+using	/man/2/keyring-sha1
+using	/man/2/keyset
+using	/man/2/math-0intro
+using	/man/2/math-fp
+using	/man/2/msgio
+using	/man/2/plumbmsg
+using	/man/2/popup
+using	/man/2/prefab-0intro
+using	/man/2/prefab-compound
+using	/man/2/prefab-element
+using	/man/2/prefab-style
+using	/man/2/registries
+using	/man/2/scsiio
+using	/man/2/secstore
+using	/man/2/security-0intro
+using	/man/2/security-auth
+using	/man/2/security-login
+using	/man/2/security-ssl
+using	/man/2/sexprs
+using	/man/2/spki
+using	/man/2/spree
+using	/man/2/spree-allow
+using	/man/2/spree-cardlib
+using	/man/2/spree-objstore
+using	/man/2/srv
+using	/man/2/styx
+using	/man/2/styxservers
+using	/man/2/sys-0intro
+using	/man/2/sys-dial
+using	/man/2/sys-export
+using	/man/2/sys-iounit
+using	/man/2/sys-pctl
+using	/man/2/sys-read
+using	/man/2/sys-self
+using	/man/2/sys-stat
+using	/man/2/tftp
+using	/man/2/timers
+using	/man/2/tk
+using	/man/2/translate
+using	/man/2/ubfa
+using	/man/2/w3c-uris
+using	/man/2/w3c-xpointers
+using	/man/2/wmsrv
+using	/man/3/arch
+using	/man/3/audio
+using	/man/3/cap
+using	/man/3/cmd
+using	/man/3/cons
+using	/man/3/draw
+using	/man/3/env
+using	/man/3/flash
+using	/man/3/fs
+using	/man/3/indir
+using	/man/3/ip
+using	/man/3/mpeg
+using	/man/3/plap
+using	/man/3/pnp
+using	/man/3/sd
+using	/man/3/sign
+using	/man/3/srv
+using	/man/3/srv9
+using	/man/3/ssl
+using	/man/3/tls
+using	/man/4/dbfs
+using	/man/4/factotum
+using	/man/4/ftpfs
+using	/man/4/grid-cpu
+using	/man/4/keyfs
+using	/man/4/keysrv
+using	/man/4/kfs
+using	/man/4/mntgen
+using	/man/4/registry
+using	/man/5/0intro
+using	/man/5/attach
+using	/man/5/walk
+using	/man/6/attrdb
+using	/man/6/audio
+using	/man/6/auth
+using	/man/6/colour
+using	/man/6/dis
+using	/man/6/font
+using	/man/6/image
+using	/man/6/keyboard
+using	/man/6/keys
+using	/man/6/keytext
+using	/man/6/login
+using	/man/6/ndb
+using	/man/6/plumbing
+using	/man/6/sbl
+using	/man/6/sexprs
+using	/man/6/translate
+using	/man/6/ubfa
+using	/man/7/dbsrv
+using	/man/8/ai2key
+using	/man/8/collabsrv
+using	/man/8/cs
+using	/man/8/dhcp
+using	/man/8/dns
+using	/man/8/getauthinfo
+using	/man/8/httpd
+using	/man/8/init
+using	/man/8/prep
+using	/man/8/rip
+using	/man/8/rstyxd
+using	/man/8/signer
+using	/man/8/styxchat
+using	/man/8/virgild
+using	/man/9/0intro
+using	/man/9/button
+using	/man/9/canvas
+using	/man/9/checkbutton
+using	/man/9/cursor
+using	/man/9/entry
+using	/man/9/label
+using	/man/9/listbox
+using	/man/9/menu
+using	/man/9/menubutton
+using	/man/9/options
+using	/man/9/panel
+using	/man/9/radiobutton
+using	/man/9/scale
+using	/man/9/text
+using	/man/9/types
+usr	/man/1/alphabet-main
+usr	/man/1/bind
+usr	/man/1/cd
+usr	/man/1/charon
+usr	/man/1/collab
+usr	/man/1/deb
+usr	/man/1/emu
+usr	/man/1/filename
+usr	/man/1/grid-ns
+usr	/man/1/grid-session
+usr	/man/1/listen
+usr	/man/1/logon
+usr	/man/1/passwd
+usr	/man/1/rcmd
+usr	/man/1/toolbar
+usr	/man/2/keyring-auth
+usr	/man/2/keyset
+usr	/man/2/registries
+usr	/man/2/security-auth
+usr	/man/3/dup
+usr	/man/3/kprof
+usr	/man/3/pbus
+usr	/man/3/plap
+usr	/man/4/namespace
+usr	/man/6/plumbing
+usr	/man/6/proto
+usr	/man/7/db
+usr	/man/7/dbsrv
+usr	/man/8/getauthinfo
+usr	/man/8/plumber
+usr	/man/8/rstyxd
+ustom	/man/10/print
+usual	/man/1/acme
+usual	/man/1/calc
+usual	/man/1/dmview
+usual	/man/1/fs
+usual	/man/1/ftree
+usual	/man/1/listen
+usual	/man/1/sh
+usual	/man/1/sh-alphabet
+usual	/man/1/sh-arg
+usual	/man/1/sh-csv
+usual	/man/1/sh-sexprs
+usual	/man/1/sh-std
+usual	/man/1/wm-misc
+usual	/man/2/secstore
+usual	/man/2/sh
+usual	/man/2/spki
+usual	/man/2/spree-cardlib
+usual	/man/2/styxflush
+usual	/man/3/ip
+usual	/man/4/acme
+usual	/man/5/attach
+usual	/man/6/image
+usual	/man/6/keyboard
+usual	/man/8/svc
+usual	/man/9/canvas
+usual	/man/9/send
+usual	/man/9/text
+utctime	/man/2/asn1
+utf	/man/1/brutus
+utf	/man/1/charon
+utf	/man/1/freq
+utf	/man/1/mdb
+utf	/man/1/sh-file2chan
+utf	/man/1/sh-sexprs
+utf	/man/1/sh-std
+utf	/man/1/strings
+utf	/man/1/tcs
+utf	/man/1/unicode
+utf	/man/1/wc
+utf	/man/10/getfields
+utf	/man/10/kbdputc
+utf	/man/10/print
+utf	/man/10/rune
+utf	/man/10/strcat
+utf	/man/2/asn1
+utf	/man/2/bufio
+utf	/man/2/convcs
+utf	/man/2/draw-0intro
+utf	/man/2/draw-image
+utf	/man/2/keyring-getmsg
+utf	/man/2/msgio
+utf	/man/2/palmfile
+utf	/man/2/plumbmsg
+utf	/man/2/sexprs
+utf	/man/2/styxservers
+utf	/man/2/sys-byte2char
+utf	/man/2/sys-print
+utf	/man/2/sys-utfbytes
+utf	/man/3/cons
+utf	/man/3/prog
+utf	/man/4/keysrv
+utf	/man/5/0intro
+utf	/man/6/dis
+utf	/man/6/keyboard
+utf	/man/6/keytext
+utf	/man/6/sexprs
+utf	/man/6/utf
+utf	/man/8/rstyxd
+utf	/man/8/styxchat
+utf8	/man/1/tcs
+utf8	/man/2/wmsrv
+utfbytes	/man/2/sys-byte2char
+utfbytes	/man/2/sys-utfbytes
+utferror	/man/2/sys-byte2char
+utflen	/man/10/rune
+utflen.c	/man/10/rune
+utfmax	/man/10/rune
+utfmax	/man/2/sys-byte2char
+utfrrune	/man/10/rune
+utfrrune.c	/man/10/rune
+utfrune	/man/10/rune
+utfrune.c	/man/10/rune
+utfutf	/man/10/rune
+utilise	/man/1/0intro
+utilities	/man/1/gettar
+utilities	/man/1/gzip
+utilities	/man/1/math-misc
+utilities	/man/1/vacget
+utilities	/man/1/wm-misc
+utilities	/man/4/namespace
+utilities	/man/8/0intro
+utilities	/man/8/ai2key
+utility	/man/1/wm-misc
+utility	/man/10/0intro
+utility	/man/10/styxserver
+utility	/man/2/keyring-ipint
+utility	/man/2/rfc822
+utility	/man/2/sh
+utility	/man/2/styxservers
+utils	/man/1/idea
+utils	/man/10/2a
+utils	/man/10/2c
+utils	/man/10/2l
+utils	/man/10/5coff
+utils	/man/10/5cv
+utils	/man/10/acid
+utils	/man/10/c2l
+utils	/man/10/iar
+utils	/man/10/inm
+utils	/man/10/kprof
+utils	/man/10/ksize
+utils	/man/10/kstrip
+utils	/man/10/mk
+utils	/man/10/ntsrv
+utils	/man/10/srclist
+utils	/man/4/namespace
+utils.c	/man/10/atoi
+utm	/man/2/geodesy
+utmeur	/man/2/geodesy
+uudecode	/man/1/uuencode
+uudecode.b	/man/1/uuencode
+uuencode	/man/1/uuencode
+uuencode.b	/man/1/uuencode
+uvalue	/man/2/ubfa
+uvalue.atom	/man/2/ubfa
+uvalue.atom.s	/man/2/ubfa
+uvalue.binary	/man/2/ubfa
+uvalue.int	/man/2/ubfa
+uvalue.list	/man/2/ubfa
+uvalue.string	/man/2/ubfa
+uvalue.tag	/man/2/ubfa
+uvalue.tuple	/man/2/ubfa
+uvatom	/man/2/ubfa
+uvbinary	/man/2/ubfa
+uvint	/man/2/ubfa
+uvlist	/man/2/ubfa
+uvlong	/man/10/styxserver
+uvstring	/man/2/ubfa
+uvtag	/man/2/ubfa
+uvtuple	/man/2/ubfa
+ux	/man/10/print
+uy0	/man/3/tv
+v.bytes	/man/2/asn1
+v.out	/man/10/acid
+v4allrouter	/man/2/ip
+v4allsys	/man/2/ip
+v4bcast	/man/2/ip
+v4noaddr	/man/2/ip
+v4prefix	/man/2/ip
+va	/man/10/2a
+va	/man/10/dmainit
+va	/man/10/print
+vac:score	/man/1/vacget
+vac:score	/man/4/vacfs
+vacfs	/man/1/vacget
+vacfs	/man/4/vacfs
+vacfs.b	/man/4/vacfs
+vacget	/man/1/vacget
+vacget	/man/4/vacfs
+vacget.b	/man/1/vacget
+vacput	/man/1/vacget
+vacput	/man/4/vacfs
+vacput.b	/man/1/vacget
+vain	/man/3/cmd
+val	/man/1/sh-sexprs
+val	/man/10/acid
+val	/man/10/readnum
+val	/man/2/asn1
+val	/man/2/attrdb
+val	/man/2/debug
+val	/man/2/env
+val	/man/2/factotum
+val	/man/2/format
+val	/man/2/hash
+val	/man/2/json
+val	/man/2/plumbmsg
+val	/man/2/registries
+val	/man/2/rfc822
+val	/man/2/spree
+val	/man/2/stringinttab
+val	/man/2/ubfa
+val	/man/2/w3c-xpointers
+val	/man/3/dbg
+val	/man/3/gpio
+val	/man/4/factotum
+val	/man/4/spree
+val	/man/6/keytext
+val	/man/9/listbox
+val	/man/9/options
+val	/man/9/text
+val.tostring	/man/2/asn1
+val1	/man/2/asn1
+val1	/man/8/cs
+val2	/man/2/asn1
+val2	/man/8/cs
+val:hashval	/man/2/hash
+valid	/man/1/acme
+valid	/man/1/alphabet-fs
+valid	/man/1/charon
+valid	/man/1/fc
+valid	/man/1/fs
+valid	/man/1/pwd
+valid	/man/1/sh-file2chan
+valid	/man/1/sh-sexprs
+valid	/man/1/tktester
+valid	/man/1/wm-sh
+valid	/man/10/devattach
+valid	/man/10/error
+valid	/man/10/iar
+valid	/man/10/newchan
+valid	/man/10/styx
+valid	/man/10/styxserver
+valid	/man/2/convcs
+valid	/man/2/dhcpclient
+valid	/man/2/dis
+valid	/man/2/factotum
+valid	/man/2/ip
+valid	/man/2/keyring-sha1
+valid	/man/2/palmfile
+valid	/man/2/regex
+valid	/man/2/security-0intro
+valid	/man/2/spki
+valid	/man/2/spki-verifier
+valid	/man/2/spree-allow
+valid	/man/2/styx
+valid	/man/2/styxservers
+valid	/man/2/sys-0intro
+valid	/man/2/sys-byte2char
+valid	/man/2/sys-dup
+valid	/man/2/xml
+valid	/man/3/0intro
+valid	/man/3/gpio
+valid	/man/3/i2c
+valid	/man/3/ip
+valid	/man/3/mpeg
+valid	/man/3/pnp
+valid	/man/3/sign
+valid	/man/3/tls
+valid	/man/3/touch
+valid	/man/3/tv
+valid	/man/3/vga
+valid	/man/4/factotum
+valid	/man/4/keysrv
+valid	/man/5/0intro
+valid	/man/5/clunk
+valid	/man/5/walk
+valid	/man/6/sbl
+valid	/man/8/changelogin
+valid	/man/8/ftl
+valid	/man/8/prep
+valid	/man/8/svc
+valid	/man/9/bind
+valid	/man/9/grid
+valid	/man/9/pack
+validate	/man/5/attach
+validated	/man/2/security-0intro
+validated	/man/5/attach
+validity	/man/1/secstore
+validity	/man/10/styx
+validity	/man/2/spki
+validity	/man/2/spki-verifier
+validity	/man/2/styxservers
+validity	/man/2/sys-byte2char
+validlt	/man/2/ip
+validly	/man/10/styx
+validly	/man/8/register
+value	/man/1/0intro
+value	/man/1/acme
+value	/man/1/alphabet-fs
+value	/man/1/calc
+value	/man/1/charon
+value	/man/1/cprof
+value	/man/1/dd
+value	/man/1/du
+value	/man/1/emu
+value	/man/1/env
+value	/man/1/fc
+value	/man/1/freq
+value	/man/1/fs
+value	/man/1/grid-query
+value	/man/1/grid-register
+value	/man/1/itest
+value	/man/1/m4
+value	/man/1/mash
+value	/man/1/mash-tk
+value	/man/1/math-misc
+value	/man/1/mdb
+value	/man/1/mk
+value	/man/1/plumb
+value	/man/1/sendmail
+value	/man/1/sh
+value	/man/1/sh-alphabet
+value	/man/1/sh-csv
+value	/man/1/sh-expr
+value	/man/1/sh-file2chan
+value	/man/1/sh-sexprs
+value	/man/1/sh-std
+value	/man/1/sh-string
+value	/man/1/sh-tk
+value	/man/1/strings
+value	/man/1/tail
+value	/man/1/tkcmd
+value	/man/1/tktester
+value	/man/1/tr
+value	/man/1/unicode
+value	/man/1/wm
+value	/man/1/yacc
+value	/man/1/zeros
+value	/man/10/2c
+value	/man/10/a.out
+value	/man/10/acid
+value	/man/10/atoi
+value	/man/10/dev
+value	/man/10/devattach
+value	/man/10/dmainit
+value	/man/10/dynld
+value	/man/10/error
+value	/man/10/inb
+value	/man/10/inm
+value	/man/10/intrenable
+value	/man/10/kbdputc
+value	/man/10/kproc
+value	/man/10/lock
+value	/man/10/memory
+value	/man/10/mk
+value	/man/10/newchan
+value	/man/10/plan9.ini
+value	/man/10/print
+value	/man/10/qio
+value	/man/10/readnum
+value	/man/10/splhi
+value	/man/10/styx
+value	/man/10/styxserver
+value	/man/2/0intro
+value	/man/2/alphabet-intro
+value	/man/2/asn1
+value	/man/2/attrdb
+value	/man/2/bufio
+value	/man/2/cfg
+value	/man/2/complete
+value	/man/2/convcs
+value	/man/2/crc
+value	/man/2/csv
+value	/man/2/daytime
+value	/man/2/dbm
+value	/man/2/debug
+value	/man/2/dhcpclient
+value	/man/2/dial
+value	/man/2/dialog
+value	/man/2/dict
+value	/man/2/dis
+value	/man/2/diskblocks
+value	/man/2/disks
+value	/man/2/draw-0intro
+value	/man/2/draw-context
+value	/man/2/draw-display
+value	/man/2/draw-image
+value	/man/2/draw-rect
+value	/man/2/draw-screen
+value	/man/2/env
+value	/man/2/exception
+value	/man/2/factotum
+value	/man/2/filter
+value	/man/2/format
+value	/man/2/hash
+value	/man/2/ida
+value	/man/2/ip
+value	/man/2/json
+value	/man/2/keyring-certtostr
+value	/man/2/keyring-getstring
+value	/man/2/keyring-ipint
+value	/man/2/keyring-sha1
+value	/man/2/lists
+value	/man/2/math-0intro
+value	/man/2/math-export
+value	/man/2/math-fp
+value	/man/2/msgio
+value	/man/2/palmfile
+value	/man/2/plumbmsg
+value	/man/2/popup
+value	/man/2/prefab-compound
+value	/man/2/prof
+value	/man/2/pslib
+value	/man/2/readdir
+value	/man/2/registries
+value	/man/2/rfc822
+value	/man/2/scsiio
+value	/man/2/secstore
+value	/man/2/security-0intro
+value	/man/2/security-auth
+value	/man/2/selectfile
+value	/man/2/sexprs
+value	/man/2/sh
+value	/man/2/spki
+value	/man/2/spki-verifier
+value	/man/2/spree
+value	/man/2/spree-cardlib
+value	/man/2/string
+value	/man/2/stringinttab
+value	/man/2/styx
+value	/man/2/styxpersist
+value	/man/2/styxservers
+value	/man/2/styxservers-nametree
+value	/man/2/sys-0intro
+value	/man/2/sys-bind
+value	/man/2/sys-dial
+value	/man/2/sys-dirread
+value	/man/2/sys-export
+value	/man/2/sys-fauth
+value	/man/2/sys-fversion
+value	/man/2/sys-millisec
+value	/man/2/sys-open
+value	/man/2/sys-pctl
+value	/man/2/sys-print
+value	/man/2/sys-read
+value	/man/2/sys-seek
+value	/man/2/sys-stat
+value	/man/2/sys-utfbytes
+value	/man/2/sys-werrstr
+value	/man/2/timers
+value	/man/2/ubfa
+value	/man/2/w3c-css
+value	/man/2/w3c-uris
+value	/man/2/w3c-xpointers
+value	/man/2/wait
+value	/man/2/wmsrv
+value	/man/2/xml
+value	/man/3/audio
+value	/man/3/cmd
+value	/man/3/cons
+value	/man/3/dbg
+value	/man/3/env
+value	/man/3/ether
+value	/man/3/flash
+value	/man/3/ftl
+value	/man/3/gpio
+value	/man/3/i2c
+value	/man/3/ip
+value	/man/3/lpt
+value	/man/3/pnp
+value	/man/3/pointer
+value	/man/3/prof
+value	/man/3/prog
+value	/man/3/sign
+value	/man/3/switch
+value	/man/3/tls
+value	/man/3/tv
+value	/man/3/vga
+value	/man/4/acme
+value	/man/4/dbfs
+value	/man/4/factotum
+value	/man/4/import
+value	/man/4/keysrv
+value	/man/4/registry
+value	/man/5/0intro
+value	/man/5/open
+value	/man/5/read
+value	/man/5/stat
+value	/man/5/version
+value	/man/5/walk
+value	/man/6/attrdb
+value	/man/6/colour
+value	/man/6/dis
+value	/man/6/font
+value	/man/6/image
+value	/man/6/json
+value	/man/6/keyboard
+value	/man/6/keytext
+value	/man/6/man
+value	/man/6/namespace
+value	/man/6/ndb
+value	/man/6/plumbing
+value	/man/6/proto
+value	/man/6/sbl
+value	/man/6/sexprs
+value	/man/6/ubfa
+value	/man/6/utf
+value	/man/7/cddb
+value	/man/7/db
+value	/man/8/ai2key
+value	/man/8/bootpd
+value	/man/8/changelogin
+value	/man/8/cs
+value	/man/8/getauthinfo
+value	/man/8/init
+value	/man/8/ping
+value	/man/8/prep
+value	/man/8/rstyxd
+value	/man/8/styxchat
+value	/man/9/0intro
+value	/man/9/bind
+value	/man/9/button
+value	/man/9/canvas
+value	/man/9/checkbutton
+value	/man/9/choicebutton
+value	/man/9/entry
+value	/man/9/frame
+value	/man/9/grid
+value	/man/9/image
+value	/man/9/label
+value	/man/9/listbox
+value	/man/9/menu
+value	/man/9/menubutton
+value	/man/9/options
+value	/man/9/pack
+value	/man/9/panel
+value	/man/9/radiobutton
+value	/man/9/scale
+value	/man/9/scrollbar
+value	/man/9/send
+value	/man/9/text
+value	/man/9/types
+value	/man/9/variable
+value's	/man/2/asn1
+value.bigint	/man/2/asn1
+value.bitstring	/man/2/asn1
+value.bool	/man/2/asn1
+value.eoc	/man/2/asn1
+value.function	/man/2/w3c-css
+value.hexcolour	/man/2/w3c-css
+value.int	/man/2/asn1
+value.null	/man/2/asn1
+value.objid	/man/2/asn1
+value.octets	/man/2/asn1
+value.other	/man/2/asn1
+value.real	/man/2/asn1
+value.rgb	/man/2/w3c-css
+value.s	/man/2/alphabet-intro
+value.seq	/man/2/asn1
+value.set	/man/2/asn1
+value.string	/man/2/asn1
+value.unit	/man/2/w3c-css
+valuecount	/man/9/choicebutton
+valued	/man/1/fs
+valued	/man/2/dial
+valued	/man/2/sys-dial
+valued	/man/2/sys-dup
+valued	/man/6/ndb
+valued	/man/6/utf
+valued	/man/8/mkfs
+values	/man/1/calc
+values	/man/1/charon
+values	/man/1/chmod
+values	/man/1/deb
+values	/man/1/emu
+values	/man/1/env
+values	/man/1/fc
+values	/man/1/fs
+values	/man/1/grid-query
+values	/man/1/look
+values	/man/1/m4
+values	/man/1/mash
+values	/man/1/mash-tk
+values	/man/1/math-misc
+values	/man/1/mk
+values	/man/1/sh
+values	/man/1/sh-alphabet
+values	/man/1/sh-csv
+values	/man/1/sh-expr
+values	/man/1/sh-sexprs
+values	/man/1/sh-tk
+values	/man/1/sort
+values	/man/1/stack
+values	/man/1/tcs
+values	/man/1/unicode
+values	/man/1/wm-misc
+values	/man/1/xd
+values	/man/10/2c
+values	/man/10/2l
+values	/man/10/a.out
+values	/man/10/acid
+values	/man/10/c2l
+values	/man/10/dev
+values	/man/10/devattach
+values	/man/10/dynld
+values	/man/10/inb
+values	/man/10/mk
+values	/man/10/newchan
+values	/man/10/odbc
+values	/man/10/plan9.ini
+values	/man/10/print
+values	/man/10/qio
+values	/man/10/readnum
+values	/man/10/styx
+values	/man/2/0intro
+values	/man/2/alphabet-intro
+values	/man/2/asn1
+values	/man/2/attrdb
+values	/man/2/bufio-chanfill
+values	/man/2/cfg
+values	/man/2/convcs
+values	/man/2/csv
+values	/man/2/daytime
+values	/man/2/dbm
+values	/man/2/dhcpclient
+values	/man/2/dis
+values	/man/2/disks
+values	/man/2/draw-0intro
+values	/man/2/draw-display
+values	/man/2/draw-image
+values	/man/2/factotum
+values	/man/2/format
+values	/man/2/geodesy
+values	/man/2/hash
+values	/man/2/ida
+values	/man/2/ip
+values	/man/2/json
+values	/man/2/keyring-0intro
+values	/man/2/lists
+values	/man/2/math-export
+values	/man/2/math-fp
+values	/man/2/palmfile
+values	/man/2/popup
+values	/man/2/readdir
+values	/man/2/registries
+values	/man/2/rfc822
+values	/man/2/secstore
+values	/man/2/security-0intro
+values	/man/2/sexprs
+values	/man/2/sh
+values	/man/2/spki
+values	/man/2/spree
+values	/man/2/spree-cardlib
+values	/man/2/styx
+values	/man/2/styxservers
+values	/man/2/sys-0intro
+values	/man/2/sys-fversion
+values	/man/2/sys-open
+values	/man/2/sys-stat
+values	/man/2/ubfa
+values	/man/2/volume
+values	/man/2/w3c-css
+values	/man/2/w3c-uris
+values	/man/2/w3c-xpointers
+values	/man/3/audio
+values	/man/3/cons
+values	/man/3/dbg
+values	/man/3/draw
+values	/man/3/eia
+values	/man/3/env
+values	/man/3/flash
+values	/man/3/fs
+values	/man/3/ftl
+values	/man/3/gpio
+values	/man/3/ip
+values	/man/3/pnp
+values	/man/3/touch
+values	/man/3/tv
+values	/man/3/vga
+values	/man/3/vid
+values	/man/4/factotum
+values	/man/4/registry
+values	/man/5/stat
+values	/man/6/audio
+values	/man/6/auth
+values	/man/6/colour
+values	/man/6/dis
+values	/man/6/font
+values	/man/6/image
+values	/man/6/json
+values	/man/6/keyboard
+values	/man/6/keytext
+values	/man/6/login
+values	/man/6/man
+values	/man/6/namespace
+values	/man/6/ndb
+values	/man/6/plumbing
+values	/man/6/sbl
+values	/man/6/sexprs
+values	/man/6/ubfa
+values	/man/6/utf
+values	/man/8/ai2key
+values	/man/8/collabsrv
+values	/man/8/dhcp
+values	/man/8/styxchat
+values	/man/9/button
+values	/man/9/canvas
+values	/man/9/checkbutton
+values	/man/9/choicebutton
+values	/man/9/entry
+values	/man/9/frame
+values	/man/9/grid
+values	/man/9/label
+values	/man/9/listbox
+values	/man/9/menu
+values	/man/9/menubutton
+values	/man/9/options
+values	/man/9/pack
+values	/man/9/panel
+values	/man/9/radiobutton
+values	/man/9/scale
+values	/man/9/scrollbar
+values	/man/9/send
+values	/man/9/text
+values	/man/9/types
+van	/man/2/math-linalg
+vanished	/man/2/plumbmsg
+vanishes	/man/1/collab-clients
+vanishes	/man/2/registries
+var	/man/1/acme
+var	/man/1/mk
+var	/man/1/sh-std
+var	/man/10/mk
+var	/man/2/env
+var	/man/2/w3c-xpointers
+var	/man/9/variable
+varargck	/man/10/2c
+varfont	/man/1/acme
+varfont	/man/4/acme
+variable	/man/1/0intro
+variable	/man/1/acme
+variable	/man/1/alphabet-fs
+variable	/man/1/calc
+variable	/man/1/dd
+variable	/man/1/deb
+variable	/man/1/emu
+variable	/man/1/env
+variable	/man/1/fs
+variable	/man/1/limbo
+variable	/man/1/listen
+variable	/man/1/mash
+variable	/man/1/mash-make
+variable	/man/1/math-misc
+variable	/man/1/mk
+variable	/man/1/sendmail
+variable	/man/1/sh
+variable	/man/1/sh-arg
+variable	/man/1/sh-file2chan
+variable	/man/1/sh-regex
+variable	/man/1/sh-std
+variable	/man/1/sh-string
+variable	/man/1/stack
+variable	/man/10/2c
+variable	/man/10/a.out
+variable	/man/10/acid
+variable	/man/10/inm
+variable	/man/10/kbdputc
+variable	/man/10/mk
+variable	/man/10/plan9.ini
+variable	/man/10/print
+variable	/man/10/qio
+variable	/man/10/sleep
+variable	/man/2/0intro
+variable	/man/2/asn1
+variable	/man/2/bufio
+variable	/man/2/convcs
+variable	/man/2/daytime
+variable	/man/2/diskblocks
+variable	/man/2/env
+variable	/man/2/plumbmsg
+variable	/man/2/sh
+variable	/man/2/spree-objstore
+variable	/man/2/sys-dial
+variable	/man/2/sys-dirread
+variable	/man/2/sys-self
+variable	/man/2/ubfa
+variable	/man/2/w3c-xpointers
+variable	/man/3/cons
+variable	/man/3/dbg
+variable	/man/3/ds
+variable	/man/3/env
+variable	/man/4/namespace
+variable	/man/5/0intro
+variable	/man/5/stat
+variable	/man/6/dis
+variable	/man/6/image
+variable	/man/6/proto
+variable	/man/6/sbl
+variable	/man/6/ubfa
+variable	/man/8/init
+variable	/man/8/prep
+variable	/man/9/0intro
+variable	/man/9/checkbutton
+variable	/man/9/choicebutton
+variable	/man/9/menu
+variable	/man/9/radiobutton
+variable	/man/9/send
+variable	/man/9/variable
+variable's	/man/9/radiobutton
+variables	/man/1/0intro
+variables	/man/1/acme
+variables	/man/1/alphabet-fs
+variables	/man/1/calc
+variables	/man/1/deb
+variables	/man/1/env
+variables	/man/1/fs
+variables	/man/1/limbo
+variables	/man/1/mash
+variables	/man/1/mash-make
+variables	/man/1/mash-tk
+variables	/man/1/mk
+variables	/man/1/sh
+variables	/man/1/sh-std
+variables	/man/1/stack
+variables	/man/1/yacc
+variables	/man/10/2c
+variables	/man/10/acid
+variables	/man/10/c2l
+variables	/man/10/error
+variables	/man/10/mk
+variables	/man/10/plan9.ini
+variables	/man/10/qlock
+variables	/man/2/debug
+variables	/man/2/draw-0intro
+variables	/man/2/env
+variables	/man/2/itslib
+variables	/man/2/math-0intro
+variables	/man/2/sh
+variables	/man/2/spree-objstore
+variables	/man/2/styxservers-nametree
+variables	/man/2/sys-0intro
+variables	/man/2/sys-pctl
+variables	/man/3/dbg
+variables	/man/3/env
+variables	/man/3/srv9
+variables	/man/4/9srvfs
+variables	/man/6/namespace
+variables	/man/6/plumbing
+variables	/man/6/sbl
+variables	/man/8/create
+variables	/man/9/menu
+variadic	/man/10/print
+variant	/man/1/0intro
+variant	/man/10/dynld
+variant	/man/10/plan9.ini
+variant	/man/10/qio
+variant	/man/2/asn1
+variant	/man/2/dis
+variant	/man/2/json
+variant	/man/2/sexprs
+variant	/man/2/spki
+variant	/man/2/ubfa
+variant	/man/4/dossrv
+variant	/man/6/keyboard
+variant	/man/6/sbl
+variant	/man/6/sexprs
+variant	/man/9/0intro
+variants	/man/1/chmod
+variants	/man/1/wish
+variants	/man/2/asn1
+variants	/man/2/sexprs
+variants	/man/2/spki
+variants	/man/3/ip
+variants	/man/3/touch
+variants	/man/6/sbl
+variation	/man/2/security-0intro
+variations	/man/1/chgrp
+varies	/man/10/devattach
+varies	/man/10/strcat
+varies	/man/2/alphabet-intro
+varies	/man/3/logfs
+varies	/man/3/prog
+varieties	/man/2/prefab-element
+variety	/man/1/charon
+variety	/man/1/mash-tk
+variety	/man/2/security-0intro
+variety	/man/4/acme
+various	/man/1/0intro
+various	/man/1/alphabet-abc
+various	/man/1/alphabet-grid
+various	/man/1/brutus
+various	/man/1/chgrp
+various	/man/1/cook
+various	/man/1/grid-register
+various	/man/1/grid-session
+various	/man/1/math-misc
+various	/man/1/tktester
+various	/man/1/units
+various	/man/10/0intro
+various	/man/10/plan9.ini
+various	/man/2/0intro
+various	/man/2/asn1
+various	/man/2/convcs
+various	/man/2/draw-font
+various	/man/2/draw-image
+various	/man/2/factotum
+various	/man/2/ida
+various	/man/2/imagefile
+various	/man/2/keyring-getmsg
+various	/man/2/msgio
+various	/man/2/prefab-environ
+various	/man/2/styx
+various	/man/2/styxservers
+various	/man/2/ubfa
+various	/man/2/w3c-xpointers
+various	/man/2/wmsrv
+various	/man/2/xml
+various	/man/3/gpio
+various	/man/3/mnt
+various	/man/3/pnp
+various	/man/3/rtc
+various	/man/3/srv
+various	/man/3/usb
+various	/man/4/factotum
+various	/man/4/spree
+various	/man/6/colour
+various	/man/6/dis
+various	/man/6/scancode
+various	/man/6/sexprs
+various	/man/8/collabsrv
+various	/man/8/cs
+various	/man/8/dhcp
+various	/man/9/button
+various	/man/9/canvas
+various	/man/9/checkbutton
+various	/man/9/choicebutton
+various	/man/9/entry
+various	/man/9/frame
+various	/man/9/label
+various	/man/9/listbox
+various	/man/9/menu
+various	/man/9/menubutton
+various	/man/9/options
+various	/man/9/panel
+various	/man/9/radiobutton
+various	/man/9/scale
+various	/man/9/scrollbar
+various	/man/9/text
+variously	/man/2/keyring-0intro
+varname	/man/1/sh
+vary	/man/10/master
+vary	/man/4/kfs
+varying	/man/10/plan9.ini
+varying	/man/2/keyring-0intro
+vc	/man/10/2c
+vcache	/man/1/vacget
+vcache	/man/4/vacfs
+vclk	/man/3/fpga
+vctl	/man/2/volume
+vd	/man/1/vacget
+vdpt	/man/1/vacget
+vector	/man/10/intrenable
+vector	/man/2/bloomfilter
+vector	/man/2/keyring-crypt
+vector	/man/2/math-linalg
+vector	/man/2/prefab-element
+vector	/man/5/stat
+vector	/man/6/login
+vectors	/man/10/intrenable
+vectors	/man/2/math-linalg
+vectors	/man/3/tls
+vendor	/man/2/dhcpclient
+vendor	/man/3/pnp
+vendor	/man/8/bootpd
+venti	/man/1/vacget
+venti	/man/2/venti
+venti	/man/4/vacfs
+venti	/man/8/prep
+venti.b	/man/2/venti
+venti.m	/man/2/venti
+ventisrv	/man/1/vacget
+ventisrv	/man/4/vacfs
+verb	/man/1/alphabet-fs
+verb	/man/1/alphabet-main
+verb	/man/1/fs
+verb	/man/10/print
+verb	/man/2/factotum
+verb	/man/2/itslib
+verb	/man/2/sys-print
+verb	/man/2/sys-werrstr
+verb	/man/4/factotum
+verb	/man/6/plumbing
+verb	/man/8/applylog
+verbatim	/man/1/alphabet-abc
+verbatim	/man/1/alphabet-grid
+verbatim	/man/6/sexprs
+verbatim	/man/9/1copyright
+verbose	/man/1/ar
+verbose	/man/1/secstore
+verbose	/man/1/vacget
+verbose	/man/10/iar
+verbose	/man/2/filter-deflate
+verbose	/man/2/prof
+verbose	/man/2/scsiio
+verbose	/man/2/sh
+verbosity	/man/1/itest
+verbosity	/man/1/sh-test
+verbosity	/man/2/itslib
+verbs	/man/1/alphabet-fs
+verbs	/man/1/fs
+verbs	/man/10/print
+verbs	/man/2/sys-print
+verbs	/man/4/factotum
+verbs	/man/6/plumbing
+verification	/man/2/spki-verifier
+verified	/man/2/security-0intro
+verified	/man/2/spki-verifier
+verified	/man/3/logfs
+verified	/man/4/keysrv
+verifier	/man/2/spki
+verifier	/man/2/spki-verifier
+verifier.b	/man/2/spki-verifier
+verifies	/man/1/avr
+verifies	/man/10/styx
+verifies	/man/2/factotum
+verify	/man/10/ar
+verify	/man/2/keyring-0intro
+verify	/man/2/keyring-sha1
+verify	/man/2/security-0intro
+verify	/man/2/security-login
+verify	/man/2/spki-verifier
+verify	/man/2/sys-open
+verify	/man/6/auth
+verify	/man/6/keys
+verify	/man/8/createsignerkey
+verify	/man/8/register
+verify	/man/8/signer
+verify.b	/man/8/signer
+verifying	/man/2/security-0intro
+vers	/man/10/newchan
+vers	/man/10/styxserver
+vers	/man/2/sys-stat
+vers	/man/3/tls
+vers	/man/8/httpd
+vers	/man/8/styxchat
+versa	/man/1/acme
+versa	/man/2/debug
+versa	/man/2/security-0intro
+verse	/man/1/cprof
+version	/man/1/asm
+version	/man/1/bind
+version	/man/1/blur
+version	/man/1/charon
+version	/man/1/cprof
+version	/man/1/ebook
+version	/man/1/echo
+version	/man/1/emu
+version	/man/1/ls
+version	/man/1/mprof
+version	/man/1/prof
+version	/man/1/sh-file2chan
+version	/man/1/sh-std
+version	/man/1/wm-misc
+version	/man/1/yacc
+version	/man/10/2c
+version	/man/10/9load
+version	/man/10/newchan
+version	/man/10/styx
+version	/man/10/styxserver
+version	/man/2/names
+version	/man/2/palmfile
+version	/man/2/security-0intro
+version	/man/2/styx
+version	/man/2/styxconv
+version	/man/2/styxservers
+version	/man/2/sys-fversion
+version	/man/2/sys-stat
+version	/man/2/w3c-xpointers
+version	/man/3/cons
+version	/man/3/draw
+version	/man/3/dynld
+version	/man/3/ether
+version	/man/3/ip
+version	/man/3/pbus
+version	/man/3/prog
+version	/man/3/tls
+version	/man/3/touch
+version	/man/4/namespace
+version	/man/4/registry
+version	/man/5/0intro
+version	/man/5/attach
+version	/man/5/stat
+version	/man/5/version
+version	/man/6/image
+version	/man/6/sbl
+version	/man/8/applylog
+version	/man/8/collabsrv
+version	/man/8/getauthinfo
+version	/man/8/httpd
+version	/man/8/rip
+version	/man/8/signer
+version	/man/8/styxchat
+version1	/man/8/rip
+version2	/man/1/charon
+versions	/man/1/charon
+versions	/man/1/keyboard
+versions	/man/1/tiny
+versions	/man/1/wm-sh
+versions	/man/10/conf
+versions	/man/10/styx
+versions	/man/2/keyring-sha1
+versions	/man/2/security-0intro
+versions	/man/3/ftl
+versions	/man/3/tls
+versions	/man/4/acme
+versions	/man/4/namespace
+versions	/man/6/dis
+versions	/man/6/sbl
+versions	/man/8/rdbgsrv
+vertical	/man/1/acme
+vertical	/man/1/charon
+vertical	/man/1/tiny
+vertical	/man/2/draw-font
+vertical	/man/2/draw-image
+vertical	/man/2/draw-rect
+vertical	/man/2/prefab-element
+vertical	/man/3/draw
+vertical	/man/4/acme
+vertical	/man/6/sexprs
+vertical	/man/9/canvas
+vertical	/man/9/grid
+vertical	/man/9/listbox
+vertical	/man/9/options
+vertical	/man/9/pack
+vertical	/man/9/scale
+vertical	/man/9/scrollbar
+vertical	/man/9/text
+vertically	/man/2/dividers
+vertically	/man/9/pack
+vertically	/man/9/text
+vesa	/man/3/vga
+vestige	/man/10/plan9.ini
+vfprint	/man/10/print
+vga	/man/10/conf
+vga	/man/10/plan9.ini
+vga	/man/3/vga
+vga	/man/6/colour
+vgactl	/man/3/vga
+vgaovl	/man/3/vga
+vgaovlctl	/man/3/vga
+vgasize	/man/10/plan9.ini
+via	/man/1/keyboard
+via	/man/1/mash-tk
+via	/man/1/miniterm
+via	/man/1/rcmd
+via	/man/1/tiny
+via	/man/1/wm
+via	/man/10/allocb
+via	/man/10/plan9.ini
+via	/man/2/ether
+via	/man/2/filter-slip
+via	/man/2/itslib
+via	/man/2/newns
+via	/man/2/plumbmsg
+via	/man/2/scsiio
+via	/man/2/sh
+via	/man/2/spki-verifier
+via	/man/2/sys-0intro
+via	/man/2/sys-dup
+via	/man/2/sys-pipe
+via	/man/2/tk
+via	/man/2/tkclient
+via	/man/2/volume
+via	/man/2/wmclient
+via	/man/2/wmsrv
+via	/man/3/cap
+via	/man/3/cmd
+via	/man/3/cons
+via	/man/3/draw
+via	/man/3/eia
+via	/man/3/ip
+via	/man/3/pipe
+via	/man/3/pnp
+via	/man/3/sd
+via	/man/3/touch
+via	/man/4/9srvfs
+via	/man/4/export
+via	/man/4/factotum
+via	/man/4/import
+via	/man/4/keyfs
+via	/man/4/keysrv
+via	/man/4/namespace
+via	/man/4/registry
+via	/man/6/colour
+via	/man/6/image
+via	/man/6/keys
+via	/man/6/users
+via	/man/7/db
+via	/man/7/dbsrv
+via	/man/8/dns
+via	/man/8/plumber
+via	/man/8/rdbgsrv
+via	/man/8/rstyxd
+via	/man/8/signer
+via	/man/9/0intro
+via	/man/9/canvas
+via	/man/9/entry
+via	/man/9/listbox
+via	/man/9/scale
+via	/man/9/text
+via	/man/9/update
+vice	/man/1/acme
+vice	/man/1/cprof
+vice	/man/2/debug
+vice	/man/2/security-0intro
+vid	/man/3/vid
+vidcram	/man/3/vid
+vidctl	/man/3/vid
+viddata	/man/3/vid
+video	/man/1/mux
+video	/man/10/kproc
+video	/man/10/plan9.ini
+video	/man/3/mpeg
+video	/man/3/pnp
+video	/man/3/tv
+video	/man/3/vid
+videotex	/man/1/miniterm
+videotexstring	/man/2/asn1
+view	/man/1/collab
+view	/man/1/cprof
+view	/man/1/dmview
+view	/man/1/ebook
+view	/man/1/grid-query
+view	/man/1/grid-session
+view	/man/1/wm-misc
+view	/man/8/styxchat
+view	/man/9/canvas
+view	/man/9/entry
+view	/man/9/listbox
+view	/man/9/options
+view	/man/9/scrollbar
+view	/man/9/see
+view	/man/9/text
+view.b	/man/1/wm-misc
+viewed	/man/1/0intro
+viewed	/man/1/dmview
+viewed	/man/1/ebook
+viewed	/man/1/grid-ns
+viewed	/man/1/grid-query
+viewed	/man/4/trfs
+viewer	/man/1/wm-misc
+viewer	/man/2/drawmux
+viewer's	/man/2/drawmux
+viewing	/man/1/charon
+viewing	/man/1/deb
+viewing	/man/1/dmview
+viewing	/man/1/grid-monitor
+views	/man/9/scrollbar
+villas	/man/6/attrdb
+virgil	/man/1/mux
+virgil	/man/2/virgil
+virgil	/man/8/virgild
+virgil.b	/man/2/virgil
+virgild	/man/1/mux
+virgild	/man/2/virgil
+virgild	/man/8/virgild
+virgild.b	/man/8/virgild
+virtual	/man/1/0intro
+virtual	/man/1/asm
+virtual	/man/1/emu
+virtual	/man/1/limbo
+virtual	/man/1/mk
+virtual	/man/1/sh-alphabet
+virtual	/man/1/time
+virtual	/man/10/0intro
+virtual	/man/10/9load
+virtual	/man/10/a.out
+virtual	/man/10/dev
+virtual	/man/10/dmainit
+virtual	/man/10/mk
+virtual	/man/2/0intro
+virtual	/man/2/dis
+virtual	/man/2/drawmux
+virtual	/man/2/wmsrv
+virtual	/man/3/dbg
+virtual	/man/3/ip
+virtual	/man/3/vga
+virtual	/man/4/acme
+virtual	/man/4/ramfile
+virtual	/man/6/dis
+virtual	/man/6/scancode
+virtual	/man/6/ubfa
+virtualised	/man/1/0intro
+virtualised	/man/1/wm-sh
+vis	/man/2/spree
+vis	/man/4/spree
+visibility	/man/2/spree
+visibility	/man/2/spree-cardlib
+visibility	/man/4/spree
+visible	/man/1/0intro
+visible	/man/1/acme
+visible	/man/1/cpu
+visible	/man/1/listen
+visible	/man/1/mash-tk
+visible	/man/1/mk
+visible	/man/1/sh-tk
+visible	/man/10/2c
+visible	/man/10/kproc
+visible	/man/10/mk
+visible	/man/2/draw-display
+visible	/man/2/draw-example
+visible	/man/2/draw-font
+visible	/man/2/draw-image
+visible	/man/2/draw-screen
+visible	/man/2/prefab-0intro
+visible	/man/2/prefab-compound
+visible	/man/2/prefab-element
+visible	/man/2/spree
+visible	/man/2/spree-cardlib
+visible	/man/2/sys-bind
+visible	/man/2/sys-pctl
+visible	/man/2/tkclient
+visible	/man/2/wmclient
+visible	/man/3/boot
+visible	/man/3/cmd
+visible	/man/3/cons
+visible	/man/3/dbg
+visible	/man/3/fpga
+visible	/man/3/fs
+visible	/man/3/tls
+visible	/man/3/usb
+visible	/man/4/0intro
+visible	/man/4/acme
+visible	/man/4/dossrv
+visible	/man/4/export
+visible	/man/4/ftpfs
+visible	/man/4/import
+visible	/man/4/spree
+visible	/man/4/tarfs
+visible	/man/4/trfs
+visible	/man/6/colour
+visible	/man/6/keys
+visible	/man/8/changelogin
+visible	/man/8/rdbgsrv
+visible	/man/9/canvas
+visible	/man/9/entry
+visible	/man/9/grab
+visible	/man/9/listbox
+visible	/man/9/menu
+visible	/man/9/options
+visible	/man/9/scrollbar
+visible	/man/9/see
+visible	/man/9/text
+visible	/man/9/update
+visiblestring	/man/2/asn1
+vision864	/man/3/vga
+vision964	/man/3/vga
+visited	/man/5/walk
+visual	/man/1/brutus
+visual	/man/1/deb
+visual	/man/1/mash
+visual	/man/1/mash-tk
+visual	/man/6/colour
+visual	/man/8/getauthinfo
+visually	/man/1/grid-monitor
+visually	/man/1/grid-ns
+visually	/man/4/grid-cpu
+vita	/man/9/0intro
+vita	/man/9/1copyright
+vitanuova	/man/1/webgrab
+vitanuova.com	/man/6/ndb
+vivido	/man/6/ndb
+vl	/man/10/2l
+vlevel	/man/1/itest
+vlong	/man/10/2c
+vlong	/man/10/atoi
+vlong	/man/10/dev
+vlong	/man/10/devattach
+vlong	/man/10/dynld
+vlong	/man/10/print
+vlong	/man/10/styx
+vlong	/man/10/styxserver
+vlong,int	/man/10/dynld
+vmail	/man/2/translate
+vmsgs	/man/2/venti
+vmware	/man/10/plan9.ini
+voice	/man/2/translate
+void	/man/1/alphabet-fs
+void	/man/1/fs
+void	/man/10/2c
+void	/man/10/allocb
+void	/man/10/atoi
+void	/man/10/c2l
+void	/man/10/conf
+void	/man/10/delay
+void	/man/10/dev
+void	/man/10/devattach
+void	/man/10/dmainit
+void	/man/10/dynld
+void	/man/10/error
+void	/man/10/eve
+void	/man/10/inb
+void	/man/10/intrenable
+void	/man/10/kbdputc
+void	/man/10/kproc
+void	/man/10/lock
+void	/man/10/malloc
+void	/man/10/memory
+void	/man/10/newchan
+void	/man/10/panic
+void	/man/10/print
+void	/man/10/qio
+void	/man/10/qlock
+void	/man/10/seconds
+void	/man/10/sleep
+void	/man/10/splhi
+void	/man/10/styx
+void	/man/10/styxserver
+void	/man/10/xalloc
+void	/man/6/colour
+volatile	/man/2/dhcpclient
+volatile	/man/3/rtc
+volatile	/man/3/tinyfs
+volatile	/man/4/namespace
+volatile	/man/8/prep
+volatile	/man/8/register
+voldn	/man/2/ir
+voldn	/man/2/volume
+volume	/man/1/0intro
+volume	/man/1/acme
+volume	/man/1/asm
+volume	/man/1/limbo
+volume	/man/1/mash
+volume	/man/1/mux
+volume	/man/1/secstore
+volume	/man/1/yacc
+volume	/man/10/2a
+volume	/man/10/sleep
+volume	/man/2/dis
+volume	/man/2/ir
+volume	/man/2/sys-self
+volume	/man/2/tk
+volume	/man/2/volume
+volume	/man/3/audio
+volume	/man/3/tv
+volume	/man/6/dis
+volume	/man/9/0intro
+volume.b	/man/2/volume
+volume.m	/man/2/volume
+volumectl	/man/2/volume
+volumes	/man/3/tv
+volup	/man/2/ir
+volup	/man/2/volume
+vouched	/man/8/ai2key
+vseprint	/man/10/print
+vsmprint	/man/10/print
+vsnprint	/man/10/print
+vsync	/man/3/vga
+vuaibclo	/man/1/ar
+vuaibclo	/man/10/iar
+vulnerable	/man/2/security-0intro
+vv	/man/1/webgrab
+vxxxx	/man/10/iar
+vy1	/man/3/tv
+w3c	/man/2/rfc822
+w3c	/man/2/w3c-css
+w3c	/man/2/w3c-uris
+w3c	/man/2/w3c-xpointers
+wait	/man/1/charon
+wait	/man/1/mash
+wait	/man/1/p
+wait	/man/1/sh
+wait	/man/1/tiny
+wait	/man/10/acid
+wait	/man/10/sleep
+wait	/man/2/command
+wait	/man/2/sh
+wait	/man/2/timers
+wait	/man/2/wait
+wait	/man/3/cmd
+wait	/man/3/cons
+wait	/man/3/dbg
+wait	/man/3/prog
+wait	/man/3/touch
+wait	/man/5/0intro
+wait	/man/5/flush
+waitfor	/man/1/stack
+waiting	/man/1/blur
+waiting	/man/1/p
+waiting	/man/1/sh
+waiting	/man/10/delay
+waiting	/man/10/qio
+waiting	/man/10/qlock
+waiting	/man/10/sleep
+waiting	/man/2/lock
+waiting	/man/2/sys-sleep
+waiting	/man/3/ip
+waiting	/man/3/pipe
+waiting	/man/3/touch
+waiting	/man/5/0intro
+waits	/man/1/dmview
+waits	/man/1/listen
+waits	/man/1/sh-tk
+waits	/man/1/stream
+waits	/man/1/tiny
+waits	/man/10/delay
+waits	/man/10/styxserver
+waits	/man/2/bufio-chanfill
+waits	/man/2/debug
+waits	/man/2/dialog
+waits	/man/2/plumbmsg
+waits	/man/2/virgil
+waits	/man/3/mpeg
+waits	/man/4/factotum
+waits	/man/8/styxchat
+waits	/man/8/touchcal
+wake	/man/10/kproc
+wake	/man/10/sleep
+wakeme	/man/3/dbg
+wakes	/man/10/qio
+wakes	/man/10/sleep
+wakeup	/man/10/intrenable
+wakeup	/man/10/sleep
+waking	/man/10/qio
+walk	/man/1/0intro
+walk	/man/1/alphabet-fs
+walk	/man/1/fs
+walk	/man/10/dev
+walk	/man/10/devattach
+walk	/man/10/styxserver
+walk	/man/2/styx
+walk	/man/2/styxservers
+walk	/man/2/styxservers-nametree
+walk	/man/5/0intro
+walk	/man/5/clunk
+walk	/man/5/open
+walk	/man/5/stat
+walk	/man/5/walk
+walked	/man/1/vacget
+walked	/man/2/prefab-element
+walked	/man/5/walk
+walking	/man/2/styxservers
+walking	/man/4/vacfs
+walking	/man/5/walk
+walkqid	/man/10/dev
+walkqid	/man/10/devattach
+walks	/man/10/dev
+walks	/man/10/devattach
+walks	/man/5/0intro
+walks	/man/5/walk
+wan	/man/3/pbus
+wanted	/man/1/sh-file2chan
+wanted	/man/3/prof
+wants	/man/2/security-0intro
+wants	/man/2/sh
+wants	/man/6/json
+warn	/man/10/2c
+warn	/man/2/itslib
+warning	/man/1/acme
+warning	/man/1/ar
+warning	/man/1/limbo
+warning	/man/1/mkdir
+warning	/man/10/2c
+warning	/man/10/c2l
+warning	/man/10/iar
+warning	/man/2/xml
+warning	/man/8/prep
+warning	/man/9/send
+warnings	/man/1/sh-test
+warnings	/man/10/c2l
+warnings	/man/2/fsproto
+warnings	/man/2/itslib
+warped	/man/3/touch
+warranties	/man/9/1copyright
+waserror	/man/10/error
+wasn't	/man/9/canvas
+wasted	/man/10/xalloc
+watch	/man/1/tail
+watch	/man/10/plan9.ini
+watch	/man/2/command
+watch	/man/2/spree
+watch	/man/2/spree-gather
+watch	/man/4/spree
+watchdog	/man/2/dhcpclient
+watches	/man/2/popup
+watches	/man/8/rip
+wav	/man/1/auplay
+wav2iaf	/man/1/auplay
+wav2iaf.b	/man/1/auplay
+wavelan	/man/10/plan9.ini
+wavelanpci	/man/10/plan9.ini
+ways	/man/1/0intro
+ways	/man/10/9load
+ways	/man/2/encoding
+ways	/man/2/keyring-0intro
+ways	/man/2/sh
+ways	/man/6/utf
+ways	/man/9/0intro
+ways	/man/9/button
+ways	/man/9/canvas
+ways	/man/9/checkbutton
+ways	/man/9/entry
+ways	/man/9/label
+ways	/man/9/listbox
+ways	/man/9/radiobutton
+wb.bit	/man/8/collabsrv
+wbsrv	/man/8/collabsrv
+wc	/man/1/freq
+wc	/man/1/sh-alphabet
+wc	/man/1/sum
+wc	/man/1/wc
+wc	/man/2/bufio-chanfill
+wc	/man/2/sys-file2chan
+wc.b	/man/1/wc
+wctl	/man/2/draw-context
+wd8003	/man/10/plan9.ini
+wd8013	/man/10/plan9.ini
+wday	/man/2/daytime
+wdir	/man/1/plumb
+wdir	/man/3/cmd
+weakness	/man/6/login
+wear	/man/4/kfs
+wearing	/man/3/ftl
+web	/man/1/charon
+web	/man/1/mux
+web	/man/1/webgrab
+web	/man/2/w3c-css
+web	/man/2/w3c-xpointers
+web	/man/6/ubfa
+web	/man/8/httpd
+webget	/man/1/charon
+webget	/man/1/webgrab
+webgrab	/man/1/webgrab
+webgrab.b	/man/1/webgrab
+webphone	/man/3/touch
+wed	/man/1/date
+weight	/man/9/grid
+weights	/man/9/grid
+werrstr	/man/10/dynld
+werrstr	/man/2/sys-werrstr
+wfd	/man/1/alphabet-main
+wfd	/man/1/sh-alphabet
+wfd	/man/2/alphabet-intro
+wfd	/man/2/disks
+wgs84	/man/2/geodesy
+wh	/man/10/odbc
+what's	/man/1/mk
+what's	/man/10/mk
+what's	/man/2/draw-example
+whatever	/man/1/acme
+whatever	/man/1/mash-tk
+whatever	/man/10/intrenable
+whatever	/man/3/draw
+whatever	/man/6/keyboard
+whatever	/man/9/canvas
+whatever	/man/9/pack
+whatever	/man/9/scrollbar
+whatis	/man/1/mash
+whatis	/man/1/sh
+whatis	/man/10/acid
+whatis	/man/2/sh
+whence	/man/6/colour
+whenever	/man/1/acme
+whenever	/man/1/mash
+whenever	/man/1/sh
+whenever	/man/1/sh-file2chan
+whenever	/man/1/toolbar
+whenever	/man/10/iar
+whenever	/man/10/styxserver
+whenever	/man/2/sys-0intro
+whenever	/man/2/sys-stat
+whenever	/man/2/tabs
+whenever	/man/3/pbus
+whenever	/man/4/dbfs
+whenever	/man/4/factotum
+whenever	/man/5/stat
+whenever	/man/8/dhcp
+whenever	/man/9/bind
+whenever	/man/9/button
+whenever	/man/9/canvas
+whenever	/man/9/checkbutton
+whenever	/man/9/choicebutton
+whenever	/man/9/grab
+whenever	/man/9/menu
+whenever	/man/9/menubutton
+whenever	/man/9/options
+whenever	/man/9/radiobutton
+whenever	/man/9/scale
+whenever	/man/9/text
+whereas	/man/2/asn1
+whereas	/man/4/spree
+whereas	/man/9/menu
+whereas	/man/9/text
+whereby	/man/1/charon
+whereby	/man/1/deb
+whereever	/man/1/alphabet-fs
+whereever	/man/1/fs
+whereever	/man/1/sh
+whereever	/man/1/sh-alphabet
+wherein	/man/4/acme
+whereupon	/man/1/sh-expr
+whereupon	/man/1/wm-sh
+whereupon	/man/4/spree
+wherever	/man/10/c2l
+whichever	/man/1/sh
+whichever	/man/10/memory
+whichever	/man/2/timers
+whichever	/man/3/pipe
+whichever	/man/9/grid
+whilst	/man/1/blur
+whilst	/man/1/grid-session
+whilst	/man/8/touchcal
+whimsical	/man/1/wm-misc
+whiteboard	/man/1/collab-clients
+whiteboard	/man/8/collabsrv
+whiteboard.b	/man/1/collab-clients
+whitespace	/man/1/brutus
+whitespace	/man/1/mash
+whitespace	/man/2/w3c-css
+whitespace	/man/6/sexprs
+whoever	/man/2/security-0intro
+wholly	/man/1/cprof
+whom	/man/2/security-0intro
+whom	/man/4/keyfs
+whom	/man/8/getauthinfo
+whoto	/man/2/spree
+wid	/man/1/sh-tk
+wide	/man/1/0intro
+wide	/man/1/charon
+wide	/man/1/sh-string
+wide	/man/1/wm-sh
+wide	/man/1/xd
+wide	/man/10/error
+wide	/man/2/bloomfilter
+wide	/man/2/keyring-0intro
+wide	/man/2/prefab-element
+wide	/man/2/w3c-css
+wide	/man/2/w3c-xpointers
+wide	/man/3/arch
+wide	/man/3/cons
+wide	/man/3/draw
+wide	/man/3/vga
+wide	/man/6/auth
+wide	/man/6/image
+wide	/man/6/login
+wide	/man/6/man
+wide	/man/6/utf
+wide	/man/8/cs
+wide	/man/8/ftl
+wide	/man/9/canvas
+wide	/man/9/text
+widely	/man/1/sh-csv
+widely	/man/2/security-0intro
+widen	/man/1/sh-string
+widened	/man/2/alphabet-intro
+wider	/man/2/draw-display
+wider	/man/4/registry
+wider	/man/9/text
+widest	/man/2/prefab-element
+widest	/man/9/choicebutton
+widget	/man/1/logwindow
+widget	/man/1/mprof
+widget	/man/1/prof
+widget	/man/1/tktester
+widget	/man/2/dialog
+widget	/man/2/dividers
+widget	/man/2/popup
+widget	/man/2/spree-cardlib
+widget	/man/2/tabs
+widget	/man/2/tk
+widget	/man/2/volume
+widget	/man/9/0intro
+widget	/man/9/bind
+widget	/man/9/button
+widget	/man/9/canvas
+widget	/man/9/checkbutton
+widget	/man/9/choicebutton
+widget	/man/9/entry
+widget	/man/9/focus
+widget	/man/9/frame
+widget	/man/9/label
+widget	/man/9/listbox
+widget	/man/9/menu
+widget	/man/9/menubutton
+widget	/man/9/options
+widget	/man/9/pack
+widget	/man/9/panel
+widget	/man/9/radiobutton
+widget	/man/9/scale
+widget	/man/9/scrollbar
+widget	/man/9/see
+widget	/man/9/text
+widget	/man/9/types
+widget's	/man/1/tktester
+widget's	/man/2/tk
+widget's	/man/9/checkbutton
+widget's	/man/9/entry
+widget's	/man/9/frame
+widget's	/man/9/listbox
+widget's	/man/9/options
+widget's	/man/9/radiobutton
+widget's	/man/9/text
+widget's	/man/9/types
+widgetname	/man/9/menubutton
+widgets	/man/1/mash-tk
+widgets	/man/1/tktester
+widgets	/man/2/dividers
+widgets	/man/2/popup
+widgets	/man/2/spree-cardlib
+widgets	/man/2/tk
+widgets	/man/2/tkclient
+widgets	/man/9/0intro
+widgets	/man/9/button
+widgets	/man/9/canvas
+widgets	/man/9/checkbutton
+widgets	/man/9/choicebutton
+widgets	/man/9/entry
+widgets	/man/9/frame
+widgets	/man/9/grid
+widgets	/man/9/image
+widgets	/man/9/label
+widgets	/man/9/listbox
+widgets	/man/9/menu
+widgets	/man/9/menubutton
+widgets	/man/9/options
+widgets	/man/9/pack
+widgets	/man/9/panel
+widgets	/man/9/radiobutton
+widgets	/man/9/scale
+widgets	/man/9/scrollbar
+widgets	/man/9/text
+widows	/man/6/man
+width	/man/1/9win
+width	/man/1/acme
+width	/man/1/brutus
+width	/man/1/charon
+width	/man/1/emu
+width	/man/1/fc
+width	/man/1/man
+width	/man/1/mc
+width	/man/1/wm-sh
+width	/man/1/xd
+width	/man/10/print
+width	/man/2/disks
+width	/man/2/dividers
+width	/man/2/draw-font
+width	/man/2/draw-image
+width	/man/2/prefab-element
+width	/man/2/print
+width	/man/2/pslib
+width	/man/2/spree-cardlib
+width	/man/2/sys-print
+width	/man/3/draw
+width	/man/3/flash
+width	/man/3/vga
+width	/man/4/acme
+width	/man/6/font
+width	/man/6/image
+width	/man/6/man
+width	/man/8/collabsrv
+width	/man/8/ftl
+width	/man/9/bind
+width	/man/9/button
+width	/man/9/canvas
+width	/man/9/checkbutton
+width	/man/9/choicebutton
+width	/man/9/entry
+width	/man/9/frame
+width	/man/9/grid
+width	/man/9/image
+width	/man/9/label
+width	/man/9/listbox
+width	/man/9/menubutton
+width	/man/9/options
+width	/man/9/pack
+width	/man/9/panel
+width	/man/9/radiobutton
+width	/man/9/scale
+width	/man/9/scrollbar
+width	/man/9/text
+width	/man/9/types
+widths	/man/1/mc
+wihch	/man/2/draw-screen
+wildcard	/man/1/filename
+wildcard	/man/1/mash-make
+wildcard	/man/6/dis
+wiley	/man/2/keyring-0intro
+wiley	/man/2/security-0intro
+wiley	/man/3/ssl
+willing	/man/2/dhcpclient
+willing	/man/8/logind
+willis	/man/6/attrdb
+willsend	/man/2/plumbmsg
+wilson	/man/6/attrdb
+wimagefile	/man/2/imagefile
+win	/man/1/acme
+win	/man/1/sh-tk
+win	/man/10/acid
+win	/man/2/dividers
+win	/man/2/draw-image
+win	/man/2/popup
+win	/man/4/acme
+win.b	/man/1/acme
+win.c	/man/3/draw
+win32vk	/man/6/scancode
+winctl	/man/1/sh-tk
+winctl	/man/1/wm-misc
+winctl.b	/man/1/wm-misc
+wind	/man/2/draw-image
+wind	/man/3/draw
+wind:int	/man/2/draw-image
+winding	/man/2/draw-image
+winding	/man/3/draw
+winding	/man/9/canvas
+window	/man/1/0intro
+window	/man/1/9win
+window	/man/1/acme
+window	/man/1/blur
+window	/man/1/brutus
+window	/man/1/charon
+window	/man/1/collab-clients
+window	/man/1/cprof
+window	/man/1/deb
+window	/man/1/dmview
+window	/man/1/ebook
+window	/man/1/filename
+window	/man/1/ftree
+window	/man/1/grid-monitor
+window	/man/1/grid-query
+window	/man/1/grid-session
+window	/man/1/keyboard
+window	/man/1/logwindow
+window	/man/1/man
+window	/man/1/mash
+window	/man/1/mash-tk
+window	/man/1/mc
+window	/man/1/miniterm
+window	/man/1/sh-tk
+window	/man/1/tkcmd
+window	/man/1/tktester
+window	/man/1/toolbar
+window	/man/1/wish
+window	/man/1/wm
+window	/man/1/wm-misc
+window	/man/1/wm-sh
+window	/man/10/acid
+window	/man/2/command
+window	/man/2/dialog
+window	/man/2/dividers
+window	/man/2/draw-context
+window	/man/2/draw-display
+window	/man/2/draw-example
+window	/man/2/draw-image
+window	/man/2/draw-screen
+window	/man/2/drawmux
+window	/man/2/mpeg
+window	/man/2/plumbmsg
+window	/man/2/prefab-0intro
+window	/man/2/prefab-compound
+window	/man/2/prof
+window	/man/2/selectfile
+window	/man/2/sys-pctl
+window	/man/2/tk
+window	/man/2/tkclient
+window	/man/2/wmclient
+window	/man/2/wmlib
+window	/man/2/wmsrv
+window	/man/3/draw
+window	/man/3/mpeg
+window	/man/3/prog
+window	/man/3/snarf
+window	/man/3/touch
+window	/man/3/tv
+window	/man/4/acme
+window	/man/4/factotum
+window	/man/4/namespace
+window	/man/6/image
+window	/man/8/getauthinfo
+window	/man/8/plumber
+window	/man/8/touchcal
+window	/man/9/bind
+window	/man/9/button
+window	/man/9/canvas
+window	/man/9/checkbutton
+window	/man/9/choicebutton
+window	/man/9/destroy
+window	/man/9/entry
+window	/man/9/focus
+window	/man/9/frame
+window	/man/9/grab
+window	/man/9/grid
+window	/man/9/label
+window	/man/9/listbox
+window	/man/9/lower
+window	/man/9/menu
+window	/man/9/menubutton
+window	/man/9/options
+window	/man/9/pack
+window	/man/9/panel
+window	/man/9/radiobutton
+window	/man/9/raise
+window	/man/9/scale
+window	/man/9/scrollbar
+window	/man/9/text
+window	/man/9/update
+window's	/man/1/acme
+window's	/man/1/sh-tk
+window's	/man/2/dialog
+window's	/man/2/draw-image
+window's	/man/2/selectfile
+window's	/man/2/tkclient
+window's	/man/2/wmclient
+window's	/man/4/acme
+window's	/man/9/canvas
+window's	/man/9/grab
+window's	/man/9/lower
+window's	/man/9/raise
+window's	/man/9/text
+window.open	/man/1/charon
+windowing	/man/1/cpu
+windowing	/man/1/sh-tk
+windowing	/man/2/command
+windowing	/man/8/plumber
+windows	/man/1/0intro
+windows	/man/1/9win
+windows	/man/1/acme
+windows	/man/1/auplay
+windows	/man/1/chmod
+windows	/man/1/deb
+windows	/man/1/emu
+windows	/man/1/tktester
+windows	/man/1/toolbar
+windows	/man/1/wm
+windows	/man/10/conf
+windows	/man/10/ntsrv
+windows	/man/10/odbc
+windows	/man/10/styxserver
+windows	/man/2/draw-0intro
+windows	/man/2/draw-context
+windows	/man/2/draw-image
+windows	/man/2/draw-screen
+windows	/man/2/drawmux
+windows	/man/2/prefab-compound
+windows	/man/2/tk
+windows	/man/2/tkclient
+windows	/man/2/wmclient
+windows	/man/2/wmsrv
+windows	/man/3/cmd
+windows	/man/3/draw
+windows	/man/3/fs
+windows	/man/4/acme
+windows	/man/4/dossrv
+windows	/man/4/namespace
+windows	/man/4/spree
+windows	/man/7/dbsrv
+windows	/man/8/prep
+windows	/man/9/canvas
+windows	/man/9/destroy
+windows	/man/9/grab
+windows	/man/9/grid
+windows	/man/9/pack
+windows	/man/9/text
+windows9x	/man/3/fs
+windows™	/man/6/scancode
+wine	/man/10/odbc
+winid	/man/1/sh-tk
+winm	/man/1/acme
+winname	/man/1/9win
+wins	/man/2/draw-screen
+wins	/man/2/wmsrv
+winterbottom	/man/10/acid
+wintitle	/man/1/grid-monitor
+wintitle	/man/1/sh-tk
+wired	/man/1/sh
+wired	/man/8/ftl
+wireless	/man/10/plan9.ini
+wise	/man/3/dbg
+wish	/man/1/0intro
+wish	/man/1/9win
+wish	/man/1/wish
+wish	/man/2/format
+wish	/man/2/ir
+wish	/man/2/math-fp
+wish	/man/2/spree-gather
+wish	/man/4/spree
+wish	/man/5/attach
+wish	/man/8/getauthinfo
+wish	/man/9/grid
+wish	/man/9/pack
+wish.b	/man/1/wish
+wishes	/man/1/collab-clients
+wishes	/man/10/dynld
+wishes	/man/2/security-0intro
+wishes	/man/2/spree-objstore
+wishes	/man/2/tkclient
+wishes	/man/2/wmclient
+wishes	/man/5/walk
+wishes	/man/8/svc
+wishing	/man/2/command
+wishing	/man/8/collabsrv
+wishpad	/man/1/wish
+withdrawn	/man/2/draw-image
+withtag	/man/9/canvas
+wk	/man/6/keyboard
+wl	/man/2/dividers
+wlock	/man/10/qlock
+wm	/man/1/0intro
+wm	/man/1/brutus
+wm	/man/1/calendar
+wm	/man/1/charon
+wm	/man/1/collab
+wm	/man/1/collab-clients
+wm	/man/1/cook
+wm	/man/1/cprof
+wm	/man/1/deb
+wm	/man/1/dmview
+wm	/man/1/emu
+wm	/man/1/filename
+wm	/man/1/ftree
+wm	/man/1/keyboard
+wm	/man/1/logon
+wm	/man/1/logwindow
+wm	/man/1/man
+wm	/man/1/mash-tk
+wm	/man/1/mc
+wm	/man/1/miniterm
+wm	/man/1/mprof
+wm	/man/1/mux
+wm	/man/1/plumb
+wm	/man/1/prof
+wm	/man/1/ps
+wm	/man/1/tktester
+wm	/man/1/toolbar
+wm	/man/1/wish
+wm	/man/1/wm
+wm	/man/1/wm-misc
+wm	/man/1/wm-sh
+wm	/man/2/command
+wm	/man/2/debug
+wm	/man/2/dialog
+wm	/man/2/dis
+wm	/man/2/draw-context
+wm	/man/2/drawmux
+wm	/man/2/selectfile
+wm	/man/2/sys-pctl
+wm	/man/2/tk
+wm	/man/2/tkclient
+wm	/man/2/wmclient
+wm	/man/2/wmlib
+wm	/man/2/wmsrv
+wm	/man/3/pointer
+wm	/man/3/sign
+wm	/man/3/snarf
+wm	/man/3/srv
+wm	/man/4/factotum
+wm	/man/4/logfile
+wm	/man/4/namespace
+wm	/man/6/keyboard
+wm	/man/6/namespace
+wm	/man/6/plumbing
+wm	/man/6/sbl
+wm	/man/8/getauthinfo
+wm	/man/8/plumber
+wm	/man/8/touchcal
+wm.b	/man/1/wm
+wm.dis	/man/1/emu
+wmchan	/man/8/plumber
+wmclient	/man/1/toolbar
+wmclient	/man/1/wm
+wmclient	/man/2/wmclient
+wmclient	/man/2/wmlib
+wmclient	/man/2/wmsrv
+wmclient.b	/man/2/wmclient
+wmclient.b	/man/2/wmlib
+wmclient.m	/man/2/wmclient
+wmcontext	/man/1/wm
+wmcontext	/man/2/draw-context
+wmcontext	/man/2/tk
+wmcontext	/man/2/wmclient
+wmcontext	/man/2/wmlib
+wmcontext	/man/2/wmsrv
+wmcontext.images	/man/2/wmsrv
+wmctl	/man/2/tkclient
+wmctl	/man/2/wmclient
+wmctl	/man/2/wmlib
+wmexport	/man/2/wmsrv
+wmgr	/man/2/draw-context
+wmlib	/man/1/filename
+wmlib	/man/1/sh-tk
+wmlib	/man/1/wish
+wmlib	/man/2/command
+wmlib	/man/2/dialog
+wmlib	/man/2/draw-0intro
+wmlib	/man/2/draw-context
+wmlib	/man/2/selectfile
+wmlib	/man/2/tabs
+wmlib	/man/2/tk
+wmlib	/man/2/translate
+wmlib	/man/2/wmlib
+wmlib	/man/2/wmsrv
+wmlib	/man/9/0intro
+wmlib.m	/man/2/wmlib
+wmmash	/man/1/mash-tk
+wmrect	/man/1/wm
+wmrect	/man/2/tk
+wmsetup	/man/1/toolbar
+wmsh	/man/2/plumbmsg
+wmsrv	/man/2/wmsrv
+wmsrv's	/man/2/wmsrv
+wmsrv.b	/man/2/wmsrv
+wmsrv.m	/man/2/wmsrv
+wname	/man/10/styx
+wname	/man/5/0intro
+wname	/man/5/walk
+won't	/man/9/image
+won't	/man/9/scale
+wordend	/man/9/text
+wordstart	/man/9/text
+wordval	/man/2/rfc822
+wordy	/man/1/alphabet-abc
+wordy	/man/1/alphabet-grid
+workdir	/man/1/plumb
+workdir	/man/1/pwd
+workdir	/man/2/names
+workdir	/man/2/plumbmsg
+workdir	/man/2/sys-0intro
+workdir	/man/2/sys-fd2path
+workdir	/man/2/workdir
+workdir.b	/man/2/workdir
+workdir.m	/man/2/workdir
+worked	/man/1/blur
+worker	/man/1/alphabet-abc
+worker	/man/1/alphabet-grid
+workflow	/man/1/alphabet-abc
+workflow	/man/1/alphabet-grid
+working	/man/1/acme
+working	/man/1/blur
+working	/man/1/cd
+working	/man/1/itest
+working	/man/1/limbo
+working	/man/1/plumb
+working	/man/1/pwd
+working	/man/1/vacget
+working	/man/2/ip
+working	/man/2/sexprs
+working	/man/2/spree
+working	/man/2/sys-0intro
+working	/man/2/sys-chdir
+working	/man/2/sys-pctl
+working	/man/2/workdir
+working	/man/3/cmd
+working	/man/3/prog
+working	/man/6/namespace
+working	/man/6/plumbing
+working	/man/6/sexprs
+works	/man/1/blur
+works	/man/1/cp
+works	/man/1/sh-csv
+works	/man/1/sh-sexprs
+works	/man/1/units
+works	/man/10/acid
+works	/man/10/plan9.ini
+works	/man/10/print
+works	/man/2/dial
+works	/man/2/disks
+works	/man/2/exception
+works	/man/2/hash
+works	/man/2/scsiio
+works	/man/4/ftpfs
+works	/man/6/image
+works	/man/6/namespace
+works	/man/6/ndb
+works	/man/8/ping
+works	/man/8/prep
+workshop	/man/2/ubfa
+workshop	/man/6/ubfa
+worlds	/man/3/fs
+worrying	/man/1/sh-regex
+worse	/man/1/miniterm
+worth	/man/1/sh-std
+worth	/man/10/qio
+worth	/man/9/text
+wp	/man/10/allocb
+wq	/man/10/acid
+wqid	/man/10/styx
+wqid	/man/5/0intro
+wrap	/man/1/deb
+wrap	/man/2/print
+wrap	/man/4/archfs
+wrap	/man/8/create
+wrap	/man/9/text
+wrap.b	/man/8/create
+wrapped	/man/10/odbc
+wrapped	/man/2/print
+wrapped	/man/9/text
+wrappers	/man/1/wm-sh
+wrapping	/man/1/cook
+wrapping	/man/9/text
+wraps	/man/9/text
+wreq	/man/2/tk
+wreq	/man/2/tkclient
+writable	/man/1/ftest
+writable	/man/2/styxservers
+writable	/man/3/ftl
+writable	/man/4/acme
+writable	/man/6/keys
+writable	/man/8/bootpd
+write	/man/1/0intro
+write	/man/1/acme
+write	/man/1/alphabet-fs
+write	/man/1/alphabet-main
+write	/man/1/bind
+write	/man/1/charon
+write	/man/1/chmod
+write	/man/1/cp
+write	/man/1/fs
+write	/man/1/ftest
+write	/man/1/grid-monitor
+write	/man/1/kill
+write	/man/1/limbo
+write	/man/1/ls
+write	/man/1/mash
+write	/man/1/mdb
+write	/man/1/mkdir
+write	/man/1/mv
+write	/man/1/rm
+write	/man/1/sh-file2chan
+write	/man/1/vacget
+write	/man/1/zeros
+write	/man/10/5cv
+write	/man/10/allocb
+write	/man/10/dev
+write	/man/10/devattach
+write	/man/10/dmainit
+write	/man/10/eve
+write	/man/10/kstrip
+write	/man/10/odbc
+write	/man/10/parsecmd
+write	/man/10/print
+write	/man/10/qio
+write	/man/10/qlock
+write	/man/10/styxserver
+write	/man/2/bufio
+write	/man/2/bufio-chanfill
+write	/man/2/csv
+write	/man/2/dbm
+write	/man/2/dial
+write	/man/2/diskblocks
+write	/man/2/draw-context
+write	/man/2/draw-display
+write	/man/2/draw-image
+write	/man/2/drawmux
+write	/man/2/filter
+write	/man/2/imagefile
+write	/man/2/json
+write	/man/2/keyring-0intro
+write	/man/2/keyring-auth
+write	/man/2/plumbmsg
+write	/man/2/spree
+write	/man/2/styx
+write	/man/2/styxflush
+write	/man/2/styxpersist
+write	/man/2/styxservers
+write	/man/2/sys-0intro
+write	/man/2/sys-file2chan
+write	/man/2/sys-open
+write	/man/2/sys-pipe
+write	/man/2/sys-read
+write	/man/2/sys-remove
+write	/man/2/sys-stat
+write	/man/2/ubfa
+write	/man/2/venti
+write	/man/3/boot
+write	/man/3/cap
+write	/man/3/cmd
+write	/man/3/cons
+write	/man/3/dbg
+write	/man/3/draw
+write	/man/3/ds
+write	/man/3/eia
+write	/man/3/ether
+write	/man/3/flash
+write	/man/3/fpga
+write	/man/3/fs
+write	/man/3/ftl
+write	/man/3/gpio
+write	/man/3/i2c
+write	/man/3/indir
+write	/man/3/ip
+write	/man/3/lpt
+write	/man/3/mnt
+write	/man/3/mpeg
+write	/man/3/pbus
+write	/man/3/pipe
+write	/man/3/pnp
+write	/man/3/prof
+write	/man/3/sd
+write	/man/3/sign
+write	/man/3/srv
+write	/man/3/ssl
+write	/man/3/tinyfs
+write	/man/3/tls
+write	/man/3/usb
+write	/man/3/vid
+write	/man/4/acme
+write	/man/4/dbfs
+write	/man/4/factotum
+write	/man/4/iostats
+write	/man/4/keysrv
+write	/man/4/logfile
+write	/man/4/memfs
+write	/man/4/palmsrv
+write	/man/4/ramfile
+write	/man/4/registry
+write	/man/4/spree
+write	/man/5/0intro
+write	/man/5/attach
+write	/man/5/open
+write	/man/5/read
+write	/man/5/remove
+write	/man/5/stat
+write	/man/6/audio
+write	/man/6/json
+write	/man/6/users
+write	/man/7/db
+write	/man/8/bootpd
+write	/man/8/collabsrv
+write	/man/8/cs
+write	/man/8/dhcp
+write	/man/8/dns
+write	/man/8/kfscmd
+write	/man/8/mkfs
+write	/man/8/prep
+write.2	/man/10/mk
+writeauthinfo	/man/2/keyring-0intro
+writeauthinfo	/man/2/keyring-auth
+writeauthinfo	/man/2/security-login
+writecmd	/man/1/sh-file2chan
+writeimage	/man/2/draw-display
+writeimage	/man/2/imagefile
+writeimage	/man/2/pslib
+writejson	/man/2/json
+writepixels	/man/2/draw-image
+writepixels:fn	/man/2/draw-image
+writer	/man/10/qio
+writer	/man/10/qlock
+writer	/man/2/bufio-chanfill
+writer	/man/3/ds
+writer	/man/3/eia
+writer	/man/4/lockfs
+writers	/man/1/cp
+writers	/man/1/sh
+writers	/man/1/sh-file2chan
+writers	/man/1/timestamp
+writers	/man/10/qio
+writers	/man/10/qlock
+writers	/man/3/pipe
+writes	/man/1/alphabet-abc
+writes	/man/1/alphabet-fs
+writes	/man/1/alphabet-grid
+writes	/man/1/alphabet-main
+writes	/man/1/auplay
+writes	/man/1/blur
+writes	/man/1/cat
+writes	/man/1/crypt
+writes	/man/1/disdep
+writes	/man/1/du
+writes	/man/1/echo
+writes	/man/1/fs
+writes	/man/1/gettar
+writes	/man/1/m4
+writes	/man/1/netkey
+writes	/man/1/read
+writes	/man/1/runas
+writes	/man/1/sh-file2chan
+writes	/man/1/sort
+writes	/man/1/stack
+writes	/man/1/stream
+writes	/man/1/tee
+writes	/man/1/timestamp
+writes	/man/1/vacget
+writes	/man/1/wc
+writes	/man/1/yacc
+writes	/man/1/zeros
+writes	/man/10/5coff
+writes	/man/10/dev
+writes	/man/10/devattach
+writes	/man/10/panic
+writes	/man/10/print
+writes	/man/10/qio
+writes	/man/2/bufio
+writes	/man/2/dbm
+writes	/man/2/dhcpclient
+writes	/man/2/draw-example
+writes	/man/2/draw-image
+writes	/man/2/factotum
+writes	/man/2/format
+writes	/man/2/json
+writes	/man/2/keyring-auth
+writes	/man/2/keyring-getstring
+writes	/man/2/mpeg
+writes	/man/2/msgio
+writes	/man/2/plumbmsg
+writes	/man/2/pslib
+writes	/man/2/spree
+writes	/man/2/styxpersist
+writes	/man/2/sys-0intro
+writes	/man/2/sys-iounit
+writes	/man/2/sys-pipe
+writes	/man/2/sys-print
+writes	/man/2/sys-read
+writes	/man/2/tftp
+writes	/man/2/ubfa
+writes	/man/2/venti
+writes	/man/3/arch
+writes	/man/3/audio
+writes	/man/3/boot
+writes	/man/3/cmd
+writes	/man/3/ds
+writes	/man/3/flash
+writes	/man/3/ftl
+writes	/man/3/i2c
+writes	/man/3/i82365
+writes	/man/3/ip
+writes	/man/3/pipe
+writes	/man/3/snarf
+writes	/man/3/ssl
+writes	/man/3/tls
+writes	/man/3/usb
+writes	/man/3/vid
+writes	/man/4/acme
+writes	/man/4/dbfs
+writes	/man/4/factotum
+writes	/man/4/iostats
+writes	/man/4/keysrv
+writes	/man/4/logfile
+writes	/man/4/spree
+writes	/man/5/0intro
+writes	/man/5/stat
+writes	/man/8/ai2key
+writes	/man/8/changelogin
+writes	/man/8/dhcp
+writes	/man/8/mkfs
+writes	/man/8/rdbgsrv
+writes	/man/8/signer
+writes	/man/8/styxchat
+writes	/man/8/styxmon
+writes	/man/8/touchcal
+writeubf	/man/2/ubfa
+writing	/man/1/alphabet-main
+writing	/man/1/charon
+writing	/man/1/crypt
+writing	/man/1/kill
+writing	/man/1/limbo
+writing	/man/1/sh
+writing	/man/1/sh-alphabet
+writing	/man/1/timestamp
+writing	/man/1/vacget
+writing	/man/1/wm-sh
+writing	/man/10/odbc
+writing	/man/10/plan9.ini
+writing	/man/10/qio
+writing	/man/2/bufio
+writing	/man/2/dbm
+writing	/man/2/dial
+writing	/man/2/diskblocks
+writing	/man/2/disks
+writing	/man/2/env
+writing	/man/2/imagefile
+writing	/man/2/keyring-crypt
+writing	/man/2/keyring-getmsg
+writing	/man/2/palmfile
+writing	/man/2/plumbmsg
+writing	/man/2/pslib
+writing	/man/2/security-0intro
+writing	/man/2/styxservers
+writing	/man/2/sys-0intro
+writing	/man/2/sys-bind
+writing	/man/2/sys-dial
+writing	/man/2/sys-export
+writing	/man/2/sys-file2chan
+writing	/man/2/sys-open
+writing	/man/2/sys-pipe
+writing	/man/2/sys-print
+writing	/man/2/wmsrv
+writing	/man/3/cap
+writing	/man/3/cmd
+writing	/man/3/cons
+writing	/man/3/draw
+writing	/man/3/ds
+writing	/man/3/dup
+writing	/man/3/env
+writing	/man/3/ether
+writing	/man/3/flash
+writing	/man/3/i82365
+writing	/man/3/ip
+writing	/man/3/kprof
+writing	/man/3/logfs
+writing	/man/3/lpt
+writing	/man/3/pipe
+writing	/man/3/plap
+writing	/man/3/pointer
+writing	/man/3/prog
+writing	/man/3/rtc
+writing	/man/3/sd
+writing	/man/3/sign
+writing	/man/3/snarf
+writing	/man/3/ssl
+writing	/man/3/tls
+writing	/man/3/touch
+writing	/man/3/vga
+writing	/man/4/acme
+writing	/man/4/dbfs
+writing	/man/4/factotum
+writing	/man/4/keyfs
+writing	/man/4/logfile
+writing	/man/4/palmsrv
+writing	/man/4/registry
+writing	/man/4/vacfs
+writing	/man/5/0intro
+writing	/man/5/read
+writing	/man/6/ubfa
+writing	/man/8/collabsrv
+writing	/man/8/mkfs
+writing	/man/8/rdbgsrv
+written	/man/1/0intro
+written	/man/1/acme
+written	/man/1/alphabet-fs
+written	/man/1/asm
+written	/man/1/auplay
+written	/man/1/calc
+written	/man/1/cook
+written	/man/1/fs
+written	/man/1/itest
+written	/man/1/logon
+written	/man/1/man
+written	/man/1/secstore
+written	/man/1/sh-file2chan
+written	/man/1/sh-std
+written	/man/1/timestamp
+written	/man/1/tiny
+written	/man/1/tktester
+written	/man/1/vacget
+written	/man/1/wc
+written	/man/1/webgrab
+written	/man/1/wm-sh
+written	/man/10/allocb
+written	/man/10/ar
+written	/man/10/dev
+written	/man/10/devattach
+written	/man/10/inb
+written	/man/10/kproc
+written	/man/10/kstrip
+written	/man/10/lock
+written	/man/10/master
+written	/man/10/odbc
+written	/man/10/parsecmd
+written	/man/10/print
+written	/man/10/qio
+written	/man/10/styxserver
+written	/man/10/xalloc
+written	/man/2/0intro
+written	/man/2/bufio
+written	/man/2/bufio-chanfill
+written	/man/2/dbm
+written	/man/2/dhcpclient
+written	/man/2/dial
+written	/man/2/draw-image
+written	/man/2/ether
+written	/man/2/format
+written	/man/2/keyring-0intro
+written	/man/2/keyring-getstring
+written	/man/2/msgio
+written	/man/2/palmfile
+written	/man/2/plumbmsg
+written	/man/2/registries
+written	/man/2/scsiio
+written	/man/2/secstore
+written	/man/2/security-ssl
+written	/man/2/styx
+written	/man/2/sys-0intro
+written	/man/2/sys-dial
+written	/man/2/sys-file2chan
+written	/man/2/sys-iounit
+written	/man/2/sys-pipe
+written	/man/2/sys-read
+written	/man/2/venti
+written	/man/2/xml
+written	/man/3/audio
+written	/man/3/boot
+written	/man/3/cmd
+written	/man/3/cons
+written	/man/3/draw
+written	/man/3/eia
+written	/man/3/flash
+written	/man/3/fpga
+written	/man/3/ftl
+written	/man/3/gpio
+written	/man/3/i2c
+written	/man/3/ip
+written	/man/3/logfs
+written	/man/3/lpt
+written	/man/3/mpeg
+written	/man/3/pbus
+written	/man/3/pipe
+written	/man/3/pnp
+written	/man/3/prof
+written	/man/3/prog
+written	/man/3/sign
+written	/man/3/snarf
+written	/man/3/ssl
+written	/man/3/touch
+written	/man/3/usb
+written	/man/3/vga
+written	/man/3/vid
+written	/man/4/9srvfs
+written	/man/4/acme
+written	/man/4/dbfs
+written	/man/4/factotum
+written	/man/4/keyfs
+written	/man/4/logfile
+written	/man/4/ramfile
+written	/man/4/registry
+written	/man/4/spree
+written	/man/5/attach
+written	/man/5/open
+written	/man/5/read
+written	/man/6/attrdb
+written	/man/6/audio
+written	/man/6/image
+written	/man/6/ndb
+written	/man/6/sbl
+written	/man/6/sexprs
+written	/man/8/collabsrv
+written	/man/8/cs
+written	/man/8/ftl
+written	/man/8/getauthinfo
+written	/man/8/httpd
+written	/man/8/prep
+written	/man/8/rstyxd
+written	/man/8/styxchat
+written	/man/9/1copyright
+written	/man/9/grid
+wrn	/man/1/sh-test
+wrong	/man/1/miniterm
+wrong	/man/10/print
+wrong	/man/2/attrdb
+wrong	/man/2/dhcpclient
+wrong	/man/3/ip
+wrong	/man/4/keysrv
+wrong	/man/9/variable
+wrtog	/man/3/usb
+ws1	/man/2/xml
+ws2	/man/2/xml
+wstat	/man/10/dev
+wstat	/man/10/devattach
+wstat	/man/10/styxserver
+wstat	/man/2/palmfile
+wstat	/man/2/styx
+wstat	/man/2/styxservers-nametree
+wstat	/man/2/sys-0intro
+wstat	/man/2/sys-stat
+wstat	/man/3/logfs
+wstat	/man/3/srv
+wstat	/man/4/kfs
+wstat	/man/5/0intro
+wstat	/man/5/stat
+wstat	/man/5/walk
+wstate	/man/2/styxservers-nametree
+wtype	/man/2/sh
+wunlock	/man/10/qlock
+ww	/man/1/limbo
+www	/man/1/webgrab
+www.innerhost.vitanuova.com	/man/1/webgrab
+www.json.org	/man/6/json
+www.minitelfr.com	/man/1/miniterm
+www.sics.se	/man/6/ubfa
+www.vitanuova.com	/man/1/webgrab
+www.w3.org	/man/2/w3c-css
+www.w3.org	/man/2/w3c-xpointers
+www.w3.org	/man/2/xml
+x,y	/man/9/canvas
+x.509.v3	/man/1/charon
+x.scrollbar	/man/9/options
+x.tab.h	/man/10/mk
+x.tab.h:pcmp	/man/10/mk
+x1f	/man/6/json
+xalloc	/man/10/malloc
+xalloc	/man/10/xalloc
+xamount	/man/9/canvas
+xbitmap	/man/1/charon
+xbmreader	/man/2/imagefile
+xd	/man/1/xd
+xd.b	/man/1/xd
+xfree	/man/10/xalloc
+xinches	/man/2/print
+xlate	/man/2/translate
+xlaten	/man/2/translate
+xmagic	/man/2/dis
+xmagic	/man/6/dis
+xml	/man/1/ebook
+xml	/man/2/encoding
+xml	/man/2/sexprs
+xml	/man/2/w3c-xpointers
+xml	/man/2/xml
+xml	/man/6/sexprs
+xml's	/man/2/w3c-xpointers
+xml's	/man/2/xml
+xml.b	/man/2/xml
+xml.m	/man/2/xml
+xmlns	/man/2/w3c-xpointers
+xn	/man/6/font
+xn	/man/9/canvas
+xoff	/man/3/eia
+xon	/man/3/eia
+xor	/man/1/fc
+xor	/man/1/sh-expr
+xor	/man/2/keyring-ipint
+xor	/man/8/signer
+xored	/man/2/crc
+xorigin	/man/9/canvas
+xors	/man/2/keyring-rc4
+xp	/man/10/ntsrv
+xp	/man/3/fs
+xpath	/man/2/w3c-xpointers
+xpath.e	/man/2/w3c-xpointers
+xpath.path	/man/2/w3c-xpointers
+xpath.str	/man/2/w3c-xpointers
+xpath.var	/man/2/w3c-xpointers
+xpointer	/man/2/w3c-xpointers
+xpointers	/man/2/w3c-xpointers
+xpointers.b	/man/2/w3c-xpointers
+xpointers.m	/man/2/w3c-xpointers
+xprint	/man/2/sys-print
+xptr	/man/2/w3c-xpointers
+xrect	/man/2/draw-rect
+xs	/man/2/alphabet-intro
+xscale	/man/9/canvas
+xscrollcommand	/man/9/canvas
+xscrollcommand	/man/9/entry
+xscrollcommand	/man/9/listbox
+xscrollcommand	/man/9/options
+xscrollcommand	/man/9/text
+xscrollicrement	/man/9/canvas
+xscrollincrement	/man/9/canvas
+xsize	/man/1/emu
+xspanalloc	/man/10/xalloc
+xstep	/man/2/w3c-xpointers
+xuejia	/man/1/idea
+xview	/man/9/canvas
+xview	/man/9/entry
+xview	/man/9/listbox
+xview	/man/9/scrollbar
+xview	/man/9/text
+xx	/man/1/fc
+xx	/man/1/sh-expr
+xx	/man/1/sh-mload
+xx	/man/2/draw-example
+xx	/man/2/w3c-uris
+xx	/man/6/man
+xx,yy	/man/2/draw-example
+xxx	/man/10/plan9.ini
+xxx	/man/6/login
+xxxdevtab	/man/10/dynld
+xxxs	/man/1/m4
+xxxxx	/man/8/bootpd
+xy	/man/2/draw-pointer
+xyeeeeennnnn	/man/2/geodesy
+xyeeeennnn	/man/2/geodesy
+xyeeennn	/man/2/geodesy
+xyeenn	/man/2/geodesy
+xyen	/man/2/geodesy
+y.debug	/man/1/yacc
+y.output	/man/1/yacc
+y.tab.b	/man/1/yacc
+y.tab.c	/man/10/mk
+y.tab.h	/man/10/mk
+y.tab.m	/man/1/yacc
+ya	/man/6/keyboard
+yacc	/man/1/yacc
+yacc	/man/10/mk
+yacc.b	/man/1/yacc
+yaccpar	/man/1/yacc
+yamount	/man/9/canvas
+yday	/man/2/daytime
+yellow	/man/2/draw-display
+yellow	/man/2/draw-example
+yellow	/man/9/types
+yellowgreen	/man/2/draw-display
+yes	/man/1/alphabet-fs
+yes	/man/1/fs
+yes	/man/6/man
+yes	/man/8/getauthinfo
+yes	/man/8/signer
+yes	/man/9/types
+yield	/man/1/fc
+yield	/man/1/grep
+yield	/man/1/sh
+yield	/man/1/sh-expr
+yield	/man/1/sh-string
+yield	/man/1/wm-sh
+yield	/man/10/acid
+yield	/man/2/imagefile
+yield	/man/2/sh
+yield	/man/2/spki
+yield	/man/2/sys-0intro
+yield	/man/2/wmsrv
+yield	/man/4/spree
+yielded	/man/1/sh-std
+yielding	/man/1/sh
+yielding	/man/1/sh-regex
+yielding	/man/1/sh-std
+yielding	/man/3/dbg
+yielding	/man/3/prog
+yields	/man/1/alphabet-fs
+yields	/man/1/deb
+yields	/man/1/fs
+yields	/man/1/ftest
+yields	/man/1/sh
+yields	/man/1/sh-alphabet
+yields	/man/1/sh-csv
+yields	/man/1/sh-expr
+yields	/man/1/sh-file2chan
+yields	/man/1/sh-regex
+yields	/man/1/sh-sexprs
+yields	/man/1/sh-std
+yields	/man/1/sh-string
+yields	/man/1/sh-tk
+yields	/man/2/asn1
+yields	/man/2/cfg
+yields	/man/2/sets
+yields	/man/2/sexprs
+yields	/man/2/spree
+yields	/man/2/wait
+yields	/man/2/xml
+yields	/man/3/dbg
+yields	/man/3/draw
+yields	/man/3/pnp
+yields	/man/3/sign
+yields	/man/4/dbfs
+yields	/man/4/spree
+yields	/man/5/stat
+yields	/man/6/keyboard
+yields	/man/8/ping
+yinches	/man/2/print
+yn	/man/2/math-elem
+yn	/man/9/canvas
+yorigin	/man/9/canvas
+you'll	/man/9/canvas
+yposition	/man/9/menu
+yscale	/man/9/canvas
+yscrollcommand	/man/9/canvas
+yscrollcommand	/man/9/listbox
+yscrollcommand	/man/9/options
+yscrollcommand	/man/9/text
+yscrollicrement	/man/9/canvas
+yscrollincrement	/man/9/canvas
+ysize	/man/1/emu
+yuyv	/man/3/vga
+yview	/man/9/canvas
+yview	/man/9/listbox
+yview	/man/9/scrollbar
+yview	/man/9/text
+ywjj	/man/2/sexprs
+yy	/man/2/draw-example
+yylex	/man/1/yacc
+yylex.error	/man/1/yacc
+yylex.lex	/man/1/yacc
+yyparse	/man/1/yacc
+yystype	/man/1/yacc
+yyyy	/man/2/spki
+za	/man/1/tr
+za	/man/2/string
+za	/man/6/plumbing
+za	/man/6/sexprs
+zdiv	/man/2/math-fp
+zerodir	/man/2/styxservers-nametree
+zerodir	/man/2/sys-stat
+zeroes	/man/10/allocb
+zeroes	/man/2/draw-display
+zeroes	/man/2/ip
+zeroes	/man/6/keytext
+zeroes	/man/6/sexprs
+zeroing	/man/3/kprof
+zeropower	/man/3/rtc
+zeros.b	/man/1/zeros
+zeroth	/man/6/font
+zerox	/man/1/acme
+zeroxed	/man/4/acme
+zinfandel	/man/10/odbc
+ziv	/man/1/gzip
+ziv's	/man/6/image
+zlib	/man/2/filter-deflate
+znext	/man/2/wmsrv
+zone	/man/1/date
+zone	/man/2/daytime
+zone	/man/2/geodesy
+zone	/man/6/ndb
+zone	/man/8/dns
+zoom	/man/1/wm-misc
+zooms	/man/1/wm-misc
+zorro	/man/4/9srvfs
+zxnet1	/man/1/miniterm
--- /dev/null
+++ b/man/lib/checkman.awk
@@ -1,0 +1,187 @@
+# Usage: awk -f checkman.awk man?/*.?
+#
+# Checks:
+#	- .TH is first line, and has proper name section number
+#	- sections are in order NAME, SYNOPSIS, DESCRIPTION, EXAMPLES,
+#		FILES, SOURCE, SEE ALSO, DIAGNOSTICS, BUGS
+#	- there's a manual page for each cross-referenced page
+
+BEGIN {
+
+#	.SH sections should come in the following order
+
+	Weight["NAME"] = 1
+	Weight["SYNOPSIS"] = 2
+	Weight["DESCRIPTION"] = 4
+	Weight["EXAMPLE"] = 8
+	Weight["EXAMPLES"] = 16
+	Weight["FILES"] = 32
+	Weight["SOURCE"] = 64
+	Weight["SEE ALSO"] = 128
+	Weight["DIAGNOSTICS"] = 256
+	Weight["SYSTEM CALLS"] = 512
+	Weight["BUGS"] = 1024
+}
+
+FNR==1	{
+		n = length(FILENAME)
+		seclen = 0
+		if (substr(FILENAME, 2, 1) == "/")
+			seclen = 1
+		else if (substr(FILENAME, 3, 1) == "/")
+			seclen = 2
+		if(seclen == 0)
+			print "FILENAME", FILENAME, "not of form [0-9][0-9]?/*"
+		else if(!(substr(FILENAME, seclen+2, n-seclen-1) ~ /^[A-Z]+$/)){
+			section = substr(FILENAME, 1, seclen)
+			name = substr(FILENAME, seclen+2, n-seclen-1)
+			if($1 != ".TH" || NF != 3)
+				print "First line of", FILENAME, "not a proper .TH"
+			else if($2 != toupper(name) || substr($3, 1, seclen) != section){
+				if($2!="INTRO" || name!="0intro")
+					print ".TH of", FILENAME, "doesn't match filename"
+			}else
+				Pages[section "/" $2] = 1
+		}
+		Sh = 0
+	}
+
+$1 == ".SH" {
+		if(inex)
+			print "Unterminated .EX in", FILENAME, ":", $0
+		inex = 0;
+		if (substr($2, 1, 1) == "\"") {
+			if (NF == 2) {
+				print "Unneeded quote in", FILENAME, ":", $0
+				$2 = substr($2, 2, length($2)-2)
+			} else if (NF == 3) {
+				$2 = substr($2, 2) substr($3, 1, length($3)-1)
+				NF = 2
+			}
+		}
+		w = Weight[$2]
+		if (w) {
+			if (w < Sh)
+				print "Heading", $2, "out of order in", FILENAME
+			Sh += w
+		}
+}
+
+$1 == ".EX" {
+		if(inex)
+			print "Nested .EX in", FILENAME, ":", $0
+		inex = 1
+}
+
+$1 == ".EE" {
+		if(!inex)
+			print "Bad .EE in", FILENAME, ":", $0
+		inex = 0;
+}
+
+$0 ~ /^\..*\([0-9]\)/ {
+		if ($1 == ".IR" && $3 ~ /\([0-9]\)/) {
+			name = $2
+			section = $3
+		}else if ($1 == ".RI" && $2 == "(" && $4 ~ /\([0-9]\)/) {
+			name = $3
+			section = $4
+		}else if ($1 == ".IR" && $3 ~ /9.\([0-9]\)/) {
+			name = $2
+			section = "9"
+		}else if ($1 == ".RI" && $2 == "(" && $4 ~ /9.\([0-9]\)/) {
+			name = $3
+			section = "9"
+		} else {
+			print "Possible bad cross-reference format in", FILENAME
+			print $0
+			next
+		}
+		gsub(/[^0-9]/, "", section)
+		Refs[section "/" toupper(name)]++
+}
+
+END {
+	print "Checking Cross-Referenced Pages"
+	for (i in Refs) {
+		if (!(i in Pages))
+			print "Need", tolower(i)
+	}
+	print ""
+	print "Checking commands"
+	getindex("/usr/inferno/doc/man/1")
+	getindex("/usr/inferno/doc/man/8")
+	Skipdirs["lib"] = 1
+	getbinlist("/usr/inferno/dis")
+	for (i in List) {
+		if (!(i in Index))
+			print "Need", i, "(in " List[i] ")"
+	}
+	clearindex()
+	clearlist()
+	print ""
+	print "Checking libraries"
+	getindex("/usr/inferno/doc/man/2")
+	getbinlist("/usr/inferno/dis/lib")
+	for (i in List) {
+		if (!(i in Index))
+			print "Need", i, "(in " List[i] ")"
+	}
+}
+
+func getindex(dir,    fname)
+{
+	fname = dir "/INDEX"
+	while ((getline < fname) > 0)
+		Index[$1] = dir
+	close(fname)
+}
+
+func getindex9(dir,    fname)
+{
+	fname = dir "/INDEX"
+	while ((getline < fname) > 0)
+		if($2 ~ "(getflags|picopen|getcmap)")
+			Index[$1] = dir
+	close(fname)
+}
+
+func getbinlist(dir,    cmd, subdirs, nsd)
+{
+	cmd = "ls -p -l " dir
+	nsd = 0
+	while (cmd | getline) {
+		if ($1 ~ /^d/) {
+			if (!($10 in Skipdirs))
+				subdirs[++nsd] = $10
+		} else {
+			sub(".dis", "", $10)
+			List[$10] = dir
+		}
+	}
+	for ( ; nsd > 0 ; nsd--)
+		getbinlist(dir "/" subdirs[nsd])
+	close(cmd)
+}
+
+func getnmlist(lib,    cmd)
+{
+	cmd = "nm -g -h " lib
+	while (cmd | getline) {
+		if (($1 == "T" || $1 == "L") && $2 !~ "^_")
+			List[$2] = lib
+	}
+	close(cmd)
+}
+
+func clearindex(    i)
+{
+	for (i in Index)
+		delete Index[i]
+}
+
+func clearlist(    i)
+{
+	for (i in List)
+		delete List[i]
+}
--- /dev/null
+++ b/man/lib/colophon
@@ -1,0 +1,26 @@
+.sp |3i
+.vs 12
+.ll 5.1i
+.in |0.4i
+.fp 1 R LucidaSans
+.ft 1
+.fi
+This book was typeset by the authors using
+.sp
+.ft CW
+.ps -1
+.ce
+eqn | tbl | troff -mpm | lp -d stdout | cropmarks
+.ps +1
+.ft R
+.sp
+The input text was characters from the Unicode Standard encoded in UTF-8.
+.sp
+The fonts used were Lucida Sans,
+in a special version incorporating over 1700 characters from the Unicode Standard,
+along with Lucida Sans Italic,
+Lucida Sans DemiBold, and
+Lucida Typewriter,
+designed by Bigelow & Holmes, Atherton, California.
+The hinted Adobe Type 1 representation of the fonts was
+provided by Y&Y Inc., 45 Walden Street, Concord, MA, 01742, USA.
--- /dev/null
+++ b/man/lib/lookman/junkwords
@@ -1,0 +1,509 @@
+
+a
+about
+above
+according
+across
+act
+action
+after
+again
+against
+ago
+air
+all
+allowed
+almost
+along
+already
+also
+although
+always
+american
+among
+an
+and
+another
+any
+anything
+appear
+appears
+appropriate
+are
+area
+areas
+argument
+arguments
+around
+as
+asked
+associated
+at
+available
+away
+b
+back
+be
+became
+because
+become
+been
+before
+began
+begin
+beginning
+begins
+behind
+being
+below
+best
+better
+between
+big
+board
+body
+both
+boy
+brought
+business
+but
+by
+c
+call
+called
+calls
+came
+can
+cannot
+car
+case
+cause
+causes
+certain
+change
+changed
+changes
+changing
+children
+church
+city
+close
+college
+come
+command
+commands
+community
+company
+contain
+containing
+contents
+corresponding
+could
+country
+course
+court
+currently
+d
+day
+days
+death
+depending
+description
+development
+did
+didnt
+different
+do
+does
+doesnt
+done
+dont
+door
+down
+during
+e
+each
+early
+economic
+effect
+either
+element
+elements
+else
+empty
+encountered
+end
+enough
+even
+ever
+every
+example
+examples
+experience
+eyes
+f
+face
+fact
+family
+far
+federal
+feet
+felt
+few
+field
+find
+first
+five
+followed
+following
+follows
+for
+force
+form
+found
+four
+free
+from
+function
+functions
+g
+gave
+general
+get
+give
+given
+gives
+giving
+go
+god
+going
+good
+got
+government
+great
+group
+h
+had
+half
+hand
+hands
+has
+have
+having
+he
+head
+heard
+held
+help
+her
+here
+high
+him
+himself
+his
+history
+home
+house
+how
+however
+human
+i
+if
+ignored
+im
+immediately
+implemented
+important
+in
+including
+individual
+information
+initial
+instead
+interest
+interpreted
+into
+is
+it
+its
+itself
+j
+job
+john
+just
+k
+keep
+kind
+knew
+know
+known
+l
+large
+last
+later
+law
+least
+left
+less
+let
+life
+light
+like
+line
+little
+local
+long
+look
+looked
+love
+m
+made
+major
+make
+makes
+making
+man
+many
+marked
+matter
+may
+me
+meaning
+means
+members
+men
+might
+mind
+miss
+moment
+money
+more
+most
+mr
+mrs
+much
+multiple
+must
+my
+n
+name
+named
+national
+need
+never
+new
+next
+night
+no
+nonzero
+normal
+normally
+not
+nothing
+now
+number
+numbers
+o
+of
+off
+office
+often
+old
+on
+once
+one
+only
+open
+or
+order
+other
+others
+otherwise
+our
+out
+over
+own
+p
+part
+passed
+past
+people
+per
+perhaps
+period
+place
+placed
+point
+political
+position
+possible
+power
+preceded
+present
+president
+previous
+probably
+problem
+problems
+program
+public
+put
+q
+question
+quite
+r
+rather
+really
+reason
+represented
+respectively
+result
+right
+room
+s
+said
+same
+saw
+say
+school
+second
+see
+seemed
+seems
+seen
+sense
+separate
+separated
+sequence
+service
+set
+sets
+several
+shall
+she
+should
+show
+side
+simple
+since
+single
+small
+so
+social
+society
+some
+something
+south
+special
+specified
+start
+starting
+state
+states
+still
+street
+study
+subsequent
+such
+supplied
+sure
+synopsis
+system
+t
+take
+taken
+tell
+terminated
+terminating
+than
+that
+the
+their
+them
+themselves
+then
+there
+these
+they
+thing
+things
+think
+this
+those
+though
+thought
+three
+through
+thus
+to
+today
+together
+told
+too
+took
+top
+toward
+trailing
+treated
+true
+turn
+turned
+two
+u
+under
+united
+until
+up
+upon
+us
+use
+used
+useful
+uses
+usually
+v
+very
+w
+want
+war
+was
+water
+way
+we
+week
+well
+went
+were
+west
+what
+when
+where
+whether
+which
+while
+white
+who
+whole
+whose
+why
+will
+with
+within
+without
+word
+words
+work
+world
+would
+x
+y
+year
+years
+yet
+york
+you
+young
+your
+z
+zero
+zeros
--- /dev/null
+++ b/man/lib/lookman/mkindex
@@ -1,0 +1,13 @@
+#!/bin/rc
+# creates the index used by lookman
+>index
+for(i in /usr/inferno/man/[0-9]*/[a-z0-9:]*){
+	p=`{echo $i | sed 's@/usr/inferno(/man/.*)$@\1@'}
+	deroff -w < $i |
+	tr 'A-Z' 'a-z' |
+	sort -u |
+	comm -23 - junkwords |
+	sed 's@$@	'$p'@' >>index		# stick file name on end of line
+}
+sort -o index index
+mv index /usr/inferno/man/index
--- /dev/null
+++ b/man/lib/notes
@@ -1,0 +1,10 @@
+.fp 1 R LucidaSans
+.ft 1
+.ps 20
+.sp |0.5i
+.ce
+Notes
+.bp
+.sp |0.5i
+.ce
+Notes
--- /dev/null
+++ b/man/lib/preface
@@ -1,0 +1,73 @@
+.FP lucidasans
+.nr PS -1
+.nr VS -1
+.TL
+Preface
+.SP 0.4i exactly
+.LP
+Inferno benefits from the results of many years of systems research
+at the Computing Science Research Center at Lucent Technologies, Bell Labs.
+The system is clearly a cultural descendent of the earliest Unix systems,
+and amongst Inferno's inventors, listed below, are several venerable programmers
+associated with the development of Unix.
+Inferno looks out on a very different world from Unix: complexity is no longer
+confined to large mainframes, but has sprawled
+across world wide networks, trapping programmers in its web.
+.LP
+Inferno tackles this as radically now as Unix did then.
+First, it adopts key ideas from the system Plan 9, also from Bell Labs:
+.IP \(bu
+Replace a plethora of protocols by a simple, unifying file service protocol (Styx),
+that can be served even by tiny devices, giving a uniform
+way to access objects throughout the network.
+.IP \(bu
+Let applications `compute a name space': all resources are represented
+as file systems, which an application assembles into an application-specific
+hierarchy or `name space', private or shared, that hides their source (local or remote)
+and nature (static or dynamic), for completely transparent access.
+.IP \(bu
+Using those primitives, implement windowing systems, networked graphics, remote debugging,
+device control, and much more, with remarkable ease
+and great simplicity.
+.LP
+Inferno carries Plan 9's ideas further.
+Plan 9 virtualised resources; Inferno virtualises the whole system.
+The operating system kernel can run both native and `hosted' on a range
+of platforms presenting identical interfaces on all, offering wider portability.
+The Limbo programming language offers proper concurrent programming,
+and straightforward yet dynamic modularity.
+The Dis virtual machine allows applications to cross architecture boundaries
+invisibly during execution.
+Inferno shows the `continued appliance of computer science'.
+.LP
+The original development team at Bell Labs was
+Sean Dorward, Rob Pike and Phil Winterbottom,
+with Eric Grosse, Jim McKie, Dave Presotto,
+Dennis Ritchie, Ken Thompson and Howard Trickey.
+Many others have contributed much since then, both within Lucent and without.
+.LP
+Inferno® is now a supported, commercial product of Vita Nuova.
+The Third Edition of the Programmer's manual marked that event.
+The Fourth Edition brings many changes in content, but also makes the full
+source available as Free Software under a new `dual licence' scheme.
+.LP
+.sp
+.in 4i
+.nf
+.ft I
+.ce 100
+Dave Atkin
+John Bates
+Danny Byrne
+John Firth
+Charles Forsyth
+Michael Jeffrey
+Chris Locke
+Roger Peppé
+Nigel Roles
+.sp
+Vita Nuova
+.br
+June 2003
+.ce 0
+.in -4i
--- /dev/null
+++ b/man/lib/secindex
@@ -1,0 +1,43 @@
+#!/bin/rc
+U='ABCDEFGHIJKLMNOPQRSTUVWXYZ'
+L='abcdefghijklmnopqrstuvwxyz'
+builtin cd $1
+for (i in [a-z0-9:]*) {
+	sed -n '
+	/SH *NAM/,/SH/{
+		/SH/d
+		s/, *$//
+		ty
+		:y
+		s/ *\\*-.*//
+		tx
+		s/ *\\\(mi.*//
+		tx
+		s/[,:] */\
+/g
+		s/\n\\n/\
+/g
+		y/'$U'/'$L'/
+		s/\n/ '$i'&/g
+		s/$/ '$i'/
+		p
+	}
+	/SH *DES/q
+	d
+	:x
+	s/ *\\*-.*//
+	s/ *\\\(mi.*//
+	/^$/d
+	s/[,:] */\
+/g
+	s/\n\n/\
+/g
+	y/'$U'/'$L'/
+	s/\n/ '$i'&/g
+	s/(.|\n)*$/& '$i'/
+	p
+	q
+' $i
+kw=`{echo $i  | sed 's/0intro/intro/'}
+echo $kw $i
+} | sort -u
--- /dev/null
+++ b/man/lib/title
@@ -1,0 +1,77 @@
+.fp 1 R LucidaSans
+.fp 2 I LucidaSansI
+.ps36
+.sp |2i
+.ce
+Inferno\s36\u\s8\u™
+.sp |3.275i
+.ps24
+.ce
+Programmer's Manual
+.sp .3i
+.ce
+Volume 1
+.ps12
+.sp |4.8i
+.ft I
+.ce
+Fourth Edition
+.sp |7.6i
+.ce
+Vita Nuova Holdings Limited
+.ce
+3 Innovation Close
+.ce
+York Science Park
+.ce
+University Road
+.ce
+York  YO10 5ZF
+.ce
+England
+.bp
+.ft R
+\" .ll 5.5i
+\" .in .5i
+.hy 0
+.vs 10p
+.ps 10
+.sp |1i
+.nf
+Published by Vita Nuova Holdings Limited,
+3 Innovation Close,
+York Science Park,
+University Road,
+York YO10 5ZF,
+England
+.sp 3
+.ps 8
+Copyright © 1995-1999 Lucent Technologies Inc. All Rights Reserved.
+Portions Copyright © 1999-2005 Vita Nuova Holdings Limited.  All Rights Reserved.
+Portions of Section 9 are derived from works which themselves are
+Copyright © 1990 The Regents of the University of California, and
+Copyright © 1994-1996 Sun Microsystems, Inc.
+subject to the terms described in the copyright notice in Section 9.
+.ps 10
+.sp 3
+All rights reserved; no part of this publication may be reproduced,
+stored in a retrieval system or transmitted in any form or by any means,
+electronic, mechanical, photocopying, recording or otherwise without
+the prior written permission of the publishers.
+.sp 3
+First published 2000. This edition published 2005.
+.sp 3
+.ig
+ISBN for complete set of 2 volumes: 0 9538701 0 3
+.br
+ISBN for this volume: 0 9538701 1 1
+.sp 3
+.ft I
+Printed in Great Britain by
+William Clowes,
+Beccles,
+Suffolk NR34 9QE,
+England.
+.ft R
+.sp 3
+Cover Design: Jeff Parker
--- /dev/null
+++ b/man/lib/trademarks
@@ -1,0 +1,46 @@
+.sp |2i
+.vs 12
+.fp 1 R LucidaSans
+.ft 1
+.fi
+Trademarks referenced in this document:
+.sp 2
+.ps -1
+.vs -1
+.nf
+Inferno, Dis, Styx and Limbo are registered trademarks of Vita Nuova Holdings Limited
+    in the United States and other countries.
+68020 and 68040 are trademarks of Motorola.
+AMD is a trade mark of Advanced Micro Devices.
+ARM is a trade mark of Advanced Risc Machines, Limited.
+DSP3210 is a trade mark of AT&T.
+Aladdin Ghostscript is a trademark of Aladdin Enterprises.
+CGA is a trademark of International Business Machines Corporation.
+Challenge, Indigo\s-2\u2\d\s0, Indy, and POWER Series are trademarks of Silicon Graphics, Inc.
+Ethernet is a trademark of Xerox Corporation.
+IBM, PS/2, and VGA are registered trademarks of International Business Machines Corporation.
+IDEA is a trademark of Ascom-Tech AG.
+Intel, i386, 960, 8088, 80286, 80386, 80486, and Pentium are trademarks of Intel Corporation.
+Java is a trademark and Sun is a registered trademark of Sun Microsystems Inc.
+Lucida and Pellucida are registered trademarks of Bigelow & Holmes.
+MIPS and R3000 are registered trademarks of MIPS Technologies, Inc.
+Microsoft is a registered trademark of Microsoft Corporation.
+Windows, Windows NT and MS-DOS are registered trademarks of Microsoft Corporation
+    in the United States and other countries.
+NFS is a registered trademark of Sun Microsystems, Inc.
+PDP and VAX are registered trademarks of Digital Equipment Corp.
+Plan 9 is a trademark of Lucent Technologies Inc.
+PostScript is a registered trademark of Adobe Systems Incorporated.
+ProPhone is a registered trademark of Prolink Corporation.
+R2000, R6000, R4000, and R4400 are trademarks of MIPS Technologies, Inc.
+RC4 is a registered trademark of RSA Security Inc.
+SecureNet is a registered trademark of Digital Pathways, Inc.
+Silicon Graphics, IRIS Indigo, IRIS, and IRIX are registered trademarks of Silicon Graphics, Inc.
+Sound Blaster is a registered trademark of Creative Labs, Inc.
+SPARC is a registered trademark of SPARC International, Inc.
+StrongARM is a registered trademark of Advanced RISC Machines, Ltd.
+ThinkJet  is a registered trademark of the Hewlett-Packard Company.
+Unicode is a registered trademark of Unicode, Inc.
+UNIX is a registered trademark of The Open Group.
+.ps
+.vs
--- /dev/null
+++ b/man/mkfile
@@ -1,0 +1,119 @@
+<../mkconfig
+DOC=$ROOT
+<$DOC/man/fonts
+
+LIB=$DOC/man/lib
+
+default:V: check
+
+indices:V:
+	rfork n
+	for (i in [0-9] 10){
+		$LIB/secindex $i > $i/INDEX
+	}
+	mk lookindex
+
+
+permind:V:
+	rm -f $LIB/permind/toc
+	{
+		echo -n $FONTS
+		echo .am TH
+		echo .tm '\\$1' '\\$2' '\\n%'
+		echo ..
+		for (i in [0-9]){
+			builtin cd $i
+			for(j in [a-z0-9]*)
+				switch($i/$j){
+				case 1/tbl
+					tbl $j
+				case 1/eqn 6/auth
+					eqn $j
+				case 1/pic
+					pic $j
+				case 1/grap
+					grap $j | pic
+				case *
+					cat $j
+				}
+			builtin cd ..
+		}
+		# section 10 is in a special order
+		builtin cd 10
+		cat 0intro
+		cat `{grep -l '^\.TH.*\.1' *}
+		cat `{grep -l '^\.TH.*\.2' *}
+		cat `{grep -l '^\.TH.*\.6' *}
+		cat `{grep -l '^\.TH.*\.8' *}
+		builtin cd ..
+	} | troff -$MAN > /dev/null >[2] $LIB/permind/toc
+	builtin cd $LIB/permind
+	rm -f out
+	mk out > /dev/null >[2] /dev/null
+
+check:V: checksource
+	awk -f $LIB/checkman.awk [0-9]/* | sed '/\/(cda|av|midi|pub|weather|service\.9net|isdn)(\/|\))/d'
+
+checksource:QV:
+	sam -d >[2]/dev/null <<'!'
+	f input
+	< cat [0-9]/[0-9a-z]*
+	B output
+	b input
+	,x/^\.SH SOURCE/ .,/^\.SH/ x g/^\.B/t "output
+	b output
+	,x/^\.B.? / d
+	,x/ .*/d
+	,s/.+/if(! test -f $ROOT& \&\& ! test -d $ROOT&) echo no such SOURCE file '&'/g
+	,>rc
+	!
+
+lookindex:V:
+	builtin cd $LIB/lookman
+	mkindex
+
+
+print.out:V: permind
+	{
+		{echo -n $FONTS; cat $LIB/title} | troff
+		{echo -n $FONTS; cat $LIB/trademarks} | troff -ms
+		{echo -n $FONTS; echo ' '} | troff
+		{echo -n $FONTS; cat $LIB/preface} | troff -ms
+		{echo -n $FONTS; echo ' '} | troff
+		{
+			for (i in [0-9]){
+				builtin cd $i
+				for(j in [a-z0-9]*)
+					switch($i/$j){
+					case 1/tbl
+						tbl $j
+					case 1/eqn 6/auth
+						eqn $j
+					case 1/pic
+						pic $j
+					case 1/grap
+						grap $j | pic
+					case *
+						cat $j
+					}
+				builtin cd ..
+			}
+			# section 10 is in a special order
+			builtin cd 10
+			cat 0intro
+			cat `{grep -l '^\.TH.*\.1' *}
+			cat `{grep -l '^\.TH.*\.2' *}
+			cat `{grep -l '^\.TH.*\.6' *}
+			cat `{grep -l '^\.TH.*\.8' *}
+			builtin cd ..
+		} | troff -$MAN
+		{echo -n $FONTS; echo ' '} | troff
+		{echo -n $FONTS; echo ' '} | troff
+		cat $LIB/permind/out
+		{echo -n $FONTS; echo ' '} | troff
+		{echo -n $FONTS; echo ' '} | troff
+		{echo -n $FONTS; cat $LIB/colophon} | troff
+	} > print.out
+
+clean:V:
+	rm -f man.out
--- /dev/null
+++ b/mkconfig
@@ -1,0 +1,33 @@
+#
+#	Set the following 4 variables.  The host system is the system where
+#	the software will be built; the target system is where it will run.
+#	They are almost always the same.
+
+#	On Nt systems, the ROOT path MUST be of the form `drive:/path'
+ROOT=$HOME/src/purgatorio
+
+#
+#	Specify the flavour of Tk (std for standard builds)
+#
+TKSTYLE=std
+
+#
+#	Except for building kernels, SYSTARG must always be the same as SYSHOST
+#
+SYSHOST=Linux		# build system OS type (Hp, Inferno, Irix, Linux, MacOSX, Nt, Plan9, Solaris)
+SYSTARG=$SYSHOST	# target system OS type (Hp, Inferno, Irix, Linux, Nt, Plan9, Solaris)
+
+#
+#	specify the architecture of the target system - Plan 9 imports it from the
+#	environment; for other systems it is usually just hard-coded
+#
+#OBJTYPE=386			# target system object type (eg, 386, arm, mips, power, s800, sparc)
+OBJTYPE=386
+
+#
+#	no changes required beyond this point
+#
+OBJDIR=$SYSTARG/$OBJTYPE
+
+<$ROOT/mkfiles/mkhost-$SYSHOST			# variables appropriate for host system
+<$ROOT/mkfiles/mkfile-$SYSTARG-$OBJTYPE	# variables used to build target object type
--- /dev/null
+++ b/mkfile
@@ -1,0 +1,183 @@
+# Directories common to all architectures.
+# Build in order:
+#	- critical libraries used by the limbo compiler
+#	- the limbo compiler (used to build some subsequent libraries)
+#	- the remaining libraries
+#	- commands
+#	- utilities
+
+EMUDIRS=\
+	lib9\
+	libbio\
+	libmp\
+	libsec\
+	libmath\
+	utils/iyacc\
+	limbo\
+	libinterp\
+	libkeyring\
+	libdraw\
+	libprefab\
+	libtk\
+	libfreetype\
+	libmemdraw\
+	libmemlayer\
+	libdynld\
+	utils/data2c\
+	utils/ndate\
+	emu\
+
+KERNEL_DIRS=\
+	os\
+	os/boot/pc\
+
+# mkconfig is included at this point to allow it to override
+#the preceding declarations (particularly KERNEL_DIRS) if need be
+
+<mkconfig
+
+DIRS=\
+	$EMUDIRS\
+#	appl\
+
+foo:QV:
+	echo mk all, clean, install, installall or nuke
+
+all:V:		all-$HOSTMODEL
+clean:V:	clean-$HOSTMODEL
+install:V:	install-$HOSTMODEL
+installall:V:	installall-$HOSTMODEL
+emu:V:	emu/all-$HOSTMODEL
+emuinstall:V:	emu/install-$HOSTMODEL
+emuclean:V:	emu/clean-$HOSTMODEL
+emunuke:V:	emu/nuke-$HOSTMODEL
+kernel:V:	kernel/all-$HOSTMODEL
+kernelall:V:	kernel/all-$HOSTMODEL
+kernelclean:V:	kernel/clean-$HOSTMODEL
+kernelinstall:V:	kernel/install-$HOSTMODEL
+kernelinstallall:V:	kernel/installall-$HOSTMODEL
+kernelnuke:V:	kernel/nuke-$HOSTMODEL
+nuke:V:		nuke-$HOSTMODEL
+
+cleandist:V: clean
+	rm -f $ROOT/$OBJDIR/lib/lib*.a
+
+nukedist:V: nuke
+	rm -f $ROOT/$OBJDIR/bin/*.exe
+	rm -f $ROOT/$OBJDIR/lib/lib*.a
+	
+&-Posix:QV:
+	for j in $DIRS utils tools
+	do
+		echo "(cd $j; mk $MKFLAGS $stem)"
+		(cd $j; mk $MKFLAGS $stem) || exit 1
+	done
+
+&-Nt:QV:
+	for (j in $DIRS utils tools)
+	{
+		echo '@{builtin cd' $j '; mk $MKFLAGS $stem}'
+		@{builtin cd $j; mk.exe $MKFLAGS $stem }
+	}
+
+&-Inferno:QV:
+	for (j in $DIRS utils)
+	{
+		echo '@{builtin cd' $j '; mk $MKFLAGS $stem}'
+		@{builtin cd $j; mk $MKFLAGS $stem }
+	}
+
+&-Plan9:QV:
+	for (j in $DIRS utils)
+	{
+		echo '@{builtin cd' $j '; mk $MKFLAGS $stem}'
+		@{builtin cd $j; mk $MKFLAGS $stem }
+	}
+
+emu/&-Posix:QV:
+	for j in $EMUDIRS
+	do
+		echo "(cd $j; mk $MKFLAGS $stem)"
+		(cd $j; mk $MKFLAGS $stem) || exit 1
+	done
+
+emu/&-Nt:QV:
+	for (j in $EMUDIRS)
+	{
+		echo '@{builtin cd' $j '; mk $MKFLAGS $stem}'
+		@{builtin cd $j; mk $MKFLAGS $stem }
+	}
+
+emu/&-Plan9:QV:
+	for (j in $EMUDIRS)
+	{
+		echo '@{builtin cd' $j '; mk $MKFLAGS $stem}'
+		@{builtin cd $j; mk $MKFLAGS $stem }
+	}
+
+kernel/&-Posix:QV:
+	for j in $KERNEL_DIRS
+	do
+		echo "(cd $j; mk $MKFLAGS $stem)"
+		(cd $j; mk $MKFLAGS $stem) || exit 1
+	done
+
+kernel/&-Nt:QV:
+	for (j in $KERNEL_DIRS)
+	{
+		echo '@{builtin cd' $j '; mk $MKFLAGS $stem}'
+		@{builtin cd $j; mk $MKFLAGS $stem }
+	}
+
+kernel/&-Inferno:QV:
+	for (j in $KERNEL_DIRS)
+	{
+		echo '@{builtin cd' $j '; mk $MKFLAGS $stem}'
+		@{builtin cd $j; mk $MKFLAGS $stem }
+	}
+
+kernel/&-Plan9:QV:
+	for (j in $KERNEL_DIRS)
+	{
+		echo '@{builtin cd' $j '; mk $MKFLAGS $stem}'
+		@{builtin cd $j; mk $MKFLAGS $stem }
+	}
+
+# Convenience targets
+
+Inferno-% inferno-% Inferno-386-% inferno-386-%:V:
+	mk 'SYSHOST=Inferno' 'OBJTYPE=386' $stem
+
+Inferno-arm-% inferno-arm-%:V:
+	mk 'SYSHOST=Inferno' 'OBJTYPE=arm' $stem
+
+Plan9-% plan9-%:V:
+	mk 'SYSHOST=Plan9' 'OBJTYPE=386' $stem
+
+Irix-% irix-%:V:
+	mk 'SYSHOST=Irix' 'OBJTYPE=mips' $stem
+
+Linux-% linux-%:V:
+	mk 'SYSHOST=Linux' 'OBJTYPE=386' $stem
+
+NetBSD-% netbsd-%:V:
+	mk 'SYSHOST=NetBSD' 'OBJTYPE=386' $stem
+
+Nt-% nt-% Win95-% win95-%:V:
+	mk 'SYSHOST=Nt' 'OBJTYPE=386' $stem
+
+Solaris-% solaris-%:V:
+	mk 'SYSHOST=Solaris' 'OBJTYPE=sparc' $stem
+
+mkdirs:V:	mkdirs-$SHELLTYPE
+
+mkdirs-rc:V:
+	mkdir -p `{cat lib/emptydirs}
+	chmod 555 mnt/* n/client/* n/*
+
+mkdirs-sh:V:
+	mkdir -p `cat lib/emptydirs`
+	chmod 555 mnt/* n/client/* n/*
+
+mkdirs-nt:V:
+	mkdir -p `{cmd /c type lib\emptydirs}
--- /dev/null
+++ b/mkfiles/mkdis
@@ -1,0 +1,34 @@
+BINTARG=${TARG:%=$DISBIN/%}
+MODDIR=$ROOT/module
+SYS_MODULE=${SYSMODULES:%=$MODDIR/%}
+LIMBOFLAGS=-I$MODDIR
+
+all:V:		$TARG
+
+install:V:	$BINTARG
+
+installall:V:	install
+
+clean:V: 	clean-std
+
+nuke:V:		nuke-std
+
+$DISBIN/%.dis:	%.dis
+	rm -f $DISBIN/$stem.dis && cp $stem.dis $DISBIN/$stem.dis
+
+%.dis:		$MODULES $SYS_MODULE
+
+%.dis:		%.b
+	limbo $LIMBOFLAGS -gw $stem.b
+
+%.s:		%.b
+	limbo $LIMBOFLAGS -w -G -S $stem.b
+
+%.install:V:	$DISBIN/%.dis
+%.installall:V:	$DISBIN/%.dis
+
+clean-std:V:
+	rm -f *.dis *.sbl
+
+nuke-std:V:	clean-std
+	cd $DISBIN; rm -f $TARG
--- /dev/null
+++ b/mkfiles/mkfile-DragonFly-386
@@ -1,0 +1,32 @@
+TARGMODEL=	Posix
+TARGSHTYPE=	sh
+CPUS=		386
+
+O=		o
+OS=		o
+
+AR=		ar
+ARFLAGS=	ruvs
+
+AS=		cc -c
+ASFLAGS=
+
+CC=		cc -c
+CFLAGS=	-g\
+		-O\
+		-Wno-deprecated-declarations -Wuninitialized -Wunused -Wreturn-type -Wimplicit\
+		-I$ROOT/DragonFly/386/include\
+		-I$ROOT/include\
+		-I/usr/local/include\
+ 		-I/usr/X11R6/include\
+
+ANSICPP=
+LD=		cc
+LDFLAGS=	-L/usr/openwin/lib\
+		-L/usr/local/lib\
+		-L/usr/X11R6/lib\
+
+SYSLIBS=
+
+YACC=		iyacc
+YFLAGS=		-d
--- /dev/null
+++ b/mkfiles/mkfile-FreeBSD-386
@@ -1,0 +1,33 @@
+TARGMODEL=	Posix
+TARGSHTYPE=	sh
+CPUS=		386
+
+O=		o
+OS=		o
+
+AR=		ar
+ARFLAGS=	ruvs
+
+AS=		cc -c -m32
+ASFLAGS=
+
+CC=		cc -c -m32
+CFLAGS=		-g\
+		-O\
+		-Wno-deprecated-declarations -Wuninitialized -Wunused -Wreturn-type -Wimplicit\
+		-I$ROOT/FreeBSD/386/include\
+		-I$ROOT/include\
+		-I/usr/local/include\
+ 		-I/usr/X11R6/include\
+		-DFREEBSD_386
+
+ANSICPP=
+LD=		cc -m32
+LDFLAGS=	-L/usr/openwin/lib\
+		-L/usr/local/lib\
+		-L/usr/X11R6/lib\
+
+SYSLIBS=
+
+YACC=		iyacc
+YFLAGS=		-d
--- /dev/null
+++ b/mkfiles/mkfile-Inferno-386
@@ -1,0 +1,24 @@
+TARGMODEL=	Inferno
+TARGSHTYPE=	rc
+CPUS=		arm mips 386 amd64
+
+O=		8
+OS=		v851ok0q2t6
+
+AR=		ar
+ARFLAGS=	vu
+
+AS=		8a
+ASFLAGS=
+
+CC=		8c
+CFLAGS=		-wFVT -I$ROOT/Inferno/$OBJTYPE/include -I$ROOT/Inferno/include -I$ROOT/include
+ANSICPP= -p
+
+LD=		8l
+LDFLAGS=
+
+SYSLIBS=	-lc
+
+YACC=		yacc
+YFLAGS=		-d
--- /dev/null
+++ b/mkfiles/mkfile-Inferno-arm
@@ -1,0 +1,24 @@
+TARGMODEL=	Inferno
+TARGSHTYPE=	rc
+CPUS=		arm mips 386 amd64
+
+O=		5
+OS=		v851ok0q2t6
+
+AR=		iar
+ARFLAGS=	vu
+
+AS=		5a
+ASFLAGS=
+
+CC=		5c
+CFLAGS=		-wFV -I$ROOT/Inferno/$OBJTYPE/include -I$ROOT/Inferno/include -I$ROOT/include
+ANSICPP= -p
+
+LD=		5l
+LDFLAGS=
+
+SYSLIBS=	#-lc
+
+YACC=		yacc
+YFLAGS=		-d
--- /dev/null
+++ b/mkfiles/mkfile-Inferno-mips
@@ -1,0 +1,24 @@
+TARGMODEL=	Inferno
+TARGSHTYPE=	rc
+CPUS=		mips 386 amd64
+
+O=		v
+OS=		v851ok0q2t6
+
+AR=		ar
+ARFLAGS=	vu
+
+AS=		va
+ASFLAGS=
+
+CC=		vc
+CFLAGS=		-wFV -I$ROOT/Inferno/$OBJTYPE/include -I$ROOT/Inferno/include -I$ROOT/include
+ANSICPP= -p
+
+LD=		vl
+LDFLAGS=
+
+SYSLIBS=	-lc
+
+YACC=		yacc
+YFLAGS=		-d
--- /dev/null
+++ b/mkfiles/mkfile-Inferno-power
@@ -1,0 +1,24 @@
+TARGMODEL=	Inferno
+TARGSHTYPE=	rc
+CPUS=		arm power 386 amd64
+
+O=		q
+OS=		v851ok0q2t6
+
+AR=		ar
+ARFLAGS=	vu
+
+AS=		qa
+ASFLAGS=
+
+CC=		qc
+CFLAGS=		-wFV -I$ROOT/Inferno/$OBJTYPE/include -I$ROOT/Inferno/include -I$ROOT/include
+ANSICPP= -p
+
+LD=		ql
+LDFLAGS=
+
+SYSLIBS=	#-lc
+
+YACC=		yacc
+YFLAGS=		-d
--- /dev/null
+++ b/mkfiles/mkfile-Inferno-sparc
@@ -1,0 +1,24 @@
+TARGMODEL=	Inferno
+TARGSHTYPE=	rc
+CPUS=		sparc arm mips 386 amd64
+
+O=		k
+OS=		v851ok0q2t6
+
+AR=		ar
+ARFLAGS=	vu	
+
+AS=		ka
+ASFLAGS=
+
+CC=		kc
+CFLAGS=		-wFV -I$ROOT/Inferno/$OBJTYPE/include -I$ROOT/Inferno/include -I$ROOT/include
+ANSICPP= -p
+
+LD=		kl
+LDFLAGS=
+
+SYSLIBS=	#-lc
+
+YACC=		yacc
+YFLAGS=		-d
--- /dev/null
+++ b/mkfiles/mkfile-Inferno-spim
@@ -1,0 +1,24 @@
+TARGMODEL=	Inferno
+TARGSHTYPE=	rc
+CPUS=		spim 386 amd64
+
+O=		0
+OS=		v851ok0q2t6
+
+AR=		ar
+ARFLAGS=	vu
+
+AS=		0a
+ASFLAGS=
+
+CC=		0c
+CFLAGS=		-w -I$ROOT/Inferno/$OBJTYPE/include -I$ROOT/Inferno/include -I$ROOT/include
+ANSICPP= -p
+
+LD=		0l
+LDFLAGS=
+
+SYSLIBS=	-lc
+
+YACC=		yacc
+YFLAGS=		-d
--- /dev/null
+++ b/mkfiles/mkfile-Inferno-thumb
@@ -1,0 +1,24 @@
+TARGMODEL=	Inferno
+TARGSHTYPE=	rc
+CPUS=		arm mips 386 amd64
+
+O=		t
+OS=		v851ok0q2t6
+
+AR=		iar
+ARFLAGS=	vu
+
+AS=5a -t
+ASFLAGS=
+
+CC=		tc
+CFLAGS=		-wFV -I$ROOT/Inferno/$OBJTYPE/include -I$ROOT/Inferno/include -I$ROOT/include
+ANSICPP= -p
+
+LD=5l
+LDFLAGS=
+
+SYSLIBS=	#-lc
+
+YACC=		yacc
+YFLAGS=		-d
--- /dev/null
+++ b/mkfiles/mkfile-Irix-mips
@@ -1,0 +1,30 @@
+TARGMODEL=	Posix
+TARGSHTYPE=	sh
+CPUS=		mips
+
+O=		o
+OS=		o
+
+AR=		ar
+ARFLAGS=	rvu
+
+AS=		cc -c
+ASFLAGS=	-mips2
+
+CC=		cc -c
+CFLAGS=		-w\
+		-O\
+		-g3\
+		-common\
+		-Xcpluscomm\
+		-I$ROOT/Irix/mips/include\
+		-I$ROOT/include\
+
+ANSICPP=
+LD=		cc
+LDFLAGS=
+
+SYSLIBS=	-lfpe
+
+YACC=		iyacc
+YFLAGS=		-d
--- /dev/null
+++ b/mkfiles/mkfile-Linux-386
@@ -1,0 +1,31 @@
+TARGMODEL=	Posix
+TARGSHTYPE=	sh
+CPUS=		386
+
+O=		o
+OS=		o
+
+AR=		ar
+ARFLAGS=	ruvs
+
+AS=		cc -c -m32
+ASFLAGS=
+
+CC=		cc -c -m32
+CFLAGS=		-g\
+		-O\
+		-fno-strict-aliasing\
+		-fno-aggressive-loop-optimizations\
+		-Wuninitialized -Wunused-variable -Wreturn-type -Wimplicit\
+		-I$ROOT/Linux/386/include\
+		-I$ROOT/include\
+		-DLINUX_386
+
+ANSICPP=
+LD=		cc -m32
+LDFLAGS=
+
+SYSLIBS=
+
+YACC=		iyacc
+YFLAGS=		-d
--- /dev/null
+++ b/mkfiles/mkfile-Linux-arm
@@ -1,0 +1,28 @@
+TARGMODEL=	Posix
+TARGSHTYPE=	sh
+CPUS=		arm
+
+O=		o
+OS=		o
+
+AR=		ar
+ARFLAGS=	ruvs
+
+AS=		arm-gcc -c 
+ASFLAGS=
+
+CC=		arm-gcc -c 
+CFLAGS=		-O\
+		-Wuninitialized -Wunused-variable -Wreturn-type -Wimplicit\
+		-I$ROOT/Linux/arm/include\
+		-I$ROOT/include\
+		-DLINUX_ARM
+
+ANSICPP=
+LD=		arm-gcc 
+LDFLAGS=	
+
+SYSLIBS=
+
+YACC=		iyacc
+YFLAGS=		-d
--- /dev/null
+++ b/mkfiles/mkfile-Linux-power
@@ -1,0 +1,30 @@
+TARGMODEL=	Posix
+TARGSHTYPE=	sh
+CPUS=		power
+
+O=		o
+OS=		o
+
+AR=		ar
+ARFLAGS=	crvs
+
+AS=		cc -c -m32
+ASFLAGS=
+
+CC=		cc -c -m32
+CFLAGS=		-g\
+		-O\
+		-Wuninitialized -Wunused-variable -Wreturn-type -Wimplicit\
+		-I$ROOT/Linux/power/include\
+		-I$ROOT/include\
+		-I/usr/X11R6/include
+
+ANSICPP=
+LD=		cc -m32
+LDFLAGS=	-L/usr/openwin/lib\
+		-L/usr/X11R6/lib\
+
+SYSLIBS=
+
+YACC=		yacc
+YFLAGS=		-d
--- /dev/null
+++ b/mkfiles/mkfile-Linux-spim
@@ -1,0 +1,29 @@
+TARGMODEL=	Posix
+TARGSHTYPE=	sh
+CPUS=		spim
+
+O=		o
+OS=		o
+
+AR=		mipsel-linux-uclibc-ar
+ARFLAGS=	crvs
+
+AS=		mipsel-linux-uclibc-gcc -c -mips32
+ASFLAGS=
+
+CC=		mipsel-linux-uclibc-gcc -c -mips32
+CFLAGS=		-g\
+		-Os\
+		-I$ROOT/Linux/spim/include\
+		-I$ROOT/include\
+ 		-I/usr/X11R6/include
+
+ANSICPP=
+LD=		mipsel-linux-uclibc-gcc
+LDFLAGS=	-L/usr/openwin/lib\
+		-L/usr/X11R6/lib\
+
+SYSLIBS=
+
+YACC=		iyacc
+YFLAGS=		-d
--- /dev/null
+++ b/mkfiles/mkfile-MacOSX-386
@@ -1,0 +1,51 @@
+TARGMODEL=	Posix
+TARGSHTYPE=	sh
+CPUS=		386
+
+O=		o
+OS=		o
+
+AR=		ar
+ARFLAGS=	ruvs
+A=		a
+
+MACVER=	10.5
+
+
+AS=		cc -c -arch i386 -m32
+ASFLAGS= -mmacosx-version-min=$MACVER
+
+ISYSROOT=	-isysroot /Developer/SDKs/MacOSX$MACVER.sdk
+
+CC=		cc -c -m32
+COPTFLAGS=	-Os
+CDEBUGFLAGS=
+CTHREADFLAGS=
+CFLAGS= 	-arch i386 -m32\
+		-mmacosx-version-min=$MACVER\
+		-Wno-deprecated-declarations -Wuninitialized -Wunused -Wreturn-type -Wimplicit -Wno-four-char-constants -Wno-unknown-pragmas\
+		-Wno-main-return-type \
+		-Wno-logical-op-parentheses \
+		-Wno-constant-conversion -Wno-bitwise-op-parentheses -Wno-unused-function \
+		-Wno-shift-op-parentheses -Wno-dangling-else \
+		-Wno-parentheses \
+		-Wno-switch \
+		-Wno-tautological-compare \
+		-Wno-string-plus-int \
+		-pipe\
+		-fno-strict-aliasing\
+		-no-cpp-precomp\
+		-I$ROOT/MacOSX/386/include\
+		-I$ROOT/include\
+		$COPTFLAGS $CDEBUGFLAGS\
+
+LD=		cc -arch i386 -m32
+LDFLAGS=\
+		-mmacosx-version-min=$MACVER\
+		-multiply_defined suppress
+
+SYSLIBS=
+
+YACC=		iyacc
+YFLAGS=		-d
+
--- /dev/null
+++ b/mkfiles/mkfile-MacOSX-power
@@ -1,0 +1,32 @@
+TARGMODEL=	Posix
+TARGSHTYPE=	sh
+CPUS=		power
+
+O=		o
+OS=		o
+
+AR=		ar
+ARFLAGS=	ruvs
+A=		a
+
+AS=		cc -c -arch ppc
+ASFLAGS=	
+
+CC=		cc -c
+CFLAGS=		-arch ppc\
+		-Wno-deprecated-declarations -Wuninitialized -Wunused -Wreturn-type -Wimplicit -Wno-four-char-constants -Wno-unknown-pragmas\
+		-pipe\
+		-I$ROOT/MacOSX/power/include\
+		-I$ROOT/include\
+		-malign-natural -O2
+COPTFLAGS=	-O2
+CDEBFLAGS=	-g
+CTHREADFLAGS=
+
+LD=		cc -arch ppc
+LDFLAGS= -multiply_defined suppress
+
+SYSLIBS=
+
+YACC=		iyacc
+YFLAGS=		-d
--- /dev/null
+++ b/mkfiles/mkfile-NetBSD-386
@@ -1,0 +1,29 @@
+TARGMODEL=	Posix
+TARGSHTYPE=	sh
+CPUS=		386
+
+O=		o
+OS=		o
+
+AR=		ar
+ARFLAGS=	ruvs
+
+AS=		cc -c
+ASFLAGS=
+
+CC=		cc -c
+CFLAGS=		-g\
+		-O\
+		-Wno-deprecated-declarations -Wuninitialized -Wunused -Wreturn-type -Wimplicit\
+		-I$ROOT/NetBSD/386/include\
+		-I$ROOT/include\
+		-I/usr/X11R7/include
+
+ANSICPP=
+LD=		cc
+LDFLAGS=
+
+SYSLIBS=
+
+YACC=		iyacc
+YFLAGS=		-d
--- /dev/null
+++ b/mkfiles/mkfile-Nt-386
@@ -1,0 +1,40 @@
+TARGMODEL=	Nt
+TARGSHTYPE=	rc
+CPUS=		386
+
+O=		obj
+OS=		obj
+
+AR=		LIB
+ARFLAGS=	-nologo
+ARPREFIX=	-out:
+
+AS=		ml
+ASFLAGS=	-c\
+		-nologo\
+		-coff\
+		-Cx\
+		-Zm\
+		-DQUIET\
+		-Di386\
+
+CC=		cl
+CFLAGS=		-c\
+		-nologo\
+		-W3\
+		-Zi\
+		-MT\
+		-D_WIN32_WINNT=0x0600\
+		-I$ROOT/Nt/386/include\
+		-I$ROOT/include\
+		$XCFLAGS\
+
+ANSICPP=
+LD=		link
+LDFLAGS=	$LDEBUG -nologo -incremental:no -map
+# LDFLAGS=	-nologo -incremental:no -debug
+
+SYSLIBS=	binmode.obj
+
+YACC=		iyacc
+YFLAGS=		-d
--- /dev/null
+++ b/mkfiles/mkfile-OpenBSD-386
@@ -1,0 +1,30 @@
+TARGMODEL=	Posix
+TARGSHTYPE=	sh
+CPUS=		386
+
+O=		o
+OS=		o
+
+AR=		ar
+ARFLAGS=	ruvs
+
+AS=		cc -c
+ASFLAGS=
+
+CC=		cc -c
+CFLAGS=	-g\
+		-O\
+		-Wno-deprecated-declarations -Wuninitialized -Wunused -Wreturn-type -Wimplicit\
+		-I$ROOT/OpenBSD/386/include\
+		-I$ROOT/include\
+ 		-I/usr/X11R6/include\
+		-DOPENBSD_386 -fno-stack-protector
+
+ANSICPP=
+LD=		cc
+LDFLAGS=	-L/usr/X11R6/lib
+
+SYSLIBS=
+
+YACC=		iyacc
+YFLAGS=		-d
--- /dev/null
+++ b/mkfiles/mkfile-Plan9-386
@@ -1,0 +1,24 @@
+TARGMODEL=	Plan9
+TARGSHTYPE=	rc
+CPUS=		386 sparc mips power amd64
+
+O=		8
+OS=		v851ok0q2t6
+
+AR=		ar
+ARFLAGS=	vu
+
+AS=		8a
+ASFLAGS=
+
+CC=		8c
+CFLAGS=		-wFVT -I$ROOT/Plan9/$OBJTYPE/include -I$ROOT/Plan9/include -I$ROOT/include
+ANSICPP= -p
+
+LD=		8l
+LDFLAGS=
+
+SYSLIBS=-lc
+
+YACC=		yacc
+YFLAGS=		-d
--- /dev/null
+++ b/mkfiles/mkfile-Plan9-68020
@@ -1,0 +1,24 @@
+TARGMODEL=	Plan9
+TARGSHTYPE=	rc
+CPUS=		386 sparc mips power amd64
+
+O=		2
+OS=		v851ok0q2t6
+
+AR=		ar
+ARFLAGS=	vu
+
+AS=		2a
+ASFLAGS=
+
+CC=		2c
+CFLAGS=		-wFV -I$ROOT/Plan9/$OBJTYPE/include -I$ROOT/Plan9/include -I$ROOT/include
+ANSICPP= -p
+
+LD=		2l
+LDFLAGS=
+
+SYSLIBS=-lc
+
+YACC=		yacc
+YFLAGS=		-d
--- /dev/null
+++ b/mkfiles/mkfile-Plan9-amd64
@@ -1,0 +1,24 @@
+TARGMODEL=	Plan9
+TARGSHTYPE=	rc
+CPUS=		386 sparc mips power amd64
+
+O=		6
+OS=		v851ok0q26
+
+AR=		ar
+ARFLAGS=	vu
+
+AS=		6a
+ASFLAGS=
+
+CC=		6c
+CFLAGS=		-wFV -I$ROOT/Plan9/$OBJTYPE/include -I$ROOT/Plan9/include -I$ROOT/include
+ANSICPP= -p
+
+LD=		6l
+LDFLAGS=
+
+SYSLIBS=-lc
+
+YACC=		yacc
+YFLAGS=		-d
--- /dev/null
+++ b/mkfiles/mkfile-Plan9-arm
@@ -1,0 +1,24 @@
+TARGMODEL=	Plan9
+TARGSHTYPE=	rc
+CPUS=		386 sparc mips power amd64 arm
+
+O=		5
+OS=		v851ok0q2t6
+
+AR=		ar
+ARFLAGS=	vu
+
+AS=		5a
+ASFLAGS=
+
+CC=		5c
+CFLAGS=		-wFV -I$ROOT/Plan9/$OBJTYPE/include -I$ROOT/Plan9/include -I$ROOT/include
+ANSICPP= -p
+
+LD=		5l
+LDFLAGS=
+
+SYSLIBS=-lc
+
+YACC=		yacc
+YFLAGS=		-d
--- /dev/null
+++ b/mkfiles/mkfile-Plan9-mips
@@ -1,0 +1,24 @@
+TARGMODEL=	Plan9
+TARGSHTYPE=	rc
+CPUS=		386 sparc mips power amd64
+
+O=		v
+OS=		v851ok0q2t6
+
+AR=		ar
+ARFLAGS=	vu
+
+AS=		va
+ASFLAGS=
+
+CC=		vc
+CFLAGS=		-wFV -I$ROOT/Plan9/$OBJTYPE/include -I$ROOT/Plan9/include -I$ROOT/include
+ANSICPP= -p
+
+LD=		vl
+LDFLAGS=
+
+SYSLIBS=-lc
+
+YACC=		yacc
+YFLAGS=		-d
--- /dev/null
+++ b/mkfiles/mkfile-Plan9-power
@@ -1,0 +1,24 @@
+TARGMODEL=	Plan9
+TARGSHTYPE=	rc
+CPUS=		386 sparc mips power amd64
+
+O=		q
+OS=		v851ok0q2t6
+
+AR=		ar
+ARFLAGS=	vu
+
+AS=		qa
+ASFLAGS=
+
+CC=		qc
+CFLAGS=		-wFV -I$ROOT/Plan9/$OBJTYPE/include -I$ROOT/Plan9/include -I$ROOT/include
+ANSICPP= -p
+
+LD=		ql
+LDFLAGS=
+
+SYSLIBS=-lc
+
+YACC=		yacc
+YFLAGS=		-d
--- /dev/null
+++ b/mkfiles/mkfile-Plan9-sparc
@@ -1,0 +1,24 @@
+TARGMODEL=	Plan9
+TARGSHTYPE=	rc
+CPUS=		386 sparc mips power amd64
+
+O=		k
+OS=		v851ok0q2t6
+
+AR=		ar
+ARFLAGS=	vu
+
+AS=		ka
+ASFLAGS=
+
+CC=		kc
+CFLAGS=		-wFV -I$ROOT/Plan9/$OBJTYPE/include -I$ROOT/Plan9/include -I$ROOT/include
+ANSICPP= -p
+
+LD=		kl
+LDFLAGS=
+
+SYSLIBS=-lc
+
+YACC=		yacc
+YFLAGS=		-d
--- /dev/null
+++ b/mkfiles/mkfile-Solaris-386
@@ -1,0 +1,36 @@
+TARGMODEL=	Posix
+TARGSHTYPE=	sh
+CPUS=		386
+
+O=		o
+OS=		o
+
+AR=		ar
+ARFLAGS=	cvru
+
+AS=		cc
+ASFLAGS=	-c
+
+CC=		cc
+CFLAGS=		-c\
+		-mt\
+		-g\
+		-fstore\
+		-w\
+		-xCC \
+		-Xa\
+		-I$ROOT/Solaris/386/include\
+		-I$ROOT/include\
+		-I/usr/openwin/share/include\
+
+ANSICPP=
+LD=		cc
+LDFLAGS=	$LDFLAGS\
+		-mt\
+		-L/usr/openwin/lib\
+
+SYSLIBS=
+EMULIBS=	-lnsl -lsocket -lX11 -lm -lposix4
+
+YACC=		iyacc
+YFLAGS=		-d
--- /dev/null
+++ b/mkfiles/mkfile-Solaris-sparc
@@ -1,0 +1,58 @@
+TARGMODEL=	Posix
+TARGSHTYPE=	sh
+CPUS=		sparc
+
+O=		o
+OS=		o
+
+AR=		ar
+ARFLAGS=	cvru
+
+YACC=		iyacc
+YFLAGS=		-d
+
+AS=             gcc
+ASFLAGS=        -c
+
+CC=gcc
+CFLAGS=	-c\
+		-g\
+		-O\
+		-munaligned-doubles\
+		-threads\
+		-Wimplicit\
+		-I$ROOT/Solaris/sparc/include\
+		-I$ROOT/include\
+		-I/usr/openwin/share/include\
+
+ANSICPP=
+LD=		gcc
+LDFLAGS=-L/usr/openwin/lib\
+
+SYSLIBS=
+EMULIBS=	-lthread -lsocket -lm -lX11 -lXext -lnsl -lposix4
+
+# use the following settings in order to use the native sun C compiler
+# rather than gcc.
+# this has not been tested in this release.
+
+# AS=		cc
+# ASFLAGS=	-c
+# 
+# CC=		cc
+# CFLAGS=		-c\
+# 		-mt\
+# 		-g\
+# 		-w\
+# 		-xCC \
+# 		-Xa\
+# 		-I$ROOT/Solaris/sparc/include\
+# 		-I$ROOT/include\
+# 		-I/usr/openwin/share/include\
+# 
+# LD=		cc
+# LDFLAGS=	-mt\
+# 		-L/usr/openwin/lib\
+# 
+# SYSLIBS=
+# EMULIBS=	-lsunmath -lsocket -lm -lX11 -lXext -lnsl -lposix4
--- /dev/null
+++ b/mkfiles/mkfile-Unixware-386
@@ -1,0 +1,40 @@
+SYSTARG=	Unixware
+TARGMODEL=	Posix
+TARGSHTYPE=	sh
+OBJTYPE=386
+
+
+A=		a
+O=		o
+OS=		o
+
+
+AR=		ar
+ARFLAGS=	ruvs
+
+AS=		cc -c
+ASFLAGS=
+
+CC=		cc -c
+# cannot use -O and -g without compiler warnings
+
+CFLAGS=\
+		$CFLAGS\
+		-g\
+		-I$ROOT/$SYSTARG/$OBJTYPE/include\
+		-I$ROOT/include\
+
+ANSICPP=
+LD=		cc
+LDFLAGS=\
+		$LDFLAGS\
+		-L$ROOT/$SYSTARG/$OBJTYPE/lib\
+		-L/usr/openwin/lib\
+		-L/usr/X11R6/lib\
+
+LDSYSLIBS=	-lm -lX11
+
+RANLIB=		true
+
+YACC=		iyacc
+YFLAGS=		-d
--- /dev/null
+++ b/mkfiles/mkfile-os-386
@@ -1,0 +1,24 @@
+TARGMODEL=	Inferno
+TARGSHTYPE=	sh
+CPUS=		386
+OBJDIR=		Inferno/386		#force everything to point to inferno directory
+
+O=		8
+OS=		8
+
+AR=		iar
+ARFLAGS=	vu
+ARPREFIX=
+
+AS=		8a
+ASFLAGS=
+
+CC=		8c
+CFLAGS=		-wFV -I$ROOT/Inferno/386/include -I$ROOT/include
+ANSICPP= -p
+
+LD=		8l
+LDFLAGS=
+
+YACC=		iyacc
+YFLAGS=		-d
--- /dev/null
+++ b/mkfiles/mkfile-os-arm
@@ -1,0 +1,26 @@
+TARGMODEL=	Inferno
+TARGSHTYPE=	sh
+CPUS=		arm
+OBJDIR=		Inferno/arm		#force everything to point to inferno directory
+
+O=		5
+OS=		5
+
+AR=		iar
+ARFLAGS=	vu
+ARPREFIX=
+
+AS=		5a
+ASFLAGS=
+
+CC=		5c
+CFLAGS=		-wFV -I$ROOT/Inferno/arm/include -I$ROOT/include
+ANSICPP= -p
+
+LD=		5l
+LDFLAGS=
+
+SYSLIBS=	#-lc
+
+YACC=		iyacc
+YFLAGS=		-d
--- /dev/null
+++ b/mkfiles/mkfile-os-power
@@ -1,0 +1,24 @@
+TARGMODEL=	Inferno
+TARGSHTYPE=	sh
+CPUS=		power
+OBJDIR=		Inferno/power		#force everything to point to inferno directory
+
+O=		q
+OS=		q
+
+AR=		iar
+ARFLAGS=	vu
+ARPREFIX=
+
+AS=		qa
+ASFLAGS=
+
+CC=		qc
+CFLAGS=		-wFV -I$ROOT/Inferno/power/include -I$ROOT/include
+ANSICPP= -p
+
+LD=		ql
+LDFLAGS=
+
+YACC=		iyacc
+YFLAGS=		-d
--- /dev/null
+++ b/mkfiles/mkfile-os-sparc
@@ -1,0 +1,24 @@
+TARGMODEL=	Inferno
+TARGSHTYPE=	sh
+CPUS=		sparc
+OBJDIR=		Inferno/sparc		#force everything to point to inferno directory
+
+O=		k
+OS=		k
+
+AR=		iar
+ARFLAGS=	vu
+ARPREFIX=
+
+AS=		ka
+ASFLAGS=
+
+CC=		kc
+CFLAGS=		-wFV -I$ROOT/Inferno/sparc/include -I$ROOT/include
+ANSICPP= -p
+
+LD=		kl
+LDFLAGS=
+
+YACC=		iyacc
+YFLAGS=		-d
--- /dev/null
+++ b/mkfiles/mkfile-os-spim
@@ -1,0 +1,24 @@
+TARGMODEL=	Inferno
+TARGSHTYPE=	sh
+CPUS=		spim
+OBJDIR=		Inferno/spim		#force everything to point to inferno directory
+
+O=		0
+OS=		0
+
+AR=		iar
+ARFLAGS=	vu
+ARPREFIX=
+
+AS=		0a
+ASFLAGS=
+
+CC=		0c
+CFLAGS=		-wFV -I$ROOT/Inferno/spim/include -I$ROOT/include
+ANSICPP= -p
+
+LD=		0l
+LDFLAGS=
+
+YACC=		iyacc
+YFLAGS=		-d
--- /dev/null
+++ b/mkfiles/mkfile-os-thumb
@@ -1,0 +1,26 @@
+TARGMODEL=	Inferno
+TARGSHTYPE=	sh
+CPUS=		arm
+OBJDIR=		Inferno/thumb		#force everything to point to inferno directory
+
+O=		t
+OS=		5t
+
+AR=		iar
+ARFLAGS=	vu
+ARPREFIX=
+
+AS=		5a
+ASFLAGS=	-t
+
+CC=		tc
+CFLAGS=		-wFV -I$ROOT/Inferno/thumb/include -I$ROOT/include
+ANSICPP= -p
+
+LD=		5l
+LDFLAGS=
+
+SYSLIBS=	#-lc
+
+YACC=		iyacc
+YFLAGS=		-d
--- /dev/null
+++ b/mkfiles/mkhost-DragonFly
@@ -1,0 +1,19 @@
+
+#	Variables for host system type = Unixware
+
+SHELLTYPE=	sh
+SHELLNAME=	/bin/sh
+HOSTMODEL=	Posix
+OSTARG=	os
+
+DATA2S=	data2s
+NDATE=		ndate
+KSIZE=		ksize
+AWK=		awk
+CP=	cp
+ECHO=	echo
+FALSE=	false
+MKDIR=	mkdir -p
+RM=	rm -f
+RMDIR=	rmdir
+TRUE=	true
--- /dev/null
+++ b/mkfiles/mkhost-FreeBSD
@@ -1,0 +1,19 @@
+
+#	Variables for host system type = Unixware
+
+SHELLTYPE=	sh
+SHELLNAME=	/bin/sh
+HOSTMODEL=	Posix
+OSTARG=		os
+
+DATA2S=		data2s
+NDATE=		ndate
+KSIZE=		ksize
+AWK=		awk
+CP=	cp
+ECHO=	echo
+FALSE=	false
+MKDIR=	mkdir -p
+RM=	rm -f
+RMDIR=	rmdir
+TRUE=	true
--- /dev/null
+++ b/mkfiles/mkhost-Inferno
@@ -1,0 +1,12 @@
+
+#	Variables for host system type = Inferno
+
+SHELLTYPE=	rc
+SHELLNAME=	rc
+HOSTMODEL=	Inferno
+OSTARG=		Inferno
+
+DATA2S=		data2s
+NDATE=		date -n
+KSIZE=		size
+AWK=		awk
--- /dev/null
+++ b/mkfiles/mkhost-Irix
@@ -1,0 +1,12 @@
+
+#	Variables for host system type = Irix
+
+SHELLTYPE=	sh
+SHELLNAME=	/bin/sh
+HOSTMODEL=	Posix
+OSTARG=		os
+
+DATA2S=		data2s
+NDATE=		ndate
+KSIZE=		ksize
+AWK=		awk
--- /dev/null
+++ b/mkfiles/mkhost-Linux
@@ -1,0 +1,12 @@
+
+#	Variables for host system type = Linux
+
+SHELLTYPE=	sh
+SHELLNAME=	/bin/sh
+HOSTMODEL=	Posix
+OSTARG=		os
+
+DATA2S=		data2s
+NDATE=		ndate
+KSIZE=		ksize
+AWK=		awk
--- /dev/null
+++ b/mkfiles/mkhost-MacOSX
@@ -1,0 +1,24 @@
+#
+#   Supports all Darwin based systems (Darwin, Mac OS X, Mac OS X Server)
+#	Variables for host system type = Darwin
+#
+
+SHELLTYPE=	sh
+SHELLNAME=	/bin/sh
+HOSTMODEL=	Posix
+OSTARG=		os
+
+DATA2S=		data2s
+NDATE=		ndate
+KSIZE=		ksize
+AWK=		awk
+CP=	cp
+ECHO=	echo
+FALSE=	false
+MKDIR=	mkdir -p
+RM=	rm -f
+RMDIR=	rmdir
+TRUE=	true
+
+JAVAC=  /usr/bin/javac 
+MACOSINF=caseinsensitive	# nobble .S rules
--- /dev/null
+++ b/mkfiles/mkhost-NetBSD
@@ -1,0 +1,19 @@
+
+#	Variables for host system type = NetBSD
+
+SHELLTYPE=	sh
+SHELLNAME=	/bin/sh
+HOSTMODEL=	Posix
+OSTARG=		os
+
+DATA2S=		data2s
+NDATE=		date +'%s'
+KSIZE=		ksize
+AWK=		awk
+CP=	cp
+ECHO=	echo
+FALSE=	false
+MKDIR=	mkdir -p
+RM=	rm -f
+RMDIR=	rmdir
+TRUE=	true
--- /dev/null
+++ b/mkfiles/mkhost-Nt
@@ -1,0 +1,14 @@
+
+#	Variables for host system type = Nt (or Windows 95)
+
+SHELLTYPE=	nt
+SHELLNAME=	rcsh
+HOSTMODEL=	Nt
+OSTARG=		os
+
+DATA2S=		data2s
+NDATE=		ndate
+KSIZE=		ksize
+AWK=		awk
+
+#COMSPEC=	$ROOT/$SYSHOST/$OBJTYPE/bin/rcsh.exe
--- /dev/null
+++ b/mkfiles/mkhost-OpenBSD
@@ -1,0 +1,19 @@
+
+#	Variables for host system type = OpenBSD
+
+SHELLTYPE=	sh
+SHELLNAME=	/bin/sh
+HOSTMODEL=	Posix
+OSTARG=		os
+
+DATA2S=		data2s
+NDATE=		ndate
+KSIZE=		ksize
+AWK=		awk
+CP=	cp
+ECHO=	echo
+FALSE=	false
+MKDIR=	mkdir -p
+RM=	rm -f
+RMDIR=	rmdir
+TRUE=	true
--- /dev/null
+++ b/mkfiles/mkhost-Plan9
@@ -1,0 +1,12 @@
+
+#	Variables for host system type = Plan9
+
+SHELLTYPE=	rc
+SHELLNAME=	rc
+HOSTMODEL=	Plan9
+OSTARG=		Inferno
+
+DATA2S=	data2s
+NDATE=		date -n
+KSIZE=		size
+AWK=		awk
--- /dev/null
+++ b/mkfiles/mkhost-Solaris
@@ -1,0 +1,12 @@
+
+#	Variables for host system type = Solaris
+
+SHELLTYPE=	sh
+SHELLNAME=	/bin/sh
+HOSTMODEL=	Posix
+OSTARG=		os
+
+DATA2S=		data2s
+NDATE=		ndate
+KSIZE=		ksize
+AWK=		nawk
--- /dev/null
+++ b/mkfiles/mkhost-Unixware
@@ -1,0 +1,19 @@
+
+#	Variables for host system type = Unixware
+
+SHELLTYPE=	sh
+SHELLNAME=	/bin/sh
+HOSTMODEL=	Posix
+OSTARG=		os
+
+DATA2S=		data2s
+NDATE=		ndate
+KSIZE=		ksize
+AWK=		awk
+CP=	cp
+ECHO=	echo
+FALSE=	false
+MKDIR=	mkdir -p
+RM=	rm -f
+RMDIR=	rmdir
+TRUE=	true
--- /dev/null
+++ b/mkfiles/mkjava
@@ -1,0 +1,53 @@
+BINTARG=${TARG:%=$DISBIN/%}
+MODDIR=$ROOT/module
+SYS_MODULE=${SYSMODULES:%=$MODDIR/%}
+
+JROOT=$ROOT/java
+LIMBOINCL=-I$MODDIR -I$JROOT/module
+LIMBOFLAGS=-C
+JPATH=$JROOT/pkg
+CLASSPATH=	# make sure it's not set
+
+all:V:		$TARG
+
+install:V:	$BINTARG
+
+installall:V:	install
+
+clean:V: 	clean-std
+
+nuke:V:		nuke-std
+
+$DISBIN/%.dis:	%.dis
+	rm -f $DISBIN/$stem.dis && cp $stem.dis $DISBIN/$stem.dis
+
+%.dis:		$MODULES $SYS_MODULE
+
+%.dis:		%.b
+	limbo $LIMBOFLAGS $LIMBOINCL -gw $stem.b
+
+#%.s:		%.b
+#	limbo $LIMBOFLAGS $LIMBOINCL -w -G -S $stem.b
+
+%.class:    %.java
+	$JAVAC -classpath $JPATH $stem.java
+
+%.dis: %.class
+	j2d $stem.class
+
+%.install:V:	$DISBIN/%.dis
+%.installall:V:	$DISBIN/%.dis
+
+clean-std:V:
+	rm -f *.dis *.sbl *.s *.class
+
+nuke-std:V:	clean-std nuke-std-$SHELLTYPE
+
+nuke-std-rc nuke-std-nt:V:
+	rm -f $BINTARG
+
+nuke-std-sh:V:
+	for j in $TARG
+	do
+		rm -f $DISBIN/$j
+	done
--- /dev/null
+++ b/mkfiles/mklibsubdirs
@@ -1,0 +1,25 @@
+all:V:	all-$SHELLTYPE
+install:V: install-$SHELLTYPE
+uninstall:V: uninstall-$SHELLTYPE
+nuke:V: nuke-$SHELLTYPE
+clean:V: clean-$SHELLTYPE
+
+%-rc %-nt:QV:
+	for (j in $DIRS)
+	{
+		{
+			test -d $j && {
+				echo '@{builtin cd' $j ';' mk $MKFLAGS 'SYSTARG='$SYSTARG 'OBJTYPE='$OBJTYPE $stem'}'
+				@{builtin cd $j; mk $MKFLAGS 'SYSTARG='$SYSTARG 'OBJTYPE='$OBJTYPE $stem}
+			}
+		} || test ! -e $j
+	}
+
+%-sh:QV:
+	for j in $DIRS
+	do
+		if test -d $j; then
+			echo "(cd $j; mk $MKFLAGS SYSTARG=$SYSTARG OBJTYPE=$OBJTYPE $stem)"
+			(cd $j; mk $MKFLAGS 'SYSTARG='$SYSTARG 'OBJTYPE='$OBJTYPE $stem) || exit 1
+		fi || test ! -e $j
+	done
--- /dev/null
+++ b/mkfiles/mkone-nt
@@ -1,0 +1,40 @@
+libs=${LIBS:%=$ROOT/$OBJDIR/lib/lib%.a}
+TARGPROG=$TARG.exe
+
+all:V:	$O.out
+
+install:V:	$BIN/$TARGPROG
+
+installall:V:
+	for objtype in $CPUS
+	do
+		mk $MKFLAGS install
+	done
+
+nuke:V:	nuke-std
+
+clean:V: clean-std
+
+$O.out:	$OFILES $libs
+	$LD $LDFLAGS -out:$target $OFILES $libs $SYSLIBS
+
+%.$O:	$HFILES		# don't combine with following %.$O rules
+
+%.$O:	%.c
+	$CC $CFLAGS $stem.c
+
+%.$O:	%.s
+	$AS $ASFLAGS $stem.s
+
+y.tab.h y.tab.c:	$YFILES
+	$YACC $YFLAGS $YFILES
+
+clean-std:V:
+	rm -f *.$O *.exe $O.out y.tmp.*
+	rm -f y.tab.? y.debug y.output *.pdb *.pch
+
+nuke-std:V:	clean-std
+	rm -f y.tab.? y.debug y.output *.pdb *.pch
+
+$BIN/%:	$O.out
+	rm -f $BIN/$stem && cp $O.out $BIN/$stem
--- /dev/null
+++ b/mkfiles/mkone-rc
@@ -1,0 +1,40 @@
+libs=${LIBS:%=$ROOT/$OBJDIR/lib/lib%.a}
+
+all:V:	$O.out
+
+install:V:	$BIN/$TARG
+
+safeinstall:	$O.out
+	mv $BIN/$TARG $BIN/$TARG.`{date -n}
+	cp $O.out $BIN/$TARG
+
+installall:V:
+	for(objtype in $CPUS)
+		mk $MKFLAGS install
+
+nuke:V:	nuke-std
+
+clean:V: clean-std
+
+$O.out:	$OFILES $libs
+	$LD $LDFLAGS $OFILES $libs $SYSLIBS
+
+%.$O:	$HFILES		# don't combine with following %.$O rules
+
+%.$O:	%.c
+	$CC $CFLAGS $stem.c
+
+%.$O:	%.s
+	$AS $ASFLAGS $stem.s
+
+y.tab.h y.tab.c:	$YFILES
+	$YACC $YFLAGS $YFILES
+
+nuke-std:V:	clean-std
+	rm -f y.tab.? y.debug y.output
+
+clean-std:V:
+	rm -f *.[$OS] [$OS].out
+
+$BIN/%:	$O.out
+	rm -f $BIN/$stem && cp $O.out $BIN/$stem
--- /dev/null
+++ b/mkfiles/mkone-sh
@@ -1,0 +1,42 @@
+libs=${LIBS:%=$ROOT/$OBJDIR/lib/lib%.a}
+
+all:V:	$O.out
+
+install:V:	$BIN/$TARG
+
+installall:V:
+	for objtype in $CPUS
+	do
+		mk $MKFLAGS install
+	done
+
+nuke:V:	nuke-std
+
+clean:V: clean-std
+
+$O.out:	$OFILES $libs
+	$LD $LDFLAGS -o $target $OFILES $libs $SYSLIBS
+
+%.$O:	$HFILES		# don't combine with following %.$O rules
+
+%.$O:	%.c
+	$CC $CFLAGS -o $target $stem.c
+
+%.$O:	%.s
+	$AS $ASFLAGS -o $target $stem.s
+
+%.$O:	%.S$MACOSINF
+	$AS $ASFLAGS -o $target $stem.S
+
+y.tab.h y.tab.c:	$YFILES
+	$YACC $YFLAGS $YFILES
+
+clean-std:V:
+	rm -f core [$OS].out 
+	rm -f `echo $OS | sed 's/./ *.&/g'`
+
+nuke-std:V:	clean-std
+	rm -f y.tab.? y.debug y.output
+
+$BIN/%:	$O.out
+	rm -f $BIN/$stem && cp $O.out $BIN/$stem
--- /dev/null
+++ b/mkfiles/mksubdirs
@@ -1,0 +1,25 @@
+all:V:	all-$SHELLTYPE
+install:V: install-$SHELLTYPE
+uninstall:V: uninstall-$SHELLTYPE
+nuke:V: nuke-$SHELLTYPE
+clean:V: clean-$SHELLTYPE
+
+%-rc %-nt:QV:
+	for (j in $DIRS)
+	{
+		{
+			test -d $j && {
+				echo '@{builtin cd' $j '; mk $MKFLAGS $stem}'
+				@{builtin cd $j; mk $MKFLAGS $stem}
+			}
+		} || test ! -e $j
+	}
+
+%-sh:QV:
+	for j in $DIRS
+	do
+		if test -d $j; then
+			echo "(cd $j; mk $MKFLAGS $stem)"
+			(cd $j; mk $MKFLAGS $stem) || exit 1
+		fi || test ! -e $j
+	done
--- /dev/null
+++ b/mkfiles/mksyslib-nt
@@ -1,0 +1,54 @@
+#
+#	Rules for updating a library with Nt rcsh
+#
+LIBDIR=$ROOT/$OBJDIR/lib
+LIBRARY=$LIBDIR/$LIB
+LIBOBJ=${OFILES:%=$LIBRARY(%)}
+
+default:V:	all
+
+all install:V:	$LIBRARY
+
+installall:V:
+	for (objtype in $CPUS)
+		mk $MKFLAGS install
+
+clean:V:	clean-std	
+
+nuke:V:		nuke-std
+
+LIB1=${LIBRARY:%=$ARPREFIX%}
+
+$LIBRARY:	$LIBOBJ $OFILES
+	$AR $ARFLAGS $LIB1 $OFILES
+
+$LIBRARY(%.$O):N:	%.$O
+
+%.$O:	$HFILES		# don't combine with following %.$O rules
+
+%.$O:	%.c
+	$CC $CFLAGS $stem.c
+
+%.$O:	%.s
+	$AS $ASFLAGS $stem.s
+
+y.tab.h y.tab.c:	$YFILES
+	$YACC $YFLAGS $prereq
+
+clean-std:V:
+	rm -f *.$O y.tmp.*
+	rm -f y.tab.? y.output y.error *.pdb *.pch
+
+nuke-std:V:	clean-std
+	rm -f y.tab.? y.output y.error *.pdb *.pch
+	rm -f $LIBRARY
+
+#nuke-std:V:	clean-std $LIBDIR/fake.lib
+#	rm -f y.tab.? y.output y.error *.pdb *.pch
+#	cp $LIBDIR/fake.lib $LIBRARY
+#
+#$LIBDIR/fake.lib:
+#	echo 'void axzzzzzzz(void) { return; }' > fooxx.c
+#	$CC $CFLAGS fooxx.c
+#	$AR $ARFLAGS -out:$target fooxx.obj
+#	rm -f fooxx.*
--- /dev/null
+++ b/mkfiles/mksyslib-rc
@@ -1,0 +1,41 @@
+#
+#	Rules for updating a library with rc
+#
+LIBDIR=$ROOT/$OBJDIR/lib
+LIBRARY=$LIBDIR/$LIB
+LIBOBJ=${OFILES:%=$LIBRARY(%)}
+
+default:V:	all
+
+all install:V:	$LIBRARY
+
+installall:V:
+	for(objtype in $CPUS)
+		mk $MKFLAGS install
+
+clean:V: clean-std	
+
+nuke:V:	nuke-std
+
+$LIBRARY:	$LIBOBJ
+	$AR $ARFLAGS $target $newmember
+
+$LIBRARY(%.$O):N:	%.$O
+
+%.$O:	$HFILES		# don't combine with following %.$O rules
+
+%.$O:	%.c
+	$CC $CFLAGS $stem.c
+
+%.$O:	%.s
+	$AS $ASFLAGS $stem.s
+
+y.tab.h y.tab.c:	$YFILES
+	$YACC $YFLAGS $prereq
+
+clean-std:V:
+	rm -f *.[$OS] [$OS].out
+
+nuke-std:V:	clean-std
+	rm -f y.tab.? y.output y.error
+	rm -f $LIBRARY
--- /dev/null
+++ b/mkfiles/mksyslib-sh
@@ -1,0 +1,47 @@
+#
+#	Rules for updating a library with sh
+#
+LIBDIR=$ROOT/$OBJDIR/lib
+LIBRARY=$LIBDIR/$LIB
+LIBOBJ=${OFILES:%=$LIBRARY(%)}
+
+default:V:	all
+
+all install:V:	$LIBRARY
+
+installall:V:
+	for objtype in $CPUS
+	do
+		mk $MKFLAGS install
+	done
+
+clean:V:	clean-std	
+
+nuke:V:		nuke-std
+
+$LIBRARY:	$LIBOBJ
+	$AR $ARFLAGS $target $newmember
+
+$LIBRARY(%.$O):N:	%.$O
+
+%.$O:	$HFILES		# don't combine with following %.$O rules
+
+%.$O:	%.c
+	$CC $CFLAGS $stem.c
+
+%.$O:	%.s
+	$AS $ASFLAGS $stem.s
+
+%.$O:	%.S$MACOSINF
+	$AS $ASFLAGS -o $target $stem.S
+
+y.tab.h y.tab.c:	$YFILES
+	$YACC $YFLAGS $prereq
+
+clean-std:V:
+	rm -f $O.out
+	rm -f `echo $OS | sed 's/./ *.&/g'`
+
+nuke-std:V:	clean-std
+	rm -f y.tab.? y.output y.error
+	rm -f $LIBRARY
--- /dev/null
+++ b/module/9p.m
@@ -1,0 +1,179 @@
+Ninep: module
+{
+	PATH:	con "/dis/lib/ninep.dis";
+
+	VERSION:	con "9P2000";
+	MAXWELEM:	con 16;
+
+	NOTAG:	con 16rFFFF;
+	NOFID:	con int ~0;	# 32 bits in this version of 9P
+
+	BIT8SZ:	con 1;
+	BIT16SZ:	con 2;
+	BIT32SZ:	con 4;
+	BIT64SZ:	con 8;
+	QIDSZ:	con BIT8SZ+BIT32SZ+BIT64SZ;
+
+	STATFIXLEN:	con BIT16SZ+QIDSZ+5*BIT16SZ+4*BIT32SZ+BIT64SZ;	# amount of fixed length data in a stat buffer
+	IOHDRSZ:	con 24;	# room for Twrite/Rread header
+	DEFIOUNIT: con 8192;	# `reasonable' iounit
+	DEFMSIZE:	con IOHDRSZ+DEFIOUNIT;	# usable default for fversion and iounit
+
+	Tversion,		# 100
+	Rversion,
+	Tauth,		# 102
+	Rauth,
+	Tattach,		# 104
+	Rattach,
+	Terror,		# 106, illegal
+	Rerror,
+	Tflush,		#108
+	Rflush,
+	Twalk,		# 110
+	Rwalk,
+	Topen,		# 112
+	Ropen,
+	Tcreate,		# 114
+	Rcreate,
+	Tread,		# 116
+	Rread,
+	Twrite,		# 118
+	Rwrite,
+	Tclunk,		# 120
+	Rclunk,
+	Tremove,		# 122
+	Rremove,
+	Tstat,		# 124
+	Rstat,
+	Twstat,		#126
+	Rwstat,
+	Tmax: con 100+iota;
+
+	ERRMAX:	con 128;
+
+	OREAD:	con 0; 		# open for read
+	OWRITE:	con 1; 		# write
+	ORDWR:	con 2; 		# read and write
+	OEXEC:	con 3; 		# execute, == read but check execute permission
+	OTRUNC:	con 16; 		# or'ed in (except for exec), truncate file first
+	ORCLOSE: con 64; 		# or'ed in, remove on close
+
+	# mode bits in Dir.mode used by the protocol
+	DMDIR:		con int 1<<31;		# mode bit for directory
+	DMAPPEND:	con int 1<<30;		# mode bit for append-only files
+	DMEXCL:		con int 1<<29;		# mode bit for exclusive use files
+	DMAUTH:		con int 1<<27;		# mode bit for authentication files
+
+	# Qid.qtype
+	QTDIR:	con 16r80;
+	QTAPPEND:	con 16r40;
+	QTEXCL:	con 16r20;
+	QTAUTH:	con 16r08;
+	QTFILE:	con 16r00;
+
+	Tmsg: adt {
+		tag: int;
+		pick {
+		Readerror =>
+			error: string;		# tag is unused in this case
+		Version =>
+			msize: int;
+			version: string;
+		Auth =>
+			afid: int;
+			uname, aname: string;
+		Attach =>
+			fid, afid: int;
+			uname, aname: string;
+		Flush =>
+			oldtag: int;
+		Walk =>
+			fid, newfid: int;
+			names: array of string;
+		Open =>
+			fid, mode: int;
+		Create =>
+			fid: int;
+			name: string;
+			perm, mode: int;
+		Read =>
+			fid: int;
+			offset: big;
+			count: int;
+		Write =>
+			fid: int;
+			offset: big;
+			data: array of byte;
+		Clunk or
+		Stat or
+		Remove => 
+			fid: int;
+		Wstat =>
+			fid: int;
+			stat: Sys->Dir;
+		}
+
+		read:	fn(fd: ref Sys->FD, msize: int): ref Tmsg;
+		unpack:	fn(a: array of byte): (int, ref Tmsg);
+		pack:	fn(nil: self ref Tmsg): array of byte;
+		packedsize:	fn(nil: self ref Tmsg): int;
+		text:	fn(nil: self ref Tmsg): string;
+		mtype: fn(nil: self ref Tmsg): int;
+	};
+
+	Rmsg: adt {
+		tag: int;
+		pick {
+		Readerror =>
+			error: string;		# tag is unused in this case
+		Version =>
+			msize: int;
+			version: string;
+		Auth =>
+			aqid: Sys->Qid;
+		Attach =>
+			qid: Sys->Qid;
+		Flush =>
+		Error =>
+			ename: string;
+		Clunk or
+		Remove or
+		Wstat =>
+		Walk =>
+			qids: array of Sys->Qid;
+		Create or
+		Open =>
+			qid: Sys->Qid;
+			iounit: int;
+		Read =>
+			data: array of byte;
+		Write =>
+			count: int;
+		Stat =>
+			stat: Sys->Dir;
+		}
+
+		read:	fn(fd: ref Sys->FD, msize: int): ref Rmsg;
+		unpack:	fn(a: array of byte): (int, ref Rmsg);
+		pack:	fn(nil: self ref Rmsg): array of byte;
+		packedsize:	fn(nil: self ref Rmsg): int;
+		text:	fn(nil: self ref Rmsg): string;
+		mtype: fn(nil: self ref Rmsg): int;
+		write: fn(nil: self ref Rmsg, fd: ref Sys->FD, msize: int): int;
+	};
+
+	init:	fn();
+
+	readmsg:	fn(fd: ref Sys->FD, msize: int): (array of byte, string);
+	istmsg:	fn(f: array of byte): int;
+
+	compatible:	fn(t: ref Tmsg.Version, msize: int, version: string): (int, string);
+
+	packdirsize:	fn(d: Sys->Dir): int;
+	packdir:	fn(d: Sys->Dir): array of byte;
+	unpackdir: fn(f: array of byte): (int, Sys->Dir);
+	dir2text:	fn(d: Sys->Dir): string;
+	qid2text:	fn(q: Sys->Qid): string;
+
+	utflen:	fn(s: string): int;
+};
--- /dev/null
+++ b/module/NOTICE
@@ -1,0 +1,25 @@
+This copyright NOTICE applies to all files in this directory and
+subdirectories, unless another copyright notice appears in a given
+file or subdirectory.  If you take substantial code from this software to use in
+other programs, you must somehow include with it an appropriate
+copyright notice that includes the copyright notice and the other
+notices below.  It is fine (and often tidier) to do that in a separate
+file such as NOTICE, LICENCE or COPYING.
+
+Copyright © 1995-1999 Lucent Technologies Inc.
+Portions Copyright © 1997-2000 Vita Nuova Limited
+Portions Copyright © 2000-2010 Vita Nuova Holdings Limited
+
+This program is free software; you can redistribute it and/or modify
+it under the terms of the GNU Lesser General Public License (`LGPL') as published by
+the Free Software Foundation; either version 2 of the License, or
+(at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU Lesser General Public License
+along with this program; if not, write to the Free Software
+Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
--- /dev/null
+++ b/module/alphabet.m
@@ -1,0 +1,246 @@
+Alphabet: module {
+	PATH: con "/dis/alphabet/alphabet.dis";
+	ONDEMAND, CHECK: con 1<<iota;
+
+	init:	fn();
+	copy: fn(): Alphabet;
+	quit: fn();
+	loadtypeset: fn(qname: string, c: chan of ref Proxy->Typescmd[ref Value], errorc: chan of string): string;
+	declare: fn(qname: string, sig: string, flags: int): string;
+	undeclare: fn(name: string): string;
+	importmodule: fn(qname: string): string;
+	importtype: fn(qname: string): string;
+	importvalue: fn(v: ref Value, qname: string): (ref Value, string);
+	autoconvert: fn(src, dst: string, transform: ref Sh->Cmd, errorc: chan of string): string;
+	define: fn(name: string, expr: ref Sh->Cmd, errorc: chan of string): string;
+	setautodeclare: fn(on: int);
+
+	Decltypeset: adt {
+		name: string;
+		alphabet: string;
+		types: array of string;
+		mods: array of (string, string);
+	};
+	Declarations: adt {
+		typesets: array of Decltypeset;
+		defs: array of (string, string);
+	};
+#	getdecls: fn(): ref Declarations;
+#	getexprdecls: fn(e: ref Sh->Cmd): ref Declarations;
+#	declcompat: fn(d0, d1: ref Declarations): int;
+
+	getmodule: fn(name: string): (string, string, ref Sh->Cmd);
+	gettypesets: fn(): list of string;
+	getmodules: fn(): list of string;
+	gettypesetmodules: fn(tsname: string): chan of string;
+	gettypes: fn(typeset: string): list of string;
+	getautoconversions: fn(): list of (string, string, ref Sh->Cmd);
+	typecompat: fn(t0, t1: string): (int, string);
+	show: fn();
+
+	mkqname: fn(typeset, name: string): string;
+	canon: fn(qname: string): string;
+	splitqname: fn(qname: string): (string, string);
+	parse: fn(expr: string): (ref Sh->Cmd, string);
+
+	eval: fn(expr: ref Sh->Cmd,
+			drawctxt: ref Draw->Context,
+			args: list of ref Value): string;
+	eval0: fn(expr: ref Sh->Cmd,
+			dsttype: string,
+			drawctxt: ref Draw->Context,
+			report: ref Reports->Report,
+			errorc: chan of string,
+			args: list of ref Value,
+			vc: chan of ref Value);
+	rewrite: fn(expr: ref Sh->Cmd, dsttype: string,
+			errorc: chan of string): (ref Sh->Cmd, string);
+	
+	Value: adt {
+		free:		fn(v: self ref Value, used: int);
+		dup:		fn(v: self ref Value): ref Value;
+		gets:		fn(v: self ref Value): string;
+		isstring:	fn(v: self ref Value): int;
+		type2s:	fn(tc: int): string;
+		typec:	fn(v: self ref Value): int;
+		typename: fn(v: self ref Value): string;
+
+		c: fn(v: self ref Value): ref Value.Vc;
+		s: fn(v: self ref Value): ref Value.Vs;
+		r: fn(v: self ref Value): ref Value.Vr;
+		f: fn(v: self ref Value): ref Value.Vf;
+		w: fn(v: self ref Value): ref Value.Vw;
+		d: fn(v: self ref Value): ref Value.Vd;
+		z: fn(v: self ref Value): ref Value.Vz;
+
+		pick{
+		Vc =>
+			i: ref Sh->Cmd;
+		Vs =>
+			i: string;
+		Vr =>
+			i: chan of string;
+		Vf or
+		Vw =>
+			i: chan of ref Sys->FD;
+		Vd =>
+			i: Datachan;
+		Vz =>
+			i: Proxyval;		# a proxy for the actual value, held by another process
+		}
+	};
+
+	Proxyval: adt {
+		typec: int;
+		id: int;
+	};
+
+	Datachan: adt {
+		d: chan of array of byte;
+		stop: chan of int;
+	};
+};
+
+Mainmodule: module {
+	typesig: fn(): string;
+	init:	fn();
+	quit: fn();
+	run: fn(ctxt: ref Draw->Context, r: ref Reports->Report, errorc: chan of string,
+		opts: list of (int, list of ref Alphabet->Value), args: list of ref Alphabet->Value): ref Alphabet->Value;
+};
+
+# evaluate an expression
+Eval: module {
+	PATH: con "/dis/alphabet/eval.dis";
+	init: fn();
+
+	Context: adt[V, M, Ectxt]
+		for {
+		V =>
+			dup:		fn(t: self V): V;
+			free:		fn(v: self V, used: int);
+			isstring:	fn(v: self V): int;
+			gets:		fn(t: self V): string;
+			type2s:	fn(tc: int): string;
+			typec:	fn(t: self V): int;
+		M =>
+			find:		fn(c: Ectxt, s: string): (M, string);
+			typesig:	fn(m: self M): string;
+			run:		fn(m: self M, c: Ectxt, errorc: chan of string,
+						opts: list of (int, list of V), args: list of V): V;
+			mks:		fn(c: Ectxt, s: string): V;
+			mkc:		fn(c: Ectxt, cmd: ref Sh->Cmd): V;
+			typename2c: fn(s: string): int;
+			cvt:		fn(c: Ectxt, v: V, tc: int, errorc: chan of string): V;
+		}
+	{
+		eval: fn(
+			expr: ref Sh->Cmd,
+			ctxt: Ectxt,
+			errorc: chan of string,
+			args: list of V
+		): V;
+	};
+	cmdusage: fn[V](nil: V, sig: string): string
+		for {
+		V =>
+			type2s:	fn(tc: int): string;
+		};
+	usage2sig: fn[V](nil: V, u: string): (string, string)
+		for{
+		V =>
+			typename2c: fn(s: string): int;
+		};
+	blocksig: fn[M, Ectxt](nil: M, ctxt: Ectxt, c: ref Sh->Cmd): (string, string)
+		for{
+		M =>
+			typename2c: fn(s: string): int;
+			find:	fn(c: Ectxt, s: string): (M, string);
+			typesig: fn(m: self M): string;
+		};
+	typecompat: fn(t0, t1: string): int;
+	splittype: fn(t: string): (int, string, string);
+};
+
+Extvalues: module {
+	PATH: con "/dis/alphabet/extvalues.dis";
+	Values: adt[V] {
+		lock: chan of int;
+		v: array of (int, V);
+		freeids: list of int;
+		new: fn(): ref Values[V];
+		add: fn(vals: self ref Values, v: V): int;
+		inc: fn(vals: self ref Values, id: int);
+		del: fn(vals: self ref Values, id: int);
+	};
+};
+
+# generic proxy implementation:
+Proxy: module {
+	PATH: con "/dis/alphabet/proxy.dis";
+
+	# operators on a type system
+	Typescmd: adt[V] {
+		pick {
+		Load =>
+			cmd: string;
+			reply: chan of (chan of ref Modulecmd[V], string);
+		Dup =>
+			v: V;
+			reply: chan of V;
+		Free =>
+			v: V;
+			used: int;
+			reply: chan of int;
+		Alphabet =>
+			reply: chan of string;
+		Type2s =>
+			tc: int;
+			reply: chan of string;
+		Loadtypes =>
+			name: string;
+			reply: chan of (chan of ref Typescmd[V], string);
+		Modules =>
+			reply: chan of string;
+		}
+	};
+	
+	# proxy for a loaded module.
+	Modulecmd: adt[V] {
+		pick {
+		Typesig =>
+			reply: chan of string;
+		Run =>
+			ctxt: ref Draw->Context;
+			report: ref Reports->Report;
+			errorc: chan of string;
+#			stopc: chan of int;
+			opts: list of (int, list of V);
+			args: list of V;
+			reply: chan of V;
+		}
+	};
+
+	proxy: fn[Ctxt,Cvt,M,V,EV](ctxt: Ctxt): (
+			chan of ref Proxy->Typescmd[EV],
+			chan of (string, chan of ref Proxy->Typescmd[V])
+		) for {
+		M =>
+			typesig: fn(m: self M): string;
+			run: fn(m: self M, ctxt: ref Draw->Context, r: ref Reports->Report, errorc: chan of string,
+					opts: list of (int, list of V), args: list of V): V;
+			quit: fn(m: self M);
+		Ctxt =>
+			loadtypes: fn(ctxt: self Ctxt, name: string): (chan of ref Proxy->Typescmd[V], string);
+			type2s: fn(ctxt: self Ctxt, tc: int): string;
+			alphabet: fn(ctxt: self Ctxt): string;
+			modules: fn(ctxt: self Ctxt, r: chan of string);
+			find: fn(ctxt: self Ctxt, s: string): (M, string);
+			getcvt: fn(ctxt: self Ctxt): Cvt;
+		Cvt =>
+			int2ext: fn(cvt: self Cvt, v: V): EV;
+			ext2int: fn(cvt: self Cvt, ev: EV): V;
+			free: fn(cvt: self Cvt, v: EV, used: int);
+			dup:	fn(cvt: self Cvt, v: EV): EV;
+	};
+};
--- /dev/null
+++ b/module/alphabet/abc.m
@@ -1,0 +1,74 @@
+# warning: autogenerated code; don't bother to change this, change mktypeset.b or abc.b instead
+Abc: module {
+	PATH: con "/dis/alphabet/abc.dis";
+	Value: adt {
+		m:	fn(v: self ref Value): ref Value.Vm;	# vmods
+		t:	fn(v: self ref Value): ref Value.Vt;	# vtypes
+		A:	fn(v: self ref Value): ref Value.VA;	# abc
+		w:	fn(v: self ref Value): ref Value.Vw;	# wfd
+		c:	fn(v: self ref Value): ref Value.Vc;	# cmd
+		r:	fn(v: self ref Value): ref Value.Vr;	# status
+		f:	fn(v: self ref Value): ref Value.Vf;	# fd
+		s:	fn(v: self ref Value): ref Value.Vs;	# string
+		typec: fn(v: self ref Value): int;
+		type2s:	fn(t: int): string;
+		free: fn(v: self ref Value, used: int);
+		dup:	fn(v: self ref Value): ref Value;
+		pick {
+		Vm =>
+			i: Modulesval;
+		Vt =>
+			i: Typesval;
+		VA =>
+			i: Abcval;
+		Vw =>
+			i: chan of ref Sys->FD;
+		Vc =>
+			i: ref Sh->Cmd;
+		Vr =>
+			i: chan of string;
+		Vf =>
+			i: chan of ref Sys->FD;
+		Vs =>
+			i: string;
+		}
+	};
+	init: fn();
+	Abcval: adt {
+		refcount: chan of int;
+		alphabet: Alphabet;
+	};
+	Typesval: adt {
+		abc: ref Value.VA;
+		types: list of ref Type;
+	};
+	Modulesval: adt {
+		abc: ref Value.VA;
+		types: list of ref Type;
+		mods: list of (string, string, ref Sh->Cmd);
+		query: chan of (string, chan of (string, ref Sh->Cmd));
+	};
+	Type: adt {
+		name: string;
+		actname: string;
+		nodup: int;
+		destructor: ref Sh->Cmd;
+	};
+	mkabc: fn(alphabet: Alphabet): ref Value.VA;
+
+};
+
+Abcmodule: module {
+	types: fn(): string;
+	init: fn();
+	quit: fn();
+	run: fn(errorc: chan of string, r: ref Reports->Report,
+		opts: list of (int, list of ref Abc->Value), args: list of ref Abc->Value): ref Abc->Value;
+};
+
+Declares: module {
+	PATH: con "/dis/alphabet/abc/declares.dis";
+	init: fn();
+	quit: fn();
+	declares: fn(a: Alphabet, decls: ref Sh->Cmd, errorc: chan of string, stopc: chan of int): string;
+};
--- /dev/null
+++ b/module/alphabet/abcstyx.m
@@ -1,0 +1,33 @@
+ABCStyx: module {
+	PATH: con "/dis/alphabet/abcstyx.dis";
+	Value: adt {
+		c:	fn(v: self ref Value): ref Value.C;	# cmd
+		s:	fn(v: self ref Value): ref Value.S;	# string
+		w:	fn(v: self ref Value): ref Value.W;	# wfd
+		x:	fn(v: self ref Value): ref Value.D;	# styx
+
+		typec: fn(v: self ref Value): int;
+		type2s:	fn(t: int): string;
+		discard: fn(v: self ref Value);
+		reusable:	fn(v: self ref Value): int;
+
+		pick {
+		S =>
+			i: string;
+		C =>
+			i: ref Sh->Cmd;
+		W =>
+			i: chan of ref Sys->FD;
+		X =>
+			i: (chan of ref Styx->Rmsg, chan of ref Styx->Tmsg);
+		}
+	};
+	init: fn();
+};
+
+Styxmodule: module {
+	types: fn(): string;
+	init: fn();
+	run: fn(errorc: chan of string, r: ref Reports->Report,
+		opts: list of (int, list of ref ABCStyx->Value), args: list of ref ABCStyx->Value): ref ABCStyx->Value;
+};
--- /dev/null
+++ b/module/alphabet/abctypes.m
@@ -1,0 +1,23 @@
+# warning: autogenerated code; don't bother to change this, change mktypeset.b or abc.b instead
+Abctypes: module {
+	PATH: con "/dis/alphabet/abctypes.dis";
+	Abccvt: adt {
+		values: ref Extvalues->Values[ref Abc->Value];
+
+		int2ext: fn(cvt: self ref Abccvt, v: ref Abc->Value): ref Alphabet->Value;
+		ext2int: fn(cvt: self ref Abccvt, ev: ref Alphabet->Value): ref Abc->Value;
+		dup: fn(cvt: self ref Abccvt, ev: ref Alphabet->Value): ref Alphabet->Value;
+		free: fn(cvt: self ref Abccvt, ev: ref Alphabet->Value, used: int);
+	};
+
+	proxy: fn(): chan of ref Proxy->Typescmd[ref Alphabet->Value];
+	proxy0: fn(): (
+			chan of ref Proxy->Typescmd[ref Alphabet->Value],
+			chan of (string, chan of ref Proxy->Typescmd[ref Abc->Value]),
+			ref Abccvt
+		);
+};
+
+Abcsubtypes: module {
+	proxy: fn(): chan of ref Proxy->Typescmd[ref Abc->Value];
+};
--- /dev/null
+++ b/module/alphabet/endpoints.m
@@ -1,0 +1,14 @@
+Endpoints: module {
+	PATH: con "/dis/alphabet/endpoints.dis";
+	Endpoint: adt {
+		addr: string;
+		id: string;
+		about: string;
+		text: fn(e: self Endpoint): string;
+		mk: fn(s: string): Endpoint;
+	};
+	init: fn();
+	new: fn(net, addr: string, force: int): string;
+	create: fn(addr: string): (ref Sys->FD, Endpoint);
+	open: fn(net: string, ep: Endpoint): (ref Sys->FD, string);
+};
--- /dev/null
+++ b/module/alphabet/extvalues.m
@@ -1,0 +1,12 @@
+Extvalues: module {
+	PATH: con "/dis/alphabet/extvalues.dis";
+	Values: adt[V] {
+		lock: chan of int;
+		v: array of (int, V);
+		freeids: list of int;
+		new: fn(): ref Values[V];
+		add: fn(vals: self ref Values, v: V): int;
+		inc: fn(vals: self ref Values, id: int);
+		del: fn(vals: self ref Values, id: int);
+	};
+};
--- /dev/null
+++ b/module/alphabet/fs.m
@@ -1,0 +1,82 @@
+# warning: autogenerated code; don't bother to change this, change mktypeset.b or fs.b instead
+Fs: module {
+	PATH: con "/dis/alphabet/fs.dis";
+	Value: adt {
+		r:	fn(v: self ref Value): ref Value.Vr;	# status
+		d:	fn(v: self ref Value): ref Value.Vd;	# data
+		c:	fn(v: self ref Value): ref Value.Vc;	# command
+		f:	fn(v: self ref Value): ref Value.Vf;	# fd
+		s:	fn(v: self ref Value): ref Value.Vs;	# string
+		m:	fn(v: self ref Value): ref Value.Vm;	# selector
+		p:	fn(v: self ref Value): ref Value.Vp;	# gate
+		t:	fn(v: self ref Value): ref Value.Vt;	# entries
+		x:	fn(v: self ref Value): ref Value.Vx;	# fs
+		typec: fn(v: self ref Value): int;
+		type2s:	fn(t: int): string;
+		free: fn(v: self ref Value, used: int);
+		dup:	fn(v: self ref Value): ref Value;
+		pick {
+		Vr =>
+			i: chan of string;
+		Vd =>
+			i: Datachan;
+		Vc =>
+			i: ref Sh->Cmd;
+		Vf =>
+			i: chan of ref Sys->FD;
+		Vs =>
+			i: string;
+		Vm =>
+			i: Cmpchan;
+		Vp =>
+			i: Gatechan;
+		Vt =>
+			i: Entrychan;
+		Vx =>
+			i: Fschan;
+		}
+	};
+	init: fn();
+	sendnulldir:	fn(c: Fschan): int;
+	copy:		fn(src, dst: Fschan): int;
+	Option: adt {
+		opt: int;
+		args: list of ref Value;
+	};
+	Datachan: adt {
+		d: chan of array of byte;
+		stop: chan of int;
+	};
+	Entrychan: adt {
+		sync: chan of int;
+		c: chan of Entry;
+	};
+	Cmpchan: type chan of (ref Sys->Dir, ref Sys->Dir, chan of int);
+	Entry: type (ref Sys->Dir, string, int);
+	Gatequery: type (Entry, chan of int);
+	Gatechan: type chan of Gatequery;
+	Fsdata: adt {
+		dir: ref Sys->Dir;
+		data: array of byte;
+	};
+	Fschan: type chan of (Fsdata, chan of int);
+	Next, Down, Skip, Quit: con iota;
+	Nilentry: con (nil, nil, 0);
+
+};
+
+Fsmodule: module {
+	types: fn(): string;
+	init:	fn();
+	run: fn(ctxt: ref Draw->Context, r: ref Reports->Report,
+		opts: list of Fs->Option, args: list of ref Fs->Value): ref Fs->Value;
+};
+Fsfilter: module {
+	PATH: con "/dis/alphabet/fsfilter.dis";
+	filter: fn[T](t: T, src, dst: Fs->Fschan)
+		for{
+		T =>
+			query: fn(t: self T, d: ref Sys->Dir, name: string, depth: int): int;
+		};
+};
+
--- /dev/null
+++ b/module/alphabet/fstypes.m
@@ -1,0 +1,23 @@
+# warning: autogenerated code; don't bother to change this, change mktypeset.b or fs.b instead
+Fstypes: module {
+	PATH: con "/dis/alphabet/fstypes.dis";
+	Fscvt: adt {
+		values: ref Extvalues->Values[ref Fs->Value];
+
+		int2ext: fn(cvt: self ref Fscvt, v: ref Fs->Value): ref Alphabet->Value;
+		ext2int: fn(cvt: self ref Fscvt, ev: ref Alphabet->Value): ref Fs->Value;
+		dup: fn(cvt: self ref Fscvt, ev: ref Alphabet->Value): ref Alphabet->Value;
+		free: fn(cvt: self ref Fscvt, ev: ref Alphabet->Value, used: int);
+	};
+
+	proxy: fn(): chan of ref Proxy->Typescmd[ref Alphabet->Value];
+	proxy0: fn(): (
+			chan of ref Proxy->Typescmd[ref Alphabet->Value],
+			chan of (string, chan of ref Proxy->Typescmd[ref Fs->Value]),
+			ref Fscvt
+		);
+};
+
+Fssubtypes: module {
+	proxy: fn(): chan of ref Proxy->Typescmd[ref Fs->Value];
+};
--- /dev/null
+++ b/module/alphabet/grid.m
@@ -1,0 +1,43 @@
+# warning: autogenerated code; don't bother to change this, change mktypeset.b or grid.b instead
+Grid: module {
+	PATH: con "/dis/alphabet/grid.dis";
+	Value: adt {
+		b:	fn(v: self ref Value): ref Value.Vb;	# records
+		e:	fn(v: self ref Value): ref Value.Ve;	# endpoint
+		w:	fn(v: self ref Value): ref Value.Vw;	# wfd
+		c:	fn(v: self ref Value): ref Value.Vc;	# cmd
+		r:	fn(v: self ref Value): ref Value.Vr;	# status
+		f:	fn(v: self ref Value): ref Value.Vf;	# fd
+		s:	fn(v: self ref Value): ref Value.Vs;	# string
+		typec: fn(v: self ref Value): int;
+		type2s:	fn(t: int): string;
+		free: fn(v: self ref Value, used: int);
+		dup:	fn(v: self ref Value): ref Value;
+		pick {
+		Vb =>
+			i: chan of ref Sys->FD;
+		Ve =>
+			i: chan of Endpoints->Endpoint;
+		Vw =>
+			i: chan of ref Sys->FD;
+		Vc =>
+			i: ref Sh->Cmd;
+		Vr =>
+			i: chan of string;
+		Vf =>
+			i: chan of ref Sys->FD;
+		Vs =>
+			i: string;
+		}
+	};
+	init: fn();
+
+};
+
+Gridmodule: module {
+	types: fn(): string;
+	init: fn();
+	run: fn(errorc: chan of string, r: ref Reports->Report,
+		opts: list of (int, list of ref Grid->Value), args: list of ref Grid->Value): ref Grid->Value;
+};
+
--- /dev/null
+++ b/module/alphabet/gridtypes.m
@@ -1,0 +1,23 @@
+# warning: autogenerated code; don't bother to change this, change mktypeset.b or grid.b instead
+Gridtypes: module {
+	PATH: con "/dis/alphabet/gridtypes.dis";
+	Gridcvt: adt {
+		values: ref Extvalues->Values[ref Grid->Value];
+
+		int2ext: fn(cvt: self ref Gridcvt, v: ref Grid->Value): ref Alphabet->Value;
+		ext2int: fn(cvt: self ref Gridcvt, ev: ref Alphabet->Value): ref Grid->Value;
+		dup: fn(cvt: self ref Gridcvt, ev: ref Alphabet->Value): ref Alphabet->Value;
+		free: fn(cvt: self ref Gridcvt, ev: ref Alphabet->Value, used: int);
+	};
+
+	proxy: fn(): chan of ref Proxy->Typescmd[ref Alphabet->Value];
+	proxy0: fn(): (
+			chan of ref Proxy->Typescmd[ref Alphabet->Value],
+			chan of (string, chan of ref Proxy->Typescmd[ref Grid->Value]),
+			ref Gridcvt
+		);
+};
+
+Gridsubtypes: module {
+	proxy: fn(): chan of ref Proxy->Typescmd[ref Grid->Value];
+};
--- /dev/null
+++ b/module/alphabet/reports.m
@@ -1,0 +1,16 @@
+Reports: module {
+	PATH: con "/dis/alphabet/reports.dis";
+	Report: adt {
+		startc: chan of (string, chan of string, chan of int);
+		enablec: chan of int;
+	
+		enable:	fn(r: self ref Report);
+		start:		fn(r: self ref Report, name: string): chan of string;
+		add:		fn(r: self ref Report, name: string, errorc: chan of string, stopc: chan of int);
+	};
+	KILL, PROPAGATE: con 1<<iota;
+	reportproc: fn(errorc: chan of string, stopc: chan of int, reply: chan of ref Report);
+	quit: fn(errorc: chan of string);
+	report: fn(errorc: chan of string, err: string);
+	newpgrp: fn(stopc: chan of int, flags: int): chan of int;
+};
--- /dev/null
+++ b/module/arg.m
@@ -1,0 +1,14 @@
+Arg : module
+{
+	PATH: con "/dis/lib/arg.dis";
+
+	init: fn(argv: list of string);
+	setusage: fn(usage: string);
+	usage: fn();
+	opt: fn(): int;
+	arg: fn(): string;
+	earg: fn(): string;
+
+	progname: fn(): string;
+	argv: fn(): list of string;
+};
--- /dev/null
+++ b/module/asn1.m
@@ -1,0 +1,101 @@
+ASN1: module {
+	PATH: con "/dis/lib/asn1.dis";
+
+	# Tag classes
+	Universal : con 0;
+	Application : con 16r40;
+	Context : con 16r80;
+	Private : con 16rC0;
+
+	# Universal tags
+	BOOLEAN : con 1;
+	INTEGER : con 2;
+	BIT_STRING : con 3;
+	OCTET_STRING : con 4;
+	NULL : con 5;
+	OBJECT_ID: con 6;
+	ObjectDescriptor : con 7;
+	EXTERNAL : con 8;
+	REAL : con 9;
+	ENUMERATED : con 10;
+	EMBEDDED_PDV : con 11;
+	SEQUENCE : con 16;		# also SEQUENCE OF
+	SET : con 17;			# also SET  OF
+	NumericString : con 18;
+	PrintableString : con 19;
+	TeletexString : con 20;
+	VideotexString : con 21;
+	IA5String : con 22;
+	UTCTime : con 23;
+	GeneralizedTime : con 24;
+	GraphicString : con 25;
+	VisibleString : con 26;
+	GeneralString : con 27;
+	UniversalString : con 28;
+	BMPString : con 30;
+
+	Elem: adt {
+		tag: Tag;
+		val: ref Value;
+
+		is_seq: fn(e: self ref Elem) : (int, list of ref Elem);
+		is_set: fn(e: self ref Elem) : (int, list of ref Elem);
+		is_int: fn(e: self ref Elem) : (int, int);
+		is_bigint: fn(e: self ref Elem) : (int, array of byte);
+		is_bitstring: fn(e: self ref Elem) : (int, int, array of byte);
+		is_octetstring: fn(e: self ref Elem) : (int, array of byte);
+		is_oid: fn(e: self ref Elem) : (int, ref Oid);
+		is_string: fn(e: self ref Elem) : (int, string);
+		is_time: fn(e: self ref Elem) : (int, string);
+		tostring: fn(e: self ref Elem) : string;
+	};
+
+	Tag: adt {
+		class: int;
+		num: int;
+		constr: int;	# ignored by encode()
+
+		tostring: fn(t: self Tag) : string;
+	};
+
+	Value: adt {
+		pick {
+			Bool or Int =>
+				v: int;
+			Octets or BigInt or Real or Other =>
+				# BigInt: integer too big for limbo int
+				# Real: we don't care to decode these
+				#    since they are hardly ever used
+				bytes: array of byte;
+			BitString =>
+				# pack into bytes, with perhaps some
+				# unused bits in last byte
+				unusedbits: int;
+				bits: array of byte;
+			Null or EOC =>
+				dummy: int;
+			ObjId =>
+				id: ref Oid;
+			String =>
+				s: string;
+			Seq or Set =>
+				l: list of ref Elem;
+		}
+
+		tostring : fn(v: self ref Value) : string;
+	};
+
+	Oid: adt {
+		nums: array of int;
+
+		tostring: fn(o: self ref Oid) : string;
+	};
+
+	init: fn();
+	decode: fn(a: array of byte) : (string, ref Elem);
+	decode_seq: fn(a: array of byte) : (string, list of ref Elem);
+	decode_value: fn(a: array of byte, kind, constr: int) : (string, ref Value);
+	encode: fn(e: ref Elem) : (string, array of byte);
+	oid_lookup: fn(o: ref Oid, tab: array of Oid) : int;
+	print_elem: fn(e: ref Elem);
+};
--- /dev/null
+++ b/module/attrdb.m
@@ -1,0 +1,110 @@
+Attrdb: module
+{
+	PATH:	con "/dis/lib/attrdb.dis";
+
+	Attr: adt {
+		attr:	string;
+		val:	string;
+		tag:	int;		# application-defined data, initially 0
+	};
+
+	Tuples: adt {
+		n:	int;
+		pairs:	list of ref Attr;
+
+		hasattr:	fn(t: self ref Tuples, attr: string): int;
+		haspair:	fn(t: self ref Tuples, attr: string, value: string): int;
+		find:	fn(t: self ref Tuples, attr: string): list of ref Attr;
+		findbyattr:	fn(t: self ref Tuples, attr: string, value: string, rattr: string): list of ref Attr;
+	};
+
+	Dbentry: adt {
+		n:	int;
+		lines:	list of ref Tuples;
+
+		find:	fn(e: self ref Dbentry, attr: string): list of (ref Tuples, list of ref Attr);
+		findfirst:	fn(e: self ref Dbentry, attr: string): string;
+		findpair:	fn(e: self ref Dbentry, attr: string, value: string): list of ref Tuples;
+		findbyattr:	fn(e: self ref Dbentry, attr: string, value: string, rattr: string): list of (ref Tuples, list of ref Attr);
+	};
+
+	Dbptr: adt {
+		dbs:	list of ref Dbf;
+		index:	ref Attrindex->Index;
+		pick{
+		Direct =>
+			offset:	int;
+		Hash =>
+			current:	int;
+			next:	int;
+		}
+	};
+
+	Dbf: adt {
+		fd:	ref Bufio->Iobuf;
+		name:	string;
+		dir:	ref Sys->Dir;
+		indices:	list of ref Attrindex->Index;
+		lockc:	chan of int;
+
+		open:	fn(path: string): ref Dbf;
+		sopen:	fn(data: string): ref Dbf;
+		changed:	fn(f: self ref Dbf): int;
+		reopen:	fn(f: self ref Dbf): int;
+
+		# for supporting commands:
+		readentry:	fn(dbf: self ref Dbf, offset: int, attr: string, value: string, useval: int): (ref Dbentry, int, int);
+	};
+
+	Db: adt {
+		dbs:		list of ref Dbf;
+
+		open:	fn(path: string): ref Db;
+		sopen:	fn(data: string): ref Db;
+		append:	fn(db1: self ref Db, db2: ref Db): ref Db;
+		changed:	fn(db: self ref Db): int;
+		reopen:	fn(db: self ref Db): int;
+
+		find:	fn(db: self ref Db, start: ref Dbptr, attr: string): (ref Dbentry, ref Dbptr);
+		findpair:	fn(db: self ref Db, start: ref Dbptr, attr: string, value: string): (ref Dbentry, ref Dbptr);
+		findbyattr:	fn(db: self ref Db, start: ref Dbptr, attr: string, value: string, rattr: string): (ref Dbentry, ref Dbptr);
+	};
+
+	init:	fn(): string;
+
+	parseentry:	fn(s: string, lno: int): (ref Dbentry, int, string);
+	parseline:	fn(s: string, lno: int): (ref Tuples, string);
+};
+
+Attrindex: module
+{
+	PATH:	con "/dis/lib/attrhash.dis";
+
+	Index: adt {
+		fd:	ref Sys->FD;
+		attr:	string;
+		mtime:	int;
+		size:	int;
+		tab:	array of byte;
+
+		open:	fn(dbf: Attrdb->Dbf, attr: string, fd: ref Sys->FD): ref Index;
+	};
+
+	init:	fn(): string;
+};
+
+Attrhash: module
+{
+	PATH:	con "/dis/lib/attrhash.dis";
+
+	NDBPLEN: con 3;	# file pointer length in bytes
+	NDBHLEN:	con 4+4;	# file header length (mtime[4], length[4])
+	NDBSPEC:	con 1<<23;	# flag bit for something special
+	NDBCHAIN: con NDBSPEC;	# pointer to collision chain
+	NDBNAP:	con NDBSPEC | 1;	# not a pointer
+
+	init:	fn(): string;
+	attrindex:	fn(): Attrindex;
+	hash:	fn(s: string, hlen: int): int;
+	get3, get4:	fn(a: array of byte): int;
+};
--- /dev/null
+++ b/module/auth9.m
@@ -1,0 +1,103 @@
+Auth9: module
+{
+	PATH:	con "/dis/lib/auth9.dis";
+
+	#
+	# plan 9 authentication
+	#
+
+	ANAMELEN: con 	28; # maximum size of name in previous proto
+	AERRLEN: con 	64; # maximum size of errstr in previous proto
+	DOMLEN: con 		48; # length of an authentication domain name
+	DESKEYLEN: con 	7; # length of a des key for encrypt/decrypt
+	CHALLEN: con 	8; # length of a plan9 sk1 challenge
+	NETCHLEN: con 	16; # max network challenge length (used in AS protocol)
+	SECRETLEN: con 	32; # max length of a secret
+
+	# encryption numberings (anti-replay)
+	AuthTreq: con 1; 	# ticket request
+	AuthChal: con 2; 	# challenge box request
+	AuthPass: con 3; 	# change password
+	AuthOK: con 4; 	# fixed length reply follows
+	AuthErr: con 5; 	# error follows
+	AuthMod: con 6; 	# modify user
+	AuthApop: con 7; 	# apop authentication for pop3
+	AuthOKvar: con 9; 	# variable length reply follows
+	AuthChap: con 10; 	# chap authentication for ppp
+	AuthMSchap: con 11; 	# MS chap authentication for ppp
+	AuthCram: con 12; 	# CRAM verification for IMAP (RFC2195 & rfc2104)
+	AuthHttp: con 13; 	# http domain login
+	AuthVNC: con 14; 	# VNC server login (deprecated)
+
+
+	AuthTs: con 64;	# ticket encrypted with server's key
+	AuthTc: con 65;	# ticket encrypted with client's key
+	AuthAs: con 66;	# server generated authenticator
+	AuthAc: con 67;	# client generated authenticator
+	AuthTp: con 68;	# ticket encrypted with client's key for password change
+	AuthHr: con 69;	# http reply
+
+	Ticketreq: adt {
+		rtype: int;
+		authid: string;	# [ANAMELEN]	server's encryption id
+		authdom: string;	# [DOMLEN]	server's authentication domain
+		chal:	array of byte; # [CHALLEN]	challenge from server
+		hostid: string;	# [ANAMELEN]		host's encryption id
+		uid: string;	# [ANAMELEN]	uid of requesting user on host
+
+		pack:	fn(t: self ref Ticketreq): array of byte;
+		unpack:	fn(a: array of byte): (int, ref Ticketreq);
+	};
+		TICKREQLEN: con	3*ANAMELEN+CHALLEN+DOMLEN+1;
+
+	Ticket: adt {
+		num: int;	# replay protection
+		chal:	array of byte;	# [CHALLEN]	server challenge
+		cuid: string;	# [ANAMELEN]	uid on client
+		suid: string;	# [ANAMELEN]	uid on server
+		key:	array of byte;	# [DESKEYLEN]	nonce DES key
+
+		pack:	fn(t: self ref Ticket, key: array of byte): array of byte;
+		unpack:	fn(a: array of byte, key: array of byte): (int, ref Ticket);
+	};
+		TICKETLEN: con CHALLEN+2*ANAMELEN+DESKEYLEN+1;
+
+	Authenticator: adt {
+		num: int;			# replay protection
+		chal: array of byte;	# [CHALLEN]
+		id:	int;			# authenticator id, ++'d with each auth
+
+		pack:	fn(f: self ref Authenticator, key: array of byte): array of byte;
+		unpack:	fn(a: array of byte, key: array of byte): (int, ref Authenticator);
+	};
+		AUTHENTLEN: con CHALLEN+4+1;
+
+	Passwordreq: adt {
+		num: int;
+		old:	array of byte;	# [ANAMELEN]
+		new:	array of byte;	# [ANAMELEN]
+		changesecret:	int;
+		secret:	array of byte; # [SECRETLEN]	new secret
+
+		pack:	fn(f: self ref Passwordreq, key: array of byte): array of byte;
+		unpack:	fn(a: array of byte, key: array of byte): (int, ref Passwordreq);
+	};
+	PASSREQLEN: con	2*ANAMELEN+1+1+SECRETLEN;
+
+	# secure ID and Plan 9 auth key/request/reply encryption
+	netcrypt:	fn(key: array of byte, chal: string): string;
+	passtokey:	fn(pw: string): array of byte;
+	des56to64:	fn(a: array of byte): array of byte;
+	encrypt:	fn(key: array of byte, data: array of byte, n: int);
+	decrypt:	fn(key: array of byte, data: array of byte, n: int);
+
+	# dial auth server
+#	authdial(netroot: string, authdom: string): ref Sys->FD;
+
+	# exchange messages with auth server
+	_asgetticket:	fn(fd: ref Sys->FD, tr: ref Ticketreq, key: array of byte): (ref Ticket, array of byte);
+	_asrdresp:	fn(fd: ref Sys->FD, n: int): array of byte;
+
+	init:	fn();
+};
+
--- /dev/null
+++ b/module/bench.m
@@ -1,0 +1,17 @@
+# Benchmarking
+
+Bench: module
+{
+	PATH:	con "$Bench";
+
+	FD: adt
+	{
+		fd:	int;
+	};
+
+	microsec:		fn(): big;
+	reset:		fn();
+	read:			fn(fd: ref FD, buf: array of byte, n: int): int;
+	disablegc:		fn();
+	enablegc:		fn();
+};
\ No newline at end of file
--- /dev/null
+++ b/module/bloomfilter.m
@@ -1,0 +1,7 @@
+Bloomfilter: module {
+	PATH:	con "/dis/lib/bloomfilter.dis";
+	init:		fn();
+	# logm is log base 2 of the number of bits in the bloom filter.
+	# k is number of independent hashes of d that are entered into the filter.
+	filter:	fn(d: array of byte, logm, k: int): Sets->Set;
+};
--- /dev/null
+++ b/module/brutus.m
@@ -1,0 +1,22 @@
+Brutus: module
+{
+	# Font tags are given as (font*NSIZE + size)
+	Size6, Size8, Size10, Size12, Size16, NSIZE: con iota;
+	Roman, Italic, Bold, Type, NFONT: con iota;
+	NFONTTAG: con NFONT*NSIZE;
+	Example, Caption, List, Listelem, Label, Labelref, Exercise, Heading,
+		Nofill, Author, Title, Index, Indextopic, NTAG: con NFONTTAG + iota;
+	DefFont: con Roman;
+	DefSize: con Size10;
+	TitleFont: con Bold;
+	TitleSize: con Size16;
+	HeadingFont: con Bold;
+	HeadingSize: con Size12;
+
+	fontname: array of string;
+	sizename: array of string;
+	tagname: array of string;
+	tagconfig: array of string;
+
+	init:	fn(ctxt: ref Draw->Context, args: list of string);
+};
--- /dev/null
+++ b/module/brutusext.m
@@ -1,0 +1,24 @@
+Brutusext: module
+{
+	# More tags, needed by Cook
+	SGML, Text, Par, Extension, Float, Special: con Brutus->NTAG + iota;
+
+	# Output formats
+	FLatex, FLatexProc, FLatexBook, FLatexPart, FLatexSlides, FLatexPaper, FHtml: con iota;
+
+	# Cook element
+	Celem: adt
+	{
+		tag: int;
+		s: string;
+		contents: cyclic ref Celem;
+		parent: cyclic ref Celem;
+		next: cyclic ref Celem;
+		prev: cyclic ref Celem;
+	};
+
+
+	init:	fn(sys: Sys, draw: Draw, bufio: Bufio, tk: Tk, tkclient: Tkclient);
+	create:	fn(parent: string, t: ref Tk->Toplevel, name, args: string): string;
+	cook:	fn(parent: string, fmt: int, args: string) : (ref Celem, string);
+};
--- /dev/null
+++ b/module/bufio.m
@@ -1,0 +1,70 @@
+Bufio: module
+{
+	PATH:		con "/dis/lib/bufio.dis";
+
+	SEEKSTART:	con Sys->SEEKSTART;
+	SEEKRELA:	con Sys->SEEKRELA;
+	SEEKEND:	con Sys->SEEKEND;
+
+	OREAD:		con Sys->OREAD;
+	OWRITE:		con Sys->OWRITE;
+	ORDWR:		con Sys->ORDWR;
+
+	EOF:		con -1;
+	ERROR:		con -2;
+
+	Iobuf: adt {
+		seek:		fn(b: self ref Iobuf, n: big, where: int): big;
+		offset:		fn(b: self ref Iobuf): big;
+
+		read:		fn(b: self ref Iobuf, a: array of byte, n: int): int;
+		write:		fn(b: self ref Iobuf, a: array of byte, n: int): int;
+
+		getb:		fn(b: self ref Iobuf): int;
+		getc:		fn(b: self ref Iobuf): int;
+		gets:		fn(b: self ref Iobuf, sep: int): string;
+		gett:		fn(b: self ref Iobuf, sep: string): string;
+
+		ungetb:		fn(b: self ref Iobuf): int;
+		ungetc:		fn(b: self ref Iobuf): int;
+
+		putb:		fn(b: self ref Iobuf, b: byte): int;
+		putc:		fn(b: self ref Iobuf, c: int): int;
+		puts:		fn(b: self ref Iobuf, s: string): int;
+
+		flush:		fn(b: self ref Iobuf): int;
+		close:		fn(b: self ref Iobuf);
+
+		setfill:	fn(b: self ref Iobuf, f: BufioFill);
+
+		# Internal variables
+		fd:		ref Sys->FD;	# the file
+		buffer:		array of byte;	# the buffer
+		index:		int;		# read/write pointer in buffer
+		size:		int;		# characters remaining/written
+		dirty:		int;		# needs flushing
+		bufpos:		big;		# position in file of buf[0]
+		filpos:		big;		# current file pointer
+		lastop:		int;		# OREAD or OWRITE
+		mode:		int;		# mode of open
+	};
+
+	open:		fn(name: string, mode: int): ref Iobuf;
+	create:		fn(name: string, mode, perm: int): ref Iobuf;
+	fopen:		fn(fd: ref Sys->FD, mode: int): ref Iobuf;
+	sopen:		fn(input: string): ref Iobuf;
+	aopen:		fn(input: array of byte): ref Iobuf;
+};
+
+BufioFill: module
+{
+	fill:	fn(b: ref Bufio->Iobuf): int;
+};
+
+ChanFill: module
+{
+	PATH:	con "/dis/lib/chanfill.dis";
+
+	init:	fn(data: array of byte, fid: int, wc: Sys->Rwrite, r: ref Sys->FileIO, b: Bufio): ref Bufio->Iobuf;
+	fill:	fn(b: ref Bufio->Iobuf): int;
+};
--- /dev/null
+++ b/module/bundle.m
@@ -1,0 +1,9 @@
+Bundle: module {
+	PATH: con "/dis/fs/bundle.dis";
+
+	types: fn(): string;
+	init: fn();
+	run: fn(nil: ref Draw->Context, report: ref Fslib->Report,
+			nil: list of Fslib->Option, args: list of ref Fslib->Value): ref Fslib->Value;
+	bundle:	fn(r: ref Fslib->Report, iob: ref Bufio->Iobuf, c: Fslib->Fschan): chan of int;
+};
--- /dev/null
+++ b/module/cci.m
@@ -1,0 +1,9 @@
+CCI: module
+{
+	PATH:	con "/dis/lib/cci.dis";
+
+	# Common Client Interface, for external control of Charon
+
+	init: fn(smod: String, hctl: chan of string);
+	view: fn(url, ctype: string, data: array of byte);
+};
--- /dev/null
+++ b/module/cfg.m
@@ -1,0 +1,25 @@
+Cfg : module {
+	PATH : con "/dis/lib/cfg.dis";
+
+	Attr : adt {
+		name : string;
+		value : string;
+	};
+
+	Tuple : adt {
+		lnum : int;
+		attrs : list of Attr;
+		lookup: fn (t : self ref Tuple, name : string) : string;
+	};
+
+	Record : adt {
+		tuples : list of ref Tuple;
+		lookup : fn (r : self ref Record, name : string) : (string, ref Tuple);
+	};
+
+	init : fn (path : string) : string;
+	reset:	fn();
+	lookup : fn (name : string) : list of (string, ref Record);
+	getkeys : fn () : list of string;
+	parseline:	fn(s: string, lno: int): (ref Tuple, string);
+};
--- /dev/null
+++ b/module/cfgfile.m
@@ -1,0 +1,23 @@
+#
+# simple adt that operates on whitespace separated config files
+# such as /services/webget/config
+#
+CfgFile: module
+{
+	PATH: con	"/dis/lib/cfgfile.dis";
+	ConfigFile: adt
+	{
+		getcfg:	fn(me: self ref ConfigFile,field:string):list of string;
+		setcfg:	fn(me: self ref ConfigFile,field:string,val:string);
+		delete:	fn(me: self ref ConfigFile,field:string);
+		flush:	fn(me: self ref ConfigFile): string;
+
+		 # ----- private ------
+		lines:	list of string;
+		file:	string;
+		readonly:	int;
+	};
+
+	init:	fn(file:string):ref ConfigFile;
+	verify:	fn(defaultpath: string, path: string) :ref Sys->FD;
+};
--- /dev/null
+++ b/module/complete.m
@@ -1,0 +1,15 @@
+Complete: module
+{
+	PATH:	con "/dis/lib/complete.dis";
+
+	Completion: adt {
+		advance: int;	# whether forward progress has been made
+		complete: int;	# whether the completion now represents a file or directory
+		str:	string;	# string to advance, suffixed " " (file) or "/" (directory)
+		nmatch: int;	# number of files that matched
+		filename:	array of string;	# their names
+	};
+
+	init: fn();
+	complete: fn(dir, s: string): (ref Completion, string);
+};
--- /dev/null
+++ b/module/convcs.m
@@ -1,0 +1,29 @@
+#
+# Copyright © 2001 Vita Nuova Limited
+#
+Btos: module {
+	init: fn(arg: string): string;
+	btos: fn(s: Convcs->State, b: array of byte, nchars: int) : (Convcs->State, string, int);
+};
+
+Stob: module {
+	init: fn(arg: string): string;
+	stob: fn(s: Convcs->State, str: string): (Convcs->State, array of byte);
+};
+
+Convcs: module {
+	PATH: con "/dis/lib/convcs/convcs.dis";
+	CHARSETS: con "/lib/charsets";
+
+	BTOS, STOB: con 1 << iota;		# enumcs() mode values
+	BOTH: con BTOS | STOB;
+
+	State: type string;
+	Startstate: con "";
+
+	init: fn(csfile: string): string;
+	getbtos: fn(cs: string): (Btos, string);
+	getstob: fn(cs: string): (Stob, string);
+	enumcs: fn(): list of (string, string, int);		# (cs, description, mode)
+	aliases: fn(cs : string): (string, list of string);
+};
--- /dev/null
+++ b/module/crc.m
@@ -1,0 +1,23 @@
+Crc: module
+{
+	PATH: con "/dis/lib/crc.dis";
+
+	CRCstate: adt {
+		crc: int;
+		crctab: array of int;
+		reg: int;
+	};
+
+	# setup crc table with given polynomial (if 0 default polynomial used) 
+	# (the polynomial has an implicit top bit set)
+	# reg is the initial value of the CRC register 
+	# (usually 0 but 16rfffffffrf in the CRC32 algorithm for example)
+	init: fn(poly: int, reg: int): ref CRCstate;
+
+	# calculate crc of first nb bytes in given array of bytes and return its value
+	# may be called repeatedly to calculate crc of a series of arrays of bytes
+	crc :	fn(state: ref CRCstate, buf: array of byte, nb: int): int;
+
+	# reset crc state to its initial value
+	reset: fn(state: ref CRCstate);
+};
--- /dev/null
+++ b/module/crypt.m
@@ -1,0 +1,191 @@
+#
+#  basic cryptography routines implemented in C
+#
+Crypt: module
+{
+	PATH:	con	"$Crypt";
+
+	# state held while creating digests
+	DigestState: adt
+	{
+		x:	int;		# dummy for C compiler for runt.h
+		# all the state is hidden
+
+		copy:	fn(d: self ref DigestState): ref DigestState;
+	};
+
+	# expanded AES key + state for chaining
+	AESstate: adt
+	{
+		x:	int;		# dummy for C compiler for runt.h
+		# all the state is hidden
+	};
+
+	# expanded DES key + state for chaining
+	DESstate: adt
+	{
+		x:	int;		# dummy for C compiler for runt.h
+		# all the state is hidden
+	};
+
+	# expanded IDEA key + state for chaining
+	IDEAstate: adt
+	{
+		x:	int;		# dummy for C compiler for runt.h
+		# all the state is hidden
+	};
+
+	# expanded RC4 key + encryption state
+	RC4state: adt
+	{
+		x:	int;		# dummy for C compiler for runt.h
+		# all the state is hidden
+	};
+
+	# expanded Blowfish key + state for chaining
+	BFstate: adt
+	{
+		x:	int;		# dummy for C compiler for runt.h
+		# all the state is hidden
+	};
+
+	# digests
+	sha1: fn(buf: array of byte, n: int, digest: array of byte, state: ref DigestState):
+		ref DigestState;
+	sha224: fn(buf: array of byte, n: int, digest: array of byte, state: ref DigestState):
+		ref DigestState;
+	sha256: fn(buf: array of byte, n: int, digest: array of byte, state: ref DigestState):
+		ref DigestState;
+	sha384: fn(buf: array of byte, n: int, digest: array of byte, state: ref DigestState):
+		ref DigestState;
+	sha512: fn(buf: array of byte, n: int, digest: array of byte, state: ref DigestState):
+		ref DigestState;
+	md4: fn(buf: array of byte, n: int, digest: array of byte, state: ref DigestState):
+		ref DigestState;
+	md5: fn(buf: array of byte, n: int, digest: array of byte, state: ref DigestState):
+		ref DigestState;
+
+	hmac_sha1: fn(data: array of byte, n: int, key: array of byte, digest: array of byte, state: ref DigestState):
+		ref DigestState;
+	hmac_md5: fn(data: array of byte, n: int, key: array of byte, digest: array of byte, state: ref DigestState):
+		ref DigestState;
+
+	SHA1dlen: con 20;
+	SHA224dlen:	con 28;
+	SHA256dlen: con 32;
+	SHA384dlen: con 48;
+	SHA512dlen: con 64;
+	MD5dlen:	con 16;
+	MD4dlen:	con 16;
+
+	# encryption interfaces
+	Encrypt:	con 0;
+	Decrypt:	con 1;
+
+	AESbsize:	con 16;
+
+	aessetup: fn(key: array of byte, ivec: array of byte): ref AESstate;
+	aescbc: fn(state: ref AESstate, buf: array of byte, n: int, direction: int);
+
+	DESbsize: con 8;
+
+	dessetup: fn(key: array of byte, ivec: array of byte): ref DESstate;
+	desecb: fn(state: ref DESstate, buf: array of byte, n: int, direction: int);
+	descbc: fn(state: ref DESstate, buf: array of byte, n: int, direction: int);
+
+	IDEAbsize: con 8;
+
+	ideasetup: fn(key: array of byte, ivec: array of byte): ref IDEAstate;
+	ideaecb: fn(state: ref IDEAstate, buf: array of byte, n: int, direction: int);
+	ideacbc: fn(state: ref IDEAstate, buf: array of byte, n: int, direction: int);
+
+	BFbsize: con 8;
+
+	blowfishsetup: fn(key: array of byte, ivec: array of byte): ref BFstate;
+#	blowfishecb: fn(state: ref BFstate, buf: array of byte, n: int, direction: int);
+	blowfishcbc: fn(state: ref BFstate, buf: array of byte, n: int, direction: int);
+
+	rc4setup:	fn(seed: array of byte): ref RC4state;
+	rc4:	fn(state: ref RC4state, buf: array of byte, n: int);
+	rc4skip:	fn(state: ref RC4state, n: int);
+	rc4back:	fn(state: ref RC4state, n: int);
+
+	# create an alpha and p for diffie helman exchanges
+	dhparams: fn(nbits: int): (ref IPints->IPint, ref IPints->IPint);
+
+	# public key
+	PK: adt
+	{
+		pick {
+		RSA =>
+			n:	ref IPints->IPint;		# modulus
+			ek:	ref IPints->IPint;		# exp (encryption key)
+		Elgamal =>
+			p:	ref IPints->IPint;		# modulus
+			alpha: ref IPints->IPint;		# generator
+			key:	ref IPints->IPint;		# encryption key (alpha**secret mod p)
+		DSA =>
+			p:	ref IPints->IPint;	# modulus
+			q:	ref IPints->IPint;	# group order, q divides p-1
+			alpha: ref IPints->IPint;	# group generator
+			key:	ref IPints->IPint;	# encryption key (alpha**secret mod p)
+		}
+	};
+	
+	# secret key (private/public key pair)
+	SK: adt
+	{
+		pick {
+		RSA =>
+			pk:	ref PK.RSA;
+			dk:	ref IPints->IPint;		# exp (decryption key)
+			p:	ref IPints->IPint;		# q in pkcs
+			q:	ref IPints->IPint;		# p in pkcs
+			# precomputed crt values
+			kp:	ref IPints->IPint;		# k mod p-1
+			kq:	ref IPints->IPint;		# k mod q-1
+			c2:	ref IPints->IPint;		# for converting residues to number
+		Elgamal =>
+			pk:	ref PK.Elgamal;
+			secret:	ref IPints->IPint;	# decryption key
+		DSA =>
+			pk:	ref PK.DSA;
+			secret:	ref IPints->IPint;	# decryption key
+		}
+	};
+
+	# public key signature
+	PKsig: adt
+	{
+		# could just have list or array of ref IPints->IPint
+		pick {
+		RSA =>
+			n:	ref IPints->IPint;
+		Elgamal =>
+			r:	ref IPints->IPint;
+			s:	ref IPints->IPint;
+		DSA =>
+			r:	ref IPints->IPint;
+			s:	ref IPints->IPint;
+		}
+	};
+
+	# RSA keys
+	rsagen:	fn(nlen: int, elen: int, nrep: int): ref SK.RSA;
+	rsafill:	fn(n: ref IPints->IPint, ek: ref IPints->IPint, dk: ref IPints->IPint, p: ref IPints->IPint, q: ref IPints->IPint): ref SK.RSA;
+	rsadecrypt:	fn(k: ref SK.RSA, m: ref IPints->IPint): ref IPints->IPint;
+	rsaencrypt:	fn(k: ref PK.RSA, m: ref IPints->IPint): ref IPints->IPint;
+
+	# Elgamal
+	eggen:	fn(nlen: int, nrep: int): ref SK.Elgamal;
+
+	# DSA
+	dsagen:	fn(oldpk: ref PK.DSA): ref SK.DSA;
+
+	# generic signature functions
+	genSK: 	fn(algname: string, length: int): ref SK;
+	genSKfromPK: fn(pk: ref PK): ref SK;
+	sign:		fn(sk: ref SK, m: ref IPints->IPint): ref PKsig;
+	verify:	fn(pk: ref PK, sig: ref PKsig, m: ref IPints->IPint): int;
+	sktopk:	fn(sk: ref SK): ref PK;
+};
--- /dev/null
+++ b/module/css.m
@@ -1,0 +1,88 @@
+#
+# CSS parsing module
+#
+# CSS2.1 style sheets
+#
+# Copyright © 2001, 2005 Vita Nuova Holdings Limited.  All rights reserved.
+#
+CSS: module
+{
+	PATH:	con "/dis/lib/w3c/css.dis";
+
+	Stylesheet: adt {
+		charset:	string;
+		imports:	list of ref Import;
+		statements:	list of ref Statement;
+	};
+
+	Import: adt {
+		name:	string;
+		media:	list of string;
+	};
+
+	Statement: adt {
+		pick{
+		Media =>
+			media:	list of string;
+			rules:	list of ref Statement.Ruleset;
+		Page =>
+			pseudo:	string;
+			decls:	list of ref Decl;
+		Ruleset =>
+			selectors:	list of Selector;
+			decls:	list of ref Decl;
+		}
+	};
+
+	Decl: adt {
+		property:	string;
+		values:	list of ref Value;
+		important:	int;
+	};
+
+	Selector:	type list of (int, Simplesel);	# int is combinator from [ >+]
+	Simplesel: type list of ref Select;
+
+	Select: adt {
+		name:	string;
+		pick{
+		Element or ID or Any or Class or Pseudo =>
+		Attrib =>
+			op:	string;	# "=" "~=" "|="
+			value:	ref Value;	# optional Ident or String
+		Pseudofn =>
+			arg:	string;
+		}
+	};
+
+	Value: adt {
+		sep:	int;	# which operator of [ ,/] preceded this value in list
+		pick{
+		String or
+		Number or
+		Percentage or
+		Url or
+		Unicoderange =>
+			value:	string;
+		Hexcolour =>
+			value:	string;	# as given
+			rgb:	(int, int, int);	# converted
+		RGB =>
+			args:	cyclic list of ref Value;	# as given
+			rgb:	(int, int, int);		# converted
+		Ident =>
+			name:	string;
+		Unit =>
+			value:	string;	# int or float
+			units:	string;	# suffix giving units ("cm", "khz", and so on, always lower case)
+		Function =>
+			name:	string;
+			args:		cyclic list of ref Value;
+		}
+	};
+
+	init:	fn(diag: int);
+	parse:	fn(s: string): (ref Stylesheet, string);
+	parsedecl:	fn(s: string): (list of ref Decl, string);
+#	unescape:	fn(s: string): string;
+};
--- /dev/null
+++ b/module/csv.m
@@ -1,0 +1,8 @@
+CSV: module
+{
+	PATH:	con "/dis/lib/csv.dis";
+
+	init:	fn(b: Bufio);
+	getline:	fn(fd: ref Bufio->Iobuf): list of string;
+	quote:	fn(s: string): string;
+};
--- /dev/null
+++ b/module/cvsimages.m
@@ -1,0 +1,14 @@
+Cvsimages: module {
+	PATH: 	con "/dis/lib/cvsimages.dis";
+	init:		fn();
+	Cvsimage: adt {
+		new:		fn(win: ref Tk->Toplevel, w: string): ref Cvsimage;
+		fix:		fn(ci: self ref Cvsimage);
+		config:	fn(ci: self ref Cvsimage): int;
+	
+		image:	ref Draw->Image;
+		win:		ref Tk->Toplevel;
+		w:		string;
+		r:		Draw->Rect;
+	};
+};
--- /dev/null
+++ b/module/daytime.m
@@ -1,0 +1,53 @@
+Daytime: module
+{
+	PATH:	con "/dis/lib/daytime.dis";
+
+	Tm: adt {
+		sec:	int;	# seconds (0 to 59)
+		min:	int;	# minutes (0 to 59)
+		hour:	int;	# hours (0 to 23)
+		mday:	int;	# day of the month (1 to 31)
+		mon:	int;	# month (0 to 11)
+		year:	int;	# year-1900; 2000AD is 100
+		wday:	int;	# day of week (0 to 6, Sunday is 0)
+		yday:	int;	# day of year (0 to 365)
+		zone:	string;	# time zone name
+		tzoff:	int;	# time zone offset (seconds from GMT)
+	};
+
+	# now:
+	# return the time in seconds since the epoch
+	#
+	# time:
+	# return the current local time as string
+	#
+	# text:
+	# convert a time structure from local or gmt
+	# into a text string
+	#
+	# filet:
+	# return a string containing the file time
+	# prints mon day hh:mm if the file is < 6 months old
+	# 	 mon day year  if > 6 months old
+	#
+	# local:
+	# uses /locale/timezone to convert an epoch time in seconds into
+	# a local time structure
+	#
+	# gmt:
+	# return a time structure for GMT
+	#
+	# tm2epoch:
+	# convert a time structure to an epoch time in seconds
+	#
+	# string2tm:
+	# parse a string representing a date into a time structure
+	now:		fn(): int;
+	time:		fn(): string;
+	text:		fn(tm: ref Tm): string;
+	filet:		fn(now, file: int): string;
+	local:		fn(tim: int): ref Tm;
+	gmt:		fn(tim: int): ref Tm;
+	tm2epoch:	fn(tm: ref Tm): int;
+	string2tm:	fn(date: string): ref Tm;
+};
--- /dev/null
+++ b/module/db.m
@@ -1,0 +1,69 @@
+DB : module
+{
+	PATH : con "/dis/lib/db.dis";
+
+	# Open the connection to the DB server
+    	# returns (New handle, "") or (nil, "Error Message")
+	#
+	open:	fn(addr, username, password, dbname: string) :
+					(ref DB_Handle, list of string);
+	#
+	# Opens a connection to an Inferno on the database machine, with the
+	# specified level of security.
+	# 
+	connect : fn(addr, alg : string) : (ref Sys->FD, string);
+	#
+	# Mounts the file descriptor on dir, then opens the database.
+	#
+	dbopen: fn(fd : ref Sys->FD, username, password, dbname : string) :
+						(ref DB_Handle, list of string);
+
+	DB_Handle : adt
+	{
+		#
+		# Open another SQL stream for the same connection.
+		#
+		SQLOpen :	fn(oldh : self ref DB_Handle) : (int, ref DB_Handle);
+		SQLClose : fn(dbh : self ref DB_Handle) : int;
+
+	                   # Execute the SQL command
+		# returns (0, "") or (error code, "Message")
+		#
+	    	SQL:	fn(handle: self ref DB_Handle, command: string)
+							: (int, list of string);
+
+		# Check the number of columns of last select command
+		#
+		columns:	fn(handle: self ref DB_Handle): int;
+
+		# Fetch the next row of the selection results.
+		# returns current row number, or 0
+		#
+	  	nextRow:	fn(handle: self ref DB_Handle): int;	
+
+		# Read the data of column[i] of current row
+		#
+	  	read:	fn(handle: self ref DB_Handle, column: int)
+							: (int, array of byte);
+
+		# Write data to be used for parameter[i]
+		#
+		write:	fn(handle: self ref DB_Handle, column: int,
+						fieldval: array of byte) : int;
+
+		# Title of the column[i]
+		#
+	  	columnTitle: 	fn(handle: self ref DB_Handle, column: int)
+							: string;
+
+		#error message associated with last command
+		#
+		errmsg:		fn(handle: self ref DB_Handle): string;
+
+		datafd : ref Sys->FD;
+		sqlconn:int;
+		sqlstream : int;
+		lock : chan of int;
+
+	};
+};
--- /dev/null
+++ b/module/dbm.m
@@ -1,0 +1,35 @@
+Dbm: module
+{
+	PATH: con "/dis/lib/dbm.dis";
+
+	Datum:	type array of byte;
+
+	Dbf: adt {
+		create:	fn(file: string, perm: int): ref Dbf;
+		open:	fn(file: string, flags: int): ref Dbf;
+
+		fetch:	fn(db: self ref Dbf, key: Datum): Datum;
+		delete:	fn(db: self ref Dbf, key: Datum): int;
+		store:	fn(db: self ref Dbf, key: Datum, dat: Datum, replace: int): int;
+		firstkey:	fn(db: self ref Dbf): Datum;
+		nextkey:	fn(db: self ref Dbf, key: Datum): Datum;
+
+		flush:	fn(db: self ref Dbf);
+
+		isrdonly:	fn(db: self ref Dbf): int;
+
+		dirf:	ref Sys->FD;	# directory
+		pagf:	ref Sys->FD;	# page
+		flags:	int;
+		maxbno:	int;	# last `bno' in page file
+		bitno:	int;
+		hmask:	int;
+		blkno:	int;	# current page to read/write
+		pagbno:	int;	# current page in pagbuf
+		pagbuf:	array of byte;	# [PBLKSIZ]
+		dirbno:	int;	# current block in dirbuf
+		dirbuf:	array of byte;	# [DBLKSIZ]
+	};
+
+	init:	fn();
+};
--- /dev/null
+++ b/module/debug.m
@@ -1,0 +1,116 @@
+Debug: module
+{
+	PATH:	con "/dis/lib/debug.dis";
+
+	Terror, Tid, Tadt, Tadtpick, Tarray, Tbig, Tbyte, Tchan, Treal,
+	Tfn, Targ, Tlocal, Tglobal, Tint, Tlist, Tmodule, Tnil, Tnone,
+	Tref, Tstring, Ttuple, Tend, Targs, Tslice, Tpoly: con iota;
+
+	Pos: adt
+	{
+		file:		string;
+		line:		int;		# line number: origin 1
+		pos:		int;		# character within the line: origin 0
+	};
+	Src: adt
+	{
+		start:		Pos;		# range within source files
+		stop:		Pos;
+	};
+	Id: adt
+	{
+		src:		ref Src;
+		name:		string;
+		offset:		int;		# start of pc, offset in frame, etc
+		stoppc:		int;		# limit pc of function
+		t:		cyclic ref Type;
+	};
+	Type: adt
+	{
+		src:		ref Src;
+		kind:		int;
+		size:		int;
+		name:		string;			# for adts, modules
+		Of:		cyclic ref Type;	# for lists, arrays, etc.
+		ids:		cyclic array of ref Id;	# for adts, etc. locals for fns
+		tags:		cyclic array of ref Type;# for adts with pick tags
+
+		text:		fn(t: self ref Type, sym: ref Sym): string;
+		getkind:	fn(t: self ref Type, sym: ref Sym): int;
+	};
+	Sym: adt
+	{
+		path:		string;
+		name:		string;		# implements name
+		src:		array of ref Src;
+		srcstmt:	array of int;
+		adts:		array of ref Type;
+		fns:		array of ref Id;
+		vars:		array of ref Id;
+
+		srctopc:	fn(s: self ref Sym, src: ref Src): int;
+		pctosrc:	fn(s: self ref Sym, pc: int): ref Src;
+	};
+
+	Module: adt
+	{
+		path:	string;		# from whence loaded
+		code:	int;		# address of code start
+		data:	int;		# address of data
+		comp:	int;		# compiled to native assembler?
+		sym:	ref Sym;
+
+		addsym:	fn(m: self ref Module, sym: ref Sym);
+		stdsym:	fn(m: self ref Module);
+		dis:	fn(m: self ref Module): string;
+		sbl:	fn(m: self ref Module): string;
+	};
+
+	StepExp, StepStmt, StepOver, StepOut: con iota;
+	Prog: adt
+	{
+		id:	int;
+		heap:	ref Sys->FD;	# prog heap file
+		ctl:	ref Sys->FD;	# prog control file
+		dbgctl:	ref Sys->FD;	# debug file
+		stk:	ref Sys->FD;	# stack file
+
+		status:	fn(p: self ref Prog): (int, string, string, string);
+		stack:	fn(p: self ref Prog): (array of ref Exp, string);
+		step:	fn(p: self ref Prog, how: int): string;
+		cont:	fn(p: self ref Prog): string;
+		grab:	fn(p: self ref Prog): string;
+		start:	fn(p: self ref Prog): string;
+		stop:	fn(p: self ref Prog): string;
+		unstop:	fn(p: self ref Prog): string;
+		kill:	fn(p: self ref Prog): string;
+		event:	fn(p: self ref Prog): string;
+		setbpt:	fn(p: self ref Prog, dis: string, pc: int): string;
+		delbpt:	fn(p: self ref Prog, dis: string, pc: int): string;
+	};
+
+	Exp: adt
+	{
+		name:	string;
+		offset:	int;
+		pc:	int;
+		m:	ref Module;
+		p:	ref Prog;
+
+		# this is private
+		id:	ref Id;
+
+		expand:	fn(e: self ref Exp): array of ref Exp;
+		val:	fn(e: self ref Exp): (string, int);
+		typename:	fn(e: self ref Exp): string;
+		kind: fn(e: self ref Exp): int;
+		src:	fn(e: self ref Exp): ref Src;
+		findsym:fn(e: self ref Exp): string;
+		srcstr:	fn(e: self ref Exp): string;
+	};
+
+	init:		fn(): int;
+	sym:		fn(sbl: string): (ref Sym, string);
+	prog:	fn(pid: int): (ref Prog, string);
+	startprog:	fn(dis, dir: string, ctxt: ref Draw->Context, argv: list of string): (ref Prog, string);
+};
--- /dev/null
+++ b/module/devpointer.m
@@ -1,0 +1,21 @@
+Devpointer: module
+{
+	PATH:	con	"/dis/lib/devpointer.dis";
+
+	Size:		con 1+4*12;	# 'm' plus 4 12-byte decimal integers
+	# merge events that have the same button state.
+	Ptrqueue: adt {
+		last: ref Draw->Pointer;
+		h, t: list of ref Draw->Pointer;
+		put:			fn(q: self ref Ptrqueue, s: ref Draw->Pointer);
+		get:			fn(q: self ref Ptrqueue): ref Draw->Pointer;
+		peek:		fn(q: self ref Ptrqueue): ref Draw->Pointer;
+		nonempty:	fn(q: self ref Ptrqueue): int;
+	};
+
+	init:		fn();
+	reader:	fn(file: string, posn: chan of ref Draw->Pointer, pid: chan of (int, string));
+	bytes2ptr:	fn(b: array of byte): ref Draw->Pointer;
+	ptr2bytes:	fn(p: ref Draw->Pointer): array of byte;
+	srv:	fn(c: chan of ref Draw->Pointer, f: ref Sys->FileIO);
+};
--- /dev/null
+++ b/module/dhcp.m
@@ -1,0 +1,84 @@
+Dhcpclient: module
+{
+	PATH: con "/dis/lib/dhcpclient.dis";
+
+	Bootconf: adt {
+		ip:	string;
+		ipgw:	string;
+		ipmask:	string;
+		bootf:	string;
+		bootip:	string;
+		dhcpip:	string;
+		siaddr:	string;
+		serverid:	string;
+		sys:	string;
+		dom:	string;
+		lease:	int;
+		options:	array of array of byte;
+		vendor:	array of array of byte;
+
+		new:	fn(): ref Bootconf;
+		get:	fn(c: self ref Bootconf, n: int): array of byte;
+		getint:	fn(c: self ref Bootconf, n: int): int;
+		getip:	fn(c: self ref Bootconf, n: int): string;
+		getips:	fn(c: self ref Bootconf, n: int): list of string;
+		gets:	fn(c: self ref Bootconf, n: int): string;
+		put:	fn(c: self ref Bootconf, n: int, a: array of byte);
+		putint:	fn(c: self ref Bootconf, n: int, v: int);
+		putips:	fn(c: self ref Bootconf, n: int, ips: list of string);
+		puts:	fn(c: self ref Bootconf, n: int, s: string);
+	};
+
+	Lease: adt {
+		pid:	int;
+		configs:	chan of (ref Bootconf, string);
+
+		release:	fn(l: self ref Lease);
+	};
+
+	init:	fn();
+	tracing:	fn(debug: int);
+	bootp:	fn(net: string, ctlifc: ref Sys->FD, device: string, init: ref Bootconf): (ref Bootconf, string);
+	dhcp:	fn(net: string, ctlifc: ref Sys->FD, device: string, init: ref Bootconf, options: array of int): (ref Bootconf, ref Lease, string);
+
+	applycfg:	fn(net: string, ctlifc: ref Sys->FD, conf: ref Bootconf): string;
+	removecfg:	fn(net: string, ctlifc: ref Sys->FD, conf: ref Bootconf): string;
+
+	# bootp options used here
+	Opad: con 0;
+	Oend: con 255;
+	Omask: con 1;
+	Orouter: con 3;
+	Odnsserver: con 6;
+	Ocookieserver: con 8;
+	Ohostname: con 12;
+	Odomainname: con 15;
+	Ontpserver: con 42;
+	Ovendorinfo: con 43;
+	Onetbiosns: con 44;
+	Osmtpserver: con 69;
+	Opop3server: con 70;
+	Owwwserver: con 72;
+
+	# dhcp options
+	Oipaddr: con 50;
+	Olease: con 51;
+	Ooverload: con 52;
+	Otype: con 53;
+	Oserverid: con 54;
+	Oparams: con 55;
+	Omessage: con 56;
+	Omaxmsg: con 57;
+	Orenewaltime: con 58;
+	Orebindingtime: con 59;
+	Ovendorclass: con 60;
+	Oclientid: con 61;
+	Otftpserver: con 66;
+	Obootfile: con 67;
+
+	Ovendor:	con (1<<8);
+	OP9fs: con Ovendor|128;	# plan 9 file server
+	OP9auth:	con Ovendor|129;	# plan 9 auth server
+
+	Infinite:	con ~0;	# lease
+};
--- /dev/null
+++ b/module/dial.m
@@ -1,0 +1,34 @@
+Dial: module
+{
+	PATH: con "/dis/lib/dial.dis";
+
+	Connection: adt
+	{
+		dfd:	ref Sys->FD;
+		cfd:	ref Sys->FD;
+		dir:	string;
+	};
+
+	Conninfo: adt
+	{
+		dir:	string;
+		root:	string;
+		spec:	string;
+		lsys:	string;
+		lserv:	string;
+		rsys:	string;
+		rserv:	string;
+		laddr:	string;
+		raddr:	string;
+	};
+
+	announce:	fn(addr: string): ref Connection;
+	dial:	fn(addr, local: string): ref Connection;
+	listen:	fn(c: ref Connection): ref Connection;
+	accept:	fn(c: ref Connection): ref Sys->FD;
+	reject:	fn(c: ref Connection, why: string): int;
+#	parse:	fn(addr: string): (string, string, string);
+
+	netmkaddr:	fn(addr, net, svc: string): string;
+	netinfo:	fn(c: ref Connection): ref Conninfo;
+};
--- /dev/null
+++ b/module/dialog.m
@@ -1,0 +1,9 @@
+Dialog: module
+{
+	PATH:		con "/dis/lib/dialog.dis";
+	init:		fn(): string;
+
+	prompt:		fn(ctxt: ref Draw->Context, parent: ref Draw->Image, ico, title, msg: string,
+				dflt: int, labs: list of string): int;
+	getstring:	fn(ctxt: ref Draw->Context, parent: ref Draw->Image, msg: string): string;
+};
--- /dev/null
+++ b/module/dict.m
@@ -1,0 +1,14 @@
+Dictionary: module {
+	PATH: con "/dis/lib/dict.dis";
+
+	Dict: adt {
+		entries: list of (string, string);
+
+		add:	fn( d: self ref Dict, e: (string, string) );
+		delete:	fn( d: self ref Dict, k: string );
+		lookup:	fn( d: self ref Dict, k: string ) :string;
+		keys:	fn( d: self ref Dict ): list of string;
+	};
+
+
+};
\ No newline at end of file
--- /dev/null
+++ b/module/dis.m
@@ -1,0 +1,323 @@
+Dis: module
+{
+	PATH:	con "/dis/lib/dis.dis";
+
+	XMAGIC:		con	819248;
+	SMAGIC:		con	923426;
+	MUSTCOMPILE:	con	1<<0;
+	DONTCOMPILE:	con 	1<<1;
+	SHAREMP:	con 	1<<2;
+	DYNMOD:	con	1<<3;
+	HASLDT0:	con	1<<4;
+	HASEXCEPT:	con	1<<5;
+	HASLDT:	con	1<<6;
+
+	AMP:	con 16r00;	# Src/Dst op addressing
+	AFP:	con 16r01;
+	AIMM:	con 16r02;
+	AXXX:	con 16r03;
+	AIND:	con 16r04;
+	AMASK:	con 16r07;
+
+	ARM:	con 16rC0;	# Middle op addressing
+	AXNON:	con 16r00;
+	AXIMM:	con 16r40;
+	AXINF:	con 16r80;
+	AXINM:	con 16rC0;
+
+	DEFZ:	con 0;
+	DEFB:	con 1;		# Byte
+	DEFW:	con 2;		# Word
+	DEFS:	con 3;		# Utf-string
+	DEFF:	con 4;		# Real value
+	DEFA:	con 5;		# Array
+	DIND:	con 6;		# Set index
+	DAPOP:	con 7;		# Restore address register
+	DEFL:	con 8;		# BIG
+	DMAX:	con 1<<4;
+
+	Inst: adt
+	{
+		op:	int;
+		addr:	int;
+		mid:	int;
+		src:	int;
+		dst:	int;
+	};
+
+	Type: adt
+	{
+		size:	int;
+		np:	int;
+		map:	array of byte;
+	};
+
+	Data: adt
+	{
+		op:	int;		# encoded op
+		n:	int;		# number of elements
+		off:	int;		# byte offset in data space
+		pick {
+		Zero =>		# DEFZ
+		Bytes =>		# DEFB
+			bytes:	array of byte;
+		Words =>		# DEFW
+			words:	array of int;
+		String =>		# DEFS
+			str:	string;
+		Reals =>		# DEFF
+			reals:	array of real;
+		Array =>		# DEFA
+			typex:	int;
+			length:	int;
+		Aindex =>		# DIND
+			index:	int;
+		Arestore =>	# DAPOP
+		Bigs =>		# DEFL
+			bigs:		array of big;
+		}
+	};
+
+	Link: adt
+	{
+		pc:	int;
+		desc:	int;
+		sig:	int;
+		name:	string;
+	};
+
+	Import: adt
+	{
+		sig:	int;
+		name:	string;
+	};
+
+	Except: adt
+	{
+		s:	string;
+		pc:	int;
+	};
+
+	Handler: adt
+	{
+		pc1:	int;
+		pc2:	int;
+		eoff:	int;
+		ne:	int;
+		t:	ref Type;
+		etab:	array of ref Except;
+	};
+
+	Mod: adt
+	{
+		name:	string;
+		srcpath:	string;
+
+		magic:	int;
+		rt:	int;
+		ssize:	int;
+		isize:	int;
+		dsize:	int;
+		tsize:	int;
+		lsize:	int;
+		entry:	int;
+		entryt:	int;
+
+		inst:	array of ref Inst;
+		types:	array of ref Type;
+		data:	list of ref Data;
+		links:	array of ref Link;
+		imports:	array of array of ref Import;
+		handlers:	array of ref Handler;
+
+		sign:	array of byte;
+	};
+
+	INOP,
+	IALT,
+	INBALT,
+	IGOTO,
+	ICALL,
+	IFRAME,
+	ISPAWN,
+	IRUNT,
+	ILOAD,
+	IMCALL,
+	IMSPAWN,
+	IMFRAME,
+	IRET,
+	IJMP,
+	ICASE,
+	IEXIT,
+	INEW,
+	INEWA,
+	INEWCB,
+	INEWCW,
+	INEWCF,
+	INEWCP,
+	INEWCM,
+	INEWCMP,
+	ISEND,
+	IRECV,
+	ICONSB,
+	ICONSW,
+	ICONSP,
+	ICONSF,
+	ICONSM,
+	ICONSMP,
+	IHEADB,
+	IHEADW,
+	IHEADP,
+	IHEADF,
+	IHEADM,
+	IHEADMP,
+	ITAIL,
+	ILEA,
+	IINDX,
+	IMOVP,
+	IMOVM,
+	IMOVMP,
+	IMOVB,
+	IMOVW,
+	IMOVF,
+	ICVTBW,
+	ICVTWB,
+	ICVTFW,
+	ICVTWF,
+	ICVTCA,
+	ICVTAC,
+	ICVTWC,
+	ICVTCW,
+	ICVTFC,
+	ICVTCF,
+	IADDB,
+	IADDW,
+	IADDF,
+	ISUBB,
+	ISUBW,
+	ISUBF,
+	IMULB,
+	IMULW,
+	IMULF,
+	IDIVB,
+	IDIVW,
+	IDIVF,
+	IMODW,
+	IMODB,
+	IANDB,
+	IANDW,
+	IORB,
+	IORW,
+	IXORB,
+	IXORW,
+	ISHLB,
+	ISHLW,
+	ISHRB,
+	ISHRW,
+	IINSC,
+	IINDC,
+	IADDC,
+	ILENC,
+	ILENA,
+	ILENL,
+	IBEQB,
+	IBNEB,
+	IBLTB,
+	IBLEB,
+	IBGTB,
+	IBGEB,
+	IBEQW,
+	IBNEW,
+	IBLTW,
+	IBLEW,
+	IBGTW,
+	IBGEW,
+	IBEQF,
+	IBNEF,
+	IBLTF,
+	IBLEF,
+	IBGTF,
+	IBGEF,
+	IBEQC,
+	IBNEC,
+	IBLTC,
+	IBLEC,
+	IBGTC,
+	IBGEC,
+	ISLICEA,
+	ISLICELA,
+	ISLICEC,
+	IINDW,
+	IINDF,
+	IINDB,
+	INEGF,
+	IMOVL,
+	IADDL,
+	ISUBL,
+	IDIVL,
+	IMODL,
+	IMULL,
+	IANDL,
+	IORL,
+	IXORL,
+	ISHLL,
+	ISHRL,
+	IBNEL,
+	IBLTL,
+	IBLEL,
+	IBGTL,
+	IBGEL,
+	IBEQL,
+	ICVTLF,
+	ICVTFL,
+	ICVTLW,
+	ICVTWL,
+	ICVTLC,
+	ICVTCL,
+	IHEADL,
+	ICONSL,
+	INEWCL,
+	ICASEC,
+	IINDL,
+	IMOVPC,
+	ITCMP,
+	IMNEWZ,
+	ICVTRF,
+	ICVTFR,
+	ICVTWS,
+	ICVTSW,
+	ILSRW,
+	ILSRL,
+	IECLR,
+	INEWZ,
+	INEWAZ,
+	IRAISE,
+	ICASEL,
+	IMULX,
+	IDIVX,
+	ICVTXX,
+	IMULX0,
+	IDIVX0,
+	ICVTXX0,
+	IMULX1,
+	IDIVX1,
+	ICVTXX1,
+	ICVTFX,
+	ICVTXF,
+	IEXPW,
+	IEXPL,
+	IEXPF,
+	ISELF,
+	# add new instructions here
+	MAXDIS: con iota;
+
+	init:		fn();
+	loadobj:	fn(file: string): (ref Mod, string);
+	op2s:	fn(op: int): string;
+	inst2s:	fn(ins: ref Inst): string;
+	src:		fn(file: string): string;
+};
+#
+# derived by Vita Nuova Limited 1998 from /appl/wm/rt.b and /include/isa.h, both
+# Copyright © 1996-1999 Lucent Technologies Inc.  All rights reserved.
+#
--- /dev/null
+++ b/module/diskblocks.m
@@ -1,0 +1,26 @@
+Diskblocks: module {
+	PATH: con "/dis/lib/diskblocks.dis";
+
+	Block: adt {
+		addr:	big;	# address on file
+		n:	int;	# size in bytes
+	};
+
+	Disk: adt {
+		fd: ref Sys->FD;
+		addr: big;		# length of temp file
+		free: array of list of ref Block;
+		maxblock:	int;
+		gran:	int;
+		lock:	chan of int;
+
+		init:	fn(fd: ref Sys->FD, gran: int, maxblock: int): ref Disk;
+		new:	fn(d: self ref Disk, n: int): ref Block;
+		release:	fn(d: self ref Disk, b: ref Block);
+		read:	fn(d: self ref Disk, b: ref Block, a: array of byte, n: int): int;
+		write:	fn(d: self ref Disk, b: ref Block, a: array of byte, n: int): ref Block;
+	};
+
+	init: fn();
+	tempfile: fn(): ref Sys->FD;
+};
--- /dev/null
+++ b/module/disks.m
@@ -1,0 +1,54 @@
+#
+# adapted from /sys/include/disk.h on Plan 9: subject to the Lucent Public License 1.02
+#
+Disks: module
+{
+	PATH: con "/dis/lib/disks.dis";
+
+	# disk partition interface
+	Disk: adt {
+		prefix:	string;
+		part:	string;
+		fd:	ref Sys->FD;
+		wfd:	ref Sys->FD;
+		ctlfd:	ref Sys->FD;
+		rdonly:	int;
+		dtype:	string;	# "file", "sd" or "floppy"
+
+		secs:	big;
+		secsize:	int;
+		size:	big;
+		offset:	big;	# within larger disk, perhaps
+		width:	int;	# of disk size in bytes as decimal string
+		c:	int;	# geometry: cyl, head, sectors
+		h:	int;
+		s:	int;
+		chssrc:	string; # "part", "disk" or "guess"
+
+		open:	fn(f: string, mode: int, noctl: int): ref Disk;
+		readn:	fn(d: self ref Disk, buf: array of byte, n: int): int;
+	};
+
+	init:	fn();
+
+	# PC partition grot
+	PCpart: adt {
+		active:	int;	# Active or 0
+		ptype:	int;
+		base:	big;	# base block address: 0 or first extended partition in chain
+		offset:	big;	# block offset from base to partition
+		size:		big;	# in sectors
+
+		extract:	fn(a: array of byte, d: ref Disk): PCpart;
+		bytes:	fn(p: self PCpart, d: ref Disk): array of byte;
+	};
+	Toffset:	con 446;	# offset of partition table in sector
+	TentrySize:	con 2+2*3+4+4;	# partition table entry size
+	NTentry:	con 4;	# number of table entries
+	Magic0:	con 16r55;
+	Magic1:	con 16rAA;
+	Active:	con 16r80;	# partition is active
+	Type9: 	con 16r39;	# partition type used by Plan 9 and Inferno
+
+	chstext:	fn(p: array of byte): string;
+};
--- /dev/null
+++ b/module/dividers.m
@@ -1,0 +1,33 @@
+Dividers: module {
+	init: fn();
+	Divider: adt {
+		new: fn(win: ref Tk->Toplevel, w: string, wl: list of string, dir: int): (ref Divider, chan of string);
+		event: fn(d: self ref Divider, e: string);
+
+		# private from here.
+		win: ref Tk->Toplevel;
+		w: string;
+		state: ref DState;
+		
+		dir: int;			# NS or EW
+		widgets: array of ref DWidget;
+		canvsize: Draw->Point;
+	};
+
+	EW, NS: con iota;
+	PATH: con "/dis/lib/dividers.dis";
+
+	# private from here
+	DWidget: adt {
+		w: string;
+		r: Draw->Rect;
+		size: Draw->Point;
+	};
+	
+	DState: adt {
+		dragdiv: int;
+		dy: int;
+		maxy, miny: int;
+	};
+	
+};
--- /dev/null
+++ b/module/draw.m
@@ -1,0 +1,298 @@
+Draw: module
+{
+	PATH:	con	"$Draw";
+
+	# predefined colors; pass to Display.color
+	Opaque:	con int 16rFFFFFFFF;
+	Transparent:	con int 16r00000000;		# only useful for Display.newimage
+	Black:	con int 16r000000FF;
+	White:	con int 16rFFFFFFFF;
+	Red:	con int 16rFF0000FF;
+	Green:	con int 16r00FF00FF;
+	Blue:	con int 16r0000FFFF;
+	Cyan:	con int 16r00FFFFFF;
+	Magenta:	con int 16rFF00FFFF;
+	Yellow:	con int 16rFFFF00FF;
+	Grey:	con int 16rEEEEEEFF;
+	Paleyellow:	con int 16rFFFFAAFF;
+	Darkyellow:	con int 16rEEEE9EFF;
+	Darkgreen:	con int 16r448844FF;
+	Palegreen:	con int 16rAAFFAAFF;
+	Medgreen:	con int 16r88CC88FF;
+	Darkblue:	con int 16r000055FF;
+	Palebluegreen:	con int 16rAAFFFFFF;
+	Paleblue:	con int 16r0000BBFF;
+	Bluegreen:	con int 16r008888FF;
+	Greygreen:	con int 16r55AAAAFF;
+	Palegreygreen:	con int 16r9EEEEEFF;
+	Yellowgreen:	con int 16r99994CFF;
+	Medblue:	con int 16r000099FF;
+	Greyblue:	con int 16r005DBBFF;
+	Palegreyblue:	con int 16r4993DDFF;
+	Purpleblue:	con int 16r8888CCFF;
+
+	Notacolor:	con int 16rFFFFFF00;
+	Nofill:		con Notacolor;
+
+	# end styles for line
+	Endsquare:	con 0;
+	Enddisc:	con 1;
+	Endarrow:	con 2;
+
+	# flush control
+	Flushoff:	con 0;
+	Flushon:	con 1;
+	Flushnow:	con 2;
+
+	# image backing store
+	Refbackup:	con 0;
+	Refnone:	con 1;
+
+	# compositing operators
+	SinD:	con 1<<3;
+	DinS:	con 1<<2;
+	SoutD:	con 1<<1;
+	DoutS:	con 1<<0;
+
+	S:		con SinD|SoutD;
+	SoverD:	con SinD|SoutD|DoutS;
+	SatopD:	con SinD|DoutS;
+	SxorD:	con SoutD|DoutS;
+
+	D:		con DinS|DoutS;
+	DoverS:	con DinS|DoutS|SoutD;
+	DatopS:	con DinS|SoutD;
+	DxorS:	con DoutS|SoutD;
+
+	Clear:	con 0;
+
+	# Image channels descriptor
+	Chans: adt
+	{
+		desc:	int;		# descriptor packed into an int
+
+		# interpret standard channel string
+		mk:	fn(s: string): Chans;
+		# standard printable form
+		text:	fn(c: self Chans): string;
+		# equality
+		eq:	fn(c: self Chans, d: Chans): int;
+		# bits per pixel
+		depth:	fn(c: self Chans): int;
+	};
+
+	CRed, CGreen, CBlue, CGrey, CAlpha, CMap, CIgnore: con iota;
+
+	GREY1: con Chans((CGrey<<4) | 1);
+	GREY2: con Chans((CGrey<<4) | 2);
+	GREY4: con Chans((CGrey<<4) | 4);
+	GREY8: con Chans((CGrey<<4) | 8);
+	CMAP8: con Chans((CMap<<4) | 8);
+	RGB15: con Chans(((CIgnore<<4)|1)<<24 | ((CRed<<4)|5)<<16 | ((CGreen<<4)|5)<<8 | ((CBlue<<4)|5));
+	RGB16: con Chans(((CRed<<4)|5)<<16 | ((CGreen<<4)|6)<<8 | ((CBlue<<4)|5));
+	RGB24: con Chans(((CRed<<4)|8)<<16 | ((CGreen<<4)|8)<<8 | ((CBlue<<4)|8));
+	RGBA32: con Chans((((CRed<<4)|8)<<16 | ((CGreen<<4)|8)<<8 | ((CBlue<<4)|8))<<8 | ((CAlpha<<4)|8));
+	ARGB32: con Chans(((CAlpha<<4)|8)<<24 | ((CRed<<4)|8)<<16 | ((CGreen<<4)|8)<<8 | ((CBlue<<4)|8));	# stupid VGAs
+	XRGB32: con Chans(((CIgnore<<4)|8)<<24 | ((CRed<<4)|8)<<16 | ((CGreen<<4)|8)<<8 | ((CBlue<<4)|8));	# stupid VGAs
+
+	# Coordinate of a pixel on display
+	Point: adt
+	{
+		x:	int;
+		y:	int;
+
+		# arithmetic
+		add:	fn(p: self Point, q: Point): Point;
+		sub:	fn(p: self Point, q: Point): Point;
+		mul:	fn(p: self Point, i: int): Point;
+		div:	fn(p: self Point, i: int): Point;
+		# equality
+		eq:	fn(p: self Point, q: Point): int;
+		# inside rectangle
+		in:	fn(p: self Point, r: Rect): int;
+	};
+
+	# Rectangle of pixels on the display; min <= max
+	Rect: adt
+	{
+		min:	Point;	# upper left corner
+		max:	Point;	# lower right corner
+
+		# make sure min <= max
+		canon:		fn(r: self Rect): Rect;
+		# extent
+		dx:		fn(r: self Rect): int;
+		dy:		fn(r: self Rect): int;
+		size:		fn(r: self Rect): Point;
+		# equality
+		eq:		fn(r: self Rect, s: Rect): int;
+		# intersection and clipping
+		Xrect:		fn(r: self Rect, s: Rect): int;
+		inrect:		fn(r: self Rect, s: Rect): int;
+		clip:		fn(r: self Rect, s: Rect): (Rect, int);
+		contains:	fn(r: self Rect, p: Point): int;
+		combine:	fn(r: self Rect, s: Rect): Rect;
+		# arithmetic
+		addpt:		fn(r: self Rect, p: Point): Rect;
+		subpt:		fn(r: self Rect, p: Point): Rect;
+		inset:		fn(r: self Rect, n: int): Rect;
+	};
+
+	# a picture; if made by Screen.newwindow, a window.  always attached to a Display
+	Image: adt
+	{
+		# these data are local copies, but repl and clipr
+		# are monitored by the runtime and may be modified as desired.
+		r:	Rect;		# rectangle in data area, local coords
+		clipr:	Rect;		# clipping region
+		depth:	int;		# number of bits per pixel
+		chans:	Chans;
+		repl:	int;		# whether data area replicates to tile the plane
+		display:	ref Display; # where Image resides
+		screen:		ref Screen;	 # nil if not window
+		iname:	string;
+
+		# graphics operators
+		drawop:		fn(dst: self ref Image, r: Rect, src: ref Image, matte: ref Image, p: Point, op: int);
+		draw:		fn(dst: self ref Image, r: Rect, src: ref Image, matte: ref Image, p: Point);
+		gendrawop:		fn(dst: self ref Image, r: Rect, src: ref Image, p0: Point, matte: ref Image, p1: Point, op: int);
+		gendraw:		fn(dst: self ref Image, r: Rect, src: ref Image, p0: Point, matte: ref Image, p1: Point);
+		lineop:		fn(dst: self ref Image, p0,p1: Point, end0,end1,radius: int, src: ref Image, sp: Point, op: int);
+		line:		fn(dst: self ref Image, p0,p1: Point, end0,end1,radius: int, src: ref Image, sp: Point);
+		polyop:		fn(dst: self ref Image, p: array of Point, end0,end1,radius: int, src: ref Image, sp: Point, op: int);
+		poly:		fn(dst: self ref Image, p: array of Point, end0,end1,radius: int, src: ref Image, sp: Point);
+		bezsplineop:		fn(dst: self ref Image, p: array of Point, end0,end1,radius: int, src: ref Image, sp: Point, op: int);
+		bezspline:		fn(dst: self ref Image, p: array of Point, end0,end1,radius: int, src: ref Image, sp: Point);
+		fillpolyop:	fn(dst: self ref Image, p: array of Point, wind: int, src: ref Image, sp: Point, op: int);
+		fillpoly:	fn(dst: self ref Image, p: array of Point, wind: int, src: ref Image, sp: Point);
+		fillbezsplineop:	fn(dst: self ref Image, p: array of Point, wind: int, src: ref Image, sp: Point, op: int);
+		fillbezspline:	fn(dst: self ref Image, p: array of Point, wind: int, src: ref Image, sp: Point);
+		ellipseop:	fn(dst: self ref Image, c: Point, a, b, thick: int, src: ref Image, sp: Point, op: int);
+		ellipse:	fn(dst: self ref Image, c: Point, a, b, thick: int, src: ref Image, sp: Point);
+		fillellipseop:	fn(dst: self ref Image, c: Point, a, b: int, src: ref Image, sp: Point, op: int);
+		fillellipse:	fn(dst: self ref Image, c: Point, a, b: int, src: ref Image, sp: Point);
+		arcop:	fn(dst: self ref Image, c: Point, a, b, thick: int, src: ref Image, sp: Point, alpha, phi: int, op: int);
+		arc:	fn(dst: self ref Image, c: Point, a, b, thick: int, src: ref Image, sp: Point, alpha, phi: int);
+		fillarcop:	fn(dst: self ref Image, c: Point, a, b: int, src: ref Image, sp: Point, alpha, phi: int, op: int);
+		fillarc:	fn(dst: self ref Image, c: Point, a, b: int, src: ref Image, sp: Point, alpha, phi: int);
+		bezierop:	fn(dst: self ref Image, a,b,c,d: Point, end0,end1,radius: int, src: ref Image, sp: Point, op: int);
+		bezier:	fn(dst: self ref Image, a,b,c,d: Point, end0,end1,radius: int, src: ref Image, sp: Point);
+		fillbezierop:	fn(dst: self ref Image, a,b,c,d: Point, wind:int, src: ref Image, sp: Point, op: int);
+		fillbezier:	fn(dst: self ref Image, a,b,c,d: Point, wind:int, src: ref Image, sp: Point);
+		textop:		fn(dst: self ref Image, p: Point, src: ref Image, sp: Point, font: ref Font, str: string, op: int): Point;
+		text:		fn(dst: self ref Image, p: Point, src: ref Image, sp: Point, font: ref Font, str: string): Point;
+		textbgop:		fn(dst: self ref Image, p: Point, src: ref Image, sp: Point, font: ref Font, str: string, bg: ref Image, bgp: Point, op: int): Point;
+		textbg:		fn(dst: self ref Image, p: Point, src: ref Image, sp: Point, font: ref Font, str: string, bg: ref Image, bgp: Point): Point;
+		border:	fn(dst: self ref Image, r: Rect, i: int, src: ref Image, sp: Point);
+		arrow:		fn(a,b,c: int): int;
+		# direct access to pixels
+		readpixels:	fn(src: self ref Image, r: Rect, data: array of byte): int;
+		writepixels:	fn(dst: self ref Image, r: Rect, data: array of byte): int;
+		# publishing
+		name:	fn(src: self ref Image, name: string, in: int): int;
+		# windowing
+		top:		fn(win: self ref Image);
+		bottom:		fn(win: self ref Image);
+		flush:		fn(win: self ref Image, func: int);
+		origin:		fn(win: self ref Image, log, scr: Point): int;
+	};
+
+	# a frame buffer, holding a connection to /dev/draw
+	Display: adt
+	{
+		image:	ref Image;	# holds the contents of the display
+		white:	ref Image;
+		black:	ref Image;
+		opaque:	ref Image;
+		transparent:	ref Image;
+
+		# allocate and start refresh slave
+		allocate:	fn(dev: string): ref Display;
+		startrefresh:	fn(d: self ref Display);
+		# attach to existing Screen
+		publicscreen:	fn(d: self ref Display, id: int): ref Screen;
+		getwindow:	fn(d: self ref Display, winname: string, screen: ref Screen, image: ref Image, backup: int): (ref Screen, ref Image);
+		# image creation
+		newimage:	fn(d: self ref Display, r: Rect, chans: Chans, repl, color: int): ref Image;
+		color:		fn(d: self ref Display, color: int): ref Image;
+		colormix:		fn(d: self ref Display, c1: int, c2: int): ref Image;
+		rgb:		fn(d: self ref Display, r, g, b: int): ref Image;
+		# attach to named Image
+		namedimage:	fn(d: self ref Display, name: string): ref Image;
+		# I/O to files
+		open:		fn(d: self ref Display, name: string): ref Image;
+		readimage:	fn(d: self ref Display, fd: ref Sys->FD): ref Image;
+		writeimage:	fn(d: self ref Display, fd: ref Sys->FD, i: ref Image): int;
+		# color map
+		rgb2cmap:	fn(d: self ref Display, r, g, b: int): int;
+		cmap2rgb:	fn(d: self ref Display, c: int): (int, int, int);
+		cmap2rgba:	fn(d: self ref Display, c: int): int;
+	};
+
+	# a mapping between characters and pictures; always attached to a Display
+	Font: adt
+	{
+		name:	string;		# *default* or a file name (this may change)
+		height:	int;		# interline spacing of font
+		ascent:	int;		# distance from baseline to top
+		display:	ref Display;	# where Font resides
+
+		# read from file or construct from local description
+		open:		fn(d: ref Display, name: string): ref Font;
+		build:		fn(d: ref Display, name, desc: string): ref Font;
+		# string extents
+		width:		fn(f: self ref Font, str: string): int;
+		bbox:		fn(f: self ref Font, str: string): Rect;
+	};
+
+	# a collection of windows; always attached to a Display
+	Screen: adt
+	{
+		id:		int;		# for export when public
+		image:		ref Image;	# root of window tree
+		fill:		ref Image;	# picture to use when repainting
+		display:	ref Display;	# where Screen resides
+
+		# create; see also Display.publicscreen
+		allocate:	fn(image, fill: ref Image, public: int): ref Screen;
+		# allocate a new window
+		newwindow:	fn(screen: self ref Screen, r: Rect, backing: int, color: int): ref Image;
+		# raise or lower a group of windows
+		top:		fn(screen: self ref Screen, wins: array of ref Image);
+		bottom:		fn(screen: self ref Screen, wins: array of ref Image);
+	};
+
+	# the state of a pointer device, e.g. a mouse or stylus
+	Pointer: adt
+	{
+		buttons:	int;	# bits 1 2 4 ... represent state of buttons left to right; 1 means pressed
+		xy:		Point;	# position
+		msec:	int;	# millisecond time stamp
+	};
+
+	# graphics context
+	Context: adt
+	{
+		display: 	ref Display;		# frame buffer on which windows reside
+		screen:		ref Screen;			# place to make windows (mux only)
+		wm:	chan of (string, chan of (string, ref Wmcontext));		# connect to window manager
+	};
+
+	# connection to window manager for one or more windows (as Images)
+	Wmcontext: adt
+	{
+		kbd: 		chan of int;		# incoming characters from keyboard
+		ptr: 		chan of ref Pointer;	# incoming stream of mouse positions
+		ctl:		chan of string;		# commands from wm to application
+		wctl:		chan of string;		# commands from application to wm
+		images:	chan of ref Image;	# exchange of images
+		connfd:	ref Sys->FD;		# connection control
+		ctxt:		ref Context;
+	};
+
+	# functions that don't fit well in any adt
+	setalpha:	fn(c: int, a: int): int;
+	bytesperline:	fn(r: Rect, d: int): int;
+	icossin:	fn(deg: int): (int, int);
+	icossin2:	fn(p: Point): (int, int);
+};
--- /dev/null
+++ b/module/ecmascript.m
@@ -1,0 +1,334 @@
+ESHostobj: module
+{
+	#
+	# extensible interface for adding host objects
+	#
+	# any implementation must obey the rules of the interpreter.
+	# it is an error to return bogus values, and may cause
+	# the interpreter to crash.
+	#
+	# get/put must return/set the value of property in o
+	#
+	# canput and hasproperty return es->true or es->false
+	#
+	# defaultval must return a primitive (non-object) value.
+	#	this means it can't return a String object, etc.
+	#
+	# call gets the caller's execution context in ex.
+	# the new this value is passed as an argument,
+	# but no new scopechain is allocated
+	# it returns a reference, which is typically just a value
+	#
+	# construct should make up a new object
+	#
+	get:		fn(ex: ref Ecmascript->Exec, o: ref Ecmascript->Obj, property: string): ref Ecmascript->Val;
+	put:		fn(ex: ref Ecmascript->Exec, o: ref Ecmascript->Obj, property: string, val: ref Ecmascript->Val);
+	canput:		fn(ex: ref Ecmascript->Exec, o: ref Ecmascript->Obj, property: string): ref Ecmascript->Val;
+	hasproperty:	fn(ex: ref Ecmascript->Exec, o: ref Ecmascript->Obj, property: string): ref Ecmascript->Val;
+	delete:		fn(ex: ref Ecmascript->Exec, o: ref Ecmascript->Obj, property: string);
+	defaultval:	fn(ex: ref Ecmascript->Exec, o: ref Ecmascript->Obj, tyhint: int): ref Ecmascript->Val;
+	call:		fn(ex: ref Ecmascript->Exec, func, this: ref Ecmascript->Obj, args: array of ref Ecmascript->Val, eval: int): ref Ecmascript->Ref;
+	construct:	fn(ex: ref Ecmascript->Exec, func: ref Ecmascript->Obj, args: array of ref Ecmascript->Val): ref Ecmascript->Obj;
+};
+
+#
+# calls to init and mkexec do the following
+#	math->FPcontrol(0, Math->INVAL|Math->ZDIV|Math->OVFL|Math->UNFL|Math->INEX);
+#
+Ecmascript: module
+{
+	PATH:	con "/dis/lib/ecmascript.dis";
+
+	#
+	# an execution context
+	#
+	Exec: adt
+	{
+		#
+		# well known glop
+		#
+		objproto:	cyclic ref Obj;
+		funcproto:	cyclic ref Obj;
+		strproto:	cyclic ref Obj;	
+		numproto:	cyclic ref Obj;
+		boolproto:	cyclic ref Obj;
+		arrayproto:	cyclic ref Obj;
+		dateproto:	cyclic ref Obj;
+		regexpproto:	cyclic ref Obj;
+		errproto:		cyclic ref Obj;
+		evlerrproto:		cyclic ref Obj;
+		ranerrproto:		cyclic ref Obj;
+		referrproto:		cyclic ref Obj;
+		synerrproto:		cyclic ref Obj;
+		typerrproto:		cyclic ref Obj;
+		urierrproto:		cyclic ref Obj;
+		interrproto:		cyclic ref Obj;
+		
+		global:		cyclic ref Obj;
+		this:		cyclic ref Obj;
+		scopechain:	cyclic list of ref Obj;
+
+		error:		string;
+		errval:		cyclic ref Val;
+
+		#
+		# private, keep out
+		#
+		stack:		cyclic array of ref Ref;
+		sp:	int;
+	};
+
+	#
+	# must be called at the dawn of time
+	# returns error string
+	init:	fn(): string;
+
+	#
+	# initialize a new global execution context
+	# if go is supplied, it's the global object
+	# if not, one is made up automatically
+	#
+	mkexec:		fn(go: ref Obj): ref Exec;
+
+	#
+	# throw a runtime error
+	# msg ends up in ex.error, and an
+	# "ecmascript runtime error" is raised
+	#
+	RUNTIME:	con "ecmascript runtime error";
+	runtime:	fn(ex: ref Exec, o: ref Obj, msg: string);
+
+	# runtime errors
+	EvalError, RangeError, ReferenceError, SyntaxError, TypeError, URIError, InternalError: ref Obj;
+
+	#
+	# debug flags: array of 256 indexed by char
+	#
+	# e	print ops as they are executed
+	# f	abort on an internal error
+	# p	print parsed code
+	# r	abort on any runtime error
+	# v	print value of expression statements
+	#
+	debug: array of int;
+
+	#
+	# parse and runt the source string
+	#
+	eval:	fn(ex: ref Exec, src: string): Completion;
+
+	Re: type ref Arena;
+
+	# the fundamental data structure
+	Obj: adt
+	{
+		props:		cyclic array of ref Prop;
+		prototype:	cyclic ref Obj;		# some builtin properties
+		val:		cyclic ref Val;
+		call:		cyclic ref Call;
+		construct:	cyclic ref Call;
+		class:		string;
+		host:		ESHostobj;		# method suite for host objects
+		re:		Re;				# compiled regexp for RegExp objects
+	};
+
+	Call: adt
+	{
+		params:		array of string;
+		code:		cyclic ref Code;
+		ex:		cyclic ref Exec;
+	};
+
+	# attributes
+	ReadOnly, DontEnum, DontDelete: con 1 << iota;
+	Prop: adt
+	{
+		attr:		int;
+		name:		string;
+		val:		cyclic ref RefVal;
+	};
+
+	# an extra level of indirection, because sometimes properties are aliased
+	RefVal: adt
+	{
+		val:		cyclic ref Val;
+	};
+
+	# types of js values
+	TUndef, TNull, TBool, TNum, TStr, TObj, TRegExp, NoHint: con iota;
+	Val: adt
+	{
+		ty:		int;
+		num:		real;
+		str:		string;
+		obj:		cyclic ref Obj;
+		rev:		ref REval;
+	};
+
+	# regular expression
+	REval: adt
+	{
+		p: string;
+		f: string;
+		i: int;
+	};
+
+	# intermediate result of expression evaluation
+	Ref: adt
+	{
+		isref:		int;
+		val:		ref Val;
+		base:		ref Obj;
+		name:		string;				# name of property within base
+	};
+
+	# completion values of statements
+	CNormal, CBreak, CContinue, CReturn, CThrow: con iota;
+	Completion: adt
+	{
+		kind:		int;
+		val:		ref Val;
+		lab:		string;
+	};
+
+	Code: adt
+	{
+		ops:		array of byte;			# all instructions
+		npc:		int;				# end of active portion of ops
+		vars:		cyclic array of ref Prop;	# variables defined in the code
+		ids:		array of string;		# ids used in the code
+		strs:		array of string;		# string literal
+		nums:		array of real;			# numerical literals
+		fexps:	cyclic array of ref Obj;	# function expressions
+	};
+
+	#
+	# stuff for adding host objects
+	#
+	# ecmascript is also a host object;
+	get:		fn(ex: ref Ecmascript->Exec, o: ref Ecmascript->Obj, property: string): ref Ecmascript->Val;
+	put:		fn(ex: ref Ecmascript->Exec, o: ref Ecmascript->Obj, property: string, val: ref Ecmascript->Val);
+	canput:		fn(ex: ref Ecmascript->Exec, o: ref Ecmascript->Obj, property: string): ref Ecmascript->Val;
+	hasproperty:	fn(ex: ref Ecmascript->Exec, o: ref Ecmascript->Obj, property: string): ref Ecmascript->Val;
+	delete:		fn(ex: ref Ecmascript->Exec, o: ref Ecmascript->Obj, property: string);
+	defaultval:	fn(ex: ref Ecmascript->Exec, o: ref Ecmascript->Obj, tyhint: int): ref Ecmascript->Val;
+	call:		fn(ex: ref Ecmascript->Exec, func, this: ref Ecmascript->Obj, args: array of ref Ecmascript->Val, eval: int): ref Ecmascript->Ref;
+	construct:	fn(ex: ref Ecmascript->Exec, func: ref Ecmascript->Obj, args: array of ref Ecmascript->Val): ref Ecmascript->Obj;
+
+	#
+	# return the named variable from the scope chain sc
+	#
+	bivar:		fn(ex: ref Exec, sc: list of ref Obj, s: string): ref Val;
+
+	#
+	# return the nth argument value, or undefined if too far
+	#
+	biarg:		fn(args: array of ref Val, n: int): ref Val;
+
+	#
+	# make up a new object
+	# most often called as mkobj(ex.objproto, "Object")
+	#
+	mkobj:		fn(proto: ref Obj, class: string): ref Obj;
+
+	#
+	# object installation helpers
+	#
+	Builtin: adt
+	{
+		name:	string;
+		val:	string;
+		params:	array of string;
+		length:	int;
+	};
+	biinst:		fn(o: ref Obj, bi: Builtin, proto: ref Obj, h: ESHostobj): ref Obj;
+	biminst:	fn(o: ref Obj, bis: array of Builtin, proto: ref Obj, h: ESHostobj);
+
+	#
+	# instantiate a new variable inside an object
+	#
+	varinstant:	fn(in: ref Obj, attr: int, name: string, val: ref RefVal);
+
+	#
+	# various constructors
+	#
+	objval:		fn(o: ref Obj): ref Val;
+	strval:		fn(s: string): ref Val;
+	numval:		fn(r: real): ref Val;
+	valref:		fn(v: ref Val): ref Ref;
+
+	#
+	# conversion routines defined in section 9
+	#
+	toPrimitive:	fn(ex: ref Exec, v: ref Val, ty: int): ref Val;
+	toBoolean:	fn(ex: ref Exec, v: ref Val): ref Val;
+	toNumber:	fn(ex: ref Exec, v: ref Val): real;
+	toInteger:	fn(ex: ref Exec, v: ref Val): real;
+	toInt32:	fn(ex: ref Exec, v: ref Val): int;
+	toUint32:	fn(ex: ref Exec, v: ref Val): big;
+	toUint16:	fn(ex: ref Exec, v: ref Val): int;
+	toString:	fn(ex: ref Exec, v: ref Val): string;
+	toObject:	fn(ex: ref Exec, v: ref Val): ref Obj;
+
+	#
+	# simple coercion routines to force
+	# Boolean, String, and Number values to objects and vice versa
+	#
+	coerceToObj:	fn(ex: ref Exec, v: ref Val): ref Val;
+	coerceToVal:	fn(v: ref Val): ref Val;
+
+	#
+	# object/value kind checkers
+	#
+	isstrobj:	fn(o: ref Obj): int;
+	isnumobj:	fn(o: ref Obj): int;
+	isboolobj:	fn(o: ref Obj): int;
+	isdateobj:	fn(o: ref Obj): int;
+	isregexpobj: fn(o: ref Obj): int;
+	isarray:	fn(o: ref Obj): int;
+	isstr:		fn(v: ref Val): int;
+	isnum:		fn(v: ref Val): int;
+	isbool:		fn(v: ref Val): int;
+	isobj:		fn(v: ref Val): int;
+
+	#
+	# well-known ecmascript primitive values
+	#
+	undefined:	ref Val;
+	true:		ref Val;
+	false:		ref Val;
+	null:		ref Val;
+
+	# regexp data structures
+
+	refRex: type int;	# used instead of ref Rex to avoid circularity
+
+	Set: adt {				# character class
+		neg: int;			# 0 or 1
+		ascii: array of int;		# ascii members, bit array
+		unicode: list of (int,int);	# non-ascii char ranges
+		subset: cyclic list of ref Set;
+	};
+
+	Nstate: adt{
+		m: int;
+		n: int;
+	};
+
+	Rex: adt {		# node in parse of regex, or state of fsm
+		kind: int;	# kind of node: char or ALT, CAT, etc
+		left: refRex;	# left descendant
+		right: refRex;	# right descendant, or next state
+		set: ref Set;	# character class
+		pno: int;
+		greedy: int;
+		ns: ref Nstate;
+	};
+
+	Arena: adt {		# free store from which nodes are allocated
+		rex: array of Rex;		
+		ptr: refRex;	# next available space
+		start: refRex;	# root of parse, or start of fsm
+		pno: int;
+	};
+};
--- /dev/null
+++ b/module/emio.m
@@ -1,0 +1,103 @@
+#
+# File: emio.m
+#
+# This file contains the declaration of the EMIO module.
+# The EMIO module provides protocol independent access
+# to an email server.
+#
+ 
+EMIO : module
+{
+        #
+        # The init function initializes the EMIO module.
+        # It must be called before any other function in the
+        # module.
+        #
+        init: fn();
+ 
+        #
+        # The open function opens a connection with the email
+        # server.  The function requires the email server's
+        # tcp/ip address, a username and a password to make the
+        # connection to the email server.  It returns a tuple
+        # (int, string).  The int indicates success or failure
+        # (0 = failure, 1 = success). If the function fails,
+        # the int returned is 0, the string returned will indicate
+        # why the function failed. It should be called after the
+        # init function and before any other function in the module.
+        #
+        open: fn(ipaddr : string,
+                 username : string,
+                 password : string) : (int, string);
+ 
+        #
+        # The numberofmessages function indicates how many mail
+        # messages are in the specified users mailbox. It returns
+        # a tuple (int, string).  The int indicates the number of
+        # mail messages in the mailbox (-1 = function failed, 0 =
+        # no mail message, 1 = one mail message ...). If the function fails,
+        # the int returned is -1, the string returned will indicate
+        # why the function failed.
+        #
+        numberofmessages: fn() : (int, string);
+
+	#
+	# This function provides the number of octets in the specified
+	# message number.  It returns a tuple (int, string).  The int indicates
+	# the number of octets in the mail message.  If it is -1, the
+	# function has failed and the string returned will contain the
+	# possible reason.  
+	# This function implements the LIST command, but only with an
+	# argument - the message number.
+	messagelength: fn(num : int) : (int, string); 
+
+        #
+        # The messagetext function returns the text of the specified
+        # mail message.  The function requires the number of the
+        # mail message to retrieve. It returns a triple
+        # (int, string, list of string). The int indicates success or failure
+        # (0 = failure, 1 = success). If the function fails,
+        # the int returned is 0, the string returned will indicate
+        # why the function failed. If the function succeded the list
+        # of string contains the text for the specified mail message.
+        #
+        messagetext: fn(messagenumber : int) : (int, string, list of string);
+
+	#
+	# This is similar to messagetext() but returns a string, rather than
+	# a list of string. The string contains the complete text of the mail
+	# message, header and body. Each line of the message is separate by a
+	# DELIMETER (currently set to |&|) fo easier processing.
+	#
+	msgtextstring: fn (num : int) : (int, string, string);
+ 
+        #
+        # The deletemessage function markes the specified mail
+        # message as deleted. The function requires the number of
+        # the mail message to delete. It returns a tuple
+        # (int, string).  The int indicates success or failure
+        # (0 = failure, 1 = success). If the function fails,
+        # the int returned is 0, the string returned will indicate
+        # why the function failed.
+        #
+        deletemessage: fn(messagenumber : int) : (int, string);
+ 
+        #
+        # The reset function unmarks all messages that have been
+        # marked deleted during this session. It returns a tuple
+        # (int, string).  The int indicates success or failure
+        # (0 = failure, 1 = success). If the function fails,
+        # the int returned is 0, the string returned will indicate
+        # why the function failed.
+        #
+        reset: fn() : (int, string);
+ 
+        #
+        # The close function closes a connection with the email
+        # server. It returns a tuple (int, string).  The int
+        # indicates success or failure (0 = failure, 1 = success).
+        # If the function fails, the int returned is 0, the string
+        # returned will indicate why the function failed.
+        #
+        close: fn() : (int, string);
+};
--- /dev/null
+++ b/module/encoding.m
@@ -1,0 +1,11 @@
+Encoding: module
+{
+	BASE64PATH:	con "/dis/lib/encoding/base64.dis";
+	BASE32PATH:	con "/dis/lib/encoding/base32.dis";
+	BASE32APATH:	con "/dis/lib/encoding/base32a.dis";
+	BASE16PATH:	con "/dis/lib/encoding/base16.dis";
+
+	enc:	fn(a: array of byte): string;
+	dec:	fn(s: string): array of byte;
+#	deca:	fn(a: array of byte): array of byte;
+};
--- /dev/null
+++ b/module/env.m
@@ -1,0 +1,10 @@
+Env: module {
+	getenv:	fn(var: string): string;		# returns nil if var not set
+	setenv:	fn(var: string, val: string): int;	# returns -1 on failure
+	getall: fn(): list of (string, string);
+
+	clone:	fn(): int;					# forks a copy of the environment, returns -1 on failure
+	new:		fn(): int;					# sets up new empty environment, returns -1 on failure
+
+	PATH:	con "/dis/lib/env.dis";
+};
--- /dev/null
+++ b/module/ether.m
@@ -1,0 +1,11 @@
+Ether: module
+{
+	PATH:	con "/dis/lib/ether.dis";
+	Eaddrlen:	con 6;
+
+	init:	fn();
+	parse:	fn(s: string): array of byte;
+	text:	fn(a: array of byte): string;
+	addressof:	fn(dev: string): array of byte;
+	eqaddr:	fn(a, b: array of byte): int;
+};
--- /dev/null
+++ b/module/exception.m
@@ -1,0 +1,18 @@
+Exception: module{
+
+	PATH:	con "/dis/lib/exception.dis";
+
+	# returns the last exception in the form pc, module, exception
+	# on the process with the given pid (-1 gives current process)
+	# returns (0, nil, nil) if no exception
+	getexc:	fn(pid: int): (int, string, string);
+
+	NOTIFYLEADER, PROPAGATE: con iota;
+
+	# set the exception mode(NOTIFYLEADER or PROPAGATE)
+	# on the current process
+	# it is assumed that the process is a group leader (see Sys->NEWPGRP)
+	# returns -1 on failure, 0 on success
+	setexcmode:	fn(mode: int): int;
+
+};
--- /dev/null
+++ b/module/factotum.m
@@ -1,0 +1,69 @@
+Factotum: module
+{
+	PATH:	con "/dis/lib/factotum.dis";
+
+	# client interface to Plan 9 or Inferno factotum
+
+	Authinfo: adt {
+		cuid:	string;	# caller id
+		suid:	string;	# server id
+		cap:	string;	# capability (only valid on server side)
+		secret:	array of byte;
+		# TO DO: add attrs
+
+		unpack:	fn(a: array of byte): (int, ref Authinfo);
+		read:	fn(fd: ref Sys->FD): ref Authinfo;
+	};
+
+	mount:	fn(fd: ref Sys->FD, mnt: string, flags: int, aname: string, keyspec: string): (int, ref Authinfo);
+
+	# factotum interaction
+	AuthRpcMax: con 4096;
+
+	init:	fn();
+	open:	fn(): ref Sys->FD;
+	rpc:	fn(fd: ref Sys->FD, verb: string, a: array of byte): (string, array of byte);
+	proxy:	fn(afd: ref Sys->FD, facfd: ref Sys->FD, arg: string): ref Authinfo;
+	genproxy: fn(
+		readc: chan of (array of byte, chan of (int, string)),
+		writec: chan of (array of byte, chan of (int, string)),
+		donec: chan of (ref Authinfo, string),
+		afd: ref Sys->FD,
+		params: string);
+	rpcattrs:	fn(afd: ref Sys->FD): list of ref Attr;
+
+	getuserpasswd:	fn(keyspec: string): (string, string);
+
+	# challenge/response
+	Challenge: adt {
+		user:	string;
+		chal:	string;
+		afd:	ref Sys->FD;
+	};
+
+	challenge:	fn(keyspec: string): ref Challenge;
+	response:	fn(c: ref Challenge, resp: string): ref Authinfo;
+	respond:	fn(chal: string, keyspec: string): (string, string);
+
+	dump:	fn(a: array of byte): string;
+	setdebug:	fn(i: int);
+
+	Aattr, Aval, Aquery: con iota;
+
+	Attr: adt {
+		tag:	int;
+		name:	string;
+		val:	string;
+
+		text:	fn(a: self ref Attr): string;
+	};
+
+	parseattrs:	fn(s: string): list of ref Attr;
+	copyattrs:		fn(l: list of ref Attr): list of ref Attr;
+	delattr:	fn(l: list of ref Attr, n: string): list of ref Attr;
+	takeattrs:	fn(l: list of ref Attr, names: list of string): list of ref Attr;
+	findattr:	fn(l: list of ref Attr, n: string): ref Attr;
+	findattrval:	fn(l: list of ref Attr, n: string): string;
+	publicattrs:	fn(l: list of ref Attr): list of ref Attr;
+	attrtext:	fn(l: list of ref Attr): string;
+};
--- /dev/null
+++ b/module/ffts.m
@@ -1,0 +1,32 @@
+FFTs: module{
+	PATH:	con	"/dis/math/ffts.dis";
+
+	ffts: fn(a,b:array of real, ntot,n,nspan,isn:int);
+};
+
+#  multivariate complex fourier transform, computed in place
+#    using mixed-radix fast fourier transform algorithm.
+#  arrays a and b originally hold the real and imaginary
+#    components of the data, and return the real and
+#    imaginary components of the resulting fourier coefficients.
+#  multivariate data is indexed according to the fortran
+#    array element successor function, without limit
+#    on the number of implied multiple subscripts.
+#    the subroutine is called once for each variate.
+#    the calls for a multivariate transform may be in any order.
+#  ntot is the total number of complex data values.
+#  n is the dimension of the current variable.
+#  nspan/n is the spacing of consecutive data values
+#    while indexing the current variable.
+#  the sign of isn determines the sign of the complex
+#    exponential, and the magnitude of isn is normally one.
+#  univariate transform:
+#      ffts(a,b,n,n,n,1)
+#  trivariate transform with a(n1,n2,n3), b(n1,n2,n3):
+#      ffts(a,b,n1*n2*n3,n1,n1,1)
+#      ffts(a,b,n1*n2*n3,n2,n1*n2,1)
+#      ffts(a,b,n1*n2*n3,n3,n1*n2*n3,1)
+#  the data can alternatively be stored in a single vector c
+#    alternating real and imaginary parts. the magnitude of isn changed
+#    to two to give correct indexing increment, and a[0:] and a[1:] used
+#    for a and b
--- /dev/null
+++ b/module/filepat.m
@@ -1,0 +1,10 @@
+Filepat: module
+{
+	PATH:	con "/dis/lib/filepat.dis";
+	
+	# Turn file name with * ? [] into list of files.  Slashes are significant.
+	expand:	fn(pat: string): list of string;
+
+	# See if file name matches pattern; slashes not treated specially.
+	match:	fn(pat, name: string): int;
+};
--- /dev/null
+++ b/module/filter.m
@@ -1,0 +1,25 @@
+Filter: module
+{
+	DEFLATEPATH: con "/dis/lib/deflate.dis";
+	INFLATEPATH: con "/dis/lib/inflate.dis";
+	SLIPPATH: con "/dis/lib/slip.dis";
+
+	Rq: adt {
+		pick {
+		Start =>
+			pid: int;
+		Fill or Result =>
+			buf: array of byte;
+			reply: chan of int;
+		Info =>
+			msg: string;
+		Finished =>
+			buf: array of byte;
+		Error =>
+			e: string;
+		}
+	};
+
+	init: fn();
+	start: fn(param: string): chan of ref Rq;
+};
--- /dev/null
+++ b/module/format.m
@@ -1,0 +1,30 @@
+Format: module {
+	PATH: con "/dis/lib/format.dis";
+	Fmtspec: adt {
+		name: string;
+		fields: cyclic array of Fmtspec;
+	};
+	Fmt: adt {
+		kind: int;
+		fields: cyclic array of Fmt;
+	};
+	Fmtval: adt {
+		val: ref Sexprs->Sexp;
+		recs: cyclic array of array of Fmtval;
+
+		text: fn(v: self Fmtval): string;
+	};
+	Fmtfile: adt {
+		spec: array of Fmtspec;
+		descr: array of byte;
+	
+		new: fn(spec: array of Fmtspec): Fmtfile;
+		open: fn(f: self Fmtfile, name: string): ref Bufio->Iobuf;
+		read: fn(f: self Fmtfile, iob: ref Bufio->Iobuf): (array of Fmtval, string);
+	};
+	init: fn();
+	spec2se: fn(spec: array of Fmtspec): list of ref Sexprs->Sexp;
+	spec2fmt: fn(spec: array of Fmtspec): array of Fmt;
+	se2fmt: fn(spec: array of Fmtspec, se: ref Sexprs->Sexp): (array of Fmt, string);
+	rec2val: fn(spec: array of Fmtspec, rec: ref Sexprs->Sexp): (array of Fmtval, string);
+};
--- /dev/null
+++ b/module/freetype.m
@@ -1,0 +1,44 @@
+Freetype: module {
+	PATH: con "$Freetype";
+
+	Matrix: adt {
+		a, b: int;	# 16.16 fixed-point coefficients
+		c, d: int;
+	};
+
+	Vector: adt {
+		dx: int;	# 26.6 fixed-point deltas
+		dy: int;
+	};
+
+	STYLE_ITALIC,
+	STYLE_BOLD: con 1 << iota;
+
+	Face: adt {
+		nfaces: int;
+		index: int;
+		style: int;		# STYLE_xxx
+		height: int;
+		ascent: int;
+		familyname: string;
+		stylename: string;
+
+		# pts - point size as a 26.6 fixed-point value
+		setcharsize: fn(face: self ref Face, pts, hdpi, vdpi: int): string;
+		settransform: fn(face: self ref Face, m: ref Matrix, v: ref Vector): string;
+		haschar: fn(face: self ref Face, c: int): int;
+		loadglyph: fn(face: self ref Face, c: int): ref Glyph;
+	};
+
+	Glyph: adt {
+		top: int;
+		left: int;
+		height: int;
+		width: int;
+		advance: Draw->Point;	# 26.6 fixed-point
+		bitmap:	array of byte;	# (width*height) 8-bit greyscale
+	};
+
+	newface: fn(path: string, index: int): ref Face;
+	newmemface: fn(data: array of byte, index: int): ref Face;
+};
--- /dev/null
+++ b/module/fslib.m
@@ -1,0 +1,88 @@
+Fslib: module {
+	PATH: con "/dis/lib/fslib.dis";
+	Value: adt {
+		x:	fn(v: self ref Value): ref Value.X;
+		p:	fn(v: self ref Value): ref Value.P;
+		s:	fn(v: self ref Value): ref Value.S;
+		c:	fn(v: self ref Value): ref Value.C;
+		t:	fn(v: self ref Value): ref Value.T;
+		v:	fn(v: self ref Value): ref Value.V;
+		m:	fn(v: self ref Value): ref Value.M;
+		typec: fn(v: self ref Value): int;
+		discard: fn(v: self ref Value);
+		pick {
+		X =>
+			i: Fschan;
+		T =>
+			i: Entrychan;
+		P =>
+			i: Gatechan;
+		C =>
+			i: ref Sh->Cmd;
+		S =>
+			i: string;
+		V =>
+			i: chan of int;		# sync channel for void-valued processes
+		M =>
+			i: Cmpchan;
+		}
+	};
+	init:			fn();
+	typecompat:	fn(t, act: string): int;
+	sendnulldir:	fn(c: Fschan): int;
+	quit:			fn(errorc: chan of string);
+	report:		fn(errorc: chan of string, err: string);
+	copy:		fn(src, dst: Fschan): int;
+
+	cmdusage:	fn(cmd, t: string): string;
+	type2s:		fn(t: int): string;
+	opttypes:		fn(opt: int, opts: string): (int, string);
+	splittype:		fn(t: string): (int, string, string);
+
+	Report: adt {
+
+		reportc: chan of string;
+		startc: chan of (string, chan of string);
+		enablec: chan of int;
+	
+		new:		fn(): ref Report;
+		enable:	fn(r: self ref Report);
+		start:		fn(r: self ref Report, name: string): chan of string;
+	};
+	Option: adt {
+		opt: int;
+		args: list of ref Value;
+	};
+	Entrychan: adt {
+		sync: chan of int;
+		c: chan of Entry;
+	};
+	Cmpchan: type chan of (ref Sys->Dir, ref Sys->Dir, chan of int);
+	Entry: type (ref Sys->Dir, string, int);
+	Gatequery: type (Entry, chan of int);
+	Gatechan: type chan of Gatequery;
+	Fsdata: adt {
+		dir: ref Sys->Dir;
+		data: array of byte;
+	};
+	Fschan: type chan of (Fsdata, chan of int);
+	Next, Down, Skip, Quit: con iota;
+
+	Nilentry: con (nil, nil, 0);
+};
+
+Fsmodule: module {
+	types: fn(): string;
+	init:	fn();
+	run: fn(ctxt: ref Draw->Context, r: ref Fslib->Report,
+		opts: list of Fslib->Option, args: list of ref Fslib->Value): ref Fslib->Value;
+};
+
+Fsfilter: module {
+	PATH: con "/dis/lib/fsfilter.dis";
+	filter: fn[T](t: T, src, dst: Fslib->Fschan)
+		for{
+		T =>
+			query: fn(t: self T, d: ref Sys->Dir, name: string, depth: int): int;
+		};
+};
--- /dev/null
+++ b/module/fsproto.m
@@ -1,0 +1,10 @@
+FSproto: module
+{
+	PATH: con "/dis/lib/fsproto.dis";
+
+	Direntry: type (string, string, ref Sys->Dir);
+
+	init:	fn(): string;
+	readprotofile: fn(proto: string, root: string, entries: chan of Direntry, warnings: chan of (string, string)): string;
+	readprotostring: fn(proto: string, root: string, entries: chan of Direntry, warnings: chan of (string, string));
+};
--- /dev/null
+++ b/module/gr.m
@@ -1,0 +1,51 @@
+GR: module{
+	PATH:	con "/dis/math/gr.dis";
+
+	OP: adt{
+		code, n: int;
+		x, y: array of real;
+		t: string;
+	};
+
+	open:	fn(ctxt: ref Draw->Context, title: string): ref Plot;
+
+	Plot: adt{
+		bye:	fn(p: self ref Plot);
+		equalxy:fn(p: self ref Plot);
+		graph:	fn(p: self ref Plot, x, y: array of real);
+		paint: 	fn(p: self ref Plot, xlabel, xunit, ylabel, yunit: string);
+		pen:	fn(p: self ref Plot, nib: int);
+		text:	fn(p: self ref Plot, justify: int, s: string, x, y: real);
+
+		op: list of OP;
+		xmin, xmax, ymin, ymax: real;
+		textsize: real;
+		t: ref Tk->Toplevel;		# window containing .fc.c canvas
+		titlechan: chan of string;	# Wm titlebar
+		canvaschan: chan of string;	# button clicks for measurements
+	};
+
+	# op code
+	GRAPH:		con 1;
+	TEXT:		con 2;
+	PEN:		con 3;
+
+	# pen
+	CIRCLE:		con 101;
+	CROSS:		con 102;
+	SOLID:		con 103;
+	DASHED:		con 104;
+	INVIS:		con 105;
+	REFERENCE:	con 106;
+	DOTTED:		con 107;
+
+	# text justify
+	LJUST:		con 8r00;
+	CENTER:		con 8r01;
+	RJUST:		con 8r02;
+	HIGH:		con 8r00;
+	MED:		con 8r10;
+	BASE:		con 8r20;
+	LOW:		con 8r30;
+	UP:		con 8r100;
+};
--- /dev/null
+++ b/module/grid/announce.m
@@ -1,0 +1,5 @@
+Announce: module {
+	PATH:	con "/dis/grid/lib/announce.dis";
+	init:		fn();
+	announce:	fn(): (string, ref Sys->Connection);	# find a local address, any port, and return its name and an announced connection.
+};
--- /dev/null
+++ b/module/grid/browse.m
@@ -1,0 +1,43 @@
+Browse: module
+{
+	PATH: con "/dis/grid/lib/browse.dis";
+	
+	# panetype
+	SINGLEPANE: con 1; # does not work yet
+	SPLITPANE: con 2;
+	# filetype
+	DIRSONLY: con 3;
+	FILESONLY: con 4;
+	FILESORDIRS: con 5;
+	# selecttype
+	SINGLE: con 1;
+	MULTI: con 2;
+	SELECT: con 1;
+	TOGGLE: con 2;
+	NONE: con 3;
+	opened : list of string;
+	init : fn (top: ref Tk->Toplevel, rlabel, root: string, ptype, ftype, stype: int, pathrd : PathReader);
+	getselectedpath : fn (pane: int): string;
+	refresh : fn (top: ref Tk->Toplevel);
+	gotofile : fn (top: ref Tk->Toplevel, path:string, pnum: int);
+	opendir : fn (top: ref Tk->Toplevel, path, tkpath: string);
+	changepane : fn (top: ref Tk->Toplevel, ptype: int);
+	resizewin : fn (top: ref Tk->Toplevel, width, height: int);
+	selectfile : fn (top: ref Tk->Toplevel, pane, action: int, path, tkpath: string);
+	setscrollr : fn (top: ref Tk->Toplevel);
+	getpnum : fn (tkpath: string): int;
+	pane1see : fn (top: ref Tk->Toplevel);
+	gotopath : fn (top: ref Tk->Toplevel, dir: string, openfinal, pnum: int): string;
+	getpath : fn (top: ref Tk->Toplevel, f: string): string;
+	prevpath : fn (path: string): string;
+	setcentre : fn (top1, top2: ref Tk->Toplevel);
+	addselection : fn (top: ref Tk->Toplevel, file: string, val, args, dups: int, sval, sargs: string): string;
+	delselection : fn (top: ref Tk->Toplevel, n: string): string;
+	newfl : fn (top: ref Tk->Toplevel, rlabel, root: string);
+	setc3frame : fn (top: ref Tk->Toplevel, frame: string);
+	doargs : fn (top: ref Tk->Toplevel, lst: list of string);
+	getselected : fn (top: ref Tk->Toplevel, frame: string): list of (string, string, string);
+	movdiv : fn (top: ref Tk->Toplevel, x: int);
+	dialog : fn (ctxt: ref draw->Context, oldtop: ref Tk->Toplevel, butlist: list of string, title, msg: string): int;
+	getc3frame : fn (): string;
+};
\ No newline at end of file
--- /dev/null
+++ b/module/grid/browser.m
@@ -1,0 +1,97 @@
+Browser: module {
+
+	PATH: con "/dis/grid/lib/browser.dis";
+
+	DESELECT: con 0;
+	SELECT: con 1;
+	TOGGLE: con 2;
+	OPEN: con 3;
+	CLOSE: con 4;
+
+	init: fn ();
+	dialog: fn (ctxt: ref draw->Context, oldtop: ref Tk->Toplevel, butlist: list of string, title, msg: string): int;
+	prevpath: fn (path: string): string;
+	setcentre: fn (top1, top2: ref Tk->Toplevel);
+
+	Browse: adt {
+		new: fn (top: ref Tk->Toplevel, tkchanname, root, rlabel: string, nopanes: int, reader: PathReader): ref Browse;
+		refresh: fn (b: self ref Browse);
+		defaultaction: fn (b: self ref Browse, lst: list of string, f: ref File);
+		getpath: fn (b: self ref Browse, tkpath: string): ref File;
+		opendir: fn (b: self ref Browse, file: File, tkpath: string, action: int): int;
+		newroot: fn (b: self ref Browse, root, rlabel: string);
+		changeview: fn (b: self ref Browse, nopanes: int);
+		selectfile: fn (b: self ref Browse, pane, action: int, file: File, tkpath: string);
+		gotoselectfile: fn (b: self ref Browse, file: File): string;
+		gotopath: fn (b: self ref Browse, dir: File, openfinal: int): (File, string);
+		getselected: fn (b: self ref Browse, pane: int): File;
+		addopened: fn (b: self ref Browse, file: File, add: int);
+		showpath: fn (b: self ref Browse, on: int);
+		resize: fn (b: self ref Browse);
+		top: ref Tk->Toplevel;
+		tkchan: string;
+		bgnorm, bgselect: string;
+		nopanes: int;
+		selected: array of Selected;
+		opened: list of File;
+		root, rlabel: string;
+		reader: PathReader;
+		pane1: File;
+		pane0width: string;
+		width: int;
+		showpathlabel: int;
+		released: int;
+	};
+
+	SELECTED: con 0;
+	UNSELECTED: con 1;
+	ALL: con 2;
+
+	Select: adt {
+		new: fn (top: ref Tk->Toplevel, tkchanname: string): ref Select;
+		addframe: fn (s: self ref Select, fname, title: string);
+		showframe: fn (s: self ref Select, fname: string);
+		delframe: fn (s: self ref Select, fname: string);
+		addselection: fn (s: self ref Select, fname, text: string, lp: list of ref Parameter, allowdups: int): string;
+		delselection: fn (s: self ref Select, fname, tkpath: string);
+		getselection: fn (s: self ref Select, fname: string): list of (string, list of ref Parameter);
+		getselected: fn (s: self ref Select, fname: string): string;
+		select: fn (s: self ref Select, fname, tkpath: string, action: int);
+		defaultaction: fn (s: self ref Select, lst: list of string);
+		resize: fn (s: self ref Select, width, height: int);
+		setscrollr: fn (s: self ref Select, fname: string);
+		top: ref Tk->Toplevel;
+		tkchan: string;
+		currfname, currfid: string;
+		frames: list of ref Frame;
+	};
+
+	Frame: adt {
+		name: string;
+		path: string;
+		selected: string;
+	};
+
+	Parameter: adt {
+		pick {
+		ArgIn =>
+			name, initval: string;
+		ArgOut =>
+			name, val: string;
+		IntIn =>
+			min, max, initval: int;
+		IntOut =>
+			val: int;
+		}
+	};
+
+	File: adt {
+		eq: fn (a,b: File): int;
+		path, qid: string;
+	};
+
+	Selected: adt {
+		file: File;
+		tkpath: string;
+	};
+};
\ No newline at end of file
--- /dev/null
+++ b/module/grid/demo/block.m
@@ -1,0 +1,14 @@
+Block : module
+{
+	PATH: con "/dis/grid/demo/block.dis";
+
+	init : fn (pathname: string, ep: Exproc);
+	slave : fn ();
+	writedata : fn (s: string);
+	masterinit : fn (noblocks: int);
+	reader : fn (noblocks: int, chanout: chan of string, sync: chan of int);
+	makefile : fn (block: int, let: string): string;
+	err : fn (s: string);
+	cleanfiles : fn (delpath: string);
+	isin : fn (l: list of string, s: string): int;
+};
\ No newline at end of file
--- /dev/null
+++ b/module/grid/demo/exproc.m
@@ -1,0 +1,7 @@
+Exproc : module
+{
+	getslavedata : fn (lst: list of string);
+	doblock : fn (block: int, bpath: string);
+	readblock : fn (block: int, dir: string, chanout: chan of string): int;
+	finish : fn (waittime: int, tkchan: chan of string);
+};
\ No newline at end of file
--- /dev/null
+++ b/module/grid/fbrowse.m
@@ -1,0 +1,13 @@
+FBrowse : module
+{
+	PATH: con	"/dis/grid/lib/fbrowse.dis";
+	NOTHING: con	0;
+	RUN: con		1;
+	OPEN: con	2;
+	WRITE: con	3;
+	ERROR: con	-1;
+
+	init : fn (ctxt : ref Draw->Context, title, root, currdir: string): string;
+	readpath : fn (dir: Browser->File): (array of ref sys->Dir, int);
+};
+
--- /dev/null
+++ b/module/grid/pathreader.m
@@ -1,0 +1,3 @@
+PathReader : module {
+	readpath: fn (dir: Browser->File): (array of ref sys->Dir, int);
+};
\ No newline at end of file
--- /dev/null
+++ b/module/grid/readjpg.m
@@ -1,0 +1,95 @@
+Readjpg: module
+{
+
+	PATH: con "/dis/grid/readjpg.dis";
+
+	ImageSource: adt
+	{
+		width:	int;
+		height:	int;
+		origw:	int;
+		origh:	int;
+		i:		int;
+		jstate:	ref Jpegstate;
+		data:		array of byte;
+	};
+
+	Jpegstate: adt
+	{
+		# variables in i/o routines
+		sr:	int;	# shift register, right aligned
+		cnt:	int;	# # bits in right part of sr
+	
+		Nf:		int;
+		comp:	array of Framecomp;
+		mode:	byte;
+		X,Y:		int;
+		qt:		array of array of int;	# quantization tables
+		dcht:		array of ref Huffman;
+		acht:		array of ref Huffman;
+		Ns:		int;
+		scomp:	array of Scancomp;
+		Ss:		int;
+		Se:		int;
+		Ah:		int;
+		Al:		int;
+		ri:		int;
+		nseg:	int;
+		nblock:	array of int;
+		
+		# progressive scan
+		dccoeff:	array of array of int;
+		accoeff:	array of array of array of int;	# only need 8 bits plus quantization
+		nacross:	int;
+		ndown:	int;
+		Hmax:	int;
+		Vmax:	int;
+	};
+	
+	Huffman: adt
+	{
+		bits:	array of int;
+		size:	array of int;
+		code:	array of int;
+		val:	array of int;
+		mincode:	array of int;
+		maxcode:	array of int;
+		valptr:	array of int;
+		# fast lookup
+		value:	array of int;
+		shift:	array of int;
+	};
+		
+	Framecomp: adt	# Frame component specifier from SOF marker
+	{
+		C:	int;
+		H:	int;
+		V:	int;
+		Tq:	int;
+	};
+	
+	Scancomp: adt	# Frame component specifier from SOF marker
+	{
+		C:	int;
+		tdc:	int;
+		tac:	int;
+	};
+	
+	# Constants, all preceded by byte 16rFF
+	SOF:	con 16rC0;	# Start of Frame
+	SOF2:	con 16rC2;	# Start of Frame; progressive Huffman
+	DHT:	con 16rC4;	# Define Huffman Tables
+	RST:	con 16rD0;	# Restart interval termination
+	SOI:	con 16rD8;	# Start of Image
+	EOI:	con 16rD9;	# End of Image
+	SOS:	con 16rDA;	# Start of Scan
+	DQT:	con 16rDB;	# Define quantization tables
+	DNL:	con 16rDC;	# Define number of lines
+	DRI:	con 16rDD;	# Define restart interval
+	APPn:	con 16rE0;	# Reserved for application segments
+	COM:	con 16rFE;	# Comment
+
+	init : fn (disp: ref Draw->Display);
+	fjpg2img : fn (fd: ref sys->FD, cachepath: string, chanin, chanout: chan of string): ref Image;
+	jpg2img : fn (filename, cachepath: string, chanin, chanout: chan of string): ref Image;
+};
\ No newline at end of file
--- /dev/null
+++ b/module/grid/regpoll.m
@@ -1,0 +1,6 @@
+RegPoll : module {
+	PATH: con "/usr/danny/res/regpoll.dis";
+
+	STOPPED, STARTED, ERROR: con iota;
+	init : fn (regaddr: string): (chan of int, chan of int);
+};
--- /dev/null
+++ b/module/grid/srvbrowse.m
@@ -1,0 +1,16 @@
+Srvbrowse: module
+{
+	PATH:	con "/dis/grid/lib/srvbrowse.dis";
+
+	services : list of ref Registries->Service;
+
+	init : fn ();
+	refreshservices : fn (filter: list of list of (string, string));
+	servicepath2Service : fn (path, qid: string): list of ref Registries->Service;
+	servicepath2Dir : fn (path: string, qid: int): (array of ref sys->Dir, int);
+	getresname : fn (srvc: ref Registries->Service): (string, string);
+	getqid : fn (srvc: ref Registries->Service): string;
+	find : fn (filter: list of list of (string, string)): list of ref Registries->Service;
+	addservice: fn (srvc: ref Registries->Service);
+	searchwin: fn (ctxt: ref Draw->Context, chanout: chan of string, filter: list of list of (string, string));
+};
\ No newline at end of file
--- /dev/null
+++ b/module/hash.m
@@ -1,0 +1,23 @@
+Hash: module{
+	PATH: con "/dis/lib/hash.dis";
+	fun1, fun2: fn(s:string,n:int):int;
+
+	HashVal: adt{
+		i: int;
+		r: real;
+		s: string;
+	};
+	HashNode: adt{
+		key:string;
+		val:ref HashVal;  # insert() can update contents
+	};
+	HashTable: adt{
+		a:	array of list of HashNode;
+		find:	fn(h:self ref HashTable, key:string):ref HashVal;
+		insert:	fn(h:self ref HashTable, key:string, val:HashVal);
+		delete:	fn(h:self ref HashTable, key:string);
+		all:	fn(h:self ref HashTable): list of HashNode;
+	};
+	new: fn(size:int):ref HashTable;
+};
+
--- /dev/null
+++ b/module/html.m
@@ -1,0 +1,41 @@
+HTML: module
+{
+	PATH:		con "/dis/lib/html.dis";
+
+	Lex: adt
+	{
+		tag:		int;
+		text:		string;	# text in Data, attribute text in tag
+		attr:		list of Attr;
+	};
+
+	Attr: adt
+	{
+		name:	string;
+		value:	string;
+	};
+
+	# sorted in lexical order; used as array indices
+	Notfound,
+	Ta, Taddress, Tapplet, Tarea, Tatt_footer, Tb,
+		Tbase, Tbasefont, Tbig, Tblink, Tblockquote, Tbody,
+		Tbq, Tbr, Tcaption, Tcenter, Tcite, Tcode, Tcol, Tcolgroup,
+		Tdd, Tdfn, Tdir, Tdiv, Tdl, Tdt, Tem,
+		Tfont, Tform, Tframe, Tframeset,
+		Th1, Th2, Th3, Th4, Th5, Th6, Thead, Thr, Thtml, Ti, Timg,
+		Tinput, Tisindex, Titem, Tkbd, Tli, Tlink, Tmap, Tmenu,
+		Tmeta, Tnobr, Tnoframes, Tol, Toption, Tp, Tparam, Tpre,
+		Tq, Tsamp, Tscript, Tselect, Tsmall, Tstrike, Tstrong,
+		Tstyle, Tsub, Tsup, Tt, Ttable, Ttbody, Ttd, Ttextarea, Ttextflow, Ttfoot, Tth,
+		Tthead, Ttitle, Ttr, Ttt, Tu, Tul, Tvar
+			: con iota;
+	RBRA: con 1000;
+	Data: con 2000;
+	Latin1, UTF8: con iota;	# charsets
+
+	lex:		fn(b: array of byte, charset: int, keepnls: int): array of ref Lex;
+	attrvalue:	fn(attr: list of Attr, name: string): (int, string);
+	globalattr:	fn(html: array of ref Lex, tag: int, attr: string): (int, string);
+	isbreak:	fn(h: array of ref Lex, i: int): int;
+	lex2string:	fn(l: ref Lex): string;
+};
--- /dev/null
+++ b/module/ida.m
@@ -1,0 +1,24 @@
+Ida: module
+{
+	PATH: con "/dis/lib/ida/ida.dis";
+
+	Frag: adt {
+		dlen:	int;	# length of original data
+		m:	int;	# minimum pieces for reconstruction
+		a:	array of int;	# encoding array row for this fragment
+		enc:	array of int;	# encoded data
+
+		tag:	array of byte;	# user data, such as SHA1 hash
+	};
+
+	init:	fn();
+	fragment:	fn(data: array of byte, m: int): ref Frag;
+	consistent:	fn(frags: array of ref Frag): array of ref Frag;
+	reconstruct:	fn(frags: array of ref Frag): (array of byte, string);
+};
+
+Idatab: module
+{
+	PATH: con "/dis/lib/ida/idatab.dis";
+	init:	fn(): array of int;
+};
--- /dev/null
+++ b/module/imagefile.m
@@ -1,0 +1,48 @@
+RImagefile: module 
+{
+	READGIFPATH:	con "/dis/lib/readgif.dis";
+	READJPGPATH:	con "/dis/lib/readjpg.dis";
+	READXBMPATH:	con "/dis/lib/readxbitmap.dis";
+	READPICPATH:	con "/dis/lib/readpicfile.dis";
+	READPNGPATH:	con "/dis/lib/readpng.dis";
+
+	Rawimage: adt
+	{
+		r:	Draw->Rect;
+		cmap:    array of byte;
+		transp:  int;	# transparency flag (only for nchans=1)
+		trindex: byte;	# transparency index
+		nchans:  int;
+		chans:   array of array of byte;
+		chandesc:int;
+
+		fields:	int;    # defined by format
+	};
+
+	# chandesc
+	CRGB:   con 0;  # three channels, no map
+	CY:     con 1;  # one channel, luminance
+	CRGB1:  con 2;  # one channel, map present
+
+	init:	fn(bufio: Bufio);
+	read:	fn(fd: ref Bufio->Iobuf): (ref Rawimage, string);
+	readmulti:	fn(fd: ref Bufio->Iobuf): (array of ref Rawimage, string);
+};
+
+WImagefile: module 
+{
+	WRITEGIFPATH:	con "/dis/lib/writegif.dis";
+
+	init:	fn(bufio: Bufio);
+#	write:	fn(fd: ref Bufio->Iobuf, ref RImagefile->Rawimage): string;
+	writeimage:	fn(fd: ref Bufio->Iobuf, image: ref Draw->Image): string;
+};
+
+
+Imageremap: module
+{
+	PATH:	con "/dis/lib/imageremap.dis";
+
+	init:	fn(d: ref Draw->Display);
+	remap:	fn(i: ref RImagefile->Rawimage, d: ref Draw->Display, errdiff: int): (ref Draw->Image, string);
+};
--- /dev/null
+++ b/module/inflate.m
@@ -1,0 +1,25 @@
+Inflate: module
+{
+	PATH:	con "/dis/lib/inflate.dis";
+
+	InflateBlock:	con 16r8000;
+	InflateMask:	con 16rf0000;
+
+	InflateEmptyIn,
+	InflateFlushOut,
+	InflateAck,
+	InflateDone,
+	InflateError:	con iota + (1 << 16) + 1;
+
+	# conduit for data streaming between inflate and its producer/consumer
+	InflateIO: adt
+	{
+		ibuf: array of byte;	# input buffer [InflateBlock]
+		obuf: array of byte;	# output buffer [InflateBlock]
+		c: chan of int;	# for inflate <-> server comm.
+	};
+	
+	init: fn();
+	reset: fn(): ref InflateIO;
+	inflate: fn();
+};
--- /dev/null
+++ b/module/ip.m
@@ -1,0 +1,104 @@
+IP: module
+{
+	PATH:	con "/dis/lib/ip.dis";
+
+	IPaddrlen:	con 16;
+	IPv4addrlen:	con 4;
+	IPv4off: con 12;
+
+	IPaddr: adt {
+		a:	array of byte;
+
+		newv6:	fn(nil: array of byte): IPaddr;
+		newv4:	fn(nil: array of byte): IPaddr;
+		copy:	fn(nil: self IPaddr): IPaddr;
+		eq:	fn(nil: self IPaddr, v: IPaddr): int;
+		mask:	fn(nil: self IPaddr, m: IPaddr): IPaddr;
+		maskn:	fn(nil: self IPaddr, m: IPaddr): IPaddr;
+		isv4:	fn(nil: self IPaddr): int;
+		ismulticast:	fn(nil: self IPaddr): int;
+		isvalid:	fn(nil: self IPaddr): int;
+
+		v4:	fn(nil: self IPaddr): array of byte;
+		v6:	fn(nil: self IPaddr): array of byte;
+		class:	fn(nil: self IPaddr): int;
+		classmask:	fn(nil: self IPaddr): IPaddr;
+
+		parse:	fn(s: string): (int, IPaddr);
+		parsemask:	fn(s: string): (int, IPaddr);
+		parsecidr:	fn(s: string): (int, IPaddr, IPaddr);
+
+		text:	fn(nil: self IPaddr): string;
+		masktext:	fn(nil: self IPaddr): string;
+	};
+
+	v4bcast, v4allsys, v4allrouter, v4noaddr, noaddr, allbits, selfv6, selfv4: IPaddr;
+	v4prefix: array of byte;
+
+	Ifcaddr: adt {
+		ip:	IPaddr;
+		mask:	IPaddr;
+		net:	IPaddr;
+		preflt:	big;
+		validlt:	big;
+	};
+
+	Ipifc: adt {
+		index:	int;	# /net/ipifc/N
+		dev:	string;	# bound device
+		addrs:	list of ref Ifcaddr;
+		sendra:	int;	# !=0, send router adverts
+		recvra:	int;	# !=0, receive router adverts
+		mtu:	int;
+		pktin:	big;	# packets in
+		pktout:	big;	# packets out
+		errin:	big;	# input errors
+		errout:	big;	# output errors
+		rp:	IPv6rp;	# IPv6 route advert params
+	};
+
+	IPv6rp: adt {
+		mflag:	int;
+		oflag:	int;
+		maxraint:	int;	# max route advert interval
+		minraint:	int;	# min route advert interval
+		linkmtu:	int;
+		reachtime:	int;
+		rxmitra:	int;
+		ttl:	int;
+		routerlt:	int;
+	};
+
+	Udp4hdrlen:	con 2*IPv4addrlen+2*2;
+	OUdphdrlen:	con 2*IPaddrlen+2*2;
+
+	Udphdrlen:	con 52;
+	Udpraddr:	con 0;
+	Udpladdr: con Udpraddr + IPaddrlen;
+	Udpifcaddr: con Udpladdr + IPaddrlen;
+	Udprport: con Udpifcaddr + IPaddrlen;
+	Udplport: con Udprport + 2;
+
+	Udphdr: adt {
+		raddr:	IPaddr;
+		laddr:	IPaddr;
+		ifcaddr:	IPaddr;
+		rport:	int;
+		lport:	int;
+
+		new:		fn(): ref Udphdr;
+		unpack:	fn(a: array of byte, n: int): ref Udphdr;
+		pack:	fn(h: self ref Udphdr, a: array of byte, n: int);
+	};
+
+	init:	fn();
+	readipifc:	fn(net: string, index: int): (list of ref Ipifc, string);
+	addressesof:	fn(l: list of ref Ipifc, all: int): list of IPaddr;
+	interfaceof:	fn(l: list of ref Ipifc, ip: IPaddr): (ref Ipifc, ref Ifcaddr);
+	ownerof:	fn(l: list of ref Ipifc, ip: IPaddr): (ref Ipifc, ref Ifcaddr);
+
+	get2:	fn(a: array of byte, o: int): int;
+	put2:	fn(a: array of byte, o: int, v: int): int;
+	get4:	fn(a: array of byte, o: int): int;
+	put4:	fn(a: array of byte, o: int, v: int): int;
+};
--- /dev/null
+++ b/module/ipattr.m
@@ -1,0 +1,20 @@
+IPattr: module
+{
+
+	PATH: con "/dis/lib/ipattr.dis";
+
+	Netattr: adt {
+		name:	string;
+		pairs:	list of ref Attrdb->Attr;
+		net:	IP->IPaddr;
+		mask:	IP->IPaddr;
+	};
+
+	init:	fn(attrdb: Attrdb, ip: IP);
+
+	dbattr:	fn(s: string): string;
+	findnetattr:	fn(db: ref Attrdb->Db, attr: string, val: string, rattr: string): (string, string);
+	findnetattrs:	fn(db: ref Attrdb->Db, attr: string, val: string, rattrs: list of string): (list of (IP->IPaddr, list of ref Netattr), string);
+	valueof:	fn(l: list of ref Netattr, attr: string): list of string;
+	netvalueof:	fn(l: list of ref Netattr, attr: string, ip: IP->IPaddr): list of string;
+};
--- /dev/null
+++ b/module/ipints.m
@@ -1,0 +1,57 @@
+IPints: module
+{
+	PATH:	con	"$IPints";
+
+	# infinite precision integers
+	IPint: adt
+	{
+		x:	int;	# dummy for C compiler for runt.h
+
+		# conversions
+		iptob64:	fn(i: self ref IPint): string;
+		iptob64z:	fn(i: self ref IPint): string;
+		b64toip:	fn(str: string): ref IPint;
+		iptobytes:	fn(i: self ref IPint): array of byte;
+		iptobebytes:	fn(i: self ref IPint): array of byte;
+		bytestoip:	fn(buf: array of byte): ref IPint;
+		bebytestoip:	fn(mag: array of byte): ref IPint;
+		inttoip:	fn(i: int): ref IPint;
+		iptoint:	fn(i: self ref IPint): int;
+		iptostr:	fn(i: self ref IPint, base: int): string;
+		strtoip:	fn(str: string, base: int): ref IPint;
+
+		# create a random large integer
+		random:		fn(nbits: int): ref IPint;
+
+		# operations
+		bits:		fn(i: self ref IPint): int;
+		expmod:	fn(base: self ref IPint, exp, mod: ref IPint): ref IPint;
+		invert:	fn(base: self ref IPint, mod: ref IPint): ref IPint;
+		add:		fn(i1: self ref IPint, i2: ref IPint): ref IPint;
+		sub:		fn(i1: self ref IPint, i2: ref IPint): ref IPint;
+		neg:		fn(i: self ref IPint): ref IPint;
+		mul:		fn(i1: self ref IPint, i2: ref IPint): ref IPint;
+		div:		fn(i1: self ref IPint, i2: ref IPint): (ref IPint, ref IPint);
+		mod:	fn(i1: self ref IPint, i2: ref IPint): ref IPint;
+		eq:		fn(i1: self ref IPint, i2: ref IPint): int;
+		cmp:		fn(i1: self ref IPint, i2: ref IPint): int;
+		copy:	fn(i: self ref IPint): ref IPint;
+
+		# shifts
+		shl:	fn(i: self ref IPint, n: int): ref IPint;
+		shr:	fn(i: self ref IPint, n: int): ref IPint;
+
+		# bitwise
+		and:	fn(i1: self ref IPint, i2: ref IPint): ref IPint;
+		ori:	fn(i1: self ref IPint, i2: ref IPint): ref IPint;
+		xor:	fn(i1: self ref IPint, i2: ref IPint): ref IPint;
+		not:	fn(i1: self ref IPint): ref IPint;
+	};
+
+	# primes
+	probably_prime:	fn(n: ref IPint, nrep: int): int;
+	genprime:	fn(nbits: int, nrep: int): ref IPint;
+	genstrongprime:	fn(nbits: int, nrep: int): ref IPint;
+	gensafeprime:	fn(nbits: int, nrep: int): (ref IPint, ref IPint);
+	DSAprimes:	fn(): (ref IPint, ref IPint, array of byte);
+};
--- /dev/null
+++ b/module/ir.m
@@ -1,0 +1,43 @@
+Ir: module
+{
+	PATH:	con	"/dis/lib/ir.dis";
+	SIMPATH:	con	"/dis/lib/irsim.dis";
+	MPATH:	con	"/dis/lib/irmpath.dis";
+	SAGEPATH:	con	"/dis/lib/irsage.dis";
+
+	#
+	# "standard" remote buttons
+	#
+	Zero:	con 0;
+	One:	con 1;
+	Two:	con 2;
+	Three:	con 3;
+	Four:	con 4;
+	Five:	con 5;
+	Six:	con 6;
+	Seven:	con 7;
+	Eight:	con 8;
+	Nine:	con 9;
+	ChanUP:	con 10;
+	ChanDN:	con 11;
+	VolUP:	con 12;
+	VolDN:	con 13;
+	FF:	con 14;
+	Rew:	con 15;
+	Up:	con 16;
+	Dn:	con 17;
+	Select:	con 18;
+	Power:	con 19;
+	Enter:	con 20;
+	Rcl:	con 21;
+	Record:	con 22;
+	Mute:	con 23;
+	#
+	# Control
+	#
+	Error:	con 9999;
+	EOF:	con -1;
+
+	init: 		fn(c, p: chan of int): int;
+	translate:	fn(c: int): int;
+};
--- /dev/null
+++ b/module/itslib.m
@@ -1,0 +1,24 @@
+
+Itslib: module {
+
+	PATH: con "/dis/lib/itslib.dis";
+
+	init: fn(): ref Tconfig;
+ 	S_INFO: con 0;
+	S_WARN: con 1;
+	S_ERROR: con 2;
+	S_FATAL: con 3;
+	S_STIME: con 4;
+	S_ETIME: con 5;
+	ENV_VERBOSITY: con "ITS_VERBOSITY";
+	ENV_MFD: con "ITS_MFD";
+
+
+	Tconfig: adt {
+		verbosity: int;
+		mfd: ref Sys->FD;
+		report: fn(t: self ref Tconfig, sev: int, verb: int, msg: string);
+		done: fn(t: self ref Tconfig);
+	};
+
+};
--- /dev/null
+++ b/module/json.m
@@ -1,0 +1,50 @@
+JSON: module
+{
+	PATH:	con "/dis/lib/json.dis";
+
+	JValue: adt {
+		pick{
+		Object =>
+			mem: cyclic list of (string, ref JValue);
+		Array =>
+			a: cyclic array of ref JValue;
+		String =>
+			s: string;
+		Int =>
+			value:	big;	# could use IPint?	# just use Number (as string)
+		Real =>
+			value:	real;
+		True or False or Null =>
+		}
+
+		isarray:	fn(o: self ref JValue): int;
+		isfalse:	fn(o: self ref JValue): int;
+		isint:		fn(o: self ref JValue): int;
+		isnull:	fn(o: self ref JValue): int;
+		isnumber: fn(o: self ref JValue): int;
+		isobject:	fn(o: self ref JValue): int;
+		isreal:	fn(o: self ref JValue): int;
+		isstring:	fn(o: self ref JValue): int;
+		istrue:	fn(o: self ref JValue): int;
+		copy:	fn(o: self ref JValue): ref JValue;
+		eq:	fn(a: self ref JValue, b: ref JValue): int;
+		get:	fn(a: self ref JValue, n: string): ref JValue;
+		set:	fn(a: self ref JValue, mem: string, value: ref JValue);
+		text:	fn(a: self ref JValue): string;
+	};
+
+	init:	fn(bufio: Bufio);
+	readjson:	fn(buf: ref Bufio->Iobuf): (ref JValue, string);
+	writejson:	fn(buf: ref Bufio->Iobuf, val: ref JValue): int;
+
+	# shorthand?
+	jvarray:	fn(a: array of ref JValue): ref JValue.Array;
+	jvbig:	fn(b: big): ref JValue.Int;
+	jvfalse:	fn(): ref JValue.False;
+	jvint:		fn(i: int): ref JValue.Int;
+	jvnull:	fn(): ref JValue.Null;
+	jvobject:	fn(m: list of (string, ref JValue)): ref JValue.Object;
+	jvreal:	fn(r: real): ref JValue.Real;
+	jvstring:	fn(s: string): ref JValue.String;
+	jvtrue:	fn(): ref JValue.True;
+};
--- /dev/null
+++ b/module/keyboard.m
@@ -1,0 +1,50 @@
+Keyboard : module {
+	# Inferno Generic Scan Conversions
+	# this file needs to be kept in sync with include/keyboard.h 
+
+	No: con -1;
+	Esc: con 16r1b;
+
+	Spec: con 16rE000;		# Special Function Keys - mapped to Unicode reserved range
+	Shift: con Spec|16r00;	# Shifter (Held) Keys 
+	View: con Spec|16r10;	# View Keys
+	PF: con	Spec|16r20;	# num pad
+	KF: con	Spec|16r40;	# function keys
+
+	LShift: con Shift|0;
+	RShift: con Shift|1;
+	LCtrl: con Shift|2;
+	RCtrl: con Shift|3;
+	Caps: con Shift|4;
+	Num: con Shift|5;
+	Meta: con Shift|6;
+	LAlt: con Shift|7;
+	RAlt: con Shift|8;
+	NShifts: con 9;			# total number of shift keys
+
+	Home: con View|0;
+	End: con View|1;
+	Up: con View|2;
+	Down: con View|3;
+	Left: con View|4;
+	Right: con View|5;
+	Pgup: con View|6;
+	Pgdown: con View|7;
+	BackTab: con View|8;
+
+	Scroll: con Spec|16r62;
+	Ins: con Spec|16r63;
+	Del: con Spec|16r64;
+	Print: con Spec|16r65;
+	Pause: con Spec|16r66;
+	Middle: con Spec|16r67;
+	Break: con Spec|16r66;
+	SysRq: con Spec|16r69;
+	PwrOn: con Spec|16r6c;
+	PwrOff: con Spec|16r6d;
+	PwrLow: con Spec|16r6e;
+	Latin: con Spec|16r6f;
+
+	APP: con Spec|16r200;	# for application use (ALT keys)
+};
+
--- /dev/null
+++ b/module/keyring.m
@@ -1,0 +1,335 @@
+#
+#  security routines implemented in C
+#
+Keyring: module
+{
+	PATH:	con	"$Keyring";
+
+	# infinite precision integers
+	IPint: adt
+	{
+		x:	int;	# dummy for C compiler for runt.h
+
+		# conversions
+		iptob64:	fn(i: self ref IPint): string;
+		iptob64z:	fn(i: self ref IPint): string;
+		b64toip:	fn(str: string): ref IPint;
+		iptobytes:	fn(i: self ref IPint): array of byte;
+		iptobebytes:	fn(i: self ref IPint): array of byte;
+		bytestoip:	fn(buf: array of byte): ref IPint;
+		bebytestoip:	fn(mag: array of byte): ref IPint;
+		inttoip:	fn(i: int): ref IPint;
+		iptoint:	fn(i: self ref IPint): int;
+		iptostr:	fn(i: self ref IPint, base: int): string;
+		strtoip:	fn(str: string, base: int): ref IPint;
+
+		# create a random large integer using the accelerated generator
+		random:		fn(minbits, maxbits: int): ref IPint;
+
+		# operations
+		bits:		fn(i: self ref IPint): int;
+		expmod:	fn(base: self ref IPint, exp, mod: ref IPint): ref IPint;
+		invert:	fn(base: self ref IPint, mod: ref IPint): ref IPint;
+		add:		fn(i1: self ref IPint, i2: ref IPint): ref IPint;
+		sub:		fn(i1: self ref IPint, i2: ref IPint): ref IPint;
+		neg:		fn(i: self ref IPint): ref IPint;
+		mul:		fn(i1: self ref IPint, i2: ref IPint): ref IPint;
+		div:		fn(i1: self ref IPint, i2: ref IPint): (ref IPint, ref IPint);
+		mod:	fn(i1: self ref IPint, i2: ref IPint): ref IPint;
+		eq:		fn(i1: self ref IPint, i2: ref IPint): int;
+		cmp:		fn(i1: self ref IPint, i2: ref IPint): int;
+		copy:	fn(i: self ref IPint): ref IPint;
+
+		# shifts
+		shl:	fn(i: self ref IPint, n: int): ref IPint;
+		shr:	fn(i: self ref IPint, n: int): ref IPint;
+
+		# bitwise
+		and:	fn(i1: self ref IPint, i2: ref IPint): ref IPint;
+		ori:	fn(i1: self ref IPint, i2: ref IPint): ref IPint;
+		xor:	fn(i1: self ref IPint, i2: ref IPint): ref IPint;
+		not:	fn(i1: self ref IPint): ref IPint;
+	};
+
+	# signature algorithm
+	SigAlg: adt
+	{
+		name:	string;
+		# C function pointers are hidden
+	};
+	
+	# generic public key
+	PK: adt
+	{
+		sa:	ref SigAlg;	# signature algorithm
+		owner:	string;		# owner's name
+		# key and system parameters are hidden
+	};
+	
+	# generic secret key
+	SK: adt
+	{
+		sa:	ref SigAlg;	# signature algorithm
+		owner:	string;		# owner's name
+		# key and system parameters are hidden
+	};
+
+	# generic certificate
+	Certificate: adt
+	{
+		sa:	ref SigAlg;	# signature algorithm
+		ha:	string;		# hash algorithm
+		signer:	string;		# name of signer
+		exp:	int;		# expiration date
+		# actual signature is hidden
+	};
+
+	# state held while creating digests
+	DigestState: adt
+	{
+		x:	int;		# dummy for C compiler for runt.h
+		# all the state is hidden
+
+		copy:	fn(d: self ref DigestState): ref DigestState;
+	};
+
+	# expanded AES key + state for chaining
+	AESstate: adt
+	{
+		x:	int;		# dummy for C compiler for runt.h
+		# all the state is hidden
+	};
+
+	# expanded DES key + state for chaining
+	DESstate: adt
+	{
+		x:	int;		# dummy for C compiler for runt.h
+		# all the state is hidden
+	};
+
+	# expanded IDEA key + state for chaining
+	IDEAstate: adt
+	{
+		x:	int;		# dummy for C compiler for runt.h
+		# all the state is hidden
+	};
+
+	# expanded RC4 key + encryption state
+	RC4state: adt
+	{
+		x:	int;		# dummy for C compiler for runt.h
+		# all the state is hidden
+	};
+
+	# expanded Blowfish key + state for chaining
+	BFstate: adt
+	{
+		x:	int;		# dummy for C compiler for runt.h
+		# all the state is hidden
+	};
+
+	# authentication info
+	Authinfo: adt
+	{
+		mysk:	ref SK;			# my private key
+		mypk:	ref PK;			# my public key
+		cert:	ref Certificate;	# signature of my public key
+		spk:	ref PK;			# signers public key
+		alpha:	ref IPint;		# diffie helman parameters
+		p:	ref IPint;
+	};
+
+	# convert types to byte strings
+	certtostr: fn (c: ref Certificate): string;
+	pktostr: fn (pk: ref PK): string;
+	sktostr: fn (sk: ref SK): string;
+
+	# parse byte strings into types
+	strtocert: fn (s: string): ref Certificate;
+	strtopk: fn (s: string): ref PK;
+	strtosk: fn (s: string): ref SK;
+
+	# convert types to attr/value pairs
+	certtoattr: fn (c: ref Certificate): string;
+	pktoattr: fn (pk: ref PK): string;
+	sktoattr: fn (sk: ref SK): string;
+
+	# parse a/v pairs into types
+#	attrtocert: fn (s: string): ref Certificate;
+#	attrtopk: fn (s: string): ref PK;
+#	attrtosk: fn (s: string): ref SK;
+
+	# create and verify signatures
+	sign: fn (sk: ref SK, exp: int, state: ref DigestState, ha: string):
+		ref Certificate;
+	verify: fn (pk: ref PK, cert: ref Certificate, state: ref DigestState):
+		int;
+	signm: fn (sk: ref SK, m: ref IPint, ha: string):
+		ref Certificate;
+	verifym: fn (pk: ref PK, cert: ref Certificate, m: ref IPint):
+		int;
+
+	# generate keys
+	genSK: fn (algname, owner: string, length: int): ref SK; 
+	genSKfromPK: fn (pk: ref PK, owner: string): ref SK;
+	sktopk: fn (sk: ref SK): ref PK;
+
+	# digests
+	md4: fn(buf: array of byte, n: int, digest: array of byte, state: ref DigestState):
+		ref DigestState;
+	md5: fn(buf: array of byte, n: int, digest: array of byte, state: ref DigestState):
+		ref DigestState;
+	sha1: fn(buf: array of byte, n: int, digest: array of byte, state: ref DigestState):
+		ref DigestState;
+	sha224: fn(buf: array of byte, n: int, digest: array of byte, state: ref DigestState):
+		ref DigestState;
+	sha256: fn(buf: array of byte, n: int, digest: array of byte, state: ref DigestState):
+		ref DigestState;
+	sha384: fn(buf: array of byte, n: int, digest: array of byte, state: ref DigestState):
+		ref DigestState;
+	sha512: fn(buf: array of byte, n: int, digest: array of byte, state: ref DigestState):
+		ref DigestState;
+
+	hmac_sha1: fn(data: array of byte, n: int, key: array of byte, digest: array of byte, state: ref DigestState):
+		ref DigestState;
+	hmac_md5: fn(data: array of byte, n: int, key: array of byte, digest: array of byte, state: ref DigestState):
+		ref DigestState;
+
+	SHA1dlen:	con 20;
+	SHA224dlen:	con 28;
+	SHA256dlen:	con 32;
+	SHA384dlen:	con 48;
+	SHA512dlen:	con 64;
+	MD5dlen:	con 16;
+	MD4dlen:	con 16;
+
+	# encryption interfaces
+	Encrypt:	con 0;
+	Decrypt:	con 1;
+
+	AESbsize:	con 16;
+
+	aessetup: fn(key: array of byte, ivec: array of byte): ref AESstate;
+	aescbc: fn(state: ref AESstate, buf: array of byte, n: int, direction: int);
+
+	DESbsize: con 8;
+
+	dessetup: fn(key: array of byte, ivec: array of byte): ref DESstate;
+	desecb: fn(state: ref DESstate, buf: array of byte, n: int, direction: int);
+	descbc: fn(state: ref DESstate, buf: array of byte, n: int, direction: int);
+
+	IDEAbsize: con 8;
+
+	ideasetup: fn(key: array of byte, ivec: array of byte): ref IDEAstate;
+	ideaecb: fn(state: ref IDEAstate, buf: array of byte, n: int, direction: int);
+	ideacbc: fn(state: ref IDEAstate, buf: array of byte, n: int, direction: int);
+
+	BFbsize: con 8;
+
+	blowfishsetup: fn(key: array of byte, ivec: array of byte): ref BFstate;
+#	blowfishecb: fn(state: ref BFstate, buf: array of byte, n: int, direction: int);
+	blowfishcbc: fn(state: ref BFstate, buf: array of byte, n: int, direction: int);
+
+	rc4setup:	fn(seed: array of byte): ref RC4state;
+	rc4:	fn(state: ref RC4state, buf: array of byte, n: int);
+	rc4skip:	fn(state: ref RC4state, n: int);
+	rc4back:	fn(state: ref RC4state, n: int);
+
+	# create an alpha and p for diffie helman exchanges
+	dhparams: fn(nbits: int): (ref IPint, ref IPint);
+
+	# comm link authentication is symmetric
+	auth: fn(fd: ref Sys->FD, info: ref Authinfo, setid: int): (string, array of byte);
+
+	# auth io
+	readauthinfo: fn(filename: string): ref Authinfo;
+	writeauthinfo: fn(filename: string, info: ref Authinfo): int;
+
+	# message io on a delimited connection (ssl for example)
+	#  messages > 4096 bytes are truncated
+	#  errors > 64 bytes are truncated
+	# getstring and getbytearray return (result, error).
+	getstring: fn(fd: ref Sys->FD): (string, string);
+	putstring: fn(fd: ref Sys->FD, s: string): int;
+	getbytearray: fn(fd: ref Sys->FD): (array of byte, string);
+	putbytearray: fn(fd: ref Sys->FD, a: array of byte, n: int): int;
+	puterror: fn(fd: ref Sys->FD, s: string): int;
+
+	# to send and receive messages when ssl isn't pushed
+	getmsg: fn(fd: ref Sys->FD): array of byte;
+	sendmsg: fn(fd: ref Sys->FD, buf: array of byte, n: int): int;
+	senderrmsg: fn(fd: ref Sys->FD, s: string): int;
+
+	RSApk: adt {
+		n:	ref IPint;		# modulus
+		ek:	ref IPint;		# exp (encryption key)
+
+		encrypt:	fn(k: self ref RSApk, m: ref IPint): ref IPint;
+		verify:	fn(k: self ref RSApk, sig: ref RSAsig, m: ref IPint): int;
+	};
+
+	RSAsk: adt {
+		pk:	ref RSApk;
+		dk:	ref IPint;		# exp (decryption key)
+		p:	ref IPint;		# q in pkcs
+		q:	ref IPint;		# p in pkcs
+
+		# precomputed crt values
+		kp:	ref IPint;		# k mod p-1
+		kq:	ref IPint;		# k mod q-1
+		c2:	ref IPint;		# for converting residues to number
+
+		gen:	fn(nlen: int, elen: int, nrep: int): ref RSAsk;
+		fill:	fn(n: ref IPint, e: ref IPint, d: ref IPint, p: ref IPint, q: ref IPint): ref RSAsk;
+		decrypt:	fn(k: self ref RSAsk, m: ref IPint): ref IPint;
+		sign:	fn(k: self ref RSAsk, m: ref IPint): ref RSAsig;
+	};
+
+	RSAsig: adt {
+		n:	ref IPint;
+	};
+
+	DSApk: adt {
+		p:	ref IPint;	# modulus
+		q:	ref IPint;	# group order, q divides p-1
+		alpha: ref IPint;	# group generator
+		key:	ref IPint;	# encryption key (alpha**secret mod p)
+
+		verify:	fn(k: self ref DSApk, sig: ref DSAsig, m: ref IPint): int;
+	};
+
+	DSAsk: adt {
+		pk:	ref DSApk;
+		secret:	ref IPint;	# decryption key
+
+		gen:	fn(oldpk: ref DSApk): ref DSAsk;
+		sign:	fn(k: self ref DSAsk, m: ref IPint): ref DSAsig;
+	};
+
+	DSAsig: adt {
+		r:	ref IPint;
+		s:	ref IPint;
+	};
+
+	EGpk: adt {
+		p:	ref IPint;		# modulus
+		alpha: ref IPint;		# generator
+		key:	ref IPint;		# encryption key (alpha**secret mod p)
+
+		verify:	fn(k: self ref EGpk, sig: ref EGsig, m: ref IPint): int;
+	};
+
+	EGsk: adt {
+		pk:	ref EGpk;
+		secret:	ref IPint;	# decryption key
+
+		gen:	fn(nlen: int, nrep: int): ref EGsk;
+		sign:	fn(k: self ref EGsk, m: ref IPint): ref EGsig;
+	};
+
+	EGsig: adt {
+		r:	ref IPint;
+		s:	ref IPint;
+	};
+
+};
--- /dev/null
+++ b/module/keyset.m
@@ -1,0 +1,8 @@
+Keyset: module
+{
+	PATH:	con "/dis/lib/keyset.dis";
+
+	init:	fn(): string;
+	pkhash:	fn(pk: string): string;
+	keysforsigner:	fn(signername: string, spk: string, user: string, dir: string): (list of (string, string, string), string);
+};
--- /dev/null
+++ b/module/libc.m
@@ -1,0 +1,30 @@
+Libc: module
+{
+	PATH: con "/dis/lib/libc.dis";
+
+	isalnum: fn(c: int): int;
+	isalpha: fn(c: int): int;
+	isascii: fn(c: int): int;
+	iscntrl: fn(c: int): int;
+	isdigit: fn(c: int): int;
+	isgraph: fn(c: int): int;
+	islower: fn(c: int): int;
+	isprint: fn(c: int): int;
+	ispunct: fn(c: int): int;
+	isspace: fn(c: int): int;
+	isupper: fn(c: int): int;
+	isxdigit: fn(c: int): int;
+
+	tolower: fn(c: int): int;
+	toupper: fn(c: int): int;
+	toascii: fn(c: int): int;
+
+	strchr: fn(s: string, n: int): int;
+	strrchr: fn(s: string, n: int): int;
+	strncmp: fn(s1: string, s2: string, n: int): int;
+
+	abs: fn(n: int): int;
+	min: fn(m: int, n: int): int;
+	max: fn(m: int, n: int): int;
+
+};
--- /dev/null
+++ b/module/libc0.m
@@ -1,0 +1,40 @@
+Libc0: module
+{
+	PATH: con "/dis/lib/libc0.dis";
+
+	isalnum: fn(c: int): int;
+	isalpha: fn(c: int): int;
+	isascii: fn(c: int): int;
+	iscntrl: fn(c: int): int;
+	isdigit: fn(c: int): int;
+	isgraph: fn(c: int): int;
+	islower: fn(c: int): int;
+	isprint: fn(c: int): int;
+	ispunct: fn(c: int): int;
+	isspace: fn(c: int): int;
+	isupper: fn(c: int): int;
+	isxdigit: fn(c: int): int;
+
+	tolower: fn(c: int): int;
+	toupper: fn(c: int): int;
+	toascii: fn(c: int): int;
+
+	strlen: fn(s: array of byte): int;
+	strcmp: fn(s1: array of byte, s2: array of byte): int;
+	strcpy: fn(s1: array of byte, s2: array of byte): array of byte;
+	strcat: fn(s1: array of byte, s2: array of byte): array of byte;
+	strncmp: fn(s1: array of byte, s2: array of byte, n: int): int;
+	strncpy: fn(s1: array of byte, s2: array of byte, n: int): array of byte;
+	strncat: fn(s1: array of byte, s2: array of byte, n: int): array of byte;
+	strdup: fn(s: array of byte): array of byte;
+	strchr: fn(s: array of byte, n: int): array of byte;
+	strrchr: fn(s: array of byte, n: int): array of byte;
+
+	abs: fn(n: int): int;
+	min: fn(m: int, n: int): int;
+	max: fn(m: int, n: int): int;
+
+	ls2aab: fn(argl: list of string): array of array of byte;
+	s2ab: fn(s: string): array of byte;
+	ab2s: fn(a: array of byte): string;
+};
--- /dev/null
+++ b/module/linalg.m
@@ -1,0 +1,24 @@
+# The convention used here for storing matrices is the same commonly
+# used for scientific programming in C, namely linearizing in Fortran order.
+# Let A be an m by n matrix.  We represent this by
+#	a: array of real;
+#	m, n, lda: int;
+# where the variable lda ("leading dimension of a") is used so that a
+# succession of matrix problems of varying sizes can be created without
+# wholesale copying of data.  The element of A in the i-th row and j-th column
+# is stored in a[i+lda*j], where 0<=i<m and 0<=j<n.  This 0-origin indexing
+# is used everywhere, and in particular in permutation vectors.
+
+LinAlg: module{
+	PATH:	con "/dis/math/linalg.dis";
+
+	Vector: type array of real;
+	Matrix: adt{
+		m, L, n: int;     # rows, column stride, columns
+		a: Vector; # data, stored A[i,j] = a[i+L*j]
+	};
+
+	dgefa:	fn(a:array of real, lda, n:int, ipvt:array of int): int;
+	dgesl:	fn(a:array of real, lda, n:int, ipvt:array of int, b:array of real, job:int);
+	printmat: fn(label:string, a:array of real, lda, m, n:int);
+};
--- /dev/null
+++ b/module/lists.m
@@ -1,0 +1,27 @@
+Lists: module
+{
+	PATH:	con "/dis/lib/lists.dis";
+	map:	fn[T](f: ref fn(x: T): T, l: list of T): list of T;
+	allsat:	fn[T](p: ref fn(x: T): int, l: list of T): int;
+	anysat:	fn[T](p: ref fn(x: T): int, l: list of T): int;
+	filter:	fn[T](p: ref fn(x: T): int, l: list of T): list of T;
+	partition:	fn[T](p: ref fn(x: T): int, l: list of T): (list of T, list of T);
+
+	append:	fn[T](l: list of T, x: T): list of T;
+	concat:	fn[T](l: list of T, l2: list of T): list of T;
+	combine:	fn[T](l: list of T, l2: list of T): list of T;
+	reverse:	fn[T](l: list of T): list of T;
+	last:		fn[T](l: list of T): T;
+	find:		fn[T](x: T, l: list of T): list of T
+		for { T => eq:	fn(a, b: T): int; };
+	delete:	fn[T](x: T, l: list of T): list of T
+		for { T => eq:	fn(a, b: T): int; };
+	pair:	fn[T1, T2](l1: list of T1, l2: list of T2): list of (T1, T2);
+	unpair:	fn[T1, T2](l: list of (T1, T2)): (list of T1, list of T2);
+	ismember:	fn[T](x: T, l: list of T): int
+		for { T =>	eq:	fn(a, b: T): int; };
+};
+
+#sort?
+#join
+#split
--- /dev/null
+++ b/module/loader.m
@@ -1,0 +1,48 @@
+#
+# External loader interface
+#
+Nilmod: module
+{
+};
+
+Loader: module
+{
+	PATH:	con	"$Loader";
+
+	Inst: adt
+	{
+		op:	byte;
+		addr:	byte;
+		src:	int;
+		mid:	int;
+		dst:	int;
+	};
+
+	Typedesc: adt
+	{
+		size:	int;
+		map:	array of byte;
+	};
+
+	Link: adt
+	{
+		name:	string;
+		sig:	int;
+		pc:	int;
+		tdesc:	int;
+	};
+
+	Niladt: adt
+	{
+	};
+
+	ifetch:		fn(mp: Nilmod): array of Inst;
+	tdesc:		fn(mp: Nilmod): array of Typedesc;
+	newmod:		fn(name: string, ss, nlink: int,
+				inst: array of Inst, data: ref Niladt): Nilmod;
+	tnew:		fn(mp: Nilmod, size: int, map: array of byte): int;
+	link:		fn(mp: Nilmod): array of Link;
+	ext:		fn(mp: Nilmod, idx, pc: int, tdesc: int): int;
+	dnew:		fn(size: int, map: array of byte): ref Niladt;
+	compile:	fn(mp: Nilmod, flag: int): int;
+};
--- /dev/null
+++ b/module/lock.m
@@ -1,0 +1,13 @@
+Lock: module
+{
+	PATH:	con "/dis/lib/lock.dis";
+
+	Semaphore: adt {
+		c: chan of int;
+		obtain:	fn(nil: self ref Semaphore);
+		release: fn(nil: self ref Semaphore);
+		new: fn(): ref Semaphore;
+	};
+	
+	init: fn();
+};
--- /dev/null
+++ b/module/man.m
@@ -1,0 +1,41 @@
+Parseman: module {
+	PATH: con "/dis/lib/parseman.dis";
+
+	Metrics: adt {
+		pagew: int;
+		dpi: int;
+		em: int;	# size in dots
+		en: int;	# size in dots
+		V: int;	# font height in dots
+		indent: int;
+		ssindent: int;
+	};
+
+	Text: adt {
+		font: int;
+		attr: int;
+		text: string;
+		heading: int;	# heading level
+		link: string;
+	};
+
+	# Text fonts and attributes
+	FONT_ROMAN,
+	FONT_ITALIC,
+	FONT_BOLD: con iota;
+	ATTR_SMALL, ATTR_LAST: con 1 << iota;
+
+	init: fn(): string;
+	parseman: fn[T](fd: ref Sys->FD, metrics: Metrics, ql: int, t: T, setline: chan of list of (int, Text))
+		for{
+		T =>
+			textwidth: fn(t: self T, text: Text): int;
+		};
+};
+
+Man: module {
+	PATH: con "/dis/lib/man.dis";
+
+	loadsections: fn(sections: list of string): string;
+	getfiles: fn(sections: list of string , keys: list of string): list of (int, string, string);
+};
--- /dev/null
+++ b/module/math.m
@@ -1,0 +1,91 @@
+Math: module
+{
+	PATH:	con	"$Math";
+
+	Infinity:	con 1e400;
+	NaN:		con 0./0.;
+	MachEps:	con 2.2204460492503131e-16;
+	Pi:		con 3.14159265358979323846;
+	Degree:		con Pi/180.;
+	INVAL:		con (1<<0);
+	ZDIV:		con (1<<1);
+	OVFL:		con (1<<2);
+	UNFL:		con (1<<3);
+	INEX:		con (1<<4);
+	RND_NR:		con (0<<8);
+	RND_NINF:	con (1<<8);
+	RND_PINF:	con (2<<8);
+	RND_Z:		con (3<<8);
+	RND_MASK:	con (3<<8);
+	acos:		fn(x: real): real;	# arccos(x) in [0,pi]
+	acosh:		fn(x: real): real;
+	asin:		fn(x: real): real;	# arcsin(x) in [-pi/2,pi/2]
+	asinh:		fn(x: real): real;
+	atan:		fn(x: real): real;	# arctan(x) in [-pi/2,pi/2]
+	atan2:		fn(y, x: real): real;	# arctan(y/x) in [-pi,pi]
+	atanh:		fn(x: real): real;
+	cbrt:		fn(x: real): real;
+	ceil:		fn(x: real): real;
+	copysign:	fn(x, s: real): real;
+	cos:		fn(x: real): real;
+	cosh:		fn(x: real): real;
+	dot:		fn(x, y: array of real): real;
+	erf:		fn(x: real): real;
+	erfc:		fn(x: real): real;
+	exp:		fn(x: real): real;
+	expm1:		fn(x: real): real;
+	fabs:		fn(x: real): real;
+	fdim, fmin, fmax: fn(x, y: real): real;
+	finite:		fn(x: real): int;
+	floor:		fn(x: real): real;
+	fmod:		fn(x, y: real): real;
+	gemm:		fn(transa, transb: int,  # upper case N or T
+			m, n, k: int, alpha: real,
+			a: array of real, lda: int,
+			b: array of real, ldb: int, beta: real,
+			c: array of real, ldc: int);
+	getFPcontrol, getFPstatus: fn(): int;
+	FPcontrol, FPstatus: fn(r, mask: int): int;
+	hypot:		fn(x, y: real): real;
+	iamax:		fn(x: array of real): int;
+	ilogb:		fn(x: real): int;
+	isnan:		fn(x: real): int;
+	j0:		fn(x: real): real;
+	j1:		fn(x: real): real;
+	jn:		fn(n: int, x: real): real;
+	lgamma:		fn(x: real): (int,real);
+	log:		fn(x: real): real;
+	log10:		fn(x: real): real;
+	log1p:		fn(x: real): real;
+	modf:		fn(x: real): (int,real);
+	nextafter:	fn(x, y: real): real;
+	norm1, norm2:	fn(x: array of real): real;
+	pow:		fn(x, y: real): real;
+	pow10:		fn(p: int): real;
+	remainder:	fn(x, p: real): real;
+	rint:		fn(x: real): real;
+	scalbn:		fn(x: real, n: int): real;
+	sin:		fn(x: real): real;
+	sinh:		fn(x: real): real;
+	sort:		fn(x: array of real, pi: array of int);
+	sqrt:		fn(x: real): real;
+	tan:		fn(x: real): real;
+	tanh:		fn(x: real): real;
+	y0:		fn(x: real): real;
+	y1:		fn(x: real): real;
+	yn:		fn(n: int, x: real): real;
+
+
+	import_int:	fn(b: array of byte, x: array of int);
+	import_real32:	fn(b: array of byte, x: array of real);
+	import_real:	fn(b: array of byte, x: array of real);
+	export_int:	fn(b: array of byte, x: array of int);
+	export_real32:	fn(b: array of byte, x: array of real);
+	export_real:	fn(b: array of byte, x: array of real);
+
+	# undocumented, of specialized interest only    DEPRECATED
+	bits32real:	fn(b: int): real; # IEEE 32-bit format to real
+	bits64real:	fn(b: big): real; # IEEE 64-bit format to real
+	realbits32:	fn(x: real): int; # real to IEEE 32-bit format
+	realbits64:	fn(x: real): big; # real to IEEE 64-bit format
+};
--- /dev/null
+++ b/module/math/geodesy.m
@@ -1,0 +1,58 @@
+Geodesy: module
+{
+	PATH: con "/dis/math/geodesy.dis";
+
+	# easting, northing in metres
+	Eano: adt{
+		e: real;
+		n: real;
+	};
+
+	# latitude, longitude in radians
+	Lalo: adt{
+		la: real;
+		lo: real;
+	};
+
+	# datums
+	# WGS84 and ITRS2000 effectively the same
+	OSGB36, Ireland65, ED50, WGS84, ITRS2000, ETRS89: con iota;
+
+	# transverse Mercator projections
+	Natgrid, IrishNatgrid, UTMEur, UTM: con iota;
+
+	# call first
+	# d specifies the datum (default WGS84)
+	# t specifies the transverse Mercator projection (default Natgrid)
+	# z specifies the UTM zone if relevant (default 30)
+	# calls format below
+	init: fn(d: int, t: int, z: int);
+
+	# alters the current datum, transverse Mercator projection and UTM zone
+	# use a negative value to leave unaltered
+	format: fn(d: int, t: int, z: int);
+
+	# OS string to (easting, northing) and back
+	# formats XYen, XYeenn, XYeeennn, XYeeeennnn, XYeeeeennnnn or
+	# formats eenn, eeennn, eeeennnn, eeeeennnnn, eeeeeennnnnn
+	os2en: fn(s: string): (int, Eano);	# returns (0, ...) if bad string format
+	en2os: fn(en: Eano): string;
+
+	# latitude/longitude string to (latitude, longitude) and back
+	# format latitude longitude
+	# formats deg[N|S], deg:min[N|S], deg:min:sec[N|S] for latitude
+	# formats deg[E|W], deg:min[E|W], deg:min:sec[E|W] for longitude
+	str2lalo: fn(s: string): (int, Lalo);	# returns (0, ...) if bad string format
+	lalo2str: fn(lalo: Lalo): string;
+
+	# general string to (easting, northing)
+	# OS grid or latitude/longitude format as above
+	str2en: fn(s: string): (int, Eano);	# returns (0, ...) if bad string format
+
+	# (easting, northing) to (latitude, longitude) and back
+	en2lalo: fn(en: Eano): Lalo;
+	lalo2en: fn(lalo: Lalo): Eano;
+
+	# approximate transformations between any of OSGB36, WGS84, ITRS2000, ETRS89
+	datum2datum: fn(lalo: Lalo, f: int, t: int): Lalo;
+};
--- /dev/null
+++ b/module/math/polyfill.m
@@ -1,0 +1,18 @@
+Polyfill: module
+{
+	PATH: con "/dis/math/polyfill.dis";
+
+	Zstate: adt{
+		r: Draw->Rect;
+		zbuf0, zbuf1: array of int;
+		xlen: int;
+		ylen: int;
+		xylen: int;
+	};
+
+	init: fn();
+	initzbuf: fn(r: Draw->Rect): ref Zstate;
+	clearzbuf: fn(s: ref Zstate);
+	setzbuf: fn(s: ref Zstate, zd: int);
+	fillpoly: fn(d: ref Image, v: array of Point, w: int, s: ref Image, p: Point, zstate: ref Zstate, dc, dx, dy: int);
+};
\ No newline at end of file
--- /dev/null
+++ b/module/math/polyhedra.m
@@ -1,0 +1,25 @@
+Polyhedra: module
+{
+	PATH: con "/dis/math/polyhedra.dis";
+
+	Vector: adt{
+		x, y, z: real;
+	};
+
+	Polyhedron: adt{
+		name, dname: string;
+		indx, V, E, F, concave, anti, allf, adj: int;
+		v, f: array of Vector;
+		fv, vf: array of array of int;
+		offset: big;
+		prv, nxt: cyclic ref Polyhedron;
+		inc: real;
+	};
+
+	# read in details of all polyhedra in the given file
+	scanpolyhedra: fn(f: string): (int, ref Polyhedron, ref Bufio->Iobuf);
+	# read in the coordinates of all polyhedra
+	getpolyhedra: fn(p: ref Polyhedron, b: ref Bufio->Iobuf);
+	# read in the coordinates of the given polyhedron
+	getpolyhedron: fn(p: ref Polyhedron, b: ref Bufio->Iobuf);
+};
--- /dev/null
+++ b/module/memfs.m
@@ -1,0 +1,5 @@
+MemFS : module {
+	PATH : con "/dis/lib/memfs.dis";
+	init : fn() : string;
+	newfs : fn (maxsz : int) : ref Sys->FD;
+};
--- /dev/null
+++ b/module/mpeg.m
@@ -1,0 +1,13 @@
+#
+# This module has a primitive interface to the
+# mpeg device driver
+#
+Mpeg: module
+{
+	PATH:		con "/dis/lib/mpeg.dis";
+
+	play:		fn(d: ref Draw->Display, w: ref Image, dopaint: int,
+			r: Draw->Rect, file: string, notify: chan of string): string;
+	ctl:		fn(msg: string): int;
+	keycolor:	fn(d: ref Draw->Display): ref Draw->Image;	
+};
--- /dev/null
+++ b/module/msgio.m
@@ -1,0 +1,23 @@
+Msgio: module
+{
+	PATH:	con "/dis/lib/msgio.dis";
+
+	init:	fn();
+
+	Maxmsg: con 4096;
+
+	# message io on a delimited connection (ssl for example)
+	#  messages >= Maxmsg bytes are truncated
+	#  errors > 64 bytes are truncated
+	# getstring and getbytearray return (result, error).
+	getstring: fn(fd: ref Sys->FD): (string, string);
+	putstring: fn(fd: ref Sys->FD, s: string): int;
+	getbytearray: fn(fd: ref Sys->FD): (array of byte, string);
+	putbytearray: fn(fd: ref Sys->FD, a: array of byte, n: int): int;
+	puterror: fn(fd: ref Sys->FD, s: string): int;
+
+	# to send and receive messages when ssl isn't pushed
+	getmsg: fn(fd: ref Sys->FD): array of byte;
+	sendmsg: fn(fd: ref Sys->FD, buf: array of byte, n: int): int;
+	senderrmsg: fn(fd: ref Sys->FD, s: string): int;
+};
--- /dev/null
+++ b/module/multistyx.m
@@ -1,0 +1,8 @@
+Multistyx: module {
+	PATH: con "/dis/lib/multistyx.dis";
+	init: fn(): Styxlib;
+	srv: fn(addr, mntpath: string, doauth: int, algs: list of string):
+			(chan of (int, ref Styxlib->Styxserver, string),
+			chan of (int, ref Styxlib->Styxserver, ref Styxlib->Tmsg),
+			string);
+};
--- /dev/null
+++ b/module/muxclient.m
@@ -1,0 +1,23 @@
+Muxclient: module
+{
+	# From appl to mux
+	AMexit:		con 10;		# application is exiting
+	AMstartir:	con 11;		# application is ready to receive IR events
+	AMstartkbd:	con 12;		# application is ready to receive keyboard characters
+	AMstartptr:	con 13;		# application is ready to receive mouse events
+	AMnewpin:	con 14;		# application needs a PIN
+
+	# From mux to appl
+	MAtop:		con 20;		# application should make all its windows visible
+
+	Context: adt
+	{
+		screen: 	ref Screen;		# place to make windows
+		display: 	ref Display;		# frame buffer on which windows reside
+		cir: 		chan of int;		# incoming events from IR remote
+		ckbd: 		chan of int;		# incoming characters from keyboard
+		cptr: 		chan of ref Pointer;	# incoming stream of mouse positions
+		ctoappl:	chan of int;		# commands from mux to application
+		ctomux:		chan of int;		# commands from application to mux
+	};
+};
--- /dev/null
+++ b/module/names.m
@@ -1,0 +1,13 @@
+Names: module
+{
+	PATH:	con "/dis/lib/names.dis";
+
+	cleanname:	fn(name: string): string;
+	dirname:	fn(name: string): string;
+	basename:	fn(name: string, suffix: string): string;
+	elements:	fn(name: string): list of string;
+	isprefix:	fn(a: string, b: string): int;
+	pathname:	fn(els: list of string): string;
+	rooted:	fn(root: string, name: string): string;
+	relative:	fn(name: string, root: string): string;
+};
--- /dev/null
+++ b/module/newns.m
@@ -1,0 +1,7 @@
+Newns: module
+{
+	PATH:	con "/dis/lib/newns.dis";
+
+	newns:	fn(user: string, nsfile: string): string;
+	newuser:	fn(user: string, cap: string, nsfile: string): string;
+};
--- /dev/null
+++ b/module/oldauth.m
@@ -1,0 +1,48 @@
+Oldauth: module
+{
+	PATH:	con "/dis/lib/oldauth.dis";
+
+	init:	fn();
+
+	# Inferno certificate
+	Certificate: adt
+	{
+		sa:	string;	# signature algorithm
+		ha:	string;		# hash algorithm
+		signer:	string;	# name of signer
+		exp:	int;		# expiration date
+		sig:	ref Crypt->PKsig;
+	};
+
+	# authentication info
+	Authinfo: adt
+	{
+		mysk:	ref Crypt->SK;			# my private key
+		mypk:	ref Crypt->PK;			# my public key
+		owner:	string;	# owner of mypk for certificate
+		cert:	ref Certificate;	# signature of my public key
+		spk:	ref Crypt->PK;			# signers public key
+		alpha:	ref IPints->IPint;		# diffie helman parameters
+		p:	ref IPints->IPint;
+	};
+
+	# auth io
+	readauthinfo: fn(filename: string): ref Authinfo;
+	writeauthinfo: fn(filename: string, info: ref Authinfo): int;
+
+	# convert types to text in a canonical form
+	certtostr: fn (c: ref Certificate): string;
+	pktostr: fn (pk: ref Crypt->PK, owner: string): string;
+	sktostr: fn (sk: ref Crypt->SK, owner: string): string;
+
+	# parse text into types
+	strtocert: fn (s: string): ref Certificate;
+	strtopk: fn (s: string): (ref Crypt->PK, string);
+	strtosk: fn (s: string): (ref Crypt->SK, string);
+
+	# create and verify Certificates
+	sign: fn (sk: ref Crypt->SK, signer: string, exp: int, state: ref Crypt->DigestState, ha: string):
+		ref Certificate;
+	verify: fn (pk: ref Crypt->PK, cert: ref Certificate, state: ref Crypt->DigestState):
+		int;
+};
--- /dev/null
+++ b/module/palm.m
@@ -1,0 +1,213 @@
+Palm: module {
+
+	#
+	# basic Palm data types
+	#
+
+	PATH:	con "/dis/lib/palm.dis";
+
+	DBInfo: adt {
+		name:	string;
+		attr:		int;
+		dtype:	string;	# database type (byte[4])
+		version:	int;	# defined by application
+		creator:	string;	# creating application (byte[4])
+		ctime:	int;
+		mtime:	int;
+		btime:	int;	# last backup
+		modno:	int;	# modification number: set to zero
+		uidseed:	int;	# unique record ID seed (unused, set to zero)
+
+		# the following is used by the database access protocol
+		index:	int;
+
+		new:		fn(name: string, attr: int, dtype: string, version: int, creator: string): ref DBInfo;
+	};
+
+	# file attributes:
+
+	Fresource:	con 1<<0;		# file is .prc not .pdb
+	Fronly:		con 1<<1;		# read only
+	Fappinfodirty:	con 1<<2;
+	Fbackup:		con 1<<3;		# no conduit exists
+	Foverwrite:	con 1<<4;		# overwrite older copy if present
+	Freset:		con 1<<5;		# reset after installation
+	Fprivate:		con 1<<6;		# don't allow copy of this to be beamed
+	Fstream:		con 1<<7;		# file is an array of bytes, not a database
+
+	# extended (misc) attributes for Desklink->ReadDBList
+	Fnosync:		con (1<<7)<<16;
+	Frambased:	con (1<<6)<<16;
+
+	Noindex:		con 16rFFFF;	# unknown index
+
+	Record: adt {
+		id:	int;	# unique record ID (24 bits)
+		attr:	int;	# record attributes
+		cat:	int;	# category
+		data:	array of byte;
+
+		new:	fn(id: int, attr: int, cat: int, size: int): ref Record;
+	};
+
+	# Record.attr values:
+
+	Rdelete:	con 16r80; # delete next sync
+	Rdirty:	con 16r40; # record modified
+	Rinuse:	con 16r20; # record in use
+	Rsecret:	con 16r10; # record is secret
+	Rarchive:	con 16r08; # archive next sync
+	Rmcat:	con 16r0F; # mask for category field in Palmdb->Entry.attrs
+
+	Resource: adt {
+		name:	int;	# byte[4]: resource name or type
+		id:	int;	# resource ID (16 bits)
+		data:	array of byte;
+
+		new:	fn(name: int, id: int, size: int): ref Resource;
+	};
+
+	# common form of category data in appinfo
+	Categories: adt {
+		renamed:	int;	# which categories have been renamed
+		labels:	array of string;	# 16 category names
+		uids:		array of int;	# corresponding unique IDs
+		lastuid:	int;		# last unique ID assigned
+		appdata:	array of byte;	# remaining data is application-specific
+
+		new:		fn(labels: array of string): ref Categories;
+		unpack:	fn(a: array of byte): ref Categories;
+		pack:	fn(c: self ref Categories): array of byte;
+		mkidmap:	fn(c: self ref Categories): array of int;
+	};
+
+	Doc: adt {
+		m:	Palmdb;
+		file:	ref Palmdb->PDB;
+		version:	int;
+		length:	int;	# uncompressed
+		nrec:		int;	# text records only
+		recsize:	int;	# uncompressed
+		position:	int;
+		sizes:	array of int;	# sizes of uncompressed records
+
+		open:	fn(m: Palmdb, file: ref Palmdb->PDB): (ref Doc, string);
+		read:		fn(nil: self ref Doc, i: int): (string, string);
+		iscompressed:	fn(nil: self ref Doc): int;
+		unpacktext:	fn(d: self ref Doc, a: array of byte): (string, string);
+		textlength:	fn(d: self ref Doc, a: array of byte): int;
+	};
+
+	init:	fn(): string;
+
+	# name mapping
+	filename:	fn(s: string): string;
+	dbname:	fn(s: string): string;
+
+	# convert between resource/application ID and string
+	id2s:	fn(id: int): string;
+	s2id:	fn(s: string): int;
+
+	# time conversion
+	pilot2epoch:	fn(t: int): int;
+	epoch2pilot:	fn(t: int): int;
+
+	# Latin-1 to string conversion
+	gets:	fn(a: array of byte): string;
+	puts:	fn(a: array of byte, s: string);
+
+	# big-endian conversion
+	get2:	fn(a: array of byte): int;
+	get3:	fn(a: array of byte): int;
+	get4:	fn(a: array of byte): int;
+	put2:	fn(a: array of byte, v: int);
+	put3:	fn(a: array of byte, v: int);
+	put4:	fn(a: array of byte, v: int);
+
+	# argument wrapping for Desklink and CMP 2.x
+	ArgIDbase: con 16r20;		# first argument ID
+	argsize:	fn(args: array of (int, array of byte)): int;
+	packargs:	fn(out: array of byte, args: array of (int, array of byte)): array of byte;
+	unpackargs:	fn(argc: int, reply: array of byte): (array of (int, array of byte), string);
+
+};
+
+Palmdb: module {
+
+	PATH:	con "/dis/lib/palmdb.dis";
+
+	DB: adt {
+		x:	int;			# instance index, used internally
+
+		mode:	int;
+		attr:		int;		# essential database attributes
+
+		open:	fn(nil: string, mode: int): (ref DB, string);
+		create:	fn(nil: string, mode: int, perm: int, nil: ref Palm->DBInfo): (ref DB, string);
+		close:	fn(nil: self ref DB): string;
+
+		stat:		fn(nil: self ref DB): ref Palm->DBInfo;
+		wstat:	fn(nil: self ref DB, nil: ref Palm->DBInfo, flags: int);
+
+		rdappinfo:	fn(nil: self ref DB): (array of byte, string);
+		wrappinfo:	fn(nil: self ref DB, nil: array of byte): string;
+
+		rdsortinfo:	fn(nil: self ref DB): (array of int, string);
+		wrsortinfo:	fn(nil: self ref DB, nil: array of int): string;
+
+		readidlist:	fn(nil: self ref DB, sort: int): array of int;
+		nentries:	fn(nil: self ref DB): int;
+		resetsyncflags:	fn(nil: self ref DB): string;
+
+		records:	fn(nil: self ref DB): ref PDB;
+		resources:	fn(nil: self ref DB): ref PRC;
+	};
+
+	# database files (.pdb, .doc, and most others)
+	PDB: adt {
+		db:	ref DB;
+
+		read:		fn(nil: self ref PDB, index: int): ref Palm->Record;
+		readid:	fn(nil: self ref PDB, id: int): (ref Palm->Record, int);
+
+		resetnext:	fn(nil: self ref PDB): int;
+		readnextmod:	fn(nil: self ref PDB): (ref Palm->Record, int);
+#			DLP 1.1 functions:
+#		readnextincat(nil: self ref DB, cat: int): (ref Palm->Record, string);
+#		readnextmodincat(nil: self ref DB, cat: int): (ref Palm->Record, string);
+
+		write:	fn(nil: self ref PDB, r: ref Palm->Record): string;
+
+		truncate:	fn(nil: self ref PDB): string;
+		delete:	fn(nil: self ref PDB, id: int): string;
+		deletecat:	fn(nil: self ref PDB, cat: int): string;
+		purge:	fn(nil: self ref PDB): string;
+
+		movecat:	fn(nil: self ref PDB, old: int, new: int): string;
+
+	};
+
+	# resource files (.prc)
+	PRC: adt {
+		db:	ref DB;
+
+		# read by index, or by type & id
+		read:		fn(nil: self ref PRC, index: int): ref Palm->Resource;
+		readtype:	fn(nil: self ref PRC, name: int, id: int): (ref Palm->Resource, int);
+
+		# write by type and id only (desklink)
+		write:	fn(nil: self ref PRC, r: ref Palm->Resource): string;
+
+		truncate:	fn(nil: self ref PRC): string;
+		delete:	fn(nil: self ref PRC, name: int, id: int): string;
+	};
+
+	# open modes (not the same as Sys->)
+	OREAD:		con 16r80;
+	OWRITE:		con 16r40;
+	ORDWR:		con OREAD|OWRITE;
+	OEXCL:		con 16r20;
+	OSECRET:		con 16r10;
+
+	init:	fn(m: Palm): string;
+};
--- /dev/null
+++ b/module/palmfile.m
@@ -1,0 +1,140 @@
+Palmfile: module {
+
+	PATH:	con "/dis/lib/palmfile.dis";
+
+	DBInfo: adt {
+		name:	string;
+		attr:		int;
+		dtype:	string;	# database type (byte[4])
+		version:	int;	# defined by application
+		creator:	string;	# creating application (byte[4])
+		ctime:	int;
+		mtime:	int;
+		btime:	int;	# last backup
+		modno:	int;	# modification number: set to zero
+		uidseed:	int;	# unique record ID seed (unused, set to zero)
+
+		# used internally and by the database access protocol
+		appinfo:	int;	# AppInfo offset
+		sortinfo:	int;	# SortInfo offset
+
+		# the following are used by the database access protocol
+		index:	int;
+		more:	int;
+
+		new:		fn(name: string, attr: int, dtype: string, version: int, creator: string): ref DBInfo;
+	};
+
+	# file attributes:
+
+	Fresource:	con 1<<0;		# file is .prc not .pdb
+	Fronly:		con 1<<1;		# read only
+	Fappinfodirty:	con 1<<2;
+	Fbackup:		con 1<<3;		# no conduit exists
+	Foverwrite:	con 1<<4;		# overwrite older copy if present
+	Freset:		con 1<<5;		# reset after installation
+	Fprivate:		con 1<<6;		# don't allow copy of this to be beamed
+
+	Record: adt {
+		id:	int;	# resource: ID; data: unique record ID
+		index:	int;
+		name:	int;	# byte[4]: resource record only
+		attr:	int;	# data record only
+		cat:	int;	# category
+		data:	array of byte;
+
+#		new:	fn(size: int): ref Record;
+	};
+
+	Entry: adt {
+		id:	int;	# resource: id; record: unique ID
+		offset:	int;
+		size:	int;
+		name:	int;	# resource entry only
+		attr:	int;	# record entry only
+	};
+
+	# record attributes:
+
+	Rdelete:	con 16r80; # delete next sync
+	Rdirty:	con 16r40; # record modified
+	Rinuse:	con 16r20; # record in use
+	Rsecret:	con 16r10; # record is secret
+	Rarchive:	con 16r08; # archive next sync
+	Rmcat:	con 16r0F; # mask for category field in Entry.attrs
+
+	# common form of category data in appinfo
+	Categories: adt {
+		renamed:	int;	# which categories have been renamed
+		labels:	array of string;	# 16 category names
+		uids:		array of int;	# corresponding unique IDs
+		lastuid:	int;		# last unique ID assigned
+		appdata:	array of byte;	# remaining data is application-specific
+
+		new:		fn(labels: array of string): ref Categories;
+		unpack:	fn(a: array of byte): ref Categories;
+		pack:	fn(c: self ref Categories): array of byte;
+		mkidmap:	fn(c: self ref Categories): array of int;
+	};
+
+	Pfile: adt {
+		fname:	string;
+		f:	ref Bufio->Iobuf;
+		mode:	int;
+
+		info:	ref DBInfo;
+		appinfo:	array of byte;
+		sortinfo:	array of int;
+
+		uidseed:	int;
+		entries:	array of ref Entry;
+
+		open:	fn(nil: string, mode: int): (ref Pfile, string);
+#		create:	fn(nil: string, mode: int, perm: int, nil: ref DBInfo): ref Pfile;
+		close:	fn(nil: self ref Pfile): int;
+
+		stat:		fn(nil: self ref Pfile): ref DBInfo;
+#		wstat:	fn(nil: self ref Pfile, nil: ref DBInfo);
+
+		read:		fn(nil: self ref Pfile, index: int): (ref Record, string);
+#		readid:	fn(nil: self ref Pfile, nil: int): (ref Record, string);
+#		append:	fn(nil: self ref Pfile, r: ref Record): int;
+
+#		setappinfo:	fn(nil: self ref Pfile, nil: array of byte);
+#		setsortinfo:	fn(nil: self ref Pfile, nil: array of int);
+	};
+
+	Doc: adt {
+		file:	ref Pfile;
+		version:	int;
+		length:	int;	# uncompressed
+		nrec:		int;	# text records only
+		recsize:	int;	# uncompressed
+		position:	int;
+		sizes:	array of int;	# sizes of uncompressed records
+
+		open:	fn(file: ref Pfile): (ref Doc, string);
+		read:		fn(nil: self ref Doc, i: int): (string, string);
+		iscompressed:	fn(nil: self ref Doc): int;
+		unpacktext:	fn(d: self ref Doc, a: array of byte): (string, string);
+		textlength:	fn(d: self ref Doc, a: array of byte): int;
+	};
+
+	init:	fn(): string;
+
+	# name mapping
+	filename:	fn(s: string): string;
+	dbname:	fn(s: string): string;
+
+	# Latin-1 to string conversion
+	gets:	fn(a: array of byte): string;
+	puts:	fn(a: array of byte, s: string);
+
+	# big-endian conversion
+	get2:	fn(a: array of byte): int;
+	get3:	fn(a: array of byte): int;
+	get4:	fn(a: array of byte): int;
+	put2:	fn(a: array of byte, v: int);
+	put3:	fn(a: array of byte, v: int);
+	put4:	fn(a: array of byte, v: int);
+};
--- /dev/null
+++ b/module/pkcs.m
@@ -1,0 +1,176 @@
+#
+# Public-Key Cryptography Standards (PKCS)
+#
+#	Ref: 	http://www.rsa.com
+#		RFC1423
+#
+
+PKCS: module {
+
+	PATH: con "/dis/lib/crypt/pkcs.dis";
+
+	init: fn(): string;
+
+	# PKCS Object Identifiers
+
+	objIdTab			: array of ASN1->Oid;
+
+	id_pkcs,
+	id_pkcs_1,
+	id_pkcs_rsaEncryption,
+	id_pkcs_md2WithRSAEncryption,
+	id_pkcs_md4WithRSAEncryption,
+	id_pkcs_md5WithRSAEncryption,
+	id_pkcs_3,
+	id_pkcs_dhKeyAgreement,
+	id_pkcs_5,
+	id_pkcs_pbeWithMD2AndDESCBC,
+	id_pkcs_pbeWithMD5AndDESCBC,
+	id_pkcs_7,
+	id_pkcs_data,
+	id_pkcs_singnedData,
+	id_pkcs_envelopedData,
+	id_pkcs_signedAndEnvelopedData,
+	id_pkcs_digestData,
+	id_pkcs_encryptedData,
+	id_pkcs_9,
+	id_pkcs_emailAddress,
+	id_pkcs_unstructuredName,
+	id_pkcs_contentType,
+	id_pkcs_messageDigest,
+	id_pkcs_signingTime,
+	id_pkcs_countersignature,
+	id_pkcs_challengePassword,
+	id_pkcs_unstructuredAddress,
+	id_pkcs_extCertAttrs,
+	id_algorithm_shaWithDSS		: con iota;
+
+	# PKCS1
+
+	RSAParams: adt {
+		modulus			: ref Keyring->IPint;
+		exponent		: ref Keyring->IPint;
+	};
+
+	RSAKey: adt {
+		modulus			: ref Keyring->IPint;
+		modlen			: int;
+		exponent		: ref Keyring->IPint;
+
+		bits: fn(k: self ref RSAKey): int;
+		#tostring: fn(k: self ref RSAKey): string;
+	};
+
+	MD2_WithRSAEncryption		: con 0;
+	MD5_WithRSAEncryption		: con 1;	
+
+	rsa_encrypt: fn(data: array of byte, key: ref RSAKey, blocktype: int): (string, array of byte); 
+	rsa_decrypt: fn(data: array of byte, key: ref RSAKey, public: int): (string, array of byte); 
+	rsa_sign: fn(data: array of byte, sk: ref RSAKey, algid: int): (string, array of byte);
+	rsa_verify: fn(data, signature: array of byte, pk: ref RSAKey, algid: int): int;
+	decode_rsapubkey: fn(a: array of byte): (string, ref RSAKey);
+
+	# Note:
+	#	DSS included here is only for completeness.
+
+	DSSParams: adt {
+		p			: ref Keyring->IPint;
+		q			: ref Keyring->IPint;
+		alpha			: ref Keyring->IPint;
+	};
+
+	DSSPublicKey: adt {
+		params			: ref DSSParams;
+		y			: ref Keyring->IPint;
+	};
+
+	DSSPrivateKey: adt {
+		params			: ref DSSParams;
+		x			: ref Keyring->IPint;
+	};
+
+	generateDSSKeyPair: fn(strength: int): (ref DSSPublicKey, ref DSSPrivateKey);
+	dss_sign: fn(a: array of byte, sk: ref DSSPrivateKey): (string, array of byte);
+	dss_verify: fn(a, signa: array of byte, pk: ref DSSPublicKey): int;
+	decode_dsspubkey: fn(a: array of byte): (string, ref DSSPublicKey);
+
+	# PKCS3
+
+	DHParams: adt {
+		prime			: ref Keyring->IPint; # prime (p)
+		base			: ref Keyring->IPint; # generator (alpha)
+		privateValueLength	: int;
+	};
+
+	DHPublicKey: adt {
+		param			: ref DHParams;
+		pk			: ref Keyring->IPint;
+	};
+
+	DHPrivateKey: adt {
+		param			: ref DHParams;
+		pk			: ref Keyring->IPint;
+		sk			: ref Keyring->IPint;
+	};
+
+	generateDHParams: fn(primelen: int): ref DHParams; 
+	setupDHAgreement: fn(dh: ref DHParams): (ref DHPrivateKey, ref DHPublicKey);
+	computeDHAgreedKey: fn(dh: ref DHParams, mysk, upk: ref Keyring->IPint): array of byte;
+	decode_dhpubkey: fn(a: array of byte): (string, ref DHPublicKey);
+
+	# PKCS5
+
+	PBEParams: adt {
+		salt			: array of byte; # [8]
+		iterationCount		: int;
+	};	
+
+	PBE_MD2_DESCBC			: con 0;
+	PBE_MD5_DESCBC			: con 1;
+
+	generateDESKey: fn(pw: array of byte, param: ref PBEParams, alg: int)
+		: (ref Keyring->DESstate, array of byte, array of byte);
+	pbe_encrypt: fn(state: ref Keyring->DESstate, b: array of byte): array of byte;
+	pbe_decrypt: fn(state: ref Keyring->DESstate, eb: array of byte): array of byte;
+
+	# PKCS6
+
+	ExtCertInfo: adt {
+  		version 		: int;
+  		cert 			: array of byte; # der encoded x509 Certificate
+  		attrs 			: list of array of byte; # attribute as array of byte 
+	};
+
+	# PKCS7
+	#	See module X509
+
+	# PKCS8
+
+	PrivateKeyInfo: adt {		# as SEQUENCE
+		version			: int; # should be 0
+		privateKeyAlgorithm	: ref AlgIdentifier;
+		privateKey		: array of byte; # octet string
+		attrs			: list of array of byte; # [0] IMPLICIT Attributes OPTIONAL 
+
+		encode: fn(p: self ref PrivateKeyInfo): (string, array of byte);
+		decode: fn(a: array of byte): (string, ref PrivateKeyInfo);		
+	};
+
+	EncryptedPrivateKeyInfo: adt {	# as SEQUENCE
+  		encryptionAlgorithm 	: ref AlgIdentifier;
+  		encryptedData 		: array of byte; # octet string
+
+		encode: fn(ep: self ref EncryptedPrivateKeyInfo): (string, array of byte);
+		decode: fn(a: array of byte): (string, ref EncryptedPrivateKeyInfo);
+	};
+
+	AlgIdentifier: adt {		# TODO: move this to ASN1
+		oid			: ref ASN1->Oid;
+		parameter		: array of byte;
+	};
+
+	# PKCS10
+	#	See module X509
+};
+
+
--- /dev/null
+++ b/module/plumbmsg.m
@@ -1,0 +1,44 @@
+Plumbmsg: module
+{
+	PATH:	con "/dis/lib/plumbmsg.dis";
+
+	# Message format:
+	#	source application\n
+	#	destination application\n
+	#	working directory\n
+	#	type\n
+	#	properties\n
+	#	nbytes\n
+	#	n bytes
+
+	Msg: adt
+	{
+		src:		string;
+		dst:		string;
+		dir:		string;
+		kind:		string;
+		attr:		string;
+		data:		array of byte;
+
+		# used by applications
+		send: 	fn(msg: self ref Msg): int;
+		recv: 	fn(): ref Msg;
+
+		# used by plumb and send, recv
+		pack: 	fn(msg: self ref Msg): array of byte;
+		unpack: 	fn(b: array of byte): ref Msg;
+	};
+
+	Attr: adt
+	{
+		name:	string;
+		val:		string;
+	};
+
+	init:	fn(doinput: int, rcvport: string, maxdata: int): int;
+	shutdown:	fn();
+
+	string2attrs:	fn(s: string): list of ref Attr;
+	attrs2string:	fn(l: list of ref Attr): string;
+	lookup:	fn(attrs: list of ref Attr, name: string): (int, string);
+};
--- /dev/null
+++ b/module/pop3.m
@@ -1,0 +1,40 @@
+# pop3 protocol independent access to an email server.
+ 
+Pop3: module
+{
+	PATH: con "/dis/lib/pop3.dis";
+ 
+	# all functions return status (-ve when error)
+
+         # open a connection with the pop3 server
+         # requires the email server's name or address or nil if a default server is to be used
+	# returns (status, errror string)
+         open: fn(user, password, server: string) : (int, string);
+
+	# stat the user's mailbox 
+	# returns (status, error string, no. messages, total no. bytes)
+	stat: fn(): (int, string, int, int);
+
+	# list the user's mailbox
+	# returns (status, error string, list of (message no., bytes in message))
+	msglist: fn(): (int, string, list of (int, int));
+
+	# list as above but return (status, error string, list of message nos.)
+	msgnolist: fn(): (int, string, list of int);
+
+	# top of a message given it's no.
+	# returns (status, error string, message top)
+	top: fn(m: int) : (int, string, string);
+
+	# full text of a message given it's no.
+	# returns (status, error string, message)
+	get: fn(m: int) : (int, string, string);
+
+	# delete a message given it's no.
+	# returns (status, error string)
+	delete: fn(m: int) : (int, string);
+
+         # close the connection
+	# returns (status, error string)
+         close: fn(): (int, string);
+};
--- /dev/null
+++ b/module/popup.m
@@ -1,0 +1,11 @@
+# pop-up menus.
+# use choicebuttons instead - it's difficult to get these right.
+Popup: module {
+	PATH: con "/dis/lib/popup.dis";
+	init: fn();
+#	mkbutton: fn(win: ref Tk->Toplevel, w: string, a: array of string, n: int): chan of string;
+#	changebutton: fn(win: ref Tk->Toplevel, w: string, a: array of string, n: int);
+#	event: fn(win: ref Tk->Toplevel, e: string, a: array of string): int;
+#	add: fn(a: array of string, s: string): (array of string, int);
+	post: fn(win: ref Tk->Toplevel, p: Draw->Point, a: array of string, n: int): chan of int;
+};
--- /dev/null
+++ b/module/powerman.m
@@ -1,0 +1,8 @@
+Powerman: module
+{
+	PATH:	con "/dis/lib/powerman.dis";
+	init:	fn(file: string, cmd: chan of string): int;
+	ack:	fn(cmd: string);
+	ctl:	fn(cmd: string): string;
+	stop:	fn();
+};
--- /dev/null
+++ b/module/prefab.m
@@ -1,0 +1,113 @@
+Prefab: module
+{
+	PATH:	con	"$Prefab";
+
+	# types of Elements
+	EIcon:		con 0;
+	EText:		con 1;
+	ETitle:		con 2;
+	EHorizontal:	con 3;
+	EVertical:	con 4;
+	ESeparator:	con 5;
+
+	# first arg to Element.adjust: size of elements
+	Adjpack:	con 10;	# leave alone, pack tightly
+	Adjequal:	con 11;	# make equal
+	Adjfill:	con 12;	# make equal, filling available space
+
+	# second arg: position of element within space
+	Adjleft:	con 20;
+	Adjup:		con 20;
+	Adjcenter:	con 21;
+	Adjright:	con 22;
+	Adjdown:	con 22;
+
+	# default fonts and colors for objects
+	Style: adt
+	{
+		titlefont:	ref Draw->Font;
+		textfont:	ref Draw->Font;
+		elemcolor:	ref Draw->Image;
+		edgecolor:	ref Draw->Image;
+		titlecolor:	ref Draw->Image;
+		textcolor:	ref Draw->Image;
+		highlightcolor:	ref Draw->Image;
+	};
+
+	# drawing environment for objects
+	Environ: adt
+	{
+		screen:	ref Draw->Screen;
+		style:	ref Style;
+	};
+
+	# operand for layout operators; set either (font, color, text) or (icon, mask)
+	Layout: adt
+	{
+		font:		ref Draw->Font;
+		color:		ref Draw->Image;
+		text:		string;
+		icon:		ref Draw->Image;
+		mask:		ref Draw->Image;
+		tag:		string;
+	};
+
+	# graphical objects in the interface, recursively defined for making lists
+	Element: adt
+	{
+		# part of Ell elements
+		kind:		int;			# type: EIcon, EText, etc.
+		r:		Draw->Rect;		# rectangle on screen
+		environ:	ref Environ;		# graphics screen, style
+		tag:		string;			# identifier for selection
+
+		# different fields defined for different kinds of Elements
+		kids:		list of ref Element;	# children of EHorizontal, EVertical
+		str:		string;			# text in an EText element
+		mask:		ref Draw->Image;	# part of Eicon, ESeparator
+		image:		ref Draw->Image;	# part of Eicon, ESeparator, EText, Etitle
+		font:		ref Draw->Font;		# part of EText, Etitle
+
+		# constructors
+		icon:		fn(env: ref Environ, r: Draw->Rect, icon, mask: ref Draw->Image): ref Element;
+		text:		fn(env: ref Environ, text: string, r: Draw->Rect, kind: int): ref Element;
+		layout:	fn(env: ref Environ, lay: list of Layout, r: Draw->Rect, kind: int): ref Element;
+		elist:		fn(env: ref Environ, elem: ref Element, kind: int): ref Element;
+		separator:	fn(env: ref Environ, r: Draw->Rect, icon, mask: ref Draw->Image): ref Element;
+
+		# editing and geometry
+		append:	fn(elist: self ref Element, elem: ref Element): int;
+		adjust:	fn(elem: self ref Element, equal: int, dir: int);
+		clip:		fn(elem: self ref Element, r: Draw->Rect);
+		scroll:	fn(elem: self ref Element, d: Draw->Point);
+		translate:	fn(elem: self ref Element, d: Draw->Point);
+		show:	fn(elist: self ref Element, elem: ref Element): int;
+	};
+
+	# connects an element to a window for display
+	Compound: adt
+	{
+		image:		ref Draw->Image;	# window on which contents are drawn
+		environ:	ref Environ;		# graphics screen, style
+		r:		Draw->Rect;		# rectangle on screen
+		title:		ref Element;		# above the line (may be nil)
+		contents:	ref Element;		# below the line
+
+		# constructors
+		iconbox:	fn(env: ref Environ, p: Draw->Point, title: string, icon, mask: ref Draw->Image): ref Compound;
+		textbox:	fn(env: ref Environ, r: Draw->Rect, title, text: string): ref Compound;
+		layoutbox:fn(env: ref Environ, r: Draw->Rect, title: string, lay: list of Layout): ref Compound;
+		box:		fn(env: ref Environ, p: Draw->Point, title, elist: ref Element): ref Compound;
+
+		# display
+		draw:	fn(comp: self ref Compound);
+		redraw:	fn(comp: self ref Compound, r: Draw->Rect);
+		scroll:	fn(comp: self ref Compound, elem: ref Element, d: Draw->Point);
+		show:	fn(comp: self ref Compound, elem: ref Element): int;
+
+		# support for using EHorizontal and EVertical as menus
+		select:	fn(comp: self ref Compound, elem: ref Element, i: int, c: chan of int): (int, int, ref Element);
+		tagselect:	fn(comp: self ref Compound, elem: ref Element, i: int, c: chan of int): (int, int, ref Element);
+		highlight:	fn(comp: self ref Compound, elem: ref Element, on: int);
+	};
+};
--- /dev/null
+++ b/module/print.m
@@ -1,0 +1,88 @@
+Print: module
+{
+	PATH: con "/dis/lib/print/print.dis";
+	CONFIG_PATH: con "/lib/print/";
+
+	init: fn(): int;
+	set_printfd: fn(fd: ref Sys->FD);
+	print_image: fn(p: ref Printer, display: ref Draw->Display, im: ref Draw->Image, pcwidth: int, cancel: chan of int): int;
+	print_textfd: fn(p: ref Printer, fd: ref Sys->FD, ps: real, pr: int, wrap: int): int;
+	get_defprinter: fn(): ref Printer;
+	set_defprinter: fn(p: ref Printer);
+	get_size: fn(p: ref Printer): (int, int, int);	# dpi, xpixels, ypixels
+	get_printers: fn(): list of ref Printer;
+	get_papers: fn(): list of ref Paper;
+	save_settings: fn(): int;
+
+	# Printer types
+	
+	Ptype: adt {
+		name: string;
+		desc: string;
+		modes: list of ref Pmode;
+		driver: string;
+		hpmapfile: string;
+	};
+	
+	# Paper sizes
+	
+	Paper: adt {
+		name: string;
+		hpcode: string;
+		width_inches: real;
+		height_inches: real;
+	};
+	
+	# Print modes
+	
+	Pmode: adt {
+		name: string;
+		desc: string;
+		resx: int;
+		resy: int;
+		blackdepth: int;
+		coldepth: int;
+		blackresmult: int;
+	};
+	
+	# Print options
+	
+	Popt: adt {
+		name: string;
+		mode: ref Pmode;
+		paper: ref Paper;
+		orientation: int;
+		duplex: int;
+	};
+	
+	# Printer instance
+	
+	PORTRAIT: con 0;
+	LANDSCAPE: con 1;
+	
+	DUPLEX_OFF: con 0;
+	DUPLEX_LONG: con 1;
+	DUPLEX_SHORT: con 2;
+	
+	Printer: adt {
+		name: string;
+		ptype: ref Ptype;
+		device: string;
+		popt: ref Popt;
+		pdriver: Pdriver;
+	};
+
+};
+
+
+Pdriver: module
+{
+	PATHPREFIX: con "/dis/lib/print/";
+	DATAPREFIX: con "/lib/print/";
+
+	init: fn(debug: int);
+	sendimage: fn(p: ref Print->Printer, tfd: ref Sys->FD, display: ref Draw->Display, im: ref Draw->Image, width: int, lmargin: int, cancel: chan of int): int;
+	sendtextfd: fn(p: ref Print->Printer, pfd, tfd: ref Sys->FD, ps: real, pr: int, wrap: int): int;
+	printable_pixels: fn(p: ref Print->Printer): (int, int);
+
+};
--- /dev/null
+++ b/module/profile.m
@@ -1,0 +1,78 @@
+Profile: module
+{
+	PATH: con "/dis/lib/profile.dis";
+
+	Range: adt{
+		l: int;
+		u: int;
+		f: int;
+		n: cyclic ref Range;
+	};
+
+	Funprof: adt{
+		name: string;
+		# file: string;
+		line: int;
+		count: int;
+		counte: int;
+	};
+
+	Modprof: adt{
+		name: string;
+		path: string;
+		srcpath: string;
+		rawtab: array of (int, int);
+		linetab: array of int;
+		rngtab: array of ref Range;
+		funtab: array of Funprof;
+		total: int;
+		totals: array of int;
+		coverage: int;
+	};
+
+	Prof: adt{
+		mods: list of Modprof;
+		total: int;
+		totals: array of int;
+	};
+
+	Coverage: type list of (string, int, list of (list of (int, int, int), string));
+
+	# constants to or into second arg of show()
+	MODULE: con 1;	# give stats for each module
+	FUNCTION: con 2;	# give stats for each function
+	LINE: con 4;		# give stats for each line
+	VERBOSE: con 8;	# full stats
+	FULLHDR: con 16;	# file:lineno: on each line of output
+	FREQUENCY: con 32;	# show frequency rather than coverage
+
+	init: fn(): int;
+	lasterror: fn(): string;
+	profile: fn(m: string): int;
+	sample: fn(i: int): int;
+	start: fn(): int;
+	stop: fn(): int;
+	stats: fn() : Prof;
+	show: fn(p: Prof, v: int): int;
+	end: fn(): int;
+
+	# coverage profiling specific functions
+
+	cpstart: fn(pid: int): int;
+	cpstats: fn(rec: int, v: int): Prof;
+	cpfstats: fn(v: int): Prof;
+	cpshow: fn(p: Prof, v: int): int;
+
+	coverage: fn(p: Prof, v: int): Coverage;
+
+	# memory profiling specific functions
+
+	MAIN: con 1;
+	HEAP: con 2;
+	IMAGE: con 4;
+
+	memstart: fn(mem: int): int;
+	memstats: fn(): Prof;
+	memshow: fn(p: Prof, v: int): int;
+
+};
--- /dev/null
+++ b/module/pslib.m
@@ -1,0 +1,7 @@
+Pslib : module 
+{
+	PATH:		con "/dis/lib/pslib.dis";
+
+	init:	fn(bufio: Bufio);
+	writeimage: fn(ioutb: ref Bufio->Iobuf, im: ref Draw->Image, dpi: int);
+};
--- /dev/null
+++ b/module/quicktime.m
@@ -1,0 +1,73 @@
+#
+# Apple QuickTime File Format
+#
+QuickTime: module
+{
+	PATH:	con "/dis/lib/quicktime.dis";
+
+	DEFBUF:		con 8192;
+
+	AtomHDR:	con 8;
+
+	Tkhdr: adt
+	{
+		version:	int;
+		creation:	int;
+		modtime:	int;
+		trackid:	int;
+		timescale:	int;
+		duration:	int;
+		timeoff:	int;
+		priority:	int;
+		layer:		int;
+		altgrp:		int;
+		volume:		int;
+		matrix:		array of int;
+		width:		int;
+		height:		int;
+	};
+
+	MvhdrSIZE:	con 100;
+	Mvhdr: adt
+	{
+		version:	int;
+		create:		int;
+		modtime:	int;
+		timescale:	int;
+		duration:	int;
+		rate:		int;
+		vol:		int;
+		r1:		int;
+		r2:		int;
+		matrix:		array of int;
+		r3:		int;
+		r4:		int;
+		pvtime:		int;
+		posttime:	int;
+		seltime:	int;
+		seldurat:	int;
+		curtime:	int;
+		nxttkid:	int;
+	};
+
+	# QuickTime descriptor
+	QD: adt
+	{
+		fd:	ref sys->FD;		# descriptor of QuickTime file
+		buf:	array of byte;		# buffer
+		nbyte:	int;			# bytes remaining
+		ptr:	int;			# buffer pointer
+
+		mvhdr:	ref Mvhdr;		# movie header desctiptor
+
+		readn:		fn(r: self ref QD, b: array of byte, l: int): int;
+		skip:		fn(r: self ref QD, size: int): int;
+		skipatom:	fn(r: self ref QD, size: int): int;
+		atomhdr:	fn(r: self ref QD): (string, int);
+		mvhd:		fn(r: self ref QD, l: int): string;
+		trak:		fn(r: self ref QD, l: int): string;
+	};
+
+	init:	fn();
+	open:	fn(file: string): (ref QD, string);
+};
--- /dev/null
+++ b/module/rabin.m
@@ -1,0 +1,28 @@
+Rabin: module
+{
+	PATH:	con "/dis/lib/rabin.dis";
+	init:	fn(bufio: Bufio);
+
+	debug:	int;
+
+	open:	fn(rcfg: ref Rcfg, b: ref Iobuf, min, max: int): (ref Rfile, string);
+
+	Rcfg: adt {
+		prime, width, mod: int;
+		tab:	array of int;
+
+		mk:	fn(prime, width, mod: int): (ref Rcfg, string);
+	};
+
+	Rfile: adt {
+		b:	ref Iobuf;
+		rcfg:	ref Rcfg;
+		min, max:	int;
+		buf:	array of byte;
+		n:	int;
+		state:	int;
+		off:	big;
+
+		read:	fn(r: self ref Rfile): (array of byte, big, string);
+	};
+};
--- /dev/null
+++ b/module/rand.m
@@ -1,0 +1,7 @@
+Rand: module
+{
+	PATH:	con "/dis/lib/rand.dis";
+	init:	fn(seed: int);
+	rand:       fn(modulus: int): int;
+	bigrand:    fn(modulus: big): big;
+};
--- /dev/null
+++ b/module/readdir.m
@@ -1,0 +1,19 @@
+Readdir: module
+{
+	PATH:	con	"/dis/lib/readdir.dis";
+
+	# sortkey is one of NAME, ATIME, MTIME, SIZE, or NONE
+	# possibly with DESCENDING or'd in
+
+	NAME, ATIME, MTIME, SIZE, NONE: con iota;
+	DESCENDING:	con (1<<5);
+
+	init:	fn(path: string, sortkey: int): (array of ref Sys->Dir, int);
+	readall:	fn(fd: ref Sys->FD, sortkey: int): (array of ref Sys->Dir, int);
+	sortdir:fn(a: array of ref Sys->Dir, key: int): (array of ref Sys->Dir, int);
+
+	# COMPACT can be or'd into sortkey to preserve only the first
+	# (by depth) of a group of duplicate names in a union
+
+	COMPACT:	con (1<<4);
+};
--- /dev/null
+++ b/module/regex.m
@@ -1,0 +1,38 @@
+Regex: module {
+
+	PATH:	con "/dis/lib/regex.dis";
+
+# normally imported identifiers
+
+	Re: type ref Arena;
+	compile:	fn(nil:string,nil:int): (Re, string);
+	execute:	fn(nil:Re, nil:string): array of (int, int);
+	executese:	fn(nil:Re, nil:string, se: (int, int), bol: int, eol: int): array of (int, int);
+
+# internal identifiers, not normally imported
+
+	ALT, CAT, DOT, SET, HAT, DOL, NUL, PCLO, CLO, OPT, LPN, RPN : con (1<<16)+iota;
+
+	refRex : type int;	# used instead of ref Rex to avoid circularity
+
+	Set: adt {				# character class
+		neg: int;			# 0 or 1
+		ascii : array of int;		# ascii members, bit array
+		unicode : list of (int,int);	# non-ascii char ranges
+	};
+
+	Rex: adt {		# node in parse of regex, or state of fsm
+		kind : int;	# kind of node: char or ALT, CAT, etc
+		left : refRex;	# left descendant
+		right : refRex;	# right descendant, or next state
+		set : ref Set;	# character class
+		pno : int;
+	};
+
+	Arena: adt {		# free store from which nodes are allocated
+		rex : array of Rex;		
+		ptr : refRex;	# next available space
+		start : refRex;	# root of parse, or start of fsm
+		pno : int;
+	};
+};
--- /dev/null
+++ b/module/registries.m
@@ -1,0 +1,44 @@
+Registries: module {
+	PATH:	con "/dis/lib/registries.dis";
+	init:			fn();
+
+	Attributes: adt {
+		attrs: list of (string, string);
+
+		get:	fn(a: self ref Attributes, attr: string): string;
+		set:	fn(a: self ref Attributes, attr, val: string);
+		new:	fn(attrs: list of (string, string)): ref Attributes;
+	};
+
+	Attached: adt {
+		fd:			ref Sys->FD;
+		signerpkhash:	string;
+		localuser:		string;
+		remoteuser:	string;
+	};
+
+	Service: adt {
+		addr: string;			# dial this to connect to the service.
+		attrs: ref Attributes;		# information about the nature of the service.
+
+		attach:	fn(s: self ref Service, user: string, keydir: string): ref Attached;
+	};
+
+	Registered: adt {
+		addr:	string;
+		reg:		ref Registry;
+		fd:		ref Sys->FD;
+	};
+
+	Registry: adt {
+		dir:		string;
+		indexfd:	ref Sys->FD;
+
+		new:		fn(dir: string): ref Registry;
+		connect:	fn(svc: ref Service, user: string, keydir: string): ref Registry;
+		services:	fn(r: self ref Registry): (list of ref Service, string);
+		find:		fn(r: self ref Registry, a: list of (string, string)): (list of ref Service, string);
+		register:	fn(r: self ref Registry, addr: string, attrs: ref Attributes, persist: int): (ref Registered, string);
+		unregister:	fn(r: self ref Registry, addr: string): string;
+	};
+};
--- /dev/null
+++ b/module/rfc822.m
@@ -1,0 +1,68 @@
+RFC822: module
+{
+	PATH: con "/dis/lib/rfc822.dis";
+
+	init: fn(b: Bufio);
+
+	# TO DO: multipart ...
+
+	# token values reserved to represent a word and a quoted string
+	Word, QString: con 1+iota;
+
+	Maxrequest: con 16*1024;	# more than enough for anything sensible
+
+	Rfclex: adt {
+		fd:	ref Bufio->Iobuf;	# open on a single line
+		wordval:	string;		# text if Word or QString
+		tok:	int;				# last token seen
+		eof:	int;				# end of file (ignore subsequent ungetc)
+
+		seen:	list of (int, string);	# pushback
+
+		mk:		fn(a: array of byte): ref Rfclex;
+		getc:		fn(p: self ref Rfclex): int;
+		ungetc:	fn(p: self ref Rfclex);
+		lex:		fn(p: self ref Rfclex): int;
+		unlex:	fn(p: self ref Rfclex);
+		skipws:	fn(p: self ref Rfclex): int;
+
+		line:		fn(p: self ref Rfclex): string;
+	};
+
+	readheaders:	fn(fd: ref Bufio->Iobuf, limit: int): array of (string, array of byte);
+	parseparams:	fn(ps: ref Rfclex): list of (string, string);
+	parsecontent:	fn(ps: ref Rfclex, multipart: int, head: list of ref Content): list of ref Content;
+	mimefields:	fn(ps: ref Rfclex): list of (string, list of (string, string));
+	# TO DO: parse addresses
+
+	quotable:	fn(s: string): int;
+	quote:	fn(s: string): string;
+
+	# convert an epoch time into http-formatted text
+	sec2date: fn(secs: int): string;
+
+	# convert a date in http text format to seconds from epoch
+	date2sec: fn(s: string): int;
+
+	# current time
+	now:	fn(): int;
+
+	# current time as a string
+	time:	fn(): string;
+
+	#
+	# mime-related things
+	#
+	Content: adt{
+		generic: string;
+		specific: string;
+		params: list of (string, string);
+
+		mk:	fn(generic: string, specific: string, params: list of (string, string)): ref Content;
+		check: fn(c: self ref Content, oks: list of ref Content): int;
+		text:	fn(c: self ref Content): string;
+	};
+
+	suffixclass: fn(name: string): (ref Content, ref Content);
+	dataclass:	fn(a: array of byte): (ref Content, ref Content);
+};
--- /dev/null
+++ b/module/riff.m
@@ -1,0 +1,96 @@
+#
+# Microsoft Resource Interchange File Format
+# with AVI support
+#
+Riff: module
+{
+	PATH:	con "/dis/lib/riff.dis";
+
+	DEFBUF:		con 8192;
+
+	BI_RGB:		con 0;
+	BI_RLE8:	con 1;
+	BI_RLE4:	con 2;
+	BI_BITFEILD:	con 3;
+
+	RGB: adt
+	{
+		r:	int;
+		g:	int;
+		b:	int;
+	};
+
+	Binfosize:	con 10*4;
+	Bitmapinfo: adt		# Windows bitmap info structure
+	{
+		width:		int;		# width in pixels
+		height:		int;		# height in pixels
+		planes:		int;		# planes of output device (must be 1)
+		bitcount:	int;		# bits per pixel
+		compression:	int;		# coding BI_RGB... or IV32 for indeo
+		sizeimage:	int;		# size in bytes of image
+		xpelpermeter:	int;		# resolution in pixels per meter
+		ypelpermeter:	int;
+		clrused:	int;		# colors used
+		clrimportant:	int;		# how fixed is the map
+
+		cmap:		array of RGB;	# color map
+	};
+
+	AVImainhdr:	con 14*4;
+	AVIhdr: adt
+	{
+		usecperframe:	int;
+		bytesec:	int;
+		flag:		int;
+		frames:		int;
+		initframes:	int;
+		streams:	int;
+		bufsize:	int;
+		width:		int;
+		height:		int;
+	};
+
+	AVIstreamhdr:	con 2*4 + 10*4;
+	AVIstream: adt
+	{
+		# Stream Header information
+		stype:		string;
+		handler:	string;
+		flags:		int;
+		priority:	int;
+		initframes:	int;
+		scale:		int;
+		rate:		int;
+		start:		int;
+		length:		int;
+		bufsize:	int;
+		quality:	int;
+		samplesz:	int;
+
+		# Stream Format information (decoder specific)
+		fmt:		array of byte;
+		binfo:		ref Bitmapinfo;
+
+		fmt2binfo:	fn(a: self ref AVIstream): string;
+	};
+
+	# Riff descriptor
+	RD: adt
+	{
+		fd:	ref sys->FD;		# descriptor of RIFF file
+		buf:	array of byte;		# buffer
+		nbyte:	int;			# bytes remaining
+		ptr:	int;			# buffer pointer
+
+		gethdr:		fn(r: self ref RD): (string, int);
+		readn:		fn(r: self ref RD, b: array of byte, l: int): int;
+		check4:		fn(r: self ref RD, code: string): string;
+		avihdr:		fn(r: self ref RD): (ref AVIhdr, string);
+		streaminfo:	fn(r: self ref RD): (ref AVIstream, string);
+		skip:		fn(r: self ref RD, size: int): int;
+	};
+
+	init:	fn();
+	open:	fn(file: string): (ref RD, string);
+};
--- /dev/null
+++ b/module/runt.m
@@ -1,0 +1,11 @@
+implement nothing;
+
+include "sys.m";
+include "draw.m";
+include "prefab.m";
+include "tk.m";
+include "math.m";
+include "ipints.m";
+include "crypt.m";
+include "loader.m";
+include "freetype.m";
--- /dev/null
+++ b/module/scoretable.m
@@ -1,0 +1,12 @@
+# only used by tetris currently. this interface will change.
+Scoretable: module {
+	PATH: con "/dis/lib/scoretable.dis";
+	Score: adt {
+		user: string;
+		score: int;
+		other: string;
+	};
+	init: fn(port: int, user, name: string, scorefile: string): (int, string);
+	setscore: fn(score: int, other: string): int;
+	scores: fn(): list of Score;
+};
--- /dev/null
+++ b/module/scsiio.m
@@ -1,0 +1,27 @@
+#
+# adapted from /sys/include/disk.h on Plan 9: subject to the Lucent Public License 1.02
+#
+ScsiIO: module
+{
+	PATH: con "/dis/lib/scsiio.dis";
+
+	# SCSI interface
+	Scsi: adt {
+		lock:	chan of int;
+		inquire:	string;
+		rawfd:	ref Sys->FD;
+		nchange:	int;
+		changetime:	int;
+
+		open:	fn(f: string): ref Scsi;
+		rawcmd:	fn(s: self ref Scsi, c: array of byte, d: array of byte, io: int): int;
+		cmd:		fn(s: self ref Scsi, c: array of byte, d: array of byte, io: int): int;
+		ready:	fn(s: self ref Scsi): int;
+	};
+
+	Sread, Swrite, Snone: con iota;
+
+	scsierror:	fn(asc: int, ascq: int): string;
+
+	init:	fn(verbose: int);
+};
--- /dev/null
+++ b/module/secstore.m
@@ -1,0 +1,28 @@
+Secstore: module
+{
+	PATH:	con "/dis/lib/secstore.dis";
+
+	Maxfilesize: con 128*1024;	# default
+	Maxmsg:	con 4096;
+
+	init:		fn();
+	privacy:	fn(): int;
+	cansecstore:	fn(addr: string, user: string): int;
+	mkseckey:	fn(pass: string): array of byte;
+	connect:		fn(addr: string, user: string, pwhash: array of byte): (ref Dial->Connection, string, string);
+	dial:		fn(addr: string): ref Dial->Connection;
+	auth:		fn(conn: ref Dial->Connection, user: string, pwhash: array of byte): (string, string);
+	sendpin:	fn(conn: ref Dial->Connection, pin: string): int;
+	files:		fn(conn: ref Dial->Connection): list of (string, int, string, string, array of byte);
+	getfile:	fn(conn: ref Dial->Connection, filename: string, maxsize: int): array of byte;
+	remove:	fn(conn: ref Dial->Connection, filename: string): int;
+	putfile:	fn(conn: ref Dial->Connection, filename: string, data: array of byte): int;
+	bye:		fn(conn: ref Dial->Connection);
+
+	mkfilekey:	fn(pass: string): array of byte;
+	decrypt:	fn(a: array of byte, key: array of byte): array of byte;
+	encrypt:	fn(a: array of byte, key: array of byte): array of byte;
+	erasekey:	fn(a: array of byte);
+
+	lines:	fn(file: array of byte): list of array of byte;
+};
--- /dev/null
+++ b/module/security.m
@@ -1,0 +1,59 @@
+#
+#  security routines implemented in limbo
+#
+
+
+Virgil: module
+{
+	PATH:	con "/dis/lib/virgil.dis";
+
+	virgil:	fn(args: list of string): string;
+};
+
+Random: module
+{
+	PATH:	con "/dis/lib/random.dis";
+
+	ReallyRandom:	con 0;
+	NotQuiteRandom:	con 1;
+
+	randomint: fn(which: int): int;
+	randombuf: fn(which, n: int): array of byte;
+};
+
+#
+#  secure socket layer emulator
+#
+SSL: module
+{
+	PATH:	con "/dis/lib/ssl.dis";
+
+	connect: fn(fd: ref Sys->FD): (string, ref Sys->Connection);
+	secret: fn(c: ref Sys->Connection, secretin, secretout: array of byte): string;
+};
+
+
+#
+#  Encrypted Key Exchange protocol
+#
+Login: module 
+{
+	PATH:	con "/dis/lib/login.dis";
+
+	login:	fn(id, password, dest: string): (string, ref Keyring->Authinfo);
+};
+
+#
+#  Station To Station protocol
+#
+Auth: module
+{
+	PATH:	con "/dis/lib/auth.dis";
+
+	init: fn(): string;
+	server: fn(algs: list of string, ai: ref Keyring->Authinfo, fd: ref Sys->FD, setid: int): (ref Sys->FD, string);
+	client: fn(alg: string, ai: ref Keyring->Authinfo, fd: ref Sys->FD): (ref Sys->FD, string);
+	auth:	fn(ai: ref Keyring->Authinfo, keyspec: string, alg: string, dfd: ref Sys->FD): (ref Sys->FD, ref Keyring->Authinfo, string);
+	keyfile:	fn(keyspec: string): string;
+	key:	fn(keyspec: string): ref Keyring->Authinfo;
+};
--- /dev/null
+++ b/module/selectfile.m
@@ -1,0 +1,13 @@
+Selectfile: module
+{
+	PATH:	con "/dis/lib/selectfile.dis";
+
+	init:	fn(): string;
+	filename:	fn(ctxt: ref Draw->Context, parent: ref Draw->Image,
+				title: string,
+				pat: list of string,
+				dir: string): string;
+#	select: fn(top: ref Tk->Toplevel, w: string,
+#			root, dir: string,
+#			pats: list of string, action: string): chan of (int, string);
+};
--- /dev/null
+++ b/module/sets.m
@@ -1,0 +1,34 @@
+Sets: module {
+	A: con 2r1010;
+	B: con 2r1100;
+
+	PATH: con "/dis/lib/sets.dis";
+
+	init:		fn();
+	set:		fn(): Set;
+	str2set:	fn(str: string): Set;
+	bytes2set:	fn(d: array of byte): Set;
+	Set: adt {
+		m:	int;
+		a:	array of int;
+
+		X:		fn(s1: self Set, o: int, s2: Set): Set;
+		add:		fn(s: self Set, n: int): Set;
+		addlist:	fn(s: self Set, ns: list of int): Set;
+		# dellist:	fn(s: self Set, ns: list of int): Set;
+		del:		fn(s: self Set, n: int): Set;
+		invert:	fn(s: self Set): Set;
+
+		eq:		fn(s1: self Set, s2: Set): int;
+		holds:	fn(s: self Set, n: int): int;
+		isempty:	fn(s: self Set): int;
+		msb:		fn(s: self Set): int;
+		limit:		fn(s: self Set): int;
+
+		str:		fn(s: self Set): string;
+		bytes:	fn(s: self Set, n: int): array of byte;
+		debugstr:	fn(s: self Set): string;
+	};
+	All: con Set(~0, nil);
+	None: con Set(0, nil);
+};
--- /dev/null
+++ b/module/sets32.m
@@ -1,0 +1,33 @@
+Sets: module {
+	A: con 2r1010;
+	B: con 2r1100;
+
+	PATH: con "/dis/lib/sets32.dis";
+
+	init:		fn();
+	set:		fn(): Set;
+	str2set:	fn(str: string): Set;
+	bytes2set:	fn(d: array of byte): Set;
+	Set: adt {
+		s:	int;
+
+		X:		fn(s1: self Set, o: int, s2: Set): Set;
+		add:		fn(s: self Set, n: int): Set;
+		addlist:	fn(s: self Set, ns: list of int): Set;
+		# dellist:	fn(s: self Set, ns: list of int): Set;
+		del:		fn(s: self Set, n: int): Set;
+		invert:	fn(s: self Set): Set;
+
+		eq:		fn(s1: self Set, s2: Set): int;
+		holds:	fn(s: self Set, n: int): int;
+		isempty:	fn(s: self Set): int;
+		msb:		fn(s: self Set): int;
+		limit:		fn(s: self Set): int;
+
+		str:		fn(s: self Set): string;
+		bytes:	fn(s: self Set, lim: int): array of byte;
+		debugstr:	fn(s: self Set): string;
+	};
+	All: con Set(~0);
+	None: con Set(0);
+};
--- /dev/null
+++ b/module/sexprs.m
@@ -1,0 +1,41 @@
+Sexprs: module
+{
+	PATH:	con "/dis/lib/sexprs.dis";
+
+	Sexp: adt {
+		pick {
+		String =>
+			s: string;
+			hint:	string;
+		Binary =>
+			data:	array of byte;
+			hint: string;
+		List =>
+			l:	cyclic list of ref Sexp;
+		}
+
+		read:	fn[T](b: T): (ref Sexp, string) for {
+				T =>
+					getb:	fn(nil: self T): int;
+					ungetb:	fn(nil: self T): int;
+					offset:	fn(nil: self T): big;
+				};
+		parse:	fn(s: string): (ref Sexp, string, string);
+		unpack:	fn(a: array of byte): (ref Sexp, array of byte, string);
+		text:	fn(e: self ref Sexp): string;
+		packedsize:	fn(e: self ref Sexp): int;
+		pack:	fn(e: self ref Sexp): array of byte;
+		b64text:	fn(e: self ref Sexp): string;
+
+		islist:	fn(e: self ref Sexp): int;
+		els:	fn(e: self ref Sexp): list of ref Sexp;
+		op:	fn(e: self ref Sexp): string;
+		args:	fn(e: self ref Sexp): list of ref Sexp;
+		eq:	fn(e: self ref Sexp, t: ref Sexp): int;
+		copy:	fn(e: self ref Sexp): ref Sexp;
+		asdata:	fn(e: self ref Sexp): array of byte;
+		astext:	fn(e: self ref Sexp): string;
+	};
+
+	init:	fn();
+};
--- /dev/null
+++ b/module/sh.m
@@ -1,0 +1,106 @@
+Command: module
+{
+	PATH:	con "/dis/sh.dis";
+
+	init:	fn(ctxt: ref Draw->Context, argv: list of string);
+};
+
+Sh: module
+{
+	PATH: con "/dis/sh.dis";
+	initialise:		fn();
+	init:			fn(ctxt: ref Draw->Context, argv: list of string);
+	system:		fn(drawctxt: ref Draw->Context, cmd: string): string;
+	run:			fn(drawctxt: ref Draw->Context, argv: list of string): string;
+	parse:		fn(s: string): (ref Cmd, string);
+	cmd2string:	fn(c: ref Cmd): string;
+
+	Context: adt {
+		new:			fn(drawcontext: ref Draw->Context): ref Context;
+		get:			fn(c: self ref Context, name: string): list of ref Listnode;
+		set:			fn(c: self ref Context, name: string, val: list of ref Listnode);
+		setlocal:		fn(c: self ref Context, name: string, val: list of ref Listnode);
+		envlist:		fn(c: self ref Context): list of (string, list of ref Listnode);
+		push:		fn(c: self ref Context);
+		pop:			fn(c: self ref Context);
+		copy:		fn(c: self ref Context, copyenv: int): ref Context;
+		run:			fn(c: self ref Context, args: list of ref Listnode, last: int): string;
+		addmodule:	fn(c: self ref Context, name: string, mod: Shellbuiltin);
+		addbuiltin:	fn(c: self ref Context, name: string, mod: Shellbuiltin);
+		removebuiltin:	fn(c: self ref Context, name: string, mod: Shellbuiltin);
+		addsbuiltin:	fn(c: self ref Context, name: string, mod: Shellbuiltin);
+		removesbuiltin: fn(c: self ref Context, name: string, mod: Shellbuiltin);
+		fail:			fn(c: self ref Context, ename, msg: string);
+		options:		fn(c: self ref Context): int;
+		setoptions:	fn(c: self ref Context, flags, on: int): int;
+		INTERACTIVE, VERBOSE, EXECPRINT, ERROREXIT: con 1 << iota;
+
+		env:			ref Environment;
+		waitfd:		ref Sys->FD;
+		drawcontext:	ref Draw->Context;
+		keepfds:		list of int;
+	};
+
+	list2stringlist:	fn(nl: list of ref Listnode): list of string;
+	stringlist2list:	fn(sl: list of string): list of ref Listnode;
+	quoted:		fn(val: list of ref Listnode, quoteblocks: int): string;
+
+	initbuiltin:		fn(c: ref Context, sh: Sh): string;
+	whatis:		fn(nil: ref Sh->Context, nil: Sh, nil: string, nil: int): string;
+	runbuiltin:	fn(c: ref Context, sh: Sh, cmd: list of ref Listnode, last: int): string;
+	runsbuiltin:	fn(c: ref Context, sh: Sh, cmd: list of ref Listnode): list of ref Listnode;
+	getself: 		fn(): Shellbuiltin;
+	Cmd: type Node;
+	Node: adt {
+		ntype: int;
+		left, right: ref Node;
+		word: string;
+		redir: ref Redir;
+	};
+	Redir: adt {
+		rtype: int;
+		fd1, fd2: int;
+	};
+	Var: adt {
+		name: string;
+		val: list of ref Listnode;
+		flags: int;
+		CHANGED, NOEXPORT: con (1 << iota);
+	};
+	Environment: adt {
+		sbuiltins: ref Builtins;
+		builtins: ref Builtins;
+		bmods: list of (string, Shellbuiltin);
+		localenv: ref Localenv;
+	};
+	Localenv: adt {
+		vars: array of list of ref Var;
+		pushed: ref Localenv;
+		flags: int;
+	};
+	Listnode: adt {
+		cmd: ref Node;
+		word: string;
+	};
+	Builtins: adt {
+		ba: array of (string, list of Shellbuiltin);
+		n: int;
+	};
+	# node types
+	n_BLOCK,  n_VAR, n_BQ, n_BQ2, n_REDIR,
+	n_DUP, n_LIST, n_SEQ, n_CONCAT, n_PIPE, n_ADJ,
+	n_WORD, n_NOWAIT, n_SQUASH, n_COUNT,
+	n_ASSIGN, n_LOCAL: con iota;
+	GLOB: con 1;
+};
+
+Shellbuiltin: module {
+	initbuiltin: fn(c: ref Sh->Context, sh: Sh): string;
+	runbuiltin: fn(c: ref Sh->Context, sh: Sh,
+			cmd: list of ref Sh->Listnode, last: int): string;
+	runsbuiltin: fn(c: ref Sh->Context, sh: Sh,
+			cmd: list of ref Sh->Listnode): list of ref Sh->Listnode;
+	BUILTIN, SBUILTIN, OTHER: con iota;
+	whatis: fn(c: ref Sh->Context, sh: Sh, name: string, wtype: int): string;
+	getself: fn(): Shellbuiltin;
+};
--- /dev/null
+++ b/module/smtp.m
@@ -1,0 +1,22 @@
+# smtp protocol independent access to an email server.
+ 
+Smtp : module
+{
+	PATH : con "/dis/lib/smtp.dis";
+ 
+	# all functions return status (-ve when error)
+
+         # open a connection with the email server
+         # requires the email server's name or address or nil if a default server is to be used
+	# returns (status, errror string)
+         open: fn(server : string) : (int, string);
+
+	# send mail - returns (status, error string)
+	sendmail: fn(fromwho: string, 
+		             towho: list of string, 
+		             cc : list of string,
+		             msg: list of string) : (int, string);
+
+         # close the connection - returns (status, error string)
+         close: fn() : (int, string);
+};
--- /dev/null
+++ b/module/sort.m
@@ -1,0 +1,8 @@
+Sort: module {
+	PATH: con "/dis/lib/sort.dis";
+	sort: fn[S, T](s: S, a: array of T)
+		for{
+		S =>
+			gt: fn(s: self S, x, y: T): int;
+		};
+};
--- /dev/null
+++ b/module/spki.m
@@ -1,0 +1,254 @@
+Rawsexprs: module
+{
+	PATH:	con "rawsexprs.dis";
+
+	Sexp: adt {
+		pick {
+		String =>
+			s: string;
+			hint:	string;
+		Binary =>
+			data:	array of byte;
+			hint: string;
+		List =>
+			l:	cyclic list of ref Sexp;
+		}
+
+		unpack:	fn(a: array of byte): (ref Sexp, array of byte, string);
+		text:	fn(e: self ref Sexp): string;
+		packedsize:	fn(e: self ref Sexp): int;
+		pack:	fn(e: self ref Sexp): array of byte;
+	};
+
+	init:	fn();
+};
+
+SPKI: module
+{
+	PATH: con "/dis/lib/spki/spki.dis";
+
+	Hash: adt {
+		alg:	string;
+		hash:	array of byte;
+
+		sexp:	fn(h: self ref Hash): ref Sexprs->Sexp;
+		text:	fn(h: self ref Hash): string;
+		eq:	fn(h1: self ref Hash, h2: ref Hash): int;
+	};
+
+	Key: adt {
+		pk:	ref Keyring->PK;	# either pk/sk or hash might be nil
+		sk:	ref Keyring->SK;
+		nbits:	int;
+		halg:	string;	# basic signature hash algorithm
+		henc:	string;	# pre-signature encoding
+		hash:	list of ref Hash;
+
+		hashed:	fn(k: self ref Key, alg: string): array of byte;
+		hashexp:	fn(k: self ref Key, alg: string): ref Hash;
+		ishash:	fn(k: self ref Key): int;
+		public:	fn(k: self ref Key): ref Key;
+		sigalg:	fn(k: self ref Key): string;
+		text:	fn(k: self ref Key): string;
+		sexp:	fn(k: self ref Key): ref Sexprs->Sexp;
+		eq:	fn(k1: self ref Key, k2: ref Key): int;
+	};
+
+	Name: adt {
+		principal:	ref Key;
+		names:	list of string;
+
+		isprincipal:	fn(n: self ref Name): int;
+		local:	fn(n: self ref Name): ref Name;
+		islocal:	fn(n: self ref Name): int;
+		isprefix:	fn(n1: self ref Name, n2: ref Name): int;
+		text:	fn(n: self ref Name): string;
+		sexp:	fn(n: self ref Name): ref Sexprs->Sexp;
+		eq:	fn(n1: self ref Name, n2: ref Name): int;
+	};
+
+	Cert: adt {
+		e:	ref Sexprs->Sexp;	# S-expression, if originally parsed
+		issuer:	ref Name;
+		subject:	ref Subject;
+		valid:	ref Valid;
+		pick {
+		A or KH or O =>	# auth, keyholder or object
+			delegate:	int;
+			tag:	ref Sexprs->Sexp;
+		N =>	# name
+		}
+
+		text:	fn(c: self ref Cert): string;
+		sexp:	fn(c: self ref Cert): ref Sexprs->Sexp;
+	};
+
+	# the pick might move to a more general `Principal' structure,
+	# allowing compound and quoting principals
+	Subject: adt {
+		pick{
+		P =>
+			key:	ref Key;
+		N =>
+			name:	ref Name;
+		O =>
+			hash:	ref Hash;
+		KH =>
+			holder:	ref Name;
+		T =>
+			k, n:	int;
+			subs:	cyclic list of ref Subject;
+		}
+
+		eq:	fn(s1: self ref Subject, s2: ref Subject): int;
+		principal:	fn(s: self ref Subject): ref Key;
+		text:	fn(s: self ref Subject): string;
+		sexp:	fn(s: self ref Subject): ref Sexprs->Sexp;
+	};
+
+	Principal: adt[T] {
+		pick{
+		N =>
+			name:	ref Name;
+		Q =>
+			quoter:	T;
+			quotes:	cyclic ref Principal;
+		}
+	};
+
+	Signature: adt {
+		hash:	ref Hash;
+		key:	ref Key;	# find by hash if necessary
+		sa:	string;	# alg[-[encoding-]hash]
+		sig:	list of (string, array of byte);
+
+		algs:	fn(s: self ref Signature): (string, string, string);
+		sexp:	fn(s: self ref Signature): ref Sexprs->Sexp;
+		text:	fn(s: self ref Signature): string;
+	};
+
+	Seqel: adt {
+		pick{
+		C =>
+			c: ref Cert;
+		K =>
+			k: ref Key;
+		O =>
+			op: string;
+			args: list of ref Sexprs->Sexp;
+		S =>
+			sig: ref Signature;
+		RV =>	# <reval>
+			ok:	list of (string, string);
+			onetime:	array of byte;
+			valid:	ref Valid;
+		CRL =>
+			bad:	list of (string, string);
+			valid:	ref Valid;
+		Delta =>
+			hash:	string;
+			bad:	list of (string, string);
+			valid:	ref Valid;
+		E =>
+			exp:	ref Sexprs->Sexp;
+		}
+
+		sexp:	fn(se: self ref Seqel): ref Sexprs->Sexp;
+		text:	fn(se: self ref Seqel): string;
+	};
+
+	Valid: adt {
+		notbefore:	string;
+		notafter:	string;
+
+		intersect:	fn(a: self Valid, b: Valid): (int, Valid);
+		text:	fn(a: self Valid): string;
+		sexp:	fn(a: self Valid): ref Sexprs->Sexp;
+	};
+
+	Toplev: adt {
+		pick {
+		C =>
+			v:	ref Cert;
+		Sig =>
+			v:	ref Signature;
+		K =>
+			v:	ref Key;
+		Seq =>
+			v:	list of ref Seqel;
+		}
+
+		sexp:	fn(t: self ref Toplev): ref Sexprs->Sexp;
+		text:	fn(t: self ref Toplev): string;
+	};
+
+	init:	fn();
+
+	# parse structures
+	parse:	fn(s: ref Sexprs->Sexp): (ref Toplev, string);
+	parseseq:	fn(s: ref Sexprs->Sexp): list of ref Seqel;
+	parsecert:	fn(s: ref Sexprs->Sexp): ref Cert;
+	parsesig:	fn(s: ref Sexprs->Sexp): ref Signature;
+	parsename:	fn(s: ref Sexprs->Sexp): ref Name;
+	parsekey:	fn(s: ref Sexprs->Sexp): ref Key;
+	parsehash:	fn(s: ref Sexprs->Sexp): ref Hash;
+	parsecompound:	fn(s: ref Sexprs->Sexp): ref Name;
+	parsevalid:	fn(s: ref Sexprs->Sexp): ref Valid;
+
+	# signature checking
+	checksig:	fn(c: ref Cert, sig: ref Signature): string;
+	sig2icert:	fn(sig: ref Signature, signer: string, exp: int): ref Keyring->Certificate;
+
+	# signature making
+	signcert:	fn(c: ref Cert, sigalg: string, key: ref Key): (ref Signature, string);
+	signbytes:	fn(a: array of byte, sigalg: string, key: ref Key): (ref Signature, string);
+
+	# tags
+	maketag:	fn(e: ref Sexprs->Sexp): ref Sexprs->Sexp;
+	tagintersect:	fn(t1: ref Sexprs->Sexp, t2: ref Sexprs->Sexp): ref Sexprs->Sexp;
+	tagimplies:	fn(t1: ref Sexprs->Sexp, t2: ref Sexprs->Sexp): int;
+
+	# hash canonical s-expression
+	hashbytes:	fn(a: array of byte, alg: string): array of byte;
+	hashexp:	fn(e: ref Sexprs->Sexp, alg: string): array of byte;
+
+	# convert between date and time strings and Inferno form
+	date2epoch:	fn(s: string): int;	# YYYY-MM-DD_HH:MM:SS
+	epoch2date:	fn(t: int): string;
+	time2secs:	fn(s: string): int;	# HH:MM:SS
+	secs2time:	fn(t: int): string;
+
+	# misc
+	sigalgs:	fn(algs: string): (string, string, string);
+
+	# debugging
+	dump:	fn(s: string, a: array of byte);
+};
+
+Proofs: module
+{
+	Proof: adt {
+		n:	int;
+
+		parse:	fn(s: string): ref Proof;
+		sexp:	fn(p: self ref Proof): ref Sexprs->Sexp;
+		text:	fn(p: self ref Proof): string;
+	};
+
+	init:	fn(): string;
+};
+
+Verifier: module
+{
+	PATH:	con "/dis/lib/spki/verifier.dis";
+
+	Speaksfor: adt {
+		subject:	ref SPKI->Subject;
+		name:	ref SPKI->Name;
+		regarding:	ref Sexprs->Sexp;
+		valid:	ref SPKI->Valid;
+	};
+
+	init:	fn();
+	verify:	fn(seq: list of ref SPKI->Seqel): (ref Speaksfor, list of ref SPKI->Seqel, string);
+};
--- /dev/null
+++ b/module/srv.m
@@ -1,0 +1,17 @@
+Srv: module
+{
+	PATH:	con	"$Srv";	# some hosted, never native
+
+	#
+	# IP network database lookups
+	#
+	#	iph2a:	host name to ip addrs
+	#	ipa2h:	ip addr to host aliases
+	#	ipn2p:	service name to port
+	#
+	iph2a:	fn(host: string): list of string;
+	ipa2h:	fn(addr: string): list of string;
+	ipn2p:	fn(net, service: string): string;
+
+	init:	fn();
+};
--- /dev/null
+++ b/module/srvrunt.b
@@ -1,0 +1,3 @@
+implement nothing;
+
+include "srv.m";
--- /dev/null
+++ b/module/ssl3.m
@@ -1,0 +1,204 @@
+#
+# ssl 3.0 protocol
+#
+
+SSL3: module {
+
+	PATH: con "/dis/lib/crypt/ssl3.dis";
+
+	init: fn(): string;
+
+	# SSL cipher suites
+
+	NULL_WITH_NULL_NULL,
+	RSA_WITH_NULL_MD5,
+	RSA_WITH_NULL_SHA,
+	RSA_EXPORT_WITH_RC4_40_MD5,
+	RSA_WITH_RC4_128_MD5,
+	RSA_WITH_RC4_128_SHA,
+	RSA_EXPORT_WITH_RC2_CBC_40_MD5,
+	RSA_WITH_IDEA_CBC_SHA,
+	RSA_EXPORT_WITH_DES40_CBC_SHA,
+	RSA_WITH_DES_CBC_SHA,
+	RSA_WITH_3DES_EDE_CBC_SHA,
+	DH_DSS_EXPORT_WITH_DES40_CBC_SHA,
+	DH_DSS_WITH_DES_CBC_SHA,
+	DH_DSS_WITH_3DES_EDE_CBC_SHA,
+	DH_RSA_EXPORT_WITH_DES40_CBC_SHA,
+	DH_RSA_WITH_DES_CBC_SHA,
+	DH_RSA_WITH_3DES_EDE_CBC_SHA,
+	DHE_DSS_EXPORT_WITH_DES40_CBC_SHA,
+	DHE_DSS_WITH_DES_CBC_SHA,
+	DHE_DSS_WITH_3DES_EDE_CBC_SHA,
+	DHE_RSA_EXPORT_WITH_DES40_CBC_SHA,
+	DHE_RSA_WITH_DES_CBC_SHA,
+	DHE_RSA_WITH_3DES_EDE_CBC_SHA,
+	DH_anon_EXPORT_WITH_RC4_40_MD5,
+	DH_anon_WITH_RC4_128_MD5,
+	DH_anon_EXPORT_WITH_DES40_CBC_SHA,
+	DH_anon_WITH_DES_CBC_SHA,
+	DH_anon_WITH_3DES_EDE_CBC_SHA,
+	FORTEZZA_KEA_WITH_NULL_SHA,
+	FORTEZZA_KEA_WITH_FORTEZZA_CBC_SHA,
+	FORTEZZA_KEA_WITH_RC4_128_SHA 		: con iota;
+
+	Authinfo: adt {
+		suites: array of byte; # [2] x
+		comprs: array of byte; # [1] x
+
+		sk: ref PrivateKey; # for user certs 
+		root_type: int; # root type of certs
+		certs: list of array of byte; # x509 cert chain
+
+		types: array of byte; # acceptable cert types
+		dns: list of array of byte; # acceptable cert authorities
+	};
+
+	PrivateKey: adt {
+		pick {
+		RSA =>
+			sk			: ref PKCS->RSAKey;
+		DSS =>
+			sk			: ref PKCS->DSSPrivateKey;
+		DH =>
+			sk			: ref PKCS->DHPrivateKey;
+		}
+	};
+
+	Record: adt {
+		content_type			: int;
+		version			 	: array of byte; # [2]		
+		data				: array of byte;
+	};
+
+	# key exchange algorithms
+
+	KeyExAlg: adt {
+		pick {
+		NULL =>
+		DH =>
+			params			: ref PKCS->DHParams;
+			sk			: ref PKCS->DHPrivateKey;			
+			peer_params		: ref PKCS->DHParams;
+			peer_pk			: ref PKCS->DHPublicKey;
+			exch_pk			: ref PKCS->DHPublicKey;
+		RSA =>
+			sk	 		: ref PKCS->RSAKey; # for RSA key exchange
+			export_key 		: ref PKCS->RSAKey; # server RSA temp key
+			peer_pk			: ref PKCS->RSAKey; # temp key from server
+		FORTEZZA_KEA =>	
+			# not supported yet
+		}
+	};
+
+	SigAlg: adt {
+		pick {
+		anon =>
+		RSA =>
+			sk	 		: ref PKCS->RSAKey; # for sign
+			peer_pk			: ref PKCS->RSAKey; # for verify from peer cert
+		DSS =>
+			sk	 		: ref PKCS->DSSPrivateKey; # for sign
+			peer_pk			: ref PKCS->DSSPublicKey; # for verify from peer cert
+		FORTEZZA_KEA =>	# not supported yet
+		}
+	};
+
+	CipherSpec: adt {
+		is_exportable			: int;
+	
+		bulk_cipher_algorithm		: int;
+		cipher_type			: int;
+		key_material			: int;
+		IV_size				: int;
+
+		mac_algorithm			: int;
+		hash_size			: int;		
+	};
+
+	# record format queue
+
+	RecordQueue: adt {
+		macState			: ref MacState;
+		cipherState			: ref CipherState;
+
+		length				: int;
+		sequence_numbers		: array of int;
+	
+		data				: list of ref Record;
+		fragment			: int;
+		b, e				: int;
+
+		new: fn(): ref RecordQueue;
+		read: fn(q: self ref RecordQueue, ctx: ref Context, fd: ref Sys->FD): string;
+		write: fn(q: self ref RecordQueue, ctx: ref Context, fd: ref Sys->FD, r: ref Record): string;
+		calcmac: fn(q: self ref RecordQueue, ctx: ref Context, cntype: int, a: array of byte, ofs, n: int) : array of byte;
+	};
+
+	MacState: adt {
+		hash_size			: int;
+		pick {
+		null =>
+		md5 =>
+			ds			: array of ref Keyring->DigestState;
+		sha =>
+			ds			: array of ref Keyring->DigestState;
+		}
+	};
+
+	CipherState: adt {
+		block_size			: int;
+		pick {
+		null =>
+		rc4 =>
+			es			: ref Keyring->RC4state;
+		descbc =>
+			es			: ref Keyring->DESstate;
+		ideacbc =>
+			es			: ref Keyring->IDEAstate;
+		}
+	};
+
+	# context for processing both v2 and v3 protocols.
+
+	Context: adt {
+		c				: ref Sys->Connection;
+		session				: ref SSLsession->Session;
+
+		sel_keyx			: ref KeyExAlg;
+		sel_sign			: ref SigAlg;
+		sel_ciph			: ref CipherSpec;
+		sel_cmpr			: int;
+
+		local_info			: ref Authinfo;
+
+		client_random			: array of byte; # [32]
+		server_random			: array of byte; # [32]
+		
+		sha_state			: ref Keyring->DigestState;
+		md5_state			: ref Keyring->DigestState;
+
+		cw_mac				: array of byte;
+		sw_mac				: array of byte;
+		cw_key				: array of byte;
+		sw_key				: array of byte;
+		cw_IV				: array of byte;
+		sw_IV				: array of byte;
+
+		in_queue			: ref RecordQueue;
+		out_queue			: ref RecordQueue;
+
+		status				: int;
+		state				: int;
+
+
+		new: fn(): ref Context;
+		client: fn(ctx: self ref Context, fd: ref Sys->FD, peer: string, ver: int, info: ref Authinfo): (string, int);
+		server: fn(ctx: self ref Context, fd: ref Sys->FD, info: ref Authinfo, client_auth: int): string;
+		use_devssl: fn(ctx: self ref Context);
+		set_version: fn(ctx: self ref Context, vers: int): string;
+		connect: fn(ctx: self ref Context, fd: ref Sys->FD): string;
+		read: fn(ctx: self ref Context, a: array of byte, n: int): int;
+		write: fn(ctx: self ref Context, a: array of byte, n: int): int;
+	};
+};
--- /dev/null
+++ b/module/sslsession.m
@@ -1,0 +1,27 @@
+SSLsession: module {
+
+	PATH: con "/dis/lib/crypt/sslsession.dis";
+
+	Session: adt {
+		session_id			: array of byte; # [32]
+		peer				: string;
+	
+		connection_time			: int;
+		version				: array of byte; # [2]
+
+		suite				: array of byte; # [2]
+		compression			: byte;
+
+		master_secret			: array of byte; # [48]	
+		peer_certs			: list of array of byte;
+
+		new: fn(peer: string, time: int, ver: array of byte): ref Session;
+		duplicate: fn(s: self ref Session): ref Session;
+	};
+
+	init: fn(): string;
+	add_session: fn(s: ref Session);
+	get_session_byid: fn(session_id: array of byte): ref Session;
+	get_session_byname: fn(peer: string): ref Session;
+	set_timeout: fn(t: int);
+};
--- /dev/null
+++ b/module/string.m
@@ -1,0 +1,40 @@
+String: module
+{
+	PATH:		con	"/dis/lib/string.dis";
+
+	# the second arg of the following is a character class
+	#    e.g., "a-zA-Z", or "^ \t\n"
+	# (ranges indicated by - except in first position;
+	#  ^ is first position means "not in" the following class)
+	# splitl splits just before first char in class;  (s, "") if no split
+	# splitr splits just after last char in class; ("", s) if no split
+	# drop removes maximal prefix in class
+	# take returns maximal prefix in class
+
+	splitl:		fn(s, cl: string): (string, string);
+	splitr:		fn(s, cl: string): (string, string);
+	drop:		fn(s, cl: string): string;
+	take:		fn(s, cl: string): string;
+	in:		fn(c: int, cl: string): int;
+
+	# in these, the second string is a string to match, not a class
+	splitstrl:	fn(s, t: string): (string, string);
+	splitstrr:	fn(s, t: string): (string, string);
+
+	# is first arg a prefix of second?
+	prefix:		fn(pre, s: string): int;
+
+	tolower:	fn(s: string): string;
+	toupper:	fn(s: string): string;
+
+	# string to int returning value, remainder
+	toint:		fn(s: string, base: int): (int, string);
+	tobig:		fn(s: string, base: int): (big, string);
+	toreal:		fn(s: string, base: int): (real, string);
+
+	# append s to end of l
+	append:		fn(s: string, l: list of string): list of string;
+	quoted:		fn(argv: list of string): string;
+	quotedc:		fn(argv: list of string, cl: string): string;
+	unquoted:		fn(args: string): list of string;
+};
--- /dev/null
+++ b/module/strinttab.m
@@ -1,0 +1,17 @@
+StringIntTab: module
+{
+	PATH: con "/dis/lib/strinttab.dis";
+
+	StringInt: adt{
+		key: string;
+		val: int;
+	};
+
+	# Binary search of t (which must be sorted) for key.
+	# Returns (found, val).
+	lookup: fn(t: array of StringInt, key: string) : (int, int);
+
+	# Linear search of t for first pair with given val.
+	# Returns key (nil if no match).
+	revlookup: fn(t: array of StringInt, val: int) : string;
+};
--- /dev/null
+++ b/module/strokes.m
@@ -1,0 +1,122 @@
+#
+# Li-Yeung character recognition
+#
+Strokes: module
+{
+	PATH:	con "/dis/lib/strokes/strokes.dis";
+
+	Penpoint: adt
+	{
+		x, y: int;
+		chaincode: int;
+	};
+
+	Stroke: adt
+	{
+		npts:	int;
+		pts:	array of Penpoint;
+		xrange, yrange: int;
+
+		new:	fn(n: int): ref Stroke;
+		copy:	fn(nil: self ref Stroke): ref Stroke;
+		trim:	fn(nil: self ref Stroke, n: int);
+		bbox:	fn(nil: self ref Stroke): (int, int, int, int);
+		scaleup:	fn(nil: self ref Stroke): int;
+		translate:	fn(nil: self ref Stroke, minx: int, miny: int, scalex: int, scaley: int);
+		center:	fn(nil: self ref Stroke);
+		regions:	fn(nil: self ref Stroke): ref Region;
+		dominant:	fn(nil: self ref Stroke): ref Stroke;
+		interpolate:	fn(points: self ref Stroke): ref Stroke;
+		length:	fn(nil: self ref Stroke): int;
+		pathlen:	fn(nil: self ref Stroke, first: int, last: int): int;
+		contourangles:	fn(nil: self ref Stroke, regions: ref Region): array of int;
+		filter:	fn(nil: self ref Stroke): ref Stroke;
+	};
+
+	# ordered list of regions
+	Region: adt
+	{
+		rtype: int;
+		start: int;
+		end: int;
+		next: cyclic ref Region;
+	};
+
+	#  region types
+	Rconvex, Rconcave, Rplain, Rpseudo: con iota;
+
+	Classifier: adt
+	{
+		nclasses:	int;	# number of symbols in class
+		examples:	array of list of ref Stroke;	# optional training examples
+		cnames:	array of string;		# the class names
+		canonex:	array of ref Stroke;		# optional canonical versions of the strokes
+		dompts:	array of ref Stroke;	# dominant points
+
+		match:	fn(nil: self ref Classifier, stroke: ref Stroke): (int, string);
+	};
+
+	init:	fn();
+
+	preprocess_stroke:	fn(nil: ref Stroke);
+	score_stroke:	fn(a: ref Stroke, b: ref Stroke): (int, int);
+
+	compute_similarity:	fn(a: ref Stroke, b: ref Stroke): int;
+	compute_distance:	fn(a: ref Stroke, b: ref Stroke): int;
+	compute_chain_code:	fn(nil: ref Stroke);
+	compute_unit_chain_code:	fn(pts: ref Stroke);
+
+	regiontype:	fn(ang: int): int;
+
+	sqrt:		fn(n: int): int;
+	likeatan:	fn(top: int, bot: int): int;
+	quadr:	fn(t: int): int;
+
+	printpoints:	fn(fd: ref Sys->FD, nil: ref Stroke, sep: string);
+
+	MAXDIST:	con 16r7FFFFFFF;
+};
+
+Readstrokes: module
+{
+	PATH: con "/dis/lib/strokes/readstrokes.dis";
+
+	init:	fn(nil: Strokes);
+	read_classifier:	fn(file: string, build: int, needex: int): (string, ref Strokes->Classifier);
+	read_digest:	fn(fd: ref Sys->FD): (string, array of string, array of ref Strokes->Stroke);
+	read_examples:	fn(fd: ref Sys->FD): (string, array of string, array of list of ref Strokes->Stroke);
+};
+
+Writestrokes: module
+{
+	PATH: con "/dis/lib/strokes/writestrokes.dis";
+
+	init:	fn(nil: Strokes);
+	write_digest:	fn(fd: ref Sys->FD, nil: array of string, nil: array of ref Strokes->Stroke): string;
+	write_examples:	fn(fd: ref Sys->FD, nil: array of string, nil: array of list of ref Strokes->Stroke): string;
+};
+
+Buildstrokes: module
+{
+	PATH: con "/dis/lib/strokes/buildstrokes.dis";
+
+	init:	fn(nil: Strokes);
+	canonical_example:	fn(n: int, cnames: array of string, nil: array of list of ref Strokes->Stroke): (string, array of ref Strokes->Stroke, array of ref Strokes->Stroke);
+	canonical_stroke:	fn(points: ref Strokes->Stroke): ref Strokes->Stroke;
+	compute_equipoints:	fn(nil: ref Strokes->Stroke): ref Strokes->Stroke;
+};
+
+# special characters and gestures
+#	in digits.cl:	BNASRPUVWX
+#	in punc.cl:	TFGHIJK
+#	in letters.cl:	ABNPRSUVWX
+
+#	L caps lock
+#	N num lock
+#	P ctrl (Unix), punc shift (orig)
+#	S shift
+
+#	A space
+#	B backspace
+#	R return
+#	. puncshift
--- /dev/null
+++ b/module/styx.m
@@ -1,0 +1,182 @@
+Styx: module
+{
+	PATH:	con "/dis/lib/styx.dis";
+	PATHV1:	con "/dis/lib/styx1.dis";
+
+	VERSION:	con "9P2000";
+	MAXWELEM:	con 16;
+
+	NOTAG:	con 16rFFFF;
+	NOFID:	con int ~0;	# 32 bits in this version of Styx
+
+	BIT8SZ:	con 1;
+	BIT16SZ:	con 2;
+	BIT32SZ:	con 4;
+	BIT64SZ:	con 8;
+	QIDSZ:	con BIT8SZ+BIT32SZ+BIT64SZ;
+
+	STATFIXLEN:	con BIT16SZ+QIDSZ+5*BIT16SZ+4*BIT32SZ+BIT64SZ;	# amount of fixed length data in a stat buffer
+	IOHDRSZ:	con 24;	# room for Twrite/Rread header
+	MAXFDATA: con 8192;	# `reasonable' iounit
+	MAXRPC:	con IOHDRSZ+MAXFDATA;	# usable default for fversion and iounit
+
+	Tversion,		# 100
+	Rversion,
+	Tauth,		# 102
+	Rauth,
+	Tattach,		# 104
+	Rattach,
+	Terror,		# 106, illegal
+	Rerror,
+	Tflush,		#108
+	Rflush,
+	Twalk,		# 110
+	Rwalk,
+	Topen,		# 112
+	Ropen,
+	Tcreate,		# 114
+	Rcreate,
+	Tread,		# 116
+	Rread,
+	Twrite,		# 118
+	Rwrite,
+	Tclunk,		# 120
+	Rclunk,
+	Tremove,		# 122
+	Rremove,
+	Tstat,		# 124
+	Rstat,
+	Twstat,		#126
+	Rwstat,
+	Tmax: con 100+iota;
+
+	ERRMAX:	con 128;
+
+	OREAD:	con 0; 		# open for read
+	OWRITE:	con 1; 		# write
+	ORDWR:	con 2; 		# read and write
+	OEXEC:	con 3; 		# execute, == read but check execute permission
+	OTRUNC:	con 16; 		# or'ed in (except for exec), truncate file first
+	ORCLOSE: con 64; 		# or'ed in, remove on close
+
+	# mode bits in Dir.mode used by the protocol
+	DMDIR:		con int 1<<31;		# mode bit for directory
+	DMAPPEND:	con int 1<<30;		# mode bit for append-only files
+	DMEXCL:		con int 1<<29;		# mode bit for exclusive use files
+	DMAUTH:		con int 1<<27;		# mode bit for authentication files
+
+	# Qid.qtype
+	QTDIR:	con 16r80;
+	QTAPPEND:	con 16r40;
+	QTEXCL:	con 16r20;
+	QTAUTH:	con 16r08;
+	QTFILE:	con 16r00;
+
+	Tmsg: adt {
+		tag: int;
+		pick {
+		Readerror =>
+			error: string;		# tag is unused in this case
+		Version =>
+			msize: int;
+			version: string;
+		Auth =>
+			afid: int;
+			uname, aname: string;
+		Attach =>
+			fid, afid: int;
+			uname, aname: string;
+		Flush =>
+			oldtag: int;
+		Walk =>
+			fid, newfid: int;
+			names: array of string;
+		Open =>
+			fid, mode: int;
+		Create =>
+			fid: int;
+			name: string;
+			perm, mode: int;
+		Read =>
+			fid: int;
+			offset: big;
+			count: int;
+		Write =>
+			fid: int;
+			offset: big;
+			data: array of byte;
+		Clunk or
+		Stat or
+		Remove => 
+			fid: int;
+		Wstat =>
+			fid: int;
+			stat: Sys->Dir;
+		}
+
+		read:	fn(fd: ref Sys->FD, msize: int): ref Tmsg;
+		unpack:	fn(a: array of byte): (int, ref Tmsg);
+		pack:	fn(nil: self ref Tmsg): array of byte;
+		packedsize:	fn(nil: self ref Tmsg): int;
+		text:	fn(nil: self ref Tmsg): string;
+		mtype: fn(nil: self ref Tmsg): int;
+	};
+
+	Rmsg: adt {
+		tag: int;
+		pick {
+		Readerror =>
+			error: string;		# tag is unused in this case
+		Version =>
+			msize: int;
+			version: string;
+		Auth =>
+			aqid: Sys->Qid;
+		Attach =>
+			qid: Sys->Qid;
+		Flush =>
+		Error =>
+			ename: string;
+		Clunk or
+		Remove or
+		Wstat =>
+		Walk =>
+			qids: array of Sys->Qid;
+		Create or
+		Open =>
+			qid: Sys->Qid;
+			iounit: int;
+		Read =>
+			data: array of byte;
+		Write =>
+			count: int;
+		Stat =>
+			stat: Sys->Dir;
+		}
+
+		read:	fn(fd: ref Sys->FD, msize: int): ref Rmsg;
+		unpack:	fn(a: array of byte): (int, ref Rmsg);
+		pack:	fn(nil: self ref Rmsg): array of byte;
+		packedsize:	fn(nil: self ref Rmsg): int;
+		text:	fn(nil: self ref Rmsg): string;
+		mtype: fn(nil: self ref Rmsg): int;
+	};
+
+	init:	fn();
+
+	readmsg:	fn(fd: ref Sys->FD, msize: int): (array of byte, string);
+	istmsg:	fn(f: array of byte): int;
+
+	compatible:	fn(t: ref Tmsg.Version, msize: int, version: string): (int, string);
+
+	packdirsize:	fn(d: Sys->Dir): int;
+	packdir:	fn(d: Sys->Dir): array of byte;
+	unpackdir: fn(f: array of byte): (int, Sys->Dir);
+	dir2text:	fn(d: Sys->Dir): string;
+	qid2text:	fn(q: Sys->Qid): string;
+
+	utflen:	fn(s: string): int;
+
+	# temporary undocumented compatibility function
+	write:	fn(fd: ref Sys->FD, a: array of byte, n: int): int;
+};
--- /dev/null
+++ b/module/styxconv.m
@@ -1,0 +1,10 @@
+Styxconv: module
+{
+	PATHOLD2NEW: con "/dis/lib/styxconv/old2new.dis";
+	PATHNEW2OLD: con "/dis/lib/styxconv/new2old.dis";
+
+	# call first
+	init: fn();
+	# spawn and synchronize
+	styxconv: fn(client: ref Sys->FD, server: ref Sys->FD);
+};
--- /dev/null
+++ b/module/styxflush.m
@@ -1,0 +1,7 @@
+Styxflush: module {
+	PATH: con "/dis/lib/styxflush.dis";
+	Einterrupted: con "interrupted";
+	init: fn();
+	tmsg: fn(m: ref Styx->Tmsg, flushc: chan of (int, chan of int), reply: chan of ref Styx->Rmsg): (int, ref Styx->Rmsg);
+	rmsg: fn(m: ref Styx->Rmsg): int;
+};
--- /dev/null
+++ b/module/styxlib.m
@@ -1,0 +1,87 @@
+#
+# deprecated: use styxservers(2) instead
+#
+
+Styxlib: module
+{
+	PATH: con "/dis/lib/styxlib.dis";
+	Chan: adt {
+		fid: int;
+		qid: Sys->Qid;
+		open: int;
+		mode: int;
+		uname: string;
+		path: string;
+		data: array of byte;
+
+		isdir: fn(c: self ref Chan): int;
+	};
+
+	Dirtab: adt {
+		name: string;
+		qid: Sys->Qid;
+		length: big;
+		perm: int;
+	};
+
+	Styxserver: adt {
+		fd: ref Sys->FD;
+		chans: array of list of ref Chan;
+		uname: string;
+		msize: int;
+
+		new: fn(fd: ref Sys->FD): (chan of ref Styx->Tmsg, ref Styxserver);
+		reply: fn(srv: self ref Styxserver, m: ref Styx->Rmsg): int;
+
+		fidtochan: fn(srv: self ref Styxserver, fid: int): ref Chan;
+		newchan: fn(srv: self ref Styxserver, fid: int): ref Chan;
+		chanfree: fn(srv: self ref Styxserver, c: ref Chan);
+		chanlist: fn(srv: self ref Styxserver): list of ref Chan;
+		clone: fn(srv: self ref Styxserver, c: ref Chan, fid: int): ref Chan;
+
+		devversion: fn(srv: self ref Styxserver, m: ref Styx->Tmsg.Version): int;
+		devauth: fn(srv: self ref Styxserver, m: ref Styx->Tmsg.Auth);
+		devattach: fn(srv: self ref Styxserver, m: ref Styx->Tmsg.Attach): ref Chan;
+		devflush: fn(srv: self ref Styxserver, m: ref Styx->Tmsg.Flush);
+		devwalk: fn(srv: self ref Styxserver, m: ref Styx->Tmsg.Walk,
+							gen: Dirgenmod, tab: array of Dirtab): ref Chan;
+		devclunk: fn(srv: self ref Styxserver, m: ref Styx->Tmsg.Clunk): ref Chan;
+		devstat: fn(srv: self ref Styxserver, m: ref Styx->Tmsg.Stat,
+							gen: Dirgenmod, tab: array of Dirtab);
+		devdirread: fn(srv: self ref Styxserver, m: ref Styx->Tmsg.Read,
+							gen: Dirgenmod, tab: array of Dirtab);
+		devopen: fn(srv: self ref Styxserver, m: ref Styx->Tmsg.Open,
+							gen: Dirgenmod, tab: array of Dirtab): ref Chan;
+		devremove: fn(srv: self ref Styxserver, m: ref Styx->Tmsg.Remove): ref Chan;
+	};
+
+	init:	fn(s: Styx): string;
+
+	readbytes: fn(m: ref Styx->Tmsg.Read, d: array of byte): ref Styx->Rmsg.Read;
+	readnum: fn(m: ref Styx->Tmsg.Read, val, size: int): ref Styx->Rmsg.Read;
+	readstr: fn(m: ref Styx->Tmsg.Read, d: string): ref Styx->Rmsg.Read;
+
+	openok: fn(omode, perm: int, uname, funame, fgname: string): int;
+	openmode: fn(o: int): int;
+	
+	devdir: fn(c: ref Chan, qid: Sys->Qid, n: string, length: big,
+				user: string, perm: int): Sys->Dir;
+
+	dirgenmodule: fn(): Dirgenmod;
+	dirgen: fn(srv: ref Styxserver, c: ref Chan, tab: array of Dirtab, i: int): (int, Sys->Dir);
+
+	Einuse		: con "fid already in use";
+	Ebadfid		: con "bad fid";
+	Eopen		: con "fid already opened";
+	Enotfound	: con "file does not exist";
+	Enotdir		: con "not a directory";
+	Eperm		: con "permission denied";
+	Ebadarg		: con "bad argument";
+	Eexists		: con "file already exists";
+};
+
+
+Dirgenmod: module {
+	dirgen: fn(srv: ref Styxlib->Styxserver, c: ref Styxlib->Chan,
+			tab: array of Styxlib->Dirtab, i: int): (int, Sys->Dir);
+};
--- /dev/null
+++ b/module/styxpersist.m
@@ -1,0 +1,4 @@
+Styxpersist: module {
+	PATH: con "/dis/lib/styxpersist.dis";
+	init: fn(clientfd: ref Sys->FD, usefac: int, keyspec: string): (chan of chan of ref Sys->FD, string);
+};
--- /dev/null
+++ b/module/styxservers.m
@@ -1,0 +1,136 @@
+Styxservers: module
+{
+	PATH: con "/dis/lib/styxservers.dis";
+
+	Fid: adt {
+		fid:		int;		# client's fid
+		path:		big;		# file's 64-bit unique path
+		qtype:	int;		# file's qid type (eg, Sys->QTDIR if directory)
+		isopen:	int;		# non-zero if file is open
+		mode:	int;		# if open, the open mode
+		doffset:	(int, int);	# (internal) cache of directory offset
+		uname:	string;	# user name from original attach
+		param:	string;	# attach aname from original attach
+		data:		array of byte;	# application data
+
+		clone:	fn(f: self ref Fid, nf: ref Fid): ref Fid;
+		open:	fn(f: self ref Fid, mode: int, qid: Sys->Qid);
+		walk:	fn(f: self ref Fid, qid: Sys->Qid);
+	};
+
+	Navigator: adt {
+		c:		chan of ref Navop;
+		reply:	chan of (ref Sys->Dir, string);
+
+		new:		fn(c: chan of ref Navop): ref Navigator;
+		stat:		fn(t: self ref Navigator, q: big): (ref Sys->Dir, string);
+		walk:	fn(t: self ref Navigator, parentq: big, name: string): (ref Sys->Dir, string);
+		readdir:	fn(t: self ref Navigator, q: big, offset, count: int): array of ref Sys->Dir;
+	};
+
+	Navop: adt {
+		reply:	chan of (ref Sys->Dir, string);	# channel for reply
+		path:		big;		# file or directory path
+		pick {
+		Stat =>
+		Walk =>
+			name: string;
+		Readdir =>
+			offset:	int;	# index (origin 0) of first directory entry to return
+			count: 	int;	# number of directory entries requested
+		}
+	};
+
+	Styxserver: adt {
+		fd:		ref Sys->FD;		# file server end of connection
+		fids:		array of list of ref Fid;	# hash table of fids
+		fidlock:	chan of int;
+		t:		ref Navigator;	# name space navigator for this server
+		rootpath:	big;		# Qid.path of root of its name space
+		msize:	int;		# negotiated Styx message size
+		replychan:	chan of ref Styx->Rmsg;
+
+		new:		fn(fd: ref Sys->FD, t: ref Navigator, rootpath: big): (chan of ref Styx->Tmsg, ref Styxserver);
+		reply:	fn(srv: self ref Styxserver, m: ref Styx->Rmsg): int;
+		replydirect:	fn(srv: self ref Styxserver, m: ref Styx->Rmsg): int;
+		error:	fn(srv: self ref Styxserver, m: ref Styx->Tmsg, msg: string);
+
+		# protocol operations
+		attach:	fn(srv: self ref Styxserver, m: ref Styx->Tmsg.Attach): ref Fid;
+		clunk:	fn(srv: self ref Styxserver, m: ref Styx->Tmsg.Clunk): ref Fid;
+		walk:	fn(srv: self ref Styxserver, m: ref Styx->Tmsg.Walk): ref Fid;
+		open:	fn(srv: self ref Styxserver, m: ref Styx->Tmsg.Open): ref Fid;
+		read:		fn(srv: self ref Styxserver, m: ref Styx->Tmsg.Read): ref Fid;
+		remove:	fn(srv: self ref Styxserver, m: ref Styx->Tmsg.Remove): ref Fid;
+		stat:		fn(srv: self ref Styxserver, m: ref Styx->Tmsg.Stat);
+
+		default:	fn(srv: self ref Styxserver, gm: ref Styx->Tmsg);
+
+		# check validity but don't reply
+		cancreate:	fn(srv: self ref Styxserver, m: ref Styx->Tmsg.Create): (ref Fid, int, ref Sys->Dir, string);
+		canopen:		fn(srv: self ref Styxserver, m: ref Styx->Tmsg.Open): (ref Fid, int, ref Sys->Dir, string);
+		canremove:	fn(srv: self ref Styxserver, m: ref Styx->Tmsg.Remove): (ref Fid, big, string);
+		canread:		fn(srv: self ref Styxserver, m: ref Styx->Tmsg.Read): (ref Fid, string);
+		canwrite:		fn(srv: self ref Styxserver, m: ref Styx->Tmsg.Write): (ref Fid, string);
+
+		# fid management
+		getfid:	fn(srv: self ref Styxserver, fid: int): ref Fid;
+		newfid:	fn(srv: self ref Styxserver, fid: int): ref Fid;
+		delfid:	fn(srv: self ref Styxserver, c: ref Fid);
+		allfids:	fn(srv: self ref Styxserver): list of ref Fid;
+
+		iounit:	fn(srv: self ref Styxserver): int;
+	};
+
+	init:		fn(styx: Styx);
+	traceset:	fn(on: int);
+
+	readbytes: fn(m: ref Styx->Tmsg.Read, d: array of byte): ref Styx->Rmsg.Read;
+	readstr: fn(m: ref Styx->Tmsg.Read, s: string): ref Styx->Rmsg.Read;
+
+	openok:	fn(uname: string, omode, perm: int, fuid, fgid: string): int;
+	openmode: fn(o: int): int;
+	
+	Einuse:	con "fid already in use";
+	Ebadfid:	con "bad fid";
+	Eopen:	con "fid already opened";
+	Enotfound:	con "file does not exist";
+	Enotdir:	con "not a directory";
+	Eperm:	con "permission denied";
+	Ebadarg:	con "bad argument";
+	Eexists:	con "file already exists";
+	Emode:	con "open/create -- unknown mode";
+	Eoffset:	con "read/write -- bad offset";
+	Ecount:	con "read/write -- count negative or exceeds msgsize";
+	Enotopen: con "read/write -- on non open fid";
+	Eaccess:	con "read/write -- not open in suitable mode";
+	Ename:		con "bad character in file name";
+	Edot:		con ". and .. are illegal names";
+};
+
+Nametree: module {
+	PATH: con "/dis/lib/nametree.dis";
+	Tree: adt {
+		c:		chan of ref Treeop;
+		reply:	chan of string;
+
+		quit:		fn(t: self ref Tree);
+		create:	fn(t: self ref Tree, parent: big, d: Sys->Dir): string;
+		wstat:	fn(t: self ref Tree, path: big, d: Sys->Dir): string;
+		remove:	fn(t: self ref Tree, path: big): string;
+		getpath:	fn(t: self ref Tree, path: big): string;
+	};
+	Treeop: adt {
+		reply: chan of string;
+		q: big;
+		pick {
+		Create or
+		Wstat =>
+			d: Sys->Dir;
+		Remove =>
+		Getpath =>
+		}
+	};
+	init:		fn();
+	start:		fn(): (ref Tree, chan of ref Styxservers->Navop);
+};
--- /dev/null
+++ b/module/sys.m
@@ -1,0 +1,163 @@
+
+SELF:	con	"$self";		# Language support for loading my instance
+
+Sys: module
+{
+	PATH:	con	"$Sys";
+
+	Maxint:	con	2147483647;
+
+	# Unique file identifier for file objects
+	Qid: adt
+	{
+		path:	big;
+		vers:	int;
+		qtype:	int;
+	};
+
+	QTDIR:	con 16r80;
+	QTAPPEND:	con 16r40;
+	QTEXCL:	con 16r20;
+	QTAUTH:	con 16r08;
+	QTTMP:	con 16r04;
+	QTFILE:	con 0;
+
+	# Return from stat and directory read
+	Dir: adt
+	{
+		name:	string;
+		uid:	string;
+		gid:	string;
+		muid:	string;
+		qid:	Qid;
+		mode:	int;
+		atime:	int;
+		mtime:	int;
+		length:	big;
+		dtype:	int;
+		dev:	int;
+	};
+	nulldir:	con Dir(nil, nil, nil, nil, (~big 0, ~0, ~0), ~0, ~0, ~0, ~big 0, ~0, ~0);
+	zerodir:	con Dir(nil, nil, nil, nil, (big 0, 0, 0), 0, 0, 0, big 0, 0, 0);
+
+	# File descriptor
+	#
+	FD: adt
+	{
+		fd:	int;
+	};
+
+	# Network connection returned by dial
+	#
+	Connection: adt
+	{
+		dfd:	ref FD;
+		cfd:	ref FD;
+		dir:	string;
+	};
+
+	# File IO structures returned from file2chan
+	# read:  (offset, bytes, fid, chan)
+	# write: (offset, data, fid, chan)
+	#
+	Rread:	type chan of (array of byte, string);
+	Rwrite:	type chan of (int, string);
+	FileIO: adt
+	{
+		read:	chan of (int, int, int, Rread);
+		write:	chan of (int, array of byte, int, Rwrite);
+	};
+
+	# Maximum read which will be completed atomically;
+	# also the optimum block size
+	#
+	ATOMICIO:	con 8192;
+
+	SEEKSTART:	con 0;
+	SEEKRELA:	con 1;
+	SEEKEND:	con 2;
+
+	NAMEMAX:	con 256;
+	ERRMAX:		con 128;
+	WAITLEN:	con ERRMAX+64;
+
+	OREAD:		con 0;
+	OWRITE:		con 1;
+	ORDWR:		con 2;
+	OTRUNC:		con 16;
+	ORCLOSE:	con 64;
+	OEXCL:		con 16r1000;
+
+	DMDIR:		con int 1<<31;
+	DMAPPEND:	con int 1<<30;
+	DMEXCL:		con int 1<<29;
+	DMAUTH:		con int 1<<27;
+	DMTMP:		con int 1<<26;
+
+	MREPL:		con 0;
+	MBEFORE:	con 1;
+	MAFTER:		con 2;
+	MCREATE:	con 4;
+	MCACHE:		con 16;
+
+	NEWFD:		con (1<<0);
+	FORKFD:		con (1<<1);
+	NEWNS:		con (1<<2);
+	FORKNS:		con (1<<3);
+	NEWPGRP:	con (1<<4);
+	NODEVS:		con (1<<5);
+	NEWENV:		con (1<<6);
+	FORKENV:	con (1<<7);
+
+	EXPWAIT:	con 0;
+	EXPASYNC:	con 1;
+
+	UTFmax:		con 4;	# maximum bytes per rune in UTF-8
+	UTFerror:	con 16rFFFD;	# decoding error in UTF
+	Runemax:	con 16r10FFFF;	# 21-bit rune
+	Runemask:	con 16r1FFFFF;	# bits used by runes
+
+	announce:	fn(addr: string): (int, Connection);
+	aprint:		fn(s: string, *): array of byte;
+	bind:		fn(s, on: string, flags: int): int;
+	byte2char:	fn(buf: array of byte, n: int): (int, int, int);
+	char2byte:	fn(c: int, buf: array of byte, n: int): int;
+	chdir:		fn(path: string): int;
+	create:		fn(s: string, mode, perm: int): ref FD;
+	dial:		fn(addr, local: string): (int, Connection);
+	dirread:	fn(fd: ref FD): (int, array of Dir);
+	dup:		fn(old, new: int): int;
+	export:		fn(c: ref FD, dir: string, flag: int): int;
+	fauth:		fn(fd: ref FD, aname: string): ref FD;
+	fd2path:	fn(fd: ref FD): string;
+	fildes:		fn(fd: int): ref FD;
+	file2chan:	fn(dir, file: string): ref FileIO;
+	fprint:		fn(fd: ref FD, s: string, *): int;
+	fstat:		fn(fd: ref FD): (int, Dir);
+	fversion:	fn(fd: ref FD, msize: int, version: string): (int, string);
+	fwstat:		fn(fd: ref FD, d: Dir): int;
+	iounit:		fn(fd: ref FD): int;
+	listen:		fn(c: Connection): (int, Connection);
+	millisec:	fn(): int;
+	mount:		fn(fd: ref FD, afd: ref FD, on: string, flags: int, spec: string): int;
+	open:		fn(s: string, mode: int): ref FD;
+	pctl:		fn(flags: int, movefd: list of int): int;
+	pipe:		fn(fds: array of ref FD): int;
+	print:		fn(s: string, *): int;
+	pread:	fn(fd: ref FD, buf: array of byte, n: int, off: big): int;
+	pwrite:	fn(fd: ref FD, buf: array of byte, n: int, off: big): int;
+	read:		fn(fd: ref FD, buf: array of byte, n: int): int;
+	readn:		fn(fd: ref FD, buf: array of byte, n: int): int;
+	remove:		fn(s: string): int;
+	seek:		fn(fd: ref FD, off: big, start: int): big;
+	sleep:		fn(period: int): int;
+	sprint:		fn(s: string, *): string;
+	stat:		fn(s: string): (int, Dir);
+	stream:		fn(src, dst: ref FD, bufsiz: int): int;
+	tokenize:	fn(s, delim: string): (int, list of string);
+	unmount:	fn(s1: string, s2: string): int;
+	utfbytes:	fn(buf: array of byte, n: int): int;
+	werrstr:	fn(s: string): int;
+	write:		fn(fd: ref FD, buf: array of byte, n: int): int;
+	wstat:		fn(s: string, d: Dir): int;
+};
--- /dev/null
+++ b/module/tables.m
@@ -1,0 +1,24 @@
+Tables: module {
+	PATH: con "/dis/lib/tables.dis";
+	Table: adt[T] {
+		items:	array of list of (int, T);
+		nilval:	T;
+	
+		new: fn(nslots: int, nilval: T): ref Table[T];
+		add:	fn(t: self ref Table, id: int, x: T): int;
+		del:	fn(t: self ref Table, id: int): int;
+		find:	fn(t: self ref Table, id: int): T;
+	};
+	
+	Strhash: adt[T] {
+		items:	array of list of (string, T);
+		nilval:	T;
+	
+		new: fn(nslots: int, nilval: T): ref Strhash[T];
+		add:	fn(t: self ref Strhash, id: string, x: T);
+		del:	fn(t: self ref Strhash, id: string);
+		find:	fn(t: self ref Strhash, id: string): T;
+	};
+
+	hash: fn(s: string, n: int): int;
+};
--- /dev/null
+++ b/module/tabs.m
@@ -1,0 +1,16 @@
+Tabs: module
+{
+	PATH:	con "/dis/lib/tabs.dis";
+
+	init:	fn();
+
+	mktabs:		fn(t: ref Tk->Toplevel, dot: string,
+				tabs: array of (string, string),
+				dflt: int): chan of string;
+
+	tabsctl:	fn(t: ref Tk->Toplevel,
+				dot: string,
+				tabs: array of (string, string),
+				id: int,
+				s: string): int;
+};
--- /dev/null
+++ b/module/tcllib.m
@@ -1,0 +1,6 @@
+TclLib : module
+{
+	exec : fn(tcl : ref Tcl_Core->TclData,argv : array of string) : 
+			(int,string);
+	about : fn() : array of string;
+};
--- /dev/null
+++ b/module/tftp.m
@@ -1,0 +1,6 @@
+Tftp: module
+{
+	PATH: con "/dis/lib/tftp.dis";
+	init: fn(progress: int);
+	receive: fn(host: string, filename: string, fd: ref Sys->FD): string;
+};
--- /dev/null
+++ b/module/timers.m
@@ -1,0 +1,17 @@
+Timers: module
+{
+	PATH: con "/dis/lib/timers.dis";
+
+	Sec: con 1000;
+
+	Timer: adt {
+		dt:	int;
+		timeout:	chan of int;
+
+		start:	fn(msec: int): ref Timer;
+		stop:	fn(t: self ref Timer);
+	};
+
+	init:	fn(gran: int): int;
+	shutdown:	fn();
+};
--- /dev/null
+++ b/module/titlebar.m
@@ -1,0 +1,18 @@
+Titlebar: module{
+	PATH: con "/dis/lib/titlebar.dis";
+
+	Resize,
+	Hide,
+	Help,
+	OK,
+	Popup,
+	Plain:		con 1 << iota;
+	Appl:		con Resize | Hide;
+
+	init:	fn();
+	new:		fn(top: ref Tk->Toplevel, buts: int): chan of string;
+	minsize:	fn(top: ref Tk->Toplevel): Draw->Point;
+	title:		fn(top: ref Tk->Toplevel): string;
+	settitle:	fn(top: ref Tk->Toplevel, title: string): string;
+	sendctl:	fn(top: ref Tk->Toplevel, c: string);
+};
--- /dev/null
+++ b/module/tk.m
@@ -1,0 +1,25 @@
+Tk: module
+{
+	PATH:	con	"$Tk";
+
+	Toplevel: adt
+	{
+		display:	ref Draw->Display;
+		wreq:	chan of string;
+		image:	ref Draw->Image;
+		ctxt:		ref Draw->Wmcontext;	# placeholder, not used by tk
+		screenr:	Draw->Rect;			# writable
+	};
+	Border, Required, Local: con 1<<iota;
+	rect:			fn(t: ref Toplevel, name: string, flags: int): Draw->Rect;
+
+	toplevel:		fn(d: ref Draw->Display, arg: string): ref Toplevel;
+	namechan:	fn(t: ref Toplevel, c: chan of string, n: string): string;
+	cmd:			fn(t: ref Toplevel, arg: string): string;
+	pointer:		fn(t: ref Toplevel, p: Draw->Pointer);
+	keyboard:		fn(t: ref Toplevel, key: int);
+	putimage:		fn(t: ref Toplevel, name: string, i, m: ref Draw->Image): string;
+	getimage:		fn(t: ref Toplevel, name: string): (ref Draw->Image, ref Draw->Image, string);
+	quote:		fn(s: string): string;
+	color:		fn(col: string): int;
+};
--- /dev/null
+++ b/module/tkclient.m
@@ -1,0 +1,26 @@
+Tkclient: module
+{
+	PATH:		con "/dis/lib/tkclient.dis";
+
+	Resize,
+	Hide,
+	Help,
+	OK,
+	Popup,		# XXX is this useful?
+	Plain:		con 1 << iota;
+
+	Appl:		con Resize | Hide;
+
+	init:		fn();
+	makedrawcontext: fn():	ref Draw->Context;
+	toplevel:	fn(ctxt: ref Draw->Context, topconfig: string,
+				title: string, buts: int): (ref Tk->Toplevel, chan of string);
+	onscreen:		fn(top: ref Tk->Toplevel, how: string);
+	startinput:		fn(top: ref Tk->Toplevel, devs: list of string);
+	wmctl:		fn(top: ref Tk->Toplevel, request: string): string;
+	settitle:		fn(top: ref Tk->Toplevel, name: string): string;
+	handler:		fn(top: ref Tk->Toplevel, stop: chan of int);
+
+	snarfput:	fn(buf: string);
+	snarfget:	fn(): string;
+};
--- /dev/null
+++ b/module/translate.m
@@ -1,0 +1,30 @@
+#
+# Copyright © 2000 Vita Nuova Limited
+#
+Translate: module
+{
+	PATH:	con "/dis/lib/translate.dis";
+
+	Dict: adt {
+		texts:	array of list of ref Phrase;
+		notes:	array of list of ref Phrase;
+
+		new:		fn(): ref Dict;
+		add:		fn(d: self ref Dict, file: string): string;
+		xlate:	fn(d: self ref Dict, nil: string): string;
+		xlaten:	fn(d: self ref Dict, nil: string, note: string): string;
+	};
+
+	Phrase: adt {
+		key:	string;
+		text:	string;	# nil for a note
+		hash:	int;
+		n:	int;
+		note:	int;
+	};
+
+	init:	fn();
+	opendict:	fn(file: string): (ref Dict, string);
+	opendicts: fn(files: list of string): (ref Dict, string);
+	mkdictname:	fn(locale, app: string): string;
+};
--- /dev/null
+++ b/module/ubfa.m
@@ -1,0 +1,57 @@
+UBFa: module
+{
+	PATH:	con "/dis/lib/ubfa.dis";
+
+	UValue: adt {
+		pick{
+		Atom =>
+			name: string;
+		Int =>
+			value:	int;	# should have big as well?
+		String =>
+			s: string;
+		Binary =>
+			a: array of byte;
+		Tuple =>
+			a: cyclic array of ref UValue;	# tree
+		List =>
+			l: cyclic list of ref UValue;	# tree
+		Tag =>
+			name:	string;
+			o:	cyclic ref UValue;
+		}
+
+		isatom:	fn(o: self ref UValue): int;
+		isstring:	fn(o: self ref UValue): int;
+		isint:		fn(o: self ref UValue): int;
+		istuple:	fn(o: self ref UValue): int;
+		isop: 	fn(o: self ref UValue, op: string, arity: int): int;
+		islist:		fn(o: self ref UValue): int;
+		isbinary:	fn(o: self ref UValue): int;
+		istag:	fn(o: self ref UValue): int;
+		text:		fn(o: self ref UValue): string;
+		eq:		fn(o: self ref UValue, v: ref UValue): int;
+		op:		fn(o: self ref UValue, arity: int): string;
+		args:		fn(o: self ref UValue, arity: int): array of ref UValue;
+		els:		fn(o: self ref UValue): list of ref UValue;
+		val:		fn(o: self ref UValue): int;
+		binary:	fn(o: self ref UValue): array of byte;
+		objtag:	fn(o: self ref UValue): string;
+		obj:		fn(o: self ref UValue): ref UValue;
+	};
+
+	init:	fn(bufio: Bufio);
+	readubf:	fn(input: ref Iobuf): (ref UValue, string);
+	writeubf:	fn(out: ref Iobuf, obj: ref UValue): int;
+	uniq:	fn(s: string): string;
+
+	# shorthand
+	uvatom:	fn(s: string): ref UValue.Atom;
+	uvint:	fn(i: int): ref UValue.Int;
+	uvbig:	fn(i: big): ref UValue.Int;
+	uvstring:	fn(s: string): ref UValue.String;
+	uvbinary:	fn(a: array of byte): ref UValue.Binary;
+	uvtuple:	fn(a: array of ref UValue): ref UValue.Tuple;
+	uvlist:	fn(l: list of ref UValue): ref UValue.List;
+	uvtag:	fn(name: string, o: ref UValue): ref UValue.Tag;
+};
--- /dev/null
+++ b/module/unbundle.m
@@ -1,0 +1,9 @@
+Unbundle: module {
+	PATH: con "/dis/fs/bundle.dis";
+
+	types: fn(): string;
+	init: fn();
+	run: fn(nil: ref Draw->Context, report: ref Report,
+			nil: list of Option, args: list of ref Value): ref Value;
+	unbundle:	fn(r: ref Fslib->Report, iob: ref Bufio->Iobuf, seekable: int, blocksize: int): Fslib->Fschan;
+};
--- /dev/null
+++ b/module/uris.m
@@ -1,0 +1,36 @@
+URIs: module
+{
+	PATH: con "/dis/lib/w3c/uris.dis";
+
+	# URI Generic Syntax (RFC 3986)
+	#
+	#	scheme://authority/path?query#fragment
+	#
+	URI: adt
+	{
+		scheme:	string;
+		userinfo:	string;	# authority, part I
+		host:		string;	# authority, part II
+		port:		string;	# authority, part III
+		path:		string;	# starts with / if path-abempty or path-absolute
+		query:	string;	# includes ? if not nil
+		fragment:	string;	# includes # if not nil
+
+		parse:	fn(s: string): ref URI;
+		text: fn(u: self ref URI): string;
+		authority:	fn(u: self ref URI): string;
+		addbase:	fn(u: self ref URI, base: ref URI): ref URI;
+		copy:	fn(u: self ref URI): ref URI;
+		hasauthority:	fn(u: self ref URI): int;
+		isabsolute:	fn(u: self ref URI): int;
+		nodots:	fn(u: self ref URI): ref URI;
+		pathonly:	fn(u: self ref URI): ref URI;
+		userpw:	fn(u: self ref URI): (string, string);	# ``deprecated format''
+		eq:	fn(u: self ref URI, v: ref URI): int;
+		eqf:	fn(u: self ref URI, v: ref URI): int;
+	};
+
+	init:	fn();
+	dec:	fn(s: string): string;
+	enc:	fn(s: string, safe: string): string;
+};
--- /dev/null
+++ b/module/url.m
@@ -1,0 +1,34 @@
+Url: module
+{
+	PATH : con "/dis/lib/url.dis";
+
+	# scheme ids
+	NOSCHEME, HTTP, HTTPS, FTP, FILE, GOPHER, MAILTO, NEWS,
+		NNTP, TELNET, WAIS, PROSPERO, JAVASCRIPT, UNKNOWN: con iota;
+
+	# general url syntax:
+	#    <scheme>://<user>:<passwd>@<host>:<port>/<path>?<query>#<fragment>
+	#
+	# relative urls might omit some prefix of the above
+	ParsedUrl: adt
+	{
+		scheme:	int;
+		utf8:		int;		# strings not in us-ascii
+		user:		string;
+		passwd:	string;
+		host:		string;
+		port:		string;
+		pstart:	string;	# what precedes <path>: either "/" or ""
+		path:		string;
+		query:	string;
+		frag:		string;
+
+		makeabsolute: fn(url: self ref ParsedUrl, base: ref ParsedUrl);
+		tostring: fn(url: self ref ParsedUrl) : string;
+	};
+
+	schemes: array of string;
+
+	init: fn();	# call before anything else
+	makeurl: fn(s: string) : ref ParsedUrl;
+};
--- /dev/null
+++ b/module/usb.m
@@ -1,0 +1,130 @@
+Usb: module
+{
+	PATH: con "/dis/lib/usb/usb.dis";
+	DATABASEPATH: con "/lib/usbdb";
+	RH2D: con 0<<7;
+	RD2H: con 1<<7;
+	Rstandard: con 0<<5;
+	Rclass: con 1<<5;
+	Rvendor: con 2<<5;
+	Rdevice: con 0;
+	Rinterface: con 1;
+	Rendpt: con 2;
+	Rother: con 3;
+	
+	GET_STATUS: con 0;
+	CLEAR_FEATURE: con 1;
+	SET_FEATURE: con 3;
+	SET_ADDRESS: con 5;
+	GET_DESCRIPTOR: con 6;
+	SET_DESCRIPTOR: con 7;
+	GET_CONFIGURATION: con 8;
+	SET_CONFIGURATION: con 9;
+	GET_INTERFACE: con 10;
+	SET_INTERFACE: con 11;
+	SYNCH_FRAME: con 12;
+	
+	DEVICE: con 1;
+	CONFIGURATION: con 2;
+	STRING: con 3;
+	INTERFACE: con 4;
+	ENDPOINT: con 5;
+	HID: con 16r21;
+	REPORT: con 16r22;
+	PHYSICAL: con 16r23;
+	HUB: con 16r29;
+
+	CL_AUDIO: con 1;
+	CL_COMMS: con 2;
+	CL_HID: con 3;
+	CL_PRINTER: con 7;
+	CL_MASS: con 8;
+	CL_HUB: con 9;
+	CL_DATA: con 10;
+
+	DDEVLEN: con 18;
+	DCONFLEN: con 9;
+	DINTERLEN: con 9;
+	DENDPLEN: con 7;
+	DHUBLEN: con 9;
+	DHIDLEN: con 9;
+
+	PORT_CONNECTION: con 0;
+	PORT_ENABLE: con 1;
+	PORT_SUSPEND: con 2;
+	PORT_OVER_CURRENT: con 3;
+	PORT_RESET: con 4;
+	PORT_POWER: con 8;
+	PORT_LOW_SPEED: con 9;
+
+	Endpt: adt {
+		addr: int;
+		d2h:	int;
+		attr:	int;
+		etype:	int;
+		isotype:	int;
+		maxpkt: int;
+		interval: int;
+	};
+
+	Econtrol, Eiso, Ebulk, Eintr: con iota;	# Endpt.etype
+	Eunknown, Easync, Eadapt, Esync: con iota;	# Endpt.isotype
+	
+	NendPt: con 16;
+	
+	Device: adt {
+		usbmajor, usbminor, relmajor, relminor: int;
+		class, subclass, proto, maxpkt0, vid, did, nconf: int;
+	};
+
+	AltInterface: adt {
+		id: int;
+		class, subclass, proto: int;
+		ep: array of ref Endpt;
+	};
+
+	Interface: adt {
+		altiface: list of ref AltInterface;
+	};
+
+	Configuration: adt {
+		id: int;
+		attr: int;
+		powerma: int;
+		iface: array of Interface;
+	};
+
+	init: fn();
+	get2: fn(b: array of byte): int;
+	put2: fn(buf: array of byte, v: int);
+	get4: fn(b: array of byte): int;
+	put4: fn(buf: array of byte, v: int);
+	bigget2: fn(b: array of byte): int;
+	bigput2: fn(buf: array of byte, v: int);
+	bigget4: fn(b: array of byte): int;
+	bigput4: fn(buf: array of byte, v: int);
+	memset: fn(b: array of byte, v: int);
+	strtol: fn(s: string, base: int): (int, string);
+	sclass: fn(class, subclass, proto: int): string;
+	
+	get_descriptor: fn(fd: ref Sys->FD, rtyp: int, dtyp: int, dindex: int, langid: int, buf: array of byte): int;
+	get_standard_descriptor: fn(fd: ref Sys->FD, dtyp: int, index: int, buf: array of byte): int;
+	get_class_descriptor: fn(fd: ref Sys->FD, dtyp: int, index: int, buf: array of byte): int;
+	get_vendor_descriptor: fn(fd: ref Sys->FD, dtyp: int, index: int, buf: array of byte): int;
+	get_status: fn(fd: ref Sys->FD, port: int): int;
+	set_address: fn(fd: ref Sys->FD, address: int): int;
+	set_configuration: fn(fd: ref Sys->FD, n: int): int;
+	setclear_feature: fn(fd: ref Sys->FD, rtyp: int, value: int, index: int, on: int): int;
+	setup: fn(setupfd: ref Sys->FD, typ, req, value, index: int, outbuf: array of byte, inbuf: array of byte): int;
+	get_parsed_configuration_descriptor: fn(fd: ref Sys->FD, n: int): ref Configuration;
+	get_parsed_device_descriptor: fn(fd: ref Sys->FD): ref Device;
+
+	dump_configuration: fn(fd: ref Sys->FD, conf: ref Configuration);
+};
+
+UsbDriver: module
+{
+	MOUSEPATH: con "/appl/cmd/usb/usbmouse.dis";
+	init: fn(usb: Usb, setupfd, ctlfd: ref Sys->FD, dev: ref Usb->Device, conf: array of ref Usb->Configuration, path: string): int;
+	shutdown: fn();
+};
--- /dev/null
+++ b/module/vac.m
@@ -1,0 +1,160 @@
+Vac: module {
+	PATH:	con "/dis/lib/vac.dis";
+	init:	fn();
+
+	dflag:	int;
+
+	# mode bits
+	Modeperm: con 8r777;
+	Modesticky,
+	Modesetuid,
+	Modesetgid,
+	Modeappend,
+	Modeexcl,
+	Modesymlink,
+	Modedir,
+	Modehidden,
+	Modesystem,
+	Modearchive,
+	Modetemp,
+	Modesnapshot,
+	Modedev,
+	Modenamedpipe: con 1<<(9+iota);
+
+	Metablocksize:	con 12;
+	Metaentrysize:	con 4;
+
+	Dsize:	con 8*1024;
+
+	# DirPlan9, DirNT & DirGen not valid in version >= 9
+	DirPlan9,
+	DirNT,
+	DirQidspace,
+	DirGen: con 1+iota;
+
+	Direntry: adt {
+		version:	int;
+		elem:	string;
+		entry, gen:	int;
+		mentry, mgen:	int;
+		qid:	big;
+		uid, gid, mid:	string;
+		mtime, mcount, ctime, atime, mode, emode: int;
+		qidspace:	int;
+		qidoff:		big;
+		qidmax:		big;
+
+		new:	fn(): ref Direntry;
+		mk:	fn(d: Sys->Dir): ref Direntry;
+		mkdir:	fn(de: self ref Direntry): ref Sys->Dir;
+		pack:	fn(de: self ref Direntry): array of byte;
+		unpack:	fn(d: array of byte): ref Direntry;
+	};
+
+	Metablock: adt {
+		size, free, maxindex, nindex:	int;
+
+		new:	fn(): ref Metablock;
+		pack:	fn(mb: self ref Metablock, d: array of byte);
+		unpack:	fn(d: array of byte): ref Metablock;
+	};
+
+	Metaentry: adt {
+		offset, size:	int;
+
+		pack:	fn(me: self ref Metaentry, d: array of byte);
+		unpack:	fn(d: array of byte, i: int): ref Metaentry;
+	};
+
+	# single block
+	Page: adt {
+		d:	array of byte;
+		o:	int;
+
+		new:	fn(dsize: int): ref Page;
+		add:	fn(p: self ref Page, s: Venti->Score);
+		full:	fn(p: self ref Page): int;
+		data:	fn(p: self ref Page): array of byte;
+	};
+
+	# hash tree file
+	File: adt {
+		p:	array of ref Page;
+		dtype, dsize:	int;
+		size:	big;
+		s:	ref Venti->Session;
+
+		new:	fn(s: ref Venti->Session, dtype, dsize: int): ref File;
+		write:	fn(f: self ref File, d: array of byte): int;
+		finish:	fn(f: self ref File): ref Venti->Entry;
+	};
+
+	# for writing venti directories
+	Sink: adt {
+		f:	ref File;
+		d:	array of byte;
+		nd, ne:	int;
+
+		new:	fn(s: ref Venti->Session, dsize: int): ref Sink;
+		add:	fn(m: self ref Sink, e: ref Venti->Entry): int;
+		finish:	fn(m: self ref Sink): ref Venti->Entry;
+	};
+
+	Mentry: adt {
+		elem:	string;
+		me:	ref Metaentry;
+
+		cmp:	fn(a, b: ref Mentry): int;
+	};
+
+	# for writing directory entries (meta blocks, meta entries, direntries)
+	MSink: adt {
+		f: 	ref File;
+		de:	array of byte;
+		nde:	int;
+		l:	list of ref Mentry;
+
+		new:	fn(s: ref Venti->Session, dsize: int): ref MSink;
+		add:	fn(m: self ref MSink, de: ref Direntry): int;
+		finish:	fn(m: self ref MSink): ref Venti->Entry;
+	};
+
+	# for reading pages from a hash tree referenced by an entry
+	Source: adt {
+		session:	ref Venti->Session;
+		e:	ref Venti->Entry;
+		dsize:	int;  # real dsize
+
+		new:	fn(s: ref Venti->Session, e: ref Venti->Entry): ref Source;
+		get:	fn(s: self ref Source, i: big, d: array of byte): int;
+	};
+
+	# for reading from a hash tree while keeping offset
+	Vacfile: adt {
+		s:	ref Source;
+		o:	big;
+
+		mk:	fn(s: ref Source): ref Vacfile;
+		new:	fn(session: ref Venti->Session, e: ref Venti->Entry): ref Vacfile;
+		read:	fn(v: self ref Vacfile, d: array of byte, n: int): int;
+		seek:	fn(v: self ref Vacfile, offset: big): big;
+		pread:	fn(v: self ref Vacfile, d: array of byte, n: int, offset: big): int;
+	};
+
+	# for listing contents of a vac directory and walking to path elements
+	Vacdir: adt {
+		vf:	ref Vacfile;
+		ms:	ref Source;
+		p:	big;
+		i:	int;
+
+		mk:	fn(vf: ref Vacfile, ms: ref Source): ref Vacdir;
+		new:	fn(session: ref Venti->Session, e, me: ref Venti->Entry): ref Vacdir;
+		walk:	fn(v: self ref Vacdir, elem: string): ref Direntry;
+		open:	fn(v: self ref Vacdir, de: ref Direntry): (ref Venti->Entry, ref Venti->Entry);
+		readdir:	fn(v: self ref Vacdir): (int, ref Direntry);
+		rewind:		fn(v: self ref Vacdir);
+	};
+
+	vdroot:	fn(session: ref Venti->Session, score: Venti->Score): (ref Vacdir, ref Direntry, string);
+};
--- /dev/null
+++ b/module/venti.m
@@ -1,0 +1,163 @@
+Venti: module {
+	PATH:	con "/dis/lib/venti.dis";
+	Scoresize:		con 20;
+	Maxstringsize:	con 1000;
+	Authsize:		con  1024;  	# size of auth group - in bits - must be multiple of 8	
+	Maxfragsize:	con 9*1024;
+
+	Cryptostrengthnone,
+	Cryptostrengthauth,
+	Cryptostrengthweak,
+	Cryptostrengthstrong:	con iota;
+
+	Cryptonone,
+	CryptoSSL3,
+	CryptoTLS1,
+	Cryptomax:	con iota;
+
+	Codecnone,
+	Codecdeflate,
+	CodecThwack,
+	Codecmax:	con iota;
+
+	Terror,		# not used
+	Rerror,
+	Tping,
+	Rping,
+	Thello,
+	Rhello,
+	Tgoodbye,
+	Rgoodbye,	# not used
+	Tauth0,
+	Rauth0,
+	Tauth1,
+	Rauth1,
+	Tread,
+	Rread,
+	Twrite,
+	Rwrite,
+	Tsync,
+	Rsync,
+	Tmax:		con iota;
+
+	# versions
+	Version01,
+	Version02:	con iota + 1;
+
+	# Lump Types
+	Errtype,		# illegal
+
+	Roottype,
+	Dirtype,
+	Pointertype0,
+	Pointertype1,
+	Pointertype2,
+	Pointertype3,
+	Pointertype4,
+	Pointertype5,
+	Pointertype6,
+	Pointertype7,		# not used
+	Pointertype8,		# not used
+	Pointertype9,		# not used
+	Datatype,
+
+	Maxtype:		con iota;
+
+	# Dir Entry flags
+	Entryactive:	con (1<<0);			# entry is in use
+	Entrydir:		con (1<<1);			# a directory
+	Entrydepthshift: con 2;				# shift for pointer depth
+	Entrydepthmask: con (16r7<<2);		# mask for pointer depth
+	Entrylocal: con (1<<5);				# used for local storage: should not be set for venti blocks
+
+	Maxlumpsize:	con 56 * 1024;
+	Pointerdepth:	con 7;
+	Entrysize:		con 40;
+	Rootsize:		con 300;
+	Rootversion:	con 2;
+
+	Maxfilesize:	con (big 1 << 48) - big 1;
+
+	Vmsg: adt {
+		istmsg:	int;
+		tid:		int;
+		pick {
+		Thello =>
+			version:	string;
+			uid:		string;
+			cryptostrength:	int;
+			cryptos:	array of byte;
+			codecs:	array of byte;
+		Rhello =>
+			sid:		string;
+			crypto:	int;
+			codec:	int;
+		Tping =>
+		Rping =>
+		Tread =>
+			score:	Score;
+			etype:	int;
+			n:		int;
+		Rread =>
+			data:		array of byte;
+		Twrite =>
+			etype:	int;
+			data:		array of byte;
+		Rwrite =>
+			score:	Score;
+		Tsync =>
+		Rsync =>
+		Tgoodbye =>
+		Rerror =>
+			e:		string;
+		}
+		read:			fn(fd: ref Sys->FD): (ref Vmsg, string);
+		unpack:		fn(a: array of byte): (int, ref Vmsg);
+		pack:		fn(nil: self ref Vmsg): array of byte;
+		packedsize:	fn(nil: self ref Vmsg): int;
+		text:			fn(nil: self ref Vmsg): string;
+	};
+
+	Root: adt {
+		version:	int;
+		name:	string;
+		rtype:	string;
+		score:	Venti->Score;		# to a Dir block
+		blocksize:	int;				# maximum block size
+		prev:		ref Venti->Score;		# last root block
+
+		pack:	fn(r: self ref Root): array of byte;
+		unpack:	fn(d: array of byte): ref Root;
+	};
+
+	Entry: adt {
+		gen:		int;		# generation number (XXX should be unsigned)
+		psize:	int;		# pointer block size
+		dsize:	int;		# data block size
+		depth:	int;		# unpacked from flags
+		flags:	int;
+		size:		big;		# (XXX should be unsigned)
+		score:	Venti->Score;
+
+		pack:	fn(e: self ref Entry): array of byte;
+		unpack:	fn(d: array of byte): ref Entry;
+	};
+	Score: adt {
+		a: array of byte;
+		eq:		fn(a: self Score, b: Score): int;
+		text:		fn(a: self Score): string;
+		parse:	fn(s: string): (int, Score);
+		zero:		fn(): Score;
+	};
+	Session: adt {
+		fd:		ref Sys->FD;
+		version:	string;
+
+		new:		fn(fd: ref Sys->FD): ref Session;
+		read:		fn(s: self ref Session, score: Venti->Score, etype: int, maxn: int): array of byte;
+		write:	fn(s: self ref Session, etype: int, buf: array of byte): (int, Venti->Score);
+		sync:	fn(s: self ref Session): int;
+		rpc:		fn(s: self ref Session, m: ref Vmsg): (ref Vmsg, string);
+	};
+	init:	fn();
+};
--- /dev/null
+++ b/module/volume.m
@@ -1,0 +1,18 @@
+Volumectl: module
+{
+	PATH:	con "/dis/lib/volume.dis";
+
+	# Volumectl should be spawned as a separate process from
+	# any process that desires volume control.  The parameters
+	# are a ref Context that provides volumectl with access to
+	# the display, a chan of int through which volumectl receives
+	# Ir->Enter, Ir->VolUP, or Ir->VolDN commands (others are
+	# ignored), and a string that names the specific volume to
+	# be controlled (typically "audio out").
+	# Volumectl exits upon receiving Ir->Enter.
+	# It displays a volume control slider when receiving either
+	# Ir->VolUP or Ir->VolDN.  The slider automatically disappears
+	# after a period of inactivity.
+
+	volumectl:	fn(ctxt: ref Draw->Context, ch: chan of int, device: string);
+};
--- /dev/null
+++ b/module/wait.m
@@ -1,0 +1,9 @@
+Wait: module
+{
+	PATH:	con "/dis/lib/wait.dis";
+
+	init:	fn();
+	read:	fn(fd: ref Sys->FD): (int, string, string);
+	monitor:	fn(fd: ref Sys->FD): (int, chan of (int, string, string));
+	parse:	fn(status: string): (int, string, string);
+};
--- /dev/null
+++ b/module/watchvars.m
@@ -1,0 +1,13 @@
+Watchvars: module {
+	PATH: con "/dis/lib/watchvars.dis";
+	Watchvar: adt[T] {
+		c: chan of (T, chan of T);
+
+		new:	fn(v: T): Watchvar[T];
+		get:	fn(e: self Watchvar[T]): T;
+		set:	fn(e: self Watchvar[T], v: T);
+		wait:	fn(e: self Watchvar[T]): T;
+		waitc:	fn(e: self Watchvar[T]): (T, chan of T);
+		waited:	fn(e: self Watchvar[T], ic: chan of T, v: T);
+	};
+};
--- /dev/null
+++ b/module/webget.m
@@ -1,0 +1,7 @@
+Webget: module
+{
+	PATH: con "/dis/svc/webget/webget.dis";
+
+	init: fn(ctxt: ref Draw->Context, argv: list of string);
+	start: fn(ctl: chan of int);
+};
--- /dev/null
+++ b/module/winplace.m
@@ -1,0 +1,6 @@
+Winplace: module {
+	PATH: con "/dis/lib/winplace.dis";
+	init: fn();
+	place: fn(wins: list of Draw->Rect, scr, lastrect: Draw->Rect, minsize: Draw->Point): Draw->Rect;
+	find: fn(wins: list of Draw->Rect, scr: Draw->Rect): list of Draw->Rect;
+};
--- /dev/null
+++ b/module/wmclient.m
@@ -1,0 +1,49 @@
+Wmclient: module
+{
+	PATH:		con "/dis/lib/wmclient.dis";
+
+	Resize,
+	Hide,
+	Help,
+	OK,
+	Popup,
+	Plain:		con 1 << iota;
+	Appl:		con Resize | Hide;
+
+	init:		fn();
+	makedrawcontext: fn(): ref Draw->Context;
+	window:		fn(ctxt: ref Draw->Context, title: string, buts: int): ref Window;
+	snarfput:		fn(buf: string);
+	snarfget:		fn(): string;
+	cursorspec:	fn(img: ref Draw->Image): string;
+
+	Window: adt{
+		display:	ref Draw->Display;
+		r: Draw->Rect;		# full rectangle of window, including titlebar.
+		image: ref Draw->Image;
+		displayr: Draw->Rect;
+		ctxt: ref Draw->Wmcontext;
+		bd:		int;
+		focused:	int;
+		ctl:		chan of string;
+
+		# private from here:
+		titlebar:	ref Tk->Toplevel;		# XXX i wish this didn't have to be visible to the application...
+		tbsize: 	Draw->Point;			# size requested by titlebar.
+		tbrect:	Draw->Rect;
+		screen:	ref Draw->Screen;
+		buttons:	int;
+		ptrfocus:	int;
+		saved:	Draw->Point;			# saved origin before task
+
+		startinput:	fn(w: self ref Window, devs: list of string);
+		wmctl:	fn(w: self ref Window, request: string): string;
+		settitle:	fn(w: self ref Window, name: string): string;
+		reshape:	fn(w: self ref Window, r: Draw->Rect);
+		onscreen:	fn(w: self ref Window, how: string);
+		screenr:	fn(w: self ref Window, sr: Draw->Rect): Draw->Rect;
+		imager:	fn(w: self ref Window, ir: Draw->Rect): Draw->Rect;
+		pointer:	fn(w: self ref Window, p: Draw->Pointer): int;
+	};
+
+};
--- /dev/null
+++ b/module/wmlib.m
@@ -1,0 +1,21 @@
+Wmlib: module
+{
+	PATH:		con "/dis/lib/wmlib.dis";
+
+	init:		fn();
+	makedrawcontext: fn(): ref Draw->Context;
+	importdrawcontext: fn(devdraw, mntwm: string): (ref Draw->Context, string);
+	connect:	fn(ctxt: ref Draw->Context): ref Draw->Wmcontext;
+	reshape:	fn(w: ref Draw->Wmcontext, name: string, r: Draw->Rect, i: ref Draw->Image, how: string): ref Draw->Image;
+	startinput:	fn(w: ref Draw->Wmcontext, devs: list of string): string;	# could be part of connect?
+	wmctl:	fn(w: ref Draw->Wmcontext, request: string): (string, ref Draw->Image, string);
+#	wmtoken:	fn(w: ref Draw->Wmcontext): string;
+	snarfput:	fn(buf: string);
+	snarfget:	fn(): string;
+
+	# XXX these don't really belong here, but where should they go?
+	splitqword:	fn(s: string, e: int): ((int, int), int);
+	qslice:		fn(s: string, r: (int, int)): string;
+	qword:		fn(s: string, e: int): (string, int);
+	s2r:			fn(s: string, e: int): (Draw->Rect, int);
+};
--- /dev/null
+++ b/module/wmsrv.m
@@ -1,0 +1,46 @@
+Wmsrv: module{
+	PATH: con "/dis/lib/wmsrv.dis";
+
+	init:	fn(): 	(chan of (string, chan of (string, ref Draw->Wmcontext)),
+		chan of (ref Client, chan of string),
+		chan of (ref Client, array of byte, Sys->Rwrite));
+
+	find:	fn(p: Draw->Point): ref Client;
+	top:	fn(): ref Client;
+
+	Window: adt {
+		tag:	string;
+		r:	Draw->Rect;
+		img:	ref Draw->Image;
+	};
+
+	Client: adt {
+		kbd:		chan of int;
+		ptr:		chan of ref Draw->Pointer;
+		ctl:		chan of string;
+		stop:		chan of int;
+		flags:	int;			# general purpose.
+		cursor:	string;		# hack.
+		wins:	list of ref Window;
+		znext:	cyclic ref Client;
+
+		# private:
+		images:	chan of (ref Draw->Point, ref Draw->Image, chan of int);
+		id:		int;				# index into clients array
+		fid:		int;
+		token:	int;
+		wmctxt:	ref Draw->Wmcontext;
+
+		window:	fn(c: self ref Client, tag: string): ref Window;
+		contains:	fn(c: self ref Client, p: Draw->Point): int;
+		image:	fn(c: self ref Client, tag: string):	ref Draw->Image;
+		setimage:	fn(c: self ref Client, tag: string,  i: ref Draw->Image): int;	# only in response to some msgs.
+		setorigin:	fn(c: self ref Client, tag: string, o: Draw->Point): int;		# only in response to some msgs.
+		top:		fn(c: self ref Client);			# bring to top.
+		bottom:	fn(c: self ref Client);			# send to bottom.
+		hide:		fn(w: self ref Client);		# move offscreen.
+		unhide:	fn(w: self ref Client);		# move onscreen.
+		remove:	fn(w: self ref Client);
+	};
+};
+
--- /dev/null
+++ b/module/workdir.m
@@ -1,0 +1,8 @@
+Workdir: module
+{
+	PATH:	con	"/dis/lib/workdir.dis";
+
+	# Return current working directory
+
+	init:	fn(): string;
+};
--- /dev/null
+++ b/module/x509.m
@@ -1,0 +1,370 @@
+#
+# X.509 v3 by ITU-T Recommendation (11/93) & PKCS7 & PKCS10
+#
+
+X509: module {
+
+	PATH: con "/dis/lib/crypt/x509.dis";
+
+	init: fn(): string;
+
+	## x509 (id_at) and x509 extention v3 (id_ce) Object Identifiers
+
+	objIdTab			: array of ASN1->Oid;
+
+	id_at,
+	id_at_commonName,
+	id_at_countryName,
+	id_at_localityName,
+	id_at_stateOrProvinceName,
+	id_at_organizationName,
+	id_at_organizationalUnitName,
+	id_at_userPassword,
+	id_at_userCertificate,
+	id_at_cAcertificate,
+	id_at_authorityRevocationList,
+	id_at_certificateRevocationList,
+	id_at_crossCertificatePair,
+	id_at_supportedAlgorithms,
+	id_at_deltaRevocationList,
+	id_ce,
+	id_ce_subjectDirectoryAttributes,
+	id_ce_subjectKeyIdentifier,
+	id_ce_keyUsage,
+	id_ce_privateKeyUsage,
+	id_ce_subjectAltName,
+	id_ce_issuerAltName,
+	id_ce_basicConstraints,
+	id_ce_cRLNumber,
+	id_ce_reasonCode,
+	id_ce_instructionCode,
+	id_ce_invalidityDate,
+	id_ce_deltaCRLIndicator,
+	id_ce_issuingDistributionPoint,
+	id_ce_certificateIssuer,
+	id_ce_nameConstraints,
+	id_ce_cRLDistributionPoint,
+	id_ce_certificatePolicies,
+	id_ce_policyMapping,
+	id_ce_authorityKeyIdentifier,
+	id_ce_policyConstraints,
+	id_mr,
+	id_mr_certificateExactMatch,
+ 	id_mr_certificateMatch,
+ 	id_mr_certificatePairExactMatch,
+ 	id_mr_certificatePairMatch,
+ 	id_mr_certificateListExactMatch,
+ 	id_mr_certificateListMatch,
+ 	id_mr_algorithmidentifierMatch	: con iota;
+
+	## Signed (as Public Key, CRL, Attribute Certificates and CertificationRequest)
+
+	Signed: adt {
+		tobe_signed		: array of byte;
+  		alg			: ref AlgIdentifier;
+  		signature		: array of byte; # BIT STRING, DER encoding
+		
+		decode: fn(a: array of byte): (string, ref Signed);
+		encode: fn(s: self ref Signed): (string, array of byte);
+		sign: fn(s: self ref Signed, sk: ref PrivateKey, hash: int): (string, array of byte);
+		verify: fn(s: self ref Signed, pk: ref PublicKey, hash: int): int;
+		tostring: fn(s: self ref Signed): string;
+	};
+
+	## Certificate Path
+
+	verify_certchain: fn(cs: list of array of byte): (int, string);
+	verify_certpath: fn(cp: list of (ref Signed, ref Certificate)): (int, string);
+
+	## TBS (Public Key) Certificate
+
+	Certificate: adt {
+  		version			: int; # v1(0; default) or v2(1) or v3(2)
+  		serial_number		: ref Keyring->IPint;
+  		sig			: ref AlgIdentifier;
+  		issuer			: ref Name;
+  		validity		: ref Validity;
+  		subject			: ref Name;
+  		subject_pkinfo		: ref SubjectPKInfo;
+					# OPTIONAL for v2 and v3; must be in order
+  		issuer_uid		: array of byte; # v2
+  		subject_uid		: array of byte; # v2 or v3
+  		exts			: list of ref Extension; # v3
+
+		decode: fn(a: array of byte): (string, ref Certificate);
+		encode: fn(c: self ref Certificate): (string, array of byte);
+		tostring: fn(c: self ref Certificate): string;
+		is_expired: fn(c: self ref Certificate, date: int): int;
+	};
+
+	AlgIdentifier: adt {
+		oid			: ref ASN1->Oid;
+		parameter		: array of byte;
+
+		tostring: fn(a: self ref AlgIdentifier): string;
+	};
+
+	Name: adt {
+		rd_names		: list of ref RDName;
+
+		equal: fn(a: self ref Name, b: ref Name): int;
+		tostring: fn(n: self ref Name): string;
+	};
+
+	RDName: adt {
+		avas			: list of ref AVA;
+
+		equal: fn(a: self ref RDName, b: ref RDName): int;
+		tostring: fn(r: self ref RDName): string;
+	};
+
+	AVA: adt {
+		oid			: ref ASN1->Oid;
+		value			: string;
+		
+		equal: fn(a: self ref AVA, b: ref AVA): int;
+		tostring: fn(a: self ref AVA): string;
+	};
+
+	Validity: adt {
+  		not_before		: int;
+  		not_after		: int;
+
+		tostring: fn(v: self ref Validity, format: string): string;
+	};
+
+	SubjectPKInfo: adt {
+  		alg_id			: ref AlgIdentifier;
+  		subject_pk		: array of byte; # BIT STRING
+
+		getPublicKey: fn(c: self ref SubjectPKInfo): (string, int, ref PublicKey);
+		tostring: fn(c: self ref SubjectPKInfo): string;
+	};
+
+	Extension: adt{
+  		oid			: ref ASN1->Oid;
+  		critical		: int; # default false 
+  		value			: array of byte;
+
+		tostring: fn(e: self ref Extension): string;
+	};
+
+	PublicKey: adt {
+		pick {
+		RSA =>
+			pk		: ref PKCS->RSAKey;
+		DSS =>
+			pk		: ref PKCS->DSSPublicKey;
+		DH =>
+			pk		: ref PKCS->DHPublicKey;
+		}
+	};
+
+	PrivateKey: adt {
+		pick {
+		RSA =>
+			sk		: ref PKCS->RSAKey;
+		DSS =>
+			sk		: ref PKCS->DSSPrivateKey;
+		DH =>
+			sk		: ref PKCS->DHPrivateKey;
+		}
+	};
+
+	## Certificate Revocation List
+
+	CRL: adt {
+		version			: int; # OPTIONAL; v2
+		sig			: ref AlgIdentifier;
+		issuer			: ref Name; 
+		this_update		: int;
+		next_update		: int; # OPTIONAL
+		revoked_certs		: list of ref RevokedCert; # OPTIONAL
+		exts			: list of ref Extension; # OPTIONAL
+
+		decode: fn(a: array of byte): (string, ref CRL);
+		encode: fn(c: self ref CRL): (string, array of byte);
+		tostring: fn(c: self ref CRL): string;
+		is_revoked: fn(c: self ref CRL, sn: ref Keyring->IPint): int;
+	};
+
+	RevokedCert: adt {
+		user_cert		: ref Keyring->IPint; # serial_number
+		revoc_date		: int; # OPTIONAL
+		exts			: list of ref Extension; # OPTIONAL; CRL entry extensions
+
+		tostring: fn(rc: self ref RevokedCert): string;	
+	};
+
+	## Certificate Extensions
+
+	# get critical extensions	
+	cr_exts: fn(es: list of ref Extension): list of ref Extension;
+
+	# get non-critical extensions
+	noncr_exts: fn(es: list of ref Extension): list of ref Extension;
+
+	# decode a list of extensions
+	parse_exts: fn(es: list of ref Extension): (string, list of ref ExtClass);
+
+	# extension classes
+	ExtClass: adt {
+		pick {
+		AuthorityKeyIdentifier =>
+			id		: array of byte; # OCTET STRING
+			issuer		: ref GeneralName;
+			serial_number	: ref Keyring->IPint;
+		SubjectKeyIdentifier =>
+			id		: array of byte; # OCTET STRING
+		BasicConstraints =>	
+			depth		: int; # certificate path constraints
+		KeyUsage =>
+			usage		: int;
+		PrivateKeyUsage =>
+			period		: ref Validity;
+		PolicyMapping =>	# (issuer, subject) domain policy pairs
+			pairs		: list of (ref ASN1->Oid, ref ASN1->Oid);
+		CertificatePolicies =>
+			policies	: list of ref PolicyInfo;
+		IssuerAltName =>
+			alias		: list of ref GeneralName;
+		SubjectAltName =>
+			alias		: list of ref GeneralName;
+		NameConstraints =>
+			permitted	: list of ref GSubtree;
+			excluded	: list of ref GSubtree;
+		PolicyConstraints =>
+			require		: int;
+			inhibit		: int;
+		CRLNumber =>
+			curr		: int;
+		ReasonCode =>
+			code		: int;
+		InstructionCode =>
+			oid		: ref ASN1->Oid; # hold instruction code field
+		InvalidityDate =>
+			date		: int;
+		CRLDistributionPoint =>
+			ps		: list of ref DistrPoint;
+		IssuingDistributionPoint =>
+			name		: ref DistrPointName;
+			only_usercerts	: int; # DEFAULT FALSE
+			only_cacerts	: int; # DEFAULT FALSE
+			only_reasons	: int;
+			indirect_crl	: int; # DEFAULT FALSE	 	 
+		CertificateIssuer =>
+			names		: list of ref GeneralName;
+		DeltaCRLIndicator =>
+			number		: ref Keyring->IPint;
+		SubjectDirectoryAttributes =>
+			attrs		: list of ref Attribute;
+		UnknownType =>
+			ext		: ref Extension;
+		}
+
+		decode: fn(ext: ref Extension): (string, ref ExtClass);
+		encode: fn(et: self ref ExtClass, critical: int): ref Extension;
+		tostring: fn(et: self ref ExtClass): string;
+	};
+
+	# key usage
+	KeyUsage_DigitalSignature, KeyUsage_NonRepudiation, KeyUsage_KeyEncipherment,
+	KeyUsage_DataEncipherment, KeyUsage_KeyAgreement, KeyUsage_KeyCertSign, 
+	KeyUsage_CRLSign, KeyUsage_EncipherOnly, KeyUsage_DecipherOnly : con iota << 1;
+
+	# CRL reason
+	Reason_Unspecified, Reason_KeyCompromise, Reason_CACompromise, 
+	Reason_AffiliationChanged, Reason_Superseded, Reason_CessationOfOperation, 
+	Reason_CertificateHold, Reason_RemoveFromCRL : con iota << 1;
+
+	# General Name
+	GeneralName: adt {
+		pick {
+		otherName or 		# [0]
+		rfc822Name or 		# [1]
+		dNSName or 		# [2]
+		x400Address or 		# [3]
+		uniformResourceIdentifier => # [6]
+			str		: string;
+		iPAddress =>		# [7]
+			ip		: array of byte;
+		registeredID =>		# [8]
+			oid		: ref ASN1->Oid;
+		ediPartyName =>		# [5]
+			nameAssigner	: ref Name; # [0]
+			partyName	: ref Name; # [1]
+		directoryName =>	# [4]
+			dir		: ref Name;
+		}
+
+		tostring: fn(g: self ref GeneralName): string;
+	};
+
+	# security policies
+	PolicyInfo: adt {
+		oid			: ref ASN1->Oid;
+		qualifiers		: list of ref PolicyQualifier;
+
+		tostring: fn(pi: self ref PolicyInfo): string;
+	};
+
+	PolicyQualifier: adt {
+		oid			: ref ASN1->Oid;
+		value			: array of byte; # OCTET STRING; OPTIONAL
+
+		tostring: fn(pq: self ref PolicyQualifier): string;
+	};
+
+	GSubtree: adt {
+		base			: ref GeneralName;
+		min			: int;
+		max			: int;
+	
+		tostring: fn(gs: self ref GSubtree): string;
+	};
+	
+	# crl distribution point
+	# with known reason code
+	# Unused [0], KeyCompromise [1], CACompromise [2], AffilationChanged [3],
+	# Superseded [4], CessationOfOperation [5], CertificateHold [6] 
+	DistrPoint: adt{
+		name			: ref DistrPointName;
+ 		reasons			: int;
+		issuer			: list of ref GeneralName;
+
+		tostring: fn(dp: self ref DistrPoint): string;
+	};
+	
+	DistrPointName: adt {
+		full_name		: list of ref GeneralName;
+		rdname			: list of ref RDName;
+	};
+
+	Attribute: adt {
+		id			: ASN1->Oid;
+		value			: array of byte;
+	};
+};
+
+#X509Attribute: module {
+#
+#	## Attribute Certificate
+#
+#	AttrCert: adt {
+#		version			: int; # default v1
+#		base_certid		: ref IssuerSerial; # [0]
+#		subject_name		: list of ref GeneralName; # [1]
+#		issuer			: list of ref GeneralName;
+#		serial_number		: ref IPint;
+#		validity		: ref Validity;
+#		attrs			: list of ref Attribute;
+#		issuer_uid		: array of byte; # OPTIONAL
+#		exts			: list of ref Extension; # OPTIONAL			
+#	};
+#
+#	IssuerSerial: adt {
+#		issuer			: list of ref GeneralName;
+#		serial			: ref IPint;
+#		issuer_uid		: array of byte; # OPTIONAL
+#	};
+#};
--- /dev/null
+++ b/module/xml.m
@@ -1,0 +1,79 @@
+Xml: module {
+	PATH: con "/dis/lib/xml.dis";
+	Item: adt {
+		fileoffset:	int;
+		pick {
+		Tag =>
+			name:	string;
+			attrs:		Attributes;
+		Text =>
+			ch:		string;
+			ws1, ws2: int;
+		Process =>
+			target:	string;
+			data:		string;
+		Doctype =>
+			name:	string;
+			public:	int;
+			params:	list of string;
+		Stylesheet =>
+			attrs:		Attributes;
+		Error =>
+			loc:		Locator;
+			msg:		string;
+		}
+	};
+
+	Locator: adt {
+		line:				int;
+		systemid:			string;
+		publicid:			string;
+	};
+
+	Attribute: adt {
+		name:			string;
+		value:			string;
+	};
+
+	Attributes: adt {
+		attrs:			list of Attribute;
+
+		all:			fn(a: self Attributes): list of Attribute;
+		get:			fn(a: self Attributes, name: string): string;
+	};
+
+	Mark: adt {
+		estack:	list of string;
+		line:		int;
+		offset:	int;
+		readdepth:	int;
+
+		str:		fn(m: self ref Mark): string;	
+	};
+
+	Parser: adt {
+		in:		ref Bufio->Iobuf;
+		eof:		int;
+		lastnl:	int;
+		estack:	list of string;
+		loc:		Locator;
+		warning:	chan of (Locator, string);
+		errormsg:	string;
+		actdepth:	int;
+		readdepth:	int;
+		fileoffset:	int;
+		preelem:	string;
+		ispre:	int;
+
+		next:		fn(p: self ref Parser): ref Item;
+		up:		fn(p: self ref Parser);
+		down:	fn(p: self ref Parser);
+		mark:	fn(p: self ref Parser): ref Mark;
+		atmark:	fn(p: self ref Parser, m: ref Mark): int;
+		goto:	fn(p: self ref Parser, m: ref Mark);
+		str2mark:	fn(p: self ref Parser, s: string): ref Mark;
+	};
+	init:	fn(): string;
+	open: fn(f: string, warning: chan of (Locator, string), preelem: string): (ref Parser, string);
+	fopen:	fn(f: ref Bufio->Iobuf, srcname: string, warning: chan of (Locator, string), preelem: string): (ref Parser, string);
+};
--- /dev/null
+++ b/module/xpointers.m
@@ -1,0 +1,67 @@
+Xpointers: module
+{
+	PATH: con "/dis/lib/w3c/xpointers.dis";
+
+	One, Ole, Oge, Omul, Odiv, Omod, Oand, Oor, Oneg,
+	Onodetype, Onametest, Ofilter, Opath: con 'A'+iota;
+
+	# axis types
+	Aancestor,
+	Aancestor_or_self,
+	Aattribute,
+	Achild,
+	Adescendant,
+	Adescendant_or_self,
+	Afollowing,
+	Afollowing_sibling,
+	Anamespace,
+	Aparent,
+	Apreceding,
+	Apreceding_sibling,
+	Aself: con iota;
+
+	Xstep: adt {
+		axis:	int;	# Aancestor, ... (above)
+		op:	int;	# Onametest or Onodetype
+		ns:	string;
+		name:	string;
+		arg:	string;	# optional parameter to processing-instruction
+		preds:	cyclic list of ref Xpath;
+
+		text:	fn(nil: self ref Xstep): string;
+		axisname:	fn(i: int): string;
+	};
+
+	Xpath: adt {
+		pick{
+		E =>
+			op: int;
+			l, r: cyclic ref Xpath;
+		Fn =>
+			ns:	string;
+			name:	string;
+			args:	cyclic list of ref Xpath;
+		Var =>
+			ns: string;
+			name: string;
+		Path =>
+			abs:	int;
+			steps:	list of ref Xstep;
+		Int =>
+			val: big;
+		Real =>
+			val: real;
+		Str =>
+			s: string;
+		}
+		text:	fn(nil: self ref Xpath): string;
+	};
+
+	init:	fn();
+	framework:	fn(s: string): (string, list of (string, string, string), string);
+
+	# predefined schemes
+	element:	fn(s: string): (string, list of int, string);
+	xmlns:	fn(s: string): (string, string, string);
+	xpointer:	fn(s: string): (ref Xpath, string);
+};
--- /dev/null
+++ b/opt/README
@@ -1,0 +1,2 @@
+optional components live in subdirectories of /opt.
+see opt(6) and opt(1)
--- /dev/null
+++ b/os/NOTICE
@@ -1,0 +1,31 @@
+This copyright NOTICE applies to all files in this directory and
+subdirectories, unless another copyright notice appears in a given
+file or subdirectory.  If you take substantial code from this software to use in
+other programs, you must somehow include with it an appropriate
+copyright notice that includes the copyright notice and the other
+notices below.  It is fine (and often tidier) to do that in a separate
+file such as NOTICE, LICENCE or COPYING.
+
+	Copyright © 1994-1999 Lucent Technologies Inc.  All rights reserved.
+	Portions Copyright © 1997-1999 Vita Nuova Limited
+	Portions Copyright © 2000-2007 Vita Nuova Holdings Limited (www.vitanuova.com)
+	Revisions Copyright © 2000-2007 Lucent Technologies Inc. and others
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in
+all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+THE SOFTWARE.
+
--- /dev/null
+++ b/os/README
@@ -1,0 +1,32 @@
+#	a few sample native ports for different architectures
+
+cerf1110	arm	Intrinsyc SA-1110 Cerf Cube
+ipaq1110	arm	Strongarm SA1110 native kernel for Compaq iPAQ 36xx
+sa1110	arm	SA1110 code common to most/all SA1110 platforms (eg, ipaq, cerf)
+
+cerf250	arm	Intrinsyc Cerfboard250 (PXA250)
+gum	arm	Gumstix PXA255	not yet ready for distribution
+pxa	arm	general PXA support
+
+js	sparc	original port to Javastation-1, included as sparc example but will not build
+
+ks32	arm	ARM Evaluator 7t demo
+
+manga	arm	Micrel KS8695P ARM922 core with 4-port switch and PCI support
+
+cerf405	power	Cerfcube 405EP (based on IBM 405EP processor)
+
+ipengine	power	Bright Star Engineering (www.brightstareng.com) ipEngine-1 (MPC823)
+rpcg	power	RPCG-Lite MPC823/850 board
+fads	power	MPC8xx FADS development board
+mpc	power	code common to ports to Motorola MPC8xx boards
+
+pc	386	basis for ports to various PC-based platforms
+
+#			basic Inferno native kernel components
+port	common	native operating system components
+ip	common	Internet protocol stack, Ether and PPP media
+init	common	Limbo initialisation program (run by native kernel on startup)
+
+#	sample bootstraps
+boot	-	directory of several bootstrap programs; see README there
--- /dev/null
+++ b/os/boot/README
@@ -1,0 +1,33 @@
+Often the devices we use now come with some
+form of bootstrap (often annoyingly complicated,
+which gets in the way).  Older boards were sometimes
+bare, and we had to provide something (often annoyingly
+complicated...).  On the PC it's currently helpful to
+have something that can boot from disc or ether;
+since it's a thankless task to write such a thing, we
+use Plan 9's, and thank its authors.
+
+The current scheme on newer devices is to have a simple
+program that can put a stripped-down Inferno kernel into
+flash, and use that to boot from other devices (including over the net)
+as required during development.
+
+There are two distinct models for bootstrap in this directory.
+
+	Model I
+
+Each member of the first model is represented by a self-contained directory.
+They are derived from various ages of Plan 9's /sys/src/boot/pc.
+
+arm1110	arm	a prefix to a gzip'd kernel to decompress it (runs after basic bootloader)
+pc	386	pc-specific bootstrap essentially identical to current Plan 9
+		and covered by the Lucent Public License; it uses
+libflate	-	zlib-style inflate/deflate library
+mpc	power	PowerPC bootstrap for FADS board derived from an older version
+		of Plan 9 but covered by our Inferno licence (because it came with Inferno)
+puma	arm	SA110 bootstrap for Teralogics Puma, also covered by the Inferno licence
+
+	Model II
+omap	purpose-built bootstrap for the OMAP processor
+
+Not all of these are being distributed.
--- /dev/null
+++ b/os/boot/arm1110/Mk
@@ -1,0 +1,8 @@
+#!/bin/rc
+rfork ne
+ROOT=/usr/inferno
+fn cd
+NPROC=3
+path=(/usr/inferno/Plan9/$cputype/bin $path)
+#bind /usr/inferno/mkconfig.dist /usr/inferno/mkconfig
+exec mk $*
--- /dev/null
+++ b/os/boot/arm1110/dat.h
@@ -1,0 +1,1 @@
+/* deliberately empty */
--- /dev/null
+++ b/os/boot/arm1110/donprint.c
@@ -1,0 +1,332 @@
+#include	"u.h"
+#include	"lib.h"
+
+#define	PTR	sizeof(char*)
+#define	SHORT	sizeof(int)
+#define	INT	sizeof(int)
+#define	LONG	sizeof(long)
+#define	IDIGIT	30
+#define	MAXCON	30
+
+#define	FLONG	(1<<0)
+#define	FSHORT	(1<<1)
+#define	FUNSIGN	(1<<2)
+
+typedef struct Op	Op;
+struct Op
+{
+	char	*p;
+	char	*ep;
+	void	*argp;
+	int	f1;
+	int	f2;
+	int	f3;
+};
+
+static	int	noconv(Op*);
+static	int	cconv(Op*);
+static	int	dconv(Op*);
+static	int	hconv(Op*);
+static	int	lconv(Op*);
+static	int	oconv(Op*);
+static	int	sconv(Op*);
+static	int	uconv(Op*);
+static	int	xconv(Op*);
+static	int	Xconv(Op*);
+static	int	percent(Op*);
+
+static
+int	(*fmtconv[MAXCON])(Op*) =
+{
+	noconv,
+	cconv, dconv, hconv, lconv,
+	oconv, sconv, uconv, xconv,
+	Xconv, percent,
+};
+static
+char	fmtindex[128] =
+{
+	['c'] 1,
+	['d'] 2,
+	['h'] 3,
+	['l'] 4,
+	['o'] 5,
+	['s'] 6,
+	['u'] 7,
+	['x'] 8,
+	['X'] 9,
+	['%'] 10,
+};
+
+static	int	convcount  = { 11 };
+static	int	ucase;
+
+static void
+PUT(Op *o, int c)
+{
+	static int pos;
+	int opos;
+
+	if(c == '\t'){
+		opos = pos;
+		pos = (opos+8) & ~7;
+		while(opos++ < pos && o->p < o->ep)
+			*o->p++ = ' ';
+		return;
+	}
+	if(o->p < o->ep){
+		*o->p++ = c;
+		pos++;
+	}
+	if(c == '\n')
+		pos = 0;
+}
+
+int
+fmtinstall(char c, int (*f)(Op*))
+{
+
+	c &= 0177;
+	if(fmtindex[c] == 0) {
+		if(convcount >= MAXCON)
+			return 1;
+		fmtindex[c] = convcount++;
+	}
+	fmtconv[fmtindex[c]] = f;
+	return 0;
+}
+
+char*
+donprint(char *p, char *ep, char *fmt, void *argp)
+{
+	int sf1, c;
+	Op o;
+
+	o.p = p;
+	o.ep = ep;
+	o.argp = argp;
+
+loop:
+	c = *fmt++;
+	if(c != '%') {
+		if(c == 0) {
+			if(o.p < o.ep)
+				*o.p = 0;
+			return o.p;
+		}
+		PUT(&o, c);
+		goto loop;
+	}
+	o.f1 = 0;
+	o.f2 = -1;
+	o.f3 = 0;
+	c = *fmt++;
+	sf1 = 0;
+	if(c == '-') {
+		sf1 = 1;
+		c = *fmt++;
+	}
+	while(c >= '0' && c <= '9') {
+		o.f1 = o.f1*10 + c-'0';
+		c = *fmt++;
+	}
+	if(sf1)
+		o.f1 = -o.f1;
+	if(c != '.')
+		goto l1;
+	c = *fmt++;
+	while(c >= '0' && c <= '9') {
+		if(o.f2 < 0)
+			o.f2 = 0;
+		o.f2 = o.f2*10 + c-'0';
+		c = *fmt++;
+	}
+l1:
+	if(c == 0)
+		fmt--;
+	c = (*fmtconv[fmtindex[c&0177]])(&o);
+	if(c < 0) {
+		o.f3 |= -c;
+		c = *fmt++;
+		goto l1;
+	}
+	o.argp = (char*)o.argp + c;
+	goto loop;
+}
+
+void
+strconv(char *o, Op *op, int f1, int f2)
+{
+	int n, c;
+	char *p;
+
+	n = strlen(o);
+	if(f1 >= 0)
+		while(n < f1) {
+			PUT(op, ' ');
+			n++;
+		}
+	for(p=o; c = *p++;)
+		if(f2 != 0) {
+			PUT(op, c);
+			f2--;
+		}
+	if(f1 < 0) {
+		f1 = -f1;
+		while(n < f1) {
+			PUT(op, ' ');
+			n++;
+		}
+	}
+}
+
+int
+numbconv(Op *op, int base)
+{
+	char b[IDIGIT];
+	int i, f, n, r;
+	long v;
+	short h;
+
+	f = 0;
+	switch(op->f3 & (FLONG|FSHORT|FUNSIGN)) {
+	case FLONG:
+		v = *(long*)op->argp;
+		r = LONG;
+		break;
+
+	case FUNSIGN|FLONG:
+		v = *(ulong*)op->argp;
+		r = LONG;
+		break;
+
+	case FSHORT:
+		h = *(int*)op->argp;
+		v = h;
+		r = SHORT;
+		break;
+
+	case FUNSIGN|FSHORT:
+		h = *(int*)op->argp;
+		v = (ushort)h;
+		r = SHORT;
+		break;
+
+	default:
+		v = *(int*)op->argp;
+		r = INT;
+		break;
+
+	case FUNSIGN:
+		v = *(unsigned*)op->argp;
+		r = INT;
+		break;
+	}
+	if(!(op->f3 & FUNSIGN) && v < 0) {
+		v = -v;
+		f = 1;
+	}
+	b[IDIGIT-1] = 0;
+	for(i = IDIGIT-2;; i--) {
+		n = (ulong)v % base;
+		n += '0';
+		if(n > '9'){
+			n += 'a' - ('9'+1);
+			if(ucase)
+				n += 'A'-'a';
+		}
+		b[i] = n;
+		if(i < 2)
+			break;
+		v = (ulong)v / base;
+		if(op->f2 >= 0 && i >= IDIGIT-op->f2)
+			continue;
+		if(v <= 0)
+			break;
+	}
+	if(f)
+		b[--i] = '-';
+	strconv(b+i, op, op->f1, -1);
+	return r;
+}
+
+static	int
+noconv(Op *op)
+{
+
+	strconv("***", op, 0, -1);
+	return 0;
+}
+
+static	int
+cconv(Op *op)
+{
+	char b[2];
+
+	b[0] = *(int*)op->argp;
+	b[1] = 0;
+	strconv(b, op, op->f1, -1);
+	return INT;
+}
+
+static	int
+dconv(Op *op)
+{
+	return numbconv(op, 10);
+}
+
+static	int
+hconv(Op*)
+{
+	return -FSHORT;
+}
+
+static	int
+lconv(Op*)
+{
+	return -FLONG;
+}
+
+static	int
+oconv(Op *op)
+{
+	return numbconv(op, 8);
+}
+
+static	int
+sconv(Op *op)
+{
+	strconv(*(char**)op->argp, op, op->f1, op->f2);
+	return PTR;
+}
+
+static	int
+uconv(Op*)
+{
+	return -FUNSIGN;
+}
+
+static	int
+xconv(Op *op)
+{
+	return numbconv(op, 16);
+}
+
+static	int
+Xconv(Op *op)
+{
+	int r;
+
+	ucase = 1;
+	r = numbconv(op, 16);
+	ucase = 0;
+	return r;
+}
+
+static	int
+percent(Op *op)
+{
+
+	PUT(op, '%');
+	return 0;
+}
--- /dev/null
+++ b/os/boot/arm1110/fns.h
@@ -1,0 +1,7 @@
+/*
+ *  functions defined locally
+ */
+extern int	gunzip(uchar *out, int outn, uchar *in, int inn);
+extern void	delay(int ms);
+extern void	serialputs(char *str, int n);
+extern void	draincache(void);
--- /dev/null
+++ b/os/boot/arm1110/il.s
@@ -1,0 +1,99 @@
+#include "mem.h"
+/*
+ *  Entered here from Compaq's bootldr.  First relocate to
+ *  the location we're linked for and then copy back the
+ *  decompressed kernel.
+ *
+ *  All 
+ */
+TEXT _start(SB), $-4
+	MOVW	$setR12(SB), R12		/* load the SB */
+	MOVW	$1, R0		/* dance to make 5l think that the magic */
+	MOVW	$1, R1		/* numbers in WORDs below are being used */
+	CMP.S	R0, R1		/* and to align them to where bootldr wants */
+	BEQ	_start2
+	WORD	$0x016f2818	/* magic number to say we are a kernel */
+	WORD	$0xc0008000	/* entry point address */
+	WORD	$0		/* size?, or end of data? */
+
+_start2:
+
+	/* SVC mode, interrupts disabled */
+	MOVW	$(PsrDirq|PsrDfiq|PsrMsvc), R1
+	MOVW	R1, CPSR
+
+	/* disable the MMU */
+	MOVW	$0x130, R1
+	MCR     CpMMU, 0, R1, C(CpControl), C(0x0)
+
+	/* enable caches */
+	MRC	CpMMU, 0, R0, C(CpControl), C(0x0)
+	ORR	$(CpCdcache|CpCicache|CpCwb), R0
+	MCR     CpMMU, 0, R0, C(CpControl), C(0x0)
+
+	/* flush caches */
+	MCR	CpMMU, 0, R0, C(CpCacheFlush), C(0x7), 0
+	/* drain prefetch */
+	MOVW	R0,R0						
+	MOVW	R0,R0
+	MOVW	R0,R0
+	MOVW	R0,R0
+
+	/* drain write buffer */
+	MCR	CpMMU, 0, R0, C(CpCacheFlush), C(0xa), 4
+
+	/* relocate to where we expect to be */
+	MOVW	$(FLATESIZE),R3
+	MOVW	$0xC0008000,R1
+	MOVW	$0xC0200000,R2
+	ADD	R1,R3
+_relloop:
+	MOVW	(R1),R0
+	MOVW	R0,(R2)
+	ADD	$4,R1
+	ADD	$4,R2
+	CMP.S	R1,R3
+	BNE	_relloop
+
+	MOVW	$(MACHADDR+BY2PG), R13		/* stack */
+	SUB	$4, R13				/* link */
+
+	/* jump to where we've been relocated */
+	MOVW	$_relocated(SB),R15
+
+TEXT _relocated(SB),$-4
+	BL	main(SB)
+	BL	exit(SB)
+	/* we shouldn't get here */
+_mainloop:
+	B	_mainloop
+	BL	_div(SB)			/* hack to get _div etc loaded */
+
+TEXT mypc(SB),$-4
+	MOVW	R14,R0
+	RET
+
+TEXT draincache(SB),$-4
+	/* write back any dirty data */
+	MOVW	$0xe0000000,R0
+	ADD	$(8*1024),R0,R1
+_cfloop:
+	MOVW.P	32(R0),R2
+	CMP.S	R0,R1
+	BNE	_cfloop
+	
+	/* drain write buffer and invalidate i&d cache contents */
+	MCR	CpMMU, 0, R0, C(CpCacheFlush), C(0xa), 4
+	MCR	CpMMU, 0, R0, C(CpCacheFlush), C(0x7), 0
+
+	/* drain prefetch */
+	MOVW	R0,R0						
+	MOVW	R0,R0
+	MOVW	R0,R0
+	MOVW	R0,R0
+
+	/* disable caches */
+	MRC	CpMMU, 0, R0, C(CpControl), C(0x0)
+	BIC	$(CpCdcache|CpCicache|CpCwb), R0
+	MCR     CpMMU, 0, R0, C(CpControl), C(0x0)
+	RET
--- /dev/null
+++ b/os/boot/arm1110/imain.c
@@ -1,0 +1,48 @@
+#include "u.h"
+#include "lib.h"
+#include "fns.h"
+#include "dat.h"
+#include "mem.h"
+
+void
+main(void)
+{
+	void (*f)(void);
+	ulong *kernel;
+
+	print("inflating kernel\n");
+
+	kernel = (ulong*)(0xc0200000+20*1024);
+	if(gunzip((uchar*)0xc0008000, 2*1024*1024, (uchar*)kernel, FLATESIZE) > 0){
+		f = (void (*)(void))0xc0008010;
+		draincache();
+	} else {
+		print("inflation failed\n");
+		f = nil;
+	}
+	(*f)();
+}
+
+void
+exit(void)
+{
+
+	void (*f)(void);
+
+	delay(1000);
+
+	print("it's a wonderful day to die\n");
+	f = nil;
+	(*f)();
+}
+
+void
+delay(int ms)
+{
+	int i;
+
+	while(ms-- > 0){
+		for(i = 0; i < 1000; i++)
+			;
+	}
+}
--- /dev/null
+++ b/os/boot/arm1110/inflate.c
@@ -1,0 +1,208 @@
+#include "u.h"
+#include "lib.h"
+#include "mem.h"
+#include "dat.h"
+#include "fns.h"
+#include "io.h"
+#include <flate.h>
+
+typedef struct Biobuf	Biobuf;
+
+struct Biobuf
+{
+	uchar *bp;
+	uchar *p;
+	uchar *ep;
+};
+
+static int	header(Biobuf*);
+static int	trailer(Biobuf*, Biobuf*);
+static int	getc(void*);
+static ulong	offset(Biobuf*);
+static int	crcwrite(void *out, void *buf, int n);
+static ulong	get4(Biobuf *b);
+static ulong	Boffset(Biobuf *bp);
+
+/* GZIP flags */
+enum {
+	Ftext=		(1<<0),
+	Fhcrc=		(1<<1),
+	Fextra=		(1<<2),
+	Fname=		(1<<3),
+	Fcomment=	(1<<4),
+
+	GZCRCPOLY	= 0xedb88320UL,
+};
+
+static ulong	*crctab;
+static ulong	crc;
+
+int
+gunzip(uchar *out, int outn, uchar *in, int inn)
+{
+	Biobuf bin, bout;
+	int err;
+
+	crc = 0;
+	crctab = mkcrctab(GZCRCPOLY);
+	err = inflateinit();
+	if(err != FlateOk)
+		print("inflateinit failed: %s\n", flateerr(err));
+
+	bin.bp = bin.p = in;
+	bin.ep = in+inn;
+	bout.bp = bout.p = out;
+	bout.ep = out+outn;
+
+	err = header(&bin);
+	if(err != FlateOk)
+		return err;
+
+	err = inflate(&bout, crcwrite, &bin, getc);
+	if(err != FlateOk)
+		print("inflate failed: %s\n", flateerr(err));
+
+	err = trailer(&bout, &bin);
+	if(err != FlateOk)
+		return err;
+
+	return Boffset(&bout);
+}
+
+static int
+header(Biobuf *bin)
+{
+	int i, flag;
+
+	if(getc(bin) != 0x1f || getc(bin) != 0x8b){
+		print("bad magic\n");
+		return FlateCorrupted;
+	}
+	if(getc(bin) != 8){
+		print("unknown compression type\n");
+		return FlateCorrupted;
+	}
+	
+	flag = getc(bin);
+	
+	/* mod time */
+	get4(bin);
+	
+	/* extra flags */
+	getc(bin);
+	
+	/* OS type */
+	getc(bin);
+
+	if(flag & Fextra)
+		for(i=getc(bin); i>0; i--)
+			getc(bin);
+	
+	/* name */
+	if(flag&Fname)
+		while(getc(bin) != 0)
+			;
+
+	/* comment */
+	if(flag&Fcomment)
+		while(getc(bin) != 0)
+			;
+
+	/* crc16 */
+	if(flag&Fhcrc) {
+		getc(bin);
+		getc(bin);
+	}
+		
+	return FlateOk;
+}
+
+static int
+trailer(Biobuf *bout, Biobuf *bin)
+{
+	/* crc32 */
+	ulong x;
+
+	x = get4(bin);
+	if(crc != x){
+		print("crc mismatch %lux %lux\n", crc, x);
+		return FlateCorrupted;
+	}
+
+	/* length */
+	if(get4(bin) != Boffset(bout)){
+		print("bad output len\n");
+		return FlateCorrupted;
+	}
+	return FlateOk;
+}
+
+static ulong
+get4(Biobuf *b)
+{
+	ulong v;
+	int i, c;
+
+	v = 0;
+	for(i = 0; i < 4; i++){
+		c = getc(b);
+		v |= c << (i * 8);
+	}
+	return v;
+}
+
+static int
+getc(void *in)
+{
+	Biobuf *bp = in;
+
+	if((bp->p - bp->bp) % 10000 == 0)
+		print(".");
+	if(bp->p >= bp->ep)
+		return -1;
+	return *bp->p++;
+}
+
+static ulong
+Boffset(Biobuf *bp)
+{
+	return bp->p - bp->bp;
+}
+
+static int
+crcwrite(void *out, void *buf, int n)
+{
+	Biobuf *bp;
+
+	crc = blockcrc(crctab, crc, buf, n);
+	bp = out;
+	if(n > bp->ep-bp->p)
+		n = bp->ep-bp->p;
+	memmove(bp->p, buf, n);
+	bp->p += n;
+	return n;
+}
+
+#undef malloc
+#undef free
+
+static ulong ibrkp = ~0;
+
+void *
+malloc(ulong n)
+{
+	ulong rv;
+
+	if(ibrkp == ~0)
+		ibrkp = ((ulong)end)+1024*1024;
+	n = (n+3)>>2;
+	n <<= 2;
+	rv = ibrkp;
+	ibrkp += n;
+	return (void*)rv;
+}
+
+void
+free(void *)
+{
+}
--- /dev/null
+++ b/os/boot/arm1110/io.h
@@ -1,0 +1,261 @@
+/*
+ *  Definitions for IO devices.  Used only in C.
+ */
+
+enum
+{
+	/* hardware counter frequency */
+	ClockFreq=	3686400,
+};
+
+/*
+ *  IRQ's defined by SA1100
+ */
+enum
+{
+	IRQgpio0=	0,
+	IRQgpio1=	1,
+	IRQgpio2=	2,
+	IRQgpio3=	3,
+	IRQgpio4=	4,
+	IRQgpio5=	5,
+	IRQgpio6=	6,
+	IRQgpio7=	7,
+	IRQgpio8=	8,
+	IRQgpio9=	9,
+	IRQgpio10=	10,
+	IRQgpiohi=	11,
+	IRQlcd=		12,
+	IRQudc=		13,
+	IRQuart1b=	15,
+	IRQuart2=	16,
+	IRQuart3=	17,
+	IRQmcp=		18,
+	IRQssp=		19,
+	IRQdma0=	20,
+	IRQdma1=	21,
+	IRQdma2=	22,
+	IRQdma3=	23,
+	IRQdma4=	24,
+	IRQdma5=	25,
+	IRQtimer0=	26,
+	IRQtimer1=	27,
+	IRQtimer2=	28,
+	IRQtimer3=	29,
+	IRQsecond=	30,
+	IRQrtc=		31,
+};
+
+/*
+ *  GPIO lines (signal names from compaq document).  _i indicates input
+ *  and _o output.
+ */
+enum
+{
+	GPIO_PWR_ON_i=		1<<0,	/* power button */
+	GPIO_UP_IRQ_i=		1<<1,	/* microcontroller interrupts */
+	GPIO_LDD8_o=		1<<2,	/* LCD data 8-15 */
+	GPIO_LDD9_o=		1<<3,
+	GPIO_LDD10_o=		1<<4,
+	GPIO_LDD11_o=		1<<5,
+	GPIO_LDD12_o=		1<<6,
+	GPIO_LDD13_o=		1<<7,
+	GPIO_LDD14_o=		1<<8,
+	GPIO_LDD15_o=		1<<9,
+	GPIO_CARD_IND1_i=	1<<10,	/* card inserted in PCMCIA socket 1 */
+	GPIO_CARD_IRQ1_i=	1<<11,	/* PCMCIA socket 1 interrupt */
+	GPIO_CLK_SET0_o=	1<<12,	/* clock selects for audio codec */
+	GPIO_CLK_SET1_o=	1<<13,
+	GPIO_L3_SDA_io=		1<<14,	/* UDA1341 interface */
+	GPIO_L3_MODE_o=		1<<15,
+	GPIO_L3_SCLK_o=		1<<16,
+	GPIO_CARD_IND0_i=	1<<17,	/* card inserted in PCMCIA socket 0 */
+	GPIO_KEY_ACT_i=		1<<18,	/* hot key from cradle */
+	GPIO_SYS_CLK_i=		1<<19,	/* clock from codec */
+	GPIO_BAT_FAULT_i=	1<<20,	/* battery fault */
+	GPIO_CARD_IRQ0_i=	1<<21,	/* PCMCIA socket 0 interrupt */
+	GPIO_LOCK_i=		1<<22,	/* expansion pack lock/unlock */
+	GPIO_COM_DCD_i=		1<<23,	/* DCD from UART3 */
+	GPIO_OPT_IRQ_i=		1<<24,	/* expansion pack IRQ */
+	GPIO_COM_CTS_i=		1<<25,	/* CTS from UART3 */
+	GPIO_COM_RTS_o=		1<<26,	/* RTS to UART3 */
+	GPIO_OPT_IND_i=		1<<27,	/* expansion pack inserted */
+
+/* Peripheral Unit GPIO pin assignments: alternate functions */
+	GPIO_SSP_TXD_o=		1<<10,	/* SSP Transmit Data */
+	GPIO_SSP_RXD_i=		1<<11,	/* SSP Receive Data */
+	GPIO_SSP_SCLK_o=	1<<12,	/* SSP Sample CLocK */
+	GPIO_SSP_SFRM_o=	1<<13,	/* SSP Sample FRaMe */
+	/* ser. port 1: */
+	GPIO_UART_TXD_o=	1<<14,	/* UART Transmit Data */
+	GPIO_UART_RXD_i=	1<<15,	/* UART Receive Data */
+	GPIO_SDLC_SCLK_io=	1<<16,	/* SDLC Sample CLocK (I/O) */
+	GPIO_SDLC_AAF_o=	1<<17,	/* SDLC Abort After Frame */
+	GPIO_UART_SCLK1_i=	1<<18,	/* UART Sample CLocK 1 */
+	/* ser. port 4: */
+	GPIO_SSP_CLK_i=		1<<19,	/* SSP external CLocK */
+	/* ser. port 3: */
+	GPIO_UART_SCLK3_i=	1<<20,	/* UART Sample CLocK 3 */
+	/* ser. port 4: */
+	GPIO_MCP_CLK_i=		1<<21,	/* MCP CLocK */
+	/* test controller: */
+	GPIO_TIC_ACK_o=		1<<21,	/* TIC ACKnowledge */
+	GPIO_MBGNT_o=		1<<21,	/* Memory Bus GraNT */
+	GPIO_TREQA_i=		1<<22,	/* TIC REQuest A */
+	GPIO_MBREQ_i=		1<<22,	/* Memory Bus REQuest */
+	GPIO_TREQB_i=		1<<23,	/* TIC REQuest B */
+	GPIO_1Hz_o=			1<<25,	/* 1 Hz clock */
+	GPIO_RCLK_o=		1<<26,	/* internal (R) CLocK (O, fcpu/2) */
+	GPIO_32_768kHz_o=	1<<27,	/* 32.768 kHz clock (O, RTC) */
+};
+
+/*
+ *  types of interrupts
+ */
+enum
+{
+	GPIOrising,
+	GPIOfalling,
+	GPIOboth,
+	IRQ,
+};
+
+/* hardware registers */
+typedef struct Uartregs Uartregs;
+struct Uartregs
+{
+	ulong	ctl[4];
+	ulong	dummya;
+	ulong	data;
+	ulong	dummyb;
+	ulong	status[2];
+};
+Uartregs *uart3regs;
+
+/* general purpose I/O lines control registers */
+typedef struct GPIOregs GPIOregs;
+struct GPIOregs
+{
+	ulong	level;		/* 1 == high */
+	ulong	direction;	/* 1 == output */
+	ulong	set;		/* a 1 sets the bit, 0 leaves it alone */
+	ulong	clear;		/* a 1 clears the bit, 0 leaves it alone */
+	ulong	rising;		/* rising edge detect enable */
+	ulong	falling;	/* falling edge detect enable */
+	ulong	edgestatus;	/* writing a 1 bit clears */
+	ulong	altfunc;	/* turn on alternate function for any set bits */
+};
+
+extern GPIOregs *gpioregs;
+
+/* extra general purpose I/O bits, output only */
+enum
+{
+	EGPIO_prog_flash=	1<<0,
+	EGPIO_pcmcia_reset=	1<<1,
+	EGPIO_exppack_reset=	1<<2,
+	EGPIO_codec_reset=	1<<3,
+	EGPIO_exp_nvram_power=	1<<4,
+	EGPIO_exp_full_power=	1<<5,
+	EGPIO_lcd_3v=		1<<6,
+	EGPIO_rs232_power=	1<<7,
+	EGPIO_lcd_ic_power=	1<<8,
+	EGPIO_ir_power=		1<<9,
+	EGPIO_audio_power=	1<<10,
+	EGPIO_audio_ic_power=	1<<11,
+	EGPIO_audio_mute=	1<<12,
+	EGPIO_fir=		1<<13,	/* not set is sir */
+	EGPIO_lcd_5v=		1<<14,
+	EGPIO_lcd_9v=		1<<15,
+};
+extern ulong *egpioreg;
+
+/* Peripheral pin controller registers */
+typedef struct PPCregs PPCregs;
+struct PPCregs {
+	ulong	direction;
+	ulong	state;
+	ulong	assignment;
+	ulong	sleepdir;
+	ulong	flags;
+};
+extern PPCregs *ppcregs;
+
+/* Synchronous Serial Port controller registers */
+typedef struct SSPregs SSPregs;
+struct SSPregs {
+	ulong	control0;
+	ulong	control1;
+	ulong	dummy0;
+	ulong	data;
+	ulong	dummy1;
+	ulong	status;
+};
+extern SSPregs *sspregs;
+
+/* Multimedia Communications Port controller registers */
+typedef struct MCPregs MCPregs;
+struct MCPregs {
+	ulong	control0;
+	ulong	reserved0;
+	ulong	data0;
+	ulong	data1;
+	ulong	data2;
+	ulong	reserved1;
+	ulong	status;
+	ulong	reserved[11];
+	ulong	control1;
+};
+extern MCPregs *mcpregs;
+
+/*
+ *  memory configuration
+ */
+enum
+{
+	/* bit shifts for pcmcia access time counters */
+	MECR_io0=	0,
+	MECR_attr0=	5,
+	MECR_mem0=	10,
+	MECR_fast0=	11,
+	MECR_io1=	MECR_io0+16,
+	MECR_attr1=	MECR_attr0+16,
+	MECR_mem1=	MECR_mem0+16,
+	MECR_fast1=	MECR_fast0+16,
+};
+
+typedef struct MemConfRegs MemConfRegs;
+struct MemConfRegs
+{
+	ulong	mdcnfg;		/* dram */
+	ulong	mdcas00;	/* dram banks 0/1 */
+	ulong	mdcas01;
+	ulong	mdcas02;
+	ulong	msc0;		/* static */
+	ulong	msc1;
+	ulong	mecr;		/* pcmcia */
+	ulong	mdrefr;		/* dram refresh */
+	ulong	mdcas20;	/* dram banks 2/3 */
+	ulong	mdcas21;
+	ulong	mdcas22;
+	ulong	msc2;		/* static */
+	ulong	smcnfg;		/* SMROM config */
+};
+extern MemConfRegs *memconfregs;
+
+/*
+ *  power management
+ */
+typedef struct PowerRegs PowerRegs;
+struct PowerRegs
+{
+	ulong	pmcr;	/* Power manager control register */
+	ulong	pssr;	/* Power manager sleep status register */
+	ulong	pspr;	/* Power manager scratch pad register */
+	ulong	pwer;	/* Power manager wakeup enable register */
+	ulong	pcfr;	/* Power manager general configuration register */
+	ulong	ppcr;	/* Power manager PPL configuration register */
+	ulong	pgsr;	/* Power manager GPIO sleep state register */
+	ulong	posr;	/* Power manager oscillator status register */
+};
+extern PowerRegs *powerregs;
--- /dev/null
+++ b/os/boot/arm1110/l.s
@@ -1,0 +1,454 @@
+#include "mem.h"
+
+/*
+ * Entered here from Compaq's bootldr with MMU disabled.
+ */
+TEXT _start(SB), $-4
+	MOVW	$setR12(SB), R12		/* load the SB */
+_main:
+	/* SVC mode, interrupts disabled */
+	MOVW	$(PsrDirq|PsrDfiq|PsrMsvc), R1
+	MOVW	R1, CPSR
+
+	/* disable the MMU */
+	MOVW	$0x130, R1
+	MCR     CpMMU, 0, R1, C(CpControl), C(0x0)
+
+	/* flush caches */
+	MCR	CpMMU, 0, R0, C(CpCacheFlush), C(0x7), 0
+	/* drain prefetch */
+	MOVW	R0,R0						
+	MOVW	R0,R0
+	MOVW	R0,R0
+	MOVW	R0,R0
+
+	/* drain write buffer */
+	MCR	CpMMU, 0, R0, C(CpCacheFlush), C(0xa), 4
+
+	MOVW	$(MACHADDR+BY2PG), R13		/* stack */
+	SUB	$4, R13				/* link */
+	BL	main(SB)
+	BL	exit(SB)
+	/* we shouldn't get here */
+_mainloop:
+	B	_mainloop
+	BL	_div(SB)			/* hack to get _div etc loaded */
+
+/* flush tlb's */
+TEXT mmuinvalidate(SB), $-4
+	MCR	CpMMU, 0, R0, C(CpTLBFlush), C(0x7)
+	RET
+
+/* flush tlb's */
+TEXT mmuinvalidateaddr(SB), $-4
+	MCR	CpMMU, 0, R0, C(CpTLBFlush), C(0x6), 1
+	RET
+
+/* write back and invalidate i and d caches */
+TEXT cacheflush(SB), $-4
+	/* write back any dirty data */
+	MOVW	$0xe0000000,R0
+	ADD	$(8*1024),R0,R1
+_cfloop:
+	MOVW.P	32(R0),R2
+	CMP.S	R0,R1
+	BNE	_cfloop
+	
+	/* drain write buffer and invalidate i&d cache contents */
+	MCR	CpMMU, 0, R0, C(CpCacheFlush), C(0xa), 4
+	MCR	CpMMU, 0, R0, C(CpCacheFlush), C(0x7), 0
+
+	/* drain prefetch */
+	MOVW	R0,R0						
+	MOVW	R0,R0
+	MOVW	R0,R0
+	MOVW	R0,R0
+	RET
+
+/* write back d cache */
+TEXT cachewb(SB), $-4
+	/* write back any dirty data */
+_cachewb:
+	MOVW	$0xe0000000,R0
+	ADD	$(8*1024),R0,R1
+_cwbloop:
+	MOVW.P	32(R0),R2
+	CMP.S	R0,R1
+	BNE	_cwbloop
+	
+	/* drain write buffer */
+	MCR	CpMMU, 0, R0, C(CpCacheFlush), C(0xa), 4
+	RET
+
+/* write back a single cache line */
+TEXT cachewbaddr(SB), $-4
+	BIC	$31,R0
+	MCR	CpMMU, 0, R0, C(CpCacheFlush), C(0xa), 1
+	B	_wbflush
+
+/* write back a region of cache lines */
+TEXT cachewbregion(SB), $-4
+	MOVW	4(FP),R1
+	CMP.S	$(4*1024),R1
+	BGT	_cachewb
+	ADD	R0,R1
+	BIC	$31,R0
+_cwbrloop:
+	MCR	CpMMU, 0, R0, C(CpCacheFlush), C(0xa), 1
+	ADD	$32,R0
+	CMP.S	R0,R1
+	BGT	_cwbrloop
+	B	_wbflush
+
+/* invalidate the dcache */
+TEXT dcacheinvalidate(SB), $-4
+	MCR	CpMMU, 0, R0, C(CpCacheFlush), C(0x6)
+	RET
+
+/* invalidate the icache */
+TEXT icacheinvalidate(SB), $-4
+	MCR	CpMMU, 0, R0, C(CpCacheFlush), C(0x9)
+	RET
+
+/* drain write buffer */
+TEXT wbflush(SB), $-4
+_wbflush:
+	MCR	CpMMU, 0, R0, C(CpCacheFlush), C(0xa), 4
+	RET
+
+/* return cpu id */
+TEXT getcpuid(SB), $-4
+	MRC	CpMMU, 0, R0, C(CpCPUID), C(0x0)
+	RET
+
+/* return fault status */
+TEXT getfsr(SB), $-4
+	MRC	CpMMU, 0, R0, C(CpFSR), C(0x0)
+	RET
+
+/* return fault address */
+TEXT getfar(SB), $-4
+	MRC	CpMMU, 0, R0, C(CpFAR), C(0x0)
+	RET
+
+/* return fault address */
+TEXT putfar(SB), $-4
+	MRC	CpMMU, 0, R0, C(CpFAR), C(0x0)
+	RET
+
+/* set the translation table base */
+TEXT putttb(SB), $-4
+	MCR	CpMMU, 0, R0, C(CpTTB), C(0x0)
+	RET
+
+/*
+ *  enable mmu, i and d caches
+ */
+TEXT mmuenable(SB), $-4
+	MRC	CpMMU, 0, R0, C(CpControl), C(0x0)
+	ORR	$(CpCmmuena|CpCdcache|CpCicache|CpCwb), R0
+	MCR     CpMMU, 0, R0, C(CpControl), C(0x0)
+	RET
+
+TEXT mmudisable(SB), $-4
+	MRC	CpMMU, 0, R0, C(CpControl), C(0x0)
+	BIC	$(CpCmmuena|CpCdcache|CpCicache|CpCwb|CpCvivec), R0
+	MCR     CpMMU, 0, R0, C(CpControl), C(0x0)
+	RET
+
+/*
+ *  use exception vectors at 0xffff0000
+ */
+TEXT mappedIvecEnable(SB), $-4
+	MRC	CpMMU, 0, R0, C(CpControl), C(0x0)
+	ORR	$(CpCvivec), R0
+	MCR     CpMMU, 0, R0, C(CpControl), C(0x0)
+	RET
+TEXT mappedIvecDisable(SB), $-4
+	MRC	CpMMU, 0, R0, C(CpControl), C(0x0)
+	BIC	$(CpCvivec), R0
+	MCR     CpMMU, 0, R0, C(CpControl), C(0x0)
+	RET
+
+/* set the translation table base */
+TEXT putdac(SB), $-4
+	MCR	CpMMU, 0, R0, C(CpDAC), C(0x0)
+	RET
+
+/* set address translation pid */
+TEXT putpid(SB), $-4
+	MCR	CpMMU, 0, R0, C(CpPID), C(0x0)
+	RET
+
+/*
+ *  set the stack value for the mode passed in R0
+ */
+TEXT setr13(SB), $-4
+	MOVW	4(FP), R1
+
+	MOVW	CPSR, R2
+	BIC	$PsrMask, R2, R3
+	ORR	R0, R3
+	MOVW	R3, CPSR
+
+	MOVW	R13, R0
+	MOVW	R1, R13
+
+	MOVW	R2, CPSR
+	RET
+
+/*
+ *  exception vectors, copied by trapinit() to somewhere useful
+ */
+
+TEXT vectors(SB), $-4
+	MOVW	0x18(R15), R15			/* reset */
+	MOVW	0x18(R15), R15			/* undefined */
+	MOVW	0x18(R15), R15			/* SWI */
+	MOVW	0x18(R15), R15			/* prefetch abort */
+	MOVW	0x18(R15), R15			/* data abort */
+	MOVW	0x18(R15), R15			/* reserved */
+	MOVW	0x18(R15), R15			/* IRQ */
+	MOVW	0x18(R15), R15			/* FIQ */
+
+TEXT vtable(SB), $-4
+	WORD	$_vsvc(SB)			/* reset, in svc mode already */
+	WORD	$_vund(SB)			/* undefined, switch to svc mode */
+	WORD	$_vsvc(SB)			/* swi, in svc mode already */
+	WORD	$_vpabt(SB)			/* prefetch abort, switch to svc mode */
+	WORD	$_vdabt(SB)			/* data abort, switch to svc mode */
+	WORD	$_vsvc(SB)			/* reserved */
+	WORD	$_virq(SB)			/* IRQ, switch to svc mode */
+	WORD	$_vfiq(SB)			/* FIQ, switch to svc mode */
+
+TEXT _vrst(SB), $-4
+	BL	resettrap(SB)
+
+TEXT _vsvc(SB), $-4			/* SWI */
+	MOVW.W	R14, -4(R13)		/* ureg->pc = interupted PC */
+	MOVW	SPSR, R14		/* ureg->psr = SPSR */
+	MOVW.W	R14, -4(R13)		/* ... */
+	MOVW	$PsrMsvc, R14		/* ureg->type = PsrMsvc */
+	MOVW.W	R14, -4(R13)		/* ... */
+	MOVM.DB.W.S [R0-R14], (R13)	/* save user level registers, at end r13 points to ureg */
+	MOVW	$setR12(SB), R12	/* Make sure we've got the kernel's SB loaded */
+	MOVW	R13, R0			/* first arg is pointer to ureg */
+	SUB	$8, R13			/* space for argument+link */
+
+	BL	syscall(SB)
+
+	ADD	$(8+4*15), R13		/* make r13 point to ureg->type */
+	MOVW	8(R13), R14		/* restore link */
+	MOVW	4(R13), R0		/* restore SPSR */
+	MOVW	R0, SPSR		/* ... */
+	MOVM.DB.S (R13), [R0-R14]	/* restore registers */
+	ADD	$8, R13			/* pop past ureg->{type+psr} */
+	RFE				/* MOVM.IA.S.W (R13), [R15] */
+
+TEXT _vund(SB), $-4			/* undefined */
+	MOVM.IA	[R0-R4], (R13)		/* free some working space */
+	MOVW	$PsrMund, R0
+	B	_vswitch
+
+TEXT _vpabt(SB), $-4			/* prefetch abort */
+	MOVM.IA	[R0-R4], (R13)		/* free some working space */
+	MOVW	$PsrMabt, R0		/* r0 = type */
+	B	_vswitch
+
+TEXT _vdabt(SB), $-4			/* prefetch abort */
+	MOVM.IA	[R0-R4], (R13)		/* free some working space */
+	MOVW	$(PsrMabt+1), R0		/* r0 = type */
+	B	_vswitch
+
+TEXT _virq(SB), $-4			/* IRQ */
+	MOVM.IA	[R0-R4], (R13)		/* free some working space */
+	MOVW	$PsrMirq, R0		/* r0 = type */
+	B	_vswitch
+
+	/*
+	 *  come here with type in R0 and R13 pointing above saved [r0-r4]
+	 *  and type in r0.  we'll switch to SVC mode and then call trap.
+	 */
+_vswitch:
+	MOVW	SPSR, R1		/* save SPSR for ureg */
+	MOVW	R14, R2			/* save interrupted pc for ureg */
+	MOVW	R13, R3			/* save pointer to where the original [R0-R3] are */
+
+	/* switch to svc mode */
+	MOVW	CPSR, R14
+	BIC	$PsrMask, R14
+	ORR	$(PsrDirq|PsrDfiq|PsrMsvc), R14
+	MOVW	R14, CPSR
+
+	/* interupted code kernel or user? */
+	AND.S	$0xf, R1, R4
+	BEQ	_userexcep
+
+	/* here for trap from SVC mode */
+	MOVM.DB.W [R0-R2], (R13)	/* set ureg->{type, psr, pc}; r13 points to ureg->type  */
+	MOVM.IA	  (R3), [R0-R4]		/* restore [R0-R4] from previous mode's stack */
+	MOVM.DB.W [R0-R14], (R13)	/* save kernel level registers, at end r13 points to ureg */
+	MOVW	$setR12(SB), R12	/* Make sure we've got the kernel's SB loaded */
+	MOVW	R13, R0			/* first arg is pointer to ureg */
+	SUB	$8, R13			/* space for argument+link (for debugger) */
+	MOVW	$0xdeaddead,R11		/* marker */
+
+	BL	trap(SB)
+
+	ADD	$(8+4*15), R13		/* make r13 point to ureg->type */
+	MOVW	8(R13), R14		/* restore link */
+	MOVW	4(R13), R0		/* restore SPSR */
+	MOVW	R0, SPSR		/* ... */
+	MOVM.DB (R13), [R0-R14]	/* restore registers */
+	ADD	$8, R13			/* pop past ureg->{type+psr} */
+	RFE				/* MOVM.IA.S.W (R13), [R15] */
+
+	/* here for trap from USER mode */
+_userexcep:
+	MOVM.DB.W [R0-R2], (R13)	/* set ureg->{type, psr, pc}; r13 points to ureg->type  */
+	MOVM.IA	  (R3), [R0-R4]		/* restore [R0-R4] from previous mode's stack */
+	MOVM.DB.W.S [R0-R14], (R13)	/* save kernel level registers, at end r13 points to ureg */
+	MOVW	$setR12(SB), R12	/* Make sure we've got the kernel's SB loaded */
+	MOVW	R13, R0			/* first arg is pointer to ureg */
+	SUB	$8, R13			/* space for argument+link (for debugger) */
+
+	BL	trap(SB)
+
+	ADD	$(8+4*15), R13		/* make r13 point to ureg->type */
+	MOVW	8(R13), R14		/* restore link */
+	MOVW	4(R13), R0		/* restore SPSR */
+	MOVW	R0, SPSR		/* ... */
+	MOVM.DB.S (R13), [R0-R14]	/* restore registers */
+	ADD	$8, R13			/* pop past ureg->{type+psr} */
+	RFE				/* MOVM.IA.S.W (R13), [R15] */
+
+TEXT _vfiq(SB), $-4			/* FIQ */
+	RFE				/* FIQ is special, ignore it for now */
+
+/*
+ *  This is the first jump from kernel to user mode.
+ *  Fake a return from interrupt.
+ *
+ *  Enter with R0 containing the user stack pointer.
+ *  UTZERO + 0x20 is always the entry point.
+ *  
+ */
+TEXT touser(SB),$-4
+	/* store the user stack pointer into the USR_r13 */
+	MOVM.DB.W [R0], (R13)
+	MOVM.S.IA.W (R13),[R13]
+
+	/* set up a PSR for user level */
+	MOVW	$(PsrMusr), R0
+	MOVW	R0,SPSR
+
+	/* save the PC on the stack */
+	MOVW	$(UTZERO+0x20), R0
+	MOVM.DB.W [R0],(R13)
+
+	/* return from interrupt */
+	RFE				/* MOVM.IA.S.W (R13), [R15] */
+	
+/*
+ *  here to jump to a newly forked process
+ */
+TEXT forkret(SB),$-4
+	ADD	$(4*15), R13		/* make r13 point to ureg->type */
+	MOVW	8(R13), R14		/* restore link */
+	MOVW	4(R13), R0		/* restore SPSR */
+	MOVW	R0, SPSR		/* ... */
+	MOVM.DB.S (R13), [R0-R14]	/* restore registers */
+	ADD	$8, R13			/* pop past ureg->{type+psr} */
+	RFE				/* MOVM.IA.S.W (R13), [R15] */
+
+TEXT splhi(SB), $-4
+	/* save caller pc in Mach */
+	MOVW	$(MACHADDR+0x04),R2
+	MOVW	R14,0(R2)
+	/* turn off interrupts */
+	MOVW	CPSR, R0
+	ORR	$(PsrDfiq|PsrDirq), R0, R1
+	MOVW	R1, CPSR
+	RET
+
+TEXT spllo(SB), $-4
+	MOVW	CPSR, R0
+	BIC	$(PsrDfiq|PsrDirq), R0, R1
+	MOVW	R1, CPSR
+	RET
+
+TEXT splx(SB), $-4
+	/* save caller pc in Mach */
+	MOVW	$(MACHADDR+0x04),R2
+	MOVW	R14,0(R2)
+	/* reset interrupt level */
+	MOVW	R0, R1
+	MOVW	CPSR, R0
+	MOVW	R1, CPSR
+	RET
+
+TEXT splxpc(SB), $-4				/* for iunlock */
+	MOVW	R0, R1
+	MOVW	CPSR, R0
+	MOVW	R1, CPSR
+	RET
+
+TEXT spldone(SB), $0
+	RET
+
+TEXT islo(SB), $-4
+	MOVW	CPSR, R0
+	AND	$(PsrDfiq|PsrDirq), R0
+	EOR	$(PsrDfiq|PsrDirq), R0
+	RET
+
+TEXT cpsrr(SB), $-4
+	MOVW	CPSR, R0
+	RET
+
+TEXT spsrr(SB), $-4
+	MOVW	SPSR, R0
+	RET
+
+TEXT getcallerpc(SB), $-4
+	MOVW	0(R13), R0
+	RET
+
+TEXT tas(SB), $-4
+	MOVW	R0, R1
+	MOVW	$0xDEADDEAD, R2
+	SWPW	R2, (R1), R0
+	RET
+
+TEXT setlabel(SB), $-4
+	MOVW	R13, 0(R0)			/* sp */
+	MOVW	R14, 4(R0)			/* pc */
+	MOVW	$0, R0
+	RET
+
+TEXT gotolabel(SB), $-4
+	MOVW	0(R0), R13			/* sp */
+	MOVW	4(R0), R14			/* pc */
+	MOVW	$1, R0
+	RET
+
+
+/* The first MCR instruction of this function needs to be on a cache-line
+ * boundary; to make this happen, it will be copied (in trap.c).
+ *
+ * Doze puts the machine into idle mode.  Any interrupt will get it out
+ * at the next instruction (the RET, to be precise).
+ */
+TEXT _doze(SB), $-4
+	MOVW	$UCDRAMZERO, R1
+	MOVW	R0,R0
+	MOVW	R0,R0
+	MOVW	R0,R0
+	MOVW	R0,R0
+	MOVW	R0,R0
+	MOVW	R0,R0
+	MOVW	R0,R0
+	MCR     CpPWR, 0, R0, C(CpTest), C(0x2), 2
+	MOVW	(R1), R0
+	MCR     CpPWR, 0, R0, C(CpTest), C(0x8), 2
+	RET
--- /dev/null
+++ b/os/boot/arm1110/lib.h
@@ -1,0 +1,143 @@
+/*
+ * functions (possibly) linked in, complete, from libc.
+ */
+
+/*
+ * mem routines
+ */
+extern	void	*memccpy(void*, void*, int, long);
+extern	void	*memset(void*, int, long);
+extern	int	memcmp(void*, void*, long);
+extern	void	*memmove(void*, void*, long);
+extern	void	*memchr(void*, int, long);
+
+/*
+ * string routines
+ */
+extern	char	*strcat(char*, char*);
+extern	char	*strchr(char*, char);
+extern	char	*strrchr(char*, char);
+extern	int	strcmp(char*, char*);
+extern	char	*strcpy(char*, char*);
+extern	char	*strncat(char*, char*, long);
+extern	char	*strncpy(char*, char*, long);
+extern	int	strncmp(char*, char*, long);
+extern	long	strlen(char*);
+extern	char*	strstr(char*, char*);
+extern	int	atoi(char*);
+
+enum
+{
+	UTFmax		= 3,	/* maximum bytes per rune */
+	Runesync	= 0x80,	/* cannot represent part of a UTF sequence */
+	Runeself	= 0x80,	/* rune and UTF sequences are the same (<) */
+	Runeerror	= 0x80,	/* decoding error in UTF */
+};
+
+/*
+ * rune routines
+ */
+extern	int	runetochar(char*, Rune*);
+extern	int	chartorune(Rune*, char*);
+extern	char*	utfrune(char*, long);
+extern	int	utflen(char*);
+extern	int	runelen(long);
+
+extern	int	abs(int);
+
+/*
+ * print routines
+ */
+typedef struct Cconv Fconv;
+extern	char*	donprint(char*, char*, char*, void*);
+extern	int	sprint(char*, char*, ...);
+extern	char*	seprint(char*, char*, char*, ...);
+extern	int	snprint(char*, int, char*, ...);
+extern	int	print(char*, ...);
+
+/*
+ * one-of-a-kind
+ */
+extern	char*	cleanname(char*);
+extern	ulong	getcallerpc(void*);
+extern	long	strtol(char*, char**, int);
+extern	ulong	strtoul(char*, char**, int);
+extern	vlong	strtoll(char*, char**, int);
+extern	uvlong	strtoull(char*, char**, int);
+extern	char	etext[];
+extern	char	edata[];
+extern	char	end[];
+extern	int	getfields(char*, char**, int, int, char*);
+
+/*
+ * Syscall data structures
+ */
+#define	MORDER	0x0003	/* mask for bits defining order of mounting */
+#define	MREPL	0x0000	/* mount replaces object */
+#define	MBEFORE	0x0001	/* mount goes before others in union directory */
+#define	MAFTER	0x0002	/* mount goes after others in union directory */
+#define	MCREATE	0x0004	/* permit creation in mounted directory */
+#define	MCACHE	0x0010	/* cache some data */
+#define	MMASK	0x001F	/* all bits on */
+
+#define	OREAD	0	/* open for read */
+#define	OWRITE	1	/* write */
+#define	ORDWR	2	/* read and write */
+#define	OEXEC	3	/* execute, == read but check execute permission */
+#define	OTRUNC	16	/* or'ed in (except for exec), truncate file first */
+#define	OCEXEC	32	/* or'ed in, close on exec */
+#define	ORCLOSE	64	/* or'ed in, remove on close */
+
+#define	NCONT	0	/* continue after note */
+#define	NDFLT	1	/* terminate after note */
+#define	NSAVE	2	/* clear note but hold state */
+#define	NRSTR	3	/* restore saved state */
+
+typedef struct Qid	Qid;
+typedef struct Dir	Dir;
+typedef struct Waitmsg	Waitmsg;
+
+#define	ERRLEN		64
+#define	DIRLEN		116
+#define	NAMELEN		28
+
+struct Qid
+{
+	ulong	path;
+	ulong	vers;
+};
+
+struct Dir
+{
+	char	name[NAMELEN];
+	char	uid[NAMELEN];
+	char	gid[NAMELEN];
+	Qid	qid;
+	ulong	mode;
+	long	atime;
+	long	mtime;
+	vlong	length;
+	short	type;
+	short	dev;
+};
+
+struct Waitmsg
+{
+	char	pid[12];	/* of loved one */
+	char	time[3*12];	/* of loved one and descendants */
+	char	msg[ERRLEN];
+};
+
+/*
+ *  locks
+ */
+typedef
+struct Lock {
+	int	val;
+} Lock;
+
+extern int	_tas(int*);
+
+extern	void	lock(Lock*);
+extern	void	unlock(Lock*);
+extern	int	canlock(Lock*);
--- /dev/null
+++ b/os/boot/arm1110/map
@@ -1,0 +1,10 @@
+defn acidmap()
+{
+	local dfoffset;
+
+	dfoffset = map()[1][3];
+	map({"text", _start, etext, 0x20});
+	map({"data", etext+1, edata, dfoffset});
+	print("Set map for plan 9 kernel image\n");
+	print("btext ", _start, " etext ", etext, "\n");
+}
--- /dev/null
+++ b/os/boot/arm1110/mem.h
@@ -1,0 +1,215 @@
+/*
+ * Memory and machine-specific definitions.  Used in C and assembler.
+ */
+
+/*
+ * Sizes
+ */
+#define	BI2BY		8			/* bits per byte */
+#define BI2WD		32			/* bits per word */
+#define	BY2WD		4			/* bytes per word */
+#define	BY2V		8			/* bytes per double word */
+#define	BY2PG		4096			/* bytes per page */
+#define	WD2PG		(BY2PG/BY2WD)		/* words per page */
+#define	PGSHIFT		12			/* log(BY2PG) */
+#define ROUND(s, sz)	(((s)+(sz-1))&~(sz-1))
+#define PGROUND(s)	ROUND(s, BY2PG)
+#define	BLOCKALIGN	8
+
+#define	MAXMACH		1			/* max # cpus system can run */
+
+/*
+ * Time
+ */
+#define	HZ		(20)				/* clock frequency */
+#define	MS2HZ		(1000/HZ)			/* millisec per clock tick */
+#define	TK2SEC(t)	((t)/HZ)			/* ticks to seconds */
+#define	TK2MS(t)	((((ulong)(t))*1000)/HZ)	/* ticks to milliseconds */
+#define	MS2TK(t)	((((ulong)(t))*HZ)/1000)	/* milliseconds to ticks */
+
+/*
+ *  Virtual addresses:
+ *
+ *  We direct map all discovered DRAM and the area twixt 0xe0000000 and
+ *  0xe8000000 used to provide zeros for cache flushing.
+ *
+ *  Flash is mapped to 0xb0000000 and special registers are mapped
+ *  on demand to areas starting at 0xa0000000.
+ *
+ *  The direct mapping is convenient but not necessary.  It means
+ *  that we don't have to turn on the MMU till well into the
+ *  kernel.  This can be changed by providing a mapping in l.s
+ *  before calling main.
+ */
+#define	UZERO		0			/* base of user address space */
+#define	UTZERO		(UZERO+BY2PG)		/* first address in user text */
+#define	KZERO		0xC0000000		/* base of kernel address space */
+#define	KTZERO		0xC0008000		/* first address in kernel text */
+#define	EMEMZERO	0x90000000		/* 256 meg for add on memory */
+#define	EMEMTOP		0xA0000000		/* ... */
+#define	REGZERO		0xA0000000		/* 128 meg for mapspecial regs */
+#define	REGTOP		0xA8000000		/* ... */
+#define	FLASHZERO	0xB0000000		/* 128 meg for flash */
+#define	FLASHTOP	0xB8000000		/* ... */
+#define	DRAMZERO	0xC0000000		/* 128 meg for dram */
+#define DRAMTOP		0xC8000000		/* ... */
+#define	UCDRAMZERO	0xC8000000		/* 128 meg for dram (uncached/unbuffered) */
+#define UCDRAMTOP	0xD0000000		/* ... */
+#define	NULLZERO	0xE0000000		/* 128 meg for cache flush zeroes */
+#define NULLTOP		0xE8000000		/* ... */
+#define	USTKTOP		0x2000000		/* byte just beyond user stack */
+#define	USTKSIZE	(8*1024*1024)		/* size of user stack */
+#define	TSTKTOP		(USTKTOP-USTKSIZE)	/* end of new stack in sysexec */
+#define TSTKSIZ 	100
+#define MACHADDR	(KZERO+0x00001000)
+#define	EVECTORS	0xFFFF0000		/* virt base of exception vectors */
+
+#define KSTACK		(16*1024)		/* Size of kernel stack */
+
+#define	FLATESIZE	(700*1024)		/* maximum size of compressed image */
+
+/*
+ *  Offsets into flash
+ */
+#define Flash_bootldr	(FLASHZERO+0x0)		/* boot loader */
+#define Flash_kernel	(FLASHZERO+0x10000)	/* boot kernel */
+#define	Flash_tar	(FLASHZERO+0x100000)	/* tar file containing fs.sac */
+
+/*
+ *  virtual MMU
+ */
+#define PTEMAPMEM	(1024*1024)	
+#define	PTEPERTAB	(PTEMAPMEM/BY2PG)
+#define SEGMAPSIZE	1984
+#define SSEGMAPSIZE	16
+#define PPN(x)		((x)&~(BY2PG-1))
+
+/*
+ *  SA1110 definitions
+ */
+
+/*
+ *  memory physical addresses
+ */
+#define PHYSFLASH0	0x00000000
+#define PHYSDRAM0	0xC0000000
+#define	PHYSNULL0	0xE0000000
+
+/*
+ *  peripheral control module physical addresses
+ */
+#define USBREGS		0x80000000	/* serial port 0 - USB */
+#define UART1REGS	0x80010000	/* serial port 1 - UART */
+#define GPCLKREGS	0x80020060	/* serial port 1 - general purpose clock */
+#define UART2REGS	0x80030000	/* serial port 2 - low speed IR */
+#define HSSPREGS	0x80040060	/* serial port 2 - high speed IR */
+#define UART3REGS	0x80050000	/* serial port 3 - RS232 UART */
+#define MCPREGS		0x80060000	/* serial port 4 - multimedia comm port */
+#define SSPREGS		0x80070060	/* serial port 4 - synchronous serial port */
+#define OSTIMERREGS	0x90000000	/* operating system timer registers */
+#define POWERREGS	0x90020000	/* power management */
+#define GPIOREGS	0x90040000	/* 28 general purpose IO pins */
+#define INTRREGS	0x90050000	/* interrupt registers */
+#define PPCREGS		0x90060000	/* peripheral pin controller */
+#define MEMCONFREGS	0xA0000000	/* memory configuration */
+#define LCDREGS		0xB0100000	/* display */
+
+/*
+ *  PCMCIA addresses
+ */
+#define PHYSPCM0REGS	0x20000000
+#define PYHSPCM0ATTR	0x28000000
+#define PYHSPCM0MEM	0x2C000000
+#define PHYSPCM1REGS	0x30000000
+#define PYHSPCM1ATTR	0x38000000
+#define PYHSPCM1MEM	0x3C000000
+
+/*
+ *  Program Status Registers
+ */
+#define PsrMusr		0x00000010	/* mode */
+#define PsrMfiq		0x00000011
+#define PsrMirq		0x00000012
+#define PsrMsvc		0x00000013
+#define PsrMabt		0x00000017
+#define PsrMund		0x0000001B
+#define PsrMask		0x0000001F
+
+#define PsrDfiq		0x00000040	/* disable FIQ interrupts */
+#define PsrDirq		0x00000080	/* disable IRQ interrupts */
+
+#define PsrV		0x10000000	/* overflow */
+#define PsrC		0x20000000	/* carry/borrow/extend */
+#define PsrZ		0x40000000	/* zero */
+#define PsrN		0x80000000	/* negative/less than */
+
+/*
+ *  Coprocessors
+ */
+#define CpMMU		15
+#define CpPWR		15
+
+/*
+ *  Internal MMU coprocessor registers
+ */
+#define CpCPUID		0		/* R: */
+#define CpControl	1		/* R: */
+#define CpTTB		2		/* RW: translation table base */
+#define CpDAC		3		/* RW: domain access control */
+#define CpFSR		5		/* RW: fault status */
+#define CpFAR		6		/* RW: fault address */
+#define CpCacheFlush	7		/* W: cache flushing, wb draining*/
+#define CpTLBFlush	8		/* W: TLB flushing */
+#define CpRBFlush	9		/* W: Read Buffer ops */
+#define CpPID		13		/* RW: PID for virtual mapping */
+#define	CpBpt		14		/* W: Breakpoint register */
+#define CpTest		15		/* W: Test, Clock and Idle Control */
+
+/*
+ *  CpControl
+ */
+#define CpCmmuena	0x00000001	/* M: MMU enable */
+#define CpCalign	0x00000002	/* A: alignment fault enable */
+#define CpCdcache	0x00000004	/* C: data cache on */
+#define CpCwb		0x00000008	/* W: write buffer turned on */
+#define CpCi32		0x00000010	/* P: 32-bit program space */
+#define CpCd32		0x00000020	/* D: 32-bit data space */
+#define CpCbe		0x00000080	/* B: big-endian operation */
+#define CpCsystem	0x00000100	/* S: system permission */
+#define CpCrom		0x00000200	/* R: ROM permission */
+#define CpCicache	0x00001000	/* I: instruction cache on */
+#define CpCvivec	0x00002000	/* X: virtual interrupt vector adjust */
+
+/*
+ *  fault codes
+ */
+#define	FCterm		0x2	/* terminal */
+#define	FCvec		0x0	/* vector */
+#define	FCalignf	0x1	/* unaligned full word data access */
+#define	FCalignh	0x3	/* unaligned half word data access */
+#define	FCl1abort	0xc	/* level 1 external abort on translation */
+#define	FCl2abort	0xe	/* level 2 external abort on translation */
+#define	FCtransSec	0x5	/* section translation */
+#define	FCtransPage	0x7	/* page translation */
+#define	FCdomainSec	0x9	/* section domain  */
+#define	FCdomainPage	0x11	/* page domain */
+#define	FCpermSec	0x9	/* section permissions  */
+#define	FCpermPage	0x11	/* page permissions */
+#define	FCabortLFSec	0x4	/* external abort on linefetch for section */
+#define	FCabortLFPage	0x6	/* external abort on linefetch for page */
+#define	FCabortNLFSec	0x8	/* external abort on non-linefetch for section */
+#define	FCabortNLFPage	0xa	/* external abort on non-linefetch for page */
+
+/*
+ *  PTE bits used by fault.h.  mmu.c translates them to real values.
+ */
+#define	PTEVALID	(1<<0)
+#define	PTERONLY	0	/* this is implied by the absence of PTEWRITE */
+#define	PTEWRITE	(1<<1)
+#define	PTEUNCACHED	(1<<2)
+#define PTEKERNEL	(1<<3)	/* no user access */
+
+/*
+ *  H3650 specific definitions
+ */
+#define EGPIOREGS	0x49000000	/* Additional GPIO register */
--- /dev/null
+++ b/os/boot/arm1110/mkfile
@@ -1,0 +1,86 @@
+<../../../mkconfig
+objtype=arm
+SYSTARG=$OSTARG
+OBJTYPE=arm
+BIN=$ROOT/Inferno/$OBJTYPE
+LIBDIR=$ROOT/Inferno/$OBJTYPE/lib
+LIBDIRS=../libflate $ROOT/libkern
+LIBS=\
+	libflate\
+	libkern\
+
+LIBFILES=${LIBS:%=$LIBDIR/%.a}
+<$ROOT/mkfiles/mkfile-$SYSTARG-$OBJTYPE
+
+BIN=$ROOT/Inferno/$OBJTYPE
+
+TARG=\
+	inflate\
+
+INFLATE=\
+	il.$O\
+	imain.$O\
+
+CORE=\
+	uart.$O\
+	inflate.$O\
+	donprint.$O\
+	print.$O\
+
+HFILES=\
+	mem.h\
+
+CFLAGS=-FVw -I.  -I$ROOT/Inferno/$OBJTYPE/include -I$ROOT/include
+
+all:V:	$TARG
+
+install:V:	$BIN/$TARG
+
+$BIN/%:	%
+	cp $stem $BIN/$stem
+
+inflate: $INFLATE $CORE $LIBFILES
+	$LD -o s$target -R4 -T0xC0200010 -l $prereq
+	$LD -o _$target -H5 -R4 -T0xC0200010 -l $prereq
+	dd -conv sync -ibs 20k -if _$target -of $target
+
+%.$O:	%.s
+	$AS $stem.s
+
+%.$O:	%.c
+	$CC $CFLAGS $stem.c
+
+%.$O:	$HFILES
+
+clean:
+	rm -f *.[$OS] [$OS].out y.tab.? y.debug y.output $TARG _$TARG s$TARG
+
+
+# added to cause libflate to be made automatically:
+
+$ROOT/Inferno/$OBJTYPE/lib/lib%.a:Q:	all-$SHELLTYPE
+	#
+
+rc-lib%.a nt-lib%.a:VQ:
+	echo '@{builtin cd ' $ROOT/lib$stem ';mk SHELLTYPE=$SHELLTYPE SYSTARG=$SYSTARG OBJTYPE=$OBJTYPE install}'
+	@{builtin cd  $ROOT/lib$stem ;mk 'SHELLTYPE='$SHELLTYPE 'SYSTARG='$SYSTARG 'OBJTYPE='$OBJTYPE install}
+
+sh-lib%.a:VQ:
+	echo "(cd $ROOT/lib$stem ; mk SHELLTYPE=$SHELLTYPE SYSTARG=$SYSTARG OBJTYPE=$OBJTYPE install)"
+	(cd $ROOT/lib$stem ; mk SHELLTYPE=$SHELLTYPE SYSTARG=$SYSTARG OBJTYPE=$OBJTYPE install)
+
+%-sh:QV:
+		for i in $LIBDIRS
+		do
+			echo "(cd $i ; mk SHELLTYPE=$SHELLTYPE SYSTARG=$SYSTARG OBJTYPE=$OBJTYPE $stem)"
+			(cd $i; mk 'SHELLTYPE='$SHELLTYPE 'SYSTARG='$SYSTARG 'OBJTYPE='$OBJTYPE $stem)
+		done
+
+%-rc %-nt:QV:
+		for (i in $LIBDIRS)
+		{
+			echo '@{cd $i ; mk SHELLTYPE=$SHELLTYPE SYSTARG=$SYSTARG OBJTYPE=$OBJTYPE $stem}'
+			@{cd $i; mk 'SHELLTYPE='$SHELLTYPE 'SYSTARG='$SYSTARG 'OBJTYPE='$OBJTYPE $stem}
+		}
+
+nuke:V:		clean nuke-$SHELLTYPE
--- /dev/null
+++ b/os/boot/arm1110/print.c
@@ -1,0 +1,56 @@
+#include "u.h"
+#include "lib.h"
+#include "fns.h"
+#include "dat.h"
+
+
+#define	SIZE	1024
+
+int
+print(char *fmt, ...)
+{
+	char buf[SIZE], *out;
+	va_list arg;
+
+	va_start(arg, fmt);
+	out = donprint(buf, buf+SIZE, fmt, arg);
+	va_end(arg);
+	serialputs(buf, out-buf);
+	return out-buf;
+}
+
+int
+sprint(char *buf, char *fmt, ...)
+{
+	char *out;
+	va_list arg;
+
+	va_start(arg, fmt);
+	out = donprint(buf, buf+SIZE, fmt, arg);
+	va_end(arg);
+	return out-buf;
+}
+
+int
+snprint(char *buf, int len, char *fmt, ...)
+{
+	char *out;
+	va_list arg;
+
+	va_start(arg, fmt);
+	out = donprint(buf, buf+len, fmt, arg);
+	va_end(arg);
+	return out-buf;
+}
+
+char*
+seprint(char *buf, char *e, char *fmt, ...)
+{
+	char *out;
+	va_list arg;
+
+	va_start(arg, fmt);
+	out = donprint(buf, e, fmt, arg);
+	va_end(arg);
+	return out;
+}
--- /dev/null
+++ b/os/boot/arm1110/uart.c
@@ -1,0 +1,69 @@
+#include	"u.h"
+#include	"lib.h"
+#include	"mem.h"
+#include	"dat.h"
+#include	"fns.h"
+#include	"io.h"
+
+enum
+{
+	/* ctl[0] bits */
+	Parity=		1<<0,
+	Even=		1<<1,
+	Stop2=		1<<2,
+	Bits8=		1<<3,
+	SCE=		1<<4,	/* synchronous clock enable */
+	RCE=		1<<5,	/* rx on falling edge of clock */
+	TCE=		1<<6,	/* tx on falling edge of clock */
+
+	/* ctl[3] bits */
+	Rena=		1<<0,	/* receiver enable */
+	Tena=		1<<1,	/* transmitter enable */
+	Break=		1<<2,	/* force TXD3 low */
+	Rintena=	1<<3,	/* enable receive interrupt */
+	Tintena=	1<<4,	/* enable transmitter interrupt */
+	Loopback=	1<<5,	/* loop back data */
+
+	/* data bits */
+	DEparity=	1<<8,	/* parity error */
+	DEframe=	1<<9,	/* framing error */
+	DEoverrun=	1<<10,	/* overrun error */
+
+	/* status[0] bits */
+	Tint=		1<<0,	/* transmit fifo half full interrupt */
+	Rint0=		1<<1,	/* receiver fifo 1/3-2/3 full */
+	Rint1=		1<<2,	/* receiver fifo not empty and receiver idle */
+	Breakstart=	1<<3,
+	Breakend=	1<<4,
+	Fifoerror=	1<<5,	/* fifo error */
+
+	/* status[1] bits */
+	Tbusy=		1<<0,	/* transmitting */
+	Rnotempty=	1<<1,	/* receive fifo not empty */
+	Tnotfull=	1<<2,	/* transmit fifo not full */
+	ParityError=	1<<3,
+	FrameError=	1<<4,
+	Overrun=	1<<5,
+};
+
+Uartregs *uart3regs = UART3REGS;
+
+
+/*
+ *  for iprint, just write it
+ */
+void
+serialputs(char *str, int n)
+{
+	Uartregs *ur;
+
+	ur = uart3regs;
+	while(n-- > 0){
+		/* wait for output ready */
+		while((ur->status[1] & Tnotfull) == 0)
+			;
+		ur->data = *str++;
+	}
+	while((ur->status[1] & Tbusy))
+		;
+}
--- /dev/null
+++ b/os/boot/libflate/LICENCE
@@ -1,0 +1,237 @@
+Lucent Public License Version 1.02
+
+THE ACCOMPANYING PROGRAM IS PROVIDED UNDER THE TERMS OF THIS PUBLIC
+LICENSE ("AGREEMENT"). ANY USE, REPRODUCTION OR DISTRIBUTION OF THE
+PROGRAM CONSTITUTES RECIPIENT'S ACCEPTANCE OF THIS AGREEMENT.
+
+1. DEFINITIONS
+
+"Contribution" means:
+
+  a. in the case of Lucent Technologies Inc. ("LUCENT"), the Original
+     Program, and
+  b. in the case of each Contributor,
+
+     i. changes to the Program, and
+    ii. additions to the Program;
+
+    where such changes and/or additions to the Program were added to the
+    Program by such Contributor itself or anyone acting on such
+    Contributor's behalf, and the Contributor explicitly consents, in
+    accordance with Section 3C, to characterization of the changes and/or
+    additions as Contributions.
+
+"Contributor" means LUCENT and any other entity that has Contributed a
+Contribution to the Program.
+
+"Distributor" means a Recipient that distributes the Program,
+modifications to the Program, or any part thereof.
+
+"Licensed Patents" mean patent claims licensable by a Contributor
+which are necessarily infringed by the use or sale of its Contribution
+alone or when combined with the Program.
+
+"Original Program" means the original version of the software
+accompanying this Agreement as released by LUCENT, including source
+code, object code and documentation, if any.
+
+"Program" means the Original Program and Contributions or any part
+thereof
+
+"Recipient" means anyone who receives the Program under this
+Agreement, including all Contributors.
+
+2. GRANT OF RIGHTS
+
+ a. Subject to the terms of this Agreement, each Contributor hereby
+    grants Recipient a non-exclusive, worldwide, royalty-free copyright
+    license to reproduce, prepare derivative works of, publicly display,
+    publicly perform, distribute and sublicense the Contribution of such
+    Contributor, if any, and such derivative works, in source code and
+    object code form.
+    
+ b. Subject to the terms of this Agreement, each Contributor hereby
+    grants Recipient a non-exclusive, worldwide, royalty-free patent
+    license under Licensed Patents to make, use, sell, offer to sell,
+    import and otherwise transfer the Contribution of such Contributor, if
+    any, in source code and object code form. The patent license granted
+    by a Contributor shall also apply to the combination of the
+    Contribution of that Contributor and the Program if, at the time the
+    Contribution is added by the Contributor, such addition of the
+    Contribution causes such combination to be covered by the Licensed
+    Patents. The patent license granted by a Contributor shall not apply
+    to (i) any other combinations which include the Contribution, nor to
+    (ii) Contributions of other Contributors. No hardware per se is
+    licensed hereunder.
+    
+ c. Recipient understands that although each Contributor grants the
+    licenses to its Contributions set forth herein, no assurances are
+    provided by any Contributor that the Program does not infringe the
+    patent or other intellectual property rights of any other entity. Each
+    Contributor disclaims any liability to Recipient for claims brought by
+    any other entity based on infringement of intellectual property rights
+    or otherwise. As a condition to exercising the rights and licenses
+    granted hereunder, each Recipient hereby assumes sole responsibility
+    to secure any other intellectual property rights needed, if any. For
+    example, if a third party patent license is required to allow
+    Recipient to distribute the Program, it is Recipient's responsibility
+    to acquire that license before distributing the Program.
+
+ d. Each Contributor represents that to its knowledge it has sufficient
+    copyright rights in its Contribution, if any, to grant the copyright
+    license set forth in this Agreement.
+
+3. REQUIREMENTS
+
+A. Distributor may choose to distribute the Program in any form under
+this Agreement or under its own license agreement, provided that:
+
+ a. it complies with the terms and conditions of this Agreement;
+
+ b. if the Program is distributed in source code or other tangible
+    form, a copy of this Agreement or Distributor's own license agreement
+    is included with each copy of the Program; and
+
+ c. if distributed under Distributor's own license agreement, such
+    license agreement:
+
+      i. effectively disclaims on behalf of all Contributors all warranties
+         and conditions, express and implied, including warranties or
+         conditions of title and non-infringement, and implied warranties or
+         conditions of merchantability and fitness for a particular purpose;
+     ii. effectively excludes on behalf of all Contributors all liability
+         for damages, including direct, indirect, special, incidental and
+         consequential damages, such as lost profits; and
+    iii. states that any provisions which differ from this Agreement are
+         offered by that Contributor alone and not by any other party.
+
+B. Each Distributor must include the following in a conspicuous
+   location in the Program:
+
+   Copyright (C) 2003, Lucent Technologies Inc. and others. All Rights
+   Reserved.
+
+C. In addition, each Contributor must identify itself as the
+originator of its Contribution in a manner that reasonably allows
+subsequent Recipients to identify the originator of the Contribution.
+Also, each Contributor must agree that the additions and/or changes
+are intended to be a Contribution. Once a Contribution is contributed,
+it may not thereafter be revoked.
+
+4. COMMERCIAL DISTRIBUTION
+
+Commercial distributors of software may accept certain
+responsibilities with respect to end users, business partners and the
+like. While this license is intended to facilitate the commercial use
+of the Program, the Distributor who includes the Program in a
+commercial product offering should do so in a manner which does not
+create potential liability for Contributors. Therefore, if a
+Distributor includes the Program in a commercial product offering,
+such Distributor ("Commercial Distributor") hereby agrees to defend
+and indemnify every Contributor ("Indemnified Contributor") against
+any losses, damages and costs (collectively"Losses") arising from
+claims, lawsuits and other legal actions brought by a third party
+against the Indemnified Contributor to the extent caused by the acts
+or omissions of such Commercial Distributor in connection with its
+distribution of the Program in a commercial product offering. The
+obligations in this section do not apply to any claims or Losses
+relating to any actual or alleged intellectual property infringement.
+In order to qualify, an Indemnified Contributor must: a) promptly
+notify the Commercial Distributor in writing of such claim, and b)
+allow the Commercial Distributor to control, and cooperate with the
+Commercial Distributor in, the defense and any related settlement
+negotiations. The Indemnified Contributor may participate in any such
+claim at its own expense.
+
+For example, a Distributor might include the Program in a commercial
+product offering, Product X. That Distributor is then a Commercial
+Distributor. If that Commercial Distributor then makes performance
+claims, or offers warranties related to Product X, those performance
+claims and warranties are such Commercial Distributor's responsibility
+alone. Under this section, the Commercial Distributor would have to
+defend claims against the Contributors related to those performance
+claims and warranties, and if a court requires any Contributor to pay
+any damages as a result, the Commercial Distributor must pay those
+damages.
+
+5. NO WARRANTY
+
+EXCEPT AS EXPRESSLY SET FORTH IN THIS AGREEMENT, THE PROGRAM IS
+PROVIDED ON AN"AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+KIND, EITHER EXPRESS OR IMPLIED INCLUDING, WITHOUT LIMITATION, ANY
+WARRANTIES OR CONDITIONS OF TITLE, NON-INFRINGEMENT, MERCHANTABILITY
+OR FITNESS FOR A PARTICULAR PURPOSE. Each Recipient is solely
+responsible for determining the appropriateness of using and
+distributing the Program and assumes all risks associated with its
+exercise of rights under this Agreement, including but not limited to
+the risks and costs of program errors, compliance with applicable
+laws, damage to or loss of data, programs or equipment, and
+unavailability or interruption of operations.
+
+6. DISCLAIMER OF LIABILITY
+
+EXCEPT AS EXPRESSLY SET FORTH IN THIS AGREEMENT, NEITHER RECIPIENT NOR
+ANY CONTRIBUTORS SHALL HAVE ANY LIABILITY FOR ANY DIRECT, INDIRECT,
+INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING
+WITHOUT LIMITATION LOST PROFITS), HOWEVER CAUSED AND ON ANY THEORY OF
+LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OR
+DISTRIBUTION OF THE PROGRAM OR THE EXERCISE OF ANY RIGHTS GRANTED
+HEREUNDER, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGES.
+
+7. EXPORT CONTROL
+
+Recipient agrees that Recipient alone is responsible for compliance
+with the United States export administration regulations (and the
+export control laws and regulation of any other countries).
+
+8. GENERAL
+
+If any provision of this Agreement is invalid or unenforceable under
+applicable law, it shall not affect the validity or enforceability of
+the remainder of the terms of this Agreement, and without further
+action by the parties hereto, such provision shall be reformed to the
+minimum extent necessary to make such provision valid and enforceable.
+
+If Recipient institutes patent litigation against a Contributor with
+respect to a patent applicable to software (including a cross-claim or
+counterclaim in a lawsuit), then any patent licenses granted by that
+Contributor to such Recipient under this Agreement shall terminate as
+of the date such litigation is filed. In addition, if Recipient
+institutes patent litigation against any entity (including a
+cross-claim or counterclaim in a lawsuit) alleging that the Program
+itself (excluding combinations of the Program with other software or
+hardware) infringes such Recipient's patent(s), then such Recipient's
+rights granted under Section 2(b) shall terminate as of the date such
+litigation is filed.
+
+All Recipient's rights under this Agreement shall terminate if it
+fails to comply with any of the material terms or conditions of this
+Agreement and does not cure such failure in a reasonable period of
+time after becoming aware of such noncompliance. If all Recipient's
+rights under this Agreement terminate, Recipient agrees to cease use
+and distribution of the Program as soon as reasonably practicable.
+However, Recipient's obligations under this Agreement and any licenses
+granted by Recipient relating to the Program shall continue and
+survive.
+
+LUCENT may publish new versions (including revisions) of this
+Agreement from time to time. Each new version of the Agreement will be
+given a distinguishing version number. The Program (including
+Contributions) may always be distributed subject to the version of the
+Agreement under which it was received. In addition, after a new
+version of the Agreement is published, Contributor may elect to
+distribute the Program (including its Contributions) under the new
+version. No one other than LUCENT has the right to modify this
+Agreement. Except as expressly stated in Sections 2(a) and 2(b) above,
+Recipient receives no rights or licenses to the intellectual property
+of any Contributor under this Agreement, whether expressly, by
+implication, estoppel or otherwise. All rights in the Program not
+expressly granted under this Agreement are reserved.
+
+This Agreement is governed by the laws of the State of New York and
+the intellectual property laws of the United States of America. No
+party to this Agreement will bring a legal action under this Agreement
+more than one year after the cause of action arose. Each party waives
+its rights to a jury trial in any resulting litigation.
+
--- /dev/null
+++ b/os/boot/libflate/NOTICE
@@ -1,0 +1,8 @@
+Copyright © 2002 Lucent Technologies Inc.
+All Rights Reserved
+
+This software was originally developed for Plan 9.
+It is provided under the terms of the Lucent Public License, Version 1.02.
+
+Trivial modifications have been made to make it compile for Inferno.
+	Vita Nuova Holdings Limited.
--- /dev/null
+++ b/os/boot/libflate/adler.c
@@ -1,0 +1,71 @@
+#include "lib9.h"
+#include <flate.h>
+
+enum
+{
+	ADLERITERS	= 5552,	/* max iters before can overflow 32 bits */
+	ADLERBASE	= 65521 /* largest prime smaller than 65536 */
+};
+
+ulong
+adler32(ulong adler, void *vbuf, int n)
+{
+	ulong s1, s2;
+	uchar *buf, *ebuf;
+	int m;
+
+	buf = vbuf;
+	s1 = adler & 0xffff;
+	s2 = (adler >> 16) & 0xffff;
+	for(; n >= 16; n -= m){
+		m = n;
+		if(m > ADLERITERS)
+			m = ADLERITERS;
+		m &= ~15;
+		for(ebuf = buf + m; buf < ebuf; buf += 16){
+			s1 += buf[0];
+			s2 += s1;
+			s1 += buf[1];
+			s2 += s1;
+			s1 += buf[2];
+			s2 += s1;
+			s1 += buf[3];
+			s2 += s1;
+			s1 += buf[4];
+			s2 += s1;
+			s1 += buf[5];
+			s2 += s1;
+			s1 += buf[6];
+			s2 += s1;
+			s1 += buf[7];
+			s2 += s1;
+			s1 += buf[8];
+			s2 += s1;
+			s1 += buf[9];
+			s2 += s1;
+			s1 += buf[10];
+			s2 += s1;
+			s1 += buf[11];
+			s2 += s1;
+			s1 += buf[12];
+			s2 += s1;
+			s1 += buf[13];
+			s2 += s1;
+			s1 += buf[14];
+			s2 += s1;
+			s1 += buf[15];
+			s2 += s1;
+		}
+		s1 %= ADLERBASE;
+		s2 %= ADLERBASE;
+	}
+	if(n){
+		for(ebuf = buf + n; buf < ebuf; buf++){
+			s1 += buf[0];
+			s2 += s1;
+		}
+		s1 %= ADLERBASE;
+		s2 %= ADLERBASE;
+	}
+	return (s2 << 16) + s1;
+}
--- /dev/null
+++ b/os/boot/libflate/crc.c
@@ -1,0 +1,39 @@
+#include "lib9.h"
+#include <flate.h>
+
+ulong*
+mkcrctab(ulong poly)
+{
+	ulong *crctab;
+	ulong crc;
+	int i, j;
+
+	crctab = malloc(256 * sizeof(ulong));
+	if(crctab == nil)
+		return nil;
+
+	for(i = 0; i < 256; i++){
+		crc = i;
+		for(j = 0; j < 8; j++){
+			if(crc & 1)
+				crc = (crc >> 1) ^ poly;
+			else
+				crc >>= 1;
+		}
+		crctab[i] = crc;
+	}
+	return crctab;
+}
+
+ulong
+blockcrc(ulong *crctab, ulong crc, void *vbuf, int n)
+{
+	uchar *buf, *ebuf;
+
+	crc ^= 0xffffffff;
+	buf = vbuf;
+	ebuf = buf + n;
+	while(buf < ebuf)
+		crc = crctab[(crc & 0xff) ^ *buf++] ^ (crc >> 8);
+	return crc ^ 0xffffffff;
+}
--- /dev/null
+++ b/os/boot/libflate/deflate.c
@@ -1,0 +1,1358 @@
+#include "lib9.h"
+#include <flate.h>
+
+typedef struct Chain	Chain;
+typedef struct Chains	Chains;
+typedef struct Dyncode	Dyncode;
+typedef struct Huff	Huff;
+typedef struct LZblock	LZblock;
+typedef struct LZstate	LZstate;
+
+enum
+{
+	/*
+	 * deflate format paramaters
+	 */
+	DeflateUnc	= 0,			/* uncompressed block */
+	DeflateFix	= 1,			/* fixed huffman codes */
+	DeflateDyn	= 2,			/* dynamic huffman codes */
+
+	DeflateEob	= 256,			/* end of block code in lit/len book */
+	DeflateMaxBlock	= 64*1024-1,		/* maximum size of uncompressed block */
+
+	DeflateMaxExp	= 10,			/* maximum expansion for a block */
+
+	LenStart	= 257,			/* start of length codes in litlen */
+	Nlitlen		= 288,			/* number of litlen codes */
+	Noff		= 30,			/* number of offset codes */
+	Nclen		= 19,			/* number of codelen codes */
+
+	MaxOff		= 32*1024,
+	MinMatch	= 3,			/* shortest match possible */
+	MaxMatch	= 258,			/* longest match possible */
+
+	/*
+	 * huffman code paramaters
+	 */
+	MaxLeaf		= Nlitlen,
+	MaxHuffBits	= 16,			/* max bits in a huffman code */
+	ChainMem	= 2 * (MaxHuffBits - 1) * MaxHuffBits,
+
+	/*
+	 * coding of the lz parse
+	 */
+	LenFlag		= 1 << 3,
+	LenShift	= 4,			/* leaves enough space for MinMatchMaxOff */
+	MaxLitRun	= LenFlag - 1,
+
+	/*
+	 * internal lz paramaters
+	 */
+	DeflateOut	= 4096,			/* output buffer size */
+	BlockSize	= 8192,			/* attempted input read quanta */
+	DeflateBlock	= DeflateMaxBlock & ~(BlockSize - 1),
+	MinMatchMaxOff	= 4096,			/* max profitable offset for small match;
+						 * assumes 8 bits for len, 5+10 for offset
+						 * DONT CHANGE WITHOUT CHANGING LZPARSE CONSTANTS
+						 */
+	HistSlop	= 512,			/* must be at lead MaxMatch */
+	HistBlock	= 64*1024,
+	HistSize	= HistBlock + HistSlop,
+
+	HashLog		= 13,
+	HashSize	= 1<<HashLog,
+
+	MaxOffCode	= 256,			/* biggest offset looked up in direct table */
+
+	EstLitBits	= 8,
+	EstLenBits	= 4,
+	EstOffBits	= 5,
+};
+
+/*
+ * knuth vol. 3 multiplicative hashing
+ * each byte x chosen according to rules
+ * 1/4 < x < 3/10, 1/3 x < < 3/7, 4/7 < x < 2/3, 7/10 < x < 3/4
+ * with reasonable spread between the bytes & their complements
+ *
+ * the 3 byte value appears to be as almost good as the 4 byte value,
+ * and might be faster on some machines
+ */
+/*
+#define hashit(c)	(((ulong)(c) * 0x6b43a9) >> (24 - HashLog))
+*/
+#define hashit(c)	((((ulong)(c) & 0xffffff) * 0x6b43a9b5) >> (32 - HashLog))
+
+/*
+ * lempel-ziv style compression state
+ */
+struct LZstate
+{
+	uchar	hist[HistSize];
+	ulong	pos;				/* current location in history buffer */
+	ulong	avail;				/* data available after pos */
+	int	eof;
+	ushort	hash[HashSize];			/* hash chains */
+	ushort	nexts[MaxOff];
+	int	now;				/* pos in hash chains */
+	int	dot;				/* dawn of time in history */
+	int	prevlen;			/* lazy matching state */
+	int	prevoff;
+	int	maxcheck;			/* compressor tuning */
+
+	uchar	obuf[DeflateOut];
+	uchar	*out;				/* current position in the output buffer */
+	uchar	*eout;
+	ulong	bits;				/* bit shift register */
+	int	nbits;
+	int	rbad;				/* got an error reading the buffer */
+	int	wbad;				/* got an error writing the buffer */
+	int	(*w)(void*, void*, int);
+	void	*wr;
+
+	ulong	totr;				/* total input size */
+	ulong	totw;				/* total output size */
+	int	debug;
+};
+
+struct LZblock
+{
+	ushort	parse[DeflateMaxBlock / 2 + 1];
+	int	lastv;				/* value being constucted for parse */
+	ulong	litlencount[Nlitlen];
+	ulong	offcount[Noff];
+	ushort	*eparse;			/* limit for parse table */
+	int	bytes;				/* consumed from the input */
+	int	excost;				/* cost of encoding extra len & off bits */
+};
+
+/*
+ * huffman code table
+ */
+struct Huff
+{
+	short	bits;				/* length of the code */
+	ushort	encode;				/* the code */
+};
+
+/*
+ * encoding of dynamic huffman trees
+ */
+struct Dyncode
+{
+	int	nlit;
+	int	noff;
+	int	nclen;
+	int	ncode;
+	Huff	codetab[Nclen];
+	uchar	codes[Nlitlen+Noff];
+	uchar	codeaux[Nlitlen+Noff];
+};
+
+static	int	deflateb(LZstate *lz, LZblock *lzb, void *rr, int (*r)(void*, void*, int));
+static	int	lzcomp(LZstate*, LZblock*, uchar*, ushort*, int finish);
+static	void	wrblock(LZstate*, int, ushort*, ushort*, Huff*, Huff*);
+static	int	bitcost(Huff*, ulong*, int);
+static	int	huffcodes(Dyncode*, Huff*, Huff*);
+static	void	wrdyncode(LZstate*, Dyncode*);
+static	void	lzput(LZstate*, ulong bits, int nbits);
+static	void	lzflushbits(LZstate*);
+static	void	lzflush(LZstate *lz);
+static	void	lzwrite(LZstate *lz, void *buf, int n);
+
+static	int	hufftabinit(Huff*, int, ulong*, int);
+static	int	mkgzprecode(Huff*, ulong *, int, int);
+
+static	int	mkprecode(Huff*, ulong *, int, int, ulong*);
+static	void	nextchain(Chains*, int);
+static	void	leafsort(ulong*, ushort*, int, int);
+
+/* conversion from len to code word */
+static int lencode[MaxMatch];
+
+/*
+ * conversion from off to code word
+ * off <= MaxOffCode ? offcode[off] : bigoffcode[off >> 7]
+*/
+static int offcode[MaxOffCode];
+static int bigoffcode[256];
+
+/* litlen code words LenStart-285 extra bits */
+static int litlenbase[Nlitlen-LenStart];
+static int litlenextra[Nlitlen-LenStart] =
+{
+/* 257 */	0, 0, 0,
+/* 260 */	0, 0, 0, 0, 0, 1, 1, 1, 1, 2,
+/* 270 */	2, 2, 2, 3, 3, 3, 3, 4, 4, 4,
+/* 280 */	4, 5, 5, 5, 5, 0, 0, 0
+};
+
+/* offset code word extra bits */
+static int offbase[Noff];
+static int offextra[] =
+{
+	0,  0,  0,  0,  1,  1,  2,  2,  3,  3,
+	4,  4,  5,  5,  6,  6,  7,  7,  8,  8,
+	9,  9,  10, 10, 11, 11, 12, 12, 13, 13,
+	0,  0,
+};
+
+/* order code lengths */
+static int clenorder[Nclen] =
+{
+        16, 17, 18, 0, 8, 7, 9, 6, 10, 5, 11, 4, 12, 3, 13, 2, 14, 1, 15
+};
+
+/* static huffman tables */
+static	Huff	litlentab[Nlitlen];
+static	Huff	offtab[Noff];
+static	Huff	hofftab[Noff];
+
+/* bit reversal for brain dead endian swap in huffman codes */
+static	uchar	revtab[256];
+static	ulong	nlits;
+static	ulong	nmatches;
+
+int
+deflateinit(void)
+{
+	ulong bitcount[MaxHuffBits];
+	int i, j, ci, n;
+
+	/* byte reverse table */
+	for(i=0; i<256; i++)
+		for(j=0; j<8; j++)
+			if(i & (1<<j))
+				revtab[i] |= 0x80 >> j;
+
+	/* static Litlen bit lengths */
+	for(i=0; i<144; i++)
+		litlentab[i].bits = 8;
+	for(i=144; i<256; i++)
+		litlentab[i].bits = 9;
+	for(i=256; i<280; i++)
+		litlentab[i].bits = 7;
+	for(i=280; i<Nlitlen; i++)
+		litlentab[i].bits = 8;
+
+	memset(bitcount, 0, sizeof(bitcount));
+	bitcount[8] += 144 - 0;
+	bitcount[9] += 256 - 144;
+	bitcount[7] += 280 - 256;
+	bitcount[8] += Nlitlen - 280;
+
+	if(!hufftabinit(litlentab, Nlitlen, bitcount, 9))
+		return FlateInternal;
+
+	/* static offset bit lengths */
+	for(i = 0; i < Noff; i++)
+		offtab[i].bits = 5;
+
+	memset(bitcount, 0, sizeof(bitcount));
+	bitcount[5] = Noff;
+
+	if(!hufftabinit(offtab, Noff, bitcount, 5))
+		return FlateInternal;
+
+	bitcount[0] = 0;
+	bitcount[1] = 0;
+	if(!mkgzprecode(hofftab, bitcount, 2, MaxHuffBits))
+		return FlateInternal;
+
+	/* conversion tables for lens & offs to codes */
+	ci = 0;
+	for(i = LenStart; i < 286; i++){
+		n = ci + (1 << litlenextra[i - LenStart]);
+		litlenbase[i - LenStart] = ci;
+		for(; ci < n; ci++)
+			lencode[ci] = i;
+	}
+	/* patch up special case for len MaxMatch */
+	lencode[MaxMatch-MinMatch] = 285;
+	litlenbase[285-LenStart] = MaxMatch-MinMatch;
+
+	ci = 0;
+	for(i = 0; i < 16; i++){
+		n = ci + (1 << offextra[i]);
+		offbase[i] = ci;
+		for(; ci < n; ci++)
+			offcode[ci] = i;
+	}
+
+	ci = ci >> 7;
+	for(; i < 30; i++){
+		n = ci + (1 << (offextra[i] - 7));
+		offbase[i] = ci << 7;
+		for(; ci < n; ci++)
+			bigoffcode[ci] = i;
+	}
+	return FlateOk;
+}
+
+static void
+deflatereset(LZstate *lz, int level, int debug)
+{
+	memset(lz->nexts, 0, sizeof lz->nexts);
+	memset(lz->hash, 0, sizeof lz->hash);
+	lz->totr = 0;
+	lz->totw = 0;
+	lz->pos = 0;
+	lz->avail = 0;
+	lz->out = lz->obuf;
+	lz->eout = &lz->obuf[DeflateOut];
+	lz->prevlen = MinMatch - 1;
+	lz->prevoff = 0;
+	lz->now = MaxOff + 1;
+	lz->dot = lz->now;
+	lz->bits = 0;
+	lz->nbits = 0;
+	lz->maxcheck = (1 << level);
+	lz->maxcheck -= lz->maxcheck >> 2;
+	if(lz->maxcheck < 2)
+		lz->maxcheck = 2;
+	else if(lz->maxcheck > 1024)
+		lz->maxcheck = 1024;
+
+	lz->debug = debug;
+}
+
+int
+deflate(void *wr, int (*w)(void*, void*, int), void *rr, int (*r)(void*, void*, int), int level, int debug)
+{
+	LZstate *lz;
+	LZblock *lzb;
+	int ok;
+
+	lz = malloc(sizeof *lz + sizeof *lzb);
+	if(lz == nil)
+		return FlateNoMem;
+	lzb = (LZblock*)&lz[1];
+
+	deflatereset(lz, level, debug);
+	lz->w = w;
+	lz->wr = wr;
+	lz->wbad = 0;
+	lz->rbad = 0;
+	lz->eof = 0;
+	ok = FlateOk;
+	while(!lz->eof || lz->avail){
+		ok = deflateb(lz, lzb, rr, r);
+		if(ok != FlateOk)
+			break;
+	}
+	if(ok == FlateOk && lz->rbad)
+		ok = FlateInputFail;
+	if(ok == FlateOk && lz->wbad)
+		ok = FlateOutputFail;
+	free(lz);
+	return ok;
+}
+
+static int
+deflateb(LZstate *lz, LZblock *lzb, void *rr, int (*r)(void*, void*, int))
+{
+	Dyncode dyncode, hdyncode;
+	Huff dlitlentab[Nlitlen], dofftab[Noff], hlitlentab[Nlitlen];
+	ulong litcount[Nlitlen];
+	long nunc, ndyn, nfix, nhuff;
+	uchar *slop, *hslop;
+	ulong ep;
+	int i, n, m, mm, nslop;
+
+	memset(lzb->litlencount, 0, sizeof lzb->litlencount);
+	memset(lzb->offcount, 0, sizeof lzb->offcount);
+	lzb->litlencount[DeflateEob]++;
+
+	lzb->bytes = 0;
+	lzb->eparse = lzb->parse;
+	lzb->lastv = 0;
+	lzb->excost = 0;
+
+	slop = &lz->hist[lz->pos];
+	n = lz->avail;
+	while(n < DeflateBlock && (!lz->eof || lz->avail)){
+		/*
+		 * fill the buffer as much as possible,
+		 * while leaving room for MaxOff history behind lz->pos,
+		 * and not reading more than we can handle.
+		 *
+		 * make sure we read at least HistSlop bytes.
+		 */
+		if(!lz->eof){
+			ep = lz->pos + lz->avail;
+			if(ep >= HistBlock)
+				ep -= HistBlock;
+			m = HistBlock - MaxOff - lz->avail;
+			if(m > HistBlock - n)
+				m = HistBlock - n;
+			if(m > (HistBlock + HistSlop) - ep)
+				m = (HistBlock + HistSlop) - ep;
+			if(m & ~(BlockSize - 1))
+				m &= ~(BlockSize - 1);
+
+			/*
+			 * be nice to the caller: stop reads that are too small.
+			 * can only get here when we've already filled the buffer some
+			 */
+			if(m < HistSlop){
+				if(!m || !lzb->bytes)
+					return FlateInternal;
+				break;
+			}
+
+			mm = (*r)(rr, &lz->hist[ep], m);
+			if(mm > 0){
+				/*
+				 * wrap data to end if we're read it from the beginning
+				 * this way, we don't have to wrap searches.
+				 *
+				 * wrap reads past the end to the beginning.
+				 * this way, we can guarantee minimum size reads.
+				 */
+				if(ep < HistSlop)
+					memmove(&lz->hist[ep + HistBlock], &lz->hist[ep], HistSlop - ep);
+				else if(ep + mm > HistBlock)
+					memmove(&lz->hist[0], &lz->hist[HistBlock], ep + mm - HistBlock);
+
+				lz->totr += mm;
+				n += mm;
+				lz->avail += mm;
+			}else{
+				if(mm < 0)
+					lz->rbad = 1;
+				lz->eof = 1;
+			}
+		}
+		ep = lz->pos + lz->avail;
+		if(ep > HistSize)
+			ep = HistSize;
+		if(lzb->bytes + ep - lz->pos > DeflateMaxBlock)
+			ep = lz->pos + DeflateMaxBlock - lzb->bytes;
+		m = lzcomp(lz, lzb, &lz->hist[ep], lzb->eparse, lz->eof);
+		lzb->bytes += m;
+		lz->pos = (lz->pos + m) & (HistBlock - 1);
+		lz->avail -= m;
+	}
+	if(lzb->lastv)
+		*lzb->eparse++ = lzb->lastv;
+	if(lzb->eparse > lzb->parse + nelem(lzb->parse))
+		return FlateInternal;
+	nunc = lzb->bytes;
+
+	if(!mkgzprecode(dlitlentab, lzb->litlencount, Nlitlen, MaxHuffBits)
+	|| !mkgzprecode(dofftab, lzb->offcount, Noff, MaxHuffBits))
+		return FlateInternal;
+
+	ndyn = huffcodes(&dyncode, dlitlentab, dofftab);
+	if(ndyn < 0)
+		return FlateInternal;
+	ndyn += bitcost(dlitlentab, lzb->litlencount, Nlitlen)
+		+ bitcost(dofftab, lzb->offcount, Noff)
+		+ lzb->excost;
+
+	memset(litcount, 0, sizeof litcount);
+
+	nslop = nunc;
+	if(nslop > &lz->hist[HistSize] - slop)
+		nslop = &lz->hist[HistSize] - slop;
+
+	for(i = 0; i < nslop; i++)
+		litcount[slop[i]]++;
+	hslop = &lz->hist[HistSlop - nslop];
+	for(; i < nunc; i++)
+		litcount[hslop[i]]++;
+	litcount[DeflateEob]++;
+
+	if(!mkgzprecode(hlitlentab, litcount, Nlitlen, MaxHuffBits))
+		return FlateInternal;
+	nhuff = huffcodes(&hdyncode, hlitlentab, hofftab);
+	if(nhuff < 0)
+		return FlateInternal;
+	nhuff += bitcost(hlitlentab, litcount, Nlitlen);
+
+	nfix = bitcost(litlentab, lzb->litlencount, Nlitlen)
+		+ bitcost(offtab, lzb->offcount, Noff)
+		+ lzb->excost;
+
+	lzput(lz, lz->eof && !lz->avail, 1);
+
+	if(lz->debug){
+		fprint(2, "block: bytes=%lud entries=%ld extra bits=%d\n\tuncompressed=%lud fixed=%lud dynamic=%lud huffman=%lud\n",
+			nunc, lzb->eparse - lzb->parse, lzb->excost, (nunc + 4) * 8, nfix, ndyn, nhuff);
+		fprint(2, "\tnlit=%lud matches=%lud eof=%d\n", nlits, nmatches, lz->eof && !lz->avail);
+	}
+
+	if((nunc + 4) * 8 < ndyn && (nunc + 4) * 8 < nfix && (nunc + 4) * 8 < nhuff){
+		lzput(lz, DeflateUnc, 2);
+		lzflushbits(lz);
+
+		lzput(lz, nunc & 0xff, 8);
+		lzput(lz, (nunc >> 8) & 0xff, 8);
+		lzput(lz, ~nunc & 0xff, 8);
+		lzput(lz, (~nunc >> 8) & 0xff, 8);
+		lzflush(lz);
+
+		lzwrite(lz, slop, nslop);
+		lzwrite(lz, &lz->hist[HistSlop], nunc - nslop);
+	}else if(ndyn < nfix && ndyn < nhuff){
+		lzput(lz, DeflateDyn, 2);
+
+		wrdyncode(lz, &dyncode);
+		wrblock(lz, slop - lz->hist, lzb->parse, lzb->eparse, dlitlentab, dofftab);
+		lzput(lz, dlitlentab[DeflateEob].encode, dlitlentab[DeflateEob].bits);
+	}else if(nhuff < nfix){
+		lzput(lz, DeflateDyn, 2);
+
+		wrdyncode(lz, &hdyncode);
+
+		m = 0;
+		for(i = nunc; i > MaxLitRun; i -= MaxLitRun)
+			lzb->parse[m++] = MaxLitRun;
+		lzb->parse[m++] = i;
+
+		wrblock(lz, slop - lz->hist, lzb->parse, lzb->parse + m, hlitlentab, hofftab);
+		lzput(lz, hlitlentab[DeflateEob].encode, hlitlentab[DeflateEob].bits);
+	}else{
+		lzput(lz, DeflateFix, 2);
+
+		wrblock(lz, slop - lz->hist, lzb->parse, lzb->eparse, litlentab, offtab);
+		lzput(lz, litlentab[DeflateEob].encode, litlentab[DeflateEob].bits);
+	}
+
+	if(lz->eof && !lz->avail){
+		lzflushbits(lz);
+		lzflush(lz);
+	}
+	return FlateOk;
+}
+
+static void
+lzwrite(LZstate *lz, void *buf, int n)
+{
+	int nw;
+
+	if(n && lz->w){
+		nw = (*lz->w)(lz->wr, buf, n);
+		if(nw != n){
+			lz->w = nil;
+			lz->wbad = 1;
+		}else
+			lz->totw += n;
+	}
+}
+
+static void
+lzflush(LZstate *lz)
+{
+	lzwrite(lz, lz->obuf, lz->out - lz->obuf);
+	lz->out = lz->obuf;
+}
+
+static void
+lzput(LZstate *lz, ulong bits, int nbits)
+{
+	bits = (bits << lz->nbits) | lz->bits;
+	for(nbits += lz->nbits; nbits >= 8; nbits -= 8){
+		*lz->out++ = bits;
+		if(lz->out == lz->eout)
+			lzflush(lz);
+		bits >>= 8;
+	}
+	lz->bits = bits;
+	lz->nbits = nbits;
+}
+
+static void
+lzflushbits(LZstate *lz)
+{
+	if(lz->nbits)
+		lzput(lz, 0, 8 - (lz->nbits & 7));
+}
+
+/*
+ * write out a block of n samples,
+ * given lz encoding and counts for huffman tables
+ */
+static void
+wrblock(LZstate *out, int litoff, ushort *soff, ushort *eoff, Huff *litlentab, Huff *offtab)
+{
+	ushort *off;
+	int i, run, offset, lit, len, c;
+
+	if(out->debug > 2){
+		for(off = soff; off < eoff; ){
+			offset = *off++;
+			run = offset & MaxLitRun;
+			if(run){
+				for(i = 0; i < run; i++){
+					lit = out->hist[litoff & (HistBlock - 1)];
+					litoff++;
+					fprint(2, "\tlit %.2ux %c\n", lit, lit);
+				}
+				if(!(offset & LenFlag))
+					continue;
+				len = offset >> LenShift;
+				offset = *off++;
+			}else if(offset & LenFlag){
+				len = offset >> LenShift;
+				offset = *off++;
+			}else{
+				len = 0;
+				offset >>= LenShift;
+			}
+			litoff += len + MinMatch;
+			fprint(2, "\t<%d, %d>\n", offset + 1, len + MinMatch);
+		}
+	}
+
+	for(off = soff; off < eoff; ){
+		offset = *off++;
+		run = offset & MaxLitRun;
+		if(run){
+			for(i = 0; i < run; i++){
+				lit = out->hist[litoff & (HistBlock - 1)];
+				litoff++;
+				lzput(out, litlentab[lit].encode, litlentab[lit].bits);
+			}
+			if(!(offset & LenFlag))
+				continue;
+			len = offset >> LenShift;
+			offset = *off++;
+		}else if(offset & LenFlag){
+			len = offset >> LenShift;
+			offset = *off++;
+		}else{
+			len = 0;
+			offset >>= LenShift;
+		}
+		litoff += len + MinMatch;
+		c = lencode[len];
+		lzput(out, litlentab[c].encode, litlentab[c].bits);
+		c -= LenStart;
+		if(litlenextra[c])
+			lzput(out, len - litlenbase[c], litlenextra[c]);
+
+		if(offset < MaxOffCode)
+			c = offcode[offset];
+		else
+			c = bigoffcode[offset >> 7];
+		lzput(out, offtab[c].encode, offtab[c].bits);
+		if(offextra[c])
+			lzput(out, offset - offbase[c], offextra[c]);
+	}
+}
+
+/*
+ * look for the longest, closest string which matches
+ * the next prefix.  the clever part here is looking for
+ * a string 1 longer than the previous best match.
+ *
+ * follows the recommendation of limiting number of chains
+ * which are checked.  this appears to be the best heuristic.
+ */
+static int
+lzmatch(int now, int then, uchar *p, uchar *es, ushort *nexts, uchar *hist, int runlen, int check, int *m)
+{
+	uchar *s, *t;
+	int ml, off, last;
+
+	ml = check;
+	if(runlen >= 8)
+		check >>= 2;
+	*m = 0;
+	if(p + runlen >= es)
+		return runlen;
+	last = 0;
+	for(; check-- > 0; then = nexts[then & (MaxOff-1)]){
+		off = (ushort)(now - then);
+		if(off <= last || off > MaxOff)
+			break;
+		s = p + runlen;
+		t = hist + (((p - hist) - off) & (HistBlock-1));
+		t += runlen;
+		for(; s >= p; s--){
+			if(*s != *t)
+				goto matchloop;
+			t--;
+		}
+
+		/*
+		 * we have a new best match.
+		 * extend it to it's maximum length
+		 */
+		t += runlen + 2;
+		s += runlen + 2;
+		for(; s < es; s++){
+			if(*s != *t)
+				break;
+			t++;
+		}
+		runlen = s - p;
+		*m = off - 1;
+		if(s == es || runlen > ml)
+			break;
+matchloop:;
+		last = off;
+	}
+	return runlen;
+}
+
+static int
+lzcomp(LZstate *lz, LZblock *lzb, uchar *ep, ushort *parse, int finish)
+{
+	ulong cont, excost, *litlencount, *offcount;
+	uchar *p, *q, *s, *es;
+	ushort *nexts, *hash;
+	int v, i, h, runlen, n, now, then, m, prevlen, prevoff, maxdefer;
+
+	litlencount = lzb->litlencount;
+	offcount = lzb->offcount;
+	nexts = lz->nexts;
+	hash = lz->hash;
+	now = lz->now;
+
+	p = &lz->hist[lz->pos];
+	if(lz->prevlen != MinMatch - 1)
+		p++;
+
+	/*
+	 * hash in the links for any hanging link positions,
+	 * and calculate the hash for the current position.
+	 */
+	n = MinMatch;
+	if(n > ep - p)
+		n = ep - p;
+	cont = 0;
+	for(i = 0; i < n - 1; i++){
+		m = now - ((MinMatch-1) - i);
+		if(m < lz->dot)
+			continue;
+		s = lz->hist + (((p - lz->hist) - (now - m)) & (HistBlock-1));
+
+		cont = (s[0] << 16) | (s[1] << 8) | s[2];
+		h = hashit(cont);
+		prevoff = 0;
+		for(then = hash[h]; ; then = nexts[then & (MaxOff-1)]){
+			v = (ushort)(now - then);
+			if(v <= prevoff || v >= (MinMatch-1) - i)
+				break;
+			prevoff = v;
+		}
+		if(then == (ushort)m)
+			continue;
+		nexts[m & (MaxOff-1)] = hash[h];
+		hash[h] = m;
+	}
+	for(i = 0; i < n; i++)
+		cont = (cont << 8) | p[i];
+
+	/*
+	 * now must point to the index in the nexts array
+	 * corresponding to p's position in the history
+	 */
+	prevlen = lz->prevlen;
+	prevoff = lz->prevoff;
+	maxdefer = lz->maxcheck >> 2;
+	excost = 0;
+	v = lzb->lastv;
+	for(;;){
+		es = p + MaxMatch;
+		if(es > ep){
+			if(!finish || p >= ep)
+				break;
+			es = ep;
+		}
+
+		h = hashit(cont);
+		runlen = lzmatch(now, hash[h], p, es, nexts, lz->hist, prevlen, lz->maxcheck, &m);
+
+		/*
+		 * back out of small matches too far in the past
+		 */
+		if(runlen == MinMatch && m >= MinMatchMaxOff){
+			runlen = MinMatch - 1;
+			m = 0;
+		}
+
+		/*
+		 * record the encoding and increment counts for huffman trees
+		 * if we get a match, defer selecting it until we check for
+		 * a longer match at the next position.
+		 */
+		if(prevlen >= runlen && prevlen != MinMatch - 1){
+			/*
+			 * old match at least as good; use that one
+			 */
+			n = prevlen - MinMatch;
+			if(v || n){
+				*parse++ = v | LenFlag | (n << LenShift);
+				*parse++ = prevoff;
+			}else
+				*parse++ = prevoff << LenShift;
+			v = 0;
+
+			n = lencode[n];
+			litlencount[n]++;
+			excost += litlenextra[n - LenStart];
+
+			if(prevoff < MaxOffCode)
+				n = offcode[prevoff];
+			else
+				n = bigoffcode[prevoff >> 7];
+			offcount[n]++;
+			excost += offextra[n];
+
+			runlen = prevlen - 1;
+			prevlen = MinMatch - 1;
+			nmatches++;
+		}else if(runlen == MinMatch - 1){
+			/*
+			 * no match; just put out the literal
+			 */
+			if(++v == MaxLitRun){
+				*parse++ = v;
+				v = 0;
+			}
+			litlencount[*p]++;
+			nlits++;
+			runlen = 1;
+		}else{
+			if(prevlen != MinMatch - 1){
+				/*
+				 * longer match now. output previous literal,
+				 * update current match, and try again
+				 */
+				if(++v == MaxLitRun){
+					*parse++ = v;
+					v = 0;
+				}
+				litlencount[p[-1]]++;
+				nlits++;
+			}
+
+			prevoff = m;
+
+			if(runlen < maxdefer){
+				prevlen = runlen;
+				runlen = 1;
+			}else{
+				n = runlen - MinMatch;
+				if(v || n){
+					*parse++ = v | LenFlag | (n << LenShift);
+					*parse++ = prevoff;
+				}else
+					*parse++ = prevoff << LenShift;
+				v = 0;
+
+				n = lencode[n];
+				litlencount[n]++;
+				excost += litlenextra[n - LenStart];
+
+				if(prevoff < MaxOffCode)
+					n = offcode[prevoff];
+				else
+					n = bigoffcode[prevoff >> 7];
+				offcount[n]++;
+				excost += offextra[n];
+
+				prevlen = MinMatch - 1;
+				nmatches++;
+			}
+		}
+
+		/*
+		 * update the hash for the newly matched data
+		 * this is constructed so the link for the old
+		 * match in this position must be at the end of a chain,
+		 * and will expire when this match is added, ie it will
+		 * never be examined by the match loop.
+		 * add to the hash chain only if we have the real hash data.
+		 */
+		for(q = p + runlen; p != q; p++){
+			if(p + MinMatch <= ep){
+				h = hashit(cont);
+				nexts[now & (MaxOff-1)] = hash[h];
+				hash[h] = now;
+				if(p + MinMatch < ep)
+					cont = (cont << 8) | p[MinMatch];
+			}
+			now++;
+		}
+	}
+
+	/*
+	 * we can just store away the lazy state and
+	 * pick it up next time.  the last block will have finish set
+	 * so we won't have any pending matches
+	 * however, we need to correct for how much we've encoded
+	 */
+	if(prevlen != MinMatch - 1)
+		p--;
+
+	lzb->excost += excost;
+	lzb->eparse = parse;
+	lzb->lastv = v;
+
+	lz->now = now;
+	lz->prevlen = prevlen;
+	lz->prevoff = prevoff;
+
+	return p - &lz->hist[lz->pos];
+}
+
+/*
+ * make up the dynamic code tables, and return the number of bits
+ * needed to transmit them.
+ */
+static int
+huffcodes(Dyncode *dc, Huff *littab, Huff *offtab)
+{
+	Huff *codetab;
+	uchar *codes, *codeaux;
+	ulong codecount[Nclen], excost;
+	int i, n, m, v, c, nlit, noff, ncode, nclen;
+
+	codetab = dc->codetab;
+	codes = dc->codes;
+	codeaux = dc->codeaux;
+
+	/*
+	 * trim the sizes of the tables
+	 */
+	for(nlit = Nlitlen; nlit > 257 && littab[nlit-1].bits == 0; nlit--)
+		;
+	for(noff = Noff; noff > 1 && offtab[noff-1].bits == 0; noff--)
+		;
+
+	/*
+	 * make the code-length code
+	 */
+	for(i = 0; i < nlit; i++)
+		codes[i] = littab[i].bits;
+	for(i = 0; i < noff; i++)
+		codes[i + nlit] = offtab[i].bits;
+
+	/*
+	 * run-length compress the code-length code
+	 */
+	excost = 0;
+	c = 0;
+	ncode = nlit+noff;
+	for(i = 0; i < ncode; ){
+		n = i + 1;
+		v = codes[i];
+		while(n < ncode && v == codes[n])
+			n++;
+		n -= i;
+		i += n;
+		if(v == 0){
+			while(n >= 11){
+				m = n;
+				if(m > 138)
+					m = 138;
+				codes[c] = 18;
+				codeaux[c++] = m - 11;
+				n -= m;
+				excost += 7;
+			}
+			if(n >= 3){
+				codes[c] = 17;
+				codeaux[c++] = n - 3;
+				n = 0;
+				excost += 3;
+			}
+		}
+		while(n--){
+			codes[c++] = v;
+			while(n >= 3){
+				m = n;
+				if(m > 6)
+					m = 6;
+				codes[c] = 16;
+				codeaux[c++] = m - 3;
+				n -= m;
+				excost += 3;
+			}
+		}
+	}
+
+	memset(codecount, 0, sizeof codecount);
+	for(i = 0; i < c; i++)
+		codecount[codes[i]]++;
+	if(!mkgzprecode(codetab, codecount, Nclen, 8))
+		return -1;
+
+	for(nclen = Nclen; nclen > 4 && codetab[clenorder[nclen-1]].bits == 0; nclen--)
+		;
+
+	dc->nlit = nlit;
+	dc->noff = noff;
+	dc->nclen = nclen;
+	dc->ncode = c;
+
+	return 5 + 5 + 4 + nclen * 3 + bitcost(codetab, codecount, Nclen) + excost;
+}
+
+static void
+wrdyncode(LZstate *out, Dyncode *dc)
+{
+	Huff *codetab;
+	uchar *codes, *codeaux;
+	int i, v, c;
+
+	/*
+	 * write out header, then code length code lengths,
+	 * and code lengths
+	 */
+	lzput(out, dc->nlit-257, 5);
+	lzput(out, dc->noff-1, 5);
+	lzput(out, dc->nclen-4, 4);
+
+	codetab = dc->codetab;
+	for(i = 0; i < dc->nclen; i++)
+		lzput(out, codetab[clenorder[i]].bits, 3);
+
+	codes = dc->codes;
+	codeaux = dc->codeaux;
+	c = dc->ncode;
+	for(i = 0; i < c; i++){
+		v = codes[i];
+		lzput(out, codetab[v].encode, codetab[v].bits);
+		if(v >= 16){
+			if(v == 16)
+				lzput(out, codeaux[i], 2);
+			else if(v == 17)
+				lzput(out, codeaux[i], 3);
+			else /* v == 18 */
+				lzput(out, codeaux[i], 7);
+		}
+	}
+}
+
+static int
+bitcost(Huff *tab, ulong *count, int n)
+{
+	ulong tot;
+	int i;
+
+	tot = 0;
+	for(i = 0; i < n; i++)
+		tot += count[i] * tab[i].bits;
+	return tot;
+}
+
+static int
+mkgzprecode(Huff *tab, ulong *count, int n, int maxbits)
+{
+	ulong bitcount[MaxHuffBits];
+	int i, nbits;
+
+	nbits = mkprecode(tab, count, n, maxbits, bitcount);
+	for(i = 0; i < n; i++){
+		if(tab[i].bits == -1)
+			tab[i].bits = 0;
+		else if(tab[i].bits == 0){
+			if(nbits != 0 || bitcount[0] != 1)
+				return 0;
+			bitcount[1] = 1;
+			bitcount[0] = 0;
+			nbits = 1;
+			tab[i].bits = 1;
+		}
+	}
+	if(bitcount[0] != 0)
+		return 0;
+	return hufftabinit(tab, n, bitcount, nbits);
+}
+
+static int
+hufftabinit(Huff *tab, int n, ulong *bitcount, int nbits)
+{
+	ulong code, nc[MaxHuffBits];
+	int i, bits;
+
+	code = 0;
+	for(bits = 1; bits <= nbits; bits++){
+		code = (code + bitcount[bits-1]) << 1;
+		nc[bits] = code;
+	}
+
+	for(i = 0; i < n; i++){
+		bits = tab[i].bits;
+		if(bits){
+			code = nc[bits]++ << (16 - bits);
+			if(code & ~0xffff)
+				return 0;
+			tab[i].encode = revtab[code >> 8] | (revtab[code & 0xff] << 8);
+		}
+	}
+	return 1;
+}
+
+
+/*
+ * this should be in a library
+ */
+struct Chain
+{
+	ulong	count;				/* occurances of everything in the chain */
+	ushort	leaf;				/* leaves to the left of chain, or leaf value */
+	char	col;				/* ref count for collecting unused chains */
+	char	gen;				/* need to generate chains for next lower level */
+	Chain	*up;				/* Chain up in the lists */
+};
+
+struct Chains
+{
+	Chain	*lists[(MaxHuffBits - 1) * 2];
+	ulong	leafcount[MaxLeaf];		/* sorted list of leaf counts */
+	ushort	leafmap[MaxLeaf];		/* map to actual leaf number */
+	int	nleaf;				/* number of leaves */
+	Chain	chains[ChainMem];
+	Chain	*echains;
+	Chain	*free;
+	char	col;
+	int	nlists;
+};
+
+/*
+ * fast, low space overhead algorithm for max depth huffman type codes
+ *
+ * J. Katajainen, A. Moffat and A. Turpin, "A fast and space-economical
+ * algorithm for length-limited coding," Proc. Intl. Symp. on Algorithms
+ * and Computation, Cairns, Australia, Dec. 1995, Lecture Notes in Computer
+ * Science, Vol 1004, J. Staples, P. Eades, N. Katoh, and A. Moffat, eds.,
+ * pp 12-21, Springer Verlag, New York, 1995.
+ */
+static int
+mkprecode(Huff *tab, ulong *count, int n, int maxbits, ulong *bitcount)
+{
+	Chains cs;
+	Chain *c;
+	int i, m, em, bits;
+
+	/*
+	 * set up the sorted list of leaves
+	 */
+	m = 0;
+	for(i = 0; i < n; i++){
+		tab[i].bits = -1;
+		tab[i].encode = 0;
+		if(count[i] != 0){
+			cs.leafcount[m] = count[i];
+			cs.leafmap[m] = i;
+			m++;
+		}
+	}
+	if(m < 2){
+		if(m != 0){
+			tab[cs.leafmap[0]].bits = 0;
+			bitcount[0] = 1;
+		}else
+			bitcount[0] = 0;
+		return 0;
+	}
+	cs.nleaf = m;
+	leafsort(cs.leafcount, cs.leafmap, 0, m);
+
+	for(i = 0; i < m; i++)
+		cs.leafcount[i] = count[cs.leafmap[i]];
+
+	/*
+	 * set up free list
+	 */
+	cs.free = &cs.chains[2];
+	cs.echains = &cs.chains[ChainMem];
+	cs.col = 1;
+
+	/*
+	 * initialize chains for each list
+	 */
+	c = &cs.chains[0];
+	c->count = cs.leafcount[0];
+	c->leaf = 1;
+	c->col = cs.col;
+	c->up = nil;
+	c->gen = 0;
+	cs.chains[1] = cs.chains[0];
+	cs.chains[1].leaf = 2;
+	cs.chains[1].count = cs.leafcount[1];
+	for(i = 0; i < maxbits-1; i++){
+		cs.lists[i * 2] = &cs.chains[0];
+		cs.lists[i * 2 + 1] = &cs.chains[1];
+	}
+
+	cs.nlists = 2 * (maxbits - 1);
+	m = 2 * m - 2;
+	for(i = 2; i < m; i++)
+		nextchain(&cs, cs.nlists - 2);
+
+	bits = 0;
+	bitcount[0] = cs.nleaf;
+	for(c = cs.lists[cs.nlists - 1]; c != nil; c = c->up){
+		m = c->leaf;
+		bitcount[bits++] -= m;
+		bitcount[bits] = m;
+	}
+	m = 0;
+	for(i = bits; i >= 0; i--)
+		for(em = m + bitcount[i]; m < em; m++)
+			tab[cs.leafmap[m]].bits = i;
+
+	return bits;
+}
+
+/*
+ * calculate the next chain on the list
+ * we can always toss out the old chain
+ */
+static void
+nextchain(Chains *cs, int list)
+{
+	Chain *c, *oc;
+	int i, nleaf, sumc;
+
+	oc = cs->lists[list + 1];
+	cs->lists[list] = oc;
+	if(oc == nil)
+		return;
+
+	/*
+	 * make sure we have all chains needed to make sumc
+	 * note it is possible to generate only one of these,
+	 * use twice that value for sumc, and then generate
+	 * the second if that preliminary sumc would be chosen.
+	 * however, this appears to be slower on current tests
+	 */
+	if(oc->gen){
+		nextchain(cs, list - 2);
+		nextchain(cs, list - 2);
+		oc->gen = 0;
+	}
+
+	/*
+	 * pick up the chain we're going to add;
+	 * collect unused chains no free ones are left
+	 */
+	for(c = cs->free; ; c++){
+		if(c >= cs->echains){
+			cs->col++;
+			for(i = 0; i < cs->nlists; i++)
+				for(c = cs->lists[i]; c != nil; c = c->up)
+					c->col = cs->col;
+			c = cs->chains;
+		}
+		if(c->col != cs->col)
+			break;
+	}
+
+	/*
+	 * pick the cheapest of
+	 * 1) the next package from the previous list
+	 * 2) the next leaf
+	 */
+	nleaf = oc->leaf;
+	sumc = 0;
+	if(list > 0 && cs->lists[list-1] != nil)
+		sumc = cs->lists[list-2]->count + cs->lists[list-1]->count;
+	if(sumc != 0 && (nleaf >= cs->nleaf || cs->leafcount[nleaf] > sumc)){
+		c->count = sumc;
+		c->leaf = oc->leaf;
+		c->up = cs->lists[list-1];
+		c->gen = 1;
+	}else if(nleaf >= cs->nleaf){
+		cs->lists[list + 1] = nil;
+		return;
+	}else{
+		c->leaf = nleaf + 1;
+		c->count = cs->leafcount[nleaf];
+		c->up = oc->up;
+		c->gen = 0;
+	}
+	cs->free = c + 1;
+
+	cs->lists[list + 1] = c;
+	c->col = cs->col;
+}
+
+static int
+pivot(ulong *c, int a, int n)
+{
+	int j, pi, pj, pk;
+
+	j = n/6;
+	pi = a + j;	/* 1/6 */
+	j += j;
+	pj = pi + j;	/* 1/2 */
+	pk = pj + j;	/* 5/6 */
+	if(c[pi] < c[pj]){
+		if(c[pi] < c[pk]){
+			if(c[pj] < c[pk])
+				return pj;
+			return pk;
+		}
+		return pi;
+	}
+	if(c[pj] < c[pk]){
+		if(c[pi] < c[pk])
+			return pi;
+		return pk;
+	}
+	return pj;
+}
+
+static	void
+leafsort(ulong *leafcount, ushort *leafmap, int a, int n)
+{
+	ulong t;
+	int j, pi, pj, pn;
+
+	while(n > 1){
+		if(n > 10){
+			pi = pivot(leafcount, a, n);
+		}else
+			pi = a + (n>>1);
+
+		t = leafcount[pi];
+		leafcount[pi] = leafcount[a];
+		leafcount[a] = t;
+		t = leafmap[pi];
+		leafmap[pi] = leafmap[a];
+		leafmap[a] = t;
+		pi = a;
+		pn = a + n;
+		pj = pn;
+		for(;;){
+			do
+				pi++;
+			while(pi < pn && (leafcount[pi] < leafcount[a] || leafcount[pi] == leafcount[a] && leafmap[pi] > leafmap[a]));
+			do
+				pj--;
+			while(pj > a && (leafcount[pj] > leafcount[a] || leafcount[pj] == leafcount[a] && leafmap[pj] < leafmap[a]));
+			if(pj < pi)
+				break;
+			t = leafcount[pi];
+			leafcount[pi] = leafcount[pj];
+			leafcount[pj] = t;
+			t = leafmap[pi];
+			leafmap[pi] = leafmap[pj];
+			leafmap[pj] = t;
+		}
+		t = leafcount[a];
+		leafcount[a] = leafcount[pj];
+		leafcount[pj] = t;
+		t = leafmap[a];
+		leafmap[a] = leafmap[pj];
+		leafmap[pj] = t;
+		j = pj - a;
+
+		n = n-j-1;
+		if(j >= n){
+			leafsort(leafcount, leafmap, a, j);
+			a += j+1;
+		}else{
+			leafsort(leafcount, leafmap, a + (j+1), n);
+			n = j;
+		}
+	}
+}
--- /dev/null
+++ b/os/boot/libflate/deflateblock.c
@@ -1,0 +1,55 @@
+#include "lib9.h"
+#include <flate.h>
+
+typedef struct Block	Block;
+
+struct Block
+{
+	uchar	*pos;
+	uchar	*limit;
+};
+
+static int
+blread(void *vb, void *buf, int n)
+{
+	Block *b;
+
+	b = vb;
+	if(n > b->limit - b->pos)
+		n = b->limit - b->pos;
+	memmove(buf, b->pos, n);
+	b->pos += n;
+	return n;
+}
+
+static int
+blwrite(void *vb, void *buf, int n)
+{
+	Block *b;
+
+	b = vb;
+
+	if(n > b->limit - b->pos)
+		n = b->limit - b->pos;
+	memmove(b->pos, buf, n);
+	b->pos += n;
+	return n;
+}
+
+int
+deflateblock(uchar *dst, int dsize, uchar *src, int ssize, int level, int debug)
+{
+	Block bd, bs;
+	int ok;
+
+	bs.pos = src;
+	bs.limit = src + ssize;
+
+	bd.pos = dst;
+	bd.limit = dst + dsize;
+
+	ok = deflate(&bd, blwrite, &bs, blread, level, debug);
+	if(ok != FlateOk)
+		return ok;
+	return bd.pos - dst;
+}
--- /dev/null
+++ b/os/boot/libflate/deflatezlib.c
@@ -1,0 +1,59 @@
+#include "lib9.h"
+#include <flate.h>
+#include "zlib.h"
+
+typedef struct ZRead	ZRead;
+
+struct ZRead
+{
+	ulong	adler;
+	void	*rr;
+	int	(*r)(void*, void*, int);
+};
+
+static int
+zlread(void *vzr, void *buf, int n)
+{
+	ZRead *zr;
+
+	zr = vzr;
+	n = (*zr->r)(zr->rr, buf, n);
+	if(n <= 0)
+		return n;
+	zr->adler = adler32(zr->adler, buf, n);
+	return n;
+}
+
+int
+deflatezlib(void *wr, int (*w)(void*, void*, int), void *rr, int (*r)(void*, void*, int), int level, int debug)
+{
+	ZRead zr;
+	uchar buf[4];
+	int ok;
+
+	buf[0] = ZlibDeflate | ZlibWin32k;
+
+	/* bogus zlib encoding of compression level */
+	buf[1] = ((level > 2) + (level > 5) + (level > 8)) << 6;
+
+	/* header check field */
+	buf[1] |= 31 - ((buf[0] << 8) | buf[1]) % 31;
+	if((*w)(wr, buf, 2) != 2)
+		return FlateOutputFail;
+
+	zr.rr = rr;
+	zr.r = r;
+	zr.adler = 1;
+	ok = deflate(wr, w, &zr, zlread, level, debug);
+	if(ok != FlateOk)
+		return ok;
+
+	buf[0] = zr.adler >> 24;
+	buf[1] = zr.adler >> 16;
+	buf[2] = zr.adler >> 8;
+	buf[3] = zr.adler;
+	if((*w)(wr, buf, 4) != 4)
+		return FlateOutputFail;
+
+	return FlateOk;
+}
--- /dev/null
+++ b/os/boot/libflate/deflatezlibblock.c
@@ -1,0 +1,33 @@
+#include "lib9.h"
+#include <flate.h>
+#include "zlib.h"
+
+int
+deflatezlibblock(uchar *dst, int dsize, uchar *src, int ssize, int level, int debug)
+{
+	ulong adler;
+	int n;
+
+	if(dsize < 6)
+		return FlateOutputFail;
+
+	n = deflateblock(dst + 2, dsize - 6, src, ssize, level, debug);
+	if(n < 0)
+		return n;
+
+	dst[0] = ZlibDeflate | ZlibWin32k;
+
+	/* bogus zlib encoding of compression level */
+	dst[1] = ((level > 2) + (level > 5) + (level > 8)) << 6;
+
+	/* header check field */
+	dst[1] |= 31 - ((dst[0] << 8) | dst[1]) % 31;
+
+	adler = adler32(1, src, ssize);
+	dst[n + 2] = adler >> 24;
+	dst[n + 3] = adler >> 16;
+	dst[n + 4] = adler >> 8;
+	dst[n + 5] = adler;
+
+	return n + 6;
+}
--- /dev/null
+++ b/os/boot/libflate/flateerr.c
@@ -1,0 +1,22 @@
+#include "lib9.h"
+#include <flate.h>
+
+char *
+flateerr(int err)
+{
+	switch(err){
+	case FlateOk:
+		return "no error";
+	case FlateNoMem:
+		return "out of memory";
+	case FlateInputFail:
+		return "input error";
+	case FlateOutputFail:
+		return "output error";
+	case FlateCorrupted:
+		return "corrupted data";
+	case FlateInternal:
+		return "internal error";
+	}
+	return "unknown error";
+}
--- /dev/null
+++ b/os/boot/libflate/inflate.c
@@ -1,0 +1,692 @@
+#include "lib9.h"
+#include <flate.h>
+
+enum {
+	HistorySize=	32*1024,
+	BufSize=	4*1024,
+	MaxHuffBits=	17,	/* maximum bits in a encoded code */
+	Nlitlen=	288,	/* number of litlen codes */
+	Noff=		32,	/* number of offset codes */
+	Nclen=		19,	/* number of codelen codes */
+	LenShift=	10,	/* code = len<<LenShift|code */
+	LitlenBits=	7,	/* number of bits in litlen decode table */
+	OffBits=	6,	/* number of bits in offset decode table */
+	ClenBits=	6,	/* number of bits in code len decode table */
+	MaxFlatBits=	LitlenBits,
+	MaxLeaf=	Nlitlen
+};
+
+typedef struct Input	Input;
+typedef struct History	History;
+typedef struct Huff	Huff;
+
+struct Input
+{
+	int	error;		/* first error encountered, or FlateOk */
+	void	*wr;
+	int	(*w)(void*, void*, int);
+	void	*getr;
+	int	(*get)(void*);
+	ulong	sreg;
+	int	nbits;
+};
+
+struct History
+{
+	uchar	his[HistorySize];
+	uchar	*cp;		/* current pointer in history */
+	int	full;		/* his has been filled up at least once */
+};
+
+struct Huff
+{
+	int	maxbits;	/* max bits for any code */
+	int	minbits;	/* min bits to get before looking in flat */
+	int	flatmask;	/* bits used in "flat" fast decoding table */
+	ulong	flat[1<<MaxFlatBits];
+	ulong	maxcode[MaxHuffBits];
+	ulong	last[MaxHuffBits];
+	ulong	decode[MaxLeaf];
+};
+
+/* litlen code words 257-285 extra bits */
+static int litlenextra[Nlitlen-257] =
+{
+/* 257 */	0, 0, 0,
+/* 260 */	0, 0, 0, 0, 0, 1, 1, 1, 1, 2,
+/* 270 */	2, 2, 2, 3, 3, 3, 3, 4, 4, 4,
+/* 280 */	4, 5, 5, 5, 5, 0, 0, 0
+};
+
+static int litlenbase[Nlitlen-257];
+
+/* offset code word extra bits */
+static int offextra[Noff] =
+{
+	0,  0,  0,  0,  1,  1,  2,  2,  3,  3,
+	4,  4,  5,  5,  6,  6,  7,  7,  8,  8,
+	9,  9,  10, 10, 11, 11, 12, 12, 13, 13,
+	0,  0,
+};
+static int offbase[Noff];
+
+/* order code lengths */
+static int clenorder[Nclen] =
+{
+        16, 17, 18, 0, 8, 7, 9, 6, 10, 5, 11, 4, 12, 3, 13, 2, 14, 1, 15
+};
+
+/* for static huffman tables */
+static	Huff	litlentab;
+static	Huff	offtab;
+static	uchar	revtab[256];
+
+static int	uncblock(Input *in, History*);
+static int	fixedblock(Input *in, History*);
+static int	dynamicblock(Input *in, History*);
+static int	sregfill(Input *in, int n);
+static int	sregunget(Input *in);
+static int	decode(Input*, History*, Huff*, Huff*);
+static int	hufftab(Huff*, char*, int, int);
+static int	hdecsym(Input *in, Huff *h, int b);
+
+int
+inflateinit(void)
+{
+	char *len;
+	int i, j, base;
+
+	/* byte reverse table */
+	for(i=0; i<256; i++)
+		for(j=0; j<8; j++)
+			if(i & (1<<j))
+				revtab[i] |= 0x80 >> j;
+
+	for(i=257,base=3; i<Nlitlen; i++) {
+		litlenbase[i-257] = base;
+		base += 1<<litlenextra[i-257];
+	}
+	/* strange table entry in spec... */
+	litlenbase[285-257]--;
+
+	for(i=0,base=1; i<Noff; i++) {
+		offbase[i] = base;
+		base += 1<<offextra[i];
+	}
+
+	len = malloc(MaxLeaf);
+	if(len == nil)
+		return FlateNoMem;
+
+	/* static Litlen bit lengths */
+	for(i=0; i<144; i++)
+		len[i] = 8;
+	for(i=144; i<256; i++)
+		len[i] = 9;
+	for(i=256; i<280; i++)
+		len[i] = 7;
+	for(i=280; i<Nlitlen; i++)
+		len[i] = 8;
+
+	if(!hufftab(&litlentab, len, Nlitlen, MaxFlatBits))
+		return FlateInternal;
+
+	/* static Offset bit lengths */
+	for(i=0; i<Noff; i++)
+		len[i] = 5;
+
+	if(!hufftab(&offtab, len, Noff, MaxFlatBits))
+		return FlateInternal;
+	free(len);
+
+	return FlateOk;
+}
+
+int
+inflate(void *wr, int (*w)(void*, void*, int), void *getr, int (*get)(void*))
+{
+	History *his;
+	Input in;
+	int final, type;
+
+	his = malloc(sizeof(History));
+	if(his == nil)
+		return FlateNoMem;
+	his->cp = his->his;
+	his->full = 0;
+	in.getr = getr;
+	in.get = get;
+	in.wr = wr;
+	in.w = w;
+	in.nbits = 0;
+	in.sreg = 0;
+	in.error = FlateOk;
+
+	do {
+		if(!sregfill(&in, 3))
+			goto bad;
+		final = in.sreg & 0x1;
+		type = (in.sreg>>1) & 0x3;
+		in.sreg >>= 3;
+		in.nbits -= 3;
+		switch(type) {
+		default:
+			in.error = FlateCorrupted;
+			goto bad;
+		case 0:
+			/* uncompressed */
+			if(!uncblock(&in, his))
+				goto bad;
+			break;
+		case 1:
+			/* fixed huffman */
+			if(!fixedblock(&in, his))
+				goto bad;
+			break;
+		case 2:
+			/* dynamic huffman */
+			if(!dynamicblock(&in, his))
+				goto bad;
+			break;
+		}
+	} while(!final);
+
+	if(his->cp != his->his && (*w)(wr, his->his, his->cp - his->his) != his->cp - his->his) {
+		in.error = FlateOutputFail;
+		goto bad;
+	}
+
+	if(!sregunget(&in))
+		goto bad;
+
+	free(his);
+	if(in.error != FlateOk)
+		return FlateInternal;
+	return FlateOk;
+
+bad:
+	free(his);
+	if(in.error == FlateOk)
+		return FlateInternal;
+	return in.error;
+}
+
+static int
+uncblock(Input *in, History *his)
+{
+	int len, nlen, c;
+	uchar *hs, *hp, *he;
+
+	if(!sregunget(in))
+		return 0;
+	len = (*in->get)(in->getr);
+	len |= (*in->get)(in->getr)<<8;
+	nlen = (*in->get)(in->getr);
+	nlen |= (*in->get)(in->getr)<<8;
+	if(len != (~nlen&0xffff)) {
+		in->error = FlateCorrupted;
+		return 0;
+	}
+
+	hp = his->cp;
+	hs = his->his;
+	he = hs + HistorySize;
+
+	while(len > 0) {
+		c = (*in->get)(in->getr);
+		if(c < 0)
+			return 0;
+		*hp++ = c;
+		if(hp == he) {
+			his->full = 1;
+			if((*in->w)(in->wr, hs, HistorySize) != HistorySize) {
+				in->error = FlateOutputFail;
+				return 0;
+			}
+			hp = hs;
+		}
+		len--;
+	}
+
+	his->cp = hp;
+
+	return 1;
+}
+
+static int
+fixedblock(Input *in, History *his)
+{
+	return decode(in, his, &litlentab, &offtab);
+}
+
+static int
+dynamicblock(Input *in, History *his)
+{
+	Huff *lentab, *offtab;
+	char *len;
+	int i, j, n, c, nlit, ndist, nclen, res, nb;
+
+	if(!sregfill(in, 14))
+		return 0;
+	nlit = (in->sreg&0x1f) + 257;
+	ndist = ((in->sreg>>5) & 0x1f) + 1;
+	nclen = ((in->sreg>>10) & 0xf) + 4;
+	in->sreg >>= 14;
+	in->nbits -= 14;
+
+	if(nlit > Nlitlen || ndist > Noff || nlit < 257) {
+		in->error = FlateCorrupted;
+		return 0;
+	}
+
+	/* huff table header */
+	len = malloc(Nlitlen+Noff);
+	lentab = malloc(sizeof(Huff));
+	offtab = malloc(sizeof(Huff));
+	if(len == nil || lentab == nil || offtab == nil){
+		in->error = FlateNoMem;
+		goto bad;
+	}
+	for(i=0; i < Nclen; i++)
+		len[i] = 0;
+	for(i=0; i<nclen; i++) {
+		if(!sregfill(in, 3))
+			goto bad;
+		len[clenorder[i]] = in->sreg & 0x7;
+		in->sreg >>= 3;
+		in->nbits -= 3;
+	}
+
+	if(!hufftab(lentab, len, Nclen, ClenBits)){
+		in->error = FlateCorrupted;
+		goto bad;
+	}
+
+	n = nlit+ndist;
+	for(i=0; i<n;) {
+		nb = lentab->minbits;
+		for(;;){
+			if(in->nbits<nb && !sregfill(in, nb))
+				goto bad;
+			c = lentab->flat[in->sreg & lentab->flatmask];
+			nb = c & 0xff;
+			if(nb > in->nbits){
+				if(nb != 0xff)
+					continue;
+				c = hdecsym(in, lentab, c);
+				if(c < 0)
+					goto bad;
+			}else{
+				c >>= 8;
+				in->sreg >>= nb;
+				in->nbits -= nb;
+			}
+			break;
+		}
+
+		if(c < 16) {
+			j = 1;
+		} else if(c == 16) {
+			if(in->nbits<2 && !sregfill(in, 2))
+				goto bad;
+			j = (in->sreg&0x3)+3;
+			in->sreg >>= 2;
+			in->nbits -= 2;
+			if(i == 0) {
+				in->error = FlateCorrupted;
+				goto bad;
+			}
+			c = len[i-1];
+		} else if(c == 17) {
+			if(in->nbits<3 && !sregfill(in, 3))
+				goto bad;
+			j = (in->sreg&0x7)+3;
+			in->sreg >>= 3;
+			in->nbits -= 3;
+			c = 0;
+		} else if(c == 18) {
+			if(in->nbits<7 && !sregfill(in, 7))
+				goto bad;
+			j = (in->sreg&0x7f)+11;
+			in->sreg >>= 7;
+			in->nbits -= 7;
+			c = 0;
+		} else {
+			in->error = FlateCorrupted;
+			goto bad;
+		}
+
+		if(i+j > n) {
+			in->error = FlateCorrupted;
+			goto bad;
+		}
+
+		while(j) {
+			len[i] = c;
+			i++;
+			j--;
+		}
+	}
+
+	if(!hufftab(lentab, len, nlit, LitlenBits)
+	|| !hufftab(offtab, &len[nlit], ndist, OffBits)){
+		in->error = FlateCorrupted;
+		goto bad;
+	}
+
+	res = decode(in, his, lentab, offtab);
+
+	free(len);
+	free(lentab);
+	free(offtab);
+
+	return res;
+
+bad:
+	free(len);
+	free(lentab);
+	free(offtab);
+	return 0;
+}
+
+static int
+decode(Input *in, History *his, Huff *litlentab, Huff *offtab)
+{
+	int len, off;
+	uchar *hs, *hp, *hq, *he;
+	int c;
+	int nb;
+
+	hs = his->his;
+	he = hs + HistorySize;
+	hp = his->cp;
+
+	for(;;) {
+		nb = litlentab->minbits;
+		for(;;){
+			if(in->nbits<nb && !sregfill(in, nb))
+				return 0;
+			c = litlentab->flat[in->sreg & litlentab->flatmask];
+			nb = c & 0xff;
+			if(nb > in->nbits){
+				if(nb != 0xff)
+					continue;
+				c = hdecsym(in, litlentab, c);
+				if(c < 0)
+					return 0;
+			}else{
+				c >>= 8;
+				in->sreg >>= nb;
+				in->nbits -= nb;
+			}
+			break;
+		}
+
+		if(c < 256) {
+			/* literal */
+			*hp++ = c;
+			if(hp == he) {
+				his->full = 1;
+				if((*in->w)(in->wr, hs, HistorySize) != HistorySize) {
+					in->error = FlateOutputFail;
+					return 0;
+				}
+				hp = hs;
+			}
+			continue;
+		}
+
+		if(c == 256)
+			break;
+
+		if(c > 285) {
+			in->error = FlateCorrupted;
+			return 0;
+		}
+
+		c -= 257;
+		nb = litlenextra[c];
+		if(in->nbits < nb && !sregfill(in, nb))
+			return 0;
+		len = litlenbase[c] + (in->sreg & ((1<<nb)-1));
+		in->sreg >>= nb;
+		in->nbits -= nb;
+
+		/* get offset */
+		nb = offtab->minbits;
+		for(;;){
+			if(in->nbits<nb && !sregfill(in, nb))
+				return 0;
+			c = offtab->flat[in->sreg & offtab->flatmask];
+			nb = c & 0xff;
+			if(nb > in->nbits){
+				if(nb != 0xff)
+					continue;
+				c = hdecsym(in, offtab, c);
+				if(c < 0)
+					return 0;
+			}else{
+				c >>= 8;
+				in->sreg >>= nb;
+				in->nbits -= nb;
+			}
+			break;
+		}
+
+		if(c > 29) {
+			in->error = FlateCorrupted;
+			return 0;
+		}
+
+		nb = offextra[c];
+		if(in->nbits < nb && !sregfill(in, nb))
+			return 0;
+
+		off = offbase[c] + (in->sreg & ((1<<nb)-1));
+		in->sreg >>= nb;
+		in->nbits -= nb;
+
+		hq = hp - off;
+		if(hq < hs) {
+			if(!his->full) {
+				in->error = FlateCorrupted;
+				return 0;
+			}
+			hq += HistorySize;
+		}
+
+		/* slow but correct */
+		while(len) {
+			*hp = *hq;
+			hq++;
+			hp++;
+			if(hq >= he)
+				hq = hs;
+			if(hp == he) {
+				his->full = 1;
+				if((*in->w)(in->wr, hs, HistorySize) != HistorySize) {
+					in->error = FlateOutputFail;
+					return 0;
+				}
+				hp = hs;
+			}
+			len--;
+		}
+
+	}
+
+	his->cp = hp;
+
+	return 1;
+}
+
+static int
+revcode(int c, int b)
+{
+	/* shift encode up so it starts on bit 15 then reverse */
+	c <<= (16-b);
+	c = revtab[c>>8] | (revtab[c&0xff]<<8);
+	return c;
+}
+
+/*
+ * construct the huffman decoding arrays and a fast lookup table.
+ * the fast lookup is a table indexed by the next flatbits bits,
+ * which returns the symbol matched and the number of bits consumed,
+ * or the minimum number of bits needed and 0xff if more than flatbits
+ * bits are needed.
+ *
+ * flatbits can be longer than the smallest huffman code,
+ * because shorter codes are assigned smaller lexical prefixes.
+ * this means assuming zeros for the next few bits will give a
+ * conservative answer, in the sense that it will either give the
+ * correct answer, or return the minimum number of bits which
+ * are needed for an answer.
+ */
+static int
+hufftab(Huff *h, char *hb, int maxleaf, int flatbits)
+{
+	ulong bitcount[MaxHuffBits];
+	ulong c, fc, ec, mincode, code, nc[MaxHuffBits];
+	int i, b, minbits, maxbits;
+
+	for(i = 0; i < MaxHuffBits; i++)
+		bitcount[i] = 0;
+	maxbits = -1;
+	minbits = MaxHuffBits + 1;
+	for(i=0; i < maxleaf; i++){
+		b = hb[i];
+		if(b){
+			bitcount[b]++;
+			if(b < minbits)
+				minbits = b;
+			if(b > maxbits)
+				maxbits = b;
+		}
+	}
+
+	h->maxbits = maxbits;
+	if(maxbits <= 0){
+		h->maxbits = 0;
+		h->minbits = 0;
+		h->flatmask = 0;
+		return 1;
+	}
+	code = 0;
+	c = 0;
+	for(b = 0; b <= maxbits; b++){
+		h->last[b] = c;
+		c += bitcount[b];
+		mincode = code << 1;
+		nc[b] = mincode;
+		code = mincode + bitcount[b];
+		if(code > (1 << b))
+			return 0;
+		h->maxcode[b] = code - 1;
+		h->last[b] += code - 1;
+	}
+
+	if(flatbits > maxbits)
+		flatbits = maxbits;
+	h->flatmask = (1 << flatbits) - 1;
+	if(minbits > flatbits)
+		minbits = flatbits;
+	h->minbits = minbits;
+
+	b = 1 << flatbits;
+	for(i = 0; i < b; i++)
+		h->flat[i] = ~0;
+
+	/*
+	 * initialize the flat table to include the minimum possible
+	 * bit length for each code prefix
+	 */
+	for(b = maxbits; b > flatbits; b--){
+		code = h->maxcode[b];
+		if(code == -1)
+			break;
+		mincode = code + 1 - bitcount[b];
+		mincode >>= b - flatbits;
+		code >>= b - flatbits;
+		for(; mincode <= code; mincode++)
+			h->flat[revcode(mincode, flatbits)] = (b << 8) | 0xff;
+	}
+
+	for(i = 0; i < maxleaf; i++){
+		b = hb[i];
+		if(b <= 0)
+			continue;
+		c = nc[b]++;
+		if(b <= flatbits){
+			code = (i << 8) | b;
+			ec = (c + 1) << (flatbits - b);
+			if(ec > (1<<flatbits))
+				return 0;	/* this is actually an internal error */
+			for(fc = c << (flatbits - b); fc < ec; fc++)
+				h->flat[revcode(fc, flatbits)] = code;
+		}
+		if(b > minbits){
+			c = h->last[b] - c;
+			if(c >= maxleaf)
+				return 0;
+			h->decode[c] = i;
+		}
+	}
+	return 1;
+}
+
+static int
+hdecsym(Input *in, Huff *h, int nb)
+{
+	long c;
+
+	if((nb & 0xff) == 0xff)
+		nb = nb >> 8;
+	else
+		nb = nb & 0xff;
+	for(; nb <= h->maxbits; nb++){
+		if(in->nbits<nb && !sregfill(in, nb))
+			return -1;
+		c = revtab[in->sreg&0xff]<<8;
+		c |= revtab[(in->sreg>>8)&0xff];
+		c >>= (16-nb);
+		if(c <= h->maxcode[nb]){
+			in->sreg >>= nb;
+			in->nbits -= nb;
+			return h->decode[h->last[nb] - c];
+		}
+	}
+	in->error = FlateCorrupted;
+	return -1;
+}
+
+static int
+sregfill(Input *in, int n)
+{
+	int c;
+
+	while(n > in->nbits) {
+		c = (*in->get)(in->getr);
+		if(c < 0){
+			in->error = FlateInputFail;
+			return 0;
+		}
+		in->sreg |= c<<in->nbits;
+		in->nbits += 8;
+	}
+	return 1;
+}
+
+static int
+sregunget(Input *in)
+{
+	if(in->nbits >= 8) {
+		in->error = FlateInternal;
+		return 0;
+	}
+
+	/* throw other bits on the floor */
+	in->nbits = 0;
+	in->sreg = 0;
+	return 1;
+}
--- /dev/null
+++ b/os/boot/libflate/inflateblock.c
@@ -1,0 +1,53 @@
+#include "lib9.h"
+#include <flate.h>
+
+typedef struct Block	Block;
+
+struct Block
+{
+	uchar	*pos;
+	uchar	*limit;
+};
+
+static int
+blgetc(void *vb)
+{
+	Block *b;
+
+	b = vb;
+	if(b->pos >= b->limit)
+		return -1;
+	return *b->pos++;
+}
+
+static int
+blwrite(void *vb, void *buf, int n)
+{
+	Block *b;
+
+	b = vb;
+
+	if(n > b->limit - b->pos)
+		n = b->limit - b->pos;
+	memmove(b->pos, buf, n);
+	b->pos += n;
+	return n;
+}
+
+int
+inflateblock(uchar *dst, int dsize, uchar *src, int ssize)
+{
+	Block bd, bs;
+	int ok;
+
+	bs.pos = src;
+	bs.limit = src + ssize;
+
+	bd.pos = dst;
+	bd.limit = dst + dsize;
+
+	ok = inflate(&bd, blwrite, &bs, blgetc);
+	if(ok != FlateOk)
+		return ok;
+	return bd.pos - dst;
+}
--- /dev/null
+++ b/os/boot/libflate/inflatezlib.c
@@ -1,0 +1,65 @@
+#include "lib9.h"
+#include <flate.h>
+#include "zlib.h"
+
+typedef struct ZWrite	ZWrite;
+
+struct ZWrite
+{
+	ulong	adler;
+	void	*wr;
+	int	(*w)(void*, void*, int);
+};
+
+static int
+zlwrite(void *vzw, void *buf, int n)
+{
+	ZWrite *zw;
+
+	zw = vzw;
+	zw->adler = adler32(zw->adler, buf, n);
+	n = (*zw->w)(zw->wr, buf, n);
+	if(n <= 0)
+		return n;
+	return n;
+}
+
+int
+inflatezlib(void *wr, int (*w)(void*, void*, int), void *getr, int (*get)(void*))
+{
+	ZWrite zw;
+	ulong v;
+	int c, i;
+
+	c = (*get)(getr);
+	if(c < 0)
+		return FlateInputFail;
+	i = (*get)(getr);
+	if(i < 0)
+		return FlateInputFail;
+
+	if(((c << 8) | i) % 31)
+		return FlateCorrupted;
+	if((c & ZlibMeth) != ZlibDeflate
+	|| (c & ZlibCInfo) > ZlibWin32k)
+		return FlateCorrupted;
+
+	zw.wr = wr;
+	zw.w = w;
+	zw.adler = 1;
+	i = inflate(&zw, zlwrite, getr, get);
+	if(i != FlateOk)
+		return i;
+
+	v = 0;
+	for(i = 0; i < 4; i++){
+		c = (*get)(getr);
+		if(c < 0)
+			return FlateInputFail;
+		v = (v << 8) | c;
+	}
+	if(zw.adler != v)
+		return FlateCorrupted;
+
+	return FlateOk;
+}
--- /dev/null
+++ b/os/boot/libflate/inflatezlibblock.c
@@ -1,0 +1,67 @@
+#include "lib9.h"
+#include <flate.h>
+#include "zlib.h"
+
+typedef struct Block	Block;
+
+struct Block
+{
+	uchar	*pos;
+	uchar	*limit;
+};
+
+static int
+blgetc(void *vb)
+{
+	Block *b;
+
+	b = vb;
+	if(b->pos >= b->limit)
+		return -1;
+	return *b->pos++;
+}
+
+static int
+blwrite(void *vb, void *buf, int n)
+{
+	Block *b;
+
+	b = vb;
+
+	if(n > b->limit - b->pos)
+		n = b->limit - b->pos;
+	memmove(b->pos, buf, n);
+	b->pos += n;
+	return n;
+}
+
+int
+inflatezlibblock(uchar *dst, int dsize, uchar *src, int ssize)
+{
+	Block bd, bs;
+	int ok;
+
+	if(ssize < 6)
+		return FlateInputFail;
+
+	if(((src[0] << 8) | src[1]) % 31)
+		return FlateCorrupted;
+	if((src[0] & ZlibMeth) != ZlibDeflate
+	|| (src[0] & ZlibCInfo) > ZlibWin32k)
+		return FlateCorrupted;
+
+	bs.pos = src + 2;
+	bs.limit = src + ssize - 6;
+
+	bd.pos = dst;
+	bd.limit = dst + dsize;
+
+	ok = inflate(&bd, blwrite, &bs, blgetc);
+	if(ok != FlateOk)
+		return ok;
+
+	if(adler32(1, dst, bs.pos - dst) != ((bs.pos[0] << 24) | (bs.pos[1] << 16) | (bs.pos[2] << 8) | bs.pos[3]))
+		return FlateCorrupted;
+
+	return bd.pos - dst;
+}
--- /dev/null
+++ b/os/boot/libflate/mkfile
@@ -1,0 +1,21 @@
+<../../../mkconfig
+
+LIB=libflate.a
+OFILES=\
+	deflate.$O\
+	deflatezlib.$O\
+	deflateblock.$O\
+	deflatezlibblock.$O\
+	inflate.$O\
+	inflatezlib.$O\
+	inflateblock.$O\
+	inflatezlibblock.$O\
+	flateerr.$O\
+	crc.$O\
+	adler.$O\
+
+HFILES=\
+	$ROOT/include/flate.h\
+	zlib.h\
+
+<$ROOT/mkfiles/mksyslib-$SHELLTYPE
--- /dev/null
+++ b/os/boot/libflate/zlib.h
@@ -1,0 +1,11 @@
+/*
+ * zlib header fields
+ */
+enum
+{
+	ZlibMeth	= 0x0f,			/* mask of compression methods */
+	ZlibDeflate	= 0x08,
+
+	ZlibCInfo	= 0xf0,			/* mask of compression aux. info */
+	ZlibWin32k	= 0x70,			/* 32k history window */
+};
--- /dev/null
+++ b/os/boot/mpc/NOTICE
@@ -1,0 +1,3 @@
+Inferno® Copyright © 1996-1999 Lucent Technologies Inc.  All rights reserved.
+PowerPC support Copyright © 1995-1997 C H Forsyth (forsyth@caldo.demon.co.uk).  All rights reserved.
+MPC8xx Inferno PowerPC port Copyright © 1998-2003 Vita Nuova Holdings Limited.  All rights reserved.
--- /dev/null
+++ b/os/boot/mpc/alarm.c
@@ -1,0 +1,123 @@
+#include	"u.h"
+#include	"lib.h"
+#include	"mem.h"
+#include	"dat.h"
+#include	"fns.h"
+#include	"io.h"
+#define	MAXALARM	10
+
+Alarm	alarmtab[MAXALARM];
+
+/*
+ * Insert new into list after where
+ */
+void
+insert(List **head, List *where, List *new)
+{
+	if(where == 0){
+		new->next = *head;
+		*head = new;
+	}else{
+		new->next = where->next;
+		where->next = new;
+	}
+		
+}
+
+/*
+ * Delete old from list.  where->next is known to be old.
+ */
+void
+delete(List **head, List *where, List *old)
+{
+	if(where == 0){
+		*head = old->next;
+		return;
+	}
+	where->next = old->next;
+}
+
+Alarm*
+newalarm(void)
+{
+	int i;
+	Alarm *a;
+
+	for(i=0,a=alarmtab; i < nelem(alarmtab); i++,a++)
+		if(a->busy==0 && a->f==0){
+			a->f = 0;
+			a->arg = 0;
+			a->busy = 1;
+			return a;
+		}
+	panic("newalarm");
+	return 0;	/* not reached */
+}
+
+Alarm*
+alarm(int ms, void (*f)(Alarm*), void *arg)
+{
+	Alarm *a, *w, *pw;
+	ulong s;
+
+	if(ms < 0)
+		ms = 0;
+	s = splhi();
+	a = newalarm();
+	a->dt = MS2TK(ms);
+	a->f = f;
+	a->arg = arg;
+	pw = 0;
+	for(w=m->alarm; w; pw=w, w=w->next){
+		if(w->dt <= a->dt){
+			a->dt -= w->dt;
+			continue;
+		}
+		w->dt -= a->dt;
+		break;
+	}
+	insert(&m->alarm, pw, a);
+	splx(s);
+	return a;
+}
+
+void
+cancel(Alarm *a)
+{
+	a->f = 0;
+}
+
+void
+alarminit(void)
+{
+}
+
+#define NA 10		/* alarms per clock tick */
+void
+checkalarms(void)
+{
+	int i, n, s;
+	Alarm *a;
+	void (*f)(Alarm*);
+	Alarm *alist[NA];
+
+	s = splhi();
+	a = m->alarm;
+	if(a){
+		for(n=0; a && a->dt<=0 && n<NA; n++){
+			alist[n] = a;
+			delete(&m->alarm, 0, a);
+			a = m->alarm;
+		}
+		if(a)
+			a->dt--;
+
+		for(i = 0; i < n; i++){
+			f = alist[i]->f;	/* avoid race with cancel */
+			if(f)
+				(*f)(alist[i]);
+			alist[i]->busy = 0;
+		}
+	}
+	splx(s);
+}
--- /dev/null
+++ b/os/boot/mpc/all.h
@@ -1,0 +1,6 @@
+#include	"u.h"
+#include	"lib.h"
+#include	"dat.h"
+#include	"fns.h"
+#include	"mem.h"
+#include	"io.h"
--- /dev/null
+++ b/os/boot/mpc/archfads.c
@@ -1,0 +1,355 @@
+#include	"u.h"
+#include	"lib.h"
+#include	"mem.h"
+#include	"dat.h"
+#include	"fns.h"
+#include	"io.h"
+#include	"archfads.h"
+
+/*
+ * board-specific support for the 8xxFADS (including 860/21 development system)
+ */
+
+enum {
+	USESDRAM = 0,	/* set to 1 if kernel is to use SDRAM as well as DRAM */
+
+	/* sccr */
+	RTSEL = IBIT(8),	/* =0, select main oscillator (OSCM); =1, select external crystal (EXTCLK) */
+	RTDIV = IBIT(7),	/* =0, divide by 4; =1, divide by 512 */
+	CRQEN = IBIT(9),	/* =1, switch to high frequency when CPM active */
+	PRQEN = IBIT(10),	/* =1, switch to high frequency when interrupt pending */
+
+	/* plprcr */
+	CSRC = IBIT(21),	/* =0, clock is DFNH; =1, clock is DFNL */
+};
+
+/*
+ * called early in main.c, after machinit:
+ * using board and architecture specific registers, initialise
+ * 8xx registers that need it and complete initialisation of the Mach structure.
+ */
+void
+archinit(void)
+{
+	IMM *io;
+	int mf, isfads, sysmhz, t;
+	ulong v;
+
+	v = getimmr() & 0xFFFF;
+	isfads = 0;		/* assume it's the 860/821 board */
+	sysmhz = 40;
+	switch(v>>8){
+	case 0x00:	t = 0x86000; break;
+	case 0x20:	t = 0x82300; isfads = 1; break;
+	case 0x21:	t = 0x823a0; isfads = 1; break;
+	default:	t = 0; break;
+	}
+	m->cputype = t;
+	m->bcsr = KADDR(BCSRMEM);
+	m->bcsr[1] |= DisableRS232a | DisableIR | DisableEther | DisablePCMCIA | DisableRS232b;
+	m->bcsr[1] &= ~(DisableDRAM|DisableFlash);
+	if(isfads){
+		sysmhz = 50;
+		m->bcsr[1] &= ~EnableSDRAM;
+		m->bcsr[4] &= ~(EnableVideoClock|EnableVideoPort);
+		m->bcsr[4] |= DisableVideoLamp;
+	}
+	io = m->iomem;
+	if(1 || io->sccr & IBIT(7)){	/* RTDIV=1 */
+		/* oscillator frequency can't be determined independently: check a switch */
+		if((m->bcsr[2]>>19)&(1<<2))
+			m->clockgen = 5*MHz;
+		else
+			m->clockgen = 4*MHz;
+	} else
+		m->clockgen = 32768;
+	mf = (sysmhz*MHz)/m->clockgen;
+	m->cpuhz = m->clockgen*mf;
+	io->plprcrk = KEEP_ALIVE_KEY;
+	io->plprcr &= ~IBIT(21);	/* general system clock is DFNH */
+	io->plprcr = (io->plprcr & ((1<<20)-1)) | ((mf-1)<<20);
+	io->mptpr = 0x0400;	/* memory prescaler = 16 for refresh */
+	io->plprcrk = ~KEEP_ALIVE_KEY;
+	if(isfads){
+		m->bcsr[1] |= EnableSDRAM;
+		sdraminit(SDRAMMEM);
+		if(!USESDRAM)
+			m->bcsr[1] &= ~EnableSDRAM;	/* tells kernel not to map it */
+	}
+}
+
+static void
+archidprint(void)
+{
+	int f, i;
+	ulong v;
+
+	/* 8xx and FADS specific */
+	print("IMMR: ");
+	v = getimmr() & 0xFFFF;
+	switch(v>>8){
+	case 0x00:	print("MPC860/821"); break;
+	case 0x20:	print("MPC823"); break;
+	case 0x21:	print("MPC823A"); break;
+	default:	print("Type #%lux", v>>8); break;
+	}
+	print(", mask #%lux\n", v&0xFF);
+	v = m->bcsr[3]>>16;
+	print("MPC8xxFADS rev %lud, DB: ", ((v>>4)&8)|((v>>1)&4)|(v&3));
+	f = (v>>8)&0x3F;
+	switch(f){
+	default:	print("ID#%x", f); break;
+	case 0x00:	print("MPC860/821"); break;
+	case 0x01:	print("MPC813"); break;
+	case 0x02:	print("MPC821"); break;
+	case 0x03:	print("MPC823"); break;
+	case 0x20:	print("MPC801"); break;
+	case 0x21:	print("MPC850"); break;
+	case 0x22:	print("MPC860"); break;
+	case 0x23:	print("MPC860SAR"); break;
+	case 0x24:	print("MPC860T"); break;
+	}
+	print("ADS, rev #%lux\n", (m->bcsr[2]>>16)&7);
+	for(i=0; i<=4; i++)
+		print("BCSR%d: %8.8lux\n", i, m->bcsr[i]);
+	v = m->bcsr[2];
+	f = (v>>28)&0xF;
+	switch(f){
+	default:	print("Unknown"); break;
+	case 4:	print("SM732A2000/SM73228 - 8M SIMM"); break;
+	case 5:	print("SM732A1000A/SM73218 - 4M SIMM"); break;
+	case 6:	print("MCM29080 - 8M SIMM"); break;
+	case 7:	print("MCM29040 - 4M SIMM"); break;
+	case 8:	print("MCM29020 - 2M SIMM"); break;
+	}
+	switch((m->bcsr[3]>>20)&7){
+	default:	i = 0; break;
+	case 1:	i = 150; break;
+	case 2:	i = 120; break;
+	case 3:	i = 90; break;
+	}
+	print(" flash, %dns\n", i);
+	f = (v>>23)&0xF;
+	switch(f&3){
+	case 0:	i = 4; break;
+	case 1:	i = 32; break;
+	case 2:	i = 16; break;
+	case 3:	i = 8; break;
+	}
+	print("%dM SIMM, ", i);
+	switch(f>>2){
+	default: 	i = 0; break;
+	case 2:	i = 70; break;
+	case 3:	i = 60; break;
+	}
+	print("%dns\n", i);
+	print("options: #%lux\n", (m->bcsr[2]>>19)&0xF);
+	print("plprcr=%8.8lux sccr=%8.8lux\n", m->iomem->plprcr, m->iomem->sccr);
+}
+
+void
+cpuidprint(void)
+{
+	int t;
+
+	print("PVR: ");
+	t = getpvr()>>16;
+	switch(t){
+	case 0x01:	print("MPC601"); break;
+	case 0x03:	print("MPC603"); break;
+	case 0x04:	print("MPC604"); break;
+	case 0x06:	print("MPC603e"); break;
+	case 0x07:	print("MPC603e-v7"); break;
+	case 0x50:	print("MPC8xx"); break;
+	default:	print("PowerPC version #%x", t); break;
+	}
+	print(", revision #%lux\n", getpvr()&0xffff);
+	archidprint();
+	print("%lud MHz system\n", m->cpuhz/MHz);
+	print("\n");
+}
+
+static	char*	defplan9ini[2] = {
+	/* 860/821 */
+	"ether0=type=SCC port=1 ea=00108bf12900\r\n"
+	"vgasize=640x480x8\r\n"
+	"kernelpercent=40\r\n"
+	"console=0 lcd\r\nbaud=9600\r\n",
+
+	/* 823 */
+	"ether0=type=SCC port=2 ea=00108bf12900\r\n"
+	"vgasize=640x480x8\r\n"
+	"kernelpercent=40\r\n"
+	"console=0 lcd\r\nbaud=9600\r\n",
+};
+
+char *
+archconfig(void)
+{
+	print("Using default configuration\n");
+	return defplan9ini[MPCMODEL(m->cputype) == 0x823];
+}
+
+/*
+ * provide value for #r/switch (devrtc.c)
+ */
+int
+archoptionsw(void)
+{
+	return (m->bcsr[2]>>19)&0xF;	/* value of switch DS1 */
+}
+
+/*
+ * invoked by clock.c:/^clockintr
+ */
+static void
+twinkle(void)
+{
+	if(m->ticks%MS2TK(1000) == 0)
+		m->bcsr[4] ^= DisableLamp;
+}
+
+void	(*archclocktick)(void) = twinkle;
+
+/*
+ * for flash.c:/^flashreset
+ * retrieve flash type, virtual base and length and return 0;
+ * return -1 on error (no flash)
+ */
+int
+archflashreset(char *type, void **addr, long *length)
+{
+	char *t;
+	int mbyte;
+
+	if((m->iomem->memc[0].base & 1) == 0)
+		return -1;		/* shouldn't happen */
+	switch((m->bcsr[2]>>28)&0xF){
+	default:	return -1;	/* unknown or not there */
+	case 4:	mbyte=8; t = "SM732x8"; break;
+	case 5:	mbyte=4; t = "SM732x8"; break;
+	case 6:	mbyte=8; t = "AMD29F0x0"; break;
+	case 7:	mbyte=4; t = "AMD29F0x0"; break;
+	case 8:	mbyte=2; t = "AMD29F0x0"; break;
+	}
+	strcpy(type, t);
+	*addr = KADDR(FLASHMEM);
+	*length = mbyte*1024*1024;
+	return 0;
+}
+
+/*
+ * enable the clocks for the given SCC ether and reveal them to the caller.
+ * do anything else required to prepare the transceiver (eg, set full-duplex, reset loopback).
+ */
+int
+archetherenable(int cpmid, int *rcs, int *tcs)
+{
+	IMM *io;
+
+	switch(cpmid){
+	default:
+		/* no other SCCs are wired on the FADS board */
+		return -1;
+
+	case SCC2ID:	/* assume 8xxFADS board with 823DABS */
+		io = ioplock();
+		m->bcsr[1] |= DisableIR|DisableRS232b;
+		m->bcsr[1] &= ~DisableEther;
+		io->papar |= SIBIT(6)|SIBIT(5);	/* enable CLK2 and CLK3 */
+		io->padir &= ~(SIBIT(6)|SIBIT(5));
+
+		/* ETHLOOP etc set in BCSR elsewhere */
+		*rcs = CLK2;
+		*tcs = CLK3;
+		iopunlock();
+		break;
+
+	case SCC1ID:	/* assume 860/21 development board */
+		io = ioplock();
+		m->bcsr[1] |= DisableIR|DisableRS232b;	/* TO DO: might not be shared with RS232b */
+		m->bcsr[1] &= ~DisableEther;
+		io->papar |= SIBIT(6)|SIBIT(7);	/* enable CLK2 and CLK1 */
+		io->padir &= ~(SIBIT(6)|SIBIT(7));
+
+		/* settings peculiar to 860/821 development board */
+		io->pcpar &= ~(SIBIT(4)|SIBIT(5)|SIBIT(6));	/* ETHLOOP, TPFULDL~, TPSQEL~ */
+		io->pcdir |= SIBIT(4)|SIBIT(5)|SIBIT(6);
+		io->pcdat &= ~SIBIT(4);
+		io->pcdat |= SIBIT(5)|SIBIT(6);
+		*rcs = CLK2;
+		*tcs = CLK1;
+		iopunlock();
+		break;
+	}
+	return 0;
+}
+
+void
+archetherdisable(int id)
+{
+	USED(id);
+	m->bcsr[1] |= DisableEther|DisableIR|DisableRS232b;
+}
+
+/*
+ * do anything extra required to enable the UART on the given CPM port
+ */
+void
+archenableuart(int id, int irda)
+{
+	switch(id){
+	case SMC1ID:
+		m->bcsr[1] &= ~DisableRS232a;
+		break;
+	case SCC2ID:
+		m->bcsr[1] |= DisableEther|DisableIR|DisableRS232b;
+		if(irda)
+			m->bcsr[1] &= ~DisableIR;
+		else
+			m->bcsr[1] &= ~DisableRS232b;
+		break;
+	default:
+		/* nothing special */
+		break;
+	}
+}
+
+/*
+ * do anything extra required to disable the UART on the given CPM port
+ */
+void
+archdisableuart(int id)
+{
+	switch(id){
+	case SMC1ID:
+		m->bcsr[1] |= DisableRS232a;
+		break;
+	case SCC2ID:
+		m->bcsr[1] |= DisableIR|DisableRS232b;
+		break;
+	default:
+		/* nothing special */
+		break;
+	}
+}
+
+/*
+ * enable/disable the LCD panel's backlight via
+ * York touch panel interface (does no harm without it)
+ */
+void
+archbacklight(int on)
+{
+	IMM *io;
+
+	delay(2);
+	io = ioplock();
+	io->papar &= ~SIBIT(4);
+	io->padir |= SIBIT(4);
+	if(on)
+		io->padat |= SIBIT(4);
+	else
+		io->padat &= ~SIBIT(4);
+	iopunlock();
+}
--- /dev/null
+++ b/os/boot/mpc/archfads.h
@@ -1,0 +1,42 @@
+
+enum {
+	/* BCSR1 bits */
+	DisableFlash=	IBIT(0),
+	DisableDRAM=	IBIT(1),
+	DisableEther=	IBIT(2),
+	DisableIR=	IBIT(3),
+	DisableRS232a=	IBIT(7),
+	DisablePCMCIA=	IBIT(8),
+	PCCVCCMask=	IBIT(9)|IBIT(15),
+	PCCVPPMask=	IBIT(10)|IBIT(11),
+	DisableRS232b=	IBIT(13),
+	EnableSDRAM=	IBIT(14),
+
+	PCCVCC0V=	IBIT(15)|IBIT(9),
+	PCCVCC5V=	IBIT(9),	/* active low */
+	PCCVCC3V=	IBIT(15),	/* active low */
+	PCCVPP0V=	IBIT(10)|IBIT(11),	/* active low */
+	PCCVPP5V=	IBIT(10),	/* active low */
+	PCCVPP12V=	IBIT(11),	/* active low */
+	PCCVPPHiZ=	IBIT(10)|IBIT(11),
+
+	/* BCSR4 bits */
+	DisableTPDuplex=	IBIT(1),
+	DisableLamp=	IBIT(3),
+	DisableUSB=	IBIT(4),
+	USBFullSpeed=	IBIT(5),
+	DisableUSBVcc=	IBIT(6),
+	DisableVideoLamp=	IBIT(8),
+	EnableVideoClock=	IBIT(9),
+	EnableVideoPort=	IBIT(10),
+	DisableModem=	IBIT(11),
+};
+
+enum {
+	/* memory controller CS assignment on FADS boards */
+	BOOTCS = 0,
+	BCSRCS = 1,
+	DRAM1 = 2,
+	DRAM2 = 3,
+	SDRAM = 4,
+};
--- /dev/null
+++ b/os/boot/mpc/archpaq.c
@@ -1,0 +1,240 @@
+#include	"u.h"
+#include	"lib.h"
+#include	"mem.h"
+#include	"dat.h"
+#include	"fns.h"
+#include	"io.h"
+
+#include	"archpaq.h"
+
+/*
+ * board-specific support for the 82x PowerPAQ
+ */
+
+enum {
+	SYSMHZ = 50,	/* desired system clock in MHz */
+
+	/* sccr */
+	RTSEL = IBIT(8),	/* =0, select main oscillator (OSCM); =1, select external crystal (EXTCLK) */
+	RTDIV = IBIT(7),	/* =0, divide by 4; =1, divide by 512 */
+	CRQEN = IBIT(9),	/* =1, switch to high frequency when CPM active */
+	PRQEN = IBIT(10),	/* =1, switch to high frequency when interrupt pending */
+
+	/* plprcr */
+	CSRC = IBIT(21),	/* =0, clock is DFNH; =1, clock is DFNL */
+};
+
+/*
+ * called early in main.c, after machinit:
+ * using board and architecture specific registers, initialise
+ * 8xx registers that need it and complete initialisation of the Mach structure.
+ */
+void
+archinit(void)
+{
+	IMM *io;
+	int mf, t;
+
+	switch((getimmr()>>8)&0xFF){
+	case 0x00:	t = 0x86000; break;	/* also 821 */
+	case 0x20:	t = 0x82300; break;
+	case 0x21:	t = 0x823a0; break;
+	default:	t = 0; break;
+	}
+	m->cputype = t;
+	m->bcsr = nil;	/* there isn't one */
+	m->clockgen = 32*1024;	/* crystal frequency */
+	io = m->iomem;
+	io->sccrk = KEEP_ALIVE_KEY;
+	io->sccr &= ~RTDIV;	/* divide 32k by 4 */
+	io->sccr |= RTSEL;
+	io->sccrk = ~KEEP_ALIVE_KEY;
+	mf = (SYSMHZ*MHz)/m->clockgen;
+	m->cpuhz = m->clockgen*mf;
+	io->plprcrk = KEEP_ALIVE_KEY;
+	io->plprcr &= ~IBIT(21);	/* general system clock is DFNH */
+	io->plprcr = (io->plprcr & ((1<<20)-1)) | ((mf-1)<<20);
+	io->mptpr = 0x0400;	/* memory prescaler = 16 for refresh */
+	io->plprcrk = ~KEEP_ALIVE_KEY;
+}
+
+void
+cpuidprint(void)
+{
+	int t, v;
+
+	print("PVR: ");
+	t = getpvr()>>16;
+	switch(t){
+	case 0x01:	print("MPC601"); break;
+	case 0x03:	print("MPC603"); break;
+	case 0x04:	print("MPC604"); break;
+	case 0x06:	print("MPC603e"); break;
+	case 0x07:	print("MPC603e-v7"); break;
+	case 0x50:	print("MPC8xx"); break;
+	default:	print("PowerPC version #%x", t); break;
+	}
+	print(", revision #%lux\n", getpvr()&0xffff);
+	print("IMMR: ");
+	v = getimmr() & 0xFFFF;
+	switch(v>>8){
+	case 0x00:	print("MPC860/821"); break;
+	case 0x20:	print("MPC823"); break;
+	case 0x21:	print("MPC823A"); break;
+	default:	print("Type #%lux", v>>8); break;
+	}
+	print(", mask #%lux\n", v&0xFF);
+	print("plprcr=%8.8lux sccr=%8.8lux\n", m->iomem->plprcr, m->iomem->sccr);
+	print("%lud MHz system\n", m->cpuhz/MHz);
+	print("\n");
+}
+
+static	char*	defplan9ini[2] = {
+	/* 860/821 */
+	"ether0=type=SCC port=1 ea=00108bf12900\r\n"
+	"vgasize=640x480x8\r\n"
+	"kernelpercent=40\r\n"
+	"console=0 lcd\r\nbaud=19200\r\n",
+
+	/* 823 */
+	"ether0=type=SCC port=2 ea=00108bf12900\r\n"
+	"vgasize=640x480x8\r\n"
+	"kernelpercent=40\r\n"
+	"console=0 lcd\r\nbaud=19200\r\n",
+};
+
+char *
+archconfig(void)
+{
+	print("Using default configuration\n");
+	return defplan9ini[MPCMODEL(m->cputype) == 0x823];
+}
+
+/*
+ * provide value for #r/switch (devrtc.c)
+ */
+int
+archoptionsw(void)
+{
+	return 0;
+}
+
+/*
+ * invoked by clock.c:/^clockintr
+ */
+static void
+twinkle(void)
+{
+	/* no easy-to-use LED on PAQ (they use i2c) */
+}
+
+void	(*archclocktick)(void) = twinkle;
+
+/*
+ * for flash.c:/^flashinit
+ * retrieve flash type, virtual base and length and return 0;
+ * return -1 on error (no flash)
+ */
+int
+archflashreset(char *type, void **addr, long *length)
+{
+	strcpy(type, "AMD29F0x0");
+	*addr = KADDR(FLASHMEM);
+	*length = 8*1024*1024;	/* 8mbytes on some models */
+	return 0;
+}
+
+/*
+ * enable the clocks for the given SCC ether and reveal them to the caller.
+ * do anything else required to prepare the transceiver (eg, set full-duplex, reset loopback).
+ */
+int
+archetherenable(int cpmid, int *rcs, int *tcs)
+{
+	USED(cpmid, rcs, tcs);
+	return -1;	/* there isn't an ether on the PAQs */
+}
+
+void
+archetherdisable(int id)
+{
+	USED(id);
+}
+
+/*
+ * do anything extra required to enable the UART on the given CPM port
+ */
+void
+archenableuart(int id, int irda)
+{
+	IMM *io;
+
+	USED(irda);
+	switch(id){
+	case SMC1ID:
+		io = ioplock();
+		io->pbodr &= ~0xc0;
+		io->pbdat |= 0xc0;
+		io->pcdat |= 0x400;
+		io->pcpar &= ~0x400;
+		io->pcdir |= 0x400;
+		io->pcdat &= ~0x400;	/* enable SMC RS232 buffer */
+		iopunlock();
+		break;
+	case SCC2ID:
+		/* TO DO */
+		break;
+	default:
+		/* nothing special */
+		break;
+	}
+}
+
+/*
+ * do anything extra required to disable the UART on the given CPM port
+ */
+void
+archdisableuart(int id)
+{
+	switch(id){
+	case SMC1ID:
+		/* TO DO */
+		break;
+	case SCC2ID:
+		/* TO DO */
+		break;
+	default:
+		/* nothing special */
+		break;
+	}
+}
+
+/*
+ * enable/disable the LCD panel's backlight via i2c
+ */
+void
+archbacklight(int on)
+{
+	uchar msg;
+	IMM *io;
+
+	i2csetup();
+	msg = ~7;
+	i2csend(LEDRegI2C, &msg, 1);
+	io = ioplock();
+	io->pbpar &= ~EnableLCD;
+	io->pbodr &= ~EnableLCD;
+	io->pbdir |= EnableLCD;
+	if(on)
+		io->pbdat |= EnableLCD;
+	else
+		io->pbdat &= ~EnableLCD;
+	iopunlock();
+	if(on){
+		msg = ~(DisablePanelVCC5|DisableTFT);
+		i2csend(PanelI2C, &msg, 1);
+	}else{
+		msg = ~0;
+		i2csend(PanelI2C, &msg, 1);
+	}
+}
--- /dev/null
+++ b/os/boot/mpc/archpaq.h
@@ -1,0 +1,29 @@
+enum {
+	/* memory controller CS assignment on PowerPAQ */
+	BOOTCS = 0,
+	DRAM1 = 1,	/* UPMB */
+	DRAM2 = 2,	/* UPMB */
+	/* CS3 also connected to DRAM */
+	/* CS4 128mbyte 8-bit gpcm, trlx, 15 wait; it's DAC */
+	/* CS5 is external*/
+};
+
+enum {
+	/* I2C addresses */
+	PanelI2C = 0x21<<1,
+	  /* the control bits are active low enables, or high disables */
+	  DisableVGA = ~0xFD,	/* disable VGA signals */
+	  DisableTFT = ~0xFB,	/* disable TFT panel signals */
+	  DisableSPIBus = ~0xF7,	/* disable SPI/I2C to panel */
+	  DisablePanelVCC5 = ~0xEF,	/* disable +5V to panel(s) */
+	  DisablePanelVCC3 = ~0xDF,	/* disable +3.3V to panel(s) */
+	  DisableMonoPanel = ~0xBF,	/* disable mono panel signals */
+	  DisableSPISelect = ~0x7F,	/* disable SPI chip select to LVDS panel */
+	ContrastI2C = 0x2E<<1,
+	LEDRegI2C = 0x20<<1,
+	  DisableGreenLED = ~0xFE,
+	  DisableYellowLED = ~0xFD,
+	  DisableRedLED = ~0xFB,
+
+	EnableLCD = IBIT(23),	/* LCD enable bit in i/o port B */
+};
--- /dev/null
+++ b/os/boot/mpc/boot.h
@@ -1,0 +1,8 @@
+#include <u.h>
+#include "lib.h"
+
+#include "mem.h"
+#include "dat.h"
+#include "fns.h"
+#include "io.h"
+#include "ureg.h"
--- /dev/null
+++ b/os/boot/mpc/bootp.c
@@ -1,0 +1,507 @@
+#include "u.h"
+#include "lib.h"
+#include "mem.h"
+#include "dat.h"
+#include "fns.h"
+#include "io.h"
+
+#include "ip.h"
+
+enum {
+	CHECKSUM = 1,	/* set zero if trouble booting from Linux */
+};
+
+uchar broadcast[Eaddrlen] = {
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+};
+
+static ushort tftpport = 5000;
+static int Id = 1;
+static Netaddr myaddr;
+static Netaddr server;
+
+typedef struct {
+	uchar	header[4];
+	uchar	data[Segsize];
+} Tftp;
+static Tftp tftpb;
+
+static void
+hnputs(uchar *ptr, ushort val)
+{
+	ptr[0] = val>>8;
+	ptr[1] = val;
+}
+
+static void
+hnputl(uchar *ptr, ulong val)
+{
+	ptr[0] = val>>24;
+	ptr[1] = val>>16;
+	ptr[2] = val>>8;
+	ptr[3] = val;
+}
+
+static ulong
+nhgetl(uchar *ptr)
+{
+	return ((ptr[0]<<24) | (ptr[1]<<16) | (ptr[2]<<8) | ptr[3]);
+}
+
+static ushort
+nhgets(uchar *ptr)
+{
+	return ((ptr[0]<<8) | ptr[1]);
+}
+
+static	short	endian	= 1;
+static	char*	aendian	= (char*)&endian;
+#define	LITTLE	*aendian
+
+static ushort
+ptcl_csum(void *a, int len)
+{
+	uchar *addr;
+	ulong t1, t2;
+	ulong losum, hisum, mdsum, x;
+
+	addr = a;
+	losum = 0;
+	hisum = 0;
+	mdsum = 0;
+
+	x = 0;
+	if((ulong)addr & 1) {
+		if(len) {
+			hisum += addr[0];
+			len--;
+			addr++;
+		}
+		x = 1;
+	}
+	while(len >= 16) {
+		t1 = *(ushort*)(addr+0);
+		t2 = *(ushort*)(addr+2);	mdsum += t1;
+		t1 = *(ushort*)(addr+4);	mdsum += t2;
+		t2 = *(ushort*)(addr+6);	mdsum += t1;
+		t1 = *(ushort*)(addr+8);	mdsum += t2;
+		t2 = *(ushort*)(addr+10);	mdsum += t1;
+		t1 = *(ushort*)(addr+12);	mdsum += t2;
+		t2 = *(ushort*)(addr+14);	mdsum += t1;
+		mdsum += t2;
+		len -= 16;
+		addr += 16;
+	}
+	while(len >= 2) {
+		mdsum += *(ushort*)addr;
+		len -= 2;
+		addr += 2;
+	}
+	if(x) {
+		if(len)
+			losum += addr[0];
+		if(LITTLE)
+			losum += mdsum;
+		else
+			hisum += mdsum;
+	} else {
+		if(len)
+			hisum += addr[0];
+		if(LITTLE)
+			hisum += mdsum;
+		else
+			losum += mdsum;
+	}
+
+	losum += hisum >> 8;
+	losum += (hisum & 0xff) << 8;
+	while(hisum = losum>>16)
+		losum = hisum + (losum & 0xffff);
+
+	return ~losum;
+}
+
+static ushort
+ip_csum(uchar *addr)
+{
+	int len;
+	ulong sum = 0;
+
+	len = (addr[0]&0xf)<<2;
+
+	while(len > 0) {
+		sum += addr[0]<<8 | addr[1] ;
+		len -= 2;
+		addr += 2;
+	}
+
+	sum = (sum & 0xffff) + (sum >> 16);
+	sum = (sum & 0xffff) + (sum >> 16);
+	return (sum^0xffff);
+}
+
+static void
+udpsend(int ctlrno, Netaddr *a, void *data, int dlen)
+{
+	Udphdr *uh;
+	Etherhdr *ip;
+	static Etherpkt pkt;
+	int len, ptcllen;
+
+
+	uh = (Udphdr*)&pkt;
+
+	memset(uh, 0, sizeof(Etherpkt));
+	memmove(uh->udpcksum+sizeof(uh->udpcksum), data, dlen);
+
+	/*
+	 * UDP portion
+	 */
+	ptcllen = dlen + (UDP_HDRSIZE-UDP_PHDRSIZE);
+	uh->ttl = 0;
+	uh->udpproto = IP_UDPPROTO;
+	uh->frag[0] = 0;
+	uh->frag[1] = 0;
+	hnputs(uh->udpplen, ptcllen);
+	hnputl(uh->udpsrc, myaddr.ip);
+	hnputs(uh->udpsport, myaddr.port);
+	hnputl(uh->udpdst, a->ip);
+	hnputs(uh->udpdport, a->port);
+	hnputs(uh->udplen, ptcllen);
+	uh->udpcksum[0] = 0;
+	uh->udpcksum[1] = 0;
+	/*dlen = (dlen+1)&~1; */
+	hnputs(uh->udpcksum, ptcl_csum(&uh->ttl, dlen+UDP_HDRSIZE));
+
+	/*
+	 * IP portion
+	 */
+	ip = (Etherhdr*)&pkt;
+	len = sizeof(Udphdr)+dlen;
+	ip->vihl = IP_VER|IP_HLEN;
+	ip->tos = 0;
+	ip->ttl = 255;
+	hnputs(ip->length, len-ETHER_HDR);
+	hnputs(ip->id, Id++);
+	ip->frag[0] = 0;
+	ip->frag[1] = 0;
+	ip->cksum[0] = 0;
+	ip->cksum[1] = 0;
+	hnputs(ip->cksum, ip_csum(&ip->vihl));
+
+	/*
+	 * Ethernet MAC portion
+	 */
+	hnputs(ip->type, ET_IP);
+	memmove(ip->d, a->ea, sizeof(ip->d));
+
+	ethertxpkt(ctlrno, &pkt, len, Timeout);
+}
+
+static void
+nak(int ctlrno, Netaddr *a, int code, char *msg, int report)
+{
+	int n;
+	char buf[128];
+
+	buf[0] = 0;
+	buf[1] = Tftp_ERROR;
+	buf[2] = 0;
+	buf[3] = code;
+	strcpy(buf+4, msg);
+	n = strlen(msg) + 4 + 1;
+	udpsend(ctlrno, a, buf, n);
+	if(report)
+		print("\ntftp: error(%d): %s\n", code, msg);
+}
+
+static int
+udprecv(int ctlrno, Netaddr *a, void *data, int dlen)
+{
+	int n, len;
+	ushort csm;
+	Udphdr *h;
+	ulong addr, timo;
+	static Etherpkt pkt;
+	static int rxactive;
+
+	if(rxactive == 0)
+		timo = 1000;
+	else
+		timo = Timeout;
+	timo += TK2MS(m->ticks);
+	while(timo > TK2MS(m->ticks)){
+		n = etherrxpkt(ctlrno, &pkt, timo-TK2MS(m->ticks));
+		if(n <= 0)
+			continue;
+
+		h = (Udphdr*)&pkt;
+		if(nhgets(h->type) != ET_IP)
+			continue;
+
+		if(ip_csum(&h->vihl)) {
+			print("ip chksum error\n");
+			continue;
+		}
+		if(h->vihl != (IP_VER|IP_HLEN)) {
+			print("ip bad vers/hlen\n");
+			continue;
+		}
+
+		if(h->udpproto != IP_UDPPROTO)
+			continue;
+
+		h->ttl = 0;
+		len = nhgets(h->udplen);
+		hnputs(h->udpplen, len);
+
+		if(CHECKSUM && nhgets(h->udpcksum)) {
+			csm = ptcl_csum(&h->ttl, len+UDP_PHDRSIZE);
+			if(csm != 0) {
+				print("udp chksum error csum #%4lux len %d\n", csm, n);
+				break;
+			}
+		}
+
+		if(a->port != 0 && nhgets(h->udpsport) != a->port)
+			continue;
+
+		addr = nhgetl(h->udpsrc);
+		if(a->ip != Bcastip && addr != a->ip)
+			continue;
+
+		len -= UDP_HDRSIZE-UDP_PHDRSIZE;
+		if(len > dlen) {
+			print("udp: packet too big\n");
+			continue;
+		}
+
+		memmove(data, h->udpcksum+sizeof(h->udpcksum), len);
+		a->ip = addr;
+		a->port = nhgets(h->udpsport);
+		memmove(a->ea, pkt.s, sizeof(a->ea));
+
+		rxactive = 1;
+		return len;
+	}
+
+	return 0;
+}
+
+static int tftpblockno;
+
+static int
+tftpopen(int ctlrno, Netaddr *a, char *name, Tftp *tftp)
+{
+	int i, len, rlen, oport;
+	char buf[Segsize+2];
+
+	buf[0] = 0;
+	buf[1] = Tftp_READ;
+	len = sprint(buf+2, "%s", name) + 2;
+	len += sprint(buf+len+1, "octet") + 2;
+
+	oport = a->port;
+	for(i = 0; i < 5; i++){
+		a->port = oport;
+		udpsend(ctlrno, a, buf, len);
+		a->port = 0;
+		if((rlen = udprecv(ctlrno, a, tftp, sizeof(Tftp))) < sizeof(tftp->header))
+			continue;
+
+		switch((tftp->header[0]<<8)|tftp->header[1]){
+
+		case Tftp_ERROR:
+			print("tftpopen: error (%d): %s\n",
+				(tftp->header[2]<<8)|tftp->header[3], tftp->data);
+			return -1;
+
+		case Tftp_DATA:
+			tftpblockno = 1;
+			len = (tftp->header[2]<<8)|tftp->header[3];
+			if(len != tftpblockno){
+				print("tftpopen: block error: %d\n", len);
+				nak(ctlrno, a, 1, "block error", 0);
+				return -1;
+			}
+			return rlen-sizeof(tftp->header);
+		}
+	}
+
+	print("tftpopen: failed to connect to server\n");
+	return -1;
+}
+
+static int
+tftpread(int ctlrno, Netaddr *a, Tftp *tftp, int dlen)
+{
+	int blockno, len, retry;
+	uchar buf[4];
+
+	buf[0] = 0;
+	buf[1] = Tftp_ACK;
+	buf[2] = tftpblockno>>8;
+	buf[3] = tftpblockno;
+	tftpblockno++;
+
+	dlen += sizeof(tftp->header);
+
+	retry = 0;
+buggery:
+	udpsend(ctlrno, a, buf, sizeof(buf));
+
+	if((len = udprecv(ctlrno, a, tftp, dlen)) < dlen){
+		print("tftpread: %d != %d\n", len, dlen);
+		nak(ctlrno, a, 2, "short read", 0);
+		if(retry++ < 5)
+			goto buggery;
+		return -1;
+	}
+
+	blockno = (tftp->header[2]<<8)|tftp->header[3];
+	if(blockno != tftpblockno){
+		print("?");
+
+		if(blockno == tftpblockno-1 && retry++ < 8)
+			goto buggery;
+		print("tftpread: block error: %d, expected %d\n", blockno, tftpblockno);
+		nak(ctlrno, a, 1, "block error", 0);
+
+		return -1;
+	}
+
+	return len-sizeof(tftp->header);
+}
+
+int
+bootp(int ctlrno, char *file)
+{
+	Bootp req, rep;
+	int i, dlen, segsize, text, data, bss, total;
+	uchar *ea, *addr, *p;
+	ulong entry;
+	Exec *exec;
+	char name[128], *filename, *sysname;
+
+	if((ea = etheraddr(ctlrno)) == 0){
+		print("invalid ctlrno %d\n", ctlrno);
+		return -1;
+	}
+
+	filename = 0;
+	sysname = 0;
+	if(file && *file){
+		strcpy(name, file);
+		if(filename = strchr(name, ':')){
+			if(filename != name && *(filename-1) != '\\'){
+				sysname = name;
+				*filename++ = 0;
+			}
+		}
+		else
+			filename = name;
+	}
+		
+
+	memset(&req, 0, sizeof(req));
+	req.op = Bootrequest;
+	req.htype = 1;			/* ethernet */
+	req.hlen = Eaddrlen;		/* ethernet */
+	memmove(req.chaddr, ea, Eaddrlen);
+
+	myaddr.ip = 0;
+	myaddr.port = BPportsrc;
+	memmove(myaddr.ea, ea, Eaddrlen);
+
+	for(i = 0; i < 10; i++) {
+		server.ip = Bcastip;
+		server.port = BPportdst;
+		memmove(server.ea, broadcast, sizeof(server.ea));
+		udpsend(ctlrno, &server, &req, sizeof(req));
+		if(udprecv(ctlrno, &server, &rep, sizeof(rep)) <= 0)
+			continue;
+		if(memcmp(req.chaddr, rep.chaddr, Eaddrlen))
+			continue;
+		if(rep.htype != 1 || rep.hlen != Eaddrlen)
+			continue;
+		if(sysname == 0 || strcmp(sysname, rep.sname) == 0)
+			break;
+	}
+	if(i >= 10) {
+		print("bootp timed out\n");
+		return -1;
+	}
+
+	if(filename == 0 || *filename == 0)
+		filename = rep.file;
+
+	if(rep.sname[0] != '\0')
+		 print("%s ", rep.sname);
+	print("(%d.%d.%d.%d!%d): %s\n",
+		rep.siaddr[0],
+		rep.siaddr[1],
+		rep.siaddr[2],
+		rep.siaddr[3],
+		server.port,
+		filename);uartwait();
+
+	myaddr.ip = nhgetl(rep.yiaddr);
+	myaddr.port = tftpport++;
+	server.ip = nhgetl(rep.siaddr);
+	server.port = TFTPport;
+
+	if((dlen = tftpopen(ctlrno, &server, filename, &tftpb)) < 0)
+		return -1;
+	exec = (Exec*)(tftpb.data);
+	if(dlen < sizeof(Exec) || GLLONG(exec->magic) != Q_MAGIC){
+		nak(ctlrno, &server, 0, "bad magic number", 1);
+		return -1;
+	}
+	text = GLLONG(exec->text);
+	data = GLLONG(exec->data);
+	bss = GLLONG(exec->bss);
+	total = text+data+bss;
+	entry = GLLONG(exec->entry);
+print("load@%8.8lux: ", PADDR(entry));uartwait();
+	print("%d", text);
+
+	addr = (uchar*)PADDR(entry);
+	p = tftpb.data+sizeof(Exec);
+	dlen -= sizeof(Exec);
+	segsize = text;
+	for(;;){
+		if(dlen == 0){
+			if((dlen = tftpread(ctlrno, &server, &tftpb, sizeof(tftpb.data))) < 0)
+				return -1;
+			p = tftpb.data;
+		}
+		if(segsize <= dlen)
+			i = segsize;
+		else
+			i = dlen;
+		memmove(addr, p, i);
+
+		addr += i;
+		p += i;
+		segsize -= i;
+		dlen -= i;
+
+		if(segsize <= 0){
+			if(data == 0)
+				break;
+			print("+%d", data);
+			segsize = data;
+			data = 0;
+			addr = (uchar*)PGROUND((ulong)addr);
+		}
+	}
+	nak(ctlrno, &server, 3, "ok", 0);		/* tftpclose */
+	print("+%d=%d\n", bss, total);
+	print("entry: 0x%lux\n", entry);
+	uartwait();
+	scc2stop();
+	splhi();
+	(*(void(*)(void))(PADDR(entry)))();
+	
+	return 0;
+}
--- /dev/null
+++ b/os/boot/mpc/clock.c
@@ -1,0 +1,71 @@
+#include	"u.h"
+#include	"lib.h"
+#include	"mem.h"
+#include	"dat.h"
+#include	"fns.h"
+#include	"io.h"
+#include	"ureg.h"
+
+enum {
+	Timebase = 4,	/* system clock cycles per time base cycle */
+};
+
+void	(*archclocktick)(void);	/* set by arch*.c when desired */
+
+static	ulong	clkreload;
+
+void
+delay(int l)
+{
+	ulong i, j;
+
+	j = m->delayloop;
+	while(l-- > 0)
+		for(i=0; i < j; i++)
+			;
+}
+
+void
+microdelay(int l)
+{
+	ulong i;
+
+	l *= m->delayloop;
+	l /= 1000;
+	if(l <= 0)
+		l = 1;
+	for(i = 0; i < l; i++)
+		;
+}
+
+void
+clockintr(Ureg*, void*)
+{
+	putdec(clkreload);
+	m->ticks++;
+	checkalarms();
+	if(archclocktick != nil)
+		archclocktick();
+}
+
+void
+clockinit(void)
+{
+	long x;
+
+	m->delayloop = m->cpuhz/1000;	/* initial estimate */
+	do {
+		x = gettbl();
+		delay(10);
+		x = gettbl() - x;
+	} while(x < 0);
+
+	/*
+	 *  fix count
+	 */
+	m->delayloop = ((vlong)m->delayloop*(10*m->clockgen/1000))/(x*Timebase);
+	if(m->delayloop == 0)
+		m->delayloop = 1;
+	clkreload = (m->clockgen/Timebase)/HZ-1;
+	putdec(clkreload);
+}
--- /dev/null
+++ b/os/boot/mpc/conf.c
@@ -1,0 +1,173 @@
+#include "u.h"
+#include "lib.h"
+#include "mem.h"
+#include "dat.h"
+#include "fns.h"
+#include "io.h"
+
+#include "dosfs.h"
+
+static char *confname[MAXCONF];
+static char *confval[MAXCONF];
+static int nconf;
+
+extern char **ini;
+
+char*
+getconf(char *name)
+{
+	int i;
+
+	for(i = 0; i < nconf; i++)
+		if(strcmp(confname[i], name) == 0)
+			return confval[i];
+	return 0;
+}
+
+/*
+ *  read configuration file
+ */
+int
+plan9ini(Dos *dos, char *val)
+{
+	Dosfile rc;
+	int i, n;
+	char *cp, *p, *q, *line[MAXCONF];
+
+	cp = BOOTARGS;
+	if(dos) {
+		if(dosstat(dos, *ini, &rc) <= 0)
+			return -1;
+
+		*cp = 0;
+		n = dosread(&rc, cp, BOOTARGSLEN-1);
+		if(n <= 0)
+			return -1;
+		cp[n] = 0;
+	} else if(val != nil){
+		if(memchr(val, 0, BOOTARGSLEN-1) == nil)
+			return -1;
+		print("Using flash configuration\n");
+		strcpy(cp, val);
+		n = strlen(cp);
+	}else{
+		strcpy(cp, archconfig());
+		n = strlen(cp);
+	}
+
+	/*
+	 * Make a working copy.
+	 * We could change this to pass the parsed strings
+	 * to the booted programme instead of the raw
+	 * string, then it only gets done once.
+	 */
+	memmove(cp+BOOTARGSLEN, cp, n+1);
+	cp += BOOTARGSLEN;
+
+	/*
+	 * Strip out '\r', change '\t' -> ' '.
+	 */
+	p = cp;
+	for(q = cp; *q; q++){
+		if(*q == '\r')
+			continue;
+		if(*q == '\t')
+			*q = ' ';
+		*p++ = *q;
+	}
+	*p = 0;
+	n = getcfields(cp, line, MAXCONF, "\n");
+	for(i = 0; i < n; i++){
+		cp = strchr(line[i], '=');
+		if(cp == 0)
+			continue;
+		*cp++ = 0;
+		if(cp - line[i] >= NAMELEN+1)
+			*(line[i]+NAMELEN-1) = 0;
+		confname[nconf] = line[i];
+		confval[nconf] = cp;
+		nconf++;
+	}
+	return 0;
+}
+
+int
+parseether(uchar *to, char *from)
+{
+	char nip[4];
+	char *p;
+	int i;
+
+	p = from;
+	while(*p == ' ')
+		++p;
+	for(i = 0; i < 6; i++){
+		if(*p == 0)
+			return -1;
+		nip[0] = *p++;
+		if(*p == 0)
+			return -1;
+		nip[1] = *p++;
+		nip[2] = 0;
+		to[i] = strtoul(nip, 0, 16);
+		if(*p == ':')
+			p++;
+	}
+	return 0;
+}
+
+int
+isaconfig(char *class, int ctlrno, ISAConf *isa)
+{
+	char cc[NAMELEN], *p, *q, *r;
+	int n;
+
+	sprint(cc, "%s%d", class, ctlrno);
+	for(n = 0; n < nconf; n++){
+		if(strncmp(confname[n], cc, NAMELEN))
+			continue;
+		isa->nopt = 0;
+		p = confval[n];
+		while(*p){
+			while(*p == ' ' || *p == '\t')
+				p++;
+			if(*p == '\0')
+				break;
+			if(strncmp(p, "type=", 5) == 0){
+				p += 5;
+				for(q = isa->type; q < &isa->type[NAMELEN-1]; q++){
+					if(*p == '\0' || *p == ' ' || *p == '\t')
+						break;
+					*q = *p++;
+				}
+				*q = '\0';
+			}
+			else if(strncmp(p, "port=", 5) == 0)
+				isa->port = strtoul(p+5, &p, 0);
+			else if(strncmp(p, "irq=", 4) == 0)
+				isa->irq = strtoul(p+4, &p, 0);
+			else if(strncmp(p, "mem=", 4) == 0)
+				isa->mem = strtoul(p+4, &p, 0);
+			else if(strncmp(p, "size=", 5) == 0)
+				isa->size = strtoul(p+5, &p, 0);
+			else if(strncmp(p, "ea=", 3) == 0){
+				if(parseether(isa->ea, p+3) == -1)
+					memset(isa->ea, 0, 6);
+			}
+			else if(isa->nopt < NISAOPT){
+				r = isa->opt[isa->nopt];
+				while(*p && *p != ' ' && *p != '\t'){
+					*r++ = *p++;
+					if(r-isa->opt[isa->nopt] >= ISAOPTLEN-1)
+						break;
+				}
+				*r = '\0';
+				isa->nopt++;
+			}
+			while(*p && *p != ' ' && *p != '\t')
+				p++;
+		}
+		return 1;
+	}
+	return 0;
+}
--- /dev/null
+++ b/os/boot/mpc/console.c
@@ -1,0 +1,173 @@
+#include "u.h"
+#include "lib.h"
+#include "mem.h"
+#include "dat.h"
+#include "fns.h"
+#include "io.h"
+
+static Queue*	consiq;
+static Queue*	consoq;
+
+void
+bothputs(char *s, int n)
+{
+	uartputs(s, n);
+	screenputs(s, n);
+}
+
+static void (*consputs)(char*, int) = bothputs;	/* or screenputs */
+
+void
+consinit(void)
+{
+	char *p;
+	int baud, port;
+	static int cgadone;
+
+	p = getconf("console");
+	if(0)
+	if(p == 0 || strcmp(p, "lcd") == 0 || strcmp(p, "screen") == 0){
+		consiq = qopen(4*1024, 0, 0, 0);
+		consoq = qopen(8*1024, 0, 0, 0);
+		consputs = screenputs;
+		return;
+	}
+	if(p!=0 && strstr(p, "lcd") == 0)
+		consputs = bothputs;
+	else
+		consputs = uartputs;
+//consputs = screenputs;
+	port = 0;
+	if(p)
+		port = strtoul(p, 0, 0);
+	baud = 0;
+	if(p = getconf("baud"))
+		baud = strtoul(p, 0, 0);
+	if(baud == 0)
+		baud = 9600;
+	uartspecial(port, baud, &consiq, &consoq, kbdchar);
+}
+
+void
+kbdchar(Queue *q, int c)
+{
+	c &= 0x7F;
+	if(c == 0x10)
+		panic("^p");
+	qbputc(q, c);
+}
+
+static int
+getline(char *buf, int size, int dotimeout)
+{
+	int c, i=0;
+	ulong start;
+	char echo;
+
+	for (;;) {
+		start = m->ticks;
+		do{
+			if(dotimeout && ((m->ticks - start) > 5*HZ))
+				return -2;
+			c = qbgetc(consiq);
+		}while(c == -1);
+		if(c == '\r')
+			c = '\n'; 		/* turn carriage return into newline */
+		if(c == '\177')
+			c = '\010';		/* turn delete into backspace */
+		if(c == '\025')
+			echo = '\n';		/* echo ^U as a newline */
+		else
+			echo = c;
+		(*consputs)(&echo, 1);
+
+		if(c == '\010'){
+			if(i > 0)
+				i--; /* bs deletes last character */
+			continue;
+		}
+		/* a newline ends a line */
+		if (c == '\n')
+			break;
+		/* ^U wipes out the line */
+		if (c =='\025')
+			return -1;
+		if(i == size)
+			return size;
+		buf[i++] = c;
+	}
+	buf[i] = 0;
+	return i;
+}
+
+int
+getstr(char *prompt, char *buf, int size, char *def)
+{
+	int len, isdefault;
+
+	buf[0] = 0;
+	isdefault = (def && *def);
+	for (;;) {
+		if(isdefault)
+			print("%s[default==%s]: ", prompt, def);
+		else
+			print("%s: ", prompt);
+		len = getline(buf, size, isdefault);
+		switch(len){
+		case -1:
+			/* ^U typed */
+			continue;
+		case -2:
+			/* timeout, use default */
+			(*consputs)("\n", 1);
+			len = 0;
+			break;
+		default:
+			break;
+		}
+		if(len >= size){
+			print("line too long\n");
+			continue;
+		}
+		break;
+	}
+	if(len == 0 && isdefault)
+		strcpy(buf, def);
+	return 0;
+}
+
+int
+sprint(char *s, char *fmt, ...)
+{
+	return donprint(s, s+PRINTSIZE, fmt, (&fmt+1)) - s;
+}
+
+int
+print(char *fmt, ...)
+{
+	char buf[PRINTSIZE];
+	int n;
+
+	if(consputs == 0)
+		return 0;
+	n = donprint(buf, buf+sizeof(buf), fmt, (&fmt+1)) - buf;
+	(*consputs)(buf, n);
+	return n;
+}
+
+void
+panic(char *fmt, ...)
+{
+	char buf[PRINTSIZE];
+	int n;
+
+	if(consputs){
+		(*consputs)("panic: ", 7);
+		n = donprint(buf, buf+sizeof(buf), fmt, (&fmt+1)) - buf;
+		(*consputs)(buf, n);
+		(*consputs)("\n", 1);
+	}
+	spllo();
+	for(;;)
+		idle();
+}
--- /dev/null
+++ b/os/boot/mpc/cpm.c
@@ -1,0 +1,162 @@
+#include "u.h"
+#include "lib.h"
+#include "mem.h"
+#include "dat.h"
+#include "fns.h"
+#include "io.h"
+
+enum {
+	BDSIZE=	1024,	/* TO DO: check this */
+};
+
+static	Map	bdmapv[BDSIZE/sizeof(BD)];
+static	RMap	bdmap = {"buffer descriptors"};
+
+void
+cpminit(void)
+{
+	IMM *io;
+
+	io = m->iomem;
+	io->sdcr = 1;
+	io->lccr &= ~1;	/* disable LCD */
+	io->pcint = 0;	/* disable all port C interrupts */
+	io->pcso = 0;
+	io->pcdir =0;
+	io->pcpar = 0;
+	io->pcdat = 0;
+	io->papar = 0;
+	io->padir = 0;
+	io->paodr = 0;
+	io->padat = 0;
+	io->pbpar = 0;
+	io->pbdir = 0;
+	io->pbodr = 0;
+	io->pbdat = 0;
+	eieio();
+
+	for(io->cpcr = 0x8001; io->cpcr & 1;)	/* reset all CPM channels */
+		eieio();
+
+	mapinit(&bdmap, bdmapv, sizeof(bdmapv));
+	mapfree(&bdmap, DPBASE, BDSIZE);
+}
+
+void
+cpmop(int op, int cno, int param)
+{
+	IMM *io;
+	int s;
+
+	s = splhi();
+	io = m->iomem;
+	eieio();
+	while(io->cpcr & 1)
+		eieio();
+	io->cpcr = (op<<8)|(cno<<4)|(param<<1)|1;
+	eieio();
+	while(io->cpcr & 1)
+		eieio();
+	splx(s);
+}
+
+/*
+ * connect SCCx clocks in NSMI mode (x=1 for USB)
+ */
+void
+sccnmsi(int x, int rcs, int tcs)
+{
+	IMM *io;
+	ulong v;
+	int sh;
+
+	sh = (x-1)*8;	/* each SCCx field in sicr is 8 bits */
+	v = (((rcs&7)<<3) | (tcs&7)) << sh;
+	io = ioplock();
+	io->sicr = (io->sicr & ~(0xFF<<sh)) | v;
+	iopunlock();
+}
+
+void
+scc2stop(void)
+{
+	SCC *scc;
+
+	scc = IOREGS(0xA20, SCC);
+	if(scc->gsmrl & (3<<4)){
+		cpmop(GracefulStopTx, SCC2ID, 0);
+		cpmop(CloseRxBD, SCC2ID, 0);
+		delay(1);
+		scc->gsmrl &= ~(3<<4);	/* disable current use */
+		archetherdisable(SCC2ID);
+	}
+}
+
+BD *
+bdalloc(int n)
+{
+	ulong a;
+
+	a = mapalloc(&bdmap, 0, n*sizeof(BD), 0);
+	if(a == 0)
+		panic("bdalloc");
+	return KADDR(a);
+}
+
+void
+bdfree(BD *b, int n)
+{
+	if(b){
+		eieio();
+		mapfree(&bdmap, PADDR(b), n*sizeof(BD));
+	}
+}
+
+/*
+ * initialise receive and transmit buffer rings.
+ */
+int
+ioringinit(Ring* r, int nrdre, int ntdre, int bufsize)
+{
+	int i, x;
+
+	/* the ring entries must be aligned on sizeof(BD) boundaries */
+	r->nrdre = nrdre;
+	if(r->rdr == nil)
+		r->rdr = bdalloc(nrdre);
+	/* the buffer size must align with cache lines since the cache doesn't snoop */
+	bufsize = (bufsize+CACHELINESZ-1)&~(CACHELINESZ-1);
+	if(r->rrb == nil)
+		r->rrb = malloc(nrdre*bufsize);
+	if(r->rdr == nil || r->rrb == nil)
+		return -1;
+	dcflush(r->rrb, nrdre*bufsize);
+	x = PADDR(r->rrb);
+	for(i = 0; i < nrdre; i++){
+		r->rdr[i].length = 0;
+		r->rdr[i].addr = x;
+		r->rdr[i].status = BDEmpty|BDInt;
+		x += bufsize;
+	}
+	r->rdr[i-1].status |= BDWrap;
+	r->rdrx = 0;
+
+	r->ntdre = ntdre;
+	if(r->tdr == nil)
+		r->tdr = bdalloc(ntdre);
+	if(r->txb == nil)
+		r->txb = malloc(ntdre*sizeof(Block*));
+	if(r->tdr == nil || r->txb == nil)
+		return -1;
+	for(i = 0; i < ntdre; i++){
+		r->txb[i] = nil;
+		r->tdr[i].addr = 0;
+		r->tdr[i].length = 0;
+		r->tdr[i].status = 0;
+	}
+	r->tdr[i-1].status |= BDWrap;
+	r->tdrh = 0;
+	r->tdri = 0;
+	r->ntq = 0;
+	return 0;
+}
--- /dev/null
+++ b/os/boot/mpc/crc32.c
@@ -1,0 +1,42 @@
+#include "boot.h"
+
+/*
+ * from Rob Warnock
+ */
+static	ulong	crc32tab[256];	/* initialised on first call to crc32 */
+
+enum {
+	CRC32POLY = 0x04c11db7     /* AUTODIN II, Ethernet, & FDDI */
+};
+
+/*
+ * Build auxiliary table for parallel byte-at-a-time CRC-32.
+ */
+static void
+initcrc32(void)
+{
+	int i, j;
+	ulong c;
+
+	for(i = 0; i < 256; i++) {
+		for(c = i << 24, j = 8; j > 0; j--)
+			if(c & (1<<31))
+				c = (c<<1) ^ CRC32POLY;
+			else
+				c <<= 1;
+		crc32tab[i] = c;
+	}
+}
+
+ulong
+crc32(void *buf, int n, ulong crc)
+{
+	uchar *p;
+
+	if(crc32tab[1] == 0)
+		initcrc32();
+	crc = ~crc;
+	for(p = buf; --n >= 0;)
+		crc = (crc << 8) ^ crc32tab[(crc >> 24) ^ *p++];
+	return ~crc;
+}
--- /dev/null
+++ b/os/boot/mpc/dat.h
@@ -1,0 +1,217 @@
+typedef struct Alarm Alarm;
+typedef struct Block Block;
+typedef struct IMM IMM;
+typedef struct Queue Queue;
+
+typedef struct List {
+	void	*next;
+} List;
+
+typedef struct {
+	int	fake;
+	int	pri;
+} Lock;
+#define	lock(x)
+#define	unlock(x)
+
+struct Alarm {
+	List;
+	int	busy;
+	long	dt;
+	void	(*f)(Alarm*);
+	void	*arg;
+};
+
+enum {
+	Eaddrlen	= 6,
+	ETHERMINTU	= 60,		/* minimum transmit size */
+	ETHERMAXTU	= 1514,		/* maximum transmit size */
+	ETHERHDRSIZE	= 14,		/* size of an ethernet header */
+
+	MaxEther	= 4,
+};
+
+typedef struct {
+	uchar	d[Eaddrlen];
+	uchar	s[Eaddrlen];
+	uchar	type[2];
+	uchar	data[1500];
+	uchar	crc[4];
+} Etherpkt;
+
+extern uchar broadcast[Eaddrlen];
+
+enum {
+	Npart		= 20+2,		/* 8 sub partitions, disk, and partition */
+	Maxxfer		= 16*1024,	/* maximum transfer size/cmd */
+};
+
+typedef struct {
+	ulong	start;
+	ulong	end;
+	char	name[NAMELEN+1];
+} Partition;
+
+typedef struct {
+	int	online;
+	int	npart;		/* number of real partitions */
+	Partition p[Npart];
+	ulong	offset;
+	Partition *current;	/* current partition */
+
+	ulong	cap;		/* total bytes */
+	int	bytes;		/* bytes/sector */
+	int	sectors;	/* sectors/track */
+	int	heads;		/* heads/cyl */
+	long	cyl;		/* cylinders/drive */
+
+	char	lba;		/* true if drive has logical block addressing */
+	char	multi;		/* non-zero if drive does multiple block xfers */
+} Disc;
+
+enum {
+	ScsiTestunit	= 0x00,
+	ScsiExtsens	= 0x03,
+	ScsiInquiry	= 0x12,
+	ScsiModesense	= 0x1a,
+	ScsiStartunit	= 0x1B,
+	ScsiStopunit	= 0x1B,
+	ScsiGetcap	= 0x25,
+	ScsiRead	= 0x08,
+	ScsiWrite	= 0x0a,
+	ScsiExtread	= 0x28,
+	ScsiExtwrite	= 0x2a,
+
+	/* data direction */
+	ScsiIn		= 1,
+	ScsiOut		= 0,
+};
+
+typedef struct Scsibuf Scsibuf;
+typedef struct Scsibuf {
+	void*		virt;
+	void*		phys;
+	Scsibuf*	next;
+};
+
+typedef struct Scsidata {
+	uchar*		base;
+	uchar*		lim;
+	uchar*		ptr;
+} Scsidata;
+
+typedef struct Ureg Ureg;
+
+typedef struct Scsi {
+	ulong		pid;
+	ushort		target;
+	ushort		lun;
+	ushort		rflag;
+	ushort		status;
+	Scsidata 	cmd;
+	Scsidata 	data;
+	Scsibuf*	b;
+	uchar*		save;
+	uchar		cmdblk[16];
+} Scsi;
+
+typedef struct Segdesc {
+	ulong	d0;
+	ulong	d1;
+} Segdesc;
+
+typedef struct Mach {
+	ulong	ticks;			/* of the clock since boot time */
+	ulong	delayloop;
+	long	cpuhz;	/* general system clock (cycles) */
+	long	clockgen;	/* clock generator frequency (cycles) */
+	ulong	cpupvr;	/* cpu type in processor version register */
+	ulong	cputype;	/* cpu variant in BCD (eg, 0x823xx) */
+	void*	alarm;			/* alarms bound to this clock */
+	ulong*	bcsr;
+	IMM*	iomem;
+} Mach;
+
+/* Mach.cputype */
+#define MPCREV(x)	((x) & 0xFF)
+#define MPCMODEL(x)	(((x)>>8) & 0xFFF)
+#define MPCFAMILY(x)	(((x)>>24) & 0x0F)
+
+
+extern Mach *m;
+
+#define Q_MAGIC		((((4*21)+0)*21)+7)
+
+typedef struct Exec Exec;
+struct	Exec
+{
+	uchar	magic[4];		/* magic number */
+	uchar	text[4];	 	/* size of text segment */
+	uchar	data[4];	 	/* size of initialized data */
+	uchar	bss[4];	  		/* size of uninitialized data */
+	uchar	syms[4];	 	/* size of symbol table */
+	uchar	entry[4];	 	/* entry point */
+	uchar	spsz[4];		/* size of sp/pc offset table */
+	uchar	pcsz[4];		/* size of pc/line number table */
+};
+
+/*
+ *  bootline passed by boot program
+ */
+#define BOOTLINE ((char *)0x200000-150)
+
+/*
+ * Where we leave configuration info.
+ */
+#define BOOTARGS	((char*)(0x200000))
+#define	BOOTARGSLEN	1024
+#define	MAXCONF		32
+
+/*
+ *  a parsed plan9.ini line
+ */
+#define ISAOPTLEN	16
+#define NISAOPT		8
+
+typedef struct  ISAConf {
+	char	type[NAMELEN];
+	ulong	port;
+	ulong	irq;
+	ulong	mem;
+	ulong	size;
+	uchar	ea[6];
+
+	int	nopt;
+	char	opt[NISAOPT][ISAOPTLEN];
+} ISAConf;
+
+typedef struct {
+	int	size;
+	ulong	addr;
+} Map;
+
+typedef struct {
+	char*	name;
+	Map*	map;
+	Map*	mapend;
+
+	Lock;
+} RMap;
+
+typedef struct PCIcfg PCIcfg;
+
+extern	uchar*	vgamem;
+
+struct Block {
+	uchar	*rp;
+	uchar	*wp;
+	uchar	*lim;
+	uchar	*data;
+	Block*	next;
+	ulong	magic;
+};
+#define	BLEN(b)	((b)->wp-(b)->rp)
+
+typedef struct QLock {
+	int	dummy;
+} QLock;
--- /dev/null
+++ b/os/boot/mpc/defont0.c
@@ -1,0 +1,216 @@
+#include <u.h>
+#include <libc.h>
+#include <libg.h>
+#include <gnot.h>
+
+
+
+static ulong bits0[] = {
+ 0x907070f0,  0xf0f07000,  0xf0888888,  0xf8707070,  0xe0e0e0e0,  0xe09070f0,  0x70f870f0,  0xf870f088, 
+ 0x00000000,  0x00000000,  0x00000000,  0x00000000,  0x00000000,  0x00000000,  0x00000000,  0x00000000, 
+ 0x00000000,  0x00000000,  0x00000000,  0x00000000,  0x00000000,  0x00000000,  0x00000000,  0x00000000, 
+ 0x00000000,  0x00000000,  0x00000000,  0x00000000,  0x00000000,  0x00000000,  0x00000000,  0x000000e0, 
+ 0xd0808080,  0x80808800,  0x8888c888,  0x80888888,  0x90909090,  0x90d08080,  0x80808080,  0x80888888, 
+ 0x00000000,  0x08000000,  0x0c300000,  0x00000006,  0x00000000,  0x00000000,  0x00000000,  0x00000000, 
+ 0x00000000,  0x00000000,  0x00000000,  0x00000000,  0x00000000,  0x00000000,  0x0000003c,  0xc03c0000, 
+ 0x00006000,  0x06001e00,  0x60181860,  0x78000000,  0x00000000,  0x00000000,  0x0000001c,  0x18380090, 
+ 0xb06060e0,  0xe0e0f800,  0xf088a888,  0x80808080,  0x90909090,  0x90b060e0,  0x808060e0,  0x80808888, 
+ 0x00182428,  0x3e707018,  0x18180000,  0x00000006,  0x3c183c3c,  0x1c3e3c7e,  0x3c3c0000,  0x0200403c, 
+ 0x3c187c1e,  0x787e7e1e,  0x663c7c66,  0x6066623c,  0x7c3c7c3c,  0x7e6266c2,  0x66667e30,  0xc00c1000, 
+ 0x08006000,  0x06003000,  0x60181860,  0x18000000,  0x00000000,  0x10000000,  0x00000030,  0x180c0090, 
+ 0x90101080,  0x80808818,  0x88f8a888,  0xe0807070,  0xe0e0e0e0,  0xe0901080,  0x70e01080,  0xe098f088, 
+ 0x00182428,  0x6adad818,  0x18181000,  0x0000000c,  0x66386666,  0x2c3e667e,  0x66660000,  0x06006066, 
+ 0x42186632,  0x6c606032,  0x66181864,  0x60667224,  0x66246666,  0x186262da,  0x62620630,  0x600c3800, 
+ 0x10006000,  0x06003000,  0x60000060,  0x18000000,  0x00000000,  0x30000000,  0x00000030,  0x180c00e0, 
+ 0x00e0e0f0,  0xf0f00018,  0x88889850,  0x80880808,  0x201c1c1c,  0x1c00e0f0,  0x0080e0f0,  0x80888888, 
+ 0x00182428,  0x68dad808,  0x300c5418,  0x0000000c,  0x66580606,  0x2c206002,  0x66661818,  0x0cfe3006, 
+ 0x9e2c6660,  0x66606060,  0x6618186c,  0x60667266,  0x66666660,  0x186262da,  0x36660c30,  0x600c2800, 
+ 0x103c6c3c,  0x3e3c7e3e,  0x6c787866,  0x18d46c3c,  0x6c3e763c,  0x7e6666c2,  0x66667e18,  0x18180000, 
+ 0x44180000,  0x18241c24,  0xf0888820,  0x8070f0f0,  0x20202020,  0x201c243e,  0x1cf8241c,  0x80708870, 
+ 0x0018247c,  0x78745008,  0x300c3818,  0x00000018,  0x66180606,  0x4c206006,  0x76661818,  0x18fe180c, 
+ 0xb62c6660,  0x66606060,  0x66181868,  0x607e5a66,  0x66666470,  0x186266da,  0x34340c30,  0x300c6c00, 
+ 0x18667666,  0x66663066,  0x76181864,  0x18fe7666,  0x76663666,  0x306662da,  0x62620608,  0x1810323c, 
+ 0x44247c7c,  0x24342042,  0x00000000,  0x00000000,  0x20202020,  0x20222408,  0x22002420,  0x00000000, 
+ 0x00180028,  0x3c287610,  0x300cee7e,  0x00fe0018,  0x66180c18,  0x4c3c7c0c,  0x3c3e0000,  0x30000c18, 
+ 0xb62c7c60,  0x667c7c6e,  0x7e181878,  0x605a5a66,  0x6466783c,  0x186234da,  0x18341830,  0x300c4400, 
+ 0x18066660,  0x66663066,  0x66181868,  0x18d66666,  0x66663860,  0x306662da,  0x34620c30,  0x180c5a20, 
+ 0x44241010,  0x242c2042,  0x0e3e103e,  0x3e3c1c3e,  0x3c1c1c1c,  0x1c3e1c08,  0x3e222418,  0x0e0e0e0e, 
+ 0x0008007c,  0x1e5cdc00,  0x300c387e,  0x00fe0030,  0x66181806,  0x7e066618,  0x6e060000,  0x18001818, 
+ 0xb67e6660,  0x66606066,  0x6618186c,  0x605a4e66,  0x78666c0e,  0x1862346c,  0x2c183030,  0x180c4400, 
+ 0x003e6660,  0x667e3066,  0x66181878,  0x18d66666,  0x6666303c,  0x306634da,  0x18341808,  0x18104c38, 
+ 0x3c181010,  0x18241c42,  0x11081008,  0x20222208,  0x00000000,  0x00220408,  0x22361804,  0x11111111, 
+ 0x00000028,  0x16b6cc00,  0x300c5418,  0x00000030,  0x66183006,  0x7e066618,  0x66060000,  0x0cfe3000, 
+ 0x9a466660,  0x66606066,  0x6618186c,  0x605a4e66,  0x60666606,  0x1862346c,  0x6c183030,  0x180c0000, 
+ 0x00666660,  0x66603066,  0x6618186c,  0x18d66666,  0x66663006,  0x3066346c,  0x2c343018,  0x18180020, 
+ 0x00091010,  0x000e0942,  0x10081008,  0x20222208,  0x0f06060f,  0x0a09041e,  0x002a0e38,  0x10101010, 
+ 0x00180028,  0x56b6cc00,  0x300c1018,  0x18001860,  0x66187e66,  0x0c666630,  0x66661818,  0x06fe6018, 
+ 0x40466632,  0x6c606036,  0x66181866,  0x605a4624,  0x60246666,  0x1834186c,  0x46186030,  0x0c0c0000, 
+ 0x006e6666,  0x6e66306e,  0x66181866,  0x18d66666,  0x666e3066,  0x306e186c,  0x46186030,  0x180c003c, 
+ 0x08090909,  0x1f110aff,  0x0e081008,  0x382c2208,  0x08020901,  0x0a0a0911,  0x09220907,  0x0e0e0e0e, 
+ 0x00180028,  0x7c1c7600,  0x18180000,  0x18001860,  0x3c7e7e3c,  0x0c3c3c30,  0x3c3c1818,  0x02004018, 
+ 0x3e467c1e,  0x787e601e,  0x663c1866,  0x7e42463c,  0x603c663c,  0x1818186c,  0x66187e30,  0x0c0c0000, 
+ 0x00367c3c,  0x363c7c36,  0x667e1866,  0x7ed6663c,  0x7c367c3c,  0x1e36186c,  0x66187e30,  0x180c0008, 
+ 0x080f0606,  0x04110c18,  0x01081008,  0x20222208,  0x0e020203,  0x0a0c0d1e,  0x0d220e08,  0x01010101, 
+ 0x00000000,  0x10000000,  0x18180000,  0x080000c0,  0x00000000,  0x00000000,  0x00000008,  0x00000000, 
+ 0x00000000,  0x00000000,  0x00001800,  0x00000000,  0x000c0000,  0x00000000,  0x00000030,  0x060c00fe, 
+ 0x00000000,  0x00000006,  0x00001800,  0x00000000,  0x60060000,  0x00000000,  0x0010001c,  0x18380008, 
+ 0x08090606,  0x040e0a18,  0x11081f08,  0x20221c3e,  0x08020401,  0x0f0a0b11,  0x0b220908,  0x11111111, 
+ 0x00000000,  0x00000000,  0x0c300000,  0x080000c0,  0x00000000,  0x00000000,  0x00000008,  0x00000000, 
+ 0x00000000,  0x00000000,  0x00007000,  0x00000000,  0x00060000,  0x00000000,  0x0000003c,  0x063c0000, 
+ 0x00000000,  0x00000066,  0x00001800,  0x00000000,  0x60060000,  0x00000000,  0x00300000,  0x00000008, 
+ 0x0f090909,  0x04030900,  0x0e000000,  0x00000000,  0x0f0f0f0f,  0x0209091e,  0x09000f07,  0x0e0e0e0e, 
+ 0x00000000,  0x00000000,  0x00000000,  0x10000000,  0x00000000,  0x00000000,  0x00000010,  0x00000000, 
+ 0x00000000,  0x00000000,  0x00000000,  0x00000000,  0x00000000,  0x00000000,  0x00000000,  0x00000000, 
+ 0x00000000,  0x0000003c,  0x00007000,  0x00000000,  0x60060000,  0x00000000,  0x00600000,  0x0000000f, 
+
+};
+
+static GBitmap strike0 = {
+	bits0,
+	0,
+	32,
+	0,
+	{0, 0, 1024, 14},
+	{0, 0, 1024, 14},
+};
+
+static Fontchar info0[] = {
+	{ 0, 0, 14, 0, 8 },
+	{ 8, 0, 14, 0, 8 },
+	{ 16, 0, 14, 0, 8 },
+	{ 24, 0, 14, 0, 8 },
+	{ 32, 0, 14, 0, 8 },
+	{ 40, 0, 14, 0, 8 },
+	{ 48, 0, 14, 0, 8 },
+	{ 56, 0, 14, 0, 8 },
+	{ 64, 0, 14, 0, 8 },
+	{ 72, 0, 14, 0, 8 },
+	{ 80, 0, 14, 0, 8 },
+	{ 88, 0, 14, 0, 8 },
+	{ 96, 0, 14, 0, 8 },
+	{ 104, 0, 14, 0, 8 },
+	{ 112, 0, 14, 0, 8 },
+	{ 120, 0, 14, 0, 8 },
+	{ 128, 0, 14, 0, 8 },
+	{ 136, 0, 14, 0, 8 },
+	{ 144, 0, 14, 0, 8 },
+	{ 152, 0, 14, 0, 8 },
+	{ 160, 0, 14, 0, 8 },
+	{ 168, 0, 14, 0, 8 },
+	{ 176, 0, 14, 0, 8 },
+	{ 184, 0, 14, 0, 8 },
+	{ 192, 0, 14, 0, 8 },
+	{ 200, 0, 14, 0, 8 },
+	{ 208, 0, 14, 0, 8 },
+	{ 216, 0, 14, 0, 8 },
+	{ 224, 0, 14, 0, 8 },
+	{ 232, 0, 14, 0, 8 },
+	{ 240, 0, 14, 0, 8 },
+	{ 248, 0, 14, 0, 8 },
+	{ 256, 0, 0, 0, 8 },
+	{ 264, 2, 11, 0, 8 },
+	{ 272, 2, 6, 0, 8 },
+	{ 280, 2, 11, 0, 8 },
+	{ 288, 1, 12, 0, 8 },
+	{ 296, 2, 11, 0, 8 },
+	{ 304, 2, 11, 0, 8 },
+	{ 312, 2, 7, 0, 8 },
+	{ 320, 1, 13, 0, 8 },
+	{ 328, 1, 13, 0, 8 },
+	{ 336, 3, 10, 0, 8 },
+	{ 344, 4, 10, 0, 8 },
+	{ 352, 9, 14, 0, 8 },
+	{ 360, 6, 8, 0, 8 },
+	{ 368, 9, 11, 0, 8 },
+	{ 376, 1, 13, 0, 8 },
+	{ 384, 2, 11, 0, 8 },
+	{ 392, 2, 11, 0, 8 },
+	{ 400, 2, 11, 0, 8 },
+	{ 408, 2, 11, 0, 8 },
+	{ 416, 2, 11, 0, 8 },
+	{ 424, 2, 11, 0, 8 },
+	{ 432, 2, 11, 0, 8 },
+	{ 440, 2, 11, 0, 8 },
+	{ 448, 2, 11, 0, 8 },
+	{ 456, 2, 11, 0, 8 },
+	{ 464, 4, 11, 0, 8 },
+	{ 472, 4, 14, 0, 8 },
+	{ 480, 2, 11, 0, 8 },
+	{ 488, 4, 10, 0, 8 },
+	{ 496, 2, 11, 0, 8 },
+	{ 504, 2, 11, 0, 8 },
+	{ 512, 2, 11, 0, 8 },
+	{ 520, 2, 11, 0, 8 },
+	{ 528, 2, 11, 0, 8 },
+	{ 536, 2, 11, 0, 8 },
+	{ 544, 2, 11, 0, 8 },
+	{ 552, 2, 11, 0, 8 },
+	{ 560, 2, 11, 0, 8 },
+	{ 568, 2, 11, 0, 8 },
+	{ 576, 2, 11, 0, 8 },
+	{ 584, 2, 11, 0, 8 },
+	{ 592, 2, 13, 0, 8 },
+	{ 600, 2, 11, 0, 8 },
+	{ 608, 2, 11, 0, 8 },
+	{ 616, 2, 11, 0, 8 },
+	{ 624, 2, 11, 0, 8 },
+	{ 632, 2, 11, 0, 8 },
+	{ 640, 2, 11, 0, 8 },
+	{ 648, 2, 13, 0, 8 },
+	{ 656, 2, 11, 0, 8 },
+	{ 664, 2, 11, 0, 8 },
+	{ 672, 2, 11, 0, 8 },
+	{ 680, 2, 11, 0, 8 },
+	{ 688, 2, 11, 0, 8 },
+	{ 696, 2, 11, 0, 8 },
+	{ 704, 2, 11, 0, 8 },
+	{ 712, 2, 11, 0, 8 },
+	{ 720, 2, 11, 0, 8 },
+	{ 728, 1, 13, 0, 8 },
+	{ 736, 1, 13, 0, 8 },
+	{ 744, 1, 13, 0, 8 },
+	{ 752, 2, 8, 0, 8 },
+	{ 760, 11, 12, 0, 8 },
+	{ 768, 2, 7, 0, 8 },
+	{ 776, 4, 11, 0, 8 },
+	{ 784, 1, 11, 0, 8 },
+	{ 792, 4, 11, 0, 8 },
+	{ 800, 1, 11, 0, 8 },
+	{ 808, 4, 11, 0, 8 },
+	{ 816, 1, 11, 0, 8 },
+	{ 824, 4, 14, 0, 8 },
+	{ 832, 1, 11, 0, 8 },
+	{ 840, 1, 11, 0, 8 },
+	{ 848, 1, 14, 0, 8 },
+	{ 856, 1, 11, 0, 8 },
+	{ 864, 1, 11, 0, 8 },
+	{ 872, 4, 11, 0, 8 },
+	{ 880, 4, 11, 0, 8 },
+	{ 888, 4, 11, 0, 8 },
+	{ 896, 4, 14, 0, 8 },
+	{ 904, 4, 14, 0, 8 },
+	{ 912, 4, 11, 0, 8 },
+	{ 920, 4, 11, 0, 8 },
+	{ 928, 2, 11, 0, 8 },
+	{ 936, 4, 11, 0, 8 },
+	{ 944, 4, 11, 0, 8 },
+	{ 952, 4, 11, 0, 8 },
+	{ 960, 4, 11, 0, 8 },
+	{ 968, 4, 14, 0, 8 },
+	{ 976, 4, 11, 0, 8 },
+	{ 984, 1, 12, 0, 8 },
+	{ 992, 1, 12, 0, 8 },
+	{ 1000, 1, 12, 0, 8 },
+	{ 1008, 5, 8, 0, 8 },
+	{ 1016, 0, 14, 0, 8 },
+	{ 1024, 0, 14, 0, 8 },
+	{ 0, 0, 0, 0, 0 }
+};
+
+GSubfont defont0 = {
+	129,
+	14,
+	2,
+	info0,
+	&strike0,
+};
--- /dev/null
+++ b/os/boot/mpc/devether.c
@@ -1,0 +1,157 @@
+#include "u.h"
+#include "lib.h"
+#include "mem.h"
+#include "dat.h"
+#include "fns.h"
+#include "io.h"
+
+#include "etherif.h"
+
+static Ctlr ether[MaxEther];
+
+static struct {
+	char	*type;
+	int	(*reset)(Ctlr*);
+} cards[] = {
+	{ "SCC", sccethreset, },
+	{ "SCC2", sccethreset, },
+	{ 0, }
+};
+
+int
+etherinit(void)
+{
+	Ctlr *ctlr;
+	int ctlrno, i, mask, n;
+
+	mask = 0;
+	for(ctlrno = 0; ctlrno < MaxEther; ctlrno++){
+		ctlr = &ether[ctlrno];
+		memset(ctlr, 0, sizeof(Ctlr));
+		if(isaconfig("ether", ctlrno, &ctlr->card) == 0)
+			continue;
+		for(n = 0; cards[n].type; n++){
+			if(strcmp(cards[n].type, ctlr->card.type))
+				continue;
+			ctlr->ctlrno = ctlrno;
+			if((*cards[n].reset)(ctlr))
+				break;
+
+			ctlr->iq = qopen(16*1024, 1, 0, 0);
+			ctlr->oq = qopen(16*1024, 1, 0, 0);
+
+			ctlr->present = 1;
+			mask |= 1<<ctlrno;
+
+			print("ether%d: %s: port 0x%luX irq %d",
+				ctlr->ctlrno, ctlr->card.type, ctlr->card.port, ctlr->card.irq);
+			if(ctlr->card.mem)
+				print(" addr 0x%luX", PADDR(ctlr->card.mem));
+			if(ctlr->card.size)
+				print(" size 0x%luX", ctlr->card.size);
+			print(":");
+			for(i = 0; i < sizeof(ctlr->card.ea); i++)
+				print(" %2.2uX", ctlr->card.ea[i]);
+			print("\n"); uartwait();
+			setvec(VectorPIC + ctlr->card.irq, ctlr->card.intr, ctlr);
+			break;
+		}
+	}
+
+	return mask;
+}
+
+static Ctlr*
+attach(int ctlrno)
+{
+	Ctlr *ctlr;
+
+	if(ctlrno >= MaxEther || ether[ctlrno].present == 0)
+		return 0;
+
+	ctlr = &ether[ctlrno];
+	if(ctlr->present == 1){
+		ctlr->present = 2;
+		(*ctlr->card.attach)(ctlr);
+	}
+
+	return ctlr;
+}
+
+uchar*
+etheraddr(int ctlrno)
+{
+	Ctlr *ctlr;
+
+	if((ctlr = attach(ctlrno)) == 0)
+		return 0;
+
+	return ctlr->card.ea;
+}
+
+int
+etherrxpkt(int ctlrno, Etherpkt *pkt, int timo)
+{
+	int n;
+	Ctlr *ctlr;
+	Block *b;
+	ulong start;
+
+	if((ctlr = attach(ctlrno)) == 0)
+		return 0;
+
+	start = m->ticks;
+	while((b = qget(ctlr->iq)) == 0){
+		if(TK2MS(m->ticks - start) >= timo){
+			/*
+			print("ether%d: rx timeout\n", ctlrno);
+			 */
+			return 0;
+		}
+	}
+
+	n = BLEN(b);
+	memmove(pkt, b->rp, n);
+	freeb(b);
+
+	return n;
+}
+
+int
+etheriq(Ctlr *ctlr, Block *b, int freebp)
+{
+	if(memcmp(((Etherpkt*)b->rp)->d, ctlr->card.ea, Eaddrlen) != 0 &&
+	   memcmp(((Etherpkt*)b->rp)->d, broadcast, Eaddrlen) != 0){
+		if(freebp)
+			freeb(b);
+		return 0;
+	}
+	qbwrite(ctlr->iq, b);
+	return 1;
+}
+
+int
+ethertxpkt(int ctlrno, Etherpkt *pkt, int len, int)
+{
+	Ctlr *ctlr;
+	Block *b;
+	int s;
+
+	if((ctlr = attach(ctlrno)) == 0)
+		return 0;
+
+	if(qlen(ctlr->oq) > 16*1024){
+		print("ether%d: tx queue full\n", ctlrno);
+		return 0;
+	}
+	b = iallocb(sizeof(Etherpkt));
+	memmove(b->wp, pkt, len);
+	memmove(((Etherpkt*)b->wp)->s, ctlr->card.ea, Eaddrlen);
+	b->wp += len;
+	qbwrite(ctlr->oq, b);
+	s = splhi();
+	(*ctlr->card.transmit)(ctlr);
+	splx(s);
+
+	return 1;
+}
--- /dev/null
+++ b/os/boot/mpc/devuart.c
@@ -1,0 +1,230 @@
+#include "u.h"
+#include "lib.h"
+#include "mem.h"
+#include "dat.h"
+#include "fns.h"
+#include "io.h"
+
+/*
+ *  SMC1 in UART mode
+ */
+
+typedef struct Uartsmc Uartsmc;
+struct Uartsmc {
+	IOCparam;
+	ushort	maxidl;
+	ushort	idlc;
+	ushort	brkln;
+	ushort	brkec;
+	ushort	brkcr;
+	ushort	rmask;
+};
+
+typedef struct Uart	Uart;
+struct Uart
+{
+	int	port;
+	int	setup;
+	uchar	txbusy;
+
+	Queue*	iq;
+	Queue*	oq;
+	void	(*rx)(Queue*, int);
+	void	(*boot)(uchar*, int);
+
+	ulong	frame;
+	ulong	overrun;
+	uchar	rxbuf[128];
+	char	txbuf[16];
+	BD*	rxb;
+	BD*	txb;
+};
+
+Uart	uart[1];
+int	predawn = 1;
+
+static	void	uartintr(Ureg*, void*);
+static	void	uartkick(void*);
+
+static int
+baudgen(int baud)
+{
+	int d;
+
+	d = ((m->cpuhz/baud)+8)>>4;
+	if(d >= (1<<12))
+		return ((d+15)>>3)|1;
+	return d<<1;
+}
+
+static void
+smcsetup(Uart *up, int baud)
+{
+	IMM *io;
+	Uartsmc *p;
+	BD *bd;
+	SMC *smc;
+
+	archenableuart(SMC1ID, 0);
+	io = m->iomem;
+	io->pbpar |= IBIT(24)|IBIT(25);	/* enable SMC1 TX/RX */
+	io->pbdir &= ~(IBIT(24)|IBIT(25));
+	io->brgc1 = baudgen(baud) | BaudEnable;
+	io->simode &= ~0xF000;	/* SMC1 to NMSI mode, Tx/Rx clocks are BRG1 */
+
+	bd = bdalloc(1);
+	p = (Uartsmc*)KADDR(SMC1P);
+	p->rbase = (ushort)bd;
+	up->rxb = bd;
+	bd->status = BDEmpty|BDWrap|BDInt;
+	bd->length = 0;
+	bd->addr = PADDR(up->rxbuf);
+	bd = bdalloc(1);
+	p->tbase = (ushort)bd;
+	up->txb = bd;
+	bd->status = BDWrap|BDInt;
+	bd->length = 0;
+	bd->addr = PADDR(up->txbuf);
+
+	cpmop(InitRxTx, SMC1ID, 0);
+
+	/* protocol parameters */
+	p->rfcr = 0x18;
+	p->tfcr = 0x18;
+	p->mrblr = 1;
+	p->maxidl = 1;
+	p->brkln = 0;
+	p->brkec = 0;
+	p->brkcr = 1;
+	smc = IOREGS(0xA80, SMC);
+	smc->smce = 0xff;	/* clear events */
+	smc->smcm = 0x17;	/* enable all possible interrupts */
+	setvec(VectorCPIC+4, uartintr, up);
+	smc->smcmr = 0x4820;	/* 8-bit mode, no parity, 1 stop bit, UART mode, ... */
+	smc->smcmr |= 3;	/* enable rx/tx */
+}
+
+static void
+uartintr(Ureg*, void *arg)
+{
+	Uart *up;
+	int ch, i;
+	BD *bd;
+	SMC *smc;
+	Block *b;
+
+	up = arg;
+	smc = IOREGS(0xA80, SMC);
+	smc->smce = 0xff;	/* clear all events */
+	if((bd = up->rxb) != nil && (bd->status & BDEmpty) == 0){
+		if(up->iq != nil && bd->length > 0){
+			if(up->boot != nil){
+				up->boot(up->rxbuf, bd->length);
+			}else if(up->rx != nil){
+				for(i=0; i<bd->length; i++){
+					ch = up->rxbuf[i];
+					up->rx(up->iq, ch);
+				}
+			}else{
+				b = iallocb(bd->length);
+				memmove(b->wp, up->rxbuf, bd->length);
+				b->wp += bd->length;
+				qbwrite(up->iq, b);
+			}
+		}
+		bd->status |= BDEmpty|BDInt;
+	} else if((bd = up->txb) != nil && (bd->status & BDReady) == 0){
+		ch = -1;
+		if(up->oq)
+			ch = qbgetc(up->oq);
+		if(ch != -1){
+			up->txbuf[0] = ch;
+			bd->length = 1;
+			bd->status |= BDReady;
+		}else
+			up->txbusy = 0;
+	}
+	/* TO DO: modem status, errors, etc */
+}
+
+static void
+uartkick(void *arg)
+{
+	Uart *up = arg;
+	int s, c, i;
+
+	s = splhi();
+	while(up->txbusy == 0 && (c = qbgetc(up->oq)) != -1){
+		if(predawn){
+			while(up->txb->status & BDReady)
+				;
+		} else {
+			for(i = 0; i < 100; i++){
+				if((up->txb->status & BDReady) == 0)
+					break;
+				delay(1);
+			}
+		}
+		up->txbuf[0] = c;
+		up->txb->length = 1;
+		up->txb->status |= BDReady;
+		up->txbusy = !predawn;
+	}
+	splx(s);
+}
+
+void
+uartspecial(int port, int baud, Queue **iq, Queue **oq, void (*rx)(Queue*,int))
+{
+	Uart *up = &uart[0];
+
+	if(up->setup)
+		return;
+	up->setup = 1;
+
+	*iq = up->iq = qopen(4*1024, 0, 0, 0);
+	*oq = up->oq = qopen(16*1024, 0, uartkick, up);
+	up->rx = rx;
+	USED(port);
+	up->port = SMC1ID;
+	if(baud == 0)
+		baud = 9600;
+	smcsetup(up, baud);
+	/* if using SCCn's UART, would also set DTR and RTS, but SMC doesn't use them */
+}
+
+void
+uartsetboot(void (*f)(uchar*, int))
+{
+	uart[0].boot = f;
+}
+
+void
+uartputs(char *s, int n)
+{
+	Uart *up = &uart[0];
+	Block *b;
+	int nl;
+	char *p;
+
+	nl = 0;
+	for(p = s; p < s+n; p++)
+		if(*p == '\n')
+			nl++;
+	b = iallocb(n+nl);
+	while(n--){
+		if(*s == '\n')
+			*b->wp++ = '\r';
+		*b->wp++ = *s++;
+	}
+	qbwrite(up->oq, b);
+}
+
+void
+uartwait(void)
+{
+	Uart *up = &uart[0];
+
+	while(up->txbusy)
+		;
+}
--- /dev/null
+++ b/os/boot/mpc/dload.c
@@ -1,0 +1,103 @@
+#include <u.h>
+#include <libc.h>
+#include <bio.h>
+#include <mach.h>
+
+static	char	*kernelfile = "/power/ipaq";
+ulong	crc32(void *buf, int n, ulong crc);
+
+void
+main(int argc, char **argv)
+{
+	int ifd, n;
+	char buf[64], reply[1];
+	int i, execsize;
+	Fhdr f;
+	ulong csum;
+
+	ARGBEGIN{
+	}ARGEND
+	ifd = open(kernelfile, OREAD);
+	if(ifd < 0){
+		fprint(2, "dload: can't open %s: %r\n", kernelfile);
+		exits("open");
+	}
+	i = 0;
+	if(crackhdr(ifd, &f) == 0){
+		fprint(2, "dload: not an executable file: %r\n");
+		exits("format");
+	}
+	if(f.magic != Q_MAGIC){
+		fprint(2, "dload: not a powerpc executable\n");
+		exits("format");
+	}
+	execsize = f.txtsz + f.datsz + f.txtoff;
+	seek(ifd, 0, 0);
+	csum = ~0;
+	while(execsize > 0 && (n = read(ifd, buf, sizeof(buf))) > 0){
+		if(n > execsize)
+			n = execsize;
+		for(;;){
+			if(write(1, buf, sizeof(buf)) != sizeof(buf)){	/* always writes full buffer */
+				fprint(2, "dload: write error: %r\n");
+				exits("write");
+			}
+			if(read(0, reply, 1) != 1){
+				fprint(2, "dload: bad reply\n");
+				exits("read");
+			}
+			if(reply[0] != 'n')
+				break;
+			fprint(2, "!");
+		}
+		if(reply[0] != 'y'){
+			fprint(2, "dload: bad ack: %c\n", reply[0]);
+			exits("reply");
+		}
+		if(++i%10 == 0)
+			fprint(2, ".");
+		execsize -= n;
+	}
+	exits(0);
+}
+
+/*
+ * from Rob Warnock
+ */
+static	ulong	crc32tab[256];	/* initialised on first call to crc32 */
+
+enum {
+	CRC32POLY = 0x04c11db7     /* AUTODIN II, Ethernet, & FDDI */
+};
+
+/*
+ * Build auxiliary table for parallel byte-at-a-time CRC-32.
+ */
+static void
+initcrc32(void)
+{
+	int i, j;
+	ulong c;
+
+	for(i = 0; i < 256; i++) {
+		for(c = i << 24, j = 8; j > 0; j--)
+			if(c & (1<<31))
+				c = (c<<1) ^ CRC32POLY;
+			else
+				c <<= 1;
+		crc32tab[i] = c;
+	}
+}
+
+ulong
+crc32(void *buf, int n, ulong crc)
+{
+	uchar *p;
+
+	if(crc32tab[1] == 0)
+		initcrc32();
+	crc = ~crc;
+	for(p = buf; --n >= 0;)
+		crc = (crc << 8) ^ crc32tab[(crc >> 24) ^ *p++];
+	return ~crc;
+}
--- /dev/null
+++ b/os/boot/mpc/donprint.c
@@ -1,0 +1,332 @@
+#include	"u.h"
+#include	"lib.h"
+
+#define	PTR	sizeof(char*)
+#define	SHORT	sizeof(int)
+#define	INT	sizeof(int)
+#define	LONG	sizeof(long)
+#define	IDIGIT	30
+#define	MAXCON	30
+
+#define	FLONG	(1<<0)
+#define	FSHORT	(1<<1)
+#define	FUNSIGN	(1<<2)
+
+typedef struct Op	Op;
+struct Op
+{
+	char	*p;
+	char	*ep;
+	void	*argp;
+	int	f1;
+	int	f2;
+	int	f3;
+};
+
+static	int	noconv(Op*);
+static	int	cconv(Op*);
+static	int	dconv(Op*);
+static	int	hconv(Op*);
+static	int	lconv(Op*);
+static	int	oconv(Op*);
+static	int	sconv(Op*);
+static	int	uconv(Op*);
+static	int	xconv(Op*);
+static	int	Xconv(Op*);
+static	int	percent(Op*);
+
+static
+int	(*fmtconv[MAXCON])(Op*) =
+{
+	noconv,
+	cconv, dconv, hconv, lconv,
+	oconv, sconv, uconv, xconv,
+	Xconv, percent,
+};
+static
+char	fmtindex[128] =
+{
+	['c'] 1,
+	['d'] 2,
+	['h'] 3,
+	['l'] 4,
+	['o'] 5,
+	['s'] 6,
+	['u'] 7,
+	['x'] 8,
+	['X'] 9,
+	['%'] 10,
+};
+
+static	int	convcount  = { 11 };
+static	int	ucase;
+
+static void
+PUT(Op *o, int c)
+{
+	static int pos;
+	int opos;
+
+	if(c == '\t'){
+		opos = pos;
+		pos = (opos+8) & ~7;
+		while(opos++ < pos && o->p < o->ep)
+			*o->p++ = ' ';
+		return;
+	}
+	if(o->p < o->ep){
+		*o->p++ = c;
+		pos++;
+	}
+	if(c == '\n')
+		pos = 0;
+}
+
+int
+fmtinstall(char c, int (*f)(Op*))
+{
+
+	c &= 0177;
+	if(fmtindex[c] == 0) {
+		if(convcount >= MAXCON)
+			return 1;
+		fmtindex[c] = convcount++;
+	}
+	fmtconv[fmtindex[c]] = f;
+	return 0;
+}
+
+char*
+donprint(char *p, char *ep, char *fmt, void *argp)
+{
+	int sf1, c;
+	Op o;
+
+	o.p = p;
+	o.ep = ep;
+	o.argp = argp;
+
+loop:
+	c = *fmt++;
+	if(c != '%') {
+		if(c == 0) {
+			if(o.p < o.ep)
+				*o.p = 0;
+			return o.p;
+		}
+		PUT(&o, c);
+		goto loop;
+	}
+	o.f1 = 0;
+	o.f2 = -1;
+	o.f3 = 0;
+	c = *fmt++;
+	sf1 = 0;
+	if(c == '-') {
+		sf1 = 1;
+		c = *fmt++;
+	}
+	while(c >= '0' && c <= '9') {
+		o.f1 = o.f1*10 + c-'0';
+		c = *fmt++;
+	}
+	if(sf1)
+		o.f1 = -o.f1;
+	if(c != '.')
+		goto l1;
+	c = *fmt++;
+	while(c >= '0' && c <= '9') {
+		if(o.f2 < 0)
+			o.f2 = 0;
+		o.f2 = o.f2*10 + c-'0';
+		c = *fmt++;
+	}
+l1:
+	if(c == 0)
+		fmt--;
+	c = (*fmtconv[fmtindex[c&0177]])(&o);
+	if(c < 0) {
+		o.f3 |= -c;
+		c = *fmt++;
+		goto l1;
+	}
+	o.argp = (char*)o.argp + c;
+	goto loop;
+}
+
+void
+strconv(char *o, Op *op, int f1, int f2)
+{
+	int n, c;
+	char *p;
+
+	n = strlen(o);
+	if(f1 >= 0)
+		while(n < f1) {
+			PUT(op, ' ');
+			n++;
+		}
+	for(p=o; c = *p++;)
+		if(f2 != 0) {
+			PUT(op, c);
+			f2--;
+		}
+	if(f1 < 0) {
+		f1 = -f1;
+		while(n < f1) {
+			PUT(op, ' ');
+			n++;
+		}
+	}
+}
+
+int
+numbconv(Op *op, int base)
+{
+	char b[IDIGIT];
+	int i, f, n, r;
+	long v;
+	short h;
+
+	f = 0;
+	switch(op->f3 & (FLONG|FSHORT|FUNSIGN)) {
+	case FLONG:
+		v = *(long*)op->argp;
+		r = LONG;
+		break;
+
+	case FUNSIGN|FLONG:
+		v = *(ulong*)op->argp;
+		r = LONG;
+		break;
+
+	case FSHORT:
+		h = *(int*)op->argp;
+		v = h;
+		r = SHORT;
+		break;
+
+	case FUNSIGN|FSHORT:
+		h = *(int*)op->argp;
+		v = (ushort)h;
+		r = SHORT;
+		break;
+
+	default:
+		v = *(int*)op->argp;
+		r = INT;
+		break;
+
+	case FUNSIGN:
+		v = *(unsigned*)op->argp;
+		r = INT;
+		break;
+	}
+	if(!(op->f3 & FUNSIGN) && v < 0) {
+		v = -v;
+		f = 1;
+	}
+	b[IDIGIT-1] = 0;
+	for(i = IDIGIT-2;; i--) {
+		n = (ulong)v % base;
+		n += '0';
+		if(n > '9'){
+			n += 'a' - ('9'+1);
+			if(ucase)
+				n += 'A'-'a';
+		}
+		b[i] = n;
+		if(i < 2)
+			break;
+		v = (ulong)v / base;
+		if(op->f2 >= 0 && i >= IDIGIT-op->f2)
+			continue;
+		if(v <= 0)
+			break;
+	}
+	if(f)
+		b[--i] = '-';
+	strconv(b+i, op, op->f1, -1);
+	return r;
+}
+
+static	int
+noconv(Op *op)
+{
+
+	strconv("***", op, 0, -1);
+	return 0;
+}
+
+static	int
+cconv(Op *op)
+{
+	char b[2];
+
+	b[0] = *(int*)op->argp;
+	b[1] = 0;
+	strconv(b, op, op->f1, -1);
+	return INT;
+}
+
+static	int
+dconv(Op *op)
+{
+	return numbconv(op, 10);
+}
+
+static	int
+hconv(Op*)
+{
+	return -FSHORT;
+}
+
+static	int
+lconv(Op*)
+{
+	return -FLONG;
+}
+
+static	int
+oconv(Op *op)
+{
+	return numbconv(op, 8);
+}
+
+static	int
+sconv(Op *op)
+{
+	strconv(*(char**)op->argp, op, op->f1, op->f2);
+	return PTR;
+}
+
+static	int
+uconv(Op*)
+{
+	return -FUNSIGN;
+}
+
+static	int
+xconv(Op *op)
+{
+	return numbconv(op, 16);
+}
+
+static	int
+Xconv(Op *op)
+{
+	int r;
+
+	ucase = 1;
+	r = numbconv(op, 16);
+	ucase = 0;
+	return r;
+}
+
+static	int
+percent(Op *op)
+{
+
+	PUT(op, '%');
+	return 0;
+}
--- /dev/null
+++ b/os/boot/mpc/dosboot.c
@@ -1,0 +1,614 @@
+#include	"u.h"
+#include	"lib.h"
+#include	"mem.h"
+#include	"dat.h"
+#include	"fns.h"
+#include	"dosfs.h"
+
+extern char *premature;
+
+/*
+ *  predeclared
+ */
+static void	bootdump(Dosboot*);
+static void	setname(Dosfile*, char*);
+long		dosreadseg(Dosfile*, long, long);
+
+/*
+ *  debugging
+ */
+#define chatty	1
+#define chat	if(chatty)print
+
+/*
+ *  block io buffers
+ */
+enum
+{
+	Nbio=	16,
+};
+typedef struct	Clustbuf	Clustbuf;
+struct Clustbuf
+{
+	int	age;
+	long	sector;
+	uchar	*iobuf;
+	Dos	*dos;
+	int	size;
+};
+Clustbuf	bio[Nbio];
+
+/*
+ *  get an io block from an io buffer
+ */
+Clustbuf*
+getclust(Dos *dos, long sector)
+{
+	Clustbuf *p, *oldest;
+	int size;
+
+	chat("getclust @ %d\n", sector);
+
+	/*
+	 *  if we have it, just return it
+	 */
+	for(p = bio; p < &bio[Nbio]; p++){
+		if(sector == p->sector && dos == p->dos){
+			p->age = m->ticks;
+			chat("getclust %d in cache\n", sector);
+			return p;
+		}
+	}
+
+	/*
+	 *  otherwise, reuse the oldest entry
+	 */
+	oldest = bio;
+	for(p = &bio[1]; p < &bio[Nbio]; p++){
+		if(p->age <= oldest->age)
+			oldest = p;
+	}
+	p = oldest;
+
+	/*
+	 *  make sure the buffer is big enough
+	 */
+	size = dos->clustsize*dos->sectsize;
+	if(p->iobuf==0 || p->size < size)
+		p->iobuf = ialloc(size, 0);
+	p->size = size;
+
+	/*
+	 *  read in the cluster
+	 */
+	chat("getclust addr %d\n", (sector+dos->start)*dos->sectsize);
+	if((*dos->seek)(dos->dev, (sector+dos->start)*dos->sectsize) < 0){
+		chat("can't seek block\n");
+		return 0;
+	}
+	if((*dos->read)(dos->dev, p->iobuf, size) != size){
+		chat("can't read block\n");
+		return 0;
+	}
+
+	p->age = m->ticks;
+	p->dos = dos;
+	p->sector = sector;
+	chat("getclust %d read\n", sector);
+	return p;
+}
+
+/*
+ *  walk the fat one level ( n is a current cluster number ).
+ *  return the new cluster number or -1 if no more.
+ */
+static long
+fatwalk(Dos *dos, int n)
+{
+	ulong k, sect;
+	Clustbuf *p;
+	int o;
+
+	chat("fatwalk %d\n", n);
+
+	if(n < 2 || n >= dos->fatclusters)
+		return -1;
+
+	switch(dos->fatbits){
+	case 12:
+		k = (3*n)/2; break;
+	case 16:
+		k = 2*n; break;
+	default:
+		return -1;
+	}
+	if(k >= dos->fatsize*dos->sectsize)
+		panic("getfat");
+
+	sect = (k/(dos->sectsize*dos->clustsize))*dos->clustsize + dos->fataddr;
+	o = k%(dos->sectsize*dos->clustsize);
+	p = getclust(dos, sect);
+	k = p->iobuf[o++];
+	if(o >= dos->sectsize*dos->clustsize){
+		p = getclust(dos, sect+dos->clustsize);
+		o = 0;
+	}
+	k |= p->iobuf[o]<<8;
+	if(dos->fatbits == 12){
+		if(n&1)
+			k >>= 4;
+		else
+			k &= 0xfff;
+		if(k >= 0xff8)
+			k |= 0xf000;
+	}
+	k = k < 0xfff8 ? k : -1;
+	chat("fatwalk %d -> %d\n", n, k);
+	return k;
+}
+
+/*
+ *  map a file's logical cluster address to a physical sector address
+ */
+static long
+fileaddr(Dosfile *fp, long ltarget)
+{
+	Dos *dos = fp->dos;
+	long l;
+	long p;
+
+	chat("fileaddr %8.8s %d\n", fp->name, ltarget);
+	/*
+	 *  root directory is contiguous and easy
+	 */
+	if(fp->pstart == 0){
+		if(ltarget*dos->sectsize*dos->clustsize >= dos->rootsize*sizeof(Dosdir))
+			return -1;
+		l = dos->rootaddr + ltarget*dos->clustsize;
+		chat("fileaddr %d -> %d\n", ltarget, l);
+		return l;
+	}
+
+	/*
+	 *  anything else requires a walk through the fat
+	 */
+	if(ltarget >= fp->lcurrent && fp->pcurrent){
+		/* start at the currrent point */
+		l = fp->lcurrent;
+		p = fp->pcurrent;
+	} else {
+		/* go back to the beginning */
+		l = 0;
+		p = fp->pstart;
+	}
+	while(l != ltarget){
+		/* walk the fat */
+		p = fatwalk(dos, p);
+		if(p < 0)
+			return -1;
+		l++;
+	}
+	fp->lcurrent = l;
+	fp->pcurrent = p;
+
+	/*
+	 *  clusters start at 2 instead of 0 (why? - presotto)
+	 */
+	l =  dos->dataaddr + (p-2)*dos->clustsize;
+	chat("fileaddr %d -> %d\n", ltarget, l);
+	return l;
+}
+
+/*
+ *  read from a dos file
+ */
+long
+dosread(Dosfile *fp, void *a, long n)
+{
+	long addr;
+	long rv;
+	int i;
+	int off;
+	Clustbuf *p;
+	uchar *from, *to;
+
+	if((fp->attr & DDIR) == 0){
+		if(fp->offset >= fp->length)
+			return 0;
+		if(fp->offset+n > fp->length)
+			n = fp->length - fp->offset;
+	}
+
+	to = a;
+	for(rv = 0; rv < n; rv+=i){
+		/*
+		 *  read the cluster
+		 */
+		addr = fileaddr(fp, fp->offset/fp->dos->clustbytes);
+		if(addr < 0)
+			return -1;
+		p = getclust(fp->dos, addr);
+		if(p == 0)
+			return -1;
+
+		/*
+		 *  copy the bytes we need
+		 */
+		off = fp->offset % fp->dos->clustbytes;
+		from = &p->iobuf[off];
+		i = n - rv;
+		if(i > fp->dos->clustbytes - off)
+			i = fp->dos->clustbytes - off;
+		memmove(to, from, i);
+		to += i;
+		fp->offset += i;
+	}
+
+	return rv;
+}
+
+/*
+ *  walk a directory returns
+ * 	-1 if something went wrong
+ *	 0 if not found
+ *	 1 if found
+ */
+int
+doswalk(Dosfile *file, char *name)
+{
+	Dosdir d;
+	long n;
+
+	if((file->attr & DDIR) == 0){
+		chat("walking non-directory!\n");
+		return -1;
+	}
+
+	setname(file, name);
+
+	file->offset = 0;	/* start at the beginning */
+	while((n = dosread(file, &d, sizeof(d))) == sizeof(d)){
+		chat("comparing to %8.8s.%3.3s\n", d.name, d.ext);
+		if(memcmp(file->name, d.name, sizeof(d.name)) != 0)
+			continue;
+		if(memcmp(file->ext, d.ext, sizeof(d.ext)) != 0)
+			continue;
+		if(d.attr & DVLABEL){
+			chat("%8.8s.%3.3s is a LABEL\n", d.name, d.ext);
+			continue;
+		}
+		file->attr = d.attr;
+		file->pstart = GSHORT(d.start);
+		file->length = GLONG(d.length);
+		file->pcurrent = 0;
+		file->lcurrent = 0;
+		file->offset = 0;
+		return 1;
+	}
+	return n >= 0 ? 0 : -1;
+}
+
+
+/*
+ *  instructions that boot blocks can start with
+ */
+#define	JMPSHORT	0xeb
+#define JMPNEAR		0xe9
+
+/*
+ *  read dos file system properties
+ */
+int
+dosinit(Dos *dos, int start, int ishard)
+{
+	Dosboot *b;
+	int i;
+	Clustbuf *p;
+	Dospart *dp;
+	ulong mbroffset, offset;
+
+	/* defaults till we know better */
+	dos->start = start;
+	dos->sectsize = 512;
+	dos->clustsize = 1;
+	mbroffset = 0;
+
+dmddo:
+	/* get first sector */
+	p = getclust(dos, mbroffset);
+	if(p == 0){
+		chat("can't read boot block\n");
+		return -1;
+	}
+
+	/*
+	 * If it's a hard disc then look for an MBR and pick either an
+	 * active partition or the FAT with the lowest starting LBA.
+	 * Things are tricky because we could be pointing to, amongst others:
+	 *	1) a floppy BPB;
+	 *	2) a hard disc MBR;
+	 *	3) a hard disc extended partition table;
+	 *	4) a logical drive on a hard disc;
+	 *	5) a disc-manager boot block.
+	 * They all have the same magic at the end of the block.
+	 */
+	if(p->iobuf[0x1FE] != 0x55 || p->iobuf[0x1FF] != 0xAA) {
+		chat("not DOS\n");
+		return -1;
+	}
+	p->dos = 0;
+	b = (Dosboot *)p->iobuf;
+	if(ishard && b->mediadesc != 0xF8){
+		dp = (Dospart*)&p->iobuf[0x1BE];
+		offset = 0xFFFFFFFF;
+		for(i = 0; i < 4; i++, dp++){
+			if(dp->type == DMDDO){
+				mbroffset = 63;
+				goto dmddo;
+			}
+			if(dp->type != FAT12 && dp->type != FAT16 && dp->type != FATHUGE)
+				continue;
+			if(dp->flag & 0x80){
+				offset = GLONG(dp->start);
+				break;
+			}
+			if(GLONG(dp->start) < offset)
+				offset = GLONG(dp->start);
+		}
+		if(i != 4 || offset != 0xFFFFFFFF){
+			dos->start = mbroffset+offset;
+			p = getclust(dos, 0);
+			if(p == 0 || p->iobuf[0x1FE] != 0x55 || p->iobuf[0x1FF] != 0xAA)
+				return -1;
+		}
+		p->dos = 0;
+	}
+
+	b = (Dosboot *)p->iobuf;
+	if(b->magic[0] != JMPNEAR && (b->magic[0] != JMPSHORT || b->magic[2] != 0x90)){
+		chat("no dos file system\n");
+		return -1;
+	}
+
+	if(chatty)
+		bootdump(b);
+
+	/*
+	 *  determine the systems' wondersous properties
+	 */
+	dos->sectsize = GSHORT(b->sectsize);
+	dos->clustsize = b->clustsize;
+	dos->clustbytes = dos->sectsize*dos->clustsize;
+	dos->nresrv = GSHORT(b->nresrv);
+	dos->nfats = b->nfats;
+	dos->rootsize = GSHORT(b->rootsize);
+	dos->volsize = GSHORT(b->volsize);
+	if(dos->volsize == 0)
+		dos->volsize = GLONG(b->bigvolsize);
+	dos->mediadesc = b->mediadesc;
+	dos->fatsize = GSHORT(b->fatsize);
+	dos->fataddr = dos->nresrv;
+	dos->rootaddr = dos->fataddr + dos->nfats*dos->fatsize;
+	i = dos->rootsize*sizeof(Dosdir) + dos->sectsize - 1;
+	i = i/dos->sectsize;
+	dos->dataaddr = dos->rootaddr + i;
+	dos->fatclusters = 2+(dos->volsize - dos->dataaddr)/dos->clustsize;
+	if(dos->fatclusters < 4087)
+		dos->fatbits = 12;
+	else
+		dos->fatbits = 16;
+	dos->freeptr = 2;
+
+	/*
+	 *  set up the root
+	 */
+	dos->root.dos = dos;
+	dos->root.pstart = 0;
+	dos->root.pcurrent = dos->root.lcurrent = 0;
+	dos->root.offset = 0;
+	dos->root.attr = DDIR;
+	dos->root.length = dos->rootsize*sizeof(Dosdir);
+
+	return 0;
+}
+
+static void
+bootdump(Dosboot *b)
+{
+	if(chatty == 0)
+		return;
+	print("magic: 0x%2.2x 0x%2.2x 0x%2.2x\n",
+		b->magic[0], b->magic[1], b->magic[2]);
+	print("version: \"%8.8s\"\n", b->version);
+	print("sectsize: %d\n", GSHORT(b->sectsize));
+	print("allocsize: %d\n", b->clustsize);
+	print("nresrv: %d\n", GSHORT(b->nresrv));
+	print("nfats: %d\n", b->nfats);
+	print("rootsize: %d\n", GSHORT(b->rootsize));
+	print("volsize: %d\n", GSHORT(b->volsize));
+	print("mediadesc: 0x%2.2x\n", b->mediadesc);
+	print("fatsize: %d\n", GSHORT(b->fatsize));
+	print("trksize: %d\n", GSHORT(b->trksize));
+	print("nheads: %d\n", GSHORT(b->nheads));
+	print("nhidden: %d\n", GLONG(b->nhidden));
+	print("bigvolsize: %d\n", GLONG(b->bigvolsize));
+	print("driveno: %d\n", b->driveno);
+	print("reserved0: 0x%2.2x\n", b->reserved0);
+	print("bootsig: 0x%2.2x\n", b->bootsig);
+	print("volid: 0x%8.8x\n", GLONG(b->volid));
+	print("label: \"%11.11s\"\n", b->label);
+}
+
+/*
+ *  grab next element from a path, return the pointer to unprocessed portion of
+ *  path.
+ */
+static char *
+nextelem(char *path, char *elem)
+{
+	int i;
+
+	while(*path == '/')
+		path++;
+	if(*path==0 || *path==' ')
+		return 0;
+	for(i=0; *path!='\0' && *path!='/' && *path!=' '; i++){
+		if(i==28){
+			print("name component too long\n");
+			return 0;
+		}
+		*elem++ = *path++;
+	}
+	*elem = '\0';
+	return path;
+}
+
+int
+dosstat(Dos *dos, char *path, Dosfile *f)
+{
+	char element[NAMELEN];
+
+	*f = dos->root;
+	while(path = nextelem(path, element)){
+		switch(doswalk(f, element)){
+		case -1:
+			return -1;
+		case 0:
+			return 0;
+		}
+	}
+	return 1;
+}
+
+/*
+ *  boot
+ */
+int
+dosboot(Dos *dos, char *path)
+{
+	Dosfile file;
+	long n;
+	long addr;
+	Exec *ep;
+	void (*b)(void);
+
+	switch(dosstat(dos, path, &file)){
+
+	case -1:
+		print("error walking to %s\n", path);
+		return -1;
+	case 0:
+		print("%s not found\n", path);
+		return -1;
+	case 1:
+		print("found %8.8s.%3.3s attr 0x%ux start 0x%lux len %d\n", file.name,
+			file.ext, file.attr, file.pstart, file.length);
+		break;
+	}
+
+	/*
+	 *  read header
+	 */
+	ep = (Exec*)ialloc(sizeof(Exec), 0);
+	n = sizeof(Exec);
+	if(dosreadseg(&file, n, (ulong) ep) != n){
+		print(premature);
+		return -1;
+	}
+	if(GLLONG(ep->magic) != Q_MAGIC){
+		print("bad magic 0x%lux not a plan 9 executable!\n", GLLONG(ep->magic));
+		return -1;
+	}
+
+	/*
+	 *  read text
+	 */
+	addr = PADDR(GLLONG(ep->entry));
+	n = GLLONG(ep->text);
+	print("+%d", n);
+	if(dosreadseg(&file, n, addr) != n){
+		print(premature);
+		return -1;
+	}
+
+	/*
+	 *  read data (starts at first page after kernel)
+	 */
+	addr = PGROUND(addr+n);
+	n = GLLONG(ep->data);
+	print("+%d", n);
+	if(dosreadseg(&file, n, addr) != n){
+		print(premature);
+		return -1;
+	}
+
+	/*
+	 *  bss and entry point
+	 */
+	print("+%d\nstart at 0x%lux\n", GLLONG(ep->bss), GLLONG(ep->entry));
+
+	/*
+	 *  Go to new code. It's up to the program to get its PC relocated to
+	 *  the right place.
+	 */
+	b = (void (*)(void))(PADDR(GLLONG(ep->entry)));
+	(*b)();
+	return 0;
+}
+
+/*
+ *  read in a segment
+ */
+long
+dosreadseg(Dosfile *fp, long len, long addr)
+{
+	char *a;
+	long n, sofar;
+
+	a = (char *)addr;
+	for(sofar = 0; sofar < len; sofar += n){
+		n = 8*1024;
+		if(len - sofar < n)
+			n = len - sofar;
+		n = dosread(fp, a + sofar, n);
+		if(n <= 0)
+			break;
+		print(".");
+	}
+	return sofar;
+}
+
+/*
+ *  set up a dos file name
+ */
+static void
+setname(Dosfile *fp, char *from)
+{
+	char *to;
+
+	to = fp->name;
+	for(; *from && to-fp->name < 8; from++, to++){
+		if(*from == '.'){
+			from++;
+			break;
+		}
+		if(*from >= 'a' && *from <= 'z')
+			*to = *from + 'A' - 'a';
+		else
+			*to = *from;
+	}
+	while(to - fp->name < 8)
+		*to++ = ' ';
+	
+	to = fp->ext;
+	for(; *from && to-fp->ext < 3; from++, to++){
+		if(*from >= 'a' && *from <= 'z')
+			*to = *from + 'A' - 'a';
+		else
+			*to = *from;
+	}
+	while(to-fp->ext < 3)
+		*to++ = ' ';
+
+	chat("name is %8.8s %3.3s\n", fp->name, fp->ext);
+}
--- /dev/null
+++ b/os/boot/mpc/dosfs.h
@@ -1,0 +1,110 @@
+typedef struct Dosboot	Dosboot;
+typedef struct Dos	Dos;
+typedef struct Dosdir	Dosdir;
+typedef struct Dosfile	Dosfile;
+typedef struct Dospart	Dospart;
+
+struct Dospart
+{
+	uchar flag;		/* active flag */
+	uchar shead;		/* starting head */
+	uchar scs[2];		/* starting cylinder/sector */
+	uchar type;		/* partition type */
+	uchar ehead;		/* ending head */
+	uchar ecs[2];		/* ending cylinder/sector */
+	uchar start[4];		/* starting sector */
+	uchar len[4];		/* length in sectors */
+};
+
+#define FAT12	0x01
+#define FAT16	0x04
+#define FATHUGE	0x06
+#define DMDDO	0x54
+
+struct Dosboot{
+	uchar	magic[3];
+	uchar	version[8];
+	uchar	sectsize[2];
+	uchar	clustsize;
+	uchar	nresrv[2];
+	uchar	nfats;
+	uchar	rootsize[2];
+	uchar	volsize[2];
+	uchar	mediadesc;
+	uchar	fatsize[2];
+	uchar	trksize[2];
+	uchar	nheads[2];
+	uchar	nhidden[4];
+	uchar	bigvolsize[4];
+	uchar	driveno;
+	uchar	reserved0;
+	uchar	bootsig;
+	uchar	volid[4];
+	uchar	label[11];
+	uchar	reserved1[8];
+};
+
+struct Dosfile{
+	Dos	*dos;		/* owning dos file system */
+	char	name[8];
+	char	ext[3];
+	uchar	attr;
+	long	length;
+	long	pstart;		/* physical start cluster address */
+	long	pcurrent;	/* physical current cluster address */
+	long	lcurrent;	/* logical current cluster address */
+	long	offset;
+};
+
+struct Dos{
+	int	dev;				/* device id */
+	long	(*read)(int, void*, long);	/* read routine */
+	long	(*seek)(int, long);		/* seek routine */
+
+	int	start;		/* start of file system */
+	int	sectsize;	/* in bytes */
+	int	clustsize;	/* in sectors */
+	int	clustbytes;	/* in bytes */
+	int	nresrv;		/* sectors */
+	int	nfats;		/* usually 2 */
+	int	rootsize;	/* number of entries */
+	int	volsize;	/* in sectors */
+	int	mediadesc;
+	int	fatsize;	/* in sectors */
+	int	fatclusters;
+	int	fatbits;	/* 12 or 16 */
+	long	fataddr;	/* sector number */
+	long	rootaddr;
+	long	dataaddr;
+	long	freeptr;
+
+	Dosfile	root;
+};
+
+struct Dosdir{
+	uchar	name[8];
+	uchar	ext[3];
+	uchar	attr;
+	uchar	reserved[10];
+	uchar	time[2];
+	uchar	date[2];
+	uchar	start[2];
+	uchar	length[4];
+};
+
+#define	DRONLY	0x01
+#define	DHIDDEN	0x02
+#define	DSYSTEM	0x04
+#define	DVLABEL	0x08
+#define	DDIR	0x10
+#define	DARCH	0x20
+
+extern int chatty;
+
+extern int dosboot(Dos*, char*);
+extern int dosinit(Dos*, int, int);
+extern long dosread(Dosfile*, void*, long);
+extern int dosstat(Dos*, char*, Dosfile*);
+extern int doswalk(Dosfile*, char*);
+
+extern int plan9ini(Dos*, char*);
--- /dev/null
+++ b/os/boot/mpc/etherif.h
@@ -1,0 +1,59 @@
+/*
+ * All the goo for PC ethernet cards.
+ */
+typedef struct Card Card;
+typedef struct Type Type;
+typedef struct Ctlr Ctlr;
+
+/*
+ * Hardware interface.
+ */
+struct Card {
+	ISAConf;
+
+	int	(*reset)(Ctlr*);
+	void	(*attach)(Ctlr*);
+
+	void	*(*read)(Ctlr*, void*, ulong, ulong);
+	void	*(*write)(Ctlr*, ulong, void*, ulong);
+
+	void	(*receive)(Ctlr*);
+	void	(*transmit)(Ctlr*);
+	void	(*intr)(Ureg*, void*);
+	void	(*overflow)(Ctlr*);
+
+	uchar	bit16;			/* true if a 16 bit interface */
+	uchar	ram;			/* true if card has shared memory */
+
+	ulong	dp8390;			/* I/O address of 8390 (if any) */
+	ulong	data;			/* I/O data port if no shared memory */
+	uchar	nxtpkt;			/* software bndry */
+	uchar	tstart;			/* 8390 ring addresses */
+	uchar	pstart;
+	uchar	pstop;
+
+	uchar	dummyrr;		/* do dummy remote read */
+};
+
+/*
+ * Software controller.
+ */
+struct Ctlr {
+	Card	card;			/* hardware info */
+	int	ctlrno;
+	int	present;
+
+	Queue*	iq;
+	Queue*	oq;
+
+	int	inpackets;
+	int	outpackets;
+	int	crcs;			/* input crc errors */
+	int	oerrs;			/* output errors */
+	int	frames;			/* framing errors */
+	int	overflows;		/* packet overflows */
+	int	buffs;			/* buffering errors */
+};
+
+extern int sccethreset(Ctlr*);
+extern int	etheriq(Ctlr*, Block*, int);
--- /dev/null
+++ b/os/boot/mpc/etherscc.c
@@ -1,0 +1,411 @@
+/*
+ * SCCn ethernet
+ */
+
+#include "u.h"
+#include "lib.h"
+#include "mem.h"
+#include "dat.h"
+#include "fns.h"
+#include "io.h"
+
+#include "etherif.h"
+
+enum {
+	Nrdre		= 32,	/* receive descriptor ring entries */
+	Ntdre		= 4,	/* transmit descriptor ring entries */
+
+	Rbsize		= ETHERMAXTU+4,		/* ring buffer size (+4 for CRC) */
+	Bufsize		= (Rbsize+7)&~7,	/* aligned */
+};
+
+enum {
+	/* ether-specific Rx BD bits */
+	RxMiss=		1<<8,
+	RxeLG=		1<<5,
+	RxeNO=		1<<4,
+	RxeSH=		1<<3,
+	RxeCR=		1<<2,
+	RxeOV=		1<<1,
+	RxeCL=		1<<0,
+	RxError=		(RxeLG|RxeNO|RxeSH|RxeCR|RxeOV|RxeCL),	/* various error flags */
+
+	/* ether-specific Tx BD bits */
+	TxPad=		1<<14,	/* pad short frames */
+	TxTC=		1<<10,	/* transmit CRC */
+	TxeDEF=		1<<9,
+	TxeHB=		1<<8,
+	TxeLC=		1<<7,
+	TxeRL=		1<<6,
+	TxeUN=		1<<1,
+	TxeCSL=		1<<0,
+
+	/* scce */
+	RXB=	1<<0,
+	TXB=	1<<1,
+	BSY=		1<<2,
+	RXF=		1<<3,
+	TXE=		1<<4,
+
+	/* gsmrl */
+	ENR=	1<<5,
+	ENT=	1<<4,
+
+	/* port A */
+	RXD1=	SIBIT(15),
+	TXD1=	SIBIT(14),
+
+	/* port B */
+	RTS1=	IBIT(19),
+
+	/* port C */
+	CTS1=	SIBIT(11),
+	CD1=	SIBIT(10),
+};
+
+typedef struct Etherparam Etherparam;
+struct Etherparam {
+	SCCparam;
+	ulong	c_pres;		/* preset CRC */
+	ulong	c_mask;		/* constant mask for CRC */
+	ulong	crcec;		/* CRC error counter */
+	ulong	alec;		/* alighnment error counter */
+	ulong	disfc;		/* discard frame counter */
+	ushort	pads;		/* short frame PAD characters */
+	ushort	ret_lim;	/* retry limit threshold */
+	ushort	ret_cnt;	/* retry limit counter */
+	ushort	mflr;		/* maximum frame length reg */
+	ushort	minflr;		/* minimum frame length reg */
+	ushort	maxd1;		/* maximum DMA1 length reg */
+	ushort	maxd2;		/* maximum DMA2 length reg */
+	ushort	maxd;		/* rx max DMA */
+	ushort	dma_cnt;	/* rx dma counter */
+	ushort	max_b;		/* max bd byte count */
+	ushort	gaddr[4];		/* group address filter */
+	ulong	tbuf0_data0;	/* save area 0 - current frm */
+	ulong	tbuf0_data1;	/* save area 1 - current frm */
+	ulong	tbuf0_rba0;
+	ulong	tbuf0_crc;
+	ushort	tbuf0_bcnt;
+	ushort	paddr[3];	/* physical address LSB to MSB increasing */
+	ushort	p_per;		/* persistence */
+	ushort	rfbd_ptr;	/* rx first bd pointer */
+	ushort	tfbd_ptr;	/* tx first bd pointer */
+	ushort	tlbd_ptr;	/* tx last bd pointer */
+	ulong	tbuf1_data0;	/* save area 0 - next frame */
+	ulong	tbuf1_data1;	/* save area 1 - next frame */
+	ulong	tbuf1_rba0;
+	ulong	tbuf1_crc;
+	ushort	tbuf1_bcnt;
+	ushort	tx_len;		/* tx frame length counter */
+	ushort	iaddr[4];		/* individual address filter*/
+	ushort	boff_cnt;	/* back-off counter */
+	ushort	taddr[3];	/* temp address */
+};
+
+typedef struct {
+	SCC*	scc;
+	int	port;
+	int	cpm;
+
+	BD*	rdr;				/* receive descriptor ring */
+	void*	rrb;				/* receive ring buffers */
+	int	rdrx;				/* index into rdr */
+
+	BD*	tdr;				/* transmit descriptor ring */
+	void*	trb;				/* transmit ring buffers */
+	int	tdrx;				/* index into tdr */
+} Mot;
+static Mot mot[MaxEther];
+
+static	int	sccid[] = {-1, SCC1ID, SCC2ID, SCC3ID, SCC4ID};
+static	int	sccparam[] = {-1, SCC1P, SCC2P, SCC3P, SCC4P};
+static	int	sccreg[] = {-1, 0xA00, 0xA20, 0xA40, 0xA60};
+static	int	sccirq[] = {-1, 0x1E, 0x1D, 0x1C, 0x1B};
+
+static void
+attach(Ctlr *ctlr)
+{
+	mot[ctlr->ctlrno].scc->gsmrl |= ENR|ENT;
+	eieio();
+}
+
+static void
+transmit(Ctlr *ctlr)
+{
+	int len;
+	Mot *motp;
+	Block *b;
+	BD *tdre;
+
+	motp = &mot[ctlr->ctlrno];
+	while(((tdre = &motp->tdr[motp->tdrx])->status & BDReady) == 0){
+		b = qget(ctlr->oq);
+		if(b == 0)
+			break;
+
+		/*
+		 * Copy the packet to the transmit buffer.
+		 */
+		len = BLEN(b);
+		memmove(KADDR(tdre->addr), b->rp, len);
+	
+		/*
+		 * Give ownership of the descriptor to the chip, increment the
+		 * software ring descriptor pointer and tell the chip to poll.
+		 */
+		tdre->length = len;
+		eieio();
+		tdre->status = (tdre->status & BDWrap) | BDReady|TxPad|BDInt|BDLast|TxTC;
+		eieio();
+		motp->scc->todr = 1<<15;	/* transmit now */
+		eieio();
+		motp->tdrx = NEXT(motp->tdrx, Ntdre);
+
+		freeb(b);
+	
+	}
+}
+
+static void
+interrupt(Ureg*, void *ap)
+{
+	int len, events, status;
+	Mot *motp;
+	BD *rdre;
+	Block *b;
+	Ctlr *ctlr;
+
+	ctlr = ap;
+	motp = &mot[ctlr->ctlrno];
+
+	/*
+	 * Acknowledge all interrupts and whine about those that shouldn't
+	 * happen.
+	 */
+	events = motp->scc->scce;
+	eieio();
+	motp->scc->scce = events;
+	eieio();
+	if(events & (TXE|BSY|RXB))
+		print("ETHER.SCC#%d: scce = 0x%uX\n", ctlr->ctlrno, events);
+	//print(" %ux|", events);
+	/*
+	 * Receiver interrupt: run round the descriptor ring logging
+	 * errors and passing valid receive data up to the higher levels
+	 * until we encounter a descriptor still owned by the chip.
+	 */
+	if(events & (RXF|RXB) || 1){
+		rdre = &motp->rdr[motp->rdrx];
+		while(((status = rdre->status) & BDEmpty) == 0){
+			if(status & RxError || (status & (BDFirst|BDLast)) != (BDFirst|BDLast)){
+				//if(status & RxBuff)
+				//	ctlr->buffs++;
+				if(status & (1<<2))
+					ctlr->crcs++;
+				if(status & (1<<1))
+					ctlr->overflows++;
+				//print("eth rx: %ux\n", status);
+				if(status & RxError)
+					print("~");
+				else if((status & BDLast) == 0)
+					print("@");
+			}
+			else{
+				/*
+				 * We have a packet. Read it into the next
+				 * free ring buffer, if any.
+				 */
+				len = rdre->length-4;
+				if((b = iallocb(len)) != 0){
+					memmove(b->wp, KADDR(rdre->addr), len);
+					b->wp += len;
+					etheriq(ctlr, b, 1);
+				}
+			}
+
+			/*
+			 * Finished with this descriptor, reinitialise it,
+			 * give it back to the chip, then on to the next...
+			 */
+			rdre->length = 0;
+			rdre->status = (rdre->status & BDWrap) | BDEmpty | BDInt;
+			eieio();
+
+			motp->rdrx = NEXT(motp->rdrx, Nrdre);
+			rdre = &motp->rdr[motp->rdrx];
+		}
+	}
+
+	/*
+	 * Transmitter interrupt: handle anything queued for a free descriptor.
+	 */
+	if(events & TXB)
+		transmit(ctlr);
+	if(events & TXE)
+		cpmop(RestartTx, motp->cpm, 0);
+}
+
+static void
+ringinit(Mot* motp)
+{
+	int i, x;
+
+	/*
+	 * Initialise the receive and transmit buffer rings. The ring
+	 * entries must be aligned on 16-byte boundaries.
+	 */
+	if(motp->rdr == 0)
+		motp->rdr = bdalloc(Nrdre);
+	if(motp->rrb == 0)
+		motp->rrb = ialloc(Nrdre*Bufsize, 0);
+	x = PADDR(motp->rrb);
+	for(i = 0; i < Nrdre; i++){
+		motp->rdr[i].length = 0;
+		motp->rdr[i].addr = x;
+		motp->rdr[i].status = BDEmpty|BDInt;
+		x += Bufsize;
+	}
+	motp->rdr[i-1].status |= BDWrap;
+	motp->rdrx = 0;
+
+	if(motp->tdr == 0)
+		motp->tdr = bdalloc(Ntdre);
+	if(motp->trb == 0)
+		motp->trb = ialloc(Ntdre*Bufsize, 0);
+	x = PADDR(motp->trb);
+	for(i = 0; i < Ntdre; i++){
+		motp->tdr[i].addr = x;
+		motp->tdr[i].length = 0;
+		motp->tdr[i].status = TxPad|BDInt|BDLast|TxTC;
+		x += Bufsize;
+	}
+	motp->tdr[i-1].status |= BDWrap;
+	motp->tdrx = 0;
+}
+
+/*
+ * This follows the MPC823 user guide: section16.9.23.7's initialisation sequence,
+ * except that it sets the right bits for the MPC823ADS board when SCC2 is used,
+ * and those for the 860/821 development board for SCC1.
+ */
+static void
+sccsetup(Mot *ctlr, SCC *scc, uchar *ea)
+{
+	int i, rcs, tcs, w;
+	Etherparam *p;
+	IMM *io;
+
+
+	i = 2*(ctlr->port-1);
+	io = ioplock();
+	w = (TXD1|RXD1)<<i;	/* TXDn and RXDn in port A */
+	io->papar |= w;	/* enable TXDn and RXDn pins */
+	io->padir &= ~w;
+	io->paodr &= ~w;	/* not open drain */
+
+	w = (CD1|CTS1)<<i;	/* CLSN and RENA: CDn and CTSn in port C */
+	io->pcpar &= ~w;	/* enable CLSN (CTSn) and RENA (CDn) */
+	io->pcdir &= ~w;
+	io->pcso |= w;
+	iopunlock();
+
+	/* clocks and transceiver control: details depend on the board's wiring */
+	archetherenable(ctlr->cpm, &rcs, &tcs);
+
+	sccnmsi(ctlr->port, rcs, tcs);	/* connect the clocks */
+
+	p = (Etherparam*)KADDR(sccparam[ctlr->port]);
+	memset(p, 0, sizeof(*p));
+	p->rfcr = 0x18;
+	p->tfcr = 0x18;
+	p->mrblr = Bufsize;
+	p->rbase = PADDR(ctlr->rdr);
+	p->tbase = PADDR(ctlr->tdr);
+
+	cpmop(InitRxTx, ctlr->cpm, 0);
+
+	p->c_pres = ~0;
+	p->c_mask = 0xDEBB20E3;
+	p->crcec = 0;
+	p->alec = 0;
+	p->disfc = 0;
+	p->pads = 0x8888;
+	p->ret_lim = 0xF;
+	p->mflr = Rbsize;
+	p->minflr = ETHERMINTU+4;
+	p->maxd1 = Bufsize;
+	p->maxd2 = Bufsize;
+	p->p_per = 0;	/* only moderate aggression */
+
+	for(i=0; i<Eaddrlen; i+=2)
+		p->paddr[2-i/2] = (ea[i+1]<<8)|ea[i];	/* it's not the obvious byte order */
+
+	scc->psmr = (2<<10)|(5<<1);	/* 32-bit CRC, ignore 22 bits before SFD */
+	scc->dsr = 0xd555;
+	scc->gsmrh = 0;	/* normal operation */
+	scc->gsmrl = (1<<28)|(4<<21)|(1<<19)|0xC;	/* transmit clock invert, 48 bit preamble, repetitive 10 preamble, ethernet */
+	eieio();
+	scc->scce = ~0;	/* clear all events */
+	eieio();
+	scc->sccm = TXE | RXF | TXB;	/* enable interrupts */
+	eieio();
+
+	io = ioplock();
+	w = RTS1<<(ctlr->port-1);	/* enable TENA pin (RTSn) */
+	io->pbpar |= w;
+	io->pbdir |= w;
+	iopunlock();
+
+	/* gsmrl enable is deferred until attach */
+}
+
+/*
+ * Prepare the SCCx ethernet for booting.
+ */
+int
+sccethreset(Ctlr* ctlr)
+{
+	uchar ea[Eaddrlen];
+	Mot *motp;
+	SCC *scc;
+	char line[50], def[50];
+
+	/*
+	 * Since there's no EPROM, insist that the configuration entry
+	 * (see conf.c and flash.c) holds the Ethernet address.
+	 */
+	memset(ea, 0, Eaddrlen);
+	if(memcmp(ea, ctlr->card.ea, Eaddrlen) == 0){
+		print("no preset Ether address\n");
+		for(;;){
+			strcpy(def, "00108bf12900");	/* valid MAC address to be used only for initial configuration */
+			if(getstr("ether MAC address", line, sizeof(line), def) < 0)
+				return -1;
+			if(parseether(ctlr->card.ea, line) >= 0 || ctlr->card.ea[0] == 0xFF)
+				break;
+			print("invalid MAC address\n");
+		}
+	}
+
+	scc = IOREGS(sccreg[ctlr->card.port], SCC);
+	ctlr->card.irq = VectorCPIC+sccirq[ctlr->card.port];
+
+	motp = &mot[ctlr->ctlrno];
+	motp->scc = scc;
+	motp->port = ctlr->card.port;
+	motp->cpm = sccid[ctlr->card.port];
+
+	ringinit(motp);
+
+	sccsetup(motp, scc, ctlr->card.ea);
+
+	/* enable is deferred until attach */
+
+	ctlr->card.reset = sccethreset;
+	ctlr->card.attach = attach;
+	ctlr->card.transmit = transmit;
+	ctlr->card.intr = interrupt;
+
+	return 0;
+}
--- /dev/null
+++ b/os/boot/mpc/fblt.c
@@ -1,0 +1,531 @@
+#include <u.h>
+#include <libc.h>
+#include <libg.h>
+#include <gnot.h>
+
+/*
+ * bitblt operates a 'word' at a time.
+ * WBITS is the number of bits in a word
+ * LWBITS=log2(WBITS),
+ * W2L is the number of words in a long
+ * WMASK has bits set for the low order word of a long
+ * WType is a pointer to a word
+ */
+#ifndef WBITS
+#define WBITS	32
+#define LWBITS	5
+#define	W2L	1
+#define WMASK	~0UL
+typedef ulong	*WType;
+#endif
+
+#define DEBUG 
+
+#ifdef TEST
+/*
+ * globals used for testing
+ */
+int	FORCEFORW;
+int	FORCEBAKW;
+GBitmap	*curdm, *cursm;
+Point	curpt;
+Rectangle curr;
+Fcode	curf;
+void	*mem;
+#endif
+
+static void
+gbitexplode(ulong sw, ulong *buf, int sdep, int x)
+{
+	int j, o, q, n, nw, inc, qinc;
+	ulong s, dw, pix;
+
+	inc = 1 << sdep;
+	pix = (1 << inc) - 1;
+	nw = 1 << x;
+	n = 32 >> x;
+	qinc = (nw << sdep) - inc;
+	for(o = 32 - n; o >= 0; o -= n){
+		dw = 0;
+		s = sw >> o;
+		q = 0;
+		for(j = 0; j < n; j += inc){
+			dw |= (s & (pix << j)) << q;
+			q += qinc;
+		}
+		for(j = 0; j < x; j++)
+			dw |= dw << (inc << j);
+		*buf++ = dw;
+	}
+}
+
+/*
+void
+main(void)
+{
+	ulong buf[128];
+
+	gbitexplode(0x7777, buf, 0, 3);
+	exits(0);
+}
+*/
+
+void
+gbitblt(GBitmap *dm, Point pt, GBitmap *sm, Rectangle r, Fcode fcode)
+{
+	int	width;		/* width in bits of dst */
+	int	wwidth;		/* floor width in words */
+	int	height;		/* height in pixels minus 1 */
+	int	sdep;		/* src ldepth */
+	int 	ddep;		/* dst ldepth */
+	int	deltadep;	/* diff between ldepths */
+	int	sspan;		/* words between scanlines in src */
+	int	dspan;		/* words between scanlines in dst */
+	int	soff;		/* bit offset of src start point */
+	int	sdest;		/* bit offset of src start point that matches doff when expanded */
+	int	doff;		/* bit offset of dst start point */
+	int	delta;		/* amount to shift src by */
+	int	sign;		/* of delta */
+	ulong	*saddr;
+	ulong	*daddr;
+	ulong	*s;
+	ulong	*d;
+	ulong	mask;
+	ulong	tmp;		/* temp storage source word */
+	ulong	sw;		/* source word constructed */
+	ulong	dw;		/* dest word fetched */
+	ulong	lmask;		/* affected pixels in leftmost dst word */
+	ulong	rmask;		/* affected pixels in rightmost dst word */
+	int	i;
+	int	j;
+	ulong	buf[32];	/* for expanding a source */
+	ulong	*p;		/* pointer into buf */
+	int	spare;		/* number of words already converted */
+
+
+#ifdef TEST
+	curdm = dm;
+	cursm = sm;
+	curpt = pt;
+	curr = r;
+	curf = fcode;
+#endif
+
+	gbitbltclip(&dm);
+
+	width = r.max.x - r.min.x;
+	if(width <= 0)
+		return;
+	height = r.max.y - r.min.y - 1;
+	if(height < 0)
+		return;
+
+	ddep = dm->ldepth;
+	pt.x <<= ddep;
+	width <<= ddep;
+
+	sdep = sm->ldepth;
+	r.min.x <<= sdep;
+	r.max.x <<= sdep;
+
+	dspan = dm->width * W2L;
+	sspan = sm->width * W2L;
+
+	daddr = (ulong*)((WType)dm->base
+			+ dm->zero*W2L + pt.y*dspan
+			+ (pt.x >> LWBITS));
+	saddr = (ulong*)((WType)sm->base
+			+ sm->zero*W2L + r.min.y*sspan
+			+ (r.min.x >> LWBITS));
+
+	doff = pt.x & (WBITS - 1);
+	lmask = WMASK >> doff;
+	rmask = (WMASK << (WBITS - ((doff+width) & (WBITS-1))))&WMASK;
+	if(!rmask)
+		rmask = WMASK;
+	soff = r.min.x & (WBITS-1);
+	wwidth = ((pt.x+width-1)>>LWBITS) - (pt.x>>LWBITS);
+
+	if(sm == dm){
+#ifdef TEST
+		if(!FORCEBAKW &&
+		   (FORCEFORW || sm != dm || saddr > daddr ||
+		    (saddr == daddr && soff > doff)))
+			;
+		else{
+			daddr += height * dspan;
+			saddr += height * sspan;
+			sspan -= 2 * W2L * sm->width;
+			dspan -= 2 * W2L * dm->width;
+		}
+#else
+		if(r.min.y < pt.y){	/* bottom to top */
+			daddr += height * dspan;
+			saddr += height * sspan;
+			sspan -= 2 * W2L * sm->width;
+			dspan -= 2 * W2L * dm->width;
+		}else if(r.min.y == pt.y && r.min.x < pt.x)
+			abort()/*goto right*/;
+#endif
+	}
+	if(wwidth == 0)		/* collapse masks for narrow cases */
+		lmask &= rmask;
+	fcode &= F;
+
+	deltadep = ddep - sdep;
+	sdest = doff >> deltadep;
+	delta = soff - sdest;
+	sign = 0;
+	if(delta < 0){
+		sign = 1;
+		delta = -delta;
+	}
+
+	p = 0;
+	for(j = 0; j <= height; j++){
+		d = daddr;
+		s = saddr;
+		mask = lmask;
+		tmp = 0;
+		if(!sign)
+			tmp = *s++;
+		spare = 0;
+		for(i = wwidth; i >= 0; i--){
+			if(spare)
+				sw = *p++;
+			else{
+				if(sign){
+					sw = tmp << (WBITS-delta);
+					tmp = *s++;
+					sw |= tmp >> delta;
+				}else{
+					sw = tmp << delta;
+					tmp = *s++;
+					if(delta)
+						sw |= tmp >> (WBITS-delta);
+				}
+				spare = 1 << deltadep;
+				if(deltadep >= 1){
+					gbitexplode(sw, buf, sdep, deltadep);
+					p = buf;
+					sw = *p++;
+				}
+			}
+
+			dw = *d;
+			switch(fcode){		/* ltor bit aligned */
+			case Zero:	*d = dw & ~mask;		break;
+			case DnorS:	*d = dw ^ ((~sw | dw) & mask);	break;
+			case DandnotS:	*d = dw ^ ((sw & dw) & mask);	break;
+			case notS:	*d = dw ^ ((~sw ^ dw) & mask);	break;
+			case notDandS:	*d = dw ^ ((sw | dw) & mask);	break;
+			case notD:	*d = dw ^ mask;			break;
+			case DxorS:	*d = dw ^ (sw & mask);		break;
+			case DnandS:	*d = dw ^ ((sw | ~dw) & mask);	break;
+			case DandS:	*d = dw ^ ((~sw & dw) & mask);	break;
+			case DxnorS:	*d = dw ^ (~sw & mask);		break;
+			case D:						break;
+			case DornotS:	*d = dw | (~sw & mask);		break;
+			case S:		*d = dw ^ ((sw ^ dw) & mask);	break;
+			case notDorS:	*d = dw ^ (~(sw & dw) & mask);	break;
+			case DorS:	*d = dw | (sw & mask);		break;
+			case F:		*d = dw | mask;			break;
+			}
+			d++;
+
+			mask = WMASK;
+			if(i == 1)
+				mask = rmask;
+			spare--;
+		}
+		saddr += sspan;
+		daddr += dspan;
+	}
+}
+
+#ifdef TEST
+void	prprog(void);
+GBitmap *bb1, *bb2;
+ulong	*src, *dst, *xdst, *xans;
+int	swds, dwds;
+long	ticks;
+int	timeit;
+
+long
+func(int f, long s, int sld, long d, int dld)
+{
+	long a;
+	int sh, i, db, sb;
+
+	db = 1 << dld;
+	sb = 1 << sld;
+	sh = db - sb;
+	if(sh > 0) {
+		a = s;
+		for(i = sb; i<db; i += sb){
+			a <<= sb;
+			s |= a;
+		}
+	} else if(sh < 0)
+		s >>= -sh;
+
+	switch(f){
+	case Zero:	d = 0;			break;
+	case DnorS:	d = ~(d|s);		break;
+	case DandnotS:	d = d & ~s;		break;
+	case notS:	d = ~s;			break;
+	case notDandS:	d = ~d & s;		break;
+	case notD:	d = ~d;			break;
+	case DxorS:	d = d ^ s;		break;
+	case DnandS:	d = ~(d&s);		break;
+	case DandS:	d = d & s;		break;
+	case DxnorS:	d = ~(d^s);		break;
+	case S:		d = s;			break;
+	case DornotS:	d = d | ~s;		break;
+	case D:		d = d;			break;
+	case notDorS:	d = ~d | s;		break;
+	case DorS:	d = d | s;		break;
+	case F:		d = ~0;			break;
+	}
+
+	d &= ((1<<db)-1);
+	return d;
+}
+
+void
+run(int fr, int to, int w, int op)
+{
+	int i, j, f, t, fy, ty;
+	extern long *_clock;
+
+	fr += bb2->r.min.x;
+	to += bb1->r.min.x;
+	fy = bb2->r.min.y + 1;
+	ty = bb1->r.min.y + 1;
+	if(timeit) {
+		memcpy(dst, xdst, dwds * sizeof(long));
+		ticks -= *_clock;
+		gbitblt(bb1, Pt(to,ty), bb2, Rect(fr,fy,fr+w,fy+2), op);
+		ticks += *_clock;
+		return;
+	}
+	f = fr;
+	t = to;
+	memcpy(dst, xdst, dwds * sizeof(long));
+	for(i=0; i<w; i++) {
+		gbitblt(bb1, Pt(t,ty), bb2, Rect(f,fy,f+1,fy+1), op);
+		gbitblt(bb1, Pt(t,ty+1), bb2, Rect(f,fy+1,f+1,fy+2), op);
+		f++;
+		t++;
+	}
+	memcpy(xans, dst, dwds * sizeof(long));
+
+	memcpy(dst, xdst, dwds * sizeof(long));
+	gbitblt(bb1, Pt(to,ty), bb2, Rect(fr,fy,fr+w,fy+2), op);
+
+	if(memcmp(xans, dst, dwds * sizeof(long))) {
+		/*
+		 * print src and dst row offset, width in bits, and forw/back
+		 * then print for each of the four rows: the source (s),
+		 * the dest (d), the good value of the answer (g),
+		 * and the actual bad value of the answer (b)
+		 */
+		print("fr=%d to=%d w=%d fb=%d%d\n",
+			fr, to, w, FORCEFORW, FORCEBAKW);
+		print("dst bitmap b %#lux, z %d, w %d, ld %d, r [%d,%d][%d,%d]\n",
+			bb1->base, bb1->zero, bb1->width, bb1->ldepth,
+			bb1->r.min.x, bb1->r.min.y, bb1->r.max.x, bb1->r.max.y);
+		print("src bitmap b %#lux, z %d, w %d, ld %d, r [%d,%d][%d,%d]\n",
+			bb2->base, bb2->zero, bb2->width, bb2->ldepth,
+			bb2->r.min.x, bb2->r.min.y, bb2->r.max.x, bb2->r.max.y);
+		for(j=0; 7*j < dwds; j++) {
+			print("\ns");
+			for(i=0; i<7 && 7*j+i < dwds; i++)
+				print(" %.8lux", src[7*j + i]);
+			print("\nd");
+			for(i=0; i<7 && 7*j+i < dwds; i++)
+				print(" %.8lux", xdst[7*j + i]);
+			print("\ng");
+			for(i=0; i<7 && 7*j+i < dwds; i++)
+				print(" %.8lux", xans[7*j + i]);
+			print("\nb");
+			for(i=0; i<7 && 7*j+i < dwds; i++)
+				print(" %.8lux", dst[7*j + i]);
+			print("\n");
+		}
+		prprog();
+	}
+}
+
+void
+prprog(void)
+{
+	exits(0);
+}
+
+int
+main(int argc, char *argv[])
+{
+	int f, t, w, i, sld, dld, op, iters, simple;
+	ulong s, d, spix, dpix, apix, fpix, m, *ps, *pd;
+	Point sorg, dorg;
+	GBitmap *bs, *bd;
+	long seed;
+	char *ct;
+
+	sld = 0;
+	dld = 0;
+	timeit = 0;
+	iters = 200;
+	simple = 0;
+	ARGBEGIN {
+	case 'i':
+		iters = atoi(ARGF());
+		break;
+	case 's':
+		simple = 1;
+		break;
+	case 't':
+		timeit = 1;
+		ct = ARGF();
+		if(ct)
+			iters = atoi(ct);
+		break;
+	} ARGEND
+	if(argc > 0)
+		sld = atoi(argv[0]);
+	if(argc > 1)
+		dld = atoi(argv[1]);
+	if(!timeit && !simple) {
+		seed = time(0);
+		print("seed %lux\n", seed); srand(seed);	/**/
+	}
+
+	print("sld %d dld %d\n", sld, dld);
+	op = 1;
+
+	/* bitmaps for 1-bit tests */
+	bd = gballoc(Rect(0,0,32,1), dld);
+	bs = gballoc(Rect(0,0,32,1), sld);
+	for(i=0; i<bs->width; i++)
+		bs->base[i] = lrand();
+
+	/* bitmaps for rect tests */
+	if(simple) {
+		dorg = Pt(0,0);
+		sorg = Pt(0,0);
+	} else {
+		dorg = Pt(nrand(63)-31,nrand(63)-31);
+		sorg = Pt(nrand(63)-31,nrand(63)-31);
+	}
+	bb1 = gballoc(Rpt(dorg,add(dorg,Pt(200,4))), dld);
+	bb2 = gballoc(Rpt(sorg,add(sorg,Pt(200,4))), sld);
+	dwds = bb1->width * Dy(bb1->r);
+	swds = bb2->width * Dy(bb2->r);
+	dst = bb1->base;
+	src = bb2->base;
+	xdst = malloc(dwds * sizeof(long));
+	xans =  malloc(dwds * sizeof(long));
+	for(i=0; i<swds; i++)
+		src[i] = lrand();
+	for(i=0; i<dwds; i++)
+		xdst[i] = lrand();
+
+loop:
+	print("Op %d\n", op);
+	if(!timeit) {
+		print("one pixel\n");
+		ps = bs->base;
+		pd = bd->base;
+		FORCEFORW = 1;
+		FORCEBAKW = 0;
+		for(i=0; i<1000; i++, FORCEFORW = !FORCEFORW, FORCEBAKW = !FORCEBAKW) {
+			f = nrand(32 >> sld);
+			t = nrand(32 >> dld);
+			s = lrand();
+			d = lrand();
+			ps[0] = s;
+			pd[0] = d;
+#ifdef T386
+			spix = (byterev(s) >> (32 - ((f+1)<<sld))) & ((1 << (1<<sld)) - 1);
+			dpix = (byterev(d) >> (32 - ((t+1)<<dld))) & ((1 << (1<<dld)) - 1);
+#else
+			spix = (s >> (32 - ((f+1)<<sld))) & ((1 << (1<<sld)) - 1);
+			dpix = (d >> (32 - ((t+1)<<dld))) & ((1 << (1<<dld)) - 1);
+#endif
+#ifdef T386
+			apix = byterev(func(op, spix, sld, dpix, dld) << (32 - ((t+1)<<dld)));
+#else
+			apix = func(op, spix, sld, dpix, dld) << (32 - ((t+1)<<dld));
+#endif
+			gbitblt(bd, Pt(t,0), bs, Rect(f,0,f+1,1), op);
+			if(ps[0] != s) {
+				print("bb src %.8lux %.8lux %d %d\n", ps[0], s, f, t);
+				exits("error");
+			}
+			m = ((1 << (1<<dld)) - 1) << (32 - ((t+1)<<dld));
+#ifdef T386
+			m = byterev(m);
+#endif
+			if((pd[0] & ~m) != (d & ~m)) {
+					print("bb dst1 %.8lux %.8lux\n",
+						s, d);
+					print("bb      %.8lux %.8lux %d %d\n",
+						ps[0], pd[0], f, t);
+					prprog();
+					exits("error");
+			}
+			if((pd[0] & m) != apix) {
+				spix <<= 32 - ((f+1)<<sld);
+				dpix <<= 32 - ((t+1)<<dld);
+#ifdef T386
+				spix = byterev(spix);
+				dpix = byterev(dpix);
+#endif
+				print("bb dst2 %.8lux %.8lux\n",
+					s, d);
+				print("bb      %.8lux %.8lux %d %d\n",
+					ps[0], pd[0], f, t);
+				print("bb      %.8lux %.8lux %.8lux %.8lux\n",
+					spix, dpix, apix, pd[0] & m);
+				prprog();
+				exits("error");
+			}
+		}
+	}
+
+	print("for\n");
+	FORCEFORW = 1;
+	FORCEBAKW = 0;
+
+	for(i=0; i<iters; i++) {
+		f = nrand(64);
+		t = nrand(64);
+		w = nrand(130);
+		run(f, t, w, op);
+	}
+
+	if(sld == dld) {
+		print("bak\n");
+		FORCEFORW = 0;
+		FORCEBAKW = 1;
+	
+		for(i=0; i<iters; i++) {
+			f = nrand(64);
+			t = nrand(64);
+			w = nrand(130);
+			run(f, t, w, op);
+		}
+	}
+
+	if(op < F) {
+		op++;
+		goto loop;
+	}
+	if(timeit)
+		print("time: %d ticks\n", ticks);
+	exits(0);
+}
+
+
+#endif
--- /dev/null
+++ b/os/boot/mpc/flash.c
@@ -1,0 +1,212 @@
+#include "boot.h"
+
+typedef struct Flashdev Flashdev;
+struct Flashdev {
+	uchar*	base;
+	int	size;
+	uchar*	exec;
+	char*	config;
+	int	conflen;
+};
+
+enum {
+	FLASHSEG = 256*1024,
+	CONFIGLIM = FLASHSEG,
+	BOOTOFF = FLASHSEG,
+	BOOTLEN = 3*FLASHSEG,	/* third segment might be filsys */
+	/* rest of flash is free */
+};
+
+static Flashdev flash;
+
+/*
+ * configuration data is written between the bootstrap and
+ * the end of region 0. the region ends with allocation descriptors
+ * of the following form:
+ *
+ * byte order is big endian
+ *
+ * the last valid region found that starts with the string "#plan9.ini\n" is plan9.ini
+ */
+typedef struct Flalloc Flalloc;
+struct Flalloc {
+	ulong	check;	/* checksum of data, or ~0 */
+	ulong	base;	/* base of region; ~0 if unallocated, 0 if deleted */
+	uchar	len[3];
+	uchar	tag;		/* see below */
+	uchar	sig[4];
+};
+
+enum {
+	/* tags */
+	Tdead=	0,
+	Tboot=	0x01,	/* space reserved for boot */
+	Tconf=	0x02,	/* configuration data */
+	Tnone=	0xFF,
+
+	Noval=	~0,
+};
+
+static char flashsig[] = {0xF1, 0xA5, 0x5A, 0x1F};
+static char conftag[] = "#plan9.ini\n";
+
+static ulong
+checksum(uchar* p, int n)
+{
+	ulong s;
+
+	for(s=0; --n >= 0;)
+		s += *p++;
+	return s;
+}
+
+static int
+validptr(Flalloc *ap, uchar *p)
+{
+	return p > (uchar*)&end && p < (uchar*)ap;
+}
+
+static int
+flashcheck(Flalloc *ap, char **val, int *len)
+{
+	uchar *base;
+	int n;
+
+	if(ap->base == Noval || ap->base >= FLASHSEG || ap->tag == Tnone)
+		return 0;
+	base = flash.base+ap->base;
+	if(!validptr(ap, base))
+		return 0;
+	n = (((ap->len[0]<<8)|ap->len[1])<<8)|ap->len[2];
+	if(n == 0xFFFFFF)
+		n = 0;
+	if(n < 0)
+		return 0;
+	if(n > 0 && !validptr(ap, base+n-1))
+		return 0;
+	if(ap->check != Noval && checksum(base, n) != ap->check){
+		print("flash: bad checksum\n");
+		return 0;
+	}
+	*val = (char*)base;
+	*len = n;
+	return 1;
+}
+
+int
+flashinit(void)
+{
+	int len;
+	char *val;
+	Flalloc *ap;
+	void *addr;
+	long mbytes;
+	char type[20];
+
+	flash.base = 0;
+	flash.exec = 0;
+	flash.size = 0;
+	if(archflashreset(type, &addr, &mbytes) < 0){
+		print("flash: flash not present or not enabled\n");	/* shouldn't happen */
+		return 0;
+	}
+	flash.size = mbytes;
+	flash.base = addr;
+	flash.exec = flash.base + BOOTOFF;
+	flash.config = nil;
+	flash.conflen = 0;
+
+	for(ap = (Flalloc*)(flash.base+CONFIGLIM)-1; memcmp(ap->sig, flashsig, 4) == 0; ap--){
+		if(0)
+			print("conf #%8.8lux: #%x #%6.6lux\n", ap, ap->tag, ap->base);
+		if(ap->tag == Tconf &&
+		   flashcheck(ap, &val, &len) &&
+		   len >= sizeof(conftag)-1 &&
+		   memcmp(val, conftag, sizeof(conftag)-1) == 0){
+			flash.config = val;
+			flash.conflen = len;
+			if(0)
+				print("flash: found config %8.8lux(%d):\n%s\n", val, len, val);
+		}
+	}
+	if(flash.config == nil)
+		print("flash: no config\n");
+	else
+		print("flash config %8.8lux(%d):\n%s\n", flash.config, flash.conflen, flash.config);
+	if(issqueezed(flash.exec) == Q_MAGIC){
+		print("flash: squeezed powerpc kernel installed\n");
+		return 1<<0;
+	}
+	if(GLLONG(flash.exec) == Q_MAGIC){
+		print("flash: unsqueezed powerpc kernel installed\n");
+		return 1<<0;
+	}
+	flash.exec = 0;
+	print("flash: no powerpc kernel in Flash\n");
+	return 0;
+}
+
+char*
+flashconfig(int)
+{
+	return flash.config;
+}
+
+int
+flashbootable(int)
+{
+	return flash.exec != nil && (issqueezed(flash.exec) || GLLONG(flash.exec) == Q_MAGIC);
+}
+
+int
+flashboot(int)
+{
+	ulong entry, addr;
+	void (*b)(void);
+	Exec *ep;
+	Block in;
+	long n;
+	uchar *p;
+
+	if(flash.exec == 0)
+		return -1;
+	p = flash.exec;
+	if(GLLONG(p) == Q_MAGIC){
+		/* unsqueezed: copy data and perhaps text, then jump to it */
+		ep = (Exec*)p;
+		entry = PADDR(GLLONG(ep->entry));
+		p += sizeof(Exec);
+		addr = entry;
+		n = GLLONG(ep->text);
+		if(addr != (ulong)p){
+			memmove((void*)addr, p, n);
+			print("text: %8.8lux <- %8.8lux [%ld]\n", addr, p, n);
+		}
+		p += n;
+		if(entry >= FLASHMEM)
+			addr = 3*BY2PG;	/* kernel text is in Flash, data in RAM */
+		else
+			addr = PGROUND(addr+n);
+		n = GLLONG(ep->data);
+		memmove((void*)addr, p, n);
+		print("data: %8.8lux <- %8.8lux [%ld]\n", addr, p, n);
+	}else{
+		in.data = p;
+		in.rp = in.data;
+		in.lim = p+BOOTLEN;
+		in.wp = in.lim;
+		n = unsqueezef(&in, &entry);
+		if(n < 0)
+			return -1;
+	}
+	print("entry=0x%lux\n", entry);
+	uartwait();
+	scc2stop();
+	/*
+	 *  Go to new code. It's up to the program to get its PC relocated to
+	 *  the right place.
+	 */
+	b = (void (*)(void))KADDR(PADDR(entry));
+	(*b)();
+	return -1;
+}
--- /dev/null
+++ b/os/boot/mpc/fns.h
@@ -1,0 +1,117 @@
+Alarm*	alarm(int, void (*)(Alarm*), void*);
+void	alarminit(void);
+void	archbacklight(int);
+char*	archconfig(void);
+void	archdisableuart(int);
+void	archenableuart(int, int);
+void	archenableusb(int);
+void	archetherdisable(int);
+int	archetherenable(int, int*, int*);
+int	archflashreset(char*, void**, long*);
+void	archinit(void);
+int	archoptionsw(void);
+int	bootp(int, char*);
+void	cancel(Alarm*);
+void	checkalarms(void);
+void	clockinit(void);
+void	clockintr(Ureg*, void*);
+void	consinit(void);
+void	cpminit(void);
+void	cpuidprint(void);
+#define	dcflush(a,b)
+void	delay(int);
+void	eieio(void);
+uchar*	etheraddr(int);
+int	etherinit(void);
+int	etherrxpkt(int, Etherpkt*, int);
+int	ethertxpkt(int, Etherpkt*, int, int);
+void	exception(void);
+int	flashboot(int);
+int	flashbootable(int);
+char*	flashconfig(int);
+int	flashinit(void);
+void	free(void*);
+void	freeb(Block*);
+int	getcfields(char*, char**, int, char*);
+char*	getconf(char*);
+ulong	getdec(void);
+ulong	gethid0(void);
+ulong	getimmr(void);
+ulong	getmsr(void);
+ulong	getpvr(void);
+int	getstr(char*, char*, int, char*);
+ulong	gettbl(void);
+ulong	gettbu(void);
+int	hardinit(void);
+long	hardread(int, void*, long);
+long	hardseek(int, long);
+long	hardwrite(int, void*, long);
+long	i2csend(int, void*, long);
+void	i2csetup(void);
+void*	ialloc(ulong, int);
+Block*	iallocb(int);
+void	idle(void);
+int	isaconfig(char*, int, ISAConf*);
+int	issqueezed(uchar*);
+void	kbdchar(Queue*, int);
+void	kbdinit(void);
+void	kbdreset(void);
+void	machinit(void);
+void*	malloc(ulong);
+ulong	mapalloc(RMap*, ulong, int, int);
+void	mapfree(RMap*, ulong, int);
+void	mapinit(RMap*, Map*, int);
+void	meminit(void);
+void	microdelay(int);
+void	mmuinit(void);
+int	optionsw(void);
+void	panic(char*, ...);
+int	parseether(uchar*, char*);
+int	plan9boot(int, long (*)(int, long), long (*)(int, void*, long));
+void	putdec(ulong);
+void	puthid0(ulong);
+void	putmsr(ulong);
+int	qbgetc(Queue*);
+void	qbputc(Queue*, int);
+void	qbwrite(Queue*, Block*);
+Block*	qget(Queue*);
+long	qlen(Queue*);
+Queue*	qopen(int, int, void (*)(void*), void*);
+#define	qpass	qbwrite
+void	scc2stop(void);
+void	sccnmsi(int, int, int);
+void	sched(void);
+void	screeninit(void);
+void	screenputs(char*, int);
+void	sdraminit(ulong);
+Partition*	sethardpart(int, char*);
+Partition*	setscsipart(int, char*);
+void	setvec(int, void (*)(Ureg*, void*), void*);
+int	splhi(void);
+int	spllo(void);
+void	splx(int);
+void	trapinit(void);
+void	uartputs(char*, int);
+void	uartsetboot(void (*f)(uchar*, int));
+void	uartspecial(int, int, Queue**, Queue**, void(*)(Queue*,int));
+void	uartwait(void);
+long	unsqueezef(Block*, ulong*);
+
+#define	GSHORT(p)	(((p)[1]<<8)|(p)[0])
+#define	GLONG(p)	((GSHORT(p+2)<<16)|GSHORT(p))
+#define	GLSHORT(p)	(((p)[0]<<8)|(p)[1])
+#define	GLLONG(p)	((GLSHORT(p)<<16)|GLSHORT(p+2))
+
+#define KADDR(a)	((void*)((ulong)(a)|KZERO))
+#define PADDR(a)	((ulong)(a)&~KSEGM)
+
+/* IBM bit field order */
+#define	IBIT(b)	((ulong)1<<(31-(b)))
+#define	SIBIT(n)	((ushort)1<<(15-(n)))
+
+#define IOREGS(x, T)	((T*)((char*)m->iomem+(x)))
+
+int	uartinit(void);
+Partition*	setuartpart(int, char*);
+long	uartread(int, void*, long);
+long	uartseek(int, long);
--- /dev/null
+++ b/os/boot/mpc/gbitbltclip.c
@@ -1,0 +1,52 @@
+#include <u.h>
+#include <libc.h>
+#include <libg.h>
+#include <gnot.h>
+
+void
+gbitbltclip(void *vp)
+{
+	int dx, dy;
+	int i;
+	struct{
+		GBitmap *dm;
+		Point p;
+		GBitmap *sm;
+		Rectangle r;
+		Fcode f;
+	}*bp;
+
+	bp = vp;
+	dx = Dx(bp->r);
+	dy = Dy(bp->r);
+	if(bp->p.x < bp->dm->clipr.min.x){
+		i = bp->dm->clipr.min.x-bp->p.x;
+		bp->r.min.x += i;
+		bp->p.x += i;
+		dx -= i;
+	}
+	if(bp->p.y < bp->dm->clipr.min.y){
+		i = bp->dm->clipr.min.y-bp->p.y;
+		bp->r.min.y += i;
+		bp->p.y += i;
+		dy -= i;
+	}
+	if(bp->p.x+dx > bp->dm->clipr.max.x)
+		bp->r.max.x -= bp->p.x+dx-bp->dm->clipr.max.x;
+	if(bp->p.y+dy > bp->dm->clipr.max.y)
+		bp->r.max.y -= bp->p.y+dy-bp->dm->clipr.max.y;
+	if(bp->r.min.x < bp->sm->clipr.min.x){
+		i = bp->sm->clipr.min.x-bp->r.min.x;
+		bp->p.x += i;
+		bp->r.min.x += i;
+	}
+	if(bp->r.min.y < bp->sm->clipr.min.y){
+		i = bp->sm->clipr.min.y-bp->r.min.y;
+		bp->p.y += i;
+		bp->r.min.y += i;
+	}
+	if(bp->r.max.x > bp->sm->clipr.max.x)
+		bp->r.max.x = bp->sm->clipr.max.x;
+	if(bp->r.max.y > bp->sm->clipr.max.y)
+		bp->r.max.y = bp->sm->clipr.max.y;
+}
--- /dev/null
+++ b/os/boot/mpc/gnot.h
@@ -1,0 +1,71 @@
+
+extern void	*bbmalloc(int);
+extern void	bbfree(void *, int);
+extern int	bbonstack(void);
+extern void	bbexec(void(*)(void), int, int);
+
+/*
+ * Graphics types
+ */
+
+typedef	struct	GBitmap		GBitmap;
+typedef struct	GFont		GFont;
+typedef struct	GSubfont	GSubfont;
+typedef struct	GCacheinfo	GCacheinfo;
+
+struct	GBitmap
+{
+	ulong	*base;		/* pointer to start of data */
+	long	zero;		/* base+zero=&word containing (0,0) */
+	ulong	width;		/* width in 32 bit words of total data area */
+	int	ldepth;		/* log base 2 of number of bits per pixel */
+	Rectangle r;		/* rectangle in data area, local coords */
+	Rectangle clipr;	/* clipping region */
+	GBitmap	*cache;		/* zero; distinguishes bitmap from layer */
+};
+
+
+/*
+ * GFont etc. are not used in the library, only in devbit.c.
+ * GSubfont is only barely used.
+ */
+struct	GSubfont
+{
+	short	n;		/* number of chars in font */
+	char	height;		/* height of bitmap */
+	char	ascent;		/* top of bitmap to baseline */
+	Fontchar *info;		/* n+1 character descriptors */
+	GBitmap	*bits;		/* where the characters are */
+};
+struct GCacheinfo
+{
+	ulong		xright;	/* right edge of bits */
+	Fontchar;
+};
+
+struct GFont
+{
+	uchar		height;	/* max height of bitmap, interline spacing */
+	char		ascent;	/* top of bitmap to baseline */
+	char		width;	/* widest so far; used in caching only */	
+	char		ldepth;	/* of images */
+	short		id;	/* of font */
+	int		ncache;	/* number of entries in cache */
+	GCacheinfo	*cache;	/* cached characters */
+	GBitmap		*b;	/* cached images */
+};
+
+extern ulong	 *gaddr(GBitmap*, Point);
+extern uchar	 *gbaddr(GBitmap*, Point);
+extern void	 gbitblt(GBitmap*, Point, GBitmap*, Rectangle, Fcode);
+extern void	 gbitbltclip(void*);
+extern void	 gtexture(GBitmap*, Rectangle, GBitmap*, Fcode);
+extern Point	 gsubfstrsize(GSubfont*, char*);
+extern int	 gsubfstrwidth(GSubfont*, char*);
+extern Point	 gsubfstring(GBitmap*, Point, GSubfont*, char*, Fcode);
+extern Point	 gbitbltstring(GBitmap*, Point, GSubfont*, char*, Fcode);
+extern void	 gsegment(GBitmap*, Point, Point, int, Fcode);
+extern void	 gpoint(GBitmap*, Point, int, Fcode);
+extern void	 gflushcpucache(void);
+extern GBitmap*	 gballoc(Rectangle, int);
+extern void	 gbfree(GBitmap*);
--- /dev/null
+++ b/os/boot/mpc/i2c.c
@@ -1,0 +1,351 @@
+#include "boot.h"
+
+/*
+ * basic read/write interface to mpc8xx I2C bus (master mode)
+ */
+
+typedef struct I2C I2C;
+
+struct I2C {
+	uchar	i2mod;
+	uchar	rsv12a[3];
+	uchar	i2add;
+	uchar	rsv12b[3];
+	uchar	i2brg;
+	uchar	rsv12c[3];
+	uchar	i2com;
+	uchar	rsv12d[3];
+	uchar	i2cer;
+	uchar	rsv12e[3];
+	uchar	i2cmr;
+};
+
+enum {
+	/* i2c-specific BD flags */
+	RxeOV=		1<<1,	/* overrun */
+	TxS=			1<<10,	/* transmit start condition */
+	TxeNAK=		1<<2,	/* last transmitted byte not acknowledged */
+	TxeUN=		1<<1,	/* underflow */
+	TxeCL=		1<<0,	/* collision */
+	TxERR=		(TxeNAK|TxeUN|TxeCL),
+
+	/* i2cmod */
+	REVD=	1<<5,	/* =1, LSB first */
+	GCD=	1<<4,	/* =1, general call address disabled */
+	FLT=		1<<3,	/* =0, not filtered; =1, filtered */
+	PDIV=	3<<1,	/* predivisor field */
+	EN=		1<<0,	/* enable */
+
+	/* i2com */
+	STR=		1<<7,	/* start transmit */
+	I2CM=	1<<0,	/* master */
+	I2CS=	0<<0,	/* slave */
+
+	/* i2cer */
+	TXE =	1<<4,
+	BSY =	1<<2,
+	TXB =	1<<1,
+	RXB =	1<<0,
+
+	/* port B bits */
+	I2CSDA =	IBIT(27),
+	I2CSCL = IBIT(26),
+
+	Rbit =	1<<0,	/* bit in address byte denoting read */
+
+	/* maximum I2C I/O (can change) */
+	Bufsize =	64,
+	Tbuflen=	Bufsize+4,	/* extra address bytes and alignment */
+	Freq =	100000,
+	I2CTimeout = 250,	/* msec */
+};
+
+/* data cache needn't be flushed if buffers allocated in uncached INTMEM */
+#define	DCFLUSH(a,n)
+
+/*
+ * I2C software structures
+ */
+
+struct Ctlr {
+	Lock;
+	QLock	io;
+	int	init;
+	I2C*	i2c;
+	IOCparam*	sp;
+
+	BD*	rd;
+	BD*	td;
+	int	phase;
+	char*	addr;
+	char*	txbuf;
+	char*	rxbuf;
+};
+typedef struct Ctlr Ctlr;
+
+static	Ctlr	i2ctlr[1];
+extern	int	predawn;
+
+static	void	interrupt(Ureg*, void*);
+
+static void
+enable(void)
+{
+	I2C *i2c;
+
+	i2c = i2ctlr->i2c;
+	i2c->i2cer = ~0;	/* clear events */
+	eieio();
+	i2c->i2mod |= EN;
+	eieio();
+	i2c->i2cmr = TXE|BSY|TXB|RXB;	/* enable all interrupts */
+	eieio();
+}
+
+static void
+disable(void)
+{
+	I2C *i2c;
+
+	i2c = i2ctlr->i2c;
+	i2c->i2cmr = 0;	/* mask all interrupts */
+	i2c->i2mod &= ~EN;
+}
+
+/*
+ * called by the reset routine of any driver using the I2C
+ */
+void
+i2csetup(void)
+{
+	IMM *io;
+	I2C *i2c;
+	IOCparam *sp;
+	Ctlr *ctlr;
+	long f, e, emin;
+	int p, d, dmax;
+
+	ctlr = i2ctlr;
+	if(ctlr->init)
+		return;
+	print("i2c setup...\n");
+	ctlr->init = 1;
+	i2c = KADDR(INTMEM+0x860);
+	ctlr->i2c = i2c;
+	sp = KADDR(INTMEM+0x3c80);
+	ctlr->sp = sp;
+	disable();
+
+	if(ctlr->txbuf == nil){
+		ctlr->txbuf = ialloc(Tbuflen, 2);
+		ctlr->addr = ctlr->txbuf+Bufsize;
+	}
+	if(ctlr->rxbuf == nil)
+		ctlr->rxbuf = ialloc(Bufsize, 2);
+	if(ctlr->rd == nil){
+		ctlr->rd = bdalloc(1);
+		ctlr->rd->addr = PADDR(ctlr->rxbuf);
+		ctlr->rd->length = 0;
+		ctlr->rd->status = BDWrap;
+	}
+	if(ctlr->td == nil){
+		ctlr->td = bdalloc(2);
+		ctlr->td->addr = PADDR(ctlr->txbuf);
+		ctlr->td->length = 0;
+		ctlr->td->status = BDWrap|BDLast;
+	}
+
+	/* select port pins */
+	io = ioplock();
+	io->pbdir |= I2CSDA | I2CSCL;
+	io->pbodr |= I2CSDA | I2CSCL;
+	io->pbpar |= I2CSDA | I2CSCL;
+	iopunlock();
+
+	/* explicitly initialise parameters, because InitRxTx can't be used (see i2c/spi relocation errata) */
+	sp = ctlr->sp;
+	sp->rbase = PADDR(ctlr->rd);
+	sp->tbase = PADDR(ctlr->td);
+	sp->rfcr = 0x18;
+	sp->tfcr = 0x18;
+	sp->mrblr = Bufsize;
+	sp->rstate = 0;
+	sp->rptr = 0;
+	sp->rbptr = sp->rbase;
+	sp->rcnt = 0;
+	sp->tstate = 0;
+	sp->tbptr = sp->tbase;
+	sp->tptr = 0;
+	sp->tcnt = 0;
+	eieio();
+
+	i2c->i2com = I2CM;
+	i2c->i2mod = 0;	/* normal mode */
+	i2c->i2add = 0;
+
+	emin = Freq;
+	dmax = (m->cpuhz/Freq)/2-3;
+	for(d=0; d < dmax; d++){
+		for(p=3; p>=0; p--){
+			f = (m->cpuhz>>(p+2))/(2*(d+3));
+			e = Freq - f;
+			if(e < 0)
+				e = -e;
+			if(e < emin){
+				emin = e;
+				i2c->i2brg = d;
+				i2c->i2mod = (i2c->i2mod&~PDIV)|((3-p)<<1); /* set PDIV */
+			}
+		}
+	}
+	//print("i2brg=%d i2mod=#%2.2ux\n", i2c->i2brg, i2c->i2mod);
+	setvec(VectorCPIC+0x10, interrupt, i2ctlr);
+}
+
+enum {
+	Idling,
+	Done,
+	Busy,
+		Sending,
+		Recving,
+};
+
+static void
+interrupt(Ureg*, void *arg)
+{
+	int events;
+	Ctlr *ctlr;
+	I2C *i2c;
+
+	ctlr = arg;
+	i2c = ctlr->i2c;
+	events = i2c->i2cer;
+	eieio();
+	i2c->i2cer = events;
+	if(events & (BSY|TXE)){
+		//print("I2C#%x\n", events);
+		if(ctlr->phase != Idling){
+			ctlr->phase = Idling;
+		}
+	}else{
+		if(events & TXB){
+			//print("i2c: xmt %d %4.4ux %4.4ux\n", ctlr->phase, ctlr->td->status, ctlr->td[1].status);
+			if(ctlr->phase == Sending){
+				ctlr->phase = Done;
+			}
+		}
+		if(events & RXB){
+			//print("i2c: rcv %d %4.4ux %d\n", ctlr->phase, ctlr->rd->status, ctlr->rd->length);
+			if(ctlr->phase == Recving){
+				ctlr->phase = Done;
+			}
+		}
+	}
+}
+
+static int
+done(void *a)
+{
+	return ((Ctlr*)a)->phase < Busy;
+}
+
+static void
+i2cwait(Ctlr *ctlr)
+{
+	/* TO DO: timeout */
+	while(!done(ctlr)){
+		if(predawn)
+			interrupt(nil, ctlr);
+	}
+}
+
+long
+i2csend(int addr, void *buf, long n)
+{
+	Ctlr *ctlr;
+	int i, p, s;
+
+	ctlr = i2ctlr;
+	if(n > Bufsize)
+		return -1;
+	i = 1;
+	ctlr->txbuf[0] = addr & ~1;
+	if(addr & 1){
+		ctlr->txbuf[1] = addr>>8;
+		i++;
+	}
+	memmove(ctlr->txbuf+i, buf, n);
+	DCFLUSH(ctlr->txbuf, Tbuflen);
+	ctlr->phase = Sending;
+	ctlr->rd->status = BDEmpty|BDWrap|BDInt;
+	ctlr->td->addr = PADDR(ctlr->txbuf);
+	ctlr->td->length = n+i;
+	ctlr->td->status = BDReady|BDWrap|BDLast|BDInt;
+	enable();
+	ctlr->i2c->i2com = STR|I2CM;
+	eieio();
+	i2cwait(ctlr);
+	disable();
+	p = ctlr->phase;
+	s = ctlr->td->status;
+	if(s & BDReady || s & TxERR || p != Done)
+		return -1;
+	return n;
+}
+
+long
+i2crecv(int addr, void *buf, long n)
+{
+	Ctlr *ctlr;
+	int p, s, flag;
+	BD *td;
+	long nr;
+
+	ctlr = i2ctlr;
+	if(n > Bufsize)
+		return -1;
+	ctlr->txbuf[0] = addr|Rbit;
+	if(addr & 1){	/* special select sequence */
+		ctlr->addr[0] = addr &~ 1;
+		ctlr->addr[1] = addr>>8;
+	}
+	DCFLUSH(ctlr->txbuf, Tbuflen);
+	DCFLUSH(ctlr->rxbuf, Bufsize);
+	ctlr->phase = Recving;
+	ctlr->rd->addr = PADDR(ctlr->rxbuf);
+	ctlr->rd->status = BDEmpty|BDWrap|BDInt;
+	flag = 0;
+	td = ctlr->td;
+	td[1].status = 0;
+	if(addr & 1){
+		/* special select sequence */
+		td->addr = PADDR(ctlr->addr);
+		td->length = 2;
+		/* td->status made BDReady below */
+		td++;
+		flag = TxS;
+	}
+	td->addr = PADDR(ctlr->txbuf);
+	td->length = n+1;
+	td->status = BDReady|BDWrap|BDLast | flag;	/* not BDInt: leave that to receive */
+	if(flag)
+		ctlr->td->status = BDReady;
+	enable();
+	ctlr->i2c->i2com = STR|I2CM;
+	eieio();
+	i2cwait(ctlr);
+	disable();
+	p = ctlr->phase;
+	s = ctlr->td->status;
+	if(flag)
+		s |= ctlr->td[1].status;
+	nr = ctlr->rd->length;
+	if(nr > n)
+		nr = n;	/* shouldn't happen */
+	if(s & TxERR || s & BDReady || ctlr->rd->status & BDEmpty)
+		return -1;
+	if(p != Done)
+		return -1;
+	memmove(buf, ctlr->rxbuf, nr);
+	return nr;
+}
--- /dev/null
+++ b/os/boot/mpc/initfads.c
@@ -1,0 +1,187 @@
+/*
+ * Called from l.s in EPROM to set up a minimal working environment.
+ * Since there is no DRAM yet, and therefore no stack, no function
+ * calls may be made from sysinit0, and values can't be stored,
+ * except to INTMEM.  Global values are accessed by offset from SB,
+ * which has been set by l.s to point into EPROM.
+ *
+ * This is FADS-specific in CS assignment and access of the FADS BCSR
+ * to discover memory size and speed.
+ */
+
+#include "u.h"
+#include "lib.h"
+#include "mem.h"
+#include "dat.h"
+#include "fns.h"
+#include "io.h"
+
+#include "archfads.h"
+
+#define	MB	(1024*1024)
+
+enum {
+	UPMSIZE = 64,	/* memory controller instruction RAM */
+	SPEED = 50,	/* maximum memory clock in MHz */
+	SDRAMSIZE = 4*MB,
+
+	/* mcr */
+	WriteRAM = 0<<30,
+	ReadRAM = 1<<30,
+	ExecRAM = 2<<30,
+
+	SelUPMA = 0<<23,
+	SelUPMB = 1<<23,
+
+	Once = 1<<8,
+};
+
+/*
+ * mpc8bug uses the following for 60ns EDO DRAMs 32-50MHz
+ */
+static ulong upma50[UPMSIZE] = {
+	0x8FFFEC24,	0xFFFEC04,	0xCFFEC04,	0xFFEC04,       
+	0xFFEC00,	0x37FFEC47,	0xFFFFFFFF,	0xFFFFFFFF,
+	0x8FFFEC24,	0xFFFEC04,	0x8FFEC04,	0xFFEC0C,
+	0x3FFEC00,	0xFFEC44,	0xFFCC08,	0xCFFCC44,
+	0xFFEC0C,	0x3FFEC00,	0xFFEC44,	0xFFCC00,
+	0x3FFFC847,	0x3FFFEC47,	0xFFFFFFFF,	0xFFFFFFFF,
+	0x8FAFCC24,	0xFAFCC04,	0xCAFCC00,	0x11BFCC47,
+	0xC0FFCC84,	0xFFFFFFFF,	0xFFFFFFFF,	0xFFFFFFFF,
+	0x8FAFCC24,	0xFAFCC04,	0xCAFCC00,	0x3AFCC4C,
+	0xCAFCC00,	0x3AFCC4C,	0xCAFCC00,	0x3AFCC4C,
+	0xCAFCC00,	0x33BFCC4F,	0xFFFFFFFF,	0xFFFFFFFF,
+	0xFFFFFFFF,	0xFFFFFFFF,	0xFFFFFFFF,	0xFFFFFFFF,
+	0xC0FFCC84,	0xFFCC04,	0x7FFCC04,	0x3FFFCC06,
+	0xFFFFCC85,	0xFFFFCC05,	0xFFFFCC05,	0xFFFFFFFF,
+	0xFFFFFFFF,	0xFFFFFFFF,	0xFFFFFFFF,	0xFFFFFFFF,
+	0x33FFCC07,	0xFFFFFFFF,	0xFFFFFFFF,	0xFFFFFFFF,
+};
+
+/*
+ * the FADS manual table 3-7 suggests the following for 60ns EDO DRAMs at 20MHz
+ */
+static ulong upma20[UPMSIZE] = {
+	0x8FFFCC04, 0x08FFCC00, 0x33FFCC47, ~0, ~0, ~0, ~0, ~0,
+	[0x08]	0x8FFFCC04, 0x08FFCC08, 0x08FFCC08, 0x08FFCC08, 0x08FFCC00, 0x3FFFCC47, ~0, ~0, ~0, ~0, ~0, ~0, ~0, ~0, ~0, ~0,
+	[0x18]	0x8FEFCC00, 0x39BFCC47, ~0, ~0, ~0, ~0, ~0, ~0,
+	[0x20]	0x8FEFCC00, 0x09AFCC48, 0x09AFCC48, 0x08AFCC48, 0x39BFCC47, ~0, ~0, ~0, ~0, ~0, ~0, ~0, ~0, ~0, ~0, ~0,
+	[0x30]	0x80FFCC84, 0x17FFCC04, 0xFFFFCC86, 0xFFFFCC05, ~0, ~0, ~0, ~0, ~0, ~0, ~0, ~0,
+	[0x3C]	0x33FFCC07, ~0, ~0, ~0,
+};
+
+void
+sysinit0(int inrom)
+{
+	ulong *upm, *bcsr;
+	IMM *io;
+	int i, mb;
+
+	io = (IMM*)INTMEM;		/* running before maps, no KADDR */
+
+	/* system interface unit initialisation, FADS manual table 3-2, except as noted */
+	io->siumcr = 0x01012440;
+	io->sypcr = 0xFFFFFF88;
+	io->tbscrk = KEEP_ALIVE_KEY;
+	io->tbscr = 0xC3;	/* time base enabled */
+	io->rtcsck = KEEP_ALIVE_KEY;
+	io->rtcsc = 0xC1;	/* don't FRZ, real-time clock enabled */
+	io->rtcsck = ~KEEP_ALIVE_KEY;
+	io->piscrk = KEEP_ALIVE_KEY;
+	io->piscr = 0x82;
+
+	io->memc[BCSRCS].option = 0xFFFF8110;	/* 32k block, all types access, CS early negate, 1 ws */
+	io->memc[BCSRCS].base = BCSRMEM | 1;	/* base, 32-bit port, no parity, GPCM */
+
+	io->memc[BOOTCS].base = FLASHMEM | 1;
+	io->memc[BOOTCS].option = 0xFF800D54;
+
+	if(!inrom)
+		return;	/* can't initialise DRAM controller from DRAM */
+
+	bcsr = (ulong*)BCSRMEM;
+//	bcsr[1] &= ~DisableDRAM;
+	/* could check DRAM speed here; assume 60ns */
+	switch((bcsr[2]>>23)&3){
+	default:	return;	/* can't happen; for the compiler */
+	case 0:	mb = 4; break;
+	case 1:	mb = 32; break;
+	case 2:	mb = 16; break;
+	case 3:	mb = 8; break;
+	}
+
+	upm = upma50;
+	for(i=0; i<UPMSIZE; i++){
+		io->mdr = upm[i];
+		io->mcr = WriteRAM | SelUPMA | i;
+	}
+	io->mptpr = 0x0400;
+	if(SPEED >= 32)
+		io->mamr = (0x9C<<24) | 0xA21114;	/* 50MHz BRGCLK; FADS manual says 0xC0, mpc8bug sets 0x9C */
+	else if(SPEED >= 20)
+		io->mamr = (0x60<<24) | 0xA21114;	/* 25MHz BRGCLK */
+	else
+		io->mamr = (0x40<<24) | 0xA21114;	/* 16.67MHz BRGCLK */
+	io->memc[DRAM1].option = ~((mb<<20)-1)|0x0800;	/* address mask, SAM=1 */
+	io->memc[DRAM1].base = 0 | 0x81;	/* base at 0, 32-bit port size, no parity, UPMA */
+}
+
+/*
+ * the FADS manual table 3-9's suggestion for MB811171622A-100 32+MHz-50MHz
+ */
+static ulong upmb50[UPMSIZE] = {
+	[0x00]	0x1F07FC04, 0xEEAEFC04, 0x11ADFC04, 0xEFBBBC00, 0x1FF77C47,
+	[0x05]	0x1FF77C34, 0xEFEABC34, 0x1FB57C35,
+	[0x08]	0x1F07FC04, 0xEEAEFC04, 0x10ADFC04, 0xF0AFFC00, 0xF0AFFC00, 0xF1AFFC00, 0xEFBBBC00, 0x1FF77C47, ~0, ~0, ~0, ~0, ~0, ~0, ~0, ~0,
+	[0x18]	0x1F27FC04, 0xEEAEBC00, 0x01B93C04, 0x1FF77C47, ~0, ~0, ~0, ~0,
+	[0x20]	0x1F07FC04, 0xEEAEBC00, 0x10AD7C00, 0xF0AFFC00, 0xF0AFFC00, 0xE1BBBC04, 0x1FF77C47, ~0, ~0, ~0, ~0, ~0, ~0, ~0, ~0, ~0,
+	[0x30] 	0x1FF5FC84, 0xFFFFFC04, 0xFFFFFC04, 0xFFFFFC04, 0xFFFFFC84, 0xFFFFFC07, ~0, ~0, ~0, ~0, ~0, ~0,
+	[0x3C]	0x7FFFFC07, ~0, ~0, ~0,
+};
+
+/*
+ * the FADS manual table 3-8's suggestion for MB811171622A-100 up to 32MHz
+ */
+static	ulong	upmb32[UPMSIZE] = {
+	[0x00]	0x126CC04, 0xFB98C00, 0x1FF74C45, ~0, ~0,
+	[0x05]	0x1FE77C34, 0xEFAABC34, 0x1FA57C35,
+	[0x08]	0x0026FC04, 0x10ADFC00, 0xF0AFFC00, 0xF1AFFC00, 0xEFBBBC00, 0x1FF77C45, ~0, ~0, ~0, ~0, ~0, ~0, ~0, ~0, ~0, ~0,
+	[0x18]	0x0E26BC04, 0x01B93C00, 0x1FF77C45, ~0, ~0, ~0, ~0, ~0,
+	[0x20]	0x0E26BC00, 0x10AD7C00, 0xF0AFFC00, 0xF0AFFC00, 0xE1BBBC04, 0x1FF77C45, ~0, ~0, ~0, ~0, ~0, ~0, ~0, ~0, ~0, ~0,
+	[0x30]	0x1FF5FC84, 0xFFFFFC04, 0xFFFFFC84, 0xFFFFFC05, ~0, ~0, ~0, ~0, ~0, ~0, ~0, ~0,
+	[0x3C]	0x7FFFFC07, ~0, ~0, ~0,
+};
+
+/*
+ * optionally called by archfads.c:/^archinit to initialise access to SDRAM
+ */
+void
+sdraminit(ulong base)
+{
+	ulong *upm;
+	IMM *io;
+	int i;
+
+	io = (IMM*)INTMEM;		/* running before maps, no KADDR */
+	if(SPEED > 32)
+		upm = upmb50;
+	else
+		upm = upmb32;
+	for(i=0; i<UPMSIZE; i++){
+		io->mdr = upm[i];
+		io->mcr = WriteRAM | SelUPMB | i;
+	}
+	io->memc[SDRAM].option = ~(SDRAMSIZE-1)|0x0A00;	/* address mask, SAM=1, G5LS=1 */
+	io->memc[SDRAM].base = base | 0xC1;
+	if(SPEED > 32){
+		io->mbmr = 0xD0802114;	/* 50MHz BRGCLK */
+		io->mar = 0x88;
+	}else{
+		io->mbmr = 0x80802114;	/* 32MHz BRGCLK */
+		io->mar = 0x48;
+	}
+	io->mcr = ExecRAM | SelUPMB | (SDRAM<<13) | Once | 5;	/* run MRS command in locations 5-8 of UPMB */
+	io->mbmr = (io->mbmr & ~0xF) | 8;
+	io->mcr = ExecRAM | SelUPMB | (SDRAM<<13) | Once | 0x30;	/* run refresh sequence */
+	io->mbmr = (io->mbmr & ~0xF) | 4;	/* 4-beat refresh bursts */
+}
--- /dev/null
+++ b/os/boot/mpc/initpaq.c
@@ -1,0 +1,101 @@
+/*
+ * Called from l.s in EPROM to set up a minimal working environment.
+ * Since there is no DRAM yet, and therefore no stack, no function
+ * calls may be made from sysinit0, and values can't be stored,
+ * except to INTMEM.  Global values are accessed by offset from SB,
+ * which has been set by l.s to point into EPROM.
+ *
+ * This is PowerPAQ-specific:
+ *	- assumes 8mbytes
+ *	- powerpaq CS assignment
+ */
+
+#include "u.h"
+#include "lib.h"
+#include "mem.h"
+#include "dat.h"
+#include "fns.h"
+#include "io.h"
+
+#include "archpaq.h"
+
+#define	MB	(1024*1024)
+
+enum {
+	DRAMSIZE = 8*MB,
+	FLASHSIZE = 8*MB,
+
+	UPMSIZE = 64,	/* memory controller instruction RAM */
+	SPEED = 50,	/* maximum memory clock in MHz */
+
+	/* mcr */
+	WriteRAM = 0<<30,
+	ReadRAM = 1<<30,
+	ExecRAM = 2<<30,
+
+	SelUPMA = 0<<23,
+	SelUPMB = 1<<23,
+
+	Once = 1<<8,
+};
+
+/*
+ * mpc8bug uses the following for 60ns EDO DRAMs 32-50MHz
+ */
+static ulong upmb50[UPMSIZE] = {
+	0x8FFFEC24,	0xFFFEC04,	0xCFFEC04,	0xFFEC04,       
+	0xFFEC00,	0x37FFEC47,	0xFFFFFFFF,	0xFFFFFFFF,
+	0x8FFFEC24,	0xFFFEC04,	0x8FFEC04,	0xFFEC0C,
+	0x3FFEC00,	0xFFEC44,	0xFFCC08,	0xCFFCC44,
+	0xFFEC0C,	0x3FFEC00,	0xFFEC44,	0xFFCC00,
+	0x3FFFC847,	0x3FFFEC47,	0xFFFFFFFF,	0xFFFFFFFF,
+	0x8FAFCC24,	0xFAFCC04,	0xCAFCC00,	0x11BFCC47,
+	0xC0FFCC84,	0xFFFFFFFF,	0xFFFFFFFF,	0xFFFFFFFF,
+	0x8FAFCC24,	0xFAFCC04,	0xCAFCC00,	0x3AFCC4C,
+	0xCAFCC00,	0x3AFCC4C,	0xCAFCC00,	0x3AFCC4C,
+	0xCAFCC00,	0x33BFCC4F,	0xFFFFFFFF,	0xFFFFFFFF,
+	0xFFFFFFFF,	0xFFFFFFFF,	0xFFFFFFFF,	0xFFFFFFFF,
+	0xC0FFCC84,	0xFFCC04,	0x7FFCC04,	0x3FFFCC06,
+	0xFFFFCC85,	0xFFFFCC05,	0xFFFFCC05,	0xFFFFFFFF,
+	0xFFFFFFFF,	0xFFFFFFFF,	0xFFFFFFFF,	0xFFFFFFFF,
+	0x33FFCC07,	0xFFFFFFFF,	0xFFFFFFFF,	0xFFFFFFFF,
+};
+
+void
+sysinit0(int inrom)
+{
+	ulong *upm;
+	IMM *io;
+	int i;
+
+	io = (IMM*)INTMEM;		/* running before maps, no KADDR */
+
+	/* system interface unit initialisation, FADS manual table 3-2, except as noted */
+	io->siumcr = 0x01012440;
+	io->sypcr = 0xFFFFFF88;
+	io->tbscrk = KEEP_ALIVE_KEY;
+	io->tbscr = 0xC3;	/* time base enabled */
+	io->rtcsck = KEEP_ALIVE_KEY;
+	io->rtcsc = 0xC1;	/* don't FRZ, real-time clock enabled */
+	io->rtcsck = ~KEEP_ALIVE_KEY;
+	io->piscrk = KEEP_ALIVE_KEY;
+	io->piscr = 0x82;
+
+	io->memc[BOOTCS].base = FLASHMEM | 1;
+	io->memc[BOOTCS].option = ~(FLASHSIZE-1)|(1<<8)|(2<<4);	/* mask, BIH, 2 wait states */
+
+	if(!inrom)
+		return;	/* can't initialise DRAM controller from DRAM */
+
+	/* could check DRAM speed here; assume 60ns */
+	/* could probe DRAM for size here; assume DRAMSIZE */
+	io->mptpr = 0x400;	/* powerpaq flash has 0x1000 */
+	io->mbmr = (0xC0<<24) | 0xA21114;	/* 50MHz BRGCLK */
+	upm = upmb50;
+	for(i=0; i<UPMSIZE; i++){
+		io->mdr = upm[i];
+		io->mcr = WriteRAM | SelUPMB | i;
+	}
+	io->memc[DRAM1].option = ~(DRAMSIZE-1)|0x0800;	/* address mask, SAM=1 */
+	io->memc[DRAM1].base = 0 | 0xC1;	/* base at 0, 32-bit port size, no parity, UPMB */
+}
--- /dev/null
+++ b/os/boot/mpc/initrpcg.c
@@ -1,0 +1,91 @@
+
+/*
+ * Called from l.s in EPROM to set up a minimal working environment.
+ * Since there is no DRAM yet, and therefore no stack, no function
+ * calls may be made from sysinit, and values can't be stored,
+ * except to INTMEM.  Global values are accessed by offset from SB,
+ * which has been set by l.s to point into EPROM.
+ */
+
+#include "u.h"
+#include "lib.h"
+#include "mem.h"
+#include "dat.h"
+#include "fns.h"
+#include "io.h"
+
+#include	"archrpcg.h"
+
+#define	MB	(1024*1024)
+
+enum {
+	UPMSIZE = 64,	/* memory controller instruction RAM */
+	DRAMSIZE = 16*MB,
+	FLASHSIZE = 4*MB,
+
+	WriteRAM = 0<<30,
+	ReadRAM = 1<<30,
+	ExecRAM = 2<<30,
+
+	SelUPMA = 0<<23,
+	SelUPMB = 1<<23,
+};
+/* RPCG values for RPXLite AW */
+static	ulong	upma50[UPMSIZE] = {
+	0xCFFFCC24,	0x0FFFCC04,	0x0CAFCC04,	0x03AFCC08,       
+	0x3FBFCC27,	0xFFFFCC25,	0xFFFFCC25,	0xFFFFCC25,
+	0xCFFFCC24,	0x0FFFCC04,	0x0CAFCC84,	0x03AFCC88,
+	0x3FBFCC27,	0xFFFFCC25,	0xFFFFCC25,	0xFFFFCC25,
+	0xFFFFCC25,	0xFFFFCC25,	0xFFFFCC25,	0xFFFFCC25,
+	0xFFFFCC25,	0xFFFFCC25,	0xFFFFCC25,	0xFFFFCC25,
+	0xCFFFCC24,	0x0FFFCC04,	0x0CFFCC04,	0x03FFCC00,
+	0x3FFFCC27,	0xFFFFCC25,	0xFFFFCC25,	0xFFFFCC25,
+	0xCFFFCC24,	0x0FFFCC04,	0x0CFFCC84,	0x03FFCC84,
+	0x0CFFCC00,	0x33FFCC27,	0xFFFFCC25,	0xFFFFCC25,
+	0xFFFFCC25,	0xFFFFCC25,	0xFFFFCC25,	0xFFFFCC25,
+	0xFFFFCC25,	0xFFFFCC25,	0xFFFFCC25,	0xFFFFCC25,
+	0xC0FFCC24,	0x03FFCC24,	0x0FFFCC24,	0x0FFFCC24,
+	0x3FFFCC27,	0xFFFFCC25,	0xFFFFCC25,	0xFFFFCC25,
+	0xFFFFCC25,	0xFFFFCC25,	0xFFFFCC25,	0xFFFFCC25,
+	0xFFFFCC25,	0xFFFFCC25,	0xFFFFCC25,	0xFFFFCC25,
+};
+
+void
+sysinit0(int inrom)
+{
+	ulong *upm;
+	IMM *io;
+	int i;
+
+	io = (IMM*)INTMEM;		/* running before maps, no KADDR */
+	io->siumcr = 0x01012440;
+	io->sypcr = 0xFFFFFF88;
+	io->tbscrk = KEEP_ALIVE_KEY;
+	io->tbscr = 0xC3;
+	io->rtcsck = KEEP_ALIVE_KEY;
+	io->rtcsc = 0xC1;
+	io->rtcsck = ~KEEP_ALIVE_KEY;
+	io->piscrk = KEEP_ALIVE_KEY;
+	io->piscr = 0x82;
+return;
+	io->memc[BCSRCS].option = 0xFFFF8910;	/* 32k block, all types access, CSNT, CS early negate, burst inhibit, 1 ws */
+	io->memc[BCSRCS].base = BCSRMEM | 1;	/* base, 32-bit port, no parity, GPCM */
+
+	io->memc[BOOTCS].base = FLASHMEM | 0x801; /* base, 16 bit port */
+	io->memc[BOOTCS].option = ~(FLASHSIZE-1)|(1<<8)|(4<<4);	/* mask, BIH, 4 wait states */
+
+	if(1||!inrom)
+		return;	/* can't initialise DRAM controller from DRAM */
+
+	/* TO DO: could check DRAM size and speed now */
+
+	upm = upma50;
+	for(i=0; i<nelem(upma50); i++){
+		io->mdr = upm[i];
+		io->mcr = WriteRAM | SelUPMA | i;
+	}
+	io->mptpr = 0x0800;	/* divide by 8 */
+	io->mamr = (0x58<<24) | 0xA01430;	/* 40MHz BRGCLK */
+	io->memc[DRAM1].option = ~(DRAMSIZE-1)|0x0E00;	/* address mask, SAM=1, G5LA/S=3 */
+	io->memc[DRAM1].base = 0 | 0x81;	/* base at 0, 32-bit port size, no parity, UPMA */
+}
--- /dev/null
+++ b/os/boot/mpc/io.h
@@ -1,0 +1,463 @@
+enum
+{
+	/* software interrupt vectors (SIU and CPM) */
+	VectorPIC= 0,	/* level 0 to level 7, assigned by software */
+		CPIClevel=	4,
+	VectorIRQ=	VectorPIC+8,	/* IRQ0 to IRQ7 */
+	VectorCPIC=	VectorIRQ+8,	/* 32 CPM interrupts: 0 (error) to 0x1F (PC15) */
+};
+
+enum
+{
+	BUSUNKNOWN = 0,
+};
+
+/*
+ * Buffer Descriptors and IO Rings
+ */
+
+typedef struct BD BD;
+struct BD {
+	ushort	status;
+	ushort	length;
+	ulong	addr;
+};
+
+BD*	bdalloc(int);
+void	bdfree(BD*, int);
+
+enum {
+	/* Rx BDs, bits common to all protocols */
+	BDEmpty=	1<<15,
+	BDWrap=		1<<13,
+	BDInt=		1<<12,
+	BDLast=		1<<11,
+	BDFirst=		1<<10,
+
+	/* Tx BDs */
+	BDReady=		1<<15,
+	/* BDWrap, BDInt, BDLast */
+};
+
+typedef struct Ring Ring;
+struct Ring {
+	BD*	rdr;				/* receive descriptor ring */
+	void*	rrb;				/* receive ring buffers */
+	int	rdrx;				/* index into rdr */
+	int	nrdre;			/* length of rdr */
+
+	BD*	tdr;				/* transmit descriptor ring */
+	Block**	txb;				/* corresponding transmit ring buffers */
+	int	tdrh;				/* host index into tdr */
+	int	tdri;				/* interface index into tdr */
+	int	ntdre;			/* length of tdr */
+	int	ntq;				/* pending transmit requests */
+};
+
+#define NEXT(x, l)	(((x)+1)%(l))
+#define PREV(x, l)	(((x) == 0) ? (l)-1: (x)-1)
+#define	HOWMANY(x, y)	(((x)+((y)-1))/(y))
+#define ROUNDUP(x, y)	(HOWMANY((x), (y))*(y))
+
+int	ioringinit(Ring*, int, int, int);
+
+/*
+ * CPM
+ */
+enum {
+	/* commands */
+	InitRxTx =	0,
+	InitRx =		1,
+	InitTx =		2,
+	EnterHunt=	3,
+	StopTx=		4,
+	GracefulStopTx = 5,
+	InitIDMA =	5,
+	RestartTx =	6,
+	CloseRxBD =	7,
+	SetGroupAddr = 8,
+	SetTimer =	8,
+	GCITimeout =	9,
+	GCIAbort =	10,
+	StopIDMA =	11,
+	StartDSP = 	12,
+	ArmIDMA =	13,
+	InitDSP =		13,
+	USBCmd =	15,
+
+	/* channel IDs */
+	SCC1ID=	0,
+	USBID=	0,
+	I2CID=	1,
+	IDMA1ID= 1,
+	SCC2ID=	4,
+	SPIID=	5,
+	IDMA2ID= 5,
+	TIMERID=	5,
+	SCC3ID=	8,
+	SMC1ID=	9,
+	DSP1ID=9,
+	SCC4ID=	12,
+	SMC2ID=	13,
+	DSP2ID=	13,
+
+	BaudEnable = 1<<16,
+
+	/* sicr */
+	CLK1 = 4,		/* SCC1,2 */
+	CLK2 = 5,
+	CLK3 = 6,
+	CLK4 = 7,
+	CLK5 = CLK1,	/* SCC3,4 */
+	CLK6 = CLK2,
+	CLK7 = CLK3,
+	CLK8 = CLK4,
+};
+
+void	cpmop(int, int, int);
+#define	ioplock()	(m->iomem)
+#define	iopunlock()
+
+/*
+ * the structures below follow hardware/firmware layouts in the 8xx manuals:
+ * mind the data types, offsets and alignment
+ */
+
+/*
+ * basic IO controller parameters (SMC and SCC)
+ */
+typedef struct IOCparam IOCparam;
+struct IOCparam {
+	ushort	rbase;
+	ushort	tbase;
+	uchar	rfcr;
+	uchar	tfcr;
+	ushort	mrblr;
+	ulong	rstate;
+	ulong	rptr;
+	ushort	rbptr;
+	ushort	rcnt;
+	ulong	rtmp;
+	ulong	tstate;
+	ulong	tptr;
+	ushort	tbptr;
+	ushort	tcnt;
+	ulong	ttmp;
+};
+
+typedef struct SCCparam SCCparam;
+struct SCCparam {
+	IOCparam;
+	ulong	rcrc;
+	ulong	tcrc;
+};
+
+typedef struct SCC SCC;
+struct SCC {
+	ulong	gsmrl;
+	ulong	gsmrh;
+	ushort	psmr;
+	uchar	rsvscc0[2];
+	ushort	todr;
+	ushort	dsr;
+	ushort	scce;
+	uchar	rsvscc1[2];
+	ushort	sccm;
+	uchar	rsvscc3;
+	uchar	sccs;
+	ushort	irmode;
+	ushort	irsip;
+};
+
+typedef struct SMC SMC;
+struct SMC {
+	uchar	pad1[2];
+	ushort	smcmr;
+	uchar	pad2[2];
+	uchar	smce;
+	uchar	pad3[3];
+	uchar	smcm;
+	uchar	pad4[5];
+};
+
+typedef struct SPI SPI;
+struct SPI {
+	ushort	spmode;
+	uchar	res1[4];
+	uchar	spie;
+	uchar	res2[3];
+	uchar	spim;
+	uchar	res3[2];
+	uchar	spcom;
+	uchar	res4[10];
+};
+
+typedef struct USB USB;
+struct USB {	/* 823 only */
+	uchar	usmod;
+	uchar	usadr;
+	uchar	uscom;
+	uchar	rsvu1;
+	ushort	usep[4];
+	uchar	rsvu2[4];
+	ushort	usber;
+	uchar	rsvu3[2];
+	ushort	usbmr;
+	uchar	rsvu4;
+	uchar	usbs;
+	uchar	rsvu5[8];
+};
+
+typedef struct IMM IMM;
+struct IMM {
+	struct {	/* general SIU */
+		ulong	siumcr;
+		ulong	sypcr;
+		uchar	rsv0[0xE-0x8];
+		ushort	swsr;
+		ulong	sipend;
+		ulong	simask;
+		ulong	siel;
+		uchar	sivec;
+		uchar	padv[3];
+		ulong	tesr;
+		uchar	rsv1[0x30-0x24];
+		ulong	sdcr;
+		uchar	rsv2[0x80-0x34];
+	};
+	struct {	/* PCMCIA */
+		struct {
+			ulong	base;
+			ulong	option;
+		} pcmr[8];
+		uchar	rsv3[0xe0-0xc0];
+		ulong	pgcra;
+		ulong	pgcrb;
+		ulong	pscr;
+		uchar	rsv4[0xf0-0xec];
+		ulong	pipr;
+		uchar	rsv5[4];
+		ulong	per;
+		uchar	rsv6[4];
+	};
+	struct {	/* MEMC */
+		struct {
+			ulong	base;
+			ulong	option;
+		} memc[8];
+		uchar	rsv7a[0x24];
+		ulong	mar;
+		ulong	mcr;
+		uchar	rsv7b[4];
+		ulong	mamr;
+		ulong	mbmr;
+		ushort	mstat;
+		ushort	mptpr;
+		ulong	mdr;
+		uchar	rsv7c[0x80];
+	};
+	struct {	/* system integration timers */
+		ushort	tbscr;
+		uchar	rsv8a[2];
+		ulong	tbrefu;
+		ulong	tbrefl;
+		uchar	rsv8b[0x14];
+		ushort	rtcsc;
+		uchar	rsv8c[2];
+		ulong	rtc;
+		ulong	rtsec;
+		ulong	rtcal;
+		uchar	rsv8d[0x10];
+		ushort	piscr;
+		ushort	rsv8e;
+		ulong	pitc;
+		ulong	pitr;
+		uchar	rsv8f[0x34];
+	};
+	struct {	/* 280: clocks and resets */
+		ulong	sccr;
+		ulong	plprcr;
+		ulong	rsr;
+		uchar	rsv9[0x300-0x28c];
+	};
+	struct {	/* 300: system integration timers keys */
+		ulong	tbscrk;
+		ulong	tbrefuk;
+		ulong	tbreflk;
+		ulong	tbk;
+		uchar	rsv10a[0x10];
+		ulong	rtcsck;
+		ulong	rtck;
+		ulong	rtseck;
+		ulong	rtcalk;
+		uchar	rsv10b[0x10];
+		ulong	piscrk;
+		ulong	pitck;
+		uchar	rsv10c[0x38];
+	};
+	struct {	/* 380: clocks and resets keys */
+		ulong	sccrk;
+		ulong	plprcrk;
+		ulong	rsrk;
+		uchar	rsv11[0x800-0x38C];
+	};
+	struct {	/* 800: video controller */
+		ushort	vccr;
+		ushort	pad11a;
+		uchar	vsr;
+		uchar	pad11b;
+		uchar	vcmr;
+		uchar	pad11c;
+		ulong	vbcb;
+		ulong	pad11d;
+		ulong	vfcr0;
+		ulong	vfaa0;
+		ulong	vfba0;
+		ulong	vfcr1;
+		ulong	vfaa1;
+		ulong	vfba1;
+		uchar	rsv11a[0x840-0x828];
+	};
+	struct {	/* 840: LCD */
+		ulong	lccr;
+		ulong	lchcr;
+		ulong	lcvcr;
+		ulong	rsv11b;
+		ulong	lcfaa;
+		ulong	lcfba;
+		uchar	lcsr;
+		uchar	rsv11c[0x860-0x859];
+	};
+	struct {	/* 860: I2C */
+		uchar	i2mod;
+		uchar	rsv12a[3];
+		uchar	i2add;
+		uchar	rsv12b[3];
+		uchar	i2brg;
+		uchar	rsv12c[3];
+		uchar	i2com;
+		uchar	rsv12d[3];
+		uchar	i2cer;
+		uchar	rsv12e[3];
+		uchar	i2cmr;
+		uchar	rsv12[0x900-0x875];
+	};
+	struct {	/* 900: DMA */
+		uchar	rsv13[4];
+		ulong	sdar;
+		uchar	sdsr;
+		uchar	pad1[3];
+		uchar	sdmr;
+		uchar	pad2[3];
+		uchar	idsr1;
+		uchar	pad3[3];
+		uchar	idmr1;
+		uchar	pad4[3];
+		uchar	idsr2;
+		uchar	pad5[3];
+		uchar	idmr2;
+		uchar	pad6[0x930-0x91D];
+	};
+	struct {	/* CPM interrupt control */
+		ushort	civr;
+		uchar	pad7[0x940-0x932];
+		ulong	cicr;
+		ulong	cipr;
+		ulong	cimr;
+		ulong	cisr;
+	};
+	struct {	/* input/output port */
+		ushort	padir;
+		ushort	papar;
+		ushort	paodr;
+		ushort	padat;
+		uchar	pad8[8];
+		ushort	pcdir;
+		ushort	pcpar;
+		ushort	pcso;
+		ushort	pcdat;
+		ushort	pcint;
+		uchar	pad9[6];
+		ushort	pddir;
+		ushort	pdpar;
+		ushort	rsv14a;
+		ushort	pddat;
+		uchar	rsv14[0x980-0x978];
+	};
+	struct {	/* CPM timers */
+		ushort	tgcr;
+		uchar	rsv15a[0x990-0x982];
+		ushort	tmr1;
+		ushort	tmr2;
+		ushort	trr1;
+		ushort	trr2;
+		ushort	tcr1;
+		ushort	tcr2;
+		ushort	tcn1;
+		ushort	tcn2;
+		ushort	tmr3;
+		ushort	tmr4;
+		ushort	trr3;
+		ushort	trr4;
+		ushort	tcr3;
+		ushort	tcr4;
+		ushort	tcn3;
+		ushort	tcn4;
+		ushort	ter1;
+		ushort	ter2;
+		ushort	ter3;
+		ushort	ter4;
+		uchar	rsv15[0x9C0-0x9B8];
+	};
+	struct {	/* CPM */
+		ushort	cpcr;
+		uchar	res0[2];
+		ushort	rccr;
+		uchar	res1;
+		uchar	rmds;
+		uchar	res2a[4];
+		ushort	rctr1;
+		ushort	rctr2;
+		ushort	rctr3;
+		ushort	rctr4;
+		uchar	res2[2];
+		ushort	rter;
+		uchar	res3[2];
+		ushort	rtmr;
+		uchar	rsv16[0x9F0-0x9DC];
+	};
+	union {	/* BRG */
+		struct {
+			ulong	brgc1;
+			ulong	brgc2;
+			ulong	brgc3;
+			ulong	brgc4;
+		};
+		ulong	brgc[4];
+	};
+	uchar	skip0[0xAB2-0xA00];	/* USB, SCC, SMC, SPI: address using cpmdev(CP...)->regs */
+	struct {	/* PIP */
+		ushort	pipc;		/* not 823 */
+		ushort	ptpr;		/* not 823 */
+		ulong	pbdir;
+		ulong	pbpar;
+		uchar	pad10[2];
+		ushort	pbodr;
+		ulong	pbdat;
+		uchar	pad11[0xAE0-0xAC8];
+	};
+	struct {	/* SI */
+		ulong	simode;
+		uchar	sigmr;
+		uchar	pad12;
+		uchar	sistr;
+		uchar	sicmr;
+		uchar	pad13[4];
+		ulong	sicr;
+		ulong	sirp;
+		uchar	pad14[0xB00-0xAF4];
+	};
+	ulong	vcram[64];
+	ushort	siram[256];
+	ushort	lcdmap[256];
+};
--- /dev/null
+++ b/os/boot/mpc/ip.h
@@ -1,0 +1,98 @@
+typedef struct Udphdr Udphdr;
+struct Udphdr
+{
+	uchar	d[6];		/* Ethernet destination */
+	uchar	s[6];		/* Ethernet source */
+	uchar	type[2];	/* Ethernet packet type */
+
+	uchar	vihl;		/* Version and header length */
+	uchar	tos;		/* Type of service */
+	uchar	length[2];	/* packet length */
+	uchar	id[2];		/* Identification */
+	uchar	frag[2];	/* Fragment information */
+
+	/* Udp pseudo ip really starts here */
+	uchar	ttl;	
+	uchar	udpproto;	/* Protocol */
+	uchar	udpplen[2];	/* Header plus data length */
+	uchar	udpsrc[4];	/* Ip source */
+	uchar	udpdst[4];	/* Ip destination */
+	uchar	udpsport[2];	/* Source port */
+	uchar	udpdport[2];	/* Destination port */
+	uchar	udplen[2];	/* data length */
+	uchar	udpcksum[2];	/* Checksum */
+};
+
+typedef struct Etherhdr Etherhdr;
+struct Etherhdr
+{
+	uchar	d[6];
+	uchar	s[6];
+	uchar	type[2];
+
+	/* Now we have the ip fields */
+	uchar	vihl;		/* Version and header length */
+	uchar	tos;		/* Type of service */
+	uchar	length[2];	/* packet length */
+	uchar	id[2];		/* Identification */
+	uchar	frag[2];	/* Fragment information */
+	uchar	ttl;		/* Time to live */
+	uchar	proto;		/* Protocol */
+	uchar	cksum[2];	/* Header checksum */
+	uchar	src[4];		/* Ip source */
+	uchar	dst[4];		/* Ip destination */
+};
+
+enum
+{
+	IP_VER		= 0x40,
+	IP_HLEN		= 0x05,			
+ 	UDP_EHSIZE	= 22,
+	UDP_PHDRSIZE	= 12,
+	UDP_HDRSIZE	= 20,
+	ETHER_HDR	= 14,
+	IP_UDPPROTO	= 17,
+	ET_IP		= 0x800,
+	Bcastip		= 0xffffffff,
+	BPportsrc	= 68,
+	BPportdst	= 67,
+	TFTPport	= 69,
+	Timeout		= 5000,	/* milliseconds */
+	Bootrequest 	= 1,
+	Bootreply   	= 2,
+	Tftp_READ	= 1,
+	Tftp_WRITE	= 2,
+	Tftp_DATA	= 3,
+	Tftp_ACK	= 4,
+	Tftp_ERROR	= 5,
+	Segsize		= 512,
+	TFTPSZ		= Segsize+10,
+};
+
+typedef struct Bootp Bootp;
+struct Bootp
+{
+	uchar	op;		/* opcode */
+	uchar	htype;		/* hardware type */
+	uchar	hlen;		/* hardware address len */
+	uchar	hops;		/* hops */
+	uchar	xid[4];		/* a random number */
+	uchar	secs[2];	/* elapsed snce client started booting */
+	uchar	pad[2];
+	uchar	ciaddr[4];	/* client IP address (client tells server) */
+	uchar	yiaddr[4];	/* client IP address (server tells client) */
+	uchar	siaddr[4];	/* server IP address */
+	uchar	giaddr[4];	/* gateway IP address */
+	uchar	chaddr[16];	/* client hardware address */
+	char	sname[64];	/* server host name (optional) */
+	char	file[128];	/* boot file name */
+	char	vend[128];	/* vendor-specific goo */
+};
+
+typedef struct Netaddr Netaddr;
+struct Netaddr
+{
+	ulong	ip;
+	ushort	port;
+	char	ea[Eaddrlen];
+};
--- /dev/null
+++ b/os/boot/mpc/l.s
@@ -1,0 +1,370 @@
+#include "mem.h"
+
+/* special instruction definitions */
+#define	BDNE	BC	0,2,
+#define	BDNZ	BC	16,0,
+#define	NOOP	OR	R0,R0,R0
+
+/*
+ * common ppc special purpose registers
+ */
+#define DSISR	18
+#define DAR	19	/* Data Address Register */
+#define DEC	22	/* Decrementer */
+#define SRR0	26	/* Saved Registers (exception) */
+#define SRR1	27
+#define SPRG0	272	/* Supervisor Private Registers */
+#define SPRG1	273
+#define SPRG2	274
+#define SPRG3	275
+#define TBRU	269	/* Time base Upper/Lower (Reading) */
+#define TBRL	268
+#define TBWU	285	/* Time base Upper/Lower (Writing) */
+#define TBWL	284
+#define PVR	287	/* Processor Version */
+
+/*
+ * mpc82x-specific special purpose registers of interest here
+ */
+#define EIE	80
+#define EID	81
+#define NRI	82
+#define IMMR	638
+#define IC_CST	560
+#define IC_ADR	561
+#define IC_DAT	562
+#define DC_CST	568
+#define DC_ADR	569
+#define DC_DAT	570
+#define MI_CTR	784
+#define MI_AP	786
+#define MI_EPN	787
+#define MI_TWC	789
+#define MI_RPN	790
+#define MI_DBCAM	816
+#define MI_DBRAM0	817
+#define MI_DBRAM1	818
+#define MD_CTR	792
+#define M_CASID	793
+#define MD_AP	794
+#define MD_EPN	795
+#define M_TWB	796
+#define MD_TWC	797
+#define MD_RPN	798
+#define	M_TW	799
+#define	MD_DBCAM	824
+#define	MD_DBRAM0	825
+#define	MD_DBRAM1	826
+
+/* as on 603e, apparently mtmsr needs help in some chip revisions */
+#define	WAITMSR	SYNC; ISYNC
+
+/* use of SPRG registers in save/restore */
+#define	SAVER0	SPRG0
+#define	SAVER1	SPRG1
+#define	SAVELR	SPRG2
+#define	SAVECR	SPRG3
+
+#define	UREGSIZE	((8+32)*4)
+#define	UREGSPACE	(UREGSIZE+8)	/* allow for arg to trap, and align */
+
+/*
+ * This code is loaded by the ROM loader at location 0x3000,
+ * or lives in flash memory at 0x2800100.
+ * Move it to high memory so that it can load the kernel at 0x0000.
+ */
+
+#define LOADCODEBASE	0x3000	/* when downloaded in S records */
+#define FLASHCODEBASE	(FLASHMEM+0x100)	/* when in flash */
+
+	TEXT	start(SB), $-4
+	MOVW	MSR, R3
+	MOVW	$(EE|IP|RI), R4
+	ANDN	R4, R3
+	OR	$ME, R3
+	SYNC
+	MOVW	R3, MSR	/* turn off interrupts but enable traps */
+	WAITMSR
+
+/*
+ * reset the caches and disable them for now
+ */
+	MOVW	SPR(IC_CST), R4	/* read and clear */
+	MOVW	$(5<<25), R4
+	MOVW	R4, SPR(IC_CST)	/* unlock all */
+	ISYNC
+	MOVW	$(6<<25), R4
+	MOVW	R4, SPR(IC_CST)	/* invalidate all */
+	ISYNC
+	MOVW	$(2<<25), R4
+	MOVW	R4, SPR(IC_CST)	/* disable i-cache */
+	ISYNC
+
+	SYNC
+	MOVW	SPR(DC_CST), R4	/* read and clear */
+	MOVW	$(10<<24), R4
+	MOVW	R4, SPR(DC_CST)	/* unlock all */
+	ISYNC
+	MOVW	$(12<<24), R4
+	MOVW	R4, SPR(DC_CST)	/* invalidate all */
+	ISYNC
+	MOVW	$(4<<24), R4
+	MOVW	R4, SPR(DC_CST)	/* disable i-cache */
+	ISYNC
+
+	MOVW	$7, R4
+ANDN R4, R4, R4
+	MOVW	R4, SPR(158)		/* cancel `show cycle' for normal instruction execution */
+
+/*
+ * set other system configuration values
+ */
+	MOVW	SPR(IMMR), R5		/* save initial space pointer */
+	MOVW	$INTMEM, R4
+	MOVW	R4, SPR(IMMR)		/* set internal memory base */
+	MOVW	$0xFFFFFF88, R3
+	MOVW	R3, 4(R4)	/* disable watchdog in sypcr */
+	MOVW	$0x01012440, R3
+	MOVW	R3, 0(R4)	/* siumcr */
+
+/*
+ * system initialisation (init and map DRAM)
+ */
+	MOVW	$0, R0
+	MOVW	$setSB(SB), R2
+	MOVW	$(0xF000<<16), R3
+/*MOVW R0, R3*/
+	ANDCC	R5, R3	/* initial space is high? */
+	BEQ	notrom
+	MOVW	$FLASHCODEBASE, R5	/* where $start(SB) actually is now */
+	MOVW	$start(SB), R4	/* logical start address */
+	SUB	R4, R5, R6	/* text relocation value */
+	MOVW	$etext(SB), R7
+	SUB	R4, R7
+	ADD	R5, R7	/* data address in ROM */
+	MOVW	$bdata(SB), R8
+	SUB	R8, R2
+	ADD	R7, R2	/* relocate SB: SB' = romdata+(SB-bdata) */
+	MOVW	$sysinit0(SB), R4
+	ADD	R6, R4	/* relocate sysinit0's address */
+	MOVW	R4, CTR
+	MOVW	$inmem(SB), R4
+	ADD	R6, R4
+	MOVW	R4, LR	/* and the return address */
+	BR	(CTR)	/* call sysinit0 */
+	TEXT	inmem(SB), $-4
+	MOVW	$FLASHCODEBASE, R3
+	BR	cpu0
+notrom:
+	MOVW	$start(SB), R6
+	SUB	R6, R2
+	ADD	$LOADCODEBASE, R2
+	BL	sysinit0(SB)
+	MOVW	$LOADCODEBASE, R3
+
+/*
+ * cpu 0
+ *	relocate bootstrap to our link addresses for text and data
+ *	set new PC
+ */
+cpu0:
+	MOVW	$setSB(SB), R2	/* set correct static base register */
+	MOVW	$start(SB), R4
+	MOVW	$etext(SB), R5
+	SUB	R4, R5
+	CMP	R4, R3	/* already there? */
+	BNE	copytext
+	ADD	R5, R3	/* start of data image */
+	BR	copydata
+
+copytext:
+	ADD	$3, R5
+	SRAW	$2, R5
+	MOVW	R5, CTR
+	SUB	$4, R4
+	SUB	$4, R3
+copyt:			/* copy text */
+	MOVWU	4(R3), R5
+	MOVWU	R5, 4(R4)
+	BDNZ	copyt
+	ADD	$4, R3
+
+copydata:
+	/* copy data */
+	MOVW	$bdata(SB), R4
+	CMP	R4, R3	/* already there? */
+	BEQ	loadkpc
+	MOVW	$edata(SB), R5
+	SUB	R4, R5
+	ADD	$3, R5
+	SRAW	$2, R5
+	MOVW	R5, CTR
+	SUB	$4, R4
+	SUB	$4, R3
+copyd:
+	MOVWU	4(R3), R5
+	MOVWU	R5, 4(R4)
+	BDNZ	copyd
+
+	/* load correct PC */
+loadkpc:
+	MOVW	$start1(SB), R3
+	MOVW	R3, LR
+	BR	(LR)
+TEXT start1(SB), $-4
+	MOVW	$edata(SB), R3
+	MOVW	$end(SB), R4
+	SUBCC	R3, R4
+	BLE	skipz
+	SRAW	$2, R4
+	MOVW	R4, CTR
+	SUB	$4, R3
+	MOVW	$0, R0
+zero:
+	MOVWU	R0, 4(R3)
+	BDNZ	zero
+skipz:
+	MOVW	$mach0(SB), R1
+	MOVW	R1, m(SB)
+	ADD	$(MACHSIZE-8), R1
+	MOVW	$0, R0
+	BL	main(SB)
+	BR	0(PC)
+
+TEXT	getmsr(SB), $0
+	MOVW	MSR, R3
+	RETURN
+
+TEXT	putmsr(SB), $0
+	SYNC
+	MOVW	R3, MSR
+	WAITMSR
+	RETURN
+
+TEXT	eieio(SB), $0
+	EIEIO
+	RETURN
+
+TEXT	idle(SB), $0
+	RETURN
+
+TEXT	spllo(SB), $0
+	MOVW	MSR, R3
+	OR	$EE, R3, R4
+	SYNC
+	MOVW	R4, MSR
+	WAITMSR
+	RETURN
+
+TEXT	splhi(SB), $0
+	MOVW	MSR, R3
+	RLWNM	$0, R3, $~EE, R4
+	SYNC
+	MOVW	R4, MSR
+	WAITMSR
+	RETURN
+
+TEXT	splx(SB), $0
+	MOVW	MSR, R4
+	RLWMI	$0, R3, $EE, R4
+	SYNC
+	MOVW	R4, MSR
+	WAITMSR
+	RETURN
+
+TEXT	gettbl(SB), $0
+/*	MOVW	SPR(TBRL), R3	*/
+	WORD	$0x7c6c42e6	/* mftbl on 8xx series */
+	RETURN
+
+TEXT	getpvr(SB), $0
+	MOVW	SPR(PVR), R3
+	RETURN
+
+TEXT	getimmr(SB), $0
+	MOVW	SPR(IMMR), R3
+	RETURN
+
+TEXT	getdec(SB), $0
+	MOVW	SPR(DEC), R3
+	RETURN
+
+TEXT	putdec(SB), $0
+	MOVW	R3, SPR(DEC)
+	RETURN
+
+/*
+ * save state in Ureg on kernel stack.
+ * enter with R0 giving the PC from the call to `exception' from the vector.
+ * on return, SB (R2) has been set, and R3 has the Ureg*
+ */
+TEXT saveureg(SB), $-4
+	SUB	$UREGSPACE, R1
+	MOVMW	R2, 48(R1)	/* r2:r31 */
+	MOVW	$setSB(SB), R2
+	MOVW	SPR(SAVER1), R4
+	MOVW	R4, 44(R1)
+	MOVW	SPR(SAVER0), R5
+	MOVW	R5, 40(R1)
+	MOVW	CTR, R6
+	MOVW	R6, 36(R1)
+	MOVW	XER, R4
+	MOVW	R4, 32(R1)
+	MOVW	SPR(SAVECR), R5	/* CR */
+	MOVW	R5, 28(R1)
+	MOVW	SPR(SAVELR), R6	/* LR */
+	MOVW	R6, 24(R1)
+	/* pad at 20(R1) */
+	MOVW	SPR(SRR0), R4
+	MOVW	R4, 16(R1)	/* old PC */
+	MOVW	SPR(SRR1), R5
+	MOVW	R5, 12(R1)
+	MOVW	R0, 8(R1)	/* cause/vector, encoded in LR from vector */
+	ADD	$8, R1, R3	/* Ureg* */
+	STWCCC	R3, (R1)	/* break any pending reservations */
+	MOVW	$0, R0	/* R0ISZERO */
+	BR	(LR)
+
+/*
+ * restore state from Ureg
+ * SB (R2) is unusable on return
+ */
+TEXT restoreureg(SB), $-4
+	MOVMW	48(R1), R2	/* r2:r31 */
+	/* defer R1 */
+	MOVW	40(R1), R0
+	MOVW	R0, SPR(SAVER0)
+	MOVW	36(R1), R0
+	MOVW	R0, CTR
+	MOVW	32(R1), R0
+	MOVW	R0, XER
+	MOVW	28(R1), R0
+	MOVW	R0, CR	/* CR */
+	MOVW	24(R1), R0
+	MOVW	R0, SPR(SAVELR)	/* LR */
+	/* pad, skip */
+	MOVW	16(R1), R0
+	MOVW	R0, SPR(SRR0)	/* old PC */
+	MOVW	12(R1), R0
+	MOVW	R0, SPR(SRR1)	/* old MSR */
+	/* cause, skip */
+	MOVW	44(R1), R1	/* old SP */
+	BR	(LR)
+
+TEXT	exception(SB), $-4
+	MOVW	R1, SPR(SAVER1)
+	MOVW	CR, R0
+	MOVW	R0, SPR(SAVECR)
+	MOVW	LR, R0
+	BL	saveureg(SB)
+	MOVW	$0, R0
+	BL	trap(SB)
+	BL	restoreureg(SB)
+	MOVW	SPR(SAVELR), R0
+	MOVW	R0, LR
+	MOVW	SPR(SAVER0), R0
+	ISYNC
+	RFI
+
+GLOBL	mach0+0(SB), $MACHSIZE
+GLOBL	m(SB), $4
--- /dev/null
+++ b/os/boot/mpc/lib.h
@@ -1,0 +1,106 @@
+/*
+ * functions (possibly) linked in, complete, from libc.
+ */
+
+/*
+ * mem routines
+ */
+extern	void*	memccpy(void*, void*, int, long);
+extern	void*	memset(void*, int, long);
+extern	int	memcmp(void*, void*, long);
+extern	void*	memmove(void*, void*, long);
+extern	void*	memchr(void*, int, long);
+
+/*
+ * string routines
+ */
+extern	char*	strcat(char*, char*);
+extern	char*	strchr(char*, char);
+extern	int	strcmp(char*, char*);
+extern	char*	strcpy(char*, char*);
+extern	char*	strncat(char*, char*, long);
+extern	char*	strncpy(char*, char*, long);
+extern	int	strncmp(char*, char*, long);
+extern	long	strlen(char*);
+extern	char*	strrchr(char*, char);
+extern	char*	strstr(char*, char*);
+
+/*
+ * print routines
+ * 	Fconv isn't used but is defined to satisfy prototypes in libg.h
+ *	that are never called.
+ */
+typedef	struct Fconv Fconv;
+
+extern	char*	donprint(char*, char*, char*, void*);
+extern	int	sprint(char*, char*, ...);
+extern	int	print(char*, ...);
+
+#define	PRINTSIZE	256
+
+/*
+ * one-of-a-kind
+ */
+extern	int	atoi(char*);
+extern	long	strtol(char*, char**, int);
+extern	ulong	strtoul(char*, char**, int);
+extern	long	end;
+
+/*
+ * Syscall data structures
+ */
+
+#define	MORDER	0x0003	/* mask for bits defining order of mounting */
+#define	MREPL	0x0000	/* mount replaces object */
+#define	MBEFORE	0x0001	/* mount goes before others in union directory */
+#define	MAFTER	0x0002	/* mount goes after others in union directory */
+#define	MCREATE	0x0004	/* permit creation in mounted directory */
+#define	MMASK	0x0007	/* all bits on */
+
+#define	OREAD	0	/* open for read */
+#define	OWRITE	1	/* write */
+#define	ORDWR	2	/* read and write */
+#define	OEXEC	3	/* execute, == read but check execute permission */
+#define	OTRUNC	16	/* or'ed in (except for exec), truncate file first */
+#define	OCEXEC	32	/* or'ed in, close on exec */
+#define	ORCLOSE	64	/* or'ed in, remove on close */
+
+#define	NCONT	0	/* continue after note */
+#define	NDFLT	1	/* terminate after note */
+
+typedef struct Qid	Qid;
+typedef struct Dir	Dir;
+typedef struct Waitmsg	Waitmsg;
+
+#define	ERRLEN	64
+#define	DIRLEN	116
+#define	NAMELEN	28
+
+struct Qid
+{
+	ulong	path;
+	ulong	vers;
+};
+
+struct Dir
+{
+	char	name[NAMELEN];
+	char	uid[NAMELEN];
+	char	gid[NAMELEN];
+	Qid	qid;
+	ulong	mode;
+	long	atime;
+	long	mtime;
+	vlong	length;
+	short	type;
+	short	dev;
+};
+
+struct Waitmsg
+{
+	int	pid;		/* of loved one */
+	int	status;		/* unused; a placeholder */
+	ulong	time[3];	/* of loved one */
+	char	msg[ERRLEN];
+};
+#define	nelem(x)	(sizeof(x)/sizeof((x)[0]))
--- /dev/null
+++ b/os/boot/mpc/main.c
@@ -1,0 +1,524 @@
+#include "u.h"
+#include "lib.h"
+#include "mem.h"
+#include "dat.h"
+#include "fns.h"
+#include "io.h"
+
+#include "dosfs.h"
+
+typedef struct Type Type;
+typedef struct Medium Medium;
+typedef struct Mode Mode;
+
+enum {
+	Dany		= -1,
+	Nmedia		= 16,
+
+	/* DS1 switch options */
+	Sflashfs		= 1<<0,	/* take local fs from flash */
+	Snotflash		= 1<<1,	/* don't boot from flash */
+};
+
+enum {					/* type */
+	Tflash,
+	Tuart,
+	Tether,
+	Thard,
+
+	Tany		= -1,
+};
+
+enum {					/* flag and name */
+	Fnone		= 0x00,
+
+	Fdos		= 0x01,
+	Ndos		= 0x00,
+	Fboot		= 0x02,
+	Nboot		= 0x01,
+	Fbootp		= 0x04,
+	Nbootp		= 0x02,
+	Fflash		= 0x08,
+	Fuart		= 0x10,
+	NName		= 0x03,
+
+	Fany		= Fbootp|Fboot|Fdos|Fflash|Fuart,
+
+	Fini		= 0x10,
+	Fprobe		= 0x80,
+};
+
+enum {					/* mode */
+	Mauto		= 0x00,
+	Mlocal		= 0x01,
+	Manual		= 0x02,
+	NMode		= 0x03,
+};
+
+typedef struct Type {
+	int	type;
+	char	*cname;
+	int	flag;
+	int	(*init)(void);
+	long	(*read)(int, void*, long);
+	long	(*seek)(int, long);
+	Partition* (*setpart)(int, char*);
+	char*	name[NName];
+
+	int	mask;
+	Medium*	media;
+} Type;
+
+typedef struct Medium {
+	Type*	type;
+	int	flag;
+	Partition* partition;
+	Dos;
+
+	Medium*	next;
+} Medium;
+
+typedef struct Mode {
+	char*	name;
+	int	mode;
+} Mode;
+
+static Type types[] = {
+	{	Tflash, "flash",
+		Fflash,
+		flashinit, 0, 0, 0,
+		{ 0, "F", 0, }
+	},
+/*
+	{	Tuart, "uart",
+		Fuart|Fboot,
+		uartinit, uartread, uartseek, setuartpart,
+		{ 0, "u", 0, }
+	},
+*/
+	{	Tether, "ether",
+		Fbootp,
+		etherinit, 0, 0, 0,
+		{ 0, 0, "e", },
+	},
+	{	Thard, "ata",
+		Fini|Fboot|Fdos,
+		0, 0, 0, 0,		/* not used now, will be later with PCMCIA */
+		{ "hd", "h", 0, },
+	},
+	{-1},
+};
+
+static Medium media[Nmedia];
+static Medium *curmedium = media;
+
+static Mode modes[NMode+1] = {
+	[Mauto]		{ "auto",   Mauto,  },
+	[Mlocal]	{ "local",  Mlocal, },
+	[Manual]	{ "manual", Manual, },
+};
+
+static char *inis[] = {
+	"inferno/inferno.ini",
+	"inferno.ini",
+	"plan9/plan9.ini",
+	"plan9.ini",
+	0,
+};
+char **ini;
+int	predawn;
+
+static int
+parse(char *line, int *type, int *flag, int *dev, char *file)
+{
+	Type *tp;
+	char buf[2*NAMELEN], *v[4], *p;
+	int i;
+
+	strcpy(buf, line);
+	switch(getcfields(buf, v, 4, "!")){
+
+	case 3:
+		break;
+
+	case 2:
+		v[2] = "";
+		break;
+
+	default:
+		return 0;
+	}
+
+	*flag = 0;
+	for(tp = types; tp->cname; tp++){
+		for(i = 0; i < NName; i++){
+
+			if(tp->name[i] == 0 || strcmp(v[0], tp->name[i]))
+				continue;
+			*type = tp->type;
+			*flag |= 1<<i;
+
+			if((*dev = strtoul(v[1], &p, 0)) == 0 && p == v[1])
+				return 0;
+		
+			strcpy(file, v[2]);
+		
+			return 1;
+		}
+	}
+
+	return 0;
+
+}
+
+static int
+boot(Medium *mp, int flag, char *file)
+{
+	Dosfile df;
+	char ixdos[128], *p;
+	int r;
+
+	uartsetboot(0);
+	if(flag & Fbootp){
+		sprint(BOOTLINE, "%s!%d", mp->type->name[Nbootp], mp->dev);
+		return bootp(mp->dev, file);
+	}
+
+	if(flag & Fflash){
+		if(mp->flag & Fflash && flashbootable(0))
+			flashboot(mp->dev);
+	}
+
+	if(flag & Fboot){
+
+		if(mp->flag & Fini){
+			(*mp->type->setpart)(mp->dev, "disk");
+			plan9ini(mp, nil);
+		}
+		if(file == 0 || *file == 0)
+			file = mp->partition->name;
+		(*mp->type->setpart)(mp->dev, file);
+		sprint(BOOTLINE, "%s!%d!%s", mp->type->name[Nboot], mp->dev, file);
+		r = plan9boot(mp->dev, mp->seek, mp->read);
+		uartsetboot(0);
+		return r;
+	}
+
+	if(flag & Fdos){
+		if(mp->type->setpart)
+			(*mp->type->setpart)(mp->dev, "disk");
+		if(mp->flag & Fini)
+			plan9ini(mp, nil);
+		if(file == 0 || *file == 0){
+			strcpy(ixdos, *ini);
+			if(p = strrchr(ixdos, '/'))
+				p++;
+			else
+				p = ixdos;
+			strcpy(p, "impc");
+			if(dosstat(mp, ixdos, &df) <= 0)
+				return -1;
+		}
+		else
+			strcpy(ixdos, file);
+		sprint(BOOTLINE, "%s!%d!%s", mp->type->name[Ndos], mp->dev, ixdos);
+		return dosboot(mp, ixdos);
+	}
+
+	return -1;
+}
+
+static Medium*
+allocm(Type *tp)
+{
+	Medium **l;
+
+	if(curmedium >= &media[Nmedia])
+		return 0;
+
+	for(l = &tp->media; *l; l = &(*l)->next)
+		;
+	*l = curmedium++;
+	return *l;
+}
+
+Medium*
+probe(int type, int flag, int dev)
+{
+	Type *tp;
+	int dombr, i, start;
+	Medium *mp;
+	Dosfile df;
+	Partition *pp;
+
+	for(tp = types; tp->cname; tp++){
+		if(type != Tany && type != tp->type || tp->init == 0)
+			continue;
+
+		if(flag != Fnone){
+			for(mp = tp->media; mp; mp = mp->next){
+				if((flag & mp->flag) && (dev == Dany || dev == mp->dev))
+					return mp;
+			}
+		}
+		if((tp->flag & Fprobe) == 0){
+			tp->flag |= Fprobe;
+			tp->mask = (*tp->init)();
+		}
+
+		for(i = 0; tp->mask; i++){
+			if((tp->mask & (1<<i)) == 0)
+				continue;
+			tp->mask &= ~(1<<i);
+
+			if((mp = allocm(tp)) == 0)
+				continue;
+
+			mp->dev = i;
+			mp->flag = tp->flag;
+			mp->seek = tp->seek;
+			mp->read = tp->read;
+			mp->type = tp;
+
+			if(mp->flag & Fboot){
+				if((mp->partition = (*tp->setpart)(i, "boot")) == 0)
+					mp->flag &= ~Fboot;
+				if((mp->flag & (Fflash|Fuart)) == 0)
+					(*tp->setpart)(i, "disk");
+			}
+
+			if(mp->flag & Fdos){
+				start = 0;
+				dombr = 1;
+				if(mp->type->setpart){
+					if(pp = (*mp->type->setpart)(i, "dos")){
+						if(start = pp->start)
+							dombr = 0;
+					}
+					(*tp->setpart)(i, "disk");
+				}
+				if(dosinit(mp, start, dombr) < 0)
+					mp->flag &= ~(Fini|Fdos);
+				else
+					print("dos init failed\n");
+			}
+
+			if(mp->flag & Fini){
+				mp->flag &= ~Fini;
+				for(ini = inis; *ini; ini++){
+					if(dosstat(mp, *ini, &df) <= 0)
+						continue;
+					mp->flag |= Fini;
+					break;
+				}
+			}
+
+			if((flag & mp->flag) && (dev == Dany || dev == i))
+				return mp;
+		}
+	}
+
+	return 0;
+}
+
+void
+main(void)
+{
+	Medium *mp;
+	int dev, flag, i, mode, tried, type, options;
+	char def[2*NAMELEN], file[2*NAMELEN], line[80], *p;
+	Type *tp;
+
+	machinit();
+	archinit();
+	meminit();
+	cpminit();
+	trapinit();
+	consinit();	/* screen and keyboard initially */
+	screeninit();
+	cpuidprint();
+	alarminit();
+	clockinit();
+	predawn = 0;
+	spllo();
+	options = archoptionsw();
+
+	mp = 0;
+	for(tp = types; tp->cname; tp++){
+		if(tp->type == Tether)
+			continue;
+		if((mp = probe(tp->type, Fini, Dany)) && (mp->flag & Fini)){
+			plan9ini(mp, nil);
+			break;
+		}
+	}
+
+	if(mp == 0 || (mp->flag & Fini) == 0)
+		plan9ini(nil, flashconfig(0));
+
+	//consinit();	/* establish new console location */
+
+	if((options & Snotflash) == 0 && flashbootable(0)){
+		print("Flash boot\n");
+		flashboot(0);
+	}
+
+	tried = 0;
+	mode = Mauto;
+	p = getconf("bootfile");
+	flag = 0;
+
+	if(p != 0) {
+		mode = Manual;
+		for(i = 0; i < NMode; i++){
+			if(strcmp(p, modes[i].name) == 0){
+				mode = modes[i].mode;
+				goto done;
+			}
+		}
+		if(parse(p, &type, &flag, &dev, file) == 0) {
+			print("Bad bootfile syntax: %s\n", p);
+			goto done;
+		}
+		mp = probe(type, flag, dev);
+		if(mp == 0) {
+			print("Cannot access device: %s\n", p);
+			goto done;
+		}
+		tried = boot(mp, flag, file);
+	}
+done:
+	if(tried == 0 && mode != Manual){
+		flag = Fany;
+		if(mode == Mlocal)
+			flag &= ~Fbootp;
+		if(options & Snotflash)
+			flag &= ~Fflash;
+		if((mp = probe(Tany, flag, Dany)) != 0)
+			boot(mp, flag & mp->flag, 0);
+	}
+
+	def[0] = 0;
+	probe(Tany, Fnone, Dany);
+
+	flag = 0;
+	for(tp = types; tp->cname; tp++){
+		for(mp = tp->media; mp; mp = mp->next){
+			if(flag == 0){
+				flag = 1;
+				print("Boot devices:");
+			}
+
+			if(mp->flag & Fbootp)
+				print(" %s!%d", mp->type->name[Nbootp], mp->dev);
+			if(mp->flag & Fdos)
+				print(" %s!%d", mp->type->name[Ndos], mp->dev);
+			if(mp->flag & (Fflash|Fuart) || mp->flag & Fboot)
+				print(" %s!%d", mp->type->name[Nboot], mp->dev);
+		}
+	}
+	if(flag)
+		print("\n");
+
+	for(;;){
+		if(getstr("boot from", line, sizeof(line), def) >= 0){
+			if(parse(line, &type, &flag, &dev, file)){
+				if(mp = probe(type, flag, dev))
+					boot(mp, flag, file);
+			}
+		}
+		def[0] = 0;
+	}
+}
+
+void
+machinit(void)
+{
+	memset(m, 0, sizeof(*m));
+	m->delayloop = 20000;
+	m->cpupvr = getpvr();
+	m->iomem = KADDR(INTMEM);
+}
+
+int
+getcfields(char* lp, char** fields, int n, char* sep)
+{
+	int i;
+
+	for(i = 0; lp && *lp && i < n; i++){
+		while(*lp && strchr(sep, *lp) != 0)
+			*lp++ = 0;
+		if(*lp == 0)
+			break;
+		fields[i] = lp;
+		while(*lp && strchr(sep, *lp) == 0){
+			if(*lp == '\\' && *(lp+1) == '\n')
+				*lp++ = ' ';
+			lp++;
+		}
+	}
+
+	return i;
+}
+
+static	Map	memv[512];
+static	RMap	rammap = {"physical memory"};
+
+void
+meminit(void)
+{
+	ulong e;
+
+	mapinit(&rammap, memv, sizeof(memv));
+	e = PADDR(&end);
+	mapfree(&rammap, e, 4*1024*1024-e);	/* fixed 4Mbytes is plenty for bootstrap */
+}
+
+void*
+ialloc(ulong n, int align)
+{
+	ulong a;
+	int s;
+
+	if(align <= 0)
+		align = 4;
+	s = splhi();
+	a = mapalloc(&rammap, 0, n, align);
+	splx(s);
+	if(a == 0)
+		panic("ialloc");
+	return memset(KADDR(a), 0, n);
+}
+
+void*
+malloc(ulong n)
+{
+	ulong *p;
+
+	n = ((n+sizeof(int)-1)&~(sizeof(int)-1))+2*sizeof(int);
+	p = ialloc(n, sizeof(int));
+	*p++ = 0xcafebeef;
+	*p++ = n;
+	return p;
+}
+
+void
+free(void *ap)
+{
+	int s;
+	ulong *p;
+
+	p = ap;
+	if(p){
+		if(*(p -= 2) != 0xcafebeef)
+			panic("free");
+		s = splhi();
+		mapfree(&rammap, (ulong)p, p[1]);
+		splx(s);
+	}
+}
+
+void
+sched(void)
+{
+}
--- /dev/null
+++ b/os/boot/mpc/mem.h
@@ -1,0 +1,95 @@
+/*
+ * Memory and machine-specific definitions.  Used in C and assembler.
+ */
+
+/*
+ * Sizes
+ */
+#define	BI2BY		8			/* bits per byte */
+#define BI2WD		32			/* bits per word */
+#define	BY2WD		4			/* bytes per word */
+#define	BY2PG		4096			/* bytes per page */
+#define	WD2PG		(BY2PG/BY2WD)		/* words per page */
+#define	PGSHIFT		12			/* log(BY2PG) */
+#define PGROUND(s)	(((s)+(BY2PG-1))&~(BY2PG-1))
+
+#define	MAXMACH		1			/* max # cpus system can run */
+#define	CACHELINELOG	4
+#define CACHELINESZ	(1<<CACHELINELOG)
+
+/*
+ * Time
+ */
+#define	HZ		(50)				/* clock frequency */
+#define	MS2HZ		(1000/HZ)			/* millisec per clock tick */
+#define	TK2SEC(t)	((t)/HZ)			/* ticks to seconds */
+#define	TK2MS(t)	((((ulong)(t))*1000)/HZ)	/* ticks to milliseconds */
+#define	MS2TK(t)	((((ulong)(t))*HZ)/1000)	/* milliseconds to ticks */
+#define	MHz	1000000
+
+/*
+ * Fundamental values
+ */
+
+#define	KZERO	0	/* bootstrap runs in real mode */
+#define	MACHSIZE	4096
+
+/*
+ *  physical MMU
+ */
+#define KSEG0	0x20000000
+#define	KSEGM	0xE0000000	/* mask to check which seg */
+
+/*
+ * MSR bits
+ */
+
+#define	POW	0x40000	/* enable power mgmt */
+#define	TGPR	0x20000	/* GPR0-3 remapped; 603/603e specific */
+#define	ILE	0x10000	/* interrupts little endian */
+#define	EE	0x08000	/* enable external/decrementer interrupts */
+#define	PR	0x04000	/* =1, user mode */
+#define	FPE	0x02000	/* enable floating point */
+#define	ME	0x01000	/* enable machine check exceptions */
+#define	FE0	0x00800
+#define	SE	0x00400	/* single-step trace */
+#define	BE	0x00200	/* branch trace */
+#define	FE1	0x00100
+#define	IP	0x00040	/* =0, vector to nnnnn; =1, vector to FFFnnnnn */
+#define	IR	0x00020	/* enable instruction address translation */
+#define	DR	0x00010	/* enable data address translation */
+#define	RI	0x00002	/* exception is recoverable */
+#define	LE	0x00001	/* little endian mode */
+
+#define	KMSR	(ME|FE0|FE1|FPE)
+#define	UMSR	(KMSR|PR|EE|IR|DR)
+
+/*
+ * MPC82x addresses; mpc8bug is happy with these
+ */
+#define	BCSRMEM	0x02100000
+#define	INTMEM	0x02200000
+#define	FLASHMEM	0x02800000
+#define	SDRAMMEM	0x03000000
+
+#define	DPRAM	(INTMEM+0x2000)
+#define	DPLEN1	0x400
+#define	DPLEN2	0x200
+#define	DPLEN3	0x100
+#define	DPBASE	(DPRAM+DPLEN1)
+
+#define	SCC1P	(INTMEM+0x3C00)
+#define	I2CP	(INTMEM+0x3C80)
+#define	MISCP	(INTMEM+0x3CB0)
+#define	IDMA1P	(INTMEM+0x3CC0)
+#define	SCC2P	(INTMEM+0x3D00)
+#define	SCC3P	(INTMEM+0x3E00)
+#define	SCC4P	(INTMEM+0x3F00)
+#define	SPIP	(INTMEM+0x3D80)
+#define	TIMERP	(INTMEM+0x3DB0)
+#define	SMC1P	(INTMEM+0x3E80)
+#define	DSP1P	(INTMEM+0x3EC0)
+#define	SMC2P	(INTMEM+0x3F80)
+#define	DSP2P	(INTMEM+0x3FC0)
+
+#define KEEP_ALIVE_KEY 0x55ccaa33	/* clock and rtc register key */
--- /dev/null
+++ b/os/boot/mpc/mkfile
@@ -1,0 +1,116 @@
+objtype=power
+OBJTYPE=power	# always
+<../../../mkconfig
+SYSTARG=$OSTARG	# always
+<$ROOT/mkfiles/mkfile-$SYSTARG-$OBJTYPE
+INSTALLDIR=$ROOT/Inferno/$OBJTYPE/bin	#path of directory where kernel is installed
+ARCH=fads	# selects board dependent code
+TARG=qb$ARCH
+OFILES=\
+	l.$O\
+	arch$ARCH.$O\
+	devuart.$O\
+	uartboot.$O\
+	alarm.$O\
+	bootp.$O\
+	clock.$O\
+	conf.$O\
+	console.$O\
+	cpm.$O\
+	defont0.$O\
+	donprint.$O\
+	dosboot.$O\
+	devether.$O\
+	etherscc.$O\
+	fblt.$O\
+	gbitbltclip.$O\
+	flash.$O\
+	main.$O\
+	plan9boot.$O\
+	qio.$O\
+	rmap.$O\
+	screen.$O\
+	init$ARCH.$O\
+	trap.$O\
+	zqs.$O\
+
+HFILES=\
+	boot.h\
+	dat.h\
+	fns.h\
+	io.h\
+	lib.h\
+	mem.h\
+	squeeze.h\
+	gnot.h\
+	arch$ARCH.h\
+
+LIBS=\
+	kern\
+
+LIBDIRS=$LIBS
+LIBNAMES=${LIBS:%=lib%.a}
+LIBFILES=${LIBS:%=$ROOT/$TARGMODEL/$OBJTYPE/lib/lib%.a}
+
+#all:NV:	$TARG k.mx f.mx
+all:NV:	$TARG
+install:V: 	$INSTALLDIR/$TARG
+installall:V: 	$INSTALLDIR/$TARG
+
+$INSTALLDIR/%: %
+	rm -f $INSTALLDIR/$stem && cp $stem $INSTALLDIR/$stem
+
+$TARG: $OFILES $LIBNAMES
+	$LD -o $target -l  -T0x140000 -R4 $OFILES $LIBFILES
+	ls -l $target
+
+qbrom$ARCH:	$OFILES $LIBNAMES
+	$LD -o $target -l -T0x02800100 -R0 -D0x140000 $OFILES $LIBFILES
+
+k.mx:	$TARG
+	ms2 -S 0x100 -a 0x100 -p 4 $TARG >k.mx
+
+f.mx:	qbrom$ARCH
+	ms2 -S 0x100 -a 0x2800100 -p 4 $prereq >f.mx
+
+%.$O:	%.s
+	$AS $stem.s
+
+%.$O:	%.c
+	$CC $CFLAGS $stem.c
+
+%.$O:	$HFILES
+
+lib%.a:V:	$SHELLTYPE-lib%.a
+
+rc-lib%.a nt-lib%.a:VQ:
+	echo '@{builtin cd ' $ROOT/lib$stem ';mk SHELLTYPE=$SHELLTYPE SYSTARG=$SYSTARG OBJTYPE=$OBJTYPE install}'
+	@{builtin cd  $ROOT/lib$stem ;mk 'SHELLTYPE='$SHELLTYPE 'SYSTARG='$SYSTARG 'OBJTYPE='$OBJTYPE install}
+
+sh-lib%.a:VQ:
+	echo "(cd $ROOT/lib$stem ; mk SHELLTYPE=$SHELLTYPE SYSTARG=$SYSTARG OBJTYPE=$OBJTYPE install)"
+	(cd $ROOT/lib$stem ; mk SHELLTYPE=$SHELLTYPE SYSTARG=$SYSTARG OBJTYPE=$OBJTYPE install)
+
+clock.$O floppy.$O trap.$O:	ureg.h
+conf.$O dosboot.$O main.$O:	dosfs.h
+ether.$O etherscc.$O:	etherif.h
+bootp.$O:	ip.h
+
+clean:V:
+	rm -f *.[$OS] [$OS].out y.tab.? y.debug y.output $TARG qboot k.mx f.mx romboot
+
+nuke-sh:QV:
+		for i in $LIBDIRS
+		do
+			echo "(cd $ROOT/lib$i ; mk SHELLTYPE=$SHELLTYPE SYSTARG=$SYSTARG OBJTYPE=$OBJTYPE nuke)"
+			(cd $ROOT/lib$i; mk 'SHELLTYPE='$SHELLTYPE 'SYSTARG='$SYSTARG 'OBJTYPE='$OBJTYPE nuke)
+		done
+
+nuke-rc nuke-nt:QV:
+		for (i in $LIBDIRS)
+		{
+			echo '@{cd $ROOT/lib$i ; mk SHELLTYPE=$SHELLTYPE SYSTARG=$SYSTARG OBJTYPE=$OBJTYPE nuke}'
+			@{cd $ROOT/lib$i; mk 'SHELLTYPE='$SHELLTYPE 'SYSTARG='$SYSTARG 'OBJTYPE='$OBJTYPE nuke}
+		}
+
+nuke:V:		clean nuke-$SHELLTYPE
--- /dev/null
+++ b/os/boot/mpc/ms2.c
@@ -1,0 +1,179 @@
+#include <u.h>
+#include <libc.h>
+#include <bio.h>
+#include <mach.h>
+
+void	record(uchar*, int);
+void	usage(void);
+void	dosegment(long, int);
+void trailer(ulong);
+
+enum
+{
+	Recordsize = 32,
+};
+
+int	dsegonly;
+int	supressend;
+int	binary;
+int	addr4;
+ulong	addr;
+ulong 	psize = 4096;
+ulong	startaddr = 0x030000;
+Biobuf 	stdout;
+Biobuf	bio;
+
+void
+main(int argc, char **argv)
+{
+	Dir dir;
+	Fhdr f;
+	int fd;
+
+	ARGBEGIN{
+	case 'd':
+		dsegonly++;
+		break;
+	case 's':
+		supressend++;
+		break;
+	case 'a':
+		addr = strtoul(ARGF(), 0, 0);
+		break;
+	case 'p':
+		psize = strtoul(ARGF(), 0, 0);
+		break;
+	case 'b':
+		binary++;
+		break;
+	case 'S':
+		startaddr = strtoul(ARGF(), 0, 0);
+		break;
+	case '4':
+		addr4++;
+		break;
+	default:
+		usage();
+	}ARGEND
+
+	if(argc != 1)
+		usage();
+
+	Binit(&stdout, 1, OWRITE);
+
+	fd = open(argv[0], OREAD);
+	if(fd < 0) {
+		fprint(2, "ms2: open %s: %r\n", argv[0]);
+		exits("open");
+	}
+
+	if(binary) {
+		if(dirfstat(fd, &dir) < 0) {
+			fprint(2, "ms2: stat failed %r");
+			exits("dirfstat");
+		}
+		Binit(&bio, fd, OREAD);
+		dosegment(0, dir.length);
+		if(supressend == 0)
+			trailer(startaddr);
+		Bterm(&stdout);
+		Bterm(&bio);
+		exits(0);
+	}
+
+	if(crackhdr(fd, &f) == 0){
+		fprint(2, "ms2: bad magic: %r\n");
+		exits("magic");
+	}
+	seek(fd, 0, 0);
+
+	Binit(&bio, fd, OREAD);
+
+	if(dsegonly)
+		dosegment(f.datoff, f.datsz);
+	else {
+		dosegment(f.txtoff, f.txtsz);
+		addr = (addr+(psize-1))&~(psize-1);
+		dosegment(f.datoff, f.datsz);
+	}
+
+	if(supressend == 0)
+		trailer(startaddr);
+
+	Bterm(&stdout);
+	Bterm(&bio);
+	exits(0);
+}
+
+void
+dosegment(long foff, int len)
+{
+	int l, n;
+	uchar buf[2*Recordsize];
+
+	Bseek(&bio, foff, 0);
+	for(;;) {
+		l = len;
+		if(l > Recordsize)
+			l = Recordsize;
+		n = Bread(&bio, buf, l);
+		if(n == 0)
+			break;
+		if(n < 0) {
+			fprint(2, "ms2: read error: %r\n");
+			exits("read");
+		}
+		record(buf, l);
+		len -= l;
+	}
+}
+
+void
+record(uchar *s, int l)
+{
+	int i;
+	ulong cksum;
+
+	if(addr4 || addr & (0xFF<<24)){
+		Bprint(&stdout, "S3%.2X%.8luX", l+5, addr);
+		cksum = l+5;
+		cksum += (addr>>24)&0xff;
+	}else{
+		Bprint(&stdout, "S2%.2X%.6X", l+4, addr);
+		cksum = l+4;
+	}
+	cksum += addr&0xff;
+	cksum += (addr>>8)&0xff;
+	cksum += (addr>>16)&0xff;
+
+	for(i = 0; i < l; i++) {
+		cksum += *s;
+		Bprint(&stdout, "%.2X", *s++);
+	}
+	Bprint(&stdout, "%.2X\n", (~cksum)&0xff);
+	addr += l;
+}
+
+void
+trailer(ulong a)
+{
+	ulong cksum;
+
+	cksum = 0;
+	if(addr4 || a & (0xFF<<24)){
+		Bprint(&stdout, "S7%.8luX", a);
+		cksum += (a>>24)&0xff;
+	}else
+		Bprint(&stdout, "S9%.6X", a);
+	cksum += a&0xff;
+	cksum += (a>>8)&0xff;
+	cksum += (a>>16)&0xff;
+	Bprint(&stdout, "%.2X\n", (~cksum)&0xff);
+}
+
+void
+usage(void)
+{
+	fprint(2, "usage: ms2 [-ds] [-a address] [-p pagesize] ?.out\n");
+	exits("usage");
+}
--- /dev/null
+++ b/os/boot/mpc/plan9boot.c
@@ -1,0 +1,96 @@
+#include	"u.h"
+#include	"lib.h"
+#include	"mem.h"
+#include	"dat.h"
+#include	"fns.h"
+
+char *premature = "premature EOF\n";
+
+/*
+ *  read in a segment
+ */
+static long
+readseg(int dev, long (*read)(int, void*, long), long len, long addr)
+{
+	char *a;
+	long n, sofar;
+
+	a = (char *)addr;
+	for(sofar = 0; sofar < len; sofar += n){
+		n = 8*1024;
+		if(len - sofar < n)
+			n = len - sofar;
+		n = (*read)(dev, a + sofar, n);
+		if(n <= 0)
+			break;
+		print(".");
+	}
+	return sofar;
+}
+
+/*
+ *  boot
+ */
+int
+plan9boot(int dev, long (*seek)(int, long), long (*read)(int, void*, long))
+{
+	long n;
+	long addr;
+	void (*b)(void);
+	Exec *ep;
+
+	if((*seek)(dev, 0) < 0)
+		return -1;
+
+	/*
+	 *  read header
+	 */
+	ep = (Exec *) ialloc(sizeof(Exec), 0);
+	n = sizeof(Exec);
+	if(readseg(dev, read, n, (long) ep) != n){
+		print(premature);
+		return -1;
+	}
+	if(GLLONG(ep->magic) != Q_MAGIC){
+		print("bad magic 0x%lux not a plan 9 executable!\n", GLLONG(ep->magic));
+		return -1;
+	}
+
+	/*
+	 *  read text
+	 */
+	addr = PADDR(GLLONG(ep->entry));
+	n = GLLONG(ep->text);
+	print("%d", n);
+	if(readseg(dev, read, n, addr) != n){
+		print(premature);
+		return -1;
+	}
+
+	/*
+	 *  read data (starts at first page after kernel)
+	 */
+	addr = PGROUND(addr+n);
+	n = GLLONG(ep->data);
+	print("+%d@%8.8lux", n, addr);
+	if(readseg(dev, read, n, addr) != n){
+		print(premature);
+		return -1;
+	}
+
+	/*
+	 *  bss and entry point
+	 */
+	print("+%d\nstart at 0x%lux\n", GLLONG(ep->bss), GLLONG(ep->entry));
+	uartwait();
+	scc2stop();
+	splhi();
+
+	/*
+	 *  Go to new code. It's up to the program to get its PC relocated to
+	 *  the right place.
+	 */
+	b = (void (*)(void))(PADDR(GLLONG(ep->entry)));
+	(*b)();
+	return 0;
+}
--- /dev/null
+++ b/os/boot/mpc/qio.c
@@ -1,0 +1,128 @@
+#include "u.h"
+#include "lib.h"
+#include "mem.h"
+#include "dat.h"
+#include "fns.h"
+#include "io.h"
+
+struct Queue {
+	Block*	first;
+	Block*	last;
+	void	(*kick)(void*);
+	void*	arg;
+	long	len;
+};
+
+Block *
+iallocb(int n)
+{
+	Block *b;
+
+	b = (Block*)malloc(sizeof(Block)+n);
+	b->data = (uchar*)b + sizeof(Block);
+	b->rp = b->wp = b->data;
+	b->lim = b->data + n;
+	b->next = 0;
+	b->magic = 0xcafebee0;
+	return b;
+}
+
+void
+freeb(Block *b)
+{
+	if(b){
+		if(b->magic != 0xcafebee0)
+			panic("freeb");
+		b->magic = 0;
+		b->next = (Block*)0xdeadbabe;
+		free(b);
+	}
+}
+
+Queue *
+qopen(int limit, int msg, void (*kick)(void*), void *arg)
+{
+	Queue *q;
+
+	USED(limit, msg);
+	q = (Queue*)malloc(sizeof(Queue));
+	q->first = q->last = 0;
+	q->kick = kick;
+	q->arg = arg;
+	q->len = 0;
+	return q;
+}
+
+Block *
+qget(Queue *q)
+{
+	int s;
+	Block *b;
+
+	s = splhi();
+	if((b = q->first) != 0){
+		q->first = b->next;
+		b->next = 0;
+		q->len -= BLEN(b);
+		if(q->len < 0)
+			panic("qget");
+	}
+	splx(s);
+	return b;
+}
+
+void
+qbwrite(Queue *q, Block *b)
+{
+	int s;
+
+	s = splhi();
+	b->next = 0;
+	if(q->first == 0)
+		q->first = b;
+	else
+		q->last->next = b;
+	q->last = b;
+	q->len += BLEN(b);
+	splx(s);
+	if(q->kick)
+		q->kick(q->arg);
+}
+
+long
+qlen(Queue *q)
+{
+	return q->len;
+}
+
+int
+qbgetc(Queue *q)
+{
+	Block *b;
+	int s, c;
+
+	c = -1;
+	s = splhi();
+	while(c < 0 && (b = q->first) != nil){
+		if(b->rp < b->wp){
+			c = *b->rp++;
+			q->len--;
+		}
+		if(b->rp >= b->wp){
+			q->first = b->next;
+			b->next = nil;
+		}
+	}
+	splx(s);
+	return c;
+}
+
+void
+qbputc(Queue *q, int c)
+{
+	Block *b;
+
+	b = iallocb(1);
+	*b->wp++ = c;
+	qbwrite(q, b);
+}
--- /dev/null
+++ b/os/boot/mpc/rmap.c
@@ -1,0 +1,104 @@
+#include "u.h"
+#include "lib.h"
+#include "mem.h"
+#include "dat.h"
+#include "fns.h"
+
+void
+mapinit(RMap *rmap, Map *map, int size)
+{
+	lock(rmap);
+	rmap->map = map;
+	rmap->mapend = map+(size/sizeof(Map));
+	unlock(rmap);
+}
+
+void
+mapfree(RMap* rmap, ulong addr, int size)
+{
+	Map *mp;
+	ulong t;
+
+	if(size <= 0)
+		return;
+
+	lock(rmap);
+	for(mp = rmap->map; mp->addr <= addr && mp->size; mp++)
+		;
+
+	if(mp > rmap->map && (mp-1)->addr+(mp-1)->size == addr){
+		(mp-1)->size += size;
+		if(addr+size == mp->addr){
+			(mp-1)->size += mp->size;
+			while(mp->size){
+				mp++;
+				(mp-1)->addr = mp->addr;
+				(mp-1)->size = mp->size;
+			}
+		}
+	}
+	else{
+		if(addr+size == mp->addr && mp->size){
+			mp->addr -= size;
+			mp->size += size;
+		}
+		else do{
+			if(mp >= rmap->mapend){
+				print("mapfree: %s: losing 0x%uX, %d\n",
+					rmap->name, addr, size);
+				break;
+			}
+			t = mp->addr;
+			mp->addr = addr;
+			addr = t;
+			t = mp->size;
+			mp->size = size;
+			mp++;
+		}while(size = t);
+	}
+	unlock(rmap);
+}
+
+ulong
+mapalloc(RMap* rmap, ulong addr, int size, int align)
+{
+	Map *mp;
+	ulong maddr, oaddr;
+
+	lock(rmap);
+	for(mp = rmap->map; mp->size; mp++){
+		maddr = mp->addr;
+
+		if(addr){
+			if(maddr > addr)
+				continue;
+			if(addr+size > maddr+mp->size)
+				break;
+			maddr = addr;
+		}
+
+		if(align > 0)
+			maddr = ((maddr+align-1)/align)*align;
+		if(mp->addr+mp->size-maddr < size)
+			continue;
+
+		oaddr = mp->addr;
+		mp->addr = maddr+size;
+		mp->size -= maddr-oaddr+size;
+		if(mp->size == 0){
+			do{
+				mp++;
+				(mp-1)->addr = mp->addr;
+			}while((mp-1)->size = mp->size);
+		}
+
+		unlock(rmap);
+		if(oaddr != maddr)
+			mapfree(rmap, oaddr, maddr-oaddr);
+
+		return maddr;
+	}
+	unlock(rmap);
+
+	return 0;
+}
--- /dev/null
+++ b/os/boot/mpc/screen.c
@@ -1,0 +1,242 @@
+#include	"all.h"
+#include	<libg.h>
+#include	<gnot.h>
+
+enum {
+	Colldepth		= 3,
+	Colmaxx		= 640,
+	Colmaxxvis	= 640,
+	Colmaxy		= 480,
+};
+
+#define	MINX	8
+
+extern	GSubfont	defont0;
+
+struct{
+	Point	pos;
+	int	bwid;
+}out;
+
+typedef struct Mode Mode;
+struct Mode {
+	int	x;
+	int	y;
+	int	d;
+	char*	aperture;
+	int	apsize;
+};
+
+GBitmap	gscreen;
+Point	gchar(GBitmap*, Point, GFont*, int, Fcode);
+int	setcolor(ulong, ulong, ulong, ulong);
+static	void	lcdinit(Mode*);
+
+void
+screeninit(void)
+{
+	Mode m;
+
+	m.x = Colmaxx;
+	m.y = Colmaxy;
+	m.d = Colldepth;
+	m.aperture = 0;
+	lcdinit(&m);
+	if(m.aperture == 0)
+		return;
+	gscreen.ldepth = 3;
+	gscreen.base = (ulong*)m.aperture;
+	gscreen.width = Colmaxx/BY2WD;
+	gscreen.r = Rect(0, 0, Colmaxxvis, Colmaxy);
+	gscreen.clipr = gscreen.r;
+	/*
+	 * For now, just use a fixed colormap:
+	 * 0 == white and 255 == black
+	 */
+	setcolor(0, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF);
+	setcolor(255, 0x00000000, 0x00000000, 0x00000000);
+
+	gbitblt(&gscreen, Pt(0, 0), &gscreen, gscreen.r, Zero);
+	out.pos.x = MINX;
+	out.pos.y = 0;
+	out.bwid = defont0.info[' '].width;
+}
+
+void
+screenputc(int c)
+{
+	Fontchar *i;
+	Point p;
+
+	if(gscreen.base == nil)
+		return;
+	switch(c){
+	case '\n':
+		out.pos.x = MINX;
+		out.pos.y += defont0.height;
+		if(out.pos.y > gscreen.r.max.y-defont0.height)
+			out.pos.y = gscreen.r.min.y;
+		gbitblt(&gscreen, Pt(0, out.pos.y), &gscreen,
+		  Rect(0, out.pos.y, gscreen.r.max.x, out.pos.y+2*defont0.height),
+		  Zero);
+		break;
+	case '\t':
+		out.pos.x += (8-((out.pos.x-MINX)/out.bwid&7))*out.bwid;
+		if(out.pos.x >= gscreen.r.max.x)
+			screenputc('\n');
+		break;
+	case '\b':
+		if(out.pos.x >= out.bwid+MINX){
+			out.pos.x -= out.bwid;
+			screenputc(' ');
+			out.pos.x -= out.bwid;
+		}
+		break;
+	default:
+		if(out.pos.x >= gscreen.r.max.x-out.bwid)
+			screenputc('\n');
+		c &= 0x7f;
+		if(c <= 0 || c >= defont0.n)
+			break;
+		i = defont0.info + c;
+		p = out.pos;
+		gbitblt(&gscreen, Pt(p.x+i->left, p.y), defont0.bits,
+			Rect(i[0].x, 0, i[1].x, defont0.height),
+			S);
+		out.pos.x = p.x + i->width;
+		break;
+	}
+}
+
+void
+screenputs(char *s, int n)
+{
+	while(n-- > 0)
+		screenputc(*s++);
+}
+
+/*
+ * See section 5.2.1 (page 5-6) of the MPC823 manual
+ */
+static uchar lcdclock[17] = {	/* (a<<2)|b => divisor of (1<<a)*((b<<1)+1) */
+	0, 0, (1<<2), 1,
+	(2<<2), 2, (1<<2)|1, 3,
+	(3<<2), (1<<2)|2, (1<<2)|2, (2<<2)|1,
+	(2<<2)|1, (1<<2)|3, (1<<2)|3, (4<<2),
+	(4<<2)
+};
+	
+/*
+ * support for the Sharp LQ64D341 TFT colour display
+ */
+
+enum {
+	COLS = 640,
+	ROWS = 480,
+	LDEPTH = 3,	/* screen depth */
+	LCDFREQ = 25000000,
+
+	/* lccr */
+	ClockLow = 1<<11,
+	OELow = 1<<10,
+	HsyncLow = 1<<9,
+	VsyncLow = 1<<8,
+	DataLow = 1<<7,
+	Passive8 = 1<<4,
+	DualScan = 1<<3,
+	IsColour = 1<<2,
+	IsTFT = 1<<1,
+	Enable = 1<<0,
+
+	/* lchcr */
+	BigEndian = 1<<24,
+	AT7 = 7<<21,	/* access type */
+
+	/* sdcr */
+	LAM = 1<<6,	/* ``LCD aggressive mode'' */
+};
+
+/*
+ * TO DO: most of the data could come from a table
+ */
+static void
+lcdinit(Mode *mode)
+{
+	IMM *io;
+	int i, d;
+	long hz;
+
+	io = m->iomem;
+	mode->y = ROWS;
+	mode->x = COLS;
+	mode->d = LDEPTH;
+	mode->aperture = ialloc(mode->x*mode->y, 16);
+	mode->apsize = mode->x*mode->y;
+
+	io->sdcr &= ~LAM;	/* MPC823 errata: turn off LAM before disabling controller */
+	io->lcfaa = PADDR(mode->aperture);
+	io->lccr = (((mode->x*mode->y*(1<<LDEPTH)+127)/128) << 17) | (LDEPTH << 5) | IsColour | IsTFT | OELow | VsyncLow | ClockLow;
+
+	switch(LDEPTH){
+	default:
+	case 0:
+		/* monochrome/greyscale identity map */
+		for(i=0; i<16; i++)
+			io->lcdmap[i] = i;
+		break;
+	case 2:
+		/* 4-bit grey scale map */
+		for(i=0; i<16; i++)
+			io->lcdmap[0] = (i<<8)|(i<<4)|i;
+		break;
+	case 3:
+		/* 8-bit linear map */
+		for(i=0; i<256; i++)
+			io->lcdmap[i] = (i<<8)|(i<<4)|i;
+		break;
+	}
+
+	io->lcvcr = (mode->y << 11) | (1<<28) | 33;	/* 2 line vsync pulse, 34 line wait between frames */
+	io->lchcr = (mode->x<<10) | BigEndian | 228;	/* clock cycles between lines */
+
+	hz = m->cpuhz;
+	d = hz/LCDFREQ;
+	if(hz/d > LCDFREQ)
+		d++;
+	if(d >= 16)
+		d = 16;
+
+	/*
+	 * enable LCD outputs
+	 */
+	io->pddat = 0;
+	io->pdpar = 0x1fff;
+io->pdpar &= ~SIBIT(6);	/* 823 bug fix? */
+	io->pddir = 0x1fff;
+	io->pbpar |= IBIT(31) | IBIT(19) | IBIT(17);
+	io->pbdir |= IBIT(31) | IBIT(19) | IBIT(17);
+	io->pbodr &= ~(IBIT(31) | IBIT(19) | IBIT(17));
+
+	eieio();
+	io->sccrk = KEEP_ALIVE_KEY;
+	eieio();
+	io->sccr  = (io->sccr & ~0x1F) | lcdclock[d];
+	eieio();
+	io->sccrk = ~KEEP_ALIVE_KEY;
+	eieio();
+	gscreen.width = gscreen.width;	/* access external memory before enabling (mpc823 errata) */
+	io->lcsr = 7;	/* clear status */
+	eieio();
+	io->lccr |= Enable;
+	archbacklight(1);
+}
+
+int
+setcolor(ulong p, ulong r, ulong g, ulong b)
+{
+	r >>= 28;
+	g >>= 28;
+	b >>= 28;
+	m->iomem->lcdmap[~p&0xFF] = (r<<8) | (g<<4) | b;	/* TO DO: it's a function of the ldepth */
+	return 1;
+}
--- /dev/null
+++ b/os/boot/mpc/sload.c
@@ -1,0 +1,71 @@
+/*
+ * send S records to rpcg
+ */
+
+#include <u.h>
+#include <libc.h>
+#include <bio.h>
+
+static	int	dbg;
+static	char	buf[2048];
+static	int	run=1;
+static	void	stuffbym(char*, int, int);
+static	void	getdot(void);
+
+void
+main(int argc, char **argv)
+{
+	int n;
+	char *l;
+	Biobuf *f;
+	static int p;
+
+	ARGBEGIN{
+	case 'd': dbg++; break;
+	case 'n': run=0; break;
+	}ARGEND
+
+	f = Bopen(*argv? *argv: "k.mx", OREAD);
+	if(f == 0) {
+		fprint(2, "sload: cannot open k.mx: %r\n");
+		exits("sload");
+	}
+	getdot();
+	while((l = Brdline(f, '\n')) != 0) {
+		l[Blinelen(f)-1] = '\r';
+		stuffbym(l, Blinelen(f), 16);
+		getdot();
+		if(++p % 25 == 0)
+			write(2, ".", 1);
+	}
+	exits(0);
+}
+
+static void
+stuffbym(char *l, int n, int m)
+{
+	int nr, ns;
+
+	while(n > 0) {
+		ns = n;
+		if(ns > m)
+			ns = m;
+		write(1, l, ns);
+		l += ns;
+		n -= ns;
+	}
+}
+
+static void
+getdot(void)
+{
+	char c;
+
+	for(;;){
+		if(read(0, &c, 1) != 1)
+			exits("bang");
+		write(2, &c, 1);
+		if(c == '.')
+			break;
+	}
+}
--- /dev/null
+++ b/os/boot/mpc/squeeze.h
@@ -1,0 +1,34 @@
+
+/*
+ * squeezed file format:
+ *	Sqhdr
+ *	original Exec header
+ *	two Squeeze tables
+ *	squeezed segment
+ *	unsqueezed segment, if any
+ */
+#define	SQMAGIC	(ulong)0xFEEF0F1E
+
+typedef struct Sqhdr Sqhdr;
+struct Sqhdr {
+	uchar	magic[4];	/* SQMAGIC */
+	uchar	text[4];	/* squeezed length of text (excluding tables) */
+	uchar	data[4];	/* squeezed length of data (excluding tables) */
+	uchar	asis[4];	/* length of unsqueezed segment */
+	uchar	toptxt[4];	/* value for 0 encoding in text */
+	uchar	topdat[4];	/* value for 0 encoding in data */
+	uchar	sum[4];	/* simple checksum of unsqueezed data */
+	uchar	flags[4];
+};
+#define	SQHDRLEN	(8*4)
+
+/*
+ * certain power instruction types are rearranged by sqz
+ * so as to move the variable part of the instruction word to the
+ * low order bits.  note that the mapping is its own inverse.
+ */
+#define	QREMAP(X)\
+	switch((X)>>26){\
+	case 19: case 31: case 59: case 63:\
+		(X) = (((X) & 0xFC00F801) | (((X)>>15)&0x7FE) | (((X)&0x7FE)<<15));\
+	}
--- /dev/null
+++ b/os/boot/mpc/trap.c
@@ -1,0 +1,233 @@
+#include "boot.h"
+
+enum 
+{
+	Maxhandler=	32+16,		/* max number of interrupt handlers */
+};
+
+typedef struct Handler	Handler;
+struct Handler
+{
+	void	(*r)(Ureg*, void*);
+	void	*arg;
+	Handler	*next;
+	int	edge;
+};
+
+struct
+{
+	Handler	*ivec[128];
+	Handler	h[Maxhandler];
+	int	free;
+} halloc;
+
+char	*excname[] = {
+	"reserved 0",
+	"system reset",
+	"machine check",
+	"data access",
+	"instruction access",
+	"external interrupt",
+	"alignment",
+	"program exception",
+	"floating-point unavailable",
+	"decrementer",
+	"reserved A",
+	"reserved B",
+	"system call",
+	"trace trap",
+	"floating point assist",
+	"reserved F",
+	"software emulation",
+	"ITLB miss",
+	"DTLB miss",
+	"ITLB error",
+	"DTLB error",
+};
+
+char *regname[]={
+	"CAUSE",	"SRR1",
+	"PC",		"GOK",
+	"LR",		"CR",
+	"XER",	"CTR",
+	"R0",		"R1",
+	"R2",		"R3",
+	"R4",		"R5",
+	"R6",		"R7",
+	"R8",		"R9",
+	"R10",	"R11",
+	"R12",	"R13",
+	"R14",	"R15",
+	"R16",	"R17",
+	"R18",	"R19",
+	"R20",	"R21",
+	"R22",	"R23",
+	"R24",	"R25",
+	"R26",	"R27",
+	"R28",	"R29",
+	"R30",	"R31",
+};
+
+static	void	intr(Ureg*);
+
+void
+sethvec(int v, void (*r)(void))
+{
+	ulong *vp, pa, o;
+
+	if((ulong)r & 3)
+		panic("sethvec");
+	vp = (ulong*)KADDR(v);
+	vp[0] = 0x7c1043a6;	/* MOVW R0, SPR(SPRG0) */
+	vp[1] = 0x7c0802a6;	/* MOVW LR, R0 */
+	vp[2] = 0x7c1243a6;	/* MOVW R0, SPR(SPRG2) */
+	pa = PADDR(r);
+	o = pa >> 25;
+	if(o != 0 && o != 0x7F){
+		/* a branch too far: running from ROM */
+		vp[3] = (15<<26)|(pa>>16);	/* MOVW $r&~0xFFFF, R0 */
+		vp[4] = (24<<26)|(pa&0xFFFF);	/* OR $r&0xFFFF, R0 */
+		vp[5] = 0x7c0803a6;	/* MOVW	R0, LR */
+		vp[6] = 0x4e800021;	/* BL (LR) */
+	}else
+		vp[3] = (18<<26)|(pa&0x3FFFFFC)|3;	/* bla */
+}
+
+#define	LEV(n)	(((n)<<1)|1)
+#define	IRQ(n)	(((n)<<1)|0)
+
+void
+setvec(int v, void (*r)(Ureg*, void*), void *arg)
+{
+	Handler *h;
+	IMM *io;
+
+	if(halloc.free >= Maxhandler)
+		panic("out of interrupt handlers");
+	v -= VectorPIC;
+	h = &halloc.h[halloc.free++];
+	h->next = halloc.ivec[v];
+	h->r = r;
+	h->arg = arg;
+	halloc.ivec[v] = h;
+
+	/*
+	 * enable corresponding interrupt in SIU/CPM
+	 */
+
+	io = m->iomem;
+	if(v >= VectorCPIC){
+		v -= VectorCPIC;
+		io->cimr |= 1<<(v&0x1F);
+	}
+	else if(v >= VectorIRQ)
+		io->simask |= 1<<(31-IRQ(v&7));
+	else
+		io->simask |= 1<<(31-LEV(v));
+}
+
+void
+trapinit(void)
+{
+	int i;
+	IMM *io;
+
+	io = m->iomem;
+	io->sypcr &= ~(3<<2);	/* disable watchdog (821/823) */
+	io->simask = 0;	/* mask all */
+	io->siel = ~0;	/* edge sensitive, wake on all */
+	io->cicr = 0;	/* disable CPM interrupts */
+	io->cipr = ~0;	/* clear all interrupts */
+	io->cimr = 0;	/* mask all events */
+	io->cicr = (0xE1<<16)|(CPIClevel<<13)|(0x1F<<8);
+	io->cicr |= 1 << 7;	/* enable */
+	io->tbscrk = KEEP_ALIVE_KEY;
+	io->tbscr = 1;	/* TBE */
+	io->simask |= 1<<(31-LEV(CPIClevel));	/* CPM's level */
+	io->tbk = KEEP_ALIVE_KEY;
+	eieio();
+	putdec(~0);
+
+	/*
+	 * set all exceptions to trap
+	 */
+	for(i = 0x0; i < 0x3000; i += 0x100)
+		sethvec(i, exception);
+}
+
+void
+dumpregs(Ureg *ur)
+{
+	int i;
+	ulong *l;
+	l = &ur->cause;
+	for(i=0; i<sizeof regname/sizeof(char*); i+=2, l+=2)
+		print("%s\t%.8lux\t%s\t%.8lux\n", regname[i], l[0], regname[i+1], l[1]);
+}
+
+void
+trap(Ureg *ur)
+{
+	int c;
+
+	c = ur->cause >> 8;
+	switch(c){
+	default:
+		{extern int predawn; predawn = 1;}
+		if(c < 0 || c >= nelem(excname))
+			print("exception/interrupt #%x\n", c);
+		else
+			print("exception %s\n", excname[c]);
+		dumpregs(ur);
+		/* spllo(); */
+		print("^P to reset\n");
+		for(;;)
+			;
+
+	case 0x09:	/* decrementer */
+		clockintr(ur, 0);
+		return;
+
+	case 0x05:	/* external interrupt */
+		intr(ur);
+		break;
+	}
+}
+
+static void
+intr(Ureg *ur)
+{
+	int b, v;
+	Handler *h;
+	IMM *io;
+
+	io = m->iomem;
+	b = io->sivec>>2;
+	v = b>>1;
+	if(b & 1) {
+		if(v == CPIClevel){
+			io->civr = 1;
+			eieio();
+			v = VectorCPIC+(io->civr>>11);
+		}
+	}else
+		v += VectorIRQ;
+	h = halloc.ivec[v];
+	if(h == nil){
+		for(;;)
+			;
+		//print("unknown interrupt %d pc=0x%lux\n", v, ur->pc);
+		return;
+	}
+	if(h->edge)
+		io->sipend |= 1<<(31-b);
+	/*
+	 *  call the interrupt handlers
+	 */
+	do {
+		(*h->r)(ur, h->arg);
+		h = h->next;
+	} while(h != nil);
+	if(v >= VectorCPIC)
+		io->cisr |= 1<<(v-VectorCPIC);
+}
--- /dev/null
+++ b/os/boot/mpc/uartboot.c
@@ -1,0 +1,189 @@
+#include "boot.h"
+
+/*
+ * this doesn't yet use the crc
+ */
+
+typedef struct Uboot Uboot;
+struct Uboot {
+	Queue*	iq;
+	Block*	partial;
+	ulong	csum;
+	long	bno;
+	uchar	buf[64];
+	int	nleft;
+	int	ntimeout;
+};
+
+static	Uboot	uboot;
+ulong	crc32(void *buf, int n, ulong crc);
+
+static void
+uartbrecv(uchar *p, int n)
+{
+	Uboot *ub;
+	Block *b;
+
+	ub = &uboot;
+	if(n > 0 && ub->iq != nil){
+		b = iallocb(n);
+		memmove(b->wp, p, n);
+		b->wp += n;
+		qbwrite(ub->iq, b);
+	}
+}
+
+int
+uartinit(void)
+{
+	return 1<<0;
+}
+
+Partition*
+setuartpart(int, char *s)
+{
+	static Partition pp[1];
+
+	if(strcmp(s, "boot") != 0 && strcmp(s, "disk") != 0)
+		return 0;
+	pp[0].start = 0;
+	pp[0].end = 2*1024*1024;
+	strcpy(pp[0].name, "boot");
+	return pp;
+}
+
+long
+uartseek(int, long)
+{
+	/* start the boot */
+	if(uboot.iq == nil)
+		uboot.iq = qopen(64*1024, 0, 0, 0);
+	if(uboot.partial){
+		freeb(uboot.partial);
+		uboot.partial = 0;
+	}
+	print("uart: start transmission\n");
+	uartsetboot(uartbrecv);
+	uboot.csum = ~0;
+	uboot.bno = 0;
+	uboot.nleft = 0;
+	uboot.ntimeout = 0;
+	return 0;
+}
+
+static long
+uartreadn(void *buf, int nb)
+{
+	ulong start;
+	Uboot *ub;
+	int l;
+	Block *b;
+	uchar *p;
+
+	p = buf;
+	ub = &uboot;
+	start = m->ticks;
+	while(nb > 0){
+		b = ub->partial;
+		ub->partial = nil;
+		if(b == nil){
+			ub->ntimeout = 0;
+			while((b = qget(ub->iq)) == 0){
+				if(TK2MS(m->ticks - start) >= 15*1000){
+					if(++ub->ntimeout >= 3){
+						print("uart: timeout\n");
+						return 0;
+					}
+					uartputs("n", 1);
+				}
+			}
+		}
+		l = BLEN(b);
+		if(l > nb)
+			l = nb;
+		memmove(p, b->rp, l);
+		b->rp += l;
+		if(b->rp >= b->wp)
+			freeb(b);
+		else
+			ub->partial = b;
+		nb -= l;
+		p += l;
+	}
+	return p-(uchar*)buf;
+}
+
+long
+uartread(int, void *buf, long n)
+{
+	uchar *p;
+	int l;
+	static uchar lbuf[64];
+
+	p = buf;
+	if((l = uboot.nleft) > 0){
+		if(l > n)
+			l = n;
+		uboot.nleft -= l;
+		memmove(p, uboot.buf, l);
+		p += l;
+		n -= l;
+	}
+	while(n > 0){
+		l = uartreadn(lbuf, sizeof(lbuf));
+		if(l < sizeof(lbuf))
+			return 0;
+		if(l > n){
+			uboot.nleft = l-n;
+			memmove(uboot.buf, lbuf+n, uboot.nleft);
+			l = n;
+		}
+		memmove(p, lbuf, l);
+		n -= l;
+		p += l;
+		uboot.bno++;
+		uartputs("y", 1);
+	}
+	return p-(uchar*)buf;
+}
+
+/*
+ * from Rob Warnock
+ */
+static	ulong	crc32tab[256];	/* initialised on first call to crc32 */
+
+enum {
+	CRC32POLY = 0x04c11db7     /* AUTODIN II, Ethernet, & FDDI */
+};
+
+/*
+ * Build auxiliary table for parallel byte-at-a-time CRC-32.
+ */
+static void
+initcrc32(void)
+{
+	int i, j;
+	ulong c;
+
+	for(i = 0; i < 256; i++) {
+		for(c = i << 24, j = 8; j > 0; j--)
+			if(c & (1<<31))
+				c = (c<<1) ^ CRC32POLY;
+			else
+				c <<= 1;
+		crc32tab[i] = c;
+	}
+}
+
+ulong
+crc32(void *buf, int n, ulong crc)
+{
+	uchar *p;
+
+	if(crc32tab[1] == 0)
+		initcrc32();
+	crc = ~crc;
+	for(p = buf; --n >= 0;)
+		crc = (crc << 8) ^ crc32tab[(crc >> 24) ^ *p++];
+	return ~crc;
+}
--- /dev/null
+++ b/os/boot/mpc/ureg.h
@@ -1,0 +1,43 @@
+struct Ureg
+{
+	ulong	cause;
+	union { ulong	srr1; ulong status;};
+	ulong	pc;	/* SRR0 */
+	ulong	pad;
+	ulong	lr;
+	ulong	cr;
+	ulong	xer;
+	ulong	ctr;
+	ulong	r0;
+	union{ ulong r1;	ulong	sp;	ulong	usp; };
+	ulong	r2;
+	ulong	r3;
+	ulong	r4;
+	ulong	r5;
+	ulong	r6;
+	ulong	r7;
+	ulong	r8;
+	ulong	r9;
+	ulong	r10;
+	ulong	r11;
+	ulong	r12;
+	ulong	r13;
+	ulong	r14;
+	ulong	r15;
+	ulong	r16;
+	ulong	r17;
+	ulong	r18;
+	ulong	r19;
+	ulong	r20;
+	ulong	r21;
+	ulong	r22;
+	ulong	r23;
+	ulong	r24;
+	ulong	r25;
+	ulong	r26;
+	ulong	r27;
+	ulong	r28;
+	ulong	r29;
+	ulong	r30;
+	ulong	r31;
+};
--- /dev/null
+++ b/os/boot/mpc/zqs.c
@@ -1,0 +1,234 @@
+#include "boot.h"
+#include "squeeze.h"
+
+/*
+ * for details of `unsqueeze' see:
+ *
+ * %A Mark Taunton
+ * %T Compressed Executables: An Exercise in Thinking Small
+ * %P 385-404
+ * %I USENIX
+ * %B USENIX Conference Proceedings
+ * %D Summer 1991
+ * %C Nashville, TN
+ *
+ * several of the unimplemented improvements described in the paper
+ * have been implemented here
+ *
+ * there is a further transformation on the powerpc (QFLAG!=0) to shuffle bits
+ * in certain instructions so as to push the fixed bits to the top of the word.
+ */
+
+#define	EXECHDRLEN	(8*4)
+
+typedef struct Squeeze Squeeze;
+struct Squeeze {
+	int	n;
+	ulong	tab[7*256];
+};
+
+#define	GET4(p)	(((((((p)[0]<<8)|(p)[1])<<8)|(p)[2])<<8)|(p)[3])
+
+/*
+ * for speed of unsqueezing from Flash, certain checks are
+ * not done inside the loop (as they would be in the unsqueeze program zqs),
+ * but instead the checksum is expected to catch corrupted files.
+ * in fact the Squeeze array bounds can't be exceeded in practice
+ * because the tables are always full for a squeezed kernel.
+ */
+enum {
+	QFLAG = 1,	/* invert powerpc-specific code transformation */
+	CHECK = 0,	/* check precise bounds in Squeeze array (otherwise checksum detects error) */
+};
+
+static	ulong	chksum;
+static	int	rdtab(Block*, Squeeze*, int);
+static	ulong*	unsqueeze(ulong*, uchar*, uchar*, Squeeze*, Squeeze*, ulong);
+static	uchar*	unsqzseg(uchar*, Block*, long, long, char*);
+static	Alarm*	unsqzal;
+
+int
+issqueezed(uchar *b)
+{
+	return GET4(b) == SQMAGIC? GET4(b+SQHDRLEN): 0;
+}
+
+static void
+unsqzdot(Alarm*)
+{
+	unsqzal = alarm(500, unsqzdot, nil);
+	print(".");
+}
+
+long
+unsqueezef(Block *b, ulong *entryp)
+{
+	uchar *loada, *wp;
+	ulong toptxt, topdat, oldsum;
+	long asis, nst, nsd;
+	Sqhdr *sqh;
+	Exec *ex;
+
+	if(BLEN(b) < SQHDRLEN+EXECHDRLEN)
+		return -1;
+	sqh = (Sqhdr*)b->rp;
+	if(GET4(sqh->magic) != SQMAGIC)
+		return -1;
+	chksum = 0;
+	toptxt = GET4(sqh->toptxt);
+	topdat = GET4(sqh->topdat);
+	oldsum = GET4(sqh->sum);
+	asis = GET4(sqh->asis);
+	nst = GET4(sqh->text);
+	nsd = GET4(sqh->data);
+	b->rp += SQHDRLEN;
+	ex = (Exec*)b->rp;
+	if(GET4(ex->magic) != Q_MAGIC){
+		print("zqs: not powerPC executable\n");
+		return -1;
+	}
+	*entryp = GET4(ex->entry);
+	b->rp += EXECHDRLEN;
+	loada = KADDR(PADDR(*entryp));
+	wp = unsqzseg(loada, b, nst, toptxt, "text");
+	if(wp == nil){
+		print("zqs: format error\n");
+		return -1;
+	}
+	if(nsd){
+		wp = (uchar*)PGROUND((ulong)wp);
+		wp = unsqzseg(wp, b, nsd, topdat, "data");
+		if(wp == nil){
+			print("zqs: format error\n");
+			return -1;
+		}
+	}
+	if(asis){
+		memmove(wp, b->rp, asis);
+		wp += asis;
+		b->rp += asis;
+	}
+	if(chksum != oldsum){
+		print("\nsqueezed kernel: checksum error: %8.8lux need %8.8lux\n", chksum, oldsum);
+		return -1;
+	}
+	return wp-loada;
+}
+
+static uchar *
+unsqzseg(uchar *wp, Block *b, long ns, long top, char *what)
+{
+	static Squeeze sq3, sq4;
+
+	print("unpack %s %8.8lux %lud:", what, wp, ns);
+	if(ns == 0)
+		return wp;
+	if(rdtab(b, &sq3, 0) < 0)
+		return nil;
+	if(rdtab(b, &sq4, 8) < 0)
+		return nil;
+	if(BLEN(b) < ns){
+		print(" **size error\n");
+		return nil;
+	}
+	unsqzal = alarm(500, unsqzdot, nil);
+	wp = (uchar*)unsqueeze((ulong*)wp, b->rp, b->rp+ns, &sq3, &sq4, top);
+	cancel(unsqzal);
+	unsqzal = nil;
+	print("\n");
+	if(wp == nil){
+		print("zqs: corrupt squeezed data stream\n");
+		return nil;
+	}
+	b->rp += ns;
+	return wp;
+}
+
+static ulong*
+unsqueeze(ulong *wp, uchar *rp, uchar *ep, Squeeze *sq3, Squeeze *sq4, ulong top)
+{
+	ulong nx, csum;
+	int code, n;
+
+	if(QFLAG){
+		QREMAP(top);	/* adjust top just once, outside the loop */
+	}
+	csum = chksum;
+	while(rp < ep){
+		/* no function calls within this loop for speed */
+		code = *rp;
+		rp++;
+		n = 0;
+		nx = code>>4;
+		do{
+			if(nx == 0){
+				nx = top;
+			}else{
+				if(nx==1){
+					nx = (((((rp[3]<<8)|rp[2])<<8)|rp[1])<<8)|rp[0];
+					rp += 4;
+				}else if(nx <= 8){	/* 2 to 8 */
+					nx = ((nx-2)<<8) | rp[0];
+					if(CHECK && nx >= sq4->n)
+						return nil;	/* corrupted file */
+					nx = sq4->tab[nx] | rp[1];
+					rp += 2;
+				}else{	/* 9 to 15 */
+					nx = ((nx-9)<<8) | rp[0];
+					if(CHECK && nx >= sq3->n)
+						return nil;	/* corrupted file */
+					nx = sq3->tab[nx];
+					rp++;
+				}
+				if(rp > ep)
+					return nil;	/* corrupted file */
+				if(QFLAG){
+					QREMAP(nx);
+				}
+			}
+			*wp = nx;
+			wp++;
+			csum += nx;
+			nx = code & 0xF;
+		}while(++n == 1);
+	}
+	chksum = csum;
+	return wp;
+}
+
+static int
+rdtab(Block *b, Squeeze *sq, int shift)
+{
+	uchar *p, *ep;
+	ulong v, w;
+	int i;
+
+	if(BLEN(b) < 2)
+		return -1;
+	i = (b->rp[0]<<8) | b->rp[1];
+	if(1)
+		print(" T%d", i);
+	b->rp += 2;
+	if((i -= 2) > 0){
+		if(BLEN(b) < i)
+			return -1;
+	}
+	sq->n = 0;
+	p = b->rp;
+	ep = b->rp+i;
+	b->rp += i;
+	v = 0;
+	while(p < ep){
+		w = 0;
+		do{
+			if(p >= ep)
+				return -1;
+			w = (w<<7) | (*p & 0x7F);
+		}while(*p++ & 0x80);
+		v += w;
+		if(0)
+			print("%d %8.8lux %8.8lux\n", sq->n, v, w);
+		sq->tab[sq->n++] = v<<shift;
+	}
+	return 0;
+}
--- /dev/null
+++ b/os/boot/pc/8250.c
@@ -1,0 +1,308 @@
+#include "u.h"
+#include "lib.h"
+#include "mem.h"
+#include "dat.h"
+#include "fns.h"
+#include "io.h"
+
+/*
+ *  INS8250 uart
+ */
+enum
+{
+	/*
+	 *  register numbers
+	 */
+	Data=	0,		/* xmit/rcv buffer */
+	Iena=	1,		/* interrupt enable */
+	 Ircv=	(1<<0),		/*  for char rcv'd */
+	 Ixmt=	(1<<1),		/*  for xmit buffer empty */
+	 Irstat=(1<<2),		/*  for change in rcv'er status */
+	 Imstat=(1<<3),		/*  for change in modem status */
+	Istat=	2,		/* interrupt flag (read) */
+	Tctl=	2,		/* test control (write) */
+	Format=	3,		/* byte format */
+	 Bits8=	(3<<0),		/*  8 bits/byte */
+	 Stop2=	(1<<2),		/*  2 stop bits */
+	 Pena=	(1<<3),		/*  generate parity */
+	 Peven=	(1<<4),		/*  even parity */
+	 Pforce=(1<<5),		/*  force parity */
+	 Break=	(1<<6),		/*  generate a break */
+	 Dra=	(1<<7),		/*  address the divisor */
+	Mctl=	4,		/* modem control */
+	 Dtr=	(1<<0),		/*  data terminal ready */
+	 Rts=	(1<<1),		/*  request to send */
+	 Ri=	(1<<2),		/*  ring */
+	 Inton=	(1<<3),		/*  turn on interrupts */
+	 Loop=	(1<<4),		/*  loop back */
+	Lstat=	5,		/* line status */
+	 Inready=(1<<0),	/*  receive buffer full */
+	 Oerror=(1<<1),		/*  receiver overrun */
+	 Perror=(1<<2),		/*  receiver parity error */
+	 Ferror=(1<<3),		/*  rcv framing error */
+	 Outready=(1<<5),	/*  output buffer empty */
+	Mstat=	6,		/* modem status */
+	 Ctsc=	(1<<0),		/*  clear to send changed */
+	 Dsrc=	(1<<1),		/*  data set ready changed */
+	 Rire=	(1<<2),		/*  rising edge of ring indicator */
+	 Dcdc=	(1<<3),		/*  data carrier detect changed */
+	 Cts=	(1<<4),		/*  complement of clear to send line */
+	 Dsr=	(1<<5),		/*  complement of data set ready line */
+	 Ring=	(1<<6),		/*  complement of ring indicator line */
+	 Dcd=	(1<<7),		/*  complement of data carrier detect line */
+	Scratch=7,		/* scratchpad */
+	Dlsb=	0,		/* divisor lsb */
+	Dmsb=	1,		/* divisor msb */
+
+	Serial=	0,
+	Modem=	1,
+};
+
+typedef struct Uart	Uart;
+struct Uart
+{
+	int	port;
+	uchar	sticky[8];	/* sticky write register values */
+	uchar	txbusy;
+
+	void	(*rx)(int);	/* routine to take a received character */
+	int	(*tx)(void);	/* routine to get a character to transmit */
+
+	ulong	frame;
+	ulong	overrun;
+};
+
+static Uart com[2];
+static Uart* uart;
+
+#define UartFREQ 1843200
+
+#define uartwrreg(u,r,v)	outb((u)->port + r, (u)->sticky[r] | (v))
+#define uartrdreg(u,r)		inb((u)->port + r)
+
+/*
+ *  set the baud rate by calculating and setting the baudrate
+ *  generator constant.  This will work with fairly non-standard
+ *  baud rates.
+ */
+static void
+uartsetbaud(Uart *up, int rate)
+{
+	ulong brconst;
+
+	brconst = (UartFREQ+8*rate-1)/(16*rate);
+
+	uartwrreg(up, Format, Dra);
+	outb(up->port+Dmsb, (brconst>>8) & 0xff);
+	outb(up->port+Dlsb, brconst & 0xff);
+	uartwrreg(up, Format, 0);
+}
+
+/*
+ *  toggle DTR
+ */
+static void
+uartdtr(Uart *up, int n)
+{
+	if(n)
+		up->sticky[Mctl] |= Dtr;
+	else
+		up->sticky[Mctl] &= ~Dtr;
+	uartwrreg(up, Mctl, 0);
+}
+
+/*
+ *  toggle RTS
+ */
+static void
+uartrts(Uart *up, int n)
+{
+	if(n)
+		up->sticky[Mctl] |= Rts;
+	else
+		up->sticky[Mctl] &= ~Rts;
+	uartwrreg(up, Mctl, 0);
+}
+
+static void
+uartintr(Ureg*, void *arg)
+{
+	Uart *up;
+	int ch;
+	int s, l, loops;
+
+	up = arg;
+	for(loops = 0; loops < 1024; loops++){
+		s = uartrdreg(up, Istat);
+		switch(s & 0x3F){
+		case 6:	/* receiver line status */
+			l = uartrdreg(up, Lstat);
+			if(l & Ferror)
+				up->frame++;
+			if(l & Oerror)
+				up->overrun++;
+			break;
+	
+		case 4:	/* received data available */
+		case 12:
+			ch = inb(up->port+Data);
+			if(up->rx)
+				(*up->rx)(ch);
+			break;
+	
+		case 2:	/* transmitter empty */
+			ch = -1;
+			if(up->tx)
+				ch = (*up->tx)();
+			if(ch != -1)
+				outb(up->port+Data, ch);
+			else
+				up->txbusy = 0;
+			break;
+	
+		case 0:	/* modem status */
+			uartrdreg(up, Mstat);
+			break;
+	
+		default:
+			if(s&1)
+				return;
+			print("weird modem interrupt #%2.2ux\n", s);
+			break;
+		}
+	}
+	panic("uartintr: 0x%2.2ux\n", uartrdreg(up, Istat));
+}
+
+/*
+ *  turn on a port's interrupts.  set DTR and RTS
+ */
+static void
+uartenable(Uart *up)
+{
+	/*
+ 	 *  turn on interrupts
+	 */
+	up->sticky[Iena] = 0;
+	if(up->tx)
+		up->sticky[Iena] |= Ixmt;
+	if(up->rx)
+		up->sticky[Iena] |= Ircv|Irstat;
+	uartwrreg(up, Iena, 0);
+
+	/*
+	 *  turn on DTR and RTS
+	 */
+	uartdtr(up, 1);
+	uartrts(up, 1);
+}
+
+static void
+uartdisable(Uart* up)
+{
+	/*
+ 	 * Disable interrupts.
+	 */
+	up->sticky[Iena] = 0;
+	uartwrreg(up, Iena, 0);
+	uartdtr(up, 0);
+	uartrts(up, 0);
+}
+
+void
+uartspecial(int port, void (*rx)(int), int (*tx)(void), int baud)
+{
+	Uart *up;
+	int vector;
+
+	switch(port){
+	case 0:
+		port = 0x3F8;
+		vector = VectorUART0;
+		up = &com[0];
+		break;
+	case 1:
+		port = 0x2F8;
+		vector = VectorUART1;
+		up = &com[1];
+		break;
+	default:
+		return;
+	}
+
+	if(uart != nil && uart != up)
+		uartdisable(uart);
+	uart = up;
+
+	if(up->port == 0){
+		up->port = port;
+		setvec(vector, uartintr, up);
+	}
+
+	/*
+	 *  set rate to 9600 baud.
+	 *  8 bits/character.
+	 *  1 stop bit.
+	 *  interrupts enabled.
+	 */
+	uartsetbaud(up, 9600);
+	up->sticky[Format] = Bits8;
+	uartwrreg(up, Format, 0);
+	up->sticky[Mctl] |= Inton;
+	uartwrreg(up, Mctl, 0x0);
+
+	up->rx = rx;
+	up->tx = tx;
+	uartenable(up);
+	if(baud)
+		uartsetbaud(up, baud);
+}
+
+void
+uartputc(int c)
+{
+	int i;
+	Uart *up;
+
+	if((up = uart) == nil)
+		return;
+	for(i = 0; i < 100; i++){
+		if(uartrdreg(up, Lstat) & Outready)
+			break;
+		delay(1);
+	}
+	outb(up->port+Data, c);
+}
+
+void
+uartputs(IOQ *q, char *s, int n)
+{
+	Uart *up;
+	int c, x;
+
+	if((up = uart) == nil)
+		return;
+	while(n--){
+		if(*s == '\n')
+			q->putc(q, '\r');
+		q->putc(q, *s++);
+	}
+	x = splhi();
+	if(up->txbusy == 0 && (c = q->getc(q)) != -1){
+		uartputc(c & 0xFF);
+		up->txbusy = 1;
+	}
+	splx(x);
+}
+
+void
+uartdrain(void)
+{
+	Uart *up;
+	int timeo;
+
+	if((up = uart) == nil)
+		return;
+	for(timeo = 0; timeo < 10000 && up->txbusy; timeo++)
+		delay(1);
+}
--- /dev/null
+++ b/os/boot/pc/LICENCE
@@ -1,0 +1,237 @@
+Lucent Public License Version 1.02
+
+THE ACCOMPANYING PROGRAM IS PROVIDED UNDER THE TERMS OF THIS PUBLIC
+LICENSE ("AGREEMENT"). ANY USE, REPRODUCTION OR DISTRIBUTION OF THE
+PROGRAM CONSTITUTES RECIPIENT'S ACCEPTANCE OF THIS AGREEMENT.
+
+1. DEFINITIONS
+
+"Contribution" means:
+
+  a. in the case of Lucent Technologies Inc. ("LUCENT"), the Original
+     Program, and
+  b. in the case of each Contributor,
+
+     i. changes to the Program, and
+    ii. additions to the Program;
+
+    where such changes and/or additions to the Program were added to the
+    Program by such Contributor itself or anyone acting on such
+    Contributor's behalf, and the Contributor explicitly consents, in
+    accordance with Section 3C, to characterization of the changes and/or
+    additions as Contributions.
+
+"Contributor" means LUCENT and any other entity that has Contributed a
+Contribution to the Program.
+
+"Distributor" means a Recipient that distributes the Program,
+modifications to the Program, or any part thereof.
+
+"Licensed Patents" mean patent claims licensable by a Contributor
+which are necessarily infringed by the use or sale of its Contribution
+alone or when combined with the Program.
+
+"Original Program" means the original version of the software
+accompanying this Agreement as released by LUCENT, including source
+code, object code and documentation, if any.
+
+"Program" means the Original Program and Contributions or any part
+thereof
+
+"Recipient" means anyone who receives the Program under this
+Agreement, including all Contributors.
+
+2. GRANT OF RIGHTS
+
+ a. Subject to the terms of this Agreement, each Contributor hereby
+    grants Recipient a non-exclusive, worldwide, royalty-free copyright
+    license to reproduce, prepare derivative works of, publicly display,
+    publicly perform, distribute and sublicense the Contribution of such
+    Contributor, if any, and such derivative works, in source code and
+    object code form.
+    
+ b. Subject to the terms of this Agreement, each Contributor hereby
+    grants Recipient a non-exclusive, worldwide, royalty-free patent
+    license under Licensed Patents to make, use, sell, offer to sell,
+    import and otherwise transfer the Contribution of such Contributor, if
+    any, in source code and object code form. The patent license granted
+    by a Contributor shall also apply to the combination of the
+    Contribution of that Contributor and the Program if, at the time the
+    Contribution is added by the Contributor, such addition of the
+    Contribution causes such combination to be covered by the Licensed
+    Patents. The patent license granted by a Contributor shall not apply
+    to (i) any other combinations which include the Contribution, nor to
+    (ii) Contributions of other Contributors. No hardware per se is
+    licensed hereunder.
+    
+ c. Recipient understands that although each Contributor grants the
+    licenses to its Contributions set forth herein, no assurances are
+    provided by any Contributor that the Program does not infringe the
+    patent or other intellectual property rights of any other entity. Each
+    Contributor disclaims any liability to Recipient for claims brought by
+    any other entity based on infringement of intellectual property rights
+    or otherwise. As a condition to exercising the rights and licenses
+    granted hereunder, each Recipient hereby assumes sole responsibility
+    to secure any other intellectual property rights needed, if any. For
+    example, if a third party patent license is required to allow
+    Recipient to distribute the Program, it is Recipient's responsibility
+    to acquire that license before distributing the Program.
+
+ d. Each Contributor represents that to its knowledge it has sufficient
+    copyright rights in its Contribution, if any, to grant the copyright
+    license set forth in this Agreement.
+
+3. REQUIREMENTS
+
+A. Distributor may choose to distribute the Program in any form under
+this Agreement or under its own license agreement, provided that:
+
+ a. it complies with the terms and conditions of this Agreement;
+
+ b. if the Program is distributed in source code or other tangible
+    form, a copy of this Agreement or Distributor's own license agreement
+    is included with each copy of the Program; and
+
+ c. if distributed under Distributor's own license agreement, such
+    license agreement:
+
+      i. effectively disclaims on behalf of all Contributors all warranties
+         and conditions, express and implied, including warranties or
+         conditions of title and non-infringement, and implied warranties or
+         conditions of merchantability and fitness for a particular purpose;
+     ii. effectively excludes on behalf of all Contributors all liability
+         for damages, including direct, indirect, special, incidental and
+         consequential damages, such as lost profits; and
+    iii. states that any provisions which differ from this Agreement are
+         offered by that Contributor alone and not by any other party.
+
+B. Each Distributor must include the following in a conspicuous
+   location in the Program:
+
+   Copyright (C) 2003, Lucent Technologies Inc. and others. All Rights
+   Reserved.
+
+C. In addition, each Contributor must identify itself as the
+originator of its Contribution in a manner that reasonably allows
+subsequent Recipients to identify the originator of the Contribution.
+Also, each Contributor must agree that the additions and/or changes
+are intended to be a Contribution. Once a Contribution is contributed,
+it may not thereafter be revoked.
+
+4. COMMERCIAL DISTRIBUTION
+
+Commercial distributors of software may accept certain
+responsibilities with respect to end users, business partners and the
+like. While this license is intended to facilitate the commercial use
+of the Program, the Distributor who includes the Program in a
+commercial product offering should do so in a manner which does not
+create potential liability for Contributors. Therefore, if a
+Distributor includes the Program in a commercial product offering,
+such Distributor ("Commercial Distributor") hereby agrees to defend
+and indemnify every Contributor ("Indemnified Contributor") against
+any losses, damages and costs (collectively"Losses") arising from
+claims, lawsuits and other legal actions brought by a third party
+against the Indemnified Contributor to the extent caused by the acts
+or omissions of such Commercial Distributor in connection with its
+distribution of the Program in a commercial product offering. The
+obligations in this section do not apply to any claims or Losses
+relating to any actual or alleged intellectual property infringement.
+In order to qualify, an Indemnified Contributor must: a) promptly
+notify the Commercial Distributor in writing of such claim, and b)
+allow the Commercial Distributor to control, and cooperate with the
+Commercial Distributor in, the defense and any related settlement
+negotiations. The Indemnified Contributor may participate in any such
+claim at its own expense.
+
+For example, a Distributor might include the Program in a commercial
+product offering, Product X. That Distributor is then a Commercial
+Distributor. If that Commercial Distributor then makes performance
+claims, or offers warranties related to Product X, those performance
+claims and warranties are such Commercial Distributor's responsibility
+alone. Under this section, the Commercial Distributor would have to
+defend claims against the Contributors related to those performance
+claims and warranties, and if a court requires any Contributor to pay
+any damages as a result, the Commercial Distributor must pay those
+damages.
+
+5. NO WARRANTY
+
+EXCEPT AS EXPRESSLY SET FORTH IN THIS AGREEMENT, THE PROGRAM IS
+PROVIDED ON AN"AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+KIND, EITHER EXPRESS OR IMPLIED INCLUDING, WITHOUT LIMITATION, ANY
+WARRANTIES OR CONDITIONS OF TITLE, NON-INFRINGEMENT, MERCHANTABILITY
+OR FITNESS FOR A PARTICULAR PURPOSE. Each Recipient is solely
+responsible for determining the appropriateness of using and
+distributing the Program and assumes all risks associated with its
+exercise of rights under this Agreement, including but not limited to
+the risks and costs of program errors, compliance with applicable
+laws, damage to or loss of data, programs or equipment, and
+unavailability or interruption of operations.
+
+6. DISCLAIMER OF LIABILITY
+
+EXCEPT AS EXPRESSLY SET FORTH IN THIS AGREEMENT, NEITHER RECIPIENT NOR
+ANY CONTRIBUTORS SHALL HAVE ANY LIABILITY FOR ANY DIRECT, INDIRECT,
+INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING
+WITHOUT LIMITATION LOST PROFITS), HOWEVER CAUSED AND ON ANY THEORY OF
+LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OR
+DISTRIBUTION OF THE PROGRAM OR THE EXERCISE OF ANY RIGHTS GRANTED
+HEREUNDER, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGES.
+
+7. EXPORT CONTROL
+
+Recipient agrees that Recipient alone is responsible for compliance
+with the United States export administration regulations (and the
+export control laws and regulation of any other countries).
+
+8. GENERAL
+
+If any provision of this Agreement is invalid or unenforceable under
+applicable law, it shall not affect the validity or enforceability of
+the remainder of the terms of this Agreement, and without further
+action by the parties hereto, such provision shall be reformed to the
+minimum extent necessary to make such provision valid and enforceable.
+
+If Recipient institutes patent litigation against a Contributor with
+respect to a patent applicable to software (including a cross-claim or
+counterclaim in a lawsuit), then any patent licenses granted by that
+Contributor to such Recipient under this Agreement shall terminate as
+of the date such litigation is filed. In addition, if Recipient
+institutes patent litigation against any entity (including a
+cross-claim or counterclaim in a lawsuit) alleging that the Program
+itself (excluding combinations of the Program with other software or
+hardware) infringes such Recipient's patent(s), then such Recipient's
+rights granted under Section 2(b) shall terminate as of the date such
+litigation is filed.
+
+All Recipient's rights under this Agreement shall terminate if it
+fails to comply with any of the material terms or conditions of this
+Agreement and does not cure such failure in a reasonable period of
+time after becoming aware of such noncompliance. If all Recipient's
+rights under this Agreement terminate, Recipient agrees to cease use
+and distribution of the Program as soon as reasonably practicable.
+However, Recipient's obligations under this Agreement and any licenses
+granted by Recipient relating to the Program shall continue and
+survive.
+
+LUCENT may publish new versions (including revisions) of this
+Agreement from time to time. Each new version of the Agreement will be
+given a distinguishing version number. The Program (including
+Contributions) may always be distributed subject to the version of the
+Agreement under which it was received. In addition, after a new
+version of the Agreement is published, Contributor may elect to
+distribute the Program (including its Contributions) under the new
+version. No one other than LUCENT has the right to modify this
+Agreement. Except as expressly stated in Sections 2(a) and 2(b) above,
+Recipient receives no rights or licenses to the intellectual property
+of any Contributor under this Agreement, whether expressly, by
+implication, estoppel or otherwise. All rights in the Program not
+expressly granted under this Agreement are reserved.
+
+This Agreement is governed by the laws of the State of New York and
+the intellectual property laws of the United States of America. No
+party to this Agreement will bring a legal action under this Agreement
+more than one year after the cause of action arose. Each party waives
+its rights to a jury trial in any resulting litigation.
+
--- /dev/null
+++ b/os/boot/pc/NOTICE
@@ -1,0 +1,4 @@
+Copyright © 1995-2007 Lucent Technologies Inc. and others.  All rights reserved.
+Revisions for use with Inferno © 2003-2007 Vita Nuova Holdings Limited.
+
+This software is provided under the terms of the Lucent Public License, Version 1.02.
--- /dev/null
+++ b/os/boot/pc/ahci.h
@@ -1,0 +1,275 @@
+/*
+ * advanced host controller interface (sata)
+ * © 2007  coraid, inc
+ */
+
+/* ata errors */
+enum {
+	Emed	= 1<<0,		/* media error */
+	Enm	= 1<<1,		/* no media */
+	Eabrt	= 1<<2,		/* abort */
+	Emcr	= 1<<3,		/* media change request */
+	Eidnf	= 1<<4,		/* no user-accessible address */
+	Emc	= 1<<5,		/* media change */
+	Eunc	= 1<<6,		/* data error */
+	Ewp	= 1<<6,		/* write protect */
+	Eicrc	= 1<<7,		/* interface crc error */
+
+	Efatal	= Eidnf|Eicrc,	/* must sw reset */
+};
+
+/* ata status */
+enum {
+	ASerr	= 1<<0,		/* error */
+	ASdrq	= 1<<3,		/* request */
+	ASdf	= 1<<5,		/* fault */
+	ASdrdy	= 1<<6,		/* ready */
+	ASbsy	= 1<<7,		/* busy */
+
+	ASobs	= 1<<1|1<<2|1<<4,
+};
+
+/* pci configuration */
+enum {
+	Abar	= 5,
+};
+
+/*
+ * ahci memory configuration
+ *
+ * 0000-0023	generic host control
+ * 0024-009f	reserved
+ * 00a0-00ff	vendor specific.
+ * 0100-017f	port 0
+ * ...
+ * 1080-1100	port 31
+ */
+
+/* cap bits: supported features */
+enum {
+	Hs64a	= 1<<31,	/* 64-bit addressing */
+	Hsncq	= 1<<30,	/* ncq */
+	Hssntf	= 1<<29,	/* snotification reg. */
+	Hsmps	= 1<<28,	/* mech pres switch */
+	Hsss	= 1<<27,	/* staggered spinup */
+	Hsalp	= 1<<26,	/* aggressive link pm */
+	Hsal	= 1<<25,	/* activity led */
+	Hsclo	= 1<<24,	/* command-list override */
+	Hiss	= 1<<20,	/* for interface speed */
+//	Hsnzo	= 1<<19,
+	Hsam	= 1<<18,	/* ahci-mode only */
+	Hspm	= 1<<17,	/* port multiplier */
+//	Hfbss	= 1<<16,
+	Hpmb	= 1<<15,	/* multiple-block pio */
+	Hssc	= 1<<14,	/* slumber state */
+	Hpsc	= 1<<13,	/* partial-slumber state */
+	Hncs	= 1<<8,		/* n command slots */
+	Hcccs	= 1<<7,		/* coal */
+	Hems	= 1<<6,		/* enclosure mgmt. */
+	Hsxs	= 1<<5,		/* external sata */
+	Hnp	= 1<<0,		/* n ports */
+};
+
+/* ghc bits */
+enum {
+	Hae	= 1<<31,	/* enable ahci */
+	Hie	= 1<<1,		/* " interrupts */
+	Hhr	= 1<<0,		/* hba reset */
+};
+
+typedef struct {
+	ulong	cap;
+	ulong	ghc;
+	ulong	isr;
+	ulong	pi;		/* ports implemented */
+	ulong	ver;
+	ulong	ccc;		/* coaleasing control */
+	ulong	cccports;
+	ulong	emloc;
+	ulong	emctl;
+} Ahba;
+
+enum {
+	Acpds	= 1<<31,	/* cold port detect status */
+	Atfes	= 1<<30,	/* task file error status */
+	Ahbfs	= 1<<29,	/* hba fatal */
+	Ahbds	= 1<<28,	/* hba error (parity error) */
+	Aifs	= 1<<27,	/* interface fatal  §6.1.2 */
+	Ainfs	= 1<<26,	/* interface error (recovered) */
+	Aofs	= 1<<24,	/* too many bytes from disk */
+	Aipms	= 1<<23,	/* incorrect prt mul status */
+	Aprcs	= 1<<22,	/* PhyRdy change status Pxserr.diag.n */
+	Adpms	= 1<<7,		/* mechanical presence status */
+	Apcs 	= 1<<6,		/* port connect  diag.x */
+	Adps 	= 1<<5,		/* descriptor processed */
+	Aufs 	= 1<<4,		/* unknown fis diag.f */
+	Asdbs	= 1<<3,		/* set device bits fis received w/ i bit set */
+	Adss	= 1<<2,		/* dma setup */
+	Apio	= 1<<1,		/* pio setup fis */
+	Adhrs	= 1<<0,		/* device to host register fis */
+
+	IEM	= Acpds|Atfes|Ahbds|Ahbfs|Ahbds|Aifs|Ainfs|Aprcs|Apcs|Adps|
+			Aufs|Asdbs|Adss|Adhrs,
+	Ifatal	= Atfes|Ahbfs|Ahbds|Aifs,
+};
+
+/* serror bits */
+enum {
+	SerrX	= 1<<26,	/* exchanged */
+	SerrF	= 1<<25,	/* unknown fis */
+	SerrT	= 1<<24,	/* transition error */
+	SerrS	= 1<<23,	/* link sequence */
+	SerrH	= 1<<22,	/* handshake */
+	SerrC	= 1<<21,	/* crc */
+	SerrD	= 1<<20,	/* not used by ahci */
+	SerrB	= 1<<19,	/* 10-tp-8 decode */
+	SerrW	= 1<<18,	/* comm wake */
+	SerrI	= 1<<17,	/* phy internal */
+	SerrN	= 1<<16,	/* phyrdy change */
+
+	ErrE	= 1<<11,	/* internal */
+	ErrP	= 1<<10,	/* ata protocol violation */
+	ErrC	= 1<<9,		/* communication */
+	ErrT	= 1<<8,		/* transient */
+	ErrM	= 1<<1,		/* recoverd comm */
+	ErrI	= 1<<0,		/* recovered data integrety */
+
+	ErrAll	= ErrE|ErrP|ErrC|ErrT|ErrM|ErrI,
+	SerrAll	= SerrX|SerrF|SerrT|SerrS|SerrH|SerrC|SerrD|SerrB|SerrW|
+			SerrI|SerrN|ErrAll,
+	SerrBad	= 0x7f<<19,
+};
+
+/* cmd register bits */
+enum {
+	Aicc	= 1<<28,	/* interface communcations control. 4 bits */
+	Aasp	= 1<<27,	/* agressive slumber & partial sleep */
+	Aalpe 	= 1<<26,	/* agressive link pm enable */
+	Adlae	= 1<<25,	/* drive led on atapi */
+	Aatapi	= 1<<24,	/* device is atapi */
+	Aesp	= 1<<21,	/* external sata port */
+	Acpd	= 1<<20,	/* cold presence detect */
+	Ampsp	= 1<<19,	/* mechanical pres. */
+	Ahpcp	= 1<<18,	/* hot plug capable */
+	Apma	= 1<<17,	/* pm attached */
+	Acps	= 1<<16,	/* cold presence state */
+	Acr	= 1<<15,	/* cmdlist running */
+	Afr	= 1<<14,	/* fis running */
+	Ampss	= 1<<13,	/* mechanical presence switch state */
+	Accs	= 1<<8,		/* current command slot 12:08 */
+	Afre	= 1<<4,		/* fis enable receive */
+	Aclo	= 1<<3,		/* command list override */
+	Apod	= 1<<2,		/* power on dev (requires cold-pres. detect) */
+	Asud	= 1<<1,		/* spin-up device;  requires ss capability */
+	Ast	= 1<<0,		/* start */
+
+	Arun	= Ast|Acr|Afre|Afr,
+};
+
+/* ctl register bits */
+enum {
+	Aipm	= 1<<8,		/* interface power mgmt. 3=off */
+	Aspd	= 1<<4,
+	Adet	= 1<<0,		/* device detection */
+};
+
+#define	sstatus	scr0
+#define	sctl	scr2
+#define	serror	scr1
+#define	sactive	scr3
+
+typedef struct {
+	ulong	list;		/* PxCLB must be 1kb aligned. */
+	ulong	listhi;
+	ulong	fis;		/* 256-byte aligned */
+	ulong	fishi;
+	ulong	isr;
+	ulong	ie;		/* interrupt enable */
+	ulong	cmd;
+	ulong	res1;
+	ulong	task;
+	ulong	sig;
+	ulong	scr0;
+	ulong	scr2;
+	ulong	scr1;
+	ulong	scr3;
+	ulong	ci;		/* command issue */
+	ulong	ntf;
+	uchar	res2[8];
+	ulong	vendor;
+} Aport;
+
+/* in host's memory; not memory mapped */
+typedef struct {
+	uchar	*base;
+	uchar	*d;
+	uchar	*p;
+	uchar	*r;
+	uchar	*u;
+	ulong	*devicebits;
+} Afis;
+
+enum {
+	Lprdtl	= 1<<16,	/* physical region descriptor table len */
+	Lpmp	= 1<<12,	/* port multiplier port */
+	Lclear	= 1<<10,	/* clear busy on R_OK */
+	Lbist	= 1<<9,
+	Lreset	= 1<<8,
+	Lpref	= 1<<7,		/* prefetchable */
+	Lwrite	= 1<<6,
+	Latapi	= 1<<5,
+	Lcfl	= 1<<0,		/* command fis length in double words */
+};
+
+/* in hosts memory; memory mapped */
+typedef struct {
+	ulong	flags;
+	ulong	len;
+	ulong	ctab;
+	ulong	ctabhi;
+	uchar	reserved[16];
+} Alist;
+
+typedef struct {
+	ulong	dba;
+	ulong	dbahi;
+	ulong	pad;
+	ulong	count;
+} Aprdt;
+
+typedef struct {
+	uchar	cfis[0x40];
+	uchar	atapi[0x10];
+	uchar	pad[0x30];
+	Aprdt	prdt;
+} Actab;
+
+enum {
+	Ferror	= 1,
+	Fdone	= 2,
+};
+
+enum {
+	Dllba 	= 1,
+	Dsmart	= 1<<1,
+	Dpower	= 1<<2,
+	Dnop	= 1<<3,
+	Datapi	= 1<<4,
+	Datapi16= 1<<5,
+};
+
+typedef struct {
+//	QLock;
+//	Rendez;
+	uchar	flag;
+	uchar	feat;
+	uchar	smart;
+	Afis	fis;
+	Alist	*list;
+	Actab	*ctab;
+} Aportm;
+
+typedef struct {
+	Aport	*p;
+	Aportm	*m;
+} Aportc;
--- /dev/null
+++ b/os/boot/pc/alarm.c
@@ -1,0 +1,123 @@
+#include	"u.h"
+#include	"lib.h"
+#include	"mem.h"
+#include	"dat.h"
+#include	"fns.h"
+#include	"io.h"
+#define	MAXALARM	10
+
+Alarm	alarmtab[MAXALARM];
+
+/*
+ * Insert new into list after where
+ */
+void
+insert(List **head, List *where, List *new)
+{
+	if(where == 0){
+		new->next = *head;
+		*head = new;
+	}else{
+		new->next = where->next;
+		where->next = new;
+	}
+		
+}
+
+/*
+ * Delete old from list.  where->next is known to be old.
+ */
+void
+delete(List **head, List *where, List *old)
+{
+	if(where == 0){
+		*head = old->next;
+		return;
+	}
+	where->next = old->next;
+}
+
+Alarm*
+newalarm(void)
+{
+	int i;
+	Alarm *a;
+
+	for(i=0,a=alarmtab; i < nelem(alarmtab); i++,a++)
+		if(a->busy==0 && a->f==0){
+			a->f = 0;
+			a->arg = 0;
+			a->busy = 1;
+			return a;
+		}
+	panic("newalarm");
+	return 0;	/* not reached */
+}
+
+Alarm*
+alarm(int ms, void (*f)(Alarm*), void *arg)
+{
+	Alarm *a, *w, *pw;
+	ulong s;
+
+	if(ms < 0)
+		ms = 0;
+	s = splhi();
+	a = newalarm();
+	a->dt = MS2TK(ms);
+	a->f = f;
+	a->arg = arg;
+	pw = 0;
+	for(w=m->alarm; w; pw=w, w=w->next){
+		if(w->dt <= a->dt){
+			a->dt -= w->dt;
+			continue;
+		}
+		w->dt -= a->dt;
+		break;
+	}
+	insert(&m->alarm, pw, a);
+	splx(s);
+	return a;
+}
+
+void
+cancel(Alarm *a)
+{
+	a->f = 0;
+}
+
+void
+alarminit(void)
+{
+}
+
+#define NA 10		/* alarms per clock tick */
+void
+checkalarms(void)
+{
+	int i, n, s;
+	Alarm *a;
+	void (*f)(Alarm*);
+	Alarm *alist[NA];
+
+	s = splhi();
+	a = m->alarm;
+	if(a){
+		for(n=0; a && a->dt<=0 && n<NA; n++){
+			alist[n] = a;
+			delete(&m->alarm, 0, a);
+			a = m->alarm;
+		}
+		if(a)
+			a->dt--;
+
+		for(i = 0; i < n; i++){
+			f = alist[i]->f;	/* avoid race with cancel */
+			if(f)
+				(*f)(alist[i]);
+			alist[i]->busy = 0;
+		}
+	}
+	splx(s);
+}
--- /dev/null
+++ b/os/boot/pc/aoe.h
@@ -1,0 +1,76 @@
+/*
+ * ATA-over-Ethernet
+ */
+enum {
+	ACata,
+	ACconfig,
+};
+
+enum {
+	AQCread,
+	AQCtest,
+	AQCprefix,
+	AQCset,
+	AQCfset,
+};
+
+enum {
+	AEcmd	= 1,
+	AEarg,
+	AEdev,
+	AEcfg,
+	AEver,
+};
+
+enum {
+	Aoetype	= 0x88a2,
+	Aoever	= 1,
+
+	AFerr	= 1<<2,
+	AFrsp	= 1<<3,
+
+	AAFwrite= 1,
+	AAFext	= 1<<6,
+};
+
+enum {
+	Crd	= 0x20,
+	Crdext	= 0x24,
+	Cwr	= 0x30,
+	Cwrext	= 0x34,
+	Cid	= 0xec,
+};
+
+typedef struct {
+	uchar	dst[Eaddrlen];
+	uchar	src[Eaddrlen];
+	uchar	type[2];
+	uchar	verflag;
+	uchar	error;
+	uchar	major[2];
+	uchar	minor;
+	uchar	cmd;
+	uchar	tag[4];
+} Aoehdr;
+
+typedef struct {
+	Aoehdr;
+	uchar	aflag;
+	uchar	errfeat;
+	uchar	scnt;
+	uchar	cmdstat;
+	uchar	lba[6];
+	uchar	res[2];
+} Aoeata;
+
+typedef struct {
+	Aoehdr;
+	uchar	bufcnt[2];
+	uchar	fwver[2];
+	uchar	scnt;
+	uchar	verccmd;
+	uchar	cslen[2];
+} Aoeqc;
+
+extern char Echange[];
+extern char Enotup[];
--- /dev/null
+++ b/os/boot/pc/apm.c
@@ -1,0 +1,16 @@
+#include "u.h"
+#include "lib.h"
+#include "mem.h"
+#include "dat.h"
+#include "fns.h"
+#include "io.h"
+
+Apminfo apm;
+
+void
+apminit(void)
+{
+	if(getconf("apm0") && apm.haveinfo)
+		changeconf("apm0=ax=%x ebx=%x cx=%x dx=%x di=%x esi=%x\n",
+			apm.ax, apm.ebx, apm.cx, apm.dx, apm.di, apm.esi);
+}
--- /dev/null
+++ b/os/boot/pc/bcom.c
@@ -1,0 +1,463 @@
+/*
+ * ld - DOS boot loader of Plan 9
+ */
+#include "u.h"
+#include "lib.h"
+#include "mem.h"
+#include "dat.h"
+#include "fns.h"
+#include "io.h"
+
+#include "fs.h"
+
+Type types[] = {
+	{	Tfloppy,
+		Fini|Ffs,
+		floppyinit, floppyinitdev,
+		floppygetfspart, 0, floppyboot,
+	},
+	{	Tsd,
+		Fini|Ffs,
+		sdinit, sdinitdev,
+		sdgetfspart, sdaddconf, sdboot,
+	},
+	{	Tnil,
+		0,
+		0, 0,
+		0, 0, 0,
+	},
+};
+
+#include "sd.h"
+
+extern SDifc sdataifc;
+extern SDifc sdmylexifc;
+extern SDifc sd53c8xxifc;
+SDifc* sdifc[] = {
+	&sdataifc,
+//	&sdmylexifc,
+//	&sd53c8xxifc,
+	nil,
+};
+
+typedef struct Mode Mode;
+
+enum {
+	Maxdev		= 7,
+	Dany		= -1,
+	Nmedia		= 16,
+	Nini		= 10,
+};
+
+enum {					/* mode */
+	Mauto		= 0x00,
+	Mlocal		= 0x01,
+	Manual		= 0x02,
+	NMode		= 0x03,
+};
+
+typedef struct Medium Medium;
+struct Medium {
+	Type*	type;
+	int	flag;
+	int	dev;
+	char name[NAMELEN];
+	Fs*	inifs;
+
+	Medium*	next;
+};
+
+typedef struct Mode {
+	char*	name;
+	int	mode;
+} Mode;
+
+static Medium media[Nmedia];
+static Medium *curmedium = media;
+
+static Mode modes[NMode+1] = {
+	[Mauto]		{ "auto",   Mauto,  },
+	[Mlocal]	{ "local",  Mlocal, },
+	[Manual]	{ "manual", Manual, },
+};
+
+char *defaultpartition = "new";
+
+static Medium*
+parse(char *line, char **file)
+{
+	char *p;
+	Type *tp;
+	Medium *mp;
+
+	if(p = strchr(line, '!')) {
+		*p++ = 0;
+		*file = p;
+	} else
+		*file = "";
+
+	for(tp = types; tp->type != Tnil; tp++)
+		for(mp = tp->media; mp; mp = mp->next)
+			if(strcmp(mp->name, line) == 0)
+				return mp;
+	return nil;
+}
+
+static int
+boot(Medium *mp, char *file)
+{
+	static Boot b;
+
+	memset(&b, 0, sizeof b);
+	b.state = INIT9LOAD;
+
+//	sprint(BOOTLINE, "%s!%s", mp->name, file);
+	return (*mp->type->boot)(mp->dev, file, &b);
+}
+
+static Medium*
+allocm(Type *tp)
+{
+	Medium **l;
+
+	if(curmedium >= &media[Nmedia])
+		return 0;
+
+	for(l = &tp->media; *l; l = &(*l)->next)
+		;
+	*l = curmedium++;
+	return *l;
+}
+
+char *parts[] = { "dos", "9fat", "fs", 0 };
+
+Medium*
+probe(int type, int flag, int dev)
+{
+	Type *tp;
+	int i;
+	Medium *mp;
+
+	for(tp = types; tp->type != Tnil; tp++){
+		if(type != Tany && type != tp->type)
+			continue;
+
+		if(flag != Fnone){
+			for(mp = tp->media; mp; mp = mp->next){
+				if((flag & mp->flag) && (dev == Dany || dev == mp->dev))
+					return mp;
+			}
+		}
+
+		if((tp->flag & Fprobe) == 0){
+			tp->flag |= Fprobe;
+			tp->mask = (*tp->init)();
+		}
+
+		for(i = 0; tp->mask; i++){
+			if((tp->mask & (1<<i)) == 0)
+				continue;
+			tp->mask &= ~(1<<i);
+
+			if((mp = allocm(tp)) == 0)
+				continue;
+
+			mp->dev = i;
+			mp->flag = tp->flag;
+			mp->type = tp;
+			(*tp->initdev)(i, mp->name);
+
+			if((flag & mp->flag) && (dev == Dany || dev == i))
+				return mp;
+		}
+	}
+
+	return 0;
+}
+
+extern int loopconst;
+void
+main(void)
+{
+	Medium *mp;
+	int flag;
+	char def[2*NAMELEN], line[80], *p, *file;
+	Type *tp;
+
+	i8042a20();
+	memset(m, 0, sizeof(Mach));
+	trapinit();
+	clockinit();
+	alarminit();
+	spllo();
+
+	kbdinit();
+	
+	if((ulong)&end > (KZERO|(640*1024)))
+		panic("i'm too big");
+
+	/*
+	 * If there were any arguments, MS-DOS leaves a character
+	 * count followed by the arguments in the runtime header.
+	 * Step over the leading space.
+	 */
+	p = (char*)0x80080080;
+	if(p[0]){
+		p[p[0]+1] = 0;
+		p += 2;
+	}
+	else
+		p = 0;
+
+	/*
+	 * Advance command line to first option, if any
+	 */
+	if(p) {
+		while(*p==' ' || *p=='\t')
+			p++;
+		if(*p == 0)
+			p = nil;
+	}
+
+	/*
+ 	 * Probe everything, to collect device names.
+	 */
+	probe(Tany, Fnone, Dany);
+
+	if(p != 0) {
+		if((mp = parse(p, &file)) == nil) {
+			print("bad loadfile syntax: %s\n", p);
+			goto done;
+		}
+		boot(mp, file);
+	}
+
+done:
+	flag = 0;
+	for(tp = types; tp->type != Tnil; tp++){
+		for(mp = tp->media; mp; mp = mp->next){
+			if(flag == 0){
+				flag = 1;
+				print("Load devices:");
+			}
+			print(" %s", mp->name);
+		}
+	}
+	if(flag)
+		print("\n");
+
+	for(;;){
+		if(getstr("load from", line, sizeof(line), nil, 0) >= 0)
+			if(mp = parse(line, &file))
+				boot(mp, file);
+		def[0] = 0;
+	}
+}
+
+int
+getfields(char *lp, char **fields, int n, char sep)
+{
+	int i;
+
+	for(i = 0; lp && *lp && i < n; i++){
+		while(*lp == sep)
+			*lp++ = 0;
+		if(*lp == 0)
+			break;
+		fields[i] = lp;
+		while(*lp && *lp != sep){
+			if(*lp == '\\' && *(lp+1) == '\n')
+				*lp++ = ' ';
+			lp++;
+		}
+	}
+	return i;
+}
+
+int
+cistrcmp(char *a, char *b)
+{
+	int ac, bc;
+
+	for(;;){
+		ac = *a++;
+		bc = *b++;
+	
+		if(ac >= 'A' && ac <= 'Z')
+			ac = 'a' + (ac - 'A');
+		if(bc >= 'A' && bc <= 'Z')
+			bc = 'a' + (bc - 'A');
+		ac -= bc;
+		if(ac)
+			return ac;
+		if(bc == 0)
+			break;
+	}
+	return 0;
+}
+
+int
+cistrncmp(char *a, char *b, int n)
+{
+	unsigned ac, bc;
+
+	while(n > 0){
+		ac = *a++;
+		bc = *b++;
+		n--;
+
+		if(ac >= 'A' && ac <= 'Z')
+			ac = 'a' + (ac - 'A');
+		if(bc >= 'A' && bc <= 'Z')
+			bc = 'a' + (bc - 'A');
+
+		ac -= bc;
+		if(ac)
+			return ac;
+		if(bc == 0)
+			break;
+	}
+
+	return 0;
+}
+
+void*
+ialloc(ulong n, int align)
+{
+
+	static ulong palloc;
+	ulong p;
+	int a;
+
+	if(palloc == 0)
+		palloc = 3*1024*1024;
+
+	p = palloc;
+	if(align <= 0)
+		align = 4;
+	if(a = n % align)
+		n += align - a;
+	if(a = p % align)
+		p += align - a;
+
+	palloc = p+n;
+
+	return memset((void*)(p|KZERO), 0, n);
+}
+
+void*
+xspanalloc(ulong size, int align, ulong span)
+{
+	ulong a, v;
+
+	a = (ulong)ialloc(size+align+span, 0);
+
+	if(span > 2)
+		v = (a + span) & ~(span-1);
+	else
+		v = a;
+
+	if(align > 1)
+		v = (v + align) & ~(align-1);
+
+	return (void*)v;
+}
+
+static Block *allocbp;
+
+Block*
+allocb(int size)
+{
+	Block *bp, **lbp;
+	ulong addr;
+
+	lbp = &allocbp;
+	for(bp = *lbp; bp; bp = bp->next){
+		if((bp->lim - bp->base) >= size){
+			*lbp = bp->next;
+			break;
+		}
+		lbp = &bp->next;
+	}
+	if(bp == 0){
+		bp = ialloc(sizeof(Block)+size+64, 0);
+		addr = (ulong)bp;
+		addr = ROUNDUP(addr + sizeof(Block), 8);
+		bp->base = (uchar*)addr;
+		bp->lim = ((uchar*)bp) + sizeof(Block)+size+64;
+	}
+
+	if(bp->flag)
+		panic("allocb reuse\n");
+
+	bp->rp = bp->base;
+	bp->wp = bp->rp;
+	bp->next = 0;
+	bp->flag = 1;
+
+	return bp;
+}
+
+void
+freeb(Block* bp)
+{
+	bp->next = allocbp;
+	allocbp = bp;
+
+	bp->flag = 0;
+}
+
+enum {
+	Paddr=		0x70,	/* address port */
+	Pdata=		0x71,	/* data port */
+};
+
+uchar
+nvramread(int offset)
+{
+	outb(Paddr, offset);
+	return inb(Pdata);
+}
+
+void (*etherdetach)(void);
+void (*floppydetach)(void);
+void (*sddetach)(void);
+
+void
+warp9(ulong entry)
+{
+	if(etherdetach)
+		etherdetach();
+	consdrain();
+	(*(void(*)(void))(PADDR(entry)))();
+}
+
+char*
+getconf(char*)
+{
+	return nil;
+}
+
+void
+addconf(char*, ...)
+{
+}
+
+void
+uartspecial(int, void(*)(int), int(*)(void), int)
+{
+}
+
+void
+uartputs(IOQ*, char*, int)
+{
+}
+
+void
+uartputc(int)
+{}
+
+void
+uartdrain(void)
+{
+}
--- /dev/null
+++ b/os/boot/pc/boot.c
@@ -1,0 +1,451 @@
+#include "u.h"
+#include "lib.h"
+#include "mem.h"
+#include "dat.h"
+#include "fns.h"
+#include "io.h"
+
+#include "../../../utils/libmach/elf.h"
+
+static uchar elfident[7] = {
+	'\177', 'E', 'L', 'F', '\1', '\1', '\1'
+};
+static Ehdr ehdr, rehdr;
+static Phdr *phdr;
+static int curphdr;
+static ulong curoff;
+static ulong elftotal;
+static long (*swal)(long);
+static ushort (*swab)(ushort);
+
+/*
+ * big-endian short
+ */
+ushort
+beswab(ushort s)
+{
+	uchar *p;
+
+	p = (uchar*)&s;
+	return (p[0]<<8) | p[1];
+}
+
+/*
+ * big-endian long
+ */
+long
+beswal(long l)
+{
+	uchar *p;
+
+	p = (uchar*)&l;
+	return (p[0]<<24) | (p[1]<<16) | (p[2]<<8) | p[3];
+}
+
+/*
+ * little-endian short
+ */
+ushort
+leswab(ushort s)
+{
+	uchar *p;
+
+	p = (uchar*)&s;
+	return (p[1]<<8) | p[0];
+}
+
+/*
+ * little-endian long
+ */
+long
+leswal(long l)
+{
+	uchar *p;
+
+	p = (uchar*)&l;
+	return (p[3]<<24) | (p[2]<<16) | (p[1]<<8) | p[0];
+}
+
+/*
+ * Convert header to canonical form
+ */
+static void
+hswal(long *lp, int n, long (*swap) (long))
+{
+	while (n--) {
+		*lp = (*swap) (*lp);
+		lp++;
+	}
+}
+
+static int
+readehdr(Boot *b)
+{
+	int i;
+
+	/* bitswap the header according to the DATA format */
+	if(ehdr.ident[CLASS] != ELFCLASS32) {
+		print("bad ELF class - not 32 bit\n");
+		return 0;
+	}
+	if(ehdr.ident[DATA] == ELFDATA2LSB) {
+		swab = leswab;
+		swal = leswal;
+	} else if(ehdr.ident[DATA] == ELFDATA2MSB) {
+		swab = beswab;
+		swal = beswal;
+	} else {
+		print("bad ELF encoding - not big or little endian\n");
+		return 0;
+	}
+	memmove(&rehdr, &ehdr, sizeof(Ehdr));
+
+	ehdr.type = swab(ehdr.type);
+	ehdr.machine = swab(ehdr.machine);
+	ehdr.version = swal(ehdr.version);
+	ehdr.elfentry = swal(ehdr.elfentry);
+	ehdr.phoff = swal(ehdr.phoff);
+	ehdr.shoff = swal(ehdr.shoff);
+	ehdr.flags = swal(ehdr.flags);
+	ehdr.ehsize = swab(ehdr.ehsize);
+	ehdr.phentsize = swab(ehdr.phentsize);
+	ehdr.phnum = swab(ehdr.phnum);
+	ehdr.shentsize = swab(ehdr.shentsize);
+	ehdr.shnum = swab(ehdr.shnum);
+	ehdr.shstrndx = swab(ehdr.shstrndx);
+	if(ehdr.type != EXEC || ehdr.version != CURRENT)
+		return 0;
+	if(ehdr.phentsize != sizeof(Phdr))
+		return 0;
+
+	if(debug)
+		print("readehdr OK entry 0x%lux\n", ehdr.elfentry);
+
+	curoff = sizeof(Ehdr);
+	i = ehdr.phoff+ehdr.phentsize*ehdr.phnum - curoff;
+	b->state = READPHDR;
+	b->bp = (char*)malloc(i);
+	b->wp = b->bp;
+	b->ep = b->wp + i;
+	phdr = (Phdr*)(b->bp + ehdr.phoff-sizeof(Ehdr));
+	if(debug)
+		print("phdr...");
+
+	return 1;
+}
+
+static int
+nextphdr(Boot *b)
+{
+	Phdr *php;
+	ulong entry, offset;
+	char *paddr;
+
+	if(debug)
+		print("readedata %d\n", curphdr);
+
+	for(; curphdr < ehdr.phnum; curphdr++){
+		php = phdr+curphdr;
+		if(php->type != LOAD)
+			continue;
+		offset = php->offset;
+		paddr = (char*)PADDR(php->paddr);
+		if(offset < curoff){
+			/*
+			 * Can't (be bothered to) rewind the
+			 * input, it might be from tftp. If we
+			 * did then we could boot FreeBSD kernels
+			 * too maybe.
+			 */
+			return 0;
+		}
+		if(php->offset > curoff){
+			b->state = READEPAD;
+			b->bp = (char*)malloc(offset - curoff);
+			b->wp = b->bp;
+			b->ep = b->wp + offset - curoff;
+			if(debug)
+				print("nextphdr %lud...\n", offset - curoff);
+			return 1;
+		}
+		b->state = READEDATA;
+		b->bp = paddr;
+		b->wp = b->bp;
+		b->ep = b->wp+php->filesz;
+		print("%ud+", php->filesz);
+		elftotal += php->filesz;
+		if(debug)
+			print("nextphdr %ud@0x%p\n", php->filesz, paddr);
+
+		return 1;
+	}
+
+	if(curphdr != 0){
+		print("=%lud\n", elftotal);
+		b->state = TRYBOOT;
+		entry = ehdr.elfentry & ~0xF0000000;
+		PLLONG(b->exec.entry, entry);
+		return 1;
+	}
+
+	return 0;
+}
+
+static int
+readepad(Boot *b)
+{
+	Phdr *php;
+
+	php = phdr+curphdr;
+	if(debug)
+		print("readepad %d\n", curphdr);
+	curoff = php->offset;
+
+	return nextphdr(b);
+}
+
+static int
+readedata(Boot *b)
+{
+	Phdr *php;
+
+	php = phdr+curphdr;
+	if(debug)
+		print("readedata %d\n", curphdr);
+	if(php->filesz < php->memsz){
+		print("%lud",  php->memsz-php->filesz);
+		elftotal += php->memsz-php->filesz;
+		memset((char*)(PADDR(php->paddr)+php->filesz), 0, php->memsz-php->filesz);
+	}
+	curoff = php->offset+php->filesz;
+	curphdr++;
+
+	return nextphdr(b);
+}
+
+static int
+readphdr(Boot *b)
+{
+	Phdr *php;
+
+	php = phdr;
+	hswal((long*)php, ehdr.phentsize*ehdr.phnum/sizeof(long), swal);
+	if(debug)
+		print("phdr curoff %lud vaddr 0x%lux paddr 0x%lux\n",
+			curoff, php->vaddr, php->paddr);
+
+	curoff = ehdr.phoff+ehdr.phentsize*ehdr.phnum;
+	curphdr = 0;
+
+	return nextphdr(b);
+}
+
+static int
+addbytes(char **dbuf, char *edbuf, char **sbuf, char *esbuf)
+{
+	int n;
+
+	n = edbuf - *dbuf;
+	if(n <= 0)
+		return 0;
+	if(n > esbuf - *sbuf)
+		n = esbuf - *sbuf;
+	if(n <= 0)
+		return -1;
+
+	memmove(*dbuf, *sbuf, n);
+	*sbuf += n;
+	*dbuf += n;
+	return edbuf - *dbuf;
+}
+
+int
+bootpass(Boot *b, void *vbuf, int nbuf)
+{
+	char *buf, *ebuf;
+	Exec *ep;
+	ulong entry, data, text, bss;
+
+	if(b->state == FAILED)
+		return FAIL;
+
+	if(nbuf == 0)
+		goto Endofinput;
+
+	buf = vbuf;
+	ebuf = buf+nbuf;
+	while(addbytes(&b->wp, b->ep, &buf, ebuf) == 0) {
+		switch(b->state) {
+		case INITKERNEL:
+			b->state = READEXEC;
+			b->bp = (char*)&b->exec;
+			b->wp = b->bp;
+			b->ep = b->bp+sizeof(Exec);
+			break;
+		case READEXEC:
+			ep = &b->exec;
+			if(GLLONG(ep->magic) == I_MAGIC) {
+				b->state = READ9TEXT;
+				b->bp = (char*)PADDR(GLLONG(ep->entry));
+				b->wp = b->bp;
+				b->ep = b->wp+GLLONG(ep->text);
+				print("%lud", GLLONG(ep->text));
+				break;
+			}
+
+			/* check for gzipped kernel */
+			if(b->bp[0] == 0x1F && (uchar)b->bp[1] == 0x8B && b->bp[2] == 0x08) {
+				b->state = READGZIP;
+				b->bp = (char*)malloc(1440*1024);
+				b->wp = b->bp;
+				b->ep = b->wp + 1440*1024;
+				memmove(b->bp, &b->exec, sizeof(Exec));
+				b->wp += sizeof(Exec);
+				print("gz...");
+				break;
+			}
+
+			/*
+			 * Check for ELF.
+			 */
+			if(memcmp(b->bp, elfident, 4) == 0){
+				b->state = READEHDR;
+				b->bp = (char*)&ehdr;
+				b->wp = b->bp;
+				b->ep = b->wp + sizeof(Ehdr);
+				memmove(b->bp, &b->exec, sizeof(Exec));
+				b->wp += sizeof(Exec);
+				print("elf...");
+				break;
+			}
+
+			print("bad kernel format\n");
+			b->state = FAILED;
+			return FAIL;
+
+		case READ9TEXT:
+			ep = &b->exec;
+			b->state = READ9DATA;
+			b->bp = (char*)PGROUND(PADDR(GLLONG(ep->entry))+GLLONG(ep->text));
+			b->wp = b->bp;
+			b->ep = b->wp + GLLONG(ep->data);
+			print("+%ld", GLLONG(ep->data));
+			break;
+	
+		case READ9DATA:
+			ep = &b->exec;
+			bss = GLLONG(ep->bss);
+			print("+%ld=%ld\n",
+				bss, GLLONG(ep->text)+GLLONG(ep->data)+bss);
+			b->state = TRYBOOT;
+			return ENOUGH;
+
+		case READEHDR:
+			if(!readehdr(b)){
+				print("readehdr failed\n");
+				b->state = FAILED;
+				return FAIL;
+			}
+			break;
+
+		case READPHDR:
+			if(!readphdr(b)){
+				b->state = FAILED;
+				return FAIL;
+			}
+			break;
+
+		case READEPAD:
+			if(!readepad(b)){
+				b->state = FAILED;
+				return FAIL;
+			}
+			break;
+
+		case READEDATA:
+			if(!readedata(b)){
+				b->state = FAILED;
+				return FAIL;
+			}
+			if(b->state == TRYBOOT)
+				return ENOUGH;
+			break;
+
+		case TRYBOOT:
+		case READGZIP:
+			return ENOUGH;
+
+		case READ9LOAD:
+		case INIT9LOAD:
+			panic("9load");
+
+		default:
+			panic("bootstate");
+		}
+	}
+	return MORE;
+
+
+Endofinput:
+	/* end of input */
+	switch(b->state) {
+	case INITKERNEL:
+	case READEXEC:
+	case READ9TEXT:
+	case READ9DATA:
+	case READEHDR:
+	case READPHDR:
+	case READEPAD:
+	case READEDATA:
+		print("premature EOF\n");
+		b->state = FAILED;
+		return FAIL;
+	
+	case TRYBOOT:
+		entry = GLLONG(b->exec.entry);
+		print("entry: 0x%lux\n", entry);
+		warp9(PADDR(entry));
+		b->state = FAILED;
+		return FAIL;
+
+	case READGZIP:
+		ep = &b->exec;
+		if(b->bp[0] != 0x1F || (uchar)b->bp[1] != 0x8B || b->bp[2] != 0x08)
+			print("lost magic\n");
+
+		print("%ld => ", b->wp - b->bp);
+		if(gunzip((uchar*)ep, sizeof(*ep), (uchar*)b->bp, b->wp - b->bp) < sizeof(*ep)) {
+			print("badly compressed kernel\n");
+			return FAIL;
+		}
+
+		entry = GLLONG(ep->entry);
+		text = GLLONG(ep->text);
+		data = GLLONG(ep->data);
+		bss = GLLONG(ep->bss);
+		print("%lud+%lud+%lud=%lud\n", text, data, bss, text+data+bss);
+
+		if(gunzip((uchar*)PADDR(entry)-sizeof(Exec), sizeof(Exec)+text+data, 
+		     (uchar*)b->bp, b->wp-b->bp) < sizeof(Exec)+text+data) {
+			print("error uncompressing kernel\n");
+			return FAIL;
+		}
+
+		/* relocate data to start at page boundary */
+		memmove((void*)PGROUND(PADDR(entry+text)), (void*)(PADDR(entry+text)), data);
+
+		print("entry: %lux\n", entry);
+		warp9(PADDR(entry));
+		b->state = FAILED;
+		return FAIL;
+
+	case INIT9LOAD:
+	case READ9LOAD:
+		panic("end 9load");
+
+	default:
+		panic("bootdone");
+	}
+	b->state = FAILED;
+	return FAIL;
+}
--- /dev/null
+++ b/os/boot/pc/bootld.c
@@ -1,0 +1,108 @@
+#include "u.h"
+#include "lib.h"
+#include "mem.h"
+#include "dat.h"
+#include "fns.h"
+#include "io.h"
+
+static int
+addbytes(char **dbuf, char *edbuf, char **sbuf, char *esbuf)
+{
+	int n;
+
+	n = edbuf - *dbuf;
+	if(n <= 0)
+		return 0;
+	if(n > esbuf - *sbuf)
+		n = esbuf - *sbuf;
+	if(n <= 0)
+		return -1;
+
+	memmove(*dbuf, *sbuf, n);
+	*sbuf += n;
+	*dbuf += n;
+	return edbuf - *dbuf;
+}
+
+extern void origin(void);
+
+int
+bootpass(Boot *b, void *vbuf, int nbuf)
+{
+	char *buf, *ebuf, *p, *q;
+	ulong size;
+
+	if(b->state == FAILED)
+		return FAIL;
+
+	if(nbuf == 0)
+		goto Endofinput;
+
+	buf = vbuf;
+	ebuf = buf+nbuf;
+	while(addbytes(&b->wp, b->ep, &buf, ebuf) == 0) {
+		switch(b->state) {
+		case INIT9LOAD:
+			b->state = READ9LOAD;
+			b->bp = (char*)0x10000;
+			b->wp = b->bp;
+			b->ep = b->bp + 256*1024;
+			break;
+
+		case READ9LOAD:
+			return ENOUGH;
+
+		default:
+			panic("bootstate");
+		}
+	}
+	return MORE;
+
+
+Endofinput:
+	/* end of input */
+	print("\n");
+	switch(b->state) {
+	case INIT9LOAD:
+		print("premature EOF\n");
+		b->state = FAILED;
+		return FAIL;
+	
+	case READ9LOAD:
+		size = b->wp - b->bp;
+		if(memcmp(b->bp, origin, 16) != 0) {
+			print("beginning of file does not look right\n");
+			b->state = FAILED;
+			return FAIL;
+		}
+		if(size < 32*1024 || size > 256*1024) {
+			print("got %lud byte loader; not likely\n", size);
+			b->state = FAILED;
+			return FAIL;
+		}
+
+		p = b->bp;
+		q = b->wp;
+		if(q - p > 10000)	/* don't search much past l.s */
+			q = p+10000;
+
+		/*
+		 * The string `JUMP' appears right before
+		 * tokzero, which is where we want to jump.
+		 */
+		for(; p<q; p++) {
+			if(strncmp(p, "JUMP", 4) == 0) {
+				p += 4;
+				warp9((ulong)p);
+			}
+		}
+		print("could not find jump destination\n");
+		b->state = FAILED;
+		return FAIL;
+
+	default:
+		panic("bootdone");
+	}
+	b->state = FAILED;
+	return FAIL;
+}
--- /dev/null
+++ b/os/boot/pc/bootp.c
@@ -1,0 +1,659 @@
+#include "u.h"
+#include "lib.h"
+#include "mem.h"
+#include "dat.h"
+#include "fns.h"
+#include "io.h"
+
+#include "ip.h"
+
+extern int debugload;
+
+uchar broadcast[Eaddrlen] = {
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+};
+
+static ushort tftpport = 5000;
+static int Id = 1;
+static Netaddr myaddr;
+static Netaddr server;
+
+typedef struct {
+	uchar	header[4];
+	uchar	data[Segsize];
+} Tftp;
+static Tftp tftpb;
+
+static void
+hnputs(uchar *ptr, ushort val)
+{
+	ptr[0] = val>>8;
+	ptr[1] = val;
+}
+
+static void
+hnputl(uchar *ptr, ulong val)
+{
+	ptr[0] = val>>24;
+	ptr[1] = val>>16;
+	ptr[2] = val>>8;
+	ptr[3] = val;
+}
+
+static ulong
+nhgetl(uchar *ptr)
+{
+	return ((ptr[0]<<24) | (ptr[1]<<16) | (ptr[2]<<8) | ptr[3]);
+}
+
+static ushort
+nhgets(uchar *ptr)
+{
+	return ((ptr[0]<<8) | ptr[1]);
+}
+
+static	short	endian	= 1;
+static	char*	aendian	= (char*)&endian;
+#define	LITTLE	*aendian
+
+static ushort
+ptcl_csum(void *a, int len)
+{
+	uchar *addr;
+	ulong t1, t2;
+	ulong losum, hisum, mdsum, x;
+
+	addr = a;
+	losum = 0;
+	hisum = 0;
+	mdsum = 0;
+
+	x = 0;
+	if((ulong)addr & 1) {
+		if(len) {
+			hisum += addr[0];
+			len--;
+			addr++;
+		}
+		x = 1;
+	}
+	while(len >= 16) {
+		t1 = *(ushort*)(addr+0);
+		t2 = *(ushort*)(addr+2);	mdsum += t1;
+		t1 = *(ushort*)(addr+4);	mdsum += t2;
+		t2 = *(ushort*)(addr+6);	mdsum += t1;
+		t1 = *(ushort*)(addr+8);	mdsum += t2;
+		t2 = *(ushort*)(addr+10);	mdsum += t1;
+		t1 = *(ushort*)(addr+12);	mdsum += t2;
+		t2 = *(ushort*)(addr+14);	mdsum += t1;
+		mdsum += t2;
+		len -= 16;
+		addr += 16;
+	}
+	while(len >= 2) {
+		mdsum += *(ushort*)addr;
+		len -= 2;
+		addr += 2;
+	}
+	if(x) {
+		if(len)
+			losum += addr[0];
+		if(LITTLE)
+			losum += mdsum;
+		else
+			hisum += mdsum;
+	} else {
+		if(len)
+			hisum += addr[0];
+		if(LITTLE)
+			hisum += mdsum;
+		else
+			losum += mdsum;
+	}
+
+	losum += hisum >> 8;
+	losum += (hisum & 0xff) << 8;
+	while(hisum = losum>>16)
+		losum = hisum + (losum & 0xffff);
+
+	return ~losum;
+}
+
+static ushort
+ip_csum(uchar *addr)
+{
+	int len;
+	ulong sum = 0;
+
+	len = (addr[0]&0xf)<<2;
+
+	while(len > 0) {
+		sum += addr[0]<<8 | addr[1] ;
+		len -= 2;
+		addr += 2;
+	}
+
+	sum = (sum & 0xffff) + (sum >> 16);
+	sum = (sum & 0xffff) + (sum >> 16);
+	return (sum^0xffff);
+}
+
+enum {
+	/* this is only true of IPv4, but we're not doing v6 yet */
+	Min_udp_payload = ETHERMINTU - ETHERHDRSIZE - UDP_HDRSIZE,
+};
+
+static void
+udpsend(int ctlrno, Netaddr *a, void *data, int dlen)
+{
+	char payload[ETHERMAXTU];
+	Udphdr *uh;
+	Etherhdr *ip;
+	Etherpkt pkt;
+	int len, ptcllen;
+
+	/*
+	 * if packet is too short, make it longer rather than relying
+	 * on ethernet interface or lower layers to pad it.
+	 */
+	if (dlen < Min_udp_payload) {
+		memmove(payload, data, dlen);
+		data = payload;
+		dlen = Min_udp_payload;
+	}
+
+	uh = (Udphdr*)&pkt;
+
+	memset(uh, 0, sizeof(Etherpkt));
+	memmove(uh->udpcksum+sizeof(uh->udpcksum), data, dlen);
+
+	/*
+	 * UDP portion
+	 */
+	ptcllen = dlen + (UDP_HDRSIZE-UDP_PHDRSIZE);
+	uh->ttl = 0;
+	uh->udpproto = IP_UDPPROTO;
+	uh->frag[0] = 0;
+	uh->frag[1] = 0;
+	hnputs(uh->udpplen, ptcllen);
+	hnputl(uh->udpsrc, myaddr.ip);
+	hnputs(uh->udpsport, myaddr.port);
+	hnputl(uh->udpdst, a->ip);
+	hnputs(uh->udpdport, a->port);
+	hnputs(uh->udplen, ptcllen);
+	uh->udpcksum[0] = 0;
+	uh->udpcksum[1] = 0;
+	dlen = (dlen+1)&~1;
+	hnputs(uh->udpcksum, ptcl_csum(&uh->ttl, dlen+UDP_HDRSIZE));
+
+	/*
+	 * IP portion
+	 */
+	ip = (Etherhdr*)&pkt;
+	len = UDP_EHSIZE+UDP_HDRSIZE+dlen;		/* non-descriptive names */
+	ip->vihl = IP_VER|IP_HLEN;
+	ip->tos = 0;
+	ip->ttl = 255;
+	hnputs(ip->length, len-ETHER_HDR);
+	hnputs(ip->id, Id++);
+	ip->frag[0] = 0;
+	ip->frag[1] = 0;
+	ip->cksum[0] = 0;
+	ip->cksum[1] = 0;
+	hnputs(ip->cksum, ip_csum(&ip->vihl));
+
+	/*
+	 * Ethernet MAC portion
+	 */
+	hnputs(ip->type, ET_IP);
+	memmove(ip->d, a->ea, sizeof(ip->d));
+
+if(debug) {
+	print("udpsend ");
+}
+	ethertxpkt(ctlrno, &pkt, len, Timeout);
+}
+
+static void
+nak(int ctlrno, Netaddr *a, int code, char *msg, int report)
+{
+	int n;
+	char buf[128];
+
+	buf[0] = 0;
+	buf[1] = Tftp_ERROR;
+	buf[2] = 0;
+	buf[3] = code;
+	strcpy(buf+4, msg);
+	n = strlen(msg) + 4 + 1;
+	udpsend(ctlrno, a, buf, n);
+	if(report)
+		print("\ntftp: error(%d): %s\n", code, msg);
+}
+
+static int
+udprecv(int ctlrno, Netaddr *a, void *data, int dlen)
+{
+	int n, len;
+	ushort csm;
+	Udphdr *h;
+	ulong addr, timo;
+	Etherpkt pkt;
+	static int rxactive;
+
+	if(rxactive == 0)
+		timo = 1000;
+	else
+		timo = Timeout;
+	timo += TK2MS(m->ticks);
+	while(timo > TK2MS(m->ticks)){
+		n = etherrxpkt(ctlrno, &pkt, timo-TK2MS(m->ticks));
+		if(n <= 0)
+			continue;
+
+		h = (Udphdr*)&pkt;
+		if(debug)
+			print("udprecv %E to %E...\n", h->s, h->d);
+
+		if(nhgets(h->type) != ET_IP) {
+			if(debug)
+				print("not ip...");
+			continue;
+		}
+
+		if(ip_csum(&h->vihl)) {
+			print("ip chksum error\n");
+			continue;
+		}
+		if(h->vihl != (IP_VER|IP_HLEN)) {
+			print("ip bad vers/hlen\n");
+			continue;
+		}
+
+		if(h->udpproto != IP_UDPPROTO) {
+			if(debug)
+				print("not udp (%d)...", h->udpproto);
+			continue;
+		}
+
+		if(debug)
+			print("okay udp...");
+
+		h->ttl = 0;
+		len = nhgets(h->udplen);
+		hnputs(h->udpplen, len);
+
+		if(nhgets(h->udpcksum)) {
+			csm = ptcl_csum(&h->ttl, len+UDP_PHDRSIZE);
+			if(csm != 0) {
+				print("udp chksum error csum #%4ux len %d\n",
+					csm, n);
+				break;
+			}
+		}
+
+		if(a->port != 0 && nhgets(h->udpsport) != a->port) {
+			if(debug)
+				print("udpport %ux not %ux\n",
+					nhgets(h->udpsport), a->port);
+			continue;
+		}
+
+		addr = nhgetl(h->udpsrc);
+		if(a->ip != Bcastip && a->ip != addr) {
+			if(debug)
+				print("bad ip %lux not %lux\n", addr, a->ip);
+			continue;
+		}
+
+		len -= UDP_HDRSIZE-UDP_PHDRSIZE;
+		if(len > dlen) {
+			print("udp: packet too big: %d > %d; from addr %E\n",
+				len, dlen, h->udpsrc);
+			continue;
+		}
+
+		memmove(data, h->udpcksum+sizeof(h->udpcksum), len);
+		a->ip = addr;
+		a->port = nhgets(h->udpsport);
+		memmove(a->ea, pkt.s, sizeof(a->ea));
+
+		rxactive = 1;
+		return len;
+	}
+
+	return 0;
+}
+
+static int tftpblockno;
+
+/*
+ * format of a request packet, from the RFC:
+ *
+            2 bytes     string    1 byte     string   1 byte
+            ------------------------------------------------
+           | Opcode |  Filename  |   0  |    Mode    |   0  |
+            ------------------------------------------------
+ */
+static int
+tftpopen(int ctlrno, Netaddr *a, char *name, Tftp *tftp)
+{
+	int i, len, rlen, oport;
+	char buf[Segsize+2];
+
+	buf[0] = 0;
+	buf[1] = Tftp_READ;
+	len = 2 + sprint(buf+2, "%s", name) + 1;
+	len += sprint(buf+len, "octet") + 1;
+
+	oport = a->port;
+	for(i = 0; i < 5; i++){
+		a->port = oport;
+		udpsend(ctlrno, a, buf, len);
+		a->port = 0;
+		if((rlen = udprecv(ctlrno, a, tftp, sizeof(Tftp))) < sizeof(tftp->header))
+			continue;
+
+		switch((tftp->header[0]<<8)|tftp->header[1]){
+
+		case Tftp_ERROR:
+			print("tftpopen: error (%d): %s\n",
+				(tftp->header[2]<<8)|tftp->header[3], (char*)tftp->data);
+			return -1;
+
+		case Tftp_DATA:
+			tftpblockno = 1;
+			len = (tftp->header[2]<<8)|tftp->header[3];
+			if(len != tftpblockno){
+				print("tftpopen: block error: %d\n", len);
+				nak(ctlrno, a, 1, "block error", 0);
+				return -1;
+			}
+			rlen -= sizeof(tftp->header);
+			if(rlen < Segsize){
+				/* ACK now, in case we don't later */
+				buf[0] = 0;
+				buf[1] = Tftp_ACK;
+				buf[2] = tftpblockno>>8;
+				buf[3] = tftpblockno;
+				udpsend(ctlrno, a, buf, sizeof(tftp->header));
+			}
+			return rlen;
+		}
+	}
+
+	print("tftpopen: failed to connect to server\n");
+	return -1;
+}
+
+static int
+tftpread(int ctlrno, Netaddr *a, Tftp *tftp, int dlen)
+{
+	uchar buf[4];
+	int try, blockno, len;
+
+	dlen += sizeof(tftp->header);
+
+	for(try = 0; try < 10; try++) {
+		buf[0] = 0;
+		buf[1] = Tftp_ACK;
+		buf[2] = tftpblockno>>8;
+		buf[3] = tftpblockno;
+
+		udpsend(ctlrno, a, buf, sizeof(buf));
+		len = udprecv(ctlrno, a, tftp, dlen);
+		if(len <= sizeof(tftp->header)){
+			if(debug)
+				print("tftpread: too short %d <= %d\n",
+					len, sizeof(tftp->header));
+			continue;
+		}
+		blockno = (tftp->header[2]<<8)|tftp->header[3];
+		if(blockno <= tftpblockno){
+			if(debug)
+				print("tftpread: blkno %d <= %d\n",
+					blockno, tftpblockno);
+			continue;
+		}
+
+		if(blockno == tftpblockno+1) {
+			tftpblockno++;
+			if(len < dlen) {	/* last packet; send final ack */
+				tftpblockno++;
+				buf[0] = 0;
+				buf[1] = Tftp_ACK;
+				buf[2] = tftpblockno>>8;
+				buf[3] = tftpblockno;
+				udpsend(ctlrno, a, buf, sizeof(buf));
+			}
+			return len-sizeof(tftp->header);
+		}
+		print("tftpread: block error: %d, expected %d\n",
+			blockno, tftpblockno+1);
+	}
+
+	return -1;
+}
+
+static int
+bootpopen(int ctlrno, char *file, Bootp *rep, int dotftpopen)
+{
+	Bootp req;
+	int i, n;
+	uchar *ea;
+	char name[128], *filename, *sysname;
+
+	if (debugload)
+		print("bootpopen: ether%d!%s...", ctlrno, file);
+	if((ea = etheraddr(ctlrno)) == 0){
+		print("invalid ctlrno %d\n", ctlrno);
+		return -1;
+	}
+
+	filename = 0;
+	sysname = 0;
+	if(file && *file){
+		strcpy(name, file);
+		if(filename = strchr(name, '!')){
+			sysname = name;
+			*filename++ = 0;
+		}
+		else
+			filename = name;
+	}
+
+	memset(&req, 0, sizeof(req));
+	req.op = Bootrequest;
+	req.htype = 1;			/* ethernet */
+	req.hlen = Eaddrlen;		/* ethernet */
+	memmove(req.chaddr, ea, Eaddrlen);
+	if(filename != nil)
+		strncpy(req.file, filename, sizeof(req.file));
+	if(sysname != nil)
+		strncpy(req.sname, sysname, sizeof(req.sname));
+
+	myaddr.ip = 0;
+	myaddr.port = BPportsrc;
+	memmove(myaddr.ea, ea, Eaddrlen);
+
+	etherrxflush(ctlrno);
+	for(i = 0; i < 10; i++) {
+		server.ip = Bcastip;
+		server.port = BPportdst;
+		memmove(server.ea, broadcast, sizeof(server.ea));
+		udpsend(ctlrno, &server, &req, sizeof(req));
+		if(udprecv(ctlrno, &server, rep, sizeof(*rep)) <= 0)
+			continue;
+		if(memcmp(req.chaddr, rep->chaddr, Eaddrlen))
+			continue;
+		if(rep->htype != 1 || rep->hlen != Eaddrlen)
+			continue;
+		if(sysname == 0 || strcmp(sysname, rep->sname) == 0)
+			break;
+	}
+	if(i >= 10) {
+		print("bootp timed out\n");
+		return -1;
+	}
+
+	if(!dotftpopen)
+		return 0;
+
+	if(filename == 0 || *filename == 0){
+		if(strcmp(rep->file, "/386/9pxeload") == 0)
+			return -1;
+		filename = rep->file;
+	}
+
+	if(rep->sname[0] != '\0')
+		 print("%s ", rep->sname);
+	print("(%d.%d.%d.%d!%d): %s\n",
+		rep->siaddr[0],
+		rep->siaddr[1],
+		rep->siaddr[2],
+		rep->siaddr[3],
+		server.port,
+		filename);
+
+	myaddr.ip = nhgetl(rep->yiaddr);
+	myaddr.port = tftpport++;
+	server.ip = nhgetl(rep->siaddr);
+	server.port = TFTPport;
+
+	if((n = tftpopen(ctlrno, &server, filename, &tftpb)) < 0)
+		return -1;
+
+	return n;
+}
+
+int
+bootpboot(int ctlrno, char *file, Boot *b)
+{
+	int n;
+	Bootp rep;
+
+	if((n = bootpopen(ctlrno, file, &rep, 1)) < 0)
+		return -1;
+
+	while(bootpass(b, tftpb.data, n) == MORE){
+		n = tftpread(ctlrno, &server, &tftpb, sizeof(tftpb.data));
+		if(n < sizeof(tftpb.data))
+			break;
+	}
+
+	if(0 < n && n < sizeof(tftpb.data))	/* got to end of file */
+		bootpass(b, tftpb.data, n);
+	else
+		nak(ctlrno, &server, 3, "ok", 0);	/* tftpclose to abort transfer */
+	bootpass(b, nil, 0);	/* boot if possible */
+	return -1;
+}
+
+#include "fs.h"
+
+#define INIPATHLEN	64
+
+static struct {
+	Fs	fs;
+	char	ini[INIPATHLEN];
+} pxether[MaxEther];
+
+static vlong
+pxediskseek(Fs*, vlong)
+{
+	return -1LL;
+}
+
+static long
+pxediskread(Fs*, void*, long)
+{
+	return -1;
+}
+
+static long
+pxeread(File* f, void* va, long len)
+{
+	int n;
+	Bootp rep;
+	char *p, *v;
+
+	if((n = bootpopen(f->fs->dev, pxether[f->fs->dev].ini, &rep, 1)) < 0)
+		return -1;
+
+	p = v = va;
+	while(n > 0) {
+		if((p-v)+n > len)
+			n = len - (p-v);
+		memmove(p, tftpb.data, n);
+		p += n;
+		if(n != Segsize)
+			break;
+		if((n = tftpread(f->fs->dev, &server, &tftpb, sizeof(tftpb.data))) < 0)
+			return -1;
+	}
+	return p-v;
+}
+
+static int
+pxewalk(File* f, char* name)
+{
+	Bootp rep;
+	char *ini;
+
+	switch(f->walked){
+	default:
+		return -1;
+	case 0:
+		if(strcmp(name, "cfg") == 0){
+			f->walked = 1;
+			return 1;
+		}
+		break;
+	case 1:
+		if(strcmp(name, "pxe") == 0){
+			f->walked = 2;
+			return 1;
+		}
+		break;
+	case 2:
+		if(strcmp(name, "%E") != 0)
+			break;
+		f->walked = 3;
+
+		if(bootpopen(f->fs->dev, nil, &rep, 0) < 0)
+			return 0;
+
+		ini = pxether[f->fs->dev].ini;
+		/* use our mac address instead of relying on a bootp answer */
+		snprint(ini, INIPATHLEN, "/cfg/pxe/%E", (uchar *)myaddr.ea);
+		f->path = ini;
+
+		return 1;
+	}
+	return 0;
+}
+
+void*
+pxegetfspart(int ctlrno, char* part, int)
+{
+	if(!pxe)
+		return nil;
+	if(strcmp(part, "*") != 0)
+		return nil;
+	if(ctlrno >= MaxEther)
+		return nil;
+	if(iniread && getconf("*pxeini") != nil)
+		return nil;
+
+	pxether[ctlrno].fs.dev = ctlrno;
+	pxether[ctlrno].fs.diskread = pxediskread;
+	pxether[ctlrno].fs.diskseek = pxediskseek;
+
+	pxether[ctlrno].fs.read = pxeread;
+	pxether[ctlrno].fs.walk = pxewalk;
+
+	pxether[ctlrno].fs.root.fs = &pxether[ctlrno].fs;
+	pxether[ctlrno].fs.root.walked = 0;
+
+	return &pxether[ctlrno].fs;
+}
--- /dev/null
+++ b/os/boot/pc/cga.c
@@ -1,0 +1,91 @@
+#include "u.h"
+#include "lib.h"
+#include "mem.h"
+#include "dat.h"
+#include "fns.h"
+
+enum {
+	Width		= 160,
+	Height		= 25,
+
+	Attr		= 7,		/* white on black */
+};
+
+#define CGASCREENBASE	((uchar*)KADDR(0xB8000))
+
+static int pos;
+static int screeninitdone;
+
+static uchar
+cgaregr(int index)
+{
+	outb(0x3D4, index);
+	return inb(0x3D4+1) & 0xFF;
+}
+
+static void
+cgaregw(int index, int data)
+{
+	outb(0x3D4, index);
+	outb(0x3D4+1, data);
+}
+
+static void
+movecursor(void)
+{
+	cgaregw(0x0E, (pos/2>>8) & 0xFF);
+	cgaregw(0x0F, pos/2 & 0xFF);
+	CGASCREENBASE[pos+1] = Attr;
+}
+
+static void
+cgascreenputc(int c)
+{
+	int i;
+
+	if(c == '\n'){
+		pos = pos/Width;
+		pos = (pos+1)*Width;
+	}
+	else if(c == '\t'){
+		i = 8 - ((pos/2)&7);
+		while(i-->0)
+			cgascreenputc(' ');
+	}
+	else if(c == '\b'){
+		if(pos >= 2)
+			pos -= 2;
+		cgascreenputc(' ');
+		pos -= 2;
+	}
+	else{
+		CGASCREENBASE[pos++] = c;
+		CGASCREENBASE[pos++] = Attr;
+	}
+	if(pos >= Width*Height){
+		memmove(CGASCREENBASE, &CGASCREENBASE[Width], Width*(Height-1));
+		memset(&CGASCREENBASE[Width*(Height-1)], 0, Width);
+		pos = Width*(Height-1);
+	}
+	movecursor();
+}
+
+static void
+screeninit(void)
+{
+	if(screeninitdone == 0){
+		pos = cgaregr(0x0E)<<8;
+		pos |= cgaregr(0x0F);
+		pos *= 2;
+		screeninitdone = 1;
+	}
+}
+
+void
+cgascreenputs(char* s, int n)
+{
+	if(screeninitdone == 0)
+		screeninit();
+	while(n-- > 0)
+		cgascreenputc(*s++);
+}
--- /dev/null
+++ b/os/boot/pc/cis.c
@@ -1,0 +1,539 @@
+#include "u.h"
+#include "lib.h"
+#include "mem.h"
+#include "dat.h"
+#include "fns.h"
+#include "error.h"
+#include "io.h"
+
+enum{
+	Linktarget = 0x13,
+};
+	
+/*
+ *  read and crack the card information structure enough to set
+ *  important parameters like power
+ */
+/* cis memory walking */
+typedef struct Cisdat {
+	uchar	*cisbase;
+	int	cispos;
+	int	cisskip;
+	int	cislen;
+} Cisdat;
+
+static void	tcfig(PCMslot*, Cisdat*, int);
+static void	tentry(PCMslot*, Cisdat*, int);
+static void	tvers1(PCMslot*, Cisdat*, int);
+static void	tlonglnkmfc(PCMslot*, Cisdat*, int);
+
+static int
+readc(Cisdat *cis, uchar *x)
+{
+	if(cis->cispos >= cis->cislen)
+		return 0;
+	*x = cis->cisbase[cis->cisskip*cis->cispos];
+	cis->cispos++;
+	return 1;
+}
+
+static int
+xcistuple(int slotno, int tuple, int subtuple, void *v, int nv, int attr)
+{
+	PCMmap *m;
+	Cisdat cis;
+	int i, l;
+	uchar *p;
+	uchar type, link, n, c;
+	int this, subtype;
+
+	m = pcmmap(slotno, 0, 0, attr);
+	if(m == 0)
+		return -1;
+
+	cis.cisbase = KADDR(m->isa);
+	cis.cispos = 0;
+	cis.cisskip = attr ? 2 : 1;
+	cis.cislen = m->len;
+
+	/* loop through all the tuples */
+	for(i = 0; i < 1000; i++){
+		this = cis.cispos;
+		if(readc(&cis, &type) != 1)
+			break;
+		if(type == 0xFF)
+			break;
+		if(readc(&cis, &link) != 1)
+			break;
+		if(link == 0xFF)
+			break;
+
+		n = link;
+		if(link > 1 && subtuple != -1){
+			if(readc(&cis, &c) != 1)
+				break;
+			subtype = c;
+			n--;
+		}else
+			subtype = -1;
+
+		if(type == tuple && subtype == subtuple){
+			p = v;
+			for(l=0; l<nv && l<n; l++)
+				if(readc(&cis, p++) != 1)
+					break;
+			pcmunmap(slotno, m);
+			return nv;
+		}
+		cis.cispos = this + (2+link);
+	}
+	pcmunmap(slotno, m);
+	return -1;
+}
+
+int
+pcmcistuple(int slotno, int tuple, int subtuple, void *v, int nv)
+{
+	int n;
+
+	/* try attribute space, then memory */
+	if((n = xcistuple(slotno, tuple, subtuple, v, nv, 1)) >= 0)
+		return n;
+	return xcistuple(slotno, tuple, subtuple, v, nv, 0);
+}
+
+void
+pcmcisread(PCMslot *pp)
+{
+	int this;
+	Cisdat cis;
+	PCMmap *m;
+	uchar type, link;
+
+	memset(pp->ctab, 0, sizeof(pp->ctab));
+	pp->ncfg = 0;
+	memset(pp->cfg, 0, sizeof(pp->cfg));
+	pp->configed = 0;
+	pp->nctab = 0;
+	pp->verstr[0] = 0;
+
+	/*
+	 * Read all tuples in attribute space.
+	 */
+	m = pcmmap(pp->slotno, 0, 0, 1);
+	if(m == 0)
+		return;
+
+	cis.cisbase = KADDR(m->isa);
+	cis.cispos = 0;
+	cis.cisskip = 2;
+	cis.cislen = m->len;
+
+	/* loop through all the tuples */
+	for(;;){
+		this = cis.cispos;
+		if(readc(&cis, &type) != 1)
+			break;
+		if(type == 0xFF)
+			break;
+		if(readc(&cis, &link) != 1)
+			break;
+
+		switch(type){
+		default:
+			break;
+		case 6:
+			tlonglnkmfc(pp, &cis, type);
+			break;
+		case 0x15:
+			tvers1(pp, &cis, type);
+			break;
+		case 0x1A:
+			tcfig(pp, &cis, type);
+			break;
+		case 0x1B:
+			tentry(pp, &cis, type);
+			break;
+		}
+
+		if(link == 0xFF)
+			break;
+		cis.cispos = this + (2+link);
+	}
+	pcmunmap(pp->slotno, m);
+}
+
+static ulong
+getlong(Cisdat *cis, int size)
+{
+	uchar c;
+	int i;
+	ulong x;
+
+	x = 0;
+	for(i = 0; i < size; i++){
+		if(readc(cis, &c) != 1)
+			break;
+		x |= c<<(i*8);
+	}
+	return x;
+}
+
+static void
+tcfig(PCMslot *pp, Cisdat *cis, int )
+{
+	uchar size, rasize, rmsize;
+	uchar last;
+
+	if(readc(cis, &size) != 1)
+		return;
+	rasize = (size&0x3) + 1;
+	rmsize = ((size>>2)&0xf) + 1;
+	if(readc(cis, &last) != 1)
+		return;
+
+	if(pp->ncfg >= 8){
+		print("tcfig: too many configuration registers\n");
+		return;
+	}
+	
+	pp->cfg[pp->ncfg].caddr = getlong(cis, rasize);
+	pp->cfg[pp->ncfg].cpresent = getlong(cis, rmsize);
+	pp->ncfg++;
+}
+
+static ulong vexp[8] =
+{
+	1, 10, 100, 1000, 10000, 100000, 1000000, 10000000
+};
+static ulong vmant[16] =
+{
+	10, 12, 13, 15, 20, 25, 30, 35, 40, 45, 50, 55, 60, 70, 80, 90,
+};
+
+static ulong
+microvolt(Cisdat *cis)
+{
+	uchar c;
+	ulong microvolts;
+	ulong exp;
+
+	if(readc(cis, &c) != 1)
+		return 0;
+	exp = vexp[c&0x7];
+	microvolts = vmant[(c>>3)&0xf]*exp;
+	while(c & 0x80){
+		if(readc(cis, &c) != 1)
+			return 0;
+		switch(c){
+		case 0x7d:
+			break;		/* high impedence when sleeping */
+		case 0x7e:
+		case 0x7f:
+			microvolts = 0;	/* no connection */
+			break;
+		default:
+			exp /= 10;
+			microvolts += exp*(c&0x7f);
+		}
+	}
+	return microvolts;
+}
+
+static ulong
+nanoamps(Cisdat *cis)
+{
+	uchar c;
+	ulong nanoamps;
+
+	if(readc(cis, &c) != 1)
+		return 0;
+	nanoamps = vexp[c&0x7]*vmant[(c>>3)&0xf];
+	while(c & 0x80){
+		if(readc(cis, &c) != 1)
+			return 0;
+		if(c == 0x7d || c == 0x7e || c == 0x7f)
+			nanoamps = 0;
+	}
+	return nanoamps;
+}
+
+/*
+ * only nominal voltage (feature 1) is important for config,
+ * other features must read card to stay in sync.
+ */
+static ulong
+power(Cisdat *cis)
+{
+	uchar feature;
+	ulong mv;
+
+	mv = 0;
+	if(readc(cis, &feature) != 1)
+		return 0;
+	if(feature & 1)
+		mv = microvolt(cis);
+	if(feature & 2)
+		microvolt(cis);
+	if(feature & 4)
+		microvolt(cis);
+	if(feature & 8)
+		nanoamps(cis);
+	if(feature & 0x10)
+		nanoamps(cis);
+	if(feature & 0x20)
+		nanoamps(cis);
+	if(feature & 0x40)
+		nanoamps(cis);
+	return mv/1000000;
+}
+
+static ulong mantissa[16] =
+{ 0, 10, 12, 13, 15, 20, 25, 30, 35, 40, 45, 50, 55, 60, 70, 80, };
+
+static ulong exponent[8] =
+{ 1, 10, 100, 1000, 10000, 100000, 1000000, 10000000, };
+
+static ulong
+ttiming(Cisdat *cis, int scale)
+{
+	uchar unscaled;
+	ulong nanosecs;
+
+	if(readc(cis, &unscaled) != 1)
+		return 0;
+	nanosecs = (mantissa[(unscaled>>3)&0xf]*exponent[unscaled&7])/10;
+	nanosecs = nanosecs * vexp[scale];
+	return nanosecs;
+}
+
+static void
+timing(Cisdat *cis, PCMconftab *ct)
+{
+	uchar c, i;
+
+	if(readc(cis, &c) != 1)
+		return;
+	i = c&0x3;
+	if(i != 3)
+		ct->maxwait = ttiming(cis, i);		/* max wait */
+	i = (c>>2)&0x7;
+	if(i != 7)
+		ct->readywait = ttiming(cis, i);		/* max ready/busy wait */
+	i = (c>>5)&0x7;
+	if(i != 7)
+		ct->otherwait = ttiming(cis, i);		/* reserved wait */
+}
+
+static void
+iospaces(Cisdat *cis, PCMconftab *ct)
+{
+	uchar c;
+	int i, nio;
+
+	ct->nio = 0;
+	if(readc(cis, &c) != 1)
+		return;
+
+	ct->bit16 = ((c>>5)&3) >= 2;
+	if(!(c & 0x80)){
+		ct->io[0].start = 0;
+		ct->io[0].len = 1<<(c&0x1f);
+		ct->nio = 1;
+		return;
+	}
+
+	if(readc(cis, &c) != 1)
+		return;
+
+	/*
+	 * For each of the range descriptions read the
+	 * start address and the length (value is length-1).
+	 */
+	nio = (c&0xf)+1;
+	for(i = 0; i < nio; i++){
+		ct->io[i].start = getlong(cis, (c>>4)&0x3);
+		ct->io[i].len = getlong(cis, (c>>6)&0x3)+1;
+	}
+	ct->nio = nio;
+}
+
+static void
+irq(Cisdat *cis, PCMconftab *ct)
+{
+	uchar c;
+
+	if(readc(cis, &c) != 1)
+		return;
+	ct->irqtype = c & 0xe0;
+	if(c & 0x10)
+		ct->irqs = getlong(cis, 2);
+	else
+		ct->irqs = 1<<(c&0xf);
+	ct->irqs &= 0xDEB8;		/* levels available to card */
+}
+
+static void
+memspace(Cisdat *cis, int asize, int lsize, int host)
+{
+	ulong haddress, address, len;
+
+	len = getlong(cis, lsize)*256;
+	address = getlong(cis, asize)*256;
+	USED(len, address);
+	if(host){
+		haddress = getlong(cis, asize)*256;
+		USED(haddress);
+	}
+}
+
+static void
+tentry(PCMslot *pp, Cisdat *cis, int )
+{
+	uchar c, i, feature;
+	PCMconftab *ct;
+
+	if(pp->nctab >= nelem(pp->ctab))
+		return;
+	if(readc(cis, &c) != 1)
+		return;
+	ct = &pp->ctab[pp->nctab++];
+
+	/* copy from last default config */
+	if(pp->def)
+		*ct = *pp->def;
+
+	ct->index = c & 0x3f;
+
+	/* is this the new default? */
+	if(c & 0x40)
+		pp->def = ct;
+
+	/* memory wait specified? */
+	if(c & 0x80){
+		if(readc(cis, &i) != 1)
+			return;
+		if(i&0x80)
+			ct->memwait = 1;
+	}
+
+	if(readc(cis, &feature) != 1)
+		return;
+	switch(feature&0x3){
+	case 1:
+		ct->vpp1 = ct->vpp2 = power(cis);
+		break;
+	case 2:
+		power(cis);
+		ct->vpp1 = ct->vpp2 = power(cis);
+		break;
+	case 3:
+		power(cis);
+		ct->vpp1 = power(cis);
+		ct->vpp2 = power(cis);
+		break;
+	default:
+		break;
+	}
+	if(feature&0x4)
+		timing(cis, ct);
+	if(feature&0x8)
+		iospaces(cis, ct);
+	if(feature&0x10)
+		irq(cis, ct);
+	switch((feature>>5)&0x3){
+	case 1:
+		memspace(cis, 0, 2, 0);
+		break;
+	case 2:
+		memspace(cis, 2, 2, 0);
+		break;
+	case 3:
+		if(readc(cis, &c) != 1)
+			return;
+		for(i = 0; i <= (c&0x7); i++)
+			memspace(cis, (c>>5)&0x3, (c>>3)&0x3, c&0x80);
+		break;
+	}
+	pp->configed++;
+}
+
+static void
+tvers1(PCMslot *pp, Cisdat *cis, int )
+{
+	uchar c, major, minor, last;
+	int  i;
+
+	if(readc(cis, &major) != 1)
+		return;
+	if(readc(cis, &minor) != 1)
+		return;
+	last = 0;
+	for(i = 0; i < sizeof(pp->verstr)-1; i++){
+		if(readc(cis, &c) != 1)
+			return;
+		if(c == 0)
+			c = ';';
+		if(c == '\n')
+			c = ';';
+		if(c == 0xff)
+			break;
+		if(c == ';' && last == ';')
+			continue;
+		pp->verstr[i] = c;
+		last = c;
+	}
+	pp->verstr[i] = 0;
+}
+
+static void
+tlonglnkmfc(PCMslot *pp, Cisdat *cis, int)
+{
+	int i, npos, opos;
+	uchar nfn, space, expect, type, this, link;
+
+	readc(cis, &nfn);
+	for(i = 0; i < nfn; i++){
+		readc(cis, &space);
+		npos        = getlong(cis, 4);
+		opos        = cis->cispos;
+		cis->cispos = npos;
+		expect      = Linktarget;
+
+		while(1){
+			this = cis->cispos;
+			if(readc(cis, &type) != 1)
+				break;
+			if(type == 0xFF)
+				break;
+			if(readc(cis, &link) != 1)
+				break;
+
+			if(expect && expect != type){
+				print("tlonglnkmfc: expected %X found %X\n",
+					expect, type);
+				break;
+			}
+			expect = 0;
+
+			switch(type){
+			default:
+				break;
+			case 0x15:
+				tvers1(pp, cis, type);
+				break;
+			case 0x1A:
+				tcfig(pp, cis, type);
+				break;
+			case 0x1B:
+				tentry(pp, cis, type);
+				break;
+			}
+
+			if(link == 0xFF)
+				break;
+			cis->cispos = this + (2+link);
+		}
+		cis->cispos = opos;
+	}
+}
--- /dev/null
+++ b/os/boot/pc/clock.c
@@ -1,0 +1,309 @@
+#include	"u.h"
+#include	"lib.h"
+#include	"mem.h"
+#include	"dat.h"
+#include	"fns.h"
+#include	"io.h"
+#include	"ureg.h"
+
+/*
+ *  8253 timer
+ */
+enum
+{
+	T0cntr=	0x40,		/* counter ports */
+	T1cntr=	0x41,		/* ... */
+	T2cntr=	0x42,		/* ... */
+	Tmode=	0x43,		/* mode port */
+
+	/* commands */
+	Latch0=	0x00,		/* latch counter 0's value */
+	Load0=	0x30,		/* load counter 0 with 2 bytes */
+
+	/* modes */
+	Square=	0x36,		/* perioic square wave */
+
+	Freq=	1193182,	/* Real clock frequency */
+};
+
+static uvlong cpuhz = 66000000;
+static int cpumhz = 66;
+static int loopconst = 100;
+int cpuidax, cpuiddx;
+int havetsc;
+
+extern void _cycles(uvlong*);		/* in l.s */
+extern void wrmsr(int, vlong);
+
+static void
+clockintr(Ureg*, void*)
+{
+	m->ticks++;
+	checkalarms();
+}
+
+#define STEPPING(x)	((x)&0xf)
+#define X86MODEL(x)	(((x)>>4)&0xf)
+#define X86FAMILY(x)	(((x)>>8)&0xf)
+
+enum
+{
+	/* flags */
+	CpuidFPU	= 0x001,	/* on-chip floating point unit */
+	CpuidMCE	= 0x080,	/* machine check exception */
+	CpuidCX8	= 0x100,	/* CMPXCHG8B instruction */
+};
+
+typedef struct
+{
+	int family;
+	int model;
+	int aalcycles;
+	char *name;
+} X86type;
+
+X86type x86intel[] =
+{
+	{ 4,	0,	22,	"486DX", },	/* known chips */
+	{ 4,	1,	22,	"486DX50", },
+	{ 4,	2,	22,	"486SX", },
+	{ 4,	3,	22,	"486DX2", },
+	{ 4,	4,	22,	"486SL", },
+	{ 4,	5,	22,	"486SX2", },
+	{ 4,	7,	22,	"DX2WB", },	/* P24D */
+	{ 4,	8,	22,	"DX4", },	/* P24C */
+	{ 4,	9,	22,	"DX4WB", },	/* P24CT */
+	{ 5,	0,	23,	"P5", },
+	{ 5,	1,	23,	"P5", },
+	{ 5,	2,	23,	"P54C", },
+	{ 5,	3,	23,	"P24T", },
+	{ 5,	4,	23,	"P55C MMX", },
+	{ 5,	7,	23,	"P54C VRT", },
+	{ 6,	1,	16,	"PentiumPro", },/* trial and error */
+	{ 6,	3,	16,	"PentiumII", },
+	{ 6,	5,	16,	"PentiumII/Xeon", },
+	{ 6,	6,	16,	"Celeron", },
+	{ 6,	7,	16,	"PentiumIII/Xeon", },
+	{ 6,	8,	16,	"PentiumIII/Xeon", },
+	{ 6,	0xB,	16,	"PentiumIII/Xeon", },
+	{ 0xF,	1,	16,	"P4", },	/* P4 */
+	{ 0xF,	2,	16,	"PentiumIV/Xeon", },
+
+	{ 3,	-1,	32,	"386", },	/* family defaults */
+	{ 4,	-1,	22,	"486", },
+	{ 5,	-1,	23,	"P5", },
+	{ 6,	-1,	16,	"P6", },
+	{ 0xF,	-1,	16,	"P4", },	/* P4 */
+
+	{ -1,	-1,	16,	"unknown", },	/* total default */
+};
+
+
+/*
+ * The AMD processors all implement the CPUID instruction.
+ * The later ones also return the processor name via functions
+ * 0x80000002, 0x80000003 and 0x80000004 in registers AX, BX, CX
+ * and DX:
+ *	K5	"AMD-K5(tm) Processor"
+ *	K6	"AMD-K6tm w/ multimedia extensions"
+ *	K6 3D	"AMD-K6(tm) 3D processor"
+ *	K6 3D+	?
+ */
+static X86type x86amd[] =
+{
+	{ 5,	0,	23,	"AMD-K5", },	/* guesswork */
+	{ 5,	1,	23,	"AMD-K5", },	/* guesswork */
+	{ 5,	2,	23,	"AMD-K5", },	/* guesswork */
+	{ 5,	3,	23,	"AMD-K5", },	/* guesswork */
+	{ 5,	4,	23,	"AMD Geode GX1", },	/* guesswork */
+	{ 5,	5,	23,	"AMD Geode GX2", },	/* guesswork */
+	{ 5,	6,	11,	"AMD-K6", },	/* trial and error */
+	{ 5,	7,	11,	"AMD-K6", },	/* trial and error */
+	{ 5,	8,	11,	"AMD-K6-2", },	/* trial and error */
+	{ 5,	9,	11,	"AMD-K6-III", },/* trial and error */
+	{ 5,	0xa,	23,	"AMD Geode LX", },	/* guesswork */
+
+	{ 6,	1,	11,	"AMD-Athlon", },/* trial and error */
+	{ 6,	2,	11,	"AMD-Athlon", },/* trial and error */
+
+	{ 4,	-1,	22,	"Am486", },	/* guesswork */
+	{ 5,	-1,	23,	"AMD-K5/K6", },	/* guesswork */
+	{ 6,	-1,	11,	"AMD-Athlon", },/* guesswork */
+	{ 0xF,	-1,	11,	"AMD64", },	/* guesswork */
+
+	{ -1,	-1,	11,	"unknown", },	/* total default */
+};
+
+static X86type	*cputype;
+
+
+void
+delay(int millisecs)
+{
+	millisecs *= loopconst;
+	if(millisecs <= 0)
+		millisecs = 1;
+	aamloop(millisecs);
+}
+
+void
+microdelay(int microsecs)
+{
+	microsecs *= loopconst;
+	microsecs /= 1000;
+	if(microsecs <= 0)
+		microsecs = 1;
+	aamloop(microsecs);
+}
+
+extern void cpuid(char*, int*, int*);
+
+X86type*
+cpuidentify(void)
+{
+	int family, model;
+	X86type *t;
+	char cpuidid[16];
+	int cpuidax, cpuiddx;
+
+	cpuid(cpuidid, &cpuidax, &cpuiddx);
+	if(strncmp(cpuidid, "AuthenticAMD", 12) == 0 ||
+	   strncmp(cpuidid, "Geode by NSC", 12) == 0)
+		t = x86amd;
+	else
+		t = x86intel;
+	family = X86FAMILY(cpuidax);
+	model = X86MODEL(cpuidax);
+	if (0)
+		print("cpuidentify: cpuidax 0x%ux cpuiddx 0x%ux\n",
+			cpuidax, cpuiddx);
+	while(t->name){
+		if((t->family == family && t->model == model)
+		|| (t->family == family && t->model == -1)
+		|| (t->family == -1))
+			break;
+		t++;
+	}
+	if(t->name == nil)
+		panic("cpuidentify");
+
+	if(cpuiddx & 0x10){
+		havetsc = 1;
+		if(cpuiddx & 0x20)
+			wrmsr(0x10, 0);
+	}
+
+	return t;
+}
+
+void
+clockinit(void)
+{
+	uvlong a, b, cpufreq;
+	int loops, incr, x, y;
+	X86type *t;
+
+	/*
+	 *  set vector for clock interrupts
+	 */
+	setvec(VectorCLOCK, clockintr, 0);
+
+	t = cpuidentify();
+
+	/*
+	 *  set clock for 1/HZ seconds
+	 */
+	outb(Tmode, Load0|Square);
+	outb(T0cntr, (Freq/HZ));	/* low byte */
+	outb(T0cntr, (Freq/HZ)>>8);	/* high byte */
+
+	/*
+	 * Introduce a little delay to make sure the count is
+	 * latched and the timer is counting down; with a fast
+	 * enough processor this may not be the case.
+	 * The i8254 (which this probably is) has a read-back
+	 * command which can be used to make sure the counting
+	 * register has been written into the counting element.
+	 */
+	x = (Freq/HZ);
+	for(loops = 0; loops < 100000 && x >= (Freq/HZ); loops++){
+		outb(Tmode, Latch0);
+		x = inb(T0cntr);
+		x |= inb(T0cntr)<<8;
+	}
+
+	/* find biggest loop that doesn't wrap */
+	incr = 16000000/(t->aalcycles*HZ*2);
+	x = 2000;
+	for(loops = incr; loops < 64*1024; loops += incr) {
+	
+		/*
+		 *  measure time for the loop
+		 *
+		 *			MOVL	loops,CX
+		 *	aaml1:	 	AAM
+		 *			LOOP	aaml1
+		 *
+		 *  the time for the loop should be independent of external
+		 *  cache and memory system since it fits in the execution
+		 *  prefetch buffer.
+		 *
+		 */
+		outb(Tmode, Latch0);
+		if(havetsc)
+			_cycles(&a);
+		x = inb(T0cntr);
+		x |= inb(T0cntr)<<8;
+		aamloop(loops);
+		outb(Tmode, Latch0);
+		if(havetsc)
+			_cycles(&b);
+		y = inb(T0cntr);
+		y |= inb(T0cntr)<<8;
+		x -= y;
+	
+		if(x < 0)
+			x += Freq/HZ;
+
+		if(x > Freq/(3*HZ))
+			break;
+	}
+
+	/*
+ 	 *  figure out clock frequency and a loop multiplier for delay().
+	 *  counter  goes at twice the frequency, once per transition,
+	 *  i.e., twice per square wave
+	 */
+	cpufreq = (vlong)loops*((t->aalcycles*2*Freq)/x);
+	loopconst = (cpufreq/1000)/t->aalcycles;	/* AAM+LOOP's for 1 ms */
+
+	if(havetsc){
+		/* counter goes up by 2*Freq */
+		b = (b-a)<<1;
+		b *= Freq;
+		b /= x;
+
+		/*
+		 *  round to the nearest megahz
+		 */
+		cpumhz = (b+500000)/1000000L;
+		cpuhz = b;
+	}
+	else{
+		/*
+		 *  add in possible .5% error and convert to MHz
+		 */
+		cpumhz = (cpufreq + cpufreq/200)/1000000;
+		cpuhz = cpufreq;
+	}
+
+	if(debug){
+		int timeo;
+
+		print("%dMHz %s loop %d\n", cpumhz, t->name, loopconst);
+		print("tick...");
+		for(timeo = 0; timeo < 10; timeo++)
+			delay(1000);
+		print("tock...\n");
+	}
+}
--- /dev/null
+++ b/os/boot/pc/conf.c
@@ -1,0 +1,537 @@
+#include "u.h"
+#include "lib.h"
+#include "mem.h"
+#include "dat.h"
+#include "fns.h"
+#include "io.h"
+
+#include "fs.h"
+
+/*
+ * Where configuration info is left for the loaded programme.
+ * This will turn into a structure as more is done by the boot loader
+ * (e.g. why parse the .ini file twice?).
+ * There are 3584 bytes available at CONFADDR.
+ *
+ * The low-level boot routines in l.s leave data for us at CONFADDR,
+ * which we pick up before reading the plan9.ini file.
+ */
+#define BOOTLINELEN	64
+#define BOOTARGS	((char*)(CONFADDR+BOOTLINELEN))
+#define	BOOTARGSLEN	(3584-0x200-BOOTLINELEN)
+#define	MAXCONF		100
+
+static char *confname[MAXCONF];
+static char *confval[MAXCONF];
+static int nconf;
+
+extern char **ini;
+
+typedef struct {
+	char*	name;
+	int	start;
+	int	end;
+} Mblock;
+
+typedef struct {
+	char*	tag;
+	Mblock*	mb;
+} Mitem;
+
+static Mblock mblock[MAXCONF];
+static int nmblock;
+static Mitem mitem[MAXCONF];
+static int nmitem;
+static char* mdefault;
+static char mdefaultbuf[10];
+static int mtimeout;
+
+static char*
+comma(char* line, char** residue)
+{
+	char *q, *r;
+
+	if((q = strchr(line, ',')) != nil){
+		*q++ = 0;
+		if(*q == ' ')
+			q++;
+	}
+	*residue = q;
+
+	if((r = strchr(line, ' ')) != nil)
+		*r = 0;
+
+	if(*line == ' ')
+		line++;
+	return line;
+}
+
+static Mblock*
+findblock(char* name, char** residue)
+{
+	int i;
+	char *p;
+
+	p = comma(name, residue);
+	for(i = 0; i < nmblock; i++){
+		if(strcmp(p, mblock[i].name) == 0)
+			return &mblock[i];
+	}
+	return nil;
+}
+
+static Mitem*
+finditem(char* name, char** residue)
+{
+	int i;
+	char *p;
+
+	p = comma(name, residue);
+	for(i = 0; i < nmitem; i++){
+		if(strcmp(p, mitem[i].mb->name) == 0)
+			return &mitem[i];
+	}
+	return nil;
+}
+
+static void
+parsemenu(char* str, char* scratch, int len)
+{
+	Mitem *mi;
+	Mblock *mb, *menu;
+	char buf[20], *p, *q, *line[MAXCONF];
+	int i, inblock, n, show;
+
+	inblock = 0;
+	menu = nil;
+	memmove(scratch, str, len);
+	n = getfields(scratch, line, MAXCONF, '\n');
+	if(n >= MAXCONF)
+		print("warning: possibly too many lines in plan9.ini\n");
+	for(i = 0; i < n; i++){
+		p = line[i];
+		if(inblock && *p == '['){
+			mblock[nmblock].end = i;
+			if(strcmp(mblock[nmblock].name, "menu") == 0)
+				menu = &mblock[nmblock];
+			nmblock++;
+			inblock = 0;
+		}
+		if(*p == '['){
+			if(nmblock == 0 && i != 0){
+				mblock[nmblock].name = "common";
+				mblock[nmblock].start = 0;
+				mblock[nmblock].end = i;
+				nmblock++;
+			}
+			q = strchr(p+1, ']');
+			if(q == nil || *(q+1) != 0){
+				print("malformed menu block header - %s\n", p);
+				return;
+			}
+			*q = 0;
+			mblock[nmblock].name = p+1;
+			mblock[nmblock].start = i+1;
+			inblock = 1;
+		}
+	}
+
+	if(inblock){
+		mblock[nmblock].end = i;
+		nmblock++;
+	}
+	if(menu == nil)
+		return;
+	if(nmblock < 2){
+		print("incomplete menu specification\n");
+		return;
+	}
+
+	for(i = menu->start; i < menu->end; i++){
+		p = line[i];
+		if(cistrncmp(p, "menuitem=", 9) == 0){
+			p += 9;
+			if((mb = findblock(p, &q)) == nil){
+				print("no block for menuitem %s\n", p);
+				return;
+			}
+			if(q != nil)
+				mitem[nmitem].tag = q;
+			else
+				mitem[nmitem].tag = mb->name;
+			mitem[nmitem].mb = mb;
+			nmitem++;
+		}
+		else if(cistrncmp(p, "menudefault=", 12) == 0){
+			p += 12;
+			if((mi = finditem(p, &q)) == nil){
+				print("no item for menudefault %s\n", p);
+				return;
+			}
+			if(q != nil)
+				mtimeout = strtol(q, 0, 0);
+			sprint(mdefaultbuf, "%ld", mi-mitem+1);
+			mdefault = mdefaultbuf;
+		}
+		else if(cistrncmp(p, "menuconsole=", 12) == 0){
+			p += 12;
+			p = comma(p, &q);
+			consinit(p, q);
+		}
+		else{
+			print("invalid line in [menu] block - %s\n", p);
+			return;
+		}
+	}
+
+again:
+	print("\nPlan 9 Startup Menu:\n====================\n");
+	for(i = 0; i < nmitem; i++)
+		print("    %d. %s\n", i+1, mitem[i].tag);
+	for(;;){
+		getstr("Selection", buf, sizeof(buf), mdefault, mtimeout);
+		mtimeout = 0;
+		i = strtol(buf, &p, 0)-1;
+		if(i < 0 || i >= nmitem)
+			goto again;
+		switch(*p){
+		case 'p':
+		case 'P':
+			show = 1;
+			print("\n");
+			break;
+		case 0:
+			show = 0;
+			break;
+		default:
+			continue;
+			
+		}
+		mi = &mitem[i];
+	
+		p = str;
+		p += sprint(p, "menuitem=%s\n", mi->mb->name);
+		for(i = 0; i < nmblock; i++){
+			mb = &mblock[i];
+			if(mi->mb != mb && cistrcmp(mb->name, "common") != 0)
+				continue;
+			for(n = mb->start; n < mb->end; n++)
+				p += sprint(p, "%s\n", line[n]);
+		}
+
+		if(show){
+			for(q = str; q < p; q += i){
+				if((i = print(q)) <= 0)
+					break;
+			}
+			goto again;
+		}
+		break;
+	}
+	print("\n");
+}
+
+/*
+static void
+msleep(int msec)
+{
+	ulong start;
+
+	for(start = m->ticks; TK2MS(m->ticks - start) < msec; )
+		;
+}
+*/
+
+void
+readlsconf(void)
+{
+	uchar *p;
+
+	p = (uchar*)CONFADDR;
+	for(;;) {
+		if(strcmp((char*)p, "APM") == 0){
+			apm.haveinfo = 1;
+			apm.ax = *(ushort*)(p+4);
+			apm.cx = *(ushort*)(p+6);
+			apm.dx = *(ushort*)(p+8);
+			apm.di = *(ushort*)(p+10);
+			apm.ebx = *(ulong*)(p+12);
+			apm.esi = *(ulong*)(p+16);
+			print("apm ax=%x cx=%x dx=%x di=%x ebx=%x esi=%x\n",
+				apm.ax, apm.cx, apm.dx, apm.di, apm.ebx, apm.esi);
+			p += 20;
+			continue;
+		}
+		break;
+	}
+}
+
+char*
+getconf(char *name)
+{
+	int i, n, nmatch;
+	char buf[20];
+
+	nmatch = 0;
+	for(i = 0; i < nconf; i++)
+		if(cistrcmp(confname[i], name) == 0)
+			nmatch++;
+
+	switch(nmatch) {
+	default:
+		print("\n");
+		nmatch = 0;
+		for(i = 0; i < nconf; i++)
+			if(cistrcmp(confname[i], name) == 0)
+				print("%d. %s\n", ++nmatch, confval[i]);
+		print("%d. none of the above\n", ++nmatch);
+		do {
+			getstr(name, buf, sizeof(buf), nil, 0);
+			n = atoi(buf);
+		} while(n < 1 || n > nmatch);
+
+		for(i = 0; i < nconf; i++)
+			if(cistrcmp(confname[i], name) == 0)
+				if(--n == 0)
+					return confval[i];
+		break;
+
+	case 1:
+		for(i = 0; i < nconf; i++)
+			if(cistrcmp(confname[i], name) == 0)
+				return confval[i];
+		break;
+
+	case 0:
+		break;
+	}
+	return nil;
+}
+
+void
+addconf(char *fmt, ...)
+{
+	va_list arg;
+
+	va_start(arg, fmt);
+	vseprint(BOOTARGS+strlen(BOOTARGS), BOOTARGS+BOOTARGSLEN, fmt, arg);
+	va_end(arg);
+}
+
+void
+changeconf(char *fmt, ...)
+{
+	va_list arg;
+	char *p, *q, pref[20], buf[128];
+
+	va_start(arg, fmt);
+	vseprint(buf, buf+sizeof buf, fmt, arg);
+	va_end(arg);
+	strncpy(pref+1, buf, 19);
+	pref[19] = '\0';
+	if(p = strchr(pref, '='))
+		*(p+1) = '\0';
+	else
+		print("warning: did not change %s in plan9.ini\n", buf);
+
+	/* find old line by looking for \nwhat= */
+	pref[0] = '\n';
+	if(strncmp(BOOTARGS, pref+1, strlen(pref+1)) == 0)
+		p = BOOTARGS;
+	else if(p = strstr(BOOTARGS, pref))
+		p++;
+	else
+		p = nil;
+
+	/* move rest of args up, deleting what= line. */
+	if(p != nil && (q = strchr(p, '\n')) != nil)
+		memmove(p, q+1, strlen(q+1)+1);
+
+	/* add replacement to end */
+	addconf("%s", buf);
+}
+
+/*
+ *  read configuration file
+ */
+static char inibuf[BOOTARGSLEN];
+static char id[8] = "ZORT 0\r\n";
+
+int
+dotini(Fs *fs)
+{
+	File rc;
+	int blankline, i, incomment, inspace, n;
+	char *cp, *p, *q, *line[MAXCONF];
+
+	if(fswalk(fs, *ini, &rc) <= 0)
+		return -1;
+
+	cp = inibuf;
+	*cp = 0;
+	n = fsread(&rc, cp, BOOTARGSLEN-1);
+	if(n <= 0)
+		return -1;
+
+	cp[n] = 0;
+
+	/*
+	 * Strip out '\r', change '\t' -> ' '.
+	 * Change runs of spaces into single spaces.
+	 * Strip out trailing spaces, blank lines.
+	 *
+	 * We do this before we make the copy so that if we 
+	 * need to change the copy, it is already fairly clean.
+	 * The main need is in the case when plan9.ini has been
+	 * padded with lots of trailing spaces, as is the case 
+	 * for those created during a distribution install.
+	 */
+	p = cp;
+	blankline = 1;
+	incomment = inspace = 0;
+	for(q = cp; *q; q++){
+		if(*q == '\r')
+			continue;
+		if(*q == '\t')
+			*q = ' ';
+		if(*q == ' '){
+			inspace = 1;
+			continue;
+		}
+		if(*q == '\n'){
+			if(!blankline){
+				if(!incomment)
+					*p++ = '\n';
+				blankline = 1;
+			}
+			incomment = inspace = 0;
+			continue;
+		}
+		if(inspace){
+			if(!blankline && !incomment)
+				*p++ = ' ';
+			inspace = 0;
+		}
+		if(blankline && *q == '#')
+			incomment = 1;
+		blankline = 0;
+		if(!incomment)
+			*p++ = *q;	
+	}
+	if(p > cp && p[-1] != '\n')
+		*p++ = '\n';
+	*p++ = 0;
+	n = p-cp;
+
+	parsemenu(cp, BOOTARGS, n);
+
+	/*
+	 * Keep a copy.
+	 * We could change this to pass the parsed strings
+	 * to the booted programme instead of the raw
+	 * string, then it only gets done once.
+	 */
+	if(strncmp(cp, id, sizeof(id))){
+		memmove(BOOTARGS, id, sizeof(id));
+		if(n+1+sizeof(id) >= BOOTARGSLEN)
+			n -= sizeof(id);
+		memmove(BOOTARGS+sizeof(id), cp, n+1);
+	}
+	else
+		memmove(BOOTARGS, cp, n+1);
+
+	n = getfields(cp, line, MAXCONF, '\n');
+	for(i = 0; i < n; i++){
+		cp = strchr(line[i], '=');
+		if(cp == 0)
+			continue;
+		*cp++ = 0;
+		if(cp - line[i] >= NAMELEN+1)
+			*(line[i]+NAMELEN-1) = 0;
+		confname[nconf] = line[i];
+		confval[nconf] = cp;
+		nconf++;
+	}
+	return 0;
+}
+
+static int
+parseether(uchar *to, char *from)
+{
+	char nip[4];
+	char *p;
+	int i;
+
+	p = from;
+	while(*p == ' ')
+		++p;
+	for(i = 0; i < 6; i++){
+		if(*p == 0)
+			return -1;
+		nip[0] = *p++;
+		if(*p == 0)
+			return -1;
+		nip[1] = *p++;
+		nip[2] = 0;
+		to[i] = strtoul(nip, 0, 16);
+		if(*p == ':')
+			p++;
+	}
+	return 0;
+}
+
+int
+isaconfig(char *class, int ctlrno, ISAConf *isa)
+{
+	char cc[NAMELEN], *p, *q, *r;
+	int n;
+
+	sprint(cc, "%s%d", class, ctlrno);
+	for(n = 0; n < nconf; n++){
+		if(cistrncmp(confname[n], cc, NAMELEN))
+			continue;
+		isa->nopt = 0;
+		p = confval[n];
+		while(*p){
+			while(*p == ' ' || *p == '\t')
+				p++;
+			if(*p == '\0')
+				break;
+			if(cistrncmp(p, "type=", 5) == 0){
+				p += 5;
+				for(q = isa->type; q < &isa->type[NAMELEN-1]; q++){
+					if(*p == '\0' || *p == ' ' || *p == '\t')
+						break;
+					*q = *p++;
+				}
+				*q = '\0';
+			}
+			else if(cistrncmp(p, "port=", 5) == 0)
+				isa->port = strtoul(p+5, &p, 0);
+			else if(cistrncmp(p, "irq=", 4) == 0)
+				isa->irq = strtoul(p+4, &p, 0);
+			else if(cistrncmp(p, "mem=", 4) == 0)
+				isa->mem = strtoul(p+4, &p, 0);
+			else if(cistrncmp(p, "size=", 5) == 0)
+				isa->size = strtoul(p+5, &p, 0);
+			else if(cistrncmp(p, "ea=", 3) == 0){
+				if(parseether(isa->ea, p+3) == -1)
+					memset(isa->ea, 0, 6);
+			}
+			else if(isa->nopt < NISAOPT){
+				r = isa->opt[isa->nopt];
+				while(*p && *p != ' ' && *p != '\t'){
+					*r++ = *p++;
+					if(r-isa->opt[isa->nopt] >= ISAOPTLEN-1)
+						break;
+				}
+				*r = '\0';
+				isa->nopt++;
+			}
+			while(*p && *p != ' ' && *p != '\t')
+				p++;
+		}
+		return 1;
+	}
+	return 0;
+}
--- /dev/null
+++ b/os/boot/pc/console.c
@@ -1,0 +1,236 @@
+#include "u.h"
+#include "lib.h"
+#include "mem.h"
+#include "dat.h"
+#include "fns.h"
+#include "io.h"
+
+IOQ consiq;
+IOQ consoq;
+
+static int useuart;
+
+int	debug = 0;
+
+void
+kbdchar(int c)
+{
+	c &= 0x7F;
+	if(c == 0x10)
+		warp86("\n^P\n", 0);
+	if(c == 0x12)
+		debug = !debug;
+	consiq.putc(&consiq, c);
+}
+
+static int
+consputc(void)
+{
+	return consoq.getc(&consoq);
+}
+
+void
+kbdinit(void)
+{
+	i8042init();
+	qinit(&consiq);
+}
+
+void
+consinit(char* name, char* speed)
+{
+	int baud, port;
+
+	if(name == nil || cistrcmp(name, "cga") == 0)
+		return;
+	port = strtoul(name, 0, 0);
+	if(port < 0 || port > 1)
+		return;
+	if(speed == nil || (baud = strtoul(speed, 0, 0)) == 0)
+		baud = 9600;
+
+	qinit(&consoq);
+
+	uartspecial(port, kbdchar, consputc, baud);
+	useuart = 1;
+	uartputs(&consoq, "\n", 1);
+}
+
+void
+consdrain(void)
+{
+	if(useuart)
+		uartdrain();
+}
+
+void
+consputs(char* s, int n)
+{
+	cgascreenputs(s, n);
+	if(useuart)
+		uartputs(&consoq, s, n);
+}
+
+void
+warp86(char* s, ulong)
+{
+	if(s != nil)
+		print(s);
+	spllo();
+	consdrain();
+
+	i8042reset();
+	print("Takes a licking and keeps on ticking...\n");
+	for(;;)
+		idle();
+}
+
+static int
+getline(char *buf, int size, int timeout)
+{
+	int c, i=0;
+	ulong start;
+	char echo;
+
+	for (;;) {
+		start = m->ticks;
+		do{
+			/* timeout seconds to first char */
+			if(timeout && ((m->ticks - start) > timeout*HZ))
+				return -2;
+			c = consiq.getc(&consiq);
+		}while(c == -1);
+		timeout = 0;
+
+		if(c == '\r')
+			c = '\n'; 		/* turn carriage return into newline */
+		if(c == '\177')
+			c = '\010';		/* turn delete into backspace */
+		if(c == '\025')
+			echo = '\n';		/* echo ^U as a newline */
+		else
+			echo = c;
+		consputs(&echo, 1);
+
+		if(c == '\010'){
+			if(i > 0)
+				i--; /* bs deletes last character */
+			continue;
+		}
+		/* a newline ends a line */
+		if (c == '\n')
+			break;
+		/* ^U wipes out the line */
+		if (c =='\025')
+			return -1;
+		if(i == size)
+			return size;
+		buf[i++] = c;
+	}
+	buf[i] = 0;
+	return i;
+}
+
+int
+getstr(char *prompt, char *buf, int size, char *def, int timeout)
+{
+	int len, isdefault;
+	char pbuf[PRINTSIZE];
+
+	buf[0] = 0;
+	isdefault = (def && *def);
+	if(isdefault == 0){
+		timeout = 0;
+		sprint(pbuf, "%s: ", prompt);
+	}
+	else if(timeout)
+		sprint(pbuf, "%s[default==%s (%ds timeout)]: ", prompt, def, timeout);
+	else
+		sprint(pbuf, "%s[default==%s]: ", prompt, def);
+	for (;;) {
+		print(pbuf);
+		len = getline(buf, size, timeout);
+		switch(len){
+		case 0:
+			/* RETURN */
+			if(isdefault)
+				break;
+			continue;
+		case -1:
+			/* ^U typed */
+			continue;
+		case -2:
+			/* timeout, use default */
+			consputs("\n", 1);
+			len = 0;
+			break;
+		default:
+			break;
+		}
+		if(len >= size){
+			print("line too long\n");
+			continue;
+		}
+		break;
+	}
+	if(len == 0 && isdefault)
+		strcpy(buf, def);
+	return 0;
+}
+
+int
+print(char *fmt, ...)
+{
+	int n;
+	va_list arg;
+	char buf[PRINTSIZE];
+
+	va_start(arg, fmt);
+	n = vseprint(buf, buf+sizeof(buf), fmt, arg) - buf;
+	va_end(arg);
+	consputs(buf, n);
+
+	return n;
+}
+
+int
+sprint(char *s, char *fmt, ...)
+{
+	int n;
+	va_list arg;
+
+	va_start(arg, fmt);
+	n = vseprint(s, s+PRINTSIZE, fmt, arg) - s;
+	va_end(arg);
+
+	return n;
+}
+
+void
+panic(char *fmt, ...)
+{
+	int n;
+	va_list arg;
+	char buf[PRINTSIZE];
+
+	strcpy(buf, "panic: ");
+	va_start(arg, fmt);
+	n = vseprint(buf+7, buf+sizeof(buf), fmt, arg) - buf;
+	va_end(arg);
+	buf[n] = '\n';
+	consputs(buf, n+1);
+
+//floppymemwrite();
+//splhi(); for(;;);
+	if(etherdetach)
+		etherdetach();
+	if(sddetach)
+		sddetach();
+
+	consputs("\nPress almost any key to reset...", 32);
+	spllo();
+	while(consiq.getc(&consiq) == -1)
+		;
+
+	warp86(nil, 0);
+}
--- /dev/null
+++ b/os/boot/pc/dat.h
@@ -1,0 +1,215 @@
+typedef struct List {
+	void	*next;
+} List;
+
+typedef struct Alarm Alarm;
+typedef struct Alarm {
+	List;
+	int	busy;
+	long	dt;
+	void	(*f)(Alarm*);
+	void	*arg;
+} Alarm;
+
+typedef struct Apminfo {
+	int haveinfo;
+	int ax;
+	int cx;
+	int dx;
+	int di;
+	int ebx;
+	int esi;
+} Apminfo;
+
+typedef struct Block Block;
+struct Block {
+	Block*	next;
+	uchar*	rp;			/* first unconsumed byte */
+	uchar*	wp;			/* first empty byte */
+	uchar*	lim;			/* 1 past the end of the buffer */
+	uchar*	base;			/* start of the buffer */
+	ulong	flag;
+};
+#define BLEN(s)	((s)->wp - (s)->rp)
+
+typedef struct IOQ IOQ;
+typedef struct IOQ {
+	uchar	buf[4096];
+	uchar	*in;
+	uchar	*out;
+	int	state;
+	int	(*getc)(IOQ*);
+	int	(*putc)(IOQ*, int);
+	void	*ptr;
+};
+
+enum {
+	Eaddrlen	= 6,
+	/* next two exclude 4-byte ether CRC */
+	ETHERMINTU	= 60,		/* minimum transmit size */
+	ETHERMAXTU	= 1514,		/* maximum transmit size */
+	ETHERHDRSIZE	= 14,		/* size of an ethernet header */
+
+	MaxEther	= 6,
+};
+
+typedef struct {
+	uchar	d[Eaddrlen];
+	uchar	s[Eaddrlen];
+	uchar	type[2];
+	uchar	data[1500];
+	uchar	crc[4];
+} Etherpkt;
+
+extern uchar broadcast[Eaddrlen];
+
+typedef struct Ureg Ureg;
+#pragma incomplete Ureg
+
+typedef struct Segdesc {
+	ulong	d0;
+	ulong	d1;
+} Segdesc;
+
+typedef struct Mach {
+	ulong	ticks;			/* of the clock since boot time */
+	void	*alarm;			/* alarms bound to this clock */
+} Mach;
+
+extern Mach *m;
+
+#define I_MAGIC		((((4*11)+0)*11)+7)
+
+typedef struct Exec Exec;
+struct	Exec
+{
+	uchar	magic[4];		/* magic number */
+	uchar	text[4];	 	/* size of text segment */
+	uchar	data[4];	 	/* size of initialized data */
+	uchar	bss[4];	  		/* size of uninitialized data */
+	uchar	syms[4];	 	/* size of symbol table */
+	uchar	entry[4];	 	/* entry point */
+	uchar	spsz[4];		/* size of sp/pc offset table */
+	uchar	pcsz[4];		/* size of pc/line number table */
+};
+
+/*
+ *  a parsed .ini line
+ */
+#define ISAOPTLEN	32
+#define NISAOPT		8
+
+typedef struct  ISAConf {
+	char	type[NAMELEN];
+	ulong	port;
+	ulong	irq;
+	ulong	mem;
+	ulong	size;
+	uchar	ea[6];
+
+	int	nopt;
+	char	opt[NISAOPT][ISAOPTLEN];
+} ISAConf;
+
+typedef struct Pcidev Pcidev;
+typedef struct PCMmap PCMmap;
+typedef struct PCMslot PCMslot;
+
+#define BOOTLINE	((char*)CONFADDR)
+
+enum {
+	MB =		(1024*1024),
+};
+#define ROUND(s, sz)	(((s)+((sz)-1))&~((sz)-1))
+
+
+typedef struct Type Type;
+typedef struct Medium Medium;
+typedef struct Boot Boot;
+
+enum {					/* type */
+	Tnil		= 0x00,
+
+	Tfloppy		= 0x01,
+	Tsd		= 0x02,
+	Tether		= 0x03,
+	Tcd		= 0x04,
+	Tbios		= 0x05,
+
+	Tany		= -1,
+};
+
+enum {					/* name and flag */
+	Fnone		= 0x00,
+
+	Nfs		= 0x00,
+	Ffs		= (1<<Nfs),
+	Nboot		= 0x01,
+	Fboot		= (1<<Nboot),
+	Nbootp		= 0x02,
+	Fbootp		= (1<<Nbootp),
+	NName		= 3,
+
+	Fany		= Fbootp|Fboot|Ffs,
+
+	Fini		= 0x10,
+	Fprobe		= 0x80,
+};
+
+typedef struct Type {
+	int	type;
+	int	flag;
+	int	(*init)(void);
+	void	(*initdev)(int, char*);
+	void*	(*getfspart)(int, char*, int);	/* actually returns Dos* */
+	void	(*addconf)(int);
+	int	(*boot)(int, char*, Boot*);
+	void	(*printdevs)(int);
+	char**	parts;
+	char**	inis;
+	int	mask;
+	Medium*	media;
+} Type;
+
+extern void (*etherdetach)(void);
+extern void (*floppydetach)(void);
+extern void (*sddetach)(void);
+
+typedef struct Lock {	/* for ilock, iunlock */
+	int locked;
+	int spl;
+} Lock;
+
+enum {	/* returned by bootpass */
+	MORE, ENOUGH, FAIL
+};
+enum {
+	INITKERNEL,
+	READEXEC,
+	READ9TEXT,
+	READ9DATA,
+	READGZIP,
+	READEHDR,
+	READPHDR,
+	READEPAD,
+	READEDATA,
+	TRYBOOT,
+	INIT9LOAD,
+	READ9LOAD,
+	FAILED
+};
+
+struct Boot {
+	int state;
+
+	Exec exec;
+	char *bp;	/* base ptr */
+	char *wp;	/* write ptr */
+	char *ep;	/* end ptr */
+};
+
+extern int	debug;
+extern Apminfo	apm;
+extern char	*defaultpartition;
+extern int	iniread;
+extern int	pxe;
--- /dev/null
+++ b/os/boot/pc/devbios.c
@@ -1,0 +1,428 @@
+/*
+ * boot driver for BIOS devices
+ */
+#include <u.h>
+#include "lib.h"
+#include "mem.h"
+#include "dat.h"
+#include "fns.h"
+#include "io.h"
+#include "ureg.h"
+#include "fs.h"
+
+typedef uvlong Devbytes, Devsects;
+
+typedef struct Biosdrive Biosdrive;	/* 1 drive -> ndevs */
+typedef struct Biosdev Biosdev;
+
+enum {
+	Debug = 0,
+	Maxdevs = 4,
+
+	CF = 1,
+	Flopid = 0,			/* first floppy */
+	Baseid = 0x80,			/* first disk */
+
+	/* bios calls: int 13 disk services */
+	Biosinit	= 0,		/* initialise disk & floppy ctlrs */
+	Biosdrvsts,
+	Bioschsrdsects,
+	Biosdrvparam	= 8,
+	Biosctlrinit,
+	Biosreset	=  0xd,		/* reset disk */
+	Biosdrvrdy	= 0x10,
+	Biosdrvtype	= 0x15,
+	Biosckext	= 0x41,
+	Biosrdsect,
+	Biosedrvparam	= 0x48,
+
+	/* disk types */
+	Typenone = 0,
+	Typedisk = 3,
+};
+
+struct Biosdrive {
+	int	ndevs;
+};
+struct Biosdev {
+	Devbytes size;
+	Devbytes offset;
+	uchar	id;			/* drive number; e.g., 0x80 */
+	char	type;
+	ushort	sectsz;
+};
+
+typedef struct Extread {
+	uchar	size;
+	uchar	unused1;
+	uchar	nsects;
+	uchar	unused2;
+	ulong	addr;		/* segment:offset */
+	uvlong	stsect;		/* starting sector */
+} Extread;
+typedef struct Edrvparam {
+	/* from edd 1.1 spec */
+	ushort	size;			/* max. buffer size */
+	ushort	flags;
+	ulong	physcyls;
+	ulong	physheads;
+	ulong	phystracksects;
+	uvlong	physsects;
+	ushort	sectsz;
+	void	*dpte;			/* ~0ull: invalid */
+
+	/* remainder from edd 3.0 spec */
+	ushort	key;			/* 0xbedd if present */
+	uchar	dpilen;
+	uchar	unused1;
+	ushort	unused2;
+	char	bustype[4];		/* "PCI" or "ISA" */
+	char	ifctype[8]; /* "ATA", "ATAPI", "SCSI", "USB", "1394", "FIBRE" */
+	uvlong	ifcpath;
+	uvlong	devpath;
+	uchar	unused3;
+	uchar	dpicksum;
+} Edrvparam;
+
+void	realmode(int intr, Ureg *ureg);		/* from trap.c */
+
+int onlybios0;
+int biosinited;
+
+static Biosdev bdev[Maxdevs];
+static Biosdrive bdrive;
+static Ureg regs;
+
+static int	dreset(uchar drive);
+static Devbytes	extgetsize(Biosdev *);
+static Devsects	getsize(uchar drive, char *type);
+static int	islba(uchar drive);
+
+static int
+biosdiskcall(Ureg *rp, uchar op, ulong bx, ulong dx, ulong si)
+{
+	memset(rp, 0, sizeof *rp);
+	rp->ax = op << 8;
+	rp->bx = bx;
+	rp->dx = dx;			/* often drive id */
+	rp->si = si;
+	/* pass command in *rp, get results from there */
+	realmode(0x13, rp);
+	if (rp->flags & CF) {
+//		print("biosdiskcall: int 13 op 0x%ux drive 0x%lux failed, "
+//			"ah error code 0x%ux\n", op, dx, (uchar)(rp->ax >> 8));
+		return -1;
+	}
+	return 0;
+}
+
+/*
+ * Find out what the bios knows about devices.
+ * our boot device could be usb; ghod only knows where it will appear.
+ */
+int
+biosinit(void)
+{
+	int devid, lba, mask, lastbit;
+	Devbytes size;
+	char type;
+	Biosdev *bdp;
+	static int beenhere;
+
+	mask = lastbit = 0;
+	if (beenhere)
+		return mask;
+	beenhere = 1;
+	/* 9pxeload can't use bios int 13 calls; they wedge the machine */
+	if (pxe || getconf("*nobiosload") != nil || onlybios0 || !biosinited)
+		return mask;
+	for (devid = 0; devid < (1 << 8) && bdrive.ndevs < Maxdevs; devid++) {
+		lba = islba(devid);
+		if(!lba /* || devid != Baseid && dreset(devid) < 0 */ )
+			continue;
+		type = Typedisk;		/* HACK */
+		if (getsize(devid, &type) == 0) { /* no device, end of range */
+			devid &= ~0xf;
+			devid += 0x10;
+			devid--;
+			continue;
+		}
+		lastbit = 1 << bdrive.ndevs;
+		mask |= lastbit;
+		bdp = &bdev[bdrive.ndevs];
+		bdp->id = devid;
+		bdp->type = type;
+		size = extgetsize(bdp);
+		bdp->size = size;
+		print("bios%d: drive 0x%ux: %llud bytes, type %d\n",
+			bdrive.ndevs, devid, size, type);
+		bdrive.ndevs++;
+	}
+	/*
+	 * bioses seem to only be able to read from drive number 0x80
+	 * and certainly can't read from the highest drive number when we
+	 * call them, even if there is only one.  attempting to read from
+	 * the last drive number yields a hung machine or a two-minute pause.
+	 */
+	if (bdrive.ndevs > 0) {
+		if (bdrive.ndevs == 1) {
+			print("biosinit: sorry, only one bios drive; "
+				"can't read last one\n");
+			onlybios0 = 1;
+		} else
+			biosinited = 1;
+		bdrive.ndevs--;	/* omit last drive number; it can't be read */
+		mask &= ~lastbit;
+	}
+	return mask;
+}
+
+void
+biosinitdev(int i, char *name)
+{
+	if(i >= bdrive.ndevs)
+		panic("biosinitdev");
+	sprint(name, "bios%d", i);
+}
+
+void
+biosprintdevs(int i)
+{
+	if(i >= bdrive.ndevs){
+		print("got a print for %d, only got %d\n", i, bdrive.ndevs);
+		panic("biosprintdevs");
+	}
+	print(" bios%d", i);
+}
+
+int
+biosboot(int dev, char *file, Boot *b)
+{
+	Fs *fs;
+
+	if(strncmp(file, "dos!", 4) == 0)
+		file += 4;
+	if(strchr(file, '!') != nil || strcmp(file, "") == 0) {
+		print("syntax is bios0!file\n");
+		return -1;
+	}
+
+	fs = biosgetfspart(dev, "9fat", 1);
+	if(fs == nil)
+		return -1;
+	return fsboot(fs, file, b);
+}
+
+/* read n bytes at sector offset into a from drive id */
+long
+sectread(Biosdev *bdp, void *a, long n, Devsects offset)
+{
+	uchar *biosparam, *cp;
+	Extread *erp;
+
+	if(n < 0 || n > bdp->sectsz)
+		return -1;
+	if(Debug)
+		memset((uchar *)BIOSXCHG, 'r', bdp->sectsz); /* preclean the buffer. */
+
+	biosdiskcall(&regs, Biosdrvrdy, 0, bdp->id, 0);
+
+	/* space for a BIG sector, just in case... */
+	biosparam = (uchar *)BIOSXCHG + 2*1024;
+
+	/* read into BIOSXCHG */
+	erp = (Extread *)biosparam;
+	memset(erp, 0, sizeof *erp);
+	erp->size = sizeof *erp;
+	erp->nsects = 1;
+	erp->addr = PADDR(BIOSXCHG);
+	erp->stsect = offset;
+	if (biosdiskcall(&regs, Biosrdsect, 0, bdp->id, PADDR(erp)) < 0) {
+		print("sectread: bios failed to read %ld @ sector %lld of 0x%ux\n",
+			n, offset, bdp->id);
+		return -1;
+	}
+
+	/* copy into caller's buffer */
+	memmove(a, (char *)BIOSXCHG, n);
+	if(Debug){
+		cp = (uchar *)BIOSXCHG;
+		print("-%ux %ux %ux %ux--%16.16s-\n",
+			cp[0], cp[1], cp[2], cp[3], (char *)cp + 480);
+	}
+	return n;
+}
+
+/* not tested yet. */
+static int
+dreset(uchar drive)
+{
+if (0) {
+print("devbios: resetting disk controllers...");
+	biosdiskcall(&regs, Biosinit, 0, drive, 0);
+print("\n");
+}
+	return regs.ax? -1: 0;		/* ax!=0 on error */
+}
+
+static int
+islba(uchar drive)
+{
+	if (biosdiskcall(&regs, Biosckext, 0x55aa, drive, 0) < 0)
+		return 0;
+	if(regs.bx != 0xaa55){
+		print("islba: buggy bios\n");
+		return 0;
+	}
+	if (Debug)
+		print("islba: drive 0x%ux extensions version %d.%d cx 0x%lux\n",
+			drive, (uchar)(regs.ax >> 8),
+			(uchar)regs.ax, regs.cx); /* cx: 4=edd, 1=use dap */
+	return regs.cx & 1;		/* dap bit */
+}
+
+/*
+ * works so so... some floppies are 0x80+x when they shouldn't be,
+ * and report lba even if they cannot...
+ */
+static Devsects
+getsize(uchar id, char *typep)
+{
+	int dtype;
+
+	if (biosdiskcall(&regs, Biosdrvtype, 0x55aa, id, 0) < 0)
+		return 0;
+
+	dtype = (ushort)regs.ax >> 8;
+	if(dtype == Typenone){
+		print("no such device 0x%ux of type %d\n", id, dtype);
+		return 0;
+	}
+	if(dtype != Typedisk){
+		print("non-disk device 0x%ux of type %d\n", id, dtype);
+		return 0;
+	}
+	*typep = dtype;
+	return (ushort)regs.cx | regs.dx << 16;
+}
+
+/* extended get size */
+static Devbytes
+extgetsize(Biosdev *bdp)
+{
+	Edrvparam *edp;
+
+	edp = (Edrvparam *)BIOSXCHG;
+	memset(edp, 0, sizeof *edp);
+	edp->size = sizeof *edp;
+	edp->dpilen = 36;
+	if (biosdiskcall(&regs, Biosedrvparam, 0, bdp->id, PADDR(edp)) < 0)
+		return 0;
+	if(Debug) {
+		print("extgetsize: drive 0x%ux info flags 0x%ux",
+			bdp->id, edp->flags);
+		if (edp->key == 0xbedd)
+			print(" %s %s", edp->bustype, edp->ifctype);
+		print("\n");
+	}
+	if (edp->sectsz <= 0) {
+		print("extgetsize: drive 0x%ux: non-positive sector size\n",
+			bdp->id);
+		edp->sectsz = 1;		/* don't divide by zero */
+	}
+	bdp->sectsz = edp->sectsz;
+	return edp->physsects * edp->sectsz;
+}
+
+long
+biosread(Fs *fs, void *a, long n)
+{
+	int want, got, part;
+	long totnr, stuck;
+	Devbytes offset;
+	Biosdev *bdp;
+
+	if(fs->dev > bdrive.ndevs)
+		return -1;
+	if (n <= 0)
+		return n;
+	bdp = &bdev[fs->dev];
+	offset = bdp->offset;
+	stuck = 0;
+	for (totnr = 0; totnr < n && stuck < 4; totnr += got) {
+		want = bdp->sectsz;
+		if (totnr + want > n)
+			want = n - totnr;
+		if(Debug)
+			print("bios%d, read: %ld @ off %lld, want: %d, id: 0x%ux\n",
+				fs->dev, n, offset, want, bdp->id);
+		part = offset % bdp->sectsz;
+		if (part != 0) {	/* back up to start of sector */
+			offset -= part;
+			totnr  -= part;
+			if (totnr < 0) {
+				print("biosread: negative count %ld\n", totnr);
+				return -1;
+			}
+		}
+		if ((vlong)offset < 0) {
+			print("biosread: negative offset %lld\n", offset);
+			return -1;
+		}
+		got = sectread(bdp, (char *)a + totnr, want, offset/bdp->sectsz);
+		if(got <= 0){
+//			print("biosread: failed to read %ld @ off %lld of 0x%ux, "
+//				"want %d got %d\n",
+//				n, offset, bdp->id, want, got);
+			return -1;
+		}
+		offset += got;
+		bdp->offset = offset;
+		if (got < bdp->sectsz)
+			stuck++;	/* we'll have to re-read this sector */
+		else
+			stuck = 0;
+	}
+	return totnr;
+}
+
+vlong
+biosseek(Fs *fs, vlong off)
+{
+	if (off < 0) {
+		print("biosseek(fs, %lld) is illegal\n", off);
+		return -1;
+	}
+	if(fs->dev > bdrive.ndevs) {
+		print("biosseek: fs->dev %d > bdrive.ndevs %d\n",
+			fs->dev, bdrive.ndevs);
+		return -1;
+	}
+	bdev[fs->dev].offset = off;	/* do not know size... (yet) */
+	return off;
+}
+
+void *
+biosgetfspart(int i, char *name, int chatty)
+{
+	static Fs fs;
+
+	if(strcmp(name, "9fat") != 0){
+		if(chatty)
+			print("unknown partition bios%d!%s (use bios%d!9fat)\n",
+				i, name, i);
+		return nil;
+	}
+
+	fs.dev = i;
+	fs.diskread = biosread;
+	fs.diskseek = biosseek;
+
+	if(dosinit(&fs) < 0){
+		if(chatty)
+			print("bios%d!%s does not contain a FAT file system\n",
+				i, name);
+		return nil;
+	}
+	return &fs;
+}
--- /dev/null
+++ b/os/boot/pc/devbios.h
@@ -1,0 +1,22 @@
+typedef uvlong Devbytes, Devsects;
+
+typedef struct Biosdrive Biosdrive;	/* 1 drive -> ndevs */
+typedef struct Biosdev Biosdev;
+
+struct Biosdrive {
+	int	ndevs;
+};
+struct Biosdev {
+	Devbytes size;
+	Devbytes offset;
+	uchar	id;
+	char	type;
+};
+
+int	biosboot(int dev, char *file, Boot *b);
+void*	biosgetfspart(int i, char *name, int chatty);
+void	biosinitdev(int i, char *name);
+int	biosinit(void);
+void	biosprintbootdevs(int dev);
+void	biosprintdevs(int i);
+long	biosread(Fs *fs, void *a, long n);
--- /dev/null
+++ b/os/boot/pc/devfloppy.c
@@ -1,0 +1,853 @@
+#include <u.h>
+
+#include	"lib.h"
+#include	"mem.h"
+#include	"dat.h"
+#include	"fns.h"
+#include	"io.h"
+#include	"ureg.h"
+
+#include	"fs.h"
+#include	"devfloppy.h"
+
+
+/* Intel 82077A (8272A compatible) floppy controller */
+
+/* This module expects the following functions to be defined
+ * elsewhere: 
+ * 
+ * inb()
+ * outb()
+ * floppyexec()
+ * floppyeject() 
+ * floppysetup0()
+ * floppysetup1()
+ * dmainit()
+ * dmasetup()
+ * dmaend()
+ * 
+ * On DMA systems, floppyexec() should be an empty function; 
+ * on non-DMA systems, dmaend() should be an empty function; 
+ * dmasetup() may enforce maximum transfer sizes. 
+ */
+
+enum {
+	/* file types */
+	Qdir=		0, 
+	Qdata=		(1<<2),
+	Qctl=		(2<<2),
+	Qmask=		(3<<2),
+
+	DMAchan=	2,	/* floppy dma channel */
+};
+
+#define DPRINT if(0)print
+
+FType floppytype[] =
+{
+ { "3½HD",	T1440kb, 512, 18, 2, 1, 80, 0x1B, 0x54,	0, },
+ { "3½DD",	T1440kb, 512,  9, 2, 1, 80, 0x1B, 0x54, 2, },
+ { "3½DD",	T720kb,  512,  9, 2, 1, 80, 0x1B, 0x54, 2, },
+ { "5¼HD",	T1200kb, 512, 15, 2, 1, 80, 0x2A, 0x50, 0, },
+ { "5¼DD",	T1200kb, 512,  9, 2, 2, 40, 0x2A, 0x50, 1, },
+ { "ATT3B1",	T1200kb, 512,  8, 2, 2, 48, 0x2A, 0x50, 1, },
+ { "5¼DD",	T360kb,  512,  9, 2, 1, 40, 0x2A, 0x50, 2, },
+};
+
+/*
+ *  bytes per sector encoding for the controller.
+ *  - index for b2c is is (bytes per sector/128).
+ *  - index for c2b is code from b2c
+ */
+static int b2c[] =
+{
+[1]	0,
+[2]	1,
+[4]	2,
+[8]	3,
+};
+static int c2b[] =
+{
+	128,
+	256,
+	512,
+	1024,
+};
+
+FController	fl;
+
+#define MOTORBIT(i)	(1<<((i)+4))
+
+/*
+ *  predeclared
+ */
+static int	cmddone(void*);
+static void	floppyformat(FDrive*, char*);
+static void	floppykproc(void*);
+static void	floppypos(FDrive*,long);
+static int	floppyrecal(FDrive*);
+static int	floppyresult(void);
+static void	floppyrevive(void);
+static vlong	pcfloppyseek(FDrive*, vlong);
+static int	floppysense(void);
+static void	floppywait(int);
+static long	floppyxfer(FDrive*, int, void*, long, long);
+
+static void
+fldump(void)
+{
+	DPRINT("sra %ux srb %ux dor %ux msr %ux dir %ux\n", inb(Psra), inb(Psrb),
+		inb(Pdor), inb(Pmsr), inb(Pdir));
+}
+
+static void
+floppyalarm(Alarm* a)
+{
+	FDrive *dp;
+
+	for(dp = fl.d; dp < &fl.d[fl.ndrive]; dp++){
+		if((fl.motor&MOTORBIT(dp->dev)) && TK2SEC(m->ticks - dp->lasttouched) > 5)
+			floppyoff(dp);
+	}
+
+	alarm(5*1000, floppyalarm, 0);
+	cancel(a);
+}
+
+/*
+ *  set floppy drive to its default type
+ */
+static void
+floppysetdef(FDrive *dp)
+{
+	FType *t;
+
+	for(t = floppytype; t < &floppytype[nelem(floppytype)]; t++)
+		if(dp->dt == t->dt){
+			dp->t = t;
+			break;
+		}
+}
+
+static void
+_floppydetach(void)
+{
+	/*
+	 *  stop the motors
+	 */
+	fl.motor = 0;
+	delay(10);
+	outb(Pdor, fl.motor | Fintena | Fena);
+	delay(10);
+}
+
+int
+floppyinit(void)
+{
+	FDrive *dp;
+	FType *t;
+	ulong maxtsize;
+	int mask;
+
+	dmainit(DMAchan);
+
+	floppysetup0(&fl);
+
+	/*
+	 *  init dependent parameters
+	 */
+	maxtsize = 0;
+	for(t = floppytype; t < &floppytype[nelem(floppytype)]; t++){
+		t->cap = t->bytes * t->heads * t->sectors * t->tracks;
+		t->bcode = b2c[t->bytes/128];
+		t->tsize = t->bytes * t->sectors;
+		if(maxtsize < t->tsize)
+			maxtsize = t->tsize;
+	}
+
+	fl.selected = fl.d;
+
+	floppydetach = _floppydetach;
+	floppydetach();
+
+	/*
+	 *  init drives
+	 */
+	mask = 0;
+	for(dp = fl.d; dp < &fl.d[fl.ndrive]; dp++){
+		dp->dev = dp - fl.d;
+		if(dp->dt == Tnone)
+			continue;
+		mask |= 1<<dp->dev;
+		floppysetdef(dp);
+		dp->cyl = -1;			/* because we don't know */
+		dp->cache = (uchar*)xspanalloc(maxtsize, BY2PG, 64*1024);
+		dp->ccyl = -1;
+		dp->vers = 0;
+		dp->maxtries = 5;
+	}
+
+	/*
+	 *  first operation will recalibrate
+	 */
+	fl.confused = 1;
+
+	floppysetup1(&fl);
+
+	/* to turn the motor off when inactive */
+	alarm(5*1000, floppyalarm, 0);
+
+	return mask;
+}
+
+void
+floppyinitdev(int i, char *name)
+{
+	if(i >= fl.ndrive)
+		panic("floppyinitdev");
+	sprint(name, "fd%d", i);
+}
+
+void
+floppyprintdevs(int i)
+{
+	if(i >= fl.ndrive)
+		panic("floppyprintdevs");
+	print(" fd%d", i);
+}
+
+int
+floppyboot(int dev, char *file, Boot *b)
+{
+	Fs *fs;
+
+	if(strncmp(file, "dos!", 4) == 0)
+		file += 4;
+	else if(strchr(file, '!') || strcmp(file, "")==0) {
+		print("syntax is fd0!file\n");
+		return -1;
+	}
+
+	fs = floppygetfspart(dev, "dos", 1);
+	if(fs == nil)
+		return -1;
+
+	return fsboot(fs, file, b);
+}
+
+void
+floppyprintbootdevs(int dev)
+{
+	print(" fd%d", dev);
+}
+
+/*
+ *  check if the floppy has been replaced under foot.  cause
+ *  an error if it has.
+ *
+ *  a seek and a read clears the condition.  this was determined
+ *  experimentally, there has to be a better way.
+ *
+ *  if the read fails, cycle through the possible floppy
+ *  density till one works or we've cycled through all
+ *  possibilities for this drive.
+ */
+static int
+changed(FDrive *dp)
+{
+	FType *start;
+
+	/*
+	 *  if floppy has changed or first time through
+	 */
+	if((inb(Pdir)&Fchange) || dp->vers == 0){
+		DPRINT("changed\n");
+		fldump();
+		dp->vers++;
+		floppysetdef(dp);
+		dp->maxtries = 3;
+		start = dp->t;
+
+		/* flopppyon fails if there's no drive */
+		dp->confused = 1;	/* make floppyon recal */
+		if(floppyon(dp) < 0)
+			return -1;
+
+		pcfloppyseek(dp, dp->t->heads*dp->t->tsize);
+
+		while(floppyxfer(dp, Fread, dp->cache, 0, dp->t->tsize) <= 0){
+
+			/*
+			 *  if the xfer attempt doesn't clear the changed bit,
+			 *  there's no floppy in the drive
+			 */
+			if(inb(Pdir)&Fchange)
+				return -1;
+
+			while(++dp->t){
+				if(dp->t == &floppytype[nelem(floppytype)])
+					dp->t = floppytype;
+				if(dp->dt == dp->t->dt)
+					break;
+			}
+
+			/* flopppyon fails if there's no drive */
+			if(floppyon(dp) < 0)
+				return -1;
+
+			DPRINT("changed: trying %s\n", dp->t->name);
+			fldump();
+			if(dp->t == start)
+				return -1;
+		}
+	}
+
+	return 0;
+}
+
+static int
+readtrack(FDrive *dp, int cyl, int head)
+{
+	int i, nn, sofar;
+	ulong pos;
+
+	nn = dp->t->tsize;
+	if(dp->ccyl==cyl && dp->chead==head)
+		return nn;
+	pos = (cyl*dp->t->heads+head) * nn;
+	for(sofar = 0; sofar < nn; sofar += i){
+		dp->ccyl = -1;
+		i = floppyxfer(dp, Fread, dp->cache + sofar, pos + sofar, nn - sofar);
+		if(i <= 0)
+			return -1;
+	}
+	dp->ccyl = cyl;
+	dp->chead = head;
+	return nn;
+}
+
+long
+floppyread(Fs *fs, void *a, long n)
+{
+	FDrive *dp;
+	long rv, offset;
+	int sec, head, cyl;
+	long len;
+	uchar *aa;
+
+	aa = a;
+	dp = &fl.d[fs->dev];
+
+	offset = dp->offset;
+	floppyon(dp);
+	if(changed(dp))
+		return -1;
+
+	for(rv = 0; rv < n; rv += len){
+		/*
+		 *  all xfers come out of the track cache
+		 */
+		dp->len = n - rv;
+		floppypos(dp, offset+rv);
+		cyl = dp->tcyl;
+		head = dp->thead;
+		len = dp->len;
+		sec = dp->tsec;
+		if(readtrack(dp, cyl, head) < 0)
+			break;
+		memmove(aa+rv, dp->cache + (sec-1)*dp->t->bytes, len);
+	}
+	dp->offset = offset+rv;
+
+	return rv;
+}
+
+void*
+floppygetfspart(int i, char *name, int chatty)
+{
+	static Fs fs;
+
+	if(strcmp(name, "dos") != 0){
+		if(chatty)
+			print("unknown partition fd%d!%s (use fd%d!dos)\n", i, name, i);
+		return nil;
+	}
+
+	fs.dev = i;
+	fs.diskread = floppyread;
+	fs.diskseek = floppyseek;
+
+	/* sometimes we get spurious errors and doing it again works */
+	if(dosinit(&fs) < 0 && dosinit(&fs) < 0){
+		if(chatty)
+			print("fd%d!%s does not contain a FAT file system\n", i, name);
+		return nil;
+	}
+	return &fs;
+}
+
+static int
+return0(void*)
+{
+	return 0;
+}
+
+static void
+timedsleep(int (*f)(void*), void* arg, int ms)
+{
+	int s;
+	ulong end;
+
+	end = m->ticks + 1 + MS2TK(ms);
+	while(m->ticks < end && !(*f)(arg)){
+		s = spllo();
+		delay(10);
+		splx(s);
+	}
+}
+
+/*
+ *  start a floppy drive's motor.
+ */
+static int
+floppyon(FDrive *dp)
+{
+	int alreadyon;
+	int tries;
+
+	if(fl.confused)
+		floppyrevive();
+
+	/* start motor and select drive */
+	dp->lasttouched = m->ticks;
+	alreadyon = fl.motor & MOTORBIT(dp->dev);
+	if(!alreadyon){
+		fl.motor |= MOTORBIT(dp->dev);
+		outb(Pdor, fl.motor | Fintena | Fena | dp->dev);
+		/* wait for drive to spin up */
+		timedsleep(return0, 0, 750);
+
+		/* clear any pending interrupts */
+		floppysense();
+	}
+
+	/* set transfer rate */
+	if(fl.rate != dp->t->rate){
+		fl.rate = dp->t->rate;
+		outb(Pdsr, fl.rate);
+	}
+
+	/* get drive to a known cylinder */
+	if(dp->confused)
+		for(tries = 0; tries < 4; tries++)
+			if(floppyrecal(dp) >= 0)
+				break;
+	dp->lasttouched = m->ticks;
+	fl.selected = dp;
+	if(dp->confused)
+		return -1;
+	return 0;
+}
+
+/*
+ *  stop the floppy if it hasn't been used in 5 seconds
+ */
+static void
+floppyoff(FDrive *dp)
+{
+	fl.motor &= ~MOTORBIT(dp->dev);
+	outb(Pdor, fl.motor | Fintena | Fena | dp->dev);
+}
+
+/*
+ *  send a command to the floppy
+ */
+static int
+floppycmd(void)
+{
+	int i;
+	int tries;
+
+	fl.nstat = 0;
+	for(i = 0; i < fl.ncmd; i++){
+		for(tries = 0; ; tries++){
+			if((inb(Pmsr)&(Ffrom|Fready)) == Fready)
+				break;
+			if(tries > 1000){
+				DPRINT("cmd %ux can't be sent (%d)\n", fl.cmd[0], i);
+				fldump();
+
+				/* empty fifo, might have been a bad command */
+				floppyresult();
+				return -1;
+			}
+			microdelay(1);
+		}
+		outb(Pfdata, fl.cmd[i]);
+	}
+	return 0;
+}
+
+/*
+ *  get a command result from the floppy
+ *
+ *  when the controller goes ready waiting for a command
+ *  (instead of sending results), we're done
+ * 
+ */
+static int
+floppyresult(void)
+{
+	int i, s;
+	int tries;
+
+	/* get the result of the operation */
+	for(i = 0; i < sizeof(fl.stat); i++){
+		/* wait for status byte */
+		for(tries = 0; ; tries++){
+			s = inb(Pmsr)&(Ffrom|Fready);
+			if(s == Fready){
+				fl.nstat = i;
+				return fl.nstat;
+			}
+			if(s == (Ffrom|Fready))
+				break;
+			if(tries > 1000){
+				DPRINT("floppyresult: %d stats\n", i);
+				fldump();
+				fl.confused = 1;
+				return -1;
+			}
+			microdelay(1);
+		}
+		fl.stat[i] = inb(Pfdata);
+	}
+	fl.nstat = sizeof(fl.stat);
+	return fl.nstat;
+}
+
+/*
+ *  calculate physical address of a logical byte offset into the disk
+ *
+ *  truncate dp->length if it crosses a track boundary
+ */
+static void
+floppypos(FDrive *dp, long off)
+{
+	int lsec;
+	int ltrack;
+	int end;
+
+	lsec = off/dp->t->bytes;
+	ltrack = lsec/dp->t->sectors;
+	dp->tcyl = ltrack/dp->t->heads;
+	dp->tsec = (lsec % dp->t->sectors) + 1;
+	dp->thead = (lsec/dp->t->sectors) % dp->t->heads;
+
+	/*
+	 *  can't read across track boundaries.
+	 *  if so, decrement the bytes to be read.
+	 */
+	end = (ltrack+1)*dp->t->sectors*dp->t->bytes;
+	if(off+dp->len > end)
+		dp->len = end - off;
+}
+
+/*
+ *  get the interrupt cause from the floppy.
+ */
+static int
+floppysense(void)
+{
+	fl.ncmd = 0;
+	fl.cmd[fl.ncmd++] = Fsense;
+	if(floppycmd() < 0)
+		return -1;
+	if(floppyresult() < 2){
+		DPRINT("can't read sense response\n");
+		fldump();
+		fl.confused = 1;
+		return -1;
+	}
+	return 0;
+}
+
+static int
+cmddone(void *a)
+{
+	USED(a);
+	return fl.ncmd == 0;
+}
+
+/*
+ *  Wait for a floppy interrupt.  If none occurs in 5 seconds, we
+ *  may have missed one.  This only happens on some portables which
+ *  do power management behind our backs.  Call the interrupt
+ *  routine to try to clear any conditions.
+ */
+static void
+floppywait(int slow)
+{
+	timedsleep(cmddone, 0, slow ? 5000 : 1000);
+	if(!cmddone(0)){
+		floppyintr(0);
+		fl.confused = 1;
+	}
+}
+
+/*
+ *  we've lost the floppy position, go to cylinder 0.
+ */
+static int
+floppyrecal(FDrive *dp)
+{
+	dp->ccyl = -1;
+	dp->cyl = -1;
+
+	fl.ncmd = 0;
+	fl.cmd[fl.ncmd++] = Frecal;
+	fl.cmd[fl.ncmd++] = dp->dev;
+	if(floppycmd() < 0)
+		return -1;
+	floppywait(1);
+	if(fl.nstat < 2){
+		DPRINT("recalibrate: confused %ux\n", inb(Pmsr));
+		fl.confused = 1;
+		return -1;
+	}
+	if((fl.stat[0] & (Codemask|Seekend)) != Seekend){
+		DPRINT("recalibrate: failed\n");
+		dp->confused = 1;
+		return -1;
+	}
+	dp->cyl = fl.stat[1];
+	if(dp->cyl != 0){
+		DPRINT("recalibrate: wrong cylinder %d\n", dp->cyl);
+		dp->cyl = -1;
+		dp->confused = 1;
+		return -1;
+	}
+
+	dp->confused = 0;
+	return 0;
+}
+
+/*
+ *  if the controller or a specific drive is in a confused state,
+ *  reset it and get back to a kown state
+ */
+static void
+floppyrevive(void)
+{
+	FDrive *dp;
+
+	/*
+	 *  reset the controller if it's confused
+	 */
+	if(fl.confused){
+		DPRINT("floppyrevive in\n");
+		fldump();
+
+		/* reset controller and turn all motors off */
+		splhi();
+		fl.ncmd = 1;
+		fl.cmd[0] = 0;
+		outb(Pdor, 0);
+		delay(10);
+		outb(Pdor, Fintena|Fena);
+		delay(10);
+		spllo();
+		fl.motor = 0;
+		fl.confused = 0;
+		floppywait(0);
+
+		/* mark all drives in an unknown state */
+		for(dp = fl.d; dp < &fl.d[fl.ndrive]; dp++)
+			dp->confused = 1;
+
+		/* set rate to a known value */
+		outb(Pdsr, 0);
+		fl.rate = 0;
+
+		DPRINT("floppyrevive out\n");
+		fldump();
+	}
+}
+
+/*
+ *  seek to the target cylinder
+ *
+ *	interrupt, no results
+ */
+static vlong
+pcfloppyseek(FDrive *dp, vlong off)
+{
+	floppypos(dp, off);
+	if(dp->cyl == dp->tcyl){
+		dp->offset = off;
+		return off;
+	}
+	dp->cyl = -1;
+
+	fl.ncmd = 0;
+	fl.cmd[fl.ncmd++] = Fseek;
+	fl.cmd[fl.ncmd++] = (dp->thead<<2) | dp->dev;
+	fl.cmd[fl.ncmd++] = dp->tcyl * dp->t->steps;
+	if(floppycmd() < 0)
+		return -1;
+	floppywait(1);
+	if(fl.nstat < 2){
+		DPRINT("seek: confused\n");
+		fl.confused = 1;
+		return -1;
+	}
+	if((fl.stat[0] & (Codemask|Seekend)) != Seekend){
+		DPRINT("seek: failed\n");
+		dp->confused = 1;
+		return -1;
+	}
+
+	dp->cyl = dp->tcyl;
+	dp->offset = off;
+	DPRINT("seek to %d succeeded\n", dp->offset);
+
+	return dp->offset;
+}
+
+/*
+ *  read or write to floppy.  try up to three times.
+ */
+static long
+floppyxfer(FDrive *dp, int cmd, void *a, long off, long n)
+{
+	long offset;
+	int tries;
+
+	if(off >= dp->t->cap)
+		return 0;
+	if(off + n > dp->t->cap)
+		n = dp->t->cap - off;
+
+	/* retry on error (until it gets ridiculous) */
+	for(tries = 0; tries < dp->maxtries; tries++){
+
+		dp->len = n;
+		if(pcfloppyseek(dp, off) < 0){
+			DPRINT("xfer: seek failed\n");
+			dp->confused = 1;
+			continue;
+		}
+
+		/*
+		 *  set up the dma (dp->len may be trimmed)
+		 */
+		dp->len = dmasetup(DMAchan, a, dp->len, cmd==Fread);
+		if(dp->len < 0){
+	buggery:
+			dmaend(DMAchan);
+			continue;
+		}
+	
+		/*
+		 *  start operation
+		 */
+		fl.ncmd = 0;
+		fl.cmd[fl.ncmd++] = cmd | (dp->t->heads > 1 ? Fmulti : 0);
+		fl.cmd[fl.ncmd++] = (dp->thead<<2) | dp->dev;
+		fl.cmd[fl.ncmd++] = dp->tcyl;
+		fl.cmd[fl.ncmd++] = dp->thead;
+		fl.cmd[fl.ncmd++] = dp->tsec;
+		fl.cmd[fl.ncmd++] = dp->t->bcode;
+		fl.cmd[fl.ncmd++] = dp->t->sectors;
+		fl.cmd[fl.ncmd++] = dp->t->gpl;
+		fl.cmd[fl.ncmd++] = 0xFF;
+		if(floppycmd() < 0)
+			goto buggery;
+
+		/*
+		 *  give bus to DMA, floppyintr() will read result
+		 */
+		floppywait(0);
+		dmaend(DMAchan);
+
+		/*
+		 *  check for errors
+		 */
+		if(fl.nstat < 7){
+			DPRINT("xfer: confused\n");
+			fl.confused = 1;
+			continue;
+		}
+		if((fl.stat[0] & Codemask)!=0 || fl.stat[1] || fl.stat[2]){
+			DPRINT("xfer: failed %ux %ux %ux\n", fl.stat[0],
+				fl.stat[1], fl.stat[2]);
+			DPRINT("offset %lud len %ld\n", off, dp->len);
+			if((fl.stat[0]&Codemask)==Cmdexec && fl.stat[1]==Overrun){
+				DPRINT("DMA overrun: retry\n");
+			} else
+				dp->confused = 1;
+			continue;
+		}
+
+		/*
+		 *  check for correct cylinder
+		 */
+		offset = fl.stat[3] * dp->t->heads + fl.stat[4];
+		offset = offset*dp->t->sectors + fl.stat[5] - 1;
+		offset = offset * c2b[fl.stat[6]];
+		if(offset != off+dp->len){
+			DPRINT("xfer: ends on wrong cyl\n");
+			dp->confused = 1;
+			continue;
+		}
+	
+		dp->lasttouched = m->ticks;
+		dp->maxtries = 20;
+		return dp->len;
+	}
+
+	return -1;
+}
+
+/*
+void
+floppymemwrite(void)
+{
+	int i;
+	int n;
+	uchar *a;
+	FDrive *dp;
+
+	dp = &fl.d[0];
+	a = (uchar*)0x80000000;
+	n = 0;
+	while(n < 1440*1024){
+		i = floppyxfer(dp, Fwrite, a+n, n, 1440*1024-n);
+		if(i <= 0)
+			break;
+		n += i;
+	}
+	print("floppymemwrite wrote %d bytes\n", n);
+splhi(); for(;;);
+}
+*/
+
+static void
+floppyintr(Ureg *ur)
+{
+	USED(ur);
+	switch(fl.cmd[0]&~Fmulti){
+	case Fread:
+	case Fwrite:
+	case Fformat:
+	case Fdumpreg: 
+		floppyresult();
+		break;
+	case Fseek:
+	case Frecal:
+	default:
+		floppysense();	/* to clear interrupt */
+		break;
+	}
+	fl.ncmd = 0;
+}
--- /dev/null
+++ b/os/boot/pc/devfloppy.h
@@ -1,0 +1,196 @@
+typedef	struct FController FController;
+typedef	struct FDrive FDrive;
+typedef struct FType FType;
+
+static void floppyintr(Ureg*);
+static int floppyon(FDrive*);
+static void floppyoff(FDrive*);
+static void floppysetdef(FDrive*);
+
+/*
+ *  a floppy drive
+ */
+struct FDrive
+{
+	FType	*t;		/* floppy type */
+	int	dt;		/* drive type */
+	int	dev;
+
+	ulong	lasttouched;	/* time last touched */
+	int	cyl;		/* current arm position */
+	int	confused;	/* needs to be recalibrated */
+	int	offset;		/* current offset */
+	int	vers;
+	int	maxtries;
+
+	int	tcyl;		/* target cylinder */
+	int	thead;		/* target head */
+	int	tsec;		/* target sector */
+	long	len;		/* size of xfer */
+
+	uchar	*cache;		/* track cache */
+	int	ccyl;
+	int	chead;
+
+//	Rendez	r;		/* waiting here for motor to spin up */
+	void	*aux;
+};
+
+/*
+ *  controller for 4 floppys
+ */
+struct FController
+{
+//	QLock;			/* exclusive access to the contoller */
+
+	int	ndrive;
+	FDrive	*d;		/* the floppy drives */
+	FDrive	*selected;
+	int	rate;		/* current rate selected */
+	uchar	cmd[14];	/* command */
+	int	ncmd;		/* # command bytes */
+	uchar	stat[14];	/* command status */
+	int	nstat;		/* # status bytes */
+	int	confused;	/* controler needs to be reset */
+//	Rendez	r;		/* wait here for command termination */
+	int	motor;		/* bit mask of spinning disks */
+//	Rendez	kr;		/* for motor watcher */
+};
+
+/*
+ *  floppy types (all MFM encoding)
+ */
+struct FType
+{
+	char	*name;
+	int	dt;		/* compatible drive type */
+	int	bytes;		/* bytes/sector */
+	int	sectors;	/* sectors/track */
+	int	heads;		/* number of heads */
+	int	steps;		/* steps per cylinder */
+	int	tracks;		/* tracks/disk */
+	int	gpl;		/* intersector gap length for read/write */	
+	int	fgpl;		/* intersector gap length for format */
+	int	rate;		/* rate code */
+
+	/*
+	 *  these depend on previous entries and are set filled in
+	 *  by floppyinit
+	 */
+	int	bcode;		/* coded version of bytes for the controller */
+	long	cap;		/* drive capacity in bytes */
+	long	tsize;		/* track size in bytes */
+};
+/* bits in the registers */
+enum
+{
+	/* status registers a & b */
+	Psra=		0x3f0,
+	Psrb=		0x3f1,
+
+	/* digital output register */
+	Pdor=		0x3f2,
+	Fintena=	0x8,	/* enable floppy interrupt */
+	Fena=		0x4,	/* 0 == reset controller */
+
+	/* main status register */
+	Pmsr=		0x3f4,
+	Fready=		0x80,	/* ready to be touched */
+	Ffrom=		0x40,	/* data from controller */
+	Ffloppybusy=	0x10,	/* operation not over */
+
+	/* data register */
+	Pfdata=		0x3f5,
+	Frecal=		0x07,	/* recalibrate cmd */
+	Fseek=		0x0f,	/* seek cmd */
+	Fsense=		0x08,	/* sense cmd */
+	Fread=		0x66,	/* read cmd */
+	Freadid=	0x4a,	/* read track id */
+	Fspec=		0x03,	/* set hold times */
+	Fwrite=		0x45,	/* write cmd */
+	Fformat=	0x4d,	/* format cmd */
+	Fmulti=		0x80,	/* or'd with Fread or Fwrite for multi-head */
+	Fdumpreg=	0x0e,	/* dump internal registers */
+
+	/* digital input register */
+	Pdir=		0x3F7,	/* disk changed port (read only) */
+	Pdsr=		0x3F7,	/* data rate select port (write only) */
+	Fchange=	0x80,	/* disk has changed */
+
+	/* status 0 byte */
+	Drivemask=	3<<0,
+	Seekend=	1<<5,
+	Codemask=	(3<<6)|(3<<3),
+	Cmdexec=	1<<6,
+
+	/* status 1 byte */
+	Overrun=	0x10,
+};
+
+/*
+ *  types of drive (from PC equipment byte)
+ */
+enum
+{
+	Tnone=		0,
+	T360kb=		1,
+	T1200kb=	2,
+	T720kb=		3,
+	T1440kb=	4,
+};
+
+static void
+pcfloppyintr(Ureg *ur, void *a)
+{
+	USED(a);
+
+	floppyintr(ur);
+}
+
+void
+floppysetup0(FController *fl)
+{
+	uchar equip;
+
+	/*
+	 * Read nvram for types of floppies 0 & 1.
+	 * Always try floppy 0.
+	 */
+	equip = nvramread(0x10);
+	fl->ndrive = 1;
+
+	if(equip & 0xf)
+		fl->ndrive++;
+
+	/*
+	 * Allocate the drive storage.
+	 * There's always one.
+	 */
+	fl->d = xalloc(fl->ndrive*sizeof(FDrive));
+	fl->d[0].dt = (equip >> 4) & 0xf;
+	if(fl->d[0].dt == Tnone)
+		fl->d[0].dt = T1440kb;
+
+	if(fl->ndrive == 2)
+		fl->d[1].dt = equip & 0xf;
+}
+
+void
+floppysetup1(FController*)
+{
+//	intrenable(VectorFLOPPY, pcfloppyintr, fl, BUSUNKNOWN);
+	setvec(VectorFLOPPY, pcfloppyintr, 0);
+}
+
+
+static vlong pcfloppyseek(FDrive*, vlong);
+FController	fl;
+
+vlong
+floppyseek(Fs *fs, vlong off)
+{
+	FDrive *dp;
+
+	dp = &fl.d[fs->dev];
+	return pcfloppyseek(dp, off);
+}
--- /dev/null
+++ b/os/boot/pc/devi82365.c
@@ -1,0 +1,742 @@
+#include "u.h"
+#include "lib.h"
+#include "mem.h"
+#include "dat.h"
+#include "fns.h"
+#include "error.h"
+#include "io.h"
+
+/*
+ *  Intel 82365SL PCIC controller and compatibles.
+ */
+enum
+{
+	/*
+	 *  registers indices
+	 */
+	Rid=		0x0,		/* identification and revision */
+	Ris=		0x1,		/* interface status */
+	Rpc=	 	0x2,		/* power control */
+	 Foutena=	 (1<<7),	/*  output enable */
+	 Fautopower=	 (1<<5),	/*  automatic power switching */
+	 Fcardena=	 (1<<4),	/*  PC card enable */
+	Rigc= 		0x3,		/* interrupt and general control */
+	 Fiocard=	 (1<<5),	/*  I/O card (vs memory) */
+	 Fnotreset=	 (1<<6),	/*  reset if not set */	
+	 FSMIena=	 (1<<4),	/*  enable change interrupt on SMI */ 
+	Rcsc= 		0x4,		/* card status change */
+	Rcscic= 	0x5,		/* card status change interrupt config */
+	 Fchangeena=	 (1<<3),	/*  card changed */
+	 Fbwarnena=	 (1<<1),	/*  card battery warning */
+	 Fbdeadena=	 (1<<0),	/*  card battery dead */
+	Rwe= 		0x6,		/* address window enable */
+	 Fmem16=	 (1<<5),	/*  use A23-A12 to decode address */
+	Rio= 		0x7,		/* I/O control */
+	 Fwidth16=	 (1<<0),	/*  16 bit data width */
+	 Fiocs16=	 (1<<1),	/*  IOCS16 determines data width */
+	 Fzerows=	 (1<<2),	/*  zero wait state */
+	 Ftiming=	 (1<<3),	/*  timing register to use */
+	Riobtm0lo=	0x8,		/* I/O address 0 start low byte */
+	Riobtm0hi=	0x9,		/* I/O address 0 start high byte */
+	Riotop0lo=	0xa,		/* I/O address 0 stop low byte */
+	Riotop0hi=	0xb,		/* I/O address 0 stop high byte */
+	Riobtm1lo=	0xc,		/* I/O address 1 start low byte */
+	Riobtm1hi=	0xd,		/* I/O address 1 start high byte */
+	Riotop1lo=	0xe,		/* I/O address 1 stop low byte */
+	Riotop1hi=	0xf,		/* I/O address 1 stop high byte */
+	Rmap=		0x10,		/* map 0 */
+
+	/*
+	 *  CL-PD67xx extension registers
+	 */
+	Rmisc1=		0x16,		/* misc control 1 */
+	 F5Vdetect=	 (1<<0),
+	 Fvcc3V=	 (1<<1),
+	 Fpmint=	 (1<<2),
+	 Fpsirq=	 (1<<3),
+	 Fspeaker=	 (1<<4),
+	 Finpack=	 (1<<7),
+	Rfifo=		0x17,		/* fifo control */
+	 Fflush=	 (1<<7),	/*  flush fifo */
+	Rmisc2=		0x1E,		/* misc control 2 */
+	 Flowpow=	 (1<<1),	/*  low power mode */
+	Rchipinfo=	0x1F,		/* chip information */
+	Ratactl=	0x26,		/* ATA control */
+
+	/*
+	 *  offsets into the system memory address maps
+	 */
+	Mbtmlo=		0x0,		/* System mem addr mapping start low byte */
+	Mbtmhi=		0x1,		/* System mem addr mapping start high byte */
+	 F16bit=	 (1<<7),	/*  16-bit wide data path */
+	Mtoplo=		0x2,		/* System mem addr mapping stop low byte */
+	Mtophi=		0x3,		/* System mem addr mapping stop high byte */
+	 Ftimer1=	 (1<<6),	/*  timer set 1 */
+	Mofflo=		0x4,		/* Card memory offset address low byte */
+	Moffhi=		0x5,		/* Card memory offset address high byte */
+	 Fregactive=	 (1<<6),	/*  attribute memory */
+
+	/*
+	 *  configuration registers - they start at an offset in attribute
+	 *  memory found in the CIS.
+	 */
+	Rconfig=	0,
+	 Creset=	 (1<<7),	/*  reset device */
+	 Clevel=	 (1<<6),	/*  level sensitive interrupt line */
+	 Cirq=		 (1<<2),	/*  IRQ enable */
+	 Cdecode=	 (1<<1),	/*  address decode */
+	 Cfunc=		 (1<<0),	/*  function enable */
+	Riobase0=	5,
+	Riobase1=	6,
+	Riosize=	9,
+};
+
+static int pcmcia_pcmspecial(char *, ISAConf *);
+static void pcmcia_pcmspecialclose(int);
+
+#define MAP(x,o)	(Rmap + (x)*0x8 + o)
+
+typedef struct I82365	I82365;
+
+/* a controller */
+enum
+{
+	Ti82365,
+	Tpd6710,
+	Tpd6720,
+	Tvg46x,
+};
+struct I82365
+{
+	int	type;
+	int	dev;
+	int	nslot;
+	int	xreg;		/* index register address */
+	int	dreg;		/* data register address */
+	int	irq;
+};
+static I82365 *controller[4];
+static int ncontroller;
+static PCMslot	*slot;
+static PCMslot	*lastslot;
+static nslot;
+
+static void	i82365intr(Ureg*, void*);
+static void	i82365reset(void);
+static int	pcmio(int, ISAConf*);
+
+static void i82365dump(PCMslot*);
+
+void
+devi82365link(void)
+{
+	static int already;
+	char *p;
+
+	if(already)
+		return;
+	already = 1;
+
+	if((p=getconf("pcmcia0")) && strncmp(p, "disabled", 8)==0)
+		return;
+
+	if (_pcmspecial)
+		return;
+	
+	_pcmspecial = pcmcia_pcmspecial;
+	_pcmspecialclose = pcmcia_pcmspecialclose;
+}
+
+/*
+ *  reading and writing card registers
+ */
+static uchar
+rdreg(PCMslot *pp, int index)
+{
+	outb(((I82365*)pp->cp)->xreg, pp->base + index);
+	return inb(((I82365*)pp->cp)->dreg);
+}
+static void
+wrreg(PCMslot *pp, int index, uchar val)
+{
+	outb(((I82365*)pp->cp)->xreg, pp->base + index);
+	outb(((I82365*)pp->cp)->dreg, val);
+}
+
+/*
+ *  get info about card
+ */
+static void
+slotinfo(PCMslot *pp)
+{
+	uchar isr;
+
+	isr = rdreg(pp, Ris);
+	pp->occupied = (isr & (3<<2)) == (3<<2);
+	pp->powered = isr & (1<<6);
+	pp->battery = (isr & 3) == 3;
+	pp->wrprot = isr & (1<<4);
+	pp->busy = isr & (1<<5);
+	//pp->msec = TK2MS(MACHP(0)->ticks);
+}
+
+static int
+vcode(int volt)
+{
+	switch(volt){
+	case 5:
+		return 1;
+	case 12:
+		return 2;
+	default:
+		return 0;
+	}
+}
+
+/*
+ *  enable the slot card
+ */
+static void
+slotena(PCMslot *pp)
+{
+	if(pp->enabled)
+		return;
+
+	/* power up and unreset, wait's are empirical (???) */
+	wrreg(pp, Rpc, Fautopower|Foutena|Fcardena);
+	delay(300);
+	wrreg(pp, Rigc, 0);
+	delay(100);
+	wrreg(pp, Rigc, Fnotreset);
+	delay(500);
+
+	/* get configuration */
+	slotinfo(pp);
+	if(pp->occupied){
+		pcmcisread(pp);
+		pp->enabled = 1;
+	} else
+		wrreg(pp, Rpc, Fautopower);
+}
+
+/*
+ *  disable the slot card
+ */
+static void
+slotdis(PCMslot *pp)
+{
+	wrreg(pp, Rpc, 0);	/* turn off card power */
+	wrreg(pp, Rwe, 0);	/* no windows */
+	pp->enabled = 0;
+}
+
+/*
+ *  status change interrupt
+ */
+static void
+i82365intr(Ureg *, void *)
+{
+	uchar csc, was;
+	PCMslot *pp;
+
+	if(slot == 0)
+		return;
+
+	for(pp = slot; pp < lastslot; pp++){
+		csc = rdreg(pp, Rcsc);
+		was = pp->occupied;
+		slotinfo(pp);
+		if(csc & (1<<3) && was != pp->occupied){
+			if(!pp->occupied)
+				slotdis(pp);
+		}
+	}
+}
+
+enum
+{
+	Mshift=	12,
+	Mgran=	(1<<Mshift),	/* granularity of maps */
+	Mmask=	~(Mgran-1),	/* mask for address bits important to the chip */
+};
+
+/*
+ *  get a map for pc card region, return corrected len
+ */
+PCMmap*
+pcmmap(int slotno, ulong offset, int len, int attr)
+{
+	PCMslot *pp;
+	uchar we, bit;
+	PCMmap *m, *nm;
+	int i;
+	ulong e;
+
+	pp = slot + slotno;
+	lock(&pp->mlock);
+
+	/* convert offset to granularity */
+	if(len <= 0)
+		len = 1;
+	e = ROUND(offset+len, Mgran);
+	offset &= Mmask;
+	len = e - offset;
+
+	/* look for a map that covers the right area */
+	we = rdreg(pp, Rwe);
+	bit = 1;
+	nm = 0;
+	for(m = pp->mmap; m < &pp->mmap[nelem(pp->mmap)]; m++){
+		if((we & bit))
+		if(m->attr == attr)
+		if(offset >= m->ca && e <= m->cea){
+
+			m->ref++;
+			unlock(&pp->mlock);
+			return m;
+		}
+		bit <<= 1;
+		if(nm == 0 && m->ref == 0)
+			nm = m;
+	}
+	m = nm;
+	if(m == 0){
+		unlock(&pp->mlock);
+		return 0;
+	}
+
+	/* if isa space isn't big enough, free it and get more */
+	if(m->len < len){
+		if(m->isa){
+			umbfree(m->isa, m->len);
+			m->len = 0;
+		}
+		m->isa = PADDR(umbmalloc(0, len, Mgran));
+		if(m->isa == 0){
+			print("pcmmap %d: out of isa space\n", len);
+			unlock(&pp->mlock);
+			return 0;
+		}
+		m->len = len;
+	}
+
+	/* set up new map */
+	m->ca = offset;
+	m->cea = m->ca + m->len;
+	m->attr = attr;
+	i = m-pp->mmap;
+	bit = 1<<i;
+	wrreg(pp, Rwe, we & ~bit);		/* disable map before changing it */
+	wrreg(pp, MAP(i, Mbtmlo), m->isa>>Mshift);
+	wrreg(pp, MAP(i, Mbtmhi), (m->isa>>(Mshift+8)) | F16bit);
+	wrreg(pp, MAP(i, Mtoplo), (m->isa+m->len-1)>>Mshift);
+	wrreg(pp, MAP(i, Mtophi), ((m->isa+m->len-1)>>(Mshift+8)));
+	offset -= m->isa;
+	offset &= (1<<25)-1;
+	offset >>= Mshift;
+	wrreg(pp, MAP(i, Mofflo), offset);
+	wrreg(pp, MAP(i, Moffhi), (offset>>8) | (attr ? Fregactive : 0));
+	wrreg(pp, Rwe, we | bit);		/* enable map */
+	m->ref = 1;
+
+	unlock(&pp->mlock);
+	return m;
+}
+
+void
+pcmunmap(int slotno, PCMmap* m)
+{
+	PCMslot *pp;
+
+	pp = slot + slotno;
+	lock(&pp->mlock);
+	m->ref--;
+	unlock(&pp->mlock);
+}
+
+static void
+increfp(PCMslot *pp)
+{
+	lock(pp);
+	if(pp->ref++ == 0)
+		slotena(pp);
+	unlock(pp);
+}
+
+static void
+decrefp(PCMslot *pp)
+{
+	lock(pp);
+	if(pp->ref-- == 1)
+		slotdis(pp);
+	unlock(pp);
+}
+
+/*
+ *  look for a card whose version contains 'idstr'
+ */
+static int
+pcmcia_pcmspecial(char *idstr, ISAConf *isa)
+{
+	PCMslot *pp;
+	extern char *strstr(char*, char*);
+	int enabled;
+
+	i82365reset();
+	for(pp = slot; pp < lastslot; pp++){
+		if(pp->special)
+			continue;	/* already taken */
+		enabled = 0;
+		/* make sure we don't power on cards when we already know what's
+		 * in them.  We'll reread every two minutes if necessary
+		 */
+		if (pp->verstr[0] == '\0') {
+			increfp(pp);
+			enabled++;
+		}
+
+		if(pp->occupied) {
+			if(strstr(pp->verstr, idstr)){
+				if (!enabled){
+					enabled = 1;
+					increfp(pp);
+				}
+				if(isa == 0 || pcmio(pp->slotno, isa) == 0){
+					pp->special = 1;
+					return pp->slotno;
+				}
+			}
+		} else
+			pp->special = 1;
+		if (enabled)
+			decrefp(pp);
+	}
+	return -1;
+}
+
+static void
+pcmcia_pcmspecialclose(int slotno)
+{
+	PCMslot *pp;
+
+	print("pcmspecialclose called\n");
+	if(slotno >= nslot)
+		panic("pcmspecialclose");
+	pp = slot + slotno;
+	pp->special = 0;
+	decrefp(pp);
+}
+
+static char *chipname[] =
+{
+[Ti82365]	"Intel 82365SL",
+[Tpd6710]	"Cirrus Logic PD6710",
+[Tpd6720]	"Cirrus Logic PD6720",
+[Tvg46x]	"Vadem VG-46x",
+};
+
+static I82365*
+i82365probe(int x, int d, int dev)
+{
+	uchar c, id;
+	I82365 *cp;
+	ISAConf isa;
+	int i, nslot;
+
+	outb(x, Rid + (dev<<7));
+	id = inb(d);
+	if((id & 0xf0) != 0x80)
+		return 0;		/* not a memory & I/O card */
+	if((id & 0x0f) == 0x00)
+		return 0;		/* no revision number, not possible */
+
+	cp = xalloc(sizeof(I82365));
+	cp->xreg = x;
+	cp->dreg = d;
+	cp->dev = dev;
+	cp->type = Ti82365;
+	cp->nslot = 2;
+
+	switch(id){
+	case 0x82:
+	case 0x83:
+	case 0x84:
+		/* could be a cirrus */
+		outb(x, Rchipinfo + (dev<<7));
+		outb(d, 0);
+		c = inb(d);
+		if((c & 0xc0) != 0xc0)
+			break;
+		c = inb(d);
+		if((c & 0xc0) != 0x00)
+			break;
+		if(c & 0x20){
+			cp->type = Tpd6720;
+		} else {
+			cp->type = Tpd6710;
+			cp->nslot = 1;
+		}
+
+		/* low power mode */
+		outb(x, Rmisc2 + (dev<<7));
+		c = inb(d);
+		outb(d, c & ~Flowpow);
+		break;
+	}
+
+	/* if it's not a Cirrus, it could be a Vadem... */
+	if(cp->type == Ti82365){
+		/* unlock the Vadem extended regs */
+		outb(x, 0x0E + (dev<<7));
+		outb(x, 0x37 + (dev<<7));
+
+		/* make the id register show the Vadem id */
+		outb(x, 0x3A + (dev<<7));
+		c = inb(d);
+		outb(d, c|0xC0);
+		outb(x, Rid + (dev<<7));
+		c = inb(d);
+		if(c & 0x08)
+			cp->type = Tvg46x;
+
+		/* go back to Intel compatible id */
+		outb(x, 0x3A + (dev<<7));
+		c = inb(d);
+		outb(d, c & ~0xC0);
+	}
+
+	memset(&isa, 0, sizeof(ISAConf));
+	if(isaconfig("pcmcia", ncontroller, &isa) && isa.irq)
+		cp->irq = isa.irq;
+	else
+		cp->irq = VectorPCMCIA - VectorPIC;
+
+	for(i = 0; i < isa.nopt; i++){
+		if(cistrncmp(isa.opt[i], "nslot=", 6))
+			continue;
+		nslot = strtol(&isa.opt[i][6], nil, 0);
+		if(nslot > 0 && nslot <= 2)
+			cp->nslot = nslot;
+	}
+
+	controller[ncontroller++] = cp;
+	return cp;
+}
+
+static void
+i82365dump(PCMslot *pp)
+{
+	int i;
+
+	for(i = 0; i < 0x40; i++){
+		if((i&0x0F) == 0)
+			print("\n%2.2uX:	", i);
+		if(((i+1) & 0x0F) == 0x08)
+			print(" - ");
+		print("%2.2uX ", rdreg(pp, i));
+	}
+	print("\n");
+}
+
+/*
+ *  set up for slot cards
+ */
+static void
+i82365reset(void)
+{
+	static int already;
+	int i, j;
+	I82365 *cp;
+	PCMslot *pp;
+
+	if(already)
+		return;
+	already = 1;
+
+
+	/* look for controllers */
+	i82365probe(0x3E0, 0x3E1, 0);
+	i82365probe(0x3E0, 0x3E1, 1);
+	i82365probe(0x3E2, 0x3E3, 0);
+	i82365probe(0x3E2, 0x3E3, 1);
+
+	for(i = 0; i < ncontroller; i++)
+		nslot += controller[i]->nslot;
+	slot = xalloc(nslot * sizeof(PCMslot));
+
+	lastslot = slot;
+	for(i = 0; i < ncontroller; i++){
+		cp = controller[i];
+		print("#y%d: %d slot %s: port 0x%uX irq %d\n",
+			i, cp->nslot, chipname[cp->type], cp->xreg, cp->irq);
+		for(j = 0; j < cp->nslot; j++){
+			pp = lastslot++;
+			pp->slotno = pp - slot;
+			pp->memlen = 64*MB;
+			pp->base = (cp->dev<<7) | (j<<6);
+			pp->cp = cp;
+			pp->msec = ~0;
+			pp->verstr[0] = 0;
+			slotdis(pp);
+
+			/* interrupt on status change */
+			wrreg(pp, Rcscic, (cp->irq<<4) | Fchangeena);
+			rdreg(pp, Rcsc);
+		}
+
+		/* for card management interrupts */
+		setvec(cp->irq+VectorPIC, i82365intr, 0);
+	}
+}
+
+/*
+ *  configure the PCMslot for IO.  We assume very heavily that we can read
+ *  configuration info from the CIS.  If not, we won't set up correctly.
+ */
+static int
+pcmio(int slotno, ISAConf *isa)
+{
+	uchar we, x, *p;
+	PCMslot *pp;
+	PCMconftab *ct, *et, *t;
+	PCMmap *m;
+	int i, index, irq;
+	char *cp;
+
+	irq = isa->irq;
+	if(irq == 2)
+		irq = 9;
+
+	if(slotno > nslot)
+		return -1;
+	pp = slot + slotno;
+
+	if(!pp->occupied)
+		return -1;
+
+	et = &pp->ctab[pp->nctab];
+
+	ct = 0;
+	for(i = 0; i < isa->nopt; i++){
+		if(strncmp(isa->opt[i], "index=", 6))
+			continue;
+		index = strtol(&isa->opt[i][6], &cp, 0);
+		if(cp == &isa->opt[i][6] || index >= pp->nctab)
+			return -1;
+		ct = &pp->ctab[index];
+	}
+
+	if(ct == 0){
+	
+		/* assume default is right */
+		if(pp->def)
+			ct = pp->def;
+		else
+			ct = pp->ctab;
+	
+		/* try for best match */
+		if(ct->nio == 0
+		|| ct->io[0].start != isa->port || ((1<<irq) & ct->irqs) == 0){
+			for(t = pp->ctab; t < et; t++)
+				if(t->nio
+				&& t->io[0].start == isa->port
+				&& ((1<<irq) & t->irqs)){
+					ct = t;
+					break;
+				}
+		}
+		if(ct->nio == 0 || ((1<<irq) & ct->irqs) == 0){
+			for(t = pp->ctab; t < et; t++)
+				if(t->nio && ((1<<irq) & t->irqs)){
+					ct = t;
+					break;
+				}
+		}
+		if(ct->nio == 0){
+			for(t = pp->ctab; t < et; t++)
+				if(t->nio){
+					ct = t;
+					break;
+				}
+		}
+	}
+
+	if(ct == et || ct->nio == 0)
+		return -1;
+	if(isa->port == 0 && ct->io[0].start == 0)
+		return -1;
+
+	/* route interrupts */
+	isa->irq = irq;
+	wrreg(pp, Rigc, irq | Fnotreset | Fiocard);
+	
+	/* set power and enable device */
+	x = vcode(ct->vpp1);
+	wrreg(pp, Rpc, x|Fautopower|Foutena|Fcardena);
+
+	/* 16-bit data path */
+	if(ct->bit16)
+		x = Ftiming|Fiocs16|Fwidth16;
+	else
+		x = Ftiming;
+	if(ct->nio == 2 && ct->io[1].start)
+		x |= x<<4;
+	wrreg(pp, Rio, x);
+
+	/*
+	 * enable io port map 0
+	 * the 'top' register value includes the last valid address
+	 */
+	if(isa->port == 0)
+		isa->port = ct->io[0].start;
+	we = rdreg(pp, Rwe);
+	wrreg(pp, Riobtm0lo, isa->port);
+	wrreg(pp, Riobtm0hi, isa->port>>8);
+	i = isa->port+ct->io[0].len-1;
+	wrreg(pp, Riotop0lo, i);
+	wrreg(pp, Riotop0hi, i>>8);
+	we |= 1<<6;
+	if(ct->nio >= 2 && ct->io[1].start){
+		wrreg(pp, Riobtm1lo, ct->io[1].start);
+		wrreg(pp, Riobtm1hi, ct->io[1].start>>8);
+		i = ct->io[1].start+ct->io[1].len-1;
+		wrreg(pp, Riotop1lo, i);
+		wrreg(pp, Riotop1hi, i>>8);
+		we |= 1<<7;
+	}
+	wrreg(pp, Rwe, we);
+
+	/* only touch Rconfig if it is present */
+	m = pcmmap(slotno, pp->cfg[0].caddr + Rconfig, 0x20, 1);
+	p = KADDR(m->isa + pp->cfg[0].caddr - m->ca);
+	if(pp->cfg[0].cpresent & (1<<Rconfig)){
+		/*  Reset adapter */
+
+		/*  set configuration and interrupt type.
+		 *  if level is possible on the card, use it.
+		 */
+		x = ct->index;
+		if(ct->irqtype & 0x20)
+			x |= Clevel;
+
+		/*  enable the device, enable address decode and
+		 *  irq enable.
+		 */
+		x |= Cfunc|Cdecode|Cirq;
+
+		p[0] = x;
+		//delay(5);
+		microdelay(40);
+	}
+
+	if(pp->cfg[0].cpresent & (1<<Riobase0)){
+		/* set up the iobase 0 */
+		p[Riobase0 << 1] = isa->port;
+		p[Riobase1 << 1] = isa->port >> 8;
+	}
+
+	if(pp->cfg[0].cpresent & (1<<Riosize))
+		p[Riosize << 1] = ct->io[0].len;
+	pcmunmap(slotno, m);
+	return 0;
+}
--- /dev/null
+++ b/os/boot/pc/devpccard.c
@@ -1,0 +1,1606 @@
+/*
+     cardbus and pcmcia (grmph) support.
+*/
+#include "u.h"
+#include "lib.h"
+#include "mem.h"
+#include "dat.h"
+#include "fns.h"
+#include "error.h"
+#include "io.h"
+
+extern int pciscan(int, Pcidev **);
+
+int (*_pcmspecial)(char *, ISAConf *);
+void (*_pcmspecialclose)(int);
+
+int
+pcmspecial(char *idstr, ISAConf *isa)
+{
+	return (_pcmspecial  != nil)? _pcmspecial(idstr, isa): -1;
+}
+
+void
+pcmspecialclose(int a)
+{
+	if (_pcmspecialclose != nil)
+		_pcmspecialclose(a);
+}
+
+static ulong
+ioreserve(ulong, int size, int align, char *)
+{
+	static ulong isaend = 0x400; /*0xfd00*/
+	ulong ioaddr;
+
+	if (align)
+		isaend = ((isaend + align - 1) / align) * align;
+	ioaddr = isaend;
+	isaend += size;
+	return ioaddr;
+}
+
+#define MAP(x,o)	(Rmap + (x)*0x8 + o)
+
+enum {
+	TI_vid = 0x104c,
+	TI_1131_did = 0xAC15,
+	TI_1250_did = 0xAC16,
+	TI_1450_did = 0xAC1B,
+	TI_1251A_did = 0xAC1D,
+	TI_1420_did = 0xAC51,
+
+	Ricoh_vid = 0x1180,
+	Ricoh_475_did = 0x0475,
+	Ricoh_476_did = 0x0476,
+	Ricoh_478_did = 0x0478,
+
+	O2_vid = 0x1217,
+	O2_OZ711M3_did = 0x7134,
+
+	Nslots = 4,		/* Maximum number of CardBus slots to use */
+
+	K = 1024,
+	M = K * K,
+
+	LegacyAddr = 0x3e0,
+	NUMEVENTS = 10,
+
+	TI1131xSC = 0x80,		// system control
+		TI122X_SC_INTRTIE	= 1 << 29,
+	TI12xxIM = 0x8c,		// 
+	TI1131xCC = 0x91,		// card control
+		TI113X_CC_RIENB = 1 << 7,
+		TI113X_CC_ZVENABLE = 1 << 6,
+		TI113X_CC_PCI_IRQ_ENA = 1 << 5,
+		TI113X_CC_PCI_IREQ = 1 << 4,
+		TI113X_CC_PCI_CSC = 1 << 3,
+		TI113X_CC_SPKROUTEN = 1 << 1,
+		TI113X_CC_IFG = 1 << 0,
+	TI1131xDC = 0x92,		// device control
+};
+
+typedef struct Variant Variant;
+struct Variant {
+	ushort	vid;
+	ushort	did;
+	char	*name;
+};
+
+static Variant variant[] = {
+{	Ricoh_vid,	Ricoh_475_did,	"Ricoh 475 PCI/Cardbus bridge",	},
+{	Ricoh_vid,	Ricoh_476_did,	"Ricoh 476 PCI/Cardbus bridge",	},
+{	Ricoh_vid,	Ricoh_478_did,	"Ricoh 478 PCI/Cardbus bridge",	},
+{	TI_vid,		TI_1131_did,	"TI PCI-1131 Cardbus Controller", },
+{	TI_vid,		TI_1250_did,	"TI PCI-1250 Cardbus Controller", },
+{	TI_vid,		TI_1450_did,	"TI PCI-1450 Cardbus Controller", },
+{	TI_vid,		TI_1251A_did,	"TI PCI-1251A Cardbus Controller", },
+{	TI_vid,		TI_1420_did,	"TI PCI-1420 Cardbus Controller", },
+{	O2_vid,		O2_OZ711M3_did,	"O2Micro OZ711M3 MemoryCardBus", },
+};
+
+/* Cardbus registers */
+enum {
+	SocketEvent = 0,
+		SE_CCD = 3 << 1,
+		SE_POWER = 1 << 3,
+	SocketMask = 1,
+	SocketState = 2,
+		SS_CCD = 3 << 1,
+		SS_POWER = 1 << 3,
+		SS_PC16 = 1 << 4,
+		SS_CBC = 1 << 5,
+		SS_NOTCARD = 1 << 7,
+		SS_BADVCC = 1 << 9,
+		SS_5V = 1 << 10,
+		SS_3V = 1 << 11,
+	SocketForce = 3,
+	SocketControl = 4,
+		SC_5V = 0x22,
+		SC_3V = 0x33,
+};
+
+enum {
+	PciPCR_IO = 1 << 0,
+	PciPCR_MEM = 1 << 1,
+	PciPCR_Master = 1 << 2,
+
+	PciPMC = 0xa4,
+
+	Nbars = 6,
+	Ncmd = 10,
+	CBIRQ = 9,
+
+	PC16,
+	PC32,
+};
+
+enum {
+	Ti82365,
+	Tpd6710,
+	Tpd6720,
+	Tvg46x,
+};
+
+/*
+ *  Intel 82365SL PCIC controller for the PCMCIA or
+ *  Cirrus Logic PD6710/PD6720 which is mostly register compatible
+ */
+enum
+{
+	/*
+	 *  registers indices
+	 */
+	Rid=		0x0,		/* identification and revision */
+	Ris=		0x1,		/* interface status */
+	Rpc=	 	0x2,		/* power control */
+	 Foutena=	 (1<<7),	/*  output enable */
+	 Fautopower=	 (1<<5),	/*  automatic power switching */
+	 Fcardena=	 (1<<4),	/*  PC card enable */
+	Rigc= 		0x3,		/* interrupt and general control */
+	 Fiocard=	 (1<<5),	/*  I/O card (vs memory) */
+	 Fnotreset=	 (1<<6),	/*  reset if not set */	
+	 FSMIena=	 (1<<4),	/*  enable change interrupt on SMI */ 
+	Rcsc= 		0x4,		/* card status change */
+	Rcscic= 	0x5,		/* card status change interrupt config */
+	 Fchangeena=	 (1<<3),	/*  card changed */
+	 Fbwarnena=	 (1<<1),	/*  card battery warning */
+	 Fbdeadena=	 (1<<0),	/*  card battery dead */
+	Rwe= 		0x6,		/* address window enable */
+	 Fmem16=	 (1<<5),	/*  use A23-A12 to decode address */
+	Rio= 		0x7,		/* I/O control */
+	 Fwidth16=	 (1<<0),	/*  16 bit data width */
+	 Fiocs16=	 (1<<1),	/*  IOCS16 determines data width */
+	 Fzerows=	 (1<<2),	/*  zero wait state */
+	 Ftiming=	 (1<<3),	/*  timing register to use */
+	Riobtm0lo=	0x8,		/* I/O address 0 start low byte */
+	Riobtm0hi=	0x9,		/* I/O address 0 start high byte */
+	Riotop0lo=	0xa,		/* I/O address 0 stop low byte */
+	Riotop0hi=	0xb,		/* I/O address 0 stop high byte */
+	Riobtm1lo=	0xc,		/* I/O address 1 start low byte */
+	Riobtm1hi=	0xd,		/* I/O address 1 start high byte */
+	Riotop1lo=	0xe,		/* I/O address 1 stop low byte */
+	Riotop1hi=	0xf,		/* I/O address 1 stop high byte */
+	Rmap=		0x10,		/* map 0 */
+
+	/*
+	 *  CL-PD67xx extension registers
+	 */
+	Rmisc1=		0x16,		/* misc control 1 */
+	 F5Vdetect=	 (1<<0),
+	 Fvcc3V=	 (1<<1),
+	 Fpmint=	 (1<<2),
+	 Fpsirq=	 (1<<3),
+	 Fspeaker=	 (1<<4),
+	 Finpack=	 (1<<7),
+	Rfifo=		0x17,		/* fifo control */
+	 Fflush=	 (1<<7),	/*  flush fifo */
+	Rmisc2=		0x1E,		/* misc control 2 */
+	 Flowpow=	 (1<<1),	/*  low power mode */
+	Rchipinfo=	0x1F,		/* chip information */
+	Ratactl=	0x26,		/* ATA control */
+
+	/*
+	 *  offsets into the system memory address maps
+	 */
+	Mbtmlo=		0x0,		/* System mem addr mapping start low byte */
+	Mbtmhi=		0x1,		/* System mem addr mapping start high byte */
+	 F16bit=	 (1<<7),	/*  16-bit wide data path */
+	Mtoplo=		0x2,		/* System mem addr mapping stop low byte */
+	Mtophi=		0x3,		/* System mem addr mapping stop high byte */
+	 Ftimer1=	 (1<<6),	/*  timer set 1 */
+	Mofflo=		0x4,		/* Card memory offset address low byte */
+	Moffhi=		0x5,		/* Card memory offset address high byte */
+	 Fregactive=	 (1<<6),	/*  attribute memory */
+
+	/*
+	 *  configuration registers - they start at an offset in attribute
+	 *  memory found in the CIS.
+	 */
+	Rconfig=	0,
+	 Creset=	 (1<<7),	/*  reset device */
+	 Clevel=	 (1<<6),	/*  level sensitive interrupt line */
+};
+
+/*
+ *  read and crack the card information structure enough to set
+ *  important parameters like power
+ */
+/* cis memory walking */
+typedef struct Cisdat Cisdat;
+struct Cisdat {
+	uchar		*cisbase;
+	int		cispos;
+	int		cisskip;
+	int		cislen;
+};
+
+typedef struct Pcminfo Pcminfo;
+struct Pcminfo {
+	char		verstr[512];		/* Version string */
+	PCMmap		mmap[4];		/* maps, last is always for the kernel */
+	ulong		conf_addr;		/* Config address */
+	uchar		conf_present;		/* Config register present */
+	int		nctab;			/* In use configuration tables */
+	PCMconftab	ctab[8];		/* Configuration tables */
+	PCMconftab	*defctab;		/* Default conftab */
+
+	int		port;			/* Actual port usage */
+	int		irq;			/* Actual IRQ usage */
+};
+
+typedef struct Cardbus Cardbus;
+struct Cardbus {
+	Lock;
+	Variant		*variant;		/* Which CardBus chipset */
+	Pcidev		*pci;			/* The bridge itself */
+	ulong		*regs;			/* Cardbus registers */
+	int		ltype;			/* Legacy type */
+	int		lindex;			/* Legacy port index address */
+	int		ldata;			/* Legacy port data address */
+	int		lbase;			/* Base register for this socket */
+
+	int		state;			/* Current state of card */
+	int		type;			/* Type of card */
+	Pcminfo		linfo;			/* PCMCIA slot info */
+
+	int		special;		/* card is allocated to a driver */
+
+	int		refs;			/* Number of refs to slot */
+	Lock		refslock;		/* inc/dev ref lock */
+};
+
+enum {
+	Mshift=	12,
+	Mgran=	(1<<Mshift),	/* granularity of maps */
+	Mmask=	~(Mgran-1),	/* mask for address bits important to the chip */
+};
+
+static Cardbus cbslots[Nslots];
+static int nslots;
+
+static ulong exponent[8] = { 
+	1, 10, 100, 1000, 10000, 100000, 1000000, 10000000, 
+};
+
+static ulong vmant[16] = {
+	10, 12, 13, 15, 20, 25, 30, 35, 40, 45, 50, 55, 60, 70, 80, 90,
+};
+
+static ulong mantissa[16] = { 
+	0, 10, 12, 13, 15, 20, 25, 30, 35, 40, 45, 50, 55, 60, 70, 80, 
+};
+
+static void cbint(Ureg *, void *);
+static int powerup(Cardbus *);
+static void configure(Cardbus *);
+static void managecard(Cardbus *);
+static void cardmanager(void *);
+static void eject(Cardbus *);
+static void interrupt(Ureg *, void *);
+static void powerdown(Cardbus *cb);
+static void unconfigure(Cardbus *cb);
+
+static void i82365probe(Cardbus *cb, int lindex, int ldata);
+static void i82365configure(Cardbus *cb);
+static PCMmap *isamap(Cardbus *cb, ulong offset, int len, int attr);
+static void isaunmap(PCMmap* m);
+static uchar rdreg(Cardbus *cb, int index);
+static void wrreg(Cardbus *cb, int index, uchar val);
+static int readc(Cisdat *cis, uchar *x);
+static void tvers1(Cardbus *cb, Cisdat *cis, int );
+static void tcfig(Cardbus *cb, Cisdat *cis, int );
+static void tentry(Cardbus *cb, Cisdat *cis, int );
+static int vcode(int volt);
+static int pccard_pcmspecial(char *idstr, ISAConf *isa);
+static void pccard_pcmspecialclose(int slotno);
+
+enum {
+	CardDetected,
+	CardPowered,
+	CardEjected,
+	CardConfigured,
+};
+
+static char *messages[] = {
+[CardDetected]		"CardDetected",
+[CardPowered]		"CardPowered",
+[CardEjected]		"CardEjected",
+[CardConfigured]	"CardConfigured",
+};
+
+enum {
+	SlotEmpty,
+	SlotFull,
+	SlotPowered,
+	SlotConfigured,
+};
+
+static char *states[] = {
+[SlotEmpty]		"SlotEmpty",
+[SlotFull]		"SlotFull",
+[SlotPowered]		"SlotPowered",
+[SlotConfigured]	"SlotConfigured",
+};
+
+static void
+engine(Cardbus *cb, int message)
+{
+	// print("engine(%d): %s(%s)\n", 
+	//	 (int)(cb - cbslots), states[cb->state], messages[message]);
+	switch (cb->state) {
+	case SlotEmpty:
+
+		switch (message) {
+		case CardDetected:
+			cb->state = SlotFull;
+			powerup(cb);
+			break;
+		case CardEjected:
+			break;
+		default:
+			//print("#Y%d: Invalid message %s in SlotEmpty state\n",
+			//	(int)(cb - cbslots), messages[message]);
+			break;
+		}
+		break;
+
+	case SlotFull:
+
+		switch (message) {
+		case CardPowered:
+			cb->state = SlotPowered;
+			configure(cb);
+			break;
+		case CardEjected:
+			cb->state = SlotEmpty;
+			powerdown(cb);
+			break;
+		default:
+			//print("#Y%d: Invalid message %s in SlotFull state\n",
+			//	(int)(cb - cbslots), messages[message]);
+			break;
+		}
+		break;
+
+	case SlotPowered:
+
+		switch (message) {
+		case CardConfigured:
+			cb->state = SlotConfigured;
+			break;
+		case CardEjected:
+			cb->state = SlotEmpty;
+			unconfigure(cb);
+			powerdown(cb);
+			break;
+		default:
+			print("#Y%d: Invalid message %s in SlotPowered state\n",
+				(int)(cb - cbslots), messages[message]);
+			break;
+		}
+		break;
+
+	case SlotConfigured:
+
+		switch (message) {
+		case CardEjected:
+			cb->state = SlotEmpty;
+			unconfigure(cb);
+			powerdown(cb);
+			break;
+		default:
+			//print("#Y%d: Invalid message %s in SlotConfigured state\n",
+			//	(int)(cb - cbslots), messages[message]);
+			break;
+		}
+		break;
+	}
+}
+
+void
+devpccardlink(void)
+{
+	static int initialized;
+	Pcidev *pci;
+	int i;
+	char *p;
+
+	if (initialized) 
+		return;
+	initialized = 1;
+
+	if((p=getconf("pccard0")) && strncmp(p, "disabled", 8)==0)
+		return;
+
+	if(_pcmspecial)
+		return;
+
+
+	/* Allocate legacy space */
+	if (ioalloc(LegacyAddr, 2, 0, "i82365.0") < 0)
+		print("#Y: WARNING: Cannot allocate legacy ports\n");
+
+	/* Find all CardBus controllers */
+	pci = nil;
+	while ((pci = pcimatch(pci, 0, 0)) != nil) {
+		ulong baddr;
+		uchar pin;
+		Cardbus *cb;
+		int slot;
+
+		if(pci->ccrb != 6 || pci->ccru != 7)
+			continue;
+		for (i = 0; i != nelem(variant); i++)
+			if (pci->vid == variant[i].vid && pci->did == variant[i].did)
+				break;
+		if (i == nelem(variant))
+			continue;
+
+		/* initialize this slot */
+		slot = nslots++;
+		cb = &cbslots[slot];
+
+		cb->pci = pci;
+		cb->variant = &variant[i];
+
+		// Set up PCI bus numbers if needed.
+		if (pcicfgr8(pci, PciSBN) == 0) {
+			static int busbase = 0x20;
+
+			pcicfgw8(pci, PciSBN, busbase);
+			pcicfgw8(pci, PciUBN, busbase + 2);
+			busbase += 3;
+		}
+
+		// Patch up intl if needed.
+		if ((pin = pcicfgr8(pci, PciINTP)) != 0 && 
+		    (pci->intl == 0xff || pci->intl == 0)) {
+			pci->intl = pciipin(nil, pin);
+			pcicfgw8(pci, PciINTL, pci->intl);
+
+			if (pci->intl == 0xff || pci->intl == 0)
+				print("#Y%d: No interrupt?\n", (int)(cb - cbslots));
+		}
+		
+		// Don't you love standards!
+		if (pci->vid == TI_vid) {
+			if (pci->did <= TI_1131_did) {
+				uchar cc;
+
+				cc = pcicfgr8(pci, TI1131xCC);
+				cc &= ~(TI113X_CC_PCI_IRQ_ENA |
+						TI113X_CC_PCI_IREQ | 
+						TI113X_CC_PCI_CSC |
+						TI113X_CC_ZVENABLE);
+				cc |= TI113X_CC_PCI_IRQ_ENA | 
+						TI113X_CC_PCI_IREQ | 
+						TI113X_CC_SPKROUTEN;
+				pcicfgw8(pci, TI1131xCC, cc);
+
+				// PCI interrupts only
+				pcicfgw8(pci, TI1131xDC, 
+						pcicfgr8(pci, TI1131xDC) & ~6);
+
+				// CSC ints to PCI bus.
+				wrreg(cb, Rigc, rdreg(cb, Rigc) | 0x10);
+			}
+			else if (pci->did == TI_1250_did) {
+				print("No support yet for the TI_1250_did, prod pb\n");
+			}
+			else if (pci->did == TI_1420_did) {
+				// Disable Vcc protection
+				pcicfgw32(cb->pci, 0x80, 
+					pcicfgr32(cb->pci, 0x80) | (1 << 21));
+			}
+			
+			pcicfgw16(cb->pci, PciPMC, pcicfgr16(cb->pci, PciPMC) & ~3);
+		}
+	
+		if ((baddr = pcicfgr32(cb->pci, PciBAR0)) == 0) {
+			int size = (pci->did == Ricoh_478_did)? 0x10000: 0x1000;
+
+			baddr = upamalloc(baddr, size, size);
+			pcicfgw32(cb->pci, PciBAR0, baddr);
+			cb->regs = (ulong *)KADDR(baddr);
+		}
+		else
+			cb->regs = (ulong *)KADDR(upamalloc(baddr, 4096, 0));
+		cb->state = SlotEmpty;
+
+		/* Don't really know what to do with this... */
+		i82365probe(cb, LegacyAddr, LegacyAddr + 1);
+
+		print("#Y%ld: %s, %.8ulX intl %d\n", cb - cbslots, 
+			 variant[i].name, baddr, pci->intl);
+	}
+
+	if (nslots == 0)
+		return;
+
+	_pcmspecial = pccard_pcmspecial;
+	_pcmspecialclose = pccard_pcmspecialclose;
+
+	for (i = 0; i != nslots; i++) {
+		Cardbus *cb = &cbslots[i];
+
+		if ((cb->regs[SocketState] & SE_CCD) == 0)
+			engine(cb, CardDetected);
+	}
+
+	delay(500);			/* Allow time for power up */
+
+	for (i = 0; i != nslots; i++) {
+		Cardbus *cb = &cbslots[i];
+
+		if (cb->regs[SocketState] & SE_POWER)
+			engine(cb, CardPowered);
+
+		/* Ack and enable interrupts on all events */
+		//cb->regs[SocketEvent] = cb->regs[SocketEvent];
+		//cb->regs[SocketMask] |= 0xF;	
+		//wrreg(cb, Rcscic, 0xC);
+	}
+}
+
+static int
+powerup(Cardbus *cb)
+{
+	ulong state;
+	ushort bcr;
+
+	state = cb->regs[SocketState];
+	if (state & SS_PC16) {
+	
+		// print("#Y%ld: Probed a PC16 card, powering up card\n", cb - cbslots);
+		cb->type = PC16;
+		memset(&cb->linfo, 0, sizeof(Pcminfo));
+
+		/* power up and unreset, wait's are empirical (???) */
+		wrreg(cb, Rpc, Fautopower|Foutena|Fcardena);
+		delay(300);
+		wrreg(cb, Rigc, 0);
+		delay(100);
+		wrreg(cb, Rigc, Fnotreset);
+		delay(500);
+
+		return 1;
+	}
+
+	if (state & SS_CCD)
+		return 0;
+
+	if (state & SS_NOTCARD) {
+		print("#Y%ld: No card inserted\n", cb - cbslots);
+		return 0;
+	}
+
+	if (state & SS_BADVCC) {
+		print("#Y%ld: Bad VCC request to card, powering down card!\n", 
+			 cb - cbslots);
+		cb->regs[SocketControl] = 0;
+		return 0;
+	}
+
+	if ((state & SS_3V) == 0 && (state & SS_5V) == 0) {
+		print("#Y%ld: Unsupported voltage, powering down card!\n", 
+			cb - cbslots);
+		cb->regs[SocketControl] = 0;
+		return 0;
+	}
+
+	//print("#Y%ld: card %spowered at %d volt\n", cb - cbslots, 
+	//	(state & SS_POWER)? "": "not ", 
+	//	(state & SS_3V)? 3: (state & SS_5V)? 5: -1);
+
+	/* Power up the card
+	 * and make sure the secondary bus is not in reset.
+	 */
+	cb->regs[SocketControl] = (state & SS_5V)? SC_5V: SC_3V;
+	delay(50);
+	bcr = pcicfgr16(cb->pci, PciBCR);
+	bcr &= ~0x40;
+	pcicfgw16(cb->pci, PciBCR, bcr);
+	delay(100);
+
+	cb->type = PC32;
+
+	return 1;
+}
+
+static void
+powerdown(Cardbus *cb)
+{
+	ushort bcr;
+
+	if (cb->type == PC16) {
+
+		wrreg(cb, Rpc, 0);	/* turn off card power */
+		wrreg(cb, Rwe, 0);	/* no windows */
+
+		cb->type = -1;
+		return;
+	}
+
+	bcr = pcicfgr16(cb->pci, PciBCR);
+	bcr |= 0x40;
+	pcicfgw16(cb->pci, PciBCR, bcr);
+	cb->regs[SocketControl] = 0;
+	cb->type = -1;
+}
+
+static void
+configure(Cardbus *cb)
+{
+	Pcidev *pci;
+	ulong size, bar;
+	int i, ioindex, memindex, r;
+
+	//print("configuring slot %d (%s)\n", (int)(cb - cbslots), states[cb->state]);
+	if (cb->state == SlotConfigured)
+		return;
+	engine(cb, CardConfigured);
+
+	delay(50);					/* Emperically established */
+
+	if (cb->type == PC16) {
+		i82365configure(cb);
+		return;
+	}
+
+	/* Scan the CardBus for new PCI devices */
+	pciscan(pcicfgr8(cb->pci, PciSBN), &cb->pci->bridge);
+	pci = cb->pci->bridge;
+	while (pci) {
+		r = pcicfgr16(pci, PciPCR);
+		r &= ~(PciPCR_IO|PciPCR_MEM);
+		pcicfgw16(pci, PciPCR, r);
+
+		/*
+		 * Treat the found device as an ordinary PCI card.
+		 * It seems that the CIS is not always present in
+		 * CardBus cards.
+		 * XXX, need to support multifunction cards
+		 */
+		memindex = ioindex = 0;
+		for (i = 0; i != Nbars; i++) {
+
+			if (pci->mem[i].size == 0)
+				continue;
+			if (pci->mem[i].bar & 1) {
+
+				// Allocate I/O space
+				if (ioindex > 1) {
+					print("#Y%ld: WARNING: Can only configure 2 I/O slots\n", cb - cbslots);
+					continue;
+				}
+				bar = ioreserve(-1, pci->mem[i].size, 0, "cardbus");
+				pci->mem[i].bar = bar | 1;
+				pcicfgw32(pci, PciBAR0 + i * sizeof(ulong), 
+					          pci->mem[i].bar);
+				pcicfgw16(cb->pci, PciCBIBR0 + ioindex * 8, bar);
+				pcicfgw16(cb->pci, PciCBILR0 + ioindex * 8, 
+						 bar + pci->mem[i].size - 1);
+				//print("ioindex[%d] %.8uX (%d)\n", 
+				//	ioindex, bar, pci->mem[i].size);
+				ioindex++;
+				continue;
+			}
+
+			// Allocating memory space
+			if (memindex > 1) {
+				print("#Y%ld: WARNING: Can only configure 2 memory slots\n", cb - cbslots);
+				continue;
+			}
+
+			bar = upamalloc(0, pci->mem[i].size, BY2PG);
+			pci->mem[i].bar = bar | (pci->mem[i].bar & 0x80);
+			pcicfgw32(pci, PciBAR0 + i * sizeof(ulong), pci->mem[i].bar);
+			pcicfgw32(cb->pci, PciCBMBR0 + memindex * 8, bar);
+			pcicfgw32(cb->pci, PciCBMLR0 + memindex * 8, 
+					  bar + pci->mem[i].size - 1);
+
+			if (pci->mem[i].bar & 0x80) {
+				/* Enable prefetch */
+				r = pcicfgr16(cb->pci, PciBCR);
+				r |= 1 << (8 + memindex);
+				pcicfgw16(cb->pci, PciBCR, r);
+			}
+
+			//print("memindex[%d] %.8uX (%d)\n", 
+			//	  memindex, bar, pci->mem[i].size);
+			memindex++;
+		}
+
+		if ((size = pcibarsize(pci, PciEBAR0)) > 0) {
+
+			if (memindex > 1)
+				print("#Y%ld: WARNING: Too many memory spaces, not mapping ROM space\n",
+					cb - cbslots);
+			else {
+				pci->rom.bar = upamalloc(0, size, BY2PG);
+				pci->rom.size = size;
+
+				pcicfgw32(pci, PciEBAR0, pci->rom.bar);
+				pcicfgw32(cb->pci, PciCBMBR0 + memindex * 8,
+						 pci->rom.bar);
+				pcicfgw32(cb->pci, PciCBMLR0 + memindex * 8, 
+						 pci->rom.bar + pci->rom.size - 1);
+			}
+		}
+
+		/* Set the basic PCI registers for the device */
+		pci->pcr = pcicfgr16(pci, PciPCR);
+		pci->pcr |= PciPCR_IO|PciPCR_MEM|PciPCR_Master;
+		pci->cls = 8;
+		pci->ltr = 64;
+		pcicfgw16(pci, PciPCR, pci->pcr);
+		pcicfgw8(pci, PciCLS, pci->cls);
+		pcicfgw8(pci, PciLTR, pci->ltr);
+
+		if (pcicfgr8(pci, PciINTP)) {
+			pci->intl = pcicfgr8(cb->pci, PciINTL);
+			pcicfgw8(pci, PciINTL, pci->intl);
+
+			/* Route interrupts to INTA#/B# */
+			pcicfgw16(cb->pci, PciBCR, 
+					  pcicfgr16(cb->pci, PciBCR) & ~(1 << 7));
+		}
+			
+		pci = pci->list;
+	}
+}
+
+static void
+unconfigure(Cardbus *cb)
+{
+	Pcidev *pci;
+	int i, ioindex, memindex, r;
+
+	if (cb->type == PC16) {
+		print("#Y%d: Don't know how to unconfigure a PC16 card\n",
+			 (int)(cb - cbslots));
+
+		memset(&cb->linfo, 0, sizeof(Pcminfo));
+		return;
+	}
+
+	pci = cb->pci->bridge;
+	if (pci == nil) 
+		return;		/* Not configured */
+	cb->pci->bridge = nil;		
+
+	memindex = ioindex = 0;
+	while (pci) {
+		Pcidev *_pci;
+
+		for (i = 0; i != Nbars; i++) {
+			if (pci->mem[i].size == 0)
+				continue;
+			if (pci->mem[i].bar & 1) {
+				iofree(pci->mem[i].bar & ~1);
+				pcicfgw16(cb->pci, PciCBIBR0 + ioindex * 8, 
+						 (ushort)-1);
+				pcicfgw16(cb->pci, PciCBILR0 + ioindex * 8, 0);
+				ioindex++;
+				continue;
+			}
+
+			upafree(pci->mem[i].bar & ~0xF, pci->mem[i].size);
+			pcicfgw32(cb->pci, PciCBMBR0 + memindex * 8, (ulong)-1);
+			pcicfgw32(cb->pci, PciCBMLR0 + memindex * 8, 0);
+			r = pcicfgr16(cb->pci, PciBCR);
+			r &= ~(1 << (8 + memindex));
+			pcicfgw16(cb->pci, PciBCR, r);
+			memindex++;
+		}
+
+		if (pci->rom.bar && memindex < 2) {
+			upafree(pci->rom.bar & ~0xF, pci->rom.size);
+			pcicfgw32(cb->pci, PciCBMBR0 + memindex * 8, (ulong)-1);
+			pcicfgw32(cb->pci, PciCBMLR0 + memindex * 8, 0);
+			memindex++;
+		}
+
+		_pci = pci->list;
+		free(_pci);
+		pci = _pci;
+	}
+}
+
+static void
+i82365configure(Cardbus *cb)
+{
+	int this;
+	Cisdat cis;
+	PCMmap *m;
+	uchar type, link;
+
+	/*
+	 * Read all tuples in attribute space.
+	 */
+	m = isamap(cb, 0, 0, 1);
+	if(m == 0)
+		return;
+
+	cis.cisbase = KADDR(m->isa);
+	cis.cispos = 0;
+	cis.cisskip = 2;
+	cis.cislen = m->len;
+
+	/* loop through all the tuples */
+	for(;;){
+		this = cis.cispos;
+		if(readc(&cis, &type) != 1)
+			break;
+		if(type == 0xFF)
+			break;
+		if(readc(&cis, &link) != 1)
+			break;
+
+		switch(type){
+		default:
+			break;
+		case 0x15:
+			tvers1(cb, &cis, type);
+			break;
+		case 0x1A:
+			tcfig(cb, &cis, type);
+			break;
+		case 0x1B:
+			tentry(cb, &cis, type);
+			break;
+		}
+
+		if(link == 0xFF)
+			break;
+		cis.cispos = this + (2+link);
+	}
+	isaunmap(m);
+}
+
+/*
+ *  look for a card whose version contains 'idstr'
+ */
+static int
+pccard_pcmspecial(char *idstr, ISAConf *isa)
+{
+	int i, irq;
+	PCMconftab *ct, *et;
+	Pcminfo *pi;
+	Cardbus *cb;
+	uchar x, we, *p;
+
+	cb = nil;
+	for (i = 0; i != nslots; i++) {
+		cb = &cbslots[i];
+
+		lock(cb);
+		if (cb->state == SlotConfigured &&
+		    cb->type == PC16 && 
+		    !cb->special &&
+		    strstr(cb->linfo.verstr, idstr)) 
+			break;
+		unlock(cb);
+	}
+
+	if (i == nslots) {
+		// print("#Y: %s not found\n", idstr);
+		return -1;
+	}
+
+	pi = &cb->linfo;
+
+	/*
+ 	  *  configure the PCMslot for IO.  We assume very heavily that we can read
+ 	  *  configuration info from the CIS.  If not, we won't set up correctly.
+ 	  */
+	irq = isa->irq;
+	if(irq == 2)
+		irq = 9;
+
+	et = &pi->ctab[pi->nctab];
+	ct = nil;
+	for(i = 0; i < isa->nopt; i++){
+		int index;
+		char *cp;
+
+		if(strncmp(isa->opt[i], "index=", 6))
+			continue;
+		index = strtol(&isa->opt[i][6], &cp, 0);
+		if(cp == &isa->opt[i][6] || index >= pi->nctab) {
+			unlock(cb);
+			print("#Y%d: Cannot find index %d in conf table\n", 
+				 (int)(cb - cbslots), index);
+			return -1;
+		}
+		ct = &pi->ctab[index];
+	}
+
+	if(ct == nil){
+		PCMconftab *t;
+
+		/* assume default is right */
+		if(pi->defctab)
+			ct = pi->defctab;
+		else
+			ct = pi->ctab;
+	
+		/* try for best match */
+		if(ct->nio == 0
+		|| ct->io[0].start != isa->port || ((1<<irq) & ct->irqs) == 0){
+			for(t = pi->ctab; t < et; t++)
+				if(t->nio
+				&& t->io[0].start == isa->port
+				&& ((1<<irq) & t->irqs)){
+					ct = t;
+					break;
+				}
+		}
+		if(ct->nio == 0 || ((1<<irq) & ct->irqs) == 0){
+			for(t = pi->ctab; t < et; t++)
+				if(t->nio && ((1<<irq) & t->irqs)){
+					ct = t;
+					break;
+				}
+		}
+		if(ct->nio == 0){
+			for(t = pi->ctab; t < et; t++)
+				if(t->nio){
+					ct = t;
+					break;
+				}
+		}
+	}
+
+	if(ct == et || ct->nio == 0) {
+		unlock(cb);
+		print("#Y%d: No configuration?\n", (int)(cb - cbslots));
+		return -1;
+	}
+	if(isa->port == 0 && ct->io[0].start == 0) {
+		unlock(cb);
+		print("#Y%d: No part or start address\n", (int)(cb - cbslots));
+		return -1;
+	}
+
+	cb->special = 1;	/* taken */
+
+	/* route interrupts */
+	isa->irq = irq;
+	wrreg(cb, Rigc, irq | Fnotreset | Fiocard);
+
+	/* set power and enable device */
+	x = vcode(ct->vpp1);
+	wrreg(cb, Rpc, x|Fautopower|Foutena|Fcardena);
+
+	/* 16-bit data path */
+	if(ct->bit16)
+		x = Ftiming|Fiocs16|Fwidth16;
+	else
+		x = Ftiming;
+	if(ct->nio == 2 && ct->io[1].start)
+		x |= x<<4;
+	wrreg(cb, Rio, x);
+
+	/*
+	 * enable io port map 0
+	 * the 'top' register value includes the last valid address
+	 */
+	if(isa->port == 0)
+		isa->port = ct->io[0].start;
+	we = rdreg(cb, Rwe);
+	wrreg(cb, Riobtm0lo, isa->port);
+	wrreg(cb, Riobtm0hi, isa->port>>8);
+	i = isa->port+ct->io[0].len-1;
+	wrreg(cb, Riotop0lo, i);
+	wrreg(cb, Riotop0hi, i>>8);
+	we |= 1<<6;
+	if(ct->nio == 2 && ct->io[1].start){
+		wrreg(cb, Riobtm1lo, ct->io[1].start);
+		wrreg(cb, Riobtm1hi, ct->io[1].start>>8);
+		i = ct->io[1].start+ct->io[1].len-1;
+		wrreg(cb, Riotop1lo, i);
+		wrreg(cb, Riotop1hi, i>>8);
+		we |= 1<<7;
+	}
+	wrreg(cb, Rwe, we);
+
+	/* only touch Rconfig if it is present */
+	if(pi->conf_present & (1<<Rconfig)){
+		PCMmap *m;
+
+		/*  Reset adapter */
+		m = isamap(cb, pi->conf_addr + Rconfig, 1, 1);
+		p = KADDR(m->isa + pi->conf_addr + Rconfig - m->ca);
+
+		/* set configuration and interrupt type */
+		x = ct->index;
+		if((ct->irqtype & 0x20)/* && ((ct->irqtype & 0x40)==0 || isa->irq>7)*/)
+			x |= Clevel;
+		*p = x;
+		delay(5);
+
+		isaunmap(m);
+	}
+
+	pi->port = isa->port;
+	pi->irq = isa->irq;
+	unlock(cb);
+
+	print("#Y%d: %s irq %ld, port %lX\n", (int)(cb - cbslots), pi->verstr, isa->irq, isa->port);
+	return (int)(cb - cbslots);
+}
+
+static void
+pccard_pcmspecialclose(int slotno)
+{
+	Cardbus *cb = &cbslots[slotno];
+
+	wrreg(cb, Rwe, 0);	/* no windows */
+	cb->special = 0;
+}
+
+static int
+xcistuple(int slotno, int tuple, int subtuple, void *v, int nv, int attr)
+{
+	PCMmap *m;
+	Cisdat cis;
+	int i, l;
+	uchar *p;
+	uchar type, link, n, c;
+	int this, subtype;
+	Cardbus *cb = &cbslots[slotno];
+
+	m = isamap(cb, 0, 0, attr);
+	if(m == 0)
+		return -1;
+
+	cis.cisbase = KADDR(m->isa);
+	cis.cispos = 0;
+	cis.cisskip = attr ? 2 : 1;
+	cis.cislen = m->len;
+
+	/* loop through all the tuples */
+	for(i = 0; i < 1000; i++){
+		this = cis.cispos;
+		if(readc(&cis, &type) != 1)
+			break;
+		if(type == 0xFF)
+			break;
+		if(readc(&cis, &link) != 1)
+			break;
+		if(link == 0xFF)
+			break;
+
+		n = link;
+		if (link > 1 && subtuple != -1) {
+			if (readc(&cis, &c) != 1)
+				break;
+			subtype = c;
+			n--;
+		} else
+			subtype = -1;
+
+		if(type == tuple && subtype == subtuple) {
+			p = v;
+			for(l=0; l<nv && l<n; l++)
+				if(readc(&cis, p++) != 1)
+					break;
+			isaunmap(m);
+			return nv;
+		}
+		cis.cispos = this + (2+link);
+	}
+	isaunmap(m);
+	return -1;
+}
+
+static PCMmap *
+isamap(Cardbus *cb, ulong offset, int len, int attr)
+{
+	uchar we, bit;
+	PCMmap *m, *nm;
+	Pcminfo *pi;
+	int i;
+	ulong e;
+
+	pi = &cb->linfo;
+
+	/* convert offset to granularity */
+	if(len <= 0)
+		len = 1;
+	e = ROUND(offset+len, Mgran);
+	offset &= Mmask;
+	len = e - offset;
+
+	/* look for a map that covers the right area */
+	we = rdreg(cb, Rwe);
+	bit = 1;
+	nm = 0;
+	for(m = pi->mmap; m < &pi->mmap[nelem(pi->mmap)]; m++){
+		if((we & bit))
+		if(m->attr == attr)
+		if(offset >= m->ca && e <= m->cea){
+
+			m->ref++;
+			return m;
+		}
+		bit <<= 1;
+		if(nm == 0 && m->ref == 0)
+			nm = m;
+	}
+	m = nm;
+	if(m == 0)
+		return 0;
+
+	/* if isa space isn't big enough, free it and get more */
+	if(m->len < len){
+		if(m->isa){
+			umbfree(m->isa, m->len);
+			m->len = 0;
+		}
+		m->isa = PADDR(umbmalloc(0, len, Mgran));
+		if(m->isa == 0){
+			print("isamap: out of isa space\n");
+			return 0;
+		}
+		m->len = len;
+	}
+
+	/* set up new map */
+	m->ca = offset;
+	m->cea = m->ca + m->len;
+	m->attr = attr;
+	i = m - pi->mmap;
+	bit = 1<<i;
+	wrreg(cb, Rwe, we & ~bit);		/* disable map before changing it */
+	wrreg(cb, MAP(i, Mbtmlo), m->isa>>Mshift);
+	wrreg(cb, MAP(i, Mbtmhi), (m->isa>>(Mshift+8)) | F16bit);
+	wrreg(cb, MAP(i, Mtoplo), (m->isa+m->len-1)>>Mshift);
+	wrreg(cb, MAP(i, Mtophi), ((m->isa+m->len-1)>>(Mshift+8)));
+	offset -= m->isa;
+	offset &= (1<<25)-1;
+	offset >>= Mshift;
+	wrreg(cb, MAP(i, Mofflo), offset);
+	wrreg(cb, MAP(i, Moffhi), (offset>>8) | (attr ? Fregactive : 0));
+	wrreg(cb, Rwe, we | bit);		/* enable map */
+	m->ref = 1;
+
+	return m;
+}
+
+static void
+isaunmap(PCMmap* m)
+{
+	m->ref--;
+}
+
+/*
+ *  reading and writing card registers
+ */
+static uchar
+rdreg(Cardbus *cb, int index)
+{
+	outb(cb->lindex, cb->lbase + index);
+	return inb(cb->ldata);
+}
+
+static void
+wrreg(Cardbus *cb, int index, uchar val)
+{
+	outb(cb->lindex, cb->lbase + index);
+	outb(cb->ldata, val);
+}
+
+static int
+readc(Cisdat *cis, uchar *x)
+{
+	if(cis->cispos >= cis->cislen)
+		return 0;
+	*x = cis->cisbase[cis->cisskip*cis->cispos];
+	cis->cispos++;
+	return 1;
+}
+
+static ulong
+getlong(Cisdat *cis, int size)
+{
+	uchar c;
+	int i;
+	ulong x;
+
+	x = 0;
+	for(i = 0; i < size; i++){
+		if(readc(cis, &c) != 1)
+			break;
+		x |= c<<(i*8);
+	}
+	return x;
+}
+
+static void
+tcfig(Cardbus *cb, Cisdat *cis, int )
+{
+	uchar size, rasize, rmsize;
+	uchar last;
+	Pcminfo *pi;
+
+	if(readc(cis, &size) != 1)
+		return;
+	rasize = (size&0x3) + 1;
+	rmsize = ((size>>2)&0xf) + 1;
+	if(readc(cis, &last) != 1)
+		return;
+
+	pi = &cb->linfo;
+	pi->conf_addr = getlong(cis, rasize);
+	pi->conf_present = getlong(cis, rmsize);
+}
+
+static void
+tvers1(Cardbus *cb, Cisdat *cis, int )
+{
+	uchar c, major, minor, last;
+	int  i;
+	Pcminfo *pi;
+
+	pi = &cb->linfo;
+	if(readc(cis, &major) != 1)
+		return;
+	if(readc(cis, &minor) != 1)
+		return;
+	last = 0;
+	for(i = 0; i < sizeof(pi->verstr) - 1; i++){
+		if(readc(cis, &c) != 1)
+			return;
+		if(c == 0)
+			c = ';';
+		if(c == '\n')
+			c = ';';
+		if(c == 0xff)
+			break;
+		if(c == ';' && last == ';')
+			continue;
+		pi->verstr[i] = c;
+		last = c;
+	}
+	pi->verstr[i] = 0;
+}
+
+static ulong
+microvolt(Cisdat *cis)
+{
+	uchar c;
+	ulong microvolts;
+	ulong exp;
+
+	if(readc(cis, &c) != 1)
+		return 0;
+	exp = exponent[c&0x7];
+	microvolts = vmant[(c>>3)&0xf]*exp;
+	while(c & 0x80){
+		if(readc(cis, &c) != 1)
+			return 0;
+		switch(c){
+		case 0x7d:
+			break;		/* high impedence when sleeping */
+		case 0x7e:
+		case 0x7f:
+			microvolts = 0;	/* no connection */
+			break;
+		default:
+			exp /= 10;
+			microvolts += exp*(c&0x7f);
+		}
+	}
+	return microvolts;
+}
+
+static ulong
+nanoamps(Cisdat *cis)
+{
+	uchar c;
+	ulong nanoamps;
+
+	if(readc(cis, &c) != 1)
+		return 0;
+	nanoamps = exponent[c&0x7]*vmant[(c>>3)&0xf];
+	while(c & 0x80){
+		if(readc(cis, &c) != 1)
+			return 0;
+		if(c == 0x7d || c == 0x7e || c == 0x7f)
+			nanoamps = 0;
+	}
+	return nanoamps;
+}
+
+/*
+ * only nominal voltage (feature 1) is important for config,
+ * other features must read card to stay in sync.
+ */
+static ulong
+power(Cisdat *cis)
+{
+	uchar feature;
+	ulong mv;
+
+	mv = 0;
+	if(readc(cis, &feature) != 1)
+		return 0;
+	if(feature & 1)
+		mv = microvolt(cis);
+	if(feature & 2)
+		microvolt(cis);
+	if(feature & 4)
+		microvolt(cis);
+	if(feature & 8)
+		nanoamps(cis);
+	if(feature & 0x10)
+		nanoamps(cis);
+	if(feature & 0x20)
+		nanoamps(cis);
+	if(feature & 0x40)
+		nanoamps(cis);
+	return mv/1000000;
+}
+
+static ulong
+ttiming(Cisdat *cis, int scale)
+{
+	uchar unscaled;
+	ulong nanosecs;
+
+	if(readc(cis, &unscaled) != 1)
+		return 0;
+	nanosecs = (mantissa[(unscaled>>3)&0xf]*exponent[unscaled&7])/10;
+	nanosecs = nanosecs * exponent[scale];
+	return nanosecs;
+}
+
+static void
+timing(Cisdat *cis, PCMconftab *ct)
+{
+	uchar c, i;
+
+	if(readc(cis, &c) != 1)
+		return;
+	i = c&0x3;
+	if(i != 3)
+		ct->maxwait = ttiming(cis, i);		/* max wait */
+	i = (c>>2)&0x7;
+	if(i != 7)
+		ct->readywait = ttiming(cis, i);	/* max ready/busy wait */
+	i = (c>>5)&0x7;
+	if(i != 7)
+		ct->otherwait = ttiming(cis, i);	/* reserved wait */
+}
+
+static void
+iospaces(Cisdat *cis, PCMconftab *ct)
+{
+	uchar c;
+	int i, nio;
+
+	ct->nio = 0;
+	if(readc(cis, &c) != 1)
+		return;
+
+	ct->bit16 = ((c>>5)&3) >= 2;
+	if(!(c & 0x80)){
+		ct->io[0].start = 0;
+		ct->io[0].len = 1<<(c&0x1f);
+		ct->nio = 1;
+		return;
+	}
+
+	if(readc(cis, &c) != 1)
+		return;
+
+	/*
+	 * For each of the range descriptions read the
+	 * start address and the length (value is length-1).
+	 */
+	nio = (c&0xf)+1;
+	for(i = 0; i < nio; i++){
+		ct->io[i].start = getlong(cis, (c>>4)&0x3);
+		ct->io[i].len = getlong(cis, (c>>6)&0x3)+1;
+	}
+	ct->nio = nio;
+}
+
+static void
+irq(Cisdat *cis, PCMconftab *ct)
+{
+	uchar c;
+
+	if(readc(cis, &c) != 1)
+		return;
+	ct->irqtype = c & 0xe0;
+	if(c & 0x10)
+		ct->irqs = getlong(cis, 2);
+	else
+		ct->irqs = 1<<(c&0xf);
+	ct->irqs &= 0xDEB8;		/* levels available to card */
+}
+
+static void
+memspace(Cisdat *cis, int asize, int lsize, int host)
+{
+	ulong haddress, address, len;
+
+	len = getlong(cis, lsize)*256;
+	address = getlong(cis, asize)*256;
+	USED(len, address);
+	if(host){
+		haddress = getlong(cis, asize)*256;
+		USED(haddress);
+	}
+}
+
+static void
+tentry(Cardbus *cb, Cisdat *cis, int )
+{
+	uchar c, i, feature;
+	PCMconftab *ct;
+	Pcminfo *pi;
+
+	pi = &cb->linfo;
+	if(pi->nctab >= nelem(pi->ctab))
+		return;
+	if(readc(cis, &c) != 1)
+		return;
+	ct = &pi->ctab[pi->nctab++];
+
+	/* copy from last default config */
+	if(pi->defctab)
+		*ct = *pi->defctab;
+
+	ct->index = c & 0x3f;
+
+	/* is this the new default? */
+	if(c & 0x40)
+		pi->defctab = ct;
+
+	/* memory wait specified? */
+	if(c & 0x80){
+		if(readc(cis, &i) != 1)
+			return;
+		if(i&0x80)
+			ct->memwait = 1;
+	}
+
+	if(readc(cis, &feature) != 1)
+		return;
+	switch(feature&0x3){
+	case 1:
+		ct->vpp1 = ct->vpp2 = power(cis);
+		break;
+	case 2:
+		power(cis);
+		ct->vpp1 = ct->vpp2 = power(cis);
+		break;
+	case 3:
+		power(cis);
+		ct->vpp1 = power(cis);
+		ct->vpp2 = power(cis);
+		break;
+	default:
+		break;
+	}
+	if(feature&0x4)
+		timing(cis, ct);
+	if(feature&0x8)
+		iospaces(cis, ct);
+	if(feature&0x10)
+		irq(cis, ct);
+	switch((feature>>5)&0x3){
+	case 1:
+		memspace(cis, 0, 2, 0);
+		break;
+	case 2:
+		memspace(cis, 2, 2, 0);
+		break;
+	case 3:
+		if(readc(cis, &c) != 1)
+			return;
+		for(i = 0; i <= (c&0x7); i++)
+			memspace(cis, (c>>5)&0x3, (c>>3)&0x3, c&0x80);
+		break;
+	}
+}
+
+static void
+i82365probe(Cardbus *cb, int lindex, int ldata)
+{
+	uchar c, id;
+	int dev = 0;	/* According to the Ricoh spec 00->3F _and_ 80->BF seem
+				     to be the same socket A (ditto for B). */
+
+	outb(lindex, Rid + (dev<<7));
+	id = inb(ldata);
+	if((id & 0xf0) != 0x80)
+		return;		/* not a memory & I/O card */
+	if((id & 0x0f) == 0x00)
+		return;		/* no revision number, not possible */
+
+	cb->lindex = lindex;
+	cb->ldata = ldata;
+	cb->ltype = Ti82365;
+	cb->lbase = (int)(cb - cbslots) * 0x40;
+
+	switch(id){
+	case 0x82:
+	case 0x83:
+	case 0x84:
+		/* could be a cirrus */
+		outb(cb->lindex, Rchipinfo + (dev<<7));
+		outb(cb->ldata, 0);
+		c = inb(cb->ldata);
+		if((c & 0xc0) != 0xc0)
+			break;
+		c = inb(cb->ldata);
+		if((c & 0xc0) != 0x00)
+			break;
+		if(c & 0x20){
+			cb->ltype = Tpd6720;
+		} else {
+			cb->ltype = Tpd6710;
+		}
+		break;
+	}
+
+	/* if it's not a Cirrus, it could be a Vadem... */
+	if(cb->ltype == Ti82365){
+		/* unlock the Vadem extended regs */
+		outb(cb->lindex, 0x0E + (dev<<7));
+		outb(cb->lindex, 0x37 + (dev<<7));
+
+		/* make the id register show the Vadem id */
+		outb(cb->lindex, 0x3A + (dev<<7));
+		c = inb(cb->ldata);
+		outb(cb->ldata, c|0xC0);
+		outb(cb->lindex, Rid + (dev<<7));
+		c = inb(cb->ldata);
+		if(c & 0x08)
+			cb->ltype = Tvg46x;
+
+		/* go back to Intel compatible id */
+		outb(cb->lindex, 0x3A + (dev<<7));
+		c = inb(cb->ldata);
+		outb(cb->ldata, c & ~0xC0);
+	}
+}
+
+static int
+vcode(int volt)
+{
+	switch(volt){
+	case 5:
+		return 1;
+	case 12:
+		return 2;
+	default:
+		return 0;
+	}
+}
--- /dev/null
+++ b/os/boot/pc/devsd.c
@@ -1,0 +1,631 @@
+/*
+ * Storage Device.
+ */
+#include "u.h"
+#include "mem.h"
+#include "lib.h"
+#include "dat.h"
+#include "fns.h"
+#include "io.h"
+#include "ureg.h"
+#include "error.h"
+
+#include "sd.h"
+#include "fs.h"
+
+#define parttrace 0
+
+
+extern SDifc* sdifc[];
+
+static SDev* sdlist;
+static SDunit** sdunit;
+static int sdnunit;
+static int _sdmask;
+static int cdmask;
+static int sdmask;
+
+enum {
+	Rawcmd,
+	Rawdata,
+	Rawstatus,
+};
+
+void
+sdaddpart(SDunit* unit, char* name, uvlong start, uvlong end)
+{
+	SDpart *pp;
+	int i, partno;
+
+	if(parttrace)
+		print("add %d %s %s %lld %lld\n", unit->npart, unit->name, name, start, end);
+	/*
+	 * Check name not already used
+	 * and look for a free slot.
+	 */
+	if(unit->part != nil){
+		partno = -1;
+		for(i = 0; i < SDnpart; i++){
+			pp = &unit->part[i];
+			if(!pp->valid){
+				if(partno == -1)
+					partno = i;
+				break;
+			}
+			if(strcmp(name, pp->name) == 0){
+				if(pp->start == start && pp->end == end){
+					if(parttrace)
+						print("already present\n");
+					return;
+				}
+			}
+		}
+	}else{
+		if((unit->part = malloc(sizeof(SDpart)*SDnpart)) == nil){
+			if(parttrace)
+				print("malloc failed\n");
+			return;
+		}
+		partno = 0;
+	}
+
+	/*
+	 * Check there is a free slot and size and extent are valid.
+	 */
+	if(partno == -1 || start > end || end > unit->sectors){
+		print("cannot add %s!%s [%llud,%llud) to disk [0,%llud): %s\n",
+			unit->name, name, start, end, unit->sectors, 
+			partno==-1 ? "no free partitions" : "partition boundaries out of range");
+		return;
+	}
+	pp = &unit->part[partno];
+	pp->start = start;
+	pp->end = end;
+	strncpy(pp->name, name, NAMELEN);
+	pp->valid = 1;
+	unit->npart++;
+}
+
+void
+sddelpart(SDunit* unit,  char* name)
+{
+	int i;
+	SDpart *pp;
+
+	if(parttrace)
+		print("del %d %s %s\n", unit->npart, unit->name, name);
+	/*
+	 * Look for the partition to delete.
+	 * Can't delete if someone still has it open.
+	 * If it's the last valid partition zap the
+	 * whole table.
+	 */
+	pp = unit->part;
+	for(i = 0; i < SDnpart; i++){
+		if(strncmp(name, pp->name, NAMELEN) == 0)
+			break;
+		pp++;
+	}
+	if(i >= SDnpart)
+		return;
+	pp->valid = 0;
+
+	unit->npart--;
+	if(unit->npart == 0){
+		free(unit->part);
+		unit->part = nil;
+	}
+}
+
+static int
+sdinitpart(SDunit* unit)
+{
+	unit->sectors = unit->secsize = 0;
+	unit->npart = 0;
+	if(unit->part){
+		free(unit->part);
+		unit->part = nil;
+	}
+
+	if(unit->inquiry[0] & 0xC0)
+		return 0;
+	switch(unit->inquiry[0] & 0x1F){
+	case 0x00:			/* DA */
+	case 0x04:			/* WORM */
+	case 0x05:			/* CD-ROM */
+	case 0x07:			/* MO */
+		break;
+	default:
+		return 0;
+	}
+
+	if(unit->dev->ifc->online == nil || unit->dev->ifc->online(unit) == 0)
+		return 0;
+	sdaddpart(unit, "data", 0, unit->sectors);
+	return 1;
+}
+
+static SDunit*
+sdgetunit(SDev* sdev, int subno)
+{
+	int index;
+	SDunit *unit;
+
+	/*
+	 * Associate a unit with a given device and sub-unit
+	 * number on that device.
+	 * The device will be probed if it has not already been
+	 * successfully accessed.
+	 */
+	qlock(&sdqlock);
+	index = sdev->index+subno;
+	unit = sdunit[index];
+	if(unit == nil){
+		if((unit = malloc(sizeof(SDunit))) == nil){
+			qunlock(&sdqlock);
+			return nil;
+		}
+
+		if(sdev->enabled == 0 && sdev->ifc->enable)
+			sdev->ifc->enable(sdev);
+		sdev->enabled = 1;
+
+		snprint(unit->name, NAMELEN, "sd%c%d", sdev->idno, subno);
+		unit->subno = subno;
+		unit->dev = sdev;
+
+		/*
+		 * No need to lock anything here as this is only
+		 * called before the unit is made available in the
+		 * sdunit[] array.
+		 */
+		if(unit->dev->ifc->verify(unit) == 0){
+			qunlock(&sdqlock);
+			free(unit);
+			return nil;
+		}
+		sdunit[index] = unit;
+	}
+	qunlock(&sdqlock);
+
+	return unit;
+}
+
+static SDunit*
+sdindex2unit(int index)
+{
+	SDev *sdev;
+
+	/*
+	 * Associate a unit with a given index into the top-level
+	 * device directory.
+	 * The device will be probed if it has not already been
+	 * successfully accessed.
+	 */
+	for(sdev = sdlist; sdev != nil; sdev = sdev->next){
+		if(index >= sdev->index && index < sdev->index+sdev->nunit)
+			return sdgetunit(sdev, index-sdev->index);
+	}
+
+	return nil;
+}
+
+static void
+_sddetach(void)
+{
+	SDev *sdev;
+
+	for(sdev = sdlist; sdev != nil; sdev = sdev->next){
+		if(sdev->enabled == 0)
+			continue;
+		if(sdev->ifc->disable)
+			sdev->ifc->disable(sdev);
+		sdev->enabled = 0;
+	}
+}
+
+static void
+sddump(void)
+{
+	SDev *sdev;
+
+	print("sdevs:\n");
+	for(sdev = sdlist; sdev != nil; sdev = sdev->next){
+		print("sdev %c index %d nunit %d: ",
+			sdev->idno, sdev->index, sdev->nunit);
+		print("\n");
+	}
+}
+
+static int
+_sdinit(void)
+{
+	ulong m;
+	int i;
+	SDev *sdev, *tail;
+	SDunit *unit;
+
+	/*
+	 * Probe all configured controllers and make a list
+	 * of devices found, accumulating a possible maximum number
+	 * of units attached and marking each device with an index
+	 * into the linear top-level directory array of units.
+	 */
+	tail = nil;
+	for(i = 0; sdifc[i] != nil; i++){
+		if((sdev = sdifc[i]->pnp()) == nil)
+			continue;
+		if(sdlist != nil)
+			tail->next = sdev;
+		else
+			sdlist = sdev;
+		for(tail = sdev; tail->next != nil; tail = tail->next){
+			tail->index = sdnunit;
+			sdnunit += tail->nunit;
+		}
+		tail->index = sdnunit;
+		sdnunit += tail->nunit;
+	}
+	/*
+	 * Legacy and option code goes here. This will be hard...
+	 */
+
+	/*
+	 * The maximum number of possible units is known, allocate
+	 * placeholders for their datastructures; the units will be
+	 * probed and structures allocated when attached.
+	 * Allocate controller names for the different types.
+	 */
+	if(sdnunit == 0)
+		return 0;
+	if((sdunit = malloc(sdnunit*sizeof(SDunit*))) == nil)
+		return 0;
+	sddetach = _sddetach;
+
+	for(i = 0; sdifc[i] != nil; i++){
+		if(sdifc[i]->id)
+			sdifc[i]->id(sdlist);
+	}
+	if (0)
+		sddump();
+
+	m = 0;
+	cdmask = sdmask = 0;
+	for(i=0; i<sdnunit && i < 32; i++) {
+		unit = sdindex2unit(i);
+		if(unit == nil)
+			continue;
+		sdinitpart(unit);
+		partition(unit);
+		if(unit->npart > 0){	/* BUG */
+			if((unit->inquiry[0] & 0x1F) == 0x05)
+				cdmask |= (1<<i);
+			else
+				sdmask |= (1<<i);
+			m |= (1<<i);
+		}
+	}
+
+//notesdinfo();
+	_sdmask = m;
+	return m;
+}
+
+int
+cdinit(void)
+{
+	if(sdnunit == 0)
+		_sdinit();
+	return cdmask;
+}
+
+int
+sdinit(void)
+{
+	if(sdnunit == 0)
+		_sdinit();
+	return sdmask;
+}
+
+void
+sdinitdev(int i, char *s)
+{
+	SDunit *unit;
+
+	unit = sdindex2unit(i);
+	strcpy(s, unit->name);
+}
+
+void
+sdprintdevs(int i)
+{
+	char *s;
+	SDunit *unit;
+
+	unit = sdindex2unit(i);
+	for(i=0; i<unit->npart; i++){
+		s = unit->part[i].name;
+		if(strncmp(s, "dos", 3) == 0
+		|| strncmp(s, "9fat", 4) == 0
+		|| strncmp(s, "fs", 2) == 0)
+			print(" %s!%s", unit->name, s);
+	}
+}
+
+SDpart*
+sdfindpart(SDunit *unit, char *name)
+{
+	int i;
+
+	if(parttrace)
+		print("findpart %d %s %s\t\n", unit->npart, unit->name, name);
+	for(i=0; i<unit->npart; i++) {
+		if(parttrace)
+			print("%s...", unit->part[i].name);
+		if(strcmp(unit->part[i].name, name) == 0){
+			if(parttrace)
+				print("\n");
+			return &unit->part[i];
+		}
+	}
+	if(parttrace)
+		print("not found\n");
+	return nil;
+}
+
+typedef struct Scsicrud Scsicrud;
+struct Scsicrud {
+	Fs fs;
+	vlong offset;
+	SDunit *unit;
+	SDpart *part;
+};
+
+long
+sdread(Fs *vcrud, void *v, long n)
+{
+	Scsicrud *crud;
+	long x;
+
+	crud = (Scsicrud*)vcrud;
+	x = sdbio(crud->unit, crud->part, v, n, crud->offset);
+	if(x > 0)
+		crud->offset += x;
+	return x;
+}
+
+vlong
+sdseek(Fs *vcrud, vlong seek)
+{
+	((Scsicrud*)vcrud)->offset = seek;
+	return seek;
+}
+
+void*
+sdgetfspart(int i, char *s, int chatty)
+{
+	SDunit *unit;
+	SDpart *p;
+	Scsicrud *crud;
+
+	if(cdmask&(1<<i)){
+		if(strcmp(s, "cdboot") != 0)
+			return nil;
+	}else if(sdmask&(1<<i)){
+		if(strcmp(s, "cdboot") == 0)
+			return nil;
+	}
+
+	unit = sdindex2unit(i);
+	if((p = sdfindpart(unit, s)) == nil){
+		if(chatty)
+			print("unknown partition %s!%s\n", unit->name, s);
+		return nil;
+	}
+	if(p->crud == nil) {
+		crud = malloc(sizeof(Scsicrud));
+		crud->fs.dev = i;
+		crud->fs.diskread = sdread;
+		crud->fs.diskseek = sdseek;
+	//	crud->start = 0;
+		crud->unit = unit;
+		crud->part = p;
+		if(dosinit(&crud->fs) < 0 && dosinit(&crud->fs) < 0 && kfsinit(&crud->fs) < 0){
+			if(chatty)
+				print("partition %s!%s does not contain a DOS or KFS file system\n",
+					unit->name, s);
+			return nil;
+		}
+		p->crud = crud;
+	}
+	return p->crud;
+}
+
+/*
+ * Leave partitions around for devsd to pick up.
+ * (Needed by boot process; more extensive 
+ * partitioning is done by termrc or cpurc).
+ */
+void
+sdaddconf(int i)
+{
+	SDunit *unit;
+	SDpart *pp;
+
+	unit = sdindex2unit(i);
+	
+	/*
+	 * If there were no partitions (just data and partition), don't bother.
+	 */
+	if(unit->npart<= 1 || (unit->npart==2 && strcmp(unit->part[1].name, "partition")==0))
+		return;
+
+	addconf("%spart=", unit->name);
+	for(i=1, pp=&unit->part[i]; i<unit->npart; i++, pp++)	/* skip 0, which is "data" */
+		addconf("%s%s %lld %lld", i==1 ? "" : "/", pp->name,
+			pp->start, pp->end);
+	addconf("\n");
+}
+
+int
+sdboot(int dev, char *pname, Boot *b)
+{
+	char *file;
+	Fs *fs;
+
+	if((file = strchr(pname, '!')) == nil) {
+		print("syntax is sdC0!partition!file\n");
+		return -1;
+	}
+	*file++ = '\0';
+
+	fs = sdgetfspart(dev, pname, 1);
+	if(fs == nil)
+		return -1;
+
+	return fsboot(fs, file, b);
+}
+
+long
+sdbio(SDunit *unit, SDpart *pp, void* va, long len, vlong off)
+{
+	long l;
+	ulong bno, max, nb, offset;
+	static uchar *b;
+	char *a;
+	static ulong bsz;
+
+	a = va;
+memset(a, 0xDA, len);
+	qlock(&unit->ctl);
+	if(unit->changed){
+		qunlock(&unit->ctl);
+		return 0;
+	}
+
+	/*
+	 * Check the request is within bounds.
+	 * Removeable drives are locked throughout the I/O
+	 * in case the media changes unexpectedly.
+	 * Non-removeable drives are not locked during the I/O
+	 * to allow the hardware to optimise if it can; this is
+	 * a little fast and loose.
+	 * It's assumed that non-removable media parameters
+	 * (sectors, secsize) can't change once the drive has
+	 * been brought online.
+	 */
+	bno = (off/unit->secsize) + pp->start;
+	nb = ((off+len+unit->secsize-1)/unit->secsize) + pp->start - bno;
+	max = SDmaxio/unit->secsize;
+	if(nb > max)
+		nb = max;
+	if(bno+nb > pp->end)
+		nb = pp->end - bno;
+	if(bno >= pp->end || nb == 0){
+		qunlock(&unit->ctl);
+		return 0;
+	}
+	if(!(unit->inquiry[1] & 0x80))
+		qunlock(&unit->ctl);
+
+	if(bsz < nb*unit->secsize){
+		b = malloc(nb*unit->secsize);
+		bsz = nb*unit->secsize;
+	}
+//	b = sdmalloc(nb*unit->secsize);
+//	if(b == nil)
+//		return 0;
+
+	offset = off%unit->secsize;
+	if((l = unit->dev->ifc->bio(unit, 0, 0, b, nb, bno)) < 0) {
+//		sdfree(b);
+		return 0;
+	}
+
+	if(l < offset)
+		len = 0;
+	else if(len > l - offset)
+		len = l - offset;
+	if(len)
+		memmove(a, b+offset, len);
+//	sdfree(b);
+
+	if(unit->inquiry[1] & 0x80)
+		qunlock(&unit->ctl);
+
+	return len;
+}
+
+#ifdef DMA
+long
+sdrio(SDreq *r, void* a, long n)
+{
+	if(n >= SDmaxio || n < 0)
+		return 0;
+
+	r->data = nil;
+	if(n){
+		if((r->data = malloc(n)) == nil)
+			return 0;
+		if(r->write)
+			memmove(r->data, a, n);
+	}
+	r->dlen = n;
+
+	if(r->unit->dev->ifc->rio(r) != SDok){
+// cgascreenputs("1", 1);
+		if(r->data != nil){
+			sdfree(r->data);
+			r->data = nil;
+		}
+		return 0;
+	}
+// cgascreenputs("2", 1);
+
+	if(!r->write && r->rlen > 0)
+		memmove(a, r->data, r->rlen);
+// cgascreenputs("3", 1);
+	if(r->data != nil){
+		sdfree(r->data);
+		r->data = nil;
+	}
+
+// cgascreenputs("4", 1);
+	return r->rlen;
+}
+#endif /* DMA */
+
+void
+sleep(void*, int (*fn)(void*), void *v)
+{
+	int x;
+	x = spllo();
+	while(!fn(v))
+		;
+	splx(x);
+	return;
+}
+
+void
+tsleep(void*, int (*fn)(void*), void *v, int msec)
+{
+	int x;
+	ulong start;
+
+	x = spllo();
+	for(start = m->ticks; TK2MS(m->ticks - start) < msec
+		&& !fn(v); )
+		;
+	splx(x);
+	return;
+}
+
+void*
+sdmalloc(void *p, ulong sz)
+{
+	if(p != nil) {
+		memset(p, 0, sz);
+		return p;
+	}
+	return malloc(sz);
+}
--- /dev/null
+++ b/os/boot/pc/dma.c
@@ -1,0 +1,245 @@
+#include	"u.h"
+#include	"lib.h"
+#include	"mem.h"
+#include	"dat.h"
+#include	"fns.h"
+
+typedef struct DMAport	DMAport;
+typedef struct DMA	DMA;
+typedef struct DMAxfer	DMAxfer;
+
+enum
+{
+	/*
+	 *  the byte registers for DMA0 are all one byte apart
+	 */
+	Dma0=		0x00,
+	Dma0status=	Dma0+0x8,	/* status port */
+	Dma0reset=	Dma0+0xD,	/* reset port */
+
+	/*
+	 *  the byte registers for DMA1 are all two bytes apart (why?)
+	 */
+	Dma1=		0xC0,
+	Dma1status=	Dma1+2*0x8,	/* status port */
+	Dma1reset=	Dma1+2*0xD,	/* reset port */
+};
+
+/*
+ *  state of a dma transfer
+ */
+struct DMAxfer
+{
+	ulong	bpa;		/* bounce buffer physical address */
+	void*	bva;		/* bounce buffer virtual address */
+	void*	va;		/* virtual address destination/src */
+	long	len;		/* bytes to be transferred */
+	int	isread;
+};
+
+/*
+ *  the dma controllers.  the first half of this structure specifies
+ *  the I/O ports used by the DMA controllers.
+ */
+struct DMAport
+{
+	uchar	addr[4];	/* current address (4 channels) */
+	uchar	count[4];	/* current count (4 channels) */
+	uchar	page[4];	/* page registers (4 channels) */
+	uchar	cmd;		/* command status register */
+	uchar	req;		/* request registers */
+	uchar	sbm;		/* single bit mask register */
+	uchar	mode;		/* mode register */
+	uchar	cbp;		/* clear byte pointer */
+	uchar	mc;		/* master clear */
+	uchar	cmask;		/* clear mask register */
+	uchar	wam;		/* write all mask register bit */
+};
+
+struct DMA
+{
+	DMAport;
+	int	shift;
+	Lock;
+	DMAxfer	x[4];
+};
+
+DMA dma[2] = {
+	{ 0x00, 0x02, 0x04, 0x06,
+	  0x01, 0x03, 0x05, 0x07,
+	  0x87, 0x83, 0x81, 0x82,
+	  0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f,
+	 0 },
+
+	{ 0xc0, 0xc4, 0xc8, 0xcc,
+	  0xc2, 0xc6, 0xca, 0xce,
+	  0x8f, 0x8b, 0x89, 0x8a,
+	  0xd0, 0xd2, 0xd4, 0xd6, 0xd8, 0xda, 0xdc, 0xde,
+	 1 },
+};
+
+/*
+ *  DMA must be in the first 16MB.  This gets called early by the
+ *  initialisation routines of any devices which require DMA to ensure
+ *  the allocated bounce buffers are below the 16MB limit.
+ */
+void
+dmainit(int chan)
+{
+	DMA *dp;
+	DMAxfer *xp;
+	ulong v;
+	static int once;
+
+	if(once == 0){
+//		if(ioalloc(0x00, 0x10, 0, "dma") < 0
+//		|| ioalloc(0x80, 0x10, 0, "dma") < 0
+//		|| ioalloc(0xd0, 0x10, 0, "dma") < 0)
+//			panic("dmainit");
+		outb(dma[0].mc, 0);
+		outb(dma[1].mc, 0);
+		outb(dma[0].cmask, 0);
+		outb(dma[1].cmask, 0);
+		outb(dma[1].mode, 0xC0);
+		once = 1;
+	}
+
+	dp = &dma[(chan>>2)&1];
+	chan = chan & 3;
+	xp = &dp->x[chan];
+	if(xp->bva != nil)
+		return;
+
+	v = (ulong)xalloc(BY2PG+BY2PG);
+	if(v == 0 || PADDR(v) >= 16*MB){
+		print("dmainit: chan %d: 0x%luX out of range\n", chan, v);
+		xfree((void*)v);
+		v = 0;
+	}
+	xp->bva = (void*)ROUND(v, BY2PG);
+	xp->bpa = PADDR(xp->bva);
+	xp->len = 0;
+	xp->isread = 0;
+}
+
+/*
+ *  setup a dma transfer.  if the destination is not in kernel
+ *  memory, allocate a page for the transfer.
+ *
+ *  we assume BIOS has set up the command register before we
+ *  are booted.
+ *
+ *  return the updated transfer length (we can't transfer across 64k
+ *  boundaries)
+ */
+long
+dmasetup(int chan, void *va, long len, int isread)
+{
+	DMA *dp;
+	ulong pa;
+	uchar mode;
+	DMAxfer *xp;
+
+	dp = &dma[(chan>>2)&1];
+	chan = chan & 3;
+	xp = &dp->x[chan];
+
+	/*
+	 *  if this isn't kernel memory or crossing 64k boundary or above 16 meg
+	 *  use the allocated low memory page.
+	 */
+	pa = PADDR(va);
+	if((((ulong)va)&0xF0000000) != KZERO
+	|| (pa&0xFFFF0000) != ((pa+len)&0xFFFF0000)
+	|| pa > 16*MB) {
+		if(xp->bva == nil)
+			return -1;
+		if(len > BY2PG)
+			len = BY2PG;
+		if(!isread)
+			memmove(xp->bva, va, len);
+		xp->va = va;
+		xp->len = len;
+		xp->isread = isread;
+		pa = xp->bpa;
+	}
+	else
+		xp->len = 0;
+
+	/*
+	 * this setup must be atomic
+	 */
+	ilock(dp);
+	mode = (isread ? 0x44 : 0x48) | chan;
+	outb(dp->mode, mode);	/* single mode dma (give CPU a chance at mem) */
+	outb(dp->page[chan], pa>>16);
+	outb(dp->cbp, 0);		/* set count & address to their first byte */
+	outb(dp->addr[chan], pa>>dp->shift);		/* set address */
+	outb(dp->addr[chan], pa>>(8+dp->shift));
+	outb(dp->count[chan], (len>>dp->shift)-1);		/* set count */
+	outb(dp->count[chan], ((len>>dp->shift)-1)>>8);
+	outb(dp->sbm, chan);		/* enable the channel */
+	iunlock(dp);
+
+	return len;
+}
+
+int
+dmadone(int chan)
+{
+	DMA *dp;
+
+	dp = &dma[(chan>>2)&1];
+	chan = chan & 3;
+
+	return inb(dp->cmd) & (1<<chan);
+}
+
+/*
+ *  this must be called after a dma has been completed.
+ *
+ *  if a page has been allocated for the dma,
+ *  copy the data into the actual destination
+ *  and free the page.
+ */
+void
+dmaend(int chan)
+{
+	DMA *dp;
+	DMAxfer *xp;
+
+	dp = &dma[(chan>>2)&1];
+	chan = chan & 3;
+
+	/*
+	 *  disable the channel
+	 */
+	ilock(dp);
+	outb(dp->sbm, 4|chan);
+	iunlock(dp);
+
+	xp = &dp->x[chan];
+	if(xp->len == 0 || !xp->isread)
+		return;
+
+	/*
+	 *  copy out of temporary page
+	 */
+	memmove(xp->va, xp->bva, xp->len);
+	xp->len = 0;
+}
+
+/*
+int
+dmacount(int chan)
+{
+	int     retval;
+	DMA     *dp;
+ 
+	dp = &dma[(chan>>2)&1];
+	outb(dp->cbp, 0);
+	retval = inb(dp->count[chan]);
+	retval |= inb(dp->count[chan]) << 8;
+	return((retval<<dp->shift)+1);
+}
+ */
--- /dev/null
+++ b/os/boot/pc/dosboot.c
@@ -1,0 +1,582 @@
+#include	"u.h"
+#include	"lib.h"
+#include	"mem.h"
+#include	"dat.h"
+#include	"fns.h"
+#include	"fs.h"
+
+struct Dosboot{
+	uchar	magic[3];
+	uchar	version[8];
+	uchar	sectsize[2];
+	uchar	clustsize;
+	uchar	nresrv[2];
+	uchar	nfats;
+	uchar	rootsize[2];
+	uchar	volsize[2];
+	uchar	mediadesc;
+	uchar	fatsize[2];
+	uchar	trksize[2];
+	uchar	nheads[2];
+	uchar	nhidden[4];
+	uchar	bigvolsize[4];
+/* fat 32 */
+	uchar	bigfatsize[4];
+	uchar	extflags[2];
+	uchar	fsversion[2];
+	uchar	rootdirstartclust[4];
+	uchar	fsinfosect[2];
+	uchar	backupbootsect[2];
+/* ???
+	uchar	driveno;
+	uchar	reserved0;
+	uchar	bootsig;
+	uchar	volid[4];
+	uchar	label[11];
+	uchar	reserved1[8];
+*/
+};
+
+struct Dosdir{
+	uchar	name[8];
+	uchar	ext[3];
+	uchar	attr;
+	uchar	lowercase;
+	uchar	hundredth;
+	uchar	ctime[2];
+	uchar	cdate[2];
+	uchar	adate[2];
+	uchar	highstart[2];
+	uchar	mtime[2];
+	uchar	mdate[2];
+	uchar	start[2];
+	uchar	length[4];
+};
+
+#define	DOSRONLY	0x01
+#define	DOSHIDDEN	0x02
+#define	DOSSYSTEM	0x04
+#define	DOSVLABEL	0x08
+#define	DOSDIR	0x10
+#define	DOSARCH	0x20
+
+/*
+ *  predeclared
+ */
+static void	bootdump(Dosboot*);
+static void	setname(Dosfile*, char*);
+
+/*
+ *  debugging
+ */
+#define chatty	0
+#define chat	if(chatty)print
+
+/*
+ *  block io buffers
+ */
+enum
+{
+	Nbio=	16,
+};
+typedef struct	Clustbuf	Clustbuf;
+struct Clustbuf
+{
+	int	age;
+	long	sector;
+	uchar	*iobuf;
+	Dos	*dos;
+	int	size;
+};
+Clustbuf	bio[Nbio];
+
+/*
+ *  get an io block from an io buffer
+ */
+Clustbuf*
+getclust(Dos *dos, long sector)
+{
+	Fs *fs;
+	Clustbuf *p, *oldest;
+	int size;
+
+	chat("getclust @ %ld\n", sector);
+
+	/*
+	 *  if we have it, just return it
+	 */
+	for(p = bio; p < &bio[Nbio]; p++){
+		if(sector == p->sector && dos == p->dos){
+			p->age = m->ticks;
+			chat("getclust %ld in cache\n", sector);
+			return p;
+		}
+	}
+
+	/*
+	 *  otherwise, reuse the oldest entry
+	 */
+	oldest = bio;
+	for(p = &bio[1]; p < &bio[Nbio]; p++){
+		if(p->age <= oldest->age)
+			oldest = p;
+	}
+	p = oldest;
+
+	/*
+	 *  make sure the buffer is big enough
+	 */
+	size = dos->clustsize*dos->sectsize;
+	if(p->iobuf==0 || p->size < size)
+		p->iobuf = ialloc(size, 0);
+	p->size = size;
+
+	/*
+	 *  read in the cluster
+	 */
+	fs = (Fs*)dos;
+	chat("getclust addr %lud %p %p %p\n", (ulong)((sector+dos->start)*(vlong)dos->sectsize),
+		fs, fs->diskseek, fs->diskread);
+	if(fs->diskseek(fs, (sector+dos->start)*(vlong)dos->sectsize) < 0){
+		chat("can't seek block\n");
+		return 0;
+	}
+	if(fs->diskread(fs, p->iobuf, size) != size){
+		chat("can't read block\n");
+		return 0;
+	}
+
+	p->age = m->ticks;
+	p->dos = dos;
+	p->sector = sector;
+	chat("getclust %ld read\n", sector);
+	return p;
+}
+
+/*
+ *  walk the fat one level ( n is a current cluster number ).
+ *  return the new cluster number or -1 if no more.
+ */
+static long
+fatwalk(Dos *dos, int n)
+{
+	ulong k, sect;
+	Clustbuf *p;
+	int o;
+
+	chat("fatwalk %d\n", n);
+
+	if(n < 2 || n >= dos->fatclusters)
+		return -1;
+
+	switch(dos->fatbits){
+	case 12:
+		k = (3*n)/2; break;
+	case 16:
+		k = 2*n; break;
+	case 32:
+		k = 4*n; break;
+	default:
+		return -1;
+	}
+	if(k >= dos->fatsize*dos->sectsize)
+		panic("getfat");
+
+	sect = (k/(dos->sectsize*dos->clustsize))*dos->clustsize + dos->fataddr;
+	o = k%(dos->sectsize*dos->clustsize);
+	p = getclust(dos, sect);
+	k = p->iobuf[o++];
+	if(o >= dos->sectsize*dos->clustsize){
+		p = getclust(dos, sect+dos->clustsize);
+		o = 0;
+	}
+	k |= p->iobuf[o++]<<8;
+	if(dos->fatbits == 12){
+		if(n&1)
+			k >>= 4;
+		else
+			k &= 0xfff;
+		if(k >= 0xff8)
+			k = -1;
+	}
+	else if (dos->fatbits == 32){
+		if(o >= dos->sectsize*dos->clustsize){
+			p = getclust(dos, sect+dos->clustsize);
+			o = 0;
+		}
+		k |= p->iobuf[o++]<<16;
+		k |= p->iobuf[o]<<24;
+		if (k >= 0xfffffff8)
+			k = -1;
+	}
+	else
+		k = k < 0xfff8 ? k : -1;
+	chat("fatwalk %d -> %lud\n", n, k);
+	return k;
+}
+
+/*
+ *  map a file's logical cluster address to a physical sector address
+ */
+static long
+fileaddr(Dosfile *fp, long ltarget)
+{
+	Dos *dos = fp->dos;
+	long l;
+	long p;
+
+	chat("fileaddr %8.8s %ld\n", fp->name, ltarget);
+	/*
+	 *  root directory is contiguous and easy (unless FAT32)
+	 */
+	if(fp->pstart == 0 && dos->rootsize != 0) {
+		if(ltarget*dos->sectsize*dos->clustsize >= dos->rootsize*sizeof(Dosdir))
+			return -1;
+		l = dos->rootaddr + ltarget*dos->clustsize;
+		chat("fileaddr %ld -> %ld\n", ltarget, l);
+		return l;
+	}
+
+	/*
+	 *  anything else requires a walk through the fat
+	 */
+	if(ltarget >= fp->lcurrent && fp->pcurrent){
+		/* start at the currrent point */
+		l = fp->lcurrent;
+		p = fp->pcurrent;
+	} else {
+		/* go back to the beginning */
+		l = 0;
+		p = fp->pstart;
+	}
+	while(l != ltarget){
+		/* walk the fat */
+		p = fatwalk(dos, p);
+		if(p < 0)
+			return -1;
+		l++;
+	}
+	fp->lcurrent = l;
+	fp->pcurrent = p;
+
+	/*
+	 *  clusters start at 2 instead of 0 (why? - presotto)
+	 */
+	l =  dos->dataaddr + (p-2)*dos->clustsize;
+	chat("fileaddr %ld -> %ld\n", ltarget, l);
+	return l;
+}
+
+/*
+ *  read from a dos file
+ */
+long
+dosread(Dosfile *fp, void *a, long n)
+{
+	long addr;
+	long rv;
+	int i;
+	int off;
+	Clustbuf *p;
+	uchar *from, *to;
+
+	if((fp->attr & DOSDIR) == 0){
+		if(fp->offset >= fp->length)
+			return 0;
+		if(fp->offset+n > fp->length)
+			n = fp->length - fp->offset;
+	}
+
+	to = a;
+	for(rv = 0; rv < n; rv+=i){
+		/*
+		 *  read the cluster
+		 */
+		addr = fileaddr(fp, fp->offset/fp->dos->clustbytes);
+		if(addr < 0)
+			return -1;
+		p = getclust(fp->dos, addr);
+		if(p == 0)
+			return -1;
+
+		/*
+		 *  copy the bytes we need
+		 */
+		off = fp->offset % fp->dos->clustbytes;
+		from = &p->iobuf[off];
+		i = n - rv;
+		if(i > fp->dos->clustbytes - off)
+			i = fp->dos->clustbytes - off;
+		memmove(to, from, i);
+		to += i;
+		fp->offset += i;
+	}
+
+	return rv;
+}
+
+/*
+ *  walk a directory returns
+ * 	-1 if something went wrong
+ *	 0 if not found
+ *	 1 if found
+ */
+int
+doswalk(File *f, char *name)
+{
+	Dosdir d;
+	long n;
+	Dosfile *file;
+
+	chat("doswalk %s\n", name);
+
+	file = &f->dos;
+
+	if((file->attr & DOSDIR) == 0){
+		chat("walking non-directory!\n");
+		return -1;
+	}
+
+	setname(file, name);
+
+	file->offset = 0;	/* start at the beginning */
+	while((n = dosread(file, &d, sizeof(d))) == sizeof(d)){
+		chat("comparing to %8.8s.%3.3s\n", (char*)d.name, (char*)d.ext);
+		if(memcmp(file->name, d.name, sizeof(d.name)) != 0)
+			continue;
+		if(memcmp(file->ext, d.ext, sizeof(d.ext)) != 0)
+			continue;
+		if(d.attr & DOSVLABEL){
+			chat("%8.8s.%3.3s is a LABEL\n", (char*)d.name, (char*)d.ext);
+			continue;
+		}
+		file->attr = d.attr;
+		file->pstart = GSHORT(d.start);
+		if (file->dos->fatbits == 32)
+			file->pstart |= GSHORT(d.highstart) << 16;
+		file->length = GLONG(d.length);
+		file->pcurrent = 0;
+		file->lcurrent = 0;
+		file->offset = 0;
+		return 1;
+	}
+	return n >= 0 ? 0 : -1;
+}
+
+/*
+ *  instructions that boot blocks can start with
+ */
+#define	JMPSHORT	0xeb
+#define JMPNEAR		0xe9
+
+/*
+ *  read in a segment
+ */
+long
+dosreadseg(File *f, void *va, long len)
+{
+	char *a;
+	long n, sofar;
+	Dosfile *fp;
+
+	fp = &f->dos;
+	a = va;
+	for(sofar = 0; sofar < len; sofar += n){
+		n = 8*1024;
+		if(len - sofar < n)
+			n = len - sofar;
+		n = dosread(fp, a + sofar, n);
+		if(n <= 0)
+			break;
+		print(".");
+	}
+	return sofar;
+}
+
+int
+dosinit(Fs *fs)
+{
+	Clustbuf *p;
+	Dosboot *b;
+	int i;
+	Dos *dos;
+	Dosfile *root;
+
+chat("dosinit0 %p %p %p\n", fs, fs->diskseek, fs->diskread);
+
+	dos = &fs->dos;
+	/* defaults till we know better */
+	dos->sectsize = 512;
+	dos->clustsize = 1;
+
+	/* get first sector */
+	p = getclust(dos, 0);
+	if(p == 0){
+		chat("can't read boot block\n");
+		return -1;
+	}
+
+chat("dosinit0a\n");
+
+	p->dos = 0;
+	b = (Dosboot *)p->iobuf;
+	if(b->magic[0] != JMPNEAR && (b->magic[0] != JMPSHORT || b->magic[2] != 0x90)){
+		chat("no dos file system %x %x %x %x\n",
+			b->magic[0], b->magic[1], b->magic[2], b->magic[3]);
+		return -1;
+	}
+
+	if(chatty)
+		bootdump(b);
+
+	if(b->clustsize == 0) {
+unreasonable:
+		if(chatty){
+			print("unreasonable FAT BPB: ");
+			for(i=0; i<3+8+2+1; i++)
+				print(" %.2ux", p->iobuf[i]);
+			print("\n");
+		}
+		return -1;
+	}
+
+chat("dosinit1\n");
+
+	/*
+	 * Determine the systems' wondrous properties.
+	 * There are heuristics here, but there's no real way
+	 * of knowing if this is a reasonable FAT.
+	 */
+	dos->fatbits = 0;
+	dos->sectsize = GSHORT(b->sectsize);
+	if(dos->sectsize & 0xFF)
+		goto unreasonable;
+	dos->clustsize = b->clustsize;
+	dos->clustbytes = dos->sectsize*dos->clustsize;
+	dos->nresrv = GSHORT(b->nresrv);
+	dos->nfats = b->nfats;
+	dos->fatsize = GSHORT(b->fatsize);
+	dos->rootsize = GSHORT(b->rootsize);
+	dos->volsize = GSHORT(b->volsize);
+	if(dos->volsize == 0)
+		dos->volsize = GLONG(b->bigvolsize);
+	dos->mediadesc = b->mediadesc;
+	if(dos->fatsize == 0) {
+		chat("fat32\n");
+		dos->rootsize = 0;
+		dos->fatsize = GLONG(b->bigfatsize);
+		dos->fatbits = 32;
+	}
+	dos->fataddr = dos->nresrv;
+	if (dos->rootsize == 0) {
+		dos->rootaddr = 0;
+		dos->rootclust = GLONG(b->rootdirstartclust);
+		dos->dataaddr = dos->fataddr + dos->nfats*dos->fatsize;
+	} else {
+		dos->rootaddr = dos->fataddr + dos->nfats*dos->fatsize;
+		i = dos->rootsize*sizeof(Dosdir) + dos->sectsize - 1;
+		i = i/dos->sectsize;
+		dos->dataaddr = dos->rootaddr + i;
+	}
+	dos->fatclusters = 2+(dos->volsize - dos->dataaddr)/dos->clustsize;
+	if(dos->fatbits != 32) {
+		if(dos->fatclusters < 4087)
+			dos->fatbits = 12;
+		else
+			dos->fatbits = 16;
+	}
+	dos->freeptr = 2;
+
+	if(dos->clustbytes < 512 || dos->clustbytes > 64*1024)
+		goto unreasonable;
+
+chat("dosinit2\n");
+
+	/*
+	 *  set up the root
+	 */
+
+	fs->root.fs = fs;
+	root = &fs->root.dos;
+	root->dos = dos;
+	root->pstart = dos->rootsize == 0 ? dos->rootclust : 0;
+	root->pcurrent = root->lcurrent = 0;
+	root->offset = 0;
+	root->attr = DOSDIR;
+	root->length = dos->rootsize*sizeof(Dosdir);
+
+chat("dosinit3\n");
+
+	fs->read = dosreadseg;
+	fs->walk = doswalk;
+	return 0;
+}
+
+static void
+bootdump(Dosboot *b)
+{
+	if(chatty == 0)
+		return;
+	print("magic: 0x%2.2x 0x%2.2x 0x%2.2x ",
+		b->magic[0], b->magic[1], b->magic[2]);
+	print("version: \"%8.8s\"\n", (char*)b->version);
+	print("sectsize: %d ", GSHORT(b->sectsize));
+	print("allocsize: %d ", b->clustsize);
+	print("nresrv: %d ", GSHORT(b->nresrv));
+	print("nfats: %d\n", b->nfats);
+	print("rootsize: %d ", GSHORT(b->rootsize));
+	print("volsize: %d ", GSHORT(b->volsize));
+	print("mediadesc: 0x%2.2x\n", b->mediadesc);
+	print("fatsize: %d ", GSHORT(b->fatsize));
+	print("trksize: %d ", GSHORT(b->trksize));
+	print("nheads: %d ", GSHORT(b->nheads));
+	print("nhidden: %d ", GLONG(b->nhidden));
+	print("bigvolsize: %d\n", GLONG(b->bigvolsize));
+/*
+	print("driveno: %d\n", b->driveno);
+	print("reserved0: 0x%2.2x\n", b->reserved0);
+	print("bootsig: 0x%2.2x\n", b->bootsig);
+	print("volid: 0x%8.8x\n", GLONG(b->volid));
+	print("label: \"%11.11s\"\n", b->label);
+*/
+}
+
+
+/*
+ *  set up a dos file name
+ */
+static void
+setname(Dosfile *fp, char *from)
+{
+	char *to;
+
+	to = fp->name;
+	for(; *from && to-fp->name < 8; from++, to++){
+		if(*from == '.'){
+			from++;
+			break;
+		}
+		if(*from >= 'a' && *from <= 'z')
+			*to = *from + 'A' - 'a';
+		else
+			*to = *from;
+	}
+	while(to - fp->name < 8)
+		*to++ = ' ';
+	
+	/* from might be 12345678.123: don't save the '.' in ext */
+	if(*from == '.')
+		from++;
+
+	to = fp->ext;
+	for(; *from && to-fp->ext < 3; from++, to++){
+		if(*from >= 'a' && *from <= 'z')
+			*to = *from + 'A' - 'a';
+		else
+			*to = *from;
+	}
+	while(to-fp->ext < 3)
+		*to++ = ' ';
+
+	chat("name is %8.8s.%3.3s\n", fp->name, fp->ext);
+}
--- /dev/null
+++ b/os/boot/pc/dosfs.h
@@ -1,0 +1,62 @@
+typedef struct Dosboot	Dosboot;
+typedef struct Dos	Dos;
+typedef struct Dosdir	Dosdir;
+typedef struct Dosfile	Dosfile;
+typedef struct Dospart	Dospart;
+
+struct Dospart
+{
+	uchar flag;		/* active flag */
+	uchar shead;		/* starting head */
+	uchar scs[2];		/* starting cylinder/sector */
+	uchar type;		/* partition type */
+	uchar ehead;		/* ending head */
+	uchar ecs[2];		/* ending cylinder/sector */
+	uchar start[4];		/* starting sector */
+	uchar len[4];		/* length in sectors */
+};
+
+#define FAT12	0x01
+#define FAT16	0x04
+#define EXTEND	0x05
+#define FATHUGE	0x06
+#define FAT32	0x0b
+#define FAT32X	0x0c
+#define EXTHUGE	0x0f
+#define DMDDO	0x54
+#define PLAN9	0x39
+#define LEXTEND 0x85
+
+struct Dosfile{
+	Dos	*dos;		/* owning dos file system */
+	char	name[8];
+	char	ext[3];
+	uchar	attr;
+	long	length;
+	long	pstart;		/* physical start cluster address */
+	long	pcurrent;	/* physical current cluster address */
+	long	lcurrent;	/* logical current cluster address */
+	long	offset;
+};
+
+struct Dos{
+	long	start;		/* start of file system */
+	int	sectsize;	/* in bytes */
+	int	clustsize;	/* in sectors */
+	int	clustbytes;	/* in bytes */
+	int	nresrv;		/* sectors */
+	int	nfats;		/* usually 2 */
+	int	rootsize;	/* number of entries */
+	int	volsize;	/* in sectors */
+	int	mediadesc;
+	int	fatsize;	/* in sectors */
+	int	fatclusters;
+	int	fatbits;	/* 12 or 16 */
+	long	fataddr;	/* sector number */
+	long	rootaddr;
+	long	rootclust;
+	long	dataaddr;
+	long	freeptr;
+};
+
+extern int	dosinit(Fs*);
--- /dev/null
+++ b/os/boot/pc/eipfmt.c
@@ -1,0 +1,145 @@
+#include "u.h"
+#include "lib.h"
+#include "mem.h"
+#include "dat.h"
+#include "fns.h"
+#include "ip.h"
+
+
+enum 
+{
+	IPaddrlen=	16,
+	IPv4addrlen=	4,
+	IPv4off=	12,
+	IPllen=		4,
+};
+extern	int	fmtstrcpy(Fmt*, char*);
+
+
+/*
+ *  prefix of all v4 addresses
+ */
+uchar v4prefix[IPaddrlen] = {
+	0, 0, 0, 0,
+	0, 0, 0, 0,
+	0, 0, 0xff, 0xff,
+	0, 0, 0, 0
+};
+
+enum
+{
+	Isprefix= 16,
+};
+
+uchar prefixvals[256] =
+{
+[0x00] 0 | Isprefix,
+[0x80] 1 | Isprefix,
+[0xC0] 2 | Isprefix,
+[0xE0] 3 | Isprefix,
+[0xF0] 4 | Isprefix,
+[0xF8] 5 | Isprefix,
+[0xFC] 6 | Isprefix,
+[0xFE] 7 | Isprefix,
+[0xFF] 8 | Isprefix,
+};
+
+void
+hnputl(void *p, uint v)
+{
+	uchar *a;
+
+	a = p;
+	a[0] = v>>24;
+	a[1] = v>>16;
+	a[2] = v>>8;
+	a[3] = v;
+}
+
+int
+eipfmt(Fmt *f)
+{
+	char buf[5*8];
+	static char *efmt = "%.2lux%.2lux%.2lux%.2lux%.2lux%.2lux";
+	static char *ifmt = "%d.%d.%d.%d";
+	uchar *p, ip[16];
+	ulong *lp;
+	ushort s;
+	int i, j, n, eln, eli;
+
+	switch(f->r) {
+	case 'E':		/* Ethernet address */
+		p = va_arg(f->args, uchar*);
+		snprint(buf, sizeof buf, efmt, p[0], p[1], p[2], p[3], p[4], p[5]);
+		return fmtstrcpy(f, buf);
+
+	case 'I':		/* Ip address */
+		p = va_arg(f->args, uchar*);
+common:
+		if(memcmp(p, v4prefix, 12) == 0){
+			snprint(buf, sizeof buf, ifmt, p[12], p[13], p[14], p[15]);
+			return fmtstrcpy(f, buf);
+		}
+
+		/* find longest elision */
+		eln = eli = -1;
+		for(i = 0; i < 16; i += 2){
+			for(j = i; j < 16; j += 2)
+				if(p[j] != 0 || p[j+1] != 0)
+					break;
+			if(j > i && j - i > eln){
+				eli = i;
+				eln = j - i;
+			}
+		}
+
+		/* print with possible elision */
+		n = 0;
+		for(i = 0; i < 16; i += 2){
+			if(i == eli){
+				n += sprint(buf+n, "::");
+				i += eln;
+				if(i >= 16)
+					break;
+			} else if(i != 0)
+				n += sprint(buf+n, ":");
+			s = (p[i]<<8) + p[i+1];
+			n += sprint(buf+n, "%ux", s);
+		}
+		return fmtstrcpy(f, buf);
+
+	case 'i':		/* v6 address as 4 longs */
+		lp = va_arg(f->args, ulong*);
+		for(i = 0; i < 4; i++)
+			hnputl(ip+4*i, *lp++);
+		p = ip;
+		goto common;
+
+	case 'V':		/* v4 ip address */
+		p = va_arg(f->args, uchar*);
+		snprint(buf, sizeof buf, ifmt, p[0], p[1], p[2], p[3]);
+		return fmtstrcpy(f, buf);
+
+	case 'M':		/* ip mask */
+		p = va_arg(f->args, uchar*);
+
+		/* look for a prefix mask */
+		for(i = 0; i < 16; i++)
+			if(p[i] != 0xff)
+				break;
+		if(i < 16){
+			if((prefixvals[p[i]] & Isprefix) == 0)
+				goto common;
+			for(j = i+1; j < 16; j++)
+				if(p[j] != 0)
+					goto common;
+			n = 8*i + (prefixvals[p[i]] & ~Isprefix);
+		} else
+			n = 8*16;
+
+		/* got one, use /xx format */
+		snprint(buf, sizeof buf, "/%d", n);
+		return fmtstrcpy(f, buf);
+	}
+	return fmtstrcpy(f, "(eipfmt)");
+}
--- /dev/null
+++ b/os/boot/pc/error.h
@@ -1,0 +1,58 @@
+extern char Enoerror[];		/* no error */
+extern char Emount[];		/* inconsistent mount */
+extern char Eunmount[];		/* not mounted */
+extern char Eunion[];		/* not in union */
+extern char Emountrpc[];	/* mount rpc error */
+extern char Eshutdown[];	/* mounted device shut down */
+extern char Enocreate[];	/* mounted directory forbids creation */
+extern char Enonexist[];	/* file does not exist */
+extern char Eexist[];		/* file already exists */
+extern char Ebadsharp[];	/* unknown device in # filename */
+extern char Enotdir[];		/* not a directory */
+extern char Eisdir[];		/* file is a directory */
+extern char Ebadchar[];		/* bad character in file name */
+extern char Efilename[];	/* file name syntax */
+extern char Eperm[];		/* permission denied */
+extern char Ebadusefd[];	/* inappropriate use of fd */
+extern char Ebadarg[];		/* bad arg in system call */
+extern char Einuse[];		/* device or object already in use */
+extern char Eio[];		/* i/o error */
+extern char Etoobig[];		/* read or write too large */
+extern char Etoosmall[];	/* read or write too small */
+extern char Enetaddr[];		/* bad network address */
+extern char Emsgsize[];		/* message is too big for protocol */
+extern char Enetbusy[];		/* network device is busy or allocated */
+extern char Enoproto[];		/* network protocol not supported */
+extern char Enoport[];		/* network port not available */
+extern char Enoifc[];		/* bad interface or no free interface slots */
+extern char Enolisten[];	/* not announced */
+extern char Ehungup[];		/* write to hungup channel */
+extern char Ebadctl[];		/* bad process or channel control request */
+extern char Enodev[];		/* no free devices */
+extern char Enoenv[];		/* no free environment resources */
+extern char Emuxshutdown[];	/* mux server shut down */
+extern char Emuxbusy[];		/* all mux channels busy */
+extern char Emuxmsg[];		/* bad mux message format or mismatch */
+extern char Eprocdied[];	/* process exited */
+extern char Enochild[];		/* no living children */
+extern char Eioload[];		/* i/o error in demand load */
+extern char Enovmem[];		/* virtual memory allocation failed */
+extern char Ebadld[];		/* illegal line discipline */
+extern char Ebadfd[];		/* fd out of range or not open */
+extern char Eisstream[];	/* seek on a stream */
+extern char Ebadexec[];		/* exec header invalid */
+extern char Etimedout[];	/* connection timed out */
+extern char Econrefused[];	/* connection refused */
+extern char Enetunreach[];	/* network unreachable */
+extern char Eintr[];		/* interrupted */
+extern char Eneedservice[];	/* service required for tcp/udp/il calls */
+extern char Enomem[];		/* kernel allocate failed */
+extern char Enoswap[];		/* swap space full */
+extern char Esfnotcached[];	/* subfont not cached */
+extern char Esoverlap[];	/* segments overlap */
+extern char Emouseset[];	/* mouse type already set */
+extern char Erecover[];		/* failed to recover fd */
+extern char Eshort[];		/* i/o count too small */
+extern char Egreg[];		/* ken scheduled it */
+extern char Ebadspec[];		/* bad attach specifier */
+extern char Enoreg[];		/* process has no saved registers */
--- /dev/null
+++ b/os/boot/pc/ether.c
@@ -1,0 +1,291 @@
+#include "u.h"
+#include "lib.h"
+#include "mem.h"
+#include "dat.h"
+#include "fns.h"
+#include "io.h"
+#include "ip.h"
+
+#include "etherif.h"
+
+static Ether ether[MaxEther];
+
+extern int ether2114xreset(Ether*);
+extern int elnk3reset(Ether*);
+extern int i82557reset(Ether*);
+extern int igbepnp(Ether *);
+extern int i82563pnp(Ether *);
+extern int elnk3reset(Ether*);
+extern int ether589reset(Ether*);
+extern int ne2000reset(Ether*);
+extern int wd8003reset(Ether*);
+extern int ec2treset(Ether*);
+extern int amd79c970reset(Ether*);
+extern int rtl8139pnp(Ether*);
+extern int rtl8169pnp(Ether*);
+extern int ether83815reset(Ether*);
+extern int rhinepnp(Ether*);
+extern int ga620pnp(Ether*);
+extern int dp83820pnp(Ether*);
+
+struct {
+	char	*type;
+	int	(*reset)(Ether*);
+	int	noprobe;
+} ethercards[] = {
+	{ "21140", ether2114xreset, 0, },
+	{ "2114x", ether2114xreset, 0, },
+	{ "i82557", i82557reset, 0, },
+	{ "igbe",  igbepnp, 0, },
+	{ "i82563",i82563pnp, 0, },
+	{ "igbepcie",i82563pnp, 0, },
+	{ "elnk3", elnk3reset, 0, },
+	{ "3C509", elnk3reset, 0, },
+	{ "3C575", elnk3reset, 0, },
+	{ "3C589", ether589reset, 1, },
+	{ "3C562", ether589reset, 1, },
+	{ "589E", ether589reset, 1, },
+	{ "NE2000", ne2000reset, 0, },
+	{ "WD8003", wd8003reset, 1, },
+	{ "EC2T", ec2treset, 0, },
+	{ "AMD79C970", amd79c970reset, 0, },
+	{ "RTL8139", rtl8139pnp, 0, },
+	{ "RTL8169", rtl8169pnp, 0, },
+	{ "83815", ether83815reset, 0, },
+	{ "rhine", rhinepnp, 0, },
+	{ "vt6102", rhinepnp, 0, },
+	{ "GA620", ga620pnp, 0, },
+	{ "83820",   dp83820pnp, 0, },
+	{ "dp83820", dp83820pnp, 0, },
+
+	{ 0, }
+};
+
+static void xetherdetach(void);
+
+int
+etherinit(void)
+{
+	Ether *ctlr;
+	int ctlrno, i, mask, n, x;
+
+	fmtinstall('E', eipfmt);
+
+	etherdetach = xetherdetach;
+	mask = 0;
+	for(ctlrno = 0; ctlrno < MaxEther; ctlrno++){
+		ctlr = &ether[ctlrno];
+		memset(ctlr, 0, sizeof(Ether));
+		if(iniread && isaconfig("ether", ctlrno, ctlr) == 0)
+			continue;
+
+		for(n = 0; ethercards[n].type; n++){
+			if(!iniread){
+				if(ethercards[n].noprobe)
+					continue;
+				memset(ctlr, 0, sizeof(Ether));
+				strcpy(ctlr->type, ethercards[n].type);
+			}
+			else if(cistrcmp(ethercards[n].type, ctlr->type))
+				continue;
+			ctlr->ctlrno = ctlrno;
+
+			x = splhi();
+			if((*ethercards[n].reset)(ctlr)){
+				splx(x);
+				if(iniread)
+					break;
+				else
+					continue;
+			}
+
+			ctlr->state = 1;		/* card found */
+			mask |= 1<<ctlrno;
+			if(ctlr->irq == 2)
+				ctlr->irq = 9;
+			setvec(VectorPIC + ctlr->irq, ctlr->interrupt, ctlr);
+
+			print("ether#%d: %s: port 0x%luX irq %lud",
+				ctlr->ctlrno, ctlr->type, ctlr->port, ctlr->irq);
+			if(ctlr->mem)
+				print(" addr 0x%luX", ctlr->mem & ~KZERO);
+			if(ctlr->size)
+				print(" size 0x%luX", ctlr->size);
+			print(": %E\n", ctlr->ea);
+		
+			if(ctlr->nrb == 0)
+				ctlr->nrb = Nrb;
+			ctlr->rb = ialloc(sizeof(RingBuf)*ctlr->nrb, 0);
+			if(ctlr->ntb == 0)
+				ctlr->ntb = Ntb;
+			ctlr->tb = ialloc(sizeof(RingBuf)*ctlr->ntb, 0);
+
+			ctlr->rh = 0;
+			ctlr->ri = 0;
+			for(i = 0; i < ctlr->nrb; i++)
+				ctlr->rb[i].owner = Interface;
+		
+			ctlr->th = 0;
+			ctlr->ti = 0;
+			for(i = 0; i < ctlr->ntb; i++)
+				ctlr->tb[i].owner = Host;
+
+			splx(x);
+			break;
+		}
+	}
+
+	return mask;
+}
+
+void
+etherinitdev(int i, char *s)
+{
+	sprint(s, "ether%d", i);
+}
+
+void
+etherprintdevs(int i)
+{
+	print(" ether%d", i);
+}
+
+static Ether*
+attach(int ctlrno)
+{
+	Ether *ctlr;
+
+	if(ctlrno >= MaxEther || ether[ctlrno].state == 0)
+		return 0;
+
+	ctlr = &ether[ctlrno];
+	if(ctlr->state == 1){		/* card found? */
+		ctlr->state = 2;	/* attaching */
+		(*ctlr->attach)(ctlr);
+	}
+
+	return ctlr;
+}
+
+static void
+xetherdetach(void)
+{
+	Ether *ctlr;
+	int ctlrno, x;
+
+	x = splhi();
+	for(ctlrno = 0; ctlrno < MaxEther; ctlrno++){
+		ctlr = &ether[ctlrno];
+		if(ctlr->detach && ctlr->state != 0)	/* found | attaching? */
+			ctlr->detach(ctlr);
+	}
+	splx(x);
+}
+
+uchar*
+etheraddr(int ctlrno)
+{
+	Ether *ctlr;
+
+	if((ctlr = attach(ctlrno)) == 0)
+		return 0;
+
+	return ctlr->ea;
+}
+
+static int
+wait(RingBuf* ring, uchar owner, int timo)
+{
+	ulong start;
+
+	start = m->ticks;
+	while(TK2MS(m->ticks - start) < timo){
+		if(ring->owner != owner)
+			return 1;
+	}
+
+	return 0;
+}
+
+int
+etherrxpkt(int ctlrno, Etherpkt* pkt, int timo)
+{
+	int n;
+	Ether *ctlr;
+	RingBuf *ring;
+
+	if((ctlr = attach(ctlrno)) == 0)
+		return 0;
+
+	ring = &ctlr->rb[ctlr->rh];
+	if(wait(ring, Interface, timo) == 0){
+		if(debug)
+			print("ether%d: rx timeout\n", ctlrno);
+		return 0;
+	}
+
+	n = ring->len;
+	memmove(pkt, ring->pkt, n);
+	ring->owner = Interface;
+	ctlr->rh = NEXT(ctlr->rh, ctlr->nrb);
+
+	return n;
+}
+
+int
+etherrxflush(int ctlrno)
+{
+	int n;
+	Ether *ctlr;
+	RingBuf *ring;
+
+	if((ctlr = attach(ctlrno)) == 0)
+		return 0;
+
+	n = 0;
+	for(;;){
+		ring = &ctlr->rb[ctlr->rh];
+		if(wait(ring, Interface, 100) == 0)
+			break;
+
+		ring->owner = Interface;
+		ctlr->rh = NEXT(ctlr->rh, ctlr->nrb);
+		n++;
+	}
+
+	return n;
+}
+
+int
+ethertxpkt(int ctlrno, Etherpkt* pkt, int len, int)
+{
+	Ether *ctlr;
+	RingBuf *ring;
+	int s;
+
+	if((ctlr = attach(ctlrno)) == 0)
+		return 0;
+
+	ring = &ctlr->tb[ctlr->th];
+	if(wait(ring, Interface, 1000) == 0){
+		print("ether%d: tx buffer timeout\n", ctlrno);
+		return 0;
+	}
+
+	memmove(pkt->s, ctlr->ea, Eaddrlen);
+	if(debug)
+		print("%E to %E...\n", pkt->s, pkt->d);
+	memmove(ring->pkt, pkt, len);
+	if(len < ETHERMINTU){
+		memset(ring->pkt+len, 0, ETHERMINTU-len);
+		len = ETHERMINTU;
+	}
+	ring->len = len;
+	ring->owner = Interface;
+	ctlr->th = NEXT(ctlr->th, ctlr->ntb);
+	s = splhi();
+	(*ctlr->transmit)(ctlr);
+	splx(s);
+
+	return 1;
+}
--- /dev/null
+++ b/os/boot/pc/ether2000.c
@@ -1,0 +1,110 @@
+#include "u.h"
+#include "lib.h"
+#include "mem.h"
+#include "dat.h"
+#include "fns.h"
+#include "io.h"
+
+#include "etherif.h"
+#include "ether8390.h"
+
+/*
+ * Driver written for the 'Notebook Computer Ethernet LAN Adapter',
+ * a plug-in to the bus-slot on the rear of the Gateway NOMAD 425DXL
+ * laptop. The manual says NE2000 compatible.
+ * The interface appears to be pretty well described in the National
+ * Semiconductor Local Area Network Databook (1992) as one of the
+ * AT evaluation cards.
+ *
+ * The NE2000 is really just a DP8390[12] plus a data port
+ * and a reset port.
+ */
+enum {
+	Data		= 0x10,		/* offset from I/O base of data port */
+	Reset		= 0x1F,		/* offset from I/O base of reset port */
+};
+
+int
+ne2000reset(Ether* ether)
+{
+	ushort buf[16];
+	ulong port;
+	Dp8390 *ctlr;
+	int i;
+	uchar ea[Eaddrlen];
+
+	/*
+	 * Set up the software configuration.
+	 * Use defaults for port, irq, mem and size
+	 * if not specified.
+	 */
+	if(ether->port == 0)
+		ether->port = 0x300;
+	if(ether->irq == 0)
+		ether->irq = 2;
+	if(ether->mem == 0)
+		ether->mem = 0x4000;
+	if(ether->size == 0)
+		ether->size = 16*1024;
+	port = ether->port;
+
+	ether->ctlr = malloc(sizeof(Dp8390));
+	ctlr = ether->ctlr;
+	ctlr->width = 2;
+	ctlr->ram = 0;
+
+	ctlr->port = port;
+	ctlr->data = port+Data;
+
+	ctlr->tstart = HOWMANY(ether->mem, Dp8390BufSz);
+	ctlr->pstart = ctlr->tstart + HOWMANY(sizeof(Etherpkt), Dp8390BufSz);
+	ctlr->pstop = ctlr->tstart + HOWMANY(ether->size, Dp8390BufSz);
+
+	ctlr->dummyrr = 1;
+	for(i = 0; i < ether->nopt; i++){
+		if(strcmp(ether->opt[i], "nodummyrr"))
+			continue;
+		ctlr->dummyrr = 0;
+		break;
+	}
+
+	/*
+	 * Reset the board. This is done by doing a read
+	 * followed by a write to the Reset address.
+	 */
+	buf[0] = inb(port+Reset);
+	delay(2);
+	outb(port+Reset, buf[0]);
+	delay(2);
+	
+	/*
+	 * Init the (possible) chip, then use the (possible)
+	 * chip to read the (possible) PROM for ethernet address
+	 * and a marker byte.
+	 * Could just look at the DP8390 command register after
+	 * initialisation has been tried, but that wouldn't be
+	 * enough, there are other ethernet boards which could
+	 * match.
+	 */
+	dp8390reset(ether);
+	memset(buf, 0, sizeof(buf));
+	dp8390read(ctlr, buf, 0, sizeof(buf));
+	if((buf[0x0E] & 0xFF) != 0x57 || (buf[0x0F] & 0xFF) != 0x57){
+		free(ether->ctlr);
+		return -1;
+	}
+
+	/*
+	 * Stupid machine. Shorts were asked for,
+	 * shorts were delivered, although the PROM is a byte array.
+	 * Set the ethernet address.
+	 */
+	memset(ea, 0, Eaddrlen);
+	if(memcmp(ea, ether->ea, Eaddrlen) == 0){
+		for(i = 0; i < sizeof(ether->ea); i++)
+			ether->ea[i] = buf[i];
+	}
+	dp8390setea(ether);
+
+	return 0;
+}
--- /dev/null
+++ b/os/boot/pc/ether2114x.c
@@ -1,0 +1,1652 @@
+/*
+ * Digital Semiconductor DECchip 21140 PCI Fast Ethernet LAN Controller
+ * as found on the Digital Fast EtherWORKS PCI 10/100 adapter (DE-500-X).
+ * To do:
+ *	thresholds;
+ *	ring sizing;
+ *	handle more error conditions;
+ *	all the rest of it...
+ */
+#include "u.h"
+#include "lib.h"
+#include "mem.h"
+#include "dat.h"
+#include "fns.h"
+#include "io.h"
+
+#include "etherif.h"
+
+#define DEBUG		(0)
+#define debug		if(DEBUG)print
+
+enum {
+	Nrde		= 32,
+	Ntde		= 4,
+};
+
+#define Rbsz		ROUNDUP(sizeof(Etherpkt)+4, 4)
+
+enum {					/* CRS0 - Bus Mode */
+	Swr		= 0x00000001,	/* Software Reset */
+	Bar		= 0x00000002,	/* Bus Arbitration */
+	Dsl		= 0x0000007C,	/* Descriptor Skip Length (field) */
+	Ble		= 0x00000080,	/* Big/Little Endian */
+	Pbl		= 0x00003F00,	/* Programmable Burst Length (field) */
+	Cal		= 0x0000C000,	/* Cache Alignment (field) */
+	Cal8		= 0x00004000,	/* 8 longword boundary alignment */
+	Cal16		= 0x00008000,	/* 16 longword boundary alignment */
+	Cal32		= 0x0000C000,	/* 32 longword boundary alignment */
+	Tap		= 0x000E0000,	/* Transmit Automatic Polling (field) */
+	Dbo		= 0x00100000,	/* Descriptor Byte Ordering Mode */
+	Rml		= 0x00200000,	/* Read Multiple */
+}; 
+
+enum {					/* CSR[57] - Status and Interrupt Enable */
+	Ti		= 0x00000001,	/* Transmit Interrupt */
+	Tps		= 0x00000002,	/* Transmit Process Stopped */
+	Tu		= 0x00000004,	/* Transmit buffer Unavailable */
+	Tjt		= 0x00000008,	/* Transmit Jabber Timeout */
+	Unf		= 0x00000020,	/* transmit UNderFlow */
+	Ri		= 0x00000040,	/* Receive Interrupt */
+	Ru		= 0x00000080,	/* Receive buffer Unavailable */
+	Rps		= 0x00000100,	/* Receive Process Stopped */
+	Rwt		= 0x00000200,	/* Receive Watchdog Timeout */
+	Eti		= 0x00000400,	/* Early Transmit Interrupt */
+	Gte		= 0x00000800,	/* General purpose Timer Expired */
+	Fbe		= 0x00002000,	/* Fatal Bit Error */
+	Ais		= 0x00008000,	/* Abnormal Interrupt Summary */
+	Nis		= 0x00010000,	/* Normal Interrupt Summary */
+	Rs		= 0x000E0000,	/* Receive process State (field) */
+	Ts		= 0x00700000,	/* Transmit process State (field) */
+	Eb		= 0x03800000,	/* Error bits */
+};
+
+enum {					/* CSR6 - Operating Mode */
+	Hp		= 0x00000001,	/* Hash/Perfect receive filtering mode */
+	Sr		= 0x00000002,	/* Start/stop Receive */
+	Ho		= 0x00000004,	/* Hash-Only filtering mode */
+	Pb		= 0x00000008,	/* Pass Bad frames */
+	If		= 0x00000010,	/* Inverse Filtering */
+	Sb		= 0x00000020,	/* Start/stop Backoff counter */
+	Pr		= 0x00000040,	/* Promiscuous Mode */
+	Pm		= 0x00000080,	/* Pass all Multicast */
+	Fd		= 0x00000200,	/* Full Duplex mode */
+	Om		= 0x00000C00,	/* Operating Mode (field) */
+	Fc		= 0x00001000,	/* Force Collision */
+	St		= 0x00002000,	/* Start/stop Transmission Command */
+	Tr		= 0x0000C000,	/* ThReshold control bits (field) */
+	Tr128		= 0x00000000,
+	Tr256		= 0x00004000,
+	Tr512		= 0x00008000,
+	Tr1024		= 0x0000C000,
+	Ca		= 0x00020000,	/* CApture effect enable */
+	Ps		= 0x00040000,	/* Port Select */
+	Hbd		= 0x00080000,	/* HeartBeat Disable */
+	Imm		= 0x00100000,	/* IMMediate mode */
+	Sf		= 0x00200000,	/* Store and Forward */
+	Ttm		= 0x00400000,	/* Transmit Threshold Mode */
+	Pcs		= 0x00800000,	/* PCS function */
+	Scr		= 0x01000000,	/* SCRambler mode */
+	Mbo		= 0x02000000,	/* Must Be One */
+	Ra		= 0x40000000,	/* Receive All */
+	Sc		= 0x80000000,	/* Special Capture effect enable */
+
+	TrMODE		= Tr512,	/* default transmission threshold */
+};
+
+enum {					/* CSR9 - ROM and MII Management */
+	Scs		= 0x00000001,	/* serial ROM chip select */
+	Sclk		= 0x00000002,	/* serial ROM clock */
+	Sdi		= 0x00000004,	/* serial ROM data in */
+	Sdo		= 0x00000008,	/* serial ROM data out */
+	Ss		= 0x00000800,	/* serial ROM select */
+	Wr		= 0x00002000,	/* write */
+	Rd		= 0x00004000,	/* read */
+
+	Mdc		= 0x00010000,	/* MII management clock */
+	Mdo		= 0x00020000,	/* MII management write data */
+	Mii		= 0x00040000,	/* MII management operation mode (W) */
+	Mdi		= 0x00080000,	/* MII management data in */
+};
+
+enum {					/* CSR12 - General-Purpose Port */
+	Gpc		= 0x00000100,	/* General Purpose Control */
+};
+
+typedef struct Des {
+	int	status;
+	int	control;
+	ulong	addr;
+	void*	bp;
+} Des;
+
+enum {					/* status */
+	Of		= 0x00000001,	/* Rx: OverFlow */
+	Ce		= 0x00000002,	/* Rx: CRC Error */
+	Db		= 0x00000004,	/* Rx: Dribbling Bit */
+	Re		= 0x00000008,	/* Rx: Report on MII Error */
+	Rw		= 0x00000010,	/* Rx: Receive Watchdog */
+	Ft		= 0x00000020,	/* Rx: Frame Type */
+	Cs		= 0x00000040,	/* Rx: Collision Seen */
+	Tl		= 0x00000080,	/* Rx: Frame too Long */
+	Ls		= 0x00000100,	/* Rx: Last deScriptor */
+	Fs		= 0x00000200,	/* Rx: First deScriptor */
+	Mf		= 0x00000400,	/* Rx: Multicast Frame */
+	Rf		= 0x00000800,	/* Rx: Runt Frame */
+	Dt		= 0x00003000,	/* Rx: Data Type (field) */
+	De		= 0x00004000,	/* Rx: Descriptor Error */
+	Fl		= 0x3FFF0000,	/* Rx: Frame Length (field) */
+	Ff		= 0x40000000,	/* Rx: Filtering Fail */
+
+	Def		= 0x00000001,	/* Tx: DEFerred */
+	Uf		= 0x00000002,	/* Tx: UnderFlow error */
+	Lf		= 0x00000004,	/* Tx: Link Fail report */
+	Cc		= 0x00000078,	/* Tx: Collision Count (field) */
+	Hf		= 0x00000080,	/* Tx: Heartbeat Fail */
+	Ec		= 0x00000100,	/* Tx: Excessive Collisions */
+	Lc		= 0x00000200,	/* Tx: Late Collision */
+	Nc		= 0x00000400,	/* Tx: No Carrier */
+	Lo		= 0x00000800,	/* Tx: LOss of carrier */
+	To		= 0x00004000,	/* Tx: Transmission jabber timeOut */
+
+	Es		= 0x00008000,	/* [RT]x: Error Summary */
+	Own		= 0x80000000,	/* [RT]x: OWN bit */
+};
+
+enum {					/* control */
+	Bs1		= 0x000007FF,	/* [RT]x: Buffer 1 Size */
+	Bs2		= 0x003FF800,	/* [RT]x: Buffer 2 Size */
+
+	Ch		= 0x01000000,	/* [RT]x: second address CHained */
+	Er		= 0x02000000,	/* [RT]x: End of Ring */
+
+	Ft0		= 0x00400000,	/* Tx: Filtering Type 0 */
+	Dpd		= 0x00800000,	/* Tx: Disabled PaDding */
+	Ac		= 0x04000000,	/* Tx: Add CRC disable */
+	Set		= 0x08000000,	/* Tx: SETup packet */
+	Ft1		= 0x10000000,	/* Tx: Filtering Type 1 */
+	Fseg		= 0x20000000,	/* Tx: First SEGment */
+	Lseg		= 0x40000000,	/* Tx: Last SEGment */
+	Ic		= 0x80000000,	/* Tx: Interrupt on Completion */
+};
+
+enum {					/* PHY registers */
+	Bmcr		= 0,		/* Basic Mode Control */
+	Bmsr		= 1,		/* Basic Mode Status */
+	Phyidr1		= 2,		/* PHY Identifier #1 */
+	Phyidr2		= 3,		/* PHY Identifier #2 */
+	Anar		= 4,		/* Auto-Negotiation Advertisment */
+	Anlpar		= 5,		/* Auto-Negotiation Link Partner Ability */
+	Aner		= 6,		/* Auto-Negotiation Expansion */
+};
+
+enum {					/* Variants */
+	Tulip0		= (0x0009<<16)|0x1011,
+	Tulip1		= (0x0014<<16)|0x1011,
+	Tulip3		= (0x0019<<16)|0x1011,
+	Pnic		= (0x0002<<16)|0x11AD,
+	Pnic2		= (0xC115<<16)|0x11AD,
+};
+
+typedef struct Ctlr Ctlr;
+typedef struct Ctlr {
+	int	port;
+	Pcidev*	pcidev;
+	Ctlr*	next;
+	int	active;
+	int	id;			/* (pcidev->did<<16)|pcidev->vid */
+
+	uchar	*srom;
+	int	sromsz;
+	uchar*	sromea;			/* MAC address */
+	uchar*	leaf;
+	int	sct;			/* selected connection type */
+	int	k;			/* info block count */
+	uchar*	infoblock[16];
+	int	sctk;			/* sct block index */
+	int	curk;			/* current block index */
+	uchar*	type5block;
+
+	int	phy[32];		/* logical to physical map */
+	int	phyreset;		/* reset bitmap */
+	int	curphyad;
+	int	fdx;
+	int	ttm;
+
+	uchar	fd;			/* option */
+	int	medium;			/* option */
+
+	int	csr6;			/* CSR6 - operating mode */
+	int	mask;			/* CSR[57] - interrupt mask */
+	int	mbps;
+
+	Des*	rdr;			/* receive descriptor ring */
+	int	nrdr;			/* size of rdr */
+	int	rdrx;			/* index into rdr */
+
+	Des*	tdr;			/* transmit descriptor ring */
+	int	ntdr;			/* size of tdr */
+	int	tdrh;			/* host index into tdr */
+	int	tdri;			/* interface index into tdr */
+	int	ntq;			/* descriptors active */
+	Block*	setupbp;
+
+	ulong	of;			/* receive statistics */
+	ulong	ce;
+	ulong	cs;
+	ulong	tl;
+	ulong	rf;
+	ulong	de;
+
+	ulong	uf;			/* transmit statistics */
+	ulong	ec;
+	ulong	lc;
+	ulong	nc;
+	ulong	lo;
+	ulong	to;
+
+} Ctlr;
+
+static Ctlr* ctlrhead;
+static Ctlr* ctlrtail;
+
+#define csr32r(c, r)	(inl((c)->port+((r)*8)))
+#define csr32w(c, r, l)	(outl((c)->port+((r)*8), (ulong)(l)))
+
+static void
+attach(Ether* ether)
+{
+	Ctlr *ctlr;
+
+	ctlr = ether->ctlr;
+	if(!(ctlr->csr6 & Sr)){
+		ctlr->csr6 |= Sr;
+		csr32w(ctlr, 6, ctlr->csr6);
+	}
+}
+
+static void
+transmit(Ether* ether)
+{
+	Ctlr *ctlr;
+	Block *bp;
+	Des *des;
+	int control;
+	RingBuf *tb;
+
+	ctlr = ether->ctlr;
+	while(ctlr->ntq < (ctlr->ntdr-1)){
+		if(ctlr->setupbp){
+			bp = ctlr->setupbp;
+			ctlr->setupbp = 0;
+			control = Ic|Set|BLEN(bp);
+		}
+		else{
+			if(ether->ntb == 0)
+				break;
+			tb = &ether->tb[ether->ti];
+			if(tb->owner != Interface)
+				break;
+			bp = allocb(tb->len);
+			memmove(bp->wp, tb->pkt, tb->len);
+			memmove(bp->wp+Eaddrlen, ether->ea, Eaddrlen);
+			bp->wp += tb->len;
+
+			tb->owner = Host;
+			ether->ti = NEXT(ether->ti, ether->ntb);
+
+			control = Ic|Lseg|Fseg|BLEN(bp);
+		}
+
+		ctlr->tdr[PREV(ctlr->tdrh, ctlr->ntdr)].control &= ~Ic;
+		des = &ctlr->tdr[ctlr->tdrh];
+		des->bp = bp;
+		des->addr = PADDR(bp->rp);
+		des->control |= control;
+		ctlr->ntq++;
+		//coherence();
+		des->status = Own;
+		csr32w(ctlr, 1, 0);
+		ctlr->tdrh = NEXT(ctlr->tdrh, ctlr->ntdr);
+	}
+}
+
+static void
+interrupt(Ureg*, void* arg)
+{
+	Ctlr *ctlr;
+	Ether *ether;
+	int len, status;
+	Des *des;
+	RingBuf *ring;
+
+	ether = arg;
+	ctlr = ether->ctlr;
+
+	while((status = csr32r(ctlr, 5)) & (Nis|Ais)){
+		/*
+		 * Acknowledge the interrupts and mask-out
+		 * the ones that are implicitly handled.
+		 */
+		csr32w(ctlr, 5, status);
+		status &= (ctlr->mask & ~(Nis|Ais|Ti));
+
+		/*
+		 * Received packets.
+		 */
+		if(status & Ri){
+			des = &ctlr->rdr[ctlr->rdrx];
+			while((des->status & Own) == 0){
+				len = ((des->status & Fl)>>16)-4;
+				if(des->status & Es){
+					if(des->status & Of)
+						ctlr->of++;
+					if(des->status & Ce)
+						ctlr->ce++;
+					if(des->status & Cs)
+						ctlr->cs++;
+					if(des->status & Tl)
+						ctlr->tl++;
+					if(des->status & Rf)
+						ctlr->rf++;
+					if(des->status & De)
+						ctlr->de++;
+				}
+				else{
+					ring = &ether->rb[ether->ri];
+					if(ring->owner == Interface){
+						ring->owner = Host;
+						ring->len = len;
+						memmove(ring->pkt, des->bp, len);
+						ether->ri = NEXT(ether->ri, ether->nrb);
+					}
+				}
+
+				des->control &= Er;
+				des->control |= Rbsz;
+				des->status = Own;
+
+				ctlr->rdrx = NEXT(ctlr->rdrx, ctlr->nrdr);
+				des = &ctlr->rdr[ctlr->rdrx];
+			}
+			status &= ~Ri;
+		}
+
+		/*
+		 * Check the transmit side:
+		 *	check for Transmit Underflow and Adjust
+		 *	the threshold upwards;
+		 *	free any transmitted buffers and try to
+		 *	top-up the ring.
+		 */
+		if(status & Unf){
+			csr32w(ctlr, 6, ctlr->csr6 & ~St);
+			switch(ctlr->csr6 & Tr){
+			case Tr128:
+				len = Tr256;
+				break;
+			case Tr256:
+				len = Tr512;
+				break;
+			case Tr512:
+				len = Tr1024;
+				break;
+			default:
+			case Tr1024:
+				len = Sf;
+				break;
+			}
+			ctlr->csr6 = (ctlr->csr6 & ~Tr)|len;
+			csr32w(ctlr, 6, ctlr->csr6);
+			csr32w(ctlr, 5, Tps);
+			status &= ~(Unf|Tps);
+		}
+
+		while(ctlr->ntq){
+			des = &ctlr->tdr[ctlr->tdri];
+			if(des->status & Own)
+				break;
+
+			if(des->status & Es){
+				if(des->status & Uf)
+					ctlr->uf++;
+				if(des->status & Ec)
+					ctlr->ec++;
+				if(des->status & Lc)
+					ctlr->lc++;
+				if(des->status & Nc)
+					ctlr->nc++;
+				if(des->status & Lo)
+					ctlr->lo++;
+				if(des->status & To)
+					ctlr->to++;
+			}
+
+			freeb(des->bp);
+			des->control &= Er;
+
+			ctlr->ntq--;
+			ctlr->tdri = NEXT(ctlr->tdri, ctlr->ntdr);
+		}
+		transmit(ether);
+
+		/*
+		 * Anything left not catered for?
+		 */
+		if(status)
+			panic("#l%d: status %8.8uX\n", ether->ctlrno, status);
+	}
+}
+
+static void
+ctlrinit(Ether* ether)
+{
+	Ctlr *ctlr;
+	Des *des;
+	Block *bp;
+	int i;
+	uchar bi[Eaddrlen*2];
+
+	ctlr = ether->ctlr;
+
+	/*
+	 * Allocate and initialise the receive ring;
+	 * allocate and initialise the transmit ring;
+	 * unmask interrupts and start the transmit side;
+	 * create and post a setup packet to initialise
+	 * the physical ethernet address.
+	 */
+	ctlr->rdr = malloc(ctlr->nrdr*sizeof(Des));
+	for(des = ctlr->rdr; des < &ctlr->rdr[ctlr->nrdr]; des++){
+		des->bp = malloc(Rbsz);
+		des->status = Own;
+		des->control = Rbsz;
+		des->addr = PADDR(des->bp);
+	}
+	ctlr->rdr[ctlr->nrdr-1].control |= Er;
+	ctlr->rdrx = 0;
+	csr32w(ctlr, 3, PADDR(ctlr->rdr));
+
+	ctlr->tdr = ialloc(ctlr->ntdr*sizeof(Des), 32);
+	ctlr->tdr[ctlr->ntdr-1].control |= Er;
+	ctlr->tdrh = 0;
+	ctlr->tdri = 0;
+	csr32w(ctlr, 4, PADDR(ctlr->tdr));
+
+	/*
+	 * Clear any bits in the Status Register (CSR5) as
+	 * the PNIC has a different reset value from a true 2114x.
+	 */
+	ctlr->mask = Nis|Ais|Fbe|Rwt|Rps|Ru|Ri|Unf|Tjt|Tps|Ti;
+	csr32w(ctlr, 5, ctlr->mask);
+	csr32w(ctlr, 7, ctlr->mask);
+	ctlr->csr6 |= St;
+	csr32w(ctlr, 6, ctlr->csr6);
+
+	for(i = 0; i < Eaddrlen/2; i++){
+		bi[i*4] = ether->ea[i*2];
+		bi[i*4+1] = ether->ea[i*2+1];
+		bi[i*4+2] = ether->ea[i*2+1];
+		bi[i*4+3] = ether->ea[i*2];
+	}
+	bp = allocb(Eaddrlen*2*16);
+	memset(bp->rp, 0xFF, sizeof(bi));
+	for(i = sizeof(bi); i < sizeof(bi)*16; i += sizeof(bi))
+		memmove(bp->rp+i, bi, sizeof(bi));
+	bp->wp += sizeof(bi)*16;
+
+	ctlr->setupbp = bp;
+	transmit(ether);
+}
+
+static void
+csr9w(Ctlr* ctlr, int data)
+{
+	csr32w(ctlr, 9, data);
+	microdelay(1);
+}
+
+static int
+miimdi(Ctlr* ctlr, int n)
+{
+	int data, i;
+
+	/*
+	 * Read n bits from the MII Management Register.
+	 */
+	data = 0;
+	for(i = n-1; i >= 0; i--){
+		if(csr32r(ctlr, 9) & Mdi)
+			data |= (1<<i);
+		csr9w(ctlr, Mii|Mdc);
+		csr9w(ctlr, Mii);
+	}
+	csr9w(ctlr, 0);
+
+	return data;
+}
+
+static void
+miimdo(Ctlr* ctlr, int bits, int n)
+{
+	int i, mdo;
+
+	/*
+	 * Write n bits to the MII Management Register.
+	 */
+	for(i = n-1; i >= 0; i--){
+		if(bits & (1<<i))
+			mdo = Mdo;
+		else
+			mdo = 0;
+		csr9w(ctlr, mdo);
+		csr9w(ctlr, mdo|Mdc);
+		csr9w(ctlr, mdo);
+	}
+}
+
+static int
+miir(Ctlr* ctlr, int phyad, int regad)
+{
+	int data, i;
+
+	if(ctlr->id == Pnic){
+		i = 1000;
+		csr32w(ctlr, 20, 0x60020000|(phyad<<23)|(regad<<18));
+		do{
+			microdelay(1);
+			data = csr32r(ctlr, 20);
+		}while((data & 0x80000000) && --i);
+
+		if(i == 0)
+			return -1;
+		return data & 0xFFFF;
+	}
+
+	/*
+	 * Preamble;
+	 * ST+OP+PHYAD+REGAD;
+	 * TA + 16 data bits.
+	 */
+	miimdo(ctlr, 0xFFFFFFFF, 32);
+	miimdo(ctlr, 0x1800|(phyad<<5)|regad, 14);
+	data = miimdi(ctlr, 18);
+
+	if(data & 0x10000)
+		return -1;
+
+	return data & 0xFFFF;
+}
+
+static void
+miiw(Ctlr* ctlr, int phyad, int regad, int data)
+{
+	/*
+	 * Preamble;
+	 * ST+OP+PHYAD+REGAD+TA + 16 data bits;
+	 * Z.
+	 */
+	miimdo(ctlr, 0xFFFFFFFF, 32);
+	data &= 0xFFFF;
+	data |= (0x05<<(5+5+2+16))|(phyad<<(5+2+16))|(regad<<(2+16))|(0x02<<16);
+	miimdo(ctlr, data, 32);
+	csr9w(ctlr, Mdc);
+	csr9w(ctlr, 0);
+}
+
+static int
+sromr(Ctlr* ctlr, int r)
+{
+	int i, op, data, size;
+
+	if(ctlr->id == Pnic){
+		i = 1000;
+		csr32w(ctlr, 19, 0x600|r);
+		do{
+			microdelay(1);
+			data = csr32r(ctlr, 19);
+		}while((data & 0x80000000) && --i);
+
+		if(ctlr->sromsz == 0)
+			ctlr->sromsz = 6;
+
+		return csr32r(ctlr, 9) & 0xFFFF;
+	}
+
+	/*
+	 * This sequence for reading a 16-bit register 'r'
+	 * in the EEPROM is taken (pretty much) straight from Section
+	 * 7.4 of the 21140 Hardware Reference Manual.
+	 */
+reread:
+	csr9w(ctlr, Rd|Ss);
+	csr9w(ctlr, Rd|Ss|Scs);
+	csr9w(ctlr, Rd|Ss|Sclk|Scs);
+	csr9w(ctlr, Rd|Ss);
+
+	op = 0x06;
+	for(i = 3-1; i >= 0; i--){
+		data = Rd|Ss|(((op>>i) & 0x01)<<2)|Scs;
+		csr9w(ctlr, data);
+		csr9w(ctlr, data|Sclk);
+		csr9w(ctlr, data);
+	}
+
+	/*
+	 * First time through must work out the EEPROM size.
+	 * This doesn't seem to work on the 21041 as implemented
+	 * in Virtual PC for the Mac, so wire any 21041 to 6,
+	 * it's the only 21041 this code will ever likely see.
+	 */
+	if((size = ctlr->sromsz) == 0){
+		if(ctlr->id == Tulip1)
+			ctlr->sromsz = size = 6;
+		else
+			size = 8;
+	}
+
+	for(size = size-1; size >= 0; size--){
+		data = Rd|Ss|(((r>>size) & 0x01)<<2)|Scs;
+		csr9w(ctlr, data);
+		csr9w(ctlr, data|Sclk);
+		csr9w(ctlr, data);
+		microdelay(1);
+		if(ctlr->sromsz == 0 && !(csr32r(ctlr, 9) & Sdo))
+			break;
+	}
+
+	data = 0;
+	for(i = 16-1; i >= 0; i--){
+		csr9w(ctlr, Rd|Ss|Sclk|Scs);
+		if(csr32r(ctlr, 9) & Sdo)
+			data |= (1<<i);
+		csr9w(ctlr, Rd|Ss|Scs);
+	}
+
+	csr9w(ctlr, 0);
+
+	if(ctlr->sromsz == 0){
+		ctlr->sromsz = 8-size;
+		goto reread;
+	}
+
+	return data & 0xFFFF;
+}
+
+static void
+softreset(Ctlr* ctlr)
+{
+	/*
+	 * Soft-reset the controller and initialise bus mode.
+	 * Delay should be >= 50 PCI cycles (2×S @ 25MHz).
+	 */
+	csr32w(ctlr, 0, Swr);
+	microdelay(10);
+	csr32w(ctlr, 0, Rml|Cal16);
+	delay(1);
+}
+
+static int
+type5block(Ctlr* ctlr, uchar* block)
+{
+	int csr15, i, len;
+
+	/*
+	 * Reset or GPR sequence. Reset should be once only,
+	 * before the GPR sequence.
+	 * Note 'block' is not a pointer to the block head but
+	 * a pointer to the data in the block starting at the
+	 * reset length value so type5block can be used for the
+	 * sequences contained in type 1 and type 3 blocks.
+	 * The SROM docs state the 21140 type 5 block is the
+	 * same as that for the 21143, but the two controllers
+	 * use different registers and sequence-element lengths
+	 * so the 21140 code here is a guess for a real type 5
+	 * sequence.
+	 */
+	len = *block++;
+	if(ctlr->id != Tulip3){
+		for(i = 0; i < len; i++){
+			csr32w(ctlr, 12, *block);
+			block++;
+		}
+		return len;
+	}
+
+	for(i = 0; i < len; i++){
+		csr15 = *block++<<16;
+		csr15 |= *block++<<24;
+		csr32w(ctlr, 15, csr15);
+		debug("%8.8uX ", csr15);
+	}
+	return 2*len;
+}
+
+static int
+typephylink(Ctlr* ctlr, uchar*)
+{
+	int an, bmcr, bmsr, csr6, x;
+
+	/*
+	 * Fail if
+	 *	auto-negotiataion enabled but not complete;
+	 *	no valid link established.
+	 */
+	bmcr = miir(ctlr, ctlr->curphyad, Bmcr);
+	miir(ctlr, ctlr->curphyad, Bmsr);
+	bmsr = miir(ctlr, ctlr->curphyad, Bmsr);
+	debug("bmcr 0x%2.2uX bmsr 0x%2.2uX\n", bmcr, bmsr);
+	if(((bmcr & 0x1000) && !(bmsr & 0x0020)) || !(bmsr & 0x0004))
+		return 0;
+
+	if(bmcr & 0x1000){
+		an = miir(ctlr, ctlr->curphyad, Anar);
+		an &= miir(ctlr, ctlr->curphyad, Anlpar) & 0x3E0;
+		debug("an 0x%2.uX 0x%2.2uX 0x%2.2uX\n",
+	    		miir(ctlr, ctlr->curphyad, Anar),
+			miir(ctlr, ctlr->curphyad, Anlpar),
+			an);
+	
+		if(an & 0x0100)
+			x = 0x4000;
+		else if(an & 0x0080)
+			x = 0x2000;
+		else if(an & 0x0040)
+			x = 0x1000;
+		else if(an & 0x0020)
+			x = 0x0800;
+		else
+			x = 0;
+	}
+	else if((bmcr & 0x2100) == 0x2100)
+		x = 0x4000;
+	else if(bmcr & 0x2000){
+		/*
+		 * If FD capable, force it if necessary.
+		 */
+		if((bmsr & 0x4000) && ctlr->fd){
+			miiw(ctlr, ctlr->curphyad, Bmcr, 0x2100);
+			x = 0x4000;
+		}
+		else
+			x = 0x2000;
+	}
+	else if(bmcr & 0x0100)
+		x = 0x1000;
+	else
+		x = 0x0800;
+
+	csr6 = Sc|Mbo|Hbd|Ps|Ca|TrMODE|Sb;
+	if(ctlr->fdx & x)
+		csr6 |= Fd;
+	if(ctlr->ttm & x)
+		csr6 |= Ttm;
+	debug("csr6 0x%8.8uX 0x%8.8uX 0x%8.8luX\n",
+		csr6, ctlr->csr6, csr32r(ctlr, 6));
+	if(csr6 != ctlr->csr6){
+		ctlr->csr6 = csr6;
+		csr32w(ctlr, 6, csr6);
+	}
+
+	return 1;
+}
+
+static int
+typephymode(Ctlr* ctlr, uchar* block, int wait)
+{
+	uchar *p;
+	int len, mc, nway, phyx, timeo;
+
+	if(DEBUG){
+		int i;
+
+		len = (block[0] & ~0x80)+1;
+		for(i = 0; i < len; i++)
+			debug("%2.2uX ", block[i]);
+		debug("\n");
+	}
+
+	if(block[1] == 1)
+		len = 1;
+	else if(block[1] == 3)
+		len = 2;
+	else
+		return -1;
+
+	/*
+	 * Snarf the media capabilities, nway advertisment,
+	 * FDX and TTM bitmaps.
+	 */
+	p = &block[5+len*block[3]+len*block[4+len*block[3]]];
+	mc = *p++;
+	mc |= *p++<<8;
+	nway = *p++;
+	nway |= *p++<<8;
+	ctlr->fdx = *p++;
+	ctlr->fdx |= *p++<<8;
+	ctlr->ttm = *p++;
+	ctlr->ttm |= *p<<8;
+	debug("mc %4.4uX nway %4.4uX fdx %4.4uX ttm %4.4uX\n",
+		mc, nway, ctlr->fdx, ctlr->ttm);
+	USED(mc);
+
+	phyx = block[2];
+	ctlr->curphyad = ctlr->phy[phyx];
+
+	ctlr->csr6 = 0;//Sc|Mbo|Hbd|Ps|Ca|TrMODE|Sb;
+	//csr32w(ctlr, 6, ctlr->csr6);
+	if(typephylink(ctlr, block))
+		return 0;
+
+	if(!(ctlr->phyreset & (1<<phyx))){
+		debug("reset seq: len %d: ", block[3]);
+		if(ctlr->type5block)
+			type5block(ctlr, &ctlr->type5block[2]);
+		else
+			type5block(ctlr, &block[4+len*block[3]]);
+		debug("\n");
+		ctlr->phyreset |= (1<<phyx);
+	}
+
+	/*
+	 * GPR sequence.
+	 */
+	debug("gpr seq: len %d: ", block[3]);
+	type5block(ctlr, &block[3]);
+	debug("\n");
+
+	ctlr->csr6 = 0;//Sc|Mbo|Hbd|Ps|Ca|TrMODE|Sb;
+	//csr32w(ctlr, 6, ctlr->csr6);
+	if(typephylink(ctlr, block))
+		return 0;
+
+	/*
+	 * Turn off auto-negotiation, set the auto-negotiation
+	 * advertisment register then start the auto-negotiation
+	 * process again.
+	 */
+	miiw(ctlr, ctlr->curphyad, Bmcr, 0);
+	miiw(ctlr, ctlr->curphyad, Anar, nway|1);
+	miiw(ctlr, ctlr->curphyad, Bmcr, 0x1000);
+
+	if(!wait)
+		return 0;
+
+	for(timeo = 0; timeo < 30; timeo++){
+		if(typephylink(ctlr, block))
+			return 0;
+		delay(100);
+	}
+
+	return -1;
+}
+
+static int
+typesymmode(Ctlr *ctlr, uchar *block, int wait)
+{
+	uint gpmode, gpdata, command;
+
+	USED(wait);
+	gpmode = block[3] | ((uint) block[4] << 8);
+	gpdata = block[5] | ((uint) block[6] << 8);
+	command = (block[7] | ((uint) block[8] << 8)) & 0x71;
+	if (command & 0x8000) {
+		print("ether2114x.c: FIXME: handle type 4 mode blocks where cmd.active_invalid != 0\n");
+		return -1;
+	}
+	csr32w(ctlr, 15, gpmode);
+	csr32w(ctlr, 15, gpdata);
+	ctlr->csr6 = (command & 0x71) << 18;
+	csr32w(ctlr, 6, ctlr->csr6);
+	return 0;
+}
+
+static int
+type2mode(Ctlr* ctlr, uchar* block, int)
+{
+	uchar *p;
+	int csr6, csr13, csr14, csr15, gpc, gpd;
+
+	csr6 = Sc|Mbo|Ca|TrMODE|Sb;
+	debug("type2mode: medium 0x%2.2uX\n", block[2]);
+
+	/*
+	 * Don't attempt full-duplex
+	 * unless explicitly requested.
+	 */
+	if((block[2] & 0x3F) == 0x04){	/* 10BASE-TFD */
+		if(!ctlr->fd)
+			return -1;
+		csr6 |= Fd;
+	}
+
+	/*
+	 * Operating mode programming values from the datasheet
+	 * unless media specific data is explicitly given.
+	 */
+	p = &block[3];
+	if(block[2] & 0x40){
+		csr13 = (block[4]<<8)|block[3];
+		csr14 = (block[6]<<8)|block[5];
+		csr15 = (block[8]<<8)|block[7];
+		p += 6;
+	}
+	else switch(block[2] & 0x3F){
+	default:
+		return -1;
+	case 0x00:			/* 10BASE-T */
+		csr13 = 0x00000001;
+		csr14 = 0x00007F3F;
+		csr15 = 0x00000008;
+		break;
+	case 0x01:			/* 10BASE-2 */
+		csr13 = 0x00000009;
+		csr14 = 0x00000705;
+		csr15 = 0x00000006;
+		break;
+	case 0x02:			/* 10BASE-5 (AUI) */
+		csr13 = 0x00000009;
+		csr14 = 0x00000705;
+		csr15 = 0x0000000E;
+		break;
+	case 0x04:			/* 10BASE-TFD */
+		csr13 = 0x00000001;
+		csr14 = 0x00007F3D;
+		csr15 = 0x00000008;
+		break;
+	}
+	gpc = *p++<<16;
+	gpc |= *p++<<24;
+	gpd = *p++<<16;
+	gpd |= *p<<24;
+
+	csr32w(ctlr, 13, 0);
+	csr32w(ctlr, 14, csr14);
+	csr32w(ctlr, 15, gpc|csr15);
+	delay(10);
+	csr32w(ctlr, 15, gpd|csr15);
+	csr32w(ctlr, 13, csr13);
+
+	ctlr->csr6 = csr6;
+	csr32w(ctlr, 6, ctlr->csr6);
+
+	debug("type2mode: csr13 %8.8uX csr14 %8.8uX csr15 %8.8uX\n",
+		csr13, csr14, csr15);
+	debug("type2mode: gpc %8.8uX gpd %8.8uX csr6 %8.8uX\n",
+		gpc, gpd, csr6);
+
+	return 0;
+}
+
+static int
+type0link(Ctlr* ctlr, uchar* block)
+{
+	int m, polarity, sense;
+
+	m = (block[3]<<8)|block[2];
+	sense = 1<<((m & 0x000E)>>1);
+	if(m & 0x0080)
+		polarity = sense;
+	else
+		polarity = 0;
+
+	return (csr32r(ctlr, 12) & sense)^polarity;
+}
+
+static int
+type0mode(Ctlr* ctlr, uchar* block, int wait)
+{
+	int csr6, m, timeo;
+
+	csr6 = Sc|Mbo|Hbd|Ca|TrMODE|Sb;
+debug("type0: medium 0x%uX, fd %d: 0x%2.2uX 0x%2.2uX 0x%2.2uX 0x%2.2uX\n",
+    ctlr->medium, ctlr->fd, block[0], block[1], block[2], block[3]); 
+	switch(block[0]){
+	default:
+		break;
+
+	case 0x04:			/* 10BASE-TFD */
+	case 0x05:			/* 100BASE-TXFD */
+	case 0x08:			/* 100BASE-FXFD */
+		/*
+		 * Don't attempt full-duplex
+		 * unless explicitly requested.
+		 */
+		if(!ctlr->fd)
+			return -1;
+		csr6 |= Fd;
+		break;
+	}
+
+	m = (block[3]<<8)|block[2];
+	if(m & 0x0001)
+		csr6 |= Ps;
+	if(m & 0x0010)
+		csr6 |= Ttm;
+	if(m & 0x0020)
+		csr6 |= Pcs;
+	if(m & 0x0040)
+		csr6 |= Scr;
+
+	csr32w(ctlr, 12, block[1]);
+	microdelay(10);
+	csr32w(ctlr, 6, csr6);
+	ctlr->csr6 = csr6;
+
+	if(!wait)
+		return 0;
+
+	for(timeo = 0; timeo < 30; timeo++){
+		if(type0link(ctlr, block))
+			return 0;
+		delay(100);
+	}
+
+	return -1;
+}
+
+static int
+media21041(Ether* ether, int wait)
+{
+	Ctlr* ctlr;
+	uchar *block;
+	int csr6, csr13, csr14, csr15, medium, timeo;
+
+	ctlr = ether->ctlr;
+	block = ctlr->infoblock[ctlr->curk];
+	debug("media21041: block[0] %2.2uX, medium %4.4uX sct %4.4uX\n",
+		block[0], ctlr->medium, ctlr->sct);
+
+	medium = block[0] & 0x3F;
+	if(ctlr->medium >= 0 && medium != ctlr->medium)
+		return 0;
+	if(ctlr->sct != 0x0800 && (ctlr->sct & 0x3F) != medium)
+		return 0;
+
+	csr6 = Sc|Mbo|Ca|TrMODE|Sb;
+	if(block[0] & 0x40){
+		csr13 = (block[2]<<8)|block[1];
+		csr14 = (block[4]<<8)|block[3];
+		csr15 = (block[6]<<8)|block[5];
+	}
+	else switch(medium){
+	default:
+		return -1;
+	case 0x00:		/* 10BASE-T */
+		csr13 = 0xEF01;
+		csr14 = 0xFF3F;
+		csr15 = 0x0008;
+		break;
+	case 0x01:		/* 10BASE-2 */
+		csr13 = 0xEF09;
+		csr14 = 0xF73D;
+		csr15 = 0x0006;
+		break;
+	case 0x02:		/* 10BASE-5 */
+		csr13 = 0xEF09;
+		csr14 = 0xF73D;
+		csr15 = 0x000E;
+		break;
+	case 0x04:		/* 10BASE-TFD */
+		csr13 = 0xEF01;
+		csr14 = 0xFF3D;
+		csr15 = 0x0008;
+		break;
+	}
+
+	csr32w(ctlr, 13, 0);
+	csr32w(ctlr, 14, csr14);
+	csr32w(ctlr, 15, csr15);
+	csr32w(ctlr, 13, csr13);
+	delay(10);
+
+	if(medium == 0x04)
+		csr6 |= Fd;
+	ctlr->csr6 = csr6;
+	csr32w(ctlr, 6, ctlr->csr6);
+
+	debug("media21041: csr6 %8.8uX csr13 %4.4uX csr14 %4.4uX csr15 %4.4uX\n",
+		csr6, csr13, csr14, csr15);
+
+	if(!wait)
+		return 0;
+
+	for(timeo = 0; timeo < 30; timeo++){
+		if(!(csr32r(ctlr, 12) & 0x0002)){
+			debug("media21041: ok: csr12 %4.4luX timeo %d\n",
+				csr32r(ctlr, 12), timeo);
+			return 10;
+		}
+		delay(100);
+	}
+	debug("media21041: !ok: csr12 %4.4luX\n", csr32r(ctlr, 12));
+
+	return -1;
+}
+
+static int
+mediaxx(Ether* ether, int wait)
+{
+	Ctlr* ctlr;
+	uchar *block;
+
+	ctlr = ether->ctlr;
+	block = ctlr->infoblock[ctlr->curk];
+	if(block[0] & 0x80){
+		switch(block[1]){
+		default:
+			return -1;
+		case 0:
+			if(ctlr->medium >= 0 && block[2] != ctlr->medium)
+				return 0;
+/* need this test? */	if(ctlr->sct != 0x0800 && (ctlr->sct & 0x3F) != block[2])
+				return 0;
+			if(type0mode(ctlr, block+2, wait))
+				return 0;
+			break;
+		case 1:
+			if(typephymode(ctlr, block, wait))
+				return 0;
+			break;
+		case 2:
+			debug("type2: medium %d block[2] %d\n",
+				ctlr->medium, block[2]);
+			if(ctlr->medium >= 0 && ((block[2] & 0x3F) != ctlr->medium))
+				return 0;
+			if(type2mode(ctlr, block, wait))
+				return 0;
+			break;
+		case 3:
+			if(typephymode(ctlr, block, wait))
+				return 0;
+			break;
+		case 4:
+			debug("type4: medium %d block[2] %d\n",
+				ctlr->medium, block[2]);
+			if(ctlr->medium >= 0 && ((block[2] & 0x3F) != ctlr->medium))
+				return 0;
+			if(typesymmode(ctlr, block, wait))
+				return 0;
+			break;
+		}
+	}
+	else{
+		if(ctlr->medium >= 0 && block[0] != ctlr->medium)
+			return 0;
+/* need this test? */if(ctlr->sct != 0x0800 && (ctlr->sct & 0x3F) != block[0])
+			return 0;
+		if(type0mode(ctlr, block, wait))
+			return 0;
+	}
+
+	if(ctlr->csr6){
+		if(!(ctlr->csr6 & Ps) || (ctlr->csr6 & Ttm))
+			return 10;
+		return 100;
+	}
+
+	return 0;
+}
+
+static int
+media(Ether* ether, int wait)
+{
+	Ctlr* ctlr;
+	int k, mbps;
+
+	ctlr = ether->ctlr;
+	for(k = 0; k < ctlr->k; k++){
+		switch(ctlr->id){
+		default:
+			mbps = mediaxx(ether, wait);
+			break;
+		case Tulip1:			/* 21041 */
+			mbps = media21041(ether, wait);
+			break;
+		}
+		if(mbps > 0)
+			return mbps;
+		if(ctlr->curk == 0)
+			ctlr->curk = ctlr->k-1;
+		else
+			ctlr->curk--;
+	}
+
+	return 0;
+}
+
+static char* mediatable[9] = {
+	"10BASE-T",			/* TP */
+	"10BASE-2",			/* BNC */
+	"10BASE-5",			/* AUI */
+	"100BASE-TX",
+	"10BASE-TFD",
+	"100BASE-TXFD",
+	"100BASE-T4",
+	"100BASE-FX",
+	"100BASE-FXFD",
+};
+
+static uchar en1207[] = {		/* Accton EN1207-COMBO */
+	0x00, 0x00, 0xE8,		/* [0]  vendor ethernet code */
+	0x00,				/* [3]  spare */
+
+	0x00, 0x08,			/* [4]  connection (LSB+MSB = 0x0800) */
+	0x1F,				/* [6]  general purpose control */
+	2,				/* [7]  block count */
+
+	0x00,				/* [8]  media code (10BASE-TX) */
+	0x0B,				/* [9]  general purpose port data */
+	0x9E, 0x00,			/* [10] command (LSB+MSB = 0x009E) */
+
+	0x03,				/* [8]  media code (100BASE-TX) */
+	0x1B,				/* [9]  general purpose port data */
+	0x6D, 0x00,			/* [10] command (LSB+MSB = 0x006D) */
+
+					/* There is 10BASE-2 as well, but... */
+};
+
+static uchar ana6910fx[] = {		/* Adaptec (Cogent) ANA-6910FX */
+	0x00, 0x00, 0x92,		/* [0]  vendor ethernet code */
+	0x00,				/* [3]  spare */
+
+	0x00, 0x08,			/* [4]  connection (LSB+MSB = 0x0800) */
+	0x3F,				/* [6]  general purpose control */
+	1,				/* [7]  block count */
+
+	0x07,				/* [8]  media code (100BASE-FX) */
+	0x03,				/* [9]  general purpose port data */
+	0x2D, 0x00			/* [10] command (LSB+MSB = 0x000D) */
+};
+
+static uchar smc9332[] = {		/* SMC 9332 */
+	0x00, 0x00, 0xC0,		/* [0]  vendor ethernet code */
+	0x00,				/* [3]  spare */
+
+	0x00, 0x08,			/* [4]  connection (LSB+MSB = 0x0800) */
+	0x1F,				/* [6]  general purpose control */
+	2,				/* [7]  block count */
+
+	0x00,				/* [8]  media code (10BASE-TX) */
+	0x00,				/* [9]  general purpose port data */
+	0x9E, 0x00,			/* [10] command (LSB+MSB = 0x009E) */
+
+	0x03,				/* [8]  media code (100BASE-TX) */
+	0x09,				/* [9]  general purpose port data */
+	0x6D, 0x00,			/* [10] command (LSB+MSB = 0x006D) */
+};
+
+static uchar* leaf21140[] = {
+	en1207,				/* Accton EN1207-COMBO */
+	ana6910fx,			/* Adaptec (Cogent) ANA-6910FX */
+	smc9332,			/* SMC 9332 */
+	0,
+};
+
+/*
+ * Copied to ctlr->srom at offset 20.
+ */
+static uchar leafpnic[] = {
+	0x00, 0x00, 0x00, 0x00,		/* MAC address */
+	0x00, 0x00,
+	0x00,				/* controller 0 device number */
+	0x1E, 0x00,			/* controller 0 info leaf offset */
+	0x00,				/* reserved */
+	0x00, 0x08,			/* selected connection type */
+	0x00,				/* general purpose control */
+	0x01,				/* block count */
+
+	0x8C,				/* format indicator and count */
+	0x01,				/* block type */
+	0x00,				/* PHY number */
+	0x00,				/* GPR sequence length */
+	0x00,				/* reset sequence length */
+	0x00, 0x78,			/* media capabilities */
+	0xE0, 0x01,			/* Nway advertisment */
+	0x00, 0x50,			/* FDX bitmap */
+	0x00, 0x18,			/* TTM bitmap */
+};
+
+static int
+srom(Ctlr* ctlr)
+{
+	int i, k, oui, phy, x;
+	uchar *p;
+
+	/*
+	 * This is a partial decoding of the SROM format described in
+	 * 'Digital Semiconductor 21X4 Serial ROM Format, Version 4.05,
+	 * 2-Mar-98'. Only the 2114[03] are handled, support for other
+	 * controllers can be added as needed.
+	 */
+	sromr(ctlr, 0);
+	if(ctlr->srom == nil)
+		ctlr->srom = malloc((1<<ctlr->sromsz)*sizeof(ushort));
+	for(i = 0; i < (1<<ctlr->sromsz); i++){
+		x = sromr(ctlr, i);
+		ctlr->srom[2*i] = x;
+		ctlr->srom[2*i+1] = x>>8;
+	}
+
+	if(DEBUG){
+		print("srom:");
+		for(i = 0; i < ((1<<ctlr->sromsz)*sizeof(ushort)); i++){
+			if(i && ((i & 0x0F) == 0))
+				print("\n     ");
+			print(" %2.2uX", ctlr->srom[i]);
+		}
+		print("\n");
+	}
+
+	/*
+	 * There are 2 SROM layouts:
+	 *	e.g. Digital EtherWORKS	station address at offset 20;
+	 *				this complies with the 21140A SROM
+	 *				application note from Digital;
+	 * 	e.g. SMC9332		station address at offset 0 followed by
+	 *				2 additional bytes, repeated at offset
+	 *				6; the 8 bytes are also repeated in
+	 *				reverse order at offset 8.
+	 * To check which it is, read the SROM and check for the repeating
+	 * patterns of the non-compliant cards; if that fails use the one at
+	 * offset 20.
+	 */
+	ctlr->sromea = ctlr->srom;
+	for(i = 0; i < 8; i++){
+		x = ctlr->srom[i];
+		if(x != ctlr->srom[15-i] || x != ctlr->srom[16+i]){
+			ctlr->sromea = &ctlr->srom[20];
+			break;
+		}
+	}
+
+	/*
+	 * Fake up the SROM for the PNIC.
+	 * It looks like a 21140 with a PHY.
+	 * The MAC address is byte-swapped in the orginal SROM data.
+	 */
+	if(ctlr->id == Pnic){
+		memmove(&ctlr->srom[20], leafpnic, sizeof(leafpnic));
+		for(i = 0; i < Eaddrlen; i += 2){
+			ctlr->srom[20+i] = ctlr->srom[i+1];
+			ctlr->srom[20+i+1] = ctlr->srom[i];
+		}
+	}
+
+	/*
+	 * Next, try to find the info leaf in the SROM for media detection.
+	 * If it's a non-conforming card try to match the vendor ethernet code
+	 * and point p at a fake info leaf with compact 21140 entries.
+	 */
+	if(ctlr->sromea == ctlr->srom){
+		p = nil;
+		for(i = 0; leaf21140[i] != nil; i++){
+			if(memcmp(leaf21140[i], ctlr->sromea, 3) == 0){
+				p = &leaf21140[i][4];
+				break;
+			}
+		}
+		if(p == nil)
+			return -1;
+	}
+	else
+		p = &ctlr->srom[(ctlr->srom[28]<<8)|ctlr->srom[27]];
+
+	/*
+	 * Set up the info needed for later media detection.
+	 * For the 21140, set the general-purpose mask in CSR12.
+	 * The info block entries are stored in order of increasing
+	 * precedence, so detection will work backwards through the
+	 * stored indexes into ctlr->srom.
+	 * If an entry is found which matches the selected connection
+	 * type, save the index. Otherwise, start at the last entry.
+	 * If any MII entries are found (type 1 and 3 blocks), scan
+	 * for PHYs.
+	 */
+	ctlr->leaf = p;
+	ctlr->sct = *p++;
+	ctlr->sct |= *p++<<8;
+	if(ctlr->id != Tulip3 && ctlr->id != Tulip1){
+		csr32w(ctlr, 12, Gpc|*p++);
+		delay(200);
+	}
+	ctlr->k = *p++;
+	if(ctlr->k >= nelem(ctlr->infoblock))
+		ctlr->k = nelem(ctlr->infoblock)-1;
+	ctlr->sctk = ctlr->k-1;
+	phy = 0;
+	for(k = 0; k < ctlr->k; k++){
+		ctlr->infoblock[k] = p;
+		if(ctlr->id == Tulip1){
+			debug("type21041: 0x%2.2uX\n", p[0]); 
+			if(ctlr->sct != 0x0800 && *p == (ctlr->sct & 0xFF))
+				ctlr->sctk = k;
+			if(*p & 0x40)
+				p += 7;
+			else
+				p += 1;
+		}
+		/*
+		 * The RAMIX PMC665 has a badly-coded SROM,
+		 * hence the test for 21143 and type 3.
+		 */
+		else if((*p & 0x80) || (ctlr->id == Tulip3 && *(p+1) == 3)){
+			*p |= 0x80;
+			if(*(p+1) == 1 || *(p+1) == 3)
+				phy = 1;
+			if(*(p+1) == 5)
+				ctlr->type5block = p;
+			p += (*p & ~0x80)+1;
+		}
+		else{
+			debug("type0: 0x%2.2uX 0x%2.2uX 0x%2.2uX 0x%2.2uX\n",
+				p[0], p[1], p[2], p[3]); 
+			if(ctlr->sct != 0x0800 && *p == (ctlr->sct & 0xFF))
+				ctlr->sctk = k;
+			p += 4;
+		}
+	}
+	ctlr->curk = ctlr->sctk;
+	debug("sct 0x%uX medium 0x%uX k %d curk %d phy %d\n",
+		ctlr->sct, ctlr->medium, ctlr->k, ctlr->curk, phy);
+
+	if(phy){
+		x = 0;
+		for(k = 0; k < nelem(ctlr->phy); k++){
+			if((oui = miir(ctlr, k, 2)) == -1 || oui == 0)
+				continue;
+			if(DEBUG){
+				oui = (oui & 0x3FF)<<6;
+				oui |= miir(ctlr, k, 3)>>10;
+				miir(ctlr, k, 1);
+				debug("phy%d: index %d oui %uX reg1 %uX\n",
+					x, k, oui, miir(ctlr, k, 1));
+				USED(oui);
+			}
+			ctlr->phy[x] = k;
+		}
+	}
+
+	ctlr->fd = 0;
+	ctlr->medium = -1;
+
+	return 0;
+}
+
+static void
+dec2114xpci(void)
+{
+	Ctlr *ctlr;
+	Pcidev *p;
+	int x;
+
+	p = nil;
+	while(p = pcimatch(p, 0, 0)){
+		if(p->ccrb != 0x02 || p->ccru != 0)
+			continue;
+		switch((p->did<<16)|p->vid){
+		default:
+			continue;
+
+		case Tulip3:			/* 21143 */
+			/*
+			 * Exit sleep mode.
+			 */
+			x = pcicfgr32(p, 0x40);
+			x &= ~0xC0000000;
+			pcicfgw32(p, 0x40, x);
+			/*FALLTHROUGH*/
+
+		case Pnic:			/* PNIC */
+		case Pnic2:			/* PNIC-II */
+		case Tulip0:			/* 21140 */
+		case Tulip1:			/* 21041 */
+			break;
+		}
+
+		/*
+		 * bar[0] is the I/O port register address and
+		 * bar[1] is the memory-mapped register address.
+		 */
+		ctlr = malloc(sizeof(Ctlr));
+		ctlr->port = p->mem[0].bar & ~0x01;
+		ctlr->pcidev = p;
+		ctlr->id = (p->did<<16)|p->vid;
+		debug("2114x: type 0x%8.8uX rev 0x%4.4uX at port 0x%4.4uX\n",
+			ctlr->id, p->rid, ctlr->port);
+
+		/*
+		 * Some cards (e.g. ANA-6910FX) seem to need the Ps bit
+		 * set or they don't always work right after a hardware
+		 * reset.
+		 */
+		csr32w(ctlr, 6, Mbo|Ps);
+		softreset(ctlr);
+
+		if(srom(ctlr)){
+			free(ctlr);
+			break;
+		}
+
+		switch(ctlr->id){
+		default:
+			break;
+
+		case Pnic:			/* PNIC */
+			/*
+			 * Turn off the jabber timer.
+			 */
+			csr32w(ctlr, 15, 0x00000001);
+			break;
+		}
+
+		if(ctlrhead != nil)
+			ctlrtail->next = ctlr;
+		else
+			ctlrhead = ctlr;
+		ctlrtail = ctlr;
+	}
+}
+
+static void
+detach(Ether* ether)
+{
+	softreset(ether->ctlr);
+}
+
+int
+ether2114xreset(Ether* ether)
+{
+	Ctlr *ctlr;
+	int i, x;
+	uchar ea[Eaddrlen];
+	static int scandone;
+
+	if(scandone == 0){
+		dec2114xpci();
+		scandone = 1;
+	}
+
+	/*
+	 * Any adapter matches if no ether->port is supplied,
+	 * otherwise the ports must match.
+	 */
+	for(ctlr = ctlrhead; ctlr != nil; ctlr = ctlr->next){
+		if(ctlr->active)
+			continue;
+		if(ether->port == 0 || ether->port == ctlr->port){
+			ctlr->active = 1;
+			break;
+		}
+	}
+	if(ctlr == nil)
+		return -1;
+
+	ether->ctlr = ctlr;
+	ether->port = ctlr->port;
+	ether->irq = ctlr->pcidev->intl;
+	ether->tbdf = ctlr->pcidev->tbdf;
+
+	/*
+	 * Check if the adapter's station address is to be overridden.
+	 * If not, read it from the EEPROM and set in ether->ea prior to
+	 * loading the station address in the hardware.
+	 */
+	memset(ea, 0, Eaddrlen);
+	if(memcmp(ea, ether->ea, Eaddrlen) == 0)
+		memmove(ether->ea, ctlr->sromea, Eaddrlen);
+
+	/*
+	 * Look for a medium override in case there's no autonegotiation
+	 * (no MII) or the autonegotiation fails.
+	 */
+	for(i = 0; i < ether->nopt; i++){
+		if(cistrcmp(ether->opt[i], "FD") == 0){
+			ctlr->fd = 1;
+			continue;
+		}
+		for(x = 0; x < nelem(mediatable); x++){
+			debug("compare <%s> <%s>\n", mediatable[x],
+				ether->opt[i]);
+			if(cistrcmp(mediatable[x], ether->opt[i]))
+				continue;
+			ctlr->medium = x;
+	
+			switch(ctlr->medium){
+			default:
+				ctlr->fd = 0;
+				break;
+	
+			case 0x04:		/* 10BASE-TFD */
+			case 0x05:		/* 100BASE-TXFD */
+			case 0x08:		/* 100BASE-FXFD */
+				ctlr->fd = 1;
+				break;
+			}
+			break;
+		}
+	}
+
+	/*
+	 * Determine media.
+	 */
+	ctlr->mbps = media(ether, 1);
+
+	/*
+	 * Initialise descriptor rings, ethernet address.
+	 */
+	ctlr->nrdr = Nrde;
+	ctlr->ntdr = Ntde;
+	pcisetbme(ctlr->pcidev);
+	ctlrinit(ether);
+
+	/*
+	 * Linkage to the generic ethernet driver.
+	 */
+	ether->attach = attach;
+	ether->transmit = transmit;
+	ether->interrupt = interrupt;
+	ether->detach = detach;
+
+	return 0;
+}
--- /dev/null
+++ b/os/boot/pc/ether589.c
@@ -1,0 +1,211 @@
+/*
+ * 3C589 and 3C562.
+ * To do:
+ *	check xcvr10Base2 still works (is GlobalReset necessary?).
+ */
+#include "u.h"
+#include "lib.h"
+#include "mem.h"
+#include "dat.h"
+#include "fns.h"
+#include "io.h"
+#include "error.h"
+
+#include "etherif.h"
+
+enum {						/* all windows */
+	CommandR		= 0x000E,
+	IntStatusR		= 0x000E,
+};
+
+enum {						/* Commands */
+	GlobalReset		= 0x0000,
+	SelectRegisterWindow	= 0x0001,
+	RxReset			= 0x0005,
+	TxReset			= 0x000B,
+	AcknowledgeInterrupt	= 0x000D,
+};
+
+enum {						/* IntStatus bits */
+	commandInProgress	= 0x1000,
+};
+
+#define COMMAND(port, cmd, a)	outs((port)+CommandR, ((cmd)<<11)|(a))
+#define STATUS(port)		ins((port)+IntStatusR)
+
+enum {						/* Window 0 - setup */
+	Wsetup			= 0x0000,
+						/* registers */
+	ManufacturerID		= 0x0000,	/* 3C5[08]*, 3C59[27] */
+	ProductID		= 0x0002,	/* 3C5[08]*, 3C59[27] */
+	ConfigControl		= 0x0004,	/* 3C5[08]*, 3C59[27] */
+	AddressConfig		= 0x0006,	/* 3C5[08]*, 3C59[27] */
+	ResourceConfig		= 0x0008,	/* 3C5[08]*, 3C59[27] */
+	EepromCommand		= 0x000A,
+	EepromData		= 0x000C,
+						/* AddressConfig Bits */
+	autoSelect9		= 0x0080,
+	xcvrMask9		= 0xC000,
+						/* ConfigControl bits */
+	Ena			= 0x0001,
+	base10TAvailable9	= 0x0200,
+	coaxAvailable9		= 0x1000,
+	auiAvailable9		= 0x2000,
+						/* EepromCommand bits */
+	EepromReadRegister	= 0x0080,
+	EepromBusy		= 0x8000,
+};
+
+enum {						/* Window 1 - operating set */
+	Wop			= 0x0001,
+};
+
+enum {						/* Window 3 - FIFO management */
+	Wfifo			= 0x0003,
+						/* registers */
+	InternalConfig		= 0x0000,	/* 3C509B, 3C589, 3C59[0257] */
+						/* InternalConfig bits */
+	xcvr10BaseT		= 0x00000000,
+	xcvr10Base2		= 0x00300000,
+};
+
+enum {						/* Window 4 - diagnostic */
+	Wdiagnostic		= 0x0004,
+						/* registers */
+	MediaStatus		= 0x000A,
+						/* MediaStatus bits */
+	linkBeatDetect		= 0x0800,
+};
+
+extern int elnk3reset(Ether*);
+
+static char *tcmpcmcia[] = {
+	"3C589",			/* 3COM 589[ABCD] */
+	"3C562",			/* 3COM 562 */
+	"589E",				/* 3COM Megahertz 589E */
+	nil,
+};
+
+static int
+configASIC(Ether* ether, int port, int xcvr)
+{
+	int x;
+
+	/* set Window 0 configuration registers */
+	COMMAND(port, SelectRegisterWindow, Wsetup);
+	outs(port+ConfigControl, Ena);
+
+	/* IRQ must be 3 on 3C589/3C562 */
+	outs(port + ResourceConfig, 0x3F00);
+
+	x = ins(port+AddressConfig) & ~xcvrMask9;
+	x |= (xcvr>>20)<<14;
+	outs(port+AddressConfig, x);
+
+	COMMAND(port, TxReset, 0);
+	while(STATUS(port) & commandInProgress)
+		;
+	COMMAND(port, RxReset, 0);
+	while(STATUS(port) & commandInProgress)
+		;
+
+	return elnk3reset(ether);
+}
+
+int
+ether589reset(Ether* ether)
+{
+	int i, t, slot;
+	char *type;
+	int port;
+	enum { WantAny, Want10BT, Want10B2 };
+	int want;
+	uchar ea[6];
+	char *p;
+
+	if(ether->irq == 0)
+		ether->irq = 10;
+	if(ether->port == 0)
+		ether->port = 0x240;
+	port = ether->port;
+
+//	if(ioalloc(port, 0x10, 0, "3C589") < 0)
+//		return -1;
+
+	type = nil;
+	slot = -1;
+	for(i = 0; tcmpcmcia[i] != nil; i++){
+		type = tcmpcmcia[i];
+if(debug) print("try %s...", type);
+		if((slot = pcmspecial(type, ether)) >= 0)
+			break;
+	}
+	if(slot < 0){
+if(debug) print("none found\n");
+//		iofree(port);
+		return -1;
+	}
+
+	/*
+	 * Read Ethernet address from card memory
+	 * on 3C562, but only if the user has not 
+	 * overridden it.
+	 */
+	memset(ea, 0, sizeof ea);
+	if(memcmp(ea, ether->ea, 6) == 0 && strcmp(type, "3C562") == 0) {
+		if(debug)
+			print("read 562...");
+		if(pcmcistuple(slot, 0x88, -1, ea, 6) == 6) {
+			for(i = 0; i < 6; i += 2){
+				t = ea[i];
+				ea[i] = ea[i+1];
+				ea[i+1] = t;
+			}
+			memmove(ether->ea, ea, 6);
+			if(debug)
+				print("ea %E", ea);
+		}
+	}
+	/*
+	 * Allow user to specify desired media in plan9.ini
+	 */
+	want = WantAny;
+	for(i = 0; i < ether->nopt; i++){
+		if(cistrncmp(ether->opt[i], "media=", 6) != 0)
+			continue;
+		p = ether->opt[i]+6;
+		if(cistrcmp(p, "10base2") == 0)
+			want = Want10B2;
+		else if(cistrcmp(p, "10baseT") == 0)
+			want = Want10BT;
+	}
+	
+	/* try configuring as a 10BaseT */
+	if(want==WantAny || want==Want10BT){
+		if(configASIC(ether, port, xcvr10BaseT) < 0){
+			pcmspecialclose(slot);
+//			iofree(port);
+			return -1;
+		}
+		delay(100);
+		COMMAND(port, SelectRegisterWindow, Wdiagnostic);
+		if((ins(port+MediaStatus)&linkBeatDetect) || want==Want10BT){
+			COMMAND(port, SelectRegisterWindow, Wop);
+			print("#l%d: xcvr10BaseT %s\n", ether->ctlrno, type);
+			return 0;
+		}
+	}
+
+	/* try configuring as a 10base2 */
+	if(want==WantAny || want==Want10B2){
+		COMMAND(port, GlobalReset, 0);
+		if(configASIC(ether, port, xcvr10Base2) < 0){
+			pcmspecialclose(slot);
+//			iofree(port);
+			return -1;
+		}
+		print("#l%d: xcvr10Base2 %s\n", ether->ctlrno, type);
+		return 0;
+	}
+	return -1;		/* not reached */
+}
--- /dev/null
+++ b/os/boot/pc/ether79c970.c
@@ -1,0 +1,539 @@
+/*
+ * AMD79C970
+ * PCnet-PCI Single-Chip Ethernet Controller for PCI Local Bus
+ * To do:
+ *	finish this rewrite
+ */
+#include "u.h"
+#include "lib.h"
+#include "mem.h"
+#include "dat.h"
+#include "fns.h"
+#include "io.h"
+
+#include "etherif.h"
+
+enum {
+	Lognrdre	= 6,
+	Nrdre		= (1<<Lognrdre),/* receive descriptor ring entries */
+	Logntdre	= 4,
+	Ntdre		= (1<<Logntdre),/* transmit descriptor ring entries */
+
+	Rbsize		= ETHERMAXTU+4,	/* ring buffer size (+4 for CRC) */
+};
+
+enum {					/* DWIO I/O resource map */
+	Aprom		= 0x0000,	/* physical address */
+	Rdp		= 0x0010,	/* register data port */
+	Rap		= 0x0014,	/* register address port */
+	Sreset		= 0x0018,	/* software reset */
+	Bdp		= 0x001C,	/* bus configuration register data port */
+};
+
+enum {					/* CSR0 */
+	Init		= 0x0001,	/* begin initialisation */
+	Strt		= 0x0002,	/* enable chip */
+	Stop		= 0x0004,	/* disable chip */
+	Tdmd		= 0x0008,	/* transmit demand */
+	Txon		= 0x0010,	/* transmitter on */
+	Rxon		= 0x0020,	/* receiver on */
+	Iena		= 0x0040,	/* interrupt enable */
+	Intr		= 0x0080,	/* interrupt flag */
+	Idon		= 0x0100,	/* initialisation done */
+	Tint		= 0x0200,	/* transmit interrupt */
+	Rint		= 0x0400,	/* receive interrupt */
+	Merr		= 0x0800,	/* memory error */
+	Miss		= 0x1000,	/* missed frame */
+	Cerr		= 0x2000,	/* collision */
+	Babl		= 0x4000,	/* transmitter timeout */
+	Err		= 0x8000,	/* Babl|Cerr|Miss|Merr */
+};
+	
+enum {					/* CSR3 */
+	Bswp		= 0x0004,	/* byte swap */
+	Emba		= 0x0008,	/* enable modified back-off algorithm */
+	Dxmt2pd		= 0x0010,	/* disable transmit two part deferral */
+	Lappen		= 0x0020,	/* look-ahead packet processing enable */
+};
+
+enum {					/* CSR4 */
+	ApadXmt		= 0x0800,	/* auto pad transmit */
+};
+
+enum {					/* CSR15 */
+	Prom		= 0x8000,	/* promiscuous mode */
+};
+
+typedef struct {			/* Initialisation Block */
+	ushort	mode;
+	uchar	rlen;			/* upper 4 bits */
+	uchar	tlen;			/* upper 4 bits */
+	uchar	padr[6];
+	uchar	res[2];
+	uchar	ladr[8];
+	ulong	rdra;
+	ulong	tdra;
+} Iblock;
+
+typedef struct {			/* descriptor ring entry */
+	ulong	addr;
+	ulong	md1;			/* status|bcnt */
+	ulong	md2;			/* rcc|rpc|mcnt */
+	void*	data;
+} Dre;
+
+enum {					/* md1 */
+	Enp		= 0x01000000,	/* end of packet */
+	Stp		= 0x02000000,	/* start of packet */
+	RxBuff		= 0x04000000,	/* buffer error */
+	Def		= 0x04000000,	/* deferred */
+	Crc		= 0x08000000,	/* CRC error */
+	One		= 0x08000000,	/* one retry needed */
+	Oflo		= 0x10000000,	/* overflow error */
+	More		= 0x10000000,	/* more than one retry needed */
+	Fram		= 0x20000000,	/* framing error */
+	RxErr		= 0x40000000,	/* Fram|Oflo|Crc|RxBuff */
+	TxErr		= 0x40000000,	/* Uflo|Lcol|Lcar|Rtry */
+	Own		= 0x80000000,
+};
+
+enum {					/* md2 */
+	Rtry		= 0x04000000,	/* failed after repeated retries */
+	Lcar		= 0x08000000,	/* loss of carrier */
+	Lcol		= 0x10000000,	/* late collision */
+	Uflo		= 0x40000000,	/* underflow error */
+	TxBuff		= 0x80000000,	/* buffer error */
+};
+
+typedef struct Ctlr Ctlr;
+struct Ctlr {
+	Lock;
+	int	port;
+	Pcidev*	pcidev;
+	Ctlr*	next;
+	int	active;
+
+	int	init;			/* initialisation in progress */
+	Iblock	iblock;
+
+	Dre*	rdr;			/* receive descriptor ring */
+	int	rdrx;
+
+	Dre*	tdr;			/* transmit descriptor ring */
+	int	tdrh;			/* host index into tdr */
+	int	tdri;			/* interface index into tdr */
+	int	ntq;			/* descriptors active */
+
+	ulong	rxbuff;			/* receive statistics */
+	ulong	crc;
+	ulong	oflo;
+	ulong	fram;
+
+	ulong	rtry;			/* transmit statistics */
+	ulong	lcar;
+	ulong	lcol;
+	ulong	uflo;
+	ulong	txbuff;
+
+	ulong	merr;			/* bobf is such a whiner */
+	ulong	miss;
+	ulong	babl;
+
+	int		(*ior)(Ctlr*, int);
+	void		(*iow)(Ctlr*, int, int);
+};
+
+static Ctlr* ctlrhead;
+static Ctlr* ctlrtail;
+
+/*
+ * The Rdp, Rap, Sreset, Bdp ports are 32-bit port offset in the enumeration above.
+ * To get to 16-bit offsets, scale down with 0x10 staying the same.
+ */
+static int
+io16r(Ctlr* c, int r)
+{
+	if(r >= Rdp)
+		r = (r-Rdp)/2+Rdp;
+	return ins(c->port+r);
+}
+
+static void
+io16w(Ctlr* c, int r, int v)
+{
+	if(r >= Rdp)
+		r = (r-Rdp)/2+Rdp;
+	outs(c->port+r, v);
+}
+
+static int
+io32r(Ctlr* c, int r)
+{
+	return inl(c->port+r);
+}
+
+static void
+io32w(Ctlr* c, int r, int v)
+{
+	outl(c->port+r, v);
+}
+
+static void
+attach(Ether*)
+{
+}
+
+static void
+detach(Ether* ether)
+{
+	Ctlr *ctlr;
+
+	ctlr = ether->ctlr;
+	ctlr->iow(ctlr, Rdp, Iena|Stop);
+}
+
+static void
+ringinit(Ctlr* ctlr)
+{
+	Dre *dre;
+
+	/*
+	 * Initialise the receive and transmit buffer rings.
+	 * The ring entries must be aligned on 16-byte boundaries.
+	 *
+	 * This routine is protected by ctlr->init.
+	 */
+	if(ctlr->rdr == 0){
+		ctlr->rdr = ialloc(Nrdre*sizeof(Dre), 0x10);
+		for(dre = ctlr->rdr; dre < &ctlr->rdr[Nrdre]; dre++){
+			dre->data = malloc(Rbsize);
+			dre->addr = PADDR(dre->data);
+			dre->md2 = 0;
+			dre->md1 = Own|(-Rbsize & 0xFFFF);
+		}
+	}
+	ctlr->rdrx = 0;
+
+	if(ctlr->tdr == 0)
+		ctlr->tdr = ialloc(Ntdre*sizeof(Dre), 0x10);
+	memset(ctlr->tdr, 0, Ntdre*sizeof(Dre));
+	ctlr->tdrh = ctlr->tdri = 0;
+}
+
+static void
+transmit(Ether* ether)
+{
+	Ctlr *ctlr;
+	Block *bp;
+	Dre *dre;
+	RingBuf *tb;
+
+	ctlr = ether->ctlr;
+
+	if(ctlr->init)
+		return;
+
+	while(ctlr->ntq < (Ntdre-1)){
+		tb = &ether->tb[ether->ti];
+		if(tb->owner != Interface)
+			break;
+
+		bp = allocb(tb->len);
+		memmove(bp->wp, tb->pkt, tb->len);
+		memmove(bp->wp+Eaddrlen, ether->ea, Eaddrlen);
+		bp->wp += tb->len;
+
+		/*
+		 * Give ownership of the descriptor to the chip,
+		 * increment the software ring descriptor pointer
+		 * and tell the chip to poll.
+		 * There's no need to pad to ETHERMINTU
+		 * here as ApadXmt is set in CSR4.
+		 */
+		dre = &ctlr->tdr[ctlr->tdrh];
+		dre->data = bp;
+		dre->addr = PADDR(bp->rp);
+		dre->md2 = 0;
+		dre->md1 = Own|Stp|Enp|Oflo|(-BLEN(bp) & 0xFFFF);
+		ctlr->ntq++;
+		ctlr->iow(ctlr, Rap, 0);
+		ctlr->iow(ctlr, Rdp, Iena|Tdmd);
+		ctlr->tdrh = NEXT(ctlr->tdrh, Ntdre);
+
+		tb->owner = Host;
+		ether->ti = NEXT(ether->ti, ether->ntb);
+	}
+}
+
+static void
+interrupt(Ureg*, void* arg)
+{
+	Ctlr *ctlr;
+	Ether *ether;
+	int csr0;
+	Dre *dre;
+	RingBuf *rb;
+
+	ether = arg;
+	ctlr = ether->ctlr;
+
+	/*
+	 * Acknowledge all interrupts and whine about those that shouldn't
+	 * happen.
+	 */
+intrloop:
+	csr0 = ctlr->ior(ctlr, Rdp) & 0xFFFF;
+	ctlr->iow(ctlr, Rdp, Babl|Cerr|Miss|Merr|Rint|Tint|Iena);
+	if(csr0 & Merr)
+		ctlr->merr++;
+	if(csr0 & Miss)
+		ctlr->miss++;
+	if(csr0 & Babl)
+		ctlr->babl++;
+	//if(csr0 & (Babl|Miss|Merr))
+	//	print("#l%d: csr0 = 0x%uX\n", ether->ctlrno, csr0);
+	if(!(csr0 & (Rint|Tint)))
+		return;
+
+	/*
+	 * Receiver interrupt: run round the descriptor ring logging
+	 * errors and passing valid receive data up to the higher levels
+	 * until a descriptor is encountered still owned by the chip.
+	 */
+	if(csr0 & Rint){
+		dre = &ctlr->rdr[ctlr->rdrx];
+		while(!(dre->md1 & Own)){
+			rb = &ether->rb[ether->ri];
+			if(dre->md1 & RxErr){
+				if(dre->md1 & RxBuff)
+					ctlr->rxbuff++;
+				if(dre->md1 & Crc)
+					ctlr->crc++;
+				if(dre->md1 & Oflo)
+					ctlr->oflo++;
+				if(dre->md1 & Fram)
+					ctlr->fram++;
+			}
+			else if(rb->owner == Interface){
+				rb->owner = Host;
+				rb->len = (dre->md2 & 0x0FFF)-4;
+				memmove(rb->pkt, dre->data, rb->len);
+				ether->ri = NEXT(ether->ri, ether->nrb);
+			}
+
+			/*
+			 * Finished with this descriptor, reinitialise it,
+			 * give it back to the chip, then on to the next...
+			 */
+			dre->md2 = 0;
+			dre->md1 = Own|(-Rbsize & 0xFFFF);
+
+			ctlr->rdrx = NEXT(ctlr->rdrx, Nrdre);
+			dre = &ctlr->rdr[ctlr->rdrx];
+		}
+	}
+
+	/*
+	 * Transmitter interrupt: wakeup anyone waiting for a free descriptor.
+	 */
+	if(csr0 & Tint){
+		lock(ctlr);
+		while(ctlr->ntq){
+			dre = &ctlr->tdr[ctlr->tdri];
+			if(dre->md1 & Own)
+				break;
+	
+			if(dre->md1 & TxErr){
+				if(dre->md2 & Rtry)
+					ctlr->rtry++;
+				if(dre->md2 & Lcar)
+					ctlr->lcar++;
+				if(dre->md2 & Lcol)
+					ctlr->lcol++;
+				if(dre->md2 & Uflo)
+					ctlr->uflo++;
+				if(dre->md2 & TxBuff)
+					ctlr->txbuff++;
+			}
+	
+			freeb(dre->data);
+	
+			ctlr->ntq--;
+			ctlr->tdri = NEXT(ctlr->tdri, Ntdre);
+		}
+		transmit(ether);
+		unlock(ctlr);
+	}
+	goto intrloop;
+}
+
+static void
+amd79c970pci(void)
+{
+	Ctlr *ctlr;
+	Pcidev *p;
+
+	p = nil;
+	while(p = pcimatch(p, 0x1022, 0x2000)){
+		ctlr = malloc(sizeof(Ctlr));
+		ctlr->port = p->mem[0].bar & ~0x01;
+		ctlr->pcidev = p;
+
+		if(ctlrhead != nil)
+			ctlrtail->next = ctlr;
+		else
+			ctlrhead = ctlr;
+		ctlrtail = ctlr;
+	}
+}
+
+int
+amd79c970reset(Ether* ether)
+{
+	int x;
+	uchar ea[Eaddrlen];
+	Ctlr *ctlr;
+
+	if(ctlrhead == nil)
+		amd79c970pci();
+
+	/*
+	 * Any adapter matches if no port is supplied,
+	 * otherwise the ports must match.
+	 */
+	for(ctlr = ctlrhead; ctlr != nil; ctlr = ctlr->next){
+		if(ctlr->active)
+			continue;
+		if(ether->port == 0 || ether->port == ctlr->port){
+			ctlr->active = 1;
+			break;
+		}
+	}
+	if(ctlr == nil)
+		return -1;
+
+	/*
+	 * Allocate a controller structure and start to initialise it.
+	 */
+	ether->ctlr = ctlr;
+	ether->port = ctlr->port;
+	ether->irq = ctlr->pcidev->intl;
+	ether->tbdf = ctlr->pcidev->tbdf;
+	pcisetbme(ctlr->pcidev);
+	ilock(ctlr);
+	ctlr->init = 1;
+
+	io32r(ctlr, Sreset);
+	io16r(ctlr, Sreset);
+
+	if(io16w(ctlr, Rap, 0), io16r(ctlr, Rdp) == 4){
+		ctlr->ior = io16r;
+		ctlr->iow = io16w;
+	}else if(io32w(ctlr, Rap, 0), io32r(ctlr, Rdp) == 4){
+		ctlr->ior = io32r;
+		ctlr->iow = io32w;
+	}else{
+		print("#l%d: card doesn't talk right\n", ether->ctlrno);
+		iunlock(ctlr);
+		return -1;
+	}
+
+	ctlr->iow(ctlr, Rap, 88);
+	x = ctlr->ior(ctlr, Rdp);
+	ctlr->iow(ctlr, Rap, 89);
+	x |= ctlr->ior(ctlr, Rdp)<<16;
+
+	switch(x&0xFFFFFFF){
+	case 0x2420003:	/* PCnet/PCI 79C970 */
+	case 0x2621003:	/* PCnet/PCI II 79C970A */
+		break;
+	default:
+		print("unknown PCnet card version %.7ux\n", x&0xFFFFFFF);
+		iunlock(ctlr);
+		return -1;
+	}
+
+	/*
+	 * Set the software style in BCR20 to be PCnet-PCI to ensure 32-bit access.
+	 * Set the auto pad transmit in CSR4.
+	 */
+	ctlr->iow(ctlr, Rap, 20);
+	ctlr->iow(ctlr, Bdp, 0x0002);
+
+	ctlr->iow(ctlr, Rap, 4);
+	x = ctlr->ior(ctlr, Rdp) & 0xFFFF;
+	ctlr->iow(ctlr, Rdp, ApadXmt|x);
+
+	ctlr->iow(ctlr, Rap, 0);
+
+	/*
+	 * Check if the adapter's station address is to be overridden.
+	 * If not, read it from the I/O-space and set in ether->ea prior to
+	 * loading the station address in the initialisation block.
+	 */
+	memset(ea, 0, Eaddrlen);
+	if(!memcmp(ea, ether->ea, Eaddrlen)){
+		x = ctlr->ior(ctlr, Aprom);
+		ether->ea[0] = x;
+		ether->ea[1] = x>>8;
+		if(ctlr->ior == io16r)
+			x = ctlr->ior(ctlr, Aprom+2);
+		else
+			x >>= 16;
+		ether->ea[2] = x;
+		ether->ea[3] = x>>8;
+		x = ctlr->ior(ctlr, Aprom+4);
+		ether->ea[4] = x;
+		ether->ea[5] = x>>8;
+	}
+
+	/*
+	 * Start to fill in the initialisation block
+	 * (must be DWORD aligned).
+	 */
+	ctlr->iblock.rlen = Lognrdre<<4;
+	ctlr->iblock.tlen = Logntdre<<4;
+	memmove(ctlr->iblock.padr, ether->ea, sizeof(ctlr->iblock.padr));
+
+	ringinit(ctlr);
+	ctlr->iblock.rdra = PADDR(ctlr->rdr);
+	ctlr->iblock.tdra = PADDR(ctlr->tdr);
+
+	/*
+	 * Point the chip at the initialisation block and tell it to go.
+	 * Mask the Idon interrupt and poll for completion. Strt and interrupt
+	 * enables will be set later when attaching to the network.
+	 */
+	x = PADDR(&ctlr->iblock);
+	ctlr->iow(ctlr, Rap, 1);
+	ctlr->iow(ctlr, Rdp, x & 0xFFFF);
+	ctlr->iow(ctlr, Rap, 2);
+	ctlr->iow(ctlr, Rdp, (x>>16) & 0xFFFF);
+	ctlr->iow(ctlr, Rap, 3);
+	ctlr->iow(ctlr, Rdp, Idon);
+	ctlr->iow(ctlr, Rap, 0);
+	ctlr->iow(ctlr, Rdp, Init);
+
+	while(!(ctlr->ior(ctlr, Rdp) & Idon))
+		;
+
+	/*
+	 * We used to set CSR0 to Idon|Stop here, and then
+	 * in attach change it to Iena|Strt.  Apparently the simulated
+	 * 79C970 in VMware never enables after a write of Idon|Stop,
+	 * so we enable the device here now.
+	 */
+	ctlr->iow(ctlr, Rdp, Iena|Strt);
+	ctlr->init = 0;
+	iunlock(ctlr);
+
+	/*
+	 * Linkage to the generic ethernet driver.
+	 */
+	ether->attach = attach;
+	ether->transmit = transmit;
+	ether->interrupt = interrupt;
+	ether->detach = detach;
+
+	return 0;
+}
--- /dev/null
+++ b/os/boot/pc/ether8003.c
@@ -1,0 +1,258 @@
+#include "u.h"
+#include "lib.h"
+#include "mem.h"
+#include "dat.h"
+#include "fns.h"
+#include "io.h"
+
+#include "etherif.h"
+#include "ether8390.h"
+
+/*
+ * Western Digital/Standard Microsystems Corporation cards (WD80[01]3).
+ * Also handles 8216 cards (Elite Ultra).
+ * Configuration code based on that provided by SMC a long time ago.
+ */
+enum {					/* 83C584 Bus Interface Controller */
+	Msr		= 0x00,		/* Memory Select Register */
+	Icr		= 0x01,		/* Interface Configuration Register */
+	Iar		= 0x02,		/* I/O Address Register */
+	Bio		= 0x03,		/* BIOS ROM Address Register */
+	Ear		= 0x03,		/* EEROM Address Register (shared with Bio) */
+	Irr		= 0x04,		/* Interrupt Request Register */
+	Hcr		= 0x04,		/* 8216 hardware control */
+	Laar		= 0x05,		/* LA Address Register */
+	Ijr		= 0x06,		/* Initialisation Jumpers */
+	Gp2		= 0x07,		/* General Purpose Data Register */
+	Lar		= 0x08,		/* LAN Address Registers */
+	Id		= 0x0E,		/* Card ID byte */
+	Cksum		= 0x0F,		/* Checksum */
+};
+
+enum {					/* Msr */
+	Rst		= 0x80,		/* software reset */
+	Menb		= 0x40,		/* memory enable */
+};
+
+enum {					/* Icr */
+	Bit16		= 0x01,		/* 16-bit bus */
+	Other		= 0x02,		/* other register access */
+	Ir2		= 0x04,		/* IR2 */
+	Msz		= 0x08,		/* SRAM size */
+	Rla		= 0x10,		/* recall LAN address */
+	Rx7		= 0x20,		/* recall all but I/O and LAN address */
+	Rio		= 0x40,		/* recall I/O address from EEROM */
+	Sto		= 0x80,		/* non-volatile EEROM store */
+};
+
+enum {					/* Laar */
+	ZeroWS16	= 0x20,		/* zero wait states for 16-bit ops */
+	L16en		= 0x40,		/* enable 16-bit LAN operation */
+	M16en		= 0x80,		/* enable 16-bit memory access */
+};
+
+enum {					/* Ijr */
+	Ienable		= 0x01,		/* 8216 interrupt enable */
+};
+
+/*
+ * Mapping from configuration bits to interrupt level.
+ */
+static int irq8003[8] = {
+	9, 3, 5, 7, 10, 11, 15, 4,
+};
+
+static int irq8216[8] = {
+	0, 9, 3, 5, 7, 10, 11, 15,
+};
+
+static void
+reset8003(Ether* ether, uchar ea[Eaddrlen], uchar ic[8])
+{
+	Dp8390 *ctlr;
+	ulong port;
+
+	ctlr = ether->ctlr;
+	port = ether->port;
+
+	/*
+	 * Check for old, dumb 8003E, which doesn't have an interface
+	 * chip. Only Msr exists out of the 1st eight registers, reads
+	 * of the others just alias the 2nd eight registers, the LAN
+	 * address ROM. Can check Icr, Irr and Laar against the ethernet
+	 * address read above and if they match it's an 8003E (or an
+	 * 8003EBT, 8003S, 8003SH or 8003WT, doesn't matter), in which
+	 * case the default irq gets used.
+	 */
+	if(memcmp(&ea[1], &ic[1], 5) == 0){
+		memset(ic, 0, sizeof(ic));
+		ic[Msr] = (((ulong)ether->mem)>>13) & 0x3F;
+	}
+	else{
+		/*
+		 * As a final sanity check for the 8013EBT, which doesn't have
+		 * the 83C584 interface chip, but has 2 real registers, write Gp2
+		 * and if it reads back the same, it's not an 8013EBT.
+		 */
+		outb(port+Gp2, 0xAA);
+		inb(port+Msr);				/* wiggle bus */
+		if(inb(port+Gp2) != 0xAA){
+			memset(ic, 0, sizeof(ic));
+			ic[Msr] = (((ulong)ether->mem)>>13) & 0x3F;
+		}
+		else
+			ether->irq = irq8003[((ic[Irr]>>5) & 0x3)|(ic[Icr] & 0x4)];
+
+		/*
+		 * Check if 16-bit card.
+		 * If Bit16 is read/write, then it's an 8-bit card.
+		 * If Bit16 is set, it's in a 16-bit slot.
+		 */
+		outb(port+Icr, ic[Icr]^Bit16);
+		inb(port+Msr);				/* wiggle bus */
+		if((inb(port+Icr) & Bit16) == (ic[Icr] & Bit16)){
+			ctlr->width = 2;
+			ic[Icr] &= ~Bit16;
+		}
+		outb(port+Icr, ic[Icr]);
+
+		if(ctlr->width == 2 && (inb(port+Icr) & Bit16) == 0)
+			ctlr->width = 1;
+	}
+
+	ether->mem = (ulong)KADDR((ic[Msr] & 0x3F)<<13);
+	if(ctlr->width == 2)
+		ether->mem |= (ic[Laar] & 0x1F)<<19;
+	else
+		ether->mem |= 0x80000;
+
+	if(ic[Icr] & (1<<3))
+		ether->size = 32*1024;
+	if(ctlr->width == 2)
+		ether->size <<= 1;
+
+	/*
+	 * Enable interface RAM, set interface width.
+	 */
+	outb(port+Msr, ic[Msr]|Menb);
+	if(ctlr->width == 2)
+		outb(port+Laar, ic[Laar]|L16en|M16en|ZeroWS16);
+}
+
+static void
+reset8216(Ether* ether, uchar[8])
+{
+	uchar hcr, irq, x;
+	ulong addr, port;
+	Dp8390 *ctlr;
+
+	ctlr = ether->ctlr;
+	port = ether->port;
+
+	ctlr->width = 2;
+
+	/*
+	 * Switch to the alternate register set and retrieve the memory
+	 * and irq information.
+	 */
+	hcr = inb(port+Hcr);
+	outb(port+Hcr, 0x80|hcr);
+	addr = inb(port+0x0B) & 0xFF;
+	irq = inb(port+0x0D);
+	outb(port+Hcr, hcr);
+
+	ether->mem = (ulong)KADDR(0xC0000+((((addr>>2) & 0x30)|(addr & 0x0F))<<13));
+	ether->size = 8192*(1<<((addr>>4) & 0x03));
+	ether->irq = irq8216[((irq>>4) & 0x04)|((irq>>2) & 0x03)];
+
+	/*
+	 * Enable interface RAM, set interface width, and enable interrupts.
+	 */
+	x = inb(port+Msr) & ~Rst;
+	outb(port+Msr, Menb|x);
+	x = inb(port+Laar);
+	outb(port+Laar, M16en|x);
+	outb(port+Ijr, Ienable);
+}
+
+/*
+ * Get configuration parameters, enable memory.
+ * There are opportunities here for buckets of code, try to resist.
+ */
+int
+wd8003reset(Ether* ether)
+{
+	int i;
+	uchar ea[Eaddrlen], ic[8], id, nullea[Eaddrlen], sum;
+	ulong port;
+	Dp8390 *ctlr;
+
+	/*
+	 * Set up the software configuration.
+	 * Use defaults for port, irq, mem and size if not specified.
+	 * Defaults are set for the dumb 8003E which can't be
+	 * autoconfigured.
+	 */
+	if(ether->port == 0)
+		ether->port = 0x280;
+	if(ether->irq == 0)
+		ether->irq = 3;
+	if(ether->mem == 0)
+		ether->mem = 0xD0000;
+	if(ether->size == 0)
+		ether->size = 8*1024;
+
+	/*
+	 * Look for the interface. Read the LAN address ROM
+	 * and validate the checksum - the sum of all 8 bytes
+	 * should be 0xFF.
+	 * At the same time, get the (possible) interface chip
+	 * registers, they'll be used later to check for aliasing.
+	 */
+	port = ether->port;
+	sum = 0;
+	for(i = 0; i < sizeof(ea); i++){
+		ea[i] = inb(port+Lar+i);
+		sum += ea[i];
+		ic[i] = inb(port+i);
+	}
+	id = inb(port+Id);
+	sum += id;
+	sum += inb(port+Cksum);
+	if(sum != 0xFF)
+		return -1;
+
+	ether->ctlr = malloc(sizeof(Dp8390));
+	ctlr = ether->ctlr;
+	ctlr->ram = 1;
+
+	if((id & 0xFE) == 0x2A)
+		reset8216(ether, ic);
+	else
+		reset8003(ether, ea, ic);
+
+	/*
+	 * Set the DP8390 ring addresses.
+	 */
+	ctlr->port = port+0x10;
+	ctlr->tstart = 0;
+	ctlr->pstart = HOWMANY(sizeof(Etherpkt), Dp8390BufSz);
+	ctlr->pstop = HOWMANY(ether->size, Dp8390BufSz);
+
+	/*
+	 * Finally, init the 8390, set the ethernet address
+	 * and claim the memory used.
+	 */
+	dp8390reset(ether);
+	memset(nullea, 0, Eaddrlen);
+	if(memcmp(nullea, ether->ea, Eaddrlen) == 0){
+		for(i = 0; i < sizeof(ether->ea); i++)
+			ether->ea[i] = ea[i];
+	}
+	dp8390setea(ether);
+
+	if(umbrwmalloc(PADDR(ether->mem), ether->size, 0) == 0)
+		print("ether8003: warning - 0x%luX unavailable", PADDR(ether->mem));
+
+	return 0;
+}
--- /dev/null
+++ b/os/boot/pc/ether8139.c
@@ -1,0 +1,614 @@
+/*
+ * Realtek 8139 (but not the 8129).
+ * Error recovery for the various over/under -flow conditions
+ * may need work.
+ */
+#include "u.h"
+#include "lib.h"
+#include "mem.h"
+#include "dat.h"
+#include "fns.h"
+#include "io.h"
+
+#include "etherif.h"
+
+enum {					/* registers */
+	Idr0		= 0x0000,	/* MAC address */
+	Mar0		= 0x0008,	/* Multicast address */
+	Tsd0		= 0x0010,	/* Transmit Status Descriptor0 */
+	Tsad0		= 0x0020,	/* Transmit Start Address Descriptor0 */
+	Rbstart		= 0x0030,	/* Receive Buffer Start Address */
+	Erbcr		= 0x0034,	/* Early Receive Byte Count */
+	Ersr		= 0x0036,	/* Early Receive Status */
+	Cr		= 0x0037,	/* Command Register */
+	Capr		= 0x0038,	/* Current Address of Packet Read */
+	Cbr		= 0x003A,	/* Current Buffer Address */
+	Imr		= 0x003C,	/* Interrupt Mask */
+	Isr		= 0x003E,	/* Interrupt Status */
+	Tcr		= 0x0040,	/* Transmit Configuration */
+	Rcr		= 0x0044,	/* Receive Configuration */
+	Tctr		= 0x0048,	/* Timer Count */
+	Mpc		= 0x004C,	/* Missed Packet Counter */
+	Cr9346		= 0x0050,	/* 9346 Command Register */
+	Config0		= 0x0051,	/* Configuration Register 0 */
+	Config1		= 0x0052,	/* Configuration Register 1 */
+	TimerInt	= 0x0054,	/* Timer Interrupt */
+	Msr		= 0x0058,	/* Media Status */
+	Config3		= 0x0059,	/* Configuration Register 3 */
+	Config4		= 0x005A,	/* Configuration Register 4 */
+	Mulint		= 0x005C,	/* Multiple Interrupt Select */
+	RerID		= 0x005E,	/* PCI Revision ID */
+	Tsad		= 0x0060,	/* Transmit Status of all Descriptors */
+
+	Bmcr		= 0x0062,	/* Basic Mode Control */
+	Bmsr		= 0x0064,	/* Basic Mode Status */
+	Anar		= 0x0066,	/* Auto-Negotiation Advertisment */
+	Anlpar		= 0x0068,	/* Auto-Negotiation Link Partner */
+	Aner		= 0x006A,	/* Auto-Negotiation Expansion */
+	Dis		= 0x006C,	/* Disconnect Counter */
+	Fcsc		= 0x006E,	/* False Carrier Sense Counter */
+	Nwaytr		= 0x0070,	/* N-way Test */
+	Rec		= 0x0072,	/* RX_ER Counter */
+	Cscr		= 0x0074,	/* CS Configuration */
+	Phy1parm	= 0x0078,	/* PHY Parameter 1 */
+	Twparm		= 0x007C,	/* Twister Parameter */
+	Phy2parm	= 0x0080,	/* PHY Parameter 2 */
+};
+
+enum {					/* Cr */
+	Bufe		= 0x01,		/* Rx Buffer Empty */
+	Te		= 0x04,		/* Transmitter Enable */
+	Re		= 0x08,		/* Receiver Enable */
+	Rst		= 0x10,		/* Software Reset */
+};
+
+enum {					/* Imr/Isr */
+	Rok		= 0x0001,	/* Receive OK */
+	Rer		= 0x0002,	/* Receive Error */
+	Tok		= 0x0004,	/* Transmit OK */
+	Ter		= 0x0008,	/* Transmit Error */
+	Rxovw		= 0x0010,	/* Receive Buffer Overflow */
+	PunLc		= 0x0020,	/* Packet Underrun or Link Change */
+	Fovw		= 0x0040,	/* Receive FIFO Overflow */
+	Clc		= 0x2000,	/* Cable Length Change */
+	Timer		= 0x4000,	/* Timer */
+	Serr		= 0x8000,	/* System Error */
+};
+
+enum {					/* Tcr */
+	Clrabt		= 0x00000001,	/* Clear Abort */
+	TxrrSHIFT	= 4,		/* Transmit Retry Count */
+	TxrrMASK	= 0x000000F0,
+	MtxdmaSHIFT	= 8,		/* Max. DMA Burst Size */
+	MtxdmaMASK	= 0x00000700,
+	Mtxdma2048	= 0x00000700,
+	Acrc		= 0x00010000,	/* Append CRC (not) */
+	LbkSHIFT	= 17,		/* Loopback Test */
+	LbkMASK		= 0x00060000,
+	IfgSHIFT	= 24,		/* Interframe Gap */
+	IfgMASK		= 0x03000000,
+	HwveridSHIFT	= 22,		/* Hardware Version ID */
+	HwveridMASK	= 0x7CC00000,
+};
+
+enum {					/* Rcr */
+	Aap		= 0x00000001,	/* Accept All Packets */
+	Apm		= 0x00000002,	/* Accept Physical Match */
+	Am		= 0x00000004,	/* Accept Multicast */
+	Ab		= 0x00000008,	/* Accept Broadcast */
+	Ar		= 0x00000010,	/* Accept Runt */
+	Aer		= 0x00000020,	/* Accept Error */
+	Sel9356		= 0x00000040,	/* 9356 EEPROM used */
+	Wrap		= 0x00000080,	/* Rx Buffer Wrap Control */
+	MrxdmaSHIFT	= 8,		/* Max. DMA Burst Size */
+	MrxdmaMASK	= 0x00000700,
+	Mrxdmaunlimited	= 0x00000700,
+	RblenSHIFT	= 11,		/* Receive Buffer Length */
+	RblenMASK	= 0x00001800,
+	Rblen8K		= 0x00000000,	/* 8KB+16 */
+	Rblen16K	= 0x00000800,	/* 16KB+16 */
+	Rblen32K	= 0x00001000,	/* 32KB+16 */
+	Rblen64K	= 0x00001800,	/* 64KB+16 */
+	RxfthSHIFT	= 13,		/* Receive Buffer Length */
+	RxfthMASK	= 0x0000E000,
+	Rxfth256	= 0x00008000,
+	Rxfthnone	= 0x0000E000,
+	Rer8		= 0x00010000,	/* Accept Error Packets > 8 bytes */
+	MulERINT	= 0x00020000,	/* Multiple Early Interrupt Select */
+	ErxthSHIFT	= 24,		/* Early Rx Threshold */
+	ErxthMASK	= 0x0F000000,
+	Erxthnone	= 0x00000000,
+};
+
+enum {					/* Received Packet Status */
+	Rcok		= 0x0001,	/* Receive Completed OK */
+	Fae		= 0x0002,	/* Frame Alignment Error */
+	Crc		= 0x0004,	/* CRC Error */
+	Long		= 0x0008,	/* Long Packet */
+	Runt		= 0x0010,	/* Runt Packet Received */
+	Ise		= 0x0020,	/* Invalid Symbol Error */
+	Bar		= 0x2000,	/* Broadcast Address Received */
+	Pam		= 0x4000,	/* Physical Address Matched */
+	Mar		= 0x8000,	/* Multicast Address Received */
+};
+
+enum {					/* Media Status Register */
+	Rxpf		= 0x01,		/* Pause Flag */
+	Txpf		= 0x02,		/* Pause Flag */
+	Linkb		= 0x04,		/* Inverse of Link Status */
+	Speed10		= 0x08,		/* 10Mbps */
+	Auxstatus	= 0x10,		/* Aux. Power Present Status */
+	Rxfce		= 0x40,		/* Receive Flow Control Enable */
+	Txfce		= 0x80,		/* Transmit Flow Control Enable */
+};
+
+typedef struct {			/* Soft Transmit Descriptor */
+	int	tsd;
+	int	tsad;
+	uchar*	data;
+} Td;
+
+enum {					/* Tsd0 */
+	SizeSHIFT	= 0,		/* Descriptor Size */
+	SizeMASK	= 0x00001FFF,
+	Own		= 0x00002000,
+	Tun		= 0x00004000,	/* Transmit FIFO Underrun */
+	Tcok		= 0x00008000,	/* Transmit COmpleted OK */
+	EtxthSHIFT	= 16,		/* Early Tx Threshold */
+	EtxthMASK	= 0x001F0000,
+	NccSHIFT	= 24,		/* Number of Collisions Count */
+	NccMASK		= 0x0F000000,
+	Cdh		= 0x10000000,	/* CD Heartbeat */
+	Owc		= 0x20000000,	/* Out of Window Collision */
+	Tabt		= 0x40000000,	/* Transmit Abort */
+	Crs		= 0x80000000,	/* Carrier Sense Lost */
+};
+
+enum {
+	Rblen		= Rblen64K,	/* Receive Buffer Length */
+	Ntd		= 4,		/* Number of Transmit Descriptors */
+	Tdbsz		= ROUNDUP(sizeof(Etherpkt), 4),
+};
+
+typedef struct Ctlr Ctlr;
+typedef struct Ctlr {
+	int	port;
+	Pcidev*	pcidev;
+	Ctlr*	next;
+	int	active;
+	int	id;
+
+	Lock	ilock;			/* init */
+	void*	alloc;			/* base of per-Ctlr allocated data */
+
+	int	rcr;			/* receive configuration register */
+	uchar*	rbstart;		/* receive buffer */
+	int	rblen;			/* receive buffer length */
+	int	ierrs;			/* receive errors */
+
+	Lock	tlock;			/* transmit */
+	Td	td[Ntd];
+	int	ntd;			/* descriptors active */
+	int	tdh;			/* host index into td */
+	int	tdi;			/* interface index into td */
+	int	etxth;			/* early transmit threshold */
+	int	taligned;		/* packet required no alignment */
+	int	tunaligned;		/* packet required alignment */
+
+	int	dis;			/* disconnect counter */
+	int	fcsc;			/* false carrier sense counter */
+	int	rec;			/* RX_ER counter */
+} Ctlr;
+
+static Ctlr* ctlrhead;
+static Ctlr* ctlrtail;
+
+#define csr8r(c, r)	(inb((c)->port+(r)))
+#define csr16r(c, r)	(ins((c)->port+(r)))
+#define csr32r(c, r)	(inl((c)->port+(r)))
+#define csr8w(c, r, b)	(outb((c)->port+(r), (int)(b)))
+#define csr16w(c, r, w)	(outs((c)->port+(r), (ushort)(w)))
+#define csr32w(c, r, l)	(outl((c)->port+(r), (ulong)(l)))
+
+static int
+rtl8139reset(Ctlr* ctlr)
+{
+	/*
+	 * Soft reset the controller.
+	 */
+	csr8w(ctlr, Cr, Rst);
+	while(csr8r(ctlr, Cr) & Rst)
+		;
+
+	return 0;
+}
+
+static void
+rtl8139detach(Ether* edev)
+{
+	rtl8139reset(edev->ctlr);
+}
+
+static void
+rtl8139halt(Ctlr* ctlr)
+{
+	csr8w(ctlr, Cr, 0);
+	csr16w(ctlr, Imr, 0);
+	csr16w(ctlr, Isr, ~0);
+}
+
+static void
+rtl8139init(Ether* edev)
+{
+	int i;
+	ulong r;
+	Ctlr *ctlr;
+	uchar *alloc;
+
+	ctlr = edev->ctlr;
+	ilock(&ctlr->ilock);
+
+	rtl8139halt(ctlr);
+
+	/*
+	 * MAC Address.
+	 */
+	r = (edev->ea[3]<<24)|(edev->ea[2]<<16)|(edev->ea[1]<<8)|edev->ea[0];
+	csr32w(ctlr, Idr0, r);
+	r = (edev->ea[5]<<8)|edev->ea[4];
+	csr32w(ctlr, Idr0+4, r);
+
+	/*
+	 * Receiver
+	 */
+	alloc = (uchar*)ROUNDUP((ulong)ctlr->alloc, 32);
+	ctlr->rbstart = alloc;
+	alloc += ctlr->rblen+16;
+	memset(ctlr->rbstart, 0, ctlr->rblen+16);
+	csr32w(ctlr, Rbstart, PADDR(ctlr->rbstart));
+	ctlr->rcr = Rxfth256|Rblen|Mrxdmaunlimited|Ab|Apm;
+
+	/*
+	 * Transmitter.
+	 */
+	for(i = 0; i < Ntd; i++){
+		ctlr->td[i].tsd = Tsd0+i*4;
+		ctlr->td[i].tsad = Tsad0+i*4;
+		ctlr->td[i].data = alloc;
+		alloc += Tdbsz;
+	}
+	ctlr->ntd = ctlr->tdh = ctlr->tdi = 0;
+	ctlr->etxth = 128/32;
+
+	/*
+	 * Interrupts.
+	 */
+	csr32w(ctlr, TimerInt, 0);
+	csr16w(ctlr, Imr, Serr|Timer|Fovw|PunLc|Rxovw|Ter|Tok|Rer|Rok);
+	csr32w(ctlr, Mpc, 0);
+
+	/*
+	 * Enable receiver/transmitter.
+	 * Need to enable before writing the Rcr or it won't take.
+	 */
+	csr8w(ctlr, Cr, Te|Re);
+	csr32w(ctlr, Tcr, Mtxdma2048);
+	csr32w(ctlr, Rcr, ctlr->rcr);
+
+	iunlock(&ctlr->ilock);
+}
+
+static void
+rtl8139attach(Ether* edev)
+{
+	Ctlr *ctlr;
+
+	ctlr = edev->ctlr;
+	if(ctlr->alloc == nil){
+		ctlr->rblen = 1<<((Rblen>>RblenSHIFT)+13);
+		ctlr->alloc = mallocz(ctlr->rblen+16 + Ntd*Tdbsz + 32, 0);
+		rtl8139init(edev);
+	}
+}
+
+static void
+rtl8139txstart(Ether* edev)
+{
+	Td *td;
+	Ctlr *ctlr;
+	RingBuf *tb;
+
+	ctlr = edev->ctlr;
+	while(ctlr->ntd < Ntd){
+		tb = &edev->tb[edev->ti];
+		if(tb->owner != Interface)
+			break;
+
+		td = &ctlr->td[ctlr->tdh];
+		memmove(td->data, tb->pkt, tb->len);
+		csr32w(ctlr, td->tsad, PADDR(tb->pkt));
+		csr32w(ctlr, td->tsd, (ctlr->etxth<<EtxthSHIFT)|tb->len);
+
+		ctlr->ntd++;
+		ctlr->tdh = NEXT(ctlr->tdh, Ntd);
+		tb->owner = Host;
+		edev->ti = NEXT(edev->ti, edev->ntb);
+	}
+}
+
+static void
+rtl8139transmit(Ether* edev)
+{
+	Ctlr *ctlr;
+
+	ctlr = edev->ctlr;
+	ilock(&ctlr->tlock);
+	rtl8139txstart(edev);
+	iunlock(&ctlr->tlock);
+}
+
+static void
+rtl8139receive(Ether* edev)
+{
+	Ctlr *ctlr;
+	RingBuf *rb;
+	ushort capr;
+	uchar cr, *p;
+	int l, length, status;
+
+	ctlr = edev->ctlr;
+
+	/*
+	 * Capr is where the host is reading from,
+	 * Cbr is where the NIC is currently writing.
+	 */
+	capr = (csr16r(ctlr, Capr)+16) % ctlr->rblen;
+	while(!(csr8r(ctlr, Cr) & Bufe)){
+		p = ctlr->rbstart+capr;
+
+		/*
+		 * Apparently the packet length may be 0xFFF0 if
+		 * the NIC is still copying the packet into memory.
+		 */
+		length = (*(p+3)<<8)|*(p+2);
+		if(length == 0xFFF0)
+			break;
+		status = (*(p+1)<<8)|*p;
+
+		if(!(status & Rcok)){
+			/*
+			 * Reset the receiver.
+			 * Also may have to restore the multicast list
+			 * here too if it ever gets used.
+			 */
+			cr = csr8r(ctlr, Cr);
+			csr8w(ctlr, Cr, cr & ~Re);
+			csr32w(ctlr, Rbstart, PADDR(ctlr->rbstart));
+			csr8w(ctlr, Cr, cr);
+			csr32w(ctlr, Rcr, ctlr->rcr);
+
+			continue;
+		}
+
+		/*
+		 * Receive Completed OK.
+		 * Very simplistic; there are ways this could be done
+		 * without copying, but the juice probably isn't worth
+		 * the squeeze.
+		 * The packet length includes a 4 byte CRC on the end.
+		 */
+		capr = (capr+4) % ctlr->rblen;
+		p = ctlr->rbstart+capr;
+		capr = (capr+length) % ctlr->rblen;
+
+		rb = &edev->rb[edev->ri];
+		l = 0;
+		if(p+length >= ctlr->rbstart+ctlr->rblen){
+			l = ctlr->rbstart+ctlr->rblen - p;
+			if(rb->owner == Interface)
+				memmove(rb->pkt, p, l);
+			length -= l;
+			p = ctlr->rbstart;
+		}
+		if(length > 0 && rb->owner == Interface){
+			memmove(rb->pkt+l, p, length);
+			l += length;
+		}
+		if(rb->owner == Interface){
+			rb->owner = Host;
+			rb->len = l-4;
+			edev->ri = NEXT(edev->ri, edev->nrb);
+		}
+
+		capr = ROUNDUP(capr, 4);
+		csr16w(ctlr, Capr, capr-16);
+	}
+}
+
+static void
+rtl8139interrupt(Ureg*, void* arg)
+{
+	Td *td;
+	Ctlr *ctlr;
+	Ether *edev;
+	int isr, tsd;
+
+	edev = arg;
+	ctlr = edev->ctlr;
+
+	while((isr = csr16r(ctlr, Isr)) != 0){
+		csr16w(ctlr, Isr, isr);
+		if(isr & (Fovw|PunLc|Rxovw|Rer|Rok)){
+			rtl8139receive(edev);
+			if(!(isr & Rok))
+				ctlr->ierrs++;
+			isr &= ~(Fovw|Rxovw|Rer|Rok);
+		}
+
+		if(isr & (Ter|Tok)){
+			ilock(&ctlr->tlock);
+			while(ctlr->ntd){
+				td = &ctlr->td[ctlr->tdi];
+				tsd = csr32r(ctlr, td->tsd);
+				if(!(tsd & (Tabt|Tun|Tcok)))
+					break;
+
+				if(!(tsd & Tcok)){
+					if(tsd & Tun){
+						if(ctlr->etxth < ETHERMAXTU/32)
+							ctlr->etxth++;
+					}
+				}
+
+				ctlr->ntd--;
+				ctlr->tdi = NEXT(ctlr->tdi, Ntd);
+			}
+			rtl8139txstart(edev);
+			iunlock(&ctlr->tlock);
+			isr &= ~(Ter|Tok);
+		}
+
+		if(isr & PunLc)
+			isr &= ~(Clc|PunLc);
+
+		/*
+		 * Only Serr|Timer should be left by now.
+		 * Should anything be done to tidy up? TimerInt isn't
+		 * used so that can be cleared. A PCI bus error is indicated
+		 * by Serr, that's pretty serious; is there anyhing to do
+		 * other than try to reinitialise the chip?
+		 */
+		if((isr & (Serr|Timer)) != 0){
+			print("rtl8139interrupt: imr %4.4uX isr %4.4uX\n",
+				csr16r(ctlr, Imr), isr);
+			if(isr & Timer)
+				csr32w(ctlr, TimerInt, 0);
+			if(isr & Serr)
+				rtl8139init(edev);
+		}
+	}
+}
+
+static Ctlr*
+rtl8139match(Ether* edev, int id)
+{
+	int port;
+	Pcidev *p;
+	Ctlr *ctlr;
+
+	/*
+	 * Any adapter matches if no edev->port is supplied,
+	 * otherwise the ports must match.
+	 */
+	for(ctlr = ctlrhead; ctlr != nil; ctlr = ctlr->next){
+		if(ctlr->active)
+			continue;
+		p = ctlr->pcidev;
+		if(((p->did<<16)|p->vid) != id)
+			continue;
+		port = p->mem[0].bar & ~0x01;
+		if(edev->port != 0 && edev->port != port)
+			continue;
+
+		ctlr->port = port;
+		if(rtl8139reset(ctlr))
+			continue;
+		pcisetbme(p);
+
+		ctlr->active = 1;
+		return ctlr;
+	}
+	return nil;
+}
+
+static struct {
+	char*	name;
+	int	id;
+} rtl8139pci[] = {
+	{ "rtl8139",	(0x8139<<16)|0x10EC, },	/* generic */
+	{ "smc1211",	(0x1211<<16)|0x1113, },	/* SMC EZ-Card */
+	{ "dfe-538tx",	(0x1300<<16)|0x1186, }, /* D-Link DFE-538TX */
+	{ "dfe-560txd",	(0x1340<<16)|0x1186, }, /* D-Link DFE-560TXD */
+	{ nil },
+};
+
+int
+rtl8139pnp(Ether* edev)
+{
+	int i, id;
+	Pcidev *p;
+	Ctlr *ctlr;
+	uchar ea[Eaddrlen];
+
+	/*
+	 * Make a list of all ethernet controllers
+	 * if not already done.
+	 */
+	if(ctlrhead == nil){
+		p = nil;
+		while(p = pcimatch(p, 0, 0)){
+			if(p->ccrb != 0x02 || p->ccru != 0)
+				continue;
+			ctlr = malloc(sizeof(Ctlr));
+			ctlr->pcidev = p;
+			ctlr->id = (p->did<<16)|p->vid;
+
+			if(ctlrhead != nil)
+				ctlrtail->next = ctlr;
+			else
+				ctlrhead = ctlr;
+			ctlrtail = ctlr;
+		}
+	}
+
+	/*
+	 * Is it an RTL8139 under a different name?
+	 * Normally a search is made through all the found controllers
+	 * for one which matches any of the known vid+did pairs.
+	 * If a vid+did pair is specified a search is made for that
+	 * specific controller only.
+	 */
+	id = 0;
+	for(i = 0; i < edev->nopt; i++){
+		if(cistrncmp(edev->opt[i], "id=", 3) == 0)
+			id = strtol(&edev->opt[i][3], nil, 0);
+	}
+
+	ctlr = nil;
+	if(id != 0)
+		ctlr = rtl8139match(edev, id);
+	else for(i = 0; rtl8139pci[i].name; i++){
+		if((ctlr = rtl8139match(edev, rtl8139pci[i].id)) != nil)
+			break;
+	}
+	if(ctlr == nil)
+		return -1;
+
+	edev->ctlr = ctlr;
+	edev->port = ctlr->port;
+	edev->irq = ctlr->pcidev->intl;
+	edev->tbdf = ctlr->pcidev->tbdf;
+
+	/*
+	 * Check if the adapter's station address is to be overridden.
+	 * If not, read it from the device and set in edev->ea.
+	 */
+	memset(ea, 0, Eaddrlen);
+	if(memcmp(ea, edev->ea, Eaddrlen) == 0){
+		i = csr32r(ctlr, Idr0);
+		edev->ea[0] = i;
+		edev->ea[1] = i>>8;
+		edev->ea[2] = i>>16;
+		edev->ea[3] = i>>24;
+		i = csr32r(ctlr, Idr0+4);
+		edev->ea[4] = i;
+		edev->ea[5] = i>>8;
+	}
+
+	edev->attach = rtl8139attach;
+	edev->transmit = rtl8139transmit;
+	edev->interrupt = rtl8139interrupt;
+	edev->detach = rtl8139detach;
+
+	return 0;
+}
--- /dev/null
+++ b/os/boot/pc/ether8169.c
@@ -1,0 +1,958 @@
+/*
+ * Realtek RTL8110S/8169S.
+ * Mostly there. There are some magic register values used
+ * which are not described in any datasheet or driver but seem
+ * to be necessary.
+ * Why is the Fovf descriptor bit set for every received packet?
+ * Occasionally the hardware indicates an input TCP checksum error
+ * although the higher-level software seems to check the packet OK?
+ * No tuning has been done. Only tested on an RTL8110S, there
+ * are slight differences between the chips in the series so some
+ * tweaks may be needed.
+ */
+#include "u.h"
+#include "lib.h"
+#include "mem.h"
+#include "dat.h"
+#include "fns.h"
+#include "io.h"
+
+typedef struct QLock { int r; } QLock;
+#define qlock(i)	while(0)
+#define qunlock(i)	while(0)
+#define iallocb		allocb
+#define iprint		print
+#define mallocalign(n, a, o, s)	ialloc((n), (a))
+
+#include "etherif.h"
+#include "ethermii.h"
+
+enum {					/* registers */
+	Idr0		= 0x00,		/* MAC address */
+	Mar0		= 0x08,		/* Multicast address */
+	Dtccr		= 0x10,		/* Dump Tally Counter Command */
+	Tnpds		= 0x20,		/* Transmit Normal Priority Descriptors */
+	Thpds		= 0x28,		/* Transmit High Priority Descriptors */
+	Flash		= 0x30,		/* Flash Memory Read/Write */
+	Erbcr		= 0x34,		/* Early Receive Byte Count */
+	Ersr		= 0x36,		/* Early Receive Status */
+	Cr		= 0x37,		/* Command Register */
+	Tppoll		= 0x38,		/* Transmit Priority Polling */
+	Imr		= 0x3C,		/* Interrupt Mask */
+	Isr		= 0x3E,		/* Interrupt Status */
+	Tcr		= 0x40,		/* Transmit Configuration */
+	Rcr		= 0x44,		/* Receive Configuration */
+	Tctr		= 0x48,		/* Timer Count */
+	Mpc		= 0x4C,		/* Missed Packet Counter */
+	Cr9346		= 0x50,		/* 9346 Command Register */
+	Config0		= 0x51,		/* Configuration Register 0 */
+	Config1		= 0x52,		/* Configuration Register 1 */
+	Config2		= 0x53,		/* Configuration Register 2 */
+	Config3		= 0x54,		/* Configuration Register 3 */
+	Config4		= 0x55,		/* Configuration Register 4 */
+	Config5		= 0x56,		/* Configuration Register 5 */
+	Timerint	= 0x58,		/* Timer Interrupt */
+	Mulint		= 0x5C,		/* Multiple Interrupt Select */
+	Phyar		= 0x60,		/* PHY Access */
+	Tbicsr0		= 0x64,		/* TBI Control and Status */
+	Tbianar		= 0x68,		/* TBI Auto-Negotiation Advertisment */
+	Tbilpar		= 0x6A,		/* TBI Auto-Negotiation Link Partner */
+	Phystatus	= 0x6C,		/* PHY Status */
+
+	Rms		= 0xDA,		/* Receive Packet Maximum Size */
+	Cplusc		= 0xE0,		/* C+ Command */
+	Rdsar		= 0xE4,		/* Receive Descriptor Start Address */
+	Mtps		= 0xEC,		/* Max. Transmit Packet Size */
+};
+
+enum {					/* Dtccr */
+	Cmd		= 0x00000008,	/* Command */
+};
+
+enum {					/* Cr */
+	Te		= 0x04,		/* Transmitter Enable */
+	Re		= 0x08,		/* Receiver Enable */
+	Rst		= 0x10,		/* Software Reset */
+};
+
+enum {					/* Tppoll */
+	Fswint		= 0x01,		/* Forced Software Interrupt */
+	Npq		= 0x40,		/* Normal Priority Queue polling */
+	Hpq		= 0x80,		/* High Priority Queue polling */
+};
+
+enum {					/* Imr/Isr */
+	Rok		= 0x0001,	/* Receive OK */
+	Rer		= 0x0002,	/* Receive Error */
+	Tok		= 0x0004,	/* Transmit OK */
+	Ter		= 0x0008,	/* Transmit Error */
+	Rdu		= 0x0010,	/* Receive Descriptor Unavailable */
+	Punlc		= 0x0020,	/* Packet Underrun or Link Change */
+	Fovw		= 0x0040,	/* Receive FIFO Overflow */
+	Tdu		= 0x0080,	/* Transmit Descriptor Unavailable */
+	Swint		= 0x0100,	/* Software Interrupt */
+	Timeout		= 0x4000,	/* Timer */
+	Serr		= 0x8000,	/* System Error */
+};
+
+enum {					/* Tcr */
+	MtxdmaSHIFT	= 8,		/* Max. DMA Burst Size */
+	MtxdmaMASK	= 0x00000700,
+	Mtxdmaunlimited	= 0x00000700,
+	Acrc		= 0x00010000,	/* Append CRC (not) */
+	Lbk0		= 0x00020000,	/* Loopback Test 0 */
+	Lbk1		= 0x00040000,	/* Loopback Test 1 */
+	Ifg2		= 0x00080000,	/* Interframe Gap 2 */
+	HwveridSHIFT	= 23,		/* Hardware Version ID */
+	HwveridMASK	= 0x7C800000,
+	Macv01		= 0x00000000,	/* RTL8169 */
+	Macv02		= 0x00800000,	/* RTL8169S/8110S */
+	Macv03		= 0x04000000,	/* RTL8169S/8110S */
+	Macv04		= 0x10000000,	/* RTL8169SB/8110SB */
+	Macv05		= 0x18000000,	/* RTL8169SC/8110SC */
+	Macv11		= 0x30000000,	/* RTL8168B/8111B */
+	Macv12		= 0x38000000,	/* RTL8169B/8111B */
+	Macv13		= 0x34000000,	/* RTL8101E */
+	Macv14		= 0x30800000,	/* RTL8100E */
+	Macv15		= 0x38800000,	/* RTL8100E */
+	Ifg0		= 0x01000000,	/* Interframe Gap 0 */
+	Ifg1		= 0x02000000,	/* Interframe Gap 1 */
+};
+
+enum {					/* Rcr */
+	Aap		= 0x00000001,	/* Accept All Packets */
+	Apm		= 0x00000002,	/* Accept Physical Match */
+	Am		= 0x00000004,	/* Accept Multicast */
+	Ab		= 0x00000008,	/* Accept Broadcast */
+	Ar		= 0x00000010,	/* Accept Runt */
+	Aer		= 0x00000020,	/* Accept Error */
+	Sel9356		= 0x00000040,	/* 9356 EEPROM used */
+	MrxdmaSHIFT	= 8,		/* Max. DMA Burst Size */
+	MrxdmaMASK	= 0x00000700,
+	Mrxdmaunlimited	= 0x00000700,
+	RxfthSHIFT	= 13,		/* Receive Buffer Length */
+	RxfthMASK	= 0x0000E000,
+	Rxfth256	= 0x00008000,
+	Rxfthnone	= 0x0000E000,
+	Rer8		= 0x00010000,	/* Accept Error Packets > 8 bytes */
+	MulERINT	= 0x01000000,	/* Multiple Early Interrupt Select */
+};
+
+enum {					/* Cr9346 */
+	Eedo		= 0x01,		/* */
+	Eedi		= 0x02,		/* */
+	Eesk		= 0x04,		/* */
+	Eecs		= 0x08,		/* */
+	Eem0		= 0x40,		/* Operating Mode */
+	Eem1		= 0x80,
+};
+
+enum {					/* Phyar */
+	DataMASK	= 0x0000FFFF,	/* 16-bit GMII/MII Register Data */
+	DataSHIFT	= 0,
+	RegaddrMASK	= 0x001F0000,	/* 5-bit GMII/MII Register Address */
+	RegaddrSHIFT	= 16,
+	Flag		= 0x80000000,	/* */
+};
+
+enum {					/* Phystatus */
+	Fd		= 0x01,		/* Full Duplex */
+	Linksts		= 0x02,		/* Link Status */
+	Speed10		= 0x04,		/* */
+	Speed100	= 0x08,		/* */
+	Speed1000	= 0x10,		/* */
+	Rxflow		= 0x20,		/* */
+	Txflow		= 0x40,		/* */
+	Entbi		= 0x80,		/* */
+};
+
+enum {					/* Cplusc */
+	Mulrw		= 0x0008,	/* PCI Multiple R/W Enable */
+	Dac		= 0x0010,	/* PCI Dual Address Cycle Enable */
+	Rxchksum	= 0x0020,	/* Receive Checksum Offload Enable */
+	Rxvlan		= 0x0040,	/* Receive VLAN De-tagging Enable */
+	Endian		= 0x0200,	/* Endian Mode */
+};
+
+typedef struct D D;			/* Transmit/Receive Descriptor */
+struct D {
+	u32int	control;
+	u32int	vlan;
+	u32int	addrlo;
+	u32int	addrhi;
+};
+
+enum {					/* Transmit Descriptor control */
+	TxflMASK	= 0x0000FFFF,	/* Transmit Frame Length */
+	TxflSHIFT	= 0,
+	Tcps		= 0x00010000,	/* TCP Checksum Offload */
+	Udpcs		= 0x00020000,	/* UDP Checksum Offload */
+	Ipcs		= 0x00040000,	/* IP Checksum Offload */
+	Lgsen		= 0x08000000,	/* Large Send */
+};
+
+enum {					/* Receive Descriptor control */
+	RxflMASK	= 0x00003FFF,	/* Receive Frame Length */
+	RxflSHIFT	= 0,
+	Tcpf		= 0x00004000,	/* TCP Checksum Failure */
+	Udpf		= 0x00008000,	/* UDP Checksum Failure */
+	Ipf		= 0x00010000,	/* IP Checksum Failure */
+	Pid0		= 0x00020000,	/* Protocol ID0 */
+	Pid1		= 0x00040000,	/* Protocol ID1 */
+	Crce		= 0x00080000,	/* CRC Error */
+	Runt		= 0x00100000,	/* Runt Packet */
+	Res		= 0x00200000,	/* Receive Error Summary */
+	Rwt		= 0x00400000,	/* Receive Watchdog Timer Expired */
+	Fovf		= 0x00800000,	/* FIFO Overflow */
+	Bovf		= 0x01000000,	/* Buffer Overflow */
+	Bar		= 0x02000000,	/* Broadcast Address Received */
+	Pam		= 0x04000000,	/* Physical Address Matched */
+	Mar		= 0x08000000,	/* Multicast Address Received */
+};
+
+enum {					/* General Descriptor control */
+	Ls		= 0x10000000,	/* Last Segment Descriptor */
+	Fs		= 0x20000000,	/* First Segment Descriptor */
+	Eor		= 0x40000000,	/* End of Descriptor Ring */
+	Own		= 0x80000000,	/* Ownership */
+};
+
+/*
+ */
+enum {					/* Ring sizes  (<= 1024) */
+	Ntd		= 8,		/* Transmit Ring */
+	Nrd		= 32,		/* Receive Ring */
+
+	Mps		= ROUNDUP(ETHERMAXTU+4, 128),
+};
+
+typedef struct Dtcc Dtcc;
+struct Dtcc {
+	u64int	txok;
+	u64int	rxok;
+	u64int	txer;
+	u32int	rxer;
+	u16int	misspkt;
+	u16int	fae;
+	u32int	tx1col;
+	u32int	txmcol;
+	u64int	rxokph;
+	u64int	rxokbrd;
+	u32int	rxokmu;
+	u16int	txabt;
+	u16int	txundrn;
+};
+
+enum {						/* Variants */
+	Rtl8100e	= (0x8136<<16)|0x10EC,	/* RTL810[01]E ? */
+	Rtl8169c		= (0x0116<<16)|0x16EC,	/* RTL8169C+ (USR997902) */
+	Rtl8169sc	= (0x8167<<16)|0x10EC,	/* RTL8169SC */
+	Rtl8168b	= (0x8168<<16)|0x10EC,	/* RTL8168B */
+	Rtl8169		= (0x8169<<16)|0x10EC,	/* RTL8169 */
+};
+
+typedef struct Ctlr Ctlr;
+typedef struct Ctlr {
+	int	port;
+	Pcidev*	pcidev;
+	Ctlr*	next;
+	int	active;
+
+	void*	nic;
+
+	QLock	alock;			/* attach */
+	Lock	ilock;			/* init */
+	int	init;			/*  */
+
+	int	pciv;			/*  */
+	int	macv;			/* MAC version */
+	int	phyv;			/* PHY version */
+
+	Mii*	mii;
+
+	Lock	tlock;			/* transmit */
+	D*	td;			/* descriptor ring */
+	Block**	tb;			/* transmit buffers */
+	int	ntd;
+
+	int	tdh;			/* head - producer index (host) */
+	int	tdt;			/* tail - consumer index (NIC) */
+	int	ntdfree;
+	int	ntq;
+
+	int	mtps;			/* Max. Transmit Packet Size */
+
+	Lock	rlock;			/* receive */
+	D*	rd;			/* descriptor ring */
+	void**	rb;			/* receive buffers */
+	int	nrd;
+
+	int	rdh;			/* head - producer index (NIC) */
+	int	rdt;			/* tail - consumer index (host) */
+	int	nrdfree;
+
+	int	rcr;			/* receive configuration register */
+
+	QLock	slock;			/* statistics */
+	Dtcc*	dtcc;
+	uint	txdu;
+	uint	tcpf;
+	uint	udpf;
+	uint	ipf;
+	uint	fovf;
+	uint	ierrs;
+	uint	rer;
+	uint	rdu;
+	uint	punlc;
+	uint	fovw;
+} Ctlr;
+
+static Ctlr* rtl8169ctlrhead;
+static Ctlr* rtl8169ctlrtail;
+
+#define csr8r(c, r)	(inb((c)->port+(r)))
+#define csr16r(c, r)	(ins((c)->port+(r)))
+#define csr32r(c, r)	(inl((c)->port+(r)))
+#define csr8w(c, r, b)	(outb((c)->port+(r), (int)(b)))
+#define csr16w(c, r, w)	(outs((c)->port+(r), (ushort)(w)))
+#define csr32w(c, r, l)	(outl((c)->port+(r), (ulong)(l)))
+
+static int
+rtl8169miimir(Mii* mii, int pa, int ra)
+{
+	uint r;
+	int timeo;
+	Ctlr *ctlr;
+
+	if(pa != 1)
+		return -1;
+	ctlr = mii->ctlr;
+
+	r = (ra<<16) & RegaddrMASK;
+	csr32w(ctlr, Phyar, r);
+	delay(1);
+	for(timeo = 0; timeo < 2000; timeo++){
+		if((r = csr32r(ctlr, Phyar)) & Flag)
+			break;
+		microdelay(100);
+	}
+	if(!(r & Flag))
+		return -1;
+
+	return (r & DataMASK)>>DataSHIFT;
+}
+
+static int
+rtl8169miimiw(Mii* mii, int pa, int ra, int data)
+{
+	uint r;
+	int timeo;
+	Ctlr *ctlr;
+
+	if(pa != 1)
+		return -1;
+	ctlr = mii->ctlr;
+
+	r = Flag|((ra<<16) & RegaddrMASK)|((data<<DataSHIFT) & DataMASK);
+	csr32w(ctlr, Phyar, r);
+	delay(1);
+	for(timeo = 0; timeo < 2000; timeo++){
+		if(!((r = csr32r(ctlr, Phyar)) & Flag))
+			break;
+		microdelay(100);
+	}
+	if(r & Flag)
+		return -1;
+
+	return 0;
+}
+
+static int
+rtl8169mii(Ctlr* ctlr)
+{
+	MiiPhy *phy;
+
+	/*
+	 * Link management.
+	 */
+	if((ctlr->mii = malloc(sizeof(Mii))) == nil)
+		return -1;
+	ctlr->mii->mir = rtl8169miimir;
+	ctlr->mii->miw = rtl8169miimiw;
+	ctlr->mii->ctlr = ctlr;
+
+	/*
+	 * Get rev number out of Phyidr2 so can config properly.
+	 * There's probably more special stuff for Macv0[234] needed here.
+	 */
+	ctlr->phyv = rtl8169miimir(ctlr->mii, 1, Phyidr2) & 0x0F;
+	if(ctlr->macv == Macv02){
+		csr8w(ctlr, 0x82, 1);				/* magic */
+		rtl8169miimiw(ctlr->mii, 1, 0x0B, 0x0000);	/* magic */
+	}
+
+	if(mii(ctlr->mii, (1<<1)) == 0 || (phy = ctlr->mii->curphy) == nil){
+		free(ctlr->mii);
+		ctlr->mii = nil;
+		return -1;
+	}
+	print("oui %#ux phyno %d, macv = %#8.8ux phyv = %#4.4ux\n",
+		phy->oui, phy->phyno, ctlr->macv, ctlr->phyv);
+
+	miiane(ctlr->mii, ~0, ~0, ~0);
+
+	return 0;
+}
+
+static void
+rtl8169halt(Ctlr* ctlr)
+{
+	csr8w(ctlr, Cr, 0);
+	csr16w(ctlr, Imr, 0);
+	csr16w(ctlr, Isr, ~0);
+}
+
+static int
+rtl8169reset(Ctlr* ctlr)
+{
+	u32int r;
+	int timeo;
+
+	/*
+	 * Soft reset the controller.
+	 */
+	csr8w(ctlr, Cr, Rst);
+	for(r = timeo = 0; timeo < 1000; timeo++){
+		r = csr8r(ctlr, Cr);
+		if(!(r & Rst))
+			break;
+		delay(1);
+	}
+	rtl8169halt(ctlr);
+
+	if(r & Rst)
+		return -1;
+	return 0;
+}
+
+static void
+rtl8169detach(Ether* edev)
+{
+	rtl8169reset(edev->ctlr);
+}
+
+static void
+rtl8169replenish(Ctlr* ctlr)
+{
+	D *d;
+	int rdt;
+	void *bp;
+
+	rdt = ctlr->rdt;
+	while(NEXT(rdt, ctlr->nrd) != ctlr->rdh){
+		d = &ctlr->rd[rdt];
+		if(ctlr->rb[rdt] == nil){
+			/*
+			 * simple allocation for now
+			 */
+			bp = mallocalign(Mps, 8, 0, 0);
+			ctlr->rb[rdt] = bp;
+			d->addrlo = PCIWADDR(bp);
+			d->addrhi = 0;
+		}
+		coherence();
+		d->control |= Own|Mps;
+		rdt = NEXT(rdt, ctlr->nrd);
+		ctlr->nrdfree++;
+	}
+	ctlr->rdt = rdt;
+}
+
+static int
+rtl8169init(Ether* edev)
+{
+	u32int r;
+	Ctlr *ctlr;
+	u8int cplusc;
+
+	ctlr = edev->ctlr;
+	ilock(&ctlr->ilock);
+
+	rtl8169halt(ctlr);
+
+	/*
+	 * MAC Address.
+	 * Must put chip into config register write enable mode.
+	 */
+	csr8w(ctlr, Cr9346, Eem1|Eem0);
+	r = (edev->ea[3]<<24)|(edev->ea[2]<<16)|(edev->ea[1]<<8)|edev->ea[0];
+	csr32w(ctlr, Idr0, r);
+	r = (edev->ea[5]<<8)|edev->ea[4];
+	csr32w(ctlr, Idr0+4, r);
+
+	/*
+	 * Transmitter.
+	 */
+	memset(ctlr->td, 0, sizeof(D)*ctlr->ntd);
+	ctlr->tdh = ctlr->tdt = 0;
+	ctlr->td[ctlr->ntd-1].control = Eor;
+
+	/*
+	 * Receiver.
+	 * Need to do something here about the multicast filter.
+	 */
+	memset(ctlr->rd, 0, sizeof(D)*ctlr->nrd);
+	ctlr->rdh = ctlr->rdt = 0;
+	ctlr->rd[ctlr->nrd-1].control = Eor;
+	rtl8169replenish(ctlr);
+	ctlr->rcr = Rxfthnone|Mrxdmaunlimited|Ab|Apm;
+
+	/*
+	 * Mtps is in units of 128 except for the RTL8169
+	 * where is is 32. If using jumbo frames should be
+	 * set to 0x3F.
+	 * Setting Mulrw in Cplusc disables the Tx/Rx DMA burst
+	 * settings in Tcr/Rcr; the (1<<14) is magic.
+	 */
+	ctlr->mtps = HOWMANY(Mps, 128);
+	cplusc = csr16r(ctlr, Cplusc) & ~(1<<14);
+	cplusc |= Rxchksum|Mulrw;
+	switch(ctlr->macv){
+	default:
+		return -1;
+	case Macv01:
+		ctlr->mtps = HOWMANY(Mps, 32);
+		break;
+	case Macv02:
+	case Macv03:
+		cplusc |= (1<<14);			/* magic */
+		break;
+	case Macv05:
+		/*
+		 * This is interpreted from clearly bogus code
+		 * in the manufacturer-supplied driver, it could
+		 * be wrong. Untested.
+		 */
+		r = csr8r(ctlr, Config2) & 0x07;
+		if(r == 0x01)				/* 66MHz PCI */
+			csr32w(ctlr, 0x7C, 0x0007FFFF);	/* magic */
+		else
+			csr32w(ctlr, 0x7C, 0x0007FF00);	/* magic */
+		pciclrmwi(ctlr->pcidev);
+		break;
+	case Macv13:
+		/*
+		 * This is interpreted from clearly bogus code
+		 * in the manufacturer-supplied driver, it could
+		 * be wrong. Untested.
+		 */
+		pcicfgw8(ctlr->pcidev, 0x68, 0x00);	/* magic */
+		pcicfgw8(ctlr->pcidev, 0x69, 0x08);	/* magic */
+		break;
+	case Macv04:
+	case Macv11:
+	case Macv12:
+	case Macv14:
+	case Macv15:
+		break;
+	}
+
+	/*
+	 * Enable receiver/transmitter.
+	 * Need to do this first or some of the settings below
+	 * won't take.
+	 */
+	switch(ctlr->pciv){
+	default:
+		csr8w(ctlr, Cr, Te|Re);
+		csr32w(ctlr, Tcr, Ifg1|Ifg0|Mtxdmaunlimited);
+		csr32w(ctlr, Rcr, ctlr->rcr);
+	case Rtl8169sc:
+	case Rtl8168b:
+		break;
+	}
+
+	/*
+	 * Interrupts.
+	 * Disable Tdu|Tok for now, the transmit routine will tidy.
+	 * Tdu means the NIC ran out of descriptors to send, so it
+	 * doesn't really need to ever be on.
+	 */
+	csr32w(ctlr, Timerint, 0);
+	csr16w(ctlr, Imr, Serr|Timeout|Fovw|Punlc|Rdu|Ter|Rer|Rok);
+
+	/*
+	 * Clear missed-packet counter;
+	 * initial early transmit threshold value;
+	 * set the descriptor ring base addresses;
+	 * set the maximum receive packet size;
+	 * no early-receive interrupts.
+	 */
+	csr32w(ctlr, Mpc, 0);
+	csr8w(ctlr, Mtps, ctlr->mtps);
+	csr32w(ctlr, Tnpds+4, 0);
+	csr32w(ctlr, Tnpds, PCIWADDR(ctlr->td));
+	csr32w(ctlr, Rdsar+4, 0);
+	csr32w(ctlr, Rdsar, PCIWADDR(ctlr->rd));
+	csr16w(ctlr, Rms, Mps);
+	r = csr16r(ctlr, Mulint) & 0xF000;
+	csr16w(ctlr, Mulint, r);
+	csr16w(ctlr, Cplusc, cplusc);
+
+	/*
+	 * Set configuration.
+	 */
+	switch(ctlr->pciv){
+	default:
+		break;
+	case Rtl8169sc:
+		csr16w(ctlr, 0xE2, 0);			/* magic */
+		csr8w(ctlr, Cr, Te|Re);
+		csr32w(ctlr, Tcr, Ifg1|Ifg0|Mtxdmaunlimited);
+		csr32w(ctlr, Rcr, ctlr->rcr);
+		break;
+	case Rtl8168b:
+	case Rtl8169c:
+		csr16w(ctlr, 0xE2, 0);			/* magic */
+		csr16w(ctlr, Cplusc, 0x2000);		/* magic */
+		csr8w(ctlr, Cr, Te|Re);
+		csr32w(ctlr, Tcr, Ifg1|Ifg0|Mtxdmaunlimited);
+		csr32w(ctlr, Rcr, ctlr->rcr);
+		csr16w(ctlr, Rms, 0x0800);
+		csr8w(ctlr, Mtps, 0x3F);
+		break;
+	}
+
+	csr8w(ctlr, Cr9346, 0);
+
+	iunlock(&ctlr->ilock);
+
+//	rtl8169mii(ctlr);
+
+	return 0;
+}
+
+static void
+rtl8169attach(Ether* edev)
+{
+	int timeo;
+	Ctlr *ctlr;
+
+	ctlr = edev->ctlr;
+	qlock(&ctlr->alock);
+	if(ctlr->init == 0){
+		/*
+		 * Handle allocation/init errors here.
+		 */
+		ctlr->td = xspanalloc(sizeof(D)*Ntd, 256, 0);
+		ctlr->tb = malloc(Ntd*sizeof(Block*));
+		ctlr->ntd = Ntd;
+		ctlr->rd = xspanalloc(sizeof(D)*Nrd, 256, 0);
+		ctlr->rb = malloc(Nrd*sizeof(Block*));
+		ctlr->nrd = Nrd;
+		ctlr->dtcc = xspanalloc(sizeof(Dtcc), 64, 0);
+		rtl8169init(edev);
+		ctlr->init = 1;
+	}
+	qunlock(&ctlr->alock);
+
+	for(timeo = 0; timeo < 3500; timeo++){
+		if(miistatus(ctlr->mii) == 0)
+			break;
+		delay(10);
+	}
+}
+
+static void
+rtl8169transmit(Ether* edev)
+{
+	D *d;
+	Block *bp;
+	Ctlr *ctlr;
+	int control, x;
+	RingBuf *tb;
+
+	ctlr = edev->ctlr;
+
+	ilock(&ctlr->tlock);
+	for(x = ctlr->tdh; ctlr->ntq > 0; x = NEXT(x, ctlr->ntd)){
+		d = &ctlr->td[x];
+		if((control = d->control) & Own)
+			break;
+
+		/*
+		 * Check errors and log here.
+		 */
+		USED(control);
+
+		/*
+		 * Free it up.
+		 * Need to clean the descriptor here? Not really.
+		 * Simple freeb for now (no chain and freeblist).
+		 * Use ntq count for now.
+		 */
+		freeb(ctlr->tb[x]);
+		ctlr->tb[x] = nil;
+		d->control &= Eor;
+
+		ctlr->ntq--;
+	}
+	ctlr->tdh = x;
+
+	x = ctlr->tdt;
+	while(ctlr->ntq < (ctlr->ntd-1)){
+		tb = &edev->tb[edev->ti];
+		if(tb->owner != Interface)
+			break;
+
+		bp = allocb(tb->len);
+		memmove(bp->wp, tb->pkt, tb->len);
+		memmove(bp->wp+Eaddrlen, edev->ea, Eaddrlen);
+		bp->wp += tb->len;
+
+		tb->owner = Host;
+		edev->ti = NEXT(edev->ti, edev->ntb);
+
+		d = &ctlr->td[x];
+		d->addrlo = PCIWADDR(bp->rp);
+		d->addrhi = 0;
+		ctlr->tb[x] = bp;
+		coherence();
+		d->control |= Own|Fs|Ls|((BLEN(bp)<<TxflSHIFT) & TxflMASK);
+
+		x = NEXT(x, ctlr->ntd);
+		ctlr->ntq++;
+	}
+	if(x != ctlr->tdt){
+		ctlr->tdt = x;
+		csr8w(ctlr, Tppoll, Npq);
+	}
+	else if(ctlr->ntq >= (ctlr->ntd-1))
+		ctlr->txdu++;
+
+	iunlock(&ctlr->tlock);
+}
+
+static void
+rtl8169receive(Ether* edev)
+{
+	D *d;
+	int len, rdh;
+	Ctlr *ctlr;
+	u32int control;
+	RingBuf *ring;
+
+	ctlr = edev->ctlr;
+
+	rdh = ctlr->rdh;
+	for(;;){
+		d = &ctlr->rd[rdh];
+	
+		if(d->control & Own)
+			break;
+
+		control = d->control;
+		if((control & (Fs|Ls|Res)) == (Fs|Ls)){
+			len = ((control & RxflMASK)>>RxflSHIFT) - 4;
+
+			ring = &edev->rb[edev->ri];
+			if(ring->owner == Interface){
+				ring->owner = Host;
+				ring->len = len;
+				memmove(ring->pkt, ctlr->rb[rdh], len);
+				edev->ri = NEXT(edev->ri, edev->nrb);
+			}
+		}
+		else{
+			/*
+			 * Error stuff here.
+			print("control %#8.8ux\n", control);
+			 */
+		}
+		d->control &= Eor;
+		ctlr->nrdfree--;
+		rdh = NEXT(rdh, ctlr->nrd);
+	}
+	ctlr->rdh = rdh;
+	
+	if(ctlr->nrdfree < ctlr->nrd/2)
+		rtl8169replenish(ctlr);
+}
+
+static void
+rtl8169interrupt(Ureg*, void* arg)
+{
+	Ctlr *ctlr;
+	Ether *edev;
+	u32int isr;
+
+	edev = arg;
+	ctlr = edev->ctlr;
+
+	while((isr = csr16r(ctlr, Isr)) != 0 && isr != 0xFFFF){
+		csr16w(ctlr, Isr, isr);
+		if(isr & (Fovw|Punlc|Rdu|Rer|Rok)){
+			rtl8169receive(edev);
+			if(!(isr & (Punlc|Rok)))
+				ctlr->ierrs++;
+			if(isr & Rer)
+				ctlr->rer++;
+			if(isr & Rdu)
+				ctlr->rdu++;
+			if(isr & Punlc)
+				ctlr->punlc++;
+			if(isr & Fovw)
+				ctlr->fovw++;
+			isr &= ~(Fovw|Rdu|Rer|Rok);
+		}
+
+		if(isr & (Tdu|Ter|Tok)){
+			rtl8169transmit(edev);
+			isr &= ~(Tdu|Ter|Tok);
+		}
+
+		if(isr & Punlc){
+//			rtl8169link(edev);
+			isr &= ~Punlc;
+		}
+
+		/*
+		 * Some of the reserved bits get set sometimes...
+		 */
+		if(isr & (Serr|Timeout|Tdu|Fovw|Punlc|Rdu|Ter|Tok|Rer|Rok))
+			panic("rtl8169interrupt: imr %#4.4ux isr %#4.4ux\n",
+				csr16r(ctlr, Imr), isr);
+	}
+}
+
+static void
+rtl8169pci(void)
+{
+	Pcidev *p;
+	Ctlr *ctlr;
+	int i, port;
+	u32int bar;
+
+	p = nil;
+	while(p = pcimatch(p, 0, 0)){
+		if(p->ccrb != 0x02 || p->ccru != 0)
+			continue;
+
+		switch(i = ((p->did<<16)|p->vid)){
+		default:
+			continue;
+		case Rtl8100e:			/* RTL810[01]E ? */
+		case Rtl8169c:			/* RTL8169C */
+		case Rtl8169sc:			/* RTL8169SC */
+		case Rtl8168b:			/* RTL8168B */
+		case Rtl8169:			/* RTL8169 */
+			break;
+		case (0xC107<<16)|0x1259:	/* Corega CG-LAPCIGT */
+			i = Rtl8169;
+			break;
+		}
+
+		bar = p->mem[0].bar;
+		port = bar & ~0x01;
+		if(ioalloc(port, p->mem[0].size, 0, "rtl8169") < 0){
+			print("rtl8169: port %#ux in use\n", port);
+			continue;
+		}
+		ctlr = malloc(sizeof(Ctlr));
+		ctlr->port = port;
+		ctlr->pcidev = p;
+		ctlr->pciv = i;
+
+		if(pcigetpms(p) > 0){
+			pcisetpms(p, 0);
+	
+			for(i = 0; i < 6; i++)
+				pcicfgw32(p, PciBAR0+i*4, p->mem[i].bar);
+			pcicfgw8(p, PciINTL, p->intl);
+			pcicfgw8(p, PciLTR, p->ltr);
+			pcicfgw8(p, PciCLS, p->cls);
+			pcicfgw16(p, PciPCR, p->pcr);
+		}
+
+		if(rtl8169reset(ctlr)){
+			iofree(port);
+			free(ctlr);
+			continue;
+		}
+
+		/*
+		 * Extract the chip hardware version,
+		 * needed to configure each properly.
+		 */
+		ctlr->macv = csr32r(ctlr, Tcr) & HwveridMASK;
+
+		rtl8169mii(ctlr);
+
+		pcisetbme(p);
+
+		if(rtl8169ctlrhead != nil)
+			rtl8169ctlrtail->next = ctlr;
+		else
+			rtl8169ctlrhead = ctlr;
+		rtl8169ctlrtail = ctlr;
+	}
+}
+
+int
+rtl8169pnp(Ether* edev)
+{
+	u32int r;
+	Ctlr *ctlr;
+
+	if(rtl8169ctlrhead == nil)
+		rtl8169pci();
+
+	/*
+	 * Any adapter matches if no edev->port is supplied,
+	 * otherwise the ports must match.
+	 */
+	for(ctlr = rtl8169ctlrhead; ctlr != nil; ctlr = ctlr->next){
+		if(ctlr->active)
+			continue;
+		if(edev->port == 0 || edev->port == ctlr->port){
+			ctlr->active = 1;
+			break;
+		}
+	}
+	if(ctlr == nil)
+		return -1;
+
+	edev->ctlr = ctlr;
+	edev->port = ctlr->port;
+	edev->irq = ctlr->pcidev->intl;
+	edev->tbdf = ctlr->pcidev->tbdf;
+//	edev->mbps = 100;
+
+	/*
+	 * Pull the MAC address out of the chip.
+	 */
+	r = csr32r(ctlr, Idr0);
+	edev->ea[0] = r;
+	edev->ea[1] = r>>8;
+	edev->ea[2] = r>>16;
+	edev->ea[3] = r>>24;
+	r = csr32r(ctlr, Idr0+4);
+	edev->ea[4] = r;
+	edev->ea[5] = r>>8;
+
+	/*
+	 * Linkage to the generic ethernet driver.
+	 */
+	edev->attach = rtl8169attach;
+	edev->transmit = rtl8169transmit;
+	edev->interrupt = rtl8169interrupt;
+	edev->detach = rtl8169detach;
+//	edev->ifstat = rtl8169ifstat;
+//	edev->ctl = nil;
+//
+//	edev->arg = edev;
+//	edev->promiscuous = rtl8169promiscuous;
+//	edev->multicast = rtl8169multicast;
+
+	return 0;
+}
--- /dev/null
+++ b/os/boot/pc/ether82557.c
@@ -1,0 +1,882 @@
+/*
+ * Intel 82557 Fast Ethernet PCI Bus LAN Controller
+ * as found on the Intel EtherExpress PRO/100B. This chip is full
+ * of smarts, unfortunately none of them are in the right place.
+ * To do:
+ *	the PCI scanning code could be made common to other adapters;
+ *	PCI code needs rewritten to handle byte, word, dword accesses
+ *	  and using the devno as a bus+dev+function triplet.
+ */
+#include "u.h"
+#include "lib.h"
+#include "mem.h"
+#include "dat.h"
+#include "fns.h"
+#include "io.h"
+
+#include "etherif.h"
+
+enum {
+	Nrfd		= 4,		/* receive frame area */
+
+	NullPointer	= 0xFFFFFFFF,	/* 82557 NULL pointer */
+};
+
+enum {					/* CSR */
+	Status		= 0x00,		/* byte or word (word includes Ack) */
+	Ack		= 0x01,		/* byte */
+	CommandR	= 0x02,		/* byte or word (word includes Interrupt) */
+	Interrupt	= 0x03,		/* byte */
+	Pointer		= 0x04,		/* dword */
+	Port		= 0x08,		/* dword */
+	Fcr		= 0x0C,		/* Flash control register */
+	Ecr		= 0x0E,		/* EEPROM control register */
+	Mcr		= 0x10,		/* MDI control register */
+};
+
+enum {					/* Status */
+	RUidle		= 0x0000,
+	RUsuspended	= 0x0004,
+	RUnoresources	= 0x0008,
+	RUready		= 0x0010,
+	RUrbd		= 0x0020,	/* bit */
+	RUstatus	= 0x003F,	/* mask */
+
+	CUidle		= 0x0000,
+	CUsuspended	= 0x0040,
+	CUactive	= 0x0080,
+	CUstatus	= 0x00C0,	/* mask */
+
+	StatSWI		= 0x0400,	/* SoftWare generated Interrupt */
+	StatMDI		= 0x0800,	/* MDI r/w done */
+	StatRNR		= 0x1000,	/* Receive unit Not Ready */
+	StatCNA		= 0x2000,	/* Command unit Not Active (Active->Idle) */
+	StatFR		= 0x4000,	/* Finished Receiving */
+	StatCX		= 0x8000,	/* Command eXecuted */
+	StatTNO		= 0x8000,	/* Transmit NOT OK */
+};
+
+enum {					/* Command (byte) */
+	CUnop		= 0x00,
+	CUstart		= 0x10,
+	CUresume	= 0x20,
+	LoadDCA		= 0x40,		/* Load Dump Counters Address */
+	DumpSC		= 0x50,		/* Dump Statistical Counters */
+	LoadCUB		= 0x60,		/* Load CU Base */
+	ResetSA		= 0x70,		/* Dump and Reset Statistical Counters */
+
+	RUstart		= 0x01,
+	RUresume	= 0x02,
+	RUabort		= 0x04,
+	LoadHDS		= 0x05,		/* Load Header Data Size */
+	LoadRUB		= 0x06,		/* Load RU Base */
+	RBDresume	= 0x07,		/* Resume frame reception */
+};
+
+enum {					/* Interrupt (byte) */
+	InterruptM	= 0x01,		/* interrupt Mask */
+	InterruptSI	= 0x02,		/* Software generated Interrupt */
+};
+
+enum {					/* Ecr */
+	EEsk		= 0x01,		/* serial clock */
+	EEcs		= 0x02,		/* chip select */
+	EEdi		= 0x04,		/* serial data in */
+	EEdo		= 0x08,		/* serial data out */
+
+	EEstart		= 0x04,		/* start bit */
+	EEread		= 0x02,		/* read opcode */
+};
+
+enum {					/* Mcr */
+	MDIread		= 0x08000000,	/* read opcode */
+	MDIwrite	= 0x04000000,	/* write opcode */
+	MDIready	= 0x10000000,	/* ready bit */
+	MDIie		= 0x20000000,	/* interrupt enable */
+};
+
+typedef struct Rfd {
+	int	field;
+	ulong	link;
+	ulong	rbd;
+	ushort	count;
+	ushort	size;
+
+	Etherpkt;
+} Rfd;
+
+enum {					/* field */
+	RfdCollision	= 0x00000001,
+	RfdIA		= 0x00000002,	/* IA match */
+	RfdRxerr	= 0x00000010,	/* PHY character error */
+	RfdType		= 0x00000020,	/* Type frame */
+	RfdRunt		= 0x00000080,
+	RfdOverrun	= 0x00000100,
+	RfdBuffer	= 0x00000200,
+	RfdAlignment	= 0x00000400,
+	RfdCRC		= 0x00000800,
+
+	RfdOK		= 0x00002000,	/* frame received OK */
+	RfdC		= 0x00008000,	/* reception Complete */
+	RfdSF		= 0x00080000,	/* Simplified or Flexible (1) Rfd */
+	RfdH		= 0x00100000,	/* Header RFD */
+
+	RfdI		= 0x20000000,	/* Interrupt after completion */
+	RfdS		= 0x40000000,	/* Suspend after completion */
+	RfdEL		= 0x80000000,	/* End of List */
+};
+
+enum {					/* count */
+	RfdF		= 0x00004000,
+	RfdEOF		= 0x00008000,
+};
+
+typedef struct Cb {
+	int	command;
+	ulong	link;
+	uchar	data[24];	/* CbIAS + CbConfigure */
+} Cb;
+
+typedef struct TxCB {
+	int	command;
+	ulong	link;
+	ulong	tbd;
+	ushort	count;
+	uchar	threshold;
+	uchar	number;
+} TxCB;
+
+enum {					/* action command */
+	CbOK		= 0x00002000,	/* DMA completed OK */
+	CbC		= 0x00008000,	/* execution Complete */
+
+	CbNOP		= 0x00000000,
+	CbIAS		= 0x00010000,	/* Indvidual Address Setup */
+	CbConfigure	= 0x00020000,
+	CbMAS		= 0x00030000,	/* Multicast Address Setup */
+	CbTransmit	= 0x00040000,
+	CbDump		= 0x00060000,
+	CbDiagnose	= 0x00070000,
+	CbCommand	= 0x00070000,	/* mask */
+
+	CbSF		= 0x00080000,	/* CbTransmit */
+
+	CbI		= 0x20000000,	/* Interrupt after completion */
+	CbS		= 0x40000000,	/* Suspend after completion */
+	CbEL		= 0x80000000,	/* End of List */
+};
+
+enum {					/* CbTransmit count */
+	CbEOF		= 0x00008000,
+};
+
+typedef struct Ctlr Ctlr;
+typedef struct Ctlr {
+	int	port;
+	Pcidev*	pcidev;
+	Ctlr*	next;
+	int	active;
+
+	int	eepromsz;		/* address size in bits */
+	ushort*	eeprom;
+
+	int	ctlrno;
+	char*	type;
+
+	uchar	configdata[24];
+
+	Rfd	rfd[Nrfd];
+	int	rfdl;
+	int	rfdx;
+
+	Block*	cbqhead;
+	Block*	cbqtail;
+	int	cbqbusy;
+} Ctlr;
+
+static Ctlr* ctlrhead;
+static Ctlr* ctlrtail;
+
+static uchar configdata[24] = {
+	0x16,				/* byte count */
+	0x44,				/* Rx/Tx FIFO limit */
+	0x00,				/* adaptive IFS */
+	0x00,	
+	0x04,				/* Rx DMA maximum byte count */
+	0x84,				/* Tx DMA maximum byte count */
+	0x33,				/* late SCB, CNA interrupts */
+	0x01,				/* discard short Rx frames */
+	0x00,				/* 503/MII */
+
+	0x00,	
+	0x2E,				/* normal operation, NSAI */
+	0x00,				/* linear priority */
+	0x60,				/* inter-frame spacing */
+	0x00,	
+	0xF2,	
+	0x48,				/* promiscuous mode off */
+	0x00,	
+	0x40,	
+	0xF2,				/* transmit padding enable */
+	0x80,				/* full duplex pin enable */
+	0x3F,				/* no Multi IA */
+	0x05,				/* no Multi Cast ALL */
+};
+
+#define csr8r(c, r)	(inb((c)->port+(r)))
+#define csr16r(c, r)	(ins((c)->port+(r)))
+#define csr32r(c, r)	(inl((c)->port+(r)))
+#define csr8w(c, r, b)	(outb((c)->port+(r), (int)(b)))
+#define csr16w(c, r, w)	(outs((c)->port+(r), (ushort)(w)))
+#define csr32w(c, r, l)	(outl((c)->port+(r), (ulong)(l)))
+
+static void
+custart(Ctlr* ctlr)
+{
+	if(ctlr->cbqhead == 0){
+		ctlr->cbqbusy = 0;
+		return;
+	}
+	ctlr->cbqbusy = 1;
+
+	csr32w(ctlr, Pointer, PADDR(ctlr->cbqhead->rp));
+	while(csr8r(ctlr, CommandR))
+		;
+	csr8w(ctlr, CommandR, CUstart);
+}
+
+static void
+action(Ctlr* ctlr, Block* bp)
+{
+	Cb *cb;
+
+	cb = (Cb*)bp->rp;
+	cb->command |= CbEL;
+
+	if(ctlr->cbqhead){
+		ctlr->cbqtail->next = bp;
+		cb = (Cb*)ctlr->cbqtail->rp;
+		cb->link = PADDR(bp->rp);
+		cb->command &= ~CbEL;
+	}
+	else
+		ctlr->cbqhead = bp;
+	ctlr->cbqtail = bp;
+
+	if(ctlr->cbqbusy == 0)
+		custart(ctlr);
+}
+
+static void
+attach(Ether* ether)
+{
+	int status;
+	Ctlr *ctlr;
+
+	ctlr = ether->ctlr;
+	status = csr16r(ctlr, Status);
+	if((status & RUstatus) == RUidle){
+		csr32w(ctlr, Pointer, PADDR(&ctlr->rfd[ctlr->rfdx]));
+		while(csr8r(ctlr, CommandR))
+			;
+		csr8w(ctlr, CommandR, RUstart);
+	}
+}
+
+static void
+configure(void* arg, int promiscuous)
+{
+	Ctlr *ctlr;
+	Block *bp;
+	Cb *cb;
+
+	ctlr = ((Ether*)arg)->ctlr;
+
+	bp = allocb(sizeof(Cb));
+	cb = (Cb*)bp->rp;
+	bp->wp += sizeof(Cb);
+
+	cb->command = CbConfigure;
+	cb->link = NullPointer;
+	memmove(cb->data, ctlr->configdata, sizeof(ctlr->configdata));
+	if(promiscuous)
+		cb->data[15] |= 0x01;
+	action(ctlr, bp);
+}
+
+static void
+transmit(Ether* ether)
+{
+	Block *bp;
+	TxCB *txcb;
+	RingBuf *tb;
+
+	for(tb = &ether->tb[ether->ti]; tb->owner == Interface; tb = &ether->tb[ether->ti]){
+		bp = allocb(tb->len+sizeof(TxCB));
+		txcb = (TxCB*)bp->wp;
+		bp->wp += sizeof(TxCB);
+
+		txcb->command = CbTransmit;
+		txcb->link = NullPointer;
+		txcb->tbd = NullPointer;
+		txcb->count = CbEOF|tb->len;
+		txcb->threshold = 2;
+		txcb->number = 0;
+
+		memmove(bp->wp, tb->pkt, tb->len);
+		memmove(bp->wp+Eaddrlen, ether->ea, Eaddrlen);
+		bp->wp += tb->len;
+
+		action(ether->ctlr, bp);
+
+		tb->owner = Host;
+		ether->ti = NEXT(ether->ti, ether->ntb);
+	}
+}
+
+static void
+interrupt(Ureg*, void* arg)
+{
+	Rfd *rfd;
+	Block *bp;
+	Ctlr *ctlr;
+	Ether *ether;
+	int status;
+	RingBuf *rb;
+
+	ether = arg;
+	ctlr = ether->ctlr;
+
+	for(;;){
+		status = csr16r(ctlr, Status);
+		csr8w(ctlr, Ack, (status>>8) & 0xFF);
+
+		if((status & (StatCX|StatFR|StatCNA|StatRNR)) == 0)
+			return;
+
+		if(status & StatFR){
+			rfd = &ctlr->rfd[ctlr->rfdx];
+			while(rfd->field & RfdC){
+				rb = &ether->rb[ether->ri];
+				if(rb->owner == Interface){
+					rb->owner = Host;
+					rb->len = rfd->count & 0x3FFF;
+					memmove(rb->pkt, rfd->d, rfd->count & 0x3FFF);
+					ether->ri = NEXT(ether->ri, ether->nrb);
+				}
+
+				/*
+				 * Reinitialise the frame for reception and bump
+				 * the receive frame processing index;
+				 * bump the sentinel index, mark the new sentinel
+				 * and clear the old sentinel suspend bit;
+				 * set bp and rfd for the next receive frame to
+				 * process.
+				 */
+				rfd->field = 0;
+				rfd->count = 0;
+				ctlr->rfdx = NEXT(ctlr->rfdx, Nrfd);
+
+				rfd = &ctlr->rfd[ctlr->rfdl];
+				ctlr->rfdl = NEXT(ctlr->rfdl, Nrfd);
+				ctlr->rfd[ctlr->rfdl].field |= RfdS;
+				rfd->field &= ~RfdS;
+
+				rfd = &ctlr->rfd[ctlr->rfdx];
+			}
+			status &= ~StatFR;
+		}
+
+		if(status & StatRNR){
+			while(csr8r(ctlr, CommandR))
+				;
+			csr8w(ctlr, CommandR, RUresume);
+
+			status &= ~StatRNR;
+		}
+
+		if(status & StatCNA){
+			while(bp = ctlr->cbqhead){
+				if((((Cb*)bp->rp)->command & CbC) == 0)
+					break;
+				ctlr->cbqhead = bp->next;
+				freeb(bp);
+			}
+			custart(ctlr);
+
+			status &= ~StatCNA;
+		}
+
+		if(status & (StatCX|StatFR|StatCNA|StatRNR|StatMDI|StatSWI))
+			panic("%s#%d: status %uX\n", ctlr->type,  ctlr->ctlrno, status);
+	}
+}
+
+static void
+ctlrinit(Ctlr* ctlr)
+{
+	int i;
+	Rfd *rfd;
+	ulong link;
+
+	link = NullPointer;
+	for(i = Nrfd-1; i >= 0; i--){
+		rfd = &ctlr->rfd[i];
+
+		rfd->field = 0;
+		rfd->link = link;
+		link = PADDR(rfd);
+		rfd->rbd = NullPointer;
+		rfd->count = 0;
+		rfd->size = sizeof(Etherpkt);
+	}
+	ctlr->rfd[Nrfd-1].link = PADDR(&ctlr->rfd[0]);
+
+	ctlr->rfdl = 0;
+	ctlr->rfd[0].field |= RfdS;
+	ctlr->rfdx = 2;
+
+	memmove(ctlr->configdata, configdata, sizeof(configdata));
+}
+
+static int
+miir(Ctlr* ctlr, int phyadd, int regadd)
+{
+	int mcr, timo;
+
+	csr32w(ctlr, Mcr, MDIread|(phyadd<<21)|(regadd<<16));
+	mcr = 0;
+	for(timo = 64; timo; timo--){
+		mcr = csr32r(ctlr, Mcr);
+		if(mcr & MDIready)
+			break;
+		microdelay(1);
+	}
+
+	if(mcr & MDIready)
+		return mcr & 0xFFFF;
+
+	return -1;
+}
+
+static int
+miiw(Ctlr* ctlr, int phyadd, int regadd, int data)
+{
+	int mcr, timo;
+
+	csr32w(ctlr, Mcr, MDIwrite|(phyadd<<21)|(regadd<<16)|(data & 0xFFFF));
+	mcr = 0;
+	for(timo = 64; timo; timo--){
+		mcr = csr32r(ctlr, Mcr);
+		if(mcr & MDIready)
+			break;
+		microdelay(1);
+	}
+
+	if(mcr & MDIready)
+		return 0;
+
+	return -1;
+}
+
+static int
+hy93c46r(Ctlr* ctlr, int r)
+{
+	int data, i, op, size;
+
+	/*
+	 * Hyundai HY93C46 or equivalent serial EEPROM.
+	 * This sequence for reading a 16-bit register 'r'
+	 * in the EEPROM is taken straight from Section
+	 * 3.3.4.2 of the Intel 82557 User's Guide.
+	 */
+reread:
+	csr16w(ctlr, Ecr, EEcs);
+	op = EEstart|EEread;
+	for(i = 2; i >= 0; i--){
+		data = (((op>>i) & 0x01)<<2)|EEcs;
+		csr16w(ctlr, Ecr, data);
+		csr16w(ctlr, Ecr, data|EEsk);
+		microdelay(1);
+		csr16w(ctlr, Ecr, data);
+		microdelay(1);
+	}
+
+	/*
+	 * First time through must work out the EEPROM size.
+	 */
+	if((size = ctlr->eepromsz) == 0)
+		size = 8;
+
+	for(size = size-1; size >= 0; size--){
+		data = (((r>>size) & 0x01)<<2)|EEcs;
+		csr16w(ctlr, Ecr, data);
+		csr16w(ctlr, Ecr, data|EEsk);
+		delay(1);
+		csr16w(ctlr, Ecr, data);
+		microdelay(1);
+		if(!(csr16r(ctlr, Ecr) & EEdo))
+			break;
+	}
+
+	data = 0;
+	for(i = 15; i >= 0; i--){
+		csr16w(ctlr, Ecr, EEcs|EEsk);
+		microdelay(1);
+		if(csr16r(ctlr, Ecr) & EEdo)
+			data |= (1<<i);
+		csr16w(ctlr, Ecr, EEcs);
+		microdelay(1);
+	}
+
+	csr16w(ctlr, Ecr, 0);
+
+	if(ctlr->eepromsz == 0){
+		ctlr->eepromsz = 8-size;
+		ctlr->eeprom = malloc((1<<ctlr->eepromsz)*sizeof(ushort));
+		goto reread;
+	}
+
+	return data;
+}
+
+static void
+i82557pci(void)
+{
+	Pcidev *p;
+	Ctlr *ctlr;
+
+	p = nil;
+	while(p = pcimatch(p, 0x8086, 0)){
+		switch(p->did){
+		default:
+			continue;
+		case 0x1031:		/* Intel 82562EM */
+		case 0x1050:		/* Intel 82562EZ */
+		case 0x1039:		/* Intel 82801BD PRO/100 VE */
+		case 0x103A:		/* Intel 82562 PRO/100 VE */
+		case 0x1064:		/* Intel 82562 PRO/100 VE */
+		case 0x2449:		/* Intel 82562ET */
+		case 0x1209:		/* Intel 82559ER */
+		case 0x1229:		/* Intel 8255[789] */
+		case 0x1030:		/* Intel 82559 InBusiness 10/100  */
+			break;
+		}
+
+		/*
+		 * bar[0] is the memory-mapped register address (4KB),
+		 * bar[1] is the I/O port register address (32 bytes) and
+		 * bar[2] is for the flash ROM (1MB).
+		 */
+		ctlr = malloc(sizeof(Ctlr));
+		ctlr->port = p->mem[1].bar & ~0x01;
+		ctlr->pcidev = p;
+
+		if(ctlrhead != nil)
+			ctlrtail->next = ctlr;
+		else
+			ctlrhead = ctlr;
+		ctlrtail = ctlr;
+
+		pcisetbme(p);
+	}
+}
+
+static void
+detach(Ether* ether)
+{
+	Ctlr *ctlr;
+
+	ctlr = ether->ctlr;
+
+	csr32w(ctlr, Port, 0);
+	delay(1);
+
+	while(csr8r(ctlr, CommandR))
+		;
+}
+
+static int
+scanphy(Ctlr* ctlr)
+{
+	int i, oui, x;
+
+	for(i = 0; i < 32; i++){
+		if((oui = miir(ctlr, i, 2)) == -1 || oui == 0 || oui == 0xFFFF)
+			continue;
+		oui <<= 6;
+		x = miir(ctlr, i, 3);
+		oui |= x>>10;
+		//print("phy%d: oui %uX reg1 %uX\n", i, oui, miir(ctlr, i, 1));
+
+		if(oui == 0xAA00)
+			ctlr->eeprom[6] = 0x07<<8;
+		else if(oui == 0x80017){
+			if(x & 0x01)
+				ctlr->eeprom[6] = 0x0A<<8;
+			else
+				ctlr->eeprom[6] = 0x04<<8;
+		}
+		return i;
+	}
+	return -1;
+}
+
+int
+i82557reset(Ether* ether)
+{
+	int anar, anlpar, bmcr, bmsr, force, i, phyaddr, x;
+	unsigned short sum;
+	Block *bp;
+	uchar ea[Eaddrlen];
+	Ctlr *ctlr;
+	Cb *cb;
+
+
+	if(ctlrhead == nil)
+		i82557pci();
+
+	/*
+	 * Any adapter matches if no ether->port is supplied,
+	 * otherwise the ports must match.
+	 */
+	for(ctlr = ctlrhead; ctlr != nil; ctlr = ctlr->next){
+		if(ctlr->active)
+			continue;
+		if(ether->port == 0 || ether->port == ctlr->port){
+			ctlr->active = 1;
+			break;
+		}
+	}
+	if(ctlr == nil)
+		return -1;
+
+	/*
+	 * Initialise the Ctlr structure.
+	 * Perform a software reset after which need to ensure busmastering
+	 * is still enabled. The EtherExpress PRO/100B appears to leave
+	 * the PCI configuration alone (see the 'To do' list above) so punt
+	 * for now.
+	 * Load the RUB and CUB registers for linear addressing (0).
+	 */
+	ether->ctlr = ctlr;
+	ether->port = ctlr->port;
+	ether->irq = ctlr->pcidev->intl;
+	ether->tbdf = ctlr->pcidev->tbdf;
+	ctlr->ctlrno = ether->ctlrno;
+	ctlr->type = ether->type;
+
+	csr32w(ctlr, Port, 0);
+	delay(1);
+
+	while(csr8r(ctlr, CommandR))
+		;
+	csr32w(ctlr, Pointer, 0);
+	csr8w(ctlr, CommandR, LoadRUB);
+	while(csr8r(ctlr, CommandR))
+		;
+	csr8w(ctlr, CommandR, LoadCUB);
+
+	/*
+	 * Initialise the action and receive frame areas.
+	 */
+	ctlrinit(ctlr);
+
+	/*
+	 * Read the EEPROM.
+	 * Do a dummy read first to get the size
+	 * and allocate ctlr->eeprom.
+	 */
+	hy93c46r(ctlr, 0);
+	sum = 0;
+	for(i = 0; i < (1<<ctlr->eepromsz); i++){
+		x = hy93c46r(ctlr, i);
+		ctlr->eeprom[i] = x;
+		sum += x;
+	}
+	if(sum != 0xBABA)
+		print("#l%d: EEPROM checksum - 0x%4.4uX\n", ether->ctlrno, sum);
+
+	/*
+	 * Eeprom[6] indicates whether there is a PHY and whether
+	 * it's not 10Mb-only, in which case use the given PHY address
+	 * to set any PHY specific options and determine the speed.
+	 * Unfortunately, sometimes the EEPROM is blank except for
+	 * the ether address and checksum; in this case look at the
+	 * controller type and if it's am 82558 or 82559 it has an
+	 * embedded PHY so scan for that.
+	 * If no PHY, assume 82503 (serial) operation.
+	 */
+	if((ctlr->eeprom[6] & 0x1F00) && !(ctlr->eeprom[6] & 0x8000))
+		phyaddr = ctlr->eeprom[6] & 0x00FF;
+	else
+	switch(ctlr->pcidev->rid){
+	case 0x01:			/* 82557 A-step */
+	case 0x02:			/* 82557 B-step */
+	case 0x03:			/* 82557 C-step */
+	default:
+		phyaddr = -1;
+		break;
+	case 0x04:			/* 82558 A-step */
+	case 0x05:			/* 82558 B-step */
+	case 0x06:			/* 82559 A-step */
+	case 0x07:			/* 82559 B-step */
+	case 0x08:			/* 82559 C-step */
+	case 0x09:			/* 82559ER A-step */
+		phyaddr = scanphy(ctlr);
+		break;
+	}
+	if(phyaddr >= 0){
+		/*
+		 * Resolve the highest common ability of the two
+		 * link partners. In descending order:
+		 *	0x0100		100BASE-TX Full Duplex
+		 *	0x0200		100BASE-T4
+		 *	0x0080		100BASE-TX
+		 *	0x0040		10BASE-T Full Duplex
+		 *	0x0020		10BASE-T
+		 */
+		anar = miir(ctlr, phyaddr, 0x04);
+		anlpar = miir(ctlr, phyaddr, 0x05) & 0x03E0;
+		anar &= anlpar;
+		bmcr = 0;
+		if(anar & 0x380)
+			bmcr = 0x2000;
+		if(anar & 0x0140)
+			bmcr |= 0x0100;
+
+		switch((ctlr->eeprom[6]>>8) & 0x001F){
+
+		case 0x04:				/* DP83840 */
+		case 0x0A:				/* DP83840A */
+			/*
+			 * The DP83840[A] requires some tweaking for
+			 * reliable operation.
+			 * The manual says bit 10 should be unconditionally
+			 * set although it supposedly only affects full-duplex
+			 * operation (an & 0x0140).
+			 */
+			x = miir(ctlr, phyaddr, 0x17) & ~0x0520;
+			x |= 0x0420;
+			for(i = 0; i < ether->nopt; i++){
+				if(cistrcmp(ether->opt[i], "congestioncontrol"))
+					continue;
+				x |= 0x0100;
+				break;
+			}
+			miiw(ctlr, phyaddr, 0x17, x);
+
+			/*
+			 * If the link partner can't autonegotiate, determine
+			 * the speed from elsewhere.
+			 */
+			if(anlpar == 0){
+				miir(ctlr, phyaddr, 0x01);
+				bmsr = miir(ctlr, phyaddr, 0x01);
+				x = miir(ctlr, phyaddr, 0x19);
+				if((bmsr & 0x0004) && !(x & 0x0040))
+					bmcr = 0x2000;
+			}
+			break;
+
+		case 0x07:				/* Intel 82555 */
+			/*
+			 * Auto-negotiation may fail if the other end is
+			 * a DP83840A and the cable is short.
+			 */
+			bmsr = miir(ctlr, phyaddr, 0x01);
+			if((miir(ctlr, phyaddr, 0) & 0x1000) && !(bmsr & 0x0020)){
+				miiw(ctlr, phyaddr, 0x1A, 0x2010);
+				x = miir(ctlr, phyaddr, 0);
+				miiw(ctlr, phyaddr, 0, 0x0200|x);
+				for(i = 0; i < 3000; i++){
+					delay(1);
+					if(miir(ctlr, phyaddr, 0x01) & 0x0020)
+						break;
+				}
+				miiw(ctlr, phyaddr, 0x1A, 0x2000);
+					
+				anar = miir(ctlr, phyaddr, 0x04);
+				anlpar = miir(ctlr, phyaddr, 0x05) & 0x03E0;
+				anar &= anlpar;
+				bmcr = 0;
+				if(anar & 0x380)
+					bmcr = 0x2000;
+				if(anar & 0x0140)
+					bmcr |= 0x0100;
+			}
+			break;
+		}
+
+		/*
+		 * Force speed and duplex if no auto-negotiation.
+		 */
+		if(anlpar == 0){
+			force = 0;
+			for(i = 0; i < ether->nopt; i++){
+				if(cistrcmp(ether->opt[i], "fullduplex") == 0){
+					force = 1;
+					bmcr |= 0x0100;
+					ctlr->configdata[19] |= 0x40;
+				}
+				else if(cistrcmp(ether->opt[i], "speed") == 0){
+					force = 1;
+					x = strtol(&ether->opt[i][6], 0, 0);
+					if(x == 10)
+						bmcr &= ~0x2000;
+					else if(x == 100)
+						bmcr |= 0x2000;
+					else
+						force = 0;
+				}
+			}
+			if(force)
+				miiw(ctlr, phyaddr, 0x00, bmcr);
+		}
+
+		ctlr->configdata[8] = 1;
+		ctlr->configdata[15] &= ~0x80;
+	}
+	else{
+		ctlr->configdata[8] = 0;
+		ctlr->configdata[15] |= 0x80;
+	}
+
+	/*
+	 * Load the chip configuration
+	 */
+	configure(ether, 0);
+
+	/*
+	 * Check if the adapter's station address is to be overridden.
+	 * If not, read it from the EEPROM and set in ether->ea prior to loading
+	 * the station address with the Individual Address Setup command.
+	 */
+	memset(ea, 0, Eaddrlen);
+	if(memcmp(ea, ether->ea, Eaddrlen) == 0){
+		for(i = 0; i < Eaddrlen/2; i++){
+			x = ctlr->eeprom[i];
+			ether->ea[2*i] = x & 0xFF;
+			ether->ea[2*i+1] = (x>>8) & 0xFF;
+		}
+	}
+
+	bp = allocb(sizeof(Cb));
+	cb = (Cb*)bp->rp;
+	bp->wp += sizeof(Cb);
+
+	cb->command = CbIAS;
+	cb->link = NullPointer;
+	memmove(cb->data, ether->ea, Eaddrlen);
+	action(ctlr, bp);
+
+	/*
+	 * Linkage to the generic ethernet driver.
+	 */
+	ether->attach = attach;
+	ether->transmit = transmit;
+	ether->interrupt = interrupt;
+	ether->detach = detach;
+
+	return 0;
+}
--- /dev/null
+++ b/os/boot/pc/ether82563.c
@@ -1,0 +1,977 @@
+/*
+ * bootstrap driver for
+ * Intel 82563, 82571, 82573 Gigabit Ethernet PCI-Express Controllers
+ */
+#include "u.h"
+#include "lib.h"
+#include "mem.h"
+#include "dat.h"
+#include "fns.h"
+#include "io.h"
+
+#include "etherif.h"
+
+/* compatibility with cpu kernels */
+#define iallocb allocb
+#ifndef CACHELINESZ
+#define CACHELINESZ	32		/* pentium & later */
+#endif
+
+/* from pci.c */
+enum
+{					/* command register pcidev->pcr */
+	IOen		= 1<<0,
+	MEMen		= 1<<1,
+	MASen		= 1<<2,
+	MemWrInv	= 1<<4,
+	PErrEn		= 1<<6,
+	SErrEn		= 1<<8,
+};
+
+/*
+ * these are in the order they appear in the manual, not numeric order.
+ * It was too hard to find them in the book. Ref 21489, rev 2.6
+ */
+
+enum {
+	/* General */
+
+	Ctrl		= 0x00000000,	/* Device Control */
+	Status		= 0x00000008,	/* Device Status */
+	Eec		= 0x00000010,	/* EEPROM/Flash Control/Data */
+	Eerd		= 0x00000014,	/* EEPROM Read */
+	Ctrlext		= 0x00000018,	/* Extended Device Control */
+	Fla		= 0x0000001c,	/* Flash Access */
+	Mdic		= 0x00000020,	/* MDI Control */
+	Seresctl	= 0x00000024,	/* Serdes ana */
+	Fcal		= 0x00000028,	/* Flow Control Address Low */
+	Fcah		= 0x0000002C,	/* Flow Control Address High */
+	Fct		= 0x00000030,	/* Flow Control Type */
+	Kumctrlsta	= 0x00000034,	/* Kumeran Controll and Status Register */
+	Vet		= 0x00000038,	/* VLAN EtherType */
+	Fcttv		= 0x00000170,	/* Flow Control Transmit Timer Value */
+	Txcw		= 0x00000178,	/* Transmit Configuration Word */
+	Rxcw		= 0x00000180,	/* Receive Configuration Word */
+	Ledctl		= 0x00000E00,	/* LED control */
+	Pba		= 0x00001000,	/* Packet Buffer Allocation */
+
+	/* Interrupt */
+
+	Icr		= 0x000000C0,	/* Interrupt Cause Read */
+	Ics		= 0x000000C8,	/* Interrupt Cause Set */
+	Ims		= 0x000000D0,	/* Interrupt Mask Set/Read */
+	Imc		= 0x000000D8,	/* Interrupt mask Clear */
+	Iam		= 0x000000E0,	/* Interrupt acknowledge Auto Mask */
+
+	/* Receive */
+
+	Rctl		= 0x00000100,	/* Receive Control */
+	Ert		= 0x00002008,	/* Early Receive Threshold (573[EVL] only) */
+	Fcrtl		= 0x00002160,	/* Flow Control RX Threshold Low */
+	Fcrth		= 0x00002168,	/* Flow Control Rx Threshold High */
+	Psrctl		= 0x00002170,	/* Packet Split Receive Control */
+	Rdbal		= 0x00002800,	/* Rdesc Base Address Low Queue 0 */
+	Rdbah		= 0x00002804,	/* Rdesc Base Address High Queue 0 */
+	Rdlen		= 0x00002808,	/* Receive Descriptor Length Queue 0 */
+	Rdh		= 0x00002810,	/* Receive Descriptor Head Queue 0 */
+	Rdt		= 0x00002818,	/* Receive Descriptor Tail Queue 0 */
+	Rdtr		= 0x00002820,	/* Receive Descriptor Timer Ring */
+	Rxdctl		= 0x00002828,	/* Receive Descriptor Control */
+	Radv		= 0x0000282C,	/* Receive Interrupt Absolute Delay Timer */
+	Rdbal1		= 0x00002900,	/* Rdesc Base Address Low Queue 1 */
+	Rdbah1		= 0x00002804,	/* Rdesc Base Address High Queue 1 */
+	Rdlen1		= 0x00002908,	/* Receive Descriptor Length Queue 1 */
+	Rdh1		= 0x00002910,	/* Receive Descriptor Head Queue 1 */
+	Rdt1		= 0x00002918,	/* Receive Descriptor Tail Queue 1 */
+	Rxdctl1		= 0x00002928,	/* Receive Descriptor Control Queue 1 */
+	Rsrpd		= 0x00002c00,	/* Receive Small Packet Detect */
+	Raid		= 0x00002c08,	/* Receive ACK interrupt delay */
+	Cpuvec		= 0x00002c10,	/* CPU Vector */
+	Rxcsum		= 0x00005000,	/* Receive Checksum Control */
+	Rfctl		= 0x00005008,	/* Receive Filter Control */
+	Mta		= 0x00005200,	/* Multicast Table Array */
+	Ral		= 0x00005400,	/* Receive Address Low */
+	Rah		= 0x00005404,	/* Receive Address High */
+	Vfta		= 0x00005600,	/* VLAN Filter Table Array */
+	Mrqc		= 0x00005818,	/* Multiple Receive Queues Command */
+	Rssim		= 0x00005864,	/* RSS Interrupt Mask */
+	Rssir		= 0x00005868,	/* RSS Interrupt Request */
+	Reta		= 0x00005c00,	/* Redirection Table */
+	Rssrk		= 0x00005c80,	/* RSS Random Key */
+
+	/* Transmit */
+
+	Tctl		= 0x00000400,	/* Transmit Control */
+	Tipg		= 0x00000410,	/* Transmit IPG */
+	Tdbal		= 0x00003800,	/* Tdesc Base Address Low */
+	Tdbah		= 0x00003804,	/* Tdesc Base Address High */
+	Tdlen		= 0x00003808,	/* Transmit Descriptor Length */
+	Tdh		= 0x00003810,	/* Transmit Descriptor Head */
+	Tdt		= 0x00003818,	/* Transmit Descriptor Tail */
+	Tidv		= 0x00003820,	/* Transmit Interrupt Delay Value */
+	Txdctl		= 0x00003828,	/* Transmit Descriptor Control */
+	Tadv		= 0x0000382C,	/* Transmit Interrupt Absolute Delay Timer */
+	Tarc0		= 0x00003840,	/* Transmit Arbitration Counter Queue 0 */
+	Tdbal1		= 0x00003900,	/* Transmit Descriptor Base Low Queue 1 */
+	Tdbah1		= 0x00003904,	/* Transmit Descriptor Base High Queue 1 */
+	Tdlen1		= 0x00003908,	/* Transmit Descriptor Length Queue 1 */
+	Tdh1		= 0x00003910,	/* Transmit Descriptor Head Queue 1 */
+	Tdt1		= 0x00003918,	/* Transmit Descriptor Tail Queue 1 */
+	Txdctl1		= 0x00003928,	/* Transmit Descriptor Control 1 */
+	Tarc1		= 0x00003940,	/* Transmit Arbitration Counter Queue 1 */
+
+	/* Statistics */
+
+	Statistics	= 0x00004000,	/* Start of Statistics Area */
+	Gorcl		= 0x88/4,	/* Good Octets Received Count */
+	Gotcl		= 0x90/4,	/* Good Octets Transmitted Count */
+	Torl		= 0xC0/4,	/* Total Octets Received */
+	Totl		= 0xC8/4,	/* Total Octets Transmitted */
+	Nstatistics	= 64,
+
+};
+
+enum {					/* Ctrl */
+	GIOmd		= 1<<2,		/* BIO master disable */
+	Lrst		= 1<<3,		/* link reset */
+	Slu		= 1<<6,		/* Set Link Up */
+	SspeedMASK	= 3<<8,		/* Speed Selection */
+	SspeedSHIFT	= 8,
+	Sspeed10	= 0x00000000,	/* 10Mb/s */
+	Sspeed100	= 0x00000100,	/* 100Mb/s */
+	Sspeed1000	= 0x00000200,	/* 1000Mb/s */
+	Frcspd		= 1<<11,	/* Force Speed */
+	Frcdplx		= 1<<12,	/* Force Duplex */
+	SwdpinsloMASK	= 0x003C0000,	/* Software Defined Pins - lo nibble */
+	SwdpinsloSHIFT	= 18,
+	SwdpioloMASK	= 0x03C00000,	/* Software Defined Pins - I or O */
+	SwdpioloSHIFT	= 22,
+	Devrst		= 1<<26,	/* Device Reset */
+	Rfce		= 1<<27,	/* Receive Flow Control Enable */
+	Tfce		= 1<<28,	/* Transmit Flow Control Enable */
+	Vme		= 1<<30,	/* VLAN Mode Enable */
+	Phy_rst		= 1<<31,	/* Phy Reset */
+};
+
+enum {					/* Status */
+	Lu		= 1<<1,		/* Link Up */
+	Lanid		= 3<<2,		/* mask for Lan ID. */
+	Txoff		= 1<<4,		/* Transmission Paused */
+	Tbimode		= 1<<5,		/* TBI Mode Indication */
+	SpeedMASK	= 0x000000C0,
+	Speed10		= 0x00000000,	/* 10Mb/s */
+	Speed100	= 0x00000040,	/* 100Mb/s */
+	Speed1000	= 0x00000080,	/* 1000Mb/s */
+	Phyra		= 1<<10,	/* PHY Reset Asserted */
+	GIOme		= 1<<19,	/* GIO Master Enable Status */
+};
+
+enum {					/* Ctrl and Status */
+	Fd		= 0x00000001,	/* Full-Duplex */
+	AsdvMASK	= 0x00000300,
+	Asdv10		= 0x00000000,	/* 10Mb/s */
+	Asdv100		= 0x00000100,	/* 100Mb/s */
+	Asdv1000	= 0x00000200,	/* 1000Mb/s */
+};
+
+enum {					/* Eec */
+	Sk		= 1<<0,		/* Clock input to the EEPROM */
+	Cs		= 1<<1,		/* Chip Select */
+	Di		= 1<<2,		/* Data Input to the EEPROM */
+	Do		= 1<<3,		/* Data Output from the EEPROM */
+	Areq		= 1<<6,		/* EEPROM Access Request */
+	Agnt		= 1<<7,		/* EEPROM Access Grant */
+};
+
+enum {					/* Eerd */
+	ee_start	= 1<<0,		/* Start Read */
+	ee_done		= 1<<1,		/* Read done */
+	ee_addr		= 0xfff8<<2,	/* Read address [15:2] */
+	ee_data		= 0xffff<<16,	/* Read Data; Data returned from eeprom/nvm */
+};
+
+enum {					/* Ctrlext */
+	Asdchk		= 1<<12,	/* ASD Check */
+	Eerst		= 1<<13,	/* EEPROM Reset */
+	Spdbyps		= 1<<15,	/* Speed Select Bypass */
+};
+
+enum {					/* EEPROM content offsets */
+	Ea		= 0x00,		/* Ethernet Address */
+	Cf		= 0x03,		/* Compatibility Field */
+	Icw1		= 0x0A,		/* Initialization Control Word 1 */
+	Sid		= 0x0B,		/* Subsystem ID */
+	Svid		= 0x0C,		/* Subsystem Vendor ID */
+	Did		= 0x0D,		/* Device ID */
+	Vid		= 0x0E,		/* Vendor ID */
+	Icw2		= 0x0F,		/* Initialization Control Word 2 */
+};
+
+enum {					/* Mdic */
+	MDIdMASK	= 0x0000FFFF,	/* Data */
+	MDIdSHIFT	= 0,
+	MDIrMASK	= 0x001F0000,	/* PHY Register Address */
+	MDIrSHIFT	= 16,
+	MDIpMASK	= 0x03E00000,	/* PHY Address */
+	MDIpSHIFT	= 21,
+	MDIwop		= 0x04000000,	/* Write Operation */
+	MDIrop		= 0x08000000,	/* Read Operation */
+	MDIready	= 0x10000000,	/* End of Transaction */
+	MDIie		= 0x20000000,	/* Interrupt Enable */
+	MDIe		= 0x40000000,	/* Error */
+};
+
+enum {					/* Icr, Ics, Ims, Imc */
+	Txdw		= 0x00000001,	/* Transmit Descriptor Written Back */
+	Txqe		= 0x00000002,	/* Transmit Queue Empty */
+	Lsc		= 0x00000004,	/* Link Status Change */
+	Rxseq		= 0x00000008,	/* Receive Sequence Error */
+	Rxdmt0		= 0x00000010,	/* Rdesc Minimum Threshold Reached */
+	Rxo		= 0x00000040,	/* Receiver Overrun */
+	Rxt0		= 0x00000080,	/* Receiver Timer Interrupt */
+	Mdac		= 0x00000200,	/* MDIO Access Completed */
+	Rxcfg		= 0x00000400,	/* Receiving /C/ ordered sets */
+	Gpi0		= 0x00000800,	/* General Purpose Interrupts */
+	Gpi1		= 0x00001000,
+	Gpi2		= 0x00002000,
+	Gpi3		= 0x00004000,
+	Ack		= 0x00020000,	/* receive ACK frame */
+};
+
+enum {					/* Txcw */
+	TxcwFd		= 0x00000020,	/* Full Duplex */
+	TxcwHd		= 0x00000040,	/* Half Duplex */
+	TxcwPauseMASK	= 0x00000180,	/* Pause */
+	TxcwPauseSHIFT	= 7,
+	TxcwPs		= 1<<TxcwPauseSHIFT,	/* Pause Supported */
+	TxcwAs		= 2<<TxcwPauseSHIFT,	/* Asymmetric FC desired */
+	TxcwRfiMASK	= 0x00003000,	/* Remote Fault Indication */
+	TxcwRfiSHIFT	= 12,
+	TxcwNpr		= 0x00008000,	/* Next Page Request */
+	TxcwConfig	= 0x40000000,	/* Transmit COnfig Control */
+	TxcwAne		= 0x80000000,	/* Auto-Negotiation Enable */
+};
+
+enum {					/* Rctl */
+	Rrst		= 0x00000001,	/* Receiver Software Reset */
+	Ren		= 0x00000002,	/* Receiver Enable */
+	Sbp		= 0x00000004,	/* Store Bad Packets */
+	Upe		= 0x00000008,	/* Unicast Promiscuous Enable */
+	Mpe		= 0x00000010,	/* Multicast Promiscuous Enable */
+	Lpe		= 0x00000020,	/* Long Packet Reception Enable */
+	LbmMASK		= 0x000000C0,	/* Loopback Mode */
+	LbmOFF		= 0x00000000,	/* No Loopback */
+	LbmTBI		= 0x00000040,	/* TBI Loopback */
+	LbmMII		= 0x00000080,	/* GMII/MII Loopback */
+	LbmXCVR		= 0x000000C0,	/* Transceiver Loopback */
+	RdtmsMASK	= 0x00000300,	/* Rdesc Minimum Threshold Size */
+	RdtmsHALF	= 0x00000000,	/* Threshold is 1/2 Rdlen */
+	RdtmsQUARTER	= 0x00000100,	/* Threshold is 1/4 Rdlen */
+	RdtmsEIGHTH	= 0x00000200,	/* Threshold is 1/8 Rdlen */
+	MoMASK		= 0x00003000,	/* Multicast Offset */
+	Bam		= 0x00008000,	/* Broadcast Accept Mode */
+	BsizeMASK	= 0x00030000,	/* Receive Buffer Size */
+	Bsize2048	= 0x00000000,
+	Bsize1024	= 0x00010000,
+	Bsize512	= 0x00020000,
+	Bsize256	= 0x00030000,
+	Vfe		= 0x00040000,	/* VLAN Filter Enable */
+	Cfien		= 0x00080000,	/* Canonical Form Indicator Enable */
+	Cfi		= 0x00100000,	/* Canonical Form Indicator value */
+	Dpf		= 0x00400000,	/* Discard Pause Frames */
+	Pmcf		= 0x00800000,	/* Pass MAC Control Frames */
+	Bsex		= 0x02000000,	/* Buffer Size Extension */
+	Secrc		= 0x04000000,	/* Strip CRC from incoming packet */
+};
+
+enum {					/* Tctl */
+	Trst		= 0x00000001,	/* Transmitter Software Reset */
+	Ten		= 0x00000002,	/* Transmit Enable */
+	Psp		= 0x00000008,	/* Pad Short Packets */
+	Mulr		= 0x10000000,	/* Allow multiple concurrent requests */
+	CtMASK		= 0x00000FF0,	/* Collision Threshold */
+	CtSHIFT		= 4,
+	ColdMASK	= 0x003FF000,	/* Collision Distance */
+	ColdSHIFT	= 12,
+	Swxoff		= 0x00400000,	/* Sofware XOFF Transmission */
+	Pbe		= 0x00800000,	/* Packet Burst Enable */
+	Rtlc		= 0x01000000,	/* Re-transmit on Late Collision */
+	Nrtu		= 0x02000000,	/* No Re-transmit on Underrrun */
+};
+
+enum {					/* [RT]xdctl */
+	PthreshMASK	= 0x0000003F,	/* Prefetch Threshold */
+	PthreshSHIFT	= 0,
+	HthreshMASK	= 0x00003F00,	/* Host Threshold */
+	HthreshSHIFT	= 8,
+	WthreshMASK	= 0x003F0000,	/* Writebacj Threshold */
+	WthreshSHIFT	= 16,
+	Gran		= 0x01000000,	/* Granularity */
+};
+
+enum {					/* Rxcsum */
+	PcssMASK	= 0x000000FF,	/* Packet Checksum Start */
+	PcssSHIFT	= 0,
+	Ipofl		= 0x00000100,	/* IP Checksum Off-load Enable */
+	Tuofl		= 0x00000200,	/* TCP/UDP Checksum Off-load Enable */
+};
+
+typedef struct Rdesc {			/* Receive Descriptor */
+	uint	addr[2];
+	ushort	length;
+	ushort	checksum;
+	uchar	status;
+	uchar	errors;
+	ushort	special;
+} Rdesc;
+
+enum {					/* Rdesc status */
+	Rdd		= 0x01,		/* Descriptor Done */
+	Reop		= 0x02,		/* End of Packet */
+	Ixsm		= 0x04,		/* Ignore Checksum Indication */
+	Vp		= 0x08,		/* Packet is 802.1Q (matched VET) */
+	Tcpcs		= 0x20,		/* TCP Checksum Calculated on Packet */
+	Ipcs		= 0x40,		/* IP Checksum Calculated on Packet */
+	Pif		= 0x80,		/* Passed in-exact filter */
+};
+
+enum {					/* Rdesc errors */
+	Ce		= 0x01,		/* CRC Error or Alignment Error */
+	Se		= 0x02,		/* Symbol Error */
+	Seq		= 0x04,		/* Sequence Error */
+	Cxe		= 0x10,		/* Carrier Extension Error */
+	Tcpe		= 0x20,		/* TCP/UDP Checksum Error */
+	Ipe		= 0x40,		/* IP Checksum Error */
+	Rxe		= 0x80,		/* RX Data Error */
+};
+
+typedef struct Tdesc {			/* Legacy+Normal Transmit Descriptor */
+	uint	addr[2];
+	uint	control;		/* varies with descriptor type */
+	uint	status;			/* varies with descriptor type */
+} Tdesc;
+
+enum {					/* Tdesc control */
+	LenMASK		= 0x000FFFFF,	/* Data/Packet Length Field */
+	LenSHIFT	= 0,
+	DtypeCD		= 0x00000000,	/* Data Type 'Context Descriptor' */
+	DtypeDD		= 0x00100000,	/* Data Type 'Data Descriptor' */
+	PtypeTCP	= 0x01000000,	/* TCP/UDP Packet Type (CD) */
+	Teop		= 0x01000000,	/* End of Packet (DD) */
+	PtypeIP		= 0x02000000,	/* IP Packet Type (CD) */
+	Ifcs		= 0x02000000,	/* Insert FCS (DD) */
+	Tse		= 0x04000000,	/* TCP Segmentation Enable */
+	Rs		= 0x08000000,	/* Report Status */
+	Rps		= 0x10000000,	/* Report Status Sent */
+	Dext		= 0x20000000,	/* Descriptor Extension */
+	Vle		= 0x40000000,	/* VLAN Packet Enable */
+	Ide		= 0x80000000,	/* Interrupt Delay Enable */
+};
+
+enum {					/* Tdesc status */
+	Tdd		= 0x00000001,	/* Descriptor Done */
+	Ec		= 0x00000002,	/* Excess Collisions */
+	Lc		= 0x00000004,	/* Late Collision */
+	Tu		= 0x00000008,	/* Transmit Underrun */
+	CssMASK		= 0x0000FF00,	/* Checksum Start Field */
+	CssSHIFT	= 8,
+};
+
+enum {
+	Nrdesc		= 128,		/* multiple of 8 */
+	Ntdesc		= 128,		/* multiple of 8 */
+};
+
+enum {
+	i82563,
+	i82571,
+	i82573,
+};
+
+static char *tname[] = {
+	"i82563",
+	"i82571",
+	"i82573",
+};
+
+#define Type	tname[ctlr->type]
+
+typedef struct Ctlr Ctlr;
+struct Ctlr {
+	int	port;
+	Pcidev	*pcidev;
+	Ctlr	*next;
+	int	active;
+	int	cls;
+	ushort	eeprom[0x40];
+	uchar	ra[Eaddrlen];		/* receive address */
+	int	type;
+
+	int*	nic;
+	Lock	imlock;
+	int	im;			/* interrupt mask */
+
+	Lock	slock;
+	uint	statistics[Nstatistics];
+
+	Rdesc	*rdba;			/* receive descriptor base address */
+	Block	**rb;			/* receive buffers */
+	int	rdh;			/* receive descriptor head */
+	int	rdt;			/* receive descriptor tail */
+
+	Tdesc	*tdba;			/* transmit descriptor base address */
+	Lock	tdlock;
+	Block	**tb;			/* transmit buffers */
+	int	tdh;			/* transmit descriptor head */
+	int	tdt;			/* transmit descriptor tail */
+
+	int	txcw;
+	int	fcrtl;
+	int	fcrth;
+
+	/* bootstrap goo */
+	Block	*bqhead;		/* transmission queue */
+	Block	*bqtail;
+};
+
+static Ctlr	*ctlrhead;
+static Ctlr	*ctlrtail;
+
+#define csr32r(c, r)	(*((c)->nic+((r)/4)))
+#define csr32w(c, r, v)	(*((c)->nic+((r)/4)) = (v))
+
+static void
+i82563im(Ctlr* ctlr, int im)
+{
+	ilock(&ctlr->imlock);
+	ctlr->im |= im;
+	csr32w(ctlr, Ims, ctlr->im);
+	iunlock(&ctlr->imlock);
+}
+
+static void
+i82563attach(Ether* edev)
+{
+	int ctl;
+	Ctlr *ctlr;
+
+	ctlr = edev->ctlr;
+	i82563im(ctlr, 0);
+	ctl = csr32r(ctlr, Rctl)|Ren;
+	csr32w(ctlr, Rctl, ctl);
+	ctl = csr32r(ctlr, Tctl)|Ten;
+	csr32w(ctlr, Tctl, ctl);
+}
+
+
+static void
+txstart(Ether *edev)
+{
+	int tdh, tdt;
+	Ctlr *ctlr = edev->ctlr;
+	Block *bp;
+	Tdesc *tdesc;
+
+	/*
+	 * Try to fill the ring back up, moving buffers from the transmit q.
+	 */
+	tdh = PREV(ctlr->tdh, Ntdesc);
+	for(tdt = ctlr->tdt; tdt != tdh; tdt = NEXT(tdt, Ntdesc)){
+		/* pull off the head of the transmission queue */
+		if((bp = ctlr->bqhead) == nil)	/* was qget(edev->oq) */
+			break;
+		ctlr->bqhead = bp->next;
+		if (ctlr->bqtail == bp)
+			ctlr->bqtail = nil;
+
+		/* set up a descriptor for it */
+		tdesc = &ctlr->tdba[tdt];
+		tdesc->addr[0] = PCIWADDR(bp->rp);
+		tdesc->addr[1] = 0;
+		tdesc->control = /* Ide | */ Rs | Ifcs | Teop | BLEN(bp);
+
+		ctlr->tb[tdt] = bp;
+	}
+	ctlr->tdt = tdt;
+	csr32w(ctlr, Tdt, tdt);
+	i82563im(ctlr, Txdw);
+}
+
+static Block *
+fromringbuf(Ether *ether)
+{
+	RingBuf *tb = &ether->tb[ether->ti];
+	Block *bp = allocb(tb->len);
+
+	memmove(bp->wp, tb->pkt, tb->len);
+	memmove(bp->wp+Eaddrlen, ether->ea, Eaddrlen);
+	bp->wp += tb->len;
+	return bp;
+}
+
+static void
+i82563transmit(Ether* edev)
+{
+	Block *bp;
+	Ctlr *ctlr;
+	Tdesc *tdesc;
+	RingBuf *tb;
+	int tdh;
+
+	ctlr = edev->ctlr;
+	ilock(&ctlr->tdlock);
+
+	/*
+	 * Free any completed packets
+	 * - try to get the soft tdh to catch the tdt;
+	 * - if the packet had an underrun bump the threshold
+	 *   - the Tu bit doesn't seem to ever be set, perhaps
+	 *     because Rs mode is used?
+	 */
+	tdh = ctlr->tdh;
+	for(;;){
+		tdesc = &ctlr->tdba[tdh];
+		if(!(tdesc->status & Tdd))
+			break;
+		if(ctlr->tb[tdh] != nil){
+			freeb(ctlr->tb[tdh]);
+			ctlr->tb[tdh] = nil;
+		}
+		tdesc->status = 0;
+		tdh = NEXT(tdh, Ntdesc);
+	}
+	ctlr->tdh = tdh;
+
+	/* copy packets from the software RingBuf to the transmission q */
+	while((tb = &edev->tb[edev->ti])->owner == Interface){
+		bp = fromringbuf(edev);
+//		print("#l%d: tx %d %E %E\n", edev->ctlrno, edev->ti, bp->rp,
+//			bp->rp+6);
+
+		if(ctlr->bqhead)
+			ctlr->bqtail->next = bp;
+		else
+			ctlr->bqhead = bp;
+		ctlr->bqtail = bp;
+
+		txstart(edev);		/* kick transmitter */
+		tb->owner = Host;	/* give descriptor back */
+		edev->ti = NEXT(edev->ti, edev->ntb);
+	}
+	iunlock(&ctlr->tdlock);
+}
+
+static void
+i82563replenish(Ctlr* ctlr)
+{
+	int rdt;
+	Block *bp;
+	Rdesc *rdesc;
+
+	rdt = ctlr->rdt;
+	while(NEXT(rdt, Nrdesc) != ctlr->rdh){
+		rdesc = &ctlr->rdba[rdt];
+		if(ctlr->rb[rdt] != nil){
+			/* nothing to do */
+		}
+		else if((bp = iallocb(2048)) != nil){
+			ctlr->rb[rdt] = bp;
+			rdesc->addr[0] = PCIWADDR(bp->rp);
+			rdesc->addr[1] = 0;
+		}
+		else
+			break;
+		rdesc->status = 0;
+
+		rdt = NEXT(rdt, Nrdesc);
+	}
+	ctlr->rdt = rdt;
+	csr32w(ctlr, Rdt, rdt);
+}
+
+static void
+toringbuf(Ether *ether, Block *bp)
+{
+	RingBuf *rb = &ether->rb[ether->ri];
+
+	if (rb->owner == Interface) {
+		rb->len = BLEN(bp);
+		memmove(rb->pkt, bp->rp, rb->len);
+		rb->owner = Host;
+		ether->ri = NEXT(ether->ri, ether->nrb);
+	} else if (debug)
+		print("#l%d: toringbuf: dropping packets @ ri %d\n",
+			ether->ctlrno, ether->ri);
+}
+
+static void
+i82563interrupt(Ureg*, void* arg)
+{
+	int icr, im, rdh, txdw = 0;
+	Block *bp;
+	Ctlr *ctlr;
+	Ether *edev;
+	Rdesc *rdesc;
+
+	edev = arg;
+	ctlr = edev->ctlr;
+
+	ilock(&ctlr->imlock);
+	csr32w(ctlr, Imc, ~0);
+	im = ctlr->im;
+
+	for(icr = csr32r(ctlr, Icr); icr & ctlr->im; icr = csr32r(ctlr, Icr)){
+		if(icr & (Rxseq|Lsc)){
+			/* should be more here */
+		}
+
+		rdh = ctlr->rdh;
+		for (;;) {
+			rdesc = &ctlr->rdba[rdh];
+			if(!(rdesc->status & Rdd))
+				break;
+			if ((rdesc->status & Reop) && rdesc->errors == 0) {
+				bp = ctlr->rb[rdh];
+				if(0 && memcmp(bp->rp, broadcast, 6) != 0)
+					print("#l%d: rx %d %E %E %d\n",
+						edev->ctlrno, rdh, bp->rp,
+						bp->rp+6, rdesc->length);
+				ctlr->rb[rdh] = nil;
+				bp->wp += rdesc->length;
+				toringbuf(edev, bp);
+				freeb(bp);
+			} else if (rdesc->status & Reop && rdesc->errors)
+				print("%s: input packet error 0x%ux\n",
+					Type, rdesc->errors);
+			rdesc->status = 0;
+			rdh = NEXT(rdh, Nrdesc);
+		}
+		ctlr->rdh = rdh;
+		if(icr & Rxdmt0)
+			i82563replenish(ctlr);
+		if(icr & Txdw){
+			im &= ~Txdw;
+			txdw++;
+		}
+	}
+	ctlr->im = im;
+	csr32w(ctlr, Ims, im);
+	iunlock(&ctlr->imlock);
+	if(txdw)
+		i82563transmit(edev);
+}
+
+static void
+i82563init(Ether* edev)
+{
+	int csr, i, r;
+	Ctlr *ctlr;
+
+	ctlr = edev->ctlr;
+	csr = edev->ea[3]<<24 | edev->ea[2]<<16 | edev->ea[1]<<8 | edev->ea[0];
+	csr32w(ctlr, Ral, csr);
+	csr = 0x80000000 | edev->ea[5]<<8 | edev->ea[4];
+	csr32w(ctlr, Rah, csr);
+	for (i = 1; i < 16; i++) {
+		csr32w(ctlr, Ral+i*8, 0);
+		csr32w(ctlr, Rah+i*8, 0);
+	}
+	for(i = 0; i < 128; i++)
+		csr32w(ctlr, Mta+i*4, 0);
+	csr32w(ctlr, Rctl, 0);
+	ctlr->rdba = xspanalloc(Nrdesc*sizeof(Rdesc), 256, 0);
+	csr32w(ctlr, Rdbal, PCIWADDR(ctlr->rdba));
+	csr32w(ctlr, Rdbah, 0);
+	csr32w(ctlr, Rdlen, Nrdesc*sizeof(Rdesc));
+	ctlr->rdh = 0;
+	csr32w(ctlr, Rdh, ctlr->rdh);
+	ctlr->rdt = 0;
+	csr32w(ctlr, Rdt, ctlr->rdt);
+	ctlr->rb = malloc(sizeof(Block*)*Nrdesc);
+	i82563replenish(ctlr);
+	csr32w(ctlr, Rdtr, 0);
+	csr32w(ctlr, Rctl, Dpf | Bsize2048 | Bam | RdtmsHALF);
+	i82563im(ctlr, Rxt0 | Rxo | Rxdmt0 | Rxseq | Ack);
+
+	csr32w(ctlr, Tctl, 0x0F<<CtSHIFT | Psp | 0x3f<<ColdSHIFT | Mulr);
+	csr32w(ctlr, Tipg, 6<<20 | 8<<10 | 8);
+	csr32w(ctlr, Tidv, 1);
+
+	ctlr->tdba = xspanalloc(Ntdesc*sizeof(Tdesc), 256, 0);
+	memset(ctlr->tdba, 0, Ntdesc*sizeof(Tdesc));
+	csr32w(ctlr, Tdbal, PCIWADDR(ctlr->tdba));
+
+	csr32w(ctlr, Tdbah, 0);
+	csr32w(ctlr, Tdlen, Ntdesc*sizeof(Tdesc));
+	ctlr->tdh = 0;
+	csr32w(ctlr, Tdh, ctlr->tdh);
+	ctlr->tdt = 0;
+	csr32w(ctlr, Tdt, ctlr->tdt);
+	ctlr->tb = malloc(sizeof(Block*)*Ntdesc);
+
+//	r = 4<<WthreshSHIFT | 4<<HthreshSHIFT | 8<<PthreshSHIFT;
+//	csr32w(ctlr, Txdctl, r);
+	csr32w(ctlr, Rxcsum, Tuofl | Ipofl | ETHERHDRSIZE<<PcssSHIFT);
+	r = csr32r(ctlr, Tctl);
+	r |= Ten;
+	csr32w(ctlr, Tctl, r);
+}
+
+
+static ushort
+eeread(Ctlr* ctlr, int adr)
+{
+	csr32w(ctlr, Eerd, ee_start | adr << 2);
+	while ((csr32r(ctlr, Eerd) & ee_done) == 0)
+		;
+	return csr32r(ctlr, Eerd) >> 16;
+}
+
+static int
+eeload(Ctlr* ctlr)
+{
+	ushort sum;
+	int data, adr;
+
+	sum = 0;
+	for (adr = 0; adr < 0x40; adr++) {
+		data = eeread(ctlr, adr);
+		ctlr->eeprom[adr] = data;
+		sum += data;
+	}
+	return sum;
+}
+
+
+static void
+detach(Ctlr *ctlr)
+{
+	int r;
+
+	csr32w(ctlr, Imc, ~0);
+	csr32w(ctlr, Rctl, 0);
+	csr32w(ctlr, Tctl, 0);
+
+	delay(10);
+
+	r = csr32r(ctlr, Ctrl);
+	csr32w(ctlr, Ctrl, Devrst | r);
+	/* apparently needed on multi-GHz processors to avoid infinite loops */
+	delay(1);
+	while(csr32r(ctlr, Ctrl) & Devrst)
+		;
+
+	if(1 || ctlr->type != i82563){
+		r = csr32r(ctlr, Ctrl);
+		csr32w(ctlr, Ctrl, Slu | r);
+	}
+
+	csr32w(ctlr, Ctrlext, Eerst | csr32r(ctlr, Ctrlext));
+	delay(1);
+	while(csr32r(ctlr, Ctrlext) & Eerst)
+		;
+
+	csr32w(ctlr, Imc, ~0);
+	delay(1);
+	while(csr32r(ctlr, Icr))
+		;
+}
+
+static void
+i82563detach(Ether *edev)
+{
+	detach(edev->ctlr);
+}
+
+static void
+i82563shutdown(Ether* ether)
+{
+	i82563detach(ether);
+}
+
+static int
+i82563reset(Ctlr* ctlr)
+{
+	int i, r;
+
+	detach(ctlr);
+
+	r = eeload(ctlr);
+	if (r != 0 && r != 0xBABA){
+		print("%s: bad EEPROM checksum - 0x%4.4ux\n", Type, r);
+		return -1;
+	}
+
+	for(i = Ea; i < Eaddrlen/2; i++){
+		ctlr->ra[2*i]   = ctlr->eeprom[i];
+		ctlr->ra[2*i+1] = ctlr->eeprom[i]>>8;
+	}
+	r = (csr32r(ctlr, Status) & Lanid) >> 2;
+	ctlr->ra[5] += r;		/* ea ctlr[1] = ea ctlr[0]+1 */
+
+	r = ctlr->ra[3]<<24 | ctlr->ra[2]<<16 | ctlr->ra[1]<<8 | ctlr->ra[0];
+	csr32w(ctlr, Ral, r);
+	r = 0x80000000 | ctlr->ra[5]<<8 | ctlr->ra[4];
+	csr32w(ctlr, Rah, r);
+	for(i = 1; i < 16; i++){
+		csr32w(ctlr, Ral+i*8, 0);
+		csr32w(ctlr, Rah+i*8, 0);
+	}
+
+	for(i = 0; i < 128; i++)
+		csr32w(ctlr, Mta+i*4, 0);
+
+	csr32w(ctlr, Fcal, 0x00C28001);
+	csr32w(ctlr, Fcah, 0x00000100);
+	csr32w(ctlr, Fct,  0x00008808);
+	csr32w(ctlr, Fcttv, 0x00000100);
+
+	csr32w(ctlr, Fcrtl, ctlr->fcrtl);
+	csr32w(ctlr, Fcrth, ctlr->fcrth);
+
+	ilock(&ctlr->imlock);
+	csr32w(ctlr, Imc, ~0);
+	ctlr->im = 0;		/* was = Lsc, which hangs some controllers */
+	csr32w(ctlr, Ims, ctlr->im);
+	iunlock(&ctlr->imlock);
+
+	return 0;
+}
+
+static void
+i82563pci(void)
+{
+	int port, type, cls;
+	Pcidev *p;
+	Ctlr *ctlr;
+	static int first = 1;
+
+	if (first)
+		first = 0;
+	else
+		return;
+
+	p = nil;
+	while(p = pcimatch(p, 0x8086, 0)){
+		switch(p->did){
+		case 0x1096:
+		case 0x10ba:
+			type = i82563;
+			break;
+		case 0x108b:		/*  e */
+		case 0x108c:		/*  e (iamt) */
+		case 0x109a:		/*  l */
+			type = i82573;
+			break;
+		default:
+			continue;
+		}
+
+		port = upamalloc(p->mem[0].bar & ~0x0F, p->mem[0].size, 0);
+		if(port == 0){
+			print("%s: can't map %d @ 0x%8.8lux\n", tname[type],
+				p->mem[0].size, p->mem[0].bar);
+			continue;
+		}
+
+		if(p->pcr & MemWrInv){
+			cls = pcicfgr8(p, PciCLS) * 4;
+			if(cls != CACHELINESZ)
+				pcicfgw8(p, PciCLS, CACHELINESZ/4);
+		}
+
+		cls = pcicfgr8(p, PciCLS);
+		switch(cls){
+		default:
+			print("%s: unexpected CLS - %d bytes\n",
+				tname[type], cls*sizeof(long));
+			break;
+		case 0x00:
+		case 0xFF:
+			/* alphapc 164lx returns 0 */
+			print("%s: unusable PciCLS: %d, using %d longs\n",
+				tname[type], cls, CACHELINESZ/sizeof(long));
+			cls = CACHELINESZ/sizeof(long);
+			pcicfgw8(p, PciCLS, cls);
+			break;
+		case 0x08:
+		case 0x10:
+			break;
+		}
+
+		ctlr = malloc(sizeof(Ctlr));
+		ctlr->port = port;
+		ctlr->pcidev = p;
+		ctlr->cls = cls*4;
+		ctlr->type = type;
+		ctlr->nic = KADDR(ctlr->port);
+		if(i82563reset(ctlr)){
+			free(ctlr);
+			continue;
+		}
+		pcisetbme(p);
+
+		if(ctlrhead != nil)
+			ctlrtail->next = ctlr;
+		else
+			ctlrhead = ctlr;
+		ctlrtail = ctlr;
+	}
+}
+
+static uchar nilea[Eaddrlen];
+
+int
+i82563pnp(Ether* edev)
+{
+	Ctlr *ctlr;
+
+	if(ctlrhead == nil)
+		i82563pci();
+
+	/*
+	 * Any adapter matches if no edev->port is supplied,
+	 * otherwise the ports must match.
+	 */
+	for(ctlr = ctlrhead; ctlr != nil; ctlr = ctlr->next){
+		if(ctlr->active)
+			continue;
+		if(edev->port == 0 || edev->port == ctlr->port){
+			ctlr->active = 1;
+			break;
+		}
+	}
+	if(ctlr == nil)
+		return -1;
+
+	edev->ctlr = ctlr;
+	edev->port = ctlr->port;
+	edev->irq = ctlr->pcidev->intl;
+	edev->tbdf = ctlr->pcidev->tbdf;
+//	edev->mbps = 1000;
+
+	if(memcmp(edev->ea, nilea, Eaddrlen) == 0)
+		memmove(edev->ea, ctlr->ra, Eaddrlen);
+	i82563init(edev);
+
+	/*
+	 * Linkage to the generic ethernet driver.
+	 */
+	edev->attach = i82563attach;
+	edev->transmit = i82563transmit;
+	edev->interrupt = i82563interrupt;
+	edev->detach = i82563detach;
+
+	/*
+	 * with the current structure, there is no right place for this.
+	 * ideally, we recognize the interface, note it's down and move on.
+	 * currently either we can skip the interface or note it is down,
+	 * but not both.
+	 */
+	if((csr32r(ctlr, Status)&Lu) == 0){
+		print("ether#%d: 82563 (%s): link down\n", edev->ctlrno, Type);
+		return -1;
+	}
+
+	return 0;
+}
--- /dev/null
+++ b/os/boot/pc/ether83815.c
@@ -1,0 +1,820 @@
+/*
+ * National Semiconductor DP83815
+ *
+ * Supports only internal PHY and has been tested on:
+ *	Netgear FA311TX (using Netgear DS108 10/100 hub)
+ * To do:
+ *	check Ethernet address;
+ *	test autonegotiation on 10 Mbit, and 100 Mbit full duplex;
+ *	external PHY via MII (should be common code for MII);
+ *	thresholds;
+ *	ring sizing;
+ *	physical link changes/disconnect;
+ *	push initialisation back to attach.
+ *
+ * C H Forsyth, forsyth@vitanuova.com, 18th June 2001.
+ */
+
+#include "u.h"
+#include "lib.h"
+#include "mem.h"
+#include "dat.h"
+#include "fns.h"
+#include "io.h"
+
+#include "etherif.h"
+
+#define DEBUG		(1)
+#define debug		if(DEBUG)print
+
+enum {
+	Nrde		= 8,
+	Ntde		= 8,
+};
+
+#define Rbsz		ROUNDUP(sizeof(Etherpkt)+4, 4)
+
+typedef struct Des {
+	ulong	next;
+	int	cmdsts;
+	ulong	addr;
+	Block*	bp;
+} Des;
+
+enum {	/* cmdsts */
+	Own		= 1<<31,	/* set by data producer to hand to consumer */
+	More	= 1<<30,	/* more of packet in next descriptor */
+	Intr		= 1<<29,	/* interrupt when device is done with it */
+	Supcrc	= 1<<28,	/* suppress crc on transmit */
+	Inccrc	= 1<<28,	/* crc included on receive (always) */
+	Ok		= 1<<27,	/* packet ok */
+	Size		= 0xFFF,	/* packet size in bytes */
+
+	/* transmit */
+	Txa	= 1<<26,	/* transmission aborted */
+	Tfu	= 1<<25,	/* transmit fifo underrun */
+	Crs	= 1<<24,	/* carrier sense lost */
+	Td	= 1<<23,	/* transmission deferred */
+	Ed	= 1<<22,	/* excessive deferral */
+	Owc	= 1<<21,	/* out of window collision */
+	Ec	= 1<<20,	/* excessive collisions */
+	/* 19-16 collision count */
+
+	/* receive */
+	Rxa	= 1<<26,	/* receive aborted (same as Rxo) */
+	Rxo	= 1<<25,	/* receive overrun */
+	Dest	= 3<<23,	/* destination class */
+	  Drej=	0<<23,		/* packet was rejected */
+	  Duni=	1<<23,		/* unicast */
+	  Dmulti=	2<<23,		/* multicast */
+	  Dbroad=	3<<23,		/* broadcast */
+	Long = 1<<22,	/* too long packet received */
+	Runt =  1<<21,	/* packet less than 64 bytes */
+	Ise =	1<<20,	/* invalid symbol */
+	Crce =	1<<19,	/* invalid crc */
+	Fae =	1<<18,	/* frame alignment error */
+	Lbp =	1<<17,	/* loopback packet */
+	Col =	1<<16,	/* collision during receive */
+};
+
+enum {					/* Variants */
+	Nat83815		= (0x0020<<16)|0x100B,
+};
+
+typedef struct Ctlr Ctlr;
+typedef struct Ctlr {
+	int	port;
+	Pcidev*	pcidev;
+	Ctlr*	next;
+	int	active;
+	int	id;			/* (pcidev->did<<16)|pcidev->vid */
+
+	ushort	srom[0xB+1];
+	uchar	sromea[Eaddrlen];			/* MAC address */
+
+	uchar	fd;			/* option or auto negotiation */
+
+	int	mbps;
+
+	Lock	ilock;
+
+	Des*	rdr;			/* receive descriptor ring */
+	int	nrdr;			/* size of rdr */
+	int	rdrx;			/* index into rdr */
+
+	Lock	tlock;
+	Des*	tdr;			/* transmit descriptor ring */
+	int	ntdr;			/* size of tdr */
+	int	tdrh;			/* host index into tdr */
+	int	tdri;			/* interface index into tdr */
+	int	ntq;			/* descriptors active */
+	int	ntqmax;
+	Block*	bqhead;	/* transmission queue */
+	Block*	bqtail;
+
+	ulong	rxa;			/* receive statistics */
+	ulong	rxo;
+	ulong	rlong;
+	ulong	runt;
+	ulong	ise;
+	ulong	crce;
+	ulong	fae;
+	ulong	lbp;
+	ulong	col;
+	ulong	rxsovr;
+	ulong	rxorn;
+
+	ulong	txa;			/* transmit statistics */
+	ulong	tfu;
+	ulong	crs;
+	ulong	td;
+	ulong	ed;
+	ulong	owc;
+	ulong	ec;
+	ulong	txurn;
+
+	ulong	dperr;		/* system errors */
+	ulong	rmabt;
+	ulong	rtabt;
+	ulong	sserr;
+	ulong	rxsover;
+} Ctlr;
+
+static Ctlr* ctlrhead;
+static Ctlr* ctlrtail;
+
+enum {
+	/* registers (could memory map) */
+	Rcr=		0x00,	/* command register */
+	  Rst=		1<<8,
+	  Rxr=		1<<5,	/* receiver reset */
+	  Txr=		1<<4,	/* transmitter reset */
+	  Rxd=		1<<3,	/* receiver disable */
+	  Rxe=		1<<2,	/* receiver enable */
+	  Txd=		1<<1,	/* transmitter disable */
+	  Txe=		1<<0,	/* transmitter enable */
+	Rcfg=	0x04,	/* configuration */
+	  Lnksts=		1<<31,	/* link good */
+	  Speed100=	1<<30,	/* 100 Mb/s link */
+	  Fdup=		1<<29,	/* full duplex */
+	  Pol=		1<<28,	/* polarity reversal (10baseT) */
+	  Aneg_dn=	1<<27,	/* autonegotiation done */
+	  Pint_acen=	1<<17,	/* PHY interrupt auto clear enable */
+	  Pause_adv=	1<<16,	/* advertise pause during auto neg */
+	  Paneg_ena=	1<<13,	/* auto negotiation enable */
+	  Paneg_all=	7<<13,	/* auto negotiation enable 10/100 half & full */
+	  Ext_phy=	1<<12,	/* enable MII for external PHY */
+	  Phy_rst=		1<<10,	/* reset internal PHY */
+	  Phy_dis=		1<<9,	/* disable internal PHY (eg, low power) */
+	  Req_alg=	1<<7,	/* PCI bus request: set means less aggressive */
+	  Sb=			1<<6,	/* single slot back-off not random */
+	  Pow=		1<<5,	/* out of window timer selection */
+	  Exd=		1<<4,	/* disable excessive deferral timer */
+	  Pesel=		1<<3,	/* parity error algorithm selection */
+	  Brom_dis=	1<<2,	/* disable boot rom interface */
+	  Bem=		1<<0,	/* big-endian mode */
+	Rmear=	0x08,	/* eeprom access */
+	  Mdc=		1<<6,	/* MII mangement check */
+	  Mddir=		1<<5,	/* MII management direction */
+	  Mdio=		1<<4,	/* MII mangement data */
+	  Eesel=		1<<3,	/* EEPROM chip select */
+	  Eeclk=		1<<2,	/* EEPROM clock */
+	  Eedo=		1<<1,	/* EEPROM data out (from chip) */
+	  Eedi=		1<<0,	/* EEPROM data in (to chip) */
+	Rptscr=	0x0C,	/* pci test control */
+	Risr=		0x10,	/* interrupt status */
+	  Txrcmp=	1<<25,	/* transmit reset complete */
+	  Rxrcmp=	1<<24,	/* receiver reset complete */
+	  Dperr=		1<<23,	/* detected parity error */
+	  Sserr=		1<<22,	/* signalled system error */
+	  Rmabt=		1<<21,	/* received master abort */
+	  Rtabt=		1<<20,	/* received target abort */
+	  Rxsovr=		1<<16,	/* RX status FIFO overrun */
+	  Hiberr=		1<<15,	/* high bits error set (OR of 25-16) */
+	  Phy=		1<<14,	/* PHY interrupt */
+	  Pme=		1<<13,	/* power management event (wake online) */
+	  Swi=		1<<12,	/* software interrupt */
+	  Mib=		1<<11,	/* MIB service */
+	  Txurn=		1<<10,	/* TX underrun */
+	  Txidle=		1<<9,	/* TX idle */
+	  Txerr=		1<<8,	/* TX packet error */
+	  Txdesc=		1<<7,	/* TX descriptor (with Intr bit done) */
+	  Txok=		1<<6,	/* TX ok */
+	  Rxorn=		1<<5,	/* RX overrun */
+	  Rxidle=		1<<4,	/* RX idle */
+	  Rxearly=		1<<3,	/* RX early threshold */
+	  Rxerr=		1<<2,	/* RX packet error */
+	  Rxdesc=		1<<1,	/* RX descriptor (with Intr bit done) */
+	  Rxok=		1<<0,	/* RX ok */
+	Rimr=	0x14,	/* interrupt mask */
+	Rier=	0x18,	/* interrupt enable */
+	  Ie=			1<<0,	/* interrupt enable */
+	Rtxdp=	0x20,	/* transmit descriptor pointer */
+	Rtxcfg=	0x24,	/* transmit configuration */
+	  Csi=		1<<31,	/* carrier sense ignore (needed for full duplex) */
+	  Hbi=		1<<30,	/* heartbeat ignore (needed for full duplex) */
+	  Atp=		1<<28,	/* automatic padding of runt packets */
+	  Mxdma=		7<<20,	/* maximum dma transfer field */
+	  Mxdma32=	4<<20,	/* 4x32-bit words (32 bytes) */
+	  Mxdma64=	5<<20,	/* 8x32-bit words (64 bytes) */
+	  Flth=		0x3F<<8,	/* Tx fill threshold, units of 32 bytes (must be > Mxdma) */
+	  Drth=		0x3F<<0,	/* Tx drain threshold (units of 32 bytes) */
+	  Flth128=		4<<8,	/* fill at 128 bytes */
+	  Drth512=	16<<0,	/* drain at 512 bytes */
+	Rrxdp=	0x30,	/* receive descriptor pointer */
+	Rrxcfg=	0x34,	/* receive configuration */
+	  Atx=		1<<28,	/* accept transmit packets (needed for full duplex) */
+	  Rdrth=		0x1F<<1,	/* Rx drain threshold (units of 32 bytes) */
+	  Rdrth64=	2<<1,	/* drain at 64 bytes */
+	Rccsr=	0x3C,	/* CLKRUN control/status */
+	  Pmests=		1<<15,	/* PME status */
+	Rwcsr=	0x40,	/* wake on lan control/status */
+	Rpcr=	0x44,	/* pause control/status */
+	Rrfcr=	0x48,	/* receive filter/match control */
+	  Rfen=		1<<31,	/* receive filter enable */
+	  Aab=		1<<30,	/* accept all broadcast */
+	  Aam=		1<<29,	/* accept all multicast */
+	  Aau=		1<<28,	/* accept all unicast */
+	  Apm=		1<<27,	/* accept on perfect match */
+	  Apat=		0xF<<23,	/* accept on pattern match */
+	  Aarp=		1<<22,	/* accept ARP */
+	  Mhen=		1<<21,	/* multicast hash enable */
+	  Uhen=		1<<20,	/* unicast hash enable */
+	  Ulm=		1<<19,	/* U/L bit mask */
+						/* bits 0-9 are rfaddr */
+	Rrfdr=	0x4C,	/* receive filter/match data */
+	Rbrar=	0x50,	/* boot rom address */
+	Rbrdr=	0x54,	/* boot rom data */
+	Rsrr=	0x58,	/* silicon revision */
+	Rmibc=	0x5C,	/* MIB control */
+					/* 60-78 MIB data */
+
+	/* PHY registers */
+	Rbmcr=	0x80,	/* basic mode configuration */
+	  Reset=		1<<15,
+	  Sel100=		1<<13,	/* select 100Mb/sec if no auto neg */
+	  Anena=		1<<12,	/* auto negotiation enable */
+	  Anrestart=	1<<9,	/* restart auto negotiation */
+	  Selfdx=		1<<8,	/* select full duplex if no auto neg */
+	Rbmsr=	0x84,	/* basic mode status */
+	  Ancomp=	1<<5,	/* autonegotiation complete */
+	Rphyidr1= 0x88,
+	Rphyidr2= 0x8C,
+	Ranar=	0x90,	/* autonegotiation advertisement */
+	Ranlpar=	0x94,	/* autonegotiation link partner ability */
+	Raner=	0x98,	/* autonegotiation expansion */
+	Rannptr=	0x9C,	/* autonegotiation next page TX */
+	Rphysts=	0xC0,	/* PHY status */
+	Rmicr=	0xC4,	/* MII control */
+	  Inten=		1<<1,	/* PHY interrupt enable */
+	Rmisr=	0xC8,	/* MII status */
+	Rfcscr=	0xD0,	/* false carrier sense counter */
+	Rrecr=	0xD4,	/* receive error counter */
+	Rpcsr=	0xD8,	/* 100Mb config/status */
+	Rphycr=	0xE4,	/* PHY control */
+	Rtbscr=	0xE8,	/* 10BaseT status/control */
+};
+
+/*
+ * eeprom addresses
+ * 	7 to 9 (16 bit words): mac address, shifted and reversed
+ */
+
+#define csr32r(c, r)	(inl((c)->port+(r)))
+#define csr32w(c, r, l)	(outl((c)->port+(r), (ulong)(l)))
+#define csr16r(c, r)	(ins((c)->port+(r)))
+#define csr16w(c, r, l)	(outs((c)->port+(r), (ulong)(l)))
+
+static void
+dumpcregs(Ctlr *ctlr)
+{
+	int i;
+
+	for(i=0; i<=0x5C; i+=4)
+		print("%2.2ux %8.8lux\n", i, csr32r(ctlr, i));
+}
+
+static void
+attach(Ether* ether)
+{
+	Ctlr *ctlr;
+
+	ctlr = ether->ctlr;
+	ilock(&ctlr->ilock);
+	if(0)
+		dumpcregs(ctlr);
+	csr32w(ctlr, Rcr, Rxe);
+	iunlock(&ctlr->ilock);
+}
+
+static void
+detach(Ether* ether)
+{
+	Ctlr *ctlr;
+
+	ctlr = ether->ctlr;
+	csr32w(ctlr, Rcr, 0);
+	delay(1);
+}
+
+static void
+txstart(Ether* ether)
+{
+	Ctlr *ctlr;
+	Block *bp;
+	Des *des;
+	int started;
+
+	ctlr = ether->ctlr;
+	started = 0;
+	while(ctlr->ntq < ctlr->ntdr-1){
+		bp = ctlr->bqhead;
+		if(bp == nil)
+			break;
+		ctlr->bqhead = bp->next;
+		des = &ctlr->tdr[ctlr->tdrh];
+		des->bp = bp;
+		des->addr = PADDR(bp->rp);
+		ctlr->ntq++;
+		coherence();
+		des->cmdsts = Own | BLEN(bp);
+		ctlr->tdrh = NEXT(ctlr->tdrh, ctlr->ntdr);
+		started = 1;
+	}
+	if(started){
+		coherence();
+		csr32w(ctlr, Rcr, Txe);	/* prompt */
+	}
+
+	if(ctlr->ntq > ctlr->ntqmax)
+		ctlr->ntqmax = ctlr->ntq;
+}
+
+static void
+transmit(Ether* ether)
+{
+	Ctlr *ctlr;
+	Block *bp;
+	RingBuf *tb;
+
+	ctlr = ether->ctlr;
+	ilock(&ctlr->tlock);
+	while((tb = &ether->tb[ether->ti])->owner == Interface){
+		bp = allocb(tb->len);
+		memmove(bp->wp, tb->pkt, tb->len);
+		memmove(bp->wp+Eaddrlen, ether->ea, Eaddrlen);
+		bp->wp += tb->len;
+		if(ctlr->bqhead)
+			ctlr->bqtail->next = bp;
+		else
+			ctlr->bqhead = bp;
+		ctlr->bqtail = bp;
+		txstart(ether);
+		tb->owner = Host;
+		ether->ti = NEXT(ether->ti, ether->ntb);
+	}
+	iunlock(&ctlr->tlock);
+}
+
+static void
+txrxcfg(Ctlr *ctlr, int txdrth)
+{
+	ulong rx, tx;
+
+	rx = csr32r(ctlr, Rrxcfg);
+	tx = csr32r(ctlr, Rtxcfg);
+	if(ctlr->fd){
+		rx |= Atx;
+		tx |= Csi | Hbi;
+	}else{
+		rx &= ~Atx;
+		tx &= ~(Csi | Hbi);
+	}
+	tx &= ~(Mxdma|Drth|Flth);
+	tx |= Mxdma64 | Flth128 | txdrth;
+	csr32w(ctlr, Rtxcfg, tx);
+	rx &= ~(Mxdma|Rdrth);
+	rx |= Mxdma64 | Rdrth64;
+	csr32w(ctlr, Rrxcfg, rx);
+}
+
+static void
+interrupt(Ureg*, void* arg)
+{
+	Ctlr *ctlr;
+	Ether *ether;
+	int status, cmdsts;
+	Des *des;
+	RingBuf *rb;
+
+	ether = arg;
+	ctlr = ether->ctlr;
+
+	while((status = csr32r(ctlr, Risr)) != 0){
+
+		status &= ~(Pme|Mib);
+		status &= ~(Hiberr|Txrcmp|Rxrcmp|Rxsovr|Dperr|Sserr|Rmabt|Rtabt);
+
+		/*
+		 * Received packets.
+		 */
+		if(status & (Rxdesc|Rxok|Rxerr|Rxearly|Rxorn)){
+			des = &ctlr->rdr[ctlr->rdrx];
+			while((cmdsts = des->cmdsts) & Own){
+				rb = &ether->rb[ether->ri];
+				if(rb->owner == Interface && (cmdsts&Ok)){
+					rb->len = (cmdsts&Size)-4;
+					memmove(rb->pkt, des->bp->rp, rb->len);
+					rb->owner = Host;
+					ether->ri = NEXT(ether->ri, ether->nrb);
+				}
+
+				des->cmdsts = Rbsz;
+				coherence();
+
+				ctlr->rdrx = NEXT(ctlr->rdrx, ctlr->nrdr);
+				des = &ctlr->rdr[ctlr->rdrx];
+			}
+			status &= ~(Rxdesc|Rxok|Rxerr|Rxearly|Rxorn);
+		}
+
+		/*
+		 * Check the transmit side:
+		 *	check for Transmit Underflow and Adjust
+		 *	the threshold upwards;
+		 *	free any transmitted buffers and try to
+		 *	top-up the ring.
+		 */
+		if(status & Txurn){
+			ctlr->txurn++;
+			ilock(&ctlr->ilock);
+			/* change threshold */
+			iunlock(&ctlr->ilock);
+			status &= ~(Txurn);
+		}
+
+		ilock(&ctlr->tlock);
+		while(ctlr->ntq){
+			des = &ctlr->tdr[ctlr->tdri];
+			cmdsts = des->cmdsts;
+			if(cmdsts & Own)
+				break;
+
+			freeb(des->bp);
+			des->bp = nil;
+			des->cmdsts = 0;
+
+			ctlr->ntq--;
+			ctlr->tdri = NEXT(ctlr->tdri, ctlr->ntdr);
+		}
+		txstart(ether);
+		iunlock(&ctlr->tlock);
+
+		status &= ~(Txurn|Txidle|Txerr|Txdesc|Txok);
+
+		/*
+		 * Anything left not catered for?
+		 */
+		if(status)
+			print("#l%d: status %8.8uX\n", ether->ctlrno, status);
+	}
+}
+
+static void
+ctlrinit(Ether* ether)
+{
+	Ctlr *ctlr;
+	Des *des, *last;
+
+	ctlr = ether->ctlr;
+
+	/*
+	 * Allocate and initialise the receive ring;
+	 * allocate and initialise the transmit ring;
+	 * unmask interrupts and start the transmit side
+	 */
+	ctlr->rdr = malloc(ctlr->nrdr*sizeof(Des));
+	last = nil;
+	for(des = ctlr->rdr; des < &ctlr->rdr[ctlr->nrdr]; des++){
+		des->bp = allocb(Rbsz);
+		des->cmdsts = Rbsz;
+		des->addr = PADDR(des->bp->rp);
+		if(last != nil)
+			last->next = PADDR(des);
+		last = des;
+	}
+	ctlr->rdr[ctlr->nrdr-1].next = PADDR(ctlr->rdr);
+	ctlr->rdrx = 0;
+	csr32w(ctlr, Rrxdp, PADDR(ctlr->rdr));
+
+	ctlr->tdr = xspanalloc(ctlr->ntdr*sizeof(Des), 8*sizeof(ulong), 0);
+	last = nil;
+	for(des = ctlr->tdr; des < &ctlr->tdr[ctlr->ntdr]; des++){
+		des->cmdsts = 0;
+		des->bp = nil;
+		des->addr = ~0;
+		if(last != nil)
+			last->next = PADDR(des);
+		last = des;
+	}
+	ctlr->tdr[ctlr->ntdr-1].next = PADDR(ctlr->tdr);
+	ctlr->tdrh = 0;
+	ctlr->tdri = 0;
+	csr32w(ctlr, Rtxdp, PADDR(ctlr->tdr));
+
+	txrxcfg(ctlr, Drth512);
+
+	csr32w(ctlr, Rimr, Dperr|Sserr|Rmabt|Rtabt|Rxsovr|Hiberr|Txurn|Txerr|Txdesc|Txok|Rxorn|Rxerr|Rxdesc|Rxok);	/* Phy|Pme|Mib */
+	csr32r(ctlr, Risr);	/* clear status */
+	csr32w(ctlr, Rier, Ie);
+}
+
+static void
+eeclk(Ctlr *ctlr, int clk)
+{
+	csr32w(ctlr, Rmear, Eesel | clk);
+	microdelay(2);
+}
+
+static void
+eeidle(Ctlr *ctlr)
+{
+	int i;
+
+	eeclk(ctlr, 0);
+	eeclk(ctlr, Eeclk);
+	for(i=0; i<25; i++){
+		eeclk(ctlr, 0);
+		eeclk(ctlr, Eeclk);
+	}
+	eeclk(ctlr, 0);
+	csr32w(ctlr, Rmear, 0);
+	microdelay(2);
+}
+
+static int
+eegetw(Ctlr *ctlr, int a)
+{
+	int d, i, w, v;
+
+	eeidle(ctlr);
+	eeclk(ctlr, 0);
+	eeclk(ctlr, Eeclk);
+	d = 0x180 | a;
+	for(i=0x400; i; i>>=1){
+		v = (d & i) ? Eedi : 0;
+		eeclk(ctlr, v);
+		eeclk(ctlr, Eeclk|v);
+	}
+	eeclk(ctlr, 0);
+
+	w = 0;
+	for(i=0x8000; i; i >>= 1){
+		eeclk(ctlr, Eeclk);
+		if(csr32r(ctlr, Rmear) & Eedo)
+			w |= i;
+		microdelay(2);
+		eeclk(ctlr, 0);
+	}
+	eeidle(ctlr);
+	return w;
+}
+
+static void
+softreset(Ctlr* ctlr, int resetphys)
+{
+	int i, w;
+
+	/*
+	 * Soft-reset the controller
+	 */
+	csr32w(ctlr, Rcr, Rst);
+	for(i=0;; i++){
+		if(i > 100)
+			panic("ns83815: soft reset did not complete");
+		microdelay(250);
+		if((csr32r(ctlr, Rcr) & Rst) == 0)
+			break;
+		delay(1);
+	}
+
+	csr32w(ctlr, Rccsr, Pmests);
+	csr32w(ctlr, Rccsr, 0);
+	csr32w(ctlr, Rcfg, csr32r(ctlr, Rcfg) | Pint_acen);
+
+	if(resetphys){
+		/*
+		 * Soft-reset the PHY
+		 */
+		csr32w(ctlr, Rbmcr, Reset);
+		for(i=0;; i++){
+			if(i > 100)
+				panic("ns83815: PHY soft reset time out");
+			if((csr32r(ctlr, Rbmcr) & Reset) == 0)
+				break;
+			delay(1);
+		}
+	}
+
+	/*
+	 * Initialisation values, in sequence (see 4.4 Recommended Registers Configuration)
+	 */
+	csr16w(ctlr, 0xCC, 0x0001);	/* PGSEL */
+	csr16w(ctlr, 0xE4, 0x189C);	/* PMCCSR */
+	csr16w(ctlr, 0xFC, 0x0000);	/* TSTDAT */
+	csr16w(ctlr, 0xF4, 0x5040);	/* DSPCFG */
+	csr16w(ctlr, 0xF8, 0x008C);	/* SDCFG */
+
+	/*
+	 * Auto negotiate
+	 */
+	w = csr16r(ctlr, Rbmsr);	/* clear latched bits */
+	debug("anar: %4.4ux\n", csr16r(ctlr, Ranar));
+	csr16w(ctlr, Rbmcr, Anena);
+	if(csr16r(ctlr, Ranar) == 0 || (csr32r(ctlr, Rcfg) & Aneg_dn) == 0){
+		csr16w(ctlr, Rbmcr, Anena|Anrestart);
+		for(i=0;; i++){
+			if(i > 6000){
+				print("ns83815: auto neg timed out\n");
+				break;
+			}
+			if((w = csr16r(ctlr, Rbmsr)) & Ancomp)
+				break;
+			delay(1);
+		}
+		debug("%d ms\n", i);
+		w &= 0xFFFF;
+		debug("bmsr: %4.4ux\n", w);
+	}
+	debug("anar: %4.4ux\n", csr16r(ctlr, Ranar));
+	debug("anlpar: %4.4ux\n", csr16r(ctlr, Ranlpar));
+	debug("aner: %4.4ux\n", csr16r(ctlr, Raner));
+	debug("physts: %4.4ux\n", csr16r(ctlr, Rphysts));
+	debug("tbscr: %4.4ux\n", csr16r(ctlr, Rtbscr));
+}
+
+static char* mediatable[9] = {
+	"10BASE-T",				/* TP */
+	"10BASE-2",				/* BNC */
+	"10BASE-5",				/* AUI */
+	"100BASE-TX",
+	"10BASE-TFD",
+	"100BASE-TXFD",
+	"100BASE-T4",
+	"100BASE-FX",
+	"100BASE-FXFD",
+};
+
+static void
+srom(Ctlr* ctlr)
+{
+	int i, j;
+
+	for(i = 0; i < nelem(ctlr->srom); i++)
+		ctlr->srom[i] = eegetw(ctlr, i);
+
+	/*
+	 * the MAC address is reversed, straddling word boundaries
+	 */
+	memset(ctlr->sromea, 0, sizeof(ctlr->sromea));
+	j = 6*16 + 15;
+	for(i=0; i<48; i++){
+		ctlr->sromea[i>>3] |= ((ctlr->srom[j>>4] >> (15-(j&0xF))) & 1) << (i&7);
+		j++;
+	}
+}
+
+static void
+scanpci83815(void)
+{
+	Ctlr *ctlr;
+	Pcidev *p;
+
+	p = nil;
+	while(p = pcimatch(p, 0, 0)){
+		if(p->ccrb != 0x02 || p->ccru != 0)
+			continue;
+		switch((p->did<<16)|p->vid){
+		default:
+			continue;
+
+		case Nat83815:
+			break;
+		}
+
+		/*
+		 * bar[0] is the I/O port register address and
+		 * bar[1] is the memory-mapped register address.
+		 */
+		ctlr = malloc(sizeof(Ctlr));
+		ctlr->port = p->mem[0].bar & ~0x01;
+		ctlr->pcidev = p;
+		ctlr->id = (p->did<<16)|p->vid;
+
+		softreset(ctlr, 0);
+		srom(ctlr);
+
+		if(ctlrhead != nil)
+			ctlrtail->next = ctlr;
+		else
+			ctlrhead = ctlr;
+		ctlrtail = ctlr;
+	}
+}
+
+int
+ether83815reset(Ether* ether)
+{
+	Ctlr *ctlr;
+	int i, x;
+	uchar ea[Eaddrlen];
+	static int scandone;
+
+	if(scandone == 0){
+		scanpci83815();
+		scandone = 1;
+	}
+
+	/*
+	 * Any adapter matches if no ether->port is supplied,
+	 * otherwise the ports must match.
+	 */
+	for(ctlr = ctlrhead; ctlr != nil; ctlr = ctlr->next){
+		if(ctlr->active)
+			continue;
+		if(ether->port == 0 || ether->port == ctlr->port){
+			ctlr->active = 1;
+			break;
+		}
+	}
+	if(ctlr == nil)
+		return -1;
+
+	ether->ctlr = ctlr;
+	ether->port = ctlr->port;
+	ether->irq = ctlr->pcidev->intl;
+	ether->tbdf = ctlr->pcidev->tbdf;
+
+	/*
+	 * Check if the adapter's station address is to be overridden.
+	 * If not, read it from the EEPROM and set in ether->ea prior to
+	 * loading the station address in the hardware.
+	 */
+	memset(ea, 0, Eaddrlen);
+	if(memcmp(ea, ether->ea, Eaddrlen) == 0)
+		memmove(ether->ea, ctlr->sromea, Eaddrlen);
+	for(i=0; i<Eaddrlen; i+=2){
+		x = ether->ea[i] | (ether->ea[i+1]<<8);
+		csr32w(ctlr, Rrfcr, i);
+		csr32w(ctlr, Rrfdr, x);
+	}
+	csr32w(ctlr, Rrfcr, Rfen|Apm|Aab|Aam);
+
+	/*
+	 * Look for a medium override in case there's no autonegotiation
+	 * the autonegotiation fails.
+	 */
+
+	for(i = 0; i < ether->nopt; i++){
+		if(cistrcmp(ether->opt[i], "FD") == 0){
+			ctlr->fd = 1;
+			continue;
+		}
+		for(x = 0; x < nelem(mediatable); x++){
+			debug("compare <%s> <%s>\n", mediatable[x],
+				ether->opt[i]);
+			if(cistrcmp(mediatable[x], ether->opt[i]) == 0){
+				switch(x){
+				default:
+					ctlr->fd = 0;
+					break;
+
+				case 0x04:		/* 10BASE-TFD */
+				case 0x05:		/* 100BASE-TXFD */
+				case 0x08:		/* 100BASE-FXFD */
+					ctlr->fd = 1;
+					break;
+				}
+				break;
+			}
+		}
+	}
+
+	/*
+	 * Initialise descriptor rings, ethernet address.
+	 */
+	ctlr->nrdr = Nrde;
+	ctlr->ntdr = Ntde;
+	pcisetbme(ctlr->pcidev);
+	ctlrinit(ether);
+
+	/*
+	 * Linkage to the generic ethernet driver.
+	 */
+	ether->attach = attach;
+	ether->transmit = transmit;
+	ether->interrupt = interrupt;
+	ether->detach = detach;
+
+	return 0;
+}
--- /dev/null
+++ b/os/boot/pc/ether8390.c
@@ -1,0 +1,715 @@
+/*
+ * National Semiconductor DP8390 and clone
+ * Network Interface Controller.
+ */
+#include "u.h"
+#include "lib.h"
+#include "mem.h"
+#include "dat.h"
+#include "fns.h"
+#include "io.h"
+
+#include "etherif.h"
+#include "ether8390.h"
+
+enum {					/* NIC core registers */
+	Cr		= 0x00,		/* command register, all pages */
+
+					/* Page 0, read */
+	Clda0		= 0x01,		/* current local DMA address 0 */
+	Clda1		= 0x02,		/* current local DMA address 1 */
+	Bnry		= 0x03,		/* boundary pointer (R/W) */
+	Tsr		= 0x04,		/* transmit status register */
+	Ncr		= 0x05,		/* number of collisions register */
+	Fifo		= 0x06,		/* FIFO */
+	Isr		= 0x07,		/* interrupt status register (R/W) */
+	Crda0		= 0x08,		/* current remote DMA address 0 */
+	Crda1		= 0x09,		/* current remote DMA address 1 */
+	Rsr		= 0x0C,		/* receive status register */
+	Cntr0		= 0x0D,		/* frame alignment errors */
+	Cntr1		= 0x0E,		/* CRC errors */
+	Cntr2		= 0x0F,		/* missed packet errors */
+
+					/* Page 0, write */
+	Pstart		= 0x01,		/* page start register */
+	Pstop		= 0x02,		/* page stop register */
+	Tpsr		= 0x04,		/* transmit page start address */
+	Tbcr0		= 0x05,		/* transmit byte count register 0 */
+	Tbcr1		= 0x06,		/* transmit byte count register 1 */
+	Rsar0		= 0x08,		/* remote start address register 0 */
+	Rsar1		= 0x09,		/* remote start address register 1 */
+	Rbcr0		= 0x0A,		/* remote byte count register 0 */
+	Rbcr1		= 0x0B,		/* remote byte count register 1 */
+	Rcr		= 0x0C,		/* receive configuration register */
+	Tcr		= 0x0D,		/* transmit configuration register */
+	Dcr		= 0x0E,		/* data configuration register */
+	Imr		= 0x0F,		/* interrupt mask */
+
+					/* Page 1, read/write */
+	Par0		= 0x01,		/* physical address register 0 */
+	Curr		= 0x07,		/* current page register */
+	Mar0		= 0x08,		/* multicast address register 0 */
+};
+
+enum {					/* Cr */
+	Stp		= 0x01,		/* stop */
+	Sta		= 0x02,		/* start */
+	Txp		= 0x04,		/* transmit packet */
+	Rd0		= 0x08,		/* remote DMA command */
+	Rd1		= 0x10,	
+	Rd2		= 0x20,
+	RdREAD		= Rd0,		/* remote read */
+	RdWRITE		= Rd1,		/* remote write */
+	RdSEND		= Rd1|Rd0,	/* send packet */
+	RdABORT		= Rd2,		/* abort/complete remote DMA */
+	Ps0		= 0x40,		/* page select */
+	Ps1		= 0x80,
+	Page0		= 0x00,
+	Page1		= Ps0,
+	Page2		= Ps1,
+};
+
+enum {					/* Isr/Imr */
+	Prx		= 0x01,		/* packet received */
+	Ptx		= 0x02,		/* packet transmitted */
+	Rxe		= 0x04,		/* receive error */
+	Txe		= 0x08,		/* transmit error */
+	Ovw		= 0x10,		/* overwrite warning */
+	Cnt		= 0x20,		/* counter overflow */
+	Rdc		= 0x40,		/* remote DMA complete */
+	Rst		= 0x80,		/* reset status */
+};
+
+enum {					/* Dcr */
+	Wts		= 0x01,		/* word transfer select */
+	Bos		= 0x02,		/* byte order select */
+	Las		= 0x04,		/* long address select */
+	Ls		= 0x08,		/* loopback select */
+	Arm		= 0x10,		/* auto-initialise remote */
+	Ft0		= 0x20,		/* FIFO threshold select */
+	Ft1		= 0x40,
+	Ft1WORD		= 0x00,
+	Ft2WORD		= Ft0,
+	Ft4WORD		= Ft1,
+	Ft6WORD		= Ft1|Ft0,
+};
+
+enum {					/* Tcr */
+	Crc		= 0x01,		/* inhibit CRC */
+	Lb0		= 0x02,		/* encoded loopback control */
+	Lb1		= 0x04,
+	LpbkNORMAL	= 0x00,		/* normal operation */
+	LpbkNIC		= Lb0,		/* internal NIC module loopback */
+	LpbkENDEC	= Lb1,		/* internal ENDEC module loopback */
+	LpbkEXTERNAL	= Lb1|Lb0,	/* external loopback */
+	Atd		= 0x08,		/* auto transmit disable */
+	Ofst		= 0x10,		/* collision offset enable */
+};
+
+enum {					/* Tsr */
+	Ptxok		= 0x01,		/* packet transmitted */
+	Col		= 0x04,		/* transmit collided */
+	Abt		= 0x08,		/* tranmit aborted */
+	Crs		= 0x10,		/* carrier sense lost */
+	Fu		= 0x20,		/* FIFO underrun */
+	Cdh		= 0x40,		/* CD heartbeat */
+	Owc		= 0x80,		/* out of window collision */
+};
+
+enum {					/* Rcr */
+	Sep		= 0x01,		/* save errored packets */
+	Ar		= 0x02,		/* accept runt packets */
+	Ab		= 0x04,		/* accept broadcast */
+	Am		= 0x08,		/* accept multicast */
+	Pro		= 0x10,		/* promiscuous physical */
+	Mon		= 0x20,		/* monitor mode */
+};
+
+enum {					/* Rsr */
+	Prxok		= 0x01,		/* packet received intact */
+	Crce		= 0x02,		/* CRC error */
+	Fae		= 0x04,		/* frame alignment error */
+	Fo		= 0x08,		/* FIFO overrun */
+	Mpa		= 0x10,		/* missed packet */
+	Phy		= 0x20,		/* physical/multicast address */
+	Dis		= 0x40,		/* receiver disabled */
+	Dfr		= 0x80,		/* deferring */
+};
+
+typedef struct {
+	uchar	status;
+	uchar	next;
+	uchar	len0;
+	uchar	len1;
+} Hdr;
+
+void
+dp8390getea(Ether* ether, uchar* ea)
+{
+	Dp8390 *ctlr;
+	uchar cr;
+	int i;
+
+	ctlr = ether->ctlr;
+
+	/*
+	 * Get the ethernet address from the chip.
+	 * Take care to restore the command register
+	 * afterwards.
+	 */
+	ilock(ctlr);
+	cr = regr(ctlr, Cr) & ~Txp;
+	regw(ctlr, Cr, Page1|(~(Ps1|Ps0) & cr));
+	for(i = 0; i < Eaddrlen; i++)
+		ea[i] = regr(ctlr, Par0+i);
+	regw(ctlr, Cr, cr);
+	iunlock(ctlr);
+}
+
+void
+dp8390setea(Ether* ether)
+{
+	int i;
+	uchar cr;
+	Dp8390 *ctlr;
+
+	ctlr = ether->ctlr;
+
+	/*
+	 * Set the ethernet address into the chip.
+	 * Take care to restore the command register
+	 * afterwards. Don't care about multicast
+	 * addresses as multicast is never enabled
+	 * (currently).
+	 */
+	ilock(ctlr);
+	cr = regr(ctlr, Cr) & ~Txp;
+	regw(ctlr, Cr, Page1|(~(Ps1|Ps0) & cr));
+	for(i = 0; i < Eaddrlen; i++)
+		regw(ctlr, Par0+i, ether->ea[i]);
+	regw(ctlr, Cr, cr);
+	iunlock(ctlr);
+}
+
+static void*
+_dp8390read(Dp8390* ctlr, void* to, ulong from, ulong len)
+{
+	uchar cr;
+	int timo;
+
+	/*
+	 * Read some data at offset 'from' in the card's memory
+	 * using the DP8390 remote DMA facility, and place it at
+	 * 'to' in main memory, via the I/O data port.
+	 */
+	cr = regr(ctlr, Cr) & ~Txp;
+	regw(ctlr, Cr, Page0|RdABORT|Sta);
+	regw(ctlr, Isr, Rdc);
+
+	/*
+	 * Set up the remote DMA address and count.
+	 */
+	len = ROUNDUP(len, ctlr->width);
+	regw(ctlr, Rbcr0, len & 0xFF);
+	regw(ctlr, Rbcr1, (len>>8) & 0xFF);
+	regw(ctlr, Rsar0, from & 0xFF);
+	regw(ctlr, Rsar1, (from>>8) & 0xFF);
+
+	/*
+	 * Start the remote DMA read and suck the data
+	 * out of the I/O port.
+	 */
+	regw(ctlr, Cr, Page0|RdREAD|Sta);
+	rdread(ctlr, to, len);
+
+	/*
+	 * Wait for the remote DMA to complete. The timeout
+	 * is necessary because this routine may be called on
+	 * a non-existent chip during initialisation and, due
+	 * to the miracles of the bus, it's possible to get this
+	 * far and still be talking to a slot full of nothing.
+	 */
+	for(timo = 10000; (regr(ctlr, Isr) & Rdc) == 0 && timo; timo--)
+			;
+
+	regw(ctlr, Isr, Rdc);
+	regw(ctlr, Cr, cr);
+
+	return to;
+}
+
+void*
+dp8390read(Dp8390* ctlr, void* to, ulong from, ulong len)
+{
+	void *v;
+
+	ilock(ctlr);
+	v = _dp8390read(ctlr, to, from, len);
+	iunlock(ctlr);
+
+	return v;
+}
+
+static void*
+dp8390write(Dp8390* ctlr, ulong to, void* from, ulong len)
+{
+	ulong crda;
+	uchar cr;
+	int timo, width;
+
+top:
+	/*
+	 * Write some data to offset 'to' in the card's memory
+	 * using the DP8390 remote DMA facility, reading it at
+	 * 'from' in main memory, via the I/O data port.
+	 */
+	cr = regr(ctlr, Cr) & ~Txp;
+	regw(ctlr, Cr, Page0|RdABORT|Sta);
+	regw(ctlr, Isr, Rdc);
+
+	len = ROUNDUP(len, ctlr->width);
+
+	/*
+	 * Set up the remote DMA address and count.
+	 * This is straight from the DP8390[12D] datasheet,
+	 * hence the initial set up for read.
+	 * Assumption here that the A7000 EtherV card will
+	 * never need a dummyrr.
+	 */
+	if(ctlr->dummyrr && (ctlr->width == 1 || ctlr->width == 2)){
+		if(ctlr->width == 2)
+			width = 1;
+		else
+			width = 0;
+		crda = to-1-width;
+		regw(ctlr, Rbcr0, (len+1+width) & 0xFF);
+		regw(ctlr, Rbcr1, ((len+1+width)>>8) & 0xFF);
+		regw(ctlr, Rsar0, crda & 0xFF);
+		regw(ctlr, Rsar1, (crda>>8) & 0xFF);
+		regw(ctlr, Cr, Page0|RdREAD|Sta);
+	
+		for(timo=0;; timo++){
+			if(timo > 10000){
+				print("ether8390: dummyrr timeout; assuming nodummyrr\n");
+				ctlr->dummyrr = 0;
+				goto top;
+			}
+			crda = regr(ctlr, Crda0);
+			crda |= regr(ctlr, Crda1)<<8;
+			if(crda == to){
+				/*
+				 * Start the remote DMA write and make sure
+				 * the registers are correct.
+				 */
+				regw(ctlr, Cr, Page0|RdWRITE|Sta);
+	
+				crda = regr(ctlr, Crda0);
+				crda |= regr(ctlr, Crda1)<<8;
+				if(crda != to)
+					panic("crda write %d to %d\n", crda, to);
+	
+				break;
+			}
+		}
+	}
+	else{
+		regw(ctlr, Rsar0, to & 0xFF);
+		regw(ctlr, Rsar1, (to>>8) & 0xFF);
+		regw(ctlr, Rbcr0, len & 0xFF);
+		regw(ctlr, Rbcr1, (len>>8) & 0xFF);
+		regw(ctlr, Cr, Page0|RdWRITE|Sta);
+	}
+
+	/*
+	 * Pump the data into the I/O port
+	 * then wait for the remote DMA to finish.
+	 */
+	rdwrite(ctlr, from, len);
+	for(timo = 10000; (regr(ctlr, Isr) & Rdc) == 0 && timo; timo--)
+			;
+
+	regw(ctlr, Isr, Rdc);
+	regw(ctlr, Cr, cr);
+
+	return (void*)to;
+}
+
+static void
+ringinit(Dp8390* ctlr)
+{
+	regw(ctlr, Pstart, ctlr->pstart);
+	regw(ctlr, Pstop, ctlr->pstop);
+	regw(ctlr, Bnry, ctlr->pstop-1);
+
+	regw(ctlr, Cr, Page1|RdABORT|Stp);
+	regw(ctlr, Curr, ctlr->pstart);
+	regw(ctlr, Cr, Page0|RdABORT|Stp);
+
+	ctlr->nxtpkt = ctlr->pstart;
+}
+
+static uchar
+getcurr(Dp8390* ctlr)
+{
+	uchar cr, curr;
+
+	cr = regr(ctlr, Cr) & ~Txp;
+	regw(ctlr, Cr, Page1|(~(Ps1|Ps0) & cr));
+	curr = regr(ctlr, Curr);
+	regw(ctlr, Cr, cr);
+
+	return curr;
+}
+
+static void
+receive(Ether* ether)
+{
+	Dp8390 *ctlr;
+	uchar curr, *p;
+	Hdr hdr;
+	ulong count, data, len;
+	RingBuf *ring;
+
+	ctlr = ether->ctlr;
+	for(curr = getcurr(ctlr); ctlr->nxtpkt != curr; curr = getcurr(ctlr)){
+		data = ctlr->nxtpkt*Dp8390BufSz;
+		if(ctlr->ram)
+			memmove(&hdr, (void*)(ether->mem+data), sizeof(Hdr));
+		else
+			_dp8390read(ctlr, &hdr, data, sizeof(Hdr));
+
+		/*
+		 * Don't believe the upper byte count, work it
+		 * out from the software next-page pointer and
+		 * the current next-page pointer.
+		 */
+		if(hdr.next > ctlr->nxtpkt)
+			len = hdr.next - ctlr->nxtpkt - 1;
+		else
+			len = (ctlr->pstop-ctlr->nxtpkt) + (hdr.next-ctlr->pstart) - 1;
+		if(hdr.len0 > (Dp8390BufSz-sizeof(Hdr)))
+			len--;
+
+		len = ((len<<8)|hdr.len0)-4;
+
+		/*
+		 * Chip is badly scrogged, reinitialise the ring.
+		 */
+		if(hdr.next < ctlr->pstart || hdr.next >= ctlr->pstop
+		  || len < 60 || len > sizeof(Etherpkt)){
+			print("dp8390: H#%2.2ux#%2.2ux#%2.2ux#%2.2ux,%lud\n",
+				hdr.status, hdr.next, hdr.len0, hdr.len1, len);
+			regw(ctlr, Cr, Page0|RdABORT|Stp);
+			ringinit(ctlr);
+			regw(ctlr, Cr, Page0|RdABORT|Sta);
+
+			return;
+		}
+
+		/*
+		 * If it's a good packet read it in to the software buffer.
+		 * If the packet wraps round the hardware ring, read it in
+		 * two pieces.
+		 */
+		ring = &ether->rb[ether->ri];
+		if((hdr.status & (Fo|Fae|Crce|Prxok)) == Prxok && ring->owner == Interface){
+			p = ring->pkt;
+			ring->len = len;
+			data += sizeof(Hdr);
+
+			if((data+len) >= ctlr->pstop*Dp8390BufSz){
+				count = ctlr->pstop*Dp8390BufSz - data;
+				if(ctlr->ram)
+					memmove(p, (void*)(ether->mem+data), count);
+				else
+					_dp8390read(ctlr, p, data, count);
+				p += count;
+				data = ctlr->pstart*Dp8390BufSz;
+				len -= count;
+			}
+			if(len){
+				if(ctlr->ram)
+					memmove(p, (void*)(ether->mem+data), len);
+				else
+					_dp8390read(ctlr, p, data, len);
+			}
+
+			/*
+			 * Copy the packet to whoever wants it.
+			 */
+			ring->owner = Host;
+			ether->ri = NEXT(ether->ri, ether->nrb);
+		}
+
+		/*
+		 * Finished with this packet, update the
+		 * hardware and software ring pointers.
+		 */
+		ctlr->nxtpkt = hdr.next;
+
+		hdr.next--;
+		if(hdr.next < ctlr->pstart)
+			hdr.next = ctlr->pstop-1;
+		regw(ctlr, Bnry, hdr.next);
+	}
+}
+
+static void
+txstart(Ether* ether)
+{
+	int len;
+	Dp8390 *ctlr;
+	RingBuf *ring;
+	uchar minpkt[ETHERMINTU], *rp;
+
+	ctlr = ether->ctlr;
+
+	/*
+	 * This routine is called both from the top level and from interrupt
+	 * level and expects to be called with ctlr already locked.
+	 */
+	if(ether->tbusy)
+		return;
+	ring = &ether->tb[ether->ti];
+	if(ring->owner != Interface)
+		return;
+
+	/*
+	 * Make sure the packet is of minimum length;
+	 * copy it to the card's memory by the appropriate means;
+	 * start the transmission.
+	 */
+	len = ring->len;
+	rp = ring->pkt;
+	if(len < ETHERMINTU){
+		rp = minpkt;
+		memmove(rp, ring->pkt, len);
+		memset(rp+len, 0, ETHERMINTU-len);
+		len = ETHERMINTU;
+	}
+
+	if(ctlr->ram)
+		memmove((void*)(ether->mem+ctlr->tstart*Dp8390BufSz), rp, len);
+	else
+		dp8390write(ctlr, ctlr->tstart*Dp8390BufSz, rp, len);
+
+	regw(ctlr, Tbcr0, len & 0xFF);
+	regw(ctlr, Tbcr1, (len>>8) & 0xFF);
+	regw(ctlr, Cr, Page0|RdABORT|Txp|Sta);
+
+	ether->tbusy = 1;
+}
+
+static void
+transmit(Ether* ether)
+{
+	Dp8390 *ctlr;
+
+	ctlr = ether->ctlr;
+
+	ilock(ctlr);
+	txstart(ether);
+	iunlock(ctlr);
+}
+
+static void
+overflow(Ether *ether)
+{
+	Dp8390 *ctlr;
+	uchar txp;
+	int resend;
+
+	ctlr = ether->ctlr;
+
+	/*
+	 * The following procedure is taken from the DP8390[12D] datasheet,
+	 * it seems pretty adamant that this is what has to be done.
+	 */
+	txp = regr(ctlr, Cr) & Txp;
+	regw(ctlr, Cr, Page0|RdABORT|Stp);
+	delay(2);
+	regw(ctlr, Rbcr0, 0);
+	regw(ctlr, Rbcr1, 0);
+
+	resend = 0;
+	if(txp && (regr(ctlr, Isr) & (Txe|Ptx)) == 0)
+		resend = 1;
+
+	regw(ctlr, Tcr, LpbkNIC);
+	regw(ctlr, Cr, Page0|RdABORT|Sta);
+	receive(ether);
+	regw(ctlr, Isr, Ovw);
+	regw(ctlr, Tcr, LpbkNORMAL);
+
+	if(resend)
+		regw(ctlr, Cr, Page0|RdABORT|Txp|Sta);
+}
+
+static void
+interrupt(Ureg*, void* arg)
+{
+	Ether *ether;
+	Dp8390 *ctlr;
+	RingBuf *ring;
+	uchar isr, r;
+
+	ether = arg;
+	ctlr = ether->ctlr;
+
+	/*
+	 * While there is something of interest,
+	 * clear all the interrupts and process.
+	 */
+	ilock(ctlr);
+	regw(ctlr, Imr, 0x00);
+	while(isr = (regr(ctlr, Isr) & (Cnt|Ovw|Txe|Rxe|Ptx|Prx))){
+		if(isr & Ovw){
+			overflow(ether);
+			regw(ctlr, Isr, Ovw);
+		}
+
+		/*
+		 * Packets have been received.
+		 * Take a spin round the ring.
+		 */
+		if(isr & (Rxe|Prx)){
+			receive(ether);
+			regw(ctlr, Isr, Rxe|Prx);
+		}
+
+		/*
+		 * A packet completed transmission, successfully or
+		 * not. Start transmission on the next buffered packet,
+		 * and wake the output routine.
+		 */
+		if(isr & (Txe|Ptx)){
+			r = regr(ctlr, Tsr);
+			if((isr & Txe) && (r & (Cdh|Fu|Crs|Abt))){
+				print("dp8390: Tsr#%2.2ux|", r);
+			}
+
+			regw(ctlr, Isr, Txe|Ptx);
+
+			ring = &ether->tb[ether->ti];
+			ring->owner = Host;
+			ether->ti = NEXT(ether->ti, ether->ntb);
+			ether->tbusy = 0;
+			txstart(ether);
+		}
+
+		if(isr & Cnt){
+			regr(ctlr, Cntr0);
+			regr(ctlr, Cntr1);
+			regr(ctlr, Cntr2);
+			regw(ctlr, Isr, Cnt);
+		}
+	}
+	regw(ctlr, Imr, Cnt|Ovw|Txe|Rxe|Ptx|Prx);
+	iunlock(ctlr);
+}
+
+static void
+attach(Ether* ether)
+{
+	Dp8390 *ctlr;
+	uchar r;
+
+	ctlr = ether->ctlr;
+
+	/*
+	 * Enable the chip for transmit/receive.
+	 * The init routine leaves the chip in monitor
+	 * mode. Clear the missed-packet counter, it
+	 * increments while in monitor mode.
+	 * Sometimes there's an interrupt pending at this
+	 * point but there's nothing in the Isr, so
+	 * any pending interrupts are cleared and the
+	 * mask of acceptable interrupts is enabled here.
+	 */
+	r = Ab;
+	ilock(ctlr);
+	regw(ctlr, Isr, 0xFF);
+	regw(ctlr, Imr, Cnt|Ovw|Txe|Rxe|Ptx|Prx);
+	regw(ctlr, Rcr, r);
+	r = regr(ctlr, Cntr2);
+	regw(ctlr, Tcr, LpbkNORMAL);
+	iunlock(ctlr);
+	USED(r);
+}
+
+static void
+detach(Ether* ether)
+{
+	int timo;
+	Dp8390 *ctlr;
+
+	/*
+	 * Stop the chip. Set the Stp bit and wait for the chip
+	 * to finish whatever was on its tiny mind before it sets
+	 * the Rst bit.
+	 * The timeout is needed because there may not be a real
+	 * chip there if this is called when probing for a device
+	 * at boot.
+	 */
+	ctlr = ether->ctlr;
+	regw(ctlr, Cr, Page0|RdABORT|Stp);
+	regw(ctlr, Rbcr0, 0);
+	regw(ctlr, Rbcr1, 0);
+	for(timo = 10000; (regr(ctlr, Isr) & Rst) == 0 && timo; timo--)
+			;
+}
+
+int
+dp8390reset(Ether* ether)
+{
+	Dp8390 *ctlr;
+
+	ctlr = ether->ctlr;
+
+	/*
+	 * This is the initialisation procedure described
+	 * as 'mandatory' in the datasheet, with references
+	 * to the 3C503 technical reference manual.
+	 */ 
+	detach(ether);
+	if(ctlr->width != 1)
+		regw(ctlr, Dcr, Ft4WORD|Ls|Wts);
+	else
+		regw(ctlr, Dcr, Ft4WORD|Ls);
+
+	regw(ctlr, Rbcr0, 0);
+	regw(ctlr, Rbcr1, 0);
+
+	regw(ctlr, Tcr, LpbkNIC);
+	regw(ctlr, Rcr, Mon);
+
+	/*
+	 * Init the ring hardware and software ring pointers.
+	 * Can't initialise ethernet address as it may not be
+	 * known yet.
+	 */
+	ringinit(ctlr);
+	regw(ctlr, Tpsr, ctlr->tstart);
+
+	/*
+	 * Clear any pending interrupts and mask then all off.
+	 */
+	regw(ctlr, Isr, 0xFF);
+	regw(ctlr, Imr, 0);
+
+	/*
+	 * Leave the chip initialised,
+	 * but in monitor mode.
+	 */
+	regw(ctlr, Cr, Page0|RdABORT|Sta);
+
+	/*
+	 * Set up the software configuration.
+	 */
+	ether->attach = attach;
+	ether->transmit = transmit;
+	ether->interrupt = interrupt;
+	ether->detach = detach;
+
+	return 0;
+}
--- /dev/null
+++ b/os/boot/pc/ether8390.h
@@ -1,0 +1,71 @@
+/*
+ * Ctlr for the boards using the National Semiconductor DP8390
+ * and SMC 83C90 Network Interface Controller.
+ * Common code is in ether8390.c.
+ */
+typedef struct {
+	Lock;
+
+	ulong	port;			/* I/O address of 8390 */
+	ulong	data;			/* I/O data port if no shared memory */
+
+	uchar	width;			/* data transfer width in bytes */
+	uchar	ram;			/* true if card has shared memory */
+	uchar	dummyrr;		/* do dummy remote read */
+
+	uchar	nxtpkt;			/* receive: software bndry */
+	uchar	pstart;
+	uchar	pstop;
+
+	int	txbusy;			/* transmit */
+	uchar	tstart;			/* 8390 ring addresses */
+} Dp8390;
+
+#define Dp8390BufSz	256
+
+extern int dp8390reset(Ether*);
+extern void *dp8390read(Dp8390*, void*, ulong, ulong);
+extern void dp8390getea(Ether*, uchar*);
+extern void dp8390setea(Ether*);
+
+/*
+ * x86-specific code.
+ */
+#define regr(c, r)	inb((c)->port+(r))
+#define regw(c, r, v)	outb((c)->port+(r), (v))
+
+static void
+rdread(Dp8390* ctlr, void* to, int len)
+{
+	switch(ctlr->width){
+	default:
+		panic("dp8390 rdread: width %d\n", ctlr->width);
+		break;
+
+	case 2:
+		inss(ctlr->data, to, len/2);
+		break;
+
+	case 1:
+		insb(ctlr->data, to, len);
+		break;
+	}
+}
+
+static void
+rdwrite(Dp8390* ctlr, void* from, int len)
+{
+	switch(ctlr->width){
+	default:
+		panic("dp8390 rdwrite: width %d\n", ctlr->width);
+		break;
+
+	case 2:
+		outss(ctlr->data, from, len/2);
+		break;
+
+	case 1:
+		outsb(ctlr->data, from, len);
+		break;
+	}
+}
--- /dev/null
+++ b/os/boot/pc/etherdp83820.c
@@ -1,0 +1,1222 @@
+/*
+ * boot driver for
+ * National Semiconductor DP83820
+ * 10/100/1000 Mb/s Ethernet Network Interface Controller
+ * (Gig-NIC).
+ * Driver assumes little-endian and 32-bit host throughout.
+ */
+#include "u.h"
+#include "lib.h"
+#include "mem.h"
+#include "dat.h"
+#include "fns.h"
+#include "io.h"
+
+#include "etherif.h"
+#include "ethermii.h"
+
+#define iprint print
+#define waserror()	(0)
+#define poperror()
+
+enum {					/* Registers */
+	Cr		= 0x00,		/* Command */
+	Cfg		= 0x04,		/* Configuration and Media Status */
+	Mear		= 0x08,		/* MII/EEPROM Access */
+	Ptscr		= 0x0C,		/* PCI Test Control */
+	Isr		= 0x10,		/* Interrupt Status */
+	Imr		= 0x14,		/* Interrupt Mask */
+	Ier		= 0x18,		/* Interrupt Enable */
+	Ihr		= 0x1C,		/* Interrupt Holdoff */
+	Txdp		= 0x20,		/* Transmit Descriptor Pointer */
+	Txdphi		= 0x24,		/* Transmit Descriptor Pointer Hi */
+	Txcfg		= 0x28,		/* Transmit Configuration */
+	Gpior		= 0x2C,		/* General Purpose I/O Control */
+	Rxdp		= 0x30,		/* Receive Descriptor Pointer */
+	Rxdphi		= 0x34,		/* Receive Descriptor Pointer Hi */
+	Rxcfg		= 0x38,		/* Receive Configuration */
+	Pqcr		= 0x3C,		/* Priority Queueing Control */
+	Wcsr		= 0x40,		/* Wake on LAN Control/Status */
+	Pcr		= 0x44,		/* Pause Control/Status */
+	Rfcr		= 0x48,		/* Receive Filter/Match Control */
+	Rfdr		= 0x4C,		/* Receive Filter/Match Data */
+	Brar		= 0x50,		/* Boot ROM Address */
+	Brdr		= 0x54,		/* Boot ROM Data */
+	Srr		= 0x58,		/* Silicon Revision */
+	Mibc		= 0x5C,		/* MIB Control */
+	Mibd		= 0x60,		/* MIB Data */
+	Txdp1		= 0xA0,		/* Txdp Priority 1 */
+	Txdp2		= 0xA4,		/* Txdp Priority 2 */
+	Txdp3		= 0xA8,		/* Txdp Priority 3 */
+	Rxdp1		= 0xB0,		/* Rxdp Priority 1 */
+	Rxdp2		= 0xB4,		/* Rxdp Priority 2 */
+	Rxdp3		= 0xB8,		/* Rxdp Priority 3 */
+	Vrcr		= 0xBC,		/* VLAN/IP Receive Control */
+	Vtcr		= 0xC0,		/* VLAN/IP Transmit Control */
+	Vdr		= 0xC4,		/* VLAN Data */
+	Ccsr		= 0xCC,		/* Clockrun Control/Status */
+	Tbicr		= 0xE0,		/* TBI Control */
+	Tbisr		= 0xE4,		/* TBI Status */
+	Tanar		= 0xE8,		/* TBI ANAR */
+	Tanlpar		= 0xEC,		/* TBI ANLPAR */
+	Taner		= 0xF0,		/* TBI ANER */
+	Tesr		= 0xF4,		/* TBI ESR */
+};
+
+enum {					/* Cr */
+	Txe		= 0x00000001,	/* Transmit Enable */
+	Txd		= 0x00000002,	/* Transmit Disable */
+	Rxe		= 0x00000004,	/* Receiver Enable */
+	Rxd		= 0x00000008,	/* Receiver Disable */
+	Txr		= 0x00000010,	/* Transmitter Reset */
+	Rxr		= 0x00000020,	/* Receiver Reset */
+	Swien		= 0x00000080,	/* Software Interrupt Enable */
+	Rst		= 0x00000100,	/* Reset */
+	TxpriSHFT	= 9,		/* Tx Priority Queue Select */
+	TxpriMASK	= 0x00001E00,
+	RxpriSHFT	= 13,		/* Rx Priority Queue Select */
+	RxpriMASK	= 0x0001E000,
+};
+
+enum {					/* Configuration and Media Status */
+	Bem		= 0x00000001,	/* Big Endian Mode */
+	Ext125		= 0x00000002,	/* External 125MHz reference Select */
+	Bromdis		= 0x00000004,	/* Disable Boot ROM interface */
+	Pesel		= 0x00000008,	/* Parity Error Detection Action */
+	Exd		= 0x00000010,	/* Excessive Deferral Abort */
+	Pow		= 0x00000020,	/* Program Out of Window Timer */
+	Sb		= 0x00000040,	/* Single Back-off */
+	Reqalg		= 0x00000080,	/* PCI Bus Request Algorithm */
+	Extstsen	= 0x00000100,	/* Extended Status Enable */
+	Phydis		= 0x00000200,	/* Disable PHY */
+	Phyrst		= 0x00000400,	/* Reset PHY */
+	M64addren	= 0x00000800,	/* Master 64-bit Addressing Enable */
+	Data64en	= 0x00001000,	/* 64-bit Data Enable */
+	Pci64det	= 0x00002000,	/* PCI 64-bit Bus Detected */
+	T64addren	= 0x00004000,	/* Target 64-bit Addressing Enable */
+	Mwidis		= 0x00008000,	/* MWI Disable */
+	Mrmdis		= 0x00010000,	/* MRM Disable */
+	Tmrtest		= 0x00020000,	/* Timer Test Mode */
+	Spdstsien	= 0x00040000,	/* PHY Spdsts Interrupt Enable */
+	Lnkstsien	= 0x00080000,	/* PHY Lnksts Interrupt Enable */
+	Dupstsien	= 0x00100000,	/* PHY Dupsts Interrupt Enable */
+	Mode1000	= 0x00400000,	/* 1000Mb/s Mode Control */
+	Tbien		= 0x01000000,	/* Ten-Bit Interface Enable */
+	Dupsts		= 0x10000000,	/* Full Duplex Status */
+	Spdsts100	= 0x20000000,	/* SPEED100 Input Pin Status */
+	Spdsts1000	= 0x40000000,	/* SPEED1000 Input Pin Status */
+	Lnksts		= 0x80000000,	/* Link Status */
+};
+
+enum {					/* MII/EEPROM Access */
+	Eedi		= 0x00000001,	/* EEPROM Data In */
+	Eedo		= 0x00000002,	/* EEPROM Data Out */
+	Eeclk		= 0x00000004,	/* EEPROM Serial Clock */
+	Eesel		= 0x00000008,	/* EEPROM Chip Select */
+	Mdio		= 0x00000010,	/* MII Management Data */
+	Mddir		= 0x00000020,	/* MII Management Direction */
+	Mdc		= 0x00000040,	/* MII Management Clock */
+};
+
+enum {					/* Interrupts */
+	Rxok		= 0x00000001,	/* Rx OK */
+	Rxdesc		= 0x00000002,	/* Rx Descriptor */
+	Rxerr		= 0x00000004,	/* Rx Packet Error */
+	Rxearly		= 0x00000008,	/* Rx Early Threshold */
+	Rxidle		= 0x00000010,	/* Rx Idle */
+	Rxorn		= 0x00000020,	/* Rx Overrun */
+	Txok		= 0x00000040,	/* Tx Packet OK */
+	Txdesc		= 0x00000080,	/* Tx Descriptor */
+	Txerr		= 0x00000100,	/* Tx Packet Error */
+	Txidle		= 0x00000200,	/* Tx Idle */
+	Txurn		= 0x00000400,	/* Tx Underrun */
+	Mib		= 0x00000800,	/* MIB Service */
+	Swi		= 0x00001000,	/* Software Interrupt */
+	Pme		= 0x00002000,	/* Power Management Event */
+	Phy		= 0x00004000,	/* PHY Interrupt */
+	Hibint		= 0x00008000,	/* High Bits Interrupt Set */
+	Rxsovr		= 0x00010000,	/* Rx Status FIFO Overrun */
+	Rtabt		= 0x00020000,	/* Received Target Abort */
+	Rmabt		= 0x00040000,	/* Received Master Abort */
+	Sserr		= 0x00080000,	/* Signalled System Error */
+	Dperr		= 0x00100000,	/* Detected Parity Error */
+	Rxrcmp		= 0x00200000,	/* Receive Reset Complete */
+	Txrcmp		= 0x00400000,	/* Transmit Reset Complete */
+	Rxdesc0		= 0x00800000,	/* Rx Descriptor for Priority Queue 0 */
+	Rxdesc1		= 0x01000000,	/* Rx Descriptor for Priority Queue 1 */
+	Rxdesc2		= 0x02000000,	/* Rx Descriptor for Priority Queue 2 */
+	Rxdesc3		= 0x04000000,	/* Rx Descriptor for Priority Queue 3 */
+	Txdesc0		= 0x08000000,	/* Tx Descriptor for Priority Queue 0 */
+	Txdesc1		= 0x10000000,	/* Tx Descriptor for Priority Queue 1 */
+	Txdesc2		= 0x20000000,	/* Tx Descriptor for Priority Queue 2 */
+	Txdesc3		= 0x40000000,	/* Tx Descriptor for Priority Queue 3 */
+};
+
+enum {					/* Interrupt Enable */
+	Ien		= 0x00000001,	/* Interrupt Enable */
+};
+
+enum {					/* Interrupt Holdoff */
+	IhSHFT		= 0,		/* Interrupt Holdoff */
+	IhMASK		= 0x000000FF,
+	Ihctl		= 0x00000100,	/* Interrupt Holdoff Control */
+};
+
+enum {					/* Transmit Configuration */
+	TxdrthSHFT	= 0,		/* Tx Drain Threshold */
+	TxdrthMASK	= 0x000000FF,
+	FlthSHFT	= 16,		/* Tx Fill Threshold */
+	FlthMASK	= 0x0000FF00,
+	Brstdis		= 0x00080000,	/* 1000Mb/s Burst Disable */
+	MxdmaSHFT	= 20,		/* Max Size per Tx DMA Burst */
+	MxdmaMASK	= 0x00700000,
+	Ecretryen	= 0x00800000,	/* Excessive Collision Retry Enable */
+	Atp		= 0x10000000,	/* Automatic Transmit Padding */
+	Mlb		= 0x20000000,	/* MAC Loopback */
+	Hbi		= 0x40000000,	/* Heartbeat Ignore */
+	Csi		= 0x80000000,	/* Carrier Sense Ignore */
+};
+
+enum {					/* Receive Configuration */
+	RxdrthSHFT	= 1,		/* Rx Drain Threshold */
+	RxdrthMASK	= 0x0000003E,
+	Airl		= 0x04000000,	/* Accept In-Range Length Errored */
+	Alp		= 0x08000000,	/* Accept Long Packets */
+	Rxfd		= 0x10000000,	/* Receive Full Duplex */
+	Stripcrc	= 0x20000000,	/* Strip CRC */
+	Arp		= 0x40000000,	/* Accept Runt Packets */
+	Aep		= 0x80000000,	/* Accept Errored Packets */
+};
+
+enum {					/* Priority Queueing Control */
+	Txpqen		= 0x00000001,	/* Transmit Priority Queuing Enable */
+	Txfairen	= 0x00000002,	/* Transmit Fairness Enable */
+	RxpqenSHFT	= 2,		/* Receive Priority Queue Enable */
+	RxpqenMASK	= 0x0000000C,
+};
+
+enum {					/* Pause Control/Status */
+	PscntSHFT	= 0,		/* Pause Counter Value */
+	PscntMASK	= 0x0000FFFF,
+	Pstx		= 0x00020000,	/* Transmit Pause Frame */
+	PsffloSHFT	= 18,		/* Rx Data FIFO Lo Threshold */
+	PsffloMASK	= 0x000C0000,
+	PsffhiSHFT	= 20,		/* Rx Data FIFO Hi Threshold */
+	PsffhiMASK	= 0x00300000,
+	PsstloSHFT	= 22,		/* Rx Stat FIFO Hi Threshold */
+	PsstloMASK	= 0x00C00000,
+	PssthiSHFT	= 24,		/* Rx Stat FIFO Hi Threshold */
+	PssthiMASK	= 0x03000000,
+	Psrcvd		= 0x08000000,	/* Pause Frame Received */
+	Psact		= 0x10000000,	/* Pause Active */
+	Psda		= 0x20000000,	/* Pause on Destination Address */
+	Psmcast		= 0x40000000,	/* Pause on Multicast */
+	Psen		= 0x80000000,	/* Pause Enable */
+};
+
+enum {					/* Receive Filter/Match Control */
+	RfaddrSHFT	= 0,		/* Extended Register Address */
+	RfaddrMASK	= 0x000003FF,
+	Ulm		= 0x00080000,	/* U/L bit mask */
+	Uhen		= 0x00100000,	/* Unicast Hash Enable */
+	Mhen		= 0x00200000,	/* Multicast Hash Enable */
+	Aarp		= 0x00400000,	/* Accept ARP Packets */
+	ApatSHFT	= 23,		/* Accept on Pattern Match */
+	ApatMASK	= 0x07800000,
+	Apm		= 0x08000000,	/* Accept on Perfect Match */
+	Aau		= 0x10000000,	/* Accept All Unicast */
+	Aam		= 0x20000000,	/* Accept All Multicast */
+	Aab		= 0x40000000,	/* Accept All Broadcast */
+	Rfen		= 0x80000000,	/* Rx Filter Enable */
+};
+
+enum {					/* Receive Filter/Match Data */
+	RfdataSHFT	= 0,		/* Receive Filter Data */
+	RfdataMASK	= 0x0000FFFF,
+	BmaskSHFT	= 16,		/* Byte Mask */
+	BmaskMASK	= 0x00030000,
+};
+
+enum {					/* MIB Control */
+	Wrn		= 0x00000001,	/* Warning Test Indicator */
+	Frz		= 0x00000002,	/* Freeze All Counters */
+	Aclr		= 0x00000004,	/* Clear All Counters */
+	Mibs		= 0x00000008,	/* MIB Counter Strobe */
+};
+
+enum {					/* MIB Data */
+	Nmibd		= 11,		/* Number of MIB Data Registers */
+};
+
+enum {					/* VLAN/IP Receive Control */
+	Vtden		= 0x00000001,	/* VLAN Tag Detection Enable */
+	Vtren		= 0x00000002,	/* VLAN Tag Removal Enable */
+	Dvtf		= 0x00000004,	/* Discard VLAN Tagged Frames */
+	Dutf		= 0x00000008,	/* Discard Untagged Frames */
+	Ipen		= 0x00000010,	/* IP Checksum Enable */
+	Ripe		= 0x00000020,	/* Reject IP Checksum Errors */
+	Rtcpe		= 0x00000040,	/* Reject TCP Checksum Errors */
+	Rudpe		= 0x00000080,	/* Reject UDP Checksum Errors */
+};
+
+enum {					/* VLAN/IP Transmit Control */
+	Vgti		= 0x00000001,	/* VLAN Global Tag Insertion */
+	Vppti		= 0x00000002,	/* VLAN Per-Packet Tag Insertion */
+	Gchk		= 0x00000004,	/* Global Checksum Generation */
+	Ppchk		= 0x00000008,	/* Per-Packet Checksum Generation */
+};
+
+enum {					/* VLAN Data */
+	VtypeSHFT	= 0,		/* VLAN Type Field */
+	VtypeMASK	= 0x0000FFFF,
+	VtciSHFT	= 16,		/* VLAN Tag Control Information */
+	VtciMASK	= 0xFFFF0000,
+};
+
+enum {					/* Clockrun Control/Status */
+	Clkrunen	= 0x00000001,	/* CLKRUN Enable */
+	Pmeen		= 0x00000100,	/* PME Enable */
+	Pmests		= 0x00008000,	/* PME Status */
+};
+
+typedef struct {
+	u32int	link;			/* Link to the next descriptor */
+	u32int	bufptr;			/* pointer to data Buffer */
+	int	cmdsts;			/* Command/Status */
+	int	extsts;			/* optional Extended Status */
+
+	Block*	bp;			/* Block containing bufptr */
+	u32int	unused;			/* pad to 64-bit */
+} Desc;
+
+enum {					/* Common cmdsts bits */
+	SizeMASK	= 0x0000FFFF,	/* Descriptor Byte Count */
+	SizeSHFT	= 0,
+	Ok		= 0x08000000,	/* Packet OK */
+	Crc		= 0x10000000,	/* Suppress/Include CRC */
+	Intr		= 0x20000000,	/* Interrupt on ownership transfer */
+	More		= 0x40000000,	/* not last descriptor in a packet */
+	Own		= 0x80000000,	/* Descriptor Ownership */
+};
+
+enum {					/* Transmit cmdsts bits */
+	CcntMASK	= 0x000F0000,	/* Collision Count */
+	CcntSHFT	= 16,
+	Ec		= 0x00100000,	/* Excessive Collisions */
+	Owc		= 0x00200000,	/* Out of Window Collision */
+	Ed		= 0x00400000,	/* Excessive Deferral */
+	Td		= 0x00800000,	/* Transmit Deferred */
+	Crs		= 0x01000000,	/* Carrier Sense Lost */
+	Tfu		= 0x02000000,	/* Transmit FIFO Underrun */
+	Txa		= 0x04000000,	/* Transmit Abort */
+};
+
+enum {					/* Receive cmdsts bits */
+	Irl		= 0x00010000,	/* In-Range Length Error */
+	Lbp		= 0x00020000,	/* Loopback Packet */
+	Fae		= 0x00040000,	/* Frame Alignment Error */
+	Crce		= 0x00080000,	/* CRC Error */
+	Ise		= 0x00100000,	/* Invalid Symbol Error */
+	Runt		= 0x00200000,	/* Runt Packet Received */
+	Long		= 0x00400000,	/* Too Long Packet Received */
+	DestMASK	= 0x01800000,	/* Destination Class */
+	DestSHFT	= 23,
+	Rxo		= 0x02000000,	/* Receive Overrun */
+	Rxa		= 0x04000000,	/* Receive Aborted */
+};
+
+enum {					/* extsts bits */
+	EvtciMASK	= 0x0000FFFF,	/* VLAN Tag Control Information */
+	EvtciSHFT	= 0,
+	Vpkt		= 0x00010000,	/* VLAN Packet */
+	Ippkt		= 0x00020000,	/* IP Packet */
+	Iperr		= 0x00040000,	/* IP Checksum Error */
+	Tcppkt		= 0x00080000,	/* TCP Packet */
+	Tcperr		= 0x00100000,	/* TCP Checksum Error */
+	Udppkt		= 0x00200000,	/* UDP Packet */
+	Udperr		= 0x00400000,	/* UDP Checksum Error */
+};
+
+enum {
+	Nrd		= 32,		/* was 256 */
+	Nrbf		= 4*Nrd,
+	Rbsz		= ROUNDUP(sizeof(Etherpkt)+8, 8),
+	Ntd		= 8,		/* was 128 */
+};
+
+typedef struct Ctlr Ctlr;
+struct Ctlr {
+	int	port;
+	Pcidev*	pcidev;
+	Ctlr*	next;
+	int	active;
+	int	id;
+
+	int	eepromsz;		/* address size in bits */
+	ushort*	eeprom;
+
+	int*	nic;
+	int	cfg;
+	int	imr;
+
+	Lock	alock;			/* attach */
+	Lock	ilock;			/* init */
+	void*	alloc;			/* base of per-Ctlr allocated data */
+
+	Mii*	mii;
+
+	Lock	rdlock;			/* receive */
+	Desc*	rd;
+	int	nrd;
+	int	nrb;
+	int	rdx;
+	int	rxcfg;
+
+	Lock	tlock;			/* transmit */
+	Desc*	td;
+	int	ntd;
+	int	tdh;
+	int	tdt;
+	int	ntq;
+	int	txcfg;
+
+	int	rxidle;
+
+	uint	mibd[Nmibd];
+
+	int	ec;
+	int	owc;
+	int	ed;
+	int	crs;
+	int	tfu;
+	int	txa;
+};
+
+#define csr32r(c, r)	(*((c)->nic+((r)/4)))
+#define csr32w(c, r, v)	(*((c)->nic+((r)/4)) = (v))
+
+static Ctlr* dp83820ctlrhead;
+static Ctlr* dp83820ctlrtail;
+
+static Lock dp83820rblock;		/* free receive Blocks */
+static Block* dp83820rbpool;
+
+static char* dp83820mibs[Nmibd] = {
+	"RXErroredPkts",
+	"RXFCSErrors",
+	"RXMsdPktErrors",
+	"RXFAErrors",
+	"RXSymbolErrors",
+	"RXFrameToLong",
+	"RXIRLErrors",
+	"RXBadOpcodes",
+	"RXPauseFrames",
+	"TXPauseFrames",
+	"TXSQEErrors",
+};
+
+static int
+mdior(Ctlr* ctlr, int n)
+{
+	int data, i, mear, r;
+
+	mear = csr32r(ctlr, Mear);
+	r = ~(Mdc|Mddir) & mear;
+	data = 0;
+	for(i = n-1; i >= 0; i--){
+		if(csr32r(ctlr, Mear) & Mdio)
+			data |= (1<<i);
+		csr32w(ctlr, Mear, Mdc|r);
+		csr32w(ctlr, Mear, r);
+	}
+	csr32w(ctlr, Mear, mear);
+
+	return data;
+}
+
+static void
+mdiow(Ctlr* ctlr, int bits, int n)
+{
+	int i, mear, r;
+
+	mear = csr32r(ctlr, Mear);
+	r = Mddir|(~Mdc & mear);
+	for(i = n-1; i >= 0; i--){
+		if(bits & (1<<i))
+			r |= Mdio;
+		else
+			r &= ~Mdio;
+		csr32w(ctlr, Mear, r);
+		csr32w(ctlr, Mear, Mdc|r);
+	}
+	csr32w(ctlr, Mear, mear);
+}
+
+static int
+dp83820miimir(Mii* mii, int pa, int ra)
+{
+	int data;
+	Ctlr *ctlr;
+
+	ctlr = mii->ctlr;
+
+	/*
+	 * MII Management Interface Read.
+	 *
+	 * Preamble;
+	 * ST+OP+PA+RA;
+	 * LT + 16 data bits.
+	 */
+	mdiow(ctlr, 0xFFFFFFFF, 32);
+	mdiow(ctlr, 0x1800|(pa<<5)|ra, 14);
+	data = mdior(ctlr, 18);
+
+	if(data & 0x10000)
+		return -1;
+
+	return data & 0xFFFF;
+}
+
+static int
+dp83820miimiw(Mii* mii, int pa, int ra, int data)
+{
+	Ctlr *ctlr;
+
+	ctlr = mii->ctlr;
+
+	/*
+	 * MII Management Interface Write.
+	 *
+	 * Preamble;
+	 * ST+OP+PA+RA+LT + 16 data bits;
+	 * Z.
+	 */
+	mdiow(ctlr, 0xFFFFFFFF, 32);
+	data &= 0xFFFF;
+	data |= (0x05<<(5+5+2+16))|(pa<<(5+2+16))|(ra<<(2+16))|(0x02<<16);
+	mdiow(ctlr, data, 32);
+
+	return 0;
+}
+
+static Block *
+dp83820rballoc(Desc* desc)
+{
+	Block *bp;
+
+	if(desc->bp == nil){
+		ilock(&dp83820rblock);
+		if((bp = dp83820rbpool) == nil){
+			iunlock(&dp83820rblock);
+			desc->bp = nil;
+			desc->cmdsts = Own;
+			return nil;
+		}
+		dp83820rbpool = bp->next;
+		bp->next = nil;
+		iunlock(&dp83820rblock);
+	
+		desc->bufptr = PCIWADDR(bp->rp);
+		desc->bp = bp;
+	}
+	else{
+		bp = desc->bp;
+		bp->rp = bp->lim - Rbsz;
+		bp->wp = bp->rp;
+	}
+
+	coherence();
+	desc->cmdsts = Intr|Rbsz;
+
+	return bp;
+}
+
+static void
+dp83820rbfree(Block *bp)
+{
+	bp->rp = bp->lim - Rbsz;
+	bp->wp = bp->rp;
+
+	ilock(&dp83820rblock);
+	bp->next = dp83820rbpool;
+	dp83820rbpool = bp;
+	iunlock(&dp83820rblock);
+}
+
+static void
+dp83820halt(Ctlr* ctlr)
+{
+	int i, timeo;
+
+	ilock(&ctlr->ilock);
+	csr32w(ctlr, Imr, 0);
+	csr32w(ctlr, Ier, 0);
+	csr32w(ctlr, Cr, Rxd|Txd);
+	for(timeo = 0; timeo < 1000; timeo++){
+		if(!(csr32r(ctlr, Cr) & (Rxe|Txe)))
+			break;
+		microdelay(1);
+	}
+	csr32w(ctlr, Mibc, Frz);
+	iunlock(&ctlr->ilock);
+
+	if(ctlr->rd != nil){
+		for(i = 0; i < ctlr->nrd; i++){
+			if(ctlr->rd[i].bp == nil)
+				continue;
+			freeb(ctlr->rd[i].bp);
+			ctlr->rd[i].bp = nil;
+		}
+	}
+	if(ctlr->td != nil){
+		for(i = 0; i < ctlr->ntd; i++){
+			if(ctlr->td[i].bp == nil)
+				continue;
+			freeb(ctlr->td[i].bp);
+			ctlr->td[i].bp = nil;
+		}
+	}
+}
+
+static void
+dp83820cfg(Ctlr* ctlr)
+{
+	int cfg;
+
+	/*
+	 * Don't know how to deal with a TBI yet.
+	 */
+	if(ctlr->mii == nil)
+		return;
+
+	/*
+	 * The polarity of these bits is at the mercy
+	 * of the board designer.
+	 * The correct answer for all speed and duplex questions
+	 * should be to query the phy.
+	 */
+	cfg = csr32r(ctlr, Cfg);
+	if(!(cfg & Dupsts)){
+		ctlr->rxcfg |= Rxfd;
+		ctlr->txcfg |= Csi|Hbi;
+		iprint("83820: full duplex, ");
+	}
+	else{
+		ctlr->rxcfg &= ~Rxfd;
+		ctlr->txcfg &= ~(Csi|Hbi);
+		iprint("83820: half duplex, ");
+	}
+	csr32w(ctlr, Rxcfg, ctlr->rxcfg);
+	csr32w(ctlr, Txcfg, ctlr->txcfg);
+
+	switch(cfg & (Spdsts1000|Spdsts100)){
+	case Spdsts1000:		/* 100Mbps */
+	default:			/* 10Mbps */
+		ctlr->cfg &= ~Mode1000;
+		if((cfg & (Spdsts1000|Spdsts100)) == Spdsts1000)
+			iprint("100Mb/s\n");
+		else
+			iprint("10Mb/s\n");
+		break;
+	case Spdsts100:			/* 1Gbps */
+		ctlr->cfg |= Mode1000;
+		iprint("1Gb/s\n");
+		break;
+	}
+	csr32w(ctlr, Cfg, ctlr->cfg);
+}
+
+static void
+dp83820init(Ether* edev)
+{
+	int i;
+	Ctlr *ctlr;
+	Desc *desc;
+	uchar *alloc;
+
+	ctlr = edev->ctlr;
+
+	dp83820halt(ctlr);
+
+	/*
+	 * Receiver
+	 */
+	alloc = (uchar*)ROUNDUP((ulong)ctlr->alloc, 8);
+	ctlr->rd = (Desc*)alloc;
+	alloc += ctlr->nrd*sizeof(Desc);
+	memset(ctlr->rd, 0, ctlr->nrd*sizeof(Desc));
+	ctlr->rdx = 0;
+	for(i = 0; i < ctlr->nrd; i++){
+		desc = &ctlr->rd[i];
+		desc->link = PCIWADDR(&ctlr->rd[NEXT(i, ctlr->nrd)]);
+		if(dp83820rballoc(desc) == nil)
+			continue;
+	}
+	csr32w(ctlr, Rxdphi, 0);
+	csr32w(ctlr, Rxdp, PCIWADDR(ctlr->rd));
+
+	for(i = 0; i < Eaddrlen; i += 2){
+		csr32w(ctlr, Rfcr, i);
+		csr32w(ctlr, Rfdr, (edev->ea[i+1]<<8)|edev->ea[i]);
+	}
+	csr32w(ctlr, Rfcr, Rfen|Aab|Aam|Apm);
+
+	ctlr->rxcfg = Stripcrc|(((2*(ETHERMINTU+4))/8)<<RxdrthSHFT);
+	ctlr->imr |= Rxorn|Rxidle|Rxearly|Rxdesc|Rxok;
+
+	/*
+	 * Transmitter.
+	 */
+	ctlr->td = (Desc*)alloc;
+	memset(ctlr->td, 0, ctlr->ntd*sizeof(Desc));
+	ctlr->tdh = ctlr->tdt = ctlr->ntq = 0;
+	for(i = 0; i < ctlr->ntd; i++){
+		desc = &ctlr->td[i];
+		desc->link = PCIWADDR(&ctlr->td[NEXT(i, ctlr->ntd)]);
+	}
+	csr32w(ctlr, Txdphi, 0);
+	csr32w(ctlr, Txdp, PCIWADDR(ctlr->td));
+
+	ctlr->txcfg = Atp|(((2*(ETHERMINTU+4))/32)<<FlthSHFT)|((4096/32)<<TxdrthSHFT);
+	ctlr->imr |= Txurn|Txidle|Txdesc|Txok;
+
+	ilock(&ctlr->ilock);
+
+	dp83820cfg(ctlr);
+
+	csr32w(ctlr, Mibc, Aclr);
+	ctlr->imr |= Mib;
+
+	csr32w(ctlr, Imr, ctlr->imr);
+
+	/* try coalescing adjacent interrupts; use hold-off interval of 100µs */
+	csr32w(ctlr, Ihr, Ihctl|(1<<IhSHFT));
+
+	csr32w(ctlr, Ier, Ien);
+	csr32w(ctlr, Cr, Rxe|Txe);
+
+	iunlock(&ctlr->ilock);
+}
+
+static void
+dp83820attach(Ether* edev)
+{
+	Block *bp;
+	Ctlr *ctlr;
+
+	ctlr = edev->ctlr;
+	lock(&ctlr->alock);
+	if(ctlr->alloc != nil){
+		unlock(&ctlr->alock);
+		return;
+	}
+
+	if(waserror()){
+err:
+		if(ctlr->mii != nil){
+			free(ctlr->mii);
+			ctlr->mii = nil;
+		}
+		if(ctlr->alloc != nil){
+			free(ctlr->alloc);
+			ctlr->alloc = nil;
+		}
+		unlock(&ctlr->alock);
+		return;
+	}
+
+	if(!(ctlr->cfg & Tbien)){
+		if((ctlr->mii = malloc(sizeof(Mii))) == nil)
+			goto err;
+		ctlr->mii->ctlr = ctlr;
+		ctlr->mii->mir = dp83820miimir;
+		ctlr->mii->miw = dp83820miimiw;
+		if(mii(ctlr->mii, ~0) == 0)
+			goto err;
+		ctlr->cfg |= Dupstsien|Lnkstsien|Spdstsien;
+		ctlr->imr |= Phy;
+	}
+
+	ctlr->nrd = Nrd;
+	ctlr->nrb = Nrbf;
+	ctlr->ntd = Ntd;
+	ctlr->alloc = mallocz((ctlr->nrd+ctlr->ntd)*sizeof(Desc) + 7, 0);
+	if(ctlr->alloc == nil)
+		goto err;
+
+	/*
+	 * the Bill Paul bsd drivers claim that receive buffers must be aligned
+	 * on 8-byte boundaries.
+	 */
+	for(ctlr->nrb = 0; ctlr->nrb < Nrbf; ctlr->nrb++){
+		if((bp = allocb(Rbsz+8-1)) == nil)
+			break;
+		bp->rp += 8 - (uintptr)bp->rp % 8;
+		bp->wp = bp->rp;
+//		bp->free = dp83820rbfree;	/* TODO: fix */
+		dp83820rbfree(bp);
+	}
+
+	dp83820init(edev);
+
+	unlock(&ctlr->alock);
+	poperror();
+}
+
+/*
+ *  free a list of blocks
+ */
+static void
+freeblist(Block *b)
+{
+	Block *next;
+
+	for(; b != 0; b = next){
+		next = b->next;
+		b->next = 0;
+		freeb(b);
+	}
+}
+
+static void
+toringbuf(Ether *ether, Block *bp)
+{
+	RingBuf *rb = &ether->rb[ether->ri];
+
+	if (rb->owner == Interface) {
+		rb->len = BLEN(bp);
+		memmove(rb->pkt, bp->rp, rb->len);
+		rb->owner = Host;
+		ether->ri = NEXT(ether->ri, ether->nrb);
+	}
+	/* else no one is expecting packets from the network */
+}
+
+static Block *
+fromringbuf(Ether *ether)
+{
+	RingBuf *tb = &ether->tb[ether->ti];
+	Block *bp = allocb(tb->len);
+
+	memmove(bp->wp, tb->pkt, tb->len);
+	memmove(bp->wp+Eaddrlen, ether->ea, Eaddrlen);
+	bp->wp += tb->len;
+	return bp;
+}
+
+static void
+dp83820transmit(Ether* edev)
+{
+	Block *bp;
+	Ctlr *ctlr;
+	Desc *desc;
+	RingBuf *tb;
+	int cmdsts, r, x;
+
+	ctlr = edev->ctlr;
+
+	ilock(&ctlr->tlock);
+
+	bp = nil;
+	for(x = ctlr->tdh; ctlr->ntq; x = NEXT(x, ctlr->ntd)){
+		desc = &ctlr->td[x];
+		if((cmdsts = desc->cmdsts) & Own)
+			break;
+		if(!(cmdsts & Ok)){
+			if(cmdsts & Ec)
+				ctlr->ec++;
+			if(cmdsts & Owc)
+				ctlr->owc++;
+			if(cmdsts & Ed)
+				ctlr->ed++;
+			if(cmdsts & Crs)
+				ctlr->crs++;
+			if(cmdsts & Tfu)
+				ctlr->tfu++;
+			if(cmdsts & Txa)
+				ctlr->txa++;
+		}
+		desc->bp->next = bp;
+		bp = desc->bp;
+		desc->bp = nil;
+
+		ctlr->ntq--;
+	}
+	ctlr->tdh = x;
+	if(bp != nil)
+		freeblist(bp);
+
+	x = ctlr->tdt;
+//	tb = &edev->tb[edev->ti];
+	while(ctlr->ntq < ctlr->ntd - 1 /* && tb->owner == Interface */ ){
+		bp = fromringbuf(edev);
+		if (bp == nil)
+			break;
+
+		/* transmit packet from bp */
+		desc = &ctlr->td[x];
+		desc->bufptr = PCIWADDR(bp->rp);
+		desc->bp = bp;
+		ctlr->ntq++;
+		coherence();
+		desc->cmdsts = Own|Intr|BLEN(bp);	/* kick transmitter */
+//print("t");
+		tb = &edev->tb[edev->ti];
+		tb->owner = Host;		/* give descriptor back */
+		edev->ti = NEXT(edev->ti, edev->ntb);
+
+		x = NEXT(x, ctlr->ntd);
+	}
+	if(x != ctlr->tdt){
+		ctlr->tdt = x;
+		r = csr32r(ctlr, Cr);
+		csr32w(ctlr, Cr, Txe|r);
+	}
+
+	iunlock(&ctlr->tlock);
+}
+
+static void
+dp83820interrupt(Ureg*, void* arg)
+{
+	Block *bp;
+	Ctlr *ctlr;
+	Desc *desc;
+	Ether *edev;
+	int cmdsts, i, isr, r, x;
+
+	edev = arg;
+	ctlr = edev->ctlr;
+
+	for(isr = csr32r(ctlr, Isr); isr & ctlr->imr; isr = csr32r(ctlr, Isr)){
+		if(isr & (Rxorn|Rxidle|Rxearly|Rxerr|Rxdesc|Rxok)){
+			x = ctlr->rdx;
+			desc = &ctlr->rd[x];
+			while((cmdsts = desc->cmdsts) & Own){
+				if((cmdsts & Ok) && desc->bp != nil){
+					/* receive a packet into desc->bp */
+					bp = desc->bp;
+					desc->bp = nil;
+					// bp->rp = desc->bufptr;
+					bp->wp += cmdsts & SizeMASK;
+
+					toringbuf(edev, bp);
+//print("r");
+				}
+				dp83820rballoc(desc);
+
+				x = NEXT(x, ctlr->nrd);
+				desc = &ctlr->rd[x];
+			}
+			ctlr->rdx = x;
+
+			if(isr & Rxidle){
+				r = csr32r(ctlr, Cr);
+				csr32w(ctlr, Cr, Rxe|r);
+				ctlr->rxidle++;
+			}
+
+			isr &= ~(Rxorn|Rxidle|Rxearly|Rxerr|Rxdesc|Rxok);
+		}
+
+		if(isr & Txurn){
+			x = (ctlr->txcfg & TxdrthMASK)>>TxdrthSHFT;
+			r = (ctlr->txcfg & FlthMASK)>>FlthSHFT;
+			if(x < ((TxdrthMASK)>>TxdrthSHFT)
+			&& x < (2048/32 - r)){
+				ctlr->txcfg &= ~TxdrthMASK;
+				x++;
+				ctlr->txcfg |= x<<TxdrthSHFT;
+				csr32w(ctlr, Txcfg, ctlr->txcfg);
+			}
+		}
+
+		if(isr & (Txurn|Txidle|Txdesc|Txok)){
+			dp83820transmit(edev);
+			isr &= ~(Txurn|Txidle|Txdesc|Txok);
+		}
+
+		if(isr & Mib){
+			for(i = 0; i < Nmibd; i++){
+				r = csr32r(ctlr, Mibd+(i*sizeof(int)));
+				ctlr->mibd[i] += r & 0xFFFF;
+			}
+			isr &= ~Mib;
+		}
+
+		if((isr & Phy) && ctlr->mii != nil){
+			ctlr->mii->mir(ctlr->mii, 1, Bmsr);
+			print("phy: cfg %8.8uX bmsr %4.4uX\n",
+				csr32r(ctlr, Cfg),
+				ctlr->mii->mir(ctlr->mii, 1, Bmsr));
+			dp83820cfg(ctlr);
+			isr &= ~Phy;
+		}
+// TODO fix	if(isr)
+// TODO fix		iprint("dp83820: isr %8.8uX\n", isr);
+		USED(isr);
+	}
+}
+
+static int
+dp83820detach(Ctlr* ctlr)
+{
+	/*
+	 * Soft reset the controller.
+	 */
+	csr32w(ctlr, Cr, Rst);
+	delay(1);
+	while(csr32r(ctlr, Cr) & Rst)
+		delay(1);
+	return 0;
+}
+
+static void
+dp83820shutdown(Ether* ether)
+{
+print("dp83820shutdown\n");
+	dp83820detach(ether->ctlr);
+}
+
+static int
+atc93c46r(Ctlr* ctlr, int address)
+{
+	int data, i, mear, r, size;
+
+	/*
+	 * Analog Technology, Inc. ATC93C46
+	 * or equivalent serial EEPROM.
+	 */
+	mear = csr32r(ctlr, Mear);
+	mear &= ~(Eesel|Eeclk|Eedo|Eedi);
+	r = Eesel|mear;
+
+reread:
+	csr32w(ctlr, Mear, r);
+	data = 0x06;
+	for(i = 3-1; i >= 0; i--){
+		if(data & (1<<i))
+			r |= Eedi;
+		else
+			r &= ~Eedi;
+		csr32w(ctlr, Mear, r);
+		csr32w(ctlr, Mear, Eeclk|r);
+		microdelay(1);
+		csr32w(ctlr, Mear, r);
+		microdelay(1);
+	}
+
+	/*
+	 * First time through must work out the EEPROM size.
+	 */
+	if((size = ctlr->eepromsz) == 0)
+		size = 8;
+
+	for(size = size-1; size >= 0; size--){
+		if(address & (1<<size))
+			r |= Eedi;
+		else
+			r &= ~Eedi;
+		csr32w(ctlr, Mear, r);
+		microdelay(1);
+		csr32w(ctlr, Mear, Eeclk|r);
+		microdelay(1);
+		csr32w(ctlr, Mear, r);
+		microdelay(1);
+		if(!(csr32r(ctlr, Mear) & Eedo))
+			break;
+	}
+	r &= ~Eedi;
+
+	data = 0;
+	for(i = 16-1; i >= 0; i--){
+		csr32w(ctlr, Mear, Eeclk|r);
+		microdelay(1);
+		if(csr32r(ctlr, Mear) & Eedo)
+			data |= (1<<i);
+		csr32w(ctlr, Mear, r);
+		microdelay(1);
+	}
+
+	csr32w(ctlr, Mear, mear);
+
+	if(ctlr->eepromsz == 0){
+		ctlr->eepromsz = 8-size;
+		ctlr->eeprom = malloc((1<<ctlr->eepromsz)*sizeof(ushort));
+		goto reread;
+	}
+
+	return data;
+}
+
+static int
+dp83820reset(Ctlr* ctlr)
+{
+	int i, r;
+	unsigned char sum;
+
+	/*
+	 * Soft reset the controller;
+	 * read the EEPROM to get the initial settings
+	 * of the Cfg and Gpior bits which should be cleared by
+	 * the reset.
+	 */
+	csr32w(ctlr, Cr, Rst);
+	delay(1);
+	while(csr32r(ctlr, Cr) & Rst)
+		delay(1);
+
+	atc93c46r(ctlr, 0);
+	if(ctlr->eeprom == nil) {
+		print("dp83820reset: no eeprom\n");
+		return -1;
+	}
+	sum = 0;
+	for(i = 0; i < 0x0E; i++){
+		r = atc93c46r(ctlr, i);
+		ctlr->eeprom[i] = r;
+		sum += r;
+		sum += r>>8;
+	}
+
+	if(sum != 0){
+		print("dp83820reset: bad EEPROM checksum\n");
+		return -1;
+	}
+
+#ifdef notdef
+	csr32w(ctlr, Gpior, ctlr->eeprom[4]);
+
+	cfg = Extstsen|Exd;
+	r = csr32r(ctlr, Cfg);
+	if(ctlr->eeprom[5] & 0x0001)
+		cfg |= Ext125;
+	if(ctlr->eeprom[5] & 0x0002)
+		cfg |= M64addren;
+	if((ctlr->eeprom[5] & 0x0004) && (r & Pci64det))
+		cfg |= Data64en;
+	if(ctlr->eeprom[5] & 0x0008)
+		cfg |= T64addren;
+	if(!(pcicfgr16(ctlr->pcidev, PciPCR) & 0x10))
+		cfg |= Mwidis;
+	if(ctlr->eeprom[5] & 0x0020)
+		cfg |= Mrmdis;
+	if(ctlr->eeprom[5] & 0x0080)
+		cfg |= Mode1000;
+	if(ctlr->eeprom[5] & 0x0200)
+		cfg |= Tbien|Mode1000;
+	/*
+	 * What about RO bits we might have destroyed with Rst?
+	 * What about Exd, Tmrtest, Extstsen, Pintctl?
+	 * Why does it think it has detected a 64-bit bus when
+	 * it hasn't?
+	 */
+#else
+	//r = csr32r(ctlr, Cfg);
+	//r &= ~(Mode1000|T64addren|Data64en|M64addren);
+	//csr32w(ctlr, Cfg, r);
+	//csr32w(ctlr, Cfg, 0x2000);
+#endif /* notdef */
+	ctlr->cfg = csr32r(ctlr, Cfg);
+print("cfg %8.8uX pcicfg %8.8uX\n", ctlr->cfg, pcicfgr32(ctlr->pcidev, PciPCR));
+	ctlr->cfg &= ~(T64addren|Data64en|M64addren);
+	csr32w(ctlr, Cfg, ctlr->cfg);
+	csr32w(ctlr, Mibc, Aclr|Frz);
+
+	return 0;
+}
+
+static void
+dp83820pci(void)
+{
+	int port;
+	Pcidev *p;
+	Ctlr *ctlr;
+
+	p = nil;
+	while(p = pcimatch(p, 0, 0)){
+		if(p->ccrb != 0x02 || p->ccru != 0)
+			continue;
+
+		switch((p->did<<16)|p->vid){
+		default:
+			continue;
+		case (0x0022<<16)|0x100B:	/* DP83820 (Gig-NIC) */
+			break;
+		}
+
+		port = upamalloc(p->mem[1].bar & ~0x0F, p->mem[1].size, 0);
+		if(port == 0){
+			print("dp83820: can't map %d @ 0x%8.8luX\n",
+				p->mem[1].size, p->mem[1].bar);
+			continue;
+		}
+
+		ctlr = malloc(sizeof(Ctlr));
+		ctlr->port = port;
+		ctlr->pcidev = p;
+		ctlr->id = p->did<<16 | p->vid;
+
+		ctlr->nic = KADDR(ctlr->port);
+		if(dp83820reset(ctlr)){
+			free(ctlr);
+			continue;
+		}
+		pcisetbme(p);
+
+		if(dp83820ctlrhead != nil)
+			dp83820ctlrtail->next = ctlr;
+		else
+			dp83820ctlrhead = ctlr;
+		dp83820ctlrtail = ctlr;
+	}
+}
+
+int
+dp83820pnp(Ether* edev)
+{
+	int i;
+	Ctlr *ctlr;
+	uchar ea[Eaddrlen];
+
+	if(dp83820ctlrhead == nil)
+		dp83820pci();
+
+	/*
+	 * Any adapter matches if no edev->port is supplied,
+	 * otherwise the ports must match.
+	 */
+	for(ctlr = dp83820ctlrhead; ctlr != nil; ctlr = ctlr->next){
+		if(ctlr->active)
+			continue;
+		if(edev->port == 0 || edev->port == ctlr->port){
+			ctlr->active = 1;
+			break;
+		}
+	}
+	if(ctlr == nil)
+		return -1;
+
+	edev->ctlr = ctlr;
+	edev->port = ctlr->port;
+	edev->irq = ctlr->pcidev->intl;
+	edev->tbdf = ctlr->pcidev->tbdf;
+
+	/*
+	 * Check if the adapter's station address is to be overridden.
+	 * If not, read it from the EEPROM and set in ether->ea prior to
+	 * loading the station address in the hardware.
+	 */
+	memset(ea, 0, Eaddrlen);
+	if(memcmp(ea, edev->ea, Eaddrlen) == 0)
+		for(i = 0; i < Eaddrlen/2; i++){
+			edev->ea[2*i] = ctlr->eeprom[0x0C-i];
+			edev->ea[2*i+1] = ctlr->eeprom[0x0C-i]>>8;
+		}
+
+	edev->attach = dp83820attach;
+	edev->transmit = dp83820transmit;
+	edev->interrupt = dp83820interrupt;
+	edev->detach = dp83820shutdown;
+	return 0;
+}
--- /dev/null
+++ b/os/boot/pc/etherec2t.c
@@ -1,0 +1,155 @@
+/*
+ * Supposed NE2000 PCMCIA clones, see the comments in ether2000.c
+ */
+#include "u.h"
+#include "lib.h"
+#include "mem.h"
+#include "dat.h"
+#include "fns.h"
+#include "io.h"
+
+#include "etherif.h"
+#include "ether8390.h"
+
+enum {
+	Data		= 0x10,		/* offset from I/O base of data port */
+	Reset		= 0x1F,		/* offset from I/O base of reset port */
+};
+
+static char* ec2tpcmcia[] = {
+	"EC2T",				/* Linksys Combo PCMCIA EthernetCard */
+	"PCMPC100",			/* EtherFast 10/100 PC Card */
+	"EN2216",			/* Accton EtherPair-PCMCIA */
+	"FA410TX",			/* Netgear FA410TX */
+	"Network Everywhere",		/* Linksys NP10T 10BaseT Card */
+	nil,
+};
+
+int
+ec2treset(Ether* ether)
+{
+	ushort buf[16];
+	ulong port;
+	Dp8390 *ctlr;
+	int i, slot;
+	uchar ea[Eaddrlen], sum, x;
+	char *type;
+
+	/*
+	 * Set up the software configuration.
+	 * Use defaults for port, irq, mem and size
+	 * if not specified.
+	 * The manual says 16KB memory, the box
+	 * says 32KB. The manual seems to be correct.
+	 */
+	if(ether->port == 0)
+		ether->port = 0x300;
+	if(ether->irq == 0)
+		ether->irq = 9;
+	if(ether->mem == 0)
+		ether->mem = 0x4000;
+	if(ether->size == 0)
+		ether->size = 16*1024;
+	port = ether->port;
+
+	//if(ioalloc(ether->port, 0x20, 0, "ec2t") < 0)
+	//	return -1;
+	slot = -1;
+	type = nil;
+	for(i = 0; ec2tpcmcia[i] != nil; i++){
+		type = ec2tpcmcia[i];
+		if((slot = pcmspecial(type, ether)) >= 0)
+			break;
+	}
+	if(ec2tpcmcia[i] == nil){
+		for(i = 0; i < ether->nopt; i++){
+			if(cistrncmp(ether->opt[i], "id=", 3))
+				continue;
+			type = &ether->opt[i][3];
+			if((slot = pcmspecial(type, ether)) >= 0)
+				break;
+		}
+	}
+	if(slot < 0){
+	//	iofree(port);
+		return -1;
+	}
+
+	ether->ctlr = malloc(sizeof(Dp8390));
+	ctlr = ether->ctlr;
+	ctlr->width = 2;
+	ctlr->ram = 0;
+
+	ctlr->port = port;
+	ctlr->data = port+Data;
+
+	ctlr->tstart = HOWMANY(ether->mem, Dp8390BufSz);
+	ctlr->pstart = ctlr->tstart + HOWMANY(sizeof(Etherpkt), Dp8390BufSz);
+	ctlr->pstop = ctlr->tstart + HOWMANY(ether->size, Dp8390BufSz);
+
+	ctlr->dummyrr = 0;
+	for(i = 0; i < ether->nopt; i++){
+		if(cistrcmp(ether->opt[i], "nodummyrr") == 0)
+			ctlr->dummyrr = 0;
+		else if(cistrncmp(ether->opt[i], "dummyrr=", 8) == 0)
+			ctlr->dummyrr = strtol(&ether->opt[i][8], nil, 0);
+	}
+
+	/*
+	 * Reset the board. This is done by doing a read
+	 * followed by a write to the Reset address.
+	 */
+	buf[0] = inb(port+Reset);
+	delay(2);
+	outb(port+Reset, buf[0]);
+	delay(2);
+
+	/*
+	 * Init the (possible) chip, then use the (possible)
+	 * chip to read the (possible) PROM for ethernet address
+	 * and a marker byte.
+	 * Could just look at the DP8390 command register after
+	 * initialisation has been tried, but that wouldn't be
+	 * enough, there are other ethernet boards which could
+	 * match.
+	 */
+	dp8390reset(ether);
+	sum = 0;
+	if(cistrcmp(type, "PCMPC100") == 0 || cistrcmp(type, "FA410TX") == 0){
+		/*
+		 * The PCMPC100 has the ethernet address in I/O space.
+		 * There's a checksum over 8 bytes which sums to 0xFF.
+		 */
+		for(i = 0; i < 8; i++){
+			x = inb(port+0x14+i);
+			sum += x;
+			buf[i] = (x<<8)|x;
+		}
+	}
+	else{
+		memset(buf, 0, sizeof(buf));
+		dp8390read(ctlr, buf, 0, sizeof(buf));
+		if((buf[0x0E] & 0xFF) == 0x57 && (buf[0x0F] & 0xFF) == 0x57)
+			sum = 0xFF;
+	}
+	if(sum != 0xFF){
+		pcmspecialclose(slot);
+		//iofree(ether->port);
+		free(ether->ctlr);
+		return -1;
+	}
+
+	/*
+	 * Stupid machine. Shorts were asked for,
+	 * shorts were delivered, although the PROM is a byte array.
+	 * Set the ethernet address.
+	 */
+	memset(ea, 0, Eaddrlen);
+	if(memcmp(ea, ether->ea, Eaddrlen) == 0){
+		for(i = 0; i < sizeof(ether->ea); i++)
+			ether->ea[i] = buf[i];
+	}
+	dp8390setea(ether);
+
+	return 0;
+}
--- /dev/null
+++ b/os/boot/pc/etherelnk3.c
@@ -1,0 +1,1897 @@
+/*
+ * Etherlink III, Fast EtherLink and Fast EtherLink XL adapters.
+ * To do:
+ *	check robustness in the face of errors (e.g. busmaster & rxUnderrun);
+ *	RxEarly and busmaster;
+ *	autoSelect;
+ *	PCI latency timer and master enable;
+ *	errata list;
+ *	rewrite all initialisation.
+ *
+ * Product ID:
+ *	9150 ISA	3C509[B]
+ *	9050 ISA	3C509[B]-TP
+ *	9450 ISA	3C509[B]-COMBO
+ *	9550 ISA	3C509[B]-TPO
+ *
+ *	9350 EISA	3C579
+ *	9250 EISA	3C579-TP
+ *
+ *	5920 EISA	3C592-[TP|COMBO|TPO]
+ *	5970 EISA	3C597-TX	Fast Etherlink 10BASE-T/100BASE-TX
+ *	5971 EISA	3C597-T4	Fast Etherlink 10BASE-T/100BASE-T4
+ *	5972 EISA	3C597-MII	Fast Etherlink 10BASE-T/MII
+ *
+ *	5900 PCI	3C590-[TP|COMBO|TPO]
+ *	5950 PCI	3C595-TX	Fast Etherlink Shared 10BASE-T/100BASE-TX
+ *	5951 PCI	3C595-T4	Fast Etherlink Shared 10BASE-T/100BASE-T4
+ *	5952 PCI	3C595-MII	Fast Etherlink 10BASE-T/MII
+ *
+ *	9000 PCI	3C900-TPO	Etherlink III XL PCI 10BASE-T
+ *	9001 PCI	3C900-COMBO	Etherlink III XL PCI 10BASE-T/10BASE-2/AUI
+ *	9005 PCI	3C900B-COMBO	Etherlink III XL PCI 10BASE-T/10BASE-2/AUI
+ *	9050 PCI	3C905-TX	Fast Etherlink XL Shared 10BASE-T/100BASE-TX
+ *	9051 PCI	3C905-T4	Fast Etherlink Shared 10BASE-T/100BASE-T4
+ *	9055 PCI	3C905B-TX	Fast Etherlink Shared 10BASE-T/100BASE-TX
+ *	9200 PCI	3C905C-TX	Fast Etherlink Shared 10BASE-T/100BASE-TX
+ *
+ *	9058 PCMCIA	3C589[B]-[TP|COMBO]
+ *
+ *	627C MCA	3C529
+ *	627D MCA	3C529-TP
+ */
+#include "u.h"
+#include "lib.h"
+#include "mem.h"
+#include "dat.h"
+#include "fns.h"
+#include "io.h"
+
+#include "etherif.h"
+
+#define XCVRDEBUG		if(0)print
+
+enum {
+	IDport			= 0x0110,	/* anywhere between 0x0100 and 0x01F0 */
+};
+
+enum {						/* all windows */
+	CommandR		= 0x000E,
+	IntStatusR		= 0x000E,
+};
+
+enum {						/* Commands */
+	GlobalReset		= 0x0000,
+	SelectRegisterWindow	= 0x0001,
+	EnableDcConverter	= 0x0002,
+	RxDisable		= 0x0003,
+	RxEnable		= 0x0004,
+	RxReset			= 0x0005,
+	Stall			= 0x0006,	/* 3C90x */
+	TxDone			= 0x0007,
+	RxDiscard		= 0x0008,
+	TxEnable		= 0x0009,
+	TxDisable		= 0x000A,
+	TxReset			= 0x000B,
+	RequestInterrupt	= 0x000C,
+	AcknowledgeInterrupt	= 0x000D,
+	SetInterruptEnable	= 0x000E,
+	SetIndicationEnable	= 0x000F,	/* SetReadZeroMask */
+	SetRxFilter		= 0x0010,
+	SetRxEarlyThresh	= 0x0011,
+	SetTxAvailableThresh	= 0x0012,
+	SetTxStartThresh	= 0x0013,
+	StartDma		= 0x0014,	/* initiate busmaster operation */
+	StatisticsEnable	= 0x0015,
+	StatisticsDisable	= 0x0016,
+	DisableDcConverter	= 0x0017,
+	SetTxReclaimThresh	= 0x0018,	/* PIO-only adapters */
+	PowerUp			= 0x001B,	/* not all adapters */
+	PowerDownFull		= 0x001C,	/* not all adapters */
+	PowerAuto		= 0x001D,	/* not all adapters */
+};
+
+enum {						/* (Global|Rx|Tx)Reset command bits */
+	tpAuiReset		= 0x0001,	/* 10BaseT and AUI transceivers */
+	endecReset		= 0x0002,	/* internal Ethernet encoder/decoder */
+	networkReset		= 0x0004,	/* network interface logic */
+	fifoReset		= 0x0008,	/* FIFO control logic */
+	aismReset		= 0x0010,	/* autoinitialise state-machine logic */
+	hostReset		= 0x0020,	/* bus interface logic */
+	dmaReset		= 0x0040,	/* bus master logic */
+	vcoReset		= 0x0080,	/* on-board 10Mbps VCO */
+	updnReset		= 0x0100,	/* upload/download (Rx/TX) logic */
+
+	resetMask		= 0x01FF,
+};
+
+enum {						/* Stall command bits */
+	upStall			= 0x0000,
+	upUnStall		= 0x0001,
+	dnStall			= 0x0002,
+	dnUnStall		= 0x0003,
+};
+
+enum {						/* SetRxFilter command bits */
+	receiveIndividual	= 0x0001,	/* match station address */
+	receiveMulticast	= 0x0002,
+	receiveBroadcast	= 0x0004,
+	receiveAllFrames	= 0x0008,	/* promiscuous */
+};
+
+enum {						/* StartDma command bits */
+	Upload			= 0x0000,	/* transfer data from adapter to memory */
+	Download		= 0x0001,	/* transfer data from memory to adapter */
+};
+
+enum {						/* IntStatus bits */
+	interruptLatch		= 0x0001,
+	hostError		= 0x0002,	/* Adapter Failure */
+	txComplete		= 0x0004,
+	txAvailable		= 0x0008,
+	rxComplete		= 0x0010,
+	rxEarly			= 0x0020,
+	intRequested		= 0x0040,
+	updateStats		= 0x0080,
+	transferInt		= 0x0100,	/* Bus Master Transfer Complete */
+	dnComplete		= 0x0200,
+	upComplete		= 0x0400,
+	busMasterInProgress	= 0x0800,
+	commandInProgress	= 0x1000,
+
+	interruptMask		= 0x07FE,
+};
+
+#define COMMAND(port, cmd, a)	outs((port)+CommandR, ((cmd)<<11)|(a))
+#define STATUS(port)		ins((port)+IntStatusR)
+
+enum {						/* Window 0 - setup */
+	Wsetup			= 0x0000,
+						/* registers */
+	ManufacturerID		= 0x0000,	/* 3C5[08]*, 3C59[27] */
+	ProductID		= 0x0002,	/* 3C5[08]*, 3C59[27] */
+	ConfigControl		= 0x0004,	/* 3C5[08]*, 3C59[27] */
+	AddressConfig		= 0x0006,	/* 3C5[08]*, 3C59[27] */
+	ResourceConfig		= 0x0008,	/* 3C5[08]*, 3C59[27] */
+	EepromCommand		= 0x000A,
+	EepromData		= 0x000C,
+						/* AddressConfig Bits */
+	autoSelect9		= 0x0080,
+	xcvrMask9		= 0xC000,
+						/* ConfigControl bits */
+	Ena			= 0x0001,
+	base10TAvailable9	= 0x0200,
+	coaxAvailable9		= 0x1000,
+	auiAvailable9		= 0x2000,
+						/* EepromCommand bits */
+	_EepromReadRegister	= 0x0080,
+	_EepromRead8bRegister	= 0x0230,
+	EepromBusy		= 0x8000,
+};
+
+static int EepromReadRegister = _EepromReadRegister;
+
+#define EEPROMCMD(port, cmd, a)	outs((port)+EepromCommand, (cmd)|(a))
+#define EEPROMBUSY(port)	(ins((port)+EepromCommand) & EepromBusy)
+#define EEPROMDATA(port)	ins((port)+EepromData)
+
+enum {						/* Window 1 - operating set */
+	Wop			= 0x0001,
+						/* registers */
+	Fifo			= 0x0000,
+	RxError			= 0x0004,	/* 3C59[0257] only */
+	RxStatus		= 0x0008,
+	Timer			= 0x000A,
+	TxStatus		= 0x000B,
+	TxFree			= 0x000C,
+						/* RxError bits */
+	rxOverrun		= 0x0001,
+	runtFrame		= 0x0002,
+	alignmentError		= 0x0004,	/* Framing */
+	crcError		= 0x0008,
+	oversizedFrame		= 0x0010,
+	dribbleBits		= 0x0080,
+						/* RxStatus bits */
+	rxBytes			= 0x1FFF,	/* 3C59[0257] mask */
+	rxBytes9		= 0x07FF,	/* 3C5[078]9 mask */
+	rxError9		= 0x3800,	/* 3C5[078]9 error mask */
+	rxOverrun9		= 0x0000,
+	oversizedFrame9		= 0x0800,
+	dribbleBits9		= 0x1000,
+	runtFrame9		= 0x1800,
+	alignmentError9		= 0x2000,	/* Framing */
+	crcError9		= 0x2800,
+	rxError			= 0x4000,
+	rxIncomplete		= 0x8000,
+						/* TxStatus Bits */
+	txStatusOverflow	= 0x0004,
+	maxCollisions		= 0x0008,
+	txUnderrun		= 0x0010,
+	txJabber		= 0x0020,
+	interruptRequested	= 0x0040,
+	txStatusComplete	= 0x0080,
+};
+
+enum {						/* Window 2 - station address */
+	Wstation		= 0x0002,
+
+	ResetOp905B		= 0x000C,
+};
+
+enum {						/* Window 3 - FIFO management */
+	Wfifo			= 0x0003,
+						/* registers */
+	InternalConfig		= 0x0000,	/* 3C509B, 3C589, 3C59[0257] */
+	OtherInt		= 0x0004,	/* 3C59[0257] */
+	RomControl		= 0x0006,	/* 3C509B, 3C59[27] */
+	MacControl		= 0x0006,	/* 3C59[0257] */
+	ResetOptions		= 0x0008,	/* 3C59[0257] */
+	MediaOptions		= 0x0008,	/* 3C905B */
+	RxFree			= 0x000A,
+						/* InternalConfig bits */
+	disableBadSsdDetect	= 0x00000100,
+	ramLocation		= 0x00000200,	/* 0 external, 1 internal */
+	ramPartition5to3	= 0x00000000,
+	ramPartition3to1	= 0x00010000,
+	ramPartition1to1	= 0x00020000,
+	ramPartition3to5	= 0x00030000,
+	ramPartitionMask	= 0x00030000,
+	xcvr10BaseT		= 0x00000000,
+	xcvrAui			= 0x00100000,	/* 10BASE5 */
+	xcvr10Base2		= 0x00300000,
+	xcvr100BaseTX		= 0x00400000,
+	xcvr100BaseFX		= 0x00500000,
+	xcvrMii			= 0x00600000,
+	xcvrMask		= 0x00700000,
+	autoSelect		= 0x01000000,
+						/* MacControl bits */
+	deferExtendEnable	= 0x0001,
+	deferTimerSelect	= 0x001E,	/* mask */
+	fullDuplexEnable	= 0x0020,
+	allowLargePackets	= 0x0040,
+	extendAfterCollision	= 0x0080,	/* 3C90xB */
+	flowControlEnable	= 0x0100,	/* 3C90xB */
+	vltEnable		= 0x0200,	/* 3C90xB */
+						/* ResetOptions bits */
+	baseT4Available		= 0x0001,
+	baseTXAvailable		= 0x0002,
+	baseFXAvailable		= 0x0004,
+	base10TAvailable	= 0x0008,
+	coaxAvailable		= 0x0010,
+	auiAvailable		= 0x0020,
+	miiConnector		= 0x0040,
+};
+
+enum {						/* Window 4 - diagnostic */
+	Wdiagnostic		= 0x0004,
+						/* registers */
+	VcoDiagnostic		= 0x0002,
+	FifoDiagnostic		= 0x0004,
+	NetworkDiagnostic	= 0x0006,
+	PhysicalMgmt		= 0x0008,
+	MediaStatus		= 0x000A,
+	BadSSD			= 0x000C,
+	UpperBytesOk		= 0x000D,
+						/* FifoDiagnostic bits */
+	txOverrun		= 0x0400,
+	rxUnderrun		= 0x2000,
+	receiving		= 0x8000,
+						/* PhysicalMgmt bits */
+	mgmtClk			= 0x0001,
+	mgmtData		= 0x0002,
+	mgmtDir			= 0x0004,
+	cat5LinkTestDefeat	= 0x8000,
+						/* MediaStatus bits */
+	dataRate100		= 0x0002,
+	crcStripDisable		= 0x0004,
+	enableSqeStats		= 0x0008,
+	collisionDetect		= 0x0010,
+	carrierSense		= 0x0020,
+	jabberGuardEnable	= 0x0040,
+	linkBeatEnable		= 0x0080,
+	jabberDetect		= 0x0200,
+	polarityReversed	= 0x0400,
+	linkBeatDetect		= 0x0800,
+	txInProg		= 0x1000,
+	dcConverterEnabled	= 0x4000,
+	auiDisable		= 0x8000,	/* 10BaseT transceiver selected */
+};
+
+enum {						/* Window 5 - internal state */
+	Wstate			= 0x0005,
+						/* registers */
+	TxStartThresh		= 0x0000,
+	TxAvailableThresh	= 0x0002,
+	RxEarlyThresh		= 0x0006,
+	RxFilter		= 0x0008,
+	InterruptEnable		= 0x000A,
+	IndicationEnable	= 0x000C,
+};
+
+enum {						/* Window 6 - statistics */
+	Wstatistics		= 0x0006,
+						/* registers */
+	CarrierLost		= 0x0000,
+	SqeErrors		= 0x0001,
+	MultipleColls		= 0x0002,
+	SingleCollFrames	= 0x0003,
+	LateCollisions		= 0x0004,
+	RxOverruns		= 0x0005,
+	FramesXmittedOk		= 0x0006,
+	FramesRcvdOk		= 0x0007,
+	FramesDeferred		= 0x0008,
+	UpperFramesOk		= 0x0009,
+	BytesRcvdOk		= 0x000A,
+	BytesXmittedOk		= 0x000C,
+};
+
+enum {						/* Window 7 - bus master operations */
+	Wmaster			= 0x0007,
+						/* registers */
+	MasterAddress		= 0x0000,
+	MasterLen		= 0x0006,
+	MasterStatus		= 0x000C,
+						/* MasterStatus bits */
+	masterAbort		= 0x0001,
+	targetAbort		= 0x0002,
+	targetRetry		= 0x0004,
+	targetDisc		= 0x0008,
+	masterDownload		= 0x1000,
+	masterUpload		= 0x4000,
+	masterInProgress	= 0x8000,
+
+	masterMask		= 0xD00F,
+};
+
+enum {						/* 3C90x extended register set */
+	Timer905		= 0x001A,	/* 8-bits */
+	TxStatus905		= 0x001B,	/* 8-bits */
+	PktStatus		= 0x0020,	/* 32-bits */
+	DnListPtr		= 0x0024,	/* 32-bits, 8-byte aligned */
+	FragAddr		= 0x0028,	/* 32-bits */
+	FragLen			= 0x002C,	/* 16-bits */
+	ListOffset		= 0x002E,	/* 8-bits */
+	TxFreeThresh		= 0x002F,	/* 8-bits */
+	UpPktStatus		= 0x0030,	/* 32-bits */
+	FreeTimer		= 0x0034,	/* 16-bits */
+	UpListPtr		= 0x0038,	/* 32-bits, 8-byte aligned */
+
+						/* PktStatus bits */
+	fragLast		= 0x00000001,
+	dnCmplReq		= 0x00000002,
+	dnStalled		= 0x00000004,
+	upCompleteX		= 0x00000008,
+	dnCompleteX		= 0x00000010,
+	upRxEarlyEnable		= 0x00000020,
+	armCountdown		= 0x00000040,
+	dnInProg		= 0x00000080,
+	counterSpeed		= 0x00000010,	/* 0 3.2uS, 1 320nS */
+	countdownMode		= 0x00000020,
+						/* UpPktStatus bits (dpd->control) */
+	upPktLenMask		= 0x00001FFF,
+	upStalled		= 0x00002000,
+	upError			= 0x00004000,
+	upPktComplete		= 0x00008000,
+	upOverrun		= 0x00010000,	/* RxError<<16 */
+	upRuntFrame		= 0x00020000,
+	upAlignmentError	= 0x00040000,
+	upCRCError		= 0x00080000,
+	upOversizedFrame	= 0x00100000,
+	upDribbleBits		= 0x00800000,
+	upOverflow		= 0x01000000,
+
+	dnIndicate		= 0x80000000,	/* FrameStartHeader (dpd->control) */
+
+	updnLastFrag		= 0x80000000,	/* (dpd->len) */
+
+	Nup			= 32,
+	Ndn			= 64,
+};
+
+/*
+ * Up/Dn Packet Descriptors.
+ * The hardware info (np, control, addr, len) must be 8-byte aligned
+ * and this structure size must be a multiple of 8.
+ */
+typedef struct Pd Pd;
+typedef struct Pd {
+	ulong	np;				/* next pointer */
+	ulong	control;			/* FSH or UpPktStatus */
+	ulong	addr;
+	ulong	len;
+
+	Pd*	next;
+	void *vaddr;
+} Pd;
+
+typedef struct {
+	Lock	wlock;				/* window access */
+
+	int	attached;
+	int	busmaster;
+	Block*	rbp;				/* receive buffer */
+
+	Block*	txbp;				/* FIFO -based transmission */
+	int	txthreshold;
+	int	txbusy;
+
+	int	nup;				/* full-busmaster -based reception */
+	void*	upbase;
+	Pd*	upr;
+	Pd*	uphead;
+
+	int	ndn;				/* full-busmaster -based transmission */
+	void*	dnbase;
+	Pd*	dnr;
+	Pd*	dnhead;
+	Pd*	dntail;
+	int	dnq;
+
+	long	interrupts;			/* statistics */
+	long	timer[2];
+	long	stats[BytesRcvdOk+3];
+
+	int	upqmax;
+	int	upqmaxhw;
+	ulong	upinterrupts;
+	ulong	upqueued;
+	ulong	upstalls;
+	int	dnqmax;
+	int	dnqmaxhw;
+	ulong	dninterrupts;
+	ulong	dnqueued;
+
+	int	xcvr;				/* transceiver type */
+	int	rxstatus9;			/* old-style RxStatus register */
+	int	rxearly;			/* RxEarlyThreshold */
+	int	ts;				/* threshold shift */
+	int	upenabled;
+	int	dnenabled;
+} Ctlr;
+
+static void
+init905(Ctlr* ctlr)
+{
+	Pd *pd, *prev;
+	uchar *vaddr;
+
+	/*
+	 * Create rings for the receive and transmit sides.
+	 * Take care with alignment:
+	 *	make sure ring base is 8-byte aligned;
+	 *	make sure each entry is 8-byte aligned.
+	 */
+	ctlr->upbase = malloc((ctlr->nup+1)*sizeof(Pd));
+	ctlr->upr = (Pd*)ROUNDUP((ulong)ctlr->upbase, 8);
+	vaddr = ialloc((ctlr->nup+1)*ROUNDUP(sizeof(Etherpkt)+4, 8), 8);
+
+	prev = ctlr->upr;
+	for(pd = &ctlr->upr[ctlr->nup-1]; pd >= ctlr->upr; pd--){
+		pd->np = PADDR(&prev->np);
+		pd->control = 0;
+		pd->vaddr = vaddr;
+		pd->addr = PADDR(vaddr);
+		vaddr += ROUNDUP(sizeof(Etherpkt)+4, 8);
+		pd->len = updnLastFrag|sizeof(Etherpkt);
+
+		pd->next = prev;
+		prev = pd;
+	}
+	ctlr->uphead = ctlr->upr;
+
+	ctlr->dnbase = malloc((ctlr->ndn+1)*sizeof(Pd));
+	ctlr->dnr = (Pd*)ROUNDUP((ulong)ctlr->dnbase, 8);
+	vaddr = ialloc((ctlr->ndn+1)*ROUNDUP(sizeof(Etherpkt)+4, 8), 8); 
+
+	prev = ctlr->dnr;
+	for(pd = &ctlr->dnr[ctlr->ndn-1]; pd >= ctlr->dnr; pd--){
+		pd->next = prev;
+		pd->vaddr = vaddr;								
+		pd->addr = PADDR(vaddr);						
+		vaddr += ROUNDUP(sizeof(Etherpkt)+4, 8);		
+		prev = pd;
+	}
+	ctlr->dnhead = ctlr->dnr;
+	ctlr->dntail = ctlr->dnr;
+	ctlr->dnq = 0;
+}
+
+static Block*
+rbpalloc(Block* (*f)(int))
+{
+	Block *bp;
+	ulong addr;
+
+	/*
+	 * The receive buffers must be on a 32-byte
+	 * boundary for EISA busmastering.
+	 */
+	if(bp = f(ROUNDUP(sizeof(Etherpkt), 4) + 31)){
+		addr = (ulong)bp->base;
+		addr = ROUNDUP(addr, 32);
+		bp->rp = (uchar*)addr;
+	}
+
+	return bp;
+}
+
+static uchar*
+startdma(Ether* ether, ulong address)
+{
+	int port, status, w;
+	uchar *wp;
+
+	port = ether->port;
+
+	w = (STATUS(port)>>13) & 0x07;
+	COMMAND(port, SelectRegisterWindow, Wmaster);
+
+	wp = KADDR(inl(port+MasterAddress));
+	status = ins(port+MasterStatus);
+	if(status & (masterInProgress|targetAbort|masterAbort))
+		print("#l%d: BM status 0x%uX\n", ether->ctlrno, status);
+	outs(port+MasterStatus, masterMask);
+	outl(port+MasterAddress, address);
+	outs(port+MasterLen, sizeof(Etherpkt));
+	COMMAND(port, StartDma, Upload);
+
+	COMMAND(port, SelectRegisterWindow, w);
+	return wp;
+}
+
+/* On the 575B and C, interrupts need to be acknowledged in CardBus memory space */
+static void
+intrack3c575(ulong *cbfns)
+{
+	cbfns[1] = 0x8000;
+}
+
+static void
+attach(Ether* ether)
+{
+	int port, x;
+	Ctlr *ctlr;
+
+	ctlr = ether->ctlr;
+	ilock(&ctlr->wlock);
+	if(ctlr->attached){
+		iunlock(&ctlr->wlock);
+		return;
+	}
+
+	port = ether->port;
+
+	COMMAND(port, SetRxFilter, receiveIndividual|receiveBroadcast);
+	x = interruptMask;
+	if(ctlr->busmaster == 1)
+		x &= ~(rxEarly|rxComplete);
+	else{
+		if(ctlr->dnenabled)
+			x &= ~transferInt;
+		if(ctlr->upenabled)
+			x &= ~(rxEarly|rxComplete);
+	}
+	COMMAND(port, SetIndicationEnable, x);
+	COMMAND(port, SetInterruptEnable, x);
+	COMMAND(port, RxEnable, 0);
+	COMMAND(port, TxEnable, 0);
+
+	if (ether->mem)
+		/* This must be a cardbus card.  Acknowledge the interrupt */
+		intrack3c575(KADDR(ether->mem));
+		
+	/*
+	 * Prime the busmaster channel for receiving directly into a
+	 * receive packet buffer if necessary.
+	 */
+	if(ctlr->busmaster == 1)
+		startdma(ether, PADDR(ctlr->rbp->rp));
+	else{
+		if(ctlr->upenabled)
+			outl(port+UpListPtr, PADDR(&ctlr->uphead->np));
+	}
+
+	ctlr->attached = 1;
+	iunlock(&ctlr->wlock);
+
+}
+
+static void
+statistics(Ether* ether)
+{
+	int port, i, u, w;
+	Ctlr *ctlr;
+
+	port = ether->port;
+	ctlr = ether->ctlr;
+
+	/*
+	 * 3C59[27] require a read between a PIO write and
+	 * reading a statistics register.
+	 */
+	w = (STATUS(port)>>13) & 0x07;
+	COMMAND(port, SelectRegisterWindow, Wstatistics);
+	STATUS(port);
+
+	for(i = 0; i < UpperFramesOk; i++)
+		ctlr->stats[i] += inb(port+i) & 0xFF;
+	u = inb(port+UpperFramesOk) & 0xFF;
+	ctlr->stats[FramesXmittedOk] += (u & 0x30)<<4;
+	ctlr->stats[FramesRcvdOk] += (u & 0x03)<<8;
+	ctlr->stats[BytesRcvdOk] += ins(port+BytesRcvdOk) & 0xFFFF;
+	ctlr->stats[BytesRcvdOk+1] += ins(port+BytesXmittedOk) & 0xFFFF;
+
+	switch(ctlr->xcvr){
+
+	case xcvrMii:
+	case xcvr100BaseTX:
+	case xcvr100BaseFX:
+		COMMAND(port, SelectRegisterWindow, Wdiagnostic);
+		STATUS(port);
+		ctlr->stats[BytesRcvdOk+2] += inb(port+BadSSD);
+		break;
+	}
+
+	COMMAND(port, SelectRegisterWindow, w);
+}
+
+static void
+txstart(Ether* ether)
+{
+	int port, len;
+	Ctlr *ctlr;
+	RingBuf *tb;
+
+	port = ether->port;
+	ctlr = ether->ctlr;
+
+	/*
+	 * Attempt to top-up the transmit FIFO. If there's room simply
+	 * stuff in the packet length (unpadded to a dword boundary), the
+	 * packet data (padded) and remove the packet from the queue.
+	 * If there's no room post an interrupt for when there is.
+	 * This routine is called both from the top level and from interrupt
+	 * level and expects to be called with ctlr->wlock already locked
+	 * and the correct register window (Wop) in place.
+	 */
+	for(tb = &ether->tb[ether->ti]; tb->owner == Interface; tb = &ether->tb[ether->ti]){
+		len = ROUNDUP(tb->len, 4);
+		if(len+4 <= ins(port+TxFree)){
+			outl(port+Fifo, tb->len);
+			outsl(port+Fifo, tb->pkt, len/4);
+			tb->owner = Host;
+			ether->ti = NEXT(ether->ti, ether->ntb);
+		}
+		else{
+			if(ctlr->txbusy == 0){
+				ctlr->txbusy = 1;
+				COMMAND(port, SetTxAvailableThresh, len>>ctlr->ts);
+			}
+			break;
+		}
+	}
+}
+
+static void
+txstart905(Ether* ether)
+{
+	Ctlr *ctlr;
+	int port, stalled, timeo;
+	RingBuf *tb;
+	Pd *pd;
+
+	ctlr = ether->ctlr;
+	port = ether->port;
+
+	/*
+	 * Free any completed packets.
+	 */
+	pd = ctlr->dntail;
+	while(ctlr->dnq){
+		if(PADDR(&pd->np) == inl(port+DnListPtr))
+			break;
+		ctlr->dnq--;
+		pd = pd->next;
+	}
+	ctlr->dntail = pd;
+
+	stalled = 0;
+	while(ctlr->dnq < (ctlr->ndn-1)){
+		tb = &ether->tb[ether->ti];
+		if(tb->owner != Interface)
+			break;
+
+		pd = ctlr->dnhead->next;
+		pd->np = 0;
+		pd->control = dnIndicate|tb->len;
+		memmove(pd->vaddr, tb->pkt, tb->len);
+		pd->len = updnLastFrag|tb->len;
+
+		tb->owner = Host;
+		ether->ti = NEXT(ether->ti, ether->ntb);
+
+		if(stalled == 0 && ctlr->dnq && inl(port+DnListPtr)){
+			COMMAND(port, Stall, dnStall);
+			for(timeo = 100; (STATUS(port) & commandInProgress) && timeo; timeo--)
+				;
+			if(timeo == 0)
+				print("#l%d: dnstall %d\n", ether->ctlrno, timeo);
+			stalled = 1;
+		}
+
+		coherence();
+		ctlr->dnhead->np = PADDR(&pd->np);
+		ctlr->dnhead->control &= ~dnIndicate;
+		ctlr->dnhead = pd;
+		if(ctlr->dnq == 0)
+			ctlr->dntail = pd;
+		ctlr->dnq++;
+
+		ctlr->dnqueued++;
+	}
+
+	if(ctlr->dnq > ctlr->dnqmax)
+		ctlr->dnqmax = ctlr->dnq;
+
+	/*
+	 * If the adapter is not currently processing anything
+	 * and there is something on the queue, start it processing.
+	 */
+	if(inl(port+DnListPtr) == 0 && ctlr->dnq)
+		outl(port+DnListPtr, PADDR(&ctlr->dnhead->np));
+	if(stalled)
+		COMMAND(port, Stall, dnUnStall);
+}
+
+static void
+transmit(Ether* ether)
+{
+	Ctlr *ctlr;
+	int port, w;
+
+	port = ether->port;
+	ctlr = ether->ctlr;
+
+	ilock(&ctlr->wlock);
+	if(ctlr->dnenabled)
+		txstart905(ether);
+	else{
+		w = (STATUS(port)>>13) & 0x07;
+		COMMAND(port, SelectRegisterWindow, Wop);
+		txstart(ether);
+		COMMAND(port, SelectRegisterWindow, w);
+	}
+	iunlock(&ctlr->wlock);
+}
+
+static void
+receive905(Ether* ether)
+{
+	Ctlr *ctlr;
+	int len, port, q;
+	Pd *pd;
+	RingBuf *rb;
+
+	ctlr = ether->ctlr;
+	port = ether->port;
+
+	if(inl(port+UpPktStatus) & upStalled)
+		ctlr->upstalls++;
+	q = 0;
+	for(pd = ctlr->uphead; pd->control & upPktComplete; pd = pd->next){
+		if(!(pd->control & upError)){
+			rb = &ether->rb[ether->ri];
+			if (rb->owner == Interface) {
+				len = pd->control & rxBytes;
+				rb->len = len;
+				memmove(rb->pkt, pd->vaddr, len);
+				rb->owner = Host;
+				ether->ri = NEXT(ether->ri, ether->nrb);
+			}
+		}
+
+		pd->control = 0;
+		COMMAND(port, Stall, upUnStall);
+
+		q++;
+	}
+	ctlr->uphead = pd;
+
+	ctlr->upqueued += q;
+	if(q > ctlr->upqmax)
+		ctlr->upqmax = q;
+}
+
+static void
+receive(Ether* ether)
+{
+	int len, port, rxstatus;
+	RingBuf *rb;
+	Ctlr *ctlr;
+
+	port = ether->port;
+	ctlr = ether->ctlr;
+
+	while(((rxstatus = ins(port+RxStatus)) & rxIncomplete) == 0){
+		if(ctlr->busmaster == 1 && (STATUS(port) & busMasterInProgress))
+			break;
+
+		/*
+		 * If there was an error, log it and continue.
+		 * Unfortunately the 3C5[078]9 has the error info in the status register
+		 * and the 3C59[0257] implement a separate RxError register.
+		 */
+		if((rxstatus & rxError) == 0){
+			/*
+			 * Packet received. Read it into the next free
+			 * ring buffer, if any. Must read len bytes padded
+			 * to a doubleword, can be picked out 32-bits at
+			 * a time. The CRC is already stripped off.
+			 */
+			rb = &ether->rb[ether->ri];
+			if(rb->owner == Interface){
+				len = (rxstatus & rxBytes9);
+				rb->len = len;
+				insl(port+Fifo, rb->pkt, HOWMANY(len, 4));
+
+				rb->owner = Host;
+				ether->ri = NEXT(ether->ri, ether->nrb);
+			}else
+if(debug) print("toss...");
+		}
+else
+if(debug) print("error...");
+
+		/*
+		 * All done, discard the packet.
+		 */
+		COMMAND(port, RxDiscard, 0);
+		while(STATUS(port) & commandInProgress)
+			;
+	}
+}
+
+static void
+interrupt(Ureg*, void* arg)
+{
+	Ether *ether;
+	int port, status, s, txstatus, w, x;
+	Ctlr *ctlr;
+
+	ether = arg;
+	port = ether->port;
+	ctlr = ether->ctlr;
+
+	ilock(&ctlr->wlock);
+	status = STATUS(port);
+	if(!(status & (interruptMask|interruptLatch))){
+		iunlock(&ctlr->wlock);
+		return;
+	}
+	w = (status>>13) & 0x07;
+	COMMAND(port, SelectRegisterWindow, Wop);
+
+	ctlr->interrupts++;
+	if(ctlr->busmaster == 2)
+		ctlr->timer[0] += inb(port+Timer905) & 0xFF;
+	else
+		ctlr->timer[0] += inb(port+Timer) & 0xFF;
+
+	do{
+		if(status & hostError){
+			/*
+			 * Adapter failure, try to find out why, reset if
+			 * necessary. What happens if Tx is active and a reset
+			 * occurs, need to retransmit? This probably isn't right.
+			 */
+			COMMAND(port, SelectRegisterWindow, Wdiagnostic);
+			x = ins(port+FifoDiagnostic);
+			COMMAND(port, SelectRegisterWindow, Wop);
+			print("#l%d: status 0x%uX, diag 0x%uX\n",
+			    ether->ctlrno, status, x);
+
+			if(x & txOverrun){
+				if(ctlr->busmaster == 0)
+					COMMAND(port, TxReset, 0);
+				else
+					COMMAND(port, TxReset, (updnReset|dmaReset));
+				COMMAND(port, TxEnable, 0);
+			}
+
+			if(x & rxUnderrun){
+				/*
+				 * This shouldn't happen...
+				 * Reset the receiver and restore the filter and RxEarly
+				 * threshold before re-enabling.
+				 * Need to restart any busmastering?
+				 */
+				COMMAND(port, SelectRegisterWindow, Wstate);
+				s = (port+RxFilter) & 0x000F;
+				COMMAND(port, SelectRegisterWindow, Wop);
+				COMMAND(port, RxReset, 0);
+				while(STATUS(port) & commandInProgress)
+					;
+				COMMAND(port, SetRxFilter, s);
+				COMMAND(port, SetRxEarlyThresh, ctlr->rxearly>>ctlr->ts);
+				COMMAND(port, RxEnable, 0);
+			}
+
+			status &= ~hostError;
+		}
+
+		if(status & (transferInt|rxComplete)){
+			receive(ether);
+			status &= ~(transferInt|rxComplete);
+		}
+
+		if(status & (upComplete)){
+			COMMAND(port, AcknowledgeInterrupt, upComplete);
+			receive905(ether);
+			status &= ~upComplete;
+			ctlr->upinterrupts++;
+		}
+
+		if(status & txComplete){
+			/*
+			 * Pop the TxStatus stack, accumulating errors.
+			 * Adjust the TX start threshold if there was an underrun.
+			 * If there was a Jabber or Underrun error, reset
+			 * the transmitter, taking care not to reset the dma logic
+			 * as a busmaster receive may be in progress.
+			 * For all conditions enable the transmitter.
+			 */
+			if(ctlr->busmaster == 2)
+				txstatus = port+TxStatus905;
+			else
+				txstatus = port+TxStatus;
+			s = 0;
+			do{
+				if(x = inb(txstatus))
+					outb(txstatus, 0);
+				s |= x;
+			}while(STATUS(port) & txComplete);
+
+			if(s & txUnderrun){
+				if(ctlr->dnenabled){
+					while(inl(port+PktStatus) & dnInProg)
+						;
+				}
+				COMMAND(port, SelectRegisterWindow, Wdiagnostic);
+				while(ins(port+MediaStatus) & txInProg)
+					;
+				COMMAND(port, SelectRegisterWindow, Wop);
+				if(ctlr->txthreshold < ETHERMAXTU)
+					ctlr->txthreshold += ETHERMINTU;
+			}
+
+			/*
+			 * According to the manual, maxCollisions does not require
+			 * a TxReset, merely a TxEnable. However, evidence points to
+			 * it being necessary on the 3C905. The jury is still out.
+			 * On busy or badly configured networks maxCollisions can
+			 * happen frequently enough for messages to be annoying so
+			 * keep quiet about them by popular request.
+			 */
+			if(s & (txJabber|txUnderrun|maxCollisions)){
+				if(ctlr->busmaster == 0)
+					COMMAND(port, TxReset, 0);
+				else
+					COMMAND(port, TxReset, (updnReset|dmaReset));
+				while(STATUS(port) & commandInProgress)
+					;
+				COMMAND(port, SetTxStartThresh, ctlr->txthreshold>>ctlr->ts);
+				if(ctlr->busmaster == 2)
+					outl(port+TxFreeThresh, HOWMANY(ETHERMAXTU, 256));
+				if(ctlr->dnenabled)
+					status |= dnComplete;
+			}
+
+			if(s & ~(txStatusComplete|maxCollisions))
+				print("#l%d: txstatus 0x%uX, threshold %d\n",
+			    		ether->ctlrno, s, ctlr->txthreshold);
+			COMMAND(port, TxEnable, 0);
+			status &= ~txComplete;
+			status |= txAvailable;
+		}
+
+		if(status & txAvailable){
+			COMMAND(port, AcknowledgeInterrupt, txAvailable);
+			ctlr->txbusy = 0;
+			txstart(ether);
+			status &= ~txAvailable;
+		}
+
+		if(status & dnComplete){
+			COMMAND(port, AcknowledgeInterrupt, dnComplete);
+			txstart905(ether);
+			status &= ~dnComplete;
+			ctlr->dninterrupts++;
+		}
+
+		if(status & updateStats){
+			statistics(ether);
+			status &= ~updateStats;
+		}
+
+		/*
+		 * Currently, this shouldn't happen.
+		 */
+		if(status & rxEarly){
+			COMMAND(port, AcknowledgeInterrupt, rxEarly);
+			status &= ~rxEarly;
+		}
+
+		/*
+		 * Panic if there are any interrupts not dealt with.
+		 */
+		if(status & interruptMask)
+			panic("#l%d: interrupt mask 0x%uX\n", ether->ctlrno, status);
+
+		COMMAND(port, AcknowledgeInterrupt, interruptLatch);
+		if (ether->mem) 
+			intrack3c575((ulong *)KADDR(ether->mem));
+
+	}while((status = STATUS(port)) & (interruptMask|interruptLatch));
+
+	if(ctlr->busmaster == 2)
+		ctlr->timer[1] += inb(port+Timer905) & 0xFF;
+	else
+		ctlr->timer[1] += inb(port+Timer) & 0xFF;
+
+	COMMAND(port, SelectRegisterWindow, w);
+	iunlock(&ctlr->wlock);
+}
+
+static void
+txrxreset(int port)
+{
+	COMMAND(port, TxReset, 0);
+	while(STATUS(port) & commandInProgress)
+		;
+	COMMAND(port, RxReset, 0);
+	while(STATUS(port) & commandInProgress)
+		;
+}
+
+typedef struct Adapter {
+	int		port;
+	int		irq;
+	int		tbdf;
+	ulong	cbfns;
+} Adapter;
+static Block* adapter;
+
+static void
+tcmadapter(int port, int irq, int tbdf, ulong cbfns)
+{
+	Block *bp;
+	Adapter *ap;
+
+	bp = allocb(sizeof(Adapter));
+	ap = (Adapter*)bp->rp;
+	ap->port = port;
+	ap->irq = irq;
+	ap->tbdf = tbdf;
+	ap->cbfns = cbfns;
+
+	bp->next = adapter;
+	adapter = bp;
+}
+
+/*
+ * Write two 0 bytes to identify the IDport and then reset the
+ * ID sequence. Then send the ID sequence to the card to get
+ * the card into command state.
+ */
+static void
+idseq(void)
+{
+	int i;
+	uchar al;
+	static int reset, untag;
+
+	/*
+	 * One time only:
+	 *	reset any adapters listening
+	 */
+	if(reset == 0){
+		outb(IDport, 0);
+		outb(IDport, 0);
+		outb(IDport, 0xC0);
+		delay(20);
+		reset = 1;
+	}
+
+	outb(IDport, 0);
+	outb(IDport, 0);
+	for(al = 0xFF, i = 0; i < 255; i++){
+		outb(IDport, al);
+		if(al & 0x80){
+			al <<= 1;
+			al ^= 0xCF;
+		}
+		else
+			al <<= 1;
+	}
+
+	/*
+	 * One time only:
+	 *	write ID sequence to get the attention of all adapters;
+	 *	untag all adapters.
+	 * If a global reset is done here on all adapters it will confuse
+	 * any ISA cards configured for EISA mode.
+	 */
+	if(untag == 0){
+		outb(IDport, 0xD0);
+		untag = 1;
+	}
+}
+
+static ulong
+activate(void)
+{
+	int i;
+	ushort x, acr;
+
+	/*
+	 * Do the little configuration dance:
+	 *
+	 * 2. write the ID sequence to get to command state.
+	 */
+	idseq();
+
+	/*
+	 * 3. Read the Manufacturer ID from the EEPROM.
+	 *    This is done by writing the IDPort with 0x87 (0x80
+	 *    is the 'read EEPROM' command, 0x07 is the offset of
+	 *    the Manufacturer ID field in the EEPROM).
+	 *    The data comes back 1 bit at a time.
+	 *    A delay seems necessary between reading the bits.
+	 *
+	 * If the ID doesn't match, there are no more adapters.
+	 */
+	outb(IDport, 0x87);
+	delay(20);
+	for(x = 0, i = 0; i < 16; i++){
+		delay(20);
+		x <<= 1;
+		x |= inb(IDport) & 0x01;
+	}
+	if(x != 0x6D50)
+		return 0;
+
+	/*
+	 * 3. Read the Address Configuration from the EEPROM.
+	 *    The Address Configuration field is at offset 0x08 in the EEPROM).
+	 */
+	outb(IDport, 0x88);
+	for(acr = 0, i = 0; i < 16; i++){
+		delay(20);
+		acr <<= 1;
+		acr |= inb(IDport) & 0x01;
+	}
+
+	return (acr & 0x1F)*0x10 + 0x200;
+}
+
+static void
+tcm509isa(void)
+{
+	int irq, port;
+
+	/*
+	 * Attempt to activate all adapters. If adapter is set for
+	 * EISA mode (0x3F0), tag it and ignore. Otherwise, activate
+	 * it fully.
+	 */
+	while(port = activate()){
+		/*
+		 * 6. Tag the adapter so it won't respond in future.
+		 */
+		outb(IDport, 0xD1);
+		if(port == 0x3F0)
+			continue;
+
+		/*
+		 * 6. Activate the adapter by writing the Activate command
+		 *    (0xFF).
+		 */
+		outb(IDport, 0xFF);
+		delay(20);
+
+		/*
+		 * 8. Can now talk to the adapter's I/O base addresses.
+		 *    Use the I/O base address from the acr just read.
+		 *
+		 *    Enable the adapter and clear out any lingering status
+		 *    and interrupts.
+		 */
+		while(STATUS(port) & commandInProgress)
+			;
+		COMMAND(port, SelectRegisterWindow, Wsetup);
+		outs(port+ConfigControl, Ena);
+
+		txrxreset(port);
+		COMMAND(port, AcknowledgeInterrupt, 0xFF);
+
+		irq = (ins(port+ResourceConfig)>>12) & 0x0F;
+		tcmadapter(port, irq, BUSUNKNOWN, 0);
+	}
+}
+
+static void
+tcm5XXeisa(void)
+{
+	ushort x;
+	int irq, port, slot;
+
+	/*
+	 * Check if this is an EISA machine.
+	 * If not, nothing to do.
+	 */
+	if(strncmp((char*)KADDR(0xFFFD9), "EISA", 4))
+		return;
+
+	/*
+	 * Continue through the EISA slots looking for a match on both
+	 * 3COM as the manufacturer and 3C579-* or 3C59[27]-* as the product.
+	 * If an adapter is found, select window 0, enable it and clear
+	 * out any lingering status and interrupts.
+	 */
+	for(slot = 1; slot < MaxEISA; slot++){
+		port = slot*0x1000;
+		if(ins(port+0xC80+ManufacturerID) != 0x6D50)
+			continue;
+		x = ins(port+0xC80+ProductID);
+		if((x & 0xF0FF) != 0x9050 && (x & 0xFF00) != 0x5900)
+			continue;
+
+		COMMAND(port, SelectRegisterWindow, Wsetup);
+		outs(port+ConfigControl, Ena);
+
+		txrxreset(port);
+		COMMAND(port, AcknowledgeInterrupt, 0xFF);
+
+		irq = (ins(port+ResourceConfig)>>12) & 0x0F;
+		tcmadapter(port, irq, BUSUNKNOWN, 0);
+	}
+}
+
+static void
+tcm59Xpci(Ether *ether)
+{
+	Pcidev *p;
+	int irq, port;
+	ulong bar;
+
+	p = nil;
+	while(p = pcimatch(p, 0x10B7, 0)){
+		if (p->did == 0x5157) {
+			EepromReadRegister = _EepromRead8bRegister;
+			
+			/* Map the CardBus functions */
+			bar = pcicfgr32(p, PciBAR2);
+			print("ether#%d: CardBus functions at %.8luX\n", ether->ctlrno, bar & ~KZERO);
+		}
+		else
+			bar = 0;
+
+		/*
+		 * Not prepared to deal with memory-mapped
+		 * devices yet.
+		 */
+		if(!(p->mem[0].bar & 0x01))
+			continue;
+		port = p->mem[0].bar & ~0x01;
+		irq = p->intl;
+		COMMAND(port, GlobalReset, 0);
+		while(STATUS(port) & commandInProgress)
+			;
+
+		tcmadapter(port, irq, p->tbdf, bar);
+		pcisetbme(p);
+	}
+}
+
+static char* tcmpcmcia[] = {
+	"3C589",			/* 3COM 589[ABCD] */
+	"3C562",			/* 3COM 562 */
+	"589E",				/* 3COM Megahertz 589E */
+	nil,
+};
+
+static int
+tcm5XXpcmcia(Ether* ether)
+{
+	int i;
+
+	for(i = 0; tcmpcmcia[i] != nil; i++){
+		if(!cistrcmp(ether->type, tcmpcmcia[i])){
+			/*
+			 * No need for an ioalloc here, the 589 reset
+			 * code deals with it.
+			if(ioalloc(ether->port, 0x10, 0, "tcm5XXpcmcia") < 0)
+				return 0;
+			 */
+			return ether->port;
+		}
+	}
+
+	return 0;
+}
+
+static void
+setxcvr(int port, int xcvr, int is9)
+{
+	int x;
+
+	if(is9){
+		COMMAND(port, SelectRegisterWindow, Wsetup);
+		x = ins(port+AddressConfig) & ~xcvrMask9;
+		x |= (xcvr>>20)<<14;
+		outs(port+AddressConfig, x);
+	}
+	else{
+		COMMAND(port, SelectRegisterWindow, Wfifo);
+		x = inl(port+InternalConfig) & ~xcvrMask;
+		x |= xcvr;
+		outl(port+InternalConfig, x);
+	}
+
+	txrxreset(port);
+}
+
+static void
+setfullduplex(int port)
+{
+	int x;
+
+	COMMAND(port, SelectRegisterWindow, Wfifo);
+	x = ins(port+MacControl);
+	outs(port+MacControl, fullDuplexEnable|x);
+
+	txrxreset(port);
+}
+
+static int
+miimdi(int port, int n)
+{
+	int data, i;
+
+	/*
+	 * Read n bits from the MII Management Register.
+	 */
+	data = 0;
+	for(i = n-1; i >= 0; i--){
+		if(ins(port) & mgmtData)
+			data |= (1<<i);
+		microdelay(1);
+		outs(port, mgmtClk);
+		microdelay(1);
+		outs(port, 0);
+		microdelay(1);
+	}
+
+	return data;
+}
+
+static void
+miimdo(int port, int bits, int n)
+{
+	int i, mdo;
+
+	/*
+	 * Write n bits to the MII Management Register.
+	 */
+	for(i = n-1; i >= 0; i--){
+		if(bits & (1<<i))
+			mdo = mgmtDir|mgmtData;
+		else
+			mdo = mgmtDir;
+		outs(port, mdo);
+		microdelay(1);
+		outs(port, mdo|mgmtClk);
+		microdelay(1);
+		outs(port, mdo);
+		microdelay(1);
+	}
+}
+
+static int
+miir(int port, int phyad, int regad)
+{
+	int data, w;
+
+	w = (STATUS(port)>>13) & 0x07;
+	COMMAND(port, SelectRegisterWindow, Wdiagnostic);
+	port += PhysicalMgmt;
+
+	/*
+	 * Preamble;
+	 * ST+OP+PHYAD+REGAD;
+	 * TA + 16 data bits.
+	 */
+	miimdo(port, 0xFFFFFFFF, 32);
+	miimdo(port, 0x1800|(phyad<<5)|regad, 14);
+	data = miimdi(port, 18);
+
+	port -= PhysicalMgmt;
+	COMMAND(port, SelectRegisterWindow, w);
+
+	if(data & 0x10000)
+		return -1;
+
+	return data & 0xFFFF;
+}
+
+static void
+scanphy(int port)
+{
+	int i, x;
+
+	for(i = 0; i < 32; i++){
+		if((x = miir(port, i, 2)) == -1 || x == 0)
+			continue;
+		x <<= 6;
+		x |= miir(port, i, 3)>>10;
+		XCVRDEBUG("phy%d: oui %uX reg1 %uX\n", i, x, miir(port, i, 1));
+		USED(x);
+	}
+}
+
+#ifdef notdef
+static struct xxx {
+	int	available;
+	int	next;
+} xxx[8] = {
+	{ base10TAvailable,	1, },		/* xcvr10BaseT	-> xcvrAui */
+	{ auiAvailable,		3, },		/* xcvrAui	-> xcvr10Base2 */
+	{ 0, -1, },
+	{ coaxAvailable,	-1, },		/* xcvr10Base2	-> nowhere */
+	{ baseTXAvailable,	5, },		/* xcvr100BaseTX-> xcvr100BaseFX */
+	{ baseFXAvailable,	-1, },		/* xcvr100BaseFX-> nowhere */
+	{ miiConnector,		-1, },		/* xcvrMii	-> nowhere */
+	{ 0, -1, },
+};
+#endif /* notdef */
+
+static struct {
+	char *name;
+	int avail;
+	int xcvr;
+} media[] = {
+	"10BaseT",	base10TAvailable,	xcvr10BaseT,
+	"10Base2",	coaxAvailable,		xcvr10Base2,
+	"100BaseTX",	baseTXAvailable,	xcvr100BaseTX,
+	"100BaseFX",	baseFXAvailable,	xcvr100BaseFX,
+	"aui",		auiAvailable,		xcvrAui,
+	"mii",		miiConnector,		xcvrMii
+};
+
+static int
+autoselect(int port, int xcvr, int is9)
+{
+	int media, x;
+	USED(xcvr);
+
+	/*
+	 * Pathetic attempt at automatic media selection.
+	 * Really just to get the Fast Etherlink 10BASE-T/100BASE-TX
+	 * cards operational.
+	 * It's a bonus if it works for anything else.
+	 */
+	if(is9){
+		COMMAND(port, SelectRegisterWindow, Wsetup);
+		x = ins(port+ConfigControl);
+		media = 0;
+		if(x & base10TAvailable9)
+			media |= base10TAvailable;
+		if(x & coaxAvailable9)
+			media |= coaxAvailable;
+		if(x & auiAvailable9)
+			media |= auiAvailable;
+	}
+	else{
+		COMMAND(port, SelectRegisterWindow, Wfifo);
+		media = ins(port+ResetOptions);
+	}
+	XCVRDEBUG("autoselect: media %uX\n", media);
+
+	if(media & miiConnector)
+		return xcvrMii;
+
+	COMMAND(port, SelectRegisterWindow, Wdiagnostic);
+	XCVRDEBUG("autoselect: media status %uX\n", ins(port+MediaStatus));
+
+	if(media & baseTXAvailable){
+		/*
+		 * Must have InternalConfig register.
+		 */
+		setxcvr(port, xcvr100BaseTX, is9);
+
+		COMMAND(port, SelectRegisterWindow, Wdiagnostic);
+		x = ins(port+MediaStatus) & ~(dcConverterEnabled|jabberGuardEnable);
+		outs(port+MediaStatus, linkBeatEnable|x);
+		delay(10);
+
+		if(ins(port+MediaStatus) & linkBeatDetect)
+			return xcvr100BaseTX;
+		outs(port+MediaStatus, x);
+	}
+
+	if(media & base10TAvailable){
+		setxcvr(port, xcvr10BaseT, is9);
+
+		COMMAND(port, SelectRegisterWindow, Wdiagnostic);
+		x = ins(port+MediaStatus) & ~dcConverterEnabled;
+		outs(port+MediaStatus, linkBeatEnable|jabberGuardEnable|x);
+		delay(100);
+
+		XCVRDEBUG("autoselect: 10BaseT media status %uX\n", ins(port+MediaStatus));
+		if(ins(port+MediaStatus) & linkBeatDetect)
+			return xcvr10BaseT;
+		outs(port+MediaStatus, x);
+	}
+
+	/*
+	 * Botch.
+	 */
+	return autoSelect;
+}
+
+static int
+eepromdata(int port, int offset)
+{
+	COMMAND(port, SelectRegisterWindow, Wsetup);
+	while(EEPROMBUSY(port))
+		;
+	EEPROMCMD(port, EepromReadRegister, offset);
+	while(EEPROMBUSY(port))
+		;
+	return EEPROMDATA(port);
+}
+
+int
+elnk3reset(Ether* ether)
+{
+	int anar, anlpar, phyaddr, phystat, timeo, xcvr;
+	int busmaster, did, i, j, port, rxearly, rxstatus9, x;
+	Block *bp, **bpp;
+	Adapter *ap;
+	uchar ea[Eaddrlen];
+	Ctlr *ctlr;
+	static int scandone;
+	char *p;
+
+	/*
+	 * Scan for adapter on PCI, EISA and finally
+	 * using the little ISA configuration dance.
+	 */
+	if(scandone == 0){
+		tcm59Xpci(ether);
+		tcm5XXeisa();
+		tcm509isa();
+		scandone = 1;
+	}
+
+	/*
+	 * Any adapter matches if no ether->port is supplied,
+	 * otherwise the ports must match.
+	 */
+	port = 0;
+	bpp = &adapter;
+	for(bp = *bpp; bp; bp = bp->next){
+		ap = (Adapter*)bp->rp;
+		if(ether->port == 0 || ether->port == ap->port){
+			port = ap->port;
+			ether->irq = ap->irq;
+			ether->tbdf = ap->tbdf;
+			ether->mem = ap->cbfns;	/* Misuse the mem ref for the cardbus functions */
+			*bpp = bp->next;
+			freeb(bp);
+			break;
+		}
+		bpp = &bp->next;
+	}
+	if(port == 0 && (port = tcm5XXpcmcia(ether)) == 0)
+		return -1;
+
+	/*
+	 * Read the DeviceID from the EEPROM, it's at offset 0x03,
+	 * and do something depending on capabilities.
+	 */
+	switch(did = eepromdata(port, 0x03)){
+
+	case 0x5157:		/* 3C575 Cyclone */
+	case 0x4500:		/* 3C450 HomePNA Tornado */
+	case 0x6056:
+	case 0x7646:		/* 3CSOHO100-TX */
+	case 0x9055:		/* 3C905B-TX */
+	case 0x9200:		/* 3C905C-TX */
+		/*FALLTHROUGH*/
+	case 0x9000:		/* 3C900-TPO */
+	case 0x9001:		/* 3C900-COMBO */
+	case 0x9005:		/* 3C900B-COMBO */
+	case 0x9050:		/* 3C905-TX */
+	case 0x9051:		/* 3C905-T4 */
+		if(BUSTYPE(ether->tbdf) != BusPCI)
+			goto buggery;
+		busmaster = 2;
+		goto vortex;
+
+	case 0x5900:		/* 3C590-[TP|COMBO|TPO] */
+	case 0x5920:		/* 3C592-[TP|COMBO|TPO] */
+	case 0x5950:		/* 3C595-TX */
+	case 0x5951:		/* 3C595-T4 */
+	case 0x5952:		/* 3C595-MII */
+	case 0x5970:		/* 3C597-TX */
+	case 0x5971:		/* 3C597-T4 */
+	case 0x5972:		/* 3C597-MII */
+		busmaster = 1;
+	vortex:
+		COMMAND(port, SelectRegisterWindow, Wfifo);
+		xcvr = inl(port+InternalConfig) & (autoSelect|xcvrMask);
+		rxearly = 8188;
+		rxstatus9 = 0;
+		break;
+
+	buggery:
+	default:
+		busmaster = 0;
+		COMMAND(port, SelectRegisterWindow, Wsetup);
+		x = ins(port+AddressConfig);
+		xcvr = ((x & xcvrMask9)>>14)<<20;
+		if(x & autoSelect9)
+			xcvr |= autoSelect;
+		rxearly = 2044;
+		rxstatus9 = 1;
+		break;
+	}
+
+	/*
+	 * Check if the adapter's station address is to be overridden.
+	 * If not, read it from the EEPROM and set in ether->ea prior to
+	 * loading the station address in Wstation.
+	 * The EEPROM returns 16-bits at a time.
+	 */
+	memset(ea, 0, Eaddrlen);
+	if(memcmp(ea, ether->ea, Eaddrlen) == 0){
+		for(i = 0; i < Eaddrlen/2; i++){
+			x = eepromdata(port, i);
+			ether->ea[2*i] = x>>8;
+			ether->ea[2*i+1] = x;
+		}
+	}
+
+	COMMAND(port, SelectRegisterWindow, Wstation);
+	for(i = 0; i < Eaddrlen; i++)
+		outb(port+i, ether->ea[i]);
+
+	/*
+	 * Enable the transceiver if necessary and determine whether
+	 * busmastering can be used. Due to bugs in the first revision
+	 * of the 3C59[05], don't use busmastering at 10Mbps.
+	 */
+	XCVRDEBUG("reset: xcvr %uX\n", xcvr);
+
+	/*
+	 * Allow user to specify desired media in plan9.ini
+	 */
+	for(i = 0; i < ether->nopt; i++){
+		if(cistrncmp(ether->opt[i], "media=", 6) != 0)
+			continue;
+		p = ether->opt[i]+6;
+		for(j = 0; j < nelem(media); j++)
+			if(cistrcmp(p, media[j].name) == 0)
+				xcvr = media[j].xcvr;
+	}
+	
+	/*
+	 * forgive me, but i am weak
+	 */
+	switch(did){
+	default:
+		if(xcvr & autoSelect)
+			xcvr = autoselect(port, xcvr, rxstatus9);
+		break;
+	case 0x4500:
+	case 0x5157:
+	case 0x6056:
+	case 0x7646:
+	case 0x9055:
+	case 0x9200:
+		xcvr = xcvrMii;
+		txrxreset(port);
+		XCVRDEBUG("905[BC] reset ops 0x%uX\n", ins(port+ResetOp905B));
+
+		if (did == 0x5157) {
+			ushort reset_opts;
+
+			COMMAND(port, SelectRegisterWindow, Wstation);
+			reset_opts = ins(port + ResetOp905B);
+			reset_opts |= 0x0010;		/* Invert LED */
+			outs(port + ResetOp905B, reset_opts);
+		}
+		break;
+	}
+	XCVRDEBUG("autoselect returns: xcvr %uX, did 0x%uX\n", xcvr, did);
+
+	switch(xcvr){
+
+	case xcvrMii:
+		/*
+		 * Quick hack.
+		scanphy(port);
+		 */
+		phyaddr = (did == 0x5157)? 0: 24;
+		for(i = 0; i < 7; i++)
+			XCVRDEBUG(" %2.2uX", miir(port, phyaddr, i));
+			XCVRDEBUG("\n");
+
+		for(timeo = 0; timeo < 30; timeo++){
+			phystat = miir(port, phyaddr, 0x01);
+			if(phystat & 0x20)
+				break;
+			XCVRDEBUG(" %2.2uX", phystat);
+			delay(100);
+		}
+		XCVRDEBUG(" %2.2uX", miir(port, phyaddr, 0x01));
+		XCVRDEBUG("\n");
+
+		anar = miir(port, phyaddr, 0x04);
+		anlpar = miir(port, phyaddr, 0x05) & 0x03E0;
+		anar &= anlpar;
+		miir(port, phyaddr, 0x00);
+		XCVRDEBUG("mii an: %uX anlp: %uX r0:%uX r1:%uX\n",
+			anar, anlpar, miir(port, phyaddr, 0x00),
+			miir(port, phyaddr, 0x01));
+		for(i = 0; i < ether->nopt; i++){
+			if(cistrcmp(ether->opt[i], "fullduplex") == 0)
+				anar |= 0x0100;
+			else if(cistrcmp(ether->opt[i], "100BASE-TXFD") == 0)
+				anar |= 0x0100;
+			else if(cistrcmp(ether->opt[i], "force100") == 0)
+				anar |= 0x0080;
+		}
+		XCVRDEBUG("mii anar: %uX\n", anar);
+		if(anar & 0x0100){		/* 100BASE-TXFD */
+			setfullduplex(port);
+		}
+		else if(anar & 0x0200){		/* 100BASE-T4 */
+			/* nothing to do */
+		}
+		else if(anar & 0x0080){		/* 100BASE-TX */
+			/* nothing to do */;
+		}
+		else if(anar & 0x0040)		/* 10BASE-TFD */
+			setfullduplex(port);
+		else{				/* 10BASE-T */
+			/* nothing to do */;
+		}
+		break;
+
+	case xcvr100BaseTX:
+	case xcvr100BaseFX:
+		COMMAND(port, SelectRegisterWindow, Wfifo);
+		x = inl(port+InternalConfig) & ~ramPartitionMask;
+		outl(port+InternalConfig, x|ramPartition1to1);
+
+		COMMAND(port, SelectRegisterWindow, Wdiagnostic);
+		x = ins(port+MediaStatus) & ~(dcConverterEnabled|jabberGuardEnable);
+		x |= linkBeatEnable;
+		outs(port+MediaStatus, x);
+		break;
+
+	case xcvr10BaseT:
+		/*
+		 * Enable Link Beat and Jabber to start the
+		 * transceiver.
+		 */
+		COMMAND(port, SelectRegisterWindow, Wdiagnostic);
+		x = ins(port+MediaStatus) & ~dcConverterEnabled;
+		x |= linkBeatEnable|jabberGuardEnable;
+		outs(port+MediaStatus, x);
+
+		if((did & 0xFF00) == 0x5900)
+			busmaster = 0;
+		break;
+
+	case xcvr10Base2:
+		COMMAND(port, SelectRegisterWindow, Wdiagnostic);
+		x = ins(port+MediaStatus) & ~(linkBeatEnable|jabberGuardEnable);
+		outs(port+MediaStatus, x);
+
+		/*
+		 * Start the DC-DC converter.
+		 * Wait > 800 microseconds.
+		 */
+		COMMAND(port, EnableDcConverter, 0);
+		delay(1);
+		break;
+	}
+
+	/*
+	 * Wop is the normal operating register set.
+	 * The 3C59[0257] adapters allow access to more than one register window
+	 * at a time, but there are situations where switching still needs to be
+	 * done, so just do it.
+	 * Clear out any lingering Tx status.
+	 */
+	COMMAND(port, SelectRegisterWindow, Wop);
+	if(busmaster == 2)
+		x = port+TxStatus905;
+	else
+		x = port+TxStatus;
+	while(inb(x))
+		outb(x, 0);
+
+	/*
+	 * Allocate a controller structure, clear out the
+	 * adapter statistics, clear the statistics logged into ctlr
+	 * and enable statistics collection. Xcvr is needed in order
+	 * to collect the BadSSD statistics.
+	 */
+	ether->ctlr = malloc(sizeof(Ctlr));
+	ctlr = ether->ctlr;
+
+	ilock(&ctlr->wlock);
+	ctlr->xcvr = xcvr;
+	statistics(ether);
+	memset(ctlr->stats, 0, sizeof(ctlr->stats));
+
+	ctlr->busmaster = busmaster;
+	ctlr->xcvr = xcvr;
+	ctlr->rxstatus9 = rxstatus9;
+	ctlr->rxearly = rxearly;
+	if(rxearly >= 2048)
+		ctlr->ts = 2;
+
+	COMMAND(port, StatisticsEnable, 0);
+
+	/*
+	 * Allocate any receive buffers.
+	 */
+	if (ctlr->busmaster == 2) {
+		ctlr->dnenabled = 1;
+
+		/*
+		 * 10MUpldBug.
+		 * Disabling is too severe, can use receive busmastering at
+		 * 100Mbps OK, but how to tell which rate is actually being used -
+		 * the 3c905 always seems to have dataRate100 set?
+		 * Believe the bug doesn't apply if upRxEarlyEnable is set
+		 * and the threshold is set such that uploads won't start
+		 * until the whole packet has been received.
+		 */
+		ctlr->upenabled = 1;
+		x = eepromdata(port, 0x0F);
+		if(!(x & 0x01))
+			outl(port+PktStatus, upRxEarlyEnable);
+
+		ctlr->nup = Nup;
+		ctlr->ndn = Ndn;
+		init905(ctlr);
+		outl(port+TxFreeThresh, HOWMANY(ETHERMAXTU, 256));
+	}
+
+	/*
+	 * Set a base TxStartThresh which will be incremented
+	 * if any txUnderrun errors occur and ensure no RxEarly
+	 * interrupts happen.
+	 */
+	ctlr->txthreshold = ETHERMAXTU/2;
+	COMMAND(port, SetTxStartThresh, ctlr->txthreshold>>ctlr->ts);
+	COMMAND(port, SetRxEarlyThresh, rxearly>>ctlr->ts);
+
+	iunlock(&ctlr->wlock);
+
+	/*
+	 * Linkage to the generic ethernet driver.
+	 */
+	ether->port = port;
+	ether->attach = attach;
+	ether->transmit = transmit;
+	ether->interrupt = interrupt;
+
+	return 0;
+}
--- /dev/null
+++ b/os/boot/pc/etherelnk3x.c
@@ -1,0 +1,1074 @@
+/*
+ * Etherlink III and Fast EtherLink adapters.
+ * To do:
+ *	autoSelect;
+ *	busmaster channel;
+ *	PCMCIA;
+ *	PCI latency timer and master enable;
+ *	errata list.
+ *
+ * Product ID:
+ *	9150 ISA	3C509[B]
+ *	9050 ISA	3C509[B]-TP
+ *	9450 ISA	3C509[B]-COMBO
+ *	9550 ISA	3C509[B]-TPO
+ *
+ *	9350 EISA	3C579
+ *	9250 EISA	3C579-TP
+ *
+ *	5920 EISA	3C592-[TP|COMBO|TPO]
+ *	5970 EISA	3C597-TX	Fast Etherlink 10BASE-T/100BASE-TX
+ *	5971 EISA	3C597-T4	Fast Etherlink 10BASE-T/100BASE-T4
+ *	5972 EISA	3C597-MII	Fast Etherlink 10BASE-T/MII
+ *
+ *	5900 PCI	3C590-[TP|COMBO|TPO]
+ *	5950 PCI	3C595-TX	Fast Etherlink Shared 10BASE-T/100BASE-TX
+ *	5951 PCI	3C595-T4	Fast Etherlink Shared 10BASE-T/100BASE-T4
+ *	5952 PCI	3C595-MII	Fast Etherlink 10BASE-T/MII
+ *
+ *	9058 PCMCIA	3C589[B]-[TP|COMBO]
+ *
+ *	627C MCA	3C529
+ *	627D MCA	3C529-TP
+ */
+#include "u.h"
+#include "lib.h"
+#include "mem.h"
+#include "dat.h"
+#include "fns.h"
+#include "io.h"
+
+#include "etherif.h"
+
+enum {
+	IDport			= 0x0110,	/* anywhere between 0x0100 and 0x01F0 */
+};
+
+enum {						/* all windows */
+	Command			= 0x000E,
+	IntStatus		= 0x000E,
+};
+
+enum {						/* Commands */
+	GlobalReset		= 0x0000,
+	SelectRegisterWindow	= 0x0001,	
+	EnableDcConverter	= 0x0002,
+	RxDisable		= 0x0003,
+	RxEnable		= 0x0004,
+	RxReset			= 0x0005,
+	TxDone			= 0x0007,	
+	RxDiscard		= 0x0008,
+	TxEnable		= 0x0009,
+	TxDisable		= 0x000A,
+	TxReset			= 0x000B,
+	RequestInterrupt	= 0x000C,
+	AcknowledgeInterrupt	= 0x000D,
+	SetInterruptEnable	= 0x000E,
+	SetIndicationEnable	= 0x000F,	/* SetReadZeroMask */
+	SetRxFilter		= 0x0010,
+	SetRxEarlyThresh	= 0x0011,
+	SetTxAvailableThresh	= 0x0012,
+	SetTxStartThresh	= 0x0013,
+	StartDma		= 0x0014,	/* initiate busmaster operation */
+	StatisticsEnable	= 0x0015,
+	StatisticsDisable	= 0x0016,
+	DisableDcConverter	= 0x0017,
+	SetTxReclaimThresh	= 0x0018,	/* PIO-only adapters */
+	PowerUp			= 0x001B,	/* not all adapters */
+	PowerDownFull		= 0x001C,	/* not all adapters */
+	PowerAuto		= 0x001D,	/* not all adapters */
+};
+
+enum {						/* (Global|Rx|Tx)Reset command bits */
+	tpAuiReset		= 0x0001,	/* 10BaseT and AUI transceivers */
+	endecReset		= 0x0002,	/* internal Ethernet encoder/decoder */
+	networkReset		= 0x0004,	/* network interface logic */
+	fifoReset		= 0x0008,	/* FIFO control logic */
+	aismReset		= 0x0010,	/* autoinitialise state-machine logic */
+	hostReset		= 0x0020,	/* bus interface logic */
+	dmaReset		= 0x0040,	/* bus master logic */
+	vcoReset		= 0x0080,	/* on-board 10Mbps VCO */
+
+	resetMask		= 0x00FF,
+};
+
+enum {						/* SetRxFilter command bits */
+	receiveIndividual	= 0x0001,	/* match station address */
+	receiveMulticast	= 0x0002,
+	receiveBroadcast	= 0x0004,
+	receiveAllFrames	= 0x0008,	/* promiscuous */
+};
+
+enum {						/* StartDma command bits */
+	Upload			= 0x0000,	/* transfer data from adapter to memory */
+	Download		= 0x0001,	/* transfer data from memory to adapter */
+};
+
+enum {						/* IntStatus bits */
+	interruptLatch		= 0x0001,
+	hostError		= 0x0002,	/* Adapter Failure */
+	txComplete		= 0x0004,
+	txAvailable		= 0x0008,
+	rxComplete		= 0x0010,
+	rxEarly			= 0x0020,
+	intRequested		= 0x0040,
+	updateStats		= 0x0080,
+	transferInt		= 0x0100,	/* Bus Master Transfer Complete */
+	busMasterInProgress	= 0x0800,
+	commandInProgress	= 0x1000,
+
+	interruptMask		= 0x01FE,
+};
+
+#define COMMAND(port, cmd, a)	outs((port)+Command, ((cmd)<<11)|(a))
+#define STATUS(port)		ins((port)+IntStatus)
+
+enum {						/* Window 0 - setup */
+	Wsetup			= 0x0000,
+						/* registers */
+	ManufacturerID		= 0x0000,	/* 3C5[08]*, 3C59[27] */
+	ProductID		= 0x0002,	/* 3C5[08]*, 3C59[27] */
+	ConfigControl		= 0x0004,	/* 3C5[08]*, 3C59[27] */
+	AddressConfig		= 0x0006,	/* 3C5[08]*, 3C59[27] */
+	ResourceConfig		= 0x0008,	/* 3C5[08]*, 3C59[27] */
+	EepromCommand		= 0x000A,
+	EepromData		= 0x000C,
+						/* AddressConfig Bits */
+	autoSelect9		= 0x0080,
+	xcvrMask9		= 0xC000,
+						/* ConfigControl bits */
+	Ena			= 0x0001,
+						/* EepromCommand bits */
+	EepromReadRegister	= 0x0080,
+	EepromBusy		= 0x8000,
+};
+
+#define EEPROMCMD(port, cmd, a)	outs((port)+EepromCommand, (cmd)|(a))
+#define EEPROMBUSY(port)	(ins((port)+EepromCommand) & EepromBusy)
+#define EEPROMDATA(port)	ins((port)+EepromData)
+
+enum {						/* Window 1 - operating set */
+	Wop			= 0x0001,
+						/* registers */
+	Fifo			= 0x0000,
+	RxError			= 0x0004,	/* 3C59[0257] only */
+	RxStatus		= 0x0008,
+	Timer			= 0x000A,
+	TxStatus		= 0x000B,
+	TxFree			= 0x000C,
+						/* RxError bits */
+	rxOverrun		= 0x0001,
+	runtFrame		= 0x0002,
+	alignmentError		= 0x0004,	/* Framing */
+	crcError		= 0x0008,
+	oversizedFrame		= 0x0010,
+	dribbleBits		= 0x0080,
+						/* RxStatus bits */
+	rxBytes			= 0x1FFF,	/* 3C59[0257] mask */
+	rxBytes9		= 0x07FF,	/* 3C5[078]9 mask */
+	rxError9		= 0x3800,	/* 3C5[078]9 error mask */
+	rxOverrun9		= 0x0000,
+	oversizedFrame9		= 0x0800,
+	dribbleBits9		= 0x1000,
+	runtFrame9		= 0x1800,
+	alignmentError9		= 0x2000,	/* Framing */
+	crcError9		= 0x2800,
+	rxError			= 0x4000,
+	rxIncomplete		= 0x8000,
+						/* TxStatus Bits */
+	txStatusOverflow	= 0x0004,
+	maxCollisions		= 0x0008,
+	txUnderrun		= 0x0010,
+	txJabber		= 0x0020,
+	interruptRequested	= 0x0040,
+	txStatusComplete	= 0x0080,
+};
+
+enum {						/* Window 2 - station address */
+	Wstation		= 0x0002,
+};
+
+enum {						/* Window 3 - FIFO management */
+	Wfifo			= 0x0003,
+						/* registers */
+	InternalConfig		= 0x0000,	/* 3C509B, 3C589, 3C59[0257] */
+	OtherInt		= 0x0004,	/* 3C59[0257] */
+	RomControl		= 0x0006,	/* 3C509B, 3C59[27] */
+	MacControl		= 0x0006,	/* 3C59[0257] */
+	ResetOptions		= 0x0008,	/* 3C59[0257] */
+	RxFree			= 0x000A,
+						/* InternalConfig bits */
+	disableBadSsdDetect	= 0x00000100,
+	ramLocation		= 0x00000200,	/* 0 external, 1 internal */
+	ramPartition5to3	= 0x00000000,
+	ramPartition3to1	= 0x00010000,
+	ramPartition1to1	= 0x00020000,
+	ramPartition3to5	= 0x00030000,
+	ramPartitionMask	= 0x00030000,
+	xcvr10BaseT		= 0x00000000,
+	xcvrAui			= 0x00100000,	/* 10BASE5 */
+	xcvr10Base2		= 0x00300000,
+	xcvr100BaseTX		= 0x00400000,
+	xcvr100BaseFX		= 0x00500000,
+	xcvrMii			= 0x00600000,
+	xcvrMask		= 0x00700000,
+	autoSelect		= 0x01000000,
+						/* MacControl bits */
+	deferExtendEnable	= 0x0001,
+	deferTimerSelect	= 0x001E,	/* mask */
+	fullDuplexEnable	= 0x0020,
+	allowLargePackets	= 0x0040,
+						/* ResetOptions bits */
+	baseT4Available		= 0x0001,
+	baseTXAvailable		= 0x0002,
+	baseFXAvailable		= 0x0004,
+	base10TAvailable	= 0x0008,
+	coaxAvailable		= 0x0010,
+	auiAvailable		= 0x0020,
+	miiConnector		= 0x0040,
+};
+
+enum {						/* Window 4 - diagnostic */
+	Wdiagnostic		= 0x0004,
+						/* registers */
+	VcoDiagnostic		= 0x0002,
+	FifoDiagnostic		= 0x0004,
+	NetworkDiagnostic	= 0x0006,
+	PhysicalMgmt		= 0x0008,
+	MediaStatus		= 0x000A,
+	BadSSD			= 0x000C,
+						/* FifoDiagnostic bits */
+	txOverrun		= 0x0400,
+	rxUnderrun		= 0x2000,
+	receiving		= 0x8000,
+						/* MediaStatus bits */
+	dataRate100		= 0x0002,
+	crcStripDisable		= 0x0004,
+	enableSqeStats		= 0x0008,
+	collisionDetect		= 0x0010,
+	carrierSense		= 0x0020,
+	jabberGuardEnable	= 0x0040,
+	linkBeatEnable		= 0x0080,
+	jabberDetect		= 0x0200,
+	polarityReversed	= 0x0400,
+	linkBeatDetect		= 0x0800,
+	txInProg		= 0x1000,
+	dcConverterEnabled	= 0x4000,
+	auiDisable		= 0x8000,
+};
+
+enum {						/* Window 5 - internal state */
+	Wstate			= 0x0005,
+						/* registers */
+	TxStartThresh		= 0x0000,
+	TxAvalableThresh	= 0x0002,
+	RxEarlyThresh		= 0x0006,
+	RxFilter		= 0x0008,
+	InterruptEnable		= 0x000A,
+	IndicationEnable	= 0x000C,
+};
+
+enum {						/* Window 6 - statistics */
+	Wstatistics		= 0x0006,
+						/* registers */
+	CarrierLost		= 0x0000,
+	SqeErrors		= 0x0001,
+	MultipleColls		= 0x0002,
+	SingleCollFrames	= 0x0003,
+	LateCollisions		= 0x0004,
+	RxOverruns		= 0x0005,
+	FramesXmittedOk		= 0x0006,
+	FramesRcvdOk		= 0x0007,
+	FramesDeferred		= 0x0008,
+	UpperFramesOk		= 0x0009,
+	BytesRcvdOk		= 0x000A,
+	BytesXmittedOk		= 0x000C,
+};
+
+enum {						/* Window 7 - bus master operations */
+	Wmaster			= 0x0007,
+						/* registers */
+	MasterAddress		= 0x0000,
+	MasterLen		= 0x0006,
+	MasterStatus		= 0x000C,
+						/* MasterStatus bits */
+	masterAbort		= 0x0001,
+	targetAbort		= 0x0002,
+	targetRetry		= 0x0004,
+	targetDisc		= 0x0008,
+	masterDownload		= 0x1000,
+	masterUpload		= 0x4000,
+	masterInProgress	= 0x8000,
+
+	masterMask		= 0xD00F,
+};	
+
+typedef struct {
+	int	txthreshold;
+} Ctlr;
+
+static void
+attach(Ether* ether)
+{
+	int port, x;
+
+	port = ether->port;
+
+	/*
+	 * Set the receiver packet filter for this and broadcast addresses,
+	 * set the interrupt masks for all interrupts, enable the receiver
+	 * and transmitter.
+	 */
+	x = receiveBroadcast|receiveIndividual;
+	COMMAND(port, SetRxFilter, x);
+
+	x = interruptMask|interruptLatch;
+	COMMAND(port, SetIndicationEnable, x);
+	COMMAND(port, SetInterruptEnable, x);
+
+	COMMAND(port, RxEnable, 0);
+	COMMAND(port, TxEnable, 0);
+}
+
+static void
+transmit(Ether* ether)
+{
+	int port, len;
+	RingBuf *tb;
+
+	/*
+	 * Attempt to top-up the transmit FIFO. If there's room simply
+	 * stuff in the packet length (unpadded to a dword boundary), the
+	 * packet data (padded) and remove the packet from the queue.
+	 * If there's no room post an interrupt for when there is.
+	 * This routine is called both from the top level and from interrupt
+	 * level.
+	 */
+	port = ether->port;
+	for(tb = &ether->tb[ether->ti]; tb->owner == Interface; tb = &ether->tb[ether->ti]){
+		len = ROUNDUP(tb->len, 4);
+		if(len+4 <= ins(port+TxFree)){
+			outl(port+Fifo, tb->len);
+			outsl(port+Fifo, tb->pkt, len/4);
+			tb->owner = Host;
+			ether->ti = NEXT(ether->ti, ether->ntb);
+		}
+		else{
+			COMMAND(port, SetTxAvailableThresh, len);
+			break;
+		}
+	}
+}
+
+static void
+receive(Ether* ether)
+{
+	int len, port, rxstatus;
+	RingBuf *rb;
+
+	port = ether->port;
+
+	while(((rxstatus = ins(port+RxStatus)) & rxIncomplete) == 0){
+		/*
+		 * If there was an error, throw it away and continue.
+		 * The 3C5[078]9 has the error info in the status register
+		 * and the 3C59[0257] implement a separate RxError register.
+		 */
+		if((rxstatus & rxError) == 0){
+			/*
+			 * Packet received. Read it into the next free
+			 * ring buffer, if any. Must read len bytes padded
+			 * to a doubleword, can be picked out 32-bits at
+			 * a time. The CRC is already stripped off.
+			 */
+			rb = &ether->rb[ether->ri];
+			if(rb->owner == Interface){
+				len = (rxstatus & rxBytes9);
+				rb->len = len;
+				insl(port+Fifo, rb->pkt, HOWMANY(len, 4));
+
+				rb->owner = Host;
+				ether->ri = NEXT(ether->ri, ether->nrb);
+			}
+		}
+
+		/*
+		 * All done, discard the packet.
+		 */
+		COMMAND(port, RxDiscard, 0);
+		while(STATUS(port) & commandInProgress)
+			;
+	}
+}
+
+static void
+statistics(Ether* ether)
+{
+	int i, port, w;
+
+	port = ether->port;
+	w = (STATUS(port)>>13) & 0x07;
+	COMMAND(port, SelectRegisterWindow, Wop);
+	COMMAND(port, SelectRegisterWindow, Wstatistics);
+
+	for(i = 0; i < 0x0A; i++)
+		inb(port+i);
+	ins(port+BytesRcvdOk);
+	ins(port+BytesXmittedOk);
+
+	COMMAND(port, SelectRegisterWindow, w);
+}
+
+static void
+interrupt(Ureg*, void* arg)
+{
+	Ether *ether;
+	int port, status, txstatus, w, x;
+	Ctlr *ctlr;
+
+	ether = arg;
+	port = ether->port;
+	ctlr = ether->ctlr;
+
+	w = (STATUS(port)>>13) & 0x07;
+	COMMAND(port, SelectRegisterWindow, Wop);
+
+	for(;;){
+		/*
+		 * Clear the interrupt latch.
+		 * It's possible to receive a packet and for another
+		 * to become complete before exiting the interrupt
+		 * handler so this must be done first to ensure another
+		 * interrupt will occur.
+		 */
+		COMMAND(port, AcknowledgeInterrupt, interruptLatch);
+		status = STATUS(port);
+		if((status & interruptMask) == 0)
+			break;
+
+		if(status & hostError){
+			/*
+			 * Adapter failure, try to find out why, reset if
+			 * necessary. What happens if Tx is active and a reset
+			 * occurs, need to retransmit? This probably isn't right.
+			 */
+			COMMAND(port, SelectRegisterWindow, Wdiagnostic);
+			x = ins(port+FifoDiagnostic);
+			COMMAND(port, SelectRegisterWindow, Wop);
+			print("elnk3#%d: status 0x%uX, diag 0x%uX\n",
+			    ether->ctlrno, status, x);
+
+			if(x & txOverrun){
+				COMMAND(port, TxReset, 0);
+				COMMAND(port, TxEnable, 0);
+			}
+
+			if(x & rxUnderrun){
+				/*
+				 * This shouldn't happen...
+				 * Need to restart any busmastering?
+				 */
+				COMMAND(port, RxReset, 0);
+				while(STATUS(port) & commandInProgress)
+					;
+				COMMAND(port, RxEnable, 0);
+			}
+
+			status &= ~hostError;
+		}
+
+		if(status & (transferInt|rxComplete)){
+			receive(ether);
+			status &= ~(transferInt|rxComplete);
+		}
+
+		if(status & txComplete){
+			/*
+			 * Pop the TxStatus stack, accumulating errors.
+			 * Adjust the TX start threshold if there was an underrun.
+			 * If there was a Jabber or Underrun error, reset
+			 * the transmitter.
+			 * For all conditions enable the transmitter.
+			 */
+			txstatus = 0;
+			do{
+				if(x = inb(port+TxStatus))
+					outb(port+TxStatus, 0);
+				txstatus |= x;
+			}while(STATUS(port) & txComplete);
+
+			if(txstatus & txUnderrun){
+				COMMAND(port, SelectRegisterWindow, Wdiagnostic);
+				while(ins(port+MediaStatus) & txInProg)
+					;
+				COMMAND(port, SelectRegisterWindow, Wop);
+				if(ctlr->txthreshold < ETHERMAXTU)
+					ctlr->txthreshold += ETHERMINTU;
+			}
+
+			if(txstatus & (txJabber|txUnderrun)){
+				COMMAND(port, TxReset, 0);
+				while(STATUS(port) & commandInProgress)
+					;
+				COMMAND(port, SetTxStartThresh, ctlr->txthreshold);
+			}
+			COMMAND(port, TxEnable, 0);
+			status &= ~txComplete;
+			status |= txAvailable;
+		}
+
+		if(status & txAvailable){
+			COMMAND(port, AcknowledgeInterrupt, txAvailable);
+			transmit(ether);
+			status &= ~txAvailable;
+		}
+
+		if(status & updateStats){
+			statistics(ether);
+			status &= ~updateStats;
+		}
+
+		/*
+		 * Panic if there are any interrupts not dealt with.
+		 */
+		if(status & interruptMask)
+			panic("elnk3#%d: interrupt mask 0x%uX\n", ether->ctlrno, status);
+	}
+
+	COMMAND(port, SelectRegisterWindow, w);
+}
+
+typedef struct Adapter {
+	int	port;
+	int	irq;
+	int	tbdf;
+} Adapter;
+static Block* adapter;
+
+static void
+tcmadapter(int port, int irq, int tbdf)
+{
+	Block *bp;
+	Adapter *ap;
+
+	bp = allocb(sizeof(Adapter));
+	ap = (Adapter*)bp->rp;
+	ap->port = port;
+	ap->irq = irq;
+	ap->tbdf = tbdf;
+
+	bp->next = adapter;
+	adapter = bp;
+}
+
+/*
+ * Write two 0 bytes to identify the IDport and then reset the
+ * ID sequence. Then send the ID sequence to the card to get
+ * the card into command state.
+ */
+static void
+idseq(void)
+{
+	int i;
+	uchar al;
+	static int reset, untag;
+
+	/*
+	 * One time only:
+	 *	reset any adapters listening
+	 */
+	if(reset == 0){
+		outb(IDport, 0);
+		outb(IDport, 0);
+		outb(IDport, 0xC0);
+		delay(20);
+		reset = 1;
+	}
+
+	outb(IDport, 0);
+	outb(IDport, 0);
+	for(al = 0xFF, i = 0; i < 255; i++){
+		outb(IDport, al);
+		if(al & 0x80){
+			al <<= 1;
+			al ^= 0xCF;
+		}
+		else
+			al <<= 1;
+	}
+
+	/*
+	 * One time only:
+	 *	write ID sequence to get the attention of all adapters;
+	 *	untag all adapters.
+	 * If we do a global reset here on all adapters we'll confuse any
+	 * ISA cards configured for EISA mode.
+	 */
+	if(untag == 0){
+		outb(IDport, 0xD0);
+		untag = 1;
+	}
+}
+
+static ulong
+activate(void)
+{
+	int i;
+	ushort x, acr;
+
+	/*
+	 * Do the little configuration dance:
+	 *
+	 * 2. write the ID sequence to get to command state.
+	 */
+	idseq();
+
+	/*
+	 * 3. Read the Manufacturer ID from the EEPROM.
+	 *    This is done by writing the IDPort with 0x87 (0x80
+	 *    is the 'read EEPROM' command, 0x07 is the offset of
+	 *    the Manufacturer ID field in the EEPROM).
+	 *    The data comes back 1 bit at a time.
+	 *    We seem to need a delay here between reading the bits.
+	 *
+	 * If the ID doesn't match, there are no more adapters.
+	 */
+	outb(IDport, 0x87);
+	delay(20);
+	for(x = 0, i = 0; i < 16; i++){
+		delay(20);
+		x <<= 1;
+		x |= inb(IDport) & 0x01;
+	}
+	if(x != 0x6D50)
+		return 0;
+
+	/*
+	 * 3. Read the Address Configuration from the EEPROM.
+	 *    The Address Configuration field is at offset 0x08 in the EEPROM).
+	 */
+	outb(IDport, 0x88);
+	for(acr = 0, i = 0; i < 16; i++){
+		delay(20);
+		acr <<= 1;
+		acr |= inb(IDport) & 0x01;
+	}
+
+	return (acr & 0x1F)*0x10 + 0x200;
+}
+
+#ifdef notjustpcmcia
+static void
+tcm509isa(void)
+{
+	int irq, port;
+
+	/*
+	 * Attempt to activate all adapters. If adapter is set for
+	 * EISA mode (0x3F0), tag it and ignore. Otherwise, activate
+	 * it fully.
+	 */
+	while(port = activate()){
+		/*
+		 * 6. Tag the adapter so it won't respond in future.
+		 */
+		outb(IDport, 0xD1);
+		if(port == 0x3F0)
+			continue;
+
+		/*
+		 * 6. Activate the adapter by writing the Activate command
+		 *    (0xFF).
+		 */
+		outb(IDport, 0xFF);
+		delay(20);
+
+		/*
+		 * 8. Can now talk to the adapter's I/O base addresses.
+		 *    Use the I/O base address from the acr just read.
+		 *
+		 *    Enable the adapter and clear out any lingering status
+		 *    and interrupts.
+		 */
+		while(STATUS(port) & commandInProgress)
+			;
+		COMMAND(port, SelectRegisterWindow, Wsetup);
+		outs(port+ConfigControl, Ena);
+
+		COMMAND(port, TxReset, 0);
+		COMMAND(port, RxReset, 0);
+		COMMAND(port, AcknowledgeInterrupt, 0xFF);
+
+		irq = (ins(port+ResourceConfig)>>12) & 0x0F;
+		tcmadapter(port, irq, BUSUNKNOWN);
+	}
+}
+
+static void
+tcm5XXeisa(void)
+{
+	ushort x;
+	int irq, port, slot;
+
+	/*
+	 * Check if this is an EISA machine.
+	 * If not, nothing to do.
+	 */
+	if(strncmp((char*)(KZERO|0xFFFD9), "EISA", 4))
+		return;
+
+	/*
+	 * Continue through the EISA slots looking for a match on both
+	 * 3COM as the manufacturer and 3C579-* or 3C59[27]-* as the product.
+	 * If an adapter is found, select window 0, enable it and clear
+	 * out any lingering status and interrupts.
+	 */
+	for(slot = 1; slot < MaxEISA; slot++){
+		port = slot*0x1000;
+		if(ins(port+0xC80+ManufacturerID) != 0x6D50)
+			continue;
+		x = ins(port+0xC80+ProductID);
+		if((x & 0xF0FF) != 0x9050 && (x & 0xFF00) != 0x5900)
+			continue;
+
+		COMMAND(port, SelectRegisterWindow, Wsetup);
+		outs(port+ConfigControl, Ena);
+
+		COMMAND(port, TxReset, 0);
+		COMMAND(port, RxReset, 0);
+		COMMAND(port, AcknowledgeInterrupt, 0xFF);
+
+		irq = (ins(port+ResourceConfig)>>12) & 0x0F;
+		tcmadapter(port, irq, BUSUNKNOWN);
+	}
+}
+
+static void
+tcm59Xpci(void)
+{
+	Pcidev *p;
+	int irq, port;
+
+	p = nil;
+	while(p = pcimatch(p, 0x10B7, 0)){
+		port = p->mem[0].bar & ~0x01;
+		irq = p->intl;
+		COMMAND(port, GlobalReset, 0);
+		while(STATUS(port) & commandInProgress)
+			;
+
+		tcmadapter(port, irq, p->tbdf);
+	}
+}
+#endif /* notjustpcmcia */
+
+static char* tcmpcmcia[] = {
+	"3C589",			/* 3COM 589[ABCD] */
+	"3C562",			/* 3COM 562 */
+	"589E",				/* 3COM Megahertz 589E */
+	nil,
+};
+
+static int
+tcm5XXpcmcia(Ether* ether)
+{
+	int i;
+
+	for(i = 0; tcmpcmcia[i] != nil; i++){
+		if(!cistrcmp(ether->type, tcmpcmcia[i]))
+			return ether->port;
+	}
+
+	return 0;
+}
+
+static int
+autoselect(int port, int rxstatus9)
+{
+	int media, x;
+
+	/*
+	 * Pathetic attempt at automatic media selection.
+	 * Really just to get the Fast Etherlink 10BASE-T/100BASE-TX
+	 * cards operational.
+	 */
+	media = auiAvailable|coaxAvailable|base10TAvailable;
+	if(rxstatus9 == 0){
+		COMMAND(port, SelectRegisterWindow, Wfifo);
+		media = ins(port+ResetOptions);
+	}
+
+	if(media & miiConnector)
+		return xcvrMii;
+
+	if(media & baseTXAvailable){
+		/*
+		 * Must have InternalConfig register.
+		 */
+		COMMAND(port, SelectRegisterWindow, Wfifo);
+		x = inl(port+InternalConfig) & ~xcvrMask;
+		x |= xcvr100BaseTX;
+		outl(port+InternalConfig, x);
+		COMMAND(port, TxReset, 0);
+		while(STATUS(port) & commandInProgress)
+			;
+		COMMAND(port, RxReset, 0);
+		while(STATUS(port) & commandInProgress)
+			;
+
+		COMMAND(port, SelectRegisterWindow, Wdiagnostic);
+		x = ins(port+MediaStatus) & ~(dcConverterEnabled|jabberGuardEnable);
+		outs(port+MediaStatus, linkBeatEnable|x);
+		delay(10);
+
+{ int i, v;
+  for(i = 0; i < 10000; i++){
+	v = ins(port+MediaStatus);
+	if(v & linkBeatDetect){
+		print("count %d v %uX\n", i, v);
+		return xcvr100BaseTX;
+	}
+	delay(1);
+  }
+print("count %d v %uX\n", i, ins(port+MediaStatus));
+}
+		if(ins(port+MediaStatus) & linkBeatDetect)
+			return xcvr100BaseTX;
+		outs(port+MediaStatus, x);
+	}
+
+	if(media & base10TAvailable){
+		if(rxstatus9 == 0){
+			COMMAND(port, SelectRegisterWindow, Wfifo);
+			x = inl(port+InternalConfig) & ~xcvrMask;
+			x |= xcvr10BaseT;
+			outl(port+InternalConfig, x);
+		}
+		else{
+			COMMAND(port, SelectRegisterWindow, Wsetup);
+			x = ins(port+AddressConfig) & ~xcvrMask9;
+			x |= (xcvr10BaseT>>20)<<14;
+			outs(port+AddressConfig, x);
+		}
+		COMMAND(port, TxReset, 0);
+		while(STATUS(port) & commandInProgress)
+			;
+		COMMAND(port, RxReset, 0);
+		while(STATUS(port) & commandInProgress)
+			;
+
+		COMMAND(port, SelectRegisterWindow, Wdiagnostic);
+		x = ins(port+MediaStatus) & ~dcConverterEnabled;
+		outs(port+MediaStatus, linkBeatEnable|jabberGuardEnable|x);
+		delay(10);
+
+		if(ins(port+MediaStatus) & linkBeatDetect)
+			return xcvr10BaseT;
+		outs(port+MediaStatus, x);
+	}
+
+	/*
+	 * Botch.
+	 */
+	return autoSelect;
+}
+
+static int
+eepromdata(int port, int offset)
+{
+	COMMAND(port, SelectRegisterWindow, Wsetup);
+	while(EEPROMBUSY(port))
+		;
+	EEPROMCMD(port, EepromReadRegister, offset);
+	while(EEPROMBUSY(port))
+		;
+	return EEPROMDATA(port);
+}
+
+int
+elnk3reset(Ether* ether)
+{
+	int did, i, port, rxstatus9, x, xcvr;
+	Block *bp, **bpp;
+	Adapter *ap;
+	uchar ea[Eaddrlen];
+	Ctlr *ctlr;
+#ifdef notjustpcmcia
+	static int scandone;
+
+	/*
+	 * Scan for adapter on PCI, EISA and finally
+	 * using the little ISA configuration dance.
+	 */
+	if(scandone == 0){
+		tcm59Xpci();
+		tcm5XXeisa();
+		tcm509isa();
+		scandone = 1;
+	}
+#endif /* notjustpcmcia */
+
+	/*
+	 * Any adapter matches if no ether->port is supplied,
+	 * otherwise the ports must match.
+	 */
+	port = 0;
+	bpp = &adapter;
+	for(bp = *bpp; bp; bp = bp->next){
+		ap = (Adapter*)bp->rp;
+		if(ether->port == 0 || ether->port == ap->port){
+			port = ap->port;
+			ether->irq = ap->irq;
+			ether->tbdf = ap->tbdf;
+			*bpp = bp->next;
+			freeb(bp);
+			break;
+		}
+		bpp = &bp->next;
+	}
+	if(port == 0 && (port = tcm5XXpcmcia(ether)) == 0)
+		return -1;
+
+	/*
+	 * Read the DeviceID from the EEPROM, it's at offset 0x03,
+	 * and do something depending on capabilities.
+	 */
+	switch(did = eepromdata(port, 0x03)){
+
+	case 0x9000:
+	case 0x9001:
+	case 0x9050:
+	case 0x9051:
+		if(BUSTYPE(ether->tbdf) != BusPCI)
+			goto buggery;
+		goto vortex;
+
+	case 0x5900:
+	case 0x5920:
+	case 0x5950:
+	case 0x5951:
+	case 0x5952:
+	case 0x5970:
+	case 0x5971:
+	case 0x5972:
+	vortex:
+		COMMAND(port, SelectRegisterWindow, Wfifo);
+		xcvr = inl(port+InternalConfig) & (autoSelect|xcvrMask);
+		rxstatus9 = 0;
+		break;
+
+	buggery:
+	default:
+		COMMAND(port, SelectRegisterWindow, Wsetup);
+		x = ins(port+AddressConfig);
+		xcvr = ((x & xcvrMask9)>>14)<<20;
+		if(x & autoSelect9)
+			xcvr |= autoSelect;
+		rxstatus9 = 1;
+		break;
+	}
+	USED(did);
+
+	/*
+	 * Check if the adapter's station address is to be overridden.
+	 * If not, read it from the EEPROM and set in ether->ea prior to loading the
+	 * station address in Wstation. The EEPROM returns 16-bits at a time.
+	 */
+	memset(ea, 0, Eaddrlen);
+	if(memcmp(ea, ether->ea, Eaddrlen) == 0){
+		for(i = 0; i < Eaddrlen/2; i++){
+			x = eepromdata(port, i);
+			ether->ea[2*i] = x>>8;
+			ether->ea[2*i+1] = x;
+		}
+	}
+
+	COMMAND(port, SelectRegisterWindow, Wstation);
+	for(i = 0; i < Eaddrlen; i++)
+		outb(port+i, ether->ea[i]);
+
+	/*
+	 * Enable the transceiver if necessary.
+	 */
+	if(xcvr & autoSelect)
+		xcvr = autoselect(port, rxstatus9);
+	switch(xcvr){
+
+	case xcvrMii:
+		break;
+
+	case xcvr100BaseTX:
+	case xcvr100BaseFX:
+		COMMAND(port, SelectRegisterWindow, Wfifo);
+		x = inl(port+InternalConfig) & ~ramPartitionMask;
+		outl(port+InternalConfig, x|ramPartition1to1);
+
+		COMMAND(port, SelectRegisterWindow, Wdiagnostic);
+		x = ins(port+MediaStatus) & ~(dcConverterEnabled|jabberGuardEnable);
+		x |= linkBeatEnable;
+		outs(port+MediaStatus, x);
+		break;
+
+	case xcvr10BaseT:
+		/*
+		 * Enable Link Beat and Jabber to start the
+		 * transceiver.
+		 */
+		COMMAND(port, SelectRegisterWindow, Wdiagnostic);
+		x = ins(port+MediaStatus) & ~dcConverterEnabled;
+		x |= linkBeatEnable|jabberGuardEnable;
+		outs(port+MediaStatus, x);
+		break;
+
+	case xcvr10Base2:
+		COMMAND(port, SelectRegisterWindow, Wdiagnostic);
+		x = ins(port+MediaStatus) & ~(linkBeatEnable|jabberGuardEnable);
+		outs(port+MediaStatus, x);
+
+		/*
+		 * Start the DC-DC converter.
+		 * Wait > 800 microseconds.
+		 */
+		COMMAND(port, EnableDcConverter, 0);
+		delay(1);
+		break;
+	}
+
+	/*
+	 * Wop is the normal operating register set.
+	 * The 3C59[0257] adapters allow access to more than one register window
+	 * at a time, but there are situations where switching still needs to be
+	 * done, so just do it.
+	 * Clear out any lingering Tx status.
+	 */
+	COMMAND(port, SelectRegisterWindow, Wop);
+	while(inb(port+TxStatus))
+		outb(port+TxStatus, 0);
+
+	/*
+	 * Allocate a controller structure and start
+	 * to initialise it.
+	 */
+	ether->ctlr = malloc(sizeof(Ctlr));
+	ctlr = ether->ctlr;
+	memset(ctlr, 0, sizeof(Ctlr));
+
+	/*
+	 * Set a base TxStartThresh which will be incremented
+	 * if any txUnderrun errors occur and ensure no RxEarly
+	 * interrupts happen.
+	 */
+	ctlr->txthreshold = ETHERMINTU;
+	COMMAND(port, SetTxStartThresh, ETHERMINTU);
+	COMMAND(port, SetRxEarlyThresh, ETHERMAXTU);
+
+	/*
+	 * Set up the software configuration.
+	 */
+	ether->port = port;
+	ether->attach = attach;
+	ether->transmit = transmit;
+	ether->interrupt = interrupt;
+
+	return 0;
+}
--- /dev/null
+++ b/os/boot/pc/etherga620.c
@@ -1,0 +1,1147 @@
+/*
+ * bootstrap driver for
+ * Netgear GA620 Gigabit Ethernet Card.
+ * Specific for the Alteon Tigon 2 and Intel Pentium or later.
+ * To Do:
+ *	cache alignment for PCI Write-and-Invalidate
+ *	mini ring (what size)?
+ *	tune coalescing values
+ *	statistics formatting
+ *	don't update Spi if nothing to send
+ *	receive ring alignment
+ *	watchdog for link management?
+ */
+#include "u.h"
+#include "lib.h"
+#include "mem.h"
+#include "dat.h"
+#include "fns.h"
+#include "io.h"
+
+#define malign(n)	xspanalloc((n), 32, 0)
+
+#include "etherif.h"
+#include "etherga620fw.h"
+
+enum {
+	Mhc		= 0x0040,	/* Miscellaneous Host Control */
+	Mlc		= 0x0044,	/* Miscellaneous Local Control */
+	Mc		= 0x0050,	/* Miscellaneous Configuration */
+	Ps		= 0x005C,	/* PCI State */
+	Wba		= 0x0068,	/* Window Base Address */
+	Wd		= 0x006C,	/* Window Data */
+
+	DMAas		= 0x011C,	/* DMA Assist State */
+
+	CPUAstate	= 0x0140,	/* CPU A State */
+	CPUApc		= 0x0144,	/* CPU A Programme Counter */
+
+	CPUBstate	= 0x0240,	/* CPU B State */
+
+	Hi		= 0x0504,	/* Host In Interrupt Handler */
+	Cpi		= 0x050C,	/* Command Producer Index */
+	Spi		= 0x0514,	/* Send Producer Index */
+	Rspi		= 0x051C,	/* Receive Standard Producer Index */
+	Rjpi		= 0x0524,	/* Receive Jumbo Producer Index */
+	Rmpi		= 0x052C,	/* Receive Mini Producer Index */
+
+	Mac		= 0x0600,	/* MAC Address */
+	Gip		= 0x0608,	/* General Information Pointer */
+	Om		= 0x0618,	/* Operating Mode */
+	DMArc		= 0x061C,	/* DMA Read Configuration */
+	DMAwc		= 0x0620,	/* DMA Write Configuration */
+	Tbr		= 0x0624,	/* Transmit Buffer Ratio */
+	Eci		= 0x0628,	/* Event Consumer Index */
+	Cci		= 0x062C,	/* Command Consumer Index */
+
+	Rct		= 0x0630,	/* Receive Coalesced Ticks */
+	Sct		= 0x0634,	/* Send Coalesced Ticks */
+	St		= 0x0638,	/* Stat Ticks */
+	SmcBD		= 0x063C,	/* Send Max. Coalesced BDs */
+	RmcBD		= 0x0640,	/* Receive Max. Coalesced BDs */
+	Nt		= 0x0644,	/* NIC Tracing */
+	Gln		= 0x0648,	/* Gigabit Link Negotiation */
+	Fln		= 0x064C,	/* 10/100 Link Negotiation */
+	Ifx		= 0x065C,	/* Interface Index */
+	IfMTU		= 0x0660,	/* Interface MTU */
+	Mi		= 0x0664,	/* Mask Interrupts */
+	Gls		= 0x0668,	/* Gigabit Link State */
+	Fls		= 0x066C,	/* 10/100 Link State */
+
+	Cr		= 0x0700,	/* Command Ring */
+
+	Lmw		= 0x0800,	/* Local Memory Window */
+};
+
+enum {					/* Mhc */
+	Is		= 0x00000001,	/* Interrupt State */
+	Ci		= 0x00000002,	/* Clear Interrupt */
+	Hr		= 0x00000008,	/* Hard Reset */
+	Eebs		= 0x00000010,	/* Enable Endian Byte Swap */
+	Eews		= 0x00000020,	/* Enable Endian Word (64-bit) swap */
+	Mpio		= 0x00000040,	/* Mask PCI Interrupt Output */
+};
+
+enum {					/* Mlc */
+	SRAM512		= 0x00000200,	/* SRAM Bank Size of 512KB */
+	SRAMmask	= 0x00000300,
+	EEclk		= 0x00100000,	/* Serial EEPROM Clock Output */
+	EEdoe		= 0x00200000,	/* Serial EEPROM Data Out Enable */
+	EEdo		= 0x00400000,	/* Serial EEPROM Data Out Value */
+	EEdi		= 0x00800000,	/* Serial EEPROM Data Input */
+};
+
+enum {					/* Mc */
+	SyncSRAM	= 0x00100000,	/* Set Synchronous SRAM Timing */
+};
+
+enum {					/* Ps */
+	PCIwm32		= 0x000000C0,	/* Write Max DMA 32 */
+	PCImrm		= 0x00020000,	/* Use Memory Read Multiple Command */
+	PCI66		= 0x00080000,
+	PCI32		= 0x00100000,
+	PCIrcmd		= 0x06000000,	/* PCI Read Command */
+	PCIwcmd		= 0x70000000,	/* PCI Write Command */
+};
+
+enum {					/* CPUAstate */
+	CPUrf		= 0x00000010,	/* ROM Fail */
+	CPUhalt		= 0x00010000,	/* Halt the internal CPU */
+	CPUhie		= 0x00040000,	/* HALT instruction executed */
+};
+
+enum {					/* Om */
+	BswapBD		= 0x00000002,	/* Byte Swap Buffer Descriptors */
+	WswapBD		= 0x00000004,	/* Word Swap Buffer Descriptors */
+	Warn		= 0x00000008,
+	BswapDMA	= 0x00000010,	/* Byte Swap DMA Data */
+	Only1DMA	= 0x00000040,	/* Only One DMA Active at a time */
+	NoJFrag		= 0x00000200,	/* Don't Fragment Jumbo Frames */
+	Fatal		= 0x40000000,
+};
+
+enum {					/* Lmw */
+	Lmwsz		= 2*1024,	/* Local Memory Window Size */
+
+	/*
+	 * legal values are 0x3800 iff Nsr is 128, 0x3000 iff Nsr is 256,
+	 * or 0x2000 iff Nsr is 512.
+	 */
+	Sr		= 0x3800,	/* Send Ring (accessed via Lmw) */
+};
+
+enum {					/* Link */
+	Lpref		= 0x00008000,	/* Preferred Link */
+	L10MB		= 0x00010000,
+	L100MB		= 0x00020000,
+	L1000MB		= 0x00040000,
+	Lfd		= 0x00080000,	/* Full Duplex */
+	Lhd		= 0x00100000,	/* Half Duplex */
+	Lefc		= 0x00200000,	/* Emit Flow Control Packets */
+	Lofc		= 0x00800000,	/* Obey Flow Control Packets */
+	Lean		= 0x20000000,	/* Enable Autonegotiation/Sensing */
+	Le		= 0x40000000,	/* Link Enable */
+};
+
+typedef struct Host64 {
+	uint	hi;
+	uint	lo;
+} Host64;
+
+typedef struct Ere {			/* Event Ring Element */
+	int	event;			/* event<<24 | code<<12 | index */
+	int	unused;
+} Ere;
+
+typedef int Cmd;			/* cmd<<24 | flags<<12 | index */
+
+typedef struct Rbd {			/* Receive Buffer Descriptor */
+	Host64	addr;
+	int	indexlen;		/* ring-index<<16 | buffer-length */
+	int	flags;			/* only lower 16-bits */
+	int	checksum;		/* ip<<16 |tcp/udp */
+	int	error;			/* only upper 16-bits */
+	int	reserved;
+	void*	opaque;			/* passed to receive return ring */
+} Rbd;
+
+typedef struct Sbd {			/* Send Buffer Descriptor */
+	Host64	addr;
+	int	lenflags;		/* len<<16 |flags */
+	int	reserved;
+} Sbd;
+
+enum {					/* Buffer Descriptor Flags */
+	Fend		= 0x00000004,	/* Frame Ends in this Buffer */
+	Frjr		= 0x00000010,	/* Receive Jumbo Ring Buffer */
+	Funicast	= 0x00000020,	/* Unicast packet (2-bit field) */
+	Fmulticast	= 0x00000040,	/* Multicast packet */
+	Fbroadcast	= 0x00000060,	/* Broadcast packet */
+	Ferror		= 0x00000400,	/* Frame Has Error */
+	Frmr		= 0x00001000,	/* Receive Mini Ring Buffer */
+};
+
+enum {					/* Buffer Error Flags */
+	Ecrc		= 0x00010000,	/* bad CRC */
+	Ecollision	= 0x00020000,	/* collision */
+	Elink		= 0x00040000,	/* link lost */
+	Ephy		= 0x00080000,	/* unspecified PHY frame decode error */
+	Eodd		= 0x00100000,	/* odd number of nibbles */
+	Emac		= 0x00200000,	/* unspecified MAC abort */
+	Elen64		= 0x00400000,	/* short packet */
+	Eresources	= 0x00800000,	/* MAC out of internal resources */
+	Egiant		= 0x01000000,	/* packet too big */
+};
+
+typedef struct Rcb {			/* Ring Control Block */
+	Host64	addr;			/* points to the Rbd ring */
+	int	control;		/* max_len<<16 |flags */
+	int	unused;
+} Rcb;
+
+enum {
+	TcpUdpCksum	= 0x0001,	/* Perform TCP or UDP checksum */
+	IpCksum		= 0x0002,	/* Perform IP checksum */
+	NoPseudoHdrCksum= 0x0008,	/* Don't include the pseudo header */
+	VlanAssist	= 0x0010,	/* Enable VLAN tagging */
+	CoalUpdateOnly	= 0x0020,	/* Coalesce transmit interrupts */
+	HostRing	= 0x0040,	/* Sr in host memory */
+	SnapCksum	= 0x0080,	/* Parse + offload 802.3 SNAP frames */
+	UseExtRxBd	= 0x0100,	/* Extended Rbd for Jumbo frames */
+	RingDisabled	= 0x0200,	/* Jumbo or Mini RCB only */
+};
+
+typedef struct Gib {			/* General Information Block */
+	int	statistics[256];	/* Statistics */
+	Rcb	ercb;			/* Event Ring */
+	Rcb	crcb;			/* Command Ring */
+	Rcb	srcb;			/* Send Ring */
+	Rcb	rsrcb;			/* Receive Standard Ring */
+	Rcb	rjrcb;			/* Receive Jumbo Ring */
+	Rcb	rmrcb;			/* Receive Mini Ring */
+	Rcb	rrrcb;			/* Receive Return Ring */
+	Host64	epp;			/* Event Producer */
+	Host64	rrrpp;			/* Receive Return Ring Producer */
+	Host64	scp;			/* Send Consumer */
+	Host64	rsp;			/* Refresh Stats */
+} Gib;
+
+/*
+ * these sizes are all fixed in the card,
+ * except for Nsr, which has only 3 valid sizes.
+ */
+enum {					/* Host/NIC Interface ring sizes */
+	Ner		= 256,		/* event ring */
+	Ncr		= 64,		/* command ring */
+	Nsr		= 128,		/* send ring: 128, 256 or 512 */
+	Nrsr		= 512,		/* receive standard ring */
+	Nrjr		= 256,		/* receive jumbo ring */
+	Nrmr		= 1024,		/* receive mini ring, optional */
+	Nrrr		= 2048,		/* receive return ring */
+};
+
+enum {
+	NrsrHI		= 72,		/* Fill-level of Rsr (m.b. < Nrsr) */
+	NrsrLO		= 54,		/* Level at which to top-up ring */
+	NrjrHI		= 0,		/* Fill-level of Rjr (m.b. < Nrjr) */
+	NrjrLO		= 0,		/* Level at which to top-up ring */
+	NrmrHI		= 0,		/* Fill-level of Rmr (m.b. < Nrmr) */
+	NrmrLO		= 0,		/* Level at which to top-up ring */
+};
+
+typedef struct Ctlr Ctlr;
+struct Ctlr {
+	int	port;
+	Pcidev*	pcidev;
+	Ctlr*	next;
+	int	active;
+	int	id;
+
+	uchar	ea[Eaddrlen];
+
+	int*	nic;
+	Gib*	gib;
+
+	Ere*	er;
+
+	Lock	srlock;
+	Sbd*	sr;
+	Block**	srb;
+	int	nsr;			/* currently in send ring */
+
+	Rbd*	rsr;
+	int	nrsr;			/* currently in Receive Standard Ring */
+	Rbd*	rjr;
+	int	nrjr;			/* currently in Receive Jumbo Ring */
+	Rbd*	rmr;
+	int	nrmr;			/* currently in Receive Mini Ring */
+	Rbd*	rrr;
+	int	rrrci;			/* Receive Return Ring Consumer Index */
+
+	int	epi[2];			/* Event Producer Index */
+	int	rrrpi[2];		/* Receive Return Ring Producer Index */
+	int	sci[3];			/* Send Consumer Index ([2] is host) */
+
+	int	interrupts;		/* statistics */
+	int	mi;
+	uvlong	ticks;
+
+	int	coalupdateonly;		/* tuning */
+	int	hardwarecksum;
+	int	rct;			/* Receive Coalesce Ticks */
+	int	sct;			/* Send Coalesce Ticks */
+	int	st;			/* Stat Ticks */
+	int	smcbd;			/* Send Max. Coalesced BDs */
+	int	rmcbd;			/* Receive Max. Coalesced BDs */
+};
+
+static Ctlr* ctlrhead;
+static Ctlr* ctlrtail;
+
+#define csr32r(c, r)	(*((c)->nic+((r)/4)))
+#define csr32w(c, r, v)	(*((c)->nic+((r)/4)) = (v))
+
+static void
+sethost64(Host64* host64, void* addr)
+{
+	uvlong uvl;
+
+	uvl = PCIWADDR(addr);
+	host64->hi = uvl>>32;
+	host64->lo = uvl & 0xFFFFFFFFL;
+}
+
+static void
+ga620command(Ctlr* ctlr, int cmd, int flags, int index)
+{
+	int cpi;
+
+	cpi = csr32r(ctlr, Cpi);
+	csr32w(ctlr, Cr+(cpi*4), cmd<<24 | flags<<12 | index);
+	cpi = NEXT(cpi, Ncr);
+	csr32w(ctlr, Cpi, cpi);
+}
+
+static void
+ga620attach(Ether* )
+{
+}
+
+static void
+waitforlink(Ether *edev)
+{
+	int i;
+
+	if (edev->mbps == 0) {
+		print("#l%d: ga620: waiting for link", edev->ctlrno);
+		/* usually takes about 10 seconds */
+		for (i = 0; i < 20 && edev->mbps == 0; i++) {
+			print(".");
+			delay(1000);
+		}
+		print("\n");
+		if (i == 20 && edev->mbps == 0)
+			edev->mbps = 1;			/* buggered */
+	}
+}
+
+static void
+toringbuf(Ether *ether, Block *bp)
+{
+	RingBuf *rb = &ether->rb[ether->ri];
+
+	if (rb->owner == Interface) {
+		rb->len = BLEN(bp);
+		memmove(rb->pkt, bp->rp, rb->len);
+		rb->owner = Host;
+		ether->ri = NEXT(ether->ri, ether->nrb);
+	}
+	/* else no one is expecting packets from the network */
+}
+
+static Block *
+fromringbuf(Ether *ether)
+{
+	RingBuf *tb = &ether->tb[ether->ti];
+	Block *bp = allocb(tb->len);
+
+	if (bp == nil)
+		panic("fromringbuf: nil allocb return");
+	if (bp->wp == nil)
+		panic("fromringbuf: nil bp->wb");
+	memmove(bp->wp, tb->pkt, tb->len);
+	memmove(bp->wp+Eaddrlen, ether->ea, Eaddrlen);
+	bp->wp += tb->len;
+	return bp;
+}
+
+static int
+_ga620transmit(Ether* edev)
+{
+	Sbd *sbd;
+	Block *bp;
+	Ctlr *ctlr;
+	RingBuf *tb;
+	int sci, spi, work;
+
+	/*
+	 * For now there are no smarts here, just empty the
+	 * ring and try to fill it back up. Tuning comes later.
+	 */
+	ctlr = edev->ctlr;
+	waitforlink(edev);
+	ilock(&ctlr->srlock);
+
+	/*
+	 * Free any completed packets.
+	 * Ctlr->sci[0] is where the NIC has got to consuming the ring.
+	 * Ctlr->sci[2] is where the host has got to tidying up after the
+	 * NIC has done with the packets.
+	 */
+	work = 0;
+	for(sci = ctlr->sci[2]; sci != ctlr->sci[0]; sci = NEXT(sci, Nsr)){
+		if(ctlr->srb[sci] == nil)
+			continue;
+		freeb(ctlr->srb[sci]);
+		ctlr->srb[sci] = nil;
+		work++;
+	}
+	ctlr->sci[2] = sci;
+
+	sci = PREV(sci, Nsr);
+
+	tb = &edev->tb[edev->ti];
+	for(spi = csr32r(ctlr, Spi); spi != sci && tb->owner == Interface;
+	    spi = NEXT(spi, Nsr)){
+		bp = fromringbuf(edev);
+
+		sbd = &ctlr->sr[spi];
+		sethost64(&sbd->addr, bp->rp);
+		sbd->lenflags = BLEN(bp)<<16 |Fend;
+
+		ctlr->srb[spi] = bp;
+		work++;
+
+		tb->owner = Host;
+		edev->ti = NEXT(edev->ti, edev->ntb);
+		tb = &edev->tb[edev->ti];
+	}
+	csr32w(ctlr, Spi, spi);
+
+	iunlock(&ctlr->srlock);
+
+	return work;
+}
+
+static void
+ga620transmit(Ether* edev)
+{
+	_ga620transmit(edev);
+}
+
+static void
+ga620replenish(Ctlr* ctlr)
+{
+	Rbd *rbd;
+	int rspi;
+	Block *bp;
+
+	rspi = csr32r(ctlr, Rspi);
+	while(ctlr->nrsr < NrsrHI){
+		if((bp = allocb(ETHERMAXTU+4)) == nil)
+			break;
+		rbd = &ctlr->rsr[rspi];
+		sethost64(&rbd->addr, bp->rp);
+		rbd->indexlen = rspi<<16 | (ETHERMAXTU+4);
+		rbd->flags = 0;
+		rbd->opaque = bp;
+
+		rspi = NEXT(rspi, Nrsr);
+		ctlr->nrsr++;
+	}
+	csr32w(ctlr, Rspi, rspi);
+}
+
+static void
+ga620event(Ether *edev, int eci, int epi)
+{
+	unsigned event, code;
+	Ctlr *ctlr;
+
+	ctlr = edev->ctlr;
+	while(eci != epi){
+		event = ctlr->er[eci].event;
+		code = (event >> 12) & ((1<<12)-1);
+		switch(event>>24){
+		case 0x01:		/* firmware operational */
+			/* host stack (us) is up.  3rd arg of 2 means down. */
+			ga620command(ctlr, 0x01, 0x01, 0x00);
+			/*
+			 * link negotiation: any speed is okay.
+			 * 3rd arg of 1 selects gigabit only; 2 10/100 only.
+			 */
+			ga620command(ctlr, 0x0B, 0x00, 0x00);
+			print("#l%d: ga620: port %8.8uX: firmware is up\n",
+				edev->ctlrno, ctlr->port);
+			break;
+		case 0x04:		/* statistics updated */
+			break;
+		case 0x06:		/* link state changed */
+			switch (code) {
+			case 1:
+				edev->mbps = 1000;
+				break;
+			case 2:
+				print("#l%d: link down\n", edev->ctlrno);
+				break;
+			case 3:
+				edev->mbps = 100;	/* it's 10 or 100 */
+				break;
+			}
+			if (code != 2)
+				print("#l%d: %dMbps link up\n",
+					edev->ctlrno, edev->mbps);
+			break;
+		case 0x07:		/* event error */
+		default:
+			print("#l%d: ga620: er[%d] = %8.8uX\n", edev->ctlrno,
+				eci, event);
+			break;
+		}
+		eci = NEXT(eci, Ner);
+	}
+	csr32w(ctlr, Eci, eci);
+}
+
+static void
+ga620receive(Ether* edev)
+{
+	int len;
+	Rbd *rbd;
+	Block *bp;
+	Ctlr* ctlr;
+
+	ctlr = edev->ctlr;
+	while(ctlr->rrrci != ctlr->rrrpi[0]){
+		rbd = &ctlr->rrr[ctlr->rrrci];
+		/*
+		 * Errors are collected in the statistics block so
+		 * no need to tally them here, let ifstat do the work.
+		 */
+		len = rbd->indexlen & 0xFFFF;
+		if(!(rbd->flags & Ferror) && len != 0){
+			bp = rbd->opaque;
+			bp->wp = bp->rp+len;
+
+			toringbuf(edev, bp);
+		} else
+			freeb(rbd->opaque);
+		rbd->opaque = nil;
+
+		if(rbd->flags & Frjr)
+			ctlr->nrjr--;
+		else if(rbd->flags & Frmr)
+			ctlr->nrmr--;
+		else
+			ctlr->nrsr--;
+
+		ctlr->rrrci = NEXT(ctlr->rrrci, Nrrr);
+	}
+}
+
+static void
+ga620interrupt(Ureg*, void* arg)
+{
+	int csr, ie, work;
+	Ctlr *ctlr;
+	Ether *edev;
+
+	edev = arg;
+	ctlr = edev->ctlr;
+
+	if(!(csr32r(ctlr, Mhc) & Is))
+		return;
+
+	ctlr->interrupts++;
+	csr32w(ctlr, Hi, 1);
+
+	ie = 0;
+	work = 0;
+	while(ie < 2){
+		if(ctlr->rrrci != ctlr->rrrpi[0]){
+			ga620receive(edev);
+			work = 1;
+		}
+
+		if(_ga620transmit(edev) != 0)
+			work = 1;
+
+		csr = csr32r(ctlr, Eci);
+		if(csr != ctlr->epi[0]){
+			ga620event(edev, csr, ctlr->epi[0]);
+			work = 1;
+		}
+
+		if(ctlr->nrsr <= NrsrLO)
+			ga620replenish(ctlr);
+		if(work == 0){
+			if(ie == 0)
+				csr32w(ctlr, Hi, 0);
+			ie++;
+		}
+		work = 0;
+	}
+}
+
+static void
+ga620lmw(Ctlr* ctlr, int addr, int* data, int len)
+{
+	int i, l, lmw, v;
+
+	/*
+	 * Write to or clear ('data' == nil) 'len' bytes of the NIC
+	 * local memory at address 'addr'.
+	 * The destination address and count should be 32-bit aligned.
+	 */
+	v = 0;
+	while(len > 0){
+		/*
+		 * 1) Set the window. The (Lmwsz-1) bits are ignored
+		 *    in Wba when accessing through the local memory window;
+		 * 2) Find the minimum of how many bytes still to
+		 *    transfer and how many left in this window;
+		 * 3) Create the offset into the local memory window in the
+		 *    shared memory space then copy (or zero) the data;
+		 * 4) Bump the counts.
+		 */
+		csr32w(ctlr, Wba, addr);
+
+		l = ROUNDUP(addr+1, Lmwsz) - addr;
+		if(l > len)
+			l = len;
+
+		lmw = Lmw + (addr & (Lmwsz-1));
+		for(i = 0; i < l; i += 4){
+			if(data != nil)
+				v = *data++;
+			csr32w(ctlr, lmw+i, v);
+		}
+
+		len -= l;
+		addr += l;
+	}
+}
+
+static int
+ga620init(Ether* edev)
+{
+	Ctlr *ctlr;
+	Host64 host64;
+	int csr, ea, i, flags;
+
+	ctlr = edev->ctlr;
+
+	/*
+	 * Load the MAC address.
+	 */
+	ea = edev->ea[0]<<8 | edev->ea[1];
+	csr32w(ctlr, Mac, ea);
+	ea = edev->ea[2]<<24 | edev->ea[3]<<16 | edev->ea[4]<<8 | edev->ea[5];
+	csr32w(ctlr, Mac+4, ea);
+
+	/*
+	 * General Information Block.
+	 */
+	ctlr->gib = malloc(sizeof(Gib));
+	sethost64(&host64, ctlr->gib);
+	csr32w(ctlr, Gip, host64.hi);
+	csr32w(ctlr, Gip+4, host64.lo);
+
+	/*
+	 * Event Ring.
+	 * This is located in host memory. Allocate the ring,
+	 * tell the NIC where it is and initialise the indices.
+	 */
+	ctlr->er = malign(sizeof(Ere)*Ner);
+	sethost64(&ctlr->gib->ercb.addr, ctlr->er);
+	sethost64(&ctlr->gib->epp, ctlr->epi);
+	csr32w(ctlr, Eci, 0);
+
+	/*
+	 * Command Ring.
+	 * This is located in the General Communications Region
+	 * and so the value placed in the Rcb is unused, the NIC
+	 * knows where it is. Stick in the value according to
+	 * the datasheet anyway.
+	 * Initialise the ring and indices.
+	 */
+	ctlr->gib->crcb.addr.lo = Cr - 0x400;
+	for(i = 0; i < Ncr*4; i += 4)
+		csr32w(ctlr, Cr+i, 0);
+	csr32w(ctlr, Cpi, 0);
+	csr32w(ctlr, Cci, 0);
+
+	/*
+	 * Send Ring.
+	 * This ring is either in NIC memory at a fixed location depending
+	 * on how big the ring is or it is in host memory. If in NIC
+	 * memory it is accessed via the Local Memory Window; with a send
+	 * ring size of 128 the window covers the whole ring and then need
+	 * only be set once:
+	 *	ctlr->sr = (uchar*)ctlr->nic+Lmw;
+	 *	ga620lmw(ctlr, Sr, nil, sizeof(Sbd)*Nsr);
+	 *	ctlr->gib->srcb.addr.lo = Sr;
+	 * There is nowhere in the Sbd to hold the Block* associated
+	 * with this entry so an external array must be kept.
+	 */
+	ctlr->sr = malign(sizeof(Sbd)*Nsr);
+	sethost64(&ctlr->gib->srcb.addr, ctlr->sr);
+	if(ctlr->hardwarecksum)
+		flags = TcpUdpCksum|NoPseudoHdrCksum|HostRing;
+	else 
+		flags = HostRing;
+	if(ctlr->coalupdateonly) 
+		flags |= CoalUpdateOnly;
+	ctlr->gib->srcb.control = Nsr<<16 | flags;
+	sethost64(&ctlr->gib->scp, ctlr->sci);
+	csr32w(ctlr, Spi, 0);
+	ctlr->srb = malloc(sizeof(Block*)*Nsr);
+
+	/*
+	 * Receive Standard Ring.
+	 */
+	ctlr->rsr = malign(sizeof(Rbd)*Nrsr);
+	sethost64(&ctlr->gib->rsrcb.addr, ctlr->rsr);
+	if(ctlr->hardwarecksum)
+		flags = TcpUdpCksum|NoPseudoHdrCksum;
+	else
+		flags = 0;
+	ctlr->gib->rsrcb.control = (ETHERMAXTU+4)<<16 | flags;
+	csr32w(ctlr, Rspi, 0);
+
+	/*
+	 * Jumbo and Mini Rings. Unused for now.
+	 */
+	ctlr->gib->rjrcb.control = RingDisabled;
+	ctlr->gib->rmrcb.control = RingDisabled;
+
+	/*
+	 * Receive Return Ring.
+	 * This is located in host memory. Allocate the ring,
+	 * tell the NIC where it is and initialise the indices.
+	 */
+	ctlr->rrr = malign(sizeof(Rbd)*Nrrr);
+	sethost64(&ctlr->gib->rrrcb.addr, ctlr->rrr);
+	ctlr->gib->rrrcb.control = Nrrr<<16 | 0;
+	sethost64(&ctlr->gib->rrrpp, ctlr->rrrpi);
+	ctlr->rrrci = 0;
+
+	/*
+	 * Refresh Stats Pointer.
+	 * For now just point it at the existing statistics block.
+	 */
+	sethost64(&ctlr->gib->rsp, ctlr->gib->statistics);
+
+	/*
+	 * DMA configuration.
+	 * Use the recommended values.
+	 */
+	csr32w(ctlr, DMArc, 0x80);
+	csr32w(ctlr, DMAwc, 0x80);
+
+	/*
+	 * Transmit Buffer Ratio.
+	 * Set to 1/3 of available buffer space (units are 1/64ths)
+	 * if using Jumbo packets, ~64KB otherwise (assume 1MB on NIC).
+	 */
+	if(NrjrHI > 0 || Nsr > 128)
+		csr32w(ctlr, Tbr, 64/3);
+	else
+		csr32w(ctlr, Tbr, 4);
+
+	/*
+	 * Tuneable parameters.
+	 * These defaults are based on the tuning hints in the Alteon
+	 * Host/NIC Software Interface Definition and example software.
+	 */
+	ctlr->rct = 1 /*100*/;
+	csr32w(ctlr, Rct, ctlr->rct);
+	ctlr->sct = 0;
+	csr32w(ctlr, Sct, ctlr->sct);
+	ctlr->st = 1000000;
+	csr32w(ctlr, St, ctlr->st);
+	ctlr->smcbd = Nsr/4;
+	csr32w(ctlr, SmcBD, ctlr->smcbd);
+	ctlr->rmcbd = 4 /*6*/;
+	csr32w(ctlr, RmcBD, ctlr->rmcbd);
+
+	/*
+	 * Enable DMA Assist Logic.
+	 */
+	csr = csr32r(ctlr, DMAas) & ~0x03;
+	csr32w(ctlr, DMAas, csr|0x01);
+
+	/*
+	 * Link negotiation.
+	 * The bits are set here but the NIC must be given a command
+	 * once it is running to set negotiation in motion.
+	 */
+	csr32w(ctlr, Gln, Le|Lean|Lofc|Lfd|L1000MB|Lpref);
+	csr32w(ctlr, Fln, Le|Lean|Lhd|Lfd|L100MB|L10MB);
+
+	/*
+	 * A unique index for this controller and the maximum packet
+	 * length expected.
+	 * For now only standard packets are expected.
+	 */
+	csr32w(ctlr, Ifx, 1);
+	csr32w(ctlr, IfMTU, ETHERMAXTU+4);
+
+	/*
+	 * Enable Interrupts.
+	 * There are 3 ways to mask interrupts - a bit in the Mhc (which
+	 * is already cleared), the Mi register and the Hi mailbox.
+	 * Writing to the Hi mailbox has the side-effect of clearing the
+	 * PCI interrupt.
+	 */
+	csr32w(ctlr, Mi, 0);
+	csr32w(ctlr, Hi, 0);
+
+	/*
+	 * Start the firmware.
+	 */
+	csr32w(ctlr, CPUApc, tigon2FwStartAddr);
+	csr = csr32r(ctlr, CPUAstate) & ~CPUhalt;
+	csr32w(ctlr, CPUAstate, csr);
+
+	return 0;
+}
+
+static int
+at24c32io(Ctlr* ctlr, char* op, int data)
+{
+	char *lp, *p;
+	int i, loop, mlc, r;
+
+	mlc = csr32r(ctlr, Mlc);
+
+	r = 0;
+	loop = -1;
+	lp = nil;
+	for(p = op; *p != '\0'; p++){
+		switch(*p){
+		default:
+			return -1;
+		case ' ':
+			continue;
+		case ':':			/* start of 8-bit loop */
+			if(lp != nil)
+				return -1;
+			lp = p;
+			loop = 7;
+			continue;
+		case ';':			/* end of 8-bit loop */
+			if(lp == nil)
+				return -1;
+			loop--;
+			if(loop >= 0)
+				p = lp;
+			else
+				lp = nil;
+			continue;
+		case 'C':			/* assert clock */
+			mlc |= EEclk;
+			break;
+		case 'c':			/* deassert clock */
+			mlc &= ~EEclk;
+			break;
+		case 'D':			/* next bit in 'data' byte */
+			if(loop < 0)
+				return -1;
+			if(data & (1<<loop))
+				mlc |= EEdo;
+			else
+				mlc &= ~EEdo;
+			break;
+		case 'E':			/* enable data output */
+			mlc |= EEdoe;
+			break;
+		case 'e':			/* disable data output */
+			mlc &= ~EEdoe;
+			break;
+		case 'I':			/* input bit */
+			i = (csr32r(ctlr, Mlc) & EEdi) != 0;
+			if(loop >= 0)
+				r |= (i<<loop);
+			else
+				r = i;
+			continue;
+		case 'O':			/* assert data output */
+			mlc |= EEdo;
+			break;
+		case 'o':			/* deassert data output */
+			mlc &= ~EEdo;
+			break;
+		}
+		csr32w(ctlr, Mlc, mlc);
+		microdelay(1);
+	}
+	if(loop >= 0)
+		return -1;
+	return r;
+}
+
+static int
+at24c32r(Ctlr* ctlr, int addr)
+{
+	int data;
+
+	/*
+	 * Read a byte at address 'addr' from the Atmel AT24C32
+	 * Serial EEPROM. The 2-wire EEPROM access is controlled
+	 * by 4 bits in Mlc. See the AT24C32 datasheet for
+	 * protocol details.
+	 */
+	/*
+	 * Start condition - a high to low transition of data
+	 * with the clock high must precede any other command.
+	 */
+	at24c32io(ctlr, "OECoc", 0);
+
+	/*
+	 * Perform a random read at 'addr'. A dummy byte
+	 * write sequence is performed to clock in the device
+	 * and data word addresses (0 and 'addr' respectively).
+	 */
+	data = -1;
+	if(at24c32io(ctlr, "oE :DCc; oeCIc", 0xA0) != 0)
+		goto stop;
+	if(at24c32io(ctlr, "oE :DCc; oeCIc", addr>>8) != 0)
+		goto stop;
+	if(at24c32io(ctlr, "oE :DCc; oeCIc", addr) != 0)
+		goto stop;
+
+	/*
+	 * Now send another start condition followed by a
+	 * request to read the device. The EEPROM responds
+	 * by clocking out the data.
+	 */
+	at24c32io(ctlr, "OECoc", 0);
+	if(at24c32io(ctlr, "oE :DCc; oeCIc", 0xA1) != 0)
+		goto stop;
+	data = at24c32io(ctlr, ":CIc;", 0xA1);
+
+stop:
+	/*
+	 * Stop condition - a low to high transition of data
+	 * with the clock high is a stop condition. After a read
+	 * sequence, the stop command will place the EEPROM in
+	 * a standby power mode.
+	 */
+	at24c32io(ctlr, "oECOc", 0);
+
+	return data;
+}
+
+static int
+ga620detach(Ctlr* ctlr)
+{
+	int timeo;
+
+	/*
+	 * Hard reset (don't know which endian so catch both);
+	 * enable for little-endian mode;
+	 * wait for code to be loaded from serial EEPROM or flash;
+	 * make sure CPU A is halted.
+	 */
+	csr32w(ctlr, Mhc, Hr<<24 | Hr);
+	csr32w(ctlr, Mhc, (Eews|Ci)<<24 | Eews|Ci);
+
+	microdelay(1);
+	for(timeo = 0; timeo < 500000; timeo++){
+		if((csr32r(ctlr, CPUAstate) & (CPUhie|CPUrf)) == CPUhie)
+			break;
+		microdelay(1);
+	}
+	if((csr32r(ctlr, CPUAstate) & (CPUhie|CPUrf)) != CPUhie)
+		return -1;
+	csr32w(ctlr, CPUAstate, CPUhalt);
+
+	/*
+	 * After reset, CPU B seems to be stuck in 'CPUrf'.
+	 * Worry about it later.
+	 */
+	csr32w(ctlr, CPUBstate, CPUhalt);
+
+	return 0;
+}
+
+static void
+ga620shutdown(Ether* ether)
+{
+print("ga620shutdown\n");
+	ga620detach(ether->ctlr);
+}
+
+static int
+ga620reset(Ctlr* ctlr)
+{
+	int cls, csr, i, r;
+
+	if(ga620detach(ctlr) < 0)
+		return -1;
+
+	/*
+	 * Tigon 2 PCI NICs have 512KB SRAM per bank.
+	 * Clear out any lingering serial EEPROM state
+	 * bits.
+	 */
+	csr = csr32r(ctlr, Mlc) & ~(EEdi|EEdo|EEdoe|EEclk|SRAMmask);
+	csr32w(ctlr, Mlc, SRAM512|csr);
+	csr = csr32r(ctlr, Mc);
+	csr32w(ctlr, Mc, SyncSRAM|csr);
+
+	/*
+	 * Initialise PCI State register.
+	 * If PCI Write-and-Invalidate is enabled set the max write DMA
+	 * value to the host cache-line size (32 on Pentium or later).
+	 */
+	csr = csr32r(ctlr, Ps) & (PCI32|PCI66);
+	csr |= PCIwcmd|PCIrcmd|PCImrm;
+	if(ctlr->pcidev->pcr & 0x0010){
+		cls = pcicfgr8(ctlr->pcidev, PciCLS) * 4;
+		if(cls != 32)
+			pcicfgw8(ctlr->pcidev, PciCLS, 32/4);
+		csr |= PCIwm32;
+	}
+	csr32w(ctlr, Ps, csr);
+
+	/*
+	 * Operating Mode.
+	 */
+	csr32w(ctlr, Om, Fatal|NoJFrag|BswapDMA|WswapBD);
+
+	/*
+	 * Snarf the MAC address from the serial EEPROM.
+	 */
+	for(i = 0; i < Eaddrlen; i++){
+		if((r = at24c32r(ctlr, 0x8E+i)) == -1)
+			return -1;
+		ctlr->ea[i] = r;
+	}
+
+	/*
+	 * Load the firmware.
+	 */
+	ga620lmw(ctlr, tigon2FwTextAddr, tigon2FwText, tigon2FwTextLen);
+	ga620lmw(ctlr, tigon2FwRodataAddr, tigon2FwRodata, tigon2FwRodataLen);
+	ga620lmw(ctlr, tigon2FwDataAddr, tigon2FwData, tigon2FwDataLen);
+	ga620lmw(ctlr, tigon2FwSbssAddr, nil, tigon2FwSbssLen);
+	ga620lmw(ctlr, tigon2FwBssAddr, nil, tigon2FwBssLen);
+
+	/*
+	 * we will eventually get events telling us that the firmware is
+	 * up and that the link is up.
+	 */
+	return 0;
+}
+
+static void
+ga620pci(void)
+{
+	int port;
+	Pcidev *p;
+	Ctlr *ctlr;
+
+	p = nil;
+	while(p = pcimatch(p, 0, 0)){
+		if(p->ccrb != 0x02 || p->ccru != 0)
+			continue;
+
+		switch(p->did<<16 | p->vid){
+		default:
+			continue;
+		case 0x620A<<16 | 0x1385:	/* Netgear GA620 fiber */
+		case 0x630A<<16 | 0x1385:	/* Netgear GA620T copper */
+		case 0x0001<<16 | 0x12AE:	/* Alteon Acenic fiber
+						 * and DEC DEGPA-SA */
+		case 0x0002<<16 | 0x12AE:	/* Alteon Acenic copper */
+		case 0x0009<<16 | 0x10A9:	/* SGI Acenic */
+			break;
+		}
+
+		port = upamalloc(p->mem[0].bar & ~0x0F, p->mem[0].size, 0);
+		if(port == 0){
+			print("ga620: can't map %d @ 0x%8.8luX\n",
+				p->mem[0].size, p->mem[0].bar);
+			continue;
+		}
+
+		ctlr = malloc(sizeof(Ctlr));
+		ctlr->port = port;
+		ctlr->pcidev = p;
+		ctlr->id = p->did<<16 | p->vid;
+
+		ctlr->nic = KADDR(ctlr->port);
+		if(ga620reset(ctlr)){
+			free(ctlr);
+			continue;
+		}
+
+		if(ctlrhead != nil)
+			ctlrtail->next = ctlr;
+		else
+			ctlrhead = ctlr;
+		ctlrtail = ctlr;
+	}
+}
+
+int
+ga620pnp(Ether* edev)
+{
+	Ctlr *ctlr;
+	uchar ea[Eaddrlen];
+
+	if(ctlrhead == nil)
+		ga620pci();
+
+	/*
+	 * Any adapter matches if no edev->port is supplied,
+	 * otherwise the ports must match.
+	 */
+	for(ctlr = ctlrhead; ctlr != nil; ctlr = ctlr->next){
+		if(ctlr->active)
+			continue;
+		if(edev->port == 0 || edev->port == ctlr->port){
+			ctlr->active = 1;
+			break;
+		}
+	}
+	if(ctlr == nil)
+		return -1;
+
+	edev->ctlr = ctlr;
+	edev->port = ctlr->port;
+	edev->irq = ctlr->pcidev->intl;
+	edev->tbdf = ctlr->pcidev->tbdf;
+
+	/*
+	 * Check if the adapter's station address is to be overridden.
+	 * If not, read it from the EEPROM and set in ether->ea prior to
+	 * loading the station address in the hardware.
+	 */
+	memset(ea, 0, Eaddrlen);
+	if(memcmp(ea, edev->ea, Eaddrlen) == 0)
+		memmove(edev->ea, ctlr->ea, Eaddrlen);
+
+	ga620init(edev);		/* enables interrupts */
+
+	/*
+	 * Linkage to the generic ethernet driver.
+	 */
+	edev->attach = ga620attach;
+	edev->transmit = ga620transmit;
+	edev->interrupt = ga620interrupt;
+	edev->detach = ga620shutdown;
+	return 0;
+}
--- /dev/null
+++ b/os/boot/pc/etherga620fw.h
@@ -1,0 +1,4858 @@
+/* Generated by genfw.c */
+#define tigon2FwReleaseMajor 0xc
+#define tigon2FwReleaseMinor 0x4
+#define tigon2FwReleaseFix 0xb
+#define tigon2FwStartAddr 0x00004000
+#define tigon2FwTextAddr 0x00004000
+#define tigon2FwTextLen 0x11bc0
+#define tigon2FwRodataAddr 0x00015bc0
+#define tigon2FwRodataLen 0x10d0
+#define tigon2FwDataAddr 0x00016cc0
+#define tigon2FwDataLen 0x1c0
+#define tigon2FwSbssAddr 0x00016e80
+#define tigon2FwSbssLen 0xcc
+#define tigon2FwBssAddr 0x00016f50
+#define tigon2FwBssLen 0x20c0
+static int tigon2FwText[/*(MAX_TEXT_LEN/4) + 1*/] = {
+0x0, 
+0x10000003, 0x0, 0xd, 0xd, 
+0x3c1d0001, 0x8fbd6d20, 0x3a0f021, 0x3c100000, 
+0x26104000, 0xc0010c0, 0x0, 0xd, 
+0x3c1d0001, 0x8fbd6d24, 0x3a0f021, 0x3c100000, 
+0x26104000, 0xc0017e0, 0x0, 0xd, 
+0x0, 0x0, 0x0, 0x0, 
+0x0, 0x0, 0x0, 0x0, 
+0x0, 0x0, 0x0, 0x0, 
+0x0, 0x0, 0x0, 0x0, 
+0x0, 0x0, 0x0, 0x0, 
+0x0, 0x0, 0x0, 0x0, 
+0x0, 0x0, 0x0, 0x0, 
+0x0, 0x0, 0x0, 0x0, 
+0x0, 0x0, 0x0, 0x0, 
+0x0, 0x0, 0x0, 0x0, 
+0x0, 0x0, 0x0, 0x2000008, 
+0x0, 0x800172f, 0x3c0a0001, 0x800172f, 
+0x3c0a0002, 0x800172f, 0x0, 0x8002cac, 
+0x0, 0x8002c4f, 0x0, 0x800172f, 
+0x3c0a0004, 0x800328a, 0x0, 0x8001a52, 
+0x0, 0x800394d, 0x0, 0x80038f4, 
+0x0, 0x800172f, 0x3c0a0006, 0x80039bb, 
+0x3c0a0007, 0x800172f, 0x3c0a0008, 0x800172f, 
+0x3c0a0009, 0x8003a13, 0x0, 0x8002ea6, 
+0x0, 0x800172f, 0x3c0a000b, 0x800172f, 
+0x3c0a000c, 0x800172f, 0x3c0a000d, 0x80028fb, 
+0x0, 0x8002890, 0x0, 0x800172f, 
+0x3c0a000e, 0x800208c, 0x0, 0x8001964, 
+0x0, 0x8001a04, 0x0, 0x8003ca6, 
+0x0, 0x8003c94, 0x0, 0x800172f, 
+0x0, 0x800191a, 0x0, 0x800172f, 
+0x0, 0x800172f, 0x3c0a0013, 0x800172f, 
+0x3c0a0014, 0x0, 0x0, 0x0, 
+0x0, 0x0, 0x0, 0x0, 
+0x0, 0x0, 0x0, 0x0, 
+0x0, 0x0, 0x0, 0x0, 
+0x0, 0x0, 0x0, 0x0, 
+0x0, 0x0, 0x0, 0x0, 
+0x0, 0x0, 0x0, 0x0, 
+0x0, 0x0, 0x0, 0x0, 
+0x0, 0x0, 0x0, 0x0, 
+0x0, 0x0, 0x0, 0x0, 
+0x0, 0x0, 0x0, 0x0, 
+0x0, 0x0, 0x0, 0x0, 
+0x0, 0x0, 0x0, 0x0, 
+0x0, 0x0, 0x0, 0x0, 
+0x0, 0x0, 0x0, 0x0, 
+0x0, 0x0, 0x0, 0x27bdffe0, 
+0x3c1cc000, 0xafbf001c, 0xafb00018, 0x8f820140, 
+0x24030003, 0xaf8300ec, 0x34420004, 0xc002b20, 
+0xaf820140, 0x3c0100c0, 0xc001763, 0xac203ffc, 
+0x401821, 0x3c020010, 0x3c010001, 0xac236e9c, 
+0x10620011, 0x43102b, 0x14400002, 0x3c020020, 
+0x3c020008, 0x1062000c, 0x24050100, 0x3c060001, 
+0x8cc66e9c, 0x3c040001, 0x24845c74, 0x3821, 
+0xafa00010, 0xc002b3b, 0xafa00014, 0x3c020020, 
+0x3c010001, 0xac226e9c, 0x24020008, 0x3c010001, 
+0xac226eb4, 0x2402001f, 0x3c010001, 0xac226ec4, 
+0x24020016, 0x3c010001, 0xac226e98, 0x3c05fffe, 
+0x34a56f08, 0x3c020001, 0x8c426e9c, 0x3c030002, 
+0x24639010, 0x3c040001, 0x8c846cc4, 0x431023, 
+0x14800002, 0x458021, 0x2610fa38, 0x2402f000, 
+0x2028024, 0xc001785, 0x2002021, 0x2022823, 
+0x3c040020, 0x821823, 0x651823, 0x247bb000, 
+0x3c03fffe, 0x3463bf08, 0x363b821, 0x3c0600bf, 
+0x34c6f000, 0x3c070001, 0x8ce76cc0, 0x3c0300bf, 
+0x3463e000, 0x852023, 0x3c010001, 0xac246ea8, 
+0x822023, 0x3c010001, 0xac256e90, 0x52842, 
+0x3c010001, 0xac226e84, 0x27620ffc, 0x3c010001, 
+0xac226d20, 0x27621ffc, 0xdb3023, 0x7b1823, 
+0x3c010001, 0xac246e88, 0x3c010001, 0xac256eac, 
+0x3c010001, 0xac226d24, 0xaf860150, 0x10e00011, 
+0xaf830250, 0x3c1d0001, 0x8fbd6ccc, 0x3a0f021, 
+0xc001749, 0x0, 0x3c020001, 0x8c426cd0, 
+0x3c030001, 0x8c636cd4, 0x2442fe00, 0x24630200, 
+0x3c010001, 0xac226cd0, 0x3c010001, 0x10000004, 
+0xac236cd4, 0x3c1d0001, 0x8fbd6d20, 0x3a0f021, 
+0x3c020001, 0x8c426cc4, 0x1040000d, 0x26fafa38, 
+0x3c020001, 0x8c426cd0, 0x3c030001, 0x8c636cd4, 
+0x3c1a0001, 0x8f5a6cd4, 0x2442fa38, 0x246305c8, 
+0x3c010001, 0xac226cd0, 0x3c010001, 0xac236cd4, 
+0x3c020001, 0x8c426cc8, 0x14400003, 0x0, 
+0x3c010001, 0xac206cd0, 0xc001151, 0x0, 
+0x8fbf001c, 0x8fb00018, 0x3e00008, 0x27bd0020, 
+0x3c020001, 0x8c426cd0, 0x3c030001, 0x8c636cd4, 
+0x27bdff98, 0xafb00048, 0x3c100001, 0x8e1066b8, 
+0xafb20050, 0x3c120000, 0x26524100, 0xafbf0060, 
+0xafbe005c, 0xafb50058, 0xafb30054, 0xafb1004c, 
+0xafa20034, 0xafa30030, 0xafa00010, 0xafa00014, 
+0x8f860040, 0x3c040001, 0x24845c80, 0x24050200, 
+0x3c010001, 0xac326e80, 0xc002b3b, 0x2003821, 
+0x8f830040, 0x3c02f000, 0x621824, 0x3c026000, 
+0x1062000b, 0xa3a0003f, 0x240e0001, 0x3c040001, 
+0x24845c88, 0xa3ae003f, 0xafa00010, 0xafa00014, 
+0x8f860040, 0x24050300, 0xc002b3b, 0x2003821, 
+0x8f820240, 0x3c030001, 0x431025, 0xaf820240, 
+0xaf800048, 0x8f820048, 0x14400005, 0x0, 
+0xaf800048, 0x8f820048, 0x10400004, 0x0, 
+0xaf800048, 0x10000003, 0x2e02021, 0xaf80004c, 
+0x2e02021, 0x3c050001, 0xc002ba8, 0x34a540f8, 
+0x3402021, 0xc002ba8, 0x240505c8, 0x3c020001, 
+0x8c426ea8, 0x3c0d0001, 0x8dad6e88, 0x3c030001, 
+0x8c636e84, 0x3c080001, 0x8d086e90, 0x3c090001, 
+0x8d296eac, 0x3c0a0001, 0x8d4a6eb4, 0x3c0b0001, 
+0x8d6b6ec4, 0x3c0c0001, 0x8d8c6e98, 0x3c040001, 
+0x24845c94, 0x24050400, 0xaf42013c, 0x8f42013c, 
+0x24060001, 0x24070001, 0xaf400000, 0xaf4d0138, 
+0xaf430144, 0xaf480148, 0xaf49014c, 0xaf4a0150, 
+0xaf4b0154, 0xaf4c0158, 0x2442ff80, 0xaf420140, 
+0x24020001, 0xafa20010, 0xc002b3b, 0xafa00014, 
+0x8f420138, 0xafa20010, 0x8f42013c, 0xafa20014, 
+0x8f460144, 0x8f470148, 0x3c040001, 0x24845ca0, 
+0xc002b3b, 0x24050500, 0xafb70010, 0xafba0014, 
+0x8f46014c, 0x8f470150, 0x3c040001, 0x24845cac, 
+0xc002b3b, 0x24050600, 0x3c020001, 0x8c426e9c, 
+0x3603821, 0x3c060002, 0x24c69010, 0x2448ffff, 
+0x1061824, 0xe81024, 0x43102b, 0x10400006, 
+0x24050900, 0x3c040001, 0x24845cb8, 0xafa80010, 
+0xc002b3b, 0xafa00014, 0x8f82000c, 0xafa20010, 
+0x8f82003c, 0xafa20014, 0x8f860000, 0x8f870004, 
+0x3c040001, 0x24845cc4, 0xc002b3b, 0x24051000, 
+0x8c020220, 0x8c030224, 0x8c060218, 0x8c07021c, 
+0x3c040001, 0x24845ccc, 0x24051100, 0xafa20010, 
+0xc002b3b, 0xafa30014, 0xaf800054, 0xaf80011c, 
+0x8c020218, 0x30420002, 0x10400009, 0x0, 
+0x8c020220, 0x3c030002, 0x34630004, 0x431025, 
+0xaf42000c, 0x8c02021c, 0x10000008, 0x34420004, 
+0x8c020220, 0x3c030002, 0x34630006, 0x431025, 
+0xaf42000c, 0x8c02021c, 0x34420006, 0xaf420014, 
+0x8c020218, 0x30420010, 0x1040000a, 0x0, 
+0x8c02021c, 0x34420004, 0xaf420010, 0x8c020220, 
+0x3c03000a, 0x34630004, 0x431025, 0x10000009, 
+0xaf420008, 0x8c020220, 0x3c03000a, 0x34630006, 
+0x431025, 0xaf420008, 0x8c02021c, 0x34420006, 
+0xaf420010, 0x24020001, 0xaf8200a0, 0xaf8200b0, 
+0x8f830054, 0x8f820054, 0xaf8000d0, 0xaf8000c0, 
+0x10000002, 0x24630064, 0x8f820054, 0x621023, 
+0x2c420065, 0x1440fffc, 0x0, 0x8c040208, 
+0x8c05020c, 0x26e20028, 0xaee20020, 0x24020490, 
+0xaee20010, 0xaee40008, 0xaee5000c, 0x26e40008, 
+0x8c820000, 0x8c830004, 0xaf820090, 0xaf830094, 
+0x8c820018, 0xaf8200b4, 0x9482000a, 0xaf82009c, 
+0x8f420014, 0xaf8200b0, 0x8f8200b0, 0x30420004, 
+0x1440fffd, 0x0, 0x8f8200b0, 0x3c03ef00, 
+0x431024, 0x10400021, 0x0, 0x8f8200b4, 
+0xafa20010, 0x8f820090, 0x8f830094, 0x3c040001, 
+0x24845cd4, 0xafa30014, 0x8f8600b0, 0x8f87009c, 
+0x3c050001, 0xc002b3b, 0x34a5200d, 0x3c040001, 
+0x24845ce0, 0x240203c0, 0xafa20010, 0xafa00014, 
+0x8f860144, 0x3c070001, 0x24e75ce8, 0xc002b3b, 
+0x3405dead, 0x8f82011c, 0x34420002, 0xaf82011c, 
+0x8f820220, 0x34420004, 0xaf820220, 0x8f820140, 
+0x3c030001, 0x431025, 0xaf820140, 0x96e20472, 
+0x96e60452, 0x96e70462, 0xafa20010, 0x96e20482, 
+0x3c040001, 0x24845d14, 0x24051200, 0xc002b3b, 
+0xafa20014, 0x96f00452, 0x32020001, 0x10400002, 
+0xb021, 0x24160001, 0x32020002, 0x54400001, 
+0x36d60002, 0x32020008, 0x54400001, 0x36d60004, 
+0x32020010, 0x54400001, 0x36d60008, 0x32020020, 
+0x54400001, 0x36d60010, 0x32020040, 0x54400001, 
+0x36d60020, 0x32020080, 0x54400001, 0x36d60040, 
+0x96e60482, 0x30c20200, 0x54400001, 0x36d64000, 
+0x96e30472, 0x30620200, 0x10400003, 0x30620100, 
+0x10000003, 0x36d62000, 0x54400001, 0x36d61000, 
+0x96f00462, 0x32c24000, 0x14400004, 0x3207009b, 
+0x30c2009b, 0x14e20007, 0x240e0001, 0x32c22000, 
+0x1440000d, 0x32020001, 0x3062009b, 0x10e20009, 
+0x240e0001, 0x3c040001, 0x24845d20, 0x24051300, 
+0x2003821, 0xa3ae003f, 0xafa30010, 0xc002b3b, 
+0xafa00014, 0x32020001, 0x54400001, 0x36d60080, 
+0x32020002, 0x54400001, 0x36d60100, 0x32020008, 
+0x54400001, 0x36d60200, 0x32020010, 0x54400001, 
+0x36d60400, 0x32020080, 0x54400001, 0x36d60800, 
+0x8c020218, 0x30420200, 0x10400002, 0x3c020008, 
+0x2c2b025, 0x8c020218, 0x30420800, 0x10400002, 
+0x3c020080, 0x2c2b025, 0x8c020218, 0x30420400, 
+0x10400002, 0x3c020100, 0x2c2b025, 0x8c020218, 
+0x30420100, 0x10400002, 0x3c020200, 0x2c2b025, 
+0x8c020218, 0x30420080, 0x10400002, 0x3c020400, 
+0x2c2b025, 0x8c020218, 0x30422000, 0x10400002, 
+0x3c020010, 0x2c2b025, 0x8c020218, 0x30424000, 
+0x10400002, 0x3c020020, 0x2c2b025, 0x8c020218, 
+0x30421000, 0x10400002, 0x3c020040, 0x2c2b025, 
+0x8ee20498, 0x8ee3049c, 0xaf420160, 0xaf430164, 
+0x8ee204a0, 0x8ee304a4, 0xaf420168, 0xaf43016c, 
+0x8ee204a8, 0x8ee304ac, 0xaf420170, 0xaf430174, 
+0x8ee20428, 0x8ee3042c, 0xaf420178, 0xaf43017c, 
+0x8ee20448, 0x8ee3044c, 0xaf420180, 0xaf430184, 
+0x8ee20458, 0x8ee3045c, 0xaf420188, 0xaf43018c, 
+0x8ee20468, 0x8ee3046c, 0xaf420190, 0xaf430194, 
+0x8ee20478, 0x8ee3047c, 0xaf420198, 0xaf43019c, 
+0x8ee20488, 0x8ee3048c, 0xaf4201a0, 0xaf4301a4, 
+0x8ee204b0, 0x8ee304b4, 0x24040080, 0xaf4201a8, 
+0xaf4301ac, 0xc002ba8, 0x24050080, 0x8c02025c, 
+0x27440224, 0xaf4201f0, 0x8c020260, 0x24050200, 
+0x24060008, 0xc002bbf, 0xaf4201f8, 0x3c043b9a, 
+0x3484ca00, 0x3821, 0x24020006, 0x24030002, 
+0xaf4201f4, 0x240203e8, 0xaf430204, 0xaf430200, 
+0xaf4401fc, 0xaf420294, 0x24020001, 0xaf430290, 
+0xaf42029c, 0x3c030001, 0x671821, 0x90636cd8, 
+0x3471021, 0x24e70001, 0xa043022c, 0x2ce2000f, 
+0x1440fff8, 0x3471821, 0x24e70001, 0x3c080001, 
+0x350840f8, 0x8f820040, 0x3c040001, 0x24845d2c, 
+0x24051400, 0x21702, 0x24420030, 0xa062022c, 
+0x3471021, 0xa040022c, 0x8c070218, 0x2c03021, 
+0x240205c8, 0xafa20010, 0xc002b3b, 0xafa80014, 
+0x3c040001, 0x24845d38, 0x3c050000, 0x24a55c80, 
+0x24060010, 0x27b10030, 0x2203821, 0x27b30034, 
+0xc0017a3, 0xafb30010, 0x3c030001, 0x8c636cc8, 
+0x1060000a, 0x408021, 0x8fa30030, 0x2405ff00, 
+0x8fa20034, 0x246400ff, 0x852024, 0x831823, 
+0x431023, 0xafa20034, 0xafa40030, 0x3c040001, 
+0x24845d44, 0x3c050000, 0x24a54100, 0x24060108, 
+0x2203821, 0xc0017a3, 0xafb30010, 0x409021, 
+0x32c20003, 0x3c010001, 0xac326e80, 0x10400045, 
+0x2203821, 0x8f820050, 0x3c030010, 0x431024, 
+0x10400016, 0x0, 0x8c020218, 0x30420040, 
+0x1040000f, 0x24020001, 0x8f820050, 0x8c030218, 
+0x240e0001, 0x3c040001, 0x24845d50, 0xa3ae003f, 
+0xafa20010, 0xafa30014, 0x8f870040, 0x24051500, 
+0xc002b3b, 0x2c03021, 0x10000004, 0x0, 
+0x3c010001, 0x370821, 0xa02240f4, 0x3c040001, 
+0x24845d5c, 0x3c050001, 0x24a55b40, 0x3c060001, 
+0x24c65bac, 0xc53023, 0x8f420010, 0x27b30030, 
+0x2603821, 0x27b10034, 0x34420a00, 0xaf420010, 
+0xc0017a3, 0xafb10010, 0x3c040001, 0x24845d70, 
+0x3c050001, 0x24a5b714, 0x3c060001, 0x24c6ba90, 
+0xc53023, 0x2603821, 0xaf420108, 0xc0017a3, 
+0xafb10010, 0x3c040001, 0x24845d8c, 0x3c050001, 
+0x24a5be58, 0x3c060001, 0x24c6c900, 0xc53023, 
+0x2603821, 0x3c010001, 0xac226ef4, 0xc0017a3, 
+0xafb10010, 0x3c040001, 0x24845da4, 0x10000024, 
+0x24051600, 0x3c040001, 0x24845dac, 0x3c050001, 
+0x24a5a10c, 0x3c060001, 0x24c6a238, 0xc53023, 
+0xc0017a3, 0xafb30010, 0x3c040001, 0x24845dbc, 
+0x3c050001, 0x24a5b2b0, 0x3c060001, 0x24c6b70c, 
+0xc53023, 0x2203821, 0xaf420108, 0xc0017a3, 
+0xafb30010, 0x3c040001, 0x24845dd0, 0x3c050001, 
+0x24a5ba98, 0x3c060001, 0x24c6be50, 0xc53023, 
+0x2203821, 0x3c010001, 0xac226ef4, 0xc0017a3, 
+0xafb30010, 0x3c040001, 0x24845de4, 0x24051650, 
+0x2c03021, 0x3821, 0x3c010001, 0xac226ef8, 
+0xafa00010, 0xc002b3b, 0xafa00014, 0x32c20020, 
+0x10400021, 0x27a70030, 0x3c040001, 0x24845df0, 
+0x3c050001, 0x24a5b13c, 0x3c060001, 0x24c6b2a8, 
+0xc53023, 0x24022000, 0xaf42001c, 0x27a20034, 
+0xc0017a3, 0xafa20010, 0x21900, 0x31982, 
+0x3c040800, 0x641825, 0xae430028, 0x24030010, 
+0xaf43003c, 0x96e30450, 0xaf430040, 0x8f430040, 
+0x3c040001, 0x24845e04, 0xafa00014, 0xafa30010, 
+0x8f47001c, 0x24051660, 0x3c010001, 0xac226ef0, 
+0x10000025, 0x32c60020, 0x8ee20448, 0x8ee3044c, 
+0xaf43001c, 0x8f42001c, 0x2442e000, 0x2c422001, 
+0x1440000a, 0x240e0001, 0x3c040001, 0x24845e10, 
+0xa3ae003f, 0xafa00010, 0xafa00014, 0x8f46001c, 
+0x24051700, 0xc002b3b, 0x3821, 0x3c020000, 
+0x24425cbc, 0x21100, 0x21182, 0x3c030800, 
+0x431025, 0xae420028, 0x24020008, 0xaf42003c, 
+0x96e20450, 0xaf420040, 0x8f420040, 0x3c040001, 
+0x24845e1c, 0xafa00014, 0xafa20010, 0x8f47001c, 
+0x24051800, 0x32c60020, 0xc002b3b, 0x0, 
+0x3c050fff, 0x3c030001, 0x8c636ef4, 0x34a5ffff, 
+0x2403021, 0x3c020001, 0x8c426ef8, 0x3c040800, 
+0x651824, 0x31882, 0x641825, 0x451024, 
+0x21082, 0x441025, 0xacc20080, 0x32c20180, 
+0x10400056, 0xacc30020, 0x8f82005c, 0x3c030080, 
+0x431024, 0x1040000d, 0x0, 0x8f820050, 
+0xafa20010, 0x8f82005c, 0x240e0001, 0x3c040001, 
+0x24845e28, 0xa3ae003f, 0xafa20014, 0x8f870040, 
+0x24051900, 0xc002b3b, 0x2c03021, 0x8f820050, 
+0x3c030010, 0x431024, 0x10400016, 0x0, 
+0x8c020218, 0x30420040, 0x1040000f, 0x24020001, 
+0x8f820050, 0x8c030218, 0x240e0001, 0x3c040001, 
+0x24845d50, 0xa3ae003f, 0xafa20010, 0xafa30014, 
+0x8f870040, 0x24052000, 0xc002b3b, 0x2c03021, 
+0x10000004, 0x0, 0x3c010001, 0x370821, 
+0xa02240f4, 0x3c040001, 0x24845e34, 0x3c050001, 
+0x24a55ac0, 0x3c060001, 0x24c65b38, 0xc53023, 
+0x8f420008, 0x27b30030, 0x2603821, 0x27b10034, 
+0x34420e00, 0xaf420008, 0xc0017a3, 0xafb10010, 
+0x3c040001, 0x24845e4c, 0x3c050001, 0x24a5d8b4, 
+0x3c060001, 0x24c6e3c8, 0xc53023, 0x2603821, 
+0xaf42010c, 0xc0017a3, 0xafb10010, 0x3c040001, 
+0x24845e64, 0x3c050001, 0x24a5e9ac, 0x3c060001, 
+0x24c6f0f0, 0xc53023, 0x2603821, 0x3c010001, 
+0xac226f04, 0xc0017a3, 0xafb10010, 0x3c040001, 
+0x24845e7c, 0x10000027, 0x24052100, 0x3c040001, 
+0x24845e84, 0x3c050001, 0x24a59fc8, 0x3c060001, 
+0x24c6a104, 0xc53023, 0x27b10030, 0x2203821, 
+0x27b30034, 0xc0017a3, 0xafb30010, 0x3c040001, 
+0x24845e94, 0x3c050001, 0x24a5cad4, 0x3c060001, 
+0x24c6d8ac, 0xc53023, 0x2203821, 0xaf42010c, 
+0xc0017a3, 0xafb30010, 0x3c040001, 0x24845ea4, 
+0x3c050001, 0x24a5e84c, 0x3c060001, 0x24c6e9a4, 
+0xc53023, 0x2203821, 0x3c010001, 0xac226f04, 
+0xc0017a3, 0xafb30010, 0x3c040001, 0x24845eb8, 
+0x24052150, 0x2c03021, 0x3821, 0x3c010001, 
+0xac226f10, 0xafa00010, 0xc002b3b, 0xafa00014, 
+0x3c110fff, 0x3c030001, 0x8c636f04, 0x3631ffff, 
+0x2409821, 0x3c020001, 0x8c426f10, 0x3c0e0800, 
+0x711824, 0x31882, 0x6e1825, 0x511024, 
+0x21082, 0x4e1025, 0xae630038, 0xae620078, 
+0x8c020218, 0x30420040, 0x14400004, 0x24020001, 
+0x3c010001, 0x370821, 0xa02240f4, 0x3c040001, 
+0x24845ec4, 0x3c050001, 0x24a5e3d0, 0x3c060001, 
+0x24c6e52c, 0xc53023, 0x27be0030, 0x3c03821, 
+0x27b50034, 0xc0017a3, 0xafb50010, 0x3c010001, 
+0xac226efc, 0x511024, 0x21082, 0x3c0e0800, 
+0x4e1025, 0xae620050, 0x32c22000, 0x10400006, 
+0x3c03821, 0x3c020000, 0x24425cbc, 0x2221024, 
+0x1000000f, 0x21082, 0x3c040001, 0x24845ed8, 
+0x3c050001, 0x24a5e534, 0x3c060001, 0x24c6e6e4, 
+0xc53023, 0xc0017a3, 0xafb50010, 0x3c010001, 
+0xac226f14, 0x511024, 0x21082, 0x3c0e0800, 
+0x4e1025, 0xae620048, 0x32c24000, 0x10400005, 
+0x27a70030, 0x3c020000, 0x24425cbc, 0x1000000e, 
+0x21100, 0x3c040001, 0x24845ef0, 0x3c050001, 
+0x24a5e6ec, 0x3c060001, 0x24c6e844, 0xc53023, 
+0x27a20034, 0xc0017a3, 0xafa20010, 0x3c010001, 
+0xac226f08, 0x21100, 0x21182, 0x3c030800, 
+0x431025, 0xae420060, 0x3c040001, 0x24845f08, 
+0x3c050001, 0x24a58230, 0x3c060001, 0x24c68650, 
+0xc53023, 0x27b10030, 0x2203821, 0x27b30034, 
+0xc0017a3, 0xafb30010, 0x3c0e0fff, 0x35ceffff, 
+0x3c040001, 0x24845f14, 0x3c050000, 0x24a56468, 
+0x3c060000, 0x24c66588, 0xc53023, 0x2203821, 
+0x240f021, 0x3c010001, 0xac226edc, 0x4e1024, 
+0x21082, 0x3c150800, 0x551025, 0xafae0044, 
+0xafc200b8, 0xc0017a3, 0xafb30010, 0x3c040001, 
+0x24845f20, 0x3c050000, 0x24a56590, 0x3c060000, 
+0x24c66808, 0x8fae0044, 0xc53023, 0x2203821, 
+0x3c010001, 0xac226ed0, 0x4e1024, 0x21082, 
+0x551025, 0xafc200e8, 0xc0017a3, 0xafb30010, 
+0x3c040001, 0x24845f38, 0x3c050000, 0x24a56810, 
+0x3c060000, 0x24c66940, 0x8fae0044, 0xc53023, 
+0x2203821, 0x3c010001, 0xac226ec8, 0x4e1024, 
+0x21082, 0x551025, 0xafc200c0, 0xc0017a3, 
+0xafb30010, 0x3c040001, 0x24845f50, 0x3c050001, 
+0x24a5fad0, 0x3c060001, 0x24c6fba8, 0x8fae0044, 
+0xc53023, 0x2203821, 0x3c010001, 0xac226ed4, 
+0x4e1024, 0x21082, 0x551025, 0xafc200c8, 
+0xc0017a3, 0xafb30010, 0x3c040001, 0x24845f5c, 
+0x3c050001, 0x24a5c93c, 0x3c060001, 0x24c6ca20, 
+0xc53023, 0x2203821, 0xaf420110, 0xc0017a3, 
+0xafb30010, 0x3c040001, 0x24845f6c, 0x3c050001, 
+0x24a5c910, 0x3c060001, 0x24c6c934, 0xc53023, 
+0x2203821, 0xaf420124, 0xc0017a3, 0xafb30010, 
+0x3c040001, 0x24845f7c, 0x3c050001, 0x24a55a80, 
+0x3c060001, 0x24c65aac, 0xc53023, 0x2203821, 
+0xaf420120, 0xaf420114, 0xc0017a3, 0xafb30010, 
+0x3c040001, 0x24845f88, 0x3c050001, 0x24a5f298, 
+0x3c060001, 0x24c6f6b4, 0xc53023, 0x2203821, 
+0xaf420118, 0xc0017a3, 0xafb30010, 0x8fae0044, 
+0x3c010001, 0xac226f18, 0x4e1024, 0x21082, 
+0x551025, 0xc003fc3, 0xafc200d0, 0xc003c40, 
+0x0, 0xc0027a8, 0x0, 0xac000228, 
+0xac00022c, 0x96e20450, 0x2442ffff, 0xaf420038, 
+0x96e20460, 0xaf420080, 0x32c24000, 0x14400003, 
+0x0, 0x96e20480, 0xaf420084, 0x96e70490, 
+0x50e00001, 0x24070800, 0x24e2ffff, 0xaf420088, 
+0xaf42007c, 0x24020800, 0x10e2000f, 0x32c24000, 
+0x10400003, 0x24020400, 0x10e2000b, 0x0, 
+0x240e0001, 0x3c040001, 0x24845f98, 0xa3ae003f, 
+0x96e60490, 0x24052170, 0x2c03821, 0xafa00010, 
+0xc002b3b, 0xafa00014, 0x8f430138, 0x8f440138, 
+0x24020001, 0xa34205c2, 0xaf430094, 0xaf440098, 
+0xafa00010, 0xafa00014, 0x8f460080, 0x8f470084, 
+0x3c040001, 0x24845fa4, 0xc002b3b, 0x24052200, 
+0xc0024a4, 0x3c110800, 0x3c1433d8, 0x3694cb58, 
+0x3c020800, 0x34420080, 0x3c040001, 0x24845fb0, 
+0x3c050000, 0x24a55d00, 0x3c060000, 0x24c65d1c, 
+0xc53023, 0x27a70030, 0xaf820060, 0x2402ffff, 
+0xaf820064, 0x27a20034, 0xc0017a3, 0xafa20010, 
+0x3c010001, 0xac226eb8, 0x21100, 0x21182, 
+0x511025, 0xc0018fc, 0xae420000, 0x8f820240, 
+0x3c030001, 0x431025, 0xaf820240, 0x3c020000, 
+0x24424034, 0xaf820244, 0xaf800240, 0x8f820060, 
+0x511024, 0x14400005, 0x3c030800, 0x8f820060, 
+0x431024, 0x1040fffd, 0x0, 0xc003c4d, 
+0x8821, 0x3c020100, 0xafa20020, 0x8f530018, 
+0x240200ff, 0x56620001, 0x26710001, 0x8c020228, 
+0x1622000e, 0x1330c0, 0x8f42033c, 0x24420001, 
+0xaf42033c, 0x8f42033c, 0x8c020228, 0x3c040001, 
+0x24845c24, 0x3c050009, 0xafa00014, 0xafa20010, 
+0x8fa60020, 0x1000003f, 0x34a50100, 0xd71021, 
+0x8fa30020, 0x8fa40024, 0xac4304c0, 0xac4404c4, 
+0xc01821, 0x8f440178, 0x8f45017c, 0x1021, 
+0x24070004, 0xafa70010, 0xafb10014, 0x8f48000c, 
+0x24c604c0, 0x2e63021, 0xafa80018, 0x8f48010c, 
+0x24070008, 0xa32821, 0xa3482b, 0x822021, 
+0x100f809, 0x892021, 0x1440000b, 0x24070008, 
+0x8f820120, 0xafa20010, 0x8f820124, 0x3c040001, 
+0x24845c2c, 0x3c050009, 0xafa20014, 0x8fa60020, 
+0x1000001c, 0x34a50200, 0x8f440160, 0x8f450164, 
+0x8f43000c, 0xaf510018, 0x8f860120, 0x24020010, 
+0xafa20010, 0xafb10014, 0xafa30018, 0x8f42010c, 
+0x40f809, 0x24c6001c, 0x14400010, 0x0, 
+0x8f420340, 0x24420001, 0xaf420340, 0x8f420340, 
+0x8f820120, 0xafa20010, 0x8f820124, 0x3c040001, 
+0x24845c34, 0x3c050009, 0xafa20014, 0x8fa60020, 
+0x34a50300, 0xc002b3b, 0x2603821, 0x8f4202e4, 
+0x24420001, 0xaf4202e4, 0x8f4202e4, 0x93a2003f, 
+0x10400069, 0x3c020700, 0x34423000, 0xafa20028, 
+0x8f530018, 0x240200ff, 0x12620002, 0x8821, 
+0x26710001, 0x8c020228, 0x1622000e, 0x1330c0, 
+0x8f42033c, 0x24420001, 0xaf42033c, 0x8f42033c, 
+0x8c020228, 0x3c040001, 0x24845c24, 0x3c050009, 
+0xafa00014, 0xafa20010, 0x8fa60028, 0x1000003f, 
+0x34a50100, 0xd71021, 0x8fa30028, 0x8fa4002c, 
+0xac4304c0, 0xac4404c4, 0xc01821, 0x8f440178, 
+0x8f45017c, 0x1021, 0x24070004, 0xafa70010, 
+0xafb10014, 0x8f48000c, 0x24c604c0, 0x2e63021, 
+0xafa80018, 0x8f48010c, 0x24070008, 0xa32821, 
+0xa3482b, 0x822021, 0x100f809, 0x892021, 
+0x1440000b, 0x24070008, 0x8f820120, 0xafa20010, 
+0x8f820124, 0x3c040001, 0x24845c2c, 0x3c050009, 
+0xafa20014, 0x8fa60028, 0x1000001c, 0x34a50200, 
+0x8f440160, 0x8f450164, 0x8f43000c, 0xaf510018, 
+0x8f860120, 0x24020010, 0xafa20010, 0xafb10014, 
+0xafa30018, 0x8f42010c, 0x40f809, 0x24c6001c, 
+0x14400010, 0x0, 0x8f420340, 0x24420001, 
+0xaf420340, 0x8f420340, 0x8f820120, 0xafa20010, 
+0x8f820124, 0x3c040001, 0x24845c34, 0x3c050009, 
+0xafa20014, 0x8fa60028, 0x34a50300, 0xc002b3b, 
+0x2603821, 0x8f4202f0, 0x24420001, 0xaf4202f0, 
+0x8f4202f0, 0x3c040001, 0x24845fc0, 0xafa00010, 
+0xafa00014, 0x8fa60028, 0x24052300, 0xc002b3b, 
+0x3821, 0x10000004, 0x0, 0x8c020264, 
+0x10400005, 0x0, 0x8f8200a0, 0x30420004, 
+0x1440fffa, 0x0, 0x8f820044, 0x34420004, 
+0xaf820044, 0x8f420308, 0x24420001, 0xaf420308, 
+0x8f420308, 0x8f8200d8, 0x8f8300d4, 0x431023, 
+0x2442ff80, 0xaf420090, 0x8f420090, 0x2842ff81, 
+0x10400006, 0x24020001, 0x8f420090, 0x8f430144, 
+0x431021, 0xaf420090, 0x24020001, 0xaf42008c, 
+0x32c20008, 0x10400006, 0x0, 0x8f820214, 
+0x3c038100, 0x3042ffff, 0x431025, 0xaf820214, 
+0x3c030001, 0x8c636d94, 0x30620002, 0x10400009, 
+0x30620001, 0x3c040001, 0x24845fcc, 0x3c050000, 
+0x24a56d50, 0x3c060000, 0x24c671c8, 0x10000012, 
+0xc53023, 0x10400009, 0x0, 0x3c040001, 
+0x24845fdc, 0x3c050000, 0x24a571d0, 0x3c060000, 
+0x24c67678, 0x10000008, 0xc53023, 0x3c040001, 
+0x24845fec, 0x3c050000, 0x24a56948, 0x3c060000, 
+0x24c66d48, 0xc53023, 0x27a70030, 0x27a20034, 
+0xc0017a3, 0xafa20010, 0x3c010001, 0xac226ecc, 
+0x3c020001, 0x8c426ecc, 0x3c030800, 0x21100, 
+0x21182, 0x431025, 0xae420040, 0x8f8200a0, 
+0xafa20010, 0x8f8200b0, 0xafa20014, 0x8f86005c, 
+0x8f87011c, 0x3c040001, 0x24845ffc, 0x3c010001, 
+0xac366ea4, 0x3c010001, 0xac206e94, 0x3c010001, 
+0xac3c6e8c, 0x3c010001, 0xac3b6ebc, 0x3c010001, 
+0xac376ec0, 0x3c010001, 0xac3a6ea0, 0xc002b3b, 
+0x24052400, 0x8f820200, 0xafa20010, 0x8f820220, 
+0xafa20014, 0x8f860044, 0x8f870050, 0x3c040001, 
+0x24846008, 0xc002b3b, 0x24052500, 0x8f830060, 
+0x74100b, 0x242000a, 0x200f821, 0x0, 
+0xd, 0x8fbf0060, 0x8fbe005c, 0x8fb50058, 
+0x8fb30054, 0x8fb20050, 0x8fb1004c, 0x8fb00048, 
+0x3e00008, 0x27bd0068, 0x27bdffe0, 0x3c040001, 
+0x24846014, 0x24052600, 0x3021, 0x3821, 
+0xafbf0018, 0xafa00010, 0xc002b3b, 0xafa00014, 
+0x8fbf0018, 0x3e00008, 0x27bd0020, 0x3e00008, 
+0x0, 0x3e00008, 0x0, 0x0, 
+0x0, 0x0, 0x0, 0x0, 
+0x3e00008, 0x0, 0x3e00008, 0x0, 
+0x27bdfde0, 0x27a50018, 0x3c04dead, 0x3484beef, 
+0xafbf0218, 0x8f820150, 0x3c03001f, 0x3463ffff, 
+0xafa40018, 0xa22823, 0xa32824, 0x8ca20000, 
+0x1044000a, 0x0, 0xafa50010, 0x8ca20000, 
+0xafa20014, 0x8f860150, 0x8f870250, 0x3c040001, 
+0x2484601c, 0xc002b3b, 0x24052700, 0x8fbf0218, 
+0x3e00008, 0x27bd0220, 0x27bdffe0, 0x3c06abba, 
+0x34c6babe, 0xafb00018, 0x3c100004, 0x3c07007f, 
+0x34e7ffff, 0xafbf001c, 0x102840, 0x8e040000, 
+0x8ca30000, 0xaca00000, 0xae060000, 0x8ca20000, 
+0xaca30000, 0x10460005, 0xae040000, 0xa08021, 
+0xf0102b, 0x1040fff5, 0x102840, 0x3c040001, 
+0x24846028, 0x24052800, 0x2003021, 0x3821, 
+0xafa00010, 0xc002b3b, 0xafa00014, 0x2001021, 
+0x8fbf001c, 0x8fb00018, 0x3e00008, 0x27bd0020, 
+0x8c020224, 0x3047003f, 0x10e00010, 0x803021, 
+0x2821, 0x24030020, 0xe31024, 0x10400002, 
+0x63042, 0xa62821, 0x31842, 0x1460fffb, 
+0xe31024, 0x2402f000, 0xa22824, 0x3402ffff, 
+0x45102b, 0x14400003, 0x3c020001, 0x10000008, 
+0x3c020001, 0x3442ffff, 0x851823, 0x43102b, 
+0x14400003, 0xa01021, 0x3c02fffe, 0x821021, 
+0x3e00008, 0x0, 0x27bdffd0, 0xafb50028, 
+0x8fb50040, 0xafb20020, 0xa09021, 0xafb1001c, 
+0x24c60003, 0xafbf002c, 0xafb30024, 0xafb00018, 
+0x8ea20000, 0x2403fffc, 0xc38024, 0x50102b, 
+0x1440001b, 0xe08821, 0x8e330000, 0xafb00010, 
+0x8ea20000, 0xafa20014, 0x8e270000, 0x24053000, 
+0xc002b3b, 0x2403021, 0x8e230000, 0x702021, 
+0x64102b, 0x10400007, 0x2402821, 0x8ca20000, 
+0xac620000, 0x24630004, 0x64102b, 0x1440fffb, 
+0x24a50004, 0x8ea20000, 0x501023, 0xaea20000, 
+0x8e220000, 0x501021, 0x1000000b, 0xae220000, 
+0x2402002d, 0xa0820000, 0xafb00010, 0x8ea20000, 
+0x2409821, 0xafa20014, 0x8e270000, 0x24053100, 
+0xc002b3b, 0x2603021, 0x2601021, 0x8fbf002c, 
+0x8fb50028, 0x8fb30024, 0x8fb20020, 0x8fb1001c, 
+0x8fb00018, 0x3e00008, 0x27bd0030, 0x27bdffe8, 
+0x3c1cc000, 0x3c05fffe, 0x3c030001, 0x8c636e84, 
+0x3c040001, 0x8c846e90, 0x34a5bf08, 0x24021ffc, 
+0x3c010001, 0xac226cd0, 0x3c0200c0, 0x3c010001, 
+0xac226cd4, 0x3c020020, 0xafbf0010, 0x3c0100c0, 
+0xac201ffc, 0x431023, 0x441023, 0x245bb000, 
+0x365b821, 0x3c1d0001, 0x8fbd6ccc, 0x3a0f021, 
+0x3c0400c0, 0x34840200, 0x3c1a00c0, 0x3c0300c0, 
+0x346307c8, 0x24021dfc, 0x3c010001, 0xac226cd0, 
+0x24021834, 0x3c010001, 0xac246cd4, 0x3c010001, 
+0xac226cd0, 0x3c010001, 0xac236cd4, 0xc00180d, 
+0x375a0200, 0x8fbf0010, 0x3e00008, 0x27bd0018, 
+0x27bdffc8, 0x3c040001, 0x24846034, 0x24053200, 
+0x3c020001, 0x8c426cd0, 0x3c030001, 0x8c636cd4, 
+0x3021, 0x3603821, 0xafbf0030, 0xafb3002c, 
+0xafb20028, 0xafb10024, 0xafb00020, 0xafa2001c, 
+0xafa30018, 0xafb70010, 0xc002b3b, 0xafba0014, 
+0xc001916, 0x0, 0x8f820240, 0x34420004, 
+0xaf820240, 0x24020001, 0xaf420000, 0x3c020001, 
+0x571021, 0x904240f4, 0x10400092, 0x2403fffc, 
+0x3c100001, 0x2610ac73, 0x3c120001, 0x2652a84c, 
+0x2121023, 0x438024, 0x8fa3001c, 0x3c040001, 
+0x24846040, 0x70102b, 0x1440001a, 0x27b30018, 
+0x8fb10018, 0x24053000, 0x2403021, 0xafb00010, 
+0xafa30014, 0xc002b3b, 0x2203821, 0x8fa30018, 
+0x702021, 0x64102b, 0x10400007, 0x2403021, 
+0x8cc20000, 0xac620000, 0x24630004, 0x64102b, 
+0x1440fffb, 0x24c60004, 0x8fa2001c, 0x501023, 
+0xafa2001c, 0x8e620000, 0x501021, 0x1000000a, 
+0xae620000, 0x2408821, 0x24053100, 0xafb00010, 
+0xafa30014, 0x8fa70018, 0x2203021, 0x2402002d, 
+0xc002b3b, 0xa0820000, 0x24070020, 0x8fa3001c, 
+0x3c040001, 0x2484605c, 0x24120020, 0x3c010001, 
+0xac316eb0, 0x2c620020, 0x1440001d, 0x27b10018, 
+0x8fb00018, 0x24053000, 0x3c060001, 0x24c66f50, 
+0xafa70010, 0xafa30014, 0xc002b3b, 0x2003821, 
+0x8fa30018, 0x3c040001, 0x24846f50, 0x24650020, 
+0x65102b, 0x10400007, 0x0, 0x8c820000, 
+0xac620000, 0x24630004, 0x65102b, 0x1440fffb, 
+0x24840004, 0x8fa2001c, 0x521023, 0xafa2001c, 
+0x8e220000, 0x521021, 0x1000000b, 0xae220000, 
+0x3c100001, 0x26106f50, 0x24053100, 0xafa70010, 
+0xafa30014, 0x8fa70018, 0x2003021, 0x2402002d, 
+0xc002b3b, 0xa0820000, 0x24070020, 0x3c040001, 
+0x24846070, 0x8fa3001c, 0x24120020, 0x3c010001, 
+0xac306ee4, 0x2c620020, 0x1440001d, 0x27b10018, 
+0x8fb00018, 0x24053000, 0x3c060001, 0x24c66f70, 
+0xafa70010, 0xafa30014, 0xc002b3b, 0x2003821, 
+0x8fa30018, 0x3c040001, 0x24846f70, 0x24650020, 
+0x65102b, 0x10400007, 0x0, 0x8c820000, 
+0xac620000, 0x24630004, 0x65102b, 0x1440fffb, 
+0x24840004, 0x8fa2001c, 0x521023, 0xafa2001c, 
+0x8e220000, 0x521021, 0x1000000b, 0xae220000, 
+0x3c100001, 0x26106f70, 0x24053100, 0xafa70010, 
+0xafa30014, 0x8fa70018, 0x2003021, 0x2402002d, 
+0xc002b3b, 0xa0820000, 0x3c010001, 0x10000031, 
+0xac306ee0, 0x3c100001, 0x2610821f, 0x3c120001, 
+0x2652809c, 0x2121023, 0x438024, 0x8fa3001c, 
+0x3c040001, 0x24846084, 0x70102b, 0x1440001a, 
+0x27b30018, 0x8fb10018, 0x24053000, 0x2403021, 
+0xafb00010, 0xafa30014, 0xc002b3b, 0x2203821, 
+0x8fa30018, 0x702021, 0x64102b, 0x10400007, 
+0x2403021, 0x8cc20000, 0xac620000, 0x24630004, 
+0x64102b, 0x1440fffb, 0x24c60004, 0x8fa2001c, 
+0x501023, 0xafa2001c, 0x8e620000, 0x501021, 
+0x1000000a, 0xae620000, 0x2408821, 0x24053100, 
+0xafb00010, 0xafa30014, 0x8fa70018, 0x2203021, 
+0x2402002d, 0xc002b3b, 0xa0820000, 0x3c010001, 
+0xac316eb0, 0x3c030001, 0x8c636eb0, 0x24020400, 
+0x60f809, 0xaf820070, 0x8fbf0030, 0x8fb3002c, 
+0x8fb20028, 0x8fb10024, 0x8fb00020, 0x3e00008, 
+0x27bd0038, 0x0, 0x0, 0x8f820040, 
+0x3c03f000, 0x431024, 0x3c036000, 0x14430006, 
+0x0, 0x8f820050, 0x2403ff80, 0x431024, 
+0x34420055, 0xaf820050, 0x8f820054, 0x244203e8, 
+0xaf820058, 0x240201f4, 0xaf4200e0, 0x24020004, 
+0xaf4200e8, 0x24020002, 0xaf4001b0, 0xaf4000e4, 
+0xaf4200dc, 0xaf4000d8, 0xaf4000d4, 0x3e00008, 
+0xaf4000d0, 0x8f820054, 0x24420005, 0x3e00008, 
+0xaf820078, 0x27bdffe8, 0xafbf0010, 0x8f820054, 
+0x244203e8, 0xaf820058, 0x3c020800, 0x2c21024, 
+0x10400004, 0x3c02f7ff, 0x3442ffff, 0x2c2b024, 
+0x36940040, 0x3c020001, 0x8c426da8, 0x10400017, 
+0x3c020200, 0x3c030001, 0x8c636f1c, 0x10600016, 
+0x282a025, 0x3c020001, 0x8c426e44, 0x14400012, 
+0x3c020200, 0x3c020001, 0x8c426d94, 0x30420003, 
+0x1440000d, 0x3c020200, 0x8f830224, 0x3c020002, 
+0x8c428fec, 0x10620008, 0x3c020200, 0xc003daf, 
+0x0, 0x10000004, 0x3c020200, 0xc004196, 
+0x0, 0x3c020200, 0x2c21024, 0x10400003, 
+0x0, 0xc001f4b, 0x0, 0x8f4200d8, 
+0x8f4300dc, 0x24420001, 0xaf4200d8, 0x43102b, 
+0x14400003, 0x0, 0xaf4000d8, 0x36940080, 
+0x8c030238, 0x1060000c, 0x0, 0x8f4201b0, 
+0x244203e8, 0xaf4201b0, 0x43102b, 0x14400006, 
+0x0, 0x934205c5, 0x14400003, 0x0, 
+0xc001da0, 0x0, 0x8fbf0010, 0x3e00008, 
+0x27bd0018, 0x3e00008, 0x0, 0x27bdffd8, 
+0xafbf0020, 0x8f43002c, 0x8f420038, 0x10620059, 
+0x0, 0x3c020001, 0x571021, 0x904240f0, 
+0x10400026, 0x24070008, 0x8f440170, 0x8f450174, 
+0x8f48000c, 0x8f860120, 0x24020020, 0xafa20010, 
+0xafa30014, 0xafa80018, 0x8f42010c, 0x40f809, 
+0x24c6001c, 0x14400011, 0x24020001, 0x3c010001, 
+0x370821, 0xa02240f0, 0x8f820124, 0xafa20010, 
+0x8f820128, 0x3c040001, 0x24846128, 0xafa20014, 
+0x8f46002c, 0x8f870120, 0x3c050009, 0xc002b3b, 
+0x34a50900, 0x1000005c, 0x0, 0x8f420300, 
+0x24420001, 0xaf420300, 0x8f420300, 0x8f42002c, 
+0xa34005c1, 0x10000027, 0xaf420038, 0x8f440170, 
+0x8f450174, 0x8f43002c, 0x8f48000c, 0x8f860120, 
+0x24020080, 0xafa20010, 0xafa30014, 0xafa80018, 
+0x8f42010c, 0x40f809, 0x24c6001c, 0x14400011, 
+0x24020001, 0x3c010001, 0x370821, 0xa02240f1, 
+0x8f820124, 0xafa20010, 0x8f820128, 0x3c040001, 
+0x24846134, 0xafa20014, 0x8f46002c, 0x8f870120, 
+0x3c050009, 0xc002b3b, 0x34a51100, 0x10000036, 
+0x0, 0x8f420300, 0x8f43002c, 0x24420001, 
+0xaf420300, 0x8f420300, 0x24020001, 0xa34205c1, 
+0xaf430038, 0x3c010001, 0x370821, 0xa02040f1, 
+0x3c010001, 0x370821, 0xa02040f0, 0x10000026, 
+0xaf400034, 0x934205c1, 0x1040001d, 0x0, 
+0xa34005c1, 0x8f820040, 0x30420001, 0x14400008, 
+0x2021, 0x8c030104, 0x24020001, 0x50620005, 
+0x24040001, 0x8c020264, 0x10400003, 0x801021, 
+0x24040001, 0x801021, 0x10400006, 0x0, 
+0x8f42030c, 0x24420001, 0xaf42030c, 0x10000008, 
+0x8f42030c, 0x8f820044, 0x34420004, 0xaf820044, 
+0x8f420308, 0x24420001, 0xaf420308, 0x8f420308, 
+0x3c010001, 0x370821, 0xa02040f0, 0x3c010001, 
+0x370821, 0xa02040f1, 0x8f420000, 0x10400007, 
+0x0, 0xaf80004c, 0x8f82004c, 0x1040fffd, 
+0x0, 0x10000005, 0x0, 0xaf800048, 
+0x8f820048, 0x1040fffd, 0x0, 0x8f820060, 
+0x3c03ff7f, 0x3463ffff, 0x431024, 0xaf820060, 
+0x8f420000, 0x10400003, 0x0, 0x10000002, 
+0xaf80004c, 0xaf800048, 0x8fbf0020, 0x3e00008, 
+0x27bd0028, 0x3e00008, 0x0, 0x27bdffd8, 
+0xafbf0020, 0x8f430044, 0x8f42007c, 0x10620029, 
+0x24070008, 0x8f440168, 0x8f45016c, 0x8f48000c, 
+0x8f860120, 0x24020040, 0xafa20010, 0xafa30014, 
+0xafa80018, 0x8f42010c, 0x40f809, 0x24c6001c, 
+0x14400011, 0x24020001, 0x3c010001, 0x370821, 
+0xa02240f2, 0x8f820124, 0xafa20010, 0x8f820128, 
+0x3c040001, 0x2484613c, 0xafa20014, 0x8f460044, 
+0x8f870120, 0x3c050009, 0xc002b3b, 0x34a51300, 
+0x1000000f, 0x0, 0x8f420304, 0x24420001, 
+0xaf420304, 0x8f420304, 0x8f420044, 0xaf42007c, 
+0x3c010001, 0x370821, 0xa02040f2, 0x10000004, 
+0xaf400078, 0x3c010001, 0x370821, 0xa02040f2, 
+0x8f420000, 0x10400007, 0x0, 0xaf80004c, 
+0x8f82004c, 0x1040fffd, 0x0, 0x10000005, 
+0x0, 0xaf800048, 0x8f820048, 0x1040fffd, 
+0x0, 0x8f820060, 0x3c03feff, 0x3463ffff, 
+0x431024, 0xaf820060, 0x8f420000, 0x10400003, 
+0x0, 0x10000002, 0xaf80004c, 0xaf800048, 
+0x8fbf0020, 0x3e00008, 0x27bd0028, 0x3e00008, 
+0x0, 0x3c020001, 0x8c426da8, 0x27bdffa8, 
+0xafbf0050, 0xafbe004c, 0xafb50048, 0xafb30044, 
+0xafb20040, 0xafb1003c, 0xafb00038, 0x104000d5, 
+0x8f900044, 0x8f4200d0, 0x24430001, 0x2842000b, 
+0x144000e4, 0xaf4300d0, 0x8f420004, 0x30420002, 
+0x1440009c, 0xaf4000d0, 0x8f420004, 0x3c030001, 
+0x8c636d98, 0x34420002, 0xaf420004, 0x24020001, 
+0x14620003, 0x3c020600, 0x10000002, 0x34423000, 
+0x34421000, 0xafa20020, 0x8f4a0018, 0xafaa0034, 
+0x27aa0020, 0xafaa002c, 0x8faa0034, 0x240200ff, 
+0x11420002, 0x1821, 0x25430001, 0x8c020228, 
+0x609821, 0x1662000e, 0x3c050009, 0x8f42033c, 
+0x24420001, 0xaf42033c, 0x8f42033c, 0x8c020228, 
+0x8fa70034, 0x3c040001, 0x2484610c, 0xafa00014, 
+0xafa20010, 0x8fa60020, 0x10000070, 0x34a50500, 
+0x8faa0034, 0xa38c0, 0xf71021, 0x8fa30020, 
+0x8fa40024, 0xac4304c0, 0xac4404c4, 0x8f830054, 
+0x8f820054, 0x247103e8, 0x2221023, 0x2c4203e9, 
+0x1040001b, 0xa821, 0xe09021, 0x265e04c0, 
+0x8f440178, 0x8f45017c, 0x2401821, 0x240a0004, 
+0xafaa0010, 0xafb30014, 0x8f48000c, 0x1021, 
+0x2fe3021, 0xafa80018, 0x8f48010c, 0x24070008, 
+0xa32821, 0xa3482b, 0x822021, 0x100f809, 
+0x892021, 0x54400006, 0x24150001, 0x8f820054, 
+0x2221023, 0x2c4203e9, 0x1440ffe9, 0x0, 
+0x32a200ff, 0x54400018, 0xaf530018, 0x8f420378, 
+0x24420001, 0xaf420378, 0x8f420378, 0x8f820120, 
+0x8faa002c, 0x8fa70034, 0xafa20010, 0x8f820124, 
+0x3c040001, 0x24846118, 0xafa20014, 0x8d460000, 
+0x3c050009, 0x10000035, 0x34a50600, 0x8f420308, 
+0x24150001, 0x24420001, 0xaf420308, 0x8f420308, 
+0x1000001e, 0x32a200ff, 0x8f830054, 0x8f820054, 
+0x247103e8, 0x2221023, 0x2c4203e9, 0x10400016, 
+0xa821, 0x3c1e0020, 0x24120010, 0x8f42000c, 
+0x8f440160, 0x8f450164, 0x8f860120, 0xafb20010, 
+0xafb30014, 0x5e1025, 0xafa20018, 0x8f42010c, 
+0x24070008, 0x40f809, 0x24c6001c, 0x1440ffe3, 
+0x0, 0x8f820054, 0x2221023, 0x2c4203e9, 
+0x1440ffee, 0x0, 0x32a200ff, 0x14400011, 
+0x3c050009, 0x8f420378, 0x24420001, 0xaf420378, 
+0x8f420378, 0x8f820120, 0x8faa002c, 0x8fa70034, 
+0xafa20010, 0x8f820124, 0x3c040001, 0x24846120, 
+0xafa20014, 0x8d460000, 0x34a50700, 0xc002b3b, 
+0x0, 0x8f4202ec, 0x24420001, 0xaf4202ec, 
+0x8f4202ec, 0x8f420004, 0x30420001, 0x50400029, 
+0x36100040, 0x3c020400, 0x2c21024, 0x10400013, 
+0x2404ffdf, 0x8f420250, 0x8f430254, 0x8f4401b4, 
+0x14640006, 0x36100040, 0x8f420270, 0x8f430274, 
+0x8f4401b8, 0x10640007, 0x2402ffdf, 0x8f420250, 
+0x8f430254, 0x8f440270, 0x8f450274, 0x10000012, 
+0x3a100020, 0x1000002b, 0x2028024, 0x8f420250, 
+0x8f430254, 0x8f4501b4, 0x14650006, 0x2048024, 
+0x8f420270, 0x8f430274, 0x8f4401b8, 0x50640021, 
+0x36100040, 0x8f420250, 0x8f430254, 0x8f440270, 
+0x8f450274, 0x3a100040, 0xaf4301b4, 0x10000019, 
+0xaf4501b8, 0x8f4200d4, 0x24430001, 0x10000011, 
+0x28420033, 0x8f420004, 0x30420001, 0x10400009, 
+0x3c020400, 0x2c21024, 0x10400004, 0x2402ffdf, 
+0x2028024, 0x1000000b, 0x36100040, 0x10000009, 
+0x36100060, 0x8f4200d4, 0x36100040, 0x24430001, 
+0x284201f5, 0x14400003, 0xaf4300d4, 0xaf4000d4, 
+0x3a100020, 0xaf900044, 0x2402ff7f, 0x282a024, 
+0x8fbf0050, 0x8fbe004c, 0x8fb50048, 0x8fb30044, 
+0x8fb20040, 0x8fb1003c, 0x8fb00038, 0x3e00008, 
+0x27bd0058, 0x3e00008, 0x0, 0x3c020001, 
+0x8c426da8, 0x27bdffb0, 0xafbf0048, 0xafbe0044, 
+0xafb50040, 0xafb3003c, 0xafb20038, 0xafb10034, 
+0x104000c7, 0xafb00030, 0x8f4200d0, 0x24430001, 
+0x2842000b, 0x144000da, 0xaf4300d0, 0x8f420004, 
+0x30420002, 0x14400097, 0xaf4000d0, 0x8f420004, 
+0x3c030001, 0x8c636d98, 0x34420002, 0xaf420004, 
+0x24020001, 0x14620003, 0x3c020600, 0x10000002, 
+0x34423000, 0x34421000, 0xafa20020, 0x1821, 
+0x8f5e0018, 0x27aa0020, 0x240200ff, 0x13c20002, 
+0xafaa002c, 0x27c30001, 0x8c020228, 0x609021, 
+0x1642000e, 0x1e38c0, 0x8f42033c, 0x24420001, 
+0xaf42033c, 0x8f42033c, 0x8c020228, 0x3c040001, 
+0x2484610c, 0x3c050009, 0xafa00014, 0xafa20010, 
+0x8fa60020, 0x1000006d, 0x34a50500, 0xf71021, 
+0x8fa30020, 0x8fa40024, 0xac4304c0, 0xac4404c4, 
+0x8f830054, 0x8f820054, 0x247003e8, 0x2021023, 
+0x2c4203e9, 0x1040001b, 0x9821, 0xe08821, 
+0x263504c0, 0x8f440178, 0x8f45017c, 0x2201821, 
+0x240a0004, 0xafaa0010, 0xafb20014, 0x8f48000c, 
+0x1021, 0x2f53021, 0xafa80018, 0x8f48010c, 
+0x24070008, 0xa32821, 0xa3482b, 0x822021, 
+0x100f809, 0x892021, 0x54400006, 0x24130001, 
+0x8f820054, 0x2021023, 0x2c4203e9, 0x1440ffe9, 
+0x0, 0x326200ff, 0x54400017, 0xaf520018, 
+0x8f420378, 0x24420001, 0xaf420378, 0x8f420378, 
+0x8f820120, 0x8faa002c, 0xafa20010, 0x8f820124, 
+0x3c040001, 0x24846118, 0x3c050009, 0xafa20014, 
+0x8d460000, 0x10000035, 0x34a50600, 0x8f420308, 
+0x24130001, 0x24420001, 0xaf420308, 0x8f420308, 
+0x1000001e, 0x326200ff, 0x8f830054, 0x8f820054, 
+0x247003e8, 0x2021023, 0x2c4203e9, 0x10400016, 
+0x9821, 0x3c150020, 0x24110010, 0x8f42000c, 
+0x8f440160, 0x8f450164, 0x8f860120, 0xafb10010, 
+0xafb20014, 0x551025, 0xafa20018, 0x8f42010c, 
+0x24070008, 0x40f809, 0x24c6001c, 0x1440ffe3, 
+0x0, 0x8f820054, 0x2021023, 0x2c4203e9, 
+0x1440ffee, 0x0, 0x326200ff, 0x14400011, 
+0x0, 0x8f420378, 0x24420001, 0xaf420378, 
+0x8f420378, 0x8f820120, 0x8faa002c, 0xafa20010, 
+0x8f820124, 0x3c040001, 0x24846120, 0x3c050009, 
+0xafa20014, 0x8d460000, 0x34a50700, 0xc002b3b, 
+0x3c03821, 0x8f4202ec, 0x24420001, 0xaf4202ec, 
+0x8f4202ec, 0x8f420004, 0x30420001, 0x10400018, 
+0x24040001, 0x8f420250, 0x8f430254, 0x8f4501b4, 
+0x3c010001, 0x14650006, 0xa0246cf1, 0x8f420270, 
+0x8f430274, 0x8f4401b8, 0x10640021, 0x0, 
+0x8f420250, 0x8f430254, 0x3c040001, 0x90846cf0, 
+0x8f460270, 0x8f470274, 0x38840001, 0xaf4301b4, 
+0xaf4701b8, 0x3c010001, 0x10000025, 0xa0246cf0, 
+0x8f4200d4, 0x3c010001, 0xa0206cf0, 0x24430001, 
+0x28420033, 0x1440001e, 0xaf4300d4, 0x3c020001, 
+0x90426cf1, 0xaf4000d4, 0x10000017, 0x38420001, 
+0x8f420004, 0x30420001, 0x10400008, 0x0, 
+0xc00565a, 0x2021, 0x3c010001, 0xa0206cf1, 
+0x3c010001, 0x1000000e, 0xa0206cf0, 0x8f4200d4, 
+0x3c010001, 0xa0206cf0, 0x24430001, 0x284201f5, 
+0x14400007, 0xaf4300d4, 0x3c020001, 0x90426cf1, 
+0xaf4000d4, 0x421026, 0x3c010001, 0xa0226cf1, 
+0x3c030001, 0x8c636d98, 0x24020002, 0x1462000c, 
+0x3c030002, 0x3c030001, 0x90636cf1, 0x24020001, 
+0x5462001f, 0x2021, 0x3c020001, 0x90426cf0, 
+0x1443001b, 0x24040005, 0x10000019, 0x24040006, 
+0x3c020002, 0x8c428ff4, 0x431024, 0x1040000b, 
+0x24020001, 0x3c030001, 0x90636cf1, 0x54620010, 
+0x2021, 0x3c020001, 0x90426cf0, 0x1443000c, 
+0x24040003, 0x1000000a, 0x24040004, 0x3c030001, 
+0x90636cf1, 0x14620006, 0x2021, 0x3c020001, 
+0x90426cf0, 0x24040001, 0x50440001, 0x24040002, 
+0xc00565a, 0x0, 0x2402ff7f, 0x282a024, 
+0x8fbf0048, 0x8fbe0044, 0x8fb50040, 0x8fb3003c, 
+0x8fb20038, 0x8fb10034, 0x8fb00030, 0x3e00008, 
+0x27bd0050, 0x3e00008, 0x0, 0x3c020001, 
+0x8c426da8, 0x27bdffb0, 0xafbf0048, 0xafbe0044, 
+0xafb50040, 0xafb3003c, 0xafb20038, 0xafb10034, 
+0x104000de, 0xafb00030, 0x8f4200d0, 0x3c040001, 
+0x8c846d98, 0x24430001, 0x2842000b, 0xaf4400e8, 
+0x144000fe, 0xaf4300d0, 0x8f420004, 0x30420002, 
+0x14400095, 0xaf4000d0, 0x8f420004, 0x34420002, 
+0xaf420004, 0x24020001, 0x14820003, 0x3c020600, 
+0x10000002, 0x34423000, 0x34421000, 0xafa20020, 
+0x1821, 0x8f5e0018, 0x27aa0020, 0x240200ff, 
+0x13c20002, 0xafaa002c, 0x27c30001, 0x8c020228, 
+0x609021, 0x1642000e, 0x1e38c0, 0x8f42033c, 
+0x24420001, 0xaf42033c, 0x8f42033c, 0x8c020228, 
+0x3c040001, 0x2484610c, 0x3c050009, 0xafa00014, 
+0xafa20010, 0x8fa60020, 0x1000006d, 0x34a50500, 
+0xf71021, 0x8fa30020, 0x8fa40024, 0xac4304c0, 
+0xac4404c4, 0x8f830054, 0x8f820054, 0x247003e8, 
+0x2021023, 0x2c4203e9, 0x1040001b, 0x9821, 
+0xe08821, 0x263504c0, 0x8f440178, 0x8f45017c, 
+0x2201821, 0x240a0004, 0xafaa0010, 0xafb20014, 
+0x8f48000c, 0x1021, 0x2f53021, 0xafa80018, 
+0x8f48010c, 0x24070008, 0xa32821, 0xa3482b, 
+0x822021, 0x100f809, 0x892021, 0x54400006, 
+0x24130001, 0x8f820054, 0x2021023, 0x2c4203e9, 
+0x1440ffe9, 0x0, 0x326200ff, 0x54400017, 
+0xaf520018, 0x8f420378, 0x24420001, 0xaf420378, 
+0x8f420378, 0x8f820120, 0x8faa002c, 0xafa20010, 
+0x8f820124, 0x3c040001, 0x24846118, 0x3c050009, 
+0xafa20014, 0x8d460000, 0x10000035, 0x34a50600, 
+0x8f420308, 0x24130001, 0x24420001, 0xaf420308, 
+0x8f420308, 0x1000001e, 0x326200ff, 0x8f830054, 
+0x8f820054, 0x247003e8, 0x2021023, 0x2c4203e9, 
+0x10400016, 0x9821, 0x3c150020, 0x24110010, 
+0x8f42000c, 0x8f440160, 0x8f450164, 0x8f860120, 
+0xafb10010, 0xafb20014, 0x551025, 0xafa20018, 
+0x8f42010c, 0x24070008, 0x40f809, 0x24c6001c, 
+0x1440ffe3, 0x0, 0x8f820054, 0x2021023, 
+0x2c4203e9, 0x1440ffee, 0x0, 0x326200ff, 
+0x14400011, 0x0, 0x8f420378, 0x24420001, 
+0xaf420378, 0x8f420378, 0x8f820120, 0x8faa002c, 
+0xafa20010, 0x8f820124, 0x3c040001, 0x24846120, 
+0x3c050009, 0xafa20014, 0x8d460000, 0x34a50700, 
+0xc002b3b, 0x3c03821, 0x8f4202ec, 0x24420001, 
+0xaf4202ec, 0x8f4202ec, 0x8f420004, 0x30420001, 
+0x10400033, 0x3c020400, 0x2c21024, 0x10400017, 
+0x0, 0x934205c0, 0x8f440250, 0x8f450254, 
+0x8f4301b4, 0x34420020, 0x14a30006, 0xa34205c0, 
+0x8f420270, 0x8f430274, 0x8f4401b8, 0x10640008, 
+0x0, 0x8f420250, 0x8f430254, 0x934405c0, 
+0x8f460270, 0x8f470274, 0x10000016, 0x38840040, 
+0x934205c0, 0x10000048, 0x304200bf, 0x934205c0, 
+0x8f440250, 0x8f450254, 0x8f4301b4, 0x304200bf, 
+0x14a30006, 0xa34205c0, 0x8f420270, 0x8f430274, 
+0x8f4401b8, 0x1064000b, 0x0, 0x8f420250, 
+0x8f430254, 0x934405c0, 0x8f460270, 0x8f470274, 
+0x38840020, 0xaf4301b4, 0xaf4701b8, 0x10000033, 
+0xa34405c0, 0x934205c0, 0x1000002f, 0x34420020, 
+0x934205c0, 0x8f4300d4, 0x34420020, 0xa34205c0, 
+0x24620001, 0x10000023, 0x28630033, 0x8f4200e4, 
+0x8f4300e0, 0x24420001, 0xaf4200e4, 0x43102a, 
+0x14400006, 0x24030001, 0x8f4200e8, 0x14430002, 
+0xaf4000e4, 0x24030004, 0xaf4300e8, 0x8f420004, 
+0x30420001, 0x1040000d, 0x3c020400, 0x2c21024, 
+0x10400007, 0x0, 0x934205c0, 0x34420040, 
+0xa34205c0, 0x934205c0, 0x1000000f, 0x304200df, 
+0x934205c0, 0x1000000c, 0x34420060, 0x934205c0, 
+0x8f4300d4, 0x34420020, 0xa34205c0, 0x24620001, 
+0x286300fb, 0x14600005, 0xaf4200d4, 0x934205c0, 
+0xaf4000d4, 0x38420040, 0xa34205c0, 0x934205c0, 
+0x8f4300e8, 0x3042007f, 0xa34205c0, 0x24020001, 
+0x14620005, 0x0, 0x934405c0, 0x42102, 
+0x10000003, 0x348400f0, 0x934405c0, 0x3484000f, 
+0xc005640, 0x0, 0x2402ff7f, 0x282a024, 
+0x8fbf0048, 0x8fbe0044, 0x8fb50040, 0x8fb3003c, 
+0x8fb20038, 0x8fb10034, 0x8fb00030, 0x3e00008, 
+0x27bd0050, 0x3e00008, 0x0, 0x27bdffb0, 
+0x274401c0, 0x26e30028, 0x24650400, 0x65102b, 
+0xafbf0048, 0xafbe0044, 0xafb50040, 0xafb3003c, 
+0xafb20038, 0xafb10034, 0x10400007, 0xafb00030, 
+0x8c820000, 0xac620000, 0x24630004, 0x65102b, 
+0x1440fffb, 0x24840004, 0x8c020080, 0xaee20044, 
+0x8c0200c0, 0xaee20040, 0x8c020084, 0xaee20030, 
+0x8c020084, 0xaee2023c, 0x8c020088, 0xaee20240, 
+0x8c02008c, 0xaee20244, 0x8c020090, 0xaee20248, 
+0x8c020094, 0xaee2024c, 0x8c020098, 0xaee20250, 
+0x8c02009c, 0xaee20254, 0x8c0200a0, 0xaee20258, 
+0x8c0200a4, 0xaee2025c, 0x8c0200a8, 0xaee20260, 
+0x8c0200ac, 0xaee20264, 0x8c0200b0, 0xaee20268, 
+0x8c0200b4, 0xaee2026c, 0x8c0200b8, 0xaee20270, 
+0x8c0200bc, 0x24040001, 0xaee20274, 0xaee00034, 
+0x41080, 0x571021, 0x8ee30034, 0x8c42023c, 
+0x24840001, 0x621821, 0x2c82000f, 0xaee30034, 
+0x1440fff8, 0x41080, 0x8c0200cc, 0xaee20048, 
+0x8c0200d0, 0xaee2004c, 0x8c0200e0, 0xaee201f8, 
+0x8c0200e4, 0xaee201fc, 0x8c0200e8, 0xaee20200, 
+0x8c0200ec, 0xaee20204, 0x8c0200f0, 0xaee20208, 
+0x8ee400c0, 0x8ee500c4, 0x8c0200fc, 0x45102b, 
+0x1040000b, 0x0, 0x8ee200c0, 0x8ee300c4, 
+0x24040001, 0x24050000, 0x651821, 0x65302b, 
+0x441021, 0x461021, 0xaee200c0, 0xaee300c4, 
+0x8c0200fc, 0x8ee400c0, 0x8ee500c4, 0x2408ffff, 
+0x24090000, 0x401821, 0x1021, 0x882024, 
+0xa92824, 0x822025, 0xa32825, 0xaee400c0, 
+0xaee500c4, 0x8ee400d0, 0x8ee500d4, 0x8c0200f4, 
+0x45102b, 0x1040000b, 0x0, 0x8ee200d0, 
+0x8ee300d4, 0x24040001, 0x24050000, 0x651821, 
+0x65302b, 0x441021, 0x461021, 0xaee200d0, 
+0xaee300d4, 0x8c0200f4, 0x8ee400d0, 0x8ee500d4, 
+0x401821, 0x1021, 0x882024, 0xa92824, 
+0x822025, 0xa32825, 0xaee400d0, 0xaee500d4, 
+0x8ee400c8, 0x8ee500cc, 0x8c0200f8, 0x45102b, 
+0x1040000b, 0x0, 0x8ee200c8, 0x8ee300cc, 
+0x24040001, 0x24050000, 0x651821, 0x65302b, 
+0x441021, 0x461021, 0xaee200c8, 0xaee300cc, 
+0x8c0200f8, 0x8ee400c8, 0x8ee500cc, 0x401821, 
+0x1021, 0x882024, 0xa92824, 0x822025, 
+0xa32825, 0x24020008, 0xaee400c8, 0xaee500cc, 
+0xafa20010, 0xafa00014, 0x8f42000c, 0x8c040208, 
+0x8c05020c, 0xafa20018, 0x8f42010c, 0x26e60028, 
+0x40f809, 0x24070400, 0x104000f0, 0x3c020400, 
+0xafa20020, 0x934205c6, 0x10400089, 0x1821, 
+0x8f5e0018, 0x27aa0020, 0x240200ff, 0x13c20002, 
+0xafaa002c, 0x27c30001, 0x8c020228, 0x609021, 
+0x1642000e, 0x1e38c0, 0x8f42033c, 0x24420001, 
+0xaf42033c, 0x8f42033c, 0x8c020228, 0x3c040001, 
+0x2484610c, 0x3c050009, 0xafa00014, 0xafa20010, 
+0x8fa60020, 0x1000006b, 0x34a50500, 0xf71021, 
+0x8fa30020, 0x8fa40024, 0xac4304c0, 0xac4404c4, 
+0x8f830054, 0x8f820054, 0x247003e8, 0x2021023, 
+0x2c4203e9, 0x1040001b, 0x9821, 0xe08821, 
+0x263504c0, 0x8f440178, 0x8f45017c, 0x2201821, 
+0x240a0004, 0xafaa0010, 0xafb20014, 0x8f48000c, 
+0x1021, 0x2f53021, 0xafa80018, 0x8f48010c, 
+0x24070008, 0xa32821, 0xa3482b, 0x822021, 
+0x100f809, 0x892021, 0x54400006, 0x24130001, 
+0x8f820054, 0x2021023, 0x2c4203e9, 0x1440ffe9, 
+0x0, 0x326200ff, 0x54400017, 0xaf520018, 
+0x8f420378, 0x24420001, 0xaf420378, 0x8f420378, 
+0x8f820120, 0x8faa002c, 0xafa20010, 0x8f820124, 
+0x3c040001, 0x24846118, 0x3c050009, 0xafa20014, 
+0x8d460000, 0x10000033, 0x34a50600, 0x8f420308, 
+0x24130001, 0x24420001, 0xaf420308, 0x8f420308, 
+0x1000001c, 0x326200ff, 0x8f830054, 0x8f820054, 
+0x247003e8, 0x2021023, 0x2c4203e9, 0x10400014, 
+0x9821, 0x24110010, 0x8f42000c, 0x8f440160, 
+0x8f450164, 0x8f860120, 0xafb10010, 0xafb20014, 
+0xafa20018, 0x8f42010c, 0x24070008, 0x40f809, 
+0x24c6001c, 0x1440ffe5, 0x0, 0x8f820054, 
+0x2021023, 0x2c4203e9, 0x1440ffef, 0x0, 
+0x326200ff, 0x54400012, 0x24020001, 0x8f420378, 
+0x24420001, 0xaf420378, 0x8f420378, 0x8f820120, 
+0x8faa002c, 0xafa20010, 0x8f820124, 0x3c040001, 
+0x24846120, 0x3c050009, 0xafa20014, 0x8d460000, 
+0x34a50700, 0xc002b3b, 0x3c03821, 0x1021, 
+0x1440005b, 0x24020001, 0x10000065, 0x0, 
+0x8f510018, 0x240200ff, 0x12220002, 0x8021, 
+0x26300001, 0x8c020228, 0x1602000e, 0x1130c0, 
+0x8f42033c, 0x24420001, 0xaf42033c, 0x8f42033c, 
+0x8c020228, 0x3c040001, 0x248460f4, 0x3c050009, 
+0xafa00014, 0xafa20010, 0x8fa60020, 0x1000003f, 
+0x34a50100, 0xd71021, 0x8fa30020, 0x8fa40024, 
+0xac4304c0, 0xac4404c4, 0xc01821, 0x8f440178, 
+0x8f45017c, 0x1021, 0x24070004, 0xafa70010, 
+0xafb00014, 0x8f48000c, 0x24c604c0, 0x2e63021, 
+0xafa80018, 0x8f48010c, 0x24070008, 0xa32821, 
+0xa3482b, 0x822021, 0x100f809, 0x892021, 
+0x1440000b, 0x24070008, 0x8f820120, 0xafa20010, 
+0x8f820124, 0x3c040001, 0x248460fc, 0x3c050009, 
+0xafa20014, 0x8fa60020, 0x1000001c, 0x34a50200, 
+0x8f440160, 0x8f450164, 0x8f43000c, 0xaf500018, 
+0x8f860120, 0x24020010, 0xafa20010, 0xafb00014, 
+0xafa30018, 0x8f42010c, 0x40f809, 0x24c6001c, 
+0x54400011, 0x24020001, 0x8f420340, 0x24420001, 
+0xaf420340, 0x8f420340, 0x8f820120, 0xafa20010, 
+0x8f820124, 0x3c040001, 0x24846104, 0x3c050009, 
+0xafa20014, 0x8fa60020, 0x34a50300, 0xc002b3b, 
+0x2203821, 0x1021, 0x1040000d, 0x24020001, 
+0x8f4202e8, 0xa34005c6, 0xaf4001b0, 0x24420001, 
+0xaf4202e8, 0x8f4202e8, 0x8ee20150, 0x24420001, 
+0xaee20150, 0x10000003, 0x8ee20150, 0x24020001, 
+0xa34205c6, 0x8fbf0048, 0x8fbe0044, 0x8fb50040, 
+0x8fb3003c, 0x8fb20038, 0x8fb10034, 0x8fb00030, 
+0x3e00008, 0x27bd0050, 0x27bdffd8, 0xafbf0020, 
+0x8f8200b0, 0x30420004, 0x10400068, 0x0, 
+0x8f430128, 0x8f820104, 0x14620005, 0x0, 
+0x8f430130, 0x8f8200b4, 0x10620006, 0x0, 
+0x8f820104, 0xaf420128, 0x8f8200b4, 0x1000005b, 
+0xaf420130, 0x8f8200b0, 0x3c030080, 0x431024, 
+0x1040000d, 0x0, 0x8f82011c, 0x34420002, 
+0xaf82011c, 0x8f8200b0, 0x2403fffb, 0x431024, 
+0xaf8200b0, 0x8f82011c, 0x2403fffd, 0x431024, 
+0x1000004a, 0xaf82011c, 0x8f430128, 0x8f820104, 
+0x14620005, 0x0, 0x8f430130, 0x8f8200b4, 
+0x10620010, 0x0, 0x8f820104, 0xaf420128, 
+0x8f8200b4, 0x8f430128, 0xaf420130, 0xafa30010, 
+0x8f420130, 0x3c040001, 0x24846144, 0xafa20014, 
+0x8f86011c, 0x8f8700b0, 0x3c050005, 0x10000031, 
+0x34a50900, 0x8f420128, 0xafa20010, 0x8f420130, 
+0x3c040001, 0x24846150, 0xafa20014, 0x8f86011c, 
+0x8f8700b0, 0x3c050005, 0xc002b3b, 0x34a51000, 
+0x8f82011c, 0x34420002, 0xaf82011c, 0x8f830104, 
+0x8f8200b0, 0x34420001, 0xaf8200b0, 0x24020008, 
+0xaf830104, 0xafa20010, 0xafa00014, 0x8f42000c, 
+0x8c040208, 0x8c05020c, 0xafa20018, 0x8f42010c, 
+0x26e60028, 0x40f809, 0x24070400, 0x8f82011c, 
+0x2403fffd, 0x431024, 0xaf82011c, 0x8ee201dc, 
+0x24420001, 0xaee201dc, 0x8ee201dc, 0x8f420128, 
+0xafa20010, 0x8f420130, 0x3c040001, 0x2484615c, 
+0xafa20014, 0x8f86011c, 0x8f8700b0, 0x3c050005, 
+0x34a51100, 0xc002b3b, 0x0, 0x8f8200a0, 
+0x30420004, 0x10400069, 0x0, 0x8f43012c, 
+0x8f820124, 0x14620005, 0x0, 0x8f430134, 
+0x8f8200a4, 0x10620006, 0x0, 0x8f820124, 
+0xaf42012c, 0x8f8200a4, 0x1000005c, 0xaf420134, 
+0x8f8200a0, 0x3c030080, 0x431024, 0x1040000d, 
+0x0, 0x8f82011c, 0x34420002, 0xaf82011c, 
+0x8f8200a0, 0x2403fffb, 0x431024, 0xaf8200a0, 
+0x8f82011c, 0x2403fffd, 0x431024, 0x1000004b, 
+0xaf82011c, 0x8f43012c, 0x8f820124, 0x14620005, 
+0x0, 0x8f430134, 0x8f8200a4, 0x10620010, 
+0x0, 0x8f820124, 0xaf42012c, 0x8f8200a4, 
+0x8f43012c, 0xaf420134, 0xafa30010, 0x8f420134, 
+0x3c040001, 0x24846168, 0xafa20014, 0x8f86011c, 
+0x8f8700a0, 0x3c050005, 0x10000032, 0x34a51200, 
+0x8f42012c, 0xafa20010, 0x8f420134, 0x3c040001, 
+0x24846174, 0xafa20014, 0x8f86011c, 0x8f8700a0, 
+0x3c050005, 0xc002b3b, 0x34a51300, 0x8f82011c, 
+0x34420002, 0xaf82011c, 0x8f830124, 0x8f8200a0, 
+0x34420001, 0xaf8200a0, 0x24020080, 0xaf830124, 
+0xafa20010, 0xafa00014, 0x8f420014, 0x8c040208, 
+0x8c05020c, 0xafa20018, 0x8f420108, 0x3c060001, 
+0x24c66ed8, 0x40f809, 0x24070004, 0x8f82011c, 
+0x2403fffd, 0x431024, 0xaf82011c, 0x8ee201dc, 
+0x24420001, 0xaee201dc, 0x8ee201dc, 0x8f42012c, 
+0xafa20010, 0x8f420134, 0x3c040001, 0x24846180, 
+0xafa20014, 0x8f86011c, 0x8f8700a0, 0x3c050005, 
+0x34a51400, 0xc002b3b, 0x0, 0x8fbf0020, 
+0x3e00008, 0x27bd0028, 0x3c081000, 0x24070001, 
+0x3c060080, 0x3c050100, 0x8f820070, 0x481024, 
+0x1040fffd, 0x0, 0x8f820054, 0x24420005, 
+0xaf820078, 0x8c040234, 0x10800016, 0x1821, 
+0x3c020001, 0x571021, 0x8c4240e8, 0x24420005, 
+0x3c010001, 0x370821, 0xac2240e8, 0x3c020001, 
+0x571021, 0x8c4240e8, 0x44102b, 0x14400009, 
+0x0, 0x3c030080, 0x3c010001, 0x370821, 
+0xac2040e8, 0x3c010001, 0x370821, 0x1000000b, 
+0xa02740f0, 0x3c020001, 0x571021, 0x904240f0, 
+0x54400006, 0x661825, 0x3c020001, 0x571021, 
+0x904240f1, 0x54400001, 0x661825, 0x8c040230, 
+0x10800013, 0x0, 0x3c020001, 0x571021, 
+0x8c4240ec, 0x24420005, 0x3c010001, 0x370821, 
+0xac2240ec, 0x3c020001, 0x571021, 0x8c4240ec, 
+0x44102b, 0x14400006, 0x0, 0x3c010001, 
+0x370821, 0xac2040ec, 0x10000006, 0x651825, 
+0x3c020001, 0x571021, 0x904240f2, 0x54400001, 
+0x651825, 0x1060ffbc, 0x0, 0x8f420000, 
+0x10400007, 0x0, 0xaf80004c, 0x8f82004c, 
+0x1040fffd, 0x0, 0x10000005, 0x0, 
+0xaf800048, 0x8f820048, 0x1040fffd, 0x0, 
+0x8f820060, 0x431025, 0xaf820060, 0x8f420000, 
+0x10400003, 0x0, 0x1000ffa7, 0xaf80004c, 
+0x1000ffa5, 0xaf800048, 0x3e00008, 0x0, 
+0x0, 0x0, 0x0, 0x27bdffe0, 
+0xafbf0018, 0x8f860064, 0x30c20004, 0x10400025, 
+0x24040004, 0x8c020114, 0xaf420020, 0xaf840064, 
+0x8f4202fc, 0x24420001, 0xaf4202fc, 0x8f4202fc, 
+0x8f820064, 0x30420004, 0x14400005, 0x0, 
+0x8c030114, 0x8f420020, 0x1462fff2, 0x0, 
+0x8f420000, 0x10400007, 0x8f43003c, 0xaf80004c, 
+0x8f82004c, 0x1040fffd, 0x0, 0x10000005, 
+0x0, 0xaf800048, 0x8f820048, 0x1040fffd, 
+0x0, 0x8f820060, 0x431025, 0xaf820060, 
+0x8f420000, 0x10400073, 0x0, 0x1000006f, 
+0x0, 0x30c20008, 0x10400020, 0x24040008, 
+0x8c02011c, 0xaf420048, 0xaf840064, 0x8f4202a8, 
+0x24420001, 0xaf4202a8, 0x8f4202a8, 0x8f820064, 
+0x30420008, 0x14400005, 0x0, 0x8c03011c, 
+0x8f420048, 0x1462fff2, 0x0, 0x8f420000, 
+0x10400007, 0x0, 0xaf80004c, 0x8f82004c, 
+0x1040fffd, 0x0, 0x10000005, 0x0, 
+0xaf800048, 0x8f820048, 0x1040fffd, 0x0, 
+0x8f820060, 0x1000ffd9, 0x34420200, 0x30c20020, 
+0x10400023, 0x24040020, 0x8c02012c, 0xaf420068, 
+0xaf840064, 0x8f4202d8, 0x24420001, 0xaf4202d8, 
+0x8f4202d8, 0x8f820064, 0x30420020, 0x14400005, 
+0x32c24000, 0x8c03012c, 0x8f420068, 0x1462fff2, 
+0x32c24000, 0x14400002, 0x3c020001, 0x2c2b025, 
+0x8f420000, 0x10400007, 0x0, 0xaf80004c, 
+0x8f82004c, 0x1040fffd, 0x0, 0x10000005, 
+0x0, 0xaf800048, 0x8f820048, 0x1040fffd, 
+0x0, 0x8f820060, 0x1000ffb4, 0x34420800, 
+0x30c20010, 0x10400029, 0x24040010, 0x8c020124, 
+0xaf420058, 0xaf840064, 0x8f4202d4, 0x24420001, 
+0xaf4202d4, 0x8f4202d4, 0x8f820064, 0x30420010, 
+0x14400005, 0x32c22000, 0x8c030124, 0x8f420058, 
+0x1462fff2, 0x32c22000, 0x50400001, 0x36d68000, 
+0x8f420000, 0x10400007, 0x0, 0xaf80004c, 
+0x8f82004c, 0x1040fffd, 0x0, 0x10000005, 
+0x0, 0xaf800048, 0x8f820048, 0x1040fffd, 
+0x0, 0x8f820060, 0x34420100, 0xaf820060, 
+0x8f420000, 0x10400003, 0x0, 0x1000006c, 
+0xaf80004c, 0x1000006a, 0xaf800048, 0x30c20001, 
+0x10400004, 0x24020001, 0xaf820064, 0x10000064, 
+0x0, 0x30c20002, 0x1440000b, 0x3c050003, 
+0x3c040001, 0x24846244, 0x34a50500, 0x3821, 
+0xafa00010, 0xc002b3b, 0xafa00014, 0x2402ffc0, 
+0x10000057, 0xaf820064, 0x8c05022c, 0x8c02010c, 
+0x10a20048, 0x51080, 0x8c460300, 0x24a20001, 
+0x3045003f, 0x24020003, 0xac05022c, 0x61e02, 
+0x10620005, 0x24020010, 0x1062001d, 0x30c20fff, 
+0x10000039, 0x0, 0x8f4302a8, 0x8f440000, 
+0x30c20fff, 0xaf420048, 0x24630001, 0xaf4302a8, 
+0x10800007, 0x8f4202a8, 0xaf80004c, 0x8f82004c, 
+0x1040fffd, 0x0, 0x10000005, 0x0, 
+0xaf800048, 0x8f820048, 0x1040fffd, 0x0, 
+0x8f820060, 0x34420200, 0xaf820060, 0x8f420000, 
+0x1040001f, 0x0, 0x1000001b, 0x0, 
+0xaf420058, 0x32c22000, 0x50400001, 0x36d68000, 
+0x8f4202d4, 0x8f430000, 0x24420001, 0xaf4202d4, 
+0x10600007, 0x8f4202d4, 0xaf80004c, 0x8f82004c, 
+0x1040fffd, 0x0, 0x10000005, 0x0, 
+0xaf800048, 0x8f820048, 0x1040fffd, 0x0, 
+0x8f820060, 0x34420100, 0xaf820060, 0x8f420000, 
+0x10400003, 0x0, 0x10000006, 0xaf80004c, 
+0x10000004, 0xaf800048, 0xc002196, 0xc02021, 
+0x402821, 0x8c02010c, 0x14a20002, 0x24020002, 
+0xaf820064, 0x8f820064, 0x30420002, 0x14400004, 
+0x0, 0x8c02010c, 0x14a2ffac, 0x0, 
+0x8fbf0018, 0x3e00008, 0x27bd0020, 0x3e00008, 
+0x0, 0x27bdffa0, 0xafb00040, 0x808021, 
+0x101602, 0x2442ffff, 0x304300ff, 0x2c620013, 
+0xafbf0058, 0xafbe0054, 0xafb50050, 0xafb3004c, 
+0xafb20048, 0xafb10044, 0x104001f3, 0xafa50034, 
+0x31080, 0x3c010001, 0x220821, 0x8c226288, 
+0x400008, 0x0, 0x101302, 0x30440fff, 
+0x24020001, 0x10820005, 0x24020002, 0x1082000c, 
+0x2402fffe, 0x10000024, 0x3c050003, 0x8f430004, 
+0x3c020001, 0x8c426f04, 0xaf440200, 0xaf440204, 
+0x3c040001, 0x8c846e80, 0x10000009, 0x34630001, 
+0x8f430004, 0xaf440200, 0xaf440204, 0x3c040001, 
+0x8c846e80, 0x621824, 0x3c020001, 0x2442ca28, 
+0x21100, 0x21182, 0xaf430004, 0x3c030800, 
+0x431025, 0xac820038, 0x8f840054, 0x41442, 
+0x41c82, 0x431021, 0x41cc2, 0x431023, 
+0x41d02, 0x431021, 0x41d42, 0x431023, 
+0x10000009, 0xaf420208, 0x3c040001, 0x24846250, 
+0x34a51000, 0x2003021, 0x3821, 0xafa00010, 
+0xc002b3b, 0xafa00014, 0x8f4202a0, 0x24420001, 
+0xaf4202a0, 0x1000021f, 0x8f4202a0, 0x27b00028, 
+0x2002021, 0x24050210, 0xc002bbf, 0x24060008, 
+0xc002518, 0x2002021, 0x10000216, 0x0, 
+0x8faa0034, 0x27a40028, 0xa1880, 0x25420001, 
+0x3042003f, 0xafa20034, 0x8c650300, 0x8faa0034, 
+0x21080, 0x8c430300, 0x25420001, 0x3042003f, 
+0xafa20034, 0xac02022c, 0xafa50028, 0xc002518, 
+0xafa3002c, 0x10000203, 0x0, 0x27b00028, 
+0x2002021, 0x24050210, 0xc002bbf, 0x24060008, 
+0xc002657, 0x2002021, 0x100001fa, 0x0, 
+0x8faa0034, 0x27a40028, 0xa1880, 0x25420001, 
+0x3042003f, 0xafa20034, 0x8c650300, 0x8faa0034, 
+0x21080, 0x8c430300, 0x25420001, 0x3042003f, 
+0xafa20034, 0xac02022c, 0xafa50028, 0xc002657, 
+0xafa3002c, 0x100001e7, 0x0, 0x101302, 
+0x30430fff, 0x24020001, 0x10620005, 0x24020002, 
+0x1062001e, 0x3c020002, 0x10000033, 0x3c050003, 
+0x3c030002, 0x2c31024, 0x54400037, 0x2c3b025, 
+0x8f820228, 0x3c010001, 0x370821, 0xac2238d8, 
+0x8f82022c, 0x3c010001, 0x370821, 0xac2238dc, 
+0x8f820230, 0x3c010001, 0x370821, 0xac2238e0, 
+0x8f820234, 0x3c010001, 0x370821, 0xac2238e4, 
+0x2402ffff, 0xaf820228, 0xaf82022c, 0xaf820230, 
+0xaf820234, 0x10000020, 0x2c3b025, 0x2c21024, 
+0x10400012, 0x3c02fffd, 0x3c020001, 0x571021, 
+0x8c4238d8, 0xaf820228, 0x3c020001, 0x571021, 
+0x8c4238dc, 0xaf82022c, 0x3c020001, 0x571021, 
+0x8c4238e0, 0xaf820230, 0x3c020001, 0x571021, 
+0x8c4238e4, 0xaf820234, 0x3c02fffd, 0x3442ffff, 
+0x10000009, 0x2c2b024, 0x3c040001, 0x2484625c, 
+0x34a51100, 0x2003021, 0x3821, 0xafa00010, 
+0xc002b3b, 0xafa00014, 0x8f4202cc, 0x24420001, 
+0xaf4202cc, 0x1000019f, 0x8f4202cc, 0x101302, 
+0x30450fff, 0x24020001, 0x10a20005, 0x24020002, 
+0x10a2000d, 0x3c0408ff, 0x10000014, 0x3c050003, 
+0x3c0208ff, 0x3442ffff, 0x8f830220, 0x3c040004, 
+0x2c4b025, 0x621824, 0x34630008, 0xaf830220, 
+0x10000012, 0xaf450298, 0x3484fff7, 0x3c03fffb, 
+0x8f820220, 0x3463ffff, 0x2c3b024, 0x441024, 
+0xaf820220, 0x10000009, 0xaf450298, 0x3c040001, 
+0x24846268, 0x34a51200, 0x2003021, 0x3821, 
+0xafa00010, 0xc002b3b, 0xafa00014, 0x8f4202bc, 
+0x24420001, 0xaf4202bc, 0x10000176, 0x8f4202bc, 
+0x27840208, 0x24050200, 0xc002bbf, 0x24060008, 
+0x27440224, 0x24050200, 0xc002bbf, 0x24060008, 
+0x8f4202c4, 0x24420001, 0xaf4202c4, 0x10000169, 
+0x8f4202c4, 0x101302, 0x30430fff, 0x24020001, 
+0x10620011, 0x28620002, 0x50400005, 0x24020002, 
+0x10600007, 0x0, 0x10000017, 0x0, 
+0x1062000f, 0x0, 0x10000013, 0x0, 
+0x8c060248, 0x2021, 0xc005104, 0x24050004, 
+0x10000007, 0x0, 0x8c060248, 0x2021, 
+0xc005104, 0x24050004, 0x10000010, 0x0, 
+0x8c06024c, 0x2021, 0xc005104, 0x24050001, 
+0x1000000a, 0x0, 0x3c040001, 0x24846274, 
+0x3c050003, 0x34a51300, 0x2003021, 0x3821, 
+0xafa00010, 0xc002b3b, 0xafa00014, 0x8f4202c0, 
+0x24420001, 0xaf4202c0, 0x1000013a, 0x8f4202c0, 
+0xc002426, 0x0, 0x10000136, 0x0, 
+0x24020001, 0xa34205c5, 0x24100100, 0x8f4401a8, 
+0x8f4501ac, 0xafb00010, 0xafa00014, 0x8f420014, 
+0xafa20018, 0x8f420108, 0x26e60028, 0x40f809, 
+0x24070400, 0x1040fff5, 0x0, 0x10000125, 
+0x0, 0x3c03ffff, 0x34637fff, 0x8f420368, 
+0x8f440360, 0x2c3b024, 0x1821, 0xaf400058, 
+0xaf40005c, 0xaf400060, 0xaf400064, 0x441023, 
+0xaf420368, 0x3c020900, 0xaf400360, 0xafa20020, 
+0x8f5e0018, 0x27aa0020, 0x240200ff, 0x13c20002, 
+0xafaa003c, 0x27c30001, 0x8c020228, 0x609021, 
+0x1642000e, 0x1e38c0, 0x8f42033c, 0x24420001, 
+0xaf42033c, 0x8f42033c, 0x8c020228, 0x3c040001, 
+0x2484620c, 0x3c050009, 0xafa00014, 0xafa20010, 
+0x8fa60020, 0x1000006b, 0x34a50500, 0xf71021, 
+0x8fa30020, 0x8fa40024, 0xac4304c0, 0xac4404c4, 
+0x8f830054, 0x8f820054, 0x247003e8, 0x2021023, 
+0x2c4203e9, 0x1040001b, 0x9821, 0xe08821, 
+0x263504c0, 0x8f440178, 0x8f45017c, 0x2201821, 
+0x240a0004, 0xafaa0010, 0xafb20014, 0x8f48000c, 
+0x1021, 0x2f53021, 0xafa80018, 0x8f48010c, 
+0x24070008, 0xa32821, 0xa3482b, 0x822021, 
+0x100f809, 0x892021, 0x54400006, 0x24130001, 
+0x8f820054, 0x2021023, 0x2c4203e9, 0x1440ffe9, 
+0x0, 0x326200ff, 0x54400017, 0xaf520018, 
+0x8f420378, 0x24420001, 0xaf420378, 0x8f420378, 
+0x8f820120, 0x8faa003c, 0xafa20010, 0x8f820124, 
+0x3c040001, 0x24846218, 0x3c050009, 0xafa20014, 
+0x8d460000, 0x10000033, 0x34a50600, 0x8f420308, 
+0x24130001, 0x24420001, 0xaf420308, 0x8f420308, 
+0x1000001c, 0x326200ff, 0x8f830054, 0x8f820054, 
+0x247003e8, 0x2021023, 0x2c4203e9, 0x10400014, 
+0x9821, 0x24110010, 0x8f42000c, 0x8f440160, 
+0x8f450164, 0x8f860120, 0xafb10010, 0xafb20014, 
+0xafa20018, 0x8f42010c, 0x24070008, 0x40f809, 
+0x24c6001c, 0x1440ffe5, 0x0, 0x8f820054, 
+0x2021023, 0x2c4203e9, 0x1440ffef, 0x0, 
+0x326200ff, 0x14400011, 0x0, 0x8f420378, 
+0x24420001, 0xaf420378, 0x8f420378, 0x8f820120, 
+0x8faa003c, 0xafa20010, 0x8f820124, 0x3c040001, 
+0x24846220, 0x3c050009, 0xafa20014, 0x8d460000, 
+0x34a50700, 0xc002b3b, 0x3c03821, 0x8f4202b0, 
+0x24420001, 0xaf4202b0, 0x8f4202b0, 0x8f4202f8, 
+0x24420001, 0xaf4202f8, 0x1000008a, 0x8f4202f8, 
+0x8c02025c, 0x27440224, 0xaf4201f0, 0x8c020260, 
+0x24050200, 0x24060008, 0xc002bbf, 0xaf4201f8, 
+0x8f820220, 0x30420008, 0x14400002, 0x24020001, 
+0x24020002, 0xaf420298, 0x8f4202ac, 0x24420001, 
+0xaf4202ac, 0x10000077, 0x8f4202ac, 0x3c0200ff, 
+0x3442ffff, 0x2021824, 0x32c20180, 0x14400006, 
+0x3402fffb, 0x43102b, 0x14400003, 0x0, 
+0x1000006c, 0xaf4300bc, 0x3c040001, 0x24846280, 
+0x3c050003, 0x34a51500, 0x2003021, 0x3821, 
+0xafa00010, 0xc002b3b, 0xafa00014, 0x3c020700, 
+0x34421000, 0x101e02, 0x621825, 0xafa30020, 
+0x8f510018, 0x240200ff, 0x12220002, 0x8021, 
+0x26300001, 0x8c020228, 0x1602000e, 0x1130c0, 
+0x8f42033c, 0x24420001, 0xaf42033c, 0x8f42033c, 
+0x8c020228, 0x3c040001, 0x248461f4, 0x3c050009, 
+0xafa00014, 0xafa20010, 0x8fa60020, 0x1000003f, 
+0x34a50100, 0xd71021, 0x8fa30020, 0x8fa40024, 
+0xac4304c0, 0xac4404c4, 0xc01821, 0x8f440178, 
+0x8f45017c, 0x1021, 0x24070004, 0xafa70010, 
+0xafb00014, 0x8f48000c, 0x24c604c0, 0x2e63021, 
+0xafa80018, 0x8f48010c, 0x24070008, 0xa32821, 
+0xa3482b, 0x822021, 0x100f809, 0x892021, 
+0x1440000b, 0x24070008, 0x8f820120, 0xafa20010, 
+0x8f820124, 0x3c040001, 0x248461fc, 0x3c050009, 
+0xafa20014, 0x8fa60020, 0x1000001c, 0x34a50200, 
+0x8f440160, 0x8f450164, 0x8f43000c, 0xaf500018, 
+0x8f860120, 0x24020010, 0xafa20010, 0xafb00014, 
+0xafa30018, 0x8f42010c, 0x40f809, 0x24c6001c, 
+0x14400010, 0x0, 0x8f420340, 0x24420001, 
+0xaf420340, 0x8f420340, 0x8f820120, 0xafa20010, 
+0x8f820124, 0x3c040001, 0x24846204, 0x3c050009, 
+0xafa20014, 0x8fa60020, 0x34a50300, 0xc002b3b, 
+0x2203821, 0x8f4202e0, 0x24420001, 0xaf4202e0, 
+0x8f4202e0, 0x8f4202f0, 0x24420001, 0xaf4202f0, 
+0x8f4202f0, 0x8fa20034, 0x8fbf0058, 0x8fbe0054, 
+0x8fb50050, 0x8fb3004c, 0x8fb20048, 0x8fb10044, 
+0x8fb00040, 0x3e00008, 0x27bd0060, 0x27bdfff8, 
+0x2408ffff, 0x10a00014, 0x4821, 0x3c0aedb8, 
+0x354a8320, 0x90870000, 0x24840001, 0x3021, 
+0x1071026, 0x30420001, 0x10400002, 0x81842, 
+0x6a1826, 0x604021, 0x24c60001, 0x2cc20008, 
+0x1440fff7, 0x73842, 0x25290001, 0x125102b, 
+0x1440fff0, 0x0, 0x1001021, 0x3e00008, 
+0x27bd0008, 0x27bdffb0, 0xafbf0048, 0xafbe0044, 
+0xafb50040, 0xafb3003c, 0xafb20038, 0xafb10034, 
+0xafb00030, 0x8f870220, 0xafa70024, 0x8f870200, 
+0xafa7002c, 0x8f820220, 0x3c0308ff, 0x3463ffff, 
+0x431024, 0x34420004, 0xaf820220, 0x8f820200, 
+0x3c03c0ff, 0x3463ffff, 0x431024, 0x34420004, 
+0xaf820200, 0x8f530358, 0x8f55035c, 0x8f5e0360, 
+0x8f470364, 0xafa70014, 0x8f470368, 0xafa7001c, 
+0x8f4202d0, 0x274401c0, 0x24420001, 0xaf4202d0, 
+0x8f5002d0, 0x8f510204, 0x8f520200, 0xc002ba8, 
+0x24050400, 0xaf530358, 0xaf55035c, 0xaf5e0360, 
+0x8fa70014, 0xaf470364, 0x8fa7001c, 0xaf470368, 
+0xaf5002d0, 0xaf510204, 0xaf520200, 0x8c02025c, 
+0x27440224, 0xaf4201f0, 0x8c020260, 0x24050200, 
+0x24060008, 0xaf4201f8, 0x24020006, 0xc002bbf, 
+0xaf4201f4, 0x3c023b9a, 0x3442ca00, 0xaf4201fc, 
+0x240203e8, 0x24040002, 0x24030001, 0xaf420294, 
+0xaf440290, 0xaf43029c, 0x8f820220, 0x30420008, 
+0x10400004, 0x0, 0xaf430298, 0x10000003, 
+0x3021, 0xaf440298, 0x3021, 0x3c030001, 
+0x661821, 0x90636d00, 0x3461021, 0x24c60001, 
+0xa043022c, 0x2cc2000f, 0x1440fff8, 0x3461821, 
+0x24c60001, 0x8f820040, 0x24040080, 0x24050080, 
+0x21702, 0x24420030, 0xa062022c, 0x3461021, 
+0xc002ba8, 0xa040022c, 0x8fa70024, 0x30e20004, 
+0x14400006, 0x0, 0x8f820220, 0x3c0308ff, 
+0x3463fffb, 0x431024, 0xaf820220, 0x8fa7002c, 
+0x30e20004, 0x14400006, 0x0, 0x8f820200, 
+0x3c03c0ff, 0x3463fffb, 0x431024, 0xaf820200, 
+0x8fbf0048, 0x8fbe0044, 0x8fb50040, 0x8fb3003c, 
+0x8fb20038, 0x8fb10034, 0x8fb00030, 0x3e00008, 
+0x27bd0050, 0x0, 0x0, 0xaf400104, 
+0x24040001, 0x410c0, 0x2e21821, 0x24820001, 
+0x3c010001, 0x230821, 0xa42234d0, 0x402021, 
+0x2c820080, 0x1440fff8, 0x410c0, 0x24020001, 
+0x3c010001, 0x370821, 0xa42038d0, 0xaf420100, 
+0xaf800228, 0xaf80022c, 0xaf800230, 0xaf800234, 
+0x3e00008, 0x0, 0x27bdffe8, 0xafbf0014, 
+0xafb00010, 0x8f420104, 0x28420005, 0x10400026, 
+0x808021, 0x3c020001, 0x8f430104, 0x344230d0, 
+0x2e22021, 0x318c0, 0x621821, 0x2e31821, 
+0x83102b, 0x10400015, 0x1021, 0x96070000, 
+0x24840006, 0x24660006, 0x9482fffc, 0x14470009, 
+0x2821, 0x9483fffe, 0x96020002, 0x14620006, 
+0xa01021, 0x94820000, 0x96030004, 0x431026, 
+0x2c450001, 0xa01021, 0x14400009, 0x24840008, 
+0x86102b, 0x1440fff0, 0x1021, 0x304200ff, 
+0x14400030, 0x24020001, 0x1000002e, 0x1021, 
+0x1000fffa, 0x24020001, 0x2002021, 0xc00240c, 
+0x24050006, 0x3042007f, 0x218c0, 0x2e31021, 
+0x3c010001, 0x220821, 0x942230d0, 0x1040fff2, 
+0x2e31021, 0x3c060001, 0xc23021, 0x94c630d0, 
+0x10c0ffed, 0x3c080001, 0x350834d2, 0x96070000, 
+0x610c0, 0x572021, 0x882021, 0x94820000, 
+0x14470009, 0x2821, 0x94830002, 0x96020002, 
+0x14620006, 0xa01021, 0x94820004, 0x96030004, 
+0x431026, 0x2c450001, 0xa01021, 0x14400007, 
+0x610c0, 0x2e21021, 0x3c060001, 0xc23021, 
+0x94c634d0, 0x14c0ffeb, 0x610c0, 0x10c0ffd2, 
+0x24020001, 0x8fbf0014, 0x8fb00010, 0x3e00008, 
+0x27bd0018, 0x3e00008, 0x0, 0x27bdffb0, 
+0x801021, 0xafb00030, 0x24500002, 0x2002021, 
+0x24050006, 0xafb10034, 0x408821, 0xafbf0048, 
+0xafbe0044, 0xafb50040, 0xafb3003c, 0xc00240c, 
+0xafb20038, 0x3047007f, 0x710c0, 0x2e21021, 
+0x3c050001, 0xa22821, 0x94a530d0, 0x50a0001c, 
+0xa03021, 0x3c090001, 0x352934d2, 0x96280002, 
+0x510c0, 0x572021, 0x892021, 0x94820000, 
+0x14480009, 0x3021, 0x94830002, 0x96020002, 
+0x14620006, 0xc01021, 0x94820004, 0x96030004, 
+0x431026, 0x2c460001, 0xc01021, 0x14400007, 
+0x510c0, 0x2e21021, 0x3c050001, 0xa22821, 
+0x94a534d0, 0x14a0ffeb, 0x510c0, 0xa03021, 
+0x10c00014, 0x610c0, 0x571821, 0x3c010001, 
+0x230821, 0x8c2334d0, 0x571021, 0xafa30010, 
+0x3c010001, 0x220821, 0x8c2234d4, 0x3c040001, 
+0x24846394, 0xafa20014, 0x8e260000, 0x8e270004, 
+0x3c050004, 0xc002b3b, 0x34a50400, 0x10000063, 
+0x3c020800, 0x8f450100, 0x10a00006, 0x510c0, 
+0x2e21021, 0x3c010001, 0x220821, 0x942234d0, 
+0xaf420100, 0xa03021, 0x14c00011, 0x628c0, 
+0x710c0, 0x2e21021, 0xafa70010, 0x3c010001, 
+0x220821, 0x942230d0, 0x3c040001, 0x248463a0, 
+0xafa20014, 0x8e260000, 0x8e270004, 0x3c050004, 
+0xc002b3b, 0x34a50500, 0x10000048, 0x3c020800, 
+0xb71821, 0x3c020001, 0x96040000, 0x344234d2, 
+0x621821, 0xa4640000, 0x8e020002, 0x720c0, 
+0xac620002, 0x2e41021, 0x3c030001, 0x621821, 
+0x946330d0, 0x2e51021, 0x3c010001, 0x220821, 
+0xa42334d0, 0x2e41021, 0x3c010001, 0x220821, 
+0xa42630d0, 0x8f420104, 0x24420001, 0x28420080, 
+0x1040000f, 0x3c020002, 0x8f420104, 0x3c040001, 
+0x348430d2, 0x96030000, 0x210c0, 0x571021, 
+0x441021, 0xa4430000, 0x8e030002, 0xac430002, 
+0x8f420104, 0x24420001, 0xaf420104, 0x3c020002, 
+0x2c21024, 0x10400011, 0x72142, 0x3c030001, 
+0x346338d8, 0x24020003, 0x441023, 0x21080, 
+0x572021, 0x832021, 0x571021, 0x431021, 
+0x30e5001f, 0x8c430000, 0x24020001, 0xa21004, 
+0x621825, 0x1000000c, 0xac830000, 0x24020003, 
+0x441023, 0x21080, 0x5c2821, 0x5c1021, 
+0x30e4001f, 0x8c430228, 0x24020001, 0x821004, 
+0x621825, 0xaca30228, 0x3c020800, 0x34421000, 
+0x1821, 0xafa20020, 0x8f5e0018, 0x27aa0020, 
+0x240200ff, 0x13c20002, 0xafaa002c, 0x27c30001, 
+0x8c020228, 0x609021, 0x1642000e, 0x1e38c0, 
+0x8f42033c, 0x24420001, 0xaf42033c, 0x8f42033c, 
+0x8c020228, 0x3c040001, 0x2484635c, 0x3c050009, 
+0xafa00014, 0xafa20010, 0x8fa60020, 0x1000006b, 
+0x34a50500, 0xf71021, 0x8fa30020, 0x8fa40024, 
+0xac4304c0, 0xac4404c4, 0x8f830054, 0x8f820054, 
+0x247003e8, 0x2021023, 0x2c4203e9, 0x1040001b, 
+0x9821, 0xe08821, 0x263504c0, 0x8f440178, 
+0x8f45017c, 0x2201821, 0x240a0004, 0xafaa0010, 
+0xafb20014, 0x8f48000c, 0x1021, 0x2f53021, 
+0xafa80018, 0x8f48010c, 0x24070008, 0xa32821, 
+0xa3482b, 0x822021, 0x100f809, 0x892021, 
+0x54400006, 0x24130001, 0x8f820054, 0x2021023, 
+0x2c4203e9, 0x1440ffe9, 0x0, 0x326200ff, 
+0x54400017, 0xaf520018, 0x8f420378, 0x24420001, 
+0xaf420378, 0x8f420378, 0x8f820120, 0x8faa002c, 
+0xafa20010, 0x8f820124, 0x3c040001, 0x24846368, 
+0x3c050009, 0xafa20014, 0x8d460000, 0x10000033, 
+0x34a50600, 0x8f420308, 0x24130001, 0x24420001, 
+0xaf420308, 0x8f420308, 0x1000001c, 0x326200ff, 
+0x8f830054, 0x8f820054, 0x247003e8, 0x2021023, 
+0x2c4203e9, 0x10400014, 0x9821, 0x24110010, 
+0x8f42000c, 0x8f440160, 0x8f450164, 0x8f860120, 
+0xafb10010, 0xafb20014, 0xafa20018, 0x8f42010c, 
+0x24070008, 0x40f809, 0x24c6001c, 0x1440ffe5, 
+0x0, 0x8f820054, 0x2021023, 0x2c4203e9, 
+0x1440ffef, 0x0, 0x326200ff, 0x14400011, 
+0x0, 0x8f420378, 0x24420001, 0xaf420378, 
+0x8f420378, 0x8f820120, 0x8faa002c, 0xafa20010, 
+0x8f820124, 0x3c040001, 0x24846370, 0x3c050009, 
+0xafa20014, 0x8d460000, 0x34a50700, 0xc002b3b, 
+0x3c03821, 0x8f4202b4, 0x24420001, 0xaf4202b4, 
+0x8f4202b4, 0x8f4202f4, 0x24420001, 0xaf4202f4, 
+0x8f4202f4, 0x8fbf0048, 0x8fbe0044, 0x8fb50040, 
+0x8fb3003c, 0x8fb20038, 0x8fb10034, 0x8fb00030, 
+0x3e00008, 0x27bd0050, 0x27bdffa0, 0x801021, 
+0xafb00040, 0x24500002, 0x2002021, 0x24050006, 
+0xafb10044, 0x408821, 0xafbf0058, 0xafbe0054, 
+0xafb50050, 0xafb3004c, 0xc00240c, 0xafb20048, 
+0x3048007f, 0x810c0, 0x2e21021, 0x3c060001, 
+0xc23021, 0x94c630d0, 0x10c0001c, 0x3821, 
+0x3c0a0001, 0x354a34d2, 0x96290002, 0x610c0, 
+0x572021, 0x8a2021, 0x94820000, 0x14490009, 
+0x2821, 0x94830002, 0x96020002, 0x14620006, 
+0xa01021, 0x94820004, 0x96030004, 0x431026, 
+0x2c450001, 0xa01021, 0x14400008, 0x610c0, 
+0xc03821, 0x2e21021, 0x3c060001, 0xc23021, 
+0x94c634d0, 0x14c0ffea, 0x610c0, 0x14c00011, 
+0xafa70028, 0x810c0, 0x2e21021, 0xafa80010, 
+0x3c010001, 0x220821, 0x942230d0, 0x3c040001, 
+0x248463ac, 0xafa20014, 0x8e260000, 0x8e270004, 
+0x3c050004, 0xc002b3b, 0x34a50900, 0x10000075, 
+0x3c020800, 0x10e0000c, 0x610c0, 0x2e21021, 
+0x3c030001, 0x621821, 0x946334d0, 0x710c0, 
+0x2e21021, 0x3c010001, 0x220821, 0xa42334d0, 
+0x1000000b, 0x3c040001, 0x2e21021, 0x3c030001, 
+0x621821, 0x946334d0, 0x810c0, 0x2e21021, 
+0x3c010001, 0x220821, 0xa42330d0, 0x3c040001, 
+0x348430d0, 0x8f430100, 0x610c0, 0x2e21021, 
+0x3c010001, 0x220821, 0xa42334d0, 0x8f420104, 
+0x2e43821, 0x2821, 0x18400029, 0xaf460100, 
+0x24e60006, 0x94c3fffc, 0x96020000, 0x14620009, 
+0x2021, 0x94c3fffe, 0x96020002, 0x14620006, 
+0x801021, 0x94c20000, 0x96030004, 0x431026, 
+0x2c440001, 0x801021, 0x50400014, 0x24a50001, 
+0x8f420104, 0x2442ffff, 0xa2102a, 0x1040000b, 
+0x24e40004, 0x94820006, 0x8c830008, 0xa482fffe, 
+0xac830000, 0x8f420104, 0x24a50001, 0x2442ffff, 
+0xa2102a, 0x1440fff7, 0x24840008, 0x8f420104, 
+0x2442ffff, 0x10000006, 0xaf420104, 0x8f420104, 
+0x24c60008, 0xa2102a, 0x1440ffda, 0x24e70008, 
+0x810c0, 0x2e21021, 0x3c010001, 0x220821, 
+0x942230d0, 0x14400023, 0x3c020800, 0x3c020002, 
+0x2c21024, 0x10400012, 0x82142, 0x3c030001, 
+0x346338d8, 0x24020003, 0x441023, 0x21080, 
+0x572021, 0x832021, 0x571021, 0x431021, 
+0x3105001f, 0x24030001, 0x8c420000, 0xa31804, 
+0x31827, 0x431024, 0x1000000d, 0xac820000, 
+0x24020003, 0x441023, 0x21080, 0x5c2821, 
+0x5c1021, 0x3104001f, 0x24030001, 0x8c420228, 
+0x831804, 0x31827, 0x431024, 0xaca20228, 
+0x3c020800, 0x34422000, 0x1821, 0xafa20020, 
+0x8f5e0018, 0x27ab0020, 0x240200ff, 0x13c20002, 
+0xafab0034, 0x27c30001, 0x8c020228, 0x609021, 
+0x1642000e, 0x1e38c0, 0x8f42033c, 0x24420001, 
+0xaf42033c, 0x8f42033c, 0x8c020228, 0x3c040001, 
+0x2484635c, 0x3c050009, 0xafa00014, 0xafa20010, 
+0x8fa60020, 0x1000006b, 0x34a50500, 0xf71021, 
+0x8fa30020, 0x8fa40024, 0xac4304c0, 0xac4404c4, 
+0x8f830054, 0x8f820054, 0x247003e8, 0x2021023, 
+0x2c4203e9, 0x1040001b, 0x9821, 0xe08821, 
+0x263504c0, 0x8f440178, 0x8f45017c, 0x2201821, 
+0x240b0004, 0xafab0010, 0xafb20014, 0x8f48000c, 
+0x1021, 0x2f53021, 0xafa80018, 0x8f48010c, 
+0x24070008, 0xa32821, 0xa3482b, 0x822021, 
+0x100f809, 0x892021, 0x54400006, 0x24130001, 
+0x8f820054, 0x2021023, 0x2c4203e9, 0x1440ffe9, 
+0x0, 0x326200ff, 0x54400017, 0xaf520018, 
+0x8f420378, 0x24420001, 0xaf420378, 0x8f420378, 
+0x8f820120, 0x8fab0034, 0xafa20010, 0x8f820124, 
+0x3c040001, 0x24846368, 0x3c050009, 0xafa20014, 
+0x8d660000, 0x10000033, 0x34a50600, 0x8f420308, 
+0x24130001, 0x24420001, 0xaf420308, 0x8f420308, 
+0x1000001c, 0x326200ff, 0x8f830054, 0x8f820054, 
+0x247003e8, 0x2021023, 0x2c4203e9, 0x10400014, 
+0x9821, 0x24110010, 0x8f42000c, 0x8f440160, 
+0x8f450164, 0x8f860120, 0xafb10010, 0xafb20014, 
+0xafa20018, 0x8f42010c, 0x24070008, 0x40f809, 
+0x24c6001c, 0x1440ffe5, 0x0, 0x8f820054, 
+0x2021023, 0x2c4203e9, 0x1440ffef, 0x0, 
+0x326200ff, 0x14400011, 0x0, 0x8f420378, 
+0x24420001, 0xaf420378, 0x8f420378, 0x8f820120, 
+0x8fab0034, 0xafa20010, 0x8f820124, 0x3c040001, 
+0x24846370, 0x3c050009, 0xafa20014, 0x8d660000, 
+0x34a50700, 0xc002b3b, 0x3c03821, 0x8f4202b8, 
+0x24420001, 0xaf4202b8, 0x8f4202b8, 0x8f4202f4, 
+0x24420001, 0xaf4202f4, 0x8f4202f4, 0x8fbf0058, 
+0x8fbe0054, 0x8fb50050, 0x8fb3004c, 0x8fb20048, 
+0x8fb10044, 0x8fb00040, 0x3e00008, 0x27bd0060, 
+0x0, 0x0, 0x0, 0x27bdffe0, 
+0x27644000, 0xafbf0018, 0xc002ba8, 0x24051000, 
+0x3c030001, 0x34632cc0, 0x3c040001, 0x34842ec8, 
+0x24020020, 0xaf82011c, 0x2e31021, 0xaf800100, 
+0xaf800104, 0xaf800108, 0xaf800110, 0xaf800114, 
+0xaf800118, 0xaf800120, 0xaf800124, 0xaf800128, 
+0xaf800130, 0xaf800134, 0xaf800138, 0xaf4200ec, 
+0x2e31021, 0xaf4200f0, 0x2e41021, 0xaf4200f4, 
+0x2e41021, 0xaf4200f8, 0x3c020001, 0x571021, 
+0x904240f4, 0x1440001c, 0x3c050001, 0x8f82011c, 
+0x3c040001, 0x24846470, 0x3c050001, 0x34420001, 
+0xaf82011c, 0xafa00010, 0xafa00014, 0x8f86011c, 
+0x34a50100, 0xc002b3b, 0x3821, 0x8c020218, 
+0x30420040, 0x10400014, 0x0, 0x8f82011c, 
+0x3c040001, 0x2484647c, 0x3c050001, 0x34420004, 
+0xaf82011c, 0xafa00010, 0xafa00014, 0x8f86011c, 
+0x10000007, 0x34a50200, 0x3c040001, 0x24846484, 
+0xafa00010, 0xafa00014, 0x8f86011c, 0x34a50300, 
+0xc002b3b, 0x3821, 0x8fbf0018, 0x3e00008, 
+0x27bd0020, 0x8fa90010, 0x8f83012c, 0x8faa0014, 
+0x8fab0018, 0x1060000a, 0x27624fe0, 0x14620002, 
+0x24680020, 0x27684800, 0x8f820128, 0x11020004, 
+0x0, 0x8f820124, 0x15020007, 0x0, 
+0x8f430334, 0x1021, 0x24630001, 0xaf430334, 
+0x10000039, 0x8f430334, 0xac640000, 0xac650004, 
+0xac660008, 0xa467000e, 0xac690018, 0xac6a001c, 
+0xac6b0010, 0xac620014, 0xaf880120, 0x8f4200fc, 
+0x8f4400f4, 0x2442ffff, 0xaf4200fc, 0x8c820000, 
+0x10490005, 0x3042ff8f, 0x10400019, 0x3122ff8f, 
+0x10400018, 0x3c020001, 0x8c830004, 0x2c620010, 
+0x10400013, 0x3c020001, 0x24630001, 0xac830004, 
+0x8f4300f8, 0x344230c8, 0x2e21021, 0x54620004, 
+0x24620008, 0x3c020001, 0x34422ec8, 0x2e21021, 
+0x14440015, 0x24020001, 0x8f820128, 0x24420020, 
+0xaf820128, 0x8f820128, 0x1000000f, 0x24020001, 
+0x3c020001, 0x344230c8, 0x2e21021, 0x54820004, 
+0x24820008, 0x3c020001, 0x34422ec8, 0x2e21021, 
+0x402021, 0x24020001, 0xaf4400f4, 0xac890000, 
+0xac820004, 0x24020001, 0x3e00008, 0x0, 
+0x3e00008, 0x0, 0x8fa90010, 0x8f83010c, 
+0x8faa0014, 0x8fab0018, 0x1060000a, 0x276247e0, 
+0x14620002, 0x24680020, 0x27684000, 0x8f820108, 
+0x11020004, 0x0, 0x8f820104, 0x15020007, 
+0x0, 0x8f430338, 0x1021, 0x24630001, 
+0xaf430338, 0x10000035, 0x8f430338, 0xac640000, 
+0xac650004, 0xac660008, 0xa467000e, 0xac690018, 
+0xac6a001c, 0xac6b0010, 0xac620014, 0xaf880100, 
+0x8f4400ec, 0x8c820000, 0x30420006, 0x10400019, 
+0x31220006, 0x10400018, 0x3c020001, 0x8c830004, 
+0x2c620010, 0x10400013, 0x3c020001, 0x24630001, 
+0xac830004, 0x8f4300f0, 0x34422ec0, 0x2e21021, 
+0x54620004, 0x24620008, 0x3c020001, 0x34422cc0, 
+0x2e21021, 0x14440015, 0x24020001, 0x8f820108, 
+0x24420020, 0xaf820108, 0x8f820108, 0x1000000f, 
+0x24020001, 0x3c020001, 0x34422ec0, 0x2e21021, 
+0x54820004, 0x24820008, 0x3c020001, 0x34422cc0, 
+0x2e21021, 0x402021, 0x24020001, 0xaf4400ec, 
+0xac890000, 0xac820004, 0x24020001, 0x3e00008, 
+0x0, 0x3e00008, 0x0, 0x27bdffd8, 
+0x3c040001, 0x2484648c, 0x3c050001, 0xafbf0024, 
+0xafb20020, 0xafb1001c, 0xafb00018, 0x8f900104, 
+0x8f9100b0, 0x8f92011c, 0x34a52500, 0x8f820100, 
+0x2403021, 0x2203821, 0xafa20010, 0xc002b3b, 
+0xafb00014, 0x8e020008, 0xafa20010, 0x8e02000c, 
+0x3c040001, 0x24846498, 0xafa20014, 0x8e060000, 
+0x8e070004, 0x3c050001, 0xc002b3b, 0x34a52510, 
+0x8e020018, 0xafa20010, 0x8e02001c, 0x3c040001, 
+0x248464a4, 0xafa20014, 0x8e060010, 0x8e070014, 
+0x3c050001, 0xc002b3b, 0x34a52520, 0x3c027f00, 
+0x2221024, 0x3c030800, 0x54430016, 0x3c030200, 
+0x8f82009c, 0x3042ffff, 0x14400012, 0x3c030200, 
+0x3c040001, 0x248464b0, 0x3c050002, 0x34a5f030, 
+0x3021, 0x3821, 0x36420002, 0xaf82011c, 
+0x36220001, 0xaf8200b0, 0xaf900104, 0xaf92011c, 
+0xafa00010, 0xc002b3b, 0xafa00014, 0x10000024, 
+0x0, 0x2c31024, 0x1040000d, 0x2231024, 
+0x1040000b, 0x36420002, 0xaf82011c, 0x36220001, 
+0xaf8200b0, 0xaf900104, 0xaf92011c, 0x8f420330, 
+0x24420001, 0xaf420330, 0x10000015, 0x8f420330, 
+0x3c040001, 0x248464b8, 0x240202a9, 0xafa20010, 
+0xafa00014, 0x8f860144, 0x3c070001, 0x24e764c0, 
+0xc002b3b, 0x3405dead, 0x8f82011c, 0x34420002, 
+0xaf82011c, 0x8f820220, 0x34420004, 0xaf820220, 
+0x8f820140, 0x3c030001, 0x431025, 0xaf820140, 
+0x8fbf0024, 0x8fb20020, 0x8fb1001c, 0x8fb00018, 
+0x3e00008, 0x27bd0028, 0x27bdffd8, 0x3c040001, 
+0x248464e8, 0x3c050001, 0xafbf0024, 0xafb20020, 
+0xafb1001c, 0xafb00018, 0x8f900124, 0x8f9100a0, 
+0x8f92011c, 0x34a52600, 0x8f820120, 0x2403021, 
+0x2203821, 0xafa20010, 0xc002b3b, 0xafb00014, 
+0x8e020008, 0xafa20010, 0x8e02000c, 0x3c040001, 
+0x248464f4, 0xafa20014, 0x8e060000, 0x8e070004, 
+0x3c050001, 0xc002b3b, 0x34a52610, 0x8e020018, 
+0xafa20010, 0x8e02001c, 0x3c040001, 0x24846500, 
+0xafa20014, 0x8e060010, 0x8e070014, 0x3c050001, 
+0xc002b3b, 0x34a52620, 0x3c027f00, 0x2221024, 
+0x3c030800, 0x54430016, 0x3c030200, 0x8f8200ac, 
+0x3042ffff, 0x14400012, 0x3c030200, 0x3c040001, 
+0x2484650c, 0x3c050001, 0x34a5f030, 0x3021, 
+0x3821, 0x36420002, 0xaf82011c, 0x36220001, 
+0xaf8200a0, 0xaf900124, 0xaf92011c, 0xafa00010, 
+0xc002b3b, 0xafa00014, 0x10000024, 0x0, 
+0x2c31024, 0x1040000d, 0x2231024, 0x1040000b, 
+0x36420002, 0xaf82011c, 0x36220001, 0xaf8200a0, 
+0xaf900124, 0xaf92011c, 0x8f42032c, 0x24420001, 
+0xaf42032c, 0x10000015, 0x8f42032c, 0x3c040001, 
+0x248464b8, 0x240202e2, 0xafa20010, 0xafa00014, 
+0x8f860144, 0x3c070001, 0x24e764c0, 0xc002b3b, 
+0x3405dead, 0x8f82011c, 0x34420002, 0xaf82011c, 
+0x8f820220, 0x34420004, 0xaf820220, 0x8f820140, 
+0x3c030001, 0x431025, 0xaf820140, 0x8fbf0024, 
+0x8fb20020, 0x8fb1001c, 0x8fb00018, 0x3e00008, 
+0x27bd0028, 0x6021, 0x5021, 0x3021, 
+0x2821, 0x6821, 0x4821, 0x7821, 
+0x7021, 0x8f880124, 0x8f870104, 0x1580002e, 
+0x8f8b011c, 0x11a00014, 0x31620800, 0x8f820120, 
+0x10460029, 0x0, 0x3c040001, 0x8c846ee4, 
+0x8cc20000, 0x8cc30004, 0xac820000, 0xac830004, 
+0x8cc20008, 0xac820008, 0x94c2000e, 0xa482000e, 
+0x8cc20010, 0x240c0001, 0xac820010, 0x8cc20014, 
+0x10000012, 0x24c60020, 0x10400017, 0x0, 
+0x3c040001, 0x8c846ee4, 0x8d020000, 0x8d030004, 
+0xac820000, 0xac830004, 0x8d020008, 0xac820008, 
+0x9502000e, 0xa482000e, 0x8d020010, 0x25060020, 
+0xac820010, 0x8d020014, 0x240c0001, 0xc01821, 
+0xac820014, 0x27624fe0, 0x43102b, 0x54400001, 
+0x27634800, 0x603021, 0x1540002f, 0x31620100, 
+0x11200014, 0x31628000, 0x8f820100, 0x1045002a, 
+0x31620100, 0x3c040001, 0x8c846ee0, 0x8ca20000, 
+0x8ca30004, 0xac820000, 0xac830004, 0x8ca20008, 
+0xac820008, 0x94a2000e, 0xa482000e, 0x8ca20010, 
+0x240a0001, 0xac820010, 0x8ca20014, 0x10000012, 
+0x24a50020, 0x10400018, 0x31620100, 0x3c040001, 
+0x8c846ee0, 0x8ce20000, 0x8ce30004, 0xac820000, 
+0xac830004, 0x8ce20008, 0xac820008, 0x94e2000e, 
+0xa482000e, 0x8ce20010, 0x24e50020, 0xac820010, 
+0x8ce20014, 0x240a0001, 0xa01821, 0xac820014, 
+0x276247e0, 0x43102b, 0x54400001, 0x27634000, 
+0x602821, 0x31620100, 0x5440001d, 0x31621000, 
+0x11a00009, 0x31a20800, 0x10400004, 0x25020020, 
+0x8f8200a8, 0xa5e20000, 0x25020020, 0xaf820124, 
+0x8f880124, 0x6821, 0x11800011, 0x31621000, 
+0x3c040001, 0x8c846ee4, 0x8c820000, 0x8c830004, 
+0xaf820080, 0xaf830084, 0x8c820008, 0xaf8200a4, 
+0x9482000e, 0xaf8200ac, 0x8c820010, 0x6021, 
+0xaf8200a0, 0x8c8d0010, 0x8c8f0014, 0x31621000, 
+0x1440ff82, 0x0, 0x1120000f, 0x31220800, 
+0x10400004, 0x3c020002, 0x8f8200b8, 0xa5c20000, 
+0x3c020002, 0x1221024, 0x10400004, 0x24e20020, 
+0x8f8200b4, 0xaf8200d4, 0x24e20020, 0xaf820104, 
+0x8f870104, 0x4821, 0x1140ff70, 0x0, 
+0x3c040001, 0x8c846ee0, 0x8c820000, 0x8c830004, 
+0xaf820090, 0xaf830094, 0x8c820008, 0xaf8200b4, 
+0x9482000e, 0xaf82009c, 0x8c820010, 0x5021, 
+0xaf8200b0, 0x8c890010, 0x1000ff60, 0x8c8e0014, 
+0x3e00008, 0x0, 0x6021, 0x5821, 
+0x3021, 0x2821, 0x6821, 0x5021, 
+0x7821, 0x7021, 0x8f880124, 0x8f870104, 
+0x3c180100, 0x1580002e, 0x8f89011c, 0x11a00014, 
+0x31220800, 0x8f820120, 0x10460029, 0x0, 
+0x3c040001, 0x8c846ee4, 0x8cc20000, 0x8cc30004, 
+0xac820000, 0xac830004, 0x8cc20008, 0xac820008, 
+0x94c2000e, 0xa482000e, 0x8cc20010, 0x240c0001, 
+0xac820010, 0x8cc20014, 0x10000012, 0x24c60020, 
+0x10400017, 0x0, 0x3c040001, 0x8c846ee4, 
+0x8d020000, 0x8d030004, 0xac820000, 0xac830004, 
+0x8d020008, 0xac820008, 0x9502000e, 0xa482000e, 
+0x8d020010, 0x25060020, 0xac820010, 0x8d020014, 
+0x240c0001, 0xc01821, 0xac820014, 0x27624fe0, 
+0x43102b, 0x54400001, 0x27634800, 0x603021, 
+0x1560002f, 0x31220100, 0x11400014, 0x31228000, 
+0x8f820100, 0x1045002a, 0x31220100, 0x3c040001, 
+0x8c846ee0, 0x8ca20000, 0x8ca30004, 0xac820000, 
+0xac830004, 0x8ca20008, 0xac820008, 0x94a2000e, 
+0xa482000e, 0x8ca20010, 0x240b0001, 0xac820010, 
+0x8ca20014, 0x10000012, 0x24a50020, 0x10400018, 
+0x31220100, 0x3c040001, 0x8c846ee0, 0x8ce20000, 
+0x8ce30004, 0xac820000, 0xac830004, 0x8ce20008, 
+0xac820008, 0x94e2000e, 0xa482000e, 0x8ce20010, 
+0x24e50020, 0xac820010, 0x8ce20014, 0x240b0001, 
+0xa01821, 0xac820014, 0x276247e0, 0x43102b, 
+0x54400001, 0x27634000, 0x602821, 0x31220100, 
+0x5440001d, 0x31221000, 0x11a00009, 0x31a20800, 
+0x10400004, 0x25020020, 0x8f8200a8, 0xa5e20000, 
+0x25020020, 0xaf820124, 0x8f880124, 0x6821, 
+0x11800011, 0x31221000, 0x3c040001, 0x8c846ee4, 
+0x8c820000, 0x8c830004, 0xaf820080, 0xaf830084, 
+0x8c820008, 0xaf8200a4, 0x9482000e, 0xaf8200ac, 
+0x8c820010, 0x6021, 0xaf8200a0, 0x8c8d0010, 
+0x8c8f0014, 0x31221000, 0x14400022, 0x0, 
+0x1140000f, 0x31420800, 0x10400004, 0x3c020002, 
+0x8f8200b8, 0xa5c20000, 0x3c020002, 0x1421024, 
+0x10400004, 0x24e20020, 0x8f8200b4, 0xaf8200d4, 
+0x24e20020, 0xaf820104, 0x8f870104, 0x5021, 
+0x11600010, 0x0, 0x3c040001, 0x8c846ee0, 
+0x8c820000, 0x8c830004, 0xaf820090, 0xaf830094, 
+0x8c820008, 0xaf8200b4, 0x9482000e, 0xaf82009c, 
+0x8c820010, 0x5821, 0xaf8200b0, 0x8c8a0010, 
+0x8c8e0014, 0x8f820070, 0x3c031000, 0x431024, 
+0x1040ff5c, 0x0, 0x8f820054, 0x24420005, 
+0xaf820078, 0x8c040234, 0x10800016, 0x1821, 
+0x3c020001, 0x571021, 0x8c4240e8, 0x24420005, 
+0x3c010001, 0x370821, 0xac2240e8, 0x3c020001, 
+0x571021, 0x8c4240e8, 0x44102b, 0x14400009, 
+0x24020001, 0x3c030080, 0x3c010001, 0x370821, 
+0xac2040e8, 0x3c010001, 0x370821, 0x1000000c, 
+0xa02240f0, 0x3c020001, 0x571021, 0x904240f0, 
+0x14400006, 0x3c020080, 0x3c020001, 0x571021, 
+0x904240f1, 0x10400002, 0x3c020080, 0x621825, 
+0x8c040230, 0x10800013, 0x0, 0x3c020001, 
+0x571021, 0x8c4240ec, 0x24420005, 0x3c010001, 
+0x370821, 0xac2240ec, 0x3c020001, 0x571021, 
+0x8c4240ec, 0x44102b, 0x14400006, 0x0, 
+0x3c010001, 0x370821, 0xac2040ec, 0x10000006, 
+0x781825, 0x3c020001, 0x571021, 0x904240f2, 
+0x54400001, 0x781825, 0x1060ff1a, 0x0, 
+0x8f420000, 0x10400007, 0x0, 0xaf80004c, 
+0x8f82004c, 0x1040fffd, 0x0, 0x10000005, 
+0x0, 0xaf800048, 0x8f820048, 0x1040fffd, 
+0x0, 0x8f820060, 0x431025, 0xaf820060, 
+0x8f420000, 0x10400003, 0x0, 0x1000ff05, 
+0xaf80004c, 0x1000ff03, 0xaf800048, 0x3e00008, 
+0x0, 0x0, 0x0, 0x3c020001, 
+0x8c426d28, 0x27bdffe8, 0xafbf0014, 0x14400012, 
+0xafb00010, 0x3c100001, 0x26106f90, 0x2002021, 
+0xc002ba8, 0x24052000, 0x26021fe0, 0x3c010001, 
+0xac226eec, 0x3c010001, 0xac226ee8, 0xac020250, 
+0x24022000, 0xac100254, 0xac020258, 0x24020001, 
+0x3c010001, 0xac226d28, 0x8fbf0014, 0x8fb00010, 
+0x3e00008, 0x27bd0018, 0x3c090001, 0x8d296eec, 
+0x8c820000, 0x8fa30010, 0x8fa80014, 0xad220000, 
+0x8c820004, 0xad250008, 0xad220004, 0x8f820054, 
+0xad260010, 0xad270014, 0xad230018, 0xad28001c, 
+0xad22000c, 0x2529ffe0, 0x3c020001, 0x24426f90, 
+0x122102b, 0x10400003, 0x0, 0x3c090001, 
+0x8d296ee8, 0x3c020001, 0x8c426d10, 0xad220000, 
+0x3c020001, 0x8c426d10, 0x3c010001, 0xac296eec, 
+0xad220004, 0xac090250, 0x3e00008, 0x0, 
+0x27bdffd0, 0xafb00010, 0x3c100001, 0x8e106eec, 
+0x3c020001, 0x8c426d10, 0xafb10014, 0x808821, 
+0xafbe0024, 0x8fbe0040, 0x8fa40048, 0xafb20018, 
+0xa09021, 0xafbf0028, 0xafb50020, 0xafb3001c, 
+0xae020000, 0x3c020001, 0x8c426d10, 0xc09821, 
+0xe0a821, 0x10800006, 0xae020004, 0x26050008, 
+0xc002bb3, 0x24060018, 0x10000005, 0x2610ffe0, 
+0x26040008, 0xc002ba8, 0x24050018, 0x2610ffe0, 
+0x3c030001, 0x24636f90, 0x203102b, 0x10400003, 
+0x0, 0x3c100001, 0x8e106ee8, 0x8e220000, 
+0xae020000, 0x8e220004, 0xae120008, 0xae020004, 
+0x8f820054, 0xae130010, 0xae150014, 0xae1e0018, 
+0x8fa80044, 0xae08001c, 0xae02000c, 0x2610ffe0, 
+0x203102b, 0x10400003, 0x0, 0x3c100001, 
+0x8e106ee8, 0x3c020001, 0x8c426d10, 0xae020000, 
+0x3c020001, 0x8c426d10, 0x3c010001, 0xac306eec, 
+0xae020004, 0xac100250, 0x8fbf0028, 0x8fbe0024, 
+0x8fb50020, 0x8fb3001c, 0x8fb20018, 0x8fb10014, 
+0x8fb00010, 0x3e00008, 0x27bd0030, 0x851821, 
+0x83102b, 0x10400006, 0x0, 0xac800000, 
+0x24840004, 0x83102b, 0x5440fffd, 0xac800000, 
+0x3e00008, 0x0, 0xa61821, 0xa3102b, 
+0x10400007, 0x0, 0x8c820000, 0xaca20000, 
+0x24a50004, 0xa3102b, 0x1440fffb, 0x24840004, 
+0x3e00008, 0x0, 0x861821, 0x83102b, 
+0x10400007, 0x0, 0x8ca20000, 0xac820000, 
+0x24840004, 0x83102b, 0x1440fffb, 0x24a50004, 
+0x3e00008, 0x0, 0x63080, 0x861821, 
+0x83102b, 0x10400006, 0x0, 0xac850000, 
+0x24840004, 0x83102b, 0x5440fffd, 0xac850000, 
+0x3e00008, 0x0, 0x0, 0x26e50028, 
+0xa03021, 0x274301c0, 0x8f4d0358, 0x8f47035c, 
+0x8f480360, 0x8f490364, 0x8f4a0368, 0x8f4b0204, 
+0x8f4c0200, 0x24640400, 0x64102b, 0x10400008, 
+0x3c0208ff, 0x8cc20000, 0xac620000, 0x24630004, 
+0x64102b, 0x1440fffb, 0x24c60004, 0x3c0208ff, 
+0x3442ffff, 0x3c03c0ff, 0xaf4d0358, 0xaf47035c, 
+0xaf480360, 0xaf490364, 0xaf4a0368, 0xaf4b0204, 
+0xaf4c0200, 0x8f840220, 0x3463ffff, 0x8f860200, 
+0x821024, 0x34420004, 0xc31824, 0x34630004, 
+0xaf820220, 0xaf830200, 0x8ca20214, 0xac020084, 
+0x8ca20218, 0xac020088, 0x8ca2021c, 0xac02008c, 
+0x8ca20220, 0xac020090, 0x8ca20224, 0xac020094, 
+0x8ca20228, 0xac020098, 0x8ca2022c, 0xac02009c, 
+0x8ca20230, 0xac0200a0, 0x8ca20234, 0xac0200a4, 
+0x8ca20238, 0xac0200a8, 0x8ca2023c, 0xac0200ac, 
+0x8ca20240, 0xac0200b0, 0x8ca20244, 0xac0200b4, 
+0x8ca20248, 0xac0200b8, 0x8ca2024c, 0xac0200bc, 
+0x8ca2001c, 0xac020080, 0x8ca20018, 0xac0200c0, 
+0x8ca20020, 0xac0200cc, 0x8ca20024, 0xac0200d0, 
+0x8ca201d0, 0xac0200e0, 0x8ca201d4, 0xac0200e4, 
+0x8ca201d8, 0xac0200e8, 0x8ca201dc, 0xac0200ec, 
+0x8ca201e0, 0xac0200f0, 0x8ca20098, 0x8ca3009c, 
+0xac0300fc, 0x8ca200a8, 0x8ca300ac, 0xac0300f4, 
+0x8ca200a0, 0x8ca300a4, 0x30840004, 0xac0300f8, 
+0x14800007, 0x30c20004, 0x8f820220, 0x3c0308ff, 
+0x3463fffb, 0x431024, 0xaf820220, 0x30c20004, 
+0x14400006, 0x0, 0x8f820200, 0x3c03c0ff, 
+0x3463fffb, 0x431024, 0xaf820200, 0x8f4202dc, 
+0xa34005c5, 0x24420001, 0xaf4202dc, 0x8f4202dc, 
+0x3e00008, 0x0, 0x27bdffd8, 0xafbf0024, 
+0xafb00020, 0x8f430024, 0x8f420020, 0x10620038, 
+0x0, 0x8f430020, 0x8f420024, 0x622023, 
+0x4810003, 0x0, 0x8f420040, 0x822021, 
+0x8f430030, 0x8f420024, 0x43102b, 0x14400005, 
+0x0, 0x8f430040, 0x8f420024, 0x10000005, 
+0x621023, 0x8f420030, 0x8f430024, 0x431023, 
+0x2442ffff, 0x406021, 0x8c102a, 0x54400001, 
+0x806021, 0x8f4a0024, 0x8f490040, 0x8f480024, 
+0x8f440180, 0x8f450184, 0x8f460024, 0x8f4b001c, 
+0x24070001, 0xafa70010, 0x84100, 0x1001821, 
+0x14c5021, 0x2529ffff, 0x1498024, 0xafb00014, 
+0x8f470014, 0x1021, 0x63100, 0xafa70018, 
+0xa32821, 0xa3382b, 0x822021, 0x872021, 
+0x8f420108, 0x1663021, 0x40f809, 0xc3900, 
+0x54400001, 0xaf500024, 0x8f430024, 0x8f420020, 
+0x14620018, 0x0, 0x8f420000, 0x10400007, 
+0x0, 0xaf80004c, 0x8f82004c, 0x1040fffd, 
+0x0, 0x10000005, 0x0, 0xaf800048, 
+0x8f820048, 0x1040fffd, 0x0, 0x8f820060, 
+0x2403ffef, 0x431024, 0xaf820060, 0x8f420000, 
+0x10400003, 0x0, 0x10000002, 0xaf80004c, 
+0xaf800048, 0x8fbf0024, 0x8fb00020, 0x3e00008, 
+0x27bd0028, 0x3e00008, 0x0, 0x27bdffc0, 
+0x32c20020, 0xafbf0038, 0xafb30034, 0xafb20030, 
+0xafb1002c, 0x10400004, 0xafb00028, 0x8f530028, 
+0x10000002, 0x0, 0x8f530020, 0x8f420030, 
+0x105300eb, 0x21100, 0x8f43001c, 0x628021, 
+0x8e040000, 0x8e050004, 0x96120008, 0x8f420090, 
+0x9611000a, 0x3246ffff, 0x46102a, 0x10400017, 
+0x0, 0x8f8200d8, 0x8f430098, 0x431023, 
+0x2442dcbe, 0xaf420090, 0x8f420090, 0x2842dcbf, 
+0x10400005, 0x0, 0x8f420090, 0x8f430144, 
+0x431021, 0xaf420090, 0x8f420090, 0x46102a, 
+0x10400006, 0x0, 0x8f420348, 0x24420001, 
+0xaf420348, 0x100000e1, 0x8f420348, 0x8f8200fc, 
+0x14400006, 0x0, 0x8f420344, 0x24420001, 
+0xaf420344, 0x100000d9, 0x8f420344, 0x934205c2, 
+0x1040000b, 0x32c20008, 0x10400008, 0x32220200, 
+0x10400006, 0x3c034000, 0x9602000e, 0xaf4300ac, 
+0x21400, 0x10000002, 0xaf4200b0, 0xaf4000ac, 
+0x32220004, 0x1040007f, 0x32220800, 0x10400003, 
+0x3247ffff, 0x10000002, 0x24020020, 0x24020004, 
+0xafa20010, 0x8f420030, 0xafa20014, 0x8f420010, 
+0x3c030002, 0x431025, 0xafa20018, 0x8f460098, 
+0x8f420108, 0x40f809, 0x0, 0x104000b7, 
+0x0, 0x8f42009c, 0x8f430094, 0x2421021, 
+0xaf42009c, 0xae03000c, 0x8f4200ac, 0x10400008, 
+0x3c034000, 0x8f420094, 0x431025, 0xafa20020, 
+0x8f42009c, 0x8f4300b0, 0x10000004, 0x431025, 
+0x8f420094, 0xafa20020, 0x8f42009c, 0xafa20024, 
+0x8f8200fc, 0x8fa30020, 0x8fa40024, 0xac430000, 
+0xac440004, 0x24420008, 0xaf8200f0, 0x8f42009c, 
+0x8f440270, 0x8f450274, 0x401821, 0x1021, 
+0xa32821, 0xa3302b, 0x822021, 0x862021, 
+0x32230060, 0x24020040, 0xaf440270, 0xaf450274, 
+0x10620017, 0x2c620041, 0x10400005, 0x24020020, 
+0x10620008, 0x24020001, 0x10000026, 0x0, 
+0x24020060, 0x10620019, 0x24020001, 0x10000021, 
+0x0, 0x8f420278, 0x8f43027c, 0x24630001, 
+0x2c640001, 0x441021, 0xaf420278, 0xaf43027c, 
+0x8f420278, 0x8f43027c, 0x10000016, 0x24020001, 
+0x8f420280, 0x8f430284, 0x24630001, 0x2c640001, 
+0x441021, 0xaf420280, 0xaf430284, 0x8f420280, 
+0x8f430284, 0x1000000b, 0x24020001, 0x8f420288, 
+0x8f43028c, 0x24630001, 0x2c640001, 0x441021, 
+0xaf420288, 0xaf43028c, 0x8f420288, 0x8f43028c, 
+0x24020001, 0xa34205c2, 0x8f420098, 0x3244ffff, 
+0x2406fff8, 0x8f45013c, 0x441021, 0x24420007, 
+0x461024, 0x24840007, 0xaf420094, 0x8f420090, 
+0x8f430094, 0x862024, 0x441023, 0x65182b, 
+0x14600005, 0xaf420090, 0x8f420094, 0x8f430144, 
+0x431023, 0xaf420094, 0x8f420094, 0x10000023, 
+0xaf40009c, 0x3247ffff, 0x50e00022, 0x32c20020, 
+0x14400002, 0x24020010, 0x24020002, 0xafa20010, 
+0x8f420030, 0xafa20014, 0x8f420010, 0xafa20018, 
+0x8f460098, 0x8f420108, 0x40f809, 0x0, 
+0x1040003a, 0x3245ffff, 0x8f420098, 0x8f430090, 
+0x8f46013c, 0x451021, 0xaf420098, 0x8f42009c, 
+0x8f440098, 0xa34005c2, 0x651823, 0xaf430090, 
+0x451021, 0x86202b, 0x14800005, 0xaf42009c, 
+0x8f420098, 0x8f430144, 0x431023, 0xaf420098, 
+0x32c20020, 0x10400005, 0x0, 0x8f420358, 
+0x2442ffff, 0xaf420358, 0x8f420358, 0x8f420030, 
+0x8f430040, 0x24420001, 0x2463ffff, 0x431024, 
+0xaf420030, 0x8f420030, 0x14530018, 0x0, 
+0x8f420000, 0x10400007, 0x0, 0xaf80004c, 
+0x8f82004c, 0x1040fffd, 0x0, 0x10000005, 
+0x0, 0xaf800048, 0x8f820048, 0x1040fffd, 
+0x0, 0x8f820060, 0x2403fff7, 0x431024, 
+0xaf820060, 0x8f420000, 0x10400003, 0x0, 
+0x10000002, 0xaf80004c, 0xaf800048, 0x8fbf0038, 
+0x8fb30034, 0x8fb20030, 0x8fb1002c, 0x8fb00028, 
+0x3e00008, 0x27bd0040, 0x3e00008, 0x0, 
+0x27bdffd0, 0x32c20020, 0xafbf002c, 0xafb20028, 
+0xafb10024, 0x10400004, 0xafb00020, 0x8f520028, 
+0x10000002, 0x0, 0x8f520020, 0x8f420030, 
+0x105200b5, 0x21100, 0x8f43001c, 0x628021, 
+0x8e040000, 0x8e050004, 0x96110008, 0x8f420090, 
+0x9607000a, 0x3226ffff, 0x46102a, 0x10400017, 
+0x0, 0x8f8200d8, 0x8f430098, 0x431023, 
+0x2442dc46, 0xaf420090, 0x8f420090, 0x2842dc47, 
+0x10400005, 0x0, 0x8f420090, 0x8f430144, 
+0x431021, 0xaf420090, 0x8f420090, 0x46102a, 
+0x10400006, 0x0, 0x8f420348, 0x24420001, 
+0xaf420348, 0x100000ab, 0x8f420348, 0x8f8600fc, 
+0x10c0000c, 0x0, 0x8f8200f4, 0x2403fff8, 
+0x431024, 0x461023, 0x218c3, 0x58600001, 
+0x24630100, 0x8f42008c, 0x43102b, 0x14400006, 
+0x712c2, 0x8f420344, 0x24420001, 0xaf420344, 
+0x10000098, 0x8f420344, 0x934305c2, 0x1060000f, 
+0x30460001, 0x8f420010, 0x34480400, 0x32c20008, 
+0x10400008, 0x30e20200, 0x10400006, 0x3c034000, 
+0x9602000e, 0xaf4300ac, 0x21400, 0x10000004, 
+0xaf4200b0, 0x10000002, 0xaf4000ac, 0x8f480010, 
+0x30e20004, 0x10400045, 0x3227ffff, 0x8f4900ac, 
+0x11200005, 0x30c200ff, 0x14400006, 0x24020040, 
+0x10000004, 0x24020008, 0x14400002, 0x24020020, 
+0x24020004, 0xafa20010, 0x8f430030, 0x11200004, 
+0xafa30014, 0x8f4200b0, 0x621025, 0xafa20014, 
+0x3c020002, 0x1021025, 0xafa20018, 0x8f460098, 
+0x8f420108, 0x40f809, 0x0, 0x10400069, 
+0x3224ffff, 0x8f42008c, 0x8f430094, 0x24420001, 
+0xaf42008c, 0x24020001, 0xae03000c, 0xa34205c2, 
+0x8f420098, 0x2406fff8, 0x8f45013c, 0x441021, 
+0x24420007, 0x461024, 0x24840007, 0xaf420094, 
+0x8f420090, 0x8f430094, 0x862024, 0x441023, 
+0x65182b, 0x14600005, 0xaf420090, 0x8f420094, 
+0x8f430144, 0x431023, 0xaf420094, 0x8f430094, 
+0x8f420140, 0x43102b, 0x10400009, 0x0, 
+0x8f43013c, 0x8f440094, 0x8f420090, 0x8f450138, 
+0x641823, 0x431023, 0xaf420090, 0xaf450094, 
+0x8f420094, 0x1000001f, 0xaf420098, 0x10e0001d, 
+0x30c200ff, 0x14400002, 0x24020010, 0x24020002, 
+0xafa20010, 0x8f420030, 0xafa80018, 0xafa20014, 
+0x8f460098, 0x8f420108, 0x40f809, 0x0, 
+0x10400030, 0x3225ffff, 0x8f420098, 0x8f44013c, 
+0x451021, 0xaf420098, 0x8f420090, 0x8f430098, 
+0xa34005c2, 0x451023, 0x64182b, 0x14600005, 
+0xaf420090, 0x8f420098, 0x8f430144, 0x431023, 
+0xaf420098, 0x8f420030, 0x8f430040, 0x24420001, 
+0x2463ffff, 0x431024, 0xaf420030, 0x8f420030, 
+0x14520018, 0x0, 0x8f420000, 0x10400007, 
+0x0, 0xaf80004c, 0x8f82004c, 0x1040fffd, 
+0x0, 0x10000005, 0x0, 0xaf800048, 
+0x8f820048, 0x1040fffd, 0x0, 0x8f820060, 
+0x2403fff7, 0x431024, 0xaf820060, 0x8f420000, 
+0x10400003, 0x0, 0x10000002, 0xaf80004c, 
+0xaf800048, 0x8fbf002c, 0x8fb20028, 0x8fb10024, 
+0x8fb00020, 0x3e00008, 0x27bd0030, 0x3e00008, 
+0x0, 0x27bdffd8, 0x3c020001, 0x34422ec0, 
+0xafbf0020, 0x8f4300f0, 0x8f840108, 0x2e21021, 
+0x54620004, 0x24620008, 0x3c020001, 0x34422cc0, 
+0x2e21021, 0x401821, 0xaf4300f0, 0xac600000, 
+0x8f4200ec, 0x8c660004, 0x14620004, 0x3c020001, 
+0x24820020, 0x1000000f, 0xaf820108, 0x8f4300f0, 
+0x34422ec0, 0x2e21021, 0x54620004, 0x24620008, 
+0x3c020001, 0x34422cc0, 0x2e21021, 0x401821, 
+0x8c620004, 0x21140, 0x821021, 0xaf820108, 
+0xac600000, 0x8c850018, 0x30a20036, 0x1040006c, 
+0x30a20001, 0x8c82001c, 0x8f430040, 0x8f440034, 
+0x24420001, 0x2463ffff, 0x431024, 0x862021, 
+0xaf42002c, 0x30a20030, 0x14400006, 0xaf440034, 
+0x8f420034, 0x8c03023c, 0x43102b, 0x144000b4, 
+0x0, 0x32c20010, 0x10400028, 0x24070008, 
+0x8f440170, 0x8f450174, 0x8f43002c, 0x8f48000c, 
+0x8f860120, 0x24020080, 0xafa20010, 0xafa30014, 
+0xafa80018, 0x8f42010c, 0x40f809, 0x24c6001c, 
+0x14400011, 0x24020001, 0x3c010001, 0x370821, 
+0xa02240f1, 0x8f820124, 0xafa20010, 0x8f820128, 
+0x3c040001, 0x248467c4, 0xafa20014, 0x8f46002c, 
+0x8f870120, 0x3c050009, 0xc002b3b, 0x34a51100, 
+0x10000036, 0x0, 0x8f420300, 0x8f43002c, 
+0x24420001, 0xaf420300, 0x8f420300, 0x24020001, 
+0xa34205c1, 0x10000026, 0xaf430038, 0x8f440170, 
+0x8f450174, 0x8f43002c, 0x8f48000c, 0x8f860120, 
+0x24020020, 0xafa20010, 0xafa30014, 0xafa80018, 
+0x8f42010c, 0x40f809, 0x24c6001c, 0x14400011, 
+0x24020001, 0x3c010001, 0x370821, 0xa02240f0, 
+0x8f820124, 0xafa20010, 0x8f820128, 0x3c040001, 
+0x248467b8, 0xafa20014, 0x8f46002c, 0x8f870120, 
+0x3c050009, 0xc002b3b, 0x34a50900, 0x1000000f, 
+0x0, 0x8f420300, 0x24420001, 0xaf420300, 
+0x8f420300, 0x8f42002c, 0xa34005c1, 0xaf420038, 
+0x3c010001, 0x370821, 0xa02040f1, 0x3c010001, 
+0x370821, 0xa02040f0, 0xaf400034, 0x8f420314, 
+0x24420001, 0xaf420314, 0x10000059, 0x8f420314, 
+0x10400022, 0x30a27000, 0x8c85001c, 0x8f420028, 
+0xa22023, 0x4810003, 0x0, 0x8f420040, 
+0x822021, 0x8f420358, 0x8f430000, 0xaf450028, 
+0x441021, 0x10600007, 0xaf420358, 0xaf80004c, 
+0x8f82004c, 0x1040fffd, 0x0, 0x10000005, 
+0x0, 0xaf800048, 0x8f820048, 0x1040fffd, 
+0x0, 0x8f820060, 0x34420008, 0xaf820060, 
+0x8f420000, 0x10400003, 0x0, 0x10000038, 
+0xaf80004c, 0x10000036, 0xaf800048, 0x1040002f, 
+0x30a21000, 0x1040000c, 0x30a24000, 0x8c83001c, 
+0x8f420050, 0x622023, 0x4820001, 0x24840200, 
+0x8f42035c, 0x441021, 0xaf42035c, 0x8f420368, 
+0x1000001a, 0xaf430050, 0x1040000c, 0x32c28000, 
+0x8c83001c, 0x8f420070, 0x622023, 0x4820001, 
+0x24840400, 0x8f420364, 0x441021, 0xaf420364, 
+0x8f420368, 0x1000000d, 0xaf430070, 0x1040000e, 
+0x3c020800, 0x8c83001c, 0x8f420060, 0x622023, 
+0x4820001, 0x24840100, 0x8f420360, 0x441021, 
+0xaf420360, 0x8f420368, 0xaf430060, 0x441021, 
+0xaf420368, 0x3c020800, 0x2c21024, 0x50400008, 
+0x36940040, 0x10000006, 0x0, 0x30a20100, 
+0x10400003, 0x0, 0xc002bd8, 0x0, 
+0x8fbf0020, 0x3e00008, 0x27bd0028, 0x3e00008, 
+0x0, 0x27bdffa8, 0xafbf0050, 0xafbe004c, 
+0xafb50048, 0xafb30044, 0xafb20040, 0xafb1003c, 
+0xafb00038, 0x8f910108, 0x26220020, 0xaf820108, 
+0x8e320018, 0xa821, 0x32420024, 0x104001ba, 
+0xf021, 0x8e26001c, 0x8f43001c, 0x61100, 
+0x621821, 0x8c70000c, 0x9604000c, 0x962d0016, 
+0x9473000a, 0x2c8305dd, 0x38828870, 0x2c420001, 
+0x621825, 0x10600015, 0x2821, 0x32c20040, 
+0x10400015, 0x24020800, 0x96030014, 0x14620012, 
+0x3402aaaa, 0x9603000e, 0x14620007, 0x2021, 
+0x96030010, 0x24020300, 0x14620004, 0x801021, 
+0x96020012, 0x2c440001, 0x801021, 0x54400006, 
+0x24050016, 0x10000004, 0x0, 0x24020800, 
+0x50820001, 0x2405000e, 0x934205c3, 0x14400008, 
+0x5821, 0x240b0001, 0x32620180, 0xaf4500a8, 
+0xaf5000a0, 0x10400002, 0xaf4600a4, 0xa34b05c3, 
+0x10a00085, 0x2054021, 0x91020000, 0x3821, 
+0x3042000f, 0x25080, 0x32c20002, 0x10400012, 
+0x10a1821, 0x32620002, 0x10400010, 0x32c20001, 
+0x1002021, 0x94820000, 0x24840002, 0xe23821, 
+0x83102b, 0x1440fffb, 0x30e2ffff, 0x71c02, 
+0x623821, 0x71c02, 0x30e2ffff, 0x623821, 
+0x71027, 0xa502000a, 0x32c20001, 0x1040006a, 
+0x32620001, 0x10400068, 0x0, 0x8f4200a8, 
+0x10400065, 0x0, 0x8f4200a0, 0x8f4300a8, 
+0x431021, 0x904c0009, 0x318900ff, 0x39230006, 
+0x3182b, 0x39220011, 0x2102b, 0x621824, 
+0x1060000c, 0x3c050006, 0x8f4200a4, 0x3c040001, 
+0x248467d4, 0xafa20010, 0x8f4200a0, 0x34a54600, 
+0x1203821, 0xc002b3b, 0xafa20014, 0x1000004e, 
+0x0, 0x32c20004, 0x14400013, 0x2821, 
+0x316200ff, 0x14400004, 0x0, 0x95020002, 
+0x1000000d, 0x4a2823, 0x9505000c, 0x9502000e, 
+0x95030010, 0xa22821, 0xa32821, 0x95030012, 
+0x91040009, 0x95020002, 0xa32821, 0xa42821, 
+0x4a1023, 0xa22821, 0x2002021, 0x94820000, 
+0x24840002, 0xe23821, 0x88102b, 0x1440fffb, 
+0x71c02, 0x30e2ffff, 0x623821, 0x71c02, 
+0x30e2ffff, 0x623821, 0x1a52821, 0x51c02, 
+0x30a2ffff, 0x622821, 0x51c02, 0x30a2ffff, 
+0x622821, 0xa72823, 0x51402, 0xa22821, 
+0x30a5ffff, 0x50a00001, 0x3405ffff, 0x316200ff, 
+0x14400008, 0x318300ff, 0x8f4300a0, 0x8f4200a8, 
+0x624021, 0x91020000, 0x3042000f, 0x25080, 
+0x318300ff, 0x24020006, 0x14620003, 0x10a1021, 
+0x10000002, 0x24440010, 0x24440006, 0x316200ff, 
+0x14400006, 0x0, 0x94820000, 0xa22821, 
+0x51c02, 0x30a2ffff, 0x622821, 0x934205c3, 
+0x10400003, 0x32620100, 0x50400003, 0xa4850000, 
+0x52827, 0xa4850000, 0x9622000e, 0x8f43009c, 
+0x621821, 0x32a200ff, 0x10400007, 0xaf43009c, 
+0x3c024000, 0x2021025, 0xafa20020, 0x8f42009c, 
+0x10000003, 0x5e1025, 0xafb00020, 0x8f42009c, 
+0xafa20024, 0x32620080, 0x10400010, 0x32620100, 
+0x8f4200b4, 0x24430001, 0x210c0, 0x571021, 
+0xaf4300b4, 0x8fa30020, 0x8fa40024, 0x3c010001, 
+0x220821, 0xac2338e8, 0x3c010001, 0x220821, 
+0xac2438ec, 0x100000a5, 0x32c20020, 0x10400064, 
+0x0, 0x8f4200b4, 0x24430001, 0x210c0, 
+0x571021, 0xaf4300b4, 0x8fa30020, 0x8fa40024, 
+0x3c010001, 0x220821, 0xac2338e8, 0x3c010001, 
+0x220821, 0xac2438ec, 0x8f4200b4, 0x10400051, 
+0x3821, 0x3c090001, 0x352938e8, 0x3c08001f, 
+0x3508ffff, 0x240bffff, 0x340affff, 0x710c0, 
+0x571021, 0x491021, 0x8c430000, 0x8c440004, 
+0xafa30028, 0xafa4002c, 0x8f8200fc, 0x8fa30028, 
+0x8fa4002c, 0xac430000, 0xac440004, 0x24420008, 
+0xaf8200f0, 0x8f42008c, 0x2442ffff, 0xaf42008c, 
+0x97a2002e, 0x8f440270, 0x8f450274, 0x401821, 
+0x1021, 0xa32821, 0xa3302b, 0x822021, 
+0x862021, 0xaf440270, 0xaf450274, 0x8fa20028, 
+0x481024, 0x90430000, 0x30630001, 0x1460000b, 
+0x402021, 0x8f420278, 0x8f43027c, 0x24630001, 
+0x2c640001, 0x441021, 0xaf420278, 0xaf43027c, 
+0x8f420278, 0x1000001a, 0x8f43027c, 0x8c820000, 
+0x144b000e, 0x0, 0x94820004, 0x144a000b, 
+0x0, 0x8f420288, 0x8f43028c, 0x24630001, 
+0x2c640001, 0x441021, 0xaf420288, 0xaf43028c, 
+0x8f420288, 0x1000000a, 0x8f43028c, 0x8f420280, 
+0x8f430284, 0x24630001, 0x2c640001, 0x441021, 
+0xaf420280, 0xaf430284, 0x8f420280, 0x8f430284, 
+0x8f4200b4, 0x24e70001, 0xe2102b, 0x1440ffb8, 
+0x710c0, 0xa34005c3, 0x1000003f, 0xaf4000b4, 
+0x8f8200fc, 0x8fa30020, 0x8fa40024, 0xac430000, 
+0xac440004, 0x24420008, 0xaf8200f0, 0x8f42009c, 
+0x8f46008c, 0x8f440270, 0x8f450274, 0x401821, 
+0x1021, 0x24c6ffff, 0xaf46008c, 0xa32821, 
+0xa3302b, 0x822021, 0x862021, 0xaf440270, 
+0xaf450274, 0x92020000, 0x30420001, 0x1440000c, 
+0x2402ffff, 0x8f420278, 0x8f43027c, 0x24630001, 
+0x2c640001, 0x441021, 0xaf420278, 0xaf43027c, 
+0x8f420278, 0x8f43027c, 0x1000001c, 0x32c20020, 
+0x8e030000, 0x1462000f, 0x3402ffff, 0x96030004, 
+0x1462000c, 0x0, 0x8f420288, 0x8f43028c, 
+0x24630001, 0x2c640001, 0x441021, 0xaf420288, 
+0xaf43028c, 0x8f420288, 0x8f43028c, 0x1000000b, 
+0x32c20020, 0x8f420280, 0x8f430284, 0x24630001, 
+0x2c640001, 0x441021, 0xaf420280, 0xaf430284, 
+0x8f420280, 0x8f430284, 0x32c20020, 0x10400005, 
+0xaf40009c, 0x8f420358, 0x2442ffff, 0xaf420358, 
+0x8f420358, 0x8e22001c, 0x8f430040, 0x24420001, 
+0x2463ffff, 0x431024, 0xaf42002c, 0x32420060, 
+0x14400008, 0x32c20010, 0x8f420034, 0x24420001, 
+0xaf420034, 0x8c03023c, 0x43102b, 0x14400102, 
+0x32c20010, 0x10400018, 0x24070008, 0x8f440170, 
+0x8f450174, 0x8f43002c, 0x8f48000c, 0x8f860120, 
+0x24020080, 0xafa20010, 0xafa30014, 0xafa80018, 
+0x8f42010c, 0x40f809, 0x24c6001c, 0x10400047, 
+0x24020001, 0x8f420300, 0x8f43002c, 0x24420001, 
+0xaf420300, 0x8f420300, 0x24020001, 0xa34205c1, 
+0x1000007c, 0xaf430038, 0x8f440170, 0x8f450174, 
+0x8f43002c, 0x8f48000c, 0x8f860120, 0x24020020, 
+0xafa20010, 0xafa30014, 0xafa80018, 0x8f42010c, 
+0x40f809, 0x24c6001c, 0x10400057, 0x24020001, 
+0x10000065, 0x0, 0x32420012, 0x10400075, 
+0x32420001, 0x9622000e, 0x8f43009c, 0x621821, 
+0x32c20020, 0x10400005, 0xaf43009c, 0x8f420358, 
+0x2442ffff, 0xaf420358, 0x8f420358, 0x8e22001c, 
+0x8f430040, 0x24420001, 0x2463ffff, 0x431024, 
+0xaf42002c, 0x32420010, 0x14400008, 0x32c20010, 
+0x8f420034, 0x24420001, 0xaf420034, 0x8c03023c, 
+0x43102b, 0x144000bc, 0x32c20010, 0x10400028, 
+0x24070008, 0x8f440170, 0x8f450174, 0x8f43002c, 
+0x8f48000c, 0x8f860120, 0x24020080, 0xafa20010, 
+0xafa30014, 0xafa80018, 0x8f42010c, 0x40f809, 
+0x24c6001c, 0x14400011, 0x24020001, 0x3c010001, 
+0x370821, 0xa02240f1, 0x8f820124, 0xafa20010, 
+0x8f820128, 0x3c040001, 0x248467c4, 0xafa20014, 
+0x8f46002c, 0x8f870120, 0x3c050009, 0xc002b3b, 
+0x34a51100, 0x10000036, 0x0, 0x8f420300, 
+0x8f43002c, 0x24420001, 0xaf420300, 0x8f420300, 
+0x24020001, 0xa34205c1, 0x10000026, 0xaf430038, 
+0x8f440170, 0x8f450174, 0x8f43002c, 0x8f48000c, 
+0x8f860120, 0x24020020, 0xafa20010, 0xafa30014, 
+0xafa80018, 0x8f42010c, 0x40f809, 0x24c6001c, 
+0x14400011, 0x24020001, 0x3c010001, 0x370821, 
+0xa02240f0, 0x8f820124, 0xafa20010, 0x8f820128, 
+0x3c040001, 0x248467b8, 0xafa20014, 0x8f46002c, 
+0x8f870120, 0x3c050009, 0xc002b3b, 0x34a50900, 
+0x1000000f, 0x0, 0x8f420300, 0x24420001, 
+0xaf420300, 0x8f420300, 0x8f42002c, 0xa34005c1, 
+0xaf420038, 0x3c010001, 0x370821, 0xa02040f1, 
+0x3c010001, 0x370821, 0xa02040f0, 0xaf400034, 
+0x8f420314, 0x24420001, 0xaf420314, 0x10000062, 
+0x8f420314, 0x10400022, 0x32427000, 0x8e25001c, 
+0x8f420028, 0xa22023, 0x4810003, 0x0, 
+0x8f420040, 0x822021, 0x8f420358, 0x8f430000, 
+0xaf450028, 0x441021, 0x10600007, 0xaf420358, 
+0xaf80004c, 0x8f82004c, 0x1040fffd, 0x0, 
+0x10000005, 0x0, 0xaf800048, 0x8f820048, 
+0x1040fffd, 0x0, 0x8f820060, 0x34420008, 
+0xaf820060, 0x8f420000, 0x10400003, 0x0, 
+0x10000041, 0xaf80004c, 0x1000003f, 0xaf800048, 
+0x1040002f, 0x32421000, 0x1040000c, 0x32424000, 
+0x8e23001c, 0x8f420050, 0x622023, 0x4820001, 
+0x24840200, 0x8f42035c, 0x441021, 0xaf42035c, 
+0x8f420368, 0x1000001a, 0xaf430050, 0x1040000c, 
+0x32c28000, 0x8e23001c, 0x8f420070, 0x622023, 
+0x4820001, 0x24840400, 0x8f420364, 0x441021, 
+0xaf420364, 0x8f420368, 0x1000000d, 0xaf430070, 
+0x1040000e, 0x3c020800, 0x8e23001c, 0x8f420060, 
+0x622023, 0x4820001, 0x24840100, 0x8f420360, 
+0x441021, 0xaf420360, 0x8f420368, 0xaf430060, 
+0x441021, 0xaf420368, 0x3c020800, 0x2c21024, 
+0x50400011, 0x36940040, 0x1000000f, 0x0, 
+0x32420048, 0x10400007, 0x24150001, 0x8e22001c, 
+0x3c03ffff, 0x43f024, 0x3042ffff, 0x1000fd75, 
+0xae22001c, 0x32420100, 0x10400003, 0x0, 
+0xc002bd8, 0x0, 0x8fbf0050, 0x8fbe004c, 
+0x8fb50048, 0x8fb30044, 0x8fb20040, 0x8fb1003c, 
+0x8fb00038, 0x3e00008, 0x27bd0058, 0x3e00008, 
+0x0, 0x0, 0x0, 0x8f8300e4, 
+0x8f8200e0, 0x2404fff8, 0x441024, 0x621026, 
+0x2102b, 0x21023, 0x3e00008, 0x621024, 
+0x3e00008, 0x0, 0x27bdffe0, 0xafbf001c, 
+0xafb00018, 0x8f8600c4, 0x8f8400e0, 0x8f8500e4, 
+0x2402fff8, 0x821824, 0x10a30009, 0x27623ff8, 
+0x14a20002, 0x24a20008, 0x27623000, 0x408021, 
+0x16030005, 0x30820004, 0x10400004, 0xc02021, 
+0x10000022, 0x1021, 0x8e040000, 0x8f42011c, 
+0x14a20003, 0x0, 0x8f420120, 0xaf420114, 
+0x8ca30000, 0x8f420148, 0x831823, 0x43102b, 
+0x10400003, 0x0, 0x8f420148, 0x621821, 
+0x94a20006, 0x24420050, 0x62102b, 0x1440000f, 
+0xa01021, 0xafa40010, 0xafa30014, 0x8ca60000, 
+0x8ca70004, 0x3c040001, 0xc002b3b, 0x24846894, 
+0x8f42020c, 0x24420001, 0xaf42020c, 0x8f42020c, 
+0x1021, 0xaf9000e8, 0xaf9000e4, 0x8fbf001c, 
+0x8fb00018, 0x3e00008, 0x27bd0020, 0x3e00008, 
+0x0, 0x8f8400e0, 0x8f8800c4, 0x8f8300e8, 
+0x2402fff8, 0x823824, 0xe32023, 0x2c821000, 
+0x50400001, 0x24841000, 0x420c2, 0x801821, 
+0x8f440258, 0x8f45025c, 0x1021, 0xa32821, 
+0xa3302b, 0x822021, 0x862021, 0xaf440258, 
+0xaf45025c, 0x8f8300c8, 0x8f420148, 0x1032023, 
+0x82102b, 0x14400004, 0x801821, 0x8f420148, 
+0x822021, 0x801821, 0x8f440250, 0x8f450254, 
+0x1021, 0xa32821, 0xa3302b, 0x822021, 
+0x862021, 0xaf440250, 0xaf450254, 0xaf8800c8, 
+0xaf8700e4, 0xaf8700e8, 0x3e00008, 0x0, 
+0x27bdff30, 0x240a0001, 0xafbf00c8, 0xafbe00c4, 
+0xafb500c0, 0xafb300bc, 0xafb200b8, 0xafb100b4, 
+0xafb000b0, 0xa3a00097, 0xafa00044, 0xafaa005c, 
+0x934205c4, 0xa7a0008e, 0x1040000a, 0xa7a00086, 
+0x8f4b00c4, 0xafab0064, 0x8f4a00c0, 0xafaa006c, 
+0x8f4b00cc, 0xafab0074, 0x8f4a00c8, 0x10000129, 
+0xafaa007c, 0x8f420114, 0x40f809, 0x0, 
+0x403021, 0x10c0034f, 0x0, 0x8cc20000, 
+0x8cc30004, 0xafa20020, 0xafa30024, 0x8fab0024, 
+0x8faa0020, 0x3162ffff, 0x2442fffc, 0xafa2006c, 
+0x3c020006, 0x2c21024, 0xafab007c, 0x14400015, 
+0xafaa0064, 0x91420000, 0x30420001, 0x10400011, 
+0x2402ffff, 0x8d430000, 0x14620004, 0x3402ffff, 
+0x95430004, 0x1062000b, 0x0, 0xc0024bb, 
+0x8fa40064, 0x304200ff, 0x14400006, 0x0, 
+0x8f420118, 0x40f809, 0x0, 0x1000032d, 
+0x0, 0x8fa20024, 0x3c03ffbf, 0x3463ffff, 
+0x431024, 0x3c03ffff, 0x431824, 0x14600003, 
+0xafa20024, 0x10000040, 0x1821, 0x3c020080, 
+0x621024, 0x10400007, 0x0, 0x8f42038c, 
+0x24420001, 0xaf42038c, 0x8f42038c, 0x10000036, 
+0x24030001, 0x8f420210, 0x24420001, 0xaf420210, 
+0x8f420210, 0x3c020001, 0x621024, 0x10400006, 
+0x3c020002, 0x8f4201c4, 0x24420001, 0xaf4201c4, 
+0x8f4201c4, 0x3c020002, 0x621024, 0x10400006, 
+0x3c020004, 0x8f42037c, 0x24420001, 0xaf42037c, 
+0x8f42037c, 0x3c020004, 0x621024, 0x10400006, 
+0x3c020008, 0x8f420380, 0x24420001, 0xaf420380, 
+0x8f420380, 0x3c020008, 0x621024, 0x10400006, 
+0x3c020010, 0x8f420384, 0x24420001, 0xaf420384, 
+0x8f420384, 0x3c020010, 0x621024, 0x10400006, 
+0x3c020020, 0x8f4201c0, 0x24420001, 0xaf4201c0, 
+0x8f4201c0, 0x3c020020, 0x621024, 0x10400006, 
+0x24030001, 0x8f420388, 0x24420001, 0xaf420388, 
+0x8f420388, 0x24030001, 0x8c020260, 0x8fab006c, 
+0x4b102b, 0x10400014, 0x307000ff, 0x8f4201e8, 
+0x24420001, 0xaf4201e8, 0x8f4201e8, 0x8faa007c, 
+0x8f8200e0, 0x354a0100, 0xafaa007c, 0xafa20010, 
+0x8f8200e4, 0x24100001, 0x3c040001, 0x248468a0, 
+0xafa20014, 0x8fa60020, 0x8fa70024, 0x3c050007, 
+0xc002b3b, 0x34a50800, 0x12000010, 0x3c020080, 
+0x2c21024, 0x1440000e, 0x32c20400, 0x8fab007c, 
+0x3c020080, 0x34420100, 0x1621024, 0x10400005, 
+0x0, 0x8f42020c, 0x24420001, 0xaf42020c, 
+0x8f42020c, 0x100002b0, 0x8fa3006c, 0x32c20400, 
+0x10400015, 0x34028100, 0x8faa0064, 0x9543000c, 
+0x14620012, 0x3c020100, 0x240b0200, 0xa7ab008e, 
+0x9542000e, 0x8d430008, 0x8d440004, 0x8d450000, 
+0x8faa006c, 0x8fab0064, 0x254afffc, 0xafaa006c, 
+0xa7a20086, 0xad63000c, 0xad640008, 0xad650004, 
+0x256b0004, 0xafab0064, 0x3c020100, 0x2c21024, 
+0x10400004, 0x0, 0x8faa006c, 0x254a0004, 
+0xafaa006c, 0x8f4200bc, 0x5040000a, 0xafa00074, 
+0x8fab006c, 0x4b102b, 0x50400006, 0xafa00074, 
+0x8f4200bc, 0x1621023, 0xafa20074, 0x8f4a00bc, 
+0xafaa006c, 0x8f420080, 0x8fab006c, 0x4b102b, 
+0x10400056, 0x32c28000, 0x1040005e, 0x240a0003, 
+0x32c21000, 0x1040005b, 0xafaa005c, 0x10000058, 
+0x240b0004, 0x8f420350, 0x2403ffbf, 0x283a024, 
+0x24420001, 0xaf420350, 0x1000024f, 0x8f420350, 
+0x2c2b025, 0x2402ffbf, 0x282a024, 0x8f830128, 
+0x3c040001, 0x248468d0, 0x26620001, 0xafa20014, 
+0xafa30010, 0x8f860120, 0x8f870124, 0x3c050007, 
+0xc002b3b, 0x34a52250, 0x1000023f, 0x0, 
+0x2c2b025, 0x2402ffbf, 0x282a024, 0x8f830128, 
+0x3c040001, 0x248468d0, 0x24020002, 0xafa20014, 
+0xafa30010, 0x8f860120, 0x8f870124, 0x3c050007, 
+0xc002b3b, 0x34a52450, 0x1000022f, 0x0, 
+0x8ea20000, 0x8ea30004, 0x3c040001, 0x248468e8, 
+0xafb00010, 0xafbe0014, 0x8ea70018, 0x34a52800, 
+0xc002b3b, 0x603021, 0x10000223, 0x0, 
+0xa6b1000a, 0x8f820124, 0x3c040001, 0x248468f0, 
+0xafbe0014, 0xafa20010, 0x8f460044, 0x8f870120, 
+0x3c050007, 0xc002b3b, 0x34a53000, 0x10000216, 
+0x0, 0xa6b1000a, 0xa6b2000e, 0x8f820124, 
+0x3c040001, 0x248468fc, 0xafbe0014, 0xafa20010, 
+0x8f460044, 0x8f870120, 0x3c050007, 0xc002b3b, 
+0x34a53200, 0x10000208, 0x0, 0x8f420084, 
+0x8faa006c, 0x4a102b, 0x14400007, 0x3c020001, 
+0x2c21024, 0x10400004, 0x0, 0x240b0002, 
+0xafab005c, 0x8faa006c, 0x1140021b, 0x27ab0020, 
+0xafab00a4, 0x3c0a001f, 0x354affff, 0xafaa009c, 
+0x8fab005c, 0x240a0001, 0x556a0021, 0x240a0002, 
+0x8f430054, 0x8f420050, 0x1062000b, 0x274b0054, 
+0x8f5e0054, 0x3403ecc0, 0xafab004c, 0x27c20001, 
+0x304201ff, 0xafa20054, 0x1e1140, 0x431021, 
+0x1000006b, 0x2e2a821, 0x8f420044, 0x8faa006c, 
+0x3c040001, 0x248468ac, 0xafaa0014, 0xafa20010, 
+0x8f460054, 0x8f470050, 0x3c050007, 0xc002b3b, 
+0x34a51300, 0x8f430350, 0x2402ffbf, 0x282a024, 
+0x24630001, 0xaf430350, 0x100001d3, 0x8f420350, 
+0x156a001d, 0x0, 0x8f430074, 0x8f420070, 
+0x1062000a, 0x274b0074, 0x8f5e0074, 0xafab004c, 
+0x27c20001, 0x304203ff, 0xafa20054, 0x1e1140, 
+0x24426cc0, 0x1000004a, 0x2e2a821, 0x8f420044, 
+0x8faa006c, 0x3c040001, 0x248468b8, 0x3c050007, 
+0xafaa0014, 0xafa20010, 0x8f460074, 0x8f470070, 
+0x34a51500, 0x240b0001, 0xc002b3b, 0xafab005c, 
+0x1000ffc3, 0x0, 0x8f430064, 0x8f420060, 
+0x1062001a, 0x274a0064, 0x8f5e0064, 0x8fab005c, 
+0xafaa004c, 0x27c20001, 0x304200ff, 0xafa20054, 
+0x24020004, 0x1562000e, 0x1e1140, 0x1e1180, 
+0x24420cc0, 0x2e21021, 0xafa20044, 0x9442002a, 
+0x8faa0044, 0x8fab006c, 0x4b102b, 0x10400024, 
+0x25550020, 0x240a0001, 0x10000021, 0xa3aa0097, 
+0x24424cc0, 0x1000001e, 0x2e2a821, 0x8f420044, 
+0x8fab006c, 0x3c040001, 0x248468c4, 0xafab0014, 
+0xafa20010, 0x8f460064, 0x8f470060, 0x3c050007, 
+0xc002b3b, 0x34a51800, 0x3c020008, 0x2c21024, 
+0x1440ff34, 0x0, 0x8f420370, 0x240a0001, 
+0xafaa005c, 0x24420001, 0xaf420370, 0x1000ff90, 
+0x8f420370, 0x27a30036, 0x131040, 0x621821, 
+0x94620000, 0x441021, 0x10000020, 0xa4620000, 
+0x8fab0064, 0xaeab0018, 0x93a20097, 0x10400072, 
+0x9821, 0x8faa0044, 0x8fa4006c, 0x8fa300a4, 
+0x25420020, 0xafa20028, 0x25420008, 0xafa20030, 
+0x25420010, 0xafaa002c, 0xafa20034, 0x9542002a, 
+0xa7a20038, 0x95420018, 0xa7a2003a, 0x9542001a, 
+0xa7a2003c, 0x9542001c, 0xa7a2003e, 0x94620018, 
+0x24630002, 0x822023, 0x1880ffde, 0x26730001, 
+0x2e620004, 0x1440fff9, 0x0, 0x8f4200fc, 
+0x26650001, 0xa2102a, 0x1440002b, 0x24030001, 
+0x8f83012c, 0x10600023, 0x0, 0x8f820124, 
+0x431023, 0x22143, 0x58800001, 0x24840040, 
+0x8f820128, 0x431023, 0x21943, 0x58600001, 
+0x24630040, 0x64102a, 0x54400001, 0x602021, 
+0xaf4400fc, 0x8f4200fc, 0xa2102a, 0x10400011, 
+0x24030001, 0x10000015, 0x306200ff, 0x8fab0064, 
+0x96070018, 0xafab0010, 0x8e220008, 0x3c040001, 
+0x248468dc, 0x8c430004, 0x8c420000, 0x34a52400, 
+0x2403021, 0xc002b3b, 0xafa30014, 0x1000002b, 
+0x0, 0x8f420334, 0x1821, 0x24420001, 
+0xaf420334, 0x8f420334, 0x306200ff, 0x5040fedc, 
+0x3c020800, 0x12600021, 0x9021, 0x8fb100a4, 
+0x2208021, 0x8e220008, 0x96070018, 0x8fa60064, 
+0x8c440000, 0x8c450004, 0x240a0001, 0xafaa0010, 
+0xafbe0014, 0x8f420008, 0xafa20018, 0x8f42010c, 
+0x40f809, 0x0, 0x1040ffd8, 0x3c050007, 
+0x96020018, 0x8fab0064, 0x8faa009c, 0x1625821, 
+0x14b102b, 0x10400004, 0xafab0064, 0x8f420148, 
+0x1625823, 0xafab0064, 0x26100002, 0x26520001, 
+0x253102b, 0x1440ffe3, 0x26310004, 0x8fb0006c, 
+0x10000036, 0x97b10038, 0x8f4200fc, 0x24050002, 
+0xa2102a, 0x1440001b, 0x24030001, 0x8f83012c, 
+0x10600013, 0x0, 0x8f820124, 0x431023, 
+0x22143, 0x58800001, 0x24840040, 0x8f820128, 
+0x431023, 0x21943, 0x58600001, 0x24630040, 
+0x64102a, 0x54400001, 0x602021, 0xaf4400fc, 
+0x8f4200fc, 0xa2102a, 0x14400006, 0x24030001, 
+0x8f420334, 0x1821, 0x24420001, 0xaf420334, 
+0x8f420334, 0x306200ff, 0x1040fea5, 0x3c020800, 
+0x96b1000a, 0x8fb0006c, 0x3223ffff, 0x70102b, 
+0x54400001, 0x608021, 0x8ea40000, 0x8ea50004, 
+0x240b0001, 0xafab0010, 0xafbe0014, 0x8f420008, 
+0x8fa60064, 0xafa20018, 0x8f42010c, 0x40f809, 
+0x2003821, 0x1040fea2, 0x3c050007, 0x96a3000e, 
+0x97aa008e, 0x11400007, 0x609021, 0x934205c4, 
+0x14400004, 0x0, 0x97ab0086, 0x6a1825, 
+0xa6ab0016, 0x8faa007c, 0x3c02ffff, 0x1421024, 
+0x10400003, 0xa1402, 0x34630400, 0xa6a20014, 
+0x8fab006c, 0x560b0072, 0xa6a3000e, 0x34620004, 
+0xa6a2000e, 0x8faa0074, 0x16a1021, 0xa6a2000a, 
+0x8f430044, 0x8f4401a0, 0x8f4501a4, 0x34028000, 
+0xafa20010, 0x8f420044, 0x2a03021, 0x24070020, 
+0xafa20014, 0x8f42000c, 0x31940, 0x604821, 
+0xafa20018, 0x8f42010c, 0x4021, 0xa92821, 
+0xa9182b, 0x882021, 0x40f809, 0x832021, 
+0x5040fe7f, 0xa6b2000e, 0x8f420368, 0xafa0006c, 
+0xa34005c4, 0x2442ffff, 0xaf420368, 0x8fab005c, 
+0x240a0001, 0x8f420368, 0x156a0006, 0x240a0002, 
+0x8f42035c, 0x2442ffff, 0xaf42035c, 0x1000000c, 
+0x8f42035c, 0x156a0006, 0x0, 0x8f420364, 
+0x2442ffff, 0xaf420364, 0x10000005, 0x8f420364, 
+0x8f420360, 0x2442ffff, 0xaf420360, 0x8f420360, 
+0x8faa0054, 0x8fab004c, 0xad6a0000, 0x8f420044, 
+0x8f440088, 0x8f430078, 0x24420001, 0x441024, 
+0x24630001, 0xaf420044, 0xaf430078, 0x8c020240, 
+0x62182b, 0x14600075, 0x24070008, 0x8f440168, 
+0x8f45016c, 0x8f430044, 0x8f48000c, 0x8f860120, 
+0x24020040, 0xafa20010, 0xafa30014, 0xafa80018, 
+0x8f42010c, 0x40f809, 0x24c6001c, 0x14400011, 
+0x240b0001, 0x3c010001, 0x370821, 0xa02b40f2, 
+0x8f820124, 0xafa20010, 0x8f820128, 0x3c040001, 
+0x2484688c, 0xafa20014, 0x8f460044, 0x8f870120, 
+0x3c050009, 0xc002b3b, 0x34a51300, 0x1000000b, 
+0x0, 0x8f420304, 0x24420001, 0xaf420304, 
+0x8f420304, 0x8f420044, 0xaf42007c, 0x3c010001, 
+0x370821, 0xa02040f2, 0xaf400078, 0x8f420318, 
+0x24420001, 0xaf420318, 0x10000048, 0x8f420318, 
+0xa6b0000a, 0x8f430044, 0x8f4401a0, 0x8f4501a4, 
+0x34028000, 0xafa20010, 0x8f420044, 0x2a03021, 
+0x24070020, 0xafa20014, 0x8f42000c, 0x31940, 
+0x604821, 0xafa20018, 0x8f42010c, 0x4021, 
+0xa92821, 0xa9182b, 0x882021, 0x40f809, 
+0x832021, 0x1040fe1f, 0x240a0001, 0xa34a05c4, 
+0x8fab006c, 0x8faa0064, 0x1705823, 0xafab006c, 
+0x8fab009c, 0x1505021, 0x16a102b, 0x10400004, 
+0xafaa0064, 0x8f420148, 0x1425023, 0xafaa0064, 
+0x8f420368, 0x2442ffff, 0xaf420368, 0x8faa005c, 
+0x240b0001, 0x8f420368, 0x154b0006, 0x240b0002, 
+0x8f42035c, 0x2442ffff, 0xaf42035c, 0x1000000c, 
+0x8f42035c, 0x114b0006, 0x0, 0x8f420360, 
+0x2442ffff, 0xaf420360, 0x10000005, 0x8f420360, 
+0x8f420364, 0x2442ffff, 0xaf420364, 0x8f420364, 
+0x8fab0054, 0x8faa004c, 0xad4b0000, 0x8f420044, 
+0x8f440088, 0x8f430078, 0x24420001, 0x441024, 
+0x24630001, 0xaf420044, 0xaf430078, 0x8faa006c, 
+0x1540fe0b, 0x0, 0x8fab006c, 0x1160001e, 
+0x0, 0x934205c4, 0x10400009, 0x0, 
+0x8faa0064, 0xaf4a00c4, 0xaf4b00c0, 0x8fab007c, 
+0xaf4b00c8, 0x8faa0074, 0x1000000e, 0xaf4a00cc, 
+0x97ab008e, 0x1160000b, 0x34038100, 0x8fa20020, 
+0x8c46000c, 0xa443000c, 0x97aa0086, 0x8c440004, 
+0x8c450008, 0xa44a000e, 0xac440000, 0xac450004, 
+0xac460008, 0x8f42034c, 0x24420001, 0xaf42034c, 
+0x10000010, 0x8f42034c, 0x8fab007c, 0x3164ffff, 
+0x2484fffc, 0x801821, 0x8f440250, 0x8f450254, 
+0x8f460118, 0x1021, 0xa32821, 0xa3382b, 
+0x822021, 0x872021, 0xaf440250, 0xc0f809, 
+0xaf450254, 0x8fbf00c8, 0x8fbe00c4, 0x8fb500c0, 
+0x8fb300bc, 0x8fb200b8, 0x8fb100b4, 0x8fb000b0, 
+0x3e00008, 0x27bd00d0, 0x3e00008, 0x0, 
+0x27bdff38, 0x240b0001, 0xafbf00c0, 0xafbe00bc, 
+0xafb500b8, 0xafb300b4, 0xafb200b0, 0xafb100ac, 
+0xafb000a8, 0xa3a00087, 0xafa00044, 0xafab005c, 
+0x934205c4, 0xa7a00076, 0x10400007, 0xa7a0007e, 
+0x8f4c00c0, 0xafac0064, 0x8f4b00c8, 0x8f5e00c4, 
+0x10000130, 0xafab006c, 0x8f420114, 0x40f809, 
+0x0, 0x403021, 0x10c002a1, 0x0, 
+0x8cc20000, 0x8cc30004, 0xafa20020, 0xafa30024, 
+0x8fac0024, 0x8fbe0020, 0x3182ffff, 0x2442fffc, 
+0xafa20064, 0x3c020006, 0x2c21024, 0x14400015, 
+0xafac006c, 0x93c20000, 0x30420001, 0x10400011, 
+0x2402ffff, 0x8fc30000, 0x14620004, 0x3402ffff, 
+0x97c30004, 0x1062000b, 0x0, 0xc0024bb, 
+0x3c02021, 0x304200ff, 0x14400006, 0x0, 
+0x8f420118, 0x40f809, 0x0, 0x10000280, 
+0x0, 0x8fa20024, 0x3c03ffbf, 0x3463ffff, 
+0x431024, 0x3c03ffff, 0x431824, 0x14600003, 
+0xafa20024, 0x10000040, 0x8021, 0x3c020080, 
+0x621024, 0x10400007, 0x0, 0x8f42038c, 
+0x24420001, 0xaf42038c, 0x8f42038c, 0x10000036, 
+0x24100001, 0x8f420210, 0x24420001, 0xaf420210, 
+0x8f420210, 0x3c020001, 0x621024, 0x10400006, 
+0x3c020002, 0x8f4201c4, 0x24420001, 0xaf4201c4, 
+0x8f4201c4, 0x3c020002, 0x621024, 0x10400006, 
+0x3c020004, 0x8f42037c, 0x24420001, 0xaf42037c, 
+0x8f42037c, 0x3c020004, 0x621024, 0x10400006, 
+0x3c020008, 0x8f420380, 0x24420001, 0xaf420380, 
+0x8f420380, 0x3c020008, 0x621024, 0x10400006, 
+0x3c020010, 0x8f420384, 0x24420001, 0xaf420384, 
+0x8f420384, 0x3c020010, 0x621024, 0x10400006, 
+0x3c020020, 0x8f4201c0, 0x24420001, 0xaf4201c0, 
+0x8f4201c0, 0x3c020020, 0x621024, 0x10400006, 
+0x24100001, 0x8f420388, 0x24420001, 0xaf420388, 
+0x8f420388, 0x24100001, 0x8c020260, 0x8fab0064, 
+0x4b102b, 0x10400015, 0x320200ff, 0x8f4201e8, 
+0x24420001, 0xaf4201e8, 0x8f4201e8, 0x8fac006c, 
+0x8f8200e0, 0x358c0100, 0xafac006c, 0xafa20010, 
+0x8f8200e4, 0x24100001, 0x3c040001, 0x248468a0, 
+0xafa20014, 0x8fa60020, 0x8fa70024, 0x3c050007, 
+0xc002b3b, 0x34a53600, 0x320200ff, 0x10400010, 
+0x3c020080, 0x2c21024, 0x1440000e, 0x32c20400, 
+0x8fab006c, 0x3c020080, 0x34420100, 0x1621024, 
+0x10400005, 0x0, 0x8f42020c, 0x24420001, 
+0xaf42020c, 0x8f42020c, 0x10000202, 0x8fa30064, 
+0x32c20400, 0x10400012, 0x34028100, 0x97c3000c, 
+0x1462000f, 0x0, 0x240c0200, 0xa7ac0076, 
+0x97c2000e, 0x8fc30008, 0x8fc40004, 0x8fab0064, 
+0x8fc50000, 0x256bfffc, 0xafab0064, 0xa7a2007e, 
+0xafc3000c, 0xafc40008, 0xafc50004, 0x27de0004, 
+0x8fa70064, 0x320200ff, 0x14400034, 0x3c020100, 
+0x97c4000c, 0x2c8305dd, 0x38828870, 0x2c420001, 
+0x621825, 0x10600015, 0x2821, 0x32c20800, 
+0x10400015, 0x24020800, 0x97c30014, 0x14620012, 
+0x3402aaaa, 0x97c3000e, 0x14620007, 0x2021, 
+0x97c30010, 0x24020300, 0x14620004, 0x801021, 
+0x97c20012, 0x2c440001, 0x801021, 0x54400006, 
+0x24050016, 0x10000004, 0x0, 0x24020800, 
+0x50820001, 0x2405000e, 0x10a00013, 0x3c52021, 
+0x24830009, 0x3c02001f, 0x3442ffff, 0x43102b, 
+0x10400003, 0x0, 0x8f420148, 0x621823, 
+0x90620000, 0x38430006, 0x2c630001, 0x38420011, 
+0x2c420001, 0x621825, 0x10600004, 0x3c020100, 
+0x94820002, 0x453821, 0x3c020100, 0x2c21024, 
+0x5040000e, 0xafa70064, 0x8fac0064, 0x10ec0008, 
+0x3c050007, 0x3c040001, 0x24846908, 0x8fa60064, 
+0x34a54000, 0xafa00010, 0xc002b3b, 0xafa00014, 
+0x8fab0064, 0x256b0004, 0xafab0064, 0x8f420080, 
+0x8fac0064, 0x4c102b, 0x1040002c, 0x32c28000, 
+0x10400034, 0x240b0003, 0x32c21000, 0x10400031, 
+0xafab005c, 0x1000002e, 0x240c0004, 0x8f420350, 
+0x2403ffbf, 0x283a024, 0x24420001, 0xaf420350, 
+0x10000173, 0x8f420350, 0x3c020800, 0x2c2b025, 
+0x2402ffbf, 0x282a024, 0x8f830128, 0x3c040001, 
+0x248468d0, 0x26620001, 0xafa20014, 0xafa30010, 
+0x8f860120, 0x8f870124, 0x3c050007, 0xc002b3b, 
+0x34a55300, 0x10000162, 0x0, 0x8ea20000, 
+0x8ea30004, 0x3c040001, 0x248468e8, 0xafb00010, 
+0xafb10014, 0x8ea70018, 0x34a55900, 0xc002b3b, 
+0x603021, 0x10000156, 0x0, 0x8f420084, 
+0x8fab0064, 0x4b102b, 0x14400007, 0x3c020001, 
+0x2c21024, 0x10400004, 0x0, 0x240c0002, 
+0xafac005c, 0x8fab0064, 0x11600166, 0x27ac0020, 
+0xafac008c, 0x8fab005c, 0x240c0001, 0x556c0021, 
+0x240c0002, 0x8f430054, 0x8f420050, 0x1062000b, 
+0x274b0054, 0x8f510054, 0x3403ecc0, 0xafab004c, 
+0x26220001, 0x304201ff, 0xafa20054, 0x111140, 
+0x431021, 0x1000006b, 0x2e2a821, 0x8f420044, 
+0x8fac0064, 0x3c040001, 0x248468ac, 0xafac0014, 
+0xafa20010, 0x8f460054, 0x8f470050, 0x3c050007, 
+0xc002b3b, 0x34a54300, 0x8f430350, 0x2402ffbf, 
+0x282a024, 0x24630001, 0xaf430350, 0x10000124, 
+0x8f420350, 0x156c001d, 0x0, 0x8f430074, 
+0x8f420070, 0x1062000a, 0x274b0074, 0x8f510074, 
+0xafab004c, 0x26220001, 0x304203ff, 0xafa20054, 
+0x111140, 0x24426cc0, 0x1000004a, 0x2e2a821, 
+0x8f420044, 0x8fac0064, 0x3c040001, 0x248468b8, 
+0x3c050007, 0xafac0014, 0xafa20010, 0x8f460074, 
+0x8f470070, 0x34a54500, 0x240b0001, 0xc002b3b, 
+0xafab005c, 0x1000ffc3, 0x0, 0x8f430064, 
+0x8f420060, 0x1062001a, 0x274c0064, 0x8f510064, 
+0x8fab005c, 0xafac004c, 0x26220001, 0x304200ff, 
+0xafa20054, 0x24020004, 0x1562000e, 0x111140, 
+0x111180, 0x24420cc0, 0x2e21021, 0xafa20044, 
+0x9442002a, 0x8fac0044, 0x8fab0064, 0x4b102b, 
+0x10400024, 0x25950020, 0x240c0001, 0x10000021, 
+0xa3ac0087, 0x24424cc0, 0x1000001e, 0x2e2a821, 
+0x8f420044, 0x8fab0064, 0x3c040001, 0x248468c4, 
+0xafab0014, 0xafa20010, 0x8f460064, 0x8f470060, 
+0x3c050007, 0xc002b3b, 0x34a54800, 0x3c020008, 
+0x2c21024, 0x1440ff61, 0x0, 0x8f420370, 
+0x240c0001, 0xafac005c, 0x24420001, 0xaf420370, 
+0x1000ff90, 0x8f420370, 0x27a30036, 0x131040, 
+0x621821, 0x94620000, 0x441021, 0x1000001f, 
+0xa4620000, 0xaebe0018, 0x93a20087, 0x10400084, 
+0x9821, 0x8fab0044, 0x8fa40064, 0x8fa3008c, 
+0x25620020, 0xafa20028, 0x25620008, 0xafa20030, 
+0x25620010, 0xafab002c, 0xafa20034, 0x9562002a, 
+0xa7a20038, 0x95620018, 0xa7a2003a, 0x9562001a, 
+0xa7a2003c, 0x9562001c, 0xa7a2003e, 0x94620018, 
+0x24630002, 0x822023, 0x1880ffdf, 0x26730001, 
+0x2e620004, 0x1440fff9, 0x0, 0x8f4200fc, 
+0x262102a, 0x14400030, 0x24030001, 0x8f83012c, 
+0x10600028, 0x0, 0x8f820124, 0x431023, 
+0x22143, 0x58800001, 0x24840040, 0x8f820128, 
+0x431023, 0x21943, 0x58600001, 0x24630040, 
+0x64102a, 0x54400001, 0x602021, 0xaf4400fc, 
+0x8f4200fc, 0x262102a, 0x10400016, 0x24030001, 
+0x1000001a, 0x306200ff, 0x8fac008c, 0x101040, 
+0x4c1021, 0x94470018, 0x101080, 0x4c1021, 
+0xafbe0010, 0x8c420008, 0x3c040001, 0x248468dc, 
+0x3c050007, 0x8c430004, 0x8c420000, 0x34a55500, 
+0x2003021, 0xc002b3b, 0xafa30014, 0x10000039, 
+0x0, 0x8f420334, 0x1821, 0x24420001, 
+0xaf420334, 0x8f420334, 0x306200ff, 0x1040ff06, 
+0x8021, 0x8f430008, 0x2402fbff, 0x1260002d, 
+0x625024, 0x3c0b4000, 0x22b4025, 0x8fb1008c, 
+0x2669ffff, 0x2209021, 0x8e420008, 0x96270018, 
+0x8c440000, 0x8c450004, 0x56090004, 0x240b0001, 
+0x240c0002, 0x10000002, 0xafac0010, 0xafab0010, 
+0x16000004, 0xafa80014, 0x8f420008, 0x10000002, 
+0xafa20018, 0xafaa0018, 0x8f42010c, 0x3c03021, 
+0xafa80098, 0xafa9009c, 0x40f809, 0xafaa00a0, 
+0x8fa80098, 0x8fa9009c, 0x8faa00a0, 0x1040ffc2, 
+0x3c02001f, 0x96230018, 0x3442ffff, 0x3c3f021, 
+0x5e102b, 0x10400003, 0x26310002, 0x8f420148, 
+0x3c2f023, 0x26100001, 0x213102b, 0x1440ffda, 
+0x26520004, 0x8fb00064, 0x1000001a, 0x0, 
+0x96a3000a, 0x8fb00064, 0x70102b, 0x54400001, 
+0x608021, 0x8ea40000, 0x8ea50004, 0x8fab005c, 
+0x240c0002, 0xafac0010, 0x934305c4, 0xb1700, 
+0x10600003, 0x2223025, 0x3c020800, 0xc23025, 
+0xafa60014, 0x8f420008, 0xafa20018, 0x8f42010c, 
+0x3c03021, 0x40f809, 0x2003821, 0x1040fecb, 
+0x3c050007, 0x97ac0076, 0x11800007, 0x96a3000e, 
+0x934205c4, 0x14400004, 0x0, 0x97ab007e, 
+0x6c1825, 0xa6ab0016, 0x8fac006c, 0x3c02ffff, 
+0x1821024, 0x10400003, 0xc1402, 0x34630400, 
+0xa6a20014, 0xa6b0000a, 0x8fab0064, 0x560b0006, 
+0x3d0f021, 0x34620004, 0xafa00064, 0xa6a2000e, 
+0x1000000d, 0xa34005c4, 0x8fac0064, 0x3c02001f, 
+0x3442ffff, 0x5e102b, 0x1906023, 0xafac0064, 
+0xa6a3000e, 0x240b0001, 0x10400003, 0xa34b05c4, 
+0x8f420148, 0x3c2f023, 0x8fab0054, 0x8fac004c, 
+0xad8b0000, 0x8fac0064, 0x1580feba, 0x0, 
+0x8fab0064, 0x1160001b, 0x0, 0x934205c4, 
+0x10400006, 0x0, 0xaf5e00c4, 0xaf4b00c0, 
+0x8fac006c, 0x1000000e, 0xaf4c00c8, 0x97ab0076, 
+0x1160000b, 0x34038100, 0x8fa20020, 0x8c46000c, 
+0xa443000c, 0x97ac007e, 0x8c440004, 0x8c450008, 
+0xa44c000e, 0xac440000, 0xac450004, 0xac460008, 
+0x8f42034c, 0x24420001, 0xaf42034c, 0x10000010, 
+0x8f42034c, 0x8fab006c, 0x3164ffff, 0x2484fffc, 
+0x801821, 0x8f440250, 0x8f450254, 0x8f460118, 
+0x1021, 0xa32821, 0xa3382b, 0x822021, 
+0x872021, 0xaf440250, 0xc0f809, 0xaf450254, 
+0x8fbf00c0, 0x8fbe00bc, 0x8fb500b8, 0x8fb300b4, 
+0x8fb200b0, 0x8fb100ac, 0x8fb000a8, 0x3e00008, 
+0x27bd00c8, 0x3e00008, 0x0, 0x27bdffd8, 
+0xafbf0024, 0xafb00020, 0x8f43004c, 0x8f420048, 
+0x10620034, 0x0, 0x8f430048, 0x8f42004c, 
+0x622023, 0x4820001, 0x24840200, 0x8f430054, 
+0x8f42004c, 0x43102b, 0x14400004, 0x24020200, 
+0x8f43004c, 0x10000005, 0x431023, 0x8f420054, 
+0x8f43004c, 0x431023, 0x2442ffff, 0x405021, 
+0x8a102a, 0x54400001, 0x805021, 0x8f49004c, 
+0x8f48004c, 0x8f440188, 0x8f45018c, 0x8f46004c, 
+0x24071000, 0xafa70010, 0x84140, 0x1001821, 
+0x12a4821, 0x313001ff, 0xafb00014, 0x8f470014, 
+0x1021, 0x63140, 0xafa70018, 0xa32821, 
+0xa3382b, 0x822021, 0x872021, 0x3402ecc0, 
+0xc23021, 0x8f420108, 0x2e63021, 0x40f809, 
+0xa3940, 0x54400001, 0xaf50004c, 0x8f43004c, 
+0x8f420048, 0x14620018, 0x0, 0x8f420000, 
+0x10400007, 0x0, 0xaf80004c, 0x8f82004c, 
+0x1040fffd, 0x0, 0x10000005, 0x0, 
+0xaf800048, 0x8f820048, 0x1040fffd, 0x0, 
+0x8f820060, 0x2403fdff, 0x431024, 0xaf820060, 
+0x8f420000, 0x10400003, 0x0, 0x10000002, 
+0xaf80004c, 0xaf800048, 0x8fbf0024, 0x8fb00020, 
+0x3e00008, 0x27bd0028, 0x3e00008, 0x0, 
+0x27bdffd8, 0xafbf0024, 0xafb00020, 0x8f43005c, 
+0x8f420058, 0x10620049, 0x0, 0x8f430058, 
+0x8f42005c, 0x622023, 0x4820001, 0x24840100, 
+0x8f430064, 0x8f42005c, 0x43102b, 0x14400004, 
+0x24020100, 0x8f43005c, 0x10000005, 0x431023, 
+0x8f420064, 0x8f43005c, 0x431023, 0x2442ffff, 
+0x403821, 0x87102a, 0x54400001, 0x803821, 
+0x8f42005c, 0x471021, 0x305000ff, 0x32c21000, 
+0x10400015, 0x24082000, 0x8f49005c, 0x8f440190, 
+0x8f450194, 0x8f46005c, 0x73980, 0xafa80010, 
+0xafb00014, 0x8f480014, 0x94980, 0x1201821, 
+0x1021, 0xa32821, 0xa3482b, 0x822021, 
+0x892021, 0x63180, 0xafa80018, 0x8f420108, 
+0x10000014, 0x24c60cc0, 0x8f49005c, 0x8f440190, 
+0x8f450194, 0x8f46005c, 0x73940, 0xafa80010, 
+0xafb00014, 0x8f480014, 0x94940, 0x1201821, 
+0x1021, 0xa32821, 0xa3482b, 0x822021, 
+0x892021, 0x63140, 0xafa80018, 0x8f420108, 
+0x24c64cc0, 0x40f809, 0x2e63021, 0x54400001, 
+0xaf50005c, 0x8f43005c, 0x8f420058, 0x14620018, 
+0x0, 0x8f420000, 0x10400007, 0x0, 
+0xaf80004c, 0x8f82004c, 0x1040fffd, 0x0, 
+0x10000005, 0x0, 0xaf800048, 0x8f820048, 
+0x1040fffd, 0x0, 0x8f820060, 0x2403feff, 
+0x431024, 0xaf820060, 0x8f420000, 0x10400003, 
+0x0, 0x10000002, 0xaf80004c, 0xaf800048, 
+0x8fbf0024, 0x8fb00020, 0x3e00008, 0x27bd0028, 
+0x3e00008, 0x0, 0x27bdffd8, 0xafbf0024, 
+0xafb00020, 0x8f43006c, 0x8f420068, 0x10620033, 
+0x0, 0x8f430068, 0x8f42006c, 0x622023, 
+0x4820001, 0x24840400, 0x8f430074, 0x8f42006c, 
+0x43102b, 0x14400004, 0x24020400, 0x8f43006c, 
+0x10000005, 0x431023, 0x8f420074, 0x8f43006c, 
+0x431023, 0x2442ffff, 0x405021, 0x8a102a, 
+0x54400001, 0x805021, 0x8f49006c, 0x8f48006c, 
+0x8f440198, 0x8f45019c, 0x8f46006c, 0x24074000, 
+0xafa70010, 0x84140, 0x1001821, 0x12a4821, 
+0x313003ff, 0xafb00014, 0x8f470014, 0x1021, 
+0x63140, 0x24c66cc0, 0xafa70018, 0xa32821, 
+0xa3382b, 0x822021, 0x872021, 0x8f420108, 
+0x2e63021, 0x40f809, 0xa3940, 0x54400001, 
+0xaf50006c, 0x8f43006c, 0x8f420068, 0x14620018, 
+0x0, 0x8f420000, 0x10400007, 0x0, 
+0xaf80004c, 0x8f82004c, 0x1040fffd, 0x0, 
+0x10000005, 0x0, 0xaf800048, 0x8f820048, 
+0x1040fffd, 0x0, 0x8f820060, 0x2403f7ff, 
+0x431024, 0xaf820060, 0x8f420000, 0x10400003, 
+0x0, 0x10000002, 0xaf80004c, 0xaf800048, 
+0x8fbf0024, 0x8fb00020, 0x3e00008, 0x27bd0028, 
+0x3e00008, 0x0, 0x8f4200fc, 0x3c030001, 
+0x8f4400f8, 0x346330c8, 0x24420001, 0xaf4200fc, 
+0x8f850128, 0x2e31021, 0x54820004, 0x24820008, 
+0x3c020001, 0x34422ec8, 0x2e21021, 0x401821, 
+0xaf4300f8, 0xac600000, 0x8f4200f4, 0x14620004, 
+0x3c020001, 0x24a20020, 0x1000000f, 0xaf820128, 
+0x8f4300f8, 0x344230c8, 0x2e21021, 0x54620004, 
+0x24620008, 0x3c020001, 0x34422ec8, 0x2e21021, 
+0x401821, 0x8c620004, 0x21140, 0xa21021, 
+0xaf820128, 0xac600000, 0x8ca30018, 0x30620070, 
+0x1040002d, 0x30620020, 0x10400004, 0x3c020010, 
+0x2c21024, 0x1040000d, 0x0, 0x30620040, 
+0x10400004, 0x3c020020, 0x2c21024, 0x10400007, 
+0x0, 0x30620010, 0x1040001f, 0x3c020040, 
+0x2c21024, 0x1440001c, 0x0, 0x8f820040, 
+0x30420001, 0x14400008, 0x2021, 0x8c030104, 
+0x24020001, 0x50620005, 0x24040001, 0x8c020264, 
+0x10400003, 0x801021, 0x24040001, 0x801021, 
+0x10400006, 0x0, 0x8f42030c, 0x24420001, 
+0xaf42030c, 0x10000008, 0x8f42030c, 0x8f820044, 
+0x34420004, 0xaf820044, 0x8f420308, 0x24420001, 
+0xaf420308, 0x8f420308, 0x3e00008, 0x0, 
+0x3e00008, 0x0, 0x27bdff98, 0xafbf0060, 
+0xafbe005c, 0xafb50058, 0xafb30054, 0xafb20050, 
+0xafb1004c, 0xafb00048, 0x8f4200fc, 0x24420001, 
+0xaf4200fc, 0x8f880128, 0x25020020, 0xaf820128, 
+0x8d030018, 0x30620070, 0x1040002e, 0x30620020, 
+0x10400004, 0x3c020010, 0x2c21024, 0x1040000d, 
+0x0, 0x30620040, 0x10400004, 0x3c020020, 
+0x2c21024, 0x10400007, 0x0, 0x30620010, 
+0x104001a9, 0x3c020040, 0x2c21024, 0x144001a6, 
+0x0, 0x8f820040, 0x30420001, 0x14400008, 
+0x2021, 0x8c030104, 0x24020001, 0x50620005, 
+0x24040001, 0x8c020264, 0x10400003, 0x801021, 
+0x24040001, 0x801021, 0x10400006, 0x0, 
+0x8f42030c, 0x24420001, 0xaf42030c, 0x10000192, 
+0x8f42030c, 0x8f820044, 0x34420004, 0xaf820044, 
+0x8f420308, 0x24420001, 0xaf420308, 0x1000018a, 
+0x8f420308, 0x30620002, 0x1040014b, 0x3c020800, 
+0x8d1e001c, 0x1e5702, 0xafaa0034, 0x950a0016, 
+0x3c22024, 0xafaa0024, 0x8faa0034, 0x24020001, 
+0x15420006, 0x33deffff, 0x1e1140, 0x3403ecc0, 
+0x431021, 0x10000010, 0x2e2a821, 0x24020002, 
+0x15420005, 0x24020003, 0x1e1140, 0x24426cc0, 
+0x10000009, 0x2e2a821, 0x15420005, 0x1e1180, 
+0x1e1140, 0x24424cc0, 0x10000003, 0x2e2a821, 
+0x571021, 0x24550ce0, 0x96a2000e, 0x304afffc, 
+0x30420400, 0x10400003, 0xafaa002c, 0x100000e1, 
+0x8821, 0x10800004, 0x8821, 0x97b10026, 
+0x100000dd, 0xa6b10012, 0x8eb30018, 0x966a000c, 
+0xa7aa003e, 0x97a5003e, 0x2ca305dd, 0x38a28870, 
+0x2c420001, 0x621825, 0x10600015, 0x2021, 
+0x32c20800, 0x10400015, 0x24020800, 0x96630014, 
+0x14620012, 0x3402aaaa, 0x9663000e, 0x14620007, 
+0x2821, 0x96630010, 0x24020300, 0x14620004, 
+0xa01021, 0x96620012, 0x2c450001, 0xa01021, 
+0x54400006, 0x24040016, 0x10000004, 0x0, 
+0x24020800, 0x50a20001, 0x2404000e, 0x108000b9, 
+0x2649021, 0x92420000, 0x3042000f, 0x28080, 
+0x32c20100, 0x10400020, 0x2501821, 0x3c020020, 
+0x43102b, 0x1440000e, 0x2402021, 0x2821, 
+0x94820000, 0x24840002, 0xa22821, 0x83102b, 
+0x1440fffb, 0x30a2ffff, 0x51c02, 0x622821, 
+0x51c02, 0x30a2ffff, 0x10000009, 0x622821, 
+0x8f470148, 0x8f420110, 0x102842, 0x3c060020, 
+0x40f809, 0xafa80040, 0x3045ffff, 0x8fa80040, 
+0x50a00001, 0x3405ffff, 0x8faa002c, 0x354a0002, 
+0x10000002, 0xafaa002c, 0x2821, 0x32c20080, 
+0x10400090, 0xa6a50010, 0x26430009, 0x3c02001f, 
+0x3442ffff, 0x43102b, 0x10400003, 0x0, 
+0x8f420148, 0x621823, 0x90660000, 0x30c200ff, 
+0x38430006, 0x2c630001, 0x38420011, 0x2c420001, 
+0x621825, 0x1060007f, 0x24020800, 0x8821, 
+0x97a3003e, 0x1462000f, 0x2602021, 0x96710000, 
+0x96620002, 0x96630004, 0x96640006, 0x2228821, 
+0x2238821, 0x2248821, 0x96620008, 0x9663000a, 
+0x9664000c, 0x2228821, 0x2238821, 0x10000007, 
+0x2248821, 0x94820000, 0x24840002, 0x2228821, 
+0x92102b, 0x1440fffb, 0x0, 0x111c02, 
+0x3222ffff, 0x628821, 0x111c02, 0x3222ffff, 
+0x628821, 0x32c20200, 0x10400003, 0x26440006, 
+0x1000003e, 0x8021, 0x3c05001f, 0x34a5ffff, 
+0xa4102b, 0x10400003, 0x0, 0x8f420148, 
+0x822023, 0x94820000, 0x30421fff, 0x10400004, 
+0x2644000c, 0x96420002, 0x10000030, 0x508023, 
+0x96420002, 0x26430014, 0x508023, 0x3c020020, 
+0x43102b, 0x1440000a, 0xd08021, 0x9642000c, 
+0x2028021, 0x9642000e, 0x96430010, 0x96440012, 
+0x2028021, 0x2038021, 0x10000020, 0x2048021, 
+0xa4102b, 0x10400003, 0x0, 0x8f420148, 
+0x822023, 0x94820000, 0x24840002, 0x2028021, 
+0xa4102b, 0x10400003, 0x0, 0x8f420148, 
+0x822023, 0x94820000, 0x24840002, 0x2028021, 
+0xa4102b, 0x10400003, 0x0, 0x8f420148, 
+0x822023, 0x94820000, 0x24840002, 0x2028021, 
+0xa4102b, 0x10400003, 0x0, 0x8f420148, 
+0x822023, 0x94820000, 0x2028021, 0x3c020100, 
+0x2c21024, 0x1040000e, 0x0, 0x8faa002c, 
+0x31420004, 0x1040000a, 0x0, 0x9504000e, 
+0x2642021, 0xc003eec, 0x2484fffc, 0x3042ffff, 
+0x2228821, 0x111c02, 0x3222ffff, 0x628821, 
+0x8faa0024, 0x1518823, 0x111402, 0x2228821, 
+0x2308821, 0x111402, 0x2228821, 0x3231ffff, 
+0x52200001, 0x3411ffff, 0x8faa002c, 0x354a0001, 
+0xafaa002c, 0xa6b10012, 0x97aa002e, 0xa6aa000e, 
+0x8faa002c, 0x31420004, 0x10400002, 0x24091000, 
+0x34098000, 0x8f480044, 0x8f4401a0, 0x8f4501a4, 
+0xafa90010, 0x8f490044, 0x84140, 0x1001821, 
+0xafa90014, 0x8f48000c, 0x2a03021, 0x24070020, 
+0xafa80018, 0x8f48010c, 0x1021, 0xa32821, 
+0xa3482b, 0x822021, 0x100f809, 0x892021, 
+0x1440000b, 0x0, 0x8f820128, 0x3c040001, 
+0x24846914, 0xafbe0014, 0xafa20010, 0x8f860124, 
+0x8f870120, 0x3c050007, 0xc002b3b, 0x34a59920, 
+0x8f420368, 0x2442ffff, 0xaf420368, 0x8f420044, 
+0x8f430088, 0x24420001, 0x431024, 0xaf420044, 
+0x8faa0034, 0x8f440368, 0x24020001, 0x15420006, 
+0x24020002, 0x8f42035c, 0x2442ffff, 0xaf42035c, 
+0x10000049, 0x8f42035c, 0x15420006, 0x0, 
+0x8f420364, 0x2442ffff, 0xaf420364, 0x10000042, 
+0x8f420364, 0x8f420360, 0x2442ffff, 0xaf420360, 
+0x1000003d, 0x8f420360, 0x30621000, 0x10400005, 
+0x30628000, 0x8f420078, 0x24420001, 0x10000036, 
+0xaf420078, 0x10400034, 0x0, 0x8f420078, 
+0x24420001, 0xaf420078, 0x8c030240, 0x43102b, 
+0x1440002d, 0x24070008, 0x8f440168, 0x8f45016c, 
+0x8f430044, 0x8f48000c, 0x8f860120, 0x24020040, 
+0xafa20010, 0xafa30014, 0xafa80018, 0x8f42010c, 
+0x40f809, 0x24c6001c, 0x14400011, 0x24020001, 
+0x3c010001, 0x370821, 0xa02240f2, 0x8f820124, 
+0xafa20010, 0x8f820128, 0x3c040001, 0x2484688c, 
+0xafa20014, 0x8f460044, 0x8f870120, 0x3c050009, 
+0xc002b3b, 0x34a51300, 0x1000000b, 0x0, 
+0x8f420304, 0x24420001, 0xaf420304, 0x8f420304, 
+0x8f420044, 0xaf42007c, 0x3c010001, 0x370821, 
+0xa02040f2, 0xaf400078, 0x8f420318, 0x24420001, 
+0xaf420318, 0x8f420318, 0x8fbf0060, 0x8fbe005c, 
+0x8fb50058, 0x8fb30054, 0x8fb20050, 0x8fb1004c, 
+0x8fb00048, 0x3e00008, 0x27bd0068, 0x3e00008, 
+0x0, 0x0, 0x0, 0x8f42013c, 
+0xaf8200c0, 0x8f42013c, 0xaf8200c4, 0x8f42013c, 
+0xaf8200c8, 0x8f420138, 0xaf8200d0, 0x8f420138, 
+0xaf8200d4, 0x8f420138, 0x3e00008, 0xaf8200d8, 
+0x27bdffe0, 0x27840208, 0x24050200, 0xafbf0018, 
+0xc002bbf, 0x24060008, 0x8c020204, 0xc004012, 
+0xaf820210, 0x3c020001, 0x8c426d94, 0x30420002, 
+0x1040000e, 0x2021, 0x8c060248, 0x24020002, 
+0x3c010001, 0xac226d98, 0xc005104, 0x24050002, 
+0x2021, 0x8c060248, 0x24020001, 0x3c010001, 
+0xac226d98, 0x10000011, 0x24050001, 0x8c060248, 
+0x24020004, 0x3c010001, 0xac226d98, 0xc005104, 
+0x24050004, 0x3c020001, 0x8c426d94, 0x30420001, 
+0x10400008, 0x24020001, 0x3c010001, 0xac226d98, 
+0x2021, 0x24050001, 0x3c06601b, 0xc005104, 
+0x0, 0x3c040001, 0x248469d0, 0x8f420150, 
+0x8f430154, 0x3c050008, 0x8f460158, 0x21640, 
+0x31940, 0x34630403, 0x431025, 0x633c0, 
+0x461025, 0xaf82021c, 0xafa00010, 0xafa00014, 
+0x8f86021c, 0x34a50200, 0xc002b3b, 0x3821, 
+0x3c010001, 0xac206d90, 0x3c010001, 0xac206da8, 
+0x8fbf0018, 0x3e00008, 0x27bd0020, 0x27bdffe0, 
+0x3c050008, 0x34a50300, 0xafbf0018, 0xafa00010, 
+0xafa00014, 0x8f860200, 0x3c040001, 0x248469dc, 
+0xc002b3b, 0x3821, 0x8f420410, 0x24420001, 
+0xaf420410, 0x8f420410, 0x8fbf0018, 0x3e00008, 
+0x27bd0020, 0x27bdffd8, 0xafbf0020, 0xafb1001c, 
+0xafb00018, 0x8f4203a4, 0x24420001, 0xaf4203a4, 
+0x8f4203a4, 0x8f900220, 0x8f8200e0, 0xafa20010, 
+0x8f8200e4, 0xafa20014, 0x8f8600c4, 0x8f8700c8, 
+0x3c040001, 0x248469e8, 0xc002b3b, 0x2002821, 
+0x3c044000, 0x2041024, 0x504000b4, 0x3c040100, 
+0x8f4203bc, 0x24420001, 0xaf4203bc, 0x8f4203bc, 
+0x8f8700c4, 0x8f8300c8, 0x8f420148, 0x671823, 
+0x43102b, 0x10400003, 0x0, 0x8f420148, 
+0x621821, 0x10600005, 0x0, 0x8f42014c, 
+0x43102b, 0x1040000b, 0x0, 0x8f8200e0, 
+0x8f430124, 0xaf42011c, 0xaf430114, 0x8f820220, 
+0x3c0308ff, 0x3463fffb, 0x431024, 0x100000ce, 
+0x441025, 0x8f820220, 0x3c0308ff, 0x3463ffff, 
+0x431024, 0x34420004, 0xaf820220, 0x8f8200e0, 
+0x8f430124, 0xaf42011c, 0xaf430114, 0x8f8600c8, 
+0x8f840120, 0x8f830124, 0x10000005, 0x2821, 
+0x14620002, 0x24620020, 0x27624800, 0x401821, 
+0x1064000c, 0x30a200ff, 0x8c620018, 0x30420003, 
+0x1040fff7, 0x27624fe0, 0x8f4203d0, 0x24050001, 
+0x24420001, 0xaf4203d0, 0x8f4203d0, 0x8c660008, 
+0x30a200ff, 0x14400058, 0x0, 0x934205c4, 
+0x14400055, 0x0, 0x8f8700c4, 0x8f8800e0, 
+0x8f8400e4, 0x2402fff8, 0x1024024, 0x1041023, 
+0x218c3, 0x4620001, 0x24630200, 0x10600005, 
+0x24020001, 0x10620009, 0x0, 0x1000001f, 
+0x0, 0x8f4203c0, 0xe03021, 0x24420001, 
+0xaf4203c0, 0x10000040, 0x8f4203c0, 0x8f4203c4, 
+0x24420001, 0xaf4203c4, 0x8c860000, 0x8f420148, 
+0x8f4303c4, 0xe61823, 0x43102b, 0x10400004, 
+0x2c62233f, 0x8f420148, 0x621821, 0x2c62233f, 
+0x14400031, 0x0, 0x8f42020c, 0x24420001, 
+0xaf42020c, 0x8f42020c, 0xe03021, 0x24820008, 
+0xaf8200e4, 0x10000028, 0xaf8200e8, 0x8f4203c8, 
+0x24420001, 0xaf4203c8, 0x8f4203c8, 0x8c850000, 
+0x8f420148, 0xa71823, 0x43102b, 0x10400003, 
+0x0, 0x8f420148, 0x621821, 0x8f42014c, 
+0x43102b, 0x5440000a, 0xa03021, 0x8f42020c, 
+0x24420001, 0xaf42020c, 0x8f42020c, 0x24820008, 
+0xaf8200e4, 0x8f8400e4, 0x1488ffec, 0xaf8400e8, 
+0x1488000d, 0x27623000, 0x14820002, 0x2482fff8, 
+0x27623ff8, 0x94430006, 0x3c02001f, 0x3442ffff, 
+0xc33021, 0x46102b, 0x10400003, 0x0, 
+0x8f420148, 0xc23023, 0xaf8600c8, 0x8f8300c4, 
+0x8f420148, 0xc31823, 0x43102b, 0x10400003, 
+0x0, 0x8f420148, 0x621821, 0x10600005, 
+0x0, 0x8f42014c, 0x43102b, 0x50400008, 
+0x3c02fdff, 0x8f820220, 0x3c0308ff, 0x3463fffb, 
+0x431024, 0x3c034000, 0x1000003f, 0x431025, 
+0x8f4303cc, 0x3442ffff, 0x282a024, 0x24630001, 
+0xaf4303cc, 0x10000039, 0x8f4203cc, 0x2041024, 
+0x1040000e, 0x3c110200, 0x8f4203a8, 0x24420001, 
+0xaf4203a8, 0x8f4203a8, 0x8f820220, 0x3c0308ff, 
+0x3463ffff, 0x431024, 0x441025, 0xc003daf, 
+0xaf820220, 0x10000029, 0x0, 0x2111024, 
+0x50400008, 0x3c110400, 0x8f4203ac, 0x24420001, 
+0xaf4203ac, 0xc003daf, 0x8f4203ac, 0x10000019, 
+0x0, 0x2111024, 0x1040001c, 0x0, 
+0x8f830224, 0x24021402, 0x14620009, 0x3c050008, 
+0x3c040001, 0x248469f4, 0xafa00010, 0xafa00014, 
+0x8f860224, 0x34a50500, 0xc002b3b, 0x3821, 
+0x8f4203b0, 0x24420001, 0xaf4203b0, 0x8f4203b0, 
+0x8f820220, 0x2002021, 0x34420002, 0xc004e9c, 
+0xaf820220, 0x8f820220, 0x3c0308ff, 0x3463ffff, 
+0x431024, 0x511025, 0xaf820220, 0x8fbf0020, 
+0x8fb1001c, 0x8fb00018, 0x3e00008, 0x27bd0028, 
+0x3e00008, 0x0, 0x3c020001, 0x8c426da8, 
+0x27bdffb0, 0xafbf0048, 0xafbe0044, 0xafb50040, 
+0xafb3003c, 0xafb20038, 0xafb10034, 0x1040000f, 
+0xafb00030, 0x3c040001, 0x24846a00, 0x3c050008, 
+0xafa00010, 0xafa00014, 0x8f860220, 0x34a50600, 
+0x24020001, 0x3c010001, 0xac206da8, 0x3c010001, 
+0xac226d9c, 0xc002b3b, 0x3821, 0x3c037fff, 
+0x8c020268, 0x3463ffff, 0x3c04fdff, 0x431024, 
+0xac020268, 0x8f420004, 0x3484ffff, 0x30420002, 
+0x10400092, 0x284a024, 0x3c040600, 0x34842000, 
+0x8f420004, 0x2821, 0x2403fffd, 0x431024, 
+0xaf420004, 0xafa40020, 0x8f5e0018, 0x27aa0020, 
+0x240200ff, 0x13c20002, 0xafaa002c, 0x27c50001, 
+0x8c020228, 0xa09021, 0x1642000e, 0x1e38c0, 
+0x8f42033c, 0x24420001, 0xaf42033c, 0x8f42033c, 
+0x8c020228, 0x3c040001, 0x24846998, 0x3c050009, 
+0xafa00014, 0xafa20010, 0x8fa60020, 0x1000006d, 
+0x34a50500, 0xf71021, 0x8fa30020, 0x8fa40024, 
+0xac4304c0, 0xac4404c4, 0x8f830054, 0x8f820054, 
+0x247003e8, 0x2021023, 0x2c4203e9, 0x1040001b, 
+0x9821, 0xe08821, 0x263504c0, 0x8f440178, 
+0x8f45017c, 0x2201821, 0x240a0004, 0xafaa0010, 
+0xafb20014, 0x8f48000c, 0x1021, 0x2f53021, 
+0xafa80018, 0x8f48010c, 0x24070008, 0xa32821, 
+0xa3482b, 0x822021, 0x100f809, 0x892021, 
+0x54400006, 0x24130001, 0x8f820054, 0x2021023, 
+0x2c4203e9, 0x1440ffe9, 0x0, 0x326200ff, 
+0x54400017, 0xaf520018, 0x8f420378, 0x24420001, 
+0xaf420378, 0x8f420378, 0x8f820120, 0x8faa002c, 
+0xafa20010, 0x8f820124, 0x3c040001, 0x248469a4, 
+0x3c050009, 0xafa20014, 0x8d460000, 0x10000035, 
+0x34a50600, 0x8f420308, 0x24130001, 0x24420001, 
+0xaf420308, 0x8f420308, 0x1000001e, 0x326200ff, 
+0x8f830054, 0x8f820054, 0x247003e8, 0x2021023, 
+0x2c4203e9, 0x10400016, 0x9821, 0x3c150020, 
+0x24110010, 0x8f42000c, 0x8f440160, 0x8f450164, 
+0x8f860120, 0xafb10010, 0xafb20014, 0x551025, 
+0xafa20018, 0x8f42010c, 0x24070008, 0x40f809, 
+0x24c6001c, 0x1440ffe3, 0x0, 0x8f820054, 
+0x2021023, 0x2c4203e9, 0x1440ffee, 0x0, 
+0x326200ff, 0x14400011, 0x0, 0x8f420378, 
+0x24420001, 0xaf420378, 0x8f420378, 0x8f820120, 
+0x8faa002c, 0xafa20010, 0x8f820124, 0x3c040001, 
+0x248469ac, 0x3c050009, 0xafa20014, 0x8d460000, 
+0x34a50700, 0xc002b3b, 0x3c03821, 0x8f4202ec, 
+0x24420001, 0xaf4202ec, 0x8f4202ec, 0x8fbf0048, 
+0x8fbe0044, 0x8fb50040, 0x8fb3003c, 0x8fb20038, 
+0x8fb10034, 0x8fb00030, 0x3e00008, 0x27bd0050, 
+0x3c020001, 0x8c426da8, 0x27bdffe0, 0x1440000d, 
+0xafbf0018, 0x3c040001, 0x24846a0c, 0x3c050008, 
+0xafa00010, 0xafa00014, 0x8f860220, 0x34a50700, 
+0x24020001, 0x3c010001, 0xac226da8, 0xc002b3b, 
+0x3821, 0x3c020004, 0x2c21024, 0x10400007, 
+0x0, 0x8f820220, 0x3c0308ff, 0x3463ffff, 
+0x431024, 0x34420008, 0xaf820220, 0x3c050001, 
+0x8ca56d98, 0x24020001, 0x14a20007, 0x2021, 
+0xc00529b, 0x24050001, 0xac02026c, 0x8c03026c, 
+0x10000006, 0x3c020007, 0xc00529b, 0x2021, 
+0xac020268, 0x8c030268, 0x3c020007, 0x621824, 
+0x3c020002, 0x5062000d, 0x3c0205f5, 0x43102b, 
+0x14400006, 0x3c020004, 0x3c020001, 0x10620009, 
+0x3c020098, 0x1000000b, 0x0, 0x14620009, 
+0x3c023b9a, 0x10000004, 0x3442ca00, 0x10000002, 
+0x3442e100, 0x34429680, 0xaf4201fc, 0x8f4201fc, 
+0xaee20064, 0x8fbf0018, 0x3e00008, 0x27bd0020, 
+0x0, 0x0, 0x0, 0x86102b, 
+0x50400001, 0x872023, 0xc41023, 0x24843, 
+0x125102b, 0x1040001b, 0x91040, 0x824021, 
+0x88102b, 0x10400007, 0x1821, 0x94820000, 
+0x24840002, 0x621821, 0x88102b, 0x1440fffb, 
+0x0, 0x602021, 0xc73023, 0xa91023, 
+0x21040, 0xc22821, 0xc5102b, 0x10400007, 
+0x1821, 0x94c20000, 0x24c60002, 0x621821, 
+0xc5102b, 0x1440fffb, 0x0, 0x1000000d, 
+0x832021, 0x51040, 0x822821, 0x85102b, 
+0x10400007, 0x1821, 0x94820000, 0x24840002, 
+0x621821, 0x85102b, 0x1440fffb, 0x0, 
+0x602021, 0x41c02, 0x3082ffff, 0x622021, 
+0x41c02, 0x3082ffff, 0x622021, 0x3e00008, 
+0x3082ffff, 0x3e00008, 0x0, 0x802821, 
+0x30a20001, 0x1040002b, 0x3c03001f, 0x3463ffff, 
+0x24a20004, 0x62102b, 0x54400007, 0x65102b, 
+0x90a20001, 0x90a40003, 0x90a30000, 0x90a50002, 
+0x1000002a, 0x441021, 0x10400003, 0x0, 
+0x8f420148, 0xa22823, 0x90a40000, 0x24a50001, 
+0x65102b, 0x10400003, 0x0, 0x8f420148, 
+0xa22823, 0x90a20000, 0x24a50001, 0x21200, 
+0x822021, 0x65102b, 0x10400003, 0x0, 
+0x8f420148, 0xa22823, 0x90a20000, 0x24a50001, 
+0x822021, 0x65102b, 0x10400003, 0x0, 
+0x8f420148, 0xa22823, 0x90a20000, 0x1000002d, 
+0x21200, 0x3463ffff, 0x24a20004, 0x62102b, 
+0x5440000a, 0x65102b, 0x90a20000, 0x90a40002, 
+0x90a30001, 0x90a50003, 0x441021, 0x21200, 
+0x651821, 0x10000020, 0x432021, 0x10400003, 
+0x0, 0x8f420148, 0xa22823, 0x90a20000, 
+0x24a50001, 0x22200, 0x65102b, 0x10400003, 
+0x0, 0x8f420148, 0xa22823, 0x90a20000, 
+0x24a50001, 0x822021, 0x65102b, 0x10400003, 
+0x0, 0x8f420148, 0xa22823, 0x90a20000, 
+0x24a50001, 0x21200, 0x822021, 0x65102b, 
+0x10400003, 0x0, 0x8f420148, 0xa22823, 
+0x90a20000, 0x822021, 0x41c02, 0x3082ffff, 
+0x622021, 0x41c02, 0x3082ffff, 0x622021, 
+0x3e00008, 0x3082ffff, 0x0, 0x8f820220, 
+0x34420002, 0xaf820220, 0x3c020002, 0x8c428ff8, 
+0x30424000, 0x10400054, 0x24040001, 0x8f820200, 
+0x24067fff, 0x8f830200, 0x30450002, 0x2402fffd, 
+0x621824, 0xaf830200, 0xaf840204, 0x8f830054, 
+0x8f820054, 0x10000002, 0x24630001, 0x8f820054, 
+0x621023, 0x2c420002, 0x1440fffc, 0x0, 
+0x8f820224, 0x1444004d, 0x42040, 0xc4102b, 
+0x1040fff1, 0x0, 0x8f820200, 0x451025, 
+0xaf820200, 0x8f820220, 0x34428000, 0xaf820220, 
+0x8f830054, 0x8f820054, 0x10000002, 0x24630001, 
+0x8f820054, 0x621023, 0x2c420002, 0x1440fffc, 
+0x0, 0x8f820220, 0x3c030004, 0x431024, 
+0x1440000f, 0x0, 0x8f820220, 0x3c03ffff, 
+0x34637fff, 0x431024, 0xaf820220, 0x8f830054, 
+0x8f820054, 0x10000002, 0x24630001, 0x8f820054, 
+0x621023, 0x2c420002, 0x1440fffc, 0x0, 
+0x8f820220, 0x3c030004, 0x431024, 0x1440000d, 
+0x0, 0x8f820220, 0x34428000, 0xaf820220, 
+0x8f830054, 0x8f820054, 0x10000002, 0x24630001, 
+0x8f820054, 0x621023, 0x2c420002, 0x1440fffc, 
+0x0, 0x8f820220, 0x3c030004, 0x431024, 
+0x1040001b, 0x1021, 0x8f830220, 0x24020001, 
+0x10000015, 0x3c04f700, 0x8f820220, 0x3c04f700, 
+0x441025, 0xaf820220, 0x8f820220, 0x2403fffd, 
+0x431024, 0xaf820220, 0x8f820220, 0x3c030300, 
+0x431024, 0x14400003, 0x0, 0x10000008, 
+0x1021, 0x8f820220, 0x34420002, 0xaf820220, 
+0x8f830220, 0x24020001, 0x641825, 0xaf830220, 
+0x3e00008, 0x0, 0x2021, 0x3c050100, 
+0x24020001, 0xaf80021c, 0xaf820200, 0xaf820220, 
+0x27625000, 0xaf8200c0, 0x27625000, 0xaf8200c4, 
+0x27625000, 0xaf8200c8, 0x27625000, 0xaf8200d0, 
+0x27625000, 0xaf8200d4, 0x27625000, 0xaf8200d8, 
+0x27623000, 0xaf8200e0, 0x27623000, 0xaf8200e4, 
+0x27623000, 0xaf8200e8, 0x27622800, 0xaf8200f0, 
+0x27622800, 0xaf8200f4, 0x27622800, 0xaf8200f8, 
+0x418c0, 0x24840001, 0x3631021, 0xac453004, 
+0x3631021, 0xac403000, 0x28820200, 0x1440fff9, 
+0x418c0, 0x2021, 0x418c0, 0x24840001, 
+0x3631021, 0xac402804, 0x3631021, 0xac402800, 
+0x28820100, 0x1440fff9, 0x418c0, 0xaf80023c, 
+0x24030080, 0x24040100, 0xac600000, 0x24630004, 
+0x64102b, 0x5440fffd, 0xac600000, 0x8f830040, 
+0x3c02f000, 0x621824, 0x3c025000, 0x1062000c, 
+0x43102b, 0x14400006, 0x3c026000, 0x3c024000, 
+0x10620008, 0x24020800, 0x10000008, 0x0, 
+0x10620004, 0x24020800, 0x10000004, 0x0, 
+0x24020700, 0x3c010001, 0xac226dac, 0x3e00008, 
+0x0, 0x3c020001, 0x8c426dbc, 0x27bdffd0, 
+0xafbf002c, 0xafb20028, 0xafb10024, 0xafb00020, 
+0x3c010001, 0x10400005, 0xac206d94, 0xc004d9e, 
+0x0, 0x3c010001, 0xac206dbc, 0x8f830054, 
+0x8f820054, 0x10000002, 0x24630064, 0x8f820054, 
+0x621023, 0x2c420065, 0x1440fffc, 0x0, 
+0xc004db9, 0x0, 0x24040001, 0x2821, 
+0x27a60018, 0x34028000, 0xc0045be, 0xa7a20018, 
+0x8f830054, 0x8f820054, 0x10000002, 0x24630064, 
+0x8f820054, 0x621023, 0x2c420065, 0x1440fffc, 
+0x24040001, 0x24050001, 0xc00457c, 0x27a60018, 
+0x8f830054, 0x8f820054, 0x10000002, 0x24630064, 
+0x8f820054, 0x621023, 0x2c420065, 0x1440fffc, 
+0x24040001, 0x24050001, 0xc00457c, 0x27a60018, 
+0x8f830054, 0x8f820054, 0x10000002, 0x24630064, 
+0x8f820054, 0x621023, 0x2c420065, 0x1440fffc, 
+0x24040001, 0x3c060001, 0x24c66f24, 0xc00457c, 
+0x24050002, 0x8f830054, 0x8f820054, 0x10000002, 
+0x24630064, 0x8f820054, 0x621023, 0x2c420065, 
+0x1440fffc, 0x24040001, 0x24050003, 0x3c100001, 
+0x26106f26, 0xc00457c, 0x2003021, 0x97a60018, 
+0x3c070001, 0x94e76f24, 0x3c040001, 0x24846ae0, 
+0xafa00014, 0x96020000, 0x3c05000d, 0x34a50100, 
+0xc002b3b, 0xafa20010, 0x97a20018, 0x1040004d, 
+0x24036040, 0x96020000, 0x3042fff0, 0x1443000c, 
+0x24020020, 0x3c030001, 0x94636f24, 0x1462000b, 
+0x24027830, 0x24020003, 0x3c010001, 0xac226d94, 
+0x24020005, 0x3c010001, 0x1000003f, 0xac226f34, 
+0x3c030001, 0x94636f24, 0x24027830, 0x1462000c, 
+0x24030010, 0x3c020001, 0x94426f26, 0x3042fff0, 
+0x14430007, 0x24020003, 0x3c010001, 0xac226d94, 
+0x24020006, 0x3c010001, 0x1000002f, 0xac226f34, 
+0x3c020001, 0x8c426d94, 0x3c030001, 0x94636f24, 
+0x34420001, 0x3c010001, 0xac226d94, 0x24020015, 
+0x1462000b, 0x0, 0x3c020001, 0x94426f26, 
+0x3042fff0, 0x3843f420, 0x2c630001, 0x3842f430, 
+0x2c420001, 0x621825, 0x1460001b, 0x24020003, 
+0x3c030001, 0x94636f24, 0x24027810, 0x14620016, 
+0x24020002, 0x3c020001, 0x94426f26, 0x3042fff0, 
+0x14400011, 0x24020002, 0x1000000f, 0x24020004, 
+0x3c020001, 0x8c426d94, 0x34420008, 0x3c010001, 
+0xac226d94, 0x1000005e, 0x24020004, 0x3c020001, 
+0x8c426d94, 0x34420004, 0x3c010001, 0x100000af, 
+0xac226d94, 0x24020001, 0x3c010001, 0xac226f40, 
+0x3c020001, 0x8c426d94, 0x30420002, 0x144000b2, 
+0x3c09fff0, 0x24020e00, 0xaf820238, 0x8f840054, 
+0x8f820054, 0x24030008, 0x3c010001, 0xac236d98, 
+0x10000002, 0x248401f4, 0x8f820054, 0x821023, 
+0x2c4201f5, 0x1440fffc, 0x3c0200c8, 0x344201fb, 
+0xaf820238, 0x8f830054, 0x8f820054, 0x10000002, 
+0x246301f4, 0x8f820054, 0x621023, 0x2c4201f5, 
+0x1440fffc, 0x8021, 0x24120001, 0x24110009, 
+0xc004482, 0x0, 0x3c010001, 0xac326db4, 
+0xc004547, 0x0, 0x3c020001, 0x8c426db4, 
+0x1451fffb, 0x3c0200c8, 0x344201f6, 0xaf820238, 
+0x8f830054, 0x8f820054, 0x10000002, 0x2463000a, 
+0x8f820054, 0x621023, 0x2c42000b, 0x1440fffc, 
+0x0, 0x8f820220, 0x24040001, 0x34420002, 
+0xaf820220, 0x8f830200, 0x24057fff, 0x2402fffd, 
+0x621824, 0xaf830200, 0xaf840204, 0x8f830054, 
+0x8f820054, 0x10000002, 0x24630001, 0x8f820054, 
+0x621023, 0x2c420002, 0x1440fffc, 0x0, 
+0x8f820224, 0x14440005, 0x34028000, 0x42040, 
+0xa4102b, 0x1040fff0, 0x34028000, 0x1082ffa0, 
+0x26100001, 0x2e020014, 0x1440ffcd, 0x24020004, 
+0x3c010001, 0xac226d98, 0x8021, 0x24120009, 
+0x3c11ffff, 0x36313f7f, 0xc004482, 0x0, 
+0x24020001, 0x3c010001, 0xac226db4, 0xc004547, 
+0x0, 0x3c020001, 0x8c426db4, 0x1452fffb, 
+0x0, 0x8f820044, 0x511024, 0x34425080, 
+0xaf820044, 0x8f830054, 0x8f820054, 0x10000002, 
+0x2463000a, 0x8f820054, 0x621023, 0x2c42000b, 
+0x1440fffc, 0x0, 0x8f820044, 0x511024, 
+0x3442f080, 0xaf820044, 0x8f830054, 0x8f820054, 
+0x10000002, 0x2463000a, 0x8f820054, 0x621023, 
+0x2c42000b, 0x1440fffc, 0x0, 0x8f820220, 
+0x3c03f700, 0x431025, 0xaf820220, 0x8f830054, 
+0x8f820054, 0x10000002, 0x24630064, 0x8f820054, 
+0x621023, 0x2c420065, 0x1440fffc, 0x0, 
+0x8f820220, 0x24040001, 0x34420002, 0xaf820220, 
+0x8f830200, 0x24057fff, 0x2402fffd, 0x621824, 
+0xaf830200, 0xaf840204, 0x8f830054, 0x8f820054, 
+0x10000002, 0x24630001, 0x8f820054, 0x621023, 
+0x2c420002, 0x1440fffc, 0x0, 0x8f820224, 
+0x14440005, 0x34028000, 0x42040, 0xa4102b, 
+0x1040fff0, 0x34028000, 0x1082ff50, 0x26100001, 
+0x2e020064, 0x1440ffb0, 0x0, 0x3c020001, 
+0x8c426d94, 0x30420004, 0x14400007, 0x3c09fff0, 
+0x8f820044, 0x3c03ffff, 0x34633f7f, 0x431024, 
+0xaf820044, 0x3c09fff0, 0x3529bdc0, 0x3c060001, 
+0x8cc66d94, 0x3c040001, 0x24846ae0, 0x24020001, 
+0x3c010001, 0xac226d9c, 0x8f820054, 0x3c070001, 
+0x8ce76f40, 0x3c030001, 0x94636f24, 0x3c080001, 
+0x95086f26, 0x3c05000d, 0x34a50100, 0x3c010001, 
+0xac206d98, 0x491021, 0x3c010001, 0xac226f30, 
+0xafa30010, 0xc002b3b, 0xafa80014, 0x8fbf002c, 
+0x8fb20028, 0x8fb10024, 0x8fb00020, 0x3e00008, 
+0x27bd0030, 0x27bdffe8, 0x3c050001, 0x8ca56d98, 
+0x24060004, 0x24020001, 0x14a20014, 0xafbf0010, 
+0x3c020002, 0x8c428ffc, 0x30428000, 0x10400005, 
+0x3c04000f, 0x3c030001, 0x8c636f40, 0x10000005, 
+0x34844240, 0x3c040004, 0x3c030001, 0x8c636f40, 
+0x348493e0, 0x24020005, 0x14620016, 0x0, 
+0x3c04003d, 0x10000013, 0x34840900, 0x3c020002, 
+0x8c428ff8, 0x30428000, 0x10400005, 0x3c04001e, 
+0x3c030001, 0x8c636f40, 0x10000005, 0x34848480, 
+0x3c04000f, 0x3c030001, 0x8c636f40, 0x34844240, 
+0x24020005, 0x14620003, 0x0, 0x3c04007a, 
+0x34841200, 0x3c020001, 0x8c426f30, 0x8f830054, 
+0x441021, 0x431023, 0x44102b, 0x1440004c, 
+0x0, 0x3c020001, 0x8c426da0, 0x14400048, 
+0x0, 0x3c010001, 0x10c00025, 0xac206db0, 
+0x3c090001, 0x8d296d94, 0x24070001, 0x3c044000, 
+0x3c080002, 0x25088ffc, 0x250afffc, 0x52842, 
+0x14a00002, 0x24c6ffff, 0x24050008, 0xa91024, 
+0x10400010, 0x0, 0x14a70008, 0x0, 
+0x8d020000, 0x441024, 0x1040000a, 0x0, 
+0x3c010001, 0x10000007, 0xac256db0, 0x8d420000, 
+0x441024, 0x10400003, 0x0, 0x3c010001, 
+0xac276db0, 0x3c020001, 0x8c426db0, 0x6182b, 
+0x2c420001, 0x431024, 0x5440ffe5, 0x52842, 
+0x8f820054, 0x3c030001, 0x8c636db0, 0x3c010001, 
+0xac226f30, 0x1060003b, 0x24020005, 0x3c030001, 
+0x8c636f40, 0x3c010001, 0xac256d98, 0x14620012, 
+0x24020001, 0x3c020002, 0x8c428ff8, 0x3c032000, 
+0x34635000, 0x431024, 0x14400006, 0x24020001, 
+0x3c010001, 0xac206f1c, 0x3c010001, 0xac226d98, 
+0x24020001, 0x3c010001, 0xac226e24, 0x3c010001, 
+0xac226da4, 0x24020001, 0x3c010001, 0xac226d9c, 
+0x3c020001, 0x8c426db0, 0x1040001e, 0x0, 
+0x3c020001, 0x8c426d9c, 0x10400008, 0x24020001, 
+0x3c010001, 0xac206d9c, 0xaee204b8, 0x3c010001, 
+0xac206e1c, 0x3c010001, 0xac226dd4, 0x8ee304b8, 
+0x24020008, 0x10620005, 0x24020001, 0xc004239, 
+0x0, 0x1000000b, 0x0, 0x3c030001, 
+0x8c636d98, 0x10620007, 0x2402000e, 0x3c030002, 
+0x8c638f90, 0x10620003, 0x0, 0xc004e9c, 
+0x8f840220, 0x8fbf0010, 0x3e00008, 0x27bd0018, 
+0x27bdffe0, 0x3c03fdff, 0x3c040001, 0x8c846d98, 
+0x3c020001, 0x8c426dc0, 0x3463ffff, 0x283a024, 
+0x14820006, 0xafbf0018, 0x8ee304b8, 0x3c020001, 
+0x8c426dc4, 0x10620006, 0x0, 0x8ee204b8, 
+0x3c010001, 0xac246dc0, 0x3c010001, 0xac226dc4, 
+0x3c030001, 0x8c636d98, 0x24020002, 0x1062019c, 
+0x2c620003, 0x10400005, 0x24020001, 0x1062000a, 
+0x0, 0x10000226, 0x0, 0x24020004, 
+0x106200b6, 0x24020008, 0x1062010a, 0x24020001, 
+0x1000021f, 0x0, 0x8ee204b8, 0x2443ffff, 
+0x2c620008, 0x1040021c, 0x31080, 0x3c010001, 
+0x220821, 0x8c226af8, 0x400008, 0x0, 
+0x3c030001, 0x8c636f40, 0x24020005, 0x14620010, 
+0x0, 0x3c020001, 0x8c426da4, 0x10400008, 
+0x24020003, 0xc004482, 0x0, 0x24020002, 
+0xaee204b8, 0x3c010001, 0x10000002, 0xac206da4, 
+0xaee204b8, 0x3c010001, 0x10000203, 0xac206d30, 
+0xc004482, 0x0, 0x3c020001, 0x8c426da4, 
+0x3c010001, 0xac206d30, 0x1440017a, 0x24020002, 
+0x1000019d, 0x24020007, 0x3c030001, 0x8c636f40, 
+0x24020005, 0x14620003, 0x24020001, 0x3c010001, 
+0xac226dd0, 0xc0045ff, 0x0, 0x3c030001, 
+0x8c636dd0, 0x10000174, 0x24020011, 0x3c050001, 
+0x8ca56d98, 0x3c060002, 0x8cc68ffc, 0xc005104, 
+0x2021, 0x24020005, 0x3c010001, 0xac206da4, 
+0x100001e1, 0xaee204b8, 0x3c040001, 0x24846aec, 
+0x3c05000f, 0x34a50100, 0x3021, 0x3821, 
+0xafa00010, 0xc002b3b, 0xafa00014, 0x100001d6, 
+0x0, 0x8f820220, 0x3c030004, 0x431024, 
+0x14400175, 0x24020007, 0x8f830054, 0x3c020001, 
+0x8c426f28, 0x2463d8f0, 0x431023, 0x2c422710, 
+0x14400003, 0x24020001, 0x3c010001, 0xac226d9c, 
+0x3c020002, 0x8c428ffc, 0x30425000, 0x104001c2, 
+0x0, 0x8f820220, 0x30428000, 0x1040017d, 
+0x0, 0x10000175, 0x0, 0x3c050001, 
+0x8ca56d98, 0xc00529b, 0x2021, 0xc00551b, 
+0x2021, 0x3c030002, 0x8c638ff4, 0x46101b0, 
+0x24020001, 0x3c020008, 0x621024, 0x10400006, 
+0x0, 0x8f820214, 0x3c03ffff, 0x431024, 
+0x10000005, 0x3442251f, 0x8f820214, 0x3c03ffff, 
+0x431024, 0x3442241f, 0xaf820214, 0x8f820220, 
+0x3c030200, 0x34420002, 0xaf820220, 0x24020008, 
+0xaee204b8, 0x8f820220, 0x283a025, 0x3c030004, 
+0x431024, 0x14400016, 0x0, 0x3c020002, 
+0x8c428ffc, 0x30425000, 0x1040000d, 0x0, 
+0x8f820220, 0x30428000, 0x10400006, 0x0, 
+0x8f820220, 0x3c03ffff, 0x34637fff, 0x10000003, 
+0x431024, 0x8f820220, 0x34428000, 0xaf820220, 
+0x8f820220, 0x3c03f700, 0x431025, 0xaf820220, 
+0x3c030001, 0x8c636f40, 0x24020005, 0x1462000a, 
+0x0, 0x3c020001, 0x94426f26, 0x24429fbc, 
+0x2c420004, 0x10400004, 0x24040018, 0x24050002, 
+0xc004ddb, 0x24060020, 0xc003e6d, 0x0, 
+0x3c010001, 0x10000170, 0xac206e20, 0x8ee204b8, 
+0x2443ffff, 0x2c620008, 0x1040016b, 0x31080, 
+0x3c010001, 0x220821, 0x8c226b18, 0x400008, 
+0x0, 0xc004547, 0x0, 0x3c030001, 
+0x8c636db4, 0x100000e8, 0x24020009, 0x3c020002, 
+0x8c428ff8, 0x30424000, 0x10400004, 0x0, 
+0x8f820044, 0x10000006, 0x3442f080, 0x8f820044, 
+0x3c03ffff, 0x34633f7f, 0x431024, 0x3442a080, 
+0xaf820044, 0x8f830054, 0x100000ea, 0x24020004, 
+0x8f830054, 0x3c020001, 0x8c426f28, 0x2463d8f0, 
+0x431023, 0x2c422710, 0x14400147, 0x24020005, 
+0x100000d8, 0x0, 0x8f820220, 0x3c03f700, 
+0x431025, 0xaf820220, 0xaf800204, 0x3c010002, 
+0x100000d6, 0xac208fe0, 0x8f830054, 0x3c020001, 
+0x8c426f28, 0x2463fff6, 0x431023, 0x2c42000a, 
+0x14400135, 0x24020007, 0x100000d7, 0x0, 
+0xc003f50, 0x0, 0x1040012d, 0x24020001, 
+0x8f820214, 0x3c03ffff, 0x3c040001, 0x8c846f1c, 
+0x431024, 0x3442251f, 0xaf820214, 0x24020008, 
+0x10800005, 0xaee204b8, 0x3c020001, 0x8c426e44, 
+0x10400064, 0x24020001, 0x8f820220, 0x3c030008, 
+0x431024, 0x1040006a, 0x3c020200, 0x10000078, 
+0x0, 0x8ee204b8, 0x2443ffff, 0x2c620007, 
+0x10400115, 0x31080, 0x3c010001, 0x220821, 
+0x8c226b38, 0x400008, 0x0, 0xc003daf, 
+0x0, 0x3c010001, 0xac206d9c, 0xaf800204, 
+0x3c010002, 0xc004482, 0xac208fe0, 0x24020001, 
+0x3c010001, 0xac226db4, 0x24020002, 0x10000102, 
+0xaee204b8, 0xc004547, 0x0, 0x3c030001, 
+0x8c636db4, 0x10000084, 0x24020009, 0x3c020002, 
+0x8c428ff8, 0x30424000, 0x10400003, 0x3c0200c8, 
+0x10000002, 0x344201f6, 0x344201fe, 0xaf820238, 
+0x8f830054, 0x1000008b, 0x24020004, 0x8f830054, 
+0x3c020001, 0x8c426f28, 0x2463d8f0, 0x431023, 
+0x2c422710, 0x144000e8, 0x24020005, 0x10000079, 
+0x0, 0x8f820220, 0x3c03f700, 0x431025, 
+0xaf820220, 0xaf800204, 0x3c010002, 0x10000077, 
+0xac208fe0, 0x8f830054, 0x3c020001, 0x8c426f28, 
+0x2463fff6, 0x431023, 0x2c42000a, 0x144000d6, 
+0x24020007, 0x10000078, 0x0, 0xc003f50, 
+0x0, 0x104000ce, 0x24020001, 0x8f820214, 
+0x3c03ffff, 0x3c040001, 0x8c846f1c, 0x431024, 
+0x3442251f, 0xaf820214, 0x24020008, 0x1080000f, 
+0xaee204b8, 0x3c020001, 0x8c426e44, 0x1440000b, 
+0x0, 0x8f820220, 0x34420002, 0xaf820220, 
+0x24020001, 0x3c010002, 0xac228f90, 0xc004e9c, 
+0x8f840220, 0x10000016, 0x0, 0x8f820220, 
+0x3c030008, 0x431024, 0x14400011, 0x3c020200, 
+0x282a025, 0x2402000e, 0x3c010002, 0xac228f90, 
+0xc00551b, 0x2021, 0x8f820220, 0x34420002, 
+0xc003e6d, 0xaf820220, 0x3c050001, 0x8ca56d98, 
+0xc00529b, 0x2021, 0x100000a3, 0x0, 
+0x3c020001, 0x8c426e44, 0x1040009f, 0x0, 
+0x3c020001, 0x8c426e40, 0x2442ffff, 0x3c010001, 
+0xac226e40, 0x14400098, 0x24020002, 0x3c010001, 
+0xac206e44, 0x3c010001, 0x10000093, 0xac226e40, 
+0x8ee204b8, 0x2443ffff, 0x2c620007, 0x1040008e, 
+0x31080, 0x3c010001, 0x220821, 0x8c226b58, 
+0x400008, 0x0, 0x3c020001, 0x8c426da4, 
+0x10400018, 0x24020005, 0xc004482, 0x0, 
+0x24020002, 0xaee204b8, 0x3c010001, 0x1000007e, 
+0xac206da4, 0xc004963, 0x0, 0x3c030001, 
+0x8c636dd4, 0x24020006, 0x14620077, 0x24020003, 
+0x10000075, 0xaee204b8, 0x3c050001, 0x8ca56d98, 
+0x3c060002, 0x8cc68ff8, 0xc005104, 0x2021, 
+0x24020005, 0x1000006c, 0xaee204b8, 0x8f820220, 
+0x3c03f700, 0x431025, 0xaf820220, 0x8f830054, 
+0x24020006, 0xaee204b8, 0x3c010001, 0x10000062, 
+0xac236f28, 0x8f820220, 0x3c030004, 0x431024, 
+0x10400003, 0x24020007, 0x1000005b, 0xaee204b8, 
+0x8f830054, 0x3c020001, 0x8c426f28, 0x2463d8f0, 
+0x431023, 0x2c422710, 0x14400003, 0x24020001, 
+0x3c010001, 0xac226d9c, 0x3c020002, 0x8c428ff8, 
+0x30425000, 0x1040004c, 0x0, 0x8f820220, 
+0x30428000, 0x10400007, 0x0, 0x8f820220, 
+0x3c03ffff, 0x34637fff, 0x431024, 0x10000042, 
+0xaf820220, 0x8f820220, 0x34428000, 0x1000003e, 
+0xaf820220, 0x3c050001, 0x8ca56d98, 0xc00529b, 
+0x2021, 0xc00551b, 0x2021, 0x3c020002, 
+0x8c428ff0, 0x4410032, 0x24020001, 0x8f820214, 
+0x3c03ffff, 0x431024, 0x3442251f, 0xaf820214, 
+0x24020008, 0xaee204b8, 0x8f820220, 0x34420002, 
+0xaf820220, 0x8f820220, 0x3c030004, 0x431024, 
+0x14400016, 0x0, 0x3c020002, 0x8c428ff8, 
+0x30425000, 0x1040000d, 0x0, 0x8f820220, 
+0x30428000, 0x10400006, 0x0, 0x8f820220, 
+0x3c03ffff, 0x34637fff, 0x10000003, 0x431024, 
+0x8f820220, 0x34428000, 0xaf820220, 0x8f820220, 
+0x3c03f700, 0x431025, 0xaf820220, 0x3c020001, 
+0x94426f26, 0x24429fbc, 0x2c420004, 0x10400004, 
+0x24040018, 0x24050002, 0xc004ddb, 0x24060020, 
+0xc003e6d, 0x0, 0x10000003, 0x0, 
+0x3c010001, 0xac226d9c, 0x8fbf0018, 0x3e00008, 
+0x27bd0020, 0x8f820200, 0x8f820220, 0x8f820220, 
+0x34420004, 0xaf820220, 0x8f820200, 0x3c050001, 
+0x8ca56d98, 0x34420004, 0xaf820200, 0x24020002, 
+0x10a2004b, 0x2ca20003, 0x10400005, 0x24020001, 
+0x10a2000a, 0x0, 0x100000b1, 0x0, 
+0x24020004, 0x10a20072, 0x24020008, 0x10a20085, 
+0x3c02f0ff, 0x100000aa, 0x0, 0x8f830050, 
+0x3c02f0ff, 0x3442ffff, 0x3c040001, 0x8c846f40, 
+0x621824, 0x3c020700, 0x621825, 0x24020e00, 
+0x2484fffb, 0x2c840002, 0xaf830050, 0xaf850200, 
+0xaf850220, 0x14800006, 0xaf820238, 0x8f820044, 
+0x3c03ffff, 0x34633f7f, 0x431024, 0xaf820044, 
+0x3c030001, 0x8c636f40, 0x24020005, 0x14620004, 
+0x0, 0x8f820044, 0x34425000, 0xaf820044, 
+0x3c020001, 0x8c426d88, 0x3c030001, 0x8c636f40, 
+0x34420022, 0x2463fffc, 0x2c630002, 0x1460000c, 
+0xaf820200, 0x3c020001, 0x8c426dac, 0x3c030001, 
+0x8c636d90, 0x3c040001, 0x8c846d8c, 0x34428000, 
+0x621825, 0x641825, 0x1000000a, 0x34620002, 
+0x3c020001, 0x8c426d90, 0x3c030001, 0x8c636dac, 
+0x3c040001, 0x8c846d8c, 0x431025, 0x441025, 
+0x34420002, 0xaf820220, 0x1000002f, 0x24020001, 
+0x24020e01, 0xaf820238, 0x8f830050, 0x3c02f0ff, 
+0x3442ffff, 0x3c040001, 0x8c846f1c, 0x621824, 
+0x3c020d00, 0x621825, 0x24020001, 0xaf830050, 
+0xaf820200, 0xaf820220, 0x10800005, 0x3c033f00, 
+0x3c020001, 0x8c426d80, 0x10000004, 0x34630070, 
+0x3c020001, 0x8c426d80, 0x34630072, 0x431025, 
+0xaf820200, 0x3c030001, 0x8c636d84, 0x3c02f700, 
+0x621825, 0x3c020001, 0x8c426d90, 0x3c040001, 
+0x8c846dac, 0x3c050001, 0x8ca56f40, 0x431025, 
+0x441025, 0xaf820220, 0x24020005, 0x14a20006, 
+0x24020001, 0x8f820044, 0x2403afff, 0x431024, 
+0xaf820044, 0x24020001, 0x1000003d, 0xaf820238, 
+0x8f830050, 0x3c02f0ff, 0x3442ffff, 0x3c040001, 
+0x8c846f1c, 0x621824, 0x3c020a00, 0x621825, 
+0x24020001, 0xaf830050, 0xaf820200, 0x1080001e, 
+0xaf820220, 0x3c020001, 0x8c426e44, 0x1440001a, 
+0x3c033f00, 0x3c020001, 0x8c426d80, 0x1000001a, 
+0x346300e0, 0x8f830050, 0x3c040001, 0x8c846f1c, 
+0x3442ffff, 0x621824, 0x1080000f, 0xaf830050, 
+0x3c020001, 0x8c426e44, 0x1440000b, 0x3c043f00, 
+0x3c030001, 0x8c636d80, 0x348400e0, 0x24020001, 
+0xaf820200, 0xaf820220, 0x641825, 0xaf830200, 
+0x10000008, 0x3c05f700, 0x3c020001, 0x8c426d80, 
+0x3c033f00, 0x346300e2, 0x431025, 0xaf820200, 
+0x3c05f700, 0x34a58000, 0x3c030001, 0x8c636d84, 
+0x3c020001, 0x8c426d90, 0x3c040001, 0x8c846dac, 
+0x651825, 0x431025, 0x441025, 0xaf820220, 
+0x3e00008, 0x0, 0x3c030001, 0x8c636db4, 
+0x3c020001, 0x8c426db8, 0x10620003, 0x24020002, 
+0x3c010001, 0xac236db8, 0x1062001d, 0x2c620003, 
+0x10400025, 0x24020001, 0x14620023, 0x24020004, 
+0x3c030001, 0x8c636d98, 0x10620006, 0x24020008, 
+0x1462000c, 0x3c0200c8, 0x344201fb, 0x10000009, 
+0xaf820238, 0x24020e01, 0xaf820238, 0x8f820044, 
+0x3c03ffff, 0x34633f7f, 0x431024, 0x34420080, 
+0xaf820044, 0x8f830054, 0x24020002, 0x3c010001, 
+0xac226db4, 0x3c010001, 0x1000000b, 0xac236f2c, 
+0x8f830054, 0x3c020001, 0x8c426f2c, 0x2463d8f0, 
+0x431023, 0x2c422710, 0x14400003, 0x24020009, 
+0x3c010001, 0xac226db4, 0x3e00008, 0x0, 
+0x0, 0x0, 0x0, 0x27bdffd8, 
+0xafb20018, 0x809021, 0xafb3001c, 0xa09821, 
+0xafb10014, 0xc08821, 0xafb00010, 0x8021, 
+0xafbf0020, 0xa6200000, 0xc004d78, 0x24040001, 
+0x26100001, 0x2e020020, 0x1440fffb, 0x0, 
+0xc004d78, 0x2021, 0xc004d78, 0x24040001, 
+0xc004d78, 0x24040001, 0xc004d78, 0x2021, 
+0x24100010, 0x2501024, 0x10400002, 0x2021, 
+0x24040001, 0xc004d78, 0x108042, 0x1600fffa, 
+0x2501024, 0x24100010, 0x2701024, 0x10400002, 
+0x2021, 0x24040001, 0xc004d78, 0x108042, 
+0x1600fffa, 0x2701024, 0xc004db9, 0x34108000, 
+0xc004db9, 0x0, 0xc004d58, 0x0, 
+0x50400005, 0x108042, 0x96220000, 0x501025, 
+0xa6220000, 0x108042, 0x1600fff7, 0x0, 
+0xc004db9, 0x0, 0x8fbf0020, 0x8fb3001c, 
+0x8fb20018, 0x8fb10014, 0x8fb00010, 0x3e00008, 
+0x27bd0028, 0x27bdffd8, 0xafb10014, 0x808821, 
+0xafb20018, 0xa09021, 0xafb3001c, 0xc09821, 
+0xafb00010, 0x8021, 0xafbf0020, 0xc004d78, 
+0x24040001, 0x26100001, 0x2e020020, 0x1440fffb, 
+0x0, 0xc004d78, 0x2021, 0xc004d78, 
+0x24040001, 0xc004d78, 0x2021, 0xc004d78, 
+0x24040001, 0x24100010, 0x2301024, 0x10400002, 
+0x2021, 0x24040001, 0xc004d78, 0x108042, 
+0x1600fffa, 0x2301024, 0x24100010, 0x2501024, 
+0x10400002, 0x2021, 0x24040001, 0xc004d78, 
+0x108042, 0x1600fffa, 0x2501024, 0xc004d78, 
+0x24040001, 0xc004d78, 0x2021, 0x34108000, 
+0x96620000, 0x501024, 0x10400002, 0x2021, 
+0x24040001, 0xc004d78, 0x108042, 0x1600fff8, 
+0x0, 0xc004db9, 0x0, 0x8fbf0020, 
+0x8fb3001c, 0x8fb20018, 0x8fb10014, 0x8fb00010, 
+0x3e00008, 0x27bd0028, 0x3c040001, 0x8c846dd0, 
+0x3c020001, 0x8c426e18, 0x27bdffd8, 0xafbf0020, 
+0xafb1001c, 0x10820003, 0xafb00018, 0x3c010001, 
+0xac246e18, 0x3c030001, 0x8c636f40, 0x24020005, 
+0x14620005, 0x2483ffff, 0xc004963, 0x0, 
+0x1000034c, 0x0, 0x2c620013, 0x10400349, 
+0x31080, 0x3c010001, 0x220821, 0x8c226b80, 
+0x400008, 0x0, 0xc004db9, 0x8021, 
+0x34028000, 0xa7a20010, 0x27b10010, 0xc004d78, 
+0x24040001, 0x26100001, 0x2e020020, 0x1440fffb, 
+0x0, 0xc004d78, 0x2021, 0xc004d78, 
+0x24040001, 0xc004d78, 0x2021, 0xc004d78, 
+0x24040001, 0x24100010, 0x32020001, 0x10400002, 
+0x2021, 0x24040001, 0xc004d78, 0x108042, 
+0x1600fffa, 0x32020001, 0x24100010, 0xc004d78, 
+0x2021, 0x108042, 0x1600fffc, 0x0, 
+0xc004d78, 0x24040001, 0xc004d78, 0x2021, 
+0x34108000, 0x96220000, 0x501024, 0x10400002, 
+0x2021, 0x24040001, 0xc004d78, 0x108042, 
+0x1600fff8, 0x0, 0xc004db9, 0x0, 
+0x1000030e, 0x24020002, 0x27b10010, 0xa7a00010, 
+0x8021, 0xc004d78, 0x24040001, 0x26100001, 
+0x2e020020, 0x1440fffb, 0x0, 0xc004d78, 
+0x2021, 0xc004d78, 0x24040001, 0xc004d78, 
+0x24040001, 0xc004d78, 0x2021, 0x24100010, 
+0x32020001, 0x10400002, 0x2021, 0x24040001, 
+0xc004d78, 0x108042, 0x1600fffa, 0x32020001, 
+0x24100010, 0xc004d78, 0x2021, 0x108042, 
+0x1600fffc, 0x0, 0xc004db9, 0x34108000, 
+0xc004db9, 0x0, 0xc004d58, 0x0, 
+0x50400005, 0x108042, 0x96220000, 0x501025, 
+0xa6220000, 0x108042, 0x1600fff7, 0x0, 
+0xc004db9, 0x0, 0x97a20010, 0x30428000, 
+0x144002dc, 0x24020003, 0x100002d8, 0x0, 
+0x24021200, 0xa7a20010, 0x27b10010, 0x8021, 
+0xc004d78, 0x24040001, 0x26100001, 0x2e020020, 
+0x1440fffb, 0x0, 0xc004d78, 0x2021, 
+0xc004d78, 0x24040001, 0xc004d78, 0x2021, 
+0xc004d78, 0x24040001, 0x24100010, 0x32020001, 
+0x10400002, 0x2021, 0x24040001, 0xc004d78, 
+0x108042, 0x1600fffa, 0x32020001, 0x24100010, 
+0xc004d78, 0x2021, 0x108042, 0x1600fffc, 
+0x0, 0xc004d78, 0x24040001, 0xc004d78, 
+0x2021, 0x34108000, 0x96220000, 0x501024, 
+0x10400002, 0x2021, 0x24040001, 0xc004d78, 
+0x108042, 0x1600fff8, 0x0, 0xc004db9, 
+0x0, 0x8f830054, 0x10000296, 0x24020004, 
+0x8f830054, 0x3c020001, 0x8c426f3c, 0x2463ff9c, 
+0x431023, 0x2c420064, 0x1440029e, 0x24020002, 
+0x3c030001, 0x8c636f40, 0x10620297, 0x2c620003, 
+0x14400296, 0x24020011, 0x24020003, 0x10620005, 
+0x24020004, 0x10620291, 0x2402000f, 0x1000028f, 
+0x24020011, 0x1000028d, 0x24020005, 0x24020014, 
+0xa7a20010, 0x27b10010, 0x8021, 0xc004d78, 
+0x24040001, 0x26100001, 0x2e020020, 0x1440fffb, 
+0x0, 0xc004d78, 0x2021, 0xc004d78, 
+0x24040001, 0xc004d78, 0x2021, 0xc004d78, 
+0x24040001, 0x24100010, 0x32020001, 0x10400002, 
+0x2021, 0x24040001, 0xc004d78, 0x108042, 
+0x1600fffa, 0x32020001, 0x24100010, 0x32020012, 
+0x10400002, 0x2021, 0x24040001, 0xc004d78, 
+0x108042, 0x1600fffa, 0x32020012, 0xc004d78, 
+0x24040001, 0xc004d78, 0x2021, 0x34108000, 
+0x96220000, 0x501024, 0x10400002, 0x2021, 
+0x24040001, 0xc004d78, 0x108042, 0x1600fff8, 
+0x0, 0xc004db9, 0x0, 0x8f830054, 
+0x10000248, 0x24020006, 0x8f830054, 0x3c020001, 
+0x8c426f3c, 0x2463ff9c, 0x431023, 0x2c420064, 
+0x14400250, 0x24020007, 0x1000024c, 0x0, 
+0x24020006, 0xa7a20010, 0x27b10010, 0x8021, 
+0xc004d78, 0x24040001, 0x26100001, 0x2e020020, 
+0x1440fffb, 0x0, 0xc004d78, 0x2021, 
+0xc004d78, 0x24040001, 0xc004d78, 0x2021, 
+0xc004d78, 0x24040001, 0x24100010, 0x32020001, 
+0x10400002, 0x2021, 0x24040001, 0xc004d78, 
+0x108042, 0x1600fffa, 0x32020001, 0x24100010, 
+0x32020013, 0x10400002, 0x2021, 0x24040001, 
+0xc004d78, 0x108042, 0x1600fffa, 0x32020013, 
+0xc004d78, 0x24040001, 0xc004d78, 0x2021, 
+0x34108000, 0x96220000, 0x501024, 0x10400002, 
+0x2021, 0x24040001, 0xc004d78, 0x108042, 
+0x1600fff8, 0x0, 0xc004db9, 0x0, 
+0x8f830054, 0x10000207, 0x24020008, 0x8f830054, 
+0x3c020001, 0x8c426f3c, 0x2463ff9c, 0x431023, 
+0x2c420064, 0x1440020f, 0x24020009, 0x1000020b, 
+0x0, 0x27b10010, 0xa7a00010, 0x8021, 
+0xc004d78, 0x24040001, 0x26100001, 0x2e020020, 
+0x1440fffb, 0x0, 0xc004d78, 0x2021, 
+0xc004d78, 0x24040001, 0xc004d78, 0x24040001, 
+0xc004d78, 0x2021, 0x24100010, 0x32020001, 
+0x10400002, 0x2021, 0x24040001, 0xc004d78, 
+0x108042, 0x1600fffa, 0x32020001, 0x24100010, 
+0x32020018, 0x10400002, 0x2021, 0x24040001, 
+0xc004d78, 0x108042, 0x1600fffa, 0x32020018, 
+0xc004db9, 0x34108000, 0xc004db9, 0x0, 
+0xc004d58, 0x0, 0x50400005, 0x108042, 
+0x96220000, 0x501025, 0xa6220000, 0x108042, 
+0x1600fff7, 0x0, 0xc004db9, 0x8021, 
+0x97a20010, 0x27b10010, 0x34420001, 0xa7a20010, 
+0xc004d78, 0x24040001, 0x26100001, 0x2e020020, 
+0x1440fffb, 0x0, 0xc004d78, 0x2021, 
+0xc004d78, 0x24040001, 0xc004d78, 0x2021, 
+0xc004d78, 0x24040001, 0x24100010, 0x32020001, 
+0x10400002, 0x2021, 0x24040001, 0xc004d78, 
+0x108042, 0x1600fffa, 0x32020001, 0x24100010, 
+0x32020018, 0x10400002, 0x2021, 0x24040001, 
+0xc004d78, 0x108042, 0x1600fffa, 0x32020018, 
+0xc004d78, 0x24040001, 0xc004d78, 0x2021, 
+0x34108000, 0x96220000, 0x501024, 0x10400002, 
+0x2021, 0x24040001, 0xc004d78, 0x108042, 
+0x1600fff8, 0x0, 0xc004db9, 0x0, 
+0x8f830054, 0x10000193, 0x2402000a, 0x8f830054, 
+0x3c020001, 0x8c426f3c, 0x2463ff9c, 0x431023, 
+0x2c420064, 0x1440019b, 0x2402000b, 0x10000197, 
+0x0, 0x27b10010, 0xa7a00010, 0x8021, 
+0xc004d78, 0x24040001, 0x26100001, 0x2e020020, 
+0x1440fffb, 0x0, 0xc004d78, 0x2021, 
+0xc004d78, 0x24040001, 0xc004d78, 0x24040001, 
+0xc004d78, 0x2021, 0x24100010, 0x32020001, 
+0x10400002, 0x2021, 0x24040001, 0xc004d78, 
+0x108042, 0x1600fffa, 0x32020001, 0x24100010, 
+0x32020017, 0x10400002, 0x2021, 0x24040001, 
+0xc004d78, 0x108042, 0x1600fffa, 0x32020017, 
+0xc004db9, 0x34108000, 0xc004db9, 0x0, 
+0xc004d58, 0x0, 0x50400005, 0x108042, 
+0x96220000, 0x501025, 0xa6220000, 0x108042, 
+0x1600fff7, 0x0, 0xc004db9, 0x8021, 
+0x97a20010, 0x27b10010, 0x34420700, 0xa7a20010, 
+0xc004d78, 0x24040001, 0x26100001, 0x2e020020, 
+0x1440fffb, 0x0, 0xc004d78, 0x2021, 
+0xc004d78, 0x24040001, 0xc004d78, 0x2021, 
+0xc004d78, 0x24040001, 0x24100010, 0x32020001, 
+0x10400002, 0x2021, 0x24040001, 0xc004d78, 
+0x108042, 0x1600fffa, 0x32020001, 0x24100010, 
+0x32020017, 0x10400002, 0x2021, 0x24040001, 
+0xc004d78, 0x108042, 0x1600fffa, 0x32020017, 
+0xc004d78, 0x24040001, 0xc004d78, 0x2021, 
+0x34108000, 0x96220000, 0x501024, 0x10400002, 
+0x2021, 0x24040001, 0xc004d78, 0x108042, 
+0x1600fff8, 0x0, 0xc004db9, 0x0, 
+0x8f830054, 0x1000011f, 0x2402000c, 0x8f830054, 
+0x3c020001, 0x8c426f3c, 0x2463ff9c, 0x431023, 
+0x2c420064, 0x14400127, 0x24020012, 0x10000123, 
+0x0, 0x27b10010, 0xa7a00010, 0x8021, 
+0xc004d78, 0x24040001, 0x26100001, 0x2e020020, 
+0x1440fffb, 0x0, 0xc004d78, 0x2021, 
+0xc004d78, 0x24040001, 0xc004d78, 0x24040001, 
+0xc004d78, 0x2021, 0x24100010, 0x32020001, 
+0x10400002, 0x2021, 0x24040001, 0xc004d78, 
+0x108042, 0x1600fffa, 0x32020001, 0x24100010, 
+0x32020014, 0x10400002, 0x2021, 0x24040001, 
+0xc004d78, 0x108042, 0x1600fffa, 0x32020014, 
+0xc004db9, 0x34108000, 0xc004db9, 0x0, 
+0xc004d58, 0x0, 0x50400005, 0x108042, 
+0x96220000, 0x501025, 0xa6220000, 0x108042, 
+0x1600fff7, 0x0, 0xc004db9, 0x8021, 
+0x97a20010, 0x27b10010, 0x34420010, 0xa7a20010, 
+0xc004d78, 0x24040001, 0x26100001, 0x2e020020, 
+0x1440fffb, 0x0, 0xc004d78, 0x2021, 
+0xc004d78, 0x24040001, 0xc004d78, 0x2021, 
+0xc004d78, 0x24040001, 0x24100010, 0x32020001, 
+0x10400002, 0x2021, 0x24040001, 0xc004d78, 
+0x108042, 0x1600fffa, 0x32020001, 0x24100010, 
+0x32020014, 0x10400002, 0x2021, 0x24040001, 
+0xc004d78, 0x108042, 0x1600fffa, 0x32020014, 
+0xc004d78, 0x24040001, 0xc004d78, 0x2021, 
+0x34108000, 0x96220000, 0x501024, 0x10400002, 
+0x2021, 0x24040001, 0xc004d78, 0x108042, 
+0x1600fff8, 0x0, 0xc004db9, 0x0, 
+0x8f830054, 0x100000ab, 0x24020013, 0x8f830054, 
+0x3c020001, 0x8c426f3c, 0x2463ff9c, 0x431023, 
+0x2c420064, 0x144000b3, 0x2402000d, 0x100000af, 
+0x0, 0x27b10010, 0xa7a00010, 0x8021, 
+0xc004d78, 0x24040001, 0x26100001, 0x2e020020, 
+0x1440fffb, 0x0, 0xc004d78, 0x2021, 
+0xc004d78, 0x24040001, 0xc004d78, 0x24040001, 
+0xc004d78, 0x2021, 0x24100010, 0x32020001, 
+0x10400002, 0x2021, 0x24040001, 0xc004d78, 
+0x108042, 0x1600fffa, 0x32020001, 0x24100010, 
+0x32020018, 0x10400002, 0x2021, 0x24040001, 
+0xc004d78, 0x108042, 0x1600fffa, 0x32020018, 
+0xc004db9, 0x34108000, 0xc004db9, 0x0, 
+0xc004d58, 0x0, 0x50400005, 0x108042, 
+0x96220000, 0x501025, 0xa6220000, 0x108042, 
+0x1600fff7, 0x0, 0xc004db9, 0x8021, 
+0x97a20010, 0x27b10010, 0x3042fffe, 0xa7a20010, 
+0xc004d78, 0x24040001, 0x26100001, 0x2e020020, 
+0x1440fffb, 0x0, 0xc004d78, 0x2021, 
+0xc004d78, 0x24040001, 0xc004d78, 0x2021, 
+0xc004d78, 0x24040001, 0x24100010, 0x32020001, 
+0x10400002, 0x2021, 0x24040001, 0xc004d78, 
+0x108042, 0x1600fffa, 0x32020001, 0x24100010, 
+0x32020018, 0x10400002, 0x2021, 0x24040001, 
+0xc004d78, 0x108042, 0x1600fffa, 0x32020018, 
+0xc004d78, 0x24040001, 0xc004d78, 0x2021, 
+0x34108000, 0x96220000, 0x501024, 0x10400002, 
+0x2021, 0x24040001, 0xc004d78, 0x108042, 
+0x1600fff8, 0x0, 0xc004db9, 0x0, 
+0x8f830054, 0x10000037, 0x2402000e, 0x24020840, 
+0xa7a20010, 0x27b10010, 0x8021, 0xc004d78, 
+0x24040001, 0x26100001, 0x2e020020, 0x1440fffb, 
+0x0, 0xc004d78, 0x2021, 0xc004d78, 
+0x24040001, 0xc004d78, 0x2021, 0xc004d78, 
+0x24040001, 0x24100010, 0x32020001, 0x10400002, 
+0x2021, 0x24040001, 0xc004d78, 0x108042, 
+0x1600fffa, 0x32020001, 0x24100010, 0x32020013, 
+0x10400002, 0x2021, 0x24040001, 0xc004d78, 
+0x108042, 0x1600fffa, 0x32020013, 0xc004d78, 
+0x24040001, 0xc004d78, 0x2021, 0x34108000, 
+0x96220000, 0x501024, 0x10400002, 0x2021, 
+0x24040001, 0xc004d78, 0x108042, 0x1600fff8, 
+0x0, 0xc004db9, 0x0, 0x8f830054, 
+0x24020010, 0x3c010001, 0xac226dd0, 0x3c010001, 
+0x1000000c, 0xac236f3c, 0x8f830054, 0x3c020001, 
+0x8c426f3c, 0x2463ff9c, 0x431023, 0x2c420064, 
+0x14400004, 0x0, 0x24020011, 0x3c010001, 
+0xac226dd0, 0x8fbf0020, 0x8fb1001c, 0x8fb00018, 
+0x3e00008, 0x27bd0028, 0x3c030001, 0x8c636d98, 
+0x27bdffc8, 0x24020002, 0xafbf0034, 0xafb20030, 
+0xafb1002c, 0x14620004, 0xafb00028, 0x3c120002, 
+0x10000003, 0x8e528ff8, 0x3c120002, 0x8e528ffc, 
+0x3c030001, 0x8c636dd4, 0x3c020001, 0x8c426e1c, 
+0x50620004, 0x2463ffff, 0x3c010001, 0xac236e1c, 
+0x2463ffff, 0x2c620006, 0x10400377, 0x31080, 
+0x3c010001, 0x220821, 0x8c226bd8, 0x400008, 
+0x0, 0x2021, 0x2821, 0xc004ddb, 
+0x34068000, 0x24040010, 0x24050002, 0x24060002, 
+0x24020002, 0xc004ddb, 0xa7a20018, 0x24020002, 
+0x3c010001, 0x10000364, 0xac226dd4, 0x27b10018, 
+0xa7a00018, 0x8021, 0xc004d78, 0x24040001, 
+0x26100001, 0x2e020020, 0x1440fffb, 0x0, 
+0xc004d78, 0x2021, 0xc004d78, 0x24040001, 
+0xc004d78, 0x24040001, 0xc004d78, 0x2021, 
+0x24100010, 0x32020001, 0x10400002, 0x2021, 
+0x24040001, 0xc004d78, 0x108042, 0x1600fffa, 
+0x32020001, 0x24100010, 0xc004d78, 0x2021, 
+0x108042, 0x1600fffc, 0x0, 0xc004db9, 
+0x34108000, 0xc004db9, 0x0, 0xc004d58, 
+0x0, 0x50400005, 0x108042, 0x96220000, 
+0x501025, 0xa6220000, 0x108042, 0x1600fff7, 
+0x0, 0xc004db9, 0x0, 0x97a20018, 
+0x30428000, 0x14400004, 0x24020003, 0x3c010001, 
+0xac226dd4, 0x24020003, 0x3c010001, 0x1000032a, 
+0xac226dd4, 0x24040010, 0x24050002, 0x24060002, 
+0x24020002, 0xc004ddb, 0xa7a20018, 0x3c030001, 
+0x8c636e20, 0x24020001, 0x146201e1, 0x8021, 
+0x27b10018, 0xa7a00018, 0xc004d78, 0x24040001, 
+0x26100001, 0x2e020020, 0x1440fffb, 0x0, 
+0xc004d78, 0x2021, 0xc004d78, 0x24040001, 
+0xc004d78, 0x24040001, 0xc004d78, 0x2021, 
+0x24100010, 0x32020001, 0x10400002, 0x2021, 
+0x24040001, 0xc004d78, 0x108042, 0x1600fffa, 
+0x32020001, 0x24100010, 0x32020018, 0x10400002, 
+0x2021, 0x24040001, 0xc004d78, 0x108042, 
+0x1600fffa, 0x32020018, 0xc004db9, 0x34108000, 
+0xc004db9, 0x0, 0xc004d58, 0x0, 
+0x50400005, 0x108042, 0x96220000, 0x501025, 
+0xa6220000, 0x108042, 0x1600fff7, 0x0, 
+0xc004db9, 0x8021, 0x27b10018, 0xa7a00018, 
+0xc004d78, 0x24040001, 0x26100001, 0x2e020020, 
+0x1440fffb, 0x0, 0xc004d78, 0x2021, 
+0xc004d78, 0x24040001, 0xc004d78, 0x24040001, 
+0xc004d78, 0x2021, 0x24100010, 0x32020001, 
+0x10400002, 0x2021, 0x24040001, 0xc004d78, 
+0x108042, 0x1600fffa, 0x32020001, 0x24100010, 
+0x32020018, 0x10400002, 0x2021, 0x24040001, 
+0xc004d78, 0x108042, 0x1600fffa, 0x32020018, 
+0xc004db9, 0x34108000, 0xc004db9, 0x0, 
+0xc004d58, 0x0, 0x50400005, 0x108042, 
+0x96220000, 0x501025, 0xa6220000, 0x108042, 
+0x1600fff7, 0x0, 0xc004db9, 0x8021, 
+0x24040018, 0x2821, 0xc004ddb, 0x24060404, 
+0xa7a0001a, 0xc004d78, 0x24040001, 0x26100001, 
+0x2e020020, 0x1440fffb, 0x0, 0xc004d78, 
+0x2021, 0xc004d78, 0x24040001, 0xc004d78, 
+0x24040001, 0xc004d78, 0x2021, 0x24100010, 
+0x32020001, 0x10400002, 0x2021, 0x24040001, 
+0xc004d78, 0x108042, 0x1600fffa, 0x32020001, 
+0x24100010, 0x32020018, 0x10400002, 0x2021, 
+0x24040001, 0xc004d78, 0x108042, 0x1600fffa, 
+0x32020018, 0xc004db9, 0x34108000, 0xc004db9, 
+0x0, 0xc004d58, 0x0, 0x50400005, 
+0x108042, 0x97a2001a, 0x501025, 0xa7a2001a, 
+0x108042, 0x1600fff7, 0x0, 0xc004db9, 
+0x8021, 0xa7a0001a, 0xc004d78, 0x24040001, 
+0x26100001, 0x2e020020, 0x1440fffb, 0x0, 
+0xc004d78, 0x2021, 0xc004d78, 0x24040001, 
+0xc004d78, 0x24040001, 0xc004d78, 0x2021, 
+0x24100010, 0x32020001, 0x10400002, 0x2021, 
+0x24040001, 0xc004d78, 0x108042, 0x1600fffa, 
+0x32020001, 0x24100010, 0x32020018, 0x10400002, 
+0x2021, 0x24040001, 0xc004d78, 0x108042, 
+0x1600fffa, 0x32020018, 0xc004db9, 0x34108000, 
+0xc004db9, 0x0, 0xc004d58, 0x0, 
+0x50400005, 0x108042, 0x97a2001a, 0x501025, 
+0xa7a2001a, 0x108042, 0x1600fff7, 0x0, 
+0xc004db9, 0x8021, 0xa7a0001c, 0xc004d78, 
+0x24040001, 0x26100001, 0x2e020020, 0x1440fffb, 
+0x0, 0xc004d78, 0x2021, 0xc004d78, 
+0x24040001, 0xc004d78, 0x24040001, 0xc004d78, 
+0x2021, 0x24100010, 0xc004d78, 0x2021, 
+0x108042, 0x1600fffc, 0x0, 0x24100010, 
+0x3202001e, 0x10400002, 0x2021, 0x24040001, 
+0xc004d78, 0x108042, 0x1600fffa, 0x3202001e, 
+0xc004db9, 0x34108000, 0xc004db9, 0x0, 
+0xc004d58, 0x0, 0x50400005, 0x108042, 
+0x97a2001c, 0x501025, 0xa7a2001c, 0x108042, 
+0x1600fff7, 0x0, 0xc004db9, 0x8021, 
+0xa7a0001c, 0xc004d78, 0x24040001, 0x26100001, 
+0x2e020020, 0x1440fffb, 0x0, 0xc004d78, 
+0x2021, 0xc004d78, 0x24040001, 0xc004d78, 
+0x24040001, 0xc004d78, 0x2021, 0x24100010, 
+0xc004d78, 0x2021, 0x108042, 0x1600fffc, 
+0x0, 0x24100010, 0x3202001e, 0x10400002, 
+0x2021, 0x24040001, 0xc004d78, 0x108042, 
+0x1600fffa, 0x3202001e, 0xc004db9, 0x34108000, 
+0xc004db9, 0x0, 0xc004d58, 0x0, 
+0x50400005, 0x108042, 0x97a2001c, 0x501025, 
+0xa7a2001c, 0x108042, 0x1600fff7, 0x0, 
+0xc004db9, 0x8021, 0x24020002, 0xa7a2001e, 
+0xc004d78, 0x24040001, 0x26100001, 0x2e020020, 
+0x1440fffb, 0x0, 0xc004d78, 0x2021, 
+0xc004d78, 0x24040001, 0xc004d78, 0x2021, 
+0xc004d78, 0x24040001, 0x24100010, 0xc004d78, 
+0x2021, 0x108042, 0x1600fffc, 0x0, 
+0x24100010, 0x3202001e, 0x10400002, 0x2021, 
+0x24040001, 0xc004d78, 0x108042, 0x1600fffa, 
+0x3202001e, 0xc004d78, 0x24040001, 0xc004d78, 
+0x2021, 0x34108000, 0x97a2001e, 0x501024, 
+0x10400002, 0x2021, 0x24040001, 0xc004d78, 
+0x108042, 0x1600fff8, 0x0, 0xc004db9, 
+0x8021, 0xa7a00020, 0xc004d78, 0x24040001, 
+0x26100001, 0x2e020020, 0x1440fffb, 0x0, 
+0xc004d78, 0x2021, 0xc004d78, 0x24040001, 
+0xc004d78, 0x24040001, 0xc004d78, 0x2021, 
+0x24100010, 0xc004d78, 0x2021, 0x108042, 
+0x1600fffc, 0x0, 0x24100010, 0x3202001e, 
+0x10400002, 0x2021, 0x24040001, 0xc004d78, 
+0x108042, 0x1600fffa, 0x3202001e, 0xc004db9, 
+0x34108000, 0xc004db9, 0x0, 0xc004d58, 
+0x0, 0x50400005, 0x108042, 0x97a20020, 
+0x501025, 0xa7a20020, 0x108042, 0x1600fff7, 
+0x0, 0xc004db9, 0x8021, 0xa7a00020, 
+0xc004d78, 0x24040001, 0x26100001, 0x2e020020, 
+0x1440fffb, 0x0, 0xc004d78, 0x2021, 
+0xc004d78, 0x24040001, 0xc004d78, 0x24040001, 
+0xc004d78, 0x2021, 0x24100010, 0xc004d78, 
+0x2021, 0x108042, 0x1600fffc, 0x0, 
+0x24100010, 0x3202001e, 0x10400002, 0x2021, 
+0x24040001, 0xc004d78, 0x108042, 0x1600fffa, 
+0x3202001e, 0xc004db9, 0x34108000, 0xc004db9, 
+0x0, 0xc004d58, 0x0, 0x50400005, 
+0x108042, 0x97a20020, 0x501025, 0xa7a20020, 
+0x108042, 0x1600fff7, 0x0, 0xc004db9, 
+0x8021, 0xa7a00022, 0xc004d78, 0x24040001, 
+0x26100001, 0x2e020020, 0x1440fffb, 0x0, 
+0xc004d78, 0x2021, 0xc004d78, 0x24040001, 
+0xc004d78, 0x2021, 0xc004d78, 0x24040001, 
+0x24100010, 0xc004d78, 0x2021, 0x108042, 
+0x1600fffc, 0x0, 0x24100010, 0xc004d78, 
+0x2021, 0x108042, 0x1600fffc, 0x0, 
+0xc004d78, 0x24040001, 0xc004d78, 0x2021, 
+0x34108000, 0x97a20022, 0x501024, 0x10400002, 
+0x2021, 0x24040001, 0xc004d78, 0x108042, 
+0x1600fff8, 0x0, 0xc004db9, 0x0, 
+0x24040018, 0x24050002, 0xc004ddb, 0x24060004, 
+0x3c100001, 0x8e106e24, 0x24020001, 0x1602011d, 
+0x0, 0x3c020001, 0x94426f26, 0x3c010001, 
+0xac206e24, 0x24429fbc, 0x2c420004, 0x1040000c, 
+0x24040009, 0x24050001, 0xc004ddb, 0x24060400, 
+0x24040018, 0x24050001, 0xc004ddb, 0x24060020, 
+0x24040018, 0x24050001, 0xc004ddb, 0x24062000, 
+0x3c024000, 0x2421024, 0x10400123, 0x3c022000, 
+0x2421024, 0x10400004, 0x0, 0x3c010001, 
+0x10000003, 0xac306f1c, 0x3c010001, 0xac206f1c, 
+0x3c030001, 0x8c636f34, 0x24020005, 0x146200f9, 
+0x0, 0x3c020001, 0x8c426f1c, 0x10400067, 
+0x3c020004, 0x2421024, 0x10400011, 0xa7a00018, 
+0x3c020008, 0x2421024, 0x10400002, 0x24020200, 
+0xa7a20018, 0x3c020010, 0x2421024, 0x10400004, 
+0x0, 0x97a20018, 0x34420100, 0xa7a20018, 
+0x97a60018, 0x24040009, 0x10000004, 0x2821, 
+0x24040009, 0x2821, 0x3021, 0xc004ddb, 
+0x0, 0x24020001, 0xa7a2001a, 0x3c020008, 
+0x2421024, 0x1040000c, 0x3c020002, 0x2421024, 
+0x10400002, 0x24020101, 0xa7a2001a, 0x3c020001, 
+0x2421024, 0x10400005, 0x3c020010, 0x97a2001a, 
+0x34420040, 0xa7a2001a, 0x3c020010, 0x2421024, 
+0x1040000e, 0x3c020002, 0x2421024, 0x10400005, 
+0x3c020001, 0x97a2001a, 0x34420080, 0xa7a2001a, 
+0x3c020001, 0x2421024, 0x10400005, 0x3c0300a0, 
+0x97a2001a, 0x34420020, 0xa7a2001a, 0x3c0300a0, 
+0x2431024, 0x54430004, 0x3c020020, 0x97a2001a, 
+0x1000000c, 0x34420400, 0x2421024, 0x50400004, 
+0x3c020080, 0x97a2001a, 0x10000006, 0x34420800, 
+0x2421024, 0x10400004, 0x0, 0x97a2001a, 
+0x34420c00, 0xa7a2001a, 0x97a6001a, 0x24040004, 
+0xc004ddb, 0x2821, 0x3c020004, 0x2421024, 
+0x10400004, 0xa7a0001c, 0x32425000, 0x14400004, 
+0x0, 0x32424000, 0x10400005, 0x2021, 
+0xc004cf9, 0x2402021, 0x10000096, 0x0, 
+0x97a6001c, 0x2821, 0x34c61200, 0xc004ddb, 
+0xa7a6001c, 0x1000008f, 0x0, 0x2421024, 
+0x10400004, 0xa7a00018, 0x32425000, 0x14400004, 
+0x0, 0x32424000, 0x10400005, 0x3c020010, 
+0xc004cf9, 0x2402021, 0x10000019, 0xa7a0001a, 
+0x2421024, 0x10400004, 0x0, 0x97a20018, 
+0x10000004, 0xa7a20018, 0x97a20018, 0x34420100, 
+0xa7a20018, 0x3c020001, 0x2421024, 0x10400004, 
+0x0, 0x97a20018, 0x10000004, 0xa7a20018, 
+0x97a20018, 0x34422000, 0xa7a20018, 0x97a60018, 
+0x2021, 0xc004ddb, 0x2821, 0xa7a0001a, 
+0x8021, 0xc004d78, 0x24040001, 0x26100001, 
+0x2e020020, 0x1440fffb, 0x0, 0xc004d78, 
+0x2021, 0xc004d78, 0x24040001, 0xc004d78, 
+0x24040001, 0xc004d78, 0x2021, 0x24100010, 
+0x32020001, 0x10400002, 0x2021, 0x24040001, 
+0xc004d78, 0x108042, 0x1600fffa, 0x32020001, 
+0x24100010, 0xc004d78, 0x2021, 0x108042, 
+0x1600fffc, 0x0, 0xc004db9, 0x34108000, 
+0xc004db9, 0x0, 0xc004d58, 0x0, 
+0x50400005, 0x108042, 0x97a2001a, 0x501025, 
+0xa7a2001a, 0x108042, 0x1600fff7, 0x0, 
+0xc004db9, 0x8021, 0xa7a0001a, 0xc004d78, 
+0x24040001, 0x26100001, 0x2e020020, 0x1440fffb, 
+0x0, 0xc004d78, 0x2021, 0xc004d78, 
+0x24040001, 0xc004d78, 0x24040001, 0xc004d78, 
+0x2021, 0x24100010, 0x32020001, 0x10400002, 
+0x2021, 0x24040001, 0xc004d78, 0x108042, 
+0x1600fffa, 0x32020001, 0x24100010, 0xc004d78, 
+0x2021, 0x108042, 0x1600fffc, 0x0, 
+0xc004db9, 0x34108000, 0xc004db9, 0x0, 
+0xc004d58, 0x0, 0x50400005, 0x108042, 
+0x97a2001a, 0x501025, 0xa7a2001a, 0x108042, 
+0x1600fff7, 0x0, 0xc004db9, 0x0, 
+0x3c040001, 0x24846bcc, 0x97a60018, 0x97a7001a, 
+0x3c020001, 0x8c426d98, 0x3c030001, 0x8c636f1c, 
+0x3c05000d, 0x34a50205, 0xafa20010, 0xc002b3b, 
+0xafa30014, 0x8f830054, 0x24020004, 0x3c010001, 
+0xac226dd4, 0x3c010001, 0x10000017, 0xac236f38, 
+0x8f830054, 0x3c020001, 0x8c426f38, 0x2463ff9c, 
+0x431023, 0x2c420064, 0x1440000f, 0x0, 
+0x8f820220, 0x24030005, 0x3c010001, 0xac236dd4, 
+0x3c03f700, 0x431025, 0x10000007, 0xaf820220, 
+0x24020006, 0x3c010001, 0xac226dd4, 0x24020011, 
+0x3c010001, 0xac226dd0, 0x8fbf0034, 0x8fb20030, 
+0x8fb1002c, 0x8fb00028, 0x3e00008, 0x27bd0038, 
+0x27bdffd8, 0xafb00018, 0x808021, 0xafb1001c, 
+0x8821, 0x32024000, 0x10400013, 0xafbf0020, 
+0x3c020010, 0x2021024, 0x2c420001, 0x21023, 
+0x30434100, 0x3c020001, 0x2021024, 0x14400006, 
+0x34714000, 0x3c020002, 0x2021024, 0x14400002, 
+0x34716000, 0x34714040, 0x2021, 0x2821, 
+0x10000036, 0x2203021, 0x32021000, 0x10400035, 
+0x2021, 0x2821, 0xc004ddb, 0x24060040, 
+0x24040018, 0x2821, 0xc004ddb, 0x24060c00, 
+0x24040017, 0x2821, 0xc004ddb, 0x24060400, 
+0x24040016, 0x2821, 0xc004ddb, 0x24060006, 
+0x24040017, 0x2821, 0xc004ddb, 0x24062500, 
+0x24040016, 0x2821, 0xc004ddb, 0x24060006, 
+0x24040017, 0x2821, 0xc004ddb, 0x24064600, 
+0x24040016, 0x2821, 0xc004ddb, 0x24060006, 
+0x24040017, 0x2821, 0xc004ddb, 0x24066700, 
+0x24040016, 0x2821, 0xc004ddb, 0x24060006, 
+0x2404001f, 0x2821, 0xc004ddb, 0x24060010, 
+0x24040009, 0x2821, 0xc004ddb, 0x24061500, 
+0x24040009, 0x2821, 0x24061d00, 0xc004ddb, 
+0x0, 0x3c040001, 0x24846bf0, 0x3c05000e, 
+0x34a50100, 0x2003021, 0x2203821, 0xafa00010, 
+0xc002b3b, 0xafa00014, 0x8fbf0020, 0x8fb1001c, 
+0x8fb00018, 0x3e00008, 0x27bd0028, 0x8f850044, 
+0x8f820044, 0x3c030001, 0x431025, 0x3c030008, 
+0xaf820044, 0x8f840054, 0x8f820054, 0xa32824, 
+0x10000002, 0x24840001, 0x8f820054, 0x821023, 
+0x2c420002, 0x1440fffc, 0x0, 0x8f820044, 
+0x3c03fffe, 0x3463ffff, 0x431024, 0xaf820044, 
+0x8f830054, 0x8f820054, 0x10000002, 0x24630001, 
+0x8f820054, 0x621023, 0x2c420002, 0x1440fffc, 
+0x0, 0x3e00008, 0xa01021, 0x8f830044, 
+0x3c02fff0, 0x3442ffff, 0x42480, 0x621824, 
+0x3c020002, 0x822025, 0x641825, 0xaf830044, 
+0x8f820044, 0x3c03fffe, 0x3463ffff, 0x431024, 
+0xaf820044, 0x8f830054, 0x8f820054, 0x10000002, 
+0x24630001, 0x8f820054, 0x621023, 0x2c420002, 
+0x1440fffc, 0x0, 0x8f820044, 0x3c030001, 
+0x431025, 0xaf820044, 0x8f830054, 0x8f820054, 
+0x10000002, 0x24630001, 0x8f820054, 0x621023, 
+0x2c420002, 0x1440fffc, 0x0, 0x3e00008, 
+0x0, 0x8f820044, 0x2403ff7f, 0x431024, 
+0xaf820044, 0x8f830054, 0x8f820054, 0x10000002, 
+0x24630001, 0x8f820054, 0x621023, 0x2c420002, 
+0x1440fffc, 0x0, 0x8f820044, 0x34420080, 
+0xaf820044, 0x8f830054, 0x8f820054, 0x10000002, 
+0x24630001, 0x8f820054, 0x621023, 0x2c420002, 
+0x1440fffc, 0x0, 0x3e00008, 0x0, 
+0x8f820044, 0x3c03fff0, 0x3463ffff, 0x431024, 
+0xaf820044, 0x8f820044, 0x3c030001, 0x431025, 
+0xaf820044, 0x8f830054, 0x8f820054, 0x10000002, 
+0x24630001, 0x8f820054, 0x621023, 0x2c420002, 
+0x1440fffc, 0x0, 0x8f820044, 0x3c03fffe, 
+0x3463ffff, 0x431024, 0xaf820044, 0x8f830054, 
+0x8f820054, 0x10000002, 0x24630001, 0x8f820054, 
+0x621023, 0x2c420002, 0x1440fffc, 0x0, 
+0x3e00008, 0x0, 0x27bdffc8, 0xafb30024, 
+0x809821, 0xafbe002c, 0xa0f021, 0xafb20020, 
+0xc09021, 0x33c2ffff, 0xafbf0030, 0xafb50028, 
+0xafb1001c, 0xafb00018, 0x14400034, 0xa7b20010, 
+0x3271ffff, 0x27b20010, 0x8021, 0xc004d78, 
+0x24040001, 0x26100001, 0x2e020020, 0x1440fffb, 
+0x0, 0xc004d78, 0x2021, 0xc004d78, 
+0x24040001, 0xc004d78, 0x2021, 0xc004d78, 
+0x24040001, 0x24100010, 0x32020001, 0x10400002, 
+0x2021, 0x24040001, 0xc004d78, 0x108042, 
+0x1600fffa, 0x32020001, 0x24100010, 0x2301024, 
+0x10400002, 0x2021, 0x24040001, 0xc004d78, 
+0x108042, 0x1600fffa, 0x2301024, 0xc004d78, 
+0x24040001, 0xc004d78, 0x2021, 0x34108000, 
+0x96420000, 0x501024, 0x10400002, 0x2021, 
+0x24040001, 0xc004d78, 0x108042, 0x12000075, 
+0x0, 0x1000fff6, 0x0, 0x3275ffff, 
+0x27b10010, 0xa7a00010, 0x8021, 0xc004d78, 
+0x24040001, 0x26100001, 0x2e020020, 0x1440fffb, 
+0x0, 0xc004d78, 0x2021, 0xc004d78, 
+0x24040001, 0xc004d78, 0x24040001, 0xc004d78, 
+0x2021, 0x24100010, 0x32020001, 0x10400002, 
+0x2021, 0x24040001, 0xc004d78, 0x108042, 
+0x1600fffa, 0x32020001, 0x24100010, 0x2b01024, 
+0x10400002, 0x2021, 0x24040001, 0xc004d78, 
+0x108042, 0x1600fffa, 0x2b01024, 0xc004db9, 
+0x34108000, 0xc004db9, 0x0, 0xc004d58, 
+0x0, 0x50400005, 0x108042, 0x96220000, 
+0x501025, 0xa6220000, 0x108042, 0x1600fff7, 
+0x0, 0xc004db9, 0x0, 0x33c5ffff, 
+0x24020001, 0x54a20004, 0x24020002, 0x97a20010, 
+0x10000006, 0x521025, 0x14a20006, 0x3271ffff, 
+0x97a20010, 0x121827, 0x431024, 0xa7a20010, 
+0x3271ffff, 0x27b20010, 0x8021, 0xc004d78, 
+0x24040001, 0x26100001, 0x2e020020, 0x1440fffb, 
+0x0, 0xc004d78, 0x2021, 0xc004d78, 
+0x24040001, 0xc004d78, 0x2021, 0xc004d78, 
+0x24040001, 0x24100010, 0x32020001, 0x10400002, 
+0x2021, 0x24040001, 0xc004d78, 0x108042, 
+0x1600fffa, 0x32020001, 0x24100010, 0x2301024, 
+0x10400002, 0x2021, 0x24040001, 0xc004d78, 
+0x108042, 0x1600fffa, 0x2301024, 0xc004d78, 
+0x24040001, 0xc004d78, 0x2021, 0x34108000, 
+0x96420000, 0x501024, 0x10400002, 0x2021, 
+0x24040001, 0xc004d78, 0x108042, 0x1600fff8, 
+0x0, 0xc004db9, 0x0, 0x8fbf0030, 
+0x8fbe002c, 0x8fb50028, 0x8fb30024, 0x8fb20020, 
+0x8fb1001c, 0x8fb00018, 0x3e00008, 0x27bd0038, 
+0x0, 0x0, 0x0, 0x27bdffe8, 
+0xafbf0010, 0x8ee304b8, 0x24020008, 0x146201e0, 
+0x0, 0x3c020001, 0x8c426f1c, 0x14400005, 
+0x0, 0xc003daf, 0x8f840224, 0x100001d8, 
+0x0, 0x8f820220, 0x3c030008, 0x431024, 
+0x10400026, 0x24020001, 0x8f840224, 0x8f820220, 
+0x3c030400, 0x431024, 0x10400006, 0x0, 
+0x3c010002, 0xac208fa0, 0x3c010002, 0x1000000b, 
+0xac208fc0, 0x3c030002, 0x24638fa0, 0x8c620000, 
+0x24420001, 0xac620000, 0x2c420002, 0x14400003, 
+0x24020001, 0x3c010002, 0xac228fc0, 0x3c020002, 
+0x8c428fc0, 0x10400006, 0x30820040, 0x10400004, 
+0x24020001, 0x3c010002, 0x10000003, 0xac228fc4, 
+0x3c010002, 0xac208fc4, 0x3c010002, 0xac248f9c, 
+0x3c010002, 0x1000000b, 0xac208fd0, 0x3c010002, 
+0xac228fd0, 0x3c010002, 0xac208fc0, 0x3c010002, 
+0xac208fa0, 0x3c010002, 0xac208fc4, 0x3c010002, 
+0xac208f9c, 0x3c030002, 0x8c638f90, 0x3c020002, 
+0x8c428f94, 0x50620004, 0x2463ffff, 0x3c010002, 
+0xac238f94, 0x2463ffff, 0x2c62000e, 0x10400194, 
+0x31080, 0x3c010001, 0x220821, 0x8c226c00, 
+0x400008, 0x0, 0x24020002, 0x3c010002, 
+0xac208fc0, 0x3c010002, 0xac208fa0, 0x3c010002, 
+0xac208f9c, 0x3c010002, 0xac208fc4, 0x3c010002, 
+0xac208fb8, 0x3c010002, 0xac208fb0, 0xaf800224, 
+0x3c010002, 0xac228f90, 0x3c020002, 0x8c428fd0, 
+0x1440004f, 0x3c02fdff, 0x3442ffff, 0xc003daf, 
+0x282a024, 0xaf800204, 0x8f820200, 0x2403fffd, 
+0x431024, 0xaf820200, 0x3c010002, 0xac208fe0, 
+0x8f830054, 0x3c020002, 0x8c428fb8, 0x24040001, 
+0x3c010002, 0xac248fcc, 0x24420001, 0x3c010002, 
+0xac228fb8, 0x2c420004, 0x3c010002, 0xac238fb4, 
+0x14400006, 0x24020003, 0x3c010001, 0xac246d9c, 
+0x3c010002, 0x1000015e, 0xac208fb8, 0x3c010002, 
+0x1000015b, 0xac228f90, 0x8f830054, 0x3c020002, 
+0x8c428fb4, 0x2463d8f0, 0x431023, 0x2c422710, 
+0x14400003, 0x24020004, 0x3c010002, 0xac228f90, 
+0x3c020002, 0x8c428fd0, 0x14400021, 0x3c02fdff, 
+0x3442ffff, 0x1000014a, 0x282a024, 0x3c040001, 
+0x8c846f20, 0x3c010002, 0xc005084, 0xac208fa8, 
+0x3c020002, 0x8c428fdc, 0xaf820204, 0x3c020002, 
+0x8c428fd0, 0x14400012, 0x3c03fdff, 0x8f820204, 
+0x3463ffff, 0x30420030, 0x1440012f, 0x283a024, 
+0x3c030002, 0x8c638fdc, 0x24020005, 0x3c010002, 
+0xac228f90, 0x3c010002, 0x10000131, 0xac238fe0, 
+0x3c020002, 0x8c428fd0, 0x10400010, 0x3c02fdff, 
+0x3c020001, 0x8c426e3c, 0x24420001, 0x3c010001, 
+0xac226e3c, 0x2c420002, 0x14400125, 0x24020001, 
+0x3c010001, 0xac226e44, 0x3c010001, 0xac206e3c, 
+0x3c010001, 0x1000011e, 0xac226d9c, 0x3c030002, 
+0x8c638fc0, 0x3442ffff, 0x10600119, 0x282a024, 
+0x3c020002, 0x8c428f9c, 0x10400115, 0x0, 
+0x3c010002, 0xac228fc8, 0x24020003, 0x3c010002, 
+0xac228fa0, 0x100000b8, 0x24020006, 0x3c010002, 
+0xac208fa8, 0x8f820204, 0x34420040, 0xaf820204, 
+0x3c020002, 0x8c428fe0, 0x24030007, 0x3c010002, 
+0xac238f90, 0x34420040, 0x3c010002, 0xac228fe0, 
+0x3c020002, 0x8c428fc0, 0x10400005, 0x0, 
+0x3c020002, 0x8c428f9c, 0x104000f0, 0x24020002, 
+0x3c050002, 0x24a58fa0, 0x8ca20000, 0x2c424e21, 
+0x104000ea, 0x24020002, 0x3c020002, 0x8c428fc4, 
+0x104000ef, 0x2404ffbf, 0x3c020002, 0x8c428f9c, 
+0x3c030002, 0x8c638fc8, 0x441024, 0x641824, 
+0x10430004, 0x24020001, 0x3c010002, 0x100000e4, 
+0xac228f90, 0x24020003, 0xaca20000, 0x24020008, 
+0x3c010002, 0xac228f90, 0x3c020002, 0x8c428fcc, 
+0x1040000c, 0x24020001, 0x3c040002, 0xc005091, 
+0x8c848f9c, 0x3c020002, 0x8c428fe8, 0x14400005, 
+0x24020001, 0x3c020002, 0x8c428fe4, 0x10400006, 
+0x24020001, 0x3c010001, 0xac226d9c, 0x3c010002, 
+0x100000cb, 0xac208fb8, 0x3c020002, 0x8c428fb0, 
+0x3c030002, 0x8c638f9c, 0x2c420001, 0x210c0, 
+0x30630008, 0x3c010002, 0xac228fb0, 0x3c010002, 
+0xac238fac, 0x8f830054, 0x24020009, 0x3c010002, 
+0xac228f90, 0x3c010002, 0x100000b9, 0xac238fb4, 
+0x8f830054, 0x3c020002, 0x8c428fb4, 0x2463d8f0, 
+0x431023, 0x2c422710, 0x1440009f, 0x0, 
+0x3c020002, 0x8c428fc0, 0x10400005, 0x0, 
+0x3c020002, 0x8c428f9c, 0x104000a0, 0x24020002, 
+0x3c030002, 0x24638fa0, 0x8c620000, 0x2c424e21, 
+0x1040009a, 0x24020002, 0x3c020002, 0x8c428fcc, 
+0x1040000e, 0x0, 0x3c020002, 0x8c428f9c, 
+0x3c010002, 0xac208fcc, 0x30420080, 0x1040002f, 
+0x2402000c, 0x8f820204, 0x30420080, 0x1440000c, 
+0x24020003, 0x10000029, 0x2402000c, 0x3c020002, 
+0x8c428f9c, 0x30420080, 0x14400005, 0x24020003, 
+0x8f820204, 0x30420080, 0x1040001f, 0x24020003, 
+0xac620000, 0x2402000a, 0x3c010002, 0xac228f90, 
+0x3c040002, 0x24848fd8, 0x8c820000, 0x3c030002, 
+0x8c638fb0, 0x431025, 0xaf820204, 0x8c830000, 
+0x3c040002, 0x8c848fb0, 0x2402000b, 0x3c010002, 
+0xac228f90, 0x641825, 0x3c010002, 0xac238fe0, 
+0x3c050002, 0x24a58fa0, 0x8ca20000, 0x2c424e21, 
+0x10400066, 0x24020002, 0x3c020002, 0x8c428fd0, 
+0x10400005, 0x0, 0x2402000c, 0x3c010002, 
+0x10000067, 0xac228f90, 0x3c020002, 0x8c428fc0, 
+0x10400063, 0x0, 0x3c040002, 0x8c848f9c, 
+0x10800055, 0x30820008, 0x3c030002, 0x8c638fac, 
+0x1062005b, 0x24020003, 0x3c010002, 0xac248fc8, 
+0xaca20000, 0x24020006, 0x3c010002, 0x10000054, 
+0xac228f90, 0x8f820200, 0x34420002, 0xaf820200, 
+0x8f830054, 0x2402000d, 0x3c010002, 0xac228f90, 
+0x3c010002, 0xac238fb4, 0x8f830054, 0x3c020002, 
+0x8c428fb4, 0x2463d8f0, 0x431023, 0x2c422710, 
+0x14400031, 0x0, 0x3c020002, 0x8c428fd0, 
+0x10400020, 0x2402000e, 0x3c030002, 0x8c638fe4, 
+0x3c010002, 0x14600015, 0xac228f90, 0xc003e6d, 
+0x0, 0x3c050001, 0x8ca56d98, 0xc00529b, 
+0x2021, 0x3c030001, 0x8c636d98, 0x24020004, 
+0x14620005, 0x2403fffb, 0x3c020001, 0x8c426d94, 
+0x10000003, 0x2403fff7, 0x3c020001, 0x8c426d94, 
+0x431024, 0x3c010001, 0xac226d94, 0x8f830224, 
+0x3c020200, 0x3c010002, 0xac238fec, 0x10000020, 
+0x282a025, 0x3c020002, 0x8c428fc0, 0x10400005, 
+0x0, 0x3c020002, 0x8c428f9c, 0x1040000f, 
+0x24020002, 0x3c020002, 0x8c428fa0, 0x2c424e21, 
+0x1040000a, 0x24020002, 0x3c020002, 0x8c428fc0, 
+0x1040000f, 0x0, 0x3c020002, 0x8c428f9c, 
+0x1440000b, 0x0, 0x24020002, 0x3c010002, 
+0x10000007, 0xac228f90, 0x3c020002, 0x8c428fc0, 
+0x10400003, 0x0, 0xc003daf, 0x0, 
+0x8f820220, 0x3c03f700, 0x431025, 0xaf820220, 
+0x8fbf0010, 0x3e00008, 0x27bd0018, 0x3c030002, 
+0x24638fe8, 0x8c620000, 0x10400005, 0x34422000, 
+0x3c010002, 0xac228fdc, 0x10000003, 0xac600000, 
+0x3c010002, 0xac248fdc, 0x3e00008, 0x0, 
+0x27bdffe0, 0x30820030, 0xafbf0018, 0x3c010002, 
+0xac228fe4, 0x14400067, 0x3c02ffff, 0x34421f0e, 
+0x821024, 0x14400061, 0x24020030, 0x30822000, 
+0x1040005d, 0x30838000, 0x31a02, 0x30820001, 
+0x21200, 0x3c040001, 0x8c846f20, 0x621825, 
+0x331c2, 0x3c030001, 0x24636e48, 0x30828000, 
+0x21202, 0x30840001, 0x42200, 0x441025, 
+0x239c2, 0x61080, 0x431021, 0x471021, 
+0x90430000, 0x24020001, 0x10620025, 0x0, 
+0x10600007, 0x24020002, 0x10620013, 0x24020003, 
+0x1062002c, 0x3c05000f, 0x10000037, 0x0, 
+0x8f820200, 0x2403feff, 0x431024, 0xaf820200, 
+0x8f820220, 0x3c03fffe, 0x3463ffff, 0x431024, 
+0xaf820220, 0x3c010002, 0xac209004, 0x3c010002, 
+0x10000034, 0xac20900c, 0x8f820200, 0x34420100, 
+0xaf820200, 0x8f820220, 0x3c03fffe, 0x3463ffff, 
+0x431024, 0xaf820220, 0x24020100, 0x3c010002, 
+0xac229004, 0x3c010002, 0x10000026, 0xac20900c, 
+0x8f820200, 0x2403feff, 0x431024, 0xaf820200, 
+0x8f820220, 0x3c030001, 0x431025, 0xaf820220, 
+0x3c010002, 0xac209004, 0x3c010002, 0x10000019, 
+0xac23900c, 0x8f820200, 0x34420100, 0xaf820200, 
+0x8f820220, 0x3c030001, 0x431025, 0xaf820220, 
+0x24020100, 0x3c010002, 0xac229004, 0x3c010002, 
+0x1000000c, 0xac23900c, 0x34a5ffff, 0x3c040001, 
+0x24846c38, 0xafa30010, 0xc002b3b, 0xafa00014, 
+0x10000004, 0x0, 0x24020030, 0x3c010002, 
+0xac228fe8, 0x8fbf0018, 0x3e00008, 0x27bd0020, 
+0x0, 0x0, 0x0, 0x27bdffc8, 
+0xafb20028, 0x809021, 0xafb3002c, 0xa09821, 
+0xafb00020, 0xc08021, 0x3c040001, 0x24846c50, 
+0x3c050009, 0x3c020001, 0x8c426d98, 0x34a59001, 
+0x2403021, 0x2603821, 0xafbf0030, 0xafb10024, 
+0xa7a0001a, 0xafb00014, 0xc002b3b, 0xafa20010, 
+0x24020002, 0x12620083, 0x2e620003, 0x10400005, 
+0x24020001, 0x1262000a, 0x0, 0x10000173, 
+0x0, 0x24020004, 0x126200f8, 0x24020008, 
+0x126200f7, 0x3c02ffec, 0x1000016c, 0x0, 
+0x3c020001, 0x8c426d94, 0x30420002, 0x14400004, 
+0x128940, 0x3c02fffb, 0x3442ffff, 0x2028024, 
+0x3c010002, 0x310821, 0xac308ffc, 0x3c024000, 
+0x2021024, 0x1040004e, 0x1023c2, 0x30840030, 
+0x101382, 0x3042001c, 0x3c030001, 0x24636dd8, 
+0x431021, 0x823821, 0x3c020020, 0x2021024, 
+0x10400006, 0x24020100, 0x3c010002, 0x310821, 
+0xac229000, 0x10000005, 0x3c020080, 0x3c010002, 
+0x310821, 0xac209000, 0x3c020080, 0x2021024, 
+0x10400006, 0x121940, 0x3c020001, 0x3c010002, 
+0x230821, 0x10000005, 0xac229008, 0x121140, 
+0x3c010002, 0x220821, 0xac209008, 0x94e40000, 
+0x3c030001, 0x8c636f40, 0x24020005, 0x10620010, 
+0xa7a40018, 0x32024000, 0x10400002, 0x34824000, 
+0xa7a20018, 0x24040001, 0x94e20002, 0x24050004, 
+0x24e60002, 0x34420001, 0xc0045be, 0xa4e20002, 
+0x24040001, 0x2821, 0xc0045be, 0x27a60018, 
+0x3c020001, 0x8c426d98, 0x24110001, 0x3c010001, 
+0xac316da4, 0x14530004, 0x32028000, 0xc003daf, 
+0x0, 0x32028000, 0x1040011c, 0x0, 
+0xc003daf, 0x0, 0x3c030001, 0x8c636f40, 
+0x24020005, 0x10620115, 0x24020002, 0x3c010001, 
+0xac316d9c, 0x3c010001, 0x10000110, 0xac226d98, 
+0x24040001, 0x24050004, 0x27b0001a, 0xc0045be, 
+0x2003021, 0x24040001, 0x2821, 0xc0045be, 
+0x2003021, 0x3c020002, 0x511021, 0x8c428ff4, 
+0x3c040001, 0x8c846d98, 0x3c03bfff, 0x3463ffff, 
+0x3c010001, 0xac336da4, 0x431024, 0x3c010002, 
+0x310821, 0x109300f7, 0xac228ff4, 0x100000f7, 
+0x0, 0x3c022000, 0x2021024, 0x10400005, 
+0x24020001, 0x3c010001, 0xac226f1c, 0x10000004, 
+0x128940, 0x3c010001, 0xac206f1c, 0x128940, 
+0x3c010002, 0x310821, 0xac308ff8, 0x3c024000, 
+0x2021024, 0x14400014, 0x0, 0x3c020001, 
+0x8c426f1c, 0x10400006, 0x24040004, 0x24050001, 
+0xc004ddb, 0x24062000, 0x24020001, 0xaee204b8, 
+0x3c020002, 0x511021, 0x8c428ff0, 0x3c03bfff, 
+0x3463ffff, 0x431024, 0x3c010002, 0x310821, 
+0x100000d0, 0xac228ff0, 0x3c020001, 0x8c426f1c, 
+0x10400028, 0x3c0300a0, 0x2031024, 0x5443000d, 
+0x3c020020, 0x3c020001, 0x8c426f20, 0x24030100, 
+0x3c010002, 0x310821, 0xac239004, 0x3c030001, 
+0x3c010002, 0x310821, 0xac23900c, 0x10000015, 
+0x34420400, 0x2021024, 0x10400008, 0x24030100, 
+0x3c020001, 0x8c426f20, 0x3c010002, 0x310821, 
+0xac239004, 0x1000000b, 0x34420800, 0x3c020080, 
+0x2021024, 0x1040002e, 0x3c030001, 0x3c020001, 
+0x8c426f20, 0x3c010002, 0x310821, 0xac23900c, 
+0x34420c00, 0x3c010001, 0xac226f20, 0x10000025, 
+0x24040001, 0x3c020020, 0x2021024, 0x10400006, 
+0x24020100, 0x3c010002, 0x310821, 0xac229004, 
+0x10000005, 0x3c020080, 0x3c010002, 0x310821, 
+0xac209004, 0x3c020080, 0x2021024, 0x10400007, 
+0x121940, 0x3c020001, 0x3c010002, 0x230821, 
+0xac22900c, 0x10000006, 0x24040001, 0x121140, 
+0x3c010002, 0x220821, 0xac20900c, 0x24040001, 
+0x2821, 0x27b0001e, 0xc00457c, 0x2003021, 
+0x24040001, 0x2821, 0xc00457c, 0x2003021, 
+0x24040001, 0x24050001, 0x27b0001c, 0xc00457c, 
+0x2003021, 0x24040001, 0x24050001, 0xc00457c, 
+0x2003021, 0x10000077, 0x0, 0x3c02ffec, 
+0x3442ffff, 0x2028024, 0x3c020008, 0x2028025, 
+0x121140, 0x3c010002, 0x220821, 0xac308ff8, 
+0x3c022000, 0x2021024, 0x10400009, 0x0, 
+0x3c020001, 0x8c426e44, 0x14400005, 0x24020001, 
+0x3c010001, 0xac226f1c, 0x10000004, 0x3c024000, 
+0x3c010001, 0xac206f1c, 0x3c024000, 0x2021024, 
+0x1440001d, 0x24020e01, 0x3c030001, 0x8c636f1c, 
+0xaf820238, 0x3c010001, 0xac206db0, 0x10600005, 
+0x24022020, 0x3c010001, 0xac226f20, 0x24020001, 
+0xaee204b8, 0x3c04bfff, 0x121940, 0x3c020002, 
+0x431021, 0x8c428ff0, 0x3c050001, 0x8ca56d98, 
+0x3484ffff, 0x441024, 0x3c010002, 0x230821, 
+0xac228ff0, 0x24020001, 0x10a20044, 0x0, 
+0x10000040, 0x0, 0x3c020001, 0x8c426f1c, 
+0x1040001c, 0x24022000, 0x3c010001, 0xac226f20, 
+0x3c0300a0, 0x2031024, 0x14430005, 0x121140, 
+0x3402a000, 0x3c010001, 0x1000002d, 0xac226f20, 
+0x3c030002, 0x621821, 0x8c638ff8, 0x3c020020, 
+0x621024, 0x10400004, 0x24022001, 0x3c010001, 
+0x10000023, 0xac226f20, 0x3c020080, 0x621024, 
+0x1040001f, 0x3402a001, 0x3c010001, 0x1000001c, 
+0xac226f20, 0x3c020020, 0x2021024, 0x10400007, 
+0x121940, 0x24020100, 0x3c010002, 0x230821, 
+0xac229004, 0x10000006, 0x3c020080, 0x121140, 
+0x3c010002, 0x220821, 0xac209004, 0x3c020080, 
+0x2021024, 0x10400006, 0x121940, 0x3c020001, 
+0x3c010002, 0x230821, 0x10000005, 0xac22900c, 
+0x121140, 0x3c010002, 0x220821, 0xac20900c, 
+0x3c030001, 0x8c636d98, 0x24020001, 0x10620003, 
+0x0, 0xc003daf, 0x0, 0x8fbf0030, 
+0x8fb3002c, 0x8fb20028, 0x8fb10024, 0x8fb00020, 
+0x3e00008, 0x27bd0038, 0x27bdffb0, 0xafb3003c, 
+0x9821, 0xafb50040, 0xa821, 0xafb10034, 
+0x8821, 0x24020002, 0xafbf0048, 0xafbe0044, 
+0xafb20038, 0xafb00030, 0xafa4002c, 0xa7a0001a, 
+0xa7a00018, 0xa7a00020, 0xa7a0001e, 0xa7a00022, 
+0x10a20130, 0xa7a0001c, 0x2ca20003, 0x10400005, 
+0x24020001, 0x10a2000a, 0x3c024000, 0x1000025d, 
+0x2201021, 0x24020004, 0x10a2020a, 0x24020008, 
+0x10a20208, 0x2201021, 0x10000256, 0x0, 
+0x8fa8002c, 0x88140, 0x3c030002, 0x701821, 
+0x8c638ffc, 0x621024, 0x14400009, 0x24040001, 
+0x3c027fff, 0x3442ffff, 0x628824, 0x3c010002, 
+0x300821, 0xac318ff4, 0x10000246, 0x2201021, 
+0x24050001, 0xc00457c, 0x27a60018, 0x24040001, 
+0x24050001, 0xc00457c, 0x27a60018, 0x97a20018, 
+0x30420004, 0x104000d9, 0x3c114000, 0x3c020001, 
+0x8c426f40, 0x2443ffff, 0x2c620006, 0x104000d9, 
+0x31080, 0x3c010001, 0x220821, 0x8c226c68, 
+0x400008, 0x0, 0x24040001, 0x24050011, 
+0x27b0001a, 0xc00457c, 0x2003021, 0x24040001, 
+0x24050011, 0xc00457c, 0x2003021, 0x97a3001a, 
+0x30624000, 0x10400002, 0x3c150010, 0x3c150008, 
+0x30628000, 0x104000aa, 0x3c130001, 0x100000a8, 
+0x3c130002, 0x24040001, 0x24050014, 0x27b0001a, 
+0xc00457c, 0x2003021, 0x24040001, 0x24050014, 
+0xc00457c, 0x2003021, 0x97a3001a, 0x30621000, 
+0x10400002, 0x3c150010, 0x3c150008, 0x30620800, 
+0x10400097, 0x3c130001, 0x10000095, 0x3c130002, 
+0x24040001, 0x24050019, 0x27b0001c, 0xc00457c, 
+0x2003021, 0x24040001, 0x24050019, 0xc00457c, 
+0x2003021, 0x97a2001c, 0x30430700, 0x24020400, 
+0x10620027, 0x28620401, 0x1040000e, 0x24020200, 
+0x1062001f, 0x28620201, 0x10400005, 0x24020100, 
+0x5062001e, 0x3c130001, 0x1000001e, 0x24040001, 
+0x24020300, 0x50620019, 0x3c130002, 0x10000019, 
+0x24040001, 0x24020600, 0x1062000d, 0x28620601, 
+0x10400005, 0x24020500, 0x5062000b, 0x3c130002, 
+0x10000010, 0x24040001, 0x24020700, 0x1462000d, 
+0x24040001, 0x3c130004, 0x1000000a, 0x3c150008, 
+0x10000006, 0x3c130004, 0x10000005, 0x3c150008, 
+0x3c130001, 0x10000002, 0x3c150008, 0x3c150010, 
+0x24040001, 0x24050018, 0x27b0001e, 0xc00457c, 
+0x2003021, 0x24040001, 0x24050018, 0xc00457c, 
+0x2003021, 0x8fa8002c, 0x97a7001e, 0x81140, 
+0x3c060002, 0xc23021, 0x8cc68ff4, 0x97a20022, 
+0x3c100001, 0x26106c5c, 0x2002021, 0xafa20010, 
+0x97a2001c, 0x3c05000c, 0x34a50303, 0xc002b3b, 
+0xafa20014, 0x3c020004, 0x16620010, 0x3c020001, 
+0x8f840054, 0x24030001, 0x24020002, 0x3c010001, 
+0xac236d9c, 0x3c010001, 0xac226d98, 0x3c010001, 
+0xac236da4, 0x3c010001, 0xac236e24, 0x3c010001, 
+0xac246f30, 0x1000004f, 0x2b38825, 0x16620039, 
+0x3c028000, 0x3c020001, 0x8c426e20, 0x1440001e, 
+0x24040018, 0x2021, 0x2821, 0xc004ddb, 
+0x34068000, 0x8f830054, 0x8f820054, 0x2b38825, 
+0x10000002, 0x24630032, 0x8f820054, 0x621023, 
+0x2c420033, 0x1440fffc, 0x0, 0x8f830054, 
+0x24020001, 0x3c010001, 0xac226e20, 0x3c010001, 
+0xac226d9c, 0x3c010001, 0xac226d98, 0x3c010001, 
+0xac226da4, 0x3c010001, 0xac226e24, 0x3c010001, 
+0x1000002c, 0xac236f30, 0x2821, 0xc004ddb, 
+0x24060404, 0x2021, 0x2405001e, 0x27a60018, 
+0x24020002, 0xc0045be, 0xa7a20018, 0x2021, 
+0x2821, 0x27a60018, 0xc0045be, 0xa7a00018, 
+0x24040018, 0x24050002, 0xc004ddb, 0x24060004, 
+0x3c028000, 0x2221025, 0x2b31825, 0x10000015, 
+0x438825, 0x2221025, 0x2751825, 0x438825, 
+0x2002021, 0x97a6001c, 0x3c070001, 0x8ce76d98, 
+0x3c05000c, 0x34a50326, 0xafb30010, 0xc002b3b, 
+0xafb10014, 0x10000007, 0x0, 0x3c110002, 
+0x2308821, 0x8e318ffc, 0x3c027fff, 0x3442ffff, 
+0x2228824, 0x3c020001, 0x8c426da8, 0x1040001e, 
+0x0, 0x3c020001, 0x8c426f1c, 0x10400002, 
+0x3c022000, 0x2228825, 0x8fa8002c, 0x81140, 
+0x3c010002, 0x220821, 0x8c229000, 0x10400003, 
+0x3c020020, 0x10000005, 0x2228825, 0x3c02ffdf, 
+0x3442ffff, 0x2228824, 0x8fa8002c, 0x81140, 
+0x3c010002, 0x220821, 0x8c229008, 0x10400003, 
+0x3c020080, 0x10000004, 0x2228825, 0x3c02ff7f, 
+0x3442ffff, 0x2228824, 0x8fa8002c, 0x81140, 
+0x3c010002, 0x220821, 0xac318ff4, 0x10000135, 
+0x2201021, 0x8fa8002c, 0x8f140, 0x3c030002, 
+0x7e1821, 0x8c638ff8, 0x3c024000, 0x621024, 
+0x14400009, 0x24040001, 0x3c027fff, 0x3442ffff, 
+0x628824, 0x3c010002, 0x3e0821, 0xac318ff0, 
+0x10000124, 0x2201021, 0x2821, 0xc00457c, 
+0x27a60018, 0x24040001, 0x2821, 0xc00457c, 
+0x27a60018, 0x24040001, 0x24050001, 0x27b20020, 
+0xc00457c, 0x2403021, 0x24040001, 0x24050001, 
+0xc00457c, 0x2403021, 0x24040001, 0x24050004, 
+0x27b1001e, 0xc00457c, 0x2203021, 0x24040001, 
+0x24050004, 0xc00457c, 0x2203021, 0x24040001, 
+0x24050005, 0x27b00022, 0xc00457c, 0x2003021, 
+0x24040001, 0x24050005, 0xc00457c, 0x2003021, 
+0x24040001, 0x24050010, 0xc00457c, 0x27a60018, 
+0x24040001, 0x24050010, 0xc00457c, 0x27a60018, 
+0x24040001, 0x2405000a, 0xc00457c, 0x2403021, 
+0x24040001, 0x2405000a, 0xc00457c, 0x2403021, 
+0x24040001, 0x24050018, 0xc00457c, 0x2203021, 
+0x24040001, 0x24050018, 0xc00457c, 0x2203021, 
+0x24040001, 0x24050001, 0xc00457c, 0x27a60018, 
+0x24040001, 0x24050001, 0xc00457c, 0x27a60018, 
+0x97a20018, 0x30420004, 0x10400066, 0x3c114000, 
+0x3c030001, 0x8c636f34, 0x24020005, 0x14620067, 
+0x24040001, 0x24050019, 0x27b0001c, 0xc00457c, 
+0x2003021, 0x24040001, 0x24050019, 0xc00457c, 
+0x2003021, 0x97a2001c, 0x30430700, 0x24020400, 
+0x10620027, 0x28620401, 0x1040000e, 0x24020200, 
+0x1062001f, 0x28620201, 0x10400005, 0x24020100, 
+0x5062001e, 0x3c130001, 0x1000001e, 0x3c020004, 
+0x24020300, 0x50620019, 0x3c130002, 0x10000019, 
+0x3c020004, 0x24020600, 0x1062000d, 0x28620601, 
+0x10400005, 0x24020500, 0x5062000b, 0x3c130002, 
+0x10000010, 0x3c020004, 0x24020700, 0x1462000d, 
+0x3c020004, 0x3c130004, 0x1000000a, 0x3c150008, 
+0x10000006, 0x3c130004, 0x10000005, 0x3c150008, 
+0x3c130001, 0x10000002, 0x3c150008, 0x3c150010, 
+0x3c020004, 0x12620017, 0x3c028000, 0x8f820054, 
+0x24100001, 0x3c010001, 0xac306d9c, 0x3c010001, 
+0xac306d98, 0x3c010001, 0xac306da4, 0x3c010001, 
+0xac306e24, 0x3c010001, 0xac226f30, 0x3c020001, 
+0x16620022, 0x2758825, 0x2021, 0x2821, 
+0xc004ddb, 0x34068000, 0x3c010001, 0x1000001b, 
+0xac306e20, 0x2221025, 0x2b31825, 0x438825, 
+0x97a6001c, 0x3c020001, 0x8c426f1c, 0x3c070001, 
+0x8ce76d98, 0x3c040001, 0x24846c5c, 0xafa20010, 
+0x97a2001e, 0x3c05000c, 0x34a50323, 0x3c010001, 
+0xac206e20, 0xc002b3b, 0xafa20014, 0x10000007, 
+0x0, 0x3c110002, 0x23e8821, 0x8e318ff0, 
+0x3c027fff, 0x3442ffff, 0x2228824, 0x3c020001, 
+0x8c426da8, 0x10400069, 0x0, 0x3c020001, 
+0x8c426f1c, 0x10400002, 0x3c022000, 0x2228825, 
+0x8fa8002c, 0x81140, 0x3c010002, 0x220821, 
+0x8c229004, 0x10400003, 0x3c020020, 0x10000005, 
+0x2228825, 0x3c02ffdf, 0x3442ffff, 0x2228824, 
+0x8fa8002c, 0x81140, 0x3c010002, 0x220821, 
+0x8c22900c, 0x10400003, 0x3c020080, 0x1000004f, 
+0x2228825, 0x3c02ff7f, 0x3442ffff, 0x1000004b, 
+0x2228824, 0x8fa8002c, 0x82940, 0x3c030002, 
+0x651821, 0x8c638ff8, 0x3c024000, 0x621024, 
+0x14400008, 0x3c027fff, 0x3442ffff, 0x628824, 
+0x3c010002, 0x250821, 0xac318ff0, 0x10000041, 
+0x2201021, 0x3c020001, 0x8c426da8, 0x10400034, 
+0x3c11c00c, 0x3c020001, 0x8c426e44, 0x3c04c00c, 
+0x34842000, 0x3c030001, 0x8c636f1c, 0x2102b, 
+0x21023, 0x441024, 0x10600003, 0x518825, 
+0x3c022000, 0x2228825, 0x3c020002, 0x451021, 
+0x8c429004, 0x10400003, 0x3c020020, 0x10000004, 
+0x2228825, 0x3c02ffdf, 0x3442ffff, 0x2228824, 
+0x8fa8002c, 0x81140, 0x3c010002, 0x220821, 
+0x8c22900c, 0x10400003, 0x3c020080, 0x10000004, 
+0x2228825, 0x3c02ff7f, 0x3442ffff, 0x2228824, 
+0x3c020001, 0x8c426e30, 0x10400002, 0x3c020800, 
+0x2228825, 0x3c020001, 0x8c426e34, 0x10400002, 
+0x3c020400, 0x2228825, 0x3c020001, 0x8c426e38, 
+0x10400006, 0x3c020100, 0x10000004, 0x2228825, 
+0x3c027fff, 0x3442ffff, 0x628824, 0x8fa8002c, 
+0x81140, 0x3c010002, 0x220821, 0xac318ff0, 
+0x2201021, 0x8fbf0048, 0x8fbe0044, 0x8fb50040, 
+0x8fb3003c, 0x8fb20038, 0x8fb10034, 0x8fb00030, 
+0x3e00008, 0x27bd0050, 0x27bdffd0, 0xafb20028, 
+0x809021, 0xafbf002c, 0xafb10024, 0xafb00020, 
+0x8f840200, 0x3c100001, 0x8e106d98, 0x8f860220, 
+0x24020002, 0x1202005c, 0x2e020003, 0x10400005, 
+0x24020001, 0x1202000a, 0x121940, 0x1000010c, 
+0x0, 0x24020004, 0x120200bf, 0x24020008, 
+0x120200be, 0x128940, 0x10000105, 0x0, 
+0x3c050002, 0xa32821, 0x8ca58ffc, 0x3c100002, 
+0x2038021, 0x8e108ff4, 0x3c024000, 0xa21024, 
+0x10400038, 0x3c020008, 0x2021024, 0x10400020, 
+0x34840002, 0x3c020002, 0x431021, 0x8c429000, 
+0x10400005, 0x34840020, 0x34840100, 0x3c020020, 
+0x10000006, 0x2028025, 0x2402feff, 0x822024, 
+0x3c02ffdf, 0x3442ffff, 0x2028024, 0x121140, 
+0x3c010002, 0x220821, 0x8c229008, 0x10400005, 
+0x3c020001, 0xc23025, 0x3c020080, 0x10000016, 
+0x2028025, 0x3c02fffe, 0x3442ffff, 0xc23024, 
+0x3c02ff7f, 0x3442ffff, 0x1000000f, 0x2028024, 
+0x2402fedf, 0x822024, 0x3c02fffe, 0x3442ffff, 
+0xc23024, 0x3c02ff5f, 0x3442ffff, 0x2028024, 
+0x3c010002, 0x230821, 0xac209000, 0x3c010002, 
+0x230821, 0xac209008, 0xaf840200, 0xaf860220, 
+0x8f820220, 0x34420002, 0xaf820220, 0x1000000a, 
+0x121140, 0x3c02bfff, 0x3442ffff, 0x8f830200, 
+0x2028024, 0x2402fffd, 0x621824, 0xc003daf, 
+0xaf830200, 0x121140, 0x3c010002, 0x220821, 
+0x100000b7, 0xac308ff4, 0x3c020001, 0x8c426f1c, 
+0x10400069, 0x24050004, 0x24040001, 0xc00457c, 
+0x27a60018, 0x24040001, 0x24050005, 0xc00457c, 
+0x27a6001a, 0x97a30018, 0x97a2001a, 0x3c040001, 
+0x24846e48, 0x30630c00, 0x31a82, 0x30420c00, 
+0x21282, 0xa7a2001a, 0x21080, 0x441021, 
+0x431021, 0xa7a30018, 0x90480000, 0x24020001, 
+0x3103ffff, 0x10620029, 0x28620002, 0x10400005, 
+0x0, 0x10600009, 0x0, 0x1000003d, 
+0x0, 0x10700013, 0x24020003, 0x1062002c, 
+0x0, 0x10000037, 0x0, 0x8f820200, 
+0x2403feff, 0x431024, 0xaf820200, 0x8f820220, 
+0x3c03fffe, 0x3463ffff, 0x431024, 0xaf820220, 
+0x3c010002, 0xac209004, 0x3c010002, 0x10000032, 
+0xac20900c, 0x8f820200, 0x34420100, 0xaf820200, 
+0x8f820220, 0x3c03fffe, 0x3463ffff, 0x431024, 
+0xaf820220, 0x24020100, 0x3c010002, 0xac229004, 
+0x3c010002, 0x10000024, 0xac20900c, 0x8f820200, 
+0x2403feff, 0x431024, 0xaf820200, 0x8f820220, 
+0x3c030001, 0x431025, 0xaf820220, 0x3c010002, 
+0xac209004, 0x3c010002, 0x10000017, 0xac23900c, 
+0x8f820200, 0x34420100, 0xaf820200, 0x8f820220, 
+0x3c030001, 0x431025, 0xaf820220, 0x24020100, 
+0x3c010002, 0xac229004, 0x3c010002, 0x1000000a, 
+0xac23900c, 0x3c040001, 0x24846c80, 0x97a6001a, 
+0x97a70018, 0x3c050001, 0x34a5ffff, 0xafa80010, 
+0xc002b3b, 0xafa00014, 0x8f820200, 0x34420002, 
+0x1000004b, 0xaf820200, 0x128940, 0x3c050002, 
+0xb12821, 0x8ca58ff8, 0x3c100002, 0x2118021, 
+0x8e108ff0, 0x3c024000, 0xa21024, 0x14400010, 
+0x0, 0x3c020001, 0x8c426f1c, 0x14400005, 
+0x3c02bfff, 0x8f820200, 0x34420002, 0xaf820200, 
+0x3c02bfff, 0x3442ffff, 0xc003daf, 0x2028024, 
+0x3c010002, 0x310821, 0x10000031, 0xac308ff0, 
+0x3c020001, 0x8c426f1c, 0x10400005, 0x3c020020, 
+0x3c020001, 0x8c426e44, 0x10400025, 0x3c020020, 
+0xa21024, 0x10400007, 0x34840020, 0x24020100, 
+0x3c010002, 0x310821, 0xac229004, 0x10000006, 
+0x34840100, 0x3c010002, 0x310821, 0xac209004, 
+0x2402feff, 0x822024, 0x3c020080, 0xa21024, 
+0x10400007, 0x121940, 0x3c020001, 0x3c010002, 
+0x230821, 0xac22900c, 0x10000008, 0xc23025, 
+0x121140, 0x3c010002, 0x220821, 0xac20900c, 
+0x3c02fffe, 0x3442ffff, 0xc23024, 0xaf840200, 
+0xaf860220, 0x8f820220, 0x34420002, 0xaf820220, 
+0x121140, 0x3c010002, 0x220821, 0xac308ff0, 
+0x8fbf002c, 0x8fb20028, 0x8fb10024, 0x8fb00020, 
+0x3e00008, 0x27bd0030, 0x0, 0x1821, 
+0x308400ff, 0x2405ffdf, 0x2406ffbf, 0x641007, 
+0x30420001, 0x10400004, 0x0, 0x8f820044, 
+0x10000003, 0x34420040, 0x8f820044, 0x461024, 
+0xaf820044, 0x8f820044, 0x34420020, 0xaf820044, 
+0x8f820044, 0x451024, 0xaf820044, 0x24630001, 
+0x28620008, 0x5440ffee, 0x641007, 0x3e00008, 
+0x0, 0x2c820008, 0x1040001b, 0x0, 
+0x2405ffdf, 0x2406ffbf, 0x41880, 0x3c020001, 
+0x24426e60, 0x621821, 0x24640004, 0x90620000, 
+0x10400004, 0x0, 0x8f820044, 0x10000003, 
+0x34420040, 0x8f820044, 0x461024, 0xaf820044, 
+0x8f820044, 0x34420020, 0xaf820044, 0x8f820044, 
+0x451024, 0xaf820044, 0x24630001, 0x64102b, 
+0x1440ffee, 0x0, 0x3e00008, 0x0, 
+0x0, 0x0, 0x0, 0x8f8400c4, 
+0x8f8600e0, 0x8f8700e4, 0x2402fff8, 0xc22824, 
+0x10e5001a, 0x27623ff8, 0x14e20002, 0x24e80008, 
+0x27683000, 0x55050004, 0x8d0a0000, 0x30c20004, 
+0x14400012, 0x805021, 0x8ce90000, 0x8f42013c, 
+0x1494823, 0x49182b, 0x94eb0006, 0x10600002, 
+0x25630050, 0x494821, 0x123182b, 0x50400003, 
+0x8f4201fc, 0x3e00008, 0xe01021, 0xaf8800e8, 
+0x24420001, 0xaf4201fc, 0xaf8800e4, 0x3e00008, 
+0x1021, 0x3e00008, 0x0, 0x8f8300e4, 
+0x27623ff8, 0x10620004, 0x24620008, 0xaf8200e8, 
+0x3e00008, 0xaf8200e4, 0x27623000, 0xaf8200e8, 
+0x3e00008, 0xaf8200e4, 0x3e00008, 0x0, 
+0x0, 0x0, 0x0, 0x8f880120, 
+0x27624fe0, 0x8f830128, 0x15020002, 0x25090020, 
+0x27694800, 0x11230012, 0x8fa20010, 0xad040000, 
+0xad050004, 0xad060008, 0xa507000e, 0x8fa30014, 
+0xad020018, 0x8fa20018, 0xad03001c, 0x25030016, 
+0xad020010, 0xad030014, 0xaf890120, 0x8f4300fc, 
+0x24020001, 0x2463ffff, 0x3e00008, 0xaf4300fc, 
+0x8f430324, 0x1021, 0x24630001, 0x3e00008, 
+0xaf430324, 0x3e00008, 0x0, 0x8f880100, 
+0x276247e0, 0x8f830108, 0x15020002, 0x25090020, 
+0x27694000, 0x1123000f, 0x8fa20010, 0xad040000, 
+0xad050004, 0xad060008, 0xa507000e, 0x8fa30014, 
+0xad020018, 0x8fa20018, 0xad03001c, 0x25030016, 
+0xad020010, 0xad030014, 0xaf890100, 0x3e00008, 
+0x24020001, 0x8f430328, 0x1021, 0x24630001, 
+0x3e00008, 0xaf430328, 0x3e00008, 0x0, 
+0x0, 0x0, 0x0, 0x0 };
+static int tigon2FwRodata[/*(MAX_RODATA_LEN/4) + 1*/] = {
+0x24486561, 0x6465723a, 0x202f7072, 
+0x6f6a6563, 0x74732f72, 0x63732f73, 0x772f6765, 
+0x2f2e2f6e, 0x69632f66, 0x77322f63, 0x6f6d6d6f, 
+0x6e2f6677, 0x6d61696e, 0x2e632c76, 0x20312e31, 
+0x2e322e34, 0x35203139, 0x39392f30, 0x312f3234, 
+0x2030303a, 0x31303a35, 0x35207368, 0x75616e67, 
+0x20457870, 0x20240000, 0x65767452, 0x6e674600, 
+0x51657674, 0x46000000, 0x51657674, 0x505f4600, 
+0x4d657674, 0x526e6746, 0x0, 0x4d516576, 
+0x74460000, 0x4d516576, 0x505f4600, 0x5173436f, 
+0x6e495f46, 0x0, 0x5173436f, 0x6e734600, 
+0x51725072, 0x6f644600, 0x6261644d, 0x656d537a, 
+0x0, 0x68775665, 0x72000000, 0x62616448, 
+0x77566572, 0x0, 0x2a2a4441, 0x574e5f41, 
+0x0, 0x74785278, 0x4266537a, 0x0, 
+0x62664174, 0x6e4d726b, 0x0, 0x7265645a, 
+0x6f6e6531, 0x0, 0x70636943, 0x6f6e6600, 
+0x67656e43, 0x6f6e6600, 0x2a646d61, 0x5244666c, 
+0x0, 0x2a50414e, 0x49432a00, 0x2e2e2f2e, 
+0x2e2f2e2e, 0x2f2e2e2f, 0x2e2e2f73, 0x72632f6e, 
+0x69632f66, 0x77322f63, 0x6f6d6d6f, 0x6e2f6677, 
+0x6d61696e, 0x2e630000, 0x72636246, 0x6c616773, 
+0x0, 0x62616452, 0x78526362, 0x0, 
+0x676c6f62, 0x466c6773, 0x0, 0x2b5f6469, 
+0x73705f6c, 0x6f6f7000, 0x2b65765f, 0x68616e64, 
+0x6c657200, 0x63616e74, 0x31446d61, 0x0, 
+0x2b715f64, 0x6d615f74, 0x6f5f6e69, 0x635f636b, 
+0x73756d00, 0x2b685f73, 0x656e645f, 0x64617461, 
+0x5f726561, 0x64795f63, 0x6b73756d, 0x0, 
+0x2b685f64, 0x6d615f72, 0x645f6173, 0x73697374, 
+0x5f636b73, 0x756d0000, 0x74436b73, 0x6d4f6e00, 
+0x2b715f64, 0x6d615f74, 0x6f5f6e69, 0x63000000, 
+0x2b685f73, 0x656e645f, 0x64617461, 0x5f726561, 
+0x64790000, 0x2b685f64, 0x6d615f72, 0x645f6173, 
+0x73697374, 0x0, 0x74436b73, 0x6d4f6666, 
+0x0, 0x2b685f73, 0x656e645f, 0x62645f72, 
+0x65616479, 0x0, 0x68737453, 0x52696e67, 
+0x0, 0x62616453, 0x52696e67, 0x0, 
+0x6e696353, 0x52696e67, 0x0, 0x77446d61, 
+0x416c6c41, 0x0, 0x2b715f64, 0x6d615f74, 
+0x6f5f686f, 0x73745f63, 0x6b73756d, 0x0, 
+0x2b685f6d, 0x61635f72, 0x785f636f, 0x6d705f63, 
+0x6b73756d, 0x0, 0x2b685f64, 0x6d615f77, 
+0x725f6173, 0x73697374, 0x5f636b73, 0x756d0000, 
+0x72436b73, 0x6d4f6e00, 0x2b715f64, 0x6d615f74, 
+0x6f5f686f, 0x73740000, 0x2b685f6d, 0x61635f72, 
+0x785f636f, 0x6d700000, 0x2b685f64, 0x6d615f77, 
+0x725f6173, 0x73697374, 0x0, 0x72436b73, 
+0x6d4f6666, 0x0, 0x2b685f72, 0x6563765f, 
+0x62645f72, 0x65616479, 0x0, 0x2b685f72, 
+0x6563765f, 0x6a756d62, 0x6f5f6264, 0x5f726561, 
+0x64790000, 0x2b685f72, 0x6563765f, 0x6d696e69, 
+0x5f62645f, 0x72656164, 0x79000000, 0x2b6d685f, 
+0x636f6d6d, 0x616e6400, 0x2b685f74, 0x696d6572, 
+0x0, 0x2b685f64, 0x6f5f7570, 0x64617465, 
+0x5f74785f, 0x636f6e73, 0x0, 0x2b685f64, 
+0x6f5f7570, 0x64617465, 0x5f72785f, 0x70726f64, 
+0x0, 0x2b636b73, 0x756d3136, 0x0, 
+0x2b706565, 0x6b5f6d61, 0x635f7278, 0x5f776100, 
+0x2b706565, 0x6b5f6d61, 0x635f7278, 0x0, 
+0x2b646571, 0x5f6d6163, 0x5f727800, 0x2b685f6d, 
+0x61635f72, 0x785f6174, 0x746e0000, 0x62616452, 
+0x6574537a, 0x0, 0x72784264, 0x4266537a, 
+0x0, 0x2b6e756c, 0x6c5f6861, 0x6e646c65, 
+0x72000000, 0x66774f70, 0x4661696c, 0x0, 
+0x2b685f75, 0x70646174, 0x655f6c65, 0x64340000, 
+0x2b685f75, 0x70646174, 0x655f6c65, 0x64360000, 
+0x2b685f75, 0x70646174, 0x655f6c65, 0x64320000, 
+0x696e7453, 0x74617465, 0x0, 0x2a2a696e, 
+0x69744370, 0x0, 0x23736372, 0x65616d00, 
+0x69537461, 0x636b4572, 0x0, 0x70726f62, 
+0x654d656d, 0x0, 0x2a2a4441, 0x574e5f42, 
+0x0, 0x2b73775f, 0x646d615f, 0x61737369, 
+0x73745f70, 0x6c75735f, 0x74696d65, 0x72000000, 
+0x2b267072, 0x656c6f61, 0x645f7772, 0x5f646573, 
+0x63720000, 0x2b267072, 0x656c6f61, 0x645f7264, 
+0x5f646573, 0x63720000, 0x2b685f68, 0x665f7469, 
+0x6d657200, 0x24486561, 0x6465723a, 0x202f7072, 
+0x6f6a6563, 0x74732f72, 0x63732f73, 0x772f6765, 
+0x2f2e2f6e, 0x69632f66, 0x77322f63, 0x6f6d6d6f, 
+0x6e2f7469, 0x6d65722e, 0x632c7620, 0x312e312e, 
+0x322e3335, 0x20313939, 0x392f3031, 0x2f323720, 
+0x31393a30, 0x393a3530, 0x20686179, 0x65732045, 
+0x78702024, 0x0, 0x65767452, 0x6e674600, 
+0x51657674, 0x46000000, 0x51657674, 0x505f4600, 
+0x4d657674, 0x526e6746, 0x0, 0x4d516576, 
+0x74460000, 0x4d516576, 0x505f4600, 0x5173436f, 
+0x6e495f46, 0x0, 0x5173436f, 0x6e734600, 
+0x51725072, 0x6f644600, 0x542d446d, 0x61526432, 
+0x0, 0x542d446d, 0x61526431, 0x0, 
+0x542d446d, 0x61526442, 0x0, 0x542d446d, 
+0x61577232, 0x0, 0x542d446d, 0x61577231, 
+0x0, 0x542d446d, 0x61577242, 0x0, 
+0x0, 0x24486561, 0x6465723a, 0x202f7072, 
+0x6f6a6563, 0x74732f72, 0x63732f73, 0x772f6765, 
+0x2f2e2f6e, 0x69632f66, 0x77322f63, 0x6f6d6d6f, 
+0x6e2f636f, 0x6d6d616e, 0x642e632c, 0x7620312e, 
+0x312e322e, 0x32382031, 0x3939392f, 0x30312f32, 
+0x30203139, 0x3a34393a, 0x34392073, 0x6875616e, 
+0x67204578, 0x70202400, 0x65767452, 0x6e674600, 
+0x51657674, 0x46000000, 0x51657674, 0x505f4600, 
+0x4d657674, 0x526e6746, 0x0, 0x4d516576, 
+0x74460000, 0x4d516576, 0x505f4600, 0x5173436f, 
+0x6e495f46, 0x0, 0x5173436f, 0x6e734600, 
+0x51725072, 0x6f644600, 0x3f48636d, 0x644d6278, 
+0x0, 0x3f636d64, 0x48737453, 0x0, 
+0x3f636d64, 0x4d634d64, 0x0, 0x3f636d64, 
+0x50726f6d, 0x0, 0x3f636d64, 0x4c696e6b, 
+0x0, 0x3f636d64, 0x45727200, 0x86ac, 
+0x8e5c, 0x8e5c, 0x8de4, 0x8b78, 
+0x8e30, 0x8e5c, 0x8790, 0x8800, 
+0x8990, 0x8a68, 0x8a34, 0x8e5c, 
+0x8870, 0x8b24, 0x8e5c, 0x8b34, 
+0x87b4, 0x8824, 0x0, 0x0, 
+0x0, 0x24486561, 0x6465723a, 0x202f7072, 
+0x6f6a6563, 0x74732f72, 0x63732f73, 0x772f6765, 
+0x2f2e2f6e, 0x69632f66, 0x77322f63, 0x6f6d6d6f, 
+0x6e2f6d63, 0x6173742e, 0x632c7620, 0x312e312e, 
+0x322e3820, 0x31393938, 0x2f31322f, 0x30382030, 
+0x323a3336, 0x3a333620, 0x73687561, 0x6e672045, 
+0x78702024, 0x0, 0x65767452, 0x6e674600, 
+0x51657674, 0x46000000, 0x51657674, 0x505f4600, 
+0x4d657674, 0x526e6746, 0x0, 0x4d516576, 
+0x74460000, 0x4d516576, 0x505f4600, 0x5173436f, 
+0x6e495f46, 0x0, 0x5173436f, 0x6e734600, 
+0x51725072, 0x6f644600, 0x6164644d, 0x63447570, 
+0x0, 0x6164644d, 0x6346756c, 0x0, 
+0x64656c4d, 0x634e6f45, 0x0, 0x0, 
+0x0, 0x24486561, 0x6465723a, 0x202f7072, 
+0x6f6a6563, 0x74732f72, 0x63732f73, 0x772f6765, 
+0x2f2e2f6e, 0x69632f66, 0x77322f63, 0x6f6d6d6f, 
+0x6e2f646d, 0x612e632c, 0x7620312e, 0x312e322e, 
+0x32342031, 0x3939382f, 0x31322f32, 0x31203030, 
+0x3a33333a, 0x30392073, 0x6875616e, 0x67204578, 
+0x70202400, 0x65767452, 0x6e674600, 0x51657674, 
+0x46000000, 0x51657674, 0x505f4600, 0x4d657674, 
+0x526e6746, 0x0, 0x4d516576, 0x74460000, 
+0x4d516576, 0x505f4600, 0x5173436f, 0x6e495f46, 
+0x0, 0x5173436f, 0x6e734600, 0x51725072, 
+0x6f644600, 0x7377446d, 0x614f6666, 0x0, 
+0x31446d61, 0x4f6e0000, 0x7377446d, 0x614f6e00, 
+0x2372446d, 0x6141544e, 0x0, 0x72446d61, 
+0x41544e30, 0x0, 0x72446d61, 0x41544e31, 
+0x0, 0x72446d61, 0x34476200, 0x2a50414e, 
+0x49432a00, 0x2e2e2f2e, 0x2e2f2e2e, 0x2f2e2e2f, 
+0x2e2e2f73, 0x72632f6e, 0x69632f66, 0x77322f63, 
+0x6f6d6d6f, 0x6e2f646d, 0x612e6300, 0x2377446d, 
+0x6141544e, 0x0, 0x77446d61, 0x41544e30, 
+0x0, 0x77446d61, 0x41544e31, 0x0, 
+0x77446d61, 0x34476200, 0x0, 0x0, 
+0x0, 0x24486561, 0x6465723a, 0x202f7072, 
+0x6f6a6563, 0x74732f72, 0x63732f73, 0x772f6765, 
+0x2f2e2f6e, 0x69632f66, 0x77322f63, 0x6f6d6d6f, 
+0x6e2f7472, 0x6163652e, 0x632c7620, 0x312e312e, 
+0x322e3520, 0x31393938, 0x2f30392f, 0x33302031, 
+0x383a3530, 0x3a323820, 0x73687561, 0x6e672045, 
+0x78702024, 0x0, 0x0, 0x0, 
+0x0, 0x24486561, 0x6465723a, 0x202f7072, 
+0x6f6a6563, 0x74732f72, 0x63732f73, 0x772f6765, 
+0x2f2e2f6e, 0x69632f66, 0x77322f63, 0x6f6d6d6f, 
+0x6e2f6461, 0x74612e63, 0x2c762031, 0x2e312e32, 
+0x2e313220, 0x31393939, 0x2f30312f, 0x32302031, 
+0x393a3439, 0x3a353120, 0x73687561, 0x6e672045, 
+0x78702024, 0x0, 0x46575f56, 0x45525349, 
+0x4f4e3a20, 0x23312046, 0x72692041, 0x70722037, 
+0x2031373a, 0x35373a35, 0x32205044, 0x54203230, 
+0x30300000, 0x46575f43, 0x4f4d5049, 0x4c455f54, 
+0x494d453a, 0x2031373a, 0x35373a35, 0x32000000, 
+0x46575f43, 0x4f4d5049, 0x4c455f42, 0x593a2064, 
+0x65767263, 0x73000000, 0x46575f43, 0x4f4d5049, 
+0x4c455f48, 0x4f53543a, 0x20636f6d, 0x70757465, 
+0x0, 0x46575f43, 0x4f4d5049, 0x4c455f44, 
+0x4f4d4149, 0x4e3a2065, 0x6e672e61, 0x6374656f, 
+0x6e2e636f, 0x6d000000, 0x46575f43, 0x4f4d5049, 
+0x4c45523a, 0x20676363, 0x20766572, 0x73696f6e, 
+0x20322e37, 0x2e320000, 0x0, 0x12041100, 
+0x0, 0x24486561, 0x6465723a, 0x202f7072, 
+0x6f6a6563, 0x74732f72, 0x63732f73, 0x772f6765, 
+0x2f2e2f6e, 0x69632f66, 0x77322f63, 0x6f6d6d6f, 
+0x6e2f6d65, 0x6d2e632c, 0x7620312e, 0x312e322e, 
+0x35203139, 0x39382f30, 0x392f3330, 0x2031383a, 
+0x35303a30, 0x38207368, 0x75616e67, 0x20457870, 
+0x20240000, 0x24486561, 0x6465723a, 0x202f7072, 
+0x6f6a6563, 0x74732f72, 0x63732f73, 0x772f6765, 
+0x2f2e2f6e, 0x69632f66, 0x77322f63, 0x6f6d6d6f, 
+0x6e2f7365, 0x6e642e63, 0x2c762031, 0x2e312e32, 
+0x2e343420, 0x31393938, 0x2f31322f, 0x32312030, 
+0x303a3333, 0x3a313820, 0x73687561, 0x6e672045, 
+0x78702024, 0x0, 0x65767452, 0x6e674600, 
+0x51657674, 0x46000000, 0x51657674, 0x505f4600, 
+0x4d657674, 0x526e6746, 0x0, 0x4d516576, 
+0x74460000, 0x4d516576, 0x505f4600, 0x5173436f, 
+0x6e495f46, 0x0, 0x5173436f, 0x6e734600, 
+0x51725072, 0x6f644600, 0x69736e74, 0x54637055, 
+0x0, 0x24486561, 0x6465723a, 0x202f7072, 
+0x6f6a6563, 0x74732f72, 0x63732f73, 0x772f6765, 
+0x2f2e2f6e, 0x69632f66, 0x77322f63, 0x6f6d6d6f, 
+0x6e2f7265, 0x63762e63, 0x2c762031, 0x2e312e32, 
+0x2e353320, 0x31393939, 0x2f30312f, 0x31362030, 
+0x323a3535, 0x3a343320, 0x73687561, 0x6e672045, 
+0x78702024, 0x0, 0x65767452, 0x6e674600, 
+0x51657674, 0x46000000, 0x51657674, 0x505f4600, 
+0x4d657674, 0x526e6746, 0x0, 0x4d516576, 
+0x74460000, 0x4d516576, 0x505f4600, 0x5173436f, 
+0x6e495f46, 0x0, 0x5173436f, 0x6e734600, 
+0x51725072, 0x6f644600, 0x724d6163, 0x43686b30, 
+0x0, 0x72784672, 0x6d324c67, 0x0, 
+0x72784e6f, 0x53744264, 0x0, 0x72784e6f, 
+0x4d694264, 0x0, 0x72784e6f, 0x4a6d4264, 
+0x0, 0x7278436b, 0x446d6146, 0x0, 
+0x72785144, 0x6d457846, 0x0, 0x72785144, 
+0x6d614600, 0x72785144, 0x4c426446, 0x0, 
+0x72785144, 0x6d426446, 0x0, 0x72784372, 
+0x63506164, 0x0, 0x72536d51, 0x446d6146, 
+0x0, 0x24486561, 0x6465723a, 0x202f7072, 
+0x6f6a6563, 0x74732f72, 0x63732f73, 0x772f6765, 
+0x2f2e2f6e, 0x69632f66, 0x77322f63, 0x6f6d6d6f, 
+0x6e2f6d61, 0x632e632c, 0x7620312e, 0x312e322e, 
+0x32322031, 0x3939382f, 0x31322f30, 0x38203032, 
+0x3a33363a, 0x33302073, 0x6875616e, 0x67204578, 
+0x70202400, 0x65767452, 0x6e674600, 0x51657674, 
+0x46000000, 0x51657674, 0x505f4600, 0x4d657674, 
+0x526e6746, 0x0, 0x4d516576, 0x74460000, 
+0x4d516576, 0x505f4600, 0x5173436f, 0x6e495f46, 
+0x0, 0x5173436f, 0x6e734600, 0x51725072, 
+0x6f644600, 0x6d616354, 0x68726573, 0x0, 
+0x23744d61, 0x6341544e, 0x0, 0x23724d61, 
+0x6341544e, 0x0, 0x72656d41, 0x73737274, 
+0x0, 0x6c696e6b, 0x444f574e, 0x0, 
+0x6c696e6b, 0x55500000, 0x0, 0x0, 
+0x0, 0x24486561, 0x6465723a, 0x202f7072, 
+0x6f6a6563, 0x74732f72, 0x63732f73, 0x772f6765, 
+0x2f2e2f6e, 0x69632f66, 0x77322f63, 0x6f6d6d6f, 
+0x6e2f636b, 0x73756d2e, 0x632c7620, 0x312e312e, 
+0x322e3920, 0x31393939, 0x2f30312f, 0x31342030, 
+0x303a3033, 0x3a343820, 0x73687561, 0x6e672045, 
+0x78702024, 0x0, 0x65767452, 0x6e674600, 
+0x51657674, 0x46000000, 0x51657674, 0x505f4600, 
+0x4d657674, 0x526e6746, 0x0, 0x4d516576, 
+0x74460000, 0x4d516576, 0x505f4600, 0x5173436f, 
+0x6e495f46, 0x0, 0x5173436f, 0x6e734600, 
+0x51725072, 0x6f644600, 0x0, 0x0, 
+0x0, 0x50726f62, 0x65506879, 0x0, 
+0x6c6e6b41, 0x53535254, 0x0, 0x109a4, 
+0x10a1c, 0x10a50, 0x10a7c, 0x11050, 
+0x10aa8, 0x10b10, 0x111fc, 0x10dc0, 
+0x10c68, 0x10c80, 0x10cc4, 0x10cec, 
+0x10d0c, 0x10d34, 0x111fc, 0x10dc0, 
+0x10df8, 0x10e10, 0x10e40, 0x10e68, 
+0x10e88, 0x10eb0, 0x0, 0x10fdc, 
+0x11008, 0x1102c, 0x111fc, 0x11050, 
+0x11078, 0x11108, 0x0, 0x0, 
+0x0, 0x1186c, 0x1193c, 0x11a14, 
+0x11ae4, 0x11b40, 0x11c1c, 0x11c44, 
+0x11d20, 0x11d48, 0x11ef0, 0x11f18, 
+0x120c0, 0x122b8, 0x1254c, 0x12460, 
+0x1254c, 0x12578, 0x120e8, 0x12290, 
+0x7273745f, 0x676d6969, 0x0, 0x12608, 
+0x12640, 0x12728, 0x13374, 0x133b4, 
+0x133cc, 0x7365746c, 0x6f6f7000, 0x0, 
+0x0, 0x13bbc, 0x13bfc, 0x13c8c, 
+0x13cd0, 0x13d34, 0x13dc0, 0x13df4, 
+0x13e7c, 0x13f14, 0x13fe4, 0x14024, 
+0x140a8, 0x140cc, 0x141dc, 0x646f4261, 
+0x73655067, 0x0, 0x0, 0x0, 
+0x0, 0x73746d61, 0x634c4e4b, 0x0, 
+0x6765746d, 0x636c6e6b, 0x0, 0x14ed8, 
+0x14ed8, 0x14b8c, 0x14bd8, 0x14c24, 
+0x14ed8, 0x7365746d, 0x61636163, 0x74000000, 
+0x0, 0x0 };
+static int tigon2FwData[/*(MAX_DATA_LEN/4) + 1*/] = {
+0x1, 
+0x1, 0x1, 0xc001fc, 0x3ffc, 
+0xc00000, 0x416c7465, 0x6f6e2041, 0x63654e49, 
+0x43205600, 0x0, 0x0, 0x0, 
+0x0, 0x0, 0x0, 0x416c7465, 
+0x6f6e2041, 0x63654e49, 0x43205600, 0x42424242, 
+0x0, 0x0, 0x0, 0x1ffffc, 
+0x1fff7c, 0x0, 0x0, 0x0, 
+0x0, 0x0, 0x0, 0x60cf00, 
+0x60, 0xcf000000, 0x0, 0x0, 
+0x0, 0x0, 0x0, 0x0, 
+0x0, 0x0, 0x0, 0x0, 
+0x0, 0x0, 0x0, 0x0, 
+0x0, 0x0, 0x0, 0x0, 
+0x0, 0x0, 0x3, 0x0, 
+0x1, 0x0, 0x0, 0x0, 
+0x1, 0x0, 0x1, 0x0, 
+0x0, 0x0, 0x0, 0x1, 
+0x1, 0x0, 0x0, 0x0, 
+0x0, 0x0, 0x1000000, 0x21000000, 
+0x12000140, 0x0, 0x0, 0x20000000, 
+0x120000a0, 0x0, 0x12000060, 0x12000180, 
+0x120001e0, 0x0, 0x0, 0x0, 
+0x1, 0x0, 0x0, 0x0, 
+0x0, 0x0, 0x0, 0x2, 
+0x0, 0x0, 0x30001, 0x1, 
+0x30201, 0x0, 0x0, 0x1010101, 
+0x1010100, 0x10100, 0x1010001, 0x10001, 
+0x1000101, 0x101, 0x0, 0x0 };
--- /dev/null
+++ b/os/boot/pc/etherif.h
@@ -1,0 +1,47 @@
+typedef struct RingBuf {
+	uchar	owner;
+	uchar	unused;
+	ushort	len;
+	uchar	pkt[sizeof(Etherpkt)];
+} RingBuf;
+
+enum {
+	Host		= 0,		/* buffer owned by host */
+	Interface	= 1,		/* buffer owned by card */
+
+	Nrb		= 32,		/* default number of receive buffers */
+	Ntb		= 8,		/* default number of transmit buffers */
+};
+
+typedef struct Ether Ether;
+struct Ether {
+	ISAConf;			/* hardware info */
+	int	ctlrno;
+	int	state;			/* 0: unfound, 1: found, 2: attaching */
+	int	tbdf;
+
+	void	(*attach)(Ether*);	/* filled in by reset routine */
+	void	(*transmit)(Ether*);
+	void	(*interrupt)(Ureg*, void*);
+	void	(*detach)(Ether*);
+	void	*ctlr;
+
+	ushort	nrb;			/* number of software receive buffers */
+	ushort	ntb;			/* number of software transmit buffers */
+	RingBuf *rb;			/* software receive buffers */
+	RingBuf *tb;			/* software transmit buffers */
+
+	ushort	rh;			/* first receive buffer belonging to host */
+	ushort	ri;			/* first receive buffer belonging to card */	
+
+	ushort	th;			/* first transmit buffer belonging to host */	
+	ushort	ti;			/* first transmit buffer belonging to card */
+	int	tbusy;			/* transmitter is busy */
+	int	mbps;			/* zero means link down */	
+};
+
+extern void etherrloop(Ether*, Etherpkt*, long);
+extern void addethercard(char*, int(*)(Ether*));
+
+#define NEXT(x, l)	(((x)+1)%(l))
+#define PREV(x, l)	(((x) == 0) ? (l)-1: (x)-1)
--- /dev/null
+++ b/os/boot/pc/etherigbe.c
@@ -1,0 +1,1741 @@
+/*
+ * bootstrap driver for
+ * Intel RS-82543GC Gigabit Ethernet PCI Controllers
+ * as found on the Intel PRO/1000[FT] Server Adapter.
+ * The older non-[FT] cards use the 82542 (LSI L2A1157) chip; no attempt
+ * is made to handle the older chip although it should be possible.
+ *
+ * updated just enough to cope with the
+ * Intel 8254[0347]NN Gigabit Ethernet Controller
+ * as found on the Intel PRO/1000 series of adapters:
+ *	82540EM Intel PRO/1000 MT
+ *	82543GC	Intel PRO/1000 T
+ *	82544EI Intel PRO/1000 XT
+ *	82547EI built-in
+ *
+ * The datasheet is not very clear about running on a big-endian system
+ * and this driver assumes little-endian throughout.
+ * To do:
+ *	GMII/MII
+ *	check recovery from receive no buffers condition
+ *	automatic ett adjustment
+ */
+#include "u.h"
+#include "lib.h"
+#include "mem.h"
+#include "dat.h"
+#include "fns.h"
+#include "io.h"
+
+#include "etherif.h"
+#include "ethermii.h"
+
+enum {
+	Debug = 0,		/* mostly for X60 debugging */
+};
+enum {
+	i82542     = (0x1000<<16)|0x8086,
+	i82543gc   = (0x1004<<16)|0x8086,
+	i82544ei   = (0x1008<<16)|0x8086,
+	i82540em   = (0x100E<<16)|0x8086,
+	i82546eb   = (0x1010<<16)|0x8086,
+	i82547ei   = (0x1019<<16)|0x8086,
+	i82540eplp = (0x101E<<16)|0x8086,
+	i82547gi   = (0x1075<<16)|0x8086,
+	i82541gi   = (0x1076<<16)|0x8086,
+	i82541gi2  = (0x1077<<16)|0x8086,
+	i82546gb   = (0x1079<<16)|0x8086,
+	i82541pi   = (0x107c<<16)|0x8086,
+};
+
+/* compatibility with cpu kernels */
+#define iallocb allocb
+#ifndef CACHELINESZ
+#define CACHELINESZ	32		/* pentium & later */
+#endif
+
+/* from pci.c */
+enum
+{					/* command register (pcidev->pcr) */
+	IOen		= (1<<0),
+	MEMen		= (1<<1),
+	MASen		= (1<<2),
+	MemWrInv	= (1<<4),
+	PErrEn		= (1<<6),
+	SErrEn		= (1<<8),
+};
+enum {
+	Ctrl		= 0x00000000,	/* Device Control */
+	Status		= 0x00000008,	/* Device Status */
+	Eecd		= 0x00000010,	/* EEPROM/Flash Control/Data */
+	Ctrlext		= 0x00000018,	/* Extended Device Control */
+	Mdic		= 0x00000020,	/* MDI Control */
+	Fcal		= 0x00000028,	/* Flow Control Address Low */
+	Fcah		= 0x0000002C,	/* Flow Control Address High */
+	Fct		= 0x00000030,	/* Flow Control Type */
+	Icr		= 0x000000C0,	/* Interrupt Cause Read */
+	Ics		= 0x000000C8,	/* Interrupt Cause Set */
+	Ims		= 0x000000D0,	/* Interrupt Mask Set/Read */
+	Imc		= 0x000000D8,	/* Interrupt mask Clear */
+	Rctl		= 0x00000100,	/* Receive Control */
+	Fcttv		= 0x00000170,	/* Flow Control Transmit Timer Value */
+	Txcw		= 0x00000178,	/* Transmit Configuration Word */
+	Tctl		= 0x00000400,	/* Transmit Control */
+	Tipg		= 0x00000410,	/* Transmit IPG */
+	Tbt		= 0x00000448,	/* Transmit Burst Timer */
+	Ait		= 0x00000458,	/* Adaptive IFS Throttle */
+	Fcrtl		= 0x00002160,	/* Flow Control RX Threshold Low */
+	Fcrth		= 0x00002168,	/* Flow Control Rx Threshold High */
+	Rdbal		= 0x00002800,	/* Rdesc Base Address Low */
+	Rdbah		= 0x00002804,	/* Rdesc Base Address High */
+	Rdlen		= 0x00002808,	/* Receive Descriptor Length */
+	Rdh		= 0x00002810,	/* Receive Descriptor Head */
+	Rdt		= 0x00002818,	/* Receive Descriptor Tail */
+	Rdtr		= 0x00002820,	/* Receive Descriptor Timer Ring */
+	Rxdctl		= 0x00002828,	/* Receive Descriptor Control */
+	Radv		= 0x0000282C,	/* Receive Interrupt Absolute Delay Timer */
+	Txdmac		= 0x00003000,	/* Transfer DMA Control */
+	Ett		= 0x00003008,	/* Early Transmit Control */
+	Tdbal		= 0x00003800,	/* Tdesc Base Address Low */
+	Tdbah		= 0x00003804,	/* Tdesc Base Address High */
+	Tdlen		= 0x00003808,	/* Transmit Descriptor Length */
+	Tdh		= 0x00003810,	/* Transmit Descriptor Head */
+	Tdt		= 0x00003818,	/* Transmit Descriptor Tail */
+	Tidv		= 0x00003820,	/* Transmit Interrupt Delay Value */
+	Txdctl		= 0x00003828,	/* Transmit Descriptor Control */
+	Tadv		= 0x0000382C,	/* Transmit Interrupt Absolute Delay Timer */
+
+	Statistics	= 0x00004000,	/* Start of Statistics Area */
+	Gorcl		= 0x88/4,	/* Good Octets Received Count */
+	Gotcl		= 0x90/4,	/* Good Octets Transmitted Count */
+	Torl		= 0xC0/4,	/* Total Octets Received */
+	Totl		= 0xC8/4,	/* Total Octets Transmitted */
+	Nstatistics	= 64,
+
+	Rxcsum		= 0x00005000,	/* Receive Checksum Control */
+	Mta		= 0x00005200,	/* Multicast Table Array */
+	Ral		= 0x00005400,	/* Receive Address Low */
+	Rah		= 0x00005404,	/* Receive Address High */
+	Manc		= 0x00005820,	/* Management Control */
+};
+
+enum {					/* Ctrl */
+	Bem		= 0x00000002,	/* Big Endian Mode */
+	Prior		= 0x00000004,	/* Priority on the PCI bus */
+	Lrst		= 0x00000008,	/* Link Reset */
+	Asde		= 0x00000020,	/* Auto-Speed Detection Enable */
+	Slu		= 0x00000040,	/* Set Link Up */
+	Ilos		= 0x00000080,	/* Invert Loss of Signal (LOS) */
+	SspeedMASK	= 0x00000300,	/* Speed Selection */
+	SspeedSHIFT	= 8,
+	Sspeed10	= 0x00000000,	/* 10Mb/s */
+	Sspeed100	= 0x00000100,	/* 100Mb/s */
+	Sspeed1000	= 0x00000200,	/* 1000Mb/s */
+	Frcspd		= 0x00000800,	/* Force Speed */
+	Frcdplx		= 0x00001000,	/* Force Duplex */
+	SwdpinsloMASK	= 0x003C0000,	/* Software Defined Pins - lo nibble */
+	SwdpinsloSHIFT	= 18,
+	SwdpioloMASK	= 0x03C00000,	/* Software Defined Pins - I or O */
+	SwdpioloSHIFT	= 22,
+	Devrst		= 0x04000000,	/* Device Reset */
+	Rfce		= 0x08000000,	/* Receive Flow Control Enable */
+	Tfce		= 0x10000000,	/* Transmit Flow Control Enable */
+	Vme		= 0x40000000,	/* VLAN Mode Enable */
+};
+
+/*
+ * can't find Tckok nor Rbcok in any Intel docs,
+ * but even 82543gc docs define Lanid.
+ */
+enum {					/* Status */
+	Lu		= 0x00000002,	/* Link Up */
+	Lanid		= 0x0000000C,	/* mask for Lan ID. (function id) */
+//	Tckok		= 0x00000004,	/* Transmit clock is running */
+//	Rbcok		= 0x00000008,	/* Receive clock is running */
+	Txoff		= 0x00000010,	/* Transmission Paused */
+	Tbimode		= 0x00000020,	/* TBI Mode Indication */
+	SpeedMASK	= 0x000000C0,
+	Speed10		= 0x00000000,	/* 10Mb/s */
+	Speed100	= 0x00000040,	/* 100Mb/s */
+	Speed1000	= 0x00000080,	/* 1000Mb/s */
+	Mtxckok		= 0x00000400,	/* MTX clock is running */
+	Pci66		= 0x00000800,	/* PCI Bus speed indication */
+	Bus64		= 0x00001000,	/* PCI Bus width indication */
+};
+
+enum {					/* Ctrl and Status */
+	Fd		= 0x00000001,	/* Full-Duplex */
+	AsdvMASK	= 0x00000300,
+	Asdv10		= 0x00000000,	/* 10Mb/s */
+	Asdv100		= 0x00000100,	/* 100Mb/s */
+	Asdv1000	= 0x00000200,	/* 1000Mb/s */
+};
+
+enum {					/* Eecd */
+	Sk		= 0x00000001,	/* Clock input to the EEPROM */
+	Cs		= 0x00000002,	/* Chip Select */
+	Di		= 0x00000004,	/* Data Input to the EEPROM */
+	Do		= 0x00000008,	/* Data Output from the EEPROM */
+	Areq		= 0x00000040,	/* EEPROM Access Request */
+	Agnt		= 0x00000080,	/* EEPROM Access Grant */
+	Eepresent	= 0x00000100,	/* EEPROM Present */
+	Eesz256		= 0x00000200,	/* EEPROM is 256 words not 64 */
+	Eeszaddr	= 0x00000400,	/* EEPROM size for 8254[17] */
+	Spi		= 0x00002000,	/* EEPROM is SPI not Microwire */
+};
+
+enum {					/* Ctrlext */
+	Gpien		= 0x0000000F,	/* General Purpose Interrupt Enables */
+	SwdpinshiMASK	= 0x000000F0,	/* Software Defined Pins - hi nibble */
+	SwdpinshiSHIFT	= 4,
+	SwdpiohiMASK	= 0x00000F00,	/* Software Defined Pins - I or O */
+	SwdpiohiSHIFT	= 8,
+	Asdchk		= 0x00001000,	/* ASD Check */
+	Eerst		= 0x00002000,	/* EEPROM Reset */
+	Ips		= 0x00004000,	/* Invert Power State */
+	Spdbyps		= 0x00008000,	/* Speed Select Bypass */
+};
+
+enum {					/* EEPROM content offsets */
+	Ea		= 0x00,		/* Ethernet Address */
+	Cf		= 0x03,		/* Compatibility Field */
+	Pba		= 0x08,		/* Printed Board Assembly number */
+	Icw1		= 0x0A,		/* Initialization Control Word 1 */
+	Sid		= 0x0B,		/* Subsystem ID */
+	Svid		= 0x0C,		/* Subsystem Vendor ID */
+	Did		= 0x0D,		/* Device ID */
+	Vid		= 0x0E,		/* Vendor ID */
+	Icw2		= 0x0F,		/* Initialization Control Word 2 */
+};
+
+enum {					/* Mdic */
+	MDIdMASK	= 0x0000FFFF,	/* Data */
+	MDIdSHIFT	= 0,
+	MDIrMASK	= 0x001F0000,	/* PHY Register Address */
+	MDIrSHIFT	= 16,
+	MDIpMASK	= 0x03E00000,	/* PHY Address */
+	MDIpSHIFT	= 21,
+	MDIwop		= 0x04000000,	/* Write Operation */
+	MDIrop		= 0x08000000,	/* Read Operation */
+	MDIready	= 0x10000000,	/* End of Transaction */
+	MDIie		= 0x20000000,	/* Interrupt Enable */
+	MDIe		= 0x40000000,	/* Error */
+};
+
+enum {					/* Icr, Ics, Ims, Imc */
+	Txdw		= 0x00000001,	/* Transmit Descriptor Written Back */
+	Txqe		= 0x00000002,	/* Transmit Queue Empty */
+	Lsc		= 0x00000004,	/* Link Status Change */
+	Rxseq		= 0x00000008,	/* Receive Sequence Error */
+	Rxdmt0		= 0x00000010,	/* Rdesc Minimum Threshold Reached */
+	Rxo		= 0x00000040,	/* Receiver Overrun */
+	Rxt0		= 0x00000080,	/* Receiver Timer Interrupt */
+	Mdac		= 0x00000200,	/* MDIO Access Completed */
+	Rxcfg		= 0x00000400,	/* Receiving /C/ ordered sets */
+	Gpi0		= 0x00000800,	/* General Purpose Interrupts */
+	Gpi1		= 0x00001000,
+	Gpi2		= 0x00002000,
+	Gpi3		= 0x00004000,
+};
+
+/*
+ * The Mdic register isn't implemented on the 82543GC,
+ * the software defined pins are used instead.
+ * These definitions work for the Intel PRO/1000 T Server Adapter.
+ * The direction pin bits are read from the EEPROM.
+ */
+enum {
+	Mdd		= ((1<<2)<<SwdpinsloSHIFT),	/* data */
+	Mddo		= ((1<<2)<<SwdpioloSHIFT),	/* pin direction */
+	Mdc		= ((1<<3)<<SwdpinsloSHIFT),	/* clock */
+	Mdco		= ((1<<3)<<SwdpioloSHIFT),	/* pin direction */
+	Mdr		= ((1<<0)<<SwdpinshiSHIFT),	/* reset */
+	Mdro		= ((1<<0)<<SwdpiohiSHIFT),	/* pin direction */
+};
+
+enum {					/* Txcw */
+	TxcwFd		= 0x00000020,	/* Full Duplex */
+	TxcwHd		= 0x00000040,	/* Half Duplex */
+	TxcwPauseMASK	= 0x00000180,	/* Pause */
+	TxcwPauseSHIFT	= 7,
+	TxcwPs		= (1<<TxcwPauseSHIFT),	/* Pause Supported */
+	TxcwAs		= (2<<TxcwPauseSHIFT),	/* Asymmetric FC desired */
+	TxcwRfiMASK	= 0x00003000,	/* Remote Fault Indication */
+	TxcwRfiSHIFT	= 12,
+	TxcwNpr		= 0x00008000,	/* Next Page Request */
+	TxcwConfig	= 0x40000000,	/* Transmit COnfig Control */
+	TxcwAne		= 0x80000000,	/* Auto-Negotiation Enable */
+};
+
+enum {					/* Rctl */
+	Rrst		= 0x00000001,	/* Receiver Software Reset */
+	Ren		= 0x00000002,	/* Receiver Enable */
+	Sbp		= 0x00000004,	/* Store Bad Packets */
+	Upe		= 0x00000008,	/* Unicast Promiscuous Enable */
+	Mpe		= 0x00000010,	/* Multicast Promiscuous Enable */
+	Lpe		= 0x00000020,	/* Long Packet Reception Enable */
+	LbmMASK		= 0x000000C0,	/* Loopback Mode */
+	LbmOFF		= 0x00000000,	/* No Loopback */
+	LbmTBI		= 0x00000040,	/* TBI Loopback */
+	LbmMII		= 0x00000080,	/* GMII/MII Loopback */
+	LbmXCVR		= 0x000000C0,	/* Transceiver Loopback */
+	RdtmsMASK	= 0x00000300,	/* Rdesc Minimum Threshold Size */
+	RdtmsHALF	= 0x00000000,	/* Threshold is 1/2 Rdlen */
+	RdtmsQUARTER	= 0x00000100,	/* Threshold is 1/4 Rdlen */
+	RdtmsEIGHTH	= 0x00000200,	/* Threshold is 1/8 Rdlen */
+	MoMASK		= 0x00003000,	/* Multicast Offset */
+	Bam		= 0x00008000,	/* Broadcast Accept Mode */
+	BsizeMASK	= 0x00030000,	/* Receive Buffer Size */
+	Bsize2048	= 0x00000000,
+	Bsize1024	= 0x00010000,
+	Bsize512	= 0x00020000,
+	Bsize256	= 0x00030000,
+	Vfe		= 0x00040000,	/* VLAN Filter Enable */
+	Cfien		= 0x00080000,	/* Canonical Form Indicator Enable */
+	Cfi		= 0x00100000,	/* Canonical Form Indicator value */
+	Dpf		= 0x00400000,	/* Discard Pause Frames */
+	Pmcf		= 0x00800000,	/* Pass MAC Control Frames */
+	Bsex		= 0x02000000,	/* Buffer Size Extension */
+	Secrc		= 0x04000000,	/* Strip CRC from incoming packet */
+};
+
+enum {					/* Tctl */
+	Trst		= 0x00000001,	/* Transmitter Software Reset */
+	Ten		= 0x00000002,	/* Transmit Enable */
+	Psp		= 0x00000008,	/* Pad Short Packets */
+	CtMASK		= 0x00000FF0,	/* Collision Threshold */
+	CtSHIFT		= 4,
+	ColdMASK	= 0x003FF000,	/* Collision Distance */
+	ColdSHIFT	= 12,
+	Swxoff		= 0x00400000,	/* Sofware XOFF Transmission */
+	Pbe		= 0x00800000,	/* Packet Burst Enable */
+	Rtlc		= 0x01000000,	/* Re-transmit on Late Collision */
+	Nrtu		= 0x02000000,	/* No Re-transmit on Underrrun */
+};
+
+enum {					/* [RT]xdctl */
+	PthreshMASK	= 0x0000003F,	/* Prefetch Threshold */
+	PthreshSHIFT	= 0,
+	HthreshMASK	= 0x00003F00,	/* Host Threshold */
+	HthreshSHIFT	= 8,
+	WthreshMASK	= 0x003F0000,	/* Writebacj Threshold */
+	WthreshSHIFT	= 16,
+	Gran		= 0x01000000,	/* Granularity */
+};
+
+enum {					/* Rxcsum */
+	PcssMASK	= 0x000000FF,	/* Packet Checksum Start */
+	PcssSHIFT	= 0,
+	Ipofl		= 0x00000100,	/* IP Checksum Off-load Enable */
+	Tuofl		= 0x00000200,	/* TCP/UDP Checksum Off-load Enable */
+};
+
+enum {					/* Manc */
+	Arpen		= 0x00002000,	/* Enable ARP Request Filtering */
+};
+
+typedef struct Rdesc {			/* Receive Descriptor */
+	uint	addr[2];
+	ushort	length;
+	ushort	checksum;
+	uchar	status;
+	uchar	errors;
+	ushort	special;
+} Rdesc;
+
+enum {					/* Rdesc status */
+	Rdd		= 0x01,		/* Descriptor Done */
+	Reop		= 0x02,		/* End of Packet */
+	Ixsm		= 0x04,		/* Ignore Checksum Indication */
+	Vp		= 0x08,		/* Packet is 802.1Q (matched VET) */
+	Tcpcs		= 0x20,		/* TCP Checksum Calculated on Packet */
+	Ipcs		= 0x40,		/* IP Checksum Calculated on Packet */
+	Pif		= 0x80,		/* Passed in-exact filter */
+};
+
+enum {					/* Rdesc errors */
+	Ce		= 0x01,		/* CRC Error or Alignment Error */
+	Se		= 0x02,		/* Symbol Error */
+	Seq		= 0x04,		/* Sequence Error */
+	Cxe		= 0x10,		/* Carrier Extension Error */
+	Tcpe		= 0x20,		/* TCP/UDP Checksum Error */
+	Ipe		= 0x40,		/* IP Checksum Error */
+	Rxe		= 0x80,		/* RX Data Error */
+};
+
+typedef struct Tdesc {			/* Legacy+Normal Transmit Descriptor */
+	uint	addr[2];
+	uint	control;		/* varies with descriptor type */
+	uint	status;			/* varies with descriptor type */
+} Tdesc;
+
+enum {					/* Tdesc control */
+	LenMASK		= 0x000FFFFF,	/* Data/Packet Length Field */
+	LenSHIFT	= 0,
+	DtypeCD		= 0x00000000,	/* Data Type 'Context Descriptor' */
+	DtypeDD		= 0x00100000,	/* Data Type 'Data Descriptor' */
+	PtypeTCP	= 0x01000000,	/* TCP/UDP Packet Type (CD) */
+	Teop		= 0x01000000,	/* End of Packet (DD) */
+	PtypeIP		= 0x02000000,	/* IP Packet Type (CD) */
+	Ifcs		= 0x02000000,	/* Insert FCS (DD) */
+	Tse		= 0x04000000,	/* TCP Segmentation Enable */
+	Rs		= 0x08000000,	/* Report Status */
+	Rps		= 0x10000000,	/* Report Status Sent */
+	Dext		= 0x20000000,	/* Descriptor Extension */
+	Vle		= 0x40000000,	/* VLAN Packet Enable */
+	Ide		= 0x80000000,	/* Interrupt Delay Enable */
+};
+
+enum {					/* Tdesc status */
+	Tdd		= 0x00000001,	/* Descriptor Done */
+	Ec		= 0x00000002,	/* Excess Collisions */
+	Lc		= 0x00000004,	/* Late Collision */
+	Tu		= 0x00000008,	/* Transmit Underrun */
+	CssMASK		= 0x0000FF00,	/* Checksum Start Field */
+	CssSHIFT	= 8,
+};
+
+enum {
+	Nrdesc		= 32,		/* multiple of 8 */
+	Ntdesc		= 8,		/* multiple of 8 */
+};
+
+typedef struct Ctlr Ctlr;
+typedef struct Ctlr {
+	int	port;
+	Pcidev*	pcidev;
+	Ctlr*	next;
+	int	active;
+	int	id;
+	int	cls;
+	ushort	eeprom[0x40];
+
+	int*	nic;
+	Lock	imlock;
+	int	im;			/* interrupt mask */
+
+	Mii*	mii;
+
+	Lock	slock;
+	uint	statistics[Nstatistics];
+
+	uchar	ra[Eaddrlen];		/* receive address */
+	ulong	mta[128];		/* multicast table array */
+
+	Rdesc*	rdba;			/* receive descriptor base address */
+	Block**	rb;			/* receive buffers */
+	int	rdh;			/* receive descriptor head */
+	int	rdt;			/* receive descriptor tail */
+
+	Tdesc*	tdba;			/* transmit descriptor base address */
+	Lock	tdlock;
+	Block**	tb;			/* transmit buffers */
+	int	tdh;			/* transmit descriptor head */
+	int	tdt;			/* transmit descriptor tail */
+	int	ett;			/* early transmit threshold */
+
+	int	txcw;
+	int	fcrtl;
+	int	fcrth;
+
+	/* bootstrap goo */
+	Block*	bqhead;	/* transmission queue */
+	Block*	bqtail;
+} Ctlr;
+
+static Ctlr* ctlrhead;
+static Ctlr* ctlrtail;
+
+#define csr32r(c, r)	(*((c)->nic+((r)/4)))
+#define csr32w(c, r, v)	(*((c)->nic+((r)/4)) = (v))
+
+static void
+igbeim(Ctlr* ctlr, int im)
+{
+	ilock(&ctlr->imlock);
+	ctlr->im |= im;
+	csr32w(ctlr, Ims, ctlr->im);
+	iunlock(&ctlr->imlock);
+}
+
+static void
+igbeattach(Ether* edev)
+{
+	int ctl;
+	Ctlr *ctlr;
+
+	/*
+	 * To do here:
+	 *	one-time stuff;
+	 *	start off a kproc for link status change:
+	 *		adjust queue length depending on speed;
+	 *		flow control.
+	 *	more needed here...
+	 */
+	ctlr = edev->ctlr;
+	igbeim(ctlr, 0);
+	ctl = csr32r(ctlr, Rctl)|Ren;
+	csr32w(ctlr, Rctl, ctl);
+	ctl = csr32r(ctlr, Tctl)|Ten;
+	csr32w(ctlr, Tctl, ctl);
+}
+
+static char* statistics[Nstatistics] = {
+	"CRC Error",
+	"Alignment Error",
+	"Symbol Error",
+	"RX Error",
+	"Missed Packets",
+	"Single Collision",
+	"Excessive Collisions",
+	"Multiple Collision",
+	"Late Collisions",
+	nil,
+	"Collision",
+	"Transmit Underrun",
+	"Defer",
+	"Transmit - No CRS",
+	"Sequence Error",
+	"Carrier Extension Error",
+	"Receive Error Length",
+	nil,
+	"XON Received",
+	"XON Transmitted",
+	"XOFF Received",
+	"XOFF Transmitted",
+	"FC Received Unsupported",
+	"Packets Received (64 Bytes)",
+	"Packets Received (65-127 Bytes)",
+	"Packets Received (128-255 Bytes)",
+	"Packets Received (256-511 Bytes)",
+	"Packets Received (512-1023 Bytes)",
+	"Packets Received (1024-1522 Bytes)",
+	"Good Packets Received",
+	"Broadcast Packets Received",
+	"Multicast Packets Received",
+	"Good Packets Transmitted",
+	nil,
+	"Good Octets Received",
+	nil,
+	"Good Octets Transmitted",
+	nil,
+	nil,
+	nil,
+	"Receive No Buffers",
+	"Receive Undersize",
+	"Receive Fragment",
+	"Receive Oversize",
+	"Receive Jabber",
+	nil,
+	nil,
+	nil,
+	"Total Octets Received",
+	nil,
+	"Total Octets Transmitted",
+	nil,
+	"Total Packets Received",
+	"Total Packets Transmitted",
+	"Packets Transmitted (64 Bytes)",
+	"Packets Transmitted (65-127 Bytes)",
+	"Packets Transmitted (128-255 Bytes)",
+	"Packets Transmitted (256-511 Bytes)",
+	"Packets Transmitted (512-1023 Bytes)",
+	"Packets Transmitted (1024-1522 Bytes)",
+	"Multicast Packets Transmitted",
+	"Broadcast Packets Transmitted",
+	"TCP Segmentation Context Transmitted",
+	"TCP Segmentation Context Fail",
+};
+
+static void
+txstart(Ether *edev)
+{
+	int tdh, tdt, len, olen;
+	Ctlr *ctlr = edev->ctlr;
+	Block *bp;
+	Tdesc *tdesc;
+
+	/*
+	 * Try to fill the ring back up, moving buffers from the transmit q.
+	 */
+	tdh = PREV(ctlr->tdh, Ntdesc);
+	for(tdt = ctlr->tdt; tdt != tdh; tdt = NEXT(tdt, Ntdesc)){
+		/* pull off the head of the transmission queue */
+		if((bp = ctlr->bqhead) == nil)		/* was qget(edev->oq) */
+			break;
+		ctlr->bqhead = bp->next;
+		if (ctlr->bqtail == bp)
+			ctlr->bqtail = nil;
+		len = olen = BLEN(bp);
+
+		/*
+		 * if packet is too short, make it longer rather than relying
+		 * on ethernet interface to pad it and complain so the caller
+		 * will get fixed.  I don't think Psp is working right, or it's
+		 * getting cleared.
+		 */
+		if (len < ETHERMINTU) {
+			if (bp->rp + ETHERMINTU <= bp->lim)
+				bp->wp = bp->rp + ETHERMINTU;
+			else
+				bp->wp = bp->lim;
+			len = BLEN(bp);
+			print("txstart: extended short pkt %d -> %d bytes\n",
+				olen, len);
+		}
+
+		/* set up a descriptor for it */
+		tdesc = &ctlr->tdba[tdt];
+		tdesc->addr[0] = PCIWADDR(bp->rp);
+		tdesc->addr[1] = 0;
+		tdesc->control = /* Ide| */ Rs|Dext|Ifcs|Teop|DtypeDD|len;
+		tdesc->status = 0;
+
+		ctlr->tb[tdt] = bp;
+	}
+	ctlr->tdt = tdt;
+	csr32w(ctlr, Tdt, tdt);
+	igbeim(ctlr, Txdw);
+}
+
+static Block *
+fromringbuf(Ether *ether)
+{
+	RingBuf *tb = &ether->tb[ether->ti];
+	Block *bp = allocb(tb->len);
+
+	memmove(bp->wp, tb->pkt, tb->len);
+	memmove(bp->wp+Eaddrlen, ether->ea, Eaddrlen);
+	bp->wp += tb->len;
+	return bp;
+}
+
+static void
+igbetransmit(Ether* edev)
+{
+	Block *bp;
+	Ctlr *ctlr;
+	Tdesc *tdesc;
+	RingBuf *tb;
+	int tdh;
+
+	/*
+	 * For now there are no smarts here. Tuning comes later.
+	 */
+	ctlr = edev->ctlr;
+	ilock(&ctlr->tdlock);
+
+	/*
+	 * Free any completed packets
+	 * - try to get the soft tdh to catch the tdt;
+	 * - if the packet had an underrun bump the threshold
+	 *   - the Tu bit doesn't seem to ever be set, perhaps
+	 *     because Rs mode is used?
+	 */
+	tdh = ctlr->tdh;
+	for(;;){
+		tdesc = &ctlr->tdba[tdh];
+		if(!(tdesc->status & Tdd))
+			break;
+		if(tdesc->status & Tu){
+			ctlr->ett++;
+			csr32w(ctlr, Ett, ctlr->ett);
+		}
+		tdesc->status = 0;
+		if(ctlr->tb[tdh] != nil){
+			freeb(ctlr->tb[tdh]);
+			ctlr->tb[tdh] = nil;
+		}
+		tdh = NEXT(tdh, Ntdesc);
+	}
+	ctlr->tdh = tdh;
+
+	/* copy packets from the software RingBuf to the transmission q */
+	/* from boot ether83815.c */
+	while((tb = &edev->tb[edev->ti])->owner == Interface){
+		bp = fromringbuf(edev);
+
+		/* put the buffer on the transmit queue */
+		if(ctlr->bqhead)
+			ctlr->bqtail->next = bp;
+		else
+			ctlr->bqhead = bp;
+		ctlr->bqtail = bp;
+
+		txstart(edev);		/* kick transmitter */
+		tb->owner = Host;	/* give descriptor back */
+
+		edev->ti = NEXT(edev->ti, edev->ntb);
+	}
+
+	iunlock(&ctlr->tdlock);
+}
+
+static void
+igbereplenish(Ctlr* ctlr)
+{
+	int rdt;
+	Block *bp;
+	Rdesc *rdesc;
+
+	rdt = ctlr->rdt;
+	while(NEXT(rdt, Nrdesc) != ctlr->rdh){
+		rdesc = &ctlr->rdba[rdt];
+		if(ctlr->rb[rdt] != nil){
+			/* nothing to do */
+		}
+		else if((bp = iallocb(2048)) != nil){
+			ctlr->rb[rdt] = bp;
+			rdesc->addr[0] = PCIWADDR(bp->rp);
+			rdesc->addr[1] = 0;
+		}
+		else
+			break;
+		rdesc->status = 0;
+
+		rdt = NEXT(rdt, Nrdesc);
+	}
+	ctlr->rdt = rdt;
+	csr32w(ctlr, Rdt, rdt);
+}
+
+static void
+toringbuf(Ether *ether, Block *bp)
+{
+	RingBuf *rb = &ether->rb[ether->ri];
+
+	if (rb->owner == Interface) {
+		rb->len = BLEN(bp);
+		memmove(rb->pkt, bp->rp, rb->len);
+		rb->owner = Host;
+		ether->ri = NEXT(ether->ri, ether->nrb);
+	}
+	/* else no one is expecting packets from the network */
+}
+
+static void
+igbeinterrupt(Ureg*, void* arg)
+{
+	Block *bp;
+	Ctlr *ctlr;
+	Ether *edev;
+	Rdesc *rdesc;
+	int icr, im, rdh, txdw = 0;
+
+	edev = arg;
+	ctlr = edev->ctlr;
+
+	ilock(&ctlr->imlock);
+	csr32w(ctlr, Imc, ~0);
+	im = ctlr->im;
+
+	for(icr = csr32r(ctlr, Icr); icr & ctlr->im; icr = csr32r(ctlr, Icr)){
+		/*
+		 * Link status changed.
+		 */
+		if(icr & (Rxseq|Lsc)){
+			/*
+			 * should be more here...
+			 */
+		}
+
+		/*
+		 * Process any received packets.
+		 */
+		rdh = ctlr->rdh;
+		for(;;){
+			rdesc = &ctlr->rdba[rdh];
+			if(!(rdesc->status & Rdd))
+				break;
+			if ((rdesc->status & Reop) && rdesc->errors == 0) {
+				bp = ctlr->rb[rdh];
+				ctlr->rb[rdh] = nil;
+				/*
+				 * it appears that the original 82543 needed
+				 * to have the Ethernet CRC excluded, but that
+				 * the newer chips do not?
+				 */
+				bp->wp += rdesc->length /* -4 */;
+				toringbuf(edev, bp);
+				freeb(bp);
+			} else if ((rdesc->status & Reop) && rdesc->errors)
+				print("igbe: input packet error 0x%ux\n",
+					rdesc->errors);
+			rdesc->status = 0;
+			rdh = NEXT(rdh, Nrdesc);
+		}
+		ctlr->rdh = rdh;
+
+		if(icr & Rxdmt0)
+			igbereplenish(ctlr);
+		if(icr & Txdw){
+			im &= ~Txdw;
+			txdw++;
+		}
+	}
+
+	ctlr->im = im;
+	csr32w(ctlr, Ims, im);
+	iunlock(&ctlr->imlock);
+
+	if(txdw)
+		igbetransmit(edev);
+}
+
+static int
+igbeinit(Ether* edev)
+{
+	int csr, i, r, ctrl;
+	MiiPhy *phy;
+	Ctlr *ctlr;
+
+	ctlr = edev->ctlr;
+
+	/*
+	 * Set up the receive addresses.
+	 * There are 16 addresses. The first should be the MAC address.
+	 * The others are cleared and not marked valid (MS bit of Rah).
+	 */
+	csr = (edev->ea[3]<<24)|(edev->ea[2]<<16)|(edev->ea[1]<<8)|edev->ea[0];
+	csr32w(ctlr, Ral, csr);
+	csr = 0x80000000|(edev->ea[5]<<8)|edev->ea[4];
+	csr32w(ctlr, Rah, csr);
+	for(i = 1; i < 16; i++){
+		csr32w(ctlr, Ral+i*8, 0);
+		csr32w(ctlr, Rah+i*8, 0);
+	}
+
+	/*
+	 * Clear the Multicast Table Array.
+	 * It's a 4096 bit vector accessed as 128 32-bit registers.
+	 */
+	for(i = 0; i < 128; i++)
+		csr32w(ctlr, Mta+i*4, 0);
+
+	/*
+	 * Receive initialisation.
+	 * Mostly defaults from the datasheet, will
+	 * need some tuning for performance:
+	 *	Rctl	descriptor mimimum threshold size
+	 *		discard pause frames
+	 *		strip CRC
+	 * 	Rdtr	interrupt delay
+	 * 	Rxdctl	all the thresholds
+	 */
+	csr32w(ctlr, Rctl, 0);
+
+	/*
+	 * Allocate the descriptor ring and load its
+	 * address and length into the NIC.
+	 */
+	ctlr->rdba = xspanalloc(Nrdesc*sizeof(Rdesc), 128 /* was 16 */, 0);
+	csr32w(ctlr, Rdbal, PCIWADDR(ctlr->rdba));
+	csr32w(ctlr, Rdbah, 0);
+	csr32w(ctlr, Rdlen, Nrdesc*sizeof(Rdesc));
+
+	/*
+	 * Initialise the ring head and tail pointers and
+	 * populate the ring with Blocks.
+	 * The datasheet says the tail pointer is set to beyond the last
+	 * descriptor hardware can process, which implies the initial
+	 * condition is Rdh == Rdt. However, experience shows Rdt must
+	 * always be 'behind' Rdh; the replenish routine ensures this.
+	 */
+	ctlr->rdh = 0;
+	csr32w(ctlr, Rdh, ctlr->rdh);
+	ctlr->rdt = 0;
+	csr32w(ctlr, Rdt, ctlr->rdt);
+	ctlr->rb = malloc(sizeof(Block*)*Nrdesc);
+	igbereplenish(ctlr);
+
+	/*
+	 * Set up Rctl but don't enable receiver (yet).
+	 */
+	csr32w(ctlr, Rdtr, 0);
+	switch(ctlr->id){
+	case i82540em:
+	case i82540eplp:
+	case i82541gi:
+	case i82541gi2:
+	case i82541pi:
+	case i82546gb:
+	case i82546eb:
+	case i82547gi:
+		csr32w(ctlr, Radv, 64);
+		break;
+	}
+	csr32w(ctlr, Rxdctl, (8<<WthreshSHIFT)|(8<<HthreshSHIFT)|4);
+	/*
+	 * Enable checksum offload.
+	 */
+	csr32w(ctlr, Rxcsum, Tuofl|Ipofl|(ETHERHDRSIZE<<PcssSHIFT));
+
+	csr32w(ctlr, Rctl, Dpf|Bsize2048|Bam|RdtmsHALF);
+	igbeim(ctlr, Rxt0|Rxo|Rxdmt0|Rxseq);
+
+	/*
+	 * Transmit initialisation.
+	 * Mostly defaults from the datasheet, will
+	 * need some tuning for performance. The normal mode will
+	 * be full-duplex and things to tune for half-duplex are
+	 *	Tctl	re-transmit on late collision
+	 *	Tipg	all IPG times
+	 *	Tbt	burst timer
+	 *	Ait	adaptive IFS throttle
+	 * and in general
+	 *	Txdmac	packet prefetching
+	 *	Ett	transmit early threshold
+	 *	Tidv	interrupt delay value
+	 *	Txdctl	all the thresholds
+	 */
+	csr32w(ctlr, Tctl, (0x0F<<CtSHIFT)|Psp|(66<<ColdSHIFT));	/* Fd */
+	switch(ctlr->id){
+	default:
+		r = 6;
+		break;
+	case i82543gc:
+	case i82544ei:
+	case i82547ei:
+	case i82540em:
+	case i82540eplp:
+	case i82541gi:
+	case i82541gi2:
+	case i82541pi:
+	case i82546gb:
+	case i82546eb:
+	case i82547gi:
+		r = 8;
+		break;
+	}
+	csr32w(ctlr, Tipg, (6<<20)|(8<<10)|r);
+	csr32w(ctlr, Ait, 0);
+	csr32w(ctlr, Txdmac, 0);
+	csr32w(ctlr, Tidv, 128);
+
+	/*
+	 * Allocate the descriptor ring and load its
+	 * address and length into the NIC.
+	 */
+	ctlr->tdba = xspanalloc(Ntdesc*sizeof(Tdesc), 128 /* was 16 */, 0);
+	csr32w(ctlr, Tdbal, PCIWADDR(ctlr->tdba));
+	csr32w(ctlr, Tdbah, 0);
+	csr32w(ctlr, Tdlen, Ntdesc*sizeof(Tdesc));
+
+	/*
+	 * Initialise the ring head and tail pointers.
+	 */
+	ctlr->tdh = 0;
+	csr32w(ctlr, Tdh, ctlr->tdh);
+	ctlr->tdt = 0;
+	csr32w(ctlr, Tdt, ctlr->tdt);
+	ctlr->tb = malloc(sizeof(Block*)*Ntdesc);
+//	ctlr->im |= Txqe|Txdw;
+
+	r = (4<<WthreshSHIFT)|(4<<HthreshSHIFT)|(8<<PthreshSHIFT);
+	switch(ctlr->id){
+	default:
+		break;
+	case i82540em:
+	case i82540eplp:
+	case i82547gi:
+	case i82541pi:
+	case i82546gb:
+	case i82546eb:
+	case i82541gi:
+	case i82541gi2:
+		r = csr32r(ctlr, Txdctl);
+		r &= ~WthreshMASK;
+		r |= Gran|(4<<WthreshSHIFT);
+
+		csr32w(ctlr, Tadv, 64);
+		break;
+	}
+	csr32w(ctlr, Txdctl, r);
+
+	r = csr32r(ctlr, Tctl);
+	r |= Ten;
+	csr32w(ctlr, Tctl, r);
+
+	if(ctlr->mii == nil || ctlr->mii->curphy == nil) {
+		print("igbe: no mii (yet)\n");
+		return 0;
+	}
+	/* wait for the link to come up */
+	if (miistatus(ctlr->mii) < 0)
+		return -1;
+	print("igbe: phy: ");
+	phy = ctlr->mii->curphy;
+	if (phy->fd)
+		print("full duplex");
+	else
+		print("half duplex");
+	print(", %d Mb/s\n", phy->speed);
+
+	/*
+	 * Flow control.
+	 */
+	ctrl = csr32r(ctlr, Ctrl);
+	if(phy->rfc)
+		ctrl |= Rfce;
+	if(phy->tfc)
+		ctrl |= Tfce;
+	csr32w(ctlr, Ctrl, ctrl);
+
+	return 0;
+}
+
+static int
+i82543mdior(Ctlr* ctlr, int n)
+{
+	int ctrl, data, i, r;
+
+	/*
+	 * Read n bits from the Management Data I/O Interface.
+	 */
+	ctrl = csr32r(ctlr, Ctrl);
+	r = (ctrl & ~Mddo)|Mdco;
+	data = 0;
+	for(i = n-1; i >= 0; i--){
+		if(csr32r(ctlr, Ctrl) & Mdd)
+			data |= (1<<i);
+		csr32w(ctlr, Ctrl, Mdc|r);
+		csr32w(ctlr, Ctrl, r);
+	}
+	csr32w(ctlr, Ctrl, ctrl);
+
+	return data;
+}
+
+static int
+i82543mdiow(Ctlr* ctlr, int bits, int n)
+{
+	int ctrl, i, r;
+
+	/*
+	 * Write n bits to the Management Data I/O Interface.
+	 */
+	ctrl = csr32r(ctlr, Ctrl);
+	r = Mdco|Mddo|ctrl;
+	for(i = n-1; i >= 0; i--){
+		if(bits & (1<<i))
+			r |= Mdd;
+		else
+			r &= ~Mdd;
+		csr32w(ctlr, Ctrl, Mdc|r);
+		csr32w(ctlr, Ctrl, r);
+	}
+	csr32w(ctlr, Ctrl, ctrl);
+
+	return 0;
+}
+
+static int
+i82543miimir(Mii* mii, int pa, int ra)
+{
+	int data;
+	Ctlr *ctlr;
+
+	ctlr = mii->ctlr;
+
+	/*
+	 * MII Management Interface Read.
+	 *
+	 * Preamble;
+	 * ST+OP+PHYAD+REGAD;
+	 * TA + 16 data bits.
+	 */
+	i82543mdiow(ctlr, 0xFFFFFFFF, 32);
+	i82543mdiow(ctlr, 0x1800|(pa<<5)|ra, 14);
+	data = i82543mdior(ctlr, 18);
+
+	if(data & 0x10000)
+		return -1;
+
+	return data & 0xFFFF;
+}
+
+static int
+i82543miimiw(Mii* mii, int pa, int ra, int data)
+{
+	Ctlr *ctlr;
+
+	ctlr = mii->ctlr;
+
+	/*
+	 * MII Management Interface Write.
+	 *
+	 * Preamble;
+	 * ST+OP+PHYAD+REGAD+TA + 16 data bits;
+	 * Z.
+	 */
+	i82543mdiow(ctlr, 0xFFFFFFFF, 32);
+	data &= 0xFFFF;
+	data |= (0x05<<(5+5+2+16))|(pa<<(5+2+16))|(ra<<(2+16))|(0x02<<16);
+	i82543mdiow(ctlr, data, 32);
+
+	return 0;
+}
+
+static int
+igbemiimir(Mii* mii, int pa, int ra)
+{
+	Ctlr *ctlr;
+	int mdic, timo;
+
+	ctlr = mii->ctlr;
+
+	csr32w(ctlr, Mdic, MDIrop|(pa<<MDIpSHIFT)|(ra<<MDIrSHIFT));
+	mdic = 0;
+	for(timo = 64; timo; timo--){
+		mdic = csr32r(ctlr, Mdic);
+		if(mdic & (MDIe|MDIready))
+			break;
+		microdelay(1);
+	}
+
+	if((mdic & (MDIe|MDIready)) == MDIready)
+		return mdic & 0xFFFF;
+	return -1;
+}
+
+static int
+igbemiimiw(Mii* mii, int pa, int ra, int data)
+{
+	Ctlr *ctlr;
+	int mdic, timo;
+
+	ctlr = mii->ctlr;
+
+	data &= MDIdMASK;
+	csr32w(ctlr, Mdic, MDIwop|(pa<<MDIpSHIFT)|(ra<<MDIrSHIFT)|data);
+	mdic = 0;
+	for(timo = 64; timo; timo--){
+		mdic = csr32r(ctlr, Mdic);
+		if(mdic & (MDIe|MDIready))
+			break;
+		microdelay(1);
+	}
+	if((mdic & (MDIe|MDIready)) == MDIready)
+		return 0;
+	return -1;
+}
+
+static int
+igbemii(Ctlr* ctlr)
+{
+	MiiPhy *phy = (MiiPhy *)1;
+	int ctrl, p, r;
+
+	USED(phy);
+	r = csr32r(ctlr, Status);
+	if(r & Tbimode)
+		return -1;
+	if((ctlr->mii = malloc(sizeof(Mii))) == nil)
+		return -1;
+	ctlr->mii->ctlr = ctlr;
+
+	ctrl = csr32r(ctlr, Ctrl);
+	ctrl |= Slu;
+
+	switch(ctlr->id){
+	case i82543gc:
+		ctrl |= Frcdplx|Frcspd;
+		csr32w(ctlr, Ctrl, ctrl);
+
+		/*
+		 * The reset pin direction (Mdro) should already
+		 * be set from the EEPROM load.
+		 * If it's not set this configuration is unexpected
+		 * so bail.
+		 */
+		r = csr32r(ctlr, Ctrlext);
+		if(!(r & Mdro))
+			return -1;
+		csr32w(ctlr, Ctrlext, r);
+		delay(20);
+		r = csr32r(ctlr, Ctrlext);
+		r &= ~Mdr;
+		csr32w(ctlr, Ctrlext, r);
+		delay(20);
+		r = csr32r(ctlr, Ctrlext);
+		r |= Mdr;
+		csr32w(ctlr, Ctrlext, r);
+		delay(20);
+
+		ctlr->mii->mir = i82543miimir;
+		ctlr->mii->miw = i82543miimiw;
+		break;
+	case i82544ei:
+	case i82547ei:
+	case i82540em:
+	case i82540eplp:
+	case i82547gi:
+	case i82541gi:
+	case i82541gi2:
+	case i82541pi:
+	case i82546gb:
+	case i82546eb:
+		ctrl &= ~(Frcdplx|Frcspd);
+		csr32w(ctlr, Ctrl, ctrl);
+		ctlr->mii->mir = igbemiimir;
+		ctlr->mii->miw = igbemiimiw;
+		break;
+	default:
+		free(ctlr->mii);
+		ctlr->mii = nil;
+		return -1;
+	}
+
+	if(mii(ctlr->mii, ~0) == 0 || (phy = ctlr->mii->curphy) == nil){
+		if (0)
+			print("phy trouble: phy = 0x%lux\n", (ulong)phy);
+		free(ctlr->mii);
+		ctlr->mii = nil;
+		return -1;
+	}
+	if (Debug)
+		print("oui %X phyno %d\n", phy->oui, phy->phyno);
+	else
+		USED(phy);
+
+	/*
+	 * 8254X-specific PHY registers not in 802.3:
+	 *	0x10	PHY specific control
+	 *	0x14	extended PHY specific control
+	 * Set appropriate values then reset the PHY to have
+	 * changes noted.
+	 */
+	switch(ctlr->id){
+	case i82547gi:
+	case i82541gi:
+	case i82541gi2:
+	case i82541pi:
+	case i82546gb:
+	case i82546eb:
+		break;
+	default:
+		r = miimir(ctlr->mii, 16);
+		r |= 0x0800;			/* assert CRS on Tx */
+		r |= 0x0060;			/* auto-crossover all speeds */
+		r |= 0x0002;			/* polarity reversal enabled */
+		miimiw(ctlr->mii, 16, r);
+
+		r = miimir(ctlr->mii, 20);
+		r |= 0x0070;			/* +25MHz clock */
+		r &= ~0x0F00;
+		r |= 0x0100;			/* 1x downshift */
+		miimiw(ctlr->mii, 20, r);
+
+		miireset(ctlr->mii);
+		break;
+	}
+	p = 0;
+	if(ctlr->txcw & TxcwPs)
+		p |= AnaP;
+	if(ctlr->txcw & TxcwAs)
+		p |= AnaAP;
+	miiane(ctlr->mii, ~0, p, ~0);
+
+	return 0;
+}
+
+static int
+at93c46io(Ctlr* ctlr, char* op, int data)
+{
+	char *lp, *p;
+	int i, loop, eecd, r;
+
+	eecd = csr32r(ctlr, Eecd);
+
+	r = 0;
+	loop = -1;
+	lp = nil;
+	for(p = op; *p != '\0'; p++){
+		switch(*p){
+		default:
+			return -1;
+		case ' ':
+			continue;
+		case ':':			/* start of loop */
+			loop = strtol(p+1, &lp, 0)-1;
+			lp--;
+			if(p == lp)
+				loop = 7;
+			p = lp;
+			continue;
+		case ';':			/* end of loop */
+			if(lp == nil)
+				return -1;
+			loop--;
+			if(loop >= 0)
+				p = lp;
+			else
+				lp = nil;
+			continue;
+		case 'C':			/* assert clock */
+			eecd |= Sk;
+			break;
+		case 'c':			/* deassert clock */
+			eecd &= ~Sk;
+			break;
+		case 'D':			/* next bit in 'data' byte */
+			if(loop < 0)
+				return -1;
+			if(data & (1<<loop))
+				eecd |= Di;
+			else
+				eecd &= ~Di;
+			break;
+		case 'O':			/* collect data output */
+			i = (csr32r(ctlr, Eecd) & Do) != 0;
+			if(loop >= 0)
+				r |= (i<<loop);
+			else
+				r = i;
+			continue;
+		case 'I':			/* assert data input */
+			eecd |= Di;
+			break;
+		case 'i':			/* deassert data input */
+			eecd &= ~Di;
+			break;
+		case 'S':			/* enable chip select */
+			eecd |= Cs;
+			break;
+		case 's':			/* disable chip select */
+			eecd &= ~Cs;
+			break;
+		}
+		csr32w(ctlr, Eecd, eecd);
+		microdelay(50);
+	}
+	if(loop >= 0)
+		return -1;
+	return r;
+}
+
+static int
+at93c46r(Ctlr* ctlr)
+{
+	ushort sum;
+	char rop[20];
+	int addr, areq, bits, data, eecd, i;
+
+	eecd = csr32r(ctlr, Eecd);
+	if(eecd & Spi){
+		print("igbe: SPI EEPROM access not implemented\n");
+		return 0;
+	}
+	if(eecd & (Eeszaddr|Eesz256))
+		bits = 8;
+	else
+		bits = 6;
+	sum = 0;
+
+	switch(ctlr->id){
+	default:
+		areq = 0;
+		break;
+	case i82540em:
+	case i82540eplp:
+	case i82541gi:
+	case i82541gi2:
+	case i82541pi:
+	case i82547gi:
+	case i82546gb:
+	case i82546eb:
+		areq = 1;
+		csr32w(ctlr, Eecd, eecd|Areq);
+		for(i = 0; i < 1000; i++){
+			if((eecd = csr32r(ctlr, Eecd)) & Agnt)
+				break;
+			microdelay(5);
+		}
+		if(!(eecd & Agnt)){
+			print("igbe: not granted EEPROM access\n");
+			goto release;
+		}
+		break;
+	}
+	snprint(rop, sizeof(rop), "S :%dDCc;", bits+3);
+
+	for(addr = 0; addr < 0x40; addr++){
+		/*
+		 * Read a word at address 'addr' from the Atmel AT93C46
+		 * 3-Wire Serial EEPROM or compatible. The EEPROM access is
+		 * controlled by 4 bits in Eecd. See the AT93C46 datasheet
+		 * for protocol details.
+		 */
+		if(at93c46io(ctlr, rop, (0x06<<bits)|addr) != 0){
+			print("igbe: can't set EEPROM address 0x%2.2X\n", addr);
+			goto release;
+		}
+		data = at93c46io(ctlr, ":16COc;", 0);
+		at93c46io(ctlr, "sic", 0);
+		ctlr->eeprom[addr] = data;
+		sum += data;
+		if (Debug) {
+			if(addr && ((addr & 0x07) == 0))
+				print("\n");
+			print(" %4.4ux", data);
+		}
+	}
+	if (Debug)
+		print("\n");
+release:
+	if(areq)
+		csr32w(ctlr, Eecd, eecd & ~Areq);
+	return sum;
+}
+
+static void
+detach(Ctlr *ctlr)
+{
+	int r;
+
+	/*
+	 * Perform a device reset to get the chip back to the
+	 * power-on state, followed by an EEPROM reset to read
+	 * the defaults for some internal registers.
+	 */
+	csr32w(ctlr, Imc, ~0);
+	csr32w(ctlr, Rctl, 0);
+	csr32w(ctlr, Tctl, 0);
+
+	delay(20);
+
+	csr32w(ctlr, Ctrl, Devrst);
+	/* apparently needed on multi-GHz processors to avoid infinite loops */
+	delay(1);
+	while(csr32r(ctlr, Ctrl) & Devrst)
+		;
+
+	csr32w(ctlr, Ctrlext, Eerst | csr32r(ctlr, Ctrlext));
+	delay(1);
+	while(csr32r(ctlr, Ctrlext) & Eerst)
+		;
+
+	switch(ctlr->id){
+	default:
+		break;
+	case i82540em:
+	case i82540eplp:
+	case i82541gi:
+	case i82541gi2:
+	case i82541pi:
+	case i82547gi:
+	case i82546gb:
+	case i82546eb:
+		r = csr32r(ctlr, Manc);
+		r &= ~Arpen;
+		csr32w(ctlr, Manc, r);
+		break;
+	}
+
+	csr32w(ctlr, Imc, ~0);
+	delay(1);
+	while(csr32r(ctlr, Icr))
+		;
+}
+
+static void
+igbedetach(Ether *edev)
+{
+	detach(edev->ctlr);
+}
+
+static void
+igbeshutdown(Ether* ether)
+{
+print("igbeshutdown\n");
+	igbedetach(ether);
+}
+
+static int
+igbereset(Ctlr* ctlr)
+{
+	int ctrl, i, pause, r, swdpio, txcw;
+
+	detach(ctlr);
+
+	/*
+	 * Read the EEPROM, validate the checksum
+	 * then get the device back to a power-on state.
+	 */
+	r = at93c46r(ctlr);
+	/* zero return means no SPI EEPROM access */
+	if (r != 0 && r != 0xBABA){
+		print("igbe: bad EEPROM checksum - 0x%4.4uX\n", r);
+		return -1;
+	}
+
+	/*
+	 * Snarf and set up the receive addresses.
+	 * There are 16 addresses. The first should be the MAC address.
+	 * The others are cleared and not marked valid (MS bit of Rah).
+	 */
+	if ((ctlr->id == i82546gb || ctlr->id == i82546eb) &&
+	    BUSFNO(ctlr->pcidev->tbdf) == 1)
+		ctlr->eeprom[Ea+2] += 0x100;		/* second interface */
+	for(i = Ea; i < Eaddrlen/2; i++){
+		ctlr->ra[2*i]   = ctlr->eeprom[i];
+		ctlr->ra[2*i+1] = ctlr->eeprom[i]>>8;
+	}
+	/* lan id seems to vary on 82543gc; don't use it */
+	if (ctlr->id != i82543gc) {
+		r = (csr32r(ctlr, Status) & Lanid) >> 2;
+		ctlr->ra[5] += r;		/* ea ctlr[1] = ea ctlr[0]+1 */
+	}
+	r = (ctlr->ra[3]<<24)|(ctlr->ra[2]<<16)|(ctlr->ra[1]<<8)|ctlr->ra[0];
+	csr32w(ctlr, Ral, r);
+	r = 0x80000000|(ctlr->ra[5]<<8)|ctlr->ra[4];
+	csr32w(ctlr, Rah, r);
+	for(i = 1; i < 16; i++){
+		csr32w(ctlr, Ral+i*8, 0);
+		csr32w(ctlr, Rah+i*8, 0);
+	}
+
+	/*
+	 * Clear the Multicast Table Array.
+	 * It's a 4096 bit vector accessed as 128 32-bit registers.
+	 */
+	memset(ctlr->mta, 0, sizeof(ctlr->mta));
+	for(i = 0; i < 128; i++)
+		csr32w(ctlr, Mta+i*4, 0);
+
+	/*
+	 * Just in case the Eerst didn't load the defaults
+	 * (doesn't appear to fully on the 8243GC), do it manually.
+	 */
+	if (ctlr->id == i82543gc) {
+		txcw = csr32r(ctlr, Txcw);
+		txcw &= ~(TxcwAne|TxcwPauseMASK|TxcwFd);
+		ctrl = csr32r(ctlr, Ctrl);
+		ctrl &= ~(SwdpioloMASK|Frcspd|Ilos|Lrst|Fd);
+
+		if(ctlr->eeprom[Icw1] & 0x0400){
+			ctrl |= Fd;
+			txcw |= TxcwFd;
+		}
+		if(ctlr->eeprom[Icw1] & 0x0200)
+			ctrl |= Lrst;
+		if(ctlr->eeprom[Icw1] & 0x0010)
+			ctrl |= Ilos;
+		if(ctlr->eeprom[Icw1] & 0x0800)
+			ctrl |= Frcspd;
+		swdpio = (ctlr->eeprom[Icw1] & 0x01E0)>>5;
+		ctrl |= swdpio<<SwdpioloSHIFT;
+		csr32w(ctlr, Ctrl, ctrl);
+
+		ctrl = csr32r(ctlr, Ctrlext);
+		ctrl &= ~(Ips|SwdpiohiMASK);
+		swdpio = (ctlr->eeprom[Icw2] & 0x00F0)>>4;
+		if(ctlr->eeprom[Icw1] & 0x1000)
+			ctrl |= Ips;
+		ctrl |= swdpio<<SwdpiohiSHIFT;
+		csr32w(ctlr, Ctrlext, ctrl);
+
+		if(ctlr->eeprom[Icw2] & 0x0800)
+			txcw |= TxcwAne;
+		pause = (ctlr->eeprom[Icw2] & 0x3000)>>12;
+		txcw |= pause<<TxcwPauseSHIFT;
+		switch(pause){
+		default:
+			ctlr->fcrtl = 0x00002000;
+			ctlr->fcrth = 0x00004000;
+			txcw |= TxcwAs|TxcwPs;
+			break;
+		case 0:
+			ctlr->fcrtl = 0x00002000;
+			ctlr->fcrth = 0x00004000;
+			break;
+		case 2:
+			ctlr->fcrtl = 0;
+			ctlr->fcrth = 0;
+			txcw |= TxcwAs;
+			break;
+		}
+		ctlr->txcw = txcw;
+		csr32w(ctlr, Txcw, txcw);
+	}
+	/*
+	 * Flow control - values from the datasheet.
+	 */
+	csr32w(ctlr, Fcal, 0x00C28001);
+	csr32w(ctlr, Fcah, 0x00000100);
+	csr32w(ctlr, Fct, 0x00008808);
+	csr32w(ctlr, Fcttv, 0x00000100);
+
+	csr32w(ctlr, Fcrtl, ctlr->fcrtl);
+	csr32w(ctlr, Fcrth, ctlr->fcrth);
+
+	ilock(&ctlr->imlock);
+	csr32w(ctlr, Imc, ~0);
+	ctlr->im = 0;		/* was = Lsc, which hangs some controllers */
+	csr32w(ctlr, Ims, ctlr->im);
+	iunlock(&ctlr->imlock);
+
+	if(!(csr32r(ctlr, Status) & Tbimode) && igbemii(ctlr) < 0) {
+		print("igbe: igbemii failed\n");
+		return -1;
+	}
+
+	return 0;
+}
+
+static void
+igbepci(void)
+{
+	int port, cls;
+	Pcidev *p;
+	Ctlr *ctlr;
+	static int first = 1;
+
+	if (first)
+		first = 0;
+	else
+		return;
+
+	p = nil;
+	while(p = pcimatch(p, 0, 0)){
+		if(p->ccrb != 0x02 || p->ccru != 0)
+			continue;
+
+		switch((p->did<<16)|p->vid){
+		case i82542:
+		default:
+			continue;
+
+		case (0x1001<<16)|0x8086:	/* Intel PRO/1000 F */
+			break;
+		case i82543gc:
+		case i82544ei:
+		case i82547ei:
+		case i82540em:
+		case i82540eplp:
+		case i82547gi:
+		case i82541gi:
+		case i82541gi2:
+		case i82541pi:
+		case i82546gb:
+		case i82546eb:
+			break;
+		}
+
+		/* the 82547EI is on the CSA bus, whatever that is */
+		port = upamalloc(p->mem[0].bar & ~0x0F, p->mem[0].size, 0);
+		if(port == 0){
+			print("igbe: can't map %d @ 0x%8.8luX\n",
+				p->mem[0].size, p->mem[0].bar);
+			continue;
+		}
+
+		/*
+		 * from etherga620.c:
+		 * If PCI Write-and-Invalidate is enabled set the max write DMA
+		 * value to the host cache-line size (32 on Pentium or later).
+		 */
+		if(p->pcr & MemWrInv){
+			cls = pcicfgr8(p, PciCLS) * 4;
+			if(cls != CACHELINESZ)
+				pcicfgw8(p, PciCLS, CACHELINESZ/4);
+		}
+
+		cls = pcicfgr8(p, PciCLS);
+		switch(cls){
+			default:
+				print("igbe: unexpected CLS - %d bytes\n",
+					cls*sizeof(long));
+				break;
+			case 0x00:
+			case 0xFF:
+				/* alphapc 164lx returns 0 */
+				print("igbe: unusable PciCLS: %d, using %d longs\n",
+					cls, CACHELINESZ/sizeof(long));
+				cls = CACHELINESZ/sizeof(long);
+				pcicfgw8(p, PciCLS, cls);
+				break;
+			case 0x08:
+			case 0x10:
+				break;
+		}
+
+		ctlr = malloc(sizeof(Ctlr));
+		ctlr->port = port;
+		ctlr->pcidev = p;
+		ctlr->id = (p->did<<16)|p->vid;
+		ctlr->cls = cls*4;
+		ctlr->nic = KADDR(ctlr->port);
+		if (Debug)
+			print("status0 %8.8uX\n", csr32r(ctlr, Status));
+		if(igbereset(ctlr)){
+			free(ctlr);
+			continue;
+		}
+		if (Debug)
+			print("status1 %8.8uX\n", csr32r(ctlr, Status));
+		pcisetbme(p);
+
+		if(ctlrhead != nil)
+			ctlrtail->next = ctlr;
+		else
+			ctlrhead = ctlr;
+		ctlrtail = ctlr;
+	}
+}
+
+int
+igbepnp(Ether* edev)
+{
+	int i;
+	Ctlr *ctlr;
+	uchar ea[Eaddrlen];
+
+	if(ctlrhead == nil)
+		igbepci();
+
+	/*
+	 * Any adapter matches if no edev->port is supplied,
+	 * otherwise the ports must match.
+	 */
+	for(ctlr = ctlrhead; ctlr != nil; ctlr = ctlr->next){
+		if(ctlr->active)
+			continue;
+		if(edev->port == 0 || edev->port == ctlr->port){
+			ctlr->active = 1;
+			break;
+		}
+	}
+	if(ctlr == nil)
+		return -1;
+
+	edev->ctlr = ctlr;
+	edev->port = ctlr->port;
+	edev->irq = ctlr->pcidev->intl;
+	edev->tbdf = ctlr->pcidev->tbdf;
+//	edev->mbps = 1000;
+
+	/*
+	 * Check if the adapter's station address is to be overridden.
+	 * If not, read it from the EEPROM and set in ether->ea prior to
+	 * loading the station address in the hardware.
+	 */
+	memset(ea, 0, Eaddrlen);
+	if(memcmp(ea, edev->ea, Eaddrlen) == 0){
+		for(i = 0; i < Eaddrlen/2; i++){
+			edev->ea[2*i] = ctlr->eeprom[i];
+			edev->ea[2*i+1] = ctlr->eeprom[i]>>8;
+		}
+	}
+	igbeinit(edev);
+
+	/*
+	 * Linkage to the generic ethernet driver.
+	 */
+	edev->attach = igbeattach;
+	edev->transmit = igbetransmit;
+	edev->interrupt = igbeinterrupt;
+	edev->detach = igbedetach;
+
+	return 0;
+}
--- /dev/null
+++ b/os/boot/pc/ethermii.c
@@ -1,0 +1,224 @@
+#include "u.h"
+#include "lib.h"
+#include "mem.h"
+#include "dat.h"
+#include "fns.h"
+#include "io.h"
+
+#include "etherif.h"
+#include "ethermii.h"
+
+int
+mii(Mii* mii, int mask)
+{
+	MiiPhy *miiphy;
+	int bit, phyno, r, rmask;
+
+	/*
+	 * Probe through mii for PHYs in mask;
+	 * return the mask of those found in the current probe.
+	 * If the PHY has not already been probed, update
+	 * the Mii information.
+	 */
+	rmask = 0;
+	for(phyno = 0; phyno < NMiiPhy; phyno++){
+		bit = 1<<phyno;
+		if(!(mask & bit))
+			continue;
+		if(mii->mask & bit){
+			rmask |= bit;
+			continue;
+		}
+		if(mii->mir(mii, phyno, Bmsr) == -1)
+			continue;
+		if((miiphy = malloc(sizeof(MiiPhy))) == nil)
+			continue;
+
+		miiphy->mii = mii;
+		r = mii->mir(mii, phyno, Phyidr1);
+		miiphy->oui = (r & 0x3FFF)<<6;
+		r = mii->mir(mii, phyno, Phyidr2);
+		miiphy->oui |= r>>10;
+		miiphy->phyno = phyno;
+
+		miiphy->anar = ~0;
+		miiphy->fc = ~0;
+		miiphy->mscr = ~0;
+
+		mii->phy[phyno] = miiphy;
+		if(mii->curphy == nil)
+			mii->curphy = miiphy;
+		mii->mask |= bit;
+		mii->nphy++;
+
+		rmask |= bit;
+	}
+	return rmask;
+}
+
+int
+miimir(Mii* mii, int r)
+{
+	if(mii == nil || mii->ctlr == nil || mii->curphy == nil)
+		return -1;
+	return mii->mir(mii, mii->curphy->phyno, r);
+}
+
+int
+miimiw(Mii* mii, int r, int data)
+{
+	if(mii == nil || mii->ctlr == nil || mii->curphy == nil)
+		return -1;
+	return mii->miw(mii, mii->curphy->phyno, r, data);
+}
+
+int
+miireset(Mii* mii)
+{
+	int bmcr;
+
+	if(mii == nil || mii->ctlr == nil || mii->curphy == nil)
+		return -1;
+	bmcr = mii->mir(mii, mii->curphy->phyno, Bmcr);
+	bmcr |= BmcrR;
+	mii->miw(mii, mii->curphy->phyno, Bmcr, bmcr);
+	microdelay(1);
+
+	return 0;
+}
+
+int
+miiane(Mii* mii, int a, int p, int e)
+{
+	int anar, bmsr, mscr, r, phyno;
+
+	if(mii == nil || mii->ctlr == nil || mii->curphy == nil)
+		return -1;
+	phyno = mii->curphy->phyno;
+
+	bmsr = mii->mir(mii, phyno, Bmsr);
+	if(!(bmsr & BmsrAna))
+		return -1;
+
+	if(a != ~0)
+		anar = (AnaTXFD|AnaTXHD|Ana10FD|Ana10HD) & a;
+	else if(mii->curphy->anar != ~0)
+		anar = mii->curphy->anar;
+	else{
+		anar = mii->mir(mii, phyno, Anar);
+		anar &= ~(AnaAP|AnaP|AnaT4|AnaTXFD|AnaTXHD|Ana10FD|Ana10HD);
+		if(bmsr & Bmsr10THD)
+			anar |= Ana10HD;
+		if(bmsr & Bmsr10TFD)
+			anar |= Ana10FD;
+		if(bmsr & Bmsr100TXHD)
+			anar |= AnaTXHD;
+		if(bmsr & Bmsr100TXFD)
+			anar |= AnaTXFD;
+	}
+	mii->curphy->anar = anar;
+
+	if(p != ~0)
+		anar |= (AnaAP|AnaP) & p;
+	else if(mii->curphy->fc != ~0)
+		anar |= mii->curphy->fc;
+	mii->curphy->fc = (AnaAP|AnaP) & anar;
+
+	if(bmsr & BmsrEs){
+		mscr = mii->mir(mii, phyno, Mscr);
+		mscr &= ~(Mscr1000TFD|Mscr1000THD);
+		if(e != ~0)
+			mscr |= (Mscr1000TFD|Mscr1000THD) & e;
+		else if(mii->curphy->mscr != ~0)
+			mscr = mii->curphy->mscr;
+		else{
+			r = mii->mir(mii, phyno, Esr);
+			if(r & Esr1000THD)
+				mscr |= Mscr1000THD;
+			if(r & Esr1000TFD)
+				mscr |= Mscr1000TFD;
+		}
+		mii->curphy->mscr = mscr;
+		mii->miw(mii, phyno, Mscr, mscr);
+	}
+	mii->miw(mii, phyno, Anar, anar);
+
+	r = mii->mir(mii, phyno, Bmcr);
+	if(!(r & BmcrR)){
+		r |= BmcrAne|BmcrRan;
+		mii->miw(mii, phyno, Bmcr, r);
+	}
+
+	return 0;
+}
+
+int
+miistatus(Mii* mii)
+{
+	MiiPhy *phy;
+	int anlpar, bmsr, p, r, phyno;
+
+	if(mii == nil || mii->ctlr == nil || mii->curphy == nil)
+		return -1;
+	phy = mii->curphy;
+	phyno = phy->phyno;
+
+	/*
+	 * Check Auto-Negotiation is complete and link is up.
+	 * (Read status twice as the Ls bit is sticky).
+	 */
+	bmsr = mii->mir(mii, phyno, Bmsr);
+	if(!(bmsr & (BmsrAnc|BmsrAna)))
+		return -1;
+
+	bmsr = mii->mir(mii, phyno, Bmsr);
+	if(!(bmsr & BmsrLs)){
+		phy->link = 0;
+		return -1;
+	}
+
+	phy->speed = phy->fd = phy->rfc = phy->tfc = 0;
+	if(phy->mscr){
+		r = mii->mir(mii, phyno, Mssr);
+		if((phy->mscr & Mscr1000TFD) && (r & Mssr1000TFD)){
+			phy->speed = 1000;
+			phy->fd = 1;
+		}
+		else if((phy->mscr & Mscr1000THD) && (r & Mssr1000THD))
+			phy->speed = 1000;
+	}
+
+	anlpar = mii->mir(mii, phyno, Anlpar);
+	if(phy->speed == 0){
+		r = phy->anar & anlpar;
+		if(r & AnaTXFD){
+			phy->speed = 100;
+			phy->fd = 1;
+		}
+		else if(r & AnaTXHD)
+			phy->speed = 100;
+		else if(r & Ana10FD){
+			phy->speed = 10;
+			phy->fd = 1;
+		}
+		else if(r & Ana10HD)
+			phy->speed = 10;
+	}
+	if(phy->speed == 0)
+		return -1;
+
+	if(phy->fd){
+		p = phy->fc;
+		r = anlpar & (AnaAP|AnaP);
+		if(p == AnaAP && r == (AnaAP|AnaP))
+			phy->tfc = 1;
+		else if(p == (AnaAP|AnaP) && r == AnaAP)
+			phy->rfc = 1;
+		else if((p & AnaP) && (r & AnaP))
+			phy->rfc = phy->tfc = 1;
+	}
+
+	phy->link = 1;
+
+	return 0;
+}
--- /dev/null
+++ b/os/boot/pc/ethermii.h
@@ -1,0 +1,116 @@
+typedef struct Mii Mii;
+typedef struct MiiPhy MiiPhy;
+
+enum {					/* registers */
+	Bmcr		= 0x00,		/* Basic Mode Control */
+	Bmsr		= 0x01,		/* Basic Mode Status */
+	Phyidr1		= 0x02,		/* PHY Identifier #1 */
+	Phyidr2		= 0x03,		/* PHY Identifier #2 */
+	Anar		= 0x04,		/* Auto-Negotiation Advertisement */
+	Anlpar		= 0x05,		/* AN Link Partner Ability */
+	Aner		= 0x06,		/* AN Expansion */
+	Annptr		= 0x07,		/* AN Next Page TX */
+	Annprr		= 0x08,		/* AN Next Page RX */
+	Mscr		= 0x09,		/* MASTER-SLAVE Control */
+	Mssr		= 0x0A,		/* MASTER-SLAVE Status */
+	Esr		= 0x0F,		/* Extended Status */
+
+	NMiiPhyr	= 32,
+	NMiiPhy		= 32,
+};
+
+enum {					/* Bmcr */
+	BmcrSs1		= 0x0040,	/* Speed Select[1] */
+	BmcrCte		= 0x0080,	/* Collision Test Enable */
+	BmcrDm		= 0x0100,	/* Duplex Mode */
+	BmcrRan		= 0x0200,	/* Restart Auto-Negotiation */
+	BmcrI		= 0x0400,	/* Isolate */
+	BmcrPd		= 0x0800,	/* Power Down */
+	BmcrAne		= 0x1000,	/* Auto-Negotiation Enable */
+	BmcrSs0		= 0x2000,	/* Speed Select[0] */
+	BmcrLe		= 0x4000,	/* Loopback Enable */
+	BmcrR		= 0x8000,	/* Reset */
+};
+
+enum {					/* Bmsr */
+	BmsrEc		= 0x0001,	/* Extended Capability */
+	BmsrJd		= 0x0002,	/* Jabber Detect */
+	BmsrLs		= 0x0004,	/* Link Status */
+	BmsrAna		= 0x0008,	/* Auto-Negotiation Ability */
+	BmsrRf		= 0x0010,	/* Remote Fault */
+	BmsrAnc		= 0x0020,	/* Auto-Negotiation Complete */
+	BmsrPs		= 0x0040,	/* Preamble Suppression Capable */
+	BmsrEs		= 0x0100,	/* Extended Status */
+	Bmsr100T2HD	= 0x0200,	/* 100BASE-T2 HD Capable */
+	Bmsr100T2FD	= 0x0400,	/* 100BASE-T2 FD Capable */
+	Bmsr10THD	= 0x0800,	/* 100BASE-T HD Capable */
+	Bmsr10TFD	= 0x1000,	/* 10BASE-T FD Capable */
+	Bmsr100TXHD	= 0x2000,	/* 100BASE-TX HD Capable */
+	Bmsr100TXFD	= 0x4000,	/* 100BASE-TX FD Capable */
+	Bmsr100T4	= 0x8000,	/* 100BASE-T4 Capable */
+};
+
+enum {					/* Anar/Anlpar */
+	Ana10HD		= 0x0020,	/* Advertise 10BASE-T */
+	Ana10FD		= 0x0040,	/* Advertise 10BASE-T FD */
+	AnaTXHD		= 0x0080,	/* Advertise 100BASE-TX */
+	AnaTXFD		= 0x0100,	/* Advertise 100BASE-TX FD */
+	AnaT4		= 0x0200,	/* Advertise 100BASE-T4 */
+	AnaP		= 0x0400,	/* Pause */
+	AnaAP		= 0x0800,	/* Asymmetrical Pause */
+	AnaRf		= 0x2000,	/* Remote Fault */
+	AnaAck		= 0x4000,	/* Acknowledge */
+	AnaNp		= 0x8000,	/* Next Page Indication */
+};
+
+enum {					/* Mscr */
+	Mscr1000THD	= 0x0100,	/* Advertise 1000BASE-T HD */
+	Mscr1000TFD	= 0x0200,	/* Advertise 1000BASE-T FD */
+};
+
+enum {					/* Mssr */
+	Mssr1000THD	= 0x0400,	/* Link Partner 1000BASE-T HD able */
+	Mssr1000TFD	= 0x0800,	/* Link Partner 1000BASE-T FD able */
+};
+
+enum {					/* Esr */
+	Esr1000THD	= 0x1000,	/* 1000BASE-T HD Capable */
+	Esr1000TFD	= 0x2000,	/* 1000BASE-T FD Capable */
+	Esr1000XHD	= 0x4000,	/* 1000BASE-X HD Capable */
+	Esr1000XFD	= 0x8000,	/* 1000BASE-X FD Capable */
+};
+
+typedef struct Mii {
+	Lock;
+	int	nphy;
+	int	mask;
+	MiiPhy*	phy[NMiiPhy];
+	MiiPhy*	curphy;
+
+	void*	ctlr;
+	int	(*mir)(Mii*, int, int);
+	int	(*miw)(Mii*, int, int, int);
+} Mii;
+
+typedef struct MiiPhy {
+	Mii*	mii;
+	int	oui;
+	int	phyno;
+
+	int	anar;
+	int	fc;
+	int	mscr;
+
+	int	link;
+	int	speed;
+	int	fd;
+	int	rfc;
+	int	tfc;
+};
+
+extern int mii(Mii*, int);
+extern int miiane(Mii*, int, int, int);
+extern int miimir(Mii*, int);
+extern int miimiw(Mii*, int, int);
+extern int miireset(Mii*);
+extern int miistatus(Mii*);
--- /dev/null
+++ b/os/boot/pc/etherrhine.c
@@ -1,0 +1,657 @@
+/*
+	Via Rhine driver, written for VT6102.
+	Uses the ethermii to control PHY.
+
+	Currently always copies on both, tx and rx.
+	rx side could be copy-free, and tx-side might be made
+	(almost) copy-free by using (possibly) two descriptors (if it allows
+	arbitrary tx lengths, which it should..): first for alignment and
+	second for rest of the frame. Rx-part should be worth doing.
+ */
+#include "u.h"
+#include "lib.h"
+#include "mem.h"
+#include "dat.h"
+#include "fns.h"
+#include "io.h"
+
+typedef struct QLock { int r; } QLock;
+#define qlock(i)	while(0)
+#define qunlock(i)	while(0)
+#define iprint		print
+
+#include "etherif.h"
+#include "ethermii.h"
+
+enum {
+	Ntxd = 4,
+	Nrxd = 4,
+	Nwait = 50,
+	BIGSTR = 8192,
+};
+
+typedef struct Desc Desc;
+typedef struct Ctlr Ctlr;
+
+struct Desc {
+	ulong	stat;
+	ulong	size;
+	ulong	addr;
+	ulong	next;
+	char	*buf;
+	ulong	pad[3];
+};
+
+struct Ctlr {
+	Pcidev	*pci;
+	int	attached;
+	int	txused;
+	int	txhead;
+	int	txtail;
+	int	rxtail;
+	ulong	port;
+
+	Mii	mii;
+
+	Desc	*txd;		/* wants to be aligned on 16-byte boundary */
+	Desc	*rxd;
+
+	QLock	attachlck;
+	Lock	tlock;
+};
+
+#define ior8(c, r)	(inb((c)->port+(r)))
+#define iow8(c, r, b)	(outb((c)->port+(r), (int)(b)))
+#define ior16(c, r)	(ins((c)->port+(r)))
+#define ior32(c, r)	(inl((c)->port+(r)))
+#define iow16(c, r, w)	(outs((c)->port+(r), (ushort)(w)))
+#define iow32(c, r, l)	(outl((c)->port+(r), (ulong)(l)))
+
+/* names used everywhere else */
+#define csr8r ior8
+#define csr8w iow8
+#define csr16r ior16
+#define csr16w iow16
+#define csr32r ior32
+#define csr32w iow32
+
+enum Regs {
+	Eaddr		= 0x0,
+	Rcr		= 0x6,
+	Tcr		= 0x7,
+	Cr		= 0x8,
+	Isr		= 0xc,
+	Imr		= 0xe,
+	McastAddr	= 0x10,
+	RxdAddr		= 0x18,
+	TxdAddr		= 0x1C,
+	Bcr0		= 0x6E,		/* Bus Control */
+	Bcr1		= 0x6F,
+	RhineMiiPhy	= 0x6C,
+	RhineMiiSr	= 0x6D,
+	RhineMiiCr	= 0x70,
+	RhineMiiAddr	= 0x71,
+	RhineMiiData	= 0x72,
+	Eecsr		= 0x74,
+	ConfigB		= 0x79,
+	ConfigD		= 0x7B,
+	MiscCr		= 0x80,
+	HwSticky	= 0x83,
+	MiscIsr		= 0x84,
+	MiscImr		= 0x86,
+	WolCrSet	= 0xA0,
+	WolCfgSet	= 0xA1,
+	WolCgSet	= 0xA3,
+	WolCrClr	= 0xA4,
+	PwrCfgClr	= 0xA5,
+	WolCgClr	= 0xA7,
+};
+
+enum {					/* Rcr */
+	Sep		= 0x01,		/* Accept Error Packets */
+	Ar		= 0x02,		/* Accept Small Packets */
+	Am		= 0x04,		/* Accept Multicast */
+	Ab		= 0x08,		/* Accept Broadcast */
+	RxBcast		= Ab,
+	Prom		= 0x10,		/* Accept Physical Address Packets */
+	RxProm		= Prom,
+	RrftMASK	= 0xE0,		/* Receive FIFO Threshold */
+	RrftSHIFT	= 5,
+	Rrft64		= 0<<RrftSHIFT,
+	Rrft32		= 1<<RrftSHIFT,
+	Rrft128		= 2<<RrftSHIFT,
+	Rrft256		= 3<<RrftSHIFT,
+	Rrft512		= 4<<RrftSHIFT,
+	Rrft768		= 5<<RrftSHIFT,
+	Rrft1024	= 6<<RrftSHIFT,
+	RrftSAF		= 7<<RrftSHIFT,
+};
+
+enum {					/* Tcr */
+	Lb0		= 0x02,		/* Loopback Mode */
+	Lb1		= 0x04,
+	Ofset		= 0x08,		/* Back-off Priority Selection */
+	RtsfMASK	= 0xE0,		/* Transmit FIFO Threshold */
+	RtsfSHIFT	= 5,
+	Rtsf128		= 0<<RtsfSHIFT,
+	Rtsf256		= 1<<RtsfSHIFT,
+	Rtsf512		= 2<<RtsfSHIFT,
+	Rtsf1024	= 3<<RtsfSHIFT,
+	RtsfSAF		= 7<<RtsfSHIFT,
+};
+
+enum Crbits {
+	Init		= 1<<0,
+	Start		= 1<<1,
+	Stop		= 1<<2,
+	RxOn		= 1<<3,
+	TxOn		= 1<<4,
+	Tdmd		= 1<<5,
+	Rdmd		= 1<<6,
+	EarlyRx		= 1<<8,
+	Reserved0	= 1<<9,
+	FullDuplex	= 1<<10,
+	NoAutoPoll	= 1<<11,
+	Reserved1	= 1<<12,
+	Tdmd1		= 1<<13,
+	Rdmd1		= 1<<14,
+	Reset		= 1<<15,
+};
+
+enum Isrbits {
+	RxOk		= 1<<0,
+	TxOk		= 1<<1,
+	RxErr		= 1<<2,
+	TxErr		= 1<<3,
+	TxBufUdf	= 1<<4,
+	RxBufLinkErr	= 1<<5,
+	BusErr		= 1<<6,
+	CrcOvf		= 1<<7,
+	EarlyRxInt	= 1<<8,
+	TxFifoUdf	= 1<<9,
+	RxFifoOvf	= 1<<10,
+	TxPktRace	= 1<<11,
+	NoRxbuf		= 1<<12,
+	TxCollision	= 1<<13,
+	PortCh		= 1<<14,
+	GPInt		= 1<<15,
+};
+
+enum {					/* Bcr0 */
+	DmaMASK		= 0x07,		/* DMA Length */
+	DmaSHIFT	= 0,
+	Dma32		= 0<<DmaSHIFT,
+	Dma64		= 1<<DmaSHIFT,
+	Dma128		= 2<<DmaSHIFT,
+	Dma256		= 3<<DmaSHIFT,
+	Dma512		= 4<<DmaSHIFT,
+	Dma1024		= 5<<DmaSHIFT,
+	DmaSAF		= 7<<DmaSHIFT,
+	CrftMASK	= 0x38,		/* Rx FIFO Threshold */
+	CrftSHIFT	= 3,
+	Crft64		= 1<<CrftSHIFT,
+	Crft128		= 2<<CrftSHIFT,
+	Crft256		= 3<<CrftSHIFT,
+	Crft512		= 4<<CrftSHIFT,
+	Crft1024	= 5<<CrftSHIFT,
+	CrftSAF		= 7<<CrftSHIFT,
+	Extled		= 0x40,		/* Extra LED Support Control */
+	Med2		= 0x80,		/* Medium Select Control */
+};
+
+enum {					/* Bcr1 */
+	PotMASK		= 0x07,		/* Polling Timer Interval */
+	PotSHIFT	= 0,
+	CtftMASK	= 0x38,		/* Tx FIFO Threshold */
+	CtftSHIFT	= 3,
+	Ctft64		= 1<<CtftSHIFT,
+	Ctft128		= 2<<CtftSHIFT,
+	Ctft256		= 3<<CtftSHIFT,
+	Ctft512		= 4<<CtftSHIFT,
+	Ctft1024	= 5<<CtftSHIFT,
+	CtftSAF		= 7<<CtftSHIFT,
+};
+
+
+enum Eecsrbits {
+	EeAutoLoad	= 1<<5,
+};
+
+enum Descbits {
+	OwnNic		= 1<<31,	/* stat */
+	TxAbort		= 1<<8,		/* stat */
+	TxError		= 1<<15,	/* stat */
+	RxChainbuf	= 1<<10,	/* stat */
+	RxChainStart	= 1<<9,		/* stat */
+	RxChainEnd	= 1<<8,		/* stat */
+	Chainbuf	= 1<<15,	/* size rx & tx*/
+	TxDisableCrc	= 1<<16,	/* size */
+	TxChainStart	= 1<<21,	/* size */
+	TxChainEnd	= 1<<22,	/* size */
+	TxInt		= 1<<23,	/* size */
+};
+
+enum RhineMiiCrbits {
+	Mdc	= 1<<0,
+	Mdi	= 1<<1,
+	Mdo	= 1<<2,
+	Mdout	= 1<<3,
+	Mdpm	= 1<<4,
+	Wcmd	= 1<<5,
+	Rcmd	= 1<<6,
+	Mauto	= 1<<7,
+};
+
+static void
+attach(Ether *edev)
+{
+	Ctlr *ctlr;
+	Desc *txd, *rxd, *td, *rd;
+	Mii *mi;
+	MiiPhy *phy;
+	int i, s;
+
+	ctlr = edev->ctlr;
+	qlock(&ctlr->attachlck);
+	if (ctlr->attached == 0) {
+		txd = ctlr->txd;
+		rxd = ctlr->rxd;
+		for (i = 0; i < Ntxd; ++i) {
+			td = &txd[i];
+			td->next = PCIWADDR(&txd[(i+1) % Ntxd]);
+			td->buf = xspanalloc(sizeof(Etherpkt)+4, 4, 0);
+			td->addr = PCIWADDR(td->buf);
+			td->size = 0;
+			coherence();
+			td->stat = 0;
+		}
+		for (i = 0; i < Nrxd; ++i) {
+			rd = &rxd[i];
+			rd->next = PCIWADDR(&rxd[(i+1) % Nrxd]);
+			rd->buf = xspanalloc(sizeof(Etherpkt)+4, 4, 0);
+			rd->addr = PCIWADDR(rd->buf);
+			rd->size = sizeof(Etherpkt)+4;
+			coherence();
+			rd->stat = OwnNic;
+		}
+
+		ctlr->txhead = ctlr->txtail = ctlr->rxtail = 0;
+		mi = &ctlr->mii;
+		miistatus(mi);
+		phy = mi->curphy;
+		s = splhi();
+		iow32(ctlr, TxdAddr, PCIWADDR(&txd[0]));
+		iow32(ctlr, RxdAddr, PCIWADDR(&rxd[0]));
+		iow16(ctlr, Cr, (phy->fd? FullDuplex: 0) | NoAutoPoll | TxOn |
+			RxOn | Start | Rdmd);
+		iow16(ctlr, Isr, 0xFFFF);
+		iow16(ctlr, Imr, 0xFFFF);
+		iow8(ctlr, MiscIsr, 0xFF);
+		iow8(ctlr, MiscImr, ~(3<<5));
+		splx(s);
+		ctlr->attached = 1;
+	}
+	qunlock(&ctlr->attachlck);
+}
+
+static void
+txstart(Ether *edev)
+{
+	Ctlr *ctlr;
+	Desc *txd, *td;
+	int i, txused, n;
+	RingBuf *tb;
+
+	ctlr = edev->ctlr;
+	txd = ctlr->txd;
+	i = ctlr->txhead;
+	n = 0;
+	for (txused = ctlr->txused; txused < Ntxd; txused++) {
+		tb = &edev->tb[edev->ti];
+		if(tb->owner != Interface)
+			break;
+
+		td = &txd[i];
+		memmove(td->buf, tb->pkt, tb->len);
+		/* could reduce number of intrs here */
+		td->size = tb->len | TxChainStart | TxChainEnd | TxInt;
+		coherence();
+		td->stat = OwnNic;
+		i = (i + 1) % Ntxd;
+		n++;
+
+		tb->owner = Host;
+		edev->ti = NEXT(edev->ti, edev->ntb);
+	}
+	if (n)
+		iow16(ctlr, Cr, ior16(ctlr, Cr) | Tdmd);
+
+	ctlr->txhead = i;
+	ctlr->txused = txused;
+}
+
+static void
+transmit(Ether *edev)
+{
+	Ctlr *ctlr;
+
+	ctlr = edev->ctlr;
+	ilock(&ctlr->tlock);
+	txstart(edev);
+	iunlock(&ctlr->tlock);
+}
+
+static void
+txcomplete(Ether *edev)
+{
+	Ctlr *ctlr;
+	Desc *txd, *td;
+	int i, txused;
+	ulong stat;
+
+	ctlr = edev->ctlr;
+ 	txd = ctlr->txd;
+	i = ctlr->txtail;
+	for (txused = ctlr->txused; txused > 0; txused--) {
+		td = &txd[i];
+		stat = td->stat;
+		if (stat & OwnNic)
+			break;
+		i = (i + 1) % Ntxd;
+	}
+	ctlr->txused = txused;
+	ctlr->txtail = i;
+
+	if (txused <= Ntxd/2)
+		txstart(edev);
+}
+
+static void
+interrupt(Ureg *, void *arg)
+{
+	Ether *edev;
+	Ctlr *ctlr;
+	RingBuf *rb;
+	ushort  isr, misr;
+	ulong stat;
+	Desc *rxd, *rd;
+	int i, n, size;
+
+	edev = (Ether*)arg;
+	ctlr = edev->ctlr;
+	iow16(ctlr, Imr, 0);
+	isr = ior16(ctlr, Isr);
+	iow16(ctlr, Isr, 0xFFFF);
+	/* don't care about used defined intrs */
+	misr = ior16(ctlr, MiscIsr) & ~(3<<5);
+
+	if (isr & RxOk) {
+		rxd = ctlr->rxd;
+		i = ctlr->rxtail;
+
+		n = 0;
+		while ((rxd[i].stat & OwnNic) == 0) {
+			rd = &rxd[i];
+			stat = rd->stat;
+			if (stat & 0xFF)
+				iprint("rx: %lux\n", stat & 0xFF);
+			size = ((rd->stat>>16) & (2048-1)) - 4;
+
+			rb = &edev->rb[edev->ri];
+			if(rb->owner == Interface){
+				rb->owner = Host;
+				rb->len = size;
+				memmove(rb->pkt, rd->buf, size);
+				edev->ri = NEXT(edev->ri, edev->nrb);
+			}
+
+			rd->size = sizeof(Etherpkt)+4;
+			coherence();
+			rd->stat = OwnNic;
+			i = (i + 1) % Nrxd;
+			n++;
+		}
+		if (n)
+			iow16(ctlr, Cr, ior16(ctlr, Cr) | Rdmd);
+		ctlr->rxtail = i;
+		isr &= ~RxOk;
+	}
+	if (isr & TxOk) {
+		txcomplete(edev);
+		isr &= ~TxOk;
+	}
+	if (isr | misr)
+		iprint("etherrhine: unhandled irq(s). isr:%x misr:%x\n",
+			isr, misr);
+	iow16(ctlr, Imr, 0xFFFF);
+}
+
+static int
+miiread(Mii *mii, int phy, int reg)
+{
+	Ctlr *ctlr;
+	int n;
+
+	ctlr = mii->ctlr;
+
+	n = Nwait;
+	while (n-- && ior8(ctlr, RhineMiiCr) & (Rcmd | Wcmd))
+		microdelay(1);
+	if (n == Nwait)
+		iprint("etherrhine: miiread: timeout\n");
+
+	iow8(ctlr, RhineMiiCr, 0);
+	iow8(ctlr, RhineMiiPhy, phy);
+	iow8(ctlr, RhineMiiAddr, reg);
+	iow8(ctlr, RhineMiiCr, Rcmd);
+
+	n = Nwait;
+	while (n-- && ior8(ctlr, RhineMiiCr) & Rcmd)
+		microdelay(1);
+	if (n == Nwait)
+		iprint("etherrhine: miiread: timeout\n");
+
+	return ior16(ctlr, RhineMiiData);
+}
+
+static int
+miiwrite(Mii *mii, int phy, int reg, int data)
+{
+	int n;
+	Ctlr *ctlr;
+
+	ctlr = mii->ctlr;
+
+	n = Nwait;
+	while (n-- && ior8(ctlr, RhineMiiCr) & (Rcmd | Wcmd))
+		microdelay(1);
+	if (n == Nwait)
+		iprint("etherrhine: miiwrite: timeout\n");
+
+	iow8(ctlr, RhineMiiCr, 0);
+	iow8(ctlr, RhineMiiPhy, phy);
+	iow8(ctlr, RhineMiiAddr, reg);
+	iow16(ctlr, RhineMiiData, data);
+	iow8(ctlr, RhineMiiCr, Wcmd);
+
+	n = Nwait;
+	while (n-- && ior8(ctlr, RhineMiiCr) & Wcmd)
+		microdelay(1);
+	if (n == Nwait)
+		iprint("etherrhine: miiwrite: timeout\n");
+
+	return 0;
+}
+
+static void
+reset(Ctlr* ctlr)
+{
+	int r, timeo;
+
+	/*
+	 * Soft reset the controller.
+	 */
+	csr16w(ctlr, Cr, Stop);
+	csr16w(ctlr, Cr, Stop|Reset);
+	for(timeo = 0; timeo < 10000; timeo++){
+		if(!(csr16r(ctlr, Cr) & Reset))
+			break;
+		microdelay(1);
+	}
+	if(timeo >= 1000)
+		return;
+
+	/*
+	 * Load the MAC address into the PAR[01]
+	 * registers.
+	 */
+	r = csr8r(ctlr, Eecsr);
+	csr8w(ctlr, Eecsr, EeAutoLoad|r);
+	for(timeo = 0; timeo < 100; timeo++){
+		if(!(csr8r(ctlr, Cr) & EeAutoLoad))
+			break;
+		microdelay(1);
+	}
+	if(timeo >= 100)
+		return;
+
+	/*
+	 * Configure DMA and Rx/Tx thresholds.
+	 * If the Rx/Tx threshold bits in Bcr[01] are 0 then
+	 * the thresholds are determined by Rcr/Tcr.
+	 */
+	r = csr8r(ctlr, Bcr0) & ~(CrftMASK|DmaMASK);
+	csr8w(ctlr, Bcr0, r|Crft64|Dma64);
+	r = csr8r(ctlr, Bcr1) & ~CtftMASK;
+	csr8w(ctlr, Bcr1, r|Ctft64);
+
+	r = csr8r(ctlr, Rcr) & ~(RrftMASK|Prom|Ar|Sep);
+	csr8w(ctlr, Rcr, r|Ab|Am);
+
+	r = csr8r(ctlr, Tcr) & ~(RtsfMASK|Ofset|Lb1|Lb0);
+	csr8w(ctlr, Tcr, r);
+}
+
+static void
+detach(Ether* edev)
+{
+	reset(edev->ctlr);
+}
+
+static void
+init(Ether *edev)
+{
+	Ctlr *ctlr;
+	int i;
+
+	ctlr = edev->ctlr;
+	ilock(&ctlr->tlock);
+
+	pcisetbme(ctlr->pci);
+	reset(ctlr);
+
+	iow8(ctlr, Eecsr, ior8(ctlr, Eecsr) | EeAutoLoad);
+	for (i = 0; i < Nwait; ++i) {
+		if ((ior8(ctlr, Eecsr) & EeAutoLoad) == 0)
+			break;
+		delay(5);
+	}
+	if (i >= Nwait)
+		iprint("etherrhine: eeprom autoload timeout\n");
+
+	for (i = 0; i < Eaddrlen; ++i)
+		edev->ea[i] = ior8(ctlr, Eaddr + i);
+
+	ctlr->mii.mir = miiread;
+	ctlr->mii.miw = miiwrite;
+	ctlr->mii.ctlr = ctlr;
+
+	if(mii(&ctlr->mii, ~0) == 0 || ctlr->mii.curphy == nil){
+		iunlock(&ctlr->tlock);
+		iprint("etherrhine: init mii failure\n");
+		return;
+	}
+	for (i = 0; i < NMiiPhy; ++i)
+		if (ctlr->mii.phy[i])
+			if (ctlr->mii.phy[i]->oui != 0xFFFFF)
+				ctlr->mii.curphy = ctlr->mii.phy[i];
+	miistatus(&ctlr->mii);
+
+	iow16(ctlr, Imr, 0);
+	iow16(ctlr, Cr, ior16(ctlr, Cr) | Stop);
+
+	iunlock(&ctlr->tlock);
+}
+
+static Pcidev *
+rhinematch(ulong)
+{
+	static int nrhines = 0;
+	int nfound = 0;
+	Pcidev *p = nil;
+
+	while(p = pcimatch(p, 0x1106, 0)){
+		if(p->ccrb != Pcibcnet || p->ccru != Pciscether)
+			continue;
+		switch((p->did<<16)|p->vid){
+		default:
+			continue;
+		case (0x3053<<16)|0x1106:	/* Rhine III vt6105m (Soekris) */
+		case (0x3065<<16)|0x1106:	/* Rhine II */
+		case (0x3106<<16)|0x1106:	/* Rhine III */
+			if (++nfound > nrhines) {
+				nrhines++;
+				return p;
+			}
+			break;
+		}
+	}
+	return p;
+}
+
+int
+rhinepnp(Ether *edev)
+{
+	Pcidev *p;
+	Ctlr *ctlr;
+	ulong port;
+
+	if (edev->attach)
+		return 0;
+	p = rhinematch(edev->port);
+	if (p == nil)
+		return -1;
+
+	port = p->mem[0].bar & ~1;
+
+	if ((ctlr = malloc(sizeof(Ctlr))) == nil) {
+		print("etherrhine: couldn't allocate memory for ctlr\n");
+		return -1;
+	}
+	memset(ctlr, 0, sizeof(Ctlr));
+	ctlr->txd = xspanalloc(sizeof(Desc) * Ntxd, 16, 0);
+	ctlr->rxd = xspanalloc(sizeof(Desc) * Nrxd, 16, 0);
+
+	ctlr->pci = p;
+	ctlr->port = port;
+
+	edev->ctlr = ctlr;
+	edev->port = ctlr->port;
+	edev->irq = p->intl;
+	edev->tbdf = p->tbdf;
+
+	init(edev);
+
+	edev->attach = attach;
+	edev->transmit = transmit;
+	edev->interrupt = interrupt;
+	edev->detach = detach;
+
+	return 0;
+}
+
+int
+vt6102pnp(Ether *edev)
+{
+	return rhinepnp(edev);
+}
--- /dev/null
+++ b/os/boot/pc/fns.h
@@ -1,0 +1,175 @@
+void	aamloop(int);
+void	addconf(char*, ...);
+Alarm*	alarm(int, void (*)(Alarm*), void*);
+void	alarminit(void);
+Block*	allocb(int);
+void	apminit(void);
+int	biosboot(int dev, char *file, Boot *b);
+void*	biosgetfspart(int i, char *name, int chatty);
+void	biosinitdev(int i, char *name);
+int	biosinit(void);
+void	biosprintbootdevs(int dev);
+void	biosprintdevs(int i);
+int	bootpboot(int, char*, Boot*);
+int	bootpass(Boot*, void*, int);
+void	cancel(Alarm*);
+int	cdinit(void);
+void	check(char*);
+void	cgascreenputs(char*, int);
+int	cistrcmp(char*, char*);
+int	cistrncmp(char*, char*, int);
+void	changeconf(char*, ...);
+void	checkalarms(void);
+void	clockinit(void);
+#define coherence()	mb386()
+void	consdrain(void);
+void	consinit(char*, char*);
+void	consputs(char*, int);
+void	delay(int);
+uchar*	etheraddr(int);
+int	etherinit(void);
+void	etherinitdev(int, char*);
+void	etherprintdevs(int);
+int	etherrxflush(int);
+int	etherrxpkt(int, Etherpkt*, int);
+int	ethertxpkt(int, Etherpkt*, int, int);
+#define	evenaddr(x)		/* 386 doesn't care */
+int	floppyboot(int, char*, Boot*);
+int	floppyinit(void);
+void	floppyinitdev(int, char*);
+void	floppyprintdevs(int);
+void*	floppygetfspart(int, char*, int);
+void	freeb(Block*);
+char*	getconf(char*);
+ulong	getcr0(void);
+ulong	getcr2(void);
+ulong	getcr3(void);
+ulong	getcr4(void);
+int	getfields(char*, char**, int, char);
+int	getstr(char*, char*, int, char*, int);
+int	gunzip(uchar*, int, uchar*, int);
+void	i8042a20(void);
+void	i8042init(void);
+void	i8042reset(void);
+void*	ialloc(ulong, int);
+void	idle(void);
+void	ilock(Lock*);
+int	inb(int);
+ushort	ins(int);
+ulong	inl(int);
+void	insb(int, void*, int);
+void	inss(int, void*, int);
+void	insl(int, void*, int);
+#define ioalloc(addr, len, align, name)	(addr)
+#define iofree(addr)
+void	iunlock(Lock*);
+int	isaconfig(char*, int, ISAConf*);
+void	kbdinit(void);
+void	kbdchar(int);
+void	machinit(void);
+void	mb386(void);
+void	meminit(ulong);
+void	microdelay(int);
+void	mmuinit(void);
+#define	nelem(x)	(sizeof(x)/sizeof(x[0]))
+char*	nextelem(char*, char*);
+uchar	nvramread(int);
+void	outb(int, int);
+void	outs(int, ushort);
+void	outl(int, ulong);
+void	outsb(int, void*, int);
+void	outss(int, void*, int);
+void	outsl(int, void*, int);
+void	panic(char*, ...);
+ulong	pcibarsize(Pcidev*, int);
+int	pcicfgr8(Pcidev*, int);
+int	pcicfgr16(Pcidev*, int);
+int	pcicfgr32(Pcidev*, int);
+void	pcicfgw8(Pcidev*, int, int);
+void	pcicfgw16(Pcidev*, int, int);
+void	pcicfgw32(Pcidev*, int, int);
+void	pciclrbme(Pcidev*);
+void	pciclrioe(Pcidev*);
+void	pciclrmwi(Pcidev*);
+int	pcigetpms(Pcidev*);
+void	pcihinv(Pcidev*);
+Pcidev*	pcimatch(Pcidev*, int, int);
+uchar	pciintl(Pcidev *);
+uchar	pciipin(Pcidev *, uchar);
+void	pcireset(void);
+void	pcisetbme(Pcidev*);
+void	pcisetioe(Pcidev*);
+void	pcisetmwi(Pcidev*);
+int	pcisetpms(Pcidev*, int);
+void	pcmcisread(PCMslot*);
+int	pcmcistuple(int, int, int, void*, int);
+PCMmap*	pcmmap(int, ulong, int, int);
+int	pcmspecial(char*, ISAConf*);
+void	pcmspecialclose(int);
+void	pcmunmap(int, PCMmap*);
+void	ptcheck(char*);
+void	putcr3(ulong);
+void	putidt(Segdesc*, int);
+void*	pxegetfspart(int, char*, int);
+void	qinit(IOQ*);
+void	readlsconf(void);
+void	sdaddconf(int);
+int	sdboot(int, char*, Boot*);
+void	sdcheck(char*);
+void*	sdgetfspart(int, char*, int);
+int	sdinit(void);
+void	sdinitdev(int, char*);
+void	sdprintdevs(int);
+int	sdsetpart(int, char*);
+void	setvec(int, void (*)(Ureg*, void*), void*);
+int	splhi(void);
+int	spllo(void);
+void	splx(int);
+void	trapinit(void);
+void	trapdisable(void);
+void	trapenable(void);
+void	uartdrain(void);
+void	uartspecial(int, void (*)(int), int (*)(void), int);
+void	uartputs(IOQ*, char*, int);
+ulong	umbmalloc(ulong, int, int);
+void	umbfree(ulong, int);
+ulong	umbrwmalloc(ulong, int, int);
+void	upafree(ulong, int);
+ulong	upamalloc(ulong, int, int);
+void	warp86(char*, ulong);
+void	warp9(ulong);
+int	x86cpuid(int*, int*);
+void*	xspanalloc(ulong, int, ulong);
+
+#define malloc(n)	ialloc(n, 0)
+#define mallocz(n, c)	ialloc(n, 0)
+#define free(v)		while(0)
+
+#define	GSHORT(p)	(((p)[1]<<8)|(p)[0])
+#define	GLONG(p)	((GSHORT(p+2)<<16)|GSHORT(p))
+#define	GLSHORT(p)	(((p)[0]<<8)|(p)[1])
+#define	GLLONG(p)	(((ulong)GLSHORT(p)<<16)|GLSHORT(p+2))
+#define	PLLONG(p,v)	(p)[3]=(v);(p)[2]=(v)>>8;(p)[1]=(v)>>16;(p)[0]=(v)>>24
+
+#define KADDR(a)	((void*)((ulong)(a)|KZERO))
+#define PADDR(a)	((ulong)(a)&~0xF0000000)
+
+#define	HOWMANY(x, y)	(((x)+((y)-1))/(y))
+#define ROUNDUP(x, y)	(HOWMANY((x), (y))*(y))
+
+
+#define xalloc(n)	ialloc(n, 0)
+#define xfree(v)	while(0)
+#define lock(l)		if(l){/* nothing to do */;}else{/* nothing to do */;}
+#define unlock(l)	if(l){/* nothing to do */;}else{/* nothing to do */;}
+
+int	dmacount(int);
+int	dmadone(int);
+void	dmaend(int);
+void	dmainit(int);
+long	dmasetup(int, void*, long, int);
+
+extern int (*_pcmspecial)(char *, ISAConf *);
+extern void (*_pcmspecialclose)(int);
+extern void devi82365link(void);
+extern void devpccardlink(void);
--- /dev/null
+++ b/os/boot/pc/fs.c
@@ -1,0 +1,94 @@
+#include	"u.h"
+#include	"lib.h"
+#include	"mem.h"
+#include	"dat.h"
+#include	"fns.h"
+#include	"io.h"
+
+#include	"fs.h"
+
+/*
+ *  grab next element from a path, return the pointer to unprocessed portion of
+ *  path.
+ */
+char *
+nextelem(char *path, char *elem)
+{
+	int i;
+
+	while(*path == '/')
+		path++;
+	if(*path==0 || *path==' ')
+		return 0;
+	for(i=0; *path!='\0' && *path!='/' && *path!=' '; i++){
+		if(i==NAMELEN){
+			print("name component too long\n");
+			return 0;
+		}
+		*elem++ = *path++;
+	}
+	*elem = '\0';
+	return path;
+}
+
+int
+fswalk(Fs *fs, char *path, File *f)
+{
+	char element[NAMELEN];
+
+	*f = fs->root;
+	if(BADPTR(fs->walk))
+		panic("fswalk bad pointer fs->walk");
+
+	f->path = path;
+	while(path = nextelem(path, element)){
+		switch(fs->walk(f, element)){
+		case -1:
+			return -1;
+		case 0:
+			return 0;
+		}
+	}
+	return 1;
+}
+
+/*
+ *  boot
+ */
+int
+fsboot(Fs *fs, char *path, Boot *b)
+{
+	File file;
+	long n;
+	static char buf[8192];
+
+	switch(fswalk(fs, path, &file)){
+	case -1:
+		print("error walking to %s\n", path);
+		return -1;
+	case 0:
+		print("%s not found\n", path);
+		return -1;
+	case 1:
+		print("found %s\n", path);
+		break;
+	}
+
+	while((n = fsread(&file, buf, sizeof buf)) > 0) {
+		if(bootpass(b, buf, n) != MORE)
+			break;
+	}
+
+	bootpass(b, nil, 0);	/* tries boot */
+	return -1;
+}
+
+int
+fsread(File *file, void *a, long n)
+{
+	if(BADPTR(file->fs))
+		panic("bad pointer file->fs in fsread");
+	if(BADPTR(file->fs->read))
+		panic("bad pointer file->fs->read in fsread");
+	return file->fs->read(file, a, n);
+}
--- /dev/null
+++ b/os/boot/pc/fs.h
@@ -1,0 +1,36 @@
+typedef struct File File;
+typedef struct Fs Fs;
+
+#include "dosfs.h"
+#include "kfs.h"
+
+struct File{
+	union{
+		Dosfile	dos;
+		Kfsfile	kfs;
+		int walked;
+	};
+	Fs	*fs;
+	char	*path;
+};
+
+struct Fs{
+	union {
+		Dos dos;
+		Kfs kfs;
+	};
+	int	dev;				/* device id */
+	long	(*diskread)(Fs*, void*, long);	/* disk read routine */
+	vlong	(*diskseek)(Fs*, vlong);	/* disk seek routine */
+	long	(*read)(File*, void*, long);
+	int	(*walk)(File*, char*);
+	File	root;
+};
+
+extern int chatty;
+extern int dotini(Fs*);
+extern int fswalk(Fs*, char*, File*);
+extern int fsread(File*, void*, long);
+extern int fsboot(Fs*, char*, Boot*);
+
+#define BADPTR(x) ((ulong)x < 0x80000000)
--- /dev/null
+++ b/os/boot/pc/getcallerpc.c
@@ -1,0 +1,8 @@
+#include "u.h"
+#include "lib.h"
+
+ulong
+getcallerpc(void *x)
+{
+	return (((ulong*)(x))[-1]);
+}
--- /dev/null
+++ b/os/boot/pc/ilock.c
@@ -1,0 +1,24 @@
+#include "u.h"
+#include "lib.h"
+#include "mem.h"
+#include "dat.h"
+#include "fns.h"
+#include "io.h"
+
+void
+ilock(Lock *lk)
+{
+	if(lk->locked != 0)
+		panic("ilock");
+	lk->spl = splhi();
+	lk->locked = 1;
+}
+
+void
+iunlock(Lock *lk)
+{
+	if(lk->locked != 1)
+		panic("iunlock");
+	lk->locked = 0;
+	splx(lk->spl);
+}
--- /dev/null
+++ b/os/boot/pc/inflate.c
@@ -1,0 +1,199 @@
+#include "u.h"
+#include "lib.h"
+#include "mem.h"
+#include "dat.h"
+#include "fns.h"
+#include "io.h"
+#include <flate.h>
+
+typedef struct Biobuf	Biobuf;
+
+struct Biobuf
+{
+	uchar *bp;
+	uchar *p;
+	uchar *ep;
+};
+
+static int	header(Biobuf*);
+static int	trailer(Biobuf*, Biobuf*);
+static int	getc(void*);
+static ulong	offset(Biobuf*);
+static int	crcwrite(void *out, void *buf, int n);
+static ulong	get4(Biobuf *b);
+static ulong	Boffset(Biobuf *bp);
+
+/* GZIP flags */
+enum {
+	Ftext=		(1<<0),
+	Fhcrc=		(1<<1),
+	Fextra=		(1<<2),
+	Fname=		(1<<3),
+	Fcomment=	(1<<4),
+
+	GZCRCPOLY	= 0xedb88320UL,
+};
+
+static ulong	*crctab;
+static ulong	crc;
+
+extern void diff(char*);	//XXX
+int
+gunzip(uchar *out, int outn, uchar *in, int inn)
+{
+	Biobuf bin, bout;
+	int err;
+
+	crc = 0;
+	crctab = mkcrctab(GZCRCPOLY);
+	err = inflateinit();
+	if(err != FlateOk)
+		print("inflateinit failed: %s\n", flateerr(err));
+
+	bin.bp = bin.p = in;
+	bin.ep = in+inn;
+	bout.bp = bout.p = out;
+	bout.ep = out+outn;
+
+	err = header(&bin);
+	if(err != FlateOk)
+		return err;
+
+	err = inflate(&bout, crcwrite, &bin, getc);
+	if(err != FlateOk)
+		print("inflate failed: %s\n", flateerr(err));
+
+	err = trailer(&bout, &bin);
+	if(err != FlateOk)
+		return err;
+
+	return Boffset(&bout);
+}
+
+static int
+header(Biobuf *bin)
+{
+	int i, flag;
+
+	if(getc(bin) != 0x1f || getc(bin) != 0x8b){
+		print("bad magic\n");
+		return FlateCorrupted;
+	}
+	if(getc(bin) != 8){
+		print("unknown compression type\n");
+		return FlateCorrupted;
+	}
+	
+	flag = getc(bin);
+	
+	/* mod time */
+	get4(bin);
+	
+	/* extra flags */
+	getc(bin);
+	
+	/* OS type */
+	getc(bin);
+
+	if(flag & Fextra)
+		for(i=getc(bin); i>0; i--)
+			getc(bin);
+	
+	/* name */
+	if(flag&Fname)
+		while(getc(bin) != 0)
+			;
+
+	/* comment */
+	if(flag&Fcomment)
+		while(getc(bin) != 0)
+			;
+
+	/* crc16 */
+	if(flag&Fhcrc) {
+		getc(bin);
+		getc(bin);
+	}
+		
+	return FlateOk;
+}
+
+static int
+trailer(Biobuf *bout, Biobuf *bin)
+{
+	/* crc32 */
+	if(crc != get4(bin)){
+		print("crc mismatch\n");
+		return FlateCorrupted;
+	}
+
+	/* length */
+	if(get4(bin) != Boffset(bout)){
+		print("bad output len\n");
+		return FlateCorrupted;
+	}
+	return FlateOk;
+}
+
+static ulong
+get4(Biobuf *b)
+{
+	ulong v;
+	int i, c;
+
+	v = 0;
+	for(i = 0; i < 4; i++){
+		c = getc(b);
+		v |= c << (i * 8);
+	}
+	return v;
+}
+
+static int
+getc(void *in)
+{
+	Biobuf *bp = in;
+
+//	if((bp->p - bp->bp) % 10000 == 0)
+//		print(".");
+	if(bp->p >= bp->ep)
+		return -1;
+	return *bp->p++;
+}
+
+static ulong
+Boffset(Biobuf *bp)
+{
+	return bp->p - bp->bp;
+}
+
+static int
+crcwrite(void *out, void *buf, int n)
+{
+	Biobuf *bp;
+	int nn;
+
+	crc = blockcrc(crctab, crc, buf, n);
+	bp = out;
+	nn = n;
+	if(nn > bp->ep-bp->p)
+		nn = bp->ep-bp->p;
+	if(nn > 0)
+		memmove(bp->p, buf, nn);
+	bp->p += n;
+	return n;
+}
+
+#undef malloc
+#undef free
+
+void *
+malloc(ulong n)
+{
+	return ialloc(n, 8);
+}
+
+void
+free(void *)
+{
+}
--- /dev/null
+++ b/os/boot/pc/io.h
@@ -1,0 +1,317 @@
+/*
+ *  programmable interrupt vectors (for the 8259's)
+ */
+enum
+{
+	Bptvec=		3,		/* breakpoints */
+	Mathemuvec=	7,		/* math coprocessor emulation interrupt */
+	Mathovervec=	9,		/* math coprocessor overrun interrupt */
+	Matherr1vec=	16,		/* math coprocessor error interrupt */
+	Faultvec=	14,		/* page fault */
+
+	Syscallvec=	64,
+
+	VectorPIC	= 24,		/* external [A]PIC interrupts */
+	VectorCLOCK	= VectorPIC+0,
+	VectorKBD	= VectorPIC+1,
+	VectorUART1	= VectorPIC+3,
+	VectorUART0	= VectorPIC+4,
+	VectorPCMCIA	= VectorPIC+5,
+	VectorFLOPPY	= VectorPIC+6,
+	VectorLPT	= VectorPIC+7,
+	VectorIRQ7	= VectorPIC+7,
+	VectorAUX	= VectorPIC+12,	/* PS/2 port */
+	VectorIRQ13	= VectorPIC+13,	/* coprocessor on x386 */
+	VectorATA0	= VectorPIC+14,
+	VectorATA1	= VectorPIC+15,
+	MaxVectorPIC	= VectorPIC+15,
+};
+
+enum {
+	BusCBUS		= 0,		/* Corollary CBUS */
+	BusCBUSII,			/* Corollary CBUS II */
+	BusEISA,			/* Extended ISA */
+	BusFUTURE,			/* IEEE Futurebus */
+	BusINTERN,			/* Internal bus */
+	BusISA,				/* Industry Standard Architecture */
+	BusMBI,				/* Multibus I */
+	BusMBII,			/* Multibus II */
+	BusMCA,				/* Micro Channel Architecture */
+	BusMPI,				/* MPI */
+	BusMPSA,			/* MPSA */
+	BusNUBUS,			/* Apple Macintosh NuBus */
+	BusPCI,				/* Peripheral Component Interconnect */
+	BusPCMCIA,			/* PC Memory Card International Association */
+	BusTC,				/* DEC TurboChannel */
+	BusVL,				/* VESA Local bus */
+	BusVME,				/* VMEbus */
+	BusXPRESS,			/* Express System Bus */
+};
+
+#define MKBUS(t,b,d,f)	(((t)<<24)|(((b)&0xFF)<<16)|(((d)&0x1F)<<11)|(((f)&0x07)<<8))
+#define BUSFNO(tbdf)	(((tbdf)>>8)&0x07)
+#define BUSDNO(tbdf)	(((tbdf)>>11)&0x1F)
+#define BUSBNO(tbdf)	(((tbdf)>>16)&0xFF)
+#define BUSTYPE(tbdf)	((tbdf)>>24)
+#define BUSBDF(tbdf)	((tbdf)&0x00FFFF00)
+#define BUSUNKNOWN	(-1)
+
+enum {
+	MaxEISA		= 16,
+	CfgEISA		= 0xC80,
+};
+
+/*
+ * PCI support code.
+ */
+enum {					/* type 0 and type 1 pre-defined header */
+	PciVID		= 0x00,		/* vendor ID */
+	PciDID		= 0x02,		/* device ID */
+	PciPCR		= 0x04,		/* command */
+	PciPSR		= 0x06,		/* status */
+	PciRID		= 0x08,		/* revision ID */
+	PciCCRp		= 0x09,		/* programming interface class code */
+	PciCCRu		= 0x0A,		/* sub-class code */
+	PciCCRb		= 0x0B,		/* base class code */
+	PciCLS		= 0x0C,		/* cache line size */
+	PciLTR		= 0x0D,		/* latency timer */
+	PciHDT		= 0x0E,		/* header type */
+	PciBST		= 0x0F,		/* BIST */
+
+	PciBAR0		= 0x10,		/* base address */
+	PciBAR1		= 0x14,
+
+	PciINTL		= 0x3C,		/* interrupt line */
+	PciINTP		= 0x3D,		/* interrupt pin */
+};
+
+/* ccrb (base class code) values; controller types */
+enum {
+	Pcibcpci1	= 0,		/* pci 1.0; no class codes defined */
+	Pcibcstore	= 1,		/* mass storage */
+	Pcibcnet	= 2,		/* network */
+	Pcibcdisp	= 3,		/* display */
+	Pcibcmmedia	= 4,		/* multimedia */
+	Pcibcmem	= 5,		/* memory */
+	Pcibcbridge	= 6,		/* bridge */
+	Pcibccomm	= 7,		/* simple comms (e.g., serial) */
+	Pcibcbasesys	= 8,		/* base system */
+	Pcibcinput	= 9,		/* input */
+	Pcibcdock	= 0xa,		/* docking stations */
+	Pcibcproc	= 0xb,		/* processors */
+	Pcibcserial	= 0xc,		/* serial bus (e.g., USB) */
+	Pcibcwireless	= 0xd,		/* wireless */
+	Pcibcintell	= 0xe,		/* intelligent i/o */
+	Pcibcsatcom	= 0xf,		/* satellite comms */
+	Pcibccrypto	= 0x10,		/* encryption/decryption */
+	Pcibcdacq	= 0x11,		/* data acquisition & signal proc. */
+};
+
+/* ccru (sub-class code) values; common cases only */
+enum {
+	/* mass storage */
+	Pciscscsi	= 0,		/* SCSI */
+	Pciscide	= 1,		/* IDE (ATA) */
+
+	/* network */
+	Pciscether	= 0,		/* Ethernet */
+
+	/* display */
+	Pciscvga	= 0,		/* VGA */
+	Pciscxga	= 1,		/* XGA */
+	Pcisc3d		= 2,		/* 3D */
+
+	/* bridges */
+	Pcischostpci	= 0,		/* host/pci */
+	Pciscpcicpci	= 1,		/* pci/pci */
+
+	/* simple comms */
+	Pciscserial	= 0,		/* 16450, etc. */
+	Pciscmultiser	= 1,		/* multiport serial */
+
+	/* serial bus */
+	Pciscusb	= 3,		/* USB */
+};
+
+enum {					/* type 0 pre-defined header */
+	PciBAR2		= 0x18,
+	PciBAR3		= 0x1C,
+	PciBAR4		= 0x20,
+	PciBAR5		= 0x24,
+	PciCIS		= 0x28,		/* cardbus CIS pointer */
+	PciSVID		= 0x2C,		/* subsystem vendor ID */
+	PciSID		= 0x2E,		/* cardbus CIS pointer */
+	PciEBAR0	= 0x30,		/* expansion ROM base address */
+	PciMGNT		= 0x3E,		/* burst period length */
+	PciMLT		= 0x3F,		/* maximum latency between bursts */
+};
+
+enum {					/* type 1 pre-defined header */
+	PciPBN		= 0x18,		/* primary bus number */
+	PciSBN		= 0x19,		/* secondary bus number */
+	PciUBN		= 0x1A,		/* subordinate bus number */
+	PciSLTR		= 0x1B,		/* secondary latency timer */
+	PciIBR		= 0x1C,		/* I/O base */
+	PciILR		= 0x1D,		/* I/O limit */
+	PciSPSR		= 0x1E,		/* secondary status */
+	PciMBR		= 0x20,		/* memory base */
+	PciMLR		= 0x22,		/* memory limit */
+	PciPMBR		= 0x24,		/* prefetchable memory base */
+	PciPMLR		= 0x26,		/* prefetchable memory limit */
+	PciPUBR		= 0x28,		/* prefetchable base upper 32 bits */
+	PciPULR		= 0x2C,		/* prefetchable limit upper 32 bits */
+	PciIUBR		= 0x30,		/* I/O base upper 16 bits */
+	PciIULR		= 0x32,		/* I/O limit upper 16 bits */
+	PciEBAR1	= 0x28,		/* expansion ROM base address */
+	PciBCR		= 0x3E,		/* bridge control register */
+};
+
+enum {					/* type 2 pre-defined header */
+	PciCBExCA	= 0x10,
+	PciCBSPSR	= 0x16,
+	PciCBPBN	= 0x18,		/* primary bus number */
+	PciCBSBN	= 0x19,		/* secondary bus number */
+	PciCBUBN	= 0x1A,		/* subordinate bus number */
+	PciCBSLTR	= 0x1B,		/* secondary latency timer */
+	PciCBMBR0	= 0x1C,
+	PciCBMLR0	= 0x20,
+	PciCBMBR1	= 0x24,
+	PciCBMLR1	= 0x28,
+	PciCBIBR0	= 0x2C,		/* I/O base */
+	PciCBILR0	= 0x30,		/* I/O limit */
+	PciCBIBR1	= 0x34,		/* I/O base */
+	PciCBILR1	= 0x38,		/* I/O limit */
+	PciCBBCTL	= 0x3E,		/* Bridge control */
+	PciCBSVID	= 0x40,		/* subsystem vendor ID */
+	PciCBSID	= 0x42,		/* subsystem ID */
+	PciCBLMBAR	= 0x44,		/* legacy mode base address */
+};
+
+typedef struct Pcisiz Pcisiz;
+struct Pcisiz
+{
+	Pcidev*	dev;
+	int	siz;
+	int	bar;
+};
+
+typedef struct Pcidev Pcidev;
+typedef struct Pcidev {
+	int	tbdf;			/* type+bus+device+function */
+	ushort	vid;			/* vendor ID */
+	ushort	did;			/* device ID */
+
+	ushort	pcr;
+
+	uchar	rid;
+	uchar	ccrp;
+	uchar	ccru;
+	uchar	ccrb;
+	uchar	cls;
+	uchar	ltr;
+
+	struct {
+		ulong	bar;		/* base address */
+		int	size;
+	} mem[6];
+
+	struct {
+		ulong	bar;
+		int	size;
+	} rom;
+	uchar	intl;			/* interrupt line */
+
+	Pcidev*	list;
+	Pcidev*	link;			/* next device on this bno */
+
+	Pcidev*	bridge;			/* down a bus */
+	struct {
+		ulong	bar;
+		int	size;
+	} ioa, mema;
+
+	int	pmrb;			/* power management register block */
+};
+
+#define PCIWINDOW	0
+#define PCIWADDR(va)	(PADDR(va)+PCIWINDOW)
+#define ISAWINDOW	0
+#define ISAWADDR(va)	(PADDR(va)+ISAWINDOW)
+
+/*
+ * PCMCIA support code.
+ */
+typedef struct PCMslot		PCMslot;
+typedef struct PCMconftab	PCMconftab;
+
+/*
+ * Map between ISA memory space and PCMCIA card memory space.
+ */
+struct PCMmap {
+	ulong	ca;			/* card address */
+	ulong	cea;			/* card end address */
+	ulong	isa;			/* ISA address */
+	int	len;			/* length of the ISA area */
+	int	attr;			/* attribute memory */
+	int	ref;
+};
+
+/* configuration table entry */
+struct PCMconftab
+{
+	int	index;
+	ushort	irqs;		/* legal irqs */
+	uchar	irqtype;
+	uchar	bit16;		/* true for 16 bit access */
+	struct {
+		ulong	start;
+		ulong	len;
+	} io[16];
+	int	nio;
+	uchar	vpp1;
+	uchar	vpp2;
+	uchar	memwait;
+	ulong	maxwait;
+	ulong	readywait;
+	ulong	otherwait;
+};
+
+/* a card slot */
+struct PCMslot
+{
+	Lock;
+	int	ref;
+
+	void	*cp;		/* controller for this slot */
+	long	memlen;		/* memory length */
+	uchar	base;		/* index register base */
+	uchar	slotno;		/* slot number */
+
+	/* status */
+	uchar	special;	/* in use for a special device */
+	uchar	already;	/* already inited */
+	uchar	occupied;
+	uchar	battery;
+	uchar	wrprot;
+	uchar	powered;
+	uchar	configed;
+	uchar	enabled;
+	uchar	busy;
+
+	/* cis info */
+	ulong	msec;		/* time of last slotinfo call */
+	char	verstr[512];	/* version string */
+	int	ncfg;		/* number of configurations */
+	struct {
+		ushort	cpresent;	/* config registers present */
+		ulong	caddr;		/* relative address of config registers */
+	} cfg[8];
+	int	nctab;		/* number of config table entries */
+	PCMconftab	ctab[8];
+	PCMconftab	*def;	/* default conftab */
+
+	/* memory maps */
+	Lock	mlock;		/* lock down the maps */
+	int	time;
+	PCMmap	mmap[4];	/* maps, last is always for the kernel */
+};
--- /dev/null
+++ b/os/boot/pc/ip.h
@@ -1,0 +1,100 @@
+typedef struct Udphdr Udphdr;
+struct Udphdr
+{
+	uchar	d[6];		/* Ethernet destination */
+	uchar	s[6];		/* Ethernet source */
+	uchar	type[2];	/* Ethernet packet type */
+
+	uchar	vihl;		/* Version and header length */
+	uchar	tos;		/* Type of service */
+	uchar	length[2];	/* packet length */
+	uchar	id[2];		/* Identification */
+	uchar	frag[2];	/* Fragment information */
+
+	/* Udp pseudo ip really starts here */
+	uchar	ttl;	
+	uchar	udpproto;	/* Protocol */
+	uchar	udpplen[2];	/* Header plus data length */
+	uchar	udpsrc[4];	/* Ip source */
+	uchar	udpdst[4];	/* Ip destination */
+	uchar	udpsport[2];	/* Source port */
+	uchar	udpdport[2];	/* Destination port */
+	uchar	udplen[2];	/* data length */
+	uchar	udpcksum[2];	/* Checksum */
+};
+
+typedef struct Etherhdr Etherhdr;
+struct Etherhdr
+{
+	uchar	d[6];
+	uchar	s[6];
+	uchar	type[2];
+
+	/* Now we have the ip fields */
+	uchar	vihl;		/* Version and header length */
+	uchar	tos;		/* Type of service */
+	uchar	length[2];	/* packet length */
+	uchar	id[2];		/* Identification */
+	uchar	frag[2];	/* Fragment information */
+	uchar	ttl;		/* Time to live */
+	uchar	proto;		/* Protocol */
+	uchar	cksum[2];	/* Header checksum */
+	uchar	src[4];		/* Ip source */
+	uchar	dst[4];		/* Ip destination */
+};
+
+enum
+{
+	IP_VER		= 0x40,
+	IP_HLEN		= 0x05,			
+ 	UDP_EHSIZE	= 22,
+	UDP_PHDRSIZE	= 12,
+	UDP_HDRSIZE	= 20,
+	ETHER_HDR	= 14,
+	IP_UDPPROTO	= 17,
+	ET_IP		= 0x800,
+	Bcastip		= 0xffffffff,
+	BPportsrc	= 68,
+	BPportdst	= 67,
+	TFTPport	= 69,
+	Timeout		= 5000,	/* milliseconds */
+	Bootrequest 	= 1,
+	Bootreply   	= 2,
+	Tftp_READ	= 1,
+	Tftp_WRITE	= 2,
+	Tftp_DATA	= 3,
+	Tftp_ACK	= 4,
+	Tftp_ERROR	= 5,
+	Segsize		= 512,
+	TFTPSZ		= Segsize+10,
+};
+
+typedef struct Bootp Bootp;
+struct Bootp
+{
+	uchar	op;		/* opcode */
+	uchar	htype;		/* hardware type */
+	uchar	hlen;		/* hardware address len */
+	uchar	hops;		/* hops */
+	uchar	xid[4];		/* a random number */
+	uchar	secs[2];	/* elapsed since client started booting */
+	uchar	pad[2];
+	uchar	ciaddr[4];	/* client IP address (client tells server) */
+	uchar	yiaddr[4];	/* client IP address (server tells client) */
+	uchar	siaddr[4];	/* server IP address */
+	uchar	giaddr[4];	/* gateway IP address */
+	uchar	chaddr[16];	/* client hardware address */
+	char	sname[64];	/* server host name (optional) */
+	char	file[128];	/* boot file name */
+	char	vend[128];	/* vendor-specific goo */
+};
+
+typedef struct Netaddr Netaddr;
+struct Netaddr
+{
+	ulong	ip;
+	ushort	port;
+	char	ea[Eaddrlen];
+};
+
+extern int	eipfmt(Fmt*);
--- /dev/null
+++ b/os/boot/pc/kbd.c
@@ -1,0 +1,489 @@
+#include	"u.h"
+#include	"lib.h"
+#include	"mem.h"
+#include	"dat.h"
+#include	"fns.h"
+#include	"io.h"
+
+enum {
+	Data=		0x60,	/* data port */
+
+	Status=		0x64,	/* status port */
+	 Inready=	0x01,	/*  input character ready */
+	 Outbusy=	0x02,	/*  output busy */
+	 Sysflag=	0x04,	/*  system flag */
+	 Cmddata=	0x08,	/*  cmd==0, data==1 */
+	 Inhibit=	0x10,	/*  keyboard/mouse inhibited */
+	 Minready=	0x20,	/*  mouse character ready */
+	 Rtimeout=	0x40,	/*  general timeout */
+	 Parity=	0x80,
+
+	Cmd=		0x64,	/* command port (write only) */
+ 
+	Spec=	0x80,
+
+	PF=	Spec|0x20,	/* num pad function key */
+	View=	Spec|0x00,	/* view (shift window up) */
+	KF=	Spec|0x40,	/* function key */
+	Shift=	Spec|0x60,
+	Break=	Spec|0x61,
+	Ctrl=	Spec|0x62,
+	Latin=	Spec|0x63,
+	Caps=	Spec|0x64,
+	Num=	Spec|0x65,
+	No=	Spec|0x7F,	/* no mapping */
+
+	Home=	KF|13,
+	Up=	KF|14,
+	Pgup=	KF|15,
+	Print=	KF|16,
+	Left=	View,
+	Right=	View,
+	End=	'\r',
+	Down=	View,
+	Pgdown=	View,
+	Ins=	KF|20,
+	Del=	0x7F,
+};
+
+uchar kbtab[] = 
+{
+[0x00]	No,	0x1b,	'1',	'2',	'3',	'4',	'5',	'6',
+[0x08]	'7',	'8',	'9',	'0',	'-',	'=',	'\b',	'\t',
+[0x10]	'q',	'w',	'e',	'r',	't',	'y',	'u',	'i',
+[0x18]	'o',	'p',	'[',	']',	'\n',	Ctrl,	'a',	's',
+[0x20]	'd',	'f',	'g',	'h',	'j',	'k',	'l',	';',
+[0x28]	'\'',	'`',	Shift,	'\\',	'z',	'x',	'c',	'v',
+[0x30]	'b',	'n',	'm',	',',	'.',	'/',	Shift,	No,
+[0x38]	Latin,	' ',	Caps,	KF|1,	KF|2,	KF|3,	KF|4,	KF|5,
+[0x40]	KF|6,	KF|7,	KF|8,	KF|9,	KF|10,	Num,	KF|12,	Home,
+[0x48]	No,	No,	No,	No,	No,	No,	No,	No,
+[0x50]	No,	No,	No,	No,	No,	No,	No,	KF|11,
+[0x58]	KF|12,	No,	No,	No,	No,	No,	No,	No,
+};
+
+uchar kbtabshift[] =
+{
+[0x00]	No,	0x1b,	'!',	'@',	'#',	'$',	'%',	'^',
+[0x08]	'&',	'*',	'(',	')',	'_',	'+',	'\b',	'\t',
+[0x10]	'Q',	'W',	'E',	'R',	'T',	'Y',	'U',	'I',
+[0x18]	'O',	'P',	'{',	'}',	'\n',	Ctrl,	'A',	'S',
+[0x20]	'D',	'F',	'G',	'H',	'J',	'K',	'L',	':',
+[0x28]	'"',	'~',	Shift,	'|',	'Z',	'X',	'C',	'V',
+[0x30]	'B',	'N',	'M',	'<',	'>',	'?',	Shift,	No,
+[0x38]	Latin,	' ',	Caps,	KF|1,	KF|2,	KF|3,	KF|4,	KF|5,
+[0x40]	KF|6,	KF|7,	KF|8,	KF|9,	KF|10,	Num,	KF|12,	Home,
+[0x48]	No,	No,	No,	No,	No,	No,	No,	No,
+[0x50]	No,	No,	No,	No,	No,	No,	No,	KF|11,
+[0x58]	KF|12,	No,	No,	No,	No,	No,	No,	No,
+};
+
+uchar kbtabesc1[] =
+{
+[0x00]	No,	No,	No,	No,	No,	No,	No,	No,
+[0x08]	No,	No,	No,	No,	No,	No,	No,	No,
+[0x10]	No,	No,	No,	No,	No,	No,	No,	No,
+[0x18]	No,	No,	No,	No,	No,	Ctrl,	No,	No,
+[0x20]	No,	No,	No,	No,	No,	No,	No,	No,
+[0x28]	No,	No,	No,	No,	No,	No,	No,	No,
+[0x30]	No,	No,	No,	No,	No,	No,	No,	Print,
+[0x38]	Latin,	No,	No,	No,	No,	No,	No,	No,
+[0x40]	No,	No,	No,	No,	No,	No,	Break,	Home,
+[0x48]	Up,	Pgup,	No,	Down,	No,	Right,	No,	End,
+[0x50]	Left,	Pgdown,	Ins,	Del,	No,	No,	No,	No,
+[0x58]	No,	No,	No,	No,	No,	No,	No,	No,
+};
+
+struct latin
+{
+	uchar	l;
+	char	c[2];
+}latintab[] = {
+	L'¡',	"!!",	/* spanish initial ! */
+	L'¢',	"c|",	/* cent */
+	L'¢',	"c$",	/* cent */
+	L'£',	"l$",	/* pound sterling */
+	L'¤',	"g$",	/* general currency */
+	L'¥',	"y$",	/* yen */
+	L'¥',	"j$",	/* yen */
+	L'¦',	"||",	/* broken vertical bar */
+	L'§',	"SS",	/* section symbol */
+	L'¨',	"\"\"",	/* dieresis */
+	L'©',	"cr",	/* copyright */
+	L'©',	"cO",	/* copyright */
+	L'ª',	"sa",	/* super a, feminine ordinal */
+	L'«',	"<<",	/* left angle quotation */
+	L'¬',	"no",	/* not sign, hooked overbar */
+	L'­',	"--",	/* soft hyphen */
+	L'®',	"rg",	/* registered trademark */
+	L'¯',	"__",	/* macron */
+	L'°',	"s0",	/* degree (sup o) */
+	L'±',	"+-",	/* plus-minus */
+	L'²',	"s2",	/* sup 2 */
+	L'³',	"s3",	/* sup 3 */
+	L'´',	"''",	/* grave accent */
+	L'µ',	"mu",	/* mu */
+	L'¶',	"pg",	/* paragraph (pilcrow) */
+	L'·',	"..",	/* centered . */
+	L'¸',	",,",	/* cedilla */
+	L'¹',	"s1",	/* sup 1 */
+	L'º',	"so",	/* sup o */
+	L'»',	">>",	/* right angle quotation */
+	L'¼',	"14",	/* 1/4 */
+	L'½',	"12",	/* 1/2 */
+	L'¾',	"34",	/* 3/4 */
+	L'¿',	"??",	/* spanish initial ? */
+	L'À',	"A`",	/* A grave */
+	L'Á',	"A'",	/* A acute */
+	L'Â',	"A^",	/* A circumflex */
+	L'Ã',	"A~",	/* A tilde */
+	L'Ä',	"A\"",	/* A dieresis */
+	L'Ä',	"A:",	/* A dieresis */
+	L'Å',	"Ao",	/* A circle */
+	L'Å',	"AO",	/* A circle */
+	L'Æ',	"Ae",	/* AE ligature */
+	L'Æ',	"AE",	/* AE ligature */
+	L'Ç',	"C,",	/* C cedilla */
+	L'È',	"E`",	/* E grave */
+	L'É',	"E'",	/* E acute */
+	L'Ê',	"E^",	/* E circumflex */
+	L'Ë',	"E\"",	/* E dieresis */
+	L'Ë',	"E:",	/* E dieresis */
+	L'Ì',	"I`",	/* I grave */
+	L'Í',	"I'",	/* I acute */
+	L'Î',	"I^",	/* I circumflex */
+	L'Ï',	"I\"",	/* I dieresis */
+	L'Ï',	"I:",	/* I dieresis */
+	L'Ð',	"D-",	/* Eth */
+	L'Ñ',	"N~",	/* N tilde */
+	L'Ò',	"O`",	/* O grave */
+	L'Ó',	"O'",	/* O acute */
+	L'Ô',	"O^",	/* O circumflex */
+	L'Õ',	"O~",	/* O tilde */
+	L'Ö',	"O\"",	/* O dieresis */
+	L'Ö',	"O:",	/* O dieresis */
+	L'Ö',	"OE",	/* O dieresis */
+	L'Ö',	"Oe",	/* O dieresis */
+	L'×',	"xx",	/* times sign */
+	L'Ø',	"O/",	/* O slash */
+	L'Ù',	"U`",	/* U grave */
+	L'Ú',	"U'",	/* U acute */
+	L'Û',	"U^",	/* U circumflex */
+	L'Ü',	"U\"",	/* U dieresis */
+	L'Ü',	"U:",	/* U dieresis */
+	L'Ü',	"UE",	/* U dieresis */
+	L'Ü',	"Ue",	/* U dieresis */
+	L'Ý',	"Y'",	/* Y acute */
+	L'Þ',	"P|",	/* Thorn */
+	L'Þ',	"Th",	/* Thorn */
+	L'Þ',	"TH",	/* Thorn */
+	L'ß',	"ss",	/* sharp s */
+	L'à',	"a`",	/* a grave */
+	L'á',	"a'",	/* a acute */
+	L'â',	"a^",	/* a circumflex */
+	L'ã',	"a~",	/* a tilde */
+	L'ä',	"a\"",	/* a dieresis */
+	L'ä',	"a:",	/* a dieresis */
+	L'å',	"ao",	/* a circle */
+	L'æ',	"ae",	/* ae ligature */
+	L'ç',	"c,",	/* c cedilla */
+	L'è',	"e`",	/* e grave */
+	L'é',	"e'",	/* e acute */
+	L'ê',	"e^",	/* e circumflex */
+	L'ë',	"e\"",	/* e dieresis */
+	L'ë',	"e:",	/* e dieresis */
+	L'ì',	"i`",	/* i grave */
+	L'í',	"i'",	/* i acute */
+	L'î',	"i^",	/* i circumflex */
+	L'ï',	"i\"",	/* i dieresis */
+	L'ï',	"i:",	/* i dieresis */
+	L'ð',	"d-",	/* eth */
+	L'ñ',	"n~",	/* n tilde */
+	L'ò',	"o`",	/* o grave */
+	L'ó',	"o'",	/* o acute */
+	L'ô',	"o^",	/* o circumflex */
+	L'õ',	"o~",	/* o tilde */
+	L'ö',	"o\"",	/* o dieresis */
+	L'ö',	"o:",	/* o dieresis */
+	L'ö',	"oe",	/* o dieresis */
+	L'÷',	"-:",	/* divide sign */
+	L'ø',	"o/",	/* o slash */
+	L'ù',	"u`",	/* u grave */
+	L'ú',	"u'",	/* u acute */
+	L'û',	"u^",	/* u circumflex */
+	L'ü',	"u\"",	/* u dieresis */
+	L'ü',	"u:",	/* u dieresis */
+	L'ü',	"ue",	/* u dieresis */
+	L'ý',	"y'",	/* y acute */
+	L'þ',	"th",	/* thorn */
+	L'þ',	"p|",	/* thorn */
+	L'ÿ',	"y\"",	/* y dieresis */
+	L'ÿ',	"y:",	/* y dieresis */
+	0,	0,
+};
+
+enum
+{
+	/* controller command byte */
+	Cscs1=		(1<<6),		/* scan code set 1 */
+	Cmousedis=	(1<<5),		/* mouse disable */
+	Ckbddis=	(1<<4),		/* kbd disable */
+	Csf=		(1<<2),		/* system flag */
+	Cmouseint=	(1<<1),		/* mouse interrupt enable */
+	Ckbdint=	(1<<0),		/* kbd interrupt enable */
+};
+
+static uchar ccc;
+
+int
+latin1(int k1, int k2)
+{
+	struct latin *l;
+
+	for(l=latintab; l->l; l++)
+		if(k1==l->c[0] && k2==l->c[1])
+			return l->l;
+	return 0;
+}
+
+/*
+ *  wait for output no longer busy
+ */
+static int
+outready(void)
+{
+	int tries;
+
+	for(tries = 0; (inb(Status) & Outbusy); tries++){
+		if(tries > 500)
+			return -1;
+		delay(2);
+	}
+	return 0;
+}
+
+/*
+ *  wait for input
+ */
+static int
+inready(void)
+{
+	int tries;
+
+	for(tries = 0; !(inb(Status) & Inready); tries++){
+		if(tries > 500)
+			return -1;
+		delay(2);
+	}
+	return 0;
+}
+
+/*
+ *  ask 8042 to enable the use of address bit 20
+ */
+void
+i8042a20(void)
+{
+	outready();
+	outb(Cmd, 0xD1);
+	outready();
+	outb(Data, 0xDF);
+	outready();
+}
+
+/*
+ *  ask 8042 to reset the machine
+ */
+void
+i8042reset(void)
+{
+	int i, x;
+#ifdef notdef
+	ushort *s = (ushort*)(KZERO|0x472);
+
+	*s = 0x1234;		/* BIOS warm-boot flag */
+#endif /* notdef */
+
+	outready();
+	outb(Cmd, 0xFE);	/* pulse reset line (means resend on AT&T machines) */
+	outready();
+
+	/*
+	 *  Pulse it by hand (old somewhat reliable)
+	 */
+	x = 0xDF;
+	for(i = 0; i < 5; i++){
+		x ^= 1;
+		outready();
+		outb(Cmd, 0xD1);
+		outready();
+		outb(Data, x);	/* toggle reset */
+		delay(100);
+	}
+}
+
+/*
+ *  keyboard interrupt
+ */
+static void
+i8042intr(Ureg*, void*)
+{
+	int s, c;
+	static int esc1, esc2;
+	static int alt, caps, ctl, num, shift;
+	static int lstate, k1, k2;
+	int keyup;
+
+	/*
+	 *  get status
+	 */
+	s = inb(Status);
+	if(!(s&Inready))
+		return;
+
+	/*
+	 *  get the character
+	 */
+	c = inb(Data);
+
+	/*
+	 *  if it's the aux port...
+	 */
+	if(s & Minready)
+		return;
+
+	/*
+	 *  e0's is the first of a 2 character sequence
+	 */
+	if(c == 0xe0){
+		esc1 = 1;
+		return;
+	} else if(c == 0xe1){
+		esc2 = 2;
+		return;
+	}
+
+	keyup = c&0x80;
+	c &= 0x7f;
+	if(c > sizeof kbtab){
+		c |= keyup;
+		if(c != 0xFF)	/* these come fairly often: CAPSLOCK U Y */
+			print("unknown key %ux\n", c);
+		return;
+	}
+
+	if(esc1){
+		c = kbtabesc1[c];
+		esc1 = 0;
+	} else if(esc2){
+		esc2--;
+		return;
+	} else if(shift)
+		c = kbtabshift[c];
+	else
+		c = kbtab[c];
+
+	if(caps && c<='z' && c>='a')
+		c += 'A' - 'a';
+
+	/*
+	 *  keyup only important for shifts
+	 */
+	if(keyup){
+		switch(c){
+		case Latin:
+			alt = 0;
+			break;
+		case Shift:
+			shift = 0;
+			break;
+		case Ctrl:
+			ctl = 0;
+			break;
+		}
+		return;
+	}
+
+	/*
+ 	 *  normal character
+	 */
+	if(!(c & Spec)){
+		if(ctl){
+			if(alt && c == Del)
+				warp86("\nCtrl-Alt-Del\n", 0);
+			c &= 0x1f;
+		}
+		switch(lstate){
+		case 1:
+			k1 = c;
+			lstate = 2;
+			return;
+		case 2:
+			k2 = c;
+			lstate = 0;
+			c = latin1(k1, k2);
+			if(c == 0){
+				kbdchar(k1);
+				c = k2;
+			}
+			/* fall through */
+		default:
+			break;
+		}
+	} else {
+		switch(c){
+		case Caps:
+			caps ^= 1;
+			return;
+		case Num:
+			num ^= 1;
+			return;
+		case Shift:
+			shift = 1;
+			return;
+		case Latin:
+			alt = 1;
+			lstate = 1;
+			return;
+		case Ctrl:
+			ctl = 1;
+			return;
+		}
+	}
+	kbdchar(c);
+}
+
+static char *initfailed = "kbd init failed\n";
+
+void
+i8042init(void)
+{
+	int c;
+
+	/* wait for a quiescent controller */
+	while((c = inb(Status)) & (Outbusy | Inready))
+		if(c & Inready)
+			inb(Data);
+
+	/* get current controller command byte */
+	outb(Cmd, 0x20);
+	if(inready() < 0){
+		print("kbdinit: can't read ccc\n");
+		ccc = 0;
+	} else
+		ccc = inb(Data);
+
+	/* enable kbd xfers and interrupts */
+	ccc &= ~Ckbddis;
+	ccc |= Csf | Ckbdint | Cscs1;
+	if(outready() < 0)
+		print(initfailed);
+	outb(Cmd, 0x60);
+	if(outready() < 0)
+		print(initfailed);
+	outb(Data, ccc);
+	if(outready() < 0)
+		print(initfailed);
+
+	setvec(VectorKBD, i8042intr, 0);
+}
--- /dev/null
+++ b/os/boot/pc/kfs.h
@@ -1,0 +1,57 @@
+typedef struct Qid9p1 Qid9p1;
+typedef struct Dentry Dentry;
+typedef struct Kfsfile Kfsfile;
+typedef struct Kfs Kfs;
+
+/* DONT TOUCH, this is the disk structure */
+struct	Qid9p1
+{
+	long	path;
+	long	version;
+};
+
+//#define	NAMELEN		28		/* size of names */
+#define	NDBLOCK		6		/* number of direct blocks in Dentry */
+
+/* DONT TOUCH, this is the disk structure */
+struct	Dentry
+{
+	char	name[NAMELEN];
+	short	uid;
+	short	gid;
+	ushort	mode;
+/*
+		#define	DALLOC	0x8000
+		#define	DDIR	0x4000
+		#define	DAPND	0x2000
+		#define	DLOCK	0x1000
+		#define	DREAD	0x4
+		#define	DWRITE	0x2
+		#define	DEXEC	0x1
+*/
+	Qid9p1	qid;
+	long	size;
+	long	dblock[NDBLOCK];
+	long	iblock;
+	long	diblock;
+	long	atime;
+	long	mtime;
+};
+
+struct Kfsfile
+{
+	Dentry;
+	long off;
+};
+
+struct Kfs
+{
+	int	RBUFSIZE;
+	int	BUFSIZE;
+	int	DIRPERBUF;
+	int	INDPERBUF;
+	int	INDPERBUF2;
+};
+
+extern int kfsinit(Fs*);
+
--- /dev/null
+++ b/os/boot/pc/kfsboot.c
@@ -1,0 +1,256 @@
+#include	"u.h"
+#include	"lib.h"
+#include	"mem.h"
+#include	"dat.h"
+#include	"fns.h"
+#include	"fs.h"
+
+typedef struct Tag Tag;
+
+/*
+ * tags on block
+ */
+enum
+{
+	Tnone		= 0,
+	Tsuper,			/* the super block */
+	Tdir,			/* directory contents */
+	Tind1,			/* points to blocks */
+	Tind2,			/* points to Tind1 */
+	Tfile,			/* file contents */
+	Tfree,			/* in free list */
+	Tbuck,			/* cache fs bucket */
+	Tvirgo,			/* fake worm virgin bits */
+	Tcache,			/* cw cache things */
+	MAXTAG
+};
+
+#define	QPDIR	0x80000000L
+#define	QPNONE	0
+#define	QPROOT	1
+#define	QPSUPER	2
+
+/* DONT TOUCH, this is the disk structure */
+struct	Tag
+{
+	short	pad;
+	short	tag;
+	long	path;
+};
+
+static int thisblock = -1;
+static Fs *thisfs;
+static uchar *block;
+
+/*
+ * we end up reading 2x or 3x the number of blocks we need to read.
+ * this is okay because we need to read so few.  if it wasn't okay, we could
+ * have getblock return a pointer to a block, and keep a cache of the last
+ * three read blocks.  that would get us down to the minimum.
+ * but this is fine.
+ */
+static int
+getblock(Fs *fs, ulong n)
+{
+	if(!block)
+		block = malloc(16384);
+
+	if(thisblock == n && thisfs == fs)
+		return 0;
+	thisblock = -1;
+	if(fs->diskseek(fs, (vlong)n*fs->kfs.RBUFSIZE) < 0)
+		return -1;
+	if(fs->diskread(fs, block, fs->kfs.RBUFSIZE) != fs->kfs.RBUFSIZE)
+		return -1;
+	thisblock = n;
+	thisfs = fs;
+
+	return 1;
+}
+
+static int
+checktag(Fs *fs, uchar *block, int tag, long qpath)
+{
+	Tag *t;
+
+	t = (Tag*)(block+fs->kfs.BUFSIZE);
+	if(t->tag != tag)
+		return -1;
+	if(qpath != QPNONE && (qpath&~QPDIR) != t->path)
+		return -1;
+	return 1;
+}
+
+static int
+getblocktag(Fs *fs, ulong n, int tag, long qpath)
+{
+	if(getblock(fs, n) < 0 || checktag(fs, block, tag, qpath) < 0)
+		return -1;
+	return 1;
+}
+
+static int
+readinfo(Fs *fs)
+{
+	fs->kfs.RBUFSIZE = 512;
+	if(getblock(fs, 0) < 0)
+		return -1;
+
+	if(memcmp(block+256, "kfs wren device\n", 16) != 0)
+		return -1;
+
+	fs->kfs.RBUFSIZE = atoi((char*)block+256+16);
+	if(!fs->kfs.RBUFSIZE || (fs->kfs.RBUFSIZE&(fs->kfs.RBUFSIZE-1)))
+		return -1;
+
+	fs->kfs.BUFSIZE = fs->kfs.RBUFSIZE - sizeof(Tag);
+	fs->kfs.DIRPERBUF = fs->kfs.BUFSIZE / sizeof(Dentry);
+	fs->kfs.INDPERBUF = fs->kfs.BUFSIZE / sizeof(long);
+	fs->kfs.INDPERBUF2 = fs->kfs.INDPERBUF * fs->kfs.INDPERBUF;
+
+	return 1;
+}
+
+static int
+readroot(Fs *fs, Dentry *d)
+{
+	Dentry *d2;
+
+	if(getblocktag(fs, 2, Tdir, QPROOT) < 0)
+		return -1;
+	d2 = (Dentry*)block;
+	if(strcmp(d2->name, "/") != 0)
+		return -1;
+	*d = *(Dentry*)block;
+	return 1;
+}
+
+static long
+indfetch(Fs *fs, long addr, long off, int tag, long path)
+{
+	if(getblocktag(fs, addr, tag, path) < 0)
+		return -1;
+	return ((long*)block)[off];
+}
+
+static long
+rel2abs(Fs *fs, Dentry *d, long a)
+{
+	long addr;
+
+	if(a < NDBLOCK)
+		return d->dblock[a];
+	a -= NDBLOCK;
+	if(a < fs->kfs.INDPERBUF){
+		if(d->iblock == 0)
+			return 0;
+		addr = indfetch(fs, d->iblock, a, Tind1, d->qid.path);
+		if(addr == 0)
+			print("rel2abs indfetch 0 %s %ld\n", d->name, a);
+		return addr;
+	}
+	a -= fs->kfs.INDPERBUF;
+	if(a < fs->kfs.INDPERBUF2){
+		if(d->diblock == 0)
+			return 0;
+		addr = indfetch(fs, d->diblock, a/fs->kfs.INDPERBUF, Tind2, d->qid.path);
+		if(addr == 0){
+			print("rel2abs indfetch 0 %s %ld\n", d->name, a/fs->kfs.INDPERBUF);
+			return 0;
+		}
+		addr = indfetch(fs, addr, a%fs->kfs.INDPERBUF, Tind1, d->qid.path);
+		return addr;
+	}
+	print("rel2abs trip ind %s %ld\n", d->name, a);
+	return -1;
+}
+
+static int
+readdentry(Fs *fs, Dentry *d, int n, Dentry *e)
+{
+	long addr, m;
+
+	m = n/fs->kfs.DIRPERBUF;
+	if((addr = rel2abs(fs, d, m)) <= 0)
+		return addr;
+	if(getblocktag(fs, addr, Tdir, d->qid.path) < 0)
+		return -1;
+	*e = *(Dentry*)(block+(n%fs->kfs.DIRPERBUF)*sizeof(Dentry));
+	return 1;
+}
+
+static int
+getdatablock(Fs *fs, Dentry *d, long a)
+{
+	long addr;
+
+	if((addr = rel2abs(fs, d, a)) == 0)
+		return -1;
+	return getblocktag(fs, addr, Tfile, QPNONE);
+}
+
+static int
+walk(Fs *fs, Dentry *d, char *name, Dentry *e)
+{
+	int i, n;
+	Dentry x;
+
+	for(i=0;; i++){
+		if((n=readdentry(fs, d, i, &x)) <= 0)
+			return n;
+		if(strcmp(x.name, name) == 0){
+			*e = x;
+			return 1;
+		}
+	}
+}
+
+static long
+kfsread(File *f, void *va, long len)
+{
+	uchar *a;
+	long tot, off, o, n;
+	Fs *fs;
+
+	a = va;
+	fs = f->fs;
+	off = f->kfs.off;
+	tot = 0;
+	while(tot < len){
+		if(getdatablock(fs, &f->kfs, off/fs->kfs.BUFSIZE) < 0)
+			return -1;
+		o = off%fs->kfs.BUFSIZE;
+		n = fs->kfs.BUFSIZE - o;
+		if(n > len-tot)
+			n = len-tot;
+		memmove(a+tot, block+o, n);
+		off += n;
+		tot += n;
+	}
+	f->kfs.off = off;
+	return tot;
+}
+
+static int
+kfswalk(File *f, char *name)
+{
+	int n;
+
+	n = walk(f->fs, &f->kfs, name, &f->kfs);
+	if(n < 0)
+		return -1;
+	f->kfs.off = 0;
+	return 1;
+}
+
+int
+kfsinit(Fs *fs)
+{
+	if(readinfo(fs) < 0 || readroot(fs, &fs->root.kfs) < 0)
+		return -1;
+
+	fs->root.fs = fs;
+	fs->read = kfsread;
+	fs->walk = kfswalk;
+	return 0;
+}
--- /dev/null
+++ b/os/boot/pc/l.s
@@ -1,0 +1,1084 @@
+#include "x16.h"
+#include "mem.h"
+
+#define WRMSR		BYTE $0x0F; BYTE $0x30	/* WRMSR, argument in AX/DX (lo/hi) */
+#define RDTSC 		BYTE $0x0F; BYTE $0x31	/* RDTSC, result in AX/DX (lo/hi) */
+#define RDMSR		BYTE $0x0F; BYTE $0x32	/* RDMSR, result in AX/DX (lo/hi) */
+
+#ifdef PXE
+#define PDB		0x90000		/* temporary page tables (24KB) */
+#else
+#define PDB		0x08000
+#endif PXE
+
+#define NoScreenBlank	1
+/*#define ResetDiscs	1*/
+
+TEXT origin(SB), $0
+	/*
+	 * This part of l.s is used only in the boot kernel.
+	 * It assumes that we are in real address mode, i.e.,
+	 * that we look like an 8086.
+	 *
+	 * Make sure the segments are reasonable.
+	 * If we were started directly from the BIOS
+	 * (i.e. no MS-DOS) then DS may not be
+	 * right.
+	 */
+	MOVW	CS, AX
+	MOVW	AX, DS
+
+#ifdef NoScreenBlank
+	/*
+	 * Get the current video mode. If it isn't mode 3,
+	 * set text mode 3.
+	 * Well, no. Windows95 won't co-operate here so we have
+	 * to explicitly set mode 3.
+	 */
+	XORL	AX, AX
+	MOVB	$0x0F, AH
+	INT	$0x10			/* get current video mode in AL */
+	CMPB	AL, $03
+	JEQ	sayhello
+#endif /* NoScreenBlank */
+	XORL	AX, AX
+	MOVB	$0x03, AL
+	INT	$0x10			/* set video mode in AL */
+
+sayhello:
+	LWI(hello(SB), rSI)
+	CALL16(biosputs(SB))
+
+#ifdef ResetDiscs
+	XORL	AX, AX			/* reset disc system */
+	XORL	DX, DX
+	MOVB	$0x80, DL
+	INT	$0x13
+#endif /* ResetDiscs */
+
+#ifdef DOTCOM
+/*
+ *	relocate everything to a half meg and jump there
+ *	- looks weird because it is being assembled by a 32 bit
+ *	  assembler for a 16 bit world
+ *
+ *	only b.com does this - not 9load
+ */
+	MOVL	$0,BX
+	INCL	BX
+	SHLL	$15,BX
+	MOVL	BX,CX
+	MOVW	BX,ES
+	MOVL	$0,SI
+	MOVL	SI,DI
+	CLD
+	REP
+	MOVSL
+
+	/*
+	 * Jump to the copied image;
+	 * fix up the DS for the new location.
+	 */
+	FARJUMP16(0x8000, _start8000(SB))
+
+TEXT _start8000(SB), $0
+	MFSR(rCS, rAX)			/* fix up DS, ES (0x8000) */
+	MTSR(rAX, rDS)
+	MTSR(rAX, rES)
+
+	/*
+	 * If we are already in protected mode, have to get back
+	 * to real mode before trying any privileged operations
+	 * (like going into protected mode...).
+	 * Try to reset with a restart vector.
+	 */
+	MFCR(rCR0, rAX)			/* are we in protected mode? */
+	ANDI(0x0001, rAX)
+	JEQ	_real
+
+	CLR(rBX)
+	MTSR(rBX, rES)
+
+	LWI(0x0467, rBX)		/* reset entry point */
+	LWI(_start8000(SB), rAX)	/* offset within segment */
+	BYTE	$0x26
+	BYTE	$0x89
+	BYTE	$0x07			/* MOVW	AX, ES:[BX] */
+	LBI(0x69, rBL)
+	MFSR(rCS, rAX)			/* segment */
+	BYTE	$0x26
+	BYTE	$0x89
+	BYTE	$0x07			/* MOVW	AX, ES:[BX] */
+
+	CLR(rDX)
+	OUTPORTB(0x70, 0x8F)
+	OUTPORTB(0x71, 0x0A)
+
+	FARJUMP16(0xFFFF, 0x0000)		/* reset */
+#endif /* DOTCOM */
+
+_real:
+
+/*
+ *	do things that need to be done in real mode.
+ *	the results get written to CONFADDR (0x1200)
+ *	in a series of <4-byte-magic-number><block-of-data>
+ *	the data length is dependent on the magic number.
+ *
+ *	this gets parsed by conf.c:/^readlsconf
+ *
+ *	N.B. CALL16 kills rDI, so we can't call anything.
+ */
+	LWI(0x0000, rAX)
+	MTSR(rAX, rES)
+	LWI(0x1200, rDI)
+
+/*
+ *	turn off interrupts
+ */
+	CLI
+
+/*
+ *	detect APM1.2 bios support
+ */
+	/* save DI */
+	SW(rDI, rock(SB))
+
+	/* disconnect anyone else */
+	LWI(0x5304, rAX)
+	LWI(0x0000, rBX)
+	INT $0x15
+
+	/* connect */
+	CLC
+	LWI(0x5303, rAX)
+	LWI(0x0000, rBX)
+	INT $0x15
+	CLI	/* apm put interrupts back? */
+
+	JC noapm
+
+	OPSIZE; PUSHR(rSI)
+	OPSIZE; PUSHR(rBX)
+	PUSHR(rDI)
+	PUSHR(rDX)
+	PUSHR(rCX)
+	PUSHR(rAX)
+
+	/* put DI, ES back */
+	LW(rock(SB), rDI)
+	LWI(0x0000, rAX)
+	MTSR(rAX, rES)
+
+	/*
+	 * write APM data.  first four bytes are APM\0.
+	 */
+	LWI(0x5041, rAX)
+	STOSW
+
+	LWI(0x004d, rAX)
+	STOSW
+
+	LWI(8, rCX)
+apmmove:
+	POPR(rAX)
+	STOSW
+	LOOP apmmove
+
+noapm:
+
+/*
+ *	end of real mode hacks: write terminator, put ES back.
+ */
+	LWI(0x0000, rAX)
+	STOSW
+	STOSW
+
+	MFSR(rCS, rAX)			/* fix up ES (0x8000) */
+	MTSR(rAX, rES)
+
+/*
+ * 	goto protected mode
+ */
+/*	MOVL	tgdtptr(SB),GDTR /**/
+	 BYTE	$0x0f
+	 BYTE	$0x01
+	 BYTE	$0x16
+	 WORD	$tgdtptr(SB)
+
+	LWI(1, rAX)
+	/* MOV AX,MSW */
+	BYTE $0x0F; BYTE $0x01; BYTE $0xF0
+
+/*
+ *	clear prefetch queue (weird code to avoid optimizations)
+ */
+	/* JMP .+2 */
+	BYTE $0xEB
+	BYTE $0x00
+
+/*
+ *	set all segs
+ */
+/*	MOVW	$SELECTOR(1, SELGDT, 0),AX	/**/
+	 BYTE	$0xc7
+	 BYTE	$0xc0
+	 WORD	$SELECTOR(1, SELGDT, 0)
+	MOVW	AX,DS
+	MOVW	AX,SS
+	MOVW	AX,ES
+	MOVW	AX,FS
+	MOVW	AX,GS
+
+/*	JMPFAR	SELECTOR(2, SELGDT, 0):$mode32bit(SB) /**/
+	 BYTE	$0x66
+	 BYTE	$0xEA
+	 LONG	$mode32bit-KZERO(SB)
+	 WORD	$SELECTOR(2, SELGDT, 0)
+
+TEXT	mode32bit(SB),$0
+	/*
+	 *  make a bottom level page table page that maps the first
+	 *  16 meg of physical memory
+	 */
+	MOVL	$PDB, DI			/* clear 6 pages for the tables etc. */
+	XORL	AX, AX
+	MOVL	$(6*BY2PG), CX
+	SHRL	$2, CX
+
+	CLD
+	REP;	STOSL
+
+	MOVL	$PDB, AX		/* phys addr of temporary page table */
+	MOVL	$(4*1024),CX		/* pte's per page */
+	MOVL	$((((4*1024)-1)<<PGSHIFT)|PTEVALID|PTEKERNEL|PTEWRITE),BX
+setpte:
+	MOVL	BX,-4(AX)(CX*4)
+	SUBL	$(1<<PGSHIFT),BX
+	LOOP	setpte
+
+	/*
+	 *  make a top level page table page that maps the first
+	 *  16 meg of memory to 0 thru 16meg and to KZERO thru KZERO+16meg
+	 */
+	MOVL	AX,BX
+	ADDL	$(4*BY2PG),AX
+	ADDL	$(PTEVALID|PTEKERNEL|PTEWRITE),BX
+	MOVL	BX,0(AX)
+	MOVL	BX,((((KZERO>>1)&0x7FFFFFFF)>>(2*PGSHIFT-1-4))+0)(AX)
+	ADDL	$BY2PG,BX
+	MOVL	BX,4(AX)
+	MOVL	BX,((((KZERO>>1)&0x7FFFFFFF)>>(2*PGSHIFT-1-4))+4)(AX)
+	ADDL	$BY2PG,BX
+	MOVL	BX,8(AX)
+	MOVL	BX,((((KZERO>>1)&0x7FFFFFFF)>>(2*PGSHIFT-1-4))+8)(AX)
+	ADDL	$BY2PG,BX
+	MOVL	BX,12(AX)
+	MOVL	BX,((((KZERO>>1)&0x7FFFFFFF)>>(2*PGSHIFT-1-4))+12)(AX)
+
+	/*
+	 *  point processor to top level page & turn on paging
+	 *
+	 *  this produces the apparently harmless "VMX|F(125):468 Dis 0x0:0x0"
+	 *  message in the VMware log.
+	 */
+	MOVL	AX,CR3
+	MOVL	CR0,AX
+	ORL	$0X80000000,AX
+	MOVL	AX,CR0
+
+	/*
+	 *  use a jump to an absolute location to get the PC into
+	 *  KZERO.
+	 */
+	LEAL	tokzero(SB),AX
+	JMP*	AX
+
+/*
+ * When we load 9load from DOS, the bootstrap jumps
+ * to the instruction right after `JUMP', which gets
+ * us into kzero.
+ *
+ * The name prevents it from being optimized away.
+ */
+TEXT jumplabel(SB), $0
+	BYTE $'J'; BYTE $'U'; BYTE $'M'; BYTE $'P'
+
+	LEAL	tokzero(SB),AX
+	JMP*	AX
+
+TEXT	tokzero(SB),$0
+	/*
+	 * Clear BSS
+	 */
+	LEAL	edata(SB),SI
+	MOVL	SI,DI
+	ADDL	$4,DI
+	MOVL	$0,AX
+	MOVL	AX,(SI)
+	LEAL	end(SB),CX
+	SUBL	DI,CX
+	SHRL	$2,CX
+	CLD
+	REP
+	MOVSL
+
+	/*
+	 *  stack and mach
+	 */
+	MOVL	$mach0(SB),SP
+	MOVL	SP,m(SB)
+	MOVL	$0,0(SP)
+	ADDL	$(MACHSIZE-4),SP	/* start stack above machine struct */
+
+	CALL	main(SB)
+
+loop:
+	JMP	loop
+
+GLOBL	mach0+0(SB), $MACHSIZE
+GLOBL	m(SB), $4
+
+/*
+ *  gdt to get us to 32-bit/segmented/unpaged mode
+ */
+TEXT	tgdt(SB),$0
+
+	/* null descriptor */
+	LONG	$0
+	LONG	$0
+
+	/* data segment descriptor for 4 gigabytes (PL 0) */
+	LONG	$(0xFFFF)
+	LONG	$(SEGG|SEGB|(0xF<<16)|SEGP|SEGPL(0)|SEGDATA|SEGW)
+
+	/* exec segment descriptor for 4 gigabytes (PL 0) */
+	LONG	$(0xFFFF)
+	LONG	$(SEGG|SEGD|(0xF<<16)|SEGP|SEGPL(0)|SEGEXEC|SEGR)
+
+	/* exec segment descriptor for 4 gigabytes (PL 0) 16-bit */
+	LONG	$(0xFFFF)
+	LONG	$(SEGG|(0xF<<16)|SEGP|SEGPL(0)|SEGEXEC|SEGR)
+
+/*
+ *  pointer to initial gdt
+ */
+TEXT	tgdtptr(SB),$0
+	WORD	$(4*8)
+	LONG	$tgdt-KZERO(SB)
+
+/*
+ * Output a string to the display.
+ * String argument is in rSI.
+ */
+TEXT biosputs(SB), $0
+	PUSHA
+	CLR(rBX)
+_BIOSputs:
+	LODSB
+	ORB(rAL, rAL)
+	JEQ _BIOSputsret
+
+	LBI(0x0E, rAH)
+	BIOSCALL(0x10)
+	JMP _BIOSputs
+
+_BIOSputsret:
+	POPA
+	RET
+
+/*
+ *  input a byte
+ */
+TEXT	inb(SB),$0
+
+	MOVL	p+0(FP),DX
+	XORL	AX,AX
+	INB
+	RET
+
+/*
+ * input a short from a port
+ */
+TEXT	ins(SB), $0
+
+	MOVL	p+0(FP), DX
+	XORL	AX, AX
+	OPSIZE; INL
+	RET
+
+/*
+ * input a long from a port
+ */
+TEXT	inl(SB), $0
+
+	MOVL	p+0(FP), DX
+	XORL	AX, AX
+	INL
+	RET
+
+/*
+ *  output a byte
+ */
+TEXT	outb(SB),$0
+
+	MOVL	p+0(FP),DX
+	MOVL	b+4(FP),AX
+	OUTB
+	RET
+
+/*
+ * output a short to a port
+ */
+TEXT	outs(SB), $0
+	MOVL	p+0(FP), DX
+	MOVL	s+4(FP), AX
+	OPSIZE; OUTL
+	RET
+
+/*
+ * output a long to a port
+ */
+TEXT	outl(SB), $0
+	MOVL	p+0(FP), DX
+	MOVL	s+4(FP), AX
+	OUTL
+	RET
+
+/*
+ *  input a string of bytes from a port
+ */
+TEXT	insb(SB),$0
+
+	MOVL	p+0(FP),DX
+	MOVL	a+4(FP),DI
+	MOVL	c+8(FP),CX
+	CLD; REP; INSB
+	RET
+
+/*
+ *  input a string of shorts from a port
+ */
+TEXT	inss(SB),$0
+	MOVL	p+0(FP),DX
+	MOVL	a+4(FP),DI
+	MOVL	c+8(FP),CX
+	CLD
+	REP; OPSIZE; INSL
+	RET
+
+/*
+ *  output a string of bytes to a port
+ */
+TEXT	outsb(SB),$0
+
+	MOVL	p+0(FP),DX
+	MOVL	a+4(FP),SI
+	MOVL	c+8(FP),CX
+	CLD; REP; OUTSB
+	RET
+
+/*
+ *  output a string of shorts to a port
+ */
+TEXT	outss(SB),$0
+	MOVL	p+0(FP),DX
+	MOVL	a+4(FP),SI
+	MOVL	c+8(FP),CX
+	CLD
+	REP; OPSIZE; OUTSL
+	RET
+
+/*
+ *  input a string of longs from a port
+ */
+TEXT	insl(SB),$0
+
+	MOVL	p+0(FP),DX
+	MOVL	a+4(FP),DI
+	MOVL	c+8(FP),CX
+	CLD; REP; INSL
+	RET
+
+/*
+ *  output a string of longs to a port
+ */
+TEXT	outsl(SB),$0
+
+	MOVL	p+0(FP),DX
+	MOVL	a+4(FP),SI
+	MOVL	c+8(FP),CX
+	CLD; REP; OUTSL
+	RET
+
+/*
+ *  routines to load/read various system registers
+ */
+GLOBL	idtptr(SB),$6
+TEXT	putidt(SB),$0		/* interrupt descriptor table */
+	MOVL	t+0(FP),AX
+	MOVL	AX,idtptr+2(SB)
+	MOVL	l+4(FP),AX
+	MOVW	AX,idtptr(SB)
+	MOVL	idtptr(SB),IDTR
+	RET
+
+TEXT	putcr3(SB),$0		/* top level page table pointer */
+	MOVL	t+0(FP),AX
+	MOVL	AX,CR3
+	RET
+
+TEXT	getcr0(SB),$0		/* coprocessor bits */
+	MOVL	CR0,AX
+	RET
+
+TEXT	getcr2(SB),$0		/* fault address */
+	MOVL	CR2,AX
+	RET
+
+TEXT	getcr3(SB),$0		/* page directory base */
+	MOVL	CR3,AX
+	RET
+
+TEXT	getcr4(SB), $0		/* CR4 - extensions */
+	MOVL	CR4, AX
+	RET
+
+TEXT _cycles(SB), $0				/* time stamp counter */
+	RDTSC
+	MOVL	vlong+0(FP), CX			/* &vlong */
+	MOVL	AX, 0(CX)			/* lo */
+	MOVL	DX, 4(CX)			/* hi */
+	RET
+
+TEXT rdmsr(SB), $0				/* model-specific register */
+	MOVL	index+0(FP), CX
+	RDMSR
+	MOVL	vlong+4(FP), CX			/* &vlong */
+	MOVL	AX, 0(CX)			/* lo */
+	MOVL	DX, 4(CX)			/* hi */
+	RET
+	
+TEXT wrmsr(SB), $0
+	MOVL	index+0(FP), CX
+	MOVL	lo+4(FP), AX
+	MOVL	hi+8(FP), DX
+	WRMSR
+	RET
+
+TEXT mb386(SB), $0
+	POPL	AX				/* return PC */
+	PUSHFL
+	PUSHL	CS
+	PUSHL	AX
+	IRETL
+
+/*
+ *  special traps
+ */
+TEXT	intr0(SB),$0
+	PUSHL	$0
+	PUSHL	$0
+	JMP	intrcommon
+TEXT	intr1(SB),$0
+	PUSHL	$0
+	PUSHL	$1
+	JMP	intrcommon
+TEXT	intr2(SB),$0
+	PUSHL	$0
+	PUSHL	$2
+	JMP	intrcommon
+TEXT	intr3(SB),$0
+	PUSHL	$0
+	PUSHL	$3
+	JMP	intrcommon
+TEXT	intr4(SB),$0
+	PUSHL	$0
+	PUSHL	$4
+	JMP	intrcommon
+TEXT	intr5(SB),$0
+	PUSHL	$0
+	PUSHL	$5
+	JMP	intrcommon
+TEXT	intr6(SB),$0
+	PUSHL	$0
+	PUSHL	$6
+	JMP	intrcommon
+TEXT	intr7(SB),$0
+	PUSHL	$0
+	PUSHL	$7
+	JMP	intrcommon
+TEXT	intr8(SB),$0
+	PUSHL	$8
+	JMP	intrcommon
+TEXT	intr9(SB),$0
+	PUSHL	$0
+	PUSHL	$9
+	JMP	intrcommon
+TEXT	intr10(SB),$0
+	PUSHL	$10
+	JMP	intrcommon
+TEXT	intr11(SB),$0
+	PUSHL	$11
+	JMP	intrcommon
+TEXT	intr12(SB),$0
+	PUSHL	$12
+	JMP	intrcommon
+TEXT	intr13(SB),$0
+	PUSHL	$13
+	JMP	intrcommon
+TEXT	intr14(SB),$0
+	PUSHL	$14
+	JMP	intrcommon
+TEXT	intr15(SB),$0
+	PUSHL	$0
+	PUSHL	$15
+	JMP	intrcommon
+TEXT	intr16(SB),$0
+	PUSHL	$0
+	PUSHL	$16
+	JMP	intrcommon
+TEXT	intr24(SB),$0
+	PUSHL	$0
+	PUSHL	$24
+	JMP	intrcommon
+TEXT	intr25(SB),$0
+	PUSHL	$0
+	PUSHL	$25
+	JMP	intrcommon
+TEXT	intr26(SB),$0
+	PUSHL	$0
+	PUSHL	$26
+	JMP	intrcommon
+TEXT	intr27(SB),$0
+	PUSHL	$0
+	PUSHL	$27
+	JMP	intrcommon
+TEXT	intr28(SB),$0
+	PUSHL	$0
+	PUSHL	$28
+	JMP	intrcommon
+TEXT	intr29(SB),$0
+	PUSHL	$0
+	PUSHL	$29
+	JMP	intrcommon
+TEXT	intr30(SB),$0
+	PUSHL	$0
+	PUSHL	$30
+	JMP	intrcommon
+TEXT	intr31(SB),$0
+	PUSHL	$0
+	PUSHL	$31
+	JMP	intrcommon
+TEXT	intr32(SB),$0
+	PUSHL	$0
+	PUSHL	$32
+	JMP	intrcommon
+TEXT	intr33(SB),$0
+	PUSHL	$0
+	PUSHL	$33
+	JMP	intrcommon
+TEXT	intr34(SB),$0
+	PUSHL	$0
+	PUSHL	$34
+	JMP	intrcommon
+TEXT	intr35(SB),$0
+	PUSHL	$0
+	PUSHL	$35
+	JMP	intrcommon
+TEXT	intr36(SB),$0
+	PUSHL	$0
+	PUSHL	$36
+	JMP	intrcommon
+TEXT	intr37(SB),$0
+	PUSHL	$0
+	PUSHL	$37
+	JMP	intrcommon
+TEXT	intr38(SB),$0
+	PUSHL	$0
+	PUSHL	$38
+	JMP	intrcommon
+TEXT	intr39(SB),$0
+	PUSHL	$0
+	PUSHL	$39
+	JMP	intrcommon
+TEXT	intr64(SB),$0
+	PUSHL	$0
+	PUSHL	$64
+	JMP	intrcommon
+TEXT	intrbad(SB),$0
+	PUSHL	$0
+	PUSHL	$0x1ff
+	JMP	intrcommon
+
+intrcommon:
+	PUSHL	DS
+	PUSHL	ES
+	PUSHL	FS
+	PUSHL	GS
+	PUSHAL
+	MOVL	$(KDSEL),AX
+	MOVW	AX,DS
+	MOVW	AX,ES
+	LEAL	0(SP),AX
+	PUSHL	AX
+	CALL	trap(SB)
+	POPL	AX
+	POPAL
+	POPL	GS
+	POPL	FS
+	POPL	ES
+	POPL	DS
+	ADDL	$8,SP	/* error code and trap type */
+	IRETL
+
+
+/*
+ *  interrupt level is interrupts on or off
+ */
+TEXT	spllo(SB),$0
+	PUSHFL
+	POPL	AX
+	STI
+	RET
+
+TEXT	splhi(SB),$0
+	PUSHFL
+	POPL	AX
+	CLI
+	RET
+
+TEXT	splx(SB),$0
+	MOVL	s+0(FP),AX
+	PUSHL	AX
+	POPFL
+	RET
+
+/*
+ *  do nothing whatsoever till interrupt happens
+ */
+TEXT	idle(SB),$0
+	HLT
+	RET
+
+/*
+ * Try to determine the CPU type which requires fiddling with EFLAGS.
+ * If the Id bit can be toggled then the CPUID instruciton can be used
+ * to determine CPU identity and features. First have to check if it's
+ * a 386 (Ac bit can't be set). If it's not a 386 and the Id bit can't be
+ * toggled then it's an older 486 of some kind.
+ *
+ *	cpuid(id[], &ax, &dx);
+ */
+#define CPUID		BYTE $0x0F; BYTE $0xA2	/* CPUID, argument in AX */
+TEXT cpuid(SB), $0
+	MOVL	$0x240000, AX
+	PUSHL	AX
+	POPFL					/* set Id|Ac */
+
+	PUSHFL
+	POPL	BX				/* retrieve value */
+
+	MOVL	$0, AX
+	PUSHL	AX
+	POPFL					/* clear Id|Ac, EFLAGS initialised */
+
+	PUSHFL
+	POPL	AX				/* retrieve value */
+	XORL	BX, AX
+	TESTL	$0x040000, AX			/* Ac */
+	JZ	_cpu386				/* can't set this bit on 386 */
+	TESTL	$0x200000, AX			/* Id */
+	JZ	_cpu486				/* can't toggle this bit on some 486 */
+
+	MOVL	$0, AX
+	CPUID
+	MOVL	id+0(FP), BP
+	MOVL	BX, 0(BP)			/* "Genu" "Auth" "Cyri" */
+	MOVL	DX, 4(BP)			/* "ineI" "enti" "xIns" */
+	MOVL	CX, 8(BP)			/* "ntel" "cAMD" "tead" */
+
+	MOVL	$1, AX
+	CPUID
+	JMP	_cpuid
+
+_cpu486:
+	MOVL	$0x400, AX
+	MOVL	$0, DX
+	JMP	_cpuid
+
+_cpu386:
+	MOVL	$0x300, AX
+	MOVL	$0, DX
+
+_cpuid:
+	MOVL	ax+4(FP), BP
+	MOVL	AX, 0(BP)
+	MOVL	dx+8(FP), BP
+	MOVL	DX, 0(BP)
+	RET
+
+
+/*
+ *  basic timing loop to determine CPU frequency
+ */
+TEXT	aamloop(SB),$0
+
+	MOVL	c+0(FP),CX
+aaml1:
+	AAM
+	LOOP	aaml1
+	RET
+
+TEXT hello(SB), $0
+	BYTE $'P'; BYTE $'l'; BYTE $'a'; BYTE $'n';
+	BYTE $' '; BYTE $'9'; BYTE $' '; BYTE $'f';
+	BYTE $'r'; BYTE $'o'; BYTE $'m'; BYTE $' ';
+	BYTE $'B'; BYTE $'e'; BYTE $'l'; BYTE $'l';
+	BYTE $' '; BYTE $'L'; BYTE $'a'; BYTE $'b';
+	BYTE $'s'; 
+#ifdef PXE
+	BYTE $' '; BYTE $'b'; BYTE $'y'; BYTE $' ';
+	BYTE $'P'; BYTE $'X'; BYTE $'E';
+#endif
+	BYTE $'\r';
+	BYTE $'\n';
+	BYTE $'\z';
+
+TEXT rock(SB), $0
+	BYTE $0; BYTE $0; BYTE $0; BYTE $0;
+
+GLOBL pxe(SB), $4
+#ifdef PXE
+DATA	pxe+0(SB)/4, $1
+#else
+DATA	pxe+0(SB)/4, $0
+#endif /* PXE */
+
+/*
+ * Save registers.
+ */
+TEXT saveregs(SB), $0
+	/* appease 8l */
+	SUBL $32, SP
+	POPL AX
+	POPL AX
+	POPL AX
+	POPL AX
+	POPL AX
+	POPL AX
+	POPL AX
+	POPL AX
+	
+	PUSHL	AX
+	PUSHL	BX
+	PUSHL	CX
+	PUSHL	DX
+	PUSHL	BP
+	PUSHL	DI
+	PUSHL	SI
+	PUSHFL
+
+	XCHGL	32(SP), AX	/* swap return PC and saved flags */
+	XCHGL	0(SP), AX
+	XCHGL	32(SP), AX
+	RET
+
+TEXT restoreregs(SB), $0
+	/* appease 8l */
+	PUSHL	AX
+	PUSHL	AX
+	PUSHL	AX
+	PUSHL	AX
+	PUSHL	AX
+	PUSHL	AX
+	PUSHL	AX
+	PUSHL	AX
+	ADDL	$32, SP
+	
+	XCHGL	32(SP), AX	/* swap return PC and saved flags */
+	XCHGL	0(SP), AX
+	XCHGL	32(SP), AX
+
+	POPFL
+	POPL	SI
+	POPL	DI
+	POPL	BP
+	POPL	DX
+	POPL	CX
+	POPL	BX
+	POPL	AX
+	RET
+
+/*
+ * Assumed to be in protected mode at time of call.
+ * Switch to real mode, execute an interrupt, and
+ * then switch back to protected mode.  
+ *
+ * Assumes:
+ *
+ *	- no device interrupts are going to come in
+ *	- 0-16MB is identity mapped in page tables
+ *	- can use code segment 0x1000 in real mode
+ *		to get at l.s code
+ */
+TEXT realmodeidtptr(SB), $0
+	WORD	$(4*256-1)
+	LONG	$0
+
+TEXT realmode0(SB), $0
+	CALL	saveregs(SB)
+
+	/* switch to low code address */
+	LEAL	physcode-KZERO(SB), AX
+	JMP *AX
+
+TEXT physcode(SB), $0
+
+	/* switch to low stack */
+	MOVL	SP, AX
+	MOVL	$0x7C00, SP
+	PUSHL	AX
+
+	/* load IDT with real-mode version; GDT already fine */
+	MOVL	realmodeidtptr(SB), IDTR
+
+	/* edit INT $0x00 instruction below */
+	MOVL	realmodeintr(SB), AX
+	MOVB	AX, realmodeintrinst+1(SB)
+
+	/* disable paging */
+	MOVL	CR0, AX
+	ANDL	$0x7FFFFFFF, AX
+	MOVL	AX, CR0
+	/* JMP .+2 to clear prefetch queue*/
+	BYTE $0xEB; BYTE $0x00
+
+	/* jump to 16-bit code segment */
+/*	JMPFAR	SELECTOR(3, SELGDT, 0):$again16bit(SB) /**/
+	 BYTE	$0xEA
+	 LONG	$again16bit-KZERO(SB)
+	 WORD	$SELECTOR(3, SELGDT, 0)
+
+TEXT again16bit(SB), $0
+	/*
+	 * Now in 16-bit compatibility mode.
+	 * These are 32-bit instructions being interpreted
+	 * as 16-bit instructions.  I'm being lazy and
+	 * not using the macros because I know when
+	 * the 16- and 32-bit instructions look the same
+	 * or close enough.
+	 */
+
+	/* disable protected mode and jump to real mode cs */
+	OPSIZE; MOVL CR0, AX
+	OPSIZE; XORL BX, BX
+	OPSIZE; INCL BX
+	OPSIZE; XORL BX, AX
+	OPSIZE; MOVL AX, CR0
+
+	/* JMPFAR 0x1000:now16real */
+	 BYTE $0xEA
+	 WORD	$now16real-KZERO(SB)
+	 WORD	$0x1000
+
+TEXT now16real(SB), $0
+	/* copy the registers for the bios call */
+	LWI(0x1000, rAX)
+	MOVW	AX,SS
+	LWI(realmoderegs(SB), rBP)
+	
+	/* offsets are in Ureg */
+	LXW(44, xBP, rAX)
+	MOVW	AX, DS
+	LXW(40, xBP, rAX)
+	MOVW	AX, ES
+
+	OPSIZE; LXW(0, xBP, rDI)
+	OPSIZE; LXW(4, xBP, rSI)
+	OPSIZE; LXW(16, xBP, rBX)
+	OPSIZE; LXW(20, xBP, rDX)
+	OPSIZE; LXW(24, xBP, rCX)
+	OPSIZE; LXW(28, xBP, rAX)
+
+	CLC
+
+TEXT realmodeintrinst(SB), $0
+	INT $0x00
+
+
+	/* save the registers after the call */
+
+	LWI(0x7bfc, rSP)
+	OPSIZE; PUSHFL
+	OPSIZE; PUSHL AX
+
+	LWI(0x1000, rAX)
+	MOVW	AX,SS
+	LWI(realmoderegs(SB), rBP)
+	
+	OPSIZE; SXW(rDI, 0, xBP)
+	OPSIZE; SXW(rSI, 4, xBP)
+	OPSIZE; SXW(rBX, 16, xBP)
+	OPSIZE; SXW(rDX, 20, xBP)
+	OPSIZE; SXW(rCX, 24, xBP)
+	OPSIZE; POPL AX
+	OPSIZE; SXW(rAX, 28, xBP)
+
+	MOVW	DS, AX
+	OPSIZE; SXW(rAX, 44, xBP)
+	MOVW	ES, AX
+	OPSIZE; SXW(rAX, 40, xBP)
+
+	OPSIZE; POPL AX
+	OPSIZE; SXW(rAX, 64, xBP)	/* flags */
+
+	/* re-enter protected mode and jump to 32-bit code */
+	OPSIZE; MOVL $1, AX
+	OPSIZE; MOVL AX, CR0
+	
+/*	JMPFAR	SELECTOR(2, SELGDT, 0):$again32bit(SB) /**/
+	 OPSIZE
+	 BYTE $0xEA
+	 LONG	$again32bit-KZERO(SB)
+	 WORD	$SELECTOR(2, SELGDT, 0)
+
+TEXT again32bit(SB), $0
+	MOVW	$SELECTOR(1, SELGDT, 0),AX
+	MOVW	AX,DS
+	MOVW	AX,SS
+	MOVW	AX,ES
+	MOVW	AX,FS
+	MOVW	AX,GS
+
+	/* enable paging and jump to kzero-address code */
+	MOVL	CR0, AX
+	ORL	$0x80000000, AX
+	MOVL	AX, CR0
+	LEAL	again32kzero(SB), AX
+	JMP*	AX
+
+TEXT again32kzero(SB), $0
+	/* breathe a sigh of relief - back in 32-bit protected mode */
+
+	/* switch to old stack */	
+	PUSHL	AX	/* match popl below for 8l */
+	MOVL	$0x7BFC, SP
+	POPL	SP
+
+	/* restore idt */
+	MOVL	idtptr(SB),IDTR
+
+	CALL	restoreregs(SB)
+	RET
+
+TEXT realmoderegs(SB), $0
+	LONG $0; LONG $0; LONG $0; LONG $0
+	LONG $0; LONG $0; LONG $0; LONG $0
+	LONG $0; LONG $0; LONG $0; LONG $0
+	LONG $0; LONG $0; LONG $0; LONG $0
+	LONG $0; LONG $0; LONG $0; LONG $0
+
+TEXT realmodeintr(SB), $0
+	LONG $0
+
--- /dev/null
+++ b/os/boot/pc/lib.h
@@ -1,0 +1,106 @@
+#define	offsetof(s, m)	(ulong)(&(((s*)0)->m))
+
+/*
+ * functions (possibly) linked in, complete, from libc.
+ */
+
+/*
+ * mem routines
+ */
+extern	void*	memccpy(void*, void*, int, ulong);
+extern	void*	memset(void*, int, ulong);
+extern	int	memcmp(void*, void*, ulong);
+extern	void*	memmove(void*, void*, ulong);
+extern	void*	memchr(void*, int, ulong);
+
+/*
+ * string routines
+ */
+extern	char*	strcat(char*, char*);
+extern	char*	strchr(char*, int);
+extern	int	strcmp(char*, char*);
+extern	char*	strcpy(char*, char*);
+extern	char*	strncat(char*, char*, long);
+extern	char*	strncpy(char*, char*, long);
+extern	int	strncmp(char*, char*, long);
+extern	long	strlen(char*);
+extern	char*	strrchr(char*, char);
+extern	char*	strstr(char*, char*);
+
+
+/*
+ * print routines
+ */
+typedef struct Fmt	Fmt;
+typedef int (*Fmts)(Fmt*);
+struct Fmt{
+	uchar	runes;			/* output buffer is runes or chars? */
+	void	*start;			/* of buffer */
+	void	*to;			/* current place in the buffer */
+	void	*stop;			/* end of the buffer; overwritten if flush fails */
+	int	(*flush)(Fmt *);	/* called when to == stop */
+	void	*farg;			/* to make flush a closure */
+	int	nfmt;			/* num chars formatted so far */
+	va_list	args;			/* args passed to dofmt */
+	int	r;			/* % format Rune */
+	int	width;
+	int	prec;
+	ulong	flags;
+};
+extern	int	print(char*, ...);
+extern	char*	vseprint(char*, char*, char*, va_list);
+extern	int	sprint(char*, char*, ...);
+extern 	int	snprint(char*, int, char*, ...);
+extern	int	fmtinstall(int, int (*)(Fmt*));
+
+#pragma varargck	argpos	addconf 1
+#pragma	varargck	argpos	fmtprint	2
+#pragma	varargck	argpos	print		1
+#pragma	varargck	argpos	seprint		3
+#pragma	varargck	argpos	snprint		3
+#pragma	varargck	argpos	sprint		2
+#pragma varargck	type	"H" void*
+
+#pragma	varargck	type	"lld"	vlong
+#pragma	varargck	type	"llx"	vlong
+#pragma	varargck	type	"lld"	uvlong
+#pragma	varargck	type	"llx"	uvlong
+#pragma	varargck	type	"ld"	long
+#pragma	varargck	type	"lx"	long
+#pragma	varargck	type	"ld"	ulong
+#pragma	varargck	type	"lx"	ulong
+#pragma	varargck	type	"d"	int
+#pragma	varargck	type	"x"	int
+#pragma	varargck	type	"c"	int
+#pragma	varargck	type	"C"	int
+#pragma	varargck	type	"d"	uint
+#pragma	varargck	type	"x"	uint
+#pragma	varargck	type	"c"	uint
+#pragma	varargck	type	"C"	uint
+#pragma	varargck	type	"f"	double
+#pragma	varargck	type	"e"	double
+#pragma	varargck	type	"g"	double
+#pragma	varargck	type	"s"	char*
+#pragma	varargck	type	"q"	char*
+#pragma	varargck	type	"S"	Rune*
+#pragma	varargck	type	"Q"	Rune*
+#pragma	varargck	type	"r"	void
+#pragma	varargck	type	"%"	void
+#pragma	varargck	type	"|"	int
+#pragma	varargck	type	"p"	void*
+#pragma varargck	type	"lux"	void*
+#pragma	varargck	type	"E"	uchar*
+
+#define PRINTSIZE	256
+
+/*
+ * one-of-a-kind
+ */
+extern	int	atoi(char*);
+extern	uintptr	getcallerpc(void*);
+extern	long	strtol(char*, char**, int);
+extern	ulong	strtoul(char*, char**, int);
+extern	uvlong	strtoull(char*, char**, int);
+extern	long	end;
+
+#define	NAMELEN	28
--- /dev/null
+++ b/os/boot/pc/load.c
@@ -1,0 +1,627 @@
+#include "u.h"
+#include "lib.h"
+#include "mem.h"
+#include "dat.h"
+#include "fns.h"
+#include "io.h"
+#include "sd.h"
+#include "fs.h"
+
+#ifndef VERBOSE
+#define VERBOSE 0
+#endif
+
+/*
+ * "cache" must be in this list so that 9load will pass the definition of
+ * the cache partition into the kernel so that the disk named by the `cfs'
+ * variable in plan9.ini can be seen in all circumstances before termrc
+ * sets up all the disk partitions.  In particular, if it's on an odd-ball
+ * disk like sd10 rather than sdC0, this is needed.
+ */
+static char *diskparts[] = {
+	"dos", "9fat", "fs", "data", "cdboot", "cache", 0
+};
+static char *etherparts[] = { "*", 0 };
+
+static char *diskinis[] = {
+	"plan9/plan9.ini",
+	"plan9.ini",
+	0
+};
+static char *etherinis[] = {
+	"/cfg/pxe/%E",
+	0
+};
+
+/* ordering: devbios must be called before devsd calls sdbios */
+Type types[] = {
+	{	Tfloppy,
+		Fini|Ffs,
+		floppyinit, floppyinitdev,
+		floppygetfspart, 0, floppyboot,
+		floppyprintdevs,
+		diskparts,
+		diskinis,
+	},
+	{	Tether,
+		Fini|Fbootp,
+		etherinit, etherinitdev,
+		pxegetfspart, 0, bootpboot,
+		etherprintdevs,
+		etherparts,
+		etherinis,
+	},
+	{	Tbios,
+		Fini|Ffs,
+		biosinit, biosinitdev,
+		biosgetfspart, nil, biosboot,
+		biosprintdevs,
+		diskparts,
+		diskinis,
+	},
+	{	Tcd,
+		Fini|Ffs,
+		cdinit, sdinitdev,
+		sdgetfspart, sdaddconf, sdboot,
+		sdprintdevs,
+		diskparts,
+		diskinis,
+	},
+	{	Tsd,
+		Fini|Ffs,
+		sdinit, sdinitdev,
+		sdgetfspart, sdaddconf, sdboot,
+		sdprintdevs,
+		diskparts,
+		diskinis,
+	},
+	{	Tnil,
+		0,
+		nil, nil, nil, nil, nil, nil,
+		nil,
+		nil,
+		0,
+		nil,
+	},
+};
+
+static char *typenm[] = {
+	[Tnil]		"nil",
+	[Tfloppy]	"floppy",
+	[Tsd]		"sd",
+	[Tether]	"ether",
+	[Tcd]		"cd",
+	[Tbios]		"bios",
+};
+
+static char *
+typename(int type)
+{
+	if (type < 0 || type >= nelem(typenm) || typenm[type] == nil)
+		return "**gok**";
+	return typenm[type];
+}
+
+extern SDifc sdataifc;
+extern SDifc sdiahciifc;
+extern SDifc sdaoeifc;
+extern SDifc sdbiosifc;
+
+#ifdef NOSCSI
+
+SDifc* sdifc[] = {
+	&sdataifc,
+	&sdiahciifc,
+	&sdbiosifc,
+	&sdaoeifc,
+	nil,
+};
+
+#else
+
+extern SDifc sdmylexifc;
+extern SDifc sd53c8xxifc;
+
+SDifc* sdifc[] = {
+	&sdataifc,
+	&sdiahciifc,
+	&sdmylexifc,
+	&sd53c8xxifc,
+	&sdbiosifc,
+	&sdaoeifc,
+	nil,
+};
+
+#endif NOSCSI
+
+typedef struct Mode Mode;
+
+enum {
+	Maxdev		= 7,
+	Dany		= -1,
+	Nmedia		= 16,
+	Nini		= 10,
+};
+
+enum {					/* mode */
+	Mauto		= 0x00,
+	Mlocal		= 0x01,
+	Manual		= 0x02,
+	NMode		= 0x03,
+};
+
+typedef struct Medium Medium;
+struct Medium {
+	Type*	type;
+	int	flag;
+	int	dev;
+	char name[NAMELEN];
+
+	Fs *inifs;
+	char *part;
+	char *ini;
+
+	Medium*	next;
+};
+
+typedef struct Mode {
+	char*	name;
+	int	mode;
+} Mode;
+
+static Medium media[Nmedia];
+static Medium *curmedium = media;
+
+static Mode modes[NMode+1] = {
+	[Mauto]		{ "auto",   Mauto,  },
+	[Mlocal]	{ "local",  Mlocal, },
+	[Manual]	{ "manual", Manual, },
+};
+
+char **ini;
+
+int scsi0port;
+char *defaultpartition;
+int iniread;
+
+int debugload;
+
+static Medium*
+parse(char *line, char **file)
+{
+	char *p;
+	Type *tp;
+	Medium *mp;
+
+	if(p = strchr(line, '!')) {
+		*p++ = 0;
+		*file = p;
+	} else
+		*file = "";
+
+	for(tp = types; tp->type != Tnil; tp++)
+		for(mp = tp->media; mp; mp = mp->next)
+			if(strcmp(mp->name, line) == 0)
+				return mp;
+	if(p)
+		*--p = '!';
+	return nil;
+}
+
+static int
+boot(Medium *mp, char *file)
+{
+	Type *tp;
+	Medium *xmp;
+	static int didaddconf;
+	Boot b;
+
+	memset(&b, 0, sizeof b);
+	b.state = INITKERNEL;
+
+	if(didaddconf == 0) {
+		didaddconf = 1;
+		for(tp = types; tp->type != Tnil; tp++)
+			if(tp->addconf)
+				for(xmp = tp->media; xmp; xmp = xmp->next)
+					(*tp->addconf)(xmp->dev);
+	}
+
+	sprint(BOOTLINE, "%s!%s", mp->name, file);
+	print("booting %s!%s\n", mp->name, file);
+	return (*mp->type->boot)(mp->dev, file, &b);
+}
+
+static Medium*
+allocm(Type *tp)
+{
+	Medium **l;
+
+	if(curmedium >= &media[Nmedia])
+		return 0;
+
+	for(l = &tp->media; *l; l = &(*l)->next)
+		;
+	*l = curmedium++;
+	return *l;
+}
+
+Medium*
+probe(int type, int flag, int dev)
+{
+	Type *tp;
+	int i;
+	Medium *mp;
+	File f;
+	Fs *fs;
+	char **partp;
+
+	for(tp = types; tp->type != Tnil; tp++){
+		if(type != Tany && type != tp->type)
+			continue;
+
+		if(flag != Fnone){
+			for(mp = tp->media; mp; mp = mp->next){
+				if((flag & mp->flag) && (dev == Dany || dev == mp->dev))
+					return mp;
+			}
+		}
+		if (debugload)
+			print("probing %s...", typename(tp->type));
+		if((tp->flag & Fprobe) == 0){
+			tp->flag |= Fprobe;
+			tp->mask = (*tp->init)();
+		}
+
+		for(i = 0; tp->mask; i++){
+			if((tp->mask & (1<<i)) == 0)
+				continue;
+			tp->mask &= ~(1<<i);
+
+			if((mp = allocm(tp)) == 0)
+				continue;
+
+			mp->dev = i;
+			mp->flag = tp->flag;
+			mp->type = tp;
+			(*tp->initdev)(i, mp->name);
+
+			if(mp->flag & Fini){
+				mp->flag &= ~Fini;
+				for(partp = tp->parts; *partp; partp++){
+					if((fs = (*tp->getfspart)(i, *partp, 0)) == nil)
+						continue;
+
+					for(ini = tp->inis; *ini; ini++){
+						if(fswalk(fs, *ini, &f) > 0){
+							mp->inifs = fs;
+							mp->part = *partp;
+							mp->ini = f.path;
+							mp->flag |= Fini;
+							goto Break2;
+						}
+					}
+				}
+			}
+		Break2:
+			if((flag & mp->flag) && (dev == Dany || dev == i))
+				return mp;
+		}
+	}
+
+	return 0;
+}
+
+void
+main(void)
+{
+	Medium *mp;
+	int flag, i, mode, tried;
+	char def[2*NAMELEN], line[80], *p, *file;
+	Type *tp;
+
+	i8042a20();
+	memset(m, 0, sizeof(Mach));
+	trapinit();
+	clockinit();
+	alarminit();
+	meminit(0);
+	spllo();
+	consinit("0", "9600");
+	kbdinit();
+	if((ulong)&end > (KZERO|(640*1024)))
+		panic("i'm too big\n");
+
+	readlsconf();
+	print("initial probe, to find plan9.ini...");
+	/* find and read plan9.ini, setting configuration variables */
+	for(tp = types; tp->type != Tnil; tp++){
+		/* skip bios until we have read plan9.ini */
+		if(!pxe && tp->type == Tether || tp->type == Tbios)
+			continue;
+		if (VERBOSE)
+			print("probing %s...", typename(tp->type));
+		if((mp = probe(tp->type, Fini, Dany)) && (mp->flag & Fini)){
+			print("using %s!%s!%s\n", mp->name, mp->part, mp->ini);
+			iniread = !dotini(mp->inifs);
+			break;
+		}
+	}
+	print("\n");
+	apminit();
+
+	debugload = getconf("*debugload") != nil;
+	if((p = getconf("console")) != nil)
+		consinit(p, getconf("baud"));
+
+	devpccardlink();
+	devi82365link();
+
+	/*
+ 	 * Even after we find the ini file, we keep probing disks,
+	 * because we have to collect the partition tables and
+	 * have boot devices for parse.
+	 */
+	probe(Tany, Fnone, Dany);
+	if (debugload)
+		print("end disk probe\n");
+	tried = 0;
+	mode = Mauto;
+
+	p = getconf("bootfile");
+
+	if(p != 0) {
+		mode = Manual;
+		for(i = 0; i < NMode; i++){
+			if(strcmp(p, modes[i].name) == 0){
+				mode = modes[i].mode;
+				goto done;
+			}
+		}
+		if((mp = parse(p, &file)) == nil) {
+			print("Unknown boot device: %s\n", p);
+			goto done;
+		}
+		tried = boot(mp, file);
+	}
+done:
+	if(tried == 0 && mode != Manual){
+		flag = Fany;
+		if(mode == Mlocal)
+			flag &= ~Fbootp;
+		if((mp = probe(Tany, flag, Dany)) && mp->type->type != Tfloppy)
+			boot(mp, "");
+		if (debugload)
+			print("end auto probe\n");
+	}
+
+	def[0] = 0;
+	probe(Tany, Fnone, Dany);
+	if (debugload)
+		print("end final probe\n");
+	if(p = getconf("bootdef"))
+		strcpy(def, p);
+
+	flag = 0;
+	for(tp = types; tp->type != Tnil; tp++){
+		for(mp = tp->media; mp; mp = mp->next){
+			if(flag == 0){
+				flag = 1;
+				print("Boot devices:");
+			}
+			(*tp->printdevs)(mp->dev);
+		}
+	}
+	if(flag)
+		print("\n");
+
+	for(;;){
+		if(getstr("boot from", line, sizeof(line), def, (mode != Manual)*15) >= 0)
+			if(mp = parse(line, &file))
+				boot(mp, file);
+		def[0] = 0;
+	}
+}
+
+int
+getfields(char *lp, char **fields, int n, char sep)
+{
+	int i;
+
+	for(i = 0; lp && *lp && i < n; i++){
+		while(*lp == sep)
+			*lp++ = 0;
+		if(*lp == 0)
+			break;
+		fields[i] = lp;
+		while(*lp && *lp != sep){
+			if(*lp == '\\' && *(lp+1) == '\n')
+				*lp++ = ' ';
+			lp++;
+		}
+	}
+	return i;
+}
+
+int
+cistrcmp(char *a, char *b)
+{
+	int ac, bc;
+
+	for(;;){
+		ac = *a++;
+		bc = *b++;
+
+		if(ac >= 'A' && ac <= 'Z')
+			ac = 'a' + (ac - 'A');
+		if(bc >= 'A' && bc <= 'Z')
+			bc = 'a' + (bc - 'A');
+		ac -= bc;
+		if(ac)
+			return ac;
+		if(bc == 0)
+			break;
+	}
+	return 0;
+}
+
+int
+cistrncmp(char *a, char *b, int n)
+{
+	unsigned ac, bc;
+
+	while(n > 0){
+		ac = *a++;
+		bc = *b++;
+		n--;
+
+		if(ac >= 'A' && ac <= 'Z')
+			ac = 'a' + (ac - 'A');
+		if(bc >= 'A' && bc <= 'Z')
+			bc = 'a' + (bc - 'A');
+
+		ac -= bc;
+		if(ac)
+			return ac;
+		if(bc == 0)
+			break;
+	}
+
+	return 0;
+}
+
+#define PSTART		( 8*1024*1024)
+#define PEND		(16*1024*1024)
+
+ulong palloc = PSTART;
+
+void*
+ialloc(ulong n, int align)
+{
+	ulong p;
+	int a;
+
+	p = palloc;
+	if(align <= 0)
+		align = 4;
+	if(a = n % align)
+		n += align - a;
+	if(a = p % align)
+		p += align - a;
+
+
+	palloc = p+n;
+	if(palloc > PEND)
+		panic("ialloc(%lud, %d) called from %#p\n",
+			n, align, getcallerpc(&n));
+	return memset((void*)(p|KZERO), 0, n);
+}
+
+void*
+xspanalloc(ulong size, int align, ulong span)
+{
+	ulong a, v;
+
+	if((palloc + (size+align+span)) > PEND)
+		panic("xspanalloc(%lud, %d, 0x%lux) called from %#p\n",
+			size, align, span, getcallerpc(&size));
+
+	a = (ulong)ialloc(size+align+span, 0);
+
+	if(span > 2)
+		v = (a + span) & ~(span-1);
+	else
+		v = a;
+
+	if(align > 1)
+		v = (v + align) & ~(align-1);
+
+	return (void*)v;
+}
+
+static Block *allocbp;
+
+Block*
+allocb(int size)
+{
+	Block *bp, **lbp;
+	ulong addr;
+
+	lbp = &allocbp;
+	for(bp = *lbp; bp; bp = bp->next){
+		if((bp->lim - bp->base) >= size){
+			*lbp = bp->next;
+			break;
+		}
+		lbp = &bp->next;
+	}
+	if(bp == 0){
+		if((palloc + (sizeof(Block)+size+64)) > PEND)
+			panic("allocb(%d) called from %#p\n",
+				size, getcallerpc(&size));
+		bp = ialloc(sizeof(Block)+size+64, 0);
+		addr = (ulong)bp;
+		addr = ROUNDUP(addr + sizeof(Block), 8);
+		bp->base = (uchar*)addr;
+		bp->lim = ((uchar*)bp) + sizeof(Block)+size+64;
+	}
+
+	if(bp->flag)
+		panic("allocb reuse\n");
+
+	bp->rp = bp->base;
+	bp->wp = bp->rp;
+	bp->next = 0;
+	bp->flag = 1;
+
+	return bp;
+}
+
+void
+freeb(Block* bp)
+{
+	bp->next = allocbp;
+	allocbp = bp;
+
+	bp->flag = 0;
+}
+
+enum {
+	Paddr=		0x70,	/* address port */
+	Pdata=		0x71,	/* data port */
+};
+
+uchar
+nvramread(int offset)
+{
+	outb(Paddr, offset);
+	return inb(Pdata);
+}
+
+void (*etherdetach)(void);
+void (*floppydetach)(void);
+void (*sddetach)(void);
+
+void
+warp9(ulong entry)
+{
+	if(etherdetach)
+		etherdetach();
+	if(floppydetach)
+		floppydetach();
+	if(sddetach)
+		sddetach();
+	consdrain();
+
+	splhi();
+	trapdisable();
+
+	/*
+	 * This is where to push things on the stack to
+	 * boot *BSD systems, e.g.
+	(*(void(*)(void*, void*, void*, void*, ulong, ulong))(PADDR(entry)))(0, 0, 0, 0, 8196, 640);
+	 * will enable NetBSD boot (the real memory size needs to
+	 * go in the 5th argument).
+	 */
+	(*(void(*)(void))(PADDR(entry)))();
+}
--- /dev/null
+++ b/os/boot/pc/mbr.s
@@ -1,0 +1,259 @@
+/*
+ * Hard disc boot block. Loaded at 0x7C00, relocates to 0x0600:
+ *	8a mbr.s; 8l -o mbr -l -H3 -T0x0600 mbr.8
+ */
+#include "x16.h"
+#include "mem.h"
+
+/*#define FLOPPY	1		/* test on a floppy */
+#define TRACE(C)	PUSHA;\
+			CLR(rBX);\
+			MOVB $C, AL;\
+			LBI(0x0E, rAH);\
+			BIOSCALL(0x10);\
+			POPA
+
+/*
+ * We keep data on the stack, indexed by BP.
+ */
+#define Xdap		0x00		/* disc address packet */
+#define Xtable		0x10		/* partition table entry */
+#define Xdrive		0x12		/* starting disc */
+#define Xtotal		0x14		/* sum of allocated data above */
+
+/*
+ * Start: loaded at 0000:7C00, relocate to 0000:0600.
+ * Boot drive is in rDL.
+ */
+TEXT _start(SB), $0
+	CLI
+	CLR(rAX)
+	MTSR(rAX, rSS)			/* 0000 -> rSS */
+	LWI((0x7C00-Xtotal), rSP)	/* 7Bxx -> rSP */
+	MW(rSP, rBP)			/* set the indexed-data pointer */
+
+	MTSR(rAX, rDS)			/* 0000 -> rDS, source segment */
+	LWI(0x7C00, rSI)		/* 7C00 -> rSI, source offset */
+	MTSR(rAX, rES)			/* 0000 -> rES, destination segment */
+	LWI(0x600, rDI)			/* 0600 -> rDI, destination offset */
+	LWI(0x100, rCX)			/* 0100 -> rCX, loop count (words) */
+
+	CLD
+	REP; MOVSL			/* MOV DS:[(E)SI] -> ES:[(E)DI] */
+
+	FARJUMP16(0x0000, _start0600(SB))
+
+TEXT _start0600(SB), $0
+#ifdef FLOPPY
+	LBI(0x80, rDL)
+#else
+	CLRB(rAL)			/* some systems pass 0 */
+	CMPBR(rAL, rDL)
+	JNE _save
+	LBI(0x80, rDL)
+#endif /* FLOPPY */
+_save:
+	SXB(rDL, Xdrive, xBP)		/* save disc */
+
+	LWI(confidence(SB), rSI)	/* for that warm, fuzzy feeling */
+	CALL16(BIOSputs(SB))
+
+	LWI(_start+0x01BE(SB), rSI)	/* address of partition table */
+	LWI(0x04, rCX)			/* 4 entries in table */
+	LBI(0x80, rAH)			/* active entry value */
+	CLRB(rAL)			/* inactive entry value */
+
+_activeloop0:
+	LXB(0x00, xSI, rBL)		/* get active entry from table */
+	CMPBR(rBL, rAH)			/* is this an active entry? */
+	JEQ _active
+
+	CMPBR(rBL, rAL)			/* if not active it should be 0 */
+	JNE _invalidMBR
+
+	ADDI(0x10, rSI)			/* next table entry */
+	DEC(rCX)
+	JNE _activeloop0
+
+	LWI(noentry(SB), rSI)
+	CALL16(buggery(SB))
+
+_active:
+	MW(rSI, rDI)			/* save table address */
+
+_activeloop1:
+	ADDI(0x10, rSI)			/* next table entry */
+	DEC(rCX)
+	JEQ _readsector
+
+	LXB(0x00, xSI, rBL)		/* get active entry from table */
+	CMPBR(rBL, rAH)			/* is this an active entry? */
+	JNE _activeloop1		/* should only be one active */
+
+_invalidMBR:
+	LWI(invalidMBR(SB), rSI)
+	CALL16(buggery(SB))
+
+_readsector:
+	LBI(0x41, rAH)			/* check extensions present */
+	LWI(0x55AA, rBX)
+	LXB(Xdrive, xBP, rDL)		/* drive */
+	BIOSCALL(0x13)			/* CF set on failure */
+	JCS _readsector2
+	CMPI(0xAA55, rBX)
+	JNE _readsector2
+	ANDI(0x0001, rCX)
+	JEQ _readsector2
+
+_readsector42:
+	SBPBI(0x10, Xdap+0)		/* packet size */
+	SBPBI(0x00, Xdap+1)		/* reserved */
+	SBPBI(0x01, Xdap+2)		/* number of blocks to transfer */
+	SBPBI(0x00, Xdap+3)		/* reserved */
+	SBPWI(0x7C00, Xdap+4)		/* transfer buffer :offset */
+	SBPWI(0x0000, Xdap+6)		/* transfer buffer seg: */
+	LXW(0x08, xDI, rAX)		/* LBA (64-bits) */
+	SBPW(rAX, Xdap+8)
+	LXW(0x0A, xDI, rAX)
+	SBPW(rAX, Xdap+10)
+	SBPWI(0x0000, Xdap+12)
+	SBPWI(0x0000, Xdap+14)
+
+	MW(rBP, rSI)			/* disk address packet */
+	LBI(0x42, rAH)			/* extended read */
+	BIOSCALL(0x13)			/* CF set on failure */
+	JCC _readsectorok
+
+	LWI(ioerror(SB), rSI)
+	CALL16(buggery(SB))
+
+/*
+ * Read a sector from a disc using the traditional BIOS call.
+ * For BIOSCALL(0x13/AH=0x02):
+ *   rAH	0x02
+ *   rAL	number of sectors to read (1)
+ *   rCH	low 8 bits of cylinder
+ *   rCL	high 2 bits of cylinder (7-6), sector (5-0)
+ *   rDH	head
+ *   rDL	drive
+ *   rES:rBX	buffer address
+ */
+_readsector2:
+	LXB(0x01, xDI, rDH)		/* head */
+	LXW(0x02, xDI, rCX)		/* save active cylinder/sector */
+
+	LWI(0x0201, rAX)		/* read one sector */
+	LXB(Xdrive, xBP, rDL)		/* drive */
+	LWI(0x7C00, rBX)		/* buffer address (rES already OK) */
+	BIOSCALL(0x13)			/* CF set on failure */
+	JCC _readsectorok
+
+	LWI(ioerror(SB), rSI)
+	CALL16(buggery(SB))
+
+_readsectorok:
+	LWI(0x7C00, rBX)		/* buffer address (rES already OK) */
+	LXW(0x1FE, xBX, rAX)
+	CMPI(0xAA55, rAX)
+	JNE _bbnotok
+
+	/*
+	 * Jump to the loaded PBS.
+	 * rDL and rSI should still contain the drive
+	 * and partition table pointer respectively.
+	 */
+	MW(rDI, rSI)
+	FARJUMP16(0x0000, 0x7C00)
+
+_bbnotok:
+	LWI(invalidPBS(SB), rSI)
+
+TEXT buggery(SB), $0
+	CALL16(BIOSputs(SB))
+	LWI(reboot(SB), rSI)
+	CALL16(BIOSputs(SB))
+
+_wait:
+	CLR(rAX)			/* wait for any key */
+	BIOSCALL(0x16)
+
+_reset:
+	CLR(rBX)			/* set ES segment for BIOS area */
+	MTSR(rBX, rES)
+
+	LWI(0x0472, rBX)		/* warm-start code address */
+	LWI(0x1234, rAX)		/* warm-start code */
+	POKEW				/* MOVW	AX, ES:[BX] */
+
+	FARJUMP16(0xFFFF, 0x0000)	/* reset */
+
+/*
+ * Output a string to the display.
+ * String argument is in rSI.
+ */
+TEXT BIOSputs(SB), $0
+	PUSHA
+	CLR(rBX)
+_BIOSputs:
+	LODSB
+	ORB(rAL, rAL)
+	JEQ _BIOSputsret
+
+	LBI(0x0E, rAH)
+	BIOSCALL(0x10)
+	JMP _BIOSputs
+
+_BIOSputsret:
+	POPA
+	RET
+
+/* "No active entry in MBR" */
+TEXT noentry(SB), $0
+	BYTE $'N'; BYTE $'o'; BYTE $' '; BYTE $'a';
+	BYTE $'c'; BYTE $'t'; BYTE $'i'; BYTE $'v';
+	BYTE $'e'; BYTE $' '; BYTE $'e'; BYTE $'n';
+	BYTE $'t'; BYTE $'r'; BYTE $'y'; BYTE $' ';
+	BYTE $'i'; BYTE $'n'; BYTE $' '; BYTE $'M';
+	BYTE $'B'; BYTE $'R';
+	BYTE $'\z';
+
+/* "Invalid MBR" */
+TEXT invalidMBR(SB), $0
+	BYTE $'I'; BYTE $'n'; BYTE $'v'; BYTE $'a';
+	BYTE $'l'; BYTE $'i'; BYTE $'d'; BYTE $' ';
+	BYTE $'M'; BYTE $'B'; BYTE $'R';
+	BYTE $'\z';
+
+/* "I/O error" */
+TEXT ioerror(SB), $0
+	BYTE $'I'; BYTE $'/'; BYTE $'O'; BYTE $' ';
+	BYTE $'e'; BYTE $'r'; BYTE $'r'; BYTE $'o';
+	BYTE $'r';
+	BYTE $'\z';
+
+/* "Invalid PBS" */
+TEXT invalidPBS(SB), $0
+	BYTE $'I'; BYTE $'n'; BYTE $'v'; BYTE $'a';
+	BYTE $'l'; BYTE $'i'; BYTE $'d'; BYTE $' ';
+	BYTE $'P'; BYTE $'B'; BYTE $'S';
+	BYTE $'\z';
+
+/* "\r\nPress almost any key to reboot..." */
+TEXT reboot(SB), $0
+	BYTE $'\r';BYTE $'\n';
+	BYTE $'P'; BYTE $'r'; BYTE $'e'; BYTE $'s';
+	BYTE $'s'; BYTE $' '; BYTE $'a'; BYTE $'l'; 
+	BYTE $'m'; BYTE $'o'; BYTE $'s'; BYTE $'t';
+	BYTE $' '; BYTE $'a'; BYTE $'n'; BYTE $'y';
+	BYTE $' '; BYTE $'k'; BYTE $'e'; BYTE $'y';
+	BYTE $' '; BYTE $'t'; BYTE $'o'; BYTE $' ';
+	BYTE $'r'; BYTE $'e'; BYTE $'b'; BYTE $'o';
+	BYTE $'o'; BYTE $'t'; BYTE $'.'; BYTE $'.';
+	BYTE $'.';
+	BYTE $'\z';
+
+/* "MBR..." */
+TEXT confidence(SB), $0
+	BYTE $'M'; BYTE $'B'; BYTE $'R'; BYTE $'.';
+	BYTE $'.'; BYTE $'.';
+	BYTE $'\z';
--- /dev/null
+++ b/os/boot/pc/mem.h
@@ -1,0 +1,114 @@
+/*
+ * Memory and machine-specific definitions.  Used in C and assembler.
+ */
+
+/*
+ * Sizes
+ */
+#define	BI2BY		8			/* bits per byte */
+#define	BI2WD		32			/* bits per word */
+#define	BY2WD		4			/* bytes per word */
+#define	BY2PG		4096			/* bytes per page */
+#define	WD2PG		(BY2PG/BY2WD)		/* words per page */
+#define	PGSHIFT		12			/* log(BY2PG) */
+#define	PGROUND(s)	(((s)+(BY2PG-1))&~(BY2PG-1))
+
+#define	MAXMACH		1			/* max # cpus system can run */
+
+/*
+ * Time
+ */
+#define	HZ		(100)				/* clock frequency */
+#define	MS2HZ		(1000/HZ)			/* millisec per clock tick */
+#define	TK2SEC(t)	((t)/HZ)			/* ticks to seconds */
+#define	TK2MS(x)	((x)*(1000/HZ))
+#define	MS2TK(t)	((((ulong)(t))*HZ)/1000)	/* milliseconds to ticks */
+
+/*
+ * Fundamental addresses
+ */
+#define IDTADDR		0x80000800		/* idt */
+#define APBOOTSTRAP	0x80001000		/* AP bootstrap code */
+#define CONFADDR	0x80001200		/* info passed from boot loader */
+#define CPU0PDB		0x80002000		/* bootstrap processor PDB */
+#define CPU0PTE		0x80003000		/* bootstrap processor PTE's for 0-4MB */
+#define MACHADDR	0x80004000		/* as seen by current processor */
+#define CPU0MACH	0x80005000		/* Mach for bootstrap processor */
+#define BIOSXCHG	0x80006000		/* To exchange data with the BIOS */
+#define	MACHSIZE	(BY2PG*8)		/* stack size */
+
+/*
+ *  Address spaces
+ *
+ *  Kernel is at 2GB-4GB
+ */
+#define	KZERO		0x80000000		/* base of kernel address space */
+#define	KTZERO		KZERO			/* first address in kernel text */
+#define ROMBIOS		(KZERO|0xF0000)
+
+/*
+ *  known 80386 segments (in GDT) and their selectors
+ */
+#define	NULLSEG	0	/* null segment */
+#define	KDSEG	1	/* kernel data/stack */
+#define	KESEG	2	/* kernel executable */	
+#define	UDSEG	3	/* user data/stack */
+#define	UESEG	4	/* user executable */
+#define	SYSGATE	5	/* system call gate */
+#define TSSSEG	6	/* task segment */
+
+#define SELGDT	(0<<3)	/* selector is in gdt */
+#define	SELLDT	(1<<3)	/* selector is in ldt */
+
+#define SELECTOR(i, t, p)	(((i)<<3) | (t) | (p))
+
+#define NULLSEL	SELECTOR(NULLSEG, SELGDT, 0)
+#define KESEL	SELECTOR(KESEG, SELGDT, 0)
+#define KDSEL	SELECTOR(KDSEG, SELGDT, 0)
+#define UESEL	SELECTOR(UESEG, SELGDT, 3)
+#define UDSEL	SELECTOR(UDSEG, SELGDT, 3)
+#define TSSSEL	SELECTOR(TSSSEG, SELGDT, 0)
+
+/*
+ *  fields in segment descriptors
+ */
+#define SEGDATA	(0x10<<8)	/* data/stack segment */
+#define SEGEXEC	(0x18<<8)	/* executable segment */
+#define	SEGTSS	(0x9<<8)	/* TSS segment */
+#define SEGCG	(0x0C<<8)	/* call gate */
+#define	SEGIG	(0x0E<<8)	/* interrupt gate */
+#define SEGTG	(0x0F<<8)	/* task gate */
+#define SEGTYPE	(0x1F<<8)
+
+#define SEGP	(1<<15)		/* segment present */
+#define SEGPL(x) ((x)<<13)	/* priority level */
+#define SEGB	(1<<22)		/* granularity 1==4k (for expand-down) */
+#define SEGG	(1<<23)		/* granularity 1==4k (for other) */
+#define SEGE	(1<<10)		/* expand down */
+#define SEGW	(1<<9)		/* writable (for data/stack) */
+#define	SEGR	(1<<9)		/* readable (for code) */
+#define SEGD	(1<<22)		/* default 1==32bit (for code) */
+
+/*
+ *  virtual MMU
+ */
+#define PTEMAPMEM	(1024*1024)	/* ??? */	
+#define SEGMAPSIZE	16		/* ??? */
+#define	PTEPERTAB	(PTEMAPMEM/BY2PG)	/* ??? */
+#define PPN(x)		((x)&~(BY2PG-1))
+
+/*
+ *  physical MMU
+ */
+#define	PTEVALID	(1<<0)
+#define	PTEUNCACHED	0		/* everything is uncached */
+#define	PTEWRITE	(1<<1)
+#define	PTERONLY	(0<<1)
+#define	PTEKERNEL	(0<<2)
+#define	PTEUSER		(1<<2)
+#define	PTESIZE		(1<<7)
+
+/*
+ *  flag register bits that we care about
+ */
+#define IFLAG	0x200
--- /dev/null
+++ b/os/boot/pc/memory.c
@@ -1,0 +1,504 @@
+/*
+ * Size memory and create the kernel page-tables on the fly while doing so.
+ * Called from main(), this code should only be run by the bootstrap processor.
+ */
+#include "u.h"
+#include "lib.h"
+#include "mem.h"
+#include "dat.h"
+#include "fns.h"
+#include "io.h"
+
+#define MEMDEBUG	0
+
+#define PDX(va)		((((ulong)(va))>>22) & 0x03FF)
+#define PTX(va)		((((ulong)(va))>>12) & 0x03FF)
+
+enum {
+	MemUPA		= 0,		/* unbacked physical address */
+	MemRAM		= 1,		/* physical memory */
+	MemUMB		= 2,		/* upper memory block (<16MB) */
+	NMemType	= 3,
+
+	KB		= 1024,
+
+	MemMinMB	= 4,		/* minimum physical memory (<=4MB) */
+	MemMaxMB	= 768,		/* maximum physical memory to check */
+
+	NMemBase	= 10,
+};
+
+typedef struct {
+	int	size;
+	ulong	addr;
+} Map;
+
+typedef struct {
+	char*	name;
+	Map*	map;
+	Map*	mapend;
+
+	Lock;
+} RMap;
+
+static Map mapupa[8];
+static RMap rmapupa = {
+	"unallocated unbacked physical memory",
+	mapupa,
+	&mapupa[7],
+};
+
+static Map xmapupa[8];
+static RMap xrmapupa = {
+	"unbacked physical memory",
+	xmapupa,
+	&xmapupa[7],
+};
+
+static Map mapram[8];
+static RMap rmapram = {
+	"physical memory",
+	mapram,
+	&mapram[7],
+};
+
+static Map mapumb[64];
+static RMap rmapumb = {
+	"upper memory block",
+	mapumb,
+	&mapumb[63],
+};
+
+static Map mapumbrw[8];
+static RMap rmapumbrw = {
+	"UMB device memory",
+	mapumbrw,
+	&mapumbrw[7],
+};
+
+void
+memdebug(void)
+{
+	Map *mp;
+	ulong maxpa, maxpa1, maxpa2;
+
+	if(MEMDEBUG == 0)
+		return;
+
+	maxpa = (nvramread(0x18)<<8)|nvramread(0x17);
+	maxpa1 = (nvramread(0x31)<<8)|nvramread(0x30);
+	maxpa2 = (nvramread(0x16)<<8)|nvramread(0x15);
+	print("maxpa = %luX -> %luX, maxpa1 = %luX maxpa2 = %luX\n",
+		maxpa, MB+maxpa*KB, maxpa1, maxpa2);
+
+	for(mp = rmapram.map; mp->size; mp++)
+		print("%8.8luX %8.8luX %8.8luX\n", mp->addr, (ulong)mp->size, mp->addr+mp->size);
+	for(mp = rmapumb.map; mp->size; mp++)
+		print("%8.8luX %8.8luX %8.8luX\n", mp->addr, (ulong)mp->size, mp->addr+mp->size);
+	for(mp = rmapumbrw.map; mp->size; mp++)
+		print("%8.8luX %8.8luX %8.8luX\n", mp->addr, (ulong)mp->size, mp->addr+mp->size);
+	for(mp = rmapupa.map; mp->size; mp++)
+		print("%8.8luX %8.8luX %8.8luX\n", mp->addr, (ulong)mp->size, mp->addr+mp->size);
+}
+
+void
+mapfree(RMap* rmap, ulong addr, ulong size)
+{
+	Map *mp;
+	ulong t;
+
+	if(size == 0)
+		return;
+
+	lock(rmap);
+	for(mp = rmap->map; mp->addr <= addr && mp->size; mp++)
+		;
+
+	if(mp > rmap->map && (mp-1)->addr+(mp-1)->size == addr){
+		(mp-1)->size += size;
+		if(addr+size == mp->addr){
+			(mp-1)->size += mp->size;
+			while(mp->size){
+				mp++;
+				(mp-1)->addr = mp->addr;
+				(mp-1)->size = mp->size;
+			}
+		}
+	}
+	else{
+		if(addr+size == mp->addr && mp->size){
+			mp->addr -= size;
+			mp->size += size;
+		}
+		else do{
+			if(mp >= rmap->mapend){
+				print("mapfree: %s: losing 0x%luX, %lud\n",
+					rmap->name, addr, size);
+				break;
+			}
+			t = mp->addr;
+			mp->addr = addr;
+			addr = t;
+			t = mp->size;
+			mp->size = size;
+			mp++;
+		}while(size = t);
+	}
+	unlock(rmap);
+}
+
+ulong
+mapalloc(RMap* rmap, ulong addr, int size, int align)
+{
+	Map *mp;
+	ulong maddr, oaddr;
+
+	lock(rmap);
+	for(mp = rmap->map; mp->size; mp++){
+		maddr = mp->addr;
+
+		if(addr){
+			/*
+			 * A specific address range has been given:
+			 *   if the current map entry is greater then
+			 *   the address is not in the map;
+			 *   if the current map entry does not overlap
+			 *   the beginning of the requested range then
+			 *   continue on to the next map entry;
+			 *   if the current map entry does not entirely
+			 *   contain the requested range then the range
+			 *   is not in the map.
+			 */
+			if(maddr > addr)
+				break;
+			if(mp->size < addr - maddr)	/* maddr+mp->size < addr, but no overflow */
+				continue;
+			if(addr - maddr > mp->size - size)	/* addr+size > maddr+mp->size, but no overflow */
+				break;
+			maddr = addr;
+		}
+
+		if(align > 0)
+			maddr = ((maddr+align-1)/align)*align;
+		if(mp->addr+mp->size-maddr < size)
+			continue;
+
+		oaddr = mp->addr;
+		mp->addr = maddr+size;
+		mp->size -= maddr-oaddr+size;
+		if(mp->size == 0){
+			do{
+				mp++;
+				(mp-1)->addr = mp->addr;
+			}while((mp-1)->size = mp->size);
+		}
+
+		unlock(rmap);
+		if(oaddr != maddr)
+			mapfree(rmap, oaddr, maddr-oaddr);
+
+		return maddr;
+	}
+	unlock(rmap);
+
+	return 0;
+}
+
+static void
+umbscan(void)
+{
+	uchar *p;
+
+	/*
+	 * Scan the Upper Memory Blocks (0xA0000->0xF0000) for pieces
+	 * which aren't used; they can be used later for devices which
+	 * want to allocate some virtual address space.
+	 * Check for two things:
+	 * 1) device BIOS ROM. This should start with a two-byte header
+	 *    of 0x55 0xAA, followed by a byte giving the size of the ROM
+	 *    in 512-byte chunks. These ROM's must start on a 2KB boundary.
+	 * 2) device memory. This is read-write.
+	 * There are some assumptions: there's VGA memory at 0xA0000 and
+	 * the VGA BIOS ROM is at 0xC0000. Also, if there's no ROM signature
+	 * at 0xE0000 then the whole 64KB up to 0xF0000 is theoretically up
+	 * for grabs; check anyway.
+	 */
+	p = KADDR(0xD0000);	/*RSC: changed from 0xC0000 */
+	while(p < (uchar*)KADDR(0xE0000)){
+		if (p[0] == 0x55 && p[1] == 0xAA) {
+			/* Skip p[2] chunks of 512 bytes.  Test for 0x55 AA before
+			     poking obtrusively, or else the Thinkpad X20 dies when
+			     setting up the cardbus (PB) */
+			p += p[2] * 512;
+			continue;
+		}
+
+		p[0] = 0xCC;
+		p[2*KB-1] = 0xCC;
+		if(p[0] != 0xCC || p[2*KB-1] != 0xCC){
+			p[0] = 0x55;
+			p[1] = 0xAA;
+			p[2] = 4;
+			if(p[0] == 0x55 && p[1] == 0xAA){
+				p += p[2]*512;
+				continue;
+			}
+			if(p[0] == 0xFF && p[1] == 0xFF)
+				mapfree(&rmapumb, PADDR(p), 2*KB);
+		}
+		else
+			mapfree(&rmapumbrw, PADDR(p), 2*KB);
+		p += 2*KB;
+	}
+
+	p = KADDR(0xE0000);
+	if(p[0] != 0x55 || p[1] != 0xAA){
+		p[0] = 0xCC;
+		p[64*KB-1] = 0xCC;
+		if(p[0] != 0xCC && p[64*KB-1] != 0xCC)
+			mapfree(&rmapumb, PADDR(p), 64*KB);
+	}
+}
+
+
+void
+meminit(ulong)
+{
+	/* A hack to initialize unbacked physical memory.  It's assumed PCI space is assigned by 
+	     the BIOS in the 0xF0000000 range and 9load never needs more than 0x2000... to run. These
+	     values leave ample space for memory allocations for uninitialized PCI cards (e.g. cardbus 
+	     cards).  (pb) */
+	ulong maxmem = 0x40000000;
+
+	umbscan();
+	mapfree(&rmapupa, maxmem, 0x00000000-maxmem);
+	if(MEMDEBUG)
+		memdebug();
+}
+
+ulong
+umbmalloc(ulong addr, int size, int align)
+{
+	ulong a;
+
+	if(a = mapalloc(&rmapumb, addr, size, align))
+		return (ulong)KADDR(a);
+
+	return 0;
+}
+
+void
+umbfree(ulong addr, int size)
+{
+	mapfree(&rmapumb, PADDR(addr), size);
+}
+
+ulong
+umbrwmalloc(ulong addr, int size, int align)
+{
+	ulong a;
+	uchar *p;
+
+	if(a = mapalloc(&rmapumbrw, addr, size, align))
+		return(ulong)KADDR(a);
+
+	/*
+	 * Perhaps the memory wasn't visible before
+	 * the interface is initialised, so try again.
+	 */
+	if((a = umbmalloc(addr, size, align)) == 0)
+		return 0;
+	p = (uchar*)a;
+	p[0] = 0xCC;
+	p[size-1] = 0xCC;
+	if(p[0] == 0xCC && p[size-1] == 0xCC)
+		return a;
+	umbfree(a, size);
+
+	return 0;
+}
+
+void
+umbrwfree(ulong addr, int size)
+{
+	mapfree(&rmapumbrw, PADDR(addr), size);
+}
+
+ulong*
+mmuwalk(ulong* pdb, ulong va, int level, int create)
+{
+	ulong pa, *table;
+
+	/*
+	 * Walk the page-table pointed to by pdb and return a pointer
+	 * to the entry for virtual address va at the requested level.
+	 * If the entry is invalid and create isn't requested then bail
+	 * out early. Otherwise, for the 2nd level walk, allocate a new
+	 * page-table page and register it in the 1st level.
+	 */
+	table = &pdb[PDX(va)];
+	if(!(*table & PTEVALID) && create == 0)
+		return 0;
+
+	switch(level){
+
+	default:
+		return 0;
+
+	case 1:
+		return table;
+
+	case 2:
+		if(*table & PTESIZE)
+			panic("mmuwalk2: va 0x%ux entry 0x%ux\n", va, *table);
+		if(!(*table & PTEVALID)){
+			pa = PADDR(ialloc(BY2PG, BY2PG));
+			*table = pa|PTEWRITE|PTEVALID;
+		}
+		table = KADDR(PPN(*table));
+
+		return &table[PTX(va)];
+	}
+}
+
+static Lock mmukmaplock;
+
+ulong
+mmukmap(ulong pa, ulong va, int size)
+{
+	ulong pae, *table, *pdb, pgsz, *pte, x;
+	int pse, sync;
+	extern int cpuidax, cpuiddx;
+
+	pdb = KADDR(getcr3());
+	if((cpuiddx & 0x08) && (getcr4() & 0x10))
+		pse = 1;
+	else
+		pse = 0;
+	sync = 0;
+
+	pa = PPN(pa);
+	if(va == 0)
+		va = (ulong)KADDR(pa);
+	else
+		va = PPN(va);
+
+	pae = pa + size;
+	lock(&mmukmaplock);
+	while(pa < pae){
+		table = &pdb[PDX(va)];
+		/*
+		 * Possibly already mapped.
+		 */
+		if(*table & PTEVALID){
+			if(*table & PTESIZE){
+				/*
+				 * Big page. Does it fit within?
+				 * If it does, adjust pgsz so the correct end can be
+				 * returned and get out.
+				 * If not, adjust pgsz up to the next 4MB boundary
+				 * and continue.
+				 */
+				x = PPN(*table);
+				if(x != pa)
+					panic("mmukmap1: pa 0x%ux  entry 0x%ux\n",
+						pa, *table);
+				x += 4*MB;
+				if(pae <= x){
+					pa = pae;
+					break;
+				}
+				pgsz = x - pa;
+				pa += pgsz;
+				va += pgsz;
+
+				continue;
+			}
+			else{
+				/*
+				 * Little page. Walk to the entry.
+				 * If the entry is valid, set pgsz and continue.
+				 * If not, make it so, set pgsz, sync and continue.
+				 */
+				pte = mmuwalk(pdb, va, 2, 0);
+				if(pte && *pte & PTEVALID){
+					x = PPN(*pte);
+					if(x != pa)
+						panic("mmukmap2: pa 0x%ux entry 0x%ux\n",
+							pa, *pte);
+					pgsz = BY2PG;
+					pa += pgsz;
+					va += pgsz;
+					sync++;
+
+					continue;
+				}
+			}
+		}
+
+		/*
+		 * Not mapped. Check if it can be mapped using a big page -
+		 * starts on a 4MB boundary, size >= 4MB and processor can do it.
+		 * If not a big page, walk the walk, talk the talk.
+		 * Sync is set.
+		 */
+		if(pse && (pa % (4*MB)) == 0 && (pae >= pa+4*MB)){
+			*table = pa|PTESIZE|PTEWRITE|PTEUNCACHED|PTEVALID;
+			pgsz = 4*MB;
+		}
+		else{
+			pte = mmuwalk(pdb, va, 2, 1);
+			*pte = pa|PTEWRITE|PTEUNCACHED|PTEVALID;
+			pgsz = BY2PG;
+		}
+		pa += pgsz;
+		va += pgsz;
+		sync++;
+	}
+	unlock(&mmukmaplock);
+
+	/*
+	 * If something was added
+	 * then need to sync up.
+	 */
+	if(sync)
+		putcr3(PADDR(pdb));
+
+	return pa;
+}
+
+ulong
+upamalloc(ulong addr, int size, int align)
+{
+	ulong ae, a;
+
+	USED(align);
+
+	if((a = mapalloc(&rmapupa, addr, size, align)) == 0){
+		memdebug();
+		return 0;
+	}
+
+	/*
+	 * This is a travesty, but they all are.
+	 */
+	ae = mmukmap(a, 0, size);
+
+	/*
+	 * Should check here that it was all delivered
+	 * and put it back and barf if not.
+	 */
+	USED(ae);
+
+	/*
+	 * Be very careful this returns a PHYSICAL address.
+	 */
+	return a;
+}
+
+void
+upafree(ulong pa, int size)
+{
+	USED(pa, size);
+}
+
--- /dev/null
+++ b/os/boot/pc/mkfile
@@ -1,0 +1,250 @@
+<../../../mkconfig
+objtype=386
+SYSTARG=$OSTARG
+OBJTYPE=386
+BIN=$ROOT/Inferno/$OBJTYPE
+LIBDIR=$ROOT/Inferno/$OBJTYPE/lib
+LIBDIRS=../libflate $ROOT/libkern
+LIBS=\
+	libflate\
+	libkern\
+
+LIBFILES=${LIBS:%=$LIBDIR/%.a}
+<$ROOT/mkfiles/mkfile-$SYSTARG-$OBJTYPE
+
+BIN=$ROOT/Inferno/$OBJTYPE
+
+TARG=\
+	9load\
+	9pxeload\
+	9loadlite\
+	9loaddebug\
+	9loadlitedebug\
+	ld.com\
+	mbr\
+	pbs\
+	pbslba\
+
+CORE=\
+	alarm.$O\
+	cga.$O\
+	clock.$O\
+	console.$O\
+	dosboot.$O\
+	devfloppy.$O\
+	dma.$O\
+	fs.$O\
+	ilock.$O\
+	kbd.$O\
+	kfsboot.$O\
+	print.$O\
+	queue.$O\
+	trap.$O\
+	getcallerpc.$O\
+
+LOAD=\
+	8250.$O\
+	apm.$O\
+	boot.$O\
+	cis.$O\
+	conf.$O\
+	devbios.$O\
+	devi82365.$O\
+	devpccard.$O\
+	devsd.$O\
+	inflate.$O\
+	load.$O\
+	memory.$O\
+	part.$O\
+	pci.$O\
+	sdata.$O\
+	sdmylex.$O\
+	sd53c8xx.$O\
+	sdiahci.$O\
+	sdscsi.$O\
+	sdaoe.$O\
+	sdbios.$O\
+
+ETHER=\
+	bootp.$O\
+	eipfmt.$O\
+	ether.$O\
+	ether2114x.$O\
+	ether2000.$O\
+	ether589.$O\
+	ether79c970.$O\
+	ether8003.$O\
+	ether8139.$O\
+	ether8169.$O\
+	ether82563.$O\
+	ether82557.$O\
+	ether83815.$O\
+	ether8390.$O\
+	etherec2t.$O\
+	etherelnk3.$O\
+	etherga620.$O\
+	etherigbe.$O\
+	ethermii.$O\
+	etherrhine.$O\
+	etherdp83820.$O\
+
+BCOM=\
+	bcom.$O\
+	bootld.$O\
+	devsd.$O\
+	memory.$O\
+	part.$O\
+	pci.$O\
+	sdata.$O\
+	sdscsi.$O\
+
+HFILES=\
+	lib.h\
+	mem.h\
+	dat.h\
+	fns.h\
+	io.h\
+	aoe.h\
+
+CFLAGS=-FVTw -I. -I$ROOT/Inferno/$OBJTYPE/include -I$ROOT/include
+
+all:V:	$TARG
+
+9load:	l.$O $CORE $LOAD $ETHER $LIBFILES
+	$LD -o $target -H3 -T0x80010000 -l $prereq
+	ls -l $target
+
+9pxeload:	l.$O $CORE $LOAD $ETHER $LIBFILES
+	$LD -o $target -H3 -T0x80007C00 -l $prereq
+	ls -l $target
+
+9loaddebug:	l.$O $CORE $LOAD $ETHER $LIBFILES
+	$LD -o $target -T0x80010000 -l $prereq
+	ls -l $target
+	# acid $target
+	# map({"text", 0x80010000, 0x80090000, 0x00000020})
+
+9loadlite:	l.$O $CORE $LOAD noether.$O $LIBFILES
+	$LD -o $target -H3 -T0x80010000 -l $prereq
+	ls -l $target
+
+9loadlitedebug:	l.$O $CORE $LOAD noether.$O $LIBFILES
+	$LD -o $target -T0x80010000 -l $prereq
+	ls -l $target
+	# acid $target
+	# map({"text", 0x80010000, 0x80090000, 0x00000020})
+
+ld.com:	ld.$O $CORE $BCOM $LIBFILES
+	$LD -o $target -H3 -T0x80080100 -l $prereq
+	ls -l $target
+
+lddebug:	ld.$O $CORE $BCOM $LIBFILES
+	$LD -o $target -T0x80080100 -l $prereq
+	ls -l $target
+	# acid $target
+	# map({"text", 0x80080100, 0x800B0000, 0x00000020})
+
+ld.$O:	l.s
+	$AS -DDOTCOM -o $target l.s
+
+lpxe.$O:	l.s
+	$AS -DPXE -o $target l.s
+
+%.$O:	%.s
+	$AS $stem.s
+
+%.$O:	%.c
+	$CC $CFLAGS $stem.c
+
+%.$O:	$HFILES
+
+l.$O pbs.$O pbslba.$O mbr.$O:	x16.h
+
+clock.$O floppy.$O trap.$O:	ureg.h
+bcom.$O conf.$O devfloppy.$O devsd.$O dosboot.$O fs.$O \
+	kfsboot.$O load.$O part.$O:	dosfs.h fs.h kfs.h
+ether.$O etherelnk3.$O:	etherif.h
+devsd.$O part.$O sdata.$O sdscsi.$O: sd.h
+bootp.$O:	ip.h
+
+mbr:	mbr.$O
+	$LD -o $target -H3 -T0x0600 -l $prereq
+	ls -l $target
+
+pbs&:	pbs%.$O
+	$LD -o $target -H3 -T0x7C00 -l $prereq
+	ls -l $target
+
+pbs&.debug:	pbs%.$O
+	$LD -o $target -T0x7C00 -l $prereq
+	ls -l $target
+	# acid $target
+	# map({"text", 0x7C00, 0x7E00, 0x00000020})
+	
+# added to cause libflate to be made automatically:
+
+$ROOT/Inferno/$OBJTYPE/lib/lib%.a:Q:	all-$SHELLTYPE
+	#
+
+rc-lib%.a nt-lib%.a:VQ:
+	echo '@{builtin cd ' $ROOT/lib$stem ';mk SHELLTYPE=$SHELLTYPE SYSTARG=$SYSTARG OBJTYPE=$OBJTYPE install}'
+	@{builtin cd  $ROOT/lib$stem ;mk 'SHELLTYPE='$SHELLTYPE 'SYSTARG='$SYSTARG 'OBJTYPE='$OBJTYPE install}
+
+sh-lib%.a:VQ:
+	echo "(cd $ROOT/lib$stem ; mk SHELLTYPE=$SHELLTYPE SYSTARG=$SYSTARG OBJTYPE=$OBJTYPE install)"
+	(cd $ROOT/lib$stem ; mk SHELLTYPE=$SHELLTYPE SYSTARG=$SYSTARG OBJTYPE=$OBJTYPE install)
+
+clean:
+	rm -f *.[$OS] [$OS].out y.tab.? y.debug y.output $TARG 9loaddebug lddebug
+
+install:V:
+	for (i in $TARG)
+		mk $MKFLAGS $i.install
+
+%.install:V:	$BIN/%
+
+$BIN/%:	%
+	cp $stem $BIN/$stem
+
+UPDATE=\
+	mkfile\
+	${CORE:%.$O=%.c}\
+	${LOAD:%.$O=%.c}\
+	${BCOM:%.$O=%.c}\
+	${ETHER:%.$O=%.c}\
+	$HFILES\
+	l.s\
+	noether.c\
+	pbs.s\
+	pbslba.s\
+	mbr.s\
+	x16.h\
+	ureg.h\
+	dosfs.h\
+	fs.h\
+	kfs.h\
+	etherif.h\
+	sd.h\
+	ip.h\
+	devfloppy.h\
+	${TARG:%=/386/%}\
+
+update:V:
+	update $UPDATEFLAGS $UPDATE
+
+
+%-sh:QV:
+		for i in $LIBDIRS
+		do
+			echo "(cd $i ; mk SHELLTYPE=$SHELLTYPE SYSTARG=$SYSTARG OBJTYPE=$OBJTYPE $stem)"
+			(cd $i; mk 'SHELLTYPE='$SHELLTYPE 'SYSTARG='$SYSTARG 'OBJTYPE='$OBJTYPE $stem)
+		done
+
+%-rc %-nt:QV:
+		for (i in $LIBDIRS)
+		{
+			echo '@{cd $i ; mk SHELLTYPE=$SHELLTYPE SYSTARG=$SYSTARG OBJTYPE=$OBJTYPE $stem}'
+			@{cd $i; mk 'SHELLTYPE='$SHELLTYPE 'SYSTARG='$SYSTARG 'OBJTYPE='$OBJTYPE $stem}
+		}
+
+nuke:V:		clean nuke-$SHELLTYPE
--- /dev/null
+++ b/os/boot/pc/noether.c
@@ -1,0 +1,46 @@
+#include "u.h"
+#include "lib.h"
+#include "mem.h"
+#include "dat.h"
+#include "fns.h"
+#include "io.h"
+
+int
+etherinit(void)
+{
+	return -1;
+}
+
+void
+etherinitdev(int, char*)
+{
+}
+
+void
+etherprintdevs(int)
+{
+}
+
+int
+etherrxpkt(int, Etherpkt*, int)
+{
+	return -1;
+}
+
+int
+ethertxpkt(int, Etherpkt*, int, int)
+{
+	return -1;
+}
+
+int
+bootpboot(int, char*, Boot*)
+{
+	return -1;
+}
+
+void*
+pxegetfspart(int, char*, int)
+{
+	return nil;
+}
--- /dev/null
+++ b/os/boot/pc/part.c
@@ -1,0 +1,344 @@
+#include	"u.h"
+#include	"lib.h"
+#include	"mem.h"
+#include	"dat.h"
+#include	"fns.h"
+
+#include	"sd.h"
+#include	"fs.h"
+
+enum {
+	Npart = 32
+};
+
+uchar *mbrbuf, *partbuf;
+int nbuf;
+#define trace 0
+
+int
+tsdbio(SDunit *unit, SDpart *part, void *a, vlong off, int mbr)
+{
+	uchar *b;
+
+	if(sdbio(unit, part, a, unit->secsize, off) != unit->secsize){
+		if(trace)
+			print("%s: read %lud at %lld failed\n", unit->name,
+				unit->secsize, (vlong)part->start*unit->secsize+off);
+		return -1;
+	}
+	b = a;
+	if(mbr && (b[0x1FE] != 0x55 || b[0x1FF] != 0xAA)){
+		if(trace)
+			print("%s: bad magic %.2ux %.2ux at %lld\n",
+				unit->name, b[0x1FE], b[0x1FF],
+				(vlong)part->start*unit->secsize+off);
+		return -1;
+	}
+	return 0;
+}
+
+/*
+ *  read partition table.  The partition table is just ascii strings.
+ */
+#define MAGIC "plan9 partitions"
+static void
+oldp9part(SDunit *unit)
+{
+	SDpart *pp;
+	char *field[3], *line[Npart+1];
+	ulong n, start, end;
+	int i;
+
+	/*
+	 *  We have some partitions already.
+	 */
+	pp = &unit->part[unit->npart];
+
+	/*
+	 * We prefer partition tables on the second to last sector,
+	 * but some old disks use the last sector instead.
+	 */
+	strcpy(pp->name, "partition");
+	pp->start = unit->sectors - 2;
+	pp->end = unit->sectors - 1;
+
+	if(tsdbio(unit, pp, partbuf, 0, 0) < 0)
+		return;
+
+	if(strncmp((char*)partbuf, MAGIC, sizeof(MAGIC)-1) != 0) {
+		/* not found on 2nd last sector; look on last sector */
+		pp->start++;
+		pp->end++;
+		if(tsdbio(unit, pp, partbuf, 0, 0) < 0)
+			return;
+		if(strncmp((char*)partbuf, MAGIC, sizeof(MAGIC)-1) != 0)
+			return;
+		print("%s: using old plan9 partition table on last sector\n", unit->name);
+	}else
+		print("%s: using old plan9 partition table on 2nd-to-last sector\n", unit->name);
+
+	/* we found a partition table, so add a partition partition */
+	unit->npart++;
+	partbuf[unit->secsize-1] = '\0';
+
+	/*
+	 * parse partition table
+	 */
+	n = getfields((char*)partbuf, line, Npart+1, '\n');
+	if(n && strncmp(line[0], MAGIC, sizeof(MAGIC)-1) == 0){
+		for(i = 1; i < n && unit->npart < SDnpart; i++){
+			if(getfields(line[i], field, 3, ' ') != 3)
+				break;
+			start = strtoull(field[1], 0, 0);
+			end = strtoull(field[2], 0, 0);
+			if(start >= end || end > unit->sectors)
+				break;
+			sdaddpart(unit, field[0], start, end);
+		}
+	}	
+}
+
+static void
+p9part(SDunit *unit, char *name)
+{
+	SDpart *p;
+	char *field[4], *line[Npart+1];
+	uvlong start, end;
+	int i, n;
+	
+	p = sdfindpart(unit, name);
+	if(p == nil)
+		return;
+
+	if(tsdbio(unit, p, partbuf, unit->secsize, 0) < 0)
+		return;
+	partbuf[unit->secsize-1] = '\0';
+
+	if(strncmp((char*)partbuf, "part ", 5) != 0)
+		return;
+
+	n = getfields((char*)partbuf, line, Npart+1, '\n');
+	if(n == 0)
+		return;
+	for(i = 0; i < n && unit->npart < SDnpart; i++){
+		if(strncmp(line[i], "part ", 5) != 0)
+			break;
+		if(getfields(line[i], field, 4, ' ') != 4)
+			break;
+		start = strtoull(field[2], 0, 0);
+		end = strtoull(field[3], 0, 0);
+		if(start >= end || end > unit->sectors)
+			break;
+		sdaddpart(unit, field[1], p->start+start, p->start+end);
+	}
+}
+
+int
+isdos(int t)
+{
+	return t==FAT12 || t==FAT16 || t==FATHUGE || t==FAT32 || t==FAT32X;
+}
+
+int
+isextend(int t)
+{
+	return t==EXTEND || t==EXTHUGE || t==LEXTEND;
+}
+
+/* 
+ * Fetch the first dos and all plan9 partitions out of the MBR partition table.
+ * We return -1 if we did not find a plan9 partition.
+ */
+static int
+mbrpart(SDunit *unit)
+{
+	Dospart *dp;
+	ulong taboffset, start, end;
+	ulong firstxpart, nxtxpart;
+	int havedos, i, nplan9;
+	char name[10];
+
+	taboffset = 0;
+	dp = (Dospart*)&mbrbuf[0x1BE];
+	if(1) {
+		/* get the MBR (allowing for DMDDO) */
+		if(tsdbio(unit, &unit->part[0], mbrbuf, (vlong)taboffset*unit->secsize, 1) < 0)
+			return -1;
+		for(i=0; i<4; i++)
+			if(dp[i].type == DMDDO) {
+				if(trace)
+					print("DMDDO partition found\n");
+				taboffset = 63;
+				if(tsdbio(unit, &unit->part[0], mbrbuf, (vlong)taboffset*unit->secsize, 1) < 0)
+					return -1;
+				i = -1;	/* start over */
+			}
+	}
+
+	/*
+	 * Read the partitions, first from the MBR and then
+	 * from successive extended partition tables.
+	 */
+	nplan9 = 0;
+	havedos = 0;
+	firstxpart = 0;
+	for(;;) {
+		if(tsdbio(unit, &unit->part[0], mbrbuf, (vlong)taboffset*unit->secsize, 1) < 0)
+			return -1;
+		if(trace) {
+			if(firstxpart)
+				print("%s ext %lud ", unit->name, taboffset);
+			else
+				print("%s mbr ", unit->name);
+		}
+		nxtxpart = 0;
+		for(i=0; i<4; i++) {
+			if(trace)
+				print("dp %d...", dp[i].type);
+			start = taboffset+GLONG(dp[i].start);
+			end = start+GLONG(dp[i].len);
+
+			if(dp[i].type == PLAN9) {
+				if(nplan9 == 0)
+					strcpy(name, "plan9");
+				else
+					sprint(name, "plan9.%d", nplan9);
+				sdaddpart(unit, name, start, end);
+				p9part(unit, name);
+				nplan9++;
+			}
+
+			/*
+			 * We used to take the active partition (and then the first
+			 * when none are active).  We have to take the first here,
+			 * so that the partition we call ``dos'' agrees with the
+			 * partition disk/fdisk calls ``dos''. 
+			 */
+			if(havedos==0 && isdos(dp[i].type)){
+				havedos = 1;
+				sdaddpart(unit, "dos", start, end);
+			}
+
+			/* nxtxpart is relative to firstxpart (or 0), not taboffset */
+			if(isextend(dp[i].type)){
+				nxtxpart = start-taboffset+firstxpart;
+				if(trace)
+					print("link %lud...", nxtxpart);
+			}
+		}
+		if(trace)
+			print("\n");
+
+		if(!nxtxpart)
+			break;
+		if(!firstxpart)
+			firstxpart = nxtxpart;
+		taboffset = nxtxpart;
+	}	
+	return nplan9 ? 0 : -1;
+}
+
+/*
+ * To facilitate booting from CDs, we create a partition for
+ * the boot floppy image embedded in a bootable CD.
+ */
+static int
+part9660(SDunit *unit)
+{
+	uchar buf[2048];
+	ulong a, n;
+	uchar *p;
+
+	if(unit->secsize != 2048)
+		return -1;
+
+	if(sdbio(unit, &unit->part[0], buf, 2048, 17*2048) < 0)
+		return -1;
+
+	if(buf[0] || strcmp((char*)buf+1, "CD001\x01EL TORITO SPECIFICATION") != 0)
+		return -1;
+
+	
+	p = buf+0x47;
+	a = p[0] | (p[1]<<8) | (p[2]<<16) | (p[3]<<24);
+
+	if(sdbio(unit, &unit->part[0], buf, 2048, a*2048) < 0)
+		return -1;
+
+	if(memcmp(buf, "\x01\x00\x00\x00", 4) != 0
+	|| memcmp(buf+30, "\x55\xAA", 2) != 0
+	|| buf[0x20] != 0x88)
+		return -1;
+
+	p = buf+0x28;
+	a = p[0] | (p[1]<<8) | (p[2]<<16) | (p[3]<<24);
+
+	switch(buf[0x21]){
+	case 0x01:
+		n = 1200*1024;
+		break;
+	case 0x02:
+		n = 1440*1024;
+		break;
+	case 0x03:
+		n = 2880*1024;
+		break;
+	default:
+		return -1;
+	}
+	n /= 2048;
+
+	print("found partition %s!cdboot; %lud+%lud\n", unit->name, a, n);
+	sdaddpart(unit, "cdboot", a, a+n);
+	return 0;
+}
+
+enum {
+	NEW = 1<<0,
+	OLD = 1<<1
+};
+
+void
+partition(SDunit *unit)
+{
+	int type;
+	char *p;
+
+	if(unit->part == 0)
+		return;
+
+	if(part9660(unit) == 0)
+		return;
+
+	p = getconf("partition");
+	if(p == nil)
+		p = defaultpartition;
+
+	if(p != nil && strncmp(p, "new", 3) == 0)
+		type = NEW;
+	else if(p != nil && strncmp(p, "old", 3) == 0)
+		type = OLD;
+	else
+		type = NEW|OLD;
+
+	if(nbuf < unit->secsize) {
+		free(mbrbuf);
+		free(partbuf);
+		mbrbuf = malloc(unit->secsize);
+		partbuf = malloc(unit->secsize);
+		if(mbrbuf==nil || partbuf==nil) {
+			free(mbrbuf);
+			free(partbuf);
+			partbuf = mbrbuf = nil;
+			nbuf = 0;
+			return;
+		}
+		nbuf = unit->secsize;
+	}
+
+	if((type & NEW) && mbrpart(unit) >= 0){
+		/* nothing to do */;
+	}
+	else if(type & OLD)
+		oldp9part(unit);
+}
--- /dev/null
+++ b/os/boot/pc/pbs.s
@@ -1,0 +1,372 @@
+/*
+ * FAT Partition Boot Sector. Loaded at 0x7C00:
+ *	8a pbs.s; 8l -o pbs -l -H3 -T0x7C00 pbs.8
+ * Will load the target at LOADSEG*16+LOADOFF, so the target
+ * should be probably be loaded with LOADOFF added to the
+ * -Taddress.
+ * If LOADSEG is a multiple of 64KB and LOADOFF is 0 then
+ * targets larger than 64KB can be loaded.
+ *
+ * This code uses the traditional INT13 BIOS interface and can
+ * therefore only access the first 8.4GB of the disc.
+ *
+ * It relies on the _volid field in the FAT header containing
+ * the LBA of the root directory.
+ */
+#include "x16.h"
+#include "mem.h"
+
+#define LOADSEG		(0x10000/16)	/* where to load code (64KB) */
+#define LOADOFF		0
+#define DIROFF		0x0200		/* where to read the root directory */
+
+/*
+ * FAT directory entry.
+ */
+#define Dname		0x00
+#define Dext		0x08
+#define Dattr		0x0B
+#define Dtime		0x16
+#define Ddate		0x18
+#define Dstart		0x1A
+#define Dlengthlo	0x1C
+#define Dlengthhi	0x1E
+
+#define Dirsz		0x20
+
+/*
+ * Data is kept on the stack, indexed by rBP.
+ */
+#define Xdap		0x00		/* disc address packet */
+#define Xrootsz		0x10		/* file data area */
+#define Xdrive		0x12		/* boot drive, passed by BIOS or MBR */
+#define Xtotal		0x14		/* sum of allocated data above */
+
+TEXT _magic(SB), $0
+	BYTE $0xEB; BYTE $0x3C;		/* jmp .+ 0x3C  (_start0x3E) */
+	BYTE $0x90			/* nop */
+TEXT _version(SB), $0
+	BYTE $0x00; BYTE $0x00; BYTE $0x00; BYTE $0x00;
+	BYTE $0x00; BYTE $0x00; BYTE $0x00; BYTE $0x00
+TEXT _sectsize(SB), $0
+	BYTE $0x00; BYTE $0x00
+TEXT _clustsize(SB), $0
+	BYTE $0x00
+TEXT _nresrv(SB), $0
+	BYTE $0x00; BYTE $0x00
+TEXT _nfats(SB), $0
+	BYTE $0x00
+TEXT _rootsize(SB), $0
+	BYTE $0x00; BYTE $0x00
+TEXT _volsize(SB), $0
+	BYTE $0x00; BYTE $0x00
+TEXT _mediadesc(SB), $0
+	BYTE $0x00
+TEXT _fatsize(SB), $0
+	BYTE $0x00; BYTE $0x00
+TEXT _trksize(SB), $0
+	BYTE $0x00; BYTE $0x00
+TEXT _nheads(SB), $0
+	BYTE $0x00; BYTE $0x00
+TEXT _nhiddenlo(SB), $0
+	BYTE $0x00; BYTE $0x00
+TEXT _nhiddenhi(SB), $0
+	BYTE $0x00; BYTE $0x00;
+TEXT _bigvolsize(SB), $0
+	BYTE $0x00; BYTE $0x00; BYTE $0x00; BYTE $0x00;
+TEXT _driveno(SB), $0
+	BYTE $0x00
+TEXT _reserved0(SB), $0
+	BYTE $0x00
+TEXT _bootsig(SB), $0
+	BYTE $0x00
+TEXT _volid(SB), $0
+	BYTE $0x00; BYTE $0x00; BYTE $0x00; BYTE $0x00;
+TEXT _label(SB), $0
+	BYTE $0x00; BYTE $0x00; BYTE $0x00; BYTE $0x00;
+	BYTE $0x00; BYTE $0x00; BYTE $0x00; BYTE $0x00
+	BYTE $0x00; BYTE $0x00; BYTE $0x00
+TEXT _type(SB), $0
+	BYTE $0x00; BYTE $0x00; BYTE $0x00; BYTE $0x00;
+	BYTE $0x00; BYTE $0x00; BYTE $0x00; BYTE $0x00;
+
+_start0x3E:
+	CLI
+	CLR(rAX)
+	MTSR(rAX, rSS)			/* 0000 -> rSS */
+	MTSR(rAX, rDS)			/* 0000 -> rDS, source segment */
+	MTSR(rAX, rES)
+	LWI(_magic-Xtotal(SB), rSP)
+	MW(rSP, rBP)			/* set the indexed-data pointer */
+
+	SBPB(rDL, Xdrive)		/* save the boot drive */
+
+	/* booting from a CD starts us at 7C0:0.  Move to 0:7C00 */
+	PUSHR(rAX)
+	LWI(_nxt(SB), rAX)
+	PUSHR(rAX)
+	BYTE $0xCB	/* FAR RET */
+
+TEXT _nxt(SB), $0
+	STI
+
+	LWI(confidence(SB), rSI)	/* for that warm, fuzzy feeling */
+	CALL16(BIOSputs(SB))
+
+	CALL16(dreset(SB))
+
+_jmp00:
+	LW(_volid(SB), rAX)		/* Xrootlo */
+	LW(_volid+2(SB), rDX)		/* Xroothi */
+
+	LWI(_magic+DIROFF(SB), rBX)
+	CALL16(BIOSread(SB))		/* read the root directory */
+
+	LWI((512/Dirsz), rBX)
+
+	LWI(_magic+DIROFF(SB), rDI)	/* compare first directory entry */
+
+_cmp00:
+	PUSHR(rDI)			/* save for later if it matches */
+	LWI(bootfile(SB), rSI)
+	LWI(Dattr, rCX)
+	REP
+	CMPSB
+	POPR(rDI)
+	JEQ _jmp02
+
+	DEC(rBX)
+	JEQ _jmp01
+
+	ADDI(Dirsz, rDI)
+	JMP _cmp00
+_jmp01:
+	CALL16(buggery(SB))
+
+_jmp02:
+	CLR(rBX)			/* a handy value */
+	LW(_rootsize(SB), rAX)		/* calculate and save Xrootsz */
+	LWI(Dirsz, rCX)
+	MUL(rCX)
+	LW(_sectsize(SB), rCX)
+	PUSHR(rCX)
+	DEC(rCX)
+	ADD(rCX, rAX)
+	ADC(rBX, rDX)
+	POPR(rCX)			/* _sectsize(SB) */
+	DIV(rCX)
+	PUSHR(rAX)			/* Xrootsz */
+
+	/*
+	 * rDI points to the matching directory entry.
+	 */
+	LXW(Dstart, xDI, rAX)		/* starting sector address */
+	DEC(rAX)			/* that's just the way it is */
+	DEC(rAX)
+	LB(_clustsize(SB), rCL)
+	CLRB(rCH)
+	MUL(rCX)
+	LW(_volid(SB), rCX)		/* Xrootlo */
+	ADD(rCX, rAX)
+	LW(_volid+2(SB), rCX)		/* Xroothi */
+	ADC(rCX, rDX)
+	POPR(rCX)			/* Xrootsz */
+	ADD(rCX, rAX)
+	ADC(rBX, rDX)
+
+	PUSHR(rAX)			/* calculate how many sectors to read */
+	PUSHR(rDX)
+	LXW(Dlengthlo, xDI, rAX)
+	LXW(Dlengthhi, xDI, rDX)
+	LW(_sectsize(SB), rCX)
+	PUSHR(rCX)
+	DEC(rCX)
+	ADD(rCX, rAX)
+	ADC(rBX, rDX)
+	POPR(rCX)			/* _sectsize(SB) */
+	DIV(rCX)
+	MW(rAX, rCX)
+	POPR(rDX)
+	POPR(rAX)
+
+	LWI(LOADSEG, rBX)		/* address to load into (seg+offset) */
+	MTSR(rBX, rES)			/*  seg */
+	LWI(LOADOFF, rBX)		/*  offset */
+
+_readboot:
+	CALL16(BIOSread(SB))		/* read the sector */
+
+	LW(_sectsize(SB), rDI)		/* bump addresses/counts */
+	ADD(rDI, rBX)
+	JCC _incsecno
+
+	MFSR(rES, rDI)			/* next 64KB segment */
+	ADDI(0x1000, rDI)
+	MTSR(rDI, rES)
+
+_incsecno:
+	CLR(rDI)
+	INC(rAX)
+	ADC(rDI, rDX)
+	LOOP _readboot
+
+	LWI(LOADSEG, rDI)		/* set rDS for loaded code */
+	MTSR(rDI, rDS)
+	FARJUMP16(LOADSEG, LOADOFF)	/* no deposit, no return */
+
+TEXT buggery(SB), $0
+	LWI(error(SB), rSI)
+	CALL16(BIOSputs(SB))
+
+_wait:
+	CLR(rAX)			/* wait for almost any key */
+	BIOSCALL(0x16)
+
+_reset:
+	CLR(rBX)			/* set ES segment for BIOS area */
+	MTSR(rBX, rES)
+
+	LWI(0x0472, rBX)		/* warm-start code address */
+	LWI(0x1234, rAX)		/* warm-start code */
+	POKEW				/* MOVW	AX, ES:[BX] */
+
+	FARJUMP16(0xFFFF, 0x0000)	/* reset */
+
+/*
+ * Read a sector from a disc. On entry:
+ *   rDX:rAX	sector number
+ *   rES:rBX	buffer address
+ * For BIOSCALL(0x13):
+ *   rAH	0x02
+ *   rAL	number of sectors to read (1)
+ *   rCH	low 8 bits of cylinder
+ *   rCL	high 2 bits of cylinder (7-6), sector (5-0)
+ *   rDH	head
+ *   rDL	drive
+ *   rES:rBX	buffer address
+ */
+TEXT BIOSread(SB), $0
+	LWI(5, rDI)			/* retry count (ATAPI ZIPs suck) */
+_retry:
+	PUSHA				/* may be trashed by BIOSCALL */
+	PUSHR(rBX)
+
+	LW(_trksize(SB), rBX)
+	LW(_nheads(SB), rDI)
+	IMUL(rDI, rBX)
+	OR(rBX, rBX)
+	JZ _ioerror
+
+_okay:
+	DIV(rBX)			/* cylinder -> rAX, track,sector -> rDX */
+
+	MW(rAX, rCX)			/* save cylinder */
+	ROLI(0x08, rCX)			/* swap rC[HL] */
+	SHLBI(0x06, rCL)		/* move high bits up */
+
+	MW(rDX, rAX)
+	CLR(rDX)
+	LW(_trksize(SB), rBX)
+
+	DIV(rBX)			/* head -> rAX, sector -> rDX */
+
+	INC(rDX)			/* sector numbers are 1-based */
+	ANDI(0x003F, rDX)		/* should not be necessary */
+	OR(rDX, rCX)
+
+	MW(rAX, rDX)
+	SHLI(0x08, rDX)			/* form head */
+	LBPB(Xdrive, rDL)		/* form drive */
+
+	POPR(rBX)
+	LWI(0x0201, rAX)		/* form command and sectors */
+	BIOSCALL(0x13)			/* CF set on failure */
+	JCC _BIOSreadret
+
+	POPA
+	DEC(rDI)			/* too many retries? */
+	JEQ _ioerror
+
+	CALL16(dreset(SB))
+	JMP _retry
+
+_ioerror:
+	LWI(ioerror(SB), rSI)
+	CALL16(BIOSputs(SB))
+	JMP _wait
+
+_BIOSreadret:
+	POPA
+	RET
+
+TEXT dreset(SB), $0
+	PUSHA
+	CLR(rAX)			/* rAH == 0 == reset disc system */
+	LBPB(Xdrive, rDL)
+	BIOSCALL(0x13)
+	ORB(rAH, rAH)			/* status (0 == success) */
+	POPA
+	JNE _ioerror
+	RET
+
+/*
+ * Output a string to the display.
+ * String argument is in rSI.
+ */
+TEXT BIOSputs(SB), $0
+	PUSHA
+	CLR(rBX)
+_BIOSputs:
+	LODSB
+	ORB(rAL, rAL)
+	JEQ _BIOSputsret
+
+	LBI(0x0E, rAH)
+	BIOSCALL(0x10)
+	JMP _BIOSputs
+
+_BIOSputsret:
+	POPA
+	RET
+
+/* "Bad format or I/O error\r\nPress almost any key to reboot..."*/
+TEXT error(SB), $0
+	BYTE $'B'; BYTE $'a'; BYTE $'d'; BYTE $' ';
+	BYTE $'f'; BYTE $'o'; BYTE $'r'; BYTE $'m';
+	BYTE $'a'; BYTE $'t'; BYTE $' '; BYTE $'o';
+	BYTE $'r'; BYTE $' ';
+/* "I/O error\r\nPress almost any key to reboot..." */
+TEXT ioerror(SB), $0
+	BYTE $'I'; BYTE $'/'; BYTE $'O'; BYTE $' ';
+	BYTE $'e'; BYTE $'r'; BYTE $'r'; BYTE $'o';
+	BYTE $'r'; BYTE $'\r';BYTE $'\n';
+	BYTE $'P'; BYTE $'r'; BYTE $'e'; BYTE $'s';
+	BYTE $'s'; BYTE $' '; BYTE $'a'; BYTE $' ';
+	BYTE $'k'; BYTE $'e'; BYTE $'y';
+	BYTE $' '; BYTE $'t'; BYTE $'o'; BYTE $' ';
+	BYTE $'r'; BYTE $'e'; BYTE $'b'; BYTE $'o';
+	BYTE $'o'; BYTE $'t';
+	BYTE $'.'; BYTE $'.'; BYTE $'.';
+	BYTE $'\z';
+
+#ifdef USEBCOM
+/* "B       COM" */
+TEXT bootfile(SB), $0
+	BYTE $'B'; BYTE $' '; BYTE $' '; BYTE $' ';
+	BYTE $' '; BYTE $' '; BYTE $' '; BYTE $' ';
+	BYTE $'C'; BYTE $'O'; BYTE $'M';
+	BYTE $'\z';
+#else
+/* "9LOAD      " */
+TEXT bootfile(SB), $0
+	BYTE $'9'; BYTE $'L'; BYTE $'O'; BYTE $'A';
+	BYTE $'D'; BYTE $' '; BYTE $' '; BYTE $' ';
+	BYTE $' '; BYTE $' '; BYTE $' ';
+	BYTE $'\z';
+#endif /* USEBCOM */
+
+/* "PBS..." */
+TEXT confidence(SB), $0
+	BYTE $'P'; BYTE $'B'; BYTE $'S'; BYTE $'1'; 
+	BYTE $'.'; BYTE $'.'; BYTE $'.';
+	BYTE $'\z';
binary files /dev/null b/os/boot/pc/pbsdisk differ
--- /dev/null
+++ b/os/boot/pc/pbsdisk.s
@@ -1,0 +1,344 @@
+/*
+ * Debugging boot sector.  Reads the first directory
+ * sector from disk and displays it.
+ *
+ * It relies on the _volid field in the FAT header containing
+ * the LBA of the root directory.
+ */
+#include "x16.h"
+
+#define DIROFF		0x00200		/* where to read the root directory (offset) */
+#define LOADSEG		(0x10000/16)	/* where to load code (64KB) */
+#define LOADOFF		0
+
+/*
+ * FAT directory entry.
+ */
+#define Dname		0x00
+#define Dext		0x08
+#define Dattr		0x0B
+#define Dtime		0x16
+#define Ddate		0x18
+#define Dstart		0x1A
+#define Dlengthlo	0x1C
+#define Dlengthhi	0x1E
+
+#define Dirsz		0x20
+
+/*
+ * We keep data on the stack, indexed by rBP.
+ */
+#define Xdrive		0x00		/* boot drive, passed by BIOS in rDL */
+#define Xrootlo		0x02		/* offset of root directory */
+#define Xroothi		0x04
+#define Xrootsz		0x06		/* file data area */
+#define Xtotal		0x08		/* sum of allocated data above */
+#define Xdap		0x00		/* disc address packet */
+
+TEXT _magic(SB), $0
+	BYTE $0xEB; BYTE $0x3C;		/* jmp .+ 0x3C  (_start0x3E) */
+	BYTE $0x90			/* nop */
+TEXT _version(SB), $0
+	BYTE $0x00; BYTE $0x00; BYTE $0x00; BYTE $0x00;
+	BYTE $0x00; BYTE $0x00; BYTE $0x00; BYTE $0x00
+TEXT _sectsize(SB), $0
+	BYTE $0x00; BYTE $0x00
+TEXT _clustsize(SB), $0
+	BYTE $0x00
+TEXT _nresrv(SB), $0
+	BYTE $0x00; BYTE $0x00
+TEXT _nfats(SB), $0
+	BYTE $0x00
+TEXT _rootsize(SB), $0
+	BYTE $0x00; BYTE $0x00
+TEXT _volsize(SB), $0
+	BYTE $0x00; BYTE $0x00
+TEXT _mediadesc(SB), $0
+	BYTE $0x00
+TEXT _fatsize(SB), $0
+	BYTE $0x00; BYTE $0x00
+TEXT _trksize(SB), $0
+	BYTE $0x00; BYTE $0x00
+TEXT _nheads(SB), $0
+	BYTE $0x00; BYTE $0x00
+TEXT _nhiddenlo(SB), $0
+	BYTE $0x00; BYTE $0x00
+TEXT _nhiddenhi(SB), $0
+	BYTE $0x00; BYTE $0x00;
+TEXT _bigvolsize(SB), $0
+	BYTE $0x00; BYTE $0x00; BYTE $0x00; BYTE $0x00;
+TEXT _driveno(SB), $0
+	BYTE $0x00
+TEXT _reserved0(SB), $0
+	BYTE $0x00
+TEXT _bootsig(SB), $0
+	BYTE $0x00
+TEXT _volid(SB), $0
+	BYTE $0x00; BYTE $0x00; BYTE $0x00; BYTE $0x00;
+TEXT _label(SB), $0
+	BYTE $0x00; BYTE $0x00; BYTE $0x00; BYTE $0x00;
+	BYTE $0x00; BYTE $0x00; BYTE $0x00; BYTE $0x00
+	BYTE $0x00; BYTE $0x00; BYTE $0x00
+TEXT _type(SB), $0
+	BYTE $0x00; BYTE $0x00; BYTE $0x00; BYTE $0x00;
+	BYTE $0x00; BYTE $0x00; BYTE $0x00; BYTE $0x00;
+
+_start0x3E:
+	CLI
+	CLR(rAX)
+	MTSR(rAX, rSS)			/* 0000 -> rSS */
+	MTSR(rAX, rDS)			/* 0000 -> rDS, source segment */
+	MTSR(rAX, rES)
+	LWI(_magic-Xtotal(SB), rSP)
+	MW(rSP, rBP)			/* set the indexed-data pointer */
+	SBPB(rDL, Xdrive)		/* save the boot drive */
+
+	/* VMware starts us at 7C0:0.  Move to 0:7C00 */
+	PUSHR(rAX)
+	LWI(_nxt(SB), rAX)
+	PUSHR(rAX)
+	BYTE $0xCB	/* FAR RET */
+
+TEXT _nxt(SB), $0
+	STI
+	LWI(confidence(SB), rSI)	/* for that warm, fuzzy feeling */
+	CALL16(BIOSputs(SB))
+
+	CALL16(dreset(SB))
+
+_jmp00:
+	LW(_volid(SB), rAX)		/* Xrootlo */
+	LW(_volid+2(SB), rDX)		/* Xroothi */
+
+	LWI(_magic+DIROFF(SB), rBX)
+	CALL16(BIOSread(SB))		/* read the root directory */
+
+	CALL16(printnl(SB))
+	LWI(_magic+DIROFF(SB), rBX)
+	LWI((512/2), rCX)
+	CALL16(printbuf(SB))
+
+xloop:
+	JMP xloop
+
+
+TEXT buggery(SB), $0
+	LWI(error(SB), rSI)
+	CALL16(BIOSputs(SB))
+
+TEXT quietbuggery(SB), $0
+xbuggery:
+	JMP xbuggery
+
+/*
+ * Read a sector from a disc. On entry:
+ *   rDX:rAX	sector number
+ *   rES:rBX	buffer address
+ * For BIOSCALL(0x13):
+ *   rAH	0x02
+ *   rAL	number of sectors to read (1)
+ *   rCH	low 8 bits of cylinder
+ *   rCL	high 2 bits of cylinder (7-6), sector (5-0)
+ *   rDH	head
+ *   rDL	drive
+ *   rES:rBX	buffer address
+ */
+TEXT BIOSread(SB), $0
+	LWI(5, rDI)			/* retry count (ATAPI ZIPs suck) */
+_retry:
+	PUSHA				/* may be trashed by BIOSCALL */
+	PUSHR(rBX)
+
+	LW(_trksize(SB), rBX)
+	LW(_nheads(SB), rDI)
+	IMUL(rDI, rBX)
+	OR(rBX, rBX)
+	JZ _ioerror
+
+_okay:
+	DIV(rBX)			/* cylinder -> rAX, track,sector -> rDX */
+
+	MW(rAX, rCX)			/* save cylinder */
+	ROLI(0x08, rCX)			/* swap rC[HL] */
+	SHLBI(0x06, rCL)		/* move high bits up */
+
+	MW(rDX, rAX)
+	CLR(rDX)
+	LW(_trksize(SB), rBX)
+
+	DIV(rBX)			/* head -> rAX, sector -> rDX */
+
+	INC(rDX)			/* sector numbers are 1-based */
+	ANDI(0x003F, rDX)		/* should not be necessary */
+	OR(rDX, rCX)
+
+	MW(rAX, rDX)
+	SHLI(0x08, rDX)			/* form head */
+	LBPB(Xdrive, rDL)		/* form drive */
+
+	POPR(rBX)
+	LWI(0x0201, rAX)		/* form command and sectors */
+	BIOSCALL(0x13)			/* CF set on failure */
+	JCC _BIOSreadret
+
+	POPA
+	DEC(rDI)			/* too many retries? */
+	JEQ _ioerror
+
+	CALL16(dreset(SB))
+	JMP _retry
+
+_ioerror:
+	LWI(ioerror(SB), rSI)
+	CALL16(BIOSputs(SB))
+	JMP xbuggery
+
+_BIOSreadret:
+	POPA
+	RET
+
+TEXT dreset(SB), $0
+	PUSHA
+	CLR(rAX)			/* rAH == 0 == reset disc system */
+	LBPB(Xdrive, rDL)
+	BIOSCALL(0x13)
+	ORB(rAH, rAH)			/* status (0 == success) */
+	POPA
+	JNE _ioerror
+	RET
+
+TEXT printsharp(SB), $0
+	LWI(sharp(SB), rSI)
+_doprint:
+	CALL16(BIOSputs(SB))
+	RET
+
+TEXT printspace(SB), $0
+	LWI(space(SB), rSI)
+	JMP _doprint
+
+TEXT printnl(SB), $0
+	LWI(nl(SB), rSI)
+	JMP _doprint
+
+/*
+ * Output a string to the display.
+ * String argument is in rSI.
+ */
+TEXT BIOSputs(SB), $0
+	PUSHA
+	CLR(rBX)
+_BIOSputs:
+	LODSB
+	ORB(rAL, rAL)
+	JEQ _BIOSputsret
+
+	LBI(0x0E, rAH)
+	BIOSCALL(0x10)
+	JMP _BIOSputs
+
+_BIOSputsret:
+	POPA
+	RET
+
+/*
+ * Output a register to the display.
+ */
+TEXT printAX(SB), $0
+	PUSHW(rAX)
+	PUSHW(rBX)
+	PUSHW(rCX)
+	PUSHW(rDI)
+
+	LWI(4, rCX)
+	LWI(numbuf+4(SB), rSI)
+
+_nextchar:
+	DEC(rSI)
+	MW(rAX, rBX)
+	ANDI(0x000F, rBX)
+	ADDI(0x30, rBX)	/* 0x30 = '0' */
+	CMPI(0x39, rBX)	/* 0x39 = '9' */
+	JLE _dowrite
+	ADDI(0x07, rBX)	/* 0x07 = 'A'-(1+'9')*/
+
+_dowrite:
+	SXB(rBL, 0, xSI)
+	SHRI(4, rAX)
+
+	DEC(rCX)
+	JNE _nextchar
+
+	LWI(numbuf(SB), rSI)
+	CALL16(BIOSputs(SB))
+
+	POPW(rDI)
+	POPW(rCX)
+	POPW(rBX)
+	POPW(rAX)
+
+	CALL16(printspace(SB))
+	RET
+
+TEXT printDXAX(SB), $0
+	PUSHW(rAX)
+	MW(rDX, rAX)
+	CALL16(printAX(SB))
+	POPW(rAX)
+	CALL16(printAX(SB))
+	RET
+
+TEXT printBX(SB), $0
+	PUSHW(rAX)
+	MW(rBX, rAX)
+	CALL16(printAX(SB))
+	POPW(rAX)
+	RET
+
+/*
+ * Output some number of words to the display
+ * rDS:rDI - buffer
+ * rCX: number of words
+ */
+TEXT printbuf(SB), $0
+	PUSHW(rAX)
+	PUSHW(rBX)
+	PUSHW(rCX)
+
+_nextword:
+	LXW(0, xBX, rAX)
+	CALL16(printAX(SB))
+	INC(rBX)
+	INC(rBX)
+	DEC(rCX)
+	JNE _nextword
+
+	POPW(rCX)
+	POPW(rBX)
+	POPW(rAX)
+	RET
+
+TEXT error(SB), $0
+	BYTE $'E'; 
+
+TEXT ioerror(SB), $0
+	BYTE $'I'; 
+
+TEXT nl(SB), $0
+	BYTE $'\r';
+	BYTE $'\n';
+	BYTE $'\z';
+
+TEXT numbuf(SB), $0
+	BYTE $'X'; BYTE $'X'; BYTE $'X'; BYTE $'X';
+	BYTE $'\z';
+
+TEXT space(SB), $0
+	BYTE $' ';
+	BYTE $'\z';
+
+TEXT sharp(SB), $0
+	BYTE $'#'; BYTE $'\z';
+
+TEXT confidence(SB), $0
+	BYTE $'P'; BYTE $'\z'
binary files /dev/null b/os/boot/pc/pbsdisklba differ
--- /dev/null
+++ b/os/boot/pc/pbsdisklba.s
@@ -1,0 +1,327 @@
+/*
+ * Debugging boot sector.  Reads the first directory
+ * sector from disk and displays it.
+ *
+ * It relies on the _volid field in the FAT header containing
+ * the LBA of the root directory.
+ */
+#include "x16.h"
+
+#define DIROFF		0x00200		/* where to read the root directory (offset) */
+#define LOADSEG		(0x10000/16)	/* where to load code (64KB) */
+#define LOADOFF		0
+
+/*
+ * FAT directory entry.
+ */
+#define Dname		0x00
+#define Dext		0x08
+#define Dattr		0x0B
+#define Dtime		0x16
+#define Ddate		0x18
+#define Dstart		0x1A
+#define Dlengthlo	0x1C
+#define Dlengthhi	0x1E
+
+#define Dirsz		0x20
+
+/*
+ * We keep data on the stack, indexed by rBP.
+ */
+#define Xdrive		0x00		/* boot drive, passed by BIOS in rDL */
+#define Xrootlo		0x02		/* offset of root directory */
+#define Xroothi		0x04
+#define Xrootsz		0x06		/* file data area */
+#define Xtotal		0x08		/* sum of allocated data above */
+#define Xdap		0x00		/* disc address packet */
+
+TEXT _magic(SB), $0
+	BYTE $0xEB; BYTE $0x3C;		/* jmp .+ 0x3C  (_start0x3E) */
+	BYTE $0x90			/* nop */
+TEXT _version(SB), $0
+	BYTE $0x00; BYTE $0x00; BYTE $0x00; BYTE $0x00;
+	BYTE $0x00; BYTE $0x00; BYTE $0x00; BYTE $0x00
+TEXT _sectsize(SB), $0
+	BYTE $0x00; BYTE $0x00
+TEXT _clustsize(SB), $0
+	BYTE $0x00
+TEXT _nresrv(SB), $0
+	BYTE $0x00; BYTE $0x00
+TEXT _nfats(SB), $0
+	BYTE $0x00
+TEXT _rootsize(SB), $0
+	BYTE $0x00; BYTE $0x00
+TEXT _volsize(SB), $0
+	BYTE $0x00; BYTE $0x00
+TEXT _mediadesc(SB), $0
+	BYTE $0x00
+TEXT _fatsize(SB), $0
+	BYTE $0x00; BYTE $0x00
+TEXT _trksize(SB), $0
+	BYTE $0x00; BYTE $0x00
+TEXT _nheads(SB), $0
+	BYTE $0x00; BYTE $0x00
+TEXT _nhiddenlo(SB), $0
+	BYTE $0x00; BYTE $0x00
+TEXT _nhiddenhi(SB), $0
+	BYTE $0x00; BYTE $0x00;
+TEXT _bigvolsize(SB), $0
+	BYTE $0x00; BYTE $0x00; BYTE $0x00; BYTE $0x00;
+TEXT _driveno(SB), $0
+	BYTE $0x00
+TEXT _reserved0(SB), $0
+	BYTE $0x00
+TEXT _bootsig(SB), $0
+	BYTE $0x00
+TEXT _volid(SB), $0
+	BYTE $0x00; BYTE $0x00; BYTE $0x00; BYTE $0x00;
+TEXT _label(SB), $0
+	BYTE $0x00; BYTE $0x00; BYTE $0x00; BYTE $0x00;
+	BYTE $0x00; BYTE $0x00; BYTE $0x00; BYTE $0x00
+	BYTE $0x00; BYTE $0x00; BYTE $0x00
+TEXT _type(SB), $0
+	BYTE $0x00; BYTE $0x00; BYTE $0x00; BYTE $0x00;
+	BYTE $0x00; BYTE $0x00; BYTE $0x00; BYTE $0x00;
+
+_start0x3E:
+	CLI
+	CLR(rAX)
+	MTSR(rAX, rSS)			/* 0000 -> rSS */
+	MTSR(rAX, rDS)			/* 0000 -> rDS, source segment */
+	MTSR(rAX, rES)
+	LWI(_magic-Xtotal(SB), rSP)
+	MW(rSP, rBP)			/* set the indexed-data pointer */
+
+	SBPB(rDL, Xdrive)		/* save the boot drive */
+
+	STI
+
+	LWI(confidence(SB), rSI)	/* for that warm, fuzzy feeling */
+	CALL(BIOSputs(SB))
+
+	LBI(0x41, rAH)			/* check extensions present */
+	LWI(0x55AA, rBX)
+	LXB(Xdrive, xBP, rDL)		/* drive */
+	SYSCALL(0x13)			/* CF set on failure */
+	JCS _jmp01
+	CMPI(0xAA55, rBX)
+	JNE _jmp01
+	ANDI(0x0001, rCX)
+	JEQ _jmp01
+
+					/* rCX contains 0x0001 */
+	SBPWI(0x0010, Xdap+0)		/* reserved + packet size */
+	SBPW(rCX, Xdap+2)		/* reserved + # of blocks to transfer */
+
+	DEC(rCX)
+	SBPW(rCX, Xdap+12)
+	SBPW(rCX, Xdap+14)
+
+/* BIOSread will do this 	CALL(dreset(SB)) */
+
+_jmp00:
+	LW(_volid(SB), rAX)		/* Xrootlo */
+	LW(_volid+2(SB), rDX)		/* Xroothi */
+
+	LWI(_magic+DIROFF(SB), rBX)
+	CALL(BIOSread(SB))		/* read the root directory */
+
+	CALL(printnl(SB))
+	LWI(_magic+DIROFF(SB), rBX)
+	LWI((512/2), rCX)
+	CALL(printbuf(SB))
+
+xloop:
+	JMP xloop
+
+
+_jmp01:
+
+TEXT buggery(SB), $0
+	LWI(error(SB), rSI)
+	CALL(BIOSputs(SB))
+
+xbuggery:
+	JMP xbuggery
+
+/*
+ * Read a sector from a disc. On entry:
+ *   rDX:rAX	sector number
+ *   rES:rBX	buffer address
+ */
+TEXT BIOSread(SB), $0
+	LWI(5, rDI)			/* retry count (ATAPI ZIPs suck) */
+_retry:
+	PUSHA				/* may be trashed by SYSCALL */
+
+	SBPW(rBX, Xdap+4)		/* transfer buffer :offset */
+	MFSR(rES, rDI)			/* transfer buffer seg: */
+	SBPW(rDI, Xdap+6)
+	SBPW(rAX, Xdap+8)		/* LBA (64-bits) */
+	SBPW(rDX, Xdap+10)
+
+	MW(rBP, rSI)			/* disk address packet */
+	LBI(0x42, rAH)			/* extended read */
+	LBPB(Xdrive, rDL)		/* form drive */
+	SYSCALL(0x13)			/* CF set on failure */
+	JCC _BIOSreadret
+
+	POPA
+	DEC(rDI)			/* too many retries? */
+	JEQ _ioerror
+
+	CALL(dreset(SB))
+	JMP _retry
+
+_ioerror:
+	LWI(ioerror(SB), rSI)
+	CALL(BIOSputs(SB))
+	JMP xbuggery
+
+_BIOSreadret:
+	POPA
+	RET
+
+TEXT dreset(SB), $0
+	PUSHA
+	CLR(rAX)			/* rAH == 0 == reset disc system */
+	LBPB(Xdrive, rDL)
+	SYSCALL(0x13)
+	ORB(rAH, rAH)			/* status (0 == success) */
+	POPA
+	JNE _ioerror
+	RET
+
+
+TEXT printsharp(SB), $0
+	LWI(sharp(SB), rSI)
+_doprint:
+	CALL(BIOSputs(SB))
+	RET
+
+TEXT printspace(SB), $0
+	LWI(space(SB), rSI)
+	JMP _doprint
+
+TEXT printnl(SB), $0
+	LWI(nl(SB), rSI)
+	JMP _doprint
+
+/*
+ * Output a string to the display.
+ * String argument is in rSI.
+ */
+TEXT BIOSputs(SB), $0
+	PUSHA
+	CLR(rBX)
+_BIOSputs:
+	LODSB
+	ORB(rAL, rAL)
+	JEQ _BIOSputsret
+
+	LBI(0x0E, rAH)
+	SYSCALL(0x10)
+	JMP _BIOSputs
+
+_BIOSputsret:
+	POPA
+	RET
+
+/*
+ * Output a register to the display.
+ */
+TEXT printAX(SB), $0
+	PUSHW(rAX)
+	PUSHW(rBX)
+	PUSHW(rCX)
+	PUSHW(rDI)
+
+	LWI(4, rCX)
+	LWI(numbuf+4(SB), rSI)
+
+_nextchar:
+	DEC(rSI)
+	MW(rAX, rBX)
+	ANDI(0x000F, rBX)
+	ADDI(0x30, rBX)	/* 0x30 = '0' */
+	CMPI(0x39, rBX)	/* 0x39 = '9' */
+	JLE _dowrite
+	ADDI(0x07, rBX)	/* 0x07 = 'A'-(1+'9')*/
+
+_dowrite:
+	SXB(rBL, 0, xSI)
+	SHRI(4, rAX)
+
+	DEC(rCX)
+	JNE _nextchar
+
+	LWI(numbuf(SB), rSI)
+	CALL(BIOSputs(SB))
+
+	POPW(rDI)
+	POPW(rCX)
+	POPW(rBX)
+	POPW(rAX)
+
+	CALL(printspace(SB))
+	RET
+
+TEXT printDXAX(SB), $0
+	PUSHW(rAX)
+	MW(rDX, rAX)
+	CALL(printAX(SB))
+	POPW(rAX)
+	CALL(printAX(SB))
+	RET
+
+TEXT printBX(SB), $0
+	PUSHW(rAX)
+	MW(rBX, rAX)
+	CALL(printAX(SB))
+	POPW(rAX)
+	RET
+
+/*
+ * Output some number of words to the display
+ * rDS:rDI - buffer
+ * rCX: number of words
+ */
+TEXT printbuf(SB), $0
+	PUSHW(rAX)
+	PUSHW(rBX)
+	PUSHW(rCX)
+
+_nextword:
+	LXW(0, xBX, rAX)
+	CALL(printAX(SB))
+	INC(rBX)
+	INC(rBX)
+	DEC(rCX)
+	JNE _nextword
+
+	POPW(rCX)
+	POPW(rBX)
+	POPW(rAX)
+	RET
+
+TEXT error(SB), $0
+	BYTE $'E'; 
+
+TEXT ioerror(SB), $0
+	BYTE $'I'; 
+
+TEXT nl(SB), $0
+	BYTE $'\r';
+	BYTE $'\n';
+	BYTE $'\z';
+
+TEXT numbuf(SB), $0
+	BYTE $'X'; BYTE $'X'; BYTE $'X'; BYTE $'X';
+	BYTE $'\z';
+
+TEXT space(SB), $0
+	BYTE $' ';
+	BYTE $'\z';
+
+TEXT sharp(SB), $0
+	BYTE $'#'; BYTE $'\z';
--- /dev/null
+++ b/os/boot/pc/pbslba.s
@@ -1,0 +1,363 @@
+/*
+ * FAT Partition Boot Sector. Loaded at 0x7C00:
+ *	8a pbslba.s; 8l -o pbslba -l -H3 -T0x7C00 pbslba.8
+ * Will load the target at LOADSEG*16+LOADOFF, so the target
+ * should be probably be loaded with LOADOFF added to the
+ * -Taddress.
+ * If LOADSEG is a multiple of 64KB and LOADOFF is 0 then
+ * targets larger than 64KB can be loaded.
+ *
+ * This code is uses Enhanced BIOS Services for Disc Drives and
+ * can be used with discs up to 137GB in capacity.
+ *
+ * It relies on the _volid field in the FAT header containing
+ * the LBA of the root directory.
+ */
+#include "x16.h"
+#include "mem.h"
+
+#define LOADSEG		(0x10000/16)	/* where to load code (64KB) */
+#define LOADOFF		0
+#define DIROFF		0x0200		/* where to read the root directory */
+
+/*
+ * FAT directory entry.
+ */
+#define Dname		0x00
+#define Dnamesz	0x0B
+#define Dext		0x08
+#define Dattr		0x0B
+#define Dtime		0x16
+#define Ddate		0x18
+#define Dstart		0x1A
+#define Dlengthlo	0x1C
+#define Dlengthhi	0x1E
+
+#define Dirsz		0x20
+
+/*
+ * Data is kept on the stack, indexed by rBP.
+ */
+#define Xdap		0x00		/* disc address packet */
+#define Xrootsz		0x10		/* file data area */
+#define Xdrive		0x12		/* boot drive, passed by BIOS or MBR */
+#define Xtotal		0x14		/* sum of allocated data above */
+
+TEXT _magic(SB), $0
+	BYTE $0xEB; BYTE $0x3C;		/* jmp .+ 0x3C  (_start0x3E) */
+	BYTE $0x90			/* nop */
+TEXT _version(SB), $0
+	BYTE $0x00; BYTE $0x00; BYTE $0x00; BYTE $0x00;
+	BYTE $0x00; BYTE $0x00; BYTE $0x00; BYTE $0x00
+TEXT _sectsize(SB), $0
+	BYTE $0x00; BYTE $0x00
+TEXT _clustsize(SB), $0
+	BYTE $0x00
+TEXT _nresrv(SB), $0
+	BYTE $0x00; BYTE $0x00
+TEXT _nfats(SB), $0
+	BYTE $0x00
+TEXT _rootsize(SB), $0
+	BYTE $0x00; BYTE $0x00
+TEXT _volsize(SB), $0
+	BYTE $0x00; BYTE $0x00
+TEXT _mediadesc(SB), $0
+	BYTE $0x00
+TEXT _fatsize(SB), $0
+	BYTE $0x00; BYTE $0x00
+TEXT _trksize(SB), $0
+	BYTE $0x00; BYTE $0x00
+TEXT _nheads(SB), $0
+	BYTE $0x00; BYTE $0x00
+TEXT _nhiddenlo(SB), $0
+	BYTE $0x00; BYTE $0x00
+TEXT _nhiddenhi(SB), $0
+	BYTE $0x00; BYTE $0x00;
+TEXT _bigvolsize(SB), $0
+	BYTE $0x00; BYTE $0x00; BYTE $0x00; BYTE $0x00;
+TEXT _driveno(SB), $0
+	BYTE $0x00
+TEXT _reserved0(SB), $0
+	BYTE $0x00
+TEXT _bootsig(SB), $0
+	BYTE $0x00
+TEXT _volid(SB), $0
+	BYTE $0x00; BYTE $0x00; BYTE $0x00; BYTE $0x00;
+TEXT _label(SB), $0
+	BYTE $0x00; BYTE $0x00; BYTE $0x00; BYTE $0x00;
+	BYTE $0x00; BYTE $0x00; BYTE $0x00; BYTE $0x00
+	BYTE $0x00; BYTE $0x00; BYTE $0x00
+TEXT _type(SB), $0
+	BYTE $0x00; BYTE $0x00; BYTE $0x00; BYTE $0x00;
+	BYTE $0x00; BYTE $0x00; BYTE $0x00; BYTE $0x00;
+
+_start0x3E:
+	CLI
+	CLR(rAX)
+	MTSR(rAX, rSS)			/* 0000 -> rSS */
+	MTSR(rAX, rDS)			/* 0000 -> rDS, source segment */
+	MTSR(rAX, rES)
+	LWI(_magic-Xtotal(SB), rSP)
+	MW(rSP, rBP)			/* set the indexed-data pointer */
+
+	SBPB(rDL, Xdrive)		/* save the boot drive */
+
+	/* booting from a CD starts us at 7C0:0.  Move to 0:7C00 */
+	PUSHR(rAX)
+	LWI(_nxt(SB), rAX)
+	PUSHR(rAX)
+	BYTE $0xCB	/* FAR RET */
+
+TEXT _nxt(SB), $0
+	STI
+
+	LWI(confidence(SB), rSI)	/* for that warm, fuzzy feeling */
+	CALL16(BIOSputs(SB))
+
+	LBI(0x41, rAH)			/* check extensions present */
+	LWI(0x55AA, rBX)
+	LXB(Xdrive, xBP, rDL)		/* drive */
+	BIOSCALL(0x13)			/* CF set on failure */
+	JCS _jmp01
+	CMPI(0xAA55, rBX)
+	JNE _jmp01
+	ANDI(0x0001, rCX)
+	JEQ _jmp01
+
+					/* rCX contains 0x0001 */
+	SBPWI(0x0010, Xdap+0)		/* reserved + packet size */
+	SBPW(rCX, Xdap+2)		/* reserved + # of blocks to transfer */
+
+	DEC(rCX)
+	SBPW(rCX, Xdap+12)
+	SBPW(rCX, Xdap+14)
+
+	CALL16(dreset(SB))
+
+_jmp00:
+	LW(_volid(SB), rAX)		/* Xrootlo */
+	LW(_volid+2(SB), rDX)		/* Xroothi */
+
+	LWI(_magic+DIROFF(SB), rBX)
+	CALL16(BIOSread(SB))		/* read the root directory */
+
+	LWI((512/Dirsz), rBX)
+
+	LWI(_magic+DIROFF(SB), rDI)	/* compare first directory entry */
+
+_cmp00:
+	PUSHR(rDI)			/* save for later if it matches */
+	LWI(bootfile(SB), rSI)
+	LWI(Dnamesz, rCX)
+	REP
+	CMPSB
+	POPR(rDI)
+	JEQ _jmp02
+
+	DEC(rBX)
+	JEQ _jmp01
+
+	ADDI(Dirsz, rDI)
+	JMP _cmp00
+_jmp01:
+	CALL16(buggery(SB))
+
+_jmp02:
+	CLR(rBX)			/* a handy value */
+	LW(_rootsize(SB), rAX)		/* calculate and save Xrootsz */
+	LWI(Dirsz, rCX)
+	MUL(rCX)
+	LW(_sectsize(SB), rCX)
+	PUSHR(rCX)
+	DEC(rCX)
+	ADD(rCX, rAX)
+	ADC(rBX, rDX)
+	POPR(rCX)			/* _sectsize(SB) */
+	DIV(rCX)
+	PUSHR(rAX)			/* Xrootsz */
+
+	/*
+	 * rDI points to the matching directory entry.
+	 */
+	LXW(Dstart, xDI, rAX)		/* starting sector address */
+	DEC(rAX)			/* that's just the way it is */
+	DEC(rAX)
+	LB(_clustsize(SB), rCL)
+	CLRB(rCH)
+	MUL(rCX)
+	LW(_volid(SB), rCX)		/* Xrootlo */
+	ADD(rCX, rAX)
+	LW(_volid+2(SB), rCX)		/* Xroothi */
+	ADC(rCX, rDX)
+	POPR(rCX)			/* Xrootsz */
+	ADD(rCX, rAX)
+	ADC(rBX, rDX)
+
+	PUSHR(rAX)			/* calculate how many sectors to read */
+	PUSHR(rDX)
+	LXW(Dlengthlo, xDI, rAX)
+	LXW(Dlengthhi, xDI, rDX)
+	LW(_sectsize(SB), rCX)
+	PUSHR(rCX)
+	DEC(rCX)
+	ADD(rCX, rAX)
+	ADC(rBX, rDX)
+	POPR(rCX)			/* _sectsize(SB) */
+	DIV(rCX)
+	MW(rAX, rCX)
+	POPR(rDX)
+	POPR(rAX)
+
+	LWI(LOADSEG, rBX)		/* address to load into (seg+offset) */
+	MTSR(rBX, rES)			/*  seg */
+	LWI(LOADOFF, rBX)		/*  offset */
+
+_readboot:
+	CALL16(BIOSread(SB))		/* read the sector */
+
+	LW(_sectsize(SB), rDI)		/* bump addresses/counts */
+	ADD(rDI, rBX)
+	JCC _incsecno
+
+	MFSR(rES, rDI)			/* next 64KB segment */
+	ADDI(0x1000, rDI)
+	MTSR(rDI, rES)
+
+_incsecno:
+	CLR(rDI)
+	INC(rAX)
+	ADC(rDI, rDX)
+	LOOP _readboot
+
+	LWI(LOADSEG, rDI)		/* set rDS for loaded code */
+	MTSR(rDI, rDS)
+	FARJUMP16(LOADSEG, LOADOFF)	/* no deposit, no return */
+
+TEXT buggery(SB), $0
+	LWI(error(SB), rSI)
+	CALL16(BIOSputs(SB))
+
+_wait:
+	CLR(rAX)			/* wait for almost any key */
+	BIOSCALL(0x16)
+
+_reset:
+	CLR(rBX)			/* set ES segment for BIOS area */
+	MTSR(rBX, rES)
+
+	LWI(0x0472, rBX)		/* warm-start code address */
+	LWI(0x1234, rAX)		/* warm-start code */
+	POKEW				/* MOVW	AX, ES:[BX] */
+
+	FARJUMP16(0xFFFF, 0x0000)	/* reset */
+
+
+/*
+ * Read a sector from a disc. On entry:
+ *   rDX:rAX	sector number
+ *   rES:rBX	buffer address
+ */
+TEXT BIOSread(SB), $0
+	LWI(5, rDI)			/* retry count (ATAPI ZIPs suck) */
+_retry:
+	PUSHA				/* may be trashed by BIOSCALL */
+
+	SBPW(rBX, Xdap+4)		/* transfer buffer :offset */
+	MFSR(rES, rDI)			/* transfer buffer seg: */
+	SBPW(rDI, Xdap+6)
+	SBPW(rAX, Xdap+8)		/* LBA (64-bits) */
+	SBPW(rDX, Xdap+10)
+
+	MW(rBP, rSI)			/* disk address packet */
+	LBI(0x42, rAH)			/* extended read */
+	LBPB(Xdrive, rDL)		/* form drive */
+	BIOSCALL(0x13)			/* CF set on failure */
+	JCC _BIOSreadret
+
+	POPA
+	DEC(rDI)			/* too many retries? */
+	JEQ _ioerror
+
+	CALL16(dreset(SB))
+	JMP _retry
+
+_ioerror:
+	LWI(ioerror(SB), rSI)
+	CALL16(BIOSputs(SB))
+	JMP _wait
+
+_BIOSreadret:
+	POPA
+	RET
+
+TEXT dreset(SB), $0
+	PUSHA
+	CLR(rAX)			/* rAH == 0 == reset disc system */
+	LBPB(Xdrive, rDL)
+	BIOSCALL(0x13)
+	ORB(rAH, rAH)			/* status (0 == success) */
+	POPA
+	JNE _ioerror
+	RET
+
+/*
+ * Output a string to the display.
+ * String argument is in rSI.
+ */
+TEXT BIOSputs(SB), $0
+	PUSHA
+	CLR(rBX)
+_BIOSputs:
+	LODSB
+	ORB(rAL, rAL)
+	JEQ _BIOSputsret
+
+	LBI(0x0E, rAH)
+	BIOSCALL(0x10)
+	JMP _BIOSputs
+
+_BIOSputsret:
+	POPA
+	RET
+
+/* "Bad format or I/O error\r\nPress almost any key to reboot..." */
+TEXT error(SB), $0
+	BYTE $'B'; BYTE $'a'; BYTE $'d'; BYTE $' ';
+	BYTE $'f'; BYTE $'o'; BYTE $'r'; BYTE $'m';
+	BYTE $'a'; BYTE $'t'; BYTE $' '; BYTE $'o';
+	BYTE $'r'; BYTE $' ';
+/* "I/O error\r\nPress almost any key to reboot..." */
+TEXT ioerror(SB), $0
+	BYTE $'I'; BYTE $'/'; BYTE $'O'; BYTE $' ';
+	BYTE $'e'; BYTE $'r'; BYTE $'r'; BYTE $'o';
+	BYTE $'r'; BYTE $'\r';BYTE $'\n';
+	BYTE $'P'; BYTE $'r'; BYTE $'e'; BYTE $'s';
+	BYTE $'s'; BYTE $' '; BYTE $'a'; BYTE $' ';
+	BYTE $'k'; BYTE $'e'; BYTE $'y';
+	BYTE $' '; BYTE $'t'; BYTE $'o'; BYTE $' ';
+	BYTE $'r'; BYTE $'e'; BYTE $'b'; BYTE $'o';
+	BYTE $'o'; BYTE $'t';
+	BYTE $'.'; BYTE $'.'; BYTE $'.';
+	BYTE $'\z';
+
+#ifdef USEBCOM
+/* "B       COM" */
+TEXT bootfile(SB), $0
+	BYTE $'B'; BYTE $' '; BYTE $' '; BYTE $' ';
+	BYTE $' '; BYTE $' '; BYTE $' '; BYTE $' ';
+	BYTE $'C'; BYTE $'O'; BYTE $'M';
+	BYTE $'\z';
+#else
+/* "9LOAD      " */
+TEXT bootfile(SB), $0
+	BYTE $'9'; BYTE $'L'; BYTE $'O'; BYTE $'A';
+	BYTE $'D'; BYTE $' '; BYTE $' '; BYTE $' ';
+	BYTE $' '; BYTE $' '; BYTE $' ';
+	BYTE $'\z';
+#endif /* USEBCOM */
+
+/* "PBS..." */
+TEXT confidence(SB), $0
+	BYTE $'P'; BYTE $'B'; BYTE $'S'; BYTE $'2';
+	BYTE $'.'; BYTE $'.'; BYTE $'.';
+	BYTE $'\z';
--- /dev/null
+++ b/os/boot/pc/pci.c
@@ -1,0 +1,1002 @@
+/*
+ * PCI support code.
+ * To do:
+ *	initialise bridge mappings if the PCI BIOS didn't.
+ */
+#include "u.h"
+#include "lib.h"
+#include "mem.h"
+#include "dat.h"
+#include "fns.h"
+#include "io.h"
+#include "error.h"
+
+enum {					/* configuration mechanism #1 */
+	PciADDR		= 0xCF8,	/* CONFIG_ADDRESS */
+	PciDATA		= 0xCFC,	/* CONFIG_DATA */
+
+					/* configuration mechanism #2 */
+	PciCSE		= 0xCF8,	/* configuration space enable */
+	PciFORWARD	= 0xCFA,	/* which bus */
+
+	MaxFNO		= 7,
+	MaxUBN		= 255,
+};
+
+enum
+{					/* command register */
+	IOen		= (1<<0),
+	MEMen		= (1<<1),
+	MASen		= (1<<2),
+	MemWrInv	= (1<<4),
+	PErrEn		= (1<<6),
+	SErrEn		= (1<<8),
+};
+
+static Lock pcicfglock;
+static Lock pcicfginitlock;
+static int pcicfgmode = -1;
+static int pcimaxbno = 7;
+static int pcimaxdno;
+static Pcidev* pciroot;
+static Pcidev* pcilist;
+static Pcidev* pcitail;
+
+static int pcicfgrw32(int, int, int, int);
+static int pcicfgrw8(int, int, int, int);
+
+ulong
+pcibarsize(Pcidev *p, int rno)
+{
+	ulong v, size;
+
+	v = pcicfgrw32(p->tbdf, rno, 0, 1);
+	pcicfgrw32(p->tbdf, rno, 0xFFFFFFF0, 0);
+	size = pcicfgrw32(p->tbdf, rno, 0, 1);
+	if(v & 1)
+		size |= 0xFFFF0000;
+	pcicfgrw32(p->tbdf, rno, v, 0);
+
+	return -(size & ~0x0F);
+}
+
+int
+pciscan(int bno, Pcidev** list)
+{
+	Pcidev *p, *head, *tail;
+	int dno, fno, i, hdt, l, maxfno, maxubn, rno, sbn, tbdf, ubn;
+
+	maxubn = bno;
+	head = nil;
+	tail = nil;
+	for(dno = 0; dno <= pcimaxdno; dno++){
+		maxfno = 0;
+		for(fno = 0; fno <= maxfno; fno++){
+			/*
+			 * For this possible device, form the
+			 * bus+device+function triplet needed to address it
+			 * and try to read the vendor and device ID.
+			 * If successful, allocate a device struct and
+			 * start to fill it in with some useful information
+			 * from the device's configuration space.
+			 */
+			tbdf = MKBUS(BusPCI, bno, dno, fno);
+			l = pcicfgrw32(tbdf, PciVID, 0, 1);
+			if(l == 0xFFFFFFFF || l == 0)
+				continue;
+			p = malloc(sizeof(*p));
+			p->tbdf = tbdf;
+			p->vid = l;
+			p->did = l>>16;
+
+			if(pcilist != nil)
+				pcitail->list = p;
+			else
+				pcilist = p;
+			pcitail = p;
+
+			p->rid = pcicfgr8(p, PciRID);
+			p->ccrp = pcicfgr8(p, PciCCRp);
+			p->ccru = pcicfgr8(p, PciCCRu);
+			p->ccrb = pcicfgr8(p, PciCCRb);
+			p->pcr = pcicfgr32(p, PciPCR);
+
+			p->intl = pcicfgr8(p, PciINTL);
+
+			/*
+			 * If the device is a multi-function device adjust the
+			 * loop count so all possible functions are checked.
+			 */
+			hdt = pcicfgr8(p, PciHDT);
+			if(hdt & 0x80)
+				maxfno = MaxFNO;
+
+			/*
+			 * If appropriate, read the base address registers
+			 * and work out the sizes.
+			 */
+			switch(p->ccrb){
+
+			case 0x01:		/* mass storage controller */
+			case 0x02:		/* network controller */
+			case 0x03:		/* display controller */
+			case 0x04:		/* multimedia device */
+			case 0x07:		/* simple comm. controllers */
+			case 0x08:		/* base system peripherals */
+			case 0x09:		/* input devices */
+			case 0x0A:		/* docking stations */
+			case 0x0B:		/* processors */
+			case 0x0C:		/* serial bus controllers */
+				if((hdt & 0x7F) != 0)
+					break;
+				rno = PciBAR0 - 4;
+				for(i = 0; i < nelem(p->mem); i++){
+					rno += 4;
+					p->mem[i].bar = pcicfgr32(p, rno);
+					p->mem[i].size = pcibarsize(p, rno);
+				}
+				break;
+
+			case 0x00:
+			case 0x05:		/* memory controller */
+			case 0x06:		/* bridge device */
+			default:
+				break;
+			}
+
+			if(head != nil)
+				tail->link = p;
+			else
+				head = p;
+			tail = p;
+		}
+	}
+
+	*list = head;
+	for(p = head; p != nil; p = p->link){
+		/*
+		 * Find PCI-PCI and PCI-Cardbus bridges
+		 * and recursively descend the tree.
+		 */
+		if(p->ccrb != 0x06 || p->ccru != 0x04)
+			continue;
+
+		/*
+		 * If the secondary or subordinate bus number is not
+		 * initialised try to do what the PCI BIOS should have
+		 * done and fill in the numbers as the tree is descended.
+		 * On the way down the subordinate bus number is set to
+		 * the maximum as it's not known how many buses are behind
+		 * this one; the final value is set on the way back up.
+		 */
+		ubn = pcicfgr8(p, PciUBN);
+		sbn = pcicfgr8(p, PciSBN);
+
+		if(sbn == 0 || ubn == 0){
+			sbn = maxubn+1;
+			/*
+			 * Make sure memory, I/O and master enables are
+			 * off, set the primary, secondary and subordinate
+			 * bus numbers and clear the secondary status before
+			 * attempting to scan the secondary bus.
+			 *
+			 * Initialisation of the bridge should be done here.
+			 */
+			pcicfgw32(p, PciPCR, 0xFFFF0000);
+			l = (MaxUBN<<16)|(sbn<<8)|bno;
+			pcicfgw32(p, PciPBN, l);
+			pcicfgw16(p, PciSPSR, 0xFFFF);
+			maxubn = pciscan(sbn, &p->bridge);
+			l = (maxubn<<16)|(sbn<<8)|bno;
+
+			pcicfgw32(p, PciPBN, l);
+		}
+		else{
+			/*
+			 * You can't go back.
+			 * This shouldn't be possible, but the
+			 * Iwill DK8-HTX seems to have subordinate
+			 * bus numbers which get smaller on the
+			 * way down. Need to look more closely at
+			 * this.
+			 */
+			if(ubn > maxubn)
+				maxubn = ubn;
+			pciscan(sbn, &p->bridge);
+		}
+	}
+
+	return maxubn;
+}
+
+static uchar
+null_link(Pcidev *, uchar )
+{
+	return 0;
+}
+
+static void
+null_init(Pcidev *, uchar , uchar )
+{
+}
+
+static uchar
+pIIx_link(Pcidev *router, uchar link)
+{
+	uchar pirq;
+
+	/* link should be 0x60, 0x61, 0x62, 0x63 */
+	pirq = pcicfgr8(router, link);
+	return (pirq < 16)? pirq: 0;
+}
+
+static void
+pIIx_init(Pcidev *router, uchar link, uchar irq)
+{
+	pcicfgw8(router, link, irq);
+}
+
+static uchar
+via_link(Pcidev *router, uchar link)
+{
+	uchar pirq;
+
+	/* link should be 1, 2, 3, 5 */
+	pirq = (link < 6)? pcicfgr8(router, 0x55 + (link>>1)): 0;
+
+	return (link & 1)? (pirq >> 4): (pirq & 15);
+}
+
+static void
+via_init(Pcidev *router, uchar link, uchar irq)
+{
+	uchar pirq;
+
+	pirq = pcicfgr8(router, 0x55 + (link >> 1));
+	pirq &= (link & 1)? 0x0f: 0xf0;
+	pirq |= (link & 1)? (irq << 4): (irq & 15);
+	pcicfgw8(router, 0x55 + (link>>1), pirq);
+}
+
+static uchar
+opti_link(Pcidev *router, uchar link)
+{
+	uchar pirq = 0;
+
+	/* link should be 0x02, 0x12, 0x22, 0x32 */
+	if ((link & 0xcf) == 0x02)
+		pirq = pcicfgr8(router, 0xb8 + (link >> 5));
+	return (link & 0x10)? (pirq >> 4): (pirq & 15);
+}
+
+static void
+opti_init(Pcidev *router, uchar link, uchar irq)
+{
+	uchar pirq;
+
+	pirq = pcicfgr8(router, 0xb8 + (link >> 5));
+    	pirq &= (link & 0x10)? 0x0f : 0xf0;
+    	pirq |= (link & 0x10)? (irq << 4): (irq & 15);
+	pcicfgw8(router, 0xb8 + (link >> 5), pirq);
+}
+
+static uchar
+ali_link(Pcidev *router, uchar link)
+{
+	/* No, you're not dreaming */
+	static const uchar map[] = { 0, 9, 3, 10, 4, 5, 7, 6, 1, 11, 0, 12, 0, 14, 0, 15 };
+	uchar pirq;
+
+	/* link should be 0x01..0x08 */
+	pirq = pcicfgr8(router, 0x48 + ((link-1)>>1));
+	return (link & 1)? map[pirq&15]: map[pirq>>4];
+}
+
+static void
+ali_init(Pcidev *router, uchar link, uchar irq)
+{
+	/* Inverse of map in ali_link */
+	static const uchar map[] = { 0, 8, 0, 2, 4, 5, 7, 6, 0, 1, 3, 9, 11, 0, 13, 15 };
+	uchar pirq;
+
+	pirq = pcicfgr8(router, 0x48 + ((link-1)>>1));
+	pirq &= (link & 1)? 0x0f: 0xf0;
+	pirq |= (link & 1)? (map[irq] << 4): (map[irq] & 15);
+	pcicfgw8(router, 0x48 + ((link-1)>>1), pirq);
+}
+
+static uchar
+cyrix_link(Pcidev *router, uchar link)
+{
+	uchar pirq;
+
+	/* link should be 1, 2, 3, 4 */
+	pirq = pcicfgr8(router, 0x5c + ((link-1)>>1));
+	return ((link & 1)? pirq >> 4: pirq & 15);
+}
+
+static void
+cyrix_init(Pcidev *router, uchar link, uchar irq)
+{
+	uchar pirq;
+
+	pirq = pcicfgr8(router, 0x5c + (link>>1));
+	pirq &= (link & 1)? 0x0f: 0xf0;
+	pirq |= (link & 1)? (irq << 4): (irq & 15);
+	pcicfgw8(router, 0x5c + (link>>1), pirq);
+}
+
+typedef struct {
+	ushort	sb_vid, sb_did;
+	uchar	(*sb_translate)(Pcidev *, uchar);
+	void	(*sb_initialize)(Pcidev *, uchar, uchar);
+} bridge_t;
+
+static bridge_t southbridges[] = {
+	{ 0x8086, 0x122e, pIIx_link, pIIx_init },	// Intel 82371FB
+	{ 0x8086, 0x1234, pIIx_link, pIIx_init },	// Intel 82371MX
+	{ 0x8086, 0x7000, pIIx_link, pIIx_init },	// Intel 82371SB
+	{ 0x8086, 0x7110, pIIx_link, pIIx_init },	// Intel 82371AB
+	{ 0x8086, 0x7198, pIIx_link, pIIx_init },	// Intel 82443MX (fn 1)
+	{ 0x8086, 0x2410, pIIx_link, pIIx_init },	// Intel 82801AA
+	{ 0x8086, 0x2420, pIIx_link, pIIx_init },	// Intel 82801AB
+	{ 0x8086, 0x2440, pIIx_link, pIIx_init },	// Intel 82801BA
+	{ 0x8086, 0x244c, pIIx_link, pIIx_init },	// Intel 82801BAM
+	{ 0x8086, 0x2480, pIIx_link, pIIx_init },	// Intel 82801CA
+	{ 0x8086, 0x248c, pIIx_link, pIIx_init },	// Intel 82801CAM
+	{ 0x8086, 0x24c0, pIIx_link, pIIx_init },	// Intel 82801DBL
+	{ 0x8086, 0x24cc, pIIx_link, pIIx_init },	// Intel 82801DBM
+	{ 0x8086, 0x24d0, pIIx_link, pIIx_init },	// Intel 82801EB
+	{ 0x8086, 0x2640, pIIx_link, pIIx_init },	// Intel 82801FB
+	{ 0x8086, 0x27b8, pIIx_link, pIIx_init },	// Intel 82801GB
+	{ 0x8086, 0x27b9, pIIx_link, pIIx_init },	// Intel 82801GBM
+	{ 0x1106, 0x0586, via_link, via_init },		// Viatech 82C586
+	{ 0x1106, 0x0596, via_link, via_init },		// Viatech 82C596
+	{ 0x1106, 0x0686, via_link, via_init },		// Viatech 82C686
+	{ 0x1106, 0x3227, via_link, via_init },		// Viatech VT8237
+	{ 0x1045, 0xc700, opti_link, opti_init },	// Opti 82C700
+	{ 0x10b9, 0x1533, ali_link, ali_init },		// Al M1533
+	{ 0x1039, 0x0008, pIIx_link, pIIx_init },	// SI 503
+	{ 0x1039, 0x0496, pIIx_link, pIIx_init },	// SI 496
+	{ 0x1078, 0x0100, cyrix_link, cyrix_init },	// Cyrix 5530 Legacy
+
+	{ 0x1002, 0x4377, nil, nil },		// ATI Radeon Xpress 200M
+	{ 0x1002, 0x4372, nil, nil },		// ATI SB400
+	{ 0x1022, 0x746B, nil, nil },		// AMD 8111
+	{ 0x10DE, 0x00D1, nil, nil },		// NVIDIA nForce 3
+	{ 0x10DE, 0x00E0, nil, nil },		// NVIDIA nForce 3 250 Series
+	{ 0x1166, 0x0200, nil, nil },		// ServerWorks ServerSet III LE
+};
+
+typedef struct {
+	uchar	e_bus;			// Pci bus number
+	uchar	e_dev;			// Pci device number
+	uchar	e_maps[12];		// Avoid structs!  Link and mask.
+	uchar	e_slot;			// Add-in/built-in slot
+	uchar	e_reserved;
+} slot_t;
+
+typedef struct {
+	uchar	rt_signature[4];	// Routing table signature
+	uchar	rt_version[2];		// Version number
+	uchar	rt_size[2];			// Total table size
+	uchar	rt_bus;			// Interrupt router bus number
+	uchar	rt_devfn;			// Router's devfunc
+	uchar	rt_pciirqs[2];		// Exclusive PCI irqs
+	uchar	rt_compat[4];		// Compatible PCI interrupt router
+	uchar	rt_miniport[4];		// Miniport data
+	uchar	rt_reserved[11];
+	uchar	rt_checksum;
+} router_t;
+
+static ushort pciirqs;			// Exclusive PCI irqs
+static bridge_t *southbridge;	// Which southbridge to use.
+
+static void
+pcirouting(void)
+{
+	uchar *p, pin, irq;
+	ulong tbdf, vdid;
+	ushort vid, did;
+	router_t *r;
+	slot_t *e;
+	int size, i, fn;
+	Pcidev *sbpci, *pci;
+
+	// Peek in the BIOS
+	for (p = (uchar *)KADDR(0xf0000); p < (uchar *)KADDR(0xfffff); p += 16)
+		if (p[0] == '$' && p[1] == 'P' && p[2] == 'I' && p[3] == 'R')
+			break;
+
+	if (p >= (uchar *)KADDR(0xfffff))
+		return;
+
+	r = (router_t *)p;
+
+	// print("PCI interrupt routing table version %d.%d at %.6uX\n",
+	// 	r->rt_version[0], r->rt_version[1], (ulong)r & 0xfffff);
+
+	tbdf = (BusPCI << 24)|(r->rt_bus << 16)|(r->rt_devfn << 8);
+	vdid = pcicfgrw32(tbdf, PciVID, 0, 1);
+	vid = vdid;
+	did = vdid >> 16;
+
+	for (i = 0; i != nelem(southbridges); i++)
+		if (vid == southbridges[i].sb_vid && did == southbridges[i].sb_did)
+			break;
+
+	if (i == nelem(southbridges)) {
+		print("pcirouting: South bridge %.4uX, %.4uX not found\n", vid, did);
+		return;
+	}
+	southbridge = &southbridges[i];
+
+	if ((sbpci = pcimatch(nil, vid, did)) == nil) {
+		print("pcirouting: Cannot match south bridge %.4uX, %.4uX\n",
+			  vid, did);
+		return;
+	}
+
+	pciirqs = (r->rt_pciirqs[1] << 8)|r->rt_pciirqs[0];
+
+	size = (r->rt_size[1] << 8)|r->rt_size[0];
+	for (e = (slot_t *)&r[1]; (uchar *)e < p + size; e++) {
+		// print("%.2uX/%.2uX %.2uX: ", e->e_bus, e->e_dev, e->e_slot);
+		// for (i = 0; i != 4; i++) {
+		// 	uchar *m = &e->e_maps[i * 3];
+		// 	print("[%d] %.2uX %.4uX ",
+		// 		i, m[0], (m[2] << 8)|m[1]);
+		// }
+		// print("\n");
+
+		for (fn = 0; fn != 8; fn++) {
+			uchar *m;
+
+			// Retrieve the did and vid through the devfn before
+			// obtaining the Pcidev structure.
+			tbdf = (BusPCI << 24)|(e->e_bus << 16)|((e->e_dev | fn) << 8);
+			vdid = pcicfgrw32(tbdf, PciVID, 0, 1);
+			if (vdid == 0xFFFFFFFF || vdid == 0)
+				continue;
+
+			vid = vdid;
+			did = vdid >> 16;
+
+			pci = nil;
+			while ((pci = pcimatch(pci, vid, did)) != nil) {
+
+				if (pci->intl != 0 && pci->intl != 0xFF)
+					continue;
+
+				pin = pcicfgr8(pci, PciINTP);
+				if (pin == 0 || pin == 0xff)
+					continue;
+
+				m = &e->e_maps[(pin - 1) * 3];
+				irq = southbridge->sb_translate(sbpci, m[0]);
+				if (irq) {
+					print("pcirouting: %.4uX/%.4uX at pin %d irq %d\n",
+						  vid, did, pin, irq);
+					pcicfgw8(pci, PciINTL, irq);
+					pci->intl = irq;
+				}
+			}
+		}
+	}
+}
+
+static void
+pcicfginit(void)
+{
+	char *p;
+	int bno, n;
+	Pcidev **list;
+
+	lock(&pcicfginitlock);
+	if(pcicfgmode != -1)
+		goto out;
+
+	/*
+	 * Try to determine which PCI configuration mode is implemented.
+	 * Mode2 uses a byte at 0xCF8 and another at 0xCFA; Mode1 uses
+	 * a DWORD at 0xCF8 and another at 0xCFC and will pass through
+	 * any non-DWORD accesses as normal I/O cycles. There shouldn't be
+	 * a device behind these addresses so if Mode1 accesses fail try
+	 * for Mode2 (Mode2 is deprecated).
+	 */
+
+	/*
+	 * Bits [30:24] of PciADDR must be 0,
+	 * according to the spec.
+	 */
+	n = inl(PciADDR);
+	if(!(n & 0x7FF00000)){
+		outl(PciADDR, 0x80000000);
+		outb(PciADDR+3, 0);
+		if(inl(PciADDR) & 0x80000000){
+			pcicfgmode = 1;
+			pcimaxdno = 31;
+		}
+	}
+	outl(PciADDR, n);
+
+	if(pcicfgmode < 0){
+		/*
+		 * The 'key' part of PciCSE should be 0.
+		 */
+		n = inb(PciCSE);
+		if(!(n & 0xF0)){
+			outb(PciCSE, 0x0E);
+			if(inb(PciCSE) == 0x0E){
+				pcicfgmode = 2;
+				pcimaxdno = 15;
+			}
+		}
+		outb(PciCSE, n);
+	}
+
+	if(pcicfgmode < 0)
+		goto out;
+
+
+	if(p = getconf("*pcimaxbno"))
+		pcimaxbno = strtoul(p, 0, 0);
+	if(p = getconf("*pcimaxdno"))
+		pcimaxdno = strtoul(p, 0, 0);
+
+	list = &pciroot;
+	for(bno = 0; bno <= pcimaxbno; bno++) {
+		bno = pciscan(bno, list);
+		while(*list)
+			list = &(*list)->link;
+	}
+
+	pcirouting();
+
+out:
+	unlock(&pcicfginitlock);
+
+	if(getconf("*pcihinv"))
+		pcihinv(nil);
+}
+
+
+static int
+pcicfgrw8(int tbdf, int rno, int data, int read)
+{
+	int o, type, x;
+
+	if(pcicfgmode == -1)
+		pcicfginit();
+
+	if(BUSBNO(tbdf))
+		type = 0x01;
+	else
+		type = 0x00;
+	x = -1;
+	if(BUSDNO(tbdf) > pcimaxdno)
+		return x;
+
+	lock(&pcicfglock);
+	switch(pcicfgmode){
+
+	case 1:
+		o = rno & 0x03;
+		rno &= ~0x03;
+		outl(PciADDR, 0x80000000|BUSBDF(tbdf)|rno|type);
+		if(read)
+			x = inb(PciDATA+o);
+		else
+			outb(PciDATA+o, data);
+		outl(PciADDR, 0);
+		break;
+
+	case 2:
+		outb(PciCSE, 0x80|(BUSFNO(tbdf)<<1));
+		outb(PciFORWARD, BUSBNO(tbdf));
+		if(read)
+			x = inb((0xC000|(BUSDNO(tbdf)<<8)) + rno);
+		else
+			outb((0xC000|(BUSDNO(tbdf)<<8)) + rno, data);
+		outb(PciCSE, 0);
+		break;
+	}
+	unlock(&pcicfglock);
+
+	return x;
+}
+
+int
+pcicfgr8(Pcidev* pcidev, int rno)
+{
+	return pcicfgrw8(pcidev->tbdf, rno, 0, 1);
+}
+
+void
+pcicfgw8(Pcidev* pcidev, int rno, int data)
+{
+	pcicfgrw8(pcidev->tbdf, rno, data, 0);
+}
+
+static int
+pcicfgrw16(int tbdf, int rno, int data, int read)
+{
+	int o, type, x;
+
+	if(pcicfgmode == -1)
+		pcicfginit();
+
+	if(BUSBNO(tbdf))
+		type = 0x01;
+	else
+		type = 0x00;
+	x = -1;
+	if(BUSDNO(tbdf) > pcimaxdno)
+		return x;
+
+	lock(&pcicfglock);
+	switch(pcicfgmode){
+
+	case 1:
+		o = rno & 0x02;
+		rno &= ~0x03;
+		outl(PciADDR, 0x80000000|BUSBDF(tbdf)|rno|type);
+		if(read)
+			x = ins(PciDATA+o);
+		else
+			outs(PciDATA+o, data);
+		outl(PciADDR, 0);
+		break;
+
+	case 2:
+		outb(PciCSE, 0x80|(BUSFNO(tbdf)<<1));
+		outb(PciFORWARD, BUSBNO(tbdf));
+		if(read)
+			x = ins((0xC000|(BUSDNO(tbdf)<<8)) + rno);
+		else
+			outs((0xC000|(BUSDNO(tbdf)<<8)) + rno, data);
+		outb(PciCSE, 0);
+		break;
+	}
+	unlock(&pcicfglock);
+
+	return x;
+}
+
+int
+pcicfgr16(Pcidev* pcidev, int rno)
+{
+	return pcicfgrw16(pcidev->tbdf, rno, 0, 1);
+}
+
+void
+pcicfgw16(Pcidev* pcidev, int rno, int data)
+{
+	pcicfgrw16(pcidev->tbdf, rno, data, 0);
+}
+
+static int
+pcicfgrw32(int tbdf, int rno, int data, int read)
+{
+	int type, x;
+
+	if(pcicfgmode == -1)
+		pcicfginit();
+
+	if(BUSBNO(tbdf))
+		type = 0x01;
+	else
+		type = 0x00;
+	x = -1;
+	if(BUSDNO(tbdf) > pcimaxdno)
+		return x;
+
+	lock(&pcicfglock);
+	switch(pcicfgmode){
+
+	case 1:
+		rno &= ~0x03;
+		outl(PciADDR, 0x80000000|BUSBDF(tbdf)|rno|type);
+		if(read)
+			x = inl(PciDATA);
+		else
+			outl(PciDATA, data);
+		outl(PciADDR, 0);
+		break;
+
+	case 2:
+		outb(PciCSE, 0x80|(BUSFNO(tbdf)<<1));
+		outb(PciFORWARD, BUSBNO(tbdf));
+		if(read)
+			x = inl((0xC000|(BUSDNO(tbdf)<<8)) + rno);
+		else
+			outl((0xC000|(BUSDNO(tbdf)<<8)) + rno, data);
+		outb(PciCSE, 0);
+		break;
+	}
+	unlock(&pcicfglock);
+
+	return x;
+}
+
+int
+pcicfgr32(Pcidev* pcidev, int rno)
+{
+	return pcicfgrw32(pcidev->tbdf, rno, 0, 1);
+}
+
+void
+pcicfgw32(Pcidev* pcidev, int rno, int data)
+{
+	pcicfgrw32(pcidev->tbdf, rno, data, 0);
+}
+
+Pcidev*
+pcimatch(Pcidev* prev, int vid, int did)
+{
+	if(pcicfgmode == -1)
+		pcicfginit();
+
+	if(prev == nil)
+		prev = pcilist;
+	else
+		prev = prev->list;
+
+	while(prev != nil) {
+		if((vid == 0 || prev->vid == vid)
+		&& (did == 0 || prev->did == did))
+			break;
+		prev = prev->list;
+	}
+	return prev;
+}
+
+uchar
+pciipin(Pcidev *pci, uchar pin)
+{
+	if (pci == nil)
+		pci = pcilist;
+
+	while (pci) {
+		uchar intl;
+
+		if (pcicfgr8(pci, PciINTP) == pin && pci->intl != 0 && pci->intl != 0xff)
+			return pci->intl;
+
+		if (pci->bridge && (intl = pciipin(pci->bridge, pin)) != 0)
+			return intl;
+
+		pci = pci->list;
+	}
+	return 0;
+}
+
+static ushort
+pciimask(Pcidev *pci)
+{
+	ushort imask;
+
+	imask = 0;
+	while (pci) {
+		if (pcicfgr8(pci, PciINTP) && pci->intl < 16)
+			imask |= 1 << pci->intl;
+
+		if (pci->bridge)
+			imask |= pciimask(pci->bridge);
+
+		pci = pci->list;
+	}
+	return imask;
+}
+
+uchar
+pciintl(Pcidev *pci)
+{
+	ushort imask;
+	int i;
+
+	if (pci == nil)
+		pci = pcilist;
+
+	imask = pciimask(pci) | 1;
+	for (i = 0; i != 16; i++)
+		if ((imask & (1 << i)) == 0)
+			return i;
+	return 0;
+}
+
+void
+pcihinv(Pcidev* p)
+{
+	int i;
+	Pcidev *t;
+
+	if(pcicfgmode == -1)
+		pcicfginit();
+
+	if(p == nil) {
+		p = pciroot;
+		print("bus dev type vid  did intl memory\n");
+	}
+	for(t = p; t != nil; t = t->link) {
+		print("%d  %2d/%d %.2ux %.2ux %.2ux %.4ux %.4ux %3d  ",
+			BUSBNO(t->tbdf), BUSDNO(t->tbdf), BUSFNO(t->tbdf),
+			t->ccrb, t->ccru, t->ccrp, t->vid, t->did, t->intl);
+
+		for(i = 0; i < nelem(p->mem); i++) {
+			if(t->mem[i].size == 0)
+				continue;
+			print("%d:%.8lux %d ", i,
+				t->mem[i].bar, t->mem[i].size);
+		}
+		print("\n");
+	}
+	while(p != nil) {
+		if(p->bridge != nil)
+			pcihinv(p->bridge);
+		p = p->link;
+	}
+}
+
+void
+pcireset(void)
+{
+	Pcidev *p;
+	int pcr;
+
+	if(pcicfgmode == -1)
+		pcicfginit();
+
+	for(p = pcilist; p != nil; p = p->list){
+		pcr = pcicfgr16(p, PciPSR);
+		pcicfgw16(p, PciPSR, pcr & ~0x04);
+	}
+}
+
+void
+pcisetioe(Pcidev* p)
+{
+	p->pcr |= IOen;
+	pcicfgw16(p, PciPCR, p->pcr);
+}
+
+void
+pciclrioe(Pcidev* p)
+{
+	p->pcr &= ~IOen;
+	pcicfgw16(p, PciPCR, p->pcr);
+}
+
+void
+pcisetbme(Pcidev* p)
+{
+	p->pcr |= MASen;
+	pcicfgw16(p, PciPCR, p->pcr);
+}
+
+void
+pciclrbme(Pcidev* p)
+{
+	p->pcr &= ~MASen;
+	pcicfgw16(p, PciPCR, p->pcr);
+}
+
+void
+pcisetmwi(Pcidev* p)
+{
+	p->pcr |= MemWrInv;
+	pcicfgw16(p, PciPCR, p->pcr);
+}
+
+void
+pciclrmwi(Pcidev* p)
+{
+	p->pcr &= ~MemWrInv;
+	pcicfgw16(p, PciPCR, p->pcr);
+}
+
+static int
+pcigetpmrb(Pcidev* p)
+{
+	int ptr;
+
+	if(p->pmrb != 0)
+		return p->pmrb;
+	p->pmrb = -1;
+
+	/*
+	 * If there are no extended capabilities implemented,
+	 * (bit 4 in the status register) assume there's no standard
+	 * power management method.
+	 * Find the capabilities pointer based on PCI header type.
+	 */
+	if(!(pcicfgr16(p, PciPSR) & 0x0010))
+		return -1;
+	switch(pcicfgr8(p, PciHDT)){
+	default:
+		return -1;
+	case 0:					/* all other */
+	case 1:					/* PCI to PCI bridge */
+		ptr = 0x34;
+		break;
+	case 2:					/* CardBus bridge */
+		ptr = 0x14;
+		break;
+	}
+	ptr = pcicfgr32(p, ptr);
+
+	while(ptr != 0){
+		/*
+		 * Check for validity.
+		 * Can't be in standard header and must be double
+		 * word aligned.
+		 */
+		if(ptr < 0x40 || (ptr & ~0xFC))
+			return -1;
+		if(pcicfgr8(p, ptr) == 0x01){
+			p->pmrb = ptr;
+			return ptr;
+		}
+
+		ptr = pcicfgr8(p, ptr+1);
+	}
+
+	return -1;
+}
+
+int
+pcigetpms(Pcidev* p)
+{
+	int pmcsr, ptr;
+
+	if((ptr = pcigetpmrb(p)) == -1)
+		return -1;
+
+	/*
+	 * Power Management Register Block:
+	 *  offset 0:	Capability ID
+	 *	   1:	next item pointer
+	 *	   2:	capabilities
+	 *	   4:	control/status
+	 *	   6:	bridge support extensions
+	 *	   7:	data
+	 */
+	pmcsr = pcicfgr16(p, ptr+4);
+
+	return pmcsr & 0x0003;
+}
+
+int
+pcisetpms(Pcidev* p, int state)
+{
+	int ostate, pmc, pmcsr, ptr;
+
+	if((ptr = pcigetpmrb(p)) == -1)
+		return -1;
+
+	pmc = pcicfgr16(p, ptr+2);
+	pmcsr = pcicfgr16(p, ptr+4);
+	ostate = pmcsr & 0x0003;
+	pmcsr &= ~0x0003;
+
+	switch(state){
+	default:
+		return -1;
+	case 0:
+		break;
+	case 1:
+		if(!(pmc & 0x0200))
+			return -1;
+		break;
+	case 2:
+		if(!(pmc & 0x0400))
+			return -1;
+		break;
+	case 3:
+		break;
+	}
+	pmcsr |= state;
+	pcicfgw16(p, ptr+4, pmcsr);
+
+	return ostate;
+}
--- /dev/null
+++ b/os/boot/pc/print.c
@@ -1,0 +1,31 @@
+#include "u.h"
+#include "lib.h"
+#include "mem.h"
+#include "dat.h"
+#include "fns.h"
+
+static Lock fmtl;
+
+void
+_fmtlock(void)
+{
+	lock(&fmtl);
+}
+
+void
+_fmtunlock(void)
+{
+	unlock(&fmtl);
+}
+
+int
+_efgfmt(Fmt*)
+{
+	return -1;
+}
+
+int
+errfmt(Fmt*)
+{
+	return -1;
+}
--- /dev/null
+++ b/os/boot/pc/queue.c
@@ -1,0 +1,44 @@
+#include	"u.h"
+#include	"lib.h"
+#include	"mem.h"
+#include	"dat.h"
+#include	"fns.h"
+#include	"io.h"
+
+int
+qgetc(IOQ *q)
+{
+	int c;
+
+	if(q->in == q->out)
+		return -1;
+	c = *q->out;
+	if(q->out == q->buf+sizeof(q->buf)-1)
+		q->out = q->buf;
+	else
+		q->out++;
+	return c;
+}
+
+static int
+qputc(IOQ *q, int c)
+{
+	uchar *nextin;
+	if(q->in >= &q->buf[sizeof(q->buf)-1])
+		nextin = q->buf;
+	else
+		nextin = q->in+1;
+	if(nextin == q->out)
+		return -1;
+	*q->in = c;
+	q->in = nextin;
+	return 0;
+}
+
+void
+qinit(IOQ *q)
+{
+	q->in = q->out = q->buf;
+	q->getc = qgetc;
+	q->putc = qputc;
+}
--- /dev/null
+++ b/os/boot/pc/sd.h
@@ -1,0 +1,126 @@
+/*
+ * Storage Device.
+ */
+typedef struct SDev SDev;
+typedef struct SDifc SDifc;
+typedef struct SDpart SDpart;
+typedef struct SDreq SDreq;
+typedef struct SDunit SDunit;
+
+typedef struct SDpart {
+	uvlong	start;
+	uvlong	end;
+	char	name[NAMELEN];
+	char	user[NAMELEN];
+	ulong	perm;
+	int	valid;
+	void *crud;
+} SDpart;
+
+typedef struct SDunit {
+	SDev*	dev;
+	int	subno;
+	uchar	inquiry[256];		/* format follows SCSI spec */
+	char	name[NAMELEN];
+//	Rendez	rendez;
+
+//	QLock	ctl;
+	uvlong	sectors;
+	ulong	secsize;
+	SDpart*	part;
+	int	npart;			/* of valid partitions */
+	int	changed;
+
+//	QLock	raw;
+	int	state;
+	ulong	pid;
+	SDreq*	req;
+} SDunit;
+
+typedef struct SDev {
+	SDifc*	ifc;			/* pnp/legacy */
+	void	*ctlr;
+	int	idno;
+	int	index;			/* into unit space */
+	int	nunit;
+	SDev*	next;
+
+//	QLock;				/* enable/disable */
+	int	enabled;
+} SDev;
+
+typedef struct SDifc {
+	char*	name;
+
+	SDev*	(*pnp)(void);
+	SDev*	(*legacy)(int, int);
+	SDev*	(*id)(SDev*);
+	int	(*enable)(SDev*);
+	int	(*disable)(SDev*);
+
+	int	(*verify)(SDunit*);
+	int	(*online)(SDunit*);
+	int	(*rio)(SDreq*);
+	int	(*rctl)(SDunit*, char*, int);
+	int	(*wctl)(SDunit*, void*);
+
+	long	(*bio)(SDunit*, int, int, void*, long, long);
+} SDifc;
+
+typedef struct SDreq {
+	SDunit*	unit;
+	int	lun;
+	int	write;
+	uchar	cmd[16];
+	int	clen;
+	void*	data;
+	int	dlen;
+
+	int	flags;
+
+	int	status;
+	long	rlen;
+	uchar	sense[256];
+} SDreq;
+
+enum {
+	SDnosense	= 0x00000001,
+	SDvalidsense	= 0x00010000,
+};
+
+enum {
+	SDmalloc	= -4,
+	SDeio		= -3,
+	SDtimeout	= -2,
+	SDnostatus	= -1,
+
+	SDok		= 0,
+
+	SDcheck		= 0x02,		/* check condition */
+	SDbusy		= 0x08,		/* busy */
+
+	SDmaxio		= 2048*1024,
+	SDnpart		= 16,
+};
+
+/* sdscsi.c */
+extern int scsiverify(SDunit*);
+extern int scsionline(SDunit*);
+extern long scsibio(SDunit*, int, int, void*, long, long);
+extern SDev* scsiid(SDev*, SDifc*);
+
+#define IrqATA0 14
+#define IrqATA1 15
+#define qlock(i)	while(0)
+#define qunlock(i)	while(0)
+
+#define putstrn consputs
+
+void sleep(void*, int(*)(void*), void*);
+void tsleep(void*, int(*)(void*), void*, int);
+#define wakeup(x) while(0)
+extern long sdbio(SDunit *unit, SDpart *pp, void *a, long len, vlong off);
+void	partition(SDunit*);
+SDpart* sdfindpart(SDunit*, char*);
+void	sdaddpart(SDunit*, char*, uvlong, uvlong);
+void*	sdmalloc(void*, ulong);
--- /dev/null
+++ b/os/boot/pc/sd53c8xx.c
@@ -1,0 +1,2124 @@
+/*
+ * NCR 53c8xx driver for Plan 9
+ * Nigel Roles (ngr@cotswold.demon.co.uk)
+ *
+ * 08/07/99	Ultra2 fixed. Brazil #ifdefs added. Fixed script error 6 diagnostics.
+ *
+ * 09/06/99	Enhancements to support 895 and 896 correctly. Attempt at Ultra 2 negotiation,
+ *		though no device to test with yet.
+ *		Variant now contains the number of valid chip registers to assist
+ *		dumpncrregs() 
+ *
+ * 06/10/98	Various bug fixes and Brazil compiler inspired changes from jmk
+ *
+ * 05/10/98	Small fix to handle command length being greater than expected by device
+ *
+ * 04/08/98     Added missing locks to interrupt handler. Marked places where 
+ *		multiple controller extensions could go
+ *
+ * 18/05/97	Fixed overestimate in size of local SCRIPT RAM
+ *
+ * 17/05/97	Bug fix to return status
+ *
+ * 06/10/96	Enhanced list of chip IDs. 875 revision 1 has no clock doubler, so assume it
+ *		is shipped with 80MHz crystal. Use bit 3 of the GPREG to recognise differential
+ *		boards. This is Symbios specific, but since they are about the only suppliers of
+ *		differential cards.
+ *
+ * 23/9/96	Wide and Ultra supported. 825A and 860 added to variants. Dual compiling
+ *		version for fileserver and cpu. 80MHz default clock for 860
+ *		
+ * 5/8/96	Waits for an Inquiry message before initiating synchronous negotiation
+ *		in case capabilities byte [7] indicates device does not support it. Devices
+ *		which do target initiated negotiation will typically get in first; a few
+ *		bugs in handling this have been fixed
+ *
+ * 3/8/96	Added differential support (put scsi0=diff in plan9.ini)
+ *		Split exec() into exec() and io(). Exec() is small, and Io() does not
+ *		use any Plan 9 specific data structures, so alternate exec() functions
+ *		may be done for other environments, such as the fileserver
+ *
+ * GENERAL
+ *
+ * Works on 810 and 875
+ * Should work on 815, 825, 810A, 825A, 860A
+ * Uses local RAM, large FIFO, prefetch, burst opcode fetch, and 16 byte synch. offset
+ * where applicable
+ * Supports multi-target, wide, Ultra
+ * Differential mode can be enabled by putting scsi0=diff in plan9.ini
+ * NO SUPPORT FOR tagged queuing (yet)
+ *
+ * Known problems
+ */
+
+#define MAXTARGET	16		/* can be 8 or 16 */
+
+#include "u.h"
+#include "lib.h"
+#include "mem.h"
+#include "dat.h"
+#include "fns.h"
+#include "io.h"
+#include "ureg.h"
+#include "error.h"
+
+#include "sd.h"
+extern SDifc sd53c8xxifc;
+
+#define waserror()	(0)
+#define poperror()
+typedef struct QLock{ int r; } QLock;
+typedef struct Rendez{ int r; } Rendez;
+#define	intrenable(irq, f, c, tbdf, name)	setvec(VectorPIC+(irq), f, c);
+
+/**********************************/
+/* Portable configuration macros  */
+/**********************************/
+
+//#define BOOTDEBUG
+//#define ASYNC_ONLY
+//#define	INTERNAL_SCLK
+//#define ALWAYS_DO_WDTR
+#define WMR_DEBUG
+
+/**********************************/
+/* CPU specific macros            */
+/**********************************/
+
+#ifdef BOOTDEBUG
+
+#define KPRINT oprint
+#define IPRINT intrprint
+#define DEBUG(n) 0
+#define IFLUSH() iflush()
+
+#else
+
+#define KPRINT	if(0)print
+#define IPRINT	if(0)print
+#define DEBUG(n)	(0)
+#define IFLUSH()
+
+#endif /* BOOTDEBUG */
+
+/*******************************/
+/* General                     */
+/*******************************/
+
+#ifndef DMASEG
+#define DMASEG(x) PADDR(x)
+#define legetl(x) (*(ulong*)(x))
+#define lesetl(x,v) (*(ulong*)(x) = (v))
+#define swabl(a,b,c)
+#else
+#endif /*DMASEG */
+#define DMASEG_TO_KADDR(x) KADDR(PADDR(x))
+#define KPTR(x) ((x) == 0 ? 0 : DMASEG_TO_KADDR(x))
+
+#define MEGA 1000000L
+#ifdef INTERNAL_SCLK
+#define	SCLK (33 * MEGA)
+#else
+#define SCLK (40 * MEGA)
+#endif /* INTERNAL_SCLK */
+#define ULTRA_NOCLOCKDOUBLE_SCLK (80 * MEGA)
+
+#define MAXSYNCSCSIRATE (5 * MEGA)
+#define MAXFASTSYNCSCSIRATE (10 * MEGA)
+#define MAXULTRASYNCSCSIRATE (20 * MEGA)
+#define MAXULTRA2SYNCSCSIRATE (40 * MEGA)
+#define MAXASYNCCORERATE (25 * MEGA)
+#define MAXSYNCCORERATE (25 * MEGA)
+#define MAXFASTSYNCCORERATE (50 * MEGA)
+#define MAXULTRASYNCCORERATE (80 * MEGA)
+#define MAXULTRA2SYNCCORERATE (160 * MEGA)
+
+
+#define X_MSG	1
+#define X_MSG_SDTR 1
+#define X_MSG_WDTR 3
+
+struct na_patch {
+	unsigned lwoff;
+	unsigned char type;
+};
+
+typedef struct Ncr {
+	uchar scntl0;	/* 00 */
+	uchar scntl1;
+	uchar scntl2;
+	uchar scntl3;
+
+	uchar scid;	/* 04 */
+	uchar sxfer;
+	uchar sdid;
+	uchar gpreg;
+
+	uchar sfbr;	/* 08 */
+	uchar socl;
+	uchar ssid;
+	uchar sbcl;
+
+	uchar dstat;	/* 0c */
+	uchar sstat0;
+	uchar sstat1;
+	uchar sstat2;
+
+	uchar dsa[4];	/* 10 */
+
+	uchar istat;	/* 14 */
+	uchar istatpad[3];
+
+	uchar ctest0;	/* 18 */
+	uchar ctest1;
+	uchar ctest2;
+	uchar ctest3;
+
+	uchar temp[4];	/* 1c */
+
+	uchar dfifo;	/* 20 */
+	uchar ctest4;
+	uchar ctest5;
+	uchar ctest6;
+
+	uchar dbc[3];	/* 24 */
+	uchar dcmd;	/* 27 */
+
+	uchar dnad[4];	/* 28 */
+	uchar dsp[4];	/* 2c */
+	uchar dsps[4];	/* 30 */
+
+	uchar scratcha[4];	/* 34 */
+
+	uchar dmode;	/* 38 */
+	uchar dien;
+	uchar dwt;
+	uchar dcntl;
+
+	uchar adder[4];	/* 3c */
+
+	uchar sien0;	/* 40 */
+	uchar sien1;
+	uchar sist0;
+	uchar sist1;
+
+	uchar slpar;	/* 44 */
+	uchar slparpad0;
+	uchar macntl;
+	uchar gpcntl;
+
+	uchar stime0;	/* 48 */
+	uchar stime1;
+	uchar respid;
+	uchar respidpad0;
+
+	uchar stest0;	/* 4c */
+	uchar stest1;
+	uchar stest2;
+	uchar stest3;
+
+	uchar sidl;	/* 50 */
+	uchar sidlpad[3];
+
+	uchar sodl;	/* 54 */
+	uchar sodlpad[3];
+
+	uchar sbdl;	/* 58 */
+	uchar sbdlpad[3];
+
+	uchar scratchb[4];	/* 5c */
+} Ncr;
+
+typedef struct Movedata {
+	uchar dbc[4];
+	uchar pa[4];
+} Movedata;
+
+typedef enum NegoState {
+	NeitherDone, WideInit, WideResponse, WideDone,
+	SyncInit, SyncResponse, BothDone
+} NegoState;
+
+typedef enum State {
+	Allocated, Queued, Active, Done
+} State;
+
+typedef struct Dsa {
+	union {
+		uchar state[4];
+		struct {
+			uchar stateb;
+			uchar result;
+			uchar dmablks;
+			uchar flag;	/* setbyte(state,3,...) */
+		};
+	};
+
+	union {
+		ulong dmancr;		/* For block transfer: NCR order (little-endian) */
+		uchar dmaaddr[4];
+	};
+
+	uchar target;			/* Target */
+	uchar pad0[3];
+
+	uchar lun;			/* Logical Unit Number */
+	uchar pad1[3];
+
+	uchar scntl3;
+	uchar sxfer;
+	uchar pad2[2];
+
+	uchar next[4];			/* chaining for SCRIPT (NCR byte order) */
+	struct Dsa *freechain;		/* chaining for freelist */
+	Rendez;
+	uchar scsi_id_buf[4];
+	Movedata msg_out_buf;
+	Movedata cmd_buf;
+	Movedata data_buf;
+	Movedata status_buf;
+	uchar msg_out[10];		/* enough to include SDTR */
+	uchar status;
+	int p9status;
+	uchar parityerror;
+} Dsa;
+
+typedef enum Feature {
+	BigFifo = 1,			/* 536 byte fifo */
+	BurstOpCodeFetch = 2,		/* burst fetch opcodes */
+	Prefetch = 4,			/* prefetch 8 longwords */
+	LocalRAM = 8,			/* 4K longwords of local RAM */
+	Differential = 16,		/* Differential support */
+	Wide = 32,			/* Wide capable */
+	Ultra = 64,			/* Ultra capable */
+	ClockDouble = 128,		/* Has clock doubler */
+	ClockQuad = 256,		/* Has clock quadrupler (same as Ultra2) */
+	Ultra2 = 256,
+} Feature;
+
+typedef enum Burst {
+	Burst2 = 0,
+	Burst4 = 1,
+	Burst8 = 2,
+	Burst16 = 3,
+	Burst32 = 4,
+	Burst64 = 5,
+	Burst128 = 6
+} Burst;
+
+typedef struct Variant {
+	ushort did;
+	uchar maxrid;			/* maximum allowed revision ID */
+	char *name;
+	Burst burst;			/* codings for max burst */
+	uchar maxsyncoff;		/* max synchronous offset */
+	uchar registers;		/* number of 32 bit registers */
+	unsigned feature;
+} Variant;
+
+static unsigned char cf2[] = { 6, 2, 3, 4, 6, 8, 12, 16 };
+#define NULTRA2SCF (sizeof(cf2)/sizeof(cf2[0]))
+#define NULTRASCF (NULTRA2SCF - 2)
+#define NSCF (NULTRASCF - 1)
+
+typedef struct Controller {
+	Lock;
+	struct {
+		uchar scntl3;
+		uchar stest2;
+	} bios;
+	uchar synctab[NULTRA2SCF - 1][8];/* table of legal tpfs */
+	NegoState s[MAXTARGET];
+	uchar scntl3[MAXTARGET];
+	uchar sxfer[MAXTARGET];
+	uchar cap[MAXTARGET];		/* capabilities byte from Identify */
+	ushort capvalid;		/* bit per target for validity of cap[] */
+	ushort wide;			/* bit per target set if wide negotiated */
+	ulong sclk;			/* clock speed of controller */
+	uchar clockmult;		/* set by synctabinit */
+	uchar ccf;			/* CCF bits */
+	uchar tpf;			/* best tpf value for this controller */
+	uchar feature;			/* requested features */
+	int running;			/* is the script processor running? */
+	int ssm;			/* single step mode */
+	Ncr *n;				/* pointer to registers */
+	Variant *v;			/* pointer to variant type */
+	ulong *script;			/* where the real script is */
+	ulong scriptpa;			/* where the real script is */
+	Pcidev* pcidev;
+	SDev*	sdev;
+
+	struct {
+		Lock;
+		uchar head[4];		/* head of free list (NCR byte order) */
+		Dsa	*tail;
+		Dsa	*freechain;
+	} dsalist;
+
+	QLock q[MAXTARGET];		/* queues for each target */
+} Controller;
+
+static Controller controller;
+
+/* ISTAT */
+enum { Abrt = 0x80, Srst = 0x40, Sigp = 0x20, Sem = 0x10, Con = 0x08, Intf = 0x04, Sip = 0x02, Dip = 0x01 };
+
+/* DSTAT */
+enum { Dfe = 0x80, Mdpe = 0x40, Bf = 0x20, Abrted = 0x10, Ssi = 0x08, Sir = 0x04, Iid = 0x01 };
+
+/* SSTAT */
+enum { DataOut, DataIn, Cmd, Status, ReservedOut, ReservedIn, MessageOut, MessageIn };
+
+static void setmovedata(Movedata*, ulong, ulong);
+static void advancedata(Movedata*, long);
+static int bios_set_differential(Controller *c);
+
+static char *phase[] = {
+	"data out", "data in", "command", "status",
+	"reserved out", "reserved in", "message out", "message in"
+};
+
+#ifdef BOOTDEBUG
+#define DEBUGSIZE 10240
+char debugbuf[DEBUGSIZE];
+char *debuglast;
+
+static void
+intrprint(char *format, ...)
+{
+	if (debuglast == 0)
+		debuglast = debugbuf;
+	debuglast = doprint(debuglast, debugbuf + (DEBUGSIZE - 1), format, (&format + 1));
+}
+
+static void
+iflush()
+{
+	int s;
+	char *endp;
+	s = splhi();
+	if (debuglast == 0)
+		debuglast = debugbuf;
+	if (debuglast == debugbuf) {
+		splx(s);
+		return;
+	}
+	endp = debuglast;
+	splx(s);
+	screenputs(debugbuf, endp - debugbuf);
+	s = splhi();
+	memmove(debugbuf, endp, debuglast - endp);
+	debuglast -= endp - debugbuf;
+	splx(s);
+}
+
+static void
+oprint(char *format, ...)
+{
+	int s;
+
+	iflush();
+	s = splhi();
+	if (debuglast == 0)
+		debuglast = debugbuf;
+	debuglast = doprint(debuglast, debugbuf + (DEBUGSIZE - 1), format, (&format + 1));
+	splx(s);
+	iflush();	
+}
+#endif
+
+#include "sd53c8xx.i"
+
+static Dsa *
+dsaalloc(Controller *c, int target, int lun)
+{
+	Dsa *d;
+
+	ilock(&c->dsalist);
+	if ((d = c->dsalist.freechain) == 0) {
+		d = xalloc(sizeof(*d));
+		if (DEBUG(1))
+			KPRINT("sd53c8xx: %d/%d: allocated new dsa %lux\n", target, lun, d);
+		lesetl(d->next, 0);
+		lesetl(d->state, A_STATE_ALLOCATED);
+		if (legetl(c->dsalist.head) == 0)
+			lesetl(c->dsalist.head, DMASEG(d));	/* ATOMIC?!? */
+		else
+			lesetl(c->dsalist.tail->next, DMASEG(d));	/* ATOMIC?!? */
+		c->dsalist.tail = d;
+	}
+	else {
+		if (DEBUG(1))
+			KPRINT("sd53c8xx: %d/%d: reused dsa %lux\n", target, lun, d);
+		c->dsalist.freechain = d->freechain;
+		lesetl(d->state, A_STATE_ALLOCATED);
+	}
+	iunlock(&c->dsalist);
+	d->target = target;
+	d->lun = lun;
+	return d;
+}
+
+static void
+dsafree(Controller *c, Dsa *d)
+{
+	ilock(&c->dsalist);
+	d->freechain = c->dsalist.freechain;
+	c->dsalist.freechain = d;
+	lesetl(d->state, A_STATE_FREE);
+	iunlock(&c->dsalist);
+}
+
+static Dsa *
+dsafind(Controller *c, uchar target, uchar lun, uchar state)
+{
+	Dsa *d;
+	for (d = KPTR(legetl(c->dsalist.head)); d; d = KPTR(legetl(d->next))) {
+		if (d->target != 0xff && d->target != target)
+			continue;
+		if (lun != 0xff && d->lun != lun)
+			continue;
+		if (state != 0xff && d->stateb != state)
+			continue;
+		break;
+	}
+	return d;
+}
+
+static void
+dumpncrregs(Controller *c, int intr)
+{
+	int i;
+	Ncr *n = c->n;
+	int depth = c->v->registers / 4;
+
+	KPRINT("sa = %.8lux\n", c->scriptpa);
+	for (i = 0; i < depth; i++) {
+		int j;
+		for (j = 0; j < 4; j++) {
+			int k = j * depth + i;
+			uchar *p;
+
+			/* display little-endian to make 32-bit values readable */
+			p = (uchar*)n+k*4;
+			if (intr)
+				IPRINT(" %.2x%.2x%.2x%.2x %.2x %.2x", p[3], p[2], p[1], p[0], k * 4, (k * 4) + 0x80);
+			else
+				KPRINT(" %.2x%.2x%.2x%.2x %.2x %.2x", p[3], p[2], p[1], p[0], k * 4, (k * 4) + 0x80);
+			USED(p);
+		}
+		if (intr)
+			IPRINT("\n");
+		else
+			KPRINT("\n");
+	}
+}	
+
+static int
+chooserate(Controller *c, int tpf, int *scfp, int *xferpp)
+{
+	/* find lowest entry >= tpf */
+	int besttpf = 1000;
+	int bestscfi = 0;
+	int bestxferp = 0;
+	int scf, xferp;
+	int maxscf;
+
+	if (c->v->feature & Ultra2)
+		maxscf = NULTRA2SCF;
+	else if (c->v->feature & Ultra)
+		maxscf = NULTRASCF;
+	else
+		maxscf = NSCF;
+
+	/*
+	 * search large clock factors first since this should
+	 * result in more reliable transfers
+	 */
+	for (scf = maxscf; scf >= 1; scf--) {
+		for (xferp = 0; xferp < 8; xferp++) {
+			unsigned char v = c->synctab[scf - 1][xferp];
+			if (v == 0)
+				continue;
+			if (v >= tpf && v < besttpf) {
+				besttpf = v;
+				bestscfi = scf;
+				bestxferp = xferp;
+			}
+		}
+	}
+	if (besttpf == 1000)
+		return 0;
+	if (scfp)
+		*scfp = bestscfi;
+	if (xferpp)
+		*xferpp = bestxferp;
+	return besttpf;
+}
+
+static void
+synctabinit(Controller *c)
+{
+	int scf;
+	unsigned long scsilimit;
+	int xferp;
+	unsigned long cr, sr;
+	int tpf;
+	int fast;
+	int maxscf;
+
+	if (c->v->feature & Ultra2)
+		maxscf = NULTRA2SCF;
+	else if (c->v->feature & Ultra)
+		maxscf = NULTRASCF;
+	else
+		maxscf = NSCF;
+
+	/*
+	 * for chips with no clock doubler, but Ultra capable (e.g. 860, or interestingly the
+	 * first spin of the 875), assume 80MHz
+	 * otherwise use the internal (33 Mhz) or external (40MHz) default
+	 */
+
+	if ((c->v->feature & Ultra) != 0 && (c->v->feature & (ClockDouble | ClockQuad)) == 0)
+		c->sclk = ULTRA_NOCLOCKDOUBLE_SCLK;
+	else
+		c->sclk = SCLK;
+
+	/*
+	 * otherwise, if the chip is Ultra capable, but has a slow(ish) clock,
+	 * invoke the doubler
+	 */
+
+	if (SCLK <= 40000000) {
+		if (c->v->feature & ClockDouble) {
+			c->sclk *= 2;
+			c->clockmult = 1;
+		}
+		else if (c->v->feature & ClockQuad) {
+			c->sclk *= 4;
+			c->clockmult = 1;
+		}
+		else
+			c->clockmult = 0;
+	}
+	else
+		c->clockmult = 0;
+
+	/* derive CCF from sclk */
+	/* woebetide anyone with SCLK < 16.7 or > 80MHz */
+	if (c->sclk <= 25 * MEGA)
+		c->ccf = 1;
+	else if (c->sclk <= 3750000)
+		c->ccf = 2;
+	else if (c->sclk <= 50 * MEGA)
+		c->ccf = 3;
+	else if (c->sclk <= 75 * MEGA)
+		c->ccf = 4;
+	else if ((c->v->feature & ClockDouble) && c->sclk <= 80 * MEGA)
+		c->ccf = 5;
+	else if ((c->v->feature & ClockQuad) && c->sclk <= 120 * MEGA)
+		c->ccf = 6;
+	else if ((c->v->feature & ClockQuad) && c->sclk <= 160 * MEGA)
+		c->ccf = 7;
+
+	for (scf = 1; scf < maxscf; scf++) {
+		/* check for legal core rate */
+		/* round up so we run slower for safety */
+	   	cr = (c->sclk * 2 + cf2[scf] - 1) / cf2[scf];
+		if (cr <= MAXSYNCCORERATE) {
+			scsilimit = MAXSYNCSCSIRATE;
+			fast = 0;
+		}
+		else if (cr <= MAXFASTSYNCCORERATE) {
+			scsilimit = MAXFASTSYNCSCSIRATE;
+			fast = 1;
+		}
+		else if ((c->v->feature & Ultra) && cr <= MAXULTRASYNCCORERATE) {
+			scsilimit = MAXULTRASYNCSCSIRATE;
+			fast = 2;
+		}
+		else if ((c->v->feature & Ultra2) && cr <= MAXULTRA2SYNCCORERATE) {
+			scsilimit = MAXULTRA2SYNCSCSIRATE;
+			fast = 3;
+		}
+		else
+			continue;
+		for (xferp = 11; xferp >= 4; xferp--) {
+			int ok;
+			int tp;
+			/* calculate scsi rate - round up again */
+			/* start from sclk for accuracy */
+			int totaldivide = xferp * cf2[scf];
+			sr = (c->sclk * 2 + totaldivide - 1) / totaldivide;
+			if (sr > scsilimit)
+				break;
+			/*
+			 * now work out transfer period
+			 * round down now so that period is pessimistic
+			 */
+			tp = (MEGA * 1000) / sr;
+			/*
+			 * bounds check it
+			 */
+			if (tp < 25 || tp > 255 * 4)
+				continue;
+			/*
+			 * spot stupid special case for Ultra or Ultra2
+			 * while working out factor
+			 */
+			if (tp == 25)
+				tpf = 10;
+			else if (tp == 50)
+				tpf = 12;
+			else if (tp < 52)
+				continue;
+			else
+				tpf = tp / 4;
+			/*
+			 * now check tpf looks sensible
+			 * given core rate
+			 */
+			switch (fast) {
+			case 0:
+				/* scf must be ccf for SCSI 1 */
+				ok = tpf >= 50 && scf == c->ccf;
+				break;
+			case 1:
+				ok = tpf >= 25 && tpf < 50;
+				break;
+			case 2:
+				/*
+				 * must use xferp of 4, or 5 at a pinch
+				 * for an Ultra transfer
+				 */
+				ok = xferp <= 5 && tpf >= 12 && tpf < 25;
+				break;
+			case 3:
+				ok = xferp == 4 && (tpf == 10 || tpf == 11);
+				break;
+			default:
+				ok = 0;
+			}
+			if (!ok)
+				continue;
+			c->synctab[scf - 1][xferp - 4] = tpf;
+		}
+	}
+
+#ifndef NO_ULTRA2
+	if (c->v->feature & Ultra2)
+		tpf = 10;
+	else
+#endif
+	if (c->v->feature & Ultra)
+		tpf = 12;
+	else
+		tpf = 25;
+	for (; tpf < 256; tpf++) {
+		if (chooserate(c, tpf, &scf, &xferp) == tpf) {
+			unsigned tp = tpf == 10 ? 25 : (tpf == 12 ? 50 : tpf * 4);
+			unsigned long khz = (MEGA + tp - 1) / (tp);
+			KPRINT("sd53c8xx: tpf=%d scf=%d.%.1d xferp=%d mhz=%ld.%.3ld\n",
+			    tpf, cf2[scf] / 2, (cf2[scf] & 1) ? 5 : 0,
+			    xferp + 4, khz / 1000, khz % 1000);
+			USED(khz);
+			if (c->tpf == 0)
+				c->tpf = tpf;	/* note lowest value for controller */
+		}
+	}
+}
+
+static void
+synctodsa(Dsa *dsa, Controller *c)
+{
+/*
+	KPRINT("synctodsa(dsa=%lux, target=%d, scntl3=%.2lx sxfer=%.2x)\n",
+	    dsa, dsa->target, c->scntl3[dsa->target], c->sxfer[dsa->target]);
+*/
+	dsa->scntl3 = c->scntl3[dsa->target];
+	dsa->sxfer = c->sxfer[dsa->target];
+}
+
+static void
+setsync(Dsa *dsa, Controller *c, int target, uchar ultra, uchar scf, uchar xferp, uchar reqack)
+{
+	c->scntl3[target] =
+	    (c->scntl3[target] & 0x08) | (((scf << 4) | c->ccf | (ultra << 7)) & ~0x08);
+	c->sxfer[target] = (xferp << 5) | reqack;
+	c->s[target] = BothDone;
+	if (dsa) {
+		synctodsa(dsa, c);
+		c->n->scntl3 = c->scntl3[target];
+		c->n->sxfer = c->sxfer[target];
+	}
+}
+
+static void
+setasync(Dsa *dsa, Controller *c, int target)
+{
+	setsync(dsa, c, target, 0, c->ccf, 0, 0);
+}
+
+static void
+setwide(Dsa *dsa, Controller *c, int target, uchar wide)
+{
+	c->scntl3[target] = wide ? (1 << 3) : 0;
+	setasync(dsa, c, target);
+	c->s[target] = WideDone;
+}
+
+static int
+buildsdtrmsg(uchar *buf, uchar tpf, uchar offset)
+{
+	*buf++ = X_MSG;
+	*buf++ = 3;
+	*buf++ = X_MSG_SDTR;
+	*buf++ = tpf;
+	*buf = offset;
+	return 5;
+}
+
+static int
+buildwdtrmsg(uchar *buf, uchar expo)
+{
+	*buf++ = X_MSG;
+	*buf++ = 2;
+	*buf++ = X_MSG_WDTR;
+	*buf = expo;
+	return 4;
+}
+
+static void
+start(Controller *c, long entry)
+{
+	ulong p;
+
+	if (c->running)
+		panic("sd53c8xx: start called while running");
+	c->running = 1;
+	p = c->scriptpa + entry;
+	lesetl(c->n->dsp, p);
+	if (c->ssm)
+		c->n->dcntl |= 0x4;		/* start DMA in SSI mode */
+}
+
+static void
+ncrcontinue(Controller *c)
+{
+	if (c->running)
+		panic("sd53c8xx: ncrcontinue called while running");
+	/* set the start DMA bit to continue execution */
+	c->running = 1;
+	c->n->dcntl |= 0x4;
+}
+
+static void
+softreset(Controller *c)
+{
+	Ncr *n = c->n;
+
+	n->istat = Srst;		/* software reset */
+	n->istat = 0;
+	/* general initialisation */
+	n->scid = (1 << 6) | 7;		/* respond to reselect, ID 7 */
+	n->respid = 1 << 7;		/* response ID = 7 */
+
+#ifdef INTERNAL_SCLK
+	n->stest1 = 0x80;		/* disable external scsi clock */
+#else
+	n->stest1 = 0x00;
+#endif
+
+	n->stime0 = 0xdd;		/* about 0.5 second timeout on each device */
+	n->scntl0 |= 0x8;		/* Enable parity checking */
+
+	/* continued setup */
+	n->sien0 = 0x8f;
+	n->sien1 = 0x04;
+	n->dien = 0x7d;
+	n->stest3 = 0x80;		/* TolerANT enable */
+	c->running = 0;
+
+	if (c->v->feature & BigFifo)
+		n->ctest5 = (1 << 5);
+	n->dmode = c->v->burst << 6;	/* set burst length bits */
+	if (c->v->burst & 4)
+		n->ctest5 |= (1 << 2);	/* including overflow into ctest5 bit 2 */
+	if (c->v->feature & Prefetch)
+		n->dcntl |= (1 << 5);	/* prefetch enable */
+	else if (c->v->feature & BurstOpCodeFetch)
+		n->dmode |= (1 << 1);	/* burst opcode fetch */
+	if (c->v->feature & Differential) {
+		/* chip capable */
+		if ((c->feature & Differential) || bios_set_differential(c)) {
+			/* user enabled, or some evidence bios set differential */
+			if (n->sstat2 & (1 << 2))
+				print("sd53c8xx: can't go differential; wrong cable\n");
+			else {
+				n->stest2 = (1 << 5);
+				print("sd53c8xx: differential mode set\n");
+			}
+		}
+	}
+	if (c->clockmult) {
+		n->stest1 |= (1 << 3);	/* power up doubler */
+		delay(2);
+		n->stest3 |= (1 << 5);	/* stop clock */
+		n->stest1 |= (1 << 2);	/* enable doubler */
+		n->stest3 &= ~(1 << 5);	/* start clock */
+		/* pray */
+	}
+}
+
+static void
+msgsm(Dsa *dsa, Controller *c, int msg, int *cont, int *wakeme)
+{
+	uchar histpf, hisreqack;
+	int tpf;
+	int scf, xferp;
+	int len;
+
+	Ncr *n = c->n;
+
+	switch (c->s[dsa->target]) {
+	case SyncInit:
+		switch (msg) {
+		case A_SIR_MSG_SDTR:
+			/* reply to my SDTR */
+			histpf = n->scratcha[2];
+			hisreqack = n->scratcha[3];
+			KPRINT("sd53c8xx: %d: SDTN response %d %d\n",
+			    dsa->target, histpf, hisreqack);
+
+			if (hisreqack == 0)
+				setasync(dsa, c, dsa->target);
+			else {
+				/* hisreqack should be <= c->v->maxsyncoff */
+				tpf = chooserate(c, histpf, &scf, &xferp);
+				KPRINT("sd53c8xx: %d: SDTN: using %d %d\n",
+				    dsa->target, tpf, hisreqack);
+				setsync(dsa, c, dsa->target, tpf < 25, scf, xferp, hisreqack);
+			}
+			*cont = -2;
+			return;
+		case A_SIR_EV_PHASE_SWITCH_AFTER_ID:
+			/* target ignored ATN for message after IDENTIFY - not SCSI-II */
+			KPRINT("sd53c8xx: %d: illegal phase switch after ID message - SCSI-1 device?\n", dsa->target);
+			KPRINT("sd53c8xx: %d: SDTN: async\n", dsa->target);
+			setasync(dsa, c, dsa->target);
+			*cont = E_to_decisions;
+			return;
+		case A_SIR_MSG_REJECT:
+			/* rejection of my SDTR */
+			KPRINT("sd53c8xx: %d: SDTN: rejected SDTR\n", dsa->target);
+		//async:
+			KPRINT("sd53c8xx: %d: SDTN: async\n", dsa->target);
+			setasync(dsa, c, dsa->target);
+			*cont = -2;
+			return;
+		}
+		break;
+	case WideInit:
+		switch (msg) {
+		case A_SIR_MSG_WDTR:
+			/* reply to my WDTR */
+			KPRINT("sd53c8xx: %d: WDTN: response %d\n",
+			    dsa->target, n->scratcha[2]);
+			setwide(dsa, c, dsa->target, n->scratcha[2]);
+			*cont = -2;
+			return;
+		case A_SIR_EV_PHASE_SWITCH_AFTER_ID:
+			/* target ignored ATN for message after IDENTIFY - not SCSI-II */
+			KPRINT("sd53c8xx: %d: illegal phase switch after ID message - SCSI-1 device?\n", dsa->target);
+			setwide(dsa, c, dsa->target, 0);
+			*cont = E_to_decisions;
+			return;
+		case A_SIR_MSG_REJECT:
+			/* rejection of my SDTR */
+			KPRINT("sd53c8xx: %d: WDTN: rejected WDTR\n", dsa->target);
+			setwide(dsa, c, dsa->target, 0);
+			*cont = -2;
+			return;
+		}
+		break;
+
+	case NeitherDone:
+	case WideDone:
+	case BothDone:
+		switch (msg) {
+		case A_SIR_MSG_WDTR: {
+			uchar hiswide, mywide;
+			hiswide = n->scratcha[2];
+			mywide = (c->v->feature & Wide) != 0;
+			KPRINT("sd53c8xx: %d: WDTN: target init %d\n",
+			    dsa->target, hiswide);
+			if (hiswide < mywide)
+				mywide = hiswide;
+			KPRINT("sd53c8xx: %d: WDTN: responding %d\n",
+			    dsa->target, mywide);
+			setwide(dsa, c, dsa->target, mywide);
+			len = buildwdtrmsg(dsa->msg_out, mywide);
+			setmovedata(&dsa->msg_out_buf, DMASEG(dsa->msg_out), len);
+			*cont = E_response;
+			c->s[dsa->target] = WideResponse;
+			return;
+		}
+		case A_SIR_MSG_SDTR:
+#ifdef ASYNC_ONLY
+			*cont = E_reject;
+			return;
+#else
+			/* target decides to renegotiate */
+			histpf = n->scratcha[2];
+			hisreqack = n->scratcha[3];
+			KPRINT("sd53c8xx: %d: SDTN: target init %d %d\n",
+			    dsa->target, histpf, hisreqack);
+			if (hisreqack == 0) {
+				/* he wants asynchronous */
+				setasync(dsa, c, dsa->target);
+				tpf = 0;
+			}
+			else {
+				/* he wants synchronous */
+				tpf = chooserate(c, histpf, &scf, &xferp);
+				if (hisreqack > c->v->maxsyncoff)
+					hisreqack = c->v->maxsyncoff;
+				KPRINT("sd53c8xx: %d: using %d %d\n",
+				    dsa->target, tpf, hisreqack);
+				setsync(dsa, c, dsa->target, tpf < 25, scf, xferp, hisreqack);
+			}
+			/* build my SDTR message */
+			len = buildsdtrmsg(dsa->msg_out, tpf, hisreqack);
+			setmovedata(&dsa->msg_out_buf, DMASEG(dsa->msg_out), len);
+			*cont = E_response;
+			c->s[dsa->target] = SyncResponse;
+			return;
+#endif
+		}
+		break;
+	case WideResponse:
+		switch (msg) {
+		case A_SIR_EV_RESPONSE_OK:
+			c->s[dsa->target] = WideDone;
+			KPRINT("sd53c8xx: %d: WDTN: response accepted\n", dsa->target);
+			*cont = -2;
+			return;
+		case A_SIR_MSG_REJECT:
+			setwide(dsa, c, dsa->target, 0);
+			KPRINT("sd53c8xx: %d: WDTN: response REJECTed\n", dsa->target);
+			*cont = -2;
+			return;
+		}
+		break;
+	case SyncResponse:
+		switch (msg) {
+		case A_SIR_EV_RESPONSE_OK:
+			c->s[dsa->target] = BothDone;
+			KPRINT("sd53c8xx: %d: SDTN: response accepted (%s)\n",
+			    dsa->target, phase[n->sstat1 & 7]);
+			*cont = -2;
+			return;	/* chf */
+		case A_SIR_MSG_REJECT:
+			setasync(dsa, c, dsa->target);
+			KPRINT("sd53c8xx: %d: SDTN: response REJECTed\n", dsa->target);
+			*cont = -2;
+			return;
+		}
+		break;
+	}
+	KPRINT("sd53c8xx: %d: msgsm: state %d msg %d\n",
+	    dsa->target, c->s[dsa->target], msg);
+	*wakeme = 1;
+	return;
+}
+
+static void
+calcblockdma(Dsa *d, ulong base, ulong count)
+{
+	ulong blocks;
+	if (DEBUG(3))
+		blocks = 0;
+	else {
+		blocks = count / A_BSIZE;
+		if (blocks > 255)
+			blocks = 255;
+	}
+	d->dmablks = blocks;
+	d->dmaaddr[0] = base;
+	d->dmaaddr[1] = base >> 8;
+	d->dmaaddr[2] = base >> 16;
+	d->dmaaddr[3] = base >> 24;
+	setmovedata(&d->data_buf, base + blocks * A_BSIZE, count - blocks * A_BSIZE);
+	if (legetl(d->data_buf.dbc) == 0)
+		d->flag = 1;
+}
+
+static ulong
+read_mismatch_recover(Controller *c, Ncr *n, Dsa *dsa)
+{
+	ulong dbc;
+	uchar dfifo = n->dfifo;
+	int inchip;
+
+	dbc = (n->dbc[2]<<16)|(n->dbc[1]<<8)|n->dbc[0];
+	if (n->ctest5 & (1 << 5))
+		inchip = ((dfifo | ((n->ctest5 & 3) << 8)) - (dbc & 0x3ff)) & 0x3ff;
+	else
+		inchip = ((dfifo & 0x7f) - (dbc & 0x7f)) & 0x7f;
+	if (inchip) {
+		IPRINT("sd53c8xx: %d/%d: read_mismatch_recover: DMA FIFO = %d\n",
+		    dsa->target, dsa->lun, inchip);
+	}
+	if (n->sxfer & 0xf) {
+		/* SCSI FIFO */
+		uchar fifo = n->sstat1 >> 4;
+		if (c->v->maxsyncoff > 8)
+			fifo |= (n->sstat2 & (1 << 4));
+		if (fifo) {
+			inchip += fifo;
+			IPRINT("sd53c8xx: %d/%d: read_mismatch_recover: SCSI FIFO = %d\n",
+			    dsa->target, dsa->lun, fifo);
+		}
+	}
+	else {
+		if (n->sstat0 & (1 << 7)) {
+			inchip++;
+			IPRINT("sd53c8xx: %d/%d: read_mismatch_recover: SIDL full\n",
+			    dsa->target, dsa->lun);
+		}
+		if (n->sstat2 & (1 << 7)) {
+			inchip++;
+			IPRINT("sd53c8xx: %d/%d: read_mismatch_recover: SIDL msb full\n",
+			    dsa->target, dsa->lun);
+		}
+	}
+	USED(inchip);
+	return dbc;
+}
+
+static ulong
+write_mismatch_recover(Ncr *n, Dsa *dsa)
+{
+	ulong dbc;
+	uchar dfifo = n->dfifo;
+	int inchip;
+
+	dbc = (n->dbc[2]<<16)|(n->dbc[1]<<8)|n->dbc[0];
+	USED(dsa);
+	if (n->ctest5 & (1 << 5))
+		inchip = ((dfifo | ((n->ctest5 & 3) << 8)) - (dbc & 0x3ff)) & 0x3ff;
+	else
+		inchip = ((dfifo & 0x7f) - (dbc & 0x7f)) & 0x7f;
+#ifdef WMR_DEBUG
+	if (inchip) {
+		IPRINT("sd53c8xx: %d/%d: write_mismatch_recover: DMA FIFO = %d\n",
+		    dsa->target, dsa->lun, inchip);
+	}
+#endif
+	if (n->sstat0 & (1 << 5)) {
+		inchip++;
+#ifdef WMR_DEBUG
+		IPRINT("sd53c8xx: %d/%d: write_mismatch_recover: SODL full\n", dsa->target, dsa->lun);
+#endif
+	}
+	if (n->sstat2 & (1 << 5)) {
+		inchip++;
+#ifdef WMR_DEBUG
+		IPRINT("sd53c8xx: %d/%d: write_mismatch_recover: SODL msb full\n", dsa->target, dsa->lun);
+#endif
+	}
+	if (n->sxfer & 0xf) {
+		/* synchronous SODR */
+		if (n->sstat0 & (1 << 6)) {
+			inchip++;
+#ifdef WMR_DEBUG
+			IPRINT("sd53c8xx: %d/%d: write_mismatch_recover: SODR full\n",
+			    dsa->target, dsa->lun);
+#endif
+		}
+		if (n->sstat2 & (1 << 6)) {
+			inchip++;
+#ifdef WMR_DEBUG
+			IPRINT("sd53c8xx: %d/%d: write_mismatch_recover: SODR msb full\n",
+			    dsa->target, dsa->lun);
+#endif
+		}
+	}
+	/* clear the dma fifo */
+	n->ctest3 |= (1 << 2);
+	/* wait till done */
+	while ((n->dstat & Dfe) == 0)
+		;
+	return dbc + inchip;
+}
+
+static void
+interrupt(Ureg *ur, void *a)
+{
+	uchar istat;
+	ushort sist;
+	uchar dstat;
+	int wakeme = 0;
+	int cont = -1;
+	Dsa *dsa;
+	Controller *c = a;
+	Ncr *n = c->n;
+
+	USED(ur);
+	if (DEBUG(1))
+		IPRINT("sd53c8xx: int\n");
+	ilock(c);
+	istat = n->istat;
+	if (istat & Intf) {
+		Dsa *d;
+		int wokesomething = 0;
+		if (DEBUG(1))
+			IPRINT("sd53c8xx: Intfly\n");
+		n->istat = Intf;
+		/* search for structures in A_STATE_DONE */
+		for (d = KPTR(legetl(c->dsalist.head)); d; d = KPTR(legetl(d->next))) {
+			if (d->stateb == A_STATE_DONE) {
+				d->p9status = d->status;
+				if (DEBUG(1))
+					IPRINT("sd53c8xx: waking up dsa %lux\n", d);
+				wakeup(d);
+				wokesomething = 1;
+			}
+		}
+		if (!wokesomething)
+			IPRINT("sd53c8xx: nothing to wake up\n");
+	}
+
+	if ((istat & (Sip | Dip)) == 0) {
+		if (DEBUG(1))
+			IPRINT("sd53c8xx: int end %x\n", istat);
+		iunlock(c);
+		return;
+	}
+
+	sist = (n->sist1<<8)|n->sist0;	/* BUG? can two-byte read be inconsistent? */
+	dstat = n->dstat;
+	dsa = (Dsa *)DMASEG_TO_KADDR(legetl(n->dsa));
+	c->running = 0;
+	if (istat & Sip) {
+		if (DEBUG(1))
+			IPRINT("sist = %.4x\n", sist);
+		if (sist & 0x80) {
+			ulong addr;
+			ulong sa;
+			ulong dbc;
+			ulong tbc;
+			int dmablks;
+			ulong dmaaddr;
+
+			addr = legetl(n->dsp);
+			sa = addr - c->scriptpa;
+			if (DEBUG(1) || DEBUG(2))
+				IPRINT("sd53c8xx: %d/%d: Phase Mismatch sa=%.8lux\n",
+				    dsa->target, dsa->lun, sa);
+			/*
+			 * now recover
+			 */
+			if (sa == E_data_in_mismatch) {
+				dbc = read_mismatch_recover(c, n, dsa);
+				tbc = legetl(dsa->data_buf.dbc) - dbc;
+				advancedata(&dsa->data_buf, tbc);
+				if (DEBUG(1) || DEBUG(2))
+					IPRINT("sd53c8xx: %d/%d: transferred = %ld residue = %ld\n",
+					    dsa->target, dsa->lun, tbc, legetl(dsa->data_buf.dbc));
+				cont = E_to_decisions;
+			}
+			else if (sa == E_data_in_block_mismatch) {
+				dbc = read_mismatch_recover(c, n, dsa);
+				tbc = A_BSIZE - dbc;
+				/* recover current state from registers */
+				dmablks = n->scratcha[2];
+				dmaaddr = legetl(n->scratchb);
+				/* we have got to dmaaddr + tbc */
+				/* we have dmablks * A_BSIZE - tbc + residue left to do */
+				/* so remaining transfer is */
+				IPRINT("in_block_mismatch: dmaaddr = 0x%lux tbc=%lud dmablks=%d\n",
+				    dmaaddr, tbc, dmablks);
+				calcblockdma(dsa, dmaaddr + tbc,
+				    dmablks * A_BSIZE - tbc + legetl(dsa->data_buf.dbc));
+				/* copy changes into scratch registers */
+				IPRINT("recalc: dmablks %d dmaaddr 0x%lx pa 0x%lx dbc %ld\n",
+				    dsa->dmablks, legetl(dsa->dmaaddr),
+				    legetl(dsa->data_buf.pa), legetl(dsa->data_buf.dbc));
+				n->scratcha[2] = dsa->dmablks;
+				lesetl(n->scratchb, dsa->dmancr);
+				cont = E_data_block_mismatch_recover;
+			}
+			else if (sa == E_data_out_mismatch) {
+				dbc = write_mismatch_recover(n, dsa);
+				tbc = legetl(dsa->data_buf.dbc) - dbc;
+				advancedata(&dsa->data_buf, tbc);
+				if (DEBUG(1) || DEBUG(2))
+					IPRINT("sd53c8xx: %d/%d: transferred = %ld residue = %ld\n",
+					    dsa->target, dsa->lun, tbc, legetl(dsa->data_buf.dbc));
+				cont = E_to_decisions;
+			}
+			else if (sa == E_data_out_block_mismatch) {
+				dbc = write_mismatch_recover(n, dsa);
+				tbc = legetl(dsa->data_buf.dbc) - dbc;
+				/* recover current state from registers */
+				dmablks = n->scratcha[2];
+				dmaaddr = legetl(n->scratchb);
+				/* we have got to dmaaddr + tbc */
+				/* we have dmablks blocks - tbc + residue left to do */
+				/* so remaining transfer is */
+				IPRINT("out_block_mismatch: dmaaddr = %lux tbc=%lud dmablks=%d\n",
+				    dmaaddr, tbc, dmablks);
+				calcblockdma(dsa, dmaaddr + tbc,
+				    dmablks * A_BSIZE - tbc + legetl(dsa->data_buf.dbc));
+				/* copy changes into scratch registers */
+				n->scratcha[2] = dsa->dmablks;
+				lesetl(n->scratchb, dsa->dmancr);
+				cont = E_data_block_mismatch_recover;
+			}
+			else if (sa == E_id_out_mismatch) {
+				/*
+				 * target switched phases while attention held during
+				 * message out. The possibilities are:
+				 * 1. It didn't like the last message. This is indicated
+				 *    by the new phase being message_in. Use script to recover
+				 *
+				 * 2. It's not SCSI-II compliant. The new phase will be other
+				 *    than message_in. We should also indicate that the device
+				 *    is asynchronous, if it's the SDTR that got ignored
+				 * 
+				 * For now, if the phase switch is not to message_in, and
+				 * and it happens after IDENTIFY and before SDTR, we
+				 * notify the negotiation state machine.
+				 */
+				ulong lim = legetl(dsa->msg_out_buf.dbc);
+				uchar p = n->sstat1 & 7;
+				dbc = write_mismatch_recover(n, dsa);
+				tbc = lim - dbc;
+				IPRINT("sd53c8xx: %d/%d: msg_out_mismatch: %lud/%lud sent, phase %s\n",
+				    dsa->target, dsa->lun, tbc, lim, phase[p]);
+				if (p != MessageIn && tbc == 1) {
+					msgsm(dsa, c, A_SIR_EV_PHASE_SWITCH_AFTER_ID, &cont, &wakeme);
+				}
+				else
+					cont = E_id_out_mismatch_recover;
+			}
+			else if (sa == E_cmd_out_mismatch) {
+				/*
+				 * probably the command count is longer than the device wants ...
+				 */
+				ulong lim = legetl(dsa->cmd_buf.dbc);
+				uchar p = n->sstat1 & 7;
+				dbc = write_mismatch_recover(n, dsa);
+				tbc = lim - dbc;
+				IPRINT("sd53c8xx: %d/%d: cmd_out_mismatch: %lud/%lud sent, phase %s\n",
+				    dsa->target, dsa->lun, tbc, lim, phase[p]);
+				USED(p, tbc);
+				cont = E_to_decisions;
+			}
+			else {
+				IPRINT("sd53c8xx: %d/%d: ma sa=%.8lux wanted=%s got=%s\n",
+				    dsa->target, dsa->lun, sa,
+				    phase[n->dcmd & 7],
+				    phase[n->sstat1 & 7]);
+				dumpncrregs(c, 1);
+				dsa->p9status = SDeio;	/* chf */
+				wakeme = 1;
+			}
+		}
+		/*else*/ if (sist & 0x400) {
+			if (DEBUG(0))
+				IPRINT("sd53c8xx: %d/%d Sto\n", dsa->target, dsa->lun);
+			dsa->p9status = SDtimeout;
+			dsa->stateb = A_STATE_DONE;
+			softreset(c);
+			cont = E_issue_check;
+			wakeme = 1;
+		}
+		if (sist & 0x1) {
+			IPRINT("sd53c8xx: %d/%d: parity error\n", dsa->target, dsa->lun);
+			dsa->parityerror = 1;
+		}
+		if (sist & 0x4) {
+			IPRINT("sd53c8xx: %d/%d: unexpected disconnect\n",
+			    dsa->target, dsa->lun);
+			dumpncrregs(c, 1);
+			//wakeme = 1;
+			dsa->p9status = SDeio;
+		}
+	}
+	if (istat & Dip) {
+		if (DEBUG(1))
+			IPRINT("dstat = %.2x\n", dstat);
+		/*else*/ if (dstat & Ssi) {
+			ulong *p = DMASEG_TO_KADDR(legetl(n->dsp));
+			ulong w = (uchar *)p - (uchar *)c->script;
+			IPRINT("[%lux]", w);
+			USED(w);
+			cont = -2;	/* restart */
+		}
+		if (dstat & Sir) {
+			switch (legetl(n->dsps)) {
+			case A_SIR_MSG_IO_COMPLETE:
+				dsa->p9status = dsa->status;
+				wakeme = 1;
+				break;
+			case A_SIR_MSG_SDTR:
+			case A_SIR_MSG_WDTR:
+			case A_SIR_MSG_REJECT:
+			case A_SIR_EV_RESPONSE_OK:
+				msgsm(dsa, c, legetl(n->dsps), &cont, &wakeme);
+				break;
+			case A_SIR_MSG_IGNORE_WIDE_RESIDUE:
+				/* back up one in the data transfer */
+				IPRINT("sd53c8xx: %d/%d: ignore wide residue %d, WSR = %d\n",
+				    dsa->target, dsa->lun, n->scratcha[1], n->scntl2 & 1);
+				if (dsa->dmablks == 0 && dsa->flag)
+					IPRINT("sd53c8xx: %d/%d: transfer over; residue ignored\n",
+					    dsa->target, dsa->lun);
+				else
+					calcblockdma(dsa, legetl(dsa->dmaaddr) - 1,
+					    dsa->dmablks * A_BSIZE + legetl(dsa->data_buf.dbc) + 1);
+				cont = -2;
+				break;
+			case A_SIR_ERROR_NOT_MSG_IN_AFTER_RESELECT:
+				IPRINT("sd53c8xx: %d: not msg_in after reselect (%s)",
+				    n->ssid & 7, phase[n->sstat1 & 7]);
+				dsa = dsafind(c, n->ssid & 7, -1, A_STATE_DISCONNECTED);
+				dumpncrregs(c, 1);
+				wakeme = 1;
+				break;
+			case A_SIR_NOTIFY_MSG_IN:
+				IPRINT("sd53c8xx: %d/%d: msg_in %d\n",
+				    dsa->target, dsa->lun, n->sfbr);
+				cont = -2;
+				break;
+			case A_SIR_NOTIFY_DISC:
+				IPRINT("sd53c8xx: %d/%d: disconnect:", dsa->target, dsa->lun);
+				goto dsadump;
+			case A_SIR_NOTIFY_STATUS:
+				IPRINT("sd53c8xx: %d/%d: status\n", dsa->target, dsa->lun);
+				cont = -2;
+				break;
+			case A_SIR_NOTIFY_COMMAND:
+				IPRINT("sd53c8xx: %d/%d: commands\n", dsa->target, dsa->lun);
+				cont = -2;
+				break;
+			case A_SIR_NOTIFY_DATA_IN:
+				IPRINT("sd53c8xx: %d/%d: data in a %lx b %lx\n",
+				    dsa->target, dsa->lun, legetl(n->scratcha), legetl(n->scratchb));
+				cont = -2;
+				break;
+			case A_SIR_NOTIFY_BLOCK_DATA_IN:
+				IPRINT("sd53c8xx: %d/%d: block data in: a2 %x b %lx\n",
+				    dsa->target, dsa->lun, n->scratcha[2], legetl(n->scratchb));
+				cont = -2;
+				break;
+			case A_SIR_NOTIFY_DATA_OUT:
+				IPRINT("sd53c8xx: %d/%d: data out\n", dsa->target, dsa->lun);
+				cont = -2;
+				break;
+			case A_SIR_NOTIFY_DUMP:
+				IPRINT("sd53c8xx: %d/%d: dump\n", dsa->target, dsa->lun);
+				dumpncrregs(c, 1);
+				cont = -2;
+				break;
+			case A_SIR_NOTIFY_DUMP2:
+				IPRINT("sd53c8xx: %d/%d: dump2:", dsa->target, dsa->lun);
+				IPRINT(" sa %lux", legetl(n->dsp) - c->scriptpa);
+				IPRINT(" dsa %lux", legetl(n->dsa));
+				IPRINT(" sfbr %ux", n->sfbr);
+				IPRINT(" a %lux", n->scratcha);
+				IPRINT(" b %lux", legetl(n->scratchb));
+				IPRINT(" ssid %ux", n->ssid);
+				IPRINT("\n");
+				cont = -2;
+				break;
+			case A_SIR_NOTIFY_WAIT_RESELECT:
+				IPRINT("sd53c8xx: wait reselect\n");
+				cont = -2;
+				break;
+			case A_SIR_NOTIFY_RESELECT:
+				IPRINT("sd53c8xx: reselect: ssid %.2x sfbr %.2x at %ld\n",
+				    n->ssid, n->sfbr, TK2MS(m->ticks));
+				cont = -2;
+				break;
+			case A_SIR_NOTIFY_ISSUE:
+				IPRINT("sd53c8xx: %d/%d: issue:", dsa->target, dsa->lun);
+			dsadump:
+				IPRINT(" tgt=%d", dsa->target);
+				IPRINT(" time=%ld", TK2MS(m->ticks));
+				IPRINT("\n");
+				cont = -2;
+				break;
+			case A_SIR_NOTIFY_ISSUE_CHECK:
+				IPRINT("sd53c8xx: issue check\n");
+				cont = -2;
+				break;
+			case A_SIR_NOTIFY_SIGP:
+				IPRINT("sd53c8xx: responded to SIGP\n");
+				cont = -2;
+				break;
+			case A_SIR_NOTIFY_DUMP_NEXT_CODE: {
+				ulong *dsp = DMASEG_TO_KADDR(legetl(n->dsp));
+				int x;
+				IPRINT("sd53c8xx: code at %lux", dsp - c->script);
+				for (x = 0; x < 6; x++)
+					IPRINT(" %.8lux", dsp[x]);
+				IPRINT("\n");
+				USED(dsp);
+				cont = -2;
+				break;
+			}
+			case A_SIR_NOTIFY_WSR:
+				IPRINT("sd53c8xx: %d/%d: WSR set\n", dsa->target, dsa->lun);
+				cont = -2;
+				break;
+			case A_SIR_NOTIFY_LOAD_SYNC:
+				IPRINT("sd53c8xx: %d/%d: scntl=%.2x sxfer=%.2x\n",
+				    dsa->target, dsa->lun, n->scntl3, n->sxfer);
+				cont = -2;
+				break;
+			case A_SIR_NOTIFY_RESELECTED_ON_SELECT:
+				IPRINT("sd53c8xx: %d/%d: reselected during select\n",
+				    dsa->target, dsa->lun);
+				cont = -2;
+				break;
+			default:
+				IPRINT("sd53c8xx: %d/%d: script error %ld\n",
+					dsa->target, dsa->lun, legetl(n->dsps));
+				dumpncrregs(c, 1);
+				wakeme = 1;
+			}
+		}
+		/*else*/ if (dstat & Iid) {
+			ulong addr = legetl(n->dsp);
+			ulong dbc = (n->dbc[2]<<16)|(n->dbc[1]<<8)|n->dbc[0];
+			IPRINT("sd53c8xx: %d/%d: Iid pa=%.8lux sa=%.8lux dbc=%lux\n",
+			    dsa->target, dsa->lun,
+			    addr, addr - c->scriptpa, dbc);
+			addr = (ulong)DMASEG_TO_KADDR(addr);
+			IPRINT("%.8lux %.8lux %.8lux\n",
+			    *(ulong *)(addr - 12), *(ulong *)(addr - 8), *(ulong *)(addr - 4));
+			USED(addr, dbc);
+			dsa->p9status = SDeio;
+			wakeme = 1;
+		}
+		/*else*/ if (dstat & Bf) {
+			IPRINT("sd53c8xx: %d/%d: Bus Fault\n", dsa->target, dsa->lun);
+			dumpncrregs(c, 1);
+			dsa->p9status = SDeio;
+			wakeme = 1;
+		}
+	}
+	if (cont == -2)
+		ncrcontinue(c);
+	else if (cont >= 0)
+		start(c, cont);
+	if (wakeme){
+		if(dsa->p9status == SDnostatus)
+			dsa->p9status = SDeio;
+		wakeup(dsa);
+	}
+	iunlock(c);
+	if (DEBUG(1)) {
+		IPRINT("sd53c8xx: int end 1\n");
+	}
+}
+
+static int
+done(void *arg)
+{
+	return ((Dsa *)arg)->p9status != SDnostatus;
+}
+
+static void
+setmovedata(Movedata *d, ulong pa, ulong bc)
+{
+	d->pa[0] = pa;
+	d->pa[1] = pa>>8;
+	d->pa[2] = pa>>16;
+	d->pa[3] = pa>>24;
+	d->dbc[0] = bc;
+	d->dbc[1] = bc>>8;
+	d->dbc[2] = bc>>16;
+	d->dbc[3] = bc>>24;
+}
+
+static void
+advancedata(Movedata *d, long v)
+{
+	lesetl(d->pa, legetl(d->pa) + v);
+	lesetl(d->dbc, legetl(d->dbc) - v);
+}
+
+static void
+dumpwritedata(uchar *data, int datalen)
+{
+	int i;
+	uchar *bp;
+	if (!DEBUG(0)){
+		USED(data, datalen);
+		return;
+	}
+
+	if (datalen) {
+		KPRINT("sd53c8xx:write:");
+		for (i = 0, bp = data; i < 50 && i < datalen; i++, bp++)
+			KPRINT("%.2ux", *bp);
+		if (i < datalen) {
+			KPRINT("...");
+		}
+		KPRINT("\n");
+	}
+}
+
+static void
+dumpreaddata(uchar *data, int datalen)
+{
+	int i;
+	uchar *bp;
+	if (!DEBUG(0)){
+		USED(data, datalen);
+		return;
+	}
+
+	if (datalen) {
+		KPRINT("sd53c8xx:read:");
+		for (i = 0, bp = data; i < 50 && i < datalen; i++, bp++)
+			KPRINT("%.2ux", *bp);
+		if (i < datalen) {
+			KPRINT("...");
+		}
+		KPRINT("\n");
+	}
+}
+
+static void
+busreset(Controller *c)
+{
+	int x, ntarget;
+
+	/* bus reset */
+	c->n->scntl1 |= (1 << 3);
+	delay(500);
+	c->n->scntl1 &= ~(1 << 3);
+	if(!(c->v->feature & Wide))
+		ntarget = 8;
+	else
+		ntarget = MAXTARGET;
+	for (x = 0; x < ntarget; x++) {
+		setwide(0, c, x, 0);
+#ifndef ASYNC_ONLY
+		c->s[x] = NeitherDone;
+#endif
+	}
+	c->capvalid = 0;
+}
+
+static void
+reset(Controller *c)
+{
+	/* should wakeup all pending tasks */
+	softreset(c);
+	busreset(c);
+}
+
+static int
+symrio(SDreq* r)
+{
+	Dsa *d;
+	uchar *bp;
+	Controller *c;
+	uchar target_expo, my_expo;
+	int bc, check, status, target;
+
+	if((target = r->unit->subno) == 0x07)
+		return r->status = SDtimeout;	/* assign */
+	c = r->unit->dev->ctlr;
+
+	check = 0;
+	d = dsaalloc(c, target, r->lun);
+
+	qlock(&c->q[target]);			/* obtain access to target */
+docheck:
+	/* load the transfer control stuff */
+	d->scsi_id_buf[0] = 0;
+	d->scsi_id_buf[1] = c->sxfer[target];
+	d->scsi_id_buf[2] = target;
+	d->scsi_id_buf[3] = c->scntl3[target];
+	synctodsa(d, c);
+
+	bc = 0;
+
+	d->msg_out[bc] = 0x80 | r->lun;
+
+#ifndef NO_DISCONNECT
+	d->msg_out[bc] |= (1 << 6);
+#endif
+	bc++;
+
+	/* work out what to do about negotiation */
+	switch (c->s[target]) {
+	default:
+		KPRINT("sd53c8xx: %d: strange nego state %d\n", target, c->s[target]);
+		c->s[target] = NeitherDone;
+		/* fall through */
+	case NeitherDone:
+		if ((c->capvalid & (1 << target)) == 0)
+			break;
+		target_expo = (c->cap[target] >> 5) & 3;
+		my_expo = (c->v->feature & Wide) != 0;
+		if (target_expo < my_expo)
+			my_expo = target_expo;
+#ifdef ALWAYS_DO_WDTR
+		bc += buildwdtrmsg(d->msg_out + bc, my_expo);
+		KPRINT("sd53c8xx: %d: WDTN: initiating expo %d\n", target, my_expo);
+		c->s[target] = WideInit;
+		break;
+#else
+		if (my_expo) {
+			bc += buildwdtrmsg(d->msg_out + bc, (c->v->feature & Wide) ? 1 : 0);
+			KPRINT("sd53c8xx: %d: WDTN: initiating expo %d\n", target, my_expo);
+			c->s[target] = WideInit;
+			break;
+		}
+		KPRINT("sd53c8xx: %d: WDTN: narrow\n", target);
+		/* fall through */
+#endif
+	case WideDone:
+		if (c->cap[target] & (1 << 4)) {
+			KPRINT("sd53c8xx: %d: SDTN: initiating %d %d\n", target, c->tpf, c->v->maxsyncoff);
+			bc += buildsdtrmsg(d->msg_out + bc, c->tpf, c->v->maxsyncoff);
+			c->s[target] = SyncInit;
+			break;
+		}
+		KPRINT("sd53c8xx: %d: SDTN: async only\n", target);
+		c->s[target] = BothDone;
+		break;
+
+	case BothDone:
+		break;
+	}
+
+	setmovedata(&d->msg_out_buf, DMASEG(d->msg_out), bc);
+	setmovedata(&d->cmd_buf, DMASEG(r->cmd), r->clen);
+	calcblockdma(d, DMASEG(r->data), r->dlen);
+
+	if (DEBUG(0)) {
+		KPRINT("sd53c8xx: %d/%d: exec: ", target, r->lun);
+		for (bp = r->cmd; bp < &r->cmd[r->clen]; bp++)
+			KPRINT("%.2ux", *bp);
+		KPRINT("\n");
+		if (!r->write)
+			KPRINT("sd53c8xx: %d/%d: exec: limit=(%d)%ld\n",
+			  target, r->lun, d->dmablks, legetl(d->data_buf.dbc));
+		else
+			dumpwritedata(r->data, r->dlen);
+	}
+
+	setmovedata(&d->status_buf, DMASEG(&d->status), 1);	
+
+	d->p9status = SDnostatus;
+	d->parityerror = 0;
+
+	d->stateb = A_STATE_ISSUE;		/* start operation */
+
+	ilock(c);
+	if (c->ssm)
+		c->n->dcntl |= 0x10;		/* SSI */
+	if (c->running) {
+		c->n->istat |= Sigp;
+	}
+	else {
+		start(c, E_issue_check);
+	}
+	iunlock(c);
+
+	while(waserror())
+		;
+	tsleep(d, done, d, 30 * 1000);
+	poperror();
+
+	if (!done(d)) {
+		KPRINT("sd53c8xx: %d/%d: exec: Timed out\n", target, r->lun);
+		dumpncrregs(c, 0);
+		dsafree(c, d);
+		reset(c);
+		qunlock(&c->q[target]);
+		r->status = SDtimeout;
+		return r->status = SDtimeout;	/* assign */
+	}
+
+	if((status = d->p9status) == SDeio)
+		c->s[target] = NeitherDone;
+	if (d->parityerror) {
+		status = SDeio;
+	}
+
+	/*
+	 * adjust datalen
+	 */
+	r->rlen = r->dlen;
+	if (d->dmablks > 0)
+		r->rlen -= d->dmablks * A_BSIZE;
+	else if (d->flag == 0)
+		r->rlen -= legetl(d->data_buf.dbc);
+	if(!r->write)
+		dumpreaddata(r->data, r->rlen);
+	if (DEBUG(0))
+		KPRINT("53c8xx: %d/%d: exec: p9status=%d status %d rlen %ld\n",
+		    target, r->lun, d->p9status, status, r->rlen);
+	/*
+	 * spot the identify
+	 */
+	if ((c->capvalid & (1 << target)) == 0
+	 && (status == SDok || status == SDcheck)
+	 && r->cmd[0] == 0x12 && r->dlen >= 8) {
+		c->capvalid |= 1 << target;
+		bp = r->data;
+		c->cap[target] = bp[7];
+		KPRINT("sd53c8xx: %d: capabilities %.2x\n", target, bp[7]);
+	}
+	if(!check && status == SDcheck && !(r->flags & SDnosense)){
+		check = 1;
+		r->write = 0;
+		memset(r->cmd, 0, sizeof(r->cmd));
+		r->cmd[0] = 0x03;
+		r->cmd[1] = r->lun<<5;
+		r->cmd[4] = sizeof(r->sense)-1;
+		r->clen = 6;
+		r->data = r->sense;
+		r->dlen = sizeof(r->sense)-1;
+		/*
+		 * Clear out the microcode state
+		 * so the Dsa can be re-used.
+		 */
+		lesetl(d->state, A_STATE_ALLOCATED);
+		goto docheck;
+	}
+	qunlock(&c->q[target]);
+	dsafree(c, d);
+
+	if(status == SDok && check){
+		status = SDcheck;
+		r->flags |= SDvalidsense;
+	}
+	KPRINT("sd53c8xx: %d: r flags %8.8uX status %d rlen %ld\n",
+		target, r->flags, status, r->rlen);
+	return r->status = status;
+}
+
+static void
+cribbios(Controller *c)
+{
+	c->bios.scntl3 = c->n->scntl3;
+	c->bios.stest2 = c->n->stest2;
+	print("sd53c8xx: bios scntl3(%.2x) stest2(%.2x)\n", c->bios.scntl3, c->bios.stest2);
+}
+
+static int
+bios_set_differential(Controller *c)
+{
+	/* Concept lifted from FreeBSD - thanks Gerard */
+	/* basically, if clock conversion factors are set, then there is
+ 	 * evidence the bios had a go at the chip, and if so, it would
+	 * have set the differential enable bit in stest2
+	 */
+	return (c->bios.scntl3 & 7) != 0 && (c->bios.stest2 & 0x20) != 0;
+}
+
+#define NCR_VID 	0x1000
+#define NCR_810_DID 	0x0001
+#define NCR_820_DID	0x0002	/* don't know enough about this one to support it */
+#define NCR_825_DID	0x0003
+#define NCR_815_DID	0x0004
+#define SYM_810AP_DID	0x0005
+#define SYM_860_DID	0x0006
+#define SYM_896_DID	0x000b
+#define SYM_895_DID	0x000c
+#define SYM_885_DID	0x000d	/* ditto */
+#define SYM_875_DID	0x000f	/* ditto */
+#define SYM_1010_DID	0x0020
+#define SYM_1011_DID	0x0021
+#define SYM_875J_DID	0x008f
+
+static Variant variant[] = {
+{ NCR_810_DID,   0x0f, "NCR53C810",	Burst16,   8, 24, 0 },
+{ NCR_810_DID,   0x1f, "SYM53C810ALV",	Burst16,   8, 24, Prefetch },
+{ NCR_810_DID,   0xff, "SYM53C810A",	Burst16,   8, 24, Prefetch },
+{ SYM_810AP_DID, 0xff, "SYM53C810AP",	Burst16,   8, 24, Prefetch },
+{ NCR_815_DID,   0xff, "NCR53C815",	Burst16,   8, 24, BurstOpCodeFetch },
+{ NCR_825_DID,   0x0f, "NCR53C825",	Burst16,   8, 24, Wide|BurstOpCodeFetch|Differential },
+{ NCR_825_DID,   0xff, "SYM53C825A",	Burst128, 16, 24, Prefetch|LocalRAM|BigFifo|Differential|Wide },
+{ SYM_860_DID,   0x0f, "SYM53C860",	Burst16,   8, 24, Prefetch|Ultra },
+{ SYM_860_DID,   0xff, "SYM53C860LV",	Burst16,   8, 24, Prefetch|Ultra },
+{ SYM_875_DID,   0x01, "SYM53C875r1",	Burst128, 16, 24, Prefetch|LocalRAM|BigFifo|Differential|Wide|Ultra },
+{ SYM_875_DID,   0xff, "SYM53C875",	Burst128, 16, 24, Prefetch|LocalRAM|BigFifo|Differential|Wide|Ultra|ClockDouble },
+{ SYM_875J_DID,   0xff, "SYM53C875j",	Burst128, 16, 24, Prefetch|LocalRAM|BigFifo|Differential|Wide|Ultra|ClockDouble },
+{ SYM_885_DID,   0xff, "SYM53C885",	Burst128, 16, 24, Prefetch|LocalRAM|BigFifo|Wide|Ultra|ClockDouble },
+{ SYM_895_DID,   0xff, "SYM53C895",	Burst128, 16, 24, Prefetch|LocalRAM|BigFifo|Wide|Ultra|Ultra2 },
+{ SYM_896_DID,   0xff, "SYM53C896",	Burst128, 16, 64, Prefetch|LocalRAM|BigFifo|Wide|Ultra|Ultra2 },
+{ SYM_1010_DID,   0xff, "SYM53C1010",	Burst128, 16, 64, Prefetch|LocalRAM|BigFifo|Wide|Ultra|Ultra2 },
+{ SYM_1011_DID,   0xff, "SYM53C1010",	Burst128, 16, 64, Prefetch|LocalRAM|BigFifo|Wide|Ultra|Ultra2 },
+};
+
+
+static int
+xfunc(Controller *c, enum na_external x, unsigned long *v)
+{
+	switch (x) {
+	default:
+		print("xfunc: can't find external %d\n", x);
+		return 0;
+	case X_scsi_id_buf:
+		*v = offsetof(Dsa, scsi_id_buf[0]);
+		break;
+	case X_msg_out_buf:
+		*v = offsetof(Dsa, msg_out_buf);
+		break;
+	case X_cmd_buf:
+		*v = offsetof(Dsa, cmd_buf);
+		break;
+	case X_data_buf:
+		*v = offsetof(Dsa, data_buf);
+		break;
+	case X_status_buf:
+		*v = offsetof(Dsa, status_buf);
+		break;
+	case X_dsa_head:
+		*v = DMASEG(&c->dsalist.head[0]);
+		break;
+	}
+	return 1;
+}
+
+static int
+na_fixup(Controller *c, ulong pa_reg,
+    struct na_patch *patch, int patches,
+    int (*externval)(Controller*, int, ulong*))
+{
+	int p;
+	int v;
+	ulong *script, pa_script;
+	unsigned long lw, lv;
+
+	script = c->script;
+	pa_script = c->scriptpa;
+	for (p = 0; p < patches; p++) {
+		switch (patch[p].type) {
+		case 1:
+			/* script relative */
+			script[patch[p].lwoff] += pa_script;
+			break;
+		case 2:
+			/* register i/o relative */
+			script[patch[p].lwoff] += pa_reg;
+			break;
+		case 3:
+			/* data external */
+			lw = script[patch[p].lwoff];
+			v = (lw >> 8) & 0xff;
+			if (!(*externval)(c, v, &lv))
+				return 0;
+			v = lv & 0xff;
+			script[patch[p].lwoff] = (lw & 0xffff00ffL) | (v << 8);
+			break;
+		case 4:
+			/* 32 bit external */
+			lw = script[patch[p].lwoff];
+			if (!(*externval)(c, lw, &lv))
+				return 0;
+			script[patch[p].lwoff] = lv;
+			break;
+		case 5:
+			/* 24 bit external */
+			lw = script[patch[p].lwoff];
+			if (!(*externval)(c, lw & 0xffffff, &lv))
+				return 0;
+			script[patch[p].lwoff] = (lw & 0xff000000L) | (lv & 0xffffffL);
+			break;
+		}
+	}
+	return 1;
+}
+
+static SDev*
+sympnp(void)
+{
+	int ba;
+	Pcidev *p;
+	Variant *v;
+	void *scriptma;
+	Controller *ctlr;
+	SDev *sdev, *head, *tail;
+	ulong regpa, *script, scriptpa;
+
+	p = nil;
+	head = tail = nil;
+	while(p = pcimatch(p, NCR_VID, 0)){
+		for(v = variant; v < &variant[nelem(variant)]; v++){
+			if(p->did == v->did && p->rid <= v->maxrid)
+				break;
+		}
+		if(v >= &variant[nelem(variant)])
+			continue;
+		print("sd53c8xx: %s rev. 0x%2.2x intr=%d command=%4.4uX\n",
+			v->name, p->rid, p->intl, p->pcr);
+
+		regpa = p->mem[1].bar;
+		ba = 2;
+		if(regpa & 0x04){
+			if(p->mem[2].bar)
+				continue;
+			ba++;
+		}
+		regpa = upamalloc(regpa & ~0x0F, p->mem[1].size, 0);
+		if(regpa == 0)
+			continue;
+
+		script = nil;
+		scriptpa = 0;
+		scriptma = nil;
+		if((v->feature & LocalRAM) && sizeof(na_script) <= 4096){
+			scriptpa = p->mem[ba].bar;
+			if((scriptpa & 0x04) && p->mem[ba+1].bar){
+				upafree(regpa, p->mem[1].size);
+				continue;
+			}
+			scriptpa = upamalloc(scriptpa & ~0x0F,
+					p->mem[ba].size, 0);
+			if(scriptpa)
+				script = KADDR(scriptpa);
+		}
+		if(scriptpa == 0){
+			/*
+			 * Either the map failed, or this chip does not have
+			 * local RAM. It will need a copy of the microcode.
+			 */
+			scriptma = malloc(sizeof(na_script));
+			if(scriptma == nil){
+				upafree(regpa, p->mem[1].size);
+				continue;
+			}
+			scriptpa = DMASEG(scriptma);
+			script = scriptma;
+		}
+
+		ctlr = malloc(sizeof(Controller));
+		sdev = malloc(sizeof(SDev));
+		if(ctlr == nil || sdev == nil){
+buggery:
+			if(ctlr)
+				free(ctlr);
+			if(sdev)
+				free(sdev);
+			if(scriptma)
+				free(scriptma);
+			else
+				upafree(scriptpa, p->mem[ba].size);
+			upafree(regpa, p->mem[1].size);
+			continue;
+		}
+
+		ctlr->n = KADDR(regpa);
+		ctlr->v = v;
+		ctlr->script = script;
+		memmove(ctlr->script, na_script, sizeof(na_script));
+		ctlr->scriptpa = scriptpa;
+		if(!na_fixup(ctlr, regpa, na_patches, NA_PATCHES, xfunc)){
+			print("script fixup failed\n");
+			goto buggery;
+		}
+		swabl(ctlr->script, ctlr->script, sizeof(na_script));
+
+		ctlr->dsalist.freechain = 0;
+		lesetl(ctlr->dsalist.head, 0);
+
+		ctlr->pcidev = p;
+
+		sdev->ifc = &sd53c8xxifc;
+		sdev->ctlr = ctlr;
+		if(!(v->feature & Wide))
+			sdev->nunit = 8;
+		else
+			sdev->nunit = MAXTARGET;
+		ctlr->sdev = sdev;
+
+		if(head != nil)
+			tail->next = sdev;
+		else
+			head = sdev;
+		tail = sdev;
+	}
+
+	return head;
+}
+
+static SDev*
+symid(SDev* sdev)
+{
+	return scsiid(sdev, &sd53c8xxifc);
+}
+
+static int
+symenable(SDev* sdev)
+{
+	Pcidev *pcidev;
+	Controller *ctlr;
+	//char name[NAMELEN];
+
+	ctlr = sdev->ctlr;
+	pcidev = ctlr->pcidev;
+
+	pcisetbme(pcidev);
+	//snprint(name, NAMELEN, "%s (%s)", sdev->name, sdev->ifc->name);
+	intrenable(pcidev->intl, interrupt, ctlr, pcidev->tbdf, name);
+
+	ilock(ctlr);
+	synctabinit(ctlr);
+	cribbios(ctlr);
+	reset(ctlr);
+	iunlock(ctlr);
+
+	return 1;
+}
+
+static int
+symdisable(SDev* sdev)
+{
+	Ncr *n;
+	Controller *ctlr;
+
+	ctlr = sdev->ctlr;
+	n = ctlr->n;
+
+	n->istat = Srst;		/* software reset */
+	microdelay(1);
+	n->istat = 0;
+
+	n->scntl1 |= (1 << 3);		/* bus reset */
+	delay(500);
+	n->scntl1 &= ~(1 << 3);
+
+	return 1;
+}
+
+SDifc sd53c8xxifc = {
+	"53c8xx",			/* name */
+
+	sympnp,				/* pnp */
+	nil,				/* legacy */
+	symid,				/* id */
+	symenable,			/* enable */
+	symdisable,			/* disable */
+
+	scsiverify,			/* verify */
+	scsionline,			/* online */
+	symrio,				/* rio */
+	nil,				/* rctl */
+	nil,				/* wctl */
+
+	scsibio,			/* bio */
+};
--- /dev/null
+++ b/os/boot/pc/sd53c8xx.i
@@ -1,0 +1,769 @@
+unsigned long na_script[] = {
+			/*	extern	scsi_id_buf */
+			/*	extern	msg_out_buf */
+			/*	extern	cmd_buf */
+			/*	extern	data_buf */
+			/*	extern	status_buf */
+			/*	extern	msgin_buf */
+			/*	extern	dsa_0 */
+			/*	extern  dsa_1 */
+			/*	extern	dsa_head */
+			/*	SIR_MSG_IO_COMPLETE = 0 */
+			/*	error_not_cmd_complete = 1 */
+			/*	error_disconnected = 2 */
+			/*	error_reselected = 3 */
+			/*	error_unexpected_phase = 4 */
+			/*	error_weird_message = 5 */
+			/*	SIR_ERROR_NOT_MSG_IN_AFTER_RESELECT = 6 */
+			/*	error_not_identify_after_reselect = 7 */
+			/*	error_too_much_data = 8 */
+			/*	error_too_little_data = 9 */
+			/*	SIR_MSG_REJECT = 10 */
+			/*	SIR_MSG_SDTR = 11 */
+			/*	SIR_EV_RESPONSE_OK = 12 */
+			/*	error_sigp_set = 13 */
+			/*	SIR_EV_PHASE_SWITCH_AFTER_ID = 14 */
+			/*	SIR_MSG_WDTR = 15 */
+			/*	SIR_MSG_IGNORE_WIDE_RESIDUE = 16 */
+			/*	SIR_NOTIFY_DISC = 100 */
+			/*	SIR_NOTIFY_RESELECT = 101 */
+			/*	SIR_NOTIFY_MSG_IN = 102 */
+			/*	SIR_NOTIFY_STATUS = 103 */
+			/*	SIR_NOTIFY_DUMP = 104 */
+			/*	SIR_NOTIFY_DUMP2 = 105 */
+			/*	SIR_NOTIFY_SIGP = 106 */
+			/*	SIR_NOTIFY_ISSUE = 107 */
+			/*	SIR_NOTIFY_WAIT_RESELECT = 108 */
+			/*	SIR_NOTIFY_ISSUE_CHECK = 109 */
+			/*	SIR_NOTIFY_DUMP_NEXT_CODE = 110 */
+			/*	SIR_NOTIFY_COMMAND = 111 */
+			/*	SIR_NOTIFY_DATA_IN = 112 */
+			/*	SIR_NOTIFY_DATA_OUT = 113 */
+			/*	SIR_NOTIFY_BLOCK_DATA_IN = 114 */
+			/*	SIR_NOTIFY_WSR = 115 */
+			/*	SIR_NOTIFY_LOAD_SYNC = 116 */
+			/*	SIR_NOTIFY_RESELECTED_ON_SELECT = 117 */
+			/*	STATE_FREE = 0 */
+			/*	STATE_ALLOCATED = 1 */
+			/*	STATE_ISSUE = 2 */
+			/*	STATE_DISCONNECTED = 3 */
+			/*	STATE_DONE = 4 */
+			/*	RESULT_OK = 0 */
+			/*	MSG_IDENTIFY = 0x80 */
+			/*	MSG_DISCONNECT = 0x04 */
+			/*	MSG_SAVE_DATA_POINTER = 0x02 */
+			/*	MSG_RESTORE_POINTERS = 0x03 */
+			/*	MSG_IGNORE_WIDE_RESIDUE = 0x23 */
+			/*	X_MSG = 0x01 */
+			/*	X_MSG_SDTR = 0x01 */
+			/*	X_MSG_WDTR = 0x03 */
+			/*	MSG_REJECT = 0x07 */
+			/*	BSIZE = 512 */
+/* 0000 */ 0x80880000L, /*		jump	wait_for_reselection */
+/* 0004 */ 0x00000514L,
+/* 0008 */ 0x88880000L, /*		call	load_sync */
+/* 000c */ 0x0000074cL,
+/* 0010 */ 0x60000200L, /*		clear	target */
+/* 0014 */ 0x00000000L,
+/* 0018 */ 0x47000000L, /*		select	atn from scsi_id_buf, reselected_on_select */
+/* 001c */ 0x000004ecL,
+/* 0020 */ 0x878b0000L, /*		jump	start1, when msg_in */
+/* 0024 */ 0x00000000L,
+/* 0028 */ 0x1e000000L, /*		move	from msg_out_buf, when msg_out */
+/* 002c */ 0x00000001L,
+/* 0030 */ 0x868b0000L, /*		jump	start1, when msg_out */
+/* 0034 */ 0x00fffff0L,
+/* 0038 */ 0x82830000L, /*		jump	to_decisions, when not cmd */
+/* 003c */ 0x000005f0L,
+/* 0040 */ 0x60000008L, /*		clear	atn */
+/* 0044 */ 0x00000000L,
+/* 0048 */ 0x1a000000L, /*		move	from cmd_buf, when cmd */
+/* 004c */ 0x00000002L,
+/* 0050 */ 0x81830000L, /*		jump	to_decisions, when not data_in */
+/* 0054 */ 0x000005d8L,
+/* 0058 */ 0xc0000004L, /*		move	memory 4, state, scratcha */
+/* 005c */ 0x00000678L,
+/* 0060 */ 0x00000034L,
+/* 0064 */ 0xc0000004L, /*		move	memory 4, dmaaddr, scratchb */
+/* 0068 */ 0x0000067cL,
+/* 006c */ 0x0000005cL,
+/* 0070 */ 0x72360000L, /*		move	scratcha2 to sfbr */
+/* 0074 */ 0x00000000L,
+/* 0078 */ 0x808c0000L, /*		jump	data_in_normal, if 0 */
+/* 007c */ 0x00000078L,
+/* 0080 */ 0x29000200L, /*		move	BSIZE, ptr dmaaddr, when data_in */
+/* 0084 */ 0x0000067cL,
+/* 0088 */ 0x7e5d0200L, /*		move	scratchb1 + BSIZE / 256 to scratchb1 */
+/* 008c */ 0x00000000L,
+/* 0090 */ 0x7f5e0000L, /*		move	scratchb2 + 0 to scratchb2 with carry */
+/* 0094 */ 0x00000000L,
+/* 0098 */ 0x7f5f0000L, /*		move	scratchb3 + 0 to scratchb3 with carry */
+/* 009c */ 0x00000000L,
+/* 00a0 */ 0x7e36ff00L, /*		move	scratcha2 + 255 to scratcha2 */
+/* 00a4 */ 0x00000000L,
+/* 00a8 */ 0xc0000004L, /*		move	memory 4, scratchb, dmaaddr */
+/* 00ac */ 0x0000005cL,
+/* 00b0 */ 0x0000067cL,
+/* 00b4 */ 0x818b0000L, /*		jump	data_in_block_loop, when data_in */
+/* 00b8 */ 0x00ffffb4L,
+/* 00bc */ 0xc0000004L, /*		move	memory 4, scratcha, state */
+/* 00c0 */ 0x00000034L,
+/* 00c4 */ 0x00000678L,
+/* 00c8 */ 0x88880000L, /*		call	save_state */
+/* 00cc */ 0x000005e0L,
+/* 00d0 */ 0x80880000L, /*		jump	to_decisions */
+/* 00d4 */ 0x00000558L,
+/* 00d8 */ 0xc0000004L, /*		move	memory 4, scratchb, dmaaddr */
+/* 00dc */ 0x0000005cL,
+/* 00e0 */ 0x0000067cL,
+/* 00e4 */ 0xc0000004L, /*		move	memory 4, scratcha, state */
+/* 00e8 */ 0x00000034L,
+/* 00ec */ 0x00000678L,
+/* 00f0 */ 0x80880000L, /*		jump	to_decisions */
+/* 00f4 */ 0x00000538L,
+/* 00f8 */ 0x72370000L, /*		move	scratcha3 to sfbr */
+/* 00fc */ 0x00000000L,
+/* 0100 */ 0x98040000L, /*		int	error_too_much_data, if not 0 */
+/* 0104 */ 0x00000008L,
+/* 0108 */ 0x19000000L, /*		move	from data_buf, when data_in */
+/* 010c */ 0x00000003L,
+/* 0110 */ 0x78370100L, /*		move	1 to scratcha3 */
+/* 0114 */ 0x00000000L,
+/* 0118 */ 0xc0000004L, /*		move	memory 4, scratcha, state */
+/* 011c */ 0x00000034L,
+/* 0120 */ 0x00000678L,
+/* 0124 */ 0x88880000L, /*		call	save_state */
+/* 0128 */ 0x00000584L,
+/* 012c */ 0x80880000L, /*		jump	post_data_to_decisions */
+/* 0130 */ 0x0000052cL,
+/* 0134 */ 0xc0000004L, /*		move	memory 4, state, scratcha */
+/* 0138 */ 0x00000678L,
+/* 013c */ 0x00000034L,
+/* 0140 */ 0xc0000004L, /*		move	memory 4, dmaaddr, scratchb */
+/* 0144 */ 0x0000067cL,
+/* 0148 */ 0x0000005cL,
+/* 014c */ 0x72360000L, /*		move	scratcha2 to sfbr */
+/* 0150 */ 0x00000000L,
+/* 0154 */ 0x808c0000L, /*		jump	data_out_normal, if 0 */
+/* 0158 */ 0x0000005cL,
+/* 015c */ 0xc0000004L, /*		move	memory 4, dmaaddr, scratchb */
+/* 0160 */ 0x0000067cL,
+/* 0164 */ 0x0000005cL,
+/* 0168 */ 0x28000200L, /*		move	BSIZE, ptr dmaaddr, when data_out */
+/* 016c */ 0x0000067cL,
+/* 0170 */ 0x7e5d0200L, /*		move	scratchb1 + BSIZE / 256 to scratchb1 */
+/* 0174 */ 0x00000000L,
+/* 0178 */ 0x7f5e0000L, /*		move	scratchb2 + 0 to scratchb2 with carry */
+/* 017c */ 0x00000000L,
+/* 0180 */ 0x7f5f0000L, /*		move	scratchb3 + 0 to scratchb3 with carry */
+/* 0184 */ 0x00000000L,
+/* 0188 */ 0x7e36ff00L, /*		move	scratcha2 + 255 to scratcha2 */
+/* 018c */ 0x00000000L,
+/* 0190 */ 0xc0000004L, /*		move	memory 4, scratchb, dmaaddr */
+/* 0194 */ 0x0000005cL,
+/* 0198 */ 0x0000067cL,
+/* 019c */ 0x808b0000L, /*		jump	data_out_block_loop, when data_out */
+/* 01a0 */ 0x00ffffa8L,
+/* 01a4 */ 0xc0000004L, /*		move	memory 4, scratcha, state */
+/* 01a8 */ 0x00000034L,
+/* 01ac */ 0x00000678L,
+/* 01b0 */ 0x80880000L, /*		jump	to_decisions */
+/* 01b4 */ 0x00000478L,
+/* 01b8 */ 0x72370000L, /*		move	scratcha3 to sfbr */
+/* 01bc */ 0x00000000L,
+/* 01c0 */ 0x98040000L, /*		int	error_too_little_data, if not 0 */
+/* 01c4 */ 0x00000009L,
+/* 01c8 */ 0x18000000L, /*		move	from data_buf, when data_out */
+/* 01cc */ 0x00000003L,
+/* 01d0 */ 0x78370100L, /*		move	1 to scratcha3 */
+/* 01d4 */ 0x00000000L,
+/* 01d8 */ 0xc0000004L, /*		move	memory 4, scratcha, state */
+/* 01dc */ 0x00000034L,
+/* 01e0 */ 0x00000678L,
+/* 01e4 */ 0x88880000L, /*		call	save_state */
+/* 01e8 */ 0x000004c4L,
+/* 01ec */ 0x80880000L, /*		jump	post_data_to_decisions */
+/* 01f0 */ 0x0000046cL,
+/* 01f4 */ 0x1b000000L, /*		move	from status_buf, when status */
+/* 01f8 */ 0x00000004L,
+/* 01fc */ 0x9f030000L, /*		int	error_unexpected_phase, when not msg_in */
+/* 0200 */ 0x00000004L,
+/* 0204 */ 0x0f000001L, /*		move	1, scratcha, when msg_in */
+/* 0208 */ 0x00000034L,
+/* 020c */ 0x808c0007L, /*		jump	rejected, if MSG_REJECT */
+/* 0210 */ 0x00000088L,
+/* 0214 */ 0x808c0004L, /*		jump	disconnected, if MSG_DISCONNECT */
+/* 0218 */ 0x00000298L,
+/* 021c */ 0x808c0002L, /*		jump	msg_in_skip, if MSG_SAVE_DATA_POINTER */
+/* 0220 */ 0x00000090L,
+/* 0224 */ 0x808c0003L, /*		jump	msg_in_skip, if MSG_RESTORE_POINTERS */
+/* 0228 */ 0x00000088L,
+/* 022c */ 0x808c0023L, /*		jump	ignore_wide, if MSG_IGNORE_WIDE_RESIDUE */
+/* 0230 */ 0x000001f0L,
+/* 0234 */ 0x808c0001L, /*		jump	extended, if X_MSG */
+/* 0238 */ 0x00000088L,
+/* 023c */ 0x98040000L, /*		int	error_not_cmd_complete, if not 0 */
+/* 0240 */ 0x00000001L,
+/* 0244 */ 0x7c027e00L, /*		move	scntl2&0x7e to scntl2 */
+/* 0248 */ 0x00000000L,
+/* 024c */ 0x60000040L, /*		clear	ack */
+/* 0250 */ 0x00000000L,
+/* 0254 */ 0x48000000L, /*		wait	disconnect */
+/* 0258 */ 0x00000000L,
+/* 025c */ 0xc0000004L, /*		move	memory 4, state, scratcha */
+/* 0260 */ 0x00000678L,
+/* 0264 */ 0x00000034L,
+/* 0268 */ 0x78340400L, /*		move	STATE_DONE to scratcha0 */
+/* 026c */ 0x00000000L,
+/* 0270 */ 0x78350000L, /*		move	RESULT_OK to scratcha1 */
+/* 0274 */ 0x00000000L,
+/* 0278 */ 0xc0000004L, /*		move	memory 4, scratcha, state */
+/* 027c */ 0x00000034L,
+/* 0280 */ 0x00000678L,
+/* 0284 */ 0x88880000L, /*		call	save_state */
+/* 0288 */ 0x00000424L,
+/* 028c */ 0x98180000L, /*		intfly	0 */
+/* 0290 */ 0x00000000L,
+/* 0294 */ 0x80880000L, /*		jump	issue_check */
+/* 0298 */ 0x0000043cL,
+/* 029c */ 0x98080000L, /*		int	SIR_MSG_REJECT */
+/* 02a0 */ 0x0000000aL,
+/* 02a4 */ 0x60000040L, /*		clear	ack */
+/* 02a8 */ 0x00000000L,
+/* 02ac */ 0x80880000L, /*		jump	to_decisions */
+/* 02b0 */ 0x0000037cL,
+/* 02b4 */ 0x60000040L, /*		clear	ack */
+/* 02b8 */ 0x00000000L,
+/* 02bc */ 0x80880000L, /*		jump	to_decisions */
+/* 02c0 */ 0x0000036cL,
+/* 02c4 */ 0x60000040L, /*		clear	ack */
+/* 02c8 */ 0x00000000L,
+/* 02cc */ 0x9f030000L, /*		int	error_unexpected_phase, when not msg_in */
+/* 02d0 */ 0x00000004L,
+/* 02d4 */ 0x0f000001L, /*		move	1, scratcha1, when msg_in */
+/* 02d8 */ 0x00000035L,
+/* 02dc */ 0x808c0003L, /*		jump	ext_3, if 3 */
+/* 02e0 */ 0x00000030L,
+/* 02e4 */ 0x808c0002L, /*		jump	ext_2, if 2 */
+/* 02e8 */ 0x00000098L,
+/* 02ec */ 0x98040001L, /*		int	error_weird_message, if not 1 */
+/* 02f0 */ 0x00000005L,
+/* 02f4 */ 0x60000040L, /*		clear	ack */
+/* 02f8 */ 0x00000000L,
+/* 02fc */ 0x9f030000L, /*		int	error_unexpected_phase, when not msg_in */
+/* 0300 */ 0x00000004L,
+/* 0304 */ 0x0f000001L, /*		move	1, scratcha1, when msg_in */
+/* 0308 */ 0x00000035L,
+/* 030c */ 0x80880000L, /*		jump	ext_done */
+/* 0310 */ 0x000000c8L,
+/* 0314 */ 0x60000040L, /*	ext_3:	clear	ack */
+/* 0318 */ 0x00000000L,
+/* 031c */ 0x9f030000L, /*		int	error_unexpected_phase, when not msg_in */
+/* 0320 */ 0x00000004L,
+/* 0324 */ 0x0f000001L, /*		move	1, scratcha1, when msg_in */
+/* 0328 */ 0x00000035L,
+/* 032c */ 0x60000040L, /*		clear	ack */
+/* 0330 */ 0x00000000L,
+/* 0334 */ 0x9f030000L, /*		int	error_unexpected_phase, when not msg_in */
+/* 0338 */ 0x00000004L,
+/* 033c */ 0x0f000001L, /*		move	1, scratcha2, when msg_in */
+/* 0340 */ 0x00000036L,
+/* 0344 */ 0x60000040L, /*		clear	ack */
+/* 0348 */ 0x00000000L,
+/* 034c */ 0x9f030000L, /*		int	error_unexpected_phase, when not msg_in */
+/* 0350 */ 0x00000004L,
+/* 0354 */ 0x0f000001L, /*		move	1, scratcha3, when msg_in */
+/* 0358 */ 0x00000037L,
+/* 035c */ 0x72350000L, /*		move	scratcha1 to sfbr */
+/* 0360 */ 0x00000000L,
+/* 0364 */ 0x80840001L, /*		jump	ext_done, if not X_MSG_SDTR */
+/* 0368 */ 0x00000070L,
+/* 036c */ 0x98080000L, /*	sdtr:	int	SIR_MSG_SDTR */
+/* 0370 */ 0x0000000bL,
+/* 0374 */ 0x60000040L, /*		clear	ack */
+/* 0378 */ 0x00000000L,
+/* 037c */ 0x80880000L, /*		jump	to_decisions */
+/* 0380 */ 0x000002acL,
+/* 0384 */ 0x60000040L, /*	ext_2:	clear	ack */
+/* 0388 */ 0x00000000L,
+/* 038c */ 0x9f030000L, /*		int	error_unexpected_phase, when not msg_in */
+/* 0390 */ 0x00000004L,
+/* 0394 */ 0x0f000001L, /*		move	1, scratcha1, when msg_in */
+/* 0398 */ 0x00000035L,
+/* 039c */ 0x60000040L, /*		clear	ack */
+/* 03a0 */ 0x00000000L,
+/* 03a4 */ 0x9f030000L, /*		int	error_unexpected_phase, when not msg_in */
+/* 03a8 */ 0x00000004L,
+/* 03ac */ 0x0f000001L, /*		move	1, scratcha2, when msg_in */
+/* 03b0 */ 0x00000036L,
+/* 03b4 */ 0x72350000L, /*		move	scratcha1 to sfbr */
+/* 03b8 */ 0x00000000L,
+/* 03bc */ 0x80840003L, /*		jump	ext_done, if not X_MSG_WDTR */
+/* 03c0 */ 0x00000018L,
+/* 03c4 */ 0x98080000L, /*	wdtr:	int	SIR_MSG_WDTR */
+/* 03c8 */ 0x0000000fL,
+/* 03cc */ 0x60000040L, /*		clear	ack */
+/* 03d0 */ 0x00000000L,
+/* 03d4 */ 0x80880000L, /*		jump	to_decisions */
+/* 03d8 */ 0x00000254L,
+/* 03dc */ 0x58000008L, /*		set	atn */
+/* 03e0 */ 0x00000000L,
+/* 03e4 */ 0x60000040L, /*		clear	ack */
+/* 03e8 */ 0x00000000L,
+/* 03ec */ 0x78340700L, /*		move	MSG_REJECT to scratcha */
+/* 03f0 */ 0x00000000L,
+/* 03f4 */ 0x9e030000L, /*		int	error_unexpected_phase, when not msg_out */
+/* 03f8 */ 0x00000004L,
+/* 03fc */ 0x60000008L, /*		clear	atn */
+/* 0400 */ 0x00000000L,
+/* 0404 */ 0x0e000001L, /*		move	1, scratcha, when msg_out */
+/* 0408 */ 0x00000034L,
+/* 040c */ 0x60000040L, /*		clear	ack */
+/* 0410 */ 0x00000000L,
+/* 0414 */ 0x868b0000L, /*		jump	reject, when msg_out */
+/* 0418 */ 0x00ffffc0L,
+/* 041c */ 0x80880000L, /*		jump	to_decisions */
+/* 0420 */ 0x0000020cL,
+/* 0424 */ 0x60000040L, /*		clear	ack */
+/* 0428 */ 0x00000000L,
+/* 042c */ 0x9f030000L, /*		int	error_unexpected_phase, when not msg_in */
+/* 0430 */ 0x00000004L,
+/* 0434 */ 0x0f000001L, /*		move	1, scratcha1, when msg_in */
+/* 0438 */ 0x00000035L,
+/* 043c */ 0x98080000L, /*		int	SIR_MSG_IGNORE_WIDE_RESIDUE */
+/* 0440 */ 0x00000010L,
+/* 0444 */ 0x60000040L, /*		clear	ack */
+/* 0448 */ 0x00000000L,
+/* 044c */ 0x80880000L, /*		jump	to_decisions */
+/* 0450 */ 0x000001dcL,
+/* 0454 */ 0x58000008L, /*		set	atn */
+/* 0458 */ 0x00000000L,
+/* 045c */ 0x60000040L, /*		clear	ack */
+/* 0460 */ 0x00000000L,
+/* 0464 */ 0x9e030000L, /*		int	error_unexpected_phase, when not msg_out */
+/* 0468 */ 0x00000004L,
+/* 046c */ 0x1e000000L, /*		move	from msg_out_buf, when msg_out */
+/* 0470 */ 0x00000001L,
+/* 0474 */ 0x868b0000L, /*		jump	response_repeat, when msg_out */
+/* 0478 */ 0x00fffff0L,
+/* 047c */ 0x878b0000L, /*		jump	response_msg_in, when msg_in */
+/* 0480 */ 0x00000010L,
+/* 0484 */ 0x98080000L, /*		int	SIR_EV_RESPONSE_OK */
+/* 0488 */ 0x0000000cL,
+/* 048c */ 0x80880000L, /*		jump	to_decisions */
+/* 0490 */ 0x0000019cL,
+/* 0494 */ 0x0f000001L, /*		move	1, scratcha, when msg_in */
+/* 0498 */ 0x00000034L,
+/* 049c */ 0x808c0007L, /*		jump	rejected, if MSG_REJECT */
+/* 04a0 */ 0x00fffdf8L,
+/* 04a4 */ 0x98080000L, /*		int	SIR_EV_RESPONSE_OK */
+/* 04a8 */ 0x0000000cL,
+/* 04ac */ 0x80880000L, /*		jump	msg_in_not_reject */
+/* 04b0 */ 0x00fffd60L,
+/* 04b4 */ 0x7c027e00L, /*		move	scntl2&0x7e to scntl2 */
+/* 04b8 */ 0x00000000L,
+/* 04bc */ 0x60000040L, /*		clear 	ack */
+/* 04c0 */ 0x00000000L,
+/* 04c4 */ 0x48000000L, /*		wait	disconnect */
+/* 04c8 */ 0x00000000L,
+/* 04cc */ 0xc0000004L, /*		move	memory 4, state, scratcha */
+/* 04d0 */ 0x00000678L,
+/* 04d4 */ 0x00000034L,
+/* 04d8 */ 0x78340300L, /*		move	STATE_DISCONNECTED to scratcha0 */
+/* 04dc */ 0x00000000L,
+/* 04e0 */ 0xc0000004L, /*		move	memory 4, scratcha, state */
+/* 04e4 */ 0x00000034L,
+/* 04e8 */ 0x00000678L,
+/* 04ec */ 0x88880000L, /*		call	save_state */
+/* 04f0 */ 0x000001bcL,
+/* 04f4 */ 0x74020100L, /*		move	scntl2&0x01 to sfbr */
+/* 04f8 */ 0x00000000L,
+/* 04fc */ 0x98040000L, /*		int	SIR_NOTIFY_WSR, if not 0 */
+/* 0500 */ 0x00000073L,
+/* 0504 */ 0x80880000L, /*		jump	issue_check */
+/* 0508 */ 0x000001ccL,
+/* 050c */ 0x98080000L, /*		int	SIR_NOTIFY_RESELECTED_ON_SELECT */
+/* 0510 */ 0x00000075L,
+/* 0514 */ 0x80880000L, /*		jump	reselected */
+/* 0518 */ 0x00000008L,
+/* 051c */ 0x54000000L, /*		wait reselect sigp_set */
+/* 0520 */ 0x000001acL,
+/* 0524 */ 0x60000200L, /*		clear	target */
+/* 0528 */ 0x00000000L,
+/* 052c */ 0x9f030000L, /*		int	SIR_ERROR_NOT_MSG_IN_AFTER_RESELECT, when not msg_in */
+/* 0530 */ 0x00000006L,
+/* 0534 */ 0x0f000001L, /*		move	1, scratchb, when msg_in */
+/* 0538 */ 0x0000005cL,
+/* 053c */ 0x98041f80L, /*		int	error_not_identify_after_reselect, if not MSG_IDENTIFY and mask 0x1f */
+/* 0540 */ 0x00000007L,
+/* 0544 */ 0xc0000004L, /*	 	move	memory 4, dsa_head, dsa */
+/* 0548 */ 0x00000008L,
+/* 054c */ 0x00000010L,
+/* 0550 */ 0x72100000L, /*		move	dsa0 to sfbr */
+/* 0554 */ 0x00000000L,
+/* 0558 */ 0x80840000L, /*		jump	find_dsa_1, if not 0 */
+/* 055c */ 0x00000030L,
+/* 0560 */ 0x72110000L, /*		move	dsa1 to sfbr */
+/* 0564 */ 0x00000000L,
+/* 0568 */ 0x80840000L, /*		jump	find_dsa_1, if not 0 */
+/* 056c */ 0x00000020L,
+/* 0570 */ 0x72120000L, /*		move	dsa2 to sfbr */
+/* 0574 */ 0x00000000L,
+/* 0578 */ 0x80840000L, /*		jump	find_dsa_1, if not 0 */
+/* 057c */ 0x00000010L,
+/* 0580 */ 0x72130000L, /*		move	dsa3 to sfbr */
+/* 0584 */ 0x00000000L,
+/* 0588 */ 0x980c0000L, /*		int	error_reselected, if 0 */
+/* 058c */ 0x00000003L,
+/* 0590 */ 0x88880000L, /*		call	load_state */
+/* 0594 */ 0x000000f8L,
+/* 0598 */ 0xc0000004L, /*		move	memory 4, state, scratcha */
+/* 059c */ 0x00000678L,
+/* 05a0 */ 0x00000034L,
+/* 05a4 */ 0x72340000L, /*		move	scratcha0 to sfbr */
+/* 05a8 */ 0x00000000L,
+/* 05ac */ 0x80840003L, /*		jump	find_dsa_next, if not STATE_DISCONNECTED */
+/* 05b0 */ 0x00000038L,
+/* 05b4 */ 0x740a0f00L, /*		move	ssid & 15 to sfbr */
+/* 05b8 */ 0x00000000L,
+/* 05bc */ 0xc0000001L, /*		move	memory 1, targ, find_dsa_smc1 */
+/* 05c0 */ 0x00000680L,
+/* 05c4 */ 0x000005c8L,
+/* 05c8 */ 0x808400ffL, /*		jump	find_dsa_next, if not 255 */
+/* 05cc */ 0x0000001cL,
+/* 05d0 */ 0xc0000001L, /*		move	memory 1, lun, find_dsa_smc2 */
+/* 05d4 */ 0x00000684L,
+/* 05d8 */ 0x000005e4L,
+/* 05dc */ 0x725c0000L, /*		move	scratchb0 to sfbr */
+/* 05e0 */ 0x00000000L,
+/* 05e4 */ 0x808cf8ffL, /*		jump	reload_sync, if 255 and mask ~7 */
+/* 05e8 */ 0x00000034L,
+/* 05ec */ 0xc0000004L, /*		move	memory 4, next, dsa */
+/* 05f0 */ 0x0000068cL,
+/* 05f4 */ 0x00000010L,
+/* 05f8 */ 0x80880000L, /*		jump	find_dsa_loop */
+/* 05fc */ 0x00ffff50L,
+/* 0600 */ 0x60000008L, /*		clear	atn */
+/* 0604 */ 0x00000000L,
+/* 0608 */ 0x878b0000L, /*	        jump    msg_in_phase, when msg_in */
+/* 060c */ 0x00fffbf4L,
+/* 0610 */ 0x98080000L, /*	        int     SIR_MSG_REJECT */
+/* 0614 */ 0x0000000aL,
+/* 0618 */ 0x80880000L, /*	        jump    to_decisions */
+/* 061c */ 0x00000010L,
+/* 0620 */ 0x88880000L, /*		call	load_sync */
+/* 0624 */ 0x00000134L,
+/* 0628 */ 0x60000040L, /*		clear	ack */
+/* 062c */ 0x00000000L,
+/* 0630 */ 0x818b0000L, /*		jump	data_in_phase, when data_in */
+/* 0634 */ 0x00fffa20L,
+/* 0638 */ 0x828a0000L, /*		jump	cmd_phase, if cmd */
+/* 063c */ 0x00fffa00L,
+/* 0640 */ 0x808a0000L, /*		jump	data_out_phase, if data_out */
+/* 0644 */ 0x00fffaecL,
+/* 0648 */ 0x838a0000L, /*		jump	status_phase, if status */
+/* 064c */ 0x00fffba4L,
+/* 0650 */ 0x878a0000L, /*		jump	msg_in_phase, if msg_in */
+/* 0654 */ 0x00fffbacL,
+/* 0658 */ 0x98080000L, /*		int	error_unexpected_phase */
+/* 065c */ 0x00000004L,
+/* 0660 */ 0x838b0000L, /*		jump	status_phase, when status */
+/* 0664 */ 0x00fffb8cL,
+/* 0668 */ 0x878a0000L, /*		jump	msg_in_phase, if msg_in */
+/* 066c */ 0x00fffb94L,
+/* 0670 */ 0x98080000L, /*		int	error_unexpected_phase */
+/* 0674 */ 0x00000004L,
+/* 0678 */ 0x00000000L, /*	state:	defw	0 */
+/* 067c */ 0x00000000L, /*	dmaaddr: defw	0 */
+/* 0680 */ 0x00000000L, /*	targ:	defw	0 */
+/* 0684 */ 0x00000000L, /*	lun:	defw	0 */
+/* 0688 */ 0x00000000L, /*	sync:	defw	0 */
+/* 068c */ 0x00000000L, /*	next:	defw	0 */
+			/*	dsa_load_len = dsa_load_end - dsa_copy */
+			/*	dsa_save_len = dsa_save_end - dsa_copy */
+/* 0690 */ 0xc0000004L, /*		move	memory 4, dsa, load_state_smc0 + 4 */
+/* 0694 */ 0x00000010L,
+/* 0698 */ 0x000006a0L,
+/* 069c */ 0xc0000018L, /*		move	memory dsa_load_len, 0, dsa_copy */
+/* 06a0 */ 0x00000000L,
+/* 06a4 */ 0x00000678L,
+/* 06a8 */ 0x90080000L, /*		return */
+/* 06ac */ 0x00000000L,
+/* 06b0 */ 0xc0000004L, /*		move	memory 4, dsa, save_state_smc0 + 8 */
+/* 06b4 */ 0x00000010L,
+/* 06b8 */ 0x000006c4L,
+/* 06bc */ 0xc0000008L, /*		move	memory dsa_save_len, dsa_copy, 0 */
+/* 06c0 */ 0x00000678L,
+/* 06c4 */ 0x00000000L,
+/* 06c8 */ 0x90080000L, /*		return */
+/* 06cc */ 0x00000000L,
+/* 06d0 */ 0x721a0000L, /*		move	ctest2 to sfbr */
+/* 06d4 */ 0x00000000L,
+/* 06d8 */ 0xc0000004L, /*		move	memory 4, dsa_head, dsa */
+/* 06dc */ 0x00000008L,
+/* 06e0 */ 0x00000010L,
+/* 06e4 */ 0x72100000L, /*		move	dsa0 to sfbr */
+/* 06e8 */ 0x00000000L,
+/* 06ec */ 0x80840000L, /*		jump	issue_check_1, if not 0 */
+/* 06f0 */ 0x00000030L,
+/* 06f4 */ 0x72110000L, /*		move	dsa1 to sfbr */
+/* 06f8 */ 0x00000000L,
+/* 06fc */ 0x80840000L, /*		jump	issue_check_1, if not 0 */
+/* 0700 */ 0x00000020L,
+/* 0704 */ 0x72120000L, /*		move	dsa2 to sfbr */
+/* 0708 */ 0x00000000L,
+/* 070c */ 0x80840000L, /*		jump	issue_check_1, if not 0 */
+/* 0710 */ 0x00000010L,
+/* 0714 */ 0x72130000L, /*		move	dsa3 to sfbr */
+/* 0718 */ 0x00000000L,
+/* 071c */ 0x808c0000L, /*		jump	wait_for_reselection, if 0 */
+/* 0720 */ 0x00fffdf8L,
+/* 0724 */ 0x88880000L, /*	 	call	load_state */
+/* 0728 */ 0x00ffff64L,
+/* 072c */ 0xc0000004L, /*		move	memory 4, state, scratcha */
+/* 0730 */ 0x00000678L,
+/* 0734 */ 0x00000034L,
+/* 0738 */ 0x72340000L, /*		move	scratcha0 to sfbr */
+/* 073c */ 0x00000000L,
+/* 0740 */ 0x808c0002L, /*		jump	start, if STATE_ISSUE */
+/* 0744 */ 0x00fff8c0L,
+/* 0748 */ 0xc0000004L, /*		move	memory 4, next, dsa */
+/* 074c */ 0x0000068cL,
+/* 0750 */ 0x00000010L,
+/* 0754 */ 0x80880000L, /*		jump	issue_check_loop */
+/* 0758 */ 0x00ffff88L,
+/* 075c */ 0xc0000004L, /*		move	memory 4, sync, scratcha */
+/* 0760 */ 0x00000688L,
+/* 0764 */ 0x00000034L,
+/* 0768 */ 0x72340000L, /*		move	scratcha0 to sfbr */
+/* 076c */ 0x00000000L,
+/* 0770 */ 0x6a030000L, /*		move	sfbr to scntl3 */
+/* 0774 */ 0x00000000L,
+/* 0778 */ 0x72350000L, /*		move	scratcha1 to sfbr */
+/* 077c */ 0x00000000L,
+/* 0780 */ 0x6a050000L, /*		move	sfbr to sxfer */
+/* 0784 */ 0x00000000L,
+/* 0788 */ 0x90080000L, /*		return */
+/* 078c */ 0x00000000L,
+};
+
+#define NA_SCRIPT_SIZE 484
+
+struct na_patch na_patches[] = {
+	{ 0x0006, 5 }, /* 00000018 */
+	{ 0x000b, 4 }, /* 0000002c */
+	{ 0x0013, 4 }, /* 0000004c */
+	{ 0x0017, 1 }, /* 0000005c */
+	{ 0x0018, 2 }, /* 00000060 */
+	{ 0x001a, 1 }, /* 00000068 */
+	{ 0x001b, 2 }, /* 0000006c */
+	{ 0x0021, 1 }, /* 00000084 */
+	{ 0x002b, 2 }, /* 000000ac */
+	{ 0x002c, 1 }, /* 000000b0 */
+	{ 0x0030, 2 }, /* 000000c0 */
+	{ 0x0031, 1 }, /* 000000c4 */
+	{ 0x0037, 2 }, /* 000000dc */
+	{ 0x0038, 1 }, /* 000000e0 */
+	{ 0x003a, 2 }, /* 000000e8 */
+	{ 0x003b, 1 }, /* 000000ec */
+	{ 0x0043, 4 }, /* 0000010c */
+	{ 0x0047, 2 }, /* 0000011c */
+	{ 0x0048, 1 }, /* 00000120 */
+	{ 0x004e, 1 }, /* 00000138 */
+	{ 0x004f, 2 }, /* 0000013c */
+	{ 0x0051, 1 }, /* 00000144 */
+	{ 0x0052, 2 }, /* 00000148 */
+	{ 0x0058, 1 }, /* 00000160 */
+	{ 0x0059, 2 }, /* 00000164 */
+	{ 0x005b, 1 }, /* 0000016c */
+	{ 0x0065, 2 }, /* 00000194 */
+	{ 0x0066, 1 }, /* 00000198 */
+	{ 0x006a, 2 }, /* 000001a8 */
+	{ 0x006b, 1 }, /* 000001ac */
+	{ 0x0073, 4 }, /* 000001cc */
+	{ 0x0077, 2 }, /* 000001dc */
+	{ 0x0078, 1 }, /* 000001e0 */
+	{ 0x007e, 4 }, /* 000001f8 */
+	{ 0x0082, 2 }, /* 00000208 */
+	{ 0x0098, 1 }, /* 00000260 */
+	{ 0x0099, 2 }, /* 00000264 */
+	{ 0x009f, 2 }, /* 0000027c */
+	{ 0x00a0, 1 }, /* 00000280 */
+	{ 0x00b6, 2 }, /* 000002d8 */
+	{ 0x00c2, 2 }, /* 00000308 */
+	{ 0x00ca, 2 }, /* 00000328 */
+	{ 0x00d0, 2 }, /* 00000340 */
+	{ 0x00d6, 2 }, /* 00000358 */
+	{ 0x00e6, 2 }, /* 00000398 */
+	{ 0x00ec, 2 }, /* 000003b0 */
+	{ 0x0102, 2 }, /* 00000408 */
+	{ 0x010e, 2 }, /* 00000438 */
+	{ 0x011c, 4 }, /* 00000470 */
+	{ 0x0126, 2 }, /* 00000498 */
+	{ 0x0134, 1 }, /* 000004d0 */
+	{ 0x0135, 2 }, /* 000004d4 */
+	{ 0x0139, 2 }, /* 000004e4 */
+	{ 0x013a, 1 }, /* 000004e8 */
+	{ 0x014e, 2 }, /* 00000538 */
+	{ 0x0152, 4 }, /* 00000548 */
+	{ 0x0153, 2 }, /* 0000054c */
+	{ 0x0167, 1 }, /* 0000059c */
+	{ 0x0168, 2 }, /* 000005a0 */
+	{ 0x0170, 1 }, /* 000005c0 */
+	{ 0x0171, 1 }, /* 000005c4 */
+	{ 0x0175, 1 }, /* 000005d4 */
+	{ 0x0176, 1 }, /* 000005d8 */
+	{ 0x017c, 1 }, /* 000005f0 */
+	{ 0x017d, 2 }, /* 000005f4 */
+	{ 0x01a5, 2 }, /* 00000694 */
+	{ 0x01a6, 1 }, /* 00000698 */
+	{ 0x01a9, 1 }, /* 000006a4 */
+	{ 0x01ad, 2 }, /* 000006b4 */
+	{ 0x01ae, 1 }, /* 000006b8 */
+	{ 0x01b0, 1 }, /* 000006c0 */
+	{ 0x01b7, 4 }, /* 000006dc */
+	{ 0x01b8, 2 }, /* 000006e0 */
+	{ 0x01cc, 1 }, /* 00000730 */
+	{ 0x01cd, 2 }, /* 00000734 */
+	{ 0x01d3, 1 }, /* 0000074c */
+	{ 0x01d4, 2 }, /* 00000750 */
+	{ 0x01d8, 1 }, /* 00000760 */
+	{ 0x01d9, 2 }, /* 00000764 */
+};
+#define NA_PATCHES 79
+
+enum na_external {
+	X_scsi_id_buf,
+	X_msg_out_buf,
+	X_cmd_buf,
+	X_data_buf,
+	X_status_buf,
+	X_msgin_buf,
+	X_dsa_0,
+	X_dsa_1,
+	X_dsa_head,
+};
+
+enum {
+	E_issue_check_next = 1864,
+	E_issue_check_1 = 1828,
+	E_issue_check_loop = 1764,
+	E_save_state_smc0 = 1724,
+	E_load_state_smc0 = 1692,
+	E_dsa_load_end = 1680,
+	E_sync = 1672,
+	E_dsa_save_end = 1664,
+	E_dsa_copy = 1656,
+	E_id_out_mismatch_recover = 1536,
+	E_next = 1676,
+	E_reload_sync = 1568,
+	E_find_dsa_smc2 = 1508,
+	E_lun = 1668,
+	E_find_dsa_smc1 = 1480,
+	E_targ = 1664,
+	E_find_dsa_next = 1516,
+	E_load_state = 1680,
+	E_find_dsa_1 = 1424,
+	E_find_dsa_loop = 1360,
+	E_find_dsa = 1348,
+	E_sigp_set = 1744,
+	E_reselected = 1316,
+	E_wsr_check = 1268,
+	E_response_msg_in = 1172,
+	E_response_repeat = 1132,
+	E_response = 1108,
+	E_reject = 988,
+	E_wdtr = 964,
+	E_sdtr = 876,
+	E_ext_done = 988,
+	E_ext_1 = 756,
+	E_ext_2 = 900,
+	E_ext_3 = 788,
+	E_issue_check = 1752,
+	E_extended = 708,
+	E_ignore_wide = 1060,
+	E_msg_in_skip = 692,
+	E_disconnected = 1204,
+	E_msg_in_not_reject = 532,
+	E_rejected = 668,
+	E_msg_in_phase = 516,
+	E_status_phase = 500,
+	E_data_out_mismatch = 464,
+	E_data_out_block_mismatch = 368,
+	E_data_out_normal = 440,
+	E_data_out_block_loop = 332,
+	E_data_out_phase = 308,
+	E_post_data_to_decisions = 1632,
+	E_data_in_mismatch = 272,
+	E_data_block_mismatch_recover = 216,
+	E_save_state = 1712,
+	E_data_in_block_mismatch = 136,
+	E_data_in_normal = 248,
+	E_data_in_block_loop = 112,
+	E_dmaaddr = 1660,
+	E_state = 1656,
+	E_data_in_phase = 88,
+	E_cmd_out_mismatch = 80,
+	E_cmd_phase = 64,
+	E_to_decisions = 1584,
+	E_id_out_mismatch = 48,
+	E_start1 = 40,
+	E_reselected_on_select = 1292,
+	E_load_sync = 1884,
+	E_start = 8,
+	E_wait_for_reselection = 1308,
+	E_idle = 0,
+};
+#define A_dsa_save_len 8
+#define A_dsa_load_len 24
+#define A_BSIZE 512
+#define A_MSG_REJECT 7
+#define A_X_MSG_WDTR 3
+#define A_X_MSG_SDTR 1
+#define A_X_MSG 1
+#define A_MSG_IGNORE_WIDE_RESIDUE 35
+#define A_MSG_RESTORE_POINTERS 3
+#define A_MSG_SAVE_DATA_POINTER 2
+#define A_MSG_DISCONNECT 4
+#define A_MSG_IDENTIFY 128
+#define A_RESULT_OK 0
+#define A_STATE_DONE 4
+#define A_STATE_DISCONNECTED 3
+#define A_STATE_ISSUE 2
+#define A_STATE_ALLOCATED 1
+#define A_STATE_FREE 0
+#define A_SIR_NOTIFY_RESELECTED_ON_SELECT 117
+#define A_SIR_NOTIFY_LOAD_SYNC 116
+#define A_SIR_NOTIFY_WSR 115
+#define A_SIR_NOTIFY_BLOCK_DATA_IN 114
+#define A_SIR_NOTIFY_DATA_OUT 113
+#define A_SIR_NOTIFY_DATA_IN 112
+#define A_SIR_NOTIFY_COMMAND 111
+#define A_SIR_NOTIFY_DUMP_NEXT_CODE 110
+#define A_SIR_NOTIFY_ISSUE_CHECK 109
+#define A_SIR_NOTIFY_WAIT_RESELECT 108
+#define A_SIR_NOTIFY_ISSUE 107
+#define A_SIR_NOTIFY_SIGP 106
+#define A_SIR_NOTIFY_DUMP2 105
+#define A_SIR_NOTIFY_DUMP 104
+#define A_SIR_NOTIFY_STATUS 103
+#define A_SIR_NOTIFY_MSG_IN 102
+#define A_SIR_NOTIFY_RESELECT 101
+#define A_SIR_NOTIFY_DISC 100
+#define A_SIR_MSG_IGNORE_WIDE_RESIDUE 16
+#define A_SIR_MSG_WDTR 15
+#define A_SIR_EV_PHASE_SWITCH_AFTER_ID 14
+#define A_error_sigp_set 13
+#define A_SIR_EV_RESPONSE_OK 12
+#define A_SIR_MSG_SDTR 11
+#define A_SIR_MSG_REJECT 10
+#define A_error_too_little_data 9
+#define A_error_too_much_data 8
+#define A_error_not_identify_after_reselect 7
+#define A_SIR_ERROR_NOT_MSG_IN_AFTER_RESELECT 6
+#define A_error_weird_message 5
+#define A_error_unexpected_phase 4
+#define A_error_reselected 3
+#define A_error_disconnected 2
+#define A_error_not_cmd_complete 1
+#define A_SIR_MSG_IO_COMPLETE 0
--- /dev/null
+++ b/os/boot/pc/sdaoe.c
@@ -1,0 +1,733 @@
+/*
+ * aoe sd bootstrap driver, copyright © 2007 coraid
+ */
+
+#include "u.h"
+#include "mem.h"
+#include "lib.h"
+#include "dat.h"
+#include "fns.h"
+#include "io.h"
+#include "sd.h"
+#include "aoe.h"
+
+
+enum {
+	Nctlr	= 4,
+};
+
+enum {
+	/* sync with ahci.h */
+	Dllba 	= 1<<0,
+	Dsmart	= 1<<1,
+	Dpower	= 1<<2,
+	Dnop	= 1<<3,
+	Datapi	= 1<<4,
+	Datapi16= 1<<5,
+};
+
+enum {
+	Tfree	= -1,
+	Tmgmt,
+};
+
+typedef struct Ctlr Ctlr;
+struct Ctlr{
+	Ctlr	*next;
+	SDunit	*unit;
+
+	int	ctlrno;
+	int	major;
+	int	minor;
+	uchar	ea[Eaddrlen];
+	ushort	lasttag;
+
+	ulong	vers;
+	uchar	mediachange;
+	uchar	flag;
+	uchar	smart;
+	uchar	smartrs;
+	uchar	feat;
+
+	uvlong	sectors;
+	char	serial[20+1];
+	char	firmware[8+1];
+	char	model[40+1];
+	char	ident[0x100];
+};
+
+static	Ctlr	*head;
+static	Ctlr	*tail;
+
+static	int	aoeether[10];
+
+SDifc sdaoeifc;
+
+static void
+hnputs(uchar *p, ushort i)
+{
+	p[0] = i >> 8;
+	p[1] = i;
+}
+
+static void
+hnputl(uchar *p, ulong i)
+{
+	p[0] = i >> 24;
+	p[1] = i >> 16;
+	p[2] = i >> 8;
+	p[3] = i;
+}
+
+static ushort
+nhgets(uchar *p)
+{
+	return *p<<8 | p[1];
+}
+
+static ulong
+nhgetl(uchar *p)
+{
+	return p[0]<<24 | p[1]<<16 | p[2]<<8 | p[3];
+}
+
+static int
+newtag(Ctlr *d)
+{
+	int t;
+
+	for(;;){
+		t = ++d->lasttag << 16;
+		t |= m->ticks & 0xffff;
+		switch(t) {
+		case Tfree:
+		case Tmgmt:
+			break;
+		default:
+			return t;
+		}
+	}
+}
+
+static int
+hset(Ctlr *d, Aoehdr *h, int cmd)
+{
+	int tag;
+
+	memmove(h->dst, d->ea, Eaddrlen);
+	hnputs(h->type, Aoetype);
+	h->verflag = Aoever << 4;
+	hnputs(h->major, d->major);
+	h->minor = d->minor;
+	h->cmd = cmd;
+	hnputl(h->tag, tag = newtag(d));
+	return tag;
+}
+
+static void
+idmove(char *p, ushort *a, int n)
+{
+	int i;
+	char *op, *e;
+
+	op = p;
+	for(i = 0; i < n / 2; i++){
+		*p++ = a[i] >> 8;
+		*p++ = a[i];
+	}
+	*p = 0;
+	while(p > op && *--p == ' ')
+		*p = 0;
+	e = p;
+	p = op;
+	while(*p == ' ')
+		p++;
+	memmove(op, p, n - (e - p));
+}
+
+static ushort
+gbit16(void *a)
+{
+	uchar *i;
+
+	i = a;
+	return i[1]<<8 | i[0];
+}
+
+static ulong
+gbit32(void *a)
+{
+	uchar *i;
+	ulong j;
+
+	i = a;
+	j  = i[3] << 24;
+	j |= i[2] << 16;
+	j |= i[1] << 8;
+	j |= i[0];
+	return j;
+}
+
+static uvlong
+gbit64(void *a)
+{
+	uchar *i;
+
+	i = a;
+	return (uvlong)gbit32(i+4) << 32 | gbit32(a);
+}
+
+static int
+ataidentify(Ctlr *c, ushort *id)
+{
+	vlong s;
+	int i;
+
+	i = gbit16(id+83) | gbit16(id+86);
+	if(i & (1 << 10)){
+		c->feat |= Dllba;
+		s = gbit64(id+100);
+	}else
+		s = gbit32(id+60);
+
+	idmove(c->serial, id+10, 20);
+	idmove(c->firmware, id+23, 8);
+	idmove(c->model, id+27, 40);
+
+print("aoe discovers %d.%d: %s %s\n", c->major, c->minor, c->model, c->serial);
+
+	c->sectors = s;
+	c->mediachange = 1;
+	return 0;
+}
+
+static void
+identifydump(Aoeata *a)
+{
+	print("%E %E type=%.4ux verflag=%x error=%x %d.%d cmd=%d tag=%.8lux\n",
+		a->dst, a->src, nhgets(a->type), a->verflag, a->error,
+		nhgets(a->major), a->minor, a->cmd, nhgetl(a->tag));
+	print("   aflag=%x errfeat=%ux scnt=%d cmdstat=%ux, lba=%d? res=%.4ux\n",
+		a->aflag, a->errfeat, a->scnt, a->cmdstat, 0, nhgets(a->res));
+}
+
+static int
+idpkt(Ctlr *c, Aoeata *a)
+{
+	memset(a, 0, sizeof *a);
+	a->cmdstat = Cid;
+	a->scnt = 1;
+	a->lba[3] = 0xa0;
+	return hset(c, a, ACata);
+}
+
+static int
+chktag(int *out, int nout, int tag)
+{
+	int j;
+
+	for(j = 0; j <= nout; j++)
+		if(out[j] == tag)
+			return 0;
+print("wrong tag\n");
+	for(j = 0; j <= nout; j++)
+		print("%.8ux != %.8ux\n", out[j], tag);
+	return -1;
+}
+
+/*
+ * ignore the tag for identify.  better than ignoring
+ * a response to the wrong identify request
+ */
+static int
+identify(Ctlr *c)
+{
+	int tag[5], i, n;
+	Aoeata *a;
+	Etherpkt p;
+
+	memset(&p, 0, sizeof p);
+	a = (Aoeata*)&p;
+	i = 0;
+	do {
+		if(i == 5){
+			print("aoe: identify timeout\n");
+			return -1;
+		}
+		tag[i] = idpkt(c, a);
+		ethertxpkt(c->ctlrno, &p, sizeof *a, 0);
+		memset(&p, 0, sizeof p);
+next:
+		n = etherrxpkt(c->ctlrno, &p, 125);
+		if(n == 0){
+			i++;
+			continue;
+		}
+		if(nhgets(a->type) != Aoetype)
+			goto next;
+		if(nhgets(a->major) != c->major || a->minor != c->minor){
+			print("wrong device %d.%d want %d.%d; %d\n",
+				nhgets(a->major), a->minor,
+				c->major, c->minor, n);
+			goto next;
+		}
+		if(chktag(tag, i, nhgetl(a->tag)) == -1)
+			goto next;
+		if(a->cmdstat & 0xa9){
+			print("aoe: ata error on identify: %2ux\n", a->cmdstat);
+			return -1;
+		}
+	} while (a->scnt != 1);
+
+	c->feat = 0;
+	ataidentify(c, (ushort*)(a+1));
+	return 0;
+}
+
+static Ctlr*
+ctlrlookup(int major, int minor)
+{
+	Ctlr *c;
+
+	for(c = head; c; c = c->next)
+		if(c->major == major && c->minor == minor)
+			break;
+	return c;
+}
+
+static Ctlr*
+newctlr(Etherpkt *p)
+{
+	int major, minor;
+	Aoeqc *q;
+	Ctlr *c;
+
+	q = (Aoeqc*)p;
+	if(nhgets(q->type) != Aoetype)
+		return 0;
+	major = nhgets(q->major);
+	minor = q->minor;
+
+	if(major == 0xffff || minor == 0xff)
+		return 0;
+
+	if(ctlrlookup(major, minor)){
+		print("duplicate shelf.slot\n");
+		return 0;
+	}
+
+	if((c = malloc(sizeof *c)) == 0)
+		return 0;
+	c->major = major;
+	c->minor = minor;
+	memmove(c->ea, q->src, Eaddrlen);
+
+	if(head != 0)
+		tail->next = c;
+	else
+		head = c;
+	tail = c;
+	return c;
+}
+
+static void
+discover(int major, int minor)
+{
+	int i;
+	Aoehdr *h;
+	Etherpkt p;
+
+	for(i = 0; i < nelem(aoeether); i++){
+		if(aoeether[i] == 0)
+			continue;
+		memset(&p, 0, ETHERMINTU);
+		h = (Aoehdr*)&p;
+		memset(h->dst, 0xff, sizeof h->dst);
+		hnputs(h->type, Aoetype);
+		h->verflag = Aoever << 4;
+		hnputs(h->major, major);
+		h->minor = minor;
+		h->cmd = ACconfig;
+		ethertxpkt(i, &p, ETHERMINTU, 0);
+	}
+}
+
+static int
+rxany(Etherpkt *p, int t)
+{
+	int i, n;
+
+	for(i = 0; i < nelem(aoeether); i++){
+		if(aoeether[i] == 0)
+			continue;
+		while ((n = etherrxpkt(i, p, t)) != 0)
+			if(nhgets(p->type) == Aoetype)
+				return n;
+	}
+	return 0;
+}
+
+static int
+aoeprobe(int major, int minor, SDev *s)
+{
+	Ctlr *ctlr;
+	Etherpkt p;
+	int n, i;
+
+	for(i = 0;; i += 200){
+		if(i > 8000)
+			return -1;
+		discover(major, minor);
+again:
+		n = rxany(&p, 100);
+		if(n > 0 && (ctlr = newctlr(&p)))
+			break;
+		if(n > 0)
+			goto again;
+	}
+
+	s->ctlr = ctlr;
+	s->ifc = &sdaoeifc;
+	s->nunit = 1;
+	return 0;
+}
+
+static char 	*probef[32];
+static int 	nprobe;
+
+int
+pnpprobeid(char *s)
+{
+	int id;
+
+	if(strlen(s) < 2)
+		return 0;
+	id = 'e';
+	if(s[1] == '!')
+		id = s[0];
+	return id;
+}
+
+int
+tokenize(char *s, char **args, int maxargs)
+{
+	int nargs;
+
+	for(nargs = 0; nargs < maxargs; nargs++){
+		while(*s != '\0' && strchr("\t\n ", *s) != nil)
+			s++;
+		if(*s == '\0')
+			break;
+		args[nargs] = s;
+		while(*s != '\0' && strchr("\t\n ", *s) == nil)
+			s++;
+		if(*s != '\0')
+			*s++ = 0;
+	}
+	return nargs;
+}
+
+int
+aoepnp0(void)
+{
+	int i;
+	char *p, c;
+
+	if((p = getconf("aoeif")) == nil)
+		return 0;
+print("aoeif = %s\n", p);
+	nprobe = tokenize(p, probef, nelem(probef));
+	for(i = 0; i < nprobe; i++){
+		if(strncmp(probef[i], "ether", 5) != 0)
+			continue;
+		c = probef[i][5];
+		if(c > '9' || c < '0')
+			continue;
+		aoeether[c - '0'] = 1;
+	}
+
+	if((p = getconf("aoedev")) == nil)
+		return 0;
+	return nprobe = tokenize(p, probef, nelem(probef));
+}
+
+int
+probeshelf(char *s, int *shelf, int *slot)
+{
+	int a, b;
+	char *r;
+
+	for(r = s + strlen(s) - 1; r > s; r--)
+		if((*r < '0' || *r > '9') && *r != '.'){
+			r++;
+			break;
+		}
+	a = strtoul(r, &r, 10);
+	if(*r++ != '.')
+		return -1;
+	b = strtoul(r, 0, 10);
+
+	*shelf = a;
+	*slot = b;
+print("  shelf=%d.%d\n", a, b);
+	return 0;
+}
+
+Ctlr*
+pnpprobe(SDev *sd)
+{
+	int shelf, slot;
+	char *p;
+	static int i;
+
+	if(i >= nprobe)
+		return 0;
+	p = probef[i++];
+	if(strlen(p) < 2)
+		return 0;
+	if(p[1] == '!'){
+		sd->idno = p[0];
+		p += 2;
+	}
+	if(probeshelf(p, &shelf, &slot) == -1 ||
+	    aoeprobe(shelf, slot, sd) == -1 ||
+	    identify(sd->ctlr) == -1)
+		return 0;
+	return sd->ctlr;
+}
+
+/*
+ * we may need to pretend we found something
+ */
+
+SDev*
+aoepnp(void)
+{
+	int n, i, id;
+	char *p;
+	SDev *h, *t, *s;
+
+	p = getconf("aoeif");
+	if (p)
+		print("aoepnp: aoeif=%s\n", p);
+
+	if((n = aoepnp0()) == 0)
+		n = 2;
+	t = h = 0;
+	for(i = 0; i < n; i++){
+		id = 'e';
+		s = malloc(sizeof *s);
+		if(s == 0)
+			break;
+		s->ctlr = 0;
+		s->idno = id;
+		s->ifc = &sdaoeifc;
+		s->nunit = 1;
+		pnpprobe(s);
+
+		if(h)
+			t->next = s;
+		else
+			h = s;
+		t = s;
+	}
+	return h;
+}
+
+static int
+aoeverify(SDunit *u)
+{
+	Ctlr *c;
+	SDev *s;
+
+	s = u->dev;
+	c = s->ctlr;
+	if(c == 0){
+		aoepnp0();
+		if((s->ctlr = c = pnpprobe(s)) == nil)
+			return 0;
+	}
+	c->mediachange = 1;
+	return 1;
+}
+
+static int
+aoeonline(SDunit *u)
+{
+	int r;
+	Ctlr *c;
+
+	c = u->dev->ctlr;
+	if(c->mediachange){
+		r = 2;
+		c->mediachange = 0;
+		u->sectors = c->sectors;
+		u->secsize = 512;
+	} else
+		r = 1;
+	return r;
+}
+
+static int
+rio(Ctlr *c, Aoeata *a, int n, int scnt)
+{
+	int i, tag, cmd;
+
+	for(i = 0; i < 5; i++){
+		tag = hset(c, a, ACata);
+		cmd = a->cmdstat;
+		ethertxpkt(c->ctlrno, (Etherpkt*)a, n, 0);
+		memset(a, 0, sizeof *a);
+again:
+		n = etherrxpkt(c->ctlrno, (Etherpkt*)a, 125);
+		if(n == 0)
+			continue;
+		if(nhgets(a->type) != Aoetype || nhgetl(a->tag) != tag ||
+		    nhgets(a->major) != c->major || a->minor != c->minor)
+			goto again;
+		if(a->cmdstat & 0xa9){
+			print("aoe: ata rio error: %2ux\n", a->cmdstat);
+			return 0;
+		}
+		switch(cmd){
+		case Crd:
+		case Crdext:
+			if(n - sizeof *a < scnt * 512){
+				print("aoe: runt expect %d got %d\n",
+					scnt*512 + sizeof *a, n);
+				return 0;
+			}
+			return n - sizeof *a;
+		case Cwr:
+		case Cwrext:
+			return scnt * 512;
+		default:
+print("unknown cmd %ux\n", cmd);
+			break;
+		}
+	}
+	print("aoe: rio timeout\n");
+	return 0;
+}
+
+static void
+putlba(Aoeata *a, vlong lba)
+{
+	uchar *c;
+
+	c = a->lba;
+	c[0] = lba;
+	c[1] = lba >> 8;
+	c[2] = lba >> 16;
+	c[3] = lba >> 24;
+	c[4] = lba >> 32;
+	c[5] = lba >> 40;
+}
+
+/*
+ * you'll need to loop if you want to read more than 2 sectors.  for now
+ * i'm cheeting and not bothering with a loop.
+ */
+static uchar pktbuf[1024 + sizeof(Aoeata)];
+
+static int
+aoebuild(Ctlr *c, uchar *cmd, char *data, vlong lba, int scnt)
+{
+	int n;
+	Aoeata *a;
+
+	memset(pktbuf, 0, sizeof pktbuf);
+	a = (Aoeata*)pktbuf;
+	hset(c, a, ACata);
+	putlba(a, lba);
+
+	a->cmdstat = 0x20;
+	if(c->flag & Dllba){
+		a->aflag |= AAFext;
+		a->cmdstat |= 4;
+	}else{
+		a->lba[3] &= 0xf;
+		a->lba[3] |= 0xe0;		/* LBA bit+obsolete 0xa0 */
+	}
+
+	n = scnt;
+	if(n > 2)
+		n = 2;
+	a->scnt = n;
+
+	switch(*cmd){
+	case 0x2a:
+		a->aflag |= AAFwrite;
+		a->cmdstat |= 10;
+		memmove(a+1, data, n*512);
+		n = sizeof *a + n*512;
+		break;
+	case 0x28:
+		n = sizeof *a;
+		break;
+	default:
+		print("aoe: bad cmd 0x%.2ux\n", cmd[0]);
+		return -1;
+	}
+	return n;
+}
+
+static int
+aoerio(SDreq *r)
+{
+	int size, nsec, n;
+	vlong lba;
+	char *data;
+	uchar *cmd;
+	Aoeata *a;
+	Ctlr *c;
+	SDunit *unit;
+
+	unit = r->unit;
+	c = unit->dev->ctlr;
+	if(r->data == nil)
+		return SDok;
+	cmd = r->cmd;
+
+	lba = cmd[2]<<24 | cmd[3]<<16 | cmd[4]<<8 | cmd[5];	/* sic. */
+	nsec = cmd[7]<<8 | cmd[8];
+	a = (Aoeata*)pktbuf;
+	data = r->data;
+	r->rlen = 0;
+
+	for(; nsec > 0; nsec -= n){
+//		print("aoebuild(%2x, %p, %lld, %d)\n", *cmd, data, lba, nsec);
+		size = aoebuild(c, cmd, data, lba, nsec);
+		if(size < 0){
+			r->status = SDcheck;
+			return SDcheck;
+		}
+		n = a->scnt;
+		r->rlen += rio(c, a, size, n);
+		if(*cmd == 0x28)
+			memmove(r->data, a + 1, n * 512);
+		data += n * 512;
+		lba += n;
+	}
+
+	r->status = SDok;
+	return SDok;
+}
+
+SDifc sdaoeifc = {
+	"aoe",
+
+	aoepnp,
+	nil,		/* legacy */
+	nil,		/* id */
+	nil,		/* enable */
+	nil,		/* disable */
+
+	aoeverify,
+	aoeonline,
+	aoerio,
+	nil,
+	nil,
+
+	scsibio,
+};
--- /dev/null
+++ b/os/boot/pc/sdata.c
@@ -1,0 +1,1652 @@
+#include "u.h"
+#include "lib.h"
+#include "mem.h"
+#include "dat.h"
+#include "fns.h"
+#include "io.h"
+#include "ureg.h"
+#include "error.h"
+
+#include "sd.h"
+
+extern SDifc sdataifc;
+
+enum {
+	DbgCONFIG	= 0x0001,	/* detected drive config info */
+	DbgIDENTIFY	= 0x0002,	/* detected drive identify info */
+	DbgSTATE	= 0x0004,	/* dump state on panic */
+	DbgPROBE	= 0x0008,	/* trace device probing */
+	DbgDEBUG	= 0x0080,	/* the current problem... */
+	DbgINL		= 0x0100,	/* That Inil20+ message we hate */
+	Dbg48BIT	= 0x0200,	/* 48-bit LBA */
+	DbgBsy		= 0x0400,	/* interrupt but Bsy (shared IRQ) */
+};
+#define DEBUG		(DbgDEBUG|DbgCONFIG)
+
+enum {					/* I/O ports */
+	Data		= 0,
+	Error		= 1,		/* (read) */
+	Features	= 1,		/* (write) */
+	Count		= 2,		/* sector count<7-0>, sector count<15-8> */
+	Ir		= 2,		/* interrupt reason (PACKET) */
+	Sector		= 3,		/* sector number */
+	Lbalo		= 3,		/* LBA<7-0>, LBA<31-24> */
+	Cyllo		= 4,		/* cylinder low */
+	Bytelo		= 4,		/* byte count low (PACKET) */
+	Lbamid		= 4,		/* LBA<15-8>, LBA<39-32> */
+	Cylhi		= 5,		/* cylinder high */
+	Bytehi		= 5,		/* byte count hi (PACKET) */
+	Lbahi		= 5,		/* LBA<23-16>, LBA<47-40> */
+	Dh		= 6,		/* Device/Head, LBA<32-14> */
+	Status		= 7,		/* (read) */
+	Command		= 7,		/* (write) */
+
+	As		= 2,		/* Alternate Status (read) */
+	Dc		= 2,		/* Device Control (write) */
+};
+
+enum {					/* Error */
+	Med		= 0x01,		/* Media error */
+	Ili		= 0x01,		/* command set specific (PACKET) */
+	Nm		= 0x02,		/* No Media */
+	Eom		= 0x02,		/* command set specific (PACKET) */
+	Abrt		= 0x04,		/* Aborted command */
+	Mcr		= 0x08,		/* Media Change Request */
+	Idnf		= 0x10,		/* no user-accessible address */
+	Mc		= 0x20,		/* Media Change */
+	Unc		= 0x40,		/* Uncorrectable data error */
+	Wp		= 0x40,		/* Write Protect */
+	Icrc		= 0x80,		/* Interface CRC error */
+};
+
+enum {					/* Features */
+	Dma		= 0x01,		/* data transfer via DMA (PACKET) */
+	Ovl		= 0x02,		/* command overlapped (PACKET) */
+};
+
+enum {					/* Interrupt Reason */
+	Cd		= 0x01,		/* Command/Data */
+	Io		= 0x02,		/* I/O direction */
+	Rel		= 0x04,		/* Bus Release */
+};
+
+enum {					/* Device/Head */
+	Dev0		= 0xA0,		/* Master */
+	Dev1		= 0xB0,		/* Slave */
+	Lba		= 0x40,		/* LBA mode */
+};
+
+enum {					/* Status, Alternate Status */
+	Err		= 0x01,		/* Error */
+	Chk		= 0x01,		/* Check error (PACKET) */
+	Drq		= 0x08,		/* Data Request */
+	Dsc		= 0x10,		/* Device Seek Complete */
+	Serv		= 0x10,		/* Service */
+	Df		= 0x20,		/* Device Fault */
+	Dmrd		= 0x20,		/* DMA ready (PACKET) */
+	Drdy		= 0x40,		/* Device Ready */
+	Bsy		= 0x80,		/* Busy */
+};
+
+enum {					/* Command */
+	Cnop		= 0x00,		/* NOP */
+	Cdr		= 0x08,		/* Device Reset */
+	Crs		= 0x20,		/* Read Sectors */
+	Crs48		= 0x24,		/* Read Sectors Ext */
+	Crd48		= 0x25,		/* Read w/ DMA Ext */
+	Crdq48		= 0x26,		/* Read w/ DMA Queued Ext */
+	Crsm48		= 0x29,		/* Read Multiple Ext */
+	Cws		= 0x30,		/* Write Sectors */
+	Cws48		= 0x34,		/* Write Sectors Ext */
+	Cwd48		= 0x35,		/* Write w/ DMA Ext */
+	Cwdq48		= 0x36,		/* Write w/ DMA Queued Ext */
+	Cwsm48		= 0x39,		/* Write Multiple Ext */
+	Cedd		= 0x90,		/* Execute Device Diagnostics */
+	Cpkt		= 0xA0,		/* Packet */
+	Cidpkt		= 0xA1,		/* Identify Packet Device */
+	Crsm		= 0xC4,		/* Read Multiple */
+	Cwsm		= 0xC5,		/* Write Multiple */
+	Csm		= 0xC6,		/* Set Multiple */
+	Crdq		= 0xC7,		/* Read DMA queued */
+	Crd		= 0xC8,		/* Read DMA */
+	Cwd		= 0xCA,		/* Write DMA */
+	Cwdq		= 0xCC,		/* Write DMA queued */
+	Cstandby	= 0xE2,		/* Standby */
+	Cid		= 0xEC,		/* Identify Device */
+	Csf		= 0xEF,		/* Set Features */
+};
+
+enum {					/* Device Control */
+	Nien		= 0x02,		/* (not) Interrupt Enable */
+	Srst		= 0x04,		/* Software Reset */
+};
+
+enum {					/* PCI Configuration Registers */
+	Bmiba		= 0x20,		/* Bus Master Interface Base Address */
+	Idetim		= 0x40,		/* IE Timing */
+	Sidetim		= 0x44,		/* Slave IE Timing */
+	Udmactl		= 0x48,		/* Ultra DMA/33 Control */
+	Udmatim		= 0x4A,		/* Ultra DMA/33 Timing */
+};
+
+enum {					/* Bus Master IDE I/O Ports */
+	Bmicx		= 0,		/* Command */
+	Bmisx		= 2,		/* Status */
+	Bmidtpx		= 4,		/* Descriptor Table Pointer */
+};
+
+enum {					/* Bmicx */
+	Ssbm		= 0x01,		/* Start/Stop Bus Master */
+	Rwcon		= 0x08,		/* Read/Write Control */
+};
+
+enum {					/* Bmisx */
+	Bmidea		= 0x01,		/* Bus Master IDE Active */
+	Idedmae		= 0x02,		/* IDE DMA Error  (R/WC) */
+	Ideints		= 0x04,		/* IDE Interrupt Status (R/WC) */
+	Dma0cap		= 0x20,		/* Drive 0 DMA Capable */
+	Dma1cap		= 0x40,		/* Drive 0 DMA Capable */
+};
+enum {					/* Physical Region Descriptor */
+	PrdEOT		= 0x80000000,	/* Bus Master IDE Active */
+};
+
+enum {					/* offsets into the identify info. */
+	Iconfig		= 0,		/* general configuration */
+	Ilcyl		= 1,		/* logical cylinders */
+	Ilhead		= 3,		/* logical heads */
+	Ilsec		= 6,		/* logical sectors per logical track */
+	Iserial		= 10,		/* serial number */
+	Ifirmware	= 23,		/* firmware revision */
+	Imodel		= 27,		/* model number */
+	Imaxrwm		= 47,		/* max. read/write multiple sectors */
+	Icapabilities	= 49,		/* capabilities */
+	Istandby	= 50,		/* device specific standby timer */
+	Ipiomode	= 51,		/* PIO data transfer mode number */
+	Ivalid		= 53,
+	Iccyl		= 54,		/* cylinders if (valid&0x01) */
+	Ichead		= 55,		/* heads if (valid&0x01) */
+	Icsec		= 56,		/* sectors if (valid&0x01) */
+	Iccap		= 57,		/* capacity if (valid&0x01) */
+	Irwm		= 59,		/* read/write multiple */
+	Ilba		= 60,		/* LBA size */
+	Imwdma		= 63,		/* multiword DMA mode */
+	Iapiomode	= 64,		/* advanced PIO modes supported */
+	Iminmwdma	= 65,		/* min. multiword DMA cycle time */
+	Irecmwdma	= 66,		/* rec. multiword DMA cycle time */
+	Iminpio		= 67,		/* min. PIO cycle w/o flow control */
+	Iminiordy	= 68,		/* min. PIO cycle with IORDY */
+	Ipcktbr		= 71,		/* time from PACKET to bus release */
+	Iserbsy		= 72,		/* time from SERVICE to !Bsy */
+	Iqdepth		= 75,		/* max. queue depth */
+	Imajor		= 80,		/* major version number */
+	Iminor		= 81,		/* minor version number */
+	Icsfs		= 82,		/* command set/feature supported */
+	Icsfe		= 85,		/* command set/feature enabled */
+	Iudma		= 88,		/* ultra DMA mode */
+	Ierase		= 89,		/* time for security erase */
+	Ieerase		= 90,		/* time for enhanced security erase */
+	Ipower		= 91,		/* current advanced power management */
+	Ilba48		= 100,		/* 48-bit LBA size (64 bits in 100-103) */
+	Irmsn		= 127,		/* removable status notification */
+	Isecstat	= 128,		/* security status */
+};
+
+typedef struct Ctlr Ctlr;
+typedef struct Drive Drive;
+
+typedef struct Prd {
+	ulong	pa;			/* Physical Base Address */
+	int	count;
+} Prd;
+
+enum {
+	Nprd		= SDmaxio/(64*1024)+2,
+};
+
+typedef struct Ctlr {
+	int	cmdport;
+	int	ctlport;
+	int	irq;
+	int	tbdf;
+
+	Pcidev*	pcidev;
+	void	(*ienable)(Ctlr*);
+	SDev*	sdev;
+
+	Drive*	drive[2];
+
+	Prd*	prdt;			/* physical region descriptor table */
+
+//	QLock;				/* current command */
+	Drive*	curdrive;
+	int	command;		/* last command issued (debugging) */
+//	Rendez;
+	int	done;
+
+	Lock;				/* register access */
+} Ctlr;
+
+typedef struct Drive {
+	Ctlr*	ctlr;
+
+	int	dev;
+	ushort	info[256];
+	int	c;			/* cylinder */
+	int	h;			/* head */
+	int	s;			/* sector */
+	vlong	sectors;		/* total */
+	int	secsize;		/* sector size */
+
+//	int	dma;			/* DMA R/W possible */
+//	int	dmactl;
+//	int	rwm;			/* read/write multiple possible */
+//	int	rwmctl;
+
+	int	pkt;			/* PACKET device, length of pktcmd */
+	uchar	pktcmd[16];
+//	int	pktdma;			/* this PACKET command using dma */
+
+	uchar	sense[18];
+	uchar	inquiry[48];
+
+//	QLock;				/* drive access */
+	int	command;		/* current command */
+	int	write;
+	uchar*	data;
+	int	dlen;
+	uchar*	limit;
+	int	count;			/* sectors */
+	int	block;			/* R/W bytes per block */
+	int	status;
+	int	error;
+	int	flags;			/* internal flags */
+} Drive;
+
+enum {					/* internal flags */
+	Lba48		= 0x1,		/* LBA48 mode */
+	Lba48always	= 0x2,		/* ... */
+};
+
+static void
+pc87415ienable(Ctlr* ctlr)
+{
+	Pcidev *p;
+	int x;
+
+	p = ctlr->pcidev;
+	if(p == nil)
+		return;
+
+	x = pcicfgr32(p, 0x40);
+	if(ctlr->cmdport == p->mem[0].bar)
+		x &= ~0x00000100;
+	else
+		x &= ~0x00000200;
+	pcicfgw32(p, 0x40, x);
+}
+
+static int
+atadebug(int cmdport, int ctlport, char* fmt, ...)
+{
+	int i, n;
+	va_list arg;
+	char buf[PRINTSIZE];
+
+	if(!(DEBUG & DbgPROBE)){
+		USED(cmdport, ctlport, fmt);
+		return 0;
+	}
+
+	va_start(arg, fmt);
+	n = vseprint(buf, buf+sizeof(buf), fmt, arg) - buf;
+	va_end(arg);
+
+	if(cmdport){
+		if(buf[n-1] == '\n')
+			n--;
+		n += snprint(buf+n, PRINTSIZE-n, " ataregs 0x%uX:",
+			cmdport);
+		for(i = Features; i < Command; i++)
+			n += snprint(buf+n, PRINTSIZE-n, " 0x%2.2uX",
+				inb(cmdport+i));
+		if(ctlport)
+			n += snprint(buf+n, PRINTSIZE-n, " 0x%2.2uX",
+				inb(ctlport+As));
+		n += snprint(buf+n, PRINTSIZE-n, "\n");
+	}
+	putstrn(buf, n);
+
+	return n;
+}
+
+static int
+ataready(int cmdport, int ctlport, int dev, int reset, int ready, int micro)
+{
+	int as;
+
+	atadebug(cmdport, ctlport, "ataready: dev %uX reset %uX ready %uX",
+		dev, reset, ready);
+
+	for(;;){
+		/*
+		 * Wait for the controller to become not busy and
+		 * possibly for a status bit to become true (usually
+		 * Drdy). Must change to the appropriate device
+		 * register set if necessary before testing for ready.
+		 * Always run through the loop at least once so it
+		 * can be used as a test for !Bsy.
+		 */
+		as = inb(ctlport+As);
+		if(as & reset){
+			/* nothing to do */;
+		}
+		else if(dev){
+			outb(cmdport+Dh, dev);
+			dev = 0;
+		}
+		else if(ready == 0 || (as & ready)){
+			atadebug(0, 0, "ataready: %d 0x%2.2uX\n", micro, as);
+			return as;
+		}
+
+		if(micro-- <= 0){
+			atadebug(0, 0, "ataready: %d 0x%2.2uX\n", micro, as);
+			break;
+		}
+		microdelay(1);
+	}
+	atadebug(cmdport, ctlport, "ataready: timeout");
+
+	return -1;
+}
+
+static int
+atacsfenabled(Drive* drive, vlong csf)
+{
+	int cmdset, i, x;
+
+	for(i = 0; i < 3; i++){
+		x = (csf>>(16*i)) & 0xFFFF;
+		if(x == 0)
+			continue;
+		cmdset = drive->info[Icsfe+i];
+		if(cmdset == 0 || cmdset == 0xFFFF)
+			return 0;
+		return cmdset & x;
+	}
+
+	return 0;
+}
+
+/*
+static int
+atasf(int cmdport, int ctlport, int dev, uchar* command)
+{
+	int as, i;
+
+	if(ataready(cmdport, ctlport, dev, Bsy|Drq, Drdy, 108*1000) < 0)
+		return -1;
+
+	for(i = Features; i < Dh; i++)
+		outb(cmdport+i, command[i]);
+	outb(cmdport+Command, Csf);
+	microdelay(100);
+	as = ataready(cmdport, ctlport, 0, Bsy, Drdy|Df|Err, 109*1000);
+	if(as < 0 || (as & (Df|Err)))
+		return -1;
+	return 0;
+}
+ */
+
+static int
+ataidentify(int cmdport, int ctlport, int dev, int pkt, void* info)
+{
+	int as, command, drdy;
+
+	if(pkt){
+		command = Cidpkt;
+		drdy = 0;
+	}
+	else{
+		command = Cid;
+		drdy = Drdy;
+	}
+	as = ataready(cmdport, ctlport, dev, Bsy|Drq, drdy, 103*1000);
+	if(as < 0)
+		return as;
+	outb(cmdport+Command, command);
+	microdelay(1);
+
+	as = ataready(cmdport, ctlport, 0, Bsy, Drq|Err, 400*1000);
+	if(as < 0)
+		return -1;
+	if(as & Err)
+		return as;
+
+	memset(info, 0, 512);
+	inss(cmdport+Data, info, 256);
+	inb(cmdport+Status);
+
+	if(DEBUG & DbgIDENTIFY){
+		int i;
+		ushort *sp;
+
+		sp = (ushort*)info;
+		for(i = 0; i < 256; i++){
+			if(i && (i%16) == 0)
+				print("\n");
+			print(" %4.4uX ", *sp);
+			sp++;
+		}
+		print("\n");
+	}
+
+	return 0;
+}
+
+static Drive*
+atadrive(int cmdport, int ctlport, int dev)
+{
+	Drive *drive;
+	int as, i, pkt;
+	uchar buf[512], *p;
+	ushort iconfig, *sp;
+
+	atadebug(0, 0, "identify: port 0x%uX dev 0x%2.2uX\n", cmdport, dev);
+	pkt = 1;
+retry:
+	as = ataidentify(cmdport, ctlport, dev, pkt, buf);
+	if(as < 0)
+		return nil;
+	if(as & Err){
+		if(pkt == 0)
+			return nil;
+		pkt = 0;
+		goto retry;
+	}
+
+	if((drive = malloc(sizeof(Drive))) == nil)
+		return nil;
+	drive->dev = dev;
+	memmove(drive->info, buf, sizeof(drive->info));
+	drive->sense[0] = 0x70;
+	drive->sense[7] = sizeof(drive->sense)-7;
+
+	drive->inquiry[2] = 2;
+	drive->inquiry[3] = 2;
+	drive->inquiry[4] = sizeof(drive->inquiry)-4;
+	p = &drive->inquiry[8];
+	sp = &drive->info[Imodel];
+	for(i = 0; i < 20; i++){
+		*p++ = *sp>>8;
+		*p++ = *sp++;
+	}
+
+	drive->secsize = 512;
+
+	/*
+	 * Beware the CompactFlash Association feature set.
+	 * Now, why this value in Iconfig just walks all over the bit
+	 * definitions used in the other parts of the ATA/ATAPI standards
+	 * is a mystery and a sign of true stupidity on someone's part.
+	 * Anyway, the standard says if this value is 0x848A then it's
+	 * CompactFlash and it's NOT a packet device.
+	 */
+	iconfig = drive->info[Iconfig];
+	if(iconfig != 0x848A && (iconfig & 0xC000) == 0x8000){
+		if(iconfig & 0x01)
+			drive->pkt = 16;
+		else
+			drive->pkt = 12;
+	}
+	else{
+		if(drive->info[Ivalid] & 0x0001){
+			drive->c = drive->info[Iccyl];
+			drive->h = drive->info[Ichead];
+			drive->s = drive->info[Icsec];
+		}
+		else{
+			drive->c = drive->info[Ilcyl];
+			drive->h = drive->info[Ilhead];
+			drive->s = drive->info[Ilsec];
+		}
+		if(drive->info[Icapabilities] & 0x0200){
+			if(drive->info[Icsfs+1] & 0x0400){
+				drive->sectors = drive->info[Ilba48]
+					|(drive->info[Ilba48+1]<<16)
+					|((vlong)drive->info[Ilba48+2]<<32);
+				drive->flags |= Lba48;
+			}
+			else{
+				drive->sectors = (drive->info[Ilba+1]<<16)
+					 |drive->info[Ilba];
+			}
+			drive->dev |= Lba;
+		}
+		else
+			drive->sectors = drive->c*drive->h*drive->s;
+	//	atarwmmode(drive, cmdport, ctlport, dev);
+	}
+//	atadmamode(drive);	
+
+	if(DEBUG & DbgCONFIG){
+		print("dev %2.2uX port %uX config %4.4uX capabilities %4.4uX",
+			dev, cmdport, iconfig, drive->info[Icapabilities]);
+		print(" mwdma %4.4uX", drive->info[Imwdma]);
+		if(drive->info[Ivalid] & 0x04)
+			print(" udma %4.4uX", drive->info[Iudma]);
+//		print(" dma %8.8uX rwm %ud", drive->dma, drive->rwm);
+		if(drive->flags&Lba48)
+			print("\tLLBA sectors %lld", drive->sectors);
+		print("\n");
+	}
+
+	return drive;
+}
+
+static void
+atasrst(int ctlport)
+{
+	/*
+	 * Srst is a big stick and may cause problems if further
+	 * commands are tried before the drives become ready again.
+	 * Also, there will be problems here if overlapped commands
+	 * are ever supported.
+	 */
+	microdelay(5);
+	outb(ctlport+Dc, Srst);
+	microdelay(5);
+	outb(ctlport+Dc, 0);
+	microdelay(2*1000);
+}
+
+static SDev*
+ataprobe(int cmdport, int ctlport, int irq)
+{
+	Ctlr* ctlr;
+	SDev *sdev;
+	Drive *drive;
+	int dev, error, rhi, rlo;
+
+//	if(ioalloc(cmdport, 8, 0, "atacmd") < 0)
+//		return nil;
+//	if(ioalloc(ctlport+As, 1, 0, "atactl") < 0){
+//		iofree(cmdport);
+//		return nil;
+//	}
+
+	/*
+	 * Try to detect a floating bus.
+	 * Bsy should be cleared. If not, see if the cylinder registers
+	 * are read/write capable.
+	 * If the master fails, try the slave to catch slave-only
+	 * configurations.
+	 * There's no need to restore the tested registers as they will
+	 * be reset on any detected drives by the Cedd command.
+	 * All this indicates is that there is at least one drive on the
+	 * controller; when the non-existent drive is selected in a
+	 * single-drive configuration the registers of the existing drive
+	 * are often seen, only command execution fails.
+	 */
+	dev = Dev0;
+	if(inb(ctlport+As) & Bsy){
+		outb(cmdport+Dh, dev);
+		microdelay(1);
+trydev1:
+		atadebug(cmdport, ctlport, "ataprobe bsy");
+		outb(cmdport+Cyllo, 0xAA);
+		outb(cmdport+Cylhi, 0x55);
+		outb(cmdport+Sector, 0xFF);
+		rlo = inb(cmdport+Cyllo);
+		rhi = inb(cmdport+Cylhi);
+		if(rlo != 0xAA && (rlo == 0xFF || rhi != 0x55)){
+			if(dev == Dev1){
+release:
+			//	iofree(cmdport);
+			//	iofree(ctlport+As);
+				return nil;
+			}
+			dev = Dev1;
+			if(ataready(cmdport, ctlport, dev, Bsy, 0, 20*1000) < 0)
+				goto trydev1;
+		}
+	}
+
+	/*
+	 * Disable interrupts on any detected controllers.
+	 */
+	outb(ctlport+Dc, Nien);
+tryedd1:
+	if(ataready(cmdport, ctlport, dev, Bsy|Drq, 0, 105*1000) < 0){
+		/*
+		 * There's something there, but it didn't come up clean,
+		 * so try hitting it with a big stick. The timing here is
+		 * wrong but this is a last-ditch effort and it sometimes
+		 * gets some marginal hardware back online.
+		 */
+		atasrst(ctlport);
+		if(ataready(cmdport, ctlport, dev, Bsy|Drq, 0, 106*1000) < 0)
+			goto release;
+	}
+
+	/*
+	 * Can only get here if controller is not busy.
+	 * If there are drives Bsy will be set within 400nS,
+	 * must wait 2mS before testing Status.
+	 * Wait for the command to complete (6 seconds max).
+	 */
+	outb(cmdport+Command, Cedd);
+	delay(2);
+	if(ataready(cmdport, ctlport, dev, Bsy|Drq, 0, 6*1000*1000) < 0)
+		goto release;
+
+	/*
+	 * If bit 0 of the error register is set then the selected drive
+	 * exists. This is enough to detect single-drive configurations.
+	 * However, if the master exists there is no way short of executing
+	 * a command to determine if a slave is present.
+	 * It appears possible to get here testing Dev0 although it doesn't
+	 * exist and the EDD won't take, so try again with Dev1.
+	 */
+	error = inb(cmdport+Error);
+	atadebug(cmdport, ctlport, "ataprobe: dev %uX", dev);
+	if((error & ~0x80) != 0x01){
+		if(dev == Dev1)
+			goto release;
+		dev = Dev1;
+		goto tryedd1;
+	}
+
+	/*
+	 * At least one drive is known to exist, try to
+	 * identify it. If that fails, don't bother checking
+	 * any further.
+	 * If the one drive found is Dev0 and the EDD command
+	 * didn't indicate Dev1 doesn't exist, check for it.
+	 */
+	if((drive = atadrive(cmdport, ctlport, dev)) == nil)
+		goto release;
+	if((ctlr = malloc(sizeof(Ctlr))) == nil){
+		free(drive);
+		goto release;
+	}
+	if((sdev = malloc(sizeof(SDev))) == nil){
+		free(ctlr);
+		free(drive);
+		goto release;
+	}
+	drive->ctlr = ctlr;
+	if(dev == Dev0){
+		ctlr->drive[0] = drive;
+		if(!(error & 0x80)){
+			/*
+			 * Always leave Dh pointing to a valid drive,
+			 * otherwise a subsequent call to ataready on
+			 * this controller may try to test a bogus Status.
+			 * Ataprobe is the only place possibly invalid
+			 * drives should be selected.
+			 */
+			drive = atadrive(cmdport, ctlport, Dev1);
+			if(drive != nil){
+				drive->ctlr = ctlr;
+				ctlr->drive[1] = drive;
+			}
+			else{
+				outb(cmdport+Dh, Dev0);
+				microdelay(1);
+			}
+		}
+	}
+	else
+		ctlr->drive[1] = drive;
+
+	ctlr->cmdport = cmdport;
+	ctlr->ctlport = ctlport;
+	ctlr->irq = irq;
+	ctlr->tbdf = BUSUNKNOWN;
+	ctlr->command = Cedd;		/* debugging */
+
+	sdev->ifc = &sdataifc;
+	sdev->ctlr = ctlr;
+	sdev->nunit = 2;
+	ctlr->sdev = sdev;
+
+	return sdev;
+}
+
+static int
+atasetsense(Drive* drive, int status, int key, int asc, int ascq)
+{
+	drive->sense[2] = key;
+	drive->sense[12] = asc;
+	drive->sense[13] = ascq;
+
+	return status;
+}
+
+static int
+atamodesense(Drive* drive, uchar* cmd)
+{
+	int len;
+
+	/*
+	 * Fake a vendor-specific request with page code 0,
+	 * return the drive info.
+	 */
+	if((cmd[2] & 0x3F) != 0 && (cmd[2] & 0x3F) != 0x3F)
+		return atasetsense(drive, SDcheck, 0x05, 0x24, 0);
+	len = (cmd[7]<<8)|cmd[8];
+	if(len == 0)
+		return SDok;
+	if(len < 8+sizeof(drive->info))
+		return atasetsense(drive, SDcheck, 0x05, 0x1A, 0);
+	if(drive->data == nil || drive->dlen < len)
+		return atasetsense(drive, SDcheck, 0x05, 0x20, 1);
+	memset(drive->data, 0, 8);
+	drive->data[0] = sizeof(drive->info)>>8;
+	drive->data[1] = sizeof(drive->info);
+	memmove(drive->data+8, drive->info, sizeof(drive->info));
+	drive->data += 8+sizeof(drive->info);
+
+	return SDok;
+}
+
+static void
+atanop(Drive* drive, int subcommand)
+{
+	Ctlr* ctlr;
+	int as, cmdport, ctlport, timeo;
+
+	/*
+	 * Attempt to abort a command by using NOP.
+	 * In response, the drive is supposed to set Abrt
+	 * in the Error register, set (Drdy|Err) in Status
+	 * and clear Bsy when done. However, some drives
+	 * (e.g. ATAPI Zip) just go Bsy then clear Status
+	 * when done, hence the timeout loop only on Bsy
+	 * and the forced setting of drive->error.
+	 */
+	ctlr = drive->ctlr;
+	cmdport = ctlr->cmdport;
+	outb(cmdport+Features, subcommand);
+	outb(cmdport+Dh, drive->dev);
+	ctlr->command = Cnop;		/* debugging */
+	outb(cmdport+Command, Cnop);
+
+	microdelay(1);
+	ctlport = ctlr->ctlport;
+	for(timeo = 0; timeo < 1000; timeo++){
+		as = inb(ctlport+As);
+		if(!(as & Bsy))
+			break;
+		microdelay(1);
+	}
+	drive->error |= Abrt;
+}
+
+static void
+ataabort(Drive* drive, int dolock)
+{
+	/*
+	 * If NOP is available (packet commands) use it otherwise
+	 * must try a software reset.
+	 */
+	if(dolock)
+		ilock(drive->ctlr);
+	if(atacsfenabled(drive, 0x0000000000004000LL))
+		atanop(drive, 0);
+	else{
+		atasrst(drive->ctlr->ctlport);
+		drive->error |= Abrt;
+	}
+	if(dolock)
+		iunlock(drive->ctlr);
+}
+
+static int
+atapktiodone(void* arg)
+{
+	return ((Ctlr*)arg)->done;
+}
+
+static void
+atapktinterrupt(Drive* drive)
+{
+	Ctlr* ctlr;
+	int cmdport, len;
+
+	ctlr = drive->ctlr;
+	cmdport = ctlr->cmdport;
+	switch(inb(cmdport+Ir) & (/*Rel|*/Io|Cd)){
+	case Cd:
+		outss(cmdport+Data, drive->pktcmd, drive->pkt/2);
+		break;
+
+	case 0:
+		len = (inb(cmdport+Bytehi)<<8)|inb(cmdport+Bytelo);
+		if(drive->data+len > drive->limit){
+			atanop(drive, 0);
+			break;
+		}
+		outss(cmdport+Data, drive->data, len/2);
+		drive->data += len;
+		break;
+
+	case Io:
+		len = (inb(cmdport+Bytehi)<<8)|inb(cmdport+Bytelo);
+		if(drive->data+len > drive->limit){
+			atanop(drive, 0);
+			break;
+		}
+		inss(cmdport+Data, drive->data, len/2);
+		drive->data += len;
+		break;
+
+	case Io|Cd:
+	//	if(drive->pktdma)
+	//		atadmainterrupt(drive, drive->dlen);
+	//	else
+			ctlr->done = 1;
+		break;
+	}
+}
+
+static int
+atapktio(Drive* drive, uchar* cmd, int clen)
+{
+	Ctlr *ctlr;
+	int as, cmdport, ctlport, len, r;
+
+	if(cmd[0] == 0x5A && (cmd[2] & 0x3F) == 0)
+		return atamodesense(drive, cmd);
+
+	r = SDok;
+
+	drive->command = Cpkt;
+	memmove(drive->pktcmd, cmd, clen);
+	memset(drive->pktcmd+clen, 0, drive->pkt-clen);
+	drive->limit = drive->data+drive->dlen;
+
+	ctlr = drive->ctlr;
+	cmdport = ctlr->cmdport;
+	ctlport = ctlr->ctlport;
+
+	qlock(ctlr);
+
+	as = ataready(cmdport, ctlport, drive->dev, Bsy|Drq, 0, 107*1000);
+	if(as < 0 || (as&Chk)){
+		qunlock(ctlr);
+		return -1;
+	}
+
+	ilock(ctlr);
+//	if(drive->dlen && drive->dmactl && !atadmasetup(drive, drive->dlen))
+//		drive->pktdma = Dma;
+//	else
+//		drive->pktdma = 0;
+
+	outb(cmdport+Features, 0/*drive->pktdma*/);
+	outb(cmdport+Count, 0);
+	outb(cmdport+Sector, 0);
+	len = 16*drive->secsize;
+	outb(cmdport+Bytelo, len);
+	outb(cmdport+Bytehi, len>>8);
+	outb(cmdport+Dh, drive->dev);
+	ctlr->done = 0;
+	ctlr->curdrive = drive;
+	ctlr->command = Cpkt;		/* debugging */
+//	if(drive->pktdma)
+//		atadmastart(ctlr, drive->write);
+	outb(cmdport+Command, Cpkt);
+
+	if((drive->info[Iconfig] & 0x0060) != 0x0020){
+		microdelay(1);
+		as = ataready(cmdport, ctlport, 0, Bsy, Drq|Chk, 4*1000);
+		if(as < 0 || (as & (Bsy|Chk))){
+			drive->status = as<0 ? 0 : as;
+			ctlr->curdrive = nil;
+			ctlr->done = 1;
+			r = SDtimeout;
+		}else
+			atapktinterrupt(drive);
+	}
+	iunlock(ctlr);
+
+	sleep(ctlr, atapktiodone, ctlr);
+
+	qunlock(ctlr);
+
+	if(drive->status & Chk)
+		r = SDcheck;
+
+	return r;
+}
+
+static int
+atageniodone(void* arg)
+{
+	return ((Ctlr*)arg)->done;
+}
+
+static uchar cmd48[256] = {
+	[Crs]	Crs48,
+	[Crd]	Crd48,
+	[Crdq]	Crdq48,
+	[Crsm]	Crsm48,
+	[Cws]	Cws48,
+	[Cwd]	Cwd48,
+	[Cwdq]	Cwdq48,
+	[Cwsm]	Cwsm48,
+};
+
+static int
+atageniostart(Drive* drive, vlong lba)
+{
+	Ctlr *ctlr;
+	uchar cmd;
+	int as, c, cmdport, ctlport, h, len, s, use48;
+
+	use48 = 0;
+	if((drive->flags&Lba48always) || (lba>>28) || drive->count > 256){
+		if(!(drive->flags & Lba48))
+			return -1;
+		use48 = 1;
+		c = h = s = 0;
+	}else if(drive->dev & Lba){
+		c = (lba>>8) & 0xFFFF;
+		h = (lba>>24) & 0x0F;
+		s = lba & 0xFF;
+	}
+	else{
+		c = lba/(drive->s*drive->h);
+		h = ((lba/drive->s) % drive->h);
+		s = (lba % drive->s) + 1;
+	}
+
+	ctlr = drive->ctlr;
+	cmdport = ctlr->cmdport;
+	ctlport = ctlr->ctlport;
+	if(ataready(cmdport, ctlport, drive->dev, Bsy|Drq, 0, 101*1000) < 0)
+		return -1;
+
+	ilock(ctlr);
+
+	drive->block = drive->secsize;
+	if(drive->write)
+		drive->command = Cws;
+	else
+		drive->command = Crs;
+
+	drive->limit = drive->data + drive->count*drive->secsize;
+	cmd = drive->command;
+	if(use48){
+		outb(cmdport+Count, (drive->count>>8) & 0xFF);
+		outb(cmdport+Count, drive->count & 0XFF);
+		outb(cmdport+Lbalo, (lba>>24) & 0xFF);
+		outb(cmdport+Lbalo, lba & 0xFF);
+		outb(cmdport+Lbamid, (lba>>32) & 0xFF);
+		outb(cmdport+Lbamid, (lba>>8) & 0xFF);
+		outb(cmdport+Lbahi, (lba>>40) & 0xFF);
+		outb(cmdport+Lbahi, (lba>>16) & 0xFF);
+		outb(cmdport+Dh, drive->dev|Lba);
+		cmd = cmd48[cmd];
+
+		if(DEBUG & Dbg48BIT)
+			print("using 48-bit commands\n");
+	}else{
+		outb(cmdport+Count, drive->count);
+		outb(cmdport+Sector, s);
+		outb(cmdport+Cyllo, c);
+		outb(cmdport+Cylhi, c>>8);
+		outb(cmdport+Dh, drive->dev|h);
+	}
+	ctlr->done = 0;
+	ctlr->curdrive = drive;
+	ctlr->command = drive->command;	/* debugging */
+	outb(cmdport+Command, cmd);
+
+	switch(drive->command){
+	case Cws:
+	case Cwsm:
+		microdelay(1);
+		as = ataready(cmdport, ctlport, 0, Bsy, Drq|Err, 1000);
+		if(as < 0 || (as & Err)){
+			iunlock(ctlr);
+			return -1;
+		}
+		len = drive->block;
+		if(drive->data+len > drive->limit)
+			len = drive->limit-drive->data;
+		outss(cmdport+Data, drive->data, len/2);
+		break;
+
+	case Crd:
+	case Cwd:
+	//	atadmastart(ctlr, drive->write);
+		break;
+	}
+	iunlock(ctlr);
+
+	return 0;
+}
+
+static int
+atagenioretry(Drive* drive)
+{
+	return atasetsense(drive, SDcheck, 4, 8, drive->error);
+}
+
+static int
+atagenio(Drive* drive, uchar* cmd, int)
+{
+	uchar *p;
+	Ctlr *ctlr;
+	int count, max;
+	vlong lba, len;
+
+	/*
+	 * Map SCSI commands into ATA commands for discs.
+	 * Fail any command with a LUN except INQUIRY which
+	 * will return 'logical unit not supported'.
+	 */
+	if((cmd[1]>>5) && cmd[0] != 0x12)
+		return atasetsense(drive, SDcheck, 0x05, 0x25, 0);
+
+	switch(cmd[0]){
+	default:
+		return atasetsense(drive, SDcheck, 0x05, 0x20, 0);
+
+	case 0x00:			/* test unit ready */
+		return SDok;
+
+	case 0x03:			/* request sense */
+		if(cmd[4] < sizeof(drive->sense))
+			len = cmd[4];
+		else
+			len = sizeof(drive->sense);
+		if(drive->data && drive->dlen >= len){
+			memmove(drive->data, drive->sense, len);
+			drive->data += len;
+		}
+		return SDok;
+
+	case 0x12:			/* inquiry */
+		if(cmd[4] < sizeof(drive->inquiry))
+			len = cmd[4];
+		else
+			len = sizeof(drive->inquiry);
+		if(drive->data && drive->dlen >= len){
+			memmove(drive->data, drive->inquiry, len);
+			drive->data += len;
+		}
+		return SDok;
+
+	case 0x1B:			/* start/stop unit */
+		/*
+		 * NOP for now, can use the power management feature
+		 * set later.
+		 */
+		return SDok;
+
+	case 0x25:			/* read capacity */
+		if((cmd[1] & 0x01) || cmd[2] || cmd[3])
+			return atasetsense(drive, SDcheck, 0x05, 0x24, 0);
+		if(drive->data == nil || drive->dlen < 8)
+			return atasetsense(drive, SDcheck, 0x05, 0x20, 1);
+		/*
+		 * Read capacity returns the LBA of the last sector.
+		 */
+		len = drive->sectors-1;
+		p = drive->data;
+		*p++ = len>>24;
+		*p++ = len>>16;
+		*p++ = len>>8;
+		*p++ = len;
+		len = drive->secsize;
+		*p++ = len>>24;
+		*p++ = len>>16;
+		*p++ = len>>8;
+		*p = len;
+		drive->data += 8;
+		return SDok;
+
+	case 0x9E:			/* long read capacity */
+		if((cmd[1] & 0x01) || cmd[2] || cmd[3])
+			return atasetsense(drive, SDcheck, 0x05, 0x24, 0);
+		if(drive->data == nil || drive->dlen < 8)
+			return atasetsense(drive, SDcheck, 0x05, 0x20, 1);
+		/*
+		 * Read capacity returns the LBA of the last sector.
+		 */
+		len = drive->sectors-1;
+		p = drive->data;
+		*p++ = len>>56;
+		*p++ = len>>48;
+		*p++ = len>>40;
+		*p++ = len>>32;
+		*p++ = len>>24;
+		*p++ = len>>16;
+		*p++ = len>>8;
+		*p++ = len;
+		len = drive->secsize;
+		*p++ = len>>24;
+		*p++ = len>>16;
+		*p++ = len>>8;
+		*p = len;
+		drive->data += 8;
+		return SDok;
+
+	case 0x28:			/* read */
+	case 0x2A:			/* write */
+		break;
+
+	case 0x5A:
+		return atamodesense(drive, cmd);
+	}
+
+	ctlr = drive->ctlr;
+	lba = (cmd[2]<<24)|(cmd[3]<<16)|(cmd[4]<<8)|cmd[5];
+	count = (cmd[7]<<8)|cmd[8];
+	if(drive->data == nil)
+		return SDok;
+	if(drive->dlen < count*drive->secsize)
+		count = drive->dlen/drive->secsize;
+	qlock(ctlr);
+	while(count){
+		max = (drive->flags&Lba48) ? 65536 : 256;
+		if(count > max)
+			drive->count = max;
+		else
+			drive->count = count;
+		if(atageniostart(drive, lba)){
+			ilock(ctlr);
+			atanop(drive, 0);
+			iunlock(ctlr);
+			qunlock(ctlr);
+			return atagenioretry(drive);
+		}
+
+		tsleep(ctlr, atageniodone, ctlr, 10*1000);
+		if(!ctlr->done){
+			/*
+			 * What should the above timeout be? In
+			 * standby and sleep modes it could take as
+			 * long as 30 seconds for a drive to respond.
+			 * Very hard to get out of this cleanly.
+			 */
+		//	atadumpstate(drive, cmd, lba, count);
+			ataabort(drive, 1);
+			return atagenioretry(drive);
+		}
+
+		if(drive->status & Err){
+			qunlock(ctlr);
+			return atasetsense(drive, SDcheck, 4, 8, drive->error);
+		}
+		count -= drive->count;
+		lba += drive->count;
+	}
+	qunlock(ctlr);
+
+	return SDok;
+}
+
+static int
+atario(SDreq* r)
+{
+	Ctlr *ctlr;
+	Drive *drive;
+	SDunit *unit;
+	uchar cmd10[10], *cmdp, *p;
+	int clen, reqstatus, status;
+
+	unit = r->unit;
+	if((ctlr = unit->dev->ctlr) == nil || ctlr->drive[unit->subno] == nil){
+		r->status = SDtimeout;
+		return SDtimeout;
+	}
+	drive = ctlr->drive[unit->subno];
+
+	/*
+	 * Most SCSI commands can be passed unchanged except for
+	 * the padding on the end. The few which require munging
+	 * are not used internally. Mode select/sense(6) could be
+	 * converted to the 10-byte form but it's not worth the
+	 * effort. Read/write(6) are easy.
+	 */
+	switch(r->cmd[0]){
+	case 0x08:			/* read */
+	case 0x0A:			/* write */
+		cmdp = cmd10;
+		memset(cmdp, 0, sizeof(cmd10));
+		cmdp[0] = r->cmd[0]|0x20;
+		cmdp[1] = r->cmd[1] & 0xE0;
+		cmdp[5] = r->cmd[3];
+		cmdp[4] = r->cmd[2];
+		cmdp[3] = r->cmd[1] & 0x0F;
+		cmdp[8] = r->cmd[4];
+		clen = sizeof(cmd10);
+		break;
+
+	default:
+		cmdp = r->cmd;
+		clen = r->clen;
+		break;
+	}
+
+	qlock(drive);
+	drive->write = r->write;
+	drive->data = r->data;
+	drive->dlen = r->dlen;
+
+	drive->status = 0;
+	drive->error = 0;
+	if(drive->pkt)
+		status = atapktio(drive, cmdp, clen);
+	else
+		status = atagenio(drive, cmdp, clen);
+	if(status == SDok){
+		atasetsense(drive, SDok, 0, 0, 0);
+		if(drive->data){
+			p = r->data;
+			r->rlen = drive->data - p;
+		}
+		else
+			r->rlen = 0;
+	}
+	else if(status == SDcheck && !(r->flags & SDnosense)){
+		drive->write = 0;
+		memset(cmd10, 0, sizeof(cmd10));
+		cmd10[0] = 0x03;
+		cmd10[1] = r->lun<<5;
+		cmd10[4] = sizeof(r->sense)-1;
+		drive->data = r->sense;
+		drive->dlen = sizeof(r->sense)-1;
+		drive->status = 0;
+		drive->error = 0;
+		if(drive->pkt)
+			reqstatus = atapktio(drive, cmd10, 6);
+		else
+			reqstatus = atagenio(drive, cmd10, 6);
+		if(reqstatus == SDok){
+			r->flags |= SDvalidsense;
+			atasetsense(drive, SDok, 0, 0, 0);
+		}
+	}
+	qunlock(drive);
+	r->status = status;
+	if(status != SDok)
+		return status;
+
+	/*
+	 * Fix up any results.
+	 * Many ATAPI CD-ROMs ignore the LUN field completely and
+	 * return valid INQUIRY data. Patch the response to indicate
+	 * 'logical unit not supported' if the LUN is non-zero.
+	 */
+	switch(cmdp[0]){
+	case 0x12:			/* inquiry */
+		if((p = r->data) == nil)
+			break;
+		if((cmdp[1]>>5) && (!drive->pkt || (p[0] & 0x1F) == 0x05))
+			p[0] = 0x7F;
+		/*FALLTHROUGH*/
+	default:
+		break;
+	}
+
+	return SDok;
+}
+
+static void
+atainterrupt(Ureg*, void* arg)
+{
+	Ctlr *ctlr;
+	Drive *drive;
+	int cmdport, len, status;
+
+	ctlr = arg;
+
+	ilock(ctlr);
+	if(inb(ctlr->ctlport+As) & Bsy){
+		iunlock(ctlr);
+		if(DEBUG & DbgBsy)
+			print("IBsy+");
+		return;
+	}
+	cmdport = ctlr->cmdport;
+	status = inb(cmdport+Status);
+	if((drive = ctlr->curdrive) == nil){
+		iunlock(ctlr);
+		if((DEBUG & DbgINL) && ctlr->command != Cedd)
+			print("Inil%2.2uX+", ctlr->command);
+		return;
+	}
+
+	if(status & Err)
+		drive->error = inb(cmdport+Error);
+	else switch(drive->command){
+	default:
+		drive->error = Abrt;
+		break;
+
+	case Crs:
+	case Crsm:
+		if(!(status & Drq)){
+			drive->error = Abrt;
+			break;
+		}
+		len = drive->block;
+		if(drive->data+len > drive->limit)
+			len = drive->limit-drive->data;
+		inss(cmdport+Data, drive->data, len/2);
+		drive->data += len;
+		if(drive->data >= drive->limit)
+			ctlr->done = 1;
+		break;
+
+	case Cws:
+	case Cwsm:
+		len = drive->block;
+		if(drive->data+len > drive->limit)
+			len = drive->limit-drive->data;
+		drive->data += len;
+		if(drive->data >= drive->limit){
+			ctlr->done = 1;
+			break;
+		}
+		if(!(status & Drq)){
+			drive->error = Abrt;
+			break;
+		}
+		len = drive->block;
+		if(drive->data+len > drive->limit)
+			len = drive->limit-drive->data;
+		outss(cmdport+Data, drive->data, len/2);
+		break;
+
+	case Cpkt:
+		atapktinterrupt(drive);
+		break;
+
+	case Crd:
+	case Cwd:
+	//	atadmainterrupt(drive, drive->count*drive->secsize);
+		break;
+	}
+	iunlock(ctlr);
+
+	if(drive->error){
+		status |= Err;
+		ctlr->done = 1;
+	}
+
+	if(ctlr->done){
+		ctlr->curdrive = nil;
+		drive->status = status;
+		wakeup(ctlr);
+	}
+}
+
+static SDev*
+atapnp(void)
+{
+	Ctlr *ctlr;
+	Pcidev *p;
+	int channel, ispc87415, pi, r;
+	SDev *legacy[2], *sdev, *head, *tail;
+
+	legacy[0] = legacy[1] = head = tail = nil;
+	if(sdev = ataprobe(0x1F0, 0x3F4, IrqATA0)){
+		head = tail = sdev;
+		legacy[0] = sdev;
+	}
+	if(sdev = ataprobe(0x170, 0x374, IrqATA1)){
+		if(head != nil)
+			tail->next = sdev;
+		else
+			head = sdev;
+		tail = sdev;
+		legacy[1] = sdev;
+	}
+
+	p = nil;
+	while(p = pcimatch(p, 0, 0)){
+		/*
+		 * Look for devices with the correct class and sub-class
+		 * code and known device and vendor ID; add native-mode
+		 * channels to the list to be probed, save info for the
+		 * compatibility mode channels.
+		 * Note that the legacy devices should not be considered
+		 * PCI devices by the interrupt controller.
+		 * For both native and legacy, save info for busmastering
+		 * if capable.
+		 * Promise Ultra ATA/66 (PDC20262) appears to
+		 * 1) give a sub-class of 'other mass storage controller'
+		 *    instead of 'IDE controller', regardless of whether it's
+		 *    the only controller or not;
+		 * 2) put 0 in the programming interface byte (probably
+		 *    as a consequence of 1) above).
+		 * Sub-class code 0x04 is 'RAID controller', e.g. VIA VT8237.
+		 */
+		if(p->ccrb != 0x01)
+			continue;
+		if(p->ccru != 0x01 && p->ccru != 0x04 && p->ccru != 0x80)
+			continue;
+		pi = p->ccrp;
+		ispc87415 = 0;
+
+		switch((p->did<<16)|p->vid){
+		default:
+			continue;
+
+		case (0x0002<<16)|0x100B:	/* NS PC87415 */
+			/*
+			 * Disable interrupts on both channels until
+			 * after they are probed for drives.
+			 * This must be called before interrupts are
+			 * enabled because the IRQ may be shared.
+			 */
+			ispc87415 = 1;
+			pcicfgw32(p, 0x40, 0x00000300);
+			break;
+		case (0x1000<<16)|0x1042:	/* PC-Tech RZ1000 */
+			/*
+			 * Turn off prefetch. Overkill, but cheap.
+			 */
+			r = pcicfgr32(p, 0x40);
+			r &= ~0x2000;
+			pcicfgw32(p, 0x40, r);
+			break;
+		case (0x4D38<<16)|0x105A:	/* Promise PDC20262 */
+		case (0x4D30<<16)|0x105A:	/* Promise PDC202xx */
+		case (0x4D68<<16)|0x105A:	/* Promise PDC20268 */
+		case (0x4D69<<16)|0x105A:	/* Promise Ultra/133 TX2 */
+		case (0x3373<<16)|0x105A:	/* Promise 20378 RAID */
+		case (0x3149<<16)|0x1106:	/* VIA VT8237 SATA/RAID */
+		case (0x4379<<16)|0x1002:	/* ATI 4379 SATA*/
+		case (0x3112<<16)|0x1095:	/* SiL 3112 SATA (DMA busted?) */
+		case (0x3114<<16)|0x1095:	/* SiL 3114 SATA/RAID */
+			pi = 0x85;
+			break;
+		case (0x0004<<16)|0x1103:	/* HighPoint HPT-370 */
+			pi = 0x85;
+			/*
+			 * Turn off fast interrupt prediction.
+			 */
+			if((r = pcicfgr8(p, 0x51)) & 0x80)
+				pcicfgw8(p, 0x51, r & ~0x80);
+			if((r = pcicfgr8(p, 0x55)) & 0x80)
+				pcicfgw8(p, 0x55, r & ~0x80);
+			break;
+		case (0x0640<<16)|0x1095:	/* CMD 640B */
+			/*
+			 * Bugfix code here...
+			 */
+			break;
+		case (0x7441<<16)|0x1022:	/* AMD 768 */
+			/*
+			 * Set:
+			 *	0x41	prefetch, postwrite;
+			 *	0x43	FIFO configuration 1/2 and 1/2;
+			 *	0x44	status register read retry;
+			 *	0x46	DMA read and end of sector flush.
+			 */
+			r = pcicfgr8(p, 0x41);
+			pcicfgw8(p, 0x41, r|0xF0);
+			r = pcicfgr8(p, 0x43);
+			pcicfgw8(p, 0x43, (r & 0x90)|0x2A);
+			r = pcicfgr8(p, 0x44);
+			pcicfgw8(p, 0x44, r|0x08);
+			r = pcicfgr8(p, 0x46);
+			pcicfgw8(p, 0x46, (r & 0x0C)|0xF0);
+			/*FALLTHROUGH*/
+		case (0x7469<<16)|0x1022:	/* AMD 3111 */
+			/*
+			 * This can probably be lumped in with the 768 above.
+			 */
+			/*FALLTHROUGH*/
+		case (0x209A<<16)|0x1022:	/* AMD CS5536 */
+		case (0x01BC<<16)|0x10DE:	/* nVidia nForce1 */
+		case (0x0065<<16)|0x10DE:	/* nVidia nForce2 */
+		case (0x0085<<16)|0x10DE:	/* nVidia nForce2 MCP */
+		case (0x00D5<<16)|0x10DE:	/* nVidia nForce3 */
+		case (0x00E5<<16)|0x10DE:	/* nVidia nForce3 Pro */
+		case (0x0035<<16)|0x10DE:	/* nVidia nForce3 MCP */
+		case (0x0053<<16)|0x10DE:	/* nVidia nForce4 */
+		case (0x0054<<16)|0x10DE:	/* nVidia nForce4 SATA */
+		case (0x0055<<16)|0x10DE:	/* nVidia nForce4 SATA */
+			/*
+			 * Ditto, although it may have a different base
+			 * address for the registers (0x50?).
+			 */
+			break;
+		case (0x0646<<16)|0x1095:	/* CMD 646 */
+		case (0x0571<<16)|0x1106:	/* VIA 82C686 */
+		case (0x0211<<16)|0x1166:	/* ServerWorks IB6566 */
+		case (0x1230<<16)|0x8086:	/* 82371FB (PIIX) */
+		case (0x7010<<16)|0x8086:	/* 82371SB (PIIX3) */
+		case (0x7111<<16)|0x8086:	/* 82371[AE]B (PIIX4[E]) */
+		case (0x2411<<16)|0x8086:	/* 82801AA (ICH) */
+		case (0x2421<<16)|0x8086:	/* 82801AB (ICH0) */
+		case (0x244A<<16)|0x8086:	/* 82801BA (ICH2, Mobile) */
+		case (0x244B<<16)|0x8086:	/* 82801BA (ICH2, High-End) */
+		case (0x248A<<16)|0x8086:	/* 82801CA (ICH3, Mobile) */
+		case (0x248B<<16)|0x8086:	/* 82801CA (ICH3, High-End) */
+		case (0x24CA<<16)|0x8086:	/* 82801DBM (ICH4, Mobile) */
+		case (0x24CB<<16)|0x8086:	/* 82801DB (ICH4, High-End) */
+		case (0x24DB<<16)|0x8086:	/* 82801EB (ICH5) */
+		case (0x266F<<16)|0x8086:	/* 82801FB (ICH6) */
+		case (0x27C4<<16)|0x8086:	/* 82801GBM SATA (ICH7) */
+		case (0x27C5<<16)|0x8086:	/* 82801GBM SATA AHCI (ICH7) */
+			break;
+		}
+
+		for(channel = 0; channel < 2; channel++){
+			if(pi & (1<<(2*channel))){
+				sdev = ataprobe(p->mem[0+2*channel].bar & ~0x01,
+						p->mem[1+2*channel].bar & ~0x01,
+						p->intl);
+				if(sdev == nil)
+					continue;
+
+				ctlr = sdev->ctlr;
+				if(ispc87415)
+					ctlr->ienable = pc87415ienable;
+
+				if(head != nil)
+					tail->next = sdev;
+				else
+					head = sdev;
+				tail = sdev;
+				ctlr->tbdf = p->tbdf;
+			}
+			else if((sdev = legacy[channel]) == nil)
+				continue;
+			else
+				ctlr = sdev->ctlr;
+
+			ctlr->pcidev = p;
+		}
+	}
+
+	return head;
+}
+
+static SDev*
+atalegacy(int port, int irq)
+{
+	return ataprobe(port, port+0x204, irq);
+}
+
+static SDev*
+ataid(SDev* sdev)
+{
+	int i;
+	Ctlr *ctlr;
+
+	/*
+	 * Legacy controllers are always 'C' and 'D' and if
+	 * they exist and have drives will be first in the list.
+	 * If there are no active legacy controllers, native
+	 * controllers start at 'C'.
+	 */
+	if(sdev == nil)
+		return nil;
+	ctlr = sdev->ctlr;
+	if(ctlr->cmdport == 0x1F0 || ctlr->cmdport == 0x170)
+		i = 2;
+	else
+		i = 0;
+	while(sdev){
+		if(sdev->ifc == &sdataifc){
+			ctlr = sdev->ctlr;
+			if(ctlr->cmdport == 0x1F0)
+				sdev->idno = 'C';
+			else if(ctlr->cmdport == 0x170)
+				sdev->idno = 'D';
+			else{
+				sdev->idno = 'C'+i;
+				i++;
+			}
+		//	snprint(sdev->name, NAMELEN, "sd%c", sdev->idno);
+		}
+		sdev = sdev->next;
+	}
+
+	return nil;
+}
+
+static int
+ataenable(SDev* sdev)
+{
+	Ctlr *ctlr;
+
+	ctlr = sdev->ctlr;
+
+	setvec(ctlr->irq+VectorPIC, atainterrupt, ctlr);
+	outb(ctlr->ctlport+Dc, 0);
+	if(ctlr->ienable)
+		ctlr->ienable(ctlr);
+
+	return 1;
+}
+
+SDifc sdataifc = {
+	"ata",				/* name */
+
+	atapnp,				/* pnp */
+	atalegacy,			/* legacy */
+	ataid,				/* id */
+	ataenable,			/* enable */
+	nil,				/* disable */
+
+	scsiverify,			/* verify */
+	scsionline,			/* online */
+	atario,				/* rio */
+	nil,			/* rctl */
+	nil,			/* wctl */
+
+	scsibio,			/* bio */
+};
--- /dev/null
+++ b/os/boot/pc/sdbios.c
@@ -1,0 +1,165 @@
+/*
+ * boot driver for BIOS devices with partitions
+ * devbios must be called first
+ */
+#include "u.h"
+#include "lib.h"
+#include "mem.h"
+#include "dat.h"
+#include "fns.h"
+#include "io.h"
+#include "ureg.h"
+#include "error.h"
+
+#include "sd.h"
+#include "fs.h"
+
+long	biosread(Fs *, void *, long);
+vlong	biosseek(Fs *fs, vlong off);
+
+extern SDifc sdbiosifc;
+extern int onlybios0, biosinited;
+
+int
+biosverify(SDunit* )
+{
+	if (onlybios0 || !biosinited)
+		return 0;
+	return 1;
+}
+
+int
+biosonline(SDunit* unit)
+{
+	if (onlybios0 || !biosinited || !unit)
+		return 0;
+	unit->sectors = 1UL << 30;	/* a bunch */
+	unit->secsize = 512;		/* conventional */
+	return 1;
+}
+
+static int
+biosrio(SDreq* r)
+{
+	int nb;
+	long got;
+	vlong len, off;
+	uchar *p;
+	Fs fs;			/* just for fs->dev, which is zero */
+
+	if (onlybios0 || !biosinited)
+		return SDeio;
+	/*
+	 * Most SCSI commands can be passed unchanged except for
+	 * the padding on the end. The few which require munging
+	 * are not used internally. Mode select/sense(6) could be
+	 * converted to the 10-byte form but it's not worth the
+	 * effort. Read/write(6) are easy.
+	 */
+	r->rlen = 0;
+	r->status = SDok;
+	switch(r->cmd[0]){
+	case 0x08:			/* read */
+	case 0x28:			/* read */
+		if (r->cmd[0] == 0x08)
+			panic("biosrio: 0x08 read op\n");
+		off = r->cmd[2]<<24 | r->cmd[3]<<16 | r->cmd[4]<<8 | r->cmd[5];
+		nb = r->cmd[7]<<8 | r->cmd[8];	/* often 4 */
+		USED(nb);		/* is nb*512 == r->dlen? */
+		memset(&fs, 0, sizeof fs);
+		biosseek(&fs, off*512);
+		got = biosread(&fs, r->data, r->dlen);
+		if (got < 0)
+			r->status = SDeio;
+		else
+			r->rlen = got;
+		break;
+	case 0x0A:			/* write */
+	case 0x2A:			/* write */
+		r->status = SDeio;	/* boot programs don't write */
+		break;
+	case 0x25:			/* read capacity */
+		/*
+		 * Read capacity returns the LBA of the last sector.
+		 */
+		len = r->unit->sectors - 1;
+		p = r->data;
+		*p++ = len>>24;
+		*p++ = len>>16;
+		*p++ = len>>8;
+		*p++ = len;
+		len = r->unit->secsize;
+		*p++ = len>>24;
+		*p++ = len>>16;
+		*p++ = len>>8;
+		*p = len;
+		r->data = (char *)r->data + 8;
+		return SDok;
+	case 0x9E:			/* long read capacity */
+		/*
+		 * Read capacity returns the LBA of the last sector.
+		 */
+		len = r->unit->sectors - 1;
+		p = r->data;
+		*p++ = len>>56;
+		*p++ = len>>48;
+		*p++ = len>>40;
+		*p++ = len>>32;
+		*p++ = len>>24;
+		*p++ = len>>16;
+		*p++ = len>>8;
+		*p++ = len;
+		len = r->unit->secsize;
+		*p++ = len>>24;
+		*p++ = len>>16;
+		*p++ = len>>8;
+		*p = len;
+		r->data = (char *)r->data + 8;
+		return SDok;
+	/* ignore others */
+	}
+	return r->status;
+}
+
+SDev*
+biosid(SDev* sdev)
+{
+	for (; sdev; sdev = sdev->next)
+		if (sdev->ifc == &sdbiosifc)
+			sdev->idno = 'B';
+	return sdev;
+}
+
+static SDev*
+biospnp(void)
+{
+	SDev *sdev;
+
+	/* 9pxeload can't use bios int 13 calls; they wedge the machine */
+	if (pxe || getconf("*nobiosload") != nil || onlybios0 || !biosinited)
+		return nil;
+	if((sdev = malloc(sizeof(SDev))) != nil) {
+		sdev->ifc = &sdbiosifc;
+		sdev->index = -1;
+		sdev->nunit = 1;
+	}
+	return sdev;
+}
+
+SDifc sdbiosifc = {
+	"bios",				/* name */
+
+	biospnp,			/* pnp */
+	nil,				/* legacy */
+	biosid,				/* id */
+	nil,				/* enable */
+	nil,				/* disable */
+
+	biosverify,			/* verify */
+	biosonline,			/* online */
+	biosrio,			/* rio */
+	nil,				/* rctl */
+	nil,				/* wctl */
+
+	scsibio,			/* bio */
+};
--- /dev/null
+++ b/os/boot/pc/sdiahci.c
@@ -1,0 +1,1668 @@
+/*
+ * intel/amd ahci (advanced host controller interface) sata controller
+ * bootstrap driver
+ * copyright © 2007, 2008 coraid, inc.
+ */
+
+#include "u.h"
+#include "lib.h"
+#include "mem.h"
+#include "dat.h"
+#include "fns.h"
+#include "io.h"
+#include "error.h"
+#include "sd.h"
+#include "ahci.h"
+
+#define	dprint	if(debug==0){}else print
+#define	idprint	if(prid==0){}else print
+#define	aprint	if(datapi==0){}else print
+
+enum {
+	NCtlr	= 2,
+	NCtlrdrv= 8,
+	NDrive	= NCtlr*NCtlrdrv,
+
+	Read	= 0,
+	Write
+};
+
+/* pci space configurtion */
+enum {
+	Pmap	= 0x90,
+	Ppcs	= 0x91,
+	Prev	= 0xa8,
+};
+
+enum {
+	Tesb,
+	Tich,
+	Tsb600,
+};
+
+#define Intel(x)	((x) == Tesb  || (x) == Tich)
+
+static char *tname[] = {
+	"63xxesb",
+	"ich",
+	"sb600",
+};
+
+enum {
+	Dnull,
+	Dmissing,
+	Dnew,
+	Dready,
+	Derror,
+	Dreset,
+	Doffline,
+	Dportreset,
+	Dlast
+};
+
+static char *diskstates[Dlast] = {
+	"null",
+	"missing",
+	"new",
+	"ready",
+	"error",
+	"reset",
+	"offline",
+	"portreset",
+};
+
+extern SDifc sdiahciifc;
+typedef struct Ctlr Ctlr;
+
+enum {
+	DMautoneg,
+	DMsatai,
+	DMsataii,
+};
+
+static char *modename[] = {
+	"auto",
+	"satai",
+	"sataii",
+};
+
+typedef struct {
+	Lock;
+
+	Ctlr	*ctlr;
+	SDunit	*unit;
+	char	name[10];
+	Aport	*port;
+	Aportm	portm;
+	Aportc	portc;		/* redundant ptr to port and portm. */
+
+	uchar	mediachange;
+	uchar	state;
+	uchar	smartrs;
+
+	uvlong	sectors;
+	ulong	intick;
+	int	wait;
+	uchar	mode;		/* DMautoneg, satai or sataii. */
+	uchar	active;
+
+	char	serial[20+1];
+	char	firmware[8+1];
+	char	model[40+1];
+
+	ushort	info[0x200];
+
+	int	driveno;	/* ctlr*NCtlrdrv + unit */
+	int	portno;	/* ctlr port # != drive # when not all ports enabled. */
+} Drive;
+
+struct Ctlr {
+	Lock;
+
+	int	type;
+	int	enabled;
+	SDev	*sdev;
+	Pcidev	*pci;
+
+	uchar	*mmio;
+	ulong	*lmmio;
+	Ahba	*hba;
+
+	Drive	rawdrive[NCtlrdrv];
+	Drive*	drive[NCtlrdrv];
+	int	ndrive;
+};
+
+static	Ctlr	iactlr[NCtlr];
+static	SDev	sdevs[NCtlr];
+static	int	niactlr;
+
+static	int	prid = 0;
+static	int	datapi = 0;
+
+static char stab[] = {
+[0]	'i', 'm',
+[8]	't', 'c', 'p', 'e',
+[16]	'N', 'I', 'W', 'B', 'D', 'C', 'H', 'S', 'T', 'F', 'X'
+};
+
+static void
+serrstr(ulong r, char *s, char *e)
+{
+	int i;
+
+	e -= 3;
+	for(i = 0; i < nelem(stab) && s < e; i++)
+		if((r & (1<<i)) && stab[i]){
+			*s++ = stab[i];
+			if(SerrBad & (1<<i))
+				*s++ = '*';
+		}
+	*s = 0;
+}
+
+static char ntab[] = "0123456789abcdef";
+
+static void
+preg(uchar *reg, int n)
+{
+	int i;
+	char buf[25*3+1], *e;
+
+	e = buf;
+	for(i = 0; i < n; i++){
+		*e++ = ntab[reg[i] >> 4];
+		*e++ = ntab[reg[i] & 0xf];
+		*e++ = ' ';
+	}
+	*e++ = '\n';
+	*e = 0;
+	dprint(buf);
+}
+
+static void
+dreg(char *s, Aport *p)
+{
+	dprint("%stask=%lux; cmd=%lux; ci=%lux; is=%lux\n",
+		s, p->task, p->cmd, p->ci, p->isr);
+}
+
+static void
+esleep(int ms)
+{
+	delay(ms);
+}
+
+typedef struct {
+	Aport	*p;
+	int	i;
+} Asleep;
+
+static int
+ahciclear(void *v)
+{
+	Asleep *s;
+
+	s = v;
+	return (s->p->ci & s->i) == 0;
+}
+
+static void
+aesleep(Aportm *, Asleep *a, int ms)
+{
+	ulong start;
+
+	start = m->ticks;
+	while((a->p->ci & a->i) != 0)
+		if(TK2MS(m->ticks-start) >= ms)
+			break;
+}
+
+static int
+ahciwait(Aportc *c, int ms)
+{
+	Aport *p;
+	Asleep as;
+
+	p = c->p;
+	p->ci = 1;
+	as.p = p;
+	as.i = 1;
+	aesleep(c->m, &as, ms);
+	if((p->task & 1) == 0 && p->ci == 0)
+		return 0;
+	dreg("ahciwait timeout ", c->p);
+	return -1;
+}
+
+static int
+setfeatures(Aportc *pc, uchar f)
+{
+	uchar *c;
+	Actab *t;
+	Alist *l;
+
+	t = pc->m->ctab;
+	c = t->cfis;
+
+	memset(c, 0, 0x20);
+	c[0] = 0x27;
+	c[1] = 0x80;
+	c[2] = 0xef;
+	c[3] = f;
+	c[7] = 0xa0;		/* obsolete device bits */
+
+	l = pc->m->list;
+	l->flags = Lwrite|0x5;
+	l->len = 0;
+	l->ctab = PCIWADDR(t);
+	l->ctabhi = 0;
+
+	return ahciwait(pc, 3*1000);
+}
+
+static int
+setudmamode(Aportc *pc, uchar f)
+{
+	uchar *c;
+	Actab *t;
+	Alist *l;
+
+	t = pc->m->ctab;
+	c = t->cfis;
+
+	memset(c, 0, 0x20);
+	c[0] = 0x27;
+	c[1] = 0x80;
+	c[2] = 0xef;
+	c[3] = 3;		/* set transfer mode */
+	c[7] = 0xa0;		/* obsolete device bits */
+	c[12] = 0x40 | f;	/* sector count */
+
+	l = pc->m->list;
+	l->flags = Lwrite | 0x5;
+	l->len = 0;
+	l->ctab = PCIWADDR(t);
+	l->ctabhi = 0;
+
+	return ahciwait(pc, 3*1000);
+}
+
+static void
+asleep(int ms)
+{
+	delay(ms);
+}
+
+static int
+ahciportreset(Aportc *c)
+{
+	ulong *cmd, i;
+	Aport *p;
+
+	p = c->p;
+	cmd = &p->cmd;
+	*cmd &= ~(Afre|Ast);
+	for(i = 0; i < 500; i += 25){
+		if((*cmd & Acr) == 0)
+			break;
+		asleep(25);
+	}
+	p->sctl = 1 | (p->sctl & ~7);
+	delay(1);
+	p->sctl &= ~7;
+	return 0;
+}
+
+static ushort
+gbit16(void *a)
+{
+	uchar *i;
+
+	i = a;
+	return i[1]<<8 | i[0];
+}
+
+static ulong
+gbit32(void *a)
+{
+	ulong j;
+	uchar *i;
+
+	i = a;
+	j  = i[3] << 24;
+	j |= i[2] << 16;
+	j |= i[1] << 8;
+	j |= i[0];
+	return j;
+}
+
+static uvlong
+gbit64(void *a)
+{
+	uchar *i;
+
+	i = a;
+	return (uvlong) gbit32(i+4)<<32 | gbit32(a);
+}
+
+static int
+ahciidentify0(Aportc *pc, void *id, int atapi)
+{
+	uchar *c;
+	Actab *t;
+	Alist *l;
+	Aprdt *p;
+	static uchar tab[] = { 0xec, 0xa1 };
+
+	t = pc->m->ctab;
+	c = t->cfis;
+
+	memset(c, 0, 0x20);
+	c[0] = 0x27;
+	c[1] = 0x80;
+	c[2] = tab[atapi];
+	c[7] = 0xa0;		/* obsolete device bits */
+
+	l = pc->m->list;
+	l->flags = 1<<16 | 0x5;
+	l->len = 0;
+	l->ctab = PCIWADDR(t);
+	l->ctabhi = 0;
+
+	memset(id, 0, 0x100);
+	p = &t->prdt;
+	p->dba = PCIWADDR(id);
+	p->dbahi = 0;
+	p->count = 1<<31 | (0x200-2) | 1;
+
+	return ahciwait(pc, 3*1000);
+}
+
+static vlong
+ahciidentify(Aportc *pc, ushort *id)
+{
+	int i, sig;
+	vlong s;
+	Aportm *m;
+
+	m = pc->m;
+	m->feat = 0;
+	m->smart = 0;
+	i = 0;
+	sig = pc->p->sig >> 16;
+	if(sig == 0xeb14){
+		m->feat |= Datapi;
+		i = 1;
+	}
+	if(ahciidentify0(pc, id, i) == -1)
+		return -1;
+
+	i = gbit16(id+83) | gbit16(id+86);
+	if(i & (1<<10)){
+		m->feat |= Dllba;
+		s = gbit64(id+100);
+	}else
+		s = gbit32(id+60);
+
+	if(m->feat & Datapi){
+		i = gbit16(id+0);
+		if(i & 1)
+			m->feat |= Datapi16;
+	}
+
+	i = gbit16(id+83);
+	if((i>>14) != 1)
+		return s;
+	if(i & (1<<3))
+		m->feat |= Dpower;
+	i = gbit16(id+82);
+	if(i & 1)
+		m->feat |= Dsmart;
+	if(i & (1<<14))
+		m->feat |= Dnop;
+	return s;
+}
+
+static int
+ahciquiet(Aport *a)
+{
+	ulong *p, i;
+
+	p = &a->cmd;
+	*p &= ~Ast;
+	for(i = 0; i < 500; i += 50){
+		if((*p & Acr) == 0)
+			goto stop;
+		asleep(50);
+	}
+	return -1;
+stop:
+	if((a->task & (ASdrq|ASbsy)) == 0){
+		*p |= Ast;
+		return 0;
+	}
+
+	*p |= Aclo;
+	for(i = 0; i < 500; i += 50){
+		if((*p & Aclo) == 0)
+			goto stop1;
+		asleep(50);
+	}
+	return -1;
+stop1:
+	/* extra check */
+	dprint("clo clear %lx\n", a->task);
+	if(a->task & ASbsy)
+		return -1;
+	*p |= Ast;
+	return 0;
+}
+
+static int
+ahciidle(Aport *port)
+{
+	ulong *p, i, r;
+
+	p = &port->cmd;
+	if((*p & Arun) == 0)
+		return 0;
+	*p &= ~Ast;
+	r = 0;
+	for(i = 0; i < 500; i += 25){
+		if((*p & Acr) == 0)
+			goto stop;
+		asleep(25);
+	}
+	r = -1;
+stop:
+	if((*p & Afre) == 0)
+		return r;
+	*p &= ~Afre;
+	for(i = 0; i < 500; i += 25){
+		if((*p & Afre) == 0)
+			return 0;
+		asleep(25);
+	}
+	return -1;
+}
+
+/*
+ * §6.2.2.1  first part; comreset handled by reset disk.
+ *	- remainder is handled by configdisk.
+ *	- ahcirecover is a quick recovery from a failed command.
+ */
+int
+ahciswreset(Aportc *pc)
+{
+	int i;
+
+	i = ahciidle(pc->p);
+	pc->p->cmd |= Afre;
+	if(i == -1)
+		return -1;
+	if(pc->p->task & (ASdrq|ASbsy))
+		return -1;
+	return 0;
+}
+
+int
+ahcirecover(Aportc *pc)
+{
+	ahciswreset(pc);
+	pc->p->cmd |= Ast;
+	if(setudmamode(pc, 5) == -1)
+		return -1;
+	return 0;
+}
+
+static void*
+malign(int size, int align)
+{
+	void *v;
+
+	v = xspanalloc(size, align, 0);
+	memset(v, 0, size);
+	return v;
+}
+
+static void
+setupfis(Afis *f)
+{
+	f->base = malign(0x100, 0x100);
+	f->d = f->base + 0;
+	f->p = f->base + 0x20;
+	f->r = f->base + 0x40;
+	f->u = f->base + 0x60;
+	f->devicebits = (ulong*)(f->base + 0x58);
+}
+
+static void
+ahciwakeup(Aport *p)
+{
+	ushort s;
+
+	s = p->sstatus;
+	if((s & 0x700) != 0x600)
+		return;
+	if((s & 7) != 1){
+		print("ahci: slumbering drive unwakeable %ux\n", s);
+		return;
+	}
+	p->sctl = 3*Aipm | 0*Aspd | Adet;
+	delay(1);
+	p->sctl &= ~7;
+//	iprint("ahci: wake %ux -> %ux\n", s, p->sstatus);
+}
+
+static int
+ahciconfigdrive(Ahba *h, Aportc *c, int mode)
+{
+	Aportm *m;
+	Aport *p;
+
+	p = c->p;
+	m = c->m;
+
+	if(m->list == 0){
+		setupfis(&m->fis);
+		m->list = malign(sizeof *m->list, 1024);
+		m->ctab = malign(sizeof *m->ctab, 128);
+	}
+
+	if(p->sstatus & 3 && h->cap & Hsss){
+		dprint("configdrive:  spinning up ... [%lux]\n", p->sstatus);
+		p->cmd |= Apod|Asud;
+		asleep(1400);
+	}
+
+	p->serror = SerrAll;
+
+	p->list = PCIWADDR(m->list);
+	p->listhi = 0;
+	p->fis = PCIWADDR(m->fis.base);
+	p->fishi = 0;
+	p->cmd |= Afre | Ast;
+
+	if((p->sstatus & 0x707) == 0x601) /* drive coming up in slumbering? */
+		ahciwakeup(p);
+
+	/* disable power managment sequence from book. */
+	p->sctl = (3*Aipm) | (mode*Aspd) | (0*Adet);
+	p->cmd &= ~Aalpe;
+
+	p->ie = IEM;
+
+	return 0;
+}
+
+static int
+ahcienable(Ahba *h)
+{
+	h->ghc |= Hie;
+	return 0;
+}
+
+static int
+ahcidisable(Ahba *h)
+{
+	h->ghc &= ~Hie;
+	return 0;
+}
+
+static int
+countbits(ulong u)
+{
+	int i, n;
+
+	n = 0;
+	for(i = 0; i < 32; i++)
+		if(u & (1<<i))
+			n++;
+	return n;
+}
+
+static int
+ahciconf(Ctlr *c)
+{
+	ulong u;
+	Ahba *h;
+	static int count;
+
+	h = c->hba = (Ahba*)c->mmio;
+	u = h->cap;
+
+	if((u & Hsam) == 0)
+		h->ghc |= Hae;
+
+	print("ahci%d port %#p: hba sss %ld; ncs %ld; coal %ld; mports %ld; "
+		"led %ld; clo %ld; ems %ld;\n", count++, h,
+		(u>>27) & 1, (u>>8) & 0x1f, (u>>7) & 1,	u & 0x1f, (u>>25) & 1,
+		(u>>24) & 1, (u>>6) & 1);
+	return countbits(h->pi);
+}
+
+static int
+ahcihbareset(Ahba *h)
+{
+	int wait;
+
+	h->ghc |= 1;
+	for(wait = 0; wait < 1000; wait += 100){
+		if(h->ghc == 0)
+			return 0;
+		delay(100);
+	}
+	return -1;
+}
+
+static void
+idmove(char *p, ushort *a, int n)
+{
+	int i;
+	char *op, *e;
+
+	op = p;
+	for(i = 0; i < n/2; i++){
+		*p++ = a[i] >> 8;
+		*p++ = a[i];
+	}
+	*p = 0;
+	while(p > op && *--p == ' ')
+		*p = 0;
+	e = p;
+	for (p = op; *p == ' '; p++)
+		;
+	memmove(op, p, n - (e - p));
+}
+
+static int
+identify(Drive *d)
+{
+	ushort *id;
+	vlong osectors, s;
+	uchar oserial[21];
+	SDunit *u;
+
+	id = d->info;
+	s = ahciidentify(&d->portc, id);
+	if(s == -1){
+		d->state = Derror;
+		return -1;
+	}
+	osectors = d->sectors;
+	memmove(oserial, d->serial, sizeof d->serial);
+
+	d->sectors = s;
+	d->smartrs = 0;
+
+	idmove(d->serial, id+10, 20);
+	idmove(d->firmware, id+23, 8);
+	idmove(d->model, id+27, 40);
+
+	u = d->unit;
+	memset(u->inquiry, 0, sizeof u->inquiry);
+	u->inquiry[2] = 2;
+	u->inquiry[3] = 2;
+	u->inquiry[4] = sizeof u->inquiry - 4;
+	memmove(u->inquiry+8, d->model, 40);
+
+	if((osectors == 0 || osectors != s) &&
+	    memcmp(oserial, d->serial, sizeof oserial) != 0){
+		d->mediachange = 1;
+		u->sectors = 0;
+	}
+
+	return 0;
+}
+
+static void
+clearci(Aport *p)
+{
+	if((p->cmd & Ast) == 0)
+		return;
+	p->cmd &= ~Ast;
+	p->cmd |= Ast;
+}
+
+static void
+updatedrive(Drive *d)
+{
+	ulong cause, serr, s0, pr, ewake;
+	char *name;
+	Aport *p;
+	static ulong last;
+
+	pr = 1;
+	ewake = 0;
+	p = d->port;
+	cause = p->isr;
+	serr = p->serror;
+	p->isr = cause;
+	name = "??";
+	if(d->unit && d->unit->name)
+		name = d->unit->name;
+
+	if(p->ci == 0){
+		d->portm.flag |= Fdone;
+		pr = 0;
+	}else if(cause & Adps)
+		pr = 0;
+	if(cause&Ifatal){
+		ewake = 1;
+		dprint("Fatal\n");
+	}
+	if(cause & Adhrs){
+		if(p->task & (32|1)){
+			dprint("Adhrs cause = %lux; serr = %lux; task=%lux\n",
+				cause, serr, p->task);
+			d->portm.flag |= Ferror;
+			ewake = 1;
+		}
+		pr = 0;
+	}
+
+	if(pr)
+		dprint("%s: upd %lux ta %lux\n", name, cause, p->task);
+	if(cause & (Aprcs|Aifs)){
+		s0 = d->state;
+		switch(p->sstatus & 7){
+		case 0:
+			d->state = Dmissing;
+			break;
+		case 1:
+			if((p->sstatus & 0x700) == 0x600)
+				d->state = Dnew;
+			else
+				d->state = Derror;
+			break;
+		case 3:
+			/* power mgnt crap for surprise removal */
+			p->ie |= Aprcs | Apcs;	/* is this required? */
+			d->state = Dreset;
+			break;
+		case 4:
+			d->state = Doffline;
+			break;
+		}
+		dprint("%s: %s → %s [Apcrs] %lux\n", name, diskstates[s0],
+			diskstates[d->state], p->sstatus);
+		if(s0 == Dready && d->state != Dready)
+			idprint("%s: pulled\n", name);
+		if(d->state != Dready)
+			d->portm.flag |= Ferror;
+		ewake = 1;
+	}
+	p->serror = serr;
+	if(ewake)
+		clearci(p);
+	last = cause;
+}
+
+static void
+pstatus(Drive *d, ulong s)
+{
+	/*
+	 * bogus code because the first interrupt is currently dropped.
+	 * likely my fault.  serror may be cleared at the wrong time.
+	 */
+	switch(s){
+	case 0:
+		d->state = Dmissing;
+		break;
+	case 2:			/* should this be missing?  need testcase. */
+		dprint("pstatus 2\n");
+	case 3:
+		d->wait = 0;
+		d->state = Dnew;
+		break;
+	case 4:
+		d->state = Doffline;
+		break;
+	case 6:
+		d->state = Dnew;
+		break;
+	}
+}
+
+static int
+configdrive(Drive *d)
+{
+	if(ahciconfigdrive(d->ctlr->hba, &d->portc, d->mode) == -1)
+		return -1;
+	ilock(d);
+	pstatus(d, d->port->sstatus & 7);
+	iunlock(d);
+	return 0;
+}
+
+static void
+resetdisk(Drive *d)
+{
+	uint state, det, stat;
+	Aport *p;
+
+	p = d->port;
+	det = p->sctl & 7;
+	stat = p->sstatus & 7;
+	state = (p->cmd>>28) & 0xf;
+	dprint("resetdisk: icc %ux  det %d sdet %d\n", state, det, stat);
+	if(stat != 3){
+		ilock(d);
+		d->state = Dportreset;
+		iunlock(d);
+		return;
+	}
+	ilock(d);
+	state = d->state;
+	if(d->state != Dready || d->state != Dnew)
+		d->portm.flag |= Ferror;
+	clearci(p);			/* satisfy sleep condition. */
+	iunlock(d);
+
+	qlock(&d->portm);
+
+	if(p->cmd & Ast && ahciswreset(&d->portc) == -1){
+		ilock(d);
+		d->state = Dportreset;	/* get a bigger stick. */
+		iunlock(d);
+	} else {
+		ilock(d);
+		d->state = Dmissing;
+		iunlock(d);
+
+		configdrive(d);
+	}
+	dprint("resetdisk: %s → %s\n", diskstates[state], diskstates[d->state]);
+	qunlock(&d->portm);
+}
+
+static int
+newdrive(Drive *d)
+{
+	char *name, *s;
+	Aportc *c;
+	Aportm *m;
+
+	c = &d->portc;
+	m = &d->portm;
+
+	name = d->unit->name;
+	if(name == 0)
+		name = "??";
+
+	if(d->port->task == 0x80)
+		return -1;
+	qlock(c->m);
+	if(setudmamode(c, 5) == -1){
+		dprint("%s: can't set udma mode\n", name);
+		goto lose;
+	}
+	if(identify(d) == -1){
+		dprint("%s: identify failure\n", name);
+		goto lose;
+	}
+	if(m->feat & Dpower && setfeatures(c, 0x85) == -1){
+		m->feat &= ~Dpower;
+		if(ahcirecover(c) == -1) {
+			dprint("%s: ahcirecover failed\n", name);
+			goto lose;
+		}
+	}
+
+	ilock(d);
+	d->state = Dready;
+	iunlock(d);
+
+	qunlock(c->m);
+
+	s = "";
+	if(m->feat & Dllba)
+		s = "L";
+	idprint("%s: %sLBA %lld sectors\n", d->unit->name, s, d->sectors);
+	idprint("  %s %s %s %s\n", d->model, d->firmware, d->serial,
+		d->mediachange? "[mediachange]": "");
+
+	return 0;
+
+lose:
+	qunlock(&d->portm);
+	return -1;
+}
+
+enum {
+	Nms		= 256,
+	Mphywait	=  2*1024/Nms - 1,
+	Midwait		= 16*1024/Nms - 1,
+	Mcomrwait	= 64*1024/Nms - 1,
+};
+
+static void
+westerndigitalhung(Drive *d)
+{
+	if((d->portm.feat & Datapi) == 0 && d->active &&
+	    TK2MS(m->ticks - d->intick) > 5000){
+		dprint("%s: drive hung; resetting [%lux] ci=%lx\n",
+			d->unit->name, d->port->task, d->port->ci);
+		d->state = Dreset;
+	}
+}
+
+static ushort olds[NCtlr*NCtlrdrv];
+
+static int
+doportreset(Drive *d)
+{
+	int i;
+
+	i = -1;
+	qlock(&d->portm);
+	if(ahciportreset(&d->portc) == -1)
+		dprint("ahciportreset fails\n");
+	else
+		i = 0;
+	qunlock(&d->portm);
+	dprint("portreset → %s  [task %lux]\n", diskstates[d->state],
+		d->port->task);
+	return i;
+}
+
+static void
+checkdrive(Drive *d, int i)
+{
+	ushort s;
+	char *name;
+
+	ilock(d);
+	name = d->unit->name;
+	s = d->port->sstatus;
+	if(s != olds[i]){
+		dprint("%s: status: %#ux -> %#ux: %s\n", name, olds[i],
+			s, diskstates[d->state]);
+		olds[i] = s;
+		d->wait = 0;
+	}
+	westerndigitalhung(d);
+	switch(d->state){
+	case Dnull:
+		break;
+	case Dmissing:
+	case Dnew:
+		switch(s & 0x107){
+		case 1:
+			ahciwakeup(d->port);
+		case 0:
+			break;
+		default:
+			dprint("%s: unknown status %04ux\n", name, s);
+		case 0x100:
+			if(++d->wait&Mphywait)
+				break;
+reset:
+			if(++d->mode > DMsataii)
+				d->mode = 0;
+			if(d->mode == DMsatai){	/* we tried everything */
+				d->state = Dportreset;
+				goto portreset;
+			}
+			dprint("%s: reset; new mode %s\n", name,
+				modename[d->mode]);
+			iunlock(d);
+			resetdisk(d);
+			ilock(d);
+			break;
+		case 0x103:
+			if((++d->wait&Midwait) == 0){
+				dprint("%s: slow reset %#ux task=%#lux; %d\n",
+					name, s, d->port->task, d->wait);
+				goto reset;
+			}
+			s = d->port->task&0xff;
+			if(s == 0x7f || ((d->port->sig>>16) != 0xeb14 &&
+			    (s & ~0x17) != (1<<6)))
+				break;
+			iunlock(d);
+			newdrive(d);
+			ilock(d);
+			break;
+		}
+		break;
+	case Doffline:
+		if(d->wait++ & Mcomrwait)
+			break;
+	case Derror:
+	case Dreset:
+		dprint("%s: reset [%s]: mode %d; status %#ux\n",
+			name, diskstates[d->state], d->mode, s);
+		iunlock(d);
+		resetdisk(d);
+		ilock(d);
+		break;
+	case Dportreset:
+portreset:
+		if(d->wait++ & 0xff && (s & 0x100) == 0)
+			break;
+		dprint("%s: portreset [%s]: mode %d; status %04ux\n",
+			name, diskstates[d->state], d->mode, s);
+		d->portm.flag |= Ferror;
+		clearci(d->port);
+		if((s & 7) == 0){
+			d->state = Dmissing;
+			break;
+		}
+		iunlock(d);
+		doportreset(d);
+		ilock(d);
+		break;
+	}
+	iunlock(d);
+}
+
+static void
+iainterrupt(Ureg*, void *a)
+{
+	int i;
+	ulong cause, m;
+	Ctlr *c;
+	Drive *d;
+
+	c = a;
+	ilock(c);
+	/* check drive here! */
+	cause = c->hba->isr;
+	for(i = 0; i < c->ndrive; i++){
+		m = 1 << i;
+		if((cause & m) == 0)
+			continue;
+		d = c->rawdrive + i;
+		ilock(d);
+		if(d->port->isr && c->hba->pi & m)
+			updatedrive(d);
+		c->hba->isr = m;
+		iunlock(d);
+	}
+	iunlock(c);
+}
+
+static int
+iaverify(SDunit *u)
+{
+	int i;
+	Ctlr *c;
+	Drive *d;
+
+	c = u->dev->ctlr;
+	d = c->drive[u->subno];
+	ilock(c);
+	ilock(d);
+	d->unit = u;
+	iunlock(d);
+	iunlock(c);
+	for(i = 0; i < 10; i++){
+		checkdrive(d, d->driveno);
+		switch(d->state){
+		case Dmissing:
+			if(i < 4 || d->port->sstatus & 0x733)
+				break;
+			/* fall through */
+		case Dnull:
+		case Dready:
+		case Doffline:
+			print("sdiahci: drive %d in state %s after %d resets\n",
+				d->driveno, diskstates[d->state], i);
+			return 1;
+		}
+		delay(100);
+	}
+	print("sdiahci: drive %d won't come up; in state %s after %d resets\n",
+		d->driveno, diskstates[d->state], i);
+	return 1;
+}
+
+static int
+iaenable(SDev *s)
+{
+	Ctlr *c;
+
+	c = s->ctlr;
+	ilock(c);
+	if(!c->enabled) {
+		if(c->ndrive == 0)
+			panic("iaenable: zero s->ctlr->ndrive");
+		pcisetbme(c->pci);
+		setvec(c->pci->intl+VectorPIC, iainterrupt, c);
+		/* supposed to squelch leftover interrupts here. */
+		ahcienable(c->hba);
+		c->enabled = 1;
+	}
+	iunlock(c);
+	return 1;
+}
+
+static int
+iadisable(SDev *s)
+{
+	Ctlr *c;
+
+	c = s->ctlr;
+	ilock(c);
+	ahcidisable(c->hba);
+//	intrdisable(c->irq, iainterrupt, c, c->tbdf, name);
+	c->enabled = 0;
+	iunlock(c);
+	return 1;
+}
+
+static int
+iaonline(SDunit *unit)
+{
+	int r;
+	Ctlr *c;
+	Drive *d;
+
+	c = unit->dev->ctlr;
+	d = c->drive[unit->subno];
+	r = 0;
+
+	if(d->portm.feat & Datapi && d->mediachange){
+		r = scsionline(unit);
+		if(r > 0)
+			d->mediachange = 0;
+		return r;
+	}
+
+	ilock(d);
+	if(d->mediachange){
+		r = 2;
+		d->mediachange = 0;
+		/* devsd resets this after online is called; why? */
+		unit->sectors = d->sectors;
+		unit->secsize = 512;
+	} else if(d->state == Dready)
+		r = 1;
+	iunlock(d);
+	return r;
+}
+
+/* returns locked list! */
+static Alist*
+ahcibuild(Aportm *m, uchar *cmd, void *data, int n, vlong lba)
+{
+	uchar *c, acmd, dir, llba;
+	Alist *l;
+	Actab *t;
+	Aprdt *p;
+	static uchar tab[2][2] = { 0xc8, 0x25, 0xca, 0x35 };
+
+	dir = *cmd != 0x28;
+	llba = m->feat & Dllba? 1: 0;
+	acmd = tab[dir][llba];
+	qlock(m);
+	l = m->list;
+	t = m->ctab;
+	c = t->cfis;
+
+	c[0] = 0x27;
+	c[1] = 0x80;
+	c[2] = acmd;
+	c[3] = 0;
+
+	c[4] = lba;		/* sector	lba low	7:0 */
+	c[5] = lba >> 8;	/* cylinder low	lba mid	15:8 */
+	c[6] = lba >> 16;	/* cylinder hi	lba hi	23:16 */
+	c[7] = 0xa0 | 0x40;	/* obsolete device bits + lba */
+	if(llba == 0)
+		c[7] |= (lba>>24) & 7;
+
+	c[8] = lba >> 24;	/* sector (exp)		lba 	31:24 */
+	c[9] = lba >> 32;	/* cylinder low (exp)	lba	39:32	 */
+	c[10] = lba >> 48;	/* cylinder hi (exp)	lba	48:40 */
+	c[11] = 0;		/* features (exp); */
+
+	c[12] = n;		/* sector count */
+	c[13] = n >> 8;		/* sector count (exp) */
+	c[14] = 0;		/* r */
+	c[15] = 0;		/* control */
+
+	*(ulong*)(c+16) = 0;
+
+	l->flags = 1<<16 | Lpref | 0x5;	/* Lpref ?? */
+	if(dir == Write)
+		l->flags |= Lwrite;
+	l->len = 0;
+	l->ctab = PCIWADDR(t);
+	l->ctabhi = 0;
+
+	p = &t->prdt;
+	p->dba = PCIWADDR(data);
+	p->dbahi = 0;
+	p->count = 1<<31 | (512*n - 2) | 1;
+
+	return l;
+}
+
+static Alist*
+ahcibuildpkt(Aportm *m, SDreq *r, void *data, int n)
+{
+	int fill, len;
+	uchar *c;
+	Actab *t;
+	Alist *l;
+	Aprdt *p;
+
+	qlock(m);
+	l = m->list;
+	t = m->ctab;
+	c = t->cfis;
+
+	fill = m->feat & Datapi16? 16: 12;
+	if((len = r->clen) > fill)
+		len = fill;
+	memmove(t->atapi, r->cmd, len);
+	memset(t->atapi + len, 0, fill - len);
+
+	c[0] = 0x27;
+	c[1] = 0x80;
+	c[2] = 0xa0;
+	if(n != 0)
+		c[3] = 1;	/* dma */
+	else
+		c[3] = 0;	/* features (exp); */
+
+	c[4] = 0;		/* sector	lba low	7:0 */
+	c[5] = n;		/* cylinder low	lba mid	15:8 */
+	c[6] = n >> 8;		/* cylinder hi	lba hi	23:16 */
+	c[7] = 0xa0;		/* obsolete device bits */
+
+	*(ulong*)(c+8) = 0;
+	*(ulong*)(c+12) = 0;
+	*(ulong*)(c+16) = 0;
+
+	l->flags = 1<<16 | Lpref | Latapi | 0x5;
+	if(r->write != 0 && data)
+		l->flags |= Lwrite;
+	l->len = 0;
+	l->ctab = PCIWADDR(t);
+	l->ctabhi = 0;
+
+	if(data == 0)
+		return l;
+
+	p = &t->prdt;
+	p->dba = PCIWADDR(data);
+	p->dbahi = 0;
+	p->count = 1<<31 | (n - 2) | 1;
+
+	return l;
+}
+
+static int
+waitready(Drive *d)
+{
+	ulong s, t, i;
+
+	for(i = 0; i < 120; i++){
+		ilock(d);
+		s = d->port->sstatus;
+		t = d->port->task;
+		iunlock(d);
+		if((s & 0x100) == 0)
+			return -1;
+		if(d->state == Dready && (s & 7) == 3)
+			return 0;
+		if((i + 1) % 30 == 0)
+			print("%s: waitready: [%s] task=%lux sstat=%lux\n",
+				d->unit->name, diskstates[d->state], t, s);
+		esleep(1000);
+	}
+	print("%s: not responding; offline\n", d->unit->name);
+	ilock(d);
+	d->state = Doffline;
+	iunlock(d);
+	return -1;
+}
+
+static int
+iariopkt(SDreq *r, Drive *d)
+{
+	int n, count, try, max, flag, task;
+	char *name;
+	uchar *cmd, *data;
+	Aport *p;
+	Asleep as;
+
+	cmd = r->cmd;
+	name = d->unit->name;
+	p = d->port;
+
+	aprint("%02ux %02ux %c %d %p\n", cmd[0], cmd[2], "rw"[r->write],
+		r->dlen, r->data);
+//	if(cmd[0] == 0x5a && (cmd[2] & 0x3f) == 0x3f)
+//		return sdmodesense(r, cmd, d->info, sizeof d->info);
+	r->rlen = 0;
+	count = r->dlen;
+	max = 65536;
+
+	try = 0;
+retry:
+	if(waitready(d) == -1)
+		return SDeio;
+	data = r->data;
+	n = count;
+	if(n > max)
+		n = max;
+	d->active++;
+	ahcibuildpkt(&d->portm, r, data, n);
+	ilock(d);
+	d->portm.flag = 0;
+	iunlock(d);
+	p->ci = 1;
+
+	as.p = p;
+	as.i = 1;
+	d->intick = m->ticks;
+
+	while(ahciclear(&as) == 0)
+		;
+
+	ilock(d);
+	flag = d->portm.flag;
+	task = d->port->task;
+	iunlock(d);
+
+	if(task & (Efatal<<8) || task & (ASbsy|ASdrq) && d->state == Dready){
+		d->port->ci = 0;		/* @? */
+		ahcirecover(&d->portc);
+		task = d->port->task;
+	}
+	d->active--;
+	qunlock(&d->portm);
+	if(flag == 0){
+		if(++try == 10){
+			print("%s: bad disk\n", name);
+			r->status = SDcheck;
+			return SDcheck;
+		}
+		print("%s: retry\n", name);
+		esleep(1000);
+		goto retry;
+	}
+	if(flag & Ferror){
+		if((task & Eidnf) == 0)
+			print("%s: i/o error %ux\n", name, task);
+		r->status = SDcheck;
+		return SDcheck;
+	}
+
+	data += n;
+
+	r->rlen = data - (uchar*)r->data;
+	r->status = SDok;
+	return SDok;
+}
+
+static int
+iario(SDreq *r)
+{
+	int n, count, max, flag, task;
+	vlong lba;
+	char *name;
+	uchar *cmd, *data;
+	Aport *p;
+	Asleep as;
+	Ctlr *c;
+	Drive *d;
+	SDunit *unit;
+
+	unit = r->unit;
+	c = unit->dev->ctlr;
+	d = c->drive[unit->subno];
+	if(d->portm.feat & Datapi)
+		return iariopkt(r, d);
+	cmd = r->cmd;
+	name = d->unit->name;
+	p = d->port;
+
+//	if((i = sdfakescsi(r, d->info, sizeof d->info)) != SDnostatus){
+//		r->status = i;
+//		return i;
+//	}
+
+	if(*cmd != 0x28 && *cmd != 0x2a){
+		print("%s: bad cmd 0x%.2ux\n", name, cmd[0]);
+		r->status = SDcheck;
+		return SDcheck;
+	}
+
+	lba   = cmd[2]<<24 | cmd[3]<<16 | cmd[4]<<8 | cmd[5];
+	count = cmd[7]<<8  | cmd[8];
+	if(r->data == nil)
+		return SDok;
+	if(r->dlen < count * unit->secsize)
+		count = r->dlen / unit->secsize;
+	max = 128;
+
+	if(waitready(d) == -1)
+		return SDeio;
+	data = r->data;
+	while(count > 0){
+		n = count;
+		if(n > max)
+			n = max;
+		d->active++;
+		ahcibuild(&d->portm, cmd, data, n, lba);
+		ilock(d);
+		d->portm.flag = 0;
+		iunlock(d);
+		p->ci = 1;
+
+		as.p = p;
+		as.i = 1;
+		d->intick = m->ticks;
+
+		while(ahciclear(&as) == 0)
+			;
+
+		ilock(d);
+		flag = d->portm.flag;
+		task = d->port->task;
+		iunlock(d);
+
+		if(task & (Efatal<<8) ||
+		    task & (ASbsy|ASdrq) && d->state == Dready){
+			d->port->ci = 0;		/* @? */
+			ahcirecover(&d->portc);
+			task = d->port->task;
+		}
+		d->active--;
+		qunlock(&d->portm);
+		if(flag == 0 || flag & Ferror){
+			print("%s: i/o error %ux @%lld\n", name, task, lba);
+			r->status = SDeio;
+			return SDeio;
+		}
+
+		count -= n;
+		lba += n;
+		data += n * unit->secsize;
+	}
+	r->rlen = data - (uchar*)r->data;
+	r->status = SDok;
+	return SDok;
+}
+
+/*
+ * configure drives 0-5 as ahci sata  (c.f. errata)
+ */
+static int
+iaahcimode(Pcidev *p)
+{
+	dprint("iaahcimode %ux %ux %ux\n", pcicfgr8(p, 0x91),
+		pcicfgr8(p, 92), pcicfgr8(p, 93));
+	pcicfgw16(p, 0x92, pcicfgr32(p, 0x92) | 0xf);	/* ports 0-3 */
+//	pcicfgw8(p, 0x93, pcicfgr32(p, 9x93) | 3);	/* ports 4-5 */
+	return 0;
+}
+
+static void
+iasetupahci(Ctlr *c)
+{
+	/* disable cmd block decoding. */
+	pcicfgw16(c->pci, 0x40, pcicfgr16(c->pci, 0x40) & ~(1<<15));
+	pcicfgw16(c->pci, 0x42, pcicfgr16(c->pci, 0x42) & ~(1<<15));
+
+	c->lmmio[0x4/4] |= 1 << 31;	/* enable ahci mode (ghc register) */
+	c->lmmio[0xc/4] = (1<<6) - 1;	/* five ports (supposedly ro pi reg) */
+
+	/* enable ahci mode; from ich9 datasheet */
+	pcicfgw8(c->pci, 0x90, 1<<6 | 1<<5);
+}
+
+static SDev*
+iapnp(void)
+{
+	int i, n, nunit, type;
+	ulong io;
+	Ctlr *c;
+	Drive *d;
+	Pcidev *p;
+	SDev *head, *tail, *s;
+	static int done;
+
+	if (done || getconf("*noahciload") != nil)
+		return nil;
+	done = 1;
+	p = nil;
+	head = tail = nil;
+loop:
+	while((p = pcimatch(p, 0, 0)) != nil){
+		if(p->vid == 0x8086 && (p->did & 0xfffc) == 0x2680)
+			type = Tesb;
+		else if(p->vid == 0x8086 && p->did == 0x27c5)
+			type = Tich;	/* 82801g[bh]m; compat mode fails */
+		else if(p->vid == 0x8086 && (p->did & 0xfeff) == 0x2829)
+			type = Tich;		/* ich8 */
+		else if(p->vid == 0x8086 && (p->did & 0xfffe) == 0x2922)
+			type = Tich;		/* ich8 */
+		else if(p->vid == 0x1002 && p->did == 0x4380)
+			type = Tsb600;
+		else
+			continue;
+		if(niactlr == NCtlr){
+			print("iapnp: %s: too many controllers\n", tname[type]);
+			break;
+		}
+		c = iactlr + niactlr;
+		s = sdevs  + niactlr;
+		memset(c, 0, sizeof *c);
+		memset(s, 0, sizeof *s);
+		c->pci = p;
+		c->type = type;
+		io = p->mem[Abar].bar & ~0xf;
+		io = upamalloc(io, p->mem[Abar].size, 0);
+		if(io == 0){
+			print("%s: address %#lux in use, did %#ux\n",
+				tname[c->type], io, p->did);
+			continue;
+		}
+		/* ugly hack: get this in compatibility mode; see memory.c:271 */
+		if(io == 0x40000000) {
+			print("%s: did %#ux is in non-sata mode.  bar %#lux\n",
+				tname[c->type], p->did, p->mem[Abar].bar);
+			continue;
+		}
+		c->mmio = KADDR(io);
+		c->lmmio = (ulong*)c->mmio;
+		if(Intel(c->type) && p->did != 0x2681)
+			iasetupahci(c);
+		nunit = ahciconf(c);
+//		ahcihbareset((Ahba*)c->mmio);
+		if(Intel(c->type) && iaahcimode(p) == -1)
+			break;
+		if(nunit < 1){
+//			vunmap(c->mmio, p->mem[Abar].size);
+			continue;
+		}
+		niactlr++;
+		i = (c->hba->cap>>21) & 1;
+		print("%s: sata-%s with %d ports\n", tname[c->type],
+			"I\0II"+i*2, nunit);
+		s->ifc = &sdiahciifc;
+		s->ctlr = c;
+		s->nunit = nunit;
+		s->idno = 'E';
+		c->sdev = s;
+		c->ndrive = nunit;
+
+		/* map the drives -- they don't all need to be enabled. */
+		memset(c->rawdrive, 0, sizeof c->rawdrive);
+		n = 0;
+		for(i = 0; i < NCtlrdrv; i++) {
+			d = c->rawdrive+i;
+			d->portno = i;
+			d->driveno = -1;
+			d->sectors = 0;
+			d->ctlr = c;
+			if((c->hba->pi & (1<<i)) == 0)
+				continue;
+//			d->state = Dnew;
+			d->port = (Aport*)(c->mmio + 0x80*i + 0x100);
+			d->portc.p = d->port;
+			d->portc.m = &d->portm;
+			d->driveno = n++;
+			c->drive[d->driveno] = d;
+		}
+		for(i = 0; i < n; i++)
+			if(ahciidle(c->drive[i]->port) == -1){
+				print("%s: port %d wedged; abort\n",
+					tname[c->type], i);
+				goto loop;
+			}
+		for(i = 0; i < n; i++){
+			c->drive[i]->mode = DMsatai;
+			configdrive(c->drive[i]);
+		}
+
+		if(head)
+			tail->next = s;
+		else
+			head = s;
+		tail = s;
+	}
+	return head;
+}
+
+static SDev*
+iaid(SDev* sdev)
+{
+	int i;
+	Ctlr *c;
+
+	for(; sdev; sdev = sdev->next){
+		if(sdev->ifc != &sdiahciifc)
+			continue;
+		c = sdev->ctlr;
+		for(i = 0; i < NCtlr; i++)
+			if(c == iactlr + i)
+				sdev->idno = 'E' + i;
+	}
+	return nil;
+}
+
+SDifc sdiahciifc = {
+	"iahci",
+
+	iapnp,
+	nil,			/* legacy */
+	iaid,
+	iaenable,
+	iadisable,
+
+	iaverify,
+	iaonline,
+	iario,
+	nil,
+	nil,
+
+	scsibio,
+};
--- /dev/null
+++ b/os/boot/pc/sdmylex.c
@@ -1,0 +1,1292 @@
+/*
+ * Mylex MultiMaster (Buslogic BT-*) SCSI Host Adapter
+ * in both 24-bit and 32-bit mode.
+ * 24-bit mode works for Adaptec AHA-154xx series too.
+ *
+ * To do:
+ *	allocate more Ccb's as needed, up to NMbox-1;
+ *	add nmbox and nccb to Ctlr struct for the above;
+ *	64-bit LUN/explicit wide support necessary?
+ *
+ */
+#include "u.h"
+#include "lib.h"
+#include "mem.h"
+#include "dat.h"
+#include "fns.h"
+#include "io.h"
+#include "ureg.h"
+#include "error.h"
+
+#include "sd.h"
+
+#define waserror()	(0)
+#define poperror()
+typedef struct QLock{ int r; } QLock;
+typedef struct Rendez{ int r; } Rendez;
+#define	intrenable(irq, f, c, tbdf, name)	setvec(VectorPIC+(irq), f, c);\
+						USED(tbdf);
+
+#define K2BPA(va, tbdf)	PADDR(va)
+#define BPA2K(pa, tbdf)	KADDR(pa)
+
+extern SDifc sdmylexifc;
+
+enum {					/* registers */
+	Rcontrol	= 0x00,		/* WO: control register */
+	Rstatus		= 0x00,		/* RO: status register */
+	Rcpr		= 0x01,		/* WO: command/parameter register */
+	Rdatain		= 0x01,		/* RO: data-in register */
+	Rinterrupt	= 0x02,		/* RO: interrupt register */
+};
+
+enum {					/* Rcontrol */
+	Rsbus		= 0x10,		/* SCSI Bus Reset */
+	Rint		= 0x20,		/* Interrupt Reset */
+	Rsoft		= 0x40,		/* Soft Reset */
+	Rhard		= 0x80,		/* Hard Reset */
+};
+
+enum {					/* Rstatus */
+	Cmdinv		= 0x01,		/* Command Invalid */
+	Dirrdy		= 0x04,		/* Data In Register Ready */
+	Cprbsy		= 0x08,		/* Command/Parameter Register Busy */
+	Hardy		= 0x10,		/* Host Adapter Ready */
+	Inreq		= 0x20,		/* Initialisation Required */
+	Dfail		= 0x40,		/* Diagnostic Failure */
+	Dact		= 0x80,		/* Diagnostic Active */
+};
+
+enum {					/* Rcpr */
+	Cinitialise	= 0x01,		/* Initialise Mailbox */
+	Cstart		= 0x02,		/* Start Mailbox Command */
+	Cinquiry	= 0x04,		/* Adapter Inquiry */
+	Ceombri		= 0x05,		/* Enable OMBR Interrupt */
+	Cinquire	= 0x0B,		/* Inquire Configuration */
+	Cextbios	= 0x28,		/* AHA-1542: extended BIOS info. */
+	Cmbienable	= 0x29,		/* AHA-1542: Mailbox interface enable */
+	Ciem		= 0x81,		/* Initialise Extended Mailbox */
+	Ciesi		= 0x8D,		/* Inquire Extended Setup Information */
+	Cerrm		= 0x8F,		/* Enable strict round-robin mode */
+	Cwide		= 0x96,		/* Wide CCB */
+};
+
+enum {					/* Rinterrupt */
+	Imbl		= 0x01,		/* Incoming Mailbox Loaded */
+	Mbor		= 0x02,		/* Mailbox Out Ready */
+	Cmdc		= 0x04,		/* Command Complete */
+	Rsts		= 0x08,		/* SCSI Reset State */
+	Intv		= 0x80,		/* Interrupt Valid */
+};
+
+typedef struct {
+	uchar	code;			/* action/completion code */
+	uchar	ccb[3];			/* CCB pointer (MSB, ..., LSB) */
+} Mbox24;
+
+typedef struct {
+	uchar	ccb[4];			/* CCB pointer (LSB, ..., MSB) */
+	uchar	btstat;			/* BT-7[45]7[SD] status */
+	uchar	sdstat;			/* SCSI device status */
+	uchar	pad;
+	uchar	code;			/* action/completion code */
+} Mbox32;
+
+enum {					/* mailbox commands */
+	Mbfree		= 0x00,		/* Mailbox not in use */
+
+	Mbostart	= 0x01,		/* Start a mailbox command */
+	Mboabort	= 0x02,		/* Abort a mailbox command */
+
+	Mbiok		= 0x01,		/* CCB completed without error */
+	Mbiabort	= 0x02,		/* CCB aborted at request of host */
+	Mbinx		= 0x03,		/* Aborted CCB not found */
+	Mbierror	= 0x04,		/* CCB completed with error */
+};
+
+typedef struct Ccb24 Ccb24;
+typedef struct Ccb32 Ccb32;
+typedef union Ccb Ccb;
+
+typedef struct Ccb24 {
+	uchar	opcode;			/* Operation code */
+	uchar	datadir;		/* Data direction control */
+	uchar	cdblen;			/* Length of CDB */
+	uchar	senselen;		/* Length of sense area */
+	uchar	datalen[3];		/* Data length (MSB, ..., LSB) */
+	uchar	dataptr[3];		/* Data pointer (MSB, ..., LSB) */
+	uchar	linkptr[3];		/* Link pointer (MSB, ..., LSB) */
+	uchar	linkid;			/* command linking identifier */
+	uchar	btstat;			/* BT-* adapter status */
+	uchar	sdstat;			/* SCSI device status */
+	uchar	reserved[2];		/* */
+	uchar	cs[12+0xFF];		/* Command descriptor block + Sense */
+
+	void*	data;			/* buffer if address > 24-bits */
+
+	Rendez;
+	int	done;			/* command completed */
+
+	Ccb*	ccb;			/* link on free list */
+} Ccb24;
+
+
+typedef struct Ccb32 {
+	uchar	opcode;			/* Operation code */
+	uchar	datadir;		/* Data direction control */
+	uchar	cdblen;			/* Length of CDB */
+	uchar	senselen;		/* Length of sense area */
+	uchar	datalen[4];		/* Data length (LSB, ..., MSB) */
+	uchar	dataptr[4];		/* Data pointer (LSB, ..., MSB) */
+	uchar	reserved[2];
+	uchar	btstat;			/* BT-* adapter status */
+	uchar	sdstat;			/* SCSI device status */
+	uchar	targetid;		/* Target ID */
+	uchar	luntag;			/* LUN & tag */
+	uchar	cdb[12];		/* Command descriptor block */
+	uchar	ccbctl;			/* CCB control */
+	uchar	linkid;			/* command linking identifier */
+	uchar	linkptr[4];		/* Link pointer (LSB, ..., MSB) */
+	uchar	senseptr[4];		/* Sense pointer (LSB, ..., MSB) */
+	uchar	sense[0xFF];		/* Sense bytes */
+
+	Rendez;
+	int	done;			/* command completed */
+
+	Ccb*	ccb;			/* link on free list */
+} Ccb32;
+
+typedef union Ccb {
+	Ccb24;
+	Ccb32;
+} Ccb;
+
+enum {					/* opcode */
+	OInitiator	= 0x00,		/* initiator CCB */
+	Ordl		= 0x03,		/* initiator CCB with
+					 * residual data length returned
+					 */
+};
+
+enum {					/* datadir */
+	CCBdatain	= 0x08,		/* inbound, length is checked */
+	CCBdataout	= 0x10,		/* outbound, length is checked */
+};
+
+enum {					/* btstat */
+	Eok		= 0x00,		/* normal completion with no errors */
+};
+
+enum {					/* luntag */
+	TagEnable	= 0x20,		/* Tag enable */
+	SQTag		= 0x00,		/* Simple Queue Tag */
+	HQTag		= 0x40,		/* Head of Queue Tag */
+	OQTag		= 0x80,		/* Ordered Queue Tag */
+};
+
+enum {					/* CCB control */
+	NoDisc		= 0x08,		/* No disconnect */
+	NoUnd		= 0x10,		/* No underrrun error report */
+	NoData		= 0x20,		/* No data transfer */
+	NoStat		= 0x40,		/* No CCB status if zero */
+	NoIntr		= 0x80,		/* No Interrupts */
+};
+
+typedef struct {
+	int	port;			/* I/O port */
+	int	id;			/* adapter SCSI id */
+	int	bus;			/* 24 or 32 -bit */
+	int	irq;
+	int	wide;
+	Pcidev*	pcidev;
+	SDev*	sdev;
+	int	spurious;
+
+	Lock	issuelock;
+
+	Lock	ccblock;
+	QLock	ccbq;
+	Rendez	ccbr;
+
+	Lock	mboxlock;
+	void*	mb;			/* mailbox out + mailbox in */
+	int	mbox;			/* current mailbox out index into mb */
+	int	mbix;			/* current mailbox in index into mb */
+
+	Lock	cachelock;
+	Ccb*	ccb;			/* list of free Ccb's */
+	Ccb**	cache;			/* last completed Ccb */
+} Ctlr;
+
+/*
+ * The number of mailboxes should be a multiple of 8 (4 for Mbox32)
+ * to ensure the boundary between the out and in mailboxes doesn't
+ * straddle a cache-line boundary.
+ * The number of Ccb's should be less than the number of mailboxes to
+ * ensure no queueing is necessary on mailbox allocation.
+ */
+enum {
+	NMbox		= 8*8,		/* number of Mbox's */
+	NCcb		= NMbox-1,	/* number of Ccb's */
+};
+
+#define PADDR24(a, n)	((PADDR(a)+(n)) <= (1<<24))
+
+static void
+ccbfree(Ctlr* ctlr, Ccb* ccb)
+{
+	lock(&ctlr->ccblock);
+	if(ctlr->bus == 24)
+		((Ccb24*)ccb)->ccb = ctlr->ccb;
+	else
+		((Ccb32*)ccb)->ccb = ctlr->ccb;
+	if(ctlr->ccb == nil)
+		wakeup(&ctlr->ccbr);
+	ctlr->ccb = ccb;
+	unlock(&ctlr->ccblock);
+}
+
+static int
+ccbavailable(void* a)
+{
+	return ((Ctlr*)a)->ccb != nil;
+}
+
+static Ccb*
+ccballoc(Ctlr* ctlr)
+{
+	Ccb *ccb;
+
+	for(;;){
+		lock(&ctlr->ccblock);
+		if((ccb = ctlr->ccb) != nil){
+			if(ctlr->bus == 24)
+				 ctlr->ccb = ((Ccb24*)ccb)->ccb;
+			else
+				 ctlr->ccb = ((Ccb32*)ccb)->ccb;
+			unlock(&ctlr->ccblock);
+			break;
+		}
+
+		unlock(&ctlr->ccblock);
+		qlock(&ctlr->ccbq);
+		if(waserror()){
+			qunlock(&ctlr->ccbq);
+			continue;
+		}
+		sleep(&ctlr->ccbr, ccbavailable, ctlr);
+		qunlock(&ctlr->ccbq);
+		poperror();
+	}
+
+	return ccb;
+}
+
+static int
+done24(void* arg)
+{
+	return ((Ccb24*)arg)->done;
+}
+
+static int
+mylex24rio(SDreq* r)
+{
+	ulong p;
+	Ctlr *ctlr;
+	Ccb24 *ccb;
+	Mbox24 *mb;
+	uchar *data, lun, *sense;
+	int d, n, btstat, sdstat, target;
+
+	ctlr = r->unit->dev->ctlr;
+	target = r->unit->subno;
+	lun = (r->cmd[1]>>5) & 0x07;
+
+	/*
+	 * Ctlr->cache holds the last completed Ccb for this target if it
+	 * returned 'check condition'.
+	 * If this command is a request-sense and there is valid sense data
+	 * from the last completed Ccb, return it immediately.
+	 */
+	lock(&ctlr->cachelock);
+	if((ccb = ctlr->cache[target]) != nil){
+		ctlr->cache[target] = nil;
+		if(r->cmd[0] == 0x03
+		&& ccb->sdstat == SDcheck && lun == ((ccb->cs[1]>>5) & 0x07)){
+			unlock(&ctlr->cachelock);
+			if(r->dlen){
+				sense = &ccb->cs[ccb->cdblen];
+				n = 8+sense[7];
+				if(n > r->dlen)
+					n = r->dlen;
+				memmove(r->data, sense, n);
+				r->rlen = n;
+			}
+			ccbfree(ctlr, (Ccb*)ccb);
+			return SDok;
+		}
+	}
+	unlock(&ctlr->cachelock);
+	if(ccb == nil)
+		ccb = ccballoc(ctlr);
+
+	/*
+	 * Check if the transfer is to memory above the 24-bit limit the
+	 * controller can address. If it is, try to allocate a temporary
+	 * buffer as a staging area.
+	 */
+	n = r->dlen;
+	if(n && !PADDR24(r->data, n)){
+		data = mallocz(n, 0);
+		if(data == nil || !PADDR24(data, n)){
+			if(data != nil){
+				free(data);
+				ccb->data = nil;
+			}
+			ccbfree(ctlr, (Ccb*)ccb);
+			return SDmalloc;
+		}
+		if(r->write)
+			memmove(data, r->data, n);
+		ccb->data = r->data;
+	}
+	else
+		data = r->data;
+
+	/*
+	 * Fill in the ccb.
+	 */
+	ccb->opcode = Ordl;
+
+	ccb->datadir = (target<<5)|lun;
+	if(n == 0)
+		ccb->datadir |= CCBdataout|CCBdatain;
+	else if(!r->write)
+		ccb->datadir |= CCBdatain;
+	else
+		ccb->datadir |= CCBdataout;
+
+	ccb->cdblen = r->clen;
+	ccb->senselen = 0xFF;
+
+	ccb->datalen[0] = n>>16;
+	ccb->datalen[1] = n>>8;
+	ccb->datalen[2] = n;
+	p = PADDR(data);
+	ccb->dataptr[0] = p>>16;
+	ccb->dataptr[1] = p>>8;
+	ccb->dataptr[2] = p;
+
+	ccb->linkptr[0] = ccb->linkptr[1] = ccb->linkptr[2] = 0;
+	ccb->linkid = 0;
+	ccb->btstat = ccb->sdstat = 0;
+	ccb->reserved[0] = ccb->reserved[1] = 0;
+
+	memmove(ccb->cs, r->cmd, r->clen);
+
+	/*
+	 * There's one more mbox than there there is
+	 * ccb so there is always one free.
+	 */
+	lock(&ctlr->mboxlock);
+	mb = ctlr->mb;
+	mb += ctlr->mbox;
+	p = PADDR(ccb);
+	mb->ccb[0] = p>>16;
+	mb->ccb[1] = p>>8;
+	mb->ccb[2] = p;
+	mb->code = Mbostart;
+	ctlr->mbox++;
+	if(ctlr->mbox >= NMbox)
+		ctlr->mbox = 0;
+
+	/*
+	 * This command does not require Hardy
+	 * and doesn't generate a Cmdc interrupt.
+	 */
+	ccb->done = 0;
+	outb(ctlr->port+Rcpr, Cstart);
+	unlock(&ctlr->mboxlock);
+
+	/*
+	 * Wait for the request to complete and return the status.
+	 * Since the buffer is not reference counted cannot return
+	 * until the DMA is done writing into the buffer so the caller
+	 * cannot free the buffer prematurely.
+	 */
+	while(waserror())
+		;
+	tsleep(ccb, done24, ccb, 30*1000);
+	poperror();
+
+	if(!done24(ccb)){
+		print("%s: %d/%d: sd24rio timeout\n",
+			"sdmylex"/*ctlr->sdev->name*/, target, r->lun);
+		if(ccb->data != nil){
+			free(data);
+			ccb->data = nil;
+		}
+		ccbfree(ctlr, (Ccb*)ccb);
+
+		return SDtimeout;
+	}
+
+	/*
+	 * Save the status and patch up the number of
+	 * bytes actually transferred.
+	 * There's a firmware bug on some 956C controllers
+	 * which causes the return count from a successful
+	 * READ CAPACITY not be updated, so fix it here.
+	 */
+	sdstat = ccb->sdstat;
+	btstat = ccb->btstat;
+
+	d = ccb->datalen[0]<<16;
+	d |= ccb->datalen[1]<<8;
+	d |= ccb->datalen[2];
+	if(ccb->cs[0] == 0x25 && sdstat == SDok)
+		d = 0;
+	n -= d;
+	r->rlen = n;
+
+	/*
+	 * Tidy things up if a staging area was used for the data,
+	 */
+	if(ccb->data != nil){
+		if(sdstat == SDok && btstat == 0 && !r->write)
+			memmove(ccb->data, data, n);
+		free(data);
+		ccb->data = nil;
+	}
+
+	/*
+	 * If there was a check-condition, save the
+	 * ccb for a possible request-sense command.
+	 */
+	if(sdstat == SDcheck){
+		if(r->flags & SDnosense){
+			lock(&ctlr->cachelock);
+			if(ctlr->cache[target])
+				ccbfree(ctlr, ctlr->cache[target]);
+			ctlr->cache[target] = (Ccb*)ccb;
+			unlock(&ctlr->cachelock);
+			return SDcheck;
+		}
+		sense = &ccb->cs[ccb->cdblen];
+		n = 8+sense[7];
+		if(n > sizeof(r->sense)-1)
+			n = sizeof(r->sense)-1;
+		memmove(r->sense, sense, n);
+		r->flags |= SDvalidsense;
+	}
+	ccbfree(ctlr, (Ccb*)ccb);
+
+	if(btstat){
+		if(btstat == 0x11)
+			return SDtimeout;
+		return SDeio;
+	}
+	return sdstat;
+}
+
+static void
+mylex24interrupt(Ureg*, void* arg)
+{
+	ulong pa;
+	Ctlr *ctlr;
+	Ccb24 *ccb;
+	Mbox24 *mb, *mbox;
+	int port, rinterrupt, rstatus;
+
+	ctlr = arg;
+	port = ctlr->port;
+
+	/*
+	 * Save and clear the interrupt(s). The only
+	 * interrupts expected are Cmdc, which is ignored,
+	 * and Imbl which means something completed.
+	 * There's one spurious interrupt left over from
+	 * initialisation, ignore it.
+	 */
+	rinterrupt = inb(port+Rinterrupt);
+	rstatus = inb(port+Rstatus);
+	outb(port+Rcontrol, Rint);
+	if((rinterrupt & ~(Cmdc|Imbl)) != Intv && ctlr->spurious++)
+		print("%s: interrupt 0x%2.2ux\n",
+			"sdmylex"/*ctlr->sdev->name*/, rinterrupt);
+	if((rinterrupt & Cmdc) && (rstatus & Cmdinv))
+		print("%s: command invalid\n", "sdmylex"/*ctlr->sdev->name*/);
+
+	/*
+	 * Look for something in the mail.
+	 * If there is, save the status, free the mailbox
+	 * and wakeup whoever.
+	 */
+	mb = ctlr->mb;
+	for(mbox = &mb[ctlr->mbix]; mbox->code; mbox = &mb[ctlr->mbix]){
+		pa = (mbox->ccb[0]<<16)|(mbox->ccb[1]<<8)|mbox->ccb[2];
+		ccb = BPA2K(pa, BUSUNKNOWN);
+		mbox->code = 0;
+		ccb->done = 1;
+		wakeup(ccb);
+
+		ctlr->mbix++;
+		if(ctlr->mbix >= NMbox+NMbox)
+			ctlr->mbix = NMbox;
+	}
+}
+
+static int
+done32(void* arg)
+{
+	return ((Ccb32*)arg)->done;
+}
+
+static int
+mylex32rio(SDreq* r)
+{
+	ulong p;
+	uchar lun;
+	Ctlr *ctlr;
+	Ccb32 *ccb;
+	Mbox32 *mb;
+	int d, n, btstat, sdstat, target;
+
+	ctlr = r->unit->dev->ctlr;
+	target = r->unit->subno;
+	lun = (r->cmd[1]>>5) & 0x07;
+
+	/*
+	 * Ctlr->cache holds the last completed Ccb for this target if it
+	 * returned 'check condition'.
+	 * If this command is a request-sense and there is valid sense data
+	 * from the last completed Ccb, return it immediately.
+	 */
+	lock(&ctlr->cachelock);
+	if((ccb = ctlr->cache[target]) != nil){
+		ctlr->cache[target] = nil;
+		if(r->cmd[0] == 0x03
+		&& ccb->sdstat == SDcheck && lun == (ccb->luntag & 0x07)){
+			unlock(&ctlr->cachelock);
+			if(r->dlen){
+				n = 8+ccb->sense[7];
+				if(n > r->dlen)
+					n = r->dlen;
+				memmove(r->data, ccb->sense, n);
+				r->rlen = n;
+			}
+			ccbfree(ctlr, (Ccb*)ccb);
+			return SDok;
+		}
+	}
+	unlock(&ctlr->cachelock);
+	if(ccb == nil)
+		ccb = ccballoc(ctlr);
+
+	/*
+	 * Fill in the ccb.
+	 */
+	ccb->opcode = Ordl;
+
+	n = r->dlen;
+	if(n == 0)
+		ccb->datadir = CCBdataout|CCBdatain;
+	else if(!r->write)
+		ccb->datadir = CCBdatain;
+	else
+		ccb->datadir = CCBdataout;
+
+	ccb->cdblen = r->clen;
+
+	ccb->datalen[0] = n;
+	ccb->datalen[1] = n>>8;
+	ccb->datalen[2] = n>>16;
+	ccb->datalen[3] = n>>24;
+	p = PADDR(r->data);
+	ccb->dataptr[0] = p;
+	ccb->dataptr[1] = p>>8;
+	ccb->dataptr[2] = p>>16;
+	ccb->dataptr[3] = p>>24;
+
+	ccb->targetid = target;
+	ccb->luntag = lun;
+	if(r->unit->inquiry[7] & 0x02)
+		ccb->luntag |= SQTag|TagEnable;
+	memmove(ccb->cdb, r->cmd, r->clen);
+	ccb->btstat = ccb->sdstat = 0;
+	ccb->ccbctl = 0;
+
+	/*
+	 * There's one more mbox than there there is
+	 * ccb so there is always one free.
+	 */
+	lock(&ctlr->mboxlock);
+	mb = ctlr->mb;
+	mb += ctlr->mbox;
+	p = PADDR(ccb);
+	mb->ccb[0] = p;
+	mb->ccb[1] = p>>8;
+	mb->ccb[2] = p>>16;
+	mb->ccb[3] = p>>24;
+	mb->code = Mbostart;
+	ctlr->mbox++;
+	if(ctlr->mbox >= NMbox)
+		ctlr->mbox = 0;
+
+	/*
+	 * This command does not require Hardy
+	 * and doesn't generate a Cmdc interrupt.
+	 */
+	ccb->done = 0;
+	outb(ctlr->port+Rcpr, Cstart);
+	unlock(&ctlr->mboxlock);
+
+	/*
+	 * Wait for the request to complete and return the status.
+	 * Since the buffer is not reference counted cannot return
+	 * until the DMA is done writing into the buffer so the caller
+	 * cannot free the buffer prematurely.
+	 */
+	while(waserror())
+		;
+	tsleep(ccb, done32, ccb, 30*1000);
+	poperror();
+
+	if(!done32(ccb)){
+		print("%s: %d/%d: sd32rio timeout\n",
+			"sdmylex"/*ctlr->sdev->name*/, target, r->lun);
+		ccbfree(ctlr, (Ccb*)ccb);
+
+		return SDtimeout;
+	}
+
+	/*
+	 * Save the status and patch up the number of
+	 * bytes actually transferred.
+	 * There's a firmware bug on some 956C controllers
+	 * which causes the return count from a successful
+	 * READ CAPACITY not to be updated, so fix it here.
+	 */
+	sdstat = ccb->sdstat;
+	btstat = ccb->btstat;
+
+	d = ccb->datalen[0];
+	d |= (ccb->datalen[1]<<8);
+	d |= (ccb->datalen[2]<<16);
+	d |= (ccb->datalen[3]<<24);
+	if(ccb->cdb[0] == 0x25 && sdstat == SDok)
+		d = 0;
+	n -= d;
+	r->rlen = n;
+
+	/*
+	 * If there was a check-condition, save the
+	 * ccb for a possible request-sense command.
+	 */
+	if(sdstat == SDcheck){
+		if(r->flags & SDnosense){
+			lock(&ctlr->cachelock);
+			if(ctlr->cache[target])
+				ccbfree(ctlr, ctlr->cache[target]);
+			ctlr->cache[target] = (Ccb*)ccb;
+			unlock(&ctlr->cachelock);
+			return SDcheck;
+		}
+		n = 8+ccb->sense[7];
+		if(n > sizeof(r->sense)-1)
+			n = sizeof(r->sense)-1;
+		memmove(r->sense, ccb->sense, n);
+		r->flags |= SDvalidsense;
+	}
+	ccbfree(ctlr, (Ccb*)ccb);
+
+	if(btstat){
+		if(btstat == 0x11)
+			return SDtimeout;
+		return SDeio;
+	}
+	return sdstat;
+}
+
+static void
+mylex32interrupt(Ureg*, void* arg)
+{
+	ulong pa;
+	Ctlr *ctlr;
+	Ccb32 *ccb;
+	Mbox32 *mb, *mbox;
+	int port, rinterrupt, rstatus;
+
+	ctlr = arg;
+	port = ctlr->port;
+
+	/*
+	 * Save and clear the interrupt(s). The only
+	 * interrupts expected are Cmdc, which is ignored,
+	 * and Imbl which means something completed.
+	 * There's one spurious interrupt left over from
+	 * initialisation, ignore it.
+	 */
+	rinterrupt = inb(port+Rinterrupt);
+	rstatus = inb(port+Rstatus);
+	outb(port+Rcontrol, Rint);
+	if((rinterrupt & ~(Cmdc|Imbl)) != Intv && ctlr->spurious++)
+		print("%s: interrupt 0x%2.2ux\n",
+			"sdmylex"/*ctlr->sdev->name*/, rinterrupt);
+	if((rinterrupt & Cmdc) && (rstatus & Cmdinv))
+		print("%s: command invalid\n", "sdmylex"/*ctlr->sdev->name*/);
+
+	/*
+	 * Look for something in the mail.
+	 * If there is, free the mailbox and wakeup whoever.
+	 */
+	mb = ctlr->mb;
+	for(mbox = &mb[ctlr->mbix]; mbox->code; mbox = &mb[ctlr->mbix]){
+		pa = (mbox->ccb[3]<<24)
+		    |(mbox->ccb[2]<<16)
+		    |(mbox->ccb[1]<<8)
+		    |mbox->ccb[0];
+		if(ctlr->pcidev)
+			ccb = BPA2K(pa, ctlr->pcidev->tbdf);
+		else
+			ccb = BPA2K(pa, BUSUNKNOWN);
+		mbox->code = 0;
+		ccb->done = 1;
+		wakeup(ccb);
+
+		ctlr->mbix++;
+		if(ctlr->mbix >= NMbox+NMbox)
+			ctlr->mbix = NMbox;
+	}
+}
+
+static int
+mylexrio(SDreq* r)
+{
+	int subno;
+	Ctlr *ctlr;
+
+	subno = r->unit->subno;
+	ctlr = r->unit->dev->ctlr;
+	if(subno == ctlr->id || (!ctlr->wide && subno >= 8))
+		r->status = SDtimeout;
+	else if(ctlr->bus == 24)
+		r->status = mylex24rio(r);
+	else
+		r->status = mylex32rio(r);
+	return r->status;
+}
+
+/*
+ * Issue a command to a controller. The command and its length is
+ * contained in cmd and cmdlen. If any data is to be
+ * returned, datalen should be non-zero, and the returned data
+ * will be placed in data.
+ * If Cmdc is set, bail out, the invalid command will be handled
+ * when the interrupt is processed.
+ */
+static void
+issueio(int port, uchar* cmd, int cmdlen, uchar* data, int datalen)
+{
+	int len;
+
+	if(cmd[0] != Cstart && cmd[0] != Ceombri){
+		while(!(inb(port+Rstatus) & Hardy))
+			;
+	}
+	outb(port+Rcpr, cmd[0]);
+
+	len = 1;
+	while(len < cmdlen){
+		if(!(inb(port+Rstatus) & Cprbsy)){
+			outb(port+Rcpr, cmd[len]);
+			len++;
+		}
+		if(inb(port+Rinterrupt) & Cmdc)
+			return;
+	}
+
+	if(datalen){
+		len = 0;
+		while(len < datalen){
+			if(inb(port+Rstatus) & Dirrdy){
+				data[len] = inb(port+Rdatain);
+				len++;
+			}
+			if(inb(port+Rinterrupt) & Cmdc)
+				return;
+		}
+	}
+}
+
+/*
+ * Issue a command to a controller, wait for it to complete then
+ * try to reset the interrupt. Should only be called at initialisation.
+ */
+static int
+issue(Ctlr* ctlr, uchar* cmd, int cmdlen, uchar* data, int datalen)
+{
+	int port;
+	uchar rinterrupt, rstatus;
+	static Lock mylexissuelock;
+
+	port = ctlr->port;
+
+	ilock(&ctlr->issuelock);
+	issueio(port, cmd, cmdlen, data, datalen);
+
+	while(!((rinterrupt = inb(port+Rinterrupt)) & Cmdc))
+		;
+
+	rstatus = inb(port+Rstatus);
+	outb(port+Rcontrol, Rint);
+	iunlock(&ctlr->issuelock);
+
+	if((rinterrupt & Cmdc) && (rstatus & Cmdinv))
+		return 0;
+	return 1;
+}
+
+static SDev*
+mylexprobe(int port, int irq)
+{
+	SDev *sdev;
+	Ctlr *ctlr;
+	uchar cmd[6], data[256];
+	int clen, dlen, timeo;
+
+	if(ioalloc(port, 0x3, 0, "mylex") < 0)
+		return nil;
+	ctlr = nil;
+
+	/*
+	 * Attempt to hard-reset the board and reset
+	 * the SCSI bus. If the board state doesn't settle to
+	 * idle with mailbox initialisation required, either
+	 * it isn't a compatible board or it's broken.
+	 * If the controller has SCAM set this can take a while.
+	 */
+	if(getconf("*noscsireset") != nil)
+		outb(port+Rcontrol, Rhard);
+	else
+		outb(port+Rcontrol, Rhard|Rsbus);
+	for(timeo = 0; timeo < 100; timeo++){
+		if(inb(port+Rstatus) == (Inreq|Hardy))
+			break;
+		delay(100);
+	}
+	if(inb(port+Rstatus) != (Inreq|Hardy)){
+buggery:
+		if(ctlr != nil)
+			free(ctlr);
+		iofree(port);
+		return nil;
+	}
+
+	if((ctlr = malloc(sizeof(Ctlr))) == nil)
+		goto buggery;
+	ctlr->port = port;
+	ctlr->irq = irq;
+	ctlr->bus = 24;
+	ctlr->wide = 0;
+
+	/*
+	 * Try to determine if this is a 32-bit MultiMaster controller
+	 * by attempting to obtain the extended inquiry information;
+	 * this command is not implemented on Adaptec 154xx
+	 * controllers. If successful, the first byte of the returned
+	 * data is the host adapter bus type, 'E' for 32-bit EISA,
+	 * PCI and VLB buses.
+	 */
+	cmd[0] = Ciesi;
+	cmd[1] = 14;
+	clen = 2;
+	dlen = 256;
+	if(issue(ctlr, cmd, clen, data, dlen)){
+		if(data[0] == 'E')
+			ctlr->bus = 32;
+		ctlr->wide = data[0x0D] & 0x01;
+	}
+	else{
+		/*
+		 * Inconceivable though it may seem, a hard controller reset
+		 * is necessary here to clear out the command queue. Every
+		 * board seems to lock-up in a different way if you give an
+		 * invalid command and then try to clear out the
+		 * command/parameter and/or data-in register.
+		 * Soft reset doesn't do the job either. Fortunately no
+		 * serious initialisation has been done yet so there's nothing
+		 * to tidy up.
+		 */
+		outb(port+Rcontrol, Rhard);
+		for(timeo = 0; timeo < 100; timeo++){
+			if(inb(port+Rstatus) == (Inreq|Hardy))
+				break;
+			delay(100);
+		}
+		if(inb(port+Rstatus) != (Inreq|Hardy))
+			goto buggery;
+	}
+
+	/*
+	 * If the BIOS is enabled on the AHA-1542C/CF and BIOS options for
+	 * support of drives > 1Gb, dynamic scanning of the SCSI bus or more
+	 * than 2 drives under DOS 5.0 are enabled, the BIOS disables
+	 * accepting Cmbinit to protect against running with drivers which
+	 * don't support those options. In order to unlock the interface it
+	 * is necessary to read a lock-code using Cextbios and write it back
+	 * using Cmbienable; the lock-code is non-zero.
+	 */
+	cmd[0] = Cinquiry;
+	clen = 1;
+	dlen = 4;
+	if(issue(ctlr, cmd, clen, data, dlen) == 0)
+		goto buggery;
+	if(data[0] >= 0x43){
+		cmd[0] = Cextbios;
+		clen = 1;
+		dlen = 2;
+		if(issue(ctlr, cmd, clen, data, dlen) == 0)
+			goto buggery;
+
+		/*
+		 * Lock-code returned in data[1]. If it's non-zero write
+		 * it back along with bit 0 of byte 0 cleared to enable
+		 * mailbox initialisation.
+		 */
+		if(data[1]){
+			cmd[0] = Cmbienable;
+			cmd[1] = 0;
+			cmd[2] = data[1];
+			clen = 3;
+			if(issue(ctlr, cmd, clen, 0, 0) == 0)
+				goto buggery;
+		}
+	}
+
+	/*
+	 * Get the id, DMA and IRQ info from the board. This will
+	 * cause an interrupt which will hopefully not cause any
+	 * trouble because the interrupt number isn't known yet.
+	 * This is necessary as the DMA won't be set up if the
+	 * board has the BIOS disabled.
+	 *
+	 * If the IRQ is already known, this must be a 32-bit PCI
+	 * or EISA card, in which case the returned DMA and IRQ can
+	 * be ignored.
+	 */
+	cmd[0] = Cinquire;
+	clen = 1;
+	dlen = 3;
+	if(issue(ctlr, cmd, clen, data, dlen) == 0)
+		goto buggery;
+
+	ctlr->id = data[2] & 0x07;
+	if(ctlr->irq < 0){
+		switch(data[0]){		/* DMA Arbitration Priority */
+		case 0x80:			/* Channel 7 */
+			outb(0xD6, 0xC3);
+			outb(0xD4, 0x03);
+			break;
+		case 0x40:			/* Channel 6 */
+			outb(0xD6, 0xC2);
+			outb(0xD4, 0x02);
+			break;
+		case 0x20:			/* Channel 5 */
+			outb(0xD6, 0xC1);
+			outb(0xD4, 0x01);
+			break;
+		case 0x01:			/* Channel 0 */
+			outb(0x0B, 0xC0);
+			outb(0x0A, 0x00);
+			break;
+		default:
+			if(ctlr->bus == 24)
+				goto buggery;
+			break;
+		}
+	
+		switch(data[1]){		/* Interrupt Channel */
+		case 0x40:
+			ctlr->irq = 15;
+			break;
+		case 0x20:
+			ctlr->irq = 14;
+			break;
+		case 0x08:
+			ctlr->irq = 12;
+			break;
+		case 0x04:
+			ctlr->irq = 11;
+			break;
+		case 0x02:
+			ctlr->irq = 10;
+			break;
+		case 0x01:
+			ctlr->irq = 9;
+			break;
+		default:
+			goto buggery;
+		}
+	}
+
+	if((sdev = malloc(sizeof(SDev))) == nil)
+		goto buggery;
+	sdev->ifc = &sdmylexifc;
+	sdev->ctlr = ctlr;
+	ctlr->sdev = sdev;
+	if(!ctlr->wide)
+		sdev->nunit = 8;
+	else
+		sdev->nunit = 16;
+
+	return sdev;
+}
+
+static int mylexport[8] = {
+	0x330, 0x334, 0x230, 0x234, 0x130, 0x134, 0x000, 0x000,
+};
+
+static SDev*
+mylexpnp(void)
+{
+	Pcidev *p;
+	Ctlr *ctlr;
+	ISAConf isa;
+	int cfg, ctlrno, i, x;
+	SDev *sdev, *head, *tail;
+
+	p = nil;
+	head = tail = nil;
+	while(p = pcimatch(p, 0x104B, 0)){
+		if((sdev = mylexprobe(p->mem[0].bar & ~0x01, p->intl)) == nil)
+			continue;
+
+		ctlr = sdev->ctlr;
+		ctlr->pcidev = p;
+
+		if(head != nil)
+			tail->next = sdev;
+		else
+			head = sdev;
+		tail = sdev;
+	}
+
+	if(strncmp(KADDR(0xFFFD9), "EISA", 4) == 0){
+		for(cfg = 0x1000; cfg < MaxEISA*0x1000; cfg += 0x1000){
+			x = 0;
+			for(i = 0; i < 4; i++)
+				x |= inb(cfg+CfgEISA+i)<<(i*8);
+			if(x != 0x0142B30A && x != 0x0242B30A)
+				continue;
+	
+			x = inb(cfg+0xC8C);
+			if((sdev = mylexprobe(mylexport[x & 0x07], -1)) == nil)
+				continue;
+	
+			if(head != nil)
+				tail->next = sdev;
+			else
+				head = sdev;
+			tail = sdev;
+		}
+	}
+
+	for(ctlrno = 0; ctlrno < 4; ctlrno++){
+		memset(&isa, 0, sizeof(isa));
+		if(!isaconfig("scsi", ctlrno, &isa))
+			continue;
+		if(strcmp(isa.type, "aha1542"))
+			continue;
+		if((sdev = mylexprobe(isa.port, -1)) == nil)
+			continue;
+
+		if(head != nil)
+			tail->next = sdev;
+		else
+			head = sdev;
+		tail = sdev;
+	}
+
+	return head;
+}
+
+static SDev*
+mylexid(SDev* sdev)
+{
+	return scsiid(sdev, &sdmylexifc);
+}
+
+static int
+mylex24enable(Ctlr* ctlr)
+{
+	ulong p;
+	Ccb24 *ccb, *ccbp;
+	uchar cmd[6], *v;
+	int len;
+
+	len = (sizeof(Mbox24)*NMbox*2)+(sizeof(Ccb24)*NCcb);
+	v = xspanalloc(len, 32, 0);
+
+	if(!PADDR24(ctlr, sizeof(Ctlr)) || !PADDR24(v, len))
+		return 0;
+
+	ctlr->mb = v;
+	v += sizeof(Mbox24)*NMbox*2;
+
+	ccb = (Ccb24*)v;
+	for(ccbp = ccb; ccbp < &ccb[NCcb]; ccbp++){
+		ccbp->ccb = ctlr->ccb;
+		ctlr->ccb = (Ccb*)ccbp;
+	}
+
+	/*
+	 * Initialise the software controller and
+	 * set the board scanning the mailboxes.
+	 */
+	ctlr->mbix = NMbox;
+
+	cmd[0] = Cinitialise;
+	cmd[1] = NMbox;
+	p = K2BPA(ctlr->mb, BUSUNKNOWN);
+	cmd[2] = p>>16;
+	cmd[3] = p>>8;
+	cmd[4] = p;
+
+	return issue(ctlr, cmd, 5, 0, 0);
+}
+
+static int
+mylex32enable(Ctlr* ctlr)
+{
+	ulong p;
+	Ccb32 *ccb, *ccbp;
+	uchar cmd[6], *v;
+
+	v = xspanalloc((sizeof(Mbox32)*NMbox*2)+(sizeof(Ccb32)*NCcb), 32, 0);
+
+	ctlr->mb = v;
+	v += sizeof(Mbox32)*NMbox*2;
+
+	ccb = (Ccb32*)v;
+	for(ccbp = ccb; ccbp < &ccb[NCcb]; ccbp++){
+		/*
+		 * Fill in some stuff that doesn't change.
+		 */
+		ccbp->senselen = sizeof(ccbp->sense);
+		p = PADDR(ccbp->sense);
+		ccbp->senseptr[0] = p;
+		ccbp->senseptr[1] = p>>8;
+		ccbp->senseptr[2] = p>>16;
+		ccbp->senseptr[3] = p>>24;
+
+		ccbp->ccb = ctlr->ccb;
+		ctlr->ccb = (Ccb*)ccbp;
+	}
+
+	/*
+	 * Attempt wide mode setup.
+	 */
+	if(ctlr->wide){
+		cmd[0] = Cwide;
+		cmd[1] = 1;
+		if(!issue(ctlr, cmd, 2, 0, 0))
+			ctlr->wide = 0;
+	}
+
+	/*
+	 * Initialise the software controller and
+	 * set the board scanning the mailboxes.
+	 */
+	ctlr->mbix = NMbox;
+
+	cmd[0] = Ciem;
+	cmd[1] = NMbox;
+	if(ctlr->pcidev)
+		p = K2BPA(ctlr->mb, ctlr->tbdf);
+	else
+		p = K2BPA(ctlr->mb, BUSUNKNOWN);
+	cmd[2] = p;
+	cmd[3] = p>>8;
+	cmd[4] = p>>16;
+	cmd[5] = p>>24;
+
+	return issue(ctlr, cmd, 6, 0, 0);
+}
+
+static int
+mylexenable(SDev* sdev)
+{
+	int tbdf;
+	Ctlr *ctlr;
+	void (*interrupt)(Ureg*, void*);
+	char name[NAMELEN];
+
+	ctlr = sdev->ctlr;
+	if(ctlr->cache == nil){
+		if((ctlr->cache = malloc(sdev->nunit*sizeof(Ccb*))) == nil)
+			return 0;
+	}
+
+	tbdf = BUSUNKNOWN;
+	if(ctlr->bus == 32){
+		if(ctlr->pcidev){
+			tbdf = ctlr->pcidev->tbdf;
+			pcisetbme(ctlr->pcidev);
+		}
+		if(!mylex32enable(ctlr))
+			return 0;
+		interrupt = mylex32interrupt;
+	}
+	else if(mylex24enable(ctlr))
+		interrupt = mylex24interrupt;
+	else
+		return 0;
+
+	snprint(name, NAMELEN, "sd%c (%s)", sdev->idno, sdev->ifc->name);
+	intrenable(ctlr->irq, interrupt, ctlr, tbdf, name);
+
+	return 1;
+}
+
+static int
+mylexdisable(SDev* sdev)
+{
+	Ctlr *ctlr;
+	int port, timeo;
+
+	ctlr = sdev->ctlr;
+	port = ctlr->port;
+
+	if(getconf("*noscsireset") != nil)
+		outb(port+Rcontrol, Rhard);
+	else
+		outb(port+Rcontrol, Rhard|Rsbus);
+	for(timeo = 0; timeo < 100; timeo++){
+		if(inb(port+Rstatus) == (Inreq|Hardy))
+			break;
+		delay(100);
+	}
+	if(inb(port+Rstatus) != (Inreq|Hardy))
+		return 0;
+
+	return 1;
+}
+
+SDifc sdmylexifc = {
+	"mylex",			/* name */
+
+	mylexpnp,			/* pnp */
+	nil,				/* legacy */
+	mylexid,			/* id */
+	mylexenable,			/* enable */
+	mylexdisable,			/* disable */
+
+	scsiverify,			/* verify */
+	scsionline,			/* online */
+	mylexrio,			/* rio */
+	nil,				/* rctl */
+	nil,				/* wctl */
+
+	scsibio,			/* bio */
+};
--- /dev/null
+++ b/os/boot/pc/sdscsi.c
@@ -1,0 +1,376 @@
+#include "u.h"
+#include "lib.h"
+#include "mem.h"
+#include "dat.h"
+#include "fns.h"
+#include "io.h"
+#include "ureg.h"
+#include "error.h"
+
+#include "sd.h"
+
+static int
+scsitest(SDreq* r)
+{
+	r->write = 0;
+	memset(r->cmd, 0, sizeof(r->cmd));
+	r->cmd[1] = r->lun<<5;
+	r->clen = 6;
+	r->data = nil;
+	r->dlen = 0;
+	r->flags = 0;
+
+	r->status = ~0;
+
+// cgascreenputs("A", 1);
+	return r->unit->dev->ifc->rio(r);
+}
+
+int
+scsiverify(SDunit* unit)
+{
+	static SDreq *r;
+	int i, status;
+	static uchar *inquiry;
+
+	if((r = sdmalloc(r, sizeof(SDreq))) == nil)
+		return 0;
+
+	if((inquiry = sdmalloc(inquiry, sizeof(unit->inquiry))) == nil)
+		return 0;
+
+	r->unit = unit;
+	r->lun = 0;		/* ??? */
+
+	memset(unit->inquiry, 0, sizeof(unit->inquiry));
+	r->write = 0;
+	r->cmd[0] = 0x12;
+	r->cmd[1] = r->lun<<5;
+	r->cmd[4] = sizeof(unit->inquiry)-1;
+	r->clen = 6;
+	r->data = inquiry;
+	r->dlen = sizeof(unit->inquiry)-1;
+	r->flags = 0;
+
+	r->status = ~0;
+// cgascreenputs("B", 1);
+	if(unit->dev->ifc->rio(r) != SDok){
+		return 0;
+	}
+	memmove(unit->inquiry, inquiry, r->dlen);
+
+	SET(status);
+	for(i = 0; i < 3; i++){
+		while((status = scsitest(r)) == SDbusy)
+			;
+		if(status == SDok || status != SDcheck)
+			break;
+		if(!(r->flags & SDvalidsense))
+			break;
+		if((r->sense[2] & 0x0F) != 0x02)
+			continue;
+
+		/*
+		 * Unit is 'not ready'.
+		 * If it is in the process of becoming ready or needs
+		 * an initialising command, set status so it will be spun-up
+		 * below.
+		 * If there's no medium, that's OK too, but don't
+		 * try to spin it up.
+		 */
+		if(r->sense[12] == 0x04){
+			if(r->sense[13] == 0x02 || r->sense[13] == 0x01){
+				status = SDok;
+				break;
+			}
+		}
+		if(r->sense[12] == 0x3A)
+			break;
+	}
+
+	if(status == SDok){
+		/*
+		 * Try to ensure a direct-access device is spinning.
+		 * Ignore the result.
+		 */
+		if((unit->inquiry[0] & 0x1F) == 0){
+			memset(r->cmd, 0, sizeof(r->cmd));
+			r->write = 0;
+			r->cmd[0] = 0x1B;
+			r->cmd[1] = r->lun<<5;
+			r->cmd[4] = 1;
+			r->clen = 6;
+			r->data = nil;
+			r->dlen = 0;
+			r->flags = 0;
+
+			r->status = ~0;
+			unit->dev->ifc->rio(r);
+		}
+		return 1;
+	}
+	return 0;
+}
+
+int
+return0(void*)
+{
+	return 0;
+}
+
+static int
+scsirio(SDreq* r)
+{
+	/*
+	 * Perform an I/O request, returning
+	 *	-1	failure
+	 *	 0	ok
+	 *	 2	retry
+	 * The contents of r may be altered so the
+	 * caller should re-initialise if necesary.
+	 */
+	r->status = ~0;
+// cgascreenputs("C", 1);
+	switch(r->unit->dev->ifc->rio(r)){
+	default:
+		break;
+	case SDcheck:
+		if(!(r->flags & SDvalidsense))
+			return -1;
+		switch(r->sense[2] & 0x0F){
+		case 0x00:		/* no sense */
+		case 0x01:		/* recovered error */
+			return 2;
+		case 0x06:		/* check condition */
+			/*
+			 * 0x28 - not ready to ready transition,
+			 *	  medium may have changed.
+			 * 0x29 - power on or some type of reset.
+			 */
+			if(r->sense[12] == 0x28 && r->sense[13] == 0)
+				return 2;
+			if(r->sense[12] == 0x29)
+				return 2;
+			break;
+		case 0x02:		/* not ready */
+			/*
+			 * If no medium present, bail out.
+			 * If unit is becoming ready, rather than not
+			 * not ready, wait a little then poke it again. 							 */
+			if(r->sense[12] == 0x3A)
+				return -1;
+			if(r->sense[12] != 0x04 || r->sense[13] != 0x01)
+				return -1;
+
+			tsleep(nil, return0, 0, 500);
+			scsitest(r);
+			return 2;
+		default:
+			break;
+		}
+		break;
+	case SDok:
+		return 0;
+	}
+	return -1;
+}
+
+int
+scsionline(SDunit* unit)
+{
+	int ok;
+	static SDreq *r;
+	static uchar *p;
+
+	if((r = sdmalloc(r, sizeof(SDreq))) == nil)
+		return 0;
+
+	if((p = sdmalloc(p, 8)) == nil)
+		return 0;
+
+	ok = 0;
+
+	r->unit = unit;
+	r->lun = 0;				/* ??? */
+	for(;;){
+		/*
+		 * Read-capacity is mandatory for DA, WORM, CD-ROM and
+		 * MO. It may return 'not ready' if type DA is not
+		 * spun up, type MO or type CD-ROM are not loaded or just
+		 * plain slow getting their act together after a reset.
+		 */
+		r->write = 0;
+		memset(r->cmd, 0, sizeof(r->cmd));
+		r->cmd[0] = 0x25;
+		r->cmd[1] = r->lun<<5;
+		r->clen = 10;
+		r->data = p;
+		r->dlen = 8;
+		r->flags = 0;
+	
+		r->status = ~0;
+// cgascreenputs("F", 1);
+		switch(scsirio(r)){
+		default:
+			break;
+		case 0:
+			unit->sectors = (p[0]<<24)|(p[1]<<16)|(p[2]<<8)|p[3];
+			/*
+			 * Read-capacity returns the LBA of the last sector,
+			 * therefore the number of sectors must be incremented.
+			 */
+			unit->sectors++;
+			unit->secsize = (p[4]<<24)|(p[5]<<16)|(p[6]<<8)|p[7];
+			ok = 1;
+			break;
+		case 2:
+			continue;
+		}
+		break;
+	}
+
+	return ok;
+}
+
+int
+scsiexec(SDunit* unit, int write, uchar* cmd, int clen, void* data, int* dlen)
+{
+	static SDreq *r;
+	int status;
+
+	if((r = sdmalloc(r, sizeof(SDreq))) == nil)
+		return SDmalloc;
+
+	r->unit = unit;
+	r->lun = cmd[1]>>5;		/* ??? */
+	r->write = write;
+	memmove(r->cmd, cmd, clen);
+	r->clen = clen;
+	r->data = data;
+	if(dlen)
+		r->dlen = *dlen;
+	r->flags = 0;
+
+	r->status = ~0;
+
+	/*
+	 * Call the device-specific I/O routine.
+	 * There should be no calls to 'error()' below this
+	 * which percolate back up.
+	 */
+// cgascreenputs("D", 1);
+	switch(status = unit->dev->ifc->rio(r)){
+	case SDok:
+		if(dlen)
+			*dlen = r->rlen;
+		/*FALLTHROUGH*/
+	case SDcheck:
+		/*FALLTHROUGH*/
+	default:
+		/*
+		 * It's more complicated than this. There are conditions
+		 * which are 'ok' but for which the returned status code
+		 * is not 'SDok'.
+		 * Also, not all conditions require a reqsense, might
+		 * need to do a reqsense here and make it available to the
+		 * caller somehow.
+		 *
+		 * Mañana.
+		 */
+		break;
+	}
+
+	return status;
+}
+
+long
+scsibio(SDunit* unit, int lun, int write, void* data, long nb, long bno)
+{
+	static SDreq *r;
+	long rlen;
+
+	if((r = sdmalloc(r, sizeof(SDreq))) == nil)
+		return SDmalloc;
+
+	r->unit = unit;
+	r->lun = lun;
+again:
+	r->write = write;
+	if(write == 0)
+		r->cmd[0] = 0x28;
+	else
+		r->cmd[0] = 0x2A;
+	r->cmd[1] = (lun<<5);
+	r->cmd[2] = bno>>24;
+	r->cmd[3] = bno>>16;
+	r->cmd[4] = bno>>8;
+	r->cmd[5] = bno;
+	r->cmd[6] = 0;
+	r->cmd[7] = nb>>8;
+	r->cmd[8] = nb;
+	r->cmd[9] = 0;
+	r->clen = 10;
+	r->data = data;
+	r->dlen = nb*unit->secsize;
+	r->flags = 0;
+
+	r->status = ~0;
+// cgascreenputs("E", 1);
+	switch(scsirio(r)){
+	default:
+		rlen = -1;
+		break;
+	case 0:
+		rlen = r->rlen;
+		break;
+	case 2:
+		rlen = -1;
+		if(!(r->flags & SDvalidsense))
+			break;
+		switch(r->sense[2] & 0x0F){
+		default:
+			break;
+		case 0x06:		/* check condition */
+			/*
+			 * Check for a removeable media change.
+			 * If so, mark it and zap the geometry info
+			 * to force an online request.
+			 */
+			if(r->sense[12] != 0x28 || r->sense[13] != 0)
+				break;
+			if(unit->inquiry[1] & 0x80){
+				unit->sectors = 0;
+			}
+			break;
+		case 0x02:		/* not ready */
+			/*
+			 * If unit is becoming ready,
+			 * rather than not not ready, try again.
+			 */
+			if(r->sense[12] == 0x04 && r->sense[13] == 0x01)
+				goto again;
+			break;
+		}
+		break;
+	}
+
+	return rlen;
+}
+
+SDev*
+scsiid(SDev* sdev, SDifc* ifc)
+{
+	static char idno[16] = "0123456789abcdef";
+	static char *p = idno;
+
+	while(sdev){
+		if(sdev->ifc == ifc){
+			sdev->idno = *p++;
+			if(p >= &idno[sizeof(idno)])
+				break;
+		}
+		sdev = sdev->next;
+	}
+
+	return nil;
+}
--- /dev/null
+++ b/os/boot/pc/trap.c
@@ -1,0 +1,332 @@
+#include	"u.h"
+#include	"lib.h"
+#include	"mem.h"
+#include	"dat.h"
+#include	"fns.h"
+#include	"io.h"
+#include	"ureg.h"
+
+void	intr0(void), intr1(void), intr2(void), intr3(void);
+void	intr4(void), intr5(void), intr6(void), intr7(void);
+void	intr8(void), intr9(void), intr10(void), intr11(void);
+void	intr12(void), intr13(void), intr14(void), intr15(void);
+void	intr16(void);
+void	intr24(void), intr25(void), intr26(void), intr27(void);
+void	intr28(void), intr29(void), intr30(void), intr31(void);
+void	intr32(void), intr33(void), intr34(void), intr35(void);
+void	intr36(void), intr37(void), intr38(void), intr39(void);
+void	intr64(void);
+void	intrbad(void);
+
+/*
+ *  8259 interrupt controllers
+ */
+enum
+{
+	Int0ctl=	0x20,		/* control port (ICW1, OCW2, OCW3) */
+	Int0aux=	0x21,		/* everything else (ICW2, ICW3, ICW4, OCW1) */
+	Int1ctl=	0xA0,		/* control port */
+	Int1aux=	0xA1,		/* everything else (ICW2, ICW3, ICW4, OCW1) */
+
+	Icw1=		0x10,		/* select bit in ctl register */
+	Ocw2=		0x00,
+	Ocw3=		0x08,
+
+	EOI=		0x20,		/* non-specific end of interrupt */
+
+	Elcr1=		0x4D0,		/* Edge/Level Triggered Register */
+	Elcr2=		0x4D1,
+};
+
+int	int0mask = 0xff;	/* interrupts enabled for first 8259 */
+int	int1mask = 0xff;	/* interrupts enabled for second 8259 */
+int i8259elcr;				/* mask of level-triggered interrupts */
+
+/*
+ *  trap/interrupt gates
+ */
+Segdesc ilt[256];
+
+enum 
+{
+	Maxhandler=	32,		/* max number of interrupt handlers */
+};
+
+typedef struct Handler	Handler;
+struct Handler
+{
+	void	(*r)(Ureg*, void*);
+	void	*arg;
+	Handler	*next;
+};
+
+struct
+{
+	Handler	*ivec[256];
+	Handler	h[Maxhandler];
+	int	nextfree;
+} halloc;
+
+void
+sethvec(int v, void (*r)(void), int type, int pri)
+{
+	ilt[v].d0 = ((ulong)r)&0xFFFF|(KESEL<<16);
+	ilt[v].d1 = ((ulong)r)&0xFFFF0000|SEGP|SEGPL(pri)|type;
+}
+
+void
+setvec(int v, void (*r)(Ureg*, void*), void *arg)
+{
+	Handler *h;
+
+	if(halloc.nextfree >= Maxhandler)
+		panic("out of interrupt handlers");
+	h = &halloc.h[halloc.nextfree++];
+	h->next = halloc.ivec[v];
+	h->r = r;
+	h->arg = arg;
+	halloc.ivec[v] = h;
+
+	/*
+	 *  enable corresponding interrupt in 8259
+	 */
+	if((v&~0x7) == VectorPIC){
+		int0mask &= ~(1<<(v&7));
+		outb(Int0aux, int0mask);
+	} else if((v&~0x7) == VectorPIC+8){
+		int1mask &= ~(1<<(v&7));
+		outb(Int1aux, int1mask);
+	}
+}
+
+void
+trapdisable(void)
+{
+	outb(Int0aux, 0xFF);
+	outb(Int1aux, 0xFF);
+}
+
+void
+trapenable(void)
+{
+	outb(Int0aux, int0mask);
+	outb(Int1aux, int1mask);
+}
+
+
+/*
+ *  set up the interrupt/trap gates
+ */
+void
+trapinit(void)
+{
+	int i, x;
+
+	/*
+	 *  set all interrupts to panics
+	 */
+	for(i = 0; i < 256; i++)
+		sethvec(i, intrbad, SEGTG, 0);
+
+	/*
+	 *  80386 processor (and coprocessor) traps
+	 */
+	sethvec(0, intr0, SEGTG, 0);
+	sethvec(1, intr1, SEGTG, 0);
+	sethvec(2, intr2, SEGTG, 0);
+	sethvec(3, intr3, SEGTG, 0);
+	sethvec(4, intr4, SEGTG, 0);
+	sethvec(5, intr5, SEGTG, 0);
+	sethvec(6, intr6, SEGTG, 0);
+	sethvec(7, intr7, SEGTG, 0);
+	sethvec(8, intr8, SEGTG, 0);
+	sethvec(9, intr9, SEGTG, 0);
+	sethvec(10, intr10, SEGTG, 0);
+	sethvec(11, intr11, SEGTG, 0);
+	sethvec(12, intr12, SEGTG, 0);
+	sethvec(13, intr13, SEGTG, 0);
+	sethvec(14, intr14, SEGTG, 0);
+	sethvec(15, intr15, SEGTG, 0);
+	sethvec(16, intr16, SEGTG, 0);
+
+	/*
+	 *  device interrupts
+	 */
+	sethvec(24, intr24, SEGIG, 0);
+	sethvec(25, intr25, SEGIG, 0);
+	sethvec(26, intr26, SEGIG, 0);
+	sethvec(27, intr27, SEGIG, 0);
+	sethvec(28, intr28, SEGIG, 0);
+	sethvec(29, intr29, SEGIG, 0);
+	sethvec(30, intr30, SEGIG, 0);
+	sethvec(31, intr31, SEGIG, 0);
+	sethvec(32, intr32, SEGIG, 0);
+	sethvec(33, intr33, SEGIG, 0);
+	sethvec(34, intr34, SEGIG, 0);
+	sethvec(35, intr35, SEGIG, 0);
+	sethvec(36, intr36, SEGIG, 0);
+	sethvec(37, intr37, SEGIG, 0);
+	sethvec(38, intr38, SEGIG, 0);
+	sethvec(39, intr39, SEGIG, 0);
+
+	/*
+	 *  tell the hardware where the table is (and how long)
+	 */
+	putidt(ilt, sizeof(ilt)-1);
+
+	/*
+	 *  Set up the first 8259 interrupt processor.
+	 *  Make 8259 interrupts start at CPU vector VectorPIC.
+	 *  Set the 8259 as master with edge triggered
+	 *  input with fully nested interrupts.
+	 */
+	outb(Int0ctl, Icw1|0x01);	/* ICW1 - edge triggered, master,
+					   ICW4 will be sent */
+	outb(Int0aux, VectorPIC);	/* ICW2 - interrupt vector offset */
+	outb(Int0aux, 0x04);		/* ICW3 - have slave on level 2 */
+	outb(Int0aux, 0x01);		/* ICW4 - 8086 mode, not buffered */
+
+	/*
+	 *  Set up the second 8259 interrupt processor.
+	 *  Make 8259 interrupts start at CPU vector VectorPIC+8.
+	 *  Set the 8259 as master with edge triggered
+	 *  input with fully nested interrupts.
+	 */
+	outb(Int1ctl, Icw1|0x01);	/* ICW1 - edge triggered, master,
+					   ICW4 will be sent */
+	outb(Int1aux, VectorPIC+8);	/* ICW2 - interrupt vector offset */
+	outb(Int1aux, 0x02);		/* ICW3 - I am a slave on level 2 */
+	outb(Int1aux, 0x01);		/* ICW4 - 8086 mode, not buffered */
+	outb(Int1aux, int1mask);
+
+	/*
+	 *  pass #2 8259 interrupts to #1
+	 */
+	int0mask &= ~0x04;
+	outb(Int0aux, int0mask);
+
+	/*
+	 * Set Ocw3 to return the ISR when ctl read.
+	 */
+	outb(Int0ctl, Ocw3|0x03);
+	outb(Int1ctl, Ocw3|0x03);
+
+	/*
+	 * Check for Edge/Level register.
+	 * This check may not work for all chipsets.
+	 * First try a non-intrusive test - the bits for
+	 * IRQs 13, 8, 2, 1 and 0 must be edge (0). If
+	 * that's OK try a R/W test.
+	 */
+	x = (inb(Elcr2)<<8)|inb(Elcr1);
+	if(!(x & 0x2107)){
+		outb(Elcr1, 0);
+		if(inb(Elcr1) == 0){
+			outb(Elcr1, 0x20);
+			if(inb(Elcr1) == 0x20)
+				i8259elcr = x;
+			outb(Elcr1, x & 0xFF);
+			print("ELCR: %4.4uX\n", i8259elcr);
+		}
+	}
+}
+
+/*
+ *  dump registers
+ */
+static void
+dumpregs(Ureg *ur)
+{
+	print("FLAGS=%lux TRAP=%lux ECODE=%lux PC=%lux\n",
+		ur->flags, ur->trap, ur->ecode, ur->pc);
+	print("  AX %8.8lux  BX %8.8lux  CX %8.8lux  DX %8.8lux\n",
+		ur->ax, ur->bx, ur->cx, ur->dx);
+	print("  SI %8.8lux  DI %8.8lux  BP %8.8lux\n",
+		ur->si, ur->di, ur->bp);
+	print("  CS %4.4lux DS %4.4lux  ES %4.4lux  FS %4.4lux  GS %4.4lux\n",
+		ur->cs & 0xFF, ur->ds & 0xFFFF, ur->es & 0xFFFF, ur->fs & 0xFFFF, ur->gs & 0xFFFF);
+	print("  CR0 %8.8lux CR2 %8.8lux CR3 %8.8lux\n",
+		getcr0(), getcr2(), getcr3());
+}
+
+/*
+ *  All traps
+ */
+void
+trap(Ureg *ur)
+{
+	int v;
+	int c;
+	Handler *h;
+	ushort isr;
+
+	v = ur->trap;
+	/*
+	 *  tell the 8259 that we're done with the
+	 *  highest level interrupt (interrupts are still
+	 *  off at this point)
+	 */
+	c = v&~0x7;
+	isr = 0;
+	if(c==VectorPIC || c==VectorPIC+8){
+		isr = inb(Int0ctl);
+		outb(Int0ctl, EOI);
+		if(c == VectorPIC+8){
+			isr |= inb(Int1ctl)<<8;
+			outb(Int1ctl, EOI);
+		}
+	}
+
+	if(v>=256 || (h = halloc.ivec[v]) == 0){
+		if(v >= VectorPIC && v < VectorPIC+16){
+			v -= VectorPIC;
+			/*
+			 * Check for a default IRQ7. This can happen when
+			 * the IRQ input goes away before the acknowledge.
+			 * In this case, a 'default IRQ7' is generated, but
+			 * the corresponding bit in the ISR isn't set.
+			 * In fact, just ignore all such interrupts.
+			 */
+			if(isr & (1<<v))
+				print("unknown interrupt %d pc=0x%lux\n", v, ur->pc);
+			return;
+		}
+
+		switch(v){
+
+		case 0x02:				/* NMI */
+			print("NMI: nmisc=0x%2.2ux, nmiertc=0x%2.2ux, nmiesc=0x%2.2ux\n",
+				inb(0x61), inb(0x70), inb(0x461));
+			return;
+
+		default:
+			dumpregs(ur);
+			panic("exception/interrupt %d", v);
+			return;
+		}
+	}
+
+	/*
+	 *  call the trap routines
+	 */
+	do {
+		(*h->r)(ur, h->arg);
+		h = h->next;
+	} while(h);
+}
+
+extern void realmode0(void);	/* in l.s */
+
+extern int realmodeintr;
+extern Ureg realmoderegs;
+
+void
+realmode(int intr, Ureg *ureg)
+{
+	realmoderegs = *ureg;
+	realmodeintr = intr;
+	trapdisable();
+	realmode0();
+	trapenable();
+	*ureg = realmoderegs;
+}
--- /dev/null
+++ b/os/boot/pc/ureg.h
@@ -1,0 +1,27 @@
+typedef struct Ureg	Ureg;
+
+struct Ureg
+{
+	ulong	di;		/* general registers */
+	ulong	si;		/* ... */
+	ulong	bp;		/* ... */
+	ulong	nsp;
+	ulong	bx;		/* ... */
+	ulong	dx;		/* ... */
+	ulong	cx;		/* ... */
+	ulong	ax;		/* ... */
+	ulong	gs;		/* data segments */
+	ulong	fs;		/* ... */
+	ulong	es;		/* ... */
+	ulong	ds;		/* ... */
+	ulong	trap;		/* trap type */
+	ulong	ecode;		/* error code (or zero) */
+	ulong	pc;		/* pc */
+	ulong	cs;		/* old context */
+	ulong	flags;		/* old flags */
+	union {
+		ulong	usp;
+		ulong	sp;
+	};
+	ulong	ss;		/* old stack segment */
+};
--- /dev/null
+++ b/os/boot/pc/x16.h
@@ -1,0 +1,159 @@
+/*
+ * Can't write 16-bit code for 8a without getting into
+ * lots of bother, so define some simple commands and
+ * output the code directly.
+ * 
+ * N.B. CALL16(x) kills DI, so don't expect it to be
+ * saved across calls.
+ */
+#define rAX		0		/* rX  */
+#define rCX		1
+#define rDX		2
+#define rBX		3
+#define rSP		4		/* SP */
+#define rBP		5		/* BP */
+#define rSI		6		/* SI */
+#define rDI		7		/* DI */
+
+#define rAL		0		/* rL  */
+#define rCL		1
+#define rDL		2
+#define rBL		3
+#define rAH		4		/* rH */
+#define rCH		5
+#define rDH		6
+#define rBH		7
+
+#define rES		0		/* rS */
+#define rCS		1
+#define rSS		2
+#define rDS		3
+#define rFS		4
+#define rGS		5
+
+#define xSI		4		/* rI (index) */
+#define xDI		5
+#define xBP		6
+#define xBX		7
+
+#define rCR0		0		/* rC */
+#define rCR2		2
+#define rCR3		3
+#define rCR4		4
+
+#define OP(o, m, ro, rm)	BYTE $o;	/* op + modr/m byte */	\
+			BYTE $(((m)<<6)|((ro)<<3)|(rm))
+#define OPrm(o, r, m)	OP(o, 0x00, r, 0x06);	/* general r <-> m */	\
+			WORD $m;
+#define OPrr(o, r0, r1)	OP(o, 0x03, r0, r1);	/* general r -> r */
+
+#define LW(m, rX)	OPrm(0x8B, rX, m)	/* m -> rX */
+#define LXW(x, rI, r)	OP(0x8B, 0x02, r, rI);	/* x(rI) -> r */	\
+			WORD $x
+#define LBPW(x, r)	OP(0x8B, 0x02, r, xBP);	/* x(rBP) -> r */	\
+			WORD $x
+#define LB(m, rB)	OPrm(0x8A, rB, m)	/* m -> r[HL] */
+#define LXB(x, rI, r)	OP(0x8A, 0x01, r, rI);	/* x(rI) -> r */	\
+			BYTE $x
+#define LBPB(x, r)	OP(0x8A, 0x01, r, xBP);	/* x(rBP) -> r */	\
+			BYTE $x
+#define SW(rX, m)	OPrm(0x89, rX, m)	/* rX -> m */
+#define SXW(r, x, rI)	OP(0x89, 0x02, r, rI);	/* r -> x(rI) */	\
+			WORD $x
+#define SBPW(r, x)	OP(0x89, 0x02, r, xBP);	/* r -> x(rBP) */	\
+			WORD $(x)
+#define SBPWI(i, x)	OP(0xC7, 0x01, 0, xBP);	/* i -> x(rBP) */	\
+			BYTE $(x); WORD $(i)
+#define STB(rB, m)	OPrm(0x88, rB, m)	/* rB -> m */
+#define SXB(r, x, rI)	OP(0x88, 0x01, r, rI);	/* rB -> x(rI) */	\
+			BYTE $x
+#define SBPB(r, x)	OP(0x88, 0x01, r, xBP);	/* r -> x(rBP) */	\
+			BYTE $x
+#define SBPBI(i, x)	OP(0xC6, 0x01, 0, xBP);	/* i -> x(rBP) */	\
+			BYTE $(x); BYTE $(i)
+#define LWI(i, rX)	BYTE $(0xB8+rX);	/* i -> rX */		\
+			WORD $i;
+#define LBI(i, rB)	BYTE $(0xB0+rB);	/* i -> r[HL] */	\
+			BYTE $i
+
+#define MW(r0, r1)	OPrr(0x89, r0, r1)	/* r0 -> r1 */
+#define MFSR(rS, rX)	OPrr(0x8C, rS, rX)	/* rS -> rX */
+#define MTSR(rX, rS)	OPrr(0x8E, rS, rX)	/* rX -> rS */
+#define MFCR(rC, rX)	BYTE $0x0F;		/* rC -> rX */		\
+			OP(0x20, 0x03, rC, rX)
+#define MTCR(rX, rC)	BYTE $0x0F;		/* rX -> rC */		\
+			OP(0x22, 0x03, rC, rX)
+
+#define ADC(r0, r1)	OPrr(0x11, r0, r1)	/* r0 + r1 -> r1 */
+#define ADD(r0, r1)	OPrr(0x01, r0, r1)	/* r0 + r1 -> r1 */
+#define ADDI(i, r)	OP(0x81, 0x03, 0x00, r);/* i+r -> r */		\
+			WORD $i;
+#define AND(r0, r1)	OPrr(0x21, r0, r1)	/* r0&r1 -> r1 */
+#define ANDI(i, r)	OP(0x81, 0x03, 0x04, r);/* i&r -> r */		\
+			WORD $i;
+#define CLR(r)		OPrr(0x31, r, r)	/* r^r -> r */
+#define CLRB(r)		OPrr(0x30, r, r)	/* r^r -> r */
+#define CMP(r0, r1)	OPrr(0x39, r0, r1)	/* r1-r0 -> flags */
+#define CMPI(i, r)	OP(0x81, 0x03, 0x07, r);/* r-i -> flags */	\
+			WORD $i;
+#define CMPBR(r0, r1)	OPrr(0x38, r0, r1)	/* r1-r0 -> flags */
+#define DEC(r)		BYTE $(0x48|r)		/* r-1 -> r */
+#define DIV(r)		OPrr(0xF7, 0x06, r)	/* rDX:rAX/r -> rAX, rDX:rAX%r -> rDX */
+#define INC(r)		BYTE $(0x40|r)		/* r+1 -> r */
+#define MUL(r)		OPrr(0xF7, 0x04, r)	/* r*rAX -> rDX:rAX */
+#define IMUL(r0, r1)	BYTE $0x0F;		/* r0*r1 -> r1 */	\
+			OPrr(0xAF, r1, r0)	/* (signed) */
+#define OR(r0, r1)	OPrr(0x09, r0, r1)	/* r0|r1 -> r1 */
+#define ORB(r0, r1)	OPrr(0x08, r0, r1)	/* r0|r1 -> r1 */
+#define ORI(i, r)	OP(0x81, 0x03, 0x01, r);/* i|r -> r */		\
+			WORD $i;
+#define ROLI(i, r)	OPrr(0xC1, 0x00, r);	/* r<<>>i -> r */	\
+			BYTE $i;
+#define SHLI(i, r)	OPrr(0xC1, 0x04, r);	/* r<<i -> r */		\
+			BYTE $i;
+#define SHLBI(i, r)	OPrr(0xC0, 0x04, r);	/* r<<i -> r */		\
+			BYTE $i;
+#define SHRI(i, r)	OPrr(0xC1, 0x05, r);	/* r>>i -> r */		\
+			BYTE $i;
+#define SHRBI(i, r)	OPrr(0xC0, 0x05, r);	/* r>>i -> r */		\
+			BYTE $i;
+#define SUB(r0, r1)	OPrr(0x29, r0, r1)	/* r1-r0 -> r1 */
+#define SUBI(i, r)	OP(0x81, 0x03, 0x05, r);/* r-i -> r */		\
+			WORD $i;
+
+#define STOSW		STOSL
+
+#define CALL16(f)	LWI(f, rDI);		/* &f -> rDI */		\
+			BYTE $0xFF;		/* (*rDI) */		\
+			BYTE $0xD7;
+#define FARJUMP16(s, o)	BYTE $0xEA;		/* jump to ptr16:16 */	\
+			WORD $o; WORD $s
+#define FARJUMP32(s, o)	BYTE $0x66;		/* jump to ptr32:16 */	\
+			BYTE $0xEA; LONG $o; WORD $s
+#define	DELAY		BYTE $0xEB;		/* jmp .+2 */		\
+			BYTE $0x00
+#define BIOSCALL(b)	INT $b			/* INT $b */
+
+#define PEEKW		BYTE $0x26;		/* MOVW	rES:[rBX], rAX  */	\
+			BYTE $0x8B; BYTE $0x07
+#define POKEW		BYTE $0x26;		/* MOVW	rAX, rES:[rBX] */	\
+			BYTE $0x89; BYTE $0x07
+#define OUTPORTB(p, d)	LBI(d, rAL);		/* d -> I/O port p */	\
+			BYTE $0xE6;					\
+			BYTE $p; DELAY
+#define PUSHA		BYTE $0x60
+#define PUSHR(r)	BYTE $(0x50|r)		/* r  -> (--rSP) */
+#define PUSHS(rS)	BYTE $(0x06|((rS)<<3))	/* rS  -> (--rSP) */
+#define PUSHI(i)	BYTE $0x68; WORD $i;	/* i -> --(rSP) */
+#define POPA		BYTE $0x61
+#define POPR(r)		BYTE $(0x58|r)		/* (rSP++) -> r */
+#define POPS(rS)	BYTE $(0x07|((rS)<<3))	/* (rSP++) -> r */
+#define NOP		BYTE $0x90		/* nop */
+
+#define LGDT(gdtptr)	BYTE $0x0F;		/* LGDT */			\
+			BYTE $0x01; BYTE $0x16;					\
+			WORD $gdtptr
+
+/* operand size switch. */
+#define OPSIZE		BYTE $0x66
+
--- /dev/null
+++ b/os/boot/puma/8250.c
@@ -1,0 +1,312 @@
+#include "boot.h"
+
+/*
+ *  INS8250 uart
+ */
+enum
+{
+	/*
+	 *  register numbers
+	 */
+	Data=	0,		/* xmit/rcv buffer */
+	Iena=	1,		/* interrupt enable */
+	 Ircv=	(1<<0),		/*  for char rcv'd */
+	 Ixmt=	(1<<1),		/*  for xmit buffer empty */
+	 Irstat=(1<<2),		/*  for change in rcv'er status */
+	 Imstat=(1<<3),		/*  for change in modem status */
+	Istat=	2,		/* interrupt flag (read) */
+	Tctl=	2,		/* test control (write) */
+	Format=	3,		/* byte format */
+	 Bits8=	(3<<0),		/*  8 bits/byte */
+	 Stop2=	(1<<2),		/*  2 stop bits */
+	 Pena=	(1<<3),		/*  generate parity */
+	 Peven=	(1<<4),		/*  even parity */
+	 Pforce=(1<<5),		/*  force parity */
+	 Break=	(1<<6),		/*  generate a break */
+	 Dra=	(1<<7),		/*  address the divisor */
+	Mctl=	4,		/* modem control */
+	 Dtr=	(1<<0),		/*  data terminal ready */
+	 Rts=	(1<<1),		/*  request to send */
+	 Ri=	(1<<2),		/*  ring */
+	 Inton=	(1<<3),		/*  turn on interrupts */
+	 Loop=	(1<<4),		/*  loop back */
+	Lstat=	5,		/* line status */
+	 Inready=(1<<0),	/*  receive buffer full */
+	 Oerror=(1<<1),		/*  receiver overrun */
+	 Perror=(1<<2),		/*  receiver parity error */
+	 Ferror=(1<<3),		/*  rcv framing error */
+	 Outready=(1<<5),	/*  output buffer empty */
+	Mstat=	6,		/* modem status */
+	 Ctsc=	(1<<0),		/*  clear to send changed */
+	 Dsrc=	(1<<1),		/*  data set ready changed */
+	 Rire=	(1<<2),		/*  rising edge of ring indicator */
+	 Dcdc=	(1<<3),		/*  data carrier detect changed */
+	 Cts=	(1<<4),		/*  complement of clear to send line */
+	 Dsr=	(1<<5),		/*  complement of data set ready line */
+	 Ring=	(1<<6),		/*  complement of ring indicator line */
+	 Dcd=	(1<<7),		/*  complement of data carrier detect line */
+	Scratch=7,		/* scratchpad */
+	Dlsb=	0,		/* divisor lsb */
+	Dmsb=	1,		/* divisor msb */
+
+	Serial=	0,
+	Modem=	1,
+};
+
+typedef struct Uart	Uart;
+struct Uart
+{
+	int	port;
+	int	setup;
+	uchar	sticky[8];	/* sticky write register values */
+	uchar	txbusy;
+
+	Queue	*iq;
+	Queue	*oq;
+	void	(*rx)(Queue *, int);
+
+	ulong	frame;
+	ulong	overrun;
+};
+
+Uart	uart[1];
+
+static	void	uartkick(void*);
+
+
+#define UartFREQ 1843200
+
+#define uartwrreg(u,r,v)	outb((u)->port + r, (u)->sticky[r] | (v))
+#define uartrdreg(u,r)		inb((u)->port + r)
+
+/*
+ *  set the baud rate by calculating and setting the baudrate
+ *  generator constant.  This will work with fairly non-standard
+ *  baud rates.
+ */
+static void
+uartsetbaud(Uart *up, int rate)
+{
+	ulong brconst;
+
+	brconst = (UartFREQ+8*rate-1)/(16*rate);
+
+	uartwrreg(up, Format, Dra);
+	outb(up->port+Dmsb, (brconst>>8) & 0xff);
+	outb(up->port+Dlsb, brconst & 0xff);
+	uartwrreg(up, Format, 0);
+}
+
+/*
+ *  toggle DTR
+ */
+static void
+uartdtr(Uart *up, int n)
+{
+	if(n)
+		up->sticky[Mctl] |= Dtr;
+	else
+		up->sticky[Mctl] &= ~Dtr;
+	uartwrreg(up, Mctl, 0);
+}
+
+/*
+ *  toggle RTS
+ */
+static void
+uartrts(Uart *up, int n)
+{
+	if(n)
+		up->sticky[Mctl] |= Rts;
+	else
+		up->sticky[Mctl] &= ~Rts;
+	uartwrreg(up, Mctl, 0);
+}
+
+static void
+uartintr(Ureg*, void *arg)
+{
+	Uart *up;
+	int ch;
+	int s, l, loops;
+
+	up = arg;
+	for(loops = 0; loops < 1024; loops++){
+		s = uartrdreg(up, Istat);
+		switch(s){
+		case 6:	/* receiver line status */
+			l = uartrdreg(up, Lstat);
+			if(l & Ferror)
+				up->frame++;
+			if(l & Oerror)
+				up->overrun++;
+			break;
+	
+		case 4:	/* received data available */
+		case 12:
+			ch = inb(up->port+Data);
+			if(up->iq)
+				if(up->rx)
+					(*up->rx)(up->iq, ch);
+				else
+					qbputc(up->iq, ch);
+			break;
+	
+		case 2:	/* transmitter empty */
+			ch = -1;
+			if(up->oq)
+				ch = qbgetc(up->oq);
+			if(ch != -1)
+				outb(up->port+Data, ch);
+			else
+				up->txbusy = 0;
+			break;
+	
+		case 0:	/* modem status */
+			uartrdreg(up, Mstat);
+			break;
+	
+		default:
+			if(s&1)
+				return;
+			print("weird modem interrupt #%2.2ux\n", s);
+			break;
+		}
+	}
+	panic("uartintr: 0x%2.2ux\n", uartrdreg(up, Istat));
+}
+
+/*
+ *  turn on a port's interrupts.  set DTR and RTS
+ */
+static void
+uartenable(Uart *up)
+{
+	/*
+ 	 *  turn on interrupts
+	 */
+	up->sticky[Iena] = 0;
+	if(up->oq)
+		up->sticky[Iena] |= Ixmt;
+	if(up->iq)
+		up->sticky[Iena] |= Ircv|Irstat;
+	uartwrreg(up, Iena, 0);
+
+	/*
+	 *  turn on DTR and RTS
+	 */
+	uartdtr(up, 1);
+	uartrts(up, 1);
+}
+
+void
+uartspecial(int port, int baud, Queue **iq, Queue **oq, void (*rx)(Queue *, int))
+{
+	Uart *up = &uart[0];
+
+	if(up->setup)
+		return;
+	up->setup = 1;
+
+	*iq = up->iq = qopen(4*1024, 0, 0, 0);
+	*oq = up->oq = qopen(16*1024, 0, uartkick, up);
+	switch(port){
+
+	case 0:
+		up->port = 0x3F8;
+		setvec(V_COM1, uartintr, up);
+		break;
+
+	case 1:
+		up->port = 0x2F8;
+		setvec(V_COM2, uartintr, up);
+		break;
+
+	default:
+		return;
+	}
+
+	/*
+	 *  set rate to 9600 baud.
+	 *  8 bits/character.
+	 *  1 stop bit.
+	 *  interrupts enabled.
+	 */
+	uartsetbaud(up, 9600);
+	up->sticky[Format] = Bits8;
+	uartwrreg(up, Format, 0);
+	up->sticky[Mctl] |= Inton;
+	uartwrreg(up, Mctl, 0x0);
+
+	up->rx = rx;
+	uartenable(up);
+	if(baud)
+		uartsetbaud(up, baud);
+}
+
+static void
+uartputc(int c)
+{
+	Uart *up = &uart[0];
+	int i;
+
+	for(i = 0; i < 100; i++){
+		if(uartrdreg(up, Lstat) & Outready)
+			break;
+		delay(1);
+	}
+	outb(up->port+Data, c);
+}
+
+void
+uartputs(char *s, int n)
+{
+	Uart *up = &uart[0];
+	Block *b;
+	int nl;
+	char *p;
+
+	nl = 0;
+	for(p = s; p < s+n; p++)
+		if(*p == '\n')
+			nl++;
+	b = iallocb(n+nl);
+	while(n--){
+		if(*s == '\n')
+			*b->wp++ = '\r';
+		*b->wp++ = *s++;
+	}
+	qbwrite(up->oq, b);
+}
+
+/*
+ *  (re)start output
+ */
+static void
+uartkick(void *arg)
+{
+	Uart *up = arg;
+	int x, n, c;
+
+	x = splhi();
+	while(up->txbusy == 0 && (c = qbgetc(up->oq)) != -1) {
+		n = 0;
+		while((uartrdreg(up, Lstat) & Outready) == 0){
+			if(++n > 100000){
+				print("stuck serial line\n");
+				break;
+			}
+		}
+			outb(up->port + Data, c);
+	}
+	splx(x);
+}
+
+void
+uartwait(void)
+{
+	Uart *up = &uart[0];
+
+	while(up->txbusy)
+		;
+}
--- /dev/null
+++ b/os/boot/puma/alarm.c
@@ -1,0 +1,123 @@
+#include	"u.h"
+#include	"lib.h"
+#include	"mem.h"
+#include	"dat.h"
+#include	"fns.h"
+#include	"io.h"
+#define	MAXALARM	10
+
+Alarm	alarmtab[MAXALARM];
+
+/*
+ * Insert new into list after where
+ */
+void
+insert(List **head, List *where, List *new)
+{
+	if(where == 0){
+		new->next = *head;
+		*head = new;
+	}else{
+		new->next = where->next;
+		where->next = new;
+	}
+		
+}
+
+/*
+ * Delete old from list.  where->next is known to be old.
+ */
+void
+delete(List **head, List *where, List *old)
+{
+	if(where == 0){
+		*head = old->next;
+		return;
+	}
+	where->next = old->next;
+}
+
+Alarm*
+newalarm(void)
+{
+	int i;
+	Alarm *a;
+
+	for(i=0,a=alarmtab; i < nelem(alarmtab); i++,a++)
+		if(a->busy==0 && a->f==0){
+			a->f = 0;
+			a->arg = 0;
+			a->busy = 1;
+			return a;
+		}
+	panic("newalarm");
+	return 0;	/* not reached */
+}
+
+Alarm*
+alarm(int ms, void (*f)(Alarm*), void *arg)
+{
+	Alarm *a, *w, *pw;
+	ulong s;
+
+	if(ms < 0)
+		ms = 0;
+	s = splhi();
+	a = newalarm();
+	a->dt = MS2TK(ms);
+	a->f = f;
+	a->arg = arg;
+	pw = 0;
+	for(w=m->alarm; w; pw=w, w=w->next){
+		if(w->dt <= a->dt){
+			a->dt -= w->dt;
+			continue;
+		}
+		w->dt -= a->dt;
+		break;
+	}
+	insert(&m->alarm, pw, a);
+	splx(s);
+	return a;
+}
+
+void
+cancel(Alarm *a)
+{
+	a->f = 0;
+}
+
+void
+alarminit(void)
+{
+}
+
+#define NA 10		/* alarms per clock tick */
+void
+checkalarms(void)
+{
+	int i, n, s;
+	Alarm *a;
+	void (*f)(void*);
+	Alarm *alist[NA];
+
+	s = splhi();
+	a = m->alarm;
+	if(a){
+		for(n=0; a && a->dt<=0 && n<NA; n++){
+			alist[n] = a;
+			delete(&m->alarm, 0, a);
+			a = m->alarm;
+		}
+		if(a)
+			a->dt--;
+
+		for(i = 0; i < n; i++){
+			f = alist[i]->f;	/* avoid race with cancel */
+			if(f)
+				(*f)(alist[i]);
+			alist[i]->busy = 0;
+		}
+	}
+	splx(s);
+}
--- /dev/null
+++ b/os/boot/puma/armv4.h
@@ -1,0 +1,99 @@
+/*
+ * PSR
+ */
+#define PsrMusr		0x10 	/* mode */
+#define PsrMfiq		0x11 
+#define PsrMirq		0x12
+#define PsrMsvc		0x13
+#define PsrMabt		0x17
+#define PsrMund		0x1B
+#define PsrMsys		0x1F
+#define PsrMask		0x1F
+
+#define PsrDfiq		0x00000040	/* disable FIQ interrupts */
+#define PsrDirq		0x00000080	/* disable IRQ interrupts */
+
+#define PsrV		0x10000000	/* overflow */
+#define PsrC		0x20000000	/* carry/borrow/extend */
+#define PsrZ		0x40000000	/* zero */
+#define PsrN		0x80000000	/* negative/less than */
+
+/*
+ * Internal MMU coprocessor registers
+ */
+#define CpCPUID		0		/* R: */
+#define CpControl	1		/* R: */
+#define CpTTB		2		/* W: translation table base */
+#define CpDAC		3		/* W: domain access control */
+#define CpFSR		5		/* R: fault status */
+#define CpTLBflush	5		/* W: */
+#define CpFAR		6		/* R: fault address */
+#define CpTLBpurge	6		/* W: */
+#define CpCacheCtl	7		/* W: */
+
+#define CpDebug		14		/* R/W: debug registers */
+/*
+ * Coprocessors
+ */
+#define CpMMU		15
+
+/*
+ * Internal MMU coprocessor registers
+ */
+#define CpCmmu		0x00000001	/* M: MMU enable */
+#define CpCalign	0x00000002	/* A: alignment fault enable */
+#define CpCDcache	0x00000004	/* C: instruction/data cache on */
+#define CpCwb		0x00000008	/* W: write buffer turned on */
+#define CpCi32		0x00000010	/* P: 32-bit programme space */
+#define CpCd32		0x00000020	/* D: 32-bit data space */
+#define CpCbe		0x00000080	/* B: big-endian operation */
+#define CpCsystem	0x00000100	/* S: system permission */
+#define CpCrom		0x00000200	/* R: ROM permission */
+#define CpCIcache	0x00001000	/* C: Instruction Cache on */
+
+/*
+ * Debug support internal registers
+ */
+#define CpDBAR	0
+#define CpDBVR	1
+#define CpDBMR	2
+#define CpDBCR	3
+#define CpIBCR	8
+/*
+ * MMU
+ */
+/*
+ * Small pages:
+ *	L1: 12-bit index -> 4096 descriptors -> 16Kb
+ *	L2:  8-bit index ->  256 descriptors ->  1Kb
+ * Each L2 descriptor has access permissions for 4 1Kb sub-pages.
+ *
+ *	TTB + L1Tx gives address of L1 descriptor
+ *	L1 descriptor gives PTBA
+ *	PTBA + L2Tx gives address of L2 descriptor
+ *	L2 descriptor gives PBA
+ */
+#define MmuTTB(pa)	((pa) & ~0x3FFF)	/* translation table base */
+#define MmuL1x(pa)	(((pa)>>20) & 0xFFF)	/* L1 table index */
+#define MmuPTBA(pa)	((pa) & ~0x3FF)		/* page table base address */
+#define MmuL2x(pa)	(((pa)>>12) & 0xFF)	/* L2 table index */
+#define MmuPBA(pa)	((pa) & ~0xFFF)		/* page base address */
+#define MmuSBA(pa)	((pa) & ~0xFFFFF)	/* section base address */
+
+#define MmuL1page	0x011			/* descriptor is for L2 pages */
+#define MmuL1section	0x012			/* descriptor is for section */
+
+#define MmuL2invalid	0x000
+#define MmuL2large	0x001			/* large */
+#define MmuL2small	0x002			/* small */
+#define MmuWB		0x004			/* data goes through write buffer */
+#define MmuIDC		0x008			/* data placed in cache */
+
+#define MmuDAC(d)	(((d) & 0xF)<<5)	/* L1 domain */
+#define MmuAP(i, v)	((v)<<(((i)*2)+4))	/* access permissions */
+#define MmuL1AP(v)	MmuAP(3, (v))
+#define MmuL2AP(v)	MmuAP(3, (v))|MmuAP(2, (v))|MmuAP(1, (v))|MmuAP(0, (v))
+#define MmuAPsro	0			/* supervisor rw */
+#define MmuAPsrw	1			/* supervisor rw */
+#define MmuAPuro	2			/* supervisor rw + user ro */
+#define MmuAPurw	3			/* supervisor rw + user rw */
--- /dev/null
+++ b/os/boot/puma/boot.h
@@ -1,0 +1,12 @@
+#include <u.h>
+#include "lib.h"
+
+#include "mem.h"
+#include "dat.h"
+#include "fns.h"
+#include "io.h"
+#include "ureg.h"
+
+#include "armv4.h"
+#include "puma.h"
+
--- /dev/null
+++ b/os/boot/puma/bootp.c
@@ -1,0 +1,502 @@
+#include "u.h"
+#include "lib.h"
+#include "mem.h"
+#include "dat.h"
+#include "fns.h"
+#include "io.h"
+
+#include "ip.h"
+
+uchar broadcast[Eaddrlen] = {
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+};
+
+static ushort tftpport = 5000;
+static int Id = 1;
+static Netaddr myaddr;
+static Netaddr server;
+
+typedef struct {
+	uchar	header[4];
+	uchar	data[Segsize];
+} Tftp;
+static Tftp tftpb;
+
+static void
+hnputs(uchar *ptr, ushort val)
+{
+	ptr[0] = val>>8;
+	ptr[1] = val;
+}
+
+static void
+hnputl(uchar *ptr, ulong val)
+{
+	ptr[0] = val>>24;
+	ptr[1] = val>>16;
+	ptr[2] = val>>8;
+	ptr[3] = val;
+}
+
+static ulong
+nhgetl(uchar *ptr)
+{
+	return ((ptr[0]<<24) | (ptr[1]<<16) | (ptr[2]<<8) | ptr[3]);
+}
+
+static ushort
+nhgets(uchar *ptr)
+{
+	return ((ptr[0]<<8) | ptr[1]);
+}
+
+static	short	endian	= 1;
+static	char*	aendian	= (char*)&endian;
+#define	LITTLE	*aendian
+
+static ushort
+ptcl_csum(void *a, int len)
+{
+	uchar *addr;
+	ulong t1, t2;
+	ulong losum, hisum, mdsum, x;
+
+	addr = a;
+	losum = 0;
+	hisum = 0;
+	mdsum = 0;
+
+	x = 0;
+	if((ulong)addr & 1) {
+		if(len) {
+			hisum += addr[0];
+			len--;
+			addr++;
+		}
+		x = 1;
+	}
+	while(len >= 16) {
+		t1 = *(ushort*)(addr+0);
+		t2 = *(ushort*)(addr+2);	mdsum += t1;
+		t1 = *(ushort*)(addr+4);	mdsum += t2;
+		t2 = *(ushort*)(addr+6);	mdsum += t1;
+		t1 = *(ushort*)(addr+8);	mdsum += t2;
+		t2 = *(ushort*)(addr+10);	mdsum += t1;
+		t1 = *(ushort*)(addr+12);	mdsum += t2;
+		t2 = *(ushort*)(addr+14);	mdsum += t1;
+		mdsum += t2;
+		len -= 16;
+		addr += 16;
+	}
+	while(len >= 2) {
+		mdsum += *(ushort*)addr;
+		len -= 2;
+		addr += 2;
+	}
+	if(x) {
+		if(len)
+			losum += addr[0];
+		if(LITTLE)
+			losum += mdsum;
+		else
+			hisum += mdsum;
+	} else {
+		if(len)
+			hisum += addr[0];
+		if(LITTLE)
+			hisum += mdsum;
+		else
+			losum += mdsum;
+	}
+
+	losum += hisum >> 8;
+	losum += (hisum & 0xff) << 8;
+	while(hisum = losum>>16)
+		losum = hisum + (losum & 0xffff);
+
+	return ~losum;
+}
+
+static ushort
+ip_csum(uchar *addr)
+{
+	int len;
+	ulong sum = 0;
+
+	len = (addr[0]&0xf)<<2;
+
+	while(len > 0) {
+		sum += addr[0]<<8 | addr[1] ;
+		len -= 2;
+		addr += 2;
+	}
+
+	sum = (sum & 0xffff) + (sum >> 16);
+	sum = (sum & 0xffff) + (sum >> 16);
+	return (sum^0xffff);
+}
+
+static void
+udpsend(int ctlrno, Netaddr *a, void *data, int dlen)
+{
+	Udphdr *uh;
+	Etherhdr *ip;
+	Etherpkt pkt;
+	int len, ptcllen;
+
+
+	uh = (Udphdr*)&pkt;
+
+	memset(uh, 0, sizeof(Etherpkt));
+	memmove(uh->udpcksum+sizeof(uh->udpcksum), data, dlen);
+
+	/*
+	 * UDP portion
+	 */
+	ptcllen = dlen + (UDP_HDRSIZE-UDP_PHDRSIZE);
+	uh->ttl = 0;
+	uh->udpproto = IP_UDPPROTO;
+	uh->frag[0] = 0;
+	uh->frag[1] = 0;
+	hnputs(uh->udpplen, ptcllen);
+	hnputl(uh->udpsrc, myaddr.ip);
+	hnputs(uh->udpsport, myaddr.port);
+	hnputl(uh->udpdst, a->ip);
+	hnputs(uh->udpdport, a->port);
+	hnputs(uh->udplen, ptcllen);
+	uh->udpcksum[0] = 0;
+	uh->udpcksum[1] = 0;
+	dlen = (dlen+1)&~1;
+	hnputs(uh->udpcksum, ptcl_csum(&uh->ttl, dlen+UDP_HDRSIZE));
+
+	/*
+	 * IP portion
+	 */
+	ip = (Etherhdr*)&pkt;
+	len = sizeof(Udphdr)+dlen;
+	ip->vihl = IP_VER|IP_HLEN;
+	ip->tos = 0;
+	ip->ttl = 255;
+	hnputs(ip->length, len-ETHER_HDR);
+	hnputs(ip->id, Id++);
+	ip->frag[0] = 0;
+	ip->frag[1] = 0;
+	ip->cksum[0] = 0;
+	ip->cksum[1] = 0;
+	hnputs(ip->cksum, ip_csum(&ip->vihl));
+
+	/*
+	 * Ethernet MAC portion
+	 */
+	hnputs(ip->type, ET_IP);
+	memmove(ip->d, a->ea, sizeof(ip->d));
+
+	ethertxpkt(ctlrno, &pkt, len, Timeout);
+}
+
+static void
+nak(int ctlrno, Netaddr *a, int code, char *msg, int report)
+{
+	int n;
+	char buf[128];
+
+	buf[0] = 0;
+	buf[1] = Tftp_ERROR;
+	buf[2] = 0;
+	buf[3] = code;
+	strcpy(buf+4, msg);
+	n = strlen(msg) + 4 + 1;
+	udpsend(ctlrno, a, buf, n);
+	if(report)
+		print("\ntftp: error(%d): %s\n", code, msg);
+}
+
+static int
+udprecv(int ctlrno, Netaddr *a, void *data, int dlen)
+{
+	int n, len;
+	ushort csm;
+	Udphdr *h;
+	ulong addr, timo;
+	Etherpkt pkt;
+	static int rxactive;
+
+	if(rxactive == 0)
+		timo = 1000;
+	else
+		timo = Timeout;
+	timo += TK2MS(m->ticks);
+	while(timo > TK2MS(m->ticks)){
+		n = etherrxpkt(ctlrno, &pkt, timo-TK2MS(m->ticks));
+
+		if(n <= 0)
+			continue;
+
+		h = (Udphdr*)&pkt;
+		if(nhgets(h->type) != ET_IP)
+			continue;
+
+		if(ip_csum(&h->vihl)) {
+			print("ip chksum error\n");
+			continue;
+		}
+		if(h->vihl != (IP_VER|IP_HLEN)) {
+			print("ip bad vers/hlen\n");
+			continue;
+		}
+
+		if(h->udpproto != IP_UDPPROTO)
+			continue;
+
+		h->ttl = 0;
+		len = nhgets(h->udplen);
+		hnputs(h->udpplen, len);
+
+		if(nhgets(h->udpcksum)) {
+			csm = ptcl_csum(&h->ttl, len+UDP_PHDRSIZE);
+			if(csm != 0) {
+				print("udp chksum error csum #%4lux len %d\n", csm, n);
+				break;
+			}
+		}
+
+		if(a->port != 0 && nhgets(h->udpsport) != a->port)
+			continue;
+
+		addr = nhgetl(h->udpsrc);
+		if(a->ip != Bcastip && addr != a->ip)
+			continue;
+
+		len -= UDP_HDRSIZE-UDP_PHDRSIZE;
+		if(len > dlen) {
+			print("udp: packet too big\n");
+			continue;
+		}
+
+		memmove(data, h->udpcksum+sizeof(h->udpcksum), len);
+		a->ip = addr;
+		a->port = nhgets(h->udpsport);
+		memmove(a->ea, pkt.s, sizeof(a->ea));
+
+		rxactive = 1;
+		return len;
+	}
+
+	return 0;
+}
+
+static int tftpblockno;
+
+static int
+tftpopen(int ctlrno, Netaddr *a, char *name, Tftp *tftp)
+{
+	int i, len, rlen, oport;
+	char buf[Segsize+2];
+
+	buf[0] = 0;
+	buf[1] = Tftp_READ;
+	len = sprint(buf+2, "%s", name) + 2;
+	len += sprint(buf+len+1, "octet") + 2;
+
+	oport = a->port;
+	for(i = 0; i < 5; i++){
+		a->port = oport;
+		udpsend(ctlrno, a, buf, len);
+		a->port = 0;
+		if((rlen = udprecv(ctlrno, a, tftp, sizeof(Tftp))) < sizeof(tftp->header))
+			continue;
+
+		switch((tftp->header[0]<<8)|tftp->header[1]){
+
+		case Tftp_ERROR:
+			print("tftpopen: error (%d): %s\n",
+				(tftp->header[2]<<8)|tftp->header[3], tftp->data);
+			return -1;
+
+		case Tftp_DATA:
+			tftpblockno = 1;
+			len = (tftp->header[2]<<8)|tftp->header[3];
+			if(len != tftpblockno){
+				print("tftpopen: block error: %d\n", len);
+				nak(ctlrno, a, 1, "block error", 0);
+				return -1;
+			}
+			return rlen-sizeof(tftp->header);
+		}
+	}
+
+	print("tftpopen: failed to connect to server\n");
+	return -1;
+}
+
+static int
+tftpread(int ctlrno, Netaddr *a, Tftp *tftp, int dlen)
+{
+	int blockno, len, retry;
+	uchar buf[4];
+
+	buf[0] = 0;
+	buf[1] = Tftp_ACK;
+	buf[2] = tftpblockno>>8;
+	buf[3] = tftpblockno;
+	tftpblockno++;
+
+	dlen += sizeof(tftp->header);
+
+	retry = 0;
+buggery:
+	udpsend(ctlrno, a, buf, sizeof(buf));
+
+	if((len = udprecv(ctlrno, a, tftp, dlen)) != dlen){
+		print("tftpread: %d != %d\n", len, dlen);
+		nak(ctlrno, a, 2, "short read", 0);
+		if(retry++ < 5)
+			goto buggery;
+		return -1;
+	}
+
+	blockno = (tftp->header[2]<<8)|tftp->header[3];
+	if(blockno != tftpblockno){
+		print("tftpread: block error: %d, expected %d\n", blockno, tftpblockno);
+
+		if(blockno == tftpblockno-1 && retry++ < 5)
+			goto buggery;
+		nak(ctlrno, a, 1, "block error", 0);
+
+		return -1;
+	}
+
+	return len-sizeof(tftp->header);
+}
+
+int
+bootp(int ctlrno, char *file)
+{
+	Bootp req, rep;
+	int i, dlen, segsize, text, data, bss, total;
+	uchar *ea, *addr, *p;
+	ulong entry;
+	Exec *exec;
+	char name[128], *filename, *sysname;
+
+	if((ea = etheraddr(ctlrno)) == 0){
+		print("invalid ctlrno %d\n", ctlrno);
+		return -1;
+	}
+
+	filename = 0;
+	sysname = 0;
+	if(file && *file){
+		strcpy(name, file);
+		if(filename = strchr(name, ':')){
+			if(filename != name && *(filename-1) != '\\'){
+				sysname = name;
+				*filename++ = 0;
+			}
+		}
+		else
+			filename = name;
+	}
+		
+
+	memset(&req, 0, sizeof(req));
+	req.op = Bootrequest;
+	req.htype = 1;			/* ethernet */
+	req.hlen = Eaddrlen;		/* ethernet */
+	memmove(req.chaddr, ea, Eaddrlen);
+
+	myaddr.ip = 0;
+	myaddr.port = BPportsrc;
+	memmove(myaddr.ea, ea, Eaddrlen);
+
+	for(i = 0; i < 10; i++) {
+		server.ip = Bcastip;
+		server.port = BPportdst;
+		memmove(server.ea, broadcast, sizeof(server.ea));
+		udpsend(ctlrno, &server, &req, sizeof(req));
+		if(udprecv(ctlrno, &server, &rep, sizeof(rep)) <= 0)
+			continue;
+		if(memcmp(req.chaddr, rep.chaddr, Eaddrlen))
+			continue;
+		if(rep.htype != 1 || rep.hlen != Eaddrlen)
+			continue;
+		if(sysname == 0 || strcmp(sysname, rep.sname) == 0)
+			break;
+	}
+	if(i >= 10) {
+		print("bootp timed out\n");
+		return -1;
+	}
+
+	if(filename == 0 || *filename == 0)
+		filename = rep.file;
+
+	if(rep.sname[0] != '\0')
+		 print("%s ", rep.sname);
+	print("(%d.%d.%d.%d!%d): %s\n",
+		rep.siaddr[0],
+		rep.siaddr[1],
+		rep.siaddr[2],
+		rep.siaddr[3],
+		server.port,
+		filename);
+
+	myaddr.ip = nhgetl(rep.yiaddr);
+	myaddr.port = tftpport++;
+	server.ip = nhgetl(rep.siaddr);
+	server.port = TFTPport;
+
+	if((dlen = tftpopen(ctlrno, &server, filename, &tftpb)) < 0)
+		return -1;
+
+	exec = (Exec*)(tftpb.data);
+	if(dlen < sizeof(Exec) || GLLONG(exec->magic) != E_MAGIC){
+		nak(ctlrno, &server, 0, "bad magic number", 1);
+		return -1;
+	}
+	text = GLLONG(exec->text);
+	data = GLLONG(exec->data);
+	bss = GLLONG(exec->bss);
+	total = text+data+bss;
+	entry = GLLONG(exec->entry);
+print("load@%8.8lux: ", PADDR(entry));
+	print("%d", text);
+
+	addr = (uchar*)PADDR(entry);
+	p = tftpb.data+sizeof(Exec);
+	dlen -= sizeof(Exec);
+	segsize = text;
+	for(;;){
+		if(dlen == 0){
+			if((dlen = tftpread(ctlrno, &server, &tftpb, sizeof(tftpb.data))) < 0)
+				return -1;
+			p = tftpb.data;
+		}
+		if(segsize <= dlen)
+			i = segsize;
+		else
+			i = dlen;
+		memmove(addr, p, i);
+
+		addr += i;
+		p += i;
+		segsize -= i;
+		dlen -= i;
+
+		if(segsize <= 0){
+			if(data == 0)
+				break;
+			print("+%d", data);
+			segsize = data;
+			data = 0;
+			addr = (uchar*)PGROUND((ulong)addr);
+		}
+	}
+	nak(ctlrno, &server, 3, "ok", 0);		/* tftpclose */
+	print("+%d=%d\n", bss, total);
+	print("entry: 0x%lux\n", entry);
+
+	(*(void(*)(void))(PADDR(entry)))();
+	
+	return 0;
+}
--- /dev/null
+++ b/os/boot/puma/cga.c
@@ -1,0 +1,92 @@
+#include "u.h"
+#include "lib.h"
+#include "mem.h"
+#include "dat.h"
+#include "io.h"
+#include "fns.h"
+
+enum {
+	Width		= 160,
+	Height		= 25,
+
+	Attr		= 7,		/* white on black */
+};
+
+#define CGASCREENBASE	((uchar*)KADDR(0xB8000))
+
+static int pos;
+static int screeninitdone;
+
+static uchar
+cgaregr(int index)
+{
+	outb(0x3D4, index);
+	return inb(0x3D4+1) & 0xFF;
+}
+
+static void
+cgaregw(int index, int data)
+{
+	outb(0x3D4, index);
+	outb(0x3D4+1, data);
+}
+
+static void
+movecursor(void)
+{
+	cgaregw(0x0E, (pos/2>>8) & 0xFF);
+	cgaregw(0x0F, pos/2 & 0xFF);
+	CGASCREENBASE[pos+1] = Attr;
+}
+
+static void
+cgascreenputc(int c)
+{
+	int i;
+
+	if(c == '\n'){
+		pos = pos/Width;
+		pos = (pos+1)*Width;
+	}
+	else if(c == '\t'){
+		i = 8 - ((pos/2)&7);
+		while(i-->0)
+			cgascreenputc(' ');
+	}
+	else if(c == '\b'){
+		if(pos >= 2)
+			pos -= 2;
+		cgascreenputc(' ');
+		pos -= 2;
+	}
+	else{
+		CGASCREENBASE[pos++] = c;
+		CGASCREENBASE[pos++] = Attr;
+	}
+	if(pos >= Width*Height){
+		memmove(CGASCREENBASE, &CGASCREENBASE[Width], Width*(Height-1));
+		memset(&CGASCREENBASE[Width*(Height-1)], 0, Width);
+		pos = Width*(Height-1);
+	}
+	movecursor();
+}
+
+void
+screeninit(void)
+{
+	if(screeninitdone == 0){
+		pos = cgaregr(0x0E)<<8;
+		pos |= cgaregr(0x0F);
+		pos *= 2;
+		screeninitdone = 1;
+	}
+}
+
+void
+cgascreenputs(char* s, int n)
+{
+	if(screeninitdone == 0)
+		screeninit();
+	while(n-- > 0)
+		cgascreenputc(*s++);
+}
--- /dev/null
+++ b/os/boot/puma/clock.c
@@ -1,0 +1,154 @@
+#include "boot.h"
+
+ /*
+ * Control Word Read/Write Counter (mode 0)  LSB, MSB
+ */
+#define PIT_RW_COUNTER0  0x30
+#define PIT_RW_COUNTER1  0x70
+#define PIT_RW_COUNTER2  0xB0
+#define PIT_COUNTERLATCH0	0x00
+#define PIT_COUNTERLATCH1	0x40
+#define PIT_COUNTERLATCH2	0x80
+
+#define PIT_MODE_0	0	/* Interrupt on Terminal Count */
+#define PIT_MODE_1	2	/* Hardware Retriggeable One-shot */
+#define PIT_MODE_2	4	/* Rate Generator */
+#define PIT_MODE_3	6	/* Square Wave Mode */
+#define PIT_MODE_4	8	/* Software Triggered Mode */
+#define PIT_MODE_5	10	/* Hardware Triggered Mode (Retriggeable) */
+
+/*
+ * Harris 82C54 Programmable Interval Timer
+ * On the Puma board the PIT is memory mapped
+ * starting at 0xf2000000 and with each of the 8-bit
+ * registers addressed on a consecutive 4-byte boundary.
+ */
+#undef inb
+#undef outb
+#define 	inb(port)			((*(uchar *)(port))&0xff)
+#define 	outb(port, data)	(*(uchar *)(port) = (data))
+enum
+{
+	Cnt0=	0xf2000000,		/* counter locations */
+	Cnt1=	0xf2000004,		/* ... */
+	Cnt2=	0xf2000008,		/* ... */
+	Ctlw=	0xf200000c,		/* control word register*/
+
+	/* commands */
+	Latch0=	0x00,		/* latch counter 0's value */
+	Load0=	0x30,		/* load counter 0 with 2 bytes */
+	Latch1=	0x40,		/* latch counter 1's value */
+	Load1=	0x70,		/* load counter 1 with 2 bytes */
+
+	/* modes */
+	Square=	0x06,		/* periodic square wave */
+	RateGen=	0x04,		/* rate generator */
+
+	Freq=	3686400,	/* Real clock frequency */
+};
+
+static int cpufreq = 233000000;
+static int aalcycles = 14;
+
+static void
+clockintr(Ureg*, void*)
+{
+	m->ticks++;
+	checkalarms();
+}
+
+/*
+ *  delay for l milliseconds more or less.  delayloop is set by
+ *  clockinit() to match the actual CPU speed.
+ */
+void
+delay(int l)
+{
+	l *= m->delayloop;
+	if(l <= 0)
+		l = 1;
+	aamloop(l);
+}
+
+void
+microdelay(int l)
+{
+	l *= m->delayloop;
+	l /= 1000;
+	if(l <= 0)
+		l = 1;
+	aamloop(l);
+}
+
+void
+clockinit(void)
+{
+	int x, y;	/* change in counter */
+	int loops, incr;
+
+	/*
+	 *  set vector for clock interrupts
+	 */
+	setvec(V_TIMER0, clockintr, 0);
+
+	/*
+	 *  set clock for 1/HZ seconds
+	 */
+	outb(Ctlw, Load0|Square);
+	outb(Cnt0, (Freq/HZ));	/* low byte */
+	outb(Cnt0, (Freq/HZ)>>8);	/* high byte */
+
+	/* find biggest loop that doesn't wrap */
+	incr = 16000000/(aalcycles*HZ*2);
+	x = 2000;
+	for(loops = incr; loops < 64*1024; loops += incr) {
+		/*
+		 *  measure time for the loop
+		 *	TEXT aamloop(SB), $-4
+		 *	_aamloop:
+		 *		MOVW	R0, R0
+		 *		MOVW	R0, R0
+		 *		MOVW	R0, R0
+		 *		SUB		$1, R0
+		 *		CMP		$0, R0
+		 *		BNE		_aamloop
+		 *		RET
+		 *
+		 *  the time for the loop should be independent of external
+		 *  cache and memory system since it fits in the execution
+		 *  prefetch buffer.
+		 *
+		 */
+		outb(Ctlw, Latch0);
+		x = inb(Cnt0);
+		x |= inb(Cnt0)<<8;
+		aamloop(loops);
+		outb(Ctlw, Latch0);
+		y = inb(Cnt0);
+		y |= inb(Cnt0)<<8;
+		x -= y;
+	
+		if(x < 0)
+			x += Freq/HZ;
+
+		if(x > Freq/(3*HZ))
+			break;
+	}
+
+	/*
+	 *  counter  goes at twice the frequency, once per transition,
+	 *  i.e., twice per square wave
+	 */
+	x >>= 1;
+
+	/*
+ 	 *  figure out clock frequency and a loop multiplier for delay().
+	 */
+	cpufreq = loops*((aalcycles*Freq)/x);
+	m->delayloop = (cpufreq/1000)/aalcycles;	/* AAMLOOPs for 1 ms */
+
+	/*
+	 *  add in possible .2% error and convert to MHz
+	 */
+	m->speed = (cpufreq + cpufreq/500)/1000000;
+}
--- /dev/null
+++ b/os/boot/puma/conf.c
@@ -1,0 +1,181 @@
+#include "u.h"
+#include "lib.h"
+#include "mem.h"
+#include "dat.h"
+#include "fns.h"
+#include "io.h"
+
+#include "dosfs.h"
+
+static char *confname[MAXCONF];
+static char *confval[MAXCONF];
+static int nconf;
+
+static	char*	defplan9ini =
+	"ether0=type=CS8900\r\n"
+	"vgasize=640x480x8\r\n"
+	"kernelpercent=40\r\n"
+	"console=1\r\nbaud=9600\r\n"
+;
+
+extern char **ini;
+
+char*
+getconf(char *name)
+{
+	int i;
+
+	for(i = 0; i < nconf; i++)
+		if(strcmp(confname[i], name) == 0)
+			return confval[i];
+	return 0;
+}
+
+/*
+ *  read configuration file
+ */
+int
+plan9ini(Dos *dos, char *val)
+{
+	Dosfile rc;
+	int i, n;
+	char *cp, *p, *q, *line[MAXCONF];
+
+	cp = BOOTARGS;
+	if(dos) {
+		if(dosstat(dos, *ini, &rc) <= 0)
+			return -1;
+
+		*cp = 0;
+		n = dosread(&rc, cp, BOOTARGSLEN-1);
+		if(n <= 0)
+			return -1;
+		cp[n] = 0;
+	} else if(val != nil){
+		if(memchr(val, 0, BOOTARGSLEN-1) == nil)
+			return -1;
+		print("Using flash configuration\n");
+		strcpy(cp, val);
+		n = strlen(cp);
+	}else{
+		print("Using default configuration\n");
+		strcpy(cp, defplan9ini);
+		n = strlen(cp);
+	}
+
+	/*
+	 * Make a working copy.
+	 * We could change this to pass the parsed strings
+	 * to the booted programme instead of the raw
+	 * string, then it only gets done once.
+	 */
+	memmove(cp+BOOTARGSLEN, cp, n+1);
+	cp += BOOTARGSLEN;
+
+	/*
+	 * Strip out '\r', change '\t' -> ' '.
+	 */
+	p = cp;
+	for(q = cp; *q; q++){
+		if(*q == '\r')
+			continue;
+		if(*q == '\t')
+			*q = ' ';
+		*p++ = *q;
+	}
+	*p = 0;
+	n = getcfields(cp, line, MAXCONF, "\n");
+	for(i = 0; i < n; i++){
+		cp = strchr(line[i], '=');
+		if(cp == 0)
+			continue;
+		*cp++ = 0;
+		if(cp - line[i] >= NAMELEN+1)
+			*(line[i]+NAMELEN-1) = 0;
+		confname[nconf] = line[i];
+		confval[nconf] = cp;
+		nconf++;
+	}
+	return 0;
+}
+
+static int
+parseether(uchar *to, char *from)
+{
+	char nip[4];
+	char *p;
+	int i;
+
+	p = from;
+	while(*p == ' ')
+		++p;
+	for(i = 0; i < 6; i++){
+		if(*p == 0)
+			return -1;
+		nip[0] = *p++;
+		if(*p == 0)
+			return -1;
+		nip[1] = *p++;
+		nip[2] = 0;
+		to[i] = strtoul(nip, 0, 16);
+		if(*p == ':')
+			p++;
+	}
+	return 0;
+}
+
+int
+isaconfig(char *class, int ctlrno, ISAConf *isa)
+{
+	char cc[NAMELEN], *p, *q, *r;
+	int n;
+
+	sprint(cc, "%s%d", class, ctlrno);
+	for(n = 0; n < nconf; n++){
+		if(strncmp(confname[n], cc, NAMELEN))
+			continue;
+		isa->nopt = 0;
+		p = confval[n];
+		while(*p){
+			while(*p == ' ' || *p == '\t')
+				p++;
+			if(*p == '\0')
+				break;
+			if(strncmp(p, "type=", 5) == 0){
+				p += 5;
+				for(q = isa->type; q < &isa->type[NAMELEN-1]; q++){
+					if(*p == '\0' || *p == ' ' || *p == '\t')
+						break;
+					*q = *p++;
+				}
+				*q = '\0';
+			}
+			else if(strncmp(p, "port=", 5) == 0)
+				isa->port = strtoul(p+5, &p, 0);
+			else if(strncmp(p, "irq=", 4) == 0)
+				isa->irq = strtoul(p+4, &p, 0);
+			else if(strncmp(p, "mem=", 4) == 0)
+				isa->mem = strtoul(p+4, &p, 0);
+			else if(strncmp(p, "size=", 5) == 0)
+				isa->size = strtoul(p+5, &p, 0);
+			else if(strncmp(p, "ea=", 3) == 0){
+				if(parseether(isa->ea, p+3) == -1)
+					memset(isa->ea, 0, 6);
+			}
+			else if(isa->nopt < NISAOPT){
+				r = isa->opt[isa->nopt];
+				while(*p && *p != ' ' && *p != '\t'){
+					*r++ = *p++;
+					if(r-isa->opt[isa->nopt] >= ISAOPTLEN-1)
+						break;
+				}
+				*r = '\0';
+				isa->nopt++;
+			}
+			while(*p && *p != ' ' && *p != '\t')
+				p++;
+		}
+		return 1;
+	}
+	return 0;
+}
--- /dev/null
+++ b/os/boot/puma/console.c
@@ -1,0 +1,181 @@
+#include "u.h"
+#include "lib.h"
+#include "mem.h"
+#include "dat.h"
+#include "fns.h"
+#include "io.h"
+
+static Queue*	consiq;
+static Queue*	consoq;
+
+void
+bothputs(char *s, int n)
+{
+	uartputs(s, n);
+//	cgascreenputs(s, n);
+}
+
+static void (*consputs)(char*, int) =  0;
+
+void
+consinit(void)
+{
+	char *p;
+	int baud, port;
+	static int cgadone;
+
+	if((p = getconf("console")) == 0 || strcmp(p, "lcd") == 0 || strcmp(p, "screen") == 0){
+		consiq = qopen(4*1024, 0, 0, 0);
+		consoq = qopen(8*1024, 0, 0, 0);
+		consputs = uartputs;
+		if(!cgadone) {
+			cgadone = 1;
+			//screeninit();
+			//kbdinit();
+		port = 1;
+		baud = 9600;
+		uartspecial(port, baud, &consiq, &consoq, kbdchar);
+		}
+		return;
+	}
+	if(0 || strstr(p, "lcd") == 0)
+		consputs = bothputs;
+	else
+		consputs = uartputs;
+
+	port = strtoul(p, 0, 0);
+	baud = 0;
+	if(p = getconf("baud"))
+		baud = strtoul(p, 0, 0);
+	if(baud == 0)
+		baud = 9600;
+	uartspecial(port, baud, &consiq, &consoq, kbdchar);
+}
+
+void
+kbdchar(Queue *q, int c)
+{
+	c &= 0x7F;
+	if(c == 0x10)
+		panic("^p");
+	if(q == 0) {
+		if(consiq != 0)
+			qbputc(consiq, c);
+	} else
+		qbputc(q, c);
+}
+
+static int
+getline(char *buf, int size, int dotimeout)
+{
+	int c, i=0;
+	ulong start;
+	char echo;
+
+	for (;;) {
+		start = m->ticks;
+		do{
+			if(dotimeout && ((m->ticks - start) > 5*HZ))
+				return -2;
+			c = qbgetc(consiq);
+		}while(c == -1);
+		if(c == '\r')
+			c = '\n'; 		/* turn carriage return into newline */
+		if(c == '\177')
+			c = '\010';		/* turn delete into backspace */
+		if(c == '\025')
+			echo = '\n';		/* echo ^U as a newline */
+		else
+			echo = c;
+		(*consputs)(&echo, 1);
+
+		if(c == '\010'){
+			if(i > 0)
+				i--; /* bs deletes last character */
+			continue;
+		}
+		/* a newline ends a line */
+		if (c == '\n')
+			break;
+		/* ^U wipes out the line */
+		if (c =='\025')
+			return -1;
+		if(i == size)
+			return size;
+		buf[i++] = c;
+	}
+	buf[i] = 0;
+	return i;
+}
+
+int
+getstr(char *prompt, char *buf, int size, char *def)
+{
+	int len, isdefault;
+
+	buf[0] = 0;
+	isdefault = (def && *def);
+	for (;;) {
+		if(isdefault)
+			print("%s[default==%s]: ", prompt, def);
+		else
+			print("%s: ", prompt);
+		len = getline(buf, size, isdefault);
+		switch(len){
+		case -1:
+			/* ^U typed */
+			continue;
+		case -2:
+			/* timeout, use default */
+			(*consputs)("\n", 1);
+			len = 0;
+			break;
+		default:
+			break;
+		}
+		if(len >= size){
+			print("line too long\n");
+			continue;
+		}
+		break;
+	}
+	if(len == 0 && isdefault)
+		strcpy(buf, def);
+	return 0;
+}
+
+int
+sprint(char *s, char *fmt, ...)
+{
+	return donprint(s, s+PRINTSIZE, fmt, (&fmt+1)) - s;
+}
+
+int
+print(char *fmt, ...)
+{
+	char buf[PRINTSIZE];
+	int n;
+
+	if(consputs == 0)
+		return 0;
+	n = donprint(buf, buf+sizeof(buf), fmt, (&fmt+1)) - buf;
+	(*consputs)(buf, n);
+	return n;
+}
+
+void
+panic(char *fmt, ...)
+{
+	char buf[PRINTSIZE];
+	int n;
+
+	if(consputs){
+		(*consputs)("panic: ", 7);
+		n = donprint(buf, buf+sizeof(buf), fmt, (&fmt+1)) - buf;
+		(*consputs)(buf, n);
+		(*consputs)("\n", 1);
+	}
+	spllo();
+	for(;;)
+		idle();
+}
--- /dev/null
+++ b/os/boot/puma/dat.h
@@ -1,0 +1,205 @@
+typedef struct Block Block;
+typedef struct Queue Queue;
+
+typedef struct List {
+	void	*next;
+} List;
+
+typedef struct {
+	int	fake;
+	int	pri;
+} Lock;
+#define	lock(x)
+#define	unlock(x)
+
+typedef struct Alarm {
+	List;
+	int	busy;
+	long	dt;
+	void	(*f)(void*);
+	void	*arg;
+} Alarm;
+
+enum {
+	Eaddrlen	= 6,
+	ETHERMINTU	= 60,		/* minimum transmit size */
+	ETHERMAXTU	= 1514,		/* maximum transmit size */
+	ETHERHDRSIZE	= 14,		/* size of an ethernet header */
+
+	MaxEther	= 2,
+};
+
+typedef struct {
+	uchar	d[Eaddrlen];
+	uchar	s[Eaddrlen];
+	uchar	type[2];
+	uchar	data[1500];
+	uchar	crc[4];
+} Etherpkt;
+
+extern uchar broadcast[Eaddrlen];
+
+enum {
+	Npart		= 20+2,		/* 8 sub partitions, disk, and partition */
+	Maxxfer		= 16*1024,	/* maximum transfer size/cmd */
+};
+
+typedef struct {
+	ulong	start;
+	ulong	end;
+	char	name[NAMELEN+1];
+} Partition;
+
+typedef struct {
+	int	online;
+	int	npart;		/* number of real partitions */
+	Partition p[Npart];
+	ulong	offset;
+	Partition *current;	/* current partition */
+
+	ulong	cap;		/* total bytes */
+	int	bytes;		/* bytes/sector */
+	int	sectors;		/* sectors/track */
+	int	heads;		/* heads/cyl */
+	long	cyl;			/* cylinders/drive */
+
+	char	lba;			/* true if drive has logical block addressing */
+	char	multi;		/* non-zero if drive does multiple block xfers */
+} Disc;
+
+enum {
+	ScsiTestunit	= 0x00,
+	ScsiExtsens	= 0x03,
+	ScsiInquiry	= 0x12,
+	ScsiModesense	= 0x1a,
+	ScsiStartunit	= 0x1B,
+	ScsiStopunit	= 0x1B,
+	ScsiGetcap	= 0x25,
+	ScsiRead	= 0x08,
+	ScsiWrite	= 0x0a,
+	ScsiExtread	= 0x28,
+	ScsiExtwrite	= 0x2a,
+
+	/* data direction */
+	ScsiIn		= 1,
+	ScsiOut		= 0,
+};
+
+typedef struct Scsibuf Scsibuf;
+typedef struct Scsibuf {
+	void*		virt;
+	void*		phys;
+	Scsibuf*	next;
+};
+
+typedef struct Scsidata {
+	uchar*		base;
+	uchar*		lim;
+	uchar*		ptr;
+} Scsidata;
+
+typedef struct Ureg Ureg;
+
+typedef struct Scsi {
+	ulong		pid;
+	ushort		target;
+	ushort		lun;
+	ushort		rflag;
+	ushort		status;
+	Scsidata 	cmd;
+	Scsidata 	data;
+	Scsibuf*	b;
+	uchar*		save;
+	uchar		cmdblk[16];
+} Scsi;
+
+typedef struct Segdesc {
+	ulong	d0;
+	ulong	d1;
+} Segdesc;
+
+typedef struct Mach {
+	ulong	ticks;		/* of the clock since boot time */
+	ulong	delayloop;
+	int		speed;		/* general system clock in MHz */
+	int		oscclk;		/* oscillator frequency in MHz */
+	void*	alarm;		/* alarms bound to this clock */
+} Mach;
+
+extern Mach *m;
+
+#define E_MAGIC		((((4*20)+0)*20)+7)
+
+typedef struct Exec Exec;
+struct	Exec
+{
+	uchar	magic[4];		/* magic number */
+	uchar	text[4];	 	/* size of text segment */
+	uchar	data[4];	 	/* size of initialized data */
+	uchar	bss[4];	  		/* size of uninitialized data */
+	uchar	syms[4];	 	/* size of symbol table */
+	uchar	entry[4];	 	/* entry point */
+	uchar	spsz[4];		/* size of sp/pc offset table */
+	uchar	pcsz[4];		/* size of pc/line number table */
+};
+
+/*
+ *  bootline passed by boot program
+ */
+#define BOOTLINE ((char *)0x18000-150)
+
+/*
+ * Where we leave configuration info.
+ */
+#define BOOTARGS	((char*)(0x18000))
+#define	BOOTARGSLEN	1024
+#define	MAXCONF		32
+
+/*
+ *  a parsed plan9.ini line
+ */
+#define ISAOPTLEN	16
+#define NISAOPT		8
+
+typedef struct  ISAConf {
+	char	type[NAMELEN];
+	ulong	port;
+	ulong	irq;
+	ulong	mem;
+	ulong	size;
+	uchar	ea[6];
+
+	int	nopt;
+	char	opt[NISAOPT][ISAOPTLEN];
+} ISAConf;
+
+typedef struct {
+	int	size;
+	ulong	addr;
+} Map;
+
+typedef struct {
+	char*	name;
+	Map*	map;
+	Map*	mapend;
+
+	Lock;
+} RMap;
+
+typedef struct PCIcfg PCIcfg;
+
+extern	uchar*	vgamem;
+
+struct Block {
+	uchar	*rp;
+	uchar	*wp;
+	uchar	*lim;
+	uchar	*data;
+	Block*	next;
+	ulong	magic;
+};
+#define	BLEN(b)	((b)->wp-(b)->rp)
+
+typedef struct QLock {
+	int	dummy;
+} QLock;
--- /dev/null
+++ b/os/boot/puma/div.s
@@ -1,0 +1,122 @@
+/*
+ * Div/Mod taken from the Inferno 2.0 ebsit code
+ */
+
+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
--- /dev/null
+++ b/os/boot/puma/donprint.c
@@ -1,0 +1,332 @@
+#include	"u.h"
+#include	"lib.h"
+
+#define	PTR	sizeof(char*)
+#define	SHORT	sizeof(int)
+#define	INT	sizeof(int)
+#define	LONG	sizeof(long)
+#define	IDIGIT	30
+#define	MAXCON	30
+
+#define	FLONG	(1<<0)
+#define	FSHORT	(1<<1)
+#define	FUNSIGN	(1<<2)
+
+typedef struct Op	Op;
+struct Op
+{
+	char	*p;
+	char	*ep;
+	void	*argp;
+	int	f1;
+	int	f2;
+	int	f3;
+};
+
+static	int	noconv(Op*);
+static	int	cconv(Op*);
+static	int	dconv(Op*);
+static	int	hconv(Op*);
+static	int	lconv(Op*);
+static	int	oconv(Op*);
+static	int	sconv(Op*);
+static	int	uconv(Op*);
+static	int	xconv(Op*);
+static	int	Xconv(Op*);
+static	int	percent(Op*);
+
+static
+int	(*fmtconv[MAXCON])(Op*) =
+{
+	noconv,
+	cconv, dconv, hconv, lconv,
+	oconv, sconv, uconv, xconv,
+	Xconv, percent,
+};
+static
+char	fmtindex[128] =
+{
+	['c'] 1,
+	['d'] 2,
+	['h'] 3,
+	['l'] 4,
+	['o'] 5,
+	['s'] 6,
+	['u'] 7,
+	['x'] 8,
+	['X'] 9,
+	['%'] 10,
+};
+
+static	int	convcount  = { 11 };
+static	int	ucase;
+
+static void
+PUT(Op *o, int c)
+{
+	static int pos;
+	int opos;
+
+	if(c == '\t'){
+		opos = pos;
+		pos = (opos+8) & ~7;
+		while(opos++ < pos && o->p < o->ep)
+			*o->p++ = ' ';
+		return;
+	}
+	if(o->p < o->ep){
+		*o->p++ = c;
+		pos++;
+	}
+	if(c == '\n')
+		pos = 0;
+}
+
+int
+fmtinstall(char c, int (*f)(Op*))
+{
+
+	c &= 0177;
+	if(fmtindex[c] == 0) {
+		if(convcount >= MAXCON)
+			return 1;
+		fmtindex[c] = convcount++;
+	}
+	fmtconv[fmtindex[c]] = f;
+	return 0;
+}
+
+char*
+donprint(char *p, char *ep, char *fmt, void *argp)
+{
+	int sf1, c;
+	Op o;
+
+	o.p = p;
+	o.ep = ep;
+	o.argp = argp;
+
+loop:
+	c = *fmt++;
+	if(c != '%') {
+		if(c == 0) {
+			if(o.p < o.ep)
+				*o.p = 0;
+			return o.p;
+		}
+		PUT(&o, c);
+		goto loop;
+	}
+	o.f1 = 0;
+	o.f2 = -1;
+	o.f3 = 0;
+	c = *fmt++;
+	sf1 = 0;
+	if(c == '-') {
+		sf1 = 1;
+		c = *fmt++;
+	}
+	while(c >= '0' && c <= '9') {
+		o.f1 = o.f1*10 + c-'0';
+		c = *fmt++;
+	}
+	if(sf1)
+		o.f1 = -o.f1;
+	if(c != '.')
+		goto l1;
+	c = *fmt++;
+	while(c >= '0' && c <= '9') {
+		if(o.f2 < 0)
+			o.f2 = 0;
+		o.f2 = o.f2*10 + c-'0';
+		c = *fmt++;
+	}
+l1:
+	if(c == 0)
+		fmt--;
+	c = (*fmtconv[fmtindex[c&0177]])(&o);
+	if(c < 0) {
+		o.f3 |= -c;
+		c = *fmt++;
+		goto l1;
+	}
+	o.argp = (char*)o.argp + c;
+	goto loop;
+}
+
+void
+strconv(char *o, Op *op, int f1, int f2)
+{
+	int n, c;
+	char *p;
+
+	n = strlen(o);
+	if(f1 >= 0)
+		while(n < f1) {
+			PUT(op, ' ');
+			n++;
+		}
+	for(p=o; c = *p++;)
+		if(f2 != 0) {
+			PUT(op, c);
+			f2--;
+		}
+	if(f1 < 0) {
+		f1 = -f1;
+		while(n < f1) {
+			PUT(op, ' ');
+			n++;
+		}
+	}
+}
+
+int
+numbconv(Op *op, int base)
+{
+	char b[IDIGIT];
+	int i, f, n, r;
+	long v;
+	short h;
+
+	f = 0;
+	switch(op->f3 & (FLONG|FSHORT|FUNSIGN)) {
+	case FLONG:
+		v = *(long*)op->argp;
+		r = LONG;
+		break;
+
+	case FUNSIGN|FLONG:
+		v = *(ulong*)op->argp;
+		r = LONG;
+		break;
+
+	case FSHORT:
+		h = *(int*)op->argp;
+		v = h;
+		r = SHORT;
+		break;
+
+	case FUNSIGN|FSHORT:
+		h = *(int*)op->argp;
+		v = (ushort)h;
+		r = SHORT;
+		break;
+
+	default:
+		v = *(int*)op->argp;
+		r = INT;
+		break;
+
+	case FUNSIGN:
+		v = *(unsigned*)op->argp;
+		r = INT;
+		break;
+	}
+	if(!(op->f3 & FUNSIGN) && v < 0) {
+		v = -v;
+		f = 1;
+	}
+	b[IDIGIT-1] = 0;
+	for(i = IDIGIT-2;; i--) {
+		n = (ulong)v % base;
+		n += '0';
+		if(n > '9'){
+			n += 'a' - ('9'+1);
+			if(ucase)
+				n += 'A'-'a';
+		}
+		b[i] = n;
+		if(i < 2)
+			break;
+		v = (ulong)v / base;
+		if(op->f2 >= 0 && i >= IDIGIT-op->f2)
+			continue;
+		if(v <= 0)
+			break;
+	}
+	if(f)
+		b[--i] = '-';
+	strconv(b+i, op, op->f1, -1);
+	return r;
+}
+
+static	int
+noconv(Op *op)
+{
+
+	strconv("***", op, 0, -1);
+	return 0;
+}
+
+static	int
+cconv(Op *op)
+{
+	char b[2];
+
+	b[0] = *(int*)op->argp;
+	b[1] = 0;
+	strconv(b, op, op->f1, -1);
+	return INT;
+}
+
+static	int
+dconv(Op *op)
+{
+	return numbconv(op, 10);
+}
+
+static	int
+hconv(Op*)
+{
+	return -FSHORT;
+}
+
+static	int
+lconv(Op*)
+{
+	return -FLONG;
+}
+
+static	int
+oconv(Op *op)
+{
+	return numbconv(op, 8);
+}
+
+static	int
+sconv(Op *op)
+{
+	strconv(*(char**)op->argp, op, op->f1, op->f2);
+	return PTR;
+}
+
+static	int
+uconv(Op*)
+{
+	return -FUNSIGN;
+}
+
+static	int
+xconv(Op *op)
+{
+	return numbconv(op, 16);
+}
+
+static	int
+Xconv(Op *op)
+{
+	int r;
+
+	ucase = 1;
+	r = numbconv(op, 16);
+	ucase = 0;
+	return r;
+}
+
+static	int
+percent(Op *op)
+{
+
+	PUT(op, '%');
+	return 0;
+}
--- /dev/null
+++ b/os/boot/puma/dosboot.c
@@ -1,0 +1,614 @@
+#include	"u.h"
+#include	"lib.h"
+#include	"mem.h"
+#include	"dat.h"
+#include	"fns.h"
+#include	"dosfs.h"
+
+extern char *premature;
+
+/*
+ *  predeclared
+ */
+static void	bootdump(Dosboot*);
+static void	setname(Dosfile*, char*);
+long		dosreadseg(Dosfile*, long, long);
+
+/*
+ *  debugging
+ */
+#define chatty	1
+#define chat	if(chatty)print
+
+/*
+ *  block io buffers
+ */
+enum
+{
+	Nbio=	16,
+};
+typedef struct	Clustbuf	Clustbuf;
+struct Clustbuf
+{
+	int	age;
+	long	sector;
+	uchar	*iobuf;
+	Dos	*dos;
+	int	size;
+};
+Clustbuf	bio[Nbio];
+
+/*
+ *  get an io block from an io buffer
+ */
+Clustbuf*
+getclust(Dos *dos, long sector)
+{
+	Clustbuf *p, *oldest;
+	int size;
+
+	chat("getclust @ %d\n", sector);
+
+	/*
+	 *  if we have it, just return it
+	 */
+	for(p = bio; p < &bio[Nbio]; p++){
+		if(sector == p->sector && dos == p->dos){
+			p->age = m->ticks;
+			chat("getclust %d in cache\n", sector);
+			return p;
+		}
+	}
+
+	/*
+	 *  otherwise, reuse the oldest entry
+	 */
+	oldest = bio;
+	for(p = &bio[1]; p < &bio[Nbio]; p++){
+		if(p->age <= oldest->age)
+			oldest = p;
+	}
+	p = oldest;
+
+	/*
+	 *  make sure the buffer is big enough
+	 */
+	size = dos->clustsize*dos->sectsize;
+	if(p->iobuf==0 || p->size < size)
+		p->iobuf = ialloc(size, 0);
+	p->size = size;
+
+	/*
+	 *  read in the cluster
+	 */
+	chat("getclust addr %d\n", (sector+dos->start)*dos->sectsize);
+	if((*dos->seek)(dos->dev, (sector+dos->start)*dos->sectsize) < 0){
+		chat("can't seek block\n");
+		return 0;
+	}
+	if((*dos->read)(dos->dev, p->iobuf, size) != size){
+		chat("can't read block\n");
+		return 0;
+	}
+
+	p->age = m->ticks;
+	p->dos = dos;
+	p->sector = sector;
+	chat("getclust %d read\n", sector);
+	return p;
+}
+
+/*
+ *  walk the fat one level ( n is a current cluster number ).
+ *  return the new cluster number or -1 if no more.
+ */
+static long
+fatwalk(Dos *dos, int n)
+{
+	ulong k, sect;
+	Clustbuf *p;
+	int o;
+
+	chat("fatwalk %d\n", n);
+
+	if(n < 2 || n >= dos->fatclusters)
+		return -1;
+
+	switch(dos->fatbits){
+	case 12:
+		k = (3*n)/2; break;
+	case 16:
+		k = 2*n; break;
+	default:
+		return -1;
+	}
+	if(k >= dos->fatsize*dos->sectsize)
+		panic("getfat");
+
+	sect = (k/(dos->sectsize*dos->clustsize))*dos->clustsize + dos->fataddr;
+	o = k%(dos->sectsize*dos->clustsize);
+	p = getclust(dos, sect);
+	k = p->iobuf[o++];
+	if(o >= dos->sectsize*dos->clustsize){
+		p = getclust(dos, sect+dos->clustsize);
+		o = 0;
+	}
+	k |= p->iobuf[o]<<8;
+	if(dos->fatbits == 12){
+		if(n&1)
+			k >>= 4;
+		else
+			k &= 0xfff;
+		if(k >= 0xff8)
+			k |= 0xf000;
+	}
+	k = k < 0xfff8 ? k : -1;
+	chat("fatwalk %d -> %d\n", n, k);
+	return k;
+}
+
+/*
+ *  map a file's logical cluster address to a physical sector address
+ */
+static long
+fileaddr(Dosfile *fp, long ltarget)
+{
+	Dos *dos = fp->dos;
+	long l;
+	long p;
+
+	chat("fileaddr %8.8s %d\n", fp->name, ltarget);
+	/*
+	 *  root directory is contiguous and easy
+	 */
+	if(fp->pstart == 0){
+		if(ltarget*dos->sectsize*dos->clustsize >= dos->rootsize*sizeof(Dosdir))
+			return -1;
+		l = dos->rootaddr + ltarget*dos->clustsize;
+		chat("fileaddr %d -> %d\n", ltarget, l);
+		return l;
+	}
+
+	/*
+	 *  anything else requires a walk through the fat
+	 */
+	if(ltarget >= fp->lcurrent && fp->pcurrent){
+		/* start at the currrent point */
+		l = fp->lcurrent;
+		p = fp->pcurrent;
+	} else {
+		/* go back to the beginning */
+		l = 0;
+		p = fp->pstart;
+	}
+	while(l != ltarget){
+		/* walk the fat */
+		p = fatwalk(dos, p);
+		if(p < 0)
+			return -1;
+		l++;
+	}
+	fp->lcurrent = l;
+	fp->pcurrent = p;
+
+	/*
+	 *  clusters start at 2 instead of 0 (why? - presotto)
+	 */
+	l =  dos->dataaddr + (p-2)*dos->clustsize;
+	chat("fileaddr %d -> %d\n", ltarget, l);
+	return l;
+}
+
+/*
+ *  read from a dos file
+ */
+long
+dosread(Dosfile *fp, void *a, long n)
+{
+	long addr;
+	long rv;
+	int i;
+	int off;
+	Clustbuf *p;
+	uchar *from, *to;
+
+	if((fp->attr & DDIR) == 0){
+		if(fp->offset >= fp->length)
+			return 0;
+		if(fp->offset+n > fp->length)
+			n = fp->length - fp->offset;
+	}
+
+	to = a;
+	for(rv = 0; rv < n; rv+=i){
+		/*
+		 *  read the cluster
+		 */
+		addr = fileaddr(fp, fp->offset/fp->dos->clustbytes);
+		if(addr < 0)
+			return -1;
+		p = getclust(fp->dos, addr);
+		if(p == 0)
+			return -1;
+
+		/*
+		 *  copy the bytes we need
+		 */
+		off = fp->offset % fp->dos->clustbytes;
+		from = &p->iobuf[off];
+		i = n - rv;
+		if(i > fp->dos->clustbytes - off)
+			i = fp->dos->clustbytes - off;
+		memmove(to, from, i);
+		to += i;
+		fp->offset += i;
+	}
+
+	return rv;
+}
+
+/*
+ *  walk a directory returns
+ * 	-1 if something went wrong
+ *	 0 if not found
+ *	 1 if found
+ */
+int
+doswalk(Dosfile *file, char *name)
+{
+	Dosdir d;
+	long n;
+
+	if((file->attr & DDIR) == 0){
+		chat("walking non-directory!\n");
+		return -1;
+	}
+
+	setname(file, name);
+
+	file->offset = 0;	/* start at the beginning */
+	while((n = dosread(file, &d, sizeof(d))) == sizeof(d)){
+		chat("comparing to %8.8s.%3.3s\n", d.name, d.ext);
+		if(memcmp(file->name, d.name, sizeof(d.name)) != 0)
+			continue;
+		if(memcmp(file->ext, d.ext, sizeof(d.ext)) != 0)
+			continue;
+		if(d.attr & DVLABEL){
+			chat("%8.8s.%3.3s is a LABEL\n", d.name, d.ext);
+			continue;
+		}
+		file->attr = d.attr;
+		file->pstart = GSHORT(d.start);
+		file->length = GLONG(d.length);
+		file->pcurrent = 0;
+		file->lcurrent = 0;
+		file->offset = 0;
+		return 1;
+	}
+	return n >= 0 ? 0 : -1;
+}
+
+
+/*
+ *  instructions that boot blocks can start with
+ */
+#define	JMPSHORT	0xeb
+#define JMPNEAR		0xe9
+
+/*
+ *  read dos file system properties
+ */
+int
+dosinit(Dos *dos, int start, int ishard)
+{
+	Dosboot *b;
+	int i;
+	Clustbuf *p;
+	Dospart *dp;
+	ulong mbroffset, offset;
+
+	/* defaults till we know better */
+	dos->start = start;
+	dos->sectsize = 512;
+	dos->clustsize = 1;
+	mbroffset = 0;
+
+dmddo:
+	/* get first sector */
+	p = getclust(dos, mbroffset);
+	if(p == 0){
+		chat("can't read boot block\n");
+		return -1;
+	}
+
+	/*
+	 * If it's a hard disc then look for an MBR and pick either an
+	 * active partition or the FAT with the lowest starting LBA.
+	 * Things are tricky because we could be pointing to, amongst others:
+	 *	1) a floppy BPB;
+	 *	2) a hard disc MBR;
+	 *	3) a hard disc extended partition table;
+	 *	4) a logical drive on a hard disc;
+	 *	5) a disc-manager boot block.
+	 * They all have the same magic at the end of the block.
+	 */
+	if(p->iobuf[0x1FE] != 0x55 || p->iobuf[0x1FF] != 0xAA) {
+		chat("not DOS\n");
+		return -1;
+	}
+	p->dos = 0;
+	b = (Dosboot *)p->iobuf;
+	if(ishard && b->mediadesc != 0xF8){
+		dp = (Dospart*)&p->iobuf[0x1BE];
+		offset = 0xFFFFFFFF;
+		for(i = 0; i < 4; i++, dp++){
+			if(dp->type == DMDDO){
+				mbroffset = 63;
+				goto dmddo;
+			}
+			if(dp->type != FAT12 && dp->type != FAT16 && dp->type != FATHUGE)
+				continue;
+			if(dp->flag & 0x80){
+				offset = GLONG(dp->start);
+				break;
+			}
+			if(GLONG(dp->start) < offset)
+				offset = GLONG(dp->start);
+		}
+		if(i != 4 || offset != 0xFFFFFFFF){
+			dos->start = mbroffset+offset;
+			p = getclust(dos, 0);
+			if(p == 0 || p->iobuf[0x1FE] != 0x55 || p->iobuf[0x1FF] != 0xAA)
+				return -1;
+		}
+		p->dos = 0;
+	}
+
+	b = (Dosboot *)p->iobuf;
+	if(b->magic[0] != JMPNEAR && (b->magic[0] != JMPSHORT || b->magic[2] != 0x90)){
+		chat("no dos file system\n");
+		return -1;
+	}
+
+	if(chatty)
+		bootdump(b);
+
+	/*
+	 *  determine the systems' wondersous properties
+	 */
+	dos->sectsize = GSHORT(b->sectsize);
+	dos->clustsize = b->clustsize;
+	dos->clustbytes = dos->sectsize*dos->clustsize;
+	dos->nresrv = GSHORT(b->nresrv);
+	dos->nfats = b->nfats;
+	dos->rootsize = GSHORT(b->rootsize);
+	dos->volsize = GSHORT(b->volsize);
+	if(dos->volsize == 0)
+		dos->volsize = GLONG(b->bigvolsize);
+	dos->mediadesc = b->mediadesc;
+	dos->fatsize = GSHORT(b->fatsize);
+	dos->fataddr = dos->nresrv;
+	dos->rootaddr = dos->fataddr + dos->nfats*dos->fatsize;
+	i = dos->rootsize*sizeof(Dosdir) + dos->sectsize - 1;
+	i = i/dos->sectsize;
+	dos->dataaddr = dos->rootaddr + i;
+	dos->fatclusters = 2+(dos->volsize - dos->dataaddr)/dos->clustsize;
+	if(dos->fatclusters < 4087)
+		dos->fatbits = 12;
+	else
+		dos->fatbits = 16;
+	dos->freeptr = 2;
+
+	/*
+	 *  set up the root
+	 */
+	dos->root.dos = dos;
+	dos->root.pstart = 0;
+	dos->root.pcurrent = dos->root.lcurrent = 0;
+	dos->root.offset = 0;
+	dos->root.attr = DDIR;
+	dos->root.length = dos->rootsize*sizeof(Dosdir);
+
+	return 0;
+}
+
+static void
+bootdump(Dosboot *b)
+{
+	if(chatty == 0)
+		return;
+	print("magic: 0x%2.2x 0x%2.2x 0x%2.2x\n",
+		b->magic[0], b->magic[1], b->magic[2]);
+	print("version: \"%8.8s\"\n", b->version);
+	print("sectsize: %d\n", GSHORT(b->sectsize));
+	print("allocsize: %d\n", b->clustsize);
+	print("nresrv: %d\n", GSHORT(b->nresrv));
+	print("nfats: %d\n", b->nfats);
+	print("rootsize: %d\n", GSHORT(b->rootsize));
+	print("volsize: %d\n", GSHORT(b->volsize));
+	print("mediadesc: 0x%2.2x\n", b->mediadesc);
+	print("fatsize: %d\n", GSHORT(b->fatsize));
+	print("trksize: %d\n", GSHORT(b->trksize));
+	print("nheads: %d\n", GSHORT(b->nheads));
+	print("nhidden: %d\n", GLONG(b->nhidden));
+	print("bigvolsize: %d\n", GLONG(b->bigvolsize));
+	print("driveno: %d\n", b->driveno);
+	print("reserved0: 0x%2.2x\n", b->reserved0);
+	print("bootsig: 0x%2.2x\n", b->bootsig);
+	print("volid: 0x%8.8x\n", GLONG(b->volid));
+	print("label: \"%11.11s\"\n", b->label);
+}
+
+/*
+ *  grab next element from a path, return the pointer to unprocessed portion of
+ *  path.
+ */
+static char *
+nextelem(char *path, char *elem)
+{
+	int i;
+
+	while(*path == '/')
+		path++;
+	if(*path==0 || *path==' ')
+		return 0;
+	for(i=0; *path!='\0' && *path!='/' && *path!=' '; i++){
+		if(i==28){
+			print("name component too long\n");
+			return 0;
+		}
+		*elem++ = *path++;
+	}
+	*elem = '\0';
+	return path;
+}
+
+int
+dosstat(Dos *dos, char *path, Dosfile *f)
+{
+	char element[NAMELEN];
+
+	*f = dos->root;
+	while(path = nextelem(path, element)){
+		switch(doswalk(f, element)){
+		case -1:
+			return -1;
+		case 0:
+			return 0;
+		}
+	}
+	return 1;
+}
+
+/*
+ *  boot
+ */
+int
+dosboot(Dos *dos, char *path)
+{
+	Dosfile file;
+	long n;
+	long addr;
+	Exec *ep;
+	void (*b)(void);
+
+	switch(dosstat(dos, path, &file)){
+
+	case -1:
+		print("error walking to %s\n", path);
+		return -1;
+	case 0:
+		print("%s not found\n", path);
+		return -1;
+	case 1:
+		print("found %8.8s.%3.3s attr 0x%ux start 0x%lux len %d\n", file.name,
+			file.ext, file.attr, file.pstart, file.length);
+		break;
+	}
+
+	/*
+	 *  read header
+	 */
+	ep = (Exec*)ialloc(sizeof(Exec), 0);
+	n = sizeof(Exec);
+	if(dosreadseg(&file, n, (ulong) ep) != n){
+		print(premature);
+		return -1;
+	}
+	if(GLLONG(ep->magic) != E_MAGIC){
+		print("bad magic 0x%lux not a plan 9 executable!\n", GLLONG(ep->magic));
+		return -1;
+	}
+
+	/*
+	 *  read text
+	 */
+	addr = PADDR(GLLONG(ep->entry));
+	n = GLLONG(ep->text);
+	print("+%d", n);
+	if(dosreadseg(&file, n, addr) != n){
+		print(premature);
+		return -1;
+	}
+
+	/*
+	 *  read data (starts at first page after kernel)
+	 */
+	addr = PGROUND(addr+n);
+	n = GLLONG(ep->data);
+	print("+%d", n);
+	if(dosreadseg(&file, n, addr) != n){
+		print(premature);
+		return -1;
+	}
+
+	/*
+	 *  bss and entry point
+	 */
+	print("+%d\nstart at 0x%lux\n", GLLONG(ep->bss), GLLONG(ep->entry));
+
+	/*
+	 *  Go to new code. It's up to the program to get its PC relocated to
+	 *  the right place.
+	 */
+	b = (void (*)(void))(PADDR(GLLONG(ep->entry)));
+	(*b)();
+	return 0;
+}
+
+/*
+ *  read in a segment
+ */
+long
+dosreadseg(Dosfile *fp, long len, long addr)
+{
+	char *a;
+	long n, sofar;
+
+	a = (char *)addr;
+	for(sofar = 0; sofar < len; sofar += n){
+		n = 8*1024;
+		if(len - sofar < n)
+			n = len - sofar;
+		n = dosread(fp, a + sofar, n);
+		if(n <= 0)
+			break;
+		print(".");
+	}
+	return sofar;
+}
+
+/*
+ *  set up a dos file name
+ */
+static void
+setname(Dosfile *fp, char *from)
+{
+	char *to;
+
+	to = fp->name;
+	for(; *from && to-fp->name < 8; from++, to++){
+		if(*from == '.'){
+			from++;
+			break;
+		}
+		if(*from >= 'a' && *from <= 'z')
+			*to = *from + 'A' - 'a';
+		else
+			*to = *from;
+	}
+	while(to - fp->name < 8)
+		*to++ = ' ';
+	
+	to = fp->ext;
+	for(; *from && to-fp->ext < 3; from++, to++){
+		if(*from >= 'a' && *from <= 'z')
+			*to = *from + 'A' - 'a';
+		else
+			*to = *from;
+	}
+	while(to-fp->ext < 3)
+		*to++ = ' ';
+
+	chat("name is %8.8s %3.3s\n", fp->name, fp->ext);
+}
--- /dev/null
+++ b/os/boot/puma/dosfs.h
@@ -1,0 +1,110 @@
+typedef struct Dosboot	Dosboot;
+typedef struct Dos	Dos;
+typedef struct Dosdir	Dosdir;
+typedef struct Dosfile	Dosfile;
+typedef struct Dospart	Dospart;
+
+struct Dospart
+{
+	uchar flag;		/* active flag */
+	uchar shead;		/* starting head */
+	uchar scs[2];		/* starting cylinder/sector */
+	uchar type;		/* partition type */
+	uchar ehead;		/* ending head */
+	uchar ecs[2];		/* ending cylinder/sector */
+	uchar start[4];		/* starting sector */
+	uchar len[4];		/* length in sectors */
+};
+
+#define FAT12	0x01
+#define FAT16	0x04
+#define FATHUGE	0x06
+#define DMDDO	0x54
+
+struct Dosboot{
+	uchar	magic[3];
+	uchar	version[8];
+	uchar	sectsize[2];
+	uchar	clustsize;
+	uchar	nresrv[2];
+	uchar	nfats;
+	uchar	rootsize[2];
+	uchar	volsize[2];
+	uchar	mediadesc;
+	uchar	fatsize[2];
+	uchar	trksize[2];
+	uchar	nheads[2];
+	uchar	nhidden[4];
+	uchar	bigvolsize[4];
+	uchar	driveno;
+	uchar	reserved0;
+	uchar	bootsig;
+	uchar	volid[4];
+	uchar	label[11];
+	uchar	reserved1[8];
+};
+
+struct Dosfile{
+	Dos	*dos;		/* owning dos file system */
+	char	name[8];
+	char	ext[3];
+	uchar	attr;
+	long	length;
+	long	pstart;		/* physical start cluster address */
+	long	pcurrent;	/* physical current cluster address */
+	long	lcurrent;	/* logical current cluster address */
+	long	offset;
+};
+
+struct Dos{
+	int	dev;				/* device id */
+	long	(*read)(int, void*, long);	/* read routine */
+	long	(*seek)(int, long);		/* seek routine */
+
+	int	start;		/* start of file system */
+	int	sectsize;	/* in bytes */
+	int	clustsize;	/* in sectors */
+	int	clustbytes;	/* in bytes */
+	int	nresrv;		/* sectors */
+	int	nfats;		/* usually 2 */
+	int	rootsize;	/* number of entries */
+	int	volsize;	/* in sectors */
+	int	mediadesc;
+	int	fatsize;	/* in sectors */
+	int	fatclusters;
+	int	fatbits;	/* 12 or 16 */
+	long	fataddr;	/* sector number */
+	long	rootaddr;
+	long	dataaddr;
+	long	freeptr;
+
+	Dosfile	root;
+};
+
+struct Dosdir{
+	uchar	name[8];
+	uchar	ext[3];
+	uchar	attr;
+	uchar	reserved[10];
+	uchar	time[2];
+	uchar	date[2];
+	uchar	start[2];
+	uchar	length[4];
+};
+
+#define	DRONLY	0x01
+#define	DHIDDEN	0x02
+#define	DSYSTEM	0x04
+#define	DVLABEL	0x08
+#define	DDIR	0x10
+#define	DARCH	0x20
+
+extern int chatty;
+
+extern int dosboot(Dos*, char*);
+extern int dosinit(Dos*, int, int);
+extern long dosread(Dosfile*, void*, long);
+extern int dosstat(Dos*, char*, Dosfile*);
+extern int doswalk(Dosfile*, char*);
+
+extern int plan9ini(Dos*, char*);
--- /dev/null
+++ b/os/boot/puma/ebsit.trap.c
@@ -1,0 +1,206 @@
+#include "u.h"
+#include "../port/lib.h"
+#include "mem.h"
+#include "ebsit.h"
+#include "dat.h"
+#include "fns.h"
+
+#include "ureg.h"
+
+int inpanic;
+
+#define CSR ((ushort *) 0x2000000)
+ 
+
+typedef struct Irqctlr {
+	uint	addr;
+  	uint	enabled;
+	struct {
+		void	(*r)(Ureg*, void*);
+		void 	*a;
+	} h[16];
+} Irqctlr;
+
+static Irqctlr irqctlr;  
+
+void 
+csrset( int bit )
+{
+static ushort *csr_val = 0x8c;
+
+	*csr_val ^= (1 << bit);
+	putcsr(*csr_val);
+}
+
+void
+intrinit( void )
+{
+int offset;
+ulong op;
+
+
+	irqctlr.addr = 1;
+	irqctlr.enabled = 0;
+ 
+	/* Reset Exception */
+	offset = ((((ulong) _vsvccall) - 0x0)-8) >> 2;
+	op = ( 0xea << 24 ) | offset;
+	*((ulong *) 0x0) = op;
+
+	/* Undefined Instruction Exception */
+	offset = ((((ulong) _vundcall) - 0x4)-8) >> 2;
+	op = ( 0xea << 24 ) | offset;
+	*((ulong *) 0x4) = op;
+
+	/* SWI Exception */
+	offset = ((((ulong) _vsvccall) - 0x8)-8) >> 2;
+	op = ( 0xea << 24 ) | offset;
+	*((ulong *) 0x8) = op;
+
+	/* Prefetch Abort Exception */
+	offset = ((((ulong) _vpabcall) - 0xc)-8) >> 2;
+	op = ( 0xea << 24 ) | offset;
+	*((ulong *) 0xc) = op;
+
+	/* Data Abort Exception */
+	offset = ((((ulong) _vdabcall) - 0x10)-8) >> 2;
+	op = ( 0xea << 24 ) | offset;
+	*((ulong *) 0x10) = op;
+
+	/* IRQ Exception */
+	offset = ((((ulong) _virqcall) - 0x18)-8) >> 2;
+	op = ( 0xea << 24 ) | offset;
+	*((ulong *) 0x18) = op;
+
+
+}
+
+void
+intrenable(uint addr, int bit, void (*r)(Ureg*, void*), void* a)
+{
+	int i;
+	USED(addr);
+	for(i = 0; i < 16; i++)
+		{
+		if((bit & (1<<i)) == 0)
+			continue;
+		irqctlr.h[i].r = r;
+		irqctlr.h[i].a = a;
+		irqctlr.enabled |= (1<<i); 
+		if (i < 7) 
+			csrset(i);
+		}
+	return;
+}
+
+int lucifer;					/* Global to store the last CSR (eric) */
+
+static void
+interrupt(Ureg* ureg)
+{
+	int i, mask;
+
+ 		mask = *CSR;
+		lucifer = mask;			/* eric */
+		if(irqctlr.enabled == 0){
+		
+			return;
+ 			}
+		for(i = 0; i < 16; i++)
+			{
+
+			if((irqctlr.enabled & (1<<i)) == 0)
+				continue;
+			if(( mask & (1 << i)) == 0)
+				continue;
+			if (!irqctlr.h[i].r)
+				continue;
+			(irqctlr.h[i].r)(ureg, irqctlr.h[i].a);
+			mask &= ~(1 << i);
+			}
+
+		if ((mask) && (mask < 0x90))		/* ignore non-maskable interrupts */
+			{
+			print("unknown or unhandled interrupt\n");
+			panic("unknown or unhandled interrupt: mask=%ux",mask);
+			}
+	
+}
+
+static void
+dumpregs(Ureg* ureg)
+{
+	print("PSR %8.8uX type %2.2uX PC %8.8uX LINK %8.8uX\n",
+		ureg->psr, ureg->type, ureg->pc, ureg->link);
+	print("R14 %8.8uX R13 %8.8uX R12 %8.8uX R11 %8.8uX R10 %8.8uX\n",
+		ureg->r14, ureg->r13, ureg->r12, ureg->r11, ureg->r10);
+	print("R9  %8.8uX R8  %8.8uX R7  %8.8uX R6  %8.8uX R5  %8.8uX\n",
+		ureg->r9, ureg->r8, ureg->r7, ureg->r6, ureg->r5);
+	print("R4  %8.8uX R3  %8.8uX R2  %8.8uX R1  %8.8uX R0  %8.8uX\n",
+		ureg->r4, ureg->r3, ureg->r2, ureg->r1, ureg->r0);
+	print("Last Interrupt's CSR: %8.8uX\n",lucifer);
+	print("CPSR %8.8uX SPSR %8.8uX\n", cpsrr(), spsrr());
+}
+
+void
+dumpstack(void)
+{
+}
+
+void
+exception(Ureg* ureg)
+{
+	static Ureg old_ureg;
+	uint far =0;
+	uint fsr =0;
+
+	static lasttype = 0;
+
+	LOWBAT;	
+	
+	USED(far, fsr);
+
+	lasttype = ureg->type;
+
+	/*
+	 * All interrupts/exceptions should be resumed at ureg->pc-4,
+	 * except for Data Abort which resumes at ureg->pc-8.
+	 */
+
+	if(ureg->type == (PsrMabt+1))
+		ureg->pc -= 8;
+	else
+		ureg->pc -= 4;
+
+	switch(ureg->type){
+
+	case PsrMfiq:				/* (Fast) */
+		print("Fast\n");
+		print("We should never be here\n");
+		while(1);
+
+	case PsrMirq:				/* Interrupt Request */
+		interrupt(ureg);
+		break;
+
+	case PsrMund:				/* Undefined instruction */
+		print("Undefined instruction\n");
+	case PsrMsvc:				/* Jump through 0, SWI or reserved trap */
+		print("SWI/SVC trap\n");
+	case PsrMabt:				/* Prefetch abort */
+		print("Prefetch Abort\n");
+	case PsrMabt+1:				/* Data abort */
+		print("Data Abort\n");
+
+
+	default:
+		dumpregs(ureg);
+		/* panic("exception %uX\n", ureg->type); */
+		break;
+	}
+
+	LOWBAT;				/* Low bat off after interrupt */
+
+	splhi();
+
+}
--- /dev/null
+++ b/os/boot/puma/ether.c
@@ -1,0 +1,156 @@
+#include "u.h"
+#include "lib.h"
+#include "mem.h"
+#include "dat.h"
+#include "fns.h"
+#include "io.h"
+
+#include "ether.h"
+
+static Ctlr ether[MaxEther];
+
+static struct {
+	char	*type;
+	int	(*reset)(Ctlr*);
+} cards[] = {
+	{ "CS8900", cs8900reset, },
+	{ 0, }
+};
+
+int
+etherinit(void)
+{
+	Ctlr *ctlr;
+	int ctlrno, i, mask, n;
+
+	mask = 0;
+	for(ctlrno = 0; ctlrno < MaxEther; ctlrno++){
+		ctlr = &ether[ctlrno];
+		memset(ctlr, 0, sizeof(Ctlr));
+		if(isaconfig("ether", ctlrno, &ctlr->card) == 0)
+			continue;
+		for(n = 0; cards[n].type; n++){
+			if(strcmp(cards[n].type, ctlr->card.type))
+				continue;
+			ctlr->ctlrno = ctlrno;
+			if((*cards[n].reset)(ctlr))
+				break;
+
+			ctlr->iq = qopen(16*1024, 1, 0, 0);
+			ctlr->oq = qopen(16*1024, 1, 0, 0);
+
+			ctlr->present = 1;
+			mask |= 1<<ctlrno;
+
+			print("ether%d: %s: port 0x%luX irq %d",
+				ctlr->ctlrno, ctlr->card.type, ctlr->card.port, ctlr->card.irq);
+			if(ctlr->card.mem)
+				print(" addr 0x%luX", ctlr->card.mem & ~KZERO);
+			if(ctlr->card.size)
+				print(" size 0x%luX", ctlr->card.size);
+			print(":");
+			for(i = 0; i < sizeof(ctlr->card.ea); i++)
+				print(" %2.2uX", ctlr->card.ea[i]);
+			print("\n"); uartwait();
+			setvec(ctlr->card.irq, ctlr->card.intr, ctlr);
+			break;
+		}
+	}
+
+	return mask;
+}
+
+static Ctlr*
+attach(int ctlrno)
+{
+	Ctlr *ctlr;
+
+	if(ctlrno >= MaxEther || ether[ctlrno].present == 0)
+		return 0;
+
+	ctlr = &ether[ctlrno];
+	if(ctlr->present == 1){
+		ctlr->present = 2;
+		(*ctlr->card.attach)(ctlr);
+	}
+
+	return ctlr;
+}
+
+uchar*
+etheraddr(int ctlrno)
+{
+	Ctlr *ctlr;
+
+	if((ctlr = attach(ctlrno)) == 0)
+		return 0;
+
+	return ctlr->card.ea;
+}
+
+int
+etherrxpkt(int ctlrno, Etherpkt *pkt, int timo)
+{
+	int n;
+	Ctlr *ctlr;
+	Block *b;
+	ulong start;
+
+	if((ctlr = attach(ctlrno)) == 0)
+		return 0;
+
+	start = m->ticks;
+	while((b = qget(ctlr->iq)) == 0){
+		if(TK2MS(m->ticks - start) >= timo){
+			/*
+			print("ether%d: rx timeout\n", ctlrno);
+			 */
+			return 0;
+		}
+		//delay(1);
+	}
+
+	n = BLEN(b);
+	memmove(pkt, b->rp, n);
+	freeb(b);
+
+	return n;
+}
+
+int
+etheriq(Ctlr *ctlr, Block *b, int freebp)
+{
+	if(memcmp(((Etherpkt*)b->rp)->d, ctlr->card.ea, Eaddrlen) != 0){
+		if(freebp)
+			freeb(b);
+		return 0;
+	}
+	qbwrite(ctlr->iq, b);
+	return 1;
+}
+
+int
+ethertxpkt(int ctlrno, Etherpkt *pkt, int len, int)
+{
+	Ctlr *ctlr;
+	Block *b;
+	int s;
+
+	if((ctlr = attach(ctlrno)) == 0)
+		return 0;
+
+	if(qlen(ctlr->oq) > 16*1024){
+		print("ether%d: tx queue full\n", ctlrno);
+		return 0;
+	}
+	b = iallocb(sizeof(Etherpkt));
+	memmove(b->wp, pkt, len);
+	memmove(((Etherpkt*)b->wp)->s, ctlr->card.ea, Eaddrlen);
+	b->wp += len;
+	qbwrite(ctlr->oq, b);
+	s = splhi();
+	(*ctlr->card.transmit)(ctlr);
+	splx(s);
+
+	return 1;
+}
--- /dev/null
+++ b/os/boot/puma/ether.h
@@ -1,0 +1,82 @@
+/*
+ * All the goo for PC ethernet cards.
+ */
+typedef struct Card Card;
+typedef struct RingBuf RingBuf;
+typedef struct Type Type;
+typedef struct Ctlr Ctlr;
+
+/*
+ * Hardware interface.
+ */
+struct Card {
+	ISAConf;
+
+	int	(*reset)(Ctlr*);
+	void	(*attach)(Ctlr*);
+
+	void	*(*read)(Ctlr*, void*, ulong, ulong);
+	void	*(*write)(Ctlr*, ulong, void*, ulong);
+
+	void	(*receive)(Ctlr*);
+	void	(*transmit)(Ctlr*);
+	void	(*intr)(Ureg*, Ctlr*);
+	void	(*overflow)(Ctlr*);
+
+	uchar	bit16;			/* true if a 16 bit interface */
+	uchar	ram;			/* true if card has shared memory */
+
+	ulong	dp8390;			/* I/O address of 8390 (if any) */
+	ulong	data;			/* I/O data port if no shared memory */
+	uchar	nxtpkt;			/* software bndry */
+	uchar	tstart;			/* 8390 ring addresses */
+	uchar	pstart;
+	uchar	pstop;
+
+	uchar	dummyrr;		/* do dummy remote read */
+};
+
+/*
+ * Software ring buffer.
+ */
+struct RingBuf {
+	uchar	owner;
+	uchar	busy;			/* unused */
+	ushort	len;
+	uchar	pkt[sizeof(Etherpkt)];
+};
+
+enum {
+	Host		= 0,		/* buffer owned by host */
+	Interface	= 1,		/* buffer owned by card */
+
+	Nrb		= 16,		/* default number of receive buffers */
+	Ntb		= 2,		/* default number of transmit buffers */
+};
+
+/*
+ * Software controller.
+ */
+struct Ctlr {
+	Card	card;			/* hardware info */
+	int	ctlrno;
+	int	present;
+
+	Queue*	iq;
+	Queue*	oq;
+
+	int	inpackets;
+	int	outpackets;
+	int	crcs;			/* input crc errors */
+	int	oerrs;			/* output errors */
+	int	frames;			/* framing errors */
+	int	overflows;		/* packet overflows */
+	int	buffs;			/* buffering errors */
+};
+
+#define NEXT(x, l)	(((x)+1)%(l))
+#define	HOWMANY(x, y)	(((x)+((y)-1))/(y))
+#define ROUNDUP(x, y)	(HOWMANY((x), (y))*(y))
+
+extern int cs8900reset(Ctlr*);
+extern int	etheriq(Ctlr*, Block*, int);
--- /dev/null
+++ b/os/boot/puma/ether8900.c
@@ -1,0 +1,555 @@
+/*
+ * Crystal CS8900 ethernet controller
+ * Specifically for the Teralogic Puma architecture
+ */
+
+#include "u.h"
+#include "lib.h"
+#include "mem.h"
+#include "dat.h"
+#include "fns.h"
+#include "io.h"
+
+#include "ether.h"
+#include "puma.h"
+
+/*
+ * On the Puma board the CS8900 can be addressed from either 
+ * ISA I/O space or ISA memory space at the following locations.
+ * The cs8900 address pins are shifted by 1 relative to the CPU.
+ */
+enum {
+	IsaIOBase		= 0xf0000000,
+	IsaMemBase	= 0xe0000000,
+
+	IOBase		= 0x300,
+	MemBase		= 0xc0000,
+};
+
+/* I/O accesses */
+#define	out16(port, val)	(*((ushort *)IsaIOBase + IOBase + (port)) = (val))
+#define	in16(port)			*((ushort *)IsaIOBase + IOBase + (port))
+#define	in8(port)			*((uchar *)IsaIOBase + ((IOBase+(port))<<1))
+#define	regIOw(reg, val)	 do {out16(PpPtr, (reg)|0x3000); out16(PpData, val);} while(0)
+#define	regIOr(reg)		(out16(PpPtr, (reg)|0x3000), in16(PpData))
+#define	regIOr1(reg)		(out16(PpPtr, (reg)|0x3000), in16(PpData1))
+
+/* Memory accesses */
+#define	regw(reg, val)		*((ushort *)IsaMemBase + MemBase + (reg)) = (val)
+#define	regr(reg)			*((ushort *)IsaMemBase + MemBase + (reg))
+
+/* Puma frame copying */
+#define	copyout(src, len)	{ \
+						int _len = (len); \
+						ushort *_src = (ushort *)(src); \
+						ushort *_dst = (ushort *)IsaMemBase + MemBase + TxFrame; \
+						while(_len > 0) { \
+							*_dst++ = *_src++; \
+							_dst++; \
+							_len -= 2; \
+						} \
+					}
+#define	copyoutIO(src, len)	{ \
+						int _len = (len); \
+						ushort *_src = (ushort *)(src); \
+						while(_len > 0) { \
+							out16(RxTxData, *_src); \
+							_src++; \
+							_len -= 2; \
+						} \
+					}
+#define	copyin(dst, len)	{ \
+						int _len = (len), _len2 = (len)&~1; \
+						ushort *_src = (ushort *)IsaMemBase + MemBase + RxFrame; \
+						ushort *_dst = (ushort *)(dst); \
+						while(_len2 > 0) { \
+							*_dst++ = *_src++; \
+							_src++; \
+							_len2 -= 2; \
+						} \
+						if(_len&1) \
+							*(uchar*)_dst = (*_src)&0xff; \
+					}
+#define	copyinIO(dst, len)	{ \
+						int _i, _len = (len), _len2 = (len)&~1; \
+						ushort *_dst = (ushort *)(dst); \
+						_i = in16(RxTxData); USED(_i); /* RxStatus */ \
+						_i = in16(RxTxData); USED(_i); /* RxLen */ \
+						while(_len2 > 0) { \
+							*_dst++ = in16(RxTxData); \
+							_len2 -= 2; \
+						} \
+						if(_len&1) \
+							*(uchar*)_dst = (in16(RxTxData))&0xff; \
+					}
+						
+	
+
+enum {					/* I/O Mode Register Offsets */
+	RxTxData	= 0x00,		/* receive/transmit data - port 0 */
+	TxCmdIO = 0x04,		/* transmit command */
+	TxLenIO	= 0x06,		/* transmit length */
+	IsqIO	= 0x08,		/* Interrupt status queue */
+	PpPtr	= 0x0a,		/* packet page pointer */
+	PpData	= 0x0c,		/* packet page data */
+	PpData1	= 0x0e,		/* packet page data - port 1*/
+};
+
+enum {					/* Memory Mode Register Offsets */
+	/* Bus Interface Registers */
+	Ern		= 0x0000,		/* EISA registration numberion */
+	Pic		= 0x0002,		/* Product identification code */
+	Iob		= 0x0020,		/* I/O base address */
+	Intr		= 0x0022,		/* interrupt number */
+	Mba		= 0x002c,		/* memory base address */
+	
+	Ecr		= 0x0040,		/* EEPROM command register */
+	Edw		= 0x0042,		/* EEPROM data word */
+	Rbc		= 0x0050,		/* receive frame byte counter */
+
+	/* Status and Control Registers */
+	RxCfg	= 0x0102,
+	RxCtl	= 0x0104,
+	TxCfg	= 0x0106,
+	BufCfg	= 0x010a,
+	LineCtl	= 0x0112,
+	SelfCtl	= 0x0114,
+	BusCtl	= 0x0116,
+	TestCtl	= 0x0118,
+	Isq		= 0x0120,
+	RxEvent	= 0x0124,
+	TxEvent	= 0x0128,
+	BufEvent	= 0x012c,
+	RxMISS	= 0x0130,
+	TxCol	= 0x0132,
+	LineSt	= 0x0134,
+	SelfSt	= 0x0136,
+	BusSt	= 0x0138,
+	Tdr		= 0x013c,
+
+	/* Initiate Transmit Registers */
+	TxCmd	= 0x0144,		/* transmit command */
+	TxLen	= 0x0146,		/* transmit length */
+
+	/* Address Filter Registers */
+	IndAddr	= 0x0158,		/* individual address registers */
+
+	/* Frame Location */
+	RxStatus	= 0x0400,		/* receive status */
+	RxLen	= 0x0402,		/* receive length */
+	RxFrame	= 0x0404,		/* receive frame location */
+	TxFrame	= 0x0a00,		/* transmit frame location */
+};
+
+enum {					/* Ecr */
+	Addr			= 0x00ff,		/* EEPROM word address (field) */
+	Opcode		= 0x0300,		/* command opcode (field) */
+};
+
+enum {					/* Isq */
+	Regnum		= 0x003f,		/* register number held by Isq (field) */
+		IsqRxEvent	= 0x04,
+		IsqTxEvent	= 0x08,
+		IsqBufEvent	= 0x0c,
+		IsqRxMiss		= 0x10,
+		IsqTxCol		= 0x12,
+	RegContent 	= 0xffc0,		/* register data contents (field) */
+};
+
+enum {					/* RxCfg */
+	Skip_1		= 0x0040,
+	StreamE		= 0x0080,
+	RxOKiE		= 0x0100,
+	RxDMAonly	= 0x0200,
+	AutoRxDMAE	= 0x0400,
+	BufferCRC		= 0x0800,
+	CRCerroriE	= 0x1000,
+	RuntiE		= 0x2000,
+	ExtradataiE	= 0x4000,
+};
+
+enum {					/* RxEvent */
+	IAHash		= 0x0040,
+	Dribblebits	= 0x0080,
+	RxOK		= 0x0100,
+	Hashed		= 0x0200,
+	IndividualAdr	= 0x0400,
+	Broadcast		= 0x0800,
+	CRCerror		= 0x1000,
+	Runt			= 0x2000,
+	Extradata		= 0x4000,
+};
+
+enum {					/* RxCtl */
+	IAHashA		= 0x0040,
+	PromiscuousA	= 0x0080,
+	RxOKA		= 0x0100,
+	MulticastA	= 0x0200,
+	IndividualA	= 0x0400,
+	BroadcastA	= 0x0800,
+	CRCerrorA	= 0x1000,
+	RuntA		= 0x2000,
+	ExtradataA	= 0x4000,
+};
+
+enum {					/* TxCfg */
+	LossofCRSiE	= 0x0040,
+	SQEerroriE	= 0x0080,
+	TxOKiE		= 0x0100,
+	OutofWindowiE	= 0x0200,
+	JabberiE		= 0x0400,
+	AnycolliE		= 0x0800,
+	Coll16iE		= 0x8000,
+};
+
+enum {					/* TxEvent */
+	LossofCRS	= 0x0040,
+	SQEerror		= 0x0080,
+	TxOK		= 0x0100,
+	OutofWindow	= 0x0200,
+	Jabber		= 0x0400,
+	NTxCols		= 0x7800,		/* number of Tx collisions (field) */
+	coll16		= 0x8000,
+};
+
+enum {					/* BufCfg */
+	SWintX		= 0x0040,
+	RxDMAiE		= 0x0080,
+	Rdy4TxiE		= 0x0100,
+	TxUnderruniE	= 0x0200,
+	RxMissiE		= 0x0400,
+	Rx128iE		= 0x0800,
+	TxColOvfiE	= 0x1000,
+	MissOvfloiE	= 0x2000,
+	RxDestiE		= 0x8000,
+};
+
+enum {					/* BufEvent */
+	SWint		= 0x0040,
+	RxDMAFrame	= 0x0080,
+	Rdy4Tx		= 0x0100,
+	TxUnderrun	= 0x0200,
+	RxMiss		= 0x0400,
+	Rx128		= 0x0800,
+	RxDest		= 0x8000,
+};
+
+enum {					/* RxMiss */
+	MissCount	= 0xffc0,
+};
+
+enum {					/* TxCol */
+	ColCount	= 0xffc0,
+};
+
+enum {					/* LineCtl */
+	SerRxOn		= 0x0040,
+	SerTxOn		= 0x0080,
+	Iface			= 0x0300,		/* (field) 01 - AUI, 00 - 10BASE-T, 10 - Auto select */
+	ModBackoffE	= 0x0800,
+	PolarityDis	= 0x1000,
+	DefDis		= 0x2000,
+	LoRxSquelch	= 0x4000,
+};
+
+enum {					/* LineSt */
+	LinkOK		= 0x0080,
+	AUI			= 0x0100,
+	TenBT		= 0x0200,
+	PolarityOK	= 0x1000,
+	CRS			= 0x4000,
+};
+
+enum {					/* SelfCtl */
+	RESET		= 0x0040,
+	SWSuspend	= 0x0100,
+	HWSleepE		= 0x0200,
+	HWStandbyE	= 0x0400,
+};
+
+enum {					/* SelfSt */
+	INITD		= 0x0080,
+	SIBUSY		= 0x0100,
+	EepromPresent	= 0x0200,
+	EepromOK	= 0x0400,
+	ElPresent		= 0x0800,
+	EeSize		= 0x1000,
+};
+
+enum {					/* BusCtl */
+	ResetRxDMA	= 0x0040,
+	UseSA		= 0x0200,
+	MemoryE		= 0x0400,
+	DMABurst		= 0x0800,
+	EnableIRQ		= 0x8000,
+};
+
+enum {					/* BusST */
+	TxBidErr		= 0x0080,
+	Rdy4TxNOW	= 0x0100,
+};
+
+enum {					/* TestCtl */
+	FDX			= 0x4000,		/* full duplex */
+};
+
+enum {					/* TxCmd */
+	TxStart		= 0x00c0,		/* bytes before transmit starts (field) */
+		TxSt5	= 0x0000,		/* start after 5 bytes */
+		TxSt381	= 0x0040,		/* start after 381 bytes */
+		TxSt1021	= 0x0080,		/* start after 1021 bytes */
+		TxStAll	= 0x00c0,		/* start after the entire frame is in the cs8900 */
+	Force		= 0x0100,
+	Onecoll		= 0x0200,
+	InhibitCRC	= 0x1000,
+	TxPadDis		= 0x2000,
+};
+
+static Queue *pendingTx[MaxEther];
+
+static void
+attach(Ctlr *ctlr)
+{
+	int reg;
+
+	USED(ctlr);
+	/* enable transmit and receive */
+	reg = regr(BusCtl);
+	regw(BusCtl, reg|EnableIRQ);
+	reg = regr(LineCtl);
+	regw(LineCtl, reg|SerRxOn|SerTxOn);
+}
+
+static char pbuf[200];
+int
+sprintx(void *f, char *to, int count)
+{
+	int i, printable;
+	char *start = to;
+	uchar *from = f;
+
+	if(count < 0) {
+		print("BAD DATA COUNT %d\n", count);
+		return 0;
+	}
+	printable = 1;
+	if(count > 40)
+		count = 40;
+	for(i=0; i<count && printable; i++)
+		if((from[i]<32 && from[i] !='\n' && from[i] !='\r' && from[i] !='\b' && from[i] !='\t') || from[i]>127)
+			printable = 0;
+	*to++ = '\'';
+	if(printable){
+		memmove(to, from, count);
+		to += count;
+	}else{
+		for(i=0; i<count; i++){
+			if(i>0 && i%4==0)
+				*to++ = ' ';
+			sprint(to, "%2.2ux", from[i]);
+			to += 2;
+		}
+	}
+	*to++ = '\'';
+	*to = 0;
+	return to - start;
+}
+
+static void
+transmit(Ctlr *ctlr)
+{
+	int len, status;
+	Block *b;
+
+	for(;;){
+		/* is TxCmd pending ? - check */
+		if(qlen(pendingTx[ctlr->ctlrno]) > 0)
+			break;
+		b = qget(ctlr->oq);
+		if(b == 0)
+			break;
+		len = BLEN(b);
+		regw(TxCmd, TxSt381);
+		regw(TxLen, len);
+		status = regr(BusSt);
+		if((status & Rdy4TxNOW) == 0) {
+			qbwrite(pendingTx[ctlr->ctlrno], b);
+			break;
+		}
+		/*
+		 * Copy the packet to the transmit buffer.
+		 */
+		copyout(b->rp, len);
+		freeb(b);
+	}
+}
+
+static void
+interrupt(Ureg*, Ctlr *ctlr)
+{
+	int len, events, status;
+	Block *b;
+	Queue *q;
+
+	while((events = regr(Isq)) != 0) {
+		status = events&RegContent;
+	
+		switch(events&Regnum) {
+
+		case IsqBufEvent:
+			if(status&Rdy4Tx) {
+				if(qlen(pendingTx[ctlr->ctlrno]) > 0)
+					q = pendingTx[ctlr->ctlrno];
+				else
+					q = ctlr->oq;
+				b = qget(q);
+				if(b == 0)
+					break;
+				len = BLEN(b);
+				copyout(b->rp, len);
+				freeb(b);
+			} else
+			if(status&TxUnderrun) {
+				print("TxUnderrun\n");
+			} else
+			if(status&RxMiss) {
+				print("RxMiss\n");
+			} else {
+				print("IsqBufEvent status = %ux\n", status);
+			}
+			break;
+
+		case IsqRxEvent:
+			if(status&RxOK) {
+				len = regr(RxLen);
+				if((b = iallocb(len)) != 0) {
+					copyin(b->wp, len);
+					b->wp += len;
+					etheriq(ctlr, b, 1);
+				}
+			} else {
+				print("IsqRxEvent status = %ux\n", status);
+			}
+			break;
+
+		case IsqTxEvent:
+			if(status&TxOK) {
+				if(qlen(pendingTx[ctlr->ctlrno]) > 0)
+					q = pendingTx[ctlr->ctlrno];
+				else
+					q = ctlr->oq;
+				b = qget(q);
+				if(b == 0)
+					break;
+				len = BLEN(b);
+				regw(TxCmd, TxSt381);
+				regw(TxLen, len);
+if((regr(BusSt) & Rdy4TxNOW) == 0) {
+	print("IsqTxEvent and Rdy4TxNow == 0\n");
+}
+				copyout(b->rp, len);
+				freeb(b);
+			} else {
+				print("IsqTxEvent status = %ux\n", status);
+			}
+			break;
+		case IsqRxMiss:
+			break;
+		case IsqTxCol:
+			break;
+		}
+	}
+}
+
+int
+cs8900reset(Ctlr* ctlr)
+{
+	int i, reg;
+	uchar ea[Eaddrlen];
+
+	ctlr->card.irq = V_ETHERNET;
+	pendingTx[ctlr->ctlrno] = qopen(16*1024, 1, 0, 0);
+
+	/*
+	 * If the Ethernet address is not set in the plan9.ini file
+	 * a) try reading from the Puma board ROM. The ether address is found in
+	 * 	bytes 4-9 of the ROM. The Teralogic Organizational Unique Id (OUI) 
+	 *	is in bytes 4-6 and should be 00 10 8a.
+	 */
+	memset(ea, 0, Eaddrlen);
+	if(memcmp(ea, ctlr->card.ea, Eaddrlen) == 0) {
+		uchar *rom = (uchar *)EPROM_BASE;
+		if(rom[4] != 0x00 || rom[5] != 0x10 || rom[6] != 0x8a)
+			panic("no ether address");
+		memmove(ea, &rom[4], Eaddrlen);
+	}
+	memmove(ctlr->card.ea, ea, Eaddrlen);
+
+	/* 
+	 * Identify the chip by reading the Pic register.
+	 * The EISA registration number is in the low word
+	 * and the product identification code in the high code.
+	 * The ERN for Crystal Semiconductor is 0x630e.
+	 * Bits 0-7 and 13-15 of the Pic should be zero for a CS8900.
+	 */
+	if(regIOr(Ern) != 0x630e || (regIOr(Pic) & 0xe0ff) != 0)
+		panic("no cs8900 found");
+
+	/*
+	 * Reset the chip and ensure 16-bit mode operation
+	 */
+	regIOw(SelfCtl, RESET);
+	delay(10);
+	i=in8(PpPtr); 	USED(i);
+	i=in8(PpPtr+1); USED(i);
+	i=in8(PpPtr); 	USED(i);
+	i=in8(PpPtr+1);	USED(i);
+
+	/*
+	 * Wait for initialisation and EEPROM reads to complete
+	 */
+	i=0;
+	for(;;) {
+		short st = regIOr(SelfSt);
+		if((st&SIBUSY) == 0 && st&INITD)
+			break;
+		if(i++ > 1000000)
+			panic("cs8900: initialisation failed");
+	}
+
+	/*
+	 * Enable memory mode operation.
+	 */
+	regIOw(Mba, MemBase & 0xffff);
+	regIOw(Mba+2, MemBase >> 16);
+	regIOw(BusCtl, MemoryE|UseSA);
+
+	/*
+	 * Enable 10BASE-T half duplex, transmit in interrupt mode
+	 */
+	reg = regr(LineCtl);
+	regw(LineCtl, reg&~Iface);
+	reg = regr(TestCtl);
+	regw(TestCtl, reg&~FDX);
+	regw(BufCfg, Rdy4TxiE|TxUnderruniE|RxMissiE);
+	regw(TxCfg, TxOKiE|JabberiE|Coll16iE);
+	regw(RxCfg, RxOKiE);
+	regw(RxCtl, RxOKA|IndividualA|BroadcastA);
+
+	for(i=0; i<Eaddrlen; i+=2)
+		regw(IndAddr+i, ea[i] | (ea[i+1] << 8));
+
+	/* Puma IRQ tied to INTRQ0 */
+	regw(Intr, 0);
+
+	ctlr->card.reset = cs8900reset;
+	ctlr->card.port = 0x300;
+	ctlr->card.attach = attach;
+	ctlr->card.transmit = transmit;
+	ctlr->card.intr = interrupt;
+
+	print("Ether reset...\n");uartwait();
+
+	return 0;
+}
+
--- /dev/null
+++ b/os/boot/puma/flash.c
@@ -1,0 +1,226 @@
+#include "boot.h"
+
+typedef struct Flashdev Flashdev;
+struct Flashdev {
+	uchar*	base;
+	int	size;
+	uchar*	exec;
+	char*	type;
+	char*	config;
+	int	conflen;
+};
+
+enum {
+	FLASHSEG = 256*1024,
+	CONFIGLIM = FLASHSEG,
+	BOOTOFF = FLASHSEG,
+	BOOTLEN = 3*FLASHSEG,	/* third segment might be filsys */
+	/* rest of flash is free */
+};
+
+static Flashdev flash;
+
+/*
+ * configuration data is written between the bootstrap and
+ * the end of region 0. the region ends with allocation descriptors
+ * of the following form:
+ *
+ * byte order is big endian
+ *
+ * the last valid region found that starts with the string "#plan9.ini\n" is plan9.ini
+ */
+typedef struct Flalloc Flalloc;
+struct Flalloc {
+	ulong	check;	/* checksum of data, or ~0 */
+	ulong	base;	/* base of region; ~0 if unallocated, 0 if deleted */
+	uchar	len[3];
+	uchar	tag;		/* see below */
+	uchar	sig[4];
+};
+
+enum {
+	/* tags */
+	Tdead=	0,
+	Tboot=	0x01,	/* space reserved for boot */
+	Tconf=	0x02,	/* configuration data */
+	Tnone=	0xFF,
+
+	Noval=	~0,
+};
+
+static char flashsig[] = {0xF1, 0xA5, 0x5A, 0x1F};
+static char conftag[] = "#plan9.ini\n";
+
+static ulong
+checksum(uchar* p, int n)
+{
+	ulong s;
+
+	for(s=0; --n >= 0;)
+		s += *p++;
+	return s;
+}
+
+static int
+validptr(Flalloc *ap, uchar *p)
+{
+	return p > (uchar*)end && p < (uchar*)ap;
+}
+
+static int
+flashcheck(Flalloc *ap, char **val, int *len)
+{
+	uchar *base;
+	int n;
+
+	if(ap->base == Noval || ap->base >= FLASHSEG || ap->tag == Tnone)
+		return 0;
+	base = flash.base+ap->base;
+	if(!validptr(ap, base))
+		return 0;
+	n = (((ap->len[0]<<8)|ap->len[1])<<8)|ap->len[2];
+	if(n == 0xFFFFFF)
+		n = 0;
+	if(n < 0)
+		return 0;
+	if(n > 0 && !validptr(ap, base+n-1))
+		return 0;
+	if(ap->check != Noval && checksum(base, n) != ap->check){
+		print("flash: bad checksum\n");
+		return 0;
+	}
+	*val = (char*)base;
+	*len = n;
+	return 1;
+}
+
+int
+flashinit(void)
+{
+	int f, n, len;
+	char *type, *val;
+	Flalloc *ap;
+
+	flash.base = 0;
+	flash.exec = 0;
+	flash.type = 0;
+	/* TODO - check for presence and type */
+/*
+ *	if((m->iomem->memc[0].base & 1) == 0){
+ *		print("flash: flash not present or not enabled\n");
+ *		return 0;
+ *	}
+ *	f = (m->bcsr[2]>>28)&0xF;
+ */
+f = 0;
+	switch(f){
+	default:
+			print("flash boot: unknown or no flash\n");
+			return 0;
+	case 4:	n=8; type = "SM732x8"; break;
+	case 5:	n=4; type = "SM732x8"; break;
+	case 6:	n=8; type = "AMD29F0x0"; break;
+	case 7:	n=4; type = "AMD29F0x0"; break;
+	case 8:	n=2; type = "AMD29F0x0"; break;
+	}
+	flash.type = type;
+	flash.size = n*1024*1024;
+	flash.base = KADDR(FLASH_BASE);
+	flash.exec = flash.base + BOOTOFF;
+	flash.config = nil;
+	flash.conflen = 0;
+
+	for(ap = (Flalloc*)(flash.base+CONFIGLIM)-1; memcmp(ap->sig, flashsig, 4) == 0; ap--){
+		if(1)
+			print("conf #%8.8lux: #%x #%6.6lux\n", ap, ap->tag, ap->base);
+		if(ap->tag == Tconf &&
+		   flashcheck(ap, &val, &len) &&
+		   len >= sizeof(conftag)-1 &&
+		   memcmp(val, conftag, sizeof(conftag)-1) == 0){
+			flash.config = val;
+			flash.conflen = len;
+			print("flash: found config %8.8lux(%d):\n%s\n", val, len, val);
+		}
+	}
+	if(flash.config){
+		print("flash config %8.8lux(%d):\n%s\n", flash.config, flash.conflen, flash.config);
+		flash.config = nil;	/* not that daring yet */
+	}else
+		print("flash: no config\n");
+	if(issqueezed(flash.exec) == E_MAGIC){
+		print("flash: squeezed StrongARM kernel installed\n");
+		return 1<<0;
+	}
+	if(GLLONG(flash.exec) == E_MAGIC){
+		print("flash: unsqueezed stringARM kernel installed\n");
+		return 1<<0;
+	}
+	flash.exec = 0;
+	print("flash: no StrongARM kernel in Flash\n");
+	return 0;
+}
+
+char*
+flashconfig(int)
+{
+	return flash.config;
+}
+
+int
+flashbootable(int)
+{
+	return flash.exec != nil && (issqueezed(flash.exec) || GLLONG(flash.exec) == E_MAGIC);
+}
+
+int
+flashboot(int)
+{
+	ulong entry, addr;
+	void (*b)(void);
+	Exec *ep;
+	Block in;
+	long n;
+	uchar *p;
+
+	if(flash.exec == 0)
+		return -1;
+	p = flash.exec;
+	if(GLLONG(p) == E_MAGIC){
+		/* unsqueezed: copy data and perhaps text, then jump to it */
+		ep = (Exec*)p;
+		entry = PADDR(GLLONG(ep->entry));
+		p += sizeof(Exec);
+		addr = entry;
+		n = GLLONG(ep->text);
+		if(addr != (ulong)p){
+			memmove((void*)addr, p, n);
+			print("text: %8.8lux <- %8.8lux [%ld]\n", addr, p, n);
+		}
+		p += n;
+		if(entry >= FLASH_BASE)
+			addr = 3*BY2PG;	/* kernel text is in Flash, data in RAM */
+		else
+			addr = PGROUND(addr+n);
+		n = GLLONG(ep->data);
+		memmove((void*)addr, p, n);
+		print("data: %8.8lux <- %8.8lux [%ld]\n", addr, p, n);
+	}else{
+		in.data = p;
+		in.rp = in.data;
+		in.lim = p+BOOTLEN;
+		in.wp = in.lim;
+		n = unsqueezef(&in, &entry);
+		if(n < 0)
+			return -1;
+	}
+	print("entry=0x%lux\n", entry);
+	uartwait();
+	/* scc2stop(); */
+	/*
+	 *  Go to new code. It's up to the program to get its PC relocated to
+	 *  the right place.
+	 */
+	b = (void (*)(void))KADDR(PADDR(entry));
+	(*b)();
+	return -1;
+}
--- /dev/null
+++ b/os/boot/puma/fns.h
@@ -1,0 +1,111 @@
+void	aamloop(int);
+Alarm*	alarm(int, void (*)(Alarm*), void*);
+void	alarminit(void);
+int	bootp(int, char*);
+void	cancel(Alarm*);
+void	checkalarms(void);
+void	clockinit(void);
+void	consinit(void);
+void	delay(int);
+uchar*	etheraddr(int);
+int	etherinit(void);
+int	etherrxpkt(int, Etherpkt*, int);
+int	ethertxpkt(int, Etherpkt*, int, int);
+int	flashboot(int);
+int	flashbootable(int);
+char*	flashconfig(int);
+int	flashinit(void);
+char*	getconf(char*);
+int	getcfields(char*, char**, int, char*);
+int	getstr(char*, char*, int, char*);
+int	hardinit(void);
+long	hardread(int, void*, long);
+long	hardseek(int, long);
+long	hardwrite(int, void*, long);
+void*	ialloc(ulong, int);
+void	idle(void);
+int	isaconfig(char*, int, ISAConf*);
+int	isgzipped(uchar*);
+int	issqueezed(uchar*);
+void	kbdinit(void);
+void	kbdchar(Queue*, int);
+void	machinit(void);
+void	meminit(void);
+void	microdelay(int);
+void	mmuinit(void);
+uchar	nvramread(int);
+void	outb(int, int);
+void	outs(int, ushort);
+void	outl(int, ulong);
+void	outsb(int, void*, int);
+void	outss(int, void*, int);
+void	outsl(int, void*, int);
+void	panic(char*, ...);
+int	optionsw(void);
+int	plan9boot(int, long (*)(int, long), long (*)(int, void*, long));
+Partition*	setflashpart(int, char*);
+Partition* sethardpart(int, char*);
+Partition* setscsipart(int, char*);
+void	setvec(int, void (*)(Ureg*, void*), void*);
+void	screeninit(void);
+void	screenputs(char*, int);
+void setr13(int, void*);
+int	splhi(void);
+int	spllo(void);
+void	splx(int);
+void	trapinit(void);
+void	uartspecial(int, int, Queue**, Queue**, void(*)(Queue*,int));
+void	uartputs(char*, int);
+void	uartwait(void);
+long	unsqueezef(Block*, ulong*);
+
+#define	GSHORT(p)	(((p)[1]<<8)|(p)[0])
+#define	GLONG(p)	((GSHORT(p+2)<<16)|GSHORT(p))
+#define	GLSHORT(p)	(((p)[0]<<8)|(p)[1])
+#define	GLLONG(p)	((GLSHORT(p)<<16)|GLSHORT(p+2))
+
+#define KADDR(a)	((void*)((ulong)(a)|KZERO))
+#define PADDR(a)	((ulong)(a)&~KZERO)
+
+
+void	mapinit(RMap*, Map*, int);
+void	mapfree(RMap*, ulong, int);
+ulong	mapalloc(RMap*, ulong, int, int);
+
+/* IBM bit field order */
+#define	IBFEXT(v,a,b) (((ulong)(v)>>(32-(b)-1)) & ~(~0L<<(((b)-(a)+1))))
+#define	IBIT(b)	((ulong)1<<(31-(b)))
+
+#define	SIBIT(n)	((ushort)1<<(15-(n)))
+
+void*	malloc(ulong);
+void	free(void*);
+
+extern Block*	iallocb(int);
+extern void	freeb(Block*);
+extern Queue*	qopen(int, int, void (*)(void*), void*);
+extern Block*	qget(Queue*);
+extern void	qbwrite(Queue*, Block*);
+extern long	qlen(Queue*);
+#define	qpass	qbwrite
+extern void	qbputc(Queue*, int);
+extern int	qbgetc(Queue*);
+
+int	sio_inb(int);
+void	sio_outb(int, int);
+void	led(int);
+
+extern void _virqcall(void);
+extern void _vfiqcall(void);
+extern void _vundcall(void);
+extern void _vsvccall(void);
+extern void _vpabcall(void);
+extern void _vdabcall(void);
+
+void flushIcache(void);
+void writeBackDC(void);
+void flushDcache(void);
+void flushIcache(void);
+void drainWBuffer(void);
+
+void pumainit(void);
--- /dev/null
+++ b/os/boot/puma/hard.c
@@ -1,0 +1,773 @@
+#include	"u.h"
+#include	"lib.h"
+#include	"mem.h"
+#include	"dat.h"
+#include	"fns.h"
+#include	"io.h"
+
+#define DPRINT if(0)print
+
+typedef	struct Drive		Drive;
+typedef	struct Ident		Ident;
+typedef	struct Controller	Controller;
+
+enum
+{
+	/* ports */
+	Pbase0=		0x1F0,	/* primary */
+	Pbase1=		0x170,	/* secondary */
+	Pbase2=		0x1E8,	/* tertiary */
+	Pbase3=		0x168,	/* quaternary */
+	Pdata=		0,	/* data port (16 bits) */
+	Perror=		1,	/* error port (read) */
+	Pprecomp=	1,	/* buffer mode port (write) */
+	Pcount=		2,	/* sector count port */
+	Psector=	3,	/* sector number port */
+	Pcyllsb=	4,	/* least significant byte cylinder # */
+	Pcylmsb=	5,	/* most significant byte cylinder # */
+	Pdh=		6,	/* drive/head port */
+	 DHmagic=	0xA0,
+	 DHslave=	0x10,
+	Pstatus=	7,	/* status port (read) */
+	 Sbusy=		 (1<<7),
+	 Sready=	 (1<<6),
+	 Sdrq=		 (1<<3),
+	 Serr=		 (1<<0),
+	Pcmd=		7,	/* cmd port (write) */
+
+	/* commands */
+	Crecal=		0x10,
+	Cread=		0x20,
+	Cwrite=		0x30,
+	Cident=		0xEC,
+	Cident2=	0xFF,	/* pseudo command for post Cident interrupt */
+	Csetbuf=	0xEF,
+
+	/* file types */
+	Qdir=		0,
+
+	Timeout=	5,		/* seconds to wait for things to complete */
+
+	NCtlr=		4,
+	NDrive=		NCtlr*2,
+};
+
+/*
+ *  ident sector from drive.  this is from ANSI X3.221-1994
+ */
+struct Ident
+{
+	ushort	config;		/* general configuration info */
+	ushort	cyls;		/* # of cylinders (default) */
+	ushort	reserved0;
+	ushort	heads;		/* # of heads (default) */
+	ushort	b2t;		/* unformatted bytes/track */
+	ushort	b2s;		/* unformated bytes/sector */
+	ushort	s2t;		/* sectors/track (default) */
+	ushort	reserved1[3];
+/* 10 */
+	ushort	serial[10];	/* serial number */
+	ushort	type;		/* buffer type */
+	ushort	bsize;		/* buffer size/512 */
+	ushort	ecc;		/* ecc bytes returned by read long */
+	ushort	firm[4];	/* firmware revision */
+	ushort	model[20];	/* model number */
+/* 47 */
+	ushort	s2i;		/* number of sectors/interrupt */
+	ushort	dwtf;		/* double word transfer flag */
+	ushort	capabilities;
+	ushort	reserved2;
+	ushort	piomode;
+	ushort	dmamode;
+	ushort	cvalid;		/* (cvald&1) if next 4 words are valid */
+	ushort	ccyls;		/* current # cylinders */
+	ushort	cheads;		/* current # heads */
+	ushort	cs2t;		/* current sectors/track */
+	ushort	ccap[2];	/* current capacity in sectors */
+	ushort	cs2i;		/* current number of sectors/interrupt */
+/* 60 */
+	ushort	lbasecs[2];	/* # LBA user addressable sectors */
+	ushort	dmasingle;
+	ushort	dmadouble;
+/* 64 */
+	ushort	reserved3[64];
+	ushort	vendor[32];	/* vendor specific */
+	ushort	reserved4[96];
+};
+
+/*
+ *  a hard drive
+ */
+struct Drive
+{
+	Controller *cp;
+	uchar	driveno;
+	uchar	dh;
+
+	Disc;
+};
+
+/*
+ *  a controller for 2 drives
+ */
+struct Controller
+{
+	int	pbase;		/* base port */
+	uchar	ctlrno;
+
+	/*
+	 *  current operation
+	 */
+	int	cmd;		/* current command */
+	char	*buf;		/* xfer buffer */
+	int	tcyl;		/* target cylinder */
+	int	thead;		/* target head */
+	int	tsec;		/* target sector */
+	int	tbyte;		/* target byte */
+	int	nsecs;		/* length of transfer (sectors) */
+	int	sofar;		/* bytes transferred so far */
+	int	status;
+	int	error;
+	Drive	*dp;		/* drive being accessed */
+};
+
+static int atactlrmask;
+static Controller *atactlr[NCtlr];
+static int atadrivemask;
+static Drive *atadrive[NDrive];
+static int pbase[NCtlr] = {
+	Pbase0, Pbase1, Pbase2, Pbase3,
+};
+
+static void	hardintr(Ureg*, void*);
+static long	hardxfer(Drive*, Partition*, int, ulong, long);
+static int	hardident(Drive*);
+static void	hardsetbuf(Drive*, int);
+static void	hardpart(Drive*);
+static int	hardparams(Drive*);
+static void	hardrecal(Drive*);
+static int	hardprobe(Drive*, int, int, int);
+
+static void
+atactlrprobe(int ctlrno, int irq)
+{
+	Controller *ctlr;
+	Drive *drive;
+	int driveno, port;
+
+	if(atactlrmask & (1<<ctlrno))
+		return;
+	atactlrmask |= 1<<ctlrno;
+
+	port = pbase[ctlrno];
+	outb(port+Pdh, DHmagic);
+	delay(1);
+	if((inb(port+Pdh) & 0xFF) != DHmagic){
+		DPRINT("ata%d: DHmagic not ok\n", ctlrno);
+		return;
+	}
+	DPRINT("ata%d: DHmagic ok\n", ctlrno);
+
+	atactlr[ctlrno] = ialloc(sizeof(Controller), 0);
+	ctlr = atactlr[ctlrno];
+	ctlr->pbase = port;
+	ctlr->ctlrno = ctlrno;
+	ctlr->buf = ialloc(Maxxfer, 0);
+	inb(ctlr->pbase+Pstatus);
+	setvec(irq, hardintr, ctlr);
+
+	driveno = ctlrno*2;
+	atadrive[driveno] = ialloc(sizeof(Drive), 0);
+	drive = atadrive[driveno];
+	drive->cp = ctlr;
+	drive->driveno = driveno;
+	drive->dh = DHmagic;
+
+	driveno++;
+	atadrive[driveno] = ialloc(sizeof(Drive), 0);
+	drive = atadrive[driveno];
+	drive->cp = ctlr;
+	drive->driveno = driveno;
+	drive->dh = DHmagic|DHslave;
+}
+
+static Drive*
+atadriveprobe(int driveno)
+{
+	Drive *drive;
+	int ctlrno;
+	ISAConf isa;
+
+	ctlrno = driveno/2;
+	if(atactlr[ctlrno] == 0){
+		if(atactlrmask & (1<<ctlrno))
+			return 0;
+		memset(&isa, 0, sizeof(ISAConf));
+		if(isaconfig("ata", ctlrno, &isa) == 0)
+			return 0;
+		if(ctlrno && isa.irq)
+			atactlrprobe(ctlrno, Int0vec+isa.irq);
+		if(atactlr[ctlrno] == 0)
+			return 0;
+	}
+
+	drive = atadrive[driveno];
+	if(drive->online == 0){
+		if(atadrivemask & (1<<driveno))
+			return 0;
+		atadrivemask |= 1<<driveno;
+		if(hardparams(drive))
+			return 0;
+		if(drive->lba)
+			print("hd%d: LBA %d sectors, %ud bytes\n",
+				drive->driveno, drive->sectors, drive->cap);
+		else
+			print("hd%d: CHS %d/%d/%d %d bytes\n",
+				drive->driveno, drive->cyl, drive->heads,
+				drive->sectors, drive->cap);
+		drive->online = 1;
+		hardpart(drive);
+		hardsetbuf(drive, 1);
+	}
+
+	return drive;
+}
+
+int
+hardinit(void)
+{
+	atactlrprobe(0, ATAvec0);
+	return 0xFF;
+}
+
+long
+hardseek(int driveno, long offset)
+{
+	Drive *drive;
+
+	if((drive = atadriveprobe(driveno)) == 0)
+		return -1;
+	drive->offset = offset;
+	return offset;
+}
+
+/*
+ *  did an interrupt happen?
+ */
+static void
+hardwait(Controller *cp)
+{
+	ulong start;
+	int x;
+
+	x = spllo();
+	for(start = m->ticks; TK2SEC(m->ticks - start) < Timeout && cp->cmd;)
+		if(cp->cmd == Cident2 && TK2SEC(m->ticks - start) >= 1)
+			break;
+	if(TK2SEC(m->ticks - start) >= Timeout){
+		DPRINT("hardwait timed out %ux\n", inb(cp->pbase+Pstatus));
+		hardintr(0, cp);
+	}
+	splx(x);
+}
+
+Partition*
+sethardpart(int driveno, char *p)
+{
+	Partition *pp;
+	Drive *dp;
+
+	if((dp = atadriveprobe(driveno)) == 0)
+		return 0;
+
+	for(pp = dp->p; pp < &dp->p[dp->npart]; pp++)
+		if(strcmp(pp->name, p) == 0){
+			dp->current = pp;
+			return pp;
+		}
+	return 0;
+}
+
+long
+hardread(int driveno, void *a, long n)
+{
+	Drive *dp;
+	long rv, i;
+	int skip;
+	uchar *aa = a;
+	Partition *pp;
+	Controller *cp;
+
+	if((dp = atadriveprobe(driveno)) == 0)
+		return 0;
+
+	pp = dp->current;
+	if(pp == 0)
+		return -1;
+	cp = dp->cp;
+
+	skip = dp->offset % dp->bytes;
+	for(rv = 0; rv < n; rv += i){
+		i = hardxfer(dp, pp, Cread, dp->offset+rv-skip, n-rv+skip);
+		if(i == 0)
+			break;
+		if(i < 0)
+			return -1;
+		i -= skip;
+		if(i > n - rv)
+			i = n - rv;
+		memmove(aa+rv, cp->buf + skip, i);
+		skip = 0;
+	}
+	dp->offset += rv;
+
+	return rv;
+}
+
+/*
+ *  wait for the controller to be ready to accept a command
+ */
+static int
+cmdreadywait(Drive *drive)
+{
+	ulong end;
+	uchar dh, status;
+	Controller *ctlr;
+
+	ctlr = drive->cp;
+	end = m->ticks+MS2TK(10)+1;
+	dh = (inb(ctlr->pbase+Pdh) & DHslave)^(drive->dh & DHslave);
+	
+	status = 0;
+	while(m->ticks < end){
+		status = inb(ctlr->pbase+Pstatus);
+		if(status & Sbusy)
+			continue;
+		if(dh){
+			outb(ctlr->pbase+Pdh, drive->dh);
+			dh = 0;
+			continue;
+		}
+		if(status & Sready)
+			return 0;
+	}
+	USED(status);
+
+	DPRINT("hd%d: cmdreadywait failed %uX\n", drive->driveno, status);
+	outb(ctlr->pbase+Pdh, DHmagic);
+	return -1;
+}
+
+/*
+ *  transfer a number of sectors.  hardintr will perform all the iterative
+ *  parts.
+ */
+static long
+hardxfer(Drive *dp, Partition *pp, int cmd, ulong start, long len)
+{
+	Controller *cp;
+	long lsec;
+
+	if(dp->online == 0){
+		DPRINT("disk not on line\n");
+		return -1;
+	}
+
+	if(cmd == Cwrite)
+		return -1;
+
+	/*
+	 *  cut transfer size down to disk buffer size
+	 */
+	start = start / dp->bytes;
+	if(len > Maxxfer)
+		len = Maxxfer;
+	len = (len + dp->bytes - 1) / dp->bytes;
+
+	/*
+	 *  calculate physical address
+	 */
+	cp = dp->cp;
+	lsec = start + pp->start;
+	if(lsec >= pp->end){
+		DPRINT("read past end of partition\n");
+		return 0;
+	}
+	if(dp->lba){
+		cp->tsec = lsec & 0xff;
+		cp->tcyl = (lsec>>8) & 0xffff;
+		cp->thead = (lsec>>24) & 0xf;
+	} else {
+		cp->tcyl = lsec/(dp->sectors*dp->heads);
+		cp->tsec = (lsec % dp->sectors) + 1;
+		cp->thead = (lsec/dp->sectors) % dp->heads;
+	}
+
+	/*
+	 *  can't xfer past end of disk
+	 */
+	if(lsec+len > pp->end)
+		len = pp->end - lsec;
+	cp->nsecs = len;
+
+	if(cmdreadywait(dp) < 0)
+		return -1;
+
+	/*
+	 *  start the transfer
+	 */
+	cp->cmd = cmd;
+	cp->dp = dp;
+	cp->sofar = 0;
+	cp->status = 0;
+	DPRINT("xfer:\ttcyl %d, tsec %d, thead %d\n", cp->tcyl, cp->tsec, cp->thead);
+	DPRINT("\tnsecs %d, sofar %d\n", cp->nsecs, cp->sofar);
+	outb(cp->pbase+Pcount, cp->nsecs);
+	outb(cp->pbase+Psector, cp->tsec);
+	outb(cp->pbase+Pdh, dp->dh | (dp->lba<<6) | cp->thead);
+	outb(cp->pbase+Pcyllsb, cp->tcyl);
+	outb(cp->pbase+Pcylmsb, cp->tcyl>>8);
+	outb(cp->pbase+Pcmd, cmd);
+
+	hardwait(cp);
+
+	if(cp->status & Serr){
+		DPRINT("hd%d err: status %lux, err %lux\n",
+			dp->driveno, cp->status, cp->error);
+		DPRINT("\ttcyl %d, tsec %d, thead %d\n",
+			cp->tcyl, cp->tsec, cp->thead);
+		DPRINT("\tnsecs %d, sofar %d\n", cp->nsecs, cp->sofar);
+		return -1;
+	}
+
+	return cp->nsecs*dp->bytes;
+}
+
+/*
+ *  set read ahead mode (1 == on, 0 == off)
+ */
+static void
+hardsetbuf(Drive *dp, int on)
+{
+	Controller *cp = dp->cp;
+
+	if(cmdreadywait(dp) < 0)
+		return;
+
+	cp->cmd = Csetbuf;
+	/* BUG: precomp varies by hard drive...this is safari-specific? */
+	outb(cp->pbase+Pprecomp, on ? 0xAA : 0x55);
+	outb(cp->pbase+Pdh, dp->dh);
+	outb(cp->pbase+Pcmd, Csetbuf);
+
+	hardwait(cp);
+}
+
+static int
+isatapi(Drive *drive)
+{
+	Controller *cp;
+
+	cp = drive->cp;
+	outb(cp->pbase+Pdh, drive->dh);
+	microdelay(1);
+	if(inb(cp->pbase+Pstatus))
+		return 0;
+	if(inb(cp->pbase+Pcylmsb) != 0xEB || inb(cp->pbase+Pcyllsb) != 0x14)
+		return 0;
+	return 1;
+}
+
+/*
+ *  get parameters from the drive
+ */
+static int
+hardident(Drive *dp)
+{
+	Controller *cp;
+	Ident *ip;
+
+	dp->bytes = 512;
+	cp = dp->cp;
+
+	if(isatapi(dp) || cmdreadywait(dp) < 0)
+		return -1;
+
+	cp->nsecs = 1;
+	cp->sofar = 0;
+	cp->cmd = Cident;
+	cp->dp = dp;
+	outb(cp->pbase+Pdh, dp->dh);
+	outb(cp->pbase+Pcmd, Cident);
+
+	hardwait(cp);
+
+	if(cp->status & Serr)
+		return -1;
+	
+	hardwait(cp);
+
+	ip = (Ident*)cp->buf;
+	DPRINT("LBA%d: %lud\n",
+		ip->capabilities & (1<<9) == 1, (ip->lbasecs[0]) | (ip->lbasecs[1]<<16));
+	DPRINT("DEF: %ud/%ud/%ud\nMAP %ud/%ud/%ud\n",
+		ip->cyls, ip->heads, ip->s2t,
+		ip->ccyls, ip->cheads, ip->cs2t);
+	if(ip->capabilities & (1<<9)){
+		dp->lba = 1;
+		dp->sectors = (ip->lbasecs[0]) | (ip->lbasecs[1]<<16);
+		dp->cap = dp->bytes * dp->sectors;
+/*print("\nata%d model %s with %d lba sectors\n", dp->driveno, id, dp->sectors);/**/
+	} else {
+		dp->lba = 0;
+
+		/* use default (unformatted) settings */
+		dp->cyl = ip->cyls;
+		dp->heads = ip->heads;
+		dp->sectors = ip->s2t;
+/*print("\nata%d model %s with default %d cyl %d head %d sec\n", dp->driveno,
+			id, dp->cyl, dp->heads, dp->sectors);/**/
+
+		if(ip->cvalid&(1<<0)){
+			/* use current settings */
+			dp->cyl = ip->ccyls;
+			dp->heads = ip->cheads;
+			dp->sectors = ip->cs2t;
+/*print("\tchanged to %d cyl %d head %d sec\n", dp->cyl, dp->heads, dp->sectors);/**/
+		}
+		dp->cap = dp->bytes * dp->cyl * dp->heads * dp->sectors;
+	}
+
+	return 0;
+}
+
+/*
+ *  probe the given sector to see if it exists
+ */
+static int
+hardprobe(Drive *dp, int cyl, int sec, int head)
+{
+	Controller *cp;
+
+	cp = dp->cp;
+	if(cmdreadywait(dp) < 0)
+		return -1;
+
+	/*
+	 *  start the transfer
+	 */
+	cp->cmd = Cread;
+	cp->dp = dp;
+	cp->sofar = 0;
+	cp->nsecs = 1;
+	cp->status = 0;
+	outb(cp->pbase+Pcount, 1);
+	outb(cp->pbase+Psector, sec+1);
+	outb(cp->pbase+Pdh, dp->dh | (dp->lba<<6) | head);
+	outb(cp->pbase+Pcyllsb, cyl);
+	outb(cp->pbase+Pcylmsb, cyl>>8);
+	outb(cp->pbase+Pcmd, Cread);
+
+	hardwait(cp);
+
+	if(cp->status & Serr)
+		return -1;
+
+	return 0;
+}
+
+/*
+ *  figure out the drive parameters
+ */
+static int
+hardparams(Drive *dp)
+{
+	int i, hi, lo;
+
+	/*
+	 *  first try the easy way, ask the drive and make sure it
+	 *  isn't lying.
+	 */
+	dp->bytes = 512;
+	if(hardident(dp) < 0)
+		return -1;
+	if(dp->lba){
+		i = dp->sectors - 1;
+		if(hardprobe(dp, (i>>8)&0xffff, (i&0xff)-1, (i>>24)&0xf) == 0)
+			return 0;
+	} else {
+		if(hardprobe(dp, dp->cyl-1, dp->sectors-1, dp->heads-1) == 0)
+			return 0;
+	}
+
+	DPRINT("hardparam: cyl %d sectors %d heads %d\n", dp->cyl, dp->sectors, dp->heads);
+	/*
+	 *  the drive lied, determine parameters by seeing which ones
+	 *  work to read sectors.
+	 */
+	dp->lba = 0;
+	for(i = 0; i < 16; i++)
+		if(hardprobe(dp, 0, 0, i) < 0)
+			break;
+	dp->heads = i;
+	for(i = 0; i < 64; i++)
+		if(hardprobe(dp, 0, i, 0) < 0)
+			break;
+	dp->sectors = i;
+	for(i = 512; ; i += 512)
+		if(hardprobe(dp, i, dp->sectors-1, dp->heads-1) < 0)
+			break;
+	lo = i - 512;
+	hi = i;
+	for(; hi-lo > 1;){
+		i = lo + (hi - lo)/2;
+		if(hardprobe(dp, i, dp->sectors-1, dp->heads-1) < 0)
+			hi = i;
+		else
+			lo = i;
+	}
+	dp->cyl = lo + 1;
+	dp->cap = dp->bytes * dp->cyl * dp->heads * dp->sectors;
+
+	if(dp->cyl == 0 || dp->heads == 0 || dp->sectors == 0 || dp->cap == 0)
+		return -1;
+
+	return 0;
+}
+
+/*
+ *  read partition table.  The partition table is just ascii strings.
+ */
+#define MAGIC "plan9 partitions"
+static void
+hardpart(Drive *dp)
+{
+	Partition *pp;
+	Controller *cp;
+	char *field[3], *line[Npart+1], *p, buf[NAMELEN];
+	ulong n;
+	int i;
+
+	cp = dp->cp;
+
+	/*
+	 *  we always have a partition for the whole disk
+	 *  and one for the partition table
+	 */
+	pp = &dp->p[0];
+	strcpy(pp->name, "disk");
+	pp->start = 0;
+	pp->end = dp->cap / dp->bytes;
+	pp++;
+	strcpy(pp->name, "partition");
+	pp->start = dp->p[0].end - 1;
+	pp->end = dp->p[0].end;
+	dp->npart = 2;
+
+	/*
+	 * Check if the partitions are described in plan9.ini.
+	 * If not, read the disc.
+	 */
+	sprint(buf, "hd%dpartition", dp->driveno);
+	if((p = getconf(buf)) == 0){	
+		/*
+		 *  read last sector from disk, null terminate.  This used
+		 *  to be the sector we used for the partition tables.
+		 *  However, this sector is special on some PC's so we've
+		 *  started to use the second last sector as the partition
+		 *  table instead.  To avoid reconfiguring all our old systems
+		 *  we first look to see if there is a valid partition
+		 *  table in the last sector.  If so, we use it.  Otherwise
+		 *  we switch to the second last.
+		 */
+		hardxfer(dp, pp, Cread, 0, dp->bytes);
+		cp->buf[dp->bytes-1] = 0;
+		n = getcfields(cp->buf, line, Npart+1, "\n");
+		if(n == 0 || strncmp(line[0], MAGIC, sizeof(MAGIC)-1)){
+			dp->p[0].end--;
+			dp->p[1].start--;
+			dp->p[1].end--;
+			hardxfer(dp, pp, Cread, 0, dp->bytes);
+			cp->buf[dp->bytes-1] = 0;
+			n = getcfields(cp->buf, line, Npart+1, "\n");
+		}
+	}
+	else{
+		strcpy(cp->buf, p);
+		n = getcfields(cp->buf, line, Npart+1, "\n");
+	}
+
+	/*
+	 *  parse partition table.
+	 */
+	if(n && strncmp(line[0], MAGIC, sizeof(MAGIC)-1) == 0){
+		for(i = 1; i < n; i++){
+			pp++;
+			if(getcfields(line[i], field, 3, " ") != 3)
+				break;
+			strncpy(pp->name, field[0], NAMELEN);
+			pp->start = strtoul(field[1], 0, 0);
+			pp->end = strtoul(field[2], 0, 0);
+			if(pp->start > pp->end || pp->start >= dp->p[0].end)
+				break;
+			dp->npart++;
+		}
+	}
+	return;
+}
+
+/*
+ *  we get an interrupt for every sector transferred
+ */
+static void
+hardintr(Ureg*, void *arg)
+{
+	Controller *cp;
+	Drive *dp;
+	long loop;
+
+	cp = arg;
+	dp = cp->dp;
+
+	loop = 0;
+	while((cp->status = inb(cp->pbase+Pstatus)) & Sbusy)
+		if(++loop > 100000){
+			print("hardintr 0x%lux\n", cp->status);
+			break;
+		}
+	switch(cp->cmd){
+	case Cread:
+	case Cident:
+		if(cp->status & Serr){
+			cp->cmd = 0;
+			cp->error = inb(cp->pbase+Perror);
+			return;
+		}
+		loop = 0;
+		while((inb(cp->pbase+Pstatus) & Sdrq) == 0)
+			if(++loop > 100000){
+				print("hardintr 2 cmd %ux status %ux",
+					cp->cmd, inb(cp->pbase+Pstatus));
+				cp->cmd = 0;
+				return;
+			}
+		inss(cp->pbase+Pdata, &cp->buf[cp->sofar*dp->bytes],
+			dp->bytes/2);
+		cp->sofar++;
+		if(cp->sofar >= cp->nsecs){
+			if(cp->cmd == Cident && (cp->status & Sready) == 0)
+				cp->cmd = Cident2; /* sometimes we get a second intr */
+			else
+				cp->cmd = 0;
+			inb(cp->pbase+Pstatus);
+		}
+		break;
+	case Csetbuf:
+	case Cident2:
+		cp->cmd = 0;
+		break;
+	default:
+		cp->cmd = 0;
+		break;
+	}
+}
--- /dev/null
+++ b/os/boot/puma/io.h
@@ -1,0 +1,3 @@
+#define 	inb(port)			sio_inb(port)
+#define 	outb(port, data)	sio_outb(port, data)
+
--- /dev/null
+++ b/os/boot/puma/ip.h
@@ -1,0 +1,98 @@
+typedef struct Udphdr Udphdr;
+struct Udphdr
+{
+	uchar	d[6];		/* Ethernet destination */
+	uchar	s[6];		/* Ethernet source */
+	uchar	type[2];	/* Ethernet packet type */
+
+	uchar	vihl;		/* Version and header length */
+	uchar	tos;		/* Type of service */
+	uchar	length[2];	/* packet length */
+	uchar	id[2];		/* Identification */
+	uchar	frag[2];	/* Fragment information */
+
+	/* Udp pseudo ip really starts here */
+	uchar	ttl;	
+	uchar	udpproto;	/* Protocol */
+	uchar	udpplen[2];	/* Header plus data length */
+	uchar	udpsrc[4];	/* Ip source */
+	uchar	udpdst[4];	/* Ip destination */
+	uchar	udpsport[2];	/* Source port */
+	uchar	udpdport[2];	/* Destination port */
+	uchar	udplen[2];	/* data length */
+	uchar	udpcksum[2];	/* Checksum */
+};
+
+typedef struct Etherhdr Etherhdr;
+struct Etherhdr
+{
+	uchar	d[6];
+	uchar	s[6];
+	uchar	type[2];
+
+	/* Now we have the ip fields */
+	uchar	vihl;		/* Version and header length */
+	uchar	tos;		/* Type of service */
+	uchar	length[2];	/* packet length */
+	uchar	id[2];		/* Identification */
+	uchar	frag[2];	/* Fragment information */
+	uchar	ttl;		/* Time to live */
+	uchar	proto;		/* Protocol */
+	uchar	cksum[2];	/* Header checksum */
+	uchar	src[4];		/* Ip source */
+	uchar	dst[4];		/* Ip destination */
+};
+
+enum
+{
+	IP_VER		= 0x40,
+	IP_HLEN		= 0x05,			
+ 	UDP_EHSIZE	= 22,
+	UDP_PHDRSIZE	= 12,
+	UDP_HDRSIZE	= 20,
+	ETHER_HDR	= 14,
+	IP_UDPPROTO	= 17,
+	ET_IP		= 0x800,
+	Bcastip		= 0xffffffff,
+	BPportsrc	= 68,
+	BPportdst	= 67,
+	TFTPport	= 69,
+	Timeout		= 5000,	/* milliseconds */
+	Bootrequest 	= 1,
+	Bootreply   	= 2,
+	Tftp_READ	= 1,
+	Tftp_WRITE	= 2,
+	Tftp_DATA	= 3,
+	Tftp_ACK	= 4,
+	Tftp_ERROR	= 5,
+	Segsize		= 512,
+	TFTPSZ		= Segsize+10,
+};
+
+typedef struct Bootp Bootp;
+struct Bootp
+{
+	uchar	op;		/* opcode */
+	uchar	htype;		/* hardware type */
+	uchar	hlen;		/* hardware address len */
+	uchar	hops;		/* hops */
+	uchar	xid[4];		/* a random number */
+	uchar	secs[2];	/* elapsed snce client started booting */
+	uchar	pad[2];
+	uchar	ciaddr[4];	/* client IP address (client tells server) */
+	uchar	yiaddr[4];	/* client IP address (server tells client) */
+	uchar	siaddr[4];	/* server IP address */
+	uchar	giaddr[4];	/* gateway IP address */
+	uchar	chaddr[16];	/* client hardware address */
+	char	sname[64];	/* server host name (optional) */
+	char	file[128];	/* boot file name */
+	char	vend[128];	/* vendor-specific goo */
+};
+
+typedef struct Netaddr Netaddr;
+struct Netaddr
+{
+	ulong	ip;
+	ushort	port;
+	char	ea[Eaddrlen];
+};
--- /dev/null
+++ b/os/boot/puma/kbd.c
@@ -1,0 +1,482 @@
+#include "boot.h"
+
+
+enum {
+	Data=		0x60,	/* data port */
+
+	Status=		0x64,	/* status port */
+	 Inready=	0x01,	/*  input character ready */
+	 Outbusy=	0x02,	/*  output busy */
+	 Sysflag=	0x04,	/*  system flag */
+	 Cmddata=	0x08,	/*  cmd==0, data==1 */
+	 Inhibit=	0x10,	/*  keyboard/mouse inhibited */
+	 Minready=	0x20,	/*  mouse character ready */
+	 Rtimeout=	0x40,	/*  general timeout */
+	 Parity=	0x80,
+
+	Cmd=		0x64,	/* command port (write only) */
+ 
+	Spec=	0x80,
+
+	PF=	Spec|0x20,	/* num pad function key */
+	View=	Spec|0x00,	/* view (shift window up) */
+	KF=	Spec|0x40,	/* function key */
+	Shift=	Spec|0x60,
+	Break=	Spec|0x61,
+	Ctrl=	Spec|0x62,
+	Latin=	Spec|0x63,
+	Caps=	Spec|0x64,
+	Num=	Spec|0x65,
+	No=	Spec|0x7F,	/* no mapping */
+
+	Home=	KF|13,
+	Up=	KF|14,
+	Pgup=	KF|15,
+	Print=	KF|16,
+	Left=	View,
+	Right=	View,
+	End=	'\r',
+	Down=	View,
+	Pgdown=	View,
+	Ins=	KF|20,
+	Del=	0x7F,
+};
+
+uchar kbtab[] = 
+{
+[0x00]	No,	0x1b,	'1',	'2',	'3',	'4',	'5',	'6',
+[0x08]	'7',	'8',	'9',	'0',	'-',	'=',	'\b',	'\t',
+[0x10]	'q',	'w',	'e',	'r',	't',	'y',	'u',	'i',
+[0x18]	'o',	'p',	'[',	']',	'\n',	Ctrl,	'a',	's',
+[0x20]	'd',	'f',	'g',	'h',	'j',	'k',	'l',	';',
+[0x28]	'\'',	'`',	Shift,	'\\',	'z',	'x',	'c',	'v',
+[0x30]	'b',	'n',	'm',	',',	'.',	'/',	Shift,	No,
+[0x38]	Latin,	' ',	Caps,	KF|1,	KF|2,	KF|3,	KF|4,	KF|5,
+[0x40]	KF|6,	KF|7,	KF|8,	KF|9,	KF|10,	Num,	KF|12,	Home,
+[0x48]	No,	No,	No,	No,	No,	No,	No,	No,
+[0x50]	No,	No,	No,	No,	No,	No,	No,	KF|11,
+[0x58]	KF|12,	No,	No,	No,	No,	No,	No,	No,
+};
+
+uchar kbtabshift[] =
+{
+[0x00]	No,	0x1b,	'!',	'@',	'#',	'$',	'%',	'^',
+[0x08]	'&',	'*',	'(',	')',	'_',	'+',	'\b',	'\t',
+[0x10]	'Q',	'W',	'E',	'R',	'T',	'Y',	'U',	'I',
+[0x18]	'O',	'P',	'{',	'}',	'\n',	Ctrl,	'A',	'S',
+[0x20]	'D',	'F',	'G',	'H',	'J',	'K',	'L',	':',
+[0x28]	'"',	'~',	Shift,	'|',	'Z',	'X',	'C',	'V',
+[0x30]	'B',	'N',	'M',	'<',	'>',	'?',	Shift,	No,
+[0x38]	Latin,	' ',	Caps,	KF|1,	KF|2,	KF|3,	KF|4,	KF|5,
+[0x40]	KF|6,	KF|7,	KF|8,	KF|9,	KF|10,	Num,	KF|12,	Home,
+[0x48]	No,	No,	No,	No,	No,	No,	No,	No,
+[0x50]	No,	No,	No,	No,	No,	No,	No,	KF|11,
+[0x58]	KF|12,	No,	No,	No,	No,	No,	No,	No,
+};
+
+uchar kbtabesc1[] =
+{
+[0x00]	No,	No,	No,	No,	No,	No,	No,	No,
+[0x08]	No,	No,	No,	No,	No,	No,	No,	No,
+[0x10]	No,	No,	No,	No,	No,	No,	No,	No,
+[0x18]	No,	No,	No,	No,	No,	Ctrl,	No,	No,
+[0x20]	No,	No,	No,	No,	No,	No,	No,	No,
+[0x28]	No,	No,	No,	No,	No,	No,	No,	No,
+[0x30]	No,	No,	No,	No,	No,	No,	No,	Print,
+[0x38]	Latin,	No,	No,	No,	No,	No,	No,	No,
+[0x40]	No,	No,	No,	No,	No,	No,	Break,	Home,
+[0x48]	Up,	Pgup,	No,	Down,	No,	Right,	No,	End,
+[0x50]	Left,	Pgdown,	Ins,	Del,	No,	No,	No,	No,
+[0x58]	No,	No,	No,	No,	No,	No,	No,	No,
+};
+
+struct latin
+{
+	uchar	l;
+	char	c[2];
+}latintab[] = {
+	L'¡',	"!!",	/* spanish initial ! */
+	L'¢',	"c|",	/* cent */
+	L'¢',	"c$",	/* cent */
+	L'£',	"l$",	/* pound sterling */
+	L'¤',	"g$",	/* general currency */
+	L'¥',	"y$",	/* yen */
+	L'¥',	"j$",	/* yen */
+	L'¦',	"||",	/* broken vertical bar */
+	L'§',	"SS",	/* section symbol */
+	L'¨',	"\"\"",	/* dieresis */
+	L'©',	"cr",	/* copyright */
+	L'©',	"cO",	/* copyright */
+	L'ª',	"sa",	/* super a, feminine ordinal */
+	L'«',	"<<",	/* left angle quotation */
+	L'¬',	"no",	/* not sign, hooked overbar */
+	L'­',	"--",	/* soft hyphen */
+	L'®',	"rg",	/* registered trademark */
+	L'¯',	"__",	/* macron */
+	L'°',	"s0",	/* degree (sup o) */
+	L'±',	"+-",	/* plus-minus */
+	L'²',	"s2",	/* sup 2 */
+	L'³',	"s3",	/* sup 3 */
+	L'´',	"''",	/* grave accent */
+	L'µ',	"mu",	/* mu */
+	L'¶',	"pg",	/* paragraph (pilcrow) */
+	L'·',	"..",	/* centered . */
+	L'¸',	",,",	/* cedilla */
+	L'¹',	"s1",	/* sup 1 */
+	L'º',	"so",	/* sup o */
+	L'»',	">>",	/* right angle quotation */
+	L'¼',	"14",	/* 1/4 */
+	L'½',	"12",	/* 1/2 */
+	L'¾',	"34",	/* 3/4 */
+	L'¿',	"??",	/* spanish initial ? */
+	L'À',	"A`",	/* A grave */
+	L'Á',	"A'",	/* A acute */
+	L'Â',	"A^",	/* A circumflex */
+	L'Ã',	"A~",	/* A tilde */
+	L'Ä',	"A\"",	/* A dieresis */
+	L'Ä',	"A:",	/* A dieresis */
+	L'Å',	"Ao",	/* A circle */
+	L'Å',	"AO",	/* A circle */
+	L'Æ',	"Ae",	/* AE ligature */
+	L'Æ',	"AE",	/* AE ligature */
+	L'Ç',	"C,",	/* C cedilla */
+	L'È',	"E`",	/* E grave */
+	L'É',	"E'",	/* E acute */
+	L'Ê',	"E^",	/* E circumflex */
+	L'Ë',	"E\"",	/* E dieresis */
+	L'Ë',	"E:",	/* E dieresis */
+	L'Ì',	"I`",	/* I grave */
+	L'Í',	"I'",	/* I acute */
+	L'Î',	"I^",	/* I circumflex */
+	L'Ï',	"I\"",	/* I dieresis */
+	L'Ï',	"I:",	/* I dieresis */
+	L'Ð',	"D-",	/* Eth */
+	L'Ñ',	"N~",	/* N tilde */
+	L'Ò',	"O`",	/* O grave */
+	L'Ó',	"O'",	/* O acute */
+	L'Ô',	"O^",	/* O circumflex */
+	L'Õ',	"O~",	/* O tilde */
+	L'Ö',	"O\"",	/* O dieresis */
+	L'Ö',	"O:",	/* O dieresis */
+	L'Ö',	"OE",	/* O dieresis */
+	L'Ö',	"Oe",	/* O dieresis */
+	L'×',	"xx",	/* times sign */
+	L'Ø',	"O/",	/* O slash */
+	L'Ù',	"U`",	/* U grave */
+	L'Ú',	"U'",	/* U acute */
+	L'Û',	"U^",	/* U circumflex */
+	L'Ü',	"U\"",	/* U dieresis */
+	L'Ü',	"U:",	/* U dieresis */
+	L'Ü',	"UE",	/* U dieresis */
+	L'Ü',	"Ue",	/* U dieresis */
+	L'Ý',	"Y'",	/* Y acute */
+	L'Þ',	"P|",	/* Thorn */
+	L'Þ',	"Th",	/* Thorn */
+	L'Þ',	"TH",	/* Thorn */
+	L'ß',	"ss",	/* sharp s */
+	L'à',	"a`",	/* a grave */
+	L'á',	"a'",	/* a acute */
+	L'â',	"a^",	/* a circumflex */
+	L'ã',	"a~",	/* a tilde */
+	L'ä',	"a\"",	/* a dieresis */
+	L'ä',	"a:",	/* a dieresis */
+	L'å',	"ao",	/* a circle */
+	L'æ',	"ae",	/* ae ligature */
+	L'ç',	"c,",	/* c cedilla */
+	L'è',	"e`",	/* e grave */
+	L'é',	"e'",	/* e acute */
+	L'ê',	"e^",	/* e circumflex */
+	L'ë',	"e\"",	/* e dieresis */
+	L'ë',	"e:",	/* e dieresis */
+	L'ì',	"i`",	/* i grave */
+	L'í',	"i'",	/* i acute */
+	L'î',	"i^",	/* i circumflex */
+	L'ï',	"i\"",	/* i dieresis */
+	L'ï',	"i:",	/* i dieresis */
+	L'ð',	"d-",	/* eth */
+	L'ñ',	"n~",	/* n tilde */
+	L'ò',	"o`",	/* o grave */
+	L'ó',	"o'",	/* o acute */
+	L'ô',	"o^",	/* o circumflex */
+	L'õ',	"o~",	/* o tilde */
+	L'ö',	"o\"",	/* o dieresis */
+	L'ö',	"o:",	/* o dieresis */
+	L'ö',	"oe",	/* o dieresis */
+	L'÷',	"-:",	/* divide sign */
+	L'ø',	"o/",	/* o slash */
+	L'ù',	"u`",	/* u grave */
+	L'ú',	"u'",	/* u acute */
+	L'û',	"u^",	/* u circumflex */
+	L'ü',	"u\"",	/* u dieresis */
+	L'ü',	"u:",	/* u dieresis */
+	L'ü',	"ue",	/* u dieresis */
+	L'ý',	"y'",	/* y acute */
+	L'þ',	"th",	/* thorn */
+	L'þ',	"p|",	/* thorn */
+	L'ÿ',	"y\"",	/* y dieresis */
+	L'ÿ',	"y:",	/* y dieresis */
+	0,	0,
+};
+
+enum
+{
+	/* controller command byte */
+	Cscs1=		(1<<6),		/* scan code set 1 */
+	Cmousedis=	(1<<5),		/* mouse disable */
+	Ckbddis=	(1<<4),		/* kbd disable */
+	Csf=		(1<<2),		/* system flag */
+	Cmouseint=	(1<<1),		/* mouse interrupt enable */
+	Ckbdint=	(1<<0),		/* kbd interrupt enable */
+};
+
+static uchar ccc;
+
+int
+latin1(int k1, int k2)
+{
+	struct latin *l;
+
+	for(l=latintab; l->l; l++)
+		if(k1==l->c[0] && k2==l->c[1])
+			return l->l;
+	return 0;
+}
+
+/*
+ *  wait for output no longer busy
+ */
+static int
+outready(void)
+{
+	int tries;
+
+	for(tries = 0; (inb(Status) & Outbusy); tries++){
+		if(tries > 500)
+			return -1;
+		delay(2);
+	}
+	return 0;
+}
+
+/*
+ *  wait for input
+ */
+static int
+inready(void)
+{
+	int tries;
+
+	for(tries = 0; !(inb(Status) & Inready); tries++){
+		if(tries > 500)
+			return -1;
+		delay(2);
+	}
+	return 0;
+}
+
+/*
+ *  ask 8042 to enable the use of address bit 20
+ */
+void
+i8042a20(void)
+{
+	outready();
+	outb(Cmd, 0xD1);
+	outready();
+	outb(Data, 0xDF);
+	outready();
+}
+
+/*
+ *  ask 8042 to reset the machine
+ */
+void
+i8042reset(void)
+{
+	ushort *s = (ushort*)(KZERO|0x472);
+	int i, x;
+
+	*s = 0x1234;		/* BIOS warm-boot flag */
+
+	outready();
+	outb(Cmd, 0xFE);	/* pulse reset line (means resend on AT&T machines) */
+	outready();
+
+	/*
+	 *  Pulse it by hand (old somewhat reliable)
+	 */
+	x = 0xDF;
+	for(i = 0; i < 5; i++){
+		x ^= 1;
+		outready();
+		outb(Cmd, 0xD1);
+		outready();
+		outb(Data, x);	/* toggle reset */
+		delay(100);
+	}
+}
+
+/*
+ *  keyboard interrupt
+ */
+int
+kbdintr0(void)
+{
+	int s, c;
+	static int esc1, esc2;
+	static int shift;
+	static int caps;
+	static int ctl;
+	static int num;
+	static int lstate, k1, k2;
+	int keyup;
+
+	/*
+	 *  get status
+	 */
+	s = inb(Status);
+	if(!(s&Inready))
+		return -1;
+
+	/*
+	 *  get the character
+	 */
+	c = inb(Data);
+
+	/*
+	 *  e0's is the first of a 2 character sequence
+	 */
+	if(c == 0xe0){
+		esc1 = 1;
+		return 0;
+	} else if(c == 0xe1){
+		esc2 = 2;
+		return 0;
+	}
+
+	keyup = c&0x80;
+	c &= 0x7f;
+	if(c > sizeof kbtab){
+		print("unknown key %ux\n", c|keyup);
+		kbdchar(0, k1);
+		return 0;
+	}
+
+	if(esc1){
+		c = kbtabesc1[c];
+		esc1 = 0;
+		kbdchar(0, c);
+		return 0;
+	} else if(esc2){
+		esc2--;
+		return 0;
+	} else if(shift)
+		c = kbtabshift[c];
+	else
+		c = kbtab[c];
+
+	if(caps && c<='z' && c>='a')
+		c += 'A' - 'a';
+
+	/*
+	 *  keyup only important for shifts
+	 */
+	if(keyup){
+		switch(c){
+		case Shift:
+			shift = 0;
+			break;
+		case Ctrl:
+			ctl = 0;
+			break;
+		}
+		return 0;
+	}
+
+	/*
+ 	 *  normal character
+	 */
+	if(!(c & Spec)){
+		if(ctl)
+			c &= 0x1f;
+		switch(lstate){
+		case 1:
+			k1 = c;
+			lstate = 2;
+			return 0;
+		case 2:
+			k2 = c;
+			lstate = 0;
+			c = latin1(k1, k2);
+			if(c == 0){
+				kbdchar(0, k1);
+				c = k2;
+			}
+			/* fall through */
+		default:
+			break;
+		}
+	} else {
+		switch(c){
+		case Caps:
+			caps ^= 1;
+			return 0;
+		case Num:
+			num ^= 1;
+			return 0;
+		case Shift:
+			shift = 1;
+			return 0;
+		case Latin:
+			lstate = 1;
+			return 0;
+		case Ctrl:
+			ctl = 1;
+			return 0;
+		}
+	}
+	kbdchar(0, c);
+	return 0;
+}
+
+static void
+kbdintr(Ureg*, void*)
+{
+	while(kbdintr0() == 0)
+		;
+}
+
+static char *initfailed = "kbd init failed\n";
+
+void
+kbdinit(void)
+{
+	int c;
+
+	/* wait for a quiescent controller */
+	while((c = inb(Status)) & (Outbusy | Inready))
+		if(c & Inready)
+			inb(Data);
+
+	/* get current controller command byte */
+	outb(Cmd, 0x20);
+	if(inready() < 0){
+		print("kbdinit: can't read ccc\n");
+		ccc = 0;
+	} else
+		ccc = inb(Data);
+
+	/* enable kbd xfers and interrupts */
+	ccc &= ~Ckbddis;
+	ccc |= Csf | Ckbdint | Cscs1;
+	if(outready() < 0)
+		print(initfailed);
+	outb(Cmd, 0x60);
+	if(outready() < 0)
+		print(initfailed);
+	outb(Data, ccc);
+	if(outready() < 0)
+		print(initfailed);
+
+	setvec(V_KEYBOARD, kbdintr, 0);
+}
--- /dev/null
+++ b/os/boot/puma/l.s
@@ -1,0 +1,427 @@
+/*
+ *	File: 		l.s
+ *	Purpose:
+ *  	Puma Board StrongARM 110 Architecture Specific Assembly
+ *	
+ */
+
+#include "mem.h"
+#include "armv4.h"
+#include "puma.h"
+
+#define	DRAMWAIT	100000		/* 3.125μsec per iteration */
+#define	TL750R(r)	(TL750_BASE+(r)*4)
+
+#define	BOOTBASE	0x00200000
+
+TEXT		_main(SB),1,$-4
+	MOVW	R15, R7	/* save PC on entry */
+
+/*
+ * initialise DRAM controller on the TL750 (SDRAM mode)
+ */
+	MOVW	$DRAMWAIT, R0	/* wait 312 ms after reset before touching DRAM */
+dram1:
+	SUB.S	$1, R0
+	BNE	dram1
+
+	MOVW	$TL750R(0x103), R0	/* DMC_DELAY */
+	MOVW	$0x03333333, R1	/* DRAM timing parameters */
+	MOVW	R1, (R0)
+
+	MOVW	$TL750R(0x101), R0	/* DMC_SDRAM */
+	MOVW	$0x03133011, R1	/* SDRAM parameters for Puma */
+	MOVW	R1, (R0)
+
+	MOVW	$DRAMWAIT, R0	/* wait 312 ms for initialisation */
+dram2:
+	SUB.S	$1, R0
+	BNE	dram2
+
+	MOVW		$setR12(SB),R12
+
+/*
+ * copy bootstrap to final location in DRAM
+ */
+	MOVW	R7, baddr(SB)
+	MOVW	$(BOOTBASE+8), R0
+	CMP	R0, R7
+	BEQ	inplace
+	MOVW	$((128*1024)/4), R6
+copyboot:
+	MOVW.P	4(R7), R5
+	MOVW.P	R5, 4(R0)
+	SUB.S	$1, R6
+	BNE	copyboot
+	MOVW	$bootrel(SB), R7
+	MOVW	R7, R15
+
+TEXT	bootrel(SB), $-4
+
+/*
+ * set C environment and invoke main
+ */
+inplace:
+	MOVW		$mach0(SB),R13
+	MOVW		R13,m(SB)
+	ADD			$(MACHSIZE-12),R13
+
+	/* disable MMU activity */
+	BL			mmuctlregr(SB)
+	BIC			$(CpCmmu|CpCDcache|CpCwb|CpCIcache), R0
+	BL			mmuctlregw(SB)
+		
+	BL			main(SB)
+loop:
+	B			loop
+
+TEXT		idle(SB),$0
+	RET
+
+/*
+ *  basic timing loop to determine CPU frequency
+ */
+TEXT aamloop(SB), $-4				/* 3 */
+_aamloop:
+	MOVW		R0, R0			/* 1 */
+	MOVW		R0, R0			/* 1 */
+	MOVW		R0, R0			/* 1 */
+	SUB			$1, R0			/* 1 */
+	CMP			$0, R0			/* 1 */
+	BNE			_aamloop			/* 3 */
+	RET							/* 3 */
+
+/*
+ * Function: setr13( mode, pointer )
+ * Purpose:
+ *		Sets the stack pointer for a particular mode
+ */
+
+TEXT setr13(SB), $-4
+	MOVW		4(FP), R1
+
+	MOVW		CPSR, R2
+	BIC			$PsrMask, R2, R3
+	ORR			R0, R3
+	MOVW		R3, CPSR
+
+	MOVW		R13, R0
+	MOVW		R1, R13
+
+	MOVW		R2, CPSR
+
+	RET
+
+/*
+ * Function: _vundcall
+ * Purpose:
+ *		Undefined Instruction Trap Handler
+ *
+ */
+
+TEXT _vundcall(SB), $-4			
+_vund:
+	MOVM.DB		[R0-R3], (R13)
+	MOVW		$PsrMund, R0
+	B			_vswitch
+
+/*
+ * Function: _vsvccall
+ * Purpose:
+ *		Reset or SWI Handler
+ *
+ */
+
+TEXT _vsvccall(SB), $-4				
+_vsvc:
+	SUB			$12, R13
+	MOVW		R14, 8(R13)
+	MOVW		CPSR, R14
+	MOVW		R14, 4(R13)
+	MOVW		$PsrMsvc, R14
+	MOVW		R14, (R13)
+	B			_vsaveu
+
+/*
+ * Function: _pabcall
+ * Purpose:
+ *		Prefetch Abort Trap Handler
+ *
+ */
+
+TEXT _vpabcall(SB), $-4			
+_vpab:
+	MOVM.DB		[R0-R3], (R13)
+	MOVW		$PsrMabt, R0
+	B			_vswitch
+
+/*
+ * Function: _vdabcall
+ * Purpose:
+ *		Data Abort Trap Handler
+ *
+ */
+
+TEXT _vdabcall(SB), $-4	
+_vdab:
+	MOVM.DB		[R0-R3], (R13)
+	MOVW		$(PsrMabt+1), R0
+	B			_vswitch
+
+/*
+ * Function: _virqcall
+ * Purpose:
+ *		IRQ Trap Handler 
+ *
+ */
+
+TEXT _virqcall(SB), $-4				/* IRQ */
+_virq:
+	MOVM.DB		[R0-R3], (R13)
+	MOVW		$PsrMirq, R0
+	B			_vswitch
+
+/*
+ * Function: _vfiqcall
+ * Purpose:
+ *		FIQ Trap Handler 
+ *
+ */
+
+TEXT _vfiqcall(SB), $-4				/* FIQ */
+_vfiq:
+	MOVM.DB		[R0-R3], (R13)
+	MOVW		$PsrMfiq, R0
+	/* FALLTHROUGH */
+
+_vswitch:					/* switch to svc mode */
+	MOVW		SPSR, R1
+	MOVW		R14, R2
+	MOVW		R13, R3
+
+	MOVW		CPSR, R14
+	BIC			$PsrMask, R14
+	ORR			$(PsrDirq|PsrDfiq|PsrMsvc), R14
+	MOVW		R14, CPSR
+
+	MOVM.DB.W 	[R0-R2], (R13)
+	MOVM.DB	  	(R3), [R0-R3]
+
+_vsaveu:						/* Save Registers */
+	SUB			$4, R13		/* save link */
+	MOVW		R14, (R13)	/* MOVW.W R14,4(R13)*/
+
+	SUB			$8, R13
+
+	MOVW		R13, R14	/* ur->sp */
+	ADD			$(6*4), R14
+	MOVW		R14, 0(R13)
+
+	MOVW		8(SP), R14			/* ur->link */
+	MOVW		R14, 4(SP)
+
+	MOVM.DB.W 	[R0-R12], (R13)	
+	MOVW		R0, R0				/* gratuitous noop */
+
+	MOVW		$setR12(SB), R12		/* static base (SB) */
+	MOVW		R13, R0				/* argument is ureg */
+	SUB			$8, R13				/* space for arg+lnk*/
+	BL			trap(SB)
+
+
+_vrfe:							/* Restore Regs */
+	MOVW		CPSR, R0			/* splhi on return */
+	ORR			$(PsrDirq|PsrDfiq), R0, R1
+	MOVW		R1, CPSR
+	ADD			$(8+4*15), R13		/* [r0-R14]+argument+link */
+	MOVW		(R13), R14			/* restore link */
+	MOVW		8(R13), R0
+	MOVW		R0, SPSR
+	MOVM.DB.S 	(R13), [R0-R14]		/* restore user registers */
+	MOVW		R0, R0				/* gratuitous nop */
+	ADD			$12, R13		/* skip saved link+type+SPSR*/
+	RFE					/* MOVM.IA.S.W (R13), [R15] */
+
+
+/*
+ * Function: splhi
+ * Purpose:
+ *		Disable Interrupts
+ * Returns:
+ *		Previous interrupt state
+ */
+	
+TEXT splhi(SB), $-4					
+	MOVW		CPSR, R0
+	ORR			$(PsrDirq|PsrDfiq), R0, R1
+	MOVW		R1, CPSR
+	RET
+
+/*
+ * Function: spllo
+ * Purpose:
+ *		Enable Interrupts
+ * Returns:
+ *		Previous interrupt state
+ */
+
+TEXT spllo(SB), $-4
+	MOVW		CPSR, R0
+	BIC			$(PsrDirq), R0, R1
+	MOVW		R1, CPSR
+	RET
+
+/*
+ * Function: splx(level)
+ * Purpose:
+ *		Restore interrupt level
+ */
+
+TEXT splx(SB), $-4
+	MOVW		R0, R1
+	MOVW		CPSR, R0
+	MOVW		R1, CPSR
+	RET
+
+/*
+ * Function: islo
+ * Purpose:
+ *		Check if interrupts are enabled
+ *
+ */
+
+TEXT islo(SB), $-4
+	MOVW		CPSR, R0
+	AND			$(PsrDirq), R0
+	EOR			$(PsrDirq), R0
+	RET
+
+/*
+ * Function: cpsrr
+ * Purpose:
+ *		Returns current program status register
+ *
+ */
+
+TEXT cpsrr(SB), $-4
+	MOVW		CPSR, R0
+	RET
+
+/*
+ * Function: spsrr
+ * Purpose:
+ *		Returns saved program status register
+ *
+ */
+
+TEXT spsrr(SB), $-4
+	MOVW		SPSR, R0
+	RET
+
+/*
+ * MMU Operations
+ */
+TEXT mmuctlregr(SB), $-4
+	MRC		CpMMU, 0, R0, C(CpControl), C(0)
+	RET	
+
+TEXT mmuctlregw(SB), $-4
+	MCR		CpMMU, 0, R0, C(CpControl), C(0)
+	MOVW		R0, R0
+	MOVW		R0, R0
+	RET	
+
+/*
+ * Cache Routines
+ */
+
+/*
+ * Function: flushIcache
+ * Purpose:
+ *		Flushes the *WHOLE* instruction cache
+ */
+
+TEXT flushIcache(SB), $-4
+	MCR	 	CpMMU, 0, R0, C(CpCacheCtl), C(5), 0	
+	MOVW		R0,R0							
+	MOVW		R0,R0
+	MOVW		R0,R0
+	MOVW		R0,R0
+	RET
+
+
+
+/*
+ * Function: flushDentry
+ * Purpose:
+ *		Flushes an entry of the data cache
+ */
+
+TEXT flushDentry(SB), $-4
+	MCR		CpMMU, 0, R0, C(CpCacheCtl), C(6), 1
+	RET
+
+/*
+ * Function: drainWBuffer
+ * Purpose:
+ *		Drains the Write Buffer
+ */
+
+TEXT drainWBuffer(SB), $-4
+	MCR		CpMMU, 0, R0, C(CpCacheCtl), C(10), 4	
+	RET
+
+/*
+ * Function: writeBackDC
+ * Purpose:
+ *		Drains the dcache prior to flush
+ */
+
+TEXT writeBackDC(SB), $-4
+	MOVW		$0xE0000000, R0
+	MOVW		$8192, R1
+	ADD		R0, R1
+
+wbflush:
+	MOVW		(R0), R2
+	ADD		$32, R0
+	CMP		R1,R0
+	BNE		wbflush
+	RET
+
+/*
+ * Function: flushDcache(SB)
+ * Purpose:
+ *		Flush the dcache 
+ */
+
+TEXT flushDcache(SB), $-4
+	MCR		CpMMU, 0, R0, C(CpCacheCtl), C(6), 0	
+	RET
+
+/*
+ * Function: writeBackBDC(SB)
+ * Purpose:
+ *		Write back the Baby D-Cache
+ */
+
+TEXT writeBackBDC(SB), $-4		
+	MOVW		$0xE4000000, R0
+	MOVW		$0x200, R1
+	ADD		R0, R1
+
+wbbflush:
+	MOVW		(R0), R2
+	ADD		$32, R0
+	CMP		R1,R0
+	BNE		wbbflush
+	MCR		CpMMU, 0, R0, C(CpCacheCtl), C(10), 4	
+	MOVW		R0,R0								
+	MOVW		R0,R0
+	MOVW		R0,R0
+	MOVW		R0,R0
+	RET
+
+GLOBL	mach0+0(SB), $MACHSIZE
+GLOBL	m(SB), $4
+GLOBL	baddr(SB), $4
--- /dev/null
+++ b/os/boot/puma/lib.h
@@ -1,0 +1,107 @@
+/*
+ * functions (possibly) linked in, complete, from libc.
+ */
+
+/*
+ * mem routines
+ */
+extern	void*	memccpy(void*, void*, int, long);
+extern	void*	memset(void*, int, long);
+extern	int	memcmp(void*, void*, long);
+extern	void*	memmove(void*, void*, long);
+extern	void*	memchr(void*, int, long);
+
+/*
+ * string routines
+ */
+extern	char*	strcat(char*, char*);
+extern	char*	strchr(char*, char);
+extern	int	strcmp(char*, char*);
+extern	char*	strcpy(char*, char*);
+extern	char*	strncat(char*, char*, long);
+extern	char*	strncpy(char*, char*, long);
+extern	int	strncmp(char*, char*, long);
+extern	long	strlen(char*);
+extern	char*	strrchr(char*, char);
+extern	char*	strstr(char*, char*);
+
+/*
+ * print routines
+ * 	Fconv isn't used but is defined to satisfy prototypes in libg.h
+ *	that are never called.
+ */
+typedef	struct Fconv Fconv;
+
+extern	char*	donprint(char*, char*, char*, void*);
+extern	int	sprint(char*, char*, ...);
+extern	int	print(char*, ...);
+
+#define	PRINTSIZE	256
+
+/*
+ * one-of-a-kind
+ */
+extern	int	atoi(char*);
+extern	long	strtol(char*, char**, int);
+extern	ulong	strtoul(char*, char**, int);
+extern	char	end[];
+extern 	char edata[];
+
+/*
+ * Syscall data structures
+ */
+
+#define	MORDER	0x0003	/* mask for bits defining order of mounting */
+#define	MREPL	0x0000	/* mount replaces object */
+#define	MBEFORE	0x0001	/* mount goes before others in union directory */
+#define	MAFTER	0x0002	/* mount goes after others in union directory */
+#define	MCREATE	0x0004	/* permit creation in mounted directory */
+#define	MMASK	0x0007	/* all bits on */
+
+#define	OREAD	0	/* open for read */
+#define	OWRITE	1	/* write */
+#define	ORDWR	2	/* read and write */
+#define	OEXEC	3	/* execute, == read but check execute permission */
+#define	OTRUNC	16	/* or'ed in (except for exec), truncate file first */
+#define	OCEXEC	32	/* or'ed in, close on exec */
+#define	ORCLOSE	64	/* or'ed in, remove on close */
+
+#define	NCONT	0	/* continue after note */
+#define	NDFLT	1	/* terminate after note */
+
+typedef struct Qid	Qid;
+typedef struct Dir	Dir;
+typedef struct Waitmsg	Waitmsg;
+
+#define	ERRLEN	64
+#define	DIRLEN	116
+#define	NAMELEN	28
+
+struct Qid
+{
+	ulong	path;
+	ulong	vers;
+};
+
+struct Dir
+{
+	char	name[NAMELEN];
+	char	uid[NAMELEN];
+	char	gid[NAMELEN];
+	Qid	qid;
+	ulong	mode;
+	long	atime;
+	long	mtime;
+	ulong	length;
+	short	type;
+	short	dev;
+};
+
+struct Waitmsg
+{
+	int	pid;		/* of loved one */
+	int	status;		/* unused; a placeholder */
+	ulong	time[3];	/* of loved one */
+	char	msg[ERRLEN];
+};
+#define	nelem(x)	(sizeof(x)/sizeof((x)[0]))
--- /dev/null
+++ b/os/boot/puma/main.c
@@ -1,0 +1,552 @@
+#include "u.h"
+#include "lib.h"
+#include "mem.h"
+#include "dat.h"
+#include "fns.h"
+#include "io.h"
+
+#include "dosfs.h"
+
+typedef struct Type Type;
+typedef struct Medium Medium;
+typedef struct Mode Mode;
+
+enum {
+	Dany		= -1,
+	Nmedia		= 16,
+
+	/* DS1 switch options */
+	Sflashfs		= 1<<0,	/* take local fs from flash */
+	Snotflash		= 1<<1,	/* don't boot from flash */
+};
+
+enum {					/* type */
+	Tflash,
+	Tether,
+	Thard,
+
+	Tany		= -1,
+};
+
+enum {					/* flag and name */
+	Fnone		= 0x00,
+
+	Fdos		= 0x01,
+	Ndos		= 0x00,
+	Fboot		= 0x02,
+	Nboot		= 0x01,
+	Fbootp		= 0x04,
+	Nbootp		= 0x02,
+	Fflash		= 0x08,
+	NName		= 0x03,
+
+	Fany		= Fbootp|Fboot|Fdos|Fflash,
+
+	Fini		= 0x10,
+	Fprobe		= 0x80,
+};
+
+enum {					/* mode */
+	Mauto		= 0x00,
+	Mlocal		= 0x01,
+	Manual		= 0x02,
+	NMode		= 0x03,
+};
+
+typedef struct Type {
+	int	type;
+	char	*cname;
+	int	flag;
+	int	(*init)(void);
+	long	(*read)(int, void*, long);
+	long	(*seek)(int, long);
+	Partition* (*setpart)(int, char*);
+	char*	name[NName];
+
+	int	mask;
+	Medium*	media;
+} Type;
+
+typedef struct Medium {
+	Type*	type;
+	int	flag;
+	Partition* partition;
+	Dos;
+
+	Medium*	next;
+} Medium;
+
+typedef struct Mode {
+	char*	name;
+	int	mode;
+} Mode;
+
+static Type types[] = {
+	{	Tflash, "flash",
+		Fflash,
+		flashinit, 0, 0, 0,
+		{ 0, "F", 0, }
+	},
+	{	Tether, "ether",
+		Fbootp,
+		etherinit, 0, 0, 0,
+		{ 0, 0, "e", },
+	},
+	{	Thard, "ata",
+		Fini|Fboot|Fdos,
+		0, 0, 0, 0,		/* not used now, will be later with PCMCIA */
+		{ "hd", "h", 0, },
+	},
+	{-1},
+};
+
+static Medium media[Nmedia];
+static Medium *curmedium = media;
+
+static Mode modes[NMode+1] = {
+	[Mauto]		{ "auto",   Mauto,  },
+	[Mlocal]	{ "local",  Mlocal, },
+	[Manual]	{ "manual", Manual, },
+};
+
+static char *inis[] = {
+	"inferno/inferno.ini",
+	"inferno.ini",
+	"plan9/plan9.ini",
+	"plan9.ini",
+	0,
+};
+char **ini;
+void	printversion(void);
+
+static int
+parse(char *line, int *type, int *flag, int *dev, char *file)
+{
+	Type *tp;
+	char buf[2*NAMELEN], *v[4], *p;
+	int i;
+
+	strcpy(buf, line);
+	switch(getcfields(buf, v, 4, "!")){
+
+	case 3:
+		break;
+
+	case 2:
+		v[2] = "";
+		break;
+
+	default:
+		return 0;
+	}
+
+	*flag = 0;
+	for(tp = types; tp->cname; tp++){
+		for(i = 0; i < NName; i++){
+
+			if(tp->name[i] == 0 || strcmp(v[0], tp->name[i]))
+				continue;
+			*type = tp->type;
+			*flag |= 1<<i;
+
+			if((*dev = strtoul(v[1], &p, 0)) == 0 && p == v[1])
+				return 0;
+		
+			strcpy(file, v[2]);
+		
+			return 1;
+		}
+	}
+
+	return 0;
+
+}
+
+static int
+boot(Medium *mp, int flag, char *file)
+{
+	Dosfile df;
+	char ixdos[128], *p;
+
+	if(flag & Fbootp){
+		sprint(BOOTLINE, "%s!%d", mp->type->name[Nbootp], mp->dev);
+		return bootp(mp->dev, file);
+	}
+
+	if(flag & Fflash){
+		if(mp->flag & Fflash && flashbootable(0))
+			flashboot(mp->dev);
+	}
+
+	if(flag & Fboot){
+
+		if(mp->flag & Fini){
+			(*mp->type->setpart)(mp->dev, "disk");
+			plan9ini(mp, nil);
+		}
+		if(file == 0 || *file == 0)
+			file = mp->partition->name;
+		(*mp->type->setpart)(mp->dev, file);
+		sprint(BOOTLINE, "%s!%d!%s", mp->type->name[Nboot], mp->dev, file);
+		return plan9boot(mp->dev, mp->seek, mp->read);
+	}
+
+	if(flag & Fdos){
+		if(mp->type->setpart)
+			(*mp->type->setpart)(mp->dev, "disk");
+		if(mp->flag & Fini)
+			plan9ini(mp, nil);
+		if(file == 0 || *file == 0){
+			strcpy(ixdos, *ini);
+			if(p = strrchr(ixdos, '/'))
+				p++;
+			else
+				p = ixdos;
+			strcpy(p, "infernopuma");
+			if(dosstat(mp, ixdos, &df) <= 0)
+				return -1;
+		}
+		else
+			strcpy(ixdos, file);
+		sprint(BOOTLINE, "%s!%d!%s", mp->type->name[Ndos], mp->dev, ixdos);
+		return dosboot(mp, ixdos);
+	}
+
+	return -1;
+}
+
+static Medium*
+allocm(Type *tp)
+{
+	Medium **l;
+
+	if(curmedium >= &media[Nmedia])
+		return 0;
+
+	for(l = &tp->media; *l; l = &(*l)->next)
+		;
+	*l = curmedium++;
+	return *l;
+}
+
+Medium*
+probe(int type, int flag, int dev)
+{
+	Type *tp;
+	int dombr, i, start;
+	Medium *mp;
+	Dosfile df;
+	Partition *pp;
+
+	for(tp = types; tp->cname; tp++){
+		if(type != Tany && type != tp->type || tp->init == 0)
+			continue;
+
+		if(flag != Fnone){
+			for(mp = tp->media; mp; mp = mp->next){
+				if((flag & mp->flag) && (dev == Dany || dev == mp->dev))
+					return mp;
+			}
+		}
+		if((tp->flag & Fprobe) == 0){
+			tp->flag |= Fprobe;
+			tp->mask = (*tp->init)();
+		}
+
+		for(i = 0; tp->mask; i++){
+			if((tp->mask & (1<<i)) == 0)
+				continue;
+			tp->mask &= ~(1<<i);
+
+			if((mp = allocm(tp)) == 0)
+				continue;
+
+			mp->dev = i;
+			mp->flag = tp->flag;
+			mp->seek = tp->seek;
+			mp->read = tp->read;
+			mp->type = tp;
+
+			if(mp->flag & Fboot){
+				if((mp->partition = (*tp->setpart)(i, "boot")) == 0)
+					mp->flag &= ~Fboot;
+				if((mp->flag & Fflash) == 0)
+					(*tp->setpart)(i, "disk");
+			}
+
+			if(mp->flag & Fdos){
+				start = 0;
+				dombr = 1;
+				if(mp->type->setpart){
+					if(pp = (*mp->type->setpart)(i, "dos")){
+						if(start = pp->start)
+							dombr = 0;
+					}
+					(*tp->setpart)(i, "disk");
+				}
+				if(dosinit(mp, start, dombr) < 0)
+					mp->flag &= ~(Fini|Fdos);
+				else
+					print("dos init failed\n");
+			}
+
+			if(mp->flag & Fini){
+				mp->flag &= ~Fini;
+				for(ini = inis; *ini; ini++){
+					if(dosstat(mp, *ini, &df) <= 0)
+						continue;
+					mp->flag |= Fini;
+					break;
+				}
+			}
+
+			if((flag & mp->flag) && (dev == Dany || dev == i))
+				return mp;
+		}
+	}
+
+	return 0;
+}
+
+static void
+pause(void)
+{
+	long d;
+	for(d=0; d<10000000; d++)
+		;
+	USED(d);
+}
+
+static void
+flash(int n)
+{
+	int i;
+
+	if(n <= 0)
+		return;
+	for(i=0; i<n-1; i++) {
+		led(1);
+		pause();
+		led(0);
+		pause();
+	}
+	led(1);
+	pause();pause();pause();
+	led(0);
+	pause();pause();pause();
+}
+
+void
+main(void)
+{
+	Medium *mp;
+	int dev, flag, i, mode, tried, type, options;
+	char def[2*NAMELEN], file[2*NAMELEN], line[80], *p;
+	Type *tp;
+
+	memset(edata, 0, end-edata );			/* clear the BSS */		
+	pumainit();
+
+	machinit();
+	meminit();
+	trapinit();
+	consinit();	/* screen and keyboard initially */
+//	screeninit();
+	alarminit();
+	clockinit();
+	printversion();
+
+	spllo();
+	options = optionsw();
+
+
+	mp = 0;
+	for(tp = types; tp->cname; tp++){
+		if(tp->type == Tether)
+			continue;
+		if((mp = probe(tp->type, Fini, Dany)) && (mp->flag & Fini)){
+			plan9ini(mp, nil);
+			break;
+		}
+	}
+
+	if(mp == 0 || (mp->flag & Fini) == 0)
+		plan9ini(nil, flashconfig(0));
+
+	//consinit();	/* establish new console location */
+
+	if((options & Snotflash) == 0 && flashbootable(0)){
+		print("Flash boot\n");
+		flashboot(0);
+	}
+
+	tried = 0;
+	mode = Mauto;
+	p = getconf("bootfile");
+	flag = 0;
+
+	if(p != 0) {
+		mode = Manual;
+		for(i = 0; i < NMode; i++){
+			if(strcmp(p, modes[i].name) == 0){
+				mode = modes[i].mode;
+				goto done;
+			}
+		}
+		if(parse(p, &type, &flag, &dev, file) == 0) {
+			print("Bad bootfile syntax: %s\n", p);
+			goto done;
+		}
+		mp = probe(type, flag, dev);
+		if(mp == 0) {
+			print("Cannot access device: %s\n", p);
+			goto done;
+		}
+		tried = boot(mp, flag, file);
+	}
+done:
+	if(tried == 0 && mode != Manual){
+		flag = Fany;
+		if(mode == Mlocal)
+			flag &= ~Fbootp;
+		if(options & Snotflash)
+			flag &= ~Fflash;
+		if((mp = probe(Tany, flag, Dany)) != 0)
+			boot(mp, flag & mp->flag, 0);
+	}
+
+	def[0] = 0;
+	probe(Tany, Fnone, Dany);
+
+	flag = 0;
+	for(tp = types; tp->cname; tp++){
+		for(mp = tp->media; mp; mp = mp->next){
+			if(flag == 0){
+				flag = 1;
+				print("Boot devices:");
+			}
+
+			if(mp->flag & Fbootp)
+				print(" %s!%d", mp->type->name[Nbootp], mp->dev);
+			if(mp->flag & Fdos)
+				print(" %s!%d", mp->type->name[Ndos], mp->dev);
+			if(mp->flag & Fflash || mp->flag & Fboot)
+				print(" %s!%d", mp->type->name[Nboot], mp->dev);
+		}
+	}
+	if(flag)
+		print("\n");
+
+	for(;;){
+		if(getstr("boot from", line, sizeof(line), def) >= 0){
+			if(parse(line, &type, &flag, &dev, file)){
+				if(mp = probe(type, flag, dev))
+					boot(mp, flag, file);
+			}
+		}
+		def[0] = 0;
+	}
+}
+
+void
+machinit(void)
+{
+	memset(m, 0, sizeof(*m));
+	m->delayloop = 20000;
+}
+
+void
+printversion(void)
+{
+	print("StrongARM SA-110 ");
+	print("%d MHz system\n", m->speed);
+	print("\n");
+{extern long baddr; print("%8.8lux\n", baddr);}
+}
+
+int
+optionsw()
+{
+	return 0;
+}
+
+int
+getcfields(char* lp, char** fields, int n, char* sep)
+{
+	int i;
+
+	for(i = 0; lp && *lp && i < n; i++){
+		while(*lp && strchr(sep, *lp) != 0)
+			*lp++ = 0;
+		if(*lp == 0)
+			break;
+		fields[i] = lp;
+		while(*lp && strchr(sep, *lp) == 0){
+			if(*lp == '\\' && *(lp+1) == '\n')
+				*lp++ = ' ';
+			lp++;
+		}
+	}
+
+	return i;
+}
+
+static	Map	memv[512];
+static	RMap	rammap = {"physical memory"};
+
+void
+meminit(void)
+{
+	ulong e;
+
+	mapinit(&rammap, memv, sizeof(memv));
+	e = PADDR(end);
+	mapfree(&rammap, e, 4*1024*1024-e);
+}
+
+void*
+ialloc(ulong n, int align)
+{
+	ulong a;
+	int s;
+
+	if(align <= 0)
+		align = 4;
+	s = splhi();
+	a = mapalloc(&rammap, 0, n, align);
+	splx(s);
+	if(a == 0)
+		panic("ialloc");
+	return memset(KADDR(a), 0, n);
+}
+
+void*
+malloc(ulong n)
+{
+	ulong *p;
+
+	n = ((n+sizeof(int)-1)&~(sizeof(int)-1))+2*sizeof(int);
+	p = ialloc(n, sizeof(int));
+	*p++ = 0xcafebeef;
+	*p++ = n;
+	return p;
+}
+
+void
+free(ulong *p)
+{
+	int s;
+
+	if(p){
+		if(*(p -= 2) != 0xcafebeef)
+			panic("free");
+		s = splhi();
+		mapfree(&rammap, (ulong)p, p[1]);
+		splx(s);
+	}
+}
+
+void
+sched(void)
+{
+}
--- /dev/null
+++ b/os/boot/puma/mem.h
@@ -1,0 +1,38 @@
+/*
+ * Memory and machine-specific definitions.  Used in C and assembler.
+ */
+
+/*
+ * Sizes
+ */
+#define	BI2BY		8			/* bits per byte */
+#define	BI2WD		32			/* bits per word */
+#define	BY2WD		4			/* bytes per word */
+#define	BY2PG		4096			/* bytes per page */
+#define	WD2PG		(BY2PG/BY2WD)		/* words per page */
+#define	PGSHIFT		12			/* log(BY2PG) */
+#define	PGROUND(s)	(((s)+(BY2PG-1))&~(BY2PG-1))
+
+#define	MAXMACH		1			/* max # cpus system can run */
+
+/*
+ * Time
+ */
+#define	HZ		(20)				/* clock frequency */
+#define	MS2HZ		(1000/HZ)			/* millisec per clock tick */
+#define	TK2SEC(t)	((t)/HZ)			/* ticks to seconds */
+#define	TK2MS(t)	((((ulong)(t))*1000)/HZ)	/* ticks to milliseconds */
+#define	MS2TK(t)	((((ulong)(t))*HZ)/1000)	/* milliseconds to ticks */
+
+/*
+ * Fundamental addresses
+ */
+
+/*
+ *  Address spaces
+ *
+ *  Kernel is at 0x00008000
+ */
+#define	KZERO		0x00000000		/* base of kernel address space */
+#define	KTZERO		KZERO			/* first address in kernel text */
+#define	MACHSIZE	4096
--- /dev/null
+++ b/os/boot/puma/mkfile
@@ -1,0 +1,71 @@
+<../../../mkconfig
+
+SYSTARG=Inferno
+OBJTYPE=arm
+<$ROOT/mkfiles/mkfile-$SYSTARG-$OBJTYPE	#set vars based on target system
+
+TARGET=${O}boot
+OBJ=\
+	l.$O\
+	div.$O\
+	8250.$O\
+	alarm.$O\
+	bootp.$O\
+	clock.$O\
+	console.$O\
+	conf.$O\
+	dosboot.$O\
+	donprint.$O\
+	ether.$O\
+	ether8900.$O\
+	flash.$O\
+	kbd.$O\
+	main.$O\
+	plan9boot.$O\
+	puma.$O\
+	qio.$O\
+	rmap.$O\
+	trap.$O\
+	zqs.$O
+
+HFILES=\
+	lib.h\
+	mem.h\
+	dat.h\
+	fns.h\
+	io.h\
+	boot.h\
+	armv4.h\
+	puma.h\
+
+CFLAGS=-w -I.
+LFLAGS=-H1 -R4 -T0x00200000 -E_main #-a
+#LFLAGS=-H1 -R4 -T0x00008080 -E_main #-a
+#LFLAGS = -H1 -R4 -T0xa00000c0 -E_main #-a
+
+all:V:	$TARGET
+
+$TARGET:	$OBJ
+	$LD -o $target $LFLAGS -l $prereq -lc
+	ls -l $target
+
+installall:V:	install
+install:V:	bb $TARGET
+	cp $TARGET /arm
+
+%.$O:	%.s
+	$AS $stem.s
+
+%.$O:	%.c
+	$CC $CFLAGS $stem.c
+
+%.$O:	$HFILES
+
+clock.$O floppy.$O trap.$O:	ureg.h
+conf.$O dosboot.$O main.$O:	dosfs.h
+ether.$O ether2000.$O ether509.$O ether8003.$O ether8390.$O:	ether.h
+bootp.$O:	ip.h
+
+clean:
+	rm -f *.[$OS] [$OS].out y.tab.? y.debug y.output $TARGET
+
--- /dev/null
+++ b/os/boot/puma/outb.c
@@ -1,0 +1,20 @@
+typedef	unsigned short	ushort;
+typedef	unsigned char	uchar;
+
+enum {
+	IsaIOBase		= 0xf0000000,
+	IsaMemBase	= 0xe0000000,
+
+	IOBase		= 0x300,
+	MemBase		= 0xc0000,
+
+	TxFrame		= 0x0a00,
+};
+
+#define	regw(reg, val)		*((ushort *)IsaMemBase + MemBase + (reg)) = (val)
+
+void
+main(void)
+{
+	regw(TxFrame, 0x1234);
+}
--- /dev/null
+++ b/os/boot/puma/plan9boot.c
@@ -1,0 +1,93 @@
+#include	"u.h"
+#include	"lib.h"
+#include	"mem.h"
+#include	"dat.h"
+#include	"fns.h"
+
+char *premature = "premature EOF\n";
+
+/*
+ *  read in a segment
+ */
+static long
+readseg(int dev, long (*read)(int, void*, long), long len, long addr)
+{
+	char *a;
+	long n, sofar;
+
+	a = (char *)addr;
+	for(sofar = 0; sofar < len; sofar += n){
+		n = 8*1024;
+		if(len - sofar < n)
+			n = len - sofar;
+		n = (*read)(dev, a + sofar, n);
+		if(n <= 0)
+			break;
+		print(".");
+	}
+	return sofar;
+}
+
+/*
+ *  boot
+ */
+int
+plan9boot(int dev, long (*seek)(int, long), long (*read)(int, void*, long))
+{
+	long n;
+	long addr;
+	void (*b)(void);
+	Exec *ep;
+
+	if((*seek)(dev, 0) < 0)
+		return -1;
+
+	/*
+	 *  read header
+	 */
+	ep = (Exec *) ialloc(sizeof(Exec), 0);
+	n = sizeof(Exec);
+	if(readseg(dev, read, n, (ulong) ep) != n){
+		print(premature);
+		return -1;
+	}
+	if(GLLONG(ep->magic) != E_MAGIC){
+		print("bad magic 0x%lux not a plan 9 executable!\n", GLLONG(ep->magic));
+		return -1;
+	}
+
+	/*
+	 *  read text
+	 */
+	addr = PADDR(GLLONG(ep->entry));
+	n = GLLONG(ep->text);
+	print("%d", n);
+	if(readseg(dev, read, n, addr) != n){
+		print(premature);
+		return -1;
+	}
+
+	/*
+	 *  read data (starts at first page after kernel)
+	 */
+	addr = PGROUND(addr+n);
+	n = GLLONG(ep->data);
+	print("+%d", n);
+	if(readseg(dev, read, n, addr) != n){
+		print(premature);
+		return -1;
+	}
+
+	/*
+	 *  bss and entry point
+	 */
+	print("+%d\nstart at 0x%lux\n", GLLONG(ep->bss), GLLONG(ep->entry)); uartwait();
+
+	/*
+	 *  Go to new code. It's up to the program to get its PC relocated to
+	 *  the right place.
+	 */
+	b = (void (*)(void))(PADDR(GLLONG(ep->entry)));
+	(*b)();
+	return 0;
+}
--- /dev/null
+++ b/os/boot/puma/puma.c
@@ -1,0 +1,123 @@
+#include "boot.h"
+
+//#define GPIO1_PORT		0x0078
+//#define GPIO2_PORT		0x0079
+
+#define GPIO2_N_LED1_DIAG	0x10	/* diagnostic led mask */
+
+#define	HARI2_N_LED1		0x01
+#define	HARI2_N_LED2		0x02
+#define	HARI2_N_LED3		0x04
+#define	HARI2_N_LED4		0x08
+
+/*
+ * National Semiconductor PC87306 Super I/O
+ */
+enum {
+	Index=		0x398,	/* sio configuration index register */
+	Data=		0x399,	/* sio configuration data register */
+};
+
+
+// byte flip table for Puma SuperIO port permuted as 76543210 -> 01234567
+unsigned char
+byteflip[] = {
+	0x00,0x80,0x40,0xC0,0x20,0xA0,0x60,0xE0,0x10,0x90,0x50,0xD0,0x30,0xB0,0x70,0xF0,
+	0x08,0x88,0x48,0xC8,0x28,0xA8,0x68,0xE8,0x18,0x98,0x58,0xD8,0x38,0xB8,0x78,0xF8,
+	0x04,0x84,0x44,0xC4,0x24,0xA4,0x64,0xE4,0x14,0x94,0x54,0xD4,0x34,0xB4,0x74,0xF4,
+	0x0C,0x8C,0x4C,0xCC,0x2C,0xAC,0x6C,0xEC,0x1C,0x9C,0x5C,0xDC,0x3C,0xBC,0x7C,0xFC,
+	0x02,0x82,0x42,0xC2,0x22,0xA2,0x62,0xE2,0x12,0x92,0x52,0xD2,0x32,0xB2,0x72,0xF2,
+	0x0A,0x8A,0x4A,0xCA,0x2A,0xAA,0x6A,0xEA,0x1A,0x9A,0x5A,0xDA,0x3A,0xBA,0x7A,0xFA,
+	0x06,0x86,0x46,0xC6,0x26,0xA6,0x66,0xE6,0x16,0x96,0x56,0xD6,0x36,0xB6,0x76,0xF6,
+	0x0E,0x8E,0x4E,0xCE,0x2E,0xAE,0x6E,0xEE,0x1E,0x9E,0x5E,0xDE,0x3E,0xBE,0x7E,0xFE,
+	0x01,0x81,0x41,0xC1,0x21,0xA1,0x61,0xE1,0x11,0x91,0x51,0xD1,0x31,0xB1,0x71,0xF1,
+	0x09,0x89,0x49,0xC9,0x29,0xA9,0x69,0xE9,0x19,0x99,0x59,0xD9,0x39,0xB9,0x79,0xF9,
+	0x05,0x85,0x45,0xC5,0x25,0xA5,0x65,0xE5,0x15,0x95,0x55,0xD5,0x35,0xB5,0x75,0xF5,
+	0x0D,0x8D,0x4D,0xCD,0x2D,0xAD,0x6D,0xED,0x1D,0x9D,0x5D,0xDD,0x3D,0xBD,0x7D,0xFD,
+	0x03,0x83,0x43,0xC3,0x23,0xA3,0x63,0xE3,0x13,0x93,0x53,0xD3,0x33,0xB3,0x73,0xF3,
+	0x0B,0x8B,0x4B,0xCB,0x2B,0xAB,0x6B,0xEB,0x1B,0x9B,0x5B,0xDB,0x3B,0xBB,0x7B,0xFB,
+	0x07,0x87,0x47,0xC7,0x27,0xA7,0x67,0xE7,0x17,0x97,0x57,0xD7,0x37,0xB7,0x77,0xF7,
+	0x0F,0x8F,0x4F,0xCF,0x2F,0xAF,0x6F,0xEF,0x1F,0x9F,0x5F,0xDF,0x3F,0xBF,0x7F,0xFF
+};
+
+int
+sio_inb(int port)
+{
+	unsigned char b = *(uchar *)IOBADDR(port);
+//	b = byteflip[b];
+	return b&0xff;
+}
+
+void
+sio_outb(int port, int data)
+{
+//	data = byteflip[data];
+	*(uchar *)IOBADDR(port) = data;
+}
+
+static void
+siocfg(int r, int v, int rsv)
+{
+	sio_outb(Index, r);
+	if(rsv)
+		v = (sio_inb(Data)&rsv) | (v&~rsv);
+	sio_outb(Data, v);
+	sio_outb(Data, v);	/* ``write data twice'' */
+}
+
+void
+sioinit(void)
+{
+	/* determine which byte flip is required ... */
+
+	siocfg(SIO_CONFIG_FAR, FAR_LPT_LPTA | FAR_UART1_COM1 | FAR_UART2_COM2, 0);
+	siocfg(SIO_CONFIG_FER,	FER_LPT_ENABLE | FER_UART1_ENABLE | FER_UART2_ENABLE | FER_IDE_ENABLE, 0);
+	siocfg(SIO_CONFIG_KRR,	KRR_KBC_ENABLE | KRR_KBC_MUST_BE_1 | KRR_RTC_ENABLE, 0x50);
+	siocfg(SIO_CONFIG_SCF0,SCF0_GPIO_PORT1_ENABLE | SCF0_GPIO_PORT2_ENABLE, 0xC0);
+
+	/* force UART interrupt pins low, not tristate by setting ienable in MCR (!) */
+	sio_outb(COM1_PORT+UART_MCR, sio_inb(COM1_PORT+UART_MCR)|(1<<3));
+	sio_outb(COM2_PORT+UART_MCR, sio_inb(COM2_PORT+UART_MCR)|(1<<3));
+}
+
+void
+led(int on)
+{
+	int gpio, hari, hbits;
+	int s;
+
+	s = splhi();
+
+	gpio = sio_inb(GPIO2_PORT);
+	if (on)
+		gpio &= ~GPIO2_N_LED1_DIAG;
+	else
+		gpio |= GPIO2_N_LED1_DIAG;
+	sio_outb(GPIO2_PORT, gpio);
+
+	hari = HARI2_INIT;
+	hbits = HARI2_N_LED1|HARI2_N_LED2|HARI2_N_LED3|HARI2_N_LED4;
+	if (on)
+		hari &= ~hbits;
+	else
+		hari |= hbits;
+	*(uchar *)HARI2 = hari;
+
+	
+	splx(s);
+
+}
+
+void
+pumainit(void)
+{
+	/*
+	 * Initialise HARI2 for:
+	 * a) Leds off
+	 * b) No Timer2 aborts
+	 * c) Ethernet on IRQ
+	 */
+	*(uchar *)HARI2 = HARI2_INIT|HARI2_N_LED1|HARI2_N_LED2|HARI2_N_LED3|HARI2_N_LED4;
+	sioinit();
+}
+
--- /dev/null
+++ b/os/boot/puma/puma.h
@@ -1,0 +1,435 @@
+/*
+ * Teralogic TL750 - Puma Evaluation Board
+ */
+ 
+/*
+ * Puma addresses
+ */
+#define EPROM_BASE	0x80000000	/* EPROM */
+#define FLASH_BASE	0xa0000000	/* Flash */
+#define TL750_BASE	0xc0000000	/* TL750 registers */
+#define ISAMEM_BASE	0xe0000000	/* ISA memory space */
+#define ISAIO_BASE		0xf0000000	/* ISA I/O space */
+
+#define ISAIO_SHIFT	2
+#define IOBADDR(io_port) (ISAIO_BASE + (io_port << ISAIO_SHIFT))
+
+/* Hardware address register for interrupts (HARI) */
+#define HARI1			0xE2000000 /* Interrupt status on read, User interrupt on write */
+#define HARI2			0xE3000000 /* More interrupt status on read, LEDs on write */	
+#define HARI1_FIQ_MASK	0x92	/* FIQ indicator bits in HARI1, others are IRQ */
+#define HARI2_INIT			0x20	/* No timer2 aborts, Ethernet on IRQ */			
+
+
+
+/*
+ * Interrupt Vectors
+ * corresponding to the HARIx_xxx_IRQ/FIQ bits above. 
+ *
+ * HARI1 interrupts 
+ */
+#define V_LPT				0		/* Parallel port interrupt */
+#define V_NM0			1		/* MPEG Decode Interrupt */
+#define V_NM1			2		/* MPEG Decode Interrupt */
+#define V_COM2			3		/* Serial Port 2 Interrupt */
+#define V_COM1			4       	/* Serial Port 1 Interrupt */
+#define V_MOUSE			5		/* Mouse Interrupt */
+#define V_KEYBOARD		6		/* Keyboard Interrupt */
+#define V_ETHERNET		7		/* Ethernet Interrupt */
+/* 
+ * HARI2 interrupts 
+ */
+#define V_TIMER0			8		/* 82C54 Timer 0 Interrupt */
+#define V_TIMER1			9		/* 82C54 Timer 1 Interrupt */
+#define V_TIMER2			10		/* 82C54 Timer 2 Interrupt */
+#define V_SOFTWARE		11		/* Software Interrupt */
+#define V_IDE				12		/* IDE Hard Drive Interrupt */
+#define V_SMARTCARD		13		/* Smart Card Interrupt */
+#define V_TL750			14		/* TL750 Interrupt */
+								/* Nothing in vector 15 for now */
+#define V_MAXNUM     		15
+
+/*
+ * Definitions for National Semiconductor PC87306 SuperIO configuration
+ */
+#define SIO_CONFIG_INDEX		0x398	/* SuperIO configuration index register */
+#define SIO_CONFIG_DATA			0x399	/* SuperIO configuration data register */
+
+#define SIO_CONFIG_RESET_VAL		0x88	/* Value read from first read of sio_config_index reg after reset */
+/*
+ * PC87306 Configuration Registers (The value listed is the configuration space
+ * index.)
+ */
+#define SIO_CONFIG_FER			0x00	/* Function Enable Register */
+
+#define     FER_LPT_ENABLE				0x01	/* Enable Parallel Port */
+#define     FER_UART1_ENABLE				0x02	/* Enable Serial Port 1 */
+#define     FER_UART2_ENABLE				0x04	/* Enable Serial Port 2 */
+#define     FER_FDC_ENABLE				0x08	/* Enable Floppy Controller */
+#define     FER_FDC_4DRIVE_ENCODING			0x10	/* Select Floppy 4 Drive Encoding */
+#define     FER_FDC_ADDR_ENABLE				0x20	/* Select Floppy Secondary Address */
+								/* 0: [0x3F0..0x3F7] */
+								/* 1: [0x370..0x377] */
+#define     FER_IDE_ENABLE				0x40	/* Enable IDE Controller */
+#define     FER_IDE_ADDR_SELECT				0x80	/* Select IDE Secondary Address */
+								/* 0: [0x1F0..0x1F7,0x3F6,0x3F7] */
+								/* 1: [0x170..0x177,0x376,0x377] */
+
+#define SIO_CONFIG_FAR			0x01	/* Function Address Register */
+
+#define     FAR_LPT_ADDR_MASK				0x03	/* Select LPT Address */
+								/* If (PNP0[4] == 0) then: */
+								/*     0: LPTB [0x378..0x37F] IRQ5/7 */
+								/*     1: LPTA [0x3BC..0x3BE] IRQ7 */
+								/*     2: LPTC [0x278..0x27F] IRQ5 */
+								/*     3: Reserved */
+								/* Else ignored. */
+#define	FAR_LPT_LPTB	0	/* 0: LPTB 0x378 irq5/7 */
+#define	FAR_LPT_LPTA	1	/* 1: LPTA 0x3BC irq 7 */
+#define	FAR_LPT_LPTC	2	/* 2: LPTC 0x278 irq5 */
+
+#define     FAR_UART1_ADDR_MASK				0x0C	/* Select Serial Port 1 Address */
+								/* 0: COM1 [0x3F8..0x3FF] */
+								/* 1: COM2 [0x2F8..0x2FF] */
+								/* 2: COM3 (See FAR[7:6]) */
+								/* 3: COM4 (See FAR[7:6]) */
+#define	FAR_UART1_COM1		0x00 
+#define     FAR_UART2_ADDR_MASK				0x30	/* Select Serial Port 2 Address */
+								/* 0: COM1 [0x3F8..0x3FF] */
+								/* 1: COM2 [0x2F8..0x2FF] */
+								/* 2: COM3 (See FAR[7:6]) */
+								/* 3: COM4 (See FAR[7:6]) */
+#define	FAR_UART2_COM2		0x10
+#define     FAR_EXTENDED_UART_ADDR_SELECT		0xC0	/* Extended Address Selects */
+								/*    COM3@IRQ4,  COM4@IRQ3 */
+								/* 0: COM3@0x3E8, COM4@0x2E8 */
+								/* 1: COM3@0x338, COM4@0x238 */
+								/* 2: COM3@0x2E8, COM4@0x2E0 */
+								/* 3: COM3@0x220, COM4@0x228 */
+
+#define SIO_CONFIG_PTR			0x02	/* Power & Test Register */
+
+#define     PTR_POWER_DOWN				0x01	/* Power down all enabled functions */
+#define     PTR_LPT_IRQ_SELECT				0x08	/* Select LPT IRQ if (FAR[1:0] == 0) */
+								/* 0: IRQ5 */
+								/* 1: IRQ7 */
+#define     PTR_UART1_TEST_MODE				0x10	/* Set serial port 1 test mode */
+#define     PTR_UART2_TEST_MODE				0x20	/* Set serial port 2 test mode */
+#define     PTR_LOCK_CONFIGURATION			0x40	/* Prevent all further config writes */
+								/* Only a RESET will reenable writes */
+#define     PTR_LPT_EXTENDED_MODE_SELECT		0x80	/* Select Mode if not EPP/ECP */
+								/* 0: Compatible Mode */
+								/* 1: Extended Mode */
+
+#define SIO_CONFIG_FCR			0x03	/* Function Control Register */
+							/* WARNING: The FCR register must be written */
+							/* using read-modify-write! */
+#define     FCR_TDR_MODE_SELECT				0x01	/* ? (floppy/tape) */
+#define     FCR_IDE_DMA_ENABLE				0x02	/* Enable IDE DMA mode */
+#define     FCR_EPP_ZERO_WAIT_STATE			0x40	/* Enable EPP zero wait state */
+
+#define SIO_CONFIG_PCR			0x04	/* Printer Control Register */
+
+#define     PCR_EPP_ENABLE				0x01	/* Enable parallel port EPP mode */
+#define     PCR_EPP_VERSION_SELECT			0x02	/* Select version of EPP mode */
+								/* 0: Version 1.7 */
+								/* 1: Version 1.9 (IEEE 1284) */
+#define     PCR_ECP_ENABLE				0x04	/* Enable parallel port ECP mode */
+#define     PCR_ECP_POWER_DOWN_CLOCK_ENABLE		0x08	/* Enable clock in power-down state */
+								/* 0: Freeze ECP clock */
+								/* 1: Run ECP clock */
+#define     PCR_ECP_INT_POLARITY_CONTROL		0x20	/* Interrupt polarity control */
+								/* 0: Level high or negative pulse */
+								/* 1: Level low or positive pulse */
+#define     PCR_ECP_INT_IO_CONTROL			0x40	/* Interrupt I/O control */
+								/* WARNING: Slightly safer to choose */
+								/* open drain if you don't know the */
+								/* exact requirements of the circuit */
+								/* 0: Totem-pole output */
+								/* 1: Open drain output */
+#define     PCR_RTC_RAM_WRITE_DISABLE			0x80	/* Disable writes to RTC RAM */
+								/* 0: Enable writes */
+								/* 1: Disable writes */
+
+#define SIO_CONFIG_KRR			0x05	/* Keyboard & RTC Control Register */
+							/* WARNING: The KRR register must be written */
+							/* with a 1 in bit 2, else the KBC will not */
+							/* work! */
+#define     KRR_KBC_ENABLE				0x01	/* Enable keyboard controller */
+#define     KRR_KBC_SPEED_CONTROL			0x02	/* Select clock divisor if !KRR[7] */
+								/* 0: Divide by 3 */
+								/* 1: Divide by 2 */
+#define	    KRR_KBC_MUST_BE_1				0x04	/* Reserved: This bit must be 1! */
+#define     KRR_RTC_ENABLE				0x08	/* Enable real time clock */
+#define     KRR_RTC_RAMSEL				0x20	/* Select RTC RAM bank */
+#define     KRR_KBC_CLOCK_SOURCE_SELECT			0x80	/* Select clock source */
+								/* 0: Use X1 clock source */
+								/* 1: Use SYSCLK clock source */
+
+#define SIO_CONFIG_PMC			0x06	/* Power Management Control Register */
+
+#define     PMC_IDE_TRISTATE_CONTROL			0x01	/* ? (power management) */
+#define     PMC_FDC_TRISTATE_CONTROL			0x02	/* ? (power management) */
+#define     PMC_UART1_TRISTATE_CONTROL			0x04	/* ? (power management) */
+#define     PMC_SELECTIVE_LOCK				0x20	/* ? (power management) */
+#define     PMC_LPT_TRISTATE_CONTROL			0x40	/* ? (power management) */
+
+#define SIO_CONFIG_TUP			0x07	/* Tape, UARTS & Parallel Port Register */
+
+#define     TUP_EPP_TIMEOUT_INT_ENABLE			0x04	/* Enable EPP timeout interrupts */
+
+#define SIO_CONFIG_SID			0x08	/* Super I/O Identification Register */
+
+#define     SID_ID_MASK					0xF8	/* Super I/O ID field */
+#define         SID_ID_PC87306					0x70	/* PC87306 ID value */
+
+#define SIO_CONFIG_ASC			0x09	/* Advanced Super I/O Config Register */
+							/* WARNING: The ASC register must be written */
+							/* with a 0 in bit 3! */
+							/* WARNING: The ASC register resets to 1 in */
+							/* bit 7 (PC/AT mode)! */
+#define     ASC_VLD_MASK				0x03	/* ? (floppy/tape) */
+#define     ASC_ENHANCED_TDR_SUPPORT			0x04	/* ? (floppy/tape) */
+#define     ASC_MUST_BE_0				0x08	/* Reserved: Must be 0 */
+#define     ASC_ECP_CNFGA				0x20	/* ? */
+#define     ASC_DENSEL_POLARITY_BIT			0x40	/* ? (floppy/tape) */
+#define     ASC_SYSTEM_OPERATION_MODE			0x80	/* Select system operation mode */
+								/* 0: PS/2 mode */
+								/* 1: PC/AT mode */
+
+#define SIO_CONFIG_CS0LA			0x0A	/* Chip Select 0 Low Address Register */
+
+#define SIO_CONFIG_CS0CF			0x0B	/* Chip Select 0 Configuration Register */
+							/* WARNING: The CS0CF register must be */
+							/* written with a 1 in bit 7! */
+#define     CS0CF_CS0_DECODE				0x08	/* Select CS0 decode sensitivity */
+								/* 0: Decode full 16-bit address */
+								/* 1: Decode only bits 15 thru 12 */
+#define     CS0CF_CS0_WRITE_ENABLE			0x10	/* Enable CS0 on write cycles */
+#define     CS0CF_CS0_READ_ENABLE			0x20	/* Enable CS0 on read cycles */
+#define     CS0CF_CS0_MUST_BE_1				0x80	/* Reserved: Must be 1 */
+
+#define SIO_CONFIG_CS1LA			0x0C	/* Chip Select 1 Low Address Register */
+
+#define SIO_CONFIG_CS1CF			0x0D	/* Chip Select 1 Configuration Register */
+
+#define     CS1CF_CS1_DECODE				0x08	/* Select CS1 decode sensitivity */
+								/* 0: Decode full 16-bit address */
+								/* 1: Decode only bits 15 thru 12 */
+#define     CS1CF_CS1_WRITE_ENABLE			0x10	/* Enable CS1 on write cycles */
+#define     CS1CF_CS1_READ_ENABLE			0x20	/* Enable CS1 on read cycles */
+
+#define SIO_CONFIG_IRC			0x0E	/* Infrared Configuration Register */
+
+#define     IRC_UART2_INTERFACE_MODE			0x01	/* Select UART2 interface mode */
+								/* 0: Normal (modem) mode */
+								/* 1: IR mode */
+#define     IRC_IR_FULL_DUPLEX				0x02	/* Select IR duplex mode */
+								/* 0: Full duplex mode */
+								/* 1: Half duplex mode */
+#define     IRC_ENCODED_IR_TRANSMITTER_DRIVE		0x10	/* IR transmitter drive control */
+								/* 0: IRTX active for 1.6usec */
+								/* 1: IRTX active for 3/16 baud */
+#define     IRC_ENCODED_IR_MODE				0x20	/* IR encode mode */
+								/* 0: Encoded mode */
+								/* 1: Non-encoded mode */
+
+#define SIO_CONFIG_GPBA			0x0F	/* GP I/O Port Base Address Config Register */
+
+#define SIO_CONFIG_CS0HA			0x10	/* Chip Select 0 High Address Register */
+
+#define SIO_CONFIG_CS1HA			0x11	/* Chip Select 1 High Address Register */
+
+#define SIO_CONFIG_SCF0			0x12	/* Super I/O Configuration Register 0 */
+
+#define     SCF0_RTC_RAM_LOCK				0x01	/* Lock (1) will prevent all further */
+								/* accesses to RTC RAM.  Only RESET */
+								/* return this bit to a 0. */
+#define     SCF0_IRQ1_12_LATCH_ENABLE			0x02	/* Enable IRQ1/IRQ12 latching */
+#define     SCF0_IRQ12_TRISTATE				0x04	/* IRQ12 tri-state control */
+								/* 0: Use quasi-bidirectional buffer */
+								/* 1: Tri-state IRQ12 */
+#define     SCF0_UART2_TRISTATE				0x08	/* Force UART2/IR outputs to */
+								/* tri-state when disabled */
+#define     SCF0_GPIO_PORT1_ENABLE			0x10	/* Enable GPIO port 1 */
+#define     SCF0_GPIO_PORT2_ENABLE			0x20	/* Enable GPIO port 2 */
+
+#define SIO_CONFIG_SCF1			0x18	/* Super I/O Configuration Register 1 */
+
+#define     SCF1_REPORTED_ECP_DMA			0x06	/* Reported ECP DMA number */
+								/* 0: Jumpered 8-bit DMA */
+								/* 1: DMA channel 1 */
+								/* 2: DMA channel 2 */
+								/* 3: DMA channel 3 */
+#define     SCF1_SELECTED_ECP_DMA			0x08	/* Selected ECP DMA pins */
+								/* 0: PDRQ0 & PDACK0 */
+								/* 1: PDRQ1 & PDACK1 */
+#define     SCF1_SCRATCH_BITS				0xC0	/* ? */
+
+#define SIO_CONFIG_LPTBA			0x19	/* LPT Base Address */
+
+#define SIO_CONFIG_PNP0			0x1B	/* Plug & Play Configuration Register 0 */
+
+#define     PNP0_LPT_INT_SELECT_CONTROL			0x10	/* LPT IRQ select control */
+								/* 0: IRQ selected by FAR[1:0] */
+								/* 1: IRQ selected by PNP0[5] */
+#define     PNP0_LPT_INT_MAPPING			0x20	/* LPT IRQ mapping */
+								/* 0: IRQ5 */
+								/* 1: IRQ7 */
+#define     PNP0_LPTA_BASE_ADDR_SELECT			0x40	/* LPTA base address */
+								/* 0: Always 0x3BC */
+								/* 1: Selected by LPTBA[7:0] */
+
+#define SIO_CONFIG_PNP1			0x1C	/* Plug & Play Configuration Register 1 */
+
+#define     PNP1_UARTS_INT_SELECT_CONTROL		0x01	/* UART interrupt select control */
+								/* 0: Use FAR[3:2] & FAR[5:4] */
+								/* 1: Use PNP1[2] & PNP1[6] */
+#define     PNP1_UART1_INT_MAPPING			0x04	/* UART1 interrupt mapping */
+								/* 0: IRQ3 */
+								/* 1: IRQ4 */
+#define     PNP1_UART2_INT_MAPPING			0x40	/* UART2 interrupt mapping */
+								/* 0: IRQ3 */
+								/* 1: IRQ4 */
+/*---------------------------------------------------------------------------*/
+
+/*
+ * Definitions for the SuperIO UART.
+ */
+#define COM1_PORT       0x3f8   
+#define COM2_PORT       0x2f8
+
+/*
+ * Register offsets.
+ */
+#define UART_RX    		0      /* Receive port, read only */
+#define UART_TX         0      /* transmit port, write only */
+#define UART_IER        1      /* Interrupt enable, read/write */
+#define UART_IIR        2      /* Interrupt id, read only */
+#define UART_FIFO_CONTROL 2    /* FIFO control, write only */
+#define UART_LCR        3      /* Line control register */
+#define UART_MCR      	4      /* Modem control register */
+#define UART_LSR        5      /* Line Status register */
+#define UART_MSR        6      /* Modem Status register */
+
+/* with the DLAB bit set, the first two registers contain the baud rate */
+#define UART_DLLSB      0
+#define UART_DLMSB      1
+
+/*
+ * Line control register 
+ */
+#define LCR_DB          3      /* Data bits in transmission (0 = 5, 1 = 6, 2 = 7, 3 = 8) */
+#define LCR_SB          4      /* Stop bit */
+#define LCR_PE          8      /* Parity enable */
+#define LCR_EP          16     /* Even parity */
+#define LCR_SP          32     /* Stick parity */
+#define LCR_BC          64     /* break control */
+#define LCR_DLAB        128    /* Divisor latch access bit */
+
+
+/*
+ *  Modem Control register
+ */
+#define MCR_DTR			1	/* Data Terminal Ready */
+#define MCR_RTS			2	/* Request To Send */
+#define MCR_OUT1		4	/* Out1 (not used) */
+#define MCR_IRQ_ENABLE  8	/* Enable IRQ */
+#define MCR_LOOP		16	/* Loopback mode */
+
+/*
+ * Line status bits.
+ */
+#define    LSR_DR     0x01        /* Data ready                         */
+#define    LSR_OE     0x02        /* Overrun error                      */
+#define    LSR_PE     0x04        /* Parity error                       */
+#define    LSR_FE     0x08        /* Framing error                      */
+#define    LSR_BI     0x10        /* Break interrupt                    */
+#define    LSR_THRE   0x20        /* Transmitter holding register empty */
+#define    LSR_TEMT   0x40        /* Transmitter empty                  */
+#define    LSR_FFE    0x80        /* Receiver FIFO error                */
+
+#define LSR_ERROR       (LSR_OE | LSR_PE | LSR_FE) 
+
+/*
+ * Interrupt Identification register (IIR)
+ */
+#define IIR_IP          1     /* No Interrupt pending */
+#define IIR_RECEIVE_LINE_STATUS	6 /* Overrun, Parity, Framing erros, Break */
+#define IIR_RDA			4     /* Receive data available */
+#define IIR_FIFO_FLAG	8     /* FIFO flag */
+#define IIR_FIFO_TIMEOUT (IIR_RDA+IIR_FIFO_FLAG)  /* Got data some time ago, but FIFO time out */
+#define IIR_THRE		2     /* Transmitter holding register empty. */
+#define IIR_MS			0     /* CTS, DSR, RING, DCD changed */
+#define IIR_HPIP        6     /* Highest priority interrupt pending */
+
+/*
+ * Interrupt enable register (IER)
+ */
+#define IER_RDA         1     /* Received data available */
+#define IER_THRE        2     /* Transmitter holding register empty */
+#define IER_RLS         4     /* Receiver line status */
+#define IER_MS          8     /* Modem status */
+
+/*
+ * PC87306 Parallel I/O Port
+ */
+#define LPT1_PORT		0x03BC
+
+/*
+ * PC87306 General Purpose I/O Ports
+ */
+#define GPIO1_PORT		0x0078
+#define GPIO2_PORT		0x0079
+
+/*
+ * PC87306 IDE Port
+ */
+#define IDE_PORT_1		0x01F0
+#define IDE_PORT_2		0x03F6
+#define IDE_PORT_3		0x03F7
+
+/*
+ * PC87306 Floppy Port
+ */
+#define FDC_PORT		0x03F0
+
+/*
+ * PC87306 Real Time Clock/battery backed up RAM port
+ */
+#define RTC_INDEX_PORT		0x0070
+#define RTC_DATA_PORT		0x0071
+
+/*
+ * Offsets in RTC memory (RAMSEL = 0)
+ */
+#define RTC_SECONDS			0
+#define	RTC_SECONDS_ALARM	1
+#define RTC_MINUTES			2
+#define RTC_MINUTES_ALARM	3
+#define RTC_HOURS			4
+#define RTC_HOURS_ALARM		5
+#define RTC_DAY_OF_WEEK		6
+#define RTC_DAY_OF_MONTH	7
+#define RTC_MONTH			8
+#define RTC_YEAR			9
+#define RTC_CONTROL_A		0xA
+#define RTC_CONTROL_B		0xB
+#define RTC_CONTROL_C		0xC
+#define RTC_CONTROL_D		0xD
+
+#define RTC_NVRAM0_START	0xE
+#define RTC_NVRAM0_SIZE		114
+#define RTC_NVRAM1_START	0
+#define RTC_NVRAM1_SIZE		128
+#define	RTC_NVRAM_SIZE		(RTC_NVRAM0_SIZE+RTC_NVRAM1_SIZE)
+
+#define RTC_PWNVRAM_START	0x38	/* Start of protected NVRAM */
+#define RTC_PWNVRAM_SIZE	8		/* Size of protected NVRAM */
+
+
+/*
+ * PC87306 Keyboard controller ports
+ */
+#define KEYBD_DATA_PORT		0x0060
+#define KEYBD_CTRL_PORT 	0x0064
--- /dev/null
+++ b/os/boot/puma/qio.c
@@ -1,0 +1,128 @@
+#include "u.h"
+#include "lib.h"
+#include "mem.h"
+#include "dat.h"
+#include "fns.h"
+#include "io.h"
+
+struct Queue {
+	Block*	first;
+	Block*	last;
+	void	(*kick)(void*);
+	void*	arg;
+	long	len;
+};
+
+Block *
+iallocb(int n)
+{
+	Block *b;
+
+	b = (Block*)malloc(sizeof(Block)+n);
+	b->data = (uchar*)b + sizeof(Block);
+	b->rp = b->wp = b->data;
+	b->lim = b->data + n;
+	b->next = 0;
+	b->magic = 0xcafebee0;
+	return b;
+}
+
+void
+freeb(Block *b)
+{
+	if(b){
+		if(b->magic != 0xcafebee0)
+			panic("freeb");
+		b->magic = 0;
+		b->next = (Block*)0xdeadbabe;
+		free(b);
+	}
+}
+
+Queue *
+qopen(int limit, int msg, void (*kick)(void*), void *arg)
+{
+	Queue *q;
+
+	USED(limit, msg);
+	q = (Queue*)malloc(sizeof(Queue));
+	q->first = q->last = 0;
+	q->kick = kick;
+	q->arg = arg;
+	q->len = 0;
+	return q;
+}
+
+Block *
+qget(Queue *q)
+{
+	int s;
+	Block *b;
+
+	s = splhi();
+	if((b = q->first) != 0){
+		q->first = b->next;
+		b->next = 0;
+		q->len -= BLEN(b);
+		if(q->len < 0)
+			panic("qget");
+	}
+	splx(s);
+	return b;
+}
+
+void
+qbwrite(Queue *q, Block *b)
+{
+	int s;
+
+	s = splhi();
+	b->next = 0;
+	if(q->first == 0)
+		q->first = b;
+	else
+		q->last->next = b;
+	q->last = b;
+	q->len += BLEN(b);
+	splx(s);
+	if(q->kick)
+		q->kick(q->arg);
+}
+
+long
+qlen(Queue *q)
+{
+	return q->len;
+}
+
+int
+qbgetc(Queue *q)
+{
+	Block *b;
+	int s, c;
+
+	c = -1;
+	s = splhi();
+	while(c < 0 && (b = q->first) != nil){
+		if(b->rp < b->wp){
+			c = *b->rp++;
+			q->len--;
+		}
+		if(b->rp >= b->wp){
+			q->first = b->next;
+			b->next = nil;
+		}
+	}
+	splx(s);
+	return c;
+}
+
+void
+qbputc(Queue *q, int c)
+{
+	Block *b;
+
+	b = iallocb(1);
+	*b->wp++ = c;
+	qbwrite(q, b);
+}
--- /dev/null
+++ b/os/boot/puma/rmap.c
@@ -1,0 +1,104 @@
+#include "u.h"
+#include "lib.h"
+#include "mem.h"
+#include "dat.h"
+#include "fns.h"
+
+void
+mapinit(RMap *rmap, Map *map, int size)
+{
+	lock(rmap);
+	rmap->map = map;
+	rmap->mapend = map+(size/sizeof(Map));
+	unlock(rmap);
+}
+
+void
+mapfree(RMap* rmap, ulong addr, int size)
+{
+	Map *mp;
+	ulong t;
+
+	if(size <= 0)
+		return;
+
+	lock(rmap);
+	for(mp = rmap->map; mp->addr <= addr && mp->size; mp++)
+		;
+
+	if(mp > rmap->map && (mp-1)->addr+(mp-1)->size == addr){
+		(mp-1)->size += size;
+		if(addr+size == mp->addr){
+			(mp-1)->size += mp->size;
+			while(mp->size){
+				mp++;
+				(mp-1)->addr = mp->addr;
+				(mp-1)->size = mp->size;
+			}
+		}
+	}
+	else{
+		if(addr+size == mp->addr && mp->size){
+			mp->addr -= size;
+			mp->size += size;
+		}
+		else do{
+			if(mp >= rmap->mapend){
+				print("mapfree: %s: losing 0x%uX, %d\n",
+					rmap->name, addr, size);
+				break;
+			}
+			t = mp->addr;
+			mp->addr = addr;
+			addr = t;
+			t = mp->size;
+			mp->size = size;
+			mp++;
+		}while(size = t);
+	}
+	unlock(rmap);
+}
+
+ulong
+mapalloc(RMap* rmap, ulong addr, int size, int align)
+{
+	Map *mp;
+	ulong maddr, oaddr;
+
+	lock(rmap);
+	for(mp = rmap->map; mp->size; mp++){
+		maddr = mp->addr;
+
+		if(addr){
+			if(maddr > addr)
+				continue;
+			if(addr+size > maddr+mp->size)
+				break;
+			maddr = addr;
+		}
+
+		if(align > 0)
+			maddr = ((maddr+align-1)/align)*align;
+		if(mp->addr+mp->size-maddr < size)
+			continue;
+
+		oaddr = mp->addr;
+		mp->addr = maddr+size;
+		mp->size -= maddr-oaddr+size;
+		if(mp->size == 0){
+			do{
+				mp++;
+				(mp-1)->addr = mp->addr;
+			}while((mp-1)->size = mp->size);
+		}
+
+		unlock(rmap);
+		if(oaddr != maddr)
+			mapfree(rmap, oaddr, maddr-oaddr);
+
+		return maddr;
+	}
+	unlock(rmap);
+
+	return 0;
+}
--- /dev/null
+++ b/os/boot/puma/squeeze.h
@@ -1,0 +1,34 @@
+
+/*
+ * squeezed file format:
+ *	Sqhdr
+ *	original Exec header
+ *	two Squeeze tables
+ *	squeezed segment
+ *	unsqueezed segment, if any
+ */
+#define	SQMAGIC	(ulong)0xFEEF0F1E
+
+typedef struct Sqhdr Sqhdr;
+struct Sqhdr {
+	uchar	magic[4];	/* SQMAGIC */
+	uchar	text[4];	/* squeezed length of text (excluding tables) */
+	uchar	data[4];	/* squeezed length of data (excluding tables) */
+	uchar	asis[4];	/* length of unsqueezed segment */
+	uchar	toptxt[4];	/* value for 0 encoding in text */
+	uchar	topdat[4];	/* value for 0 encoding in data */
+	uchar	sum[4];	/* simple checksum of unsqueezed data */
+	uchar	flags[4];
+};
+#define	SQHDRLEN	(8*4)
+
+/*
+ * certain power instruction types are rearranged by sqz
+ * so as to move the variable part of the instruction word to the
+ * low order bits.  note that the mapping is its own inverse.
+ */
+#define	QREMAP(X)\
+	switch((X)>>26){\
+	case 19: case 31: case 59: case 63:\
+		(X) = (((X) & 0xFC00F801) | (((X)>>15)&0x7FE) | (((X)&0x7FE)<<15));\
+	}
--- /dev/null
+++ b/os/boot/puma/sum.c
@@ -1,0 +1,13 @@
+int
+sum(int a, int b, int c)
+{
+	return a+b+c;
+}
+
+void
+main(void)
+{
+	int s;
+
+	s = sum(1, 2, 3);
+}
--- /dev/null
+++ b/os/boot/puma/trap.c
@@ -1,0 +1,190 @@
+#include "boot.h"
+
+typedef struct IrqEntry {
+		void	(*r)(Ureg*, void*);
+		void 	*a;				
+} IrqEntry;
+
+IrqEntry Irq[V_MAXNUM+1];
+
+static void dumpstk(ulong *);
+void dumpregs(Ureg* ureg);
+
+void
+setvec(int v, void (*f)(Ureg*, void*), void* a)
+{
+	if(v < 0 || v >= V_MAXNUM)
+		panic("setvec: interrupt source %d out of range\n", v);
+	Irq[v].r = f;
+	Irq[v].a = a;
+}
+
+ulong irqstack[64];
+ulong fiqstack[64];
+ulong abtstack[64];
+ulong undstack[64];
+
+static void
+safeintr(Ureg*, void *a)
+{
+	int v = (int)a;
+//	print("spurious interrupt %d\n", v);
+	USED(v);
+}
+
+void
+trapinit(void)
+{
+	int offset;
+	ulong op;
+	int v;
+	int s;
+
+	s = splhi();
+
+	/* set up stacks for various exceptions */ 
+	setr13(PsrMirq, irqstack+nelem(irqstack)-1);
+	setr13(PsrMfiq, fiqstack+nelem(fiqstack)-1);
+	setr13(PsrMabt, abtstack+nelem(abtstack)-1);
+	setr13(PsrMund, undstack+nelem(undstack)-1);
+
+	for(v = 0; v <= V_MAXNUM; v++) {
+		Irq[v].r = safeintr;
+		Irq[v].a = (void *)v;
+	}
+
+	/* Reset Exception */
+	offset = ((((ulong) _vsvccall) - 0x0)-8) >> 2;
+	op = ( 0xea << 24 ) | offset;
+	*((ulong *) 0x0) = op; 
+
+	/* Undefined Instruction Exception */
+	offset = ((((ulong) _vundcall) - 0x4)-8) >> 2;
+	op = ( 0xea << 24 ) | offset;
+	*((ulong *) 0x4) = op;
+
+	/* SWI Exception */
+	offset = ((((ulong) _vsvccall) - 0x8)-8) >> 2;
+	op = ( 0xea << 24 ) | offset;
+	*((ulong *) 0x8) = op;
+
+	/* Prefetch Abort Exception */
+	offset = ((((ulong) _vpabcall) - 0xc)-8) >> 2;
+	op = ( 0xea << 24 ) | offset;
+	*((ulong *) 0xc) = op;
+
+	/* Data Abort Exception */
+	offset = ((((ulong) _vdabcall) - 0x10)-8) >> 2;
+	op = ( 0xea << 24 ) | offset;
+	*((ulong *) 0x10) = op;
+
+	/* IRQ Exception */
+ 	offset = ((((ulong) _virqcall) - 0x18)-8) >> 2;
+	op = ( 0xea << 24 ) | offset;
+	*((ulong *) 0x18) = op;
+
+	/* FIQ Exception */
+ 	offset = ((((ulong) _vfiqcall) - 0x1c)-8) >> 2;
+	op = ( 0xea << 24 ) | offset;
+	*((ulong *) 0x1c) = op;
+
+
+	flushIcache();
+	writeBackDC();
+	flushDcache();
+	flushIcache();
+	drainWBuffer();
+
+	splx(s);
+}
+
+/*
+ *  trap is called splhi().
+ */
+
+void
+trap(Ureg* ureg)
+{
+	ushort mask;
+	IrqEntry *ip;
+
+	/*
+	 * All interrupts/exceptions should be resumed at ureg->pc-4,
+	 * except for Data Abort which resumes at ureg->pc-8.
+	 */
+	ureg->pc -= 4;
+
+	switch(ureg->type) {
+	case PsrMirq:				/* Interrupt Request */
+		mask = *(uchar*)HARI1 | ((*(uchar*)HARI2) << 8);
+		ip = Irq;
+		while (mask != 0) {
+			if(mask&1)
+				ip->r(ureg, ip->a);
+			ip++;
+			mask >>= 1;
+		}
+		break;
+
+	case PsrMfiq:					/* FIQ */
+		mask = *(uchar*)HARI1 & HARI1_FIQ_MASK;
+		ip = Irq;
+		while (mask != 0) {
+			if(mask&1)
+				ip->r(ureg, ip->a);
+			ip++;
+			mask >>= 1;
+		}
+		break;
+
+	case PsrMund:			/* Undefined instruction */
+		dumpregs(ureg);
+		panic("Undefined Instruction Exception\n");
+		break;
+
+	case PsrMsvc:				/* Jump through 0 or SWI  */
+		dumpregs(ureg);
+		panic("SVC/SWI Exception\n");
+		break;
+
+	case PsrMabt:					/* Prefetch abort */
+		ureg->pc -= 4;
+		/* FALLTHROUGH */
+
+	case PsrMabt+1:	{				/* Data abort */
+		uint far =0;
+		uint fsr =0;
+
+		USED(far,fsr);
+		fsr = 0;	/*mmuregr(CpFSR);*/
+		far = 0;	/*mmuregr(CpFAR);	*/
+		if (ureg->type == PsrMabt)
+			print("Prefetch Abort/");
+		print("Data Abort\n");
+		
+		print("Data Abort: FSR %8.8uX FAR %8.8uX\n", fsr, far);
+		/* FALLTHROUGH */
+	}
+	default:
+		dumpregs(ureg);
+		panic("exception %uX\n", ureg->type);
+		break;
+	}
+
+	splhi();
+}
+
+void
+dumpregs(Ureg* ureg)
+{
+	print("PSR %8.8uX type %2.2uX PC %8.8uX LINK %8.8uX\n",
+		ureg->psr, ureg->type, ureg->pc, ureg->link);
+	print("R14 %8.8uX R13 %8.8uX R12 %8.8uX R11 %8.8uX R10 %8.8uX\n",
+		ureg->r14, ureg->r13, ureg->r12, ureg->r11, ureg->r10);
+	print("R9  %8.8uX R8  %8.8uX R7  %8.8uX R6  %8.8uX R5  %8.8uX\n",
+		ureg->r9, ureg->r8, ureg->r7, ureg->r6, ureg->r5);
+	print("R4  %8.8uX R3  %8.8uX R2  %8.8uX R1  %8.8uX R0  %8.8uX\n",
+		ureg->r4, ureg->r3, ureg->r2, ureg->r1, ureg->r0);
+	print("Stack is at: %8.8uX\n", ureg);
+/*	print("CPSR %8.8uX SPSR %8.8uX\n", cpsrr(), spsrr());*/
+}
--- /dev/null
+++ b/os/boot/puma/ureg.h
@@ -1,0 +1,22 @@
+typedef struct Ureg {
+	uint	r0;
+	uint	r1;
+	uint	r2;
+	uint	r3;
+	uint	r4;
+	uint	r5;
+	uint	r6;
+	uint	r7;
+	uint	r8;
+	uint	r9;
+	uint	r10;
+	uint	r11;
+	uint	r12;
+	uint	r13;
+	uint	r14;
+	uint	link;
+	uint	type;
+	uint	psr;
+//	uint	sp;
+	uint	pc;
+} Ureg;
--- /dev/null
+++ b/os/boot/puma/zqs.c
@@ -1,0 +1,216 @@
+#include "boot.h"
+#include "squeeze.h"
+
+#define	EXECHDRLEN	(8*4)
+
+typedef struct Squeeze Squeeze;
+struct Squeeze {
+	int	n;
+	ulong	tab[7*256];
+};
+
+#define	GET4(p)	(((((((p)[0]<<8)|(p)[1])<<8)|(p)[2])<<8)|(p)[3])
+
+/*
+ * for speed of unsqueezing from Flash, certain checks are
+ * not done inside the loop (as they would be in the unsqueeze program zqs),
+ * but instead the checksum is expected to catch corrupted files.
+ * in fact the Squeeze array bounds can't be exceeded in practice
+ * because the tables are always full for a squeezed kernel.
+ */
+enum {
+	QFLAG = 1,	/* invert powerpc-specific code transformation */
+	CHECK = 0,	/* check precise bounds in Squeeze array (otherwise checksum detects error) */
+};
+
+static	ulong	chksum;
+static	int	rdtab(Block*, Squeeze*, int);
+static	ulong*	unsqueeze(ulong*, uchar*, uchar*, Squeeze*, Squeeze*, ulong);
+static	uchar*	unsqzseg(uchar*, Block*, long, long, char*);
+static	Alarm*	unsqzal;
+
+int
+issqueezed(uchar *b)
+{
+	return GET4(b) == SQMAGIC? GET4(b+SQHDRLEN): 0;
+}
+
+static void
+unsqzdot(void*)
+{
+	unsqzal = alarm(500, unsqzdot, nil);
+	print(".");
+}
+
+long
+unsqueezef(Block *b, ulong *entryp)
+{
+	uchar *loada, *wp;
+	ulong toptxt, topdat, oldsum;
+	long asis, nst, nsd;
+	Sqhdr *sqh;
+	Exec *ex;
+
+	if(BLEN(b) < SQHDRLEN+EXECHDRLEN)
+		return -1;
+	sqh = (Sqhdr*)b->rp;
+	if(GET4(sqh->magic) != SQMAGIC)
+		return -1;
+	chksum = 0;
+	toptxt = GET4(sqh->toptxt);
+	topdat = GET4(sqh->topdat);
+	oldsum = GET4(sqh->sum);
+	asis = GET4(sqh->asis);
+	nst = GET4(sqh->text);
+	nsd = GET4(sqh->data);
+	b->rp += SQHDRLEN;
+	ex = (Exec*)b->rp;
+	if(GET4(ex->magic) != E_MAGIC){
+		print("zqs: not StrongARM executable\n");
+		return -1;
+	}
+	*entryp = GET4(ex->entry);
+	b->rp += EXECHDRLEN;
+	loada = KADDR(PADDR(*entryp));
+	wp = unsqzseg(loada, b, nst, toptxt, "text");
+	if(wp == nil){
+		print("zqs: format error\n");
+		return -1;
+	}
+	if(nsd){
+		wp = (uchar*)PGROUND((ulong)wp);
+		wp = unsqzseg(wp, b, nsd, topdat, "data");
+		if(wp == nil){
+			print("zqs: format error\n");
+			return -1;
+		}
+	}
+	if(asis){
+		memmove(wp, b->rp, asis);
+		wp += asis;
+		b->rp += asis;
+	}
+	if(chksum != oldsum){
+		print("\nsqueezed kernel: checksum error: %8.8lux need %8.8lux\n", chksum, oldsum);
+		return -1;
+	}
+	return wp-loada;
+}
+
+static uchar *
+unsqzseg(uchar *wp, Block *b, long ns, long top, char *what)
+{
+	static Squeeze sq3, sq4;
+
+	print("unpack %s %8.8lux %lud:", what, wp, ns);
+	if(ns == 0)
+		return wp;
+	if(rdtab(b, &sq3, 0) < 0)
+		return nil;
+	if(rdtab(b, &sq4, 8) < 0)
+		return nil;
+	if(BLEN(b) < ns){
+		print(" **size error\n");
+		return nil;
+	}
+	unsqzal = alarm(500, unsqzdot, nil);
+	wp = (uchar*)unsqueeze((ulong*)wp, b->rp, b->rp+ns, &sq3, &sq4, top);
+	cancel(unsqzal);
+	unsqzal = nil;
+	print("\n");
+	if(wp == nil){
+		print("zqs: corrupt squeezed data stream\n");
+		return nil;
+	}
+	b->rp += ns;
+	return wp;
+}
+
+static ulong*
+unsqueeze(ulong *wp, uchar *rp, uchar *ep, Squeeze *sq3, Squeeze *sq4, ulong top)
+{
+	ulong nx, csum;
+	int code, n;
+
+	if(QFLAG){
+		QREMAP(top);	/* adjust top just once, outside the loop */
+	}
+	csum = chksum;
+	while(rp < ep){
+		/* no function calls within this loop for speed */
+		code = *rp;
+		rp++;
+		n = 0;
+		nx = code>>4;
+		do{
+			if(nx == 0){
+				nx = top;
+			}else{
+				if(nx==1){
+					nx = (((((rp[3]<<8)|rp[2])<<8)|rp[1])<<8)|rp[0];
+					rp += 4;
+				}else if(nx <= 8){	/* 2 to 8 */
+					nx = ((nx-2)<<8) | rp[0];
+					if(CHECK && nx >= sq4->n)
+						return nil;	/* corrupted file */
+					nx = sq4->tab[nx] | rp[1];
+					rp += 2;
+				}else{	/* 9 to 15 */
+					nx = ((nx-9)<<8) | rp[0];
+					if(CHECK && nx >= sq3->n)
+						return nil;	/* corrupted file */
+					nx = sq3->tab[nx];
+					rp++;
+				}
+				if(rp > ep)
+					return nil;	/* corrupted file */
+				if(QFLAG){
+					QREMAP(nx);
+				}
+			}
+			*wp = nx;
+			wp++;
+			csum += nx;
+			nx = code & 0xF;
+		}while(++n == 1);
+	}
+	chksum = csum;
+	return wp;
+}
+
+static int
+rdtab(Block *b, Squeeze *sq, int shift)
+{
+	uchar *p, *ep;
+	ulong v, w;
+	int i;
+
+	if(BLEN(b) < 2)
+		return -1;
+	i = (b->rp[0]<<8) | b->rp[1];
+	if(1)
+		print(" T%d", i);
+	b->rp += 2;
+	if((i -= 2) > 0){
+		if(BLEN(b) < i)
+			return -1;
+	}
+	sq->n = 0;
+	p = b->rp;
+	ep = b->rp+i;
+	b->rp += i;
+	v = 0;
+	while(p < ep){
+		w = 0;
+		do{
+			if(p >= ep)
+				return -1;
+			w = (w<<7) | (*p & 0x7F);
+		}while(*p++ & 0x80);
+		v += w;
+		if(0)
+			print("%d %8.8lux %8.8lux\n", sq->n, v, w);
+		sq->tab[sq->n++] = v<<shift;
+	}
+	return 0;
+}
--- /dev/null
+++ b/os/boot/rpcg/NOTICE
@@ -1,0 +1,3 @@
+Inferno® Copyright © 1996-1999 Lucent Technologies Inc.  All rights reserved.
+PowerPC support Copyright © 1995-1997 C H Forsyth (forsyth@caldo.demon.co.uk).  All rights reserved.
+MPC8xx Inferno PowerPC port Copyright © 1998-2003 Vita Nuova Holdings Limited.  All rights reserved.
--- /dev/null
+++ b/os/boot/rpcg/alarm.c
@@ -1,0 +1,123 @@
+#include	"u.h"
+#include	"lib.h"
+#include	"mem.h"
+#include	"dat.h"
+#include	"fns.h"
+#include	"io.h"
+#define	MAXALARM	10
+
+Alarm	alarmtab[MAXALARM];
+
+/*
+ * Insert new into list after where
+ */
+void
+insert(List **head, List *where, List *new)
+{
+	if(where == 0){
+		new->next = *head;
+		*head = new;
+	}else{
+		new->next = where->next;
+		where->next = new;
+	}
+		
+}
+
+/*
+ * Delete old from list.  where->next is known to be old.
+ */
+void
+delete(List **head, List *where, List *old)
+{
+	if(where == 0){
+		*head = old->next;
+		return;
+	}
+	where->next = old->next;
+}
+
+Alarm*
+newalarm(void)
+{
+	int i;
+	Alarm *a;
+
+	for(i=0,a=alarmtab; i < nelem(alarmtab); i++,a++)
+		if(a->busy==0 && a->f==0){
+			a->f = 0;
+			a->arg = 0;
+			a->busy = 1;
+			return a;
+		}
+	panic("newalarm");
+	return 0;	/* not reached */
+}
+
+Alarm*
+alarm(int ms, void (*f)(Alarm*), void *arg)
+{
+	Alarm *a, *w, *pw;
+	ulong s;
+
+	if(ms < 0)
+		ms = 0;
+	s = splhi();
+	a = newalarm();
+	a->dt = MS2TK(ms);
+	a->f = f;
+	a->arg = arg;
+	pw = 0;
+	for(w=m->alarm; w; pw=w, w=w->next){
+		if(w->dt <= a->dt){
+			a->dt -= w->dt;
+			continue;
+		}
+		w->dt -= a->dt;
+		break;
+	}
+	insert(&m->alarm, pw, a);
+	splx(s);
+	return a;
+}
+
+void
+cancel(Alarm *a)
+{
+	a->f = 0;
+}
+
+void
+alarminit(void)
+{
+}
+
+#define NA 10		/* alarms per clock tick */
+void
+checkalarms(void)
+{
+	int i, n, s;
+	Alarm *a;
+	void (*f)(Alarm*);
+	Alarm *alist[NA];
+
+	s = splhi();
+	a = m->alarm;
+	if(a){
+		for(n=0; a && a->dt<=0 && n<NA; n++){
+			alist[n] = a;
+			delete(&m->alarm, 0, a);
+			a = m->alarm;
+		}
+		if(a)
+			a->dt--;
+
+		for(i = 0; i < n; i++){
+			f = alist[i]->f;	/* avoid race with cancel */
+			if(f)
+				(*f)(alist[i]);
+			alist[i]->busy = 0;
+		}
+	}
+	splx(s);
+}
--- /dev/null
+++ b/os/boot/rpcg/all.h
@@ -1,0 +1,6 @@
+#include	"u.h"
+#include	"lib.h"
+#include	"dat.h"
+#include	"fns.h"
+#include	"mem.h"
+#include	"io.h"
--- /dev/null
+++ b/os/boot/rpcg/archrpcg.c
@@ -1,0 +1,279 @@
+#include	"u.h"
+#include	"lib.h"
+#include	"mem.h"
+#include	"dat.h"
+#include	"fns.h"
+#include	"io.h"
+
+#include	"archrpcg.h"
+#include "etherif.h"
+
+/*
+ * board-specific support for the RPCG RXLite
+ */
+
+enum {
+	SYSMHZ =	66,	/* target frequency */
+
+	/* sccr */
+	RTSEL = IBIT(8),	/* =0, select main oscillator (OSCM); =1, select external crystal (EXTCLK) */
+	RTDIV = IBIT(7),	/* =0, divide by 4; =1, divide by 512 */
+	CRQEN = IBIT(9),	/* =1, switch to high frequency when CPM active */
+	PRQEN = IBIT(10),	/* =1, switch to high frequency when interrupt pending */
+
+	/* plprcr */
+	CSRC = IBIT(21),	/* =0, clock is DFNH; =1, clock is DFNL */
+};
+
+static	char	flashsig[] = "RPXsignature=1.0\nNAME=qbrpcg\nSTART=FFC20100\nVERSION=1.1\n";
+static	char*	geteeprom(char*);
+
+/*
+ * called early in main.c, after machinit:
+ * using board and architecture specific registers, initialise
+ * 8xx registers that need it and complete initialisation of the Mach structure.
+ */
+void
+archinit(void)
+{
+	IMM *io;
+	int mf, t;
+	ulong v;
+
+	v = getimmr() & 0xFFFF;
+	switch(v>>8){
+	case 0x00:	t = 0x86000; break;
+	case 0x20:	t = 0x82300; break;
+	case 0x21:	t = 0x823a0; break;
+	default:	t = 0; break;
+	}
+	m->cputype = t;
+	m->bcsr = KADDR(BCSRMEM);
+	io = m->iomem;
+	m->clockgen = 8*MHz;
+	mf = (io->plprcr >> 20)+1;	/* use timing set by bootstrap */
+	m->cpuhz = m->clockgen*mf;
+	m->bcsr[0] = DisableColTest | DisableFullDplx | DisableUSB | HighSpdUSB | LedOff;	/* first write enables bcsr regs */
+return;
+	io->plprcrk = KEEP_ALIVE_KEY;
+	io->plprcr &= ~CSRC;	/* general system clock is DFNH */
+/*	io->mptpr = 0x0800;	/* memory prescaler = 8 for refresh */
+	/* use memory refresh time set by RPXLite monitor */
+	io->plprcrk = ~KEEP_ALIVE_KEY;
+}
+
+void
+cpuidprint(void)
+{
+	int t, v;
+
+	print("Inferno bootstrap\n");
+	print("PVR: ");
+	t = getpvr()>>16;
+	switch(t){
+	case 0x01:	print("MPC601"); break;
+	case 0x03:	print("MPC603"); break;
+	case 0x04:	print("MPC604"); break;
+	case 0x06:	print("MPC603e"); break;
+	case 0x07:	print("MPC603e-v7"); break;
+	case 0x50:	print("MPC8xx"); break;
+	default:	print("PowerPC version #%x", t); break;
+	}
+	print(", revision #%lux\n", getpvr()&0xffff);
+	print("IMMR: ");
+	v = getimmr() & 0xFFFF;
+	switch(v>>8){
+	case 0x00:	print("MPC860/821"); break;
+	case 0x20:	print("MPC823"); break;
+	case 0x21:	print("MPC823A"); break;
+	default:	print("Type #%lux", v>>8); break;
+	}
+	print(", mask #%lux\n", v&0xFF);
+	print("options: #%lux\n", archoptionsw());
+	print("bcsr: %8.8lux\n", m->bcsr[0]);
+	print("PLPRCR=%8.8lux SCCR=%8.8lux\n", m->iomem->plprcr, m->iomem->sccr);
+	print("%lud MHz system\n", m->cpuhz/MHz);
+	print("\n");
+//print("%s\n", geteeprom("EA"));
+print("BR0=%8.8lux OR0=%8.8lux\n", m->iomem->memc[0].base, m->iomem->memc[0].option);
+print("MPTPR=%8.8lux\n", m->iomem->mptpr);
+}
+
+static	char*	defplan9ini[2] = {
+	/* 860/821 */
+	"ether0=type=SCC port=1 ea=0010ec000051\r\n"
+	"vgasize=640x480x8\r\n"
+	"kernelpercent=40\r\n"
+	"console=0\r\nbaud=9600\r\n",
+
+	/* 823 */
+	"ether0=type=SCC port=2 ea=0010ec000051\r\n"
+	"vgasize=640x480x8\r\n"
+	"kernelpercent=40\r\n"
+	"console=0\r\nbaud=9600\r\n",
+};
+
+char *
+archconfig(void)
+{
+	print("Using default configuration\n");
+	return defplan9ini[MPCMODEL(m->cputype) == 0x823];
+}
+
+/*
+ * provide value for #r/switch (devrtc.c)
+ */
+int
+archoptionsw(void)
+{
+	return (m->bcsr[0]&DipSwitchMask)>>4;
+}
+
+/*
+ * invoked by clock.c:/^clockintr
+ */
+static void
+twinkle(void)
+{
+	if(m->ticks%MS2TK(1000) == 0)
+		m->bcsr[0] ^= LedOff;
+}
+
+void	(*archclocktick)(void) = twinkle;
+
+/*
+ * for flash.c:/^flashreset
+ * retrieve flash type, virtual base and length and return 0;
+ * return -1 on error (no flash)
+ */
+int
+archflashreset(char *type, void **addr, long *length)
+{
+	if((m->iomem->memc[BOOTCS].base & 1) == 0)
+		return -1;		/* shouldn't happen */
+	strcpy(type, "AMD29F0x0");
+	*addr = KADDR(FLASHMEM);
+	*length = 4*1024*1024;
+	return 0;
+}
+
+int
+archether(int ctlrno, Card *ether)
+{
+	char *ea;
+
+	if(ctlrno > 0)
+		return -1;
+	strcpy(ether->type, "SCC");
+	ether->port = 2;
+	ea = geteeprom("EA");
+	if(ea != nil)
+		parseether(ether->ea, ea);
+	return 1;
+}
+
+/*
+ * enable the clocks for the given SCC ether and reveal them to the caller.
+ * do anything else required to prepare the transceiver (eg, set full-duplex, reset loopback).
+ */
+int
+archetherenable(int cpmid, int *rcs, int *tcs)
+{
+	IMM *io;
+
+	switch(cpmid){
+	default:
+		/* no other SCCs are wired for ether on RPXLite*/
+		return -1;
+
+	case SCC2ID:
+		io = ioplock();
+		m->bcsr[0] |= EnableEnet;
+		io->papar |= SIBIT(6)|SIBIT(4);	/* enable CLK2 and CLK4 */
+		io->padir &= ~(SIBIT(6)|SIBIT(4));
+		*rcs = CLK4;
+		*tcs = CLK2;
+		iopunlock();
+		break;
+	}
+	return 0;
+}
+
+void
+archetherdisable(int id)
+{
+	USED(id);
+	m->bcsr[0] &= ~EnableEnet;
+}
+
+/*
+ * do anything extra required to enable the UART on the given CPM port
+ */
+void
+archenableuart(int id, int irda)
+{
+	USED(id, irda);
+}
+
+/*
+ * do anything extra required to disable the UART on the given CPM port
+ */
+void
+archdisableuart(int id)
+{
+	USED(id);
+}
+
+/*
+ * enable/disable the LCD panel's backlight
+ */
+void
+archbacklight(int on)
+{
+	USED(on);
+}
+
+static char*
+geteeprom(char *s)
+{
+	static int init;
+	static char res[64];
+	static uchar eeprom[257];
+	uchar *l, *p;
+	int i, j;
+
+	if(!init){
+		i2csetup();
+		if(i2crecv(0xa8|1|(0<<8), eeprom, 128) < 0 ||
+		   i2crecv(0xa8|1|(128<<8), eeprom+128, 128) < 0){
+			print("i2c failed\n");
+			return nil;
+		}
+		if(0){
+			print("eeprom:\n");
+			for(i=0; i<16; i++){for(j=0; j<16; j++)print(" %2.2ux[%c]", eeprom[i*16+j], eeprom[i*16+j]); print("\n");}
+		}
+		eeprom[256] = 0xFF;
+		init = 1;
+	}
+	for(l = eeprom; *l != 0xFF && *l != '\n';){
+		p = l;
+		while(*l != '\n' && *l != 0xFF && *l != '=')
+			l++;
+		if(*l == '='){
+			if(l-p == strlen(s) && strncmp(s, (char*)p, strlen(s)) == 0){
+				p = l+1;
+				while(*l != '\n' && *l != 0xFF)
+					l++;
+				memmove(res, p, l-p);
+				res[l-p] = 0;
+				return res;
+			}
+		}
+		while(*l != '\n' && *l != 0xFF)
+			l++;
+		if(*l == '\n')
+			l++;
+	}
+	return nil;
+}
--- /dev/null
+++ b/os/boot/rpcg/archrpcg.h
@@ -1,0 +1,48 @@
+/*
+ * values for RPXLite AW
+ */
+enum {
+	/* CS assignment */
+	BOOTCS = 0,
+	DRAM1 = 1,
+	/* CS2 is routed to expansion header */
+	BCSRCS = 3,
+	NVRAMCS = 4,
+	/* CS5 is routed to expansion header */
+	PCMCIA0CS = 6,	/* select even bytes */
+	PCMCIA1CS = 7,	/* select odd bytes */
+};
+
+/*
+ * BCSR bits (there are 4 8-bit registers that we access as ulong)
+ */
+enum {
+	EnableEnet =	IBIT(0),
+	EnableXcrLB=	IBIT(1),
+	DisableColTest=	IBIT(2),
+	DisableFullDplx=IBIT(3),
+	LedOff=		IBIT(4),
+	DisableUSB=	IBIT(5),
+	HighSpdUSB=	IBIT(6),
+	EnableUSBPwr=	IBIT(7),
+	/* 8,9,10 unused */
+	PCCVCCMask=	IBIT(12)|IBIT(13),
+	PCCVPPMask=	IBIT(14)|IBIT(15),
+	PCCVCC0V=	0,
+	PCCVCC5V=	IBIT(13),
+	PCCVCC3V=	IBIT(12),
+	PCCVPP0V=	0,
+	PCCVPP5V=	IBIT(14),
+	PCCVPP12V=	IBIT(15),
+	PCCVPPHiZ=	IBIT(14)|IBIT(15),
+	/* 16-23 NYI */
+	DipSwitchMask=	IBIT(24)|IBIT(25)|IBIT(26)|IBIT(27),
+	DipSwitch0=	IBIT(24),
+	DipSwitch1=	IBIT(25),
+	DipSwitch2=	IBIT(26),
+	DipSwitch3=	IBIT(27),
+	/* bit 28 RESERVED */
+	FlashComplete=	IBIT(29),
+	NVRAMBattGood=	IBIT(30),
+	RTCBattGood=	IBIT(31),
+};
--- /dev/null
+++ b/os/boot/rpcg/boot.h
@@ -1,0 +1,8 @@
+#include <u.h>
+#include "lib.h"
+
+#include "mem.h"
+#include "dat.h"
+#include "fns.h"
+#include "io.h"
+#include "ureg.h"
--- /dev/null
+++ b/os/boot/rpcg/bootp.c
@@ -1,0 +1,509 @@
+#include "u.h"
+#include "lib.h"
+#include "mem.h"
+#include "dat.h"
+#include "fns.h"
+#include "io.h"
+
+#include "ip.h"
+
+#define	XPADDR(a)	((ulong)(a) & ~KSEGM)
+
+enum {
+	CHECKSUM = 1,	/* set zero if trouble booting from Linux */
+};
+
+uchar broadcast[Eaddrlen] = {
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+};
+
+static ushort tftpport = 5000;
+static int Id = 1;
+static Netaddr myaddr;
+static Netaddr server;
+
+typedef struct {
+	uchar	header[4];
+	uchar	data[Segsize];
+} Tftp;
+static Tftp tftpb;
+
+static void
+hnputs(uchar *ptr, ushort val)
+{
+	ptr[0] = val>>8;
+	ptr[1] = val;
+}
+
+static void
+hnputl(uchar *ptr, ulong val)
+{
+	ptr[0] = val>>24;
+	ptr[1] = val>>16;
+	ptr[2] = val>>8;
+	ptr[3] = val;
+}
+
+static ulong
+nhgetl(uchar *ptr)
+{
+	return ((ptr[0]<<24) | (ptr[1]<<16) | (ptr[2]<<8) | ptr[3]);
+}
+
+static ushort
+nhgets(uchar *ptr)
+{
+	return ((ptr[0]<<8) | ptr[1]);
+}
+
+static	short	endian	= 1;
+static	char*	aendian	= (char*)&endian;
+#define	LITTLE	*aendian
+
+static ushort
+ptcl_csum(void *a, int len)
+{
+	uchar *addr;
+	ulong t1, t2;
+	ulong losum, hisum, mdsum, x;
+
+	addr = a;
+	losum = 0;
+	hisum = 0;
+	mdsum = 0;
+
+	x = 0;
+	if((ulong)addr & 1) {
+		if(len) {
+			hisum += addr[0];
+			len--;
+			addr++;
+		}
+		x = 1;
+	}
+	while(len >= 16) {
+		t1 = *(ushort*)(addr+0);
+		t2 = *(ushort*)(addr+2);	mdsum += t1;
+		t1 = *(ushort*)(addr+4);	mdsum += t2;
+		t2 = *(ushort*)(addr+6);	mdsum += t1;
+		t1 = *(ushort*)(addr+8);	mdsum += t2;
+		t2 = *(ushort*)(addr+10);	mdsum += t1;
+		t1 = *(ushort*)(addr+12);	mdsum += t2;
+		t2 = *(ushort*)(addr+14);	mdsum += t1;
+		mdsum += t2;
+		len -= 16;
+		addr += 16;
+	}
+	while(len >= 2) {
+		mdsum += *(ushort*)addr;
+		len -= 2;
+		addr += 2;
+	}
+	if(x) {
+		if(len)
+			losum += addr[0];
+		if(LITTLE)
+			losum += mdsum;
+		else
+			hisum += mdsum;
+	} else {
+		if(len)
+			hisum += addr[0];
+		if(LITTLE)
+			hisum += mdsum;
+		else
+			losum += mdsum;
+	}
+
+	losum += hisum >> 8;
+	losum += (hisum & 0xff) << 8;
+	while(hisum = losum>>16)
+		losum = hisum + (losum & 0xffff);
+
+	return ~losum;
+}
+
+static ushort
+ip_csum(uchar *addr)
+{
+	int len;
+	ulong sum = 0;
+
+	len = (addr[0]&0xf)<<2;
+
+	while(len > 0) {
+		sum += addr[0]<<8 | addr[1] ;
+		len -= 2;
+		addr += 2;
+	}
+
+	sum = (sum & 0xffff) + (sum >> 16);
+	sum = (sum & 0xffff) + (sum >> 16);
+	return (sum^0xffff);
+}
+
+static void
+udpsend(int ctlrno, Netaddr *a, void *data, int dlen)
+{
+	Udphdr *uh;
+	Etherhdr *ip;
+	static Etherpkt pkt;
+	int len, ptcllen;
+
+
+	uh = (Udphdr*)&pkt;
+
+	memset(uh, 0, sizeof(Etherpkt));
+	memmove(uh->udpcksum+sizeof(uh->udpcksum), data, dlen);
+
+	/*
+	 * UDP portion
+	 */
+	ptcllen = dlen + (UDP_HDRSIZE-UDP_PHDRSIZE);
+	uh->ttl = 0;
+	uh->udpproto = IP_UDPPROTO;
+	uh->frag[0] = 0;
+	uh->frag[1] = 0;
+	hnputs(uh->udpplen, ptcllen);
+	hnputl(uh->udpsrc, myaddr.ip);
+	hnputs(uh->udpsport, myaddr.port);
+	hnputl(uh->udpdst, a->ip);
+	hnputs(uh->udpdport, a->port);
+	hnputs(uh->udplen, ptcllen);
+	uh->udpcksum[0] = 0;
+	uh->udpcksum[1] = 0;
+	/*dlen = (dlen+1)&~1; */
+	hnputs(uh->udpcksum, ptcl_csum(&uh->ttl, dlen+UDP_HDRSIZE));
+
+	/*
+	 * IP portion
+	 */
+	ip = (Etherhdr*)&pkt;
+	len = sizeof(Udphdr)+dlen;
+	ip->vihl = IP_VER|IP_HLEN;
+	ip->tos = 0;
+	ip->ttl = 255;
+	hnputs(ip->length, len-ETHER_HDR);
+	hnputs(ip->id, Id++);
+	ip->frag[0] = 0;
+	ip->frag[1] = 0;
+	ip->cksum[0] = 0;
+	ip->cksum[1] = 0;
+	hnputs(ip->cksum, ip_csum(&ip->vihl));
+
+	/*
+	 * Ethernet MAC portion
+	 */
+	hnputs(ip->type, ET_IP);
+	memmove(ip->d, a->ea, sizeof(ip->d));
+
+	ethertxpkt(ctlrno, &pkt, len, Timeout);
+}
+
+static void
+nak(int ctlrno, Netaddr *a, int code, char *msg, int report)
+{
+	int n;
+	char buf[128];
+
+	buf[0] = 0;
+	buf[1] = Tftp_ERROR;
+	buf[2] = 0;
+	buf[3] = code;
+	strcpy(buf+4, msg);
+	n = strlen(msg) + 4 + 1;
+	udpsend(ctlrno, a, buf, n);
+	if(report)
+		print("\ntftp: error(%d): %s\n", code, msg);
+}
+
+static int
+udprecv(int ctlrno, Netaddr *a, void *data, int dlen)
+{
+	int n, len;
+	ushort csm;
+	Udphdr *h;
+	ulong addr, timo;
+	static Etherpkt pkt;
+	static int rxactive;
+
+	if(rxactive == 0)
+		timo = 1000;
+	else
+		timo = Timeout;
+	timo += TK2MS(m->ticks);
+	while(timo > TK2MS(m->ticks)){
+		n = etherrxpkt(ctlrno, &pkt, timo-TK2MS(m->ticks));
+		if(n <= 0)
+			continue;
+
+		h = (Udphdr*)&pkt;
+		if(nhgets(h->type) != ET_IP)
+			continue;
+
+		if(ip_csum(&h->vihl)) {
+			print("ip chksum error\n");
+			continue;
+		}
+		if(h->vihl != (IP_VER|IP_HLEN)) {
+			print("ip bad vers/hlen\n");
+			continue;
+		}
+
+		if(h->udpproto != IP_UDPPROTO)
+			continue;
+
+		h->ttl = 0;
+		len = nhgets(h->udplen);
+		hnputs(h->udpplen, len);
+
+		if(CHECKSUM && nhgets(h->udpcksum)) {
+			csm = ptcl_csum(&h->ttl, len+UDP_PHDRSIZE);
+			if(csm != 0) {
+				print("udp chksum error csum #%4lux len %d\n", csm, n);
+				break;
+			}
+		}
+
+		if(a->port != 0 && nhgets(h->udpsport) != a->port)
+			continue;
+
+		addr = nhgetl(h->udpsrc);
+		if(a->ip != Bcastip && addr != a->ip)
+			continue;
+
+		len -= UDP_HDRSIZE-UDP_PHDRSIZE;
+		if(len > dlen) {
+			print("udp: packet too big\n");
+			continue;
+		}
+
+		memmove(data, h->udpcksum+sizeof(h->udpcksum), len);
+		a->ip = addr;
+		a->port = nhgets(h->udpsport);
+		memmove(a->ea, pkt.s, sizeof(a->ea));
+
+		rxactive = 1;
+		return len;
+	}
+
+	return 0;
+}
+
+static int tftpblockno;
+
+static int
+tftpopen(int ctlrno, Netaddr *a, char *name, Tftp *tftp)
+{
+	int i, len, rlen, oport;
+	char buf[Segsize+2];
+
+	buf[0] = 0;
+	buf[1] = Tftp_READ;
+	len = sprint(buf+2, "%s", name) + 2;
+	len += sprint(buf+len+1, "octet") + 2;
+
+	oport = a->port;
+	for(i = 0; i < 5; i++){
+		a->port = oport;
+		udpsend(ctlrno, a, buf, len);
+		a->port = 0;
+		if((rlen = udprecv(ctlrno, a, tftp, sizeof(Tftp))) < sizeof(tftp->header))
+			continue;
+
+		switch((tftp->header[0]<<8)|tftp->header[1]){
+
+		case Tftp_ERROR:
+			print("tftpopen: error (%d): %s\n",
+				(tftp->header[2]<<8)|tftp->header[3], tftp->data);
+			return -1;
+
+		case Tftp_DATA:
+			tftpblockno = 1;
+			len = (tftp->header[2]<<8)|tftp->header[3];
+			if(len != tftpblockno){
+				print("tftpopen: block error: %d\n", len);
+				nak(ctlrno, a, 1, "block error", 0);
+				return -1;
+			}
+			return rlen-sizeof(tftp->header);
+		}
+	}
+
+	print("tftpopen: failed to connect to server\n");
+	return -1;
+}
+
+static int
+tftpread(int ctlrno, Netaddr *a, Tftp *tftp, int dlen)
+{
+	int blockno, len, retry;
+	uchar buf[4];
+
+	buf[0] = 0;
+	buf[1] = Tftp_ACK;
+	buf[2] = tftpblockno>>8;
+	buf[3] = tftpblockno;
+	tftpblockno++;
+
+	dlen += sizeof(tftp->header);
+
+	retry = 0;
+buggery:
+	udpsend(ctlrno, a, buf, sizeof(buf));
+
+	if((len = udprecv(ctlrno, a, tftp, dlen)) < dlen){
+		print("tftpread: %d != %d\n", len, dlen);
+		nak(ctlrno, a, 2, "short read", 0);
+		if(retry++ < 5)
+			goto buggery;
+		return -1;
+	}
+
+	blockno = (tftp->header[2]<<8)|tftp->header[3];
+	if(blockno != tftpblockno){
+		print("?");
+
+		if(blockno == tftpblockno-1 && retry++ < 8)
+			goto buggery;
+		print("tftpread: block error: %d, expected %d\n", blockno, tftpblockno);
+		nak(ctlrno, a, 1, "block error", 0);
+
+		return -1;
+	}
+
+	return len-sizeof(tftp->header);
+}
+
+int
+bootp(int ctlrno, char *file)
+{
+	Bootp req, rep;
+	int i, dlen, segsize, text, data, bss, total;
+	uchar *ea, *addr, *p;
+	ulong entry;
+	Exec *exec;
+	char name[128], *filename, *sysname;
+
+	if((ea = etheraddr(ctlrno)) == 0){
+		print("invalid ctlrno %d\n", ctlrno);
+		return -1;
+	}
+
+	filename = 0;
+	sysname = 0;
+	if(file && *file){
+		strcpy(name, file);
+		if(filename = strchr(name, ':')){
+			if(filename != name && *(filename-1) != '\\'){
+				sysname = name;
+				*filename++ = 0;
+			}
+		}
+		else
+			filename = name;
+	}
+		
+
+	memset(&req, 0, sizeof(req));
+	req.op = Bootrequest;
+	req.htype = 1;			/* ethernet */
+	req.hlen = Eaddrlen;		/* ethernet */
+	memmove(req.chaddr, ea, Eaddrlen);
+
+	myaddr.ip = 0;
+	myaddr.port = BPportsrc;
+	memmove(myaddr.ea, ea, Eaddrlen);
+
+	for(i = 0; i < 10; i++) {
+		server.ip = Bcastip;
+		server.port = BPportdst;
+		memmove(server.ea, broadcast, sizeof(server.ea));
+		udpsend(ctlrno, &server, &req, sizeof(req));
+		if(udprecv(ctlrno, &server, &rep, sizeof(rep)) <= 0)
+			continue;
+		if(memcmp(req.chaddr, rep.chaddr, Eaddrlen))
+			continue;
+		if(rep.htype != 1 || rep.hlen != Eaddrlen)
+			continue;
+		if(sysname == 0 || strcmp(sysname, rep.sname) == 0)
+			break;
+	}
+	if(i >= 10) {
+		print("bootp timed out\n");
+		return -1;
+	}
+
+	if(filename == 0 || *filename == 0)
+		filename = rep.file;
+
+	if(rep.sname[0] != '\0')
+		 print("%s ", rep.sname);
+	print("(%d.%d.%d.%d!%d): %s\n",
+		rep.siaddr[0],
+		rep.siaddr[1],
+		rep.siaddr[2],
+		rep.siaddr[3],
+		server.port,
+		filename);uartwait();
+
+	myaddr.ip = nhgetl(rep.yiaddr);
+	myaddr.port = tftpport++;
+	server.ip = nhgetl(rep.siaddr);
+	server.port = TFTPport;
+
+	if((dlen = tftpopen(ctlrno, &server, filename, &tftpb)) < 0)
+		return -1;
+	exec = (Exec*)(tftpb.data);
+	if(dlen < sizeof(Exec) || GLLONG(exec->magic) != Q_MAGIC){
+		nak(ctlrno, &server, 0, "bad magic number", 1);
+		return -1;
+	}
+	text = GLLONG(exec->text);
+	data = GLLONG(exec->data);
+	bss = GLLONG(exec->bss);
+	total = text+data+bss;
+	entry = GLLONG(exec->entry);
+print("load@%8.8lux: ", XPADDR(entry));uartwait();
+	print("%d", text);
+
+	addr = (uchar*)XPADDR(entry);
+	p = tftpb.data+sizeof(Exec);
+	dlen -= sizeof(Exec);
+	segsize = text;
+	for(;;){
+		if(dlen == 0){
+			if((dlen = tftpread(ctlrno, &server, &tftpb, sizeof(tftpb.data))) < 0)
+				return -1;
+			p = tftpb.data;
+		}
+		if(segsize <= dlen)
+			i = segsize;
+		else
+			i = dlen;
+		memmove(addr, p, i);
+
+		addr += i;
+		p += i;
+		segsize -= i;
+		dlen -= i;
+
+		if(segsize <= 0){
+			if(data == 0)
+				break;
+			print("+%d", data);
+			segsize = data;
+			data = 0;
+			addr = (uchar*)PGROUND((ulong)addr);
+		}
+	}
+	nak(ctlrno, &server, 3, "ok", 0);		/* tftpclose */
+	print("+%d=%d\n", bss, total);
+	print("entry: 0x%lux\n", entry);
+	uartwait();
+	scc2stop();
+	splhi();
+	(*(void(*)(void))(XPADDR(entry)))();
+	
+	return 0;
+}
--- /dev/null
+++ b/os/boot/rpcg/clock.c
@@ -1,0 +1,71 @@
+#include	"u.h"
+#include	"lib.h"
+#include	"mem.h"
+#include	"dat.h"
+#include	"fns.h"
+#include	"io.h"
+#include	"ureg.h"
+
+enum {
+	Timebase = 4,	/* system clock cycles per time base cycle */
+};
+
+void	(*archclocktick)(void);	/* set by arch*.c when desired */
+
+static	ulong	clkreload;
+
+void
+delay(int l)
+{
+	ulong i, j;
+
+	j = m->delayloop;
+	while(l-- > 0)
+		for(i=0; i < j; i++)
+			;
+}
+
+void
+microdelay(int l)
+{
+	ulong i;
+
+	l *= m->delayloop;
+	l /= 1000;
+	if(l <= 0)
+		l = 1;
+	for(i = 0; i < l; i++)
+		;
+}
+
+void
+clockintr(Ureg*, void*)
+{
+	putdec(clkreload);
+	m->ticks++;
+	checkalarms();
+	if(archclocktick != nil)
+		archclocktick();
+}
+
+void
+clockinit(void)
+{
+	long x;
+
+	m->delayloop = m->cpuhz/1000;	/* initial estimate */
+	do {
+		x = gettbl();
+		delay(10);
+		x = gettbl() - x;
+	} while(x < 0);
+
+	/*
+	 *  fix count
+	 */
+	m->delayloop = ((vlong)m->delayloop*(10*m->clockgen/1000))/(x*Timebase);
+	if(m->delayloop == 0)
+		m->delayloop = 1;
+	clkreload = (m->clockgen/Timebase)/HZ-1;
+	putdec(clkreload);
+}
--- /dev/null
+++ b/os/boot/rpcg/conf.c
@@ -1,0 +1,173 @@
+#include "u.h"
+#include "lib.h"
+#include "mem.h"
+#include "dat.h"
+#include "fns.h"
+#include "io.h"
+
+#include "dosfs.h"
+
+static char *confname[MAXCONF];
+static char *confval[MAXCONF];
+static int nconf;
+
+extern char **ini;
+
+char*
+getconf(char *name)
+{
+	int i;
+
+	for(i = 0; i < nconf; i++)
+		if(strcmp(confname[i], name) == 0)
+			return confval[i];
+	return 0;
+}
+
+/*
+ *  read configuration file
+ */
+int
+plan9ini(Dos *dos, char *val)
+{
+	Dosfile rc;
+	int i, n;
+	char *cp, *p, *q, *line[MAXCONF];
+
+	cp = BOOTARGS;
+	if(dos) {
+		if(dosstat(dos, *ini, &rc) <= 0)
+			return -1;
+
+		*cp = 0;
+		n = dosread(&rc, cp, BOOTARGSLEN-1);
+		if(n <= 0)
+			return -1;
+		cp[n] = 0;
+	} else if(val != nil){
+		if(memchr(val, 0, BOOTARGSLEN-1) == nil)
+			return -1;
+		print("Using flash configuration\n");
+		strcpy(cp, val);
+		n = strlen(cp);
+	}else{
+		strcpy(cp, archconfig());
+		n = strlen(cp);
+	}
+
+	/*
+	 * Make a working copy.
+	 * We could change this to pass the parsed strings
+	 * to the booted programme instead of the raw
+	 * string, then it only gets done once.
+	 */
+	memmove(cp+BOOTARGSLEN, cp, n+1);
+	cp += BOOTARGSLEN;
+
+	/*
+	 * Strip out '\r', change '\t' -> ' '.
+	 */
+	p = cp;
+	for(q = cp; *q; q++){
+		if(*q == '\r')
+			continue;
+		if(*q == '\t')
+			*q = ' ';
+		*p++ = *q;
+	}
+	*p = 0;
+	n = getcfields(cp, line, MAXCONF, "\n");
+	for(i = 0; i < n; i++){
+		cp = strchr(line[i], '=');
+		if(cp == 0)
+			continue;
+		*cp++ = 0;
+		if(cp - line[i] >= NAMELEN+1)
+			*(line[i]+NAMELEN-1) = 0;
+		confname[nconf] = line[i];
+		confval[nconf] = cp;
+		nconf++;
+	}
+	return 0;
+}
+
+int
+parseether(uchar *to, char *from)
+{
+	char nip[4];
+	char *p;
+	int i;
+
+	p = from;
+	while(*p == ' ')
+		++p;
+	for(i = 0; i < 6; i++){
+		if(*p == 0)
+			return -1;
+		nip[0] = *p++;
+		if(*p == 0)
+			return -1;
+		nip[1] = *p++;
+		nip[2] = 0;
+		to[i] = strtoul(nip, 0, 16);
+		if(*p == ':')
+			p++;
+	}
+	return 0;
+}
+
+int
+isaconfig(char *class, int ctlrno, ISAConf *isa)
+{
+	char cc[NAMELEN], *p, *q, *r;
+	int n;
+
+	sprint(cc, "%s%d", class, ctlrno);
+	for(n = 0; n < nconf; n++){
+		if(strncmp(confname[n], cc, NAMELEN))
+			continue;
+		isa->nopt = 0;
+		p = confval[n];
+		while(*p){
+			while(*p == ' ' || *p == '\t')
+				p++;
+			if(*p == '\0')
+				break;
+			if(strncmp(p, "type=", 5) == 0){
+				p += 5;
+				for(q = isa->type; q < &isa->type[NAMELEN-1]; q++){
+					if(*p == '\0' || *p == ' ' || *p == '\t')
+						break;
+					*q = *p++;
+				}
+				*q = '\0';
+			}
+			else if(strncmp(p, "port=", 5) == 0)
+				isa->port = strtoul(p+5, &p, 0);
+			else if(strncmp(p, "irq=", 4) == 0)
+				isa->irq = strtoul(p+4, &p, 0);
+			else if(strncmp(p, "mem=", 4) == 0)
+				isa->mem = strtoul(p+4, &p, 0);
+			else if(strncmp(p, "size=", 5) == 0)
+				isa->size = strtoul(p+5, &p, 0);
+			else if(strncmp(p, "ea=", 3) == 0){
+				if(parseether(isa->ea, p+3) == -1)
+					memset(isa->ea, 0, 6);
+			}
+			else if(isa->nopt < NISAOPT){
+				r = isa->opt[isa->nopt];
+				while(*p && *p != ' ' && *p != '\t'){
+					*r++ = *p++;
+					if(r-isa->opt[isa->nopt] >= ISAOPTLEN-1)
+						break;
+				}
+				*r = '\0';
+				isa->nopt++;
+			}
+			while(*p && *p != ' ' && *p != '\t')
+				p++;
+		}
+		return 1;
+	}
+	return 0;
+}
--- /dev/null
+++ b/os/boot/rpcg/console.c
@@ -1,0 +1,173 @@
+#include "u.h"
+#include "lib.h"
+#include "mem.h"
+#include "dat.h"
+#include "fns.h"
+#include "io.h"
+
+static Queue*	consiq;
+static Queue*	consoq;
+
+void
+bothputs(char *s, int n)
+{
+	uartputs(s, n);
+	screenputs(s, n);
+}
+
+static void (*consputs)(char*, int) = bothputs;	/* or screenputs */
+
+void
+consinit(void)
+{
+	char *p;
+	int baud, port;
+	static int cgadone;
+
+	p = getconf("console");
+	if(0)
+	if(p == 0 || strcmp(p, "lcd") == 0 || strcmp(p, "screen") == 0){
+		consiq = qopen(4*1024, 0, 0, 0);
+		consoq = qopen(8*1024, 0, 0, 0);
+		consputs = screenputs;
+		return;
+	}
+	if(p!=0 && strstr(p, "lcd") == 0)
+		consputs = bothputs;
+	else
+		consputs = uartputs;
+//consputs = screenputs;
+	port = 0;
+	if(p)
+		port = strtoul(p, 0, 0);
+	baud = 0;
+	if(p = getconf("baud"))
+		baud = strtoul(p, 0, 0);
+	if(baud == 0)
+		baud = 9600;
+	uartspecial(port, baud, &consiq, &consoq, kbdchar);
+}
+
+void
+kbdchar(Queue *q, int c)
+{
+	c &= 0x7F;
+	if(c == 0x10)
+		panic("^p");
+	qbputc(q, c);
+}
+
+static int
+getline(char *buf, int size, int dotimeout)
+{
+	int c, i=0;
+	ulong start;
+	char echo;
+
+	for (;;) {
+		start = m->ticks;
+		do{
+			if(dotimeout && ((m->ticks - start) > 5*HZ))
+				return -2;
+			c = qbgetc(consiq);
+		}while(c == -1);
+		if(c == '\r')
+			c = '\n'; 		/* turn carriage return into newline */
+		if(c == '\177')
+			c = '\010';		/* turn delete into backspace */
+		if(c == '\025')
+			echo = '\n';		/* echo ^U as a newline */
+		else
+			echo = c;
+		(*consputs)(&echo, 1);
+
+		if(c == '\010'){
+			if(i > 0)
+				i--; /* bs deletes last character */
+			continue;
+		}
+		/* a newline ends a line */
+		if (c == '\n')
+			break;
+		/* ^U wipes out the line */
+		if (c =='\025')
+			return -1;
+		if(i == size)
+			return size;
+		buf[i++] = c;
+	}
+	buf[i] = 0;
+	return i;
+}
+
+int
+getstr(char *prompt, char *buf, int size, char *def)
+{
+	int len, isdefault;
+
+	buf[0] = 0;
+	isdefault = (def && *def);
+	for (;;) {
+		if(isdefault)
+			print("%s[default==%s]: ", prompt, def);
+		else
+			print("%s: ", prompt);
+		len = getline(buf, size, isdefault);
+		switch(len){
+		case -1:
+			/* ^U typed */
+			continue;
+		case -2:
+			/* timeout, use default */
+			(*consputs)("\n", 1);
+			len = 0;
+			break;
+		default:
+			break;
+		}
+		if(len >= size){
+			print("line too long\n");
+			continue;
+		}
+		break;
+	}
+	if(len == 0 && isdefault)
+		strcpy(buf, def);
+	return 0;
+}
+
+int
+sprint(char *s, char *fmt, ...)
+{
+	return donprint(s, s+PRINTSIZE, fmt, (&fmt+1)) - s;
+}
+
+int
+print(char *fmt, ...)
+{
+	char buf[PRINTSIZE];
+	int n;
+
+	if(consputs == 0)
+		return 0;
+	n = donprint(buf, buf+sizeof(buf), fmt, (&fmt+1)) - buf;
+	(*consputs)(buf, n);
+	return n;
+}
+
+void
+panic(char *fmt, ...)
+{
+	char buf[PRINTSIZE];
+	int n;
+
+	if(consputs){
+		(*consputs)("panic: ", 7);
+		n = donprint(buf, buf+sizeof(buf), fmt, (&fmt+1)) - buf;
+		(*consputs)(buf, n);
+		(*consputs)("\n", 1);
+	}
+	spllo();
+	for(;;)
+		idle();
+}
--- /dev/null
+++ b/os/boot/rpcg/cpm.c
@@ -1,0 +1,162 @@
+#include "u.h"
+#include "lib.h"
+#include "mem.h"
+#include "dat.h"
+#include "fns.h"
+#include "io.h"
+
+enum {
+	BDSIZE=	1024,	/* TO DO: check this */
+};
+
+static	Map	bdmapv[BDSIZE/sizeof(BD)];
+static	RMap	bdmap = {"buffer descriptors"};
+
+void
+cpminit(void)
+{
+	IMM *io;
+
+	io = m->iomem;
+	io->sdcr = 1;
+	io->lccr &= ~1;	/* disable LCD */
+	io->pcint = 0;	/* disable all port C interrupts */
+	io->pcso = 0;
+	io->pcdir =0;
+	io->pcpar = 0;
+	io->pcdat = 0;
+	io->papar = 0;
+	io->padir = 0;
+	io->paodr = 0;
+	io->padat = 0;
+	io->pbpar = 0;
+	io->pbdir = 0;
+	io->pbodr = 0;
+	io->pbdat = 0;
+	eieio();
+
+	for(io->cpcr = 0x8001; io->cpcr & 1;)	/* reset all CPM channels */
+		eieio();
+
+	mapinit(&bdmap, bdmapv, sizeof(bdmapv));
+	mapfree(&bdmap, DPBASE, BDSIZE);
+}
+
+void
+cpmop(int op, int cno, int param)
+{
+	IMM *io;
+	int s;
+
+	s = splhi();
+	io = m->iomem;
+	eieio();
+	while(io->cpcr & 1)
+		eieio();
+	io->cpcr = (op<<8)|(cno<<4)|(param<<1)|1;
+	eieio();
+	while(io->cpcr & 1)
+		eieio();
+	splx(s);
+}
+
+/*
+ * connect SCCx clocks in NSMI mode (x=1 for USB)
+ */
+void
+sccnmsi(int x, int rcs, int tcs)
+{
+	IMM *io;
+	ulong v;
+	int sh;
+
+	sh = (x-1)*8;	/* each SCCx field in sicr is 8 bits */
+	v = (((rcs&7)<<3) | (tcs&7)) << sh;
+	io = ioplock();
+	io->sicr = (io->sicr & ~(0xFF<<sh)) | v;
+	iopunlock();
+}
+
+void
+scc2stop(void)
+{
+	SCC *scc;
+
+	scc = IOREGS(0xA20, SCC);
+	if(scc->gsmrl & (3<<4)){
+		cpmop(GracefulStopTx, SCC2ID, 0);
+		cpmop(CloseRxBD, SCC2ID, 0);
+		delay(1);
+		scc->gsmrl &= ~(3<<4);	/* disable current use */
+		archetherdisable(SCC2ID);
+	}
+}
+
+BD *
+bdalloc(int n)
+{
+	ulong a;
+
+	a = mapalloc(&bdmap, 0, n*sizeof(BD), 0);
+	if(a == 0)
+		panic("bdalloc");
+	return KADDR(a);
+}
+
+void
+bdfree(BD *b, int n)
+{
+	if(b){
+		eieio();
+		mapfree(&bdmap, PADDR(b), n*sizeof(BD));
+	}
+}
+
+/*
+ * initialise receive and transmit buffer rings.
+ */
+int
+ioringinit(Ring* r, int nrdre, int ntdre, int bufsize)
+{
+	int i, x;
+
+	/* the ring entries must be aligned on sizeof(BD) boundaries */
+	r->nrdre = nrdre;
+	if(r->rdr == nil)
+		r->rdr = bdalloc(nrdre);
+	/* the buffer size must align with cache lines since the cache doesn't snoop */
+	bufsize = (bufsize+CACHELINESZ-1)&~(CACHELINESZ-1);
+	if(r->rrb == nil)
+		r->rrb = malloc(nrdre*bufsize);
+	if(r->rdr == nil || r->rrb == nil)
+		return -1;
+	dcflush(r->rrb, nrdre*bufsize);
+	x = PADDR(r->rrb);
+	for(i = 0; i < nrdre; i++){
+		r->rdr[i].length = 0;
+		r->rdr[i].addr = x;
+		r->rdr[i].status = BDEmpty|BDInt;
+		x += bufsize;
+	}
+	r->rdr[i-1].status |= BDWrap;
+	r->rdrx = 0;
+
+	r->ntdre = ntdre;
+	if(r->tdr == nil)
+		r->tdr = bdalloc(ntdre);
+	if(r->txb == nil)
+		r->txb = malloc(ntdre*sizeof(Block*));
+	if(r->tdr == nil || r->txb == nil)
+		return -1;
+	for(i = 0; i < ntdre; i++){
+		r->txb[i] = nil;
+		r->tdr[i].addr = 0;
+		r->tdr[i].length = 0;
+		r->tdr[i].status = 0;
+	}
+	r->tdr[i-1].status |= BDWrap;
+	r->tdrh = 0;
+	r->tdri = 0;
+	r->ntq = 0;
+	return 0;
+}
--- /dev/null
+++ b/os/boot/rpcg/crc32.c
@@ -1,0 +1,42 @@
+#include "boot.h"
+
+/*
+ * from Rob Warnock
+ */
+static	ulong	crc32tab[256];	/* initialised on first call to crc32 */
+
+enum {
+	CRC32POLY = 0x04c11db7     /* AUTODIN II, Ethernet, & FDDI */
+};
+
+/*
+ * Build auxiliary table for parallel byte-at-a-time CRC-32.
+ */
+static void
+initcrc32(void)
+{
+	int i, j;
+	ulong c;
+
+	for(i = 0; i < 256; i++) {
+		for(c = i << 24, j = 8; j > 0; j--)
+			if(c & (1<<31))
+				c = (c<<1) ^ CRC32POLY;
+			else
+				c <<= 1;
+		crc32tab[i] = c;
+	}
+}
+
+ulong
+crc32(void *buf, int n, ulong crc)
+{
+	uchar *p;
+
+	if(crc32tab[1] == 0)
+		initcrc32();
+	crc = ~crc;
+	for(p = buf; --n >= 0;)
+		crc = (crc << 8) ^ crc32tab[(crc >> 24) ^ *p++];
+	return ~crc;
+}
--- /dev/null
+++ b/os/boot/rpcg/dat.h
@@ -1,0 +1,217 @@
+typedef struct Alarm Alarm;
+typedef struct Block Block;
+typedef struct IMM IMM;
+typedef struct Queue Queue;
+
+typedef struct List {
+	void	*next;
+} List;
+
+typedef struct {
+	int	fake;
+	int	pri;
+} Lock;
+#define	lock(x)
+#define	unlock(x)
+
+struct Alarm {
+	List;
+	int	busy;
+	long	dt;
+	void	(*f)(Alarm*);
+	void	*arg;
+};
+
+enum {
+	Eaddrlen	= 6,
+	ETHERMINTU	= 60,		/* minimum transmit size */
+	ETHERMAXTU	= 1514,		/* maximum transmit size */
+	ETHERHDRSIZE	= 14,		/* size of an ethernet header */
+
+	MaxEther	= 4,
+};
+
+typedef struct {
+	uchar	d[Eaddrlen];
+	uchar	s[Eaddrlen];
+	uchar	type[2];
+	uchar	data[1500];
+	uchar	crc[4];
+} Etherpkt;
+
+extern uchar broadcast[Eaddrlen];
+
+enum {
+	Npart		= 20+2,		/* 8 sub partitions, disk, and partition */
+	Maxxfer		= 16*1024,	/* maximum transfer size/cmd */
+};
+
+typedef struct {
+	ulong	start;
+	ulong	end;
+	char	name[NAMELEN+1];
+} Partition;
+
+typedef struct {
+	int	online;
+	int	npart;		/* number of real partitions */
+	Partition p[Npart];
+	ulong	offset;
+	Partition *current;	/* current partition */
+
+	ulong	cap;		/* total bytes */
+	int	bytes;		/* bytes/sector */
+	int	sectors;	/* sectors/track */
+	int	heads;		/* heads/cyl */
+	long	cyl;		/* cylinders/drive */
+
+	char	lba;		/* true if drive has logical block addressing */
+	char	multi;		/* non-zero if drive does multiple block xfers */
+} Disc;
+
+enum {
+	ScsiTestunit	= 0x00,
+	ScsiExtsens	= 0x03,
+	ScsiInquiry	= 0x12,
+	ScsiModesense	= 0x1a,
+	ScsiStartunit	= 0x1B,
+	ScsiStopunit	= 0x1B,
+	ScsiGetcap	= 0x25,
+	ScsiRead	= 0x08,
+	ScsiWrite	= 0x0a,
+	ScsiExtread	= 0x28,
+	ScsiExtwrite	= 0x2a,
+
+	/* data direction */
+	ScsiIn		= 1,
+	ScsiOut		= 0,
+};
+
+typedef struct Scsibuf Scsibuf;
+typedef struct Scsibuf {
+	void*		virt;
+	void*		phys;
+	Scsibuf*	next;
+};
+
+typedef struct Scsidata {
+	uchar*		base;
+	uchar*		lim;
+	uchar*		ptr;
+} Scsidata;
+
+typedef struct Ureg Ureg;
+
+typedef struct Scsi {
+	ulong		pid;
+	ushort		target;
+	ushort		lun;
+	ushort		rflag;
+	ushort		status;
+	Scsidata 	cmd;
+	Scsidata 	data;
+	Scsibuf*	b;
+	uchar*		save;
+	uchar		cmdblk[16];
+} Scsi;
+
+typedef struct Segdesc {
+	ulong	d0;
+	ulong	d1;
+} Segdesc;
+
+typedef struct Mach {
+	ulong	ticks;			/* of the clock since boot time */
+	ulong	delayloop;
+	long	cpuhz;	/* general system clock (cycles) */
+	long	clockgen;	/* clock generator frequency (cycles) */
+	ulong	cpupvr;	/* cpu type in processor version register */
+	ulong	cputype;	/* cpu variant in BCD (eg, 0x823xx) */
+	void*	alarm;			/* alarms bound to this clock */
+	ulong*	bcsr;
+	IMM*	iomem;
+} Mach;
+
+/* Mach.cputype */
+#define MPCREV(x)	((x) & 0xFF)
+#define MPCMODEL(x)	(((x)>>8) & 0xFFF)
+#define MPCFAMILY(x)	(((x)>>24) & 0x0F)
+
+
+extern Mach *m;
+
+#define Q_MAGIC		((((4*21)+0)*21)+7)
+
+typedef struct Exec Exec;
+struct	Exec
+{
+	uchar	magic[4];		/* magic number */
+	uchar	text[4];	 	/* size of text segment */
+	uchar	data[4];	 	/* size of initialized data */
+	uchar	bss[4];	  		/* size of uninitialized data */
+	uchar	syms[4];	 	/* size of symbol table */
+	uchar	entry[4];	 	/* entry point */
+	uchar	spsz[4];		/* size of sp/pc offset table */
+	uchar	pcsz[4];		/* size of pc/line number table */
+};
+
+/*
+ *  bootline passed by boot program
+ */
+#define BOOTLINE ((char *)0x200000-150)
+
+/*
+ * Where we leave configuration info.
+ */
+#define BOOTARGS	((char*)(0x200000))
+#define	BOOTARGSLEN	1024
+#define	MAXCONF		32
+
+/*
+ *  a parsed plan9.ini line
+ */
+#define ISAOPTLEN	16
+#define NISAOPT		8
+
+typedef struct  ISAConf {
+	char	type[NAMELEN];
+	ulong	port;
+	ulong	irq;
+	ulong	mem;
+	ulong	size;
+	uchar	ea[6];
+
+	int	nopt;
+	char	opt[NISAOPT][ISAOPTLEN];
+} ISAConf;
+
+typedef struct {
+	int	size;
+	ulong	addr;
+} Map;
+
+typedef struct {
+	char*	name;
+	Map*	map;
+	Map*	mapend;
+
+	Lock;
+} RMap;
+
+typedef struct PCIcfg PCIcfg;
+
+extern	uchar*	vgamem;
+
+struct Block {
+	uchar	*rp;
+	uchar	*wp;
+	uchar	*lim;
+	uchar	*data;
+	Block*	next;
+	ulong	magic;
+};
+#define	BLEN(b)	((b)->wp-(b)->rp)
+
+typedef struct QLock {
+	int	dummy;
+} QLock;
--- /dev/null
+++ b/os/boot/rpcg/defont0.c
@@ -1,0 +1,216 @@
+#include <u.h>
+#include <libc.h>
+#include <libg.h>
+#include <gnot.h>
+
+
+
+static ulong bits0[] = {
+ 0x907070f0,  0xf0f07000,  0xf0888888,  0xf8707070,  0xe0e0e0e0,  0xe09070f0,  0x70f870f0,  0xf870f088, 
+ 0x00000000,  0x00000000,  0x00000000,  0x00000000,  0x00000000,  0x00000000,  0x00000000,  0x00000000, 
+ 0x00000000,  0x00000000,  0x00000000,  0x00000000,  0x00000000,  0x00000000,  0x00000000,  0x00000000, 
+ 0x00000000,  0x00000000,  0x00000000,  0x00000000,  0x00000000,  0x00000000,  0x00000000,  0x000000e0, 
+ 0xd0808080,  0x80808800,  0x8888c888,  0x80888888,  0x90909090,  0x90d08080,  0x80808080,  0x80888888, 
+ 0x00000000,  0x08000000,  0x0c300000,  0x00000006,  0x00000000,  0x00000000,  0x00000000,  0x00000000, 
+ 0x00000000,  0x00000000,  0x00000000,  0x00000000,  0x00000000,  0x00000000,  0x0000003c,  0xc03c0000, 
+ 0x00006000,  0x06001e00,  0x60181860,  0x78000000,  0x00000000,  0x00000000,  0x0000001c,  0x18380090, 
+ 0xb06060e0,  0xe0e0f800,  0xf088a888,  0x80808080,  0x90909090,  0x90b060e0,  0x808060e0,  0x80808888, 
+ 0x00182428,  0x3e707018,  0x18180000,  0x00000006,  0x3c183c3c,  0x1c3e3c7e,  0x3c3c0000,  0x0200403c, 
+ 0x3c187c1e,  0x787e7e1e,  0x663c7c66,  0x6066623c,  0x7c3c7c3c,  0x7e6266c2,  0x66667e30,  0xc00c1000, 
+ 0x08006000,  0x06003000,  0x60181860,  0x18000000,  0x00000000,  0x10000000,  0x00000030,  0x180c0090, 
+ 0x90101080,  0x80808818,  0x88f8a888,  0xe0807070,  0xe0e0e0e0,  0xe0901080,  0x70e01080,  0xe098f088, 
+ 0x00182428,  0x6adad818,  0x18181000,  0x0000000c,  0x66386666,  0x2c3e667e,  0x66660000,  0x06006066, 
+ 0x42186632,  0x6c606032,  0x66181864,  0x60667224,  0x66246666,  0x186262da,  0x62620630,  0x600c3800, 
+ 0x10006000,  0x06003000,  0x60000060,  0x18000000,  0x00000000,  0x30000000,  0x00000030,  0x180c00e0, 
+ 0x00e0e0f0,  0xf0f00018,  0x88889850,  0x80880808,  0x201c1c1c,  0x1c00e0f0,  0x0080e0f0,  0x80888888, 
+ 0x00182428,  0x68dad808,  0x300c5418,  0x0000000c,  0x66580606,  0x2c206002,  0x66661818,  0x0cfe3006, 
+ 0x9e2c6660,  0x66606060,  0x6618186c,  0x60667266,  0x66666660,  0x186262da,  0x36660c30,  0x600c2800, 
+ 0x103c6c3c,  0x3e3c7e3e,  0x6c787866,  0x18d46c3c,  0x6c3e763c,  0x7e6666c2,  0x66667e18,  0x18180000, 
+ 0x44180000,  0x18241c24,  0xf0888820,  0x8070f0f0,  0x20202020,  0x201c243e,  0x1cf8241c,  0x80708870, 
+ 0x0018247c,  0x78745008,  0x300c3818,  0x00000018,  0x66180606,  0x4c206006,  0x76661818,  0x18fe180c, 
+ 0xb62c6660,  0x66606060,  0x66181868,  0x607e5a66,  0x66666470,  0x186266da,  0x34340c30,  0x300c6c00, 
+ 0x18667666,  0x66663066,  0x76181864,  0x18fe7666,  0x76663666,  0x306662da,  0x62620608,  0x1810323c, 
+ 0x44247c7c,  0x24342042,  0x00000000,  0x00000000,  0x20202020,  0x20222408,  0x22002420,  0x00000000, 
+ 0x00180028,  0x3c287610,  0x300cee7e,  0x00fe0018,  0x66180c18,  0x4c3c7c0c,  0x3c3e0000,  0x30000c18, 
+ 0xb62c7c60,  0x667c7c6e,  0x7e181878,  0x605a5a66,  0x6466783c,  0x186234da,  0x18341830,  0x300c4400, 
+ 0x18066660,  0x66663066,  0x66181868,  0x18d66666,  0x66663860,  0x306662da,  0x34620c30,  0x180c5a20, 
+ 0x44241010,  0x242c2042,  0x0e3e103e,  0x3e3c1c3e,  0x3c1c1c1c,  0x1c3e1c08,  0x3e222418,  0x0e0e0e0e, 
+ 0x0008007c,  0x1e5cdc00,  0x300c387e,  0x00fe0030,  0x66181806,  0x7e066618,  0x6e060000,  0x18001818, 
+ 0xb67e6660,  0x66606066,  0x6618186c,  0x605a4e66,  0x78666c0e,  0x1862346c,  0x2c183030,  0x180c4400, 
+ 0x003e6660,  0x667e3066,  0x66181878,  0x18d66666,  0x6666303c,  0x306634da,  0x18341808,  0x18104c38, 
+ 0x3c181010,  0x18241c42,  0x11081008,  0x20222208,  0x00000000,  0x00220408,  0x22361804,  0x11111111, 
+ 0x00000028,  0x16b6cc00,  0x300c5418,  0x00000030,  0x66183006,  0x7e066618,  0x66060000,  0x0cfe3000, 
+ 0x9a466660,  0x66606066,  0x6618186c,  0x605a4e66,  0x60666606,  0x1862346c,  0x6c183030,  0x180c0000, 
+ 0x00666660,  0x66603066,  0x6618186c,  0x18d66666,  0x66663006,  0x3066346c,  0x2c343018,  0x18180020, 
+ 0x00091010,  0x000e0942,  0x10081008,  0x20222208,  0x0f06060f,  0x0a09041e,  0x002a0e38,  0x10101010, 
+ 0x00180028,  0x56b6cc00,  0x300c1018,  0x18001860,  0x66187e66,  0x0c666630,  0x66661818,  0x06fe6018, 
+ 0x40466632,  0x6c606036,  0x66181866,  0x605a4624,  0x60246666,  0x1834186c,  0x46186030,  0x0c0c0000, 
+ 0x006e6666,  0x6e66306e,  0x66181866,  0x18d66666,  0x666e3066,  0x306e186c,  0x46186030,  0x180c003c, 
+ 0x08090909,  0x1f110aff,  0x0e081008,  0x382c2208,  0x08020901,  0x0a0a0911,  0x09220907,  0x0e0e0e0e, 
+ 0x00180028,  0x7c1c7600,  0x18180000,  0x18001860,  0x3c7e7e3c,  0x0c3c3c30,  0x3c3c1818,  0x02004018, 
+ 0x3e467c1e,  0x787e601e,  0x663c1866,  0x7e42463c,  0x603c663c,  0x1818186c,  0x66187e30,  0x0c0c0000, 
+ 0x00367c3c,  0x363c7c36,  0x667e1866,  0x7ed6663c,  0x7c367c3c,  0x1e36186c,  0x66187e30,  0x180c0008, 
+ 0x080f0606,  0x04110c18,  0x01081008,  0x20222208,  0x0e020203,  0x0a0c0d1e,  0x0d220e08,  0x01010101, 
+ 0x00000000,  0x10000000,  0x18180000,  0x080000c0,  0x00000000,  0x00000000,  0x00000008,  0x00000000, 
+ 0x00000000,  0x00000000,  0x00001800,  0x00000000,  0x000c0000,  0x00000000,  0x00000030,  0x060c00fe, 
+ 0x00000000,  0x00000006,  0x00001800,  0x00000000,  0x60060000,  0x00000000,  0x0010001c,  0x18380008, 
+ 0x08090606,  0x040e0a18,  0x11081f08,  0x20221c3e,  0x08020401,  0x0f0a0b11,  0x0b220908,  0x11111111, 
+ 0x00000000,  0x00000000,  0x0c300000,  0x080000c0,  0x00000000,  0x00000000,  0x00000008,  0x00000000, 
+ 0x00000000,  0x00000000,  0x00007000,  0x00000000,  0x00060000,  0x00000000,  0x0000003c,  0x063c0000, 
+ 0x00000000,  0x00000066,  0x00001800,  0x00000000,  0x60060000,  0x00000000,  0x00300000,  0x00000008, 
+ 0x0f090909,  0x04030900,  0x0e000000,  0x00000000,  0x0f0f0f0f,  0x0209091e,  0x09000f07,  0x0e0e0e0e, 
+ 0x00000000,  0x00000000,  0x00000000,  0x10000000,  0x00000000,  0x00000000,  0x00000010,  0x00000000, 
+ 0x00000000,  0x00000000,  0x00000000,  0x00000000,  0x00000000,  0x00000000,  0x00000000,  0x00000000, 
+ 0x00000000,  0x0000003c,  0x00007000,  0x00000000,  0x60060000,  0x00000000,  0x00600000,  0x0000000f, 
+
+};
+
+static GBitmap strike0 = {
+	bits0,
+	0,
+	32,
+	0,
+	{0, 0, 1024, 14},
+	{0, 0, 1024, 14},
+};
+
+static Fontchar info0[] = {
+	{ 0, 0, 14, 0, 8 },
+	{ 8, 0, 14, 0, 8 },
+	{ 16, 0, 14, 0, 8 },
+	{ 24, 0, 14, 0, 8 },
+	{ 32, 0, 14, 0, 8 },
+	{ 40, 0, 14, 0, 8 },
+	{ 48, 0, 14, 0, 8 },
+	{ 56, 0, 14, 0, 8 },
+	{ 64, 0, 14, 0, 8 },
+	{ 72, 0, 14, 0, 8 },
+	{ 80, 0, 14, 0, 8 },
+	{ 88, 0, 14, 0, 8 },
+	{ 96, 0, 14, 0, 8 },
+	{ 104, 0, 14, 0, 8 },
+	{ 112, 0, 14, 0, 8 },
+	{ 120, 0, 14, 0, 8 },
+	{ 128, 0, 14, 0, 8 },
+	{ 136, 0, 14, 0, 8 },
+	{ 144, 0, 14, 0, 8 },
+	{ 152, 0, 14, 0, 8 },
+	{ 160, 0, 14, 0, 8 },
+	{ 168, 0, 14, 0, 8 },
+	{ 176, 0, 14, 0, 8 },
+	{ 184, 0, 14, 0, 8 },
+	{ 192, 0, 14, 0, 8 },
+	{ 200, 0, 14, 0, 8 },
+	{ 208, 0, 14, 0, 8 },
+	{ 216, 0, 14, 0, 8 },
+	{ 224, 0, 14, 0, 8 },
+	{ 232, 0, 14, 0, 8 },
+	{ 240, 0, 14, 0, 8 },
+	{ 248, 0, 14, 0, 8 },
+	{ 256, 0, 0, 0, 8 },
+	{ 264, 2, 11, 0, 8 },
+	{ 272, 2, 6, 0, 8 },
+	{ 280, 2, 11, 0, 8 },
+	{ 288, 1, 12, 0, 8 },
+	{ 296, 2, 11, 0, 8 },
+	{ 304, 2, 11, 0, 8 },
+	{ 312, 2, 7, 0, 8 },
+	{ 320, 1, 13, 0, 8 },
+	{ 328, 1, 13, 0, 8 },
+	{ 336, 3, 10, 0, 8 },
+	{ 344, 4, 10, 0, 8 },
+	{ 352, 9, 14, 0, 8 },
+	{ 360, 6, 8, 0, 8 },
+	{ 368, 9, 11, 0, 8 },
+	{ 376, 1, 13, 0, 8 },
+	{ 384, 2, 11, 0, 8 },
+	{ 392, 2, 11, 0, 8 },
+	{ 400, 2, 11, 0, 8 },
+	{ 408, 2, 11, 0, 8 },
+	{ 416, 2, 11, 0, 8 },
+	{ 424, 2, 11, 0, 8 },
+	{ 432, 2, 11, 0, 8 },
+	{ 440, 2, 11, 0, 8 },
+	{ 448, 2, 11, 0, 8 },
+	{ 456, 2, 11, 0, 8 },
+	{ 464, 4, 11, 0, 8 },
+	{ 472, 4, 14, 0, 8 },
+	{ 480, 2, 11, 0, 8 },
+	{ 488, 4, 10, 0, 8 },
+	{ 496, 2, 11, 0, 8 },
+	{ 504, 2, 11, 0, 8 },
+	{ 512, 2, 11, 0, 8 },
+	{ 520, 2, 11, 0, 8 },
+	{ 528, 2, 11, 0, 8 },
+	{ 536, 2, 11, 0, 8 },
+	{ 544, 2, 11, 0, 8 },
+	{ 552, 2, 11, 0, 8 },
+	{ 560, 2, 11, 0, 8 },
+	{ 568, 2, 11, 0, 8 },
+	{ 576, 2, 11, 0, 8 },
+	{ 584, 2, 11, 0, 8 },
+	{ 592, 2, 13, 0, 8 },
+	{ 600, 2, 11, 0, 8 },
+	{ 608, 2, 11, 0, 8 },
+	{ 616, 2, 11, 0, 8 },
+	{ 624, 2, 11, 0, 8 },
+	{ 632, 2, 11, 0, 8 },
+	{ 640, 2, 11, 0, 8 },
+	{ 648, 2, 13, 0, 8 },
+	{ 656, 2, 11, 0, 8 },
+	{ 664, 2, 11, 0, 8 },
+	{ 672, 2, 11, 0, 8 },
+	{ 680, 2, 11, 0, 8 },
+	{ 688, 2, 11, 0, 8 },
+	{ 696, 2, 11, 0, 8 },
+	{ 704, 2, 11, 0, 8 },
+	{ 712, 2, 11, 0, 8 },
+	{ 720, 2, 11, 0, 8 },
+	{ 728, 1, 13, 0, 8 },
+	{ 736, 1, 13, 0, 8 },
+	{ 744, 1, 13, 0, 8 },
+	{ 752, 2, 8, 0, 8 },
+	{ 760, 11, 12, 0, 8 },
+	{ 768, 2, 7, 0, 8 },
+	{ 776, 4, 11, 0, 8 },
+	{ 784, 1, 11, 0, 8 },
+	{ 792, 4, 11, 0, 8 },
+	{ 800, 1, 11, 0, 8 },
+	{ 808, 4, 11, 0, 8 },
+	{ 816, 1, 11, 0, 8 },
+	{ 824, 4, 14, 0, 8 },
+	{ 832, 1, 11, 0, 8 },
+	{ 840, 1, 11, 0, 8 },
+	{ 848, 1, 14, 0, 8 },
+	{ 856, 1, 11, 0, 8 },
+	{ 864, 1, 11, 0, 8 },
+	{ 872, 4, 11, 0, 8 },
+	{ 880, 4, 11, 0, 8 },
+	{ 888, 4, 11, 0, 8 },
+	{ 896, 4, 14, 0, 8 },
+	{ 904, 4, 14, 0, 8 },
+	{ 912, 4, 11, 0, 8 },
+	{ 920, 4, 11, 0, 8 },
+	{ 928, 2, 11, 0, 8 },
+	{ 936, 4, 11, 0, 8 },
+	{ 944, 4, 11, 0, 8 },
+	{ 952, 4, 11, 0, 8 },
+	{ 960, 4, 11, 0, 8 },
+	{ 968, 4, 14, 0, 8 },
+	{ 976, 4, 11, 0, 8 },
+	{ 984, 1, 12, 0, 8 },
+	{ 992, 1, 12, 0, 8 },
+	{ 1000, 1, 12, 0, 8 },
+	{ 1008, 5, 8, 0, 8 },
+	{ 1016, 0, 14, 0, 8 },
+	{ 1024, 0, 14, 0, 8 },
+	{ 0, 0, 0, 0, 0 }
+};
+
+GSubfont defont0 = {
+	129,
+	14,
+	2,
+	info0,
+	&strike0,
+};
--- /dev/null
+++ b/os/boot/rpcg/devether.c
@@ -1,0 +1,157 @@
+#include "u.h"
+#include "lib.h"
+#include "mem.h"
+#include "dat.h"
+#include "fns.h"
+#include "io.h"
+
+#include "etherif.h"
+
+static Ctlr ether[MaxEther];
+
+static struct {
+	char	*type;
+	int	(*reset)(Ctlr*);
+} cards[] = {
+	{ "SCC", sccethreset, },
+	{ "SCC2", sccethreset, },
+	{ 0, }
+};
+
+int
+etherinit(void)
+{
+	Ctlr *ctlr;
+	int ctlrno, i, mask, n;
+
+	mask = 0;
+	for(ctlrno = 0; ctlrno < MaxEther; ctlrno++){
+		ctlr = &ether[ctlrno];
+		memset(ctlr, 0, sizeof(Ctlr));
+		if(archether(ctlrno, &ctlr->card) <= 0)
+			continue;
+		for(n = 0; cards[n].type; n++){
+			if(strcmp(cards[n].type, ctlr->card.type))
+				continue;
+			ctlr->ctlrno = ctlrno;
+			if((*cards[n].reset)(ctlr))
+				break;
+
+			ctlr->iq = qopen(16*1024, 1, 0, 0);
+			ctlr->oq = qopen(16*1024, 1, 0, 0);
+
+			ctlr->present = 1;
+			mask |= 1<<ctlrno;
+
+			print("ether%d: %s: port 0x%luX irq %d",
+				ctlr->ctlrno, ctlr->card.type, ctlr->card.port, ctlr->card.irq);
+			if(ctlr->card.mem)
+				print(" addr 0x%luX", PADDR(ctlr->card.mem));
+			if(ctlr->card.size)
+				print(" size 0x%luX", ctlr->card.size);
+			print(":");
+			for(i = 0; i < sizeof(ctlr->card.ea); i++)
+				print(" %2.2uX", ctlr->card.ea[i]);
+			print("\n"); uartwait();
+			setvec(VectorPIC + ctlr->card.irq, ctlr->card.intr, ctlr);
+			break;
+		}
+	}
+
+	return mask;
+}
+
+static Ctlr*
+attach(int ctlrno)
+{
+	Ctlr *ctlr;
+
+	if(ctlrno >= MaxEther || ether[ctlrno].present == 0)
+		return 0;
+
+	ctlr = &ether[ctlrno];
+	if(ctlr->present == 1){
+		ctlr->present = 2;
+		(*ctlr->card.attach)(ctlr);
+	}
+
+	return ctlr;
+}
+
+uchar*
+etheraddr(int ctlrno)
+{
+	Ctlr *ctlr;
+
+	if((ctlr = attach(ctlrno)) == 0)
+		return 0;
+
+	return ctlr->card.ea;
+}
+
+int
+etherrxpkt(int ctlrno, Etherpkt *pkt, int timo)
+{
+	int n;
+	Ctlr *ctlr;
+	Block *b;
+	ulong start;
+
+	if((ctlr = attach(ctlrno)) == 0)
+		return 0;
+
+	start = m->ticks;
+	while((b = qget(ctlr->iq)) == 0){
+		if(TK2MS(m->ticks - start) >= timo){
+			/*
+			print("ether%d: rx timeout\n", ctlrno);
+			 */
+			return 0;
+		}
+	}
+
+	n = BLEN(b);
+	memmove(pkt, b->rp, n);
+	freeb(b);
+
+	return n;
+}
+
+int
+etheriq(Ctlr *ctlr, Block *b, int freebp)
+{
+	if(memcmp(((Etherpkt*)b->rp)->d, ctlr->card.ea, Eaddrlen) != 0 &&
+	   memcmp(((Etherpkt*)b->rp)->d, broadcast, Eaddrlen) != 0){
+		if(freebp)
+			freeb(b);
+		return 0;
+	}
+	qbwrite(ctlr->iq, b);
+	return 1;
+}
+
+int
+ethertxpkt(int ctlrno, Etherpkt *pkt, int len, int)
+{
+	Ctlr *ctlr;
+	Block *b;
+	int s;
+
+	if((ctlr = attach(ctlrno)) == 0)
+		return 0;
+
+	if(qlen(ctlr->oq) > 16*1024){
+		print("ether%d: tx queue full\n", ctlrno);
+		return 0;
+	}
+	b = iallocb(sizeof(Etherpkt));
+	memmove(b->wp, pkt, len);
+	memmove(((Etherpkt*)b->wp)->s, ctlr->card.ea, Eaddrlen);
+	b->wp += len;
+	qbwrite(ctlr->oq, b);
+	s = splhi();
+	(*ctlr->card.transmit)(ctlr);
+	splx(s);
+
+	return 1;
+}
--- /dev/null
+++ b/os/boot/rpcg/devuart.c
@@ -1,0 +1,230 @@
+#include "u.h"
+#include "lib.h"
+#include "mem.h"
+#include "dat.h"
+#include "fns.h"
+#include "io.h"
+
+/*
+ *  SMC1 in UART mode
+ */
+
+typedef struct Uartsmc Uartsmc;
+struct Uartsmc {
+	IOCparam;
+	ushort	maxidl;
+	ushort	idlc;
+	ushort	brkln;
+	ushort	brkec;
+	ushort	brkcr;
+	ushort	rmask;
+};
+
+typedef struct Uart	Uart;
+struct Uart
+{
+	int	port;
+	int	setup;
+	uchar	txbusy;
+
+	Queue*	iq;
+	Queue*	oq;
+	void	(*rx)(Queue*, int);
+	void	(*boot)(uchar*, int);
+
+	ulong	frame;
+	ulong	overrun;
+	uchar	rxbuf[128];
+	char	txbuf[16];
+	BD*	rxb;
+	BD*	txb;
+};
+
+Uart	uart[1];
+int	predawn = 1;
+
+static	void	uartintr(Ureg*, void*);
+static	void	uartkick(void*);
+
+static int
+baudgen(int baud)
+{
+	int d;
+
+	d = ((m->cpuhz/baud)+8)>>4;
+	if(d >= (1<<12))
+		return ((d+15)>>3)|1;
+	return d<<1;
+}
+
+static void
+smcsetup(Uart *up, int baud)
+{
+	IMM *io;
+	Uartsmc *p;
+	BD *bd;
+	SMC *smc;
+
+	archenableuart(SMC1ID, 0);
+	io = m->iomem;
+	io->pbpar |= IBIT(24)|IBIT(25);	/* enable SMC1 TX/RX */
+	io->pbdir &= ~(IBIT(24)|IBIT(25));
+	io->brgc1 = baudgen(baud) | BaudEnable;
+	io->simode &= ~0xF000;	/* SMC1 to NMSI mode, Tx/Rx clocks are BRG1 */
+
+	bd = bdalloc(1);
+	p = (Uartsmc*)KADDR(SMC1P);
+	p->rbase = (ushort)bd;
+	up->rxb = bd;
+	bd->status = BDEmpty|BDWrap|BDInt;
+	bd->length = 0;
+	bd->addr = PADDR(up->rxbuf);
+	bd = bdalloc(1);
+	p->tbase = (ushort)bd;
+	up->txb = bd;
+	bd->status = BDWrap|BDInt;
+	bd->length = 0;
+	bd->addr = PADDR(up->txbuf);
+
+	cpmop(InitRxTx, SMC1ID, 0);
+
+	/* protocol parameters */
+	p->rfcr = 0x18;
+	p->tfcr = 0x18;
+	p->mrblr = 1;
+	p->maxidl = 1;
+	p->brkln = 0;
+	p->brkec = 0;
+	p->brkcr = 1;
+	smc = IOREGS(0xA80, SMC);
+	smc->smce = 0xff;	/* clear events */
+	smc->smcm = 0x17;	/* enable all possible interrupts */
+	setvec(VectorCPIC+4, uartintr, up);
+	smc->smcmr = 0x4820;	/* 8-bit mode, no parity, 1 stop bit, UART mode, ... */
+	smc->smcmr |= 3;	/* enable rx/tx */
+}
+
+static void
+uartintr(Ureg*, void *arg)
+{
+	Uart *up;
+	int ch, i;
+	BD *bd;
+	SMC *smc;
+	Block *b;
+
+	up = arg;
+	smc = IOREGS(0xA80, SMC);
+	smc->smce = 0xff;	/* clear all events */
+	if((bd = up->rxb) != nil && (bd->status & BDEmpty) == 0){
+		if(up->iq != nil && bd->length > 0){
+			if(up->boot != nil){
+				up->boot(up->rxbuf, bd->length);
+			}else if(up->rx != nil){
+				for(i=0; i<bd->length; i++){
+					ch = up->rxbuf[i];
+					up->rx(up->iq, ch);
+				}
+			}else{
+				b = iallocb(bd->length);
+				memmove(b->wp, up->rxbuf, bd->length);
+				b->wp += bd->length;
+				qbwrite(up->iq, b);
+			}
+		}
+		bd->status |= BDEmpty|BDInt;
+	} else if((bd = up->txb) != nil && (bd->status & BDReady) == 0){
+		ch = -1;
+		if(up->oq)
+			ch = qbgetc(up->oq);
+		if(ch != -1){
+			up->txbuf[0] = ch;
+			bd->length = 1;
+			bd->status |= BDReady;
+		}else
+			up->txbusy = 0;
+	}
+	/* TO DO: modem status, errors, etc */
+}
+
+static void
+uartkick(void *arg)
+{
+	Uart *up = arg;
+	int s, c, i;
+
+	s = splhi();
+	while(up->txbusy == 0 && (c = qbgetc(up->oq)) != -1){
+		if(predawn){
+			while(up->txb->status & BDReady)
+				;
+		} else {
+			for(i = 0; i < 100; i++){
+				if((up->txb->status & BDReady) == 0)
+					break;
+				delay(1);
+			}
+		}
+		up->txbuf[0] = c;
+		up->txb->length = 1;
+		up->txb->status |= BDReady;
+		up->txbusy = !predawn;
+	}
+	splx(s);
+}
+
+void
+uartspecial(int port, int baud, Queue **iq, Queue **oq, void (*rx)(Queue*,int))
+{
+	Uart *up = &uart[0];
+
+	if(up->setup)
+		return;
+	up->setup = 1;
+
+	*iq = up->iq = qopen(4*1024, 0, 0, 0);
+	*oq = up->oq = qopen(16*1024, 0, uartkick, up);
+	up->rx = rx;
+	USED(port);
+	up->port = SMC1ID;
+	if(baud == 0)
+		baud = 9600;
+	smcsetup(up, baud);
+	/* if using SCCn's UART, would also set DTR and RTS, but SMC doesn't use them */
+}
+
+void
+uartsetboot(void (*f)(uchar*, int))
+{
+	uart[0].boot = f;
+}
+
+void
+uartputs(char *s, int n)
+{
+	Uart *up = &uart[0];
+	Block *b;
+	int nl;
+	char *p;
+
+	nl = 0;
+	for(p = s; p < s+n; p++)
+		if(*p == '\n')
+			nl++;
+	b = iallocb(n+nl);
+	while(n--){
+		if(*s == '\n')
+			*b->wp++ = '\r';
+		*b->wp++ = *s++;
+	}
+	qbwrite(up->oq, b);
+}
+
+void
+uartwait(void)
+{
+	Uart *up = &uart[0];
+
+	while(up->txbusy)
+		;
+}
--- /dev/null
+++ b/os/boot/rpcg/dload.c
@@ -1,0 +1,103 @@
+#include <u.h>
+#include <libc.h>
+#include <bio.h>
+#include <mach.h>
+
+static	char	*kernelfile = "/power/ipaq";
+ulong	crc32(void *buf, int n, ulong crc);
+
+void
+main(int argc, char **argv)
+{
+	int ifd, n;
+	char buf[64], reply[1];
+	int i, execsize;
+	Fhdr f;
+	ulong csum;
+
+	ARGBEGIN{
+	}ARGEND
+	ifd = open(kernelfile, OREAD);
+	if(ifd < 0){
+		fprint(2, "dload: can't open %s: %r\n", kernelfile);
+		exits("open");
+	}
+	i = 0;
+	if(crackhdr(ifd, &f) == 0){
+		fprint(2, "dload: not an executable file: %r\n");
+		exits("format");
+	}
+	if(f.magic != Q_MAGIC){
+		fprint(2, "dload: not a powerpc executable\n");
+		exits("format");
+	}
+	execsize = f.txtsz + f.datsz + f.txtoff;
+	seek(ifd, 0, 0);
+	csum = ~0;
+	while(execsize > 0 && (n = read(ifd, buf, sizeof(buf))) > 0){
+		if(n > execsize)
+			n = execsize;
+		for(;;){
+			if(write(1, buf, sizeof(buf)) != sizeof(buf)){	/* always writes full buffer */
+				fprint(2, "dload: write error: %r\n");
+				exits("write");
+			}
+			if(read(0, reply, 1) != 1){
+				fprint(2, "dload: bad reply\n");
+				exits("read");
+			}
+			if(reply[0] != 'n')
+				break;
+			fprint(2, "!");
+		}
+		if(reply[0] != 'y'){
+			fprint(2, "dload: bad ack: %c\n", reply[0]);
+			exits("reply");
+		}
+		if(++i%10 == 0)
+			fprint(2, ".");
+		execsize -= n;
+	}
+	exits(0);
+}
+
+/*
+ * from Rob Warnock
+ */
+static	ulong	crc32tab[256];	/* initialised on first call to crc32 */
+
+enum {
+	CRC32POLY = 0x04c11db7     /* AUTODIN II, Ethernet, & FDDI */
+};
+
+/*
+ * Build auxiliary table for parallel byte-at-a-time CRC-32.
+ */
+static void
+initcrc32(void)
+{
+	int i, j;
+	ulong c;
+
+	for(i = 0; i < 256; i++) {
+		for(c = i << 24, j = 8; j > 0; j--)
+			if(c & (1<<31))
+				c = (c<<1) ^ CRC32POLY;
+			else
+				c <<= 1;
+		crc32tab[i] = c;
+	}
+}
+
+ulong
+crc32(void *buf, int n, ulong crc)
+{
+	uchar *p;
+
+	if(crc32tab[1] == 0)
+		initcrc32();
+	crc = ~crc;
+	for(p = buf; --n >= 0;)
+		crc = (crc << 8) ^ crc32tab[(crc >> 24) ^ *p++];
+	return ~crc;
+}
--- /dev/null
+++ b/os/boot/rpcg/donprint.c
@@ -1,0 +1,332 @@
+#include	"u.h"
+#include	"lib.h"
+
+#define	PTR	sizeof(char*)
+#define	SHORT	sizeof(int)
+#define	INT	sizeof(int)
+#define	LONG	sizeof(long)
+#define	IDIGIT	30
+#define	MAXCON	30
+
+#define	FLONG	(1<<0)
+#define	FSHORT	(1<<1)
+#define	FUNSIGN	(1<<2)
+
+typedef struct Op	Op;
+struct Op
+{
+	char	*p;
+	char	*ep;
+	void	*argp;
+	int	f1;
+	int	f2;
+	int	f3;
+};
+
+static	int	noconv(Op*);
+static	int	cconv(Op*);
+static	int	dconv(Op*);
+static	int	hconv(Op*);
+static	int	lconv(Op*);
+static	int	oconv(Op*);
+static	int	sconv(Op*);
+static	int	uconv(Op*);
+static	int	xconv(Op*);
+static	int	Xconv(Op*);
+static	int	percent(Op*);
+
+static
+int	(*fmtconv[MAXCON])(Op*) =
+{
+	noconv,
+	cconv, dconv, hconv, lconv,
+	oconv, sconv, uconv, xconv,
+	Xconv, percent,
+};
+static
+char	fmtindex[128] =
+{
+	['c'] 1,
+	['d'] 2,
+	['h'] 3,
+	['l'] 4,
+	['o'] 5,
+	['s'] 6,
+	['u'] 7,
+	['x'] 8,
+	['X'] 9,
+	['%'] 10,
+};
+
+static	int	convcount  = { 11 };
+static	int	ucase;
+
+static void
+PUT(Op *o, int c)
+{
+	static int pos;
+	int opos;
+
+	if(c == '\t'){
+		opos = pos;
+		pos = (opos+8) & ~7;
+		while(opos++ < pos && o->p < o->ep)
+			*o->p++ = ' ';
+		return;
+	}
+	if(o->p < o->ep){
+		*o->p++ = c;
+		pos++;
+	}
+	if(c == '\n')
+		pos = 0;
+}
+
+int
+fmtinstall(char c, int (*f)(Op*))
+{
+
+	c &= 0177;
+	if(fmtindex[c] == 0) {
+		if(convcount >= MAXCON)
+			return 1;
+		fmtindex[c] = convcount++;
+	}
+	fmtconv[fmtindex[c]] = f;
+	return 0;
+}
+
+char*
+donprint(char *p, char *ep, char *fmt, void *argp)
+{
+	int sf1, c;
+	Op o;
+
+	o.p = p;
+	o.ep = ep;
+	o.argp = argp;
+
+loop:
+	c = *fmt++;
+	if(c != '%') {
+		if(c == 0) {
+			if(o.p < o.ep)
+				*o.p = 0;
+			return o.p;
+		}
+		PUT(&o, c);
+		goto loop;
+	}
+	o.f1 = 0;
+	o.f2 = -1;
+	o.f3 = 0;
+	c = *fmt++;
+	sf1 = 0;
+	if(c == '-') {
+		sf1 = 1;
+		c = *fmt++;
+	}
+	while(c >= '0' && c <= '9') {
+		o.f1 = o.f1*10 + c-'0';
+		c = *fmt++;
+	}
+	if(sf1)
+		o.f1 = -o.f1;
+	if(c != '.')
+		goto l1;
+	c = *fmt++;
+	while(c >= '0' && c <= '9') {
+		if(o.f2 < 0)
+			o.f2 = 0;
+		o.f2 = o.f2*10 + c-'0';
+		c = *fmt++;
+	}
+l1:
+	if(c == 0)
+		fmt--;
+	c = (*fmtconv[fmtindex[c&0177]])(&o);
+	if(c < 0) {
+		o.f3 |= -c;
+		c = *fmt++;
+		goto l1;
+	}
+	o.argp = (char*)o.argp + c;
+	goto loop;
+}
+
+void
+strconv(char *o, Op *op, int f1, int f2)
+{
+	int n, c;
+	char *p;
+
+	n = strlen(o);
+	if(f1 >= 0)
+		while(n < f1) {
+			PUT(op, ' ');
+			n++;
+		}
+	for(p=o; c = *p++;)
+		if(f2 != 0) {
+			PUT(op, c);
+			f2--;
+		}
+	if(f1 < 0) {
+		f1 = -f1;
+		while(n < f1) {
+			PUT(op, ' ');
+			n++;
+		}
+	}
+}
+
+int
+numbconv(Op *op, int base)
+{
+	char b[IDIGIT];
+	int i, f, n, r;
+	long v;
+	short h;
+
+	f = 0;
+	switch(op->f3 & (FLONG|FSHORT|FUNSIGN)) {
+	case FLONG:
+		v = *(long*)op->argp;
+		r = LONG;
+		break;
+
+	case FUNSIGN|FLONG:
+		v = *(ulong*)op->argp;
+		r = LONG;
+		break;
+
+	case FSHORT:
+		h = *(int*)op->argp;
+		v = h;
+		r = SHORT;
+		break;
+
+	case FUNSIGN|FSHORT:
+		h = *(int*)op->argp;
+		v = (ushort)h;
+		r = SHORT;
+		break;
+
+	default:
+		v = *(int*)op->argp;
+		r = INT;
+		break;
+
+	case FUNSIGN:
+		v = *(unsigned*)op->argp;
+		r = INT;
+		break;
+	}
+	if(!(op->f3 & FUNSIGN) && v < 0) {
+		v = -v;
+		f = 1;
+	}
+	b[IDIGIT-1] = 0;
+	for(i = IDIGIT-2;; i--) {
+		n = (ulong)v % base;
+		n += '0';
+		if(n > '9'){
+			n += 'a' - ('9'+1);
+			if(ucase)
+				n += 'A'-'a';
+		}
+		b[i] = n;
+		if(i < 2)
+			break;
+		v = (ulong)v / base;
+		if(op->f2 >= 0 && i >= IDIGIT-op->f2)
+			continue;
+		if(v <= 0)
+			break;
+	}
+	if(f)
+		b[--i] = '-';
+	strconv(b+i, op, op->f1, -1);
+	return r;
+}
+
+static	int
+noconv(Op *op)
+{
+
+	strconv("***", op, 0, -1);
+	return 0;
+}
+
+static	int
+cconv(Op *op)
+{
+	char b[2];
+
+	b[0] = *(int*)op->argp;
+	b[1] = 0;
+	strconv(b, op, op->f1, -1);
+	return INT;
+}
+
+static	int
+dconv(Op *op)
+{
+	return numbconv(op, 10);
+}
+
+static	int
+hconv(Op*)
+{
+	return -FSHORT;
+}
+
+static	int
+lconv(Op*)
+{
+	return -FLONG;
+}
+
+static	int
+oconv(Op *op)
+{
+	return numbconv(op, 8);
+}
+
+static	int
+sconv(Op *op)
+{
+	strconv(*(char**)op->argp, op, op->f1, op->f2);
+	return PTR;
+}
+
+static	int
+uconv(Op*)
+{
+	return -FUNSIGN;
+}
+
+static	int
+xconv(Op *op)
+{
+	return numbconv(op, 16);
+}
+
+static	int
+Xconv(Op *op)
+{
+	int r;
+
+	ucase = 1;
+	r = numbconv(op, 16);
+	ucase = 0;
+	return r;
+}
+
+static	int
+percent(Op *op)
+{
+
+	PUT(op, '%');
+	return 0;
+}
--- /dev/null
+++ b/os/boot/rpcg/dosboot.c
@@ -1,0 +1,614 @@
+#include	"u.h"
+#include	"lib.h"
+#include	"mem.h"
+#include	"dat.h"
+#include	"fns.h"
+#include	"dosfs.h"
+
+extern char *premature;
+
+/*
+ *  predeclared
+ */
+static void	bootdump(Dosboot*);
+static void	setname(Dosfile*, char*);
+long		dosreadseg(Dosfile*, long, long);
+
+/*
+ *  debugging
+ */
+#define chatty	1
+#define chat	if(chatty)print
+
+/*
+ *  block io buffers
+ */
+enum
+{
+	Nbio=	16,
+};
+typedef struct	Clustbuf	Clustbuf;
+struct Clustbuf
+{
+	int	age;
+	long	sector;
+	uchar	*iobuf;
+	Dos	*dos;
+	int	size;
+};
+Clustbuf	bio[Nbio];
+
+/*
+ *  get an io block from an io buffer
+ */
+Clustbuf*
+getclust(Dos *dos, long sector)
+{
+	Clustbuf *p, *oldest;
+	int size;
+
+	chat("getclust @ %d\n", sector);
+
+	/*
+	 *  if we have it, just return it
+	 */
+	for(p = bio; p < &bio[Nbio]; p++){
+		if(sector == p->sector && dos == p->dos){
+			p->age = m->ticks;
+			chat("getclust %d in cache\n", sector);
+			return p;
+		}
+	}
+
+	/*
+	 *  otherwise, reuse the oldest entry
+	 */
+	oldest = bio;
+	for(p = &bio[1]; p < &bio[Nbio]; p++){
+		if(p->age <= oldest->age)
+			oldest = p;
+	}
+	p = oldest;
+
+	/*
+	 *  make sure the buffer is big enough
+	 */
+	size = dos->clustsize*dos->sectsize;
+	if(p->iobuf==0 || p->size < size)
+		p->iobuf = ialloc(size, 0);
+	p->size = size;
+
+	/*
+	 *  read in the cluster
+	 */
+	chat("getclust addr %d\n", (sector+dos->start)*dos->sectsize);
+	if((*dos->seek)(dos->dev, (sector+dos->start)*dos->sectsize) < 0){
+		chat("can't seek block\n");
+		return 0;
+	}
+	if((*dos->read)(dos->dev, p->iobuf, size) != size){
+		chat("can't read block\n");
+		return 0;
+	}
+
+	p->age = m->ticks;
+	p->dos = dos;
+	p->sector = sector;
+	chat("getclust %d read\n", sector);
+	return p;
+}
+
+/*
+ *  walk the fat one level ( n is a current cluster number ).
+ *  return the new cluster number or -1 if no more.
+ */
+static long
+fatwalk(Dos *dos, int n)
+{
+	ulong k, sect;
+	Clustbuf *p;
+	int o;
+
+	chat("fatwalk %d\n", n);
+
+	if(n < 2 || n >= dos->fatclusters)
+		return -1;
+
+	switch(dos->fatbits){
+	case 12:
+		k = (3*n)/2; break;
+	case 16:
+		k = 2*n; break;
+	default:
+		return -1;
+	}
+	if(k >= dos->fatsize*dos->sectsize)
+		panic("getfat");
+
+	sect = (k/(dos->sectsize*dos->clustsize))*dos->clustsize + dos->fataddr;
+	o = k%(dos->sectsize*dos->clustsize);
+	p = getclust(dos, sect);
+	k = p->iobuf[o++];
+	if(o >= dos->sectsize*dos->clustsize){
+		p = getclust(dos, sect+dos->clustsize);
+		o = 0;
+	}
+	k |= p->iobuf[o]<<8;
+	if(dos->fatbits == 12){
+		if(n&1)
+			k >>= 4;
+		else
+			k &= 0xfff;
+		if(k >= 0xff8)
+			k |= 0xf000;
+	}
+	k = k < 0xfff8 ? k : -1;
+	chat("fatwalk %d -> %d\n", n, k);
+	return k;
+}
+
+/*
+ *  map a file's logical cluster address to a physical sector address
+ */
+static long
+fileaddr(Dosfile *fp, long ltarget)
+{
+	Dos *dos = fp->dos;
+	long l;
+	long p;
+
+	chat("fileaddr %8.8s %d\n", fp->name, ltarget);
+	/*
+	 *  root directory is contiguous and easy
+	 */
+	if(fp->pstart == 0){
+		if(ltarget*dos->sectsize*dos->clustsize >= dos->rootsize*sizeof(Dosdir))
+			return -1;
+		l = dos->rootaddr + ltarget*dos->clustsize;
+		chat("fileaddr %d -> %d\n", ltarget, l);
+		return l;
+	}
+
+	/*
+	 *  anything else requires a walk through the fat
+	 */
+	if(ltarget >= fp->lcurrent && fp->pcurrent){
+		/* start at the currrent point */
+		l = fp->lcurrent;
+		p = fp->pcurrent;
+	} else {
+		/* go back to the beginning */
+		l = 0;
+		p = fp->pstart;
+	}
+	while(l != ltarget){
+		/* walk the fat */
+		p = fatwalk(dos, p);
+		if(p < 0)
+			return -1;
+		l++;
+	}
+	fp->lcurrent = l;
+	fp->pcurrent = p;
+
+	/*
+	 *  clusters start at 2 instead of 0 (why? - presotto)
+	 */
+	l =  dos->dataaddr + (p-2)*dos->clustsize;
+	chat("fileaddr %d -> %d\n", ltarget, l);
+	return l;
+}
+
+/*
+ *  read from a dos file
+ */
+long
+dosread(Dosfile *fp, void *a, long n)
+{
+	long addr;
+	long rv;
+	int i;
+	int off;
+	Clustbuf *p;
+	uchar *from, *to;
+
+	if((fp->attr & DDIR) == 0){
+		if(fp->offset >= fp->length)
+			return 0;
+		if(fp->offset+n > fp->length)
+			n = fp->length - fp->offset;
+	}
+
+	to = a;
+	for(rv = 0; rv < n; rv+=i){
+		/*
+		 *  read the cluster
+		 */
+		addr = fileaddr(fp, fp->offset/fp->dos->clustbytes);
+		if(addr < 0)
+			return -1;
+		p = getclust(fp->dos, addr);
+		if(p == 0)
+			return -1;
+
+		/*
+		 *  copy the bytes we need
+		 */
+		off = fp->offset % fp->dos->clustbytes;
+		from = &p->iobuf[off];
+		i = n - rv;
+		if(i > fp->dos->clustbytes - off)
+			i = fp->dos->clustbytes - off;
+		memmove(to, from, i);
+		to += i;
+		fp->offset += i;
+	}
+
+	return rv;
+}
+
+/*
+ *  walk a directory returns
+ * 	-1 if something went wrong
+ *	 0 if not found
+ *	 1 if found
+ */
+int
+doswalk(Dosfile *file, char *name)
+{
+	Dosdir d;
+	long n;
+
+	if((file->attr & DDIR) == 0){
+		chat("walking non-directory!\n");
+		return -1;
+	}
+
+	setname(file, name);
+
+	file->offset = 0;	/* start at the beginning */
+	while((n = dosread(file, &d, sizeof(d))) == sizeof(d)){
+		chat("comparing to %8.8s.%3.3s\n", d.name, d.ext);
+		if(memcmp(file->name, d.name, sizeof(d.name)) != 0)
+			continue;
+		if(memcmp(file->ext, d.ext, sizeof(d.ext)) != 0)
+			continue;
+		if(d.attr & DVLABEL){
+			chat("%8.8s.%3.3s is a LABEL\n", d.name, d.ext);
+			continue;
+		}
+		file->attr = d.attr;
+		file->pstart = GSHORT(d.start);
+		file->length = GLONG(d.length);
+		file->pcurrent = 0;
+		file->lcurrent = 0;
+		file->offset = 0;
+		return 1;
+	}
+	return n >= 0 ? 0 : -1;
+}
+
+
+/*
+ *  instructions that boot blocks can start with
+ */
+#define	JMPSHORT	0xeb
+#define JMPNEAR		0xe9
+
+/*
+ *  read dos file system properties
+ */
+int
+dosinit(Dos *dos, int start, int ishard)
+{
+	Dosboot *b;
+	int i;
+	Clustbuf *p;
+	Dospart *dp;
+	ulong mbroffset, offset;
+
+	/* defaults till we know better */
+	dos->start = start;
+	dos->sectsize = 512;
+	dos->clustsize = 1;
+	mbroffset = 0;
+
+dmddo:
+	/* get first sector */
+	p = getclust(dos, mbroffset);
+	if(p == 0){
+		chat("can't read boot block\n");
+		return -1;
+	}
+
+	/*
+	 * If it's a hard disc then look for an MBR and pick either an
+	 * active partition or the FAT with the lowest starting LBA.
+	 * Things are tricky because we could be pointing to, amongst others:
+	 *	1) a floppy BPB;
+	 *	2) a hard disc MBR;
+	 *	3) a hard disc extended partition table;
+	 *	4) a logical drive on a hard disc;
+	 *	5) a disc-manager boot block.
+	 * They all have the same magic at the end of the block.
+	 */
+	if(p->iobuf[0x1FE] != 0x55 || p->iobuf[0x1FF] != 0xAA) {
+		chat("not DOS\n");
+		return -1;
+	}
+	p->dos = 0;
+	b = (Dosboot *)p->iobuf;
+	if(ishard && b->mediadesc != 0xF8){
+		dp = (Dospart*)&p->iobuf[0x1BE];
+		offset = 0xFFFFFFFF;
+		for(i = 0; i < 4; i++, dp++){
+			if(dp->type == DMDDO){
+				mbroffset = 63;
+				goto dmddo;
+			}
+			if(dp->type != FAT12 && dp->type != FAT16 && dp->type != FATHUGE)
+				continue;
+			if(dp->flag & 0x80){
+				offset = GLONG(dp->start);
+				break;
+			}
+			if(GLONG(dp->start) < offset)
+				offset = GLONG(dp->start);
+		}
+		if(i != 4 || offset != 0xFFFFFFFF){
+			dos->start = mbroffset+offset;
+			p = getclust(dos, 0);
+			if(p == 0 || p->iobuf[0x1FE] != 0x55 || p->iobuf[0x1FF] != 0xAA)
+				return -1;
+		}
+		p->dos = 0;
+	}
+
+	b = (Dosboot *)p->iobuf;
+	if(b->magic[0] != JMPNEAR && (b->magic[0] != JMPSHORT || b->magic[2] != 0x90)){
+		chat("no dos file system\n");
+		return -1;
+	}
+
+	if(chatty)
+		bootdump(b);
+
+	/*
+	 *  determine the systems' wondersous properties
+	 */
+	dos->sectsize = GSHORT(b->sectsize);
+	dos->clustsize = b->clustsize;
+	dos->clustbytes = dos->sectsize*dos->clustsize;
+	dos->nresrv = GSHORT(b->nresrv);
+	dos->nfats = b->nfats;
+	dos->rootsize = GSHORT(b->rootsize);
+	dos->volsize = GSHORT(b->volsize);
+	if(dos->volsize == 0)
+		dos->volsize = GLONG(b->bigvolsize);
+	dos->mediadesc = b->mediadesc;
+	dos->fatsize = GSHORT(b->fatsize);
+	dos->fataddr = dos->nresrv;
+	dos->rootaddr = dos->fataddr + dos->nfats*dos->fatsize;
+	i = dos->rootsize*sizeof(Dosdir) + dos->sectsize - 1;
+	i = i/dos->sectsize;
+	dos->dataaddr = dos->rootaddr + i;
+	dos->fatclusters = 2+(dos->volsize - dos->dataaddr)/dos->clustsize;
+	if(dos->fatclusters < 4087)
+		dos->fatbits = 12;
+	else
+		dos->fatbits = 16;
+	dos->freeptr = 2;
+
+	/*
+	 *  set up the root
+	 */
+	dos->root.dos = dos;
+	dos->root.pstart = 0;
+	dos->root.pcurrent = dos->root.lcurrent = 0;
+	dos->root.offset = 0;
+	dos->root.attr = DDIR;
+	dos->root.length = dos->rootsize*sizeof(Dosdir);
+
+	return 0;
+}
+
+static void
+bootdump(Dosboot *b)
+{
+	if(chatty == 0)
+		return;
+	print("magic: 0x%2.2x 0x%2.2x 0x%2.2x\n",
+		b->magic[0], b->magic[1], b->magic[2]);
+	print("version: \"%8.8s\"\n", b->version);
+	print("sectsize: %d\n", GSHORT(b->sectsize));
+	print("allocsize: %d\n", b->clustsize);
+	print("nresrv: %d\n", GSHORT(b->nresrv));
+	print("nfats: %d\n", b->nfats);
+	print("rootsize: %d\n", GSHORT(b->rootsize));
+	print("volsize: %d\n", GSHORT(b->volsize));
+	print("mediadesc: 0x%2.2x\n", b->mediadesc);
+	print("fatsize: %d\n", GSHORT(b->fatsize));
+	print("trksize: %d\n", GSHORT(b->trksize));
+	print("nheads: %d\n", GSHORT(b->nheads));
+	print("nhidden: %d\n", GLONG(b->nhidden));
+	print("bigvolsize: %d\n", GLONG(b->bigvolsize));
+	print("driveno: %d\n", b->driveno);
+	print("reserved0: 0x%2.2x\n", b->reserved0);
+	print("bootsig: 0x%2.2x\n", b->bootsig);
+	print("volid: 0x%8.8x\n", GLONG(b->volid));
+	print("label: \"%11.11s\"\n", b->label);
+}
+
+/*
+ *  grab next element from a path, return the pointer to unprocessed portion of
+ *  path.
+ */
+static char *
+nextelem(char *path, char *elem)
+{
+	int i;
+
+	while(*path == '/')
+		path++;
+	if(*path==0 || *path==' ')
+		return 0;
+	for(i=0; *path!='\0' && *path!='/' && *path!=' '; i++){
+		if(i==28){
+			print("name component too long\n");
+			return 0;
+		}
+		*elem++ = *path++;
+	}
+	*elem = '\0';
+	return path;
+}
+
+int
+dosstat(Dos *dos, char *path, Dosfile *f)
+{
+	char element[NAMELEN];
+
+	*f = dos->root;
+	while(path = nextelem(path, element)){
+		switch(doswalk(f, element)){
+		case -1:
+			return -1;
+		case 0:
+			return 0;
+		}
+	}
+	return 1;
+}
+
+/*
+ *  boot
+ */
+int
+dosboot(Dos *dos, char *path)
+{
+	Dosfile file;
+	long n;
+	long addr;
+	Exec *ep;
+	void (*b)(void);
+
+	switch(dosstat(dos, path, &file)){
+
+	case -1:
+		print("error walking to %s\n", path);
+		return -1;
+	case 0:
+		print("%s not found\n", path);
+		return -1;
+	case 1:
+		print("found %8.8s.%3.3s attr 0x%ux start 0x%lux len %d\n", file.name,
+			file.ext, file.attr, file.pstart, file.length);
+		break;
+	}
+
+	/*
+	 *  read header
+	 */
+	ep = (Exec*)ialloc(sizeof(Exec), 0);
+	n = sizeof(Exec);
+	if(dosreadseg(&file, n, (ulong) ep) != n){
+		print(premature);
+		return -1;
+	}
+	if(GLLONG(ep->magic) != Q_MAGIC){
+		print("bad magic 0x%lux not a plan 9 executable!\n", GLLONG(ep->magic));
+		return -1;
+	}
+
+	/*
+	 *  read text
+	 */
+	addr = PADDR(GLLONG(ep->entry));
+	n = GLLONG(ep->text);
+	print("+%d", n);
+	if(dosreadseg(&file, n, addr) != n){
+		print(premature);
+		return -1;
+	}
+
+	/*
+	 *  read data (starts at first page after kernel)
+	 */
+	addr = PGROUND(addr+n);
+	n = GLLONG(ep->data);
+	print("+%d", n);
+	if(dosreadseg(&file, n, addr) != n){
+		print(premature);
+		return -1;
+	}
+
+	/*
+	 *  bss and entry point
+	 */
+	print("+%d\nstart at 0x%lux\n", GLLONG(ep->bss), GLLONG(ep->entry));
+
+	/*
+	 *  Go to new code. It's up to the program to get its PC relocated to
+	 *  the right place.
+	 */
+	b = (void (*)(void))(PADDR(GLLONG(ep->entry)));
+	(*b)();
+	return 0;
+}
+
+/*
+ *  read in a segment
+ */
+long
+dosreadseg(Dosfile *fp, long len, long addr)
+{
+	char *a;
+	long n, sofar;
+
+	a = (char *)addr;
+	for(sofar = 0; sofar < len; sofar += n){
+		n = 8*1024;
+		if(len - sofar < n)
+			n = len - sofar;
+		n = dosread(fp, a + sofar, n);
+		if(n <= 0)
+			break;
+		print(".");
+	}
+	return sofar;
+}
+
+/*
+ *  set up a dos file name
+ */
+static void
+setname(Dosfile *fp, char *from)
+{
+	char *to;
+
+	to = fp->name;
+	for(; *from && to-fp->name < 8; from++, to++){
+		if(*from == '.'){
+			from++;
+			break;
+		}
+		if(*from >= 'a' && *from <= 'z')
+			*to = *from + 'A' - 'a';
+		else
+			*to = *from;
+	}
+	while(to - fp->name < 8)
+		*to++ = ' ';
+	
+	to = fp->ext;
+	for(; *from && to-fp->ext < 3; from++, to++){
+		if(*from >= 'a' && *from <= 'z')
+			*to = *from + 'A' - 'a';
+		else
+			*to = *from;
+	}
+	while(to-fp->ext < 3)
+		*to++ = ' ';
+
+	chat("name is %8.8s %3.3s\n", fp->name, fp->ext);
+}
--- /dev/null
+++ b/os/boot/rpcg/dosfs.h
@@ -1,0 +1,110 @@
+typedef struct Dosboot	Dosboot;
+typedef struct Dos	Dos;
+typedef struct Dosdir	Dosdir;
+typedef struct Dosfile	Dosfile;
+typedef struct Dospart	Dospart;
+
+struct Dospart
+{
+	uchar flag;		/* active flag */
+	uchar shead;		/* starting head */
+	uchar scs[2];		/* starting cylinder/sector */
+	uchar type;		/* partition type */
+	uchar ehead;		/* ending head */
+	uchar ecs[2];		/* ending cylinder/sector */
+	uchar start[4];		/* starting sector */
+	uchar len[4];		/* length in sectors */
+};
+
+#define FAT12	0x01
+#define FAT16	0x04
+#define FATHUGE	0x06
+#define DMDDO	0x54
+
+struct Dosboot{
+	uchar	magic[3];
+	uchar	version[8];
+	uchar	sectsize[2];
+	uchar	clustsize;
+	uchar	nresrv[2];
+	uchar	nfats;
+	uchar	rootsize[2];
+	uchar	volsize[2];
+	uchar	mediadesc;
+	uchar	fatsize[2];
+	uchar	trksize[2];
+	uchar	nheads[2];
+	uchar	nhidden[4];
+	uchar	bigvolsize[4];
+	uchar	driveno;
+	uchar	reserved0;
+	uchar	bootsig;
+	uchar	volid[4];
+	uchar	label[11];
+	uchar	reserved1[8];
+};
+
+struct Dosfile{
+	Dos	*dos;		/* owning dos file system */
+	char	name[8];
+	char	ext[3];
+	uchar	attr;
+	long	length;
+	long	pstart;		/* physical start cluster address */
+	long	pcurrent;	/* physical current cluster address */
+	long	lcurrent;	/* logical current cluster address */
+	long	offset;
+};
+
+struct Dos{
+	int	dev;				/* device id */
+	long	(*read)(int, void*, long);	/* read routine */
+	long	(*seek)(int, long);		/* seek routine */
+
+	int	start;		/* start of file system */
+	int	sectsize;	/* in bytes */
+	int	clustsize;	/* in sectors */
+	int	clustbytes;	/* in bytes */
+	int	nresrv;		/* sectors */
+	int	nfats;		/* usually 2 */
+	int	rootsize;	/* number of entries */
+	int	volsize;	/* in sectors */
+	int	mediadesc;
+	int	fatsize;	/* in sectors */
+	int	fatclusters;
+	int	fatbits;	/* 12 or 16 */
+	long	fataddr;	/* sector number */
+	long	rootaddr;
+	long	dataaddr;
+	long	freeptr;
+
+	Dosfile	root;
+};
+
+struct Dosdir{
+	uchar	name[8];
+	uchar	ext[3];
+	uchar	attr;
+	uchar	reserved[10];
+	uchar	time[2];
+	uchar	date[2];
+	uchar	start[2];
+	uchar	length[4];
+};
+
+#define	DRONLY	0x01
+#define	DHIDDEN	0x02
+#define	DSYSTEM	0x04
+#define	DVLABEL	0x08
+#define	DDIR	0x10
+#define	DARCH	0x20
+
+extern int chatty;
+
+extern int dosboot(Dos*, char*);
+extern int dosinit(Dos*, int, int);
+extern long dosread(Dosfile*, void*, long);
+extern int dosstat(Dos*, char*, Dosfile*);
+extern int doswalk(Dosfile*, char*);
+
+extern int plan9ini(Dos*, char*);
--- /dev/null
+++ b/os/boot/rpcg/etherif.h
@@ -1,0 +1,59 @@
+/*
+ * All the goo for PC ethernet cards.
+ */
+typedef struct Card Card;
+typedef struct Type Type;
+typedef struct Ctlr Ctlr;
+
+/*
+ * Hardware interface.
+ */
+struct Card {
+	ISAConf;
+
+	int	(*reset)(Ctlr*);
+	void	(*attach)(Ctlr*);
+
+	void	*(*read)(Ctlr*, void*, ulong, ulong);
+	void	*(*write)(Ctlr*, ulong, void*, ulong);
+
+	void	(*receive)(Ctlr*);
+	void	(*transmit)(Ctlr*);
+	void	(*intr)(Ureg*, void*);
+	void	(*overflow)(Ctlr*);
+
+	uchar	bit16;			/* true if a 16 bit interface */
+	uchar	ram;			/* true if card has shared memory */
+
+	ulong	dp8390;			/* I/O address of 8390 (if any) */
+	ulong	data;			/* I/O data port if no shared memory */
+	uchar	nxtpkt;			/* software bndry */
+	uchar	tstart;			/* 8390 ring addresses */
+	uchar	pstart;
+	uchar	pstop;
+
+	uchar	dummyrr;		/* do dummy remote read */
+};
+
+/*
+ * Software controller.
+ */
+struct Ctlr {
+	Card	card;			/* hardware info */
+	int	ctlrno;
+	int	present;
+
+	Queue*	iq;
+	Queue*	oq;
+
+	int	inpackets;
+	int	outpackets;
+	int	crcs;			/* input crc errors */
+	int	oerrs;			/* output errors */
+	int	frames;			/* framing errors */
+	int	overflows;		/* packet overflows */
+	int	buffs;			/* buffering errors */
+};
+
+extern int sccethreset(Ctlr*);
+extern int	etheriq(Ctlr*, Block*, int);
--- /dev/null
+++ b/os/boot/rpcg/etherscc.c
@@ -1,0 +1,411 @@
+/*
+ * SCCn ethernet
+ */
+
+#include "u.h"
+#include "lib.h"
+#include "mem.h"
+#include "dat.h"
+#include "fns.h"
+#include "io.h"
+
+#include "etherif.h"
+
+enum {
+	Nrdre		= 32,	/* receive descriptor ring entries */
+	Ntdre		= 4,	/* transmit descriptor ring entries */
+
+	Rbsize		= ETHERMAXTU+4,		/* ring buffer size (+4 for CRC) */
+	Bufsize		= (Rbsize+7)&~7,	/* aligned */
+};
+
+enum {
+	/* ether-specific Rx BD bits */
+	RxMiss=		1<<8,
+	RxeLG=		1<<5,
+	RxeNO=		1<<4,
+	RxeSH=		1<<3,
+	RxeCR=		1<<2,
+	RxeOV=		1<<1,
+	RxeCL=		1<<0,
+	RxError=		(RxeLG|RxeNO|RxeSH|RxeCR|RxeOV|RxeCL),	/* various error flags */
+
+	/* ether-specific Tx BD bits */
+	TxPad=		1<<14,	/* pad short frames */
+	TxTC=		1<<10,	/* transmit CRC */
+	TxeDEF=		1<<9,
+	TxeHB=		1<<8,
+	TxeLC=		1<<7,
+	TxeRL=		1<<6,
+	TxeUN=		1<<1,
+	TxeCSL=		1<<0,
+
+	/* scce */
+	RXB=	1<<0,
+	TXB=	1<<1,
+	BSY=		1<<2,
+	RXF=		1<<3,
+	TXE=		1<<4,
+
+	/* gsmrl */
+	ENR=	1<<5,
+	ENT=	1<<4,
+
+	/* port A */
+	RXD1=	SIBIT(15),
+	TXD1=	SIBIT(14),
+
+	/* port B */
+	RTS1=	IBIT(19),
+
+	/* port C */
+	CTS1=	SIBIT(11),
+	CD1=	SIBIT(10),
+};
+
+typedef struct Etherparam Etherparam;
+struct Etherparam {
+	SCCparam;
+	ulong	c_pres;		/* preset CRC */
+	ulong	c_mask;		/* constant mask for CRC */
+	ulong	crcec;		/* CRC error counter */
+	ulong	alec;		/* alighnment error counter */
+	ulong	disfc;		/* discard frame counter */
+	ushort	pads;		/* short frame PAD characters */
+	ushort	ret_lim;	/* retry limit threshold */
+	ushort	ret_cnt;	/* retry limit counter */
+	ushort	mflr;		/* maximum frame length reg */
+	ushort	minflr;		/* minimum frame length reg */
+	ushort	maxd1;		/* maximum DMA1 length reg */
+	ushort	maxd2;		/* maximum DMA2 length reg */
+	ushort	maxd;		/* rx max DMA */
+	ushort	dma_cnt;	/* rx dma counter */
+	ushort	max_b;		/* max bd byte count */
+	ushort	gaddr[4];		/* group address filter */
+	ulong	tbuf0_data0;	/* save area 0 - current frm */
+	ulong	tbuf0_data1;	/* save area 1 - current frm */
+	ulong	tbuf0_rba0;
+	ulong	tbuf0_crc;
+	ushort	tbuf0_bcnt;
+	ushort	paddr[3];	/* physical address LSB to MSB increasing */
+	ushort	p_per;		/* persistence */
+	ushort	rfbd_ptr;	/* rx first bd pointer */
+	ushort	tfbd_ptr;	/* tx first bd pointer */
+	ushort	tlbd_ptr;	/* tx last bd pointer */
+	ulong	tbuf1_data0;	/* save area 0 - next frame */
+	ulong	tbuf1_data1;	/* save area 1 - next frame */
+	ulong	tbuf1_rba0;
+	ulong	tbuf1_crc;
+	ushort	tbuf1_bcnt;
+	ushort	tx_len;		/* tx frame length counter */
+	ushort	iaddr[4];		/* individual address filter*/
+	ushort	boff_cnt;	/* back-off counter */
+	ushort	taddr[3];	/* temp address */
+};
+
+typedef struct {
+	SCC*	scc;
+	int	port;
+	int	cpm;
+
+	BD*	rdr;				/* receive descriptor ring */
+	void*	rrb;				/* receive ring buffers */
+	int	rdrx;				/* index into rdr */
+
+	BD*	tdr;				/* transmit descriptor ring */
+	void*	trb;				/* transmit ring buffers */
+	int	tdrx;				/* index into tdr */
+} Mot;
+static Mot mot[MaxEther];
+
+static	int	sccid[] = {-1, SCC1ID, SCC2ID, SCC3ID, SCC4ID};
+static	int	sccparam[] = {-1, SCC1P, SCC2P, SCC3P, SCC4P};
+static	int	sccreg[] = {-1, 0xA00, 0xA20, 0xA40, 0xA60};
+static	int	sccirq[] = {-1, 0x1E, 0x1D, 0x1C, 0x1B};
+
+static void
+attach(Ctlr *ctlr)
+{
+	mot[ctlr->ctlrno].scc->gsmrl |= ENR|ENT;
+	eieio();
+}
+
+static void
+transmit(Ctlr *ctlr)
+{
+	int len;
+	Mot *motp;
+	Block *b;
+	BD *tdre;
+
+	motp = &mot[ctlr->ctlrno];
+	while(((tdre = &motp->tdr[motp->tdrx])->status & BDReady) == 0){
+		b = qget(ctlr->oq);
+		if(b == 0)
+			break;
+
+		/*
+		 * Copy the packet to the transmit buffer.
+		 */
+		len = BLEN(b);
+		memmove(KADDR(tdre->addr), b->rp, len);
+	
+		/*
+		 * Give ownership of the descriptor to the chip, increment the
+		 * software ring descriptor pointer and tell the chip to poll.
+		 */
+		tdre->length = len;
+		eieio();
+		tdre->status = (tdre->status & BDWrap) | BDReady|TxPad|BDInt|BDLast|TxTC;
+		eieio();
+		motp->scc->todr = 1<<15;	/* transmit now */
+		eieio();
+		motp->tdrx = NEXT(motp->tdrx, Ntdre);
+
+		freeb(b);
+	
+	}
+}
+
+static void
+interrupt(Ureg*, void *ap)
+{
+	int len, events, status;
+	Mot *motp;
+	BD *rdre;
+	Block *b;
+	Ctlr *ctlr;
+
+	ctlr = ap;
+	motp = &mot[ctlr->ctlrno];
+
+	/*
+	 * Acknowledge all interrupts and whine about those that shouldn't
+	 * happen.
+	 */
+	events = motp->scc->scce;
+	eieio();
+	motp->scc->scce = events;
+	eieio();
+	if(events & (TXE|BSY|RXB))
+		print("ETHER.SCC#%d: scce = 0x%uX\n", ctlr->ctlrno, events);
+	//print(" %ux|", events);
+	/*
+	 * Receiver interrupt: run round the descriptor ring logging
+	 * errors and passing valid receive data up to the higher levels
+	 * until we encounter a descriptor still owned by the chip.
+	 */
+	if(events & (RXF|RXB) || 1){
+		rdre = &motp->rdr[motp->rdrx];
+		while(((status = rdre->status) & BDEmpty) == 0){
+			if(status & RxError || (status & (BDFirst|BDLast)) != (BDFirst|BDLast)){
+				//if(status & RxBuff)
+				//	ctlr->buffs++;
+				if(status & (1<<2))
+					ctlr->crcs++;
+				if(status & (1<<1))
+					ctlr->overflows++;
+				//print("eth rx: %ux\n", status);
+				if(status & RxError)
+					print("~");
+				else if((status & BDLast) == 0)
+					print("@");
+			}
+			else{
+				/*
+				 * We have a packet. Read it into the next
+				 * free ring buffer, if any.
+				 */
+				len = rdre->length-4;
+				if((b = iallocb(len)) != 0){
+					memmove(b->wp, KADDR(rdre->addr), len);
+					b->wp += len;
+					etheriq(ctlr, b, 1);
+				}
+			}
+
+			/*
+			 * Finished with this descriptor, reinitialise it,
+			 * give it back to the chip, then on to the next...
+			 */
+			rdre->length = 0;
+			rdre->status = (rdre->status & BDWrap) | BDEmpty | BDInt;
+			eieio();
+
+			motp->rdrx = NEXT(motp->rdrx, Nrdre);
+			rdre = &motp->rdr[motp->rdrx];
+		}
+	}
+
+	/*
+	 * Transmitter interrupt: handle anything queued for a free descriptor.
+	 */
+	if(events & TXB)
+		transmit(ctlr);
+	if(events & TXE)
+		cpmop(RestartTx, motp->cpm, 0);
+}
+
+static void
+ringinit(Mot* motp)
+{
+	int i, x;
+
+	/*
+	 * Initialise the receive and transmit buffer rings. The ring
+	 * entries must be aligned on 16-byte boundaries.
+	 */
+	if(motp->rdr == 0)
+		motp->rdr = bdalloc(Nrdre);
+	if(motp->rrb == 0)
+		motp->rrb = ialloc(Nrdre*Bufsize, 0);
+	x = PADDR(motp->rrb);
+	for(i = 0; i < Nrdre; i++){
+		motp->rdr[i].length = 0;
+		motp->rdr[i].addr = x;
+		motp->rdr[i].status = BDEmpty|BDInt;
+		x += Bufsize;
+	}
+	motp->rdr[i-1].status |= BDWrap;
+	motp->rdrx = 0;
+
+	if(motp->tdr == 0)
+		motp->tdr = bdalloc(Ntdre);
+	if(motp->trb == 0)
+		motp->trb = ialloc(Ntdre*Bufsize, 0);
+	x = PADDR(motp->trb);
+	for(i = 0; i < Ntdre; i++){
+		motp->tdr[i].addr = x;
+		motp->tdr[i].length = 0;
+		motp->tdr[i].status = TxPad|BDInt|BDLast|TxTC;
+		x += Bufsize;
+	}
+	motp->tdr[i-1].status |= BDWrap;
+	motp->tdrx = 0;
+}
+
+/*
+ * This follows the MPC823 user guide: section16.9.23.7's initialisation sequence,
+ * except that it sets the right bits for the MPC823ADS board when SCC2 is used,
+ * and those for the 860/821 development board for SCC1.
+ */
+static void
+sccsetup(Mot *ctlr, SCC *scc, uchar *ea)
+{
+	int i, rcs, tcs, w;
+	Etherparam *p;
+	IMM *io;
+
+
+	i = 2*(ctlr->port-1);
+	io = ioplock();
+	w = (TXD1|RXD1)<<i;	/* TXDn and RXDn in port A */
+	io->papar |= w;	/* enable TXDn and RXDn pins */
+	io->padir &= ~w;
+	io->paodr &= ~w;	/* not open drain */
+
+	w = (CD1|CTS1)<<i;	/* CLSN and RENA: CDn and CTSn in port C */
+	io->pcpar &= ~w;	/* enable CLSN (CTSn) and RENA (CDn) */
+	io->pcdir &= ~w;
+	io->pcso |= w;
+	iopunlock();
+
+	/* clocks and transceiver control: details depend on the board's wiring */
+	archetherenable(ctlr->cpm, &rcs, &tcs);
+
+	sccnmsi(ctlr->port, rcs, tcs);	/* connect the clocks */
+
+	p = (Etherparam*)KADDR(sccparam[ctlr->port]);
+	memset(p, 0, sizeof(*p));
+	p->rfcr = 0x18;
+	p->tfcr = 0x18;
+	p->mrblr = Bufsize;
+	p->rbase = PADDR(ctlr->rdr);
+	p->tbase = PADDR(ctlr->tdr);
+
+	cpmop(InitRxTx, ctlr->cpm, 0);
+
+	p->c_pres = ~0;
+	p->c_mask = 0xDEBB20E3;
+	p->crcec = 0;
+	p->alec = 0;
+	p->disfc = 0;
+	p->pads = 0x8888;
+	p->ret_lim = 0xF;
+	p->mflr = Rbsize;
+	p->minflr = ETHERMINTU+4;
+	p->maxd1 = Bufsize;
+	p->maxd2 = Bufsize;
+	p->p_per = 0;	/* only moderate aggression */
+
+	for(i=0; i<Eaddrlen; i+=2)
+		p->paddr[2-i/2] = (ea[i+1]<<8)|ea[i];	/* it's not the obvious byte order */
+
+	scc->psmr = (2<<10)|(5<<1);	/* 32-bit CRC, ignore 22 bits before SFD */
+	scc->dsr = 0xd555;
+	scc->gsmrh = 0;	/* normal operation */
+	scc->gsmrl = (1<<28)|(4<<21)|(1<<19)|0xC;	/* transmit clock invert, 48 bit preamble, repetitive 10 preamble, ethernet */
+	eieio();
+	scc->scce = ~0;	/* clear all events */
+	eieio();
+	scc->sccm = TXE | RXF | TXB;	/* enable interrupts */
+	eieio();
+
+	io = ioplock();
+	w = RTS1<<(ctlr->port-1);	/* enable TENA pin (RTSn) */
+	io->pbpar |= w;
+	io->pbdir |= w;
+	iopunlock();
+
+	/* gsmrl enable is deferred until attach */
+}
+
+/*
+ * Prepare the SCCx ethernet for booting.
+ */
+int
+sccethreset(Ctlr* ctlr)
+{
+	uchar ea[Eaddrlen];
+	Mot *motp;
+	SCC *scc;
+	char line[50], def[50];
+
+	/*
+	 * Since there's no EPROM, insist that the configuration entry
+	 * (see conf.c and flash.c) holds the Ethernet address.
+	 */
+	memset(ea, 0, Eaddrlen);
+	if(memcmp(ea, ctlr->card.ea, Eaddrlen) == 0){
+		print("no preset Ether address\n");
+		for(;;){
+			strcpy(def, "00108bf12900");	/* valid MAC address to be used only for initial configuration */
+			if(getstr("ether MAC address", line, sizeof(line), def) < 0)
+				return -1;
+			if(parseether(ctlr->card.ea, line) >= 0 || ctlr->card.ea[0] == 0xFF)
+				break;
+			print("invalid MAC address\n");
+		}
+	}
+
+	scc = IOREGS(sccreg[ctlr->card.port], SCC);
+	ctlr->card.irq = VectorCPIC+sccirq[ctlr->card.port];
+
+	motp = &mot[ctlr->ctlrno];
+	motp->scc = scc;
+	motp->port = ctlr->card.port;
+	motp->cpm = sccid[ctlr->card.port];
+
+	ringinit(motp);
+
+	sccsetup(motp, scc, ctlr->card.ea);
+
+	/* enable is deferred until attach */
+
+	ctlr->card.reset = sccethreset;
+	ctlr->card.attach = attach;
+	ctlr->card.transmit = transmit;
+	ctlr->card.intr = interrupt;
+
+	return 0;
+}
--- /dev/null
+++ b/os/boot/rpcg/fblt.c
@@ -1,0 +1,531 @@
+#include <u.h>
+#include <libc.h>
+#include <libg.h>
+#include <gnot.h>
+
+/*
+ * bitblt operates a 'word' at a time.
+ * WBITS is the number of bits in a word
+ * LWBITS=log2(WBITS),
+ * W2L is the number of words in a long
+ * WMASK has bits set for the low order word of a long
+ * WType is a pointer to a word
+ */
+#ifndef WBITS
+#define WBITS	32
+#define LWBITS	5
+#define	W2L	1
+#define WMASK	~0UL
+typedef ulong	*WType;
+#endif
+
+#define DEBUG 
+
+#ifdef TEST
+/*
+ * globals used for testing
+ */
+int	FORCEFORW;
+int	FORCEBAKW;
+GBitmap	*curdm, *cursm;
+Point	curpt;
+Rectangle curr;
+Fcode	curf;
+void	*mem;
+#endif
+
+static void
+gbitexplode(ulong sw, ulong *buf, int sdep, int x)
+{
+	int j, o, q, n, nw, inc, qinc;
+	ulong s, dw, pix;
+
+	inc = 1 << sdep;
+	pix = (1 << inc) - 1;
+	nw = 1 << x;
+	n = 32 >> x;
+	qinc = (nw << sdep) - inc;
+	for(o = 32 - n; o >= 0; o -= n){
+		dw = 0;
+		s = sw >> o;
+		q = 0;
+		for(j = 0; j < n; j += inc){
+			dw |= (s & (pix << j)) << q;
+			q += qinc;
+		}
+		for(j = 0; j < x; j++)
+			dw |= dw << (inc << j);
+		*buf++ = dw;
+	}
+}
+
+/*
+void
+main(void)
+{
+	ulong buf[128];
+
+	gbitexplode(0x7777, buf, 0, 3);
+	exits(0);
+}
+*/
+
+void
+gbitblt(GBitmap *dm, Point pt, GBitmap *sm, Rectangle r, Fcode fcode)
+{
+	int	width;		/* width in bits of dst */
+	int	wwidth;		/* floor width in words */
+	int	height;		/* height in pixels minus 1 */
+	int	sdep;		/* src ldepth */
+	int 	ddep;		/* dst ldepth */
+	int	deltadep;	/* diff between ldepths */
+	int	sspan;		/* words between scanlines in src */
+	int	dspan;		/* words between scanlines in dst */
+	int	soff;		/* bit offset of src start point */
+	int	sdest;		/* bit offset of src start point that matches doff when expanded */
+	int	doff;		/* bit offset of dst start point */
+	int	delta;		/* amount to shift src by */
+	int	sign;		/* of delta */
+	ulong	*saddr;
+	ulong	*daddr;
+	ulong	*s;
+	ulong	*d;
+	ulong	mask;
+	ulong	tmp;		/* temp storage source word */
+	ulong	sw;		/* source word constructed */
+	ulong	dw;		/* dest word fetched */
+	ulong	lmask;		/* affected pixels in leftmost dst word */
+	ulong	rmask;		/* affected pixels in rightmost dst word */
+	int	i;
+	int	j;
+	ulong	buf[32];	/* for expanding a source */
+	ulong	*p;		/* pointer into buf */
+	int	spare;		/* number of words already converted */
+
+
+#ifdef TEST
+	curdm = dm;
+	cursm = sm;
+	curpt = pt;
+	curr = r;
+	curf = fcode;
+#endif
+
+	gbitbltclip(&dm);
+
+	width = r.max.x - r.min.x;
+	if(width <= 0)
+		return;
+	height = r.max.y - r.min.y - 1;
+	if(height < 0)
+		return;
+
+	ddep = dm->ldepth;
+	pt.x <<= ddep;
+	width <<= ddep;
+
+	sdep = sm->ldepth;
+	r.min.x <<= sdep;
+	r.max.x <<= sdep;
+
+	dspan = dm->width * W2L;
+	sspan = sm->width * W2L;
+
+	daddr = (ulong*)((WType)dm->base
+			+ dm->zero*W2L + pt.y*dspan
+			+ (pt.x >> LWBITS));
+	saddr = (ulong*)((WType)sm->base
+			+ sm->zero*W2L + r.min.y*sspan
+			+ (r.min.x >> LWBITS));
+
+	doff = pt.x & (WBITS - 1);
+	lmask = WMASK >> doff;
+	rmask = (WMASK << (WBITS - ((doff+width) & (WBITS-1))))&WMASK;
+	if(!rmask)
+		rmask = WMASK;
+	soff = r.min.x & (WBITS-1);
+	wwidth = ((pt.x+width-1)>>LWBITS) - (pt.x>>LWBITS);
+
+	if(sm == dm){
+#ifdef TEST
+		if(!FORCEBAKW &&
+		   (FORCEFORW || sm != dm || saddr > daddr ||
+		    (saddr == daddr && soff > doff)))
+			;
+		else{
+			daddr += height * dspan;
+			saddr += height * sspan;
+			sspan -= 2 * W2L * sm->width;
+			dspan -= 2 * W2L * dm->width;
+		}
+#else
+		if(r.min.y < pt.y){	/* bottom to top */
+			daddr += height * dspan;
+			saddr += height * sspan;
+			sspan -= 2 * W2L * sm->width;
+			dspan -= 2 * W2L * dm->width;
+		}else if(r.min.y == pt.y && r.min.x < pt.x)
+			abort()/*goto right*/;
+#endif
+	}
+	if(wwidth == 0)		/* collapse masks for narrow cases */
+		lmask &= rmask;
+	fcode &= F;
+
+	deltadep = ddep - sdep;
+	sdest = doff >> deltadep;
+	delta = soff - sdest;
+	sign = 0;
+	if(delta < 0){
+		sign = 1;
+		delta = -delta;
+	}
+
+	p = 0;
+	for(j = 0; j <= height; j++){
+		d = daddr;
+		s = saddr;
+		mask = lmask;
+		tmp = 0;
+		if(!sign)
+			tmp = *s++;
+		spare = 0;
+		for(i = wwidth; i >= 0; i--){
+			if(spare)
+				sw = *p++;
+			else{
+				if(sign){
+					sw = tmp << (WBITS-delta);
+					tmp = *s++;
+					sw |= tmp >> delta;
+				}else{
+					sw = tmp << delta;
+					tmp = *s++;
+					if(delta)
+						sw |= tmp >> (WBITS-delta);
+				}
+				spare = 1 << deltadep;
+				if(deltadep >= 1){
+					gbitexplode(sw, buf, sdep, deltadep);
+					p = buf;
+					sw = *p++;
+				}
+			}
+
+			dw = *d;
+			switch(fcode){		/* ltor bit aligned */
+			case Zero:	*d = dw & ~mask;		break;
+			case DnorS:	*d = dw ^ ((~sw | dw) & mask);	break;
+			case DandnotS:	*d = dw ^ ((sw & dw) & mask);	break;
+			case notS:	*d = dw ^ ((~sw ^ dw) & mask);	break;
+			case notDandS:	*d = dw ^ ((sw | dw) & mask);	break;
+			case notD:	*d = dw ^ mask;			break;
+			case DxorS:	*d = dw ^ (sw & mask);		break;
+			case DnandS:	*d = dw ^ ((sw | ~dw) & mask);	break;
+			case DandS:	*d = dw ^ ((~sw & dw) & mask);	break;
+			case DxnorS:	*d = dw ^ (~sw & mask);		break;
+			case D:						break;
+			case DornotS:	*d = dw | (~sw & mask);		break;
+			case S:		*d = dw ^ ((sw ^ dw) & mask);	break;
+			case notDorS:	*d = dw ^ (~(sw & dw) & mask);	break;
+			case DorS:	*d = dw | (sw & mask);		break;
+			case F:		*d = dw | mask;			break;
+			}
+			d++;
+
+			mask = WMASK;
+			if(i == 1)
+				mask = rmask;
+			spare--;
+		}
+		saddr += sspan;
+		daddr += dspan;
+	}
+}
+
+#ifdef TEST
+void	prprog(void);
+GBitmap *bb1, *bb2;
+ulong	*src, *dst, *xdst, *xans;
+int	swds, dwds;
+long	ticks;
+int	timeit;
+
+long
+func(int f, long s, int sld, long d, int dld)
+{
+	long a;
+	int sh, i, db, sb;
+
+	db = 1 << dld;
+	sb = 1 << sld;
+	sh = db - sb;
+	if(sh > 0) {
+		a = s;
+		for(i = sb; i<db; i += sb){
+			a <<= sb;
+			s |= a;
+		}
+	} else if(sh < 0)
+		s >>= -sh;
+
+	switch(f){
+	case Zero:	d = 0;			break;
+	case DnorS:	d = ~(d|s);		break;
+	case DandnotS:	d = d & ~s;		break;
+	case notS:	d = ~s;			break;
+	case notDandS:	d = ~d & s;		break;
+	case notD:	d = ~d;			break;
+	case DxorS:	d = d ^ s;		break;
+	case DnandS:	d = ~(d&s);		break;
+	case DandS:	d = d & s;		break;
+	case DxnorS:	d = ~(d^s);		break;
+	case S:		d = s;			break;
+	case DornotS:	d = d | ~s;		break;
+	case D:		d = d;			break;
+	case notDorS:	d = ~d | s;		break;
+	case DorS:	d = d | s;		break;
+	case F:		d = ~0;			break;
+	}
+
+	d &= ((1<<db)-1);
+	return d;
+}
+
+void
+run(int fr, int to, int w, int op)
+{
+	int i, j, f, t, fy, ty;
+	extern long *_clock;
+
+	fr += bb2->r.min.x;
+	to += bb1->r.min.x;
+	fy = bb2->r.min.y + 1;
+	ty = bb1->r.min.y + 1;
+	if(timeit) {
+		memcpy(dst, xdst, dwds * sizeof(long));
+		ticks -= *_clock;
+		gbitblt(bb1, Pt(to,ty), bb2, Rect(fr,fy,fr+w,fy+2), op);
+		ticks += *_clock;
+		return;
+	}
+	f = fr;
+	t = to;
+	memcpy(dst, xdst, dwds * sizeof(long));
+	for(i=0; i<w; i++) {
+		gbitblt(bb1, Pt(t,ty), bb2, Rect(f,fy,f+1,fy+1), op);
+		gbitblt(bb1, Pt(t,ty+1), bb2, Rect(f,fy+1,f+1,fy+2), op);
+		f++;
+		t++;
+	}
+	memcpy(xans, dst, dwds * sizeof(long));
+
+	memcpy(dst, xdst, dwds * sizeof(long));
+	gbitblt(bb1, Pt(to,ty), bb2, Rect(fr,fy,fr+w,fy+2), op);
+
+	if(memcmp(xans, dst, dwds * sizeof(long))) {
+		/*
+		 * print src and dst row offset, width in bits, and forw/back
+		 * then print for each of the four rows: the source (s),
+		 * the dest (d), the good value of the answer (g),
+		 * and the actual bad value of the answer (b)
+		 */
+		print("fr=%d to=%d w=%d fb=%d%d\n",
+			fr, to, w, FORCEFORW, FORCEBAKW);
+		print("dst bitmap b %#lux, z %d, w %d, ld %d, r [%d,%d][%d,%d]\n",
+			bb1->base, bb1->zero, bb1->width, bb1->ldepth,
+			bb1->r.min.x, bb1->r.min.y, bb1->r.max.x, bb1->r.max.y);
+		print("src bitmap b %#lux, z %d, w %d, ld %d, r [%d,%d][%d,%d]\n",
+			bb2->base, bb2->zero, bb2->width, bb2->ldepth,
+			bb2->r.min.x, bb2->r.min.y, bb2->r.max.x, bb2->r.max.y);
+		for(j=0; 7*j < dwds; j++) {
+			print("\ns");
+			for(i=0; i<7 && 7*j+i < dwds; i++)
+				print(" %.8lux", src[7*j + i]);
+			print("\nd");
+			for(i=0; i<7 && 7*j+i < dwds; i++)
+				print(" %.8lux", xdst[7*j + i]);
+			print("\ng");
+			for(i=0; i<7 && 7*j+i < dwds; i++)
+				print(" %.8lux", xans[7*j + i]);
+			print("\nb");
+			for(i=0; i<7 && 7*j+i < dwds; i++)
+				print(" %.8lux", dst[7*j + i]);
+			print("\n");
+		}
+		prprog();
+	}
+}
+
+void
+prprog(void)
+{
+	exits(0);
+}
+
+int
+main(int argc, char *argv[])
+{
+	int f, t, w, i, sld, dld, op, iters, simple;
+	ulong s, d, spix, dpix, apix, fpix, m, *ps, *pd;
+	Point sorg, dorg;
+	GBitmap *bs, *bd;
+	long seed;
+	char *ct;
+
+	sld = 0;
+	dld = 0;
+	timeit = 0;
+	iters = 200;
+	simple = 0;
+	ARGBEGIN {
+	case 'i':
+		iters = atoi(ARGF());
+		break;
+	case 's':
+		simple = 1;
+		break;
+	case 't':
+		timeit = 1;
+		ct = ARGF();
+		if(ct)
+			iters = atoi(ct);
+		break;
+	} ARGEND
+	if(argc > 0)
+		sld = atoi(argv[0]);
+	if(argc > 1)
+		dld = atoi(argv[1]);
+	if(!timeit && !simple) {
+		seed = time(0);
+		print("seed %lux\n", seed); srand(seed);	/**/
+	}
+
+	print("sld %d dld %d\n", sld, dld);
+	op = 1;
+
+	/* bitmaps for 1-bit tests */
+	bd = gballoc(Rect(0,0,32,1), dld);
+	bs = gballoc(Rect(0,0,32,1), sld);
+	for(i=0; i<bs->width; i++)
+		bs->base[i] = lrand();
+
+	/* bitmaps for rect tests */
+	if(simple) {
+		dorg = Pt(0,0);
+		sorg = Pt(0,0);
+	} else {
+		dorg = Pt(nrand(63)-31,nrand(63)-31);
+		sorg = Pt(nrand(63)-31,nrand(63)-31);
+	}
+	bb1 = gballoc(Rpt(dorg,add(dorg,Pt(200,4))), dld);
+	bb2 = gballoc(Rpt(sorg,add(sorg,Pt(200,4))), sld);
+	dwds = bb1->width * Dy(bb1->r);
+	swds = bb2->width * Dy(bb2->r);
+	dst = bb1->base;
+	src = bb2->base;
+	xdst = malloc(dwds * sizeof(long));
+	xans =  malloc(dwds * sizeof(long));
+	for(i=0; i<swds; i++)
+		src[i] = lrand();
+	for(i=0; i<dwds; i++)
+		xdst[i] = lrand();
+
+loop:
+	print("Op %d\n", op);
+	if(!timeit) {
+		print("one pixel\n");
+		ps = bs->base;
+		pd = bd->base;
+		FORCEFORW = 1;
+		FORCEBAKW = 0;
+		for(i=0; i<1000; i++, FORCEFORW = !FORCEFORW, FORCEBAKW = !FORCEBAKW) {
+			f = nrand(32 >> sld);
+			t = nrand(32 >> dld);
+			s = lrand();
+			d = lrand();
+			ps[0] = s;
+			pd[0] = d;
+#ifdef T386
+			spix = (byterev(s) >> (32 - ((f+1)<<sld))) & ((1 << (1<<sld)) - 1);
+			dpix = (byterev(d) >> (32 - ((t+1)<<dld))) & ((1 << (1<<dld)) - 1);
+#else
+			spix = (s >> (32 - ((f+1)<<sld))) & ((1 << (1<<sld)) - 1);
+			dpix = (d >> (32 - ((t+1)<<dld))) & ((1 << (1<<dld)) - 1);
+#endif
+#ifdef T386
+			apix = byterev(func(op, spix, sld, dpix, dld) << (32 - ((t+1)<<dld)));
+#else
+			apix = func(op, spix, sld, dpix, dld) << (32 - ((t+1)<<dld));
+#endif
+			gbitblt(bd, Pt(t,0), bs, Rect(f,0,f+1,1), op);
+			if(ps[0] != s) {
+				print("bb src %.8lux %.8lux %d %d\n", ps[0], s, f, t);
+				exits("error");
+			}
+			m = ((1 << (1<<dld)) - 1) << (32 - ((t+1)<<dld));
+#ifdef T386
+			m = byterev(m);
+#endif
+			if((pd[0] & ~m) != (d & ~m)) {
+					print("bb dst1 %.8lux %.8lux\n",
+						s, d);
+					print("bb      %.8lux %.8lux %d %d\n",
+						ps[0], pd[0], f, t);
+					prprog();
+					exits("error");
+			}
+			if((pd[0] & m) != apix) {
+				spix <<= 32 - ((f+1)<<sld);
+				dpix <<= 32 - ((t+1)<<dld);
+#ifdef T386
+				spix = byterev(spix);
+				dpix = byterev(dpix);
+#endif
+				print("bb dst2 %.8lux %.8lux\n",
+					s, d);
+				print("bb      %.8lux %.8lux %d %d\n",
+					ps[0], pd[0], f, t);
+				print("bb      %.8lux %.8lux %.8lux %.8lux\n",
+					spix, dpix, apix, pd[0] & m);
+				prprog();
+				exits("error");
+			}
+		}
+	}
+
+	print("for\n");
+	FORCEFORW = 1;
+	FORCEBAKW = 0;
+
+	for(i=0; i<iters; i++) {
+		f = nrand(64);
+		t = nrand(64);
+		w = nrand(130);
+		run(f, t, w, op);
+	}
+
+	if(sld == dld) {
+		print("bak\n");
+		FORCEFORW = 0;
+		FORCEBAKW = 1;
+	
+		for(i=0; i<iters; i++) {
+			f = nrand(64);
+			t = nrand(64);
+			w = nrand(130);
+			run(f, t, w, op);
+		}
+	}
+
+	if(op < F) {
+		op++;
+		goto loop;
+	}
+	if(timeit)
+		print("time: %d ticks\n", ticks);
+	exits(0);
+}
+
+
+#endif
--- /dev/null
+++ b/os/boot/rpcg/flash.c
@@ -1,0 +1,212 @@
+#include "boot.h"
+
+typedef struct Flashdev Flashdev;
+struct Flashdev {
+	uchar*	base;
+	int	size;
+	uchar*	exec;
+	char*	config;
+	int	conflen;
+};
+
+enum {
+	FLASHSEG = 256*1024,
+	CONFIGLIM = FLASHSEG,
+	BOOTOFF = FLASHSEG,
+	BOOTLEN = 3*FLASHSEG,	/* third segment might be filsys */
+	/* rest of flash is free */
+};
+
+static Flashdev flash;
+
+/*
+ * configuration data is written between the bootstrap and
+ * the end of region 0. the region ends with allocation descriptors
+ * of the following form:
+ *
+ * byte order is big endian
+ *
+ * the last valid region found that starts with the string "#plan9.ini\n" is plan9.ini
+ */
+typedef struct Flalloc Flalloc;
+struct Flalloc {
+	ulong	check;	/* checksum of data, or ~0 */
+	ulong	base;	/* base of region; ~0 if unallocated, 0 if deleted */
+	uchar	len[3];
+	uchar	tag;		/* see below */
+	uchar	sig[4];
+};
+
+enum {
+	/* tags */
+	Tdead=	0,
+	Tboot=	0x01,	/* space reserved for boot */
+	Tconf=	0x02,	/* configuration data */
+	Tnone=	0xFF,
+
+	Noval=	~0,
+};
+
+static char flashsig[] = {0xF1, 0xA5, 0x5A, 0x1F};
+static char conftag[] = "#plan9.ini\n";
+
+static ulong
+checksum(uchar* p, int n)
+{
+	ulong s;
+
+	for(s=0; --n >= 0;)
+		s += *p++;
+	return s;
+}
+
+static int
+validptr(Flalloc *ap, uchar *p)
+{
+	return p > (uchar*)&end && p < (uchar*)ap;
+}
+
+static int
+flashcheck(Flalloc *ap, char **val, int *len)
+{
+	uchar *base;
+	int n;
+
+	if(ap->base == Noval || ap->base >= FLASHSEG || ap->tag == Tnone)
+		return 0;
+	base = flash.base+ap->base;
+	if(!validptr(ap, base))
+		return 0;
+	n = (((ap->len[0]<<8)|ap->len[1])<<8)|ap->len[2];
+	if(n == 0xFFFFFF)
+		n = 0;
+	if(n < 0)
+		return 0;
+	if(n > 0 && !validptr(ap, base+n-1))
+		return 0;
+	if(ap->check != Noval && checksum(base, n) != ap->check){
+		print("flash: bad checksum\n");
+		return 0;
+	}
+	*val = (char*)base;
+	*len = n;
+	return 1;
+}
+
+int
+flashinit(void)
+{
+	int len;
+	char *val;
+	Flalloc *ap;
+	void *addr;
+	long mbytes;
+	char type[20];
+
+	flash.base = 0;
+	flash.exec = 0;
+	flash.size = 0;
+	if(archflashreset(type, &addr, &mbytes) < 0){
+		print("flash: flash not present or not enabled\n");	/* shouldn't happen */
+		return 0;
+	}
+	flash.size = mbytes;
+	flash.base = addr;
+	flash.exec = flash.base + BOOTOFF;
+	flash.config = nil;
+	flash.conflen = 0;
+
+	for(ap = (Flalloc*)(flash.base+CONFIGLIM)-1; memcmp(ap->sig, flashsig, 4) == 0; ap--){
+		if(0)
+			print("conf #%8.8lux: #%x #%6.6lux\n", ap, ap->tag, ap->base);
+		if(ap->tag == Tconf &&
+		   flashcheck(ap, &val, &len) &&
+		   len >= sizeof(conftag)-1 &&
+		   memcmp(val, conftag, sizeof(conftag)-1) == 0){
+			flash.config = val;
+			flash.conflen = len;
+			if(0)
+				print("flash: found config %8.8lux(%d):\n%s\n", val, len, val);
+		}
+	}
+	if(flash.config == nil)
+		print("flash: no config\n");
+	else
+		print("flash config %8.8lux(%d):\n%s\n", flash.config, flash.conflen, flash.config);
+	if(issqueezed(flash.exec) == Q_MAGIC){
+		print("flash: squeezed powerpc kernel installed\n");
+		return 1<<0;
+	}
+	if(GLLONG(flash.exec) == Q_MAGIC){
+		print("flash: unsqueezed powerpc kernel installed\n");
+		return 1<<0;
+	}
+	flash.exec = 0;
+	print("flash: no powerpc kernel in Flash\n");
+	return 0;
+}
+
+char*
+flashconfig(int)
+{
+	return flash.config;
+}
+
+int
+flashbootable(int)
+{
+	return flash.exec != nil && (issqueezed(flash.exec) || GLLONG(flash.exec) == Q_MAGIC);
+}
+
+int
+flashboot(int)
+{
+	ulong entry, addr;
+	void (*b)(void);
+	Exec *ep;
+	Block in;
+	long n;
+	uchar *p;
+
+	if(flash.exec == 0)
+		return -1;
+	p = flash.exec;
+	if(GLLONG(p) == Q_MAGIC){
+		/* unsqueezed: copy data and perhaps text, then jump to it */
+		ep = (Exec*)p;
+		entry = PADDR(GLLONG(ep->entry));
+		p += sizeof(Exec);
+		addr = entry;
+		n = GLLONG(ep->text);
+		if(addr != (ulong)p){
+			memmove((void*)addr, p, n);
+			print("text: %8.8lux <- %8.8lux [%ld]\n", addr, p, n);
+		}
+		p += n;
+		if(entry >= FLASHMEM)
+			addr = 3*BY2PG;	/* kernel text is in Flash, data in RAM */
+		else
+			addr = PGROUND(addr+n);
+		n = GLLONG(ep->data);
+		memmove((void*)addr, p, n);
+		print("data: %8.8lux <- %8.8lux [%ld]\n", addr, p, n);
+	}else{
+		in.data = p;
+		in.rp = in.data;
+		in.lim = p+BOOTLEN;
+		in.wp = in.lim;
+		n = unsqueezef(&in, &entry);
+		if(n < 0)
+			return -1;
+	}
+	print("entry=0x%lux\n", entry);
+	uartwait();
+	scc2stop();
+	/*
+	 *  Go to new code. It's up to the program to get its PC relocated to
+	 *  the right place.
+	 */
+	b = (void (*)(void))KADDR(PADDR(entry));
+	(*b)();
+	return -1;
+}
--- /dev/null
+++ b/os/boot/rpcg/fns.h
@@ -1,0 +1,118 @@
+Alarm*	alarm(int, void (*)(Alarm*), void*);
+void	alarminit(void);
+void	archbacklight(int);
+char*	archconfig(void);
+void	archdisableuart(int);
+void	archenableuart(int, int);
+void	archenableusb(int);
+void	archetherdisable(int);
+int	archetherenable(int, int*, int*);
+int	archflashreset(char*, void**, long*);
+void	archinit(void);
+int	archoptionsw(void);
+int	bootp(int, char*);
+void	cancel(Alarm*);
+void	checkalarms(void);
+void	clockinit(void);
+void	clockintr(Ureg*, void*);
+void	consinit(void);
+void	cpminit(void);
+void	cpuidprint(void);
+#define	dcflush(a,b)
+void	delay(int);
+void	eieio(void);
+uchar*	etheraddr(int);
+int	etherinit(void);
+int	etherrxpkt(int, Etherpkt*, int);
+int	ethertxpkt(int, Etherpkt*, int, int);
+void	exception(void);
+int	flashboot(int);
+int	flashbootable(int);
+char*	flashconfig(int);
+int	flashinit(void);
+void	free(void*);
+void	freeb(Block*);
+int	getcfields(char*, char**, int, char*);
+char*	getconf(char*);
+ulong	getdec(void);
+ulong	gethid0(void);
+ulong	getimmr(void);
+ulong	getmsr(void);
+ulong	getpvr(void);
+int	getstr(char*, char*, int, char*);
+ulong	gettbl(void);
+ulong	gettbu(void);
+int	hardinit(void);
+long	hardread(int, void*, long);
+long	hardseek(int, long);
+long	hardwrite(int, void*, long);
+long	i2crecv(int, void*, long);
+long	i2csend(int, void*, long);
+void	i2csetup(void);
+void*	ialloc(ulong, int);
+Block*	iallocb(int);
+void	idle(void);
+int	isaconfig(char*, int, ISAConf*);
+int	issqueezed(uchar*);
+void	kbdchar(Queue*, int);
+void	kbdinit(void);
+void	kbdreset(void);
+void	machinit(void);
+void*	malloc(ulong);
+ulong	mapalloc(RMap*, ulong, int, int);
+void	mapfree(RMap*, ulong, int);
+void	mapinit(RMap*, Map*, int);
+void	meminit(void);
+void	microdelay(int);
+void	mmuinit(void);
+int	optionsw(void);
+void	panic(char*, ...);
+int	parseether(uchar*, char*);
+int	plan9boot(int, long (*)(int, long), long (*)(int, void*, long));
+void	putdec(ulong);
+void	puthid0(ulong);
+void	putmsr(ulong);
+int	qbgetc(Queue*);
+void	qbputc(Queue*, int);
+void	qbwrite(Queue*, Block*);
+Block*	qget(Queue*);
+long	qlen(Queue*);
+Queue*	qopen(int, int, void (*)(void*), void*);
+#define	qpass	qbwrite
+void	scc2stop(void);
+void	sccnmsi(int, int, int);
+void	sched(void);
+void	screeninit(void);
+void	screenputs(char*, int);
+void	sdraminit(ulong);
+Partition*	sethardpart(int, char*);
+Partition*	setscsipart(int, char*);
+void	setvec(int, void (*)(Ureg*, void*), void*);
+int	splhi(void);
+int	spllo(void);
+void	splx(int);
+void	trapinit(void);
+void	uartputs(char*, int);
+void	uartsetboot(void (*f)(uchar*, int));
+void	uartspecial(int, int, Queue**, Queue**, void(*)(Queue*,int));
+void	uartwait(void);
+long	unsqueezef(Block*, ulong*);
+
+#define	GSHORT(p)	(((p)[1]<<8)|(p)[0])
+#define	GLONG(p)	((GSHORT(p+2)<<16)|GSHORT(p))
+#define	GLSHORT(p)	(((p)[0]<<8)|(p)[1])
+#define	GLLONG(p)	((GLSHORT(p)<<16)|GLSHORT(p+2))
+
+#define KADDR(a)	((void*)((ulong)(a)|KZERO))
+#define PADDR(a)	((((ulong)(a)&KSEGM)!=KSEG0)?(ulong)(a):((ulong)(a)&~KZERO))
+
+/* IBM bit field order */
+#define	IBIT(b)	((ulong)1<<(31-(b)))
+#define	SIBIT(n)	((ushort)1<<(15-(n)))
+
+#define IOREGS(x, T)	((T*)((char*)m->iomem+(x)))
+
+int	uartinit(void);
+Partition*	setuartpart(int, char*);
+long	uartread(int, void*, long);
+long	uartseek(int, long);
--- /dev/null
+++ b/os/boot/rpcg/g.mx
@@ -1,0 +1,1987 @@
+S0030000FC
+S325000100007C6000A63C800000608480427C632078606310007C0004AC7C6001247C0004AC52
+S325000100204C00012C7C908AA63C800A007C908BA64C00012C3C800C007C908BA64C00012C0A
+S325000100403C8004007C908BA64C00012C7C0004AC7C988AA63C800A007C988BA64C00012C2F
+S325000100603C800C007C988BA64C00012C3C8004007C988BA64C00012C388000077C9E23A6D3
+S325000100807CBE9AA63C80FA207C9E9BA63860FF88906400043C6001016063244090640000DE
+S325000100A0380000003C40001460427FF83C40001460427FF83C60FFC26063D7103882800866
+S325000100C07C0418004182002C38A2A1F87CA4285038A500037CA516707CA903A63884FFFC7B
+S325000100E03863FFFC84A3000494A400044200FFF83C60FFC2606302007C6803A64E80002026
+S325000101003862A1F8388227107C8320514081001C7C8416707C8903A63863FFFC3800000065
+S32500010120940300044200FFFC3822A3C89022801438210FF8380000004800AA01480086F587
+S32500010140480000003D00FA40806800003CA008007C63287890680000480000007C6000A6CC
+S325000101604E8000207C0004AC7C6001247C0004AC4C00012C4E8000207C0006AC4E800020AE
+S325000101804E8000207C6000A6606480007C0004AC7C8001247C0004AC4C00012C4E800020C4
+S325000101A07C6000A65464045E7C0004AC7C8001247C0004AC4C00012C4E8000207C8000A61A
+S325000101C0506404207C0004AC7C8001247C0004AC4C00012C4E8000207C6C42E64E80002062
+S325000101E07C7F42A64E8000207C7E9AA64E8000207C7602A64E8000207C7603A64E800020EE
+S325000102003821FF58BC4100303C40001460427FF87C9142A69081002C7CB042A690A10028B2
+S325000102207CC902A690C100247C8102A6908100207CB342A690A1001C7CD242A690C100187C
+S325000102407C9A02A6908100107CBB02A690A1000C90010008386100087C60092D3800000018
+S325000102604E800020B8410030800100287C1043A6800100247C0903A6800100207C0103A6A8
+S325000102808001001C7C0FF120800100187C1243A6800100107C1A03A68001000C7C1B03A671
+S325000102A08021002C4E8000207C3143A67C0000267C1343A67C0802A64BFFFF4938000000D6
+S325000102C04800AC294BFFFFA17C1242A67C0803A67C1042A64C00012C4C0000647FE802A6C1
+S325000102E097E1FFE84BFFFF05812280147064FFFF5484C23F418200802C0400204182006CA6
+S325000103002C040021418200587C0A0378914900143D00FA409109001C814900203CC0007AEE
+S3250001032060C6120090C9000C80EA028454E7653E394700018089000C7C8451D6908900086D
+S3250001034080E9001C3CC03E0090C7000083E100007FE803A6382100184E8000203D40000828
+S32500010360614A23A04BFFFFA83D400008614A23004BFFFF9C3D400008614A60004BFFFF9076
+S325000103807FE802A697E1FFE038629EC0480035D538629ED3480035CD4BFFFE49546A843E40
+S325000103A02C0A0006418101B4418201A42C0A0001418201902C0A00034182017C2C0A0004DD
+S325000103C04182016838629F0891410008480035954BFFFE117066FFFF90C1001038629F1CDA
+S325000103E081010010910100084800357938629F2E480035714BFFFDF5706AFFFF9141001881
+S325000104007D464671418201182C060020418201042C060021418200F038629F4F7D4846705C
+S32500010420910100084800353D38629F5A80810018708400FF90810008480035294800019D1D
+S325000104409061001038629F6880810010908100084800351138629F7880E2801480E7001C11
+S3250001046080E7000090E10008480034F98142801438629F87808A002080840284908100083C
+S3250001048080CA002080C6028090C1000C480034D538629FA481028014810800083D20000F84
+S325000104A0612942407D084BD691010008480034B538629FB5480034AD8142801438629FB75A
+S325000104C080AA002080A5010090A1000880EA002080E7010490E1000C4800348938629FD0EB
+S325000104E08082801480840020A084017A908100084800347183E100007FE803A638210020A9
+S325000105004E80002038629F47480034594BFFFF1C38629F404800344D4BFFFF1038629F3523
+S32500010520480034414BFFFF0438629EE7480034354BFFFEA038629EE0480034294BFFFE9459
+S3250001054038629ED94800341D4BFFFE8838629EEE480034114BFFFE7C2C0A00074182001C87
+S325000105602C0A0050418200084BFFFE5C38629F01480033F14BFFFE5C38629EF6480033E5A7
+S325000105804BFFFE507FE802A697E1FFF03862A0A7480033D180628014806300145463C23EF5
+S325000105A070630FFF2C03082340820028386000015463103A388283C07C6322148063000080
+S325000105C083E100007FE803A6382100104E8000207C0303784BFFFFDC806280148063001CB5
+S325000105E080630000706300F05463E13E4E8000208102801480C8000038E000327FE63B96AB
+S325000106007FFF39D67CDF30514082001480E8001C80C700006CC6080090C700004E8000204A
+S325000106207FE802A697E1FFF080A2801480A5002080A5010070A50001408200183860FFFF96
+S3250001064083E100007FE803A6382100104E80002038E2A0C490E100084800CDA18081001802
+S325000106603CC0FFC090C4000080E1001C3C800040908700007C03037883E100007FE803A666
+S32500010680382100104E8000207FE802A697E1FFE87C030000408100183860FFFF83E100003C
+S325000106A07FE803A6382100184E800020806100203882A0CE908100084800CD4180C10020CB
+S325000106C0390000029106001C3862A0D2480000CD7C671B787C030000418200148061002037
+S325000106E03863002C90E10008480029053860000183E100007FE803A6382100184E800020D1
+S325000107003821FFF0814280142C030004418200103860FFFF382100104E800020812A002075
+S32500010720810A001C80E8000064E7800090E80000A0E9095260E70A00B0E90952A0E9095055
+S3250001074070E7F5FFB0E9095080A1001838E0000790E500008101001C38A0000590A80000D5
+S325000107607C030378382100104E80002080A2801480A5001C808500003FE07FFF63FFFFFF28
+S325000107807FE42039908500004E8000204E8000204E8000204E8000207FE802A697E1FFD073
+S325000107A09061003480C284247C0600004082006048008929386000A93882C3A89081000806
+S325000107C038C0008090C1000C48008F4D7C0300004180019C3C600000606380A93882C3A88F
+S325000107E0388400809081000838C0008090C1000C48008F257C0300004180017438E000FF00
+S3250001080098E2C4A839000001910284243922C3A888C900002C0600FF4182001088E90000EA
+S325000108202C07000A408200187C03037883E100007FE803A6382100304E80002091210028DB
+S3250001084088A900002C05000A4182001088C900002C0600FF408201009121002C88C90000DE
+S325000108602C06003D40820050806100344800CB5D8121002C810100287C8848507C041800BF
+S3250001088040820034806100344800CB41906100188061003480A1002890A1000880E10018D9
+S325000108A090E1000C4800CAAD8121002C7C03000041820038888900002C04000A418200108F
+S325000108C088A900002C0500FF40820018888900002C04000A4082FF3C392900014BFFFF34AE
+S325000108E0392900014BFFFFD039490001890900002C08000A41820010888900002C0400FF0F
+S325000109004082004C38628048914100089121002C914100287CCA485090C1000C4800C64520
+S3250001092038E2804880A1002C7CE53A14810100287CE83850980700003862804883E10000D7
+S325000109407FE803A6382100304E800020392900014BFFFF9C88E900002C07003D4082000816
+S325000109604BFFFEF8392900014BFFFED83862A0D548002FF17C03037883E100007FE803A6C8
+S32500010980382100304E8000203821FFF080A2801480A500087CA51BD638A500087CA5267000
+S325000109A02C051000418000183865000F7C631E7060630001382100104E80002054A3083CA7
+S325000109C0382100104E8000207FE802A697E1FFD89061002C38600009900100084BFFFDB10C
+S325000109E0810280148128002080A90ABC60A500C090A90ABC9121002480A90AB83FE0FFFF7F
+S32500010A0063FFFF3F7FE5283990A90AB8806100304BFFFF798121002464680001910909F076
+S32500010A2080A90AE03FE0FFFF63FF0FFF7FE5283990A90AE038600001480032B18141002C75
+S32500010A407C691B783CA0FA2060A53E8090A10020B0650000906A00B43C8000006084B000FA
+S32500010A60B0830000B0030002390A00247508E0003CA020007C0828004182011038EA002401
+S32500010A8090E9000438600001480032618141002C7C691B7881010020B0680002906A00B88A
+S32500010AA038C03000B0C30000B003000238AA00A474A5E0003CE020007C053800418200B8F0
+S32500010AC0388A00A4908900047C03037839000009910100089001000C48003085812100205A
+S32500010AE038E0001898E9000438A0001898A9000539000001B109000638C00001B0C9002870
+S32500010B00B009002CB009002E39000001B1090030808280148084002039240A8038C000FF46
+S32500010B2098C9000691210018388000179889000A386000143CA0FFC260A50C9C90A1000854
+S32500010B4080E1002C90E1000C4800A1298121001838E04820B0E90002A0A9000260A500034A
+S32500010B60B0A9000283E100007FE803A6382100284E800020388A00A43FE0FFFF63FFFFFF4D
+S32500010B807FE420394BFFFF4038EA00243FE0FFFF63FFFFFF7FE738394BFFFEE87FE802A630
+S32500010BA097E1FFD08121003880A2801480A5002038A50A8038E000FF98E50006810900B4D3
+S32500010BC0910100207C0800004182001480810020A0840000708480004182009C816900B847
+S32500010BE07C0B00004182001491610020A08B0000708480004182001483E100007FE803A694
+S32500010C00382100304E8000203940FFFF80C900107C0600004182001C9121002C806900104E
+S32500010C2048009409816100208121002C7C6A1B782C0AFFFF4182002C994900A438C00001DD
+S32500010C40B0CB0002A08B000060848000B08B000083E100007FE803A6382100304E8000205B
+S32500010C609809000883E100007FE803A6382100304E80002080A9000C7C050000418200144C
+S32500010C8080810020A08400027C0400404181002481010020A0A8000060A59000B0A8000089
+S32500010CA083E100007FE803A6382100304E80002080A900187C0500004182002438690024D4
+S32500010CC080A10020A0A5000290A1000880E900187CE803A64E8000214BFFFFB89121002CF0
+S32500010CE080C900147C060000418200487C0A037881010020A10800027C0A40404080FF945C
+S32500010D00914100247CCA4A1488C600248069000C90C1000880E900147CE803A64E800021F9
+S32500010D208121002C80A10024394500014BFFFFC480610020A0630002480090E9906100183D
+S32500010D408063000480C1002C38C6002490C1000880810020A08400029081000C4800C2054A
+S32500010D608121001881010020A1080002808900047D044214910900048061002C8063000CE7
+S32500010D80912100084800920D4BFFFF087FE802A697E1FFE09061001C4BFFF4098121001CE2
+S32500010DA090610018888900087C0400004082001C80690010480092758121001C90610014A1
+S32500010DC02C03FFFF4082001C806100184BFFF3F183E100007FE803A6382100204E800020FF
+S32500010DE080C280B47C06000041820064810900B8A108000071088000418200084BFFFFF0E5
+S32500010E0080C1001498C900A480C900B838A00001B0A6000280C900B8A0A6000060A5800073
+S32500010E20B0A600007D284B7880A280B47C0500004082001038A0000198A800084BFFFF6872
+S32500010E407C05037898A800084BFFFF5C7C0A0378914100102C0A00644080FFA880C900B8BD
+S32500010E60A0C6000070C68000408200084BFFFF943860000148001C098121001C80C1001093
+S32500010E80394600014BFFFFCC7FE802A697E1FFE0906100243902C4B080A800047C050000DF
+S32500010EA04182001483E100007FE803A6382100204E8000209101001C38C0000190C8000476
+S32500010EC038601000900100089001000C90010010480090098121001C9069000C8101002C3A
+S32500010EE09068000038604000900100083CE0FFC260E70E8C90E1000C9121001048008FDDD1
+S32500010F00814100288121001C9069001080C100309066000080A1003490A90014388000094F
+S32500010F20908900007C0A000040820008394025807D234B78914100084BFFFA9183E10000AD
+S32500010F407FE803A6382100204E8000209062C4C84E8000207FE802A697E1FFD88181003017
+S32500010F607C6D1B7838C2C4B090C100247C0B03787C6A1B7891A1002C7D0D62147C0A40402C
+S32500010F804080002C88AA00007CA507742C05000A40820008396B0001394A000191A1002C04
+S32500010FA07D0D62147C0A40404180FFDC7C6C5A1448008E71818100308141002C7C6B1B7851
+S32500010FC07D856378398CFFFF7C0500004182005488AA00007CA507742C05000A40820018EF
+S32500010FE080CB000439060001910B00043900000D99060000808B000438C4000190CB00046B
+S325000110007D465378394A000188C6000098C400007D856378398CFFFF7C0500004082FFB477
+S3250001102080610024806300109161000848008F6583E100007FE803A6382100284E80002098
+S325000110403821FFF03902C4B088E800087C070000418200084BFFFFF4382100104E80002038
+S325000110607FE802A697E1FFE0812100289061002438A2C5707C0900004081001490A1001C6E
+S32500011080808500007C0400004082001483E100007FE803A6382100204E8000207D234B78B0
+S325000110A048008D819061001880630004808100249081000880E1002890E1000C4800BEA5F4
+S325000110C08121001880E10028808900047CE43A1490E900048061001C8063000091210008F4
+S325000110E048008EB183E100007FE803A6382100204E800020386000014E8000207FE802A6F1
+S3250001110097E1FFF08061001838A2819890A100084800C3517C0300004182001C8061001889
+S325000111203882819D908100084800C3397C03000040820038900281F83CC0002090C281FC04
+S32500011140386281F83863000838E281A290E100084800C2A9386281F883E100007FE803A6E2
+S32500011160382100104E8000207C03037883E100007FE803A6382100104E8000207FE802A63D
+S3250001118097E1FFE88082C5707C0400004082001C3C600001900100089001000C90010010E0
+S325000111A048008D399062C5708102C5747C080000418200108062C57448008CC19002C57465
+S325000111C0386281A74800279D3C60FFC2606311604BFFFD7D38C0FFFF90C2C5789002C57C8D
+S325000111E09002C5C09002C5C47C03037883E100007FE803A6382100184E8000207FE802A6DA
+S3250001120097E1FFD08181003890610034810100349101001C3942C57080E2801480E70000B0
+S3250001122090E1002C918100387C0C000040810140812A000491410028900A00047C0900006A
+S32500011240408200A0900A0054806A000048008CC981810038814100287C691B787C03000095
+S325000112604082008080C2801480C600008101002C7CC8305054C5083C7CC62A1454C6183850
+S3250001128054A5482C7CC6285038E000327CC63B9628063A984180FFB480EA005438E7000171
+S325000112A090EA00542C07000341800020386281C1480026B17C03037883E100007FE803A6D9
+S325000112C0382100304E800020386281D039000001910100084BFFFC81814100284BFFFF6C6B
+S325000112E080A9000480C900007D4628507C0A6000408100087D8A63788061001C91210020D6
+S3250001130080E9000090E10008914100249141000C4800BC5181610024814100288121002009
+S32500011320808900007CE45A1490E9000080E9000080C900047C073040418000507D234B7839
+S3250001134048008B39816100248141002880E100387D8B385080E1001C7CA75A1490A1001C06
+S32500011360918100387C0C00004181FEC88061001C80E100347C67185083E100007FE803A6BB
+S32500011380382100304E800020912A00044BFFFFC07FE802A697E1FFE0808100289081001C4B
+S325000113A08122C5C07C0900004081005880E1002C7C093800408100088121002C8102C5C077
+S325000113C07CC9405090C2C5C08061001C38E2C57038E7001090E10008912100189121000CDE
+S325000113E04800BB81812100188081001C7CE44A1490E1001C8081002C7CE9205090E1002CA1
+S3250001140080E1002C7C070000408100C83862815038A0004090A100084BFFFDE58141002C56
+S325000114207C691B782C030040408000187C03037883E100007FE803A6382100204E80002011
+S325000114407C035000408100307C8A48509082C5C03862C5703863001038A281507CA55214E4
+S3250001146090A1000880A2C5C090A1000C4800BAF58121002C8061001C38C2815090C1000862
+S32500011480912100189121000C4800BAD9812100188101002C7CC9405090C1002C8101001C8A
+S325000114A07CC84A1490C1001C80C2C57C38C6000190C2C57C386281D2388000019081000842
+S325000114C04BFFFA9580E1002C7C0700004181FF408061001C810100287C68185083E10000C4
+S325000114E07FE803A6382100204E8000203821FFE87C0B03782C0B0100408000545569C00E54
+S32500011500394000087C0A00004081002875248000418200445526083C3FE004C163FF1DB7DB
+S325000115207FE93278394AFFFF7C0A00004181FFE05567103A38A2DC307CE72A1491270000A5
+S32500011540396B00012C0B01004180FFB4382100184E8000205529083C4BFFFFCC7FE802A6EE
+S3250001156097E1FFF081410018906100148082DC347C0400004082000C4BFFFF758141001826
+S3250001158080C1001C3FE0FFFF63FFFFFF7FE8327881210014394AFFFF7C0A00004180003C9F
+S325000115A05505463E7D264B783929000188C600007CA5327854A5103A38C2DC307CA53214B4
+S325000115C080A500005507402E7CE82A78394AFFFF7C0A00004080FFCC3FE0FFFF63FFFFFF00
+S325000115E07FE3427883E100007FE803A6382100104E800020816100088121000C7C0B0000DE
+S32500011600408200148083000090890000912300004E80002080AB000090A90000912B00000F
+S325000116204E800020814100088121000C7C0A00004082001080890000908300004E800020DB
+S3250001164080C9000090CA00004E8000207FE802A697E1FFE87C09037838E2ED382C09000A06
+S325000116604080002C80C700047C0600004082001080A7000C7C0500004182003039290001CE
+S3250001168038E700142C09000A4180FFDC386282D8480023597C03037883E100007FE803A60F
+S325000116A0382100184E8000209007000C9007001038A0000190A700047CE33B7883E10000F0
+S325000116C07FE803A6382100184E8000207FE802A697E1FFD89061002C7C03000040800008D2
+S325000116E09001002C4BFFEABD906100184BFFFF61818280147C6A1B7880C1002C54C5083CA8
+S325000117007CC62A1454C620367CC62A1438E003E87CC63B9690C3000880A1003090A3000C51
+S3250001172080810034908300107C0B0378812C00187C0900004182003480A90008808A0008C4
+S325000117407C0520004181005480A90008808A00087CA5205090AA00087D2B4B7881290000A0
+S325000117607C0900004082FFD4386C001891610008914100249141000C4BFFFE7D8061001800
+S325000117804BFFEA3D8061002483E100007FE803A6382100284E800020808A00088109000845
+S325000117A07C844050908900084BFFFFC09003000C4E8000204E8000207FE802A697E1FFA8BF
+S325000117C04BFFE9E1814280149061004C812A00187C090000418200A47C0B03787C09000024
+S325000117E04182001080E900087C070000408100A47C0900004182001080E9000838E7FFFFD0
+S3250001180090E900087C090378916100507C095800408000685527103A3901001C7CE7421424
+S3250001182080E7000080E7000C7C07000041820028912100545523103A38C1001C7C63321457
+S32500011840806300007CE803A64E80002181610050812100545525103A3881001C7CA522148A
+S3250001186080A500009005000439290001916100507C0958004180FFA08061004C4BFFE94120
+S3250001188083E100007FE803A6382100584E8000202C0B000A418000084BFFFF589161005041
+S325000118A05568103A38C1001C7D08321491280000386A0018900100089121000C4BFFFD69C0
+S325000118C081428014812A001880E10050396700014BFFFF0CA121000A5527043E54E7C23E7B
+S325000118E098E300005526043E98C300014E800020814100085544463E988300005546843E00
+S3250001190098C300015548C23E99030002994300034E8000207C671B78886300005463C00E76
+S32500011920888700015484801E7C63237888A7000254A5402E7C632B7888C700037C633378AA
+S325000119404E8000207C671B78886300005463402E88A700017C632B784E8000203821FFD83C
+S325000119608222809C814100307C691B787C0F03787C0C03787C0B03787C100378706500014E
+S32500011980418200207C0A000041820014888900007D8C2214394AFFFF392900013A00000191
+S325000119A02C0A001041800054A0E90000A08900027D6B3A14A0C900047DAB2214A109000660
+S325000119C07DCD3214A0A900087DCE4214A0E9000A7DCE2A14A089000C7DCE3A14A0C9000E1D
+S325000119E07DAE22147D6D3214394AFFF0392900102C0A00104080FFB42C0A00024180001C9D
+S32500011A00A0C900007D6B3214394AFFFE392900022C0A00024080FFEC7C10000041820070A2
+S32500011A207C0A00004182000C88A900007DEF2A14889100007C8407747C0400004182004850
+S32500011A407DEF5A145588C23E7D2F4214718800FF5508402E7D2942145528843F41820014F0
+S32500011A607127FFFF7D283A145528843F4082FFF43FE0FFFF63FFFFFF7FE34A7838210028BF
+S32500011A804E8000207D8C5A144BFFFFBC7C0A00004182000C88A900007D8C2A1488910000EF
+S32500011AA07C8407747C0400004182000C7D8C5A144BFFFF947DEF5A144BFFFF8C3821FFF00A
+S32500011AC07C691B787C0B037888E3000070E7000F54EA103A7C0A00004081002888C900006C
+S32500011AE054C6402E890900017CC643787D6B3214394AFFFE392900027C0A00004181FFE08E
+S32500011B007168FFFF5566843E7D0832147104FFFF5507843E7C843A146883FFFF382100106E
+S32500011B204E8000207FE802A697E1FFD0906100343862B7C89061002C9001000838E005F059
+S32500011B4090E1000C4800B1E98061002C3863002A80A1003C90A10008810100409101000C57
+S32500011B604800B4018121002C80C1004039460008980900163900001199090017980900141C
+S32500011B80980900153869001891410020914100084BFFFD458061002C3863001A8102822090
+S32500011BA0910100084BFFFD4D8061002C38630022A0E2822490E100084BFFFD1D8061002C14
+S32500011BC03863001E8101003881080000910100084BFFFD218061002C3863002480810038FB
+S32500011BE0A0840004908100084BFFFCED8061002C3863002680E1002090E100084BFFFCD983
+S32500011C008121002C98090028980900293869001680E1004038E7001490E100084BFFFD41D0
+S32500011C205464043E908100188061002C3863002881010018910100084BFFFC9D3922B7C8B9
+S32500011C40810100403948002C390000459909000E9809000F390000FF990900169121002866
+S32500011C60386900109141002438AAFFF290A100084BFFFC6580610028386300128082801CAB
+S32500011C8038E4000190E2801C908100084BFFFC49812100289809001498090015980900187C
+S32500011CA0980900193869000E4BFFFE155468043E91010018806100283863001880810018DA
+S32500011CC0908100084BFFFC11806100283863000C39000800910100084BFFFBFD80610028B7
+S32500011CE080C1003838C6000690C10008388000069081000C4800B26D8061003438E2B7C817
+S32500011D0090E10008808100249081000C38E0138890E100104800471583E100007FE803A6B5
+S32500011D20382100304E8000207FE802A697E1FF60906100A49801001C38A0000598A1001DC2
+S32500011D409801001E80C100AC98C1001F3861001C3863000480C100B090C100084800B69D27
+S32500011D60806100B04800B66538830005806100A4810100A89101000838C1001C90C1000CED
+S32500011D80908100104BFFFDA180C100B47C0600004182001C38629C48810100AC9101000897
+S32500011DA080C100B090C1000C48001BB983E100007FE803A6382100A04E8000207FE802A648
+S32500011DC097E1FFD09061003480A281F47C0500004082029C392003E880828014808400003A
+S32500011DE05485083C7C842A145484183854A5482C7C84285038C000327C8433967C89221446
+S32500011E009081001880A2801480A5000054A6083C7CA5321454A5183854C6482C7CA530509A
+S32500011E2038E000327CA53B96810100187C082840408102288061003438E2BDB890E10008D1
+S32500011E4080828014808400005486083C7C8432145484183854C6482C7C84305038E00032FD
+S32500011E607C843B96810100187C8440509081000C480044319061002C7C0300004181000820
+S32500011E804BFFFF843882BDB8908100203864000C4BFFFAB55466043E2C060800418200086C
+S32500011EA04BFFFF64806100203863000E4BFFFC11812100207C0300004182001038629C5EC5
+S32500011EC048001AA14BFFFF4088E9000E2C0700454182001038629C6F48001A894BFFFF28A4
+S32500011EE0890900172C080011418200084BFFFF1898090016386900264BFFFA4D5466043EB6
+S32500011F00806100203863001890C1002890C100084BFFF9C580610020386300284BFFFA29FB
+S32500011F207C030000408200F480A10038A0A500047C050000418200208061002038630022A1
+S32500011F404BFFFA0581010038A10800047C0340004082FEB4806100203863001A4BFFF9B9E5
+S32500011F60814100389061001C80AA00003880FFFF7C0520004182001080AA00007C0328002E
+S32500011F804082FE8480C100283926FFF880C100407C0930004081001038629CA6480019C52E
+S32500011FA04BFFFE648061003C808100203884002A90810008912100289121000C4800AFA5FD
+S32500011FC080A1003880E1001C90E5000080610020386300224BFFF97181210038B069000446
+S32500011FE03869000638A2BDB838A5000690A1000838E0000690E1000C4800AF693880000114
+S32500012000908281F48061002883E100007FE803A6382100304E80002080610020386300168C
+S32500012020808100283884000C908100084BFFF9315469043E5528043E7C0800004182FEEC2C
+S3250001204038629C815527043E90E1000880E1002C90E1000C4800190D7C03037883E10000B5
+S325000120607FE803A6382100304E800020392013884BFFFD687FE802A697E1FDC89061023CB4
+S325000120809801002638E0000198E10027386100263863000238829CBB9081000880C10244B4
+S325000120A090C1000C48001879390300023861002638630001910102307C681A1438829CBE60
+S325000120C090810008480018598161024038A3000280C102307CA62A1490A10230A08B0004C1
+S325000120E0908102287C090378912102342C0900054080007081010228B10B00048061023CC1
+S325000121009161000838E1002690E1000C80810230908100104BFFFA1181210240B0090004B8
+S325000121208061023C9121000880A1024890A1000C39000204910100104BFFFC858161024047
+S32500012140812102482C0300044080003480C1023439260001912102342C0900054180FF9814
+S3250001216038629D05480017FD3860FFFF83E100007FE803A6382102384E8000208889000024
+S325000121805484402E88C900017C8433782C040003418200482C040005418200084BFFFFB0BE
+S325000121A038629CC488C9000254C6402E890900037CC6437890C1000838A9000490A1000C36
+S325000121C0480017A13860FFFF83E100007FE803A6382102384E8000203960000188890002C0
+S325000121E05484402E88C900037C843378916283E87C0458004182004838629CDE9081000825
+S32500012200480017618061023C80C1024090C10008388000019081000C38C29CF990C1001036
+S32500012220900100144BFFFB053860FFFF83E100007FE803A6382102384E8000203863FFFC87
+S3250001224083E100007FE803A6382102384E8000207FE802A697E1FFD0812283E890610034F9
+S325000122609801002038800004988100217D25467098A100229921002338A9000190A283E899
+S32500012280810100403908000491010040900100248061003480E1003890E1000838A1002089
+S325000122A090A1000C38E0000490E100104BFFF8798061003480E1003890E1000880A1003CFE
+S325000122C090A1000C81010040910100104BFFFAF1816100408141003C7C691B787C035800B2
+S325000122E04080006438629D2C912100089161000C480016718061003480C1003890C10008E2
+S32500012300388000029081000C38C29D4090C10010900100144BFFFA158101002438C8000102
+S3250001232090C100242C080005408000084BFFFF643860FFFF83E100007FE803A638210030E0
+S325000123404E800020890A00025508402E88AA00037D082B789101002C808283E87C082000FC
+S325000123604182007C38629D4B480015F9814283E88121002C38CAFFFF7C09300040820018B4
+S3250001238080A1002439050001910100242C0500084180FF0038629D4D912100089141000CE7
+S325000123A0480015C18061003480E1003890E1000838A0000190A1000C38E29D7590E100100E
+S325000123C0900100144BFFF9653860FFFF83E100007FE803A6382100304E8000203863FFFC92
+S325000123E083E100007FE803A6382100304E8000207FE802A697E1FC48906103BC48003E6981
+S32500012400814103C0906100C07C0300004082002838629D8180A103BC90A1000848001545A3
+S325000124203860FFFF83E100007FE803A6382103B84E8000209001002C900100287C0A00008D
+S325000124404182001488AA00007CA507747C050000408205C03861024C9001000838A0016C03
+S3250001246090A1000C4800A8C938E0000198E1024C390000019901024D388000069881024E35
+S325000124803861024C3863001C80A100C090A10008390000069101000C4800AAC990028220B1
+S325000124A038A00044B0A28224386282203863000680C100C090C10008388000069081000CEF
+S325000124C04800AAA1900100DC808100DC2C04000A4080009038C0FFFF90C2823838E0004331
+S325000124E0B0E2823C3862823838630006390283689101000838A0000690A1000C4800AA655E
+S32500012500806103BC390282389101000838A1024C90A1000C38E0016C90E100104BFFF609D2
+S32500012520806103BC38C2823890C10008390100E09101000C38A0016C90A100104BFFF881E6
+S325000125407C0300004181045C80C100DC38C6000190C100DC808100DC2C04000A4180FF783B
+S3250001256080C100DC2C06000A4180002038629D94480013F13860FFFF83E100007FE803A6F9
+S32500012580382103B84E8000208101002C7C0800004182040080A1002C88A500007CA5077423
+S325000125A07C050000418203EC8901010C7D0807747C0800004182001838629DA538E100E016
+S325000125C038E7002C90E100084800139938629DA9888100F490810008888100F59081000C2B
+S325000125E0888100F690810010888100F790810014A082823C908100188081002C9081001C2C
+S32500012600480013614BFFEA3D386100E0386300104BFFF30590628220A08283A438E4000126
+S32500012620B0E283A4B0828224386100E0386300144BFFF2E59062823838E00045B0E2823C60
+S32500012640806103BC38C2823890C100088121002C9121000C38C2C5C890C100104BFFFA19F5
+S325000126607C030000408000183860FFFF83E100007FE803A6382103B84E8000203882C5C8A9
+S3250001268039440004906100D82C030020418002CC890A00005508402E88CA00017D0833782A
+S325000126A05508801E88EA000254E7402E888A00037CE723787D083B782C0806EB4082029C2B
+S325000126C088EA000454E7402E88AA00057CE72B7854E7801E88CA000654C6402E890A000744
+S325000126E07CC643787CED337888AA000854A5402E890A00097CA5437854A5801E888A000AF6
+S325000127005484402E88CA000B7C8433787CAC2378890A000C5508402E88CA000D7D083378A8
+S325000127205508801E88EA000E54E7402E888A000F7CE723787D083B78910100C8918100CCDF
+S325000127407CC8621491A100D07CC66A1490C100C488AA001454A5402E890A00157CA54378B0
+S3250001276054A5801E888A00165484402E88CA00177C8433787CA5237838629DD090A100B491
+S325000127803FE01FFF63FFFFFF7FE8283991010008480011D14BFFE8AD38629DDF80C100D003
+S325000127A090C10008480011BD808100B43FE01FFF63FFFFFF7FE42039908100BC38E2C5C821
+S325000127C039A70024808100D83944FFE0816100D0916100D47C0A000040820050806103BC09
+S325000127E0388282389081000838E2C5C890E1000C39000200910100104BFFFA59816100D4F1
+S325000128007C6A1B787C030000408000183860FFFF83E100007FE803A6382103B84E800020D5
+S3250001282038C2C5C839A60004914100D87C0B5000418100087D6A5B78806100BC91A100B89B
+S3250001284091A10008914100DC9141000C4800A715818100CC814100DC80A100BC7D05521476
+S32500012860910100BC80A100B87DA5521491A100B880A100D47D6A285080A100D87D4A28502C
+S32500012880914100D8916100D47C0B00004181FF487C0C000040820088806103BC38C282380B
+S325000128A090C10008390000039101000C38A29DE690A10010900100144BFFF47138629DE9CC
+S325000128C080E100C890E1000880A100C490A1000C4800109138629DF1810100B4910100084C
+S325000128E0480010814BFFE75D4800137D4BFFD8B580C100B43FE01FFF63FFFFFF7FE630395B
+S325000129007CC803A64E8000217C03037883E100007FE803A6382103B84E80002038629DE24B
+S32500012920918100084800103D81A100B8814100D8816100CC916100D4900100CC80E100BC7F
+S3250001294038E70FFF3FE0FFFF63FFF0007FE7383990E100BC4BFFFE80806103BC38C28238B4
+S3250001296090C100089001000C39029DBF9101001038A0000190A100144BFFF3B13860FFFF7F
+S3250001298083E100007FE803A6382103B84E80002038C100E038C6006C90C1002C4BFFFC0CA8
+S325000129A03861024C3863001C390100E03908001C9101000838A0000690A1000C4800AB0D46
+S325000129C0814100287C030000418200084BFFFB7C88E100E12C0700014082FB7088C100E225
+S325000129E02C0600064082FB647C0A00004182FB747D435378388100E03884002C908100089A
+S32500012A004800AA617C0300004182FB584BFFFB3C38610030914100084800A9E13861003008
+S32500012A203880003A908100084800A2E17C0300004182004038E100309061002C7C0338001A
+S32500012A404182FA1488A3FFFF7CA507742C05005C408200084BFFFA0038C1003090C100289C
+S32500012A6038C3000190C1002C980300004BFFF9E8390100309101002C4BFFF9DC3821FFF081
+S32500012A807C691B7880A28014814500047D254B783929FFFF7C050000408100187C0703781A
+S32500012AA07C0750404080FFE838E700014BFFFFF4382100104E8000203821FFF08102801442
+S32500012AC0810800047D0341D638A003E87D482BD741810008394000017C0903787C095040E8
+S32500012AE040800010392900017C0950404180FFF8382100104E8000207FE802A697E1FFF002
+S32500012B00806280284BFFD6F580A2801480E5000038E7000190E500004BFFECA18102823053
+S32500012B207C0800004182000C7D0803A64E80002183E100007FE803A6382100104E80002053
+S32500012B407FE802A697E1FFB88142801480CA000838E003E87CC63BD690CA00044BFFD67D36
+S32500012B60906100443860000A4BFFFF154BFFD66D808100447CE4185090E100447C07000047
+S32500012B80408000084BFFFFD83861002881028014810800049101000848009D69386100203F
+S32500012BA0810280148108000C5504103A7D0822145508083C38A003E87D082BD69101000880
+S32500012BC048009D253861003039010008392100288089000080A900049088000090A80004CD
+S32500012BE038A1001038C10020810600008126000491050000912500044800A0DD38610028C4
+S32500012C00810100445508103A9101000848009CD93861003838C10008390100308128000004
+S32500012C208088000491260000908600043881001038A1002880C500008105000490C40000C3
+S32500012C40910400044800935939010004392100388089000080A900049088000090A8000446
+S32500012C6048009DD581428014906A0004810A00047C0800004082000C38800001908A000486
+S32500012C8080AA000C38C000047CA533D638E000327CA53BD63865FFFF906280284BFFD55DA4
+S32500012CA083E100007FE803A6382100484E8000207FE802A697E1FFE89061001C7C0903788F
+S32500012CC080A282347C09280040800060912100145523103A38C226107C6332148063000088
+S32500012CE08101001C910100084800A779812100147C030000408200245523103A38822690E0
+S32500012D007C6322148063000083E100007FE803A6382100184E8000203929000180A28234A6
+S32500012D207C0928004180FFA87C03037883E100007FE803A6382100184E8000207FE802A69B
+S32500012D4097E1FF28816100E03CE0002090E100A87C0300004182021080E280A080E7000079
+S32500012D6090E1000838E100B490E1000C48002AA9814100A87C030000418100183860FFFF15
+S32500012D8083E100007FE803A6382100D84E800020980A0000386100B49141000838A003FFF6
+S32500012DA090A1000C48001FA57C6A1B787C030000418100183860FFFF83E100007FE803A6E7
+S32500012DC0382100D84E80002080A100A87CA51A1498050000806100A838630400810100A8C6
+S32500012DE09101000838CA000190C1000C4800A175808100A8396404007D6A5B787D6C5B78B4
+S32500012E00888A00007C8407747C04000041820028890A00007D0807742C08000D4082011804
+S32500012E20394A0001888A00007C8407747C0400004082FFE0980B00007D83637838E10020A2
+S32500012E4090E10008388000209081000C38C2886B90C1001048005EF1906100AC7C0A03787A
+S32500012E60808100AC7C0A200040800044914100B05543103A38A100207C632A148063000097
+S32500012E8038E0003D90E1000848009E8181828234814100B07C03000040820028394A0001DE
+S32500012EA0808100AC7C0A20004180FFC47C03037883E100007FE803A6382100D84E800020A7
+S32500012EC039630001980300005547103A390100207CE7421480E700007CE758502C07001DF8
+S32500012EE0418000185546103A38A100207CC62A1480C600009806001B5587103A38A226105F
+S32500012F007CE72A145546103A38A100207CC62A1480C6000090C700005588103A38C2269037
+S32500012F207D08321491680000388C0001908282344BFFFF6C888A00007C8407742C040009BE
+S32500012F404082000C38E0002098EA00007D685B78396B0001892A00007D29077499280000F0
+S32500012F604BFFFEC07C0B00004182005C7D635B7890010008388003FF9081000C4800A5BDCF
+S32500012F807C030000408200183860FFFF83E100007FE803A6382100D84E8000203862885036
+S32500012FA0480009C1806100A880E100E090E100084800A449806100A84800A4117C6A1B7881
+S32500012FC04BFFFE144BFFD5C190610018806100A880C1001890C100084800A421806100A8D4
+S32500012FE04800A3E97C6A1B784BFFFDEC7FE802A697E1FFD89061002C8121003088A90000CC
+S325000130007CA507742C050020408200183929000188A900007CA507742C0500204182FFF0AF
+S325000130207C0A03789141001C2C0A0006408000C0888900007C8407747C04000040820018F8
+S325000130403860FFFF83E100007FE803A6382100284E8000203889000188A900007CA5077461
+S3250001306098A10024890400007D0807747C080000408200183860FFFF83E100007FE803A6F7
+S32500013080382100284E8000203904000191010020888400007C840774988100259801002646
+S325000130A03861002490010008388000109081000C48009EB58141001C812100208101002CE5
+S325000130C07D08521498680000890900007D0807742C08003A4082000839290001394A00014D
+S325000130E09141001C2C0A00064180FF487C03037883E100007FE803A6382100284E800020BA
+S325000131007FE802A697E1FFB87C691B783861002C3902886D910100089121000C81010050D3
+S3250001312091010010480007F97C09037880C282347C093000408000489121001C5523103A59
+S3250001314038E226107C633A14806300003881002C9081000838C0001C90C1000C4800A1F5BB
+S325000131608121001C7C030000418200283929000180C282347C0930004180FFC07C03037896
+S3250001318083E100007FE803A6382100484E80002081010054900800345525103A388226904F
+S325000131A07CA5221480A5000090A1002880E1002888E700007CE707747C07000041820040D7
+S325000131C080C1002888C600007CC607742C0600204182035080E1002888E700007CE7077436
+S325000131E02C0700094182033C80C1002888C600007CC607747C060000408200183860000121
+S3250001320083E100007FE803A6382100484E80002080610028388288729081000838C00005D1
+S3250001322090C1000C4800A12D814100547C030000408200D48101002839080005910100283F
+S325000132407D49537838CA001B7C0930404080002C80A1002888A500007CA507747C05000045
+S325000132604182001880C1002888C600007CC607742C0600204082005C9809000080810028BE
+S32500013280888400007C8407747C0400004182FF2081010028890800007D0807742C080020AF
+S325000132A0408200084BFFFF0880810028888400007C8407742C040009408200084BFFFEF001
+S325000132C080A1002838A5000190A100284BFFFFB080E1002888E700007CE707742C0700095C
+S325000132E04182FF9880A10028388500019081002888A500007CA5077498A9000039290001C0
+S325000133004BFFFF448061002838E2887890E10008388000059081000C4800A0397C03000003
+S3250001332040820028806100283863000539010028910100089001000C48009C2D80A10054D4
+S325000133409065001C4BFFFF388061002838A2887E90A1000838E0000490E1000C48009FF53D
+S325000133607C03000040820028806100283863000438C1002890C100089001000C48009BE952
+S3250001338081010054906800204BFFFEF480610028390288839101000838A0000490A1000CFA
+S325000133A048009FB17C03000040820028806100283863000438810028908100089001000CC6
+S325000133C048009BA580C10054906600244BFFFEB08061002838C2888890C10008390000050D
+S325000133E09101000C48009F6D7C03000040820028806100283863000538E1002890E1000808
+S325000134009001000C48009B6180810054906400284BFFFE6C806100283882888E90810008AD
+S3250001342038C0000390C1000C48009F29812100547C0300004082003C3869002C80A1002894
+S3250001344038A5000390A100084BFFFBA52C03FFFF4082FE2C806100543863002C90010008B4
+S32500013460388000069081000C480098C54BFFFE1080C900342C0600084080FE0480A9003497
+S3250001348054A520367CA54A143945003881010028890800007D0807747C0800004182001807
+S325000134A080E1002888E700007CE707742C07002040820018980A000080C9003438C60001E4
+S325000134C090C900344BFFFDB881010028890800007D0807742C080009408200084BFFFFD8F6
+S325000134E07D445378394A000180A10028390500019101002888A500007CA5077498A400006E
+S325000135007CE9505081090034550820367CE8385038E7FFC82C07000F4180FF744BFFFF9805
+S325000135208101002839080001910100284BFFFC947FE802A697E1FFF09061001480C1001830
+S3250001354090C100084BFFDA118061001480A1001890A100084800729183E100007FE803A6B0
+S32500013560382100104E8000207FE802A697E1FFD0386295484BFFF73D7C691B787C090000A5
+S32500013580418200247D234B789121002C38A2955B90A1000848009C7D8121002C7C0300004B
+S325000135A04182009C3C80FFC260841054908282F4900100247C090000418200187D234B78E0
+S325000135C0900100089001000C4800999D90610024900100283862955F4BFFF6D981210028F1
+S325000135E07C03000041820014900100089001000C480099757C691B787C090000408200081B
+S32500013600392025808061002491210008388281909081000C38C281D890C100103D00FFC24C
+S325000136206108374C910100144BFFD86183E100007FE803A6382100304E8000203CE0FFC2A6
+S3250001364060E7363090E282F44BFFFF687FE802A697E1FFF09061001480C1001870C8007F92
+S32500013660910100182C08001040820010386295644800037981010018806100149101000803
+S3250001368048006A4D83E100007FE803A6382100104E8000207FE802A697E1FFE090610024DE
+S325000136A09001001880C2801480C6000090C100148101002C7C0800004182001C80A2801412
+S325000136C080A5000080C100147CA62850280500FA4181010480628190480069517C691B7874
+S325000136E02C03FFFF408200084BFFFFC82C03000D408200083920000A2C09007F40820008D4
+S32500013700392000089121001C2C090015408200C03900000A99010013386100133880000152
+S325000137209081000880C282F47CC803A64E80002181810024816100288141001C812100180D
+S325000137402C0A0008408200187C0900004081FF5838E9FFFF90E100184BFFFF4C2C0A000A30
+S32500013760408200207CEC4A14980700007D234B7883E100007FE803A6382100204E800020BD
+S325000137802C0A0015408200183860FFFF83E100007FE803A6382100204E8000207C095800AF
+S325000137A0408200187D635B7883E100007FE803A6382100204E8000203909000191010018AD
+S325000137C07C8C4A14994400004BFFFEDC992100134BFFFF483860FFFE83E100007FE803A614
+S325000137E0382100204E8000207FE802A697E1FFE090610024808100289804000080E100308A
+S325000138007C0700004182001880810030888400007C8407747C040000408200FC7C06037850
+S3250001382090C10018808100187C040000418200683862956780C1002490C1000880810030CF
+S325000138409081000C4800011D8061002880E1002C90E1000880C1001890C1000C4BFFFE3998
+S325000138607C691B782C03FFFE4182008C2C03FFFF4182FFB48101002C7C0940004180002C4B
+S3250001388038629580480000DD808100187C0400004082FFA03862957980E1002490E10008AD
+S325000138A0480000C14BFFFFA47C0900004082001080E100187C070000408200187C030378E4
+S325000138C083E100007FE803A6382100204E8000208061002880A1003090A1000848009B1D73
+S325000138E07C03037883E100007FE803A6382100204E8000203862957E38800001908100086D
+S3250001390080C282F47CC803A64E8000217C0903784BFFFF6438C000014BFFFF087FE802A606
+S3250001392097E1FFE89061001C38C3010090C10008808100209081000C38E1002038E7000425
+S3250001394090E10010480007F18081001C7C64185083E100007FE803A6382100184E80002067
+S325000139607FE802A697E1FEE09061012480E282F47C070000408200187C03037883E1000032
+S325000139807FE803A6382101204E8000203861002038C1002038C6010090C1000881010124D7
+S325000139A09101000C38C1012438C6000490C1001048000785390100207C881850386100208E
+S325000139C09081001C90810008810282F47D0803A64E8000218061001C83E100007FE803A613
+S325000139E0382101204E8000207FE802A697E1FEE0812282F4906101247C0900004182007408
+S32500013A003862958F38800007908100087D2803A64E8000213861002038A1002038A501003C
+S32500013A2090A1000880E1012490E1000C38A1012438A5000490A10010480006FD38E100209F
+S32500013A407D071850386100209101000880E282F47CE803A64E8000213862959738800001CD
+S32500013A609081000880C282F47CC803A64E8000214BFFC7154BFFC70D4BFFFFFC7FE802A6FA
+S32500013A8097E1FFE0808280148124002038C0000190C90030808908403FE0FFFF63FFFFFE1E
+S32500013AA07FE4203990890840B0090968B0090964B0090960B0090962B0090966B009095210
+S32500013AC0B0090950B0090954B009095690090ABC90090AB8B0090AC29121001C90090AC42B
+S32500013AE04BFFC6998101001C3CA0000060A58001B0A809C080C1001CA0C609C070C6000132
+S32500013B00418200184BFFC67580C1001CA0C609C070C600014082FFF0386281E038E2B3C83A
+S32500013B2090E10008388004009081000C480065E9386281E03CC0FA2060C6240090C10008E2
+S32500013B40390004009101000C480065F183E100007FE803A6382100204E8000207FE802A6FB
+S32500013B6097E1FFE89061001C4BFFC639906100108102801481080020910100144BFFC5FD16
+S32500013B8081010014A10809C071080001418200184BFFC5E981010014A10809C07108000147
+S32500013BA04082FFF08081001C5484402E80A1002054A520367C842B7880C1002454C6083CF4
+S32500013BC07C8433786084000180A10014B08509C04BFFC5A980E10014A0E709C070E7000146
+S32500013BE0418200184BFFC59580E10014A0E709C070E700014082FFF0806100104BFFC5C1B0
+S32500013C0083E100007FE803A6382100184E8000203821FFE85465183838A5FFF8810100200B
+S32500013C20710800075508183880E1002470E700077D083B787D0B283080E280148127002097
+S32500013C4080E90AEC390000FF7D0828307D0840F87CE740387CE75B7890E90AEC382100183B
+S32500013C604E8000207FE802A697E1FFE080E2801480E7002039270A209121001C80E90000AB
+S32500013C8070E70030418200543860000538C0000490C100089001000C4BFFFEC53860000744
+S32500013CA039000004910100089001000C4BFFFEB1386000014BFFEDC980A1001C80E5000055
+S32500013CC03FE0FFFF63FFFFCF7FE7383990E50000386000044BFFCA9983E100007FE803A687
+S32500013CE0382100204E8000207FE802A697E1FFE07C681B78386281E0900100085507183839
+S32500013D0090E1000C90010010480065997C681B789061001C7C030000408200103862833B0B
+S32500013D204BFFFCC98101001C7D03437883E100007FE803A6382100204E8000207FE802A6AA
+S32500013D4097E1FFE89061001C7C030000418200384BFFC4298121001C386281E07528E00009
+S32500013D603CA020007C0828004182002C7D274B7890E1000880810020548418389081000C5F
+S32500013D80480063B983E100007FE803A6382100184E8000203FE0FFFF63FFFFFF7FE7483981
+S32500013DA04BFFFFD07FE802A697E1FFE83E0000006210900081E1002481C100207C6A1B78D4
+S32500013DC091C3000C9061001C80E300007C070000408200247DC373784BFFFF113E000000E0
+S32500013DE06210900081E1002481C100208141001C906A000080C1002838C6000F3FE0FFFF67
+S32500013E0063FFFFF07FED3039808A00047C0400004082002C91A100287C6E69D64800518558
+S32500013E203E0000006210900081E1002481C1002081A100288141001C906A000480CA0000E3
+S32500013E407C060000418201A880AA00047C0500004182019C80AA000474A5E0003CC020001B
+S32500013E607C05300041820174808A00047C8C23787C0B03787C0B700040800044556418389B
+S32500013E8080EA00007C843A14B004000255661838808A00007CC622149186000455651838FB
+S32500013EA0810A00007CA54214B20500007D8C6A14396B00017C0B70004180FFC4556818388E
+S32500013EC080CA00007D083214A0A8FFF860A52000B0A8FFF8900A000891EA0020808A0010BC
+S32500013EE07C040000408200187DE37B784BFFFDFD81E100248141001C906A0010810A0014BD
+S32500013F007C0800004082001855E3103A4800509581E100248141001C906A001480EA0010A1
+S32500013F207C070000418200A080CA00147C060000418200947C0B03787C0B7800408000504C
+S32500013F405567103A80AA00147CE72A14900700005564183880EA00107C843A149004000475
+S32500013F6055661838808A00107CC62214B00600025568183880CA00107D083214B0080000FB
+S32500013F80396B00017C0B78004180FFB855651838810A00107CA54214A0E5FFF860E72000FF
+S32500013FA0B0E5FFF8900A0018900A001C900A00247C03037883E100007FE803A63821001869
+S32500013FC04E8000203860FFFF83E100007FE803A6382100184E800020808A00043FE0FFFF58
+S32500013FE063FFFFFF7FE420394BFFFE843860FFFF83E100007FE803A6382100184E80002069
+S325000140003821FFF0818282D0816100187C6A1B782C0B00094082005C7D8B6378388C00087C
+S325000140203FE0FFFF63FFFFF87FEC20397D675B78396B0001918282D07C07600040800014C7
+S32500014040808A0000810A00047C0440404180000C382100104E80002080EA000039270001D1
+S32500014060912A000038800020988700004BFFFFC080A30000808300047C0520404080001C97
+S325000140808103000039280001912300009968000038AC000190A282D02C0B000A408200080A
+S325000140A0900282D0382100104E800020814283E07C6907747D26077470C6007F7CC90774A5
+S325000140C07D24077438C285A87C843214888400007C8407747C0400004082002C2C0A001E07
+S325000140E04180000C386000014E8000207D26077438E285A87CC63A14390A0001910283E036
+S32500014100994600007D250774390285A87CA5421488A500007CA5077454A5103A38C284D063
+S325000141207CA5321480E1000890E500007C0303784E8000207FE802A697E1FFC890610018F4
+S3250001414080E1004090E1001C80810048908100208141004438EA000190E10044894A0000FF
+S325000141607D4A07742C0A0025418200487C0A00004082003080A1001880E1001C7C05384069
+S325000141804080000C80810018980400008061001883E100007FE803A6382100384E800020AB
+S325000141A038610018914100084BFFFE594BFFFFA49001002438A0FFFF90A100289001002C3E
+S325000141C03967000191610044894700007D4A07747C0C03782C0A002D4082001C39800001F1
+S325000141E07D6A5B78396B000191610044894A00007D4A07742C0A00304180000C2C0A00396C
+S325000142004081010C7C0C00004182001080A100247D050050910100242C0A002E41820084F6
+S325000142207C0A00004082000C38CBFFFF90C100443861001838A285A87146007F7CA5321438
+S3250001424088A500007CA5077454A5103A38C284D07CA5321480A500007CA803A64E800021B5
+S325000142607C0300004080002C7CA300508081002C7C852B7890A1002C81410044396A000185
+S3250001428091610044894A00007D4A07744BFFFF9480E100207CE71A1490E100204BFFFEB450
+S325000142A07D6A5B78396B000191610044894A00007D4A07742C0A00304180FF682C0A003950
+S325000142C0408100084BFFFF5C808100287C040000408000089001002880C1002854C5103A73
+S325000142E07CC62A1454C6083C7CC6521438C6FFD090C100287D6A5B78396B00019161004456
+S32500014300894A00007D4A07744BFFFFAC810100245507103A7D083A145508083C7D085214E1
+S325000143203908FFD0910100247D6A5B78396B000191610044894A00007D4A07744BFFFEB806
+S325000143407FE802A697E1FFE0906100244800907D7C6A1B789061001C80E1002C7C070000F0
+S325000143604180003C9141001C80C1002C7C0A30004080002C80610028388000209081000842
+S325000143804BFFFC8180C1001C394600019141001C80C1002C7C0A30004180FFDC81610024BF
+S325000143A07D665B78396B00019161001488C600007CC607747C0600004182003480E1003080
+S325000143C07C0700004182FFDC8061002890C100084BFFFC31816100148141001C8101003056
+S325000143E03908FFFF910100304BFFFFB880C1002C7C0600004080004880A1002C7D0500509E
+S325000144009101002C9141001C80A1002C7C0A28004080002C80610028390000209101000806
+S325000144204BFFFBE180A1001C394500019141001C80A1002C7C0A28004180FFDC83E10000AA
+S325000144407FE803A6382100204E8000207FE802A697E1FFA881C282E881A100607C6C1B7806
+S325000144607C0F037880C3001470C600072C0600044181015C418201442C060001418201281F
+S325000144802C0600024182010080AC00088145000038E0000490E1002880CC001470C60004D4
+S325000144A04082000C7C0A0000418000D0980100573960001C7FEA6B967FFF69D67CFF505029
+S325000144C0392700302C09003940810014392900277C0E0000418200083929FFE038C1003AB0
+S325000144E07CC65A14992600002C0B0002408000547C0F0000418200183881003A396BFFFFFE
+S325000145007C845A1438A0002D98A400003861003A7C635A149181000880EC000C90E1000CB6
+S3250001452038E0FFFF90E100104BFFFE198061002883E100007FE803A6382100584E80002060
+S325000145407D4A6B96810C00107C0800004180001880EC00103900001E7CE740507C0B38000D
+S32500014560408000107C0A0000418100084BFFFF84396BFFFF4BFFFF407D4A005039E00001EB
+S325000145804BFFFF2C80AC000880A500007CA907347D2407347C8A2378388000049081002874
+S325000145A04BFFFEF8808C00088144000038C0000490C100284BFFFEE4810C000881480000DC
+S325000145C038A0000490A100284BFFFED02C060005418200302C060006418200084BFFFEAC66
+S325000145E080AC000880A500007CA907345524043E7C8A237838800004908100284BFFFE9CC6
+S3250001460080E300088147000038800004908100284BFFFE887FE802A697E1FFE87C671B78B7
+S32500014620386280B890E100089001000C3880FFFF908100104BFFFD0D7C03037883E1000002
+S325000146407FE803A6382100184E8000207FE802A697E1FFE07C681B7880E3000880E7000035
+S3250001466098E1001E9801001F3861001E910100088088000C9081000C3880FFFF908100108B
+S325000146804BFFFCC13860000483E100007FE803A6382100204E8000207FE802A697E1FFF01F
+S325000146A03900000A910100084BFFFDA583E100007FE803A6382100104E8000203860FFFECA
+S325000146C04E8000203860FFFF4E8000207FE802A697E1FFF038E0000890E100084BFFFD719A
+S325000146E083E100007FE803A6382100104E8000207FE802A697E1FFE87C671B788063000819
+S325000147008063000090E100088107000C9101000C81070010910100104BFFFC2938600004BF
+S3250001472083E100007FE803A6382100184E8000203860FFFC4E8000207FE802A697E1FFF0A8
+S3250001474038A0001090A100084BFFFD0583E100007FE803A6382100104E8000207FE802A60B
+S3250001476097E1FFE839000001910282E838C0001090C100084BFFFCD9900282E883E10000BC
+S325000147807FE803A6382100184E8000207FE802A697E1FFF038E0002590E100084BFFF865D0
+S325000147A07C03037883E100007FE803A6382100104E8000207FE802A697E1FFD89061002CB2
+S325000147C03862889880A1003090A100084BFFF19581A100308181002C3942A1F83902A1F856
+S325000147E0390801407C0A40404080003080CA00047C0D30004082001080EA000C7C0C38002B
+S32500014800418201B4394A00143902A1F8390801407C0A40404180FFD83962A1F838C2A1F8C7
+S325000148203946001438C2A1F838C601407C0A30404080002C808A0000810B00007C044000D4
+S32500014840418100087D4B5378394A001438C2A1F838C601407C0A30404180FFDC7D6A5B783F
+S3250001486080CC001480EC00107D6639D69141002480CA00087C0600004182011480AA001087
+S325000148807C055800418001089161001C916A0010386288BD80AC000C7CAD2A14810C00103A
+S325000148A07CA541D690A100084BFFF0B98141002C806A000080EA000C812100307CE93A14BA
+S325000148C080AA00107CE729D690E1000880AA00087CA803A64E8000218141002C7C03000061
+S325000148E040800020386288CF4BFFF0797C03037883E100007FE803A6382100284E80002050
+S32500014900806A00008101002481080008910100088101001C9101000C80CA00047CC803A65E
+S325000149204E800021816100308141002480A1001C7C03280041820020386288E14BFFF02560
+S325000149407C03037883E100007FE803A6382100284E8000208082801480840000908A0000BF
+S3250001496080C1002C90CA000C916A0004386288F3916100084BFFEFED8061002483E10000C0
+S325000149807FE803A6382100284E8000207D635B789161001C90010008480045818161001C2B
+S325000149A08141002481A100308181002C906A00084BFFFED89141002480A2801480A5000097
+S325000149C090AA0000386288A791A100084BFFEF958061002483E100007FE803A638210028CB
+S325000149E04E8000207FE802A697E1FFD89061002C3862890580E1003090E100084BFFEF6577
+S32500014A00816100308141002C2C0B0002418001AC80EA00347C0B3800408001A0810A003867
+S32500014A202C08000C418201802C080010418200183860FFFF83E100007FE803A63821002841
+S32500014A404E800020556B083C810A0030812A00107D0849D6916100247C0B40404180001451
+S32500014A60386289114BFFEF85816100248141002C808A001080EA00147C8439D67C8B2396E2
+S32500014A80810A00147C8441D680EA003C7C843A1480EA0010810A00147CE741D67FEB3B9697
+S32500014AA07FFF39D67CFF585090E100187D43537890810020908100084BFFFCFD8181002C70
+S32500014AC07C6A1B7880C3000881010018396800017CC6421488C6000090C1002480CC001018
+S32500014AE080EC00147CC639D67C0B3000418000287D836378808C001480A100207C852214CB
+S32500014B00908100084BFFFCB18181002C7C6A1B787C0B0378810A00087D085A1489080000BE
+S32500014B205508402E808100247C8A437880EC00382C07000C4082002080C1003070C6000150
+S32500014B404182005C554AE13E280A0FF841800008614AF0003CE0000060E7FFF87C0A38407C
+S32500014B60408000347D4453783862891880A1003090A10008908100249081000C4BFFEDE57B
+S32500014B808061002483E100007FE803A6382100284E8000203880FFFF4BFFFFD0714A0FFF8E
+S32500014BA04BFFFFA85567083C7D0B3A14392000027D684BD64BFFFE943860FFFF83E10000F0
+S32500014BC07FE803A6382100284E8000207FE802A697E1FFD87C691B7881030000910100243F
+S32500014BE03862892A9121002C390900049101000880C1003090C1000C4BFFED6981A1002CE7
+S32500014C008181003081610024808D00147C04000040820070810B00107D0C41D680CB0014E7
+S32500014C207D0831D680AB002454A528347C082800418000183860FFFF83E100007FE803A6AE
+S32500014C40382100284E800020808B004080AB00147CAC29D67C842A143862893D9181000875
+S32500014C60908100209081000C4BFFECF98061002083E100007FE803A6382100284E800020CC
+S32500014C80808D001C7C0C20004180001080AD00187C050000408200A87C0A0378812D001478
+S32500014CA0914100207C0A6000418200507D635B78912100084BFFFD3181A1002C818100309D
+S32500014CC0816100247C691B787C030000408000183860FFFF83E100007FE803A6382100286D
+S32500014CE04E80002080C1002039460001914100207C0A60004082FFB8914D001C912D0018BD
+S32500014D0038E9FFFE80CB00147CE731D680AB00447CE53A14386289509181000890E1002069
+S32500014D2090E1000C4BFFEC3D8061002083E100007FE803A6382100284E800020814D001CAE
+S32500014D40812D00184BFFFF5C7FE802A697E1FFC87C6A1B788903000F7108001040820050E4
+S32500014D60808A0020810A00107C044000418000187C03037883E100007FE803A638210038CF
+S32500014D804E80002080EA0020810100447CE74214808A00107C07200040810014810A0010E8
+S32500014DA0812A00207D09405091010044810100409101001C7C0B03789161003080E10044FC
+S32500014DC07C0B38004080010C7D43537880CA00209141003C80EA000080E700187CC63BD601
+S32500014DE090C100084BFFFDE97C6A1B787C030000408000183860FFFF83E100007FE803A649
+S32500014E00382100384E8000208061003C80630000914100084BFFF9A18181003C7C03000091
+S32500014E20408200183860FFFF83E100007FE803A6382100384E80002080CC002080EC000030
+S32500014E4080E700187FE63BD67FFF39D67D5F3050810300087DA8521480C1004480810030A0
+S32500014E607D64305080EC000080E700187CEA38507C0B380040810010810C00008108001833
+S32500014E807D6A40508061001C91A100089161002C9161000C480080CD8161002C8141003CA0
+S32500014EA080E1001C7CA75A1490A1001C808A00207CE45A1490EA002080E100307D675A141B
+S32500014EC09161003080E100447C0B38004180FEFC7D635B7883E100007FE803A638210038D2
+S32500014EE04E8000207FE802A697E1FFC08883000F7084001040820020386289634BFFEA6558
+S32500014F003860FFFF83E100007FE803A6382100404E8000209061004480C1004890C10008E2
+S32500014F2048000EA981210044900900207D234B78388100209081000838E0002090E1000CC2
+S32500014F404BFFFE092C030020408201103862897B388100209081000838C1002038C6000828
+S32500014F6090C1000C4BFFE9FD806100443863000438A1002090A1000838E0000890E1000C0A
+S32500014F8048008549812100447C030000418200084BFFFF9C3869000C38C1002038C6000813
+S32500014FA090C10008390000039101000C4800851D812100447C030000418200084BFFFF70E4
+S32500014FC088A1002B70A500084182002838628995388100209081000838C1002038C60008A5
+S32500014FE090C1000C4BFFE97D812100444BFFFF4088A1002B98A9000F88E1003B54E7402E3D
+S325000150008901003A7CE7437890E900148881003F5484402E88A1003E7C842B785484801E0C
+S3250001502088A1003D54A5402E88C1003C7CA533787C842B7890890010900900189009001C19
+S32500015040900900203860000183E100007FE803A6382100404E8000207C03000041800018A4
+S325000150607C03037883E100007FE803A6382100404E8000203860FFFF83E100007FE803A62A
+S32500015080382100404E8000207FE802A697E1FFD07C6D1B7880E1003890E3000C38C002009E
+S325000150A090C3001038800001908300147C0903787DA36B7891A100349121001C91210008B5
+S325000150C04BFFF6F581A100347C6A1B787C03000040820020386289AD4BFFE8893860FFFF43
+S325000150E083E100007FE803A6382100304E80002081030008890801FE2C08005540820380D4
+S3250001510080C3000888C601FF2C0600AA408203709003000C8123000880C1003C7C06000094
+S3250001512041820010890900152C0800F8408201E8812A000888A900002C0500E9418200381E
+S3250001514088C900002C0600EB4082001088E900022C07009041820020386289CD4BFFE805C8
+S325000151603860FFFF83E100007FE803A6382100304E8000207D234B789121002C4800031DFE
+S325000151808141002C81210034888A000C5484402E88CA000B7C8433789089001088EA000D30
+S325000151A090E900148089001080A900147C8429D69089001888CA000F54C6402E890A000E4B
+S325000151C07CC6437890C9001C888A00109089002088CA001254C6402E890A00117CC643786E
+S325000151E090C9002488EA001454E7402E888A00137CE7237890E90028810900287C08000002
+S325000152004082003088EA002354E7402E88AA00227CE72B7854E7801E88CA002154C6402E29
+S32500015220890A00207CC643787CE7337890E90028888A00159089002C88CA001754C6402EAB
+S32500015240890A00167CC6437890C900308089001C9089003C8089002080A900307C8429D61D
+S3250001526080A9003C7C8522149089004080C9002454C6283480E900107CC63A143946FFFFC9
+S3250001528080C900107D4A33D680C900407CC6521490C900448089002880A900447C85205001
+S325000152A080A900147C842BD6388400029089003480C900342C060FF74080005038E0000CB5
+S325000152C090E9003839000002910900489129004C9009006090090068900900649009006CF7
+S325000152E038A0001098A9005B81090024550828349109005C7C03037883E100007FE803A658
+S32500015300382100304E80002038A0001090A900384BFFFFB4808A0008392401BE3980FFFFD4
+S325000153207C0B03782C0B000440800064890900042C0800544082000C3920003F4BFFFD74C6
+S3250001534088A900042C0500014182001088C900042C0600044082011889090000710800801B
+S32500015360418200A88889000B5484402E88E9000A7C843B785484801E890900095508402E4F
+S3250001538088A900087D082B787C8C43782C0B0004408200183900FFFF7C0C40004082000C00
+S325000153A0900A000C4BFFFD8C80C1001C7CC6621490CD000C7DA36B78900100084BFFF3F91D
+S325000153C07C6A1B787C0300004182001480E3000888E701FE2C070055418200183860FFFF25
+S325000153E083E100007FE803A6382100304E80002080A3000888A501FF2C0500AA4082FFE0E7
+S32500015400900A000C4BFFFD2C88E9000B54E7402E88A9000A7CE72B7854E7801E88C90009D3
+S3250001542054C6402E890900087CC643787CE733787C0760404080002C88C9000B54C6402E45
+S325000154408889000A7CC6237854C6801E88A9000954A5402E88E900087CA53B787CCC2B78BC
+S32500015460396B0001392900104BFFFEBC88E900042C0700064082FFEC4BFFFEE0386289C4A0
+S325000154804BFFE4E13860FFFF83E100007FE803A6382100304E8000207FE802A697E1FFE807
+S325000154A07C691B78386289E1888900009081000888C9000190C1000C9121001C890900022E
+S325000154C0910100104BFFE49D38628A0180A1001C38A5000390A100084BFFE4898121001C68
+S325000154E038628A138909000C5508402E88A9000B7D082B78910100084BFFE46938628A212B
+S3250001550080E1001C88E7000D90E100084BFFE4558121001C38628A308889000F5484402E17
+S3250001552088C9000E7C843378908100084BFFE43538628A3C8101001C8908001091010008A5
+S325000155404BFFE4218121001C38628A4788A9001254A5402E88E900117CA53B7890A1000893
+S325000155604BFFE4018121001C38628A55888900145484402E88C900137C843378908100082B
+S325000155804BFFE3E138628A628101001C89080015910100084BFFE3CD8121001C38628A7640
+S325000155A088A9001754A5402E88E900167CA53B7890A100084BFFE3AD8121001C38628A8362
+S325000155C0888900195484402E88C900187C843378908100084BFFE38D8121001C38628A90FB
+S325000155E08909001B5508402E88A9001A7D082B78910100084BFFE36D8121001C38628A9C07
+S3250001560088E9001F54E7402E88A9001E7CE72B7854E7801E88C9001D54C6402E8909001C78
+S325000156207CC643787CE7337890E100084BFFE3358121001C38628AA988A9002354A5402E37
+S32500015640890900227CA5437854A5801E888900215484402E88C900207C8433787CA52378CC
+S3250001566090A100084BFFE2FD38628AB98101001C89080024910100084BFFE2E938628AC6F8
+S3250001568080A1001C88A5002590A100084BFFE2D538628ADA80E1001C88E7002690E10008B1
+S325000156A04BFFE2C18121001C38628AEC8889002A5484402E88E900297C843B785484801EE4
+S325000156C0890900285508402E88A900277D082B787C844378908100084BFFE28938628AFC15
+S325000156E080E1001C38E7002B90E100084BFFE27583E100007FE803A6382100184E800020EF
+S325000157007FE802A697E1FFE8816100207C691B7888C900007CC607742C06002F4082001851
+S325000157203929000188C900007CC607742C06002F4182FFF088A900007CA507747C05000090
+S32500015740418200C088C900007CC607742C060020418200B07C0A037888A900007CA507741E
+S325000157607C0500004182001488C900007CC607742C06002F4082001C980B00007D234B7877
+S3250001578083E100007FE803A6382100184E80002088E900007CE707742C0700204082001CAF
+S325000157A0980B00007D234B7883E100007FE803A6382100184E8000202C0A001C40820020D5
+S325000157C038628B0E4BFFE19D7C03037883E100007FE803A6382100184E8000207D655B7845
+S325000157E0396B00017D264B783929000188C600007CC6077498C50000394A00014BFFFF5CA3
+S325000158007C03037883E100007FE803A6382100184E8000207FE802A697E1FFD080E1003CC1
+S325000158207C641B7838C0000380A4004C8104005090A7000080A400549107000490A7000824
+S3250001584038C6FFFF3884000C38E7000C7C0600004181FFD88061003838A1001490A10008F8
+S325000158604BFFFEA1906100387C030000418200508061003C38A1001490A100084BFFF66991
+S325000158802C03FFFF418200247C030000418200084BFFFFC47C03037883E100007FE803A628
+S325000158A0382100304E8000203860FFFF83E100007FE803A6382100304E8000203860000150
+S325000158C083E100007FE803A6382100304E8000207FE802A697E1FFA880E1006090E100086E
+S325000158E038C1003490C1000C4BFFFF2D814100602C03FFFF418204207C030000418203F82E
+S325000159002C030001418203B43860002090010008480036097C6B1B7838A0002038610034BF
+S3250001592090A1003090A10008916100289161000C4800040581410028810100307C03400002
+S3250001594041820020806282CC4BFFE0193860FFFF83E100007FE803A6382100584E80002041
+S3250001596088CA000054C6402E888A00017CC6237854C6801E88AA000254A5402E88EA00038E
+S325000159807CA53B787CC62B782C0606EB4182004C38628B7C890A00005508402E88CA0001C3
+S325000159A07D0833785508801E88EA000254E7402E888A00037CE723787D083B7891010008B3
+S325000159C04BFFDFA13860FFFF83E100007FE803A6382100584E80002088EA001454E7402E1E
+S325000159E088AA00157CE72B7854E7801E890A00165508402E892A00177D084B787CE74378D6
+S32500015A0074E7E0003C8020007C0720004182027488CA001454C6402E88EA00157CC63B7822
+S32500015A2054C6801E890A00165508402E88AA00177D082B787CC6437890C1002C890A0004B1
+S32500015A405508402E88CA00057D0833785508801E88EA000654E7402E888A00077CE72378BA
+S32500015A607D083B7838628BA791010030910100084BFFDEF13861003480A1003090A100084F
+S32500015A808101002C9101000C480002AD81610030814100287C03580041820020806282CCD6
+S32500015AA04BFFDEC13860FFFF83E100007FE803A6382100584E80002080E1002C7C8B3A146B
+S32500015AC038840FFF3FE0FFFF63FFF0007FE420399081002C88AA000854A5402E890A000950
+S32500015AE07CA5437854A5801E888A000A5484402E88CA000B7C8433787CA5237838628BAB36
+S32500015B0090A1003090A100084BFFDE593861003480E1003090E1000880A1002C90A1000C02
+S32500015B204800021581410028810100307C03400041820020806282CC4BFFDE293860FFFFAA
+S32500015B4083E100007FE803A6382100584E80002038628BAF88CA000C54C6402E888A000D52
+S32500015B607CC6237854C6801E88AA000E54A5402E88EA000F7CA53B787CC62B7890C100084F
+S32500015B80888A00145484402E88EA00157C843B785484801E890A00165508402E88AA001725
+S32500015BA07D082B787C8443789081000C4BFFDDB581410028890A00145508402E88CA00153F
+S32500015BC07D0833785508801E892A00165529402E888A00177D2923787D084B787508E000CF
+S32500015BE03CA020007C0828004182004888EA001454E7402E890A00157CE7437854E7801E1D
+S32500015C00888A00165484402E88CA00177C8433787CE723787CE803A64E8000217C03037807
+S32500015C2083E100007FE803A6382100584E80002088EA001454E7402E888A00157CE72378F6
+S32500015C4054E7801E88AA001654A5402E890A00177CA543787CE72B783FE0FFFF63FFFFFFAC
+S32500015C607FE738397CE803A64E8000217C03037883E100007FE803A6382100584E8000203D
+S32500015C8088CA001454C6402E890A00157CC6437854C6801E888A00165484402E88EA0017BB
+S32500015CA07C843B787CC623783FE0FFFF63FFFFFF7FE630394BFFFD8438628B4A38A10034BB
+S32500015CC038A5000490A1000838E1003438E7000C90E1000C888100439081001080810048F8
+S32500015CE09081001480E1004490E100184BFFDC754BFFFC1838628B3C914100084BFFDC658B
+S32500015D003860FFFF83E100007FE803A6382100584E80002038628B27914100084BFFDC4542
+S32500015D203860FFFF83E100007FE803A6382100584E8000207FE802A697E1FFD88161003043
+S32500015D409061002C80E1003490E100247C0A03787C0A580040800044392020007C8A5850EB
+S32500015D607C044800408000087D2A58508061002C808100249141001C7C845214908100089E
+S32500015D809121000C4BFFEFC58141001C906100207C030000418100187D43537883E1000009
+S32500015DA07FE803A6382100284E80002038628BC44BFFDBB1816100308081002080C1001C0E
+S32500015DC07D4622144BFFFF8C7FE802A697E1FFE0812100287C6B1B783943000488E9000053
+S32500015DE07CE707747C070000418200147CCB505038C6FFFC2C060008418001047CAB5050BD
+S32500015E0038A5FFFC2C050008408000247D455378394A000138C0002098C500007CAB505039
+S32500015E2038A5FFFC2C0500084180FFE4394B000C88A900007CA507747C05000041820014F1
+S32500015E407C8B50503884FFF42C0400034180005C7C8B50503884FFF42C0400034080002428
+S32500015E607D445378394A000138A0002098A400007C8B50503884FFF42C0400034180FFE4AA
+S32500015E8038628BC638AB000490A10008390B000C9101000C4BFFDACD83E100007FE803A69D
+S32500015EA0382100204E800020888900007C8407742C0400614180001488A900007CA50774B5
+S32500015EC02C05007A4081001C888900007C840774988A000039290001394A00014BFFFF5401
+S32500015EE0890900007D0807743908FFE0990A000039290001394A00014BFFFF3888C900008D
+S32500015F007CC607742C06002E4082000C392900014BFFFEEC88A900007CA507742C05006199
+S32500015F204180001488C900007CC607742C06007A4081001C88A900007CA5077498AA0000DF
+S32500015F4039290001394A00014BFFFE94888900007C8407743884FFE0988A000039290001CB
+S32500015F60394A00014BFFFE787FE802A697E1FFC8900100287C0A03782C0A00044080006074
+S32500015F8091410030554818387C8A4214548428347C88205038A2D7D07C642A149061003443
+S32500015FA09001000838E0011890E1000C48006D818061003080C1003490C100084BFFA6CDC1
+S32500015FC0816100347C0300004181002880810030394400012C0A00044180FFA880610028E1
+S32500015FE083E100007FE803A6382100384E8000207C0A03785547183838C282807CE7321415
+S3250001600080E700007C0700004182FFC4914100245543183838E282807C633A1480630000FF
+S32500016020916100084800743D81610034814100247C0300004182000C394A00014BFFFFB897
+S3250001604080810030908B00EC7D635B785548183838E282807D083A14810800047D0803A6B7
+S325000160604E8000217C030000418200084BFFFF603860400038A0000190A100089001000CB0
+S325000160809001001048003E5580E10034906700F43860400038C0000190C100089001000C36
+S325000160A09001001048003E3581410034906A00F839000001910A00F03880000180C10030A6
+S325000160C07C843030810100287D0423789081002838629A1180CA00EC90C100089141000CA8
+S325000160E0808A001C90810010808A0020908100144BFFD87181410034808A00247C040000CC
+S325000161004182002C38629A31808A00247484E0003D2020007C04480041820098810A0024CF
+S32500016120910100084BFFD83D8141003480CA00287C0600004182001438629A3E80AA0028DA
+S3250001614090A100084BFFD81D38629A4B4BFFD8157C0A03782C0A00064080003038629A4D5C
+S325000161609141002C80C100347CAA321488A5002C90A100084BFFD7ED80A1002C39450001CD
+S325000161802C0A00064180FFD838629A554BFFD7D54BFFAEB181410034806A0020810A00D0A1
+S325000161A0910100089141000C48004AC94BFFFE20810A00243FE0FFFF63FFFFFF7FE8403991
+S325000161C04BFFFF607FE802A697E1FFE82C03000440800080546618387CA3321454A528346A
+S325000161E07CA6285038C2D7D07CA5321480A500F07C0500004182005C546718387D033A1468
+S32500016200550828347D0740503922D7D07D484A1480AA00F02C0500014082002438C0000259
+S3250001622090CA00F07D43537891410014810A00BC7D0803A64E800021814100147D435378D7
+S3250001624083E100007FE803A6382100184E8000207C03037883E100007FE803A63821001885
+S325000162604E8000207FE802A697E1FFE84BFFFF597C030000408200187C03037883E1000062
+S325000162807FE803A6382100184E8000203863002C83E100007FE803A6382100184E800020EE
+S325000162A07FE802A697E1FFD84BFFFF1D906100207C030000408200187C03037883E100004B
+S325000162C07FE803A6382100284E80002080828014808400009081001880610020806300F49D
+S325000162E048003C357C6A1B787C0300004082005080A2801480A5000080E100187CA72850E5
+S3250001630054A4083C7CA5221454A518385484482C7CA5205038C000327CA5339680C1003434
+S325000163207C0530404180FFB47C03037883E100007FE803A6382100284E800020810300048C
+S32500016340812300007D094050806100309141001C808A000090810008910100249101000C06
+S3250001636048006C018061001C48003B118061002483E100007FE803A6382100284E800020E8
+S325000163807FE802A697E1FFE87C691B7880610020806300009121001C3889002C9081000858
+S325000163A038E0000690E1000C48007121812100207C03000041820028806900003902836826
+S325000163C09101000838A0000690A1000C480070FD812100207C030000408200288061001C24
+S325000163E0806300F49121000848003BA93860000183E100007FE803A6382100184E8000206D
+S3250001640080E100247C0700004182000C7D234B7848003A697C03037883E100007FE803A6E2
+S32500016420382100184E8000207FE802A697E1FFD89061002C4BFFFD917C030000408200184A
+S325000164407C03037883E100007FE803A6382100284E80002090610024806300F848003BC520
+S325000164602C0340004081002838629A5780A1002C90A100084BFFD4ED7C03037883E1000043
+S325000164807FE803A6382100284E800020386005F0480039919061002080630004810100302D
+S325000164A09101000880C1003490C1000C48006AB580610020806300043863000680E10024F4
+S325000164C038E7002C90E1000838A0000690A1000C48006A91812100208081003480C900044F
+S325000164E07C8622149089000480610024806300F89121000848003A9D4BFF9CA98121002432
+S325000165009061001C7D234B78810900CC7D0803A64E8000218061001C4BFF9CA53860000170
+S3250001652083E100007FE803A6382100284E8000207FE802A697E1FFF880A300EC54A7183899
+S325000165407CA53A1454A5103A3902A3387CA5421480A5000080C5000060C6003090C50000E0
+S325000165604BFF9C1983E100007FE803A6382100084E8000207FE802A697E1FFD89061002CD7
+S325000165808081002C808400EC548518387C842A145484103A38C2A3387D24321481090020E8
+S325000165A0550818389121002080C900187D08321491010018A1080000710880004082001CFF
+S325000165C08061002C806300F84800394D7C691B787C0300004082001483E100007FE803A6BD
+S325000165E0382100284E80002080A3000480C300007CA6285080610018806300049121001C73
+S32500016600810900009101000890A1002490A1000C4800695180C1001881010024B106000203
+S325000166204BFF9B5981210018A0A9000070A5200060A5DC00B0A900004BFF9B41808100205C
+S32500016640808400003D00000061088000B104000C4BFF9B298121002080C9002038C6000110
+S3250001666038E000047FE63BD67FFF39D67CDF305090C900208061001C480038018121002060
+S325000166804BFFFF1C7FE802A697E1FFC8808100409081001C80C400EC54C718387CC63A14A7
+S325000166A054C6103A3902A3387CC6421490C1002880A60000A0A5001090A100304BFF9ABDCB
+S325000166C0808100288084000081010030B10400104BFF9AA98181001C8141002881010030C8
+S325000166E0710800154182002438629BD880AC00EC90A1000880A1003090A1000C4BFFD265B1
+S325000167008181001C8141002881010030710800094082000480AA001454A5183880CA000C93
+S325000167207D65321491610024A0AB00007CA92B7870A58000408201247128003F408200C427
+S3250001674071250C002C050C00408200B8A0AB00023865FFFC90610034480036C98161002482
+S325000167607C0300004182004C9061002080630004808B000490810008808100349081000C12
+S32500016780480067E1812100208081003480C900047C862214908900048061001C9121000812
+S325000167A0390000019101000C4BFFFBD981610024B00B0002A08B0000708420006084900066
+S325000167C0B08B00004BFF99B58181001C8141002880AA001438A5000138C000207FE533D636
+S325000167E07FFF31D67CBF285090AA001480EA001454E71838810A000C7D6742144BFFFF28C1
+S32500016800712700044182001080CC010438C6000190CC01047124000241820010810C011049
+S3250001682039080001910C01107126003F4182001438629BF44BFFD12D816100244BFFFF7480
+S32500016840712808004082FF6C38629BF64BFFD115816100244BFFFF5C80E1003070E7000273
+S32500016860418200107D8363784BFFFD0D8141002880A1003070A50010418200183860000636
+S3250001688080EA000890E100089001000C4BFFD2D183E100007FE803A6382100384E80002089
+S325000168A07FE802A697E1FFE03DA0000061AD90007C6A1B78906100248103000C7C0800004E
+S325000168C04082001C386000204BFFD4213DA0000061AD900081410024906A000C80CA00101B
+S325000168E07C060000408200243C6000006063BE0090010008480026253DA0000061AD900065
+S3250001690081410024906A001080AA001074A5E0003CC020007C0530004182015C808A001046
+S325000169207C8C23787C0B03782C0B0020408000445567183880AA000C7CE72A14B0070002B4
+S325000169405564183880EA000C7C843A14918400045568183880CA000C7D083214B1A80000C3
+S32500016960398C05F0396B00012C0B00204180FFC45564183880EA000C7C843A14A0C4FFF8AE
+S3250001698060C62000B0C4FFF8900A001480CA00187C06000040820014386000044BFFD34DD1
+S325000169A081410024906A0018808A001C7C04000040820018386017C0900100084800255D86
+S325000169C081410024906A001C810A001C7508E0003D2020007C0848004182008880EA001C96
+S325000169E07CEC3B787C0B03782C0B00044080004855651838810A00187CA5421491850004F2
+S32500016A005564183880EA00187C843A14B004000255661838808A00187CC6221439005C00A6
+S32500016A20B1060000398C05F0396B00012C0B00044180FFC05567183880AA00187CE72A148F
+S32500016A40A087FFF860842000B087FFF8900A002083E100007FE803A6382100204E8000204A
+S32500016A6080EA001C3FE0FFFF63FFFFFF7FE738394BFFFF70808A00103FE0FFFF63FFFFFFE5
+S32500016A807FE420394BFFFE9C7FE802A697E1FFD080A3000454A5083C3985FFFE80A2801424
+S32500016AA08145002038A000037CAB6030A0CA09527CC75B78B0EA09527D6658F8A0AA0950B1
+S32500016AC07CA63038B0CA09507D6558F8A08A09547C852838B0AA095438E000307CEB603042
+S32500016AE07D6458F8A10A09627D042038B08A09627D6858F8A0EA09607CE84038B10A0960A2
+S32500016B00A0CA09647CC75B78B0EA0964906100348063000838E1002890E1000838810024D3
+S32500016B209081000C4BFF9BDD8061003480630004808100289081000880E1002490E1000C2F
+S32500016B404BFFD0D180A1003480A5000454A5103A38C283A87CA53214806500009061001C04
+S32500016B609001000838A000A490A1000C480061C13D8020008161001C8141003438800018B1
+S32500016B80988B000438E0001898EB000538A005F0B0AB0006810A000C7508E0007C08600009
+S32500016BA04182018480CA000CB0CB000080AA001874A5E0007C05600041820158810A0018DA
+S32500016BC0B10B00027C03037880AA000890A100089001000C4BFFCF8981A100388181003CB4
+S32500016BE08161001C38E0FFFF90EB00303CA0DEBB60A520E390AB0034900B0038900B003C39
+S32500016C00900B00403C80000060848888B08B004438E0000FB0EB004638A005EEB0AB004AEB
+S32500016C2039000040B10B004C38C005F0B0CB004E388005F0B08B0050B00B00787C0A0378AA
+S32500016C402C0A00064080003C7CEA621488E7000154E7402E7D0C5214890800007CE7437862
+S32500016C60392000027D0A4BD65508083C7D085850B0E80076394A00022C0A00064180FFCCE1
+S32500016C803900080AB10D00083CC0000060C6D555B0CD000E900D00043CC0108860C6000C9E
+S32500016CA090CD00004BFF94D580E100383C8000006084FFFFB08700104BFF94C180A1003847
+S32500016CC038E0001AB0E500144BFF94B1810280148168002081010034810800043908FFFFA1
+S32500016CE0392010007D284030808B0ABC7C87437890EB0ABC80CB0AB87CC44378908B0AB85A
+S32500016D0083E100007FE803A6382100304E800020810A00183FE0FFFF63FFFFFF7FE8403981
+S32500016D204BFFFEA080CA000C3FE0FFFF63FFFFFF7FE630394BFFFE747FE802A697E1FF7011
+S32500016D40906100943861008A9001000838A0000690A1000C48005FD93861008A8101009417
+S32500016D603908002C9101000838C0000690C1000C48006759812100947C030000408200909B
+S32500016D8038629BF84BFFCBDD3861001C39029C11910100084800666538629C1E38C1004EE8
+S32500016DA090C10008390000329101000C38C1001C90C100104BFFCA357C0300004080001854
+S32500016DC03860FFFF83E100007FE803A6382100904E800020806100943863002C3881004E88
+S32500016DE0908100084BFFC209812100947C0300004080001C88C9002C2C0600FF418200104C
+S32500016E0038629C304BFFCB5D4BFFFF8080E9001C54E7103A388280887CE7221480E70000FE
+S32500016E2080C2801480C600207D663A1480C9001C54C6103A38E280307CC63A1480C6000075
+S32500016E4038C6001090C9002080E900EC54E818387CE7421454E7103A3882A3387C672214D7
+S32500016E60916100809163000080E9001C90E3000480E9001C54E7103A390283107CE7421418
+S32500016E8080E7000090E30008906100844BFFFA158061008480A1008090A1000881010094E6
+S32500016EA03908002C9101000C4BFFFBE1812100943D00FFC261086E38910900B83CE0FFC228
+S32500016EC060E7663090E900BC3CC0FFC260C6667490C900CC3CA0FFC260A5678490A900D021
+S32500016EE07C03037883E100007FE803A6382100904E8000203821FFD08161003C82810038C5
+S32500016F00824100407C751B7838A000017CAE583038A000017CA570303A65FFFF38A0000148
+S32500016F207CAA903038A000207CAF96307D4558307E2E2850388000207DAF20507C0D000010
+S32500016F40418000787C0A03787EB06C307C0C03787C0B03787C0B7800408000247E665830D2
+S32500016F607E0630387CC660307D4A33787D8C8A147D6B72147C0B78004180FFE47C0B037820
+S32500016F807C0B90004080001C7DC758307D4738307D4A3B78396B00017C0B90004180FFEC1D
+S32500016FA07E88A3783A940004914800007DAF68507C0D00004080FF90382100304E800020CB
+S32500016FC07FE802A697E1FEF89061010C3861010C480006498141011880C101248081011C93
+S32500016FE07CC430514181001483E100007FE803A6382101084E8000208081012880E1012083
+S325000170007C8720503884FFFF908100FC80A100FC7C0500004080001483E100007FE803A649
+S32500017020382101084E80002080E1010C81A7000C808101107C87683090E1011091A100F402
+S325000170407CCB6830836A000C8081011C7C87D83090E1011C936100F8808101247C87D8307D
+S3250001706090E1012480E1010C83470008832A000880E10114934100E87CE7D1D680A1010C14
+S3250001708080A500047CE72A14808101107C842E707CE43A1454E7103A80C1010C80C60000A8
+S325000170A07EE7321492E100CC80C10120932100EC7CC6C9D6808A00047CC622148101011CD7
+S325000170C07D082E707CC8321454C6103A80AA00007EC62A1492C100D080A1011070AC001F5C
+S325000170E03900FFFF7D086430910100B0918100DC7D0C5A147108001F38A000207D08285085
+S3250001710038C0FFFF7CC840303FE0FFFF63FFFFFF7FE84039910100AC810100AC7C08000071
+S325000171204082000C38A0FFFF90A100AC80C1011C70C6001F90C100E4810101107C8B4214EF
+S325000171403884FFFF7C842E7080A101107CA52E707C8520509081010080A1010C7C0A280080
+S325000171604082005880E10120808101147C0720004080046080C100FC7CC6D1D654C6103A05
+S325000171807EF73214808100FC7C84C9D65484103A7ED6221480EA000854E7083C7F27C85036
+S325000171A0932100EC8081010C808400085484083C7F44D050934100E8808101007C040000D1
+S325000171C04082001480C100AC810100B07D06303890C100B080C1012C70C6000F90C1012C96
+S325000171E07E9B68507D85A63080C100E47E6530507C1803787C1300004080000C3B000001B1
+S325000172007E7300507C1003787C15037892A100A480A100FC7C152800418100F892E100CC6D
+S325000172207EECBB7892E100C492C100D07ECFB37881A100B091A100C07C0E03787C1800007B
+S32500017240408200107DE57B7839EF000481C500007C12037882210100922100A87C110000F9
+S325000172604180008C7C1200004182027C7E0783783A10000481670000814C00008101012CB9
+S325000172802C08000841810194418201702C08000441810110418200F87C080000418200D83B
+S325000172A02C080001418200B02C080002418200942C0800034182006C398C0004918100C48D
+S325000172C039A0FFFF91A100C02C1100014082000C81A100AC91A100C03A52FFFF3A31FFFF1F
+S325000172E0922100A87C1100004080FF7C5727103A7ED63A145747103A7EF73A143AB500015F
+S3250001730092A100A480A100FC7C1528004081FF1083E100007FE803A6382101084E80002025
+S325000173203FE0FFFF63FFFFFF7FE65A787CC652787CC668387D46327890CC00004BFFFF7C16
+S325000173407D6750387CE768387D473A7890EC00004BFFFF683FE0FFFF63FFFFFF7FE85A785E
+S325000173607D0853787D0868387D484278910C00004BFFFF483FE0FFFF63FFFFFF7FE56A7821
+S325000173807D45283890AC00004BFFFF307D6553787CA568387D452A7890AC00004BFFFF1C9C
+S325000173A02C080005418200482C080006418200302C080007418200084BFFFF003FE0FFFFE9
+S325000173C063FFFFFF7FE852787D6843787D0868387D484278910C00004BFFFEE07D67683888
+S325000173E07D473A7890EC00004BFFFED07D446A78908C00004BFFFEC43FE0FFFF63FFFFFF34
+S325000174007FE75A787CE750387CE768387D473A7890EC00004BFFFEA42C08000C41810070EF
+S32500017420418200582C080009418200342C08000A4182FE882C08000B418200084BFFFE7CA1
+S325000174403FE0FFFF63FFFFFF7FE75A787CE768387D473B7890EC00004BFFFE603FE0FFFFB5
+S3250001746063FFFFFF7FE65A787CC668387D46327890CC00004BFFFE447D6852787D08683863
+S325000174807D484278910C00004BFFFE302C08000D418200342C08000E4182001C2C08000FB5
+S325000174A0418200084BFFFE147D476B7890EC00004BFFFE087D6668387D46337890CC0000DE
+S325000174C04BFFFDF87D6750383FE0FFFF63FFFFFF7FE73A787CE768387D473A7890EC000070
+S325000174E04BFFFDD8930100D47C180000418200AC38E000207CF338507DC738307DE57B78CB
+S3250001750039EF000491E100C881C5000091C100BC926100D87DC89C307CEB437838E0000193
+S325000175207CF2A0309241001C2C1400014180FD4C7D635B7838A1002490A100089361000CE3
+S32500017540928100F0928100104BFFF9AD836100F8834100E8832100EC830100D482E100CC6F
+S3250001756082C100D082A100A4828100F0826100D88241001C822100A881E100C881C100BC2A
+S3250001758081A100C0818100C4390100243A080004816800004BFFFCE47DCB98307DE77B781E
+S325000175A039EF000491E100C881C7000091C100BC926100D87C1300004182FF6438E0002050
+S325000175C07CF338507DC73C307D6B3B784BFFFF5080C10120810101147C0640004082FBDC75
+S325000175E080A1011C80E101107C053800418000084BFFFBC84800600181A100F4836100F8AA
+S32500017600834100E8832100EC818100DC82E100CC82C100D04BFFFBA43821FFE87C691B7866
+S3250001762080E30018810300107D68385080E3001C810300147D48385080E3000480C30000B9
+S3250001764080C600207C0730004080003080E9000080E70020808900047CE43850810900109B
+S325000176607CC83A1490C9001080A900047D053A14910900047D6758508109000880E90000E6
+S3250001768080E700247C08380040800030810900008108002480A900087D0540508089001415
+S325000176A07CE4421490E9001480C900087C864214908900087D485050808900047C845A14D6
+S325000176C080C9000080C600287C0430004081002480E900047CEB3A1480A9000080A50028BF
+S325000176E07CE53850810900187CE7405090E9001880C900087CC65214810900008108002C3C
+S325000177007C06400040810024808900087C8A221480E9000080E7002C7C87205080A9001CBA
+S325000177207C8428509089001C8109001080E9000C80E700207C0838004080002C8109000CC1
+S325000177408108002080A900107D054050808900047CE4421490E9000480C900107C8642143D
+S3250001776090890010808900148109000C810800247C0440004080002C8089000C8084002490
+S3250001778080C900147C86205080A900087D0522149109000880E900147CA7221490A9001465
+S325000177A080A900188089000C808400287C0520004081001080E9000C80E7002890E9001839
+S325000177C08089001C8109000C8108002C7C0440004081001080C9000C80C6002C90C9001C65
+S325000177E0382100184E8000203821FFF0814100187C691B787C070378394AFFFF7C0A000084
+S32500017800418000207D244B7839290001888400007CE72214394AFFFF7C0A00004080FFE866
+S325000178207CE33B78382100104E80002081210008390227107C0940404081000C7C0918400D
+S325000178404180000C7C0303784E800020386000014E8000207FE802A697E1FFE080C3000438
+S3250001786038A0FFFF7C0628004182001480C300043CA000047C062840418000187C030378C6
+S3250001788083E100007FE803A6382100204E80002088C3000B2C0600FF4182FFE480E3000472
+S325000178A0810282F87D683A14906100249161001C916100084BFFFF79816100247C0300002D
+S325000178C0408200187C03037883E100007FE803A6382100204E80002088AB000854A5402E50
+S325000178E0890B00097CA5437854A5402E888B000A7CAA23783D0000FF6108FFFF7C0A40005A
+S32500017900408200087C0A03787C0A0000408000187C03037883E100007FE803A63821002050
+S325000179204E8000207C0A0000408100307D635B78914100188121001C7CAA4A1438A5FFFF21
+S3250001794090A100084BFFFEE981610024814100187C0300004182007C80CB000038A0FFFFF7
+S325000179607C062800418200288061001C91410018914100084BFFFE758141001880E100248E
+S3250001798080E700007C0338004082002C80A1002880E1001C90E500008081002C9144000097
+S325000179A03860000183E100007FE803A6382100204E80002038628CD04BFFBFA97C030378AA
+S325000179C083E100007FE803A6382100204E8000207C03037883E100007FE803A638210020DE
+S325000179E04E8000207FE802A697E1FFB8900282F890028300900282FC386100203881003879
+S32500017A009081000838C1003490C1000C4BFF8C157C0300004080002038628CE54BFFBF4519
+S32500017A207C03037883E100007FE803A6382100484E80002081010034910282FC80A1003822
+S32500017A4090A282F880E282F83CE7000490E28300900283049002830880A282F83FE0000387
+S32500017A6063FFFFF07D3F2A149121003C3869000C390282F09101000838A0000490A1000CB9
+S32500017A8048005A497C030000408200A080E1003C88E7000B2C070002408200248061003CC4
+S32500017AA038A1004090A1000838E1004490E1000C4BFFFDA57C030000408200108101003C98
+S32500017AC03928FFF04BFFFFA4808100442C04000B408000108101003C3928FFF04BFFFF8C2F
+S32500017AE080610040390280C09101000838A0000B90A1000C480059D57C0300004182001061
+S32500017B008101003C3928FFF04BFFFF6080E1004090E2830480810044908283088101003C6D
+S32500017B203928FFF04BFFFF4480A283047C050000408200B438628D504BFFBE2980628300B4
+S32500017B404800356D2C0306EB4082002038628D804BFFBE113860000183E100007FE803A665
+S32500017B60382100484E80002081028300890800005508402E80C2830088C600017D083378C9
+S32500017B805508801E80E2830088E7000254E7402E80828300888400037CE723787D083B781A
+S32500017BA02C0806EB4082002038628DAA4BFFBDB53860000183E100007FE803A6382100487C
+S32500017BC04E8000209002830038628DD64BFFBD957C03037883E100007FE803A638210048F3
+S32500017BE04E80002038628D6280E2830490E1000880A2830890A1000C81028304910100100F
+S32500017C004BFFBD614BFFFF38806283044E8000207FE802A697E1FFF080C283007C06000060
+S32500017C204182006880628300480034857C0300004082004480A2830088A5000054A5402EEE
+S32500017C4081028300890800017CA5437854A5801E80828300888400025484402E80C2830074
+S32500017C6088C600037C8433787CA523782C0506EB408200183860000183E100007FE803A63C
+S32500017C80382100104E8000207C03037883E100007FE803A6382100104E8000207FE802A6B2
+S32500017CA097E1FFB080E283007C070000408200183860FFFF83E100007FE803A638210050A1
+S32500017CC04E8000208142830088CA000054C6402E888A00017CC6237854C6801E88AA0002B3
+S32500017CE054A5402E88EA00037CA53B787CC62B782C0606EB408202387D49537888CA0014D2
+S32500017D0054C6402E888A00157CC6237854C6801E88EA001654E7402E890A00177CE743781F
+S32500017D207CC63B7874C6E0003D0020007C064000418201C488A9001454A5402E88C9001574
+S32500017D407CA5337854A5801E88E9001654E7402E888900177CE723787CA53B7890A1004C12
+S32500017D60398A00208161004C88E9000454E7402E88A900057CE72B7854E7801E88C9000661
+S32500017D8054C6402E91210040890900077CC643787CEA33787C0B6000418200507D635B780E
+S32500017DA0916100489181002091810008914100249141000C480051AD38628DF980C1004873
+S32500017DC090C10008808100209081000C80E1002490E100104BFFBB8D818100208161004821
+S32500017DE081410024812100407D8C521480A1004C3CE0FFC07C053840418000E439403000B6
+S32500017E0088A9000854A5402E890900097CA5437854A5801E8889000A5484402E88C9000BF0
+S32500017E207C8433787CA523787D43537891410048918100209181000890A1002490A1000C51
+S32500017E404800512138628E1980A1004890A10008810100209101000C80C1002490C1001078
+S32500017E604BFFBB0138628E398081004C908100084BFFBAF14BFF91CD4BFFBDED8101004CCF
+S32500017E807508E0003CA020007C0828004182002480E1004C7CE803A64E8000213860FFFFB0
+S32500017EA083E100007FE803A6382100504E80002080E1004C3FE0FFFF63FFFFFF7FE73839AF
+S32500017EC07CE803A64E8000213860FFFF83E100007FE803A6382100504E8000207C8A5A148A
+S32500017EE038840FFF3FE0FFFF63FFF0007FEA20394BFFFF1088AA001454A5402E88EA0015F3
+S32500017F007CA53B7854A5801E890A00165508402E88CA00177D0833787CA543783FE0FFFFE9
+S32500017F2063FFFFFF7FE528394BFFFE349141003480C1003490C100283D0A000C9101003090
+S32500017F4080C1003090C1002C386100283881004C90810008480032057C0300004080FF0888
+S32500017F603860FFFF83E100007FE803A6382100504E8000207FE802A697E1FF907C691B78CB
+S32500017F803861003491210008480054713861003438C1002490C10008390000049101000C28
+S32500017FA038C29AD590C1001048000D9D2C030002418201482C030003418200187C030378BA
+S32500017FC083E100007FE803A6382100704E8000208101007C9008000039429E0080AA000492
+S32500017FE07C050000418201007C0903782C090003408000E45524103A7C8452148084001C10
+S325000180007C040000418200C4806100249121001C5528103A9141006C7D0852148108001CEA
+S32500018020910100084800543D8141006C8121001C7C0300004082009480C10078810A0000C1
+S32500018040910600008101007C38A000017CA54830808800007C852B7890A800008061002825
+S3250001806038A1002090A100089001000C48004EF981010080906800007C030000408200144C
+S3250001808080A1002080E100287C053800418200288061008480A1002C90A100084800535D88
+S325000180A03860000183E100007FE803A6382100704E8000207C03037883E100007FE803A687
+S325000180C0382100704E800020392900012C0900034180FF24394A003080AA00047C05000001
+S325000180E04082FF087C03037883E100007FE803A6382100704E80002039029AD79101002C21
+S325000181004BFFFED07FE802A697E1FF30906100D47C0303784BFF8E39814100D8812100D4AA
+S3250001812071440004418200503C60001F6063FF6A38C29AD890C10008810900008108002489
+S325000181409101000C80A9000C90A100104BFFB7D1806100D48063000C810100DC9101000896
+S325000181604BFFA29183E100007FE803A6382100D04E800020714600084182002881090004B8
+S32500018180710800084182001C7C0303784BFFFA85814100D8812100D47C03000040820274EE
+S325000181A071460002418200FC8109000471080010418200348069000C38A29ADE90A10008B2
+S325000181C080E9000080E700187CE803A64E800021806100D43863000C900100084BFFAB6169
+S325000181E0812100D4808100DC7C040000418200A480C100DC88C600007CC607747C06000094
+S32500018200418200908069000C80A100DC90A1000881090000810800187D0803A64E80002191
+S32500018220812100D43C60001F6063FF6A38829AE39081000880C9000080C6002090C1000C7E
+S325000182408109000C91010010810100DC910100144BFFB6CD812100D48069000C80A9001466
+S3250001826090A1000880A9001090A1000C480017A5906100247C0303784BFF8CD58061002485
+S3250001828083E100007FE803A6382100D04E80002080E9000838E7000890E100DC4BFFFF68B6
+S325000182A0714700014182015880890000808400187C040000418200248069000C39029AECA0
+S325000182C09101000880A9000080A500187CA803A64E800021812100D480C9000470C60010D2
+S325000182E0418200103869000C900100084BFFAA5180C100DC7C06000041820084810100DCD5
+S32500018300890800007D0807747C080000418200703861002C810100DC91010008480050DDDC
+S325000183203C60001F6063FF6A38C29AF690C10008810100D4810800008108001C9101000C4A
+S3250001834080A100D480A5000C90A1001038A1002C90A100144BFFB5C9806100D43863000C41
+S325000183603901002C910100084BFFD56983E100007FE803A6382100D04E8000203861002C1E
+S32500018380808280A080840000908100084800506D3861002C38C0002F90C1000848004F3D79
+S325000183A07C03000041820050392300017D234B7838A29AF190A1000848005041806100D438
+S325000183C03863000C3881002C9081000838C100AC90C1000C4BFFD4417C0300004181FF440C
+S325000183E03860FFFF83E100007FE803A6382100D04E8000203921002C4BFFFFB43860FFFF3C
+S3250001840083E100007FE803A6382100D04E8000208069000C4BFFF889814100D8812100D4FA
+S325000184204BFFFD803821FFF08142801838C2E53838C608007C0A3040418000107C030378ED
+S32500018440382100104E8000203923002C80E900007C0700004182001880E900003927007C2A
+S3250001846080E900007C0700004082FFF038EA008090E2801891490000806900003821001080
+S325000184804E8000207FE802A697E1FFA89061005C39629E00810B00047C080000418200302C
+S325000184A080E1005C2C07FFFF41820038808B00008101005C7C08200041820028396B003080
+S325000184C0810B00047C0800004082FFD87C03037883E100007FE803A6382100584E800020DB
+S325000184E080AB000C7C0500004182FFD4808100607C0400004182005C814B002C7C0A0000A9
+S3250001850041820050810A0004812100607D2840394182003480A100642C05FFFF4182001410
+S3250001852080EA000C80C100647C063800408200187D43537883E100007FE803A638210058D5
+S325000185404E800020814A007C7C0A00004082FFB8808B0008708400804082002880AB00083C
+S3250001856060A5008090AB000891610054808B000C7C8803A64E80002181610054906B0028DA
+S325000185807C0D0378808B00287C0400004182FF30810B0028392000017D2968307D084839D9
+S325000185A04082000C39AD00014BFFFFDC3880000191A1004C7C8468307C8420F8810B00283F
+S325000185C07D042038908B00287D635B78916100544BFFFE5581A1004C816100547C6A1B78C5
+S325000185E07C0300004082000C39AD00014BFFFF9891A3000C80CB000890C30004810B0014D5
+S325000186009103001480AB001090A300109163000090610044808300047084000241820078CC
+S325000186207DA36B7838C29AFF90C10008812B00187D2803A64E80002181A1004C816100549F
+S3250001864081410044906A00087C0300004082001880EA00043FE0FFFF63FFFFFD7FE73839F2
+S3250001866090EA000480AA000470A50018408200287DA36B7838A29B0490A1000880EB0018F8
+S325000186807CE803A64E80002181A1004C816100548141004480CA000470C60001418200B82D
+S325000186A09001004838A0000190A1005080CA000080C600187C0600004182005C7DA36B7834
+S325000186C038A29B0990A10008810A0000810800187D0803A64E8000217C0300004182001839
+S325000186E080830000908100487C04000041820008900100508061004C38C29B0D90C10008C3
+S3250001870081010054810800187D0803A64E80002181410044386A000C8081004890810008A8
+S32500018720810100509101000C4BFFC96181A1004C81610054814100447C030000408000DC89
+S3250001874080EA00043FE0FFFF63FFFFEE7FE7383990EA000480AA000470A50010418200705D
+S3250001876080CA00043FE0FFFF63FFFFEF7FE6303990CA0004398282B8918280A0808C0000D7
+S325000187807C04000041820048386A000C80AC000090A1000838A1002090A1000C4BFFD0796B
+S325000187A081A1004C81610054814100447C0300004181005880C280A039860004918280A017
+S325000187C0808C00007C0400004082FFC080EA0004810100607D0738394182FDCC808100644F
+S325000187E02C04FFFF4182001080C100647C0668004082FDB47D43537883E100007FE803A670
+S32500018800382100584E800020808A000460840010908A00044BFFFFB838629B124BFFB1450A
+S3250001882081A1004C81610054814100444BFFFF287FE802A697E1FEF8480004C14BFF7AA127
+S32500018840480006654BFFB2394800254D4BFFAD1D4BFF7B314BFF8F614BFFA2E938629B23FE
+S325000188604BFFB101900280B44BFF791D38629B2C4BFFB0F14BFF7D657C6A1B7838629B32F7
+S32500018880914100E8914100084BFFB0D97C0B037839429E0080CA00047C060000418200209C
+S325000188A080AA00002C050002408203FC394A003080CA00047C0600004082FFE87C0B0000E0
+S325000188C0418203C480CB000470C60010418203B880A100E870A50002408200147C03037804
+S325000188E04BFFF3317C03000040820388900100F0900100F438629B4C4BFFA3B990610024F5
+S32500018900900100FC80A100247C0500004182005838E0000290E100F47C0A03782C0A000329
+S32500018920408002BC80610024914100F855451838388283487CA5221480A5000090A10008BF
+S3250001894048004B21814100F87C030000408202845547183838C283487CE7321480E7000416
+S3250001896090E100F4808100F07C0400004082001080C100F42C060002408201D4980100B0FF
+S325000189803860FFFF9001000838C0FFFF90C1000C4BFFFAF5900100FC39029E0091010020FD
+S325000189A081010020810800047C080000418200D480E100208147002C914101047C0A000094
+S325000189C0418200A4808100FC7C0400004082001838C0000190C100FC38629B884BFFAF8551
+S325000189E08141010480EA000470E700044182002438629B9680CA000080C6002490C1000821
+S32500018A00810A000C9101000C4BFFAF5981410104810A0004710800014182002438629B9D3F
+S32500018A2080EA000080E7001C90E10008808A000C9081000C4BFFAF2D81410104808A00049B
+S32500018A4070840018408200E4810A000471080002408200D8814A007C914101047C0A000015
+S32500018A604082FF6480A1002038A5003090A1002081010020810800047C0800004082FF3483
+S32500018A80808100FC7C0400004182000C38629BAB4BFFAED138629BAD38E1002890E100083E
+S32500018AA0388000509081000C38E100B090E100104BFFAD397C0300004180006838610028A7
+S32500018AC0388100EC9081000838C100FC90C1000C392101009121001038A1007890A10014CC
+S32500018AE04BFFF4957C03000041820038806100EC80A100FC90A10008810101009101000CDE
+S32500018B004BFFF9857C03000041820018810100FC9101000838C1007890C1000C4BFFF5E91E
+S32500018B20980100B04BFFFF7038629BA480EA000080E7002090E10008808A000C9081000CB6
+S32500018B404BFFAE21814101044BFFFF0C3900001F910100FC808100F42C04000140820018F3
+S32500018B6080C100FC3FE0FFFF63FFFFFB7FE6303990C100FC808100E8708400024182001863
+S32500018B8080C100FC3FE0FFFF63FFFFF77FE6303990C100FC3860FFFF80C100FC90C10008D5
+S32500018BA038A0FFFF90A1000C4BFFF8DD7C0300004182FDCC81030004812100FC7D2840382E
+S32500018BC0910100089001000C4BFFF53D4BFFFDB0394A00012C0A00034180FD4C8061002418
+S32500018BE038C100EC90C10008390100FC9101000C38A1010090A1001038E1007890E100142B
+S32500018C004BFFF3757C0300004082001838629B5580810024908100084BFFAD494BFFFD48AB
+S32500018C20806100EC808100FC90810008810101009101000C4BFFF8517C030000408200183D
+S32500018C4038629B6E81010024910100084BFFAD154BFFFD14810100FC9101000838E100781A
+S32500018C6090E1000C4BFFF4A1906100F04BFFFCF838629B404BFFACED7C0303784BFFF021C5
+S32500018C804BFFFC6C7C0303784BFFEF81906100187C03037880E1001890E100084BFFA0A1E7
+S32500018CA04BFFFC3091410020806A000038A0001090A1000838C0FFFF90C1000C4BFFF7C9DD
+S32500018CC0814100207C6B1B787C0300004182FBE0808B000470840010408200084BFFFBD022
+S32500018CE091610104386B000C900100084BFFA051816101044BFFFBC87FE802A697E1FFE891
+S32500018D008062801490010008388000249081000C4800401D80C2801439004E209106000487
+S32500018D204BFF74C180E28014906700103CA0FA2090A7002083E100007FE803A6382100187E
+S32500018D404E8000207FE802A697E1FFE87C691B78900100147C0900004182001488E90000C6
+S32500018D607CE707747C070000408200188061001483E100007FE803A6382100184E800020E9
+S32500018D8080C10014810100247C064000418000188061001483E100007FE803A6382100185C
+S32500018DA04E80002088A900007CA507747C05000041820028806100289121001C88C900005D
+S32500018DC07CC6077490C1000848003F418121001C7C030000408200C0890900007D0807745D
+S32500018DE07C080000408200188061001483E100007FE803A6382100184E8000208081001431
+S32500018E005484103A80E100207C843A149124000088C900007CC607747C060000418200282A
+S32500018E20806100289121001C88E900007CE7077490E1000848003ED58121001C7C030000F4
+S32500018E404182001480E1001438E7000190E100144BFFFF04888900007C8407742C04005CB5
+S32500018E604082001488A900017CA507742C05000A4182000C392900014BFFFF987D264B7893
+S32500018E803929000138E0002098E60000392900014BFFFF807D284B7839290001980800001B
+S32500018EA04BFFFF047FE802A697E1FFE03862813838C2C7D090C10008390010009101000CD4
+S32500018EC04800125538C2271074C6E0003CE020007C0638004182003038A227103862813844
+S32500018EE090A100083CE000407CE5385090E1000C4800124983E100007FE803A638210020E0
+S32500018F004E80002038A227103FE0FFFF63FFFFFF7FE528394BFFFFC87FE802A697E1FFD89A
+S32500018F209061002C810100307C0800004181000C38A0000490A100304BFF72699061002096
+S32500018F4038628138900100088081002C9081000C80E1003090E10010480013499061002409
+S32500018F60806100204BFF725980E100247C0700004082000C38629BC74BFFAA718061002498
+S32500018F809001000880A1002C90A1000C48003DA183E100007FE803A6382100284E8000209E
+S32500018FA07FE802A697E1FFE8388300033FE0FFFF63FFFFFC7FE42039386400089061001C97
+S32500018FC038800004908100084BFFFF5138A300043D00CAFE6108BEEF9103000038650004EC
+S32500018FE08121001C9125000083E100007FE803A6382100184E8000207FE802A697E1FFE0BD
+S325000190007C0300004182005038C3FFF890C1001880C600003CE0CAFE60E7BEEF7C06380084
+S325000190204182000C38629BCE4BFFA9C14BFF7175812100189061001C38628138912100089F
+S32500019040810900049101000C480010F18061001C4BFF716D83E100007FE803A63821002082
+S325000190604E8000204E8000207FE802A697E1FFF0808283809081000C390000FF9904001090
+S325000190804BFF70F98081000C88C4000060C6000198C400004BFF70E58081000C38C000177F
+S325000190A098C400144BFF70D583E100007FE803A6382100104E8000203821FFF080E2838032
+S325000190C09807001489070000710800FE99070000382100104E8000207FE802A697E1FFC092
+S325000190E038C2837090C100308106000C7C0800004182001483E100007FE803A63821004000
+S325000191004E800020386283C84BFFA8598141003038800001908A000C3CA0FA2060A50860F6
+S3250001912090A1003890AA00103CA0FA2060A53C8090AA00144BFFFF858121003080A9002C7B
+S325000191407C050000408200283860008438A0000290A100084BFFFDC5812100309069002C6B
+S3250001916080E9002C38E7008490E9002880E900307C0700004082001C3860008438E00002DF
+S3250001918090E100084BFFFD95812100309069003080E900187C0700004082004838600001D1
+S325000191A04BFFAB49812100309069001880C9003074C6E0003CE020007C06380041820248F1
+S325000191C080A9003080E9001890A7000480E90018B007000280C9001838A02000B0A600008A
+S325000191E080C9001C7C06000040820048386000024BFFAAF9812100309069001C80A9002CB4
+S3250001920074A5E0003CC020007C053000418201E48089002C80C9001C9086000480C9001CC0
+S32500019220B006000280A9001C38802800B085000080A280148145002080EA0AB860E70030D6
+S3250001924090EA0AB8A0EA0AC260E70030B0EA0AC280EA0ABC60E7003090EA0ABC81490014D3
+S3250001926080E9001874E7E0003CC020007C0730004182016C80A90018B0AA00008089001C6C
+S325000192807484E0003D0020007C0440004182013C80E9001CB0EA000238C0001898CA00043B
+S325000192A038800018988A000538E00084B0EA0006900A0008900A000CA08A0000B08A0010B8
+S325000192C0B00A0012900A0018A0AA0002B0AA0020900A001CB00A00224BFF6EA181E2801461
+S325000192E03DC0000161CE86A08181003838C0000198CC000C980C0000980C00047DCD7378F0
+S3250001930080EF00087CE773D6388000027CE723D63A07FFFD7C0B03787C0B80004080008087
+S32500019320394000037C0A000041800068808F000838AA00027C842E305567083C38E700067D
+S325000193407C843BD67D247051408000087D2900507C096800408000307D2D4B78996C00087E
+S3250001936088AC00003FE0FFFF63FFFFF97FE52839390000037D0A40505508083C7CA54378A6
+S3250001938098AC0000394AFFFF7C0A00004080FFA0396B00017C0B80004180FF883860002070
+S325000193A03CC0FFC260C6951890C10008390283709101000C480018BD83E100007FE803A660
+S325000193C0382100404E80002080E9001C3FE0FFFF63FFFFFF7FE738394BFFFEBC80A90018E1
+S325000193E03FE0FFFF63FFFFFF7FE528394BFFFE8C8089002C3FE0FFFF63FFFFFF7FE42039E2
+S325000194004BFFFE1480A900303FE0FFFF63FFFFFF7FE528394BFFFDB07FE802A697E1FFE0F1
+S3250001942080A1002890A10018812500109121001488E9001090E1001C4BFF6D418141001837
+S325000194408121001C80810014992400107126001441820034386283D6912100084BFFA50522
+S325000194608121001880C900207C060000418200089009002083E100007FE803A638210020CF
+S325000194804E8000207127000241820018808A00202C0400034082000C38A0000190AA002004
+S325000194A0712600014182FFD0810A00202C0800044082FFC438800001908A002083E10000BC
+S325000194C07FE803A6382100204E800020806300202C0300024080000C386000014E80002087
+S325000194E07C0303784E8000207FE802A697E1FFE89061001C80C1001C900600249001001446
+S325000195008061001C4BFFFFC97C0300004082007480A280B47C0500004182FFE880E100148A
+S3250001952038E7000190E100142C0700644081002C80C1001C39000001910600208081001C90
+S3250001954038C0000190C4002483E100007FE803A6382100184E800020386000014BFF952127
+S325000195607C03037880E1001C90E100084BFFFEAD8061001C4BFFFF597C0300004182FF948B
+S3250001958083E100007FE803A6382100184E8000207FE802A697E1FFD88181003439628370CF
+S325000195A02C0C0080408100183860FFFF83E100007FE803A6382100284E8000203940000120
+S325000195C080CB002C3FE0FFFF63FFFFFE7FE5183998A60000706500014182001480EB002C5A
+S325000195E07C66467098C70001394A000191610024806B002C914100207C63521480A1003033
+S3250001960090A100089181000C48003959812100243900000391090020808900183D000000F8
+S325000196206108B000B104000080A9002C74A5E0003CC020007C053000418200C88089002C7A
+S3250001964080C9001C9086000480C9001C80A10034808100207CA52214B0A600028109001C54
+S325000196603CE0000060E7B800B0E800004BFFF9FD810100248108001038E0008198E8000C8C
+S325000196804BFF6AF9806100244BFFFE614BFFFA2D81610024814B0020808B001CA0840000BA
+S325000196A0708880004082000C70850007418200183860FFFF83E100007FE803A638210028FB
+S325000196C04E8000202C0A0001418200183860FFFF83E100007FE803A6382100284E8000200A
+S325000196E0808B00247C0400004082FFC88061003483E100007FE803A6382100284E80002033
+S325000197008089002C3FE0FFFF63FFFFFF7FE420394BFFFF307FE802A697E1FFD081E1003C67
+S325000197203DC020007C6C1B78394283702C0F0080408100183860FFFF83E100007FE803A67E
+S32500019740382100304E800020808A002C6068000199040000706800014182002480AA0028DD
+S325000197603FE0FFFF63FFFFFE7FE4603998850000808A00287D8846709904000138A00004E6
+S3250001978090AA002080EA003074E7E0007C077000418201D080CA0030810A001890C8000493
+S325000197A0810A00183CE0000060E7B000B0E800007C0D0378816A001CB00B0008718500018F
+S325000197C04182002C810A00287508E0007C0870004182017C80EA002890EB00043880000284
+S325000197E0B08B0002396B000839A0040080AA002C74A5E0007C0570004182013C9141002C5E
+S32500019800808A002C908B000438CF0001B0CB000261A8A800B10B000091A100207C0D00001F
+S325000198204182001480AA001C3C80000060848000B08500004BFFF83580A1002C80A50010B6
+S32500019840388000819885000C4BFF69318061002C4BFFFC994BFFF86581C1003C8141002CBC
+S3250001986081AA0020816A001CA16B000080A100207C0500004182001080EA001CA0E70008D9
+S325000198807D6B3B78818A0018A18C00027C0C7000408100087DCC7378716800074082000C31
+S325000198A071658000418200183860FFFF83E100007FE803A6382100304E8000202C0D0001B5
+S325000198C0418200183860FFFF83E100007FE803A6382100304E800020808A0018A0840000DF
+S325000198E070848000418200183860FFFF83E100007FE803A6382100304E80002080EA002403
+S325000199007C0700004082FFA48061003880EA003090E10008918100189181000C4800364521
+S325000199208061001883E100007FE803A6382100304E8000209141002C808A002C3FE0FFFFEB
+S3250001994063FFFFFF7FE420394BFFFEBC80EA00283FE0FFFF63FFFFFF7FE738394BFFFE7C36
+S3250001996080CA00303FE0FFFF63FFFFFF7FE630394BFFFE287FE802A697E1FFD88161003437
+S325000199809061002C80A1003890A100247C0A03787C0A58004080004C392020007CEA585083
+S325000199A07C074800408000087D2A58508061002C80E100249141001C7CE7521490E10008FC
+S325000199C09121000C80C100307CC803A64E8000218141001C906100207C030000418100182D
+S325000199E07D43537883E100007FE803A6382100284E800020386285574BFF9F698161003414
+S32500019A0080E100208081001C7D443A144BFFFF847FE802A697E1FFD09061003490010008B1
+S32500019A2080A100387CA803A64E8000217C030000408000183860FFFF83E100007FE803A6A9
+S32500019A40382100304E80002038600020900100084BFFF4C97C6B1B783880002080610034C9
+S32500019A6080E1003C90E100089081002C9081000C91610020916100104BFFFEFD8141002034
+S32500019A8080C1002C7C03300041820020806282CC4BFF9ED13860FFFF83E100007FE803A6CD
+S32500019AA0382100304E800020888A00005484402E88EA00017C843B785484801E890A00029F
+S32500019AC05508402E88AA00037D082B787C8443782C0406EB4182004C3862855988CA0000A2
+S32500019AE054C6402E888A00017CC6237854C6801E88AA000254A5402E88EA00037CA53B784B
+S32500019B007CC62B7890C100084BFF9E593860FFFF83E100007FE803A6382100304E8000203E
+S32500019B2088AA001454A5402E890A00157CA5437854A5801E88CA001654C6402E88EA0017DD
+S32500019B407CC63B787CA5337874A5E0003CE020007C05380041820294888A00145484402E8A
+S32500019B6088AA00157C842B785484801E88CA001654C6402E890A00177CC643787C8433783C
+S32500019B809081002888CA000454C6402E888A00057CC6237854C6801E88AA000654A5402E5C
+S32500019BA088EA00077CA53B787CC62B783862858490C1002C90C100084BFF9DA9806100344E
+S32500019BC08081003C908100088101002C9101000C80A1002890A100104BFFFD9D8161002C60
+S32500019BE0814100207C03580041820020806282CC4BFF9D713860FFFF83E100007FE803A630
+S32500019C00382100304E800020808100287CCB221438C60FFF3FE0FFFF63FFF0007FEB3039D2
+S32500019C2088EA000854E7402E88AA00097CE72B7854E7801E88CA000A54C6402E890A000B5E
+S32500019C407CC643787CE733783862858790E1002C90E10008916100289161000C4BFF9D0532
+S32500019C60806100348101003C9101000880E1002C90E1000C80810028908100104BFFFCF9DD
+S32500019C808141002080E1002C7C03380041820020806282CC4BFF9CCD3860FFFF83E10000D7
+S32500019CA07FE803A6382100304E8000203862859388AA000C54A5402E890A000D7CA5437843
+S32500019CC054A5801E888A000E5484402E88CA000F7C8433787CA5237890A10008890A0014D8
+S32500019CE05508402E88CA00157D0833785508801E88EA001654E7402E888A00177CE72378A8
+S32500019D007D083B789101000C4BFF9C594BFF73354BFF9F554BFF648D8141002088EA001454
+S32500019D2054E7402E88AA00157CE72B7854E7801E890A00165508402E892A00177D084B78C7
+S32500019D407CE7437874E7E0003C8020007C0720004182004888CA001454C6402E88EA0015A4
+S32500019D607CC63B7854C6801E890A00165508402E88AA00177D082B787CC643787CC803A660
+S32500019D804E8000217C03037883E100007FE803A6382100304E80002088CA001454C6402EFA
+S32500019DA0890A00157CC6437854C6801E888A00165484402E88EA00177C843B787CC62378B8
+S32500019DC03FE0FFFF63FFFFFF7FE630397CC803A64E8000217C03037883E100007FE803A6E7
+S32500019DE0382100304E800020888A00145484402E88CA00157C8433785484801E88EA001669
+S32500019E0054E7402E88AA00177CE72B787C843B783FE0FFFF63FFFFFF7FE420394BFFFD64AC
+S32500019E207FE802A697E1FFE89061001C386300184BFFF17138A3001890A3000C8103000C7F
+S32500019E40910300049103000080C3000C80E1001C7CC63A1490C30008900300103D00CAFE70
+S32500019E606108BEE09103001483E100007FE803A6382100184E8000207FE802A697E1FFF0E3
+S32500019E807C691B787C0300004182004090610014810300143CE0CAFE60E7BEE07C0838009F
+S32500019EA041820010386281284BFF9B4181210014900900143CE0DEAD60E7BABE90E900100D
+S32500019EC07D234B784BFFF13583E100007FE803A6382100104E8000207FE802A697E1FFE86F
+S32500019EE0386000144BFFF0BD900300049003000080A1002490A30008808100289083000CC6
+S32500019F009003001083E100007FE803A6382100184E8000207FE802A697E1FFE89061001C49
+S32500019F204BFF62818141001C90610014812A0000912100107C0900004182003C80A90010E0
+S32500019F4090AA00009009001080E90004810900007CE83850810A00107CE7405090EA00101C
+S32500019F6080CA00107C0600004080000C3862812E4BFF9A79806100144BFF624580610010B5
+S32500019F8083E100007FE803A6382100184E8000207FE802A697E1FFE89061001C4BFF6205BB
+S32500019FA08141001C812100207C6B1B789009001080EA00007C0700004082005C912A000011
+S32500019FC0912A000480A9000480C900007CA6285080CA00107CA62A1490AA00107D635B78F4
+S32500019FE04BFF61DD8121001C80A900087C050000418200148069000C80E900087CE803A618
+S3250001A0004E80002183E100007FE803A6382100184E80002080AA0004912500104BFFFFA496
+S3250001A020806300104E8000207FE802A697E1FFE09061002438C0FFFF90C100144BFF616552
+S3250001A04081610014814100247C6C1B78916100147C0B000040800010812A00007C09000015
+S3250001A060408200207D8363784BFF61558061001483E100007FE803A6382100204E8000204C
+S3250001A08080E9000080C900047C073040408000208169000038EB000190E90000896B0000B5
+S3250001A0A0808A00103884FFFF908A001080E9000080C900047C0730404180FF9480E9001025
+S3250001A0C090EA0000900900104BFFFF847FE802A697E1FFE89061001C386000014BFFFD45E9
+S3250001A0E07C681B788083000438C4000190C3000480C1002098C400008061001C9101000833
+S3250001A1004BFFFE9183E100007FE803A6382100184E800020812100089123000480C1000CDD
+S3250001A12038A000087CC62BD654C618387CC64A1490C300084E8000207FE802A697E1FFD844
+S3250001A14081810034816100307C6E1B787C0C00004181001483E100007FE803A638210028E0
+S3250001A1604E8000208143000480AA00047C0558404181001080CA00007C0600004082011C5E
+S3250001A18080AE00047C0A28404081001C38CAFFFC80C6000080EAFFF87CC63A147C065800AD
+S3250001A1A0418200A47D0B6214812A00047C0848004082001080EA00007C07000040820060D7
+S3250001A1C0808E00087C0A20404180002C3862829880AE000090A100089161000C9181001054
+S3250001A1E04BFF978183E100007FE803A6382100284E80002081AA0004916A00047DAB6B78DA
+S3250001A20081AA0000918A0000394A00087DAC6B787C0D00004182FFD04BFFFFA880AA000420
+S3250001A2207D0C2850910A000480EA00007CA7621490AA000083E100007FE803A63821002845
+S3250001A2404E80002080CAFFF87C866214908AFFF87C8B621480AA00047C0428004082FF88A3
+S3250001A26080AA0000808AFFF87CA42A1490AAFFF880EA00007C0700004182FF6C394A000877
+S3250001A280388AFFFC80CA000490C40000810A0000910AFFF84BFFFFDC394A00084BFFFECC77
+S3250001A2A07FE802A697E1FFD881E1003081C1003481A100387C701B7881630004810B0000E4
+S3250001A2C07C08000041820028818B00047C0F00004182005C7C0C784040810028396B000879
+S3250001A2E0810B00007C0800004082FFE07C03037883E100007FE803A6382100284E800020C9
+S3250001A3007D0F7214812B00007D2C4A147C084840408100187C03037883E100007FE803A61E
+S3250001A320382100284E8000207DEC7B787C0D0000408100147C8D62143884FFFF7C846B96B3
+S3250001A3407D8469D680CB000080EB00047CC63A147CCC30507C067040408000084BFFFF80E1
+S3250001A36081AB00047CEC721490EB00047D0C72147D0D4050812B00007D084850910B0000AB
+S3250001A38080CB00007C06000040820028396B000838CBFFFC810B00049106000080AB000003
+S3250001A3A090ABFFF87C050000418200084BFFFFE07C0D6000418200207E03837891A100086D
+S3250001A3C0918100207CCD605090C1000C4BFFFD6D818100207D83637883E100007FE803A6C9
+S3250001A3E0382100284E8000207FE802A697E1FFB8388002809081003438A001E090A1003808
+S3250001A40038C0000390C1003C90010040386100344800042180E100407C07000040820014A8
+S3250001A42083E100007FE803A6382100484E800020388000039082825480A1004090A2824812
+S3250001A44038E000A090E28250900282589002825C3900028091028260388001E0908282645C
+S3250001A46038A2824838C2824880E600108106001490E5002091050024810600188086001C4C
+S3250001A480910500289085002C7C0303783880FFFF9081000838C0FFFF90C1000C3900FFFF63
+S3250001A4A09101001048000641386000FF900100089001000C900100104800062D3862824817
+S3250001A4C038A1000890A1002C80E1002C900700008081002C9004000438C2824890C1001029
+S3250001A4E0390100143882824880A4001080C4001490A8000090C8000480C4001880E4001C87
+S3250001A50090C8000890E8000C900100244BFFCAB538E0000890E280A8900280AC8102800C4B
+S3250001A52089080105910280B083E100007FE803A6382100484E8000207FE802A697E1FFB879
+S3250001A5407C691B7880C282487C0600004082001483E100007FE803A6382100484E80002015
+S3250001A5609061004C2C030008418202302C030009418201D02C03000A4182010480A282609A
+S3250001A580810280B07CA8285080C280A87C062800418000103860000A4BFFFFA18121004C06
+S3250001A5A07129007F408100C8A8E280087C093800408000BC5528183880C2800C7D28321421
+S3250001A5C03881003C38A280A880C5000080E5000490C4000090E400043862824838E100087E
+S3250001A5E090E100348081003488C900047CC607748101003C7CC8321490C4000080C1003457
+S3250001A600810100409106000480A2801090A10010390100149101003080A10030A8E90000F1
+S3250001A62090E50000808100309004000480C1003091210044A90900089106000880A10030C4
+S3250001A64088E2800A7CE7077490E5000C38E0000C90E100244BFFC96D808100448884000511
+S3250001A66080C1003C7C843214908280A883E100007FE803A6382100484E8000203900000892
+S3250001A680910280A88882800A7C840774810280AC7C882214908280AC88C2800A7CC6077431
+S3250001A6A080E282647CC63850810280AC7C0830004081000C80A2825C90A280AC386282488F
+S3250001A6C038E1000890E10034808100349004000080C10034810280AC9106000438A2824881
+S3250001A6E090A1001038E1001490E10030808100309004000080C10030810280AC91060004C4
+S3250001A70080A1003080E2826090E500088081003088C2800A7CC6077454C6083C80E280AC72
+S3250001A7207CC63A1490C4000C900100244BFFC89583E100007FE803A6382100484E800020C3
+S3250001A740808280A83884FFF880A280B07C842BD67084000738C000087C84305080E280B0B5
+S3250001A7607C8439D680A280A87C852214908280A880E280A8808282607C0720004180FEF048
+S3250001A7803860000A4BFFFDB583E100007FE803A6382100484E80002080C280B038C6000899
+S3250001A7A0810280A87C0830004180FEC480A280B080E280A87CA5385090A280A83860002019
+S3250001A7C04BFFFD7980A280B080E280A87CA5385090A280A883E100007FE803A638210048BE
+S3250001A7E04E8000207FE802A697E1FFF07C691B788101001838C8FFFF90C100187C080000F1
+S3250001A8004081002038E9000190E10014886900007C6307744BFFFD25812100144BFFFFD41F
+S3250001A82083E100007FE803A6382100104E8000207FE802A697E1FFD87C6A1B7880E2801479
+S3250001A84080E7002090E10024388001E09083000438E0028090E3000038A0000390A3000802
+S3250001A860806300009141002C80EA00047C6339D638A0001090A100084BFFE6A13DA0017D47
+S3250001A88061AD78408181002C81610024906C000C80AC000080CC00047CA531D690AC0010BF
+S3250001A8A0808B00303FE0FFFF63FFFFBF7FE42039908B0030810C000C7508E0003D2020009F
+S3250001A8C07C0848004182020C80EC000C90EB085080EC0000810C00047CE741D654E7183887
+S3250001A8E038E7007F390000807CE743D654E7881C60E70D6690EB084038A000037C050000C6
+S3250001A900418200142C050002418201A02C050003418201687C0A03782C0A00104080001C3F
+S3250001A9205546083C7CC65A14B1460E00394A00012C0A00104180FFEC808C0004548458289E
+S3250001A9403FE0100063FF00217FE42378908B084880CC000054C6502A3FE0010063FF00E48F
+S3250001A9607FE6337890CB084480C28014818600087D4C6BD67CCC53D67C0668004081000806
+S3250001A980394A00019141001C2C0A00104180000C38E0001090E1001CB00B097638E01FFF06
+S3250001A9A0B0EB0972A0AB097270A5FDFFB0AB097238A01FFFB0AB0970810B0ABC61085001F7
+S3250001A9C0910B0ABC810B0AB861085001910B0AB8A10B0AC27108AFFEB10B0AC24BFF579D44
+S3250001A9E080C100243D0055CC6108AA33910603804BFF578981410024808A02803FE0FFFF74
+S3250001AA0063FFFFE07FE4203938A281108101001C7CA5421488A500007C842B78908A028046
+S3250001AA204BFF575980A100243CE0AA3360E755CC90E503804BFF57458102825091028250D7
+S3250001AA4080A1002438E0000798E508584BFF572D8101002480A8084060A5000190A8084044
+S3250001AA60386000014BFF5D3183E100007FE803A6382100284E8000207C0A03782C0A010043
+S3250001AA804080FEB85544402E554520367C842B787C8453785545083C7CA55A14B0850E0024
+S3250001AAA0394A00014BFFFFD87C0A03782C0A00104080FE885546402E554720367CC63B786D
+S3250001AAC07CC65378B0CB0E00394A00014BFFFFE080EC000C3FE0FFFF63FFFFFF7FE7383960
+S3250001AAE04BFFFDEC80810008548B273E80E1000C54EA273E80A1001054A9273E3FE0FFFF0F
+S3250001AB0063FFFFFF7FE81A78710800FF5508083C80C2801480C600207D0832145565402E8D
+S3250001AB20554620367CA533787CA54B78B0A80E00386000014E8000203821FFE83D4055CCA2
+S3250001AB40614AAA333D20FA209149030038C000C3B0C902009149032038E000C1B0E902204B
+S3250001AB603CA0AA3360A555CC90A903209149034038C00082B0C90240382100184E800020E2
+S3250001AB807FE802A697E1FFE081410028906100247145000341820010386287E44BFF8E4D93
+S3250001ABA081410028812100243CC07C1060C643A690C900003C807C08608402A69089000405
+S3250001ABC03CE07C1260E743A690E900087544E0003CC020007C043000418200887D485378D3
+S3250001ABE07D0A437855053E7F4182000C2C05007F408200303FE003FF63FFFFFC7FE5503919
+S3250001AC003FE0480063FF00037FE52B7890A9000C83E100007FE803A6382100204E8000203A
+S3250001AC205544843E64843C009089000C7144FFFF64846000908900103C807C08608403A678
+S3250001AC40908900143CE04E8060E7002190E9001883E100007FE803A6382100204E80002002
+S3250001AC603FE0FFFF63FFFFFF7FE850394BFFFF747FE802A697E1FFE89061001C8082E53011
+S3250001AC802C0400304180000C386287EC4BFF8D5D80C1001C7D4602148082E53039040001B4
+S3250001ACA09102E5305484203638A2E03038A502007D642A145547103A38C2E0307CE7321436
+S3250001ACC080E7000090EB000881010020910B000080E1002490EB00045544103A38E2E03034
+S3250001ACE07C843A149164000080A28014816500202C0A00104180003038EAFFF070E7001F90
+S3250001AD0038C000017CC7383080CB09487CC73B7890EB094883E100007FE803A63821001845
+S3250001AD204E8000202C0A000841800038714800075508083C38E0001F7D08385039200001E8
+S3250001AD407D284030808B00147C884378910B001483E100007FE803A6382100184E80002076
+S3250001AD605544083C608400013900001F7C84405038A000017CA4203080AB00147CA42378DF
+S3250001AD80908B001483E100007FE803A6382100184E8000207FE802A697E1FFE03D4055CCA6
+S3250001ADA0614AAA33810280148128002080A900043FE0FFFF63FFFFF37FE5283990A9000484
+S3250001ADC09009001438E0FFFF90E900189009094038E0FFFF90E90944900909483CE000E173
+S3250001ADE060E79F0090E9094080A9094060A5008090A909409149030039000001B109020058
+S3250001AE0080C9001464C6004090C900149149030C4BFF53693860FFFF4BFF53E17C090378F4
+S3250001AE202C0930004080002C7D234B789121001C3D00FFC2610803A8910100084BFFFD4551
+S3250001AE4080E1001C392701002C0930004180FFDC83E100007FE803A6382100204E80002031
+S3250001AE607FE802A697E1FFD87C6A1B787C0903782C09002840800064386288065526103A86
+S3250001AE8038A284307CC62A1480C6000090C10008810A00009101000C912100245528103A38
+S3250001AEA038E284307D083A1481080004910100109141002080AA000490A100144BFF8AA5DD
+S3250001AEC0810100243928000280C10020394600082C0900284180FFA483E100007FE803A645
+S3250001AEE0382100284E8000207FE802A697E1FFE87C6A1B789061001C80C3000054C9C23E88
+S3250001AF002C090005418200802C0900094182005C38C0000190C280B47C0900004180003C4F
+S3250001AF202C09001540800034386288345524103A390295A07C8442148084000090810008D0
+S3250001AF404BFF8A218061001C4BFFFF19386288424BFF8A11480000003862881B912100080E
+S3250001AF604BFF8A014BFFFFE07D435378900100084BFF7B8983E100007FE803A63821001875
+S3250001AF804E8000204800001583E100007FE803A6382100184E8000207FE802A697E1FFD82E
+S3250001AFA09061002C810280148168002091610018890B001C550CF0BE7D8A0E7091810024C9
+S3250001AFC071880001418200E02C0A00044082002438800001B08B09304BFF51A1818100241E
+S3250001AFE081610018A0AB093054A5AAFE39450010914100205545103A3882E0307CA52214A6
+S3250001B000814500007C0A00004082000848000000808A000C7C0400004182002038A0001F5B
+S3250001B0207CAC285038C000017CC5283080CB00107CC52B7890AB00108061002C80EA0004D2
+S3250001B04090E100089141001C80EA00007CE803A64E80002180C1001C814600087C0A00006A
+S3250001B060418200084BFFFFD480C100202C060010418000248101001880A1002038A5FFF0B2
+S3250001B080392000017D2528308088094C7C852B7890A8094C83E100007FE803A638210028D2
+S3250001B0A04E800020394A00084BFFFF487C691B7888A3000054A5402E888300017CA52378AD
+S3250001B0C054A5402E88C300027CA5337854A5402E88C300037CA533783CE0FEEF60E70F1EEB
+S3250001B0E07C05380040820030886300205463402E88C900217C6333785463402E88E90022BA
+S3250001B1007C633B785463402E890900237C6343784E8000207C0303784E8000207FE802A63D
+S3250001B12097E1FFE8386001F43CA0FFC260A5B21C90A100089001000C4BFF65959062822CF2
+S3250001B14038628BE04BFF881D83E100007FE803A6382100184E8000207FE802A697E1FFB8E3
+S3250001B160818100507C6B1B788083000480A300007C8520502C040040408000183860FFFF83
+S3250001B18083E100007FE803A6382100484E80002081430000890A00005508402E88EA000110
+S3250001B1A07D083B785508402E892A00027D084B785508402E888A00037D0823783CA0FEEFBF
+S3250001B1C060A50F1E7C082800418200183860FFFF83E100007FE803A6382100484E80002016
+S3250001B1E0900282EC88CA001054C6402E88AA00117CC62B7854C6402E88EA00127CC63B7835
+S3250001B20054C6402E88EA00137CCE3B78888A00145484402E890A00157C8443785484402E08
+S3250001B22088AA00167C842B785484402E88AA00177C842B789081003888EA001854E7402E6E
+S3250001B24088CA00197CE7337854E7402E890A001A7CE7437854E7402E890A001B7CE7437885
+S3250001B26090E1003488AA000C54A5402E888A000D7CA5237854A5402E88CA000E7CA533780F
+S3250001B28054A5402E88CA000F7CA5337890A10030890A00045508402E88EA00057D083B78A1
+S3250001B2A05508402E892A00067D084B785508402E888A00077D0D237888CA000854C6402ED0
+S3250001B2C088AA00097CC62B7854C6402E88EA000A7CC63B7854C6402E88EA000B7CC63B7854
+S3250001B2E090C1002880C3000038C6002090C3000081430000888A00005484402E890A00016A
+S3250001B3007C8443785484402E88AA00027C842B785484402E88AA00037C842B782C0406EB0F
+S3250001B3204182002038628BE24BFF86393860FFFF83E100007FE803A6382100484E8000207A
+S3250001B34088CA001454C6402E88AA00157CC62B7854C6402E88EA00167CC63B7854C6402E3F
+S3250001B36088EA00177CC63B7890CC000081030000390800209103000080EC000074E7E000CC
+S3250001B3803D0020007C0740004182015480CC00007CC3337890C100449161004C916100086B
+S3250001B3A091A1000C91C1001038A28BFF90A1001448000141816100287C6A1B787C030000B1
+S3250001B3C04082002038628C044BFF85993860FFFF83E100007FE803A6382100484E80002059
+S3250001B3E07C0B00004182005C38EA0FFF3FE0FFFF63FFF0007FE3383980E1004C90E1000808
+S3250001B4009161000C810100389101001038C28C1790C10014480000DD7C6A1B787C030000AC
+S3250001B4204082002038628C1C4BFF85393860FFFF83E100007FE803A6382100484E80002040
+S3250001B44080C100307C060000418200407D435378914100408081004C8084000090810008E8
+S3250001B460808100309081000C48001AF981610030808100407D445A1480E1004C80C70000A6
+S3250001B4807C865A1490870000808282EC80C100347C0430004182003038628C2F810282EC50
+S3250001B4A09101000880C1003490C1000C4BFF84B53860FFFF83E100007FE803A638210048EB
+S3250001B4C04E80002080C100447C66505083E100007FE803A6382100484E80002080CC000021
+S3250001B4E03FE0FFFF63FFFFFF7FE630394BFFFEA47FE802A697E1FFE07C6A1B7838628C67A2
+S3250001B5008081003490810008914100249141000C80A1002C90A100104BFF84498101002CAF
+S3250001B5207C080000408200188061002483E100007FE803A6382100204E80002080610028BD
+S3250001B5403902EE00910100089001000C480003257C030000408000187C03037883E100005F
+S3250001B5607FE803A6382100204E8000208061002838E20A0890E10008388000089081000CC2
+S3250001B580480002F1814100287C030000408000187C03037883E100007FE803A63821002041
+S3250001B5A04E80002080CA000480EA00007CC7305080A1002C7C0628004080002038628C7F9F
+S3250001B5C04BFF83A17C03037883E100007FE803A6382100204E800020386001F43D00FFC296
+S3250001B5E06108B21C910100089001000C4BFF60E1814100289062822C80610024810A000031
+S3250001B60091010008810A00008121002C7D084A149101000C38E2EE0090E1001039020A08D9
+S3250001B6209101001480A1003090A100184800006D906100248062822C4BFF61759002822C09
+S3250001B64038628C8E4BFF831D814100247C0A00004082002038628C904BFF83097C03037871
+S3250001B66083E100007FE803A6382100204E80002080E100288081002C80C700007C862214B3
+S3250001B680908700007D43537883E100007FE803A6382100204E8000203821FFE8820100243F
+S3250001B6A081610030812100207C6C1B78556536BE2C050013418201942C05001F4182018C4A
+S3250001B6C02C05003B418201842C05003F4182017C81A282EC7C0980404080016088890000F7
+S3250001B6E07C8F2378392900017C0E03787C8A26707C0A00004082002C7D6A5B78914C00008E
+S3250001B700398C00047DAD521471EA000F39CE00012C0E00014082FFC07C0A00004182FFDC77
+S3250001B7202C0A00014082009488E9000354E7402E88C900027CE7337854E7402E89090001BB
+S3250001B7407CE7437854E7402E890900007CEA4378392900047C098040408100107C030378F2
+S3250001B760382100184E800020554736BE2C070013418200202C07001F418200182C07003B0F
+S3250001B780418200102C07003F418200084BFFFF703FE0FC0063FFF8017FE8503955478BFE4E
+S3250001B7A070E707FE7D083B78714407FE548478207D0A23784BFFFF48280A000841810044D1
+S3250001B7C038AAFFFE54A5402E890900007CA5437854A5103A80C1002C7CA5321480A500046E
+S3250001B7E0890900017CAA4378392900027C0980404081FF787C030378382100184E80002094
+S3250001B800390AFFF75508402E88C900007D0833785508103A80E100287D083A1481480004CC
+S3250001B820392900017C0980404081FF407C030378382100184E80002091A282EC7D83637884
+S3250001B840382100184E8000203FE0FC0063FFF8017FE6583955658BFE70A507FE7CC62B78D4
+S3250001B860716707FE54E778207CCB3B784BFFFE647FE802A697E1FFD08083000480A30000EB
+S3250001B8807C8520502C040002408000183860FFFF83E100007FE803A6382100304E800020A5
+S3250001B8A081030000890800005508402E9061003480E3000088E700017D083B7838628CB398
+S3250001B8C09101001C910100084BFF809981C100388161003480CB000038C6000290CB000080
+S3250001B8E08081001C39A4FFFE7C0D00004081002C810B0004812B00007D0940507C08680096
+S3250001B900408000183860FFFF83E100007FE803A6382100304E800020900E0000814B00005D
+S3250001B92080CB00007D866A14810B00007CC86A1490CB00007C0D03787C0A60404080009011
+S3250001B9407C0B03787C0A6040418000183860FFFF83E100007FE803A6382100304E8000205E
+S3250001B9605568383088CA000070C6007F7D0B33787D455378394A000188A5000070A500808E
+S3250001B980418200207C0A60404180FFD83860FFFF83E100007FE803A6382100304E8000207E
+S3250001B9A07DAD5A14808E000038A4000190AE00005484103A7C84721480E1003C7DA53830F0
+S3250001B9C090A400047C0A60404180FF787C03037883E100007FE803A6382100304E800020E5
+S3250001B9E03821FFF07C6B1B7880A1001C810100247D45421480C10018808100207D26221430
+S3250001BA008081001C7C0A20404080000839290001914B0004912B0000382100104E800020FE
+S3250001BA203821FFF07C6B1B788101001C80C100247D4640508081001880E100207D27205039
+S3250001BA4080E1001C7C0A3840408100083929FFFF914B0004912B0000382100104E80002048
+S3250001BA603821FFD8CA2100307C6E1B78DA210020810100203FE0000F63FFFFFF7FE84039CC
+S3250001BA80650C001081A10024810100205508653E710807FF38C004337D2830507C0B037861
+S3250001BAA07C0A03787C090000418000B02C090020408000847C090000408200607DAB6B783D
+S3250001BAC07D8A637880A1002074A580007C050000418200207C0B0000418200287C040378D2
+S3250001BAE07D6B20503FE0FFFF63FFFFFF7FEA5278914E0000916E0004382100284E800020E6
+S3250001BB007C0703787D4A3850914E0000916E0004382100284E8000207DA54C3038C00020CA
+S3250001BB207CC930507D8630307CAB33787D8A4C304BFFFF942C0900204082000C7D8B63789E
+S3250001BB404BFFFF842C0900404080FF7C38C9FFE07D8B34304BFFFF707C0703787D29385030
+S3250001BB602C09000A418100207DAB48307D85483038C000207CC930507DA634307CAA33784E
+S3250001BB804BFFFF44FC80881ED8828020814280244BFFFF347FE802A697E1FFE8C061002062
+S3250001BBA0D86100084BFFFEBD83E100007FE803A6382100184E8000203D20433080E1000430
+S3250001BBC074E780007C0700004182009C808100087C0400004182007C7C0603788101000852
+S3250001BBE07CC8305090C1000880C100043FE0FFFF63FFFFFF7FE6327890C1000480E1000496
+S3250001BC00912280206CE6800090C28024C8228020FC21D828C8828428FC01013280A100080C
+S3250001BC20912280206CA4800090828024C8628020FC63D828FC03E0004080000CC8428428DA
+S3250001BC40FC63102AFC00182AFC1C00284E8000207C04037880C100047C8620509081000411
+S3250001BC604BFFFF9C80A10004912280206CA4800090828024C8628020FC63D828C84284283B
+S3250001BC80FC0300B281010008912280206D07800090E28024C8228020FC21D828FC01E00081
+S3250001BCA04080000CC8428428FC21102AFC00082A4E8000207FE802A697E1FFF038C1000415
+S3250001BCC038E10014810700008087000491060000908600044BFFFEE5FC00001883E1000047
+S3250001BCE07FE803A6382100104E8000203821FFD83DA080008241003C822100408161002C59
+S3250001BD008181003081210034814100387C0A00004082000C7C090000418200F47C0B68405B
+S3250001BD20418000E07C0F03787C0E03787C096840418000147C096800408200307C0A784031
+S3250001BD40408000285525083C55480FFE7CA94378554A083C39CE00017C0968404180FFE8EC
+S3250001BD607C0968004182FFD87C0F03787C0D03787C0E00004180006455E4083C55A70FFEF6
+S3250001BD807C8F3B7855AD083C7C0B4840418100147C0B4800408200287C0C50404180002001
+S3250001BDA07D9063787D8A60507C0C804040810008396BFFFF7D69585061AD00015547F87E7B
+S3250001BDC05525F8007CEA2B785529F87E39CEFFFF7C0E00004080FFA47C1200004182000C9E
+S3250001BDE091B2000491F200007C1100004182000C9191000491710000382100284E8000207F
+S3250001BE007D6D5B787D8F63784BFFFF207D8C53964BFFFF0C7FE802A697E1FFE07C691B78E9
+S3250001BE2080E100287C07000040820010808100307C0400004182005438E1000439010028D6
+S3250001BE408088000080A800049087000090A7000438A1000C38C1003080E600008106000456
+S3250001BE6090E5000091050004912100147C06037890C100184BFFFE7983E100007FE803A64B
+S3250001BE80382100204E8000207C0803789109000080C1002C80A100347CC62B9690C9000479
+S3250001BEA083E100007FE803A6382100204E8000207FE802A697E1FFE07C691B788101002818
+S3250001BEC07C0800004082001080A100307C05000041820054390100043881002880A40000D9
+S3250001BEE080C4000490A8000090C8000438C1000C38E10030810700008087000491060000E7
+S3250001BF00908600047C04037890810014912100184BFFFDDD83E100007FE803A63821002005
+S3250001BF204E8000207C0403789089000080E1002C80C100347FE733967FFF31D67CFF38503F
+S3250001BF4090E9000483E100007FE803A6382100204E800020808300047C0400004082001821
+S3250001BF6080A300007C0603787CA5305090A300004E800020808300047C0503787C8428505D
+S3250001BF8090830004810300003FE0FFFF63FFFFFF7FE84278910300004E8000207FE802A6D0
+S3250001BFA097E1FFD07C691B78906100348081003C7C84FE7080C100387C06200040820018F6
+S3250001BFC0810100447D08FE7080A100407C054000418200AC80E1003854E70FFE90E1002C92
+S3250001BFE07C07000041820010386100384BFFFF698121003480E1004054E70FFE90E1002809
+S3250001C0007C07000041820010386100404BFFFF498121003438E100043901003880880000EB
+S3250001C02080A800049087000090A7000438A1000C38C1004080E600008106000490E50000F7
+S3250001C04091050004912100147C06037890C100184BFFFC9D80E1002C808100287C072000D7
+S3250001C0604182000C806100344BFFFEED83E100007FE803A6382100304E8000208081003C78
+S3250001C080810100447C8443D69089000480E900047CE7FE7090E9000083E100007FE803A672
+S3250001C0A0382100304E8000207FE802A697E1FFD07C6A1B78906100348101003C7D08FE705D
+S3250001C0C080A100387C0540004082001880E100447CE7FE70808100407C043800418200A48F
+S3250001C0E080C1003854C60FFE90C1002C7C06000041820010386100384BFFFE5D814100345B
+S3250001C10080C1004054C60FFE7C06000041820010386100404BFFFE418141003438C10004C6
+S3250001C12038E10038810700008087000491060000908600043881000C38A1004080C5000040
+S3250001C14080E5000490C4000090E400047C07037890E10014914100184BFFFB9580C1002CEF
+S3250001C1607C0600004182000C806100344BFFFDE983E100007FE803A6382100304E80002037
+S3250001C1808101003C80E100447FE83BD67FFF39D67D1F4050910A000480CA00047CC6FE7067
+S3250001C1A090CA000083E100007FE803A6382100304E8000203821FFF081610020814100180F
+S3250001C1C02C0B0020418000387D46FE7090C300002C0B0040418000147D48FE70910300046D
+S3250001C1E0382100104E800020390BFFE07D48463091030004382100104E8000207C0B00000D
+S3250001C200418100189143000080E1001C90E30004382100104E8000207D465E3090C300007A
+S3250001C22038E000207CEB38507D47383080A1001C7CA55C307CE72B7890E3000438210010D4
+S3250001C2404E8000203821FFF081410020816100182C0A0020418000387C05037890A3000047
+S3250001C2602C0A0040418000147C05037890A30004382100104E800020390AFFE07D68443067
+S3250001C28091030004382100104E8000207C0A0000418100189163000080E1001C90E3000460
+S3250001C2A0382100104E8000207D66543090C3000038E000207CEA38507D67383080A1001CB7
+S3250001C2C07CA554307CE72B7890E30004382100104E8000203821FFF0814100208161001CB6
+S3250001C2E02C0A0020418000387C05037890A300042C0A0040418000147C05037890A300003B
+S3250001C300382100104E800020390AFFE07D68403091030000382100104E8000207C0A0000D7
+S3250001C320418100189163000480E1001890E30000382100104E8000207D66503090C3000427
+S3250001C34038E000207CEA38507D673C3080A100187CA550307CE72B7890E3000038210010A4
+S3250001C3604E80002080C1000880A100107CC6283890C300008081000C810100147C8440383E
+S3250001C380908300044E8000208101000880E100107D083B789103000080C1000C80A1001448
+S3250001C3A07CC62B7890C300044E80002080A10008812100107CA54A7890A300008101000CCD
+S3250001C3C080E100147D083A78910300044E8000208121000880E9000090E300008089000491
+S3250001C3E09083000480C9000438C6000190C90004808900047C0400004082001080A90000EE
+S3250001C40038A5000190A900004E8000208121000880C9000090C300008109000491030004A4
+S3250001C420810900047C08000040820010808900003884FFFF90890000808900043884FFFF6E
+S3250001C440908900044E800020812100087C6A1B7880A9000438A5000190A9000481090004D1
+S3250001C4607C0800004082001080890000388400019089000080890000908A000080C9000410
+S3250001C48090CA00044E800020812100087C6A1B7880E900047C0700004082001081090000DA
+S3250001C4A03908FFFF91090000810900043908FFFF9109000481090000910A000080A90004DF
+S3250001C4C090AA00044E8000207FE802A697E1FFD0814100388121003C90610034390100207C
+S3250001C4E08083000080A300049088000090A8000480A100402C0500064181029C4182023CBE
+S3250001C5002C05000341810164418201002C050001418200942C05000241820030480010D915
+S3250001C5208101003438A1002080C5000080E5000490C8000090E8000483E100007FE803A64F
+S3250001C540382100304E80002088AA000090A1002C7C07037890E1002838610020390100083C
+S3250001C5603881002880A4000080C4000490A8000090C8000438C1001038E1004481070000E5
+S3250001C5808087000491060000908600047D2803A64E80002180C100388101002499060000DD
+S3250001C5A04BFFFF8088AA00007CA5077490A1002C80E1002C54E70FFE90E100283861002059
+S3250001C5C038A1000838C1002880E600008106000490E500009105000439010010388100440B
+S3250001C5E080A4000080C4000490A8000090C800047D2803A64E8000218101003880A10024F8
+S3250001C60098A800004BFFFF1CA90A00009101002C80A1002C54A50FFE90A100283861002098
+S3250001C620390100083881002880A4000080C4000490A8000090C8000438C1001038E100446A
+S3250001C640810700008087000491060000908600047D2803A64E80002180C100388101002433
+S3250001C660B10600004BFFFEBC2C050004418200702C050005418200084BFFFEA4808A000099
+S3250001C6809081002C8081002C54840FFE908100283861002038E100083901002880880000C7
+S3250001C6A080A800049087000090A7000438A1001038C1004480E600008106000490E5000069
+S3250001C6C0910500047D2803A64E80002180A1003880E1002490E500004BFFFE48A10A0000EE
+S3250001C6E09101002C7C05037890A100283861002038C1000838E10028810700008087000492
+S3250001C70091060000908600043881001038A1004480C5000080E5000490C4000090E4000401
+S3250001C7207D2803A64E8000218081003880C10024B0C400004BFFFDEC808A00009081002C29
+S3250001C7407C040378908100283861002038A1000838C1002880E600008106000490E500007D
+S3250001C76091050004390100103881004480A4000080C4000490A8000090C800047D2803A683
+S3250001C7804E8000218101003880A1002490A800004BFFFD902C050007418200DC2C05000885
+S3250001C7A0418200D42C0500094182006C2C05000A418200084BFFFD6880CA000090C1002CF6
+S3250001C7C07C06037890C100283861002038E10008390100288088000080A800049087000055
+S3250001C7E090A7000438A1001038C1004480E600008106000490E50000910500047D2803A683
+S3250001C8004E80002180A1003880E1002490E500004BFFFD1080CA000090C1002C80C1002C44
+S3250001C82054C60FFE90C10028386100203881000838A1002880C5000080E5000490C40000D4
+S3250001C84090E4000438E10010390100448088000080A800049087000090A700047D2803A6DE
+S3250001C8604E80002180E1003880810024908700004BFFFCB0386100203881000880AA000053
+S3250001C88080CA000490A4000090C4000438C1001038E1004481070000808700049106000027
+S3250001C8A0908600047D2803A64E80002180C10038390100208088000080A8000490860000FD
+S3250001C8C090A600044BFFFC5C3821FFF081010018910300047C06037890C300003821001042
+S3250001C8E04E8000203821FFF081010018910300047D06FE7090C30000382100104E8000202E
+S3250001C9003821FFF080A1001890A300047C08037891030000382100104E8000203821FFF026
+S3250001C92080A1001890A300047CA8FE7091030000382100104E8000203821FFF080E1001842
+S3250001C94090E300047C05037890A30000382100104E8000203821FFF080E1001854E7801E39
+S3250001C9607CE88670910300047D06FE7090C30000382100104E8000203821FFF080A10018A2
+S3250001C98070A5FFFF90A300047C04037890830000382100104E8000203821FFF080C1001840
+S3250001C9A054C6C00E7CC7C67090E300047CE5FE7090A30000382100104E8000203821FFF0F7
+S3250001C9C080810018708400FF908300047C08037891030000382100104E8000203821FFF0FB
+S3250001C9E080810018708400FF5483C00E7C63C670382100104E80002080610008706300FF58
+S3250001CA004E8000203821FFF0808100187084FFFF5483801E7C638670382100104E8000202D
+S3250001CA20806100087063FFFF4E800020806100084E800020806100084E80002080610008B0
+S3250001CA404E800020806100084E800020810100087C0800004082001880A100047C0500007C
+S3250001CA604082000C7C0303784E800020386000014E80002080E10008808100107C07200055
+S3250001CA804082001480C100048101000C7C0640004182000C7C0303784E80002038600001D4
+S3250001CAA04E80002080A1000880E100107C0538004082001C8081000480C1000C7C0430004E
+S3250001CAC04082000C7C0303784E800020386000014E8000208101000480A1000C7C082800B3
+S3250001CAE04180002480E100048081000C7C0720004082001C80C10008810100107C0640407A
+S3250001CB004080000C386000014E8000207C0303784E80002080A1000480E1000C7C05380088
+S3250001CB20418000248081000480C1000C7C0430004082001C8101000880A100107C08284082
+S3250001CB404181000C386000014E8000207C0303784E80002080E100048081000C7C0720007C
+S3250001CB604181002480C100048101000C7C0640004082001C80A1000880E100107C053840C2
+S3250001CB804081000C386000014E8000207C0303784E8000208081000480C1000C7C04300050
+S3250001CBA0418100248101000480A1000C7C0828004082001C80E10008808100107C072040EE
+S3250001CBC04180000C386000014E8000207C0303784E80002080C100048101000C7C0640403D
+S3250001CBE04180002480A1000480E1000C7C0538004082001C8081000880C100107C043040D6
+S3250001CC004080000C386000014E8000207C0303784E8000208101000480A1000C7C08284033
+S3250001CC204180002480E100048081000C7C0720004082001C80C10008810100107C06404038
+S3250001CC404181000C386000014E8000207C0303784E80002080A1000480E1000C7C05384005
+S3250001CC60418100248081000480C1000C7C0430004082001C8101000880A100107C08284040
+S3250001CC804081000C386000014E8000207C0303784E80002080E100048081000C7C072040FC
+S3250001CCA04181002480C100048101000C7C0640004082001C80A1000880E100107C05384081
+S3250001CCC04180000C386000014E8000207C0303784E8000208121000C814100088081001484
+S3250001CCE080A100107CC921D67CE920167D0451D67CE742147D0549D67CE7421490C3000413
+S3250001CD0090E300004E8000208881000B3863FFFF8CC300017C062000418200107C060000B7
+S3250001CD204082FFF07C0303784E800020906100048081000C7C0400004081007C80A100086B
+S3250001CD4054A5063E50A5442E50A5801E7CA62B787CA72B787CA82B782C04001040810050F2
+S3250001CD607069000741820018212900087D2103A67CA01D2A7C634A147C8920507C892671A7
+S3250001CD804081002C7D2903A690A300003943000890C3000490EA0000386A0008910A00047F
+S3250001CDA04200FFE85484073F4182000C7C8103A67CA01D2A806100044E8000209061000485
+S3250001CDC08121000C7C6A1B787C090000418200A841800184816100082C0900104081008C73
+S3250001CDE07DAB4A147D8A4A147C0A5840418100CC7D67527870E7000340820084714700038C
+S3250001CE004182002020E700047D2748507CE103A67E005C2A7D6B3A147E00552A7D4A3A148F
+S3250001CE207D2E2671408100447DC903A6396BFFFC394AFFFC860B0004960A0004862B0004AA
+S3250001CE40962A0004860B0004960A0004862B0004962A00044200FFE05529073F4182001895
+S3250001CE60396B0004394A00047D2103A67E005C2A7E00552A806100044E8000207D2E26711F
+S3250001CE80408100247DC903A67E0B84AA396B00107E0A85AA394A00104200FFF05529073F0D
+S3250001CEA04182FFD47D2103A67E005C2A7E00552A806100044E8000202C090004418000843C
+S3250001CEC07DA7627870E700034082007871A70003418200188E0DFFFF9E0CFFFF3929FFFF22
+S3250001CEE071A700034082FFF07D2E2671408100347DC903A6860DFFFC960CFFFC862DFFFC60
+S3250001CF00962CFFFC860DFFFC960CFFFC862DFFFC962CFFFC4200FFE05529073F4182FF58B8
+S3250001CF207D2E16714081001C7DC903A6860DFFFC960CFFFC4200FFF8552907BF4182FF384A
+S3250001CF407C0D58404081FF308E0DFFFF9E0CFFFF4BFFFFF090000000806100044E800020DC
+S3250001CF604BFFFE5C3821FFD08281003881C1003C7C731B787C6B1B787C1203787C0D03781C
+S3250001CF807C1003787C0F0378888B00007C8407742C04000C41810268418202502C04000939
+S3250001CFA0418202482C04000A418202402C04000B4182023888CB00007CC607742C06002D77
+S3250001CFC04182020888EB00007CE707742C07002B418201F87C0E00004082001888AB00007B
+S3250001CFE07CA507742C050030418201B039C0000A2C0E0002418000782C0E002441810070B1
+S3250001D0002C0E00104082001488EB00007CE707742C070030418200F07C0D037838A0FFFFA8
+S3250001D0207E25739688CB00007CC607747CCA33787DCC73782C0600304180000C2C0A00396A
+S3250001D040408100BC2C0A00614180000C2C0A007A408100A42C0A00414180000C2C0A005AFF
+S3250001D0604081008C7C0C7000418000547C100000408200087E6B9B787C14000041820008A2
+S3250001D080917400007C0F0000418200103860FFFF382100304E8000207C12000041820014B4
+S3250001D0A07C0303787C6D1850382100304E8000207DA36B78382100304E8000207C0D8840DC
+S3250001D0C04081000839E000017CAD71D67D4562147C0A68404080000839E000017D4D537819
+S3250001D0E0396B00013A1000014BFFFF3C398AFFC94BFFFF74398AFFA94BFFFF6C398AFFD0BA
+S3250001D1004BFFFF64890B00017D0807742C08007841820018888B00017C8407742C04005828
+S3250001D120418200084BFFFEF488AB00027CA507742C0500304180001C88CB00027CC60774C0
+S3250001D1402C0600394181000C396B00024BFFFECC88EB00027CE707742C0700614180001419
+S3250001D160890B00027D0807742C0800664081FFDC888B00027C8407742C0400414180FE9C80
+S3250001D18088AB00027CA507742C0500464181FE8C396B00024BFFFE8439C00008890B0001EC
+S3250001D1A07D0807742C08007841820018888B00017C8407742C040058418200084BFFFE3483
+S3250001D1C039C000104BFFFE2C7D685B78396B0001890800007D0807742C08002D4082FDF8C5
+S3250001D1E03A4000014BFFFDF0396B0001888B00007C8407742C04000C4081FDA02C04000D6C
+S3250001D2004182FFE82C0400204182FFE04BFFFDA87FE802A697E1FFD8814100309061002C0F
+S3250001D22088AA00007CA5077490A100207C0500004082001483E100007FE803A6382100287C
+S3250001D2404E8000207D435378480001819061001C8061002C80A1002090A100084BFFFAADFF
+S3250001D2607C691B787C0900004182005C7D234B789121002480810030908100088101001C6A
+S3250001D2809101000C480000CD812100247C030000408200187D234B7883E100007FE803A6DE
+S3250001D2A0382100284E8000203869000180C1002090C100084BFFFA557C691B787C09000006
+S3250001D2C04082FFAC7C03037883E100007FE803A6382100284E8000207FE802A697E1FFE88F
+S3250001D2E07C691B7880A100207C050000408200247D234B787C080378910100084BFFFA0DBA
+S3250001D30083E100007FE803A6382100184E8000207C040378908100147D234B78810100200E
+S3250001D320910100084BFFF9E57C0300004182001039230001906100144BFFFFE08061001452
+S3250001D34083E100007FE803A6382100184E8000203821FFF081A100188141001C7C6C1B7818
+S3250001D3607C0A0000408100587D896378398C0001892900007D2907747DAB6B7839AD00019B
+S3250001D380896B00007D6B0774394AFFFF7C095800418200247C095840408100103860000163
+S3250001D3A0382100104E8000203860FFFF382100104E8000207C0900004082FFA87C0303783A
+S3250001D3C0382100104E8000207FE802A697E1FFF0906100147C07037890E100084BFFF92D8D
+S3250001D3E0810100147C68185083E100007FE803A6382100104E8000207FE802A697E1FFE013
+S3250001D400814100287C691B789061001C7D234B789121002491410028914100087C0803788A
+S3250001D4209101000C3900271091010010480001617C0300004082001880A100243925271058
+S3250001D44081010028394827104BFFFFC48061001C83E100007FE803A6382100204E8000207E
+S3250001D4603821FFF0818100187C6B1B787D695B78396B0001892900007D2907747D8A6378B6
+S3250001D480398C0001894A00007D4A07747C095000418200247C09504040810010386000016F
+S3250001D4A0382100104E8000203860FFFF382100104E8000207C0900004082FFB47C0303782D
+S3250001D4C0382100104E8000203821FFE8812100247C6D1B78818100207C0900404081005074
+S3250001D4E07DAB6B7839AD0001896B00007D8A6378398C0001894A00007C0B50004182002406
+S3250001D5007C0B50404081001038600001382100184E8000203860FFFF382100184E8000202F
+S3250001D5203929FFFF7C0900404181FFB87C030378382100184E8000203821FFF08121001CE7
+S3250001D5407C6A1B7880E1001870EB00FF7C090040408100307D475378394A000188E7000040
+S3250001D5607C07580040820010386AFFFF382100104E8000203929FFFF7C0900404181FFD842
+S3250001D5807C030378382100104E8000203821FFF0812100207C6A1B78816100188081001C99
+S3250001D5A0708C00FF7C090040408100407D465378394A00017D645B78396B0001888400002C
+S3250001D5C0988600005484063E7C046000408200107D435378382100104E8000203929FFFF16
+S3250001D5E07C0900404181FFC87C030378382100104E8000207C040378808400007C04000006
+S3150001D600418200084BFFFFF04E8000200000000021
+S3250001D61000810E02001415F0001403E80000000000146530000000010000000000000000A0
+S3250001D6300000000000000000FFFFFFFF0000001E0000001D0000001C0000001B0000000065
+S3250001D6500000000000000000000000000000000000000000000000000000000000000000B3
+S3250001D670000000000000000000000000000000000000000000000000000000000000000093
+S3250001D690FFFFFFFF00000A0000000A2000000A4000000A6000140104000000000000000076
+S3250001D6B0000000000000000000000000000000012A2A2A000000000023706C616E392E6936
+S3250001D6D06E690A00000000005250587369676E61747572653D312E300A4E414D453D71627F
+S3250001D6F0727063670A53544152543D46464332303130300A56455253494F4E3D312E310AC9
+S3250001D710000000000001000000000401080205030C06060909070710100000000000000082
+S3250001D7306672656562007167657400000000000000141BAF0000000000000000000000003F
+S3250001D7500000000000000000000000000000000000000000000000000000000000000000B2
+S3250001D770000000000000000000000000000000000000000000000000000000000000000092
+S3250001D79000000000000000000000000000000000626F6F74006469736B00626F6F740075EA
+S3250001D7B06172743A207374617274207472616E736D697373696F6E0A00756172743A2074A5
+S3250001D7D0696D656F75740A006E007900000000000000000000000000001403200000000077
+S3250001D7F0000000000000000000000000000000000000000000000000000000000000000012
+S3250001D8100000000000000000000000000000000000000000000000000000000000000000F1
+S3250001D8300000000000000000FFC206F000000000000000000000000000000000000000001A
+S3250001D8500000000000000000000000000000000000000000000000000000000000000000B1
+S3250001D87000000000000000000000000000000000000000000000000000141A00FFC26E38FC
+S3250001D89000141A04FFC26E3800000000000000006D6170667265653A2025733A206C6F735E
+S3250001D8B0696E672030782575582C2025640A000000141A9300141AA700141AB300141AC312
+S3250001D8D0000000000014054000000000000000006E6577616C61726D000000000000000081
+S3250001D8F00000000000000000F1A55A1FFFC2363000000000000000000000000000000000DB
+S3250001D9100000000000000000FFFFFFFF0000000000000004000000080000000C00000000DC
+S3250001D9306275666665722064657363726970746F7273006264616C6C6F63000000000000B3
+S3250001D95000141A810000000000141A860000000100141A8C00000002000000000000000090
+S3250001D970FFFFFFFFFFFF000000000000000000000000000000000000000000000000000096
+S3250001D9900000000000000000000000000000000000000000000000000000000013880000D5
+S3250001D9B0FFFFFFFFFA203C00FA203D00FA203E00FA203F000000000000141FD70014203B7D
+S3250001D9D06932632073657475702E2E2E0A004932432325780A0000000000000B00000000BA
+S3250001D9F0000000000000000000140E4000000000000000200000000000000000000000008E
+S3250001DA10000004000000000E0000000000000000000004000000000E0000000000000000CB
+S3250001DA3041F0000000000000001407460014074C0014075100140754001407580014075B12
+S3250001DA500014075E0014076200140766001407690014076C0014076F001407720014077586
+S3250001DA70001407780014077B0014077E0014078100140784001407880014078C001407909D
+S3250001DA9000140794001407980014079C001407A0001407A4001407A8001407AC001407B087
+S3250001DAB0001407B4001407B8001407BC001407C0001407C4001407C8001407CC001407D067
+S3250001DAD0001407D4001407D8FFC24714FFC2474CFFC24798FFC247BCFFC247C4FFC247CCD9
+S3250001DAF0FFC247F0FFC24830FFC24838FFC2485CFFC2488C000000000000000000000000A3
+S3250001DB100000000000000000000000000000000000000000000000000000000000000000EE
+S3250001DB300000000000000000000000000000000000000000000000000000000000000000CE
+S3250001DB507072656D617475726520454F460A002E00626164206D61676963203078256C7591
+S3250001DB7078206E6F74206120706C616E20392065786563757461626C65210A002564002BDF
+S3250001DB9025644025382E386C7578002B25640A7374617274206174203078256C75780A0058
+S3250001DBB000000000000000000000000000000000000000000000000000000000000000004E
+S3250001DBD000000000000A000000000000000000000000000000000000000000000000000024
+S3250001DBF0000000000000000000000000000000000000000000000000090000000000000005
+S3250001DC100000000102000000030000000400000500000006000700000800000000000000C9
+S3250001DC30726573657276656420300073797374656D207265736574006D616368696E652040
+S3250001DC50636865636B00646174612061636365737300696E737472756374696F6E206163DD
+S3250001DC70636573730065787465726E616C20696E7465727275707400616C69676E6D656E24
+S3250001DC90740070726F6772616D20657863657074696F6E00666C6F6174696E672D706F6949
+S3250001DCB06E7420756E617661696C61626C650064656372656D656E74657200726573657218
+S3250001DCD0766564204100726573657276656420420073797374656D2063616C6C0074726123
+S3250001DCF06365207472617000666C6F6174696E6720706F696E742061737369737400726542
+S3250001DD10736572766564204600736F66747761726520656D756C6174696F6E0049544C4279
+S3250001DD30206D6973730044544C42206D6973730049544C42206572726F720044544C422033
+S3250001DD506572726F72004341555345005352523100504300474F4B004C52004352005845A5
+S3250001DD7052004354520052300052310052320052330052340052350052360052370052389B
+S3250001DD900052390052313000523131005231320052313300523134005231350052313600E7
+S3250001DDB052313700523138005231390052323000523231005232320052323300523234008D
+S3250001DDD0523235005232360052323700523238005232390052333000523331007365746866
+S3250001DDF0766563006F7574206F6620696E746572727570742068616E646C6572730025736B
+S3250001DE1009252E386C757809257309252E386C75780A00657863657074696F6E2F696E741D
+S3250001DE30657272757074202325780A00657863657074696F6E2025730A005E5020746F20DD
+S3250001DE5072657365740A00005573696E6720666C61736820636F6E66696775726174696F20
+S3250001DE706E0A000A002573256400747970653D00706F72743D006972713D006D656D3D00E2
+S3250001DE9073697A653D0065613D00000000000000676574636C75737420402025640A00678B
+S3250001DEB06574636C75737420256420696E2063616368650A00676574636C75737420616439
+S3250001DED064722025640A0063616E2774207365656B20626C6F636B0A0063616E2774207279
+S3250001DEF065616420626C6F636B0A00676574636C75737420256420726561640A0066617497
+S3250001DF1077616C6B2025640A006765746661740066617477616C6B202564202D3E20256446
+S3250001DF300A0066696C65616464722025382E38732025640A0066696C656164647220256498
+S3250001DF50202D3E2025640A0066696C6561646472202564202D3E2025640A0077616C6B6932
+S3250001DF706E67206E6F6E2D6469726563746F7279210A00636F6D706172696E6720746F203A
+S3250001DF9025382E38732E25332E33730A0025382E38732E25332E33732069732061204C4116
+S3250001DFB042454C0A0063616E2774207265616420626F6F7420626C6F636B0A006E6F74206A
+S3250001DFD0444F530A006E6F20646F732066696C652073797374656D0A006D616769633A200D
+S3250001DFF0307825322E327820307825322E327820307825322E32780A0076657273696F6E35
+S3250001E0103A202225382E3873220A007365637473697A653A2025640A00616C6C6F637369CD
+S3250001E0307A653A2025640A006E72657372763A2025640A006E666174733A2025640A0072F5
+S3250001E0506F6F7473697A653A2025640A00766F6C73697A653A2025640A006D656469616452
+S3250001E0706573633A20307825322E32780A0066617473697A653A2025640A0074726B736903
+S3250001E0907A653A2025640A006E68656164733A2025640A006E68696464656E3A2025640A76
+S3250001E0B000626967766F6C73697A653A2025640A0064726976656E6F3A2025640A00726563
+S3250001E0D0736572766564303A20307825322E32780A00626F6F747369673A20307825322EB7
+S3250001E0F032780A00766F6C69643A20307825382E38780A006C6162656C3A20222531312EBF
+S3250001E110313173220A006E616D6520636F6D706F6E656E7420746F6F206C6F6E670A0065A2
+S3250001E13072726F722077616C6B696E6720746F2025730A002573206E6F7420666F756E64EC
+S3250001E1500A00666F756E642025382E38732E25332E3373206174747220307825757820738A
+S3250001E17074617274203078256C7578206C656E2025640A00626164206D6167696320307865
+S3250001E190256C7578206E6F74206120706C616E20392065786563757461626C65210A002B3C
+S3250001E1B02564002B2564002B25640A7374617274206174203078256C75780A002E006E61DD
+S3250001E1D06D652069732025382E38732025332E33730A0000000000002E007A71733A206E5A
+S3250001E1F06F7420706F77657250432065786563757461626C650A0074657874007A71733A9C
+S3250001E21020666F726D6174206572726F720A0064617461007A71733A20666F726D617420EF
+S3250001E2306572726F720A000A73717565657A6564206B65726E656C3A20636865636B737542
+S3250001E2506D206572726F723A2025382E386C7578206E6565642025382E386C75780A007503
+S3250001E2706E7061636B2025732025382E386C757820256C75643A00202A2A73697A6520650E
+S3250001E29072726F720A000A007A71733A20636F72727570742073717565657A65642064615C
+S3250001E2B074612073747265616D0A00205425640025642025382E386C75782025382E386CAB
+S3250001E2D075780A0000000000666C6173683A2062616420636865636B73756D0A00666C61F1
+S3250001E2F073683A20666C617368206E6F742070726573656E74206F72206E6F7420656E616C
+S3250001E310626C65640A00636F6E66202325382E386C75783A20232578202325362E366C7543
+S3250001E330780A00666C6173683A20666F756E6420636F6E6669672025382E386C75782825A1
+S3250001E35064293A0A25730A00666C6173683A206E6F20636F6E6669670A00666C6173682020
+S3250001E370636F6E6669672025382E386C7578282564293A0A25730A00666C6173683A207339
+S3250001E390717565657A656420706F7765727063206B65726E656C20696E7374616C6C6564D2
+S3250001E3B00A00666C6173683A20756E73717565657A656420706F7765727063206B65726E9B
+S3250001E3D0656C20696E7374616C6C65640A00666C6173683A206E6F20706F776572706320E6
+S3250001E3F06B65726E656C20696E20466C6173680A00746578743A2025382E386C7578203CDF
+S3250001E4102D2025382E386C7578205B256C645D0A00646174613A2025382E386C7578203C39
+S3250001E4302D2025382E386C7578205B256C645D0A00656E7472793D3078256C75780A0000E6
+S3250001E450907070F0F0F07000F0888888F8707070E0E0E0E0E09070F070F870F0F870F0882D
+S3250001E470000000000000000000000000000000000000000000000000000000000000000085
+S3250001E490000000000000000000000000000000000000000000000000000000000000000065
+S3250001E4B000000000000000000000000000000000000000000000000000000000000000E065
+S3250001E4D0D0808080808088008888C888808888889090909090D0808080808080808888881D
+S3250001E4F000000000080000000C3000000000000600000000000000000000000000000000BB
+S3250001E5100000000000000000000000000000000000000000000000000000003CC03C0000AC
+S3250001E5300000600006001E00601818607800000000000000000000000000001C18380090DC
+S3250001E550B06060E0E0E0F800F088A888808080809090909090B060E0808060E080808888E4
+S3250001E570001824283E70701818180000000000063C183C3C1C3E3C7E3C3C00000200403CDE
+S3250001E5903C187C1E787E7E1E663C7C666066623C7C3C7C3C7E6266C266667E30C00C10002E
+S3250001E5B008006000060030006018186018000000000000001000000000000030180C0090AA
+S3250001E5D0901010808080881888F8A888E0807070E0E0E0E0E090108070E01080E098F08814
+S3250001E5F0001824286ADAD818181810000000000C663866662C3E667E6666000006006066D0
+S3250001E610421866326C606032661818646066722466246666186262DA62620630600C380093
+S3250001E63010006000060030006000006018000000000000003000000000000030180C00E0E1
+S3250001E65000E0E0F0F0F000188888985080880808201C1C1C1C00E0F00080E0F08088888823
+S3250001E6700018242868DAD808300C54180000000C665806062C206002666618180CFE300695
+S3250001E6909E2C6660666060606618186C6066726666666660186262DA36660C30600C2800F9
+S3250001E6B0103C6C3C3E3C7E3E6C78786618D46C3C6C3E763C7E6666C266667E1818180000C9
+S3250001E6D04418000018241C24F08888208070F0F020202020201C243E1CF8241C8070887001
+S3250001E6F00018247C78745008300C381800000018661806064C2060067666181818FE180CC1
+S3250001E710B62C66606660606066181868607E5A6666666470186266DA34340C30300C6C0072
+S3250001E73018667666666630667618186418FE766676663666306662DA626206081810323C58
+S3250001E75044247C7C2434204200000000000000002020202020222408220024200000000034
+S3250001E770001800283C287610300CEE7E00FE001866180C184C3C7C0C3C3E000030000C181A
+S3250001E790B62C7C60667C7C6E7E181878605A5A666466783C186234DA18341830300C44001E
+S3250001E7B018066660666630666618186818D6666666663860306662DA34620C30180C5A209E
+S3250001E7D044241010242C20420E3E103E3E3C1C3E3C1C1C1C1C3E1C083E2224180E0E0E0E98
+S3250001E7F00008007C1E5CDC00300C387E00FE0030661818067E0666186E06000018001818AE
+S3250001E810B67E6660666060666618186C605A4E6678666C0E1862346C2C183030180C44006D
+S3250001E830003E6660667E30666618187818D666666666303C306634DA1834180818104C3887
+S3250001E8503C18101018241C4211081008202222080000000000220408223618041111111110
+S3250001E8700000002816B6CC00300C541800000030661830067E066618660600000CFE30008D
+S3250001E8909A466660666060666618186C605A4E66606666061862346C6C183030180C00006B
+S3250001E8B000666660666030666618186C18D66666666630063066346C2C34301818180020CD
+S3250001E8D000091010000E094210081008202222080F06060F0A09041E002A0E3810101010F4
+S3250001E8F00018002856B6CC00300C10181800186066187E660C6666306666181806FE601813
+S3250001E910404666326C60603666181866605A4624602466661834186C461860300C0C00001A
+S3250001E930006E66666E66306E6618186618D66666666E3066306E186C46186030180C003C84
+S3250001E950080909091F110AFF0E081008382C2208080209010A0A0911092209070E0E0E0ED3
+S3250001E970001800287C1C760018180000180018603C7E7E3C0C3C3C303C3C18180200401848
+S3250001E9903E467C1E787E601E663C18667E42463C603C663C1818186C66187E300C0C000036
+S3250001E9B000367C3C363C7C36667E18667ED6663C7C367C3C1E36186C66187E30180C00083C
+S3250001E9D0080F060604110C1801081008202222080E0202030A0C0D1E0D220E080101010198
+S3250001E9F0000000001000000018180000080000C000000000000000000000000800000000F0
+S3250001EA1000000000000000000000180000000000000C00000000000000000030060C00FE7B
+S3250001EA300000000000000006000018000000000060060000000000000010001C18380008B7
+S3250001EA5008090606040E0A1811081F0820221C3E080204010F0A0B110B22090811111111AC
+S3250001EA7000000000000000000C300000080000C00000000000000000000000080000000073
+S3250001EA900000000000000000000070000000000000060000000000000000003C063C00006B
+S3250001EAB0000000000000006600001800000000006006000000000000003000000000000823
+S3250001EAD00F090909040309000E000000000000000F0F0F0F0209091E09000F070E0E0E0E12
+S3250001EAF00000000000000000000000001000000000000000000000000000001000000000DF
+S3250001EB100000000000000000000000000000000000000000000000000000000000000000DE
+S3250001EB30000000000000003C00007000000000006006000000000000006000000000000F3D
+S3250001EB50636F6E736F6C65006C63640073637265656E006C63640062617564005E70002536
+S3250001EB70735B64656661756C743D3D25735D3A200025733A20000A006C696E6520746F6FEC
+S3250001EB90206C6F6E670A0070616E69633A20000A0000000000000000001406200014062B96
+S3250001EBB000140638001406460014065200140665001406780014068200140694001406AFFC
+S3250001EBD0001406BB001406C6001406D1001406DD001406E8001406FE001407090014071C12
+S3250001EBF000140726001407300014073B000000000000000E000800000008000E00080000E8
+S3250001EC100010000E000800000018000E000800000020000E000800000028000E0008000015
+S3250001EC300030000E000800000038000E000800000040000E000800000048000E0008000075
+S3250001EC500050000E000800000058000E000800000060000E000800000068000E00080000D5
+S3250001EC700070000E000800000078000E000800000080000E000800000088000E0008000035
+S3250001EC900090000E000800000098000E0008000000A0000E0008000000A8000E0008000095
+S3250001ECB000B0000E0008000000B8000E0008000000C0000E0008000000C8000E00080000F5
+S3250001ECD000D0000E0008000000D8000E0008000000E0000E0008000000E8000E0008000055
+S3250001ECF000F0000E0008000000F8000E0008000001000000000800000108020B00080000C2
+S3250001ED1001100206000800000118020B000800000120010C000800000128020B0008000019
+S3250001ED300130020B0008000001380207000800000140010D000800000148010D0008000076
+S3250001ED500150030A000800000158040A000800000160090E000800000168060800080000C8
+S3250001ED700170090B000800000178010D000800000180020B000800000188020B000800002C
+S3250001ED900190020B000800000198020B0008000001A0020B0008000001A8020B0008000094
+S3250001EDB001B0020B0008000001B8020B0008000001C0020B0008000001C8020B00080000F4
+S3250001EDD001D0040B0008000001D8040E0008000001E0020B0008000001E8040A000800004C
+S3250001EDF001F0020B0008000001F8020B000800000200020B000800000208020B00080000B2
+S3250001EE100210020B000800000218020B000800000220020B000800000228020B000800000F
+S3250001EE300230020B000800000238020B000800000240020B000800000248020B000800006F
+S3250001EE500250020D000800000258020B000800000260020B000800000268020B00080000CD
+S3250001EE700270020B000800000278020B000800000280020B000800000288020D000800002D
+S3250001EE900290020B000800000298020B0008000002A0020B0008000002A8020B000800008F
+S3250001EEB002B0020B0008000002B8020B0008000002C0020B0008000002C8020B00080000EF
+S3250001EED002D0020B0008000002D8010D0008000002E0010D0008000002E8010D000800004C
+S3250001EEF002F002080008000002F80B0C0008000003000207000800000308040B00080000A8
+S3250001EF100310010B000800000318040B000800000320010B000800000328040B0008000008
+S3250001EF300330010B000800000338040E000800000340010B000800000348010B0008000068
+S3250001EF500350010E000800000358010B000800000360010B000800000368040B00080000C8
+S3250001EF700370040B000800000378040B000800000380040E000800000388040E000800001C
+S3250001EF900390040B000800000398040B0008000003A0020B0008000003A8040B0008000084
+S3250001EFB003B0040B0008000003B8040B0008000003C0040B0008000003C8040E00080000DF
+S3250001EFD003D0040B0008000003D8010C0008000003E0010C0008000003E8010C0008000048
+S3250001EFF003F005080008000003F8000E000800000400000E000800000000000000000000C7
+S3250001F010534343005343433200657468657225643A2025733A20706F7274203078256C7575
+S3250001F0305820697271202564002061646472203078256C7558002073697A65203078256C37
+S3250001F0507558003A002025322E327558000A00657468657225643A207478207175657565B8
+S3250001F0702066756C6C0A0000666C6173680046006574686572006500617461006864006861
+S3250001F090006175746F006C6F63616C006D616E75616C00696E6665726E6F2F696E666572E3
+S3250001F0B06E6F2E696E6900696E6665726E6F2E696E6900706C616E392F706C616E392E6901
+S3250001F0D06E6900706C616E392E696E69002100002573212564006469736B002573212564A0
+S3250001F0F0212573006469736B00696D706300257321256421257300626F6F74006469736B92
+S3250001F11000646F73006469736B00646F7320696E6974206661696C65640A00707265646132
+S3250001F130776E0A006461776E0A006F7074696F6E733D232575780A00466C61736820626FAE
+S3250001F1506F740A00626F6F7466696C650042616420626F6F7466696C652073796E74617815
+S3250001F1703A2025730A0043616E6E6F7420616363657373206465766963653A2025730A00FB
+S3250001F190426F6F7420646576696365733A00202573212564002025732125640020257321EA
+S3250001F1B02564000A00626F6F742066726F6D00706879736963616C206D656D6F727900699E
+S3250001F1D0616C6C6F63006672656500000000000045544845522E5343432325643A20736310
+S3250001F1F06365203D2030782575580A007E0040006E6F207072657365742045746865722089
+S3250001F210616464726573730A00303031303862663132393030006574686572204D41432001
+S3250001F2306164647265737300696E76616C6964204D414320616464726573730A00000000E9
+S3250001F2500A746674703A206572726F72282564293A2025730A0069702063686B73756D20D1
+S3250001F2706572726F720A0069702062616420766572732F686C656E0A007564702063686BC4
+S3250001F29073756D206572726F72206373756D202325346C7578206C656E2025640A00756400
+S3250001F2B0703A207061636B657420746F6F206269670A002573006F637465740074667470B2
+S3250001F2D06F70656E3A206572726F7220282564293A2025730A00746674706F70656E3A2021
+S3250001F2F0626C6F636B206572726F723A2025640A00626C6F636B206572726F720074667412
+S3250001F310706F70656E3A206661696C656420746F20636F6E6E65637420746F207365727605
+S3250001F33065720A0074667470726561643A20256420213D2025640A0073686F727420726540
+S3250001F3506164003F0074667470726561643A20626C6F636B206572726F723A2025642C205A
+S3250001F37065787065637465642025640A00626C6F636B206572726F7200696E76616C696435
+S3250001F3902063746C726E6F2025640A00626F6F74702074696D6564206F75740A0025732060
+S3250001F3B0002825642E25642E25642E2564212564293A2025730A00626164206D6167696349
+S3250001F3D0206E756D626572006C6F61644025382E386C75783A20002564002B2564006F6B00
+S3250001F3F0002B25643D25640A00656E7472793A203078256C75780A000000000000141A6820
+S3250001F41000000008FFC27AE40000000000000000000000000000000000141A6E0000000012
+S3250001F43000000000000000000000000200141A7000000004FFC26068000000000000000088
+S3250001F45000000000000000000000000000141A7600000000000000000000000300141A7848
+S3250001F470000000130000000000000000000000000000000000141A7C00141A7F000000000B
+S3250001F4900000000000000000FFFFFFFF000000000000000000000000000000000000000059
+S3250001F4B0000000000000000000000000000000000000000000000000496E6665726E6F2044
+S3250001F4D0626F6F7473747261700A005056523A20004D5043363031004D5043363033004D9E
+S3250001F4F05043363034004D504336303365004D5043363033652D7637004D5043387878002A
+S3250001F510506F77657250432076657273696F6E20232578002C207265766973696F6E202330
+S3250001F530256C75780A00494D4D523A20004D50433836302F383231004D5043383233004D8B
+S3250001F55050433832334100547970652023256C7578002C206D61736B2023256C75780A00FD
+S3250001F5706F7074696F6E733A2023256C75780A00626373723A2025382E386C75780A0050E9
+S3250001F5904C505243523D25382E386C757820534343523D25382E386C75780A00256C756460
+S3250001F5B0204D487A2073797374656D0A000A004252303D25382E386C7578204F52303D25BC
+S3250001F5D0382E386C75780A004D505450523D25382E386C75780A006574686572303D7479AB
+S3250001F5F070653D53434320706F72743D312065613D3030313065633030303035310D0A7657
+S3250001F610676173697A653D3634307834383078380D0A6B65726E656C70657263656E743DEF
+S3250001F63034300D0A636F6E736F6C653D300D0A626175643D393630300D0A006574686572EA
+S3250001F650303D747970653D53434320706F72743D322065613D303031306563303030303559
+S3250001F670310D0A76676173697A653D3634307834383078380D0A6B65726E656C7065726355
+S3250001F690656E743D34300D0A636F6E736F6C653D300D0A626175643D393630300D0A0055C9
+S3250001F6B073696E672064656661756C7420636F6E66696775726174696F6E0A00414D4432D7
+S3250001F6D039463078300053434300454100693263206661696C65640A00656570726F6D3A0E
+S3250001F6F00A002025322E3275785B25635D000A00CFFFCC240FFFCC040CAFCC0403AFCC082E
+S3250001F7103FBFCC27FFFFCC25FFFFCC25FFFFCC25CFFFCC240FFFCC040CAFCC8403AFCC8867
+S3250001F7303FBFCC27FFFFCC25FFFFCC25FFFFCC25FFFFCC25FFFFCC25FFFFCC25FFFFCC2538
+S3250001F750FFFFCC25FFFFCC25FFFFCC25FFFFCC25CFFFCC240FFFCC040CFFCC0403FFCC0091
+S3250001F7703FFFCC27FFFFCC25FFFFCC25FFFFCC25CFFFCC240FFFCC040CFFCC8403FFCC842B
+S3250001F7900CFFCC0033FFCC27FFFFCC25FFFFCC25FFFFCC25FFFFCC25FFFFCC25FFFFCC25BC
+S3250001F7B0FFFFCC25FFFFCC25FFFFCC25FFFFCC25C0FFCC2403FFCC240FFFCC240FFFCC24D9
+S3250001F7D03FFFCC27FFFFCC25FFFFCC25FFFFCC25FFFFCC25FFFFCC25FFFFCC25FFFFCC2558
+S3150001F7F0FFFFCC25FFFFCC25FFFFCC25FFFFCC2546
+S700000100FE
--- /dev/null
+++ b/os/boot/rpcg/gbitbltclip.c
@@ -1,0 +1,52 @@
+#include <u.h>
+#include <libc.h>
+#include <libg.h>
+#include <gnot.h>
+
+void
+gbitbltclip(void *vp)
+{
+	int dx, dy;
+	int i;
+	struct{
+		GBitmap *dm;
+		Point p;
+		GBitmap *sm;
+		Rectangle r;
+		Fcode f;
+	}*bp;
+
+	bp = vp;
+	dx = Dx(bp->r);
+	dy = Dy(bp->r);
+	if(bp->p.x < bp->dm->clipr.min.x){
+		i = bp->dm->clipr.min.x-bp->p.x;
+		bp->r.min.x += i;
+		bp->p.x += i;
+		dx -= i;
+	}
+	if(bp->p.y < bp->dm->clipr.min.y){
+		i = bp->dm->clipr.min.y-bp->p.y;
+		bp->r.min.y += i;
+		bp->p.y += i;
+		dy -= i;
+	}
+	if(bp->p.x+dx > bp->dm->clipr.max.x)
+		bp->r.max.x -= bp->p.x+dx-bp->dm->clipr.max.x;
+	if(bp->p.y+dy > bp->dm->clipr.max.y)
+		bp->r.max.y -= bp->p.y+dy-bp->dm->clipr.max.y;
+	if(bp->r.min.x < bp->sm->clipr.min.x){
+		i = bp->sm->clipr.min.x-bp->r.min.x;
+		bp->p.x += i;
+		bp->r.min.x += i;
+	}
+	if(bp->r.min.y < bp->sm->clipr.min.y){
+		i = bp->sm->clipr.min.y-bp->r.min.y;
+		bp->p.y += i;
+		bp->r.min.y += i;
+	}
+	if(bp->r.max.x > bp->sm->clipr.max.x)
+		bp->r.max.x = bp->sm->clipr.max.x;
+	if(bp->r.max.y > bp->sm->clipr.max.y)
+		bp->r.max.y = bp->sm->clipr.max.y;
+}
--- /dev/null
+++ b/os/boot/rpcg/gnot.h
@@ -1,0 +1,71 @@
+
+extern void	*bbmalloc(int);
+extern void	bbfree(void *, int);
+extern int	bbonstack(void);
+extern void	bbexec(void(*)(void), int, int);
+
+/*
+ * Graphics types
+ */
+
+typedef	struct	GBitmap		GBitmap;
+typedef struct	GFont		GFont;
+typedef struct	GSubfont	GSubfont;
+typedef struct	GCacheinfo	GCacheinfo;
+
+struct	GBitmap
+{
+	ulong	*base;		/* pointer to start of data */
+	long	zero;		/* base+zero=&word containing (0,0) */
+	ulong	width;		/* width in 32 bit words of total data area */
+	int	ldepth;		/* log base 2 of number of bits per pixel */
+	Rectangle r;		/* rectangle in data area, local coords */
+	Rectangle clipr;	/* clipping region */
+	GBitmap	*cache;		/* zero; distinguishes bitmap from layer */
+};
+
+
+/*
+ * GFont etc. are not used in the library, only in devbit.c.
+ * GSubfont is only barely used.
+ */
+struct	GSubfont
+{
+	short	n;		/* number of chars in font */
+	char	height;		/* height of bitmap */
+	char	ascent;		/* top of bitmap to baseline */
+	Fontchar *info;		/* n+1 character descriptors */
+	GBitmap	*bits;		/* where the characters are */
+};
+struct GCacheinfo
+{
+	ulong		xright;	/* right edge of bits */
+	Fontchar;
+};
+
+struct GFont
+{
+	uchar		height;	/* max height of bitmap, interline spacing */
+	char		ascent;	/* top of bitmap to baseline */
+	char		width;	/* widest so far; used in caching only */	
+	char		ldepth;	/* of images */
+	short		id;	/* of font */
+	int		ncache;	/* number of entries in cache */
+	GCacheinfo	*cache;	/* cached characters */
+	GBitmap		*b;	/* cached images */
+};
+
+extern ulong	 *gaddr(GBitmap*, Point);
+extern uchar	 *gbaddr(GBitmap*, Point);
+extern void	 gbitblt(GBitmap*, Point, GBitmap*, Rectangle, Fcode);
+extern void	 gbitbltclip(void*);
+extern void	 gtexture(GBitmap*, Rectangle, GBitmap*, Fcode);
+extern Point	 gsubfstrsize(GSubfont*, char*);
+extern int	 gsubfstrwidth(GSubfont*, char*);
+extern Point	 gsubfstring(GBitmap*, Point, GSubfont*, char*, Fcode);
+extern Point	 gbitbltstring(GBitmap*, Point, GSubfont*, char*, Fcode);
+extern void	 gsegment(GBitmap*, Point, Point, int, Fcode);
+extern void	 gpoint(GBitmap*, Point, int, Fcode);
+extern void	 gflushcpucache(void);
+extern GBitmap*	 gballoc(Rectangle, int);
+extern void	 gbfree(GBitmap*);
--- /dev/null
+++ b/os/boot/rpcg/i2c.c
@@ -1,0 +1,360 @@
+#include "boot.h"
+
+/*
+ * basic read/write interface to mpc8xx I2C bus (master mode)
+ */
+
+typedef struct I2C I2C;
+
+struct I2C {
+	uchar	i2mod;
+	uchar	rsv12a[3];
+	uchar	i2add;
+	uchar	rsv12b[3];
+	uchar	i2brg;
+	uchar	rsv12c[3];
+	uchar	i2com;
+	uchar	rsv12d[3];
+	uchar	i2cer;
+	uchar	rsv12e[3];
+	uchar	i2cmr;
+};
+
+enum {
+	/* i2c-specific BD flags */
+	RxeOV=		1<<1,	/* overrun */
+	TxS=			1<<10,	/* transmit start condition */
+	TxeNAK=		1<<2,	/* last transmitted byte not acknowledged */
+	TxeUN=		1<<1,	/* underflow */
+	TxeCL=		1<<0,	/* collision */
+	TxERR=		(TxeNAK|TxeUN|TxeCL),
+
+	/* i2cmod */
+	REVD=	1<<5,	/* =1, LSB first */
+	GCD=	1<<4,	/* =1, general call address disabled */
+	FLT=		1<<3,	/* =0, not filtered; =1, filtered */
+	PDIV=	3<<1,	/* predivisor field */
+	EN=		1<<0,	/* enable */
+
+	/* i2com */
+	STR=		1<<7,	/* start transmit */
+	I2CM=	1<<0,	/* master */
+	I2CS=	0<<0,	/* slave */
+
+	/* i2cer */
+	TXE =	1<<4,
+	BSY =	1<<2,
+	TXB =	1<<1,
+	RXB =	1<<0,
+
+	/* port B bits */
+	I2CSDA =	IBIT(27),
+	I2CSCL = IBIT(26),
+
+	Rbit =	1<<0,	/* bit in address byte denoting read */
+
+	/* maximum I2C I/O (can change) */
+	MaxIO=	128,
+	Bufsize =	MaxIO+4,	/* extra space for address/clock bytes and alignment */
+	Freq =	100000,
+	I2CTimeout = 250,	/* msec */
+};
+
+/* data cache needn't be flushed if buffers allocated in uncached INTMEM */
+#define	DCFLUSH(a,n)
+
+/*
+ * I2C software structures
+ */
+
+struct Ctlr {
+	Lock;
+	QLock	io;
+	int	init;
+	I2C*	i2c;
+	IOCparam*	sp;
+
+	BD*	rd;
+	BD*	td;
+	int	phase;
+	int	timeout;
+	char*	addr;
+	char*	txbuf;
+	char*	rxbuf;
+};
+typedef struct Ctlr Ctlr;
+
+static	Ctlr	i2ctlr[1];
+extern	int	predawn;
+
+static	void	interrupt(Ureg*, void*);
+
+static void
+enable(void)
+{
+	I2C *i2c;
+
+	i2c = i2ctlr->i2c;
+	i2c->i2cer = ~0;	/* clear events */
+	eieio();
+	i2c->i2mod |= EN;
+	eieio();
+	i2c->i2cmr = TXE|BSY|TXB|RXB;	/* enable all interrupts */
+	eieio();
+}
+
+static void
+disable(void)
+{
+	I2C *i2c;
+
+	i2c = i2ctlr->i2c;
+	i2c->i2cmr = 0;	/* mask all interrupts */
+	i2c->i2mod &= ~EN;
+}
+
+/*
+ * called by the reset routine of any driver using the I2C
+ */
+void
+i2csetup(void)
+{
+	IMM *io;
+	I2C *i2c;
+	IOCparam *sp;
+	Ctlr *ctlr;
+	long f, e, emin;
+	int p, d, dmax;
+
+	ctlr = i2ctlr;
+	if(ctlr->init)
+		return;
+	print("i2c setup...\n");
+	ctlr->init = 1;
+	i2c = KADDR(INTMEM+0x860);
+	ctlr->i2c = i2c;
+	sp = KADDR(INTMEM+0x3c80);
+	ctlr->sp = sp;
+	disable();
+
+	if(ctlr->txbuf == nil){
+		ctlr->txbuf = ialloc(Bufsize, 2);
+		ctlr->addr = ctlr->txbuf+Bufsize;
+	}
+	if(ctlr->rxbuf == nil)
+		ctlr->rxbuf = ialloc(Bufsize, 2);
+	if(ctlr->rd == nil){
+		ctlr->rd = bdalloc(1);
+		ctlr->rd->addr = PADDR(ctlr->rxbuf);
+		ctlr->rd->length = 0;
+		ctlr->rd->status = BDWrap;
+	}
+	if(ctlr->td == nil){
+		ctlr->td = bdalloc(2);
+		ctlr->td->addr = PADDR(ctlr->txbuf);
+		ctlr->td->length = 0;
+		ctlr->td->status = BDWrap|BDLast;
+	}
+
+	/* select port pins */
+	io = ioplock();
+	io->pbdir |= I2CSDA | I2CSCL;
+	io->pbodr |= I2CSDA | I2CSCL;
+	io->pbpar |= I2CSDA | I2CSCL;
+	iopunlock();
+
+	/* explicitly initialise parameters, because InitRxTx can't be used (see i2c/spi relocation errata) */
+	sp = ctlr->sp;
+	sp->rbase = PADDR(ctlr->rd);
+	sp->tbase = PADDR(ctlr->td);
+	sp->rfcr = 0x18;
+	sp->tfcr = 0x18;
+	sp->mrblr = Bufsize;
+	sp->rstate = 0;
+	sp->rptr = 0;
+	sp->rbptr = sp->rbase;
+	sp->rcnt = 0;
+	sp->tstate = 0;
+	sp->tbptr = sp->tbase;
+	sp->tptr = 0;
+	sp->tcnt = 0;
+	eieio();
+
+	i2c->i2com = I2CM;
+	i2c->i2mod = 0;	/* normal mode */
+	i2c->i2add = 0;
+
+	emin = Freq;
+	dmax = (m->cpuhz/Freq)/2-3;
+	for(d=0; d < dmax; d++){
+		for(p=3; p>=0; p--){
+			f = (m->cpuhz>>(p+2))/(2*(d+3));
+			e = Freq - f;
+			if(e < 0)
+				e = -e;
+			if(e < emin){
+				emin = e;
+				i2c->i2brg = d;
+				i2c->i2mod = (i2c->i2mod&~PDIV)|((3-p)<<1); /* set PDIV */
+			}
+		}
+	}
+	//print("i2brg=%d i2mod=#%2.2ux\n", i2c->i2brg, i2c->i2mod);
+	setvec(VectorCPIC+0x10, interrupt, i2ctlr);
+}
+
+enum {
+	Idling,
+	Done,
+	Busy,
+		Sending,
+		Recving,
+};
+
+static void
+interrupt(Ureg*, void *arg)
+{
+	int events;
+	Ctlr *ctlr;
+	I2C *i2c;
+
+	ctlr = arg;
+	i2c = ctlr->i2c;
+	events = i2c->i2cer;
+	eieio();
+	i2c->i2cer = events;
+	if(events & (BSY|TXE)){
+		print("I2C#%x\n", events);
+		if(ctlr->phase != Idling){
+			ctlr->phase = Idling;
+		}
+	}else{
+		if(events & TXB){
+			//print("i2c: xmt %d %4.4ux %4.4ux\n", ctlr->phase, ctlr->td->status, ctlr->td[1].status);
+			if(ctlr->phase == Sending){
+				ctlr->phase = Done;
+			}
+		}
+		if(events & RXB){
+			//print("i2c: rcv %d %4.4ux %d\n", ctlr->phase, ctlr->rd->status, ctlr->rd->length);
+			if(ctlr->phase == Recving){
+				ctlr->phase = Done;
+			}
+		}
+	}
+}
+
+static int
+done(void *a)
+{
+	return ((Ctlr*)a)->phase < Busy;
+}
+
+static void
+i2cwait(Ctlr *ctlr)
+{
+	int i;
+
+	ctlr->timeout = 0;
+	i = 0;
+	while(!done(ctlr)){
+		if(predawn){
+			if(++i > 100){
+				ctlr->phase = Done;
+				ctlr->timeout = 1;
+				return;
+			}
+			delay(1);
+			interrupt(nil, ctlr);
+		}
+	}
+}
+
+long
+i2csend(int addr, void *buf, long n)
+{
+	Ctlr *ctlr;
+	int i, p, s;
+
+	ctlr = i2ctlr;
+	if(n > MaxIO)
+		return -1;
+	i = 1;
+	ctlr->txbuf[0] = addr & ~1;
+	if(addr & 1){
+		ctlr->txbuf[1] = addr>>8;
+		i++;
+	}
+	memmove(ctlr->txbuf+i, buf, n);
+	DCFLUSH(ctlr->txbuf, Bufsize);
+	ctlr->phase = Sending;
+	ctlr->rd->status = BDEmpty|BDWrap|BDInt;
+	ctlr->td->addr = PADDR(ctlr->txbuf);
+	ctlr->td->length = n+i;
+	ctlr->td->status = BDReady|BDWrap|BDLast|BDInt;
+	enable();
+	ctlr->i2c->i2com = STR|I2CM;
+	eieio();
+	i2cwait(ctlr);
+	disable();
+	p = ctlr->phase;
+	s = ctlr->td->status;
+	if(s & BDReady || s & TxERR || p != Done || ctlr->timeout)
+		return -1;
+	return n;
+}
+
+long
+i2crecv(int addr, void *buf, long n)
+{
+	Ctlr *ctlr;
+	int p, s, flag;
+	BD *td;
+	long nr;
+
+	ctlr = i2ctlr;
+	if(n > MaxIO)
+		return -1;
+	ctlr->txbuf[0] = addr|Rbit;
+	if(addr & 1){	/* special select sequence */
+		ctlr->addr[0] = addr &~ 1;
+		ctlr->addr[1] = addr>>8;
+	}
+	DCFLUSH(ctlr->txbuf, Bufsize);
+	DCFLUSH(ctlr->rxbuf, Bufsize);
+	ctlr->phase = Recving;
+	ctlr->rd->addr = PADDR(ctlr->rxbuf);
+	ctlr->rd->status = BDEmpty|BDWrap|BDInt;
+	flag = 0;
+	td = ctlr->td;
+	td[1].status = 0;
+	if(addr & 1){
+		/* special select sequence */
+		td->addr = PADDR(ctlr->addr);
+		td->length = 2;
+		/* td->status made BDReady below */
+		td++;
+		flag = TxS;
+	}
+	td->addr = PADDR(ctlr->txbuf);
+	td->length = n+1;
+	td->status = BDReady|BDWrap|BDLast | flag;	/* not BDInt: leave that to receive */
+	if(flag)
+		ctlr->td->status = BDReady;
+	enable();
+	ctlr->i2c->i2com = STR|I2CM;
+	eieio();
+	i2cwait(ctlr);
+	disable();
+	p = ctlr->phase;
+	s = ctlr->td->status;
+	if(flag)
+		s |= ctlr->td[1].status;
+	nr = ctlr->rd->length;
+	if(nr > n)
+		nr = n;	/* shouldn't happen */
+	if(s & TxERR || s & BDReady || p != Done || ctlr->rd->status & BDEmpty || ctlr->timeout)
+		return -1;
+	memmove(buf, ctlr->rxbuf, nr);
+	return nr;
+}
--- /dev/null
+++ b/os/boot/rpcg/initfads.c
@@ -1,0 +1,187 @@
+/*
+ * Called from l.s in EPROM to set up a minimal working environment.
+ * Since there is no DRAM yet, and therefore no stack, no function
+ * calls may be made from sysinit0, and values can't be stored,
+ * except to INTMEM.  Global values are accessed by offset from SB,
+ * which has been set by l.s to point into EPROM.
+ *
+ * This is FADS-specific in CS assignment and access of the FADS BCSR
+ * to discover memory size and speed.
+ */
+
+#include "u.h"
+#include "lib.h"
+#include "mem.h"
+#include "dat.h"
+#include "fns.h"
+#include "io.h"
+
+#include "archfads.h"
+
+#define	MB	(1024*1024)
+
+enum {
+	UPMSIZE = 64,	/* memory controller instruction RAM */
+	SPEED = 50,	/* maximum memory clock in MHz */
+	SDRAMSIZE = 4*MB,
+
+	/* mcr */
+	WriteRAM = 0<<30,
+	ReadRAM = 1<<30,
+	ExecRAM = 2<<30,
+
+	SelUPMA = 0<<23,
+	SelUPMB = 1<<23,
+
+	Once = 1<<8,
+};
+
+/*
+ * mpc8bug uses the following for 60ns EDO DRAMs 32-50MHz
+ */
+static ulong upma50[UPMSIZE] = {
+	0x8FFFEC24,	0xFFFEC04,	0xCFFEC04,	0xFFEC04,       
+	0xFFEC00,	0x37FFEC47,	0xFFFFFFFF,	0xFFFFFFFF,
+	0x8FFFEC24,	0xFFFEC04,	0x8FFEC04,	0xFFEC0C,
+	0x3FFEC00,	0xFFEC44,	0xFFCC08,	0xCFFCC44,
+	0xFFEC0C,	0x3FFEC00,	0xFFEC44,	0xFFCC00,
+	0x3FFFC847,	0x3FFFEC47,	0xFFFFFFFF,	0xFFFFFFFF,
+	0x8FAFCC24,	0xFAFCC04,	0xCAFCC00,	0x11BFCC47,
+	0xC0FFCC84,	0xFFFFFFFF,	0xFFFFFFFF,	0xFFFFFFFF,
+	0x8FAFCC24,	0xFAFCC04,	0xCAFCC00,	0x3AFCC4C,
+	0xCAFCC00,	0x3AFCC4C,	0xCAFCC00,	0x3AFCC4C,
+	0xCAFCC00,	0x33BFCC4F,	0xFFFFFFFF,	0xFFFFFFFF,
+	0xFFFFFFFF,	0xFFFFFFFF,	0xFFFFFFFF,	0xFFFFFFFF,
+	0xC0FFCC84,	0xFFCC04,	0x7FFCC04,	0x3FFFCC06,
+	0xFFFFCC85,	0xFFFFCC05,	0xFFFFCC05,	0xFFFFFFFF,
+	0xFFFFFFFF,	0xFFFFFFFF,	0xFFFFFFFF,	0xFFFFFFFF,
+	0x33FFCC07,	0xFFFFFFFF,	0xFFFFFFFF,	0xFFFFFFFF,
+};
+
+/*
+ * the FADS manual table 3-7 suggests the following for 60ns EDO DRAMs at 20MHz
+ */
+static ulong upma20[UPMSIZE] = {
+	0x8FFFCC04, 0x08FFCC00, 0x33FFCC47, ~0, ~0, ~0, ~0, ~0,
+	[0x08]	0x8FFFCC04, 0x08FFCC08, 0x08FFCC08, 0x08FFCC08, 0x08FFCC00, 0x3FFFCC47, ~0, ~0, ~0, ~0, ~0, ~0, ~0, ~0, ~0, ~0,
+	[0x18]	0x8FEFCC00, 0x39BFCC47, ~0, ~0, ~0, ~0, ~0, ~0,
+	[0x20]	0x8FEFCC00, 0x09AFCC48, 0x09AFCC48, 0x08AFCC48, 0x39BFCC47, ~0, ~0, ~0, ~0, ~0, ~0, ~0, ~0, ~0, ~0, ~0,
+	[0x30]	0x80FFCC84, 0x17FFCC04, 0xFFFFCC86, 0xFFFFCC05, ~0, ~0, ~0, ~0, ~0, ~0, ~0, ~0,
+	[0x3C]	0x33FFCC07, ~0, ~0, ~0,
+};
+
+void
+sysinit0(int inrom)
+{
+	ulong *upm, *bcsr;
+	IMM *io;
+	int i, mb;
+
+	io = (IMM*)INTMEM;		/* running before maps, no KADDR */
+
+	/* system interface unit initialisation, FADS manual table 3-2, except as noted */
+	io->siumcr = 0x01012440;
+	io->sypcr = 0xFFFFFF88;
+	io->tbscrk = KEEP_ALIVE_KEY;
+	io->tbscr = 0xC3;	/* time base enabled */
+	io->rtcsck = KEEP_ALIVE_KEY;
+	io->rtcsc = 0xC1;	/* don't FRZ, real-time clock enabled */
+	io->rtcsck = ~KEEP_ALIVE_KEY;
+	io->piscrk = KEEP_ALIVE_KEY;
+	io->piscr = 0x82;
+
+	io->memc[BCSRCS].option = 0xFFFF8110;	/* 32k block, all types access, CS early negate, 1 ws */
+	io->memc[BCSRCS].base = BCSRMEM | 1;	/* base, 32-bit port, no parity, GPCM */
+
+	io->memc[BOOTCS].base = FLASHMEM | 1;
+	io->memc[BOOTCS].option = 0xFF800D54;
+
+	if(!inrom)
+		return;	/* can't initialise DRAM controller from DRAM */
+
+	bcsr = (ulong*)BCSRMEM;
+//	bcsr[1] &= ~DisableDRAM;
+	/* could check DRAM speed here; assume 60ns */
+	switch((bcsr[2]>>23)&3){
+	default:	return;	/* can't happen; for the compiler */
+	case 0:	mb = 4; break;
+	case 1:	mb = 32; break;
+	case 2:	mb = 16; break;
+	case 3:	mb = 8; break;
+	}
+
+	upm = upma50;
+	for(i=0; i<UPMSIZE; i++){
+		io->mdr = upm[i];
+		io->mcr = WriteRAM | SelUPMA | i;
+	}
+	io->mptpr = 0x0400;
+	if(SPEED >= 32)
+		io->mamr = (0x9C<<24) | 0xA21114;	/* 50MHz BRGCLK; FADS manual says 0xC0, mpc8bug sets 0x9C */
+	else if(SPEED >= 20)
+		io->mamr = (0x60<<24) | 0xA21114;	/* 25MHz BRGCLK */
+	else
+		io->mamr = (0x40<<24) | 0xA21114;	/* 16.67MHz BRGCLK */
+	io->memc[DRAM1].option = ~((mb<<20)-1)|0x0800;	/* address mask, SAM=1 */
+	io->memc[DRAM1].base = 0 | 0x81;	/* base at 0, 32-bit port size, no parity, UPMA */
+}
+
+/*
+ * the FADS manual table 3-9's suggestion for MB811171622A-100 32+MHz-50MHz
+ */
+static ulong upmb50[UPMSIZE] = {
+	[0x00]	0x1F07FC04, 0xEEAEFC04, 0x11ADFC04, 0xEFBBBC00, 0x1FF77C47,
+	[0x05]	0x1FF77C34, 0xEFEABC34, 0x1FB57C35,
+	[0x08]	0x1F07FC04, 0xEEAEFC04, 0x10ADFC04, 0xF0AFFC00, 0xF0AFFC00, 0xF1AFFC00, 0xEFBBBC00, 0x1FF77C47, ~0, ~0, ~0, ~0, ~0, ~0, ~0, ~0,
+	[0x18]	0x1F27FC04, 0xEEAEBC00, 0x01B93C04, 0x1FF77C47, ~0, ~0, ~0, ~0,
+	[0x20]	0x1F07FC04, 0xEEAEBC00, 0x10AD7C00, 0xF0AFFC00, 0xF0AFFC00, 0xE1BBBC04, 0x1FF77C47, ~0, ~0, ~0, ~0, ~0, ~0, ~0, ~0, ~0,
+	[0x30] 	0x1FF5FC84, 0xFFFFFC04, 0xFFFFFC04, 0xFFFFFC04, 0xFFFFFC84, 0xFFFFFC07, ~0, ~0, ~0, ~0, ~0, ~0,
+	[0x3C]	0x7FFFFC07, ~0, ~0, ~0,
+};
+
+/*
+ * the FADS manual table 3-8's suggestion for MB811171622A-100 up to 32MHz
+ */
+static	ulong	upmb32[UPMSIZE] = {
+	[0x00]	0x126CC04, 0xFB98C00, 0x1FF74C45, ~0, ~0,
+	[0x05]	0x1FE77C34, 0xEFAABC34, 0x1FA57C35,
+	[0x08]	0x0026FC04, 0x10ADFC00, 0xF0AFFC00, 0xF1AFFC00, 0xEFBBBC00, 0x1FF77C45, ~0, ~0, ~0, ~0, ~0, ~0, ~0, ~0, ~0, ~0,
+	[0x18]	0x0E26BC04, 0x01B93C00, 0x1FF77C45, ~0, ~0, ~0, ~0, ~0,
+	[0x20]	0x0E26BC00, 0x10AD7C00, 0xF0AFFC00, 0xF0AFFC00, 0xE1BBBC04, 0x1FF77C45, ~0, ~0, ~0, ~0, ~0, ~0, ~0, ~0, ~0, ~0,
+	[0x30]	0x1FF5FC84, 0xFFFFFC04, 0xFFFFFC84, 0xFFFFFC05, ~0, ~0, ~0, ~0, ~0, ~0, ~0, ~0,
+	[0x3C]	0x7FFFFC07, ~0, ~0, ~0,
+};
+
+/*
+ * optionally called by archfads.c:/^archinit to initialise access to SDRAM
+ */
+void
+sdraminit(ulong base)
+{
+	ulong *upm;
+	IMM *io;
+	int i;
+
+	io = (IMM*)INTMEM;		/* running before maps, no KADDR */
+	if(SPEED > 32)
+		upm = upmb50;
+	else
+		upm = upmb32;
+	for(i=0; i<UPMSIZE; i++){
+		io->mdr = upm[i];
+		io->mcr = WriteRAM | SelUPMB | i;
+	}
+	io->memc[SDRAM].option = ~(SDRAMSIZE-1)|0x0A00;	/* address mask, SAM=1, G5LS=1 */
+	io->memc[SDRAM].base = base | 0xC1;
+	if(SPEED > 32){
+		io->mbmr = 0xD0802114;	/* 50MHz BRGCLK */
+		io->mar = 0x88;
+	}else{
+		io->mbmr = 0x80802114;	/* 32MHz BRGCLK */
+		io->mar = 0x48;
+	}
+	io->mcr = ExecRAM | SelUPMB | (SDRAM<<13) | Once | 5;	/* run MRS command in locations 5-8 of UPMB */
+	io->mbmr = (io->mbmr & ~0xF) | 8;
+	io->mcr = ExecRAM | SelUPMB | (SDRAM<<13) | Once | 0x30;	/* run refresh sequence */
+	io->mbmr = (io->mbmr & ~0xF) | 4;	/* 4-beat refresh bursts */
+}
--- /dev/null
+++ b/os/boot/rpcg/initpaq.c
@@ -1,0 +1,101 @@
+/*
+ * Called from l.s in EPROM to set up a minimal working environment.
+ * Since there is no DRAM yet, and therefore no stack, no function
+ * calls may be made from sysinit0, and values can't be stored,
+ * except to INTMEM.  Global values are accessed by offset from SB,
+ * which has been set by l.s to point into EPROM.
+ *
+ * This is PowerPAQ-specific:
+ *	- assumes 8mbytes
+ *	- powerpaq CS assignment
+ */
+
+#include "u.h"
+#include "lib.h"
+#include "mem.h"
+#include "dat.h"
+#include "fns.h"
+#include "io.h"
+
+#include "archpaq.h"
+
+#define	MB	(1024*1024)
+
+enum {
+	DRAMSIZE = 8*MB,
+	FLASHSIZE = 8*MB,
+
+	UPMSIZE = 64,	/* memory controller instruction RAM */
+	SPEED = 50,	/* maximum memory clock in MHz */
+
+	/* mcr */
+	WriteRAM = 0<<30,
+	ReadRAM = 1<<30,
+	ExecRAM = 2<<30,
+
+	SelUPMA = 0<<23,
+	SelUPMB = 1<<23,
+
+	Once = 1<<8,
+};
+
+/*
+ * mpc8bug uses the following for 60ns EDO DRAMs 32-50MHz
+ */
+static ulong upmb50[UPMSIZE] = {
+	0x8FFFEC24,	0xFFFEC04,	0xCFFEC04,	0xFFEC04,       
+	0xFFEC00,	0x37FFEC47,	0xFFFFFFFF,	0xFFFFFFFF,
+	0x8FFFEC24,	0xFFFEC04,	0x8FFEC04,	0xFFEC0C,
+	0x3FFEC00,	0xFFEC44,	0xFFCC08,	0xCFFCC44,
+	0xFFEC0C,	0x3FFEC00,	0xFFEC44,	0xFFCC00,
+	0x3FFFC847,	0x3FFFEC47,	0xFFFFFFFF,	0xFFFFFFFF,
+	0x8FAFCC24,	0xFAFCC04,	0xCAFCC00,	0x11BFCC47,
+	0xC0FFCC84,	0xFFFFFFFF,	0xFFFFFFFF,	0xFFFFFFFF,
+	0x8FAFCC24,	0xFAFCC04,	0xCAFCC00,	0x3AFCC4C,
+	0xCAFCC00,	0x3AFCC4C,	0xCAFCC00,	0x3AFCC4C,
+	0xCAFCC00,	0x33BFCC4F,	0xFFFFFFFF,	0xFFFFFFFF,
+	0xFFFFFFFF,	0xFFFFFFFF,	0xFFFFFFFF,	0xFFFFFFFF,
+	0xC0FFCC84,	0xFFCC04,	0x7FFCC04,	0x3FFFCC06,
+	0xFFFFCC85,	0xFFFFCC05,	0xFFFFCC05,	0xFFFFFFFF,
+	0xFFFFFFFF,	0xFFFFFFFF,	0xFFFFFFFF,	0xFFFFFFFF,
+	0x33FFCC07,	0xFFFFFFFF,	0xFFFFFFFF,	0xFFFFFFFF,
+};
+
+void
+sysinit0(int inrom)
+{
+	ulong *upm;
+	IMM *io;
+	int i;
+
+	io = (IMM*)INTMEM;		/* running before maps, no KADDR */
+
+	/* system interface unit initialisation, FADS manual table 3-2, except as noted */
+	io->siumcr = 0x01012440;
+	io->sypcr = 0xFFFFFF88;
+	io->tbscrk = KEEP_ALIVE_KEY;
+	io->tbscr = 0xC3;	/* time base enabled */
+	io->rtcsck = KEEP_ALIVE_KEY;
+	io->rtcsc = 0xC1;	/* don't FRZ, real-time clock enabled */
+	io->rtcsck = ~KEEP_ALIVE_KEY;
+	io->piscrk = KEEP_ALIVE_KEY;
+	io->piscr = 0x82;
+
+	io->memc[BOOTCS].base = FLASHMEM | 1;
+	io->memc[BOOTCS].option = ~(FLASHSIZE-1)|(1<<8)|(2<<4);	/* mask, BIH, 2 wait states */
+
+	if(!inrom)
+		return;	/* can't initialise DRAM controller from DRAM */
+
+	/* could check DRAM speed here; assume 60ns */
+	/* could probe DRAM for size here; assume DRAMSIZE */
+	io->mptpr = 0x400;	/* powerpaq flash has 0x1000 */
+	io->mbmr = (0xC0<<24) | 0xA21114;	/* 50MHz BRGCLK */
+	upm = upmb50;
+	for(i=0; i<UPMSIZE; i++){
+		io->mdr = upm[i];
+		io->mcr = WriteRAM | SelUPMB | i;
+	}
+	io->memc[DRAM1].option = ~(DRAMSIZE-1)|0x0800;	/* address mask, SAM=1 */
+	io->memc[DRAM1].base = 0 | 0xC1;	/* base at 0, 32-bit port size, no parity, UPMB */
+}
--- /dev/null
+++ b/os/boot/rpcg/initrpcg.c
@@ -1,0 +1,91 @@
+
+/*
+ * Called from l.s in EPROM to set up a minimal working environment.
+ * Since there is no DRAM yet, and therefore no stack, no function
+ * calls may be made from sysinit, and values can't be stored,
+ * except to INTMEM.  Global values are accessed by offset from SB,
+ * which has been set by l.s to point into EPROM.
+ */
+
+#include "u.h"
+#include "lib.h"
+#include "mem.h"
+#include "dat.h"
+#include "fns.h"
+#include "io.h"
+
+#include	"archrpcg.h"
+
+#define	MB	(1024*1024)
+
+enum {
+	UPMSIZE = 64,	/* memory controller instruction RAM */
+	DRAMSIZE = 16*MB,
+	FLASHSIZE = 4*MB,
+
+	WriteRAM = 0<<30,
+	ReadRAM = 1<<30,
+	ExecRAM = 2<<30,
+
+	SelUPMA = 0<<23,
+	SelUPMB = 1<<23,
+};
+/* RPCG values for RPXLite AW */
+static	ulong	upma50[UPMSIZE] = {
+	0xCFFFCC24,	0x0FFFCC04,	0x0CAFCC04,	0x03AFCC08,       
+	0x3FBFCC27,	0xFFFFCC25,	0xFFFFCC25,	0xFFFFCC25,
+	0xCFFFCC24,	0x0FFFCC04,	0x0CAFCC84,	0x03AFCC88,
+	0x3FBFCC27,	0xFFFFCC25,	0xFFFFCC25,	0xFFFFCC25,
+	0xFFFFCC25,	0xFFFFCC25,	0xFFFFCC25,	0xFFFFCC25,
+	0xFFFFCC25,	0xFFFFCC25,	0xFFFFCC25,	0xFFFFCC25,
+	0xCFFFCC24,	0x0FFFCC04,	0x0CFFCC04,	0x03FFCC00,
+	0x3FFFCC27,	0xFFFFCC25,	0xFFFFCC25,	0xFFFFCC25,
+	0xCFFFCC24,	0x0FFFCC04,	0x0CFFCC84,	0x03FFCC84,
+	0x0CFFCC00,	0x33FFCC27,	0xFFFFCC25,	0xFFFFCC25,
+	0xFFFFCC25,	0xFFFFCC25,	0xFFFFCC25,	0xFFFFCC25,
+	0xFFFFCC25,	0xFFFFCC25,	0xFFFFCC25,	0xFFFFCC25,
+	0xC0FFCC24,	0x03FFCC24,	0x0FFFCC24,	0x0FFFCC24,
+	0x3FFFCC27,	0xFFFFCC25,	0xFFFFCC25,	0xFFFFCC25,
+	0xFFFFCC25,	0xFFFFCC25,	0xFFFFCC25,	0xFFFFCC25,
+	0xFFFFCC25,	0xFFFFCC25,	0xFFFFCC25,	0xFFFFCC25,
+};
+
+void
+sysinit0(int inrom)
+{
+	ulong *upm;
+	IMM *io;
+	int i;
+
+	io = (IMM*)INTMEM;		/* running before maps, no KADDR */
+//	io->siumcr = 0x01012440;
+//	io->sypcr = 0xFFFFFF88;
+	io->tbscrk = KEEP_ALIVE_KEY;
+	io->tbscr = 0xC3;
+	io->rtcsck = KEEP_ALIVE_KEY;
+	io->rtcsc = 0xC1;
+	io->rtcsck = ~KEEP_ALIVE_KEY;
+	io->piscrk = KEEP_ALIVE_KEY;
+	io->piscr = 0x82;
+return;
+	io->memc[BCSRCS].option = 0xFFFF8910;	/* 32k block, all types access, CSNT, CS early negate, burst inhibit, 1 ws */
+	io->memc[BCSRCS].base = BCSRMEM | 1;	/* base, 32-bit port, no parity, GPCM */
+
+//	io->memc[BOOTCS].base = FLASHMEM | 0x801; /* base, 16 bit port */
+//	io->memc[BOOTCS].option = ~(FLASHSIZE-1)|(1<<8)|(4<<4);	/* mask, BIH, 4 wait states */
+
+	if(1||!inrom)
+		return;	/* can't initialise DRAM controller from DRAM */
+
+	/* TO DO: could check DRAM size and speed now */
+
+	upm = upma50;
+	for(i=0; i<nelem(upma50); i++){
+		io->mdr = upm[i];
+		io->mcr = WriteRAM | SelUPMA | i;
+	}
+	io->mptpr = 0x0800;	/* divide by 8 */
+	io->mamr = (0x58<<24) | 0xA01430;	/* 40MHz BRGCLK */
+	io->memc[DRAM1].option = ~(DRAMSIZE-1)|0x0E00;	/* address mask, SAM=1, G5LA/S=3 */
+	io->memc[DRAM1].base = 0 | 0x81;	/* base at 0, 32-bit port size, no parity, UPMA */
+}
--- /dev/null
+++ b/os/boot/rpcg/io.h
@@ -1,0 +1,463 @@
+enum
+{
+	/* software interrupt vectors (SIU and CPM) */
+	VectorPIC= 0,	/* level 0 to level 7, assigned by software */
+		CPIClevel=	4,
+	VectorIRQ=	VectorPIC+8,	/* IRQ0 to IRQ7 */
+	VectorCPIC=	VectorIRQ+8,	/* 32 CPM interrupts: 0 (error) to 0x1F (PC15) */
+};
+
+enum
+{
+	BUSUNKNOWN = 0,
+};
+
+/*
+ * Buffer Descriptors and IO Rings
+ */
+
+typedef struct BD BD;
+struct BD {
+	ushort	status;
+	ushort	length;
+	ulong	addr;
+};
+
+BD*	bdalloc(int);
+void	bdfree(BD*, int);
+
+enum {
+	/* Rx BDs, bits common to all protocols */
+	BDEmpty=	1<<15,
+	BDWrap=		1<<13,
+	BDInt=		1<<12,
+	BDLast=		1<<11,
+	BDFirst=		1<<10,
+
+	/* Tx BDs */
+	BDReady=		1<<15,
+	/* BDWrap, BDInt, BDLast */
+};
+
+typedef struct Ring Ring;
+struct Ring {
+	BD*	rdr;				/* receive descriptor ring */
+	void*	rrb;				/* receive ring buffers */
+	int	rdrx;				/* index into rdr */
+	int	nrdre;			/* length of rdr */
+
+	BD*	tdr;				/* transmit descriptor ring */
+	Block**	txb;				/* corresponding transmit ring buffers */
+	int	tdrh;				/* host index into tdr */
+	int	tdri;				/* interface index into tdr */
+	int	ntdre;			/* length of tdr */
+	int	ntq;				/* pending transmit requests */
+};
+
+#define NEXT(x, l)	(((x)+1)%(l))
+#define PREV(x, l)	(((x) == 0) ? (l)-1: (x)-1)
+#define	HOWMANY(x, y)	(((x)+((y)-1))/(y))
+#define ROUNDUP(x, y)	(HOWMANY((x), (y))*(y))
+
+int	ioringinit(Ring*, int, int, int);
+
+/*
+ * CPM
+ */
+enum {
+	/* commands */
+	InitRxTx =	0,
+	InitRx =		1,
+	InitTx =		2,
+	EnterHunt=	3,
+	StopTx=		4,
+	GracefulStopTx = 5,
+	InitIDMA =	5,
+	RestartTx =	6,
+	CloseRxBD =	7,
+	SetGroupAddr = 8,
+	SetTimer =	8,
+	GCITimeout =	9,
+	GCIAbort =	10,
+	StopIDMA =	11,
+	StartDSP = 	12,
+	ArmIDMA =	13,
+	InitDSP =		13,
+	USBCmd =	15,
+
+	/* channel IDs */
+	SCC1ID=	0,
+	USBID=	0,
+	I2CID=	1,
+	IDMA1ID= 1,
+	SCC2ID=	4,
+	SPIID=	5,
+	IDMA2ID= 5,
+	TIMERID=	5,
+	SCC3ID=	8,
+	SMC1ID=	9,
+	DSP1ID=9,
+	SCC4ID=	12,
+	SMC2ID=	13,
+	DSP2ID=	13,
+
+	BaudEnable = 1<<16,
+
+	/* sicr */
+	CLK1 = 4,		/* SCC1,2 */
+	CLK2 = 5,
+	CLK3 = 6,
+	CLK4 = 7,
+	CLK5 = CLK1,	/* SCC3,4 */
+	CLK6 = CLK2,
+	CLK7 = CLK3,
+	CLK8 = CLK4,
+};
+
+void	cpmop(int, int, int);
+#define	ioplock()	(m->iomem)
+#define	iopunlock()
+
+/*
+ * the structures below follow hardware/firmware layouts in the 8xx manuals:
+ * mind the data types, offsets and alignment
+ */
+
+/*
+ * basic IO controller parameters (SMC and SCC)
+ */
+typedef struct IOCparam IOCparam;
+struct IOCparam {
+	ushort	rbase;
+	ushort	tbase;
+	uchar	rfcr;
+	uchar	tfcr;
+	ushort	mrblr;
+	ulong	rstate;
+	ulong	rptr;
+	ushort	rbptr;
+	ushort	rcnt;
+	ulong	rtmp;
+	ulong	tstate;
+	ulong	tptr;
+	ushort	tbptr;
+	ushort	tcnt;
+	ulong	ttmp;
+};
+
+typedef struct SCCparam SCCparam;
+struct SCCparam {
+	IOCparam;
+	ulong	rcrc;
+	ulong	tcrc;
+};
+
+typedef struct SCC SCC;
+struct SCC {
+	ulong	gsmrl;
+	ulong	gsmrh;
+	ushort	psmr;
+	uchar	rsvscc0[2];
+	ushort	todr;
+	ushort	dsr;
+	ushort	scce;
+	uchar	rsvscc1[2];
+	ushort	sccm;
+	uchar	rsvscc3;
+	uchar	sccs;
+	ushort	irmode;
+	ushort	irsip;
+};
+
+typedef struct SMC SMC;
+struct SMC {
+	uchar	pad1[2];
+	ushort	smcmr;
+	uchar	pad2[2];
+	uchar	smce;
+	uchar	pad3[3];
+	uchar	smcm;
+	uchar	pad4[5];
+};
+
+typedef struct SPI SPI;
+struct SPI {
+	ushort	spmode;
+	uchar	res1[4];
+	uchar	spie;
+	uchar	res2[3];
+	uchar	spim;
+	uchar	res3[2];
+	uchar	spcom;
+	uchar	res4[10];
+};
+
+typedef struct USB USB;
+struct USB {	/* 823 only */
+	uchar	usmod;
+	uchar	usadr;
+	uchar	uscom;
+	uchar	rsvu1;
+	ushort	usep[4];
+	uchar	rsvu2[4];
+	ushort	usber;
+	uchar	rsvu3[2];
+	ushort	usbmr;
+	uchar	rsvu4;
+	uchar	usbs;
+	uchar	rsvu5[8];
+};
+
+typedef struct IMM IMM;
+struct IMM {
+	struct {	/* general SIU */
+		ulong	siumcr;
+		ulong	sypcr;
+		uchar	rsv0[0xE-0x8];
+		ushort	swsr;
+		ulong	sipend;
+		ulong	simask;
+		ulong	siel;
+		uchar	sivec;
+		uchar	padv[3];
+		ulong	tesr;
+		uchar	rsv1[0x30-0x24];
+		ulong	sdcr;
+		uchar	rsv2[0x80-0x34];
+	};
+	struct {	/* PCMCIA */
+		struct {
+			ulong	base;
+			ulong	option;
+		} pcmr[8];
+		uchar	rsv3[0xe0-0xc0];
+		ulong	pgcra;
+		ulong	pgcrb;
+		ulong	pscr;
+		uchar	rsv4[0xf0-0xec];
+		ulong	pipr;
+		uchar	rsv5[4];
+		ulong	per;
+		uchar	rsv6[4];
+	};
+	struct {	/* MEMC */
+		struct {
+			ulong	base;
+			ulong	option;
+		} memc[8];
+		uchar	rsv7a[0x24];
+		ulong	mar;
+		ulong	mcr;
+		uchar	rsv7b[4];
+		ulong	mamr;
+		ulong	mbmr;
+		ushort	mstat;
+		ushort	mptpr;
+		ulong	mdr;
+		uchar	rsv7c[0x80];
+	};
+	struct {	/* system integration timers */
+		ushort	tbscr;
+		uchar	rsv8a[2];
+		ulong	tbrefu;
+		ulong	tbrefl;
+		uchar	rsv8b[0x14];
+		ushort	rtcsc;
+		uchar	rsv8c[2];
+		ulong	rtc;
+		ulong	rtsec;
+		ulong	rtcal;
+		uchar	rsv8d[0x10];
+		ushort	piscr;
+		ushort	rsv8e;
+		ulong	pitc;
+		ulong	pitr;
+		uchar	rsv8f[0x34];
+	};
+	struct {	/* 280: clocks and resets */
+		ulong	sccr;
+		ulong	plprcr;
+		ulong	rsr;
+		uchar	rsv9[0x300-0x28c];
+	};
+	struct {	/* 300: system integration timers keys */
+		ulong	tbscrk;
+		ulong	tbrefuk;
+		ulong	tbreflk;
+		ulong	tbk;
+		uchar	rsv10a[0x10];
+		ulong	rtcsck;
+		ulong	rtck;
+		ulong	rtseck;
+		ulong	rtcalk;
+		uchar	rsv10b[0x10];
+		ulong	piscrk;
+		ulong	pitck;
+		uchar	rsv10c[0x38];
+	};
+	struct {	/* 380: clocks and resets keys */
+		ulong	sccrk;
+		ulong	plprcrk;
+		ulong	rsrk;
+		uchar	rsv11[0x800-0x38C];
+	};
+	struct {	/* 800: video controller */
+		ushort	vccr;
+		ushort	pad11a;
+		uchar	vsr;
+		uchar	pad11b;
+		uchar	vcmr;
+		uchar	pad11c;
+		ulong	vbcb;
+		ulong	pad11d;
+		ulong	vfcr0;
+		ulong	vfaa0;
+		ulong	vfba0;
+		ulong	vfcr1;
+		ulong	vfaa1;
+		ulong	vfba1;
+		uchar	rsv11a[0x840-0x828];
+	};
+	struct {	/* 840: LCD */
+		ulong	lccr;
+		ulong	lchcr;
+		ulong	lcvcr;
+		ulong	rsv11b;
+		ulong	lcfaa;
+		ulong	lcfba;
+		uchar	lcsr;
+		uchar	rsv11c[0x860-0x859];
+	};
+	struct {	/* 860: I2C */
+		uchar	i2mod;
+		uchar	rsv12a[3];
+		uchar	i2add;
+		uchar	rsv12b[3];
+		uchar	i2brg;
+		uchar	rsv12c[3];
+		uchar	i2com;
+		uchar	rsv12d[3];
+		uchar	i2cer;
+		uchar	rsv12e[3];
+		uchar	i2cmr;
+		uchar	rsv12[0x900-0x875];
+	};
+	struct {	/* 900: DMA */
+		uchar	rsv13[4];
+		ulong	sdar;
+		uchar	sdsr;
+		uchar	pad1[3];
+		uchar	sdmr;
+		uchar	pad2[3];
+		uchar	idsr1;
+		uchar	pad3[3];
+		uchar	idmr1;
+		uchar	pad4[3];
+		uchar	idsr2;
+		uchar	pad5[3];
+		uchar	idmr2;
+		uchar	pad6[0x930-0x91D];
+	};
+	struct {	/* CPM interrupt control */
+		ushort	civr;
+		uchar	pad7[0x940-0x932];
+		ulong	cicr;
+		ulong	cipr;
+		ulong	cimr;
+		ulong	cisr;
+	};
+	struct {	/* input/output port */
+		ushort	padir;
+		ushort	papar;
+		ushort	paodr;
+		ushort	padat;
+		uchar	pad8[8];
+		ushort	pcdir;
+		ushort	pcpar;
+		ushort	pcso;
+		ushort	pcdat;
+		ushort	pcint;
+		uchar	pad9[6];
+		ushort	pddir;
+		ushort	pdpar;
+		ushort	rsv14a;
+		ushort	pddat;
+		uchar	rsv14[0x980-0x978];
+	};
+	struct {	/* CPM timers */
+		ushort	tgcr;
+		uchar	rsv15a[0x990-0x982];
+		ushort	tmr1;
+		ushort	tmr2;
+		ushort	trr1;
+		ushort	trr2;
+		ushort	tcr1;
+		ushort	tcr2;
+		ushort	tcn1;
+		ushort	tcn2;
+		ushort	tmr3;
+		ushort	tmr4;
+		ushort	trr3;
+		ushort	trr4;
+		ushort	tcr3;
+		ushort	tcr4;
+		ushort	tcn3;
+		ushort	tcn4;
+		ushort	ter1;
+		ushort	ter2;
+		ushort	ter3;
+		ushort	ter4;
+		uchar	rsv15[0x9C0-0x9B8];
+	};
+	struct {	/* CPM */
+		ushort	cpcr;
+		uchar	res0[2];
+		ushort	rccr;
+		uchar	res1;
+		uchar	rmds;
+		uchar	res2a[4];
+		ushort	rctr1;
+		ushort	rctr2;
+		ushort	rctr3;
+		ushort	rctr4;
+		uchar	res2[2];
+		ushort	rter;
+		uchar	res3[2];
+		ushort	rtmr;
+		uchar	rsv16[0x9F0-0x9DC];
+	};
+	union {	/* BRG */
+		struct {
+			ulong	brgc1;
+			ulong	brgc2;
+			ulong	brgc3;
+			ulong	brgc4;
+		};
+		ulong	brgc[4];
+	};
+	uchar	skip0[0xAB2-0xA00];	/* USB, SCC, SMC, SPI: address using cpmdev(CP...)->regs */
+	struct {	/* PIP */
+		ushort	pipc;		/* not 823 */
+		ushort	ptpr;		/* not 823 */
+		ulong	pbdir;
+		ulong	pbpar;
+		uchar	pad10[2];
+		ushort	pbodr;
+		ulong	pbdat;
+		uchar	pad11[0xAE0-0xAC8];
+	};
+	struct {	/* SI */
+		ulong	simode;
+		uchar	sigmr;
+		uchar	pad12;
+		uchar	sistr;
+		uchar	sicmr;
+		uchar	pad13[4];
+		ulong	sicr;
+		ulong	sirp;
+		uchar	pad14[0xB00-0xAF4];
+	};
+	ulong	vcram[64];
+	ushort	siram[256];
+	ushort	lcdmap[256];
+};
--- /dev/null
+++ b/os/boot/rpcg/ip.h
@@ -1,0 +1,98 @@
+typedef struct Udphdr Udphdr;
+struct Udphdr
+{
+	uchar	d[6];		/* Ethernet destination */
+	uchar	s[6];		/* Ethernet source */
+	uchar	type[2];	/* Ethernet packet type */
+
+	uchar	vihl;		/* Version and header length */
+	uchar	tos;		/* Type of service */
+	uchar	length[2];	/* packet length */
+	uchar	id[2];		/* Identification */
+	uchar	frag[2];	/* Fragment information */
+
+	/* Udp pseudo ip really starts here */
+	uchar	ttl;	
+	uchar	udpproto;	/* Protocol */
+	uchar	udpplen[2];	/* Header plus data length */
+	uchar	udpsrc[4];	/* Ip source */
+	uchar	udpdst[4];	/* Ip destination */
+	uchar	udpsport[2];	/* Source port */
+	uchar	udpdport[2];	/* Destination port */
+	uchar	udplen[2];	/* data length */
+	uchar	udpcksum[2];	/* Checksum */
+};
+
+typedef struct Etherhdr Etherhdr;
+struct Etherhdr
+{
+	uchar	d[6];
+	uchar	s[6];
+	uchar	type[2];
+
+	/* Now we have the ip fields */
+	uchar	vihl;		/* Version and header length */
+	uchar	tos;		/* Type of service */
+	uchar	length[2];	/* packet length */
+	uchar	id[2];		/* Identification */
+	uchar	frag[2];	/* Fragment information */
+	uchar	ttl;		/* Time to live */
+	uchar	proto;		/* Protocol */
+	uchar	cksum[2];	/* Header checksum */
+	uchar	src[4];		/* Ip source */
+	uchar	dst[4];		/* Ip destination */
+};
+
+enum
+{
+	IP_VER		= 0x40,
+	IP_HLEN		= 0x05,			
+ 	UDP_EHSIZE	= 22,
+	UDP_PHDRSIZE	= 12,
+	UDP_HDRSIZE	= 20,
+	ETHER_HDR	= 14,
+	IP_UDPPROTO	= 17,
+	ET_IP		= 0x800,
+	Bcastip		= 0xffffffff,
+	BPportsrc	= 68,
+	BPportdst	= 67,
+	TFTPport	= 69,
+	Timeout		= 5000,	/* milliseconds */
+	Bootrequest 	= 1,
+	Bootreply   	= 2,
+	Tftp_READ	= 1,
+	Tftp_WRITE	= 2,
+	Tftp_DATA	= 3,
+	Tftp_ACK	= 4,
+	Tftp_ERROR	= 5,
+	Segsize		= 512,
+	TFTPSZ		= Segsize+10,
+};
+
+typedef struct Bootp Bootp;
+struct Bootp
+{
+	uchar	op;		/* opcode */
+	uchar	htype;		/* hardware type */
+	uchar	hlen;		/* hardware address len */
+	uchar	hops;		/* hops */
+	uchar	xid[4];		/* a random number */
+	uchar	secs[2];	/* elapsed snce client started booting */
+	uchar	pad[2];
+	uchar	ciaddr[4];	/* client IP address (client tells server) */
+	uchar	yiaddr[4];	/* client IP address (server tells client) */
+	uchar	siaddr[4];	/* server IP address */
+	uchar	giaddr[4];	/* gateway IP address */
+	uchar	chaddr[16];	/* client hardware address */
+	char	sname[64];	/* server host name (optional) */
+	char	file[128];	/* boot file name */
+	char	vend[128];	/* vendor-specific goo */
+};
+
+typedef struct Netaddr Netaddr;
+struct Netaddr
+{
+	ulong	ip;
+	ushort	port;
+	char	ea[Eaddrlen];
+};
--- /dev/null
+++ b/os/boot/rpcg/l.s
@@ -1,0 +1,388 @@
+#include "mem.h"
+
+/* special instruction definitions */
+#define	BDNE	BC	0,2,
+#define	BDNZ	BC	16,0,
+#define	NOOP	OR	R0,R0,R0
+
+/*
+ * common ppc special purpose registers
+ */
+#define DSISR	18
+#define DAR	19	/* Data Address Register */
+#define DEC	22	/* Decrementer */
+#define SRR0	26	/* Saved Registers (exception) */
+#define SRR1	27
+#define SPRG0	272	/* Supervisor Private Registers */
+#define SPRG1	273
+#define SPRG2	274
+#define SPRG3	275
+#define TBRU	269	/* Time base Upper/Lower (Reading) */
+#define TBRL	268
+#define TBWU	285	/* Time base Upper/Lower (Writing) */
+#define TBWL	284
+#define PVR	287	/* Processor Version */
+
+/*
+ * mpc82x-specific special purpose registers of interest here
+ */
+#define EIE	80
+#define EID	81
+#define NRI	82
+#define IMMR	638
+#define IC_CST	560
+#define IC_ADR	561
+#define IC_DAT	562
+#define DC_CST	568
+#define DC_ADR	569
+#define DC_DAT	570
+#define MI_CTR	784
+#define MI_AP	786
+#define MI_EPN	787
+#define MI_TWC	789
+#define MI_RPN	790
+#define MI_DBCAM	816
+#define MI_DBRAM0	817
+#define MI_DBRAM1	818
+#define MD_CTR	792
+#define M_CASID	793
+#define MD_AP	794
+#define MD_EPN	795
+#define M_TWB	796
+#define MD_TWC	797
+#define MD_RPN	798
+#define	M_TW	799
+#define	MD_DBCAM	824
+#define	MD_DBRAM0	825
+#define	MD_DBRAM1	826
+
+/* as on 603e, apparently mtmsr needs help in some chip revisions */
+#define	WAITMSR	SYNC; ISYNC
+
+/* use of SPRG registers in save/restore */
+#define	SAVER0	SPRG0
+#define	SAVER1	SPRG1
+#define	SAVELR	SPRG2
+#define	SAVECR	SPRG3
+
+#define	UREGSIZE	((8+32)*4)
+#define	UREGSPACE	(UREGSIZE+8)	/* allow for arg to trap, and align */
+
+/*
+ * This code is loaded by the ROM loader at location 0x3000,
+ * or lives in flash memory at FLASHMEM+0x100
+ * Move it to high memory so that it can load the kernel at 0x0000.
+ */
+
+#define LOADCODEBASE	0x3000	/* when downloaded in S records */
+#define FLASHCODEBASE	(FLASHMEM+0x20000+0x100)	/* when in flash */
+
+	TEXT	start(SB), $-4
+	MOVW	MSR, R3
+	MOVW	$(EE|IP|RI), R4
+	ANDN	R4, R3
+	OR	$ME, R3
+	SYNC
+	MOVW	R3, MSR	/* turn off interrupts but enable traps */
+	WAITMSR
+
+/*
+ * reset the caches and disable them for now
+ */
+	MOVW	SPR(IC_CST), R4	/* read and clear */
+	MOVW	$(5<<25), R4
+	MOVW	R4, SPR(IC_CST)	/* unlock all */
+	ISYNC
+	MOVW	$(6<<25), R4
+	MOVW	R4, SPR(IC_CST)	/* invalidate all */
+	ISYNC
+	MOVW	$(2<<25), R4
+	MOVW	R4, SPR(IC_CST)	/* disable i-cache */
+	ISYNC
+
+	SYNC
+	MOVW	SPR(DC_CST), R4	/* read and clear */
+	MOVW	$(10<<24), R4
+	MOVW	R4, SPR(DC_CST)	/* unlock all */
+	ISYNC
+	MOVW	$(12<<24), R4
+	MOVW	R4, SPR(DC_CST)	/* invalidate all */
+	ISYNC
+	MOVW	$(4<<24), R4
+	MOVW	R4, SPR(DC_CST)	/* disable i-cache */
+	ISYNC
+
+	MOVW	$7, R4
+	MOVW	R4, SPR(158)		/* cancel `show cycle' for normal instruction execution */
+
+/*
+ * set other system configuration values
+ */
+	MOVW	SPR(IMMR), R5		/* save initial space pointer */
+	MOVW	$INTMEM, R4
+	MOVW	R4, SPR(IMMR)		/* set internal memory base */
+	MOVW	$0xFFFFFF88, R3
+	MOVW	R3, 4(R4)	/* disable watchdog in sypcr */
+	MOVW	$0x01012440, R3
+	MOVW	R3, 0(R4)	/* siumcr */
+
+/*
+ * system initialisation (init and map DRAM)
+ */
+	MOVW	$0, R0
+	MOVW	$setSB(SB), R2
+#ifndef confrpcg
+	MOVW	$(0xF000<<16), R3
+/*MOVW R0, R3*/
+	ANDCC	R5, R3	/* initial space is high? */
+	BEQ	notrom
+	MOVW	$FLASHCODEBASE, R5	/* where $start(SB) actually is now */
+	MOVW	$start(SB), R4	/* logical start address */
+	SUB	R4, R5, R6	/* text relocation value */
+	MOVW	$etext(SB), R7
+	SUB	R4, R7
+	ADD	R5, R7	/* data address in ROM */
+	MOVW	$bdata(SB), R8
+	SUB	R8, R2
+	ADD	R7, R2	/* relocate SB: SB' = romdata+(SB-bdata) */
+	MOVW	$sysinit0(SB), R4
+	ADD	R6, R4	/* relocate sysinit0's address */
+	MOVW	R4, CTR
+	MOVW	$inmem(SB), R4
+	ADD	R6, R4
+	MOVW	R4, LR	/* and the return address */
+	BR	(CTR)	/* call sysinit0 */
+	TEXT	inmem(SB), $-4
+	MOVW	$FLASHCODEBASE, R3
+	BR	cpu0
+notrom:
+	MOVW	$start(SB), R6
+	SUB	R6, R2
+	ADD	$LOADCODEBASE, R2
+	BL	sysinit0(SB)
+	MOVW	$LOADCODEBASE, R3
+#endif
+
+/*
+ * cpu 0
+ *	relocate bootstrap to our link addresses for text and data
+ *	set new PC
+ */
+cpu0:
+	MOVW	$setSB(SB), R2	/* set correct static base register */
+#ifndef confrpcg
+	MOVW	$start(SB), R4
+	MOVW	$etext(SB), R5
+	SUB	R4, R5
+	CMP	R4, R3	/* already there? */
+	BNE	copytext
+	ADD	R5, R3	/* start of data image */
+#else
+	MOVW	$etext(SB), R3
+#endif
+	BR	copydata
+
+copytext:
+	ADD	$3, R5
+	SRAW	$2, R5
+	MOVW	R5, CTR
+	SUB	$4, R4
+	SUB	$4, R3
+copyt:			/* copy text */
+	MOVWU	4(R3), R5
+	MOVWU	R5, 4(R4)
+	BDNZ	copyt
+	ADD	$4, R3
+
+copydata:
+	/* copy data */
+	MOVW	$bdata(SB), R4
+	CMP	R4, R3	/* already there? */
+	BEQ	loadkpc
+	MOVW	$edata(SB), R5
+	SUB	R4, R5
+	ADD	$3, R5
+	SRAW	$2, R5
+	MOVW	R5, CTR
+	SUB	$4, R4
+	SUB	$4, R3
+copyd:
+	MOVWU	4(R3), R5
+	MOVWU	R5, 4(R4)
+	BDNZ	copyd
+#endif
+
+	/* load correct PC */
+loadkpc:
+	MOVW	$start1(SB), R3
+	MOVW	R3, LR
+	BR	(LR)
+TEXT start1(SB), $-4
+	MOVW	$edata(SB), R3
+	MOVW	$end(SB), R4
+	SUBCC	R3, R4
+	BLE	skipz
+	SRAW	$2, R4
+	MOVW	R4, CTR
+	SUB	$4, R3
+	MOVW	$0, R0
+zero:
+	MOVWU	R0, 4(R3)
+	BDNZ	zero
+skipz:
+	MOVW	$mach0(SB), R1
+	MOVW	R1, m(SB)
+	ADD	$(MACHSIZE-8), R1
+	MOVW	$0, R0
+
+	BL	sysinit0(SB)
+
+	BL	main(SB)
+	BR	0(PC)
+
+TEXT ledx(SB), $0
+
+	MOVW	$BCSRMEM, R8
+	MOVW	0(R8), R3
+	MOVW	$(0x0800<<16), R5
+	ANDN	R5, R3, R3
+	MOVW	R3, 0(R8)
+	BR	0(PC)
+
+TEXT	getmsr(SB), $0
+	MOVW	MSR, R3
+	RETURN
+
+TEXT	putmsr(SB), $0
+	SYNC
+	MOVW	R3, MSR
+	WAITMSR
+	RETURN
+
+TEXT	eieio(SB), $0
+	EIEIO
+	RETURN
+
+TEXT	idle(SB), $0
+	RETURN
+
+TEXT	spllo(SB), $0
+	MOVW	MSR, R3
+	OR	$EE, R3, R4
+	SYNC
+	MOVW	R4, MSR
+	WAITMSR
+	RETURN
+
+TEXT	splhi(SB), $0
+	MOVW	MSR, R3
+	RLWNM	$0, R3, $~EE, R4
+	SYNC
+	MOVW	R4, MSR
+	WAITMSR
+	RETURN
+
+TEXT	splx(SB), $0
+	MOVW	MSR, R4
+	RLWMI	$0, R3, $EE, R4
+	SYNC
+	MOVW	R4, MSR
+	WAITMSR
+	RETURN
+
+TEXT	gettbl(SB), $0
+/*	MOVW	SPR(TBRL), R3	*/
+	WORD	$0x7c6c42e6	/* mftbl on 8xx series */
+	RETURN
+
+TEXT	getpvr(SB), $0
+	MOVW	SPR(PVR), R3
+	RETURN
+
+TEXT	getimmr(SB), $0
+	MOVW	SPR(IMMR), R3
+	RETURN
+
+TEXT	getdec(SB), $0
+	MOVW	SPR(DEC), R3
+	RETURN
+
+TEXT	putdec(SB), $0
+	MOVW	R3, SPR(DEC)
+	RETURN
+
+/*
+ * save state in Ureg on kernel stack.
+ * enter with R0 giving the PC from the call to `exception' from the vector.
+ * on return, SB (R2) has been set, and R3 has the Ureg*
+ */
+TEXT saveureg(SB), $-4
+	SUB	$UREGSPACE, R1
+	MOVMW	R2, 48(R1)	/* r2:r31 */
+	MOVW	$setSB(SB), R2
+	MOVW	SPR(SAVER1), R4
+	MOVW	R4, 44(R1)
+	MOVW	SPR(SAVER0), R5
+	MOVW	R5, 40(R1)
+	MOVW	CTR, R6
+	MOVW	R6, 36(R1)
+	MOVW	XER, R4
+	MOVW	R4, 32(R1)
+	MOVW	SPR(SAVECR), R5	/* CR */
+	MOVW	R5, 28(R1)
+	MOVW	SPR(SAVELR), R6	/* LR */
+	MOVW	R6, 24(R1)
+	/* pad at 20(R1) */
+	MOVW	SPR(SRR0), R4
+	MOVW	R4, 16(R1)	/* old PC */
+	MOVW	SPR(SRR1), R5
+	MOVW	R5, 12(R1)
+	MOVW	R0, 8(R1)	/* cause/vector, encoded in LR from vector */
+	ADD	$8, R1, R3	/* Ureg* */
+	STWCCC	R3, (R1)	/* break any pending reservations */
+	MOVW	$0, R0	/* R0ISZERO */
+	BR	(LR)
+
+/*
+ * restore state from Ureg
+ * SB (R2) is unusable on return
+ */
+TEXT restoreureg(SB), $-4
+	MOVMW	48(R1), R2	/* r2:r31 */
+	/* defer R1 */
+	MOVW	40(R1), R0
+	MOVW	R0, SPR(SAVER0)
+	MOVW	36(R1), R0
+	MOVW	R0, CTR
+	MOVW	32(R1), R0
+	MOVW	R0, XER
+	MOVW	28(R1), R0
+	MOVW	R0, CR	/* CR */
+	MOVW	24(R1), R0
+	MOVW	R0, SPR(SAVELR)	/* LR */
+	/* pad, skip */
+	MOVW	16(R1), R0
+	MOVW	R0, SPR(SRR0)	/* old PC */
+	MOVW	12(R1), R0
+	MOVW	R0, SPR(SRR1)	/* old MSR */
+	/* cause, skip */
+	MOVW	44(R1), R1	/* old SP */
+	BR	(LR)
+
+TEXT	exception(SB), $-4
+	MOVW	R1, SPR(SAVER1)
+	MOVW	CR, R0
+	MOVW	R0, SPR(SAVECR)
+	MOVW	LR, R0
+	BL	saveureg(SB)
+	MOVW	$0, R0
+	BL	trap(SB)
+	BL	restoreureg(SB)
+	MOVW	SPR(SAVELR), R0
+	MOVW	R0, LR
+	MOVW	SPR(SAVER0), R0
+	ISYNC
+	RFI
+
+GLOBL	mach0+0(SB), $MACHSIZE
+GLOBL	m(SB), $4
--- /dev/null
+++ b/os/boot/rpcg/lib.h
@@ -1,0 +1,106 @@
+/*
+ * functions (possibly) linked in, complete, from libc.
+ */
+
+/*
+ * mem routines
+ */
+extern	void*	memccpy(void*, void*, int, long);
+extern	void*	memset(void*, int, long);
+extern	int	memcmp(void*, void*, long);
+extern	void*	memmove(void*, void*, long);
+extern	void*	memchr(void*, int, long);
+
+/*
+ * string routines
+ */
+extern	char*	strcat(char*, char*);
+extern	char*	strchr(char*, char);
+extern	int	strcmp(char*, char*);
+extern	char*	strcpy(char*, char*);
+extern	char*	strncat(char*, char*, long);
+extern	char*	strncpy(char*, char*, long);
+extern	int	strncmp(char*, char*, long);
+extern	long	strlen(char*);
+extern	char*	strrchr(char*, char);
+extern	char*	strstr(char*, char*);
+
+/*
+ * print routines
+ * 	Fconv isn't used but is defined to satisfy prototypes in libg.h
+ *	that are never called.
+ */
+typedef	struct Fconv Fconv;
+
+extern	char*	donprint(char*, char*, char*, void*);
+extern	int	sprint(char*, char*, ...);
+extern	int	print(char*, ...);
+
+#define	PRINTSIZE	256
+
+/*
+ * one-of-a-kind
+ */
+extern	int	atoi(char*);
+extern	long	strtol(char*, char**, int);
+extern	ulong	strtoul(char*, char**, int);
+extern	long	end;
+
+/*
+ * Syscall data structures
+ */
+
+#define	MORDER	0x0003	/* mask for bits defining order of mounting */
+#define	MREPL	0x0000	/* mount replaces object */
+#define	MBEFORE	0x0001	/* mount goes before others in union directory */
+#define	MAFTER	0x0002	/* mount goes after others in union directory */
+#define	MCREATE	0x0004	/* permit creation in mounted directory */
+#define	MMASK	0x0007	/* all bits on */
+
+#define	OREAD	0	/* open for read */
+#define	OWRITE	1	/* write */
+#define	ORDWR	2	/* read and write */
+#define	OEXEC	3	/* execute, == read but check execute permission */
+#define	OTRUNC	16	/* or'ed in (except for exec), truncate file first */
+#define	OCEXEC	32	/* or'ed in, close on exec */
+#define	ORCLOSE	64	/* or'ed in, remove on close */
+
+#define	NCONT	0	/* continue after note */
+#define	NDFLT	1	/* terminate after note */
+
+typedef struct Qid	Qid;
+typedef struct Dir	Dir;
+typedef struct Waitmsg	Waitmsg;
+
+#define	ERRLEN	64
+#define	DIRLEN	116
+#define	NAMELEN	28
+
+struct Qid
+{
+	ulong	path;
+	ulong	vers;
+};
+
+struct Dir
+{
+	char	name[NAMELEN];
+	char	uid[NAMELEN];
+	char	gid[NAMELEN];
+	Qid	qid;
+	ulong	mode;
+	long	atime;
+	long	mtime;
+	vlong	length;
+	short	type;
+	short	dev;
+};
+
+struct Waitmsg
+{
+	int	pid;		/* of loved one */
+	int	status;		/* unused; a placeholder */
+	ulong	time[3];	/* of loved one */
+	char	msg[ERRLEN];
+};
+#define	nelem(x)	(sizeof(x)/sizeof((x)[0]))
--- /dev/null
+++ b/os/boot/rpcg/libg.h
@@ -1,0 +1,418 @@
+#pragma	src	"/sys/src/libg"
+#pragma	lib	"libg.a"
+
+enum	/* constants for I/O to devgraphics */
+{
+	Tilehdr = 40,
+	Tilesize = 8000
+};
+
+/*
+ *  you may think it's a blit, but it's gnot
+ */
+enum
+{
+	EMAXMSG = 128+8192,	/* size of 9p header+data */
+};
+
+/*
+ * Types
+ */
+
+typedef struct	Bitmap		Bitmap;
+typedef struct	Display	Display;
+typedef struct	Point		Point;
+typedef struct	Rectangle 	Rectangle;
+typedef struct	Cursor		Cursor;
+typedef struct	Mouse		Mouse;
+typedef struct	Menu		Menu;
+typedef struct	Font		Font;
+typedef struct	Fontchar	Fontchar;
+typedef struct	Subfont		Subfont;
+typedef struct	Cachefont	Cachefont;
+typedef struct	Cacheinfo	Cacheinfo;
+typedef struct	Cachesubf	Cachesubf;
+typedef struct	Event		Event;
+typedef struct	Slave		Slave;
+typedef struct	Ebuf		Ebuf;
+typedef struct	RGB		RGB;
+typedef struct	Linedesc	Linedesc;
+typedef struct	DRefret	DRefret;
+
+struct DRefret
+{
+	int		n;	/* number of bytes */
+	int		dy;	/* number of lines */
+	uchar	*dp;	/* pointer to data */
+};
+
+struct	Point
+{
+	int	x;
+	int	y;
+};
+
+struct Rectangle
+{
+	Point min;
+	Point max;
+};
+
+typedef	DRefret DRefresh(Display*, int, Rectangle, uchar*, uchar*, int);
+
+struct	Bitmap
+{
+	Rectangle		r;		/* rectangle in data area, local coords */
+	Rectangle 	clipr;		/* clipping region */
+	int			ldepth;	/* log base 2 of number of bits per pixel */
+	ulong		*base;	/* pointer to start of data */
+	int			zero;		/* base+zero=&word containing (0,0) */
+	ulong		width;	/* width in words of total data area */
+	Display		*display;	/* if present */
+};
+
+struct	Display
+{
+	uchar		*data;	/* transfer buffer */
+	Rectangle		r;
+	int			ldepth;
+	Rectangle		bb;		/* bounding box of changes */
+	int			waste;	/* unused part of bb */
+	Rectangle		bound;	/* memory for boundin/boundout */
+	Bitmap		*image;	/* owner */
+	int			id;
+	int			fd;
+	int			ctlfd;
+	int			local;
+	int			bytewidth;
+	void			*drdata1;	/* storage for drefresh() */
+	void			*drdata2;	/* storage for drefresh() */
+	DRefresh		*drefresh;
+};
+
+
+struct	Mouse
+{
+	int	buttons;	/* bit array: LMR=124 */
+	Point	xy;
+	ulong	msec;
+};
+
+struct	Cursor
+{
+	Point	offset;
+	uchar	clr[2*16];
+	uchar	set[2*16];
+};
+
+struct Menu
+{
+	char	**item;
+	char	*(*gen)(int);
+	int	lasthit;
+};
+
+struct Linedesc
+{
+	int	x0;
+	int	y0;
+	char	xmajor;
+	char	slopeneg;
+	long	dminor;
+	long	dmajor;
+};
+
+/*
+ * Subfonts
+ *
+ * given char c, Subfont *f, Fontchar *i, and Point p, one says
+ *	i = f->info+c;
+ *	bitblt(b, Pt(p.x+i->left,p.y+i->top),
+ *		bitmap, Rect(i->x,i->top,(i+1)->x,i->bottom),
+ *		fc);
+ *	p.x += i->width;
+ * where bitmap b is the repository of the images.
+ *
+ */
+
+struct	Fontchar
+{
+	short	x;		/* left edge of bits */
+	uchar	top;		/* first non-zero scan-line */
+	uchar	bottom;		/* last non-zero scan-line + 1 */
+	char	left;		/* offset of baseline */
+	uchar	width;		/* width of baseline */
+};
+
+struct	Subfont
+{
+	short	n;		/* number of chars in font */
+	uchar	height;		/* height of bitmap */
+	char	ascent;		/* top of bitmap to baseline */
+	Fontchar *info;		/* n+1 character descriptors */
+	Bitmap	*bits;		/* of font */
+};
+
+enum
+{
+	/* starting values */
+	LOG2NFCACHE =	6,
+	NFCACHE =	(1<<LOG2NFCACHE),	/* #chars cached */
+	NFLOOK =	5,			/* #chars to scan in cache */
+	NFSUBF =	2,			/* #subfonts to cache */
+	/* max value */
+	MAXFCACHE =	2048+NFLOOK,		/* generous upper limit */
+	MAXSUBF =	50,			/* generous upper limit */
+	/* deltas */
+	DSUBF = 	4,
+	/* expiry ages */
+	SUBFAGE	=	10000,
+	CACHEAGE =	10000,
+};
+
+struct Cachefont
+{
+	Rune	min;	/* lowest rune value to be taken from subfont */
+	Rune	max;	/* highest rune value+1 to be taken from subfont */
+	int	offset;	/* position in subfont of character at min */
+	int	abs;	/* name has been made absolute */
+	char	*name;
+};
+
+struct Cacheinfo
+{
+	Rune		value;	/* value of character at this slot in cache */
+	ushort		age;
+	ulong		xright;	/* right edge of bits */
+	Fontchar;
+};
+
+struct Cachesubf
+{
+	ulong		age;	/* for replacement */
+	Cachefont	*cf;	/* font info that owns us */
+	Subfont		*f;	/* attached subfont */
+};
+
+struct Font
+{
+	char		*name;
+	short		height;	/* max height of bitmap, interline spacing */
+	short		ascent;	/* top of bitmap to baseline */
+	int			maxldepth;	/* over all loaded subfonts */
+	short		width;	/* widest so far; used in caching only */	
+	short		ldepth;	/* of images */
+	short		nsub;	/* number of subfonts */
+	ulong		age;	/* increasing counter; used for LRU */
+	int		ncache;	/* size of cache */
+	int		nsubf;	/* size of subfont list */
+	Cacheinfo	*cache;
+	Cachesubf	*subf;
+	Cachefont	**sub;	/* as read from file */
+	Bitmap	*cacheimage;
+};
+
+struct	Event
+{
+	int	kbdc;
+	Mouse	mouse;
+	int	n;		/* number of characters in mesage */
+	uchar	data[EMAXMSG];	/* message from an arbitrary file descriptor */
+};
+
+struct Slave{
+	int	pid;
+	Ebuf	*head;		/* queue of messages for this descriptor */
+	Ebuf	*tail;
+};
+
+struct Ebuf{
+	Ebuf	*next;
+	int	n;		/* number of bytes in buf */
+	uchar	buf[EMAXMSG];
+};
+
+struct RGB
+{
+	ulong	red;
+	ulong	green;
+	ulong	blue;
+};
+
+/*
+ * Codes for bitblt etc.
+ *
+ *	       D
+ *	     0   1
+ *         ---------
+ *	 0 | 1 | 2 |
+ *     S   |---|---|
+ * 	 1 | 4 | 8 |
+ *         ---------
+ *
+ *	Usually used as D|S; DorS is so tracebacks are readable.
+ */
+typedef
+enum	Fcode
+{
+	Zero		= 0x0,
+	DnorS		= 0x1,
+	DandnotS	= 0x2,
+	notS		= 0x3,
+	notDandS	= 0x4,
+	notD		= 0x5,
+	DxorS		= 0x6,
+	DnandS		= 0x7,
+	DandS		= 0x8,
+	DxnorS		= 0x9,
+	D		= 0xA,
+	DornotS		= 0xB,
+	S		= 0xC,
+	notDorS		= 0xD,
+	DorS		= 0xE,
+	F		= 0xF,
+} Fcode;
+
+/*
+ * Miscellany
+ */
+
+extern Point	 add(Point, Point), sub(Point, Point);
+extern Point	 mul(Point, int), div(Point, int);
+extern Rectangle rsubp(Rectangle, Point), raddp(Rectangle, Point), inset(Rectangle, int);
+extern Rectangle rmul(Rectangle, int), rdiv(Rectangle, int);
+extern Rectangle rshift(Rectangle, int), rcanon(Rectangle);
+extern Bitmap*	 balloc(Rectangle, int);
+extern Bitmap*	 ballocnomem(Rectangle, int);
+extern Bitmap*	 brealloc(Bitmap*, Rectangle, int);
+extern Bitmap*	 breallocnomem(Bitmap*, Rectangle, int);
+extern int		bbytewidth(Bitmap*, int*, int*);
+extern void	 bfree(Bitmap*);
+extern void	 bfreemem(Bitmap*);
+extern int	 rectclip(Rectangle*, Rectangle);
+extern void	 binit(void(*)(char*), char*, char*);
+extern void	 binit1(void(*)(char*), char*, char*, int);
+extern void	 bclose(void);
+extern void	 berror(char*);
+extern void	 bitblt(Bitmap*, Point, Bitmap*, Rectangle, Fcode);
+extern int	 bitbltclip(void*);
+extern Font*	 rdfontfile(char*, int);
+extern void	 ffree(Font*);
+extern void	fminldepth(Font*);
+extern Font*	 mkfont(Subfont*, Rune);
+extern Subfont*	 subfalloc(int, int, int, Fontchar*, Bitmap*);
+extern void	 subffree(Subfont*);
+extern int	 cachechars(Font*, char**, ushort*, int, int*);
+extern Point	 string(Bitmap*, Point, Font*, char*, Fcode);
+extern Point	 subfstring(Bitmap*, Point, Subfont*, char*, Fcode);
+extern void	 segment(Bitmap*, Point, Point, int, Fcode);
+extern void	 point(Bitmap*, Point, int, Fcode);
+extern void	 arc(Bitmap*, Point, Point, Point, int, Fcode);
+extern void	 circle(Bitmap*, Point, int, int, Fcode);
+extern void	 disc(Bitmap*, Point, int, int, Fcode);
+extern void	 ellipse(Bitmap*, Point, int, int, int, Fcode);
+extern long	 strwidth(Font*, char*);
+extern void	 agefont(Font*);
+extern int	 loadchar(Font*, Rune, Cacheinfo*, int, int);
+extern Point	 strsize(Font*, char*);
+extern long	 charwidth(Font*, Rune);
+extern void	 texture(Bitmap*, Rectangle, Bitmap*, Fcode);
+extern void	 wrbitmap(Bitmap*, int, int, uchar*);
+extern void	 rdbitmap(Bitmap*, int, int, uchar*);
+extern void	 wrbitmapfile(int, Bitmap*);
+extern Bitmap*	 rdbitmapfile(int);
+extern void	 wrsubfontfile(int, Subfont*);
+extern void	 wrcolmap(Bitmap*, RGB*);
+extern void	 rdcolmap(Bitmap*, RGB*);
+extern Subfont*	 rdsubfontfile(int, Bitmap*);
+extern void	_unpackinfo(Fontchar*, uchar*, int);
+
+extern int	 ptinrect(Point, Rectangle), rectinrect(Rectangle, Rectangle);
+extern int	 rectXrect(Rectangle, Rectangle);
+extern int	 eqpt(Point, Point), eqrect(Rectangle, Rectangle);
+extern void	 border(Bitmap*, Rectangle, int, Fcode);
+extern void	 cursorswitch(Cursor*);
+extern void	 cursorset(Point);
+extern Rectangle bscreenrect(Rectangle*);
+extern void	 bflush(void);
+extern void	 bexit(void);
+extern int	 _clipline(Rectangle, Point*, Point*, Linedesc*);
+extern int	 clipline(Rectangle, Point*, Point*);
+extern int	 clipr(Bitmap*, Rectangle);
+
+extern void	 einit(ulong);
+extern ulong	 estart(ulong, int, int);
+extern ulong	 etimer(ulong, int);
+extern ulong	 event(Event*);
+extern ulong	 eread(ulong, Event*);
+extern Ebuf*	 ebread(Slave*);
+extern Mouse	 emouse(void);
+extern int	 ekbd(void);
+extern int	 ecanread(ulong);
+extern int	 ecanmouse(void);
+extern int	 ecankbd(void);
+extern void	 ereshaped(Rectangle);	/* supplied by user */
+extern int	 menuhit(int, Mouse*, Menu*);
+extern Rectangle getrect(int, Mouse*);
+extern ulong	 rgbpix(Bitmap*, RGB);
+extern int	_gminor(long, Linedesc*);
+
+enum{
+	Emouse		= 1,
+	Ekeyboard	= 2,
+};
+
+enum
+{
+	MAXSLAVE = 32,
+};
+
+#define	Pt(x, y)		((Point){(x), (y)})
+#define	Rect(x1, y1, x2, y2)	((Rectangle){Pt(x1, y1), Pt(x2, y2)})
+#define	Rpt(p1, p2)		((Rectangle){(p1), (p2)})
+
+
+#define	Dx(r)	((r).max.x-(r).min.x)
+#define	Dy(r)	((r).max.y-(r).min.y)
+
+extern	Bitmap	screen;
+extern	Font	*font;
+extern	uchar	_btmp[8192];
+
+extern	int	_mousefd;
+extern	int	_cursorfd;
+
+#define	BGSHORT(p)		(((p)[0]<<0) | ((p)[1]<<8))
+#define	BGLONG(p)		((BGSHORT(p)<<0) | (BGSHORT(p+2)<<16))
+#define	BPSHORT(p, v)		((p)[0]=(v), (p)[1]=((v)>>8))
+#define	BPLONG(p, v)		(BPSHORT(p, (v)), BPSHORT(p+2, (v)>>16))
+
+ulong	*wordaddr(Bitmap*, Point);
+uchar	*byteaddr(Bitmap*, Point);
+int		dfree(Display*);
+int		dwritectl(Display*, char*, int);
+int		dreadctl(Display*, char*, int);
+int		dinfo(Display*, int, int*, Rectangle*);
+void*	dinit(Display*, Bitmap*, int, int);
+int		ddelete(Display*);
+void		dfreemem(Display*);
+int		dreadctl(Display*, char*, int);
+int		dwritectl(Display*, char*, int);
+void	dbound(Display*, Rectangle);
+void	bload(Bitmap*, Rectangle, uchar*);
+ulong	bunload(Bitmap*, Rectangle, uchar*);
+void		drefresh(Display*, Rectangle);
+Display	*dopen(char*, int, DRefresh*);
+Bitmap*	dbitmap(Display*, DRefresh*, int);
+void		dclose(Display*);
+void		dflush(Display*);
+void		_bltinit(void);
+Bitmap*	battach(Bitmap*, int, int);
+int		readmouse(Mouse*);
+int		atomouse(Mouse*, char*, int);
+
+/*
+ * Refresh functions
+ */
+DRefresh	drtexture;
+DRefresh	drbackstore;
--- /dev/null
+++ b/os/boot/rpcg/main.c
@@ -1,0 +1,527 @@
+#include "u.h"
+#include "lib.h"
+#include "mem.h"
+#include "dat.h"
+#include "fns.h"
+#include "io.h"
+
+#include "dosfs.h"
+
+typedef struct Type Type;
+typedef struct Medium Medium;
+typedef struct Mode Mode;
+
+enum {
+	Dany		= -1,
+	Nmedia		= 16,
+
+	/* DS1 switch options */
+	Sflashfs		= 1<<0,	/* take local fs from flash */
+	Snotflash		= 1<<1,	/* don't boot from flash */
+};
+
+enum {					/* type */
+	Tflash,
+	Tuart,
+	Tether,
+	Thard,
+
+	Tany		= -1,
+};
+
+enum {					/* flag and name */
+	Fnone		= 0x00,
+
+	Fdos		= 0x01,
+	Ndos		= 0x00,
+	Fboot		= 0x02,
+	Nboot		= 0x01,
+	Fbootp		= 0x04,
+	Nbootp		= 0x02,
+	Fflash		= 0x08,
+	Fuart		= 0x10,
+	NName		= 0x03,
+
+	Fany		= Fbootp|Fboot|Fdos|Fflash|Fuart,
+
+	Fini		= 0x10,
+	Fprobe		= 0x80,
+};
+
+enum {					/* mode */
+	Mauto		= 0x00,
+	Mlocal		= 0x01,
+	Manual		= 0x02,
+	NMode		= 0x03,
+};
+
+typedef struct Type {
+	int	type;
+	char	*cname;
+	int	flag;
+	int	(*init)(void);
+	long	(*read)(int, void*, long);
+	long	(*seek)(int, long);
+	Partition* (*setpart)(int, char*);
+	char*	name[NName];
+
+	int	mask;
+	Medium*	media;
+} Type;
+
+typedef struct Medium {
+	Type*	type;
+	int	flag;
+	Partition* partition;
+	Dos;
+
+	Medium*	next;
+} Medium;
+
+typedef struct Mode {
+	char*	name;
+	int	mode;
+} Mode;
+
+static Type types[] = {
+	{	Tflash, "flash",
+		Fflash,
+		flashinit, 0, 0, 0,
+		{ 0, "F", 0, }
+	},
+/*
+	{	Tuart, "uart",
+		Fuart|Fboot,
+		uartinit, uartread, uartseek, setuartpart,
+		{ 0, "u", 0, }
+	},
+*/
+	{	Tether, "ether",
+		Fbootp,
+		etherinit, 0, 0, 0,
+		{ 0, 0, "e", },
+	},
+	{	Thard, "ata",
+		Fini|Fboot|Fdos,
+		0, 0, 0, 0,		/* not used now, will be later with PCMCIA */
+		{ "hd", "h", 0, },
+	},
+	{-1},
+};
+
+static Medium media[Nmedia];
+static Medium *curmedium = media;
+
+static Mode modes[NMode+1] = {
+	[Mauto]		{ "auto",   Mauto,  },
+	[Mlocal]	{ "local",  Mlocal, },
+	[Manual]	{ "manual", Manual, },
+};
+
+static char *inis[] = {
+	"inferno/inferno.ini",
+	"inferno.ini",
+	"plan9/plan9.ini",
+	"plan9.ini",
+	0,
+};
+char **ini;
+int	predawn;
+
+static int
+parse(char *line, int *type, int *flag, int *dev, char *file)
+{
+	Type *tp;
+	char buf[2*NAMELEN], *v[4], *p;
+	int i;
+
+	strcpy(buf, line);
+	switch(getcfields(buf, v, 4, "!")){
+
+	case 3:
+		break;
+
+	case 2:
+		v[2] = "";
+		break;
+
+	default:
+		return 0;
+	}
+
+	*flag = 0;
+	for(tp = types; tp->cname; tp++){
+		for(i = 0; i < NName; i++){
+
+			if(tp->name[i] == 0 || strcmp(v[0], tp->name[i]))
+				continue;
+			*type = tp->type;
+			*flag |= 1<<i;
+
+			if((*dev = strtoul(v[1], &p, 0)) == 0 && p == v[1])
+				return 0;
+		
+			strcpy(file, v[2]);
+		
+			return 1;
+		}
+	}
+
+	return 0;
+
+}
+
+static int
+boot(Medium *mp, int flag, char *file)
+{
+	Dosfile df;
+	char ixdos[128], *p;
+	int r;
+
+	uartsetboot(0);
+	if(flag & Fbootp){
+		sprint(BOOTLINE, "%s!%d", mp->type->name[Nbootp], mp->dev);
+		return bootp(mp->dev, file);
+	}
+
+	if(flag & Fflash){
+		if(mp->flag & Fflash && flashbootable(0))
+			flashboot(mp->dev);
+	}
+
+	if(flag & Fboot){
+
+		if(mp->flag & Fini){
+			(*mp->type->setpart)(mp->dev, "disk");
+			plan9ini(mp, nil);
+		}
+		if(file == 0 || *file == 0)
+			file = mp->partition->name;
+		(*mp->type->setpart)(mp->dev, file);
+		sprint(BOOTLINE, "%s!%d!%s", mp->type->name[Nboot], mp->dev, file);
+		r = plan9boot(mp->dev, mp->seek, mp->read);
+		uartsetboot(0);
+		return r;
+	}
+
+	if(flag & Fdos){
+		if(mp->type->setpart)
+			(*mp->type->setpart)(mp->dev, "disk");
+		if(mp->flag & Fini)
+			plan9ini(mp, nil);
+		if(file == 0 || *file == 0){
+			strcpy(ixdos, *ini);
+			if(p = strrchr(ixdos, '/'))
+				p++;
+			else
+				p = ixdos;
+			strcpy(p, "impc");
+			if(dosstat(mp, ixdos, &df) <= 0)
+				return -1;
+		}
+		else
+			strcpy(ixdos, file);
+		sprint(BOOTLINE, "%s!%d!%s", mp->type->name[Ndos], mp->dev, ixdos);
+		return dosboot(mp, ixdos);
+	}
+
+	return -1;
+}
+
+static Medium*
+allocm(Type *tp)
+{
+	Medium **l;
+
+	if(curmedium >= &media[Nmedia])
+		return 0;
+
+	for(l = &tp->media; *l; l = &(*l)->next)
+		;
+	*l = curmedium++;
+	return *l;
+}
+
+Medium*
+probe(int type, int flag, int dev)
+{
+	Type *tp;
+	int dombr, i, start;
+	Medium *mp;
+	Dosfile df;
+	Partition *pp;
+
+	for(tp = types; tp->cname; tp++){
+		if(type != Tany && type != tp->type || tp->init == 0)
+			continue;
+
+		if(flag != Fnone){
+			for(mp = tp->media; mp; mp = mp->next){
+				if((flag & mp->flag) && (dev == Dany || dev == mp->dev))
+					return mp;
+			}
+		}
+		if((tp->flag & Fprobe) == 0){
+			tp->flag |= Fprobe;
+			tp->mask = (*tp->init)();
+		}
+
+		for(i = 0; tp->mask; i++){
+			if((tp->mask & (1<<i)) == 0)
+				continue;
+			tp->mask &= ~(1<<i);
+
+			if((mp = allocm(tp)) == 0)
+				continue;
+
+			mp->dev = i;
+			mp->flag = tp->flag;
+			mp->seek = tp->seek;
+			mp->read = tp->read;
+			mp->type = tp;
+
+			if(mp->flag & Fboot){
+				if((mp->partition = (*tp->setpart)(i, "boot")) == 0)
+					mp->flag &= ~Fboot;
+				if((mp->flag & (Fflash|Fuart)) == 0)
+					(*tp->setpart)(i, "disk");
+			}
+
+			if(mp->flag & Fdos){
+				start = 0;
+				dombr = 1;
+				if(mp->type->setpart){
+					if(pp = (*mp->type->setpart)(i, "dos")){
+						if(start = pp->start)
+							dombr = 0;
+					}
+					(*tp->setpart)(i, "disk");
+				}
+				if(dosinit(mp, start, dombr) < 0)
+					mp->flag &= ~(Fini|Fdos);
+				else
+					print("dos init failed\n");
+			}
+
+			if(mp->flag & Fini){
+				mp->flag &= ~Fini;
+				for(ini = inis; *ini; ini++){
+					if(dosstat(mp, *ini, &df) <= 0)
+						continue;
+					mp->flag |= Fini;
+					break;
+				}
+			}
+
+			if((flag & mp->flag) && (dev == Dany || dev == i))
+				return mp;
+		}
+	}
+
+	return 0;
+}
+
+void
+main(void)
+{
+	Medium *mp;
+	int dev, flag, i, mode, tried, type, options;
+	char def[2*NAMELEN], file[2*NAMELEN], line[80], *p;
+	Type *tp;
+
+	machinit();
+	archinit();
+	meminit();
+	cpminit();
+	trapinit();
+	consinit();	/* screen and keyboard initially */
+/*	screeninit();	*/
+	cpuidprint();
+	alarminit();
+	clockinit();
+print("predawn\n");
+	predawn = 0;
+	spllo();
+print("dawn\n");
+	options = archoptionsw();
+print("options=#%ux\n", options);
+
+	mp = 0;
+	for(tp = types; tp->cname; tp++){
+		if(tp->type == Tether)
+			continue;
+		if((mp = probe(tp->type, Fini, Dany)) && (mp->flag & Fini)){
+			plan9ini(mp, nil);
+			break;
+		}
+	}
+
+	if(mp == 0 || (mp->flag & Fini) == 0)
+		plan9ini(nil, flashconfig(0));
+
+	//consinit();	/* establish new console location */
+
+	if((options & Snotflash) == 0 && flashbootable(0)){
+		print("Flash boot\n");
+		flashboot(0);
+	}
+
+	tried = 0;
+	mode = Mauto;
+	p = getconf("bootfile");
+	flag = 0;
+
+	if(p != 0) {
+		mode = Manual;
+		for(i = 0; i < NMode; i++){
+			if(strcmp(p, modes[i].name) == 0){
+				mode = modes[i].mode;
+				goto done;
+			}
+		}
+		if(parse(p, &type, &flag, &dev, file) == 0) {
+			print("Bad bootfile syntax: %s\n", p);
+			goto done;
+		}
+		mp = probe(type, flag, dev);
+		if(mp == 0) {
+			print("Cannot access device: %s\n", p);
+			goto done;
+		}
+		tried = boot(mp, flag, file);
+	}
+done:
+	if(tried == 0 && mode != Manual){
+		flag = Fany;
+		if(mode == Mlocal)
+			flag &= ~Fbootp;
+		if(options & Snotflash)
+			flag &= ~Fflash;
+		if((mp = probe(Tany, flag, Dany)) != 0)
+			boot(mp, flag & mp->flag, 0);
+	}
+
+	def[0] = 0;
+	probe(Tany, Fnone, Dany);
+
+	flag = 0;
+	for(tp = types; tp->cname; tp++){
+		for(mp = tp->media; mp; mp = mp->next){
+			if(flag == 0){
+				flag = 1;
+				print("Boot devices:");
+			}
+
+			if(mp->flag & Fbootp)
+				print(" %s!%d", mp->type->name[Nbootp], mp->dev);
+			if(mp->flag & Fdos)
+				print(" %s!%d", mp->type->name[Ndos], mp->dev);
+			if(mp->flag & (Fflash|Fuart) || mp->flag & Fboot)
+				print(" %s!%d", mp->type->name[Nboot], mp->dev);
+		}
+	}
+	if(flag)
+		print("\n");
+
+	for(;;){
+		if(getstr("boot from", line, sizeof(line), def) >= 0){
+			if(parse(line, &type, &flag, &dev, file)){
+				if(mp = probe(type, flag, dev))
+					boot(mp, flag, file);
+			}
+		}
+		def[0] = 0;
+	}
+}
+
+void
+machinit(void)
+{
+	memset(m, 0, sizeof(*m));
+	m->delayloop = 20000;
+	m->cpupvr = getpvr();
+	m->iomem = KADDR(INTMEM);
+}
+
+int
+getcfields(char* lp, char** fields, int n, char* sep)
+{
+	int i;
+
+	for(i = 0; lp && *lp && i < n; i++){
+		while(*lp && strchr(sep, *lp) != 0)
+			*lp++ = 0;
+		if(*lp == 0)
+			break;
+		fields[i] = lp;
+		while(*lp && strchr(sep, *lp) == 0){
+			if(*lp == '\\' && *(lp+1) == '\n')
+				*lp++ = ' ';
+			lp++;
+		}
+	}
+
+	return i;
+}
+
+static	Map	memv[512];
+static	RMap	rammap = {"physical memory"};
+
+void
+meminit(void)
+{
+	ulong e;
+
+	mapinit(&rammap, memv, sizeof(memv));
+	e = PADDR(&end);
+	mapfree(&rammap, e, 4*1024*1024-e);	/* fixed 4Mbytes is plenty for bootstrap */
+}
+
+void*
+ialloc(ulong n, int align)
+{
+	ulong a;
+	int s;
+
+	if(align <= 0)
+		align = 4;
+	s = splhi();
+	a = mapalloc(&rammap, 0, n, align);
+	splx(s);
+	if(a == 0)
+		panic("ialloc");
+	return memset(KADDR(a), 0, n);
+}
+
+void*
+malloc(ulong n)
+{
+	ulong *p;
+
+	n = ((n+sizeof(int)-1)&~(sizeof(int)-1))+2*sizeof(int);
+	p = ialloc(n, sizeof(int));
+	*p++ = 0xcafebeef;
+	*p++ = n;
+	return p;
+}
+
+void
+free(void *ap)
+{
+	int s;
+	ulong *p;
+
+	p = ap;
+	if(p){
+		if(*(p -= 2) != 0xcafebeef)
+			panic("free");
+		s = splhi();
+		mapfree(&rammap, (ulong)p, p[1]);
+		splx(s);
+	}
+}
+
+void
+sched(void)
+{
+}
--- /dev/null
+++ b/os/boot/rpcg/mem.h
@@ -1,0 +1,94 @@
+/*
+ * Memory and machine-specific definitions.  Used in C and assembler.
+ */
+
+/*
+ * Sizes
+ */
+#define	BI2BY		8			/* bits per byte */
+#define BI2WD		32			/* bits per word */
+#define	BY2WD		4			/* bytes per word */
+#define	BY2PG		4096			/* bytes per page */
+#define	WD2PG		(BY2PG/BY2WD)		/* words per page */
+#define	PGSHIFT		12			/* log(BY2PG) */
+#define PGROUND(s)	(((s)+(BY2PG-1))&~(BY2PG-1))
+
+#define	MAXMACH		1			/* max # cpus system can run */
+#define	CACHELINELOG	4
+#define CACHELINESZ	(1<<CACHELINELOG)
+
+/*
+ * Time
+ */
+#define	HZ		(50)				/* clock frequency */
+#define	MS2HZ		(1000/HZ)			/* millisec per clock tick */
+#define	TK2SEC(t)	((t)/HZ)			/* ticks to seconds */
+#define	TK2MS(t)	((((ulong)(t))*1000)/HZ)	/* ticks to milliseconds */
+#define	MS2TK(t)	((((ulong)(t))*HZ)/1000)	/* milliseconds to ticks */
+#define	MHz	1000000
+
+/*
+ * Fundamental values
+ */
+
+#define	KZERO	0	/* bootstrap runs in real mode */
+#define	MACHSIZE	4096
+
+/*
+ *  physical MMU
+ */
+#define KSEG0	0x20000000
+#define	KSEGM	0xE0000000	/* mask to check which seg */
+
+/*
+ * MSR bits
+ */
+
+#define	POW	0x40000	/* enable power mgmt */
+#define	TGPR	0x20000	/* GPR0-3 remapped; 603/603e specific */
+#define	ILE	0x10000	/* interrupts little endian */
+#define	EE	0x08000	/* enable external/decrementer interrupts */
+#define	PR	0x04000	/* =1, user mode */
+#define	FPE	0x02000	/* enable floating point */
+#define	ME	0x01000	/* enable machine check exceptions */
+#define	FE0	0x00800
+#define	SE	0x00400	/* single-step trace */
+#define	BE	0x00200	/* branch trace */
+#define	FE1	0x00100
+#define	IP	0x00040	/* =0, vector to nnnnn; =1, vector to FFFnnnnn */
+#define	IR	0x00020	/* enable instruction address translation */
+#define	DR	0x00010	/* enable data address translation */
+#define	RI	0x00002	/* exception is recoverable */
+#define	LE	0x00001	/* little endian mode */
+
+#define	KMSR	(ME|FE0|FE1|FPE)
+#define	UMSR	(KMSR|PR|EE|IR|DR)
+
+/*
+ * MPC82x addresses
+ */
+#define	BCSRMEM	0xFA400000
+#define	FLASHMEM	0xFFC00000
+#define	INTMEM	0xFA200000
+
+#define	DPRAM	(INTMEM+0x2000)
+#define	DPLEN1	0x400
+#define	DPLEN2	0x200
+#define	DPLEN3	0x100
+#define	DPBASE	(DPRAM+DPLEN1)
+
+#define	SCC1P	(INTMEM+0x3C00)
+#define	I2CP	(INTMEM+0x3C80)
+#define	MISCP	(INTMEM+0x3CB0)
+#define	IDMA1P	(INTMEM+0x3CC0)
+#define	SCC2P	(INTMEM+0x3D00)
+#define	SCC3P	(INTMEM+0x3E00)
+#define	SCC4P	(INTMEM+0x3F00)
+#define	SPIP	(INTMEM+0x3D80)
+#define	TIMERP	(INTMEM+0x3DB0)
+#define	SMC1P	(INTMEM+0x3E80)
+#define	DSP1P	(INTMEM+0x3EC0)
+#define	SMC2P	(INTMEM+0x3F80)
+#define	DSP2P	(INTMEM+0x3FC0)
+
+#define KEEP_ALIVE_KEY 0x55ccaa33	/* clock and rtc register key */
--- /dev/null
+++ b/os/boot/rpcg/mkfile
@@ -1,0 +1,120 @@
+objtype=power
+OBJTYPE=power	# always
+<../../../mkconfig
+SYSTARG=$OSTARG	# always
+<$ROOT/mkfiles/mkfile-$SYSTARG-$OBJTYPE
+INSTALLDIR=$ROOT/Inferno/$OBJTYPE/bin	#path of directory where kernel is installed
+CONF=rpcg	# selects board dependent code
+TARG=qb$CONF
+OFILES=\
+	l.$O\
+	arch$CONF.$O\
+	devuart.$O\
+	uartboot.$O\
+	alarm.$O\
+	bootp.$O\
+	clock.$O\
+	conf.$O\
+	console.$O\
+	cpm.$O\
+	defont0.$O\
+	donprint.$O\
+	dosboot.$O\
+	devether.$O\
+	etherscc.$O\
+	fblt.$O\
+	gbitbltclip.$O\
+	flash.$O\
+	main.$O\
+	i2c.$O\
+	plan9boot.$O\
+	qio.$O\
+	rmap.$O\
+	screen.$O\
+	init$CONF.$O\
+	trap.$O\
+	zqs.$O\
+
+HFILES=\
+	boot.h\
+	dat.h\
+	fns.h\
+	io.h\
+	lib.h\
+	mem.h\
+	squeeze.h\
+	gnot.h\
+	arch$CONF.h\
+
+LIBS=\
+	kern\
+
+LIBDIRS=$LIBS
+LIBNAMES=${LIBS:%=lib%.a}
+LIBFILES=${LIBS:%=$ROOT/$TARGMODEL/$OBJTYPE/lib/lib%.a}
+
+#all:NV:	$TARG k.mx f.mx
+all:NV:	$TARG
+install:V: 	$INSTALLDIR/$TARG
+installall:V: 	$INSTALLDIR/$TARG
+
+$INSTALLDIR/%: %
+	rm -f $INSTALLDIR/$stem && cp $stem $INSTALLDIR/$stem
+
+$TARG: $OFILES $LIBNAMES
+	$LD -o $target -l  -T0x140000 -R4 $OFILES $LIBFILES
+	ls -l $target
+
+qbrom$CONF:	$OFILES $LIBNAMES
+	$LD -o $target -l -T0xFFC20100 -R0 -D0x140000 $OFILES $LIBFILES
+
+k.mx:	$TARG
+	ms2 -S 0x100 -a 0x140000 -3 -p 4 $TARG >$target
+
+f.mx:	qbrom$CONF
+	ms2 -S 0x100 -a 0xFFC20100 -3 -p 4 $prereq >$target
+
+g.mx:	qbrom$CONF
+	ms2 -S 0x100 -a 0x10000 -3 -p 4 $prereq >$target
+
+%.$O:	%.s
+	$AS -Dconf$CONF $stem.s
+
+%.$O:	%.c
+	$CC $CFLAGS $stem.c
+
+%.$O:	$HFILES
+
+lib%.a:V:	$SHELLTYPE-lib%.a
+
+rc-lib%.a nt-lib%.a:VQ:
+	echo '@{builtin cd ' $ROOT/lib$stem ';mk SHELLTYPE=$SHELLTYPE SYSTARG=$SYSTARG OBJTYPE=$OBJTYPE install}'
+	@{builtin cd  $ROOT/lib$stem ;mk 'SHELLTYPE='$SHELLTYPE 'SYSTARG='$SYSTARG 'OBJTYPE='$OBJTYPE install}
+
+sh-lib%.a:VQ:
+	echo "(cd $ROOT/lib$stem ; mk SHELLTYPE=$SHELLTYPE SYSTARG=$SYSTARG OBJTYPE=$OBJTYPE install)"
+	(cd $ROOT/lib$stem ; mk SHELLTYPE=$SHELLTYPE SYSTARG=$SYSTARG OBJTYPE=$OBJTYPE install)
+
+clock.$O floppy.$O trap.$O:	ureg.h
+conf.$O dosboot.$O main.$O:	dosfs.h
+ether.$O etherscc.$O:	etherif.h
+bootp.$O:	ip.h
+
+clean:V:
+	rm -f *.[$OS] [$OS].out y.tab.? y.debug y.output $TARG qboot k.mx f.mx romboot
+
+nuke-sh:QV:
+		for i in $LIBDIRS
+		do
+			echo "(cd $ROOT/lib$i ; mk SHELLTYPE=$SHELLTYPE SYSTARG=$SYSTARG OBJTYPE=$OBJTYPE nuke)"
+			(cd $ROOT/lib$i; mk 'SHELLTYPE='$SHELLTYPE 'SYSTARG='$SYSTARG 'OBJTYPE='$OBJTYPE nuke)
+		done
+
+nuke-rc nuke-nt:QV:
+		for (i in $LIBDIRS)
+		{
+			echo '@{cd $ROOT/lib$i ; mk SHELLTYPE=$SHELLTYPE SYSTARG=$SYSTARG OBJTYPE=$OBJTYPE nuke}'
+			@{cd $ROOT/lib$i; mk 'SHELLTYPE='$SHELLTYPE 'SYSTARG='$SYSTARG 'OBJTYPE='$OBJTYPE nuke}
+		}
+
+nuke:V:		clean nuke-$SHELLTYPE
--- /dev/null
+++ b/os/boot/rpcg/ms2.c
@@ -1,0 +1,179 @@
+#include <u.h>
+#include <libc.h>
+#include <bio.h>
+#include <mach.h>
+
+void	record(uchar*, int);
+void	usage(void);
+void	dosegment(long, int);
+void trailer(ulong);
+
+enum
+{
+	Recordsize = 32,
+};
+
+int	dsegonly;
+int	supressend;
+int	binary;
+int	addr4;
+ulong	addr;
+ulong 	psize = 4096;
+ulong	startaddr = 0x030000;
+Biobuf 	stdout;
+Biobuf	bio;
+
+void
+main(int argc, char **argv)
+{
+	Dir dir;
+	Fhdr f;
+	int fd;
+
+	ARGBEGIN{
+	case 'd':
+		dsegonly++;
+		break;
+	case 's':
+		supressend++;
+		break;
+	case 'a':
+		addr = strtoul(ARGF(), 0, 0);
+		break;
+	case 'p':
+		psize = strtoul(ARGF(), 0, 0);
+		break;
+	case 'b':
+		binary++;
+		break;
+	case 'S':
+		startaddr = strtoul(ARGF(), 0, 0);
+		break;
+	case '4':
+		addr4++;
+		break;
+	default:
+		usage();
+	}ARGEND
+
+	if(argc != 1)
+		usage();
+
+	Binit(&stdout, 1, OWRITE);
+
+	fd = open(argv[0], OREAD);
+	if(fd < 0) {
+		fprint(2, "ms2: open %s: %r\n", argv[0]);
+		exits("open");
+	}
+
+	if(binary) {
+		if(dirfstat(fd, &dir) < 0) {
+			fprint(2, "ms2: stat failed %r");
+			exits("dirfstat");
+		}
+		Binit(&bio, fd, OREAD);
+		dosegment(0, dir.length);
+		if(supressend == 0)
+			trailer(startaddr);
+		Bterm(&stdout);
+		Bterm(&bio);
+		exits(0);
+	}
+
+	if(crackhdr(fd, &f) == 0){
+		fprint(2, "ms2: bad magic: %r\n");
+		exits("magic");
+	}
+	seek(fd, 0, 0);
+
+	Binit(&bio, fd, OREAD);
+
+	if(dsegonly)
+		dosegment(f.datoff, f.datsz);
+	else {
+		dosegment(f.txtoff, f.txtsz);
+		addr = (addr+(psize-1))&~(psize-1);
+		dosegment(f.datoff, f.datsz);
+	}
+
+	if(supressend == 0)
+		trailer(startaddr);
+
+	Bterm(&stdout);
+	Bterm(&bio);
+	exits(0);
+}
+
+void
+dosegment(long foff, int len)
+{
+	int l, n;
+	uchar buf[2*Recordsize];
+
+	Bseek(&bio, foff, 0);
+	for(;;) {
+		l = len;
+		if(l > Recordsize)
+			l = Recordsize;
+		n = Bread(&bio, buf, l);
+		if(n == 0)
+			break;
+		if(n < 0) {
+			fprint(2, "ms2: read error: %r\n");
+			exits("read");
+		}
+		record(buf, l);
+		len -= l;
+	}
+}
+
+void
+record(uchar *s, int l)
+{
+	int i;
+	ulong cksum;
+
+	if(addr4 || addr & (0xFF<<24)){
+		Bprint(&stdout, "S3%.2X%.8luX", l+5, addr);
+		cksum = l+5;
+		cksum += (addr>>24)&0xff;
+	}else{
+		Bprint(&stdout, "S2%.2X%.6X", l+4, addr);
+		cksum = l+4;
+	}
+	cksum += addr&0xff;
+	cksum += (addr>>8)&0xff;
+	cksum += (addr>>16)&0xff;
+
+	for(i = 0; i < l; i++) {
+		cksum += *s;
+		Bprint(&stdout, "%.2X", *s++);
+	}
+	Bprint(&stdout, "%.2X\n", (~cksum)&0xff);
+	addr += l;
+}
+
+void
+trailer(ulong a)
+{
+	ulong cksum;
+
+	cksum = 0;
+	if(addr4 || a & (0xFF<<24)){
+		Bprint(&stdout, "S7%.8luX", a);
+		cksum += (a>>24)&0xff;
+	}else
+		Bprint(&stdout, "S9%.6X", a);
+	cksum += a&0xff;
+	cksum += (a>>8)&0xff;
+	cksum += (a>>16)&0xff;
+	Bprint(&stdout, "%.2X\n", (~cksum)&0xff);
+}
+
+void
+usage(void)
+{
+	fprint(2, "usage: ms2 [-ds] [-a address] [-p pagesize] ?.out\n");
+	exits("usage");
+}
--- /dev/null
+++ b/os/boot/rpcg/plan9boot.c
@@ -1,0 +1,96 @@
+#include	"u.h"
+#include	"lib.h"
+#include	"mem.h"
+#include	"dat.h"
+#include	"fns.h"
+
+char *premature = "premature EOF\n";
+
+/*
+ *  read in a segment
+ */
+static long
+readseg(int dev, long (*read)(int, void*, long), long len, long addr)
+{
+	char *a;
+	long n, sofar;
+
+	a = (char *)addr;
+	for(sofar = 0; sofar < len; sofar += n){
+		n = 8*1024;
+		if(len - sofar < n)
+			n = len - sofar;
+		n = (*read)(dev, a + sofar, n);
+		if(n <= 0)
+			break;
+		print(".");
+	}
+	return sofar;
+}
+
+/*
+ *  boot
+ */
+int
+plan9boot(int dev, long (*seek)(int, long), long (*read)(int, void*, long))
+{
+	long n;
+	long addr;
+	void (*b)(void);
+	Exec *ep;
+
+	if((*seek)(dev, 0) < 0)
+		return -1;
+
+	/*
+	 *  read header
+	 */
+	ep = (Exec *) ialloc(sizeof(Exec), 0);
+	n = sizeof(Exec);
+	if(readseg(dev, read, n, (long) ep) != n){
+		print(premature);
+		return -1;
+	}
+	if(GLLONG(ep->magic) != Q_MAGIC){
+		print("bad magic 0x%lux not a plan 9 executable!\n", GLLONG(ep->magic));
+		return -1;
+	}
+
+	/*
+	 *  read text
+	 */
+	addr = PADDR(GLLONG(ep->entry));
+	n = GLLONG(ep->text);
+	print("%d", n);
+	if(readseg(dev, read, n, addr) != n){
+		print(premature);
+		return -1;
+	}
+
+	/*
+	 *  read data (starts at first page after kernel)
+	 */
+	addr = PGROUND(addr+n);
+	n = GLLONG(ep->data);
+	print("+%d@%8.8lux", n, addr);
+	if(readseg(dev, read, n, addr) != n){
+		print(premature);
+		return -1;
+	}
+
+	/*
+	 *  bss and entry point
+	 */
+	print("+%d\nstart at 0x%lux\n", GLLONG(ep->bss), GLLONG(ep->entry));
+	uartwait();
+	scc2stop();
+	splhi();
+
+	/*
+	 *  Go to new code. It's up to the program to get its PC relocated to
+	 *  the right place.
+	 */
+	b = (void (*)(void))(PADDR(GLLONG(ep->entry)));
+	(*b)();
+	return 0;
+}
binary files /dev/null b/os/boot/rpcg/qbromrpcg differ
--- /dev/null
+++ b/os/boot/rpcg/qio.c
@@ -1,0 +1,128 @@
+#include "u.h"
+#include "lib.h"
+#include "mem.h"
+#include "dat.h"
+#include "fns.h"
+#include "io.h"
+
+struct Queue {
+	Block*	first;
+	Block*	last;
+	void	(*kick)(void*);
+	void*	arg;
+	long	len;
+};
+
+Block *
+iallocb(int n)
+{
+	Block *b;
+
+	b = (Block*)malloc(sizeof(Block)+n);
+	b->data = (uchar*)b + sizeof(Block);
+	b->rp = b->wp = b->data;
+	b->lim = b->data + n;
+	b->next = 0;
+	b->magic = 0xcafebee0;
+	return b;
+}
+
+void
+freeb(Block *b)
+{
+	if(b){
+		if(b->magic != 0xcafebee0)
+			panic("freeb");
+		b->magic = 0;
+		b->next = (Block*)0xdeadbabe;
+		free(b);
+	}
+}
+
+Queue *
+qopen(int limit, int msg, void (*kick)(void*), void *arg)
+{
+	Queue *q;
+
+	USED(limit, msg);
+	q = (Queue*)malloc(sizeof(Queue));
+	q->first = q->last = 0;
+	q->kick = kick;
+	q->arg = arg;
+	q->len = 0;
+	return q;
+}
+
+Block *
+qget(Queue *q)
+{
+	int s;
+	Block *b;
+
+	s = splhi();
+	if((b = q->first) != 0){
+		q->first = b->next;
+		b->next = 0;
+		q->len -= BLEN(b);
+		if(q->len < 0)
+			panic("qget");
+	}
+	splx(s);
+	return b;
+}
+
+void
+qbwrite(Queue *q, Block *b)
+{
+	int s;
+
+	s = splhi();
+	b->next = 0;
+	if(q->first == 0)
+		q->first = b;
+	else
+		q->last->next = b;
+	q->last = b;
+	q->len += BLEN(b);
+	splx(s);
+	if(q->kick)
+		q->kick(q->arg);
+}
+
+long
+qlen(Queue *q)
+{
+	return q->len;
+}
+
+int
+qbgetc(Queue *q)
+{
+	Block *b;
+	int s, c;
+
+	c = -1;
+	s = splhi();
+	while(c < 0 && (b = q->first) != nil){
+		if(b->rp < b->wp){
+			c = *b->rp++;
+			q->len--;
+		}
+		if(b->rp >= b->wp){
+			q->first = b->next;
+			b->next = nil;
+		}
+	}
+	splx(s);
+	return c;
+}
+
+void
+qbputc(Queue *q, int c)
+{
+	Block *b;
+
+	b = iallocb(1);
+	*b->wp++ = c;
+	qbwrite(q, b);
+}
--- /dev/null
+++ b/os/boot/rpcg/rmap.c
@@ -1,0 +1,104 @@
+#include "u.h"
+#include "lib.h"
+#include "mem.h"
+#include "dat.h"
+#include "fns.h"
+
+void
+mapinit(RMap *rmap, Map *map, int size)
+{
+	lock(rmap);
+	rmap->map = map;
+	rmap->mapend = map+(size/sizeof(Map));
+	unlock(rmap);
+}
+
+void
+mapfree(RMap* rmap, ulong addr, int size)
+{
+	Map *mp;
+	ulong t;
+
+	if(size <= 0)
+		return;
+
+	lock(rmap);
+	for(mp = rmap->map; mp->addr <= addr && mp->size; mp++)
+		;
+
+	if(mp > rmap->map && (mp-1)->addr+(mp-1)->size == addr){
+		(mp-1)->size += size;
+		if(addr+size == mp->addr){
+			(mp-1)->size += mp->size;
+			while(mp->size){
+				mp++;
+				(mp-1)->addr = mp->addr;
+				(mp-1)->size = mp->size;
+			}
+		}
+	}
+	else{
+		if(addr+size == mp->addr && mp->size){
+			mp->addr -= size;
+			mp->size += size;
+		}
+		else do{
+			if(mp >= rmap->mapend){
+				print("mapfree: %s: losing 0x%uX, %d\n",
+					rmap->name, addr, size);
+				break;
+			}
+			t = mp->addr;
+			mp->addr = addr;
+			addr = t;
+			t = mp->size;
+			mp->size = size;
+			mp++;
+		}while(size = t);
+	}
+	unlock(rmap);
+}
+
+ulong
+mapalloc(RMap* rmap, ulong addr, int size, int align)
+{
+	Map *mp;
+	ulong maddr, oaddr;
+
+	lock(rmap);
+	for(mp = rmap->map; mp->size; mp++){
+		maddr = mp->addr;
+
+		if(addr){
+			if(maddr > addr)
+				continue;
+			if(addr+size > maddr+mp->size)
+				break;
+			maddr = addr;
+		}
+
+		if(align > 0)
+			maddr = ((maddr+align-1)/align)*align;
+		if(mp->addr+mp->size-maddr < size)
+			continue;
+
+		oaddr = mp->addr;
+		mp->addr = maddr+size;
+		mp->size -= maddr-oaddr+size;
+		if(mp->size == 0){
+			do{
+				mp++;
+				(mp-1)->addr = mp->addr;
+			}while((mp-1)->size = mp->size);
+		}
+
+		unlock(rmap);
+		if(oaddr != maddr)
+			mapfree(rmap, oaddr, maddr-oaddr);
+
+		return maddr;
+	}
+	unlock(rmap);
+
+	return 0;
+}
--- /dev/null
+++ b/os/boot/rpcg/screen.c
@@ -1,0 +1,242 @@
+#include	"all.h"
+#include	<libg.h>
+#include	<gnot.h>
+
+enum {
+	Colldepth		= 3,
+	Colmaxx		= 640,
+	Colmaxxvis	= 640,
+	Colmaxy		= 480,
+};
+
+#define	MINX	8
+
+extern	GSubfont	defont0;
+
+struct{
+	Point	pos;
+	int	bwid;
+}out;
+
+typedef struct Mode Mode;
+struct Mode {
+	int	x;
+	int	y;
+	int	d;
+	char*	aperture;
+	int	apsize;
+};
+
+GBitmap	gscreen;
+Point	gchar(GBitmap*, Point, GFont*, int, Fcode);
+int	setcolor(ulong, ulong, ulong, ulong);
+static	void	lcdinit(Mode*);
+
+void
+screeninit(void)
+{
+	Mode m;
+
+	m.x = Colmaxx;
+	m.y = Colmaxy;
+	m.d = Colldepth;
+	m.aperture = 0;
+	lcdinit(&m);
+	if(m.aperture == 0)
+		return;
+	gscreen.ldepth = 3;
+	gscreen.base = (ulong*)m.aperture;
+	gscreen.width = Colmaxx/BY2WD;
+	gscreen.r = Rect(0, 0, Colmaxxvis, Colmaxy);
+	gscreen.clipr = gscreen.r;
+	/*
+	 * For now, just use a fixed colormap:
+	 * 0 == white and 255 == black
+	 */
+	setcolor(0, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF);
+	setcolor(255, 0x00000000, 0x00000000, 0x00000000);
+
+	gbitblt(&gscreen, Pt(0, 0), &gscreen, gscreen.r, Zero);
+	out.pos.x = MINX;
+	out.pos.y = 0;
+	out.bwid = defont0.info[' '].width;
+}
+
+void
+screenputc(int c)
+{
+	Fontchar *i;
+	Point p;
+
+	if(gscreen.base == nil)
+		return;
+	switch(c){
+	case '\n':
+		out.pos.x = MINX;
+		out.pos.y += defont0.height;
+		if(out.pos.y > gscreen.r.max.y-defont0.height)
+			out.pos.y = gscreen.r.min.y;
+		gbitblt(&gscreen, Pt(0, out.pos.y), &gscreen,
+		  Rect(0, out.pos.y, gscreen.r.max.x, out.pos.y+2*defont0.height),
+		  Zero);
+		break;
+	case '\t':
+		out.pos.x += (8-((out.pos.x-MINX)/out.bwid&7))*out.bwid;
+		if(out.pos.x >= gscreen.r.max.x)
+			screenputc('\n');
+		break;
+	case '\b':
+		if(out.pos.x >= out.bwid+MINX){
+			out.pos.x -= out.bwid;
+			screenputc(' ');
+			out.pos.x -= out.bwid;
+		}
+		break;
+	default:
+		if(out.pos.x >= gscreen.r.max.x-out.bwid)
+			screenputc('\n');
+		c &= 0x7f;
+		if(c <= 0 || c >= defont0.n)
+			break;
+		i = defont0.info + c;
+		p = out.pos;
+		gbitblt(&gscreen, Pt(p.x+i->left, p.y), defont0.bits,
+			Rect(i[0].x, 0, i[1].x, defont0.height),
+			S);
+		out.pos.x = p.x + i->width;
+		break;
+	}
+}
+
+void
+screenputs(char *s, int n)
+{
+	while(n-- > 0)
+		screenputc(*s++);
+}
+
+/*
+ * See section 5.2.1 (page 5-6) of the MPC823 manual
+ */
+static uchar lcdclock[17] = {	/* (a<<2)|b => divisor of (1<<a)*((b<<1)+1) */
+	0, 0, (1<<2), 1,
+	(2<<2), 2, (1<<2)|1, 3,
+	(3<<2), (1<<2)|2, (1<<2)|2, (2<<2)|1,
+	(2<<2)|1, (1<<2)|3, (1<<2)|3, (4<<2),
+	(4<<2)
+};
+	
+/*
+ * support for the Sharp LQ64D341 TFT colour display
+ */
+
+enum {
+	COLS = 640,
+	ROWS = 480,
+	LDEPTH = 3,	/* screen depth */
+	LCDFREQ = 25000000,
+
+	/* lccr */
+	ClockLow = 1<<11,
+	OELow = 1<<10,
+	HsyncLow = 1<<9,
+	VsyncLow = 1<<8,
+	DataLow = 1<<7,
+	Passive8 = 1<<4,
+	DualScan = 1<<3,
+	IsColour = 1<<2,
+	IsTFT = 1<<1,
+	Enable = 1<<0,
+
+	/* lchcr */
+	BigEndian = 1<<24,
+	AT7 = 7<<21,	/* access type */
+
+	/* sdcr */
+	LAM = 1<<6,	/* ``LCD aggressive mode'' */
+};
+
+/*
+ * TO DO: most of the data could come from a table
+ */
+static void
+lcdinit(Mode *mode)
+{
+	IMM *io;
+	int i, d;
+	long hz;
+
+	io = m->iomem;
+	mode->y = ROWS;
+	mode->x = COLS;
+	mode->d = LDEPTH;
+	mode->aperture = ialloc(mode->x*mode->y, 16);
+	mode->apsize = mode->x*mode->y;
+
+	io->sdcr &= ~LAM;	/* MPC823 errata: turn off LAM before disabling controller */
+	io->lcfaa = PADDR(mode->aperture);
+	io->lccr = (((mode->x*mode->y*(1<<LDEPTH)+127)/128) << 17) | (LDEPTH << 5) | IsColour | IsTFT | OELow | VsyncLow | ClockLow;
+
+	switch(LDEPTH){
+	default:
+	case 0:
+		/* monochrome/greyscale identity map */
+		for(i=0; i<16; i++)
+			io->lcdmap[i] = i;
+		break;
+	case 2:
+		/* 4-bit grey scale map */
+		for(i=0; i<16; i++)
+			io->lcdmap[0] = (i<<8)|(i<<4)|i;
+		break;
+	case 3:
+		/* 8-bit linear map */
+		for(i=0; i<256; i++)
+			io->lcdmap[i] = (i<<8)|(i<<4)|i;
+		break;
+	}
+
+	io->lcvcr = (mode->y << 11) | (1<<28) | 33;	/* 2 line vsync pulse, 34 line wait between frames */
+	io->lchcr = (mode->x<<10) | BigEndian | 228;	/* clock cycles between lines */
+
+	hz = m->cpuhz;
+	d = hz/LCDFREQ;
+	if(hz/d > LCDFREQ)
+		d++;
+	if(d >= 16)
+		d = 16;
+
+	/*
+	 * enable LCD outputs
+	 */
+	io->pddat = 0;
+	io->pdpar = 0x1fff;
+io->pdpar &= ~SIBIT(6);	/* 823 bug fix? */
+	io->pddir = 0x1fff;
+	io->pbpar |= IBIT(31) | IBIT(19) | IBIT(17);
+	io->pbdir |= IBIT(31) | IBIT(19) | IBIT(17);
+	io->pbodr &= ~(IBIT(31) | IBIT(19) | IBIT(17));
+
+	eieio();
+	io->sccrk = KEEP_ALIVE_KEY;
+	eieio();
+	io->sccr  = (io->sccr & ~0x1F) | lcdclock[d];
+	eieio();
+	io->sccrk = ~KEEP_ALIVE_KEY;
+	eieio();
+	gscreen.width = gscreen.width;	/* access external memory before enabling (mpc823 errata) */
+	io->lcsr = 7;	/* clear status */
+	eieio();
+	io->lccr |= Enable;
+	archbacklight(1);
+}
+
+int
+setcolor(ulong p, ulong r, ulong g, ulong b)
+{
+	r >>= 28;
+	g >>= 28;
+	b >>= 28;
+	m->iomem->lcdmap[~p&0xFF] = (r<<8) | (g<<4) | b;	/* TO DO: it's a function of the ldepth */
+	return 1;
+}
binary files /dev/null b/os/boot/rpcg/sload differ
--- /dev/null
+++ b/os/boot/rpcg/sload.c
@@ -1,0 +1,71 @@
+/*
+ * send S records to rpcg
+ */
+
+#include <u.h>
+#include <libc.h>
+#include <bio.h>
+
+static	int	dbg;
+static	char	buf[2048];
+static	int	run=1;
+static	void	stuffbym(char*, int, int);
+static	void	getdot(void);
+
+void
+main(int argc, char **argv)
+{
+	int n;
+	char *l;
+	Biobuf *f;
+	static int p;
+
+	ARGBEGIN{
+	case 'd': dbg++; break;
+	case 'n': run=0; break;
+	}ARGEND
+
+	f = Bopen(*argv? *argv: "k.mx", OREAD);
+	if(f == 0) {
+		fprint(2, "sload: cannot open k.mx: %r\n");
+		exits("sload");
+	}
+	getdot();
+	while((l = Brdline(f, '\n')) != 0) {
+		l[Blinelen(f)-1] = '\r';
+		stuffbym(l, Blinelen(f), 16);
+		getdot();
+		if(++p % 25 == 0)
+			write(2, ".", 1);
+	}
+	exits(0);
+}
+
+static void
+stuffbym(char *l, int n, int m)
+{
+	int nr, ns;
+
+	while(n > 0) {
+		ns = n;
+		if(ns > m)
+			ns = m;
+		write(1, l, ns);
+		l += ns;
+		n -= ns;
+	}
+}
+
+static void
+getdot(void)
+{
+	char c;
+
+	for(;;){
+		if(read(0, &c, 1) != 1)
+			exits("bang");
+		write(2, &c, 1);
+		if(c == '.')
+			break;
+	}
+}
--- /dev/null
+++ b/os/boot/rpcg/squeeze.h
@@ -1,0 +1,34 @@
+
+/*
+ * squeezed file format:
+ *	Sqhdr
+ *	original Exec header
+ *	two Squeeze tables
+ *	squeezed segment
+ *	unsqueezed segment, if any
+ */
+#define	SQMAGIC	(ulong)0xFEEF0F1E
+
+typedef struct Sqhdr Sqhdr;
+struct Sqhdr {
+	uchar	magic[4];	/* SQMAGIC */
+	uchar	text[4];	/* squeezed length of text (excluding tables) */
+	uchar	data[4];	/* squeezed length of data (excluding tables) */
+	uchar	asis[4];	/* length of unsqueezed segment */
+	uchar	toptxt[4];	/* value for 0 encoding in text */
+	uchar	topdat[4];	/* value for 0 encoding in data */
+	uchar	sum[4];	/* simple checksum of unsqueezed data */
+	uchar	flags[4];
+};
+#define	SQHDRLEN	(8*4)
+
+/*
+ * certain power instruction types are rearranged by sqz
+ * so as to move the variable part of the instruction word to the
+ * low order bits.  note that the mapping is its own inverse.
+ */
+#define	QREMAP(X)\
+	switch((X)>>26){\
+	case 19: case 31: case 59: case 63:\
+		(X) = (((X) & 0xFC00F801) | (((X)>>15)&0x7FE) | (((X)&0x7FE)<<15));\
+	}
--- /dev/null
+++ b/os/boot/rpcg/trap.c
@@ -1,0 +1,233 @@
+#include "boot.h"
+
+enum 
+{
+	Maxhandler=	32+16,		/* max number of interrupt handlers */
+};
+
+typedef struct Handler	Handler;
+struct Handler
+{
+	void	(*r)(Ureg*, void*);
+	void	*arg;
+	Handler	*next;
+	int	edge;
+};
+
+struct
+{
+	Handler	*ivec[128];
+	Handler	h[Maxhandler];
+	int	free;
+} halloc;
+
+char	*excname[] = {
+	"reserved 0",
+	"system reset",
+	"machine check",
+	"data access",
+	"instruction access",
+	"external interrupt",
+	"alignment",
+	"program exception",
+	"floating-point unavailable",
+	"decrementer",
+	"reserved A",
+	"reserved B",
+	"system call",
+	"trace trap",
+	"floating point assist",
+	"reserved F",
+	"software emulation",
+	"ITLB miss",
+	"DTLB miss",
+	"ITLB error",
+	"DTLB error",
+};
+
+char *regname[]={
+	"CAUSE",	"SRR1",
+	"PC",		"GOK",
+	"LR",		"CR",
+	"XER",	"CTR",
+	"R0",		"R1",
+	"R2",		"R3",
+	"R4",		"R5",
+	"R6",		"R7",
+	"R8",		"R9",
+	"R10",	"R11",
+	"R12",	"R13",
+	"R14",	"R15",
+	"R16",	"R17",
+	"R18",	"R19",
+	"R20",	"R21",
+	"R22",	"R23",
+	"R24",	"R25",
+	"R26",	"R27",
+	"R28",	"R29",
+	"R30",	"R31",
+};
+
+static	void	intr(Ureg*);
+
+void
+sethvec(int v, void (*r)(void))
+{
+	ulong *vp, pa, o;
+
+	if((ulong)r & 3)
+		panic("sethvec");
+	vp = (ulong*)KADDR(v);
+	vp[0] = 0x7c1043a6;	/* MOVW R0, SPR(SPRG0) */
+	vp[1] = 0x7c0802a6;	/* MOVW LR, R0 */
+	vp[2] = 0x7c1243a6;	/* MOVW R0, SPR(SPRG2) */
+	pa = PADDR(r);
+	o = pa >> 25;
+	if(o != 0 && o != 0x7F){
+		/* a branch too far: running from ROM */
+		vp[3] = (15<<26)|(pa>>16);	/* MOVW $r&~0xFFFF, R0 */
+		vp[4] = (24<<26)|(pa&0xFFFF);	/* OR $r&0xFFFF, R0 */
+		vp[5] = 0x7c0803a6;	/* MOVW	R0, LR */
+		vp[6] = 0x4e800021;	/* BL (LR) */
+	}else
+		vp[3] = (18<<26)|(pa&0x3FFFFFC)|3;	/* bla */
+}
+
+#define	LEV(n)	(((n)<<1)|1)
+#define	IRQ(n)	(((n)<<1)|0)
+
+void
+setvec(int v, void (*r)(Ureg*, void*), void *arg)
+{
+	Handler *h;
+	IMM *io;
+
+	if(halloc.free >= Maxhandler)
+		panic("out of interrupt handlers");
+	v -= VectorPIC;
+	h = &halloc.h[halloc.free++];
+	h->next = halloc.ivec[v];
+	h->r = r;
+	h->arg = arg;
+	halloc.ivec[v] = h;
+
+	/*
+	 * enable corresponding interrupt in SIU/CPM
+	 */
+
+	io = m->iomem;
+	if(v >= VectorCPIC){
+		v -= VectorCPIC;
+		io->cimr |= 1<<(v&0x1F);
+	}
+	else if(v >= VectorIRQ)
+		io->simask |= 1<<(31-IRQ(v&7));
+	else
+		io->simask |= 1<<(31-LEV(v));
+}
+
+void
+trapinit(void)
+{
+	int i;
+	IMM *io;
+
+	io = m->iomem;
+	io->sypcr &= ~(3<<2);	/* disable watchdog (821/823) */
+	io->simask = 0;	/* mask all */
+	io->siel = ~0;	/* edge sensitive, wake on all */
+	io->cicr = 0;	/* disable CPM interrupts */
+	io->cipr = ~0;	/* clear all interrupts */
+	io->cimr = 0;	/* mask all events */
+	io->cicr = (0xE1<<16)|(CPIClevel<<13)|(0x1F<<8);
+	io->cicr |= 1 << 7;	/* enable */
+	io->tbscrk = KEEP_ALIVE_KEY;
+	io->tbscr = 1;	/* TBE */
+	io->simask |= 1<<(31-LEV(CPIClevel));	/* CPM's level */
+	io->tbk = KEEP_ALIVE_KEY;
+	eieio();
+	putdec(~0);
+
+	/*
+	 * set all exceptions to trap
+	 */
+	for(i = 0x0; i < 0x3000; i += 0x100)
+		sethvec(i, exception);
+}
+
+void
+dumpregs(Ureg *ur)
+{
+	int i;
+	ulong *l;
+	l = &ur->cause;
+	for(i=0; i<sizeof regname/sizeof(char*); i+=2, l+=2)
+		print("%s\t%.8lux\t%s\t%.8lux\n", regname[i], l[0], regname[i+1], l[1]);
+}
+
+void
+trap(Ureg *ur)
+{
+	int c;
+
+	c = ur->cause >> 8;
+	switch(c){
+	default:
+		{extern int predawn; predawn = 1;}
+		if(c < 0 || c >= nelem(excname))
+			print("exception/interrupt #%x\n", c);
+		else
+			print("exception %s\n", excname[c]);
+		dumpregs(ur);
+		/* spllo(); */
+		print("^P to reset\n");
+		for(;;)
+			;
+
+	case 0x09:	/* decrementer */
+		clockintr(ur, 0);
+		return;
+
+	case 0x05:	/* external interrupt */
+		intr(ur);
+		break;
+	}
+}
+
+static void
+intr(Ureg *ur)
+{
+	int b, v;
+	Handler *h;
+	IMM *io;
+
+	io = m->iomem;
+	b = io->sivec>>2;
+	v = b>>1;
+	if(b & 1) {
+		if(v == CPIClevel){
+			io->civr = 1;
+			eieio();
+			v = VectorCPIC+(io->civr>>11);
+		}
+	}else
+		v += VectorIRQ;
+	h = halloc.ivec[v];
+	if(h == nil){
+		for(;;)
+			;
+		//print("unknown interrupt %d pc=0x%lux\n", v, ur->pc);
+		return;
+	}
+	if(h->edge)
+		io->sipend |= 1<<(31-b);
+	/*
+	 *  call the interrupt handlers
+	 */
+	do {
+		(*h->r)(ur, h->arg);
+		h = h->next;
+	} while(h != nil);
+	if(v >= VectorCPIC)
+		io->cisr |= 1<<(v-VectorCPIC);
+}
--- /dev/null
+++ b/os/boot/rpcg/uartboot.c
@@ -1,0 +1,189 @@
+#include "boot.h"
+
+/*
+ * this doesn't yet use the crc
+ */
+
+typedef struct Uboot Uboot;
+struct Uboot {
+	Queue*	iq;
+	Block*	partial;
+	ulong	csum;
+	long	bno;
+	uchar	buf[64];
+	int	nleft;
+	int	ntimeout;
+};
+
+static	Uboot	uboot;
+ulong	crc32(void *buf, int n, ulong crc);
+
+static void
+uartbrecv(uchar *p, int n)
+{
+	Uboot *ub;
+	Block *b;
+
+	ub = &uboot;
+	if(n > 0 && ub->iq != nil){
+		b = iallocb(n);
+		memmove(b->wp, p, n);
+		b->wp += n;
+		qbwrite(ub->iq, b);
+	}
+}
+
+int
+uartinit(void)
+{
+	return 1<<0;
+}
+
+Partition*
+setuartpart(int, char *s)
+{
+	static Partition pp[1];
+
+	if(strcmp(s, "boot") != 0 && strcmp(s, "disk") != 0)
+		return 0;
+	pp[0].start = 0;
+	pp[0].end = 2*1024*1024;
+	strcpy(pp[0].name, "boot");
+	return pp;
+}
+
+long
+uartseek(int, long)
+{
+	/* start the boot */
+	if(uboot.iq == nil)
+		uboot.iq = qopen(64*1024, 0, 0, 0);
+	if(uboot.partial){
+		freeb(uboot.partial);
+		uboot.partial = 0;
+	}
+	print("uart: start transmission\n");
+	uartsetboot(uartbrecv);
+	uboot.csum = ~0;
+	uboot.bno = 0;
+	uboot.nleft = 0;
+	uboot.ntimeout = 0;
+	return 0;
+}
+
+static long
+uartreadn(void *buf, int nb)
+{
+	ulong start;
+	Uboot *ub;
+	int l;
+	Block *b;
+	uchar *p;
+
+	p = buf;
+	ub = &uboot;
+	start = m->ticks;
+	while(nb > 0){
+		b = ub->partial;
+		ub->partial = nil;
+		if(b == nil){
+			ub->ntimeout = 0;
+			while((b = qget(ub->iq)) == 0){
+				if(TK2MS(m->ticks - start) >= 15*1000){
+					if(++ub->ntimeout >= 3){
+						print("uart: timeout\n");
+						return 0;
+					}
+					uartputs("n", 1);
+				}
+			}
+		}
+		l = BLEN(b);
+		if(l > nb)
+			l = nb;
+		memmove(p, b->rp, l);
+		b->rp += l;
+		if(b->rp >= b->wp)
+			freeb(b);
+		else
+			ub->partial = b;
+		nb -= l;
+		p += l;
+	}
+	return p-(uchar*)buf;
+}
+
+long
+uartread(int, void *buf, long n)
+{
+	uchar *p;
+	int l;
+	static uchar lbuf[64];
+
+	p = buf;
+	if((l = uboot.nleft) > 0){
+		if(l > n)
+			l = n;
+		uboot.nleft -= l;
+		memmove(p, uboot.buf, l);
+		p += l;
+		n -= l;
+	}
+	while(n > 0){
+		l = uartreadn(lbuf, sizeof(lbuf));
+		if(l < sizeof(lbuf))
+			return 0;
+		if(l > n){
+			uboot.nleft = l-n;
+			memmove(uboot.buf, lbuf+n, uboot.nleft);
+			l = n;
+		}
+		memmove(p, lbuf, l);
+		n -= l;
+		p += l;
+		uboot.bno++;
+		uartputs("y", 1);
+	}
+	return p-(uchar*)buf;
+}
+
+/*
+ * from Rob Warnock
+ */
+static	ulong	crc32tab[256];	/* initialised on first call to crc32 */
+
+enum {
+	CRC32POLY = 0x04c11db7     /* AUTODIN II, Ethernet, & FDDI */
+};
+
+/*
+ * Build auxiliary table for parallel byte-at-a-time CRC-32.
+ */
+static void
+initcrc32(void)
+{
+	int i, j;
+	ulong c;
+
+	for(i = 0; i < 256; i++) {
+		for(c = i << 24, j = 8; j > 0; j--)
+			if(c & (1<<31))
+				c = (c<<1) ^ CRC32POLY;
+			else
+				c <<= 1;
+		crc32tab[i] = c;
+	}
+}
+
+ulong
+crc32(void *buf, int n, ulong crc)
+{
+	uchar *p;
+
+	if(crc32tab[1] == 0)
+		initcrc32();
+	crc = ~crc;
+	for(p = buf; --n >= 0;)
+		crc = (crc << 8) ^ crc32tab[(crc >> 24) ^ *p++];
+	return ~crc;
+}
--- /dev/null
+++ b/os/boot/rpcg/ureg.h
@@ -1,0 +1,43 @@
+struct Ureg
+{
+	ulong	cause;
+	union { ulong	srr1; ulong status;};
+	ulong	pc;	/* SRR0 */
+	ulong	pad;
+	ulong	lr;
+	ulong	cr;
+	ulong	xer;
+	ulong	ctr;
+	ulong	r0;
+	union{ ulong r1;	ulong	sp;	ulong	usp; };
+	ulong	r2;
+	ulong	r3;
+	ulong	r4;
+	ulong	r5;
+	ulong	r6;
+	ulong	r7;
+	ulong	r8;
+	ulong	r9;
+	ulong	r10;
+	ulong	r11;
+	ulong	r12;
+	ulong	r13;
+	ulong	r14;
+	ulong	r15;
+	ulong	r16;
+	ulong	r17;
+	ulong	r18;
+	ulong	r19;
+	ulong	r20;
+	ulong	r21;
+	ulong	r22;
+	ulong	r23;
+	ulong	r24;
+	ulong	r25;
+	ulong	r26;
+	ulong	r27;
+	ulong	r28;
+	ulong	r29;
+	ulong	r30;
+	ulong	r31;
+};
--- /dev/null
+++ b/os/boot/rpcg/zqs.c
@@ -1,0 +1,234 @@
+#include "boot.h"
+#include "squeeze.h"
+
+/*
+ * for details of `unsqueeze' see:
+ *
+ * %A Mark Taunton
+ * %T Compressed Executables: An Exercise in Thinking Small
+ * %P 385-404
+ * %I USENIX
+ * %B USENIX Conference Proceedings
+ * %D Summer 1991
+ * %C Nashville, TN
+ *
+ * several of the unimplemented improvements described in the paper
+ * have been implemented here
+ *
+ * there is a further transformation on the powerpc (QFLAG!=0) to shuffle bits
+ * in certain instructions so as to push the fixed bits to the top of the word.
+ */
+
+#define	EXECHDRLEN	(8*4)
+
+typedef struct Squeeze Squeeze;
+struct Squeeze {
+	int	n;
+	ulong	tab[7*256];
+};
+
+#define	GET4(p)	(((((((p)[0]<<8)|(p)[1])<<8)|(p)[2])<<8)|(p)[3])
+
+/*
+ * for speed of unsqueezing from Flash, certain checks are
+ * not done inside the loop (as they would be in the unsqueeze program zqs),
+ * but instead the checksum is expected to catch corrupted files.
+ * in fact the Squeeze array bounds can't be exceeded in practice
+ * because the tables are always full for a squeezed kernel.
+ */
+enum {
+	QFLAG = 1,	/* invert powerpc-specific code transformation */
+	CHECK = 0,	/* check precise bounds in Squeeze array (otherwise checksum detects error) */
+};
+
+static	ulong	chksum;
+static	int	rdtab(Block*, Squeeze*, int);
+static	ulong*	unsqueeze(ulong*, uchar*, uchar*, Squeeze*, Squeeze*, ulong);
+static	uchar*	unsqzseg(uchar*, Block*, long, long, char*);
+static	Alarm*	unsqzal;
+
+int
+issqueezed(uchar *b)
+{
+	return GET4(b) == SQMAGIC? GET4(b+SQHDRLEN): 0;
+}
+
+static void
+unsqzdot(Alarm*)
+{
+	unsqzal = alarm(500, unsqzdot, nil);
+	print(".");
+}
+
+long
+unsqueezef(Block *b, ulong *entryp)
+{
+	uchar *loada, *wp;
+	ulong toptxt, topdat, oldsum;
+	long asis, nst, nsd;
+	Sqhdr *sqh;
+	Exec *ex;
+
+	if(BLEN(b) < SQHDRLEN+EXECHDRLEN)
+		return -1;
+	sqh = (Sqhdr*)b->rp;
+	if(GET4(sqh->magic) != SQMAGIC)
+		return -1;
+	chksum = 0;
+	toptxt = GET4(sqh->toptxt);
+	topdat = GET4(sqh->topdat);
+	oldsum = GET4(sqh->sum);
+	asis = GET4(sqh->asis);
+	nst = GET4(sqh->text);
+	nsd = GET4(sqh->data);
+	b->rp += SQHDRLEN;
+	ex = (Exec*)b->rp;
+	if(GET4(ex->magic) != Q_MAGIC){
+		print("zqs: not powerPC executable\n");
+		return -1;
+	}
+	*entryp = GET4(ex->entry);
+	b->rp += EXECHDRLEN;
+	loada = KADDR(PADDR(*entryp));
+	wp = unsqzseg(loada, b, nst, toptxt, "text");
+	if(wp == nil){
+		print("zqs: format error\n");
+		return -1;
+	}
+	if(nsd){
+		wp = (uchar*)PGROUND((ulong)wp);
+		wp = unsqzseg(wp, b, nsd, topdat, "data");
+		if(wp == nil){
+			print("zqs: format error\n");
+			return -1;
+		}
+	}
+	if(asis){
+		memmove(wp, b->rp, asis);
+		wp += asis;
+		b->rp += asis;
+	}
+	if(chksum != oldsum){
+		print("\nsqueezed kernel: checksum error: %8.8lux need %8.8lux\n", chksum, oldsum);
+		return -1;
+	}
+	return wp-loada;
+}
+
+static uchar *
+unsqzseg(uchar *wp, Block *b, long ns, long top, char *what)
+{
+	static Squeeze sq3, sq4;
+
+	print("unpack %s %8.8lux %lud:", what, wp, ns);
+	if(ns == 0)
+		return wp;
+	if(rdtab(b, &sq3, 0) < 0)
+		return nil;
+	if(rdtab(b, &sq4, 8) < 0)
+		return nil;
+	if(BLEN(b) < ns){
+		print(" **size error\n");
+		return nil;
+	}
+	unsqzal = alarm(500, unsqzdot, nil);
+	wp = (uchar*)unsqueeze((ulong*)wp, b->rp, b->rp+ns, &sq3, &sq4, top);
+	cancel(unsqzal);
+	unsqzal = nil;
+	print("\n");
+	if(wp == nil){
+		print("zqs: corrupt squeezed data stream\n");
+		return nil;
+	}
+	b->rp += ns;
+	return wp;
+}
+
+static ulong*
+unsqueeze(ulong *wp, uchar *rp, uchar *ep, Squeeze *sq3, Squeeze *sq4, ulong top)
+{
+	ulong nx, csum;
+	int code, n;
+
+	if(QFLAG){
+		QREMAP(top);	/* adjust top just once, outside the loop */
+	}
+	csum = chksum;
+	while(rp < ep){
+		/* no function calls within this loop for speed */
+		code = *rp;
+		rp++;
+		n = 0;
+		nx = code>>4;
+		do{
+			if(nx == 0){
+				nx = top;
+			}else{
+				if(nx==1){
+					nx = (((((rp[3]<<8)|rp[2])<<8)|rp[1])<<8)|rp[0];
+					rp += 4;
+				}else if(nx <= 8){	/* 2 to 8 */
+					nx = ((nx-2)<<8) | rp[0];
+					if(CHECK && nx >= sq4->n)
+						return nil;	/* corrupted file */
+					nx = sq4->tab[nx] | rp[1];
+					rp += 2;
+				}else{	/* 9 to 15 */
+					nx = ((nx-9)<<8) | rp[0];
+					if(CHECK && nx >= sq3->n)
+						return nil;	/* corrupted file */
+					nx = sq3->tab[nx];
+					rp++;
+				}
+				if(rp > ep)
+					return nil;	/* corrupted file */
+				if(QFLAG){
+					QREMAP(nx);
+				}
+			}
+			*wp = nx;
+			wp++;
+			csum += nx;
+			nx = code & 0xF;
+		}while(++n == 1);
+	}
+	chksum = csum;
+	return wp;
+}
+
+static int
+rdtab(Block *b, Squeeze *sq, int shift)
+{
+	uchar *p, *ep;
+	ulong v, w;
+	int i;
+
+	if(BLEN(b) < 2)
+		return -1;
+	i = (b->rp[0]<<8) | b->rp[1];
+	if(1)
+		print(" T%d", i);
+	b->rp += 2;
+	if((i -= 2) > 0){
+		if(BLEN(b) < i)
+			return -1;
+	}
+	sq->n = 0;
+	p = b->rp;
+	ep = b->rp+i;
+	b->rp += i;
+	v = 0;
+	while(p < ep){
+		w = 0;
+		do{
+			if(p >= ep)
+				return -1;
+			w = (w<<7) | (*p & 0x7F);
+		}while(*p++ & 0x80);
+		v += w;
+		if(0)
+			print("%d %8.8lux %8.8lux\n", sq->n, v, w);
+		sq->tab[sq->n++] = v<<shift;
+	}
+	return 0;
+}
--- /dev/null
+++ b/os/cerf1110/Mk
@@ -1,0 +1,8 @@
+#!/bin/rc
+rfork ne
+ROOT=/usr/inferno
+fn cd
+NPROC=3
+path=(/usr/inferno/Plan9/$cputype/bin $path)
+#bind /usr/inferno/mkconfig.dist /usr/inferno/mkconfig
+exec mk $*
--- /dev/null
+++ b/os/cerf1110/NOTICE
@@ -1,0 +1,2 @@
+Inferno® Copyright © 1996-1999 Lucent Technologies Inc.  All rights reserved.
+Cerfcube 1110 Inferno port Copyright © 2000,2003 Vita Nuova Holdings Limited.  All rights reserved.
--- /dev/null
+++ b/os/cerf1110/README
@@ -1,0 +1,22 @@
+Booting Inferno on a Cerf cube
+
+Build the /usr/inferno/os/cerf1110 kernel into /usr/inferno/os/cerf1110/icerf:
+	mk
+It uses common SA1110 code in ../sa1110, as well as ../port etc.
+
+Make that icerf file available to the cerf cube by tftp.  How you do that depends on
+your host system.
+
+It should then be easy:
+
+1. Reset the cerf cube (power off/on), and quickly, during `Waiting for RTC to stabilize'
+	hit a key.
+
+2. type
+	tftp /usr/inferno/os/cerf1110/icerf c0008000
+    with appropriate substitution for file name.
+
+3. on success
+	boot c0008000 0 0
+
+it should run.
--- /dev/null
+++ b/os/cerf1110/archcerf.c
@@ -1,0 +1,177 @@
+/*
+ * Intrinsyc Cerf cube
+ */
+#include	"u.h"
+#include	"../port/lib.h"
+#include	"mem.h"
+#include	"dat.h"
+#include	"fns.h"
+#include	"../port/error.h"
+#include	"io.h"
+#include	"draw.h"
+#include	<memdraw.h>
+
+#include "../port/netif.h"
+#include "etherif.h"
+#include	"../port/flashif.h"
+
+enum {
+	/* Cerf GPIO assignment */
+	LED0 = 1<<0,	/* active high */
+	LED1 = 1<<1,
+	LED2 = 1<<2,
+	LED3 = 1<<3,
+	/* 4 to 15 appear on J2 */
+	CFBVD1 = 1<<20,
+	CFBVD2 = 1<<19,
+	CFReset = 1<<21,	/* active low for IDE mode; active high for IO or memory mode */
+	CFRdypin = 22,
+	CFRdy = 1<<CFRdypin,	/* RDY/nBSY */
+	CFnCDxpin = 23,
+	CFnCDx = 1<<CFnCDxpin,	/* low => card inserted */
+	EnableRS232In = 1<<24,
+	EnableRS232Out = 1<<25,
+	/* CS8900 interrupt on 26, active high */
+	/* CS8900 nHWSLEEP on 27 */
+};
+
+void
+archreset(void)
+{
+	GpioReg *g = GPIOREG;
+
+	g->grer = 0;
+	g->gfer = 0;
+	g->gedr = g->gedr;
+	g->gpdr = 0;
+
+	g->gpdr = EnableRS232In | EnableRS232Out | CFReset;
+	g->gpsr = EnableRS232In | EnableRS232Out;
+
+	GPCLKREG->gpclkr0 |= 1;	/* SUS=1 for uart on serial 1 */
+}
+
+void
+archconfinit(void)
+{
+	int w;
+
+	conf.topofmem = 0xC0000000+32*MB;
+	w = PMGRREG->ppcr & 0x1f;
+	m->cpuhz = CLOCKFREQ*(w*4+16);
+
+	conf.useminicache = 1;
+	conf.portrait = 1;	/* should take from param flash or allow dynamic change */
+}
+
+void
+archconsole(void)
+{
+	uartspecial(0, 38400, 'n', &kbdq, &printq, kbdcr2nl);
+}
+
+void
+archuartpower(int, int)
+{
+}
+
+void
+kbdinit(void)
+{
+}
+
+void
+archreboot(void)
+{
+	dcflushall();
+	GPIOREG->gedr = 1<<0;
+	mmuputctl(mmugetctl() & ~CpCaltivec);	/* restore bootstrap's vectors */
+	RESETREG->rsrr = 1;	/* software reset */
+	for(;;)
+		spllo();
+}
+
+void
+archflashwp(Flash*, int)
+{
+}
+
+/*
+ * for devflash.c:/^flashreset
+ * retrieve flash type, virtual base and length and return 0;
+ * return -1 on error (no flash)
+ */
+int
+archflashreset(int bank, Flash *f)
+{
+	if(bank != 0)
+		return -1;
+	f->type = "cfi16";
+	f->addr = KADDR(FLASHMEM);
+	f->size = 0;
+	f->width = 2;
+	return 0;
+}
+
+/*
+ * pcmcia
+ */
+int
+pcmpowered(int slotno)
+{
+	if(slotno)
+		return 0;
+	return 3;
+}
+
+void
+pcmpower(int slotno, int on)
+{
+	USED(slotno, on);
+}
+
+void
+pcmreset(int slot)
+{
+	if(slot != 0)
+		return;
+	GPIOREG->gpsr = CFReset;
+	delay(100);
+	GPIOREG->gpcr = CFReset;
+}
+
+int
+pcmpin(int slot, int type)
+{
+	if(slot)
+		return -1;
+	switch(type){
+	case PCMready:
+		return CFRdypin;
+	case PCMeject:
+		return CFnCDxpin;
+	case PCMstschng:
+		return -1;
+	}
+}
+
+void
+pcmsetvpp(int slot, int vpp)
+{
+	USED(slot, vpp);
+}
+
+/*
+ * set ether parameters: the contents should be derived from EEPROM or NVRAM
+ */
+int
+archether(int ctlno, Ether *ether)
+{
+	if(ctlno > 0)
+		return -1;
+	sprint(ether->type, "CS8900");
+	ether->nopt = 0;
+	ether->irq = 26;	 /* GPIO */
+	ether->itype = BusGPIOrising;
+	return 1;
+}
--- /dev/null
+++ b/os/cerf1110/cerf
@@ -1,0 +1,162 @@
+dev
+	root
+	cons archcerf
+	env
+	gpio
+	mnt
+	pipe
+	prog
+	rtc
+	srv
+	dup
+	ssl
+	cap
+	sign
+#	draw	screen
+#	pointer
+	uart
+	ip	bootp ip ipv6 ipaux iproute arp netlog ptclbsum iprouter plan9 nullmedium pktmedium netaux
+	flash
+	ftl
+	pcmcia	cis
+	ata
+	ether netif netaux ethermedium
+
+	cerf
+#	kprof
+
+ip
+	il
+	tcp
+	udp
+#	rudp
+#	igmp
+	ipifc
+	icmp
+	icmp6
+#	ipmux
+
+link	
+	flashcfi16
+	ether8900
+
+lib
+	interp
+#	tk
+#	draw
+#	memlayer
+#	memdraw
+	keyring
+	sec
+	mp
+	math
+	kern
+
+mod
+	math
+	sys
+#	draw
+#	tk
+	keyring
+	crypt
+	ipints
+
+
+port
+	alarm
+	alloc
+	allocb
+	chan
+	dev
+	dial
+	dis
+	discall
+	exception
+	exportfs
+	inferno
+	latin1
+	nocache
+	nodynld
+	parse
+	pgrp
+	print
+	proc
+	qio
+	qlock
+	random
+	sysfile
+	taslock
+	xalloc
+
+code
+	int kernel_pool_pcnt = 10;
+	int main_pool_pcnt = 40;
+	int heap_pool_pcnt = 40;
+	int image_pool_pcnt = 0;
+	int cflag = 0;	/* for JIT */
+
+	int consoleprint = 1;
+	int redirectconsole = 1;
+	char debug_keys = 1;
+	int panicreset = 0;
+	void screeninit(void){}
+
+init
+	cerfinit
+
+root
+	/chan	/
+	/dev	/
+	/dis
+	/env	/
+	/fd	/
+	/net	/
+	/net.alt	/
+	/nvfs /
+	/prog	/
+	/dis/lib
+	/dis/disk
+	/osinit.dis
+
+# dos file system
+	/dis/dossrv.dis
+	/dis/lib/arg.dis
+	/dis/lib/styx.dis
+	/dis/lib/string.dis
+	/dis/lib/daytime.dis
+
+	/dis/disk/format.dis
+
+# For development work:
+	/dis/sh.dis	/dis/tiny/sh.dis
+	/dis/ls.dis
+	/dis/cat.dis
+	/dis/bind.dis
+	/dis/mount.dis
+	/dis/pwd.dis
+	/dis/echo.dis
+	/dis/cd.dis
+	/dis/xd.dis
+	/dis/cp.dis
+	/dis/mkdir.dis
+	/dis/rm.dis
+	/dis/p.dis
+	/dis/ps.dis
+	/dis/lib/readdir.dis
+	/dis/lib/workdir.dis
+	/dis/lib/daytime.dis
+	/dis/lib/auth.dis
+	/dis/lib/ssl.dis
+	/dis/lib/bufio.dis
+	/dis/lib/string.dis
+#	/dis/pcmcia.dis	/usr/forsyth/pcmcia.dis
+
+	/n/remote
+	/n/local
+	/n/client
+	/n/rdbg
+	/n/dump
+	/n/disk
+	/n/kfs
+# Authentication
+	/nvfs/default	/usr/inferno/keyring/default
--- /dev/null
+++ b/os/cerf1110/dat.h
@@ -1,0 +1,135 @@
+typedef struct Conf	Conf;
+typedef struct Dma	Dma;
+typedef struct FPU	FPU;
+typedef struct FPenv	FPenv;
+typedef struct Label	Label;
+typedef struct Lock	Lock;
+typedef struct Mach	Mach;
+typedef struct Ureg	Ureg;
+typedef struct ISAConf	ISAConf;
+typedef struct PCMmap	PCMmap;
+typedef struct PCMslot	PCMslot;
+
+typedef ulong Instr;
+
+struct Conf
+{
+	ulong	nmach;			/* processors */
+	ulong	nproc;			/* processes */
+	ulong	npage0;			/* total physical pages of memory */
+	ulong	npage1;			/* total physical pages of memory */
+	ulong	topofmem;		/* highest physical address + 1 */
+	ulong	npage;			/* total physical pages of memory */
+	ulong	base0;			/* base of bank 0 */
+	ulong	base1;			/* base of bank 1 */
+	ulong	ialloc;			/* max interrupt time allocation in bytes */
+
+	int		useminicache;		/* use mini cache: screen.c/lcd.c */
+	int		textwrite;			/* writeable text segment, for debug */
+	int		portrait;	/* display orientation */
+};
+
+#define NISAOPT 8
+struct ISAConf {
+	char	type[KNAMELEN];
+	ulong	port;
+	ulong	irq;
+	int	itype;
+	ulong	dma;
+	ulong	mem;
+	ulong	size;
+	ulong	freq;
+
+	int	nopt;
+	char	*opt[NISAOPT];
+};
+
+/*
+ * FPenv.status
+ */
+enum
+{
+	FPINIT,
+	FPACTIVE,
+	FPINACTIVE,
+};
+
+struct	FPenv
+{
+	ulong	status;
+	ulong	control;
+	ushort	fpistate;	/* emulated fp */
+	ulong	regs[8][3];	/* emulated fp */	
+};
+
+/*
+ * This structure must agree with fpsave and fprestore asm routines
+ */
+struct	FPU
+{
+	FPenv	env;
+};
+
+struct Label
+{
+	ulong	sp;
+	ulong	pc;
+};
+
+struct Lock
+{
+	ulong	key;
+	ulong	sr;
+	ulong	pc;
+	int	pri;
+};
+
+#include "../port/portdat.h"
+
+/*
+ *  machine dependent definitions not used by ../port/portdat.h
+ */
+struct Mach
+{
+	/* OFFSETS OF THE FOLLOWING KNOWN BY l.s */
+	ulong	splpc;		/* pc of last caller to splhi */
+
+	/* ordering from here on irrelevant */
+
+	int	machno;		/* physical id of processor */
+	ulong	ticks;			/* of the clock since boot time */
+	Proc	*proc;			/* current process on this processor */
+	Label	sched;			/* scheduler wakeup */
+	Lock	alarmlock;		/* access to alarm list */
+	void	*alarm;			/* alarms bound to this clock */
+	ulong	cpuhz;
+
+	/* stacks for exceptions */
+	ulong	fiqstack[4];
+	ulong	irqstack[4];
+	ulong	abtstack[4];
+	ulong	undstack[4];
+
+	int	stack[1];
+};
+
+#define	MACHP(n)	(n == 0 ? (Mach*)(MACHADDR) : (Mach*)0)
+
+extern Mach *m;
+extern Proc *up;
+
+typedef struct MemBank {
+	uint	pbase;
+	uint	plimit;
+	uint	vbase;
+	uint	vlimit;
+} MemBank;
+
+/*
+ * Layout at virtual address 0.
+ */
+typedef struct Vectorpage {
+	void	(*vectors[8])(void);
+	uint	vtable[8];
+} Vectorpage;
+extern Vectorpage *page0;
--- /dev/null
+++ b/os/cerf1110/devata.c
@@ -1,0 +1,1200 @@
+#include	"u.h"
+#include	"../port/lib.h"
+#include	"mem.h"
+#include	"dat.h"
+#include	"fns.h"
+#include	"io.h"
+#include	"../port/error.h"
+
+#define DPRINT if(0)iprint
+
+typedef	struct Drive		Drive;
+typedef	struct Ident		Ident;
+typedef	struct Controller	Controller;
+typedef struct Partition	Partition;
+typedef struct Repl		Repl;
+
+enum
+{
+	/* ports */
+	Pbase=		0x1F0,
+	Pdata=		0,	/* data port (16 bits) */
+	Perror=		1,	/* error port (read) */
+	Pprecomp=	1,	/* buffer mode port (write) */
+	Pcount=		2,	/* sector count port */
+	Psector=	3,	/* sector number port */
+	Pcyllsb=	4,	/* least significant byte cylinder # */
+	Pcylmsb=	5,	/* most significant byte cylinder # */
+	Pdh=		6,	/* drive/head port */
+	Pstatus=	7,	/* status port (read) */
+	 Sbusy=		 (1<<7),
+	 Sready=	 (1<<6),
+	 Sdrq=		 (1<<3),
+	 Serr=		 (1<<0),
+	Pcmd=		7,	/* cmd port (write) */
+
+	/* commands */
+	Crecal=		0x10,
+	Cread=		0x20,
+	Cwrite=		0x30,
+	Cident=		0xEC,
+	Cident2=	0xFF,	/* pseudo command for post Cident interrupt */
+	Csetbuf=	0xEF,
+	Cinitparam=	0x91,
+
+	/* conner specific commands */
+	Cstandby=	0xE2,
+	Cidle=		0xE1,
+	Cpowerdown=	0xE3,
+
+	/* disk states */
+	Sspinning,
+	Sstandby,
+	Sidle,
+	Spowerdown,
+
+	/* something we have to or into the drive/head reg */
+	DHmagic=	0xA0,
+
+	/* file types */
+	Qdir=		0,
+	Qfile,
+
+	Maxxfer=	BY2PG,		/* maximum transfer size/cmd */
+	Npart=		8+2,		/* 8 sub partitions, disk, and partition */
+	Nrepl=		64,		/* maximum replacement blocks */
+};
+#define PART(x)		(((x)>>1)&0xF)
+#define DRIVE(x)	(((x)>>5)&0x7)
+#define MKQID(d,p)	(((d)<<5) | ((p)<<1) | Qfile)
+
+struct Partition
+{
+	ulong	start;
+	ulong	end;
+	char	name[KNAMELEN+1];
+};
+
+struct Repl
+{
+	Partition *p;
+	int	nrepl;
+	ulong	blk[Nrepl];
+};
+
+#define PARTMAGIC	"plan9 partitions"
+#define REPLMAGIC	"block replacements"
+
+/*
+ *  an ata drive
+ */
+struct Drive
+{
+	QLock;
+
+	Controller *cp;
+	int	drive;
+	int	confused;	/* needs to be recalibrated (or worse) */
+	int	online;
+	int	npart;		/* number of real partitions */
+	Partition p[Npart];
+	Repl	repl;
+	ulong	usetime;
+	int	state;
+	char	vol[KNAMELEN];
+
+	ulong	cap;		/* total bytes */
+	int	bytes;		/* bytes/sector */
+	int	sectors;	/* sectors/track */
+	int	heads;		/* heads/cyl */
+	long	cyl;		/* cylinders/drive */
+
+	char	lba;		/* true if drive has logical block addressing */
+	char	multi;		/* non-zero if drive does multiple block xfers */
+};
+
+/*
+ *  a controller for 2 drives
+ */
+struct Controller
+{
+	QLock;			/* exclusive access to the controller */
+	ISAConf;		/* interface to pcmspecial */
+
+	Lock	reglock;	/* exclusive access to the registers */
+
+	int	confused;	/* needs to be recalibrated (or worse) */
+	ulong	pbase;		/* base port (copied from ISAConf) */
+
+	/*
+	 *  current operation
+	 */
+	int	cmd;		/* current command */
+	int	lastcmd;	/* debugging info */
+	Rendez	r;		/* wait here for command termination */
+	char	*buf;		/* xfer buffer */
+	int	nsecs;		/* length of transfer (sectors) */
+	int	sofar;		/* sectors transferred so far */
+	int	status;
+	int	error;
+	Drive	*dp;		/* drive being accessed */
+};
+
+Controller	*atac;
+Drive		*ata;
+static char*	ataerr;
+static int	nhard;
+static int	spindowntime;
+
+static void	ataintr(Ureg*, void*);
+static long	ataxfer(Drive*, Partition*, int, long, long, char*);
+static void	ataident(Drive*);
+static void	atasetbuf(Drive*, int);
+static void	ataparams(Drive*);
+static void	atapart(Drive*);
+static int	ataprobe(Drive*, int, int, int);
+
+static int
+atagen(Chan *c, char*, Dirtab*, int, int s, Dir *dirp)
+{
+	Qid qid;
+	int drive;
+	Drive *dp;
+	Partition *pp;
+	ulong l;
+
+	if(s == DEVDOTDOT){
+		mkqid(&qid, 0, 0, QTDIR);
+		sprint(up->genbuf, "#%C", devtab[c->type]->dc);
+		devdir(c, qid, up->genbuf, 0, eve, 0555, dirp);
+		return 1;
+	}
+	qid.vers = 0;
+	qid.type = QTFILE;
+	drive = s/Npart;
+	s = s % Npart;
+	if(drive >= nhard)
+		return -1;
+	dp = &ata[drive];
+
+	if(dp->online == 0 || s >= dp->npart)
+		return 0;
+
+	pp = &dp->p[s];
+	sprint(up->genbuf, "%s%s", dp->vol, pp->name);
+	qid.path = MKQID(drive, s);
+	l = (pp->end - pp->start) * dp->bytes;
+	devdir(c, qid, up->genbuf, l, eve, 0660, dirp);
+	return 1;
+}
+
+static void
+atainit(void)
+{
+	Drive *dp;
+	Controller *cp;
+	uchar equip;
+	int pcmslot;
+
+	if (atac)
+		return;		/* already done */
+
+	equip = 0x10;		/* hard coded */
+
+	cp = malloc(sizeof(*cp));
+	if (!cp)
+		error(Enomem);
+
+	cp->port = Pbase;
+	cp->irq = 14;
+	cp->nopt = 1;
+	cp->opt[0] = "index=1";
+
+	if((pcmslot = pcmspecial("ATA FLASH", cp)) < 0) {
+		DPRINT("No ATA card\n");
+		free(cp);
+		ataerr = Enoifc;
+		return;
+	}
+	ata = malloc(2 * sizeof(*ata));
+	if(ata == nil) {
+		pcmspecialclose(pcmslot);
+		free(cp);
+		error(Enomem);
+	}
+
+	atac = cp;
+	cp->buf = 0;
+	cp->lastcmd = cp->cmd;
+	cp->cmd = 0;
+	cp->pbase = cp->port;
+	intrenable(cp->irq, ataintr, cp, cp->itype, "ata");
+
+	dp = ata;
+	if(equip & 0xf0){
+		dp->drive = 0;
+		dp->online = 0;
+		dp->cp = cp;
+		dp++;
+	}
+	if((equip & 0x0f)){
+		dp->drive = 1;
+		dp->online = 0;
+		dp->cp = cp;
+		dp++;
+	}
+	nhard = dp - ata;
+	
+	spindowntime = 1;
+}
+
+
+/*
+ *  Get the characteristics of each drive.  Mark unresponsive ones
+ *  off line.
+ */
+static Chan*
+ataattach(char *spec)
+{
+	Drive *dp;
+
+	atainit();
+	if (!ata)
+		error(ataerr ? ataerr : Enoifc);
+	for(dp = ata; dp < &ata[nhard]; dp++){
+		if(waserror()){
+			dp->online = 0;
+			qunlock(dp);
+			continue;
+		}
+		qlock(dp);
+		if(!dp->online){
+			/*
+			 * Make sure ataclock() doesn't
+			 * interfere.
+			 */
+			dp->usetime = m->ticks;
+			ataparams(dp);
+			dp->online = 1;
+			atasetbuf(dp, 1);
+		}
+
+		/*
+		 *  read Plan 9 partition table
+		 */
+		atapart(dp);
+		qunlock(dp);
+		poperror();
+	}
+	return devattach('H', spec);
+}
+
+static Walkqid*
+atawalk(Chan *c, Chan *nc, char **name, int nname)
+{
+	return devwalk(c, nc, name, nname, 0, 0, atagen);
+}
+
+static int
+atastat(Chan *c, uchar *dp, int n)
+{
+	return devstat(c, dp, n, 0, 0, atagen);
+}
+
+static Chan*
+ataopen(Chan *c, int omode)
+{
+	return devopen(c, omode, 0, 0, atagen);
+}
+
+static void
+ataclose(Chan *c)
+{
+	Drive *d;
+	Partition *p;
+
+	if(c->mode != OWRITE && c->mode != ORDWR)
+		return;
+
+	d = &ata[DRIVE(c->qid.path)];
+	p = &d->p[PART(c->qid.path)];
+	if(strcmp(p->name, "partition") != 0)
+		return;
+
+	if(waserror()){
+		qunlock(d);
+		nexterror();
+	}
+	qlock(d);
+	atapart(d);
+	qunlock(d);
+	poperror();
+}
+
+static long
+ataread(Chan *c, void *a, long n, vlong offset)
+{
+	Drive *dp;
+	long rv, i;
+	int skip;
+	uchar *aa = a;
+	Partition *pp;
+	char *buf;
+
+	if(c->qid.type & QTDIR)
+		return devdirread(c, a, n, 0, 0, atagen);
+
+	buf = smalloc(Maxxfer);
+	if(waserror()){
+		free(buf);
+		nexterror();
+	}
+
+	dp = &ata[DRIVE(c->qid.path)];
+	pp = &dp->p[PART(c->qid.path)];
+
+	skip = offset % dp->bytes;
+	for(rv = 0; rv < n; rv += i){
+		i = ataxfer(dp, pp, Cread, offset+rv-skip, n-rv+skip, buf);
+		if(i == 0)
+			break;
+		i -= skip;
+		if(i > n - rv)
+			i = n - rv;
+		memmove(aa+rv, buf + skip, i);
+		skip = 0;
+	}
+
+	free(buf);
+	poperror();
+
+	return rv;
+}
+
+static long
+atawrite(Chan *c, void *a, long n, vlong offset)
+{
+	Drive *dp;
+	long rv, i, partial;
+	uchar *aa = a;
+	Partition *pp;
+	char *buf;
+
+	if(c->qid.type & QTDIR)
+		error(Eisdir);
+
+	dp = &ata[DRIVE(c->qid.path)];
+	pp = &dp->p[PART(c->qid.path)];
+	buf = smalloc(Maxxfer);
+	if(waserror()){
+		free(buf);
+		nexterror();
+	}
+
+	/*
+	 *  if not starting on a sector boundary,
+	 *  read in the first sector before writing
+	 *  it out.
+	 */
+	partial = offset % dp->bytes;
+	if(partial){
+		ataxfer(dp, pp, Cread, offset-partial, dp->bytes, buf);
+		if(partial+n > dp->bytes)
+			rv = dp->bytes - partial;
+		else
+			rv = n;
+		memmove(buf+partial, aa, rv);
+		ataxfer(dp, pp, Cwrite, offset-partial, dp->bytes, buf);
+	} else
+		rv = 0;
+
+	/*
+	 *  write out the full sectors
+	 */
+	partial = (n - rv) % dp->bytes;
+	n -= partial;
+	for(; rv < n; rv += i){
+		i = n - rv;
+		if(i > Maxxfer)
+			i = Maxxfer;
+		memmove(buf, aa+rv, i);
+		i = ataxfer(dp, pp, Cwrite, offset+rv, i, buf);
+		if(i == 0)
+			break;
+	}
+
+	/*
+	 *  if not ending on a sector boundary,
+	 *  read in the last sector before writing
+	 *  it out.
+	 */
+	if(partial){
+		ataxfer(dp, pp, Cread, offset+rv, dp->bytes, buf);
+		memmove(buf, aa+rv, partial);
+		ataxfer(dp, pp, Cwrite, offset+rv, dp->bytes, buf);
+		rv += partial;
+	}
+
+	free(buf);
+	poperror();
+
+	return rv;
+}
+
+/*
+ *  did an interrupt happen?
+ */
+static int
+cmddone(void *a)
+{
+	Controller *cp = a;
+
+	return cp->cmd == 0;
+}
+
+/*
+ * Wait for the controller to be ready to accept a command.
+ * This is protected from interference by ataclock() by
+ * setting dp->usetime before it is called.
+ */
+static void
+cmdreadywait(Drive *dp)
+{
+	long start;
+	int period;
+	Controller *cp = dp->cp;
+
+	/* give it 2 seconds to spin down and up */
+	if(dp->state == Sspinning)
+		period = 10;
+	else
+		period = 2000;
+
+	start = m->ticks;
+	while((inb(cp->pbase+Pstatus) & (Sready|Sbusy)) != Sready)
+		if(TK2MS(m->ticks - start) > period){
+			DPRINT("cmdreadywait failed\n");
+			error(Eio);
+		}
+}
+
+static void
+atarepl(Drive *dp, long bblk)
+{
+	int i;
+
+	if(dp->repl.p == 0)
+		return;
+	for(i = 0; i < dp->repl.nrepl; i++){
+		if(dp->repl.blk[i] == bblk)
+			DPRINT("found bblk %ld at offset %d\n", bblk, i);
+	}
+}
+
+static void
+atasleep(Controller *cp, int ms)
+{
+        tsleep(&cp->r, cmddone, cp, ms);
+	if(cp->cmd && cp->cmd != Cident2){
+		DPRINT("ata: cmd 0x%uX timeout, status=%ux\n",
+				cp->cmd, inb(cp->pbase+Pstatus));
+		error("ata drive timeout");
+	}
+}
+
+/*
+ *  transfer a number of sectors.  ataintr will perform all the iterative
+ *  parts.
+ */
+static long
+ataxfer(Drive *dp, Partition *pp, int cmd, long start, long len, char *buf)
+{
+	Controller *cp;
+	long lblk;
+	int cyl, sec, head;
+	int loop, stat;
+
+	if(dp->online == 0)
+		error(Eio);
+
+	/*
+	 *  cut transfer size down to disk buffer size
+	 */
+	start = start / dp->bytes;
+	if(len > Maxxfer)
+		len = Maxxfer;
+	len = (len + dp->bytes - 1) / dp->bytes;
+	if(len == 0)
+		return 0;
+
+	/*
+	 *  calculate physical address
+	 */
+	lblk = start + pp->start;
+	if(lblk >= pp->end)
+		return 0;
+	if(lblk+len > pp->end)
+		len = pp->end - lblk;
+	if(dp->lba){
+		sec = lblk & 0xff;
+		cyl = (lblk>>8) & 0xffff;
+		head = (lblk>>24) & 0xf;
+	} else {
+		cyl = lblk/(dp->sectors*dp->heads);
+		sec = (lblk % dp->sectors) + 1;
+		head = ((lblk/dp->sectors) % dp->heads);
+	}
+
+	DPRINT("<%s %ld>", (cmd == Cwrite) ? "W" : "R", lblk);
+	cp = dp->cp;
+	qlock(cp);
+	if(waserror()){
+		cp->buf = 0;
+		qunlock(cp);
+		nexterror();
+	}
+
+	/*
+	 * Make sure hardclock() doesn't
+	 * interfere.
+	 */
+	dp->usetime = m->ticks;
+	cmdreadywait(dp);
+
+	ilock(&cp->reglock);
+	cp->sofar = 0;
+	cp->buf = buf;
+	cp->nsecs = len;
+	cp->cmd = cmd;
+	cp->dp = dp;
+	cp->status = 0;
+
+	outb(cp->pbase+Pcount, cp->nsecs);
+	outb(cp->pbase+Psector, sec);
+	outb(cp->pbase+Pdh, DHmagic | (dp->drive<<4) | (dp->lba<<6) | head);
+	outb(cp->pbase+Pcyllsb, cyl);
+	outb(cp->pbase+Pcylmsb, cyl>>8);
+	outb(cp->pbase+Pcmd, cmd);
+
+	if(cmd == Cwrite){
+		loop = 0;
+		microdelay(1);
+		while((stat = inb(cp->pbase+Pstatus) & (Serr|Sdrq)) == 0)
+			if(++loop > 10000)
+				panic("ataxfer");
+		outss(cp->pbase+Pdata, cp->buf, dp->bytes/2);
+	} else
+		stat = 0;
+	iunlock(&cp->reglock);
+
+	if(stat & Serr)
+		error(Eio);
+
+	/*
+	 *  wait for command to complete.  if we get a note,
+	 *  remember it but keep waiting to let the disk finish
+	 *  the current command.
+	 */
+	loop = 0;
+	while(waserror()){
+		DPRINT("interrupted ataxfer\n");
+		if(loop++ > 10){
+			print("ata disk error\n");
+			nexterror();
+		}
+	}
+	atasleep(cp, 3000);
+	dp->state = Sspinning;
+	dp->usetime = m->ticks;
+	poperror();
+	if(loop)
+		nexterror();
+
+	if(cp->status & Serr){
+		DPRINT("hd%ld err: lblk %ld status %ux, err %ux\n",
+			dp-ata, lblk, cp->status, cp->error);
+		DPRINT("\tcyl %d, sec %d, head %d\n", cyl, sec, head);
+		DPRINT("\tnsecs %d, sofar %d\n", cp->nsecs, cp->sofar);
+		atarepl(dp, lblk+cp->sofar);
+		error(Eio);
+	}
+	cp->buf = 0;
+	len = cp->sofar*dp->bytes;
+	qunlock(cp);
+	poperror();
+
+	return len;
+}
+
+/*
+ *  set read ahead mode
+ */
+static void
+atasetbuf(Drive *dp, int on)
+{
+	Controller *cp = dp->cp;
+
+	qlock(cp);
+	if(waserror()){
+		qunlock(cp);
+		nexterror();
+	}
+
+	cmdreadywait(dp);
+
+	ilock(&cp->reglock);
+	cp->cmd = Csetbuf;
+	outb(cp->pbase+Pprecomp, on ? 0xAA : 0x55);	/* read look ahead */
+	outb(cp->pbase+Pdh, DHmagic | (dp->drive<<4));
+	outb(cp->pbase+Pcmd, Csetbuf);
+	iunlock(&cp->reglock);
+
+	atasleep(cp, 5000);
+
+/*	if(cp->status & Serr)
+		DPRINT("hd%d setbuf err: status %lux, err %lux\n",
+			dp-ata, cp->status, cp->error);/**/
+
+	poperror();
+	qunlock(cp);
+}
+
+/*
+ *  ident sector from drive.  this is from ANSI X3.221-1994
+ */
+struct Ident
+{
+	ushort	config;		/* general configuration info */
+	ushort	cyls;		/* # of cylinders (default) */
+	ushort	reserved0;
+	ushort	heads;		/* # of heads (default) */
+	ushort	b2t;		/* unformatted bytes/track */
+	ushort	b2s;		/* unformated bytes/sector */
+	ushort	s2t;		/* sectors/track (default) */
+	ushort	reserved1[3];
+/* 10 */
+	ushort	serial[10];	/* serial number */
+	ushort	type;		/* buffer type */
+	ushort	bsize;		/* buffer size/512 */
+	ushort	ecc;		/* ecc bytes returned by read long */
+	ushort	firm[4];	/* firmware revision */
+	ushort	model[20];	/* model number */
+/* 47 */
+	ushort	s2i;		/* number of sectors/interrupt */
+	ushort	dwtf;		/* double word transfer flag */
+	ushort	capabilities;
+	ushort	reserved2;
+	ushort	piomode;
+	ushort	dmamode;
+	ushort	cvalid;		/* (cvald&1) if next 4 words are valid */
+	ushort	ccyls;		/* current # cylinders */
+	ushort	cheads;		/* current # heads */
+	ushort	cs2t;		/* current sectors/track */
+	ushort	ccap[2];	/* current capacity in sectors */
+	ushort	cs2i;		/* current number of sectors/interrupt */
+/* 60 */
+	ushort	lbasecs[2];	/* # LBA user addressable sectors */
+	ushort	dmasingle;
+	ushort	dmadouble;
+/* 64 */
+	ushort	reserved3[64];
+	ushort	vendor[32];	/* vendor specific */
+	ushort	reserved4[96];
+};
+
+/*
+ *  get parameters from the drive
+ */
+static void
+ataident(Drive *dp)
+{
+	Controller *cp;
+	char *buf;
+	Ident *ip;
+	char id[21];
+
+	cp = dp->cp;
+	buf = smalloc(Maxxfer);
+	qlock(cp);
+	if(waserror()){
+		cp->buf = 0;
+		qunlock(cp);
+		free(buf);
+		nexterror();
+	}
+
+	cmdreadywait(dp);
+
+	ilock(&cp->reglock);
+	cp->nsecs = 1;
+	cp->sofar = 0;
+	cp->cmd = Cident;
+	cp->dp = dp;
+	cp->buf = buf;
+	outb(cp->pbase+Pdh, DHmagic | (dp->drive<<4));
+	outb(cp->pbase+Pcmd, Cident);
+	iunlock(&cp->reglock);
+
+	atasleep(cp, 5000);
+	if(cp->status & Serr){
+		DPRINT("bad disk ident status\n");
+		error(Eio);
+	}
+	ip = (Ident*)buf;
+
+	/*
+	 * this function appears to respond with an extra interrupt after
+	 * the ident information is read, except on the safari.  The following
+	 * delay gives this extra interrupt a chance to happen while we are quiet.
+	 * Otherwise, the interrupt may come during a subsequent read or write,
+	 * causing a panic and much confusion.
+	 */
+	if (cp->cmd == Cident2)
+		tsleep(&cp->r, return0, 0, 10);
+
+	memmove(id, ip->model, sizeof(id)-1);
+	id[sizeof(id)-1] = 0;
+
+	if(ip->capabilities & (1<<9)){
+		dp->lba = 1;
+		dp->sectors = (ip->lbasecs[0]) | (ip->lbasecs[1]<<16);
+		dp->cap = dp->bytes * dp->sectors;
+/*print("\nata%d model %s with %d lba sectors\n", dp->drive, id, dp->sectors);/**/
+	} else {
+		dp->lba = 0;
+
+		/* use default (unformatted) settings */
+		dp->cyl = ip->cyls;
+		dp->heads = ip->heads;
+		dp->sectors = ip->s2t;
+/*print("\nata%d model %s with default %d cyl %d head %d sec\n", dp->drive,
+			id, dp->cyl, dp->heads, dp->sectors);/**/
+
+		if(ip->cvalid&(1<<0)){
+			/* use current settings */
+			dp->cyl = ip->ccyls;
+			dp->heads = ip->cheads;
+			dp->sectors = ip->cs2t;
+/*print("\tchanged to %d cyl %d head %d sec\n", dp->cyl, dp->heads, dp->sectors);/**/
+		}
+		dp->cap = dp->bytes * dp->cyl * dp->heads * dp->sectors;
+	}
+	cp->lastcmd = cp->cmd;
+	cp->cmd = 0;
+	cp->buf = 0;
+	free(buf);
+	poperror();
+	qunlock(cp);
+}
+
+/*
+ *  probe the given sector to see if it exists
+ */
+static int
+ataprobe(Drive *dp, int cyl, int sec, int head)
+{
+	Controller *cp;
+	char *buf;
+	int rv;
+
+	cp = dp->cp;
+	buf = smalloc(Maxxfer);
+	qlock(cp);
+	if(waserror()){
+		free(buf);
+		qunlock(cp);
+		nexterror();
+	}
+
+	cmdreadywait(dp);
+
+	ilock(&cp->reglock);
+	cp->cmd = Cread;
+	cp->dp = dp;
+	cp->status = 0;
+	cp->nsecs = 1;
+	cp->sofar = 0;
+
+	outb(cp->pbase+Pcount, 1);
+	outb(cp->pbase+Psector, sec+1);
+	outb(cp->pbase+Pdh, DHmagic | head | (dp->lba<<6) | (dp->drive<<4));
+	outb(cp->pbase+Pcyllsb, cyl);
+	outb(cp->pbase+Pcylmsb, cyl>>8);
+	outb(cp->pbase+Pcmd, Cread);
+	iunlock(&cp->reglock);
+
+	atasleep(cp, 5000);
+
+	if(cp->status & Serr)
+		rv = -1;
+	else
+		rv = 0;
+
+	cp->buf = 0;
+	free(buf);
+	poperror();
+	qunlock(cp);
+	return rv;
+}
+
+/*
+ *  figure out the drive parameters
+ */
+static void
+ataparams(Drive *dp)
+{
+	int i, hi, lo;
+
+	/*
+	 *  first try the easy way, ask the drive and make sure it
+	 *  isn't lying.
+	 */
+	dp->bytes = 512;
+	ataident(dp);
+	if(dp->lba){
+		i = dp->sectors - 1;
+		if(ataprobe(dp, (i>>8)&0xffff, (i&0xff)-1, (i>>24)&0xf) == 0)
+			return;
+	} else {
+		if(ataprobe(dp, dp->cyl-1, dp->sectors-1, dp->heads-1) == 0)
+			return;
+	}
+
+	/*
+	 *  the drive lied, determine parameters by seeing which ones
+	 *  work to read sectors.
+	 */
+	dp->lba = 0;
+	for(i = 0; i < 32; i++)
+		if(ataprobe(dp, 0, 0, i) < 0)
+			break;
+	dp->heads = i;
+	for(i = 0; i < 128; i++)
+		if(ataprobe(dp, 0, i, 0) < 0)
+			break;
+	dp->sectors = i;
+	for(i = 512; ; i += 512)
+		if(ataprobe(dp, i, dp->sectors-1, dp->heads-1) < 0)
+			break;
+	lo = i - 512;
+	hi = i;
+	for(; hi-lo > 1;){
+		i = lo + (hi - lo)/2;
+		if(ataprobe(dp, i, dp->sectors-1, dp->heads-1) < 0)
+			hi = i;
+		else
+			lo = i;
+	}
+	dp->cyl = lo + 1;
+	dp->cap = dp->bytes * dp->cyl * dp->heads * dp->sectors;
+}
+
+/*
+ *  Read block replacement table.
+ *  The table is just ascii block numbers.
+ */
+static void
+atareplinit(Drive *dp)
+{
+	char *line[Nrepl+1];
+	char *field[1];
+	ulong n;
+	int i;
+	char *buf;
+
+	/*
+	 *  check the partition is big enough
+	 */
+	if(dp->repl.p->end - dp->repl.p->start < Nrepl+1){
+		dp->repl.p = 0;
+		return;
+	}
+
+	buf = smalloc(Maxxfer);
+	if(waserror()){
+		free(buf);
+		nexterror();
+	}
+
+	/*
+	 *  read replacement table from disk, null terminate
+	 */
+	ataxfer(dp, dp->repl.p, Cread, 0, dp->bytes, buf);
+	buf[dp->bytes-1] = 0;
+
+	/*
+	 *  parse replacement table.
+	 */
+	n = getfields(buf, line, Nrepl+1, 1, "\n");
+	if(strncmp(line[0], REPLMAGIC, sizeof(REPLMAGIC)-1)){
+		dp->repl.p = 0;
+	} else {
+		for(dp->repl.nrepl = 0, i = 1; i < n; i++, dp->repl.nrepl++){
+			if(getfields(line[i], field, 1, 1, " ") != 1)
+				break;
+			dp->repl.blk[dp->repl.nrepl] = strtoul(field[0], 0, 0);
+			if(dp->repl.blk[dp->repl.nrepl] <= 0)
+				break;
+		}
+	}
+	free(buf);
+	poperror();
+}
+
+/*
+ *  read partition table.  The partition table is just ascii strings.
+ */
+static void
+atapart(Drive *dp)
+{
+	Partition *pp;
+	char *line[Npart+1];
+	char *field[3];
+	ulong n;
+	int i;
+	char *buf;
+
+	sprint(dp->vol, "hd%ld", dp - ata);
+
+	/*
+	 *  we always have a partition for the whole disk
+	 *  and one for the partition table
+	 */
+	pp = &dp->p[0];
+	strcpy(pp->name, "disk");
+	pp->start = 0;
+	pp->end = dp->cap / dp->bytes;
+	pp++;
+	strcpy(pp->name, "partition");
+	pp->start = dp->p[0].end - 1;
+	pp->end = dp->p[0].end;
+	pp++;
+	dp->npart = 2;
+
+	/*
+	 * initialise the bad-block replacement info
+	 */
+	dp->repl.p = 0;
+
+	buf = smalloc(Maxxfer);
+	if(waserror()){
+		free(buf);
+		nexterror();
+	}
+
+	/*
+	 *  read last sector from disk, null terminate.  This used
+	 *  to be the sector we used for the partition tables.
+	 *  However, this sector is special on some PC's so we've
+	 *  started to use the second last sector as the partition
+	 *  table instead.  To avoid reconfiguring all our old systems
+	 *  we first look to see if there is a valid partition
+	 *  table in the last sector.  If so, we use it.  Otherwise
+	 *  we switch to the second last.
+	 */
+	ataxfer(dp, dp->p+1, Cread, 0, dp->bytes, buf);
+	buf[dp->bytes-1] = 0;
+	n = getfields(buf, line, Npart+1, 1, "\n");
+	if(n == 0 || strncmp(line[0], PARTMAGIC, sizeof(PARTMAGIC)-1)){
+		dp->p[0].end--;
+		dp->p[1].start--;
+		dp->p[1].end--;
+		ataxfer(dp, dp->p+1, Cread, 0, dp->bytes, buf);
+		buf[dp->bytes-1] = 0;
+		n = getfields(buf, line, Npart+1, 1, "\n");
+	}
+
+	/*
+	 *  parse partition table.
+	 */
+	if(n > 0 && strncmp(line[0], PARTMAGIC, sizeof(PARTMAGIC)-1) == 0){
+		for(i = 1; i < n; i++){
+			switch(getfields(line[i], field, 3, 1, " ")) {
+			case 2:
+				if(strcmp(field[0], "unit") == 0)
+					strncpy(dp->vol, field[1], KNAMELEN);
+				break;	
+			case 3:
+				strncpy(pp->name, field[0], KNAMELEN);
+				if(strncmp(pp->name, "repl", KNAMELEN) == 0)
+					dp->repl.p = pp;
+				pp->start = strtoul(field[1], 0, 0);
+				pp->end = strtoul(field[2], 0, 0);
+				if(pp->start > pp->end || pp->end > dp->p[0].end)
+					break;
+				dp->npart++;
+				pp++;
+			}
+		}
+	}
+	free(buf);
+	poperror();
+
+	if(dp->repl.p)
+		atareplinit(dp);
+}
+
+enum
+{
+	Maxloop=	10000,
+};
+
+/*
+ *  we get an interrupt for every sector transferred
+ */
+static void
+ataintr(Ureg*, void *arg)
+{
+	Controller *cp;
+	Drive *dp;
+	long loop;
+	char *addr;
+
+	cp = arg;
+	dp = cp->dp;
+
+	ilock(&cp->reglock);
+
+	loop = 0;
+	while((cp->status = inb(cp->pbase+Pstatus)) & Sbusy){
+		if(++loop > Maxloop) {
+			DPRINT("cmd=%ux status=%ux\n",
+				cp->cmd, inb(cp->pbase+Pstatus));
+			panic("ataintr: wait busy");
+		}
+	}
+
+	switch(cp->cmd){
+	case Cwrite:
+		if(cp->status & Serr){
+			cp->lastcmd = cp->cmd;
+			cp->cmd = 0;
+			cp->error = inb(cp->pbase+Perror);
+			wakeup(&cp->r);
+			break;
+		}
+		cp->sofar++;
+		if(cp->sofar < cp->nsecs){
+			loop = 0;
+			while(((cp->status = inb(cp->pbase+Pstatus)) & Sdrq) == 0)
+				if(++loop > Maxloop) {
+					DPRINT("cmd=%ux status=%ux\n",
+						cp->cmd, inb(cp->pbase+Pstatus));
+					panic("ataintr: write");
+				}
+			addr = cp->buf;
+			if(addr){
+				addr += cp->sofar*dp->bytes;
+				outss(cp->pbase+Pdata, addr, dp->bytes/2);
+			}
+		} else{
+			cp->lastcmd = cp->cmd;
+			cp->cmd = 0;
+			wakeup(&cp->r);
+		}
+		break;
+	case Cread:
+	case Cident:
+		loop = 0;
+		while((cp->status & (Serr|Sdrq)) == 0){
+			if(++loop > Maxloop) {
+				DPRINT("cmd=%ux status=%ux\n",
+					cp->cmd, inb(cp->pbase+Pstatus));
+				panic("ataintr: read/ident");
+			}
+			cp->status = inb(cp->pbase+Pstatus);
+		}
+		if(cp->status & Serr){
+			cp->lastcmd = cp->cmd;
+			cp->cmd = 0;
+			cp->error = inb(cp->pbase+Perror);
+			wakeup(&cp->r);
+			break;
+		}
+		addr = cp->buf;
+		if(addr){
+			addr += cp->sofar*dp->bytes;
+			inss(cp->pbase+Pdata, addr, dp->bytes/2);
+		}
+		cp->sofar++;
+		if(cp->sofar > cp->nsecs)
+			print("ataintr %d %d\n", cp->sofar, cp->nsecs);
+		if(cp->sofar >= cp->nsecs){
+			cp->lastcmd = cp->cmd;
+			if (cp->cmd == Cread)
+				cp->cmd = 0;
+			else
+				cp->cmd = Cident2;
+			wakeup(&cp->r);
+		}
+		break;
+	case Cinitparam:
+	case Csetbuf:
+	case Cidle:
+	case Cstandby:
+	case Cpowerdown:
+		cp->lastcmd = cp->cmd;
+		cp->cmd = 0;
+		wakeup(&cp->r);
+		break;
+	case Cident2:
+		cp->lastcmd = cp->cmd;
+		cp->cmd = 0;
+		break;
+	default:
+		print("weird disk interrupt, cmd=%.2ux, lastcmd= %.2ux status=%.2ux\n",
+			cp->cmd, cp->lastcmd, cp->status);
+		break;
+	}
+
+	iunlock(&cp->reglock);
+}
+
+void
+hardclock(void)
+{
+	int drive;
+	Drive *dp;
+	Controller *cp;
+	int diff;
+
+	if(spindowntime <= 0)
+		return;
+
+	for(drive = 0; drive < nhard; drive++){
+		dp = &ata[drive];
+		cp = dp->cp;
+
+		diff = TK2SEC(m->ticks - dp->usetime);
+		if((dp->state == Sspinning) && (diff >= spindowntime)){
+			ilock(&cp->reglock);
+			cp->cmd = Cstandby;
+			outb(cp->pbase+Pcount, 0);
+			outb(cp->pbase+Pdh, DHmagic | (dp->drive<<4) | 0);
+			outb(cp->pbase+Pcmd, cp->cmd);
+			iunlock(&cp->reglock);
+			dp->state = Sstandby;
+		}
+	}
+}
+
+Dev atadevtab = {
+	'H',
+	"ata",
+
+	devreset,
+	atainit,
+	devshutdown,
+	ataattach,
+	atawalk,
+	atastat,
+	ataopen,
+	devcreate,
+	ataclose,
+	ataread,
+	devbread,
+	atawrite,
+	devbwrite,
+	devremove,
+	devwstat,
+};
--- /dev/null
+++ b/os/cerf1110/devcerf.c
@@ -1,0 +1,128 @@
+#include	"u.h"
+#include	"../port/lib.h"
+#include	"mem.h"
+#include	"dat.h"
+#include	"fns.h"
+#include	"../port/error.h"
+
+#include "io.h"
+
+
+enum{
+	Qdir,
+	Qled,
+};
+
+static
+Dirtab cerftab[]={
+	".",			{Qdir, 0, QTDIR},	0,	0555,
+	"cerfled",		{Qled, 0},	0,	0660,
+};
+
+static void
+cerfinit(void)						/* default in dev.c */
+{
+	int s;
+
+	s = splhi();
+	GPIOREG->gpdr |= 0xF;
+	GPIOREG->gpsr = 1<<0;	/* we're here */
+	splx(s);
+}
+
+static Chan*
+cerfattach(char* spec)
+{
+	return devattach('T', spec);
+}
+
+static Walkqid*
+cerfwalk(Chan *c, Chan *nc, char **name, int nname)
+{
+	return devwalk(c, nc, name, nname, cerftab, nelem(cerftab), devgen);
+}
+
+static int
+cerfstat(Chan* c, uchar *db, int n)
+{
+	return devstat(c, db, n, cerftab, nelem(cerftab), devgen);
+}
+
+static Chan*
+cerfopen(Chan* c, int omode)
+{
+	return devopen(c, omode, cerftab, nelem(cerftab), devgen);
+}
+
+static void
+cerfclose(Chan* c)
+{
+	USED(c);
+}
+
+static long
+cerfread(Chan* c, void* a, long n, vlong offset)
+{
+	char buf[16];
+
+	switch((ulong)c->qid.path){
+	case Qdir:
+		return devdirread(c, a, n, cerftab, nelem(cerftab), devgen);
+	case Qled:
+		snprint(buf, sizeof(buf), "%2.2lux", GPIOREG->gplr&0xF);
+		return readstr(offset, a, n, buf);
+	default:
+		n=0;
+		break;
+	}
+	return n;
+}
+
+static long
+cerfwrite(Chan* c, void* a, long n, vlong)
+{
+	char buf[16];
+	ulong v;
+
+	switch((ulong)c->qid.path){
+	case Qled:
+		if(n >= sizeof(buf))
+			n = sizeof(buf)-1;
+		memmove(buf, a, n);
+		buf[n] = 0;
+		v = GPIOREG->gplr & 0xF;
+		if(buf[0] == '+')
+			v |= strtoul(buf+1, nil, 0);
+		else if(buf[0] == '-')
+			v &= ~strtoul(buf+1, nil, 0);
+		else
+			v = strtoul(buf, nil, 0);
+		GPIOREG->gpsr = v & 0xF;
+		GPIOREG->gpcr = ~v & 0xF;
+		break;
+	default:
+		error(Ebadusefd);
+	}
+	return n;
+}
+
+Dev cerfdevtab = {
+	'T',
+	"cerf",
+
+	devreset,
+	cerfinit,
+	devshutdown,
+	cerfattach,
+	cerfwalk,
+	cerfstat,
+	cerfopen,
+	devcreate,
+	cerfclose,
+	cerfread,
+	devbread,
+	cerfwrite,
+	devbwrite,
+	devremove,
+	devwstat,
+};
--- /dev/null
+++ b/os/cerf1110/ether8900.c
@@ -1,0 +1,702 @@
+/*
+ * Crystal CS8900 ethernet controller
+ * 
+ * Todo:
+ * - promiscuous
+ *
+ * Copyright © 1998 Vita Nuova Limited.  All rights reserved.
+ * Revisions Copyright © 2000,2003 Vita Nuova Holdings Limited.  All rights reserved.
+ */
+
+#include "u.h"
+#include "../port/lib.h"
+#include "mem.h"
+#include "dat.h"
+#include "fns.h"
+#include "io.h"
+#include "../port/netif.h"
+
+#include "etherif.h"
+
+typedef struct Ctlr Ctlr;
+
+/*
+ * The CS8900 can be addressed from either  ISA I/O space
+ * or ISA memory space at the following virtual addresses,
+ * depending on the hardware's wiring.  MEMORY controls
+ * use of memory space.
+ * The cs8900 address pins are shifted by 1 relative to the CPU.
+ */
+enum {//18000000
+	IsaIOBase		= 0x08000000,
+	IsaMemBase	= 0xe0000000,
+
+	IOBase		= 0x300,
+	MemBase		= 0xc0000,
+
+	MEMORY = 0,		/* set non-zero if memory mode to be used */
+	DORESET = 1,		/* send soft-reset during initialisation */
+	DEBUG = 0,
+};
+
+#define	IOSHIFT	0	/* was 2 */
+#define	IOREG(r) (IsaIOBase+((IOBase+(r))<<IOSHIFT))
+
+/* I/O accesses */
+#define	out16(port, val)	(*((ushort *)IOREG(port)) = (val))
+#define	in16(port)			*((ushort *)IOREG(port))
+#define	in8(port)			*((uchar *)IOREG(port))
+#define	regIOw(reg, val)	 do {out16(PpPtr, (reg)|0x3000); out16(PpData, val);} while(0)
+#define	regIOr(reg)		(out16(PpPtr, (reg)|0x3000), in16(PpData))
+#define	regIOr1(reg)		(out16(PpPtr, (reg)|0x3000), in16(PpData1))
+
+/* Memory accesses */
+
+#define	REGW(reg, val)		*((ushort *)IsaMemBase + MemBase + (reg)) = (val)
+#define	REGR(reg)			*((ushort *)IsaMemBase + MemBase + (reg))
+
+enum {					/* I/O Mode Register Offsets */
+	RxTxData	= 0x00,		/* receive/transmit data - port 0 */
+	RxTxData1 = 0x02,		/* r/t data port 1 */
+	TxCmdIO = 0x04,		/* transmit command */
+	TxLenIO	= 0x06,		/* transmit length */
+	IsqIO	= 0x08,		/* Interrupt status queue */
+	PpPtr	= 0x0a,		/* packet page pointer */
+	PpData	= 0x0c,		/* packet page data */
+	PpData1	= 0x0e,		/* packet page data - port 1*/
+};
+
+enum {					/* Memory Mode Register Offsets */
+	/* Bus Interface Registers */
+	Ern		= 0x0000,		/* EISA registration numberion */
+	Pic		= 0x0002,		/* Product identification code */
+	Iob		= 0x0020,		/* I/O base address */
+	Intr		= 0x0022,		/* interrupt number */
+	Mba		= 0x002c,		/* memory base address */
+	
+	Ecr		= 0x0040,		/* EEPROM command register */
+	Edw		= 0x0042,		/* EEPROM data word */
+	Rbc		= 0x0050,		/* receive frame byte counter */
+
+	/* Status and Control Registers */
+	RxCfg	= 0x0102,
+	RxCtl	= 0x0104,
+	TxCfg	= 0x0106,
+	BufCfg	= 0x010a,
+	LineCtl	= 0x0112,
+	SelfCtl	= 0x0114,
+	BusCtl	= 0x0116,
+	TestCtl	= 0x0118,
+	Isq		= 0x0120,
+	RxEvent	= 0x0124,
+	TxEvent	= 0x0128,
+	BufEvent	= 0x012c,
+	RxMISS	= 0x0130,
+	TxCol	= 0x0132,
+	LineSt	= 0x0134,
+	SelfSt	= 0x0136,
+	BusSt	= 0x0138,
+	Tdr		= 0x013c,
+
+	/* Initiate Transmit Registers */
+	TxCmd	= 0x0144,		/* transmit command */
+	TxLen	= 0x0146,		/* transmit length */
+
+	/* Address Filter Registers */
+	IndAddr	= 0x0158,		/* individual address registers */
+
+	/* Frame Location */
+	RxStatus	= 0x0400,		/* receive status */
+	RxLen	= 0x0402,		/* receive length */
+	RxFrame	= 0x0404,		/* receive frame location */
+	TxFrame	= 0x0a00,		/* transmit frame location */
+};
+
+enum {					/* Ecr */
+	Addr			= 0x00ff,		/* EEPROM word address (field) */
+	Opcode		= 0x0300,		/* command opcode (field) */
+		EEread	= 0x0200,
+		EEwrite	= 0x0100,
+};
+
+enum {					/* Isq */
+	Regnum		= 0x003f,		/* register number held by Isq (field) */
+		IsqRxEvent	= 0x04,
+		IsqTxEvent	= 0x08,
+		IsqBufEvent	= 0x0c,
+		IsqRxMiss		= 0x10,
+		IsqTxCol		= 0x12,
+	RegContent 	= 0xffc0,		/* register data contents (field) */
+};
+
+enum {					/* RxCfg */
+	Skip_1		= 0x0040,
+	StreamE		= 0x0080,
+	RxOKiE		= 0x0100,
+	RxDMAonly	= 0x0200,
+	AutoRxDMAE	= 0x0400,
+	BufferCRC		= 0x0800,
+	CRCerroriE	= 0x1000,
+	RuntiE		= 0x2000,
+	ExtradataiE	= 0x4000,
+};
+
+enum {					/* RxEvent */
+	IAHash		= 0x0040,
+	Dribblebits	= 0x0080,
+	RxOK		= 0x0100,
+	Hashed		= 0x0200,
+	IndividualAdr	= 0x0400,
+	Broadcast		= 0x0800,
+	CRCerror		= 0x1000,
+	Runt			= 0x2000,
+	Extradata		= 0x4000,
+};
+
+enum {					/* RxCtl */
+	IAHashA		= 0x0040,
+	PromiscuousA	= 0x0080,
+	RxOKA		= 0x0100,
+	MulticastA	= 0x0200,
+	IndividualA	= 0x0400,
+	BroadcastA	= 0x0800,
+	CRCerrorA	= 0x1000,
+	RuntA		= 0x2000,
+	ExtradataA	= 0x4000,
+};
+
+enum {					/* TxCfg */
+	LossofCRSiE	= 0x0040,
+	SQEerroriE	= 0x0080,
+	TxOKiE		= 0x0100,
+	OutofWindowiE	= 0x0200,
+	JabberiE		= 0x0400,
+	AnycolliE		= 0x0800,
+	Coll16iE		= 0x8000,
+};
+
+enum {					/* TxEvent */
+	LossofCRS	= 0x0040,
+	SQEerror		= 0x0080,
+	TxOK		= 0x0100,
+	OutofWindow	= 0x0200,
+	Jabber		= 0x0400,
+	NTxCols		= 0x7800,		/* number of Tx collisions (field) */
+	coll16		= 0x8000,
+};
+
+enum {					/* BufCfg */
+	SWintX		= 0x0040,
+	RxDMAiE		= 0x0080,
+	Rdy4TxiE		= 0x0100,
+	TxUnderruniE	= 0x0200,
+	RxMissiE		= 0x0400,
+	Rx128iE		= 0x0800,
+	TxColOvfiE	= 0x1000,
+	MissOvfloiE	= 0x2000,
+	RxDestiE		= 0x8000,
+};
+
+enum {					/* BufEvent */
+	SWint		= 0x0040,
+	RxDMAFrame	= 0x0080,
+	Rdy4Tx		= 0x0100,
+	TxUnderrun	= 0x0200,
+	RxMiss		= 0x0400,
+	Rx128		= 0x0800,
+	RxDest		= 0x8000,
+};
+
+enum {					/* RxMiss */
+	MissCount	= 0xffc0,
+};
+
+enum {					/* TxCol */
+	ColCount	= 0xffc0,
+};
+
+enum {					/* LineCtl */
+	SerRxOn		= 0x0040,
+	SerTxOn		= 0x0080,
+	Iface			= 0x0300,		/* (field) 01 - AUI, 00 - 10BASE-T, 10 - Auto select */
+	ModBackoffE	= 0x0800,
+	PolarityDis	= 0x1000,
+	DefDis		= 0x2000,
+	LoRxSquelch	= 0x4000,
+};
+
+enum {					/* LineSt */
+	LinkOK		= 0x0080,
+	AUI			= 0x0100,
+	TenBT		= 0x0200,
+	PolarityOK	= 0x1000,
+	CRS			= 0x4000,
+};
+
+enum {					/* SelfCtl */
+	RESET		= 0x0040,
+	SWSuspend	= 0x0100,
+	HWSleepE		= 0x0200,
+	HWStandbyE	= 0x0400,
+};
+
+enum {					/* SelfSt */
+	Active3V		= 0x0040,
+	INITD		= 0x0080,
+	SIBUSY		= 0x0100,
+	EepromPresent	= 0x0200,
+	EepromOK	= 0x0400,
+	ElPresent		= 0x0800,
+	EeSize		= 0x1000,
+};
+
+enum {					/* BusCtl */
+	ResetRxDMA	= 0x0040,
+	UseSA		= 0x0200,
+	MemoryE		= 0x0400,
+	DMABurst		= 0x0800,
+	EnableIRQ		= 0x8000,
+};
+
+enum {					/* BusST */
+	TxBidErr		= 0x0080,
+	Rdy4TxNOW	= 0x0100,
+};
+
+enum {					/* TestCtl */
+	FDX			= 0x4000,		/* full duplex */
+};
+
+enum {					/* TxCmd */
+	TxStart		= 0x00c0,		/* bytes before transmit starts (field) */
+		TxSt5	= 0x0000,		/* start after 5 bytes */
+		TxSt381	= 0x0040,		/* start after 381 bytes */
+		TxSt1021	= 0x0080,		/* start after 1021 bytes */
+		TxStAll	= 0x00c0,		/* start after the entire frame is in the cs8900 */
+	Force		= 0x0100,
+	Onecoll		= 0x0200,
+	InhibitCRC	= 0x1000,
+	TxPadDis		= 0x2000,
+};
+
+enum {	/* EEPROM format */
+	Edataoff	= 0x1C,	/* start of data (ether address) */
+	Edatalen	= 0x14,	/* data count in 16-bit words */
+};
+
+struct Ctlr {
+	Lock;
+	Block*	waiting;	/* waiting for space in FIFO */
+	int	model;
+	int	rev;
+
+	ulong	collisions;
+};
+
+static void
+regw(int reg, int val)
+{
+	if(DEBUG)
+		print("r%4.4ux <- %4.4ux\n", reg, val);
+	if(MEMORY){
+		REGW(reg, val);
+	}else{
+		out16(PpPtr, reg);
+		out16(PpData, val);
+	}
+}
+
+static int
+regr(int reg)
+{
+	int v;
+
+	if(MEMORY)
+		return REGR(reg);
+	out16(PpPtr, reg);
+	v = in16(PpData);
+	if(DEBUG)
+		print("r%4.4ux = %4.4ux\n", reg, v);
+	return v;
+}
+
+/*
+ * copy frames in and out, accounting for shorts aligned as longs in IO memory
+ */
+
+static void
+copypktin(void *ad, int len)
+{
+	ushort *s, *d;
+	int ns;
+
+	if(!MEMORY){
+		d = ad;
+		/*
+		 * contrary to data sheet DS271PP3 pages 77-78,
+		 * the data is not preceded by status & length
+		 * perhaps because it has been read directly.
+		 */
+		for(ns = len>>1; --ns >= 0;)
+			*d++ = in16(RxTxData);
+		if(len & 1)
+			*(uchar*)d = in16(RxTxData);
+		return;
+	}
+	d = ad;
+	s = (ushort*)IsaMemBase + MemBase + RxFrame;
+	for(ns = len>>1; --ns >= 0;){
+		*d++ = *s;
+		s += 2;
+	}
+	if(len & 1)
+		*(uchar*)d = *s;
+}
+
+static void
+copypktout(void *as, int len)
+{
+	ushort *s, *d;
+	int ns;
+
+	if(!MEMORY){
+		s = as;
+		ns = (len+1)>>1;
+		while(--ns >= 0)
+			out16(RxTxData, *s++);
+		return;
+	}
+	s = as;
+	d = (ushort*)IsaMemBase + MemBase + TxFrame;
+	ns = (len+1)>>1;
+	while(--ns >= 0){
+		*d = *s++;
+		d += 2;
+	}
+}
+
+static long
+ifstat(Ether* ether, void* a, long n, ulong offset)
+{
+	Ctlr *ctlr;
+	char *p;
+	int len;
+
+	if(n == 0)
+		return 0;
+
+	ctlr = ether->ctlr;
+	p = malloc(READSTR);
+	len = snprint(p, READSTR, "Overflow: %ud\n", ether->overflows);
+	len += snprint(p+len, READSTR-len, "CRC Error: %ud\n", ether->crcs);
+	snprint(p+len, READSTR-len, "Collision Seen: %lud\n", ctlr->collisions);
+
+	n = readstr(offset, a, n, p);
+	free(p);
+
+	return n;
+}
+
+static void
+promiscuous(void* arg, int on)
+{
+	USED(arg, on);
+}
+
+static void
+attach(Ether *ether)
+{
+	int reg;
+
+	USED(ether);
+	/* enable transmit and receive */
+	reg = regr(BusCtl);
+	regw(BusCtl, reg|EnableIRQ);
+	reg = regr(LineCtl);
+	regw(LineCtl, reg|SerRxOn|SerTxOn);
+	if(DEBUG){
+		iprint("bus=%4.4ux line=%4.4ux\n", regr(BusCtl), regr(LineCtl));
+		iprint("rc=%4.4ux tc=%4.4ux bc=%4.4ux\n", regr(RxCfg), regr(TxCfg), regr(BufCfg));
+	}
+}
+
+static void
+txstart(Ether *ether, int dowait)
+{
+	int len, status;
+	Ctlr *ctlr;
+	Block *b;
+
+	ctlr = ether->ctlr;
+	for(;;){
+		if((b = ctlr->waiting) == nil){
+			if((b = qget(ether->oq)) == nil)
+				break;
+		}else{
+			if(!dowait)
+				break;
+			ctlr->waiting = nil;
+		}
+		len = BLEN(b);
+		if(MEMORY){
+			regw(TxCmd, TxSt381);
+			regw(TxLen, len);
+		}else{
+			out16(TxCmdIO, TxStAll);
+			out16(TxLenIO, len);
+		}
+		status = regr(BusSt);
+		if((status & Rdy4TxNOW) == 0) {
+			ctlr->waiting = b;
+			break;
+		}
+		/*
+		 * Copy the packet to the transmit buffer.
+		 */
+		copypktout(b->rp, len);
+		freeb(b);
+	}
+}
+
+static void
+transmit(Ether *ether)
+{
+	Ctlr *ctlr;
+
+	ctlr = ether->ctlr;
+	ilock(ctlr);
+	txstart(ether, 0);
+	iunlock(ctlr);
+}
+
+static void
+interrupt(Ureg*, void *arg)
+{
+	Ether *ether;
+	Ctlr *ctlr;
+	int len, events, status;
+	Block *b;
+
+	ether = arg;
+	ctlr = ether->ctlr;
+	ilock(ctlr);
+	while((events = (MEMORY?regr(Isq):in16(IsqIO))) != 0) {
+		status = events&RegContent;
+		if(DEBUG)
+			iprint("status %4.4ux event %4.4ux\n", status, events);
+		switch(events&Regnum) {
+
+		case IsqBufEvent:
+			if(status&Rdy4Tx) {
+				if((b = ctlr->waiting) != nil){
+					ctlr->waiting = nil;
+					copypktout(b->rp, BLEN(b));
+					freeb(b);
+					/* wait for IsqTxEvent to send remaining packets in txstart */
+				}else
+					txstart(ether, 0);
+			}
+			break;
+
+		case IsqRxEvent:
+			if(status&RxOK) {
+				len = regr(RxLen);
+				if(DEBUG)
+					iprint("rxlen=%d\n", len);
+				if((b = iallocb(len)) != 0) {
+					copypktin(b->wp, len);
+					b->wp += len;
+					etheriq(ether, b, 1);
+				}
+			}
+			break;
+
+		case IsqTxEvent:
+			if(status&TxOK)
+				txstart(ether, 1);
+			break;
+
+		case IsqRxMiss:
+			ether->overflows++;
+			break;
+
+		case IsqTxCol:
+			ctlr->collisions++;
+			break;
+		}
+	}
+	iunlock(ctlr);
+}
+
+static int
+eepromwait(void)
+{
+	int i;
+
+	for(i=0; i<100000; i++)
+		if((regIOr(SelfSt) & SIBUSY) == 0)
+			return 0;
+	return -1;
+}
+
+static int
+eepromrd(void *buf, int off, int n)
+{
+	int i;
+	ushort *p;
+
+	p = buf;
+	n /= 2;
+	for(i=0; i<n; i++){
+		if(eepromwait() < 0)
+			return -1;
+		regIOw(Ecr, EEread | (off+i));
+		if(eepromwait() < 0)
+			return -1;
+		p[i] = regIOr(Edw);
+	}
+	return 0;
+}
+
+static int
+reset(Ether* ether)
+{
+	int i, reg, easet;
+	uchar ea[Eaddrlen];
+	ushort buf[Edatalen];
+	Ctlr *ctlr;
+
+	if(!MEMORY)
+		mmuphysmap(IsaIOBase, 64*1024);
+
+	delay(120);	/* allow time for chip to reset */
+
+	if(0){
+		*(ushort*)IsaIOBase = 0xDEAD;	/* force rubbish on bus */
+		for(i=0; i<100; i++){
+			if(in16(PpPtr) == 0x3000)
+				break;
+			delay(1);
+		}
+		if(i>=100){
+			iprint("failed init: reg(0xA): %4.4ux, should be 0x3000\n", in16(PpPtr));
+			return -1;
+		}
+	}
+iprint("8900: %4.4ux (selfst) %4.4ux (linest)\n", regIOr(SelfSt), regIOr(LineSt));
+iprint("8900: %4.4ux %4.4ux\n", regIOr(Ern), regIOr(Pic));
+
+	/* 
+	 * Identify the chip by reading the Pic register.
+	 * The EISA registration number is in the low word
+	 * and the product identification code in the high code.
+	 * The ERN for Crystal Semiconductor is 0x630e.
+	 * Bits 0-7 and 13-15 of the Pic should be zero for a CS8900.
+	 */
+	if(regIOr(Ern) != 0x630e || (regIOr(Pic) & 0xe0ff) != 0)
+		return -1;
+
+	if(ether->ctlr == nil)
+		ether->ctlr = malloc(sizeof(Ctlr));
+	ctlr = ether->ctlr;
+
+	reg = regIOr(Pic);
+	ctlr->model = reg>>14;
+	ctlr->rev = (reg >> 8) & 0x1F;
+
+	ether->mbps = 10;
+
+	memset(ea, 0, Eaddrlen);
+	easet = memcmp(ea, ether->ea, Eaddrlen);
+	memset(buf, 0, sizeof(buf));
+	if(regIOr(SelfSt) & EepromPresent) {	/* worth a look */
+		if(eepromrd(buf, Edataoff, sizeof(buf)) >= 0){
+			for(i=0; i<3; i++){
+				ether->ea[2*i] = buf[i];
+				ether->ea[2*i+1] = buf[i] >> 8;
+			}
+			easet = 1;
+		}else
+			iprint("cs8900: can't read EEPROM\n");
+	}
+	if(!easet){
+		iprint("cs8900: ethernet address not configured\n");
+		return -1;
+	}
+	memmove(ea, ether->ea, Eaddrlen);
+
+	if(DORESET){
+		/*
+		 * Reset the chip and ensure 16-bit mode operation
+		 */
+		regIOw(SelfCtl, RESET);
+		delay(10);
+		i=in8(PpPtr); 	USED(i);
+		i=in8(PpPtr+1); USED(i);
+		i=in8(PpPtr); 	USED(i);
+		i=in8(PpPtr+1);	USED(i);
+
+		/*
+		 * Wait for initialisation and EEPROM reads to complete
+		 */
+		i=0;
+		for(;;) {
+			short st = regIOr(SelfSt);
+			if((st&SIBUSY) == 0 && st&INITD)
+				break;
+			if(i++ > 1000000)
+				panic("cs8900: initialisation failed");
+		}
+	}
+
+	if(MEMORY){
+		/*
+		 * Enable memory mode operation.
+		 */
+		regIOw(Mba, MemBase & 0xffff);
+		regIOw(Mba+2, MemBase >> 16);
+		regIOw(BusCtl, MemoryE|UseSA);
+	}
+
+	/*
+	 * Enable 10BASE-T half duplex, transmit in interrupt mode
+	 */
+	reg = regr(LineCtl);
+	regw(LineCtl, reg&~Iface);
+	reg = regr(TestCtl);
+	if(ether->fullduplex)
+		regw(TestCtl, reg|FDX);
+	else
+		regw(TestCtl, reg&~FDX);
+	regw(BufCfg, Rdy4TxiE|TxUnderruniE);
+	regw(TxCfg, TxOKiE|AnycolliE|LossofCRSiE|Coll16iE);
+	regw(RxCfg, RxOKiE|CRCerroriE|RuntiE|ExtradataiE);
+	regw(RxCtl, RxOKA|IndividualA|BroadcastA);
+
+	for(i=0; i<Eaddrlen; i+=2)
+		regw(IndAddr+i, ea[i] | (ea[i+1] << 8));
+
+	/* IRQ tied to INTRQ0 */
+	regw(Intr, 0);
+
+	/*
+	 * Linkage to the generic ethernet driver.
+	 */
+	ether->attach = attach;
+	ether->transmit = transmit;
+	ether->interrupt = interrupt;
+	ether->ifstat = ifstat;
+
+	ether->arg = ether;
+	ether->promiscuous = promiscuous;
+
+	ether->itype = BusGPIOrising;	/* TO DO: this shouldn't be done here */
+
+	return 0;
+}
+
+void
+ether8900link(void)
+{
+	addethercard("CS8900",  reset);
+}
--- /dev/null
+++ b/os/cerf1110/fns.h
@@ -1,0 +1,179 @@
+#include "../port/portfns.h"
+
+ulong	aifinit(uchar *aifarr);
+void	aamloop(int);
+int	archaudiopower(int);
+void	archaudiomute(int);
+void	archaudioamp(int);
+int	archaudiospeed(int, int);
+void	archcodecreset(void);
+void	archconfinit(void);
+void	archconsole(void);
+int	archflash12v(int);
+int	archhooksw(int);
+long	archkprofmicrosecondspertick(void);
+void	archkprofenable(int);
+void	archlcdenable(int);
+void	archpowerdown(void);
+void	archpowerup(void);
+void	archreboot(void);
+void	archreset(void);
+vlong	archrdtsc(void);
+ulong archrdtsc32(void);
+void	archuartpower(int, int);
+void	blankscreen(int);
+ulong	call_apcs(ulong addr, int nargs, ...);
+ulong	call_apcs0(ulong addr);
+ulong	call_apcs1(ulong addr, ulong a1);
+ulong	call_apcs2(ulong addr, ulong a1, ulong a2);
+ulong	call_apcs3(ulong addr, ulong a1, ulong a2, ulong a3);
+void	cisread(int slotno, void (*f)(int, uchar *));
+void	clockcheck(void);
+void	clockinit(void);
+void	clockpoll(void);
+#define	coherence()		/* nothing to do for cache coherence for uniprocessor */
+void	cursorhide(void);
+void	cursorunhide(void);
+void	dcflush(void*, ulong);
+void	dcflushall(void);
+void	dcinval(void);
+int	dmaidle(Dma*);
+Dma*	dmasetup(int device, int direction, int bigend, void(*)(void*,ulong), void*);
+int	dmastart(Dma*, void*, int);
+int	dmacontinue(Dma*, void*, int);
+void	dmastop(Dma*);
+int	dmaerror(Dma*);
+void	dmafree(Dma*);
+void	dmareset(void);
+void	dmawait(Dma*);
+void dumplongs(char *, ulong *, int);
+void	dumpregs(Ureg* ureg);
+void	dumpstack(void);
+int	fpiarm(Ureg*);
+void	fpinit(void);
+ulong	getcallerpc(void*);
+ulong	getcpsr(void);
+ulong	getcpuid(void);
+ulong	getspsr(void);
+void	gotopc(ulong);
+
+void	icflushall(void);
+void	_idlemode(void);
+void	(*idle)(void);
+void	idlehands(void);
+int	inb(ulong);
+int	ins(ulong);
+ulong	inl(ulong);
+void	outb(ulong, int);
+void	outs(ulong, int);
+void	outl(ulong, ulong);
+void inss(ulong, void*, int);
+void outss(ulong, void*, int);
+void	insb(ulong, void*, int);
+void	outsb(ulong, void*, int);
+void	intrdisable(int, void (*)(Ureg*, void*), void*, int, char*);
+void	intrenable(int, void (*)(Ureg*, void*), void*, int, char*);
+void	iofree(int);
+#define	iofree(x)
+void	ioinit(void);
+int	iounused(int, int);
+int	ioalloc(int, int, int, char*);
+#define	ioalloc(a,b,c,d) 0
+int	iprint(char*, ...);
+void	installprof(void (*)(Ureg *, int));
+int	isvalid_va(void*);
+void	kbdinit(void);
+void	L3init(void);
+int	L3read(int, void*, int);
+int	L3write(int, void*, int);
+void	lcd_setbacklight(int);
+void	lcd_sethz(int);
+void	lights(ulong);
+void	links(void);
+ulong	mcpgettfreq(void);
+void	mcpinit(void);
+void	mcpsettfreq(ulong tfreq);
+void	mcpspeaker(int, int);
+void	mcptelecomsetup(ulong hz, int adm, int xint, int rint);
+ushort	mcpadcread(int ts);
+void	mcptouchsetup(int ts);
+void	mcptouchintrenable(void);
+void	mcptouchintrdisable(void);
+void	mcpgpiowrite(ushort mask, ushort data);
+void	mcpgpiosetdir(ushort mask, ushort dir);
+ushort	mcpgpioread(void);
+void*	minicached(void*);
+void	minidcflush(void);
+void	mmuenable(ulong);
+ulong	mmugetctl(void);
+ulong	mmugetdac(void);
+ulong	mmugetfar(void);
+ulong	mmugetfsr(void);
+void	mmuinit(void);
+void*	mmuphysmap(ulong, ulong);
+void	mmuputctl(ulong);
+void	mmuputdac(ulong);
+void	mmuputfsr(ulong);
+void	mmuputttb(ulong);
+void	mmureset(void);
+void	mouseinit(void);
+void	nowriteSeg(void *, void *);
+void*	pa2va(ulong);
+void	pcmcisread(PCMslot*);
+int	pcmcistuple(int, int, int, void*, int);
+PCMmap*	pcmmap(int, ulong, int, int);
+void	pcmunmap(int, PCMmap*);
+int	pcmpin(int slot, int type);
+void	pcmpower(int slotno, int on);
+int	pcmpowered(int);
+void	pcmreset(int);
+void	pcmsetvcc(int, int);
+void	pcmsetvpp(int, int);
+int	pcmspecial(char *idstr, ISAConf *isa);
+void	pcmspecialclose(int slotno);
+void	pcmintrenable(int, void (*)(Ureg*, void*), void*);
+void	powerenable(void (*)(int));
+void	powerdisable(void (*)(int));
+void	powerdown(void);
+void	powerinit(void);
+void	powersuspend(void);
+#define procsave(p)
+#define procrestore(p)
+long	rtctime(void);
+void	screeninit(void);
+void	(*screenputs)(char*, int);
+int	segflush(void*, ulong);
+void	setpanic(void);
+void	setr13(int, void*);
+int	splfhi(void);
+int	splflo(void);
+void	_suspendcode(void);
+void	tlbinvalidate(void);
+void	tlbinvalidateaddr(void*);
+void	trapinit(void);
+void	trapstacks(void);
+void	trapspecial(int (*)(Ureg *, uint));
+int	uartprint(char*, ...);
+void	uartspecial(int, int, char, Queue**, Queue**, int (*)(Queue*, int));
+ulong	va2pa(void*);
+void	vectors(void);
+void	vtable(void);
+#define	waserror()	(up->nerrlab++, setlabel(&up->errlab[up->nerrlab-1]))
+int	wasbusy(int);
+
+#define KADDR(p)	((void *) p)
+#define PADDR(v)	va2pa((void*)(v))
+
+// #define timer_start()	(*OSCR)
+// #define timer_ticks(t)	(*OSCR - (ulong)(t))
+ulong	timer_start(void);
+ulong	timer_ticks(ulong);
+int 	timer_devwait(ulong *adr, ulong mask, ulong val, int ost);
+void 	timer_setwatchdog(int ost);
+void 	timer_delay(int ost);
+ulong	ms2tmr(int ms);
+int	tmr2ms(ulong t);
+void	delay(int ms);
+ulong	us2tmr(int us);
+int	tmr2us(ulong t);
+void 	microdelay(int us);
--- /dev/null
+++ b/os/cerf1110/io.h
@@ -1,0 +1,1 @@
+#include "../sa1110/sa1110io.h"
--- /dev/null
+++ b/os/cerf1110/main.c
@@ -1,0 +1,255 @@
+#include "u.h"
+#include "../port/lib.h"
+#include "mem.h"
+#include "dat.h"
+#include "fns.h"
+#include "../port/error.h"
+#include "io.h"
+#include "version.h"
+
+Mach *m = (Mach*)MACHADDR;
+Proc *up = 0;
+Vectorpage	*page0 = (Vectorpage*)KZERO;	/* doubly-mapped to AIVECADDR */
+Conf conf;
+
+extern ulong kerndate;
+extern int cflag;
+extern int main_pool_pcnt;
+extern int heap_pool_pcnt;
+extern int image_pool_pcnt;
+ulong cpuidlecount;
+
+static void
+poolsizeinit(void)
+{
+	ulong nb;
+
+	nb = conf.npage*BY2PG;
+	poolsize(mainmem, (nb*main_pool_pcnt)/100, 0);
+	poolsize(heapmem, (nb*heap_pool_pcnt)/100, 0);
+	poolsize(imagmem, (nb*image_pool_pcnt)/100, 1);
+}
+
+void
+main(void)
+{
+	memset(edata, 0, end-edata);		/* clear the BSS */
+	memset(m, 0, sizeof(Mach));	/* clear the mach struct */
+	conf.nmach = 1;
+	archreset();
+	dmareset();
+	quotefmtinstall();
+	confinit();
+	xinit();
+	mmuinit();
+	poolinit();
+	poolsizeinit();
+	trapinit();
+	clockinit(); 
+	printinit();
+	screeninit();
+	procinit();
+	links();
+	chandevreset();
+
+	eve = strdup("inferno");
+
+	archconsole();
+	kbdinit();
+
+	print("%ld MHz id %8.8lux\n", (m->cpuhz+500000)/1000000, getcpuid());
+	print("\nInferno %s\n", VERSION);
+	print("Vita Nuova\n");
+	print("conf %s (%lud) jit %d\n\n",conffile, kerndate, cflag);
+
+	userinit();
+	schedinit();
+}
+
+void
+reboot(void)
+{
+	exit(0);
+}
+
+void
+halt(void)
+{
+	spllo();
+	print("cpu halted\n");
+	for(;;){
+		/* nothing to do */
+	}
+}
+
+void
+confinit(void)
+{
+	ulong base;
+
+	archconfinit();
+
+	base = PGROUND((ulong)end);
+	conf.base0 = base;
+
+	conf.base1 = 0;
+	conf.npage1 = 0;
+
+	conf.npage0 = (conf.topofmem - base)/BY2PG;
+
+	conf.npage = conf.npage0 + conf.npage1;
+	conf.ialloc = (((conf.npage*(main_pool_pcnt))/100)/2)*BY2PG;
+
+	conf.nproc = 100 + ((conf.npage*BY2PG)/MB)*5;
+	conf.nmach = 1;
+
+}
+
+void
+init0(void)
+{
+	Osenv *o;
+	char buf[2*KNAMELEN];
+
+	up->nerrlab = 0;
+
+	spllo();
+
+	if(waserror())
+		panic("init0 %r");
+	/*
+	 * These are o.k. because rootinit is null.
+	 * Then early kproc's will have a root and dot.
+	 */
+	o = up->env;
+	o->pgrp->slash = namec("#/", Atodir, 0, 0);
+	cnameclose(o->pgrp->slash->name);
+	o->pgrp->slash->name = newcname("/");
+	o->pgrp->dot = cclone(o->pgrp->slash);
+
+	chandevinit();
+
+	if(!waserror()){
+		ksetenv("cputype", "arm", 0);
+		snprint(buf, sizeof(buf), "arm %s", conffile);
+		ksetenv("terminal", buf, 0);
+		poperror();
+	}
+
+	poperror();
+
+	disinit("/osinit.dis");
+}
+
+void
+userinit(void)
+{
+	Proc *p;
+	Osenv *o;
+
+	p = newproc();
+	o = p->env;
+
+	o->fgrp = newfgrp(nil);
+	o->pgrp = newpgrp();
+	o->egrp = newegrp();
+	kstrdup(&o->user, eve);
+
+	strcpy(p->text, "interp");
+
+	p->fpstate = FPINIT;
+
+	/*
+	 * Kernel Stack
+	 *
+	 * N.B. The -12 for the stack pointer is important.
+	 *	4 bytes for gotolabel's return PC
+	 */
+	p->sched.pc = (ulong)init0;
+	p->sched.sp = (ulong)p->kstack+KSTACK-8;
+
+	ready(p);
+}
+
+void
+exit(int inpanic)
+{
+	up = 0;
+
+	/* Shutdown running devices */
+	chandevshutdown();
+
+	if(inpanic && 0){
+		print("Hit the reset button\n");
+		for(;;)
+			clockpoll();
+	}
+	archreboot();
+}
+
+static void
+linkproc(void)
+{
+	spllo();
+	if (waserror())
+		print("error() underflow: %r\n");
+	else
+		(*up->kpfun)(up->arg);
+	pexit("end proc", 1);
+}
+
+void
+kprocchild(Proc *p, void (*func)(void*), void *arg)
+{
+	p->sched.pc = (ulong)linkproc;
+	p->sched.sp = (ulong)p->kstack+KSTACK-8;
+
+	p->kpfun = func;
+	p->arg = arg;
+}
+
+void
+idlehands(void)
+{
+	cpuidlecount++;
+	INTRREG->iccr = 1;	/* only unmasked interrupts will stop idle mode */
+	idle();
+}
+
+/* stubs */
+void
+setfsr(ulong)
+{
+}
+
+ulong
+getfsr()
+{
+	return 0;
+}
+
+void
+setfcr(ulong)
+{
+}
+
+ulong
+getfcr()
+{
+	return 0;
+}
+
+void
+fpinit(void)
+{
+}
+
+void
+FPsave(void*)
+{
+}
+
+void
+FPrestore(void*)
+{
+}
--- /dev/null
+++ b/os/cerf1110/mem.h
@@ -1,0 +1,200 @@
+/*
+ * Memory and machine-specific definitions.  Used in C and assembler.
+ */
+
+/*
+ * Sizes
+ */
+#define	BI2BY		8			/* bits per byte */
+#define BI2WD		32			/* bits per word */
+#define	BY2WD		4			/* bytes per word */
+#define	BY2V		8			/* bytes per double word */
+#define	BY2PG		4096			/* bytes per page */
+#define	WD2PG		(BY2PG/BY2WD)		/* words per page */
+#define	PGSHIFT		12			/* log(BY2PG) */
+#define ROUND(s, sz)	(((s)+(sz-1))&~(sz-1))
+#define PGROUND(s)	ROUND(s, BY2PG)
+#define BIT(n)		(1<<n)
+#define BITS(a,b)	((1<<(b+1))-(1<<a))
+
+#define	MAXMACH		1			/* max # cpus system can run */
+
+/*
+ * Time
+ */
+#define	HZ		(100)			/* clock frequency */
+#define	MS2HZ		(1000/HZ)		/* millisec per clock tick */
+#define	TK2SEC(t)	((t)/HZ)		/* ticks to seconds */
+#define	MS2TK(t)	((t)/MS2HZ)		/* milliseconds to ticks */
+
+/*
+ * More accurate time
+ */
+#define CLOCKFREQ	3686400
+#define MS2TMR(t)	((ulong)(((uvlong)(t)*CLOCKFREQ)/1000))
+#define US2TMR(t)	((ulong)(((uvlong)(t)*CLOCKFREQ)/1000000))
+
+/*
+ *  Address spaces
+ *	nearly everything maps 1-1 with physical addresses
+ *	0 to 1Mb is not mapped
+ *	cache strategy varies as needed (see mmu.c)
+ */
+
+#define KZERO		0xC0000000
+#define MACHADDR	(KZERO+0x00001000)
+#define KTTB		(KZERO+0x00004000)
+#define KTZERO	(KZERO+0x00008010)
+#define KSTACK	8192			/* Size of kernel stack */
+#define FLASHMEM	0x50000000	/* map flash at phys 0 to otherwise unused virtual space */
+#define FLUSHMEM	0xE0000000	/* internally decoded zero memory (for cache flushing) */
+#define DCFADDR	FLUSHMEM	/* cached and buffered for cache writeback */
+#define MCFADDR	(FLUSHMEM+(1<<20))	/* cached and unbuffered for minicache writeback */
+#define UCDRAMZERO	0xC8000000	/* base of memory doubly-mapped as uncached */
+#define AIVECADDR	0xFFFF0000	/* alternative interrupt vector address (other is 0) */
+
+/*
+ * Physical addresses
+ */
+#define PHYSFLASH0	0x00000000	/* flash (chip select 0) */
+#define PHYSCS1		0x08000000	/* static chip select 1 */
+#define PHYSCS2		0x10000000	/* static chip select 2 */
+#define PHYSCS3		0x18000000	/* static chip select 3 */
+#define PHYSPCMCIA0	0x20000000	/* PCMCIA socket 0 space */
+#define PHYSPCMCIA1	0x30000000	/* PCMCIA socket 1 space */
+#define PCMCIASIZE		0x10000000	/* they're both huge */
+#define PHYSCS4		0x40000000	/* static chip select 4 */
+#define PHYSCS5		0x48000000	/* static chip select 5 */
+#define PHYSSERIAL(n)	(0x80000000+0x10000*(n))	/* serial devices */
+#define PHYSUSB		0x80000000
+#define PHYSGPCLK		0x80020060
+#define PHYSMCP		0x80060000
+#define PHYSSSP		0x80070060
+#define PHYSOSTMR		0x90000000	/* timers */
+#define PHYSRTC		0x90010000	/* real time clock */
+#define PHYSPOWER		0x90020000	/* power management registers */
+#define PHYSRESET		0x90030000	/* reset controller */
+#define PHYSGPIO		0x90040000
+#define PHYSINTR		0x90050000	/* interrupt controller */
+#define PHYSPPC		0x90060000	/* peripheral pin controller */
+#define PHYSMEMCFG	0xA0000000	/* memory configuration */
+#define PHYSDMA		0xB0000000	/* DMA controller */
+#define PHYSLCD		0xB0100000	/* LCD controller */
+#define PHYSMEM0		0xC0000000
+#define PHYSFLUSH0	0xE0000000	/* internally decoded, for cache flushing */
+
+/*
+ * Memory Interface Control Registers
+ */
+#define 	MDCNFG	(PHYSMEMCFG)	/* memory controller configuration */
+#define	MDCAS0	(PHYSMEMCFG+4)
+#define	MDCAS1	(PHYSMEMCFG+8)
+#define	MDCAS2	(PHYSMEMCFG+0xC)
+#define	MSC0	(PHYSMEMCFG+0x10)
+#define	MSC1	(PHYSMEMCFG+0x14)
+#define	MSC2	(PHYSMEMCFG+0x2C)	/* SA1110, but not SA1100 */
+
+#define	MSCx(RRR, RDN, RDF, RBW, RT)	((((RRR)&0x7)<<13)|(((RDN)&0x1F)<<8)|(((RDF)&0x1F)<<3)|(((RBW)&1)<<2)|((RT)&3))
+
+#define	CACHELINELOG	5
+#define	CACHELINESZ	(1<<CACHELINELOG)
+
+/*
+ * PSR
+ */
+#define PsrMusr		0x10 	/* mode */
+#define PsrMfiq		0x11 
+#define PsrMirq		0x12
+#define PsrMsvc		0x13
+#define PsrMabt		0x17
+#define PsrMund		0x1B
+#define PsrMsys		0x1F
+#define PsrMask		0x1F
+
+#define PsrDfiq		0x00000040	/* disable FIQ interrupts */
+#define PsrDirq		0x00000080	/* disable IRQ interrupts */
+
+#define PsrV		0x10000000	/* overflow */
+#define PsrC		0x20000000	/* carry/borrow/extend */
+#define PsrZ		0x40000000	/* zero */
+#define PsrN		0x80000000	/* negative/less than */
+
+/*
+ * Internal MMU coprocessor registers
+ */
+#define CpCPUID	0		/* R: */
+#define CpControl	1		/* R/W: */
+#define CpTTB		2		/* R/W: translation table base */
+#define CpDAC		3		/* R/W: domain access control */
+#define CpFSR		5		/* R/W: fault status */
+#define CpFAR		6		/* R/W: fault address */
+#define CpCacheCtl	7		/* W: */
+#define CpTLBops	8		/* W: TLB operations */
+#define CpRBops	9		/* W: Read Buffer operations */
+#define CpPID		13		/* R/W: Process ID Virtual Mapping */
+#define CpDebug	14		/* R/W: debug registers */
+#define CpTest		15		/* W: Test, Clock and Idle Control */
+
+/*
+ * Coprocessors
+ */
+#define CpMMU		15
+#define CpPWR		15
+
+/*
+ * CpControl bits
+ */
+#define CpCmmu	0x00000001	/* M: MMU enable */
+#define CpCalign	0x00000002	/* A: alignment fault enable */
+#define CpCDcache	0x00000004	/* C: instruction/data cache on */
+#define CpCwb		0x00000008	/* W: write buffer turned on */
+#define CpCi32		0x00000010	/* P: 32-bit programme space */
+#define CpCd32	0x00000020	/* D: 32-bit data space */
+#define CpCbe		0x00000080	/* B: big-endian operation */
+#define CpCsystem	0x00000100	/* S: system permission */
+#define CpCrom	0x00000200	/* R: ROM permission */
+#define CpCIcache	0x00001000	/* I: Instruction Cache on */
+#define CpCaltivec	0x00002000	/* X: alternative interrupt vectors */
+
+/*
+ * MMU
+ */
+/*
+ * Small pages:
+ *	L1: 12-bit index -> 4096 descriptors -> 16Kb
+ *	L2:  8-bit index ->  256 descriptors ->  1Kb
+ * Each L2 descriptor has access permissions for 4 1Kb sub-pages.
+ *
+ *	TTB + L1Tx gives address of L1 descriptor
+ *	L1 descriptor gives PTBA
+ *	PTBA + L2Tx gives address of L2 descriptor
+ *	L2 descriptor gives PBA
+ */
+#define MmuSection	(1<<20)
+#define MmuLargePage	(1<<16)
+#define MmuSmallPage	(1<<12)
+#define MmuTTB(pa)	((pa) & ~0x3FFF)	/* translation table base */
+#define MmuL1x(pa)	(((pa)>>20) & 0xFFF)	/* L1 table index */
+#define MmuPTBA(pa)	((pa) & ~0x3FF)		/* page table base address */
+#define MmuL2x(pa)	(((pa)>>12) & 0xFF)	/* L2 table index */
+#define MmuPBA(pa)	((pa) & ~0xFFF)		/* page base address */
+#define MmuSBA(pa)	((pa) & ~0xFFFFF)	/* section base address */
+
+#define MmuL1type	0x03
+#define MmuL1page	0x01			/* descriptor is for L2 pages */
+#define MmuL1section	0x02			/* descriptor is for section */
+
+#define MmuL2invalid	0x000
+#define MmuL2large	0x001			/* large */
+#define MmuL2small	0x002			/* small */
+#define MmuWB		0x004			/* data goes through write buffer */
+#define MmuIDC		0x008			/* data placed in cache */
+
+#define MmuDAC(d)	(((d) & 0xF)<<5)	/* L1 domain */
+#define MmuAP(i, v)	((v)<<(((i)*2)+4))	/* access permissions */
+#define MmuL1AP(v)	MmuAP(3, (v))
+#define MmuL2AP(v)	MmuAP(3, (v))|MmuAP(2, (v))|MmuAP(1, (v))|MmuAP(0, (v))
+#define MmuAPsro	0			/* supervisor ro if S|R */
+#define MmuAPsrw	1			/* supervisor rw */
+#define MmuAPuro	2			/* supervisor rw + user ro */
+#define MmuAPurw	3			/* supervisor rw + user rw */
--- /dev/null
+++ b/os/cerf1110/mkfile
@@ -1,0 +1,101 @@
+<../../mkconfig
+TKSTYLE=std
+
+#Configurable parameters
+
+CONF=cerf				#default configuration
+CONFLIST=cerf
+
+SYSTARG=$OSTARG
+OBJTYPE=arm
+INSTALLDIR=$ROOT/Inferno/$OBJTYPE/bin	#path of directory where kernel is installed
+#end configurable parameters
+
+<$ROOT/mkfiles/mkfile-$SYSTARG-$OBJTYPE	#set vars based on target system
+
+<| $SHELLNAME ../port/mkdevlist $CONF	#sets $IP, $DEVS, $ETHERS, $VGAS, $PORT, $MISC, $LIBS, $OTHERS
+
+KTZERO=0xC0008010
+
+OBJ=\
+	l.$O\
+	clock.$O\
+	dma.$O\
+	fpi.$O\
+	fpiarm.$O\
+	fpimem.$O\
+	main.$O\
+	mmu.$O\
+	trap.$O\
+	$CONF.root.$O\
+	$IP\
+	$DEVS\
+	$ETHERS\
+	$LINKS\
+	$PORT\
+	$MISC\
+	$OTHERS\
+
+LIBNAMES=${LIBS:%=lib%.a}
+LIBDIRS=$LIBS
+
+HFILES=\
+	mem.h\
+	dat.h\
+	fns.h\
+	io.h\
+	../sa1110/sa1110io.h\
+	../sa1110/fpi.h\
+
+CFLAGS=-wFV -I$ROOT/Inferno/$OBJTYPE/include -I$ROOT/include -I$ROOT/libinterp -I../sa1110
+KERNDATE=`{$NDATE}
+
+default:V: i$CONF i$CONF.p9
+
+install:V: $INSTALLDIR/i$CONF
+
+i$CONF: $OBJ $CONF.c $CONF.root.h $LIBNAMES i$CONF.p9
+	$CC $CFLAGS '-DKERNDATE='$KERNDATE $CONF.c
+	$LD -s -o $target -H5 -T0xC0008010 -R4 -l $OBJ $CONF.$O $LIBFILES
+
+i$CONF.p9: $OBJ $CONF.c $CONF.root.h $LIBNAMES
+	$CC $CFLAGS '-DKERNDATE='$KERNDATE $CONF.c
+	$LD -o $target -T0xC0008010 -R4 -l $OBJ $CONF.$O $LIBFILES
+
+<../port/portmkfile
+
+%.$O:		../sa1110/%.c
+		$CC $CFLAGS -I. ../sa1110/$stem.c
+
+%.$O:		../sa1110/%.s
+	$AS -I. -I../sa1110 ../sa1110/$stem.s
+
+../init/$INIT.dis:	../init/$INIT.b
+		cd ../init; mk $INIT.dis
+
+clock.$O:	$ROOT/Inferno/$OBJTYPE/include/ureg.h
+devether.$O:	$ROOT/Inferno/$OBJTYPE/include/ureg.h
+devsapcm.$O:	$ROOT/Inferno/$OBJTYPE/include/ureg.h
+main.$O:	$ROOT/Inferno/$OBJTYPE/include/ureg.h
+trap.$O:	$ROOT/Inferno/$OBJTYPE/include/ureg.h
+
+devether.$O $ETHERS:	../sa1110/etherif.h ../port/netif.h
+$IP devip.$O:		../ip/ip.h
+io.h:N:	../sa1110/sa1110io.h
+
+dummy:V:
+
+# to be moved to libinterp 
+bench.h:D: ../../module/bench.m
+	rm -f $target && limbo -a -I../../module ../../module/bench.m > $target
+benchmod.h:D:  ../../module/bench.m
+	rm -f $target && limbo -t Bench -I../../module ../../module/bench.m > $target
+devbench.$O: bench.h benchmod.h
+
+devaudio.$O:	devaudio.c
+	$CC $CFLAGS devaudio.c
+
+arch$CONF.$O:	../sa1110/etherif.h
+
+devuart.$O:	../sa1110/devuart.c
+	$CC $CFLAGS ../sa1110/devuart.c
--- /dev/null
+++ b/os/cerf250/NOTICE
@@ -1,0 +1,2 @@
+Inferno® Copyright © 1996-1999 Lucent Technologies Inc.  All rights reserved.
+Cerfcube 250 Inferno port Copyright © 2000,2003,2004 Vita Nuova Holdings Limited.  All rights reserved.
--- /dev/null
+++ b/os/cerf250/README
@@ -1,0 +1,44 @@
+Booting Inferno on a Cerfboard 250
+
+This is a preliminary version (work in progress) of Inferno
+on an Intrinsyc Cerfboard 250 (without daughterboard[s]).
+It and ../pxa were initially the results of a fairly idle `afternoon and an evening' port.
+A little work has been done on it since then.
+Updates will be available shortly to complete peripheral support
+(at least for the Cerfboard 250), and provide suspend mode.
+
+It allows Inferno to boot up and take resources from the net,
+chatting on the console.  I2C to the PCF8563 clock and EEPROMs is
+also supported.
+
+Substitute appropriate your own directory's name for /usr/inferno
+in the following.
+
+Build the /usr/inferno/os/cerf250 kernel into /usr/inferno/os/cerf250/icerf:
+	mk
+It uses common PXA25x code in ../pxa, as well as ../port etc.
+
+Make that icerf file available to the cerf cube by tftp.  How you do that depends on
+your host system.
+
+It should then be easy:
+
+1. Reset the cerf cube (power off/on), and quickly, during `hit a key ...'
+	hit a key.
+
+2. type
+	network.start()
+	download 10.0.0.1 "/usr/inferno/os/cerf250/icerf" 0xa0020000
+    with appropriate substitution for boot server and file name.
+
+3. on success
+	jump 0xa0020020
+
+it should run.
+
+once you're happy with it, the kernel image can replace the Linux one in flash.
+i plan, however, to use my sqz code to compress it by about 50% with
+fast decompression.
+
+forsyth@vitanuova.com
+Fri Mar 19 16:42:07 GMT 2004
--- /dev/null
+++ b/os/cerf250/archcerf.c
@@ -1,0 +1,266 @@
+/*
+ * Intrinsyc Cerfboard 250
+ */
+#include	"u.h"
+#include	"../port/lib.h"
+#include	"mem.h"
+#include	"dat.h"
+#include	"fns.h"
+#include	"../port/error.h"
+#include	"io.h"
+#include	"draw.h"
+#include	<memdraw.h>
+
+#include "../port/netif.h"
+#include "etherif.h"
+#include	"../port/flashif.h"
+
+enum {
+	/* Cerf250 GPIO assignment */
+	GPIO_LED0_o = 4,	/* active high */
+	GPIO_nCFD_i=	14,	/* compact flash detect, active low: falling edge */
+
+	Maxmac=	4,	/* number of MAC addresses taken from EEPROM */
+};
+
+static uchar	macaddrs[Maxmac][Eaddrlen];
+
+void
+archreset(void)
+{
+	GpioReg *g = GPIOREG;
+
+	g->grer[0] = 0;
+	g->grer[1] = 0;
+	g->grer[2] = 0;
+	g->gfer[0] = 0;
+	g->gfer[1] = 0;
+	g->gfer[2] = 0;
+	g->gedr[0] = g->gedr[0];
+	g->gedr[1] = g->gedr[1];
+	g->gedr[2] = g->gedr[2];
+
+	g->gafr[2] |= GPAF(GPIO_FFRXD_1_i, 1);
+	g->gafr[2] |= GPAF(GPIO_FFTXD_2_o, 2);
+	g->gpdr[0] |= GPB(GPIO_LED0_o);
+	g->gpdr[1] |= GPB(GPIO_FFTXD_2_o);
+	g->gpsr[0] = GPB(GPIO_LED0_o);
+
+	uartdebuginit();
+}
+
+void
+ledset(int n)
+{
+	if(n)
+		GPIOREG->gpsr[0] = GPB(GPIO_LED0_o);
+	else
+		GPIOREG->gpcr[0] = GPB(GPIO_LED0_o);
+}
+
+void
+archconfinit(void)
+{
+	int w;
+
+	conf.topofmem = PHYSMEM0+64*MB;
+//	w = PMGRREG->ppcr & 0x1f;
+	m->cpuhz = CLOCKFREQ*(27*2*2);
+}
+
+void
+archuartpower(int, int)
+{
+}
+
+void
+kbdinit(void)
+{
+}
+
+void
+archreboot(void)
+{
+	dcflushall();
+	ledset(1);
+//	GPIOREG->gedr = 1<<0;
+	mmuputctl(mmugetctl() & ~CpCaltivec);	/* restore bootstrap's vectors */
+//	RESETREG->rsrr = 1;	/* software reset */
+	for(;;)
+		spllo();
+}
+
+void
+archflashwp(Flash*, int)
+{
+}
+
+/*
+ * for devflash.c:/^flashreset
+ * retrieve flash type, virtual base and length and return 0;
+ * return -1 on error (no flash)
+ */
+int
+archflashreset(int bank, Flash *f)
+{
+	if(bank != 0)
+		return -1;
+	f->type = "cfi16";
+	f->addr = KADDR(FLASHMEM);
+	f->size = 0;
+	f->width = 4;
+	f->interleave = 1;
+	return 0;
+}
+
+/*
+ * set ether parameters: the contents should be derived from EEPROM or NVRAM
+ */
+int
+archether(int ctlno, Ether *ether)
+{
+	if(ctlno > 0)
+		return -1;
+	sprint(ether->type, "91c111");
+	ether->mem = PHYSCS1;
+	ether->nopt = 0;
+	ether->port = 0x300;	/* there isn't an ether EEPROM; use chip's default */
+	ether->irq = 21;	 /* GPIO */
+	ether->itype = GPIOrising;	/* active high */
+//	gpioreserve(ether->irq);
+//	gpioconfig(ether->irq, Gpio_gpio | Gpio_in);
+	memmove(ether->ea, macaddrs[ctlno], Eaddrlen);
+	return 1;
+}
+
+/*
+ * pcmcia
+ */
+int
+pcmpowered(int slotno)
+{
+	if(slotno)
+		return 0;
+	return 3;
+}
+
+void
+pcmpower(int slotno, int on)
+{
+	USED(slotno, on);
+}
+
+void
+pcmreset(int slot)
+{
+	if(slot != 0)
+		return;
+//	GPIOREG->gpsr = CFReset;
+//	delay(100);
+//	GPIOREG->gpcr = CFReset;
+}
+
+int
+pcmpin(int slot, int type)
+{
+	if(slot)
+		return -1;
+	return -1;
+//	switch(type){
+//	case PCMready:
+//		return CFRdypin;
+//	case PCMeject:
+//		return CFnCDxpin;
+//	case PCMstschng:
+//		return -1;
+//	}
+}
+
+void
+pcmsetvpp(int slot, int vpp)
+{
+	USED(slot, vpp);
+}
+
+/*
+ * boot environment in eeprom
+ */
+
+enum {
+	EEpromHdr=	8,	/* bytes */
+	Envitemsize=	64,
+	Ekeysize=	48,
+};
+
+static I2Cdev eedev;
+static struct {
+	uchar	buf[Envitemsize];
+	int	size;
+} bootenv;
+
+static int
+eepromitem(uchar *buf, int lim, ulong *off)
+{
+	int l;
+	uchar b;
+
+	if(i2crecv(&eedev, &b, 1, (*off)++) != 1)
+		return -1;
+	l = b;
+	if(l & 0x80){
+		if(i2crecv(&eedev, &b, 1, (*off)++) != 1)
+			return -1;
+		l = ((l & 0x7F)<<8) | b;
+	}
+	if(buf == nil)
+		return l;
+	if(l > lim)
+		l = lim;
+	return i2crecv(&eedev, buf, l, *off);
+}
+
+void
+eepromscan(void)
+{
+	int n, l;
+	ulong off;
+	uchar buf[2];
+	char *p;
+	int mac;
+
+	eedev.addr = 0x56;
+	eedev.salen = 2;
+	i2csetup(1);
+	n = i2crecv(&eedev, buf, sizeof(buf), 0);
+	if(n <= 0){
+		iprint("eepromscan: %d\n", n);
+		return;
+	}
+	if(buf[0] != 0xEF || buf[1] != 0xBE){
+		iprint("eeprom invalid\n");
+		return;
+	}
+	bootenv.size = 0;
+	for(off = EEpromHdr; off < 16384;){
+		l = eepromitem(bootenv.buf, sizeof(bootenv.buf), &off);	/* key */
+		if(l <= 0)
+			break;
+		off += l;
+		if(memcmp(bootenv.buf, "MACAD", 5) == 0){	/* only look for MAC addresses now */
+			mac = bootenv.buf[5] - '0';
+			if(mac >= 0 && mac < Maxmac){
+				l = eepromitem(bootenv.buf, sizeof(bootenv.buf), &off);
+				if(l < 0)
+					break;
+				if(l == Eaddrlen)
+					memmove(macaddrs[mac], bootenv.buf, Eaddrlen);
+				off += l+2;
+				continue;
+			}
+		}
+		l = eepromitem(nil, 0, &off);	/* skip value */
+		if(l < 0)
+			break;
+		off += l+2;	/* 2 byte crc */
+	}
+}
--- /dev/null
+++ b/os/cerf250/cerf
@@ -1,0 +1,166 @@
+dev
+	root
+	cons archcerf
+	env
+	mnt
+	pipe
+	prog
+#	rtc
+	pcf8563	i2c
+	srv
+	dup
+	ssl
+	cap
+#	sign
+
+#	draw	screen
+#	pointer
+	uart
+	ip	bootp ip ipv6 ipaux iproute arp netlog ptclbsum iprouter plan9 nullmedium pktmedium netaux
+	flash
+#	ftl
+#	pcmcia	cis
+#	ata
+	ether netif netaux
+
+#	cerf
+#	kprof
+	i2c	i2c
+
+ip
+	il
+	tcp
+	udp
+#	rudp
+#	igmp
+	ipifc
+	icmp
+	icmp6
+#	ipmux
+
+link	
+	flashcfi16
+	ether91c111	# ethermii
+	ethermedium
+
+lib
+	interp
+#	tk
+#	draw
+#	memlayer
+#	memdraw
+	keyring
+	math
+	sec
+	mp
+	kern
+
+mod
+	math
+	sys
+#	draw
+#	tk
+	keyring
+	crypt
+	ipints
+
+
+port
+	alarm
+	alloc
+	allocb
+	chan
+	dev
+	dial
+	dis
+	discall
+	exception
+	exportfs
+	inferno
+	latin1
+	nocache
+	nodynld
+	parse
+	pgrp
+	print
+	proc
+	qio
+	qlock
+	random
+	sysfile
+	taslock
+	xalloc
+
+code
+	int main_pool_pcnt = 50;
+	int heap_pool_pcnt = 50;
+	int image_pool_pcnt = 0;
+	int cflag = 0;	/* for JIT */
+
+	int consoleprint = 1;
+	char debug_keys = 1;
+	int panicreset = 0;
+	void screeninit(void){}
+
+init
+	cerfinit
+
+root
+	/chan	/
+	/dev	/
+	/env	/
+	/fd	/
+	/net	/
+	/net.alt	/
+	/nvfs /
+	/prog	/
+	/root	/
+	/nvfs	/
+	/osinit.dis
+	/tmp	/
+	/dis
+	/dis/lib
+	/dis/disk
+
+# dos file system
+	/dis/dossrv.dis
+	/dis/lib/arg.dis
+	/dis/lib/styx.dis
+	/dis/lib/string.dis
+	/dis/lib/daytime.dis
+
+	/dis/disk/format.dis
+
+# For development work:
+	/dis/sh.dis	/dis/tiny/sh.dis
+	/dis/ls.dis
+	/dis/cat.dis
+	/dis/bind.dis
+	/dis/mount.dis
+	/dis/pwd.dis
+	/dis/echo.dis
+	/dis/cd.dis
+	/dis/xd.dis
+	/dis/cp.dis
+	/dis/mkdir.dis
+	/dis/rm.dis
+	/dis/p.dis
+	/dis/ps.dis
+	/dis/lib/readdir.dis
+	/dis/lib/workdir.dis
+	/dis/lib/daytime.dis
+	/dis/lib/auth.dis
+	/dis/lib/ssl.dis
+	/dis/lib/bufio.dis
+	/dis/lib/string.dis
+#	/dis/pcmcia.dis	/usr/forsyth/pcmcia.dis
+
+	/n/remote
+	/n/local
+	/n/client
+	/n/rdbg
+	/n/dump
+	/n/disk
+	/n/kfs
+# Authentication
+	/nvfs/default	/usr/inferno/keyring/default
--- /dev/null
+++ b/os/cerf250/dat.h
@@ -1,0 +1,135 @@
+typedef struct Conf	Conf;
+typedef struct Dma	Dma;
+typedef struct FPU	FPU;
+typedef struct FPenv	FPenv;
+typedef struct Label	Label;
+typedef struct Lock	Lock;
+typedef struct Mach	Mach;
+typedef struct Ureg	Ureg;
+typedef struct ISAConf	ISAConf;
+typedef struct PCMmap	PCMmap;
+typedef struct PCMslot	PCMslot;
+
+typedef ulong Instr;
+
+struct Conf
+{
+	ulong	nmach;			/* processors */
+	ulong	nproc;			/* processes */
+	ulong	npage0;			/* total physical pages of memory */
+	ulong	npage1;			/* total physical pages of memory */
+	ulong	topofmem;		/* highest physical address + 1 */
+	ulong	npage;			/* total physical pages of memory */
+	ulong	base0;			/* base of bank 0 */
+	ulong	base1;			/* base of bank 1 */
+	ulong	ialloc;			/* max interrupt time allocation in bytes */
+
+	int		useminicache;		/* use mini cache: screen.c/lcd.c */
+	int		textwrite;			/* writeable text segment, for debug */
+	int		portrait;	/* display orientation */
+};
+
+#define NISAOPT 8
+struct ISAConf {
+	char	type[KNAMELEN];
+	ulong	port;
+	ulong	irq;
+	int	itype;
+	ulong	dma;
+	ulong	mem;
+	ulong	size;
+	ulong	freq;
+
+	int	nopt;
+	char	*opt[NISAOPT];
+};
+
+/*
+ * FPenv.status
+ */
+enum
+{
+	FPINIT,
+	FPACTIVE,
+	FPINACTIVE,
+};
+
+struct	FPenv
+{
+	ulong	status;
+	ulong	control;
+	ushort	fpistate;	/* emulated fp */
+	ulong	regs[8][3];	/* emulated fp */	
+};
+
+/*
+ * This structure must agree with fpsave and fprestore asm routines
+ */
+struct	FPU
+{
+	FPenv	env;
+};
+
+struct Label
+{
+	ulong	sp;
+	ulong	pc;
+};
+
+struct Lock
+{
+	ulong	key;
+	ulong	sr;
+	ulong	pc;
+	int	pri;
+};
+
+#include "../port/portdat.h"
+
+/*
+ *  machine dependent definitions not used by ../port/portdat.h
+ */
+struct Mach
+{
+	/* OFFSETS OF THE FOLLOWING KNOWN BY l.s */
+	ulong	splpc;		/* pc of last caller to splhi */
+
+	/* ordering from here on irrelevant */
+
+	int	machno;		/* physical id of processor */
+	ulong	ticks;			/* of the clock since boot time */
+	Proc	*proc;			/* current process on this processor */
+	Label	sched;			/* scheduler wakeup */
+	Lock	alarmlock;		/* access to alarm list */
+	void	*alarm;			/* alarms bound to this clock */
+	ulong	cpuhz;
+
+	/* stacks for exceptions */
+	ulong	fiqstack[4];
+	ulong	irqstack[4];
+	ulong	abtstack[4];
+	ulong	undstack[4];
+
+	int	stack[1];
+};
+
+#define	MACHP(n)	(n == 0 ? (Mach*)(MACHADDR) : (Mach*)0)
+
+extern Mach *m;
+extern Proc *up;
+
+typedef struct MemBank {
+	uint	pbase;
+	uint	plimit;
+	uint	vbase;
+	uint	vlimit;
+} MemBank;
+
+/*
+ * Layout at virtual address 0.
+ */
+typedef struct Vectorpage {
+	void	(*vectors[8])(void);
+	uint	vtable[8];
+} Vectorpage;
+extern Vectorpage *page0;
--- /dev/null
+++ b/os/cerf250/devpcf8563.c
@@ -1,0 +1,371 @@
+/*
+ *	Philips PCF8563 real-time clock on I⁲C (and compatibles)
+ *
+ *	currently this can't coexist with ../pxa/devrtc.c
+ */
+
+#include	"u.h"
+#include	"../port/lib.h"
+#include	"mem.h"
+#include	"dat.h"
+#include	"fns.h"
+#include	"../port/error.h"
+
+#include	"io.h"
+
+typedef struct Rtc	Rtc;
+typedef struct Rtcreg	Rtcreg;
+
+struct Rtc
+{
+	int	sec;
+	int	min;
+	int	hour;
+	int	wday;
+	int	mday;
+	int	mon;
+	int	year;
+};
+
+struct Rtcreg
+{
+	uchar	csr1;
+	uchar	csr2;
+	uchar	sec;	/* 00-59 and VL */
+	uchar	min;		/* 00-59 */
+	uchar	hour;	/* 00-23 */
+	uchar	mday;	/* 01-31 */
+	uchar	wday;	/* 0=Sun */
+	uchar	mon;	/* 1-12 and 1900 bit */
+	uchar	year;
+	uchar	amin;	/* minute alarm */
+	uchar	ahour;
+	uchar	aday;
+	uchar	awday;
+};
+
+enum{
+	Qdir = 0,
+	Qrtc,
+
+	Rtclen=	0x0C+1,		/* bytes read and written to timekeeper */
+	VL=	0x80,	/* reliable clock data no longer guaranteed */
+};
+
+static QLock	rtclock;		/* mutex on nvram operations */
+static I2Cdev	rtdev;
+
+static Dirtab rtcdir[]={
+	".",		{Qdir, 0, QTDIR},	0,	DMDIR|0555,
+	"rtc",		{Qrtc, 0},	0,	0664,
+};
+
+static ulong	rtc2sec(Rtc*);
+static void	sec2rtc(ulong, Rtc*);
+static void	setrtc(Rtc*);
+
+static void
+rtcreset(void)
+{
+	rtdev.addr = 0x51;
+	rtdev.salen = 1;
+	i2csetup(1);
+}
+
+static Chan*
+rtcattach(char *spec)
+{
+	return devattach('r', spec);
+}
+
+static Walkqid*
+rtcwalk(Chan *c, Chan *nc, char **name, int nname)
+{
+	return devwalk(c, nc, name, nname, rtcdir, nelem(rtcdir), devgen);
+}
+
+static int	 
+rtcstat(Chan *c, uchar *dp, int n)
+{
+	return devstat(c, dp, n, rtcdir, nelem(rtcdir), devgen);
+}
+
+static Chan*
+rtcopen(Chan *c, int omode)
+{
+	omode = openmode(omode);
+	switch((ulong)c->qid.path){
+	case Qrtc:
+		if(strcmp(up->env->user, eve)!=0 && omode!=OREAD)
+			error(Eperm);
+		break;
+	}
+	return devopen(c, omode, rtcdir, nelem(rtcdir), devgen);
+}
+
+static void	 
+rtcclose(Chan*)
+{
+}
+
+static long	 
+rtcread(Chan *c, void *buf, long n, vlong offset)
+{
+	ulong t, ot;
+
+	if(c->qid.type & QTDIR)
+		return devdirread(c, buf, n, rtcdir, nelem(rtcdir), devgen);
+
+	switch((ulong)c->qid.path){
+	case Qrtc:
+		qlock(&rtclock);
+		t = rtctime();
+		do{
+			ot = t;
+			t = rtctime();	/* make sure there's no skew */
+		}while(t != ot);
+		qunlock(&rtclock);
+		return readnum(offset, buf, n, t, 12);
+	}
+	error(Egreg);
+	return -1;		/* never reached */
+}
+
+static long	 
+rtcwrite(Chan *c, void *buf, long n, vlong off)
+{
+	Rtc rtc;
+	ulong secs;
+	char *cp, sbuf[32];
+	ulong offset = off;
+
+	switch((ulong)c->qid.path){
+	case Qrtc:
+		if(offset!=0 || n >= sizeof(sbuf)-1)
+			error(Ebadarg);
+		memmove(sbuf, buf, n);
+		sbuf[n] = '\0';
+		/*
+		 *  read the time
+		 */
+		cp = sbuf;
+		while(*cp){
+			if(*cp>='0' && *cp<='9')
+				break;
+			cp++;
+		}
+		secs = strtoul(cp, 0, 0);
+		/*
+		 *  convert to bcd
+		 */
+		sec2rtc(secs, &rtc);
+		/*
+		 * write it
+		 */
+		setrtc(&rtc);
+		return n;
+	}
+	error(Egreg);
+	return -1;		/* never reached */
+}
+
+Dev pcf8563devtab = {
+	'r',
+	"pcf8563",
+
+	rtcreset,
+	devinit,
+	devshutdown,
+	rtcattach,
+	rtcwalk,
+	rtcstat,
+	rtcopen,
+	devcreate,
+	rtcclose,
+	rtcread,
+	devbread,
+	rtcwrite,
+	devbwrite,
+	devremove,
+	devwstat,
+};
+
+static int
+getbcd(int bcd)
+{
+	return (bcd&0x0f) + 10 * (bcd>>4);
+}
+
+static int
+putbcd(int val)
+{
+	return (val % 10) | (((val/10) % 10) << 4);
+}
+
+long	 
+rtctime(void)
+{
+	Rtc rtc;
+	Rtcreg d;
+
+	if(waserror()){
+		iprint("rtc: err %s\n", up->env->errstr);
+		return 0;
+	}
+	if(i2crecv(&rtdev, &d, Rtclen, 0) != Rtclen)
+		return 0;
+	poperror();
+	rtc.sec = getbcd(d.sec & 0x7F);
+	rtc.min = getbcd(d.min & 0x7F);
+	rtc.hour = getbcd(d.hour & 0x3F);
+	rtc.mday = getbcd(d.mday & 0x3F);
+	rtc.mon = getbcd(d.mon & 0x1f);
+	rtc.year = getbcd(d.year);
+	if(rtc.mon < 1 || rtc.mon > 12)
+		return 0;
+	if(d.mon & (1<<7))
+		rtc.year += 1900;
+	else
+		rtc.year += 2000;
+	return rtc2sec(&rtc);
+}
+
+static void
+setrtc(Rtc *rtc)
+{
+	Rtcreg d;
+
+	memset(&d, 0, sizeof(d));
+	d.year = putbcd(rtc->year % 100);
+	d.mon = putbcd(rtc->mon);
+	if(rtc->year <  2000)
+		d.mon |= 1<<7;
+	d.wday = rtc->wday+1;
+	d.mday = putbcd(rtc->mday);
+	d.hour = putbcd(rtc->hour);
+	d.min = putbcd(rtc->min);
+	d.sec = putbcd(rtc->sec);
+	i2csend(&rtdev, &d, Rtclen, 0);
+}
+
+#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
+ */
+static ulong
+rtc2sec(Rtc *rtc)
+{
+	ulong secs;
+	int i;
+	int *d2m;
+
+	secs = 0;
+
+	/*
+	 *  seconds per year
+	 */
+	for(i = 1970; i < rtc->year; i++){
+		d2m = yrsize(i);
+		secs += d2m[0] * SEC2DAY;
+	}
+
+	/*
+	 *  seconds per month
+	 */
+	d2m = yrsize(rtc->year);
+	for(i = 1; i < rtc->mon; i++)
+		secs += d2m[i] * SEC2DAY;
+
+	secs += (rtc->mday-1) * SEC2DAY;
+	secs += rtc->hour * SEC2HOUR;
+	secs += rtc->min * SEC2MIN;
+	secs += rtc->sec;
+
+	return secs;
+}
+
+/*
+ *  compute rtc from seconds since Jan 1 1970
+ */
+static void
+sec2rtc(ulong secs, Rtc *rtc)
+{
+	int d;
+	long hms, day;
+	int *d2m;
+
+	/*
+	 * break initial number into days
+	 */
+	hms = secs % SEC2DAY;
+	day = secs / SEC2DAY;
+	if(hms < 0) {
+		hms += SEC2DAY;
+		day -= 1;
+	}
+
+	/*
+	 * day is the day number.
+	 * generate day of the week.
+	 * The addend is 4 mod 7 (1/1/1970 was Thursday)
+	 */
+
+	rtc->wday = (day + 7340036L) % 7;
+
+	/*
+	 * generate hours:minutes:seconds
+	 */
+	rtc->sec = hms % 60;
+	d = hms / 60;
+	rtc->min = d % 60;
+	d /= 60;
+	rtc->hour = d;
+
+	/*
+	 * year number
+	 */
+	if(day >= 0)
+		for(d = 1970; day >= *yrsize(d); d++)
+			day -= *yrsize(d);
+	else
+		for (d = 1970; day < 0; d--)
+			day += *yrsize(d-1);
+	rtc->year = d;
+
+	/*
+	 * generate month
+	 */
+	d2m = yrsize(rtc->year);
+	for(d = 1; day >= d2m[d]; d++)
+		day -= d2m[d];
+	rtc->mday = day + 1;
+	rtc->mon = d;
+}
--- /dev/null
+++ b/os/cerf250/ether91c111.c
@@ -1,0 +1,1056 @@
+/*
+ * SMsC 91c111 ethernet controller
+ * Copyright © 2001,2004 Vita Nuova Holdings Limited.  All rights reserved.
+ *
+ * TO DO:
+ *	- use ethermii
+ *	- use DMA where available
+ */
+
+#include "u.h"
+#include "../port/lib.h"
+#include "mem.h"
+#include "dat.h"
+#include "fns.h"
+#include "io.h"
+#include "../port/netif.h"
+
+#include "etherif.h"
+
+/*
+ * chip definitions
+ */
+
+typedef struct Ctlr Ctlr;
+
+enum {
+	SMSC91C11x,
+	SMSC91C110,
+	SMSC91C111,
+	SMSC91C96,
+};
+
+struct Ctlr {
+	Lock;
+	uchar	*base;
+	int	type;
+	int	rev;
+	int	hasmii;
+	int	phyad;
+	int	bank;	/* currently selected bank */
+	Block*	waiting;	/* waiting for space in FIFO */
+
+	ulong	collisions;
+	ulong	toolongs;
+	ulong	tooshorts;
+	ulong	aligns;
+	ulong	txerrors;
+	int	oddworks;
+	int	bus32bit;
+};
+
+#define MKREG(bank, off)		((bank << 8) | (off))
+
+enum {
+	/* Bank 0 */
+	Tcr=	MKREG(0, 0),	/* transmit control */
+	  TcrSwfdup=	1<<15,	/* switched full duplex */
+	  TcrEphLoop=	1<<13,	/* internal loopback */
+	  TcrStpSqet=	1<<12,	/* stop transmission on SQET error */
+	  TcrFduplx=	1<<11,	/* enable full duplex */
+	  TcrMonCsn=	1<<10,	/* monitor collision (0 for MII operation) */
+	  TcrNoCRC=	1<<8,	/* don't add CRC */
+	  TcrPadEn=	1<<7,	/* pad short frames */
+	  TcrForceCol=	1<<2,	/* force collision */
+	  TcrLoop=	1<<1,	/* PHY loopback */
+	  TcrTxena=	1<<0,	/* enable transmitter */
+	Eph=	MKREG(0, 2),	/* there are more bits but we don't use them */
+	  EphLinkOk=	1<<14,
+	  EphCtrRol=	1<<12,	/* counter roll over; clear by reading Ecr */
+	Rcr=	MKREG(0, 4),	/* receive control */
+	  RcrSoftRst=	1<<15,
+	  RcrFiltCar=	1<<14,
+	  RcrAbortEnb=	1<<13,
+	  RcrStripCRC=	1<<9,
+	  RcrRxEn=	1<<8,
+	  RcrAlmul=	1<<2,	/* ~=0, accept all multicast frames (=0, match multicast table) */
+	  RcrPrms=	1<<1,	/* promiscuous mode */
+	  RcrRxAbort=	1<<0,	/* set if receive frame longer than 2k bytes */
+	Ecr=	MKREG(0, 6),	/* counter */
+	  EcrExcDeferred=	0xF<<12,	/* excessively deferred Tx */
+	  EcrDeferred=	0xF<<8,	/* deferred Tx */
+	  EcrMultCol=	0xF<<4,	/* multiple collisions */
+	  EcrCollision=	0xF<<0,	/* single collision */
+	Mir=	MKREG(0, 8),	/* memory information */
+	Mcr=	MKREG(0, 0xA),	/* memory config (91cxx) */
+	Rpcr=	Mcr,	/* receive/phy control (91c111) */
+
+	/* Bank 1 */
+	Config=	MKREG(1, 0),
+	  CfgMiiSelect=	1<<15,	/* 91c110 */
+	  CfgEphPowerEn=	CfgMiiSelect,	/* =1, powered (after reset MMU); =0, low power mode (91c111) */
+	  CfgNoWait=	1<<12,	/* don't request additional wait states */
+	  CfgSetSqlch=	1<<9,	/* 91cxx */
+	  CfgGpcntrl=	1<<9,	/* general purpose output (CNTRL), perhaps power-enable (91c111) */
+	  CfgAuiSelect=	1<<8,	/* 91cxx */
+	  CfgExtPhy=	1<<8,	/* enable external PHY/MII (91c111) */
+	  Cfg16Bit=	1<<7,	/* 91cxx */
+	BaseAddress=	MKREG(1, 2),
+	Iaddr0_1=	MKREG(1, 4),
+	Iaddr2_3=	MKREG(1, 6),
+	Iaddr4_5=	MKREG(1, 8),
+	Gpr=		MKREG(1, 0xA),	/* general purpose reg (EEPROM interface) */
+	Control=	MKREG(1, 0xC),	/* control register */
+	  CtlRcvBad=	1<<14,	/* allow bad CRC packets through */
+	  CtlAutoRelease=	1<<11,	/* transmit pages released automatically w/out interrupt */
+	  CtlLeEnable=	1<<7,	/* link error enable */
+	  CtlCrEnable=	1<<6,	/* counter roll over enable */
+	  CtlTeEnable=	1<<5,	/* transmit error enable */
+	  CtlEeSelect=	1<<2,	/* EEPROM select */
+	  CtlReload=	1<<1,	/* read EEPROM and update relevant registers */
+	  CtlStore=	1<<0,	/* store relevant registers in EEPROM */
+
+	/* Bank 2 */
+	Mmucr=	MKREG(2, 0),	/* MMU command */
+	  McrAllocTx=	1<<5,	/* allocate space for outgoing packet */
+	  McrReset=	2<<5,	/* reset to initial state */
+	  McrReadFIFO=	3<<5,	/* remove frame from top of FIFO */
+	  McrRemove=	4<<5,	/* remove and release top of Rx FIFO */
+	  McrFreeTx=	5<<5,	/* release specific packet (eg, packets done Tx) */
+	  McrEnqueue=	6<<5,	/* enqueue packet number to Tx FIFO */
+	  McrResetTx=	7<<5,	/* reset both Tx FIFOs */
+	  McrBusy=	1<<0,
+	ArrPnr=	MKREG(2, 2),	/* Pnr (low byte), Arr (high byte) */
+	  ArrFailed=	1<<15,
+	FifoPorts=	MKREG(2, 4),
+	  FifoRxEmpty=	1<<15,
+	  FifoTxEmpty=	1<<7,
+	Pointer=	MKREG(2, 6),
+	  PtrRcv=	1<<15,
+	  PtrAutoIncr=	1<<14,
+	  PtrRead=	1<<13,
+	  PtrEtEn=	1<<12,
+	  PtrNotEmpty=	1<<11,
+	Data=	MKREG(2, 8),
+	Interrupt=	MKREG(2, 0xC),	/* status/ack (low byte), mask (high byte) */
+	  IntMii=	1<<7,	/* PHY/MII state change */
+	  IntErcv=	1<<6,	/* early receive interrupt (received > Ercv threshold) */
+	  IntEph=	1<<5,	/* ethernet protocol interrupt */
+	  IntRxOvrn=	1<<4,	/* overrun */
+	  IntAlloc=	1<<3,	/* allocation complete */
+	  IntTxEmpty=	1<<2,	/* TX FIFO now empty */
+	  IntTx=	1<<1,	/* transmit done */
+	  IntRcv=	1<<0,	/* packet received */
+	IntrMask=	MKREG(2, 0xD),
+	  IntrMaskShift=	8,	/* shift for Int... values to mask position in 16-bit register */
+	  IntrMaskField=	0xFF00,
+
+	/* Bank 3 */
+	Mt0_1=	MKREG(3, 0),	/* multicast table */
+	Mt2_3=	MKREG(3, 2),
+	Mt4_5=	MKREG(3, 4),
+	Mt6_7=	MKREG(3, 6),
+	Mgmt=	MKREG(3, 8),	/* management interface (MII) */
+	  MgmtMdo=	1<<0,	/* MDO pin */
+	  MgmtMdi=	1<<1,	/* MDI pin */
+	  MgmtMclk=	1<<2,	/* drive MDCLK */
+	  MgmtMdoEn=	1<<3,	/* MDO driven when high, tri-stated when low */
+	Revision=		MKREG(3, 0xA),
+	Ercv=	MKREG(3, 0xC),	/* early receive */
+
+	/* Bank 4 (91cxx only) */
+	EcsrEcor=	MKREG(4, 0),	/* status and option registers */
+
+	/* all banks */
+	BankSelect=	MKREG(0, 0xe),
+};
+
+enum {
+	/* receive frame status word (p 38) */
+	RsAlgnErr=	1<<15,
+	RsBroadcast=	1<<14,
+	RsBadCRC=	1<<13,
+	RsOddFrame=	1<<12,
+	RsTooLong=	1<<11,
+	RsTooShort=	1<<10,
+	RsMulticast=	1<<1,
+	RsError=	RsBadCRC | RsAlgnErr | RsTooLong | RsTooShort,
+
+	Framectlsize=	6,
+};
+
+static void miiw(Ctlr *ctlr, int regad, int val);
+static int miir(Ctlr *ctlr, int regad);
+
+/*
+ * architecture dependent section - collected here in case
+ * we want to port the driver
+ */
+
+#define PHYMIIADDR_91C110		3
+#define PHYMIIADDR_91C111		0
+
+#define llregr(ctlr, reg)	(*(ushort*)(ctlr->base + (reg)))
+#define llregr32(ctlr, reg)	(*(ulong*)(ctlr->base + (reg)))
+#define llregw(ctlr, reg, val)	(*(ushort*)(ctlr->base + (reg)) = (val))
+
+static void
+adinit(Ether *ether)
+{
+	Ctlr *ctlr;
+
+	ctlr = ether->ctlr;
+	// TODO: code to turn on device clocks
+	ctlr->base = (uchar*)mmuphysmap(PHYSCS1, 0x100000) + ether->port;
+iprint("adinit: %8.8lux -> %8.8lux mcs0=%8.8lux\n", (ulong)ctlr->base, PADDR(ctlr->base), MEMCFGREG->msc0);
+{ulong v; v = *(ulong*)ctlr->base; iprint("value=%8.8lux\n", v);}
+	ctlr->bus32bit = 1;
+}
+
+static void
+adsetfd(Ctlr *ctlr)
+{	
+	miiw(ctlr, 0x18, miir(ctlr, 0x18) | (1 << 5));
+}
+
+/*
+ * architecture independent section
+ */
+
+static ushort
+regr(Ctlr *ctlr, int reg)
+{
+	int bank;
+	ushort val;
+
+	bank = reg >> 8;
+	if(ctlr->bank != bank){
+		ctlr->bank = bank;
+		llregw(ctlr, BankSelect, bank);
+	}
+	val = llregr(ctlr, reg & 0xff);
+	return val;
+}
+
+static ulong
+regr32(Ctlr *ctlr, int reg)
+{
+	int bank;
+	ulong val;
+
+	bank = reg >> 8;
+	if(ctlr->bank != bank){
+		ctlr->bank = bank;
+		llregw(ctlr, BankSelect, bank);
+	}
+	val = llregr32(ctlr, reg & 0xff);
+	return val;
+}
+
+static void
+regw(Ctlr *ctlr, int reg, ushort val)
+{
+	int bank;
+
+	bank = reg >> 8;
+	if(ctlr->bank != bank){
+		ctlr->bank = bank;
+		llregw(ctlr, BankSelect, bank);
+	}
+	llregw(ctlr, reg & 0xff, val);
+}
+
+static void
+regwdatam(Ctlr *ctlr, ushort *data, int ns)
+{
+	int bank;
+	ushort *faddr;
+
+	bank = Data >> 8;
+	if(ctlr->bank != bank){
+		ctlr->bank = bank;
+		llregw(ctlr, BankSelect, bank);
+	}
+	faddr = (ushort*)(ctlr->base + (Data & 0xff));
+	while(ns-- > 0){
+		*faddr = *data;
+		data++;
+	}
+}
+
+static void
+regrdatam(Ctlr *ctlr, void *data, int nb)
+{
+	int bank;
+	ushort *f, *t;
+	int laps, ns;
+
+	bank = Data >> 8;
+	if(ctlr->bank != bank){
+		ctlr->bank = bank;
+		llregw(ctlr, BankSelect, bank);
+	}
+
+	if((ulong)data & 3)
+		iprint("bad buffer alignment\n");
+
+	t = data;
+	f = (ushort*)(ctlr->base + (Data & 0xff));
+	ns = nb >> 1;
+	laps = ns / 8;	
+	switch(ns & 7){	/* Duff's device */
+	do {
+		*t++ = *f;
+	case 7: *t++ = *f;
+	case 6: *t++ = *f;
+	case 5: *t++ = *f;
+	case 4: *t++ = *f;
+	case 3: *t++ = *f;
+	case 2: *t++ = *f;
+	case 1: *t++ = *f;
+	case 0:
+		;
+	} while(laps-- > 0);
+	}
+}
+
+static void
+regrdatam32(Ctlr *ctlr, void *data, int nb)
+{
+	int bank;
+	ulong *f, *t;
+	int laps, nw;
+
+	bank = Data >> 8;
+	if(ctlr->bank != bank){
+		ctlr->bank = bank;
+		llregw(ctlr, BankSelect, bank);
+	}
+
+	if((ulong)data & 3)
+		iprint("bad buffer alignment\n");
+
+	t = data;
+	f = (ulong*)(ctlr->base + (Data & 0xff));
+	nw = nb>>2;
+	laps = nw / 8;	
+	switch(nw & 7){	/* Duff's device */
+	do {
+		*t++ = *f;
+	case 7: *t++ = *f;
+	case 6: *t++ = *f;
+	case 5: *t++ = *f;
+	case 4: *t++ = *f;
+	case 3: *t++ = *f;
+	case 2: *t++ = *f;
+	case 1: *t++ = *f;
+	case 0:
+		;
+	} while(laps-- > 0);
+	}
+}
+
+static void
+regor(Ctlr *ctlr, int reg, ushort val)
+{
+	int bank;
+
+	bank = reg >> 8;
+	if(ctlr->bank != bank){
+		ctlr->bank = bank;
+		llregw(ctlr, BankSelect, bank);
+	}
+	reg &= 0xff;
+	llregw(ctlr, reg, llregr(ctlr, reg) | val);
+}
+
+static void
+regclear(Ctlr *ctlr, int reg, ushort val)
+{
+	int bank;
+
+	bank = reg >> 8;
+	if(ctlr->bank != bank){
+		ctlr->bank = bank;
+		llregw(ctlr, BankSelect, bank);
+	}
+	reg &= 0xff;
+	llregw(ctlr, reg, llregr(ctlr, reg) & ~val);
+}
+
+static long
+ifstat(Ether* ether, void* a, long n, ulong offset)
+{
+	Ctlr *ctlr;
+	char *p;
+	int len;
+
+	if(n == 0)
+		return 0;
+
+	ctlr = ether->ctlr;
+	p = smalloc(READSTR);
+	if(waserror()){
+		free(p);
+		nexterror();
+	}
+	len = snprint(p, READSTR, "Overflow: %ud\n", ether->overflows);
+	len += snprint(p+len, READSTR, "Soft Overflow: %ud\n", ether->soverflows);
+	len += snprint(p+len, READSTR, "Transmit Error: %lud\n", ctlr->txerrors);
+	len += snprint(p+len, READSTR-len, "CRC Error: %ud\n", ether->crcs);
+	len += snprint(p+len, READSTR-len, "Collision: %lud\n", ctlr->collisions);
+	len += snprint(p+len, READSTR-len, "Align: %lud\n", ctlr->aligns);
+	len += snprint(p+len, READSTR-len, "Too Long: %lud\n", ctlr->toolongs);
+	snprint(p+len, READSTR-len, "Too Short: %lud\n", ctlr->tooshorts);
+
+	n = readstr(offset, a, n, p);
+	poperror();
+	free(p);
+
+	return n;
+}
+
+static void
+promiscuous(void* arg, int on)
+{
+	Ether *ether;
+	Ctlr *ctlr;
+	int r;
+
+	ether = arg;
+	ctlr = ether->ctlr;
+	ilock(ctlr);
+	r = regr(ctlr, Rcr);
+	if(on)
+		r |= RcrPrms;
+	else
+		r &= ~RcrPrms;
+	regw(ctlr, Rcr, r);
+	iunlock(ctlr);
+}
+
+static void
+attach(Ether *ether)
+{
+	Ctlr *ctlr;
+
+	ctlr = ether->ctlr;
+
+	/*
+	 * enable transmit and receive
+	 */
+	regw(ctlr, Interrupt, (IntMii | IntTx | IntRcv | IntRxOvrn)<<IntrMaskShift);
+	regor(ctlr, Rcr, RcrRxEn);
+	regor(ctlr, Tcr, TcrTxena);
+}
+
+static void
+pointtotxpacket(Ctlr *ctlr, int pkt, int read)		 // read=PtrRead in failure case
+{
+	ushort junk;
+
+	pkt &= 0x3F;
+	regw(ctlr, ArrPnr, pkt);
+	while(regr(ctlr, Pointer) & PtrNotEmpty)
+		;
+	regw(ctlr, Pointer, read | PtrAutoIncr);
+	junk = llregr(ctlr, BankSelect);			/* possible wait state */
+	USED(junk);
+}
+
+static void
+pointtorxpacket(Ctlr *ctlr, int offset)
+{
+	ushort junk;
+
+	regw(ctlr, Pointer, PtrRcv | PtrAutoIncr | PtrRead | offset);
+	junk = llregr(ctlr, BankSelect);			/* possible wait state */
+	USED(junk);
+}
+
+static void
+mmucommand(Ctlr *ctlr, ushort cmd)
+{
+	while(regr(ctlr, Mmucr) & McrBusy)	// should signal free resource
+		;
+	regw(ctlr, Mmucr, cmd);	  // do the work
+}
+
+static void
+txloadpacket(Ether *ether)
+{
+	Ctlr *ctlr;
+	int pkt;
+	Block *b;
+	ushort lastw;
+	int lenb, lenw;
+	int odd;
+
+	ctlr = ether->ctlr;
+	b = ctlr->waiting;
+	ctlr->waiting = nil;
+	if(b == nil)
+		return;	/* shouldn't happen */
+	pkt = regr(ctlr, ArrPnr);		/* get packet number presumably just allocated */
+	if(pkt & 0xC0){
+		print("smc91c111: invalid packet number\n");
+		freeb(b);
+		return;
+	}
+	
+	pointtotxpacket(ctlr, pkt, 0);
+
+	lenb = BLEN(b);
+	odd = lenb & 1;
+	lenw = lenb >> 1;
+	regw(ctlr, Data, 0);		// status word padding
+	regw(ctlr, Data, (lenw << 1) + Framectlsize);
+	regwdatam(ctlr, (ushort*)b->rp, lenw);	// put packet into 91cxxx memory
+	lastw = 0x1000;
+	if(odd){
+		lastw |= 0x2000;	/* odd byte flag in control byte */
+		lastw |= b->rp[lenb - 1];
+	}
+	regw(ctlr, Data, lastw);
+	mmucommand(ctlr, McrEnqueue);  // chip now owns buff
+	freeb(b);
+	regw(ctlr, Interrupt, (regr(ctlr, Interrupt) & IntrMaskField) | (IntTxEmpty << IntrMaskShift));
+}
+
+static void
+txstart(Ether *ether)
+{
+	Ctlr *ctlr;
+	int n;
+
+	ctlr = ether->ctlr;
+	if(ctlr->waiting != nil)	/* allocate pending; must wait for that */
+		return;
+	for(;;){
+		if((ctlr->waiting = qget(ether->oq)) == nil)
+			break;
+		/* ctlr->waiting is a new block to transmit: allocate space */
+		n = (BLEN(ctlr->waiting) & ~1) + Framectlsize;	/* Framectlsize includes odd byte, if any */
+		mmucommand(ctlr, McrAllocTx | (n >> 8));
+		if(regr(ctlr, ArrPnr) & ArrFailed){
+			regw(ctlr, Interrupt, (regr(ctlr, Interrupt) & IntrMaskField) | (IntAlloc << IntrMaskShift));
+			break;
+		}
+		txloadpacket(ether);
+	}
+}
+
+static void
+transmit(Ether *ether)
+{
+	Ctlr *ctlr;
+
+	ctlr = ether->ctlr;
+	ilock(ctlr);
+	txstart(ether);
+	iunlock(ctlr);
+}
+
+static void
+process(Ether *ether)
+{
+	Ctlr *ctlr;
+	int status, intrreg, intr, mask, fifo;
+	int pkt;
+	ulong data;
+	int count, len, alen;
+	Block *b;
+
+	ctlr = ether->ctlr;
+
+Recheck:
+	intrreg = regr(ctlr, Interrupt);
+	regw(ctlr, Interrupt, 0);
+	mask = intrreg >> IntrMaskShift;
+	intr = intrreg & mask;
+	if(intr == 0){
+		regw(ctlr, Interrupt, mask<<IntrMaskShift);
+		return;
+	}
+
+	if(intr & IntAlloc){
+		regw(ctlr, Interrupt, IntAlloc);
+		intr &= ~IntAlloc;
+		if(ctlr->waiting)
+			txloadpacket(ether);
+		mask &= ~IntAlloc;
+		mask |= IntTxEmpty;
+	}
+
+	if(intr & IntRxOvrn){
+		regw(ctlr, Interrupt, IntRxOvrn);
+		intr &= ~IntRxOvrn;
+		ether->overflows++;
+	}
+	if(intr & IntRcv){
+		fifo = regr(ctlr, FifoPorts);
+		while((fifo & FifoRxEmpty) == 0){
+			ether->inpackets++;
+			pointtorxpacket(ctlr, 0);
+			data = regr32(ctlr, Data);
+			status = data & 0xFFFF;
+			count = (data>>16) & 0x7FE;
+			if(status & RsBadCRC)
+				ether->crcs++;
+			else if(status & RsAlgnErr)
+				ether->frames++;
+			else if(status & (RsTooLong | RsTooShort))
+				ether->buffs++;
+			else {
+				len = count - Framectlsize;
+				if(len < 0)
+					panic("smc:interrupt");
+				if(ctlr->type == SMSC91C111 && !ctlr->oddworks)
+					len++;
+				else if(status & RsOddFrame)
+					len++;
+				alen = (len + 1) & ~1;
+				if(ctlr->bus32bit)
+					alen = (alen + 3) & ~3;
+				b = iallocb(alen);
+				if(b){
+					(ctlr->bus32bit? regrdatam32: regrdatam)(ctlr, b->wp, alen);
+					b->wp += len;
+					etheriq(ether, b, 1);
+				}else
+					ether->soverflows++;
+			}
+			mmucommand(ctlr, McrRemove);
+			fifo = regr(ctlr, FifoPorts);
+		}
+		intr &= ~IntRcv;
+	}
+	if(intr & IntTx){
+		/* some kind of failure  */
+		fifo = regr(ctlr, FifoPorts);
+		ctlr->txerrors++;
+		if((fifo & FifoTxEmpty) == 0){
+			pkt = fifo & 0x3f;
+			pointtotxpacket(ctlr, pkt, PtrRead);
+			mmucommand(ctlr, McrFreeTx);
+		}					
+		regw(ctlr, Interrupt, IntTx);
+		intr &= ~IntTx;
+	}
+	if(intr & IntTxEmpty){
+		/* acknowledge and disable TX_EMPTY */
+		regw(ctlr, Interrupt, IntTxEmpty);
+		mask &= ~IntTxEmpty;
+		intr &= ~IntTxEmpty;
+	}
+	if(intr)
+		panic("91c111: unhandled interrupts %.4ux\n", intr);
+	regw(ctlr, Interrupt, mask<<IntrMaskShift);
+	txstart(ether);
+	goto Recheck;
+}
+
+static void
+interrupt(Ureg*, void *arg)
+{
+	Ether *ether;
+	Ctlr *ctlr;
+	int bank;
+
+	ether = arg;
+	ctlr = ether->ctlr;
+	ilock(ctlr);
+	bank = llregr(ctlr, BankSelect);
+	process(ether);
+	llregw(ctlr, BankSelect, bank);
+	ctlr->bank = bank;
+	iunlock(ctlr);
+}
+
+#define MIIDELAY 5
+
+static int
+miimdi(Ctlr *ctlr, int n)
+{
+	int data, i;
+
+	/*
+	 * Read n bits from the MII Management Register.
+	 */
+	data = 0;
+	for(i = n - 1; i >= 0; i--){
+		if(regr(ctlr, Mgmt) & MgmtMdi)
+			data |= (1 << i);
+		microdelay(MIIDELAY);
+		regw(ctlr, Mgmt, MgmtMclk);
+		microdelay(MIIDELAY);
+		regw(ctlr, Mgmt, 0);
+		microdelay(MIIDELAY);
+	}
+
+	return data;
+}
+
+static void
+miimdo(Ctlr *ctlr, int bits, int n)
+{
+	int i, mdo;
+
+	/*
+	 * Write n bits to the MII Management Register.
+	 */
+	for(i = n - 1; i >= 0; i--){
+		if(bits & (1 << i))
+			mdo = MgmtMdoEn | MgmtMdo;
+		else
+			mdo = MgmtMdoEn;
+		regw(ctlr, Mgmt, mdo);
+		microdelay(MIIDELAY);
+		regw(ctlr, Mgmt, mdo | MgmtMclk);
+		microdelay(MIIDELAY);
+		regw(ctlr, Mgmt, mdo);
+		microdelay(MIIDELAY);
+	}
+}
+
+static int
+miir(Ctlr *ctlr, int regad)
+{
+	int data;
+
+	/*
+	 * Preamble;
+	 * ST+OP+PHYAD+REGAD;
+	 * TA + 16 data bits.
+	 */
+	miimdo(ctlr, 0xFFFFFFFF, 32);
+	miimdo(ctlr, 0x1800 | (ctlr->phyad << 5) | regad, 14);
+	data = miimdi(ctlr, 18);
+	regw(ctlr, Mgmt, 0);
+	microdelay(MIIDELAY);
+
+	return data & 0xFFFF;
+}
+
+static void
+miiw(Ctlr* ctlr, int regad, int data)
+{
+	/*
+	 * Preamble;
+	 * ST+OP+PHYAD+REGAD+TA + 16 data bits;
+	 * Z.
+	 */
+	miimdo(ctlr, 0xFFFFFFFF, 32);
+	data &= 0xFFFF;
+	data |= (0x05 << (5 + 5 + 2 + 16)) | (ctlr->phyad << (5 + 2 +16)) | (regad << (2 + 16)) | (0x02 << 16);
+	miimdo(ctlr, data, 32);
+	regw(ctlr, Mgmt, 0);
+	microdelay(MIIDELAY);
+}
+
+static void
+miinegostatus(Ctlr *ctlr, int *speed, int *full)
+{
+	int reg;
+
+	switch(ctlr->type){
+	case SMSC91C110:
+		reg = miir(ctlr, 25);
+		if((reg & (1<<4)) == 0)
+			break;
+		*speed = (reg & (1 << 5))? 100: 10;
+		*full = (reg & (1 << 6)) != 0;
+		return;
+	case SMSC91C111:
+		reg = miir(ctlr, 18);
+		*speed = (reg & (1 << 7))? 100: 10;
+		*full = (reg & (1 << 6)) != 0;
+		return;
+	}
+	*speed = 0;
+	*full = 0;
+}
+
+void
+dump111phyregs(Ctlr *ctlr)
+{
+	int x;
+	for(x = 0; x < 6; x++)
+		iprint("reg%d 0x%.4ux\n", x, miir(ctlr, x));
+	for(x = 16; x <= 20; x++)
+		iprint("reg%d 0x%.4ux\n", x, miir(ctlr, x));
+}
+
+static void
+miireset(Ctlr *ctlr)
+{
+	miiw(ctlr, 0, 0x8000);
+	while(miir(ctlr, 0) & 0x8000)
+		;
+	delay(100);
+}
+
+static int
+miinegotiate(Ctlr *ctlr, int modes)
+{
+	ulong now, timeout;
+	int success;
+	int reg4;
+
+	// Taken from TRM - don't argue
+
+	miireset(ctlr);
+	miiw(ctlr, 0, 0);
+	regw(ctlr, Rpcr, 0x800 | (4 << 2));
+	delay(50);
+	reg4 = miir(ctlr, 4);
+	reg4 &= ~(0x1f << 5);
+	reg4 |= ((modes & 0x1f) << 5);
+	miiw(ctlr, 4, reg4);
+	miir(ctlr, 18);	// clear the status output so we can tell which bits got set...
+	miiw(ctlr, 0, 0x3300);
+	now = timer_start();
+	timeout = ms2tmr(3000);
+	success = 0;
+	while(!success && (timer_start() - now) < timeout){
+		ushort status;
+		status = miir(ctlr, 1);
+		if(status & (1 << 5))
+			success = 1;
+		if(status & (1 << 4)){
+			success = 0;
+			miiw(ctlr, 0, 0x3300);
+		}
+	}
+	return success;
+}
+
+static int
+ether91c111reset(Ether* ether)
+{
+	int i;
+	char *p;
+	uchar ea[Eaddrlen];
+	Ctlr *ctlr;
+	ushort rev;
+
+	if(ether->ctlr == nil){
+		ether->ctlr = malloc(sizeof(Ctlr));
+		if(ether->ctlr == nil)
+			return -1;
+	}
+
+	ctlr = ether->ctlr;
+	ctlr->bank = -1;
+
+	/*
+	 * do architecture dependent intialisation
+	 */
+	adinit(ether);
+
+	regw(ctlr, Rcr, RcrSoftRst);
+	regw(ctlr, Config, CfgEphPowerEn|CfgNoWait|Cfg16Bit);
+	delay(4*20);			// rkw -  (750us for eeprom alone)4x just to be ultra conservative 10 for linux.
+	regw(ctlr, Rcr, 0);		// rkw - now remove reset and let the sig's fly.
+	regw(ctlr, Tcr, TcrSwfdup);
+
+	regw(ctlr, Control, CtlAutoRelease | CtlTeEnable);
+	mmucommand(ctlr, McrReset);  // rkw - reset the mmu
+	delay(5);
+
+	/*
+	 * Identify the chip by reading...
+	 * 1) the bank select register - the top byte will be 0x33
+	 * 2) changing the bank to see if it reads back appropriately
+	 * 3) check revision register for code 9
+	 */
+	if((llregr(ctlr, BankSelect) >> 8) != 0x33){
+	gopanic:
+		free(ctlr);
+		return -1;
+	}
+
+	llregw(ctlr, BankSelect, 0xfffb);
+	if((llregr(ctlr, BankSelect) & 0xff07) != 0x3303)
+		goto gopanic;
+
+	rev = regr(ctlr, Revision);
+	
+	if((rev >> 8) != 0x33)
+		goto gopanic;
+
+	rev &= 0xff;
+	switch(rev){
+	case 0x40:
+		/* 91c96 */
+		ctlr->type = SMSC91C96;
+		ctlr->oddworks = 1;
+		break;
+	case 0x90:
+		ctlr->type = SMSC91C11x;
+		ctlr->hasmii = 1;
+		/* 91c110/9c111 */
+		/* 91c111s are supposed to be revision one, but it's not the case */
+		// See man page 112, revision history.  rev not incremented till 08/01
+		ctlr->oddworks = 0;  // dont know if it works at this point
+		break;
+	case 0x91:
+		ctlr->type = SMSC91C111;
+		ctlr->hasmii = 1;
+		ctlr->oddworks = 1;
+		break;
+	default:
+		iprint("ether91c111: chip 0x%.1ux detected\n", rev);
+		goto gopanic;
+	}
+
+	memset(ea, 0, sizeof(ea));
+	if(memcmp(ether->ea, ea, Eaddrlen) == 0)
+		panic("ethernet address not set");
+#ifdef YYY
+		if((rev == 0x90) || (rev == 0x91))	// assuming no eeprom setup for these
+			panic("ethernet address not set in environment");
+		for(i = 0; i < Eaddrlen; i += 2){
+			ushort w;
+			w = regr(ctlr, Iaddr0_1 + i);
+			iprint("0x%.4ux\n", w);
+			ea[i] = w;
+			ea[i + 1] = w >> 8;
+		}
+	}else{
+		for(i = 0; i < 6; i++){
+			char buf[3];
+			buf[0] = p[i * 2];
+			buf[1] = p[i * 2 + 1];
+			buf[2] = 0;
+			ea[i] = strtol(buf, 0, 16);
+		}
+	}
+	memmove(ether->ea, ea, Eaddrlen);
+#endif
+
+	/*
+	 * set the local address
+	 */
+	for(i=0; i<Eaddrlen; i+=2)
+		regw(ctlr, Iaddr0_1 + i, ether->ea[i] | (ether->ea[i+1] << 8));
+
+	/*
+	 * initialise some registers
+	 */
+	regw(ctlr, Rcr, RcrRxEn | RcrAbortEnb | RcrStripCRC);	   // strip can now be used again
+
+	if(rev == 0x90){		   // its either a 110 or a 111 rev A at this point
+		int reg2, reg3;
+		/*
+		 * how to tell the difference?
+		 * the standard MII dev
+		 */
+		ctlr->phyad = PHYMIIADDR_91C110;
+		ctlr->type = SMSC91C110;
+		ctlr->oddworks = 1;		// assume a 110
+		reg2 = miir(ctlr, 2);	// check if a 111 RevA
+		if(reg2 <= 0){
+			ctlr->phyad = PHYMIIADDR_91C111;
+			ctlr->type = SMSC91C111;
+			reg2 = miir(ctlr, 2);
+			ctlr->oddworks = 0;	   // RevA
+		}
+		if(reg2 > 0){
+			reg3 = miir(ctlr, 3);
+			iprint("reg2 0x%.4ux reg3 0x%.4ux\n", reg2, reg3);
+		}
+		else
+			panic("ether91c111: can't find phy on MII\n");
+	}
+
+	if(ctlr->type == SMSC91C110)
+		regor(ctlr, Config, CfgMiiSelect);
+	if(rev == 0x40){
+		regor(ctlr, Config, CfgSetSqlch);
+		regclear(ctlr, Config, CfgAuiSelect);
+		regor(ctlr, Config, Cfg16Bit);
+	}
+
+	if(ctlr->type == SMSC91C111){
+		int modes;
+		char *ethermodes;
+
+		miiw(ctlr, 0, 0x1000);				/* clear MII_DIS and enable AUTO_NEG */
+//		miiw(ctlr, 16, miir(ctlr, 16) | 0x8000);
+		// Rpcr set in INIT.
+		ethermodes=nil;	/* was getconf("ethermodes"); */
+		if(ethermodes == nil)
+			modes = 0xf;
+		else {
+			char *s;
+			char *args[10];
+			int nargs;
+			int x;
+
+			s = strdup(ethermodes);
+			if(s == nil)
+				panic("ether91c111reset: no memory for ethermodes");
+			nargs = getfields(s, args, nelem(args), 1, ",");
+			modes = 0;
+			for(x = 0; x < nargs; x++){
+				if(cistrcmp(args[x], "10HD") == 0)
+					modes |= 1;
+				else if(cistrcmp(args[x], "10FD") == 0)
+					modes |= 2;
+				else if(cistrcmp(args[x], "100HD") == 0)
+					modes |= 4;
+				else if(cistrcmp(args[x], "100FD") == 0)
+					modes |= 8;
+			}
+			free(s);
+		}
+		if(!miinegotiate(ctlr, modes)){
+			iprint("ether91c111: negotiation timed out\n");
+			return -1;
+		}
+	}
+
+	if(ctlr->hasmii)
+		miinegostatus(ctlr, &ether->mbps, &ether->fullduplex);
+	else if(regr(ctlr, Eph) & EphLinkOk){
+		ether->mbps = 10;
+		ether->fullduplex = 0;
+	}
+	else {
+		ether->mbps = 0;
+		ether->fullduplex = 0;
+	}
+
+	if(ether->fullduplex && ctlr->type == SMSC91C110){
+		// application note 79
+		regor(ctlr, Tcr, TcrFduplx);
+		// application note 85
+		adsetfd(ctlr);
+	}
+
+	iprint("91c111 enabled: %dmbps %s\n", ether->mbps, ether->fullduplex ? "FDX" : "HDX");
+	if(rev == 0x40){
+		iprint("EcsrEcor 0x%.4ux\n", regr(ctlr, EcsrEcor));
+		regor(ctlr, EcsrEcor, 1);
+	}
+
+	/*
+	 * Linkage to the generic ethernet driver.
+	 */
+	ether->attach = attach;
+	ether->transmit = transmit;
+	ether->interrupt = interrupt;
+	ether->ifstat = ifstat;
+
+	ether->arg = ether;
+	ether->promiscuous = promiscuous;
+
+	return 0;
+}
+
+void
+ether91c111link(void)
+{
+	addethercard("91c111",  ether91c111reset);
+}
--- /dev/null
+++ b/os/cerf250/fns.h
@@ -1,0 +1,168 @@
+#include "../port/portfns.h"
+
+ulong	aifinit(uchar *aifarr);
+int	archaudiopower(int);
+void	archaudiomute(int);
+void	archaudioamp(int);
+int	archaudiospeed(int, int);
+void	archconfinit(void);
+void	archconsole(void);
+int	archflash12v(int);
+long	archkprofmicrosecondspertick(void);
+void	archkprofenable(int);
+void	archlcdenable(int);
+void	archpowerdown(void);
+void	archpowerup(void);
+void	archreboot(void);
+void	archreset(void);
+vlong	archrdtsc(void);
+ulong archrdtsc32(void);
+void	archuartpower(int, int);
+void	blankscreen(int);
+ulong	call_apcs(ulong addr, int nargs, ...);
+ulong	call_apcs0(ulong addr);
+ulong	call_apcs1(ulong addr, ulong a1);
+ulong	call_apcs2(ulong addr, ulong a1, ulong a2);
+ulong	call_apcs3(ulong addr, ulong a1, ulong a2, ulong a3);
+void	cisread(int slotno, void (*f)(int, uchar *));
+void	clockcheck(void);
+void	clockinit(void);
+void	clockpoll(void);
+#define	coherence()		/* nothing to do for cache coherence for uniprocessor */
+void	cursorhide(void);
+void	cursorunhide(void);
+void	dcflush(void*, ulong);
+void	dcflushall(void);
+void	dcinval(void);
+int	dmaidle(Dma*);
+Dma*	dmasetup(int device, void(*)(void*,ulong), void*, ulong);
+int	dmastart(Dma*, void*, void*, int);
+int	dmacontinue(Dma*, void*, int);
+void	dmastop(Dma*);
+int	dmaerror(Dma*);
+void	dmafree(Dma*);
+void	dmareset(void);
+void	dmawait(Dma*);
+void dumplongs(char *, ulong *, int);
+void	dumpregs(Ureg* ureg);
+void	dumpstack(void);
+int	fpiarm(Ureg*);
+void	fpinit(void);
+ulong	getcallerpc(void*);
+ulong	getcclkcfg(void);
+ulong	getcpsr(void);
+ulong	getcpuid(void);
+ulong	getspsr(void);
+void	gotopc(ulong);
+
+void	icflush(void*, ulong);
+void	icflushall(void);
+void	idle(void);
+void	idlehands(void);
+int	inb(ulong);
+int	ins(ulong);
+ulong	inl(ulong);
+void	outb(ulong, int);
+void	outs(ulong, int);
+void	outl(ulong, ulong);
+void inss(ulong, void*, int);
+void outss(ulong, void*, int);
+void	insb(ulong, void*, int);
+void	outsb(ulong, void*, int);
+void	intrdisable(int, int, void (*)(Ureg*, void*), void*, char*);
+void	intrenable(int, int, void (*)(Ureg*, void*), void*, char*);
+void	iofree(int);
+#define	iofree(x)
+void	ioinit(void);
+int	iounused(int, int);
+int	ioalloc(int, int, int, char*);
+#define	ioalloc(a,b,c,d) 0
+int	iprint(char*, ...);
+void	installprof(void (*)(Ureg *, int));
+int	isvalid_va(void*);
+void	kbdinit(void);
+void	lcd_setbacklight(int);
+void	lcd_sethz(int);
+void	ledset(int);
+void	lights(ulong);
+void	links(void);
+void*	minicached(void*);
+void	minidcflush(void);
+void	mmuenable(ulong);
+ulong	mmugetctl(void);
+ulong	mmugetdac(void);
+ulong	mmugetfar(void);
+ulong	mmugetfsr(void);
+void	mmuinit(void);
+void*	mmuphysmap(ulong, ulong);
+void	mmuputctl(ulong);
+void	mmuputdac(ulong);
+void	mmuputfsr(ulong);
+void	mmuputttb(ulong);
+void	mmureset(void);
+void	mouseinit(void);
+void*	pa2va(ulong);
+void	pcmcisread(PCMslot*);
+int	pcmcistuple(int, int, int, void*, int);
+PCMmap*	pcmmap(int, ulong, int, int);
+void	pcmunmap(int, PCMmap*);
+int	pcmpin(int slot, int type);
+void	pcmpower(int slotno, int on);
+int	pcmpowered(int);
+void	pcmreset(int);
+void	pcmsetvcc(int, int);
+void	pcmsetvpp(int, int);
+int	pcmspecial(char *idstr, ISAConf *isa);
+void	pcmspecialclose(int slotno);
+void	pcmintrenable(int, void (*)(Ureg*, void*), void*);
+void	powerenable(void (*)(int));
+void	powerdisable(void (*)(int));
+void	powerdown(void);
+void	powerinit(void);
+void	powersuspend(void);
+#define procsave(p)
+#define procrestore(p)
+void	putcclkcfg(ulong);
+long	rtctime(void);
+void	screeninit(void);
+void	(*screenputs)(char*, int);
+int	segflush(void*, ulong);
+void	setpanic(void);
+void	setr13(int, void*);
+int	splfhi(void);
+int	splflo(void);
+void	_suspendcode(void);
+void	tlbinvalidate(void);
+void	tlbinvalidateaddr(void*);
+void	trapinit(void);
+void	trapstacks(void);
+void	trapspecial(int (*)(Ureg *, uint));
+void	uartdebuginit(void);
+void	uartinstall(void);
+int	uartprint(char*, ...);
+void	uartspecial(int, int, Queue**, Queue**, int (*)(Queue*, int));
+ulong	va2pa(void*);
+void	vectors(void);
+void	vtable(void);
+#define	waserror()	(up->nerrlab++, setlabel(&up->errlab[up->nerrlab-1]))
+int	wasbusy(int);
+
+#define KADDR(p)	((void *) p)
+#define PADDR(v)	va2pa((void*)(v))
+
+ulong	timer_start(void);
+ulong	timer_ticks(ulong);
+int 	timer_devwait(ulong *adr, ulong mask, ulong val, int ost);
+void 	timer_setwatchdog(int ost);
+void 	timer_delay(int ost);
+ulong	ms2tmr(int ms);
+int	tmr2ms(ulong t);
+void	delay(int ms);
+ulong	us2tmr(int us);
+int	tmr2us(ulong t);
+void 	microdelay(int us);
+
+#define	archuartclock(p,rate)	14745600
+
+/* debugging */
+void	xdelay(int);
--- /dev/null
+++ b/os/cerf250/io.h
@@ -1,0 +1,1 @@
+#include "../pxa/pxaio.h"
--- /dev/null
+++ b/os/cerf250/main.c
@@ -1,0 +1,351 @@
+#include "u.h"
+#include "../port/lib.h"
+#include "mem.h"
+#include "dat.h"
+#include "fns.h"
+#include "../port/error.h"
+#include "io.h"
+#include "version.h"
+
+#define	MAXCONF		32
+
+Mach *m = (Mach*)MACHADDR;
+Proc *up = 0;
+Vectorpage	*page0 = (Vectorpage*)KZERO;	/* doubly-mapped to AIVECADDR */
+Conf conf;
+
+extern ulong kerndate;
+extern int cflag;
+extern int main_pool_pcnt;
+extern int heap_pool_pcnt;
+extern int image_pool_pcnt;
+ulong cpuidlecount;
+
+char *confname[MAXCONF];
+char *confval[MAXCONF];
+int nconf;
+
+void addconf(char *, char *);
+void	eepromscan(void);
+char*	getconf(char*);
+
+void
+doc(char *m)
+{
+	USED(m);
+	print("%s...\n", m);
+}
+
+void
+idoc(char *m)
+{
+	uartputs(m, strlen(m)); //xdelay(1);
+}
+
+static void
+poolsizeinit(void)
+{
+	ulong nb;
+
+	nb = conf.npage*BY2PG;
+	poolsize(mainmem, (nb*main_pool_pcnt)/100, 0);
+	poolsize(heapmem, (nb*heap_pool_pcnt)/100, 0);
+	poolsize(imagmem, (nb*image_pool_pcnt)/100, 1);
+}
+
+static void
+serialconsole(void)
+{
+	char *p;
+	int port, baud;
+
+	p = getconf("console");
+	if(p == nil)
+		p = "0";
+	if(p != nil){
+		port = strtol(p, nil, 0);
+		baud = 38400;
+		p = getconf("baud");
+		if(p != nil){
+			baud = strtol(p, nil, 0);
+			if(baud < 38400)
+				baud = 38400;
+		}
+		uartspecial(port, baud, &kbdq, &printq, kbdcr2nl);
+	}
+}
+
+static char *hello = "Inferno\n";
+
+void
+main(void)
+{
+	memset(edata, 0, end-edata);		/* clear the BSS */
+	memset(m, 0, sizeof(Mach));	/* clear the mach struct */
+	conf.nmach = 1;
+	archreset();
+	idoc(hello);
+if(0){
+	putcclkcfg(0);	/* leave turbo mode */
+	COREREG->cccr = (COREREG->cccr & ~(7<<7)) | (4<<7);	/* turbo mode multiplier=2 */
+	putcclkcfg(1);	/* enter turbo mode */
+}
+	quotefmtinstall();
+	idoc("confinit...\n");
+	confinit();
+	idoc("xinit...\n");
+	xinit();
+	idoc("mmuinit...\n");
+	mmuinit();
+	poolsizeinit();
+	poolinit();
+	idoc("trapinit...\n");
+	trapinit();
+//	dmareset();
+	idoc("printinit...\n");
+	printinit();
+	idoc("uartinstall...\n");
+	uartinstall();
+	eepromscan();
+	doc("clockinit");
+	clockinit();
+	doc("screeninit");
+//	screeninit();
+	doc("procinit");
+	procinit();
+//	cpuidprint();
+	doc("links");
+	links();
+	doc("chandevreset");
+	chandevreset();
+
+	eve = strdup("inferno");
+
+	serialconsole();
+	kbdinit();
+
+	print("%ld MHz id %8.8lux\n", (m->cpuhz+500000)/1000000, getcpuid());
+	print("\nInferno %s\n", VERSION);
+	print("Vita Nuova\n");
+	print("conf %s (%lud) jit %d\n\n",conffile, kerndate, cflag);
+print("pmcr=%8.8lux pwer=%8.8lux prer=%8.8lux pfer=%8.8lux pedr=%8.8lux pcfr=%8.8lux rcsr=%8.8lux\n",
+	PMGRREG->pmcr, PMGRREG->pwer, PMGRREG->prer, PMGRREG->pfer, PMGRREG->pedr, PMGRREG->pcfr,
+	PMGRREG->rcsr);
+print("cccr=%8.8lux cken=%8.8lux oscc=%8.8lux\n", COREREG->cccr, COREREG->cken, COREREG->oscc);
+print("msc0=%8.8lux mcs1=%8.8lux mcs2=%8.8lux\n", MEMCFGREG->msc0, MEMCFGREG->msc1, MEMCFGREG->msc2);
+print("clkcfg=%8.8lux\n", getcclkcfg());
+
+	userinit();
+	schedinit();
+}
+
+void
+reboot(void)
+{
+	exit(0);
+}
+
+void
+halt(void)
+{
+	spllo();
+	print("cpu halted\n");
+	for(;;){
+		/* nothing to do */
+	}
+}
+
+Conf	conf;
+
+void
+addconf(char *name, char *val)
+{
+	if(nconf >= MAXCONF)
+		return;
+	confname[nconf] = name;
+	confval[nconf] = val;
+	nconf++;
+}
+
+char*
+getconf(char *name)
+{
+	int i;
+
+	for(i = 0; i < nconf; i++)
+		if(cistrcmp(confname[i], name) == 0)
+			return confval[i];
+	return 0;
+}
+
+void
+confinit(void)
+{
+	ulong base;
+
+	archconfinit();
+
+	base = PGROUND((ulong)end);
+	conf.base0 = base;
+
+	conf.base1 = 0;
+	conf.npage1 = 0;
+
+	conf.npage0 = (conf.topofmem - base)/BY2PG;
+
+	conf.npage = conf.npage0 + conf.npage1;
+	conf.ialloc = (((conf.npage*(main_pool_pcnt))/100)/2)*BY2PG;
+
+	conf.nproc = 100 + ((conf.npage*BY2PG)/MB)*5;
+	conf.nmach = 1;
+
+}
+
+void
+init0(void)
+{
+	Osenv *o;
+	char buf[2*KNAMELEN];
+
+	up->nerrlab = 0;
+
+	spllo();
+
+	if(waserror())
+		panic("init0 %r");
+	/*
+	 * These are o.k. because rootinit is null.
+	 * Then early kproc's will have a root and dot.
+	 */
+	o = up->env;
+	o->pgrp->slash = namec("#/", Atodir, 0, 0);
+	cnameclose(o->pgrp->slash->name);
+	o->pgrp->slash->name = newcname("/");
+	o->pgrp->dot = cclone(o->pgrp->slash);
+
+	chandevinit();
+
+	if(!waserror()){
+		ksetenv("cputype", "arm", 0);
+		snprint(buf, sizeof(buf), "arm %s", conffile);
+		ksetenv("terminal", buf, 0);
+		poperror();
+	}
+
+	poperror();
+
+	disinit("/osinit.dis");
+}
+
+void
+userinit(void)
+{
+	Proc *p;
+	Osenv *o;
+
+	p = newproc();
+	o = p->env;
+
+	o->fgrp = newfgrp(nil);
+	o->pgrp = newpgrp();
+	o->egrp = newegrp();
+	kstrdup(&o->user, eve);
+
+	strcpy(p->text, "interp");
+
+	p->fpstate = FPINIT;
+
+	/*
+	 * Kernel Stack
+	 *
+	 * N.B. The -12 for the stack pointer is important.
+	 *	4 bytes for gotolabel's return PC
+	 */
+	p->sched.pc = (ulong)init0;
+	p->sched.sp = (ulong)p->kstack+KSTACK-8;
+
+	ready(p);
+}
+
+void
+exit(int inpanic)
+{
+	up = 0;
+
+	/* Shutdown running devices */
+	chandevshutdown();
+
+	if(inpanic && 0){
+		print("Hit the reset button\n");
+		for(;;)
+			clockpoll();
+	}
+	archreboot();
+}
+
+static void
+linkproc(void)
+{
+	spllo();
+	if (waserror())
+		print("error() underflow: %r\n");
+	else
+		(*up->kpfun)(up->arg);
+	pexit("end proc", 1);
+}
+
+void
+kprocchild(Proc *p, void (*func)(void*), void *arg)
+{
+	p->sched.pc = (ulong)linkproc;
+	p->sched.sp = (ulong)p->kstack+KSTACK-8;
+
+	p->kpfun = func;
+	p->arg = arg;
+}
+
+void
+idlehands(void)
+{
+	cpuidlecount++;
+	INTRREG->iccr = 1;	/* only unmasked interrupts will stop idle mode */
+	idle();
+}
+
+/* stubs */
+void
+setfsr(ulong)
+{
+}
+
+ulong
+getfsr()
+{
+	return 0;
+}
+
+void
+setfcr(ulong)
+{
+}
+
+ulong
+getfcr()
+{
+	return 0;
+}
+
+void
+fpinit(void)
+{
+}
+
+void
+FPsave(void*)
+{
+}
+
+void
+FPrestore(void*)
+{
+}
--- /dev/null
+++ b/os/cerf250/mem.h
@@ -1,0 +1,174 @@
+/*
+ * Memory and machine-specific definitions.  Used in C and assembler.
+ */
+
+/*
+ * Sizes
+ */
+#define	BI2BY		8			/* bits per byte */
+#define BI2WD		32			/* bits per word */
+#define	BY2WD		4			/* bytes per word */
+#define	BY2V		8			/* bytes per double word */
+#define	BY2PG		4096			/* bytes per page */
+#define	WD2PG		(BY2PG/BY2WD)		/* words per page */
+#define	PGSHIFT		12			/* log(BY2PG) */
+#define ROUND(s, sz)	(((s)+(sz-1))&~(sz-1))
+#define PGROUND(s)	ROUND(s, BY2PG)
+#define BIT(n)		(1<<n)
+#define BITS(a,b)	((1<<(b+1))-(1<<a))
+
+#define	MAXMACH		1			/* max # cpus system can run */
+
+/*
+ * Time
+ */
+#define	HZ		(100)			/* clock frequency */
+#define	MS2HZ		(1000/HZ)		/* millisec per clock tick */
+#define	TK2SEC(t)	((t)/HZ)		/* ticks to seconds */
+#define	MS2TK(t)	((t)/MS2HZ)		/* milliseconds to ticks */
+
+/*
+ * More accurate time
+ */
+#define CLOCKFREQ	3686400
+#define MS2TMR(t)	((ulong)(((uvlong)(t)*CLOCKFREQ)/1000))
+#define US2TMR(t)	((ulong)(((uvlong)(t)*CLOCKFREQ)/1000000))
+
+/*
+ *  Address spaces
+ *	nearly everything maps 1-1 with physical addresses
+ *	0 to 1Mb is not mapped
+ *	cache strategy varies as needed (see mmu.c)
+ */
+
+#define KZERO		0xA0000000
+#define MACHADDR	(KZERO+0x00001000)
+#define KTTB		(KZERO+0x00004000)
+/*#define KTZERO	(KZERO+0x00008010)*/
+#define KTZERO	(KZERO+0x200020)	/* temporary */
+#define KSTACK	8192			/* Size of kernel stack */
+#define FLASHMEM	0x50000000	/* map flash at phys 0 to otherwise unused virtual space */
+#define FLUSHMEM	0xE0000000	/* virtual address reserved for cache flushing */
+#define DCFADDR	FLUSHMEM	/* cached and buffered for cache writeback */
+#define MCFADDR	(FLUSHMEM+(1<<20))	/* cached and unbuffered for minicache writeback */
+#define UCDRAMZERO	0xA8000000	/* base of memory doubly-mapped as uncached */
+#define AIVECADDR	0xFFFF0000	/* alternative interrupt vector address (other is 0) */
+
+/*
+ * Physical addresses
+ */
+#define PHYSFLASH0	0x00000000	/* flash (chip select 0) */
+#define PHYSCS1		0x04000000	/* static chip select 1 */
+#define PHYSCS2		0x08000000	/* static chip select 2 */
+#define PHYSCS3		0x0C000000	/* static chip select 3 */
+#define PHYSCS4		0x10000000	/* static chip select 4 */
+#define PHYSCS5		0x18000000	/* static chip select 5 */
+#define PHYSPCMCIA0	0x20000000	/* PCMCIA socket 0 space */
+#define PHYSPCMCIA1	0x30000000	/* PCMCIA socket 1 space */
+#define PCMCIASIZE		0x10000000	/* they're both huge */
+#define PHYSREGS		0x40000000	/* memory mapped registers */
+#define PHYSDMA		0x40000000	/* DMA controller */
+#define PHYSUART0		0x40100000	/* full function UART*/
+#define PHYSUARTBT	0x40200000	/* bluetooth UART */
+#define PHYSI2C		0x40301680
+#define PHYSI2S		0x40400000	/* serial audio */
+#define PHYSAC97		0x40500000	/* AC97/PCM/modem */
+#define PHYSUDC		0x40600000	/* USB client */
+#define PHYSUART1		0x40700000	/* standard UART */
+#define PHYSICP		0x40800000
+#define PHYSRTC		0x40900000	/* real-time clock */
+#define PHYSOSTMR		0x40A00000	/* timers */
+#define PHYSPWM0		0x40B00000	/* pulse width modulator */
+#define PHYSPWM1		0x40C00000
+#define PHYSINTR		0x40D00000	/* interrupt controller */
+#define PHYSGPIO		0x40E00000	/* pins */
+#define PHYSPOWER		0x40F00000	/* power management registers */
+#define PHYSSSP		0x41000000
+#define PHYSMMC		0x41100000
+#define PHYSCORE		0x41300000	/* clocks manager */
+#define PHYSNETSSP	0x41400000	/* network SSP */
+#define PHYSUART2		0x41600000	/* hardware UART */
+#define PHYSLCD		0x44000000	/* LCD controller */
+#define PHYSMEMCFG	0x48000000	/* memory configuration */
+#define PHYSMEM0		0xA0000000
+
+/*
+ * Memory Interface Control Registers
+ */
+#define 	MDCNFG	(PHYSMEMCFG)	/* memory controller configuration */
+#define	MDREFR	(PHYSMEMCFG+4)
+#define	MSC0	(MDREFR+4)
+#define	MSC1	(MSC0+4)
+#define	MSC2	(MSC1+4)
+
+#define	MSCx(RRR, RDN, RDF, RBW, RT)	((((RRR)&0x7)<<13)|(((RDN)&0x1F)<<8)|(((RDF)&0x1F)<<3)|(((RBW)&1)<<2)|((RT)&3))
+
+#define	CACHELINELOG	5
+#define	CACHELINESZ	(1<<CACHELINELOG)
+#define	CACHESIZE	(32*1024)		/* I & D caches are the same size */
+#define	MINICACHESIZE	(2*1024)
+
+/*
+ * PSR
+ */
+#define PsrMusr		0x10 	/* mode */
+#define PsrMfiq		0x11 
+#define PsrMirq		0x12
+#define PsrMsvc		0x13
+#define PsrMabt		0x17
+#define PsrMund		0x1B
+#define PsrMsys		0x1F
+#define PsrMask		0x1F
+
+#define PsrDfiq		0x00000040	/* disable FIQ interrupts */
+#define PsrDirq		0x00000080	/* disable IRQ interrupts */
+
+#define PsrV		0x10000000	/* overflow */
+#define PsrC		0x20000000	/* carry/borrow/extend */
+#define PsrZ		0x40000000	/* zero */
+#define PsrN		0x80000000	/* negative/less than */
+
+/*
+ * Internal MMU coprocessor registers
+ */
+#define CpCPUID	0		/* R: opcode_2 is 0*/
+#define CpCacheID	0		/* R: opcode_2 is 1 */
+#define CpControl	1		/* R/W: control (opcode_2 is 0) */
+#define CpAuxctl	1		/* R/W: auxiliary control (opcode_2 is 1) */
+#define CpTTB		2		/* R/W: translation table base */
+#define CpDAC		3		/* R/W: domain access control */
+#define CpFSR		5		/* R/W: fault status */
+#define CpFAR		6		/* R/W: fault address */
+#define CpCacheCtl	7		/* W: */
+#define CpTLBops	8		/* W: TLB operations */
+#define CpCacheLk	9		/* W: cache lock down */
+#define CpPID		13		/* R/W: Process ID Virtual Mapping */
+#define CpDebug	14		/* R/W: debug registers */
+#define CpAccess	15		/* R/W: Coprocessor Access */
+
+/*
+ * Coprocessors
+ */
+#define CpMMU		15
+#define CpPWR		14
+
+/*
+ * CpControl bits
+ */
+#define CpCmmu	(1<<0)	/* M: MMU enable */
+#define CpCalign	(1<<1)	/* A: alignment fault enable */
+#define CpCDcache	(1<<2)	/* C: data cache on */
+#define CpCwpd	(15<<3)	/* W, P, D, must be one */
+#define CpCbe		(1<<7)	/* B: big-endian operation */
+#define CpCsystem	(1<<8)	/* S: system permission */
+#define CpCrom	(1<<9)	/* R: ROM permission */
+#define CpCbranch	(1<<11)	/* Z: branch target buffer enable */
+#define CpCIcache	(1<<12)	/* I: Instruction Cache on */
+#define CpCaltivec	(1<<13)	/* V: exception vector relocation */
+
+/*
+ * CpAux bits
+ */
+#define	CpWBdisable	(1<<1)	/* globally disable write buffer coalescing */
+#define	CpMDrwa	(1<<4)	/* mini data cache r/w allocate */
+#define	CpMDwt	(1<<5)	/* mini data cache write through */
--- /dev/null
+++ b/os/cerf250/mkfile
@@ -1,0 +1,102 @@
+<../../mkconfig
+TKSTYLE=std
+
+#Configurable parameters
+
+CONF=cerf				#default configuration
+CONFLIST=cerf
+
+SYSTARG=$OSTARG
+OBJTYPE=arm
+INSTALLDIR=$ROOT/Inferno/$OBJTYPE/bin	#path of directory where kernel is installed
+#end configurable parameters
+
+<$ROOT/mkfiles/mkfile-$SYSTARG-$OBJTYPE	#set vars based on target system
+
+<| $SHELLNAME ../port/mkdevlist $CONF	#sets $IP, $DEVS, $ETHERS, $VGAS, $PORT, $MISC, $LIBS, $OTHERS
+
+#KTZERO=0xA0008020
+KTZERO=0xA0020020
+
+OBJ=\
+	l.$O\
+	clock.$O\
+	fpi.$O\
+	fpiarm.$O\
+	fpimem.$O\
+	gpio.$O\
+	main.$O\
+	mmu.$O\
+	trap.$O\
+	$CONF.root.$O\
+	$IP\
+	$DEVS\
+	$ETHERS\
+	$LINKS\
+	$PORT\
+	$MISC\
+	$OTHERS\
+
+LIBNAMES=${LIBS:%=lib%.a}
+LIBDIRS=$LIBS
+
+HFILES=\
+	mem.h\
+	dat.h\
+	fns.h\
+	io.h\
+	../pxa/pxaio.h\
+	../pxa/fpi.h\
+
+CFLAGS=-wFV -I$ROOT/Inferno/$OBJTYPE/include -I$ROOT/include -I$ROOT/libinterp -I../pxa
+KERNDATE=`{$NDATE}
+
+default:V: i$CONF i$CONF.p9
+
+install:V: $INSTALLDIR/i$CONF
+
+i$CONF: $OBJ $CONF.c $CONF.root.h $LIBNAMES i$CONF.p9
+	$CC $CFLAGS '-DKERNDATE='$KERNDATE $CONF.c
+	$LD -s -o $target -T$KTZERO -R4 -l $OBJ $CONF.$O $LIBFILES
+
+i$CONF.p9: $OBJ $CONF.c $CONF.root.h $LIBNAMES
+	$CC $CFLAGS '-DKERNDATE='$KERNDATE $CONF.c
+	$LD -o $target -T$KTZERO -R4 -l $OBJ $CONF.$O $LIBFILES
+
+<../port/portmkfile
+
+%.$O:		../pxa/%.c
+		$CC $CFLAGS -I. ../pxa/$stem.c
+
+%.$O:		../pxa/%.s
+	$AS -I. -I../pxa ../pxa/$stem.s
+
+../init/$INIT.dis:	../init/$INIT.b
+		cd ../init; mk $INIT.dis
+
+clock.$O:	$ROOT/Inferno/$OBJTYPE/include/ureg.h
+devether.$O:	$ROOT/Inferno/$OBJTYPE/include/ureg.h
+devsapcm.$O:	$ROOT/Inferno/$OBJTYPE/include/ureg.h
+main.$O:	$ROOT/Inferno/$OBJTYPE/include/ureg.h
+trap.$O:	$ROOT/Inferno/$OBJTYPE/include/ureg.h
+
+devether.$O $ETHERS:	../pxa/etherif.h ../port/netif.h
+$IP devip.$O:		../ip/ip.h
+io.h:N:	../pxa/pxaio.h
+
+dummy:V:
+
+# to be moved to libinterp 
+bench.h:D: ../../module/bench.m
+	rm -f $target && limbo -a -I../../module ../../module/bench.m > $target
+benchmod.h:D:  ../../module/bench.m
+	rm -f $target && limbo -t Bench -I../../module ../../module/bench.m > $target
+devbench.$O: bench.h benchmod.h
+
+devaudio.$O:	devaudio.c
+	$CC $CFLAGS devaudio.c
+
+arch$CONF.$O:	../pxa/etherif.h
+
+devuart.$O:	../pxa/devuart.c uart.h
+	$CC $CFLAGS ../pxa/devuart.c
--- /dev/null
+++ b/os/cerf250/uart.h
@@ -1,0 +1,118 @@
+/*
+ * PXA250 specific code for its uart.
+ */
+
+#define	UR(p,r)	((ulong*)(p))[r]
+#define uartwr(u,r,v)	(UR(u->regs,r) = (v))
+#define uartwrreg(u,r,v)	(UR(u->regs,r)= (u)->sticky[r] | (v))
+#define uartrdreg(u,r)		UR(u->regs,r)
+
+extern void	uartsetup(ulong, void*, ulong, char*);
+extern void	uartclock(void);
+
+static void
+uartportpower(Uart *p, int on)
+{
+	/* TO DO: power control */
+	if(on)
+		p->sticky[Iena] |= Uue;
+	else
+		p->sticky[Iena] &= ~Uue;
+	uartwrreg(p, Iena, 0);
+}
+
+/*
+ *  handle an interrupt to a single uart
+ */
+static void
+uartintrx(Ureg*, void* arg)
+{
+	uartintr(arg);
+}
+
+/*
+ *  install the uarts (called by reset)
+ */
+void
+uartinstall(void)
+{
+	static int already;
+
+	if(already)
+		return;
+	already = 1;
+
+	/* first two ports are always there */
+	uartsetup(0, (void*)PHYSUART0, 0, "eia0");	/* full function */
+	intrenable(IRQ, IRQffuart, uartintrx, uart[0], "uart0");
+	uartsetup(2, (void*)PHYSUART2, 0, "eia2");	/* hardware uart */
+	intrenable(IRQ, IRQhwuart, uartintrx, uart[1], "uart2");
+	addclock0link(uartclock, 22);
+}
+
+/*
+ * If the UART's receiver can be connected to a DMA channel,
+ * this function does what is necessary to create the
+ * connection and returns the DMA channel number.
+ * If the UART's receiver cannot be connected to a DMA channel,
+ * a -1 is returned.
+ */
+char
+uartdmarcv(int dev)
+{
+ 
+	USED(dev);
+	return -1;
+}
+
+void
+uartdebuginit(void)
+{
+	ulong *p;
+
+	p = (ulong*)PHYSUART0;
+	p[Iena] = Uue;
+	p[Format] = Dra;
+	p[Dmsb] = 0;
+	p[Dlsb] = 24;
+	p[Format] = Bits8;
+}
+
+void
+uartputc(int c)
+{
+	ulong *p;
+
+	if(c == 0)
+		return;
+	p = (ulong*)PHYSUART0;
+	while((UR(p,Lstat) & Outready) == 0){
+		;
+	}
+	UR(p,Data) = c;
+	if(c == '\n')
+		while((UR(p,Lstat) & Outready) == 0){	/* let fifo drain */
+			;
+		}
+}
+
+void
+uartputs(char *data, int len)
+{
+	int s;
+
+//	if(!uartspcl && !redirectconsole)
+//		return;
+	s = splhi();
+	while(--len >= 0){
+		if(*data == '\n')
+			uartputc('\r');
+		uartputc(*data++);
+	}
+	splx(s);
+}
+
+void
+uartwait(void)
+{
+}
--- /dev/null
+++ b/os/cerf405/NOTICE
@@ -1,0 +1,3 @@
+Inferno® Copyright © 1996-1999 Lucent Technologies Inc.  All rights reserved.
+PowerPC support Copyright © 1995-1997,2002,2003 C H Forsyth (forsyth@terzarima.net).  All rights reserved.
+405EP Inferno PowerPC port Copyright © 2003 Vita Nuova Holdings Limited.  All rights reserved.
--- /dev/null
+++ b/os/cerf405/README
@@ -1,0 +1,45 @@
+Cerfcube 405EP
+
+This is the basis of a port to a range of IBM PowerPC 405 devices.
+At some point all the common code will move to ../ppc, as is done
+for SA1110, MPC8xx, and others.
+
+Currently untested:
+	- both EMAC0 and EMAC1 are initialised, but EMAC1 hasn't been tested (i need to get a cable made);
+	  the PHY for it is detected and initialised, so i think it should work
+	- pci has not been tested since i haven't yet got the mini-PCI card i need
+
+Will change:
+	- devuart.c/uart.h will be replaced by ../port/devuart.c and uart405.c eventually
+	- power control will be extended
+
+mk in this directory should produce a file `icerfhd', which is icerf parcelled as ppcboot/uboot expects it.
+put it where your tftp server can find it.
+
+on the machine to be used as file server, run inferno and start svc/net
+to serve Styx
+
+To boot Inferno:
+	1. set up a dhcp/tftp server for the EMAC0 MAC address, referring to the icerfhd file as the bootfile.
+	2. connect a b115200 l8 pn serial port to talk to the ppcboot/uboot bootstrap
+	3. reset the 405, and interrupt the automatic boot to get to the bootstrap's prompt
+	4. type
+		bootp
+	5. if dhcp is set correctly, that should load the Inferno boot image into memory at 0x300000
+	6. type
+		bootm
+	    to boot that image.
+
+To put Inferno kernel image (icerfhd) on NAND flash:
+	- the boot area is 0 to 0x100000 on NAND (perhaps a bit more, but 0x100000 is currently fine)
+	1. get to the bootstrap's prompt.
+	2. nand erase 0 0x100000
+	3. bootp		# loads the image to 0x300000 by tftp
+	4. nand write 0x300000 0 0x100000
+	5. a subsequent {nand; bootm} (as for auto boot) should run that kernel.
+
+To initialise a file system on the NAND flash:
+	1. set logfsformat=yes in the environment
+	2. boot inferno
+	3. choose remote file system or kernel file system when offered
+	4. the empty flash file system should be in /n/local
--- /dev/null
+++ b/os/cerf405/cerf
@@ -1,0 +1,142 @@
+# Intrinsyc Cerf Cube 405EP
+dev
+	root
+	cons
+	env
+	mnt
+	pipe
+	prog
+	rtc	iic
+	srv
+	dup
+	ssl
+	cap
+#	sign
+
+	ip	ip ipv6 ipaux iproute arp netlog ptclbsum iprouter plan9 nullmedium pktmedium netaux
+	ether netif netaux
+	uart
+	flash
+
+	logfs
+	i2c	iic
+	pci	pci inb
+
+
+ip
+	il
+	tcp
+	udp
+#	rudp
+#	igmp
+	ipifc
+	icmp
+	icmp6
+	ipmux
+
+lib
+	interp
+	keyring
+	sec
+	mp
+	math
+	kern
+	logfs
+	nandfs
+
+link
+	etheremac	ethermii
+	flashamd29f0x0
+	flashnand	nand
+	ethermedium
+
+mod
+	math
+	sys
+	keyring
+	crypt
+	ipints
+
+
+port
+	alarm
+	alloc
+	allocb
+	chan
+	dev
+	dial
+	dis
+	discall
+	exception
+	exportfs
+	inferno
+	latin1
+	nocache
+	nodynld
+	parse
+	pgrp
+	print
+	proc
+	qio
+	qlock
+	random
+	sysfile
+	taslock
+	xalloc
+
+code
+	int cflag = 0;
+	int consoleprint = 1;
+	int panicreset = 0;
+	int main_pool_pcnt = 50;
+	int heap_pool_pcnt = 50;
+	int image_pool_pcnt = 0;
+	void	screeninit(void){}
+
+init
+	cerf405
+
+root
+	/chan	/
+	/dev	/
+	/boot	/
+	/env	/
+	/fd	/
+	/net	/
+	/prog	/
+	/root	/
+	/nvfs	/
+	/osinit.dis
+	/tmp /
+
+# files used by osinit.dis during bootstrap
+	/boot/n	/
+	/boot/n/local	/
+	/boot/n/remote	/
+# authentication
+	/boot/nvfs/default	/usr/inferno/keyring/default
+	/boot/dis/lib/auth.dis	/dis/lib/auth.dis
+	/boot/dis/lib/ssl.dis	/dis/lib/ssl.dis
+# dhcp
+	/boot/dis/lib/dhcpclient.dis	/dis/lib/dhcpclient.dis
+	/boot/dis/lib/ip.dis	/dis/lib/ip.dis
+
+# and other files used to poke round during development
+	/boot/dis/cat.dis /dis/cat.dis
+	/boot/dis/echo.dis /dis/echo.dis
+	/boot/dis/lib/arg.dis /dis/lib/arg.dis
+
+	/boot/dis/sh.dis /dis/sh.dis
+	/boot/dis/lib/bufio.dis /dis/lib/bufio.dis
+	/boot/dis/lib/filepat.dis /dis/lib/filepat.dis
+	/boot/dis/lib/readdir.dis /dis/lib/readdir.dis
+	/boot/dis/lib/string.dis /dis/lib/string.dis
+
+	/boot/dis/cd.dis /dis/cd.dis
+	/boot/dis/bind.dis /dis/bind.dis
+	/boot/dis/dd.dis /dis/dd.dis
+	/boot/dis/p.dis /dis/p.dis
+	/boot/dis/ls.dis /dis/ls.dis
+	/boot/dis/lib/daytime.dis /dis/lib/daytime.dis
+	/boot/dis/time.dis /dis/time.dis
+	/boot/dis/xd.dis /dis/xd.dis
--- /dev/null
+++ b/os/cerf405/clock.c
@@ -1,0 +1,159 @@
+#include	"u.h"
+#include	"../port/lib.h"
+#include	"mem.h"
+#include	"dat.h"
+#include	"fns.h"
+#include	"io.h"
+#include	"ureg.h"
+
+#include	<isa.h>
+#include	<interp.h>
+
+typedef struct Clock0link Clock0link;
+typedef struct Clock0link {
+	void		(*clock)(void);
+	Clock0link*	link;
+} Clock0link;
+
+static Clock0link *clock0link;
+static Lock clock0lock;
+ulong	clkrelinq;
+void	(*kproftick)(ulong);	/* set by devkprof.c when active */
+void	(*archclocktick)(void);	/* set by arch*.c when desired */
+
+Timer*
+addclock0link(void (*clock)(void), int)
+{
+	Clock0link *lp;
+
+	if((lp = malloc(sizeof(Clock0link))) == 0){
+		print("addclock0link: too many links\n");
+		return nil;
+	}
+	ilock(&clock0lock);
+	lp->clock = clock;
+	lp->link = clock0link;
+	clock0link = lp;
+	iunlock(&clock0lock);
+	return nil;
+}
+
+void
+delay(int l)
+{
+	ulong i, j;
+
+	j = m->delayloop;
+	while(l-- > 0)
+		for(i=0; i < j; i++)
+			;
+}
+
+void
+microdelay(int l)
+{
+	ulong i;
+
+	l *= m->delayloop;
+	l /= 1000;
+	if(l <= 0)
+		l = 1;
+	for(i = 0; i < l; i++)
+		;
+}
+
+enum {
+	Timebase = 1,	/* system clock cycles per time base cycle */
+
+	Wp17=	0<<30,	/* watchdog period (2^x clocks) */
+	Wp21=	1<<30,
+	Wp25=	2<<30,
+	Wp29=	3<<30,
+	Wrnone=	0<<28,	/* no watchdog reset */
+	Wrcore=	1<<28,	/* core reset */
+	Wrchip=	2<<28,	/* chip reset */
+	Wrsys=	3<<28,	/* system reset */
+	Wie=		1<<27,	/* watchdog interrupt enable */
+	Pie=		1<<26,	/* enable PIT interrupt */
+	Fit9=		0<<24,	/* fit period (2^x clocks) */
+	Fit13=	1<<24,
+	Fit17=	2<<24,
+	Fit21=	3<<24,
+	Fie=		1<<23,	/* fit interrupt enable */
+	Are=		1<<22,	/* auto reload enable */
+
+	/* dcr */
+	Boot=	0x0F1,
+	Epctl=	0x0F3,
+	Pllmr0=	0x0F0,
+	Pllmr1=	0x0F4,
+	Ucr=		0x0F5,
+};
+
+void
+clockinit(void)
+{
+	long x;
+
+	m->delayloop = m->cpuhz/1000;	/* initial estimate */
+	do {
+		x = gettbl();
+		delay(10);
+		x = gettbl() - x;
+	} while(x < 0);
+
+	/*
+	 *  fix count
+	 */
+	m->delayloop = ((vlong)m->delayloop*(10*(vlong)m->clockgen/1000))/(x*Timebase);
+	if((int)m->delayloop <= 0)
+		m->delayloop = 20000;
+
+	x = (m->clockgen/Timebase)/HZ;
+	putpit(x);
+iprint("pit value=%.8lux [%lud]\n", x, x);
+	puttsr(~0);
+	puttcr(Pie|Are);
+iprint("boot=%.8lux epctl=%.8lux pllmr0=%.8lux pllmr1=%.8lux ucr=%.8lux\n",
+	getdcr(Boot), getdcr(Epctl), getdcr(Pllmr0), getdcr(Pllmr1), getdcr(Ucr));
+}
+
+void
+clockintr(Ureg *ur)
+{
+	Clock0link *lp;
+
+	/* PIT was set to reload automatically */
+	puttsr(~0);
+	m->ticks++;
+
+	if(up)
+		up->pc = ur->pc;
+
+	if(archclocktick != nil)
+		archclocktick();
+	checkalarms();
+	if(m->machno == 0) {
+		if(kproftick != nil)
+			(*kproftick)(ur->pc);
+		lock(&clock0lock);
+		for(lp = clock0link; lp; lp = lp->link)
+			lp->clock();
+		unlock(&clock0lock);
+	}
+
+	if(up && up->state == Running){
+		if(cflag && up->type == Interp && tready(nil))
+			ur->cr |= 1;	/* set flag in condition register for ../../interp/comp-power.c:/^schedcheck */
+		if(anyready())
+			sched();
+	}
+}
+
+uvlong
+fastticks(uvlong *hz)
+{
+	if(hz)
+		*hz = HZ;
+	return m->ticks;
+}
--- /dev/null
+++ b/os/cerf405/compile.c
@@ -1,0 +1,34 @@
+#include	"u.h"
+#include	"../port/lib.h"
+#include	"mem.h"
+#include	"dat.h"
+#include	"fns.h"
+#include	"io.h"
+
+#define	MAXDCR	0x220
+
+#define	DCRF(n)	((((n)>>5)&0x1F)|(((n)&0x1F)<<5))
+#define	MTDCR(s,n)	((31<<26)|((s)<<21)|(DCRF(n)<<11)|(451<<1))
+#define	MFDCR(n,t)	((31<<26)|((t)<<21)|(DCRF(n)<<11)|(323<<1))
+#define	RETURN	0x4e800020
+ulong	_getdcr[MAXDCR][2];
+ulong	_putdcr[MAXDCR][2];
+
+void
+compiledcr(void)
+{
+	ulong *p;
+	int i;
+
+	for(i=0; i<MAXDCR; i++){
+		p = _getdcr[i];
+		p[0] = MFDCR(i, 3);
+		p[1] = RETURN;
+		p = _putdcr[i];
+		p[0] = MTDCR(3, i);
+		p[1] = RETURN;
+	}
+	dcflush(_getdcr, sizeof(_getdcr));
+	dcflush(_putdcr, sizeof(_putdcr));
+	/* no need to flush icache since they won't be there */
+}
--- /dev/null
+++ b/os/cerf405/dat.h
@@ -1,0 +1,159 @@
+typedef struct Conf	Conf;
+typedef struct FPU	FPU;
+typedef struct FPenv	FPenv;
+typedef struct Irqctl	Irqctl;
+typedef struct ISAConf	ISAConf;
+typedef struct Label	Label;
+typedef struct Lock	Lock;
+typedef struct Mach	Mach;
+typedef struct Map	Map;
+typedef struct Pcidev Pcidev;
+typedef struct Power Power;
+typedef struct RMap RMap;
+typedef struct Ureg	Ureg;
+
+typedef ulong Instr;
+
+#define	MACHP(n)	(n==0? &mach0 : *(Mach**)0)
+
+struct	Lock
+{
+	ulong	key;
+	ulong	pc;
+	ulong	sr;
+	int	pri;
+};
+
+struct	Label
+{
+	ulong	sp;
+	ulong	pc;
+};
+
+/*
+ * Proc.fpstate
+ */
+enum
+{
+	FPINIT,
+	FPACTIVE,
+	FPINACTIVE,
+};
+
+/*
+ * This structure must agree with FPsave and FPrestore asm routines
+ */
+struct FPenv
+{
+	union {
+		double	fpscrd;
+		struct {
+			ulong	pad;
+			ulong	fpscr;
+		};
+	};
+	int	fpistate;	/* emulated fp */
+	ulong	emreg[32][3];	/* emulated fp */
+};
+/*
+ * This structure must agree with fpsave and fprestore asm routines
+ */
+struct	FPU
+{
+	double	fpreg[32];
+	FPenv	env;
+};
+
+struct Conf
+{
+	ulong	nmach;		/* processors */
+	ulong	nproc;		/* processes */
+	ulong	npage0;		/* total physical pages of memory */
+	ulong	npage1;		/* total physical pages of memory */
+	ulong	npage;		/* total physical pages of memory */
+	ulong	base0;		/* base of bank 0 */
+	ulong	base1;		/* base of bank 1 */
+	ulong	ialloc;		/* max interrupt time allocation in bytes */
+};
+
+#include "../port/portdat.h"
+
+/*
+ *  machine dependent definitions not used by ../port/dat.h
+ */
+
+struct Mach
+{
+	/* OFFSETS OF THE FOLLOWING KNOWN BY l.s */
+	int	machno;			/* physical id of processor (unused) */
+	ulong	splpc;			/* pc of last caller to splhi (unused) */
+	int	mmask;			/* 1<<m->machno (unused) */
+
+	/* ordering from here on irrelevant */
+	ulong	ticks;			/* of the clock since boot time */
+	Proc	*proc;			/* current process on this processor */
+	Label	sched;			/* scheduler wakeup */
+	Lock	alarmlock;		/* access to alarm list */
+	void	*alarm;			/* alarms bound to this clock */
+	int	nrdy;
+	int	speed;	/* general system clock in MHz */
+	long	oscclk;	/* oscillator frequency (MHz) */
+	long	cpuhz;	/* general system clock (cycles) */
+	long	clockgen;	/* clock generator frequency (cycles) */
+	long	vcohz;
+	long	pllhz;
+	long	plbhz;
+	long	opbhz;
+	long	epbhz;
+	long	pcihz;
+	int	cputype;
+	ulong	delayloop;
+
+	/* MUST BE LAST */
+	int	stack[1];
+};
+extern	Mach	mach0;
+
+
+/*
+ *  a parsed .ini line
+ */
+#define NISAOPT		8
+
+struct ISAConf {
+	char*	type;
+	ulong	port;
+	ulong	irq;
+	ulong	mem;
+	int	dma;
+	ulong	size;
+	ulong	freq;
+	uchar	bus;
+
+	int	nopt;
+	char*	opt[NISAOPT];
+};
+
+struct Map {
+	int	size;
+	ulong	addr;
+};
+
+struct RMap {
+	char*	name;
+	Map*	map;
+	Map*	mapend;
+
+	Lock;
+};
+
+struct Power {
+	Dev*	dev;
+	int	(*powerdown)(Power*);
+	int	(*powerup)(Power*);
+	int	state;
+	void*	arg;
+};
+
+extern register Mach	*m;
+extern register Proc	*up;
--- /dev/null
+++ b/os/cerf405/devboot.c
@@ -1,0 +1,132 @@
+#include	"u.h"
+#include	"../port/lib.h"
+#include	"mem.h"
+#include	"dat.h"
+#include	"fns.h"
+#include	"../port/error.h"
+
+enum{
+	Qdir,
+	Qboot,
+	Qmem,
+};
+
+Dirtab bootdir[]={
+	".",			{Qdir,0,QTDIR},	0,	0555,
+	"boot",		{Qboot},	0,	0666,
+	"mem",		{Qmem},		0,	0666,
+};
+
+#define	NBOOT	(sizeof bootdir/sizeof(Dirtab))
+
+static void
+bootreset(void)
+{
+}
+
+static Chan*
+bootattach(char *spec)
+{
+	return devattach('B', spec);
+}
+
+static Walkqid*
+bootwalk(Chan *c, Chan *nc, char **name, int nname)
+{
+	return devwalk(c, nc, name, nname, bootdir, NBOOT, devgen);
+}
+
+static int
+bootstat(Chan *c, uchar *dp, int n)
+{
+	return devstat(c, dp, n, bootdir, NBOOT, devgen);
+}
+
+static Chan*
+bootopen(Chan *c, int omode)
+{
+	return devopen(c, omode, bootdir, NBOOT, devgen);
+}
+
+static void	 
+bootclose(Chan*)
+{
+}
+
+static long	 
+bootread(Chan *c, void *buf, long n, vlong off)
+{
+	ulong offset = off;
+
+	switch((ulong)c->qid.path){
+
+	case Qdir:
+		return devdirread(c, buf, n, bootdir, NBOOT, devgen);
+
+	case Qmem:
+		/* kernel memory */
+		if(offset>=KZERO && offset<KZERO+conf.npage*BY2PG){
+			if(offset+n > KZERO+conf.npage*BY2PG)
+				n = KZERO+conf.npage*BY2PG - offset;
+			memmove(buf, (char*)offset, n);
+			return n;
+		}
+		error(Ebadarg);
+	}
+
+	error(Egreg);
+	return 0;	/* not reached */
+}
+
+static long	 
+bootwrite(Chan *c, void *buf, long n, vlong off)
+{
+	ulong offset = off;
+	ulong pc;
+	uchar *p;
+
+	switch((ulong)c->qid.path){
+	case Qmem:
+		/* kernel memory */
+		if(offset>=KZERO && offset<KZERO+conf.npage*BY2PG){
+			if(offset+n > KZERO+conf.npage*BY2PG)
+				n = KZERO+conf.npage*BY2PG - offset;
+			memmove((char*)offset, buf, n);
+			segflush((void*)offset, n);
+			return n;
+		}
+		error(Ebadarg);
+
+	case Qboot:
+		p = (uchar*)buf;
+		pc = (((((p[0]<<8)|p[1])<<8)|p[2])<<8)|p[3];
+		if(pc < KZERO || pc >= KZERO+conf.npage*BY2PG)
+			error(Ebadarg);
+		splhi();
+		segflush((void*)pc, 64*1024);
+		gotopc(pc);
+	}
+	error(Ebadarg);
+	return 0;	/* not reached */
+}
+
+Dev bootdevtab = {
+	'B',
+	"boot",
+
+	bootreset,
+	devinit,
+	devshutdown,
+	bootattach,
+	bootwalk,
+	bootstat,
+	bootopen,
+	devcreate,
+	bootclose,
+	bootread,
+	devbread,
+	bootwrite,
+	devbwrite,
+	devremove,
+	devwstat,
+};
--- /dev/null
+++ b/os/cerf405/devether.c
@@ -1,0 +1,617 @@
+#include "u.h"
+#include "../port/lib.h"
+#include "mem.h"
+#include "dat.h"
+#include "fns.h"
+#include "io.h"
+#include "ureg.h"
+#include "../port/error.h"
+#include "../port/netif.h"
+
+#include "etherif.h"
+
+static Ether *etherxx[MaxEther];
+
+Chan*
+etherattach(char* spec)
+{
+	ulong ctlrno;
+	char *p;
+	Chan *chan;
+	Ether *ether;
+
+	ctlrno = 0;
+	if(spec && *spec){
+		ctlrno = strtoul(spec, &p, 0);
+		if((ctlrno == 0 && p == spec) || *p || (ctlrno >= MaxEther))
+			error(Ebadarg);
+	}
+	if((ether = etherxx[ctlrno]) == 0)
+		error(Enodev);
+	rlock(ether);
+	if(waserror()){
+		runlock(ether);
+		nexterror();
+	}
+	chan = devattach('l', spec);
+	chan->dev = ctlrno;
+	if(ether->attach)
+		ether->attach(etherxx[ctlrno]);
+	poperror();
+	runlock(ether);
+	return chan;
+}
+
+static void
+ethershutdown(void)
+{
+	Ether *ether;
+	int i;
+
+	for(i=0; i<MaxEther; i++){
+		ether = etherxx[i];
+		if(ether != nil && ether->detach != nil)
+			ether->detach(ether);
+	}
+}
+
+static Walkqid*
+etherwalk(Chan* chan, Chan *nchan, char **name, int nname)
+{
+	Walkqid *wq;
+	Ether *ether;
+
+	ether = etherxx[chan->dev];
+	rlock(ether);
+	if(waserror()) {
+		runlock(ether);
+		nexterror();
+	}
+	wq = netifwalk(etherxx[chan->dev], chan, nchan, name, nname);
+	poperror();
+	runlock(ether);
+	return wq;
+}
+
+static int
+etherstat(Chan* chan, uchar* dp, int n)
+{
+	int s;
+	Ether *ether;
+
+	ether = etherxx[chan->dev];
+	rlock(ether);
+	if(waserror()) {
+		runlock(ether);
+		nexterror();
+	}
+	s = netifstat(ether, chan, dp, n);
+	poperror();
+	runlock(ether);
+	return s;
+}
+
+static Chan*
+etheropen(Chan* chan, int omode)
+{
+	Chan *c;
+	Ether *ether;
+
+	ether = etherxx[chan->dev];
+	rlock(ether);
+	if(waserror()) {
+		runlock(ether);
+		nexterror();
+	}
+	c = netifopen(ether, chan, omode);
+	poperror();
+	runlock(ether);
+	return c;
+}
+
+static void
+ethercreate(Chan*, char*, int, ulong)
+{
+}
+
+static void
+etherclose(Chan* chan)
+{
+	Ether *ether;
+
+	ether = etherxx[chan->dev];
+	rlock(ether);
+	if(waserror()) {
+		runlock(ether);
+		nexterror();
+	}
+	netifclose(ether, chan);
+	poperror();
+	runlock(ether);
+}
+
+static long
+etherread(Chan* chan, void* buf, long n, vlong off)
+{
+	Ether *ether;
+	ulong offset = off;
+	long r;
+
+	ether = etherxx[chan->dev];
+	rlock(ether);
+	if(waserror()) {
+		runlock(ether);
+		nexterror();
+	}
+	if((chan->qid.type & QTDIR) == 0 && ether->ifstat){
+		/*
+		 * With some controllers it is necessary to reach
+		 * into the chip to extract statistics.
+		 */
+		if(NETTYPE(chan->qid.path) == Nifstatqid){
+			r = ether->ifstat(ether, buf, n, offset);
+			goto out;
+		}
+		if(NETTYPE(chan->qid.path) == Nstatqid)
+			ether->ifstat(ether, buf, 0, offset);
+	}
+	r = netifread(ether, chan, buf, n, offset);
+out:
+	poperror();
+	runlock(ether);
+	return r;
+}
+
+static Block*
+etherbread(Chan* chan, long n, ulong offset)
+{
+	Block *b;
+	Ether *ether;
+
+	ether = etherxx[chan->dev];
+	rlock(ether);
+	if(waserror()) {
+		runlock(ether);
+		nexterror();
+	}
+	b = netifbread(ether, chan, n, offset);
+	poperror();
+	runlock(ether);
+	return b;
+}
+
+static int
+etherwstat(Chan* chan, uchar* dp, int n)
+{
+	Ether *ether;
+	int r;
+
+	ether = etherxx[chan->dev];
+	rlock(ether);
+	if(waserror()) {
+		runlock(ether);
+		nexterror();
+	}
+	r = netifwstat(ether, chan, dp, n);
+	poperror();
+	runlock(ether);
+	return r;
+}
+
+static void
+etherrtrace(Netfile* f, Etherpkt* pkt, int len)
+{
+	int i, n;
+	Block *bp;
+
+	if(qwindow(f->in) <= 0)
+		return;
+	if(len > 58)
+		n = 58;
+	else
+		n = len;
+	bp = iallocb(64);
+	if(bp == nil)
+		return;
+	memmove(bp->wp, pkt->d, n);
+	i = TK2MS(MACHP(0)->ticks);
+	bp->wp[58] = len>>8;
+	bp->wp[59] = len;
+	bp->wp[60] = i>>24;
+	bp->wp[61] = i>>16;
+	bp->wp[62] = i>>8;
+	bp->wp[63] = i;
+	bp->wp += 64;
+	qpass(f->in, bp);
+}
+
+Block*
+etheriq(Ether* ether, Block* bp, int fromwire)
+{
+	Etherpkt *pkt;
+	ushort type;
+	int len, multi, tome, fromme;
+	Netfile **ep, *f, **fp, *fx;
+	Block *xbp;
+
+	ether->inpackets++;
+
+	pkt = (Etherpkt*)bp->rp;
+	len = BLEN(bp);
+	type = (pkt->type[0]<<8)|pkt->type[1];
+	fx = 0;
+	ep = &ether->f[Ntypes];
+
+	multi = pkt->d[0] & 1;
+	/* check for valid multcast addresses */
+	if(multi && memcmp(pkt->d, ether->bcast, sizeof(pkt->d)) != 0 && ether->prom == 0){
+		if(!activemulti(ether, pkt->d, sizeof(pkt->d))){
+			if(fromwire){
+				freeb(bp);
+				bp = 0;
+			}
+			return bp;
+		}
+	}
+
+	/* is it for me? */
+	tome = memcmp(pkt->d, ether->ea, sizeof(pkt->d)) == 0;
+	fromme = memcmp(pkt->s, ether->ea, sizeof(pkt->s)) == 0;
+
+	/*
+	 * Multiplex the packet to all the connections which want it.
+	 * If the packet is not to be used subsequently (fromwire != 0),
+	 * attempt to simply pass it into one of the connections, thereby
+	 * saving a copy of the data (usual case hopefully).
+	 */
+	for(fp = ether->f; fp < ep; fp++){
+		if((f = *fp) && (f->type == type || f->type < 0))
+		if(tome || multi || f->prom){
+			/* Don't want to hear bridged packets */
+			if(f->bridge && !fromwire && !fromme)
+				continue;
+			if(!f->headersonly){
+				if(fromwire && fx == 0)
+					fx = f;
+				else if(xbp = iallocb(len)){
+					memmove(xbp->wp, pkt, len);
+					xbp->wp += len;
+					if(qpass(f->in, xbp) < 0)
+						ether->soverflows++;
+				}
+				else
+					ether->soverflows++;
+			}
+			else
+				etherrtrace(f, pkt, len);
+		}
+	}
+
+	if(fx){
+		if(qpass(fx->in, bp) < 0)
+			ether->soverflows++;
+		return 0;
+	}
+	if(fromwire){
+		freeb(bp);
+		return 0;
+	}
+
+	return bp;
+}
+
+static int
+etheroq(Ether* ether, Block* bp)
+{
+	int len, loopback, s;
+	Etherpkt *pkt;
+
+	ether->outpackets++;
+
+	/*
+	 * Check if the packet has to be placed back onto the input queue,
+	 * i.e. if it's a loopback or broadcast packet or the interface is
+	 * in promiscuous mode.
+	 * If it's a loopback packet indicate to etheriq that the data isn't
+	 * needed and return, etheriq will pass-on or free the block.
+	 * To enable bridging to work, only packets that were originated
+	 * by this interface are fed back.
+	 */
+	pkt = (Etherpkt*)bp->rp;
+	len = BLEN(bp);
+	loopback = memcmp(pkt->d, ether->ea, sizeof(pkt->d)) == 0;
+	if(loopback || memcmp(pkt->d, ether->bcast, sizeof(pkt->d)) == 0 || ether->prom){
+		s = splhi();
+		etheriq(ether, bp, 0);
+		splx(s);
+	}
+
+	if(!loopback){
+		qbwrite(ether->oq, bp);
+		if(ether->transmit != nil)
+			ether->transmit(ether);
+	}else
+		freeb(bp);
+
+	return len;
+}
+
+static long
+etherwrite(Chan* chan, void* buf, long n, vlong)
+{
+	Ether *ether;
+	Block *bp;
+	int onoff;
+	Cmdbuf *cb;
+	long l;
+
+	ether = etherxx[chan->dev];
+	rlock(ether);
+	if(waserror()) {
+		runlock(ether);
+		nexterror();
+	}
+	if(NETTYPE(chan->qid.path) != Ndataqid) {
+		l = netifwrite(ether, chan, buf, n);
+		if(l >= 0)
+			goto out;
+		cb = parsecmd(buf, n);
+		if(strcmp(cb->f[0], "nonblocking") == 0){
+			if(cb->nf <= 1)
+				onoff = 1;
+			else
+				onoff = atoi(cb->f[1]);
+			qnoblock(ether->oq, onoff);
+			free(cb);
+			goto out;
+		}
+		free(cb);
+		if(ether->ctl!=nil){
+			l = ether->ctl(ether,buf,n);
+			goto out;
+		}
+		error(Ebadctl);
+	}
+
+	if(n > ether->maxmtu)
+		error(Etoobig);
+	if(n < ether->minmtu)
+		error(Etoosmall);
+	bp = allocb(n);
+	if(waserror()){
+		freeb(bp);
+		nexterror();
+	}
+	memmove(bp->rp, buf, n);
+	memmove(bp->rp+Eaddrlen, ether->ea, Eaddrlen);
+	bp->wp += n;
+	poperror();
+
+	l = etheroq(ether, bp);
+out:
+	poperror();
+	runlock(ether);
+	return l;
+}
+
+static long
+etherbwrite(Chan* chan, Block* bp, ulong)
+{
+	Ether *ether;
+	long n;
+
+	n = BLEN(bp);
+	if(NETTYPE(chan->qid.path) != Ndataqid){
+		if(waserror()) {
+			freeb(bp);
+			nexterror();
+		}
+		n = etherwrite(chan, bp->rp, n, 0);
+		poperror();
+		freeb(bp);
+		return n;
+	}
+	ether = etherxx[chan->dev];
+	rlock(ether);
+	if(waserror()) {
+		runlock(ether);
+		nexterror();
+	}
+	if(n > ether->maxmtu){
+		freeb(bp);
+		error(Etoobig);
+	}
+	if(n < ether->minmtu){
+		freeb(bp);
+		error(Etoosmall);
+	}
+	n = etheroq(ether, bp);
+	poperror();
+	runlock(ether);
+	return n;
+}
+
+static struct {
+	char*	type;
+	int	(*reset)(Ether*);
+} cards[MaxEther+1];
+
+void
+addethercard(char* t, int (*r)(Ether*))
+{
+	static int ncard;
+
+	if(ncard == MaxEther)
+		panic("too many ether cards");
+	cards[ncard].type = t;
+	cards[ncard].reset = r;
+	ncard++;
+}
+
+int
+parseether(uchar *to, char *from)
+{
+	char nip[4];
+	char *p;
+	int i;
+
+	p = from;
+	for(i = 0; i < Eaddrlen; i++){
+		if(*p == 0)
+			return -1;
+		nip[0] = *p++;
+		if(*p == 0)
+			return -1;
+		nip[1] = *p++;
+		nip[2] = 0;
+		to[i] = strtoul(nip, 0, 16);
+		if(*p == ':')
+			p++;
+	}
+	return 0;
+}
+
+static void
+etherreset(void)
+{
+	Ether *ether;
+	int i, n, ctlrno;
+	char name[KNAMELEN], buf[128];
+
+	for(ether = 0, ctlrno = 0; ctlrno < MaxEther; ctlrno++){
+		if(ether == 0)
+			ether = malloc(sizeof(Ether));
+		memset(ether, 0, sizeof(Ether));
+		ether->ctlrno = ctlrno;
+		ether->mbps = 10;
+		ether->minmtu = ETHERMINTU;
+		ether->maxmtu = ETHERMAXTU;
+		ether->tbdf = BUSUNKNOWN;
+
+		if(archether(ctlrno, ether) <= 0)
+			continue;
+
+		for(n = 0; cards[n].type; n++){
+			if(cistrcmp(cards[n].type, ether->type))
+				continue;
+			for(i = 0; i < ether->nopt; i++){
+				if(cistrncmp(ether->opt[i], "ea=", 3) == 0){
+					if(parseether(ether->ea, &ether->opt[i][3]) == -1)
+						memset(ether->ea, 0, Eaddrlen);
+				}else if(cistrcmp(ether->opt[i], "fullduplex") == 0 ||
+					cistrcmp(ether->opt[i], "10BASE-TFD") == 0)
+					ether->fullduplex = 1;
+				else if(cistrcmp(ether->opt[i], "100BASE-TXFD") == 0)
+					ether->mbps = 100;
+			}
+			if(cards[n].reset(ether))
+				break;
+			snprint(name, sizeof(name), "ether%d", ctlrno);
+
+			if(ether->interrupt != nil)
+				intrenable(ether->irq, ether->interrupt, ether, ether->tbdf, name);
+
+			i = sprint(buf, "#l%d: %s: %dMbps port 0x%luX irq %lud",
+				ctlrno, ether->type, ether->mbps, ether->port, ether->irq);
+			if(ether->mem)
+				i += sprint(buf+i, " addr 0x%luX", PADDR(ether->mem));
+			if(ether->size)
+				i += sprint(buf+i, " size 0x%luX", ether->size);
+			i += sprint(buf+i, ": %2.2uX%2.2uX%2.2uX%2.2uX%2.2uX%2.2uX",
+				ether->ea[0], ether->ea[1], ether->ea[2],
+				ether->ea[3], ether->ea[4], ether->ea[5]);
+			sprint(buf+i, "\n");
+			print(buf);
+
+			if(ether->mbps == 100){
+				netifinit(ether, name, Ntypes, 256*1024);
+				if(ether->oq == 0)
+					ether->oq = qopen(256*1024, Qmsg, 0, 0);
+			}
+			else{
+				netifinit(ether, name, Ntypes, 64*1024);
+				if(ether->oq == 0)
+					ether->oq = qopen(64*1024, Qmsg, 0, 0);
+			}
+			if(ether->oq == 0)
+				panic("etherreset %s", name);
+			ether->alen = Eaddrlen;
+			memmove(ether->addr, ether->ea, Eaddrlen);
+			memset(ether->bcast, 0xFF, Eaddrlen);
+
+			etherxx[ctlrno] = ether;
+			ether = 0;
+			break;
+		}
+	}
+	if(ether)
+		free(ether);
+}
+
+static void
+etherpower(int on)
+{
+	int i;
+	Ether *ether;
+
+	for(i = 0; i < MaxEther; i++){
+		if((ether = etherxx[i]) == nil || ether->power == nil)
+			continue;
+		if(on){
+			if(canrlock(ether))
+				continue;
+			if(ether->power != nil)
+				ether->power(ether, on);
+			wunlock(ether);
+		}else{
+			if(!canrlock(ether))
+				continue;
+			wlock(ether);
+			if(ether->power != nil)
+				ether->power(ether, on);
+			/* Keep locked until power goes back on */
+		}
+	}
+}
+
+#define POLY 0xedb88320
+
+/* really slow 32 bit crc for ethers */
+ulong
+ethercrc(uchar *p, int len)
+{
+	int i, j;
+	ulong crc, b;
+
+	crc = 0xffffffff;
+	for(i = 0; i < len; i++){
+		b = *p++;
+		for(j = 0; j < 8; j++){
+			crc = (crc>>1) ^ (((crc^b) & 1) ? POLY : 0);
+			b >>= 1;
+		}
+	}
+	return crc;
+}
+
+Dev etherdevtab = {
+	'l',
+	"ether",
+
+	etherreset,
+	devinit,
+	ethershutdown,
+	etherattach,
+	etherwalk,
+	etherstat,
+	etheropen,
+	ethercreate,
+	etherclose,
+	etherread,
+	etherbread,
+	etherwrite,
+	etherbwrite,
+	devremove,
+	etherwstat,
+	etherpower,
+};
--- /dev/null
+++ b/os/cerf405/devrtc.c
@@ -1,0 +1,369 @@
+/*
+ *	DS1339 Timekeeper (on I2C)
+ */
+
+#include	"u.h"
+#include	"../port/lib.h"
+#include	"mem.h"
+#include	"dat.h"
+#include	"fns.h"
+#include	"../port/error.h"
+
+#include	"io.h"
+
+typedef struct Rtc	Rtc;
+typedef struct Rtcreg	Rtcreg;
+
+struct Rtc
+{
+	int	sec;
+	int	min;
+	int	hour;
+	int	wday;
+	int	mday;
+	int	mon;
+	int	year;
+};
+
+struct Rtcreg
+{
+	uchar	sec;
+	uchar	min;
+	uchar	hour;
+	uchar	wday;	/* 1=Sun */
+	uchar	mday;	/* 00-31 */
+	uchar	mon;	/* 1-12 */
+	uchar	year;
+};
+
+enum{
+	Qdir = 0,
+	Qrtc,
+
+	Rtclen=	7,		/* bytes read and written to timekeeper */
+};
+
+static QLock	rtclock;		/* mutex on nvram operations */
+static I2Cdev	rtdev;
+
+static Dirtab rtcdir[]={
+	".",		{Qdir, 0, QTDIR},	0,	DMDIR|0555,
+	"rtc",		{Qrtc, 0},	0,	0664,
+};
+
+static ulong	rtc2sec(Rtc*);
+static void	sec2rtc(ulong, Rtc*);
+static void	setrtc(Rtc*);
+
+static void
+rtcreset(void)
+{
+	rtdev.addr = 0x68;
+	rtdev.salen = 1;
+	i2csetup(1);
+}
+
+static Chan*
+rtcattach(char *spec)
+{
+	return devattach('r', spec);
+}
+
+static Walkqid*
+rtcwalk(Chan *c, Chan *nc, char **name, int nname)
+{
+	return devwalk(c, nc, name, nname, rtcdir, nelem(rtcdir), devgen);
+}
+
+static int	 
+rtcstat(Chan *c, uchar *dp, int n)
+{
+	return devstat(c, dp, n, rtcdir, nelem(rtcdir), devgen);
+}
+
+static Chan*
+rtcopen(Chan *c, int omode)
+{
+	omode = openmode(omode);
+	switch((ulong)c->qid.path){
+	case Qrtc:
+		if(strcmp(up->env->user, eve)!=0 && omode!=OREAD)
+			error(Eperm);
+		break;
+	}
+	return devopen(c, omode, rtcdir, nelem(rtcdir), devgen);
+}
+
+static void	 
+rtcclose(Chan*)
+{
+}
+
+static long	 
+rtcread(Chan *c, void *buf, long n, vlong offset)
+{
+	ulong t, ot;
+
+	if(c->qid.type & QTDIR)
+		return devdirread(c, buf, n, rtcdir, nelem(rtcdir), devgen);
+
+	switch((ulong)c->qid.path){
+	case Qrtc:
+		qlock(&rtclock);
+		t = rtctime();
+		do{
+			ot = t;
+			t = rtctime();	/* make sure there's no skew */
+		}while(t != ot);
+		qunlock(&rtclock);
+		return readnum(offset, buf, n, t, 12);
+	}
+	error(Egreg);
+	return -1;		/* never reached */
+}
+
+static long	 
+rtcwrite(Chan *c, void *buf, long n, vlong off)
+{
+	Rtc rtc;
+	ulong secs;
+	char *cp, sbuf[32];
+	ulong offset = off;
+
+	switch((ulong)c->qid.path){
+	case Qrtc:
+		if(offset!=0 || n >= sizeof(sbuf)-1)
+			error(Ebadarg);
+		memmove(sbuf, buf, n);
+		sbuf[n] = '\0';
+		/*
+		 *  read the time
+		 */
+		cp = sbuf;
+		while(*cp){
+			if(*cp>='0' && *cp<='9')
+				break;
+			cp++;
+		}
+		secs = strtoul(cp, 0, 0);
+		/*
+		 *  convert to bcd
+		 */
+		sec2rtc(secs, &rtc);
+		/*
+		 * write it
+		 */
+		setrtc(&rtc);
+		return n;
+	}
+	error(Egreg);
+	return -1;		/* never reached */
+}
+
+Dev rtcdevtab = {
+	'r',
+	"rtc",
+
+	rtcreset,
+	devinit,
+	devshutdown,
+	rtcattach,
+	rtcwalk,
+	rtcstat,
+	rtcopen,
+	devcreate,
+	rtcclose,
+	rtcread,
+	devbread,
+	rtcwrite,
+	devbwrite,
+	devremove,
+	devwstat,
+};
+
+static int
+getbcd(int bcd)
+{
+	return (bcd&0x0f) + 10 * (bcd>>4);
+}
+
+static int
+putbcd(int val)
+{
+	return (val % 10) | (((val/10) % 10) << 4);
+}
+
+long	 
+rtctime(void)
+{
+	Rtc rtc;
+	Rtcreg d;
+	int h;
+
+	if(waserror()){
+		iprint("rtc: err %s\n", up->env->errstr);
+		return 0;
+	}
+	if(i2crecv(&rtdev, &d, Rtclen, 0) != Rtclen)
+		return 0;
+	poperror();
+	rtc.sec = getbcd(d.sec);
+	rtc.min = getbcd(d.min);
+	if(d.hour & (1<<6)){	/* 12 hour clock */
+		h = d.hour & 0x1F;
+		if(d.hour & (1<<5))
+			h += 0x12;
+		rtc.hour = getbcd(h);
+	}else
+		rtc.hour = getbcd(d.hour);
+	rtc.mday = getbcd(d.mday);
+	rtc.mon = getbcd(d.mon & 0x7f);
+	rtc.year = getbcd(d.year);
+	if(rtc.mon < 1 || rtc.mon > 12)
+		return 0;
+	if(d.mon & (1<<7))
+		rtc.year += 2000;
+	else
+		rtc.year += 1900;
+	return rtc2sec(&rtc);
+}
+
+static void
+setrtc(Rtc *rtc)
+{
+	Rtcreg d;
+
+	memset(&d, 0, sizeof(d));
+	d.year = putbcd(rtc->year % 100);
+	d.mon = putbcd(rtc->mon);
+	if(rtc->year >= 2000)
+		d.mon |= 1<<7;
+	d.wday = rtc->wday+1;
+	d.mday = putbcd(rtc->mday);
+	d.hour = putbcd(rtc->hour);
+	d.min = putbcd(rtc->min);
+	d.sec = putbcd(rtc->sec);
+	i2csend(&rtdev, &d, Rtclen, 0);
+}
+
+#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
+ */
+static ulong
+rtc2sec(Rtc *rtc)
+{
+	ulong secs;
+	int i;
+	int *d2m;
+
+	secs = 0;
+
+	/*
+	 *  seconds per year
+	 */
+	for(i = 1970; i < rtc->year; i++){
+		d2m = yrsize(i);
+		secs += d2m[0] * SEC2DAY;
+	}
+
+	/*
+	 *  seconds per month
+	 */
+	d2m = yrsize(rtc->year);
+	for(i = 1; i < rtc->mon; i++)
+		secs += d2m[i] * SEC2DAY;
+
+	secs += (rtc->mday-1) * SEC2DAY;
+	secs += rtc->hour * SEC2HOUR;
+	secs += rtc->min * SEC2MIN;
+	secs += rtc->sec;
+
+	return secs;
+}
+
+/*
+ *  compute rtc from seconds since Jan 1 1970
+ */
+static void
+sec2rtc(ulong secs, Rtc *rtc)
+{
+	int d;
+	long hms, day;
+	int *d2m;
+
+	/*
+	 * break initial number into days
+	 */
+	hms = secs % SEC2DAY;
+	day = secs / SEC2DAY;
+	if(hms < 0) {
+		hms += SEC2DAY;
+		day -= 1;
+	}
+
+	/*
+	 * day is the day number.
+	 * generate day of the week.
+	 * The addend is 4 mod 7 (1/1/1970 was Thursday)
+	 */
+
+	rtc->wday = (day + 7340036L) % 7;
+
+	/*
+	 * generate hours:minutes:seconds
+	 */
+	rtc->sec = hms % 60;
+	d = hms / 60;
+	rtc->min = d % 60;
+	d /= 60;
+	rtc->hour = d;
+
+	/*
+	 * year number
+	 */
+	if(day >= 0)
+		for(d = 1970; day >= *yrsize(d); d++)
+			day -= *yrsize(d);
+	else
+		for (d = 1970; day < 0; d--)
+			day += *yrsize(d-1);
+	rtc->year = d;
+
+	/*
+	 * generate month
+	 */
+	d2m = yrsize(rtc->year);
+	for(d = 1; day >= d2m[d]; d++)
+		day -= d2m[d];
+	rtc->mday = day + 1;
+	rtc->mon = d;
+}
--- /dev/null
+++ b/os/cerf405/devuart.c
@@ -1,0 +1,1064 @@
+#include	"u.h"
+#include	"../port/lib.h"
+#include	"mem.h"
+#include	"dat.h"
+#include	"fns.h"
+#include	"io.h"
+#include	"../port/error.h"
+
+#include	"../port/netif.h"
+
+/*
+ *  Driver for the uart.
+ */
+enum
+{
+	/*
+	 *  register numbers
+	 */
+	Data=	0,		/* xmit/rcv buffer */
+	Iena=	1,		/* interrupt enable */
+	 Ircv=	(1<<0),		/*  for char rcv'd */
+	 Ixmt=	(1<<1),		/*  for xmit buffer empty */
+	 Irstat=(1<<2),		/*  for change in rcv'er status */
+	 Imstat=(1<<3),		/*  for change in modem status */
+	Istat=	2,		/* interrupt flag (read) */
+	 Ipend=	1,		/* interrupt pending (not) */
+	 Fenabd=(3<<6),   		/*  on if fifo's enabled */
+	Fifoctl=2,		/* fifo control (write) */
+	 Fena=	(1<<0),		/*  enable xmit/rcv fifos */
+	 Fdma=	(1<<3),		/* dma on */
+	 Ftrig=	(1<<6),		/*  trigger after 4 input characters */
+	 Fclear=(3<<1),		/*  clear xmit & rcv fifos */
+	Format=	3,		/* byte format */
+	 Bits8=	(3<<0),		/*  8 bits/byte */
+	 Stop2=	(1<<2),		/*  2 stop bits */
+	 Pena=	(1<<3),		/*  generate parity */
+	 Peven=	(1<<4),		/*  even parity */
+	 Pforce=(1<<5),		/*  force parity */
+	 Break=	(1<<6),		/*  generate a break */
+	 Dra=	(1<<7),		/*  address the divisor */
+	Mctl=	4,		/* modem control */
+	 Dtr=	(1<<0),		/*  data terminal ready */
+	 Rts=	(1<<1),		/*  request to send */
+	 Ri=	(1<<2),		/*  ring */
+	 Inton=	(1<<3),		/*  turn on interrupts */
+	 Loop=	(1<<4),		/*  loop back */
+	Lstat=	5,		/* line status */
+	 Inready=(1<<0),	/*  receive buffer full */
+	 Oerror=(1<<1),		/*  receiver overrun */
+	 Perror=(1<<2),		/*  receiver parity error */
+	 Ferror=(1<<3),		/*  rcv framing error */
+	 Berror=(1<<4),		/* break alarm */
+	 Outready=(1<<5),	/*  output buffer full */
+	Mstat=	6,		/* modem status */
+	 Ctsc=	(1<<0),		/*  clear to send changed */
+	 Dsrc=	(1<<1),		/*  data set ready changed */
+	 Rire=	(1<<2),		/*  rising edge of ring indicator */
+	 Dcdc=	(1<<3),		/*  data carrier detect changed */
+	 Cts=	(1<<4),		/*  complement of clear to send line */
+	 Dsr=	(1<<5),		/*  complement of data set ready line */
+	 Ringl=	(1<<6),		/*  complement of ring indicator line */
+	 Dcd=	(1<<7),		/*  complement of data carrier detect line */
+	Scratch=7,		/* scratchpad */
+	Dlsb=	0,		/* divisor lsb */
+	Dmsb=	1,		/* divisor msb */
+
+	CTLS= 023,
+	CTLQ= 021,
+
+	Stagesize= 1024,
+	Nuart=	2,		/* max per machine */
+};
+
+typedef struct Uart Uart;
+struct Uart
+{
+	QLock;
+	int	opens;
+
+	int	enabled;
+	Uart	*elist;			/* next enabled interface */
+	char	name[KNAMELEN];
+
+	uchar	sticky[8];		/* sticky write register values */
+	void*	regs;
+	ulong	port;
+	ulong	freq;			/* clock frequency */
+	uchar	mask;			/* bits/char */
+	int	dev;
+	int	baud;			/* baud rate */
+
+	uchar	istat;			/* last istat read */
+	int	frame;			/* framing errors */
+	int	overrun;		/* rcvr overruns */
+
+	/* buffers */
+	int	(*putc)(Queue*, int);
+	Queue	*iq;
+	Queue	*oq;
+
+	Lock	flock;			/* fifo */
+	uchar	fifoon;			/* fifo's enabled */
+	uchar	nofifo;			/* earlier chip version with nofifo */
+
+	Lock	rlock;			/* receive */
+	uchar	istage[Stagesize];
+	uchar	*ip;
+	uchar	*ie;
+
+	int	haveinput;
+
+	Lock	tlock;			/* transmit */
+	uchar	ostage[Stagesize];
+	uchar	*op;
+	uchar	*oe;
+
+	int	modem;			/* hardware flow control on */
+	int	xonoff;			/* software flow control on */
+	int	blocked;
+	int	cts, dsr, dcd;		/* keep track of modem status */ 
+	int	ctsbackoff;
+	int	hup_dsr, hup_dcd;	/* send hangup upstream? */
+	int	dohup;
+
+	Rendez	r;
+};
+
+static Uart *uart[Nuart];
+static int nuart;
+
+struct Uartalloc {
+	Lock;
+	Uart *elist;	/* list of enabled interfaces */
+} uartalloc;
+
+static void uartintr(Uart*);
+
+/*
+ *  pick up architecture specific routines and definitions
+ */
+#include "uart.h"
+
+/*
+ *  set the baud rate by calculating and setting the baudrate
+ *  generator constant.  This will work with fairly non-standard
+ *  baud rates.
+ */
+static void
+uartsetbaud(Uart *p, int rate)
+{
+	ulong brconst;
+
+	if(rate <= 0)
+		return;
+
+	p->freq = archuartclock(p->port, rate);
+	if(p->freq == 0)
+		return;
+
+	brconst = (p->freq+8*rate-1)/(16*rate);
+
+	uartwrreg(p, Format, Dra);
+	uartwr(p, Dmsb, (brconst>>8) & 0xff);
+	uartwr(p, Dlsb, brconst & 0xff);
+	uartwrreg(p, Format, 0);
+
+	p->baud = rate;
+}
+
+/*
+ * decide if we should hangup when dsr or dcd drops.
+ */
+static void
+uartdsrhup(Uart *p, int n)
+{
+	p->hup_dsr = n;
+}
+
+static void
+uartdcdhup(Uart *p, int n)
+{
+	p->hup_dcd = n;
+}
+
+static void
+uartparity(Uart *p, char type)
+{
+	switch(type){
+	case 'e':
+		p->sticky[Format] |= Pena|Peven;
+		break;
+	case 'o':
+		p->sticky[Format] &= ~Peven;
+		p->sticky[Format] |= Pena;
+		break;
+	default:
+		p->sticky[Format] &= ~(Pena|Peven);
+		break;
+	}
+	uartwrreg(p, Format, 0);
+}
+
+/*
+ *  set bits/character, default 8
+ */
+void
+uartbits(Uart *p, int bits)
+{
+	if(bits < 5 || bits > 8)
+		error(Ebadarg);
+
+	p->sticky[Format] &= ~3;
+	p->sticky[Format] |= bits-5;
+
+	uartwrreg(p, Format, 0);
+}
+
+
+/*
+ *  toggle DTR
+ */
+void
+uartdtr(Uart *p, int n)
+{
+	if(n)
+		p->sticky[Mctl] |= Dtr;
+	else
+		p->sticky[Mctl] &= ~Dtr;
+
+	uartwrreg(p, Mctl, 0);
+}
+
+/*
+ *  toggle RTS
+ */
+void
+uartrts(Uart *p, int n)
+{
+	if(n)
+		p->sticky[Mctl] |= Rts;
+	else
+		p->sticky[Mctl] &= ~Rts;
+
+	uartwrreg(p, Mctl, 0);
+}
+
+/*
+ *  send break
+ */
+static void
+uartbreak(Uart *p, int ms)
+{
+	if(ms == 0)
+		ms = 200;
+
+	uartwrreg(p, Format, Break);
+	tsleep(&up->sleep, return0, 0, ms);
+	uartwrreg(p, Format, 0);
+}
+
+static void
+uartfifoon(Uart *p)
+{
+	ulong i, x;
+
+	if(p->nofifo || uartrdreg(p, Istat) & Fenabd)
+		return;
+
+	x = splhi();
+
+	/* reset fifos */
+	p->sticky[Fifoctl] = 0;
+	uartwrreg(p, Fifoctl, Fclear);
+
+	/* empty buffer and interrupt conditions */
+	for(i = 0; i < 16; i++){
+		if(uartrdreg(p, Istat)){
+			/* nothing to do */
+		}
+		if(uartrdreg(p, Data)){
+			/* nothing to do */
+		}
+	}
+
+	/* turn on fifo */
+	p->fifoon = 1;
+	p->sticky[Fifoctl] = Fena|Ftrig;
+	uartwrreg(p, Fifoctl, 0);
+	p->istat = uartrdreg(p, Istat);
+	if((p->istat & Fenabd) == 0) {
+		/* didn't work, must be an earlier chip type */
+		p->nofifo = 1;
+	}
+
+	splx(x);
+}
+
+/*
+ *  modem flow control on/off (rts/cts)
+ */
+static void
+uartmflow(Uart *p, int n)
+{
+	ilock(&p->tlock);
+	if(n){
+		p->sticky[Iena] |= Imstat;
+		uartwrreg(p, Iena, 0);
+		p->modem = 1;
+		p->cts = uartrdreg(p, Mstat) & Cts;
+	} else {
+		p->sticky[Iena] &= ~Imstat;
+		uartwrreg(p, Iena, 0);
+		p->modem = 0;
+		p->cts = 1;
+	}
+	iunlock(&p->tlock);
+
+//	ilock(&p->flock);
+//	if(1)
+//		/* turn on fifo's */
+//		uartfifoon(p);
+//	else {
+//		/* turn off fifo's */
+//		p->fifoon = 0;
+//		p->sticky[Fifoctl] = 0;
+//		uartwrreg(p, Fifoctl, Fclear);
+//	}
+//	iunlock(&p->flock);
+}
+
+/*
+ *  turn on a port's interrupts.  set DTR and RTS
+ */
+static void
+uartenable(Uart *p)
+{
+	Uart **l;
+
+	if(p->enabled)
+		return;
+
+	uartportpower(p, 1);
+
+	p->hup_dsr = p->hup_dcd = 0;
+	p->cts = p->dsr = p->dcd = 0;
+
+	/*
+ 	 *  turn on interrupts
+	 */
+	p->sticky[Iena] = Ircv | Ixmt | Irstat;
+	uartwrreg(p, Iena, 0);
+
+	/*
+	 *  turn on DTR and RTS
+	 */
+	uartdtr(p, 1);
+	uartrts(p, 1);
+
+	uartfifoon(p);
+
+	/*
+	 *  assume we can send
+	 */
+	ilock(&p->tlock);
+	p->cts = 1;
+	p->blocked = 0;
+	iunlock(&p->tlock);
+
+	/*
+	 *  set baud rate to the last used
+	 */
+	uartsetbaud(p, p->baud);
+
+	lock(&uartalloc);
+	for(l = &uartalloc.elist; *l; l = &(*l)->elist){
+		if(*l == p)
+			break;
+	}
+	if(*l == 0){
+		p->elist = uartalloc.elist;
+		uartalloc.elist = p;
+	}
+	p->enabled = 1;
+	unlock(&uartalloc);
+}
+
+/*
+ *  turn off a port's interrupts.  reset DTR and RTS
+ */
+static void
+uartdisable(Uart *p)
+{
+	Uart **l;
+
+	/*
+ 	 *  turn off interrupts
+	 */
+	p->sticky[Iena] = 0;
+	uartwrreg(p, Iena, 0);
+
+	/*
+	 *  revert to default settings
+	 */
+	p->sticky[Format] = Bits8;
+	uartwrreg(p, Format, 0);
+
+	/*
+	 *  turn off DTR, RTS, hardware flow control & fifo's
+	 */
+	uartdtr(p, 0);
+	uartrts(p, 0);
+	uartmflow(p, 0);
+	ilock(&p->tlock);
+	p->xonoff = p->blocked = 0;
+	iunlock(&p->tlock);
+
+	uartportpower(p, 0);
+
+	lock(&uartalloc);
+	for(l = &uartalloc.elist; *l; l = &(*l)->elist){
+		if(*l == p){
+			*l = p->elist;
+			break;
+		}
+	}
+	p->enabled = 0;
+	unlock(&uartalloc);
+}
+
+/*
+ *  put some bytes into the local queue to avoid calling
+ *  qconsume for every character
+ */
+static int
+stageoutput(Uart *p)
+{
+	int n;
+
+	n = qconsume(p->oq, p->ostage, Stagesize);
+	if(n <= 0)
+		return 0;
+	p->op = p->ostage;
+	p->oe = p->ostage + n;
+	return n;
+}
+
+/*
+ *  (re)start output
+ */
+static void
+uartkick0(Uart *p)
+{
+	int i;
+	if((p->modem && (p->cts == 0)) || p->blocked)
+		return;
+
+	/*
+	 *  128 here is an arbitrary limit to make sure
+	 *  we don't stay in this loop too long.  If the
+	 *  chips output queue is longer than 128, too
+	 *  bad -- presotto
+	 */
+	for(i = 0; i < 128; i++){
+		if(!(uartrdreg(p, Lstat) & Outready))
+			break;
+		if(p->op >= p->oe && stageoutput(p) == 0)
+			break;
+		uartwr(p, Data, *p->op++);
+	}
+}
+
+static void
+uartkick(void *v)
+{
+	Uart *p;
+
+	p = v;
+	ilock(&p->tlock);
+	uartkick0(p);
+	iunlock(&p->tlock);
+}
+
+/*
+ *  restart input if it's off
+ */
+static void
+uartflow(void *v)
+{
+	Uart *p;
+
+	p = v;
+	if(p->modem)
+		uartrts(p, 1);
+	ilock(&p->rlock);
+	p->haveinput = 1;
+	iunlock(&p->rlock);
+}
+
+/*
+ *  default is 9600 baud, 1 stop bit, 8 bit chars, no interrupts,
+ *  transmit and receive enabled, interrupts disabled.
+ */
+static void
+uartsetup0(Uart *p)
+{
+	memset(p->sticky, 0, sizeof(p->sticky));
+	/*
+	 *  set rate to 9600 baud.
+	 *  8 bits/character.
+	 *  1 stop bit.
+	 *  interrupts enabled.
+	 */
+	p->sticky[Format] = Bits8;
+	uartwrreg(p, Format, 0);
+	p->sticky[Mctl] |= Inton;
+	uartwrreg(p, Mctl, 0x0);
+
+	uartsetbaud(p, 9600);
+
+	p->iq = qopen(4*1024, 0, uartflow, p);
+	p->oq = qopen(4*1024, 0, uartkick, p);
+	if(p->iq == nil || p->oq == nil)
+		panic("uartsetup0");
+
+	p->ip = p->istage;
+	p->ie = &p->istage[Stagesize];
+	p->op = p->ostage;
+	p->oe = p->ostage;
+}
+
+/*
+ *  called by uartinstall() to create a new duart
+ */
+void
+uartsetup(ulong port, void *regs, ulong freq, char *name)
+{
+	Uart *p;
+
+	if(nuart >= Nuart)
+		return;
+
+	p = xalloc(sizeof(Uart));
+	uart[nuart] = p;
+	strcpy(p->name, name);
+	p->dev = nuart;
+	nuart++;
+	p->port = port;
+	p->regs = regs;
+	p->freq = freq;
+	uartsetup0(p);
+}
+
+/*
+ *  called by main() to configure a duart port as a console or a mouse
+ */
+void
+uartspecial(int port, int baud, Queue **in, Queue **out, int (*putc)(Queue*, int))
+{
+	Uart *p = uart[port];
+	uartenable(p);
+	if(baud)
+		uartsetbaud(p, baud);
+	p->putc = putc;
+	if(in)
+		*in = p->iq;
+	if(out)
+		*out = p->oq;
+	p->opens++;
+}
+
+/*
+ *  handle an interrupt to a single uart
+ */
+static void
+uartintr(Uart *p)
+{
+	uchar ch;
+	int s, l;
+
+	for (s = uartrdreg(p, Istat); !(s&Ipend); s = uartrdreg(p, Istat)) {
+		switch(s&0x3f){
+		case 4:	/* received data available */
+		case 6:	/* receiver line status (alarm or error) */
+		case 12:	/* character timeout indication */
+			while ((l = uartrdreg(p, Lstat)) & Inready) {
+				if(l & Ferror)
+					p->frame++;
+				if(l & Oerror)
+					p->overrun++;
+				ch = uartrdreg(p, Data) & 0xff;
+				if (l & (Berror|Perror|Ferror)) {
+					/* ch came with break, parity or framing error - consume */
+					continue;
+				}
+				if (ch == CTLS || ch == CTLQ) {
+					ilock(&p->tlock);
+					if(p->xonoff){
+						if(ch == CTLS)
+							p->blocked = 1;
+						else
+							p->blocked = 0;	/* clock gets output going again */
+					}
+					iunlock(&p->tlock);
+				}
+				if(p->putc)
+					p->putc(p->iq, ch);
+				else {
+					ilock(&p->rlock);
+					if(p->ip < p->ie)
+						*p->ip++ = ch;
+					else
+						p->overrun++;
+					p->haveinput = 1;
+					iunlock(&p->rlock);
+				}
+			}
+			break;
+
+		case 2:	/* transmitter not full */
+			uartkick(p);
+			break;
+
+		case 0:	/* modem status */
+			ch = uartrdreg(p, Mstat);
+			if(ch & Ctsc){
+				ilock(&p->tlock);
+				l = p->cts;
+				p->cts = ch & Cts;
+				if(l == 0 && p->cts)
+					p->ctsbackoff = 2; /* clock gets output going again */
+				iunlock(&p->tlock);
+			}
+	 		if (ch & Dsrc) {
+				l = ch & Dsr;
+				if(p->hup_dsr && p->dsr && !l){
+					ilock(&p->rlock);
+					p->dohup = 1;
+					iunlock(&p->rlock);
+				}
+				p->dsr = l;
+			}
+	 		if (ch & Dcdc) {
+				l = ch & Dcd;
+				if(p->hup_dcd && p->dcd && !l){
+					ilock(&p->rlock);
+					p->dohup = 1;
+					iunlock(&p->rlock);
+				}
+				p->dcd = l;
+			}
+			break;
+
+		default:
+			iprint("weird uart interrupt #%2.2ux\n", s);
+			break;
+		}
+	}
+	p->istat = s;
+}
+
+/*
+ *  we save up input characters till clock time
+ *
+ *  There's also a bit of code to get a stalled print going.
+ *  It shouldn't happen, but it does.  Obviously I don't
+ *  understand something.  Since it was there, I bundled a
+ *  restart after flow control with it to give some hysteresis
+ *  to the hardware flow control.  This makes compressing
+ *  modems happier but will probably bother something else.
+ *	 -- presotto
+ */
+void
+uartclock(void)
+{
+	int n;
+	Uart *p;
+
+	for(p = uartalloc.elist; p; p = p->elist){
+
+		/* this amortizes cost of qproduce to many chars */
+		if(p->haveinput){
+			ilock(&p->rlock);
+			if(p->haveinput){
+				n = p->ip - p->istage;
+				if(n > 0 && p->iq){
+					if(n > Stagesize)
+						panic("uartclock");
+					if(qproduce(p->iq, p->istage, n) < 0)
+						uartrts(p, 0);
+					else
+						p->ip = p->istage;
+				}
+				p->haveinput = 0;
+			}
+			iunlock(&p->rlock);
+		}
+		if(p->dohup){
+			ilock(&p->rlock);
+			if(p->dohup){
+				qhangup(p->iq, 0);
+				qhangup(p->oq, 0);
+			}
+			p->dohup = 0;
+			iunlock(&p->rlock);
+		}
+
+		/* this adds hysteresis to hardware flow control */
+		if(p->ctsbackoff){
+			ilock(&p->tlock);
+			if(p->ctsbackoff){
+				if(--(p->ctsbackoff) == 0)
+					uartkick0(p);
+			}
+			iunlock(&p->tlock);
+		}
+	}
+}
+
+Dirtab *uartdir;
+int ndir;
+
+static void
+setlength(int i)
+{
+	Uart *p;
+
+	if(i >= 0){
+		p = uart[i];
+		if(p && p->opens && p->iq)
+			uartdir[1+3*i].length = qlen(p->iq);
+	} else for(i = 0; i < nuart; i++){
+		p = uart[i];
+		if(p && p->opens && p->iq)
+			uartdir[1+3*i].length = qlen(p->iq);
+	}
+}
+
+/*
+ *  all uarts must be uartsetup() by this point or inside of uartinstall()
+ */
+static void
+uartreset(void)
+{
+	int i;
+	Dirtab *dp;
+	uartinstall();	/* architecture specific */
+
+	ndir = 1+3*nuart;
+	uartdir = xalloc(ndir * sizeof(Dirtab));
+	dp = uartdir;
+	strcpy(dp->name, ".");
+	mkqid(&dp->qid, 0, 0, QTDIR);
+	dp->length = 0;
+	dp->perm = DMDIR|0555;
+	dp++;
+	for(i = 0; i < nuart; i++){
+		/* 3 directory entries per port */
+		strcpy(dp->name, uart[i]->name);
+		dp->qid.path = NETQID(i, Ndataqid);
+		dp->perm = 0666;
+		dp++;
+		sprint(dp->name, "%sctl", uart[i]->name);
+		dp->qid.path = NETQID(i, Nctlqid);
+		dp->perm = 0666;
+		dp++;
+		sprint(dp->name, "%sstatus", uart[i]->name);
+		dp->qid.path = NETQID(i, Nstatqid);
+		dp->perm = 0444;
+		dp++;
+	}
+}
+
+static Chan*
+uartattach(char *spec)
+{
+	return devattach('t', spec);
+}
+
+static Walkqid*
+uartwalk(Chan *c, Chan *nc, char **name, int nname)
+{
+	return devwalk(c, nc, name, nname, uartdir, ndir, devgen);
+}
+
+static int
+uartstat(Chan *c, uchar *dp, int n)
+{
+	if(NETTYPE(c->qid.path) == Ndataqid)
+		setlength(NETID(c->qid.path));
+	return devstat(c, dp, n, uartdir, ndir, devgen);
+}
+
+static Chan*
+uartopen(Chan *c, int omode)
+{
+	Uart *p;
+
+	c = devopen(c, omode, uartdir, ndir, devgen);
+
+	switch(NETTYPE(c->qid.path)){
+	case Nctlqid:
+	case Ndataqid:
+		p = uart[NETID(c->qid.path)];
+		qlock(p);
+		if(p->opens++ == 0){
+			uartenable(p);
+			qreopen(p->iq);
+			qreopen(p->oq);
+		}
+		qunlock(p);
+		break;
+	}
+
+	return c;
+}
+
+static void
+uartclose(Chan *c)
+{
+	Uart *p;
+
+	if(c->qid.type & QTDIR)
+		return;
+	if((c->flag & COPEN) == 0)
+		return;
+	switch(NETTYPE(c->qid.path)){
+	case Ndataqid:
+	case Nctlqid:
+		p = uart[NETID(c->qid.path)];
+		qlock(p);
+		if(--(p->opens) == 0){
+			uartdisable(p);
+			qclose(p->iq);
+			qclose(p->oq);
+			p->ip = p->istage;
+			p->dcd = p->dsr = p->dohup = 0;
+		}
+		qunlock(p);
+		break;
+	}
+}
+
+static long
+uartstatus(Chan*, Uart *p, void *buf, long n, long offset)
+{
+	uchar mstat, fstat, istat, tstat;
+	char str[256];
+
+	str[0] = 0;
+	tstat = p->sticky[Mctl];
+	mstat = uartrdreg(p, Mstat);
+	istat = p->sticky[Iena];
+	fstat = p->sticky[Format];
+	snprint(str, sizeof str,
+		"b%d c%d d%d e%d l%d m%d p%c r%d s%d\n"
+		"%d %d %d%s%s%s%s%s\n",
+
+		p->baud,
+		p->hup_dcd, 
+		(tstat & Dtr) != 0,
+		p->hup_dsr,
+		(fstat & Bits8) + 5,
+		(istat & Imstat) != 0, 
+		(fstat & Pena) ? ((fstat & Peven) ? 'e' : 'o') : 'n',
+		(tstat & Rts) != 0,
+		(fstat & Stop2) ? 2 : 1,
+
+		p->dev,
+		p->frame,
+		p->overrun, 
+		uartrdreg(p, Istat) & Fenabd       ? " fifo" : "",
+		(mstat & Cts)    ? " cts"  : "",
+		(mstat & Dsr)    ? " dsr"  : "",
+		(mstat & Dcd)    ? " dcd"  : "",
+		(mstat & Ringl)   ? " ring" : ""
+	);
+	return readstr(offset, buf, n, str);
+}
+
+static long
+uartread(Chan *c, void *buf, long n, vlong off)
+{
+	Uart *p;
+	ulong offset = off;
+
+	if(c->qid.type & QTDIR){
+		setlength(-1);
+		return devdirread(c, buf, n, uartdir, ndir, devgen);
+	}
+
+	p = uart[NETID(c->qid.path)];
+	switch(NETTYPE(c->qid.path)){
+	case Ndataqid:
+		return qread(p->iq, buf, n);
+	case Nctlqid:
+		return readnum(offset, buf, n, NETID(c->qid.path), NUMSIZE);
+	case Nstatqid:
+		return uartstatus(c, p, buf, n, offset);
+	}
+
+	return 0;
+}
+
+static void
+uartctl(Uart *p, char *cmd)
+{
+	int i, n;
+
+	/* let output drain for a while */
+	for(i = 0; i < 16 && qlen(p->oq); i++)
+		tsleep(&p->r, (int(*)(void*))qlen, p->oq, 125);
+
+	if(strncmp(cmd, "break", 5) == 0){
+		uartbreak(p, 0);
+		return;
+	}
+
+
+	n = atoi(cmd+1);
+	switch(*cmd){
+	case 'B':
+	case 'b':
+		uartsetbaud(p, n);
+		break;
+	case 'C':
+	case 'c':
+		uartdcdhup(p, n);
+		break;
+	case 'D':
+	case 'd':
+		uartdtr(p, n);
+		break;
+	case 'E':
+	case 'e':
+		uartdsrhup(p, n);
+		break;
+	case 'f':
+	case 'F':
+		qflush(p->oq);
+		break;
+	case 'H':
+	case 'h':
+		qhangup(p->iq, 0);
+		qhangup(p->oq, 0);
+		break;
+	case 'L':
+	case 'l':
+		uartbits(p, n);
+		break;
+	case 'm':
+	case 'M':
+		uartmflow(p, n);
+		break;
+	case 'n':
+	case 'N':
+		qnoblock(p->oq, n);
+		break;
+	case 'P':
+	case 'p':
+		uartparity(p, *(cmd+1));
+		break;
+	case 'K':
+	case 'k':
+		uartbreak(p, n);
+		break;
+	case 'R':
+	case 'r':
+		uartrts(p, n);
+		break;
+	case 'Q':
+	case 'q':
+		qsetlimit(p->iq, n);
+		qsetlimit(p->oq, n);
+		break;
+	case 'W':
+	case 'w':
+		/* obsolete */
+		break;
+	case 'X':
+	case 'x':
+		ilock(&p->tlock);
+		p->xonoff = n;
+		iunlock(&p->tlock);
+		break;
+	}
+}
+
+static long
+uartwrite(Chan *c, void *buf, long n, vlong)
+{
+	Uart *p;
+	char cmd[32];
+
+	if(c->qid.type & QTDIR)
+		error(Eperm);
+
+	p = uart[NETID(c->qid.path)];
+
+	/*
+	 *  The fifo's turn themselves off sometimes.
+	 *  It must be something I don't understand. -- presotto
+	 */
+	lock(&p->flock);
+	if((p->istat & Fenabd) == 0 && p->fifoon && p->nofifo == 0)
+		uartfifoon(p);
+	unlock(&p->flock);
+
+	switch(NETTYPE(c->qid.path)){
+	case Ndataqid:
+		return qwrite(p->oq, buf, n);
+	case Nctlqid:
+		if(n >= sizeof(cmd))
+			n = sizeof(cmd)-1;
+		memmove(cmd, buf, n);
+		cmd[n] = 0;
+		uartctl(p, cmd);
+		return n;
+	}
+}
+
+static int
+uartwstat(Chan *c, uchar *dp, int n)
+{
+	Dir d;
+	Dirtab *dt;
+
+	if(!iseve())
+		error(Eperm);
+	if(c->qid.type & QTDIR)
+		error(Eperm);
+	if(NETTYPE(c->qid.path) == Nstatqid)
+		error(Eperm);
+
+	dt = &uartdir[1+3 * NETID(c->qid.path)];
+	n = convM2D(dp, n, &d, nil);
+	if(n == 0)
+		error(Eshortstat);
+	if(d.mode != ~0UL){
+		d.mode &= 0666;
+		dt[0].perm = dt[1].perm = d.mode;
+	}
+	return n;
+}
+
+Dev uartdevtab = {
+	't',
+	"uart",
+
+	uartreset,
+	devinit,
+	devshutdown,
+	uartattach,
+	uartwalk,
+	uartstat,
+	uartopen,
+	devcreate,
+	uartclose,
+	uartread,
+	devbread,
+	uartwrite,
+	devbwrite,
+	devremove,
+	uartwstat,
+};
--- /dev/null
+++ b/os/cerf405/etheremac.c
@@ -1,0 +1,829 @@
+/*
+ * ethernet
+ */
+
+#include "u.h"
+#include "lib.h"
+#include "mem.h"
+#include "dat.h"
+#include "fns.h"
+#include "io.h"
+#include "../port/error.h"
+#include "../port/netif.h"
+
+#include "ethermii.h"
+#include "etherif.h"
+#include "ureg.h"
+
+/*
+ * TO DO:
+ *	- test EMAC1
+ */
+
+#define	DBG	if(0)iprint
+#define	MIIDBG	if(0)iprint
+
+enum {
+	Nrdre		= 64,	/* receive descriptor ring entries */
+	Ntdre		= 32,	/* transmit descriptor ring entries */
+	Nrxchan		= 2,
+	Ntxchan		= 2,	/* there are actually 4 but we only use 2 now */
+
+	Rbsize		= ETHERMAXTU,		/* ring buffer size */
+	Bufsize		= (Rbsize+CACHELINESZ-1)&~(CACHELINESZ-1),	/* aligned */
+};
+
+enum {
+	/* emac-specific Rx BD bits */
+	RxOverrun=	1<<9,	/* not enough empty space in FIFO */
+	RxPause=		1<<8,	/* control pause packet */
+	RxBad=		1<<7,	/* packet error */
+	RxRunt=		1<<6,
+	RxShort=		1<<5,
+	RxAlign=		1<<4,
+	RxFCS=		1<<3,
+	RxLong=		1<<2,
+	RxRange=		1<<1,	/* out of range error */
+	RxInRange=	1<<0,	/* in range error */
+	RxError=		(0x3FF & ~RxPause),	/* error flags */
+
+	/* emac-specific Tx BD bits */
+	/* write access */
+	TxFCS=		1<<9,	/* generate FCS */
+	TxPad=		1<<8,	/* pad short frames */
+	TxInsSA=		1<<7,	/* insert source address */
+	TxRepSA=		1<<6,	/* replace source address */
+	TxInsVLAN=	1<<5,	/* insert VLAN tag */
+	TxRepVLAN=	1<<4,	/* replace VLAN tag */
+
+	/* read access (status) */
+	TxBadFCS=	1<<9,
+	TxBadPrev=	1<<8,	/* bad previous packet in dependent mode */
+	TxLostCarrier=	1<<7,
+	TxEDef=		1<<6,	/* excessive deferral */
+	TxECol=		1<<5,	/* excessive collisions */
+	TxLateCol=	1<<4,	/* late collision (half-duplex only) */
+	TxManyCol=	1<<3,	/* more than 1 but less than 16 collisions */
+	TxCollision=	1<<2,	/* single collision */
+	TxUnderrun=	1<<1,	/* didn't fill FIFO in time */
+	TxSQE=		1<<0,	/* signal quality test failed (10mbit half-duplex only) */
+	TxError=		0x3FF,	/* error flags */
+};
+
+typedef struct Emac Emac;
+struct Emac {
+	ulong	mr0;		/* mode register 0 [see 19-48] */
+	ulong	mr1;		/* mode register 1 [Reset] */
+	ulong	tmr0;	/* transmit mode register 0 [see 19-28] */
+	ulong	tmr1;	/* transmit mode register 1 [see 19-28] */
+	ulong	rmr;		/* receive mode register [Reset] */
+	ulong	isr;		/* interrupt status register [Always] */
+	ulong	iser;		/* interrupt status enable register [Reset] */
+	ulong	iahr;		/* individual address high [Reset, R, T]*/
+	ulong	ialr;		/* individual address low [Reset, R, T] */
+	ulong	vtpid;	/* VLAN Tag Protocol Identifier [Reset, R, T] */
+	ulong	vtci;		/* VLAN Tag Control Information [Reset, R, T] */
+	ulong	ptr;		/* pause timer [Reset, T] */
+	ulong	iaht[4];	/* individual address hash table [Reset, R] */
+	ulong	gaht[4];	/* group address hash table [Reset, R] */
+	ulong	lsah;		/* last source address high */
+	ulong	lsal;		/* last source address low */
+	ulong	ipgvr;	/* inter-packet gap value [Reset, T] */
+	ulong	stacr;	/* STA control register [see 19-41] */
+	ulong	trtr;		/* transmit request threshold register [see 19-42] */
+	ulong	rwmr;	/* receive low/high water mark [Reset] */
+	ulong	octx;		/* bytes transmitted */
+	ulong	ocrx;	/* bytes received */
+};
+
+enum {
+	/* mode register 0 */
+	Mr0Rxi=	1<<31,	/* receive MAC idle */
+	Mr0Txi=	1<<30,	/* transmit MAC idle */
+	Mr0Srst=	1<<29,	/* soft reset; soft reset in progress */
+	Mr0Txe=	1<<28,	/* tx MAC enable */
+	Mr0Rxe=	1<<27,	/* rx MAC enable */
+	Mr0Wke=	1<<26,	/* enable wake-up packets */
+
+	/* mode register 1 */
+	Mr1Fde=	1<<31,	/* full-duplex enable */
+	Mr1Ile=	1<<30,	/* internal loop-back enable */
+	Mr1Vle=	1<<29,	/* VLAN enable */
+	Mr1Eifc=	1<<28,	/* enable integrated flow control */
+	Mr1App=	1<<27,	/* allow pause packets */
+	Mr1Ist=	1<<24,	/* ignore sqe test (all but half-duplex 10m/bit) */
+	Mr1Mf10=	0<<22,	/* medium [MII] frequency is 10 mbps */
+	Mr1Mf100=	1<<22,	/* medium frequency is 100 mbps */
+	Mr1Rfs512=	0<<20,	/* RX FIFO size (512 bytes) */
+	Mr1Rfs1024=	1<<20,
+	Mr1Rfs2048=	2<<20,
+	Mr1Rfs4096=	3<<20,
+	Mr1Tfs1024=	1<<18,	/* TX FIFO size (1024 bytes) */
+	Mr1Tfs2048=	2<<18,
+	Mr1Tr0sp=	0<<15,	/* transmit request 0: single packet */
+	Mr1Tr0mp=	1<<15,	/* multiple packets */
+	Mr1Tr0dm=	2<<15,	/* dependent mode */
+	Mr1Tr1sp=	0<<13,	/* transmit request 1: single packet */
+	Mr1Tr1mp=	1<<13,	/* multiple packets */
+	Mr1Tr1dm=	2<<13,	/* dependent mode */
+
+	/* transmit mode register 0 */
+	Tmr0Gnp0=	1<<31,	/* get new packet channel 0 */
+	Tmr0Gnp1=	1<<30,	/* get new packet channel 1 */
+	Tmr0Gnpd=	1<<29,	/* get new packet dependent mode */
+	Tmr0Fc=		1<<28,	/* first channel (dependent mode) */
+
+	/* transmit mode register 1 */
+	Tmr1Trl_s=	27,		/* transmit low request (shift) */
+	Tmr1Tur_s=	16,		/* transmit urgent request (shift) */
+
+	/* receive mode register */
+	RmrSp=		1<<31,	/* strip pad/FCS bytes */
+	RmrSfcs=		1<<30,	/* strip FCS */
+	RmrRrp=		1<<29,	/* receive runt packets */
+	RmrRfp=		1<<28,	/* receive packets with FCS error */
+	RmrRop=		1<<27,	/* receive oversize packets */
+	RmrRpir=		1<<26,	/* receive packets with in range error */
+	RmrPpp=		1<<25,	/* propagate pause packet */
+	RmrPme=		1<<24,	/* promiscuous mode enable */
+	RmrPmme=	1<<23,	/* promiscuous mode multicast enable */
+	RmrIae=		1<<22,	/* individual address enable */
+	RmrMiae=		1<<21,	/* multiple individual address enable */
+	RmrBae=		1<<20,	/* broadcast address enable */
+	RmrMae=		1<<19,	/* multicast address enable */
+
+	/* interrupt status register */
+	IsrOvr=		1<<25,	/* overrun error */
+	IsrPp=		1<<24,	/* pause packet */
+	IsrBp=		1<<23,	/* bad packet */
+	IsrRp=		1<<22,	/* runt packet */
+	IsrSe=		1<<21,	/* short event */
+	IsrAle=		1<<20,	/* alignment error */
+	IsrBfcs=		1<<19,	/* bad FCS */
+	IsrPtle=		1<<18,	/* packet too long error */
+	IsrOre=		1<<17,	/* out of range error */
+	IsrIre=		1<<16,	/* in range error */
+	IsrDbdm=		1<<9,	/* dead bit dependent mode */
+	IsrDb0=		1<<8,	/* dead bit 0 */
+	IsrSe0=		1<<7,	/* sqe 0 */
+	IsrTe0=		1<<6,	/* tx error 0 */
+	IsrDb1=		1<<5,	/* dead bit 1 */
+	IsrSe1=		1<<4,	/* sqe 1 */
+	IsrTe1=		1<<3,	/* tx error 1 */
+	IsrMos=		1<<1,	/* MMA operation succeeded */
+	IsrMof=		1<<0,	/* MMA operation failed */
+
+	/* STA control register */
+	StaOc=		1<<15,	/* operation complete */
+	StaPhye=		1<<14,	/* PHY error */
+	StaRead=		1<<12,	/* STA read */
+	StaWrite=		2<<12,	/* STA write */
+	StaOpb50=	0<<10,	/* OPB frequency */
+	StaOpb66=	1<<10,
+	StaOpb83=	2<<10,
+	StaOpb100=	3<<10,
+
+	/* transmit request threshold */
+	TrtrTrt_s=		27,	/* threshold (shift) -- and the value is (threshold/64)-1 */
+
+	/* receive low/high water mark register */
+	RwmrRlwm_s=	23,	/* low water mark (shift) */
+	RwmrRhwm_s=	7,	/* high water mark (shift) */
+};
+
+typedef struct {
+	Lock;
+	int	port;
+	int	init;
+	int	active;
+	Emac	*regs;
+	Emac	*miiregs;
+	Mal*	rx;
+	Mal*	tx;
+
+	Mii	*mii;
+
+	Ring;
+
+	ulong	interrupts;			/* statistics */
+	ulong	deferred;
+	ulong	heartbeat;
+	ulong	latecoll;
+	ulong	retrylim;
+	ulong	underrun;
+	ulong	overrun;
+	ulong	carrierlost;
+	ulong	retrycount;
+} Ctlr;
+
+static void dumpemac(Emac*);
+
+static void
+attach(Ether *ether)
+{
+	Ctlr *ctlr;
+
+	ctlr = ether->ctlr;
+	ilock(ctlr);
+	if(!ctlr->active){
+		malrxenable(ctlr->rx);
+		maltxenable(ctlr->tx);
+		eieio();
+		ctlr->regs->mr0 = Mr0Txe | Mr0Rxe;
+		eieio();
+		ctlr->active = 1;
+	}
+	iunlock(ctlr);
+}
+
+static void
+closed(Ether *ether)
+{
+	Ctlr *ctlr;
+
+	ctlr = ether->ctlr;
+	if(ctlr->active){
+		ilock(ctlr);
+iprint("ether closed\n");
+		ctlr->regs->mr0 &= ~(Mr0Txe | Mr0Rxe);	/* reset enable bits */
+		/* TO DO: reset ring */
+		/* TO DO: could wait */
+		ctlr->active = 0;
+		iunlock(ctlr);
+	}
+}
+
+static void
+promiscuous(void* arg, int on)
+{
+	Ether *ether;
+	Ctlr *ctlr;
+
+	ether = (Ether*)arg;
+	ctlr = ether->ctlr;
+
+	ilock(ctlr);
+	if(on || ether->nmaddr)
+		ctlr->regs->rmr |= RmrPme | RmrPmme;
+	else
+		ctlr->regs->rmr &= ~(RmrPme | RmrPmme);
+	iunlock(ctlr);
+}
+
+static void
+multicast(void* arg, uchar *addr, int on)
+{
+	Ether *ether;
+	Ctlr *ctlr;
+
+	USED(addr, on);	/* if on, could SetGroupAddress; if !on, it's hard */
+
+	ether = (Ether*)arg;
+	ctlr = ether->ctlr;
+
+	ilock(ctlr);
+	if(ether->prom || ether->nmaddr)
+		ctlr->regs->rmr |= RmrPmme;
+	else
+		ctlr->regs->rmr &= ~RmrPmme;
+	iunlock(ctlr);
+}
+
+static void
+txstart(Ether *ether)
+{
+	int len;
+	Ctlr *ctlr;
+	Block *b;
+	BD *dre;
+
+	ctlr = ether->ctlr;
+	while(ctlr->ntq < ctlr->ntdre-1){
+		b = qget(ether->oq);
+		if(b == 0)
+			break;
+
+		dre = &ctlr->tdr[ctlr->tdrh];
+		if(dre->status & BDReady)
+			panic("ether: txstart");
+
+		/*
+		 * Give ownership of the descriptor to the chip, increment the
+		 * software ring descriptor pointer and tell the chip to poll.
+		 */
+		len = BLEN(b);
+		if(ctlr->txb[ctlr->tdrh] != nil)
+			panic("etheremac: txstart");
+		ctlr->txb[ctlr->tdrh] = b;
+		dre->addr = PADDR(b->rp);
+		dre->length = len;
+		dcflush(b->rp, len);
+		eieio();
+		dre->status = (dre->status & BDWrap) | BDReady|BDInt|BDLast|TxFCS|TxPad;
+		eieio();
+		ctlr->regs->tmr0 = Tmr0Gnp0;	/* TO DO: several channels */
+		eieio();
+		ctlr->ntq++;
+		ctlr->tdrh = NEXT(ctlr->tdrh, ctlr->ntdre);
+	}
+}
+
+static void
+transmit(Ether* ether)
+{
+	Ctlr *ctlr;
+
+	ctlr = ether->ctlr;
+	ilock(ctlr);
+	txstart(ether);
+	iunlock(ctlr);
+}
+
+/*
+ * allocate receive buffer space on cache-line boundaries
+ */
+static Block*
+clallocb(void)
+{
+	Block *b;
+
+	b = iallocb(Bufsize+CACHELINESZ-1);
+	if(b == nil)
+		return b;
+	dcflush(b->base, BALLOC(b));
+	b->wp = b->rp = (uchar*)(((ulong)b->base + CACHELINESZ - 1) & ~(CACHELINESZ-1));
+	return b;
+}
+
+
+static void
+rxring(Ureg*, void *arg)
+{
+	Ether *ether;
+	ulong status;
+	Ctlr *ctlr;
+	BD *dre;
+	Block *b, *rb;
+
+	ether = arg;
+	ctlr = ether->ctlr;
+	ctlr->interrupts++;
+
+	/*
+	 * Receiver interrupt: run round the descriptor ring logging
+	 * errors and passing valid receive data up to the higher levels
+	 * until we encounter a descriptor still owned by the chip.
+	 * We rely on the descriptor accesses being uncached.
+	 */
+	dre = &ctlr->rdr[ctlr->rdrx];
+	while(((status = dre->status) & BDEmpty) == 0){
+		if(status & RxError || (status & (BDFirst|BDLast)) != (BDFirst|BDLast)){
+			if(status & (RxShort|RxLong))
+				ether->buffs++;
+			if(status & (RxBad|RxAlign|RxRange|RxInRange))
+				ether->frames++;
+			if(status & RxFCS)
+				ether->crcs++;
+			if(status & RxOverrun)
+				ether->overflows++;
+			iprint("eth rx: %lux\n", status);
+		}else if((status & RxPause) == 0){
+			/*
+			 * We have a packet. Read it in.
+			 */
+			b = clallocb();
+			if(b != nil){
+				rb = ctlr->rxb[ctlr->rdrx];
+				rb->wp += dre->length;
+				ctlr->rxb[ctlr->rdrx] = b;
+				ctlr->rdr[ctlr->rdrx].addr = PADDR(b->wp);
+				etheriq(ether, rb, 1);
+			}else
+				ether->soverflows++;
+		}
+
+		/*
+		 * Finished with this descriptor, reinitialise it,
+		 * give it back to the chip, then on to the next...
+		 */
+		dre->status = (status & BDWrap) | BDEmpty | BDInt;
+		eieio();
+
+		ctlr->rdrx = NEXT(ctlr->rdrx, ctlr->nrdre);
+		dre = &ctlr->rdr[ctlr->rdrx];
+	}
+}
+
+static void
+txring(Ureg*, void *arg)
+{
+	Ether *ether;
+	ulong status;
+	Ctlr *ctlr;
+	BD *dre;
+	Block *b;
+
+	ether = arg;
+	ctlr = ether->ctlr;
+	ctlr->interrupts++;
+
+	/*
+	 * Transmitter interrupt: handle anything queued for a free descriptor.
+	 */
+	lock(ctlr);
+	while(ctlr->ntq){
+		dre = &ctlr->tdr[ctlr->tdri];
+		status = dre->status;
+		if(status & BDReady)
+			break;
+		if(status & TxEDef)
+			ctlr->deferred++;
+		if(status & TxLateCol)
+			ctlr->latecoll++;
+		if(status & TxECol)
+			ctlr->retrylim++;
+		if(status & TxUnderrun)
+			ctlr->underrun++;
+		if(status & (TxManyCol|TxCollision))
+			ctlr->retrycount++;
+		b = ctlr->txb[ctlr->tdri];
+		if(b == nil)
+			panic("etheremac: bufp");
+		ctlr->txb[ctlr->tdri] = nil;
+		freeb(b);
+		ctlr->ntq--;
+		ctlr->tdri = NEXT(ctlr->tdri, ctlr->ntdre);
+	}
+	txstart(ether);
+	unlock(ctlr);
+}
+
+static void
+interrupt(Ureg*, void *arg)
+{
+	Ether *ether;
+	ulong events;
+	Ctlr *ctlr;
+
+	ether = arg;
+	ctlr = ether->ctlr;
+
+	events = ctlr->regs->isr;
+	eieio();
+	ctlr->regs->isr = events;
+	eieio();
+	ctlr->interrupts++;
+//iprint("eth: %8.8lux\n", events);
+	if(!ctlr->active || events == 0)
+		return;
+
+	if(events & IsrOvr)
+		ctlr->overrun++;
+	if(events & (IsrTe0|IsrTe1))
+		ether->oerrs++;
+
+	rxring(nil, arg);
+	txring(nil, arg);
+	ctlr->interrupts -= 2;
+
+	/* TO DO: restart tx/rx on error */
+}
+
+static long
+ifstat(Ether* ether, void* a, long n, ulong offset)
+{
+	char *p;
+	int len;
+	Ctlr *ctlr;
+
+	if(n == 0)
+		return 0;
+
+	ctlr = ether->ctlr;
+
+	p = malloc(READSTR);
+	len = snprint(p, READSTR, "interrupts: %lud\n", ctlr->interrupts);
+	len += snprint(p+len, READSTR-len, "carrierlost: %lud\n", ctlr->carrierlost);
+	len += snprint(p+len, READSTR-len, "heartbeat: %lud\n", ctlr->heartbeat);
+	len += snprint(p+len, READSTR-len, "retrylimit: %lud\n", ctlr->retrylim);
+	len += snprint(p+len, READSTR-len, "retrycount: %lud\n", ctlr->retrycount);
+	len += snprint(p+len, READSTR-len, "latecollisions: %lud\n", ctlr->latecoll);
+	len += snprint(p+len, READSTR-len, "rxoverruns: %lud\n", ctlr->overrun);
+	len += snprint(p+len, READSTR-len, "txunderruns: %lud\n", ctlr->underrun);
+	snprint(p+len, READSTR-len, "framesdeferred: %lud\n", ctlr->deferred);
+	n = readstr(offset, a, n, p);
+	free(p);
+
+	return n;
+}
+
+static QLock miilock;	/* the PHY are both on EMAC0's MII bus */
+
+static int
+miird(Mii *mii, int pa, int ra)
+{
+	Ctlr *ctlr;
+	Emac *em;
+	ulong r;
+	int i;
+
+	if(up)
+		qlock(&miilock);
+	ctlr = mii->ctlr;
+	em = ctlr->miiregs;
+	MIIDBG("r: %x.%x:", pa, ra);
+	if((em->stacr & StaOc) == 0)
+		iprint("mii-not oc\n");
+	em->stacr = StaRead | StaOpb66 | (pa<<5) | ra;
+	for(i=0; i<100 && (em->stacr & StaOc) == 0; i++)
+		microdelay(1);
+	r = em->stacr;
+	if(up)
+		qunlock(&miilock);
+	if((r & StaOc) == 0)
+		iprint("mii'-not oc\n");
+	if(r & StaPhye)
+		return -1;
+	MIIDBG(" %8.8lux\n", r);
+	return r >> 16;
+}
+
+static int
+miiwr(Mii *mii, int pa, int ra, int v)
+{
+	Ctlr *ctlr;
+	Emac *em;
+	ulong r;
+	int i;
+
+	if(up)
+		qlock(&miilock);
+	ctlr = mii->ctlr;
+	em = ctlr->miiregs;
+	if((em->stacr & StaOc) == 0)
+		iprint("miiw-not oc\n");
+	em->stacr = (v<<16) | StaWrite | StaOpb66 | (pa<<5) | ra;
+	for(i=0; i<100 && (em->stacr & StaOc) == 0; i++)
+		microdelay(1);
+	r = em->stacr;
+	if(up)
+		qunlock(&miilock);
+	if((r & StaOc) == 0)
+		iprint("miiw'-not oc\n");
+	if(r & StaPhye)
+		return -1;
+	MIIDBG("w: %x.%x: %8.8lux\n", pa, ra, r);
+	return 0;
+}
+
+static int
+emacmii(Ctlr *ctlr)
+{
+	MiiPhy *phy;
+	int i;
+
+	MIIDBG("mii\n");
+	if((ctlr->mii = malloc(sizeof(Mii))) == nil)
+		return -1;
+	ctlr->mii->ctlr = ctlr;
+	ctlr->mii->mir = miird;
+	ctlr->mii->miw = miiwr;
+
+	if(mii(ctlr->mii, 1<<(ctlr->port+1)) == 0 || (phy = ctlr->mii->curphy) == nil){
+		free(ctlr->mii);
+		ctlr->mii = nil;
+		return -1;
+	}
+
+	iprint("oui %X phyno %d\n", phy->oui, phy->phyno);
+	if(miistatus(ctlr->mii) < 0){
+
+		miireset(ctlr->mii);
+		MIIDBG("miireset\n");
+		if(miiane(ctlr->mii, ~0, 0, ~0) < 0){
+			iprint("miiane failed\n");
+			return -1;
+		}
+		MIIDBG("miistatus...\n");
+		miistatus(ctlr->mii);
+		if(miird(ctlr->mii, phy->phyno, Bmsr) & BmsrLs){
+			for(i=0;; i++){
+				if(i > 600){
+					iprint("emac%d: autonegotiation failed\n", ctlr->port);
+					break;
+				}
+				if(miird(ctlr->mii, phy->phyno, Bmsr) & BmsrAnc)
+					break;
+				delay(10);
+			}
+			if(miistatus(ctlr->mii) < 0)
+				iprint("miistatus failed\n");
+		}else{
+			iprint("emac%d: no link\n", ctlr->port);
+			phy->speed = 10;	/* simple default */
+		}
+	}
+
+	iprint("emac%d mii: fd=%d speed=%d tfc=%d rfc=%d\n", ctlr->port, phy->fd, phy->speed, phy->tfc, phy->rfc);
+
+	MIIDBG("mii done\n");
+
+	return 0;
+}
+
+static void
+emacsetup(Ctlr *ctlr, Ether *ether)
+{
+	int i;
+	Emac *em;
+	ulong mode;
+	MiiPhy *phy;
+
+	/* apparently don't need to set any Alt1 in GPIO */
+
+	em = ctlr->regs;
+
+	/* errata emac_8 */
+	if(em->mr0 & Mr0Rxe){	/* probably never happens in our config */
+		em->mr0 &= ~Mr0Rxe;
+		eieio();
+		for(i=0; (em->mr0 & Mr0Rxi) == 0; i++){
+			if(i > 100){
+				iprint("ethermac: Rxe->Rxi timed out\n");
+				break;	/* we'll try soft reset anyway */
+			}
+			microdelay(100);
+		}
+	}
+
+	/* soft reset */
+	em->mr0 = Mr0Srst;
+	eieio();
+	for(i=0; em->mr0 & Mr0Srst; i++){
+		if(i > 20){
+			iprint("ethermac: reset (PHY clocks not running?)");
+			i=0;
+		}
+		microdelay(100);
+	}
+iprint("%d: rx=%8.8lux tx=%8.8lux\n", ctlr->port, PADDR(ctlr->rdr), PADDR(ctlr->tdr));
+//if(ctlr->port)return;
+
+	malrxinit(ctlr->rx, ctlr, Bufsize/16);
+	maltxinit(ctlr->tx, ctlr);
+	malrxreset(ctlr->rx);
+	maltxreset(ctlr->tx);
+
+	em->mr0 = 0;
+	mode = Mr1Rfs4096 | Mr1Tfs2048 | Mr1Tr0mp;
+	if(ctlr->mii != nil && (phy = ctlr->mii->curphy) != nil){
+		if(phy->speed == 10){
+			mode |= Mr1Mf10;
+			if(phy->fd)
+				mode |= Mr1Ist;
+		}else
+			mode |= Mr1Mf100 | Mr1Ist;
+		if(phy->fd)
+			mode |= Mr1Fde;
+		/* errata emac_9 suggests not using integrated flow control (it's broken); so don't negotiate it */
+		if(0 && (phy->rfc || phy->tfc))
+			mode |= Mr1App | Mr1Eifc;
+		ether->mbps = phy->speed;
+		ether->fullduplex = phy->fd;
+	}else{
+		iprint("mii: didn't work: default 100FD\n");
+		mode |= Mr1Mf100 | Mr1Ist | Mr1Fde;
+		ether->mbps = 100;
+		ether->fullduplex = 1;
+	}
+		
+	em->mr1 = mode;
+	em->tmr1 = (9<<Tmr1Trl_s) | (256<<Tmr1Tur_s);	/* TO DO: validate these sizes */
+	em->rmr = RmrSp | RmrSfcs | RmrIae | RmrBae;
+	em->iahr = (ether->ea[0]<<8) | ether->ea[1];
+	em->ialr = (ether->ea[2]<<24) | (ether->ea[3]<<16) | (ether->ea[4]<<8) | ether->ea[5];
+	em->vtpid = 0;
+	em->vtci = 0;
+	em->ptr = 1;		/* pause timer [Reset, T] */
+	for(i=0; i<4; i++){
+		em->iaht[i] = 0;	/* individual address hash table */
+		em->gaht[i] = 0;	/* group address hash table */
+	}
+	em->ipgvr = (96/8)/3;	/* minimise bit times between packets */
+	em->trtr = ((256/64)-1)<<TrtrTrt_s;		/* transmission threshold (probably could be smaller) */
+	em->rwmr = (32<<RwmrRlwm_s) | (128<<RwmrRhwm_s);	/* receive low/high water mark (TO DO: check) */
+	/* 0x0f002000? */
+	//dumpemac(em);
+	//dumpmal();
+	eieio();
+	em->isr = em->isr;		/* clear all events */
+	eieio();
+	em->iser = IsrOvr | IsrBp | IsrSe | IsrSe0 | IsrTe0 | IsrSe1 | IsrTe1;	/* enable various error interrupts */
+	/* packet tx/rx interrupts come from MAL */
+	eieio();
+
+	/* tx/rx enable is deferred until attach */
+}
+
+static int
+reset(Ether* ether)
+{
+	uchar ea[Eaddrlen];
+	Ctlr *ctlr;
+	int i;
+
+	ioringreserve(Nrxchan, Nrdre, Ntxchan, Ntdre);
+
+	/*
+	 * Insist that the platform-specific code provide the Ethernet address
+	 */
+	memset(ea, 0, Eaddrlen);
+	if(memcmp(ea, ether->ea, Eaddrlen) == 0){
+		print("no ether address");
+		return -1;
+	}
+
+	ctlr = malloc(sizeof(*ctlr));
+	ctlr->port = ether->port;
+
+	switch(ether->port){
+	case 0:
+		ctlr->regs = KADDR(PHYSEMAC0);
+		ctlr->miiregs = ctlr->regs;
+		ctlr->rx = malchannel(0, 0, rxring, ether);
+		ctlr->tx = malchannel(0, 1, txring, ether);
+		ether->irq = VectorEMAC0;
+		break;
+	case 1:
+		ctlr->regs = KADDR(PHYSEMAC1);
+		ctlr->miiregs = KADDR(PHYSEMAC0);	/* p. 19-41: ``only the MDIO interface for EMAC0 is pinned out'' */
+		ctlr->rx = malchannel(1, 0, rxring, ether);
+		ctlr->tx = malchannel(2, 1, txring, ether);
+		ether->irq = VectorEMAC1;
+		break;
+	default:
+		print("%s ether: no port %lud\n", ether->type, ether->port);
+		free(ctlr);
+		return -1;
+	}
+
+	if(emacmii(ctlr) < 0){
+		free(ctlr);
+		return -1;
+	}
+
+	ether->ctlr = ctlr;
+
+	if(ioringinit(ctlr, Nrdre, Ntdre) < 0)	/* TO DO: there are two transmit rings*/
+		panic("etheremac initring");
+
+	for(i = 0; i < ctlr->nrdre; i++){
+		ctlr->rxb[i] = clallocb();
+		ctlr->rdr[i].addr = PADDR(ctlr->rxb[i]->wp);
+	}
+
+	emacsetup(ctlr, ether);
+
+	ether->attach = attach;
+	ether->closed = closed;
+	ether->transmit = transmit;
+	ether->interrupt = interrupt;	/* oddly, it's only error interrupts; see malchannel call above for tx/rx */
+	ether->ifstat = ifstat;
+
+	ether->arg = ether;
+	ether->promiscuous = promiscuous;
+	ether->multicast = multicast;
+
+	return 0;
+}
+
+void
+etheremaclink(void)
+{
+	addethercard("EMAC", reset);
+}
+
+static void
+dumpemac(Emac *r)
+{
+	iprint("mr0=%8.8lux\n", r->mr0);		/* mode register 0 [see 19-48] */
+	iprint("mr1=%8.8lux\n", r->mr1);		/* mode register 1 [Reset] */
+	iprint("tmr0=%8.8lux\n", r->tmr0);	/* transmit mode register 0 [see 19-28] */
+	iprint("tmr1=%8.8lux\n", r->tmr1);	/* transmit mode register 1 [see 19-28] */
+	iprint("rmr=%8.8lux\n", r->rmr);		/* receive mode register [Reset] */
+	iprint("isr=%8.8lux\n", r->isr);		/* interrupt status register [Always] */
+	iprint("iser=%8.8lux\n", r->iser);		/* interrupt status enable register [Reset] */
+	iprint("iahr=%8.8lux\n", r->iahr);		/* individual address high [Reset, R, T]*/
+	iprint("ialr=%8.8lux\n", r->ialr);		/* individual address low [Reset, R, T] */
+	iprint("vtpid=%8.8lux\n", r->vtpid);	/* VLAN Tag Protocol Identifier [Reset, R, T] */
+	iprint("vtci=%8.8lux\n", r->vtci);		/* VLAN Tag Control Information [Reset, R, T] */
+	iprint("ptr=%8.8lux\n", r->ptr);		/* pause timer [Reset, T] */
+	iprint("lsah=%8.8lux\n", r->lsah);		/* last source address high */
+	iprint("lsal=%8.8lux\n", r->lsal);		/* last source address low */
+	iprint("ipgvr=%8.8lux\n", r->ipgvr);	/* inter-packet gap value [Reset, T] */
+	iprint("stacr=%8.8lux\n", r->stacr);	/* STA control register [see 19-41] */
+	iprint("trtr=%8.8lux\n", r->trtr);		/* transmit request threshold register [see 19-42] */
+	iprint("rwmr=%8.8lux\n", r->rwmr);	/* receive low/high water mark [Reset] */
+	iprint("octx=%8.8lux\n", r->octx);		/* bytes transmitted */
+	iprint("ocrx=%8.8lux\n", r->ocrx);	/* bytes received */
+}
--- /dev/null
+++ b/os/cerf405/etherif.h
@@ -1,0 +1,37 @@
+enum {
+	MaxEther	= 4,
+	Ntypes		= 8,
+};
+
+typedef struct Ether Ether;
+struct Ether {
+RWlock;	/* TO DO */
+	ISAConf;			/* hardware info */
+	int	ctlrno;
+	int	tbdf;			/* type+busno+devno+funcno */
+	int	minmtu;
+	int	maxmtu;
+	uchar	ea[Eaddrlen];
+	int	encry;
+
+	void	(*attach)(Ether*);	/* filled in by reset routine */
+	void	(*closed)(Ether*);
+	void	(*detach)(Ether*);
+	void	(*transmit)(Ether*);
+	void	(*interrupt)(Ureg*, void*);
+	long	(*ifstat)(Ether*, void*, long, ulong);
+	long	(*ctl)(Ether*, void*, long); /* custom ctl messages */
+	void	(*power)(Ether*, int);	/* power on/off */
+	void	(*shutdown)(Ether*);	/* shutdown hardware before reboot */
+	void	*ctlr;
+	int	pcmslot;		/* PCMCIA */
+	int	fullduplex;	/* non-zero if full duplex */
+
+	Queue*	oq;
+
+	Netif;
+};
+
+extern Block* etheriq(Ether*, Block*, int);
+extern void addethercard(char*, int(*)(Ether*));
+extern int archether(int, Ether*);
--- /dev/null
+++ b/os/cerf405/fns.h
@@ -1,0 +1,147 @@
+#include "../port/portfns.h"
+
+void	addpower(Power*);
+void	archbacklight(int);
+void	archconfinit(void);
+int	archconfval(char**, char**, int);
+void	archdisableuart(int);
+void	archdisableusb(void);
+void	archdisablevideo(void);
+void	archenableuart(int, int);
+void	archenableusb(int, int);
+void	archenablevideo(void);
+void	archkbdinit(void);
+void	archresetvideo(void);
+void	archinit(void);
+int	archoptionsw(void);
+void	archreboot(void);
+ulong	archuartclock(int, int);
+void	archuartdma(int, int);
+void	clockcheck(void);
+void	clockinit(void);
+void	clockintr(Ureg*);
+void	clrfptrap(void);
+#define	coherence()		/* nothing needed for uniprocessor */
+void	compiledcr(void);
+void	cpuidprint(void);
+void*	dcflush(void*, ulong);
+void	dcinval(void*, ulong);
+void	delay(int);
+void	dtlbmiss(void);
+void	dumplongs(char*, ulong*, int);
+void	dumpregs(Ureg*);
+void	eieio(void);
+void	firmware(int);
+void	fpinit(void);
+int	fpipower(Ureg*);
+void	fpoff(void);
+void	fprestore(FPU*);
+void	fpsave(FPU*);
+ulong	fpstatus(void);
+char*	getconf(char*);
+ulong	getccr0(void);
+ulong	getdar(void);
+ulong	getdcr(int);
+ulong	getdear(void);
+ulong	getdepn(void);
+ulong	getdsisr(void);
+ulong	getesr(void);
+ulong	getimmr(void);
+ulong	getmsr(void);
+ulong	getpit(void);
+ulong	getpvr(void);
+ulong	gettbl(void);
+ulong	gettbu(void);
+ulong	gettsr(void);
+void	gotopc(ulong);
+void	icflush(void*, ulong);
+void	idle(void);
+void	idlehands(void);
+int	inb(int);
+ulong	inl(int);
+int	ins(int);
+void	insb(int, void*, int);
+void	insl(int, void*, int);
+void	inss(int, void*, int);
+void	intr(Ureg*);
+void	intrenable(int, void (*)(Ureg*, void*), void*, int, char*);
+void	intrdisable(int, void (*)(Ureg*, void*), void*, int, char*);
+int	intrstats(char*, int);
+void	intrvec(void);
+void	intrcvec(void);
+void	ioinit(void);
+void	ioreset(void);
+int	isaconfig(char*, int, ISAConf*);
+int	isvalid_va(void*);
+void	itlbmiss(void);
+void	kbdinit(void);
+void	kbdreset(void);
+void*	kmapphys(void*, ulong, ulong, ulong, ulong);
+void	lcdpanel(int);
+void	links(void);
+void	mapfree(RMap*, ulong, int);
+void	mapinit(RMap*, Map*, int);
+void	mathinit(void);
+void	mmuinit(void);
+void*	mmucacheinhib(void*, ulong);
+ulong	mmumapsize(ulong);
+void	pcimapinit(void);
+int	pciscan(int, Pcidev **);
+ulong	pcibarsize(Pcidev *, int);
+int	pcicfgr8(Pcidev*, int);
+int	pcicfgr16(Pcidev*, int);
+int	pcicfgr32(Pcidev*, int);
+void	pcicfgw8(Pcidev*, int, int);
+void	pcicfgw16(Pcidev*, int, int);
+void	pcicfgw32(Pcidev*, int, int);
+void	pciclrbme(Pcidev*);
+void	pcihinv(Pcidev*);
+uchar	pciipin(Pcidev *, uchar);
+Pcidev* pcimatch(Pcidev*, int, int);
+Pcidev* pcimatchtbdf(int);
+void	pcireset(void);
+void	pcisetbme(Pcidev*);
+void	procsave(Proc*);
+void	procsetup(Proc*);
+void	putdcr(int, ulong);
+void	putesr(ulong);
+void	putevpr(ulong);
+void	putmsr(ulong);
+void	putpit(ulong);
+void	puttcr(ulong);
+void	puttsr(ulong);
+void	puttwb(ulong);
+ulong	rmapalloc(RMap*, ulong, int, int);
+long	rtctime(void);
+void	screeninit(void);
+int	screenprint(char*, ...);			/* debugging */
+void	(*screenputs)(char*, int);
+int	segflush(void*, ulong);
+void	toggleled(int);
+void	setpanic(void);
+ulong	_tas(ulong*);
+ulong	tlbrehi(int);
+ulong	tlbrelo(int);
+int	tlbsxcc(void*);
+void	tlbwehi(int, ulong);
+void	tlbwelo(int, ulong);
+void	trapinit(void);
+void	trapvec(void);
+void	trapcvec(void);
+void	uartinstall(void);
+void	uartspecial(int, int, Queue**, Queue**, int (*)(Queue*, int));
+void	uartwait(void);	/* debugging */
+void	wbflush(void);
+
+#define	waserror()	(up->nerrlab++, setlabel(&up->errlab[up->nerrlab-1]))
+ulong	getcallerpc(void*);
+
+#define	isphys(a)	(((ulong)(a)&KSEGM)!=KSEG0 && ((ulong)(a)&KSEGM)!=KSEG1)
+#define KADDR(a)	((void*)((ulong)(a)|KZERO))
+#define PADDR(a)	(isphys(a)?(ulong)(a):((ulong)(a)&~KSEGM))
+
+/* IBM bit field order */
+#define	IBIT(b)	(((ulong)(1<<31))>>(b))
+#define	SIBIT(n)	((ushort)1<<(15-(n)))
+#define	CIBIT(n)	((uchar)1<<(7-(n)))
+
--- /dev/null
+++ b/os/cerf405/fpi.h
@@ -1,0 +1,61 @@
+typedef long Word;
+typedef unsigned long Single;
+typedef struct {
+	unsigned long h;
+	unsigned long l;
+} Double;
+
+enum {
+	FractBits	= 28,
+	CarryBit	= 0x10000000,
+	HiddenBit	= 0x08000000,
+	MsBit		= HiddenBit,
+	NGuardBits	= 3,
+	GuardMask	= 0x07,
+	LsBit		= (1<<NGuardBits),
+
+	SingleExpBias	= 127,
+	SingleExpMax	= 255,
+	DoubleExpBias	= 1023,
+	DoubleExpMax	= 2047,
+
+	ExpBias		= DoubleExpBias,
+	ExpInfinity	= DoubleExpMax,
+};
+
+typedef struct {
+	unsigned char s;
+	short e;
+	long l;				/* 0000FFFFFFFFFFFFFFFFFFFFFFFFFGGG */
+	long h;				/* 0000HFFFFFFFFFFFFFFFFFFFFFFFFFFF */
+} Internal;
+
+#define IsWeird(n)	((n)->e >= ExpInfinity)
+#define	IsInfinity(n)	(IsWeird(n) && (n)->h == HiddenBit && (n)->l == 0)
+#define	SetInfinity(n)	((n)->e = ExpInfinity, (n)->h = HiddenBit, (n)->l = 0)
+#define IsNaN(n)	(IsWeird(n) && (((n)->h & ~HiddenBit) || (n)->l))
+#define	SetQNaN(n)	((n)->s = 0, (n)->e = ExpInfinity, 		\
+			 (n)->h = HiddenBit|(LsBit<<1), (n)->l = 0)
+#define IsZero(n)	((n)->e == 1 && (n)->h == 0 && (n)->l == 0)
+#define SetZero(n)	((n)->e = 1, (n)->h = 0, (n)->l = 0)
+
+/*
+ * fpi.c
+ */
+extern void fpiround(Internal *);
+extern void fpiadd(Internal *, Internal *, Internal *);
+extern void fpisub(Internal *, Internal *, Internal *);
+extern void fpimul(Internal *, Internal *, Internal *);
+extern void fpidiv(Internal *, Internal *, Internal *);
+extern int fpicmp(Internal *, Internal *);
+extern void fpinormalise(Internal*);
+
+/*
+ * fpimem.c
+ */
+extern void fpis2i(Internal *, void *);
+extern void fpid2i(Internal *, void *);
+extern void fpiw2i(Internal *, void *);
+extern void fpii2s(void *, Internal *);
+extern void fpii2d(void *, Internal *);
+extern void fpii2w(Word *, Internal *);
--- /dev/null
+++ b/os/cerf405/fpipower.c
@@ -1,0 +1,970 @@
+/*
+ * this doesn't attempt to implement Power architecture floating-point properties
+ * that aren't visible in the Inferno environment.
+ * all arithmetic is done in double precision.
+ * the FP trap status isn't updated.
+ */
+#include "u.h"
+#include	"../port/lib.h"
+#include	"mem.h"
+#include	"dat.h"
+#include	"fns.h"
+#include	"io.h"
+
+#include	"ureg.h"
+
+#include	"fpi.h"
+
+#define	REG(x) (*(long*)(((char*)em->ur)+roff[(x)]))
+#define	FR(x) (*(Internal*)em->fr[(x)&0x1F])
+#define	REGSP	1	/* stack pointer */
+
+/* BUG: check fetch (not worthwhile in Inferno) */
+#define	getulong(a) (*(ulong*)(a))
+
+enum {
+	CRLT = 1<<31,
+	CRGT = 1<<30,
+	CREQ = 1<<29,
+	CRSO = 1<<28,
+	CRFU = CRSO,
+
+	CRFX = 1<<27,
+	CRFEX = 1<<26,
+	CRVX = 1<<25,
+	CROX = 1<<24,
+};
+
+#define getCR(x,w) (((w)>>(28-(x*4)))&0xF)
+#define mkCR(x,v) (((v)&0xF)<<(28-(x*4)))
+
+#define simm(xx, ii)	xx = (short)(ii&0xFFFF);
+#define getairr(i)	rd = (i>>21)&0x1f; ra = (i>>16)&0x1f; simm(imm,i)
+#define getarrr(i)	rd = (i>>21)&0x1f; ra = (i>>16)&0x1f; rb = (i>>11)&0x1f;
+#define getop(i) ((i>>26)&0x3F)
+#define getxo(i) ((i>>1)&0x3FF)
+
+#define	FPS_FX	(1<<31)	/* exception summary (sticky) */
+#define	FPS_EX	(1<<30)	/* enabled exception summary */
+#define	FPS_VX	(1<<29)	/* invalid operation exception summary */
+#define	FPS_OX	(1<<28)	/* overflow exception OX (sticky) */
+#define	FPS_UX	(1<<27)	/* underflow exception UX (sticky) */
+#define	FPS_ZX	(1<<26)	/* zero divide exception ZX (sticky) */
+#define	FPS_XX	(1<<25)	/* inexact exception XX (sticky) */
+#define	FPS_VXSNAN (1<<24)	/* invalid operation exception for SNaN (sticky) */
+#define	FPS_VXISI	(1<<23)	/* invalid operation exception for ∞-∞ (sticky) */
+#define	FPS_VXIDI	(1<<22)	/* invalid operation exception for ∞/∞ (sticky) */
+#define	FPS_VXZDZ (1<<21)	/* invalid operation exception for 0/0 (sticky) */
+#define	FPS_VXIMZ	(1<<20)	/* invalid operation exception for ∞*0 (sticky) */
+#define	FPS_VXVC	(1<<19)	/* invalid operation exception for invalid compare (sticky) */
+#define	FPS_FR	(1<<18)	/* fraction rounded */
+#define	FPS_FI	(1<<17)	/* fraction inexact */
+#define	FPS_FPRF	(1<<16)	/* floating point result class */
+#define	FPS_FPCC	(0xF<<12)	/* <, >, =, unordered */
+#define	FPS_VXCVI	(1<<8)	/* enable exception for invalid integer convert (sticky) */
+#define	FPS_VE	(1<<7)	/* invalid operation exception enable */
+#define	FPS_OE	(1<<6)	/* enable overflow exceptions */
+#define	FPS_UE	(1<<5)	/* enable underflow */
+#define	FPS_ZE	(1<<4)	/* enable zero divide */
+#define	FPS_XE	(1<<3)	/* enable inexact exceptions */
+#define	FPS_RN	(3<<0)	/* rounding mode */
+
+typedef struct Emreg Emreg;
+
+struct Emreg {
+	Ureg*	ur;
+	ulong	(*fr)[3];
+	FPenv*	ufp;
+	ulong	ir;
+	char*	name;
+};
+
+int	fpemudebug = 0;
+
+#undef OFR
+#define	OFR(X)	((ulong)&((Ureg*)0)->X)
+
+static	int	roff[] = {
+	OFR(r0), OFR(r1), OFR(r2), OFR(r3),
+	OFR(r4), OFR(r5), OFR(r6), OFR(r7),
+	OFR(r8), OFR(r9), OFR(r10), OFR(r11),
+	OFR(r12), OFR(r13), OFR(r14), OFR(r15),
+	OFR(r16), OFR(r17), OFR(r18), OFR(r19),
+	OFR(r20), OFR(r21), OFR(r22), OFR(r23),
+	OFR(r24), OFR(r25), OFR(r26), OFR(r27),
+	OFR(r28), OFR(r29), OFR(r30), OFR(r31),
+};
+
+/*
+ * initial FP register values assumed by qc's code
+ */
+static Internal fpreginit[] = {
+	/* s, e, l, h */
+	{0, 0x400, 0x00000000, 0x08000000},	/* F31=2.0 */
+	{0, 0x3FF, 0x00000000, 0x08000000},	/* F30=1.0 */
+	{0, 0x3FE, 0x00000000, 0x08000000},	/* F29=0.5 */
+	{0, 0x1, 0x00000000, 0x00000000}, /* F28=0.0 */
+	{0, 0x433, 0x00000000, 0x08000040},	/* F27=FREGCVI */
+};
+
+static void
+fadd(Emreg *em, Internal *d, int ra, int rb)
+{
+	Internal a, b;
+
+	a = FR(ra);
+	b = FR(rb);
+	(a.s == b.s? fpiadd: fpisub)(&b, &a, d);
+}
+
+static void
+fsub(Emreg *em, Internal *d, int ra, int rb)
+{
+	Internal a, b;
+
+	a = FR(ra);
+	b = FR(rb);
+	b.s ^= 1;
+	(b.s == a.s? fpiadd: fpisub)(&b, &a, d);
+}
+
+static void
+fmul(Emreg *em, Internal *d, int ra, int rb)
+{
+	Internal a, b;
+
+	a = FR(ra);
+	b = FR(rb);
+	fpimul(&b, &a, d);
+}
+
+static void
+fdiv(Emreg *em, Internal *d, int ra, int rb)
+{
+	Internal a, b;
+
+	a = FR(ra);
+	b = FR(rb);
+	fpidiv(&b, &a, d);
+}
+
+static void
+fmsub(Emreg *em, Internal *d, int ra, int rc, int rb)
+{
+	Internal a, c, b, t;
+
+	a = FR(ra);
+	c = FR(rc);
+	b = FR(rb);
+	fpimul(&a, &c, &t);
+	b.s ^= 1;
+	(b.s == t.s? fpiadd: fpisub)(&b, &t, d);
+}
+
+static void
+fmadd(Emreg *em, Internal *d, int ra, int rc, int rb)
+{
+	Internal a, c, b, t;
+
+	a = FR(ra);
+	c = FR(rc);
+	b = FR(rb);
+	fpimul(&a, &c, &t);
+	(t.s == b.s? fpiadd: fpisub)(&b, &t, d);
+}
+
+static ulong	setfpscr(Emreg*);
+static void	setfpcc(Emreg*, int);
+
+static void
+unimp(Emreg *em, ulong op)
+{
+	char buf[60];
+
+	snprint(buf, sizeof(buf), "sys: fp: pc=%lux unimp fp 0x%.8lux", em->ur->pc, op);
+	if(fpemudebug)
+		print("FPE: %s\n", buf);
+	error(buf);
+	/* no return */
+}
+
+/*
+ * floating load/store
+ */
+
+static void
+fpeairr(Emreg *em, ulong ir, void **eap, int *rdp)
+{
+	ulong ea;
+	long imm;
+	int ra, rd, upd;
+
+	getairr(ir);
+	ea = imm;
+	upd = (ir&(1L<<26))!=0;
+	if(ra) {
+		ea += REG(ra);
+		if(upd){
+			if(ra == REGSP)
+				panic("fpemu: r1 update");	/* can't do it because we're running on the same stack */
+			REG(ra) = ea;
+		}
+	} else {
+		if(upd)
+			unimp(em, ir);
+	}
+	*rdp = rd;
+	*eap = (void*)ea;
+	if(fpemudebug)
+		print("%8.8lux %s\tf%d,%ld(r%d) ea=%lux upd=%d\n", em->ur->pc, em->name, rd, imm, ra, ea, upd);
+}
+
+static void
+fpearrr(Emreg *em, ulong ir, int upd, void **eap, int *rdp)
+{
+	ulong ea;
+	int ra, rb, rd;
+
+	getarrr(ir);
+	ea = REG(rb);
+	if(ra){
+		ea += REG(ra);
+		if(upd){
+			if(ra == REGSP)
+				panic("fpemu: r1 update");
+			REG(ra) = ea;
+		}
+		if(fpemudebug)
+			print("%8.8lux %s\tf%d,(r%d+r%d) ea=%lux upd=%d\n", em->ur->pc, em->name, rd, ra, rb, ea, upd);
+	} else {
+		if(upd)
+			unimp(em, ir);
+		if(fpemudebug)
+			print("%8.8lux %s\tf%d,(r%d) ea=%lux\n", em->ur->pc, em->name, rd, rb, ea);
+	}
+	*eap = (void*)ea;
+	*rdp = rd;
+}
+
+static void
+lfs(Emreg *em, ulong ir)
+{
+	void *ea;
+	int rd;
+
+	em->name = "lfs";
+	fpeairr(em, ir, &ea, &rd);
+	fpis2i(&FR(rd), (void*)ea);
+}
+
+static void
+lfsx(Emreg *em, ulong ir)
+{
+	void *ea;
+	int rd;
+
+	em->name = "lfsx";
+	fpearrr(em, ir, ((ir>>1)&0x3FF)==567, &ea, &rd);
+	fpis2i(&FR(rd), (void*)ea);
+}
+
+static void
+lfd(Emreg *em, ulong ir)
+{
+	void *ea;
+	int rd;
+
+	em->name = "lfd";
+	fpeairr(em, ir, &ea, &rd);
+	fpid2i(&FR(rd), (void*)ea);
+}
+
+static void
+lfdx(Emreg *em, ulong ir)
+{
+	void *ea;
+	int rd;
+
+	em->name = "lfdx";
+	fpearrr(em, ir, ((ir>>1)&0x3FF)==631, &ea, &rd);
+	fpid2i(&FR(rd), (void*)ea);
+}
+
+static void
+stfs(Emreg *em, ulong ir)
+{
+	void *ea;
+	int rd;
+	Internal tmp;
+
+	em->name = "stfs";
+	fpeairr(em, ir, &ea, &rd);
+	tmp = FR(rd);
+	fpii2s(ea, &tmp);
+}
+
+static void
+stfsx(Emreg *em, ulong ir)
+{
+	void *ea;
+	int rd;
+	Internal tmp;
+
+	em->name = "stfsx";
+	fpearrr(em, ir, getxo(ir)==695, &ea, &rd);
+	tmp = FR(rd);
+	fpii2s(ea, &tmp);
+}
+
+static void
+stfd(Emreg *em, ulong ir)
+{
+	void *ea;
+	int rd;
+	Internal tmp;
+
+	em->name = "stfd";
+	fpeairr(em, ir, &ea, &rd);
+	tmp = FR(rd);
+	fpii2d(ea, &tmp);
+}
+
+static void
+stfdx(Emreg *em, ulong ir)
+{
+	void *ea;
+	int rd;
+	Internal tmp;
+
+	em->name = "stfdx";
+	fpearrr(em, ir, ((ir>>1)&0x3FF)==759, &ea, &rd);
+	tmp = FR(rd);
+	fpii2d(ea, &tmp);
+}
+
+static void
+mcrfs(Emreg *em, ulong ir)
+{
+	int rd, ra, rb;
+	static ulong fpscr0[] ={
+		FPS_FX|FPS_OX,
+		FPS_UX|FPS_ZX|FPS_XX|FPS_VXSNAN,
+		FPS_VXISI|FPS_VXIDI|FPS_VXZDZ|FPS_VXIMZ,
+		FPS_VXVC,
+		0,
+		FPS_VXCVI,
+	};
+
+	getarrr(ir);
+	if(rb || ra&3 || rd&3)
+		unimp(em, ir);
+	ra >>= 2;
+	rd >>= 2;
+	em->ur->cr = (em->ur->cr & ~mkCR(rd, 0xF)) | mkCR(rd, getCR(ra, em->ufp->fpscr));
+	em->ufp->fpscr &= ~fpscr0[ra];
+	if(fpemudebug)
+		print("%8.8lux mcrfs\tcrf%d,crf%d\n", em->ur->pc, rd, ra);
+}
+
+static void
+mffs(Emreg *em, ulong ir)
+{
+	int rd, ra, rb;
+	Double dw;
+
+	getarrr(ir);
+	if(ra || rb)
+		unimp(em, ir);
+	dw.h = 0;
+	dw.l = ((uvlong)0xFFF8000L<<16)|em->ufp->fpscr;
+	fpid2i(&FR(rd), &dw);
+	/* it's anyone's guess how CR1 should be set when ir&1 */
+	em->ur->cr &= ~mkCR(1, 0xE);	/* leave SO, reset others */
+	if(fpemudebug)
+		print("%8.8lux mffs%s\tfr%d\n", em->ur->pc, ir&1?".":"", rd);
+}
+
+static void
+mtfsb1(Emreg *em, ulong ir)
+{
+	int rd, ra, rb;
+
+	getarrr(ir);
+	if(ra || rb)
+		unimp(em, ir);
+	em->ufp->fpscr |= (1L << (31-rd));
+	/* BUG: should set summary bits */
+	if(ir & 1)
+		em->ur->cr &= ~mkCR(1, 0xE);	/* BUG: manual unclear: leave SO, reset others? */
+	if(fpemudebug)
+		print("%8.8lux mtfsb1%s\tfr%d\n", em->ur->pc, ir&1?".":"", rd);
+}
+
+static void
+mtfsb0(Emreg *em, ulong ir)
+{
+	int rd, ra, rb;
+
+	getarrr(ir);
+	if(ra || rb)
+		unimp(em, ir);
+	em->ufp->fpscr &= ~(1L << (31-rd));
+	if(ir & 1)
+		em->ur->cr &= ~mkCR(1, 0xE);		/* BUG: manual unclear: leave SO, reset others? */
+	if(fpemudebug)
+		print("%8.8lux mtfsb0%s\tfr%d\n", em->ur->pc, ir&1?".":"", rd);
+}
+
+static void
+mtfsf(Emreg *em, ulong ir)
+{
+	int fm, rb, i;
+	ulong v;
+	Internal b;
+	Double db;
+
+	if(ir & ((1L << 25)|(1L << 16)))
+		unimp(em, ir);
+	rb = (ir >> 11) & 0x1F;
+	fm = (ir >> 17) & 0xFF;
+	b = FR(rb);
+	fpii2d(&db, &b);	/* reconstruct hi/lo format to recover low word */
+	v = db.l;
+	for(i=0; i<8; i++)
+		if(fm & (1 << (7-i)))
+			em->ufp->fpscr = (em->ufp->fpscr & ~mkCR(i, 0xF)) | mkCR(i, getCR(i, v));
+	/* BUG: should set FEX and VX `according to the usual rule' */
+	if(ir & 1)
+		em->ur->cr &= ~mkCR(1, 0xE);		/* BUG: manual unclear: leave SO, reset others? */
+	if(fpemudebug)
+		print("%8.8lux mtfsf%s\t#%.2x,fr%d\n", em->ur->pc, ir&1?".":"", fm, rb);
+}
+
+static void
+mtfsfi(Emreg *em, ulong ir)
+{
+	int imm, rd;
+
+	if(ir & ((0x7F << 16)|(1L << 11)))
+		unimp(em, ir);
+	rd = (ir >> 23) & 0xF;
+	imm = (ir >> 12) & 0xF;
+	em->ufp->fpscr = (em->ufp->fpscr & ~mkCR(rd, 0xF)) | mkCR(rd, imm);
+	/* BUG: should set FEX and VX `according to the usual rule' */
+	if(ir & 1)
+		em->ur->cr &= ~mkCR(1, 0xE);		/* BUG: manual unclear: leave SO, reset others? */
+	if(fpemudebug)
+		print("%8.8lux mtfsfi%s\tcrf%d,#%x\n", em->ur->pc, ir&1?".":"", rd, imm);
+}
+
+static void
+fcmp(Emreg *em, ulong ir)
+{
+	int fc, rd, ra, rb, sig, i;
+
+	getarrr(ir);
+	if(rd & 3)
+		unimp(em, ir);
+	rd >>= 2;
+	sig = 0;
+	switch(getxo(ir)) {
+	default:
+		unimp(em, ir);
+	case 32:
+		if(fpemudebug)
+			print("%8.8lux fcmpo\tcr%d,f%d,f%d\n", em->ur->pc, rd, ra, rb);
+		sig = 1;
+		break;
+	case 0:
+		if(fpemudebug)
+			print("%8.8lux fcmpu\tcr%d,f%d,f%d\n", em->ur->pc, rd, ra, rb);
+		break;
+	}
+	if(IsWeird(&FR(ra)) || IsWeird(&FR(rb))) {
+		if(sig){
+			;	/* BUG: should trap if not masked ... */
+		}
+		fc = CRFU;
+	} else {
+		i = fpicmp(&FR(ra), &FR(rb));
+		if(i > 0)
+			fc = CRGT;
+		else if(i == 0)
+			fc = CREQ;
+		else
+			fc = CRLT;
+	}
+	fc >>= 28;
+	em->ur->cr = (em->ur->cr & ~mkCR(rd,~0)) | mkCR(rd, fc);
+	em->ufp->fpscr = (em->ufp->fpscr & ~0xF800) | (fc<<11);
+	/* BUG: update FX, VXSNAN, VXVC */
+}
+
+static void
+fariths(Emreg *em, ulong ir)
+{
+	int rd, ra, rb, rc, fmt;
+	char *cc, *n;
+	ulong fpscr;
+	Internal *d;
+
+	fmt = 0;
+	rc = (ir>>6)&0x1F;
+	getarrr(ir);
+	d = &FR(rd);
+	switch(getxo(ir)&0x1F) {	/* partial XO decode */
+	case 22:	/* fsqrts */
+	case 24:	/* fres */
+	default:
+		unimp(em, ir);
+		return;
+	case 18:
+		if(IsZero(&FR(rb))) {
+			em->ufp->fpscr |= FPS_ZX | FPS_FX;
+			error("sys: fp: zero divide");
+		}
+		fdiv(em, d, ra, rb);
+		n = "fdivs";
+		break;
+	case 20:
+		fsub(em, d, ra, rb);
+		n = "fsubs";
+		break;
+	case 21:
+		fadd(em, d, ra, rb);
+		n = "fadds";
+		break;
+	case 25:
+		fmul(em, d, ra, rc);
+		rb = rc;
+		n = "fmuls";
+		break;
+	case 28:
+		fmsub(em, d, ra, rc, rb);
+		fmt = 2;
+		n = "fmsubs";
+		break;
+	case 29:
+		fmadd(em, d, ra, rc, rb);
+		fmt = 2;
+		n = "fmadds";
+		break;
+	case 30:
+		fmsub(em, d, ra, rc, rb);
+		d->s ^= 1;
+		fmt = 2;
+		n = "fnmsubs";
+		break;
+	case 31:
+		fmadd(em, d, ra, rc, rb);
+		d->s ^= 1;
+		fmt = 2;
+		n = "fnmadds";
+		break;
+	}
+	if(fmt==1 && ra)
+		unimp(em, ir);
+	fpscr = setfpscr(em);
+	setfpcc(em, rd);
+	cc = "";
+	if(ir & 1) {
+		cc = ".";
+		em->ur->cr = (em->ur->cr & ~mkCR(1, ~0)) | mkCR(1, (fpscr>>28));
+	}
+	if(fpemudebug) {
+		switch(fmt) {
+		case 0:
+			print("%8.8lux %s%s\tfr%d,fr%d,fr%d\n", em->ur->pc, n, cc, rd, ra, rb);
+			break;
+		case 1:
+			print("%8.8lux %s%s\tfr%d,fr%d\n", em->ur->pc, n, cc, rd, rb);
+			break;
+		case 2:
+			print("%8.8lux %s%s\tfr%d,fr%d,fr%d,fr%d\n", em->ur->pc, n, cc, rd, ra, rc, rb);
+			break;
+		}
+	}
+}
+
+static void
+farith(Emreg *em, ulong ir)
+{
+	Word w;
+	Double dv;
+	int rd, ra, rb, rc, fmt;
+	char *cc, *n;
+	ulong fpscr;
+	int nocc;
+	Internal *d;
+
+	fmt = 0;
+	nocc = 0;
+	rc = (ir>>6)&0x1F;
+	getarrr(ir);
+	d = &FR(rd);
+	switch(getxo(ir)&0x1F) { /* partial XO decode */
+	case 22:	/* frsqrt */
+	case 23:	/* fsel */
+	case 26:	/* fsqrte */
+	default:
+		unimp(em, ir);
+		return;
+	case 12:	/* frsp */
+		*d = FR(rb);	/* BUG: doesn't round to single precision */
+		fmt = 1;
+		n = "frsp";
+		break;
+	case 14:	/* fctiw */	/* BUG: ignores rounding mode */
+	case 15:	/* fctiwz */
+		fpii2w(&w, &FR(rb));
+		dv.h = 0;
+		dv.l = w;
+		fpid2i(d, &dv);
+		fmt = 1;
+		nocc = 1;
+		n = "fctiw";
+		break;
+	case 18:
+		if(IsZero(&FR(rb))) {
+			em->ufp->fpscr |= FPS_ZX | FPS_FX;
+			error("sys: fp: zero divide");
+		}
+		fdiv(em, d, ra, rb);
+		n = "fdiv";
+		break;
+	case 20:
+		fsub(em, d, ra, rb);
+		n = "fsub";
+		break;
+	case 21:
+		fadd(em, d, ra, rb);
+		n = "fadd";
+		break;
+	case 25:
+		fmul(em, d, ra, rc);
+		rb = rc;
+		n = "fmul";
+		break;
+	case 28:
+		fmsub(em, d, ra, rc, rb);
+		fmt = 2;
+		n = "fmsub";
+		break;
+	case 29:
+		fmadd(em, d, ra, rc, rb);
+		fmt = 2;
+		n = "fmadd";
+		break;
+	case 30:
+		fmsub(em, d, ra, rc, rb);
+		d->s ^= 1;
+		fmt = 2;
+		n = "fnmsub";
+		break;
+	case 31:
+		fmadd(em, d, ra, rc, rb);
+		d->s ^= 1;
+		fmt = 2;
+		n = "fnmadd";
+		break;
+	}
+	if(fmt==1 && ra)
+		unimp(em, ir);
+	fpscr = setfpscr(em);
+	if(nocc == 0)
+		setfpcc(em, rd);
+	cc = "";
+	if(ir & 1) {
+		cc = ".";
+		em->ur->cr = (em->ur->cr & ~mkCR(1, ~0)) | mkCR(1, (fpscr>>28));
+	}
+	if(fpemudebug) {
+		switch(fmt) {
+		case 0:
+			print("%8.8lux %s%s\tfr%d,fr%d,fr%d\n", em->ur->pc, n, cc, rd, ra, rb);
+			break;
+		case 1:
+			print("%8.8lux %s%s\tfr%d,fr%d\n", em->ur->pc, n, cc, rd, rb);
+			break;
+		case 2:
+			print("%8.8lux %s%s\tfr%d,fr%d,fr%d,fr%d\n", em->ur->pc, n, cc, rd, ra, rc, rb);
+			break;
+		}
+	}
+}
+
+static void
+farith2(Emreg *em, ulong ir)
+{
+	int rd, ra, rb;
+	char *cc, *n;
+	ulong fpscr;
+	Internal *d, *b;
+
+	getarrr(ir);
+	if(ra)
+		unimp(em, ir);
+	d = &FR(rd);
+	b = &FR(rb);
+	switch(getxo(ir)) { /* full XO decode */
+	default:
+		unimp(em, ir);
+	case 40:
+		*d = *b;
+		d->s ^= 1;
+		n = "fneg";
+		break;
+	case 72:
+		*d = *b;
+		n = "fmr";
+		break;
+	case 136:
+		*d = *b;
+		d->s = 1;
+		n = "fnabs";
+		break;
+	case 264:
+		*d = *b;
+		d->s = 0;
+		n = "fabs";
+		break;
+	}
+	fpscr = setfpscr(em);
+	setfpcc(em, rd);
+	cc = "";
+	if(ir & 1) {
+		cc = ".";
+		em->ur->cr = (em->ur->cr & ~mkCR(1, ~0)) | mkCR(1, (fpscr>>28));
+	}
+	if(fpemudebug)
+		print("%8.8lux %s%s\tfr%d,fr%d\n", em->ur->pc, n, cc, rd, rb);
+}
+
+static ulong
+setfpscr(Emreg *em)
+{
+	ulong fps, fpscr;
+
+	fps = 0;	/* BUG: getfsr() */
+	fpscr = em->ufp->fpscr;
+	if(fps & FPAOVFL)
+		fpscr |= FPS_OX;
+	if(fps & FPAINEX)
+		fpscr |= FPS_XX;
+	if(fps & FPAUNFL)
+		fpscr |= FPS_UX;
+	if(fps & FPAZDIV)
+		fpscr |= FPS_ZX;
+	if(fpscr != em->ufp->fpscr) {
+		fpscr |= FPS_FX;
+		em->ufp->fpscr = fpscr;
+	}
+	return fpscr;
+}
+
+static void
+setfpcc(Emreg *em, int r)
+{
+	int c;
+	Internal *d;
+
+	d = &FR(r);
+	c = 0;
+	if(IsZero(d))
+		c |= 2;
+	else if(d->s == 1)
+		c |= 4;
+	else
+		c |= 8;
+	if(IsNaN(d))
+		c |= 1;
+	em->ufp->fpscr = (em->ufp->fpscr & ~0xF800) | (0<<15) | (c<<11); /* unsure about class bit */
+}
+
+static	uchar	op63flag[32] = {
+[12] 1, [14] 1, [15] 1, [18] 1, [20] 1, [21] 1, [22] 1,
+[23] 1, [25] 1, [26] 1, [28] 1, [29] 1, [30] 1, [31] 1,
+};
+
+/*
+ * returns the number of FP instructions emulated
+ */
+int
+fpipower(Ureg *ur)
+{
+	ulong op;
+	int xo;
+	Emreg emreg, *em;
+	FPenv *ufp;
+	int n;
+
+	ufp = &up->env->fpu;	/* because all the state is in Osenv, it need not be saved/restored */
+	em = &emreg;
+	em->ur = ur;
+	em->fr = ufp->emreg;
+	em->ufp = ufp;
+	em->name = nil;
+	if(em->ufp->fpistate != FPACTIVE) {
+		em->ufp->fpistate = FPACTIVE;
+		em->ufp->fpscr = 0;	/* TO DO */
+		for(n = 0; n < nelem(fpreginit); n++)
+			FR(31-n) = fpreginit[n];
+	}
+	for(n=0;;n++){
+		op = getulong(ur->pc);
+		em->ir = op;
+		if(fpemudebug > 1)
+			print("%8.8lux %8.8lux: ", ur->pc, op);
+		switch(op>>26){
+		default:
+			return n;
+		case 48:	/* lfs */
+		case 49:	/* lfsu */
+			lfs(em, op);
+			break;
+		case 50:	/* lfd */
+		case 51:	/* lfdu */
+			lfd(em, op);
+			break;
+		case 52:	/* stfs */
+		case 53:	/* stfsu */
+			stfs(em, op);
+			break;
+		case 54:	/* stfd */
+		case 55:	/* stfdu */
+			stfd(em, op);
+			break;
+		case 31:	/* indexed load/store */
+			xo = getxo(op);
+			if((xo & 0x300) != 0x200)
+				return n;
+			switch(xo){
+			default:
+				return n;
+			case 535:	/* lfsx */
+			case 567:	/* lfsux */
+				lfsx(em, op);
+				break;
+			case 599:	/* lfdx */
+			case 631:	/* lfdux */
+				lfdx(em, op);
+				break;
+			case 663:	/* stfsx */
+			case 695:	/* stfsux */
+				stfsx(em, op);
+				break;
+			case 727:	/* stfdx */
+			case 759:	/* stfdux */
+				stfdx(em, op);
+				break;
+			}
+			break;
+		case 63:	/* double precision */
+			xo = getxo(op);
+			if(op63flag[xo & 0x1F]){
+				farith(em, op);
+				break;
+			}
+			switch(xo){
+			default:
+				return n;
+			case 0:	/* fcmpu */
+			case 32:	/* fcmpo */
+				fcmp(em, op);
+				break;
+			case 40:	/* fneg */
+			case 72:	/* fmr */
+			case 136:	/* fnabs */
+			case 264:	/* fabs */
+				farith2(em, op);
+				break;
+			case 38:
+				mtfsb1(em, op);
+				break;
+			case 64:
+				mcrfs(em, op);
+				break;
+			case 70:
+				mtfsb0(em, op);
+				break;
+			case 134:
+				mtfsfi(em, op);
+				break;
+			case 583:
+				mffs(em, op);
+				break;
+			case 711:
+				mtfsf(em, op);
+				break;
+			}
+			break;
+		case 59:	/* single precision */
+			fariths(em, op);
+			break;
+		}
+		ur->pc += 4;
+		if(anyhigher())
+			sched();
+	}
+	return n;
+}
+
+/*
+50:	lfd	frD,d(rA)
+51:	lfdu	frD,d(rA)
+31,631:	lfdux	frD,rA,rB
+31,599:	lfdx	frD,rA,rB
+48:	lfs	frD,d(rA)
+49:	lfsu	frD,d(rA)
+31,567:	lfsux	frD,rA,rB
+31,535:	lfsx	frD,rA,rB
+
+54:	stfd	frS,d(rA)
+55:	stfdu	frS,d(rA)
+31,759:	stfdux	frS,rA,rB
+31,727:	stfdx	frS,rA,rB
+52:	stfs	frS,d(rA)
+53:	stfsu	frS,d(rA)
+31,695:	stfsux	frS,rA,rB
+31,663:	stfsx	frS,rA,rB
+
+63,64:	mcrfs	crfD,crfS
+63,583:	mffs[.]	frD
+63,70:	mtfsb0[.]	crbD
+63,38:	mtfsb1[.]	crbD
+63,711:	mtfsf[.]	FM,frB
+63,134:	mtfsfi[.]	crfD,IMM
+*/
+
+/*
+float to int:
+	FMOVD	g+0(SB),F1
+	FCTIWZ	F1,F4
+	FMOVD	F4,.rathole+0(SB)
+	MOVW	.rathole+4(SB),R7
+	MOVW	R7,l+0(SB)
+*/
+
+/*
+int to float:
+	MOVW	$1127219200,R9
+	MOVW	l+0(SB),R7
+	MOVW	R9,.rathole+0(SB)
+	XOR	$-2147483648,R7,R6
+	MOVW	R6,.rathole+4(SB)
+	FMOVD	.rathole+0(SB),F0
+	FSUB	F27,F0
+
+unsigned to float:
+	MOVW	ul+0(SB),R5
+	MOVW	R9,.rathole+0(SB)
+	XOR	$-2147483648,R5,R4
+	MOVW	R4,.rathole+4(SB)
+	FMOVD	.rathole+0(SB),F3
+	FSUB	F27,F3
+	FCMPU	F3,F28
+	BGE	,3(PC)
+	FMOVD	$4.29496729600000000e+09,F2
+	FADD	F2,F3
+	FMOVD	F3,g+0(SB)
+*/
--- /dev/null
+++ b/os/cerf405/gpio.c
@@ -1,0 +1,106 @@
+#include	"u.h"
+#include 	"mem.h"
+#include	"../port/lib.h"
+#include 	"dat.h"
+#include	"fns.h"
+#include	"io.h"
+
+#define	GPIOREGS	((Gpioregs*)KADDR(PHYSGPIO))
+
+static ulong gpioreserved;
+static Lock gpiolock;
+
+void
+gpioreserve(ulong mask)
+{
+	ilock(&gpiolock);
+	if(gpioreserved & mask)
+		panic("gpioreserve: duplicate use of 0x%.8lux", gpioreserved & mask);
+	gpioreserved |= mask;
+	iunlock(&gpiolock);
+}
+
+/*
+ * expand each of the bottom 16 bits into a two bit field
+ * with the bit as low order bit in the field
+ */
+static ulong
+inflate(ulong m)
+{
+	m  = ((m & 0xFF00) << 8) | (m & 0x00FF);
+	m = ((m << 4) | m) & 0x0F0F0F0F;
+	m = ((m << 2) | m) & 0x33333333;
+	return ((m << 1) | m) & 0x55555555;
+}
+
+/*
+ * set tcr, osr[hl], tsr[hl], odr, isr1[hl] for gpio bits in m,
+ * following the configuration bits in cfg.  when setting
+ * a gpio pin as output, set the right output value in OR first.
+ */
+void
+gpioconfig(ulong m, ulong cfg)
+{
+	Gpioregs *g;
+	ulong h, hm, l, lm;
+
+	h = inflate(m>>16);
+	hm = h | (h<<1);
+	l = inflate(m);
+	lm = l | (l<<1);
+	ilock(&gpiolock);
+	g = GPIOREGS;
+	/*
+	 * tsr has a setting ``Alt1 three-state source'' but
+	 * table 23-7 sets it to zero (use TCR) and sets TCR.
+ 	 * thus, it seems never really to be needed.
+	 */
+	g->tsrh &= ~hm;
+	g->tsrl &= ~lm;
+	/* always select pin input (don't care for outputs) */
+	g->isr1h = (g->isr1h & ~hm) | h;
+	g->isr1l = (g->isr1l & ~lm) | l;
+	if(cfg & Gpio_Alt1){	/* table 23-7 */
+		g->osrh = (g->osrh & ~hm) | h;	/* alt1 source */
+		g->osrl = (g->osrl & ~lm) | l;
+	}else{
+		g->osrh &= ~hm;	/* GPIO_OR source */
+		g->osrl &= ~lm;
+	}
+	if(cfg & Gpio_OD)
+		g->odr |= m;
+	else
+		g->odr &= ~m;
+	if(cfg & Gpio_in || cfg & Gpio_Tri)
+		g->tcr &= ~m;
+	else
+		g->tcr |= m;
+	iunlock(&gpiolock);
+}
+
+ulong
+gpioget(ulong mask)
+{
+	return GPIOREGS->ir & mask;
+}
+
+void
+gpioset(ulong mask, ulong out)
+{
+	Gpioregs *g;
+
+	ilock(&gpiolock);
+	g = GPIOREGS;
+	g->or = (g->or & ~mask) | (out & mask);
+	iunlock(&gpiolock);
+}
+
+void
+gpiorelease(ulong mask)
+{
+	ilock(&gpiolock);
+	if((gpioreserved & mask) != mask)
+		panic("gpiorelease: unexpected release of 0x%.8lux", ~gpioreserved & mask);
+	gpioreserved &= ~mask;
+	iunlock(&gpiolock);
+}
--- /dev/null
+++ b/os/cerf405/iic.c
@@ -1,0 +1,605 @@
+#include "u.h"
+#include "../port/lib.h"
+#include "mem.h"
+#include "dat.h"
+#include "fns.h"
+#include "io.h"
+#include "../port/error.h"
+
+/*
+ * basic read/write interface to 4xx IIC bus (master mode)
+ * ``referred to as IIC to distinguish it from the Phillips I⁲C bus [itself]''
+ *
+ * TO DO:
+ *	power management ref count
+ *	power up/power down on timer?
+ */
+
+typedef struct Ctlr Ctlr;
+typedef struct IICregs IICregs;
+
+struct IICregs {
+	uchar	mdbuf;	/* master data buffer */
+	uchar	rsvd0;
+	uchar	sdbuf;	/* slave data buffer */
+	uchar	rsvd1;
+	uchar	lmadr;	/* low master address */
+	uchar	hmadr;	/* high master address */
+	uchar	cntl;		/* control */
+	uchar	mdcntl;	/* mode control */
+	uchar	sts;		/* status */
+	uchar	extsts;	/* extended status */
+	uchar	lsadr;	/* low slave address */
+	uchar	hsadr;	/* high slave address */
+	uchar	clkdiv;	/* clock divide */
+	uchar	intrmsk;	/* interrupt mask */
+	uchar	xfrcnt;	/* transfer count */
+	uchar	xtcntlss;	/* extended control and slave status */
+	uchar	directcntl;	/* direct control */
+};
+
+enum {
+	/* cntl */
+	Hmt=	1<<7,	/* halt master transfer */
+	Amd10=	1<<6,	/* =0, 7-bit; =1, 10-bit addressing */
+					/* 5,4: two bit transfer count (n-1)&3 bytes */
+	Rpst=	1<<3,	/* =0, normal start; =1, repeated Start, transfer should be followed by another start */
+	Cht=		1<<2,	/* chain transfer; not the last */
+	Write=	0<<1,	/* transfer is a write */
+	Read=	1<<1,	/* transfer is a read */
+	Pt=		1<<0,	/* =0, most recent transfer complete; =1, start transfer if bus free */
+
+	/* mdcntl */
+	Fsdb=	1<<7,	/* flush slave data buffer */
+	Fmdb=	1<<6,	/* flush master data buffer */
+	Fsm=	1<<4,	/* =0, 100 kHz standard mode; =1, 400 Khz fast mode */
+	Esm=	1<<3,	/* enable slave mode */
+	Eint=		1<<2,	/* enable interrupt */
+	Eubs=	1<<1,	/* exit unknown bus state */
+	Hscl=	1<<0,	/* hold IIC serial clock low */
+
+	/* sts */
+	Sss=		1<<7,	/* slave status set (slave operation in progress) */
+	Slpr=	1<<6,	/* sleep mode */
+	Mdbs=	1<<5,	/* master data buffer has data */
+	Mdbf=	1<<4,	/* master data buffer is full */
+	Scmp=	1<<3,	/* stop complete */
+	Err=		1<<2,	/* error set in extsts */
+	Irqa=	1<<1,	/* IRQ active */
+	/* Pt as above */
+
+	/* extsts */
+	Irqp=	1<<7,	/* IRQ pending */
+	Bcs=		7<<4,
+	 Bcs_ssel=	1<<4,	/* slave-selected state */
+	 Bcs_sio=	2<<4,	/* slave transfer state */
+	 Bcs_mio=	3<<4,	/* master transfer state */
+	 Bcs_free=	4<<4,	/* bus is free */
+	 Bcs_busy= 5<<4,	/* bus is busy */
+	 Bcs_gok=	6<<4,	/* unknown state */
+	Irqd=	1<<3,	/* IRQ on deck */
+	La=		1<<2,	/* lost arbitration */
+	Ict=		1<<1,	/* incomplete transfer */
+	Xfra=	1<<0,	/* transfer aborted */
+
+	/* intrmsk */
+	Eirc=		1<<7,	/* slave read complete */
+	Eirs=		1<<6,	/* slave read needs service */
+	Eiwc=	1<<5,	/* slave write complete */
+	Eiws=	1<<4,	/* slave write needs service */
+	Eihe=	1<<3,	/* halt executed */
+	Eiic=		1<<2,	/* incomplete transfer */
+	Eita=		1<<1,	/* transfer aborted */
+	Eimtc=	1<<0,	/* master transfer complete */
+
+	/* xtcntlss */
+	Src=		1<<7,	/* slave read complete; =1, NAK or Stop, or repeated Start ended op */
+	Srs=		1<<6,	/* slave read needs service */
+	Swc=	1<<5,	/* slave write complete */
+	Sws=	1<<4,	/* slave write needs service */
+	Sdbd=	1<<3,	/* slave buffer has data */
+	Sdbf=	1<<2,	/* slave buffer is full */
+	Epi=		1<<1,	/* enable pulsed IRQ on transfer aborted */
+	Srst=		1<<0,	/* soft reset */
+
+	/* directcntl */
+	Sdac=	1<<3,	/* SDA output */
+	Scc=		1<<2,	/* SCL output */
+	Msda=	1<<1,	/* SDA input */
+	Msc=	1<<0,	/* SCL input */
+
+	/* others */
+	Rbit =	1<<0,	/* bit in address byte denoting read */
+	FIFOsize=	4,		/* most to be written at once */
+
+	MaxIO =	8192,	/* largest transfer done at once (can change) */
+	MaxSA=	2,		/* largest subaddress; could be FIFOsize */
+	Bufsize =	MaxIO,	/* subaddress bytes don't go in buffer */
+	Freq =	100000,
+	I2Ctimeout = 125,	/* msec (can change) */
+
+	Chatty = 0,
+};
+
+#define	DPRINT	if(Chatty)print
+
+/*
+ * I2C software structures
+ */
+
+struct Ctlr {
+	Lock;
+	QLock	io;
+	int	init;
+	int	polling;	/* eg, when running before system set up */
+	IICregs*	regs;	/* hardware registers */
+
+	/* controller state (see below) */
+	int	status;
+	int	phase;
+	Rendez	r;
+
+	/* transfer parameters */
+	int	cntl;		/* everything but transfer length */
+	int	rdcount;	/* requested read transfer size */
+	Block*	b;
+};
+
+enum {
+	/* Ctlr.state */
+	Idle,
+	Done,
+	Failed,
+	Busy,
+	Halting,
+};
+
+static	Ctlr	iicctlr[1];
+
+static void	interrupt(Ureg*, void*);
+static int readyxfer(Ctlr*);
+static void	rxstart(Ctlr*);
+static void	txstart(Ctlr*);
+static void	stopxfer(Ctlr*);
+static void	txoffset(Ctlr*, ulong, int);
+static int idlectlr(Ctlr*);
+
+static void
+iicdump(char *t, IICregs *iic)
+{
+	iprint("iic %s: lma=%.2ux hma=%.2ux im=%.2ux mdcntl=%.2ux sts=%.2ux ests=%.2ux cntl=%.2ux\n",
+		t, iic->lmadr, iic->hmadr, iic->intrmsk, iic->mdcntl, iic->sts, iic->extsts, iic->cntl);
+}
+
+static void
+initialise(IICregs *iic, int intrmsk)
+{
+	int d;
+
+	d = (m->opbhz-1000000)/10000000;
+	if(d <= 0)
+		d = 1;	/* just in case OPB freq < 20 Mhz */
+	/* initialisation (see 22.4, p. 22-23) */
+	iic->lmadr = 0;
+	iic->hmadr = 0;
+	iic->sts = Scmp|Irqa;
+	iic->extsts = Irqp | Irqd | La | Ict | Xfra;
+	iic->clkdiv = d;
+	iic->intrmsk = 0;	/* see below */
+	iic->xfrcnt = 0;
+	iic->xtcntlss = Src | Srs | Swc | Sws;
+	iic->mdcntl = Fsdb | Fmdb | Eubs;	/* reset; standard mode */
+	iic->cntl = 0;
+	eieio();
+	iic->mdcntl = 0;
+	eieio();
+	if(intrmsk){
+		iic->intrmsk = intrmsk;
+		iic->mdcntl = Eint;
+	}
+}
+
+/*
+ * called by the reset routine of any driver using the IIC
+ */
+void
+i2csetup(int polling)
+{
+	IICregs *iic;
+	Ctlr *ctlr;
+
+	ctlr = iicctlr;
+	ctlr->polling = polling;
+	iic = (IICregs*)KADDR(PHYSIIC);
+	ctlr->regs = iic;
+	if(!polling){
+		if(ctlr->init == 0){
+			initialise(iic, Eihe | Eiic | Eita | Eimtc);
+			ctlr->init = 1;
+			intrenable(VectorIIC, interrupt, iicctlr, BUSUNKNOWN, "iic");
+		}
+	}else
+		initialise(iic, 0);
+}
+
+static void
+interrupt(Ureg*, void *arg)
+{
+	int sts, nb, ext, avail;
+	Ctlr *ctlr;
+	Block *b;
+	IICregs *iic;
+
+	ctlr = arg;
+	iic = ctlr->regs;
+	if(0)
+		iicdump("intr", iic);
+	sts = iic->sts;
+	if(sts & Pt)
+		iprint("iic: unexpected status: %.2ux", iic->sts);
+	ext = iic->extsts;
+	if(sts & Mdbs)
+		nb = iic->xfrcnt & 7;
+	else
+		nb = 0;
+	eieio();
+	iic->sts = sts;
+	if(sts & Err && (ext & (La|Xfra)) != 0)
+		iprint("iic: s=%.2ux es=%.2ux (IO)\n", sts, ext);
+	ctlr->status = ext;
+	switch(ctlr->phase){
+	default:
+		iprint("iic: unexpected interrupt: p-%d s=%.2ux es=%.2ux\n", ctlr->phase, sts, ext);
+		break;
+
+	case Halting:
+		ctlr->phase = Idle;
+		break;
+
+	case Busy:
+		b = ctlr->b;
+		if(b == nil)
+			panic("iic: no buffer");
+		if(ctlr->cntl & Read){
+			/* copy data in from FIFO */
+			avail = b->lim - b->wp;
+			if(nb > avail)
+				nb = avail;
+			while(--nb >= 0)
+				*b->wp++ = iic->mdbuf;	/* ``the IIC interface handles the [FIFO] latency'' (22-4) */
+			if(sts & Err || ctlr->rdcount <= 0){
+				ctlr->phase = Done;
+				wakeup(&ctlr->r);
+				break;
+			}
+			rxstart(ctlr);
+		}else{
+			/* account for data transmitted */
+			if((b->rp += nb) > b->wp)
+				b->rp = b->wp;
+			if(sts & Err || BLEN(b) <= 0){
+				ctlr->phase = Done;
+				wakeup(&ctlr->r);
+				break;
+			}
+			txstart(ctlr);
+		}
+	}
+}
+
+static int
+done(void *a)
+{
+	return ((Ctlr*)a)->phase < Busy;
+}
+
+static int
+i2cerror(char *s)
+{
+	DPRINT("iic error: %s\n", s);
+	if(up)
+		error(s);
+	/* no current process, don't call error */
+	return -1;
+}
+
+static char*
+startxfer(I2Cdev *d, int op, void (*xfer)(Ctlr*), Block *b, int n, ulong offset)
+{
+	IICregs *iic;
+	Ctlr *ctlr;
+	int i, cntl, p, s;
+
+	ctlr = iicctlr;
+	if(up){
+		qlock(&ctlr->io);
+		if(waserror()){
+			qunlock(&ctlr->io);
+			nexterror();
+		}
+	}
+	ilock(ctlr);
+	if(!idlectlr(ctlr)){
+		iunlock(ctlr);
+		if(up)
+			error("bus confused");
+		return "bus confused";
+	}
+	if(ctlr->phase >= Busy)
+		panic("iic: ctlr busy");
+	cntl = op | Pt;
+	if(d->tenbit)
+		cntl |= Amd10;
+	ctlr->cntl = cntl;
+	ctlr->b = b;
+	ctlr->rdcount = n;
+	ctlr->phase = Busy;
+	iic = ctlr->regs;
+	if(d->tenbit){
+		iic->hmadr = 0xF0 | (d->addr>>7);	/* 2 higher bits of address, LSB don't care */
+		iic->lmadr = d->addr;
+	}else{
+		iic->hmadr = 0;
+		iic->lmadr = d->addr<<1;	/* 7-bit address */
+	}
+	if(d->salen)
+		txoffset(ctlr, offset, d->salen);
+	else
+		(*xfer)(ctlr);
+	iunlock(ctlr);
+
+	/* wait for it */
+	if(ctlr->polling){
+		for(i=0; !done(ctlr); i++){
+			delay(2);
+			interrupt(nil, ctlr);
+		}
+	}else
+		tsleep(&ctlr->r, done, ctlr, I2Ctimeout);
+
+	ilock(ctlr);
+	p = ctlr->phase;
+	s = ctlr->status;
+	ctlr->b = nil;
+	if(ctlr->phase != Done && ctlr->phase != Idle)
+		stopxfer(ctlr);
+	iunlock(ctlr);
+
+	if(up){
+		poperror();
+		qunlock(&ctlr->io);
+	}
+	if(p != Done || s & (La|Xfra)){	/* CHECK; time out */
+		if(s & La)
+			return "iic lost arbitration";
+		if(s & Xfra)
+			return "iic transfer aborted";
+		if(p != Done)
+			return "iic timed out";
+		sprint(up->genbuf, "iic error: phase=%d estatus=%.2ux", p, s);
+		return up->genbuf;
+	}
+	return nil;
+}
+
+long
+i2csend(I2Cdev *d, void *buf, long n, ulong offset)
+{
+	Block *b;
+	char *e;
+
+	if(n <= 0)
+		return 0;
+	if(n > MaxIO)
+		n = MaxIO;
+
+	if(up){
+		b = allocb(n);
+		if(b == nil)
+			error(Enomem);
+		if(waserror()){
+			freeb(b);
+			nexterror();
+		}
+	}else{
+		b = iallocb(n);
+		if(b == nil)
+			return -1;
+	}
+	memmove(b->wp, buf, n);
+	b->wp += n;
+	e = startxfer(d, Write, txstart, b, 0, offset);
+	if(up)
+		poperror();
+	n -= BLEN(b);	/* residue */
+	freeb(b);
+	if(e)
+		return i2cerror(e);
+	return n;
+}
+
+long
+i2crecv(I2Cdev *d, void *buf, long n, ulong offset)
+{
+	Block *b;
+	long nr;
+	char *e;
+
+	if(n <= 0)
+		return 0;
+	if(n > MaxIO)
+		n = MaxIO;
+
+	if(up){
+		b = allocb(n);
+		if(b == nil)
+			error(Enomem);
+		if(waserror()){
+			freeb(b);
+			nexterror();
+		}
+	}else{
+		b = iallocb(n);
+		if(b == nil)
+			return -1;
+	}
+	e = startxfer(d, Read, rxstart, b, n, offset);
+	nr = BLEN(b);
+	if(nr > 0)
+		memmove(buf, b->rp, nr);
+	if(up)
+		poperror();
+	freeb(b);
+	if(e)
+		return i2cerror(e);
+	return nr;
+}
+
+/*
+ * the controller must be locked for the following functions
+ */
+
+static int
+readyxfer(Ctlr *ctlr)
+{
+	IICregs *iic;
+
+	iic = ctlr->regs;
+	iic->sts = Scmp | Err;
+	if((iic->sts & Pt) != 0){
+		ctlr->phase = Failed;
+		wakeup(&ctlr->r);
+		return 0;
+	}
+	iic->mdcntl |= Fmdb;
+	return 1;
+}
+
+/*
+ * start a master  transfer to receive the next chunk of data
+ */
+static void
+rxstart(Ctlr *ctlr)
+{
+	Block *b;
+	int cntl;
+	long nb;
+
+	b = ctlr->b;
+	if(b == nil || (nb = ctlr->rdcount) <= 0){
+		ctlr->phase = Done;
+		wakeup(&ctlr->r);
+		return;
+	}
+	if(!readyxfer(ctlr))
+		return;
+	cntl = ctlr->cntl;
+	if(nb > FIFOsize){
+		nb = FIFOsize;
+		cntl |= Cht;	/* more to come */
+	}
+	ctlr->rdcount -= nb;
+	ctlr->regs->cntl = cntl | ((nb-1)<<4);
+}
+
+/*
+ * start a master transfer to send the next chunk of data
+ */
+static void
+txstart(Ctlr *ctlr)
+{
+	Block *b;
+	int cntl, i;
+	long nb;
+	IICregs *iic;
+
+	b = ctlr->b;
+	if(b == nil || (nb = BLEN(b)) <= 0){
+		ctlr->phase = Done;
+		wakeup(&ctlr->r);
+		return;
+	}
+	if(!readyxfer(ctlr))
+		return;
+	cntl = ctlr->cntl;
+	if(nb > FIFOsize){
+		nb = FIFOsize;
+		cntl |= Cht;	/* more to come */
+	}
+	iic = ctlr->regs;
+	for(i=0; i<nb; i++)
+		iic->mdbuf = *b->rp++;	/* load the FIFO */
+	iic->cntl = cntl | ((nb-1)<<4);
+}
+
+/*
+ * start a master transfer to send a sub-addressing offset;
+ * if subsequently receiving, use Rpst to cause the next transfer to include a Start;
+ * if subsequently sending, use Cht to chain the transfer without a Start.
+ */
+static void
+txoffset(Ctlr *ctlr, ulong offset, int len)
+{
+	int i, cntl;
+	IICregs *iic;
+
+	if(!readyxfer(ctlr))
+		return;
+	iic = ctlr->regs;
+	for(i=len*8; (i -= 8) >= 0;)
+		iic->mdbuf = offset>>i;	/* load offset bytes into FIFO */
+	cntl = ctlr->cntl & Amd10;
+	if(ctlr->cntl & Read)
+		cntl |= Rpst;
+	else
+		cntl |= Cht;
+	iic->cntl = cntl | ((len-1)<<4) | Write | Pt;
+}
+
+/*
+ * stop a transfer if one is in progress
+ */
+static void
+stopxfer(Ctlr *ctlr)
+{
+	IICregs *iic;
+	int ext;
+
+	iic = ctlr->regs;
+	ext = iic->extsts;
+	eieio();
+	iic->sts = Scmp | Irqa;
+	eieio();
+	if((iic->sts & Pt) == 0){
+		ctlr->phase = Idle;
+		return;
+	}
+	if((ext & Bcs) == Bcs_mio && ctlr->phase != Halting){
+		ctlr->phase = Halting;	/* interrupt will clear the state */
+		iic->cntl = Hmt;
+	}
+}
+
+static int
+idlectlr(Ctlr *ctlr)
+{
+	IICregs *iic;
+
+	iic = ctlr->regs;
+	if((iic->extsts & Bcs) == Bcs_free){
+		if((iic->sts & Pt) == 0){
+			ctlr->phase = Idle;
+			return 1;
+		}
+		iprint("iic: bus free, ctlr busy: s=%.2ux es=%.2ux\n", iic->sts, iic->extsts);
+	}
+	/* hit it with the hammer, soft reset */
+	iprint("iic: soft reset\n");
+	iic->xtcntlss = Srst;
+	iunlock(ctlr);
+	delay(1);
+	ilock(ctlr);
+	initialise(iic, Eihe | Eiic | Eita | Eimtc);
+	ctlr->phase = Idle;
+	return (iic->extsts & Bcs) == Bcs_free && (iic->sts & Pt) == 0;
+}
--- /dev/null
+++ b/os/cerf405/inb.s
@@ -1,0 +1,127 @@
+#include "mem.h"
+
+/*
+ * the tlb entry is configured for little-endian access,
+ * so byte-reversing loads aren't needed
+ */
+
+#define	ISAIO	PHYSPCIIO0
+
+#define	BDNZ	BC	16,0,
+
+TEXT	inb(SB), $0
+	OR	$ISAIO, R3
+	EIEIO
+	MOVBZ	(R3), R3
+	RETURN
+
+TEXT	insb(SB), $0
+	MOVW	v+4(FP), R4
+	MOVW	n+8(FP), R5
+	MOVW	R5, CTR
+	OR	$ISAIO, R3
+	SUB	$1, R4
+insb1:
+	EIEIO
+	MOVBZ	(R3), R7
+	MOVBU	R7, 1(R4)
+	BDNZ	insb1
+	RETURN
+
+TEXT	outb(SB), $0
+	MOVW	v+4(FP), R4
+	OR	$ISAIO, R3
+	EIEIO
+	MOVB	R4, (R3)
+	RETURN
+
+TEXT	outsb(SB), $0
+	MOVW	v+4(FP), R4
+	MOVW	n+8(FP), R5
+	MOVW	R5, CTR
+	OR	$ISAIO, R3
+	SUB	$1, R4
+outsb1:
+	EIEIO
+	MOVBZU	1(R4), R7
+	MOVB	R7, (R3)
+	BDNZ	outsb1
+	RETURN
+
+TEXT	ins(SB), $0
+	OR	$ISAIO, R3
+	EIEIO
+	MOVHZ	(R3), R3
+	RETURN
+
+TEXT	inss(SB), $0
+	MOVW	v+4(FP), R4
+	MOVW	n+8(FP), R5
+	MOVW	R5, CTR
+	OR	$ISAIO, R3
+	SUB	$2, R4
+inss1:
+	EIEIO
+	MOVHZ	(R3), R7
+	MOVHU	R7, 2(R4)
+	BDNZ	inss1
+	RETURN
+
+TEXT	outs(SB), $0
+	MOVW	v+4(FP), R4
+	OR	$ISAIO, R3
+	EIEIO
+	MOVH	R4, (R3)
+	RETURN
+
+TEXT	outss(SB), $0
+	MOVW	v+4(FP), R4
+	MOVW	n+8(FP), R5
+	MOVW	R5, CTR
+	OR	$ISAIO, R3
+	SUB	$2, R4
+outss1:
+	EIEIO
+	MOVHZU	2(R4), R7
+	MOVH	R7, (R3)
+	BDNZ	outss1
+	RETURN
+
+TEXT	inl(SB), $0
+	OR	$ISAIO, R3
+	EIEIO
+	MOVW	(R3), R3
+	RETURN
+
+TEXT	insl(SB), $0
+	MOVW	v+4(FP), R4
+	MOVW	n+8(FP), R5
+	MOVW	R5, CTR
+	OR	$ISAIO, R3
+	SUB	$4, R4
+insl1:
+	EIEIO
+	MOVW	(R3), R7
+	MOVWU	R7, 4(R4)
+	BDNZ	insl1
+	RETURN
+
+TEXT	outl(SB), $0
+	MOVW	v+4(FP), R4
+	OR	$ISAIO, R3
+	EIEIO
+	MOVW	R4, (R3)
+	RETURN
+
+TEXT	outsl(SB), $0
+	MOVW	v+4(FP), R4
+	MOVW	n+8(FP), R5
+	MOVW	R5, CTR
+	OR	$ISAIO, R3
+	SUB	$4, R4
+outsl1:
+	EIEIO
+	MOVWU	4(R4), R7
+	MOVW	R7, (R3)
+	BDNZ	outsl1
+	RETURN
--- /dev/null
+++ b/os/cerf405/io.h
@@ -1,0 +1,301 @@
+typedef struct BD BD;
+typedef struct Ring Ring;
+typedef struct MALdev MALdev;
+typedef struct I2Cdev I2Cdev;
+
+enum
+{
+	/* 405EP UIC interrupt vectors (IBM bit numbering) */
+	VectorUIC= 0,
+		VectorUART0=VectorUIC,
+		VectorUART1,
+		VectorIIC,
+		VectorPCIECW,
+		VectorRsvd1,
+		VectorDMA0,
+		VectorDMA1,
+		VectorDMA2,
+		VectorDMA3,
+		VectorEtherwake,
+		VectorMALSERR,
+		VectorMALTXEOB,
+		VectorMALRXEOB,
+		VectorMALTXDE,
+		VectorMALRXDE,
+		VectorEMAC0,
+		VectorPCISERR,
+		VectorEMAC1,
+		VectorPCIPM,
+		VectorGPT0,
+		VectorGPT1,
+		VectorGPT2,
+		VectorGPT3,
+		VectorGPT4,
+		/* 1 reserved */
+	VectorIRQ=	VectorUIC+25,	/* IRQ0 to IRQ6 */
+	MaxVector=	VectorIRQ+7,
+
+	/* some flags to change polarity and sensitivity */
+	IRQmask=		0xFF,	/* actual vector address */
+	IRQactivelow=	1<<8,
+	IRQedge=		1<<9,
+	IRQcritical=	1<<10,
+};
+
+/*
+ * these are defined to keep the interface compatible with other
+ * architectures, but only BUSUNKNOWN is currently used
+ */
+#define MKBUS(t,b,d,f)	(((t)<<24)|(((b)&0xFF)<<16)|(((d)&0x1F)<<11)|(((f)&0x07)<<8))
+#define BUSFNO(tbdf)	(((tbdf)>>8)&0x07)
+#define BUSDNO(tbdf)	(((tbdf)>>11)&0x1F)
+#define BUSBNO(tbdf)	(((tbdf)>>16)&0xFF)
+#define BUSTYPE(tbdf)	((tbdf)>>24)
+#define BUSBDF(tbdf)	((tbdf)&0x00FFFF00)
+#define BUSUNKNOWN	(-1)
+
+enum {
+	BusOPB,
+	BusPLB,
+	BusPCI,
+	MaxBus
+};
+
+/*
+ * MAL Buffer Descriptors and IO Rings
+ */
+
+struct BD {
+	ushort	status;
+	ushort	length;
+	ulong	addr;
+};
+#define	MAXIORING	256	/* hardware limit to ring size */
+#define	BDBUFLIM	(4096-16)	/* no MAL buffer larger than this */
+
+BD*	bdalloc(ulong);
+void	bdfree(BD*, int);
+void	dumpbd(char*, BD*, int);
+
+enum {
+	/* Rx BDs, bits common to all protocols */
+	BDEmpty=	1<<15,
+	BDWrap=		1<<14,	/* end of ring */
+	BDContin=	1<<13,	/* continuous mode */
+	BDLast=		1<<12,	/* last buffer in current packet */
+	BDFirst=		1<<11,	/* first buffer in current packet (set by MAL) */
+	BDInt=		1<<10,	/* interrupt when done */
+
+	/* Tx BDs */
+	BDReady=		1<<15,	/* ready to transmit; set by driver, cleared by MAL */
+	/* BDWrap, BDInt, BDLast as above */
+};
+
+struct Ring {
+	BD*	rdr;				/* receive descriptor ring */
+	Block**	rxb;			/* receive ring buffers */
+	int	rdrx;				/* index into rdr */
+	int	nrdre;			/* length of rdr */
+
+	BD*	tdr;				/* transmit descriptor ring */
+	Block**	txb;			/* transmit ring buffers */
+	int	tdrh;				/* host index into tdr */
+	int	tdri;				/* interface index into tdr */
+	int	ntdre;			/* length of tdr */
+	int	ntq;				/* pending transmit requests */
+};
+
+#define NEXT(x, l)	(((x)+1)%(l))
+#define PREV(x, l)	(((x) == 0) ? (l)-1: (x)-1)
+#define	HOWMANY(x, y)	(((x)+((y)-1))/(y))
+#define ROUNDUP(x, y)	(HOWMANY((x), (y))*(y))
+
+/*
+ * one per mal channel
+ */
+typedef struct Mal Mal;
+struct Mal {
+	int	n;
+	int	len;
+	int	tx;
+	ulong	mask;
+
+	void*	arg;
+	void	(*interrupt)(Ureg*, void*);
+};
+
+Mal*	malchannel(int, int, void (*)(Ureg*, void*), void*);
+void	maltxreset(Mal*);
+void	maltxinit(Mal*, Ring*);
+void	maltxenable(Mal*);
+void	malrxreset(Mal*);
+void	malrxinit(Mal*, Ring*, ulong);
+void	malrxenable(Mal*);
+void	ioringreserve(int, ulong, int, ulong);
+int	ioringinit(Ring*, int, int);
+
+typedef struct Gpioregs Gpioregs;
+struct Gpioregs {
+	ulong	or;	/* output register */
+	ulong	tcr;	/* tristate control */
+	ulong	osrh;	/* output select high (0-15) */
+	ulong	osrl;	/* output select low (16-31) */
+	ulong	tsrh;	/* tristate select high (0-15) */
+	ulong	tsrl;	/* tristate select low (16-31) */
+	ulong	odr;	/* open drain */
+	ulong	ir;	/* input */
+	ulong	rr1;	/* receive register */
+	ulong	pad[3];
+	ulong	isr1h;	/* input select 1 high (0-15) */
+	ulong	isr1l;	/* input select 1 low (16-31) */
+};
+
+enum {
+	/* software configuration bits for gpioconfig */
+	Gpio_Alt1=	1<<0,	/* implies specific settings of all the others, but include in or out */
+	Gpio_OD=	1<<1,
+	Gpio_Tri=		1<<2,
+	Gpio_in=		1<<4,
+	Gpio_out=	1<<5,
+};
+
+void	gpioreserve(ulong);
+void	gpioconfig(ulong, ulong);
+ulong	gpioget(ulong);
+void	gpioset(ulong, ulong);
+void	gpiorelease(ulong);
+
+/*
+ * used by ../port/devi2c.c and iic.c
+ */
+struct I2Cdev {
+	int	addr;
+	int	salen;	/* length in bytes of subaddress, if used; 0 otherwise */
+	int	tenbit;	/* 10-bit addresses */
+};
+
+long	i2crecv(I2Cdev*, void*, long, ulong);
+long	i2csend(I2Cdev*, void*, long, ulong);
+void	i2csetup(int);
+
+/*
+ * PCI support code.
+ */
+enum {					/* type 0 and type 1 pre-defined header */
+	PciVID		= 0x00,		/* vendor ID */
+	PciDID		= 0x02,		/* device ID */
+	PciPCR		= 0x04,		/* command */
+	PciPSR		= 0x06,		/* status */
+	PciRID		= 0x08,		/* revision ID */
+	PciCCRp		= 0x09,		/* programming interface class code */
+	PciCCRu		= 0x0A,		/* sub-class code */
+	PciCCRb		= 0x0B,		/* base class code */
+	PciCLS		= 0x0C,		/* cache line size */
+	PciLTR		= 0x0D,		/* latency timer */
+	PciHDT		= 0x0E,		/* header type */
+	PciBST		= 0x0F,		/* BIST */
+
+	PciBAR0		= 0x10,		/* base address */
+	PciBAR1		= 0x14,
+
+	PciINTL		= 0x3C,		/* interrupt line */
+	PciINTP		= 0x3D,		/* interrupt pin */
+};
+
+enum {					/* type 0 pre-defined header */
+	PciBAR2		= 0x18,
+	PciBAR3		= 0x1C,
+	PciBAR4		= 0x20,
+	PciBAR5		= 0x24,
+	PciCIS		= 0x28,		/* cardbus CIS pointer */
+	PciSVID		= 0x2C,		/* subsystem vendor ID */
+	PciSID		= 0x2E,		/* cardbus CIS pointer */
+	PciEBAR0	= 0x30,		/* expansion ROM base address */
+	PciMGNT		= 0x3E,		/* burst period length */
+	PciMLT		= 0x3F,		/* maximum latency between bursts */
+};
+
+enum {					/* type 1 pre-defined header */
+	PciPBN		= 0x18,		/* primary bus number */
+	PciSBN		= 0x19,		/* secondary bus number */
+	PciUBN		= 0x1A,		/* subordinate bus number */
+	PciSLTR		= 0x1B,		/* secondary latency timer */
+	PciIBR		= 0x1C,		/* I/O base */
+	PciILR		= 0x1D,		/* I/O limit */
+	PciSPSR		= 0x1E,		/* secondary status */
+	PciMBR		= 0x20,		/* memory base */
+	PciMLR		= 0x22,		/* memory limit */
+	PciPMBR		= 0x24,		/* prefetchable memory base */
+	PciPMLR		= 0x26,		/* prefetchable memory limit */
+	PciPUBR		= 0x28,		/* prefetchable base upper 32 bits */
+	PciPULR		= 0x2C,		/* prefetchable limit upper 32 bits */
+	PciIUBR		= 0x30,		/* I/O base upper 16 bits */
+	PciIULR		= 0x32,		/* I/O limit upper 16 bits */
+	PciEBAR1	= 0x28,		/* expansion ROM base address */
+	PciBCR		= 0x3E,		/* bridge control register */
+};
+
+enum {					/* type 2 pre-defined header */
+	PciCBExCA	= 0x10,
+	PciCBSPSR	= 0x16,
+	PciCBPBN	= 0x18,		/* primary bus number */
+	PciCBSBN	= 0x19,		/* secondary bus number */
+	PciCBUBN	= 0x1A,		/* subordinate bus number */
+	PciCBSLTR	= 0x1B,		/* secondary latency timer */
+	PciCBMBR0	= 0x1C,
+	PciCBMLR0	= 0x20,
+	PciCBMBR1	= 0x24,
+	PciCBMLR1	= 0x28,
+	PciCBIBR0	= 0x2C,		/* I/O base */
+	PciCBILR0	= 0x30,		/* I/O limit */
+	PciCBIBR1	= 0x34,		/* I/O base */
+	PciCBILR1	= 0x38,		/* I/O limit */
+	PciCBSVID	= 0x40,		/* subsystem vendor ID */
+	PciCBSID	= 0x42,		/* subsystem ID */
+	PciCBLMBAR	= 0x44,		/* legacy mode base address */
+};
+
+typedef struct Pcisiz Pcisiz;
+struct Pcisiz
+{
+	Pcidev*	dev;
+	int	siz;
+	int	bar;
+};
+
+typedef struct Pcidev Pcidev;
+struct Pcidev
+{
+	int	tbdf;			/* type+bus+device+function */
+	ushort	vid;			/* vendor ID */
+	ushort	did;			/* device ID */
+
+	uchar	rid;
+	uchar	ccrp;
+	uchar	ccru;
+	uchar	ccrb;
+
+	struct {
+		ulong	bar;		/* base address */
+		int	size;
+	} mem[6];
+
+	struct {
+		ulong	bar;	
+		int	size;
+	} rom;
+	uchar	intl;			/* interrupt line */
+
+	Pcidev*	list;
+	Pcidev*	link;			/* next device on this bno */
+
+	Pcidev*	bridge;			/* down a bus */
+	struct {
+		ulong	bar;
+		int	size;
+	} ioa, mema;
+	ulong	pcr;
+};
+
+#define PCIWINDOW	0x80000000
+#define PCIWADDR(va)	(PADDR(va)+PCIWINDOW)
--- /dev/null
+++ b/os/cerf405/l.s
@@ -1,0 +1,795 @@
+#include	"mem.h"
+
+#define	MB	(1024*1024)
+
+/*
+ * common ppc special purpose registers
+ */
+#define DSISR	18
+#define SRR0	26	/* Saved Registers (exception) */
+#define SRR1	27
+#define SPRG0	272	/* Supervisor Private Registers */
+#define SPRG1	273
+#define SPRG2	274
+#define SPRG3	275
+#define TBRU	269	/* Time base Upper/Lower (Reading) */
+#define TBRL	268
+#define TBWU	285	/* Time base Upper/Lower (Writing) */
+#define TBWL	284
+#define PVR	287	/* Processor Version */
+
+/*
+ * 4xx-specific special purpose registers of interest here
+ */
+#define ICCR	1019	/* instruction cache control */
+#define DCCR	1018	/* data cache control */
+#define DBCR0	1010	/* debug control register 0 */
+#define DCWR	964	/* data cache write-through */
+#define PID	945	/* TLB process ID */
+#define CCR0	947	/* core configuration register 0 */
+#define SLER	955	/* storage little-endian */
+#define SU0R	956	/* storage user-defined 0 */
+#define SRR2	990
+#define SRR3	991
+/* SPRGn up to 7, if needed, on the 400 series */
+#define DEAR	961	/* data error address */
+#define ESR	980	/* exception syndrome */
+#define EVPR	982	/* exception vector prefix */
+#define PIT	987	/* interval timer */
+#define SGR	953	/* storage guarded */
+#define TCR	986	/* timer control */
+#define TSR	984	/* timer status */
+#define ZPR	944	/* zone protection */
+
+/*
+ * 4xx-specific(?) device control registers
+ */
+#define OCM0_DSCNTL	0x1B	/* OCM data-side control register */
+
+/* use of SPRG registers in save/restore */
+#define	SAVER0	SPRG0
+#define	SAVER1	SPRG1
+#define	SAVELR	SPRG2
+#define	SAVEXX	SPRG3
+
+/* special instruction definitions */
+#define	BDNZ	BC	16,0,
+#define	BDNE	BC	0,2,
+#define	TLBIA	WORD	$((31<<26)|(370<<1))
+#define	TLBSYNC	WORD	$((31<<26)|(566<<1))
+#define	MFTB(tbr,d)	WORD	$((31<<26)|((d)<<21)|((tbr&0x1f)<<16)|(((tbr>>5)&0x1f)<<11)|(371<<1))
+
+/* 603/603e specific: load tlb entries */
+#define	TLBLD(x)	WORD	$((31<<26)|(978<<1)|((x&0x1F)<<11))
+#define	TLBLI(x)	WORD	$((31<<26)|(1010<<1)|((x&0x1F)<<11))
+
+/* 400 models; perhaps others */
+#define	ICCCI(a,b)	WORD	$((31<<26)|((a)<<16)|((b)<<11)|(966<<1))
+#define	DCCCI(a,b)	WORD	$((31<<26)|((a)<<16)|((b)<<11)|(454<<1))
+/* these follow the source -> dest ordering */
+#define	DCREAD(s,t)	WORD	$((31<<26)|((t)<<21)|((s)<<11)|(486<<1))
+#define	DCRF(n)	((((n)>>5)&0x1F)|(((n)&0x1F)<<5))
+#define	MTDCR(s,n)	WORD	$((31<<26)|((s)<<21)|(DCRF(n)<<11)|(451<<1))
+#define	MFDCR(n,t)	WORD	$((31<<26)|((t)<<21)|(DCRF(n)<<11)|(323<<1))
+#define	TLBRELO(a,t)	WORD	$((31<<26)|((t)<<21)|((a)<<16)|(1<<11)|(946<<1))
+#define	TLBREHI(a,t)	WORD	$((31<<26)|((t)<<21)|((a)<<16)|(0<<11)|(946<<1))
+#define	TLBWELO(s,a)	WORD	$((31<<26)|((s)<<21)|((a)<<16)|(1<<11)|(978<<1))
+#define	TLBWEHI(s,a)	WORD	$((31<<26)|((s)<<21)|((a)<<16)|(0<<11)|(978<<1))
+#define	TLBSX(a,b,t)	WORD	$((31<<26)|((t)<<21)|((a)<<16)|((b)<<11)|(914<<1))
+#define	TLBSXCC(a,b,t)	WORD	$((31<<26)|((t)<<21)|((a)<<16)|((b)<<11)|(914<<1)|1)
+#define	WRTMSR_EE(s)	WORD	$((31<<26)|((s)<<21)|(131<<1))
+#define	WRTMSR_EEI(e)	WORD	$((31<<26)|((e)<<16)|(163<<1))
+
+/* on some models mtmsr doesn't synchronise enough (eg, 603e) */
+#define	MSRSYNC	SYNC; ISYNC
+
+/* on the 400 series, the prefetcher madly fetches across RFI, sys call, and others; use BR 0(PC) to stop */
+#define	RFI	WORD $((19<<26)|(50<<1)); BR 0(PC)
+#define	RFCI	WORD	$((19<<26)|(51<<1)); BR 0(PC)
+
+#define	UREGSPACE	(UREGSIZE+8)
+
+/* could define STEP to set an LED to mark progress */
+#define	STEP(x)
+
+/*
+ * Boot first processor
+ */
+	TEXT start(SB), $-4
+
+	MOVW	MSR, R3
+	RLWNM	$0, R3, $~MSR_EE, R3
+	OR	$MSR_ME, R3
+	ISYNC
+	MOVW	R3, MSR	/* turn off interrupts but enable traps */
+	MSRSYNC
+	MOVW	$0, R0	/* except during trap handling, R0 is zero from now on */
+	MOVW	R0, CR
+
+	MOVW	$setSB-KZERO(SB), R2	/* SB until mmu on */
+
+/*
+ * reset the caches and disable them until mmu on
+ */
+	MOVW	R0, SPR(ICCR)
+	ICCCI(0, 2)	/* the errata reveals that EA is used; we'll use SB */
+	ISYNC
+
+	MOVW	$((CACHEWAYSIZE/CACHELINESZ)-1), R3
+	MOVW	R3, CTR
+	MOVW	R0, R3
+dcinv:
+	DCCCI(0,3)
+	ADD	$32, R3
+	BDNZ	dcinv
+
+	/* cache is copy-back, disabled; no user-defined 0; big endian throughout */
+	MOVW	R0, SPR(DCWR)
+	MOVW	R0, SPR(DCCR)
+	MOVW	R0, SPR(SU0R)
+	MOVW	R0, SPR(SLER)
+	ISYNC
+
+	/* guard everything above 0x20000000 */
+	MOVW	$~(0xF000<<16), R3
+	MOVW	R3, SPR(SGR)
+	ISYNC
+
+	/* set access to LED */
+	MOVW	$PHYSGPIO, R4
+	MOVW	$(1<<31), R6
+	MOVW	$(0xC000<<16), R5
+	MOVW	4(R4), R3
+	OR	R6, R3
+	MOVW	R3, 4(R4)	/* tcr set */
+	MOVW	0x18(R4), R3
+	ANDN	R6, R3
+	MOVW	R3, 0x18(R4)	/* odr reset */
+	MOVW	8(R4), R3
+	ANDN	R5, R3
+	MOVW	R3, 8(R4)	/* osrh uses or */
+	MOVW	0x10(R4), R3
+	ANDN	R5, R3
+	MOVW	R3, 0x10(R4)	/* tsr uses tcr */
+
+	MOVW	$(1<<31), R4	/* reset MAL */
+	MTDCR(0x180, 4)
+
+/*
+	MOVW	$'H', R3
+	BL	uartputc(SB)
+	MOVW	$'\n', R3
+	BL	uartputc(SB)
+*/
+	
+/*
+ * set other system configuration values
+ */
+	MOVW	R0, SPR(PIT)
+	MOVW	$~0, R3
+	MOVW	R3, SPR(TSR)
+
+STEP(1)
+
+	BL	kernelmmu(SB)
+	/* now running with correct addresses, mmu on */
+
+	MOVW	$setSB(SB), R2
+
+	/* enable caches for kernel 128mb in real mode; data is copy-back */
+	MOVW	R0, SPR(DCWR)
+	MOVW	$(1<<31), R3
+	MOVW	R3, SPR(DCCR)
+	MOVW	R3, SPR(ICCR)
+
+/*
+	BL	ledoff(SB)
+
+	MOVW	$0x800, R8
+	MOVW	R8, LR
+	BL	(LR)
+	BR	0(PC)
+*/
+
+STEP(2)
+	/* no kfpinit on 4xx */
+
+	MOVW	$mach0(SB), R(MACH)
+	ADD	$(MACHSIZE-8), R(MACH), R1
+	SUB	$4, R(MACH), R3
+	ADD	$4, R1, R4
+clrmach:
+	MOVWU	R0, 4(R3)
+	CMP	R3, R4
+	BNE	clrmach
+
+	MOVW	R0, R(USER)
+	MOVW	R0, 0(R(MACH))
+
+	MOVW	$edata(SB), R3
+	MOVW	$end(SB), R4
+	ADD	$4, R4
+	SUB	$4, R3
+clrbss:
+	MOVWU	R0, 4(R3)
+	CMP	R3, R4
+	BNE	clrbss
+
+STEP(3)
+	BL	main(SB)
+	BR	0(PC)
+
+TEXT	kernelmmu(SB), $-4
+	TLBIA
+	ISYNC
+	SYNC
+
+	/* make following TLB entries shared, TID=PID=0 */
+	MOVW	R0, SPR(PID)
+
+	/* all zones are supervisor, access controlled by TLB */
+	MOVW	R0, SPR(ZPR)
+
+	/* map various things 1:1 */
+	MOVW	$tlbtab-KZERO(SB), R4
+	MOVW	$tlbtabe-KZERO(SB), R5
+	SUB	R4, R5
+	MOVW	$(2*4), R6
+	DIVW	R6, R5
+	SUB	$4, R4
+	MOVW	R5, CTR
+	MOVW	R0, R3
+ltlb:
+	MOVWU	4(R4), R5	/* TLBHI */
+	TLBWEHI(5,3)
+	MOVWU	4(R4), R5	/* TLBLO */
+	TLBWELO(5,3)
+	ADD	$1, R3
+	BDNZ	ltlb
+
+	MOVW	LR, R3
+	OR	$KZERO, R3
+	MOVW	R3, SPR(SRR0)
+	MOVW	MSR, R4
+	OR	$(MSR_IR|MSR_DR), R4
+	MOVW	R4, SPR(SRR1)
+
+	RFI	/* resume in kernel mode in caller */
+
+TEXT	ledoff(SB), $0
+	MOVW	$PHYSGPIO, R4
+	MOVW	0(R4), R3
+	RLWNM	$0, R3, $~(1<<31), R3	/* LED off */
+	MOVW	R3, 0(R4)
+	RETURN
+
+TEXT	splhi(SB), $0
+	MOVW	MSR, R3
+	RLWNM	$0, R3, $~MSR_EE, R4
+	SYNC
+	MOVW	R4, MSR
+	MSRSYNC
+	MOVW	LR, R31
+	MOVW	R31, 4(R(MACH))	/* save PC in m->splpc */
+	RETURN
+
+TEXT	splx(SB), $0
+	MOVW	MSR, R4
+	RLWMI	$0, R3, $MSR_EE, R4
+	RLWNMCC	$0, R3, $MSR_EE, R5
+	BNE	splx0
+	MOVW	LR, R31
+	MOVW	R31, 4(R(MACH))	/* save PC in m->splpc */
+splx0:
+	SYNC
+	MOVW	R4, MSR
+	MSRSYNC
+	RETURN
+
+TEXT	splxpc(SB), $0
+	MOVW	MSR, R4
+	RLWMI	$0, R3, $MSR_EE, R4
+	RLWNMCC	$0, R3, $MSR_EE, R5
+	SYNC
+	MOVW	R4, MSR
+	MSRSYNC
+	RETURN
+
+TEXT	spllo(SB), $0
+	MFTB(TBRL, 3)
+	MOVW	R3, spltbl(SB)
+	MOVW	MSR, R3
+	OR	$MSR_EE, R3, R4
+	SYNC
+	MOVW	R4, MSR
+	MSRSYNC
+	RETURN
+
+TEXT	spldone(SB), $0
+	RETURN
+
+TEXT	islo(SB), $0
+	MOVW	MSR, R3
+	RLWNM	$0, R3, $MSR_EE, R3
+	RETURN
+
+TEXT	setlabel(SB), $-4
+	MOVW	LR, R31
+	MOVW	R1, 0(R3)
+	MOVW	R31, 4(R3)
+	MOVW	$0, R3
+	RETURN
+
+TEXT	gotolabel(SB), $-4
+	MOVW	4(R3), R31
+	MOVW	R31, LR
+	MOVW	0(R3), R1
+	MOVW	$1, R3
+	RETURN
+
+TEXT	tlbwelo(SB), $-4
+	MOVW	v+4(FP), R5
+	SYNC
+	TLBWELO(5, 3)
+	ISYNC
+	SYNC
+	RETURN
+
+TEXT	tlbwehi(SB), $-4
+	MOVW	v+4(FP), R5
+	SYNC
+	TLBWEHI(5, 3)
+	ISYNC
+	SYNC
+	RETURN
+
+TEXT	tlbrehi(SB), $-4
+	TLBREHI(3, 3)
+	RETURN
+
+TEXT	tlbrelo(SB), $-4
+	TLBRELO(3, 3)
+	RETURN
+
+TEXT	tlbsxcc(SB), $-4
+	TLBSXCC(0, 3, 3)
+	BEQ	tlbsxcc0
+	MOVW	$-1, R3	/* not found */
+tlbsxcc0:
+	RETURN
+
+/*
+ * enter with stack set and mapped.
+ * on return, SB (R2) has been set, and R3 has the Ureg*,
+ * the MMU has been re-enabled, kernel text and PC are in KSEG,
+ * R(MACH) has been set, and R0 contains 0.
+ *
+ * this can be simplified in the Inferno regime
+ */
+TEXT	saveureg(SB), $-4
+/*
+ * save state
+ */
+	MOVMW	R2, 48(R1)	/* r2:r31 */
+	MOVW	$setSB(SB), R2
+	MOVW	SPR(SAVER1), R4
+	MOVW	R4, 44(R1)
+	MOVW	SPR(SAVER0), R5
+	MOVW	R5, 40(R1)
+	MOVW	CTR, R6
+	MOVW	R6, 36(R1)
+	MOVW	XER, R4
+	MOVW	R4, 32(R1)
+	MOVW	CR, R5
+	MOVW	R5, 28(R1)
+	MOVW	SPR(SAVELR), R6	/* LR */
+	MOVW	R6, 24(R1)
+	/* pad at 20(R1) */
+	/* old PC(16) and status(12) saved earlier */
+	MOVW	SPR(SAVEXX), R0
+	MOVW	R0, 8(R1)	/* cause/vector */
+	ADD	$8, R1, R3	/* Ureg* */
+	STWCCC	R3, (R1)	/* break any pending reservations */
+	MOVW	$0, R0	/* compiler/linker expect R0 to be zero */
+
+	MOVW	MSR, R5
+	OR	$(MSR_IR|MSR_DR), R5	/* enable MMU */
+	MOVW	R5, SPR(SRR1)
+	MOVW	LR, R31
+	OR	$KZERO, R31	/* return PC in KSEG0 */
+	MOVW	R31, SPR(SRR0)
+	SYNC
+	ISYNC
+	RFI	/* returns to trap handler */
+
+TEXT	icflush(SB), $-4	/* icflush(virtaddr, count) */
+	MOVW	n+4(FP), R4
+	CMP	R4, R0
+	BLE	icf1
+	RLWNM	$0, R3, $~(CACHELINESZ-1), R5
+	SUB	R5, R3
+	ADD	R3, R4
+	ADD		$(CACHELINESZ-1), R4
+	SRAW	$CACHELINELOG, R4
+	MOVW	R4, CTR
+icf0:	ICBI	(R5)
+	ADD	$CACHELINESZ, R5
+	BDNZ	icf0
+icf1:
+	ISYNC
+	RETURN
+
+/*
+ * flush to store and invalidate globally
+ */
+TEXT	dcflush(SB), $-4	/* dcflush(virtaddr, count) */
+	SYNC
+	MOVW	n+4(FP), R4
+	RLWNM	$0, R3, $~(CACHELINESZ-1), R5
+	CMP	R4, $0
+	BLE	dcf1
+	SUB	R5, R3
+	ADD	R3, R4
+	ADD		$(CACHELINESZ-1), R4
+	SRAW	$CACHELINELOG, R4
+	MOVW	R4, CTR
+dcf0:	DCBF	(R5)
+	ADD	$CACHELINESZ, R5
+	BDNZ	dcf0
+	SYNC
+dcf1:
+	ISYNC
+	MOVW	R5, R3	/* check its operation */
+	RETURN
+
+/*
+ * invalidate without flush, globally
+ */
+TEXT	dcinval(SB), $-4	/* dcinval(virtaddr, count) */
+	SYNC
+	MOVW	n+4(FP), R4
+	RLWNM	$0, R3, $~(CACHELINESZ-1), R5
+	CMP	R4, $0
+	BLE	dci1
+	SUB	R5, R3
+	ADD	R3, R4
+	ADD		$(CACHELINESZ-1), R4
+	SRAW	$CACHELINELOG, R4
+	MOVW	R4, CTR
+dci0:	DCBI	(R5)
+	ADD	$CACHELINESZ, R5
+	BDNZ	dci0
+	SYNC
+	ISYNC
+dci1:
+	RETURN
+
+TEXT	dccci(SB), $-4
+	SYNC
+	DCCCI(0, 3)
+	ISYNC
+	RETURN
+
+TEXT	_tas(SB), $0
+	SYNC
+	MOVW	R3, R4
+	MOVW	$0xdeaddead,R5
+tas1:
+	DCBF	(R4)	/* fix for 603x bug */
+	LWAR	(R4), R3
+	CMP	R3, $0
+	BNE	tas0
+	STWCCC	R5, (R4)
+	BNE	tas1
+tas0:
+	SYNC
+	ISYNC
+	RETURN
+
+TEXT	gettbl(SB), $0
+	MFTB(TBRL, 3)
+	RETURN
+
+TEXT	gettbu(SB), $0
+	MFTB(TBRU, 3)
+	RETURN
+
+TEXT	getpvr(SB), $0
+	MOVW	SPR(PVR), R3
+	RETURN
+
+TEXT	getcallerpc(SB), $-4
+	MOVW	0(R1), R3
+	RETURN
+
+TEXT getdear(SB), $0
+	MOVW	SPR(DEAR), R3
+	RETURN
+
+TEXT getdsisr(SB), $0
+	MOVW	SPR(DSISR), R3
+	RETURN
+
+TEXT	getmsr(SB), $0
+	MOVW	MSR, R3
+	RETURN
+
+TEXT	putmsr(SB), $0
+	SYNC
+	MOVW	R3, MSR
+	MSRSYNC
+	RETURN
+
+TEXT	putevpr(SB), $0
+	MOVW	R3, SPR(EVPR)
+	RETURN
+
+TEXT	getesr(SB), $0
+	MOVW	SPR(ESR), R3
+	RETURN
+
+TEXT	putesr(SB), $0
+	MOVW	R3, SPR(ESR)
+	RETURN
+
+TEXT	getpit(SB), $0
+	MOVW	SPR(PIT), R3
+	RETURN
+
+TEXT	putpit(SB), $0
+	MOVW	R3, SPR(PIT)
+	RETURN
+
+TEXT	gettsr(SB), $0
+	MOVW	SPR(TSR), R3
+	RETURN
+
+TEXT	puttsr(SB), $0
+	MOVW	R3, SPR(TSR)
+	RETURN
+
+TEXT	puttcr(SB), $0
+	MOVW	R3, SPR(TCR)
+	RETURN
+
+TEXT	eieio(SB), $0
+	EIEIO
+	RETURN
+
+TEXT	gotopc(SB), $0
+	MOVW	R3, CTR
+	MOVW	LR, R31	/* for trace back */
+	BR	(CTR)
+
+TEXT getccr0(SB), $-4
+	MOVW	SPR(CCR0), R3
+	RETURN
+
+TEXT dcread(SB), $-4
+	MOVW	4(FP), R4
+	MOVW	SPR(CCR0), R5
+	RLWNM	$0, R5, $~0xFF, R5
+	OR	R4, R5
+	MOVW	R5, SPR(CCR0)
+	SYNC
+	ISYNC
+	DCREAD(3, 3)
+	RETURN
+
+TEXT	getdcr(SB), $-4
+	MOVW	$_getdcr(SB), R5
+	SLW	$3, R3
+	ADD	R3, R5
+	MOVW	R5, CTR
+	BR	(CTR)
+
+TEXT	putdcr(SB), $-4
+	MOVW	$_putdcr(SB), R5
+	SLW	$3, R3
+	ADD	R3, R5
+	MOVW	R5, CTR
+	MOVW	8(R1), R3
+	BR	(CTR)
+
+TEXT	firmware(SB), $0
+	MOVW	$(3<<28), R3
+	MOVW	R3, SPR(DBCR0)	/* system reset */
+	BR	0(PC)
+
+/*
+ * byte swapping of arrays of long and short;
+ * could possibly be avoided with more changes to drivers
+ */
+TEXT	swabl(SB), $0
+	MOVW	v+4(FP), R4
+	MOVW	n+8(FP), R5
+	SRAW	$2, R5, R5
+	MOVW	R5, CTR
+	SUB	$4, R4
+	SUB	$4, R3
+swabl1:
+	ADD	$4, R3
+	MOVWU	4(R4), R7
+	MOVWBR	R7, (R3)
+	BDNZ	swabl1
+	RETURN
+
+TEXT	swabs(SB), $0
+	MOVW	v+4(FP), R4
+	MOVW	n+8(FP), R5
+	SRAW	$1, R5, R5
+	MOVW	R5, CTR
+	SUB	$2, R4
+	SUB	$2, R3
+swabs1:
+	ADD	$2, R3
+	MOVHZU	2(R4), R7
+	MOVHBR	R7, (R3)
+	BDNZ	swabs1
+	RETURN
+
+TEXT	legetl(SB), $0
+	MOVWBR	(R3), R3
+	RETURN
+
+TEXT	lesetl(SB), $0
+	MOVW	v+4(FP), R4
+	MOVWBR	R4, (R3)
+	RETURN
+
+TEXT	legets(SB), $0
+	MOVHBR	(R3), R3
+	RETURN
+
+TEXT	lesets(SB), $0
+	MOVW	v+4(FP), R4
+	MOVHBR	R4, (R3)
+	RETURN
+
+TEXT	itlbmiss(SB), $-4
+	BR	traps
+
+TEXT	dtlbmiss(SB), $-4
+	BR	traps
+
+/*
+ * traps force memory mapping off.
+ * this code goes to much effort to restore it;
+ * (a little more effort than needed for the Inferno environment)
+ */
+TEXT	trapvec(SB), $-4
+traps:
+	MOVW	LR, R0
+
+pagefault:
+
+/*
+ * map data virtually and make space to save
+ */
+	MOVW	R0, SPR(SAVEXX)	/* vector */
+trapcomm:
+	MOVW	R1, SPR(SAVER1)
+	SYNC
+	ISYNC
+	MOVW	MSR, R0
+	OR	$(MSR_DR|MSR_ME), R0		/* make data space usable */
+	SYNC
+	MOVW	R0, MSR
+	MSRSYNC
+	SUB	$UREGSPACE, R1
+
+	MOVW	SPR(SRR0), R0	/* save SRR0/SRR1 now, since DLTB might be missing stack page */
+	MOVW	R0, LR
+	MOVW	SPR(SRR1), R0
+	RLWNM	$0, R0, $~MSR_WE, R0	/* remove wait state */
+	MOVW	R0, 12(R1)	/* save status: could take DLTB miss here */
+	MOVW	LR, R0
+	MOVW	R0, 16(R1)	/* old PC */
+	BL	saveureg(SB)
+	BL	trap(SB)
+	BR	restoreureg
+
+/*
+ * critical trap/interrupt
+ */
+TEXT	trapcvec(SB), $-4
+	MOVW	LR, R0
+	/* for now we'll just restore the state to the conventions that trap expects, since we don't use critical intrs yet */
+	MOVW	R0, SPR(SAVEXX)
+	MOVW	SPR(SRR2), R0
+	MOVW	R0, SPR(SRR0)
+	MOVW	SPR(SRR3), R0
+	MOVW	R0, SPR(SRR1)
+	BR	trapcomm
+
+TEXT	intrvec(SB), $-4
+	MOVW	LR, R0
+
+/*
+ * map data virtually and make space to save
+ */
+	MOVW	R0, SPR(SAVEXX)	/* vector */
+	MOVW	R1, SPR(SAVER1)
+	SYNC
+	ISYNC
+	MOVW	MSR, R0
+	OR	$MSR_DR, R0		/* make data space usable */
+	SYNC
+	MOVW	R0, MSR
+	MSRSYNC
+	SUB	$UREGSPACE, R1
+
+	MFTB(TBRL, 0)
+	MOVW	R0, intrtbl(SB)
+
+	MOVW	SPR(SRR0), R0
+	MOVW	R0, LR
+	MOVW	SPR(SRR1), R0
+	RLWNM	$0, R0, $~MSR_WE, R0	/* remove wait state */
+	MOVW	R0, 12(R1)
+	MOVW	LR, R0
+	MOVW	R0, 16(R1)
+	BL	saveureg(SB)
+
+	MFTB(TBRL, 5)
+	MOVW	R5, isavetbl(SB)
+
+	BL	intr(SB)
+
+/*
+ * restore state from Ureg and return from trap/interrupt
+ */
+restoreureg:
+	MOVMW	48(R1), R2	/* r2:r31 */
+	/* defer R1 */
+	MOVW	40(R1), R0
+	MOVW	R0, SPR(SAVER0)
+	MOVW	36(R1), R0
+	MOVW	R0, CTR
+	MOVW	32(R1), R0
+	MOVW	R0, XER
+	MOVW	28(R1), R0
+	MOVW	R0, CR	/* CR */
+	MOVW	24(R1), R0
+	MOVW	R0, SPR(SAVELR)	/* LR */
+	/* pad, skip */
+	MOVW	16(R1), R0
+	MOVW	R0, SPR(SRR0)	/* old PC */
+	MOVW	12(R1), R0
+	MOVW	R0, SPR(SRR1)	/* old MSR */
+	/* cause, skip */
+	MOVW	44(R1), R1	/* old SP */
+	MOVW	SPR(SAVELR), R0
+	MOVW	R0, LR
+	MOVW	SPR(SAVER0), R0
+	RFI
+
+TEXT mul64fract(SB), $0
+	MOVW	a0+8(FP), R9
+	MOVW	a1+4(FP), R10
+	MOVW	b0+16(FP), R4
+	MOVW	b1+12(FP), R5
+
+	MULLW	R10, R5, R13		/* c2 = lo(a1*b1) */
+
+	MULLW	R10, R4, R12		/* c1 = lo(a1*b0) */
+	MULHWU	R10, R4, R7		/* hi(a1*b0) */
+	ADD	R7, R13			/* c2 += hi(a1*b0) */
+
+	MULLW	R9, R5, R6		/* lo(a0*b1) */
+	MULHWU	R9, R5, R7		/* hi(a0*b1) */
+	ADDC	R6, R12			/* c1 += lo(a0*b1) */
+	ADDE	R7, R13			/* c2 += hi(a0*b1) + carry */
+
+	MULHWU	R9, R4, R7		/* hi(a0*b0) */
+	ADDC	R7, R12			/* c1 += hi(a0*b0) */
+	ADDE	R0, R13			/* c2 += carry */
+
+	MOVW	R12, 4(R3)
+	MOVW	R13, 0(R3)
+	RETURN
+
+GLOBL	mach0+0(SB), $MACHSIZE
+GLOBL	spltbl+0(SB), $4
+GLOBL	intrtbl+0(SB), $4
+GLOBL	isavetbl+0(SB), $4
--- /dev/null
+++ b/os/cerf405/main.c
@@ -1,0 +1,719 @@
+#include	"u.h"
+#include	"../port/lib.h"
+#include	"mem.h"
+#include	"dat.h"
+#include	"fns.h"
+#include	"io.h"
+#include	"ureg.h"
+#include	"../ip/ip.h"
+#include "version.h"
+
+#define	MAXCONF		32
+
+extern ulong kerndate;
+extern int cflag;
+int	remotedebug;
+
+extern int main_pool_pcnt;
+extern int heap_pool_pcnt;
+extern int image_pool_pcnt;
+
+char *confname[MAXCONF];
+char *confval[MAXCONF];
+int nconf;
+
+void addconf(char *, char *);
+void	eepromscan(void);
+
+static void
+options(void)
+{
+//	nconf = archconfval(confname, confval, sizeof(confname));
+}
+
+void
+doc(char *m)
+{
+	USED(m);
+	iprint("%s...\n", m);
+}
+
+void
+idoc(char *m)
+{
+	uartputs(m, strlen(m));
+}
+
+static void
+poolsizeinit(void)
+{
+	ulong nb;
+
+	nb = conf.npage*BY2PG;
+	poolsize(mainmem, (nb*main_pool_pcnt)/100, 0);
+	poolsize(heapmem, (nb*heap_pool_pcnt)/100, 0);
+	poolsize(imagmem, (nb*image_pool_pcnt)/100, 1);
+}
+
+static void
+serialconsole(void)
+{
+	char *p;
+	int port, baud;
+
+	p = getconf("console");
+	if(p == nil)
+		p = "0";
+	if(p != nil && !remotedebug){
+		port = strtol(p, nil, 0);
+		baud = 115200;
+		p = getconf("baud");
+		if(p != nil){
+			baud = strtol(p, nil, 0);
+			if(baud < 9600)
+				baud = 9600;
+		}
+		uartspecial(port, baud, &kbdq, &printq, kbdcr2nl);
+	}
+}
+
+void
+main(void)
+{
+	idoc("machinit...\n");
+	machinit();
+	idoc("options...\n");
+	compiledcr();
+	options();
+//	archinit();
+	quotefmtinstall();
+	idoc("confinit...\n");
+	confinit();
+	xinit();
+	poolsizeinit();
+	poolinit();
+	idoc("trapinit...\n");
+	trapinit();
+	mmuinit();
+	ioinit();
+	printinit();
+	uartinstall();
+	serialconsole();
+	pcimapinit();
+	eepromscan();
+	doc("clockinit");
+	clockinit();
+	doc("procinit");
+	procinit();
+	cpuidprint();
+	doc("links");
+	links();
+	doc("chandevreset");
+	chandevreset();
+
+	eve = strdup("inferno");
+
+	print("\nInferno %s\n", VERSION);
+	print("Vita Nuova\n");
+	print("conf %s (%lud) jit %d\n\n",conffile, kerndate, cflag);
+
+	doc("userinit");
+	userinit();
+	doc("schedinit");
+	schedinit();
+}
+
+//ccdv=1 cbdv=2 opdv=2 epdv=3 mpdv=1 ppdv=2
+
+void
+machinit(void)
+{
+	int n;
+
+	n = m->machno;
+	memset(m, 0, sizeof(Mach));
+	m->machno = n;
+	m->mmask = 1<<m->machno;
+	m->cputype = getpvr()>>16;
+	m->delayloop = 20000;	/* initial estimate only; set by clockinit */
+	m->speed = 266;	/* initial estimate only; set by archinit */
+	m->cpuhz = 266333333;
+	m->vcohz = 799000000;
+	m->pllhz = 266333333;
+	m->plbhz = 133166666;
+	m->opbhz = 66600000;
+	m->epbhz = 44*MHz;
+	m->pcihz = 66600000;
+	m->clockgen = m->cpuhz;	/* it's the internal cpu clock */
+}
+
+void
+init0(void)
+{
+	Osenv *o;
+	int i;
+	char buf[2*KNAMELEN];
+
+	up->nerrlab = 0;
+
+	spllo();
+
+	if(waserror())
+		panic("init0");
+	/*
+	 * These are o.k. because rootinit is null.
+	 * Then early kproc's will have a root and dot.
+	 */
+	o = up->env;
+	o->pgrp->slash = namec("#/", Atodir, 0, 0);
+	cnameclose(o->pgrp->slash->name);
+	o->pgrp->slash->name = newcname("/");
+	o->pgrp->dot = cclone(o->pgrp->slash);
+
+	chandevinit();
+
+	if(!waserror()){
+		ksetenv("cputype", "power", 0);
+		snprint(buf, sizeof(buf), "power %s", conffile);
+		ksetenv("terminal", buf, 0);
+		poperror();
+	}
+	for(i = 0; i < nconf; i++)
+		if(confname[i][0] != '*'){
+			if(!waserror()){
+				ksetenv(confname[i], confval[i], 0);
+				poperror();
+			}
+		}
+
+	poperror();
+	disinit("/osinit.dis");
+}
+
+void
+userinit(void)
+{
+	Proc *p;
+	Osenv *o;
+
+	p = newproc();
+	o = p->env;
+
+	o->fgrp = newfgrp(nil);
+	o->pgrp = newpgrp();
+	o->egrp = newegrp();
+	kstrdup(&o->user, eve);
+
+	strcpy(p->text, "interp");
+
+	/*
+	 * Kernel Stack
+	 */
+	p->sched.pc = (ulong)init0;
+	p->sched.sp = (ulong)p->kstack+KSTACK;
+
+	ready(p);
+}
+
+Conf	conf;
+
+void
+addconf(char *name, char *val)
+{
+	if(nconf >= MAXCONF)
+		return;
+	confname[nconf] = name;
+	confval[nconf] = val;
+	nconf++;
+}
+
+char*
+getconf(char *name)
+{
+	int i;
+
+	for(i = 0; i < nconf; i++)
+		if(cistrcmp(confname[i], name) == 0)
+			return confval[i];
+	return 0;
+}
+
+void
+confinit(void)
+{
+	char *p;
+	int pcnt;
+
+
+	if(p = getconf("*kernelpercent"))
+		pcnt = 100 - strtol(p, 0, 0);
+	else
+		pcnt = 0;
+
+	archconfinit();
+	
+	conf.npage = conf.npage0 + conf.npage1;
+	if(pcnt < 10)
+		pcnt = 70;
+	conf.ialloc = (((conf.npage*(100-pcnt))/100)/2)*BY2PG;
+
+	conf.nproc = 100 + ((conf.npage*BY2PG)/MB)*5;
+	conf.nmach = MAXMACH;
+
+}
+
+static void
+twinkle(void)
+{
+	if(m->ticks%MS2TK(1000) == 0)
+		((Gpioregs*)PHYSGPIO)->or ^= 1<<31;
+}
+
+void (*archclocktick)(void) = twinkle;
+
+void
+exit(int ispanic)
+{
+	up = 0;
+	spllo();
+	print("cpu %d exiting\n", m->machno);
+
+	/* Shutdown running devices */
+	chandevshutdown();
+
+	delay(1000);
+	splhi();
+	if(ispanic)
+		for(;;);
+	archreboot();
+}
+
+void
+reboot(void)
+{
+	exit(0);
+}
+
+void
+halt(void)
+{
+	print("cpu halted\n");
+	microdelay(1000);
+	for(;;)
+		;
+}
+
+/*
+ * kept in case it's needed for PCI/ISA devices
+ */
+int
+isaconfig(char *class, int ctlrno, ISAConf *isa)
+{
+	char cc[KNAMELEN], *p;
+	int i;
+
+	snprint(cc, sizeof cc, "%s%d", class, ctlrno);
+	p = getconf(cc);
+	if(p == nil)
+		return 0;
+
+	isa->nopt = tokenize(p, isa->opt, NISAOPT);
+	for(i = 0; i < isa->nopt; i++){
+		p = isa->opt[i];
+		if(cistrncmp(p, "type=", 5) == 0)
+			isa->type = p + 5;
+		else if(cistrncmp(p, "port=", 5) == 0)
+			isa->port = strtoul(p+5, &p, 0);
+		else if(cistrncmp(p, "irq=", 4) == 0)
+			isa->irq = strtoul(p+4, &p, 0);
+		else if(cistrncmp(p, "mem=", 4) == 0)
+			isa->mem = strtoul(p+4, &p, 0);
+		else if(cistrncmp(p, "size=", 5) == 0)
+			isa->size = strtoul(p+5, &p, 0);
+		else if(cistrncmp(p, "freq=", 5) == 0)
+			isa->freq = strtoul(p+5, &p, 0);
+		else if(cistrncmp(p, "dma=", 4) == 0)
+			isa->dma = strtoul(p+4, &p, 0);
+	}
+	return 1;
+}
+
+/*
+ *  Save the mach dependent part of the process state.
+ */
+void
+procsave(Proc*)
+{
+}
+
+void
+idlehands(void)
+{
+	putmsr(getmsr() | MSR_WE | MSR_EE | MSR_CE);	/* MSR_DE as well? */
+}
+
+/* stubs */
+void
+setfsr(ulong)
+{
+}
+
+ulong
+getfsr()
+{
+	return 0;
+}
+
+void
+setfcr(ulong)
+{
+}
+
+ulong
+getfcr()
+{
+	return 0;
+}
+
+/*
+ * some of this is possibly ice-cube specific
+ */
+
+enum {
+	Cpc0Pllmr0=	0xF0,	/* PLL mode register 0 */
+	Cpc0Boot=	0xF1,	/* clock status */
+	Cpc0Pllmr1=	0xF4,	/* PLL mode register 1 */
+	Cpc0Srr=		0xF6,	/* PCI soft reset */
+	Cpc0PCI=		0xF9,	/* PCI control */
+};
+/*
+00f0 = 00011101
+00f1 = 00000025
+00f2 = 00000000
+00f3 = 00000000
+00f4 = 8085523e
+00f5 = 00000017
+00f6 = 00000000
+ccdv=1 cbdv=2 opdv=2 epdv=3 mpdv=1 ppdv=2
+fbmul=8 fwdva=5 fwdvb=5 tun=257 m=40
+*/
+void
+archconfinit(void)
+{
+	ulong ktop;
+
+	conf.npage0 = (32*1024*1024)/BY2PG;
+	conf.base0 = 0;
+	ktop = PGROUND((ulong)end);
+	ktop = PADDR(ktop) - conf.base0;
+	conf.npage0 -= ktop/BY2PG;
+	conf.base0 += ktop;
+
+	{int i; for(i=0xF0; i<=0xF6; i++){iprint("%.4ux = %.8lux\n", i, getdcr(i));}}
+	{
+		int ccdv, cbdv, opdv, epdv, mpdv, ppdv;
+		int fbmul, fwdva, fwdvb, tun;
+		ulong mr0, mr1;
+
+		mr0 = getdcr(Cpc0Pllmr0);
+		ccdv = ((mr0>>20)&3)+1;
+		cbdv = ((mr0>>16)&3)+1;
+		opdv = ((mr0>>12)&3)+1;
+		epdv = ((mr0>>8)&3)+2;
+		mpdv = ((mr0>>4)&3)+1;
+		ppdv = (mr0&3)+1;
+		iprint("ccdv=%d cbdv=%d opdv=%d epdv=%d mpdv=%d ppdv=%d\n",
+			ccdv, cbdv, opdv, epdv, mpdv, ppdv);
+		mr1 = getdcr(Cpc0Pllmr1);
+		fbmul = (mr1>>20) & 0xF;
+		if(fbmul == 0)
+			fbmul = 16;
+		fwdva = (mr1>>16) & 7;
+		if(fwdva == 0)
+			fwdva = 8;
+		fwdvb = (mr1>>12) & 7;
+		if(fwdvb == 0)
+			fwdvb = 8;
+		tun = mr0 & 0x3FF;
+		iprint("fbmul=%d fwdva=%d fwdvb=%d tun=%d m=%d\n",
+			fbmul, fwdva, fwdvb, tun, fbmul*fwdva);
+	}
+}
+
+void
+archreboot(void)
+{
+	putevpr(~0);
+	firmware(0);
+	for(;;);
+}
+
+void
+clockcheck(void)
+{
+}
+
+void
+cpuidprint(void)
+{
+	iprint("PowerPC 405EP pvr=%8.8lux\n", getpvr());
+	/* TO DO */
+}
+
+#include	"../port/flashif.h"
+
+/*
+ * for devflash.c:/^flashreset
+ * retrieve flash type, virtual base and length and return 0;
+ * return -1 on error (no flash)
+ */
+int
+archflashreset(int bank, Flash *f)
+{
+	switch(bank){
+	case 0:
+		f->type = "AMD29F0x0";	/* not right, but will do for now */
+		f->addr = (void*)PHYSFLASH;
+		f->size = FLASHSIZE;
+		f->width = 2;
+		return 0;
+	case 1:
+		f->type = "nand";
+		f->addr = (void*)PHYSNAND;
+		f->size = 0;	/* done by probe */
+		f->width = 1;
+		return 0;
+	default:
+		return -1;
+	}
+}
+
+void
+archflashwp(Flash*, int)
+{
+}
+
+#include "../port/netif.h"
+#include "etherif.h"
+
+enum {
+	/* EMAC-PHY control, tucked away in CPC0 */
+	Cpc0Epctl=	0xF3,	/* EMAC-PHY ctl */
+
+	E0Nf=	1<<31,	/* Emac0 noise filter enable */
+	E1Nf=	1<<30,	/* Emac1 noise filter enable */
+	E1pr=	1<<7,	/* Emac1 packet reject is active high */
+	E0pr=	1<<6,	/* Emac 0 packet reject is active high */
+	E1rm=	1<<5,	/* enable Emac 1 packet removal */
+	E0rm=	1<<4,	/* enable Emac 0 packet removal */
+	E1pci=	1<<1,	/* Emac 1 clock source is Tx clock output (loopback) */
+	E0pci=	1<<0,	/* Emac 0 clock source is Tx clock output (loopback) */
+};
+
+int
+archether(int ctlno, Ether *ether)
+{
+	char name[KNAMELEN], *p;
+	int s;
+
+	if(ctlno > 1)
+		return -1;
+	ether->type = "EMAC";
+	ether->port = ctlno;
+	if(ctlno != 0)
+		snprint(name, sizeof(name), "eth%daddr", ctlno);
+	else
+		strcpy(name, "ethaddr");
+	p = getconf(name);
+	if(p == 0){
+		iprint("ether%d: no %s in EEPROM env\n", ctlno, name);
+		return -1;
+	}
+	parsemac(ether->ea, p, Eaddrlen);
+	s = splhi();
+	putdcr(Cpc0Epctl, getdcr(Cpc0Epctl) | (ctlno?E1Nf:E0Nf));
+	splx(s);
+	return 1;
+}
+
+enum {
+	/* UART control */
+	Cpc0Ucr=		0xF5,	/* UART control register */
+
+	U0Dc=		1<<21,	/* UART0 DMA clear enable */
+	U0Dt=		1<<20,	/* enable UART0 DMA transmit channel */
+	U0Dr=		1<<19,	/* enable UART0 DMA receive channel */
+	U1Dc=		1<<18,	/* UART1 DMA clear enable */
+	U1Dt=		1<<17,	/* enable UART1 DMA transmit channel */
+	U1Dr=		1<<16,	/* enable UART1 DMA receive channel */
+	U1Div_s=		8,		/* UART1 serial clock divisor (shift) */
+	U1Stop=		1<<8,
+	U0Div_s=		0,		/* UART0 serial clock divisor (shift) */
+	U0Stop=		1<<0,
+	UDiv_m=		0x7F,	/* UARTx divisor mask */
+};
+
+static ulong
+findserialclock(int rate, ulong *freq)
+{
+	ulong d, b;
+	ulong serialclock;
+	int actual, e, beste, bestd;
+
+	*freq = 0;
+	if(rate == 0)
+		return 0;
+	d = ((m->pllhz+m->opbhz-1)/m->opbhz)*2;	/* double to allow for later rounding */
+	beste = 0;
+	bestd = -1;
+	for(; d<=128; d++){
+		serialclock = (2*m->pllhz)/d;
+		b = ((serialclock+8*rate-1)/(rate*16))>>1;
+		actual = ((serialclock+8*b-1)/(b*16))>>1;
+		e = rate-actual;
+		if(e < 0)
+			e = -e;
+		if(bestd < 0 || e < beste || e == beste && (bestd&1) && (d&1)==0){
+			beste = e;
+			bestd = d;
+		}
+	}
+	if(bestd > 0)
+		*freq = m->pllhz/bestd;
+	return bestd;
+}
+
+/*
+ * return a value for UARTn's baud rate generator, and
+ * set a corresponding divsor in the UARTn clock generator
+ * (between 2 and 128)
+ */
+ulong
+archuartclock(int n, int rate)
+{
+	int d, s;
+	ulong m, freq;
+
+	d = findserialclock(rate, &freq);
+	if(d <= 0)
+		d = U0Stop;
+	m = UDiv_m;
+	if(n){
+		d <<= U1Div_s;
+		m <<= U1Div_s;
+	}
+	s = splhi();
+	putdcr(Cpc0Ucr, (getdcr(Cpc0Ucr) & ~m) | d);
+	splx(s);
+	return freq;
+}
+
+void
+archuartdma(int n, int on)
+{
+	ulong r;
+	int s;
+
+	r = n? (U1Dc|U1Dt|U1Dr): (U0Dc|U0Dt|U0Dr);
+	if(on){
+		s = splhi();
+		putdcr(Cpc0Ucr, getdcr(Cpc0Ucr) | r);
+		splx(s);
+	}else{
+		s = splhi();
+		putdcr(Cpc0Ucr, getdcr(Cpc0Ucr) & ~r);
+		splx(s);
+	}
+}
+
+/*
+ * boot environment in eeprom
+ */
+
+enum {
+	EEpromHdr=	8,	/* bytes */
+	Envsize=	0x400,
+};
+
+static I2Cdev eedev;
+static struct {
+	uchar	buf[Envsize];
+	int	size;
+} bootenv;
+
+static int
+eepromitem(uchar *buf, int lim, ulong *off)
+{
+	int l;
+	uchar b;
+
+	if(i2crecv(&eedev, &b, 1, (*off)++) != 1)
+		return -1;
+	l = b;
+	if(l & 0x80){
+		if(i2crecv(&eedev, &b, 1, (*off)++) != 1)
+			return -1;
+		l = ((l & 0x7F)<<8) | b;
+	}
+	if(buf == nil)
+		return l;
+	if(l > lim)
+		l = lim;
+	return i2crecv(&eedev, buf, l, *off);
+}
+
+void
+eepromscan(void)
+{
+	int n, l;
+	ulong off;
+	uchar buf[2];
+	char *p, *ep, *v;
+
+	eedev.addr = 0x50;
+	eedev.salen = 2;
+	i2csetup(1);
+	n = i2crecv(&eedev, buf, sizeof(buf), 0);
+	if(n <= 0){
+		iprint("eepromscan: %d\n", n);
+		return;
+	}
+	if(buf[0] != 0xEF || buf[1] != 0xBE){
+		iprint("eeprom invalid\n");
+		return;
+	}
+	bootenv.size = 0;
+	for(off = EEpromHdr; off < 16384;){
+		l = eepromitem(bootenv.buf, sizeof(bootenv.buf), &off);	/* key */
+		if(l <= 0)
+			break;
+		off += l;
+		if(l == 7 && memcmp(bootenv.buf, "PPCBOOT", 7) == 0){	/* intrinsyc key */
+			bootenv.size = eepromitem(bootenv.buf, sizeof(bootenv.buf), &off);
+			break;
+		}
+		l = eepromitem(nil, 0, &off);	/* skip value */
+		if(l < 0)
+			break;
+		off += l+2;	/* 2 byte crc */
+	}
+	p = (char*)bootenv.buf+4;	/* skip crc */
+	ep = p+bootenv.size;
+	for(; p < ep && *p; p += l){
+		l = strlen(p)+1;
+		v = strchr(p, '=');
+		if(v != nil)
+			*v++ = 0;
+		else
+			v = "";
+		addconf(p, v);
+		if(0)
+			iprint("%q = %q\n", p, v);
+	}
+}
+
+ulong
+logfsnow(void)
+{
+	return rtctime();
+}
--- /dev/null
+++ b/os/cerf405/mal.c
@@ -1,0 +1,334 @@
+#include "u.h"
+#include "lib.h"
+#include "mem.h"
+#include "dat.h"
+#include "fns.h"
+#include "io.h"
+
+/*
+ * on the 405EP the MAL is used only by the Ethernet
+ * but we keep it separate even so
+ */
+
+enum {
+	Nrxchan=	2,
+	Ntxchan=	4,
+	Maxchan		= 4
+};
+
+enum {
+	/* device control registers */
+	Cfg=		0x180,	/* configuration register */
+	Esr=		0x181,	/* error status register */
+	Ier=		0x182,	/* interrupt enable register */
+	Txcasr=	 0x184,	/* transmit channel active set register */
+	Txcarr=	 0x185,	/* transmit channel active reset register */
+	Txeobisr= 0x186,	/* transmit end of buffer interrupt status register */
+	Txdeir=	 0x187,	/* transmit descriptor error interrupt register */
+	Rxcasr=	 0x190,	/* receive channel active set register */
+	Rxcarr= 	0x191,	/* receive channel active reset register */
+	Rxeobisr=	 0x192,	/* receive channel descriptor error interrupt register */
+	Rxdeir=	 0x193,	/* receive descriptor error interrupt register */
+};
+
+#define	TXCTPR(n)	(0x1A0+(n))	/* transmit channel table pointer register */
+#define	RXCTPR(n)	(0x1C0+(n))	/* receive channel table pointer register */
+#define	RCBS(n)	(0x1E0+(n))	/* receive channel buffer size register */
+
+enum {
+	/* configuration */
+	CfgSr=		1<<31,	/* software reset */
+	CfgPlbp0=	0<<22,	/* PLB priority (0=lowest) */
+	CfgPlbp1=	1<<22,
+	CfgPlbp2=	2<<22,
+	CfgPlbp3=	3<<22,
+	CfgGa=		1<<21,	/* guarded */
+	CfgOa=		1<<20,	/* ordered */
+	CfgPlble=		1<<19,	/* lock error */
+	CfgPlbt_f=	0xF<<15,	/* latency timer field */
+	CfgPlbt_s=	15,		/* latency timer (shift) */
+	CfgPlbb=		1<<14,	/* burst enable */
+	CfgOpbbl=	1<<7,	/* OPB locked */
+	CfgOepie=	1<<2,	/* interrupt on every end of packet */
+	CfgLea=		1<<1,	/* locked error active */
+	CfgSd=		1<<0,	/* scroll to next packet on early termination */
+
+	/* error status */
+	EsrEvb=		1<<31,	/* error valid bit */
+	EsrCid_f=		0x7F<<25,	/* field: channel ID causing lock error */
+	EsrDe=		1<<20,	/* descriptor error */
+	EsrOne=		1<<19,	/* OPB non-fullword error */
+	EsrOte=		1<<18,	/* OPB timeout error */
+	EsrOse=		1<<17,	/* OPB slave error */
+	EsrPein=		1<<16,	/* PLB bus error indication */
+	EsrDei=		1<<4,	/* descriptor error interrupt */
+	EsrOnei=		1<<3,	/* OPB non-fulword error interrupt */
+	EsrOtei=		1<<2,	/* OPB timeout error interrupt */
+	EsrOsei=		1<<1,	/* OPB slave error interrupt */
+	EsrPbei=		1<<0,	/* OPB bus error interrupt */
+
+};
+
+typedef struct Malmem Malmem;
+struct Malmem {
+	Lock;
+	BD*	base;
+	BD*	limit;
+	BD*	avail;
+};
+
+static Malmem	malmem;
+
+static Mal*	malchans[2][Maxchan];
+
+static void
+errorintr(Ureg*, void*)
+{
+	ulong esr, rxdeir, txdeir;
+
+	/* mal de tête */
+	esr = getdcr(Esr);
+	txdeir = getdcr(Txdeir);
+	rxdeir = getdcr(Rxdeir);
+	iprint("mal: esr=%8.8lux txdeir=%8.8lux rxdeir=%8.8lux\n", esr, txdeir, rxdeir);
+	putdcr(Rxdeir, rxdeir);
+	putdcr(Txdeir, txdeir);
+	putdcr(Esr, esr);
+}
+
+static void
+scanintr(Ureg *ur, ulong ir, Mal *chans[])
+{
+	Mal *ml;
+	int i;
+
+	for(i=0; ir != 0 && i < Maxchan; i++)
+		if(ir & IBIT(i)){
+			ir &= ~IBIT(i);
+			ml = chans[i];
+			if(ml != nil && ml->interrupt != nil)
+				ml->interrupt(ur, ml->arg);
+			/* unexpected interrupt otherwise */
+		}
+}
+
+static void
+txinterrupt(Ureg *ur, void*)
+{
+	ulong ir;
+
+	ir = getdcr(Txeobisr);
+	putdcr(Txeobisr, ir);
+	scanintr(ur, ir, malchans[1]);
+}
+
+static void
+rxinterrupt(Ureg *ur, void*)
+{
+	ulong ir;
+
+	ir = getdcr(Rxeobisr);
+	putdcr(Rxeobisr, ir);
+	scanintr(ur, ir, malchans[0]);
+}
+
+void
+ioinit(void)
+{
+	int i;
+
+	putdcr(Txcarr, ~0);
+	putdcr(Rxcarr, ~0);
+
+	/* reset */
+	putdcr(Cfg, CfgSr);
+	while(getdcr(Cfg) & CfgSr)
+		;	/* at most one system clock */
+
+	/* clear these out whilst we're at it */
+	for(i=0; i<Nrxchan; i++){
+		putdcr(RCBS(i), 0);
+		putdcr(RXCTPR(i), 0);
+	}
+	for(i=0; i<Ntxchan; i++)
+		putdcr(TXCTPR(i), 0);
+
+	putdcr(Cfg, (0xF<<CfgPlbt_s)|CfgPlbb);	/* TO DO: check */
+
+	/* Ier */
+	intrenable(VectorMALSERR, errorintr, nil, BUSUNKNOWN, "malserr");
+	intrenable(VectorMALTXDE, errorintr, nil, BUSUNKNOWN, "maltxde");
+	intrenable(VectorMALRXDE, errorintr, nil, BUSUNKNOWN, "malrxde");
+	intrenable(VectorMALTXEOB, txinterrupt, nil, BUSUNKNOWN, "maltxeob");
+	intrenable(VectorMALRXEOB, rxinterrupt, nil, BUSUNKNOWN, "malrxeob");
+	putdcr(Ier, EsrDei | EsrOnei | EsrOtei | EsrOsei | EsrPbei);
+}
+
+Mal*
+malchannel(int n, int tx, void (*intr)(Ureg*, void*), void *arg)
+{
+	Mal *ml;
+
+	if((ml = malchans[tx][n]) == nil){
+		ml = malloc(sizeof(*m));
+		malchans[tx][n] = ml;
+	}
+	ml->n = n;
+	ml->tx = tx;
+	ml->len = 1;
+	ml->arg = arg;
+	ml->interrupt = intr;
+	return ml;
+}
+
+void
+maltxreset(Mal *ml)
+{
+	putdcr(Txcarr, IBIT(ml->n));
+}
+
+void
+maltxinit(Mal *ml, Ring *r)
+{
+	putdcr(TXCTPR(ml->n), PADDR(r->tdr));
+}
+
+void
+maltxenable(Mal *ml)
+{
+	putdcr(Txcasr, getdcr(Txcasr) | IBIT(ml->n));
+}
+
+void
+malrxreset(Mal *ml)
+{
+	putdcr(Rxcarr, IBIT(ml->n));
+}
+
+void
+malrxinit(Mal *ml, Ring *r, ulong limit)
+{
+	putdcr(RXCTPR(ml->n), PADDR(r->rdr));
+	putdcr(RCBS(ml->n), limit);
+}
+
+void
+malrxenable(Mal *ml)
+{
+	putdcr(Rxcasr, getdcr(Rxcasr) | IBIT(ml->n));
+}
+
+/*
+ * initialise receive and transmit buffer rings
+ * to use both Emacs, or two channels per emac, we'll need
+ * to allocate all rx descriptors at once, and all tx descriptors at once,
+ * in a region where all addresses have the same bits 0-12(!);
+ * see p 20-34. of the MAL chapter.
+ *
+ * the ring entries must be aligned on sizeof(BD) boundaries
+ * rings must be uncached, and buffers must align with cache lines since the cache doesn't snoop
+ *
+ * thus, we initialise it once for all, then hand it out as requested.
+ */
+void
+ioringreserve(int nrx, ulong nrb, int ntx, ulong ntb)
+{
+	ulong nb, nbd;
+
+	lock(&malmem);
+	if(malmem.base == nil){
+		nbd = nrx*nrb + ntx*ntb;
+		nb = mmumapsize(nbd*sizeof(BD));
+		/*
+		 * the data sheet says in the description of buffer tables that they must be on a 4k boundary,
+		 * but the pointer register descriptions say 8 bytes; it seems to be the latter.
+		 */
+		malmem.base = mmucacheinhib(xspanalloc(nb, nb, 1<<19), nb);
+		malmem.limit = malmem.base + nbd;
+		malmem.avail = malmem.base;
+		if((PADDR(malmem.base)&~0x7FFFF) != (PADDR(malmem.base)&~0x7FFFF))
+			print("mal: trouble ahead?\n");
+	}
+	unlock(&malmem);
+	if(malmem.base == nil)
+		panic("ioringreserve");
+}
+
+BD*
+bdalloc(ulong nd)
+{
+	BD *b;
+
+	lock(&malmem);
+	b = malmem.avail;
+	if(b+nd > malmem.limit)
+		b = nil;
+	else
+		malmem.avail = b+nd;
+	unlock(&malmem);
+	return b;
+}
+
+int
+ioringinit(Ring* r, int nrdre, int ntdre)
+{
+	int i;
+
+	/* buffers must align with cache lines since the cache doesn't snoop */
+	r->nrdre = nrdre;
+	if(r->rdr == nil)
+		r->rdr = bdalloc(nrdre);
+	if(r->rxb == nil)
+		r->rxb = malloc(nrdre*sizeof(Block*));
+	if(r->rdr == nil || r->rxb == nil)
+		return -1;
+	for(i = 0; i < nrdre; i++){
+		r->rxb[i] = nil;
+		r->rdr[i].length = 0;
+		r->rdr[i].addr = 0;
+		r->rdr[i].status = BDEmpty|BDInt;
+	}
+	r->rdr[i-1].status |= BDWrap;
+	r->rdrx = 0;
+
+	r->ntdre = ntdre;
+	if(r->tdr == nil)
+		r->tdr = bdalloc(ntdre);
+	if(r->txb == nil)
+		r->txb = malloc(ntdre*sizeof(Block*));
+	if(r->tdr == nil || r->txb == nil)
+		return -1;
+	for(i = 0; i < ntdre; i++){
+		r->txb[i] = nil;
+		r->tdr[i].addr = 0;
+		r->tdr[i].length = 0;
+		r->tdr[i].status = 0;
+	}
+	r->tdr[i-1].status |= BDWrap;
+	r->tdrh = 0;
+	r->tdri = 0;
+	r->ntq = 0;
+	return 0;
+}
+
+void
+dumpmal(void)
+{
+	int i;
+
+	iprint("Cfg=%8.8lux\n", getdcr(Cfg));
+	iprint("Esr=%8.8lux\n", getdcr(Esr));
+	iprint("Ier=%8.8lux\n", getdcr(Ier));
+	iprint("Txcasr=%8.8lux\n", getdcr(Txcasr));
+	iprint("Txcarr=%8.8lux\n", getdcr(Txcarr));
+	iprint("Txeobisr=%8.8lux\n", getdcr(Txeobisr));
+	iprint("Txdeir=%8.8lux\n", getdcr(Txdeir));
+	iprint("Rxcasr=%8.8lux\n", getdcr(Rxcasr));
+	iprint("Rxcarr=%8.8lux\n", getdcr(Rxcarr));
+	iprint("Rxeobisr=%8.8lux\n", getdcr(Rxeobisr));
+	iprint("Rxdeir=%8.8lux\n", getdcr(Rxdeir));
+	for(i=0; i<Nrxchan; i++)
+		iprint("Rxctpr[%d]=%8.8lux Rcbs[%d]=%8.8lux\n", i, getdcr(RXCTPR(i)), i, getdcr(RCBS(i)));
+	for(i=0;i<Ntxchan; i++)
+		iprint("Txctpr[%d]=%8.8lux\n", i, getdcr(TXCTPR(i)));
+}
--- /dev/null
+++ b/os/cerf405/mem.h
@@ -1,0 +1,147 @@
+/*
+ * Memory and machine-specific definitions.  Used in C and assembler.
+ */
+
+/*
+ * Sizes
+ */
+
+#define	BI2BY		8			/* bits per byte */
+#define BI2WD		32			/* bits per word */
+#define	BY2WD		4			/* bytes per word */
+#define	BY2V		8			/* bytes per double word */
+#define	BY2PG		4096			/* bytes per page */
+#define	WD2PG		(BY2PG/BY2WD)		/* words per page */
+#define	PGSHIFT		12			/* log(BY2PG) */
+#define ROUND(s, sz)	(((s)+(sz-1))&~(sz-1))
+#define PGROUND(s)	ROUND(s, BY2PG)
+#define	CACHELINELOG	5
+#define CACHELINESZ	(1<<CACHELINELOG)
+#define	CACHESIZE	16384
+#define	CACHEWAYSIZE	(CACHESIZE/2)	/* 2-way set associative */
+
+#define	MAXMACH		1			/* max # cpus system can run */
+#define	MACHSIZE	BY2PG
+
+/*
+ * Time
+ */
+#define HZ		100			/* clock frequency */
+#define	MS2HZ		(1000/HZ)		/* millisec per clock tick */
+#define	TK2SEC(t)	((t)/HZ)		/* ticks to seconds */
+#define	MS2TK(t)	((t)/MS2HZ)		/* milliseconds to ticks */
+#define	MHz	1000000
+
+/*
+ * 4xx MSR bits
+ */
+
+#define	MSR_WE	0x40000	/* wait state enable */
+#define	MSR_CE	0x20000	/* critical interrupt enable */
+#define	MSR_EE	0x08000	/* enable external/decrementer interrupts */
+#define	MSR_PR	0x04000	/* =1, user mode */
+#define	MSR_ME	0x01000	/* enable machine check exceptions */
+#define	MSR_DWE	0x00400	/* debug wait enable */
+#define	MSR_DE	0x00200	/* debug interrupts enable */
+#define	MSR_IR	0x00020	/* enable instruction address translation */
+#define	MSR_DR	0x00010	/* enable data address translation */
+
+#define	KMSR	(MSR_ME)
+#define	UMSR	(MSR_PR|MSR_DE|MSR_CE|MSR_EE|MSR_IR|MSR_DR)
+
+/*
+ * Magic registers
+ */
+
+#define	MACH	30		/* R30 is m-> */
+#define	USER		29		/* R29 is up-> */
+
+/*
+ * Fundamental addresses
+ */
+
+#define	UREGSIZE	((8+32)*4)
+
+/*
+ * MMU
+ */
+
+/* TLBHI */
+#define	TLBEPN(x)	((x) & ~0x3FF)
+#define	TLB1K		(0<<7)
+#define	TLB4K		(1<<7)
+#define	TLB16K		(2<<7)
+#define	TLB64K		(3<<7)
+#define	TLB256K		(4<<7)
+#define	TLB1MB		(5<<7)
+#define	TLB4MB		(6<<7)
+#define	TLB16MB		(7<<7)
+#define	TLBVALID		(1<<6)
+#define	TLBLE		(1<<5)	/* little-endian */
+#define	TLBU0		(1<<4)	/* user-defined attribute */
+
+/* TLBLO */
+#define	TLBRPN(x)	((x) & ~0x3FF)
+#define	TLBEX		(1<<9)	/* execute enable */
+#define	TLBWR		(1<<8)	/* write enable */
+#define	TLBZONE(x)	((x)<<4)
+#define	TLBW		(1<<3)	/* write-through */
+#define	TLBI			(1<<2)	/* cache inhibit */
+#define	TLBM		(1<<1)	/* memory coherent */
+#define	TLBG		(1<<0)	/* guarded */
+
+/*
+ * Address spaces
+ */
+
+#define	KUSEG	0x00000000
+#define	KSEG0	0x20000000
+#define	KSEG1	0x60000000	/* uncached alias for KSEG0 */
+#define	KSEGM	0xE0000000	/* mask to check which seg */
+
+#define	KZERO	KSEG0			/* base of kernel address space */
+#define	KTZERO	(KZERO+0x3000)	/* first address in kernel text */
+#define	KSTACK	8192	/* Size of kernel stack */
+
+#define	OCMZERO	0x40000000	/* on-chip memory (virtual and physical--see p 5-1) */
+
+/*
+ * Exception codes (trap vectors)
+ */
+#define	CRESET	0x01
+#define	CMCHECK 0x02
+#define	CDSI	0x03
+#define	CISI	0x04
+#define	CEI	0x05
+#define	CALIGN	0x06
+#define	CPROG	0x07
+/* 0x08 (fpu) not used */
+/* 0x09 (dec) not used */
+#define	CSYSCALL 0x0C
+/* 0x0D (trace) not used */
+/* 0x0E (fpa) not used */
+#define	CPIT		0x10
+/* FIT is 0x1010 */
+/* WDT is 0x1020 */
+#define	CDMISS	0x11
+#define	CIMISS	0x12
+#define	CDEBUG	0x20
+
+/*
+ * exception syndrome register
+ */
+#define	ESR_MCI	0x80000000	/* instruction machine check */
+#define	ESR_PIL	0x08000000	/* program interrupt: illegal instruction */
+#define	ESR_PPR	0x04000000	/* program interrupt: privileged */
+#define	ESR_PTR	0x02000000	/* program intterupt: trap with successful compare */
+#define	ESR_DST	0x00800000	/* data storage interrupt: store fault */
+#define	ESR_DIZ	0x00400000	/* data/instruction storage interrupt: zone fault */
+#define	ESR_U0F	0x00008000	/* data storage interrupt: u0 fault */
+
+#include	"physmem.h"
+
+/* cerf-cube specific */
+#define	PHYSDRAM	0
+#define	PHYSFLASH	0xFFE00000
+#define	FLASHSIZE	0x200000
+#define	PHYSNAND	0x60000000
--- /dev/null
+++ b/os/cerf405/mkfile
@@ -1,0 +1,104 @@
+SYSTARG=Inferno
+OBJTYPE=power
+<../../mkconfig
+
+#Configurable parameters
+
+CONF=cerf			#default configuration
+CONFLIST=cerf
+KZERO=0x20003020
+
+SYSTARG=$OSTARG
+OBJTYPE=power
+INSTALLDIR=$ROOT/Inferno/$OBJTYPE/bin	#path of directory where kernel is installed
+#INSTALLDIR=/$OBJTYPE
+
+#end configurable parameters
+
+<$ROOT/mkfiles/mkfile-$SYSTARG-$OBJTYPE	#set vars based on target system
+
+<| $SHELLNAME ../port/mkdevlist $CONF	#sets $IP, $DEVS, $ETHERS, $VGAS, $PORT, $MISC, $LIBS, $OTHERS
+
+OBJ=\
+	l.$O\
+	tlb.$O\
+	nofp.$O\
+	clock.$O\
+	compile.$O\
+	fpi.$O\
+	fpimem.$O\
+	fpipower.$O\
+	gpio.$O\
+	main.$O\
+	mal.$O\
+	mmu.$O\
+	rmap.$O\
+	trap.$O\
+	$CONF.root.$O\
+	$IP\
+	$DEVS\
+	$ETHERS\
+	$LINKS\
+	$VGAS\
+	$PORT\
+	$MISC\
+	$OTHERS\
+
+LIBNAMES=${LIBS:%=lib%.a}
+
+HFILES=\
+	mem.h\
+	physmem.h\
+	dat.h\
+	fns.h\
+	io.h\
+
+CFLAGS=-wFV -I. -I../port -I$ROOT/Inferno/$OBJTYPE/include -I$ROOT/include -I$ROOT/libinterp
+KERNDATE=`{$NDATE}
+
+#default:V: i$CONF.sq
+default:V:	i${CONF}hd
+
+i$CONF:	$OBJ $CONF.c $CONF.root.h $LIBNAMES
+	$CC $CFLAGS '-DKERNDATE='$KERNDATE $CONF.c
+	$LD -o $target -T$KZERO -l -R4 $OBJ $CONF.$O $LIBFILES
+	$KSIZE $target
+
+i$CONF.sq:	i$CONF
+	sqz -w i$CONF >$target
+
+out=i${CONF}hd
+
+i${CONF}hd:	i$CONF
+	mkppcimage i$CONF $out
+
+install:V: i$CONF # i$CONF.sq
+	cp i$CONF $INSTALLDIR/i$CONF
+	#cp i$CONF.sq $INSTALLDIR/i$CONF.sq
+
+uninstall:V:
+	rm -f $ROOT/$OBJDIR/bin/i$CONF
+	rm -f $ROOT/$OBJDIR/bin/i$CONF.sq
+
+<../port/portmkfile
+
+../init/$INIT.dis:	../init/$INIT.b
+		cd ../init; mk $INIT.dis
+
+clock.$O:	$ROOT/Inferno/$OBJTYPE/include/ureg.h
+devether.$O:	$ROOT/Inferno/$OBJTYPE/include/ureg.h
+faultpower.$O:	$ROOT/Inferno/$OBJTYPE/include/ureg.h
+main.$O:	$ROOT/Inferno/$OBJTYPE/include/ureg.h
+trap.$O:	$ROOT/Inferno/$OBJTYPE/include/ureg.h
+
+devether.$O $ETHERS:	etherif.h ../port/netif.h
+archipe.$O:	screen.h archipe.h
+
+#$VGAS:		screen.h vga.h
+$IP devip.$O:		../ip/ip.h
+
+devboot.$O:	devboot.c
+	$CC $CFLAGS devboot.c
+
+devuart.$O:	devuart.c
+	$CC $CFLAGS devuart.c
--- /dev/null
+++ b/os/cerf405/mmu.c
@@ -1,0 +1,122 @@
+#include	"u.h"
+#include	"../port/lib.h"
+#include	"mem.h"
+#include	"dat.h"
+#include	"fns.h"
+
+extern	ulong	tlbtab[], tlbtabe[];
+static	int	tlbx;	/* index of next free entry in TLB */
+
+enum
+{
+	/* on-chip memory dcr */
+	Isarc=	0x018,	/* instruction-side address range compare */
+	Iscntl=	0x019,	/* instruction-side control register */
+	  Isen=		1<<31,	/* enable */
+	Dsarc=	0x01A,	/* data-side address range compare register */
+	Dscntl=	0x01B,	/* data-side control register */
+	  Dsen=		1<<31,	/* enable */
+	  Dof=		1<<30,	/* must be one (p. 5-7) */
+};
+
+void
+mmuinit(void)
+{
+	int i;
+
+	/*
+	 * the l.s initial TLB settings do nearly all that is needed initially.
+	 * clear invalid entries (just for clarity) and record the address
+	 * of the first available
+ 	 */
+	tlbx = -1;
+	for(i = 0; i < 64; i++)
+		if((tlbrehi(i) & TLBVALID) == 0){
+			if(tlbx < 0)
+				tlbx = i;
+			tlbwelo(i, 0);
+			tlbwehi(i, 0);
+		}
+
+	iprint("ccr0=%8.8lux\n", getccr0());
+
+	/*
+	 * set OCM mapping, assuming:
+	 *	caches were invalidated earlier;
+	 *	and we aren't currently using it
+	 * must also set a tlb entry that validates the virtual address but
+	 * the translation is not used (see p. 5-2)
+	 */
+	putdcr(Isarc, OCMZERO);
+	putdcr(Dsarc, OCMZERO);
+	putdcr(Iscntl, Isen);
+	putdcr(Iscntl, Dsen|Dof);
+	tlbwelo(tlbx, OCMZERO|TLBZONE(0)|TLBWR|TLBEX|TLBI);
+	tlbwehi(tlbx, OCMZERO|TLB4K|TLBVALID);
+	tlbx++;
+}
+
+int
+segflush(void *a, ulong n)
+{
+	/* flush dcache then invalidate icache */
+	dcflush(a, n);
+	icflush(a, n);
+	return 0;
+}
+
+/*
+ * return required size and alignment to map n bytes in a tlb entry
+ */
+ulong
+mmumapsize(ulong n)
+{
+	ulong size;
+	int i;
+
+	size = 1024;
+	for(i = 0; i < 8 && size < n; i++)
+		size <<= 2;
+	return size;
+}
+
+/*
+ * map a physical addresses at pa to va, with the given attributes.
+ * the virtual address must not be mapped already.
+ * if va is nil, map it at pa in virtual space.
+ */
+void*
+kmapphys(void *va, ulong pa, ulong nb, ulong attr, ulong le)
+{
+	int s, i;
+	ulong size;
+
+	if(va == nil)
+		va = (void*)pa;	/* simplest is to use a 1-1 map */
+	size = 1024;
+	for(i = 0; i < 8 && size < nb; i++)
+		size <<= 2;
+	if(i >= 8)
+		return nil;
+	s = splhi();
+	tlbwelo(tlbx, pa | TLBZONE(0) | attr);
+	tlbwehi(tlbx, (ulong)va | (i<<7) | TLBVALID | le);
+	tlbx++;
+	splx(s);
+	return va;
+}
+
+/*
+ * return an uncached alias for the memory at a
+ */
+void*
+mmucacheinhib(void *a, ulong nb)
+{
+	ulong p;
+
+	if(a == nil)
+		return nil;
+	dcflush(a, nb);
+	p = PADDR(a);
+	return kmapphys((void*)(KSEG1|p), p, nb, TLBWR | TLBI | TLBG, 0);
+}
--- /dev/null
+++ b/os/cerf405/nand.c
@@ -1,0 +1,96 @@
+#include	"u.h"
+#include	"../port/lib.h"
+#include	"mem.h"
+#include	"dat.h"
+#include	"fns.h"
+#include	"io.h"
+#include	"../port/error.h"
+
+#include	"flashif.h"
+
+/*
+ * Cerf405-specific NAND flash interface
+ */
+
+#define	BE(n)	(1<<(31-(n)))	/* big-endian bit numbering */
+
+enum {
+	/* GPIO lines */
+	Gpio_CLE_o_b=	31,
+	Gpio_ALE_o_b=	30,
+	Gpio_NCE_o_b=	24,	/* CE#, active low */
+	Gpio_RDY_i_b=	23,
+
+	/* bit masks */
+	Gpio_CLE_o=	BE(Gpio_CLE_o_b),
+	Gpio_ALE_o=	BE(Gpio_ALE_o_b),
+	Gpio_NCE_o=	BE(Gpio_NCE_o_b),
+	Gpio_RDY_i=	BE(Gpio_RDY_i_b),
+
+	Gpio_NAND_o=	Gpio_CLE_o | Gpio_ALE_o | Gpio_NCE_o,
+
+	CS_NAND=	1,
+	Gpio_PerCS1_o=	BE(10),
+};
+
+void
+archnand_init(Flash*)
+{
+	gpioreserve(Gpio_NAND_o | Gpio_RDY_i);
+	gpioset(Gpio_NAND_o, Gpio_NCE_o);
+	gpioconfig(Gpio_NAND_o, Gpio_out);
+	gpioconfig(Gpio_RDY_i, Gpio_in);
+}
+
+void
+archnand_claim(Flash*, int claim)
+{
+	gpioset(Gpio_NCE_o, claim? 0: Gpio_NCE_o);
+}
+
+void
+archnand_setCLEandALE(Flash*, int cle, int ale)
+{
+	ulong v;
+
+	v = 0;
+	if(cle)
+		v |= Gpio_CLE_o;
+	if(ale)
+		v |= Gpio_ALE_o;
+	gpioset(Gpio_CLE_o | Gpio_ALE_o, v);
+}
+
+/*
+ * could unroll the loops
+ */
+
+void
+archnand_read(Flash *f, void *buf, int len)
+{
+	uchar *p, *bp;
+
+	p = f->addr;
+	if(buf != nil){
+		bp = buf;
+		while(--len >= 0)
+			*bp++ = *p;
+	}else{
+		int junk;
+		while(--len >= 0){
+			junk = *p;
+			USED(junk);
+		}
+	}
+}
+
+void
+archnand_write(Flash *f, void *buf, int len)
+{
+	uchar *p, *bp;
+
+	p = f->addr;
+	bp = buf;
+	while(--len >= 0)
+		*p = *bp++;
+}
--- /dev/null
+++ b/os/cerf405/nofp.s
@@ -1,0 +1,31 @@
+/*
+ * stubs when no floating-point hardware
+ */
+
+TEXT	kfpinit(SB), $0
+	RETURN
+
+TEXT	getfpscr(SB), $8
+	MOVW	$0, R3
+	RETURN
+
+TEXT	fpsave(SB), $0
+	RETURN
+
+TEXT	fprestore(SB), $0
+	RETURN
+
+TEXT	clrfptrap(SB), $0
+	RETURN
+
+TEXT	fpinit(SB), $0
+	RETURN
+
+TEXT	fpoff(SB), $0
+	RETURN
+
+TEXT	FPsave(SB), 1, $0
+	RETURN
+
+TEXT	FPrestore(SB), 1, $0
+	RETURN
--- /dev/null
+++ b/os/cerf405/pci.c
@@ -1,0 +1,981 @@
+/*
+ * PCI support code.
+ */
+#include "u.h"
+#include "../port/lib.h"
+#include "mem.h"
+#include "dat.h"
+#include "fns.h"
+#include "io.h"
+#include "../port/error.h"
+
+#define DBG	if(0) pcilog
+
+typedef struct Pcicfg Pcicfg;
+struct Pcicfg {
+	ulong	addr;
+	union {
+		ulong	l;
+		uchar	b[4];
+		ushort	s[2];
+	} data;
+};
+
+static Pcicfg*	pcicfg;
+static ulong*	pciack;
+static ulong*	pcimem;
+
+struct
+{
+	char	output[16384];
+	int	ptr;
+}PCICONS;
+
+int
+pcilog(char *fmt, ...)
+{
+	int n;
+	va_list arg;
+	char buf[PRINTSIZE];
+
+	va_start(arg, fmt);
+	n = vseprint(buf, buf+sizeof(buf), fmt, arg) - buf;
+	va_end(arg);
+
+	memmove(PCICONS.output+PCICONS.ptr, buf, n);
+	PCICONS.ptr += n;
+	return n;
+}
+
+enum
+{					/* configuration mechanism #1 */
+	MaxFNO		= 7,
+	MaxUBN		= 255,
+};
+
+enum
+{					/* command register */
+	IOen		= (1<<0),
+	MEMen		= (1<<1),
+	MASen		= (1<<2),
+	MemWrInv	= (1<<4),
+	PErrEn		= (1<<6),
+	SErrEn		= (1<<8),
+};
+
+static Lock pcicfglock;
+static QLock pcicfginitlock;
+static int pcicfgmode = -1;
+static int pcimaxbno = 7;
+static int pcimaxdno;
+static Pcidev* pciroot;
+static Pcidev* pcilist;
+static Pcidev* pcitail;
+
+static int pcicfgrw32(int, int, int, int);
+static int pcicfgrw8(int, int, int, int);
+
+static char* bustypes[] = {
+[BusOPB]	"OPB",
+[BusPLB]	"PLB",
+[BusPCI]	"PCI",
+};
+
+#pragma	varargck	type	"T"	int
+
+static int
+tbdffmt(Fmt* fmt)
+{
+	char *p;
+	int l, r, type, tbdf;
+
+	if((p = malloc(READSTR)) == nil)
+		return fmtstrcpy(fmt, "(tbdfconv)");
+		
+	switch(fmt->r){
+	case 'T':
+		tbdf = va_arg(fmt->args, int);
+		type = BUSTYPE(tbdf);
+		if(type < nelem(bustypes))
+			l = snprint(p, READSTR, bustypes[type]);
+		else
+			l = snprint(p, READSTR, "%d", type);
+		snprint(p+l, READSTR-l, ".%d.%d.%d",
+			BUSBNO(tbdf), BUSDNO(tbdf), BUSFNO(tbdf));
+		break;
+
+	default:
+		snprint(p, READSTR, "(tbdfconv)");
+		break;
+	}
+	r = fmtstrcpy(fmt, p);
+	free(p);
+
+	return r;
+}
+
+ulong
+pcibarsize(Pcidev *p, int rno)
+{
+	ulong v, size;
+
+	v = pcicfgrw32(p->tbdf, rno, 0, 1);
+	pcicfgrw32(p->tbdf, rno, 0xFFFFFFF0, 0);
+	size = pcicfgrw32(p->tbdf, rno, 0, 1);
+	if(v & 1)
+		size |= 0xFFFF0000;
+	pcicfgrw32(p->tbdf, rno, v, 0);
+
+	return -(size & ~0x0F);
+}
+
+static int
+pcisizcmp(void *a, void *b)
+{
+	Pcisiz *aa, *bb;
+
+	aa = a;
+	bb = b;
+	return aa->siz - bb->siz;
+}
+
+static ulong
+pcimask(ulong v)
+{
+	ulong m;
+
+	m = BI2BY*sizeof(v);
+	for(m = 1<<(m-1); m != 0; m >>= 1) {
+		if(m & v)
+			break;
+	}
+
+	m--;
+	if((v & m) == 0)
+		return v;
+
+	v |= m;
+	return v+1;
+}
+
+static void
+pcibusmap(Pcidev *root, ulong *pmema, ulong *pioa, int wrreg)
+{
+	Pcidev *p;
+	int ntb, i, size, rno, hole;
+	ulong v, mema, ioa, sioa, smema, base, limit;
+	Pcisiz *table, *tptr, *mtb, *itb;
+	extern void qsort(void*, long, long, int (*)(void*, void*));
+
+	ioa = *pioa;
+	mema = *pmema;
+
+	DBG("pcibusmap wr=%d %T mem=%luX io=%luX\n", 
+		wrreg, root->tbdf, mema, ioa);
+
+	ntb = 0;
+	for(p = root; p != nil; p = p->link)
+		ntb++;
+
+	ntb *= (PciCIS-PciBAR0)/4;
+	table = malloc(2*ntb*sizeof(Pcisiz));
+	itb = table;
+	mtb = table+ntb;
+
+	/*
+	 * Build a table of sizes
+	 */
+	for(p = root; p != nil; p = p->link) {
+		if(p->ccrb == 0x06) {
+			if(p->ccru == 0x04 && p->bridge != nil) {
+				sioa = ioa;
+				smema = mema;
+				pcibusmap(p->bridge, &smema, &sioa, 0);
+	
+				hole = pcimask(smema-mema);
+				if(hole < (1<<20))
+					hole = 1<<20;
+				p->mema.size = hole;
+	
+				hole = pcimask(sioa-ioa);
+				if(hole < (1<<12))
+					hole = 1<<12;
+	
+				p->ioa.size = hole;
+	
+				itb->dev = p;
+				itb->bar = -1;
+				itb->siz = p->ioa.size;
+				itb++;
+	
+				mtb->dev = p;
+				mtb->bar = -1;
+				mtb->siz = p->mema.size;
+				mtb++;
+			}
+			if((pcicfgr8(p, PciHDT)&0x7f) != 0)
+				continue;
+		}
+
+		for(i = 0; i <= 5; i++) {
+			rno = PciBAR0 + i*4;
+			v = pcicfgrw32(p->tbdf, rno, 0, 1);
+			size = pcibarsize(p, rno);
+			if(size == 0)
+				continue;
+
+			if(v & 1) {
+				itb->dev = p;
+				itb->bar = i;
+				itb->siz = size;
+				itb++;
+			}
+			else {
+				mtb->dev = p;
+				mtb->bar = i;
+				mtb->siz = size;
+				mtb++;
+			}
+
+			p->mem[i].size = size;
+		}
+	}
+
+	/*
+	 * Sort both tables IO smallest first, Memory largest
+	 */
+	qsort(table, itb-table, sizeof(Pcisiz), pcisizcmp);
+	tptr = table+ntb;
+	qsort(tptr, mtb-tptr, sizeof(Pcisiz), pcisizcmp);
+
+	/*
+	 * Allocate IO address space on this bus
+	 */
+	for(tptr = table; tptr < itb; tptr++) {
+		hole = tptr->siz;
+		if(tptr->bar == -1)
+			hole = 1<<12;
+		ioa = (ioa+hole-1) & ~(hole-1);
+
+		p = tptr->dev;
+		if(tptr->bar == -1)
+			p->ioa.bar = ioa;
+		else {
+			p->pcr |= IOen;
+			p->mem[tptr->bar].bar = ioa|1;
+			if(wrreg)
+				pcicfgrw32(p->tbdf, PciBAR0+(tptr->bar*4), ioa|1, 0);
+		}
+
+		ioa += tptr->siz;
+	}
+
+	/*
+	 * Allocate Memory address space on this bus
+	 */
+	for(tptr = table+ntb; tptr < mtb; tptr++) {
+		hole = tptr->siz;
+		if(tptr->bar == -1)
+			hole = 1<<20;
+		mema = (mema+hole-1) & ~(hole-1);
+
+		p = tptr->dev;
+		if(tptr->bar == -1)
+			p->mema.bar = mema;
+		else {
+			p->pcr |= MEMen;
+			p->mem[tptr->bar].bar = mema;
+			if(wrreg)
+				pcicfgrw32(p->tbdf, PciBAR0+(tptr->bar*4), mema, 0);
+		}
+		mema += tptr->siz;
+	}
+
+	*pmema = mema;
+	*pioa = ioa;
+	free(table);
+
+	if(wrreg == 0)
+		return;
+
+	/*
+	 * Finally set all the bridge addresses & registers
+	 */
+	for(p = root; p != nil; p = p->link) {
+		if(p->bridge == nil) {
+			pcicfgrw8(p->tbdf, PciLTR, 64, 0);
+
+			p->pcr |= MASen;
+			pcicfgrw32(p->tbdf, PciPCR, p->pcr, 0);
+			continue;
+		}
+
+		base = p->ioa.bar;
+		limit = base+p->ioa.size-1;
+		v = pcicfgrw32(p->tbdf, PciBAR3, 0, 1);
+		v = (v&0xFFFF0000)|(limit & 0xF000)|((base & 0xF000)>>8);
+		pcicfgrw32(p->tbdf, PciBAR3, v, 0);
+		v = (limit & 0xFFFF0000)|(base>>16);
+		pcicfgrw32(p->tbdf, 0x30, v, 0);
+
+		base = p->mema.bar;
+		limit = base+p->mema.size-1;
+		v = (limit & 0xFFF00000)|((base & 0xFFF00000)>>16);
+		pcicfgrw32(p->tbdf, PciBAR4, v, 0);
+
+		/*
+		 * Disable memory prefetch
+		 */
+		pcicfgrw32(p->tbdf, PciBAR5, 0x0000FFFF, 0);
+		pcicfgrw8(p->tbdf, PciLTR, 64, 0);
+
+		/*
+		 * Enable the bridge
+		 */
+		v = 0xFFFF0000 | IOen | MEMen | MASen;
+		pcicfgrw32(p->tbdf, PciPCR, v, 0);
+
+		sioa = p->ioa.bar;
+		smema = p->mema.bar;
+		pcibusmap(p->bridge, &smema, &sioa, 1);
+	}
+}
+
+static int
+pcilscan(int bno, Pcidev** list)
+{
+	Pcidev *p, *head, *tail;
+	int dno, fno, i, hdt, l, maxfno, maxubn, rno, sbn, tbdf, ubn;
+
+	maxubn = bno;
+	head = nil;
+	tail = nil;
+	for(dno = 0; dno <= pcimaxdno; dno++){
+		maxfno = 0;
+		for(fno = 0; fno <= maxfno; fno++){
+			/*
+			 * For this possible device, form the
+			 * bus+device+function triplet needed to address it
+			 * and try to read the vendor and device ID.
+			 * If successful, allocate a device struct and
+			 * start to fill it in with some useful information
+			 * from the device's configuration space.
+			 */
+			tbdf = MKBUS(BusPCI, bno, dno, fno);
+			l = pcicfgrw32(tbdf, PciVID, 0, 1);
+			if(l == 0xFFFFFFFF || l == 0)
+				continue;
+			p = malloc(sizeof(*p));
+			p->tbdf = tbdf;
+			p->vid = l;
+			p->did = l>>16;
+
+			if(pcilist != nil)
+				pcitail->list = p;
+			else
+				pcilist = p;
+			pcitail = p;
+
+			p->rid = pcicfgr8(p, PciRID);
+			p->ccrp = pcicfgr8(p, PciCCRp);
+			p->ccru = pcicfgr8(p, PciCCRu);
+			p->ccrb = pcicfgr8(p, PciCCRb);
+			p->pcr = pcicfgr32(p, PciPCR);
+
+			p->intl = pcicfgr8(p, PciINTL);
+
+			/*
+			 * If the device is a multi-function device adjust the
+			 * loop count so all possible functions are checked.
+			 */
+			hdt = pcicfgr8(p, PciHDT);
+			if(hdt & 0x80)
+				maxfno = MaxFNO;
+
+			/*
+			 * If appropriate, read the base address registers
+			 * and work out the sizes.
+			 */
+			switch(p->ccrb) {
+			case 0x01:		/* mass storage controller */
+			case 0x02:		/* network controller */
+			case 0x03:		/* display controller */
+			case 0x04:		/* multimedia device */
+			case 0x06:		/* bridge device */
+			case 0x07:		/* simple comm. controllers */
+			case 0x08:		/* base system peripherals */
+			case 0x09:		/* input devices */
+			case 0x0A:		/* docking stations */
+			case 0x0B:		/* processors */
+			case 0x0C:		/* serial bus controllers */
+				if((hdt & 0x7F) != 0)
+					break;
+				rno = PciBAR0 - 4;
+				for(i = 0; i < nelem(p->mem); i++) {
+					rno += 4;
+					p->mem[i].bar = pcicfgr32(p, rno);
+					p->mem[i].size = pcibarsize(p, rno);
+				}
+				break;
+
+			case 0x00:
+			case 0x05:		/* memory controller */
+			default:
+				break;
+			}
+
+			if(head != nil)
+				tail->link = p;
+			else
+				head = p;
+			tail = p;
+		}
+	}
+
+	*list = head;
+	for(p = head; p != nil; p = p->link){
+		/*
+		 * Find PCI-PCI bridges and recursively descend the tree.
+		 */
+		if(p->ccrb != 0x06 || p->ccru != 0x04)
+			continue;
+
+		/*
+		 * If the secondary or subordinate bus number is not
+		 * initialised try to do what the PCI BIOS should have
+		 * done and fill in the numbers as the tree is descended.
+		 * On the way down the subordinate bus number is set to
+		 * the maximum as it's not known how many buses are behind
+		 * this one; the final value is set on the way back up.
+		 */
+		sbn = pcicfgr8(p, PciSBN);
+		ubn = pcicfgr8(p, PciUBN);
+
+		if(sbn == 0 || ubn == 0) {
+			sbn = maxubn+1;
+			/*
+			 * Make sure memory, I/O and master enables are
+			 * off, set the primary, secondary and subordinate
+			 * bus numbers and clear the secondary status before
+			 * attempting to scan the secondary bus.
+			 *
+			 * Initialisation of the bridge should be done here.
+			 */
+			pcicfgw32(p, PciPCR, 0xFFFF0000);
+			l = (MaxUBN<<16)|(sbn<<8)|bno;
+			pcicfgw32(p, PciPBN, l);
+			pcicfgw16(p, PciSPSR, 0xFFFF);
+			maxubn = pcilscan(sbn, &p->bridge);
+			l = (maxubn<<16)|(sbn<<8)|bno;
+
+			pcicfgw32(p, PciPBN, l);
+		}
+		else {
+			maxubn = ubn;
+			pcilscan(sbn, &p->bridge);
+		}
+	}
+
+	return maxubn;
+}
+
+int
+pciscan(int bno, Pcidev **list)
+{
+	int ubn;
+
+	qlock(&pcicfginitlock);
+	ubn = pcilscan(bno, list);
+	qunlock(&pcicfginitlock);
+	return ubn;
+}
+
+static void
+pcicfginit(void)
+{
+	char *p;
+	int bno;
+	Pcidev **list;
+	ulong mema, ioa;
+
+	qlock(&pcicfginitlock);
+	if(pcicfgmode != -1)
+		goto out;
+
+	//pcimmap();
+
+	pcicfgmode = 1;
+	pcimaxdno = 31;
+
+//	fmtinstall('T', tbdffmt);
+
+	if(p = getconf("*pcimaxbno"))
+		pcimaxbno = strtoul(p, 0, 0);
+	if(p = getconf("*pcimaxdno"))
+		pcimaxdno = strtoul(p, 0, 0);
+
+
+	list = &pciroot;
+	for(bno = 0; bno <= pcimaxbno; bno++) {
+		int sbno = bno;
+		bno = pcilscan(bno, list);
+
+		while(*list)
+			list = &(*list)->link;
+
+		if (sbno == 0) {
+			Pcidev *pci;
+
+			/*
+			  * If we have found a PCI-to-Cardbus bridge, make sure
+			  * it has no valid mappings anymore.  
+			  */
+			pci = pciroot;
+			while (pci) {
+				if (pci->ccrb == 6 && pci->ccru == 7) {
+					ushort bcr;
+
+					/* reset the cardbus */
+					bcr = pcicfgr16(pci, PciBCR);
+					pcicfgw16(pci, PciBCR, 0x40 | bcr);
+					delay(50);
+				}
+				pci = pci->link;
+			}
+		}
+	}
+
+	if(pciroot == nil)
+		goto out;
+
+	/*
+	 * Work out how big the top bus is
+	 */
+	mema = 0;
+	ioa = 0;
+	pcibusmap(pciroot, &mema, &ioa, 0);
+
+	DBG("Sizes: mem=%8.8lux size=%8.8lux io=%8.8lux\n",
+		mema, pcimask(mema), ioa);
+
+	/*
+	 * Align the windows and map it
+	 */
+	ioa = 0x1000;
+	mema = 0;
+
+	pcilog("Mask sizes: mem=%lux io=%lux\n", mema, ioa);
+
+	pcibusmap(pciroot, &mema, &ioa, 1);
+	DBG("Sizes2: mem=%lux io=%lux\n", mema, ioa);
+
+out:
+	qunlock(&pcicfginitlock);
+}
+
+static int
+pcicfgrw8(int tbdf, int rno, int data, int read)
+{
+	int o, x;
+
+	if(pcicfgmode == -1)
+		pcicfginit();
+
+	x = -1;
+	if(BUSDNO(tbdf) > pcimaxdno)
+		return x;
+
+	lock(&pcicfglock);
+	o = rno & 0x03;
+	rno &= ~0x03;
+	pcicfg->addr = 0x80000000|BUSBDF(tbdf)|rno;
+	eieio();
+	if(read)
+		x = pcicfg->data.b[o];	/* TO DO: perhaps o^3 */
+	else
+		pcicfg->data.b[o] = data;
+	eieio();
+	pcicfg->addr = 0;
+	unlock(&pcicfglock);
+
+	return x;
+}
+
+int
+pcicfgr8(Pcidev* pcidev, int rno)
+{
+	return pcicfgrw8(pcidev->tbdf, rno, 0, 1);
+}
+
+void
+pcicfgw8(Pcidev* pcidev, int rno, int data)
+{
+	pcicfgrw8(pcidev->tbdf, rno, data, 0);
+}
+
+static int
+pcicfgrw16(int tbdf, int rno, int data, int read)
+{
+	int o, x;
+
+	if(pcicfgmode == -1)
+		pcicfginit();
+
+	x = -1;
+	if(BUSDNO(tbdf) > pcimaxdno)
+		return x;
+
+	lock(&pcicfglock);
+	o = (rno >> 1) & 1;
+	rno &= ~0x03;
+	pcicfg->addr = 0x80000000|BUSBDF(tbdf)|rno;
+	eieio();
+	if(read)
+		x = pcicfg->data.s[o];
+	else
+		pcicfg->data.s[o] = data;
+	eieio();
+	pcicfg->addr = 0;
+	unlock(&pcicfglock);
+
+	return x;
+}
+
+int
+pcicfgr16(Pcidev* pcidev, int rno)
+{
+	return pcicfgrw16(pcidev->tbdf, rno, 0, 1);
+}
+
+void
+pcicfgw16(Pcidev* pcidev, int rno, int data)
+{
+	pcicfgrw16(pcidev->tbdf, rno, data, 0);
+}
+
+static int
+pcicfgrw32(int tbdf, int rno, int data, int read)
+{
+	int x;
+
+	if(pcicfgmode == -1)
+		pcicfginit();
+
+	x = -1;
+	if(BUSDNO(tbdf) > pcimaxdno)
+		return x;
+
+	lock(&pcicfglock);
+	rno &= ~0x03;
+	pcicfg->addr = 0x80000000|BUSBDF(tbdf)|rno;
+	eieio();
+	if(read)
+		x = pcicfg->data.l;
+	else
+		pcicfg->data.l = data;
+	eieio();
+	pcicfg->addr = 0;
+	unlock(&pcicfglock);
+
+	return x;
+}
+
+int
+pcicfgr32(Pcidev* pcidev, int rno)
+{
+	return pcicfgrw32(pcidev->tbdf, rno, 0, 1);
+}
+
+void
+pcicfgw32(Pcidev* pcidev, int rno, int data)
+{
+	pcicfgrw32(pcidev->tbdf, rno, data, 0);
+}
+
+Pcidev*
+pcimatch(Pcidev* prev, int vid, int did)
+{
+	if(pcicfgmode == -1)
+		pcicfginit();
+
+	if(prev == nil)
+		prev = pcilist;
+	else
+		prev = prev->list;
+
+	while(prev != nil){
+		if((vid == 0 || prev->vid == vid)
+		&& (did == 0 || prev->did == did))
+			break;
+		prev = prev->list;
+	}
+	return prev;
+}
+
+Pcidev*
+pcimatchtbdf(int tbdf)
+{
+	Pcidev *pcidev;
+
+	if(pcicfgmode == -1)
+		pcicfginit();
+
+	for(pcidev = pcilist; pcidev != nil; pcidev = pcidev->list) {
+		if(pcidev->tbdf == tbdf)
+			break;
+	}
+	return pcidev;
+}
+
+uchar
+pciipin(Pcidev *pci, uchar pin)
+{
+	if (pci == nil)
+		pci = pcilist;
+
+	while (pci) {
+		uchar intl;
+
+		if (pcicfgr8(pci, PciINTP) == pin && pci->intl != 0 && pci->intl != 0xff)
+			return pci->intl;
+
+		if (pci->bridge && (intl = pciipin(pci->bridge, pin)) != 0)
+			return intl;
+
+		pci = pci->list;
+	}
+	return 0;
+}
+
+static void
+pcilhinv(Pcidev* p)
+{
+	int i;
+	Pcidev *t;
+
+	if(p == nil) {
+		putstrn(PCICONS.output, PCICONS.ptr);
+		p = pciroot;
+		print("bus dev type vid  did intl memory\n");
+	}
+	for(t = p; t != nil; t = t->link) {
+		print("%d  %2d/%d %.2ux %.2ux %.2ux %.4ux %.4ux %3d  ",
+			BUSBNO(t->tbdf), BUSDNO(t->tbdf), BUSFNO(t->tbdf),
+			t->ccrb, t->ccru, t->ccrp, t->vid, t->did, t->intl);
+
+		for(i = 0; i < nelem(p->mem); i++) {
+			if(t->mem[i].size == 0)
+				continue;
+			print("%d:%.8lux %d ", i,
+				t->mem[i].bar, t->mem[i].size);
+		}
+		if(t->ioa.bar || t->ioa.size)
+			print("ioa:%.8lux %d ", t->ioa.bar, t->ioa.size);
+		if(t->mema.bar || t->mema.size)
+			print("mema:%.8lux %d ", t->mema.bar, t->mema.size);
+		if(t->bridge)
+			print("->%d", BUSBNO(t->bridge->tbdf));
+		print("\n");
+	}
+	while(p != nil) {
+		if(p->bridge != nil)
+			pcilhinv(p->bridge);
+		p = p->link;
+	}	
+}
+
+void
+pcihinv(Pcidev* p)
+{
+	if(pcicfgmode == -1)
+		pcicfginit();
+	qlock(&pcicfginitlock);
+	pcilhinv(p);
+	qunlock(&pcicfginitlock);
+}
+
+void
+pcishutdown(void)
+{
+	Pcidev *p;
+
+	if(pcicfgmode == -1)
+		pcicfginit();
+
+	for(p = pcilist; p != nil; p = p->list){
+		/* don't mess with the bridges */
+		if(p->ccrb == 0x06)
+			continue;
+		pciclrbme(p);
+	}
+}
+
+void
+pcisetbme(Pcidev* p)
+{
+	int pcr;
+
+	pcr = pcicfgr16(p, PciPCR);
+	pcr |= MASen;
+	pcicfgw16(p, PciPCR, pcr);
+}
+
+void
+pciclrbme(Pcidev* p)
+{
+	int pcr;
+
+	pcr = pcicfgr16(p, PciPCR);
+	pcr &= ~MASen;
+	pcicfgw16(p, PciPCR, pcr);
+}
+
+/*
+ * 405EP specific
+ */
+
+typedef struct Pciplbregs Pciplbregs;
+struct Pciplbregs {
+	struct {
+		ulong	la;
+		ulong	ma;
+		ulong	pcila;
+		ulong	pciha;
+	} pmm[3];
+	struct {
+		ulong	ms;
+		ulong	la;
+	} ptm[2];
+};
+
+enum {	/* mask/attribute registers */
+	Pre=	1<<1,	/* enable prefetching (PMM only) */
+	Ena=	1<<0,	/* enable PLB to PCI map (PMM); enable PCI to PLB (PTM) */
+};
+
+enum {	/* DCR */
+	Cpc0Srr=	0xF6,	/* PCI soft reset */
+	  Rpci=	1<<18,	/* reset PCI bridge */
+	Cpc0PCI=	0xF9,	/* PCI control */
+	  Spe=	1<<4,	/* PCIINT/WE select */
+	  Hostcfgen=	1<<3,	/* enable host config */
+	  Arben=	1<<1,	/* enable internal arbiter */
+};
+
+enum {
+	/* PciPCR */
+	Se=	1<<8,	/* enable PCISErr# when parity error detected as target */
+	Per=	1<<6,	/* enable PERR# on parity errors */
+	Me=	1<<2,	/* enable bridge-to-master cycles */
+	Ma=	1<<1,	/* enable memory access (when PCI memory target) */
+
+	/* PciPSR */
+	Depe=	1<<15,	/* parity error */
+	Sse=		1<<14,	/* signalled system error */
+	Rma=	1<<13,	/* received master abort */
+	Rta=		1<<12,	/* received target abort */
+	Sta=		1<<11,	/* signalled target abort */
+	Dpe=	1<<8,	/* data parity error */
+	F66C=	1<<5,	/* 66MHz capable */
+
+	/* PciBARn */
+	Pf=		1<<3,	/* prefetchable */
+};
+
+/*
+pmm 0: la=00000080 ma=010000c0 pcila=00000080 pciha=00000000
+pmm 1: la=00000000 ma=00000000 pcila=00000000 pciha=00000000
+pmm 2: la=00000000 ma=00000000 pcila=00000000 pciha=00000000
+ptm 0: ms=01000080 la=00000000
+ptm 1: ms=00000000 la=00000000
+*/
+
+static void
+pcidumpdev(ulong tbdf)
+{
+	int i;
+
+	for(i=0; i<0x68; i+=4)
+		iprint("[%.2x]=%.8ux\n", i, pcicfgrw32(tbdf, i, 0, 1));
+}
+
+void
+pcimapinit(void)
+{
+	Pciplbregs *pm;
+	int i, bridge, psr;
+
+	pm = kmapphys(nil, PHYSPCIBCFG, sizeof(Pciplbregs), TLBWR | TLBI | TLBG, TLBLE);
+	if(0){
+		/* see what the bootstrap left */
+		iprint("PCI:\n");
+		for(i=0; i<3; i++)
+			iprint("pmm %d: la=%.8lux ma=%.8lux pcila=%.8lux pciha=%.8lux\n",
+				i, pm->pmm[i].la, pm->pmm[i].ma, pm->pmm[i].pcila, pm->pmm[i].pciha);
+		for(i=0; i<2; i++)
+			iprint("ptm %d: ms=%.8lux la=%.8lux\n",
+				i, pm->ptm[i].ms, pm->ptm[i].la);
+	}
+	putdcr(Cpc0Srr, Rpci);
+	delay(1);
+	putdcr(Cpc0Srr, 0);
+
+	kmapphys((void*)PHYSPCIIO0, PHYSPCIIO0, 64*1024, TLBWR | TLBI | TLBG, TLBLE);
+	pcicfg = kmapphys(nil, PHYSPCIADDR, sizeof(Pcicfg), TLBWR | TLBI | TLBG, TLBLE);
+	pciack = kmapphys(nil, PHYSPCIACK, sizeof(ulong), TLBWR | TLBI | TLBG, TLBLE);
+	eieio();
+
+	/*
+	 * PLB addresses between PHYSPCIBRIDGE and PHYSPCIBRIDGE+64Mb
+	 * are mapped to PCI memory at 0.
+	 */
+	pm->pmm[0].ma = 0;	/* disable during update */
+	eieio();
+	pm->pmm[0].la = PHYSPCIBRIDGE;
+	pm->pmm[0].pcila = 0;
+	pm->pmm[0].pciha = 0;
+	eieio();
+	pm->pmm[0].ma = 0xFC000000 | Ena;	/* enable prefetch? */
+	for(i=1; i<3; i++)
+		pm->pmm[i].ma = 0;	/* disable the others */
+	eieio();
+	pcimem = kmapphys((void*)PHYSPCIBRIDGE, PHYSPCIBRIDGE, 0x4000000, TLBWR | TLBI | TLBG, TLBLE);
+
+	/*
+	 * addresses presented by a PCI device between PCIWINDOW and PCIWINDOW+1Gb
+	 * are mapped to physical memory.
+ 	*/
+	pm->ptm[0].ms = 0;
+	eieio();
+	pm->ptm[0].la = PCIWINDOW;
+	eieio();
+	pm->ptm[0].ms = 0xC0000000 | Ena;	/* always enabled by hardware */
+	pm->ptm[1].ms = 0;
+	eieio();
+
+	iprint("cpc0pci=%.8lux\n", getdcr(Cpc0PCI));
+
+	/*
+	 * the 405ep's pci bridge contains IBM vendor & devid, but
+	 * ppcboot rather annoyingly clears them; put them back.
+	 */
+	pcicfgmode = 1;
+	pcimaxdno = 31;
+
+	bridge = MKBUS(BusPCI, 0, 0, 0);
+	pcicfgrw16(bridge, PciPCR, Me | Ma, 0);
+	pcicfgrw16(bridge, PciVID, 0x1014, 0);
+	pcicfgrw16(bridge, PciDID, 0x0156, 0);
+	pcicfgrw8(bridge, PciCCRb, 0x06, 0);	/* bridge */
+	pcicfgrw32(bridge, PciBAR1, Pf, 0);
+	psr = pcicfgrw16(bridge, PciPSR, 0, 1);
+	if(m->pcihz >= 66000000)
+		psr |= F66C;	/* 66 MHz */
+	pcicfgrw16(bridge, PciPSR, psr, 0);	/* reset error status */
+	if(0){
+		iprint("pci bridge':\n");
+		pcidumpdev(bridge);
+	}
+
+	pcicfgmode = -1;
+}
--- /dev/null
+++ b/os/cerf405/physmem.h
@@ -1,0 +1,22 @@
+/*
+ * Memory-mapped IO
+ */
+
+#define	PHYSPCIBRIDGE	0x80000000
+#define	PHYSMMIO	0xEF600000
+#define	MMIO(i)	(PHYSMMIO+(i)*0x100)
+#define	PHYSGPT	MMIO(0)
+#define	PHYSUART0	MMIO(3)
+#define	PHYSUART1	MMIO(4)
+#define	PHYSIIC	MMIO(5)
+#define	PHYSOPB	MMIO(6)
+#define	PHYSGPIO	MMIO(7)
+#define	PHYSEMAC0	MMIO(8)
+#define	PHYSEMAC1	MMIO(9)
+
+#define	PHYSPCIIO0	0xE8000000	/* for 64M */
+#define	PHYSPCIMEM	0x80000000
+#define	PHYSPCIADDR	0xEEC00000	/* for 8 bytes */
+#define	PHYSPCIDATA	0xEEC00004
+#define	PHYSPCIACK	0xEED00000	/* interrupt acknowledge */
+#define	PHYSPCIBCFG	0xEF400000	/* bridge configuration registers */
--- /dev/null
+++ b/os/cerf405/powerbreak.c
@@ -1,0 +1,123 @@
+#include	"u.h"
+#include	"../port/lib.h"
+#include	"mem.h"
+#include	"dat.h"
+#include	"fns.h"
+#include	"../port/error.h"
+#include	"ureg.h"
+
+extern int (*breakhandler)(Ureg *ur, Proc*);	/* trap.c */
+extern Instr BREAK;	/* trap.c */
+extern void portbreakinit(void);
+
+#define getop(i) ((i>>26)&0x3F)
+#define getxo(i) ((i>>1)&0x3FF)
+#define getbobi(i)	bo = (i>>21)&0x1f; bi = (i>>16)&0x1f; xx = (i>>11)&0x1f;
+
+void
+machbreakinit(void)
+{
+	portbreakinit();
+	breakhandler = breakhit;
+}
+
+Instr
+machinstr(ulong addr)
+{
+	if (addr < KTZERO)
+		error(Ebadarg);
+	return *(Instr*)addr;
+}
+
+void
+machbreakset(ulong addr)
+{
+	if (addr < KTZERO)
+		error(Ebadarg);
+	*(Instr*)addr = BREAK;
+	segflush((void*)addr, sizeof(Instr));
+}
+
+void
+machbreakclear(ulong addr, Instr i)
+{
+	if (addr < KTZERO)
+		error(Ebadarg);
+	*(Instr*)addr = i;
+	segflush((void*)addr, sizeof(Instr));
+}
+
+static int
+condok(Ureg *ur, ulong ir, int ctrok)
+{
+	int bo, bi, xx;
+	ulong ctrval;
+
+	ctrval = ur->ctr;
+	getbobi(ir);
+	if(xx)
+		return 0;	/* illegal */
+	if((bo & 0x4) == 0) {
+		if(!ctrok)
+			return 0;	/* illegal */
+		ctrval--;
+	}
+	if(bo & 0x4 || (ctrval!=0)^((bo>>1)&1)) {
+		if(bo & 0x10 || (((ur->cr & (1L<<(31-bi))!=0)==((bo>>3)&1))))
+			return 1;
+	}
+	return 0;
+}
+
+/*
+ * Return the address of the instruction that will be executed after the
+ * instruction at ur->pc, accounting for current branch conditions.
+ */
+ulong
+machnextaddr(Ureg *ur)
+{
+	long imm;
+	ulong ir;
+
+	ir = *(ulong*)ur->pc;
+	switch(getop(ir)) {
+	case 18:	/* branch */
+		imm = ir & 0x03FFFFFC;
+		if(ir & 0x02000000)
+			imm |= 0xFC000000;	/* sign extended */
+		if((ir & 2) == 0)	/* relative address */
+			return ur->pc + imm;
+		return imm;
+			
+	case 16:	/* conditional branch */
+		if(condok(ur, ir&0xFFFF0000, 1)){
+			imm = ir & 0xFFFC;
+			if(ir & 0x08000)
+				imm |= 0xFFFF0000;	/* sign extended */
+			if((ir & 2) == 0)	/* relative address */
+				return ur->pc + imm;
+			return imm;
+		}
+		break;
+
+	case 19:	/* conditional branch to register */
+		switch(getxo(ir)){
+		case 528:	/* bcctr */
+			if(condok(ur, ir, 0))
+				return ur->ctr & ~3;
+			break;
+		case 16:	/* bclr */
+			if(condok(ur, ir, 1))
+				return ur->lr & ~3;
+			break;
+		}
+		break;
+	}
+	return ur->pc+4;	/* next instruction */
+}
+
+int
+isvalid_va(void *v)
+{
+	return (ulong)v >= KTZERO;
+}
--- /dev/null
+++ b/os/cerf405/rmap.c
@@ -1,0 +1,106 @@
+#include "u.h"
+#include "lib.h"
+#include "mem.h"
+#include "dat.h"
+#include "fns.h"
+
+void
+mapinit(RMap *rmap, Map *map, int size)
+{
+	lock(rmap);
+	rmap->map = map;
+	rmap->mapend = map+(size/sizeof(Map));
+	unlock(rmap);
+}
+
+void
+mapfree(RMap* rmap, ulong addr, int size)
+{
+	Map *mp;
+	ulong t;
+
+	if(size <= 0)
+		return;
+
+	lock(rmap);
+	for(mp = rmap->map; mp->addr <= addr && mp->size; mp++)
+		;
+
+	if(mp > rmap->map && (mp-1)->addr+(mp-1)->size == addr){
+		(mp-1)->size += size;
+		if(addr+size == mp->addr){
+			(mp-1)->size += mp->size;
+			while(mp->size){
+				mp++;
+				(mp-1)->addr = mp->addr;
+				(mp-1)->size = mp->size;
+			}
+		}
+	}
+	else{
+		if(addr+size == mp->addr && mp->size){
+			mp->addr -= size;
+			mp->size += size;
+		}
+		else do{
+			if(mp >= rmap->mapend){
+				print("mapfree: %s: losing 0x%luX, %d\n",
+					rmap->name, addr, size);
+				break;
+			}
+			t = mp->addr;
+			mp->addr = addr;
+			addr = t;
+			t = mp->size;
+			mp->size = size;
+			mp++;
+		}while(size = t);
+	}
+	unlock(rmap);
+}
+
+ulong
+rmapalloc(RMap* rmap, ulong addr, int size, int align)
+{
+	Map *mp;
+	ulong maddr, oaddr;
+
+	lock(rmap);
+	for(mp = rmap->map; mp->size; mp++){
+		maddr = mp->addr;
+
+		if(addr){
+			if(maddr > addr)
+				break;
+			if(maddr+mp->size < addr)
+				continue;
+			if(addr+size > maddr+mp->size)
+				break;
+			maddr = addr;
+		}
+
+		if(align > 0)
+			maddr = ((maddr+align-1)/align)*align;
+		if(mp->addr+mp->size-maddr < size)
+			continue;
+
+		oaddr = mp->addr;
+		mp->addr = maddr+size;
+		mp->size -= maddr-oaddr+size;
+		if(mp->size == 0){
+			do{
+				mp++;
+				(mp-1)->addr = mp->addr;
+			}while((mp-1)->size = mp->size);
+		}
+
+		unlock(rmap);
+		if(oaddr != maddr)
+			mapfree(rmap, oaddr, maddr-oaddr);
+
+		return maddr;
+	}
+	unlock(rmap);
+
+	return 0;
+}
--- /dev/null
+++ b/os/cerf405/tlb.s
@@ -1,0 +1,30 @@
+#include	"mem.h"
+#define	MB	(1024*1024)
+
+/*
+ * TLB prototype entries, loaded once-for-all at startup,
+ * remaining unchanged thereafter.
+ * Limit the table size to ensure it fits in small TLBs.
+ */
+#define	TLBE(hi, lo)	WORD	$(hi);  WORD	$(lo)
+
+TEXT	tlbtab(SB), $-4
+
+	/* tlbhi tlblo */
+
+	/* DRAM, 32MB */
+	TLBE(KZERO|PHYSDRAM|TLB16MB|TLBVALID, PHYSDRAM|TLBZONE(0)|TLBWR|TLBEX)
+	TLBE(KZERO|(PHYSDRAM+16*MB)|TLB16MB|TLBVALID, (PHYSDRAM+16*MB)|TLBZONE(0)|TLBWR|TLBEX)
+
+	/* memory-mapped IO, 4K */
+	TLBE(PHYSMMIO|TLB4K|TLBVALID, PHYSMMIO|TLBZONE(0)|TLBWR|TLBI|TLBG)
+
+	/* NAND flash access (4K?) */
+	TLBE(PHYSNAND|TLB4K|TLBVALID, PHYSNAND|TLBZONE(0)|TLBWR|TLBI|TLBG)
+
+	/* NOR flash, 2MB */
+	TLBE(PHYSFLASH|TLB1MB|TLBVALID, PHYSFLASH|TLBZONE(0)|TLBWR|TLBEX)
+	TLBE((PHYSFLASH+MB)|TLB1MB|TLBVALID, (PHYSFLASH+MB)|TLBZONE(0)|TLBWR|TLBEX)
+
+TEXT	tlbtabe(SB), $-4
+	RETURN
--- /dev/null
+++ b/os/cerf405/trap.c
@@ -1,0 +1,581 @@
+#include	"u.h"
+#include	"../port/lib.h"
+#include	"mem.h"
+#include	"dat.h"
+#include	"fns.h"
+#include	"ureg.h"
+#include	"io.h"
+#include	"../port/error.h"
+
+enum 
+{
+	Maxhandler=	MaxVector		/* max number of interrupt handlers, assuming none shared */
+};
+
+enum {
+	/* UIC registers (p. 10-4) */
+	Usr =	0xC0,	/* status */
+	Uer =	0xC2,	/* enable */
+	Ucr =	0xC3,	/* critical interrupts */
+	Upr =	0xC4,	/* priority */
+	Utr =		0xC5,	/* trigger */
+	Umsr =	0xC6,	/* masked status */
+	Uvr =	0xC7,	/* vector */
+	Uvcr =	0xC8,	/* vector configuration */
+};
+
+typedef struct Handler	Handler;
+struct Handler
+{
+	void	(*r)(Ureg*, void*);
+	void	*arg;
+	char	name[KNAMELEN];
+	Handler	*next;
+	int	edge;
+	ulong	nintr;
+	ulong	ticks;
+	int	maxtick;
+};
+
+static Lock	veclock;
+
+static struct
+{
+	Handler	*ivec[MaxVector];
+	Handler	h[Maxhandler];
+	int	free;
+	Handler*	freelist;
+} halloc;
+
+Instr BREAK = 0x7fe00008;
+int (*breakhandler)(Ureg*, Proc*);
+
+void	kernfault(Ureg*, int);
+
+char *excname[] =
+{
+	"reserved 0",
+	"system reset",
+	"machine check",
+	"data access",
+	"instruction access",
+	"external interrupt",
+	"alignment",
+	"program exception",
+	"floating-point unavailable",
+	"decrementer",
+	"i/o controller interface error",
+	"reserved B",
+	"system call",
+	"trace trap",
+	"floating point assist",
+	"reserved F",
+	"software emulation",
+	"ITLB miss",
+	"DTLB miss",
+	"ITLB error",
+	"DTLB error",
+	"reserved 15",
+	"reserved 16",
+	"reserved 17",
+	"reserved 18",
+	"reserved 19",
+	"reserved 1A",
+	"reserved 1B",
+	"data breakpoint",
+	"instruction breakpoint",
+	"peripheral breakpoint",
+	"development port",
+	/* the following are made up on a program exception */
+	"floating point exception",		/* 20: FPEXC */
+	"illegal instruction",	/* 21 */
+	"privileged instruction",	/* 22 */
+	"trap",	/* 23 */
+	"illegal operation",	/* 24 */
+	"breakpoint",	/* 25 */
+};
+
+char *fpcause[] =
+{
+	"inexact operation",
+	"division by zero",
+	"underflow",
+	"overflow",
+	"invalid operation",
+};
+char	*fpexcname(Ureg*, ulong, char*);
+#define FPEXPMASK	0xfff80300		/* Floating exception bits in fpscr */
+
+char *regname[]={
+	"CAUSE",	"SRR1",
+	"PC",		"GOK",
+	"LR",		"CR",
+	"XER",	"CTR",
+	"R0",		"R1",
+	"R2",		"R3",
+	"R4",		"R5",
+	"R6",		"R7",
+	"R8",		"R9",
+	"R10",	"R11",
+	"R12",	"R13",
+	"R14",	"R15",
+	"R16",	"R17",
+	"R18",	"R19",
+	"R20",	"R21",
+	"R22",	"R23",
+	"R24",	"R25",
+	"R26",	"R27",
+	"R28",	"R29",
+	"R30",	"R31",
+};
+
+void
+sethvec(int v, void (*r)(void))
+{
+	ulong *vp, pa, o;
+
+	vp = (ulong*)KADDR(v);
+	vp[0] = 0x7c1043a6;	/* MOVW R0, SPR(SPRG0) */
+	vp[1] = 0x7c0802a6;	/* MOVW LR, R0 */
+	vp[2] = 0x7c1243a6;	/* MOVW R0, SPR(SPRG2) */
+	pa = PADDR(r);
+	o = pa >> 25;
+	if(o != 0 && o != 0x7F){
+		/* a branch too far: running from ROM */
+		vp[3] = (15<<26)|(pa>>16);	/* MOVW $r&~0xFFFF, R0 */
+		vp[4] = (24<<26)|(pa&0xFFFF);	/* OR $r&0xFFFF, R0 */
+		vp[5] = 0x7c0803a6;	/* MOVW	R0, LR */
+		vp[6] = 0x4e800021;	/* BL (LR) */
+	}else
+		vp[3] = (18<<26)|(pa&0x3FFFFFC)|3;	/* bla */
+	dcflush(vp, 8*sizeof(ulong));
+}
+
+void
+sethvec2(int v, void (*r)(void))
+{
+	ulong *vp;
+
+	vp = (ulong*)KADDR(v);
+	vp[0] = (18<<26)|((ulong)r&~KSEGM)|2;	/* ba */
+	dcflush(vp, sizeof(*vp));
+}
+
+static void
+faultpower(Ureg *ur, ulong addr, int read)
+{
+	char buf[ERRMAX];
+
+	if(up == nil){
+		dumpregs(ur);
+		panic("kernel fault");
+	}
+
+	up->dbgreg = ur;		/* For remote ACID */
+	spllo();
+
+	sprint(buf, "trap: fault %s pc=0x%lux addr=0x%lux",
+			read? "read": "write", ur->pc, addr);
+	if(up->type == Interp){
+		if(addr == ~0)
+			disfault(ur, "dereference of nil");
+		disfault(ur, buf);
+	}
+	dumpregs(ur);
+	panic("fault: %s\n", buf);
+}
+
+void
+trap(Ureg *ur)
+{
+	int ecode, s;
+	ulong w, esr;
+	char buf[ERRMAX];
+
+	ecode = ur->cause >> 8;
+	if(ecode < 0 || ecode >= 0x1F)
+		ecode = 0x1F;
+	esr = getesr();
+	putesr(0);
+	switch(ecode){
+	case CPIT:
+		clockintr(ur);
+		preemption(1);
+		break;
+
+	case CMCHECK:
+		if(esr & ESR_MCI){
+			faultpower(ur, ur->pc, 1);
+			break;
+		}
+		/* FALL THROUGH */
+	case CDSI:
+		faultpower(ur, getdear(), !(esr&ESR_DST));
+		break;
+
+	case CDMISS:
+		faultpower(ur, getdear(), 1);
+		break;
+
+	case CISI:
+	case CIMISS:
+		faultpower(ur, ur->pc, 1);
+		break;
+
+	case CPROG:
+		if(esr & ESR_PIL){
+			if(up == nil)
+				goto Default;
+			if((ulong)(ur+1) != ur->r1)
+				panic("fp emu stack");
+			spllo();
+			if(waserror()){
+				if(up->type == Interp)
+					disfault(ur, up->env->errstr);
+				panic("%s", up->env->errstr);
+			}
+			if(fpipower(ur) == 0){
+				splhi();
+				poperror();
+				print("pc=#%lux op=#%8.8lux\n", ur->pc, *(ulong*)ur->pc);	/* temporary */
+				goto Default;
+			}
+			poperror();
+			break;
+		}
+		/* TO DO: 4xx variant for the following */
+		if(ur->status & (1<<19)) {
+			ecode = 0x20;
+			w = ur->pc;
+			if(ur->status & (1<<16))
+				w += 4;
+			if(*(ulong*)w == 0x7fe00008){ /* tw 31,0,0 */
+				if(breakhandler){
+					s = (*breakhandler)(ur, up);
+					if(s == BrkSched){
+						if(up){
+							up->preempted = 0;
+							sched();
+							splhi();
+						}
+					}else if(s == BrkNoSched){
+						if(up){
+							up->preempted = 1;	/* stop it being preempted until next instruction */
+							up->dbgreg = 0;
+						}
+					}
+					break;
+				}
+				ecode = 0x1D;	/* breakpoint */
+			}
+		}
+		if(ur->status & (1<<18))
+			ecode = 0x21;
+		if(ur->status & (1<<17))
+			ecode = 0x22;
+		/* FALL THROUGH */
+
+	Default:
+	default:
+		if(up && up->type == Interp) {
+			spllo();
+			snprint(buf, sizeof buf, "sys: trap: %s pc=0x%lux", excname[ecode], ur->pc);
+			error(buf);
+			break;
+		}
+		print("kernel %s pc=0x%lux\n", excname[ecode], ur->pc);
+		dumpregs(ur);
+		dumpstack();
+		if(m->machno == 0)
+			spllo();
+		exit(1);
+	}
+
+	splhi();
+}
+
+void
+trapinit(void)
+{
+	int i;
+
+	putdcr(Uer, 0);	/* none enabled */
+	putdcr(Ucr, 0);	/* none are critical by default */
+	putdcr(Upr, ~IBIT(VectorPCISERR));	/* default is active high (except PCISERR) */
+	putdcr(Utr, 0);	/* all are level sensitive by default */
+	putdcr(Usr, getdcr(Usr));	/* reset interrupts */
+	putdcr(Uvcr, 0);	/* 31 is highest priority */
+	eieio();
+
+	/*
+	 * set all exceptions to trap
+	 */
+	for(i = 0x0; i < 0x3000; i += 0x100)
+		sethvec(i, trapvec);
+
+	/* on the 405, several traps are critical interrupts with different SRRs */
+	sethvec(0x0100, trapcvec);
+	sethvec(0x0200, trapcvec);
+
+	sethvec(CEI<<8, intrvec);
+	/* TO DO: FIT and WDT */
+	//sethvec2(CIMISS<<8, itlbmiss);
+	//sethvec2(CDMISS<<8, dtlbmiss);
+
+	putevpr(0);	/* use our vectors */
+}
+
+void
+intrenable(int v, void (*r)(Ureg*, void*), void *arg, int, char *name)
+{
+	Handler *h;
+	ulong w, f, bit;
+
+	f = v;
+	v &= IRQmask;
+	bit = IBIT(v);
+	if(v < 0 || v >= nelem(halloc.ivec))
+		panic("intrenable(%d)", v);
+	ilock(&veclock);
+	if((h = halloc.freelist) == nil){
+		if(halloc.free >= Maxhandler){
+			iunlock(&veclock);
+			panic("out of interrupt handlers");
+		}
+		h = &halloc.h[halloc.free++];
+	}else
+		halloc.freelist = h->next;
+	h->r = r;
+	h->arg = arg;
+	strncpy(h->name, name, KNAMELEN-1);
+	h->name[KNAMELEN-1] = 0;
+	h->next = halloc.ivec[v];
+	halloc.ivec[v] = h;
+
+	/*
+	 * enable corresponding interrupt in UIC
+	 */
+	w = getdcr(Ucr);
+	if(f & IRQcritical)
+		putdcr(Ucr, w | bit);
+	else
+		putdcr(Ucr, w & ~bit);
+	if(v >= VectorIRQ){
+		/* (only) these have got choice of polarity, etc. */
+		w = getdcr(Utr);
+		h->edge = (f & IRQedge) != 0;
+		if(h->edge)
+			putdcr(Utr, w | bit);
+		else
+			putdcr(Utr, w & ~bit);
+		w = getdcr(Upr);
+		if(f & IRQactivelow)
+			putdcr(Upr, w | bit);
+		else
+			putdcr(Upr, w & ~bit);
+	}
+	eieio();
+	putdcr(Uer, getdcr(Uer) | bit);
+	eieio();
+	iunlock(&veclock);
+}
+
+static void
+irqdisable(int v)
+{
+	putdcr(Uer, getdcr(Uer) & ~IBIT(v));
+}
+
+void
+intrdisable(int v, void (*r)(Ureg*, void*), void *arg, int, char *name)
+{
+	Handler *h, **hp;
+
+	v &= IRQmask;
+	if(v < 0 || v >= nelem(halloc.ivec))
+		panic("intrdisable(%d)", v);
+	ilock(&veclock);
+	for(hp = &halloc.ivec[v]; (h = *hp) != nil; hp = &h->next)
+		if(h->r == r && h->arg == arg && strcmp(h->name, name) == 0){
+			*hp = h->next;
+			h->next = halloc.freelist;
+			halloc.freelist = h;
+			break;
+		}
+	if(halloc.ivec[v] == nil)
+		irqdisable(v);
+	iunlock(&veclock);
+}
+
+/*
+ * called directly by l.s:/intrvec.  on a multiprocessor we'd need to lock veclock.
+ */
+void
+intr(Ureg *ur)
+{
+	ulong msr, b;
+	int v;
+	Handler *h;
+	long t0;
+	Proc *oup;
+
+	oup = up;
+	up = nil;	/* no process at interrupt level */
+	while((msr = getdcr(Umsr)) != 0){
+		for(v=0; msr!=0 && v<32; v++){
+			b = IBIT(v);
+			if((msr & b) == 0)
+				continue;
+			msr &= ~b;
+			ur->cause = (CEI<<8) | v;
+			h = halloc.ivec[v];
+			if(h == nil){
+				iprint("unknown interrupt %d pc=0x%lux\n", v, ur->pc);
+				irqdisable(v);
+				continue;
+			}
+
+			/*
+			 *  call the interrupt handlers
+			 */
+			do {
+				if(h->edge)
+					putdcr(Usr, b);
+				h->nintr++;
+				t0 = getpit();
+				(*h->r)(ur, h->arg);
+				t0 -= getpit();
+				h->ticks += t0;
+				if(h->maxtick < t0)
+					h->maxtick = t0;
+				if(!h->edge)
+					putdcr(Usr, b);
+				h = h->next;
+			} while(h != nil);
+		}
+	}
+	up = oup;
+	preemption(0);
+}
+
+int
+intrstats(char *buf, int bsize)
+{
+	Handler *h;
+	int i, n;
+
+	n = 0;
+	for(i=0; i<nelem(halloc.ivec) && n < bsize; i++)
+		if((h = halloc.ivec[i]) != nil && h->nintr)
+			n += snprint(buf+n, bsize-n, "%3d %lud %lud %ud\n", i, h->nintr, h->ticks, h->maxtick);
+	return n;
+}
+
+char*
+fpexcname(Ureg *ur, ulong fpscr, char *buf)
+{
+	int i;
+	char *s;
+	ulong fppc;
+
+	fppc = ur->pc;
+	s = 0;
+	fpscr >>= 3;		/* trap enable bits */
+	fpscr &= (fpscr>>22);	/* anded with exceptions */
+	for(i=0; i<5; i++)
+		if(fpscr & (1<<i))
+			s = fpcause[i];
+	if(s == 0)
+		return "no floating point exception";
+	sprint(buf, "%s fppc=0x%lux", s, fppc);
+	return buf;
+}
+
+#define KERNPC(x)	(KTZERO<(ulong)(x)&&(ulong)(x)<(ulong)etext)
+
+void
+kernfault(Ureg *ur, int code)
+{
+	Label l;
+
+	print("panic: kfault %s dear=0x%lux esr=0x%8.8lux\n", excname[code], getdear(), getesr());
+	print("u=0x%lux status=0x%lux pc=0x%lux sp=0x%lux\n",
+				up, ur->status, ur->pc, ur->sp);
+	dumpregs(ur);
+	l.sp = ur->sp;
+	l.pc = ur->pc;
+	dumpstack();
+	setpri(PriBackground);		/* Let the debugger in */
+	for(;;)
+		sched();
+}
+
+void
+dumpstack(void)
+{
+	ulong l, v;
+	int i;
+
+	if(up == 0)
+		return;
+	i = 0;
+	for(l=(ulong)&l; l<(ulong)(up->kstack+KSTACK); l+=4){
+		v = *(ulong*)l;
+		if(KTZERO < v && v < (ulong)etext){
+			print("%lux=%lux, ", l, v);
+			if(i++ == 4){
+				print("\n");
+				i = 0;
+			}
+		}
+	}
+}
+
+void
+dumpregs(Ureg *ur)
+{
+	int i;
+	ulong *l;
+	if(up) {
+		print("registers for %s %ld\n", up->text, up->pid);
+		if(ur->usp < (ulong)up->kstack ||
+		   ur->usp > (ulong)up->kstack+KSTACK)
+			print("invalid stack ptr\n");
+	}
+	else
+		print("registers for kernel\n");
+
+	l = &ur->cause;
+	for(i=0; i<sizeof regname/sizeof(char*); i+=2, l+=2)
+		print("%s\t%.8lux\t%s\t%.8lux\n", regname[i], l[0], regname[i+1], l[1]);
+}
+
+static void
+linkproc(void)
+{
+	spllo();
+	(*up->kpfun)(up->arg);
+	pexit("", 0);
+}
+
+void
+kprocchild(Proc *p, void (*func)(void*), void *arg)
+{
+	p->sched.pc = (ulong)linkproc;
+	p->sched.sp = (ulong)p->kstack+KSTACK;
+
+	p->kpfun = func;
+	p->arg = arg;
+}
+
+void
+setpanic(void)
+{
+	consoleprint = 1;
+iprint("panic\n");
+}
+
+void
+dumplongs(char*, ulong*, int)
+{
+}
--- /dev/null
+++ b/os/cerf405/uart.c
@@ -1,0 +1,139 @@
+#include	"u.h"
+#include	"../port/lib.h"
+#include	"mem.h"
+#include	"dat.h"
+#include	"fns.h"
+#include	"io.h"
+#include	"ureg.h"
+
+/*
+ * uart for initial port
+ */
+
+typedef struct Uartregs Uartregs;
+struct Uartregs {
+	uchar	rbr;
+#define	thr	rbr
+#define	dll	rbr
+	uchar	ier;
+#define	dlm	ier
+	uchar	fcr;
+#define	iir	fcr
+	uchar	lcr;
+	uchar	mcr;
+	uchar	lsr;
+	uchar	msr;
+	uchar	scr;
+};
+
+#define	UARTREGS(n)	((Uartregs*)(PHYSUART0+(n)*0x100))
+
+enum {
+	/* ier */
+	Edssi=	1<<3,
+	Elsi=		1<<2,
+	Etbei=	1<<1,
+	Erbfi=	1<<0,
+
+	/* iir */
+	Fci0=	0<<4,
+	Fci3=	3<<4,
+	Ipl=		7<<1,
+	Ip=		1<<0,
+
+	/* fcr */
+	Rftl1=	0<<6,	/* receiver trigger level 1, 16, 32, 56 */
+	Rftl16=	1<<6,
+	Rftl32=	2<<6,
+	Rftl56=	3<<6,
+	Dms=	1<<3,	/* =0, single transfer; =1, multiple transfers */
+	Tfr=		1<<2,	/* transmitter fifo reset */
+	Rfr=		1<<1,	/* receiver fifo reset */
+	Fifoe=	1<<0,	/* =0, disable fifos; =1, enable fifos */
+
+	/* lcr */
+	Dlab=	1<<7,	/* =0, normal; =1, dll/dlm visible */
+	Sb=		1<<6,	/* set break */
+	Sp=		1<<5,	/* =1, enable sticky parity */
+	Eps=		1<<4,	/* =1, generate even parity */
+	Pen=		1<<3,	/* =1, enable parity checking */
+	Sbs=		1<<2,	/* =0, 1 stop bit; =1, 1.5 or 2 stop bits (see Wls) */
+	Wls=		3<<0,	/* set to nbit-5 for nbit characters */
+
+	/* mcr */
+	Afc=		1<<5,	/* =1, auto flow control enabled */
+	Loop=	1<<4,	/* =1, loop back mode enabled */
+	Out2=	1<<3,	/* =0, OUT2# active */
+	Out1=	1<<2,	/* =0, OUT1# active */
+	Rts=		1<<1,	/* =0, RTS# inactive (1); =1, RTS# active (0) */
+	Dtr=		1<<0,	/* =0, DTS# inactive; =1, DTS# active */
+
+	/* lsr */
+	Rfe=		1<<7,	/* error instances in fifo */
+	Temt=	1<<6,	/* transmitter empty */
+	Thre=	1<<5,	/* transmitter holding register/fifo empty */
+	Be=		1<<4,	/* break (interrupt) */
+	Fe=		1<<3,	/* framing error */
+	Pe=		1<<2,	/* parity error */
+	Oe=		1<<1,	/* overrun error */
+	Dr=		1<<0,	/* receiver data ready */
+
+	/* msr */
+	Dcd=	1<<7,	/* follows Out2 */
+	Ri=		1<<6,	/* follows Out1 */
+	Dsr=		1<<5,	/* follows Dtr */
+	Cts=		1<<4,	/* follows Rts */
+	Ddcd=	1<<3,	/* Dcd input changed */
+	Teri=	1<<2,	/* Ri changed from 0 to 1 */
+	Ddsr=	1<<1,	/* Dsr input changed */
+	Dcts=	1<<0,	/* Cts input changed */
+};
+
+void (*serwrite)(char*, int) = uartputs;
+
+void
+uartinstall(void)
+{
+}
+
+void
+uartspecial(int, int, Queue**, Queue**, int (*)(Queue*, int))
+{
+}
+
+void
+uartputc(int c)
+{
+	Uartregs *r;
+
+	if(c == 0)
+		return;
+	r = UARTREGS(0);
+	while((r->lsr & Thre) == 0)
+		{}
+	r->thr = c;
+	if(c == '\n')
+		while((r->lsr & Thre) == 0)	/* flush xmit fifo */
+			{}
+}
+
+void
+uartputs(char *data, int len)
+{
+	int s;
+
+//	if(!uartspcl && !redirectconsole)
+//		return;
+	s = splhi();
+	while(--len >= 0){
+		if(*data == '\n')
+			uartputc('\r');
+		uartputc(*data++);
+	}
+	splx(s);
+}
+
+void
+uartwait(void)
+{
+}
--- /dev/null
+++ b/os/cerf405/uart.h
@@ -1,0 +1,101 @@
+/*
+ *  405EP specific code for its uart.
+ */
+
+#define	UR(p,r)	((uchar*)(p))[r]
+#define uartwr(u,r,v)	(UR(u->regs,r) = (v))
+#define uartwrreg(u,r,v)	(UR(u->regs,r)= (u)->sticky[r] | (v))
+#define uartrdreg(u,r)		UR(u->regs,r)
+
+extern void	uartsetup(ulong, void*, ulong, char*);
+extern void	uartclock(void);
+
+static void
+uartportpower(Uart*, int)
+{
+	/* TO DO: power control */
+}
+
+/*
+ *  handle an interrupt to a single uart
+ */
+static void
+uartintrx(Ureg*, void* arg)
+{
+	uartintr(arg);
+}
+
+/*
+ *  install the uarts (called by reset)
+ */
+void
+uartinstall(void)
+{
+	static int already;
+
+	if(already)
+		return;
+	already = 1;
+
+	/* first two ports are always there */
+	uartsetup(0, (void*)PHYSUART0, 0, "eia0");
+	intrenable(VectorUART0, uartintrx, uart[0], BUSUNKNOWN, "uart0");
+	uartsetup(1, (void*)PHYSUART1, 0, "eia1");
+	intrenable(VectorUART1, uartintrx, uart[1], BUSUNKNOWN, "uart1");
+	addclock0link(uartclock, 22);
+}
+
+/*
+ * If the UART's receiver can be connected to a DMA channel,
+ * this function does what is necessary to create the
+ * connection and returns the DMA channel number.
+ * If the UART's receiver cannot be connected to a DMA channel,
+ * a -1 is returned.
+ */
+char
+uartdmarcv(int dev)
+{
+ 
+	USED(dev);
+	return -1;
+}
+
+void
+uartputc(int c)
+{
+	uchar *p;
+
+	if(c == 0)
+		return;
+	p = (uchar*)PHYSUART0;
+	while((UR(p,Lstat) & Outready) == 0){
+		;
+	}
+	UR(p,Data) = c;
+	eieio();
+	if(c == '\n')
+		while((UR(p,Lstat) & Outready) == 0){	/* let fifo drain */
+			;
+		}
+}
+
+void
+uartputs(char *data, int len)
+{
+	int s;
+
+//	if(!uartspcl && !redirectconsole)
+//		return;
+	s = splhi();
+	while(--len >= 0){
+		if(*data == '\n')
+			uartputc('\r');
+		uartputc(*data++);
+	}
+	splx(s);
+}
+
+void
+uartwait(void)
+{
+}
--- /dev/null
+++ b/os/fads/NOTICE
@@ -1,0 +1,4 @@
+Inferno® Copyright © 1996-1999 Lucent Technologies Inc.  All rights reserved.
+PowerPC support Copyright © 1995-1997 C H Forsyth (forsyth@terzarima.net).  All rights reserved.
+MPC8xx Inferno PowerPC port Copyright © 1998-2003 Vita Nuova Holdings Limited.  All rights reserved.
+FADS Inferno PowerPC port Copyright © 1998-2003 Vita Nuova Holdings Limited.  All rights reserved.
--- /dev/null
+++ b/os/fads/archfads.c
@@ -1,0 +1,619 @@
+#include	"u.h"
+#include	"../port/lib.h"
+#include	"mem.h"
+#include	"dat.h"
+#include	"fns.h"
+#include	"io.h"
+
+#include "../port/netif.h"
+#include "../mpc/etherif.h"
+#include "../port/flashif.h"
+
+#include	<draw.h>
+#include	<memdraw.h>
+#include	<cursor.h> 
+#include	"screen.h"
+
+#include	"archfads.h"
+
+/*
+ * board-specific support for the 8xxFADS (including 860/21 development system)
+ */
+
+enum {
+	/* CS assignment on FADS boards */
+	BOOTCS = 0,
+	BCSRCS = 1,
+	DRAM1 = 2,
+	DRAM2 = 3,
+	SDRAM = 4,
+
+	/* sccr */
+	RTSEL = IBIT(8),	/* =0, select main oscillator (OSCM); =1, select external crystal (EXTCLK) */
+	RTDIV = IBIT(7),	/* =0, divide by 4; =1, divide by 512 */
+	CRQEN = IBIT(9),	/* =1, switch to high frequency when CPM active */
+	PRQEN = IBIT(10),	/* =1, switch to high frequency when interrupt pending */
+
+	/* plprcr */
+	CSRC = IBIT(21),	/* =0, clock is DFNH; =1, clock is DFNL */
+};
+
+/*
+ * called early in main.c, after machinit:
+ * using board and architecture specific registers, initialise
+ * 8xx registers that need it and complete initialisation of the Mach structure.
+ */
+void
+archinit(void)
+{
+	IMM *io;
+	int mf;
+
+	m->bcsr = KADDR(PHYSBCSR);
+	m->bcsr[1] |= DisableRS232a | DisableIR | DisableEther | DisablePCMCIA | DisableRS232b;
+	m->bcsr[1] &= ~(DisableDRAM|DisableFlash);
+	m->bcsr[1] &= ~EnableSDRAM;
+	m->bcsr[4] &= ~EnableVideoClock;
+	m->bcsr[4] |= DisableVideoLamp;
+	io = m->iomem;	/* run by reset code: no need to lock */
+	if(1 || (io->sccr & RTDIV) != 0){
+		/* oscillator frequency can't be determined independently: check a switch */
+		if((m->bcsr[2]>>19)&(1<<2))
+			m->clockgen = 5*MHz;
+		else
+			m->clockgen = 4*MHz;
+	} else
+		m->clockgen = 32768;
+	m->oscclk = m->clockgen/MHz;	/* TO DO: 32k clock */
+	io->plprcrk = KEEP_ALIVE_KEY;
+	io->plprcr &= ~CSRC;	/* general system clock is DFNH */
+	mf = (io->plprcr >> 20)+1;	/* use timing set by bootstrap */
+	io->plprcrk = ~KEEP_ALIVE_KEY;
+	io->sccrk = KEEP_ALIVE_KEY;
+	io->sccr |= CRQEN | PRQEN;
+	io->sccr |= RTSEL;	/* select EXTCLK */
+	io->sccrk = ~KEEP_ALIVE_KEY;
+	m->cpuhz = m->clockgen*mf;
+	m->speed = m->cpuhz/MHz;
+}
+
+static ulong
+banksize(int x, ulong *pa)
+{
+	IMM *io;
+
+	io = m->iomem;
+	if((io->memc[x].base & 1) == 0)
+		return 0;	/* bank not valid */
+	*pa = io->memc[x].base & ~0x7FFF;
+	return -(io->memc[x].option&~0x7FFF);
+}
+
+/*
+ * initialise the kernel's memory configuration:
+ * there are two banks (base0, npage0) and (base1, npage1).
+ * initialise any other values in conf that are board-specific.
+ */
+void
+archconfinit(void)
+{
+	ulong nbytes, pa, ktop;
+
+	conf.nscc = 2;
+	conf.nocts2 = 1;	/* not connected on the FADS board */
+
+	conf.npage0 = 0;
+	if((m->bcsr[1] & DisableDRAM) == 0){
+		nbytes = banksize(DRAM1, &pa);
+		if(nbytes){
+			conf.npage0 = nbytes/BY2PG;
+			conf.base0 = pa;
+		}
+	}
+
+	conf.npage1 = 0;
+	if(m->bcsr[1] & EnableSDRAM){
+		nbytes = banksize(SDRAM, &pa);
+		if(nbytes){
+			conf.npage1 = nbytes/BY2PG;
+			conf.base1 = pa;
+		}
+	}
+
+	/* the following assumes the kernel text and/or data is in bank 0 */
+	ktop = PGROUND((ulong)end);
+	ktop = PADDR(ktop) - conf.base0;
+	conf.npage0 -= ktop/BY2PG;
+	conf.base0 += ktop;
+}
+
+static void
+archidprint(void)
+{
+	int f, i;
+	ulong v;
+
+	/* 8xx and FADS specific */
+	print("IMMR: ");
+	v = getimmr() & 0xFFFF;
+	switch(v>>8){
+	case 0x00:	print("MPC860/821"); break;
+	case 0x20:	print("MPC823"); break;
+	case 0x21:	print("MPC823A"); break;
+	default:	print("Type #%lux", v>>8); break;
+	}
+	print(", mask #%lux\n", v&0xFF);
+	v = m->bcsr[3]>>16;
+	print("MPC8xxFADS rev %lud, DB: ", ((v>>4)&8)|((v>>1)&4)|(v&3));
+	f = (v>>8)&0x3F;
+	switch(f){
+	default:	print("ID#%x", f); break;
+	case 0x00:	print("MPC860/821"); break;
+	case 0x01:	print("MPC813"); break;
+	case 0x02:	print("MPC821"); break;
+	case 0x03:	print("MPC823"); break;
+	case 0x20:	print("MPC801"); break;
+	case 0x21:	print("MPC850"); break;
+	case 0x22:	print("MPC860"); break;
+	case 0x23:	print("MPC860SAR"); break;
+	case 0x24:	print("MPC860T"); break;
+	}
+	print("ADS, rev #%lux\n", (m->bcsr[2]>>16)&7);
+	for(i=0; i<=4; i++)
+		print("BCSR%d: %8.8lux\n", i, m->bcsr[i]);
+	v = m->bcsr[2];
+	f = (v>>28)&0xF;
+	switch(f){
+	default:	print("Unknown"); break;
+	case 4:	print("SM732A2000/SM73228 - 8M SIMM"); break;
+	case 5:	print("SM732A1000A/SM73218 - 4M SIMM"); break;
+	case 6:	print("MCM29080 - 8M SIMM"); break;
+	case 7:	print("MCM29040 - 4M SIMM"); break;
+	case 8:	print("MCM29020 - 2M SIMM"); break;
+	}
+	switch((m->bcsr[3]>>20)&7){
+	default:	i = 0; break;
+	case 1:	i = 150; break;
+	case 2:	i = 120; break;
+	case 3:	i = 90; break;
+	}
+	print(" flash, %dns\n", i);
+	f = (v>>23)&0xF;
+	switch(f&3){
+	case 0:	i = 4; break;
+	case 1:	i = 32; break;
+	case 2:	i = 16; break;
+	case 3:	i = 8; break;
+	}
+	print("%dM SIMM, ", i);
+	switch(f>>2){
+	default: 	i = 0; break;
+	case 2:	i = 70; break;
+	case 3:	i = 60; break;
+	}
+	print("%dns\n", i);
+	print("options: #%lux\n", (m->bcsr[2]>>19)&0xF);
+	print("plprcr=%8.8lux sccr=%8.8lux\n", m->iomem->plprcr, m->iomem->sccr);
+}
+
+void
+cpuidprint(void)
+{
+	print("PVR: ");
+	switch(m->cputype){
+	case 0x01:	print("MPC601"); break;
+	case 0x03:	print("MPC603"); break;
+	case 0x04:	print("MPC604"); break;
+	case 0x06:	print("MPC603e"); break;
+	case 0x07:	print("MPC603e-v7"); break;
+	case 0x50:	print("MPC8xx"); break;
+	default:	print("PowerPC version #%x", m->cputype); break;
+	}
+	print(", revision #%lux\n", getpvr()&0xffff);
+	archidprint();
+	print("%lud MHz system\n", m->cpuhz/MHz);
+	print("\n");
+}
+
+/*
+ * provide value for #r/switch (devrtc.c)
+ */
+int
+archoptionsw(void)
+{
+	return (m->bcsr[2]>>19)&0xF;	/* value of switch DS1 */
+}
+
+/*
+ * invoked by clock.c:/^clockintr
+ */
+static void
+twinkle(void)
+{
+	if(m->ticks%MS2TK(1000) == 0)
+		m->bcsr[4] ^= DisableLamp;
+}
+
+void	(*archclocktick)(void) = twinkle;
+
+/*
+ * invoked by ../port/taslock.c:/^ilock:
+ * reset watchdog timer here, if there is one and it is enabled
+ * (qboot currently disables it on the FADS board)
+ */
+void
+clockcheck(void)
+{
+}
+
+/*
+ * for devflash.c:/^flashreset
+ * retrieve flash type, virtual base and length and return 0;
+ * return -1 on error (no flash)
+ */
+int
+archflashreset(int bank, Flash *f)
+{
+	char *t;
+	int mbyte;
+
+	if(bank != 0)
+		return -1;
+	switch((m->bcsr[2]>>28)&0xF){
+	default:	return -1;	/* unknown or not there */
+	case 4:	mbyte=8; t = "SM732x8"; break;
+	case 5:	mbyte=4; t = "SM732x8"; break;
+	case 6:	mbyte=8; t = "AMD29F0x0"; break;
+	case 7:	mbyte=4; t = "AMD29F0x0"; break;
+	case 8:	mbyte=2; t = "AMD29F0x0"; break;
+	}
+	f->type = t;
+	f->addr = KADDR(PHYSFLASH);
+	f->size = mbyte*1024*1024;
+	f->width = 4;
+	f->interleave = 3;
+	return 0;
+}
+
+void
+archflashwp(Flash*, int)
+{
+}
+
+int
+archether(int ctlrno, Ether *ether)
+{
+	if(isaconfig("ether", ctlrno, ether) == 0)
+		return -1;
+	return 1;
+}
+
+/*
+ * enable the clocks for the given SCC ether and reveal them to the caller.
+ * do anything else required to prepare the transceiver (eg, set full-duplex, reset loopback).
+ */
+int
+archetherenable(int cpmid, int *rcs, int *tcs, int mbps, int fullduplex)
+{
+	IMM *io;
+
+	USED(mbps, fullduplex);	/* TO DO */
+	switch(cpmid){
+	default:
+		/* no other SCCs are wired on the FADS board */
+		return -1;
+
+	case CPscc2:	/* assume 8xxFADS board with 823DABS */
+		io = ioplock();
+		m->bcsr[1] |= DisableIR|DisableRS232b;
+		m->bcsr[1] &= ~DisableEther;
+		io->papar |= SIBIT(6)|SIBIT(5);	/* enable CLK2 and CLK3 */
+		io->padir &= ~(SIBIT(6)|SIBIT(5));
+		/* ETHLOOP etc reset in BCSR elsewhere */
+		iopunlock();
+		*rcs = CLK2;
+		*tcs = CLK3;
+		break;
+
+	case CPscc1:	/* assume 860/21 development board */
+		io = ioplock();
+		m->bcsr[1] |= DisableIR|DisableRS232b;	/* TO DO: might not be shared with RS232b */
+		m->bcsr[1] &= ~DisableEther;
+		io->papar |= SIBIT(6)|SIBIT(7);	/* enable CLK2 and CLK1 */
+		io->padir &= ~(SIBIT(6)|SIBIT(7));
+
+		/* settings peculiar to 860/821 development board */
+		io->pcpar &= ~(SIBIT(4)|SIBIT(5)|SIBIT(6));	/* ETHLOOP, TPFULDL~, TPSQEL~ */
+		io->pcdir |= SIBIT(4)|SIBIT(5)|SIBIT(6);
+		io->pcdat &= ~SIBIT(4);
+		io->pcdat |= SIBIT(5)|SIBIT(6);
+		iopunlock();
+		*rcs = CLK2;
+		*tcs = CLK1;
+		break;
+	}
+	return 0;
+}
+
+/*
+ * do anything extra required to enable the UART on the given CPM port
+ */
+void
+archenableuart(int id, int irda)
+{
+	switch(id){
+	case CPsmc1:
+		m->bcsr[1] &= ~DisableRS232a;
+		break;
+	case CPscc2:
+		m->bcsr[1] |= DisableEther|DisableIR|DisableRS232b;
+		if(irda)
+			m->bcsr[1] &= ~DisableIR;
+		else
+			m->bcsr[1] &= ~DisableRS232b;
+		break;
+	default:
+		/* nothing special */
+		break;
+	}
+}
+
+/*
+ * do anything extra required to disable the UART on the given CPM port
+ */
+void
+archdisableuart(int id)
+{
+	switch(id){
+	case CPsmc1:
+		m->bcsr[1] |= DisableRS232a;
+		break;
+	case CPscc2:
+		m->bcsr[1] |= DisableIR|DisableRS232b;
+		break;
+	default:
+		/* nothing special */
+		break;
+	}
+}
+
+/*
+ * enable the external USB transceiver
+ *	speed is 12MHz if highspeed is non-zero; 1.5MHz if zero
+ *	master is non-zero if the node is acting as USB Host and should provide power
+ */
+void
+archenableusb(int highspeed, int master)
+{
+	if(highspeed)
+		m->bcsr[4] |= USBFullSpeed;
+	else
+		m->bcsr[4] &= ~USBFullSpeed;
+	if(master)
+		m->bcsr[4] &= ~DisableUSBVcc;
+	else
+		m->bcsr[4] |= DisableUSBVcc;
+	eieio();
+	m->bcsr[4] &= ~DisableUSB;
+}
+
+/*
+ * shut down the USB transceiver
+ */
+void
+archdisableusb(void)
+{
+	m->bcsr[4] |= DisableUSBVcc | DisableUSB;
+}
+
+/*
+ * set the external infrared transceiver to the given speed
+ */
+void
+archsetirxcvr(int highspeed)
+{
+	if(!highspeed){
+		/* force low edge after enable to put TFDS6000 xcvr in low-speed mode (see 4.9.2.1 in FADS manual) */
+		m->bcsr[1] |= DisableIR;
+		microdelay(2);
+	}
+	m->bcsr[1] &= ~DisableIR;
+}
+
+/*
+ * force hardware reset/reboot
+ */
+void
+archreboot(void)
+{
+	IMM *io;
+
+	io = m->iomem;
+	io->plprcrk = KEEP_ALIVE_KEY;
+	io->plprcr |= 1<<7;	/* checkstop reset enable */
+	io->plprcrk = ~KEEP_ALIVE_KEY;
+	m->iomem->padat &= ~SIBIT(4);	/* drop backlight */
+	eieio();
+	io->sdcr = 1;
+	eieio();
+	io->lccr = 0;
+	eieio();
+	firmware(0);
+}
+
+/*
+ * board-specific PCMCIA support: assumes slot B on 82xFADS
+ */
+
+int
+pcmslotavail(int slotno)
+{
+	return slotno == 1;
+}
+
+void
+pcmenable(void)
+{
+	ioplock();
+	m->bcsr[1] = (m->bcsr[1] | PCCVPPHiZ) & ~PCCVPP5V;
+	m->bcsr[1] |= PCCVCC0V;
+	m->bcsr[1] &= ~DisablePCMCIA;
+	m->bcsr[1] &= ~PCCVCC5V;	/* apply Vcc */
+	iopunlock();
+}
+
+int
+pcmpowered(int)
+{
+	ulong r;
+
+	r = ~m->bcsr[1]&PCCVCCMask;	/* active low */
+	if(r == PCCVCC5V)
+		return 5;
+	if(r == PCCVCC3V)
+		return 3;
+	return 0;
+}
+
+void
+pcmsetvcc(int, int v)
+{
+	if(v == 5)
+		v = PCCVCC5V;
+	else if(v == 3)
+		v = PCCVCC3V;
+	else
+		v = 0;
+	ioplock();
+	m->bcsr[1] = (m->bcsr[1] | PCCVCCMask) & ~v;	/* active low */
+	iopunlock();
+}
+
+void
+pcmsetvpp(int, int v)
+{
+	if(v == 5)
+		v = PCCVPP5V;
+	else if(v == 12)
+		v = PCCVPP12V;
+	else if(v == 0)
+		v = PCCVPP0V;
+	else
+		v = 0;	/* Hi-Z */
+	ioplock();
+	m->bcsr[1] = (m->bcsr[1] | PCCVPPHiZ) & ~v;	/* active low */
+	iopunlock();
+}
+
+void
+pcmpower(int slotno, int on)
+{
+	if(!on){
+		pcmsetvcc(slotno, 0);	/* turn off card power */
+		pcmsetvpp(slotno, -1);	/* turn off programming voltage (Hi-Z) */
+	}else
+		pcmsetvcc(slotno, 5);
+}
+
+/*
+ * enable/disable the LCD panel's backlight via
+ * York touch panel interface (does no harm without it)
+ */
+void
+archbacklight(int on)
+{
+	IMM *io;
+
+	delay(2);
+	io = ioplock();
+	io->papar &= ~SIBIT(4);
+	io->padir |= SIBIT(4);
+	if(on)
+		io->padat |= SIBIT(4);
+	else
+		io->padat &= ~SIBIT(4);
+	iopunlock();
+}
+
+/*
+ * set parameters to describe the screen
+ */
+int
+archlcdmode(Mode *m)
+{
+	m->x = 640;
+	m->y = 480;
+	m->d = 3;
+	m->lcd.freq = 25000000;
+	m->lcd.ac = 0;
+	m->lcd.vpw = 2;
+	m->lcd.wbf = 34;
+	m->lcd.wbl = 106;
+	m->lcd.flags = IsColour | IsTFT | OELow | HsyncLow | VsyncLow;
+	m->lcd.notpdpar = SIBIT(6);
+	return 0;
+}
+
+/*
+ * reset 823 video port for devvid.c
+ */
+void
+archresetvideo(void)
+{
+	ioplock();
+	m->bcsr[4] &= ~DisableVideoLamp;
+	m->bcsr[4] |= EnableVideoPort;
+	eieio();
+	m->bcsr[4] &= ~EnableVideoPort;	/* falling edge to reset */
+	iopunlock();
+	delay(6);
+	ioplock();
+	m->bcsr[4] |= EnableVideoPort;
+	iopunlock();
+	delay(6);
+}
+
+/*
+ * enable 823 video port and clock
+ */
+void
+archenablevideo(void)
+{
+	ioplock();
+	m->bcsr[4] |= EnableVideoClock|EnableVideoPort;	/* enable AFTER pdpar/pddir to avoid damage */
+	iopunlock();
+}
+
+/*
+ * disable 823 video port and clock
+ */
+void
+archdisablevideo(void)
+{
+	ioplock();
+	m->bcsr[4] &= ~(EnableVideoClock|EnableVideoPort);
+	m->bcsr[4] |= DisableVideoLamp;
+	iopunlock();
+}
+
+/*
+ * allocate a frame buffer for the video, aligned on 16 byte boundary
+ */
+uchar*
+archvideobuffer(long nbytes)
+{
+	/* we shall use the on-board SDRAM if the kernel hasn't grabbed it */
+	if((m->bcsr[1] & EnableSDRAM) == 0){
+		m->bcsr[1] |= EnableSDRAM;
+		return KADDR(PHYSSDRAM);
+	}
+	return xspanalloc(nbytes, 16, 0);
+}
+
+/*
+ * there isn't a hardware keyboard port
+ */
+void
+archkbdinit(void)
+{
+}
--- /dev/null
+++ b/os/fads/archfads.h
@@ -1,0 +1,33 @@
+
+enum {
+	/* BCSR1 bits */
+	DisableFlash=	IBIT(0),
+	DisableDRAM=	IBIT(1),
+	DisableEther=	IBIT(2),
+	DisableIR=	IBIT(3),
+	DisableRS232a=	IBIT(7),
+	DisablePCMCIA=	IBIT(8),
+	PCCVCCMask=	IBIT(9)|IBIT(15),
+	PCCVPPMask=	IBIT(10)|IBIT(11),
+	DisableRS232b=	IBIT(13),
+	EnableSDRAM=	IBIT(14),
+
+	PCCVCC0V=	IBIT(15)|IBIT(9),
+	PCCVCC5V=	IBIT(9),	/* active low */
+	PCCVCC3V=	IBIT(15),	/* active low */
+	PCCVPP0V=	IBIT(10)|IBIT(11),	/* active low */
+	PCCVPP5V=	IBIT(10),	/* active low */
+	PCCVPP12V=	IBIT(11),	/* active low */
+	PCCVPPHiZ=	IBIT(10)|IBIT(11),
+
+	/* BCSR4 bits */
+	DisableTPDuplex=	IBIT(1),
+	DisableLamp=	IBIT(3),
+	DisableUSB=	IBIT(4),
+	USBFullSpeed=	IBIT(5),
+	DisableUSBVcc=	IBIT(6),
+	DisableVideoLamp=	IBIT(8),
+	EnableVideoClock=	IBIT(9),
+	EnableVideoPort=	IBIT(10),
+	DisableModem=	IBIT(11),
+};
--- /dev/null
+++ b/os/fads/dat.h
@@ -1,0 +1,162 @@
+typedef struct Conf	Conf;
+typedef struct FPU	FPU;
+typedef struct FPenv	FPenv;
+typedef struct IMM	IMM;
+typedef struct Irqctl	Irqctl;
+typedef struct ISAConf	ISAConf;
+typedef struct Label	Label;
+typedef struct Lock	Lock;
+typedef struct Mach	Mach;
+typedef struct Map	Map;
+typedef struct Power Power;
+typedef struct RMap RMap;
+typedef struct Ureg	Ureg;
+
+typedef ulong Instr;
+
+#define	MACHP(n)	(n==0? &mach0 : *(Mach**)0)
+
+struct	Lock
+{
+	ulong	key;
+	ulong	pc;
+	ulong	sr;
+	int	pri;
+};
+
+struct	Label
+{
+	ulong	sp;
+	ulong	pc;
+};
+
+/*
+ * Proc.fpstate
+ */
+enum
+{
+	FPINIT,
+	FPACTIVE,
+	FPINACTIVE,
+};
+
+/*
+ * This structure must agree with FPsave and FPrestore asm routines
+ */
+struct FPenv
+{
+	union {
+		double	fpscrd;
+		struct {
+			ulong	pad;
+			ulong	fpscr;
+		};
+	};
+	int	fpistate;	/* emulated fp */
+	ulong	emreg[32][3];	/* emulated fp */
+};
+/*
+ * This structure must agree with fpsave and fprestore asm routines
+ */
+struct	FPU
+{
+	double	fpreg[32];
+	FPenv	env;
+};
+
+struct Conf
+{
+	ulong	nmach;		/* processors */
+	ulong	nproc;		/* processes */
+	ulong	npage0;		/* total physical pages of memory */
+	ulong	npage1;		/* total physical pages of memory */
+	ulong	npage;		/* total physical pages of memory */
+	ulong	base0;		/* base of bank 0 */
+	ulong	base1;		/* base of bank 1 */
+	ulong	ialloc;		/* max interrupt time allocation in bytes */
+
+	int	nscc;	/* number of SCCs implemented */
+	ulong	smcuarts;		/* bits for SMCs to define as eiaN */
+	ulong	sccuarts;		/* bits for SCCs to define as eiaN  */
+	int	nocts2;	/* CTS2 and CD2 aren't connected */
+	uchar*	nvrambase;	/* virtual address of nvram */
+	ulong	nvramsize;	/* size in bytes */
+};
+
+#include "../port/portdat.h"
+
+/*
+ *  machine dependent definitions not used by ../port/dat.h
+ */
+
+struct Mach
+{
+	/* OFFSETS OF THE FOLLOWING KNOWN BY l.s */
+	int	machno;			/* physical id of processor (unused) */
+	ulong	splpc;			/* pc of last caller to splhi (unused) */
+	int	mmask;			/* 1<<m->machno (unused) */
+
+	/* ordering from here on irrelevant */
+	ulong	ticks;			/* of the clock since boot time */
+	Proc	*proc;			/* current process on this processor */
+	Label	sched;			/* scheduler wakeup */
+	Lock	alarmlock;		/* access to alarm list */
+	void	*alarm;			/* alarms bound to this clock */
+	int	nrdy;
+	int	speed;	/* general system clock in MHz */
+	long	oscclk;	/* oscillator frequency (MHz) */
+	long	cpuhz;	/* general system clock (cycles) */
+	long	clockgen;	/* clock generator frequency (cycles) */
+	int	cputype;
+	ulong	delayloop;
+	ulong*	bcsr;
+	IMM*	iomem;	/* MPC8xx internal i/o control memory */
+
+	/* MUST BE LAST */
+	int	stack[1];
+};
+extern	Mach	mach0;
+
+
+/*
+ *  a parsed .ini line
+ */
+#define NISAOPT		8
+
+struct ISAConf {
+	char*	type;
+	ulong	port;
+	ulong	irq;
+	ulong	mem;
+	int	dma;
+	ulong	size;
+	ulong	freq;
+	uchar	bus;
+
+	int	nopt;
+	char*	opt[NISAOPT];
+};
+
+struct Map {
+	int	size;
+	ulong	addr;
+};
+
+struct RMap {
+	char*	name;
+	Map*	map;
+	Map*	mapend;
+
+	Lock;
+};
+
+struct Power {
+	Dev*	dev;
+	int	(*powerdown)(Power*);
+	int	(*powerup)(Power*);
+	int	state;
+	void*	arg;
+};
+
+extern register Mach	*m;
+extern register Proc	*up;
--- /dev/null
+++ b/os/fads/fads
@@ -1,0 +1,124 @@
+# fads board with remote file system on ether or ppp
+dev
+	root
+	cons	archfads screen
+	env
+	mnt
+	pipe
+	prog
+	rtc
+	srv
+	dup
+	ssl
+	cap
+
+	draw
+	pointer
+
+	ip	bootp ip ipv6 ipaux iproute arp netlog ptclbsum iprouter plan9 nullmedium pktmedium
+	ether netif netaux
+	uart
+	flash
+#	usb
+	touch	spi
+#	pcmcia	cis
+#	ata	inb
+
+	ftl
+#	kfs		chk kcon console dat dentry fcall fs fswren iobuf kfs sub uid
+#	kprof
+
+#	vid	i2c
+	i2c	i2c
+
+ip
+	il
+	tcp
+	udp
+	ipifc
+	icmp
+	icmp6
+	ipmux
+
+lib
+	interp
+	tk
+	draw
+	memlayer
+	memdraw
+	keyring
+	sec
+	mp
+	math
+	kern
+
+link
+	etherscc
+	ethermedium
+	flashamd29f0x0
+#	pppmedium ppp compress
+
+mod
+	sys
+	draw
+	tk
+	math
+	keyring
+	crypt
+	ipints
+
+
+port
+	alarm
+	alloc
+	allocb
+	chan
+	dev
+	dial
+	dis
+	discall
+	exception
+	exportfs
+	inferno
+	latin1
+	nocache
+	nodynld
+	parse
+	pgrp
+	print
+	proc
+	qio
+	qlock
+	random
+	sysfile
+	taslock
+	xalloc
+
+code
+	int cflag = 0;
+	int consoleprint = 1;
+	int panicreset = 0;
+	int kernel_pool_pcnt = 10;
+	int main_pool_pcnt = 40;
+	int heap_pool_pcnt = 20;
+	int image_pool_pcnt = 40;
+
+init
+	mpcinit
+
+root
+	/chan
+	/dev
+	/dis
+	/env
+	/fd	/
+	/n
+	/net
+	/nvfs /
+	/prog
+	/icons
+	/osinit.dis
+	/dis/lib/auth.dis
+	/dis/lib/ssl.dis
+	/n/local /
+	/nvfs/default	/usr/inferno/keyring/default
--- /dev/null
+++ b/os/fads/fns.h
@@ -1,0 +1,117 @@
+#include "../port/portfns.h"
+
+void	addpower(Power*);
+void	archbacklight(int);
+void	archconfinit(void);
+void	archdisableuart(int);
+void	archdisableusb(void);
+void	archdisablevideo(void);
+void	archenableuart(int, int);
+void	archenableusb(int, int);
+void	archenablevideo(void);
+void	archkbdinit(void);
+void	archresetvideo(void);
+int	archetherenable(int, int*, int*, int, int);
+void	archinit(void);
+int	archoptionsw(void);
+void	archreboot(void);
+void	archsetirxcvr(int);
+uchar*	archvideobuffer(long);
+ulong	baudgen(int, int);
+int	brgalloc(void);
+void	brgfree(int);
+int	cistrcmp(char*, char*);
+int	cistrncmp(char*, char*, int);
+void	clockcheck(void);
+void	clockinit(void);
+void	clockintr(Ureg*);
+void	clrfptrap(void);
+#define	coherence()		/* nothing needed for uniprocessor */
+void	cpminit(void);
+void	cpuidprint(void);
+void	dcflush(void*, ulong);
+void	dcinval(void*, ulong);
+void	delay(int);
+void	dtlbmiss(void);
+void	dumplongs(char*, ulong*, int);
+void	dumpregs(Ureg*);
+void	eieio(void);
+void	faultpower(Ureg*);
+void	firmware(int);
+void	fpinit(void);
+int	fpipower(Ureg*);
+void	fpoff(void);
+void	fprestore(FPU*);
+void	fpsave(FPU*);
+ulong	fpstatus(void);
+char*	getconf(char*);
+ulong	getdar(void);
+ulong	getdec(void);
+ulong	getdepn(void);
+ulong	getdsisr(void);
+ulong	getimmr(void);
+ulong	getmsr(void);
+ulong	getpvr(void);
+ulong	gettbl(void);
+ulong	gettbu(void);
+void	gotopc(ulong);
+void	icflush(void*, ulong);
+void	idle(void);
+#define	idlehands()			/* nothing to do in the runproc */
+void	intr(Ureg*);
+void	intrenable(int, void (*)(Ureg*, void*), void*, int, char*);
+void	intrdisable(int, void (*)(Ureg*, void*), void*, int, char*);
+int	intrstats(char*, int);
+void	intrvec(void);
+int	isaconfig(char*, int, ISAConf*);
+int	isvalid_va(void*);
+void	itlbmiss(void);
+void	kbdinit(void);
+void	kbdreset(void);
+void	lcdpanel(int);
+void	links(void);
+void	mapfree(RMap*, ulong, int);
+void	mapinit(RMap*, Map*, int);
+void	mathinit(void);
+void	mmuinit(void);
+ulong*	mmuwalk(ulong*, ulong, int);
+void	pcmenable(void);
+void	pcmintrenable(int, void (*)(Ureg*, void*), void*);
+int	pcmpin(int slot, int type);
+void	pcmpower(int, int);
+int	pcmpowered(int);
+void	pcmsetvcc(int, int);
+void	pcmsetvpp(int, int);
+int	pcmslotsavail(int);
+void	procsave(Proc*);
+void	procsetup(Proc*);
+void	putdec(ulong);
+void	putmsr(ulong);
+void	puttwb(ulong);
+ulong	rmapalloc(RMap*, ulong, int, int);
+void	screeninit(void);
+int	screenprint(char*, ...);			/* debugging */
+void	screenputs(char*, int);
+int	segflush(void*, ulong);
+void	setpanic(void);
+long	spioutin(void*, long, void*);
+void	spireset(void);
+ulong	_tas(ulong*);
+void	trapinit(void);
+void	trapvec(void);
+void	uartinstall(void);
+void	uartspecial(int, int, Queue**, Queue**, int (*)(Queue*, int));
+void	uartwait(void);	/* debugging */
+void	videoreset(void);
+void	videotest(void);
+void	wbflush(void);
+
+#define	waserror()	(up->nerrlab++, setlabel(&up->errlab[up->nerrlab-1]))
+ulong	getcallerpc(void*);
+
+#define KADDR(a)	((void*)((ulong)(a)|KZERO))
+#define PADDR(a)	((((ulong)(a)&KSEGM)!=KSEG0)?(ulong)(a):((ulong)(a)&~KZERO))
+
+/* IBM bit field order */
+#define	IBIT(b)	((ulong)1<<(31-(b)))
+#define	SIBIT(n)	((ushort)1<<(15-(n)))
--- /dev/null
+++ b/os/fads/io.h
@@ -1,0 +1,1 @@
+#include "../mpc/800io.h"
--- /dev/null
+++ b/os/fads/main.c
@@ -1,0 +1,392 @@
+#include	"u.h"
+#include	"../port/lib.h"
+#include	"mem.h"
+#include	"dat.h"
+#include	"fns.h"
+#include	"io.h"
+#include	"ureg.h"
+#include "version.h"
+
+/* where b.com or qboot leaves configuration info */
+#define BOOTARGS	((char*)CONFADDR)
+#define	BOOTARGSLEN	1024
+#define	MAXCONF		32
+
+extern ulong kerndate;
+extern int cflag;
+int	remotedebug;
+
+extern int main_pool_pcnt;
+extern int heap_pool_pcnt;
+extern int image_pool_pcnt;
+
+char	bootargs[BOOTARGSLEN+1];
+char bootdisk[KNAMELEN];
+char *confname[MAXCONF];
+char *confval[MAXCONF];
+int nconf;
+
+extern void addconf(char *, char *);
+
+/*
+ *  arguments passed to initcode and /boot
+ */
+char argbuf[128];
+
+static void
+options(void)
+{
+	long i, n;
+	char *cp, *line[MAXCONF], *p, *q;
+
+	/*
+	 *  parse configuration args from bootstrap
+	 */
+	memmove(bootargs, BOOTARGS, BOOTARGSLEN);	/* where b.com leaves its config */
+	cp = bootargs;
+	cp[BOOTARGSLEN-1] = 0;
+
+	/*
+	 * Strip out '\r', change '\t' -> ' '.
+	 */
+	p = cp;
+	for(q = cp; *q; q++){
+		if(*q == '\r')
+			continue;
+		if(*q == '\t')
+			*q = ' ';
+		*p++ = *q;
+	}
+	*p = 0;
+
+	n = getfields(cp, line, MAXCONF, 1, "\n");
+	for(i = 0; i < n; i++){
+		if(*line[i] == '#')
+			continue;
+		cp = strchr(line[i], '=');
+		if(cp == 0)
+			continue;
+		*cp++ = 0;
+		confname[nconf] = line[i];
+		confval[nconf] = cp;
+		nconf++;
+	}
+}
+
+void
+doc(char *m)
+{
+	USED(m);
+	print("%s...\n", m); uartwait();
+}
+
+static void
+poolsizeinit(void)
+{
+	ulong nb;
+
+	nb = conf.npage*BY2PG;
+	poolsize(mainmem, (nb*main_pool_pcnt)/100, 0);
+	poolsize(heapmem, (nb*heap_pool_pcnt)/100, 0);
+	poolsize(imagmem, (nb*image_pool_pcnt)/100, 1);
+}
+
+static void
+serialconsole(void)
+{
+	char *p;
+	int port, baud;
+
+	p = getconf("console");
+	if(p == nil)
+		p = "0";
+	if(p != nil && !remotedebug){
+		port = strtol(p, nil, 0);
+		baud = 9600;
+		p = getconf("baud");
+		if(p != nil){
+			baud = strtol(p, nil, 0);
+			if(baud < 9600)
+				baud = 9600;
+		}
+		uartspecial(port, baud, &kbdq, &printq, kbdcr2nl);
+	}
+}
+
+void
+main(void)
+{
+	machinit();
+	options();
+	archinit();
+	quotefmtinstall();
+	confinit();
+	cpminit();
+	xinit();
+	poolsizeinit();
+	trapinit();
+	mmuinit();
+	printinit();
+	uartinstall();
+	serialconsole();
+	doc("screeninit");
+	screeninit();
+	doc("kbdinit");
+	kbdinit();
+	doc("clockinit");
+	clockinit();
+	doc("procinit");
+	procinit();
+	cpuidprint();
+	doc("links");
+	links();
+	doc("chandevreset");
+	chandevreset();
+
+	eve = strdup("inferno");
+
+	print("\nInferno %s\n", VERSION);
+	print("Vita Nuova\n");
+	print("conf %s (%lud) jit %d\n\n",conffile, kerndate, cflag);
+
+	doc("userinit");
+	userinit();
+	doc("schedinit");
+	schedinit();
+}
+
+void
+machinit(void)
+{
+	int n;
+
+	n = m->machno;
+	memset(m, 0, sizeof(Mach));
+	m->machno = n;
+	m->mmask = 1<<m->machno;
+	m->iomem = KADDR(getimmr() & ~0xFFFF);
+	m->cputype = getpvr()>>16;
+	m->delayloop = 20000;	/* initial estimate only; set by clockinit */
+	m->speed = 50;	/* initial estimate only; set by archinit */
+}
+
+void
+init0(void)
+{
+	Osenv *o;
+	int i;
+	char buf[2*KNAMELEN];
+
+	up->nerrlab = 0;
+
+	spllo();
+
+	if(waserror())
+		panic("init0");
+	/*
+	 * These are o.k. because rootinit is null.
+	 * Then early kproc's will have a root and dot.
+	 */
+	o = up->env;
+	o->pgrp->slash = namec("#/", Atodir, 0, 0);
+	cnameclose(o->pgrp->slash->name);
+	o->pgrp->slash->name = newcname("/");
+	o->pgrp->dot = cclone(o->pgrp->slash);
+
+	chandevinit();
+
+	if(!waserror()){
+		ksetenv("cputype", "power", 0);
+		snprint(buf, sizeof(buf), "power %s", conffile);
+		ksetenv("terminal", buf, 0);
+		poperror();
+	}
+	for(i = 0; i < nconf; i++)
+		if(confname[i][0] != '*'){
+			if(!waserror()){
+				ksetenv(confname[i], confval[i], 0);
+				poperror();
+			}
+		}
+
+	poperror();
+	disinit("/osinit.dis");
+}
+
+void
+userinit(void)
+{
+	Proc *p;
+	Osenv *o;
+
+	p = newproc();
+	o = p->env;
+
+	o->fgrp = newfgrp(nil);
+
+	o->pgrp = newpgrp();
+	o->egrp = newegrp();
+	kstrdup(&o->user, eve);
+
+	strcpy(p->text, "interp");
+
+	/*
+	 * Kernel Stack
+	 */
+	p->sched.pc = (ulong)init0;
+	p->sched.sp = (ulong)p->kstack+KSTACK;
+
+	ready(p);
+}
+
+Conf	conf;
+
+void
+addconf(char *name, char *val)
+{
+	if(nconf >= MAXCONF)
+		return;
+	confname[nconf] = name;
+	confval[nconf] = val;
+	nconf++;
+}
+
+char*
+getconf(char *name)
+{
+	int i;
+
+	for(i = 0; i < nconf; i++)
+		if(cistrcmp(confname[i], name) == 0)
+			return confval[i];
+	return 0;
+}
+
+void
+confinit(void)
+{
+	char *p;
+	int pcnt;
+
+	if(p = getconf("*kernelpercent"))
+		pcnt = 100 - strtol(p, 0, 0);
+	else
+		pcnt = 0;
+
+	conf.nscc = 4;
+	conf.smcuarts = 1<<0;	/* SMC1 (usual console) */
+	conf.sccuarts = 1<<1;	/* SCC2 available by default */
+
+	archconfinit();
+	
+	conf.npage = conf.npage0 + conf.npage1;
+	if(pcnt < 10)
+		pcnt = 70;
+	conf.ialloc = (((conf.npage*(100-pcnt))/100)/2)*BY2PG;
+
+	conf.nproc = 100 + ((conf.npage*BY2PG)/MB)*5;
+	conf.nmach = MAXMACH;
+}
+
+void
+exit(int ispanic)
+{
+	up = 0;
+	spllo();
+	print("cpu %d exiting\n", m->machno);
+
+	/* Shutdown running devices */
+	chandevshutdown();
+
+	delay(1000);
+	splhi();
+	if(ispanic)
+		for(;;);
+	archreboot();
+}
+
+void
+reboot(void)
+{
+	exit(0);
+}
+
+void
+halt(void)
+{
+	print("cpu halted\n");
+	microdelay(1000);
+	for(;;)
+		;
+}
+
+int
+isaconfig(char *class, int ctlrno, ISAConf *isa)
+{
+	char cc[KNAMELEN], *p;
+	int i;
+
+	snprint(cc, sizeof cc, "%s%d", class, ctlrno);
+	p = getconf(cc);
+	if(p == nil)
+		return 0;
+
+	isa->nopt = tokenize(p, isa->opt, NISAOPT);
+	for(i = 0; i < isa->nopt; i++){
+		p = isa->opt[i];
+		if(cistrncmp(p, "type=", 5) == 0)
+			isa->type = p + 5;
+		else if(cistrncmp(p, "port=", 5) == 0)
+			isa->port = strtoul(p+5, &p, 0);
+		else if(cistrncmp(p, "irq=", 4) == 0)
+			isa->irq = strtoul(p+4, &p, 0);
+		else if(cistrncmp(p, "mem=", 4) == 0)
+			isa->mem = strtoul(p+4, &p, 0);
+		else if(cistrncmp(p, "size=", 5) == 0)
+			isa->size = strtoul(p+5, &p, 0);
+		else if(cistrncmp(p, "freq=", 5) == 0)
+			isa->freq = strtoul(p+5, &p, 0);
+		else if(cistrncmp(p, "dma=", 4) == 0)
+			isa->dma = strtoul(p+4, &p, 0);
+	}
+	return 1;
+}
+
+/*
+ *  Save the mach dependent part of the process state.
+ */
+void
+procsave(Proc*)
+{
+}
+
+void
+uartputs(char *s, int n)
+{
+//	screenputs(buf, n);
+	putstrn(s, n);
+	uartwait();
+}
+
+/* stubs */
+void
+setfsr(ulong)
+{
+}
+
+ulong
+getfsr()
+{
+	return 0;
+}
+
+void
+setfcr(ulong)
+{
+}
+
+ulong
+getfcr()
+{
+	return 0;
+}
--- /dev/null
+++ b/os/fads/mem.h
@@ -1,0 +1,157 @@
+/*
+ * Memory and machine-specific definitions.  Used in C and assembler.
+ */
+
+/*
+ * Sizes
+ */
+
+#define	BI2BY		8			/* bits per byte */
+#define BI2WD		32			/* bits per word */
+#define	BY2WD		4			/* bytes per word */
+#define	BY2V		8			/* bytes per double word */
+#define	BY2PG		4096			/* bytes per page */
+#define	WD2PG		(BY2PG/BY2WD)		/* words per page */
+#define	PGSHIFT		12			/* log(BY2PG) */
+#define ROUND(s, sz)	(((s)+(sz-1))&~(sz-1))
+#define PGROUND(s)	ROUND(s, BY2PG)
+#define	CACHELINELOG	4
+#define CACHELINESZ	(1<<CACHELINELOG)
+
+#define	MAXMACH		1			/* max # cpus system can run */
+#define	MACHSIZE	BY2PG
+
+/*
+ * Time
+ */
+#define HZ		100			/* clock frequency */
+#define	MS2HZ		(1000/HZ)		/* millisec per clock tick */
+#define	TK2SEC(t)	((t)/HZ)		/* ticks to seconds */
+#define	MS2TK(t)	((t)/MS2HZ)		/* milliseconds to ticks */
+#define	MHz	1000000
+
+/*
+ * MSR bits
+ */
+
+#define	POW	0x40000	/* enable power mgmt */
+#define	TGPR	0x20000	/* GPR0-3 remapped; 603/603e specific */
+#define	ILE	0x10000	/* interrupts little endian */
+#define	EE	0x08000	/* enable external/decrementer interrupts */
+#define	PR	0x04000	/* =1, user mode */
+#define	FPE	0x02000	/* enable floating point */
+#define	ME	0x01000	/* enable machine check exceptions */
+#define	FE0	0x00800
+#define	SE	0x00400	/* single-step trace */
+#define	BE	0x00200	/* branch trace */
+#define	FE1	0x00100
+#define	MSR_IP	0x00040	/* =0, vector to nnnnn; =1, vector to FFFnnnnn */
+#define	IR	0x00020	/* enable instruction address translation */
+#define	DR	0x00010	/* enable data address translation */
+#define	RI	0x00002	/* exception is recoverable */
+#define	LE	0x00001	/* little endian mode */
+
+#define	KMSR	(ME|FE0|FE1|FPE)
+#define	UMSR	(KMSR|PR|EE|IR|DR)
+
+/*
+ * Magic registers
+ */
+
+#define	MACH		30		/* R30 is m-> */
+#define	USER		29		/* R29 is up-> */
+#define	IOMEMR		28		/* R28 will be iomem-> */
+
+/*
+ * Fundamental addresses
+ */
+
+#define	UREGSIZE	((8+32)*4)
+
+/*
+ * MMU
+ */
+
+/* L1 table entry and Mx_TWC flags */
+#define PTEVALID	(1<<0)
+#define PTEWT		(1<<1)	/* write through */
+#define PTE4K		(0<<2)
+#define PTE512K	(1<<2)
+#define PTE8MB	(3<<2)
+#define PTEG		(1<<4)	/* guarded */
+
+/* L2 table entry and Mx_RPN flags (also PTEVALID) */
+#define PTECI		(1<<1)	/*  cache inhibit */
+#define PTESH		(1<<2)	/* page is shared; ASID ignored */
+#define PTELPS		(1<<3)	/* large page size */
+#define PTEWRITE	0x9F0
+
+/* TLB and MxEPN flag */
+#define	TLBVALID	(1<<9)
+
+/*
+ * Address spaces
+ */
+
+#define	KUSEG	0x00000000
+#define KSEG0	0x20000000
+#define	KSEGM	0xE0000000	/* mask to check which seg */
+
+#define	KZERO	KSEG0			/* base of kernel address space */
+#define	KTZERO	(KZERO+0x3000)	/* first address in kernel text */
+#define	KSTACK	8192	/* Size of kernel stack */
+
+#define	CONFADDR	(KZERO|0x200000)	/* where qboot leaves configuration info */
+
+/*
+ * Exception codes (trap vectors)
+ */
+#define	CRESET	0x01
+#define	CMCHECK 0x02
+#define	CDSI	0x03
+#define	CISI	0x04
+#define	CEI	0x05
+#define	CALIGN	0x06
+#define	CPROG	0x07
+#define	CFPU	0x08
+#define	CDEC	0x09
+#define	CSYSCALL 0x0C
+#define	CTRACE	0x0D
+#define	CFPA	0x0E
+/* rest are power-implementation dependent (8xx) */
+#define	CEMU	0x10
+#define	CIMISS	0x11
+#define	CDMISS	0x12
+#define	CITLBE	0x13
+#define	CDTLBE	0x14
+#define	CDBREAK	0x1C
+#define	CIBREAK	0x1D
+#define	CPBREAK	0x1E
+#define	CDPORT	0x1F
+
+/*
+ * MPC8xx physical addresses
+ */
+
+/* those encouraged by mpc8bug */
+#define	PHYSDRAM	0x00000000
+#define	PHYSBCSR	0x02100000
+#define	PHYSIMM	0x02200000
+#define	PHYSFLASH	0x02800000
+
+/* remaining ones are our choice */
+#define	PHYSSDRAM	0x03000000
+#define	PHYSPCMCIA	0x04000000
+#define	PCMCIALEN	(8*MB)	/* chosen to allow mapping by single TLB entry */
+#define	ISAIO	(KZERO|PHYSPCMCIA)	/* for inb.s */
+
+/*
+ * MPC8xx dual-ported CPM memory physical addresses
+ */
+#define	PHYSDPRAM	(PHYSIMM+0x2000)
+#define	DPLEN1	0x200
+#define	DPLEN2	0x400
+#define	DPLEN3	0x800
+#define	DPBASE	(PHYSDPRAM+DPLEN1)
+
+#define KEEP_ALIVE_KEY 0x55ccaa33	/* clock and rtc register key */
--- /dev/null
+++ b/os/fads/mkfile
@@ -1,0 +1,107 @@
+SYSTARG=Inferno
+OBJTYPE=power
+<../../mkconfig
+
+#Configurable parameters
+
+CONF=fads			#default configuration
+CONFLIST=fads fadskfs fads2
+CLEANCONFLIST=paq
+KZERO=0x20003020
+
+SYSTARG=$OSTARG
+OBJTYPE=power
+INSTALLDIR=$ROOT/Inferno/$OBJTYPE/bin	#path of directory where kernel is installed
+#INSTALLDIR=/$OBJTYPE
+
+#end configurable parameters
+
+<$ROOT/mkfiles/mkfile-$SYSTARG-$OBJTYPE	#set vars based on target system
+
+<| $SHELLNAME ../port/mkdevlist $CONF	#sets $IP, $DEVS, $ETHERS, $VGAS, $PORT, $MISC, $LIBS, $OTHERS
+
+OBJ=\
+	l.$O\
+	tlb.$O\
+	nofp.$O\
+	clock.$O\
+	cpm.$O\
+	faultpower.$O\
+	fpi.$O\
+	fpimem.$O\
+	fpipower.$O\
+	kbd.$O\
+	main.$O\
+	mmu.$O\
+	rmap.$O\
+	trap.$O\
+	$CONF.root.$O\
+	$IP\
+	$DEVS\
+	$ETHERS\
+	$LINKS\
+	$VGAS\
+	$PORT\
+	$MISC\
+	$OTHERS\
+
+LIBNAMES=${LIBS:%=lib%.a}
+
+HFILES=\
+	mem.h\
+	dat.h\
+	fns.h\
+	io.h\
+	../mpc/800io.h\
+	../mpc/screen.h\
+
+CFLAGS=-wFV -I. -I../mpc -I../port -I$ROOT/Inferno/$OBJTYPE/include -I$ROOT/include -I$ROOT/libinterp
+KERNDATE=`{$NDATE}
+
+#default:V: i$CONF.sq
+default:V:	i$CONF
+
+i$CONF:	$OBJ $CONF.c $CONF.root.h $LIBNAMES
+	$CC $CFLAGS '-DKERNDATE='$KERNDATE $CONF.c
+	$LD -o $target -T$KZERO -l $OBJ $CONF.$O $LIBFILES
+	$KSIZE $target
+
+i$CONF.sq:	i$CONF
+	sqz -w i$CONF >$target
+
+install:V: i$CONF # i$CONF.sq
+	cp i$CONF $INSTALLDIR/i$CONF
+	#cp i$CONF.sq $INSTALLDIR/i$CONF.sq
+
+uninstall:V:
+	rm -f $ROOT/$OBJDIR/bin/i$CONF
+	rm -f $ROOT/$OBJDIR/bin/i$CONF.sq
+
+<../port/portmkfile
+
+../init/$INIT.dis:	../init/$INIT.b
+		cd ../init; mk $INIT.dis
+
+%.$O:		../mpc/%.c
+	$CC $CFLAGS -I. -I../mpc ../mpc/$stem.c
+
+%.$O:		../mpc/%.s
+	$AS -I. -I../mpc ../mpc/$stem.s
+
+clock.$O:	$ROOT/Inferno/$OBJTYPE/include/ureg.h
+devether.$O:	$ROOT/Inferno/$OBJTYPE/include/ureg.h
+faultpower.$O:	$ROOT/Inferno/$OBJTYPE/include/ureg.h
+main.$O:	$ROOT/Inferno/$OBJTYPE/include/ureg.h
+trap.$O:	$ROOT/Inferno/$OBJTYPE/include/ureg.h
+screen.h:NV:	../mpc/screen.h
+
+devether.$O $ETHERS:	../mpc/etherif.h ../port/netif.h
+
+#$VGAS:		screen.h vga.h
+$IP devip.$O:		../ip/ip.h
+
+devboot.$O:	devboot.c
+	$CC $CFLAGS devboot.c
+
+devuart.$O:	../mpc/devuart.c
+	$CC $CFLAGS ../mpc/devuart.c
--- /dev/null
+++ b/os/fads/mmu.c
@@ -1,0 +1,20 @@
+#include	"u.h"
+#include	"../port/lib.h"
+#include	"mem.h"
+#include	"dat.h"
+#include	"fns.h"
+
+void
+mmuinit(void)
+{
+	/* the l.s initial TLB settings do all that's required */
+}
+
+int
+segflush(void *a, ulong n)
+{
+	/* flush dcache then invalidate icache */
+	dcflush(a, n);
+	icflush(a, n);
+	return 0;
+}
--- /dev/null
+++ b/os/fads/tlb.s
@@ -1,0 +1,23 @@
+#include	"mem.h"
+
+#define	MB	(1024*1024)
+
+/*
+ * TLB prototype entries, loaded once-for-all at startup,
+ * remaining unchanged thereafter.
+ * Limit the table to at most 8 entries to ensure
+ * it works on the 823 (other 8xx processors allow up to 32 TLB entries).
+ */
+#define	TLBE(epn,rpn,twc)	WORD	$(epn);  WORD	$(twc); WORD	$(rpn)
+
+TEXT	tlbtab(SB), $-4
+
+	/* epn, rpn, twc */
+	TLBE(KZERO|PHYSDRAM|TLBVALID, PHYSDRAM|PTEWRITE|PTELPS|PTESH|PTEVALID, PTE8MB|/*PTEWT|*/PTEVALID)	/* DRAM, 8M */
+	TLBE(KZERO|PHYSBCSR|TLBVALID, PHYSBCSR|PTEWRITE|PTESH|PTECI|PTEVALID, PTE4K|PTEWT|PTEVALID)	/* Board CSR, 4K */
+	TLBE(KZERO|PHYSIMM|TLBVALID, PHYSIMM|PTEWRITE|PTELPS|PTESH|PTECI|PTEVALID, PTE4K|PTEWT|PTEVALID)	/* IMMR, 16K */
+	TLBE(KZERO|PHYSFLASH|TLBVALID, PHYSFLASH|PTEWRITE|PTELPS|PTESH|PTECI|PTEVALID, PTE8MB|PTEWT|PTEVALID)	/* Flash, 8M */
+	TLBE(KZERO|PHYSSDRAM|TLBVALID, PHYSSDRAM|PTEWRITE|PTELPS|PTESH|PTEVALID, PTE8MB|/*PTEWT|*/PTEVALID)	/* SDRAM, 8M */
+	TLBE(KZERO|PHYSPCMCIA|TLBVALID, PHYSPCMCIA|PTEWRITE|PTELPS|PTESH|PTECI|PTEVALID, PTE8MB|PTEWT|PTEG|PTEVALID)	/* PCMCIA, 8M */
+TEXT	tlbtabe(SB), $-4
+	RETURN
--- /dev/null
+++ b/os/gum/Firstboot
@@ -1,0 +1,51 @@
+GUM> cp.b 0xa2000000 0xa0020000 881240
+
+GUM> go 0xa0020020
+
+## Starting application at 0xA0020020 ...
+
+Inferno
+confinit...
+xinit...
+mmuinit...
+trapinit...
+printinit...
+uartinstall...
+i2c: unexpected status: 0442eepromscan: -1
+adinit: 04000300 -> 04000300 mcs0=128c26ab
+value=00003332
+ether91c111: chip 0x92 detected
+398 MHz id 69052d06
+
+Inferno Fourth Edition (20070219)
+Vita Nuova
+conf gum (1171905918) jit 0
+
+pmcr=00000000 pwer=00000003 prer=00000003 pfer=00000003 pedr=00000000 pcfr=00000000 rcsr=00000001
+cccr=00000161 cken=00017fff oscc=00000003
+msc0=128c26ab mcs1=0000128c mcs2=7ff07ff0
+clkcfg=00000002
+Initial Dis: "/osinit.dis"
+can't open #F/flash/flashctl: no free devices
+root from (kernel)[kernel] 
+time warped
+cerf$ 
+
+date
+
+cerf$ cerf$ date: '/dis/date.dis' does not exist
+cerf$ cerf$ date
+
+
+pdate: '/dis/date.dis' does not exist
+cerf$ cerf$ cerf$ s
+
+
+
+
+
+
+       2        1    inferno    0:00.0    release    12K Sh[$Sys]
+
+       5        1    inferno    0:00.0      ready    13K Ps[$Sys]
+cerf$ cerf$ cerf$ cerf$ cerf$ cerf$ cerf$ 
--- /dev/null
+++ b/os/gum/NOTICE
@@ -1,0 +1,2 @@
+Inferno® Copyright © 1996-1999 Lucent Technologies Inc.  All rights reserved.
+Gumstix® Inferno port Copyright © 2007 Vita Nuova Holdings Limited
--- /dev/null
+++ b/os/gum/README
@@ -1,0 +1,8 @@
+Booting Inferno on a Gumstix/Etherstix
+
+This is a preliminary version (work in progress) of Inferno
+on a Gumstix (with Etherstix daughterboard and tweener for console).
+The current source is on its way, after I do a few more checks.
+
+forsyth@vitanuova.com
+Sat Apr  7 13:53:23 BST 2007
--- /dev/null
+++ b/os/gum/gum
@@ -1,0 +1,165 @@
+dev
+	root
+	cons archgum
+	env
+	mnt
+	pipe
+	prog
+	rtc
+	srv
+	dup
+	ssl
+	cap
+#	sign
+
+#	draw	screen
+#	pointer
+	uart
+	ip	bootp ip ipv6 ipaux iproute arp netlog ptclbsum iprouter plan9 nullmedium pktmedium netaux
+	flash
+#	ftl
+#	pcmcia	cis
+#	ata
+	ether netif netaux
+
+#	cerf
+#	kprof
+	i2c	i2c
+
+ip
+	il
+	tcp
+	udp
+#	rudp
+#	igmp
+	ipifc
+	icmp
+	icmp6
+#	ipmux
+
+link	
+	flashcfi16
+	ether91c111	# ethermii
+	ethermedium
+
+lib
+	interp
+#	tk
+#	draw
+#	memlayer
+#	memdraw
+	keyring
+	math
+	sec
+	mp
+	kern
+
+mod
+	math
+	sys
+#	draw
+#	tk
+	keyring
+	crypt
+	ipints
+
+
+port
+	alarm
+	alloc
+	allocb
+	chan
+	dev
+	dial
+	dis
+	discall
+	exception
+	exportfs
+	inferno
+	latin1
+	nocache
+	nodynld
+	parse
+	pgrp
+	print
+	proc
+	qio
+	qlock
+	random
+	sysfile
+	taslock
+	xalloc
+
+code
+	int main_pool_pcnt = 50;
+	int heap_pool_pcnt = 50;
+	int image_pool_pcnt = 0;
+	int cflag = 0;	/* for JIT */
+
+	int consoleprint = 1;
+	char debug_keys = 1;
+	int panicreset = 0;
+	void screeninit(void){}
+
+init
+	cerfinit
+
+root
+	/chan	/
+	/dev	/
+	/env	/
+	/fd	/
+	/net	/
+	/net.alt	/
+	/nvfs /
+	/prog	/
+	/root	/
+	/nvfs	/
+	/osinit.dis
+	/tmp	/
+	/dis
+	/dis/lib
+	/dis/disk
+
+# dos file system
+	/dis/dossrv.dis
+	/dis/lib/arg.dis
+	/dis/lib/styx.dis
+	/dis/lib/string.dis
+	/dis/lib/daytime.dis
+
+	/dis/disk/format.dis
+
+# For development work:
+	/dis/sh.dis	/dis/tiny/sh.dis
+	/dis/ls.dis
+	/dis/cat.dis
+	/dis/bind.dis
+	/dis/mount.dis
+	/dis/pwd.dis
+	/dis/echo.dis
+	/dis/cd.dis
+	/dis/xd.dis
+	/dis/cp.dis
+	/dis/mkdir.dis
+	/dis/rm.dis
+	/dis/p.dis
+	/dis/ps.dis
+	/dis/lib/readdir.dis
+	/dis/lib/workdir.dis
+	/dis/lib/daytime.dis
+	/dis/lib/auth.dis
+	/dis/lib/ssl.dis
+	/dis/lib/bufio.dis
+	/dis/lib/string.dis
+#	/dis/pcmcia.dis	/usr/forsyth/pcmcia.dis
+
+	/n/remote
+	/n/local
+	/n/client
+	/n/rdbg
+	/n/dump
+	/n/disk
+	/n/kfs
+# Authentication
+	/nvfs/default	/usr/inferno/keyring/default
--- /dev/null
+++ b/os/init/README
@@ -1,0 +1,5 @@
+initialisation programs used by various native kernels to set up
+the right environment to invoke a shell, a window system, mux, or
+any specialised application.
+
+even so, there are really more than needed here just now.
--- /dev/null
+++ b/os/init/bootinit.b
@@ -1,0 +1,628 @@
+#
+# Generalized boot Inferno
+#
+
+implement Init;
+
+include "sys.m";
+	sys:	Sys;
+
+include "draw.m";
+
+include "keyring.m";
+	kr: Keyring;
+
+include "security.m";
+	auth: Auth;
+	random: Random;
+
+include "tftp.m";
+
+Bootpreadlen: con 128;
+
+Init: module
+{
+	init:	fn();
+};
+
+ip: string;
+mask: string;
+fsip: string;
+bootprotocol: string;
+bootserver: string;
+bootfile: string;
+
+debug: con 0;
+
+init()
+{
+	ipavailable: int;
+	sys = load Sys Sys->PATH;
+
+	kexecfd := sys->open("#B/kexec", Sys->OWRITE);
+	if (kexecfd == nil)
+		fatal(sys->sprint("opening #B/kexec: %r"));
+
+	ipavailable = 0;
+	if (dobind("#l", "/net", sys->MREPL) && dobind("#I", "/net", sys->MAFTER))
+		ipavailable = 1;
+
+	dobind("#c", "/dev", sys->MAFTER); 	# console device
+
+	if (!ipavailable)
+		fatal("no IP stack available");
+	cfd := sys->open("/net/ipifc/clone", sys->ORDWR);
+	if(cfd == nil)
+		fatal(sys->sprint("open /net/ipifc/clone: %r"));
+
+	if (sys->fprint(cfd, "bind ether ether0") < 0)
+		fatal(sys->sprint("binding ether0: %r"));
+
+	fsready := 0;
+
+	fsip = ipconfig(cfd);
+
+	bootstring := getenvdefault("bootpath", "tftp");
+
+	(bootprotocol, bootserver, bootfile) = parsebootstring(bootstring);
+
+	if (bootprotocol == nil)
+		fatal(bootstring + ": unrecognised syntax");
+
+	# Run dhcp if necessary
+	if (bootprotocol == "tftp" && (bootserver == nil || bootfile == nil))
+		dhcp();
+
+	# determine server
+	if (bootprotocol == "net" && bootserver == nil)
+		bootserver = fsip;
+
+	if (bootserver == nil)
+		fatal("couldn't determine boot server");
+
+	if (bootfile == nil)
+		fatal("couldn't determine boot file");
+
+	if (bootprotocol == nil)
+		fatal("couldn't determine boot protocol");
+
+	sys->print("loading %s!%s!%s\n", bootprotocol, bootserver, bootfile);
+
+	if (bootprotocol == "net") {
+		sys->print("Attempting remote mount\n");
+		if (netfs(bootserver) == 0)
+			sys->print("Remote mount successful\n");
+		else
+			fatal(sys->sprint("Remote mount failed: %r"));
+		fd := sys->open("/n/remote" + bootfile, Sys->OREAD);
+		if (fd == nil)
+			fatal(sys->sprint("%s:/n/remote%s: %r", bootserver, bootfile));
+		if (sys->stream(fd, kexecfd, 4096) < 0)
+			fatal(sys->sprint("copying %s: %r", bootfile));
+	}
+	else if (bootprotocol == "tftp") {
+		tftp := load Tftp Tftp->PATH;
+		if (tftp == nil)
+			fatal("can't load tftp module");
+		tftp->init(1);
+		errstr := tftp->receive(bootserver, bootfile, kexecfd);
+		if (errstr != nil)
+			fatal("tftp: " + errstr);
+	}
+	else
+		fatal("protocol " + bootprotocol + " not supported");
+	sys->print("Launching new kernel\n");
+	kexecfd = nil;
+}
+
+parsebootstring(s: string): (string, string, string)
+{
+	proto, server, file: string;
+	(n, l) := sys->tokenize(s, "!");
+	if (n > 3)
+		return (nil, nil, nil);
+	proto = hd l;
+	l = tl l;
+	if (l != nil) {
+		server = hd l;
+		l = tl l;
+	}
+	if (l != nil)
+		file = hd l;
+	case proto {
+	"tftp" =>
+		;
+	"net" =>
+		# can't have a default file, so n must be 3
+		if (n != 3)
+			return (nil, nil, nil);
+	* =>
+		return (nil, nil, nil);
+	}
+	return (proto, server, file);
+}
+
+dobind(f, t: string, flags: int): int
+{
+	if(sys->bind(f, t, flags) < 0) {
+		err(sys->sprint("can't bind %s on %s: %r", f, t));
+		return 0;
+	}
+	return 1;
+}
+
+err(s: string)
+{
+	sys->fprint(sys->fildes(2), "bootinit: %s\n", s);
+}
+
+hang()
+{
+	<-(chan of int);
+}
+
+fatal(s: string)
+{
+	err(s);
+	hang();
+}
+
+envlist: list of string;
+
+getenv(name: string): string
+{
+	if (envlist == nil) {
+		fd := sys->open("/dev/sysenv", Sys->OREAD);
+		if (fd != nil) {
+			ntok: int;
+			buf := array[1024] of byte;
+			nr := sys->read(fd, buf, len buf);
+			if(nr > 0)
+				(ntok, envlist) = sys->tokenize(string buf, "\n");
+		}
+	}
+	ls := envlist;
+	while(ls != nil) {
+		(ntok2, ls2) := sys->tokenize(hd ls, "=");
+		if(hd ls2 == name)
+			return hd tl ls2;
+		ls = tl ls;
+	}
+	return nil;
+}
+
+getenvdefault(name: string, default: string): string
+{
+	rv := getenv(name);
+	if (rv == nil)
+		return default;
+	return rv;
+}
+
+ipconfig(cfd: ref sys->FD): string
+{
+	ip = getenv("wireip");
+	if (ip == nil)
+		ip = getenv("ip");
+	mask = getenv("ipmask");
+	fsip = getenv("fsip");
+	if (ip != nil && mask != nil) {
+		sys->print("ip %s %s\n", ip, mask);
+		sys->fprint(cfd, "add %s %s", ip, mask);
+		gwip := getenv("gwip");
+		if (gwip != nil) {
+			sys->print("gwip %s\n", gwip);
+			rfd := sys->open("/net/iproute", Sys->ORDWR);
+			if (rfd == nil || sys->fprint(rfd, "add 0.0.0.0 0.0.0.0 %s", gwip) < 0)
+				err(sys->sprint("failed to add default route: %r"));
+		}
+	}
+	if (ip == nil || mask == nil)
+		return bootp(cfd);
+	return fsip;
+}
+
+bootpdone: int;
+
+bootp(cfd: ref sys->FD): string
+{
+	if (bootpdone == 1)
+		return fsip;
+
+	bootpdone = 1;
+
+	sys->print("bootp ...");
+
+	if (sys->fprint(cfd, "bootp") < 0) {
+		sys->print("init: bootp: %r");
+		return nil;
+	}
+
+	fd := sys->open("/net/bootp", sys->OREAD);
+	if(fd == nil) {
+		err(sys->sprint("open /net/bootp: %r"));
+		return nil;
+	}
+
+	buf := array[Bootpreadlen] of byte;
+	nr := sys->read(fd, buf, len buf);
+	fd = nil;
+	if(nr <= 0) {
+		err(sys->sprint("read /net/bootp: %r"));
+		return nil;
+	}
+	(ntok, ls) := sys->tokenize(string buf, " \t\n");
+	while(ls != nil) {
+		name := hd ls;
+		ls = tl ls;
+		if (ls == nil)
+			break;
+		value := hd ls;
+		ls = tl ls;
+		if (name == "fsip")
+			fsip = value;
+		else if (name == "ipaddr")
+			ip = value;
+		else if (name == "ipmask")
+			mask = value;
+	}
+	return fsip;
+}
+
+netfs(server: string): int
+{
+	auth = load Auth  Auth->PATH;
+	if (auth != nil)
+		auth->init();
+
+	kr = load Keyring Keyring->PATH;
+	sys->print("dial...");
+	(ok, c) := sys->dial("tcp!" + server + "!6666", nil);
+	if(ok < 0)
+		return -1;
+	
+	if(kr != nil && auth != nil){
+		err: string;
+		sys->print("Authenticate ...");
+		ai := kr->readauthinfo("/nvfs/default");
+		if(ai == nil){
+			sys->print("readauthinfo /nvfs/default failed: %r\n");
+			sys->print("trying mount as `nobody'\n");
+		}
+		(c.dfd, err) = auth->client("none", ai, c.dfd);
+		if(c.dfd == nil){
+			sys->print("authentication failed: %s\n", err);
+			return -1;
+		}
+	}
+	
+	sys->print("mount ...");
+	
+	c.cfd = nil;
+	n := sys->mount(c.dfd, nil, "/n/remote", sys->MREPL, "");
+	if(n > 0)
+		return 0;
+	return -1;
+}
+
+#
+#
+# DHCP
+#
+#
+
+Dhcp: adt {
+	op: int;
+	htype: int;
+	hops: int;
+	xid: int;
+	secs: int;
+	flags: int;
+	ciaddr: int;
+	yiaddr: int;
+	siaddr: int;
+	giaddr: int;
+	chaddr: array of byte;
+	sname: string;
+	file: string;
+};
+
+nboputl(buf: array of byte, val: int)
+{
+	buf[0] = byte (val >> 24);
+	buf[1] = byte (val >> 16);
+	buf[2] = byte (val >> 8);
+	buf[3] = byte val;
+}
+
+nboputs(buf: array of byte, val: int)
+{
+	buf[0] = byte (val >> 8);
+	buf[1] = byte val;
+}
+
+nbogets(buf: array of byte): int
+{
+	return (int buf[0] << 8) | int buf[1];
+}
+
+nbogetl(buf: array of byte): int
+{
+	return (int buf[0] << 24) | (int buf[1] << 16) | (int buf[2] << 8) | int buf[3];
+}
+
+stringget(buf: array of byte): string
+{
+	for (x := 0; x < len buf; x++)
+		if (buf[x] == byte 0)
+			break;
+	if (x == 0)
+		return nil;
+	return string buf[0 : x];
+}
+
+memcmp(b1: array of byte, b2: array of byte): int
+{
+	l := len b1;
+	if (l < len b2)
+		return int -b2[l];
+	if (l > len b2)
+		return int b1[l];
+	for (i := 0; i < l; i++) {
+		d := int b1[i] - int b2[i];
+		if (d != 0)
+			return d;
+	}
+	return 0;
+}
+
+memncpy(out: array of byte, in: array of byte)
+{
+	if (in == nil)
+		return;
+	l := len in;
+	if (l > len out)
+		l = len out;
+	out[0 :] = in[0 : l];
+}
+
+memset(out: array of byte, val: byte)
+{
+	for (l := 0; l < len out; l++)
+		out[l] = val;
+}
+
+dhcpsend(dfd: ref Sys->FD, dhcp: ref Dhcp)
+{
+	buf := array[576] of byte;
+	buf[0] = byte dhcp.op;
+	buf[1] = byte dhcp.htype;
+	buf[2] = byte len dhcp.chaddr;
+	buf[3] = byte dhcp.hops;
+	nboputl(buf[4 : 8], dhcp.xid);
+	nboputs(buf[8 : 10], dhcp.secs);
+	nboputs(buf[10 : 12], dhcp.flags);
+	nboputl(buf[12 : 16], dhcp.ciaddr);
+	nboputl(buf[16 : 20], dhcp.yiaddr);
+	nboputl(buf[20 : 24], dhcp.siaddr);
+	nboputl(buf[24 : 28], dhcp.giaddr);
+	memset(buf[28 :], byte 0);
+	memncpy(buf[28 : 44], dhcp.chaddr);
+	memncpy(buf[44 : 108], array of byte dhcp.sname);
+	memncpy(buf[108 : 236], array of byte dhcp.file);
+	sys->write(dfd, buf, len buf);
+}
+
+kill(pid: int)
+{
+	fd := sys->open("#p/" + string pid + "/ctl", sys->OWRITE);
+	if (fd == nil)
+		return;
+
+	msg := array of byte "kill";
+        sys->write(fd, msg, len msg);
+}
+
+ipfmt(ipaddr: int): string
+{
+	return sys->sprint("%ud.%ud.%ud.%ud",
+		(ipaddr >> 24) & 16rff,
+		(ipaddr >> 16) & 16rff,
+		(ipaddr >> 8) & 16rff,
+		ipaddr & 16rff);
+}
+
+dumpdhcp(dhcp: ref Dhcp)
+{
+	sys->print("op %d htype %d hops %d xid %ud\n", dhcp.op, dhcp.htype, dhcp.hops, dhcp.xid);
+	sys->print("secs %d flags 0x%.4ux\n", dhcp.secs, dhcp.flags);
+	sys->print("ciaddr %s\n", ipfmt(dhcp.ciaddr));
+	sys->print("yiaddr %s\n", ipfmt(dhcp.yiaddr));
+	sys->print("siaddr %s\n", ipfmt(dhcp.siaddr));
+	sys->print("giaddr %s\n", ipfmt(dhcp.giaddr));
+	sys->print("chaddr ");
+	for (x := 0; x < len dhcp.chaddr; x++)
+		sys->print("%.2ux", int dhcp.chaddr[x]);
+	sys->print("\n");
+	if (dhcp.sname != nil)
+		sys->print("sname %s\n", dhcp.sname);
+	if (dhcp.file != nil)
+		sys->print("file %s\n", dhcp.file);
+}
+
+dhcplisten(pidc: chan of int, fd: ref Sys->FD, dc: chan of ref Dhcp)
+{
+	pid := sys->pctl(0, nil);
+	pidc <-= pid;
+	buf := array [576] of byte;
+	while (1) {
+		n := sys->read(fd, buf, len buf);
+		dhcp := ref Dhcp;
+		dhcp.op = int buf[0];
+		dhcp.htype = int buf[1];
+		hlen := int buf[2];
+		dhcp.hops = int buf[3];
+		dhcp.xid = nbogetl(buf[4 : 8]);
+		dhcp.secs = nbogets(buf[8 : 10]);
+		dhcp.flags = nbogets(buf[10 : 12]);
+		dhcp.ciaddr = nbogetl(buf[12 : 16]);
+		dhcp.yiaddr = nbogetl(buf[16 : 20]);
+		dhcp.siaddr = nbogetl(buf[20 : 24]);
+		dhcp.giaddr = nbogetl(buf[24: 28]);
+		dhcp.chaddr = buf[28 : 28 + hlen];
+		dhcp.sname = stringget(buf[44 : 108]);
+		dhcp.file = stringget(buf[108 : 236]);
+		dc <-= dhcp;
+	}
+}
+	
+timeoutproc(pid: chan of int, howlong: int, c: chan of string)
+{
+	pid <-= sys->pctl(0, nil);
+
+	sys->sleep(howlong);
+
+	# send timeout
+	c <-= "timed out";
+}
+
+tpid := -1;
+tc: chan of string;
+
+timeoutcancel()
+{
+	if (tpid >= 0) {
+		kill(tpid);
+		tpid = -1;
+	}
+}
+
+timeoutstart(howlong: int): (chan of string)
+{
+	timeoutcancel();
+	pidc := chan of int;
+	tc = chan of string;
+	spawn timeoutproc(pidc, howlong, tc);
+	tpid = <- pidc;
+	return tc;
+}
+
+atohn(b: byte): int
+{
+	if (b >= byte '0' && b <= byte '9')
+		return int (b - byte '0');
+	if (b >= byte 'A' && b <= byte 'F')
+		return int b - 'A' + 10;
+	if (b >= byte 'a' && b <= byte 'f')
+		return int b - 'a' + 10;
+	return -1;
+}
+
+atohb(buf: array of byte): int
+{
+	tn := atohn(buf[0]);
+	bn := atohn(buf[1]);
+	if (tn < 0 || bn < 0)
+		return -1;
+	return tn * 16 + bn;
+}
+
+gethaddr(dhcp: ref Dhcp): int
+{
+	fd := sys->open("#l/ether0/addr", Sys->OREAD);
+	if (fd == nil)
+		return 0;
+	buf := array [100] of byte;
+	n := sys->read(fd, buf, len buf);
+	if (n < 0)
+		return 0;
+	dhcp.htype = 1;
+	hlen := n / 2;
+	dhcp.chaddr = array [hlen] of byte;
+	for (i := 0; i < hlen; i++)
+		dhcp.chaddr[i] = byte atohb(buf[i * 2 : i * 2 + 2]);
+	return 1;
+}
+
+parsedq(dq: string): (int, int)
+{
+	(c, l) := sys->tokenize(dq, ".");
+	if (c != 4)
+		return (0, 0);
+	a := hd l;
+	l = tl l;
+	b := hd l;
+	l = tl l;
+	d := hd l;
+	l = tl l;
+	addr := (int a << 24) | (int b << 16) | (int d << 8) | int hd l;
+	return (1, addr);
+}
+
+dhcp()
+{
+	ok: int;
+	conn: Sys->Connection;
+	rdhcp: ref Dhcp;
+
+	if (random == nil)
+		random = load Random Random->PATH;
+
+	(ok, conn) = sys->dial("udp!255.255.255.255!67", "68");
+	if (!ok)
+		fatal(sys->sprint("failed to dial udp broadcast: %r"));
+
+	pidc := chan of int;
+	dc := chan of ref Dhcp;
+	spawn dhcplisten(pidc, conn.dfd, dc);
+	dhcppid := <- pidc;
+	dhcp := ref Dhcp;
+	dhcp.op = 1;
+	dhcp.htype  = 1;
+	gethaddr(dhcp);
+	dhcp.hops = 0;
+	dhcp.xid = random->randomint(Random->NotQuiteRandom);
+	dhcp.secs = 0;
+	dhcp.flags = 0;
+	(ok, dhcp.ciaddr) = parsedq(ip);
+	dhcp.yiaddr = 0;
+	dhcp.siaddr = 0;
+	dhcp.giaddr = 0;
+	if (bootfile != "bootp")
+		dhcp.file = bootfile;
+	else
+		dhcp.file = nil;
+	ok = 0;
+	for (count := 0; !ok && count < 5; count++) {
+		mtc := timeoutstart(3000);
+		dhcpsend(conn.dfd, dhcp);
+		timedout := 0;
+		do {
+			alt {
+				<- mtc =>
+					timedout = 1;
+				rdhcp = <- dc =>
+					if (debug)
+						dumpdhcp(rdhcp);
+					if (rdhcp.ciaddr != dhcp.ciaddr || rdhcp.xid != dhcp.xid
+						|| memcmp(rdhcp.chaddr, dhcp.chaddr) != 0) {
+						break;
+					}
+					if (rdhcp.file != nil) {
+						ok = 1;
+						timeoutcancel();
+					}
+			}
+		} while (!timedout && !ok);
+		dhcp.xid++;
+	}
+	if (ok) {
+		if (bootfile == nil)
+			bootfile = rdhcp.file;
+		if (bootserver == nil)
+			bootserver = ipfmt(rdhcp.siaddr);
+	}
+	else
+		err("bootp timed out");
+	kill(dhcppid);
+}
--- /dev/null
+++ b/os/init/cerf405.b
@@ -1,0 +1,598 @@
+#
+# Intrinsyc Cerf cube 405EP, also Manga switch
+#
+# this encrusted version will be simplified shortly
+#
+
+implement Init;
+
+include "sys.m";
+	sys:	Sys;
+
+include "draw.m";
+
+include "keyring.m";
+	kr: Keyring;
+
+include "security.m";
+	auth: Auth;
+
+include "dhcp.m";
+	dhcpclient: Dhcpclient;
+	Bootconf: import dhcpclient;
+
+include "sh.m";
+
+Init: module
+{
+	init:	fn();
+};
+
+Bootpreadlen: con 128;
+Microsec: con 1000000;
+Notime: con big 800000000 * big Microsec;	# fairly arbitrary time in 1995 to check validity
+
+# conventional Inferno NAND flash partitions
+nandparts := array[] of {
+	# bootstrap from 0 to 0x210000
+	"add boot 0 0x210000",
+	"add fs 0x210000 end"
+};
+
+userdefault := array[] of {
+	"inferno inferno",
+	"sys sys"
+};
+
+ethername := "/net/ether0";
+
+#
+# initialise flash translation
+# mount flash file system
+# add devices
+# start a shell or window manager
+#
+
+init()
+{
+	sys = load Sys Sys->PATH;
+	kr = load Keyring Keyring->PATH;
+
+	sys->bind("/", "/", Sys->MREPL);
+	if(sys->bind("/boot", "/", Sys->MAFTER) < 0)
+		sys->print("can't bind /boot after /: %r\n");
+	sys->bind("/boot/nvfs", "/nvfs", Sys->MREPL);
+
+	auth = load Auth Auth->PATH;
+	if(auth != nil)
+		auth->init();
+
+	localok := 0;
+	if(lfs() >= 0){
+		# let's just take a closer look
+		sys->bind("/n/local/nvfs", "/nvfs", Sys->MBEFORE|Sys->MCREATE);
+		(rc, nil) := sys->stat("/n/local/dis/sh.dis");
+		if(rc >= 0)
+			localok = 1;
+		else
+			err("local file system unusable");
+	}
+
+	netok := sys->bind("#l", "/net", Sys->MREPL) >= 0;
+	if(!netok){
+		netok = sys->bind("#l1", "/net", Sys->MREPL) >= 0;
+		if(netok)
+			ethername = "/net/ether1";
+	}
+	if(netok)
+		configether();
+
+	dobind("#I", "/net", sys->MAFTER);	# IP
+	dobind("#p", "/prog", sys->MREPL);	# prog
+	sys->bind("#d", "/fd", Sys->MREPL);
+	dobind("#c", "/dev", sys->MREPL); 	# console
+	dobind("#t", "/dev", sys->MAFTER);	# serial line
+	drawok := sys->bind("#i", "/dev", sys->MAFTER) >= 0; 	# draw
+	sys->bind("#m", "/dev", sys->MAFTER);	# pointer
+	sys->bind("#e", "/env", sys->MREPL|sys->MCREATE);	# environment
+	sys->bind("#A", "/dev", Sys->MAFTER);	# optional audio
+	sys->bind("#ʟ", "/dev", Sys->MAFTER);	# logfs
+
+	timefile: string;
+	rootsource: string;
+	cfd := sys->open("/dev/consctl", Sys->OWRITE);
+	if(cfd != nil)
+		sys->fprint(cfd, "rawon");
+	for(;;){
+		(rootsource, timefile) = askrootsource(localok, netok);
+		if(rootsource == nil)
+			break;	# internal
+		(rc, nil) := sys->stat(rootsource+"/dis/sh.dis");
+		if(rc < 0)
+			err("%s has no shell");
+		else if(sys->bind(rootsource, "/", Sys->MAFTER) < 0)
+			sys->print("can't bind %s on /: %r\n", rootsource);
+		else{
+			sys->bind("/n/local", rootsource+"/n/local", Sys->MREPL|Sys->MCREATE);
+			sys->unmount("#//./boot", "/");
+			sys->bind(rootsource+"/dis", "/dis", Sys->MBEFORE|Sys->MCREATE);
+			break;
+		}
+	}
+	cfd = nil;
+
+	setsysname("cerf");			# set system name
+
+	rtc := big rf("#r/rtc", "0") * big Microsec;
+	now := big 0;
+	if(timefile != nil){	# synchronise with remote time if it's valid
+		now = big rf(timefile, "0");
+		if(now < Notime && rootsource != nil)
+			now = big filetime(rootsource) * big Microsec;	# try the time of the root directory
+		if(now >= Notime){
+			setclock("#r/rtc", now/big Microsec);
+			rtc = now;
+		}
+	}
+	if(now < Notime)
+		now = rtc;
+	setclock("/dev/time", now);
+
+	sys->chdir("/");
+	if(netok){
+		start("ndb/dns", nil);
+		start("ndb/cs", nil);
+	}
+	startup := "/nvfs/startup";
+	if(sys->open(startup, Sys->OREAD) != nil){
+		shell := load Command Sh->PATH;
+		if(shell != nil){
+			sys->print("Running %s\n", startup);
+			shell->init(nil, "sh" :: startup :: nil);
+		}
+	}
+	user := rdenv("user", "inferno");
+	(ok, nil) := sys->stat("/dis/wm/wm.dis");
+	if(drawok && ok >= 0)
+		(ok, nil) = sys->stat("/dis/wm/logon.dis");
+	if(drawok && ok >= 0 && userok(user)){
+		wm := load Command "/dis/wm/wm.dis";
+		if(wm != nil){
+			fd := sys->open("/nvfs/user", Sys->OWRITE);
+			if(fd != nil){
+				sys->fprint(fd, "%s", user);
+				fd = nil;
+			}
+			spawn wm->init(nil, list of {"wm/wm", "wm/logon", "-l", "-u", user});
+			exit;
+		}
+		sys->print("init: can't load wm/logon: %r");
+	}
+	sh := load Command Sh->PATH;
+	if(sh == nil){
+		err(sys->sprint("can't load %s: %r", Sh->PATH));
+		hang();
+	}
+	spawn sh->init(nil, "sh" :: nil);
+}
+
+start(cmd: string, args: list of string)
+{
+	disfile := cmd;
+	if(disfile[0] != '/')
+		disfile = "/dis/"+disfile+".dis";
+	(ok, nil) := sys->stat(disfile);
+	if(ok >= 0){
+		dis := load Command disfile;
+		if(dis == nil)
+			sys->print("init: can't load %s: %r\n", disfile);
+		else
+			spawn dis->init(nil, cmd :: args);
+	}
+}
+
+dobind(f, t: string, flags: int)
+{
+	if(sys->bind(f, t, flags) < 0)
+		err(sys->sprint("can't bind %s on %s: %r", f, t));
+}
+
+#
+# Set system name from nvram if possible
+#
+setsysname(def: string)
+{
+	v := array of byte def;
+	fd := sys->open("/nvfs/ID", sys->OREAD);
+	if(fd == nil)
+		fd = sys->open("/env/sysname", sys->OREAD);
+	if(fd != nil){
+		buf := array[Sys->NAMEMAX] of byte;
+		nr := sys->read(fd, buf, len buf);
+		while(nr > 0 && buf[nr-1] == byte '\n')
+			nr--;
+		if(nr > 0)
+			v = buf[0:nr];
+	}
+	fd = sys->open("/dev/sysname", sys->OWRITE);
+	if(fd != nil)
+		sys->write(fd, v, len v);
+}
+
+filetime(name: string): int
+{
+	(ok, dir) := sys->stat(name);
+	if(ok < 0)
+		return 0;
+	return dir.atime;
+}
+
+setclock(timefile: string, now: big)
+{
+	fd := sys->open(timefile, sys->OWRITE);
+	if(fd == nil)
+		sys->print("init: can't open %s: %r\n", timefile);
+	else if(sys->fprint(fd, "%bud", now) < 0)
+		sys->print("init: can't write to %s: %r\n", timefile);
+}
+
+err(s: string)
+{
+	sys->fprint(sys->fildes(2), "init: %s\n", s);
+}
+
+hang()
+{
+	<-chan of int;
+}
+
+tried := 0;
+
+askrootsource(localok: int, netok: int): (string, string)
+{
+	stdin := sys->fildes(0);
+	sources := "kernel" :: nil;
+	if(netok)
+		sources = "remote" :: sources;
+	if(localok){
+		sources = "local" :: sources;
+		if(netok)
+			sources = "local+remote" :: sources;
+	}
+	for(;;){
+		s := "";
+		if(tried == 0 && (s = rdenv("rootsource", nil)) != nil){
+			tried = 1;
+			sys->print("rootsource: root from %s\n", s);
+		} else {
+			sys->print("root from (");
+			cm := "";
+			for(l := sources; l != nil; l = tl l){
+				sys->print("%s%s", cm, hd l);
+				cm = ",";
+			}
+			sys->print(")[%s] ", hd sources);
+
+			s = getline(stdin, hd sources);	# default
+		}
+		(nil, choice) := sys->tokenize(s, "\t ");
+		if(choice == nil)
+			choice = sources;
+		opt := hd choice;
+		case opt {
+		* =>
+			sys->print("\ninvalid boot option: '%s'\n", opt);
+		"kernel" =>
+			return (nil, nil);
+		"local" =>
+			return ("/n/local", nil);
+		"local+remote" =>
+			if(netfs("/n/remote") >= 0)
+				return ("/n/local", nil);
+		"remote" =>
+			if(netfs("/n/remote") >= 0)
+				return ("/n/remote", "/n/remote/dev/time");
+		}
+	}
+}
+
+getline(fd: ref Sys->FD, default: string): string
+{
+	result := "";
+	buf := array[10] of byte;
+	i := 0;
+	for(;;){
+		n := sys->read(fd, buf[i:], len buf - i);
+		if(n < 1)
+			break;
+		i += n;
+		while(i >0 && (nutf := sys->utfbytes(buf, i)) > 0){
+			s := string buf[0:nutf];
+			for(j := 0; j < len s; j++)
+				case s[j] {
+				'\b' =>
+					if(result != nil)
+						result = result[0:len result-1];
+				'u'&16r1F =>
+					sys->print("^U\n");
+					result = "";
+				'\r' =>
+					;
+				* =>
+					sys->print("%c", s[j]);
+					if(s[j] == '\n' || s[j] >= 16r80){
+						if(s[j] != '\n')
+							result[len result] = s[j];
+						if(result == nil)
+							return default;
+						return result;
+					}
+					result[len result] = s[j];
+				}
+			buf[0:] = buf[nutf:i];
+			i -= nutf;
+		}
+	}
+	return default;
+}
+
+#
+# serve local file system using logfs
+#
+lfs(): int
+{
+	if(!flashpart("#F1/flash1/flashctl", nandparts))
+		return -1;
+	if(!logfsinit("#F1/flash1/fs"))
+		return -1;
+	mfd := sys->open("/dev/logfsmain", Sys->ORDWR);
+	if(mfd == nil){
+		sys->print("can't open /dev/logfsmain: %r\n");
+		return -1;
+	}
+	if(sys->mount(mfd, nil, "/n/local", Sys->MREPL|Sys->MCREATE, nil) < 0){
+		sys->print("can't mount /dev/logfsmain on /n/local: %r\n");
+		return -1;
+	}
+	return 0;
+}
+
+#
+# partition flash
+#
+flashdone := 0;
+
+flashpart(ctl: string, parts: array of string): int
+{
+	if(flashdone)
+		return 1;
+	cfd := sys->open(ctl, Sys->ORDWR);
+	if(cfd == nil){
+		sys->print("can't open %s: %r\n", ctl);
+		return 0;
+	}
+	for(i := 0; i < len parts; i++)
+		if(sys->fprint(cfd, "%s", parts[i]) < 0){
+			sys->print("can't %q to %s: %r\n", parts[i], ctl);
+			return 0;
+		}
+	flashdone = 1;
+	return 1;
+}
+
+#
+# set up logfs
+#
+logfsdone := 0;
+
+logfsinit(flashmem: string): int
+{
+	if(logfsdone)
+		return 1;
+	fd := sys->open("/dev/logfsctl", Sys->OWRITE);
+	if(fd == nil){
+		if(sys->bind("#ʟ", "/dev", Sys->MBEFORE) < 0)
+			return -1;
+		fd = sys->open("/dev/logfsctl", Sys->OWRITE);
+		if(fd == nil){
+			sys->print("can't open /dev/logfsctl: %r\n");
+			return -1;
+		}
+	}
+	sys->print("Set logfs main on %s...\n", flashmem);
+	if(!ctlw(fd, "logfs", "fsys main config "+flashmem))
+		return -1;
+	if(!ctlw(fd, "logfs", "fsys main"))
+		return -1;
+	cm := rf("#e/logfsformat", nil);
+	if(cm == "yes"){
+		if(!ctlw(fd, "logfs", "format 0"))
+			return -1;
+	}
+	cf := rf("#e/logfsopen", nil);
+	if(cf == nil)
+		cf = "open";
+	if(!ctlw(fd, "logfs", cf))
+		return -1;
+	for(i := 0; i < len userdefault; i++)
+		ctlw(fd, "logfs", "uname "+userdefault[i]);
+	logfsdone = 1;
+	return 1;
+}
+
+ctlw(fd: ref Sys->FD, w: string, cmd: string): int
+{
+	if(sys->fprint(fd, "%s", cmd) < 0){
+		sys->print("%s ctl %q: %r\n", w, cmd);
+		return 0;
+	}
+	return 1;
+}
+
+configether()
+{
+	if(ethername == nil)
+		return;
+	fd := sys->open("/nvfs/etherparams", Sys->OREAD);
+	if(fd == nil)
+		return;
+	ctl := sys->open(ethername+"/clone", Sys->OWRITE);
+	if(ctl == nil){
+		sys->print("init: can't open %s/clone: %r\n", ethername);
+		return;
+	}
+	b := array[1024] of byte;
+	n := sys->read(fd, b, len b);
+	if(n <= 0)
+		return;
+	for(i := 0; i < n;){
+		for(e := i; e < n && b[e] != byte '\n'; e++)
+			;
+		s := string b[i:e];
+		if(sys->fprint(ctl, "%s", s) < 0)
+			sys->print("init: ctl write to %s/clone: %s: %r\n", ethername, s);
+		i = e+1;
+	}
+}
+
+donebind := 0;
+server: string;
+
+#
+# set up network mount
+#
+netfs(mountpt: string): int
+{
+	fd: ref Sys->FD;
+	if(!donebind){
+		fd = sys->open("/net/ipifc/clone", sys->OWRITE);
+		if(fd == nil){
+			sys->print("init: open /net/ipifc/clone: %r\n");
+			return -1;
+		}
+		if(sys->fprint(fd, "bind ether %s", ethername) < 0){
+			sys->print("could not bind %s interface: %r\n", ethername);
+			return -1;
+		}
+		donebind = 1;
+	}else{
+		fd = sys->open("/net/ipifc/0/ctl", Sys->OWRITE);
+		if(fd == nil){
+			sys->print("init: can't reopen /net/ipifc/0/ctl: %r\n");
+			return -1;
+		}
+	}
+	server = rdenv("fsip", nil);
+	if((ip := rdenv("ip", nil)) != nil){
+		sys->print("**using %s\n", ip);
+		sys->fprint(fd, "bind ether /net/ether0");
+		s := rdenv("ipmask", nil);
+		if(s == nil)
+			s = rdenv("netmask", nil);	# alternative name used by some bootstraps
+		sys->fprint(fd, "add %s %s", ip, s);
+		gate := rdenv("ipgw", nil);
+		if(gate == nil)
+			gate = rdenv("gateway", nil);
+		if(gate != nil){
+			rfd := sys->open("/net/iproute", Sys->OWRITE);
+			if(rfd != nil){
+				sys->fprint(rfd, "add 0 0 %s", gate);
+				sys->print("set gateway %s\n", gate);
+			}
+		}
+	}else if(server == nil){
+		sys->print("dhcp...");
+		dhcpclient = load Dhcpclient Dhcpclient->PATH;
+		if(dhcpclient == nil){
+			sys->print("can't load dhcpclient: %r\n");
+			return -1;
+		}
+		dhcpclient->init();
+		(cfg, nil, e) := dhcpclient->dhcp("/net", fd, "/net/ether0/addr", nil, nil);
+		if(e != nil){
+			sys->print("dhcp: %s\n", e);
+			return -1;
+		}
+		if(server == nil)
+			server = cfg.getip(Dhcpclient->OP9fs);
+		dhcpclient = nil;
+	}
+	if(server == nil || server == "0.0.0.0"){
+		sys->print("no file server address\n");
+		return -1;
+	}
+	sys->print("fs=%s\n", server);
+
+	net := "tcp";	# how to specify il?
+	svcname := net + "!" + server + "!6666";
+
+	sys->print("dial %s...", svcname);
+
+	(ok, c) := sys->dial(svcname, nil);
+	if(ok < 0){
+		sys->print("can't dial %s: %r\n", svcname);
+		return -1;
+	}
+
+	sys->print("\nConnected ...\n");
+	if(kr != nil){
+		err: string;
+		sys->print("Authenticate ...");
+		ai := kr->readauthinfo("/nvfs/default");
+		if(ai == nil){
+			sys->print("readauthinfo /nvfs/default failed: %r\n");
+			sys->print("trying mount as `none'\n");
+		}
+		(c.dfd, err) = auth->client("none", ai, c.dfd);
+		if(c.dfd == nil){
+			sys->print("authentication failed: %s\n", err);
+			return -1;
+		}
+	}
+
+	sys->print("mount %s...", mountpt);
+
+	c.cfd = nil;
+	n := sys->mount(c.dfd, nil, mountpt, Sys->MREPL, "");
+	if(n > 0)
+		return 0;
+	if(n < 0)
+		sys->print("%r");
+	return -1;
+}
+	
+username(def: string): string
+{
+	return rdenv("user", def);
+}
+
+userok(user: string): int
+{
+	(ok, d) := sys->stat("/usr/"+user);
+	return ok >= 0 && (d.mode & Sys->DMDIR) != 0;
+}
+
+rdenv(name: string, def: string): string
+{
+	s := rf("#e/"+name, nil);
+	if(s != nil)
+		return s;
+	return rf("/nvfs/"+name, def);
+}
+
+rf(file: string, default: string): string
+{
+	fd := sys->open(file, Sys->OREAD);
+	if(fd != nil){
+		buf := array[128] of byte;
+		nr := sys->read(fd, buf, len buf);
+		if(nr > 0){
+			s := string buf[0:nr];
+			while(s != nil && ((c := s[len s-1]) == '\n' || c == '\r'))
+				s = s[0: len s-1];
+			if(s != nil)
+				return s;
+		}
+	}
+	return default;
+}
--- /dev/null
+++ b/os/init/cerfinit.b
@@ -1,0 +1,612 @@
+#
+# Intrinsyc Cerf cube
+#
+
+implement Init;
+
+include "sys.m";
+	sys:	Sys;
+
+include "draw.m";
+
+include "keyring.m";
+	kr: Keyring;
+
+include "security.m";
+	auth: Auth;
+
+include "sh.m";
+
+Init: module
+{
+	init:	fn();
+};
+
+Bootpreadlen: con 128;
+
+# standard flash partitions
+
+flashparts := array[] of {
+	# bootstrap at 0x0 to 0x20000
+	"add script 0x20000 0x40000",
+	"add kernel 0x100000 0x200000",
+	"add fs 0x200000 end",
+};
+
+ethername := "ether0";
+
+#
+# initialise flash translation
+# mount flash file system
+# add devices
+# start a shell or window manager
+#
+
+init()
+{
+	sys = load Sys Sys->PATH;
+	kr = load Keyring Keyring->PATH;
+	auth = load Auth Auth->PATH;
+	if(auth != nil)
+		auth->init();
+
+	sys->bind("/", "/", Sys->MREPL);
+
+	localok := 0;
+	if(lfs() >= 0){
+		# let's just take a closer look
+		sys->bind("/n/local/nvfs", "/nvfs", Sys->MREPL|Sys->MCREATE);
+		(rc, nil) := sys->stat("/n/local/dis/sh.dis");
+		if(rc >= 0)
+			localok = 1;
+		else
+			err("local file system unusable");
+	}
+	netok := sys->bind("#l", "/net", Sys->MREPL) >= 0;
+	if(!netok){
+		netok = sys->bind("#l1", "/net", Sys->MREPL) >= 0;
+		if(netok)
+			ethername = "ether1";
+	}
+	if(netok)
+		configether();
+	dobind("#I", "/net", sys->MAFTER);	# IP
+	dobind("#p", "/prog", sys->MREPL);	# prog
+	sys->bind("#d", "/fd", Sys->MREPL);
+	dobind("#c", "/dev", sys->MREPL); 	# console
+	dobind("#t", "/dev", sys->MAFTER);	# serial line
+	drawok := sys->bind("#i", "/dev", sys->MAFTER) >= 0; 	# draw
+	sys->bind("#m", "/dev", sys->MAFTER);	# pointer
+	sys->bind("#e", "/env", sys->MREPL|sys->MCREATE);	# environment
+	sys->bind("#A", "/dev", Sys->MAFTER);	# optional audio
+	timefile: string;
+	rootsource: string;
+	scale := 1;
+	cfd := sys->open("/dev/consctl", Sys->OWRITE);
+	if(cfd != nil)
+		sys->fprint(cfd, "rawon");
+	for(;;){
+		(rootsource, timefile, scale) = askrootsource(localok, netok);
+		if(rootsource == nil)
+			break;	# internal
+		(rc, nil) := sys->stat(rootsource+"/dis/sh.dis");
+		if(rc < 0)
+			err("%s has no shell");
+		else if(sys->bind(rootsource, "/", Sys->MAFTER) < 0)
+			sys->print("can't bind %s on /: %r\n", rootsource);
+		else{
+			sys->bind(rootsource+"/dis", "/dis", Sys->MBEFORE|Sys->MCREATE);
+			break;
+		}
+	}
+	cfd = nil;
+
+	setsysname("cerf");			# set system name
+
+	now := getclock(timefile, rootsource);
+	if(scale == 1)
+		now *= big 1000000;
+	setclock("/dev/time", now);
+	if(timefile != "#r/rtc")
+		setclock("#r/rtc", now/big 1000000);
+
+	sys->chdir("/");
+	if(netok){
+		start("ndb/dns", nil);
+		start("ndb/cs", nil);
+	}
+	startup := "/nvfs/startup";
+	if(sys->open(startup, Sys->OREAD) != nil){
+		shell := load Command Sh->PATH;
+		if(shell != nil){
+			sys->print("Running %s\n", startup);
+			shell->init(nil, "sh" :: startup :: nil);
+		}
+	}
+	user := username("inferno");
+	(ok, nil) := sys->stat("/dis/wm/wm.dis");
+	if(drawok && ok >= 0)
+		(ok, nil) = sys->stat("/dis/wm/logon.dis");
+	if(drawok && ok >= 0 && userok(user)){
+		wm := load Command "/dis/wm/wm.dis";
+		if(wm != nil){
+			fd := sys->open("/nvfs/user", Sys->OWRITE);
+			if(fd != nil){
+				sys->fprint(fd, "%s", user);
+				fd = nil;
+			}
+			spawn wm->init(nil, list of {"wm/wm", "wm/logon", "-l", "-u", user});
+			exit;
+		}
+		sys->print("init: can't load wm/logon: %r");
+	}
+	sh := load Command Sh->PATH;
+	if(sh == nil){
+		err(sys->sprint("can't load %s: %r", Sh->PATH));
+		hang();
+	}
+	spawn sh->init(nil, "sh" :: nil);
+}
+
+start(cmd: string, args: list of string)
+{
+	disfile := cmd;
+	if(disfile[0] != '/')
+		disfile = "/dis/"+disfile+".dis";
+	(ok, nil) := sys->stat(disfile);
+	if(ok >= 0){
+		dis := load Command disfile;
+		if(dis == nil)
+			sys->print("init: can't load %s: %r\n", disfile);
+		else
+			spawn dis->init(nil, cmd :: args);
+	}
+}
+
+dobind(f, t: string, flags: int)
+{
+	if(sys->bind(f, t, flags) < 0)
+		err(sys->sprint("can't bind %s on %s: %r", f, t));
+}
+
+#
+# Set system name from nvram if possible
+#
+setsysname(def: string)
+{
+	v := array of byte def;
+	fd := sys->open("/nvfs/ID", sys->OREAD);
+	if(fd == nil)
+		fd = sys->open("/env/sysname", sys->OREAD);
+	if(fd != nil){
+		buf := array[Sys->NAMEMAX] of byte;
+		nr := sys->read(fd, buf, len buf);
+		while(nr > 0 && buf[nr-1] == byte '\n')
+			nr--;
+		if(nr > 0)
+			v = buf[0:nr];
+	}
+	fd = sys->open("/dev/sysname", sys->OWRITE);
+	if(fd != nil)
+		sys->write(fd, v, len v);
+}
+
+getclock(timefile: string, timedir: string): big
+{
+	now := big 0;
+	if(timefile != nil){
+		fd := sys->open(timefile, Sys->OREAD);
+		if(fd != nil){
+			b := array[64] of byte;
+			n := sys->read(fd, b, len b-1);
+			if(n > 0){
+				now = big string b[0:n];
+				if(now <= big 16r20000000)
+					now = big 0;	# remote itself is not initialised
+			}
+		}
+	}
+	if(now == big 0){
+		if(timedir != nil){
+			(ok, dir) := sys->stat(timedir);
+			if(ok < 0) {
+				sys->print("init: stat %s: %r", timedir);
+				return big 0;
+			}
+			now = big dir.atime;
+		}else{
+			now = big 993826747000000;
+			sys->print("time warped\n");
+		}
+	}
+	return now;
+}
+
+setclock(timefile: string, now: big)
+{
+	fd := sys->open(timefile, sys->OWRITE);
+	if (fd == nil) {
+		sys->print("init: can't open %s: %r", timefile);
+		return;
+	}
+
+	b := sys->aprint("%ubd", now);
+	if (sys->write(fd, b, len b) != len b)
+		sys->print("init: can't write to %s: %r", timefile);
+}
+
+srv()
+{
+	sys->print("remote debug srv...");
+	fd := sys->open("/dev/eia0ctl", Sys->OWRITE);
+	if(fd != nil)
+		sys->fprint(fd, "b115200");
+
+	fd = sys->open("/dev/eia0", Sys->ORDWR);
+	if (fd == nil){
+		err(sys->sprint("can't open /dev/eia0: %r"));
+		return;
+	}
+	if (sys->export(fd, "/", Sys->EXPASYNC) < 0){
+		err(sys->sprint("can't export on serial port: %r"));
+		return;
+	}
+}
+
+err(s: string)
+{
+	sys->fprint(sys->fildes(2), "init: %s\n", s);
+}
+
+hang()
+{
+	<-chan of int;
+}
+
+tried := 0;
+
+askrootsource(localok: int, netok: int): (string, string, int)
+{
+	stdin := sys->fildes(0);
+	sources := "kernel" :: nil;
+	if(netok)
+		sources = "remote" :: sources;
+	if(localok){
+		sources = "local" :: sources;
+		if(netok)
+			sources = "local+remote" :: sources;
+	}
+	for(;;) {
+		s := "";
+		if (tried == 0 && (s = rf("/nvfs/rootsource", nil)) != nil) {
+			tried = 1;
+			if (s[len s - 1] == '\n')
+				s = s[:len s - 1];
+			sys->print("/nvfs/rootsource: root from %s\n", s);
+		} else {
+			sys->print("root from (");
+			cm := "";
+			for(l := sources; l != nil; l = tl l){
+				sys->print("%s%s", cm, hd l);
+				cm = ",";
+			}
+			sys->print(")[%s] ", hd sources);
+
+			s = getline(stdin, hd sources);	# default
+		}
+		(nil, choice) := sys->tokenize(s, "\t ");
+		if(choice == nil)
+			choice = sources;
+		opt := hd choice;
+		case opt {
+		* =>
+			sys->print("\ninvalid boot option: '%s'\n", opt);
+		"kernel" =>
+			return (nil, "#r/rtc", 1);
+		"local" =>
+			return ("/n/local", "#r/rtc", 1);
+		"local+remote" =>
+			if(netfs("/n/remote") >= 0)
+				return ("/n/local", "/n/remote/dev/time", 1000000);
+		"remote" =>
+			if(netfs("/n/remote") >= 0)
+				return ("/n/remote", "/n/remote/dev/time", 1000000);
+		}
+	}
+}
+
+getline(fd: ref Sys->FD, default: string): string
+{
+	result := "";
+	buf := array[10] of byte;
+	i := 0;
+	for(;;) {
+		n := sys->read(fd, buf[i:], len buf - i);
+		if(n < 1)
+			break;
+		i += n;
+		while(i >0 && (nutf := sys->utfbytes(buf, i)) > 0){
+			s := string buf[0:nutf];
+			for (j := 0; j < len s; j++)
+				case s[j] {
+				'\b' =>
+					if(result != nil)
+						result = result[0:len result-1];
+				'u'&16r1F =>
+					sys->print("^U\n");
+					result = "";
+				'\r' =>
+					;
+				* =>
+					sys->print("%c", s[j]);
+					if(s[j] == '\n' || s[j] >= 16r80){
+						if(s[j] != '\n')
+							result[len result] = s[j];
+						if(result == nil)
+							return default;
+						return result;
+					}
+					result[len result] = s[j];
+				}
+			buf[0:] = buf[nutf:i];
+			i -= nutf;
+		}
+	}
+	return default;
+}
+
+#
+# serve local DOS file system using flash translation layer
+#
+lfs(): int
+{
+	if(!flashpart("#F/flash/flashctl", flashparts))
+		return -1;
+	if(!ftlinit("#F/flash/fs"))
+		return -1;
+	c := chan of string;
+	spawn startfs(c, "/dis/dossrv.dis", "dossrv" :: "-f" :: "#X/ftldata" :: "-m" :: "/n/local" :: nil);
+	if(<-c != nil)
+		return -1;
+	return 0;
+}
+
+startfs(c: chan of string, file: string, args: list of string)
+{
+	fs := load Command file;
+	if(fs == nil){
+		sys->print("can't load %s: %r\n", file);
+		c <-= "load failed";
+	}
+	{
+		fs->init(nil, args);
+	}exception e {
+	"*" =>
+		c <-= "failed";
+		exit;
+	* =>
+		c <-= "unknown exception";
+		exit;
+	}
+	c <-= nil;
+}
+
+#
+# partition flash
+#
+flashdone := 0;
+
+flashpart(ctl: string, parts: array of string): int
+{
+	if(flashdone)
+		return 1;
+	cfd := sys->open(ctl, Sys->ORDWR);
+	if(cfd == nil){
+		sys->print("can't open %s: %r\n", ctl);
+		return 0;
+	}
+	for(i := 0; i < len parts; i++)
+		if(sys->fprint(cfd, "%s", parts[i]) < 0){
+			sys->print("can't %q to %s: %r\n", parts[i], ctl);
+			return 0;
+		}
+	flashdone = 1;
+	return 1;
+}
+
+#
+# set up flash translation layer
+#
+ftldone := 0;
+
+ftlinit(flashmem: string): int
+{
+	if(ftldone)
+		return 1;
+	sys->print("Set flash translation of %s...\n", flashmem);
+	fd := sys->open("#X/ftlctl", Sys->OWRITE);
+	if(fd == nil){
+		sys->print("can't open #X/ftlctl: %r\n");
+		return 0;
+	}
+	if(sys->fprint(fd, "init %s", flashmem) <= 0){
+		sys->print("can't init flash translation: %r\n");
+		return 0;
+	}
+	ftldone = 1;
+	return 1;
+}
+
+configether()
+{
+	if(ethername == nil)
+		return;
+	fd := sys->open("/nvfs/etherparams", Sys->OREAD);
+	if(fd == nil)
+		return;
+	ctl := sys->open("/net/"+ethername+"/clone", Sys->OWRITE);
+	if(ctl == nil){
+		sys->print("init: can't open %s's clone: %r\n", ethername);
+		return;
+	}
+	b := array[1024] of byte;
+	n := sys->read(fd, b, len b);
+	if(n <= 0)
+		return;
+	for(i := 0; i < n;){
+		for(e := i; e < n && b[e] != byte '\n'; e++)
+			;
+		s := string b[i:e];
+		if(sys->fprint(ctl, "%s", s) < 0)
+			sys->print("init: ctl write to %s: %s: %r\n", ethername, s);
+		i = e+1;
+	}
+}
+
+donebind := 0;
+
+#
+# set up network mount
+#
+netfs(mountpt: string): int
+{
+	sys->print("bootp ...");
+
+	fd: ref Sys->FD;
+	if(!donebind){
+		fd = sys->open("/net/ipifc/clone", sys->OWRITE);
+		if(fd == nil) {
+			sys->print("init: open /net/ipifc/clone: %r\n");
+			return -1;
+		}
+		if(sys->fprint(fd, "bind ether %s", ethername) < 0) {
+			sys->print("could not bind %s interface: %r\n", ethername);
+			return -1;
+		}
+		donebind = 1;
+	}else{
+		fd = sys->open("/net/ipifc/0/ctl", Sys->OWRITE);
+		if(fd == nil){
+			sys->print("init: can't reopen /net/ipifc/0/ctl: %r\n");
+			return -1;
+		}
+	}
+	if ((ip := rf("/nvfs/ip", nil)) != nil) {
+		sys->print("**using %s\n", ip);
+		sys->fprint(fd, "bind ether /net/ether0");
+		sys->fprint(fd, "add %s ", ip);
+	} else {
+		{
+			if(sys->fprint(fd, "bootp") < 0)
+				sys->print("could not bootp: %r\n");
+		} exception e {
+			"*" =>
+				sys->print("could not bootp: %s\n", e);
+		}
+	}
+	server := rf("/nvfs/fsip", nil);
+	if (server != nil) {
+		if (server[len server - 1] == '\n')
+			server = server[:len server - 1];
+		sys->print("/nvfs/fsip: server=%s\n", server);
+	} else
+		server = bootp();
+	if(server == nil || server == "0.0.0.0")
+		return -1;
+
+	net := "tcp";	# how to specify il?
+	svcname := net + "!" + server + "!6666";
+
+	sys->print("dial %s...", svcname);
+
+	(ok, c) := sys->dial(svcname, nil);
+	if(ok < 0){
+		sys->print("can't dial %s: %r\n", svcname);
+		return -1;
+	}
+
+	sys->print("\nConnected ...\n");
+	if(kr != nil){
+		err: string;
+		sys->print("Authenticate ...");
+		ai := kr->readauthinfo("/nvfs/default");
+		if(ai == nil){
+			sys->print("readauthinfo /nvfs/default failed: %r\n");
+			sys->print("trying mount as `nobody'\n");
+		}
+		(c.dfd, err) = auth->client("none", ai, c.dfd);
+		if(c.dfd == nil){
+			sys->print("authentication failed: %s\n", err);
+			return -1;
+		}
+	}
+
+	sys->print("mount %s...", mountpt);
+
+	c.cfd = nil;
+	n := sys->mount(c.dfd, nil, mountpt, sys->MREPL, "");
+	if(n > 0)
+		return 0;
+	if(n < 0)
+		sys->print("%r");
+	return -1;
+}
+
+bootp(): string
+{
+	fd := sys->open("/net/bootp", sys->OREAD);
+	if(fd == nil) {
+		sys->print("init: can't open /net/bootp: %r");
+		return nil;
+	}
+
+	buf := array[Bootpreadlen] of byte;
+	nr := sys->read(fd, buf, len buf);
+	fd = nil;
+	if(nr <= 0) {
+		sys->print("init: read /net/bootp: %r");
+		return nil;
+	}
+
+	(ntok, ls) := sys->tokenize(string buf, " \t\n");
+	while(ls != nil) {
+		if(hd ls == "fsip"){
+			ls = tl ls;
+			break;
+		}
+		ls = tl ls;
+	}
+	if(ls == nil) {
+		sys->print("init: server address not in bootp read");
+		return nil;
+	}
+
+	srv := hd ls;
+
+	sys->print("%s\n", srv);
+
+	return srv;
+}
+	
+username(def: string): string
+{
+	return rf("/nvfs/user", def);
+}
+
+userok(user: string): int
+{
+	(ok, d) := sys->stat("/usr/"+user);
+	return ok >= 0 && (d.mode & Sys->DMDIR) != 0;
+}
+
+rf(file: string, default: string): string
+{
+	fd := sys->open(file, Sys->OREAD);
+	if(fd != nil){
+		buf := array[128] of byte;
+		nr := sys->read(fd, buf, len buf);
+		if(nr > 0)
+			return string buf[0:nr];
+	}
+	return default;
+}
--- /dev/null
+++ b/os/init/evalinit.b
@@ -1,0 +1,119 @@
+implement Init;
+
+#
+# ARM evaluator 7t
+#
+
+include "sys.m";
+sys: Sys;
+FD, Connection, sprint, Dir: import sys;
+print, fprint, open, bind, mount, dial, sleep, read: import sys;
+
+include "draw.m";
+include "sh.m";
+draw: Draw;
+Context: import draw;
+
+Init: module
+{
+	init:	fn();
+};
+
+Logon: module
+{
+	init:	fn(ctxt: ref Context, argv: list of string);
+};
+
+Bootpreadlen: con 128;
+
+init()
+{
+	sys = load Sys Sys->PATH;
+#	kr = load Keyring Keyring->PATH;
+#	auth = load Auth Auth->PATH;
+#	if(auth != nil)
+#		auth->init();
+	
+	sys->print("**\n** Inferno\n** Vita Nuova\n**\n");
+
+#	sys->print("Setup boot net services ...\n");
+	
+	#
+	# Setup what we need to call a server and
+	# Authenticate
+	#
+#	bind("#l", "/net", sys->MREPL);
+#	bind("#I", "/net", sys->MAFTER);
+	bind("#c", "/dev", sys->MAFTER);
+	bind("#r", "/dev", sys->MAFTER);
+#	nvramfd := sys->open("#r/nvram", sys->ORDWR);
+#	if(nvramfd != nil){
+#		spec = "#Fnvram";
+#		if(bind(spec, "/nvfs", sys->MAFTER) < 0)
+#			print("init: bind %s: %r\n", spec);
+#	}
+
+#	setsysname();
+
+	#
+	# default namespace
+	#
+	bind("#c", "/dev", sys->MREPL);			# console
+	bind("#t", "/dev", sys->MAFTER);		# serial port
+	bind("#r", "/dev", sys->MAFTER);		# RTC
+#	if(spec != nil)
+#		bind(spec, "/nvfs", sys->MBEFORE|sys->MCREATE);	# our keys
+#	bind("#l", "/net", sys->MBEFORE);		# ethernet
+#	bind("#I", "/net", sys->MBEFORE);		# TCP/IP
+	bind("#p", "/prog", sys->MREPL);		# prog device
+	sys->bind("#d", "/fd", Sys->MREPL);
+
+	sys->print("clock...\n");
+	setclock();
+
+	sys->print("logon...\n");
+
+#	sys->chdir("/usr/inferno"); 
+#	logon := load Logon "/dis/sh.dis";
+#	spawn logon->init(dc, nil);
+	ts := load Sh "/dis/sh.dis";
+	ts->init(nil, nil);
+}
+
+setclock()
+{
+	(ok, dir) := sys->stat("/");
+	if (ok < 0) {
+		print("init: stat /: %r");
+		return;
+	}
+
+	fd := sys->open("/dev/time", sys->OWRITE);
+	if (fd == nil) {
+		print("init: open /dev/time: %r");
+		return;
+	}
+
+	# Time is kept as microsecs, atime is in secs
+	b := array of byte sprint("%d000000", dir.atime);
+	if (sys->write(fd, b, len b) != len b)
+		print("init: write /dev/time: %r");
+}
+
+#
+# Set system name from nvram
+#
+setsysname()
+{
+	fd := open("/nvfs/ID", sys->OREAD);
+	if(fd == nil)
+		return;
+	fds := open("/dev/sysname", sys->OWRITE);
+	if(fds == nil)
+		return;
+	buf := array[128] of byte;
+	nr := sys->read(fd, buf, len buf);
+	if(nr <= 0)
+		return;
+	sys->write(fds, buf, nr);
+}
--- /dev/null
+++ b/os/init/geninit.b
@@ -1,0 +1,93 @@
+implement Init;
+#
+# init program for native inferno, generic pc version
+#
+include "sys.m";
+sys: Sys;
+FD, Connection, sprint, Dir: import sys;
+print, fprint, open, bind, mount, dial, sleep, read, chdir: import sys;
+
+include "draw.m";
+draw: Draw;
+Context: import draw;
+
+include "keyring.m";
+kr: Keyring;
+
+Init: module
+{
+	init:	fn();
+};
+
+Shell: module
+{
+	init:	fn(ctxt: ref Context, argv: list of string);
+};
+
+init()
+{
+
+	sys = load Sys Sys->PATH;
+	stdin := sys->fildes(0);
+	kr = load Keyring Keyring->PATH;
+
+	sys->print("**\n** Inferno\n** Vita Nuova\n**\n");
+
+	sys->print("Setup boot net services ...\n");
+
+	#
+	# Setup what we need to call a server and
+	# Authenticate
+	#
+	sys->print("Bind console ...\n");
+	bind("#c", "/dev", sys->MAFTER);
+
+	setsysname();
+	print("Standalone mode\n");
+	#
+	# default namespace
+	#
+	sys->unmount(nil, "/dev");
+	bind("#p", "/prog", sys->MREPL);		# prog device
+	sys->bind("#d", "/fd", Sys->MREPL);
+	bind("#c", "/dev", sys->MBEFORE);		# console
+	bind("#m", "/dev", sys->MAFTER);		# mouse setup device
+	bind("#t", "/dev", sys->MAFTER);		# serial device
+
+	mouse := load Shell "/dis/mouse.dis";
+	if (mouse != nil) {
+		print("Setting up mouse\n");
+		mouse->init(nil, "/dis/mouse.dis" :: nil);
+		mouse = nil;
+	}
+
+	# create fake nameserver db that can be written to later
+	ramfile := load Shell "/dis/ramfile.dis";
+	if (ramfile != nil) {
+		ramfile->init(nil, "/dis/ramfile.dis" :: "/services/dns/db" :: "" :: nil);
+		ramfile = nil;
+	}
+
+	print("Console...\n");
+	shell := load Shell "/dis/sh.dis";
+	if(shell == nil) {
+		print("init: load /dis/sh.dis: %r\n");
+		exit;
+	}
+	print("starting shell\n");
+	shell->init(nil, "/dis/sh.dis" :: nil);
+	print("shell exited, bye bye\n");
+}
+
+
+#
+# Set system name from nvram
+#
+setsysname()
+{
+	fds := open("/dev/sysname", sys->OWRITE);
+	if(fds == nil)
+		return;
+	buf := array of byte "genericpc";
+	sys->write(fds, buf, len buf);
+}
--- /dev/null
+++ b/os/init/i4e.b
@@ -1,0 +1,210 @@
+implement Init;
+
+#
+# init program for Inferno 4thEd demo box
+#
+
+include "sys.m";
+	sys: Sys;
+	FD, Connection, Dir: import sys;
+
+include "draw.m";
+
+include "keyring.m";
+	kr: Keyring;
+
+include "security.m";
+	auth:	Auth;
+
+include "dhcp.m";
+	dhcpclient: Dhcpclient;
+	Bootconf: import dhcpclient;
+
+I4EBOOT: con "/lib/boot.sh";
+
+Init: module
+{
+	init:	fn();
+};
+
+Command: module
+{
+	init:	fn(ctxt: ref Draw->Context, argv: list of string);
+};
+
+Bootpreadlen: con 128;
+
+init()
+{
+	sys = load Sys Sys->PATH;
+	kr = load Keyring Keyring->PATH;
+	auth = load Auth Auth->PATH;
+	if(auth != nil)
+		auth->init();
+
+	sys->print("**\n** Inferno\n** Vita Nuova\n**\n");
+
+	sys->print("Setup boot net services ...\n");
+	
+	#
+	# Setup what we need to call a server and
+	# Authenticate
+	#
+	dobind("#l", "/net", Sys->MREPL);
+	dobind("#I", "/net", Sys->MAFTER);
+	dobind("#c", "/dev", Sys->MAFTER);
+
+
+	fd := sys->open("/net/ipifc/clone", sys->OWRITE);
+	if(fd == nil)
+		fail(sys->sprint("iopen /net/ipifc/clone: %r"));
+
+	if(sys->fprint(fd, "bind ether /net/ether0") < 0)
+		fail(sys->sprint("could not bind interface: %r"));
+
+	fsip: string;
+
+	dhcpclient = load Dhcpclient Dhcpclient->PATH;
+	if(dhcpclient == nil)
+		fail(sys->sprint("can't load dhcpclient: %r"));
+
+	sys->print("dhcp...");
+	dhcpclient->init();
+	(cfg, nil, e) := dhcpclient->dhcp("/net", fd, "/net/ether0/addr", nil, nil);
+	if(e != nil)
+		fail(sys->sprint("dhcp: %s", e));
+	fsip = cfg.getip(Dhcpclient->OP9fs);
+	if(fsip == nil)
+		fail("server address not in bootp/dhcp reply");
+	dhcpclient = nil;
+
+	infd := sys->open("/dev/cons", Sys->OREAD);
+	if(infd == nil)
+		sys->print("warning: no kbd\n");
+
+	err := rootfs(fsip);
+	if(err != nil)
+		fail(err);
+
+	#
+	# default namespace
+	#
+	dobind("#c", "/dev", Sys->MREPL);			# console
+	dobind("#p", "/prog", Sys->MREPL);		# prog device
+	sys->bind("#d", "/fd", Sys->MREPL);
+	sys->pctl(Sys->NEWENV, nil);
+	dobind("#e", "/env", Sys->MREPL|Sys->MCREATE);	# env device
+
+	sys->print("clock...\n");
+	setclock();
+
+	sys->print("boot...\n");
+	sys->pctl(Sys->NEWFD, 0::1::2::nil);
+	done := chan of string;
+	spawn boot(done);
+	err = <- done;
+	if(err != nil)
+		fail("boot script failed: "+err);
+	fail("boot script exit");
+}
+
+rootfs(server: string): string
+{
+	ok: int;
+	c: Connection;
+
+	sys->print("readauthinfo...\n");
+	ai := kr->readauthinfo("/keydb/mutual");
+	if(ai == nil)
+		return sys->sprint("readauthinfo /keydb/mutual failed: %r");
+
+	addr := "tcp!" + server + "!9999";
+	for(gap := 3;; gap *= 2){
+		sys->print("Connect (%s)...", addr);
+		(ok, c) = sys->dial(addr, nil);
+		if(ok != -1)
+			break;
+		sys->print("failed: %r\n");
+		if(gap > 60)
+			gap = 60;
+		sys->sleep(gap*1000);
+	}
+
+	sys->print("\nConnected ...");
+	if(kr != nil && auth != nil){
+		err: string;
+		sys->print("Authenticate ...");
+		(c.dfd, err) = auth->client("none", ai, c.dfd);
+		if(c.dfd == nil)
+			return sys->sprint("authentication failed: %s", err);
+	}
+	sys->print("mount ...");
+
+	c.cfd = nil;
+	sys->pctl(Sys->NEWNS, nil);
+	if(sys->mount(c.dfd, nil, "/", sys->MREPL, "") < 0)	# TO DO: would be better to mount behind
+		return sys->sprint("mount failed: %r");
+	sys->chdir("/");
+	return nil;
+}
+
+boot(done: chan of string)
+{
+	{
+		shell := load Command "/dis/sh.dis";
+		if(shell == nil){
+			done <-= sys->sprint("load /dis/sh.dis: %r");
+			exit;
+		}
+		shell->init(nil, "/dis/sh.dis"::I4EBOOT::nil);
+	} exception e {
+	"*" =>
+		done <-= e;
+		exit;
+	}
+	done <-= nil;
+}
+
+setclock()
+{
+	(ok, dir) := sys->stat("/");
+	if(ok < 0){
+		sys->print("stat /: %r");
+		return;
+	}
+
+	fd := sys->open("/dev/time", sys->OWRITE);
+	if(fd == nil){
+		sys->print("open /dev/time: %r");
+		return;
+	}
+
+	# Time is kept as microsecs, atime is in secs
+	b := array of byte sys->sprint("%d000000", dir.atime);
+	if(sys->write(fd, b, len b) != len b)
+		sys->print("write /dev/time: %r");
+}
+
+#
+# Bind wrapper which reports errors
+#
+
+dobind(f, t: string, flags: int)
+{
+	if(sys->bind(f, t, flags) < 0)
+		sys->print("bind(%s, %s, %d) failed: %r\n", f, t, flags);
+}
+
+fail(msg: string)
+{
+	sys->print("%s\n", msg);
+	sys->bind("/", "#//n/remote", Sys->MREPL);
+	sys->bind("#//", "/", Sys->MREPL);
+	shell := load Command "#//dis/sh.dis";
+	if(shell == nil){
+		sys->print("cannot load shell: %r\n");
+		exit;
+	}
+	shell->init(nil, "/dis/sh.dis"::"-i"::nil);
+	exit;
+}
--- /dev/null
+++ b/os/init/init.b
@@ -1,0 +1,613 @@
+implement Init;
+
+include "sys.m";
+sys: Sys;
+FD, Connection, sprint, Dir: import sys;
+print, fprint, open, bind, mount, dial, sleep, read: import sys;
+
+include "draw.m";
+draw: Draw;
+Context, Display, Font, Rect, Point, Image, Screen: import draw;
+
+include "prefab.m";
+prefab: Prefab;
+Environ, Element, Compound, Style: import prefab;
+
+include "mpeg.m";
+
+include "ir.m";
+tirc: chan of int;	# translated remote input (from irslave)
+irstopc: chan of int;	# channel to irslave
+
+include "keyring.m";
+kr: Keyring;
+IPint: import kr;
+
+Init: module
+{
+	init:	fn();
+};
+
+Shell: module
+{
+	init:	fn(ctxt: ref Context, argv: list of string);
+};
+
+Signon: con "Dialing Local Service Provider\nWait a moment ...";
+Login:  con "Connected to Service Provider";
+Intro:	con "/mpeg/youwill2";
+Garden:	con "The Garden of Delights\nHieronymus Bosch";
+
+rootfs(server: string): int
+{
+	ok, n: int;
+	c: Connection;
+	err: string;
+
+	(ok, c) = dial("tcp!" + server + "!6666", nil);
+	if(ok < 0)
+		return -1;
+
+	if(kr != nil){
+		ai := kr->readauthinfo("/nvfs/default");
+		if(ai == nil){
+			(ai, err) = register(server);
+			if(err != nil){
+				status("registration failed: "+err+"\nPress a key on your remote\ncontrol to continue.");
+				# register() may have failed before Ir loaded.
+				if(tirc!=nil){
+					<-tirc;
+					irstopc <-= 1;
+				}
+			}
+			statusbox = nil;
+		}
+		(id_or_err, secret) := kr->auth(c.dfd, ai, 0);
+		if(secret == nil){
+			status("authentication failed: "+err);
+			sys->sleep(2000);
+			statusbox = nil;
+			(ai, err) = register(server);
+			if(err != nil){
+				status("registration failed: "+err+"\nPress a key on your remote\ncontrol to continue.");
+				# register() may have failed before Ir loaded.
+				if(tirc!=nil){
+					<-tirc;
+					irstopc <-= 1;
+				}
+			}
+			statusbox = nil;
+		} else {
+			# no line encryption
+			algbuf := array of byte "none";
+			kr->sendmsg(c.dfd, algbuf, len algbuf);
+		}
+	}
+
+	c.cfd = nil;
+	n = mount(c.dfd, nil, "/", sys->MREPL, "");
+	if(n > 0)
+		return 0;
+	return -1;
+}
+
+ones: ref Image;
+screen: ref Screen;
+menuenv, tvenv: ref Environ;
+Bootpreadlen: con 128;
+textfont: ref Font;
+disp: ref Display;
+env: ref Environ;
+statusbox: ref Compound;
+
+init()
+{
+	shell: Shell;
+	nr, ntok: int;
+	c: ref Compound;
+	ls: list of string;
+	le, te, xe: ref Element;
+	spec: string;
+
+	sys = load Sys Sys->PATH;
+	draw = load Draw Draw->PATH;
+	prefab = load Prefab Prefab->PATH;
+	kr = load Keyring Keyring->PATH;
+	
+	disp = Display.allocate(nil);
+	ones = disp.ones;
+
+	textfont = Font.open(disp, "*default*");
+	screencolor := disp.rgb(161, 195, 209);
+
+	menustyle := ref Style(
+			textfont,			# titlefont
+			textfont,			# textfont
+			disp.color(16r55),		# elemcolor
+			disp.color(draw->Black),	# edgecolor
+			disp.color(draw->Yellow),	# titlecolor	
+			disp.color(draw->Black),	# textcolor
+			disp.color(draw->White));	# highlightcolor
+
+	screen = Screen.allocate(disp.image, screencolor, 0);
+	screen.image.draw(screen.image.r, screencolor, ones, (0, 0));
+	menuenv = ref Environ(screen, menustyle);
+
+	logo := disp.open("/lucent");
+	phone := disp.open("/phone");
+	if(phone == nil  || logo == nil) {
+		print("open: /phone or /lucent: %r\n");
+		exit;
+	}
+
+	#
+	# Setup what we need to call a server and
+	# Authenticate
+	#
+	bind("#l", "/net", sys->MREPL);
+	bind("#I", "/net", sys->MAFTER);
+	bind("#c", "/dev", sys->MAFTER);
+	bind("#H", "/dev", sys->MAFTER);
+	nvramfd := sys->open("#H/hd0nvram", sys->ORDWR);
+	if(nvramfd != nil){
+		spec = sys->sprint("#Fhd0nvram", nvramfd.fd);
+		if(bind(spec, "/nvfs", sys->MAFTER|sys->MCREATE) < 0)
+			print("init: bind %s: %r\n", spec);
+	}
+
+	setsysname();	# set up system name
+
+	fd := open("/net/ipifc", sys->OWRITE);
+	if(fd == nil) {
+		print("init: open /net/ipifc: %r");
+		exit;
+	}
+	fprint(fd, "bootp /net/ether0");
+
+	fd = open("/net/bootp", sys->OREAD);
+	if(fd == nil) {
+		print("init: open /net/bootp: %r");
+		exit;
+	}
+
+	buf := array[Bootpreadlen] of byte;
+	nr = read(fd, buf, len buf);
+	fd = nil;
+	if(nr <= 0) {
+		print("init: read /net/bootp: %r");
+		exit;
+	}
+
+	(ntok, ls) = sys->tokenize(string buf, " \t\n");
+	while(ls != nil) {
+		if(hd ls == "fsip"){
+			ls = tl ls;
+			break;
+		}
+		ls = tl ls;
+	}
+	if(ls == nil) {
+		print("init: server address not in bootp read");
+		exit;
+	}
+
+	zr := Rect((0,0), (0,0));
+
+	le = Element.icon(menuenv, logo.r, logo, ones);
+	le = Element.elist(menuenv, le, Prefab->EVertical);
+	xe = Element.icon(menuenv, phone.r, phone, ones);
+	xe = Element.elist(menuenv, xe, Prefab->EHorizontal);
+	te = Element.text(menuenv, Signon, zr, Prefab->EText);
+	xe.append(te);
+	xe.adjust(Prefab->Adjpack, Prefab->Adjleft);
+	le.append(xe);
+	le.adjust(Prefab->Adjpack, Prefab->Adjup);
+	c = Compound.box(menuenv, (150, 100),
+	Element.text(menuenv, "Inferno", zr, Prefab->ETitle), le);
+	c.draw();
+
+	while(rootfs(hd ls) < 0)
+		sleep(1000);
+
+	#
+	# default namespace
+	#
+	bind("#c", "/dev", sys->MBEFORE);		# console
+	bind("#H", "/dev", sys->MAFTER);
+	if(spec != nil)
+		bind(spec, "/nvfs", sys->MBEFORE|sys->MCREATE);	# our keys
+	bind("#E", "/dev", sys->MBEFORE);		# mpeg
+	bind("#l", "/net", sys->MBEFORE);		# ethernet
+	bind("#I", "/net", sys->MBEFORE);		# TCP/IP
+	bind("#V", "/dev", sys->MAFTER);		# hauppauge TV
+	bind("#p", "/prog", sys->MREPL);		# prog device
+	sys->bind("#d", "/fd", Sys->MREPL);
+
+	setclock();
+
+	le = Element.icon(menuenv, logo.r, logo, ones);
+	le = Element.elist(menuenv, le, Prefab->EVertical);
+	xe = Element.text(menuenv, Login, zr, Prefab->EText);
+	le.append(xe);
+
+	i := disp.newimage(Rect((0, 0), (320, 240)), 3, 0, 0);
+	i.draw(i.r, menustyle.elemcolor, ones, i.r.min);
+	xe = Element.icon(menuenv, i.r, i, ones);
+	le.append(xe);
+
+	le.adjust(Prefab->Adjpack, Prefab->Adjup);
+	c = Compound.box(menuenv, (160, 50),
+	Element.text(menuenv, "Inferno", zr, Prefab->ETitle), le);
+	c.draw();
+
+	xc: chan of string;
+	mpeg := load Mpeg Mpeg->PATH;
+	if(mpeg != nil) {
+		xc = chan of string;
+		r := (hd tl tl c.contents.kids).r;
+		s := mpeg->play(disp, c.image, 1, r, Intro, xc);
+		if(s != "") {
+			print("mpeg: %s\n", s);
+			xc = nil;
+		}
+	}
+
+	i2 := disp.open("/icons/delight.bit");
+	i.draw(i.r, i2, ones, i2.r.min);
+	i2 = nil;
+	if(xc != nil)
+		<-xc;
+
+	le.append(Element.text(menuenv, Garden, le.r, Prefab->EText));
+	le.adjust(Prefab->Adjpack, Prefab->Adjup);
+	c = Compound.box(menuenv, (160, 50),
+	Element.text(menuenv, "Inferno", zr, Prefab->ETitle), le);
+	c.draw();
+
+	sleep(5000);
+
+	# Do a bind to force applications to use IR module built
+	# into the kernel.
+	if(bind("#/./ir", Ir->PATH, sys->MREPL) < 0)
+		print("init: bind ir: %r\n");
+	# Uncomment the next line to load sh.dis.
+#	shell = load Shell "/dis/sh.dis";
+	dc : ref Context;
+	# Comment the next 2 lines to load sh.dis.
+	shell = load Shell "/dis/mux/mux.dis";
+	dc = ref Context(screen, disp, nil, nil, nil, nil, nil);
+	if(shell == nil) {
+		print("init: load /dis/sh.dis: %r");
+		exit;
+	}
+	shell->init(dc, nil);
+}
+
+setclock()
+{
+	(ok, dir) := sys->stat("/");
+	if (ok < 0) {
+		print("init: stat /: %r");
+		return;
+	}
+
+	fd := sys->open("/dev/time", sys->OWRITE);
+	if (fd == nil) {
+		print("init: open /dev/time: %r");
+		return;
+	}
+
+	# Time is kept as microsecs, atime is in secs
+	b := array of byte sprint("%d000000", dir.atime);
+	if (sys->write(fd, b, len b) != len b)
+		print("init: write /dev/time: %r");
+}
+
+register(signer: string): (ref Keyring->Authinfo, string)
+{
+
+	# get box id
+	fd := sys->open("/nvfs/ID", sys->OREAD);
+	if(fd == nil){
+		fd = sys->create("/nvfs/ID", sys->OWRITE, 8r664);
+		if(fd == nil)
+			return  (nil, "can't create /nvfs/ID");
+		if(sys->fprint(fd, "LT%d", randomint()) < 0)
+			return  (nil, "can't write /nvfs/ID");
+		fd = sys->open("/nvfs/ID", sys->OREAD);
+	}
+	if(fd == nil)
+		return  (nil, "can't open /nvfs/ID");
+
+	buf := array[64] of byte;
+	n := sys->read(fd, buf, (len buf) - 1);
+	if(n <= 0)
+		return (nil, "can't read /nvfs/ID");
+
+	boxid := string buf[0:n];
+	fd = nil;
+	buf = nil;
+
+	# Set-up for user input via remote control.
+	tirc = chan of int;
+	irstopc = chan of int;
+	spawn irslave(tirc, irstopc);
+	case dialogue("Register with your service provider?", "yes\nno") {
+	0 =>
+		;
+	* =>
+		return (nil, "registration not desired");
+	}
+
+	# a holder
+	info := ref Keyring->Authinfo;
+
+	# contact signer
+#	status("looking for signer");
+#	signer := virgil->virgil("$SIGNER");
+#	if(signer == nil)
+#		return (nil, "can't find signer");
+	status("dialing tcp!"+signer+"!6671");
+	(ok, c) := sys->dial("tcp!"+signer+"!6671", nil);
+	if(!ok)
+		return (nil, "can't contact signer");
+
+	# get signer's public key and diffie helman parameters
+	status("getting signer's key");
+	spkbuf := kr->getmsg(c.dfd);
+	if(spkbuf == nil)
+		return (nil, "can't read signer's key");
+	info.spk = kr->strtopk(string spkbuf);
+	if(info.spk == nil)
+		return (nil, "bad key from signer");
+	alphabuf := kr->getmsg(c.dfd);
+	if(alphabuf == nil)
+		return (nil, "can't read dh alpha");
+	info.alpha = IPint.b64toip(string alphabuf);
+	pbuf := kr->getmsg(c.dfd);
+	if(pbuf == nil)
+		return (nil, "can't read dh mod");
+	info.p = IPint.b64toip(string pbuf);
+
+	# generate our key from system parameters
+	status("generating our key");
+	info.mysk = kr->genSKfromPK(info.spk, boxid);
+	if(info.mysk == nil)
+		return (nil, "can't generate our own key");
+	info.mypk = kr->sktopk(info.mysk);
+
+	# send signer our public key
+	mypkbuf := array of byte kr->pktostr(info.mypk);
+	kr->sendmsg(c.dfd, mypkbuf, len mypkbuf);
+
+	# get blind certificate
+	status("getting blinded certificate");
+	certbuf := kr->getmsg(c.dfd);
+	if(certbuf == nil)
+		return (nil, "can't read signed key");
+
+	# verify we've got the right stuff
+	if(!verify(boxid, spkbuf, mypkbuf, certbuf))
+		return (nil, "verification failed, try again");
+
+	# contact counter signer
+	status("dialing tcp!"+signer+"!6672");
+	(ok, c) = sys->dial("tcp!"+signer+"!6672", nil);
+	if(!ok)
+		return (nil, "can't contact countersigner");
+
+	# send boxid
+	buf = array of byte boxid;
+	kr->sendmsg(c.dfd, buf, len buf);
+
+	# get blinding mask
+	status("unblinding certificate");
+	mask := kr->getmsg(c.dfd);
+	if(len mask != len certbuf)
+		return (nil, "bad mask length");
+	for(i := 0; i < len mask; i++)
+		certbuf[i] = certbuf[i] ^ mask[i];
+	info.cert = kr->strtocert(string certbuf);
+
+	status("verifying certificate");
+	state := kr->sha(mypkbuf, len mypkbuf, nil, nil);
+	if(kr->verify(info.spk, info.cert, state) == 0)
+		return (nil, "bad certificate");
+
+	status("storing keys");
+	kr->writeauthinfo("/nvfs/default", info);
+	
+	status("Congratulations, you are registered.\nPress a key to continue.");
+	<-tirc;
+	irstopc <-= 1;
+
+	return (info, nil);
+}
+
+dialogue(expl: string, selection: string): int
+{
+	c := Compound.textbox(menuenv, ((100, 100), (100, 100)), expl, selection);
+	c.draw();
+	for(;;){
+		(key, index, nil) := c.select(c.contents, 0, tirc);
+		case key {
+		Ir->Select =>
+			return index;
+		Ir->Enter =>
+			return -1;
+		}
+	}
+}
+
+status(expl: string)
+{
+#	title := Element.text(menuenv, "registration\nstatus", ((0,0),(0,0)), Prefab->ETitle);
+#	msg := Element.text(menuenv, expl, ((0,0),(0,0)), Prefab->EText);
+#	c := Compound.box(menuenv, (100, 100), title, msg);
+
+	c := Compound.textbox(menuenv, ((100, 100),(100,100)), "Registration status", expl);
+	c.draw();
+	statusbox = c;
+}
+
+pro:= array[] of {
+	"alpha", "bravo", "charlie", "delta", "echo", "foxtrot", "golf",
+	"hotel", "india", "juliet", "kilo", "lima", "mike", "nancy", "oscar",
+	"poppa", "quebec", "romeo", "sierra", "tango", "uniform",
+	"victor", "whiskey", "xray", "yankee", "zulu"
+};
+
+#
+#  prompt for acceptance
+#
+verify(boxid: string, hispk, mypk, cert: array of byte): int
+{
+	s: string;
+
+	# hash the string
+	state := kr->md5(hispk, len hispk, nil, nil);
+	kr->md5(mypk, len mypk, nil, state);
+	digest := array[Keyring->MD5dlen] of byte;
+	kr->md5(cert, len cert, digest, state);
+
+	title := Element.elist(menuenv, nil, Prefab->EVertical);
+	subtitle := Element.text(menuenv, "Telephone your service provider\n to register.  You will need\nthe following:\n", ((0,0),(0,0)), Prefab->ETitle);
+	title.append(subtitle);
+
+	line := Element.text(menuenv, "boxid is '"+boxid+"'.", ((0,0),(0,0)), Prefab->ETitle);
+	title.append(line);
+	for(i := 0; i < len digest; i++){
+		line = Element.elist(menuenv, nil, Prefab->EHorizontal);
+		s = (string (2*i)) + ": " + pro[((int digest[i])>>4)%len pro];
+		line.append(Element.text(menuenv, s, ((0,0),(0,0)), Prefab->ETitle));
+
+		s = (string (2*i+1)) + ": " + pro[(int digest[i])%len pro] + "\n";
+		line.append(Element.text(menuenv, s, ((0,0),(200,0)), Prefab->ETitle));
+
+		line.adjust(Prefab->Adjequal, Prefab->Adjleft);
+		title.append(line);
+	}
+	title.adjust(Prefab->Adjpack, Prefab->Adjleft);
+
+	le := Element.elist(menuenv, nil, Prefab->EHorizontal);
+	le.append(Element.text(menuenv, " accept ", ((0, 0), (0, 0)), Prefab->EText));
+	le.append(Element.text(menuenv, " reject ", ((0, 0), (0, 0)), Prefab->EText));
+	le.adjust(Prefab->Adjpack, Prefab->Adjleft);
+
+	c := Compound.box(menuenv, (50, 50), title, le);
+	c.draw();
+
+	for(;;){
+		(key, index, nil) := c.select(c.contents, 0, tirc);
+		case key {
+		Ir->Select =>
+			if(index == 0)
+				return 1;
+			return 0;
+		Ir->Enter =>
+			return 0;
+		}
+	}
+
+	return 0;
+}
+
+randomint(): int
+{
+	fd := sys->open("/dev/random", sys->OREAD);
+	if(fd == nil)
+		return 0;
+	buf := array[4] of byte;
+	sys->read(fd, buf, 4);
+	rand := 0;
+	for(i := 0; i < 4; i++)
+		rand = (rand<<8) | int buf[i];
+	return rand;
+}
+
+# Reads real (if possible) or simulated remote, returns Ir events on irc.
+# Must be a separate thread to be able to 1) read raw Ir input channel
+# and 2) write translated Ir input data on output channel.
+irslave(irc, stopc: chan of int)
+{
+	in, irpid: int;
+	buf: list of int;
+	outc: chan of int;
+
+	irchan := chan of int;	# Untranslated Ir input channel.
+	irpidch := chan of int;	# Ir reader pid channel.
+	irmod := load Ir "#/./ir";	# Module built into kernel.
+
+	if(irmod==nil){
+		print("irslave: failed to load #/./ir");
+		return;
+	}
+	if(irmod->init(irchan, irpidch)<0){
+		print("irslave: failed to initialize ir");
+		return;
+	}
+	irpid =<-irpidch;
+
+	hdbuf := 0;
+	dummy := chan of int;
+	for(;;){
+		if(buf == nil){
+			outc = dummy;
+		}else{
+			outc = irc;
+			hdbuf = hd buf;
+		}
+		alt{
+		in = <-irchan =>
+			buf = append(buf, in);
+		outc <-= irmod->translate(hdbuf) =>
+			buf = tl buf;
+		<-stopc =>{
+			killir(irpid);
+			return;
+			}
+		}
+	}
+}
+
+append(l: list of int, i: int): list of int
+{
+	if(l == nil)
+		return i :: nil;
+	return hd l :: append(tl l, i);
+}
+
+killir(irpid: int)
+{
+        pid := sys->sprint("%d", irpid);
+        fd := sys->open("#p/"+pid+"/ctl", sys->OWRITE);
+        if(fd==nil) {
+                print("init: process %s: %r\n", pid);
+                return;
+        }
+ 
+        msg := array of byte "kill";
+        n := sys->write(fd, msg, len msg);
+        if(n < 0) {
+                print("init: message for %s: %r\n", pid);
+                return;
+        }
+}
+
+#
+# Set system name from nvram
+#
+setsysname()
+{
+	fd := open("/nvfs/ID", sys->OREAD);
+	if(fd == nil)
+		return;
+	fds := open("/dev/sysname", sys->OWRITE);
+	if(fds == nil)
+		return;
+	buf := array[128] of byte;
+	nr := sys->read(fd, buf, len buf);
+	if(nr <= 0)
+		return;
+	sys->write(fds, buf, nr);
+}
--- /dev/null
+++ b/os/init/ipaqinit.b
@@ -1,0 +1,697 @@
+#
+# ipaq
+#
+# TO DO: read params from params flash
+#
+
+implement Init;
+
+include "sys.m";
+	sys:	Sys;
+
+include "draw.m";
+
+include "keyring.m";
+	kr: Keyring;
+
+include "security.m";
+	auth: Auth;
+
+include "dhcp.m";
+	dhcpclient: Dhcpclient;
+	Bootconf: import dhcpclient;
+
+include "keyboard.m";
+
+include "sh.m";
+
+Init: module
+{
+	init:	fn();
+};
+
+Bootpreadlen: con 128;
+
+ethername := "ether0";
+
+# standard Inferno flash partitions
+
+flashparts := array[] of {
+	# bootstrap at 0x0 to 0x40000, don't touch
+	"add params 0x40000 0x80000",
+	"add kernel 0x80000 0x140000",
+	"add fs 0x140000 end",
+};
+
+#
+# initialise flash translation
+# mount flash file system
+# add devices
+# start a shell or window manager
+#
+
+init()
+{
+	sys = load Sys Sys->PATH;
+	kr = load Keyring Keyring->PATH;
+	auth = load Auth Auth->PATH;
+	if(auth != nil)
+		auth->init();
+
+	sys->bind("/", "/", Sys->MREPL);
+
+	lightup();
+
+	localok := 0;
+	if(lfs() >= 0){
+		# let's just take a closer look
+		sys->bind("/n/local/nvfs", "/nvfs", Sys->MREPL|Sys->MCREATE);
+		(rc, nil) := sys->stat("/n/local/dis/sh.dis");
+		if(rc >= 0)
+			localok = 1;
+		else
+			err("local file system unusable");
+	}
+	netok := sys->bind("#l", "/net", Sys->MREPL) >= 0;
+	if(!netok){
+		netok = sys->bind("#l1", "/net", Sys->MREPL) >= 0;
+		if(netok)
+			ethername = "ether1";
+	}
+	if(netok)
+		configether();
+
+	dobind("#I", "/net", sys->MAFTER);	# IP
+	dobind("#p", "/prog", sys->MREPL);	# prog
+	dobind("#c", "/dev", sys->MREPL); 	# console
+	sys->bind("#d", "/fd", Sys->MREPL);
+	dobind("#t", "/dev", sys->MAFTER);	# serial line
+	dobind("#i", "/dev", sys->MAFTER); 	# draw
+	dobind("#m", "/dev", Sys->MAFTER);	# pointer
+	sys->bind("#e", "/env", sys->MREPL|sys->MCREATE);	# environment
+	sys->bind("#A", "/dev", Sys->MAFTER);	# optional audio
+	dobind("#T","/dev",sys->MAFTER);	# touch screen and other ipaq devices
+
+	timefile: string;
+	rootsource: string;
+	cfd := sys->open("/dev/consctl", Sys->OWRITE);
+	if(cfd != nil)
+		sys->fprint(cfd, "rawon");
+	for(;;){
+		(rootsource, timefile) = askrootsource(localok, netok);
+		if(rootsource == nil)
+			break;	# internal
+		(rc, nil) := sys->stat(rootsource+"/dis/sh.dis");
+		if(rc < 0)
+			err("%s has no shell");
+		else if(sys->bind(rootsource, "/", Sys->MAFTER) < 0)
+			sys->print("can't bind %s on /: %r\n", rootsource);
+		else{
+			sys->bind(rootsource+"/dis", "/dis", Sys->MBEFORE|Sys->MCREATE);
+			break;
+		}
+	}
+	cfd = nil;
+
+	setsysname("ipaq");			# set system name
+
+	now := getclock(timefile, rootsource);
+	setclock("/dev/time", now);
+	if(timefile != "#r/rtc")
+		setclock("#r/rtc", now/big 1000000);
+
+	sys->chdir("/");
+	if(netok){
+		start("ndb/dns", nil);
+		start("ndb/cs", nil);
+	}
+	calibrate();
+	startup := "/nvfs/startup";
+	if(sys->open(startup, Sys->OREAD) != nil){
+		shell := load Command Sh->PATH;
+		if(shell != nil){
+			sys->print("Running %s\n", startup);
+			shell->init(nil, "sh" :: startup :: nil);
+		}
+	}
+	user := rdenv("user", "inferno");
+	(ok, nil) := sys->stat("/dis/wm/wm.dis");
+	if(ok >= 0)
+		(ok, nil) = sys->stat("/dis/wm/logon.dis");
+	if(ok >= 0 && userok(user)){
+		wm := load Command "/dis/wm/wm.dis";
+		if(wm != nil){
+			fd := sys->open("/nvfs/user", Sys->OWRITE);
+			if(fd != nil){
+				sys->fprint(fd, "%s", user);
+				fd = nil;
+			}
+			spawn wm->init(nil, list of {"wm/wm", "wm/logon", "-l", "-n", "lib/ipaqns", "-u", user});
+			exit;
+		}
+		sys->print("init: can't load wm/logon: %r");
+	}
+	sh := load Command Sh->PATH;
+	if(sh == nil){
+		err(sys->sprint("can't load %s: %r", Sh->PATH));
+		hang();
+	}
+	spawn sh->init(nil, "sh" :: nil);
+}
+
+start(cmd: string, args: list of string)
+{
+	disfile := cmd;
+	if(disfile[0] != '/')
+		disfile = "/dis/"+disfile+".dis";
+	(ok, nil) := sys->stat(disfile);
+	if(ok >= 0){
+		dis := load Command disfile;
+		if(dis == nil)
+			sys->print("init: can't load %s: %r\n", disfile);
+		else
+			spawn dis->init(nil, cmd :: args);
+	}
+}
+
+dobind(f, t: string, flags: int)
+{
+	if(sys->bind(f, t, flags) < 0)
+		err(sys->sprint("can't bind %s on %s: %r", f, t));
+}
+
+lightup()
+{
+	# backlight
+	fd := sys->open("#T/ipaqctl", Sys->OWRITE);
+	if(fd != nil)
+		sys->fprint(fd, "light 1 1 0x80");
+}
+
+#
+# Set system name from nvram if possible
+#
+setsysname(def: string)
+{
+	v := array of byte def;
+	fd := sys->open("/nvfs/ID", sys->OREAD);
+	if(fd == nil)
+		fd = sys->open("/env/sysname", sys->OREAD);
+	if(fd != nil){
+		buf := array[Sys->NAMEMAX] of byte;
+		nr := sys->read(fd, buf, len buf);
+		while(nr > 0 && buf[nr-1] == byte '\n')
+			nr--;
+		if(nr > 0)
+			v = buf[0:nr];
+	}
+	fd = sys->open("/dev/sysname", sys->OWRITE);
+	if(fd != nil)
+		sys->write(fd, v, len v);
+}
+
+getclock(timefile: string, timedir: string): big
+{
+	now := big 0;
+	if(timefile != nil){
+		fd := sys->open(timefile, Sys->OREAD);
+		if(fd != nil){
+			b := array[64] of byte;
+			n := sys->read(fd, b, len b-1);
+			if(n > 0){
+				now = big string b[0:n];
+				if(now <= big 16r20000000)
+					now = big 0;	# remote itself is not initialised
+			}
+		}
+	}
+	if(now == big 0){
+		if(timedir != nil){
+			(ok, dir) := sys->stat(timedir);
+			if(ok < 0) {
+				sys->print("init: stat %s: %r", timedir);
+				return big 0;
+			}
+			now = big dir.atime;
+		}else{
+			now = big 993826747000000;
+			sys->print("time warped\n");
+		}
+	}
+	return now;
+}
+
+setclock(timefile: string, now: big)
+{
+	fd := sys->open(timefile, sys->OWRITE);
+	if (fd == nil) {
+		sys->print("init: can't open %s: %r", timefile);
+		return;
+	}
+
+	b := sys->aprint("%ubd", now);
+	if (sys->write(fd, b, len b) != len b)
+		sys->print("init: can't write to %s: %r", timefile);
+}
+
+srv()
+{
+	sys->print("remote debug srv...");
+	fd := sys->open("/dev/eia0ctl", Sys->OWRITE);
+	if(fd != nil)
+		sys->fprint(fd, "b115200");
+
+	fd = sys->open("/dev/eia0", Sys->ORDWR);
+	if (fd == nil){
+		err(sys->sprint("can't open /dev/eia0: %r"));
+		return;
+	}
+	if (sys->export(fd, "/", Sys->EXPASYNC) < 0){
+		err(sys->sprint("can't export on serial port: %r"));
+		return;
+	}
+}
+
+err(s: string)
+{
+	sys->fprint(sys->fildes(2), "init: %s\n", s);
+}
+
+hang()
+{
+	<-(chan of int);
+}
+
+tried := 0;
+
+askrootsource(localok: int, netok: int): (string, string)
+{
+	stdin := sys->fildes(0);
+	sources := "kernel" :: nil;
+	if(netok)
+		sources = "remote" :: sources;
+	if(localok){
+		sources = "local" :: sources;
+		if(netok)
+			sources = "local+remote" :: sources;
+	}
+Query:
+	for(;;) {
+		s := "";
+		if (tried == 0 && (s = rdenv("rootsource", nil)) != nil) {
+			tried = 1;
+			if (s[len s - 1] == '\n')
+				s = s[:len s - 1];
+			sys->print("/nvfs/rootsource: root from %s\n", s);
+		} else {
+			sys->print("root from (");
+			cm := "";
+			for(l := sources; l != nil; l = tl l){
+				sys->print("%s%s", cm, hd l);
+				cm = ",";
+			}
+			sys->print(")[%s] ", hd sources);
+
+			s = getline(stdin, hd sources);	# default
+		}
+		case s[0] {
+		Keyboard->Right or Keyboard->Left =>
+			sources = append(hd sources, tl sources);
+			sys->print("\n");
+			continue Query;
+		Keyboard->Down =>
+			s = hd sources;
+			sys->print(" %s\n", s);
+		}
+		(nil, choice) := sys->tokenize(s, "\t ");
+		if(choice == nil)
+			choice = sources;
+		opt := hd choice;
+		case opt {
+		* =>
+			sys->print("\ninvalid boot option: '%s'\n", opt);
+		"kernel" =>
+			return (nil, "#r/rtc");
+		"local" =>
+			return ("/n/local", "#r/rtc");
+		"local+remote" =>
+			if(netfs("/n/remote") >= 0)
+				return ("/n/local", "/n/remote/dev/time");
+		"remote" =>
+			if(netfs("/n/remote") >= 0)
+				return ("/n/remote", "/n/remote/dev/time");
+		}
+	}
+}
+
+getline(fd: ref Sys->FD, default: string): string
+{
+	result := "";
+	buf := array[10] of byte;
+	i := 0;
+	for(;;) {
+		n := sys->read(fd, buf[i:], len buf - i);
+		if(n < 1)
+			break;
+		i += n;
+		while(i >0 && (nutf := sys->utfbytes(buf, i)) > 0){
+			s := string buf[0:nutf];
+			for (j := 0; j < len s; j++)
+				case s[j] {
+				'\b' =>
+					if(result != nil)
+						result = result[0:len result-1];
+				'u'&16r1F =>
+					sys->print("^U\n");
+					result = "";
+				'\r' =>
+					;
+				* =>
+					sys->print("%c", s[j]);
+					if(s[j] == '\n' || s[j] >= 16r80){
+						if(s[j] != '\n')
+							result[len result] = s[j];
+						if(result == nil)
+							return default;
+						return result;
+					}
+					result[len result] = s[j];
+				}
+			buf[0:] = buf[nutf:i];
+			i -= nutf;
+		}
+	}
+	return default;
+}
+
+append(v: string, l: list of string): list of string
+{
+	if(l == nil)
+		return v :: nil;
+	return hd l :: append(v, tl l);
+}
+
+#
+# serve local DOS or kfs file system using flash translation layer
+#
+lfs(): int
+{
+	if(!flashpart("#F/flash/flashctl", flashparts))
+		return -1;
+	if(!ftlinit("#F/flash/fs"))
+		return -1;
+	if(iskfs("#X/ftldata"))
+		return lkfs("#X/ftldata");
+	c := chan of string;
+	spawn startfs(c, "/dis/dossrv.dis", "dossrv" :: "-f" :: "#X/ftldata" :: "-m" :: "/n/local" :: nil, nil);
+	if(<-c != nil)
+		return -1;
+	return 0;
+}
+
+wmagic := "kfs wren device\n";
+
+iskfs(file: string): int
+{
+	fd := sys->open(file, Sys->OREAD);
+	if(fd == nil)
+		return 0;
+	buf := array[512] of byte;
+	n := sys->read(fd, buf, len buf);
+	if(n < len buf)
+		return 0;
+	if(string buf[256:256+len wmagic] != wmagic)
+		return 0;
+	RBUFSIZE := int string buf[256+len wmagic:256+len wmagic+12];
+	if(RBUFSIZE % 512)
+		return 0;	# bad block size
+	return 1;
+}
+
+lkfs(file: string): int
+{
+	p := array[2] of ref Sys->FD;
+	if(sys->pipe(p) < 0)
+		return -1;
+	c := chan of string;
+	spawn startfs(c, "/dis/disk/kfs.dis", "disk/kfs" :: "-A" :: "-n" :: "main" :: file :: nil, p[0]);
+	if(<-c != nil)
+		return -1;
+	p[0] = nil;
+	return sys->mount(p[1], nil, "/n/local", Sys->MREPL|Sys->MCREATE, nil);
+}
+
+startfs(c: chan of string, file: string, args: list of string, fd: ref Sys->FD)
+{
+	if(fd != nil){
+		sys->pctl(Sys->NEWFD, fd.fd :: 1 :: 2 :: nil);
+		sys->dup(fd.fd, 0);
+	}
+	fs := load Command file;
+	if(fs == nil){
+		sys->print("can't load %s: %r\n", file);
+		c <-= "load failed";
+	}
+	{
+		fs->init(nil, args);
+		c <-= nil;
+	}exception {
+	"*" =>
+		c <-= "failed";
+	* =>
+		c <-= "unknown exception";
+	}
+}
+
+#
+# partition flash
+#
+flashdone := 0;
+
+flashpart(ctl: string, parts: array of string): int
+{
+	if(flashdone)
+		return 1;
+	cfd := sys->open(ctl, Sys->ORDWR);
+	if(cfd == nil){
+		sys->print("can't open %s: %r\n", ctl);
+		return 0;
+	}
+	for(i := 0; i < len parts; i++)
+		if(sys->fprint(cfd, "%s", parts[i]) < 0){
+			sys->print("can't %q to %s: %r\n", parts[i], ctl);
+			return 0;
+		}
+	flashdone = 1;
+	return 1;
+}
+
+#
+# set up flash translation layer
+#
+ftldone := 0;
+
+ftlinit(flashmem: string): int
+{
+	if(ftldone)
+		return 1;
+	sys->print("Set flash translation of %s...\n", flashmem);
+	fd := sys->open("#X/ftlctl", Sys->OWRITE);
+	if(fd == nil){
+		sys->print("can't open #X/ftlctl: %r\n");
+		return 0;
+	}
+	if(sys->fprint(fd, "init %s", flashmem) <= 0){
+		sys->print("can't init flash translation: %r\n");
+		return 0;
+	}
+	ftldone = 1;
+	return 1;
+}
+
+configether()
+{
+	if(ethername == nil)
+		return;
+	fd := sys->open("/nvfs/etherparams", Sys->OREAD);
+	if(fd == nil)
+		return;
+	ctl := sys->open("/net/"+ethername+"/clone", Sys->OWRITE);
+	if(ctl == nil){
+		sys->print("init: can't open %s's clone: %r\n", ethername);
+		return;
+	}
+	b := array[1024] of byte;
+	n := sys->read(fd, b, len b);
+	if(n <= 0)
+		return;
+	for(i := 0; i < n;){
+		for(e := i; e < n && b[e] != byte '\n'; e++)
+			;
+		s := string b[i:e];
+		if(sys->fprint(ctl, "%s", s) < 0)
+			sys->print("init: ctl write to %s: %s: %r\n", ethername, s);
+		i = e+1;
+	}
+}
+
+donebind := 0;
+
+#
+# set up network mount
+#
+netfs(mountpt: string): int
+{
+	fd: ref Sys->FD;
+	if(!donebind){
+		fd = sys->open("/net/ipifc/clone", sys->OWRITE);
+		if(fd == nil) {
+			sys->print("init: open /net/ipifc/clone: %r\n");
+			return -1;
+		}
+		if(sys->fprint(fd, "bind ether %s", ethername) < 0) {
+			sys->print("could not bind ether0 interface: %r\n");
+			return -1;
+		}
+		donebind = 1;
+	}else{
+		fd = sys->open("/net/ipifc/0/ctl", Sys->OWRITE);
+		if(fd == nil){
+			sys->print("init: can't reopen /net/ipifc/0/ctl: %r\n");
+			return -1;
+		}
+	}
+	server := rdenv("fsip", nil);
+	if((ip := rdenv("ip", nil)) != nil) {
+		sys->print("**using %s\n", ip);
+		sys->fprint(fd, "bind ether /net/ether0");
+		sys->fprint(fd, "add %s ", ip);
+		if((ipgw := rdenv("ipgw", nil)) != nil){
+			rfd := sys->open("/net/iproute", Sys->OWRITE);
+			if(rfd != nil){
+				sys->fprint(rfd, "add 0 0 %s", ipgw);
+				sys->print("**using ipgw=%s\n", ipgw);
+			}
+		}
+	}else if(server == nil){
+		sys->print("dhcp...");
+		dhcpclient = load Dhcpclient Dhcpclient->PATH;
+		if(dhcpclient == nil){
+			sys->print("can't load dhcpclient: %r\n");
+			return -1;
+		}
+		dhcpclient->init();
+		(cfg, nil, e) := dhcpclient->dhcp("/net", fd, "/net/ether0/addr", nil, nil);
+		if(e != nil){
+			sys->print("dhcp: %s\n", e);
+			return -1;
+		}
+		if(server == nil)
+			server = cfg.getip(Dhcpclient->OP9fs);
+		dhcpclient = nil;
+	}
+	if(server == nil || server == "0.0.0.0"){
+		sys->print("no file server address\n");
+		return -1;
+	}
+	sys->print("fs=%s\n", server);
+
+	net := "tcp";	# how to specify il?
+	svcname := net + "!" + server + "!6666";
+
+	sys->print("dial %s...", svcname);
+
+	(ok, c) := sys->dial(svcname, nil);
+	if(ok < 0){
+		sys->print("can't dial %s: %r\n", svcname);
+		return -1;
+	}
+
+	sys->print("\nConnected ...\n");
+	if(kr != nil){
+		err: string;
+		sys->print("Authenticate ...");
+		ai := kr->readauthinfo("/nvfs/default");
+		if(ai == nil){
+			sys->print("readauthinfo /nvfs/default failed: %r\n");
+			sys->print("trying mount as `nobody'\n");
+		}
+		(c.dfd, err) = auth->client("none", ai, c.dfd);
+		if(c.dfd == nil){
+			sys->print("authentication failed: %s\n", err);
+			return -1;
+		}
+	}
+
+	sys->print("mount %s...", mountpt);
+
+	c.cfd = nil;
+	n := sys->mount(c.dfd, nil, mountpt, sys->MREPL, "");
+	if(n > 0)
+		return 0;
+	if(n < 0)
+		sys->print("%r");
+	return -1;
+}
+
+calibrate()
+{
+	val := rf("/nvfs/calibrate", nil);
+	if(val != nil){
+		fd := sys->open("/dev/touchctl", Sys->OWRITE);
+		if(fd != nil && sys->fprint(fd, "%s", val) >= 0)
+			return;
+	}
+	done := chan of int;
+	spawn docal(done);
+	<-done;
+}
+
+docal(done: chan of int)
+{
+	sys->pctl(Sys->FORKFD, nil);
+	ofd := sys->create("/nvfs/calibrate", Sys->OWRITE, 8r644);
+	if(ofd != nil)
+		sys->dup(ofd.fd, 1);
+	cal := load Command "/dis/touchcal.dis";
+	if(cal != nil){
+		{
+			cal->init(nil, "touchcal" :: nil);
+		}exception{
+		"fail:*" =>
+			;
+		}
+	}
+	done <-= 1;
+}
+
+userok(user: string): int
+{
+	(ok, d) := sys->stat("/usr/"+user);
+	return ok >= 0 && (d.mode & Sys->DMDIR) != 0;
+}
+
+rdenv(name: string, def: string): string
+{
+	s := rf("#e/"+name, nil);
+	if(s != nil)
+		return s;
+	s = rf("/nvfs/"+name, def);
+	while(s != nil && ((c := s[len s-1]) == '\n' || c == '\r'))
+		s = s[0: len s-1];
+	if(s != nil)
+		return s;
+	return def;
+}
+
+rf(file: string, default: string): string
+{
+	fd := sys->open(file, Sys->OREAD);
+	if(fd != nil){
+		buf := array[128] of byte;
+		nr := sys->read(fd, buf, len buf);
+		if(nr > 0)
+			return string buf[0:nr];
+	}
+	return default;
+}
--- /dev/null
+++ b/os/init/ipeinit.b
@@ -1,0 +1,620 @@
+#
+# ipEngine-1
+#
+
+implement Init;
+
+include "sys.m";
+	sys:	Sys;
+
+include "draw.m";
+
+include "keyring.m";
+	kr: Keyring;
+
+include "security.m";
+	auth: Auth;
+
+include "sh.m";
+
+Init: module
+{
+	init:	fn();
+};
+
+Bootpreadlen: con 128;
+
+# standard flash partitions
+
+flashparts := array[] of {
+	# bootstrap at 0x0 to 0x6000
+	"add boot 0 0x6000",
+	"add param 0x6000 0x10000",
+	"add kernel 0x10000 0x110000",
+	"add fs 0x110000 end",
+};
+
+ethername := "ether0";
+
+#
+# initialise flash translation
+# mount flash file system
+# add devices
+# start a shell or window manager
+#
+
+init()
+{
+	sys = load Sys Sys->PATH;
+	kr = load Keyring Keyring->PATH;
+	auth = load Auth Auth->PATH;
+	if(auth != nil)
+		auth->init();
+
+	sys->bind("/", "/", Sys->MREPL);
+
+	localok := 0;
+	if(lfs() >= 0){
+		# let's just take a closer look
+		sys->bind("/n/local/nvfs", "/nvfs", Sys->MREPL|Sys->MCREATE);
+		(rc, nil) := sys->stat("/n/local/dis/sh.dis");
+		if(rc >= 0)
+			localok = 1;
+		else
+			err("local file system unusable");
+	}
+	netok := sys->bind("#l", "/net", Sys->MREPL) >= 0;
+	if(!netok){
+		netok = sys->bind("#l1", "/net", Sys->MREPL) >= 0;
+		if(netok)
+			ethername = "ether1";
+	}
+	if(netok)
+		configether();
+	dobind("#I", "/net", sys->MAFTER);	# IP
+	dobind("#p", "/prog", sys->MREPL);	# prog
+	sys->bind("#d", "/fd", Sys->MREPL);
+	dobind("#c", "/dev", sys->MREPL); 	# console
+	dobind("#t", "/dev", sys->MAFTER);	# serial line
+	drawok := sys->bind("#i", "/dev", sys->MAFTER) >= 0; 	# draw
+	sys->bind("#m", "/dev", sys->MAFTER);	# pointer
+	sys->bind("#e", "/env", sys->MREPL|sys->MCREATE);	# environment
+	sys->bind("#A", "/dev", Sys->MAFTER);	# optional audio
+	timefile: string;
+	rootsource: string;
+	cfd := sys->open("/dev/consctl", Sys->OWRITE);
+	if(cfd != nil)
+		sys->fprint(cfd, "rawon");
+	for(;;){
+		(rootsource, timefile) = askrootsource(localok, netok);
+		if(rootsource == nil)
+			break;	# internal
+		(rc, nil) := sys->stat(rootsource+"/dis/sh.dis");
+		if(rc < 0)
+			err("%s has no shell");
+		else if(sys->bind(rootsource, "/", Sys->MAFTER) < 0)
+			sys->print("can't bind %s on /: %r\n", rootsource);
+		else{
+			sys->bind(rootsource+"/dis", "/dis", Sys->MBEFORE|Sys->MCREATE);
+			break;
+		}
+	}
+	cfd = nil;
+
+	setsysname("ipe");			# set system name
+
+	now := getclock(timefile, rootsource);
+	setclock("/dev/time", now);
+	if(timefile != "#r/rtc")
+		setclock("#r/rtc", now/big 1000000);
+
+	sys->chdir("/");
+	if(netok){
+		start("ndb/dns", nil);
+		start("ndb/cs", nil);
+	}
+	startup := "/nvfs/startup";
+	if(sys->open(startup, Sys->OREAD) != nil){
+		shell := load Command Sh->PATH;
+		if(shell != nil){
+			sys->print("Running %s\n", startup);
+			shell->init(nil, "sh" :: startup :: nil);
+		}
+	}
+	user := username("inferno");
+	(ok, nil) := sys->stat("/dis/wm/wm.dis");
+	if(drawok && ok >= 0)
+		(ok, nil) = sys->stat("/dis/wm/logon.dis");
+	if(drawok && ok >= 0 && userok(user)){
+		wm := load Command "/dis/wm/wm.dis";
+		if(wm != nil){
+			fd := sys->open("/nvfs/user", Sys->OWRITE);
+			if(fd != nil){
+				sys->fprint(fd, "%s", user);
+				fd = nil;
+			}
+			spawn wm->init(nil, list of {"wm/wm", "wm/logon", "-l", "-u", user});
+			exit;
+		}
+		sys->print("init: can't load wm/logon: %r");
+	}
+	sh := load Command Sh->PATH;
+	if(sh == nil){
+		err(sys->sprint("can't load %s: %r", Sh->PATH));
+		hang();
+	}
+	spawn sh->init(nil, "sh" :: nil);
+}
+
+start(cmd: string, args: list of string)
+{
+	disfile := cmd;
+	if(disfile[0] != '/')
+		disfile = "/dis/"+disfile+".dis";
+	(ok, nil) := sys->stat(disfile);
+	if(ok >= 0){
+		dis := load Command disfile;
+		if(dis == nil)
+			sys->print("init: can't load %s: %r\n", disfile);
+		else
+			spawn dis->init(nil, cmd :: args);
+	}
+}
+
+dobind(f, t: string, flags: int)
+{
+	if(sys->bind(f, t, flags) < 0)
+		err(sys->sprint("can't bind %s on %s: %r", f, t));
+}
+
+#
+# Set system name from nvram if possible
+#
+setsysname(def: string)
+{
+	v := array of byte def;
+	fd := sys->open("/nvfs/ID", sys->OREAD);
+	if(fd == nil)
+		fd = sys->open("/env/sysname", sys->OREAD);
+	if(fd != nil){
+		buf := array[Sys->NAMEMAX] of byte;
+		nr := sys->read(fd, buf, len buf);
+		while(nr > 0 && buf[nr-1] == byte '\n')
+			nr--;
+		if(nr > 0)
+			v = buf[0:nr];
+	}
+	fd = sys->open("/dev/sysname", sys->OWRITE);
+	if(fd != nil)
+		sys->write(fd, v, len v);
+}
+
+getclock(timefile: string, timedir: string): big
+{
+	now := big 0;
+	if(timefile != nil){
+		fd := sys->open(timefile, Sys->OREAD);
+		if(fd != nil){
+			b := array[64] of byte;
+			n := sys->read(fd, b, len b-1);
+			if(n > 0){
+				now = big string b[0:n];
+				if(now <= big 16r20000000)
+					now = big 0;	# remote itself is not initialised
+			}
+		}
+	}
+	if(now == big 0){
+		if(timedir != nil){
+			(ok, dir) := sys->stat(timedir);
+			if(ok < 0) {
+				sys->print("init: stat %s: %r", timedir);
+				return big 0;
+			}
+			now = big dir.atime;
+		}else{
+			now = big 993826747000000;
+			sys->print("time warped\n");
+		}
+	}
+	return now;
+}
+
+setclock(timefile: string, now: big)
+{
+	fd := sys->open(timefile, sys->OWRITE);
+	if (fd == nil) {
+		sys->print("init: can't open %s: %r", timefile);
+		return;
+	}
+
+	b := sys->aprint("%ubd", now);
+	if (sys->write(fd, b, len b) != len b)
+		sys->print("init: can't write to %s: %r", timefile);
+}
+
+srv()
+{
+	sys->print("remote debug srv...");
+	fd := sys->open("/dev/eia0ctl", Sys->OWRITE);
+	if(fd != nil)
+		sys->fprint(fd, "b115200");
+
+	fd = sys->open("/dev/eia0", Sys->ORDWR);
+	if (fd == nil){
+		err(sys->sprint("can't open /dev/eia0: %r"));
+		return;
+	}
+	if (sys->export(fd, "/", Sys->EXPASYNC) < 0){
+		err(sys->sprint("can't export on serial port: %r"));
+		return;
+	}
+}
+
+err(s: string)
+{
+	sys->fprint(sys->fildes(2), "init: %s\n", s);
+}
+
+hang()
+{
+	<-chan of int;
+}
+
+tried := 0;
+
+askrootsource(localok: int, netok: int): (string, string)
+{
+	stdin := sys->fildes(0);
+	sources := "kernel" :: nil;
+	if(netok)
+		sources = "remote" :: sources;
+	if(localok){
+		sources = "local" :: sources;
+		if(netok)
+			sources = "local+remote" :: sources;
+	}
+	for(;;) {
+		s := "";
+		if (tried == 0 && (s = rf("/nvfs/rootsource", nil)) != nil) {
+			tried = 1;
+			if (s[len s - 1] == '\n')
+				s = s[:len s - 1];
+			sys->print("/nvfs/rootsource: root from %s\n", s);
+		} else {
+			sys->print("root from (");
+			cm := "";
+			for(l := sources; l != nil; l = tl l){
+				sys->print("%s%s", cm, hd l);
+				cm = ",";
+			}
+			sys->print(")[%s] ", hd sources);
+
+			s = getline(stdin, hd sources);	# default
+		}
+		(nil, choice) := sys->tokenize(s, "\t ");
+		if(choice == nil)
+			choice = sources;
+		opt := hd choice;
+		case opt {
+		* =>
+			sys->print("\ninvalid boot option: '%s'\n", opt);
+		"kernel" =>
+			return (nil, "#r/rtc");
+		"local" =>
+			return ("/n/local", "#r/rtc");
+		"local+remote" =>
+			if(netfs("/n/remote") >= 0)
+				return ("/n/local", "/n/remote/dev/time");
+		"remote" =>
+			if(netfs("/n/remote") >= 0)
+				return ("/n/remote", "/n/remote/dev/time");
+		}
+	}
+}
+
+getline(fd: ref Sys->FD, default: string): string
+{
+	result := "";
+	buf := array[10] of byte;
+	i := 0;
+	for(;;) {
+		n := sys->read(fd, buf[i:], len buf - i);
+		if(n < 1)
+			break;
+		i += n;
+		while(i >0 && (nutf := sys->utfbytes(buf, i)) > 0){
+			s := string buf[0:nutf];
+			for (j := 0; j < len s; j++)
+				case s[j] {
+				'\b' =>
+					if(result != nil)
+						result = result[0:len result-1];
+				'u'&16r1F =>
+					sys->print("^U\n");
+					result = "";
+				'\r' =>
+					;
+				* =>
+					sys->print("%c", s[j]);
+					if(s[j] == '\n' || s[j] >= 16r80){
+						if(s[j] != '\n')
+							result[len result] = s[j];
+						if(result == nil)
+							return default;
+						return result;
+					}
+					result[len result] = s[j];
+				}
+			buf[0:] = buf[nutf:i];
+			i -= nutf;
+		}
+	}
+	return default;
+}
+
+#
+# serve local DOS file system using flash translation layer
+#
+lfs(): int
+{
+	if(!flashpart("#F/flash/flashctl", flashparts))
+		return -1;
+	if(!ftlinit("#F/flash/fs"))
+		return -1;
+	c := chan of string;
+	spawn startfs(c, "/dis/dossrv.dis", "dossrv" :: "-f" :: "#X/ftldata" :: "-m" :: "/n/local" :: nil);
+	if(<-c != nil)
+		return -1;
+	return 0;
+}
+
+startfs(c: chan of string, file: string, args: list of string)
+{
+	fs := load Command file;
+	if(fs == nil){
+		sys->print("can't load %s: %r\n", file);
+		c <-= "load failed";
+	}
+	{
+		fs->init(nil, args);
+	}exception e {
+	"*" =>
+		c <-= "failed";
+		exit;
+	* =>
+		c <-= "unknown exception";
+		exit;
+	}
+	c <-= nil;
+}
+
+#
+# partition flash
+#
+flashdone := 0;
+
+flashpart(ctl: string, parts: array of string): int
+{
+	if(flashdone)
+		return 1;
+	cfd := sys->open(ctl, Sys->ORDWR);
+	if(cfd == nil){
+		sys->print("can't open %s: %r\n", ctl);
+		return 0;
+	}
+	for(i := 0; i < len parts; i++)
+		if(sys->fprint(cfd, "%s", parts[i]) < 0){
+			sys->print("can't %q to %s: %r\n", parts[i], ctl);
+			return 0;
+		}
+	flashdone = 1;
+	return 1;
+}
+
+#
+# set up flash translation layer
+#
+ftldone := 0;
+
+ftlinit(flashmem: string): int
+{
+	if(ftldone)
+		return 1;
+	sys->print("Set flash translation of %s...\n", flashmem);
+	fd := sys->open("#X/ftlctl", Sys->OWRITE);
+	if(fd == nil){
+		sys->print("can't open #X/ftlctl: %r\n");
+		return 0;
+	}
+	if(sys->fprint(fd, "init %s", flashmem) <= 0){
+		sys->print("can't init flash translation: %r\n");
+		return 0;
+	}
+	ftldone = 1;
+	return 1;
+}
+
+configether()
+{
+	if(ethername == nil)
+		return;
+	fd := sys->open("/nvfs/etherparams", Sys->OREAD);
+	if(fd == nil)
+		return;
+	ctl := sys->open("/net/"+ethername+"/clone", Sys->OWRITE);
+	if(ctl == nil){
+		sys->print("init: can't open %s's clone: %r\n", ethername);
+		return;
+	}
+	b := array[1024] of byte;
+	n := sys->read(fd, b, len b);
+	if(n <= 0)
+		return;
+	for(i := 0; i < n;){
+		for(e := i; e < n && b[e] != byte '\n'; e++)
+			;
+		s := string b[i:e];
+		if(sys->fprint(ctl, "%s", s) < 0)
+			sys->print("init: ctl write to %s: %s: %r\n", ethername, s);
+		i = e+1;
+	}
+}
+
+donebind := 0;
+
+#
+# set up network mount
+#
+netfs(mountpt: string): int
+{
+	sys->print("bootp ...");
+
+	fd: ref Sys->FD;
+	if(!donebind){
+		fd = sys->open("/net/ipifc/clone", sys->OWRITE);
+		if(fd == nil) {
+			sys->print("init: open /net/ipifc/clone: %r\n");
+			return -1;
+		}
+		if(sys->fprint(fd, "bind ether %s", ethername) < 0) {
+			sys->print("could not bind ether0 interface: %r\n");
+			return -1;
+		}
+		donebind = 1;
+	}else{
+		fd = sys->open("/net/ipifc/0/ctl", Sys->OWRITE);
+		if(fd == nil){
+			sys->print("init: can't reopen /net/ipifc/0/ctl: %r\n");
+			return -1;
+		}
+	}
+	if ((ip := rf("/nvfs/ip", nil)) != nil || (ip = rf("#e/myip", nil)) != nil) {
+		sys->print("**using %s\n", ip);
+		sys->fprint(fd, "bind ether /net/ether0");
+		sys->fprint(fd, "add %s %s", ip, rf("#e/netmask", nil));
+		gate := rf("#e/gateway", nil);
+		if(gate != nil){
+			rfd := sys->open("/net/iproute", Sys->OWRITE);
+			if(rfd != nil){
+				sys->fprint(rfd, "add 0 0 %s", gate);
+				sys->print("set gateway %s\n", gate);
+			}
+		}
+	} else {
+		{
+			if(sys->fprint(fd, "bootp") < 0)
+				sys->print("could not bootp: %r\n");
+		} exception e {
+			"*" =>
+				sys->print("could not bootp: %s\n", e);
+		}
+	}
+	server := rf("/nvfs/fsip", nil);
+	if(server == nil)
+		server = rf("#e/fsip", nil);
+	if (server != nil) {
+		if (server[len server - 1] == '\n')
+			server = server[:len server - 1];
+		sys->print("/nvfs/fsip: server=%s\n", server);
+	} else
+		server = bootp();
+	if(server == nil || server == "0.0.0.0")
+		return -1;
+
+	net := "tcp";	# how to specify il?
+	svcname := net + "!" + server + "!6666";
+
+	sys->print("dial %s...", svcname);
+
+	(ok, c) := sys->dial(svcname, nil);
+	if(ok < 0){
+		sys->print("can't dial %s: %r\n", svcname);
+		return -1;
+	}
+
+	sys->print("\nConnected ...\n");
+	if(kr != nil){
+		err: string;
+		sys->print("Authenticate ...");
+		ai := kr->readauthinfo("/nvfs/default");
+		if(ai == nil){
+			sys->print("readauthinfo /nvfs/default failed: %r\n");
+			sys->print("trying mount as `nobody'\n");
+		}
+		(c.dfd, err) = auth->client("none", ai, c.dfd);
+		if(c.dfd == nil){
+			sys->print("authentication failed: %s\n", err);
+			return -1;
+		}
+	}
+
+	sys->print("mount %s...", mountpt);
+
+	c.cfd = nil;
+	n := sys->mount(c.dfd, nil, mountpt, sys->MREPL, "");
+	if(n > 0)
+		return 0;
+	if(n < 0)
+		sys->print("%r");
+	return -1;
+}
+
+bootp(): string
+{
+	fd := sys->open("/net/bootp", sys->OREAD);
+	if(fd == nil) {
+		sys->print("init: can't open /net/bootp: %r");
+		return nil;
+	}
+
+	buf := array[Bootpreadlen] of byte;
+	nr := sys->read(fd, buf, len buf);
+	fd = nil;
+	if(nr <= 0) {
+		sys->print("init: read /net/bootp: %r");
+		return nil;
+	}
+
+	(ntok, ls) := sys->tokenize(string buf, " \t\n");
+	while(ls != nil) {
+		if(hd ls == "fsip"){
+			ls = tl ls;
+			break;
+		}
+		ls = tl ls;
+	}
+	if(ls == nil) {
+		sys->print("init: server address not in bootp read");
+		return nil;
+	}
+
+	srv := hd ls;
+
+	sys->print("%s\n", srv);
+
+	return srv;
+}
+	
+username(def: string): string
+{
+	return rf("/nvfs/user", def);
+}
+
+userok(user: string): int
+{
+	(ok, d) := sys->stat("/usr/"+user);
+	return ok >= 0 && (d.mode & Sys->DMDIR) != 0;
+}
+
+rf(file: string, default: string): string
+{
+	fd := sys->open(file, Sys->OREAD);
+	if(fd != nil){
+		buf := array[128] of byte;
+		nr := sys->read(fd, buf, len buf);
+		if(nr > 0)
+			return string buf[0:nr];
+	}
+	return default;
+}
--- /dev/null
+++ b/os/init/jsinit.b
@@ -1,0 +1,213 @@
+implement Init;
+#
+# init program for standalone wm using TK
+#
+include "sys.m";
+sys: Sys;
+	FD, Connection, sprint, Dir: import sys;
+	print, fprint, open, bind, mount, dial, sleep, read: import sys;
+
+include "draw.m";
+	draw: Draw;
+	Context: import draw;
+
+include "keyring.m";
+	kr: Keyring;
+
+include "security.m";
+	auth: Auth;
+
+Init: module
+{
+	init:	fn();
+};
+
+Logon: module
+{
+	init:	fn(ctxt: ref Context, argv: list of string);
+};
+
+rootfs(server: string): int
+{
+	ok, n: int;
+	c: Connection;
+
+	(ok, c) = dial("tcp!" + server + "!6666", nil);
+	if(ok < 0)
+		return -1;
+
+	sys->print("Connected ...");
+	if(kr != nil && auth != nil){
+		err: string;
+		sys->print("Authenticate ...");
+		ai := kr->readauthinfo("/nvfs/default");
+		if(ai == nil){
+			sys->print("readauthinfo /nvfs/default failed: %r\n");
+			sys->print("trying mount as `nobody'\n");
+		}
+		(c.dfd, err) = auth->client("none", ai, c.dfd);
+		if(c.dfd == nil){
+			sys->print("authentication failed: %s\n", err);
+			return -1;
+		}
+	}
+
+	sys->print("mount ...");
+
+	c.cfd = nil;
+	n = mount(c.dfd, nil, "/", sys->MREPL, "");
+	if(n > 0)
+		return 0;
+	return -1;
+}
+
+Bootpreadlen: con 128;
+
+init()
+{
+	spec: string;
+
+	sys = load Sys Sys->PATH;
+	kr = load Keyring Keyring->PATH;
+	auth = load Auth Auth->PATH;
+	if(auth != nil)
+		auth->init();
+
+	sys->print("**\n** Inferno\n** Vita Nuova\n**\n");
+
+	sys->print("Setup boot net services ...\n");
+	
+	#
+	# Setup what we need to call a server and
+	# Authenticate
+	#
+	bind("#l", "/net", sys->MREPL);
+	bind("#I", "/net", sys->MAFTER);
+	bind("#c", "/dev", sys->MAFTER);
+	bind("#r", "/dev", sys->MAFTER);
+	nvramfd := sys->open("#r/nvram", sys->ORDWR);
+	if(nvramfd != nil){
+		spec = "#Fnvram";
+		if(bind(spec, "/nvfs", sys->MAFTER) < 0)
+			print("init: bind %s: %r\n", spec);
+		nvramfd = nil;
+	}
+
+	setsysname();
+
+	sys->print("bootp...");
+
+	fd := open("/net/ipifc/clone", sys->OWRITE);
+	if(fd == nil) {
+		print("init: open /net/ipifc/clone: %r\n");
+		exit;
+	}
+	cfg := array of byte "bind ether ether";
+	if(sys->write(fd, cfg, len cfg) != len cfg) {
+		sys->print("could not bind interface: %r\n");
+		exit;
+	}
+	cfg = array of byte "bootp";
+	if(sys->write(fd, cfg, len cfg) != len cfg) {
+		sys->print("could not bootp: %r\n");
+		exit;
+	}
+
+	fd = open("/net/bootp", sys->OREAD);
+	if(fd == nil) {
+		print("init: open /net/bootp: %r");
+		exit;
+	}
+
+	buf := array[Bootpreadlen] of byte;
+	nr := read(fd, buf, len buf);
+	fd = nil;
+	if(nr <= 0) {
+		print("init: read /net/bootp: %r");
+		exit;
+	}
+
+	(ntok, ls) := sys->tokenize(string buf, " \t\n");
+	while(ls != nil) {
+		if(hd ls == "fsip"){
+			ls = tl ls;
+			break;
+		}
+		ls = tl ls;
+	}
+	if(ls == nil) {
+		print("init: server address not in bootp read");
+		exit;
+	}
+
+	srv := hd ls;
+	sys->print("server %s\nConnect ...\n", srv);
+
+	while(rootfs(srv) < 0)
+		sleep(1000);
+
+	sys->print("done\n");
+
+	#
+	# default namespace
+	#
+	bind("#c", "/dev", sys->MBEFORE);		# console
+	bind("#r", "/dev", sys->MAFTER);
+	if(spec != nil)
+		bind(spec, "/nvfs", sys->MBEFORE|sys->MCREATE);	# our keys
+	bind("#l", "/net", sys->MBEFORE);		# ethernet
+	bind("#I", "/net", sys->MBEFORE);		# TCP/IP
+	bind("#p", "/prog", sys->MREPL);		# prog device
+	sys->bind("#d", "/fd", Sys->MREPL);
+
+	sys->print("clock...\n");
+	setclock();
+
+	sys->print("logon...\n");
+
+	logon := load Logon "/dis/wm/logon.dis";
+	if(logon == nil) {
+		print("init: load /dis/wm/logon.dis: %r");
+		exit;
+	}
+	dc: ref Context;
+	spawn logon->init(dc, nil);
+}
+
+setclock()
+{
+	(ok, dir) := sys->stat("/");
+	if (ok < 0) {
+		print("init: stat /: %r");
+		return;
+	}
+
+	fd := sys->open("/dev/time", sys->OWRITE);
+	if (fd == nil) {
+		print("init: open /dev/time: %r");
+		return;
+	}
+
+	# Time is kept as microsecs, atime is in secs
+	b := array of byte sprint("%d000000", dir.atime);
+	if (sys->write(fd, b, len b) != len b)
+		print("init: write /dev/time: %r");
+}
+
+#
+# Set system name from nvram
+#
+setsysname()
+{
+	fd := open("/nvfs/ID", sys->OREAD);
+	if(fd == nil)
+		return;
+	fds := open("/dev/sysname", sys->OWRITE);
+	if(fds == nil)
+		return;
+	buf := array[128] of byte;
+	nr := sys->read(fd, buf, len buf);
+	if(nr <= 0)
+		return;
+	sys->write(fds, buf, nr);
+}
--- /dev/null
+++ b/os/init/mkfile
@@ -1,0 +1,27 @@
+<../../mkconfig
+
+OBJ=\
+	wminit.dis\
+
+all:V:	$OBJ
+install:V: all
+installall:V: all
+
+clean nuke:V:
+	rm -f *.dis *.sbl
+
+INCLD=\
+	-I$ROOT/module\
+
+%.dis:	%.b
+	limbo $INCLD -gw $stem.b
+
+%.s:	%.b
+	limbo $INCLD -w -G -S $stem.b
+
+ir%.dis: ../../appl/lib/ir%.b
+	limbo $INCLD -gw $prereq
+
+ir%.s: ../../appl/lib/ir%.b
+	limbo $INCLD -w -G -S $prereq
+
--- /dev/null
+++ b/os/init/mpcinit.b
@@ -1,0 +1,383 @@
+implement Init;
+#
+# init program for Motorola 800 series (serial console only)
+#
+include "sys.m";
+	sys: Sys;
+
+include "draw.m";
+	draw: Draw;
+
+include "keyring.m";
+	kr: Keyring;
+
+include "security.m";
+	auth: Auth;
+
+Init: module
+{
+	init:	fn();
+};
+
+Shell: module
+{
+	init:	fn(ctxt: ref Draw->Context, argv: list of string);
+};
+
+Bootpreadlen: con 128;
+
+# option switches
+UseLocalFS: con 1<<0;
+EtherBoot: con 1<<1;
+Prompting: con 1<<3;
+
+lfs(): int
+{
+	if(!ftlinit("#F/flash/flash", 1024*1024, 1024*1024))
+		return -1;
+	if(mountkfs("#X/ftldata", "main", "flash") < 0)
+		return -1;
+	if(sys->bind("#Kmain", "/n/local", sys->MREPL) < 0){
+		sys->print("can't bind #Kmain to /n/local: %r\n");
+		return -1;
+	}
+	if(sys->bind("/n/local", "/", Sys->MCREATE|Sys->MREPL) < 0){
+		sys->print("can't bind /n/local after /: %r\n");
+		return -1;
+	}
+	return 0;
+}
+
+donebind := 0;
+
+#
+# set up network mount
+#
+netfs(mountpt: string): int
+{
+	sys->print("bootp ...");
+
+	fd: ref Sys->FD;
+	if(!donebind){
+		fd = sys->open("/net/ipifc/clone", sys->OWRITE);
+		if(fd == nil) {
+			sys->print("init: open /net/ipifc/clone: %r\n");
+			return -1;
+		}
+		if(sys->fprint(fd, "bind ether %s", "/net/ether0") < 0) {
+			sys->print("could not bind ether0 interface: %r\n");
+			return -1;
+		}
+		donebind = 1;
+	}else{
+		fd = sys->open("/net/ipifc/0/ctl", Sys->OWRITE);
+		if(fd == nil){
+			sys->print("init: can't reopen /net/ipifc/0/ctl: %r\n");
+			return -1;
+		}
+	}
+	if(sys->fprint(fd, "bootp") < 0){
+		sys->print("init: bootp failed: %r\n");
+		return -1;
+	}
+
+	server := bootp();
+	if(server == nil)
+		return -1;
+
+	net := "tcp";	# how to specify il?
+	svcname := net + "!" + server + "!6666";
+
+	sys->print("dial %s...", svcname);
+
+	(ok, c) := sys->dial(svcname, nil);
+	if(ok < 0){
+		sys->print("can't dial %s: %r\n", svcname);
+		return -1;
+	}
+
+	sys->print("\nConnected ...\n");
+	if(kr != nil){
+		err: string;
+		sys->print("Authenticate ...");
+		ai := kr->readauthinfo("/nvfs/default");
+		if(ai == nil){
+			sys->print("readauthinfo /nvfs/default failed: %r\n");
+			sys->print("trying mount as `nobody'\n");
+		}
+		(c.dfd, err) = auth->client("none", ai, c.dfd);
+		if(c.dfd == nil){
+			sys->print("authentication failed: %s\n", err);
+			return -1;
+		}
+	}
+
+	sys->print("mount %s...", mountpt);
+
+	c.cfd = nil;
+	n := sys->mount(c.dfd, nil, mountpt, sys->MREPL, "");
+	if(n > 0)
+		return 0;
+	if(n < 0)
+		sys->print("%r");
+	return -1;
+}
+
+init()
+{
+	sys = load Sys Sys->PATH;
+	kr = load Keyring Keyring->PATH;
+	auth = load Auth Auth->PATH;
+	if(auth != nil)
+		auth->init();
+
+	sys->print("**\n** Inferno\n** Vita Nuova\n**\n");
+
+	optsw := options();
+	sys->print("Switch options: 0x%ux\n", optsw);
+
+	#
+	# Setup what we need to call a server and
+	# Authenticate
+	#
+	sys->bind("#l", "/net", sys->MREPL);
+	sys->bind("#I", "/net", sys->MAFTER);
+	sys->bind("#c", "/dev", sys->MAFTER);
+
+	fsready := 0;
+	mountpt := "/";
+	usertc := 0;
+
+	if((optsw & Prompting) == 0){
+		if(optsw & UseLocalFS){
+			sys->print("Option: use local file system\n");
+			if(lfs() == 0){
+				fsready = 1;
+				mountpt = "/n/remote";
+			}
+		}
+
+		if(optsw & EtherBoot){
+			sys->print("Attempting remote mount\n");
+			if(netfs(mountpt) == 0)
+				fsready = 1;
+		}
+	}
+
+	if(fsready == 0){
+
+		sys->print("\n\n");
+
+		stdin := sys->fildes(0);
+		buf := array[128] of byte;
+		sources := "fs" :: "net" :: nil;
+
+		loop: for(;;) {
+			sys->print("root from (");
+			cm := "";
+			for(l := sources; l != nil; l = tl l){
+				sys->print("%s%s", cm, hd l);
+				cm = ",";
+			}
+			sys->print(")[%s] ", hd sources);
+
+			n := sys->read(stdin, buf, len buf);
+			if(n <= 0)
+				continue;
+			if(buf[n-1] == byte '\n')
+				n--;
+
+			(nil, choice) := sys->tokenize(string buf[0:n], "\t ");
+
+			if(choice == nil)
+				choice = sources;
+			opt := hd choice;
+			case opt {
+			* =>
+				sys->print("\ninvalid boot option: '%s'\n", opt);
+				break;
+			"fs" or "" =>
+				if(lfs() == 0){
+					usertc = 1;
+					break loop;
+				}
+			"net" =>
+				if(netfs("/") == 0)
+					break loop;
+			}
+		}
+	}
+
+	#
+	# default namespace
+	#
+	sys->unmount(nil, "/dev");
+	sys->bind("#c", "/dev", sys->MBEFORE);			# console
+	sys->bind("#l", "/net", sys->MBEFORE);		# ethernet
+	sys->bind("#I", "/net", sys->MBEFORE);		# TCP/IP
+	sys->bind("#p", "/prog", sys->MREPL);		# prog device
+	sys->bind("#d", "/fd", Sys->MREPL);
+
+	setsysname();
+
+	sys->print("clock...\n");
+	setclock(usertc, mountpt);
+
+	sys->print("Console...\n");
+
+	shell := load Shell "/dis/sh.dis";
+	if(shell == nil) {
+		sys->print("init: load /dis/sh.dis: %r");
+		exit;
+	}
+	dc: ref Draw->Context;
+	shell->init(dc, nil);
+}
+
+setclock(usertc: int, timedir: string)
+{
+	now := 0;
+	if(usertc){
+		fd := sys->open("#r/rtc", Sys->OREAD);
+		if(fd != nil){
+			b := array[64] of byte;
+			n := sys->read(fd, b, len b-1);
+			if(n > 0){
+				b[n] = byte 0;
+				now = int string b;
+				if(now <= 16r20000000)
+					now = 0;	# rtc itself is not initialised
+			}
+		}
+	}
+	if(now == 0){
+		(ok, dir) := sys->stat(timedir);
+		if (ok < 0) {
+			sys->print("init: stat %s: %r", timedir);
+			return;
+		}
+		now = dir.atime;
+	}
+	fd := sys->open("/dev/time", sys->OWRITE);
+	if (fd == nil) {
+		sys->print("init: can't open /dev/time: %r");
+		return;
+	}
+
+	# Time is kept as microsecs, atime is in secs
+	b := array of byte sys->sprint("%ud000000", now);
+	if (sys->write(fd, b, len b) != len b)
+		sys->print("init: can't write /dev/time: %r");
+}
+
+#
+# Set system name from nvram
+#
+setsysname()
+{
+	fd := sys->open("/nvfs/ID", sys->OREAD);
+	if(fd == nil)
+		return;
+	fds := sys->open("/dev/sysname", sys->OWRITE);
+	if(fds == nil)
+		return;
+	buf := array[128] of byte;
+	nr := sys->read(fd, buf, len buf);
+	if(nr <= 0)
+		return;
+	sys->write(fds, buf, nr);
+}
+
+#
+# fetch options from switch DS2
+#
+options(): int
+{
+	fd := sys->open("#r/switch", Sys->OREAD);
+	if(fd == nil){
+		sys->print("can't open #r/switch: %r\n");
+		return 0;
+	}
+	b := array[20] of byte;
+	n := sys->read(fd, b, len b);
+	s := string b[0:n];
+	return int s;
+}
+
+bootp(): string
+{
+	fd := sys->open("/net/bootp", sys->OREAD);
+	if(fd == nil) {
+		sys->print("init: can't open /net/bootp: %r");
+		return nil;
+	}
+
+	buf := array[Bootpreadlen] of byte;
+	nr := sys->read(fd, buf, len buf);
+	fd = nil;
+	if(nr <= 0) {
+		sys->print("init: read /net/bootp: %r");
+		return nil;
+	}
+
+	(ntok, ls) := sys->tokenize(string buf, " \t\n");
+	while(ls != nil) {
+		if(hd ls == "fsip"){
+			ls = tl ls;
+			break;
+		}
+		ls = tl ls;
+	}
+	if(ls == nil) {
+		sys->print("init: server address not in bootp read");
+		return nil;
+	}
+
+	srv := hd ls;
+
+	sys->print("%s\n", srv);
+
+	return srv;
+}
+
+#
+# set up flash translation layer
+#
+ftldone := 0;
+
+ftlinit(flashmem: string, offset: int, length: int): int
+{
+	if(ftldone)
+		return 1;
+	sys->print("Set flash translation of %s at offset %d (%d bytes)\n", flashmem, offset, length);
+	fd := sys->open("#X/ftlctl", Sys->OWRITE);
+	if(fd == nil){
+		sys->print("can't open #X/ftlctl: %r\n");
+		return 0;
+	}
+	if(sys->fprint(fd, "init %s %ud %ud", flashmem, offset, length) <= 0){
+		sys->print("can't init flash translation: %r");
+		return 0;
+	}
+	ftldone = 1;
+	return 1;
+}
+
+#
+# Mount kfs filesystem
+#
+mountkfs(devname: string, fsname: string, options: string): int
+{
+	fd := sys->open("#Kcons/kfsctl", sys->OWRITE);
+	if(fd == nil) {
+		sys->print("could not open #Kcons/kfsctl: %r\n");
+		return -1;
+	}
+	if(sys->fprint(fd, "filsys %s %s %s", fsname, devname, options) <= 0){
+		sys->print("could not write #Kcons/kfsctl: %r\n");
+		return -1;
+	}
+	if(options == "ro")
+		sys->fprint(fd, "cons flashwrite");
+	return 0;
+}
--- /dev/null
+++ b/os/init/pcdemo.b
@@ -1,0 +1,273 @@
+#
+#	Init shell ``with the kitchen sink'', for development purposes.
+#
+implement InitShell;
+
+include "sys.m";
+include "draw.m";
+
+sys: Sys;
+FD, Connection, sprint, Dir: import sys;
+print, fprint, open, bind, mount, dial, sleep, read: import sys;
+
+stderr:	ref sys->FD;						# standard error FD
+
+InitShell: module
+{
+	init:	fn(nil: ref Draw->Context, nil: list of string);
+};
+
+Sh: module
+{
+	init:	fn(ctxt: ref Draw->Context, argv: list of string);
+};
+
+init(nil: ref Draw->Context, nil: list of string)
+{
+	sys = load Sys Sys->PATH;
+	stderr = sys->fildes(2);
+
+	sys->print("Welcome to Inferno...\n");
+
+	sys->pctl(Sys->NEWNS, nil);
+	if (imount("/n/remote")) {
+		bind("/n/remote", "/", sys->MAFTER);
+		bind("/n/remote/dis", "/dis", sys->MBEFORE);
+		mountkfs("#W/flash0fs", "fs", "/n/local", sys->MREPL);
+	}
+	else {
+		# bind("#U/pcdemo", "/", sys->MREPL);
+		# mountkfs("#U/pcdemo.kfs", "fs", "/", sys->MBEFORE);
+		# bind("#U/pcdemo/usr", "/usr", sys->MAFTER);
+		mountkfs("#R/ramdisk", "fs", "/", sys->MBEFORE);
+		bind("/services", "/data", sys->MREPL|sys->MCREATE);
+	}
+
+	namespace();
+	srv();
+
+	if (1) {
+		bind("/icons/oldlogon.bit", "/icons/logon.bit", sys->MREPL);
+		bind("/icons/tk/oldinferno.bit", "/icons/tk/inferno.bit", sys->MREPL);
+	}
+
+	sys->print("starting shell (type wm/logon or wm/wmcp)\n");
+	shell := sysenv("shell");
+	if (shell == nil)
+		shell = "/dis/sh.dis";
+	sh := load Sh shell;
+	spawn sh->init(nil, nil);
+}
+
+namespace()
+{
+	# Bind anything useful we can get our hands on.  Ignore errors.
+	sys->print("namespace...\n");
+	sys->bind("#I", "/net", sys->MAFTER);	# IP
+	sys->bind("#I1", "/net.alt", sys->MREPL);	# second IP for PPP tests
+	sys->bind("#p", "/prog", sys->MREPL);	# prog device
+	sys->bind("#d", "/fd", Sys->MREPL);
+	sys->bind("#i", "/dev", sys->MREPL); 	# draw device
+	sys->bind("#t", "/dev", sys->MAFTER);	# serial line
+	sys->bind("#c", "/dev", sys->MAFTER); 	# console device
+	sys->bind("#W", "/dev", sys->MAFTER);	# Flash
+	sys->bind("#O", "/dev", sys->MAFTER);	# Modem
+	sys->bind("#T", "/dev", sys->MAFTER);	# Touchscreen
+	sys->bind("#H", "/dev", sys->MAFTER);	# Ata disk device
+	sys->bind("#b", "/dev", sys->MAFTER);	# debug device
+	sys->bind("#c", "/chan", sys->MREPL);
+	sys->bind("/data", "/usr/inferno", sys->MREPL|sys->MCREATE);
+	sys->bind("/data", "/usr/charon", sys->MREPL|sys->MCREATE);
+	sys->bind("/data", "/usr/shaggy", sys->MREPL|sys->MCREATE);
+}
+
+mountkfs(devname: string, fsname: string, where: string, flags: int): int
+{
+	sys->print("mount kfs...\n");
+	fd := sys->open("#Kcons/kfsctl", sys->OWRITE);
+	if (fd == nil) {
+		sys->fprint(stderr, "could not open #Kcons/kfsctl: %r\n");
+		return 0;
+	}
+	kfsopt := "";
+	kfsrw := sysenv("kfsrw");
+#	if (kfsrw != "1")
+#		kfsopt = " ronly";
+	b := array of byte ("filsys " + fsname + " " + devname + kfsopt);
+	if (sys->write(fd, b, len b) < 0) {
+		sys->fprint(stderr, "could not write #Kcons/kfsctl: %r\n");
+		return 0;
+	}
+	if (sys->bind("#K" + fsname, where, flags) < 0) {
+		sys->fprint(stderr, "could not bind %s to %s: %r\n", "#K" + fsname, where);
+		return 0;
+	}
+	return 1;
+}
+
+dialfs(server: string, where: string): int
+{
+	ok, n: int;
+	c: Connection;
+
+	(ok, c) = dial("tcp!" + server + "!6666", nil);
+	if(ok < 0)
+		return 0;
+
+	sys->print("mount...");
+
+	c.cfd = nil;
+	n = mount(c.dfd, where, sys->MREPL, "");
+	if(n > 0)
+		return 1;
+	sys->print("mount failed: %r\n");
+	return 0;
+}
+
+Bootpreadlen: con 128;
+
+imount(where: string): int
+{
+	sys->print("bootp...");
+	if (sys->bind("#I", "/net", sys->MREPL) < 0) {
+		sys->fprint(stderr, "could not bind ip device: %r\n");
+		return 0;
+	}
+	if (sys->bind("#l", "/net", sys->MAFTER) < 0) {
+		sys->fprint(stderr, "could not bind ether device: %r\n");
+		return 0;
+	}
+
+	fd := sys->open("/net/ipifc/clone", sys->OWRITE);
+	if(fd == nil) {
+		sys->print("init: open /net/ipifc: %r");
+		return 0;
+	}
+
+	cfg := array of byte "bind ether ether0";
+	if(sys->write(fd, cfg, len cfg) != len cfg) {
+		sys->fprint(stderr, "could not bind interface: %r\n");
+		return 0;
+	}
+	cfg = array of byte "bootp";
+	if(sys->write(fd, cfg, len cfg) != len cfg) {
+		sys->fprint(stderr, "could not bootp: %r\n");
+		return 0;
+	}
+
+	bfd := sys->open("/net/bootp", sys->OREAD);
+	if(bfd == nil) {
+		sys->print("init: can't open /net/bootp: %r");
+		return 0;
+	}
+
+	buf := array[Bootpreadlen] of byte;
+	nr := sys->read(bfd, buf, len buf);
+	bfd = nil;
+	if(nr <= 0) {
+		sys->print("init: read /net/bootp: %r");
+		return 0;
+	}
+
+	(ntok, ls) := sys->tokenize(string buf, " \t\n");
+	while(ls != nil) {
+		if(hd ls == "fsip"){
+			ls = tl ls;
+			break;
+		}
+		ls = tl ls;
+	}
+	if(ls == nil) {
+		sys->print("init: server address not in bootp read");
+		return 0;
+	}
+
+	server := hd ls;
+	sys->print("imount: server %s\n", server);
+
+	return dialfs(server, where);
+}
+
+srv()
+{
+	remotedebug := sysenv("remotedebug");
+	if(remotedebug != "1")
+		return;
+	remotespeed := sysenv("remotespeed");
+	if (remotespeed == nil)
+		remotespeed = "38400";
+
+	sys->print("srv...");
+	if(echoto("#t/eia0ctl", "b" + remotespeed) < 0)
+		return;
+
+	fd := sys->open("/dev/eia0", Sys->ORDWR);
+	if (fd == nil) {
+		sys->print("eia data open: %r\n");
+		return;
+	}
+	if (sys->export(fd, Sys->EXPASYNC) < 0) {
+		sys->print("export: %r\n");
+		return;
+	}
+	sys->print("ok\n");
+}
+
+sysenv(param: string): string
+{
+	fd := sys->open("#c/sysenv", sys->OREAD);
+	if (fd == nil)
+		return(nil);
+	buf := array[4096] of byte;
+	nb := sys->read(fd, buf, len buf);
+	(nfl,fl) := sys->tokenize(string buf, "\n");
+	while (fl != nil) {
+		pair := hd fl;
+		(npl, pl) := sys->tokenize(pair, "=");
+		if (npl > 1) {
+			if ((hd pl) == param)
+				return hd tl pl;
+		}
+		fl = tl fl;
+	}
+	return nil;
+}
+
+echoto(fname, str: string): int
+{
+	fd := sys->open(fname, Sys->OWRITE);
+	if(fd == nil) {
+		sys->print("%s: %r\n", fname);
+		return -1;
+	}
+	x := array of byte str;
+	if(sys->write(fd, x, len x) == -1) {
+		sys->print("write: %r\n");
+		return -1;
+	}
+	return 0;
+}
+
+hang()
+{
+	c := chan of int;
+	<- c;
+}
+
+#
+# Set system name from nvram
+#
+setsysname()
+{
+	fd := open("/nvfs/ID", sys->OREAD);
+	if(fd == nil)
+		return;
+	fds := open("/dev/sysname", sys->OWRITE);
+	if(fds == nil)
+		return;
+	buf := array[128] of byte;
+	nr := sys->read(fd, buf, len buf);
+	if(nr <= 0)
+		return;
+	sys->write(fds, buf, nr);
+}
--- /dev/null
+++ b/os/init/pcinit.b
@@ -1,0 +1,405 @@
+implement Init;
+
+include "sys.m";
+	sys: Sys;
+
+include "draw.m";
+	draw: Draw;
+
+include "keyring.m";
+	kr: Keyring;
+
+include "security.m";
+	auth: Auth;
+
+include "styx.m";
+
+	dosfs : Dosfs;
+
+PROMPT:		con 1;		# boot from prompt?  (0 means boot from fs)
+SHELL:		con 0;		# Start a Shell, not Logon
+INIT: 		con "/init";	# file to read init commands from
+
+startip := 0;
+
+Bootpreadlen:	con 128;
+
+Init: module
+{
+	init:	fn();
+};
+
+Logon: module
+{
+	init:	fn(ctxt: ref Draw->Context, argv: list of string);
+};
+
+Sh: module
+{
+	init:	fn(ctxt: ref Draw->Context, argv: list of string);
+};
+
+lfs(dev: string): int
+{
+	(ok, dir) := sys->stat(dev);
+	if (ok < 0) {
+		sys->print("init: stat %s: %r\n", dev);
+		return -1;
+	}
+	pipefd := array[2] of ref Sys->FD;
+ 	dosfs = load Dosfs "#/./dosfs";
+	if(dosfs == nil) {
+		sys->fprint(sys->fildes(2),"load #/.dosfs: %r\n");
+		return -1;
+	}
+
+	dosfs->init(dev, "", 0);
+	if(sys->pipe(pipefd) < 0){
+		sys->fprint(sys->fildes(2),"pipe %r\n");
+		exit;
+	}
+	spawn dosfs->dossrv(pipefd[1]);
+
+	n := sys->mount(pipefd[0], "/", sys->MREPL|sys->MCREATE, "");
+	if(n<0) {
+		sys->print("couldn't mount. %r\n");
+		return -1;
+	}
+
+	dosfs->setup();
+
+	sys->print("mounted %s at /\n", dev);
+
+	return 0;
+}
+
+ipinit()
+{
+	fd := sys->open("/nvfs/IP", sys->OREAD);
+	if(fd == nil)
+		return;
+
+	buf := array[128] of byte;
+	nr := sys->read(fd, buf, len buf);
+	if(nr <= 0)
+		return;
+
+	cfd := sys->open("/net/ipifc/clone", sys->ORDWR);
+	if(cfd == nil) {
+		sys->print("init: open /net/ipifc/clone: %r");
+		exit;
+	}
+
+	sys->fprint(cfd, "bind ether ether0");
+	sys->fprint(cfd, "%s", string buf[0:nr]);
+}
+
+netfs(): int
+{
+	cfd := sys->open("/net/ipifc/clone", sys->ORDWR);
+	if(cfd == nil) {
+		sys->print("init: open /net/ipifc/clone: %r");
+		exit;
+	}
+	sys->fprint(cfd, "bind ether ether0");
+
+	server:= bootp(cfd);
+	sys->print("dial...");
+	(ok, c) := sys->dial("tcp!" + server + "!6666", nil);
+	if(ok < 0)
+		return -1;
+	
+	if(kr != nil && auth != nil){
+		err: string;
+		sys->print("Authenticate ...");
+		ai := kr->readauthinfo("/nvfs/default");
+		if(ai == nil){
+			sys->print("readauthinfo /nvfs/default failed: %r\n");
+			sys->print("trying mount as `nobody'\n");
+		}
+		(c.dfd, err) = auth->client("none", ai, c.dfd);
+		if(c.dfd == nil){
+			sys->print("authentication failed: %s\n", err);
+			return -1;
+		}
+	}
+	
+	sys->print("mount ...");
+	
+	c.cfd = nil;
+	n := sys->mount(c.dfd, "/", sys->MREPL, "");
+	if(n > 0)
+		return 0;
+
+	return -1;
+}
+
+init()
+{
+	spec: string;
+
+	sys = load Sys Sys->PATH;
+	kr = load Keyring Keyring->PATH;
+	auth = load Auth Auth->PATH;
+	if(auth != nil)
+		auth->init();
+
+	sys->print("**\n** Inferno\n** Vita Nuova\n**\n\n\n");
+
+	#
+	# Setup what we need to call a server and
+	# Authenticate
+	#
+	sys->bind("#l", "/net", sys->MREPL);
+	sys->bind("#I", "/net", sys->MAFTER);
+	sys->bind("#c", "/dev", sys->MAFTER);
+
+	sys->print("Non-volatile ram read ...\n");
+
+	nvramfd := sys->open("#H/hd0nvram", sys->ORDWR);
+	if(nvramfd != nil) {
+		spec = "#Fhd0nvram";
+		if(sys->bind(spec, "/nvfs", sys->MAFTER) < 0)
+			sys->print("init: bind %s: %r\n", spec);
+		sys->print("mounted tinyfs");
+		nvramfd = nil;
+	}
+
+	sys->print("\n\n");
+
+	if(!PROMPT) {
+		if(lfs("#H/hd0fs") == 0)
+			startip = 1;
+		else
+			bootfrom();
+	} else
+		bootfrom();
+
+	sys->bind("#l", "/net", sys->MBEFORE);
+	sys->bind("#I", "/net", sys->MBEFORE);
+	sys->bind("#c", "/dev", sys->MBEFORE);
+
+	if(startip)
+		ipinit();
+
+	setsysname();
+
+	sys->print("clock...\n");
+	setclock();
+
+	if(SHELL) {
+		sys->print("shell...\n");
+
+		logon := load Logon "/dis/sh.dis";
+		if(logon == nil) {
+			sys->print("init: load /dis/wm/logon.dis: %r");
+			exit;
+		}
+		dc: ref Draw->Context;
+		spawn logon->init(dc, nil);
+		exit;
+	}
+
+	runprogs();
+}
+
+bootfrom()
+{
+	buf := array[128] of byte;
+	stdin := sys->fildes(0);
+
+	fsdev := "#H/hd0disk";
+
+	loop: for(;;) {
+		sys->print("boot from [fs, net]: ");
+
+		n := sys->read(stdin, buf, len buf);
+		if(n <= 0)
+			continue;
+		if(buf[n-1] == byte '\n')
+			n--;
+
+		(nil, choice) := sys->tokenize(string buf[:n], "\t ");
+		if(choice == nil)
+			continue;
+
+		opt := hd choice;
+		choice = tl choice;
+
+		case opt {
+		* =>
+			sys->print("\ninvalid boot option: '%s'\n", opt);
+			break;
+		"fs" or "" =>
+			if(choice != nil)
+				fsdev = hd choice;
+			if(lfs(fsdev) == 0) {
+				startip = 1;
+				break loop;
+			}
+		"net" =>
+			if(netfs() == 0)
+				break loop;
+		}
+	}
+}
+
+runprogs()
+{
+	fd:= sys->open(INIT, Sys->OREAD);
+	if(fd == nil) {
+		sys->print("open %s: %r\n", INIT);
+		return;
+	}
+
+	dc := ref Draw->Context;
+	dc.ctomux = chan of int;
+
+	for(l:=1;;l++) {
+		(e, line):= getline(fd);
+		if(e != nil) {
+			sys->print(INIT+":%d: %s\n", l, e);
+			return;
+		}
+		if(line == nil)
+			break;
+		if(line == "\n" || line[0] == '#')
+			continue;
+		if(line[len line-1] == '\n')
+			line = line[:len line-1];
+		(n, f):= sys->tokenize(line, " \t");
+		if(n < 0) {
+			sys->print(INIT+":%d: tokenize: %r\n", l);
+			return;
+		}
+		if(n < 2) {
+			sys->print(INIT+":%d: not enough fields\n", l);
+			continue;
+		}
+		e = run(dc, f);
+		if(e != nil)
+			sys->print(INIT+":%d: %s\n", l, e);
+	}
+}
+
+run(dc: ref Draw->Context, argv: list of string): string
+{
+	c:= hd argv;
+	argv = tl argv;
+	prog:= hd argv;
+	ext:= ".dis";
+	if(prog[len prog-4:] == ".dis")
+		ext = "";
+	sh:= load Sh prog+ext;
+	if(sh == nil)
+		sh = load Sh "/dis/"+prog+ext;
+	if(sh == nil)
+		return sys->sprint("%s: load: %r", prog);
+
+	case c {
+	"run" =>
+		e:= ref Sys->Exception;
+		if(sys->rescue("fail:*", e))
+			return prog+": "+e.name;
+		sh->init(dc, argv);
+		return nil;
+	"spawn" =>
+		spawn sh->init(dc, argv);
+		return nil;
+	}
+	return c+": unknown command";
+}
+
+getline(fd: ref Sys->FD): (string, string)
+{
+	s:= "";
+	buf:= array[1] of byte;
+	for(;;) {
+		n:= sys->read(fd, buf, 1);
+		if(n < 0)
+			return (sys->sprint("getline: read: %r\n"), nil);
+		if(n == 0)
+			return (nil, s);
+		s += string buf;
+		if(buf[0] == byte '\n')
+			return (nil, s);
+	}
+}
+
+setclock()
+{
+	(ok, dir) := sys->stat("/");
+	if (ok < 0) {
+		sys->print("init: stat /: %r");
+		return;
+	}
+
+	fd := sys->open("/dev/time", sys->OWRITE);
+	if (fd == nil) {
+		sys->print("init: open /dev/time: %r\n");
+		return;
+	}
+
+	# Time is kept as microsecs, atime is in secs
+	b := array of byte sys->sprint("%d000000", dir.atime);
+	if (sys->write(fd, b, len b) != len b)
+		sys->print("init: write /dev/time: %r");
+}
+
+#
+# Set system name from nvram
+#
+setsysname()
+{
+	fd := sys->open("/nvfs/ID", sys->OREAD);
+	if(fd == nil)
+		return;
+	fds := sys->open("/dev/sysname", sys->OWRITE);
+	if(fds == nil)
+		return;
+	buf := array[128] of byte;
+	nr := sys->read(fd, buf, len buf);
+	if(nr <= 0)
+		return;
+	sys->write(fds, buf, nr);
+}
+
+bootp(cfd: ref sys->FD): string
+{
+	sys->print("bootp ...");
+
+	sys->fprint(cfd, "bootp");
+
+	fd := sys->open("/net/bootp", sys->OREAD);
+	if(fd == nil) {
+		sys->print("init: open /net/bootp: %r");
+		exit;
+	}
+
+
+	buf := array[Bootpreadlen] of byte;
+	nr := sys->read(fd, buf, len buf);
+	fd = nil;
+	if(nr <= 0) {
+		sys->print("init: read /net/bootp: %r");
+		exit;
+	}
+	(ntok, ls) := sys->tokenize(string buf, " \t\n");
+	while(ls != nil) {
+		if(hd ls == "fsip"){
+			ls = tl ls;
+			break;
+		}
+		ls = tl ls;
+	}
+	if(ls == nil) {
+		sys->print("init: server address not in bootp read");
+		exit;
+	}
+
+	srv := hd ls;
+
+	sys->print("(ip=%s)", srv);
+
+	return srv;
+}
--- /dev/null
+++ b/os/init/reminit.b
@@ -1,0 +1,239 @@
+implement Init;
+#
+# init program for standalone remote pc kernel - for benchmarking
+#
+include "sys.m";
+sys: Sys;
+FD, Connection, sprint, Dir: import sys;
+print, fprint, open, bind, mount, dial, sleep, read, chdir: import sys;
+
+include "draw.m";
+draw: Draw;
+Context: import draw;
+
+include "keyring.m";
+kr: Keyring;
+
+include "security.m";
+auth: Auth;
+
+Init: module
+{
+	init:	fn();
+};
+
+Shell: module
+{
+	init:	fn(ctxt: ref Context, argv: list of string);
+};
+
+remotefs(server: string): int
+{
+	ok, n: int;
+	c: Connection;
+
+	(ok, c) = dial("tcp!" + server + "!6666", nil);
+	if(ok < 0)
+		return -1;
+
+	sys->print("Connected ...\n");
+	if(kr != nil){
+		err: string;
+		sys->print("Authenticate ...");
+		ai := kr->readauthinfo("/nvfs/default");
+		if(ai == nil){
+			sys->print("readauthinfo /nvfs/default failed: %r\n");
+			sys->print("trying mount as `nobody'\n");
+		}
+		(c.dfd, err) = auth->client("none", ai, c.dfd);
+		if(c.dfd == nil){
+			sys->print("authentication failed: %s\n", err);
+			return -1;
+		}
+	}
+
+	sys->print("mount ...\n");
+
+	c.cfd = nil;
+	n = mount(c.dfd, "/n/remote", sys->MREPL, "");
+	if(n > 0)
+		return 0;
+	return -1;
+}
+
+Bootpreadlen: con 128;
+
+bootp(): string
+{
+#
+#	BUG: if bootp fails, can't then use "add ether" correctly
+#
+	fd := open("/net/ipifc", sys->OWRITE);
+	if(fd == nil) {
+		print("init: open /net/ipifc: %r");
+		return nil;
+	}
+	fprint(fd, "bootp /net/ether0");
+
+	fd = open("/net/bootp", sys->OREAD);
+	if(fd == nil) {
+		print("init: open /net/bootp: %r\n");
+		return nil;
+	}
+
+	buf := array[Bootpreadlen] of byte;
+	nr := read(fd, buf, len buf);
+	fd = nil;
+	if(nr > 0) {
+		(ntok, ls) := sys->tokenize(string buf, " \t\n");
+		while(ls != nil) {
+			if(hd ls == "fsip") {
+				ls = tl ls;
+				break;
+			}
+			ls = tl ls;
+		}
+		srv : string;
+		if(ls == nil || ((srv = hd ls) == "0.0.0.0")) {
+			print("init: server address not in bootp read\n");
+			return nil;
+		}
+		return srv;
+	}
+	return nil;
+}
+
+init()
+{
+
+	sys = load Sys Sys->PATH;
+	stdin := sys->fildes(0);
+	kr = load Keyring Keyring->PATH;
+	auth = load Auth Auth->PATH;
+	if(auth != nil)
+		auth->init();
+
+	sys->print("**\n** Inferno\n** Vita Nuova\n**\n");
+
+	sys->print("Setup boot net services ...\n");
+
+	#
+	# Setup what we need to call a server and
+	# Authenticate
+	#
+	sys->print("Bind ethernet ...\n");
+	bind("#l", "/net", sys->MREPL);
+	sys->print("Bind IP ...\n");
+	bind("#I", "/net", sys->MAFTER);
+	sys->print("Bind console ...\n");
+	bind("#c", "/dev", sys->MAFTER);
+
+	setsysname();
+	if (1) {
+		print("bootp ...\n");
+		srv := bootp();
+		if (srv != nil)
+			if (remotefs(srv) < 0)
+				print("No remote filesystem\n");
+			else {
+				print("Remote filesystem mounted\n");
+				bind("/n/remote/dis", "/dis", sys->MBEFORE);
+				bind("/n/remote/dis/lib", "/dis/lib", sys->MBEFORE);
+			}
+	} else {
+		print("Standalone mode\n");
+		fd := open("/net/ipifc", sys->OWRITE);
+		if(fd == nil) {
+			print("init: open /net/ipifc: %r");
+			exit;
+		}
+		fprint(fd, "add ether /net/ether0 %s %s", "200.1.1.60", "255.255.255.0");
+		fd = nil;
+	}
+	#
+	# default namespace
+	#
+	sys->unmount(nil, "/dev");
+	bind("#c", "/dev", sys->MBEFORE);			# console
+	bind("#l", "/net", sys->MBEFORE);		# ethernet
+	bind("#I", "/net", sys->MBEFORE);		# TCP/IP
+	bind("#p", "/prog", sys->MREPL);		# prog device
+	sys->bind("#d", "/fd", Sys->MREPL);
+
+	bind("#T", "/dev", sys->MBEFORE);		# kprof device
+	bind("#x", "/dev", sys->MBEFORE);		# bench device
+
+	print("clock...\n");
+	setclock();
+
+	print("Server...\n");
+	dc: ref Context;
+	cs := load Shell "/dis/ndb/cs.dis";
+	if(cs == nil)
+		print("Server not loaded\n");
+	else
+		cs->init(dc, "cs" :: nil);
+	server := load Shell "/dis/lib/srv.dis";
+	if(server == nil)
+		print("Server not loaded\n");
+	else {
+#		server->init(dc, "srv" :: nil);
+	}
+
+	print("Console...\n");
+sys->chdir("/n/remote/usr/john/appl/bench");
+	shell := load Shell "/dis/sh.dis";
+	if(shell == nil) {
+		print("init: load /dis/sh.dis: %r");
+		exit;
+	}
+	shell->init(dc, nil);
+}
+
+setclock()
+{
+	(ok, dir) := sys->stat("/");
+	if (ok < 0) {
+		print("init: stat /: %r");
+		return;
+	}
+
+	fd := sys->open("/dev/time", sys->OWRITE);
+	if (fd == nil) {
+		print("init: open /dev/time: %r");
+		return;
+	}
+
+	# Time is kept as microsecs, atime is in secs
+	b := array of byte sprint("%d000000", dir.atime);
+	if (sys->write(fd, b, len b) != len b)
+		print("init: write /dev/time: %r");
+}
+
+#
+# Set system name from nvram
+#
+setsysname()
+{
+	fd := open("/nvfs/ID", sys->OREAD);
+	if(fd == nil)
+		return;
+	fds := open("/dev/sysname", sys->OWRITE);
+	if(fds == nil)
+		return;
+	buf := array[128] of byte;
+	nr := sys->read(fd, buf, len buf);
+	if(nr <= 0)
+		return;
+	sys->write(fds, buf, nr);
+}
+
+readline(fd: ref Sys->FD): string
+{
+	l := array [128] of byte;
+	nb := sys->read(fd, l, len l);
+	if(nb <= 1)
+		return "";
+	return string l[0:nb-1];
+}
+
--- /dev/null
+++ b/os/init/rpcginit.b
@@ -1,0 +1,610 @@
+#
+# RPCG RPX-Lite AW
+#
+
+implement Init;
+
+include "sys.m";
+	sys:	Sys;
+
+include "draw.m";
+
+include "keyring.m";
+	kr: Keyring;
+
+include "security.m";
+	auth: Auth;
+
+include "sh.m";
+
+Init: module
+{
+	init:	fn();
+};
+
+Bootpreadlen: con 128;
+
+# standard flash partitions
+
+flashparts := array[] of {
+	# rpcg monitor at 0x300000 to end
+	"add params 0 0x20000",
+	"add boot 0x20000 0x40000",
+	"add kernel 0x40000 0xC0000",
+	"add fs 0xC0000 end",
+};
+
+ethername := "ether0";
+
+#
+# initialise flash translation
+# mount flash file system
+# add devices
+# start a shell or window manager
+#
+
+init()
+{
+	sys = load Sys Sys->PATH;
+	kr = load Keyring Keyring->PATH;
+	auth = load Auth Auth->PATH;
+	if(auth != nil)
+		auth->init();
+
+	sys->bind("/", "/", Sys->MREPL);
+
+	localok := 0;
+	if(lfs() >= 0){
+		# let's just take a closer look
+		sys->bind("/n/local/nvfs", "/nvfs", Sys->MREPL|Sys->MCREATE);
+		(rc, nil) := sys->stat("/n/local/dis/sh.dis");
+		if(rc >= 0)
+			localok = 1;
+		else
+			err("local file system unusable");
+	}
+	netok := sys->bind("#l", "/net", Sys->MREPL) >= 0;
+	if(!netok){
+		netok = sys->bind("#l1", "/net", Sys->MREPL) >= 0;
+		if(netok)
+			ethername = "ether1";
+	}
+	if(netok)
+		configether();
+	dobind("#I", "/net", sys->MAFTER);	# IP
+	dobind("#p", "/prog", sys->MREPL);	# prog
+	sys->bind("#d", "/fd", Sys->MREPL);
+	dobind("#c", "/dev", sys->MREPL); 	# console
+	dobind("#t", "/dev", sys->MAFTER);	# serial line
+	drawok := sys->bind("#i", "/dev", sys->MAFTER) >= 0; 	# draw
+	sys->bind("#m", "/dev", sys->MAFTER);	# pointer
+	sys->bind("#e", "/env", sys->MREPL|sys->MCREATE);	# environment
+	sys->bind("#A", "/dev", Sys->MAFTER);	# optional audio
+	timefile: string;
+	rootsource: string;
+	cfd := sys->open("/dev/consctl", Sys->OWRITE);
+	if(cfd != nil)
+		sys->fprint(cfd, "rawon");
+	for(;;){
+		(rootsource, timefile) = askrootsource(localok, netok);
+		if(rootsource == nil)
+			break;	# internal
+		(rc, nil) := sys->stat(rootsource+"/dis/sh.dis");
+		if(rc < 0)
+			err("%s has no shell");
+		else if(sys->bind(rootsource, "/", Sys->MAFTER) < 0)
+			sys->print("can't bind %s on /: %r\n", rootsource);
+		else{
+			sys->bind(rootsource+"/dis", "/dis", Sys->MBEFORE|Sys->MCREATE);
+			break;
+		}
+	}
+	cfd = nil;
+
+	setsysname("rpxlite");			# set system name
+
+	now := getclock(timefile, rootsource);
+	setclock("/dev/time", now);
+	if(timefile != "#r/rtc")
+		setclock("#r/rtc", now/big 1000000);
+
+	sys->chdir("/");
+	if(netok){
+		start("ndb/dns", nil);
+		start("ndb/cs", nil);
+	}
+	startup := "/nvfs/startup";
+	if(sys->open(startup, Sys->OREAD) != nil){
+		shell := load Command Sh->PATH;
+		if(shell != nil){
+			sys->print("Running %s\n", startup);
+			shell->init(nil, "sh" :: startup :: nil);
+		}
+	}
+	user := username("inferno");
+	(ok, nil) := sys->stat("/dis/wm/wm.dis");
+	if(drawok && ok >= 0)
+		(ok, nil) = sys->stat("/dis/wm/logon.dis");
+	if(drawok && ok >= 0 && userok(user)){
+		wm := load Command "/dis/wm/wm.dis";
+		if(wm != nil){
+			fd := sys->open("/nvfs/user", Sys->OWRITE);
+			if(fd != nil){
+				sys->fprint(fd, "%s", user);
+				fd = nil;
+			}
+			spawn wm->init(nil, list of {"wm/wm", "wm/logon", "-l", "-u", user});
+			exit;
+		}
+		sys->print("init: can't load wm/logon: %r");
+	}
+	sh := load Command Sh->PATH;
+	if(sh == nil){
+		err(sys->sprint("can't load %s: %r", Sh->PATH));
+		hang();
+	}
+	spawn sh->init(nil, "sh" :: nil);
+}
+
+start(cmd: string, args: list of string)
+{
+	disfile := cmd;
+	if(disfile[0] != '/')
+		disfile = "/dis/"+disfile+".dis";
+	(ok, nil) := sys->stat(disfile);
+	if(ok >= 0){
+		dis := load Command disfile;
+		if(dis == nil)
+			sys->print("init: can't load %s: %r\n", disfile);
+		else
+			spawn dis->init(nil, cmd :: args);
+	}
+}
+
+dobind(f, t: string, flags: int)
+{
+	if(sys->bind(f, t, flags) < 0)
+		err(sys->sprint("can't bind %s on %s: %r", f, t));
+}
+
+#
+# Set system name from nvram if possible
+#
+setsysname(def: string)
+{
+	v := array of byte def;
+	fd := sys->open("/nvfs/ID", sys->OREAD);
+	if(fd == nil)
+		fd = sys->open("/env/sysname", sys->OREAD);
+	if(fd != nil){
+		buf := array[Sys->NAMEMAX] of byte;
+		nr := sys->read(fd, buf, len buf);
+		while(nr > 0 && buf[nr-1] == byte '\n')
+			nr--;
+		if(nr > 0)
+			v = buf[0:nr];
+	}
+	fd = sys->open("/dev/sysname", sys->OWRITE);
+	if(fd != nil)
+		sys->write(fd, v, len v);
+}
+
+getclock(timefile: string, timedir: string): big
+{
+	now := big 0;
+	if(timefile != nil){
+		fd := sys->open(timefile, Sys->OREAD);
+		if(fd != nil){
+			b := array[64] of byte;
+			n := sys->read(fd, b, len b-1);
+			if(n > 0){
+				now = big string b[0:n];
+				if(now <= big 16r20000000)
+					now = big 0;	# remote itself is not initialised
+			}
+		}
+	}
+	if(now == big 0){
+		if(timedir != nil){
+			(ok, dir) := sys->stat(timedir);
+			if(ok < 0) {
+				sys->print("init: stat %s: %r", timedir);
+				return big 0;
+			}
+			now = big dir.atime;
+		}else{
+			now = big 993826747000000;
+			sys->print("time warped\n");
+		}
+	}
+	return now;
+}
+
+setclock(timefile: string, now: big)
+{
+	fd := sys->open(timefile, sys->OWRITE);
+	if (fd == nil) {
+		sys->print("init: can't open %s: %r", timefile);
+		return;
+	}
+
+	b := sys->aprint("%ubd", now);
+	if (sys->write(fd, b, len b) != len b)
+		sys->print("init: can't write to %s: %r", timefile);
+}
+
+srv()
+{
+	sys->print("remote debug srv...");
+	fd := sys->open("/dev/eia0ctl", Sys->OWRITE);
+	if(fd != nil)
+		sys->fprint(fd, "b115200");
+
+	fd = sys->open("/dev/eia0", Sys->ORDWR);
+	if (fd == nil){
+		err(sys->sprint("can't open /dev/eia0: %r"));
+		return;
+	}
+	if (sys->export(fd, "/", Sys->EXPASYNC) < 0){
+		err(sys->sprint("can't export on serial port: %r"));
+		return;
+	}
+}
+
+err(s: string)
+{
+	sys->fprint(sys->fildes(2), "init: %s\n", s);
+}
+
+hang()
+{
+	<-chan of int;
+}
+
+tried := 0;
+
+askrootsource(localok: int, netok: int): (string, string)
+{
+	stdin := sys->fildes(0);
+	sources := "kernel" :: nil;
+	if(netok)
+		sources = "remote" :: sources;
+	if(localok){
+		sources = "local" :: sources;
+		if(netok)
+			sources = "local+remote" :: sources;
+	}
+	for(;;) {
+		s := "";
+		if (tried == 0 && (s = rf("/nvfs/rootsource", nil)) != nil) {
+			tried = 1;
+			if (s[len s - 1] == '\n')
+				s = s[:len s - 1];
+			sys->print("/nvfs/rootsource: root from %s\n", s);
+		} else {
+			sys->print("root from (");
+			cm := "";
+			for(l := sources; l != nil; l = tl l){
+				sys->print("%s%s", cm, hd l);
+				cm = ",";
+			}
+			sys->print(")[%s] ", hd sources);
+
+			s = getline(stdin, hd sources);	# default
+		}
+		(nil, choice) := sys->tokenize(s, "\t ");
+		if(choice == nil)
+			choice = sources;
+		opt := hd choice;
+		case opt {
+		* =>
+			sys->print("\ninvalid boot option: '%s'\n", opt);
+		"kernel" =>
+			return (nil, "#r/rtc");
+		"local" =>
+			return ("/n/local", "#r/rtc");
+		"local+remote" =>
+			if(netfs("/n/remote") >= 0)
+				return ("/n/local", "/n/remote/dev/time");
+		"remote" =>
+			if(netfs("/n/remote") >= 0)
+				return ("/n/remote", "/n/remote/dev/time");
+		}
+	}
+}
+
+getline(fd: ref Sys->FD, default: string): string
+{
+	result := "";
+	buf := array[10] of byte;
+	i := 0;
+	for(;;) {
+		n := sys->read(fd, buf[i:], len buf - i);
+		if(n < 1)
+			break;
+		i += n;
+		while(i >0 && (nutf := sys->utfbytes(buf, i)) > 0){
+			s := string buf[0:nutf];
+			for (j := 0; j < len s; j++)
+				case s[j] {
+				'\b' =>
+					if(result != nil)
+						result = result[0:len result-1];
+				'u'&16r1F =>
+					sys->print("^U\n");
+					result = "";
+				'\r' =>
+					;
+				* =>
+					sys->print("%c", s[j]);
+					if(s[j] == '\n' || s[j] >= 16r80){
+						if(s[j] != '\n')
+							result[len result] = s[j];
+						if(result == nil)
+							return default;
+						return result;
+					}
+					result[len result] = s[j];
+				}
+			buf[0:] = buf[nutf:i];
+			i -= nutf;
+		}
+	}
+	return default;
+}
+
+#
+# serve local DOS file system using flash translation layer
+#
+lfs(): int
+{
+	if(!flashpart("#F/flash/flashctl", flashparts))
+		return -1;
+	if(!ftlinit("#F/flash/fs"))
+		return -1;
+	c := chan of string;
+	spawn startfs(c, "/dis/dossrv.dis", "dossrv" :: "-f" :: "#X/ftldata" :: "-m" :: "/n/local" :: nil);
+	if(<-c != nil)
+		return -1;
+	return 0;
+}
+
+startfs(c: chan of string, file: string, args: list of string)
+{
+	fs := load Command file;
+	if(fs == nil){
+		sys->print("can't load %s: %r\n", file);
+		c <-= "load failed";
+	}
+	{
+		fs->init(nil, args);
+	}exception e {
+	"*" =>
+		c <-= "failed";
+		exit;
+	* =>
+		c <-= "unknown exception";
+		exit;
+	}
+	c <-= nil;
+}
+
+#
+# partition flash
+#
+flashdone := 0;
+
+flashpart(ctl: string, parts: array of string): int
+{
+	if(flashdone)
+		return 1;
+	cfd := sys->open(ctl, Sys->ORDWR);
+	if(cfd == nil){
+		sys->print("can't open %s: %r\n", ctl);
+		return 0;
+	}
+	for(i := 0; i < len parts; i++)
+		if(sys->fprint(cfd, "%s", parts[i]) < 0){
+			sys->print("can't %q to %s: %r\n", parts[i], ctl);
+			return 0;
+		}
+	flashdone = 1;
+	return 1;
+}
+
+#
+# set up flash translation layer
+#
+ftldone := 0;
+
+ftlinit(flashmem: string): int
+{
+	if(ftldone)
+		return 1;
+	sys->print("Set flash translation of %s...\n", flashmem);
+	fd := sys->open("#X/ftlctl", Sys->OWRITE);
+	if(fd == nil){
+		sys->print("can't open #X/ftlctl: %r\n");
+		return 0;
+	}
+	if(sys->fprint(fd, "init %s", flashmem) <= 0){
+		sys->print("can't init flash translation: %r\n");
+		return 0;
+	}
+	ftldone = 1;
+	return 1;
+}
+
+configether()
+{
+	if(ethername == nil)
+		return;
+	fd := sys->open("/nvfs/etherparams", Sys->OREAD);
+	if(fd == nil)
+		return;
+	ctl := sys->open("/net/"+ethername+"/clone", Sys->OWRITE);
+	if(ctl == nil){
+		sys->print("init: can't open %s's clone: %r\n", ethername);
+		return;
+	}
+	b := array[1024] of byte;
+	n := sys->read(fd, b, len b);
+	if(n <= 0)
+		return;
+	for(i := 0; i < n;){
+		for(e := i; e < n && b[e] != byte '\n'; e++)
+			;
+		s := string b[i:e];
+		if(sys->fprint(ctl, "%s", s) < 0)
+			sys->print("init: ctl write to %s: %s: %r\n", ethername, s);
+		i = e+1;
+	}
+}
+
+donebind := 0;
+
+#
+# set up network mount
+#
+netfs(mountpt: string): int
+{
+	sys->print("bootp ...");
+
+	fd: ref Sys->FD;
+	if(!donebind){
+		fd = sys->open("/net/ipifc/clone", sys->OWRITE);
+		if(fd == nil) {
+			sys->print("init: open /net/ipifc/clone: %r\n");
+			return -1;
+		}
+		if(sys->fprint(fd, "bind ether %s", ethername) < 0) {
+			sys->print("could not bind ether0 interface: %r\n");
+			return -1;
+		}
+		donebind = 1;
+	}else{
+		fd = sys->open("/net/ipifc/0/ctl", Sys->OWRITE);
+		if(fd == nil){
+			sys->print("init: can't reopen /net/ipifc/0/ctl: %r\n");
+			return -1;
+		}
+	}
+	if ((ip := rf("/nvfs/ip", nil)) != nil) {
+		sys->print("**using %s\n", ip);
+		sys->fprint(fd, "bind ether /net/ether0");
+		sys->fprint(fd, "add %s ", ip);
+	} else {
+		{
+			if(sys->fprint(fd, "bootp") < 0)
+				sys->print("could not bootp: %r\n");
+		} exception e {
+			"*" =>
+				sys->print("could not bootp: %s\n", e);
+		}
+	}
+	server := rf("/nvfs/fsip", nil);
+	if (server != nil) {
+		if (server[len server - 1] == '\n')
+			server = server[:len server - 1];
+		sys->print("/nvfs/fsip: server=%s\n", server);
+	} else
+		server = bootp();
+	if(server == nil || server == "0.0.0.0")
+		return -1;
+
+	net := "tcp";	# how to specify il?
+	svcname := net + "!" + server + "!6666";
+
+	sys->print("dial %s...", svcname);
+
+	(ok, c) := sys->dial(svcname, nil);
+	if(ok < 0){
+		sys->print("can't dial %s: %r\n", svcname);
+		return -1;
+	}
+
+	sys->print("\nConnected ...\n");
+	if(kr != nil){
+		err: string;
+		sys->print("Authenticate ...");
+		ai := kr->readauthinfo("/nvfs/default");
+		if(ai == nil){
+			sys->print("readauthinfo /nvfs/default failed: %r\n");
+			sys->print("trying mount as `nobody'\n");
+		}
+		(c.dfd, err) = auth->client("none", ai, c.dfd);
+		if(c.dfd == nil){
+			sys->print("authentication failed: %s\n", err);
+			return -1;
+		}
+	}
+
+	sys->print("mount %s...", mountpt);
+
+	c.cfd = nil;
+	n := sys->mount(c.dfd, nil, mountpt, sys->MREPL, "");
+	if(n > 0)
+		return 0;
+	if(n < 0)
+		sys->print("%r");
+	return -1;
+}
+
+bootp(): string
+{
+	fd := sys->open("/net/bootp", sys->OREAD);
+	if(fd == nil) {
+		sys->print("init: can't open /net/bootp: %r");
+		return nil;
+	}
+
+	buf := array[Bootpreadlen] of byte;
+	nr := sys->read(fd, buf, len buf);
+	fd = nil;
+	if(nr <= 0) {
+		sys->print("init: read /net/bootp: %r");
+		return nil;
+	}
+
+	(ntok, ls) := sys->tokenize(string buf, " \t\n");
+	while(ls != nil) {
+		if(hd ls == "fsip"){
+			ls = tl ls;
+			break;
+		}
+		ls = tl ls;
+	}
+	if(ls == nil) {
+		sys->print("init: server address not in bootp read");
+		return nil;
+	}
+
+	srv := hd ls;
+
+	sys->print("%s\n", srv);
+
+	return srv;
+}
+	
+username(def: string): string
+{
+	return rf("/nvfs/user", def);
+}
+
+userok(user: string): int
+{
+	(ok, d) := sys->stat("/usr/"+user);
+	return ok >= 0 && (d.mode & Sys->DMDIR) != 0;
+}
+
+rf(file: string, default: string): string
+{
+	fd := sys->open(file, Sys->OREAD);
+	if(fd != nil){
+		buf := array[128] of byte;
+		nr := sys->read(fd, buf, len buf);
+		if(nr > 0)
+			return string buf[0:nr];
+	}
+	return default;
+}
--- /dev/null
+++ b/os/init/shell.b
@@ -1,0 +1,97 @@
+implement InitShell;
+
+include "sys.m";
+include "draw.m";
+
+sys: Sys;
+
+InitShell: module
+{
+	init:	fn(nil: ref Draw->Context, nil: list of string);
+};
+
+Sh: module
+{
+	init:	fn(ctxt: ref Draw->Context, argv: list of string);
+};
+
+init(nil: ref Draw->Context, nil: list of string)
+{
+	shell := load Sh "/dis/sh.dis";
+
+	sys = load Sys Sys->PATH;
+
+	if(sys != nil)
+		sys->print("init: starting shell\n");
+
+#	sys->bind("#I", "/net", sys->MAFTER);	# IP
+	sys->bind("#p", "/prog", sys->MREPL);	# prog device
+	sys->bind("#d", "/fd", Sys->MREPL);
+	sys->bind("#i", "/dev", sys->MREPL); 	# draw device
+	sys->bind("#t", "/dev", sys->MAFTER);	# serial line
+	sys->bind("#c", "/dev", sys->MAFTER); 	# console device
+	sys->bind("#W","/dev",sys->MAFTER);	# Flash
+#	sys->bind("#O", "/dev", sys->MAFTER);	# Modem
+#	sys->bind("#T","/dev",sys->MAFTER);	# Touchscreen
+
+	srv();
+
+	spawn shell->init(nil, nil);
+}
+
+srv()
+{
+	remotedebug := sysenv("remotedebug");
+	if(remotedebug != "1")
+		return;
+
+	sys->print("srv...");
+	if(echoto("#t/eia0ctl", "b38400") < 0)
+		return;
+
+	fd := sys->open("/dev/eia0", Sys->ORDWR);
+	if (fd == nil) {
+		sys->print("eia data open: %r\n");
+		return;
+	}
+	if (sys->export(fd, "/", Sys->EXPASYNC) < 0) {
+		sys->print("export: %r\n");
+		return;
+	}
+	sys->print("ok\n");
+}
+
+sysenv(param: string): string
+{
+	fd := sys->open("#c/sysenv", sys->OREAD);
+	if (fd == nil)
+		return(nil);
+	buf := array[4096] of byte;
+	nb := sys->read(fd, buf, len buf);
+	(nfl,fl) := sys->tokenize(string buf, "\n");
+	while (fl != nil) {
+		pair := hd fl;
+		(npl, pl) := sys->tokenize(pair, "=");
+		if (npl > 1) {
+			if ((hd pl) == param)
+				return hd tl pl;
+		}
+		fl = tl fl;
+	}
+	return nil ;
+}
+
+echoto(fname, str: string): int
+{
+	fd := sys->open(fname, Sys->OWRITE);
+	if(fd == nil) {
+		sys->print("%s: %r\n", fname);
+		return -1;
+	}
+	x := array of byte str;
+	if(sys->write(fd, x, len x) == -1) {
+		sys->print("write: %r\n");
+		return -1;
+	}
+	return 0;
+}
binary files /dev/null b/os/init/shell.dis differ
--- /dev/null
+++ b/os/init/shell.sbl
@@ -1,0 +1,318 @@
+limbo .sbl 2.1
+InitShell
+3
+shell.b
+sys.m
+draw.m
+168
+20.1,31 0
+22.1,25 1
+24.4,14 2
+25.2,38 3
+13,37 3
+2,38 3
+2,38 3
+28.1,37 4
+11,15 4
+17,24 4
+26,36 4
+1,37 4
+1,37 4
+29.1,35 5
+11,15 5
+17,22 5
+24,34 5
+1,35 5
+1,35 5
+30.1,36 6
+11,15 6
+17,23 6
+25,35 6
+1,36 6
+1,36 6
+31.1,37 7
+11,15 7
+17,23 7
+25,36 7
+1,37 7
+1,37 7
+32.1,37 8
+11,15 8
+17,23 8
+25,36 8
+1,37 8
+1,37 8
+33.1,35 9
+11,15 9
+16,22 9
+23,34 9
+1,35 9
+1,35 9
+37.1,6 10
+1,6 10
+39.1,28 11
+19,22 11
+24,27 11
+1,28 11
+40.0,1 12
+44.1,37 13
+23,36 13
+1,37 13
+1,37 13
+45.4,22 14
+46.2,8 15
+48.1,21 16
+12,20 16
+1,21 16
+1,21 16
+49.4,34 17
+11,23 17
+25,33 17
+4,34 17
+4,34 17
+4,38 17
+50.2,8 18
+52.1,41 19
+17,28 19
+30,40 19
+1,41 19
+1,41 19
+53.5,14 20
+54.2,35 21
+13,34 21
+2,35 21
+2,35 21
+55.2,8 22
+57.5,40 23
+17,19 23
+21,24 23
+26,39 23
+5,40 23
+5,40 23
+5,44 23
+58.2,28 24
+13,27 24
+2,28 24
+2,28 24
+59.2,8 25
+61.1,19 26
+12,18 26
+1,19 26
+1,19 26
+62.0,1 27
+66.1,41 28
+17,28 28
+30,40 28
+1,41 28
+1,41 28
+67.5,14 29
+68.8,13 30
+2,13 30
+69.1,27 31
+70.1,34 32
+17,19 32
+21,24 32
+26,33 32
+1,34 32
+1,34 32
+71.13,44 33
+27,37 33
+39,43 33
+13,44 33
+13,44 33
+72.8,17 34
+73.2,15 35
+74.15,39 36
+29,33 36
+35,38 36
+15,39 36
+15,39 36
+3,6 36
+8,10 36
+8,10 37
+75.6,13 38
+76.7,14 39
+7,23 39
+7,23 40
+77.14,19 41
+11,19 41
+11,19 42
+4,19 41
+79.2,12 43
+2,12 44
+2,12 45
+2,12 45
+81.8,11 46
+1,11 46
+86.1,36 47
+17,22 47
+24,35 47
+1,36 47
+1,36 47
+87.4,13 48
+88.2,31 49
+13,23 49
+25,30 49
+2,31 49
+2,31 49
+89.9,11 50
+2,11 50
+91.1,23 51
+92.4,28 52
+15,17 52
+19,20 52
+22,27 52
+4,28 52
+4,28 52
+4,34 52
+93.2,27 53
+13,26 53
+2,27 53
+2,27 53
+94.9,11 54
+2,11 54
+96.8,9 55
+1,9 55
+12
+aSys->Dir 1:26.1,39.2 64
+11
+0:name:28.2,6 s
+4:uid:29.2,5 s
+8:gid:30.2,5 s
+12:muid:31.2,6 s
+16:qid:32.2,5 @1
+
+32:mode:33.2,6 i
+36:atime:34.2,7 i
+40:mtime:35.2,7 i
+48:length:36.2,8 B
+56:dtype:37.2,7 i
+60:dev:38.2,5 i
+aSys->Qid 11.1,16.2 16
+3
+0:path:13.2,6 B
+8:vers:14.2,6 i
+12:qtype:15.2,7 i
+aDraw->Chans 2:70.1,82.2 4
+1
+0:desc:72.2,6 i
+aDraw->Context 274.1,279.2 12
+3
+0:display:276.2,9 R@4
+
+4:screen:277.2,8 R@8
+
+8:wm:278.2,4 Ct8.2
+0:t0:15,21 s
+4:t1:15,21 Ct8.2
+0:t0:32,38 s
+4:t1:32,38 R@9
+
+
+
+aDraw->Display 201.1,230.2 20
+5
+0:image:203.2,7 R@5
+
+4:white:204.2,7 R@5
+
+8:black:205.2,7 R@5
+
+12:opaque:206.2,8 R@5
+
+16:transparent:207.2,13 R@5
+
+aDraw->Image 142.1,198.2 56
+8
+0:r:146.2,3 @6
+
+16:clipr:147.2,7 @6
+
+32:depth:148.2,7 i
+36:chans:149.2,7 @2
+
+40:repl:150.2,6 i
+44:display:151.2,9 R@4
+
+48:screen:152.2,8 R@8
+
+52:iname:153.2,7 s
+aDraw->Rect 116.1,139.2 16
+2
+0:min:118.2,5 @7
+
+8:max:119.2,5 @7
+
+aDraw->Point 99.1,113.2 8
+2
+0:x:101.2,3 i
+4:y:102.2,3 i
+aDraw->Screen 249.1,263.2 16
+4
+0:id:251.2,4 i
+4:image:252.2,7 R@5
+
+8:fill:253.2,6 R@5
+
+12:display:254.2,9 R@4
+
+aDraw->Wmcontext 282.1,291.2 28
+7
+0:kbd:284.2,5 Ci
+4:ptr:285.2,5 CR@10
+
+8:ctl:286.2,5 Cs
+12:wctl:287.2,6 Cs
+16:images:288.2,8 CR@5
+
+20:connfd:289.2,8 R@11
+
+24:ctxt:290.2,6 R@3
+
+aDraw->Pointer 266.1,271.2 16
+3
+0:buttons:268.2,9 i
+4:xy:269.2,4 @7
+
+12:msec:270.2,6 i
+aSys->FD 1:45.1,48.2 4
+1
+0:fd:47.2,4 i
+4
+0:init
+0
+1
+40:shell:0:20.1,6 mSh
+13.0,16.1 0
+
+n50:srv
+0
+2
+32:fd:52.1,3 R@11
+
+36:remotedebug:44.1,12 s
+n95:sysenv
+1
+32:param:64.7,12 s
+8
+36:buf:69.1,4 Ab
+40:fd:66.1,3 R@11
+
+44:npl:74.3,6 i
+48:pl:8,10 Ls
+52:pair:73.2,6 s
+64:nb:70.1,3 i
+68:nfl:71.2,5 i
+72:fl:6,8 Ls
+s139:echoto
+2
+32:fname:84.7,12 s
+36:str:14,17 s
+2
+40:fd:86.1,3 R@11
+
+44:x:91.1,2 Ab
+i1
+108:sys:6.0,3 mSys
+1:4.0,163.1 0
+
--- /dev/null
+++ b/os/init/soeinit.b
@@ -1,0 +1,613 @@
+#
+# Soekris 4501
+#
+
+implement Init;
+
+include "sys.m";
+	sys:	Sys;
+
+include "draw.m";
+
+include "keyring.m";
+	kr: Keyring;
+
+include "security.m";
+	auth: Auth;
+
+include "sh.m";
+
+Init: module
+{
+	init:	fn();
+};
+
+Bootpreadlen: con 128;
+
+# standard flash partitions
+
+#flashparts := array[] of {
+#	# bootstrap at 0x0 to 0x20000
+#	"add script 0x20000 0x40000",
+#	"add kernel 0x100000 0x200000",
+#	"add fs 0x200000 end",
+#};
+flashparts: array of string;
+
+ethername := "ether0";
+
+#
+# initialise flash translation
+# mount flash file system
+# add devices
+# start a shell or window manager
+#
+
+init()
+{
+	sys = load Sys Sys->PATH;
+	kr = load Keyring Keyring->PATH;
+	auth = load Auth Auth->PATH;
+	if(auth != nil)
+		auth->init();
+
+	sys->bind("/", "/", Sys->MREPL);
+
+	localok := 0;
+	if(lfs() >= 0){
+		# let's just take a closer look
+		sys->bind("/n/local/nvfs", "/nvfs", Sys->MREPL|Sys->MCREATE);
+		(rc, nil) := sys->stat("/n/local/dis/sh.dis");
+		if(rc >= 0)
+			localok = 1;
+		else
+			err("local file system unusable");
+	}
+	netok := sys->bind("#l", "/net", Sys->MREPL) >= 0;
+	if(!netok){
+		netok = sys->bind("#l1", "/net", Sys->MREPL) >= 0;
+		if(netok)
+			ethername = "ether1";
+	}
+	if(netok)
+		configether();
+	dobind("#I", "/net", sys->MAFTER);	# IP
+	dobind("#p", "/prog", sys->MREPL);	# prog
+	sys->bind("#d", "/fd", Sys->MREPL);
+	dobind("#c", "/dev", sys->MREPL); 	# console
+	dobind("#t", "/dev", sys->MAFTER);	# serial line
+	drawok := sys->bind("#i", "/dev", sys->MAFTER) >= 0; 	# draw
+	sys->bind("#m", "/dev", sys->MAFTER);	# pointer
+	sys->bind("#e", "/env", sys->MREPL|sys->MCREATE);	# environment
+	sys->bind("#A", "/dev", Sys->MAFTER);	# optional audio
+	timefile: string;
+	rootsource: string;
+	scale := 1;
+	cfd := sys->open("/dev/consctl", Sys->OWRITE);
+	if(cfd != nil)
+		sys->fprint(cfd, "rawon");
+	for(;;){
+		(rootsource, timefile, scale) = askrootsource(localok, netok);
+		if(rootsource == nil)
+			break;	# internal
+		(rc, nil) := sys->stat(rootsource+"/dis/sh.dis");
+		if(rc < 0)
+			err("%s has no shell");
+		else if(sys->bind(rootsource, "/", Sys->MAFTER) < 0)
+			sys->print("can't bind %s on /: %r\n", rootsource);
+		else{
+			sys->bind(rootsource+"/dis", "/dis", Sys->MBEFORE|Sys->MCREATE);
+			break;
+		}
+	}
+	cfd = nil;
+
+	setsysname("soe");			# set system name
+
+	now := getclock(timefile, rootsource);
+	if(scale == 1)
+		now *= big 1000000;
+	setclock("/dev/time", now);
+	if(timefile != "#r/rtc")
+		setclock("#r/rtc", now/big 1000000);
+
+	sys->chdir("/");
+	if(netok){
+		start("ndb/dns", nil);
+		start("ndb/cs", nil);
+	}
+	startup := "/nvfs/startup";
+	if(sys->open(startup, Sys->OREAD) != nil){
+		shell := load Command Sh->PATH;
+		if(shell != nil){
+			sys->print("Running %s\n", startup);
+			shell->init(nil, "sh" :: startup :: nil);
+		}
+	}
+	user := username("inferno");
+	(ok, nil) := sys->stat("/dis/wm/wm.dis");
+	if(drawok && ok >= 0)
+		(ok, nil) = sys->stat("/dis/wm/logon.dis");
+	if(drawok && ok >= 0 && userok(user)){
+		wm := load Command "/dis/wm/wm.dis";
+		if(wm != nil){
+			fd := sys->open("/nvfs/user", Sys->OWRITE);
+			if(fd != nil){
+				sys->fprint(fd, "%s", user);
+				fd = nil;
+			}
+			spawn wm->init(nil, list of {"wm/wm", "wm/logon", "-l", "-u", user});
+			exit;
+		}
+		sys->print("init: can't load wm/logon: %r");
+	}
+	sh := load Command Sh->PATH;
+	if(sh == nil){
+		err(sys->sprint("can't load %s: %r", Sh->PATH));
+		hang();
+	}
+	spawn sh->init(nil, "sh" :: nil);
+}
+
+start(cmd: string, args: list of string)
+{
+	disfile := cmd;
+	if(disfile[0] != '/')
+		disfile = "/dis/"+disfile+".dis";
+	(ok, nil) := sys->stat(disfile);
+	if(ok >= 0){
+		dis := load Command disfile;
+		if(dis == nil)
+			sys->print("init: can't load %s: %r\n", disfile);
+		else
+			spawn dis->init(nil, cmd :: args);
+	}
+}
+
+dobind(f, t: string, flags: int)
+{
+	if(sys->bind(f, t, flags) < 0)
+		err(sys->sprint("can't bind %s on %s: %r", f, t));
+}
+
+#
+# Set system name from nvram if possible
+#
+setsysname(def: string)
+{
+	v := array of byte def;
+	fd := sys->open("/nvfs/ID", sys->OREAD);
+	if(fd == nil)
+		fd = sys->open("/env/sysname", sys->OREAD);
+	if(fd != nil){
+		buf := array[Sys->NAMEMAX] of byte;
+		nr := sys->read(fd, buf, len buf);
+		while(nr > 0 && buf[nr-1] == byte '\n')
+			nr--;
+		if(nr > 0)
+			v = buf[0:nr];
+	}
+	fd = sys->open("/dev/sysname", sys->OWRITE);
+	if(fd != nil)
+		sys->write(fd, v, len v);
+}
+
+getclock(timefile: string, timedir: string): big
+{
+	now := big 0;
+	if(timefile != nil){
+		fd := sys->open(timefile, Sys->OREAD);
+		if(fd != nil){
+			b := array[64] of byte;
+			n := sys->read(fd, b, len b-1);
+			if(n > 0){
+				now = big string b[0:n];
+				if(now <= big 16r20000000)
+					now = big 0;	# remote itself is not initialised
+			}
+		}
+	}
+	if(now == big 0){
+		if(timedir != nil){
+			(ok, dir) := sys->stat(timedir);
+			if(ok < 0) {
+				sys->print("init: stat %s: %r", timedir);
+				return big 0;
+			}
+			now = big dir.atime;
+		}else{
+			now = big 993826747000000;
+			sys->print("time warped\n");
+		}
+	}
+	return now;
+}
+
+setclock(timefile: string, now: big)
+{
+	fd := sys->open(timefile, sys->OWRITE);
+	if (fd == nil) {
+		sys->print("init: can't open %s: %r", timefile);
+		return;
+	}
+
+	b := sys->aprint("%ubd", now);
+	if (sys->write(fd, b, len b) != len b)
+		sys->print("init: can't write to %s: %r", timefile);
+}
+
+srv()
+{
+	sys->print("remote debug srv...");
+	fd := sys->open("/dev/eia0ctl", Sys->OWRITE);
+	if(fd != nil)
+		sys->fprint(fd, "b115200");
+
+	fd = sys->open("/dev/eia0", Sys->ORDWR);
+	if (fd == nil){
+		err(sys->sprint("can't open /dev/eia0: %r"));
+		return;
+	}
+	if (sys->export(fd, "/", Sys->EXPASYNC) < 0){
+		err(sys->sprint("can't export on serial port: %r"));
+		return;
+	}
+}
+
+err(s: string)
+{
+	sys->fprint(sys->fildes(2), "init: %s\n", s);
+}
+
+hang()
+{
+	<-chan of int;
+}
+
+tried := 0;
+
+askrootsource(localok: int, netok: int): (string, string, int)
+{
+	stdin := sys->fildes(0);
+	sources := "kernel" :: nil;
+	if(netok)
+		sources = "remote" :: sources;
+	if(localok){
+		sources = "local" :: sources;
+		if(netok)
+			sources = "local+remote" :: sources;
+	}
+	for(;;) {
+		s := "";
+		if (tried == 0 && (s = rf("/nvfs/rootsource", nil)) != nil) {
+			tried = 1;
+			if (s[len s - 1] == '\n')
+				s = s[:len s - 1];
+			sys->print("/nvfs/rootsource: root from %s\n", s);
+		} else {
+			sys->print("root from (");
+			cm := "";
+			for(l := sources; l != nil; l = tl l){
+				sys->print("%s%s", cm, hd l);
+				cm = ",";
+			}
+			sys->print(")[%s] ", hd sources);
+
+			s = getline(stdin, hd sources);	# default
+		}
+		(nil, choice) := sys->tokenize(s, "\t ");
+		if(choice == nil)
+			choice = sources;
+		opt := hd choice;
+		case opt {
+		* =>
+			sys->print("\ninvalid boot option: '%s'\n", opt);
+		"kernel" =>
+			return (nil, "#r/rtc", 1);
+		"local" =>
+			return ("/n/local", "#r/rtc", 1);
+		"local+remote" =>
+			if(netfs("/n/remote") >= 0)
+				return ("/n/local", "/n/remote/dev/time", 1000000);
+		"remote" =>
+			if(netfs("/n/remote") >= 0)
+				return ("/n/remote", "/n/remote/dev/time", 1000000);
+		}
+	}
+}
+
+getline(fd: ref Sys->FD, default: string): string
+{
+	result := "";
+	buf := array[10] of byte;
+	i := 0;
+	for(;;) {
+		n := sys->read(fd, buf[i:], len buf - i);
+		if(n < 1)
+			break;
+		i += n;
+		while(i >0 && (nutf := sys->utfbytes(buf, i)) > 0){
+			s := string buf[0:nutf];
+			for (j := 0; j < len s; j++)
+				case s[j] {
+				'\b' =>
+					if(result != nil)
+						result = result[0:len result-1];
+				'u'&16r1F =>
+					sys->print("^U\n");
+					result = "";
+				'\r' =>
+					;
+				* =>
+					sys->print("%c", s[j]);
+					if(s[j] == '\n' || s[j] >= 16r80){
+						if(s[j] != '\n')
+							result[len result] = s[j];
+						if(result == nil)
+							return default;
+						return result;
+					}
+					result[len result] = s[j];
+				}
+			buf[0:] = buf[nutf:i];
+			i -= nutf;
+		}
+	}
+	return default;
+}
+
+#
+# serve local DOS file system using flash translation layer
+#
+lfs(): int
+{
+	if(!flashpart("#F/flash/flashctl", flashparts))
+		return -1;
+	if(!ftlinit("#F/flash/fs"))
+		return -1;
+	c := chan of string;
+	spawn startfs(c, "/dis/dossrv.dis", "dossrv" :: "-f" :: "#X/ftldata" :: "-m" :: "/n/local" :: nil);
+	if(<-c != nil)
+		return -1;
+	return 0;
+}
+
+startfs(c: chan of string, file: string, args: list of string)
+{
+	fs := load Command file;
+	if(fs == nil){
+		sys->print("can't load %s: %r\n", file);
+		c <-= "load failed";
+	}
+	{
+		fs->init(nil, args);
+	}exception e {
+	"*" =>
+		c <-= "failed";
+		exit;
+	* =>
+		c <-= "unknown exception";
+		exit;
+	}
+	c <-= nil;
+}
+
+#
+# partition flash
+#
+flashdone := 0;
+
+flashpart(ctl: string, parts: array of string): int
+{
+	if(flashdone)
+		return 1;
+	cfd := sys->open(ctl, Sys->ORDWR);
+	if(cfd == nil){
+		sys->print("can't open %s: %r\n", ctl);
+		return 0;
+	}
+	for(i := 0; i < len parts; i++)
+		if(sys->fprint(cfd, "%s", parts[i]) < 0){
+			sys->print("can't %q to %s: %r\n", parts[i], ctl);
+			return 0;
+		}
+	flashdone = 1;
+	return 1;
+}
+
+#
+# set up flash translation layer
+#
+ftldone := 0;
+
+ftlinit(flashmem: string): int
+{
+	if(ftldone)
+		return 1;
+	sys->print("Set flash translation of %s...\n", flashmem);
+	fd := sys->open("#X/ftlctl", Sys->OWRITE);
+	if(fd == nil){
+		sys->print("can't open #X/ftlctl: %r\n");
+		return 0;
+	}
+	if(sys->fprint(fd, "init %s", flashmem) <= 0){
+		sys->print("can't init flash translation: %r\n");
+		return 0;
+	}
+	ftldone = 1;
+	return 1;
+}
+
+configether()
+{
+	if(ethername == nil)
+		return;
+	fd := sys->open("/nvfs/etherparams", Sys->OREAD);
+	if(fd == nil)
+		return;
+	ctl := sys->open("/net/"+ethername+"/clone", Sys->OWRITE);
+	if(ctl == nil){
+		sys->print("init: can't open %s's clone: %r\n", ethername);
+		return;
+	}
+	b := array[1024] of byte;
+	n := sys->read(fd, b, len b);
+	if(n <= 0)
+		return;
+	for(i := 0; i < n;){
+		for(e := i; e < n && b[e] != byte '\n'; e++)
+			;
+		s := string b[i:e];
+		if(sys->fprint(ctl, "%s", s) < 0)
+			sys->print("init: ctl write to %s: %s: %r\n", ethername, s);
+		i = e+1;
+	}
+}
+
+donebind := 0;
+
+#
+# set up network mount
+#
+netfs(mountpt: string): int
+{
+	sys->print("bootp ...");
+
+	fd: ref Sys->FD;
+	if(!donebind){
+		fd = sys->open("/net/ipifc/clone", sys->OWRITE);
+		if(fd == nil) {
+			sys->print("init: open /net/ipifc/clone: %r\n");
+			return -1;
+		}
+		if(sys->fprint(fd, "bind ether %s", ethername) < 0) {
+			sys->print("could not bind ether0 interface: %r\n");
+			return -1;
+		}
+		donebind = 1;
+	}else{
+		fd = sys->open("/net/ipifc/0/ctl", Sys->OWRITE);
+		if(fd == nil){
+			sys->print("init: can't reopen /net/ipifc/0/ctl: %r\n");
+			return -1;
+		}
+	}
+	if ((ip := rf("/nvfs/ip", nil)) != nil) {
+		sys->print("**using %s\n", ip);
+		sys->fprint(fd, "bind ether /net/ether0");
+		sys->fprint(fd, "add %s ", ip);
+	} else {
+		{
+			if(sys->fprint(fd, "bootp") < 0)
+				sys->print("could not bootp: %r\n");
+		} exception e {
+			"*" =>
+				sys->print("could not bootp: %s\n", e);
+		}
+	}
+	server := rf("/nvfs/fsip", nil);
+	if (server != nil) {
+		if (server[len server - 1] == '\n')
+			server = server[:len server - 1];
+		sys->print("/nvfs/fsip: server=%s\n", server);
+	} else
+		server = bootp();
+	if(server == nil || server == "0.0.0.0")
+		return -1;
+
+	net := "tcp";	# how to specify il?
+	svcname := net + "!" + server + "!6666";
+
+	sys->print("dial %s...", svcname);
+
+	(ok, c) := sys->dial(svcname, nil);
+	if(ok < 0){
+		sys->print("can't dial %s: %r\n", svcname);
+		return -1;
+	}
+
+	sys->print("\nConnected ...\n");
+	if(kr != nil){
+		err: string;
+		sys->print("Authenticate ...");
+		ai := kr->readauthinfo("/nvfs/default");
+		if(ai == nil){
+			sys->print("readauthinfo /nvfs/default failed: %r\n");
+			sys->print("trying mount as `nobody'\n");
+		}
+		(c.dfd, err) = auth->client("none", ai, c.dfd);
+		if(c.dfd == nil){
+			sys->print("authentication failed: %s\n", err);
+			return -1;
+		}
+	}
+
+	sys->print("mount %s...", mountpt);
+
+	c.cfd = nil;
+	n := sys->mount(c.dfd, nil, mountpt, sys->MREPL, "");
+	if(n > 0)
+		return 0;
+	if(n < 0)
+		sys->print("%r");
+	return -1;
+}
+
+bootp(): string
+{
+	fd := sys->open("/net/bootp", sys->OREAD);
+	if(fd == nil) {
+		sys->print("init: can't open /net/bootp: %r");
+		return nil;
+	}
+
+	buf := array[Bootpreadlen] of byte;
+	nr := sys->read(fd, buf, len buf);
+	fd = nil;
+	if(nr <= 0) {
+		sys->print("init: read /net/bootp: %r");
+		return nil;
+	}
+
+	(ntok, ls) := sys->tokenize(string buf, " \t\n");
+	while(ls != nil) {
+		if(hd ls == "fsip"){
+			ls = tl ls;
+			break;
+		}
+		ls = tl ls;
+	}
+	if(ls == nil) {
+		sys->print("init: server address not in bootp read");
+		return nil;
+	}
+
+	srv := hd ls;
+
+	sys->print("%s\n", srv);
+
+	return srv;
+}
+	
+username(def: string): string
+{
+	return rf("/nvfs/user", def);
+}
+
+userok(user: string): int
+{
+	(ok, d) := sys->stat("/usr/"+user);
+	return ok >= 0 && (d.mode & Sys->DMDIR) != 0;
+}
+
+rf(file: string, default: string): string
+{
+	fd := sys->open(file, Sys->OREAD);
+	if(fd != nil){
+		buf := array[128] of byte;
+		nr := sys->read(fd, buf, len buf);
+		if(nr > 0)
+			return string buf[0:nr];
+	}
+	return default;
+}
--- /dev/null
+++ b/os/init/srvinit.b
@@ -1,0 +1,209 @@
+implement Init;
+#
+# init program for standalone wm using TK
+#
+include "sys.m";
+sys: Sys;
+	FD, Connection, sprint, Dir: import sys;
+	print, fprint, open, bind, mount, dial, sleep, read: import sys;
+
+include "security.m";
+	auth:	Auth;
+
+include "draw.m";
+	draw: Draw;
+	Context: import draw;
+
+include "keyring.m";
+	kr: Keyring;
+
+Init: module
+{
+	init:	fn();
+};
+
+Command: module
+{
+	init:	fn(ctxt: ref Context, argv: list of string);
+};
+
+rootfs(server: string): int
+{
+	ok, n: int;
+	c: Connection;
+
+	(ok, c) = dial("tcp!" + server + "!6666", nil);
+	if(ok < 0) {
+		sys->print("cannot connected to %s: %r\n", server);
+		return -1;
+	}
+
+	sys->print("Connected ...");
+	if(kr != nil && auth != nil){
+		err: string;
+		sys->print("Authenticate ...");
+		ai := kr->readauthinfo("/nvfs/default");
+		if(ai == nil){
+			sys->print("readauthinfo /nvfs/default failed: %r\n");
+			sys->print("trying mount as `nobody'\n");
+		}
+		(c.dfd, err) = auth->client("none", ai, c.dfd);
+		if(c.dfd == nil){
+			sys->print("authentication failed: %s\n", err);
+			return -1;
+		}
+	}
+
+	sys->print("mount ...");
+
+	c.cfd = nil;
+	n = mount(c.dfd, "/", sys->MREPL, "");
+	if(n > 0)
+		return 0;
+	return -1;
+}
+
+Bootpreadlen: con 128;
+
+init()
+{
+	spec: string;
+
+	sys = load Sys Sys->PATH;
+	kr = load Keyring Keyring->PATH;
+	auth = load Auth Auth->PATH;
+	if(auth != nil)
+		auth->init();
+
+	sys->print("**\n** Inferno\n** Vita Nuova\n**\n");
+
+	sys->print("Setup boot net services ...\n");
+	
+	#
+	# Setup what we need to call a server and
+	# Authenticate
+	#
+	bind("#l", "/net", sys->MREPL);
+	bind("#I", "/net", sys->MAFTER);
+	bind("#c", "/dev", sys->MAFTER);
+	bind("#H", "/dev", sys->MAFTER);
+	nvramfd := sys->open("#H/hd0nvram", sys->ORDWR);
+	if(nvramfd != nil){
+		spec = "#Fhd0nvram";
+		if(bind(spec, "/nvfs", sys->MAFTER) < 0)
+			print("init: bind %s: %r\n", spec);
+	}
+
+	setsysname();
+
+	sys->print("bootp...");
+
+	fd := open("/net/ipifc/clone", sys->OWRITE);
+	if(fd == nil) {
+		print("init: open /net/ipifc/clone: %r\n");
+		exit;
+	}
+	cfg := array of byte "bind ether ether0";
+	if(sys->write(fd, cfg, len cfg) != len cfg) {
+		sys->print("could not bind interface: %r\n");
+		exit;
+	}
+	cfg = array of byte "bootp";
+	if(sys->write(fd, cfg, len cfg) != len cfg) {
+		sys->print("could not bootp: %r\n");
+		exit;
+	}
+
+	fd = open("/net/bootp", sys->OREAD);
+	if(fd == nil) {
+		print("init: open /net/bootp: %r");
+		exit;
+	}
+
+	buf := array[Bootpreadlen] of byte;
+	nr := read(fd, buf, len buf);
+	fd = nil;
+	if(nr <= 0) {
+		print("init: read /net/bootp: %r");
+		exit;
+	}
+
+	(ntok, ls) := sys->tokenize(string buf, " \t\n");
+	while(ls != nil) {
+		if(hd ls == "fsip"){
+			ls = tl ls;
+			break;
+		}
+		ls = tl ls;
+	}
+	if(ls == nil) {
+		print("init: server address not in bootp read");
+		exit;
+	}
+
+	srv := hd ls;
+	sys->print("server %s\nConnect ...\n", srv);
+
+	retrycount := 0;
+	while(rootfs(srv) < 0 && retrycount++ < 5)
+		sleep(1000);
+
+	sys->print("done\n");
+
+	#
+	# default namespace
+	#
+	bind("#c", "/dev", sys->MREPL);		# console
+	bind("#l", "/net", sys->MREPL);			# ethernet
+	bind("#I", "/net", sys->MBEFORE);		# TCP/IP
+	bind("#p", "/prog", sys->MREPL);		# prog device
+	sys->bind("#d", "/fd", Sys->MREPL);
+
+	sys->print("clock...\n");
+	setclock();
+
+	sys->print("sh...\n");
+	sh := load Command "/dis/sh.dis";
+	if (sh == nil)
+		print("cannot load /dis/sh.dis: %r\n");
+
+	sh->init(nil, "sh" :: "-i" :: nil);
+}
+
+setclock()
+{
+	(ok, dir) := sys->stat("/");
+	if (ok < 0) {
+		print("init: stat /: %r");
+		return;
+	}
+
+	fd := sys->open("/dev/time", sys->OWRITE);
+	if (fd == nil) {
+		print("init: open /dev/time: %r");
+		return;
+	}
+
+	# Time is kept as microsecs, atime is in secs
+	b := array of byte sprint("%d000000", dir.atime);
+	if (sys->write(fd, b, len b) != len b)
+		print("init: write /dev/time: %r");
+}
+
+#
+# Set system name from nvram
+#
+setsysname()
+{
+	fd := open("/nvfs/ID", sys->OREAD);
+	if(fd == nil)
+		return;
+	fds := open("/dev/sysname", sys->OWRITE);
+	if(fds == nil)
+		return;
+	buf := array[128] of byte;
+	nr := sys->read(fd, buf, len buf);
+	if(nr <= 0)
+		return;
+	sys->write(fds, buf, nr);
+}
--- /dev/null
+++ b/os/init/wminit.b
@@ -1,0 +1,231 @@
+implement Init;
+#
+# init program for standalone wm using TK
+#
+include "sys.m";
+sys: Sys;
+	FD, Connection, sprint, Dir: import sys;
+	print, fprint, open, bind, mount, dial, sleep, read: import sys;
+
+include "security.m";
+	auth:	Auth;
+
+include "draw.m";
+	draw: Draw;
+	Context: import draw;
+
+include "keyring.m";
+	kr: Keyring;
+
+Init: module
+{
+	init:	fn();
+};
+
+Command: module
+{
+	init:	fn(ctxt: ref Context, argv: list of string);
+};
+
+rootfs(server: string): int
+{
+	ok, n: int;
+	c: Connection;
+
+	(ok, c) = dial("tcp!" + server + "!6666", nil);
+	if(ok < 0)
+		return -1;
+
+	sys->print("Connected ...");
+	if(kr != nil && auth != nil){
+		err: string;
+		sys->print("Authenticate ...");
+		ai := kr->readauthinfo("/nvfs/default");
+		if(ai == nil){
+			sys->print("readauthinfo /nvfs/default failed: %r\n");
+			sys->print("trying mount as `nobody'\n");
+		}
+		(c.dfd, err) = auth->client("none", ai, c.dfd);
+		if(c.dfd == nil){
+			sys->print("authentication failed: %s\n", err);
+			return -1;
+		}
+	}
+
+	sys->print("mount ...");
+
+	c.cfd = nil;
+	n = mount(c.dfd, nil, "/", sys->MREPL, "");
+	if(n > 0)
+		return 0;
+	return -1;
+}
+
+Bootpreadlen: con 128;
+
+init()
+{
+	spec: string;
+
+	sys = load Sys Sys->PATH;
+	kr = load Keyring Keyring->PATH;
+	auth = load Auth Auth->PATH;
+	if(auth != nil)
+		auth->init();
+
+	sys->print("**\n** Inferno\n** Vita Nuova\n**\n");
+
+	sys->print("Setup boot net services ...\n");
+	
+	#
+	# Setup what we need to call a server and
+	# Authenticate
+	#
+	bind("#l", "/net", sys->MREPL);
+	bind("#I", "/net", sys->MAFTER);
+	bind("#c", "/dev", sys->MAFTER);
+	bind("#H", "/dev", sys->MAFTER);
+	nvramfd := sys->open("#H/hd0nvram", sys->ORDWR);
+	if(nvramfd != nil){
+		spec = "#Fhd0nvram";
+		if(bind(spec, "/nvfs", sys->MAFTER) < 0)
+			print("init: bind %s: %r\n", spec);
+	}
+
+	setsysname();
+
+	sys->print("bootp...");
+
+	fd := open("/net/ipifc/clone", sys->OWRITE);
+	if(fd == nil) {
+		print("init: open /net/ipifc/clone: %r\n");
+		exit;
+	}
+	cfg := array of byte "bind ether ether0";
+	if(sys->write(fd, cfg, len cfg) != len cfg) {
+		sys->print("could not bind interface: %r\n");
+		exit;
+	}
+	cfg = array of byte "bootp";
+	if(sys->write(fd, cfg, len cfg) != len cfg) {
+		sys->print("could not bootp: %r\n");
+		exit;
+	}
+
+	fd = open("/net/bootp", sys->OREAD);
+	if(fd == nil) {
+		print("init: open /net/bootp: %r");
+		exit;
+	}
+
+	buf := array[Bootpreadlen] of byte;
+	nr := read(fd, buf, len buf);
+	fd = nil;
+	if(nr <= 0) {
+		print("init: read /net/bootp: %r");
+		exit;
+	}
+
+	(ntok, ls) := sys->tokenize(string buf, " \t\n");
+	while(ls != nil) {
+		if(hd ls == "fsip"){
+			ls = tl ls;
+			break;
+		}
+		ls = tl ls;
+	}
+	if(ls == nil) {
+		print("init: server address not in bootp read");
+		exit;
+	}
+
+	srv := hd ls;
+	sys->print("server %s\nConnect ...\n", srv);
+
+	retrycount := 0;
+	while(rootfs(srv) < 0 && retrycount++ < 5)
+		sleep(1000);
+
+	cfd := sys->open("/dev/cons", Sys->OWRITE);
+	if (cfd != nil) {
+		sys->dup(cfd.fd, 1);
+		sys->dup(cfd.fd, 2);
+	}
+	cfd = nil;
+
+	sys->print("done\n");
+
+	#
+	# default namespace
+	#
+	bind("#c", "/dev", sys->MREPL);			# console
+	bind("#t", "/dev", sys->MAFTER);		# serial port
+	bind("#H", "/dev", sys->MAFTER);
+	if(spec != nil)
+		bind(spec, "/nvfs", sys->MBEFORE|sys->MCREATE);	# our keys
+	bind("#E", "/dev", sys->MBEFORE);		# mpeg
+	bind("#l", "/net", sys->MBEFORE);		# ethernet
+	bind("#I", "/net", sys->MBEFORE);		# TCP/IP
+	bind("#V", "/dev", sys->MAFTER);		# hauppauge TV
+	bind("#p", "/prog", sys->MREPL);		# prog device
+	sys->bind("#d", "/fd", Sys->MREPL);
+	if(bind("#m", "/dev", sys->MAFTER) >= 0){		# mouse setup device
+		mouse := load Command "/dis/mouse.dis";
+		if (mouse != nil) {
+			print("Setting up mouse\n");
+			mouse->init(nil, "/dis/mouse.dis" :: nil);
+			mouse = nil;
+		}
+	}
+
+	sys->print("clock...\n");
+	setclock();
+
+	sys->print("logon...\n");
+
+	logon := load Command "/dis/wm/logon.dis";
+	if(logon == nil) {
+		print("init: load /dis/wm/logon.dis: %r");
+		logon = load Command "/dis/sh.dis";
+	}
+	dc: ref Context;
+	spawn logon->init(dc, nil);
+}
+
+setclock()
+{
+	(ok, dir) := sys->stat("/");
+	if (ok < 0) {
+		print("init: stat /: %r");
+		return;
+	}
+
+	fd := sys->open("/dev/time", sys->OWRITE);
+	if (fd == nil) {
+		print("init: open /dev/time: %r");
+		return;
+	}
+
+	# Time is kept as microsecs, atime is in secs
+	b := array of byte sprint("%d000000", dir.atime);
+	if (sys->write(fd, b, len b) != len b)
+		print("init: write /dev/time: %r");
+}
+
+#
+# Set system name from nvram
+#
+setsysname()
+{
+	fd := open("/nvfs/ID", sys->OREAD);
+	if(fd == nil)
+		return;
+	fds := open("/dev/sysname", sys->OWRITE);
+	if(fds == nil)
+		return;
+	buf := array[128] of byte;
+	nr := sys->read(fd, buf, len buf);
+	if(nr <= 0)
+		return;
+	sys->write(fds, buf, nr);
+}
binary files /dev/null b/os/init/wminit.dis differ
--- /dev/null
+++ b/os/init/wminit.sbl
@@ -1,0 +1,667 @@
+limbo .sbl 2.1
+Init
+5
+wminit.b
+sys.m
+security.m
+draw.m
+keyring.m
+441
+35.11,47 0
+16,31 0
+16,41 0
+16,41 1
+43,46 0
+11,47 0
+11,47 0
+2,4 0
+6,7 0
+6,7 2
+6,7 3
+6,7 4
+36.4,10 5
+37.9,11 6
+2,11 6
+39.1,28 7
+12,27 7
+1,28 7
+1,28 7
+40.4,13 8
+17,28 8
+42.2,32 9
+13,31 9
+2,32 9
+2,32 9
+43.2,41 10
+25,40 10
+2,41 10
+2,41 10
+44.5,14 11
+45.3,56 12
+14,55 12
+3,56 12
+3,56 12
+46.3,43 13
+14,42 13
+3,43 13
+3,43 13
+48.17,48 14
+30,36 14
+38,40 14
+42,47 14
+17,48 14
+17,48 14
+3,8 14
+10,13 14
+10,13 15
+10,13 16
+49.5,17 17
+50.3,49 18
+14,43 18
+45,48 18
+3,49 18
+3,49 18
+51.10,12 19
+3,12 19
+3,12 20
+3,12 21
+55.1,24 22
+12,23 22
+1,24 22
+1,24 22
+57.1,12 23
+58.1,43 24
+11,16 24
+18,21 24
+23,26 24
+28,38 24
+40,42 24
+1,43 24
+1,43 24
+59.4,9 25
+60.9,10 26
+2,10 26
+61.8,10 27
+1,10 27
+70.1,25 28
+71.1,32 29
+72.1,28 30
+73.4,15 31
+74.2,14 32
+2,14 32
+2,14 32
+2,14 33
+76.1,50 34
+12,49 34
+1,50 34
+1,50 34
+78.1,44 35
+12,43 35
+1,44 35
+1,44 35
+84.1,31 36
+6,10 36
+12,18 36
+20,30 36
+1,31 36
+1,31 36
+85.1,32 37
+6,10 37
+12,18 37
+20,31 37
+1,32 37
+1,32 37
+86.1,32 38
+6,10 38
+12,18 38
+20,31 38
+1,32 38
+1,32 38
+87.1,32 39
+6,10 39
+12,18 39
+20,31 39
+1,32 39
+1,32 39
+88.1,48 40
+22,35 40
+37,47 40
+1,48 40
+1,48 40
+89.4,18 41
+90.2,21 42
+91.5,37 43
+10,14 43
+16,23 43
+25,36 43
+5,37 43
+5,37 43
+5,41 43
+92.3,37 44
+9,30 44
+32,36 44
+3,37 44
+3,37 44
+95.1,13 45
+1,13 45
+97.1,23 46
+12,22 46
+1,23 46
+1,23 46
+99.1,44 47
+12,30 47
+32,43 47
+1,44 47
+1,44 47
+100.4,13 48
+101.2,44 49
+8,43 49
+2,44 49
+2,44 49
+102.2,6 50
+104.1,41 51
+105.4,32 52
+15,17 52
+19,22 52
+24,31 52
+4,32 52
+4,32 52
+36,43 52
+4,43 52
+106.2,46 53
+13,45 53
+2,46 53
+2,46 53
+107.2,6 54
+109.1,28 55
+110.4,32 56
+15,17 56
+19,22 56
+24,31 56
+4,32 56
+4,32 56
+36,43 56
+4,43 56
+111.2,37 57
+13,36 57
+2,37 57
+2,37 57
+112.2,6 58
+115.1,36 59
+11,23 59
+25,35 59
+1,36 59
+1,36 59
+116.4,13 60
+117.2,36 61
+8,35 61
+2,36 61
+2,36 61
+118.2,6 62
+121.1,35 63
+122.1,29 64
+12,14 64
+16,19 64
+21,28 64
+1,29 64
+1,29 64
+123.1,9 65
+124.4,11 66
+125.2,36 67
+8,35 67
+2,36 67
+2,36 67
+126.2,6 68
+129.15,49 69
+29,39 69
+41,48 69
+15,49 69
+15,49 69
+130.7,16 70
+131.5,10 71
+5,20 71
+5,20 72
+132.3,13 73
+133.3,8 74
+135.2,12 75
+2,12 75
+137.4,13 76
+138.2,49 77
+8,48 77
+2,49 77
+2,49 77
+139.2,6 78
+142.1,13 79
+143.1,44 80
+12,38 80
+40,43 80
+1,44 80
+1,44 80
+145.1,16 81
+146.7,18 82
+14,17 82
+7,18 82
+7,18 82
+7,22 82
+26,38 82
+26,38 82
+26,42 82
+147.2,13 83
+8,12 83
+2,13 83
+2,13 83
+2,13 83
+149.1,43 84
+18,29 84
+31,42 84
+1,43 84
+1,43 84
+150.5,15 85
+151.2,21 86
+11,17 86
+19,20 86
+2,21 86
+2,21 86
+152.2,21 87
+11,17 87
+19,20 87
+2,21 87
+2,21 87
+154.1,10 88
+156.1,21 89
+12,20 89
+1,21 89
+1,21 89
+161.1,31 90
+6,10 90
+12,18 90
+20,30 90
+1,31 90
+1,31 90
+162.1,32 91
+6,10 91
+12,18 91
+20,31 91
+1,32 91
+1,32 91
+163.1,32 92
+6,10 92
+12,18 92
+20,31 92
+1,32 92
+1,32 92
+164.4,15 93
+165.2,48 94
+7,11 94
+13,20 94
+22,47 94
+2,48 94
+2,48 94
+166.1,33 95
+6,10 95
+12,18 95
+20,32 95
+1,33 95
+1,33 95
+167.1,33 96
+6,10 96
+12,18 96
+20,32 96
+1,33 96
+1,33 96
+168.1,33 97
+6,10 97
+12,18 97
+20,32 97
+1,33 97
+1,33 97
+169.1,32 98
+6,10 98
+12,18 98
+20,31 98
+1,32 98
+1,32 98
+170.1,32 99
+6,10 99
+12,19 99
+21,31 99
+1,32 99
+1,32 99
+171.1,35 100
+11,15 100
+17,22 100
+24,34 100
+1,35 100
+1,35 100
+172.4,35 101
+9,13 101
+15,21 101
+23,34 101
+4,35 101
+4,35 101
+4,40 101
+173.2,40 102
+174.6,18 103
+175.3,30 104
+9,29 104
+3,30 104
+3,30 104
+176.3,44 105
+15,18 105
+40,43 105
+20,43 105
+20,43 105
+20,43 106
+3,44 105
+177.3,14 107
+3,14 108
+181.1,25 109
+12,24 109
+1,25 109
+1,25 109
+182.1,11 110
+1,11 110
+184.1,25 111
+12,24 111
+1,25 111
+1,25 111
+186.1,42 112
+187.4,16 113
+188.2,43 114
+8,42 114
+2,43 114
+2,43 114
+189.2,36 115
+192.1,27 116
+19,21 116
+23,26 116
+1,27 116
+193.0,1 117
+197.14,28 118
+24,27 118
+14,28 118
+14,28 118
+198.5,11 119
+199.2,27 120
+8,26 120
+2,27 120
+2,27 120
+200.2,8 121
+203.1,42 122
+17,28 122
+30,41 122
+1,42 122
+1,42 122
+204.5,14 123
+205.2,35 124
+8,34 124
+2,35 124
+2,35 124
+206.2,8 125
+210.20,49 126
+27,37 126
+39,48 126
+20,49 126
+20,49 126
+1,49 126
+1,49 127
+211.5,29 128
+16,18 128
+20,21 128
+23,28 128
+5,29 128
+5,29 128
+33,38 128
+5,38 128
+212.2,36 129
+8,35 129
+2,36 129
+2,36 129
+213.0,1 130
+220.1,35 131
+12,22 131
+24,34 131
+1,35 131
+1,35 131
+221.4,13 132
+222.2,8 133
+223.1,41 134
+13,27 134
+29,40 134
+1,41 134
+1,41 134
+224.4,14 135
+225.2,8 136
+226.1,26 137
+227.1,34 138
+17,19 138
+21,24 138
+26,33 138
+1,34 138
+1,34 138
+228.4,11 139
+229.2,8 140
+230.1,25 141
+12,15 141
+17,20 141
+22,24 141
+1,25 141
+1,25 141
+231.0,1 142
+19
+aSys->Dir 1:26.1,39.2 64
+11
+0:name:28.2,6 s
+4:uid:29.2,5 s
+8:gid:30.2,5 s
+12:muid:31.2,6 s
+16:qid:32.2,5 @1
+
+32:mode:33.2,6 i
+36:atime:34.2,7 i
+40:mtime:35.2,7 i
+48:length:36.2,8 B
+56:dtype:37.2,7 i
+60:dev:38.2,5 i
+aSys->Qid 11.1,16.2 16
+3
+0:path:13.2,6 B
+8:vers:14.2,6 i
+12:qtype:15.2,7 i
+aDraw->Chans 3:70.1,82.2 4
+1
+0:desc:72.2,6 i
+aSys->Connection 1:52.1,57.2 12
+3
+0:dfd:54.2,5 R@4
+
+4:cfd:55.2,5 R@4
+
+8:dir:56.2,5 s
+aSys->FD 45.1,48.2 4
+1
+0:fd:47.2,4 i
+aKeyring->Authinfo 4:132.1,140.2 24
+6
+0:mysk:134.2,6 R@6
+
+4:mypk:135.2,6 R@8
+
+8:cert:136.2,6 R@9
+
+12:spk:137.2,5 R@8
+
+16:alpha:138.2,7 R@10
+
+20:p:139.2,3 R@10
+
+aKeyring->SK 70.1,75.2 8
+2
+0:sa:72.2,4 R@7
+
+4:owner:73.2,7 s
+aKeyring->SigAlg 55.1,59.2 4
+1
+0:name:57.2,6 s
+aKeyring->PK 62.1,67.2 8
+2
+0:sa:64.2,4 R@7
+
+4:owner:65.2,7 s
+aKeyring->Certificate 78.1,85.2 16
+4
+0:sa:80.2,4 R@7
+
+4:ha:81.2,4 s
+8:signer:82.2,8 s
+12:exp:83.2,5 i
+aKeyring->IPint 9.1,52.2 4
+1
+0:x:11.2,3 i
+aDraw->Context 3:274.1,279.2 12
+3
+0:display:276.2,9 R@12
+
+4:screen:277.2,8 R@16
+
+8:wm:278.2,4 Ct8.2
+0:t0:15,21 s
+4:t1:15,21 Ct8.2
+0:t0:32,38 s
+4:t1:32,38 R@17
+
+
+
+aDraw->Display 201.1,230.2 20
+5
+0:image:203.2,7 R@13
+
+4:white:204.2,7 R@13
+
+8:black:205.2,7 R@13
+
+12:opaque:206.2,8 R@13
+
+16:transparent:207.2,13 R@13
+
+aDraw->Image 142.1,198.2 56
+8
+0:r:146.2,3 @14
+
+16:clipr:147.2,7 @14
+
+32:depth:148.2,7 i
+36:chans:149.2,7 @2
+
+40:repl:150.2,6 i
+44:display:151.2,9 R@12
+
+48:screen:152.2,8 R@16
+
+52:iname:153.2,7 s
+aDraw->Rect 116.1,139.2 16
+2
+0:min:118.2,5 @15
+
+8:max:119.2,5 @15
+
+aDraw->Point 99.1,113.2 8
+2
+0:x:101.2,3 i
+4:y:102.2,3 i
+aDraw->Screen 249.1,263.2 16
+4
+0:id:251.2,4 i
+4:image:252.2,7 R@13
+
+8:fill:253.2,6 R@13
+
+12:display:254.2,9 R@12
+
+aDraw->Wmcontext 282.1,291.2 28
+7
+0:kbd:284.2,5 Ci
+4:ptr:285.2,5 CR@18
+
+8:ctl:286.2,5 Cs
+12:wctl:287.2,6 Cs
+16:images:288.2,8 CR@13
+
+20:connfd:289.2,8 R@4
+
+24:ctxt:290.2,6 R@11
+
+aDraw->Pointer 266.1,271.2 16
+3
+0:buttons:268.2,9 i
+4:xy:269.2,4 @15
+
+12:msec:270.2,6 i
+4
+0:rootfs
+1
+32:server:0:30.7,13 s
+5
+36:ai:43.2,4 R@5
+
+40:err:41.2,5 s
+44:n:32.5,6 i
+48:ok:1,3 i
+64:c:33.1,2 @3
+
+i76:init
+0
+14
+32:cfg:104.1,4 Ab
+36:fd:99.1,3 R@4
+
+40:cfd:149.1,4 R@4
+
+44:spec:68.1,5 s
+48:buf:121.1,4 Ab
+52:logon:186.1,6 mCommand
+25.0,28.1 0
+
+56:mouse:173.2,7 mCommand
+25.0,28.1 0
+
+60:srv:142.1,4 s
+64:nr:122.1,3 i
+68:nvramfd:88.1,8 R@4
+
+72:retrycount:145.1,11 i
+88:dc:191.1,3 R@11
+
+92:ntok:129.2,6 i
+96:ls:8,10 Ls
+n370:setclock
+0
+4
+32:b:210.1,2 Ab
+36:fd:203.1,3 R@4
+
+40:ok:197.2,4 i
+48:dir:6,9 @0
+
+n411:setsysname
+0
+4
+32:buf:226.1,4 Ab
+36:fd:220.1,3 R@4
+
+40:fds:223.1,4 R@4
+
+44:nr:227.1,3 i
+n4
+156:auth:11.1,5 mAuth
+2:49.0,59.1 0
+
+192:draw:0:14.1,5 mDraw
+3:1.0,298.1 0
+
+236:kr:0:18.1,3 mKeyring
+4:4.0,335.1 0
+
+264:sys:0:6.0,3 mSys
+1:4.0,163.1 0
+
--- /dev/null
+++ b/os/ip/arp.c
@@ -1,0 +1,681 @@
+#include "u.h"
+#include "../port/lib.h"
+#include "mem.h"
+#include "dat.h"
+#include "fns.h"
+#include "../port/error.h"
+
+#include "ip.h"
+#include "ipv6.h"
+
+/*
+ *  address resolution tables
+ */
+
+enum
+{
+	NHASH		= (1<<6),
+	NCACHE		= 256,
+
+	AOK		= 1,
+	AWAIT		= 2,
+};
+
+char *arpstate[] =
+{
+	"UNUSED",
+	"OK",
+	"WAIT",
+};
+
+/*
+ *  one per Fs
+ */
+struct Arp
+{
+	QLock;
+	Fs	*f;
+	Arpent	*hash[NHASH];
+	Arpent	cache[NCACHE];
+	Arpent	*rxmt;
+	Proc	*rxmitp;	/* neib sol re-transmit proc */
+	Rendez	rxmtq;
+	Block 	*dropf, *dropl;
+};
+
+char *Ebadarp = "bad arp";
+
+#define haship(s) ((s)[IPaddrlen-1]%NHASH)
+
+extern int 	ReTransTimer = RETRANS_TIMER;
+static void 	rxmitproc(void *v);
+
+void
+arpinit(Fs *f)
+{
+	f->arp = smalloc(sizeof(Arp));
+	f->arp->f = f;
+	f->arp->rxmt = nil;
+	f->arp->dropf = f->arp->dropl = nil;
+	kproc("rxmitproc", rxmitproc, f->arp, 0);
+}
+
+/*
+ *  create a new arp entry for an ip address.
+ */
+static Arpent*
+newarp6(Arp *arp, uchar *ip, Ipifc *ifc, int addrxt)
+{
+	uint t;
+	Block *next, *xp;
+	Arpent *a, *e, *f, **l;
+	Medium *m = ifc->m;
+	int empty;
+
+	/* find oldest entry */
+	e = &arp->cache[NCACHE];
+	a = arp->cache;
+	t = a->utime;
+	for(f = a; f < e; f++){
+		if(f->utime < t){
+			t = f->utime;
+			a = f;
+		}
+	}
+
+	/* dump waiting packets */
+	xp = a->hold;
+	a->hold = nil;
+
+	if(isv4(a->ip)){
+		while(xp){
+			next = xp->list;
+			freeblist(xp);
+			xp = next;
+		}
+	}
+	else {	// queue icmp unreachable for rxmitproc later on, w/o arp lock
+		if(xp){
+			if(arp->dropl == nil) 
+				arp->dropf = xp;
+			else
+				arp->dropl->list = xp;
+
+			for(next = xp->list; next; next = next->list)
+				xp = next;
+			arp->dropl = xp;
+			wakeup(&arp->rxmtq);
+		}
+	}
+
+	/* take out of current chain */
+	l = &arp->hash[haship(a->ip)];
+	for(f = *l; f; f = f->hash){
+		if(f == a){
+			*l = a->hash;
+			break;
+		}
+		l = &f->hash;
+	}
+
+	/* insert into new chain */
+	l = &arp->hash[haship(ip)];
+	a->hash = *l;
+	*l = a;
+
+	memmove(a->ip, ip, sizeof(a->ip));
+	a->utime = NOW;
+	a->ctime = 0;
+	a->type = m;
+
+	a->rtime = NOW + ReTransTimer;
+	a->rxtsrem = MAX_MULTICAST_SOLICIT;
+	a->ifc = ifc;
+	a->ifcid = ifc->ifcid;
+
+	/* put to the end of re-transmit chain; addrxt is 0 when isv4(a->ip) */
+	if(!ipismulticast(a->ip) && addrxt){
+		l = &arp->rxmt;
+		empty = (*l==nil);
+
+		for(f = *l; f; f = f->nextrxt){
+			if(f == a){
+				*l = a->nextrxt;
+				break;
+			}
+			l = &f->nextrxt;
+		}
+		for(f = *l; f; f = f->nextrxt){
+			l = &f->nextrxt;
+		}
+		*l = a;
+		if(empty) 
+			wakeup(&arp->rxmtq);
+	}
+
+	a->nextrxt = nil;
+
+	return a;
+}
+
+/* called with arp qlocked */
+
+void
+cleanarpent(Arp *arp, Arpent *a)
+{
+	Arpent *f, **l;
+
+	a->utime = 0;
+	a->ctime = 0;
+	a->type = 0;
+	a->state = 0;
+	
+	/* take out of current chain */
+	l = &arp->hash[haship(a->ip)];
+	for(f = *l; f; f = f->hash){
+		if(f == a){
+			*l = a->hash;
+			break;
+		}
+		l = &f->hash;
+	}
+
+	/* take out of re-transmit chain */
+	l = &arp->rxmt;
+	for(f = *l; f; f = f->nextrxt){
+		if(f == a){
+			*l = a->nextrxt;
+			break;
+		}
+		l = &f->nextrxt;
+	}
+	a->nextrxt = nil;
+	a->hash = nil;
+	a->hold = nil;
+	a->last = nil;
+	a->ifc = nil;
+}
+
+/*
+ *  fill in the media address if we have it.  Otherwise return an
+ *  Arpent that represents the state of the address resolution FSM
+ *  for ip.  Add the packet to be sent onto the list of packets
+ *  waiting for ip->mac to be resolved.
+ */
+Arpent*
+arpget(Arp *arp, Block *bp, int version, Ipifc *ifc, uchar *ip, uchar *mac)
+{
+	int hash;
+	Arpent *a;
+	Medium *type = ifc->m;
+	uchar v6ip[IPaddrlen];
+
+	if(version == V4){
+		v4tov6(v6ip, ip);
+		ip = v6ip;
+	}
+
+	qlock(arp);
+	hash = haship(ip);
+	for(a = arp->hash[hash]; a; a = a->hash){
+		if(memcmp(ip, a->ip, sizeof(a->ip)) == 0)
+		if(type == a->type)
+			break;
+	}
+
+	if(a == nil){
+		a = newarp6(arp, ip, ifc, (version != V4));
+		a->state = AWAIT;
+	}
+	a->utime = NOW;
+	if(a->state == AWAIT){
+		if(bp != nil){
+			if(a->hold)
+				a->last->list = bp;
+			else
+				a->hold = bp;
+			a->last = bp;
+			bp->list = nil; 
+		}
+		return a;		/* return with arp qlocked */
+	}
+
+	memmove(mac, a->mac, a->type->maclen);
+
+	/* remove old entries */
+	if(NOW - a->ctime > 15*60*1000)
+		cleanarpent(arp, a);
+
+	qunlock(arp);
+	return nil;
+}
+
+/*
+ * called with arp locked
+ */
+void
+arprelease(Arp *arp, Arpent*)
+{
+	qunlock(arp);
+}
+
+/*
+ * Copy out the mac address from the Arpent.  Return the
+ * block waiting to get sent to this mac address.
+ *
+ * called with arp locked
+ */
+Block*
+arpresolve(Arp *arp, Arpent *a, Medium *type, uchar *mac)
+{
+	Block *bp;
+	Arpent *f, **l;
+
+	if(!isv4(a->ip)){
+		l = &arp->rxmt;
+		for(f = *l; f; f = f->nextrxt){
+			if(f == a){
+				*l = a->nextrxt;
+				break;
+			}
+			l = &f->nextrxt;
+		}
+	}
+
+	memmove(a->mac, mac, type->maclen);
+	a->type = type;
+	a->state = AOK;
+	a->utime = NOW;
+	bp = a->hold;
+	a->hold = nil;
+	qunlock(arp);
+
+	return bp;
+}
+
+void
+arpenter(Fs *fs, int version, uchar *ip, uchar *mac, int n, int refresh)
+{
+	Arp *arp;
+	Route *r;
+	Arpent *a, *f, **l;
+	Ipifc *ifc;
+	Medium *type;
+	Block *bp, *next;
+	uchar v6ip[IPaddrlen];
+
+	arp = fs->arp;
+
+	if(n != 6){
+//		print("arp: len = %d\n", n);
+		return;
+	}
+
+	switch(version){
+	case V4:
+		r = v4lookup(fs, ip, nil);
+		v4tov6(v6ip, ip);
+		ip = v6ip;
+		break;
+	case V6:
+		r = v6lookup(fs, ip, nil);
+		break;
+	default:
+		panic("arpenter: version %d", version);
+		return;	/* to supress warnings */
+	}
+
+	if(r == nil){
+//		print("arp: no route for entry\n");
+		return;
+	}
+
+	ifc = r->ifc;
+	type = ifc->m;
+
+	qlock(arp);
+	for(a = arp->hash[haship(ip)]; a; a = a->hash){
+		if(a->type != type || (a->state != AWAIT && a->state != AOK))
+			continue;
+
+		if(ipcmp(a->ip, ip) == 0){
+			a->state = AOK;
+			memmove(a->mac, mac, type->maclen);
+
+			if(version == V6){
+				/* take out of re-transmit chain */
+				l = &arp->rxmt;
+				for(f = *l; f; f = f->nextrxt){
+					if(f == a){
+						*l = a->nextrxt;
+						break;
+					}
+					l = &f->nextrxt;
+				}
+			}
+
+			a->ifc = ifc;
+			a->ifcid = ifc->ifcid;
+			bp = a->hold;
+			a->hold = nil;
+			if(version == V4)
+				ip += IPv4off;
+			a->utime = NOW;
+			a->ctime = a->utime;
+			qunlock(arp);
+
+			while(bp){
+				next = bp->list;
+				if(ifc != nil){
+					if(waserror()){
+						runlock(ifc);
+						nexterror();
+					}
+					rlock(ifc);
+					if(ifc->m != nil)
+						ifc->m->bwrite(ifc, bp, version, ip);
+					else
+						freeb(bp);
+					runlock(ifc);
+					poperror();
+				} else
+					freeb(bp);
+				bp = next;
+			}
+			return;
+		}
+	}
+
+	if(refresh == 0){
+		a = newarp6(arp, ip, ifc, 0);
+		a->state = AOK;
+		a->type = type;
+		a->ctime = NOW;
+		memmove(a->mac, mac, type->maclen);
+	}
+
+	qunlock(arp);
+}
+
+int
+arpwrite(Fs *fs, char *s, int len)
+{
+	int n;
+	Route *r;
+	Arp *arp;
+	Block *bp;
+	Arpent *a, *fl, **l;
+	Medium *m;
+	char *f[4], buf[256];
+	uchar ip[IPaddrlen], mac[MAClen];
+
+	arp = fs->arp;
+
+	if(len == 0)
+		error(Ebadarp);
+	if(len >= sizeof(buf))
+		len = sizeof(buf)-1;
+	strncpy(buf, s, len);
+	buf[len] = 0;
+	if(len > 0 && buf[len-1] == '\n')
+		buf[len-1] = 0;
+
+	n = getfields(buf, f, 4, 1, " ");
+	if(strcmp(f[0], "flush") == 0){
+		qlock(arp);
+		for(a = arp->cache; a < &arp->cache[NCACHE]; a++){
+			memset(a->ip, 0, sizeof(a->ip));
+			memset(a->mac, 0, sizeof(a->mac));
+			a->hash = nil;
+			a->state = 0;
+			a->utime = 0;
+			while(a->hold != nil){
+				bp = a->hold->list;
+				freeblist(a->hold);
+				a->hold = bp;
+			}
+		}
+		memset(arp->hash, 0, sizeof(arp->hash));
+// clear all pkts on these lists (rxmt, dropf/l)
+		arp->rxmt = nil;
+		arp->dropf = nil;
+		arp->dropl = nil;
+		qunlock(arp);
+	} else if(strcmp(f[0], "add") == 0){
+		switch(n){
+		default:
+			error(Ebadarg);
+		case 3:
+			parseip(ip, f[1]);
+			if(isv4(ip))
+				r = v4lookup(fs, ip+IPv4off, nil);
+			else
+				r = v6lookup(fs, ip, nil);
+			if(r == nil)
+				error("Destination unreachable");
+			m = r->ifc->m;
+			n = parsemac(mac, f[2], m->maclen);
+			break;
+		case 4:
+			m = ipfindmedium(f[1]);
+			if(m == nil)
+				error(Ebadarp);
+			parseip(ip, f[2]);
+			n = parsemac(mac, f[3], m->maclen);
+			break;
+		}
+
+		if(m->ares == nil)
+			error(Ebadarp);
+
+		m->ares(fs, V6, ip, mac, n, 0);
+	} else if(strcmp(f[0], "del") == 0){
+		if(n != 2)
+			error(Ebadarg);
+
+		parseip(ip, f[1]);
+		qlock(arp);
+
+		l = &arp->hash[haship(ip)];
+		for(a = *l; a; a = a->hash){
+			if(memcmp(ip, a->ip, sizeof(a->ip)) == 0){
+				*l = a->hash;
+				break;
+			}
+			l = &a->hash;
+		}
+	
+		if(a){
+			/* take out of re-transmit chain */
+			l = &arp->rxmt;
+			for(fl = *l; fl; fl = fl->nextrxt){
+				if(fl == a){
+					*l = a->nextrxt;
+					break;
+				}
+				l = &fl->nextrxt;
+			}
+
+			a->nextrxt = nil;
+			a->hash = nil;
+			a->hold = nil;
+			a->last = nil;
+			a->ifc = nil;
+			memset(a->ip, 0, sizeof(a->ip));
+			memset(a->mac, 0, sizeof(a->mac));
+		}
+		qunlock(arp);
+	} else
+		error(Ebadarp);
+
+	return len;
+}
+
+enum
+{
+	Alinelen=	90,
+};
+
+char *aformat = "%-6.6s %-8.8s %-40.40I %-32.32s\n";
+
+static void
+convmac(char *p, uchar *mac, int n)
+{
+	while(n-- > 0)
+		p += sprint(p, "%2.2ux", *mac++);
+}
+
+int
+arpread(Arp *arp, char *p, ulong offset, int len)
+{
+	Arpent *a;
+	int n;
+	char mac[2*MAClen+1];
+
+	if(offset % Alinelen)
+		return 0;
+
+	offset = offset/Alinelen;
+	len = len/Alinelen;
+
+	n = 0;
+	for(a = arp->cache; len > 0 && a < &arp->cache[NCACHE]; a++){
+		if(a->state == 0)
+			continue;
+		if(offset > 0){
+			offset--;
+			continue;
+		}
+		len--;
+		qlock(arp);
+		convmac(mac, a->mac, a->type->maclen);
+		n += sprint(p+n, aformat, a->type->name, arpstate[a->state], a->ip, mac);
+		qunlock(arp);
+	}
+
+	return n;
+}
+
+extern int
+rxmitsols(Arp *arp)
+{
+	uint sflag;
+	Block *next, *xp;
+	Arpent *a, *b, **l;
+	Fs *f;
+	uchar ipsrc[IPaddrlen];
+	Ipifc *ifc = nil;
+	long nrxt;
+
+	qlock(arp);
+	f = arp->f;
+
+	a = arp->rxmt;
+	if(a==nil){
+		nrxt = 0;
+		goto dodrops; 		//return nrxt;
+	}
+	nrxt = a->rtime - NOW;
+	if(nrxt > 3*ReTransTimer/4) 
+		goto dodrops; 		//return nrxt;
+
+	for(; a; a = a->nextrxt){
+		ifc = a->ifc;
+		assert(ifc != nil);
+		if((a->rxtsrem <= 0) || !(canrlock(ifc)) || (a->ifcid != ifc->ifcid)){
+			xp = a->hold;
+			a->hold = nil;
+
+			if(xp){
+				if(arp->dropl == nil) 
+					arp->dropf = xp;
+				else
+					arp->dropl->list = xp;
+			}
+
+			cleanarpent(arp, a);
+		}
+		else
+			break;
+	}
+	if(a == nil)
+		goto dodrops;
+
+
+	qunlock(arp);	/* for icmpns */
+	if((sflag = ipv6anylocal(ifc, ipsrc)) != SRC_UNSPEC) 
+		icmpns(f, ipsrc, sflag, a->ip, TARG_MULTI, ifc->mac); 
+
+	runlock(ifc);
+	qlock(arp);	
+
+	/* put to the end of re-transmit chain */
+	l = &arp->rxmt;
+	for(b = *l; b; b = b->nextrxt){
+		if(b == a){
+			*l = a->nextrxt;
+			break;
+		}
+		l = &b->nextrxt;
+	}
+	for(b = *l; b; b = b->nextrxt){
+		l = &b->nextrxt;
+	}
+	*l = a;
+	a->rxtsrem--;
+	a->nextrxt = nil;
+	a->rtime = NOW + ReTransTimer;
+
+	a = arp->rxmt;
+	if(a==nil)
+		nrxt = 0;
+	else 
+		nrxt = a->rtime - NOW;
+
+dodrops:
+	xp = arp->dropf;
+	arp->dropf = nil;
+	arp->dropl = nil;
+	qunlock(arp);
+
+	for(; xp; xp = next){
+		next = xp->list;
+		icmphostunr(f, ifc, xp, icmp6_adr_unreach, 1);
+	}
+
+	return nrxt;
+
+}
+
+static int
+rxready(void *v)
+{
+	Arp *arp = (Arp *) v;
+	int x;
+
+	x = ((arp->rxmt != nil) || (arp->dropf != nil));
+
+	return x;
+}
+
+static void
+rxmitproc(void *v)
+{
+	Arp *arp = v;
+	long wakeupat;
+
+	arp->rxmitp = up;
+	//print("arp rxmitproc started\n");
+	if(waserror()){
+		arp->rxmitp = 0;
+		pexit("hangup", 1);
+	}
+	for(;;){
+		wakeupat = rxmitsols(arp);
+		if(wakeupat == 0) 
+			sleep(&arp->rxmtq, rxready, v); 
+		else if(wakeupat > ReTransTimer/4) 
+			tsleep(&arp->rxmtq, return0, 0, wakeupat); 
+	}
+}
+
--- /dev/null
+++ b/os/ip/bootp.c
@@ -1,0 +1,231 @@
+#include "u.h"
+#include "../port/lib.h"
+#include "mem.h"
+#include "dat.h"
+#include "fns.h"
+#include "../port/error.h"
+#include "kernel.h"
+#include "ip.h"
+
+static	ulong	fsip;
+static	ulong	auip;
+static	ulong	gwip;
+static	ulong	ipmask;
+static	ulong	ipaddr;
+
+enum
+{
+	Bootrequest = 1,
+	Bootreply   = 2,
+};
+
+typedef struct Bootp
+{
+	/* udp.c oldheader */
+	uchar	raddr[IPaddrlen];
+	uchar	laddr[IPaddrlen];
+	uchar	rport[2];
+	uchar	lport[2];
+	/* bootp itself */
+	uchar	op;		/* opcode */
+	uchar	htype;		/* hardware type */
+	uchar	hlen;		/* hardware address len */
+	uchar	hops;		/* hops */
+	uchar	xid[4];		/* a random number */
+	uchar	secs[2];	/* elapsed snce client started booting */
+	uchar	pad[2];
+	uchar	ciaddr[4];	/* client IP address (client tells server) */
+	uchar	yiaddr[4];	/* client IP address (server tells client) */
+	uchar	siaddr[4];	/* server IP address */
+	uchar	giaddr[4];	/* gateway IP address */
+	uchar	chaddr[16];	/* client hardware address */
+	uchar	sname[64];	/* server host name (optional) */
+	uchar	file[128];	/* boot file name */
+	uchar	vend[128];	/* vendor-specific goo */
+} Bootp;
+
+/*
+ * bootp returns:
+ *
+ * "fsip d.d.d.d
+ * auip d.d.d.d
+ * gwip d.d.d.d
+ * ipmask d.d.d.d
+ * ipaddr d.d.d.d"
+ *
+ * where d.d.d.d is the IP address in dotted decimal notation, and each
+ * address is followed by a newline.
+ */
+
+static	Bootp	req;
+static	Proc*	rcvprocp;
+static	int	recv;
+static	int	done;
+static	Rendez	bootpr;
+static	char	rcvbuf[512+2*IPaddrlen+2*2];
+
+static void
+rcvbootp(void *a)
+{
+	int n, fd;
+	Bootp *rp;
+	char *field[4];
+	uchar ip[IPaddrlen];
+
+	if(waserror())
+		pexit("", 0);
+	rcvprocp = up;	/* store for postnote below */
+	fd = (int)a;
+	while(done == 0) {
+		n = kread(fd, rcvbuf, sizeof(rcvbuf));
+		if(n <= 0)
+			break;
+		rp = (Bootp*)rcvbuf;
+		/* currently ignore udp's header */
+		if(memcmp(req.chaddr, rp->chaddr, 6) == 0
+		&& rp->htype == 1 && rp->hlen == 6
+		&& getfields((char*)rp->vend+4, field, 4, 1, " ") == 4
+		&& strncmp((char*)rp->vend, "p9  ", 4) == 0){
+			if(ipaddr == 0)
+				ipaddr = nhgetl(rp->yiaddr);
+			if(ipmask == 0)
+				ipmask = parseip(ip, field[0]);
+			if(fsip == 0)
+				fsip = parseip(ip, field[1]);
+			if(auip == 0)
+				auip = parseip(ip, field[2]);
+			if(gwip == 0)
+				gwip = parseip(ip, field[3]);
+			break;
+		}
+	}
+	poperror();
+	rcvprocp = nil;
+
+	recv = 1;
+	wakeup(&bootpr);
+	pexit("", 0);
+}
+
+static char*
+rbootp(Ipifc *ifc)
+{
+	int cfd, dfd, tries, n;
+	char ia[5+3*24], im[16], *av[3];
+	uchar nipaddr[4], ngwip[4], nipmask[4];
+	char dir[Maxpath];
+
+	av[1] = "0.0.0.0";
+	av[2] = "0.0.0.0";
+	ipifcadd(ifc, av, 3, 0, nil);
+
+	cfd = kannounce("udp!*!68", dir);
+	if(cfd < 0)
+		return "bootp announce failed";
+	strcat(dir, "/data");
+	if(kwrite(cfd, "headers", 7) < 0){
+		kclose(cfd);
+		return "bootp ctl headers failed";
+	}
+	kwrite(cfd, "oldheaders", 10);
+	dfd = kopen(dir, ORDWR);
+	if(dfd < 0){
+		kclose(cfd);
+		return "bootp open data failed";
+	}
+	kclose(cfd);
+	
+
+	/* create request */
+	memset(&req, 0, sizeof(req));
+	ipmove(req.raddr, IPv4bcast);
+	hnputs(req.rport, 67);
+	req.op = Bootrequest;
+	req.htype = 1;			/* ethernet (all we know) */
+	req.hlen = 6;			/* ethernet (all we know) */
+
+	/* Hardware MAC address */
+	memmove(req.chaddr, ifc->mac, 6);
+	/* Fill in the local IP address if we know it */
+	ipv4local(ifc, req.ciaddr);
+	memset(req.file, 0, sizeof(req.file));
+	strcpy((char*)req.vend, "p9  ");
+
+	done = 0;
+	recv = 0;
+
+	kproc("rcvbootp", rcvbootp, (void*)dfd, KPDUPFDG);
+
+	/*
+	 * broadcast bootp's till we get a reply,
+	 * or fixed number of tries
+	 */
+	tries = 0;
+	while(recv == 0) {
+		if(kwrite(dfd, &req, sizeof(req)) < 0)
+			print("bootp: write: %s\n", commonerror());
+
+		tsleep(&bootpr, return0, 0, 1000);
+		if(++tries > 10) {
+			print("bootp: timed out\n");
+			break;
+		}
+	}
+	kclose(dfd);
+	done = 1;
+	if(rcvprocp != nil){
+		postnote(rcvprocp, 1, "timeout", 0);
+		rcvprocp = nil;
+	}
+
+	av[1] = "0.0.0.0";
+	av[2] = "0.0.0.0";
+	ipifcrem(ifc, av, 3);
+
+	hnputl(nipaddr, ipaddr);
+	sprint(ia, "%V", nipaddr);
+	hnputl(nipmask, ipmask);
+	sprint(im, "%V", nipmask);
+	av[1] = ia;
+	av[2] = im;
+	ipifcadd(ifc, av, 3, 0, nil);
+
+	if(gwip != 0) {
+		hnputl(ngwip, gwip);
+		n = snprint(ia, sizeof(ia), "add 0.0.0.0 0.0.0.0 %V", ngwip);
+		routewrite(ifc->conv->p->f, nil, ia, n);
+	}
+	return nil;
+}
+
+static int
+rbootpread(char *bp, ulong offset, int len)
+{
+	int n;
+	char *buf;
+	uchar a[4];
+
+	buf = smalloc(READSTR);
+	if(waserror()){
+		free(buf);
+		nexterror();
+	}
+	hnputl(a, fsip);
+	n = snprint(buf, READSTR, "fsip %15V\n", a);
+	hnputl(a, auip);
+	n += snprint(buf + n, READSTR-n, "auip %15V\n", a);
+	hnputl(a, gwip);
+	n += snprint(buf + n, READSTR-n, "gwip %15V\n", a);
+	hnputl(a, ipmask);
+	n += snprint(buf + n, READSTR-n, "ipmask %15V\n", a);
+	hnputl(a, ipaddr);
+	snprint(buf + n, READSTR-n, "ipaddr %15V\n", a);
+
+	len = readstr(offset, bp, len, buf);
+	poperror();
+	free(buf);
+	return len;
+}
+
+char*	(*bootp)(Ipifc*) = rbootp;
+int	(*bootpread)(char*, ulong, int) = rbootpread;
--- /dev/null
+++ b/os/ip/compress.c
@@ -1,0 +1,520 @@
+#include	"u.h"
+#include	"../port/lib.h"
+#include	"mem.h"
+#include	"dat.h"
+#include	"fns.h"
+#include	"../port/error.h"
+
+#include	"ip.h"
+#include	"ppp.h"
+
+typedef struct Iphdr	Iphdr;
+typedef struct Tcphdr	Tcphdr;
+typedef struct Ilhdr	Ilhdr;
+typedef struct Hdr	Hdr;
+typedef struct Tcpc	Tcpc;
+
+struct Iphdr
+{
+	uchar	vihl;		/* Version and header length */
+	uchar	tos;		/* Type of service */
+	uchar	length[2];	/* packet length */
+	uchar	id[2];		/* Identification */
+	uchar	frag[2];	/* Fragment information */
+	uchar	ttl;		/* Time to live */
+	uchar	proto;		/* Protocol */
+	uchar	cksum[2];	/* Header checksum */
+	ulong	src;		/* Ip source (byte ordering unimportant) */
+	ulong	dst;		/* Ip destination (byte ordering unimportant) */
+};
+
+struct Tcphdr
+{
+	ulong	ports;		/* defined as a ulong to make comparisons easier */
+	uchar	seq[4];
+	uchar	ack[4];
+	uchar	flag[2];
+	uchar	win[2];
+	uchar	cksum[2];
+	uchar	urg[2];
+};
+
+struct Ilhdr
+{
+	uchar	sum[2];	/* Checksum including header */
+	uchar	len[2];	/* Packet length */
+	uchar	type;		/* Packet type */
+	uchar	spec;		/* Special */
+	uchar	src[2];	/* Src port */
+	uchar	dst[2];	/* Dst port */
+	uchar	id[4];	/* Sequence id */
+	uchar	ack[4];	/* Acked sequence */
+};
+
+enum
+{
+	URG		= 0x20,		/* Data marked urgent */
+	ACK		= 0x10,		/* Aknowledge is valid */
+	PSH		= 0x08,		/* Whole data pipe is pushed */
+	RST		= 0x04,		/* Reset connection */
+	SYN		= 0x02,		/* Pkt. is synchronise */
+	FIN		= 0x01,		/* Start close down */
+
+	IP_DF		= 0x4000,	/* Don't fragment */
+
+	IP_TCPPROTO	= 6,
+	IP_ILPROTO	= 40,
+	IL_IPHDR	= 20,
+};
+
+struct Hdr
+{
+	uchar	buf[128];
+	Iphdr	*ip;
+	Tcphdr	*tcp;
+	int	len;
+};
+
+struct Tcpc
+{
+	uchar	lastrecv;
+	uchar	lastxmit;
+	uchar	basexmit;
+	uchar	err;
+	uchar	compressid;
+	Hdr	t[MAX_STATES];
+	Hdr	r[MAX_STATES];
+};
+
+enum
+{	/* flag bits for what changed in a packet */
+	NEW_U=(1<<0),	/* tcp only */
+	NEW_W=(1<<1),	/* tcp only */
+	NEW_A=(1<<2),	/* il tcp */
+	NEW_S=(1<<3),	/* tcp only */
+	NEW_P=(1<<4),	/* tcp only */
+	NEW_I=(1<<5),	/* il tcp */
+	NEW_C=(1<<6),	/* il tcp */
+	NEW_T=(1<<7),	/* il only */
+	TCP_PUSH_BIT	= 0x10,
+};
+
+/* reserved, special-case values of above for tcp */
+#define SPECIAL_I (NEW_S|NEW_W|NEW_U)		/* echoed interactive traffic */
+#define SPECIAL_D (NEW_S|NEW_A|NEW_W|NEW_U)	/* unidirectional data */
+#define SPECIALS_MASK (NEW_S|NEW_A|NEW_W|NEW_U)
+
+int
+encode(void *p, ulong n)
+{
+	uchar	*cp;
+
+	cp = p;
+	if(n >= 256 || n == 0) {
+		*cp++ = 0;
+		cp[0] = n >> 8;
+		cp[1] = n;
+		return 3;
+	} else 
+		*cp = n;
+	return 1;
+}
+
+#define DECODEL(f) { \
+	if (*cp == 0) {\
+		hnputl(f, nhgetl(f) + ((cp[1] << 8) | cp[2])); \
+		cp += 3; \
+	} else { \
+		hnputl(f, nhgetl(f) + (ulong)*cp++); \
+	} \
+}
+#define DECODES(f) { \
+	if (*cp == 0) {\
+		hnputs(f, nhgets(f) + ((cp[1] << 8) | cp[2])); \
+		cp += 3; \
+	} else { \
+		hnputs(f, nhgets(f) + (ulong)*cp++); \
+	} \
+}
+
+ushort
+tcpcompress(Tcpc *comp, Block *b, Fs *)
+{
+	Iphdr	*ip;		/* current packet */
+	Tcphdr	*tcp;		/* current pkt */
+	ulong 	iplen, tcplen, hlen;	/* header length in bytes */
+	ulong 	deltaS, deltaA;	/* general purpose temporaries */
+	ulong 	changes;	/* change mask */
+	uchar	new_seq[16];	/* changes from last to current */
+	uchar	*cp;
+	Hdr	*h;		/* last packet */
+	int 	i, j;
+
+	/*
+	 * Bail if this is not a compressible TCP/IP packet
+	 */
+	ip = (Iphdr*)b->rp;
+	iplen = (ip->vihl & 0xf) << 2;
+	tcp = (Tcphdr*)(b->rp + iplen);
+	tcplen = (tcp->flag[0] & 0xf0) >> 2;
+	hlen = iplen + tcplen;
+	if((tcp->flag[1] & (SYN|FIN|RST|ACK)) != ACK)
+		return Pip;	/* connection control */
+
+	/*
+	 * Packet is compressible, look for a connection
+	 */
+	changes = 0;
+	cp = new_seq;
+	j = comp->lastxmit;
+	h = &comp->t[j];
+	if(ip->src != h->ip->src || ip->dst != h->ip->dst
+	|| tcp->ports != h->tcp->ports) {
+		for(i = 0; i < MAX_STATES; ++i) {
+			j = (comp->basexmit + i) % MAX_STATES;
+			h = &comp->t[j];
+			if(ip->src == h->ip->src && ip->dst == h->ip->dst
+			&& tcp->ports == h->tcp->ports)
+				goto found;
+		}
+
+		/* no connection, reuse the oldest */
+		if(i == MAX_STATES) {
+			j = comp->basexmit;
+			j = (j + MAX_STATES - 1) % MAX_STATES;
+			comp->basexmit = j;
+			h = &comp->t[j];
+			goto raise;
+		}
+	}
+found:
+
+	/*
+	 * Make sure that only what we expect to change changed. 
+	 */
+	if(ip->vihl  != h->ip->vihl || ip->tos   != h->ip->tos ||
+	   ip->ttl   != h->ip->ttl  || ip->proto != h->ip->proto)
+		goto raise;	/* headers changed */
+	if(iplen != sizeof(Iphdr) && memcmp(ip+1, h->ip+1, iplen - sizeof(Iphdr)))
+		goto raise;	/* ip options changed */
+	if(tcplen != sizeof(Tcphdr) && memcmp(tcp+1, h->tcp+1, tcplen - sizeof(Tcphdr)))
+		goto raise;	/* tcp options changed */
+
+	if(tcp->flag[1] & URG) {
+		cp += encode(cp, nhgets(tcp->urg));
+		changes |= NEW_U;
+	} else if(memcmp(tcp->urg, h->tcp->urg, sizeof(tcp->urg)) != 0)
+		goto raise;
+	if(deltaS = nhgets(tcp->win) - nhgets(h->tcp->win)) {
+		cp += encode(cp, deltaS);
+		changes |= NEW_W;
+	}
+	if(deltaA = nhgetl(tcp->ack) - nhgetl(h->tcp->ack)) {
+		if(deltaA > 0xffff)
+			goto raise;
+		cp += encode(cp, deltaA);
+		changes |= NEW_A;
+	}
+	if(deltaS = nhgetl(tcp->seq) - nhgetl(h->tcp->seq)) {
+		if (deltaS > 0xffff)
+			goto raise;
+		cp += encode(cp, deltaS);
+		changes |= NEW_S;
+	}
+
+	/*
+	 * Look for the special-case encodings.
+	 */
+	switch(changes) {
+	case 0:
+		/*
+		 * Nothing changed. If this packet contains data and the last
+		 * one didn't, this is probably a data packet following an
+		 * ack (normal on an interactive connection) and we send it
+		 * compressed. Otherwise it's probably a retransmit,
+		 * retransmitted ack or window probe.  Send it uncompressed
+		 * in case the other side missed the compressed version.
+		 */
+		if(nhgets(ip->length) == nhgets(h->ip->length) ||
+		   nhgets(h->ip->length) != hlen)
+			goto raise;
+		break;
+	case SPECIAL_I:
+	case SPECIAL_D:
+		/*
+		 * Actual changes match one of our special case encodings --
+		 * send packet uncompressed.
+		 */
+		goto raise;
+	case NEW_S | NEW_A:
+		if (deltaS == deltaA &&
+			deltaS == nhgets(h->ip->length) - hlen) {
+			/* special case for echoed terminal traffic */
+			changes = SPECIAL_I;
+			cp = new_seq;
+		}
+		break;
+	case NEW_S:
+		if (deltaS == nhgets(h->ip->length) - hlen) {
+			/* special case for data xfer */
+			changes = SPECIAL_D;
+			cp = new_seq;
+		}
+		break;
+	}
+	deltaS = nhgets(ip->id) - nhgets(h->ip->id);
+	if(deltaS != 1) {
+		cp += encode(cp, deltaS);
+		changes |= NEW_I;
+	}
+	if (tcp->flag[1] & PSH)
+		changes |= TCP_PUSH_BIT;
+	/*
+	 * Grab the cksum before we overwrite it below. Then update our
+	 * state with this packet's header.
+	 */
+	deltaA = nhgets(tcp->cksum);
+	memmove(h->buf, b->rp, hlen);
+	h->len = hlen;
+	h->tcp = (Tcphdr*)(h->buf + iplen);
+
+	/*
+	 * We want to use the original packet as our compressed packet. (cp -
+	 * new_seq) is the number of bytes we need for compressed sequence
+	 * numbers. In addition we need one byte for the change mask, one
+	 * for the connection id and two for the tcp checksum. So, (cp -
+	 * new_seq) + 4 bytes of header are needed. hlen is how many bytes
+	 * of the original packet to toss so subtract the two to get the new
+	 * packet size. The temporaries are gross -egs.
+	 */
+	deltaS = cp - new_seq;
+	cp = b->rp;
+	if(comp->lastxmit != j || comp->compressid == 0) {
+		comp->lastxmit = j;
+		hlen -= deltaS + 4;
+		cp += hlen;
+		*cp++ = (changes | NEW_C);
+		*cp++ = j;
+	} else {
+		hlen -= deltaS + 3;
+		cp += hlen;
+		*cp++ = changes;
+	}
+	b->rp += hlen;
+	hnputs(cp, deltaA);
+	cp += 2;
+	memmove(cp, new_seq, deltaS);
+	return Pvjctcp;
+
+raise:
+	/*
+	 * Update connection state & send uncompressed packet
+	 */
+	memmove(h->buf, b->rp, hlen);
+	h->tcp = (Tcphdr*)(h->buf + iplen);
+	h->len = hlen;
+	h->ip->proto = j;
+	comp->lastxmit = j;
+	return Pvjutcp;
+}
+
+Block*
+tcpuncompress(Tcpc *comp, Block *b, ushort type, Fs *f)
+{
+	uchar	*cp, changes;
+	int	i;
+	int	iplen, len;
+	Iphdr	*ip;
+	Tcphdr	*tcp;
+	Hdr	*h;
+
+	if(type == Pvjutcp) {
+		/*
+		 *  Locate the saved state for this connection. If the state
+		 *  index is legal, clear the 'discard' flag.
+		 */
+		ip = (Iphdr*)b->rp;
+		if(ip->proto >= MAX_STATES)
+			goto raise;
+		iplen = (ip->vihl & 0xf) << 2;
+		tcp = (Tcphdr*)(b->rp + iplen);
+		comp->lastrecv = ip->proto;
+		len = iplen + ((tcp->flag[0] & 0xf0) >> 2);
+		comp->err = 0;
+netlog(f, Logcompress, "uncompressed %d\n", comp->lastrecv);
+		/*
+		 * Restore the IP protocol field then save a copy of this
+		 * packet header. The checksum is zeroed in the copy so we
+		 * don't have to zero it each time we process a compressed
+		 * packet.
+		 */
+		ip->proto = IP_TCPPROTO;
+		h = &comp->r[comp->lastrecv];
+		memmove(h->buf, b->rp, len);
+		h->tcp = (Tcphdr*)(h->buf + iplen);
+		h->len = len;
+		h->ip->cksum[0] = h->ip->cksum[1] = 0;
+		return b;
+	}
+
+	cp = b->rp;
+	changes = *cp++;
+	if(changes & NEW_C) {
+		/*
+		 * Make sure the state index is in range, then grab the
+		 * state. If we have a good state index, clear the 'discard'
+		 * flag.
+		 */
+		if(*cp >= MAX_STATES)
+			goto raise;
+		comp->err = 0;
+		comp->lastrecv = *cp++;
+netlog(f, Logcompress, "newc %d\n", comp->lastrecv);
+	} else {
+		/*
+		 * This packet has no state index. If we've had a
+		 * line error since the last time we got an explicit state
+		 * index, we have to toss the packet.
+		 */
+		if(comp->err != 0){
+			freeblist(b);
+			return nil;
+		}
+netlog(f, Logcompress, "oldc %d\n", comp->lastrecv);
+	}
+
+	/*
+	 * Find the state then fill in the TCP checksum and PUSH bit.
+	 */
+	h = &comp->r[comp->lastrecv];
+	ip = h->ip;
+	tcp = h->tcp;
+	len = h->len;
+	memmove(tcp->cksum, cp, sizeof tcp->cksum);
+	cp += 2;
+	if(changes & TCP_PUSH_BIT)
+		tcp->flag[1] |= PSH;
+	else
+		tcp->flag[1] &= ~PSH;
+	/*
+	 * Fix up the state's ack, seq, urg and win fields based on the
+	 * changemask.
+	 */
+	switch (changes & SPECIALS_MASK) {
+	case SPECIAL_I:
+		i = nhgets(ip->length) - len;
+		hnputl(tcp->ack, nhgetl(tcp->ack) + i);
+		hnputl(tcp->seq, nhgetl(tcp->seq) + i);
+		break;
+
+	case SPECIAL_D:
+		hnputl(tcp->seq, nhgetl(tcp->seq) + nhgets(ip->length) - len);
+		break;
+
+	default:
+		if(changes & NEW_U) {
+			tcp->flag[1] |= URG;
+			if(*cp == 0){
+				hnputs(tcp->urg, nhgets(cp+1));
+				cp += 3;
+			}else
+				hnputs(tcp->urg, *cp++);
+		} else
+			tcp->flag[1] &= ~URG;
+		if(changes & NEW_W)
+			DECODES(tcp->win)
+		if(changes & NEW_A)
+			DECODEL(tcp->ack)
+		if(changes & NEW_S)
+			DECODEL(tcp->seq)
+		break;
+	}
+
+	/* Update the IP ID */
+	if(changes & NEW_I)
+		DECODES(ip->id)
+	else
+		hnputs(ip->id, nhgets(ip->id) + 1);
+
+	/*
+	 *  At this point, cp points to the first byte of data in the packet.
+	 *  Back up cp by the TCP/IP header length to make room for the
+	 *  reconstructed header.
+	 *  We assume the packet we were handed has enough space to prepend
+	 *  up to 128 bytes of header.
+	 */
+	b->rp = cp;
+	if(b->rp - b->base < len){
+		b = padblock(b, len);
+		b = pullupblock(b, blocklen(b));
+	} else
+		b->rp -= len;
+	hnputs(ip->length, BLEN(b));
+	memmove(b->rp, ip, len);
+	
+	/* recompute the ip header checksum */
+	ip = (Iphdr*)b->rp;
+	hnputs(ip->cksum, ipcsum(b->rp));
+	return b;
+
+raise:
+	netlog(f, Logcompress, "Bad Packet!\n");
+	comp->err = 1;
+	freeblist(b);
+	return nil;
+}
+
+Tcpc*
+compress_init(Tcpc *c)
+{
+	int i;
+	Hdr *h;
+
+	if(c == nil){
+		c = malloc(sizeof(Tcpc));
+		if(c == nil)
+			return nil;
+	}
+	memset(c, 0, sizeof(*c));
+	for(i = 0; i < MAX_STATES; i++){
+		h = &c->t[i];
+		h->ip = (Iphdr*)h->buf;
+		h->tcp = (Tcphdr*)(h->buf + 10);
+		h->len = 20;
+		h = &c->r[i];
+		h->ip = (Iphdr*)h->buf;
+		h->tcp = (Tcphdr*)(h->buf + 10);
+		h->len = 20;
+	}
+
+	return c;
+}
+
+ushort
+compress(Tcpc *tcp, Block *b, Fs *f)
+{
+	Iphdr		*ip;
+
+	/*
+	 * Bail if this is not a compressible IP packet
+	 */
+	ip = (Iphdr*)b->rp;
+	if((nhgets(ip->frag) & 0x3fff) != 0)
+		return Pip;
+
+	switch(ip->proto) {
+	case IP_TCPPROTO:
+		return tcpcompress(tcp, b, f);
+	default:
+		return Pip;
+	}
+}
+
+int
+compress_negotiate(Tcpc *tcp, uchar *data)
+{
+	if(data[0] != MAX_STATES - 1)
+		return -1;
+	tcp->compressid = data[1];
+	return 0;
+}
--- /dev/null
+++ b/os/ip/devip.c
@@ -1,0 +1,1417 @@
+#include	"u.h"
+#include	"../port/lib.h"
+#include	"mem.h"
+#include	"dat.h"
+#include	"fns.h"
+#include	"../port/error.h"
+#include	"../ip/ip.h"
+
+enum
+{
+	Qtopdir=	1,		/* top level directory */
+	Qtopbase,
+	Qarp=		Qtopbase,
+	Qbootp,
+	Qndb,
+	Qiproute,
+	Qiprouter,
+	Qipselftab,
+	Qlog,
+
+	Qprotodir,			/* directory for a protocol */
+	Qprotobase,
+	Qclone=		Qprotobase,
+	Qstats,
+
+	Qconvdir,			/* directory for a conversation */
+	Qconvbase,
+	Qctl=		Qconvbase,
+	Qdata,
+	Qerr,
+	Qlisten,
+	Qlocal,
+	Qremote,
+	Qstatus,
+	Qsnoop,
+
+	Logtype=	5,
+	Masktype=	(1<<Logtype)-1,
+	Logconv=	12,
+	Maskconv=	(1<<Logconv)-1,
+	Shiftconv=	Logtype,
+	Logproto=	8,
+	Maskproto=	(1<<Logproto)-1,
+	Shiftproto=	Logtype + Logconv,
+
+	Nfs=		32,
+};
+#define TYPE(x) 	( ((ulong)(x).path) & Masktype )
+#define CONV(x) 	( (((ulong)(x).path) >> Shiftconv) & Maskconv )
+#define PROTO(x) 	( (((ulong)(x).path) >> Shiftproto) & Maskproto )
+#define QID(p, c, y) 	( ((p)<<(Shiftproto)) | ((c)<<Shiftconv) | (y) )
+
+static char network[] = "network";
+
+QLock	fslock;
+Fs	*ipfs[Nfs];	/* attached fs's */
+Queue	*qlog;
+
+extern	void nullmediumlink(void);
+extern	void pktmediumlink(void);
+static	long ndbwrite(Fs*, char*, ulong, int);
+static	void	closeconv(Conv*);
+
+static int
+ip3gen(Chan *c, int i, Dir *dp)
+{
+	Qid q;
+	Conv *cv;
+	char *p;
+
+	cv = ipfs[c->dev]->p[PROTO(c->qid)]->conv[CONV(c->qid)];
+	if(cv->owner == nil)
+		kstrdup(&cv->owner, eve);
+	mkqid(&q, QID(PROTO(c->qid), CONV(c->qid), i), 0, QTFILE);
+
+	switch(i) {
+	default:
+		return -1;
+	case Qctl:
+		devdir(c, q, "ctl", 0, cv->owner, cv->perm, dp);
+		return 1;
+	case Qdata:
+		devdir(c, q, "data", qlen(cv->rq), cv->owner, cv->perm, dp);
+		return 1;
+	case Qerr:
+		devdir(c, q, "err", qlen(cv->eq), cv->owner, cv->perm, dp);
+		return 1;
+	case Qlisten:
+		devdir(c, q, "listen", 0, cv->owner, cv->perm, dp);
+		return 1;
+	case Qlocal:
+		p = "local";
+		break;
+	case Qremote:
+		p = "remote";
+		break;
+	case Qsnoop:
+		if(strcmp(cv->p->name, "ipifc") != 0)
+			return -1;
+		devdir(c, q, "snoop", qlen(cv->sq), cv->owner, 0400, dp);
+		return 1;
+	case Qstatus:
+		p = "status";
+		break;
+	}
+	devdir(c, q, p, 0, cv->owner, 0444, dp);
+	return 1;
+}
+
+static int
+ip2gen(Chan *c, int i, Dir *dp)
+{
+	Qid q;
+
+	switch(i) {
+	case Qclone:
+		mkqid(&q, QID(PROTO(c->qid), 0, Qclone), 0, QTFILE);
+		devdir(c, q, "clone", 0, network, 0666, dp);
+		return 1;
+	case Qstats:
+		mkqid(&q, QID(PROTO(c->qid), 0, Qstats), 0, QTFILE);
+		devdir(c, q, "stats", 0, network, 0444, dp);
+		return 1;
+	}	
+	return -1;
+}
+
+static int
+ip1gen(Chan *c, int i, Dir *dp)
+{
+	Qid q;
+	char *p;
+	int prot;
+	int len = 0;
+	Fs *f;
+	extern ulong	kerndate;
+
+	f = ipfs[c->dev];
+
+	prot = 0666;
+	mkqid(&q, QID(0, 0, i), 0, QTFILE);
+	switch(i) {
+	default:
+		return -1;
+	case Qarp:
+		p = "arp";
+		break;
+	case Qbootp:
+		p = "bootp";
+		if(bootp == nil)
+			return 0;
+		break;
+	case Qndb:
+		p = "ndb";
+		len = strlen(f->ndb);
+		q.vers = f->ndbvers;
+		break;
+	case Qiproute:
+		p = "iproute";
+		break;
+	case Qipselftab:
+		p = "ipselftab";
+		prot = 0444;
+		break;
+	case Qiprouter:
+		p = "iprouter";
+		break;
+	case Qlog:
+		p = "log";
+		break;
+	}
+	devdir(c, q, p, len, network, prot, dp);
+	if(i == Qndb && f->ndbmtime > kerndate)
+		dp->mtime = f->ndbmtime;
+	return 1;
+}
+
+static int
+ipgen(Chan *c, char*, Dirtab*, int, int s, Dir *dp)
+{
+	Qid q;
+	Conv *cv;
+	Fs *f;
+
+	f = ipfs[c->dev];
+
+	switch(TYPE(c->qid)) {
+	case Qtopdir:
+		if(s == DEVDOTDOT){
+			mkqid(&q, QID(0, 0, Qtopdir), 0, QTDIR);
+			sprint(up->genbuf, "#I%lud", c->dev);
+			devdir(c, q, up->genbuf, 0, network, 0555, dp);
+			return 1;
+		}
+		if(s < f->np) {
+			if(f->p[s]->connect == nil)
+				return 0;	/* protocol with no user interface */
+			mkqid(&q, QID(s, 0, Qprotodir), 0, QTDIR);
+			devdir(c, q, f->p[s]->name, 0, network, 0555, dp);
+			return 1;
+		}
+		s -= f->np;
+		return ip1gen(c, s+Qtopbase, dp);
+	case Qarp:
+	case Qbootp:
+	case Qndb:
+	case Qlog:
+	case Qiproute:
+	case Qiprouter:
+	case Qipselftab:
+		return ip1gen(c, TYPE(c->qid), dp);
+	case Qprotodir:
+		if(s == DEVDOTDOT){
+			mkqid(&q, QID(0, 0, Qtopdir), 0, QTDIR);
+			sprint(up->genbuf, "#I%lud", c->dev);
+			devdir(c, q, up->genbuf, 0, network, 0555, dp);
+			return 1;
+		}
+		if(s < f->p[PROTO(c->qid)]->ac) {
+			cv = f->p[PROTO(c->qid)]->conv[s];
+			sprint(up->genbuf, "%d", s);
+			mkqid(&q, QID(PROTO(c->qid), s, Qconvdir), 0, QTDIR);
+			devdir(c, q, up->genbuf, 0, cv->owner, 0555, dp);
+			return 1;
+		}
+		s -= f->p[PROTO(c->qid)]->ac;
+		return ip2gen(c, s+Qprotobase, dp);
+	case Qclone:
+	case Qstats:
+		return ip2gen(c, TYPE(c->qid), dp);
+	case Qconvdir:
+		if(s == DEVDOTDOT){
+			s = PROTO(c->qid);
+			mkqid(&q, QID(s, 0, Qprotodir), 0, QTDIR);
+			devdir(c, q, f->p[s]->name, 0, network, 0555, dp);
+			return 1;
+		}
+		return ip3gen(c, s+Qconvbase, dp);
+	case Qctl:
+	case Qdata:
+	case Qerr:
+	case Qlisten:
+	case Qlocal:
+	case Qremote:
+	case Qstatus:
+	case Qsnoop:
+		return ip3gen(c, TYPE(c->qid), dp);
+	}
+	return -1;
+}
+
+static void
+ipreset(void)
+{
+	nullmediumlink();
+	pktmediumlink();
+
+	fmtinstall('i', eipfmt);
+	fmtinstall('I', eipfmt);
+	fmtinstall('E', eipfmt);
+	fmtinstall('V', eipfmt);
+	fmtinstall('M', eipfmt);
+}
+
+static Fs*
+ipgetfs(int dev)
+{
+	extern void (*ipprotoinit[])(Fs*);
+	Fs *f;
+	int i;
+
+	if(dev >= Nfs)
+		return nil;
+
+	qlock(&fslock);
+	if(ipfs[dev] == nil){
+		f = smalloc(sizeof(Fs));
+		ip_init(f);
+		arpinit(f);
+		netloginit(f);
+		for(i = 0; ipprotoinit[i]; i++)
+			ipprotoinit[i](f);
+		f->dev = dev;
+		ipfs[dev] = f;
+	}
+	qunlock(&fslock);
+
+	return ipfs[dev];
+}
+
+IPaux*
+newipaux(char *owner, char *tag)
+{
+	IPaux *a;
+	int n;
+
+	a = smalloc(sizeof(*a));
+	kstrdup(&a->owner, owner);
+	memset(a->tag, ' ', sizeof(a->tag));
+	n = strlen(tag);
+	if(n > sizeof(a->tag))
+		n = sizeof(a->tag);
+	memmove(a->tag, tag, n);
+	return a;
+}
+
+#define ATTACHER(c) (((IPaux*)((c)->aux))->owner)
+
+static Chan*
+ipattach(char* spec)
+{
+	Chan *c;
+	int dev;
+
+	dev = atoi(spec);
+	if(dev >= Nfs)
+		error("bad specification");
+
+	ipgetfs(dev);
+	c = devattach('I', spec);
+	mkqid(&c->qid, QID(0, 0, Qtopdir), 0, QTDIR);
+	c->dev = dev;
+
+	c->aux = newipaux(commonuser(), "none");
+
+	return c;
+}
+
+static Walkqid*
+ipwalk(Chan* c, Chan *nc, char **name, int nname)
+{
+	IPaux *a = c->aux;
+	Walkqid* w;
+
+	w = devwalk(c, nc, name, nname, nil, 0, ipgen);
+	if(w != nil && w->clone != nil)
+		w->clone->aux = newipaux(a->owner, a->tag);
+	return w;
+}
+
+static int
+ipstat(Chan* c, uchar* db, int n)
+{
+	return devstat(c, db, n, nil, 0, ipgen);
+}
+
+static int
+incoming(void* arg)
+{
+	Conv *conv;
+
+	conv = arg;
+	return conv->incall != nil;
+}
+
+static int m2p[] = {
+	[OREAD]		4,
+	[OWRITE]	2,
+	[ORDWR]		6
+};
+
+static Chan*
+ipopen(Chan* c, int omode)
+{
+	Conv *cv, *nc;
+	Proto *p;
+	int perm;
+	Fs *f;
+
+	perm = m2p[omode&3];
+
+	f = ipfs[c->dev];
+
+	switch(TYPE(c->qid)) {
+	default:
+		break;
+	case Qndb:
+		if(omode & (OWRITE|OTRUNC) && !iseve())
+			error(Eperm);
+		if((omode & (OWRITE|OTRUNC)) == (OWRITE|OTRUNC))
+			f->ndb[0] = 0;
+		break;
+	case Qlog:
+		netlogopen(f);
+		break;
+	case Qiprouter:
+		iprouteropen(f);
+		break;
+	case Qiproute:
+		break;
+	case Qtopdir:
+	case Qprotodir:
+	case Qconvdir:
+	case Qstatus:
+	case Qremote:
+	case Qlocal:
+	case Qstats:
+	case Qbootp:
+	case Qipselftab:
+		if(omode != OREAD)
+			error(Eperm);
+		break;
+	case Qsnoop:
+		if(omode != OREAD)
+			error(Eperm);
+		p = f->p[PROTO(c->qid)];
+		cv = p->conv[CONV(c->qid)];
+		if(strcmp(ATTACHER(c), cv->owner) != 0 && !iseve())
+			error(Eperm);
+		incref(&cv->snoopers);
+		break;
+	case Qclone:
+		p = f->p[PROTO(c->qid)];
+		qlock(p);
+		if(waserror()){
+			qunlock(p);
+			nexterror();
+		}
+		cv = Fsprotoclone(p, ATTACHER(c));
+		qunlock(p);
+		poperror();
+		if(cv == nil) {
+			error(Enodev);
+			break;
+		}
+		mkqid(&c->qid, QID(p->x, cv->x, Qctl), 0, QTFILE);
+		break;
+	case Qdata:
+	case Qctl:
+	case Qerr:
+		p = f->p[PROTO(c->qid)];
+		qlock(p);
+		cv = p->conv[CONV(c->qid)];
+		qlock(cv);
+		if(waserror()) {
+			qunlock(cv);
+			qunlock(p);
+			nexterror();
+		}
+		if((perm & (cv->perm>>6)) != perm) {
+			if(strcmp(ATTACHER(c), cv->owner) != 0)
+				error(Eperm);
+		 	if((perm & cv->perm) != perm)
+				error(Eperm); 
+
+		}
+		cv->inuse++;
+		if(cv->inuse == 1){
+			kstrdup(&cv->owner, ATTACHER(c));
+			cv->perm = 0660;
+		}
+		qunlock(cv);
+		qunlock(p);
+		poperror();
+		break;
+	case Qlisten:
+		cv = f->p[PROTO(c->qid)]->conv[CONV(c->qid)];
+		if((perm & (cv->perm>>6)) != perm) {
+			if(strcmp(ATTACHER(c), cv->owner) != 0)
+				error(Eperm);
+		 	if((perm & cv->perm) != perm)
+				error(Eperm); 
+
+		}
+
+		if(cv->state != Announced)
+			error("not announced");
+
+		if(waserror()){
+			closeconv(cv);
+			nexterror();
+		}
+		qlock(cv);
+		cv->inuse++;
+		qunlock(cv);
+
+		nc = nil;
+		while(nc == nil) {
+			/* give up if we got a hangup */
+			if(qisclosed(cv->rq))
+				error("listen hungup");
+
+			qlock(&cv->listenq);
+			if(waserror()) {
+				qunlock(&cv->listenq);
+				nexterror();
+			}
+
+			/* wait for a connect */
+			sleep(&cv->listenr, incoming, cv);
+
+			qlock(cv);
+			nc = cv->incall;
+			if(nc != nil){
+				cv->incall = nc->next;
+				mkqid(&c->qid, QID(PROTO(c->qid), nc->x, Qctl), 0, QTFILE);
+				kstrdup(&cv->owner, ATTACHER(c));
+			}
+			qunlock(cv);
+
+			qunlock(&cv->listenq);
+			poperror();
+		}
+		closeconv(cv);
+		poperror();
+		break;
+	}
+	c->mode = openmode(omode);
+	c->flag |= COPEN;
+	c->offset = 0;
+	return c;
+}
+
+static int
+ipwstat(Chan *c, uchar *dp, int n)
+{
+	Dir *d;
+	Conv *cv;
+	Fs *f;
+	Proto *p;
+
+	f = ipfs[c->dev];
+	switch(TYPE(c->qid)) {
+	default:
+		error(Eperm);
+		break;
+	case Qctl:
+	case Qdata:
+		break;
+	}
+
+	d = smalloc(sizeof(*d)+n);
+	if(waserror()){
+		free(d);
+		nexterror();
+	}
+	n = convM2D(dp, n, d, (char*)&d[1]);
+	if(n == 0)
+		error(Eshortstat);
+	p = f->p[PROTO(c->qid)];
+	cv = p->conv[CONV(c->qid)];
+	if(!iseve() && strcmp(ATTACHER(c), cv->owner) != 0)
+		error(Eperm);
+	if(!emptystr(d->uid))
+		kstrdup(&cv->owner, d->uid);
+	if(d->mode != ~0UL)
+		cv->perm = d->mode & 0777;
+	poperror();
+	free(d);
+	return n;
+}
+
+static void
+closeconv(Conv *cv)
+{
+	Conv *nc;
+	Ipmulti *mp;
+
+	qlock(cv);
+
+	if(--cv->inuse > 0) {
+		qunlock(cv);
+		return;
+	}
+
+	/* close all incoming calls since no listen will ever happen */
+	for(nc = cv->incall; nc; nc = cv->incall){
+		cv->incall = nc->next;
+		closeconv(nc);
+	}
+	cv->incall = nil;
+
+	kstrdup(&cv->owner, network);
+	cv->perm = 0660;
+
+	while((mp = cv->multi) != nil)
+		ipifcremmulti(cv, mp->ma, mp->ia);
+
+	cv->r = nil;
+	cv->rgen = 0;
+	cv->p->close(cv);
+	cv->state = Idle;
+	qunlock(cv);
+}
+
+static void
+ipclose(Chan* c)
+{
+	Fs *f;
+
+	f = ipfs[c->dev];
+	switch(TYPE(c->qid)) {
+	default:
+		break;
+	case Qlog:
+		if(c->flag & COPEN)
+			netlogclose(f);
+		break;
+	case Qiprouter:
+		if(c->flag & COPEN)
+			iprouterclose(f);
+		break;
+	case Qdata:
+	case Qctl:
+	case Qerr:
+		if(c->flag & COPEN)
+			closeconv(f->p[PROTO(c->qid)]->conv[CONV(c->qid)]);
+		break;
+	case Qsnoop:
+		if(c->flag & COPEN)
+			decref(&f->p[PROTO(c->qid)]->conv[CONV(c->qid)]->snoopers);
+		break;
+	}
+	free(((IPaux*)c->aux)->owner);
+	free(c->aux);
+}
+
+enum
+{
+	Statelen=	32*1024,
+};
+
+static long
+ipread(Chan *ch, void *a, long n, vlong off)
+{
+	Conv *c;
+	Proto *x;
+	char *buf, *p;
+	long rv;
+	Fs *f;
+	ulong offset = off;
+
+	f = ipfs[ch->dev];
+
+	p = a;
+	switch(TYPE(ch->qid)) {
+	default:
+		error(Eperm);
+	case Qtopdir:
+	case Qprotodir:
+	case Qconvdir:
+		return devdirread(ch, a, n, 0, 0, ipgen);
+	case Qarp:
+		return arpread(f->arp, a, offset, n);
+ 	case Qbootp:
+ 		return bootpread(a, offset, n);
+ 	case Qndb:
+		return readstr(offset, a, n, f->ndb);
+	case Qiproute:
+		return routeread(f, a, offset, n);
+	case Qiprouter:
+		return iprouterread(f, a, n);
+	case Qipselftab:
+		return ipselftabread(f, a, offset, n);
+	case Qlog:
+		return netlogread(f, a, offset, n);
+	case Qctl:
+		sprint(up->genbuf, "%lud", CONV(ch->qid));
+		return readstr(offset, p, n, up->genbuf);
+	case Qremote:
+		buf = smalloc(Statelen);
+		x = f->p[PROTO(ch->qid)];
+		c = x->conv[CONV(ch->qid)];
+		if(x->remote == nil) {
+			sprint(buf, "%I!%d\n", c->raddr, c->rport);
+		} else {
+			(*x->remote)(c, buf, Statelen-2);
+		}
+		rv = readstr(offset, p, n, buf);
+		free(buf);
+		return rv;
+	case Qlocal:
+		buf = smalloc(Statelen);
+		x = f->p[PROTO(ch->qid)];
+		c = x->conv[CONV(ch->qid)];
+		if(x->local == nil) {
+			sprint(buf, "%I!%d\n", c->laddr, c->lport);
+		} else {
+			(*x->local)(c, buf, Statelen-2);
+		}
+		rv = readstr(offset, p, n, buf);
+		free(buf);
+		return rv;
+	case Qstatus:
+		buf = smalloc(Statelen);
+		x = f->p[PROTO(ch->qid)];
+		c = x->conv[CONV(ch->qid)];
+		(*x->state)(c, buf, Statelen-2);
+		rv = readstr(offset, p, n, buf);
+		free(buf);
+		return rv;
+	case Qdata:
+		c = f->p[PROTO(ch->qid)]->conv[CONV(ch->qid)];
+		return qread(c->rq, a, n);
+	case Qerr:
+		c = f->p[PROTO(ch->qid)]->conv[CONV(ch->qid)];
+		return qread(c->eq, a, n);
+	case Qsnoop:
+		c = f->p[PROTO(ch->qid)]->conv[CONV(ch->qid)];
+		return qread(c->sq, a, n);
+	case Qstats:
+		x = f->p[PROTO(ch->qid)];
+		if(x->stats == nil)
+			error("stats not implemented");
+		buf = smalloc(Statelen);
+		(*x->stats)(x, buf, Statelen);
+		rv = readstr(offset, p, n, buf);
+		free(buf);
+		return rv;
+	}
+}
+
+static Block*
+ipbread(Chan* ch, long n, ulong offset)
+{
+	Conv *c;
+	Proto *x;
+	Fs *f;
+
+	switch(TYPE(ch->qid)){
+	case Qdata:
+		f = ipfs[ch->dev];
+		x = f->p[PROTO(ch->qid)];
+		c = x->conv[CONV(ch->qid)];
+		return qbread(c->rq, n);
+	default:
+		return devbread(ch, n, offset);
+	}
+}
+
+/*
+ *  set local address to be that of the ifc closest to remote address
+ */
+static void
+setladdr(Conv* c)
+{
+	findlocalip(c->p->f, c->laddr, c->raddr);
+}
+
+/*
+ *  set a local port making sure the quad of raddr,rport,laddr,lport is unique
+ */
+static char*
+setluniqueport(Conv* c, int lport)
+{
+	Proto *p;
+	Conv *xp;
+	int x;
+
+	p = c->p;
+
+	qlock(p);
+	for(x = 0; x < p->nc; x++){
+		xp = p->conv[x];
+		if(xp == nil)
+			break;
+		if(xp == c)
+			continue;
+		if((xp->state == Connected || xp->state == Announced)
+		&& xp->lport == lport
+		&& xp->rport == c->rport
+		&& ipcmp(xp->raddr, c->raddr) == 0
+		&& ipcmp(xp->laddr, c->laddr) == 0){
+			qunlock(p);
+			return "address in use";
+		}
+	}
+	c->lport = lport;
+	qunlock(p);
+	return nil;
+}
+
+/*
+ *  pick a local port and set it
+ */
+static void
+setlport(Conv* c)
+{
+	Proto *p;
+	ushort *pp;
+	int x, found;
+
+	p = c->p;
+	if(c->restricted)
+		pp = &p->nextrport;
+	else
+		pp = &p->nextport;
+	qlock(p);
+	for(;;(*pp)++){
+		/*
+		 * Fsproto initialises p->nextport to 0 and the restricted
+		 * ports (p->nextrport) to 600.
+		 * Restricted ports must lie between 600 and 1024.
+		 * For the initial condition or if the unrestricted port number
+		 * has wrapped round, select a random port between 5000 and 1<<15
+		 * to start at.
+		 */
+		if(c->restricted){
+			if(*pp >= 1024)
+				*pp = 600;
+		}
+		else while(*pp < 5000)
+			*pp = nrand(1<<15);
+
+		found = 0;
+		for(x = 0; x < p->nc; x++){
+			if(p->conv[x] == nil)
+				break;
+			if(p->conv[x]->lport == *pp){
+				found = 1;
+				break;
+			}
+		}
+		if(!found)
+			break;
+	}
+	c->lport = (*pp)++;
+	qunlock(p);
+}
+
+/*
+ *  set a local address and port from a string of the form
+ *	[address!]port[!r]
+ */
+static char*
+setladdrport(Conv* c, char* str, int announcing)
+{
+	char *p;
+	char *rv;
+	ushort lport;
+	uchar addr[IPaddrlen];
+
+	rv = nil;
+
+	/*
+	 *  ignore restricted part if it exists.  it's
+	 *  meaningless on local ports.
+	 */
+	p = strchr(str, '!');
+	if(p != nil){
+		*p++ = 0;
+		if(strcmp(p, "r") == 0)
+			p = nil;
+	}
+
+	c->lport = 0;
+	if(p == nil){
+		if(announcing)
+			ipmove(c->laddr, IPnoaddr);
+		else
+			setladdr(c);
+		p = str;
+	} else {
+		if(strcmp(str, "*") == 0)
+			ipmove(c->laddr, IPnoaddr);
+		else {
+			parseip(addr, str);
+			if(ipforme(c->p->f, addr))
+				ipmove(c->laddr, addr);
+			else
+				return "not a local IP address";
+		}
+	}
+
+	/* one process can get all connections */
+	if(announcing && strcmp(p, "*") == 0){
+		if(!iseve())
+			error(Eperm);
+		return setluniqueport(c, 0);
+	}
+
+	lport = atoi(p);
+	if(lport <= 0)
+		setlport(c);
+	else
+		rv = setluniqueport(c, lport);
+	return rv;
+}
+
+static char*
+setraddrport(Conv* c, char* str)
+{
+	char *p;
+
+	p = strchr(str, '!');
+	if(p == nil)
+		return "malformed address";
+	*p++ = 0;
+	parseip(c->raddr, str);
+	c->rport = atoi(p);
+	p = strchr(p, '!');
+	if(p){
+		if(strstr(p, "!r") != nil)
+			c->restricted = 1;
+	}
+	return nil;
+}
+
+/*
+ *  called by protocol connect routine to set addresses
+ */
+char*
+Fsstdconnect(Conv *c, char *argv[], int argc)
+{
+	char *p;
+
+	switch(argc) {
+	default:
+		return "bad args to connect";
+	case 2:
+		p = setraddrport(c, argv[1]);
+		if(p != nil)
+			return p;
+		setladdr(c);
+		setlport(c);
+		break;
+	case 3:
+		p = setraddrport(c, argv[1]);
+		if(p != nil)
+			return p;
+		p = setladdrport(c, argv[2], 0);
+		if(p != nil)
+			return p;
+	}
+
+	if((memcmp(c->raddr, v4prefix, IPv4off) == 0 &&
+		memcmp(c->laddr, v4prefix, IPv4off) == 0)
+		|| ipcmp(c->raddr, IPnoaddr) == 0)
+		c->ipversion = V4;
+	else
+		c->ipversion = V6;
+
+	return nil;
+}
+/*
+ *  initiate connection and sleep till its set up
+ */
+static int
+connected(void* a)
+{
+	return ((Conv*)a)->state == Connected;
+}
+static void
+connectctlmsg(Proto *x, Conv *c, Cmdbuf *cb)
+{
+	char *p;
+
+	if(c->state != 0)
+		error(Econinuse);
+	c->state = Connecting;
+	c->cerr[0] = '\0';
+	if(x->connect == nil)
+		error("connect not supported");
+	p = x->connect(c, cb->f, cb->nf);
+	if(p != nil)
+		error(p);
+
+	qunlock(c);
+	if(waserror()){
+		qlock(c);
+		nexterror();
+	}
+	sleep(&c->cr, connected, c);
+	qlock(c);
+	poperror();
+
+	if(c->cerr[0] != '\0')
+		error(c->cerr);
+}
+
+/*
+ *  called by protocol announce routine to set addresses
+ */
+char*
+Fsstdannounce(Conv* c, char* argv[], int argc)
+{
+	memset(c->raddr, 0, sizeof(c->raddr));
+	c->rport = 0;
+	switch(argc){
+	default:
+		return "bad args to announce";
+	case 2:
+		return setladdrport(c, argv[1], 1);
+	}
+}
+
+/*
+ *  initiate announcement and sleep till its set up
+ */
+static int
+announced(void* a)
+{
+	return ((Conv*)a)->state == Announced;
+}
+static void
+announcectlmsg(Proto *x, Conv *c, Cmdbuf *cb)
+{
+	char *p;
+
+	if(c->state != 0)
+		error(Econinuse);
+	c->state = Announcing;
+	c->cerr[0] = '\0';
+	if(x->announce == nil)
+		error("announce not supported");
+	p = x->announce(c, cb->f, cb->nf);
+	if(p != nil)
+		error(p);
+
+	qunlock(c);
+	if(waserror()){
+		qlock(c);
+		nexterror();
+	}
+	sleep(&c->cr, announced, c);
+	qlock(c);
+	poperror();
+
+	if(c->cerr[0] != '\0')
+		error(c->cerr);
+}
+
+/*
+ *  called by protocol bind routine to set addresses
+ */
+char*
+Fsstdbind(Conv* c, char* argv[], int argc)
+{
+	switch(argc){
+	default:
+		return "bad args to bind";
+	case 2:
+		return setladdrport(c, argv[1], 0);
+	}
+}
+
+static void
+bindctlmsg(Proto *x, Conv *c, Cmdbuf *cb)
+{
+	char *p;
+
+	if(x->bind == nil)
+		p = Fsstdbind(c, cb->f, cb->nf);
+	else
+		p = x->bind(c, cb->f, cb->nf);
+	if(p != nil)
+		error(p);
+}
+
+static void
+tosctlmsg(Conv *c, Cmdbuf *cb)
+{
+	if(cb->nf < 2)
+		c->tos = 0;
+	else
+		c->tos = atoi(cb->f[1]);
+}
+
+static void
+ttlctlmsg(Conv *c, Cmdbuf *cb)
+{
+	if(cb->nf < 2)
+		c->ttl = MAXTTL;
+	else
+		c->ttl = atoi(cb->f[1]);
+}
+
+static long
+ipwrite(Chan* ch, void *v, long n, vlong off)
+{
+	Conv *c;
+	Proto *x;
+	char *p;
+	Cmdbuf *cb;
+	uchar ia[IPaddrlen], ma[IPaddrlen];
+	Fs *f;
+	char *a;
+
+	a = v;
+	f = ipfs[ch->dev];
+
+	switch(TYPE(ch->qid)){
+	default:
+		error(Eperm);
+	case Qdata:
+		x = f->p[PROTO(ch->qid)];
+		c = x->conv[CONV(ch->qid)];
+
+		if(c->wq == nil)
+			error(Eperm);
+
+		qwrite(c->wq, a, n);
+		break;
+	case Qarp:
+		return arpwrite(f, a, n);
+	case Qiproute:
+		return routewrite(f, ch, a, n);
+	case Qlog:
+		netlogctl(f, a, n);
+		return n;
+	case Qndb:
+		return ndbwrite(f, a, off, n);
+	case Qctl:
+		x = f->p[PROTO(ch->qid)];
+		c = x->conv[CONV(ch->qid)];
+		cb = parsecmd(a, n);
+
+		qlock(c);
+		if(waserror()) {
+			qunlock(c);
+			free(cb);
+			nexterror();
+		}
+		if(cb->nf < 1)
+			error("short control request");
+		if(strcmp(cb->f[0], "connect") == 0)
+			connectctlmsg(x, c, cb);
+		else if(strcmp(cb->f[0], "announce") == 0)
+			announcectlmsg(x, c, cb);
+		else if(strcmp(cb->f[0], "bind") == 0)
+			bindctlmsg(x, c, cb);
+		else if(strcmp(cb->f[0], "ttl") == 0)
+			ttlctlmsg(c, cb);
+		else if(strcmp(cb->f[0], "tos") == 0)
+			tosctlmsg(c, cb);
+		else if(strcmp(cb->f[0], "ignoreadvice") == 0)
+			c->ignoreadvice = 1;
+		else if(strcmp(cb->f[0], "addmulti") == 0){
+			if(cb->nf < 2)
+				error("addmulti needs interface address");
+			if(cb->nf == 2){
+				if(!ipismulticast(c->raddr))
+					error("addmulti for a non multicast address");
+				parseip(ia, cb->f[1]);
+				ipifcaddmulti(c, c->raddr, ia);
+			} else {
+				parseip(ma, cb->f[2]);
+				if(!ipismulticast(ma))
+					error("addmulti for a non multicast address");
+				parseip(ia, cb->f[1]);
+				ipifcaddmulti(c, ma, ia);
+			}
+		} else if(strcmp(cb->f[0], "remmulti") == 0){
+			if(cb->nf < 2)
+				error("remmulti needs interface address");
+			if(!ipismulticast(c->raddr))
+				error("remmulti for a non multicast address");
+			parseip(ia, cb->f[1]);
+			ipifcremmulti(c, c->raddr, ia);
+		} else if(x->ctl != nil) {
+			p = x->ctl(c, cb->f, cb->nf);
+			if(p != nil)
+				error(p);
+		} else
+			error("unknown control request");
+		qunlock(c);
+		free(cb);
+		poperror();
+	}
+	return n;
+}
+
+static long
+ipbwrite(Chan* ch, Block* bp, ulong offset)
+{
+	Conv *c;
+	Proto *x;
+	Fs *f;
+	int n;
+
+	switch(TYPE(ch->qid)){
+	case Qdata:
+		f = ipfs[ch->dev];
+		x = f->p[PROTO(ch->qid)];
+		c = x->conv[CONV(ch->qid)];
+
+		if(c->wq == nil)
+			error(Eperm);
+
+		if(bp->next)
+			bp = concatblock(bp);
+		n = BLEN(bp);
+		qbwrite(c->wq, bp);
+		return n;
+	default:
+		return devbwrite(ch, bp, offset);
+	}
+}
+
+Dev ipdevtab = {
+	'I',
+	"ip",
+
+	ipreset,
+	devinit,
+	devshutdown,
+	ipattach,
+	ipwalk,
+	ipstat,
+	ipopen,
+	devcreate,
+	ipclose,
+	ipread,
+	ipbread,
+	ipwrite,
+	ipbwrite,
+	devremove,
+	ipwstat,
+};
+
+int
+Fsproto(Fs *f, Proto *p)
+{
+	if(f->np >= Maxproto)
+		return -1;
+
+	p->f = f;
+
+	if(p->ipproto > 0){
+		if(f->t2p[p->ipproto] != nil)
+			return -1;
+		f->t2p[p->ipproto] = p;
+	}
+
+	p->qid.type = QTDIR;
+	p->qid.path = QID(f->np, 0, Qprotodir);
+	p->conv = malloc(sizeof(Conv*)*(p->nc+1));
+	if(p->conv == nil)
+		panic("Fsproto");
+
+	p->x = f->np;
+	p->nextport = 0;
+	p->nextrport = 600;
+	f->p[f->np++] = p;
+
+	return 0;
+}
+
+/*
+ *  return true if this protocol is
+ *  built in
+ */
+int
+Fsbuiltinproto(Fs* f, uchar proto)
+{
+	return f->t2p[proto] != nil;
+}
+
+/*
+ *  called with protocol locked
+ */
+Conv*
+Fsprotoclone(Proto *p, char *user)
+{
+	Conv *c, **pp, **ep;
+
+retry:
+	c = nil;
+	ep = &p->conv[p->nc];
+	for(pp = p->conv; pp < ep; pp++) {
+		c = *pp;
+		if(c == nil){
+			c = malloc(sizeof(Conv));
+			if(c == nil)
+				error(Enomem);
+			qlock(c);
+			c->p = p;
+			c->x = pp - p->conv;
+			if(p->ptclsize != 0){
+				c->ptcl = malloc(p->ptclsize);
+				if(c->ptcl == nil) {
+					free(c);
+					error(Enomem);
+				}
+			}
+			*pp = c;
+			p->ac++;
+			c->eq = qopen(1024, Qmsg, 0, 0);
+			(*p->create)(c);
+			break;
+		}
+		if(canqlock(c)){
+			/*
+			 *  make sure both processes and protocol
+			 *  are done with this Conv
+			 */
+			if(c->inuse == 0 && (p->inuse == nil || (*p->inuse)(c) == 0))
+				break;
+
+			qunlock(c);
+		}
+	}
+	if(pp >= ep) {
+		if(p->gc != nil && (*p->gc)(p))
+			goto retry;
+		return nil;
+	}
+
+	c->inuse = 1;
+	kstrdup(&c->owner, user);
+	c->perm = 0660;
+	c->state = Idle;
+	ipmove(c->laddr, IPnoaddr);
+	ipmove(c->raddr, IPnoaddr);
+	c->r = nil;
+	c->rgen = 0;
+	c->lport = 0;
+	c->rport = 0;
+	c->restricted = 0;
+	c->ttl = MAXTTL;
+	c->tos = DFLTTOS;
+	qreopen(c->rq);
+	qreopen(c->wq);
+	qreopen(c->eq);
+
+	qunlock(c);
+	return c;
+}
+
+int
+Fsconnected(Conv* c, char* msg)
+{
+	if(msg != nil && *msg != '\0')
+		kstrcpy(c->cerr, msg, sizeof(c->cerr));
+
+	switch(c->state){
+
+	case Announcing:
+		c->state = Announced;
+		break;
+
+	case Connecting:
+		c->state = Connected;
+		break;
+	}
+
+	wakeup(&c->cr);
+	return 0;
+}
+
+Proto*
+Fsrcvpcol(Fs* f, uchar proto)
+{
+	if(f->ipmux)
+		return f->ipmux;
+	else
+		return f->t2p[proto];
+}
+
+Proto*
+Fsrcvpcolx(Fs *f, uchar proto)
+{
+	return f->t2p[proto];
+}
+
+/*
+ *  called with protocol locked
+ */
+Conv*
+Fsnewcall(Conv *c, uchar *raddr, ushort rport, uchar *laddr, ushort lport, uchar version)
+{
+	Conv *nc;
+	Conv **l;
+	int i;
+
+	qlock(c);
+	i = 0;
+	for(l = &c->incall; *l; l = &(*l)->next)
+		i++;
+	if(i >= Maxincall) {
+		qunlock(c);
+		return nil;
+	}
+
+	/* find a free conversation */
+	nc = Fsprotoclone(c->p, network);
+	if(nc == nil) {
+		qunlock(c);
+		return nil;
+	}
+	ipmove(nc->raddr, raddr);
+	nc->rport = rport;
+	ipmove(nc->laddr, laddr);
+	nc->lport = lport;
+	nc->next = nil;
+	*l = nc;
+	nc->state = Connected;
+	nc->ipversion = version;
+
+	qunlock(c);
+
+	wakeup(&c->listenr);
+
+	return nc;
+}
+
+static long
+ndbwrite(Fs *f, char *a, ulong off, int n)
+{
+	if(off > strlen(f->ndb))
+		error(Eio);
+	if(off+n >= sizeof(f->ndb)-1)
+		error(Eio);
+	memmove(f->ndb+off, a, n);
+	f->ndb[off+n] = 0;
+	f->ndbvers++;
+	f->ndbmtime = seconds();
+	return n;
+}
+
+ulong
+scalednconv(void)
+{
+	if(conf.npage*BY2PG >= 128*MB)
+		return Nchans*4;
+	return Nchans;
+}
--- /dev/null
+++ b/os/ip/dhcp.c
@@ -1,0 +1,447 @@
+#include "u.h"
+#include "../port/lib.h"
+#include "mem.h"
+#include "dat.h"
+#include "fns.h"
+#include "../port/error.h"
+#include "kernel.h"
+#include "ip.h"
+#include "ppp.h"
+
+Ipaddr pppdns[2];
+
+static	ulong	fsip;
+static	ulong	auip;
+static	ulong	gwip;
+static	ulong	ipmask;
+static	ulong	ipaddr;
+static	ulong	dns1ip;
+static	ulong	dns2ip;
+
+int		dhcpmsgtype;
+int		debug=0;
+enum
+{
+	Bootrequest = 1,
+	Bootreply   = 2,
+};
+
+typedef struct Bootp
+{
+	/* udp.c oldheader */
+	uchar	raddr[IPaddrlen];
+	uchar	laddr[IPaddrlen];
+	uchar	rport[2];
+	uchar	lport[2];
+	/* bootp itself */
+	uchar	op;			/* opcode */
+	uchar	htype;		/* hardware type */
+	uchar	hlen;			/* hardware address len */
+	uchar	hops;		/* hops */
+	uchar	xid[4];		/* a random number */
+	uchar	secs[2];		/* elapsed snce client started booting */
+	uchar	flags[2];		/* flags */
+	uchar	ciaddr[4];		/* client IP address (client tells server) */
+	uchar	yiaddr[4];		/* client IP address (server tells client) */
+	uchar	siaddr[4];		/* server IP address */
+	uchar	giaddr[4];		/* gateway IP address */
+	uchar	chaddr[16];	/* client hardware address */
+	uchar	sname[64];	/* server host name (optional) */
+	uchar	file[128];		/* boot file name */
+	uchar	vend[128];	/* vendor-specific goo 340 */
+} Bootp;
+
+static	Bootp	req;
+static	Proc*	rcvprocp;
+static	int	recv;
+static	int	done;
+static	Rendez	bootpr;
+static	char	rcvbuf[512+2*IPaddrlen+2*2];	  /* 576 */
+static	uchar sid[4];
+static	ulong iplease;
+
+/*
+ * bootp returns:
+ *
+ * "fsip d.d.d.d
+ * auip d.d.d.d
+ * gwip d.d.d.d
+ * ipmask d.d.d.d
+ * ipaddr d.d.d.d
+ * dns1ip	d.d.d.d
+ * dns2ip	d.d.d.d
+ *
+ * where d.d.d.d is the IP address in dotted decimal notation, and each
+ * address is followed by a newline.
+	Last change:  SUN  13 Sep 2001    4:36 pm
+ */
+
+/*
+ * Parse the vendor specific fields according to RFC 1084.
+ * We are overloading the "cookie server" to be the Inferno 
+ * authentication server and the "resource location server"
+ * to be the Inferno file server.
+ *
+ * If the vendor specific field is formatted properly, it
+ * will being with the four bytes 99.130.83.99 and end with
+ * an 0xFF byte.
+ */
+static int
+parsevend(uchar* pvend)
+{	
+	uchar *vend=pvend;
+	int dhcpmsg=0;
+	/* The field must start with 99.130.83.99 to be compliant */
+	if ((vend[0] != 99) || (vend[1] != 130) || (vend[2] != 83) || (vend[3] != 99)){
+		print("bad bootp vendor field: %.2x%.2x%.2x%.2x", vend[0], vend[1], vend[2], vend[3]);
+		return -1;
+	}
+
+	/* Skip over the magic cookie */
+	vend += 4;
+
+	while ((vend[0] != 0) && (vend[0] != 0xFF)) {
+		int i;
+//	
+		if(debug){
+			print(">>>Opt[%d] [%d]", vend[0], vend[1]);
+			for(i=0; i<vend[1]; i++)
+				print(" %2.2x", vend[i+2]);
+			print("\n");
+		}
+//
+		switch (vend[0]) {
+		case 1:	/* Subnet mask field */
+			/* There must be only one subnet mask */
+			if (vend[1] == 4)
+				ipmask = (vend[2]<<24)|(vend[3]<<16)| (vend[4]<<8)| vend[5];
+			else{ 
+				return -1;
+			}
+			break;
+
+		case 3:	/* Gateway/router field */
+			/* We are only concerned with first address */
+			if (vend[1] >0 && vend[1]%4==0)
+				gwip = (vend[2]<<24)|(vend[3]<<16)|(vend[4]<<8)|vend[5];
+			else 
+				return -1;
+			break;
+		case 6:	/* domain name server */
+			if(vend[1]>0 && vend[1] %4==0){
+				dns1ip=(vend[2]<<24)|(vend[3]<<16)|(vend[4]<<8)|vend[5];
+				if(vend[1]>4)
+					dns2ip=(vend[6]<<24)|(vend[7]<<16)|(vend[8]<<8)|vend[9];
+			}else
+				return -1;
+			break;
+
+		case 8:	/* "Cookie server" (auth server) field */
+			/* We are only concerned with first address */
+			if (vend[1] > 0 && vend[1]%4==0)
+				auip = (vend[2]<<24)|(vend[3]<<16)|(vend[4]<<8)|vend[5];
+			else
+				return -1;
+			break;
+
+		case 11:	/* "Resource loc server" (file server) field */
+			/* We are only concerned with first address */
+			if (vend[1] > 0 && vend[1]%4==0)
+				fsip = (vend[2]<<24)| (vend[3]<<16)| (vend[4]<<8)| vend[5];
+			else
+				return -1;
+			break;
+		case 51:	/* ip lease time */
+			if(vend[1]==4){
+				iplease=(vend[2]<<24)|(vend[3]<<16)|(vend[4]<<8)|vend[5];
+			}else
+				return -1;
+			break;
+		case 53:	/* DHCP message type */
+			if(vend[1]==1)
+				dhcpmsg=vend[2];
+			else
+				return -1;
+			break;
+		case 54:	/* server identifier */
+			if(vend[1]==4){
+				memmove(sid, vend+2, 4);
+			}else
+				return -1;
+			break;
+
+		default:	/* Everything else stops us */
+			break;
+		}
+
+		/* Skip over the field */
+		vend += vend[1] + 2;
+	}
+	if(debug)
+		print(">>>Opt[%d] [%d]\n", vend[0], vend[1]);
+	return dhcpmsg;
+}
+
+static void
+dispvend(uchar* pvend)
+{	
+	uchar *vend=pvend;
+
+	//print("<<<Magic : %2.2x%2.2x%2.2x%2.2x\n", vend[0], vend[1], vend[2], vend[3]);
+	
+	vend += 4;		/* Skip over the magic cookie */
+	while ((vend[0] != 0) && (vend[0] != 0xFF)) {
+	//	int i;
+	  //	print("<<<Opt[%d] [%d]", vend[0], vend[1]);
+		//for(i=0; i<vend[1]; i++)
+		//	print(" %2.2x", vend[i+2]);
+		//print("\n");
+	
+		vend += vend[1] + 2;
+	}
+	//print("<<<Opt[ %2.2x] [%2.2x]\n", vend[0], vend[1]);
+}
+
+static void
+rcvbootp(void *a)
+{
+	int n, fd, dhcp;
+	Bootp *rp;
+
+	if(waserror())
+		pexit("", 0);
+	rcvprocp = up;	/* store for postnote below */
+	fd = (int)a;
+	while(done == 0) {
+		if(debug)
+			print("rcvbootp:looping\n");
+
+		n = kread(fd, rcvbuf, sizeof(rcvbuf));
+		if(n <= 0)
+			break;
+		rp = (Bootp*)rcvbuf;
+		if (memcmp(req.chaddr, rp->chaddr, 6) == 0 && rp->htype == 1 && rp->hlen == 6) {
+			ipaddr = (rp->yiaddr[0]<<24)| (rp->yiaddr[1]<<16)| (rp->yiaddr[2]<<8)| rp->yiaddr[3];
+			if(debug)
+				print("ipaddr = %2.2x %2.2x %2.2x %2.2x \n", rp->yiaddr[0], rp->yiaddr[1], rp->yiaddr[2], rp->yiaddr[3]);
+			//memmove(req.siaddr, rp->siaddr, 4);	/* siaddr */
+			dhcp = parsevend(rp->vend);
+	
+			if(dhcpmsgtype < dhcp){
+				dhcpmsgtype=dhcp;
+				recv = 1;
+				wakeup(&bootpr);
+				if(dhcp==0 || dhcp ==5 || dhcp == 6 )
+					break;
+			}
+		}
+	}
+	poperror();
+	rcvprocp = nil;
+
+	if(debug)
+		print("rcvbootp exit\n");
+	pexit("", 0);
+}
+
+static char*
+rbootp(Ipifc *ifc)
+{
+	int cfd, dfd, tries, n;
+	char ia[5+3*16], im[16], *av[3];
+	uchar nipaddr[4], ngwip[4], nipmask[4];
+	char dir[Maxpath];
+	static uchar vend_rfc1048[] = { 99, 130, 83, 99 };
+	uchar *vend;
+
+	/*
+	 * broadcast bootp's till we get a reply,
+	 * or fixed number of tries
+	 */
+	if(debug)
+	    print("dhcp: bootp() called\n");
+	tries = 0;
+	av[1] = "0.0.0.0";
+	av[2] = "0.0.0.0";
+	ipifcadd(ifc, av, 3, 0, nil);
+
+	cfd = kannounce("udp!*!68", dir);
+	if(cfd < 0)
+		return "dhcp announce failed";
+	strcat(dir, "/data");
+	if(kwrite(cfd, "headers", 7) < 0){
+		kclose(cfd);
+		return "dhcp ctl headers failed";
+	}
+	kwrite(cfd, "oldheaders", 10);
+	dfd = kopen(dir, ORDWR);
+	if(dfd < 0){
+		kclose(cfd);
+		return "dhcp open data failed";
+	}
+	kclose(cfd);
+	
+	while(tries<1){
+		tries++;
+		memset(sid, 0, 4);
+		iplease=0;
+		dhcpmsgtype=-2;
+/* DHCPDISCOVER*/
+		done = 0;
+		recv = 0;
+		kproc("rcvbootp", rcvbootp, (void*)dfd, KPDUPFDG);
+		/* Prepare DHCPDISCOVER */	
+		memset(&req, 0, sizeof(req));
+		ipmove(req.raddr, IPv4bcast);
+		hnputs(req.rport, 67);
+		req.op = Bootrequest;
+		req.htype = 1;			/* ethernet (all we know) */
+		req.hlen = 6;			/* ethernet (all we know) */
+		
+		memmove(req.chaddr, ifc->mac, 6);	/* Hardware MAC address */
+		//ipv4local(ifc, req.ciaddr);				/* Fill in the local IP address if we know it */
+		memset(req.file, 0, sizeof(req.file));
+		vend=req.vend;
+		memmove(vend, vend_rfc1048, 4); vend+=4;
+		*vend++=53; *vend++=1;*vend++=1;		/* dhcp msg type==3, dhcprequest */
+		
+		*vend++=61;*vend++=7;*vend++=1;
+		memmove(vend, ifc->mac, 6);vend+=6;
+		*vend=0xff;
+
+		if(debug)
+			dispvend(req.vend); 
+		for(n=0;n<4;n++){
+			if(kwrite(dfd, &req, sizeof(req))<0)	/* SEND DHCPDISCOVER */
+				print("DHCPDISCOVER: %r");
+		
+			tsleep(&bootpr, return0, 0, 1000);	/* wait DHCPOFFER */
+			if(debug)
+				print("[DHCP] DISCOVER: msgtype = %d\n", dhcpmsgtype);
+
+			if(dhcpmsgtype==2)		/* DHCPOFFER */
+				break;
+			else if(dhcpmsgtype==0)	/* bootp */
+				return nil;
+			else if(dhcpmsgtype== -2)	/* time out */
+				continue;
+			else
+				break;
+			
+		}
+		if(dhcpmsgtype!=2)
+			continue;
+
+/* DHCPREQUEST */	
+		memset(req.vend, 0, sizeof(req.vend));
+		vend=req.vend;
+		memmove(vend, vend_rfc1048, 4);vend+=4;	
+
+		*vend++=53; *vend++=1;*vend++=3;		/* dhcp msg type==3, dhcprequest */
+
+		*vend++=50;	*vend++=4;				/* requested ip address */
+		*vend++=(ipaddr >> 24)&0xff;
+		*vend++=(ipaddr >> 16)&0xff;
+		*vend++=(ipaddr >> 8) & 0xff;
+		*vend++=ipaddr & 0xff;
+
+		*vend++=51;*vend++=4;					/* lease time */
+		*vend++=(iplease>>24)&0xff; *vend++=(iplease>>16)&0xff; *vend++=(iplease>>8)&0xff; *vend++=iplease&0xff;
+
+		*vend++=54; *vend++=4;					/* server identifier */
+		memmove(vend, sid, 4);	vend+=4;
+	
+		*vend++=61;*vend++=07;*vend++=01;		/* client identifier */
+		memmove(vend, ifc->mac, 6);vend+=6;
+		*vend=0xff;
+		if(debug) 
+			dispvend(req.vend); 
+		if(kwrite(dfd, &req, sizeof(req))<0){
+			print("DHCPREQUEST: %r");
+			continue;
+		}
+		tsleep(&bootpr, return0, 0, 2000);
+		if(dhcpmsgtype==5)		/* wait for DHCPACK */
+			break;
+		else
+			continue;
+		/* CHECK ARP */
+		/* DHCPDECLINE */
+	}
+	kclose(dfd);
+	done = 1;
+	if(rcvprocp != nil){
+		postnote(rcvprocp, 1, "timeout", 0);
+		rcvprocp = nil;
+	}
+
+	av[1] = "0.0.0.0";
+	av[2] = "0.0.0.0";
+	ipifcrem(ifc, av, 3);
+
+	hnputl(nipaddr, ipaddr);
+	sprint(ia, "%V", nipaddr);
+	hnputl(nipmask, ipmask);
+	sprint(im, "%V", nipmask);
+	av[1] = ia;
+	av[2] = im;
+	ipifcadd(ifc, av, 3, 0, nil);
+
+	if(gwip != 0) {
+		hnputl(ngwip, gwip);
+		n = sprint(ia, "add 0.0.0.0 0.0.0.0 %V", ngwip);
+		routewrite(ifc->conv->p->f, nil, ia, n);
+	}
+	return nil;
+}
+
+static int
+rbootpread(char *bp, ulong offset, int len)
+{
+	int n, i;
+	char *buf;
+	uchar a[4];
+
+	if(debug)
+		print("dhcp: bootpread() \n");
+	buf = smalloc(READSTR);
+	if(waserror()){
+		free(buf);
+		nexterror();
+	}
+
+	hnputl(a, fsip);
+	n = snprint(buf, READSTR, "fsip %15V\n", a);
+	hnputl(a, auip);
+	n += snprint(buf + n, READSTR-n, "auip %15V\n", a);
+	hnputl(a, gwip);
+	n += snprint(buf + n, READSTR-n, "gwip %15V\n", a);
+	hnputl(a, ipmask);
+	n += snprint(buf + n, READSTR-n, "ipmask %15V\n", a);
+	hnputl(a, ipaddr);
+	n += snprint(buf + n, READSTR-n, "ipaddr %15V\n", a);
+	n += snprint(buf+n, READSTR-n, "expired %lud\n", iplease);
+
+	n += snprint(buf + n, READSTR-n, "dns");
+	if(dns2ip){
+		hnputl(a, dns2ip);
+		n+=snprint(buf + n, READSTR-n, " %15V", a);
+	}
+	if(dns1ip){
+		hnputl(a, dns1ip);
+		n += snprint(buf + n, READSTR-n, " %15V", a);
+	}
+
+	for(i=0; i<2; i++)
+		if(ipcmp(pppdns[i], IPnoaddr) != 0 && ipcmp(pppdns[i], v4prefix) != 0)
+			n += snprint(buf + n, READSTR-n, " %15I", pppdns[i]);
+
+	snprint(buf + n, READSTR-n, "\n");
+	len = readstr(offset, bp, len, buf);
+	poperror();
+	free(buf);
+	return len;
+}
+
+char*	(*bootp)(Ipifc*) = rbootp;
+int	(*bootpread)(char*, ulong, int) = rbootpread;
--- /dev/null
+++ b/os/ip/eipconvtest.c
@@ -1,0 +1,152 @@
+#include <u.h>
+#include <libc.h>
+
+enum
+{
+	Isprefix= 16,
+};
+
+uchar prefixvals[256] =
+{
+[0x00] 0 | Isprefix,
+[0x80] 1 | Isprefix,
+[0xC0] 2 | Isprefix,
+[0xE0] 3 | Isprefix,
+[0xF0] 4 | Isprefix,
+[0xF8] 5 | Isprefix,
+[0xFC] 6 | Isprefix,
+[0xFE] 7 | Isprefix,
+[0xFF] 8 | Isprefix,
+};
+
+uchar v4prefix[16] = {
+	0, 0, 0, 0,
+	0, 0, 0, 0,
+	0, 0, 0xff, 0xff,
+	0, 0, 0, 0
+};
+
+void
+hnputl(void *p, ulong v)
+{
+	uchar *a;
+
+	a = p;
+	a[0] = v>>24;
+	a[1] = v>>16;
+	a[2] = v>>8;
+	a[3] = v;
+}
+
+int
+eipconv(va_list *arg, Fconv *f)
+{
+	char buf[8*5];
+	static char *efmt = "%.2lux%.2lux%.2lux%.2lux%.2lux%.2lux";
+	static char *ifmt = "%d.%d.%d.%d";
+	uchar *p, ip[16];
+	ulong *lp;
+	ushort s;
+	int i, j, n, eln, eli;
+
+	switch(f->chr) {
+	case 'E':		/* Ethernet address */
+		p = va_arg(*arg, uchar*);
+		sprint(buf, efmt, p[0], p[1], p[2], p[3], p[4], p[5]);
+		break;
+	case 'I':		/* Ip address */
+		p = va_arg(*arg, uchar*);
+common:
+		if(memcmp(p, v4prefix, 12) == 0)
+			sprint(buf, ifmt, p[12], p[13], p[14], p[15]);
+		else {
+			/* find longest elision */
+			eln = eli = -1;
+			for(i = 0; i < 16; i += 2){
+				for(j = i; j < 16; j += 2)
+					if(p[j] != 0 || p[j+1] != 0)
+						break;
+				if(j > i && j - i > eln){
+					eli = i;
+					eln = j - i;
+				}
+			}
+
+			/* print with possible elision */
+			n = 0;
+			for(i = 0; i < 16; i += 2){
+				if(i == eli){
+					n += sprint(buf+n, "::");
+					i += eln;
+					if(i >= 16)
+						break;
+				} else if(i != 0)
+					n += sprint(buf+n, ":");
+				s = (p[i]<<8) + p[i+1];
+				n += sprint(buf+n, "%ux", s);
+			}
+		}
+		break;
+	case 'i':		/* v6 address as 4 longs */
+		lp = va_arg(*arg, ulong*);
+		for(i = 0; i < 4; i++)
+			hnputl(ip+4*i, *lp++);
+		p = ip;
+		goto common;
+	case 'V':		/* v4 ip address */
+		p = va_arg(*arg, uchar*);
+		sprint(buf, ifmt, p[0], p[1], p[2], p[3]);
+		break;
+	case 'M':		/* ip mask */
+		p = va_arg(*arg, uchar*);
+
+		/* look for a prefix mask */
+		for(i = 0; i < 16; i++)
+			if(p[i] != 0xff)
+				break;
+		if(i < 16){
+			if((prefixvals[p[i]] & Isprefix) == 0)
+				goto common;
+			for(j = i+1; j < 16; j++)
+				if(p[j] != 0)
+					goto common;
+			n = 8*i + (prefixvals[p[i]] & ~Isprefix);
+		} else
+			n = 8*16;
+
+		/* got one, use /xx format */
+		sprint(buf, "/%d", n);
+		break;
+	default:
+		strcpy(buf, "(eipconv)");
+	}
+	strconv(buf, f);
+	return sizeof(uchar*);
+}
+
+uchar testvec[11][16] =
+{
+ { 0,0,0,0, 0,0,0,0, 0,0,0xff,0xff, 1,3,4,5, },
+ { 0xff,0xff,0xff,0xff, 0xff,0xff,0xff,0xff, 0xff,0xff,0xff,0xff, 0xff,0xff,0xff,0xff, },
+ { 0xff,0xff,0x80,0, 0,0,0,0, 0,0,0,0, 0,0,0,0, },
+ { 0xff,0xff,0xff,0xc0, 0,0,0,0, 0,0,0,0, 0,0,0,0, },
+ { 0xff,0xff,0xff,0xff, 0xe0,0,0,0, 0,0,0,0, 0,0,0,0, },
+ { 0xff,0xff,0xff,0xff, 0xff,0xf0,0,0, 0,0,0,0, 0,0,0,0, },
+ { 0xff,0xff,0xff,0xff, 0xff,0xff,0xf8,0, 0,0,0,0, 0,0,0,0, },
+ { 0xff,0xff,0xff,0xff, 0xff,0xff,0xff,0xff, 0xff,0xff,0xff,0xff, 0xff,0xff,0xff,0xff, },
+ { 0,0,0,0, 0,0,0,0, 0,0,0,0, 0,0,0,0, },
+ { 0,0,0,0, 0,0x11,0,0, 0,0,0,0, 0,0,0,0, },
+ { 0,0,0,0x11, 0,0,0,0, 0,0,0,0, 0,0,0,0x12, },
+};
+
+void
+main(void)
+{
+	int i;
+
+	fmtinstall('I', eipconv);
+	fmtinstall('M', eipconv);
+	for(i = 0; i < 11; i++)
+		print("%I\n%M\n", testvec[i], testvec[i]);
+	exits(0);
+}
--- /dev/null
+++ b/os/ip/esp.c
@@ -1,0 +1,866 @@
+#include	"u.h"
+#include	"../port/lib.h"
+#include	"mem.h"
+#include	"dat.h"
+#include	"fns.h"
+#include	"../port/error.h"
+
+#include	"ip.h"
+
+#include	"libsec.h"
+
+typedef struct Esphdr Esphdr;
+typedef struct Esptail Esptail;
+typedef struct Userhdr Userhdr;
+typedef struct Esppriv Esppriv;
+typedef struct Espcb Espcb;
+typedef struct Algorithm Algorithm;
+typedef struct Esprc4 Esprc4;
+
+#define DPRINT if(0)print
+
+enum
+{
+	IP_ESPPROTO	= 50,
+	EsphdrSize	= 28,	// includes IP header
+	IphdrSize	= 20,	// options have been striped
+	EsptailSize	= 2,	// does not include pad or auth data
+	UserhdrSize	= 4,	// user visable header size - if enabled
+};
+
+struct Esphdr
+{
+	/* ip header */
+	uchar	vihl;		/* Version and header length */
+	uchar	tos;		/* Type of service */
+	uchar	length[2];	/* packet length */
+	uchar	id[2];		/* Identification */
+	uchar	frag[2];	/* Fragment information */
+	uchar	Unused;	
+	uchar	espproto;	/* Protocol */
+	uchar	espplen[2];	/* Header plus data length */
+	uchar	espsrc[4];	/* Ip source */
+	uchar	espdst[4];	/* Ip destination */
+
+	/* esp header */
+	uchar	espspi[4];	/* Security parameter index */
+	uchar	espseq[4];	/* Sequence number */
+};
+
+struct Esptail
+{
+	uchar	pad;
+	uchar	nexthdr;
+};
+
+/* header as seen by the user */
+struct Userhdr
+{
+	uchar	nexthdr;	// next protocol
+	uchar	unused[3];
+};
+
+struct Esppriv
+{
+	ulong	in;
+	ulong	inerrors;
+};
+
+/*
+ *  protocol specific part of Conv
+ */
+struct Espcb
+{
+	int	incoming;
+	int	header;		// user user level header
+	ulong	spi;
+	ulong	seq;		// last seq sent
+	ulong	window;		// for replay attacks
+	char	*espalg;
+	void	*espstate;	// other state for esp
+	int	espivlen;	// in bytes
+	int	espblklen;
+	int	(*cipher)(Espcb*, uchar *buf, int len);
+	char	*ahalg;
+	void	*ahstate;	// other state for esp
+	int	ahlen;		// auth data length in bytes
+	int	ahblklen;
+	int	(*auth)(Espcb*, uchar *buf, int len, uchar *hash);
+};
+
+struct Algorithm
+{
+	char 	*name;
+	int	keylen;		// in bits
+	void	(*init)(Espcb*, char* name, uchar *key, int keylen);
+};
+
+
+enum {
+	RC4forward	= 10*1024*1024,	// maximum skip forward
+	RC4back = 100*1024,		// maximum look back
+};
+
+struct Esprc4
+{
+	ulong cseq;	// current byte sequence number
+	RC4state current;
+
+	int ovalid;	// old is valid
+	ulong lgseq; // last good sequence
+	ulong oseq;	// old byte sequence number
+	RC4state old;
+};
+
+static	Conv* convlookup(Proto *esp, ulong spi);
+static	char *setalg(Espcb *ecb, char **f, int n, Algorithm *alg);
+static	void nullespinit(Espcb*, char*, uchar *key, int keylen);
+static	void nullahinit(Espcb*, char*, uchar *key, int keylen);
+static	void shaahinit(Espcb*, char*, uchar *key, int keylen);
+static	void md5ahinit(Espcb*, char*, uchar *key, int keylen);
+static	void desespinit(Espcb *ecb, char *name, uchar *k, int n);
+static	void rc4espinit(Espcb *ecb, char *name, uchar *k, int n);
+static	void espkick(void *x);
+
+static Algorithm espalg[] =
+{
+	"null",			0,	nullespinit,
+	"des_56_cbc",		64,	desespinit,
+	"rc4_128",		128,	rc4espinit,
+	nil,			0,	nil,
+};
+
+static Algorithm ahalg[] =
+{
+	"null",			0,	nullahinit,
+	"hmac_sha1_96",		128,	shaahinit,
+	"hmac_md5_96",		128,	md5ahinit,
+	nil,			0,	nil,
+};
+
+static char*
+espconnect(Conv *c, char **argv, int argc)
+{
+	char *p, *pp;
+	char *e = nil;
+	ulong spi;
+	Espcb *ecb = (Espcb*)c->ptcl;
+
+	switch(argc) {
+	default:
+		e = "bad args to connect";
+		break;
+	case 2:
+		p = strchr(argv[1], '!');
+		if(p == nil){
+			e = "malformed address";
+			break;
+		}
+		*p++ = 0;
+		parseip(c->raddr, argv[1]);
+		findlocalip(c->p->f, c->laddr, c->raddr);
+		ecb->incoming = 0;
+		ecb->seq = 0;
+		if(strcmp(p, "*") == 0) {
+			qlock(c->p);
+			for(;;) {
+				spi = nrand(1<<16) + 256;
+				if(convlookup(c->p, spi) == nil)
+					break;
+			}
+			qunlock(c->p);
+			ecb->spi = spi;
+			ecb->incoming = 1;
+			qhangup(c->wq, nil);
+		} else {
+			spi = strtoul(p, &pp, 10);
+			if(pp == p) {
+				e = "malformed address";
+				break;
+			}
+			ecb->spi = spi;
+			qhangup(c->rq, nil);
+		}
+		nullespinit(ecb, "null", nil, 0);
+		nullahinit(ecb, "null", nil, 0);
+	}
+	Fsconnected(c, e);
+
+	return e;
+}
+
+
+static int
+espstate(Conv *c, char *state, int n)
+{
+	return snprint(state, n, "%s", c->inuse?"Open\n":"Closed\n");
+}
+
+static void
+espcreate(Conv *c)
+{
+	c->rq = qopen(64*1024, Qmsg, 0, 0);
+	c->wq = qopen(64*1024, Qkick, espkick, c);
+}
+
+static void
+espclose(Conv *c)
+{
+	Espcb *ecb;
+
+	qclose(c->rq);
+	qclose(c->wq);
+	qclose(c->eq);
+	ipmove(c->laddr, IPnoaddr);
+	ipmove(c->raddr, IPnoaddr);
+
+	ecb = (Espcb*)c->ptcl;
+	free(ecb->espstate);
+	free(ecb->ahstate);
+	memset(ecb, 0, sizeof(Espcb));
+}
+
+static void
+espkick(void *x)
+{
+	Conv *c = x;
+	Esphdr *eh;
+	Esptail *et;
+	Userhdr *uh;
+	Espcb *ecb;
+	Block *bp;
+	int nexthdr;
+	int payload;
+	int pad;
+	int align;
+	uchar *auth;
+
+	bp = qget(c->wq);
+	if(bp == nil)
+		return;
+
+	qlock(c);
+	ecb = c->ptcl;
+
+	if(ecb->header) {
+		/* make sure the message has a User header */
+		bp = pullupblock(bp, UserhdrSize);
+		if(bp == nil) {
+			qunlock(c);
+			return;
+		}
+		uh = (Userhdr*)bp->rp;
+		nexthdr = uh->nexthdr;
+		bp->rp += UserhdrSize;
+	} else {
+		nexthdr = 0;  // what should this be?
+	}
+
+	payload = BLEN(bp) + ecb->espivlen;
+
+	/* Make space to fit ip header */
+	bp = padblock(bp, EsphdrSize + ecb->espivlen);
+
+	align = 4;
+	if(ecb->espblklen > align)
+		align = ecb->espblklen;
+	if(align % ecb->ahblklen != 0)
+		panic("espkick: ahblklen is important after all");
+	pad = (align-1) - (payload + EsptailSize-1)%align;
+
+	/*
+	 * Make space for tail
+	 * this is done by calling padblock with a negative size
+	 * Padblock does not change bp->wp!
+	 */
+	bp = padblock(bp, -(pad+EsptailSize+ecb->ahlen));
+	bp->wp += pad+EsptailSize+ecb->ahlen;
+
+	eh = (Esphdr *)(bp->rp);
+	et = (Esptail*)(bp->rp + EsphdrSize + payload + pad);
+
+	// fill in tail
+	et->pad = pad;
+	et->nexthdr = nexthdr;
+
+	ecb->cipher(ecb, bp->rp+EsphdrSize, payload+pad+EsptailSize);
+	auth = bp->rp + EsphdrSize + payload + pad + EsptailSize;
+
+	// fill in head
+	eh->vihl = IP_VER4;
+	hnputl(eh->espspi, ecb->spi);
+	hnputl(eh->espseq, ++ecb->seq);
+	v6tov4(eh->espsrc, c->laddr);
+	v6tov4(eh->espdst, c->raddr);
+	eh->espproto = IP_ESPPROTO;
+	eh->frag[0] = 0;
+	eh->frag[1] = 0;
+
+	ecb->auth(ecb, bp->rp+IphdrSize, (EsphdrSize-IphdrSize)+payload+pad+EsptailSize, auth);
+
+	qunlock(c);
+	//print("esp: pass down: %uld\n", BLEN(bp));
+	ipoput4(c->p->f, bp, 0, c->ttl, c->tos, c);
+}
+
+void
+espiput(Proto *esp, Ipifc*, Block *bp)
+{
+	Esphdr *eh;
+	Esptail *et;
+	Userhdr *uh;
+	Conv *c;
+	Espcb *ecb;
+	uchar raddr[IPaddrlen], laddr[IPaddrlen];
+	Fs *f;
+	uchar *auth;
+	ulong spi;
+	int payload, nexthdr;
+
+	f = esp->f;
+
+	bp = pullupblock(bp, EsphdrSize+EsptailSize);
+	if(bp == nil) {
+		netlog(f, Logesp, "esp: short packet\n");
+		return;
+	}
+
+	eh = (Esphdr*)(bp->rp);
+	spi = nhgetl(eh->espspi);
+	v4tov6(raddr, eh->espsrc);
+	v4tov6(laddr, eh->espdst);
+
+	qlock(esp);
+	/* Look for a conversation structure for this port */
+	c = convlookup(esp, spi);
+	if(c == nil) {
+		qunlock(esp);
+		netlog(f, Logesp, "esp: no conv %I -> %I!%d\n", raddr,
+			laddr, spi);
+		icmpnoconv(f, bp);
+		freeblist(bp);
+		return;
+	}
+
+	qlock(c);
+	qunlock(esp);
+
+	ecb = c->ptcl;
+	// too hard to do decryption/authentication on block lists
+	if(bp->next)
+		bp = concatblock(bp);
+
+	if(BLEN(bp) < EsphdrSize + ecb->espivlen + EsptailSize + ecb->ahlen) {
+		qunlock(c);
+		netlog(f, Logesp, "esp: short block %I -> %I!%d\n", raddr,
+			laddr, spi);
+		freeb(bp);
+		return;
+	}
+
+	eh = (Esphdr*)(bp->rp);
+	auth = bp->wp - ecb->ahlen;
+	if(!ecb->auth(ecb, eh->espspi, auth-eh->espspi, auth)) {
+		qunlock(c);
+print("esp: bad auth %I -> %I!%ld\n", raddr, laddr, spi);
+		netlog(f, Logesp, "esp: bad auth %I -> %I!%d\n", raddr,
+			laddr, spi);
+		freeb(bp);
+		return;
+	}
+
+	payload = BLEN(bp)-EsphdrSize-ecb->ahlen;
+	if(payload<=0 || payload%4 != 0 || payload%ecb->espblklen!=0) {
+		qunlock(c);
+		netlog(f, Logesp, "esp: bad length %I -> %I!%d payload=%d BLEN=%d\n", raddr,
+			laddr, spi, payload, BLEN(bp));
+		freeb(bp);
+		return;
+	}
+	if(!ecb->cipher(ecb, bp->rp+EsphdrSize, payload)) {
+		qunlock(c);
+print("esp: cipher failed %I -> %I!%ld: %r\n", raddr, laddr, spi);
+		netlog(f, Logesp, "esp: cipher failed %I -> %I!%d: %r\n", raddr,
+			laddr, spi);
+		freeb(bp);
+		return;
+	}
+
+	payload -= EsptailSize;
+	et = (Esptail*)(bp->rp + EsphdrSize + payload);
+	payload -= et->pad + ecb->espivlen;
+	nexthdr = et->nexthdr;
+	if(payload <= 0) {
+		qunlock(c);
+		netlog(f, Logesp, "esp: short packet after decrypt %I -> %I!%d\n", raddr,
+			laddr, spi);
+		freeb(bp);
+		return;
+	}
+
+	// trim packet
+	bp->rp += EsphdrSize + ecb->espivlen;
+	bp->wp = bp->rp + payload;
+	if(ecb->header) {
+		// assume UserhdrSize < EsphdrSize
+		bp->rp -= UserhdrSize;
+		uh = (Userhdr*)bp->rp;
+		memset(uh, 0, UserhdrSize);
+		uh->nexthdr = nexthdr;
+	}
+
+	if(qfull(c->rq)){
+		netlog(f, Logesp, "esp: qfull %I -> %I.%uld\n", raddr,
+			laddr, spi);
+		freeblist(bp);
+	}else {
+//print("esp: pass up: %uld\n", BLEN(bp));
+		qpass(c->rq, bp);
+	}
+
+	qunlock(c);
+}
+
+char*
+espctl(Conv *c, char **f, int n)
+{
+	Espcb *ecb = c->ptcl;
+	char *e = nil;
+
+	if(strcmp(f[0], "esp") == 0)
+		e = setalg(ecb, f, n, espalg);
+	else if(strcmp(f[0], "ah") == 0)
+		e = setalg(ecb, f, n, ahalg);
+	else if(strcmp(f[0], "header") == 0)
+		ecb->header = 1;
+	else if(strcmp(f[0], "noheader") == 0)
+		ecb->header = 0;
+	else
+		e = "unknown control request";
+	return e;
+}
+
+void
+espadvise(Proto *esp, Block *bp, char *msg)
+{
+	Esphdr *h;
+	Conv *c;
+	ulong spi;
+
+	h = (Esphdr*)(bp->rp);
+
+	spi = nhgets(h->espspi);
+	qlock(esp);
+	c = convlookup(esp, spi);
+	if(c != nil) {
+		qhangup(c->rq, msg);
+		qhangup(c->wq, msg);
+	}
+	qunlock(esp);
+	freeblist(bp);
+}
+
+int
+espstats(Proto *esp, char *buf, int len)
+{
+	Esppriv *upriv;
+
+	upriv = esp->priv;
+	return snprint(buf, len, "%lud %lud\n",
+		upriv->in,
+		upriv->inerrors);
+}
+
+static int
+esplocal(Conv *c, char *buf, int len)
+{
+	Espcb *ecb = c->ptcl;
+	int n;
+
+	qlock(c);
+	if(ecb->incoming)
+		n = snprint(buf, len, "%I!%uld\n", c->laddr, ecb->spi);
+	else
+		n = snprint(buf, len, "%I\n", c->laddr);
+	qunlock(c);
+	return n;
+}
+
+static int
+espremote(Conv *c, char *buf, int len)
+{
+	Espcb *ecb = c->ptcl;
+	int n;
+
+	qlock(c);
+	if(ecb->incoming)
+		n = snprint(buf, len, "%I\n", c->raddr);
+	else
+		n = snprint(buf, len, "%I!%uld\n", c->raddr, ecb->spi);
+	qunlock(c);
+	return n;
+}
+
+static	Conv*
+convlookup(Proto *esp, ulong spi)
+{
+	Conv *c, **p;
+	Espcb *ecb;
+
+	for(p=esp->conv; *p; p++){
+		c = *p;
+		ecb = c->ptcl;
+		if(ecb->incoming && ecb->spi == spi)
+			return c;
+	}
+	return nil;
+}
+
+static char *
+setalg(Espcb *ecb, char **f, int n, Algorithm *alg)
+{
+	uchar *key;
+	int i, nbyte, nchar;
+	int c;
+
+	if(n < 2)
+		return "bad format";
+	for(; alg->name; alg++)
+		if(strcmp(f[1], alg->name) == 0)
+			break;
+	if(alg->name == nil)
+		return "unknown algorithm";
+
+	if(n != 3)
+		return "bad format";
+	nbyte = (alg->keylen + 7) >> 3;
+	nchar = strlen(f[2]);
+	for(i=0; i<nchar; i++) {
+		c = f[2][i];
+		if(c >= '0' && c <= '9')
+			f[2][i] -= '0';
+		else if(c >= 'a' && c <= 'f')
+			f[2][i] -= 'a'-10;
+		else if(c >= 'A' && c <= 'F')
+			f[2][i] -= 'A'-10;
+		else
+			return "bad character in key";
+	}
+	key = smalloc(nbyte);
+	for(i=0; i<nchar && i*2<nbyte; i++) {
+		c = f[2][nchar-i-1];
+		if(i&1)
+			c <<= 4;
+		key[i>>1] |= c;
+	}
+
+	alg->init(ecb, alg->name, key, alg->keylen);
+	free(key);
+	return nil;
+}
+
+static int
+nullcipher(Espcb*, uchar*, int)
+{
+	return 1;
+}
+
+static void
+nullespinit(Espcb *ecb, char *name, uchar*, int)
+{
+	ecb->espalg = name;
+	ecb->espblklen = 1;
+	ecb->espivlen = 0;
+	ecb->cipher = nullcipher;
+}
+
+static int
+nullauth(Espcb*, uchar*, int, uchar*)
+{
+	return 1;
+}
+
+static void
+nullahinit(Espcb *ecb, char *name, uchar*, int)
+{
+	ecb->ahalg = name;
+	ecb->ahblklen = 1;
+	ecb->ahlen = 0;
+	ecb->auth = nullauth;
+}
+
+void
+seanq_hmac_sha1(uchar hash[SHA1dlen], uchar *t, long tlen, uchar *key, long klen)
+{
+	uchar ipad[65], opad[65];
+	int i;
+	DigestState *digest;
+	uchar innerhash[SHA1dlen];
+
+	for(i=0; i<64; i++){
+		ipad[i] = 0x36;
+		opad[i] = 0x5c;
+	}
+	ipad[64] = opad[64] = 0;
+	for(i=0; i<klen; i++){
+		ipad[i] ^= key[i];
+		opad[i] ^= key[i];
+	}
+	digest = sha1(ipad, 64, nil, nil);
+	sha1(t, tlen, innerhash, digest);
+	digest = sha1(opad, 64, nil, nil);
+	sha1(innerhash, SHA1dlen, hash, digest);
+}
+
+static int
+shaauth(Espcb *ecb, uchar *t, int tlen, uchar *auth)
+{
+	uchar hash[SHA1dlen];
+	int r;
+
+	memset(hash, 0, SHA1dlen);
+	seanq_hmac_sha1(hash, t, tlen, (uchar*)ecb->ahstate, 16);
+	r = memcmp(auth, hash, ecb->ahlen) == 0;
+	memmove(auth, hash, ecb->ahlen);
+	return r;
+}
+
+static void
+shaahinit(Espcb *ecb, char *name, uchar *key, int klen)
+{
+	if(klen != 128)
+		panic("shaahinit: bad keylen");
+	klen >>= 8;	// convert to bytes
+
+	ecb->ahalg = name;
+	ecb->ahblklen = 1;
+	ecb->ahlen = 12;
+	ecb->auth = shaauth;
+	ecb->ahstate = smalloc(klen);
+	memmove(ecb->ahstate, key, klen);
+}
+
+void
+seanq_hmac_md5(uchar hash[MD5dlen], uchar *t, long tlen, uchar *key, long klen)
+{
+	uchar ipad[65], opad[65];
+	int i;
+	DigestState *digest;
+	uchar innerhash[MD5dlen];
+
+	for(i=0; i<64; i++){
+		ipad[i] = 0x36;
+		opad[i] = 0x5c;
+	}
+	ipad[64] = opad[64] = 0;
+	for(i=0; i<klen; i++){
+		ipad[i] ^= key[i];
+		opad[i] ^= key[i];
+	}
+	digest = md5(ipad, 64, nil, nil);
+	md5(t, tlen, innerhash, digest);
+	digest = md5(opad, 64, nil, nil);
+	md5(innerhash, MD5dlen, hash, digest);
+}
+
+static int
+md5auth(Espcb *ecb, uchar *t, int tlen, uchar *auth)
+{
+	uchar hash[MD5dlen];
+	int r;
+
+	memset(hash, 0, MD5dlen);
+	seanq_hmac_md5(hash, t, tlen, (uchar*)ecb->ahstate, 16);
+	r = memcmp(auth, hash, ecb->ahlen) == 0;
+	memmove(auth, hash, ecb->ahlen);
+	return r;
+}
+
+static void
+md5ahinit(Espcb *ecb, char *name, uchar *key, int klen)
+{
+	if(klen != 128)
+		panic("md5ahinit: bad keylen");
+	klen >>= 3;	// convert to bytes
+
+
+	ecb->ahalg = name;
+	ecb->ahblklen = 1;
+	ecb->ahlen = 12;
+	ecb->auth = md5auth;
+	ecb->ahstate = smalloc(klen);
+	memmove(ecb->ahstate, key, klen);
+}
+
+static int
+descipher(Espcb *ecb, uchar *p, int n)
+{
+	uchar tmp[8];
+	uchar *pp, *tp, *ip, *eip, *ep;
+	DESstate *ds = ecb->espstate;
+
+	ep = p + n;
+	if(ecb->incoming) {
+		memmove(ds->ivec, p, 8);
+		p += 8;
+		while(p < ep){
+			memmove(tmp, p, 8);
+			block_cipher(ds->expanded, p, 1);
+			tp = tmp;
+			ip = ds->ivec;
+			for(eip = ip+8; ip < eip; ){
+				*p++ ^= *ip;
+				*ip++ = *tp++;
+			}
+		}
+	} else {
+		memmove(p, ds->ivec, 8);
+		for(p += 8; p < ep; p += 8){
+			pp = p;
+			ip = ds->ivec;
+			for(eip = ip+8; ip < eip; )
+				*pp++ ^= *ip++;
+			block_cipher(ds->expanded, p, 0);
+			memmove(ds->ivec, p, 8);
+		}
+	}
+	return 1;
+}
+	
+static void
+desespinit(Espcb *ecb, char *name, uchar *k, int n)
+{
+	uchar key[8];
+	uchar ivec[8];
+	int i;
+	
+	// bits to bytes
+	n = (n+7)>>3;
+	if(n > 8)
+		n = 8;
+	memset(key, 0, sizeof(key));
+	memmove(key, k, n);
+	for(i=0; i<8; i++)
+		ivec[i] = nrand(256);
+	ecb->espalg = name;
+	ecb->espblklen = 8;
+	ecb->espivlen = 8;
+	ecb->cipher = descipher;
+	ecb->espstate = smalloc(sizeof(DESstate));
+	setupDESstate(ecb->espstate, key, ivec);
+}
+
+static int
+rc4cipher(Espcb *ecb, uchar *p, int n)
+{
+	Esprc4 *esprc4;
+	RC4state tmpstate;
+	ulong seq;
+	long d, dd;
+
+	if(n < 4)
+		return 0;
+
+	esprc4 = ecb->espstate;
+	if(ecb->incoming) {
+		seq = nhgetl(p);
+		p += 4;
+		n -= 4;
+		d = seq-esprc4->cseq;
+		if(d == 0) {
+			rc4(&esprc4->current, p, n);
+			esprc4->cseq += n;
+			if(esprc4->ovalid) {
+				dd = esprc4->cseq - esprc4->lgseq;
+				if(dd > RC4back)
+					esprc4->ovalid = 0;
+			}
+		} else if(d > 0) {
+print("missing packet: %uld %ld\n", seq, d);
+			// this link is hosed
+			if(d > RC4forward) {
+				strcpy(up->errstr, "rc4cipher: skipped too much");
+				return 0;
+			}
+			esprc4->lgseq = seq;
+			if(!esprc4->ovalid) {
+				esprc4->ovalid = 1;
+				esprc4->oseq = esprc4->cseq;
+				memmove(&esprc4->old, &esprc4->current, sizeof(RC4state));
+			}
+			rc4skip(&esprc4->current, d);
+			rc4(&esprc4->current, p, n);
+			esprc4->cseq = seq+n;
+		} else {
+print("reordered packet: %uld %ld\n", seq, d);
+			dd = seq - esprc4->oseq;
+			if(!esprc4->ovalid || -d > RC4back || dd < 0) {
+				strcpy(up->errstr, "rc4cipher: too far back");
+				return 0;
+			}
+			memmove(&tmpstate, &esprc4->old, sizeof(RC4state));
+			rc4skip(&tmpstate, dd);
+			rc4(&tmpstate, p, n);
+			return 1;
+		}
+
+		// move old state up
+		if(esprc4->ovalid) {
+			dd = esprc4->cseq - RC4back - esprc4->oseq;
+			if(dd > 0) {
+				rc4skip(&esprc4->old, dd);
+				esprc4->oseq += dd;
+			}
+		}
+	} else {
+		hnputl(p, esprc4->cseq);
+		p += 4;
+		n -= 4;
+		rc4(&esprc4->current, p, n);
+		esprc4->cseq += n;
+	}
+	return 1;
+}
+
+static void
+rc4espinit(Espcb *ecb, char *name, uchar *k, int n)
+{	
+	Esprc4 *esprc4;
+
+	// bits to bytes
+	n = (n+7)>>3;
+	esprc4 = smalloc(sizeof(Esprc4));
+	memset(esprc4, 0, sizeof(Esprc4));
+	setupRC4state(&esprc4->current, k, n);
+	ecb->espalg = name;
+	ecb->espblklen = 4;
+	ecb->espivlen = 4;
+	ecb->cipher = rc4cipher;
+	ecb->espstate = esprc4;
+}
+	
+void
+espinit(Fs *fs)
+{
+	Proto *esp;
+
+	esp = smalloc(sizeof(Proto));
+	esp->priv = smalloc(sizeof(Esppriv));
+	esp->name = "esp";
+	esp->connect = espconnect;
+	esp->announce = nil;
+	esp->ctl = espctl;
+	esp->state = espstate;
+	esp->create = espcreate;
+	esp->close = espclose;
+	esp->rcv = espiput;
+	esp->advise = espadvise;
+	esp->stats = espstats;
+	esp->local = esplocal;
+	esp->remote = espremote;
+	esp->ipproto = IP_ESPPROTO;
+	esp->nc = Nchans;
+	esp->ptclsize = sizeof(Espcb);
+
+	Fsproto(fs, esp);
+}
--- /dev/null
+++ b/os/ip/ethermedium.c
@@ -1,0 +1,792 @@
+#include "u.h"
+#include "../port/lib.h"
+#include "mem.h"
+#include "dat.h"
+#include "fns.h"
+#include "../port/error.h"
+
+#include "ip.h"
+#include "ipv6.h"
+#include "kernel.h"
+
+typedef struct Etherhdr Etherhdr;
+struct Etherhdr
+{
+	uchar	d[6];
+	uchar	s[6];
+	uchar	t[2];
+};
+
+static uchar ipbroadcast[IPaddrlen] = {
+	0xff,0xff,0xff,0xff,  
+	0xff,0xff,0xff,0xff,  
+	0xff,0xff,0xff,0xff,  
+	0xff,0xff,0xff,0xff,
+};
+
+static uchar etherbroadcast[] = { 0xff, 0xff, 0xff, 0xff, 0xff, 0xff };
+
+static void	etherread4(void *a);
+static void	etherread6(void *a);
+static void	etherbind(Ipifc *ifc, int argc, char **argv);
+static void	etherunbind(Ipifc *ifc);
+static void	etherbwrite(Ipifc *ifc, Block *bp, int version, uchar *ip);
+static void	etheraddmulti(Ipifc *ifc, uchar *a, uchar *ia);
+static void	etherremmulti(Ipifc *ifc, uchar *a, uchar *ia);
+static Block*	multicastarp(Fs *f, Arpent *a, Medium*, uchar *mac);
+static void	sendarp(Ipifc *ifc, Arpent *a);
+static void	sendgarp(Ipifc *ifc, uchar*);
+static int	multicastea(uchar *ea, uchar *ip);
+static void	recvarpproc(void*);
+static void	resolveaddr6(Ipifc *ifc, Arpent *a);
+static void	etherpref2addr(uchar *pref, uchar *ea);
+
+Medium ethermedium =
+{
+.name=		"ether",
+.hsize=		14,
+.mintu=		60,
+.maxtu=		1514,
+.maclen=	6,
+.bind=		etherbind,
+.unbind=	etherunbind,
+.bwrite=	etherbwrite,
+.addmulti=	etheraddmulti,
+.remmulti=	etherremmulti,
+.ares=		arpenter,
+.areg=		sendgarp,
+.pref2addr=	etherpref2addr,
+};
+
+Medium gbemedium =
+{
+.name=		"gbe",
+.hsize=		14,
+.mintu=		60,
+.maxtu=		9014,
+.maclen=	6,
+.bind=		etherbind,
+.unbind=	etherunbind,
+.bwrite=	etherbwrite,
+.addmulti=	etheraddmulti,
+.remmulti=	etherremmulti,
+.ares=		arpenter,
+.areg=		sendgarp,
+.pref2addr=	etherpref2addr,
+};
+
+typedef struct	Etherrock Etherrock;
+struct Etherrock
+{
+	Fs	*f;		/* file system we belong to */
+	Proc	*arpp;		/* arp process */
+	Proc	*read4p;	/* reading process (v4)*/
+	Proc	*read6p;	/* reading process (v6)*/
+	Chan	*mchan4;	/* Data channel for v4 */
+	Chan	*achan;		/* Arp channel */
+	Chan	*cchan4;	/* Control channel for v4 */
+	Chan	*mchan6;	/* Data channel for v6 */
+	Chan	*cchan6;	/* Control channel for v6 */
+};
+
+/*
+ *  ethernet arp request
+ */
+enum
+{
+	ETARP		= 0x0806,
+	ETIP4		= 0x0800,
+	ETIP6		= 0x86DD,
+	ARPREQUEST	= 1,
+	ARPREPLY	= 2,
+};
+
+typedef struct Etherarp Etherarp;
+struct Etherarp
+{
+	uchar	d[6];
+	uchar	s[6];
+	uchar	type[2];
+	uchar	hrd[2];
+	uchar	pro[2];
+	uchar	hln;
+	uchar	pln;
+	uchar	op[2];
+	uchar	sha[6];
+	uchar	spa[4];
+	uchar	tha[6];
+	uchar	tpa[4];
+};
+
+static char *nbmsg = "nonblocking";
+
+/*
+ *  called to bind an IP ifc to an ethernet device
+ *  called with ifc wlock'd
+ */
+static void
+etherbind(Ipifc *ifc, int argc, char **argv)
+{
+	Chan *mchan4, *cchan4, *achan, *mchan6, *cchan6;
+	char addr[Maxpath];	//char addr[2*KNAMELEN];
+	char dir[Maxpath];	//char dir[2*KNAMELEN];
+	char *buf;
+	int fd, cfd, n;
+	char *ptr;
+	Etherrock *er;
+
+	if(argc < 2)
+		error(Ebadarg);
+
+	mchan4 = cchan4 = achan = mchan6 = cchan6 = nil;
+	buf = nil;
+	if(waserror()){
+		if(mchan4 != nil)
+			cclose(mchan4);
+		if(cchan4 != nil)
+			cclose(cchan4);
+		if(achan != nil)
+			cclose(achan);
+		if(mchan6 != nil)
+			cclose(mchan6);
+		if(cchan6 != nil)
+			cclose(cchan6);
+		if(buf != nil)
+			free(buf);
+		nexterror(); 
+	}
+
+	/*
+	 *  open ip converstation
+	 *
+	 *  the dial will fail if the type is already open on
+	 *  this device.
+	 */
+	snprint(addr, sizeof(addr), "%s!0x800", argv[2]);
+	fd = kdial(addr, nil, dir, &cfd);
+	if(fd < 0)
+		errorf("dial 0x800 failed: %s", up->env->errstr);
+	mchan4 = commonfdtochan(fd, ORDWR, 0, 1);
+	cchan4 = commonfdtochan(cfd, ORDWR, 0, 1);
+	kclose(fd);
+	kclose(cfd);
+
+	/*
+	 *  make it non-blocking
+	 */
+	devtab[cchan4->type]->write(cchan4, nbmsg, strlen(nbmsg), 0);
+
+	/*
+	 *  get mac address and speed
+	 */
+	snprint(addr, sizeof(addr), "%s/stats", dir);
+	fd = kopen(addr, OREAD);
+	if(fd < 0)
+		errorf("can't open ether stats: %s", up->env->errstr);
+
+	buf = smalloc(512);
+	n = kread(fd, buf, 511);
+	kclose(fd);
+	if(n <= 0)
+		error(Eio);
+	buf[n] = 0;
+
+	ptr = strstr(buf, "addr: ");
+	if(!ptr)
+		error(Eio);
+	ptr += 6;
+	parsemac(ifc->mac, ptr, 6);
+
+	ptr = strstr(buf, "mbps: ");
+	if(ptr){
+		ptr += 6;
+		ifc->mbps = atoi(ptr);
+	} else
+		ifc->mbps = 100;
+
+	/*
+ 	 *  open arp conversation
+	 */
+	snprint(addr, sizeof(addr), "%s!0x806", argv[2]);
+	fd = kdial(addr, nil, nil, nil);
+	if(fd < 0)
+		errorf("dial 0x806 failed: %s", up->env->errstr);
+	achan = commonfdtochan(fd, ORDWR, 0, 1);
+	kclose(fd);
+
+	/*
+	 *  open ip conversation
+	 *
+	 *  the dial will fail if the type is already open on
+	 *  this device.
+	 */
+	snprint(addr, sizeof(addr), "%s!0x86DD", argv[2]);
+	fd = kdial(addr, nil, dir, &cfd);
+	if(fd < 0)
+		errorf("dial 0x86DD failed: %s", up->env->errstr);
+	mchan6 = commonfdtochan(fd, ORDWR, 0, 1);
+	cchan6 = commonfdtochan(cfd, ORDWR, 0, 1);
+	kclose(fd);
+	kclose(cfd);
+
+	/*
+	 *  make it non-blocking
+	 */
+	devtab[cchan6->type]->write(cchan6, nbmsg, strlen(nbmsg), 0);
+
+	er = smalloc(sizeof(*er));
+	er->mchan4 = mchan4;
+	er->cchan4 = cchan4;
+	er->achan = achan;
+	er->mchan6 = mchan6;
+	er->cchan6 = cchan6;
+	er->f = ifc->conv->p->f;
+	ifc->arg = er;
+
+	free(buf);
+	poperror();
+
+	kproc("etherread4", etherread4, ifc, 0);
+	kproc("recvarpproc", recvarpproc, ifc, 0);
+	kproc("etherread6", etherread6, ifc, 0);
+}
+
+/*
+ *  called with ifc wlock'd
+ */
+static void
+etherunbind(Ipifc *ifc)
+{
+	Etherrock *er = ifc->arg;
+
+	if(er->read4p)
+		postnote(er->read4p, 1, "unbind", 0);
+	if(er->read6p)
+		postnote(er->read6p, 1, "unbind", 0);
+	if(er->arpp)
+		postnote(er->arpp, 1, "unbind", 0);
+
+	/* wait for readers to die */
+	while(er->arpp != 0 || er->read4p != 0 || er->read6p != 0)
+		tsleep(&up->sleep, return0, 0, 300);
+
+	if(er->mchan4 != nil)
+		cclose(er->mchan4);
+	if(er->achan != nil)
+		cclose(er->achan);
+	if(er->cchan4 != nil)
+		cclose(er->cchan4);
+	if(er->mchan6 != nil)
+		cclose(er->mchan6);
+	if(er->cchan6 != nil)
+		cclose(er->cchan6);
+
+	free(er);
+}
+
+/*
+ *  called by ipoput with a single block to write with ifc rlock'd
+ */
+static void
+etherbwrite(Ipifc *ifc, Block *bp, int version, uchar *ip)
+{
+	Etherhdr *eh;
+	Arpent *a;
+	uchar mac[6];
+	Etherrock *er = ifc->arg;
+
+	/* get mac address of destination */
+	a = arpget(er->f->arp, bp, version, ifc, ip, mac);
+	if(a){
+		/* check for broadcast or multicast */
+		bp = multicastarp(er->f, a, ifc->m, mac);
+		if(bp==nil){
+			switch(version){
+			case V4:
+				sendarp(ifc, a);
+				break;
+			case V6: 
+				resolveaddr6(ifc, a);
+				break;
+			default:
+				panic("etherbwrite: version %d", version);
+			}
+			return;
+		}
+	}
+
+	/* make it a single block with space for the ether header */
+	bp = padblock(bp, ifc->m->hsize);
+	if(bp->next)
+		bp = concatblock(bp);
+	if(BLEN(bp) < ifc->mintu)
+		bp = adjustblock(bp, ifc->mintu);
+	eh = (Etherhdr*)bp->rp;
+
+	/* copy in mac addresses and ether type */
+	memmove(eh->s, ifc->mac, sizeof(eh->s));
+	memmove(eh->d, mac, sizeof(eh->d));
+
+ 	switch(version){
+	case V4:
+		eh->t[0] = 0x08;
+		eh->t[1] = 0x00;
+		devtab[er->mchan4->type]->bwrite(er->mchan4, bp, 0);
+		break;
+	case V6:
+		eh->t[0] = 0x86;
+		eh->t[1] = 0xDD;
+		devtab[er->mchan6->type]->bwrite(er->mchan6, bp, 0);
+		break;
+	default:
+		panic("etherbwrite2: version %d", version);
+	}
+	ifc->out++;
+}
+
+
+/*
+ *  process to read from the ethernet
+ */
+static void
+etherread4(void *a)
+{
+	Ipifc *ifc;
+	Block *bp;
+	Etherrock *er;
+
+	ifc = a;
+	er = ifc->arg;
+	er->read4p = up;	/* hide identity under a rock for unbind */
+	if(waserror()){
+		er->read4p = 0;
+		pexit("hangup", 1);
+	}
+	for(;;){
+		bp = devtab[er->mchan4->type]->bread(er->mchan4, ifc->maxtu, 0);
+		if(!canrlock(ifc)){
+			freeb(bp);
+			continue;
+		}
+		if(waserror()){
+			runlock(ifc);
+			nexterror();
+		}
+		ifc->in++;
+		bp->rp += ifc->m->hsize;
+		if(ifc->lifc == nil)
+			freeb(bp);
+		else
+			ipiput4(er->f, ifc, bp);
+		runlock(ifc);
+		poperror();
+	}
+}
+
+
+/*
+ *  process to read from the ethernet, IPv6
+ */
+static void
+etherread6(void *a)
+{
+	Ipifc *ifc;
+	Block *bp;
+	Etherrock *er;
+
+	ifc = a;
+	er = ifc->arg;
+	er->read6p = up;	/* hide identity under a rock for unbind */
+	if(waserror()){
+		er->read6p = 0;
+		pexit("hangup", 1);
+	}
+	for(;;){
+		bp = devtab[er->mchan6->type]->bread(er->mchan6, ifc->maxtu, 0);
+		if(!canrlock(ifc)){
+			freeb(bp);
+			continue;
+		}
+		if(waserror()){
+			runlock(ifc);
+			nexterror();
+		}
+		ifc->in++;
+		bp->rp += ifc->m->hsize;
+		if(ifc->lifc == nil)
+			freeb(bp);
+		else
+			ipiput6(er->f, ifc, bp);
+		runlock(ifc);
+		poperror();
+	}
+}
+
+static void
+etheraddmulti(Ipifc *ifc, uchar *a, uchar *)
+{
+	uchar mac[6];
+	char buf[64];
+	Etherrock *er = ifc->arg;
+	int version;
+
+	version = multicastea(mac, a);
+	sprint(buf, "addmulti %E", mac);
+	switch(version){
+	case V4:
+		devtab[er->cchan4->type]->write(er->cchan4, buf, strlen(buf), 0);
+		break;
+	case V6:
+		devtab[er->cchan6->type]->write(er->cchan6, buf, strlen(buf), 0);
+		break;
+	default:
+		panic("etheraddmulti: version %d", version);
+	}
+}
+
+static void
+etherremmulti(Ipifc *ifc, uchar *a, uchar *)
+{
+	uchar mac[6];
+	char buf[64];
+	Etherrock *er = ifc->arg;
+	int version;
+
+	version = multicastea(mac, a);
+	sprint(buf, "remmulti %E", mac);
+	switch(version){
+	case V4:
+		devtab[er->cchan4->type]->write(er->cchan4, buf, strlen(buf), 0);
+		break;
+	case V6:
+		devtab[er->cchan6->type]->write(er->cchan6, buf, strlen(buf), 0);
+		break;
+	default:
+		panic("etherremmulti: version %d", version);
+	}
+}
+
+/*
+ *  send an ethernet arp
+ *  (only v4, v6 uses the neighbor discovery, rfc1970)
+ */
+static void
+sendarp(Ipifc *ifc, Arpent *a)
+{
+	int n;
+	Block *bp;
+	Etherarp *e;
+	Etherrock *er = ifc->arg;
+
+	/* don't do anything if it's been less than a second since the last */
+	if(NOW - a->ctime < 1000){
+		arprelease(er->f->arp, a);
+		return;
+	}
+
+	/* remove all but the last message */
+	while((bp = a->hold) != nil){
+		if(bp == a->last)
+			break;
+		a->hold = bp->list;
+		freeblist(bp);
+	}
+
+	/* try to keep it around for a second more */
+	a->ctime = NOW;
+	arprelease(er->f->arp, a);
+
+	n = sizeof(Etherarp);
+	if(n < a->type->mintu)
+		n = a->type->mintu;
+	bp = allocb(n);
+	memset(bp->rp, 0, n);
+	e = (Etherarp*)bp->rp;
+	memmove(e->tpa, a->ip+IPv4off, sizeof(e->tpa));
+	ipv4local(ifc, e->spa);
+	memmove(e->sha, ifc->mac, sizeof(e->sha));
+	memset(e->d, 0xff, sizeof(e->d));		/* ethernet broadcast */
+	memmove(e->s, ifc->mac, sizeof(e->s));
+
+	hnputs(e->type, ETARP);
+	hnputs(e->hrd, 1);
+	hnputs(e->pro, ETIP4);
+	e->hln = sizeof(e->sha);
+	e->pln = sizeof(e->spa);
+	hnputs(e->op, ARPREQUEST);
+	bp->wp += n;
+
+	n = devtab[er->achan->type]->bwrite(er->achan, bp, 0);
+	if(n < 0)
+		print("arp: send: %r\n");
+}
+
+static void
+resolveaddr6(Ipifc *ifc, Arpent *a)
+{
+	int sflag;
+	Block *bp;
+	Etherrock *er = ifc->arg;
+	uchar ipsrc[IPaddrlen];
+
+	/* don't do anything if it's been less than a second since the last */
+	if(NOW - a->ctime < ReTransTimer){
+		arprelease(er->f->arp, a);
+		return;
+	}
+
+	/* remove all but the last message */
+	while((bp = a->hold) != nil){
+		if(bp == a->last)
+			break;
+		a->hold = bp->list;
+		freeblist(bp);
+	}
+
+	/* try to keep it around for a second more */
+	a->ctime = NOW;
+	a->rtime = NOW + ReTransTimer;
+	if(a->rxtsrem <= 0) {
+		arprelease(er->f->arp, a);
+		return;
+	}
+
+	a->rxtsrem--;
+	arprelease(er->f->arp, a);
+
+	if(sflag = ipv6anylocal(ifc, ipsrc)) 
+		icmpns(er->f, ipsrc, sflag, a->ip, TARG_MULTI, ifc->mac);
+}
+
+/*
+ *  send a gratuitous arp to refresh arp caches
+ */
+static void
+sendgarp(Ipifc *ifc, uchar *ip)
+{
+	int n;
+	Block *bp;
+	Etherarp *e;
+	Etherrock *er = ifc->arg;
+
+	/* don't arp for our initial non address */
+	if(ipcmp(ip, IPnoaddr) == 0)
+		return;
+
+	n = sizeof(Etherarp);
+	if(n < ifc->m->mintu)
+		n = ifc->m->mintu;
+	bp = allocb(n);
+	memset(bp->rp, 0, n);
+	e = (Etherarp*)bp->rp;
+	memmove(e->tpa, ip+IPv4off, sizeof(e->tpa));
+	memmove(e->spa, ip+IPv4off, sizeof(e->spa));
+	memmove(e->sha, ifc->mac, sizeof(e->sha));
+	memset(e->d, 0xff, sizeof(e->d));		/* ethernet broadcast */
+	memmove(e->s, ifc->mac, sizeof(e->s));
+
+	hnputs(e->type, ETARP);
+	hnputs(e->hrd, 1);
+	hnputs(e->pro, ETIP4);
+	e->hln = sizeof(e->sha);
+	e->pln = sizeof(e->spa);
+	hnputs(e->op, ARPREQUEST);
+	bp->wp += n;
+
+	n = devtab[er->achan->type]->bwrite(er->achan, bp, 0);
+	if(n < 0)
+		print("garp: send: %r\n");
+}
+
+static void
+recvarp(Ipifc *ifc)
+{
+	int n;
+	Block *ebp, *rbp;
+	Etherarp *e, *r;
+	uchar ip[IPaddrlen];
+	static uchar eprinted[4];
+	Etherrock *er = ifc->arg;
+
+	ebp = devtab[er->achan->type]->bread(er->achan, ifc->maxtu, 0);
+	if(ebp == nil) {
+		print("arp: rcv: %r\n");
+		return;
+	}
+
+	e = (Etherarp*)ebp->rp;
+	switch(nhgets(e->op)) {
+	default:
+		break;
+
+	case ARPREPLY:
+		/* check for machine using my ip address */
+		v4tov6(ip, e->spa);
+		if(iplocalonifc(ifc, ip) || ipproxyifc(er->f, ifc, ip)){
+			if(memcmp(e->sha, ifc->mac, sizeof(e->sha)) != 0){
+				print("arprep: 0x%E/0x%E also has ip addr %V\n",
+					e->s, e->sha, e->spa);
+				break;
+			}
+		}
+
+		/* make sure we're not entering broadcast addresses */
+		if(ipcmp(ip, ipbroadcast) == 0 ||
+			!memcmp(e->sha, etherbroadcast, sizeof(e->sha))){
+			print("arprep: 0x%E/0x%E cannot register broadcast address %I\n",
+				e->s, e->sha, e->spa);
+			break;
+		}
+
+		arpenter(er->f, V4, e->spa, e->sha, sizeof(e->sha), 0);
+		break;
+
+	case ARPREQUEST:
+		/* don't answer arps till we know who we are */
+		if(ifc->lifc == 0)
+			break;
+
+		/* check for machine using my ip or ether address */
+		v4tov6(ip, e->spa);
+		if(iplocalonifc(ifc, ip) || ipproxyifc(er->f, ifc, ip)){
+			if(memcmp(e->sha, ifc->mac, sizeof(e->sha)) != 0){
+				if (memcmp(eprinted, e->spa, sizeof(e->spa))){
+					/* print only once */
+					print("arpreq: 0x%E also has ip addr %V\n", e->sha, e->spa);
+					memmove(eprinted, e->spa, sizeof(e->spa));
+				}
+			}
+		} else {
+			if(memcmp(e->sha, ifc->mac, sizeof(e->sha)) == 0){
+				print("arpreq: %V also has ether addr %E\n", e->spa, e->sha);
+				break;
+			}
+		}
+
+		/* refresh what we know about sender */
+		arpenter(er->f, V4, e->spa, e->sha, sizeof(e->sha), 1);
+
+		/* answer only requests for our address or systems we're proxying for */
+		v4tov6(ip, e->tpa);
+		if(!iplocalonifc(ifc, ip))
+		if(!ipproxyifc(er->f, ifc, ip))
+			break;
+
+		n = sizeof(Etherarp);
+		if(n < ifc->mintu)
+			n = ifc->mintu;
+		rbp = allocb(n);
+		r = (Etherarp*)rbp->rp;
+		memset(r, 0, sizeof(Etherarp));
+		hnputs(r->type, ETARP);
+		hnputs(r->hrd, 1);
+		hnputs(r->pro, ETIP4);
+		r->hln = sizeof(r->sha);
+		r->pln = sizeof(r->spa);
+		hnputs(r->op, ARPREPLY);
+		memmove(r->tha, e->sha, sizeof(r->tha));
+		memmove(r->tpa, e->spa, sizeof(r->tpa));
+		memmove(r->sha, ifc->mac, sizeof(r->sha));
+		memmove(r->spa, e->tpa, sizeof(r->spa));
+		memmove(r->d, e->sha, sizeof(r->d));
+		memmove(r->s, ifc->mac, sizeof(r->s));
+		rbp->wp += n;
+
+		n = devtab[er->achan->type]->bwrite(er->achan, rbp, 0);
+		if(n < 0)
+			print("arp: write: %r\n");
+	}
+	freeb(ebp);
+}
+
+static void
+recvarpproc(void *v)
+{
+	Ipifc *ifc = v;
+	Etherrock *er = ifc->arg;
+
+	er->arpp = up;
+	if(waserror()){
+		er->arpp = 0;
+		pexit("hangup", 1);
+	}
+	for(;;)
+		recvarp(ifc);
+}
+
+static int
+multicastea(uchar *ea, uchar *ip)
+{
+	int x;
+
+	switch(x = ipismulticast(ip)){
+	case V4:
+		ea[0] = 0x01;
+		ea[1] = 0x00;
+		ea[2] = 0x5e;
+		ea[3] = ip[13] & 0x7f;
+		ea[4] = ip[14];
+		ea[5] = ip[15];
+		break;
+ 	case V6:
+ 		ea[0] = 0x33;
+ 		ea[1] = 0x33;
+ 		ea[2] = ip[12];
+		ea[3] = ip[13];
+ 		ea[4] = ip[14];
+ 		ea[5] = ip[15];
+ 		break;
+	}
+	return x;
+}
+
+/*
+ *  fill in an arp entry for broadcast or multicast
+ *  addresses.  Return the first queued packet for the
+ *  IP address.
+ */
+static Block*
+multicastarp(Fs *f, Arpent *a, Medium *medium, uchar *mac)
+{
+	/* is it broadcast? */
+	switch(ipforme(f, a->ip)){
+	case Runi:
+		return nil;
+	case Rbcast:
+		memset(mac, 0xff, 6);
+		return arpresolve(f->arp, a, medium, mac);
+	default:
+		break;
+	}
+
+	/* if multicast, fill in mac */
+	switch(multicastea(mac, a->ip)){
+	case V4:
+	case V6:
+		return arpresolve(f->arp, a, medium, mac);
+	}
+
+	/* let arp take care of it */
+	return nil;
+}
+
+void
+ethermediumlink(void)
+{
+	addipmedium(&ethermedium);
+	addipmedium(&gbemedium);
+}
+
+
+static void 
+etherpref2addr(uchar *pref, uchar *ea)
+{
+	pref[8]  = ea[0] | 0x2;
+	pref[9]  = ea[1];
+	pref[10] = ea[2];
+	pref[11] = 0xFF;
+	pref[12] = 0xFE;
+	pref[13] = ea[3];
+	pref[14] = ea[4];
+	pref[15] = ea[5];
+}
--- /dev/null
+++ b/os/ip/gre.c
@@ -1,0 +1,282 @@
+#include "u.h"
+#include "../port/lib.h"
+#include "mem.h"
+#include "dat.h"
+#include "fns.h"
+#include "../port/error.h"
+
+#include "ip.h"
+
+#define DPRINT if(0)print
+
+enum
+{
+	GRE_IPONLY	= 12,		/* size of ip header */
+	GRE_IPPLUSGRE	= 12,		/* minimum size of GRE header */
+	IP_GREPROTO	= 47,
+
+	GRErxms		= 200,
+	GREtickms	= 100,
+	GREmaxxmit	= 10,
+};
+
+typedef struct GREhdr
+{
+	/* ip header */
+	uchar	vihl;		/* Version and header length */
+	uchar	tos;		/* Type of service */
+	uchar	len[2];		/* packet length (including headers) */
+	uchar	id[2];		/* Identification */
+	uchar	frag[2];	/* Fragment information */
+	uchar	Unused;	
+	uchar	proto;		/* Protocol */
+	uchar	cksum[2];	/* checksum */
+	uchar	src[4];		/* Ip source */
+	uchar	dst[4];		/* Ip destination */
+
+	/* gre header */
+	uchar	flags[2];
+	uchar	eproto[2];	/* encapsulation protocol */
+} GREhdr;
+
+typedef struct GREpriv GREpriv;
+struct GREpriv
+{
+	int		raw;			/* Raw GRE mode */
+
+	/* non-MIB stats */
+	ulong		csumerr;		/* checksum errors */
+	ulong		lenerr;			/* short packet */
+};
+
+static void grekick(void *x, Block *bp);
+
+static char*
+greconnect(Conv *c, char **argv, int argc)
+{
+	Proto *p;
+	char *err;
+	Conv *tc, **cp, **ecp;
+
+	err = Fsstdconnect(c, argv, argc);
+	if(err != nil)
+		return err;
+
+	/* make sure noone's already connected to this other sys */
+	p = c->p;
+	qlock(p);
+	ecp = &p->conv[p->nc];
+	for(cp = p->conv; cp < ecp; cp++){
+		tc = *cp;
+		if(tc == nil)
+			break;
+		if(tc == c)
+			continue;
+		if(tc->rport == c->rport && ipcmp(tc->raddr, c->raddr) == 0){
+			err = "already connected to that addr/proto";
+			ipmove(c->laddr, IPnoaddr);
+			ipmove(c->raddr, IPnoaddr);
+			break;
+		}
+	}
+	qunlock(p);
+
+	if(err != nil)
+		return err;
+	Fsconnected(c, nil);
+
+	return nil;
+}
+
+static void
+grecreate(Conv *c)
+{
+	c->rq = qopen(64*1024, Qmsg, 0, c);
+	c->wq = qbypass(grekick, c);
+}
+
+static int
+grestate(Conv *c, char *state, int n)
+{
+	USED(c);
+	return snprint(state, n, "%s", "Datagram");
+}
+
+static char*
+greannounce(Conv*, char**, int)
+{
+	return "pktifc does not support announce";
+}
+
+static void
+greclose(Conv *c)
+{
+	qclose(c->rq);
+	qclose(c->wq);
+	qclose(c->eq);
+	ipmove(c->laddr, IPnoaddr);
+	ipmove(c->raddr, IPnoaddr);
+	c->lport = 0;
+	c->rport = 0;
+}
+
+int drop;
+
+static void
+grekick(void *x, Block *bp)
+{
+	Conv *c = x;
+	GREhdr *ghp;
+	uchar laddr[IPaddrlen], raddr[IPaddrlen];
+
+	if(bp == nil)
+		return;
+
+	/* Make space to fit ip header (gre header already there) */
+	bp = padblock(bp, GRE_IPONLY);
+	if(bp == nil)
+		return;
+
+	/* make sure the message has a GRE header */
+	bp = pullupblock(bp, GRE_IPONLY+GRE_IPPLUSGRE);
+	if(bp == nil)
+		return;
+
+	ghp = (GREhdr *)(bp->rp);
+	ghp->vihl = IP_VER4;
+
+	if(!((GREpriv*)c->p->priv)->raw){
+		v4tov6(raddr, ghp->dst);
+		if(ipcmp(raddr, v4prefix) == 0)
+			memmove(ghp->dst, c->raddr + IPv4off, IPv4addrlen);
+		v4tov6(laddr, ghp->src);
+		if(ipcmp(laddr, v4prefix) == 0){
+			if(ipcmp(c->laddr, IPnoaddr) == 0)
+				findlocalip(c->p->f, c->laddr, raddr); /* pick interface closest to dest */
+			memmove(ghp->src, c->laddr + IPv4off, IPv4addrlen);
+		}
+		hnputs(ghp->eproto, c->rport);
+	}
+
+	ghp->proto = IP_GREPROTO;
+	ghp->frag[0] = 0;
+	ghp->frag[1] = 0;
+
+	ipoput4(c->p->f, bp, 0, c->ttl, c->tos, nil);
+}
+
+static void
+greiput(Proto *gre, Ipifc*, Block *bp)
+{
+	int len;
+	GREhdr *ghp;
+	Conv *c, **p;
+	ushort eproto;
+	uchar raddr[IPaddrlen];
+	GREpriv *gpriv;
+
+	gpriv = gre->priv;
+	ghp = (GREhdr*)(bp->rp);
+
+	v4tov6(raddr, ghp->src);
+	eproto = nhgets(ghp->eproto);
+	qlock(gre);
+
+	/* Look for a conversation structure for this port and address */
+	c = nil;
+	for(p = gre->conv; *p; p++) {
+		c = *p;
+		if(c->inuse == 0)
+			continue;
+		if(c->rport == eproto && 
+			(gpriv->raw || ipcmp(c->raddr, raddr) == 0))
+			break;
+	}
+
+	if(*p == nil) {
+		qunlock(gre);
+		freeblist(bp);
+		return;
+	}
+
+	qunlock(gre);
+
+	/*
+	 * Trim the packet down to data size
+	 */
+	len = nhgets(ghp->len) - GRE_IPONLY;
+	if(len < GRE_IPPLUSGRE){
+		freeblist(bp);
+		return;
+	}
+	bp = trimblock(bp, GRE_IPONLY, len);
+	if(bp == nil){
+		gpriv->lenerr++;
+		return;
+	}
+
+	/*
+	 *  Can't delimit packet so pull it all into one block.
+	 */
+	if(qlen(c->rq) > 64*1024)
+		freeblist(bp);
+	else{
+		bp = concatblock(bp);
+		if(bp == 0)
+			panic("greiput");
+		qpass(c->rq, bp);
+	}
+}
+
+int
+grestats(Proto *gre, char *buf, int len)
+{
+	GREpriv *gpriv;
+
+	gpriv = gre->priv;
+
+	return snprint(buf, len, "gre: len %lud\n", gpriv->lenerr);
+}
+
+char*
+grectl(Conv *c, char **f, int n)
+{
+	GREpriv *gpriv;
+
+	gpriv = c->p->priv;
+	if(n == 1){
+		if(strcmp(f[0], "raw") == 0){
+			gpriv->raw = 1;
+			return nil;
+		}
+		else if(strcmp(f[0], "cooked") == 0){
+			gpriv->raw = 0;
+			return nil;
+		}
+	}
+	return "unknown control request";
+}
+
+void
+greinit(Fs *fs)
+{
+	Proto *gre;
+
+	gre = smalloc(sizeof(Proto));
+	gre->priv = smalloc(sizeof(GREpriv));
+	gre->name = "gre";
+	gre->connect = greconnect;
+	gre->announce = greannounce;
+	gre->state = grestate;
+	gre->create = grecreate;
+	gre->close = greclose;
+	gre->rcv = greiput;
+	gre->ctl = grectl;
+	gre->advise = nil;
+	gre->stats = grestats;
+	gre->ipproto = IP_GREPROTO;
+	gre->nc = 64;
+	gre->ptclsize = 0;
+
+	Fsproto(fs, gre);
+}
--- /dev/null
+++ b/os/ip/icmp.c
@@ -1,0 +1,490 @@
+#include "u.h"
+#include "../port/lib.h"
+#include "mem.h"
+#include "dat.h"
+#include "fns.h"
+#include "../port/error.h"
+
+#include "ip.h"
+
+typedef struct Icmp {
+	uchar	vihl;		/* Version and header length */
+	uchar	tos;		/* Type of service */
+	uchar	length[2];	/* packet length */
+	uchar	id[2];		/* Identification */
+	uchar	frag[2];	/* Fragment information */
+	uchar	ttl;		/* Time to live */
+	uchar	proto;		/* Protocol */
+	uchar	ipcksum[2];	/* Header checksum */
+	uchar	src[4];		/* Ip source */
+	uchar	dst[4];		/* Ip destination */
+	uchar	type;
+	uchar	code;
+	uchar	cksum[2];
+	uchar	icmpid[2];
+	uchar	seq[2];
+	uchar	data[1];
+} Icmp;
+
+enum {			/* Packet Types */
+	EchoReply	= 0,
+	Unreachable	= 3,
+	SrcQuench	= 4,
+	Redirect	= 5,
+	EchoRequest	= 8,
+	TimeExceed	= 11,
+	InParmProblem	= 12,
+	Timestamp	= 13,
+	TimestampReply	= 14,
+	InfoRequest	= 15,
+	InfoReply	= 16,
+	AddrMaskRequest = 17,
+	AddrMaskReply   = 18,
+
+	Maxtype		= 18,
+};
+
+enum
+{
+	MinAdvise	= 24,	/* minimum needed for us to advise another protocol */ 
+};
+
+char *icmpnames[Maxtype+1] =
+{
+[EchoReply]		"EchoReply",
+[Unreachable]		"Unreachable",
+[SrcQuench]		"SrcQuench",
+[Redirect]		"Redirect",
+[EchoRequest]		"EchoRequest",
+[TimeExceed]		"TimeExceed",
+[InParmProblem]		"InParmProblem",
+[Timestamp]		"Timestamp",
+[TimestampReply]	"TimestampReply",
+[InfoRequest]		"InfoRequest",
+[InfoReply]		"InfoReply",
+[AddrMaskRequest]	"AddrMaskRequest",
+[AddrMaskReply  ]	"AddrMaskReply  ",
+};
+
+enum {
+	IP_ICMPPROTO	= 1,
+	ICMP_IPSIZE	= 20,
+	ICMP_HDRSIZE	= 8,
+};
+
+enum
+{
+	InMsgs,
+	InErrors,
+	OutMsgs,
+	CsumErrs,
+	LenErrs,
+	HlenErrs,
+
+	Nstats,
+};
+
+static char *statnames[Nstats] =
+{
+[InMsgs]	"InMsgs",
+[InErrors]	"InErrors",
+[OutMsgs]	"OutMsgs",
+[CsumErrs]	"CsumErrs",
+[LenErrs]	"LenErrs",
+[HlenErrs]	"HlenErrs",
+};
+
+typedef struct Icmppriv Icmppriv;
+struct Icmppriv
+{
+	ulong	stats[Nstats];
+
+	/* message counts */
+	ulong	in[Maxtype+1];
+	ulong	out[Maxtype+1];
+};
+
+static void icmpkick(void *x, Block*);
+
+static void
+icmpcreate(Conv *c)
+{
+	c->rq = qopen(64*1024, Qmsg, 0, c);
+	c->wq = qbypass(icmpkick, c);
+}
+
+extern char*
+icmpconnect(Conv *c, char **argv, int argc)
+{
+	char *e;
+
+	e = Fsstdconnect(c, argv, argc);
+	if(e != nil)
+		return e;
+	Fsconnected(c, e);
+
+	return nil;
+}
+
+extern int
+icmpstate(Conv *c, char *state, int n)
+{
+	USED(c);
+	return snprint(state, n, "%s qin %d qout %d",
+		"Datagram",
+		c->rq ? qlen(c->rq) : 0,
+		c->wq ? qlen(c->wq) : 0
+	);
+}
+
+extern char*
+icmpannounce(Conv *c, char **argv, int argc)
+{
+	char *e;
+
+	e = Fsstdannounce(c, argv, argc);
+	if(e != nil)
+		return e;
+	Fsconnected(c, nil);
+
+	return nil;
+}
+
+extern void
+icmpclose(Conv *c)
+{
+	qclose(c->rq);
+	qclose(c->wq);
+	ipmove(c->laddr, IPnoaddr);
+	ipmove(c->raddr, IPnoaddr);
+	c->lport = 0;
+}
+
+static void
+icmpkick(void *x, Block *bp)
+{
+	Conv *c = x;
+	Icmp *p;
+	Icmppriv *ipriv;
+
+	if(bp == nil)
+		return;
+
+	if(blocklen(bp) < ICMP_IPSIZE + ICMP_HDRSIZE){
+		freeblist(bp);
+		return;
+	}
+	p = (Icmp *)(bp->rp);
+	p->vihl = IP_VER4;
+	ipriv = c->p->priv;
+	if(p->type <= Maxtype)	
+		ipriv->out[p->type]++;
+	
+	v6tov4(p->dst, c->raddr);
+	v6tov4(p->src, c->laddr);
+	p->proto = IP_ICMPPROTO;
+	hnputs(p->icmpid, c->lport);
+	memset(p->cksum, 0, sizeof(p->cksum));
+	hnputs(p->cksum, ptclcsum(bp, ICMP_IPSIZE, blocklen(bp) - ICMP_IPSIZE));
+	ipriv->stats[OutMsgs]++;
+	ipoput4(c->p->f, bp, 0, c->ttl, c->tos, nil);
+}
+
+extern void
+icmpttlexceeded(Fs *f, uchar *ia, Block *bp)
+{
+	Block	*nbp;
+	Icmp	*p, *np;
+
+	p = (Icmp *)bp->rp;
+
+	netlog(f, Logicmp, "sending icmpttlexceeded -> %V\n", p->src);
+	nbp = allocb(ICMP_IPSIZE + ICMP_HDRSIZE + ICMP_IPSIZE + 8);
+	nbp->wp += ICMP_IPSIZE + ICMP_HDRSIZE + ICMP_IPSIZE + 8;
+	np = (Icmp *)nbp->rp;
+	np->vihl = IP_VER4;
+	memmove(np->dst, p->src, sizeof(np->dst));
+	v6tov4(np->src, ia);
+	memmove(np->data, bp->rp, ICMP_IPSIZE + 8);
+	np->type = TimeExceed;
+	np->code = 0;
+	np->proto = IP_ICMPPROTO;
+	hnputs(np->icmpid, 0);
+	hnputs(np->seq, 0);
+	memset(np->cksum, 0, sizeof(np->cksum));
+	hnputs(np->cksum, ptclcsum(nbp, ICMP_IPSIZE, blocklen(nbp) - ICMP_IPSIZE));
+	ipoput4(f, nbp, 0, MAXTTL, DFLTTOS, nil);
+
+}
+
+static void
+icmpunreachable(Fs *f, Block *bp, int code, int seq)
+{
+	Block	*nbp;
+	Icmp	*p, *np;
+	int	i;
+	uchar	addr[IPaddrlen];
+
+	p = (Icmp *)bp->rp;
+
+	/* only do this for unicast sources and destinations */
+	v4tov6(addr, p->dst);
+	i = ipforme(f, addr);
+	if((i&Runi) == 0)
+		return;
+	v4tov6(addr, p->src);
+	i = ipforme(f, addr);
+	if(i != 0 && (i&Runi) == 0)
+		return;
+
+	netlog(f, Logicmp, "sending icmpnoconv -> %V\n", p->src);
+	nbp = allocb(ICMP_IPSIZE + ICMP_HDRSIZE + ICMP_IPSIZE + 8);
+	nbp->wp += ICMP_IPSIZE + ICMP_HDRSIZE + ICMP_IPSIZE + 8;
+	np = (Icmp *)nbp->rp;
+	np->vihl = IP_VER4;
+	memmove(np->dst, p->src, sizeof(np->dst));
+	memmove(np->src, p->dst, sizeof(np->src));
+	memmove(np->data, bp->rp, ICMP_IPSIZE + 8);
+	np->type = Unreachable;
+	np->code = code;
+	np->proto = IP_ICMPPROTO;
+	hnputs(np->icmpid, 0);
+	hnputs(np->seq, seq);
+	memset(np->cksum, 0, sizeof(np->cksum));
+	hnputs(np->cksum, ptclcsum(nbp, ICMP_IPSIZE, blocklen(nbp) - ICMP_IPSIZE));
+	ipoput4(f, nbp, 0, MAXTTL, DFLTTOS, nil);
+}
+
+extern void
+icmpnoconv(Fs *f, Block *bp)
+{
+	icmpunreachable(f, bp, 3, 0);
+}
+
+extern void
+icmpcantfrag(Fs *f, Block *bp, int mtu)
+{
+	icmpunreachable(f, bp, 4, mtu);
+}
+
+static void
+goticmpkt(Proto *icmp, Block *bp)
+{
+	Conv	**c, *s;
+	Icmp	*p;
+	uchar	dst[IPaddrlen];
+	ushort	recid;
+
+	p = (Icmp *) bp->rp;
+	v4tov6(dst, p->src);
+	recid = nhgets(p->icmpid);
+
+	for(c = icmp->conv; *c; c++) {
+		s = *c;
+		if(s->lport == recid)
+		if(ipcmp(s->raddr, dst) == 0){
+			bp = concatblock(bp);
+			if(bp != nil)
+				qpass(s->rq, bp);
+			return;
+		}
+	}
+	freeblist(bp);
+}
+
+static Block *
+mkechoreply(Block *bp)
+{
+	Icmp	*q;
+	uchar	ip[4];
+
+	q = (Icmp *)bp->rp;
+	q->vihl = IP_VER4;
+	memmove(ip, q->src, sizeof(q->dst));
+	memmove(q->src, q->dst, sizeof(q->src));
+	memmove(q->dst, ip,  sizeof(q->dst));
+	q->type = EchoReply;
+	memset(q->cksum, 0, sizeof(q->cksum));
+	hnputs(q->cksum, ptclcsum(bp, ICMP_IPSIZE, blocklen(bp) - ICMP_IPSIZE));
+
+	return bp;
+}
+
+static char *unreachcode[] =
+{
+[0]	"net unreachable",
+[1]	"host unreachable",
+[2]	"protocol unreachable",
+[3]	"port unreachable",
+[4]	"fragmentation needed and DF set",
+[5]	"source route failed",
+};
+
+static void
+icmpiput(Proto *icmp, Ipifc*, Block *bp)
+{
+	int	n, iplen;
+	Icmp	*p;
+	Block	*r;
+	Proto	*pr;
+	char	*msg;
+	char	m2[128];
+	Icmppriv *ipriv;
+
+	ipriv = icmp->priv;
+	
+	ipriv->stats[InMsgs]++;
+
+	p = (Icmp *)bp->rp;
+	netlog(icmp->f, Logicmp, "icmpiput %d %d\n", p->type, p->code);
+	n = blocklen(bp);
+	if(n < ICMP_IPSIZE+ICMP_HDRSIZE){
+		ipriv->stats[InErrors]++;
+		ipriv->stats[HlenErrs]++;
+		netlog(icmp->f, Logicmp, "icmp hlen %d\n", n);
+		goto raise;
+	}
+	iplen = nhgets(p->length);
+	if(iplen > n || (iplen % 1)){
+		ipriv->stats[LenErrs]++;
+		ipriv->stats[InErrors]++;
+		netlog(icmp->f, Logicmp, "icmp length %d\n", iplen);
+		goto raise;
+	}
+	if(ptclcsum(bp, ICMP_IPSIZE, iplen - ICMP_IPSIZE)){
+		ipriv->stats[InErrors]++;
+		ipriv->stats[CsumErrs]++;
+		netlog(icmp->f, Logicmp, "icmp checksum error\n");
+		goto raise;
+	}
+	if(p->type <= Maxtype)
+		ipriv->in[p->type]++;
+
+	switch(p->type) {
+	case EchoRequest:
+		if (iplen < n)
+			bp = trimblock(bp, 0, iplen);
+		r = mkechoreply(bp);
+		ipriv->out[EchoReply]++;
+		ipoput4(icmp->f, r, 0, MAXTTL, DFLTTOS, nil);
+		break;
+	case Unreachable:
+		if(p->code > 5)
+			msg = unreachcode[1];
+		else
+			msg = unreachcode[p->code];
+
+		bp->rp += ICMP_IPSIZE+ICMP_HDRSIZE;
+		if(blocklen(bp) < MinAdvise){
+			ipriv->stats[LenErrs]++;
+			goto raise;
+		}
+		p = (Icmp *)bp->rp;
+		pr = Fsrcvpcolx(icmp->f, p->proto);
+		if(pr != nil && pr->advise != nil) {
+			(*pr->advise)(pr, bp, msg);
+			return;
+		}
+
+		bp->rp -= ICMP_IPSIZE+ICMP_HDRSIZE;
+		goticmpkt(icmp, bp);
+		break;
+	case TimeExceed:
+		if(p->code == 0){
+			sprint(m2, "ttl exceeded at %V", p->src);
+
+			bp->rp += ICMP_IPSIZE+ICMP_HDRSIZE;
+			if(blocklen(bp) < MinAdvise){
+				ipriv->stats[LenErrs]++;
+				goto raise;
+			}
+			p = (Icmp *)bp->rp;
+			pr = Fsrcvpcolx(icmp->f, p->proto);
+			if(pr != nil && pr->advise != nil) {
+				(*pr->advise)(pr, bp, m2);
+				return;
+			}
+			bp->rp -= ICMP_IPSIZE+ICMP_HDRSIZE;
+		}
+
+		goticmpkt(icmp, bp);
+		break;
+	default:
+		goticmpkt(icmp, bp);
+		break;
+	}
+	return;
+
+raise:
+	freeblist(bp);
+}
+
+void
+icmpadvise(Proto *icmp, Block *bp, char *msg)
+{
+	Conv	**c, *s;
+	Icmp	*p;
+	uchar	dst[IPaddrlen];
+	ushort	recid;
+
+	p = (Icmp *) bp->rp;
+	v4tov6(dst, p->dst);
+	recid = nhgets(p->icmpid);
+
+	for(c = icmp->conv; *c; c++) {
+		s = *c;
+		if(s->lport == recid)
+		if(ipcmp(s->raddr, dst) == 0){
+			qhangup(s->rq, msg);
+			qhangup(s->wq, msg);
+			break;
+		}
+	}
+	freeblist(bp);
+}
+
+int
+icmpstats(Proto *icmp, char *buf, int len)
+{
+	Icmppriv *priv;
+	char *p, *e;
+	int i;
+
+	priv = icmp->priv;
+	p = buf;
+	e = p+len;
+	for(i = 0; i < Nstats; i++)
+		p = seprint(p, e, "%s: %lud\n", statnames[i], priv->stats[i]);
+	for(i = 0; i <= Maxtype; i++){
+		if(icmpnames[i])
+			p = seprint(p, e, "%s: %lud %lud\n", icmpnames[i], priv->in[i], priv->out[i]);
+		else
+			p = seprint(p, e, "%d: %lud %lud\n", i, priv->in[i], priv->out[i]);
+	}
+	return p - buf;
+}
+	
+void
+icmpinit(Fs *fs)
+{
+	Proto *icmp;
+
+	icmp = smalloc(sizeof(Proto));
+	icmp->priv = smalloc(sizeof(Icmppriv));
+	icmp->name = "icmp";
+	icmp->connect = icmpconnect;
+	icmp->announce = icmpannounce;
+	icmp->state = icmpstate;
+	icmp->create = icmpcreate;
+	icmp->close = icmpclose;
+	icmp->rcv = icmpiput;
+	icmp->stats = icmpstats;
+	icmp->ctl = nil;
+	icmp->advise = icmpadvise;
+	icmp->gc = nil;
+	icmp->ipproto = IP_ICMPPROTO;
+	icmp->nc = 128;
+	icmp->ptclsize = 0;
+
+	Fsproto(fs, icmp);
+}
--- /dev/null
+++ b/os/ip/icmp6.c
@@ -1,0 +1,917 @@
+#include "u.h"
+#include "../port/lib.h"
+#include "mem.h"
+#include "dat.h"
+#include "fns.h"
+#include "../port/error.h"
+#include "ip.h"
+#include "ipv6.h"
+
+typedef struct ICMPpkt ICMPpkt;
+typedef struct IPICMP IPICMP;
+typedef struct Ndpkt Ndpkt;
+typedef struct NdiscC NdiscC;
+
+struct ICMPpkt {
+	uchar	type;
+	uchar	code;
+	uchar	cksum[2];
+	uchar	icmpid[2];
+	uchar	seq[2];
+};
+
+struct IPICMP {
+	Ip6hdr;
+	ICMPpkt;
+};
+
+struct NdiscC
+{
+	IPICMP;
+	uchar target[IPaddrlen];
+};
+
+struct Ndpkt
+{
+	NdiscC;
+	uchar otype;
+	uchar olen;	// length in units of 8 octets(incl type, code),
+				// 1 for IEEE 802 addresses
+	uchar lnaddr[6];	// link-layer address
+};
+
+enum {	
+	// ICMPv6 types
+	EchoReply	= 0,
+	UnreachableV6	= 1,
+	PacketTooBigV6	= 2,
+	TimeExceedV6	= 3,
+	SrcQuench	= 4,
+	ParamProblemV6	= 4,
+	Redirect	= 5,
+	EchoRequest	= 8,
+	TimeExceed	= 11,
+	InParmProblem	= 12,
+	Timestamp	= 13,
+	TimestampReply	= 14,
+	InfoRequest	= 15,
+	InfoReply	= 16,
+	AddrMaskRequest = 17,
+	AddrMaskReply   = 18,
+	EchoRequestV6	= 128,
+	EchoReplyV6	= 129,
+	RouterSolicit	= 133,
+	RouterAdvert	= 134,
+	NbrSolicit	= 135,
+	NbrAdvert	= 136,
+	RedirectV6	= 137,
+
+	Maxtype6	= 137,
+};
+
+char *icmpnames6[Maxtype6+1] =
+{
+[EchoReply]		"EchoReply",
+[UnreachableV6]		"UnreachableV6",
+[PacketTooBigV6]	"PacketTooBigV6",
+[TimeExceedV6]		"TimeExceedV6",
+[SrcQuench]		"SrcQuench",
+[Redirect]		"Redirect",
+[EchoRequest]		"EchoRequest",
+[TimeExceed]		"TimeExceed",
+[InParmProblem]		"InParmProblem",
+[Timestamp]		"Timestamp",
+[TimestampReply]	"TimestampReply",
+[InfoRequest]		"InfoRequest",
+[InfoReply]		"InfoReply",
+[AddrMaskRequest]	"AddrMaskRequest",
+[AddrMaskReply]		"AddrMaskReply",
+[EchoRequestV6]		"EchoRequestV6",
+[EchoReplyV6]		"EchoReplyV6",
+[RouterSolicit]		"RouterSolicit",
+[RouterAdvert]		"RouterAdvert",
+[NbrSolicit]		"NbrSolicit",
+[NbrAdvert]		"NbrAdvert",
+[RedirectV6]		"RedirectV6",
+};
+
+enum
+{
+	InMsgs6,
+	InErrors6,
+	OutMsgs6,
+	CsumErrs6,
+	LenErrs6,
+	HlenErrs6,
+	HoplimErrs6,
+	IcmpCodeErrs6,
+	TargetErrs6,
+	OptlenErrs6,
+	AddrmxpErrs6,
+	RouterAddrErrs6,
+
+	Nstats6,
+};
+
+static char *statnames6[Nstats6] =
+{
+[InMsgs6]	"InMsgs",
+[InErrors6]	"InErrors",
+[OutMsgs6]	"OutMsgs",
+[CsumErrs6]	"CsumErrs",
+[LenErrs6]	"LenErrs",
+[HlenErrs6]	"HlenErrs",
+[HoplimErrs6]	"HoplimErrs",
+[IcmpCodeErrs6]	"IcmpCodeErrs",
+[TargetErrs6]	"TargetErrs",
+[OptlenErrs6]	"OptlenErrs",
+[AddrmxpErrs6]	"AddrmxpErrs",
+[RouterAddrErrs6]	"RouterAddrErrs",
+};
+
+typedef struct Icmppriv6
+{
+	ulong	stats[Nstats6];
+
+	/* message counts */
+	ulong	in[Maxtype6+1];
+	ulong	out[Maxtype6+1];
+} Icmppriv6;
+
+typedef struct Icmpcb6 
+{
+	QLock;
+	uchar headers;
+} Icmpcb6;
+
+static char *unreachcode[] =
+{
+[icmp6_no_route]	"no route to destination",
+[icmp6_ad_prohib]	"comm with destination administratively prohibited",
+[icmp6_unassigned]	"icmp unreachable: unassigned error code (2)",
+[icmp6_adr_unreach]	"address unreachable",
+[icmp6_port_unreach]	"port unreachable",
+[icmp6_unkn_code]	"icmp unreachable: unknown code",
+};
+
+enum {
+	ICMP_USEAD6	= 40,
+};
+
+enum {
+	Oflag	= 1<<5,
+	Sflag	= 1<<6,
+	Rflag	= 1<<7,
+};
+
+enum {
+	slladd	= 1,
+	tlladd	= 2,
+	prfinfo	= 3,
+	redhdr	= 4,
+	mtuopt	= 5,
+};
+
+static void icmpkick6(void *x, Block *bp);
+
+static void
+icmpcreate6(Conv *c)
+{
+	c->rq = qopen(64*1024, Qmsg, 0, c);
+	c->wq = qbypass(icmpkick6, c);
+}
+
+static void
+set_cksum(Block *bp)
+{
+	IPICMP *p = (IPICMP *)(bp->rp);
+
+	hnputl(p->vcf, 0);  // borrow IP header as pseudoheader
+	hnputs(p->ploadlen, blocklen(bp)-IPV6HDR_LEN);
+	p->proto = 0;
+	p->ttl = ICMPv6;	// ttl gets set later
+	hnputs(p->cksum, 0);
+	hnputs(p->cksum, ptclcsum(bp, 0, blocklen(bp)));
+	p->proto = ICMPv6;
+}
+
+static Block *
+newIPICMP(int packetlen)
+{
+	Block	*nbp;
+	nbp = allocb(packetlen);
+	nbp->wp += packetlen;
+	memset(nbp->rp, 0, packetlen);
+	return nbp;
+}
+
+void
+icmpadvise6(Proto *icmp, Block *bp, char *msg)
+{
+	Conv	**c, *s;
+	IPICMP	*p;
+	ushort	recid;
+
+	p = (IPICMP *) bp->rp;
+	recid = nhgets(p->icmpid);
+
+	for(c = icmp->conv; *c; c++) {
+		s = *c;
+		if(s->lport == recid)
+		if(ipcmp(s->raddr, p->dst) == 0){
+			qhangup(s->rq, msg);
+			qhangup(s->wq, msg);
+			break;
+		}
+	}
+	freeblist(bp);
+}
+
+static void
+icmpkick6(void *x, Block *bp)
+{
+	Conv *c = x;
+	IPICMP *p;
+	uchar laddr[IPaddrlen], raddr[IPaddrlen];
+	Icmppriv6 *ipriv = c->p->priv;
+	Icmpcb6 *icb = (Icmpcb6*)c->ptcl;
+
+	if(bp == nil)
+		return;
+
+	if(icb->headers==6) {
+		/* get user specified addresses */
+		bp = pullupblock(bp, ICMP_USEAD6);
+		if(bp == nil)
+			return;
+		bp->rp += 8;
+		ipmove(laddr, bp->rp);
+		bp->rp += IPaddrlen;
+		ipmove(raddr, bp->rp);
+		bp->rp += IPaddrlen;
+		bp = padblock(bp, sizeof(Ip6hdr));
+	}
+
+	if(blocklen(bp) < sizeof(IPICMP)){
+		freeblist(bp);
+		return;
+	}
+	p = (IPICMP *)(bp->rp);
+	if(icb->headers == 6) {
+		ipmove(p->dst, raddr);
+		ipmove(p->src, laddr);
+	} else {
+		ipmove(p->dst, c->raddr);
+		ipmove(p->src, c->laddr);
+		hnputs(p->icmpid, c->lport);
+	}
+
+	set_cksum(bp);
+	p->vcf[0] = 0x06 << 4;
+	if(p->type <= Maxtype6)	
+		ipriv->out[p->type]++;
+	ipoput6(c->p->f, bp, 0, c->ttl, c->tos, nil);
+}
+
+char*
+icmpctl6(Conv *c, char **argv, int argc)
+{
+	Icmpcb6 *icb;
+
+	icb = (Icmpcb6*) c->ptcl;
+
+	if(argc==1) {
+		if(strcmp(argv[0], "headers")==0) {
+			icb->headers = 6;
+			return nil;
+		}
+	}
+	return "unknown control request";
+}
+
+static void
+goticmpkt6(Proto *icmp, Block *bp, int muxkey)
+{
+	Conv	**c, *s;
+	IPICMP	*p = (IPICMP *)bp->rp;
+	ushort	recid; 
+	uchar 	*addr;
+
+	if(muxkey == 0) {
+		recid = nhgets(p->icmpid);
+		addr = p->src;
+	}
+	else {
+		recid = muxkey;
+		addr = p->dst;
+	}
+
+	for(c = icmp->conv; *c; c++){
+		s = *c;
+		if(s->lport == recid && ipcmp(s->raddr, addr) == 0){
+			bp = concatblock(bp);
+			if(bp != nil)
+				qpass(s->rq, bp);
+			return;
+		}
+	}
+
+	freeblist(bp);
+}
+
+static Block *
+mkechoreply6(Block *bp)
+{
+	IPICMP *p = (IPICMP *)(bp->rp);
+	uchar	addr[IPaddrlen];
+
+	ipmove(addr, p->src);
+	ipmove(p->src, p->dst);
+	ipmove(p->dst, addr);
+	p->type = EchoReplyV6;
+	set_cksum(bp);
+	return bp;
+}
+
+/*
+ * sends out an ICMPv6 neighbor solicitation
+ * 	suni == SRC_UNSPEC or SRC_UNI, 
+ *	tuni == TARG_MULTI => multicast for address resolution,
+ * 	and tuni == TARG_UNI => neighbor reachability.
+ */
+
+extern void
+icmpns(Fs *f, uchar* src, int suni, uchar* targ, int tuni, uchar* mac)
+{
+	Block	*nbp;
+	Ndpkt *np;
+	Proto *icmp = f->t2p[ICMPv6];
+	Icmppriv6 *ipriv = icmp->priv;
+
+
+	nbp = newIPICMP(sizeof(Ndpkt));
+	np = (Ndpkt*) nbp->rp;
+
+
+	if(suni == SRC_UNSPEC) 
+		memmove(np->src, v6Unspecified, IPaddrlen);
+	else 
+		memmove(np->src, src, IPaddrlen);
+
+	if(tuni == TARG_UNI)
+		memmove(np->dst, targ, IPaddrlen);
+	else
+		ipv62smcast(np->dst, targ);
+
+	np->type = NbrSolicit;
+	np->code = 0;
+	memmove(np->target, targ, IPaddrlen);
+	if(suni != SRC_UNSPEC) {
+		np->otype = SRC_LLADDRESS;
+		np->olen = 1;	/* 1+1+6 = 8 = 1 8-octet */
+		memmove(np->lnaddr, mac, sizeof(np->lnaddr));
+	}
+	else {
+		int r = sizeof(Ndpkt)-sizeof(NdiscC);
+		nbp->wp -= r;
+	}
+
+	set_cksum(nbp);
+	np = (Ndpkt*) nbp->rp;
+	np->ttl = HOP_LIMIT;
+	np->vcf[0] = 0x06 << 4;
+	ipriv->out[NbrSolicit]++;
+	netlog(f, Logicmp, "sending neighbor solicitation %I\n", targ);
+	ipoput6(f, nbp, 0, MAXTTL, DFLTTOS, nil);
+}
+
+/*
+ * sends out an ICMPv6 neighbor advertisement. pktflags == RSO flags.
+ */
+extern void
+icmpna(Fs *f, uchar* src, uchar* dst, uchar* targ, uchar* mac, uchar flags)
+{
+	Block	*nbp;
+	Ndpkt *np;
+	Proto *icmp = f->t2p[ICMPv6];
+	Icmppriv6 *ipriv = icmp->priv;
+
+	nbp = newIPICMP(sizeof(Ndpkt));
+	np = (Ndpkt*) nbp->rp;
+
+	memmove(np->src, src, IPaddrlen);
+	memmove(np->dst, dst, IPaddrlen);
+
+	np->type = NbrAdvert;
+	np->code = 0;
+	np->icmpid[0] = flags;
+	memmove(np->target, targ, IPaddrlen);
+
+	np->otype = TARGET_LLADDRESS;
+	np->olen = 1;	
+	memmove(np->lnaddr, mac, sizeof(np->lnaddr));
+
+	set_cksum(nbp);
+	np = (Ndpkt*) nbp->rp;
+	np->ttl = HOP_LIMIT;
+	np->vcf[0] = 0x06 << 4;
+	ipriv->out[NbrAdvert]++;
+	netlog(f, Logicmp, "sending neighbor advertisement %I\n", src);
+	ipoput6(f, nbp, 0, MAXTTL, DFLTTOS, nil);
+}
+
+extern void
+icmphostunr(Fs *f, Ipifc *ifc, Block *bp, int code, int free)
+{
+	Block *nbp;
+	IPICMP *np;
+	Ip6hdr	*p;
+	int osz = BLEN(bp);
+	int sz = MIN(sizeof(IPICMP) + osz, v6MINTU);
+	Proto	*icmp = f->t2p[ICMPv6];
+	Icmppriv6 *ipriv = icmp->priv;
+
+	p = (Ip6hdr *) bp->rp;
+
+	if(isv6mcast(p->src)) 
+		goto clean;
+
+	nbp = newIPICMP(sz);
+	np = (IPICMP *) nbp->rp;
+
+	rlock(ifc);
+	if(ipv6anylocal(ifc, np->src)) {
+		netlog(f, Logicmp, "send icmphostunr -> s%I d%I\n", p->src, p->dst);
+	}
+	else {
+		netlog(f, Logicmp, "icmphostunr fail -> s%I d%I\n", p->src, p->dst);
+		freeblist(nbp);
+		if(free) 
+			goto clean;
+		else
+			return;
+	}
+
+	memmove(np->dst, p->src, IPaddrlen);
+	np->type = UnreachableV6;
+	np->code = code;
+	memmove(nbp->rp + sizeof(IPICMP), bp->rp, sz - sizeof(IPICMP));
+	set_cksum(nbp);
+	np->ttl = HOP_LIMIT;
+	np->vcf[0] = 0x06 << 4;
+	ipriv->out[UnreachableV6]++;
+
+	if(free)
+		ipiput6(f, ifc, nbp);
+	else {
+		ipoput6(f, nbp, 0, MAXTTL, DFLTTOS, nil);
+		return;
+	}
+
+clean:
+	runlock(ifc);
+	freeblist(bp);
+}
+
+extern void
+icmpttlexceeded6(Fs *f, Ipifc *ifc, Block *bp)
+{
+	Block *nbp;
+	IPICMP *np;
+	Ip6hdr	*p;
+	int osz = BLEN(bp);
+	int sz = MIN(sizeof(IPICMP) + osz, v6MINTU);
+	Proto	*icmp = f->t2p[ICMPv6];
+	Icmppriv6 *ipriv = icmp->priv;
+
+	p = (Ip6hdr *) bp->rp;
+
+	if(isv6mcast(p->src)) 
+		return;
+
+	nbp = newIPICMP(sz);
+	np = (IPICMP *) nbp->rp;
+
+	if(ipv6anylocal(ifc, np->src)) {
+		netlog(f, Logicmp, "send icmpttlexceeded6 -> s%I d%I\n", p->src, p->dst);
+	}
+	else {
+		netlog(f, Logicmp, "icmpttlexceeded6 fail -> s%I d%I\n", p->src, p->dst);
+		return;
+	}
+
+	memmove(np->dst, p->src, IPaddrlen);
+	np->type = TimeExceedV6;
+	np->code = 0;
+	memmove(nbp->rp + sizeof(IPICMP), bp->rp, sz - sizeof(IPICMP));
+	set_cksum(nbp);
+	np->ttl = HOP_LIMIT;
+	np->vcf[0] = 0x06 << 4;
+	ipriv->out[TimeExceedV6]++;
+	ipoput6(f, nbp, 0, MAXTTL, DFLTTOS, nil);
+}
+
+extern void
+icmppkttoobig6(Fs *f, Ipifc *ifc, Block *bp)
+{
+	Block *nbp;
+	IPICMP *np;
+	Ip6hdr	*p;
+	int osz = BLEN(bp);
+	int sz = MIN(sizeof(IPICMP) + osz, v6MINTU);
+	Proto	*icmp = f->t2p[ICMPv6];
+	Icmppriv6 *ipriv = icmp->priv;
+
+	p = (Ip6hdr *) bp->rp;
+
+	if(isv6mcast(p->src)) 
+		return;
+
+	nbp = newIPICMP(sz);
+	np = (IPICMP *) nbp->rp;
+
+	if(ipv6anylocal(ifc, np->src)) {
+		netlog(f, Logicmp, "send icmppkttoobig6 -> s%I d%I\n", p->src, p->dst);
+	}
+	else {
+		netlog(f, Logicmp, "icmppkttoobig6 fail -> s%I d%I\n", p->src, p->dst);
+		return;
+	}
+
+	memmove(np->dst, p->src, IPaddrlen);
+	np->type = PacketTooBigV6;
+	np->code = 0;
+	hnputl(np->icmpid, ifc->maxtu - ifc->m->hsize);
+	memmove(nbp->rp + sizeof(IPICMP), bp->rp, sz - sizeof(IPICMP));
+	set_cksum(nbp);
+	np->ttl = HOP_LIMIT;
+	np->vcf[0] = 0x06 << 4;
+	ipriv->out[PacketTooBigV6]++;
+	ipoput6(f, nbp, 0, MAXTTL, DFLTTOS, nil);
+}
+
+/*
+ * RFC 2461, pages 39-40, pages 57-58.
+ */
+static int
+valid(Proto *icmp, Ipifc *ifc, Block *bp, Icmppriv6 *ipriv) {
+	int 	sz, osz, unsp, n, ttl, iplen;
+	int 	pktsz = BLEN(bp);
+	uchar	*packet = bp->rp;
+	IPICMP	*p = (IPICMP *) packet;
+	Ndpkt	*np;
+
+	USED(ifc);
+	n = blocklen(bp);
+	if(n < sizeof(IPICMP)) {
+		ipriv->stats[HlenErrs6]++;
+		netlog(icmp->f, Logicmp, "icmp hlen %d\n", n);
+		goto err;
+	}
+
+	iplen = nhgets(p->ploadlen);
+	if(iplen > n-IPV6HDR_LEN || (iplen % 1)) {
+		ipriv->stats[LenErrs6]++;
+		netlog(icmp->f, Logicmp, "icmp length %d\n", iplen);
+		goto err;
+	}
+
+	// Rather than construct explicit pseudoheader, overwrite IPv6 header
+	if(p->proto != ICMPv6) {
+		// This code assumes no extension headers!!!
+		netlog(icmp->f, Logicmp, "icmp error: extension header\n");
+		goto err;
+	}
+	memset(packet, 0, 4);
+	ttl = p->ttl;
+	p->ttl = p->proto;
+	p->proto = 0;
+	if(ptclcsum(bp, 0, iplen + IPV6HDR_LEN)) {
+		ipriv->stats[CsumErrs6]++;
+		netlog(icmp->f, Logicmp, "icmp checksum error\n");
+		goto err;
+	}
+	p->proto = p->ttl;
+	p->ttl = ttl;
+
+	/* additional tests for some pkt types */
+	if( (p->type == NbrSolicit) ||
+		(p->type == NbrAdvert) ||
+		(p->type == RouterAdvert) ||
+		(p->type == RouterSolicit) ||
+		(p->type == RedirectV6) ) {
+
+		if(p->ttl != HOP_LIMIT) {
+			ipriv->stats[HoplimErrs6]++; 
+			goto err; 
+		}
+		if(p->code != 0) {
+			ipriv->stats[IcmpCodeErrs6]++; 
+			goto err; 
+		}
+
+		switch (p->type) {
+		case NbrSolicit:
+		case NbrAdvert:
+			np = (Ndpkt*) p;
+			if(isv6mcast(np->target)) {
+				ipriv->stats[TargetErrs6]++; 
+				goto err; 
+			}
+			if(optexsts(np) && (np->olen == 0)) {
+				ipriv->stats[OptlenErrs6]++; 
+				goto err; 
+			}
+		
+			if(p->type == NbrSolicit) {
+				if(ipcmp(np->src, v6Unspecified) == 0) { 
+					if(!issmcast(np->dst) || optexsts(np))  {
+						ipriv->stats[AddrmxpErrs6]++; 
+						goto err;
+					}
+				}
+			}
+		
+			if(p->type == NbrAdvert) {
+				if((isv6mcast(np->dst))&&(nhgets(np->icmpid) & Sflag)){
+					ipriv->stats[AddrmxpErrs6]++; 
+					goto err; 
+				}
+			}
+			break;
+	
+		case RouterAdvert:
+			if(pktsz - sizeof(Ip6hdr) < 16) {
+				ipriv->stats[HlenErrs6]++; 
+				goto err; 
+			}
+			if(!islinklocal(p->src)) {
+				ipriv->stats[RouterAddrErrs6]++; 
+				goto err; 
+			}
+			sz = sizeof(IPICMP) + 8;
+			while ((sz+1) < pktsz) {
+				osz = *(packet+sz+1);
+				if(osz <= 0) {
+					ipriv->stats[OptlenErrs6]++; 
+					goto err; 
+				}	
+				sz += 8*osz;
+			}
+			break;
+	
+		case RouterSolicit:
+			if(pktsz - sizeof(Ip6hdr) < 8) {
+				ipriv->stats[HlenErrs6]++; 
+				goto err; 
+			}
+			unsp = (ipcmp(p->src, v6Unspecified) == 0);
+			sz = sizeof(IPICMP) + 8;
+			while ((sz+1) < pktsz) {
+				osz = *(packet+sz+1);
+				if((osz <= 0) ||
+					(unsp && (*(packet+sz) == slladd)) ) {
+					ipriv->stats[OptlenErrs6]++; 
+					goto err; 
+				}
+				sz += 8*osz;
+			}
+			break;
+	
+		case RedirectV6:
+			//to be filled in
+			break;
+	
+		default:
+			goto err;
+		}
+	}
+
+	return 1;
+
+err:
+	ipriv->stats[InErrors6]++; 
+	return 0;
+}
+
+static int
+targettype(Fs *f, Ipifc *ifc, uchar *target)
+{
+	Iplifc *lifc;
+	int t;
+
+	rlock(ifc);
+	if(ipproxyifc(f, ifc, target)) {
+		runlock(ifc);
+		return t_uniproxy;
+	}
+
+	for(lifc = ifc->lifc; lifc; lifc = lifc->next) {
+		if(ipcmp(lifc->local, target) == 0) {
+			t = (lifc->tentative) ? t_unitent : t_unirany; 
+			runlock(ifc);
+			return t;
+		}
+	}
+
+	runlock(ifc);
+	return 0;
+}
+
+static void
+icmpiput6(Proto *icmp, Ipifc *ipifc, Block *bp)
+{
+	uchar	*packet = bp->rp;
+	IPICMP	*p = (IPICMP *)packet;
+	Icmppriv6 *ipriv = icmp->priv;
+	Block	*r;
+	Proto	*pr;
+	char	*msg, m2[128];
+	Ndpkt* np;
+	uchar pktflags;
+	uchar lsrc[IPaddrlen];
+	int refresh = 1;
+	Iplifc *lifc;
+
+	if(!valid(icmp, ipifc, bp, ipriv)) 
+		goto raise;
+
+	if(p->type <= Maxtype6)
+		ipriv->in[p->type]++;
+	else
+		goto raise;
+
+	switch(p->type) {
+	case EchoRequestV6:
+		r = mkechoreply6(bp);
+		ipriv->out[EchoReply]++;
+		ipoput6(icmp->f, r, 0, MAXTTL, DFLTTOS, nil);
+		break;
+
+	case UnreachableV6:
+		if(p->code > 4)
+			msg = unreachcode[icmp6_unkn_code];
+		else
+			msg = unreachcode[p->code];
+
+		bp->rp += sizeof(IPICMP);
+		if(blocklen(bp) < 8){
+			ipriv->stats[LenErrs6]++;
+			goto raise;
+		}
+		p = (IPICMP *)bp->rp;
+		pr = Fsrcvpcolx(icmp->f, p->proto);
+		if(pr != nil && pr->advise != nil) {
+			(*pr->advise)(pr, bp, msg);
+			return;
+		}
+
+		bp->rp -= sizeof(IPICMP);
+		goticmpkt6(icmp, bp, 0);
+		break;
+
+	case TimeExceedV6:
+		if(p->code == 0){
+			sprint(m2, "ttl exceeded at %I", p->src);
+
+			bp->rp += sizeof(IPICMP);
+			if(blocklen(bp) < 8){
+				ipriv->stats[LenErrs6]++;
+				goto raise;
+			}
+			p = (IPICMP *)bp->rp;
+			pr = Fsrcvpcolx(icmp->f, p->proto);
+			if(pr != nil && pr->advise != nil) {
+				(*pr->advise)(pr, bp, m2);
+				return;
+			}
+			bp->rp -= sizeof(IPICMP);
+		}
+
+		goticmpkt6(icmp, bp, 0);
+		break;
+
+	case RouterAdvert:
+	case RouterSolicit:
+		/* using lsrc as a temp, munge hdr for goticmp6 
+		memmove(lsrc, p->src, IPaddrlen);
+		memmove(p->src, p->dst, IPaddrlen);
+		memmove(p->dst, lsrc, IPaddrlen); */
+
+		goticmpkt6(icmp, bp, p->type);
+		break;
+
+	case NbrSolicit:
+		np = (Ndpkt*) p;
+		pktflags = 0;
+		switch (targettype(icmp->f, ipifc, np->target)) {
+		case t_unirany:
+			pktflags |= Oflag;
+			/* fall through */
+
+		case t_uniproxy: 
+			if(ipcmp(np->src, v6Unspecified) != 0) {
+				arpenter(icmp->f, V6, np->src, np->lnaddr, 8*np->olen-2, 0);
+				pktflags |= Sflag;
+			}
+			if(ipv6local(ipifc, lsrc)) {
+				icmpna(icmp->f, lsrc, 
+				   (ipcmp(np->src, v6Unspecified)==0)?v6allnodesL:np->src,
+				   np->target, ipifc->mac, pktflags); 
+			}
+			else
+				freeblist(bp);
+			break;
+
+		case t_unitent:
+			/* not clear what needs to be done. send up
+			 * an icmp mesg saying don't use this address? */
+
+		default:
+			freeblist(bp);
+		}
+
+		break;
+
+	case NbrAdvert:
+		np = (Ndpkt*) p;
+
+		/* if the target address matches one of the local interface 
+		 * address and the local interface address has tentative bit set, 
+		 * then insert into ARP table. this is so the duplication address 
+		 * detection part of ipconfig can discover duplication through 
+		 * the arp table
+		 */
+		lifc = iplocalonifc(ipifc, np->target);
+		if(lifc && lifc->tentative)
+			refresh = 0;
+		arpenter(icmp->f, V6, np->target, np->lnaddr, 8*np->olen-2, refresh);
+		freeblist(bp);
+		break;
+
+	case PacketTooBigV6:
+
+	default:
+		goticmpkt6(icmp, bp, 0);
+		break;
+	}
+	return;
+
+raise:
+	freeblist(bp);
+
+}
+
+int
+icmpstats6(Proto *icmp6, char *buf, int len)
+{
+	Icmppriv6 *priv;
+	char *p, *e;
+	int i;
+
+	priv = icmp6->priv;
+	p = buf;
+	e = p+len;
+	for(i = 0; i < Nstats6; i++)
+		p = seprint(p, e, "%s: %lud\n", statnames6[i], priv->stats[i]);
+	for(i = 0; i <= Maxtype6; i++){
+		if(icmpnames6[i])
+			p = seprint(p, e, "%s: %lud %lud\n", icmpnames6[i], priv->in[i], priv->out[i]);
+/*		else
+			p = seprint(p, e, "%d: %lud %lud\n", i, priv->in[i], priv->out[i]);
+*/
+	}
+	return p - buf;
+}
+
+
+// need to import from icmp.c
+extern int	icmpstate(Conv *c, char *state, int n);
+extern char*	icmpannounce(Conv *c, char **argv, int argc);
+extern char*	icmpconnect(Conv *c, char **argv, int argc);
+extern void	icmpclose(Conv *c);
+
+void
+icmp6init(Fs *fs)
+{
+	Proto *icmp6 = smalloc(sizeof(Proto));
+
+	icmp6->priv = smalloc(sizeof(Icmppriv6));
+	icmp6->name = "icmpv6";
+	icmp6->connect = icmpconnect;
+	icmp6->announce = icmpannounce;
+	icmp6->state = icmpstate;
+	icmp6->create = icmpcreate6;
+	icmp6->close = icmpclose;
+	icmp6->rcv = icmpiput6;
+	icmp6->stats = icmpstats6;
+	icmp6->ctl = icmpctl6;
+	icmp6->advise = icmpadvise6;
+	icmp6->gc = nil;
+	icmp6->ipproto = ICMPv6;
+	icmp6->nc = 16;
+	icmp6->ptclsize = sizeof(Icmpcb6);
+
+	Fsproto(fs, icmp6);
+}
+
--- /dev/null
+++ b/os/ip/igmp.c
@@ -1,0 +1,291 @@
+#include "u.h"
+#include "../port/lib.h"
+#include "mem.h"
+#include "dat.h"
+#include "fns.h"
+#include "../port/error.h"
+
+#include "ip.h"
+
+enum
+{
+	IGMP_IPHDRSIZE	= 20,		/* size of ip header */
+	IGMP_HDRSIZE	= 8,		/* size of IGMP header */
+	IP_IGMPPROTO	= 2,
+
+	IGMPquery	= 1,
+	IGMPreport	= 2,
+
+	MSPTICK		= 100,
+	MAXTIMEOUT	= 10000/MSPTICK,	/* at most 10 secs for a response */
+};
+
+typedef struct IGMPpkt IGMPpkt;
+struct IGMPpkt
+{
+	/* ip header */
+	uchar	vihl;		/* Version and header length */
+	uchar	tos;		/* Type of service */
+	uchar	len[2];		/* packet length (including headers) */
+	uchar	id[2];		/* Identification */
+	uchar	frag[2];	/* Fragment information */
+	uchar	Unused;	
+	uchar	proto;		/* Protocol */
+	uchar	cksum[2];	/* checksum of ip portion */
+	uchar	src[IPaddrlen];		/* Ip source */
+	uchar	dst[IPaddrlen];		/* Ip destination */
+
+	/* igmp header */
+	uchar	vertype;	/* version and type */
+	uchar	unused;
+	uchar	igmpcksum[2];		/* checksum of igmp portion */
+	uchar	group[IPaddrlen];	/* multicast group */
+};
+
+/*
+ *  lists for group reports
+ */
+typedef struct IGMPrep IGMPrep;
+struct IGMPrep
+{
+	IGMPrep		*next;
+	Media		*m;
+	int		ticks;
+	Multicast	*multi;
+};
+
+typedef struct IGMP IGMP;
+struct IGMP
+{
+	Lock;
+	Rendez	r;
+	IGMPrep	*reports;
+};
+
+IGMP igmpalloc;
+
+	Proto	igmp;
+extern	Fs	fs;
+
+static struct Stats
+{
+	ulong 	inqueries;
+	ulong	outqueries;
+	ulong	inreports;
+	ulong	outreports;
+} stats;
+
+void
+igmpsendreport(Media *m, uchar *addr)
+{
+	IGMPpkt *p;
+	Block *bp;
+
+	bp = allocb(sizeof(IGMPpkt));
+	if(bp == nil)
+		return;
+	p = (IGMPpkt*)bp->wp;
+	p->vihl = IP_VER4;
+	bp->wp += sizeof(IGMPpkt);
+	memset(bp->rp, 0, sizeof(IGMPpkt));
+	hnputl(p->src, Mediagetaddr(m));
+	hnputl(p->dst, Ipallsys);
+	p->vertype = (1<<4) | IGMPreport;
+	p->proto = IP_IGMPPROTO;
+	memmove(p->group, addr, IPaddrlen);
+	hnputs(p->igmpcksum, ptclcsum(bp, IGMP_IPHDRSIZE, IGMP_HDRSIZE));
+	netlog(Logigmp, "igmpreport %I\n", p->group);
+	stats.outreports++;
+	ipoput4(bp, 0, 1, DFLTTOS, nil);	/* TTL of 1 */
+}
+
+static int
+isreport(void *a)
+{
+	USED(a);
+	return igmpalloc.reports != 0;
+}
+
+
+void
+igmpproc(void *a)
+{
+	IGMPrep *rp, **lrp;
+	Multicast *mp, **lmp;
+	uchar ip[IPaddrlen];
+
+	USED(a);
+
+	for(;;){
+		sleep(&igmpalloc.r, isreport, 0);
+		for(;;){
+			lock(&igmpalloc);
+
+			if(igmpalloc.reports == nil)
+				break;
+	
+			/* look for a single report */
+			lrp = &igmpalloc.reports;
+			mp = nil;
+			for(rp = *lrp; rp; rp = *lrp){
+				rp->ticks++;
+				lmp = &rp->multi;
+				for(mp = *lmp; mp; mp = *lmp){
+					if(rp->ticks >= mp->timeout){
+						*lmp = mp->next;
+						break;
+					}
+					lmp = &mp->next;
+				}
+				if(mp != nil)
+					break;
+
+				if(rp->multi != nil){
+					lrp = &rp->next;
+					continue;
+				} else {
+					*lrp = rp->next;
+					free(rp);
+				}
+			}
+			unlock(&igmpalloc);
+
+			if(mp){
+				/* do a single report and try again */
+				hnputl(ip, mp->addr);
+				igmpsendreport(rp->m, ip);
+				free(mp);
+				continue;
+			}
+
+			tsleep(&up->sleep, return0, 0, MSPTICK);
+		}
+		unlock(&igmpalloc);
+	}
+
+}
+
+void
+igmpiput(Media *m, Ipifc *, Block *bp)
+{
+	int n;
+	IGMPpkt *ghp;
+	Ipaddr group;
+	IGMPrep *rp, **lrp;
+	Multicast *mp, **lmp;
+
+	ghp = (IGMPpkt*)(bp->rp);
+	netlog(Logigmp, "igmpiput: %d %I\n", ghp->vertype, ghp->group);
+
+	n = blocklen(bp);
+	if(n < IGMP_IPHDRSIZE+IGMP_HDRSIZE){
+		netlog(Logigmp, "igmpiput: bad len\n");
+		goto error;
+	}
+	if((ghp->vertype>>4) != 1){
+		netlog(Logigmp, "igmpiput: bad igmp type\n");
+		goto error;
+	}
+	if(ptclcsum(bp, IGMP_IPHDRSIZE, IGMP_HDRSIZE)){
+		netlog(Logigmp, "igmpiput: checksum error %I\n", ghp->src);
+		goto error;
+	}
+
+	group = nhgetl(ghp->group);
+	
+	lock(&igmpalloc);
+	switch(ghp->vertype & 0xf){
+	case IGMPquery:
+		/*
+		 *  start reporting groups that we're a member of.
+		 */
+		stats.inqueries++;
+		for(rp = igmpalloc.reports; rp; rp = rp->next)
+			if(rp->m == m)
+				break;
+		if(rp != nil)
+			break;	/* already reporting */
+
+		mp = Mediacopymulti(m);
+		if(mp == nil)
+			break;
+
+		rp = malloc(sizeof(*rp));
+		if(rp == nil)
+			break;
+
+		rp->m = m;
+		rp->multi = mp;
+		rp->ticks = 0;
+		for(; mp; mp = mp->next)
+			mp->timeout = nrand(MAXTIMEOUT);
+		rp->next = igmpalloc.reports;
+		igmpalloc.reports = rp;
+
+		wakeup(&igmpalloc.r);
+
+		break;
+	case IGMPreport:
+		/*
+		 *  find report list for this medium
+		 */
+		stats.inreports++;
+		lrp = &igmpalloc.reports;
+		for(rp = *lrp; rp; rp = *lrp){
+			if(rp->m == m)
+				break;
+			lrp = &rp->next;
+		}
+		if(rp == nil)
+			break;
+
+		/*
+		 *  if someone else has reported a group,
+		 *  we don't have to.
+		 */
+		lmp = &rp->multi;
+		for(mp = *lmp; mp; mp = *lmp){
+			if(mp->addr == group){
+				*lmp = mp->next;
+				free(mp);
+				break;
+			}
+			lmp = &mp->next;
+		}
+
+		break;
+	}
+	unlock(&igmpalloc);
+
+error:
+	freeb(bp);
+}
+
+int
+igmpstats(char *buf, int len)
+{
+	return snprint(buf, len, "\trcvd %d %d\n\tsent %d %d\n",
+		stats.inqueries, stats.inreports,
+		stats.outqueries, stats.outreports);
+}
+
+void
+igmpinit(Fs *fs)
+{
+	igmp.name = "igmp";
+	igmp.connect = nil;
+	igmp.announce = nil;
+	igmp.ctl = nil;
+	igmp.state = nil;
+	igmp.close = nil;
+	igmp.rcv = igmpiput;
+	igmp.stats = igmpstats;
+	igmp.ipproto = IP_IGMPPROTO;
+	igmp.nc = 0;
+	igmp.ptclsize = 0;
+
+	igmpreportfn = igmpsendreport;
+	kproc("igmpproc", igmpproc, 0, 0);
+
+	Fsproto(fs, &igmp);
+}
--- /dev/null
+++ b/os/ip/ihbootp.c
@@ -1,0 +1,323 @@
+#include "u.h"
+#include "../port/lib.h"
+#include "mem.h"
+#include "dat.h"
+#include "fns.h"
+#include "../port/error.h"
+#include "kernel.h"
+#include "ip.h"
+
+static	ulong	fsip;
+static	ulong	auip;
+static	ulong	gwip;
+static	ulong	ipmask;
+static	ulong	ipaddr;
+static	ulong	dnsip;
+
+enum
+{
+	Bootrequest = 1,
+	Bootreply   = 2,
+};
+
+typedef struct Bootp
+{
+	/* udp.c oldheader */
+	uchar	raddr[IPaddrlen];
+	uchar	laddr[IPaddrlen];
+	uchar	rport[2];
+	uchar	lport[2];
+	/* bootp itself */
+	uchar	op;		/* opcode */
+	uchar	htype;		/* hardware type */
+	uchar	hlen;		/* hardware address len */
+	uchar	hops;		/* hops */
+	uchar	xid[4];		/* a random number */
+	uchar	secs[2];	/* elapsed snce client started booting */
+	uchar	pad[2];
+	uchar	ciaddr[4];	/* client IP address (client tells server) */
+	uchar	yiaddr[4];	/* client IP address (server tells client) */
+	uchar	siaddr[4];	/* server IP address */
+	uchar	giaddr[4];	/* gateway IP address */
+	uchar	chaddr[16];	/* client hardware address */
+	uchar	sname[64];	/* server host name (optional) */
+	uchar	file[128];	/* boot file name */
+	uchar	vend[128];	/* vendor-specific goo */
+} Bootp;
+
+/*
+ * bootp returns:
+ *
+ * "fsip d.d.d.d
+ * auip d.d.d.d
+ * gwip d.d.d.d
+ * ipmask d.d.d.d
+ * ipaddr d.d.d.d
+ * dnsip d.d.d.d"
+ *
+ * where d.d.d.d is the IP address in dotted decimal notation, and each
+ * address is followed by a newline.
+ */
+
+static	Bootp	req;
+static	Proc*	rcvprocp;
+static	int	recv;
+static	int	done;
+static	Rendez	bootpr;
+static	char	rcvbuf[512];
+static	int	bootpdebug;
+
+/*
+ * Parse the vendor specific fields according to RFC 1084.
+ * We are overloading the "cookie server" to be the Inferno 
+ * authentication server and the "resource location server"
+ * to be the Inferno file server.
+ *
+ * If the vendor specific field is formatted properly, it
+ * will begin with the four bytes 99.130.83.99 and end with
+ * an 0xFF byte.
+ */
+static void
+parsevend(uchar* vend)
+{
+	/* The field must start with 99.130.83.99 to be compliant */
+	if ((vend[0] != 99) || (vend[1] != 130) ||
+	    (vend[2] != 83) || (vend[3] != 99)){
+		if(bootpdebug)
+			print("bad bootp vendor field: %.2x%.2x%.2x%.2x", vend[0], vend[1], vend[2], vend[3]);
+		return;
+	}
+
+	/* Skip over the magic cookie */
+	vend += 4;
+
+	while ((vend[0] != 0) && (vend[0] != 0xFF)) {
+		if(bootpdebug){
+			int i;
+			print("vend %d [%d]", vend[0], vend[1]);
+			for(i=0; i<vend[1]; i++)
+				print(" %2.2x", vend[i]);
+			print("\n");
+		}
+		switch (vend[0]) {
+		case 1:	/* Subnet mask field */
+			/* There must be only one subnet mask */
+			if (vend[1] != 4)
+				return;
+
+			ipmask = (vend[2]<<24)|
+				 (vend[3]<<16)|
+				 (vend[4]<<8)|
+				  vend[5];
+			break;
+
+		case 3:	/* Gateway/router field */
+			/* We are only concerned with first address */
+			if (vend[1] < 4)
+				break;
+
+			gwip =	(vend[2]<<24)|
+				(vend[3]<<16)|
+				(vend[4]<<8)|
+				 vend[5];
+			break;
+
+		case 6:	/* DNS server */
+			/* We are only concerned with first address */
+			if (vend[1] < 4)
+				break;
+
+			dnsip =	(vend[2]<<24)|
+				(vend[3]<<16)|
+				(vend[4]<<8)|
+				 vend[5];
+			break;
+
+		case 8:	/* "Cookie server" (auth server) field */
+			/* We are only concerned with first address */
+			if (vend[1] < 4)
+				break;
+
+			auip =	(vend[2]<<24)|
+				(vend[3]<<16)|
+				(vend[4]<<8)|
+				 vend[5];
+			break;
+
+		case 11:	/* "Resource loc server" (file server) field */
+			/* We are only concerned with first address */
+			if (vend[1] < 4)
+				break;
+
+			fsip =	(vend[2]<<24)|
+				(vend[3]<<16)|
+				(vend[4]<<8)|
+				 vend[5];
+			break;
+
+		default:	/* Ignore everything else */
+			break;
+		}
+
+		/* Skip over the field */
+		vend += vend[1] + 2;
+	}
+}
+
+static void
+rcvbootp(void *a)
+{
+	int n, fd;
+	Bootp *rp;
+
+	if(waserror())
+		pexit("", 0);
+	rcvprocp = up;	/* store for postnote below */
+	fd = (int)a;
+	while(done == 0) {
+		n = kread(fd, rcvbuf, sizeof(rcvbuf));
+		if(n <= 0)
+			break;
+		rp = (Bootp*)rcvbuf;
+		if (memcmp(req.chaddr, rp->chaddr, 6) == 0 &&
+		   rp->htype == 1 && rp->hlen == 6) {
+			ipaddr = (rp->yiaddr[0]<<24)|
+				 (rp->yiaddr[1]<<16)|
+				 (rp->yiaddr[2]<<8)|
+				  rp->yiaddr[3];
+			parsevend(rp->vend);
+			break;
+		}
+	}
+	poperror();
+	rcvprocp = nil;
+
+	recv = 1;
+	wakeup(&bootpr);
+	pexit("", 0);
+}
+
+static char*
+rbootp(Ipifc *ifc)
+{
+	int cfd, dfd, tries, n;
+	char ia[5+3*16], im[16], *av[3];
+	uchar nipaddr[4], ngwip[4], nipmask[4];
+	char dir[Maxpath];
+	static uchar vend_rfc1048[] = { 99, 130, 83, 99 };
+
+	av[1] = "0.0.0.0";
+	av[2] = "0.0.0.0";
+	ipifcadd(ifc, av, 3, 0, nil);
+
+	cfd = kannounce("udp!*!68", dir);
+	if(cfd < 0)
+		return "bootp announce failed";
+	strcat(dir, "/data");
+	if(kwrite(cfd, "headers", 7) < 0){
+		kclose(cfd);
+		return "bootp ctl headers failed";
+	}
+	kwrite(cfd, "oldheaders", 10);
+	dfd = kopen(dir, ORDWR);
+	if(dfd < 0){
+		kclose(cfd);
+		return "bootp open data failed";
+	}
+	kclose(cfd);
+
+	/* create request */
+	memset(&req, 0, sizeof(req));
+	ipmove(req.raddr, IPv4bcast);
+	hnputs(req.rport, 67);
+	req.op = Bootrequest;
+	req.htype = 1;			/* ethernet (all we know) */
+	req.hlen = 6;			/* ethernet (all we know) */
+
+	/* Hardware MAC address */
+	memmove(req.chaddr, ifc->mac, 6);
+	/* Fill in the local IP address if we know it */
+	ipv4local(ifc, req.ciaddr);
+	memset(req.file, 0, sizeof(req.file));
+	memmove(req.vend, vend_rfc1048, 4);
+
+	done = 0;
+	recv = 0;
+
+	kproc("rcvbootp", rcvbootp, (void*)dfd, KPDUPFDG);
+
+	/*
+	 * broadcast bootp's till we get a reply,
+	 * or fixed number of tries
+	 */
+	tries = 0;
+	while(recv == 0) {
+		if(kwrite(dfd, &req, sizeof(req)) < 0)
+			print("bootp: write: %r");
+
+		tsleep(&bootpr, return0, 0, 1000);
+		if(++tries > 10) {
+			print("bootp: timed out\n");
+			break;
+		}
+	}
+	kclose(dfd);
+	done = 1;
+	if(rcvprocp != nil){
+		postnote(rcvprocp, 1, "timeout", 0);
+		rcvprocp = nil;
+	}
+
+	av[1] = "0.0.0.0";
+	av[2] = "0.0.0.0";
+	ipifcrem(ifc, av, 3);
+
+	hnputl(nipaddr, ipaddr);
+	sprint(ia, "%V", nipaddr);
+	hnputl(nipmask, ipmask);
+	sprint(im, "%V", nipmask);
+	av[1] = ia;
+	av[2] = im;
+	ipifcadd(ifc, av, 3, 0, nil);
+
+	if(gwip != 0) {
+		hnputl(ngwip, gwip);
+		n = sprint(ia, "add 0.0.0.0 0.0.0.0 %V", ngwip);
+		routewrite(ifc->conv->p->f, nil, ia, n);
+	}
+	return nil;
+}
+
+static int
+rbootpread(char *bp, ulong offset, int len)
+{
+	int n;
+	char *buf;
+	uchar a[4];
+
+	buf = smalloc(READSTR);
+	if(waserror()){
+		free(buf);
+		nexterror();
+	}
+	hnputl(a, fsip);
+	n = snprint(buf, READSTR, "fsip %15V\n", a);
+	hnputl(a, auip);
+	n += snprint(buf + n, READSTR-n, "auip %15V\n", a);
+	hnputl(a, gwip);
+	n += snprint(buf + n, READSTR-n, "gwip %15V\n", a);
+	hnputl(a, ipmask);
+	n += snprint(buf + n, READSTR-n, "ipmask %15V\n", a);
+	hnputl(a, ipaddr);
+	n += snprint(buf + n, READSTR-n, "ipaddr %15V\n", a);
+	hnputl(a, dnsip);
+	snprint(buf + n, READSTR-n, "dnsip %15V\n", a);
+
+	len = readstr(offset, bp, len, buf);
+	poperror();
+	free(buf);
+	return len;
+}
+
+char*	(*bootp)(Ipifc*) = rbootp;
+int	(*bootpread)(char*, ulong, int) = rbootpread;
--- /dev/null
+++ b/os/ip/il.c
@@ -1,0 +1,1408 @@
+#include	"u.h"
+#include	"../port/lib.h"
+#include	"mem.h"
+#include	"dat.h"
+#include	"fns.h"
+#include	"../port/error.h"
+
+#include	"ip.h"
+
+enum				/* Connection state */
+{
+	Ilclosed,
+	Ilsyncer,
+	Ilsyncee,
+	Ilestablished,
+	Illistening,
+	Ilclosing,
+	Ilopening,		/* only for file server */
+};
+
+char	*ilstates[] = 
+{ 
+	"Closed",
+	"Syncer",
+	"Syncee",
+	"Established",
+	"Listen",
+	"Closing",
+	"Opening",		/* only for file server */
+};
+
+enum				/* Packet types */
+{
+	Ilsync,
+	Ildata,
+	Ildataquery,
+	Ilack,
+	Ilquery,
+	Ilstate,
+	Ilclose,
+};
+
+char	*iltype[] = 
+{	
+	"sync",
+	"data",
+	"dataquery",
+	"ack",
+	"query",
+	"state",
+	"close" 
+};
+
+enum
+{
+	Seconds		= 1000,
+	Iltickms 	= 50,		/* time base */
+	AckDelay	= 2*Iltickms,	/* max time twixt message rcvd & ack sent */
+	MaxTimeout 	= 30*Seconds,	/* max time between rexmit */
+	QueryTime	= 10*Seconds,	/* time between subsequent queries */
+	DeathTime	= 30*QueryTime,
+
+	MaxRexmit 	= 16,		/* max retransmissions before hangup */
+	Defaultwin	= 20,
+
+	LogAGain	= 3,
+	AGain		= 1<<LogAGain,
+	LogDGain	= 2,
+	DGain		= 1<<LogDGain,
+
+	DefByteRate	= 100,		/* assume a megabit link */
+	DefRtt		= 50,		/* cross country on a great day */
+
+	Maxrq		= 64*1024,
+};
+
+enum
+{
+	Nqt=	8,
+};
+
+typedef struct Ilcb Ilcb;
+struct Ilcb			/* Control block */
+{
+	int	state;		/* Connection state */
+	Conv	*conv;
+	QLock	ackq;		/* Unacknowledged queue */
+	Block	*unacked;
+	Block	*unackedtail;
+	ulong	unackedbytes;
+	QLock	outo;		/* Out of order packet queue */
+	Block	*outoforder;
+	ulong	next;		/* Id of next to send */
+	ulong	recvd;		/* Last packet received */
+	ulong	acksent;	/* Last packet acked */
+	ulong	start;		/* Local start id */
+	ulong	rstart;		/* Remote start id */
+	int	window;		/* Maximum receive window */
+	int	rxquery;	/* number of queries on this connection */
+	int	rxtot;		/* number of retransmits on this connection */
+	int	rexmit;		/* number of retransmits of *unacked */
+	ulong	qt[Nqt+1];	/* state table for query messages */
+	int	qtx;		/* ... index into qt */
+
+	/* if set, fasttimeout causes a connection request to terminate after 4*Iltickms */
+	int	fasttimeout;
+
+	/* timers */
+	ulong	lastxmit;	/* time of last xmit */
+	ulong	lastrecv;	/* time of last recv */
+	ulong	timeout;	/* retransmission time for *unacked */
+	ulong	acktime;	/* time to send next ack */
+	ulong	querytime;	/* time to send next query */
+
+	/* adaptive measurements */
+	int	delay;		/* Average of the fixed rtt delay */
+	int	rate;		/* Average uchar rate */
+	int	mdev;		/* Mean deviation of rtt */
+	int	maxrtt;		/* largest rtt seen */
+	ulong	rttack;		/* The ack we are waiting for */
+	int	rttlen;		/* Length of rttack packet */
+	uvlong	rttstart;	/* Time we issued rttack packet */
+};
+
+enum
+{
+	IL_IPSIZE 	= 20,
+	IL_HDRSIZE	= 18,	
+	IL_LISTEN	= 0,
+	IL_CONNECT	= 1,
+	IP_ILPROTO	= 40,
+};
+
+typedef struct Ilhdr Ilhdr;
+struct Ilhdr
+{
+	uchar	vihl;		/* Version and header length */
+	uchar	tos;		/* Type of service */
+	uchar	length[2];	/* packet length */
+	uchar	id[2];		/* Identification */
+	uchar	frag[2];	/* Fragment information */
+	uchar	ttl;		/* Time to live */
+	uchar	proto;		/* Protocol */
+	uchar	cksum[2];	/* Header checksum */
+	uchar	src[4];		/* Ip source */
+	uchar	dst[4];		/* Ip destination */
+	uchar	ilsum[2];	/* Checksum including header */
+	uchar	illen[2];	/* Packet length */
+	uchar	iltype;		/* Packet type */
+	uchar	ilspec;		/* Special */
+	uchar	ilsrc[2];	/* Src port */
+	uchar	ildst[2];	/* Dst port */
+	uchar	ilid[4];	/* Sequence id */
+	uchar	ilack[4];	/* Acked sequence */
+};
+
+enum
+{
+	InMsgs,
+	OutMsgs,
+	CsumErrs,		/* checksum errors */
+	HlenErrs,		/* header length error */
+	LenErrs,		/* short packet */
+	OutOfOrder,		/* out of order */
+	Retrans,		/* retransmissions */
+	DupMsg,
+	DupBytes,
+	DroppedMsgs,
+
+	Nstats,
+};
+
+static char *statnames[] =
+{
+[InMsgs]	"InMsgs",
+[OutMsgs]	"OutMsgs",
+[CsumErrs]	"CsumErrs",
+[HlenErrs]	"HlenErr",
+[LenErrs]	"LenErrs",
+[OutOfOrder]	"OutOfOrder",
+[Retrans]	"Retrans",
+[DupMsg]	"DupMsg",
+[DupBytes]	"DupBytes",
+[DroppedMsgs]	"DroppedMsgs",
+};
+
+typedef struct Ilpriv Ilpriv;
+struct Ilpriv
+{
+	Ipht	ht;
+
+	ulong	stats[Nstats];
+
+	ulong	csumerr;		/* checksum errors */
+	ulong	hlenerr;		/* header length error */
+	ulong	lenerr;			/* short packet */
+	ulong	order;			/* out of order */
+	ulong	rexmit;			/* retransmissions */
+	ulong	dup;
+	ulong	dupb;
+
+	/* keeping track of the ack kproc */
+	int	ackprocstarted;
+	QLock	apl;
+};
+
+/* state for query/dataquery messages */
+
+
+void	ilrcvmsg(Conv*, Block*);
+void	ilsendctl(Conv*, Ilhdr*, int, ulong, ulong, int);
+void	ilackq(Ilcb*, Block*);
+void	ilprocess(Conv*, Ilhdr*, Block*);
+void	ilpullup(Conv*);
+void	ilhangup(Conv*, char*);
+void	ilfreeq(Ilcb*);
+void	ilrexmit(Ilcb*);
+void	ilbackoff(Ilcb*);
+void	ilsettimeout(Ilcb*);
+char*	ilstart(Conv*, int, int);
+void	ilackproc(void*);
+void	iloutoforder(Conv*, Ilhdr*, Block*);
+void	iliput(Proto*, Ipifc*, Block*);
+void	iladvise(Proto*, Block*, char*);
+int	ilnextqt(Ilcb*);
+void	ilcbinit(Ilcb*);
+int	later(ulong, ulong, char*);
+void	ilreject(Fs*, Ilhdr*);
+void	illocalclose(Conv *c);
+	int 	ilcksum = 1;
+static 	int 	initseq = 25001;
+static	ulong	scalediv, scalemul;
+static	char	*etime = "connection timed out";
+
+static char*
+ilconnect(Conv *c, char **argv, int argc)
+{
+	char *e, *p;
+	int fast;
+
+	/* huge hack to quickly try an il connection */
+	fast = 0;
+	if(argc > 1){
+		p = strstr(argv[1], "!fasttimeout");
+		if(p != nil){
+			*p = 0;
+			fast = 1;
+		}
+	}
+
+	e = Fsstdconnect(c, argv, argc);
+	if(e != nil)
+		return e;
+	return ilstart(c, IL_CONNECT, fast);
+}
+
+static int
+ilstate(Conv *c, char *state, int n)
+{
+	Ilcb *ic;
+
+	ic = (Ilcb*)(c->ptcl);
+	return snprint(state, n, "%s qin %d qout %d del %5.5d Br %5.5d md %5.5d una %5.5lud rex %5.5d rxq %5.5d max %5.5d",
+		ilstates[ic->state],
+		c->rq ? qlen(c->rq) : 0,
+		c->wq ? qlen(c->wq) : 0,
+		ic->delay>>LogAGain, ic->rate>>LogAGain, ic->mdev>>LogDGain,
+		ic->unackedbytes, ic->rxtot, ic->rxquery, ic->maxrtt);
+}
+
+static int
+ilinuse(Conv *c)
+{
+	Ilcb *ic;
+
+	ic = (Ilcb*)(c->ptcl);
+	return ic->state != Ilclosed;
+
+}
+
+/* called with c locked */
+static char*
+ilannounce(Conv *c, char **argv, int argc)
+{
+	char *e;
+
+	e = Fsstdannounce(c, argv, argc);
+	if(e != nil)
+		return e;
+	e = ilstart(c, IL_LISTEN, 0);
+	if(e != nil)
+		return e;
+	Fsconnected(c, nil);
+
+	return nil;
+}
+
+void
+illocalclose(Conv *c)
+{
+	Ilcb *ic;
+	Ilpriv *ipriv;
+
+	ipriv = c->p->priv;
+	ic = (Ilcb*)c->ptcl;
+	ic->state = Ilclosed;
+	iphtrem(&ipriv->ht, c);
+	ipmove(c->laddr, IPnoaddr);
+	c->lport = 0;
+}
+
+static void
+ilclose(Conv *c)
+{
+	Ilcb *ic;
+
+	ic = (Ilcb*)c->ptcl;
+
+	qclose(c->rq);
+	qclose(c->wq);
+	qclose(c->eq);
+
+	switch(ic->state) {
+	case Ilclosing:
+	case Ilclosed:
+		break;
+	case Ilsyncer:
+	case Ilsyncee:
+	case Ilestablished:
+		ic->state = Ilclosing;
+		ilsettimeout(ic);
+		ilsendctl(c, nil, Ilclose, ic->next, ic->recvd, 0);
+		break;
+	case Illistening:
+		illocalclose(c);
+		break;
+	}
+	ilfreeq(ic);
+}
+
+void
+ilkick(void *x, Block *bp)
+{
+	Conv *c = x;
+	Ilhdr *ih;
+	Ilcb *ic;
+	int dlen;
+	ulong id, ack;
+	Fs *f;
+	Ilpriv *priv;
+
+	f = c->p->f;
+	priv = c->p->priv;
+	ic = (Ilcb*)c->ptcl;
+
+	if(bp == nil)
+		return;
+
+	switch(ic->state) {
+	case Ilclosed:
+	case Illistening:
+	case Ilclosing:
+		freeblist(bp);
+		qhangup(c->rq, nil);
+		return;
+	}
+
+	dlen = blocklen(bp);
+
+	/* Make space to fit il & ip */
+	bp = padblock(bp, IL_IPSIZE+IL_HDRSIZE);
+	ih = (Ilhdr *)(bp->rp);
+	ih->vihl = IP_VER4;
+
+	/* Ip fields */
+	ih->frag[0] = 0;
+	ih->frag[1] = 0;
+	v6tov4(ih->dst, c->raddr);
+	v6tov4(ih->src, c->laddr);
+	ih->proto = IP_ILPROTO;
+
+	/* Il fields */
+	hnputs(ih->illen, dlen+IL_HDRSIZE);
+	hnputs(ih->ilsrc, c->lport);
+	hnputs(ih->ildst, c->rport);
+
+	qlock(&ic->ackq);
+	id = ic->next++;
+	hnputl(ih->ilid, id);
+	ack = ic->recvd;
+	hnputl(ih->ilack, ack);
+	ic->acksent = ack;
+	ic->acktime = NOW + AckDelay;
+	ih->iltype = Ildata;
+	ih->ilspec = 0;
+	ih->ilsum[0] = 0;
+	ih->ilsum[1] = 0;
+
+	/* Checksum of ilheader plus data (not ip & no pseudo header) */
+	if(ilcksum)
+		hnputs(ih->ilsum, ptclcsum(bp, IL_IPSIZE, dlen+IL_HDRSIZE));
+
+	ilackq(ic, bp);
+	qunlock(&ic->ackq);
+
+	/* Start the round trip timer for this packet if the timer is free */
+	if(ic->rttack == 0) {
+		ic->rttack = id;
+		ic->rttstart = fastticks(nil);
+		ic->rttlen = dlen + IL_IPSIZE + IL_HDRSIZE;
+	}
+
+	if(later(NOW, ic->timeout, nil))
+		ilsettimeout(ic);
+	ipoput4(f, bp, 0, c->ttl, c->tos, c);
+	priv->stats[OutMsgs]++;
+}
+
+static void
+ilcreate(Conv *c)
+{
+	c->rq = qopen(Maxrq, 0, 0, c);
+	c->wq = qbypass(ilkick, c);
+}
+
+int
+ilxstats(Proto *il, char *buf, int len)
+{
+	Ilpriv *priv;
+	char *p, *e;
+	int i;
+
+	priv = il->priv;
+	p = buf;
+	e = p+len;
+	for(i = 0; i < Nstats; i++)
+		p = seprint(p, e, "%s: %lud\n", statnames[i], priv->stats[i]);
+	return p - buf;
+}
+
+void
+ilackq(Ilcb *ic, Block *bp)
+{
+	Block *np;
+	int n;
+
+	n = blocklen(bp);
+
+	/* Enqueue a copy on the unacked queue in case this one gets lost */
+	np = copyblock(bp, n);
+	if(ic->unacked)
+		ic->unackedtail->list = np;
+	else
+		ic->unacked = np;
+	ic->unackedtail = np;
+	np->list = nil;
+	ic->unackedbytes += n;
+}
+
+static
+void
+ilrttcalc(Ilcb *ic, Block *bp)
+{
+	int rtt, tt, pt, delay, rate;
+
+	rtt = fastticks(nil) - ic->rttstart;
+	rtt = (rtt*scalemul)/scalediv;
+	delay = ic->delay;
+	rate = ic->rate;
+
+	/* Guard against zero wrap */
+	if(rtt > 120000 || rtt < 0)
+		return;
+
+	/* this block had to be transmitted after the one acked so count its size */
+	ic->rttlen += blocklen(bp)  + IL_IPSIZE + IL_HDRSIZE;
+
+	if(ic->rttlen < 256){
+		/* guess fixed delay as rtt of small packets */
+		delay += rtt - (delay>>LogAGain);
+		if(delay < AGain)
+			delay = AGain;
+		ic->delay = delay;
+	} else {
+		/* if packet took longer than avg rtt delay, recalc rate */
+		tt = rtt - (delay>>LogAGain);
+		if(tt > 0){
+			rate += ic->rttlen/tt - (rate>>LogAGain);
+			if(rate < AGain)
+				rate = AGain;
+			ic->rate = rate;
+		}
+	}
+
+	/* mdev */
+	pt = ic->rttlen/(rate>>LogAGain) + (delay>>LogAGain);
+	ic->mdev += abs(rtt-pt) - (ic->mdev>>LogDGain);
+
+	if(rtt > ic->maxrtt)
+		ic->maxrtt = rtt;
+}
+
+void
+ilackto(Ilcb *ic, ulong ackto, Block *bp)
+{
+	Ilhdr *h;
+	ulong id;
+
+	if(ic->rttack == ackto)
+		ilrttcalc(ic, bp);
+
+	/* Cancel if we've passed the packet we were interested in */
+	if(ic->rttack <= ackto)
+		ic->rttack = 0;
+
+	qlock(&ic->ackq);
+	while(ic->unacked) {
+		h = (Ilhdr *)ic->unacked->rp;
+		id = nhgetl(h->ilid);
+		if(ackto < id)
+			break;
+
+		bp = ic->unacked;
+		ic->unacked = bp->list;
+		bp->list = nil;
+		ic->unackedbytes -= blocklen(bp);
+		freeblist(bp);
+		ic->rexmit = 0;
+		ilsettimeout(ic);
+	}
+	qunlock(&ic->ackq);
+}
+
+void
+iliput(Proto *il, Ipifc*, Block *bp)
+{
+	char *st;
+	Ilcb *ic;
+	Ilhdr *ih;
+	uchar raddr[IPaddrlen];
+	uchar laddr[IPaddrlen];
+	ushort sp, dp, csum;
+	int plen, illen;
+	Conv *new, *s;
+	Ilpriv *ipriv;
+
+	ipriv = il->priv;
+
+	ih = (Ilhdr *)bp->rp;
+	plen = blocklen(bp);
+	if(plen < IL_IPSIZE+IL_HDRSIZE){
+		netlog(il->f, Logil, "il: hlenerr\n");
+		ipriv->stats[HlenErrs]++;
+		goto raise;
+	}
+
+	illen = nhgets(ih->illen);
+	if(illen+IL_IPSIZE > plen){
+		netlog(il->f, Logil, "il: lenerr\n");
+		ipriv->stats[LenErrs]++;
+		goto raise;
+	}
+
+	sp = nhgets(ih->ildst);
+	dp = nhgets(ih->ilsrc);
+	v4tov6(raddr, ih->src);
+	v4tov6(laddr, ih->dst);
+
+	if((csum = ptclcsum(bp, IL_IPSIZE, illen)) != 0) {
+		if(ih->iltype > Ilclose)
+			st = "?";
+		else
+			st = iltype[ih->iltype];
+		ipriv->stats[CsumErrs]++;
+		netlog(il->f, Logil, "il: cksum %ux %ux, pkt(%s id %lud ack %lud %I/%d->%d)\n",
+			csum, st, nhgetl(ih->ilid), nhgetl(ih->ilack), raddr, sp, dp);
+		goto raise;
+	}
+
+	qlock(il);
+	s = iphtlook(&ipriv->ht, raddr, dp, laddr, sp);
+	if(s == nil){
+		if(ih->iltype == Ilsync)
+			ilreject(il->f, ih);		/* no listener */
+		qunlock(il);
+		goto raise;
+	}
+
+	ic = (Ilcb*)s->ptcl;
+	if(ic->state == Illistening){
+		if(ih->iltype != Ilsync){
+			qunlock(il);
+			if(ih->iltype > Ilclose)
+				st = "?";
+			else
+				st = iltype[ih->iltype];
+			ilreject(il->f, ih);		/* no channel and not sync */
+			netlog(il->f, Logil, "il: no channel, pkt(%s id %lud ack %lud %I/%ud->%ud)\n",
+				st, nhgetl(ih->ilid), nhgetl(ih->ilack), raddr, sp, dp); 
+			goto raise;
+		}
+
+		new = Fsnewcall(s, raddr, dp, laddr, sp, V4);
+		if(new == nil){
+			qunlock(il);
+			netlog(il->f, Logil, "il: bad newcall %I/%ud->%ud\n", raddr, sp, dp);
+			ilsendctl(s, ih, Ilclose, 0, nhgetl(ih->ilid), 0);
+			goto raise;
+		}
+		s = new;
+
+		ic = (Ilcb*)s->ptcl;
+	
+		ic->conv = s;
+		ic->state = Ilsyncee;
+		ilcbinit(ic);
+		ic->rstart = nhgetl(ih->ilid);
+		iphtadd(&ipriv->ht, s);
+	}
+
+	qlock(s);
+	qunlock(il);
+	if(waserror()){
+		qunlock(s);
+		nexterror();
+	}
+	ilprocess(s, ih, bp);
+	qunlock(s);
+	poperror();
+	return;
+raise:
+	freeblist(bp);
+}
+
+void
+_ilprocess(Conv *s, Ilhdr *h, Block *bp)
+{
+	Ilcb *ic;
+	ulong id, ack;
+	Ilpriv *priv;
+
+	id = nhgetl(h->ilid);
+	ack = nhgetl(h->ilack);
+
+	ic = (Ilcb*)s->ptcl;
+
+	ic->lastrecv = NOW;
+	ic->querytime = NOW + QueryTime;
+	priv = s->p->priv;
+	priv->stats[InMsgs]++;
+
+	switch(ic->state) {
+	default:
+		netlog(s->p->f, Logil, "il: unknown state %d\n", ic->state);
+	case Ilclosed:
+		freeblist(bp);
+		break;
+	case Ilsyncer:
+		switch(h->iltype) {
+		default:
+			break;
+		case Ilsync:
+			if(ack != ic->start)
+				ilhangup(s, "connection rejected");
+			else {
+				ic->recvd = id;
+				ic->rstart = id;
+				ilsendctl(s, nil, Ilack, ic->next, ic->recvd, 0);
+				ic->state = Ilestablished;
+				ic->fasttimeout = 0;
+				ic->rexmit = 0;
+				Fsconnected(s, nil);
+				ilpullup(s);
+			}
+			break;
+		case Ilclose:
+			if(ack == ic->start)
+				ilhangup(s, "connection rejected");
+			break;
+		}
+		freeblist(bp);
+		break;
+	case Ilsyncee:
+		switch(h->iltype) {
+		default:
+			break;
+		case Ilsync:
+			if(id != ic->rstart || ack != 0){
+				illocalclose(s);
+			} else {
+				ic->recvd = id;
+				ilsendctl(s, nil, Ilsync, ic->start, ic->recvd, 0);
+			}
+			break;
+		case Ilack:
+			if(ack == ic->start) {
+				ic->state = Ilestablished;
+				ic->fasttimeout = 0;
+				ic->rexmit = 0;
+				ilpullup(s);
+			}
+			break;
+		case Ildata:
+			if(ack == ic->start) {
+				ic->state = Ilestablished;
+				ic->fasttimeout = 0;
+				ic->rexmit = 0;
+				goto established;
+			}
+			break;
+		case Ilclose:
+			if(ack == ic->start)
+				ilhangup(s, "remote close");
+			break;
+		}
+		freeblist(bp);
+		break;
+	case Ilestablished:
+	established:
+		switch(h->iltype) {
+		case Ilsync:
+			if(id != ic->rstart)
+				ilhangup(s, "remote close");
+			else
+				ilsendctl(s, nil, Ilack, ic->next, ic->rstart, 0);
+			freeblist(bp);	
+			break;
+		case Ildata:
+			/*
+			 * avoid consuming all the mount rpc buffers in the
+			 * system.  if the input queue is too long, drop this
+			 * packet.
+			 */
+			if (s->rq && qlen(s->rq) >= Maxrq) {
+				priv->stats[DroppedMsgs]++;
+				freeblist(bp);
+				break;
+			}
+
+			ilackto(ic, ack, bp);
+			iloutoforder(s, h, bp);
+			ilpullup(s);
+			break;
+		case Ildataquery:
+			ilackto(ic, ack, bp);
+			iloutoforder(s, h, bp);
+			ilpullup(s);
+			ilsendctl(s, nil, Ilstate, ic->next, ic->recvd, h->ilspec);
+			break;
+		case Ilack:
+			ilackto(ic, ack, bp);
+			freeblist(bp);
+			break;
+		case Ilquery:
+			ilackto(ic, ack, bp);
+			ilsendctl(s, nil, Ilstate, ic->next, ic->recvd, h->ilspec);
+			freeblist(bp);
+			break;
+		case Ilstate:
+			if(ack >= ic->rttack)
+				ic->rttack = 0;
+			ilackto(ic, ack, bp);
+			if(h->ilspec > Nqt)
+				h->ilspec = 0;
+			if(ic->qt[h->ilspec] > ack){
+				ilrexmit(ic);
+				ilsettimeout(ic);
+			}
+			freeblist(bp);
+			break;
+		case Ilclose:
+			freeblist(bp);
+			if(ack < ic->start || ack > ic->next) 
+				break;
+			ic->recvd = id;
+			ilsendctl(s, nil, Ilclose, ic->next, ic->recvd, 0);
+			ic->state = Ilclosing;
+			ilsettimeout(ic);
+			ilfreeq(ic);
+			break;
+		}
+		break;
+	case Illistening:
+		freeblist(bp);
+		break;
+	case Ilclosing:
+		switch(h->iltype) {
+		case Ilclose:
+			ic->recvd = id;
+			ilsendctl(s, nil, Ilclose, ic->next, ic->recvd, 0);
+			if(ack == ic->next)
+				ilhangup(s, nil);
+			break;
+		default:
+			break;
+		}
+		freeblist(bp);
+		break;
+	}
+}
+
+void
+ilrexmit(Ilcb *ic)
+{
+	Ilhdr *h;
+	Block *nb;
+	Conv *c;
+	ulong id;
+	Ilpriv *priv;
+
+	nb = nil;
+	qlock(&ic->ackq);
+	if(ic->unacked)
+		nb = copyblock(ic->unacked, blocklen(ic->unacked));
+	qunlock(&ic->ackq);
+
+	if(nb == nil)
+		return;
+
+	h = (Ilhdr*)nb->rp;
+	h->vihl = IP_VER4;
+
+	h->iltype = Ildataquery;
+	hnputl(h->ilack, ic->recvd);
+	h->ilspec = ilnextqt(ic);
+	h->ilsum[0] = 0;
+	h->ilsum[1] = 0;
+	hnputs(h->ilsum, ptclcsum(nb, IL_IPSIZE, nhgets(h->illen)));
+
+	c = ic->conv;
+	id = nhgetl(h->ilid);
+	netlog(c->p->f, Logil, "il: rexmit %d %ud: %d %d: %i %d/%d\n", id, ic->recvd,
+		ic->rexmit, ic->timeout,
+		c->raddr, c->lport, c->rport);
+
+	ilbackoff(ic);
+
+	ipoput4(c->p->f, nb, 0, c->ttl, c->tos, c);
+
+	/* statistics */
+	ic->rxtot++;
+	priv = c->p->priv;
+	priv->rexmit++;
+}
+
+/* DEBUG */
+void
+ilprocess(Conv *s, Ilhdr *h, Block *bp)
+{
+	Ilcb *ic;
+
+	ic = (Ilcb*)s->ptcl;
+
+	USED(ic);
+	netlog(s->p->f, Logilmsg, "%11s rcv %d/%d snt %d/%d pkt(%s id %d ack %d %d->%d) ",
+		ilstates[ic->state],  ic->rstart, ic->recvd, ic->start, 
+		ic->next, iltype[h->iltype], nhgetl(h->ilid), 
+		nhgetl(h->ilack), nhgets(h->ilsrc), nhgets(h->ildst));
+
+	_ilprocess(s, h, bp);
+
+	netlog(s->p->f, Logilmsg, "%11s rcv %d snt %d\n", ilstates[ic->state], ic->recvd, ic->next);
+}
+
+void
+ilhangup(Conv *s, char *msg)
+{
+	Ilcb *ic;
+	int callout;
+
+	netlog(s->p->f, Logil, "il: hangup! %I %d/%d: %s\n", s->raddr,
+		s->lport, s->rport, msg?msg:"no reason");
+
+	ic = (Ilcb*)s->ptcl;
+	callout = ic->state == Ilsyncer;
+	illocalclose(s);
+
+	qhangup(s->rq, msg);
+	qhangup(s->wq, msg);
+
+	if(callout)
+		Fsconnected(s, msg);
+}
+
+void
+ilpullup(Conv *s)
+{
+	Ilcb *ic;
+	Ilhdr *oh;
+	Block *bp;
+	ulong oid, dlen;
+	Ilpriv *ipriv;
+
+	ic = (Ilcb*)s->ptcl;
+	if(ic->state != Ilestablished)
+		return;
+
+	qlock(&ic->outo);
+	while(ic->outoforder) {
+		bp = ic->outoforder;
+		oh = (Ilhdr*)bp->rp;
+		oid = nhgetl(oh->ilid);
+		if(oid <= ic->recvd) {
+			ic->outoforder = bp->list;
+			freeblist(bp);
+			continue;
+		}
+		if(oid != ic->recvd+1){
+			ipriv = s->p->priv;
+			ipriv->stats[OutOfOrder]++;
+			break;
+		}
+
+		ic->recvd = oid;
+		ic->outoforder = bp->list;
+
+		bp->list = nil;
+		dlen = nhgets(oh->illen)-IL_HDRSIZE;
+		bp = trimblock(bp, IL_IPSIZE+IL_HDRSIZE, dlen);
+		/*
+		 * Upper levels don't know about multiple-block
+		 * messages so copy all into one (yick).
+		 */
+		bp = concatblock(bp);
+		if(bp == 0)
+			panic("ilpullup");
+		bp = packblock(bp);
+		if(bp == 0)
+			panic("ilpullup2");
+		qpass(s->rq, bp);
+	}
+	qunlock(&ic->outo);
+}
+
+void
+iloutoforder(Conv *s, Ilhdr *h, Block *bp)
+{
+	Ilcb *ic;
+	uchar *lid;
+	Block *f, **l;
+	ulong id, newid;
+	Ilpriv *ipriv;
+
+	ipriv = s->p->priv;
+	ic = (Ilcb*)s->ptcl;
+	bp->list = nil;
+
+	id = nhgetl(h->ilid);
+	/* Window checks */
+	if(id <= ic->recvd || id > ic->recvd+ic->window) {
+		netlog(s->p->f, Logil, "il: message outside window %ud <%ud-%ud>: %i %d/%d\n",
+			id, ic->recvd, ic->recvd+ic->window, s->raddr, s->lport, s->rport);
+		freeblist(bp);
+		return;
+	}
+
+	/* Packet is acceptable so sort onto receive queue for pullup */
+	qlock(&ic->outo);
+	if(ic->outoforder == nil)
+		ic->outoforder = bp;
+	else {
+		l = &ic->outoforder;
+		for(f = *l; f; f = f->list) {
+			lid = ((Ilhdr*)(f->rp))->ilid;
+			newid = nhgetl(lid);
+			if(id <= newid) {
+				if(id == newid) {
+					ipriv->stats[DupMsg]++;
+					ipriv->stats[DupBytes] += blocklen(bp);
+					qunlock(&ic->outo);
+					freeblist(bp);
+					return;
+				}
+				bp->list = f;
+				*l = bp;
+				qunlock(&ic->outo);
+				return;
+			}
+			l = &f->list;
+		}
+		*l = bp;
+	}
+	qunlock(&ic->outo);
+}
+
+void
+ilsendctl(Conv *ipc, Ilhdr *inih, int type, ulong id, ulong ack, int ilspec)
+{
+	Ilhdr *ih;
+	Ilcb *ic;
+	Block *bp;
+	int ttl, tos;
+
+	bp = allocb(IL_IPSIZE+IL_HDRSIZE);
+	bp->wp += IL_IPSIZE+IL_HDRSIZE;
+
+	ih = (Ilhdr *)(bp->rp);
+	ih->vihl = IP_VER4;
+
+	/* Ip fields */
+	ih->proto = IP_ILPROTO;
+	hnputs(ih->illen, IL_HDRSIZE);
+	ih->frag[0] = 0;
+	ih->frag[1] = 0;
+	if(inih) {
+		hnputl(ih->dst, nhgetl(inih->src));
+		hnputl(ih->src, nhgetl(inih->dst));
+		hnputs(ih->ilsrc, nhgets(inih->ildst));
+		hnputs(ih->ildst, nhgets(inih->ilsrc));
+		hnputl(ih->ilid, nhgetl(inih->ilack));
+		hnputl(ih->ilack, nhgetl(inih->ilid));
+		ttl = MAXTTL;
+		tos = DFLTTOS;
+	}
+	else {
+		v6tov4(ih->dst, ipc->raddr);
+		v6tov4(ih->src, ipc->laddr);
+		hnputs(ih->ilsrc, ipc->lport);
+		hnputs(ih->ildst, ipc->rport);
+		hnputl(ih->ilid, id);
+		hnputl(ih->ilack, ack);
+		ic = (Ilcb*)ipc->ptcl;
+		ic->acksent = ack;
+		ic->acktime = NOW;
+		ttl = ipc->ttl;
+		tos = ipc->tos;
+	}
+	ih->iltype = type;
+	ih->ilspec = ilspec;
+	ih->ilsum[0] = 0;
+	ih->ilsum[1] = 0;
+
+	if(ilcksum)
+		hnputs(ih->ilsum, ptclcsum(bp, IL_IPSIZE, IL_HDRSIZE));
+
+if(ipc==nil)
+	panic("ipc is nil caller is %.8lux", getcallerpc(&ipc));
+if(ipc->p==nil)
+	panic("ipc->p is nil");
+
+	netlog(ipc->p->f, Logilmsg, "ctl(%s id %d ack %d %d->%d)\n",
+		iltype[ih->iltype], nhgetl(ih->ilid), nhgetl(ih->ilack), 
+		nhgets(ih->ilsrc), nhgets(ih->ildst));
+
+	ipoput4(ipc->p->f, bp, 0, ttl, tos, ipc);
+}
+
+void
+ilreject(Fs *f, Ilhdr *inih)
+{
+	Ilhdr *ih;
+	Block *bp;
+
+	bp = allocb(IL_IPSIZE+IL_HDRSIZE);
+	bp->wp += IL_IPSIZE+IL_HDRSIZE;
+
+	ih = (Ilhdr *)(bp->rp);
+	ih->vihl = IP_VER4;
+
+	/* Ip fields */
+	ih->proto = IP_ILPROTO;
+	hnputs(ih->illen, IL_HDRSIZE);
+	ih->frag[0] = 0;
+	ih->frag[1] = 0;
+	hnputl(ih->dst, nhgetl(inih->src));
+	hnputl(ih->src, nhgetl(inih->dst));
+	hnputs(ih->ilsrc, nhgets(inih->ildst));
+	hnputs(ih->ildst, nhgets(inih->ilsrc));
+	hnputl(ih->ilid, nhgetl(inih->ilack));
+	hnputl(ih->ilack, nhgetl(inih->ilid));
+	ih->iltype = Ilclose;
+	ih->ilspec = 0;
+	ih->ilsum[0] = 0;
+	ih->ilsum[1] = 0;
+
+	if(ilcksum)
+		hnputs(ih->ilsum, ptclcsum(bp, IL_IPSIZE, IL_HDRSIZE));
+
+	ipoput4(f, bp, 0, MAXTTL, DFLTTOS, nil);
+}
+
+void
+ilsettimeout(Ilcb *ic)
+{
+	ulong pt;
+
+	pt = (ic->delay>>LogAGain)
+		+ ic->unackedbytes/(ic->rate>>LogAGain)
+		+ (ic->mdev>>(LogDGain-1))
+		+ AckDelay;
+	if(pt > MaxTimeout)
+		pt = MaxTimeout;
+	ic->timeout = NOW + pt;
+}
+
+void
+ilbackoff(Ilcb *ic)
+{
+	ulong pt;
+	int i;
+
+	pt = (ic->delay>>LogAGain)
+		+ ic->unackedbytes/(ic->rate>>LogAGain)
+		+ (ic->mdev>>(LogDGain-1))
+		+ AckDelay;
+	for(i = 0; i < ic->rexmit; i++)
+		pt = pt + (pt>>1);
+	if(pt > MaxTimeout)
+		pt = MaxTimeout;
+	ic->timeout = NOW + pt;
+
+	if(ic->fasttimeout)
+		ic->timeout = NOW+Iltickms;
+
+	ic->rexmit++;
+}
+
+// complain if two numbers not within an hour of each other
+#define Tfuture (1000*60*60)
+int
+later(ulong t1, ulong t2, char *x)
+{
+	int dt;
+
+	dt = t1 - t2;
+	if(dt > 0) {
+		if(x != nil && dt > Tfuture)
+			print("%s: way future %d\n", x, dt);
+		return 1;
+	}
+	if(dt < -Tfuture) {
+		if(x != nil)
+			print("%s: way past %d\n", x, -dt);
+		return 1;
+	}
+	return 0;
+}
+
+void
+ilackproc(void *x)
+{
+	Ilcb *ic;
+	Conv **s, *p;
+	Proto *il;
+
+	il = x;
+
+loop:
+	tsleep(&up->sleep, return0, 0, Iltickms);
+	for(s = il->conv; s && *s; s++) {
+		p = *s;
+		ic = (Ilcb*)p->ptcl;
+
+		switch(ic->state) {
+		case Ilclosed:
+		case Illistening:
+			break;
+		case Ilclosing:
+			if(later(NOW, ic->timeout, "timeout0")) {
+				if(ic->rexmit > MaxRexmit){
+					ilhangup(p, nil);
+					break;
+				}
+				ilsendctl(p, nil, Ilclose, ic->next, ic->recvd, 0);
+				ilbackoff(ic);
+			}
+			break;
+
+		case Ilsyncee:
+		case Ilsyncer:
+			if(later(NOW, ic->timeout, "timeout1")) {
+				if(ic->rexmit > MaxRexmit){
+					ilhangup(p, etime);
+					break;
+				}
+				ilsendctl(p, nil, Ilsync, ic->start, ic->recvd, 0);
+				ilbackoff(ic);
+			}
+			break;
+
+		case Ilestablished:
+			if(ic->recvd != ic->acksent)
+			if(later(NOW, ic->acktime, "acktime"))
+				ilsendctl(p, nil, Ilack, ic->next, ic->recvd, 0);
+
+			if(later(NOW, ic->querytime, "querytime")){
+				if(later(NOW, ic->lastrecv+DeathTime, "deathtime")){
+					netlog(il->f, Logil, "il: hangup: deathtime\n");
+					ilhangup(p, etime);
+					break;
+				}
+				ilsendctl(p, nil, Ilquery, ic->next, ic->recvd, ilnextqt(ic));
+				ic->querytime = NOW + QueryTime;
+			}
+
+			if(ic->unacked != nil)
+			if(later(NOW, ic->timeout, "timeout2")) {
+				if(ic->rexmit > MaxRexmit){
+					netlog(il->f, Logil, "il: hangup: too many rexmits\n");
+					ilhangup(p, etime);
+					break;
+				}
+				ilsendctl(p, nil, Ilquery, ic->next, ic->recvd, ilnextqt(ic));
+				ic->rxquery++;
+				ilbackoff(ic);
+			}
+			break;
+		}
+	}
+	goto loop;
+}
+
+void
+ilcbinit(Ilcb *ic)
+{
+	ic->start = nrand(0x1000000);
+	ic->next = ic->start+1;
+	ic->recvd = 0;
+	ic->window = Defaultwin;
+	ic->unackedbytes = 0;
+	ic->unacked = nil;
+	ic->outoforder = nil;
+	ic->rexmit = 0;
+	ic->rxtot = 0;
+	ic->rxquery = 0;
+	ic->qtx = 1;
+	ic->fasttimeout = 0;
+
+	/* timers */
+	ic->delay = DefRtt<<LogAGain;
+	ic->mdev = DefRtt<<LogDGain;
+	ic->rate = DefByteRate<<LogAGain;
+	ic->querytime = NOW + QueryTime;
+	ic->lastrecv = NOW;	/* or we'll timeout right away */
+	ilsettimeout(ic);
+}
+
+char*
+ilstart(Conv *c, int type, int fasttimeout)
+{
+	Ilcb *ic;
+	Ilpriv *ipriv;
+	char kpname[KNAMELEN];
+
+	ipriv = c->p->priv;
+
+	if(ipriv->ackprocstarted == 0){
+		qlock(&ipriv->apl);
+		if(ipriv->ackprocstarted == 0){
+			sprint(kpname, "#I%dilack", c->p->f->dev);
+			kproc(kpname, ilackproc, c->p, 0);
+			ipriv->ackprocstarted = 1;
+		}
+		qunlock(&ipriv->apl);
+	}
+
+	ic = (Ilcb*)c->ptcl;
+	ic->conv = c;
+
+	if(ic->state != Ilclosed)
+		return nil;
+
+	ilcbinit(ic);
+
+	if(fasttimeout){
+		/* timeout if we can't connect quickly */
+		ic->fasttimeout = 1;
+		ic->timeout = NOW+Iltickms;
+		ic->rexmit = MaxRexmit - 4;
+	};
+
+	switch(type) {
+	default:
+		netlog(c->p->f, Logil, "il: start: type %d\n", type);
+		break;
+	case IL_LISTEN:
+		ic->state = Illistening;
+		iphtadd(&ipriv->ht, c);
+		break;
+	case IL_CONNECT:
+		ic->state = Ilsyncer;
+		iphtadd(&ipriv->ht, c);
+		ilsendctl(c, nil, Ilsync, ic->start, ic->recvd, 0);
+		break;
+	}
+
+	return nil;
+}
+
+void
+ilfreeq(Ilcb *ic)
+{
+	Block *bp, *next;
+
+	qlock(&ic->ackq);
+	for(bp = ic->unacked; bp; bp = next) {
+		next = bp->list;
+		freeblist(bp);
+	}
+	ic->unacked = nil;
+	qunlock(&ic->ackq);
+
+	qlock(&ic->outo);
+	for(bp = ic->outoforder; bp; bp = next) {
+		next = bp->list;
+		freeblist(bp);
+	}
+	ic->outoforder = nil;
+	qunlock(&ic->outo);
+}
+
+void
+iladvise(Proto *il, Block *bp, char *msg)
+{
+	Ilhdr *h;
+	Ilcb *ic;		
+	uchar source[IPaddrlen], dest[IPaddrlen];
+	ushort psource;
+	Conv *s, **p;
+
+	h = (Ilhdr*)(bp->rp);
+
+	v4tov6(dest, h->dst);
+	v4tov6(source, h->src);
+	psource = nhgets(h->ilsrc);
+
+
+	/* Look for a connection, unfortunately the destination port is missing */
+	qlock(il);
+	for(p = il->conv; *p; p++) {
+		s = *p;
+		if(s->lport == psource)
+		if(ipcmp(s->laddr, source) == 0)
+		if(ipcmp(s->raddr, dest) == 0){
+			qunlock(il);
+			ic = (Ilcb*)s->ptcl;
+			switch(ic->state){
+			case Ilsyncer:
+				ilhangup(s, msg);
+				break;
+			}
+			freeblist(bp);
+			return;
+		}
+	}
+	qunlock(il);
+	freeblist(bp);
+}
+
+int
+ilnextqt(Ilcb *ic)
+{
+	int x;
+
+	qlock(&ic->ackq);
+	x = ic->qtx;
+	if(++x > Nqt)
+		x = 1;
+	ic->qtx = x;
+	ic->qt[x] = ic->next-1;	/* highest xmitted packet */
+	ic->qt[0] = ic->qt[x];	/* compatibility with old implementations */
+	qunlock(&ic->ackq);
+
+	return x;
+}
+
+/* calculate scale constants that converts fast ticks to ms (more or less) */
+static void
+inittimescale(void)
+{
+	uvlong hz;
+
+	fastticks(&hz);
+	if(hz > 1000){
+		scalediv = hz/1000;
+		scalemul = 1;
+	} else {
+		scalediv = 1;
+		scalemul = 1000/hz;
+	}
+}
+
+void
+ilinit(Fs *f)
+{
+	Proto *il;
+
+	inittimescale();
+
+	il = smalloc(sizeof(Proto));
+	il->priv = smalloc(sizeof(Ilpriv));
+	il->name = "il";
+	il->connect = ilconnect;
+	il->announce = ilannounce;
+	il->state = ilstate;
+	il->create = ilcreate;
+	il->close = ilclose;
+	il->rcv = iliput;
+	il->ctl = nil;
+	il->advise = iladvise;
+	il->stats = ilxstats;
+	il->inuse = ilinuse;
+	il->gc = nil;
+	il->ipproto = IP_ILPROTO;
+	il->nc = scalednconv();
+	il->ptclsize = sizeof(Ilcb);
+	Fsproto(f, il);
+}
--- /dev/null
+++ b/os/ip/ip.c
@@ -1,0 +1,805 @@
+#include	"u.h"
+#include	"../port/lib.h"
+#include	"mem.h"
+#include	"dat.h"
+#include	"fns.h"
+#include	"../port/error.h"
+
+#include	"ip.h"
+
+typedef struct Ip4hdr		Ip4hdr;
+typedef struct IP		IP;
+typedef struct Fragment4	Fragment4;
+typedef struct Fragment6	Fragment6;
+typedef struct Ipfrag		Ipfrag;
+
+enum
+{
+	IP4HDR		= 20,		/* sizeof(Ip4hdr) */
+	IP6HDR		= 40,		/* sizeof(Ip6hdr) */
+	IP_HLEN4	= 0x05,		/* Header length in words */
+	IP_DF		= 0x4000,	/* Don't fragment */
+	IP_MF		= 0x2000,	/* More fragments */
+	IP6FHDR		= 8, 		/* sizeof(Fraghdr6) */
+	IP_MAX		= 64*1024,	/* Maximum Internet packet size */
+};
+
+#define BLKIPVER(xp)	(((Ip4hdr*)((xp)->rp))->vihl&0xF0)
+
+struct Ip4hdr
+{
+	uchar	vihl;		/* Version and header length */
+	uchar	tos;		/* Type of service */
+	uchar	length[2];	/* packet length */
+	uchar	id[2];		/* ip->identification */
+	uchar	frag[2];	/* Fragment information */
+	uchar	ttl;      	/* Time to live */
+	uchar	proto;		/* Protocol */
+	uchar	cksum[2];	/* Header checksum */
+	uchar	src[4];		/* IP source */
+	uchar	dst[4];		/* IP destination */
+};
+
+/* MIB II counters */
+enum
+{
+	Forwarding,
+	DefaultTTL,
+	InReceives,
+	InHdrErrors,
+	InAddrErrors,
+	ForwDatagrams,
+	InUnknownProtos,
+	InDiscards,
+	InDelivers,
+	OutRequests,
+	OutDiscards,
+	OutNoRoutes,
+	ReasmTimeout,
+	ReasmReqds,
+	ReasmOKs,
+	ReasmFails,
+	FragOKs,
+	FragFails,
+	FragCreates,
+
+	Nstats,
+};
+
+struct Fragment4
+{
+	Block*	blist;
+	Fragment4*	next;
+	ulong 	src;
+	ulong 	dst;
+	ushort	id;
+	ulong 	age;
+};
+
+struct Fragment6
+{
+	Block*	blist;
+	Fragment6*	next;
+	uchar 	src[IPaddrlen];
+	uchar 	dst[IPaddrlen];
+	uint	id;
+	ulong 	age;
+};
+
+struct Ipfrag
+{
+	ushort	foff;
+	ushort	flen;
+};
+
+/* an instance of IP */
+struct IP
+{
+	ulong		stats[Nstats];
+
+	QLock		fraglock4;
+	Fragment4*	flisthead4;
+	Fragment4*	fragfree4;
+	Ref		id4;
+
+	QLock		fraglock6;
+	Fragment6*	flisthead6;
+	Fragment6*	fragfree6;
+	Ref		id6;
+
+	int		iprouting;	/* true if we route like a gateway */
+};
+
+static char *statnames[] =
+{
+[Forwarding]	"Forwarding",
+[DefaultTTL]	"DefaultTTL",
+[InReceives]	"InReceives",
+[InHdrErrors]	"InHdrErrors",
+[InAddrErrors]	"InAddrErrors",
+[ForwDatagrams]	"ForwDatagrams",
+[InUnknownProtos]	"InUnknownProtos",
+[InDiscards]	"InDiscards",
+[InDelivers]	"InDelivers",
+[OutRequests]	"OutRequests",
+[OutDiscards]	"OutDiscards",
+[OutNoRoutes]	"OutNoRoutes",
+[ReasmTimeout]	"ReasmTimeout",
+[ReasmReqds]	"ReasmReqds",
+[ReasmOKs]	"ReasmOKs",
+[ReasmFails]	"ReasmFails",
+[FragOKs]	"FragOKs",
+[FragFails]	"FragFails",
+[FragCreates]	"FragCreates",
+};
+
+#define BLKIP(xp)	((Ip4hdr*)((xp)->rp))
+/*
+ * This sleazy macro relies on the media header size being
+ * larger than sizeof(Ipfrag). ipreassemble checks this is true
+ */
+#define BKFG(xp)	((Ipfrag*)((xp)->base))
+
+ushort		ipcsum(uchar*);
+Block*		ip4reassemble(IP*, int, Block*, Ip4hdr*);
+void		ipfragfree4(IP*, Fragment4*);
+Fragment4*	ipfragallo4(IP*);
+
+
+void
+ip_init_6(Fs *f)
+{
+	V6params *v6p;
+
+	v6p = smalloc(sizeof(V6params));
+	
+	v6p->rp.mflag		= 0;		// default not managed
+	v6p->rp.oflag		= 0;
+	v6p->rp.maxraint	= 600000;	// millisecs
+	v6p->rp.minraint	= 200000;
+	v6p->rp.linkmtu		= 0;		// no mtu sent
+	v6p->rp.reachtime	= 0;
+	v6p->rp.rxmitra		= 0;
+	v6p->rp.ttl		= MAXTTL;
+	v6p->rp.routerlt	= 3*(v6p->rp.maxraint);	
+
+	v6p->hp.rxmithost	= 1000;		// v6 RETRANS_TIMER
+
+	v6p->cdrouter 		= -1;
+
+	f->v6p			= v6p;
+
+}
+
+void
+initfrag(IP *ip, int size)
+{
+	Fragment4 *fq4, *eq4;
+	Fragment6 *fq6, *eq6;
+
+	ip->fragfree4 = (Fragment4*)malloc(sizeof(Fragment4) * size);
+	if(ip->fragfree4 == nil)
+		panic("initfrag");
+
+	eq4 = &ip->fragfree4[size];
+	for(fq4 = ip->fragfree4; fq4 < eq4; fq4++)
+		fq4->next = fq4+1;
+
+	ip->fragfree4[size-1].next = nil;
+
+	ip->fragfree6 = (Fragment6*)malloc(sizeof(Fragment6) * size);
+	if(ip->fragfree6 == nil)
+		panic("initfrag");
+
+	eq6 = &ip->fragfree6[size];
+	for(fq6 = ip->fragfree6; fq6 < eq6; fq6++)
+		fq6->next = fq6+1;
+
+	ip->fragfree6[size-1].next = nil;
+}
+
+void
+ip_init(Fs *f)
+{
+	IP *ip;
+
+	ip = smalloc(sizeof(IP));
+	initfrag(ip, 100);
+	f->ip = ip;
+
+	ip_init_6(f);
+}
+
+void
+iprouting(Fs *f, int on)
+{
+	f->ip->iprouting = on;
+	if(f->ip->iprouting==0)
+		f->ip->stats[Forwarding] = 2;
+	else
+		f->ip->stats[Forwarding] = 1;	
+}
+
+int
+ipoput4(Fs *f, Block *bp, int gating, int ttl, int tos, Conv *c)
+{
+	Ipifc *ifc;
+	uchar *gate;
+	ulong fragoff;
+	Block *xp, *nb;
+	Ip4hdr *eh, *feh;
+	int lid, len, seglen, chunk, dlen, blklen, offset, medialen;
+	Route *r, *sr;
+	IP *ip;
+	int rv = 0;
+
+	ip = f->ip;
+
+	/* Fill out the ip header */
+	eh = (Ip4hdr*)(bp->rp);
+
+	ip->stats[OutRequests]++;
+
+	/* Number of uchars in data and ip header to write */
+	len = blocklen(bp);
+
+	if(gating){
+		chunk = nhgets(eh->length);
+		if(chunk > len){
+			ip->stats[OutDiscards]++;
+			netlog(f, Logip, "short gated packet\n");
+			goto free;
+		}
+		if(chunk < len)
+			len = chunk;
+	}
+	if(len >= IP_MAX){
+		ip->stats[OutDiscards]++;
+		netlog(f, Logip, "exceeded ip max size %V\n", eh->dst);
+		goto free;
+	}
+
+	r = v4lookup(f, eh->dst, c);
+	if(r == nil){
+		ip->stats[OutNoRoutes]++;
+		netlog(f, Logip, "no interface %V\n", eh->dst);
+		rv = -1;
+		goto free;
+	}
+
+	ifc = r->ifc;
+	if(r->type & (Rifc|Runi))
+		gate = eh->dst;
+	else
+	if(r->type & (Rbcast|Rmulti)) {
+		gate = eh->dst;
+		sr = v4lookup(f, eh->src, nil);
+		if(sr != nil && (sr->type & Runi))
+			ifc = sr->ifc;
+	}
+	else
+		gate = r->v4.gate;
+
+	if(!gating)
+		eh->vihl = IP_VER4|IP_HLEN4;
+	eh->ttl = ttl;
+	if(!gating)
+		eh->tos = tos;
+
+	if(!canrlock(ifc))
+		goto free;
+	if(waserror()){
+		runlock(ifc);
+		nexterror();
+	}
+	if(ifc->m == nil)
+		goto raise;
+
+	/* If we dont need to fragment just send it */
+	medialen = ifc->maxtu - ifc->m->hsize;
+	if(len <= medialen) {
+		if(!gating)
+			hnputs(eh->id, incref(&ip->id4));
+		hnputs(eh->length, len);
+		if(!gating){
+			eh->frag[0] = 0;
+			eh->frag[1] = 0;
+		}
+		eh->cksum[0] = 0;
+		eh->cksum[1] = 0;
+		hnputs(eh->cksum, ipcsum(&eh->vihl));
+		ifc->m->bwrite(ifc, bp, V4, gate);
+		runlock(ifc);
+		poperror();
+		return 0;
+	}
+
+if((eh->frag[0] & (IP_DF>>8)) && !gating) print("%V: DF set\n", eh->dst);
+
+	if(eh->frag[0] & (IP_DF>>8)){
+		ip->stats[FragFails]++;
+		ip->stats[OutDiscards]++;
+		icmpcantfrag(f, bp, medialen);
+		netlog(f, Logip, "%V: eh->frag[0] & (IP_DF>>8)\n", eh->dst);
+		goto raise;
+	}
+
+	seglen = (medialen - IP4HDR) & ~7;
+	if(seglen < 8){
+		ip->stats[FragFails]++;
+		ip->stats[OutDiscards]++;
+		netlog(f, Logip, "%V seglen < 8\n", eh->dst);
+		goto raise;
+	}
+
+	dlen = len - IP4HDR;
+	xp = bp;
+	if(gating)
+		lid = nhgets(eh->id);
+	else
+		lid = incref(&ip->id4);
+
+	offset = IP4HDR;
+	while(xp != nil && offset && offset >= BLEN(xp)) {
+		offset -= BLEN(xp);
+		xp = xp->next;
+	}
+	xp->rp += offset;
+
+	if(gating)
+		fragoff = nhgets(eh->frag)<<3;
+	else
+		fragoff = 0;
+	dlen += fragoff;
+	for(; fragoff < dlen; fragoff += seglen) {
+		nb = allocb(IP4HDR+seglen);
+		feh = (Ip4hdr*)(nb->rp);
+
+		memmove(nb->wp, eh, IP4HDR);
+		nb->wp += IP4HDR;
+
+		if((fragoff + seglen) >= dlen) {
+			seglen = dlen - fragoff;
+			hnputs(feh->frag, fragoff>>3);
+		}
+		else	
+			hnputs(feh->frag, (fragoff>>3)|IP_MF);
+
+		hnputs(feh->length, seglen + IP4HDR);
+		hnputs(feh->id, lid);
+
+		/* Copy up the data area */
+		chunk = seglen;
+		while(chunk) {
+			if(!xp) {
+				ip->stats[OutDiscards]++;
+				ip->stats[FragFails]++;
+				freeblist(nb);
+				netlog(f, Logip, "!xp: chunk %d\n", chunk);
+				goto raise;
+			}
+			blklen = chunk;
+			if(BLEN(xp) < chunk)
+				blklen = BLEN(xp);
+			memmove(nb->wp, xp->rp, blklen);
+			nb->wp += blklen;
+			xp->rp += blklen;
+			chunk -= blklen;
+			if(xp->rp == xp->wp)
+				xp = xp->next;
+		} 
+
+		feh->cksum[0] = 0;
+		feh->cksum[1] = 0;
+		hnputs(feh->cksum, ipcsum(&feh->vihl));
+		ifc->m->bwrite(ifc, nb, V4, gate);
+		ip->stats[FragCreates]++;
+	}
+	ip->stats[FragOKs]++;
+raise:
+	runlock(ifc);
+	poperror();
+free:
+	freeblist(bp);
+	return rv;
+}
+
+void
+ipiput4(Fs *f, Ipifc *ifc, Block *bp)
+{
+	int hl;
+	int hop, tos, proto, olen;
+	Ip4hdr *h;
+	Proto *p;
+	ushort frag;
+	int notforme;
+	uchar *dp, v6dst[IPaddrlen];
+	IP *ip;
+	Route *r;
+
+	if(BLKIPVER(bp) != IP_VER4) {
+		ipiput6(f, ifc, bp);
+		return;
+	}
+
+	ip = f->ip;
+	ip->stats[InReceives]++;
+
+	/*
+	 *  Ensure we have all the header info in the first
+	 *  block.  Make life easier for other protocols by
+	 *  collecting up to the first 64 bytes in the first block.
+	 */
+	if(BLEN(bp) < 64) {
+		hl = blocklen(bp);
+		if(hl < IP4HDR)
+			hl = IP4HDR;
+		if(hl > 64)
+			hl = 64;
+		bp = pullupblock(bp, hl);
+		if(bp == nil)
+			return;
+	}
+
+	h = (Ip4hdr*)(bp->rp);
+
+	/* dump anything that whose header doesn't checksum */
+	if((bp->flag & Bipck) == 0 && ipcsum(&h->vihl)) {
+		ip->stats[InHdrErrors]++;
+		netlog(f, Logip, "ip: checksum error %V\n", h->src);
+		freeblist(bp);
+		return;
+	}
+	v4tov6(v6dst, h->dst);
+	notforme = ipforme(f, v6dst) == 0;
+
+	/* Check header length and version */
+	if((h->vihl&0x0F) != IP_HLEN4) {
+		hl = (h->vihl&0xF)<<2;
+		if(hl < (IP_HLEN4<<2)) {
+			ip->stats[InHdrErrors]++;
+			netlog(f, Logip, "ip: %V bad hivl %ux\n", h->src, h->vihl);
+			freeblist(bp);
+			return;
+		}
+	  /* If this is not routed strip off the options */
+		if(notforme == 0) {
+			olen = nhgets(h->length);
+			dp = bp->rp + (hl - (IP_HLEN4<<2));
+			memmove(dp, h, IP_HLEN4<<2);
+			bp->rp = dp;
+			h = (Ip4hdr*)(bp->rp);
+			h->vihl = (IP_VER4|IP_HLEN4);
+			hnputs(h->length, olen-hl+(IP_HLEN4<<2));
+		}
+	}
+
+	/* route */
+	if(notforme) {
+		Conv conv;
+
+		if(!ip->iprouting){
+			freeb(bp);
+			return;
+		}
+
+		/* don't forward to source's network */
+		conv.r = nil;
+		r = v4lookup(f, h->dst, &conv);
+		if(r == nil || r->ifc == ifc){
+			ip->stats[OutDiscards]++;
+			freeblist(bp);
+			return;
+		}
+
+		/* don't forward if packet has timed out */
+		hop = h->ttl;
+		if(hop < 1) {
+			ip->stats[InHdrErrors]++;
+			icmpttlexceeded(f, ifc->lifc->local, bp);
+			freeblist(bp);
+			return;
+		}
+
+		/* reassemble if the interface expects it */
+if(r->ifc == nil) panic("nil route rfc");
+		if(r->ifc->reassemble){
+			frag = nhgets(h->frag);
+			if(frag) {
+				h->tos = 0;
+				if(frag & IP_MF)
+					h->tos = 1;
+				bp = ip4reassemble(ip, frag, bp, h);
+				if(bp == nil)
+					return;
+				h = (Ip4hdr*)(bp->rp);
+			}
+		}
+
+		ip->stats[ForwDatagrams]++;
+		tos = h->tos;
+		hop = h->ttl;
+		ipoput4(f, bp, 1, hop - 1, tos, &conv);
+		return;
+	}
+
+	frag = nhgets(h->frag);
+	if(frag) {
+		h->tos = 0;
+		if(frag & IP_MF)
+			h->tos = 1;
+		bp = ip4reassemble(ip, frag, bp, h);
+		if(bp == nil)
+			return;
+		h = (Ip4hdr*)(bp->rp);
+	}
+
+	/* don't let any frag info go up the stack */
+	h->frag[0] = 0;
+	h->frag[1] = 0;
+
+	proto = h->proto;
+	p = Fsrcvpcol(f, proto);
+	if(p != nil && p->rcv != nil) {
+		ip->stats[InDelivers]++;
+		(*p->rcv)(p, ifc, bp);
+		return;
+	}
+	ip->stats[InDiscards]++;
+	ip->stats[InUnknownProtos]++;
+	freeblist(bp);
+}
+
+int
+ipstats(Fs *f, char *buf, int len)
+{
+	IP *ip;
+	char *p, *e;
+	int i;
+
+	ip = f->ip;
+	ip->stats[DefaultTTL] = MAXTTL;
+
+	p = buf;
+	e = p+len;
+	for(i = 0; i < Nstats; i++)
+		p = seprint(p, e, "%s: %lud\n", statnames[i], ip->stats[i]);
+	return p - buf;
+}
+
+Block*
+ip4reassemble(IP *ip, int offset, Block *bp, Ip4hdr *ih)
+{
+	int fend;
+	ushort id;
+	Fragment4 *f, *fnext;
+	ulong src, dst;
+	Block *bl, **l, *last, *prev;
+	int ovlap, len, fragsize, pktposn;
+
+	src = nhgetl(ih->src);
+	dst = nhgetl(ih->dst);
+	id = nhgets(ih->id);
+
+	/*
+	 *  block lists are too hard, pullupblock into a single block
+	 */
+	if(bp->next){
+		bp = pullupblock(bp, blocklen(bp));
+		ih = (Ip4hdr*)(bp->rp);
+	}
+
+	qlock(&ip->fraglock4);
+
+	/*
+	 *  find a reassembly queue for this fragment
+	 */
+	for(f = ip->flisthead4; f; f = fnext){
+		fnext = f->next;	/* because ipfragfree4 changes the list */
+		if(f->src == src && f->dst == dst && f->id == id)
+			break;
+		if(f->age < NOW){
+			ip->stats[ReasmTimeout]++;
+			ipfragfree4(ip, f);
+		}
+	}
+
+	/*
+	 *  if this isn't a fragmented packet, accept it
+	 *  and get rid of any fragments that might go
+	 *  with it.
+	 */
+	if(!ih->tos && (offset & ~(IP_MF|IP_DF)) == 0) {
+		if(f != nil) {
+			ipfragfree4(ip, f);
+			ip->stats[ReasmFails]++;
+		}
+		qunlock(&ip->fraglock4);
+		return bp;
+	}
+
+	if(bp->base+sizeof(Ipfrag) >= bp->rp){
+		bp = padblock(bp, sizeof(Ipfrag));
+		bp->rp += sizeof(Ipfrag);
+	}
+
+	BKFG(bp)->foff = offset<<3;
+	BKFG(bp)->flen = nhgets(ih->length)-IP4HDR;
+
+	/* First fragment allocates a reassembly queue */
+	if(f == nil) {
+		f = ipfragallo4(ip);
+		f->id = id;
+		f->src = src;
+		f->dst = dst;
+
+		f->blist = bp;
+
+		qunlock(&ip->fraglock4);
+		ip->stats[ReasmReqds]++;
+		return nil;
+	}
+
+	/*
+	 *  find the new fragment's position in the queue
+	 */
+	prev = nil;
+	l = &f->blist;
+	bl = f->blist;
+	while(bl != nil && BKFG(bp)->foff > BKFG(bl)->foff) {
+		prev = bl;
+		l = &bl->next;
+		bl = bl->next;
+	}
+
+	/* Check overlap of a previous fragment - trim away as necessary */
+	if(prev) {
+		ovlap = BKFG(prev)->foff + BKFG(prev)->flen - BKFG(bp)->foff;
+		if(ovlap > 0) {
+			if(ovlap >= BKFG(bp)->flen) {
+				freeblist(bp);
+				qunlock(&ip->fraglock4);
+				return nil;
+			}
+			BKFG(prev)->flen -= ovlap;
+		}
+	}
+
+	/* Link onto assembly queue */
+	bp->next = *l;
+	*l = bp;
+
+	/* Check to see if succeeding segments overlap */
+	if(bp->next) {
+		l = &bp->next;
+		fend = BKFG(bp)->foff + BKFG(bp)->flen;
+		/* Take completely covered segments out */
+		while(*l) {
+			ovlap = fend - BKFG(*l)->foff;
+			if(ovlap <= 0)
+				break;
+			if(ovlap < BKFG(*l)->flen) {
+				BKFG(*l)->flen -= ovlap;
+				BKFG(*l)->foff += ovlap;
+				/* move up ih hdrs */
+				memmove((*l)->rp + ovlap, (*l)->rp, IP4HDR);
+				(*l)->rp += ovlap;
+				break;
+			}
+			last = (*l)->next;
+			(*l)->next = nil;
+			freeblist(*l);
+			*l = last;
+		}
+	}
+
+	/*
+	 *  look for a complete packet.  if we get to a fragment
+	 *  without IP_MF set, we're done.
+	 */
+	pktposn = 0;
+	for(bl = f->blist; bl; bl = bl->next) {
+		if(BKFG(bl)->foff != pktposn)
+			break;
+		if((BLKIP(bl)->frag[0]&(IP_MF>>8)) == 0) {
+			bl = f->blist;
+			len = nhgets(BLKIP(bl)->length);
+			bl->wp = bl->rp + len;
+
+			/* Pullup all the fragment headers and
+			 * return a complete packet
+			 */
+			for(bl = bl->next; bl; bl = bl->next) {
+				fragsize = BKFG(bl)->flen;
+				len += fragsize;
+				bl->rp += IP4HDR;
+				bl->wp = bl->rp + fragsize;
+			}
+
+			bl = f->blist;
+			f->blist = nil;
+			ipfragfree4(ip, f);
+			ih = BLKIP(bl);
+			hnputs(ih->length, len);
+			qunlock(&ip->fraglock4);
+			ip->stats[ReasmOKs]++;
+			return bl;		
+		}
+		pktposn += BKFG(bl)->flen;
+	}
+	qunlock(&ip->fraglock4);
+	return nil;
+}
+
+/*
+ * ipfragfree4 - Free a list of fragments - assume hold fraglock4
+ */
+void
+ipfragfree4(IP *ip, Fragment4 *frag)
+{
+	Fragment4 *fl, **l;
+
+	if(frag->blist)
+		freeblist(frag->blist);
+
+	frag->src = 0;
+	frag->id = 0;
+	frag->blist = nil;
+
+	l = &ip->flisthead4;
+	for(fl = *l; fl; fl = fl->next) {
+		if(fl == frag) {
+			*l = frag->next;
+			break;
+		}
+		l = &fl->next;
+	}
+
+	frag->next = ip->fragfree4;
+	ip->fragfree4 = frag;
+
+}
+
+/*
+ * ipfragallo4 - allocate a reassembly queue - assume hold fraglock4
+ */
+Fragment4 *
+ipfragallo4(IP *ip)
+{
+	Fragment4 *f;
+
+	while(ip->fragfree4 == nil) {
+		/* free last entry on fraglist */
+		for(f = ip->flisthead4; f->next; f = f->next)
+			;
+		ipfragfree4(ip, f);
+	}
+	f = ip->fragfree4;
+	ip->fragfree4 = f->next;
+	f->next = ip->flisthead4;
+	ip->flisthead4 = f;
+	f->age = NOW + 30000;
+
+	return f;
+}
+
+ushort
+ipcsum(uchar *addr)
+{
+	int len;
+	ulong sum;
+
+	sum = 0;
+	len = (addr[0]&0xf)<<2;
+
+	while(len > 0) {
+		sum += addr[0]<<8 | addr[1] ;
+		len -= 2;
+		addr += 2;
+	}
+
+	sum = (sum & 0xffff) + (sum >> 16);
+	sum = (sum & 0xffff) + (sum >> 16);
+
+	return (sum^0xffff);
+}
--- /dev/null
+++ b/os/ip/ip.h
@@ -1,0 +1,672 @@
+typedef struct	Conv	Conv;
+typedef struct	Fs	Fs;
+typedef union	Hwaddr	Hwaddr;
+typedef struct	IP	IP;
+typedef struct	IPaux	IPaux;
+typedef struct	Ipself	Ipself;
+typedef struct	Ipselftab	Ipselftab;
+typedef struct	Iplink	Iplink;
+typedef struct	Iplifc	Iplifc;
+typedef struct	Ipmulti	Ipmulti;
+typedef struct	IProuter IProuter;
+typedef struct	Ipifc	Ipifc;
+typedef struct	Iphash	Iphash;
+typedef struct	Ipht	Ipht;
+typedef struct	Netlog	Netlog;
+typedef struct	Ifclog	Ifclog;
+typedef struct	Medium	Medium;
+typedef struct	Proto	Proto;
+typedef struct	Arpent	Arpent;
+typedef struct	Arp Arp;
+typedef struct	Route	Route;
+
+typedef struct	Routerparams	Routerparams;
+typedef struct 	Hostparams	Hostparams;
+typedef struct 	V6router	V6router;
+typedef struct	V6params	V6params;
+
+#pragma incomplete Arp
+#pragma	incomplete Ifclog
+#pragma incomplete Ipself
+#pragma incomplete Ipselftab
+#pragma incomplete IP
+#pragma incomplete Netlog
+
+enum
+{
+	Addrlen=	64,
+	Maxproto=	20,
+	Nhash=		64,
+	Maxincall=	5,
+	Nchans=		256,
+	MAClen=		16,		/* longest mac address */
+
+	MAXTTL=		255,
+	DFLTTOS=	0,
+
+	IPaddrlen=	16,
+	IPv4addrlen=	4,
+	IPv4off=	12,
+	IPllen=		4,
+
+	/* ip versions */
+	V4=		4,
+	V6=		6,
+	IP_VER4= 	0x40,
+	IP_VER6=	0x60,
+
+	/* 2^Lroot trees in the root table */
+	Lroot=		10,
+
+	Maxpath =	64,
+};
+
+enum
+{
+	Idle=		0,
+	Announcing=	1,
+	Announced=	2,
+	Connecting=	3,
+	Connected=	4,
+};
+
+/*
+ *  one per conversation directory
+ */
+struct Conv
+{
+	QLock;
+
+	int	x;			/* conversation index */
+	Proto*	p;
+
+	int	restricted;		/* remote port is restricted */
+	uint	ttl;			/* max time to live */
+	uint	tos;			/* type of service */
+	int	ignoreadvice;		/* don't terminate connection on icmp errors */
+
+	uchar	ipversion;
+	uchar	laddr[IPaddrlen];	/* local IP address */
+	uchar	raddr[IPaddrlen];	/* remote IP address */
+	ushort	lport;			/* local port number */
+	ushort	rport;			/* remote port number */
+
+	char	*owner;			/* protections */
+	int	perm;
+	int	inuse;			/* opens of listen/data/ctl */
+	int	length;
+	int	state;
+
+	/* udp specific */
+	int	headers;		/* data src/dst headers in udp */
+	int	reliable;		/* true if reliable udp */
+
+	Conv*	incall;			/* calls waiting to be listened for */
+	Conv*	next;
+
+	Queue*	rq;			/* queued data waiting to be read */
+	Queue*	wq;			/* queued data waiting to be written */
+	Queue*	eq;			/* returned error packets */
+	Queue*	sq;			/* snooping queue */
+	Ref	snoopers;		/* number of processes with snoop open */
+
+	Rendez	cr;
+	char	cerr[ERRMAX];
+
+	QLock	listenq;
+	Rendez	listenr;
+
+	Ipmulti	*multi;			/* multicast bindings for this interface */
+
+	void*	ptcl;			/* protocol specific stuff */
+
+	Route	*r;			/* last route used */
+	ulong	rgen;			/* routetable generation for *r */
+};
+
+struct Medium
+{
+	char	*name;
+	int	hsize;		/* medium header size */
+	int	mintu;		/* default min mtu */
+	int	maxtu;		/* default max mtu */
+	int	maclen;		/* mac address length  */
+	void	(*bind)(Ipifc*, int, char**);
+	void	(*unbind)(Ipifc*);
+	void	(*bwrite)(Ipifc *ifc, Block *b, int version, uchar *ip);
+
+	/* for arming interfaces to receive multicast */
+	void	(*addmulti)(Ipifc *ifc, uchar *a, uchar *ia);
+	void	(*remmulti)(Ipifc *ifc, uchar *a, uchar *ia);
+
+	/* process packets written to 'data' */
+	void	(*pktin)(Fs *f, Ipifc *ifc, Block *bp);
+
+	/* routes for router boards */
+	void	(*addroute)(Ipifc *ifc, int, uchar*, uchar*, uchar*, int);
+	void	(*remroute)(Ipifc *ifc, int, uchar*, uchar*);
+	void	(*flushroutes)(Ipifc *ifc);
+
+	/* for routing multicast groups */
+	void	(*joinmulti)(Ipifc *ifc, uchar *a, uchar *ia);
+	void	(*leavemulti)(Ipifc *ifc, uchar *a, uchar *ia);
+
+	/* address resolution */
+	void	(*ares)(Fs*, int, uchar*, uchar*, int, int);	/* resolve */
+	void	(*areg)(Ipifc*, uchar*);			/* register */
+
+	/* v6 address generation */
+	void	(*pref2addr)(uchar *pref, uchar *ea);
+
+	int	unbindonclose;	/* if non-zero, unbind on last close */
+};
+
+/* logical interface associated with a physical one */
+struct Iplifc
+{
+	uchar	local[IPaddrlen];
+	uchar	mask[IPaddrlen];
+	uchar	remote[IPaddrlen];
+	uchar	net[IPaddrlen];
+	uchar	tentative;	/* =1 => v6 dup disc on, =0 => confirmed unique */
+	uchar	onlink;		/* =1 => onlink, =0 offlink. */
+	uchar	autoflag;	/* v6 autonomous flag */
+	long 	validlt;	/* v6 valid lifetime */
+	long 	preflt;		/* v6 preferred lifetime */
+	long	origint;	/* time when addr was added */
+	Iplink	*link;		/* addresses linked to this lifc */
+	Iplifc	*next;
+};
+
+/* binding twixt Ipself and Iplifc */
+struct Iplink
+{
+	Ipself	*self;
+	Iplifc	*lifc;
+	Iplink	*selflink;	/* next link for this local address */
+	Iplink	*lifclink;	/* next link for this ifc */
+	ulong	expire;
+	Iplink	*next;		/* free list */
+	int	ref;
+};
+
+/* rfc 2461, pp.40--43. */
+
+/* default values, one per stack */
+struct Routerparams {
+	int	mflag;
+	int	oflag;
+	int 	maxraint;
+	int	minraint;
+	int	linkmtu;
+	int	reachtime;
+	int	rxmitra;
+	int	ttl;
+	int	routerlt;	
+};
+
+struct Hostparams {
+	int	rxmithost;
+};
+
+struct Ipifc
+{
+	RWlock;
+	
+	Conv	*conv;		/* link to its conversation structure */
+	char	dev[64];	/* device we're attached to */
+	Medium	*m;		/* Media pointer */
+	int	maxtu;		/* Maximum transfer unit */
+	int	mintu;		/* Minumum tranfer unit */
+	int	mbps;		/* megabits per second */
+	void	*arg;		/* medium specific */
+	int	reassemble;	/* reassemble IP packets before forwarding */
+
+	/* these are used so that we can unbind on the fly */
+	Lock	idlock;
+	uchar	ifcid;		/* incremented each 'bind/unbind/add/remove' */
+	int	ref;		/* number of proc's using this ipifc */
+	Rendez	wait;		/* where unbinder waits for ref == 0 */
+	int	unbinding;
+
+	uchar	mac[MAClen];	/* MAC address */
+
+	Iplifc	*lifc;		/* logical interfaces on this physical one */
+
+	ulong	in, out;	/* message statistics */
+	ulong	inerr, outerr;	/* ... */
+
+	uchar	sendra6;	/* == 1 => send router advs on this ifc	*/
+	uchar	recvra6;	/* == 1 => recv router advs on this ifc */
+	Routerparams rp;	/* router parameters as in RFC 2461, pp.40--43. 
+					used only if node is router */
+};
+
+/*
+ *  one per multicast-lifc pair used by a Conv
+ */
+struct Ipmulti
+{
+	uchar	ma[IPaddrlen];
+	uchar	ia[IPaddrlen];
+	Ipmulti	*next;
+};
+
+/*
+ *  hash table for 2 ip addresses + 2 ports
+ */
+enum
+{
+	Nipht=		521,	/* convenient prime */
+
+	IPmatchexact=	0,	/* match on 4 tuple */
+	IPmatchany,		/* *!* */
+	IPmatchport,		/* *!port */
+	IPmatchaddr,		/* addr!* */
+	IPmatchpa,		/* addr!port */
+};
+struct Iphash
+{
+	Iphash	*next;
+	Conv	*c;
+	int	match;
+};
+struct Ipht
+{
+	Lock;
+	Iphash	*tab[Nipht];
+};
+void iphtadd(Ipht*, Conv*);
+void iphtrem(Ipht*, Conv*);
+Conv* iphtlook(Ipht *ht, uchar *sa, ushort sp, uchar *da, ushort dp);
+
+/*
+ *  one per multiplexed protocol
+ */
+struct Proto
+{
+	QLock;
+	char*		name;		/* protocol name */
+	int		x;		/* protocol index */
+	int		ipproto;	/* ip protocol type */
+
+	char*		(*connect)(Conv*, char**, int);
+	char*		(*announce)(Conv*, char**, int);
+	char*		(*bind)(Conv*, char**, int);
+	int		(*state)(Conv*, char*, int);
+	void		(*create)(Conv*);
+	void		(*close)(Conv*);
+	void		(*rcv)(Proto*, Ipifc*, Block*);
+	char*		(*ctl)(Conv*, char**, int);
+	void		(*advise)(Proto*, Block*, char*);
+	int		(*stats)(Proto*, char*, int);
+	int		(*local)(Conv*, char*, int);
+	int		(*remote)(Conv*, char*, int);
+	int		(*inuse)(Conv*);
+	int		(*gc)(Proto*);	/* returns true if any conversations are freed */
+
+	Fs		*f;		/* file system this proto is part of */
+	Conv		**conv;		/* array of conversations */
+	int		ptclsize;	/* size of per protocol ctl block */
+	int		nc;		/* number of conversations */
+	int		ac;
+	Qid		qid;		/* qid for protocol directory */
+	ushort		nextport;
+	ushort		nextrport;
+
+	void		*priv;
+};
+
+/*
+ *  Stream for sending packets to user level
+ */
+struct IProuter {
+	QLock;
+	int	opens;
+	Queue	*q;
+};
+
+/*
+ *  one per IP protocol stack
+ */
+struct Fs
+{
+	RWlock;
+	int	dev;
+
+	int	np;
+	Proto*	p[Maxproto+1];		/* list of supported protocols */
+	Proto*	t2p[256];		/* vector of all protocols */
+	Proto*	ipifc;			/* kludge for ipifcremroute & ipifcaddroute */
+	Proto*	ipmux;			/* kludge for finding an ip multiplexor */
+
+	IP	*ip;
+	Ipselftab	*self;
+	Arp	*arp;
+	V6params	*v6p;
+	IProuter iprouter;
+
+	Route	*v4root[1<<Lroot];	/* v4 routing forest */
+	Route	*v6root[1<<Lroot];	/* v6 routing forest */
+	Route	*queue;			/* used as temp when reinjecting routes */
+
+	Netlog	*alog;
+	Ifclog	*ilog;
+
+	char	ndb[1024];		/* an ndb entry for this interface */
+	int	ndbvers;
+	long	ndbmtime;
+};
+
+/* one per default router known to host */
+struct V6router {
+	uchar	inuse;
+	Ipifc	*ifc;
+	int	ifcid;
+	uchar	routeraddr[IPaddrlen];
+	long	ltorigin;
+	Routerparams	rp;
+};
+
+struct V6params
+{
+	Routerparams	rp;		/* v6 params, one copy per node now */
+	Hostparams	hp;
+	V6router	v6rlist[3];	/* max 3 default routers, currently */
+	int		cdrouter;	/* uses only v6rlist[cdrouter] if   */ 
+					/* cdrouter >= 0. */
+};
+
+
+int	Fsconnected(Conv*, char*);
+Conv*	Fsnewcall(Conv*, uchar*, ushort, uchar*, ushort, uchar);
+int	Fspcolstats(char*, int);
+int	Fsproto(Fs*, Proto*);
+int	Fsbuiltinproto(Fs*, uchar);
+Conv*	Fsprotoclone(Proto*, char*);
+Proto*	Fsrcvpcol(Fs*, uchar);
+Proto*	Fsrcvpcolx(Fs*, uchar);
+char*	Fsstdconnect(Conv*, char**, int);
+char*	Fsstdannounce(Conv*, char**, int);
+char*	Fsstdbind(Conv*, char**, int);
+ulong	scalednconv(void);
+
+/* 
+ *  logging
+ */
+enum
+{
+	Logip=		1<<1,
+	Logtcp=		1<<2,
+	Logfs=		1<<3,
+	Logil=		1<<4,
+	Logicmp=	1<<5,
+	Logudp=		1<<6,
+	Logcompress=	1<<7,
+	Logilmsg=	1<<8,
+	Loggre=		1<<9,
+	Logppp=		1<<10,
+	Logtcprxmt=	1<<11,
+	Logigmp=	1<<12,
+	Logudpmsg=	1<<13,
+	Logipmsg=	1<<14,
+	Logrudp=	1<<15,
+	Logrudpmsg=	1<<16,
+	Logesp=		1<<17,
+	Logtcpwin=	1<<18,
+};
+
+void	netloginit(Fs*);
+void	netlogopen(Fs*);
+void	netlogclose(Fs*);
+void	netlogctl(Fs*, char*, int);
+long	netlogread(Fs*, void*, ulong, long);
+void	netlog(Fs*, int, char*, ...);
+void	ifcloginit(Fs*);
+long	ifclogread(Fs*, Chan *,void*, ulong, long);
+void	ifclog(Fs*, uchar *, int);
+void	ifclogopen(Fs*, Chan*);
+void	ifclogclose(Fs*, Chan*);
+
+/*
+ *  iproute.c
+ */
+typedef	struct RouteTree RouteTree;
+typedef struct Routewalk Routewalk;
+typedef struct V4route V4route;
+typedef struct V6route V6route;
+
+enum
+{
+
+	/* type bits */
+	Rv4=		(1<<0),		/* this is a version 4 route */
+	Rifc=		(1<<1),		/* this route is a directly connected interface */
+	Rptpt=		(1<<2),		/* this route is a pt to pt interface */
+	Runi=		(1<<3),		/* a unicast self address */
+	Rbcast=		(1<<4),		/* a broadcast self address */
+	Rmulti=		(1<<5),		/* a multicast self address */
+	Rproxy=		(1<<6),		/* this route should be proxied */
+};
+
+struct Routewalk
+{
+	int	o;
+	int	h;
+	char*	p;
+	char*	e;
+	void*	state;
+	void	(*walk)(Route*, Routewalk*);
+};
+
+struct	RouteTree
+{
+	Route*	right;
+	Route*	left;
+	Route*	mid;
+	uchar	depth;
+	uchar	type;
+	uchar	ifcid;		/* must match ifc->id */
+	Ipifc	*ifc;
+	char	tag[4];
+	int	ref;
+};
+
+struct V4route
+{
+	ulong	address;
+	ulong	endaddress;
+	uchar	gate[IPv4addrlen];
+};
+
+struct V6route
+{
+	ulong	address[IPllen];
+	ulong	endaddress[IPllen];
+	uchar	gate[IPaddrlen];
+};
+
+struct Route
+{
+	RouteTree;
+
+	union {
+		V6route	v6;
+		V4route v4;
+	};
+};
+extern void	v4addroute(Fs *f, char *tag, uchar *a, uchar *mask, uchar *gate, int type);
+extern void	v6addroute(Fs *f, char *tag, uchar *a, uchar *mask, uchar *gate, int type);
+extern void	v4delroute(Fs *f, uchar *a, uchar *mask, int dolock);
+extern void	v6delroute(Fs *f, uchar *a, uchar *mask, int dolock);
+extern Route*	v4lookup(Fs *f, uchar *a, Conv *c);
+extern Route*	v6lookup(Fs *f, uchar *a, Conv *c);
+extern long	routeread(Fs *f, char*, ulong, int);
+extern long	routewrite(Fs *f, Chan*, char*, int);
+extern void	routetype(int, char*);
+extern void	ipwalkroutes(Fs*, Routewalk*);
+extern void	convroute(Route*, uchar*, uchar*, uchar*, char*, int*);
+
+/*
+ *  devip.c
+ */
+
+/*
+ *  Hanging off every ip channel's ->aux is the following structure.
+ *  It maintains the state used by devip and iproute.
+ */
+struct IPaux
+{
+	char	*owner;		/* the user that did the attach */
+	char	tag[4];
+};
+
+extern IPaux*	newipaux(char*, char*);
+
+/*
+ *  arp.c
+ */
+struct Arpent
+{
+	uchar	ip[IPaddrlen];
+	uchar	mac[MAClen];
+	Medium	*type;			/* media type */
+	Arpent*	hash;
+	Block*	hold;
+	Block*	last;
+	uint	ctime;			/* time entry was created or refreshed */
+	uint	utime;			/* time entry was last used */
+	uchar	state;
+	Arpent	*nextrxt;		/* re-transmit chain */
+	uint	rtime;			/* time for next retransmission */
+	uchar	rxtsrem;
+	Ipifc	*ifc;
+	uchar	ifcid;			/* must match ifc->id */
+};
+
+extern void	arpinit(Fs*);
+extern int	arpread(Arp*, char*, ulong, int);
+extern int	arpwrite(Fs*, char*, int);
+extern Arpent*	arpget(Arp*, Block *bp, int version, Ipifc *ifc, uchar *ip, uchar *h);
+extern void	arprelease(Arp*, Arpent *a);
+extern Block*	arpresolve(Arp*, Arpent *a, Medium *type, uchar *mac);
+extern void	arpenter(Fs*, int version, uchar *ip, uchar *mac, int len, int norefresh);
+
+/*
+ * ipaux.c
+ */
+
+extern int	myetheraddr(uchar*, char*);
+extern ulong	parseip(uchar*, char*);
+extern ulong	parseipmask(uchar*, char*);
+extern char*	v4parseip(uchar*, char*);
+extern void	maskip(uchar *from, uchar *mask, uchar *to);
+extern int	parsemac(uchar *to, char *from, int len);
+extern uchar*	defmask(uchar*);
+extern int	isv4(uchar*);
+extern void	v4tov6(uchar *v6, uchar *v4);
+extern int	v6tov4(uchar *v4, uchar *v6);
+extern int	eipfmt(Fmt*);
+
+#define	ipmove(x, y) memmove(x, y, IPaddrlen)
+#define	ipcmp(x, y) ( (x)[IPaddrlen-1] != (y)[IPaddrlen-1] || memcmp(x, y, IPaddrlen) )
+
+extern uchar IPv4bcast[IPaddrlen];
+extern uchar IPv4bcastobs[IPaddrlen];
+extern uchar IPv4allsys[IPaddrlen];
+extern uchar IPv4allrouter[IPaddrlen];
+extern uchar IPnoaddr[IPaddrlen];
+extern uchar v4prefix[IPaddrlen];
+extern uchar IPallbits[IPaddrlen];
+
+#define	NOW	TK2MS(MACHP(0)->ticks)
+
+/*
+ *  media
+ */
+extern Medium	ethermedium;
+extern Medium	nullmedium;
+extern Medium	pktmedium;
+extern Medium	tripmedium;
+
+/*
+ *  ipifc.c
+ */
+extern Medium*	ipfindmedium(char *name);
+extern void	addipmedium(Medium *med);
+extern int	ipforme(Fs*, uchar *addr);
+extern int	iptentative(Fs*, uchar *addr);
+extern int	ipisbm(uchar *);
+extern int	ipismulticast(uchar *);
+extern Ipifc*	findipifc(Fs*, uchar *remote, int type);
+extern void	findprimaryip(Fs*, uchar*);
+extern void	findlocalip(Fs*, uchar *local, uchar *remote);
+extern int	ipv4local(Ipifc *ifc, uchar *addr);
+extern int	ipv6local(Ipifc *ifc, uchar *addr);
+extern int	ipv6anylocal(Ipifc *ifc, uchar *addr);
+extern Iplifc*	iplocalonifc(Ipifc *ifc, uchar *ip);
+extern int	ipproxyifc(Fs *f, Ipifc *ifc, uchar *ip);
+extern int	ipismulticast(uchar *ip);
+extern int	ipisbooting(void);
+extern int	ipifccheckin(Ipifc *ifc, Medium *med);
+extern void	ipifccheckout(Ipifc *ifc);
+extern int	ipifcgrab(Ipifc *ifc);
+extern void	ipifcaddroute(Fs*, int, uchar*, uchar*, uchar*, int);
+extern void	ipifcremroute(Fs*, int, uchar*, uchar*);
+extern void	ipifcremmulti(Conv *c, uchar *ma, uchar *ia);
+extern void	ipifcaddmulti(Conv *c, uchar *ma, uchar *ia);
+extern char*	ipifcrem(Ipifc *ifc, char **argv, int argc);
+extern char*	ipifcadd(Ipifc *ifc, char **argv, int argc, int tentative, Iplifc *lifcp);
+extern long	ipselftabread(Fs*, char *a, ulong offset, int n);
+extern char*	ipifcaddpref6(Ipifc *ifc, char**argv, int argc);
+extern void	ipsendra6(Fs *f, int on);
+
+/*
+ *  ip.c
+ */
+extern void	iprouting(Fs*, int);
+extern void	icmpnoconv(Fs*, Block*);
+extern void	icmpcantfrag(Fs*, Block*, int);
+extern void	icmpttlexceeded(Fs*, uchar*, Block*);
+extern ushort	ipcsum(uchar*);
+extern void	ipiput4(Fs*, Ipifc*, Block*);
+extern void	ipiput6(Fs*, Ipifc*, Block*);
+extern int	ipoput4(Fs*, Block*, int, int, int, Conv*);
+extern int	ipoput6(Fs*, Block*, int, int, int, Conv*);
+extern int	ipstats(Fs*, char*, int);
+extern ushort	ptclbsum(uchar*, int);
+extern ushort	ptclcsum(Block*, int, int);
+extern void	ip_init(Fs*);
+extern void	update_mtucache(uchar*, ulong);
+extern ulong	restrict_mtu(uchar*, ulong);
+
+/*
+ * bootp.c
+ */
+char*	(*bootp)(Ipifc*);
+int	(*bootpread)(char*, ulong, int);
+
+/*
+ *  iprouter.c
+ */
+void	useriprouter(Fs*, Ipifc*, Block*);
+void	iprouteropen(Fs*);
+void	iprouterclose(Fs*);
+long	iprouterread(Fs*, void*, int);
+
+/*
+ *  resolving inferno/plan9 differences
+ */
+Chan*		commonfdtochan(int, int, int, int);
+char*		commonuser(void);
+char*		commonerror(void);
+
+/*
+ * chandial.c
+ */
+extern Chan*	chandial(char*, char*, char*, Chan**);
+
+/*
+ *  global to all of the stack
+ */
+extern void	(*igmpreportfn)(Ipifc*, uchar*);
--- /dev/null
+++ b/os/ip/ipaux.c
@@ -1,0 +1,729 @@
+#include	"u.h"
+#include	"../port/lib.h"
+#include	"mem.h"
+#include	"dat.h"
+#include	"fns.h"
+#include	"../port/error.h"
+#include	"ip.h"
+#include  "ipv6.h"
+
+/*
+ *  well known IP addresses
+ */
+uchar IPv4bcast[IPaddrlen] = {
+	0, 0, 0, 0,
+	0, 0, 0, 0,
+	0, 0, 0xff, 0xff,
+	0xff, 0xff, 0xff, 0xff
+};
+uchar IPv4allsys[IPaddrlen] = {
+	0, 0, 0, 0,
+	0, 0, 0, 0,
+	0, 0, 0xff, 0xff,
+	0xe0, 0, 0, 0x01
+};
+uchar IPv4allrouter[IPaddrlen] = {
+	0, 0, 0, 0,
+	0, 0, 0, 0,
+	0, 0, 0xff, 0xff,
+	0xe0, 0, 0, 0x02
+};
+uchar IPallbits[IPaddrlen] = {
+	0xff, 0xff, 0xff, 0xff,
+	0xff, 0xff, 0xff, 0xff,
+	0xff, 0xff, 0xff, 0xff,
+	0xff, 0xff, 0xff, 0xff
+};
+
+uchar IPnoaddr[IPaddrlen];
+
+/*
+ *  prefix of all v4 addresses
+ */
+uchar v4prefix[IPaddrlen] = {
+	0, 0, 0, 0,
+	0, 0, 0, 0,
+	0, 0, 0xff, 0xff,
+	0, 0, 0, 0
+};
+
+
+char *v6hdrtypes[Maxhdrtype] =
+{
+	[HBH]		"HopbyHop",
+	[ICMP]		"ICMP",
+	[IGMP]		"IGMP",
+	[GGP]		"GGP",
+	[IPINIP]		"IP",
+	[ST]		"ST",
+	[TCP]		"TCP",
+	[UDP]		"UDP",
+	[ISO_TP4]	"ISO_TP4",
+	[RH]		"Routinghdr",
+	[FH]		"Fraghdr",
+	[IDRP]		"IDRP",
+	[RSVP]		"RSVP",
+	[AH]		"Authhdr",
+	[ESP]		"ESP",
+	[ICMPv6]	"ICMPv6",
+	[NNH]		"Nonexthdr",
+	[ISO_IP]	"ISO_IP",
+	[IGRP]		"IGRP",
+	[OSPF]		"OSPF",
+};
+
+/*
+ *  well known IPv6 addresses
+ */
+uchar v6Unspecified[IPaddrlen] = {
+	0, 0, 0, 0,
+	0, 0, 0, 0,
+	0, 0, 0, 0,
+	0, 0, 0, 0
+};
+uchar v6loopback[IPaddrlen] = {
+	0, 0, 0, 0,
+	0, 0, 0, 0,
+	0, 0, 0, 0,
+	0, 0, 0, 0x01
+};
+uchar v6linklocal[IPaddrlen] = {
+	0xfe, 0x80, 0, 0,
+	0, 0, 0, 0,
+	0, 0, 0, 0,
+	0, 0, 0, 0
+};
+uchar v6linklocalmask[IPaddrlen] = {
+	0xff, 0xff, 0xff, 0xff,
+	0xff, 0xff, 0xff, 0xff,
+	0, 0, 0, 0,
+	0, 0, 0, 0
+};
+int v6llpreflen = 8;	// link-local prefix length
+uchar v6sitelocal[IPaddrlen] = {
+	0xfe, 0xc0, 0, 0,
+	0, 0, 0, 0,
+	0, 0, 0, 0,
+	0, 0, 0, 0
+};
+uchar v6sitelocalmask[IPaddrlen] = {
+	0xff, 0xff, 0xff, 0xff,
+	0xff, 0xff, 0xff, 0xff,
+	0, 0, 0, 0,
+	0, 0, 0, 0
+};
+int v6slpreflen = 6;	// site-local prefix length
+uchar v6glunicast[IPaddrlen] = {
+	0x08, 0, 0, 0,
+	0, 0, 0, 0,
+	0, 0, 0, 0,
+	0, 0, 0, 0
+};
+uchar v6multicast[IPaddrlen] = {
+	0xff, 0, 0, 0,
+	0, 0, 0, 0,
+	0, 0, 0, 0,
+	0, 0, 0, 0
+};
+uchar v6multicastmask[IPaddrlen] = {
+	0xff, 0, 0, 0,
+	0, 0, 0, 0,
+	0, 0, 0, 0,
+	0, 0, 0, 0
+};
+int v6mcpreflen = 1;	// multicast prefix length
+uchar v6allnodesN[IPaddrlen] = {
+	0xff, 0x01, 0, 0,
+	0, 0, 0, 0,
+	0, 0, 0, 0,
+	0, 0, 0, 0x01
+};
+uchar v6allnodesNmask[IPaddrlen] = {
+	0xff, 0xff, 0, 0,
+	0, 0, 0, 0,
+	0, 0, 0, 0,
+	0, 0, 0, 0
+};
+int v6aNpreflen = 2;	// all nodes (N) prefix
+uchar v6allnodesL[IPaddrlen] = {
+	0xff, 0x02, 0, 0,
+	0, 0, 0, 0,
+	0, 0, 0, 0,
+	0, 0, 0, 0x01
+};
+uchar v6allnodesLmask[IPaddrlen] = {
+	0xff, 0xff, 0, 0,
+	0, 0, 0, 0,
+	0, 0, 0, 0,
+	0, 0, 0, 0
+};
+int v6aLpreflen = 2;	// all nodes (L) prefix
+uchar v6allroutersN[IPaddrlen] = {
+	0xff, 0x01, 0, 0,
+	0, 0, 0, 0,
+	0, 0, 0, 0,
+	0, 0, 0, 0x02
+};
+uchar v6allroutersL[IPaddrlen] = {
+	0xff, 0x02, 0, 0,
+	0, 0, 0, 0,
+	0, 0, 0, 0,
+	0, 0, 0, 0x02
+};
+uchar v6allroutersS[IPaddrlen] = {
+	0xff, 0x05, 0, 0,
+	0, 0, 0, 0,
+	0, 0, 0, 0,
+	0, 0, 0, 0x02
+};
+uchar v6solicitednode[IPaddrlen] = {
+	0xff, 0x02, 0, 0,
+	0, 0, 0, 0,
+	0, 0, 0, 0x01,
+	0xff, 0, 0, 0
+};
+uchar v6solicitednodemask[IPaddrlen] = {
+	0xff, 0xff, 0xff, 0xff,
+	0xff, 0xff, 0xff, 0xff,
+	0xff, 0xff, 0xff, 0xff,
+	0xff, 0x0, 0x0, 0x0
+};
+int v6snpreflen = 13;
+
+
+
+
+ushort
+ptclcsum(Block *bp, int offset, int len)
+{
+	uchar *addr;
+	ulong losum, hisum;
+	ushort csum;
+	int odd, blocklen, x;
+
+	/* Correct to front of data area */
+	while(bp != nil && offset && offset >= BLEN(bp)) {
+		offset -= BLEN(bp);
+		bp = bp->next;
+	}
+	if(bp == nil)
+		return 0;
+
+	addr = bp->rp + offset;
+	blocklen = BLEN(bp) - offset;
+
+	if(bp->next == nil) {
+		if(blocklen < len)
+			len = blocklen;
+		return ~ptclbsum(addr, len) & 0xffff;
+	}
+
+	losum = 0;
+	hisum = 0;
+
+	odd = 0;
+	while(len) {
+		x = blocklen;
+		if(len < x)
+			x = len;
+
+		csum = ptclbsum(addr, x);
+		if(odd)
+			hisum += csum;
+		else
+			losum += csum;
+		odd = (odd+x) & 1;
+		len -= x;
+
+		bp = bp->next;
+		if(bp == nil)
+			break;
+		blocklen = BLEN(bp);
+		addr = bp->rp;
+	}
+
+	losum += hisum>>8;
+	losum += (hisum&0xff)<<8;
+	while((csum = losum>>16) != 0)
+		losum = csum + (losum & 0xffff);
+
+	return ~losum & 0xffff;
+}
+
+enum
+{
+	Isprefix= 16,
+};
+
+static uchar prefixvals[256] =
+{
+[0x00] 0 | Isprefix,
+[0x80] 1 | Isprefix,
+[0xC0] 2 | Isprefix,
+[0xE0] 3 | Isprefix,
+[0xF0] 4 | Isprefix,
+[0xF8] 5 | Isprefix,
+[0xFC] 6 | Isprefix,
+[0xFE] 7 | Isprefix,
+[0xFF] 8 | Isprefix,
+};
+
+int
+eipfmt(Fmt *f)
+{
+	char buf[5*8];
+	static char *efmt = "%.2lux%.2lux%.2lux%.2lux%.2lux%.2lux";
+	static char *ifmt = "%d.%d.%d.%d";
+	uchar *p, ip[16];
+	ulong *lp;
+	ushort s;
+	int i, j, n, eln, eli;
+
+	switch(f->r) {
+	case 'E':		/* Ethernet address */
+		p = va_arg(f->args, uchar*);
+		return fmtprint(f, efmt, p[0], p[1], p[2], p[3], p[4], p[5]);
+
+	case 'I':		/* Ip address */
+		p = va_arg(f->args, uchar*);
+common:
+		if(memcmp(p, v4prefix, 12) == 0)
+			return fmtprint(f, ifmt, p[12], p[13], p[14], p[15]);
+
+		/* find longest elision */
+		eln = eli = -1;
+		for(i = 0; i < 16; i += 2){
+			for(j = i; j < 16; j += 2)
+				if(p[j] != 0 || p[j+1] != 0)
+					break;
+			if(j > i && j - i > eln){
+				eli = i;
+				eln = j - i;
+			}
+		}
+
+		/* print with possible elision */
+		n = 0;
+		for(i = 0; i < 16; i += 2){
+			if(i == eli){
+				n += sprint(buf+n, "::");
+				i += eln;
+				if(i >= 16)
+					break;
+			} else if(i != 0)
+				n += sprint(buf+n, ":");
+			s = (p[i]<<8) + p[i+1];
+			n += sprint(buf+n, "%ux", s);
+		}
+		return fmtstrcpy(f, buf);
+
+	case 'i':		/* v6 address as 4 longs */
+		lp = va_arg(f->args, ulong*);
+		for(i = 0; i < 4; i++)
+			hnputl(ip+4*i, *lp++);
+		p = ip;
+		goto common;
+
+	case 'V':		/* v4 ip address */
+		p = va_arg(f->args, uchar*);
+		return fmtprint(f, ifmt, p[0], p[1], p[2], p[3]);
+
+	case 'M':		/* ip mask */
+		p = va_arg(f->args, uchar*);
+
+		/* look for a prefix mask */
+		for(i = 0; i < 16; i++)
+			if(p[i] != 0xff)
+				break;
+		if(i < 16){
+			if((prefixvals[p[i]] & Isprefix) == 0)
+				goto common;
+			for(j = i+1; j < 16; j++)
+				if(p[j] != 0)
+					goto common;
+			n = 8*i + (prefixvals[p[i]] & ~Isprefix);
+		} else
+			n = 8*16;
+
+		/* got one, use /xx format */
+		return fmtprint(f, "/%d", n);
+	}
+	return fmtstrcpy(f, "(eipfmt)");
+}
+
+#define CLASS(p) ((*(uchar*)(p))>>6)
+
+extern char*
+v4parseip(uchar *to, char *from)
+{
+	int i;
+	char *p;
+
+	p = from;
+	for(i = 0; i < 4 && *p; i++){
+		to[i] = strtoul(p, &p, 0);
+		if(*p == '.')
+			p++;
+	}
+	switch(CLASS(to)){
+	case 0:	/* class A - 1 uchar net */
+	case 1:
+		if(i == 3){
+			to[3] = to[2];
+			to[2] = to[1];
+			to[1] = 0;
+		} else if(i == 2){
+			to[3] = to[1];
+			to[1] = 0;
+		}
+		break;
+	case 2:	/* class B - 2 uchar net */
+		if(i == 3){
+			to[3] = to[2];
+			to[2] = 0;
+		}
+		break;
+	}
+	return p;
+}
+
+int
+isv4(uchar *ip)
+{
+	return memcmp(ip, v4prefix, IPv4off) == 0;
+}
+
+
+/*
+ *  the following routines are unrolled with no memset's to speed
+ *  up the usual case
+ */
+void
+v4tov6(uchar *v6, uchar *v4)
+{
+	v6[0] = 0;
+	v6[1] = 0;
+	v6[2] = 0;
+	v6[3] = 0;
+	v6[4] = 0;
+	v6[5] = 0;
+	v6[6] = 0;
+	v6[7] = 0;
+	v6[8] = 0;
+	v6[9] = 0;
+	v6[10] = 0xff;
+	v6[11] = 0xff;
+	v6[12] = v4[0];
+	v6[13] = v4[1];
+	v6[14] = v4[2];
+	v6[15] = v4[3];
+}
+
+int
+v6tov4(uchar *v4, uchar *v6)
+{
+	if(v6[0] == 0
+	&& v6[1] == 0
+	&& v6[2] == 0
+	&& v6[3] == 0
+	&& v6[4] == 0
+	&& v6[5] == 0
+	&& v6[6] == 0
+	&& v6[7] == 0
+	&& v6[8] == 0
+	&& v6[9] == 0
+	&& v6[10] == 0xff
+	&& v6[11] == 0xff)
+	{
+		v4[0] = v6[12];
+		v4[1] = v6[13];
+		v4[2] = v6[14];
+		v4[3] = v6[15];
+		return 0;
+	} else {
+		memset(v4, 0, 4);
+		return -1;
+	}
+}
+
+ulong
+parseip(uchar *to, char *from)
+{
+	int i, elipsis = 0, v4 = 1;
+	ulong x;
+	char *p, *op;
+
+	memset(to, 0, IPaddrlen);
+	p = from;
+	for(i = 0; i < 16 && *p; i+=2){
+		op = p;
+		x = strtoul(p, &p, 16);
+		if(*p == '.' || (*p == 0 && i == 0)){
+			p = v4parseip(to+i, op);
+			i += 4;
+			break;
+		} else {
+			to[i] = x>>8;
+			to[i+1] = x;
+		}
+		if(*p == ':'){
+			v4 = 0;
+			if(*++p == ':'){
+				elipsis = i+2;
+				p++;
+			}
+		}
+	}
+	if(i < 16){
+		memmove(&to[elipsis+16-i], &to[elipsis], i-elipsis);
+		memset(&to[elipsis], 0, 16-i);
+	}
+	if(v4){
+		to[10] = to[11] = 0xff;
+		return nhgetl(to+12);
+	} else
+		return 6;
+}
+
+/*
+ *  hack to allow ip v4 masks to be entered in the old
+ *  style
+ */
+ulong
+parseipmask(uchar *to, char *from)
+{
+	ulong x;
+	int i;
+	uchar *p;
+
+	if(*from == '/'){
+		/* as a number of prefix bits */
+		i = atoi(from+1);
+		if(i < 0)
+			i = 0;
+		if(i > 128)
+			i = 128;
+		memset(to, 0, IPaddrlen);
+		for(p = to; i >= 8; i -= 8)
+			*p++ = 0xff;
+		if(i > 0)
+			*p = ~((1<<(8-i))-1);
+		x = nhgetl(to+IPv4off);
+	} else {
+		/* as a straight bit mask */
+		x = parseip(to, from);
+		if(memcmp(to, v4prefix, IPv4off) == 0)
+			memset(to, 0xff, IPv4off);
+	}
+	return x;
+}
+
+void
+maskip(uchar *from, uchar *mask, uchar *to)
+{
+	int i;
+
+	for(i = 0; i < IPaddrlen; i++)
+		to[i] = from[i] & mask[i];
+}
+
+uchar classmask[4][16] = {
+	0xff,0xff,0xff,0xff,  0xff,0xff,0xff,0xff,  0xff,0xff,0xff,0xff,  0xff,0x00,0x00,0x00,
+	0xff,0xff,0xff,0xff,  0xff,0xff,0xff,0xff,  0xff,0xff,0xff,0xff,  0xff,0x00,0x00,0x00,
+	0xff,0xff,0xff,0xff,  0xff,0xff,0xff,0xff,  0xff,0xff,0xff,0xff,  0xff,0xff,0x00,0x00,
+	0xff,0xff,0xff,0xff,  0xff,0xff,0xff,0xff,  0xff,0xff,0xff,0xff,  0xff,0xff,0xff,0x00,
+};
+
+uchar*
+defmask(uchar *ip)
+{
+	if(isv4(ip))
+		return classmask[ip[IPv4off]>>6];
+	else {
+		if(ipcmp(ip, v6loopback) == 0)
+			return IPallbits;
+		else if(memcmp(ip, v6linklocal, v6llpreflen) == 0)
+			return v6linklocalmask;
+		else if(memcmp(ip, v6sitelocal, v6slpreflen) == 0)
+			return v6sitelocalmask;
+		else if(memcmp(ip, v6solicitednode, v6snpreflen) == 0)
+			return v6solicitednodemask;
+		else if(memcmp(ip, v6multicast, v6mcpreflen) == 0)
+			return v6multicastmask;
+		return IPallbits;
+	}
+}
+
+void
+ipv62smcast(uchar *smcast, uchar *a)
+{
+	assert(IPaddrlen == 16);
+	memmove(smcast, v6solicitednode, IPaddrlen);
+	smcast[13] = a[13];
+	smcast[14] = a[14];
+	smcast[15] = a[15];
+}
+
+
+/*
+ *  parse a hex mac address
+ */
+int
+parsemac(uchar *to, char *from, int len)
+{
+	char nip[4];
+	char *p;
+	int i;
+
+	p = from;
+	memset(to, 0, len);
+	for(i = 0; i < len; i++){
+		if(p[0] == '\0' || p[1] == '\0')
+			break;
+
+		nip[0] = p[0];
+		nip[1] = p[1];
+		nip[2] = '\0';
+		p += 2;
+
+		to[i] = strtoul(nip, 0, 16);
+		if(*p == ':')
+			p++;
+	}
+	return i;
+}
+
+/*
+ *  hashing tcp, udp, ... connections
+ */
+ulong
+iphash(uchar *sa, ushort sp, uchar *da, ushort dp)
+{
+	return ((sa[IPaddrlen-1]<<24) ^ (sp << 16) ^ (da[IPaddrlen-1]<<8) ^ dp ) % Nhash;
+}
+
+void
+iphtadd(Ipht *ht, Conv *c)
+{
+	ulong hv;
+	Iphash *h;
+
+	hv = iphash(c->raddr, c->rport, c->laddr, c->lport);
+	h = smalloc(sizeof(*h));
+	if(ipcmp(c->raddr, IPnoaddr) != 0)
+		h->match = IPmatchexact;
+	else {
+		if(ipcmp(c->laddr, IPnoaddr) != 0){
+			if(c->lport == 0)
+				h->match = IPmatchaddr;
+			else
+				h->match = IPmatchpa;
+		} else {
+			if(c->lport == 0)
+				h->match = IPmatchany;
+			else
+				h->match = IPmatchport;
+		}
+	}
+	h->c = c;
+
+	lock(ht);
+	h->next = ht->tab[hv];
+	ht->tab[hv] = h;
+	unlock(ht);
+}
+
+void
+iphtrem(Ipht *ht, Conv *c)
+{
+	ulong hv;
+	Iphash **l, *h;
+
+	hv = iphash(c->raddr, c->rport, c->laddr, c->lport);
+	lock(ht);
+	for(l = &ht->tab[hv]; (*l) != nil; l = &(*l)->next)
+		if((*l)->c == c){
+			h = *l;
+			(*l) = h->next;
+			free(h);
+			break;
+		}
+	unlock(ht);
+}
+
+/* look for a matching conversation with the following precedence
+ *	connected && raddr,rport,laddr,lport
+ *	announced && laddr,lport
+ *	announced && *,lport
+ *	announced && laddr,*
+ *	announced && *,*
+ */
+Conv*
+iphtlook(Ipht *ht, uchar *sa, ushort sp, uchar *da, ushort dp)
+{
+	ulong hv;
+	Iphash *h;
+	Conv *c;
+
+	/* exact 4 pair match (connection) */
+	hv = iphash(sa, sp, da, dp);
+	lock(ht);
+	for(h = ht->tab[hv]; h != nil; h = h->next){
+		if(h->match != IPmatchexact)
+			continue;
+		c = h->c;
+		if(sp == c->rport && dp == c->lport
+		&& ipcmp(sa, c->raddr) == 0 && ipcmp(da, c->laddr) == 0){
+			unlock(ht);
+			return c;
+		}
+	}
+	
+	/* match local address and port */
+	hv = iphash(IPnoaddr, 0, da, dp);
+	for(h = ht->tab[hv]; h != nil; h = h->next){
+		if(h->match != IPmatchpa)
+			continue;
+		c = h->c;
+		if(dp == c->lport && ipcmp(da, c->laddr) == 0){
+			unlock(ht);
+			return c;
+		}
+	}
+	
+	/* match just port */
+	hv = iphash(IPnoaddr, 0, IPnoaddr, dp);
+	for(h = ht->tab[hv]; h != nil; h = h->next){
+		if(h->match != IPmatchport)
+			continue;
+		c = h->c;
+		if(dp == c->lport){
+			unlock(ht);
+			return c;
+		}
+	}
+	
+	/* match local address */
+	hv = iphash(IPnoaddr, 0, da, 0);
+	for(h = ht->tab[hv]; h != nil; h = h->next){
+		if(h->match != IPmatchaddr)
+			continue;
+		c = h->c;
+		if(ipcmp(da, c->laddr) == 0){
+			unlock(ht);
+			return c;
+		}
+	}
+	
+	/* look for something that matches anything */
+	hv = iphash(IPnoaddr, 0, IPnoaddr, 0);
+	for(h = ht->tab[hv]; h != nil; h = h->next){
+		if(h->match != IPmatchany)
+			continue;
+		c = h->c;
+		unlock(ht);
+		return c;
+	}
+	unlock(ht);
+	return nil;
+}
--- /dev/null
+++ b/os/ip/ipifc.c
@@ -1,0 +1,1721 @@
+#include "u.h"
+#include "../port/lib.h"
+#include "mem.h"
+#include "dat.h"
+#include "fns.h"
+#include "../port/error.h"
+
+#include "ip.h"
+#include "ipv6.h"
+
+#define DPRINT if(0)print
+
+enum {
+	Maxmedia = 32,
+	Nself = Maxmedia*5,
+	NHASH = (1<<6),
+	NCACHE = 256,
+	QMAX = 64*1024-1,
+};
+
+Medium *media[Maxmedia] =
+{
+	0
+};
+
+/*
+ *  cache of local addresses (addresses we answer to)
+ */
+struct Ipself
+{
+	uchar	a[IPaddrlen];
+	Ipself	*hnext;		/* next address in the hash table */
+	Iplink	*link;		/* binding twixt Ipself and Ipifc */
+	ulong	expire;
+	uchar	type;		/* type of address */
+	int	ref;
+	Ipself	*next;		/* free list */
+};
+
+struct Ipselftab
+{
+	QLock;
+	int	inited;
+	int	acceptall;	/* true if an interface has the null address */
+	Ipself	*hash[NHASH];	/* hash chains */
+};
+
+/*
+ *  Multicast addresses are chained onto a Chan so that
+ *  we can remove them when the Chan is closed.
+ */
+typedef struct Ipmcast Ipmcast;
+struct Ipmcast
+{
+	Ipmcast	*next;
+	uchar	ma[IPaddrlen];	/* multicast address */
+	uchar	ia[IPaddrlen];	/* interface address */
+};
+
+/* quick hash for ip addresses */
+#define hashipa(a) ( ( ((a)[IPaddrlen-2]<<8) | (a)[IPaddrlen-1] )%NHASH )
+
+static char tifc[] = "ifc ";
+
+static void	addselfcache(Fs *f, Ipifc *ifc, Iplifc *lifc, uchar *a, int type);
+static void	remselfcache(Fs *f, Ipifc *ifc, Iplifc *lifc, uchar *a);
+static char*	ipifcjoinmulti(Ipifc *ifc, char **argv, int argc);
+static char*	ipifcleavemulti(Ipifc *ifc, char **argv, int argc);
+static void	ipifcregisterproxy(Fs*, Ipifc*, uchar*);
+static char*	ipifcremlifc(Ipifc*, Iplifc*);
+
+/*
+ *  link in a new medium
+ */
+void
+addipmedium(Medium *med)
+{
+	int i;
+
+	for(i = 0; i < nelem(media)-1; i++)
+		if(media[i] == nil){
+			media[i] = med;
+			break;
+		}
+}
+
+/*
+ *  find the medium with this name
+ */
+Medium*
+ipfindmedium(char *name)
+{
+	Medium **mp;
+
+	for(mp = media; *mp != nil; mp++)
+		if(strcmp((*mp)->name, name) == 0)
+			break;
+	return *mp;
+}
+
+/*
+ *  attach a device (or pkt driver) to the interface.
+ *  called with c locked
+ */
+static char*
+ipifcbind(Conv *c, char **argv, int argc)
+{
+	Ipifc *ifc;
+	Medium *m;
+
+	if(argc < 2)
+		return Ebadarg;
+
+	ifc = (Ipifc*)c->ptcl;
+
+	/* bind the device to the interface */
+	m = ipfindmedium(argv[1]);
+	if(m == nil)
+		return "unknown interface type";
+
+	wlock(ifc);
+	if(ifc->m != nil){
+		wunlock(ifc);
+		return "interface already bound";	
+	}
+	if(waserror()){
+		wunlock(ifc);
+		nexterror();
+	}
+
+	/* do medium specific binding */
+	(*m->bind)(ifc, argc, argv);
+
+	/* set the bound device name */
+	if(argc > 2)
+		strncpy(ifc->dev, argv[2], sizeof(ifc->dev));
+	else
+		snprint(ifc->dev, sizeof ifc->dev, "%s%d", m->name, c->x);
+	ifc->dev[sizeof(ifc->dev)-1] = 0;
+
+	/* set up parameters */
+	ifc->m = m;
+	ifc->mintu = ifc->m->mintu;
+	ifc->maxtu = ifc->m->maxtu;
+	if(ifc->m->unbindonclose == 0)
+		ifc->conv->inuse++;
+	ifc->rp.mflag = 0;		// default not managed
+	ifc->rp.oflag = 0;
+	ifc->rp.maxraint = 600000;	// millisecs
+	ifc->rp.minraint = 200000;
+	ifc->rp.linkmtu = 0;		// no mtu sent
+	ifc->rp.reachtime = 0;
+	ifc->rp.rxmitra = 0;
+	ifc->rp.ttl = MAXTTL;
+	ifc->rp.routerlt = 3*(ifc->rp.maxraint);
+
+	/* any ancillary structures (like routes) no longer pertain */
+	ifc->ifcid++;
+
+	/* reopen all the queues closed by a previous unbind */
+	qreopen(c->rq);
+	qreopen(c->eq);
+	qreopen(c->sq);
+
+	wunlock(ifc);
+	poperror();
+
+	return nil;
+}
+
+/*
+ *  detach a device from an interface, close the interface
+ *  called with ifc->conv closed
+ */
+static char*
+ipifcunbind(Ipifc *ifc)
+{
+	char *err;
+
+	if(waserror()){
+		wunlock(ifc);
+		nexterror();
+	}
+	wlock(ifc);
+
+	/* dissociate routes */
+	if(ifc->m != nil && ifc->m->unbindonclose == 0)
+		ifc->conv->inuse--;
+	ifc->ifcid++;
+
+	/* disassociate device */
+	if(ifc->m != nil && ifc->m->unbind)
+		(*ifc->m->unbind)(ifc);
+	memset(ifc->dev, 0, sizeof(ifc->dev));
+	ifc->arg = nil;
+	ifc->reassemble = 0;
+
+	/* close queues to stop queuing of packets */
+	qclose(ifc->conv->rq);
+	qclose(ifc->conv->wq);
+	qclose(ifc->conv->sq);
+
+	/* disassociate logical interfaces */
+	while(ifc->lifc){
+		err = ipifcremlifc(ifc, ifc->lifc);
+		if(err)
+			error(err);
+	}
+
+	ifc->m = nil;
+	wunlock(ifc);
+	poperror();
+	return nil;
+}
+
+
+
+char sfixedformat[] = "device %s maxtu %d sendra %d recvra %d mflag %d oflag %d maxraint %d minraint %d linkmtu %d reachtime %d rxmitra %d ttl %d routerlt %d pktin %lud pktout %lud errin %lud errout %lud\n";
+
+char slineformat[] = "	%-40I %-10M %-40I %-12lud %-12lud\n";
+
+
+static int
+ipifcstate(Conv *c, char *state, int n)
+{
+	Ipifc *ifc;
+	Iplifc *lifc;
+	int m;
+
+	ifc = (Ipifc*)c->ptcl;
+
+	m = snprint(state, n, sfixedformat,
+		ifc->dev, ifc->maxtu, ifc->sendra6, ifc->recvra6,
+		ifc->rp.mflag, ifc->rp.oflag, ifc->rp.maxraint,
+		ifc->rp.minraint, ifc->rp.linkmtu, ifc->rp.reachtime,
+		ifc->rp.rxmitra, ifc->rp.ttl, ifc->rp.routerlt,
+		ifc->in, ifc->out, ifc->inerr, ifc->outerr);
+
+	rlock(ifc);
+	for(lifc = ifc->lifc; lifc && n > m; lifc = lifc->next)
+		m += snprint(state+m, n - m, slineformat,
+			lifc->local, lifc->mask, lifc->remote,
+			lifc->validlt, lifc->preflt);
+	if(ifc->lifc == nil)
+		m += snprint(state+m, n - m, "\n");
+	runlock(ifc);
+	return m;
+}
+
+static int
+ipifclocal(Conv *c, char *state, int n)
+{
+	Ipifc *ifc;
+	Iplifc *lifc;
+	Iplink *link;
+	int m;
+
+	ifc = (Ipifc*)c->ptcl;
+
+	m = 0;
+
+	rlock(ifc);
+	for(lifc = ifc->lifc; lifc; lifc = lifc->next){
+		m += snprint(state+m, n - m, "%-40.40I ->", lifc->local);
+		for(link = lifc->link; link; link = link->lifclink)
+			m += snprint(state+m, n - m, " %-40.40I", link->self->a);
+		m += snprint(state+m, n - m, "\n");
+	}
+	runlock(ifc);
+	return m;
+}
+
+static int
+ipifcinuse(Conv *c)
+{
+	Ipifc *ifc;
+
+	ifc = (Ipifc*)c->ptcl;
+	return ifc->m != nil;
+}
+
+/*
+ *  called when a process writes to an interface's 'data'
+ */
+static void
+ipifckick(void *x)
+{
+	Conv *c = x;
+	Block *bp;
+	Ipifc *ifc;
+
+	bp = qget(c->wq);
+	if(bp == nil)
+		return;
+
+	ifc = (Ipifc*)c->ptcl;
+	if(!canrlock(ifc)){
+		freeb(bp);
+		return;
+	}
+	if(waserror()){
+		runlock(ifc);
+		nexterror();
+	}
+	if(ifc->m == nil || ifc->m->pktin == nil)
+		freeb(bp);
+	else
+		(*ifc->m->pktin)(c->p->f, ifc, bp);
+	runlock(ifc);
+	poperror();
+}
+
+/*
+ *  called when a new ipifc structure is created
+ */
+static void
+ipifccreate(Conv *c)
+{
+	Ipifc *ifc;
+
+	c->rq = qopen(QMAX, 0, 0, 0);
+	c->sq = qopen(2*QMAX, 0, 0, 0);
+	c->wq = qopen(QMAX, Qkick, ipifckick, c);
+	ifc = (Ipifc*)c->ptcl;
+	ifc->conv = c;
+	ifc->unbinding = 0;
+	ifc->m = nil;
+	ifc->reassemble = 0;
+}
+
+/*
+ *  called after last close of ipifc data or ctl
+ *  called with c locked, we must unlock
+ */
+static void
+ipifcclose(Conv *c)
+{
+	Ipifc *ifc;
+	Medium *m;
+
+	ifc = (Ipifc*)c->ptcl;
+	m = ifc->m;
+	if(m != nil && m->unbindonclose)
+		ipifcunbind(ifc);
+}
+
+/*
+ *  change an interface's mtu
+ */
+char*
+ipifcsetmtu(Ipifc *ifc, char **argv, int argc)
+{
+	int mtu;
+
+	if(argc < 2)
+		return Ebadarg;
+	if(ifc->m == nil)
+		return Ebadarg;
+	mtu = strtoul(argv[1], 0, 0);
+	if(mtu < ifc->m->mintu || mtu > ifc->m->maxtu)
+		return Ebadarg;
+	ifc->maxtu = mtu;
+	return nil;
+}
+
+/*
+ *  add an address to an interface.
+ */
+char*
+ipifcadd(Ipifc *ifc, char **argv, int argc, int tentative, Iplifc *lifcp)
+{
+	uchar ip[IPaddrlen], mask[IPaddrlen], rem[IPaddrlen];
+	uchar bcast[IPaddrlen], net[IPaddrlen];
+	Iplifc *lifc, **l;
+	int i, type, mtu;
+	Fs *f;
+	int sendnbrdisc = 0;
+
+	if(ifc->m == nil)
+		return "ipifc not yet bound to device";
+
+	f = ifc->conv->p->f;
+
+	type = Rifc;
+	memset(ip, 0, IPaddrlen);
+	memset(mask, 0, IPaddrlen);
+	memset(rem, 0, IPaddrlen);
+	switch(argc){
+	case 6:
+		if(strcmp(argv[5], "proxy") == 0)
+			type |= Rproxy;
+		/* fall through */
+	case 5:
+		mtu = strtoul(argv[4], 0, 0);
+		if(mtu >= ifc->m->mintu && mtu <= ifc->m->maxtu)
+			ifc->maxtu = mtu;
+		/* fall through */
+	case 4:
+		parseip(ip, argv[1]);
+		parseipmask(mask, argv[2]);
+		parseip(rem, argv[3]);
+		maskip(rem, mask, net);
+		break;
+	case 3:
+		parseip(ip, argv[1]);
+		parseipmask(mask, argv[2]);
+		maskip(ip, mask, rem);
+		maskip(rem, mask, net);
+		break;
+	case 2:
+		parseip(ip, argv[1]);
+		memmove(mask, defmask(ip), IPaddrlen);
+		maskip(ip, mask, rem);
+		maskip(rem, mask, net);
+		break;
+	default:
+		return Ebadarg;
+		break;
+	}
+	if(isv4(ip))
+		tentative = 0;
+	wlock(ifc);
+
+	/* ignore if this is already a local address for this ifc */
+	for(lifc = ifc->lifc; lifc; lifc = lifc->next) {
+		if(ipcmp(lifc->local, ip) == 0) {
+			if(lifc->tentative != tentative)
+				lifc->tentative = tentative;
+			if(lifcp != nil) {
+				lifc->onlink = lifcp->onlink;
+				lifc->autoflag = lifcp->autoflag;
+				lifc->validlt = lifcp->validlt;
+				lifc->preflt = lifcp->preflt;
+				lifc->origint = lifcp->origint;
+			}
+			goto out;
+		}
+	}
+
+	/* add the address to the list of logical ifc's for this ifc */
+	lifc = smalloc(sizeof(Iplifc));
+	ipmove(lifc->local, ip);
+	ipmove(lifc->mask, mask);
+	ipmove(lifc->remote, rem);
+	ipmove(lifc->net, net);
+	lifc->tentative = tentative;
+	if(lifcp != nil) {
+		lifc->onlink = lifcp->onlink;
+		lifc->autoflag = lifcp->autoflag;
+		lifc->validlt = lifcp->validlt;
+		lifc->preflt = lifcp->preflt;
+		lifc->origint = lifcp->origint;
+	}
+	else {		// default values
+		lifc->onlink = 1;
+		lifc->autoflag = 1;
+		lifc->validlt = 0xffffffff;
+		lifc->preflt = 0xffffffff;
+		lifc->origint = NOW / 10^3;
+	}
+	lifc->next = nil;
+
+	for(l = &ifc->lifc; *l; l = &(*l)->next)
+		;
+	*l = lifc;
+
+	/* check for point-to-point interface */
+	if(ipcmp(ip, v6loopback))  /* skip v6 loopback, it's a special address */
+	if(ipcmp(mask, IPallbits) == 0)
+		type |= Rptpt;
+
+	/* add local routes */
+	if(isv4(ip))
+		v4addroute(f, tifc, rem+IPv4off, mask+IPv4off, rem+IPv4off, type);
+	else
+		v6addroute(f, tifc, rem, mask, rem, type);
+
+	addselfcache(f, ifc, lifc, ip, Runi);
+
+	if((type & (Rproxy|Rptpt)) == (Rproxy|Rptpt)){
+		ipifcregisterproxy(f, ifc, rem);
+		goto out;
+	}
+
+	if(isv4(ip) || ipcmp(ip, IPnoaddr) == 0) {
+		/* add subnet directed broadcast address to the self cache */
+		for(i = 0; i < IPaddrlen; i++)
+			bcast[i] = (ip[i] & mask[i]) | ~mask[i];
+		addselfcache(f, ifc, lifc, bcast, Rbcast);
+
+		/* add subnet directed network address to the self cache */
+		for(i = 0; i < IPaddrlen; i++)
+			bcast[i] = (ip[i] & mask[i]) & mask[i];
+		addselfcache(f, ifc, lifc, bcast, Rbcast);
+
+		/* add network directed broadcast address to the self cache */
+		memmove(mask, defmask(ip), IPaddrlen);
+		for(i = 0; i < IPaddrlen; i++)
+			bcast[i] = (ip[i] & mask[i]) | ~mask[i];
+		addselfcache(f, ifc, lifc, bcast, Rbcast);
+
+		/* add network directed network address to the self cache */
+		memmove(mask, defmask(ip), IPaddrlen);
+		for(i = 0; i < IPaddrlen; i++)
+			bcast[i] = (ip[i] & mask[i]) & mask[i];
+		addselfcache(f, ifc, lifc, bcast, Rbcast);
+		
+		addselfcache(f, ifc, lifc, IPv4bcast, Rbcast);
+	}
+	else {
+		if(ipcmp(ip, v6loopback) == 0) {
+			/* add node-local mcast address */
+			addselfcache(f, ifc, lifc, v6allnodesN, Rmulti);
+
+			/* add route for all node multicast */
+			v6addroute(f, tifc, v6allnodesN, v6allnodesNmask, v6allnodesN, Rmulti);
+		}
+
+		/* add all nodes multicast address */
+		addselfcache(f, ifc, lifc, v6allnodesL, Rmulti);
+		
+		/* add route for all nodes multicast */
+		v6addroute(f, tifc, v6allnodesL, v6allnodesLmask, v6allnodesL, Rmulti);
+		
+		/* add solicited-node multicast address */
+		ipv62smcast(bcast, ip);
+		addselfcache(f, ifc, lifc, bcast, Rmulti);
+
+		sendnbrdisc = 1;
+	}
+
+	/* register the address on this network for address resolution */
+	if(isv4(ip) && ifc->m->areg != nil)
+		(*ifc->m->areg)(ifc, ip);
+
+out:
+	wunlock(ifc);
+	if(tentative && sendnbrdisc)
+		icmpns(f, 0, SRC_UNSPEC, ip, TARG_MULTI, ifc->mac);
+	return nil;
+}
+
+/*
+ *  remove a logical interface from an ifc
+ *  always called with ifc wlock'd
+ */
+static char*
+ipifcremlifc(Ipifc *ifc, Iplifc *lifc)
+{
+	Iplifc **l;
+	Fs *f;
+
+	f = ifc->conv->p->f;
+
+	/*
+	 *  find address on this interface and remove from chain.
+	 *  for pt to pt we actually specify the remote address as the
+	 *  addresss to remove.
+	 */
+	for(l = &ifc->lifc; *l != nil && *l != lifc; l = &(*l)->next)
+		;
+	if(*l == nil)
+		return "address not on this interface";
+	*l = lifc->next;
+
+	/* disassociate any addresses */
+	while(lifc->link)
+		remselfcache(f, ifc, lifc, lifc->link->self->a);
+
+	/* remove the route for this logical interface */
+	if(isv4(lifc->local))
+		v4delroute(f, lifc->remote+IPv4off, lifc->mask+IPv4off, 1);
+	else {
+		v6delroute(f, lifc->remote, lifc->mask, 1);
+		if(ipcmp(lifc->local, v6loopback) == 0)
+			/* remove route for all node multicast */
+			v6delroute(f, v6allnodesN, v6allnodesNmask, 1);
+		else if(memcmp(lifc->local, v6linklocal, v6llpreflen) == 0)
+			/* remove route for all link multicast */
+			v6delroute(f, v6allnodesL, v6allnodesLmask, 1);
+	}
+
+	free(lifc);
+	return nil;
+
+}
+
+/*
+ *  remove an address from an interface.
+ *  called with c locked
+ */
+char*
+ipifcrem(Ipifc *ifc, char **argv, int argc)
+{
+	uchar ip[IPaddrlen];
+	uchar mask[IPaddrlen];
+	uchar rem[IPaddrlen];
+	Iplifc *lifc;
+	char *rv;
+
+	if(argc < 3)
+		return Ebadarg;
+
+	parseip(ip, argv[1]);
+	parseipmask(mask, argv[2]);
+	if(argc < 4)
+		maskip(ip, mask, rem);
+	else
+		parseip(rem, argv[3]);
+
+	wlock(ifc);
+
+	/*
+	 *  find address on this interface and remove from chain.
+	 *  for pt to pt we actually specify the remote address as the
+	 *  addresss to remove.
+	 */
+	for(lifc = ifc->lifc; lifc != nil; lifc = lifc->next) {
+		if (memcmp(ip, lifc->local, IPaddrlen) == 0
+		&& memcmp(mask, lifc->mask, IPaddrlen) == 0
+		&& memcmp(rem, lifc->remote, IPaddrlen) == 0)
+			break;
+	}
+
+	rv = ipifcremlifc(ifc, lifc);
+	wunlock(ifc);
+	return rv;
+}
+
+/*
+ * distribute routes to active interfaces like the
+ * TRIP linecards
+ */
+void
+ipifcaddroute(Fs *f, int vers, uchar *addr, uchar *mask, uchar *gate, int type)
+{
+	Medium *m;
+	Conv **cp, **e;
+	Ipifc *ifc;
+
+	e = &f->ipifc->conv[f->ipifc->nc];
+	for(cp = f->ipifc->conv; cp < e; cp++){
+		if(*cp != nil) {
+			ifc = (Ipifc*)(*cp)->ptcl;
+			m = ifc->m;
+			if(m == nil)
+				continue;
+			if(m->addroute != nil)
+				m->addroute(ifc, vers, addr, mask, gate, type);
+		}
+	}
+}
+
+void
+ipifcremroute(Fs *f, int vers, uchar *addr, uchar *mask)
+{
+	Medium *m;
+	Conv **cp, **e;
+	Ipifc *ifc;
+
+	e = &f->ipifc->conv[f->ipifc->nc];
+	for(cp = f->ipifc->conv; cp < e; cp++){
+		if(*cp != nil) {
+			ifc = (Ipifc*)(*cp)->ptcl;
+			m = ifc->m;
+			if(m == nil)
+				continue;
+			if(m->remroute != nil)
+				m->remroute(ifc, vers, addr, mask);
+		}
+	}
+}
+
+/*
+ *  associate an address with the interface.  This wipes out any previous
+ *  addresses.  This is a macro that means, remove all the old interfaces
+ *  and add a new one.
+ */
+static char*
+ipifcconnect(Conv* c, char **argv, int argc)
+{
+	char *err;
+	Ipifc *ifc;
+
+	ifc = (Ipifc*)c->ptcl;
+
+	if(ifc->m == nil)
+		 return "ipifc not yet bound to device";
+
+	if(waserror()){
+		wunlock(ifc);
+		nexterror();
+	}
+	wlock(ifc);
+	while(ifc->lifc){
+		err = ipifcremlifc(ifc, ifc->lifc);
+		if(err)
+			error(err);
+	}
+	wunlock(ifc);
+	poperror();
+
+	err = ipifcadd(ifc, argv, argc, 0, nil);
+	if(err)
+		return err;
+
+	Fsconnected(c, nil);
+
+	return nil;
+}
+
+char*
+ipifcsetpar6(Ipifc *ifc, char **argv, int argc)
+{
+	int i, argsleft, vmax = ifc->rp.maxraint, vmin = ifc->rp.minraint;
+
+	argsleft = argc - 1;
+	i = 1;
+
+	if(argsleft % 2 != 0)
+		return Ebadarg;
+
+	while (argsleft > 1) {
+		if(strcmp(argv[i],"recvra")==0)
+			ifc->recvra6 = (atoi(argv[i+1]) != 0);
+		else if(strcmp(argv[i],"sendra")==0)
+			ifc->sendra6 = (atoi(argv[i+1]) != 0);
+		else if(strcmp(argv[i],"mflag")==0)
+			ifc->rp.mflag = (atoi(argv[i+1]) != 0);
+		else if(strcmp(argv[i],"oflag")==0)
+			ifc->rp.oflag = (atoi(argv[i+1]) != 0);
+		else if(strcmp(argv[i],"maxraint")==0)
+			ifc->rp.maxraint = atoi(argv[i+1]);
+		else if(strcmp(argv[i],"minraint")==0)
+			ifc->rp.minraint = atoi(argv[i+1]);
+		else if(strcmp(argv[i],"linkmtu")==0)
+			ifc->rp.linkmtu = atoi(argv[i+1]);
+		else if(strcmp(argv[i],"reachtime")==0)
+			ifc->rp.reachtime = atoi(argv[i+1]);
+		else if(strcmp(argv[i],"rxmitra")==0)
+			ifc->rp.rxmitra = atoi(argv[i+1]);
+		else if(strcmp(argv[i],"ttl")==0)
+			ifc->rp.ttl = atoi(argv[i+1]);
+		else if(strcmp(argv[i],"routerlt")==0)
+			ifc->rp.routerlt = atoi(argv[i+1]);
+		else
+			return Ebadarg;	
+
+		argsleft -= 2;
+		i += 2;
+	}
+
+	// consistency check
+	if(ifc->rp.maxraint < ifc->rp.minraint) {
+		ifc->rp.maxraint = vmax;
+		ifc->rp.minraint = vmin;
+		return Ebadarg;
+	}
+
+	return nil;
+}
+
+char*
+ipifcsendra6(Ipifc *ifc, char **argv, int argc)
+{
+	int i;
+	
+	i = 0;
+	if(argc > 1)
+		i = atoi(argv[1]);
+	ifc->sendra6 = (i!=0);
+	return nil;
+}
+
+char*
+ipifcrecvra6(Ipifc *ifc, char **argv, int argc)
+{
+	int i;
+	
+	i = 0;
+	if(argc > 1)
+		i = atoi(argv[1]);
+	ifc->recvra6 = (i!=0);	
+	return nil;
+}
+
+/*
+ *  non-standard control messages.
+ *  called with c locked.
+ */
+static char*
+ipifcctl(Conv* c, char**argv, int argc)
+{
+	Ipifc *ifc;
+	int i;
+
+	ifc = (Ipifc*)c->ptcl;
+	if(strcmp(argv[0], "add") == 0)
+		return ipifcadd(ifc, argv, argc, 0, nil);
+	else if(strcmp(argv[0], "bootp") == 0)
+		return bootp(ifc);
+	else if(strcmp(argv[0], "try") == 0)
+		return ipifcadd(ifc, argv, argc, 1, nil);
+	else if(strcmp(argv[0], "remove") == 0)
+		return ipifcrem(ifc, argv, argc);
+	else if(strcmp(argv[0], "unbind") == 0)
+		return ipifcunbind(ifc);
+	else if(strcmp(argv[0], "joinmulti") == 0)
+		return ipifcjoinmulti(ifc, argv, argc);
+	else if(strcmp(argv[0], "leavemulti") == 0)
+		return ipifcleavemulti(ifc, argv, argc);
+	else if(strcmp(argv[0], "mtu") == 0)
+		return ipifcsetmtu(ifc, argv, argc);
+	else if(strcmp(argv[0], "reassemble") == 0){
+		ifc->reassemble = 1;
+		return nil;
+	}
+	else if(strcmp(argv[0], "iprouting") == 0){
+		i = 1;
+		if(argc > 1)
+			i = atoi(argv[1]);
+		iprouting(c->p->f, i);
+		return nil;
+	}
+	else if(strcmp(argv[0], "addpref6") == 0)
+		return ipifcaddpref6(ifc, argv, argc);
+	else if(strcmp(argv[0], "setpar6") == 0)
+		return ipifcsetpar6(ifc, argv, argc);
+	else if(strcmp(argv[0], "sendra6") == 0)
+		return ipifcsendra6(ifc, argv, argc);
+	else if(strcmp(argv[0], "recvra6") == 0)
+		return ipifcrecvra6(ifc, argv, argc);
+	return "unsupported ctl";
+}
+
+ipifcstats(Proto *ipifc, char *buf, int len)
+{
+	return ipstats(ipifc->f, buf, len);
+}
+
+void
+ipifcinit(Fs *f)
+{
+	Proto *ipifc;
+
+	ipifc = smalloc(sizeof(Proto));
+	ipifc->name = "ipifc";
+	ipifc->connect = ipifcconnect;
+	ipifc->announce = nil;
+	ipifc->bind = ipifcbind;
+	ipifc->state = ipifcstate;
+	ipifc->create = ipifccreate;
+	ipifc->close = ipifcclose;
+	ipifc->rcv = nil;
+	ipifc->ctl = ipifcctl;
+	ipifc->advise = nil;
+	ipifc->stats = ipifcstats;
+	ipifc->inuse = ipifcinuse;
+	ipifc->local = ipifclocal;
+	ipifc->ipproto = -1;
+	ipifc->nc = Maxmedia;
+	ipifc->ptclsize = sizeof(Ipifc);
+
+	f->ipifc = ipifc;			/* hack for ipifcremroute, findipifc, ... */
+	f->self = smalloc(sizeof(Ipselftab));	/* hack for ipforme */
+
+	Fsproto(f, ipifc);
+}
+
+/*
+ *  add to self routing cache
+ *	called with c locked
+ */
+static void
+addselfcache(Fs *f, Ipifc *ifc, Iplifc *lifc, uchar *a, int type)
+{
+	Ipself *p;
+	Iplink *lp;
+	int h;
+
+	qlock(f->self);
+
+	/* see if the address already exists */
+	h = hashipa(a);
+	for(p = f->self->hash[h]; p; p = p->next)
+		if(memcmp(a, p->a, IPaddrlen) == 0)
+			break;
+
+	/* allocate a local address and add to hash chain */
+	if(p == nil){
+		p = smalloc(sizeof(*p));
+		ipmove(p->a, a);
+		p->type = type;
+		p->next = f->self->hash[h];
+		f->self->hash[h] = p;
+
+		/* if the null address, accept all packets */
+		if(ipcmp(a, v4prefix) == 0 || ipcmp(a, IPnoaddr) == 0)
+			f->self->acceptall = 1;
+	}
+
+	/* look for a link for this lifc */
+	for(lp = p->link; lp; lp = lp->selflink)
+		if(lp->lifc == lifc)
+			break;
+
+	/* allocate a lifc-to-local link and link to both */
+	if(lp == nil){
+		lp = smalloc(sizeof(*lp));
+		lp->ref = 1;
+		lp->lifc = lifc;
+		lp->self = p;
+		lp->selflink = p->link;
+		p->link = lp;
+		lp->lifclink = lifc->link;
+		lifc->link = lp;
+
+		/* add to routing table */
+		if(isv4(a))
+			v4addroute(f, tifc, a+IPv4off, IPallbits+IPv4off, a+IPv4off, type);
+		else
+			v6addroute(f, tifc, a, IPallbits, a, type);
+
+		if((type & Rmulti) && ifc->m->addmulti != nil)
+			(*ifc->m->addmulti)(ifc, a, lifc->local);
+	} else {
+		lp->ref++;
+	}
+
+	qunlock(f->self);
+}
+
+/*
+ *  These structures are unlinked from their chains while
+ *  other threads may be using them.  To avoid excessive locking,
+ *  just put them aside for a while before freeing them.
+ *	called with f->self locked
+ */
+static Iplink *freeiplink;
+static Ipself *freeipself;
+
+static void
+iplinkfree(Iplink *p)
+{
+	Iplink **l, *np;
+	ulong now = NOW;
+
+	l = &freeiplink;
+	for(np = *l; np; np = *l){
+		if(np->expire > now){
+			*l = np->next;
+			free(np);
+			continue;
+		}
+		l = &np->next;
+	}
+	p->expire = now + 5000;		/* give other threads 5 secs to get out */
+	p->next = nil;
+	*l = p;
+}
+static void
+ipselffree(Ipself *p)
+{
+	Ipself **l, *np;
+	ulong now = NOW;
+
+	l = &freeipself;
+	for(np = *l; np; np = *l){
+		if(np->expire > now){
+			*l = np->next;
+			free(np);
+			continue;
+		}
+		l = &np->next;
+	}
+	p->expire = now + 5000;		/* give other threads 5 secs to get out */
+	p->next = nil;
+	*l = p;
+}
+
+/*
+ *  Decrement reference for this address on this link.
+ *  Unlink from selftab if this is the last ref.
+ *	called with c locked
+ */
+static void
+remselfcache(Fs *f, Ipifc *ifc, Iplifc *lifc, uchar *a)
+{
+	Ipself *p, **l;
+	Iplink *link, **l_self, **l_lifc;
+
+	qlock(f->self);
+
+	/* find the unique selftab entry */
+	l = &f->self->hash[hashipa(a)];
+	for(p = *l; p; p = *l){
+		if(ipcmp(p->a, a) == 0)
+			break;
+		l = &p->next;
+	}
+
+	if(p == nil)
+		goto out;
+
+	/*
+	 *  walk down links from an ifc looking for one
+	 *  that matches the selftab entry
+	 */
+	l_lifc = &lifc->link;
+	for(link = *l_lifc; link; link = *l_lifc){
+		if(link->self == p)
+			break;
+		l_lifc = &link->lifclink;
+	}
+
+	if(link == nil)
+		goto out;
+
+	/*
+	 *  walk down the links from the selftab looking for
+	 *  the one we just found
+	 */
+	l_self = &p->link;
+	for(link = *l_self; link; link = *l_self){
+		if(link == *(l_lifc))
+			break;
+		l_self = &link->selflink;
+	}
+
+	if(link == nil)
+		panic("remselfcache");
+
+	if(--(link->ref) != 0)
+		goto out;
+
+	if((p->type & Rmulti) && ifc->m->remmulti != nil)
+		(*ifc->m->remmulti)(ifc, a, lifc->local);
+
+	/* ref == 0, remove from both chains and free the link */
+	*l_lifc = link->lifclink;
+	*l_self = link->selflink;
+	iplinkfree(link);
+
+	if(p->link != nil)
+		goto out;
+
+	/* remove from routing table */
+	if(isv4(a))
+		v4delroute(f, a+IPv4off, IPallbits+IPv4off, 1);
+	else
+		v6delroute(f, a, IPallbits, 1);
+	
+	/* no more links, remove from hash and free */
+	*l = p->next;
+	ipselffree(p);
+
+	/* if IPnoaddr, forget */
+	if(ipcmp(a, v4prefix) == 0 || ipcmp(a, IPnoaddr) == 0)
+		f->self->acceptall = 0;
+
+out:
+	qunlock(f->self);
+}
+
+static char *stformat = "%-44.44I %2.2d %4.4s\n";
+enum
+{
+	Nstformat= 41,
+};
+
+long
+ipselftabread(Fs *f, char *cp, ulong offset, int n)
+{
+	int i, m, nifc, off;
+	Ipself *p;
+	Iplink *link;
+	char state[8];
+
+	m = 0;
+	off = offset;
+	qlock(f->self);
+	for(i = 0; i < NHASH && m < n; i++){
+		for(p = f->self->hash[i]; p != nil && m < n; p = p->next){
+			nifc = 0;
+			for(link = p->link; link; link = link->selflink)
+				nifc++;
+			routetype(p->type, state);
+			m += snprint(cp + m, n - m, stformat, p->a, nifc, state);
+			if(off > 0){
+				off -= m;
+				m = 0;
+			}
+		}
+	}
+	qunlock(f->self);
+	return m;
+}
+
+int
+iptentative(Fs *f, uchar *addr)
+{
+ 	Ipself *p;
+
+	p = f->self->hash[hashipa(addr)];
+	for(; p; p = p->next){
+		if(ipcmp(addr, p->a) == 0) {
+			return p->link->lifc->tentative;
+		}
+	}
+	return 0;
+}
+
+/*
+ *  returns
+ *	0		- no match
+ *	Runi
+ *	Rbcast
+ *	Rmcast
+ */
+int
+ipforme(Fs *f, uchar *addr)
+{
+	Ipself *p;
+
+	p = f->self->hash[hashipa(addr)];
+	for(; p; p = p->next){
+		if(ipcmp(addr, p->a) == 0)
+			return p->type;
+	}
+
+	/* hack to say accept anything */
+	if(f->self->acceptall)
+		return Runi;
+
+	return 0;
+}
+
+/*
+ *  find the ifc on same net as the remote system.  If none,
+ *  return nil.
+ */
+Ipifc*
+findipifc(Fs *f, uchar *remote, int type)
+{
+	Ipifc *ifc, *x;
+	Iplifc *lifc;
+	Conv **cp, **e;
+	uchar gnet[IPaddrlen];
+	uchar xmask[IPaddrlen];
+
+	x = nil; memset(xmask, 0, IPaddrlen);
+
+	/* find most specific match */
+	e = &f->ipifc->conv[f->ipifc->nc];
+	for(cp = f->ipifc->conv; cp < e; cp++){
+		if(*cp == 0)
+			continue;
+
+		ifc = (Ipifc*)(*cp)->ptcl;
+
+		for(lifc = ifc->lifc; lifc; lifc = lifc->next){
+			maskip(remote, lifc->mask, gnet);
+			if(ipcmp(gnet, lifc->net) == 0){
+				if(x == nil || ipcmp(lifc->mask, xmask) > 0){
+					x = ifc;
+					ipmove(xmask, lifc->mask);
+				}
+			}
+		}
+	}
+	if(x != nil)
+		return x;
+
+	/* for now for broadcast and multicast, just use first interface */
+	if(type & (Rbcast|Rmulti)){
+		for(cp = f->ipifc->conv; cp < e; cp++){
+			if(*cp == 0)
+				continue;
+			ifc = (Ipifc*)(*cp)->ptcl;
+			if(ifc->lifc != nil)
+				return ifc;
+		}
+	}
+		
+	return nil;
+}
+
+enum {
+	unknownv6,
+	multicastv6,
+	unspecifiedv6,
+	linklocalv6,
+	sitelocalv6,
+	globalv6,
+};
+
+int
+v6addrtype(uchar *addr)
+{
+	if(isv6global(addr))
+		return globalv6;
+	if(islinklocal(addr))
+		return linklocalv6;
+	if(isv6mcast(addr))
+		return multicastv6;
+	if(issitelocal(addr))
+		return sitelocalv6;
+	return unknownv6;
+}
+
+#define v6addrcurr(lifc) (( (lifc)->origint + (lifc)->preflt >= (NOW/10^3) ) || ( (lifc)->preflt == 0xffffffff ))
+
+static void
+findprimaryipv6(Fs *f, uchar *local)
+{
+	Conv **cp, **e;
+	Ipifc *ifc;
+	Iplifc *lifc;
+	int atype, atypel;
+
+	ipmove(local, v6Unspecified);
+	atype = unspecifiedv6;
+
+	/* find "best" (global > sitelocal > link local > unspecified)
+	 * local address; address must be current */
+
+	e = &f->ipifc->conv[f->ipifc->nc];
+	for(cp = f->ipifc->conv; cp < e; cp++){
+		if(*cp == 0)
+			continue;
+		ifc = (Ipifc*)(*cp)->ptcl;
+		for(lifc = ifc->lifc; lifc; lifc = lifc->next){
+			atypel = v6addrtype(lifc->local);
+			if(atypel > atype)
+			if(v6addrcurr(lifc)) {
+				ipmove(local, lifc->local);
+				atype = atypel;
+				if(atype == globalv6)
+					return;
+			}
+		}
+	}
+}
+
+/*
+ *  returns first ip address configured
+ */
+static void
+findprimaryipv4(Fs *f, uchar *local)
+{
+	Conv **cp, **e;
+	Ipifc *ifc;
+	Iplifc *lifc;
+
+	/* find first ifc local address */
+	e = &f->ipifc->conv[f->ipifc->nc];
+	for(cp = f->ipifc->conv; cp < e; cp++){
+		if(*cp == 0)
+			continue;
+		ifc = (Ipifc*)(*cp)->ptcl;
+		if((lifc = ifc->lifc) != nil){
+			ipmove(local, lifc->local);
+			return;
+		}
+	}
+}
+
+/*
+ *  find the local address 'closest' to the remote system, copy it to
+ *  local and return the ifc for that address
+ */
+void
+findlocalip(Fs *f, uchar *local, uchar *remote)
+{
+	Ipifc *ifc;
+	Iplifc *lifc;
+	Route *r;
+	uchar gate[IPaddrlen];
+	uchar gnet[IPaddrlen];
+	int version;
+	int atype = unspecifiedv6, atypel = unknownv6;
+
+	USED(atype);
+	USED(atypel);
+	qlock(f->ipifc);
+	r = v6lookup(f, remote, nil);
+ 	version = (memcmp(remote, v4prefix, IPv4off) == 0) ? V4 : V6;
+	
+	if(r != nil){
+		ifc = r->ifc;
+		if(r->type & Rv4)
+			v4tov6(gate, r->v4.gate);
+		else {
+			ipmove(gate, r->v6.gate);
+			ipmove(local, v6Unspecified);
+		}
+
+		/* find ifc address closest to the gateway to use */
+		switch(version) {
+		case V4:
+			for(lifc = ifc->lifc; lifc; lifc = lifc->next){
+				maskip(gate, lifc->mask, gnet);
+				if(ipcmp(gnet, lifc->net) == 0){
+					ipmove(local, lifc->local);
+					goto out;
+				}
+			}
+			break;
+		case V6:
+			for(lifc = ifc->lifc; lifc; lifc = lifc->next){
+				atypel = v6addrtype(lifc->local);
+				maskip(gate, lifc->mask, gnet);
+				if(ipcmp(gnet, lifc->net) == 0)
+				if(atypel > atype)
+				if(v6addrcurr(lifc)) {
+					ipmove(local, lifc->local);
+					atype = atypel;
+					if(atype == globalv6)
+						break;
+				}
+			}
+			if(atype > unspecifiedv6)
+				goto out;
+			break;
+		default:
+			panic("findlocalip: version %d", version);
+		}
+	}
+
+	switch(version){
+	case V4:
+		findprimaryipv4(f, local);
+		break;
+	case V6:
+		findprimaryipv6(f, local);
+		break;
+	default:
+		panic("findlocalip2: version %d", version);
+	}
+
+out:
+	qunlock(f->ipifc);
+}
+
+/*
+ *  return first v4 address associated with an interface
+ */
+int
+ipv4local(Ipifc *ifc, uchar *addr)
+{
+	Iplifc *lifc;
+
+	for(lifc = ifc->lifc; lifc; lifc = lifc->next){
+		if(isv4(lifc->local)){
+			memmove(addr, lifc->local+IPv4off, IPv4addrlen);
+			return 1;
+		}
+	}
+	return 0;
+}
+
+/*
+ *  return first v6 address associated with an interface
+ */
+int
+ipv6local(Ipifc *ifc, uchar *addr)
+{
+	Iplifc *lifc;
+
+	for(lifc = ifc->lifc; lifc; lifc = lifc->next){
+		if(!isv4(lifc->local) && !(lifc->tentative)){
+			ipmove(addr, lifc->local);
+			return 1;
+		}
+	}
+	return 0;
+}
+
+int
+ipv6anylocal(Ipifc *ifc, uchar *addr)
+{
+	Iplifc *lifc;
+
+	for(lifc = ifc->lifc; lifc; lifc = lifc->next){
+		if(!isv4(lifc->local)){
+			ipmove(addr, lifc->local);
+			return SRC_UNI;
+		}
+	}
+	return SRC_UNSPEC;
+}
+
+/*
+ *  see if this address is bound to the interface
+ */
+Iplifc*
+iplocalonifc(Ipifc *ifc, uchar *ip)
+{
+	Iplifc *lifc;
+
+	for(lifc = ifc->lifc; lifc; lifc = lifc->next)
+		if(ipcmp(ip, lifc->local) == 0)
+			return lifc;
+	return nil;
+}
+
+
+/*
+ *  See if we're proxying for this address on this interface
+ */
+int
+ipproxyifc(Fs *f, Ipifc *ifc, uchar *ip)
+{
+	Route *r;
+	uchar net[IPaddrlen];
+	Iplifc *lifc;
+
+	/* see if this is a direct connected pt to pt address */
+	r = v6lookup(f, ip, nil);
+	if(r == nil)
+		return 0;
+	if((r->type & (Rifc|Rproxy)) != (Rifc|Rproxy))
+		return 0;
+
+	/* see if this is on the right interface */
+	for(lifc = ifc->lifc; lifc; lifc = lifc->next){
+		maskip(ip, lifc->mask, net);
+		if(ipcmp(net, lifc->remote) == 0)
+			return 1;
+	}
+
+	return 0;
+}
+
+/*
+ *  return multicast version if any
+ */
+int
+ipismulticast(uchar *ip)
+{
+	if(isv4(ip)){
+		if(ip[IPv4off] >= 0xe0 && ip[IPv4off] < 0xf0)
+			return V4;
+	} else {
+		if(ip[0] == 0xff)
+			return V6;
+	}
+	return 0;
+}
+
+int
+ipisbm(uchar *ip)
+{
+	if(isv4(ip)){
+		if(ip[IPv4off] >= 0xe0 && ip[IPv4off] < 0xf0)
+			return V4;
+		if(ipcmp(ip, IPv4bcast) == 0)
+			return V4;
+	} else {
+		if(ip[0] == 0xff)
+			return V6;
+	}
+	return 0;
+}
+
+
+/*
+ *  add a multicast address to an interface, called with c locked
+ */
+void
+ipifcaddmulti(Conv *c, uchar *ma, uchar *ia)
+{
+	Ipifc *ifc;
+	Iplifc *lifc;
+	Conv **p;
+	Ipmulti *multi, **l;
+	Fs *f;
+
+	f = c->p->f;
+	
+	for(l = &c->multi; *l; l = &(*l)->next)
+		if(ipcmp(ma, (*l)->ma) == 0)
+		if(ipcmp(ia, (*l)->ia) == 0)
+			return;		/* it's already there */
+
+	multi = *l = smalloc(sizeof(*multi));
+	ipmove(multi->ma, ma);
+	ipmove(multi->ia, ia);
+	multi->next = nil;
+
+	for(p = f->ipifc->conv; *p; p++){
+		if((*p)->inuse == 0)
+			continue;
+		ifc = (Ipifc*)(*p)->ptcl;
+		if(waserror()){
+			wunlock(ifc);
+			nexterror();
+		}
+		wlock(ifc);
+		for(lifc = ifc->lifc; lifc; lifc = lifc->next)
+			if(ipcmp(ia, lifc->local) == 0)
+				addselfcache(f, ifc, lifc, ma, Rmulti);
+		wunlock(ifc);
+		poperror();
+	}
+}
+
+
+/*
+ *  remove a multicast address from an interface, called with c locked
+ */
+void
+ipifcremmulti(Conv *c, uchar *ma, uchar *ia)
+{
+	Ipmulti *multi, **l;
+	Iplifc *lifc;
+	Conv **p;
+	Ipifc *ifc;
+	Fs *f;
+
+	f = c->p->f;
+	
+	for(l = &c->multi; *l; l = &(*l)->next)
+		if(ipcmp(ma, (*l)->ma) == 0)
+		if(ipcmp(ia, (*l)->ia) == 0)
+			break;
+
+	multi = *l;
+	if(multi == nil)
+		return; 	/* we don't have it open */
+
+	*l = multi->next;
+
+	for(p = f->ipifc->conv; *p; p++){
+		if((*p)->inuse == 0)
+			continue;
+
+		ifc = (Ipifc*)(*p)->ptcl;
+		if(waserror()){
+			wunlock(ifc);
+			nexterror();
+		}
+		wlock(ifc);
+		for(lifc = ifc->lifc; lifc; lifc = lifc->next)
+			if(ipcmp(ia, lifc->local) == 0)
+				remselfcache(f, ifc, lifc, ma);
+		wunlock(ifc);
+		poperror();
+	}
+
+	free(multi);
+}
+
+/*
+ *  make lifc's join and leave multicast groups
+ */
+static char*
+ipifcjoinmulti(Ipifc *ifc, char **argv, int argc)
+{
+	USED(ifc, argv, argc);
+	return nil;
+}
+
+static char*
+ipifcleavemulti(Ipifc *ifc, char **argv, int argc)
+{
+	USED(ifc, argv, argc);
+	return nil;
+}
+
+static void
+ipifcregisterproxy(Fs *f, Ipifc *ifc, uchar *ip)
+{
+	Conv **cp, **e;
+	Ipifc *nifc;
+	Iplifc *lifc;
+	Medium *m;
+	uchar net[IPaddrlen];
+
+	/* register the address on any network that will proxy for us */
+	e = &f->ipifc->conv[f->ipifc->nc];
+
+	if(!isv4(ip)) { // V6
+		for(cp = f->ipifc->conv; cp < e; cp++){
+			if(*cp == nil)
+				continue;
+			nifc = (Ipifc*)(*cp)->ptcl;
+			if(nifc == ifc)
+				continue;
+	
+			rlock(nifc);
+			m = nifc->m;
+			if(m == nil || m->addmulti == nil) {
+				runlock(nifc);
+				continue;
+			}
+			for(lifc = nifc->lifc; lifc; lifc = lifc->next){
+				maskip(ip, lifc->mask, net);
+				if(ipcmp(net, lifc->remote) == 0) { /* add solicited-node multicast address */
+					ipv62smcast(net, ip);
+					addselfcache(f, nifc, lifc, net, Rmulti);
+					arpenter(f, V6, ip, nifc->mac, 6, 0);
+					//(*m->addmulti)(nifc, net, ip);
+					break;
+				}
+			}
+			runlock(nifc);
+		}
+		return;
+	}
+	else { // V4
+		for(cp = f->ipifc->conv; cp < e; cp++){
+			if(*cp == nil)
+				continue;
+			nifc = (Ipifc*)(*cp)->ptcl;
+			if(nifc == ifc)
+				continue;
+	
+			rlock(nifc);
+			m = nifc->m;
+			if(m == nil || m->areg == nil){
+				runlock(nifc);
+				continue;
+			}
+			for(lifc = nifc->lifc; lifc; lifc = lifc->next){
+				maskip(ip, lifc->mask, net);
+				if(ipcmp(net, lifc->remote) == 0){
+					(*m->areg)(nifc, ip);
+					break;
+				}
+			}
+			runlock(nifc);
+		}
+	}
+}
+
+
+// added for new v6 mesg types
+static void
+adddefroute6(Fs *f, uchar *gate, int force)
+{
+	Route *r;
+
+	r = v6lookup(f, v6Unspecified, nil);
+	if(r!=nil)
+	if(!(force) && (strcmp(r->tag,"ra")!=0))	// route entries generated
+		return;			// by all other means take
+					// precedence over router annc
+
+	v6delroute(f, v6Unspecified, v6Unspecified, 1);
+	v6addroute(f, "ra", v6Unspecified, v6Unspecified, gate, 0);
+}
+
+enum
+{
+	Ngates = 3,
+};
+
+char*
+ipifcaddpref6(Ipifc *ifc, char**argv, int argc)
+{
+	uchar	onlink = 1;
+	uchar	autoflag = 1;
+	long 	validlt = 0xffffffff;
+	long 	preflt = 0xffffffff;
+	long	origint = NOW / 10^3;
+	uchar	prefix[IPaddrlen];
+	int	plen = 64;
+	Iplifc	*lifc;
+	char	addr[40], preflen[6];
+	char	*params[3];
+
+	switch(argc) {
+	case 7:
+		preflt = atoi(argv[6]);
+		/* fall through */
+	case 6:
+		validlt = atoi(argv[5]);
+		/* fall through */
+	case 5:
+		autoflag =  atoi(argv[4]);
+		/* fall through */
+	case 4:
+		onlink = atoi(argv[3]);
+		/* fall through */
+	case 3:
+		plen = atoi(argv[2]);
+	case 2:
+		break;
+	default:
+		return Ebadarg;
+	}
+
+	if((parseip(prefix, argv[1])!=6) ||
+	 	(validlt < preflt) ||
+		(plen < 0) || (plen > 64) ||
+		(islinklocal(prefix))
+	)
+		return Ebadarg;
+
+	lifc = smalloc(sizeof(Iplifc));
+	lifc->onlink = (onlink!=0);
+	lifc->autoflag = (autoflag!=0);
+	lifc->validlt = validlt;
+	lifc->preflt = preflt;
+	lifc->origint = origint;
+
+	if(ifc->m->pref2addr!=nil)
+		ifc->m->pref2addr(prefix, ifc->mac);
+	else
+		return Ebadarg;
+	
+	sprint(addr, "%I", prefix);
+	sprint(preflen, "/%d", plen);
+	params[0] = "add";
+	params[1] = addr;
+	params[2] = preflen;
+
+	return ipifcadd(ifc, params, 3, 0, lifc);
+}
+
--- /dev/null
+++ b/os/ip/ipmux.c
@@ -1,0 +1,855 @@
+#include "u.h"
+#include "../port/lib.h"
+#include "mem.h"
+#include "dat.h"
+#include "fns.h"
+#include "../port/error.h"
+
+#include "ip.h"
+#define DPRINT if(0)print
+
+typedef struct Ipmuxrock  Ipmuxrock;
+typedef struct Ipmux      Ipmux;
+typedef struct Ip4hdr     Ip4hdr;
+typedef struct Ip6hdr     Ip6hdr;
+
+enum
+{
+	IPHDR		= 20,		/* sizeof(Ip4hdr) */
+};
+
+struct Ip4hdr
+{
+	uchar	vihl;		/* Version and header length */
+	uchar	tos;		/* Type of service */
+	uchar	length[2];	/* packet length */
+	uchar	id[2];		/* ip->identification */
+	uchar	frag[2];	/* Fragment information */
+	uchar	ttl;		/* Time to live */
+	uchar	proto;		/* Protocol */
+	uchar	cksum[2];	/* Header checksum */
+	uchar	src[4];		/* IP source */
+	uchar	dst[4];		/* IP destination */
+	uchar	data[1];	/* start of data */
+};
+
+struct Ip6hdr
+{
+	uchar vcf[4];		/* version, class label, and flow label */ 
+	uchar ploadlen[2];	/* payload length */
+	uchar proto;		/* next header, i.e. proto */
+	uchar ttl;		/* hop limit, i.e. ttl */
+	uchar src[16];		/* IP source */
+	uchar dst[16];		/* IP destination */
+};
+
+
+enum
+{
+	Tproto,
+	Tdata,
+	Tiph,
+	Tdst,
+	Tsrc,
+	Tifc,
+
+	Cother = 0,
+	Cbyte,		/* single byte */
+	Cmbyte,		/* single byte with mask */
+	Cshort,		/* single short */
+	Cmshort,	/* single short with mask */
+	Clong,		/* single long */
+	Cmlong,		/* single long with mask */
+	Cifc,
+	Cmifc,
+};
+
+char *ftname[] = 
+{
+[Tproto]	"proto",
+[Tdata]		"data",
+[Tiph]	 	"iph",
+[Tdst]		"dst",
+[Tsrc]		"src",
+[Tifc]		"ifc",
+};
+
+/*
+ *  a node in the decision tree
+ */
+struct Ipmux
+{
+	Ipmux	*yes;
+	Ipmux	*no;
+	uchar	type;		/* type of field(Txxxx) */
+	uchar	ctype;		/* tupe of comparison(Cxxxx) */
+	uchar	len;		/* length in bytes of item to compare */
+	uchar	n;		/* number of items val points to */
+	short	off;		/* offset of comparison */
+	short	eoff;		/* end offset of comparison */
+	uchar	skiphdr;	/* should offset start after ipheader */
+	uchar	*val;
+	uchar	*mask;
+	uchar	*e;		/* val+n*len*/
+
+	int	ref;		/* so we can garbage collect */
+	Conv	*conv;
+};
+
+/*
+ *  someplace to hold per conversation data
+ */
+struct Ipmuxrock
+{
+	Ipmux	*chain;
+};
+
+static int	ipmuxsprint(Ipmux*, int, char*, int);
+static void	ipmuxkick(void *x);
+
+static char*
+skipwhite(char *p)
+{
+	while(*p == ' ' || *p == '\t')
+		p++;
+	return p;
+}
+
+static char*
+follows(char *p, char c)
+{
+	char *f;
+
+	f = strchr(p, c);
+	if(f == nil)
+		return nil;
+	*f++ = 0;
+	f = skipwhite(f);
+	if(*f == 0)
+		return nil;
+	return f;
+}
+
+static Ipmux*
+parseop(char **pp)
+{
+	char *p = *pp;
+	int type, off, end, len;
+	Ipmux *f;
+
+	p = skipwhite(p);
+	if(strncmp(p, "dst", 3) == 0){
+		type = Tdst;
+		off = offsetof(Ip4hdr, dst[0]);
+		len = IPv4addrlen;
+		p += 3;
+	}
+	else if(strncmp(p, "src", 3) == 0){
+		type = Tsrc;
+		off = offsetof(Ip4hdr, src[0]);
+		len = IPv4addrlen;
+		p += 3;
+	}
+	else if(strncmp(p, "ifc", 3) == 0){
+		type = Tifc;
+		off = -IPv4addrlen;
+		len = IPv4addrlen;
+		p += 3;
+	}
+	else if(strncmp(p, "proto", 5) == 0){
+		type = Tproto;
+		off = offsetof(Ip4hdr, proto);
+		len = 1;
+		p += 5;
+	}
+	else if(strncmp(p, "data", 4) == 0 || strncmp(p, "iph", 3) == 0){
+		if(strncmp(p, "data", 4) == 0) {
+			type = Tdata;
+			p += 4;
+		}
+		else {
+			type = Tiph;
+			p += 3;
+		}
+		p = skipwhite(p);
+		if(*p != '[')
+			return nil;
+		p++;
+		off = strtoul(p, &p, 0);
+		if(off < 0 || off > (64-IPHDR))
+			return nil;
+		p = skipwhite(p);
+		if(*p != ':')
+			end = off;
+		else {
+			p++;
+			p = skipwhite(p);
+			end = strtoul(p, &p, 0);
+			if(end < off)
+				return nil;
+			p = skipwhite(p);
+		}
+		if(*p != ']')
+			return nil;
+		p++;
+		len = end - off + 1;
+	}
+	else
+		return nil;
+
+	f = smalloc(sizeof(*f));
+	f->type = type;
+	f->len = len;
+	f->off = off;
+	f->val = nil;
+	f->mask = nil;
+	f->n = 1;
+	f->ref = 1;
+	if(type == Tdata)
+		f->skiphdr = 1;
+	else
+		f->skiphdr = 0;
+
+	return f;	
+}
+
+static int
+htoi(char x)
+{
+	if(x >= '0' && x <= '9')
+		x -= '0';
+	else if(x >= 'a' && x <= 'f')
+		x -= 'a' - 10;
+	else if(x >= 'A' && x <= 'F')
+		x -= 'A' - 10;
+	else
+		x = 0;
+	return x;
+}
+
+static int
+hextoi(char *p)
+{
+	return (htoi(p[0])<<4) | htoi(p[1]);
+}
+
+static void
+parseval(uchar *v, char *p, int len)
+{
+	while(*p && len-- > 0){
+		*v++ = hextoi(p);
+		p += 2;
+	}
+}
+
+static Ipmux*
+parsemux(char *p)
+{
+	int n, nomask;
+	Ipmux *f;
+	char *val;
+	char *mask;
+	char *vals[20];
+	uchar *v;
+
+	/* parse operand */
+	f = parseop(&p);
+	if(f == nil)
+		return nil;
+
+	/* find value */
+	val = follows(p, '=');
+	if(val == nil)
+		goto parseerror;
+
+	/* parse mask */
+	mask = follows(val, '&');
+	if(mask != nil){
+		switch(f->type){
+		case Tsrc:
+		case Tdst:
+		case Tifc:
+			f->mask = smalloc(f->len);
+			v4parseip(f->mask, mask);
+			break;
+		case Tdata:
+		case Tiph:
+			f->mask = smalloc(f->len);
+			parseval(f->mask, mask, f->len);
+			break;
+		default:
+			goto parseerror;
+		}
+		nomask = 0;
+	} else {
+		nomask = 1;
+		f->mask = smalloc(f->len);
+		memset(f->mask, 0xff, f->len);
+	}
+
+	/* parse vals */
+	f->n = getfields(val, vals, sizeof(vals)/sizeof(char*), 1, "|");
+	if(f->n == 0)
+		goto parseerror;
+	f->val = smalloc(f->n*f->len);
+	v = f->val;
+	for(n = 0; n < f->n; n++){
+		switch(f->type){
+		case Tsrc:
+		case Tdst:
+		case Tifc:
+			v4parseip(v, vals[n]);
+			break;
+		case Tproto:
+		case Tdata:
+		case Tiph:
+			parseval(v, vals[n], f->len);
+			break;
+		}
+		v += f->len;
+	}
+
+	f->eoff = f->off + f->len;
+	f->e = f->val + f->n*f->len;
+	f->ctype = Cother;
+	if(f->n == 1){
+		switch(f->len){
+		case 1:
+			f->ctype = nomask ? Cbyte : Cmbyte;
+			break;
+		case 2:
+			f->ctype = nomask ? Cshort : Cmshort;
+			break;
+		case 4:
+			if(f->type == Tifc)
+				f->ctype = nomask ? Cifc : Cmifc;
+			else
+				f->ctype = nomask ? Clong : Cmlong;
+			break;
+		}
+	}
+	return f;
+
+parseerror:
+	if(f->mask)
+		free(f->mask);
+	if(f->val)
+		free(f->val);
+	free(f);
+	return nil;
+}
+
+/*
+ *  Compare relative ordering of two ipmuxs.  This doesn't compare the
+ *  values, just the fields being looked at.  
+ *
+ *  returns:	<0 if a is a more specific match
+ *		 0 if a and b are matching on the same fields
+ *		>0 if b is a more specific match
+ */
+static int
+ipmuxcmp(Ipmux *a, Ipmux *b)
+{
+	int n;
+
+	/* compare types, lesser ones are more important */
+	n = a->type - b->type;
+	if(n != 0)
+		return n;
+
+	/* compare offsets, call earlier ones more specific */
+	n = (a->off+((int)a->skiphdr)*offsetof(Ip4hdr, data[0])) - 
+		(b->off+((int)b->skiphdr)*offsetof(Ip4hdr, data[0]));
+	if(n != 0)
+		return n;
+
+	/* compare match lengths, longer ones are more specific */
+	n = b->len - a->len;
+	if(n != 0)
+		return n;
+
+	/*
+	 *  if we get here we have two entries matching
+	 *  the same bytes of the record.  Now check
+	 *  the mask for equality.  Longer masks are
+	 *  more specific.
+	 */
+	if(a->mask != nil && b->mask == nil)
+		return -1;
+	if(a->mask == nil && b->mask != nil)
+		return 1;
+	if(a->mask != nil && b->mask != nil){
+		n = memcmp(b->mask, a->mask, a->len);
+		if(n != 0)
+			return n;
+	}
+	return 0;
+}
+
+/*
+ *  Compare the values of two ipmuxs.  We're assuming that ipmuxcmp
+ *  returned 0 comparing them.
+ */
+static int
+ipmuxvalcmp(Ipmux *a, Ipmux *b)
+{
+	int n;
+
+	n = b->len*b->n - a->len*a->n;
+	if(n != 0)
+		return n;
+	return memcmp(a->val, b->val, a->len*a->n);
+} 
+
+/*
+ *  add onto an existing ipmux chain in the canonical comparison
+ *  order
+ */
+static void
+ipmuxchain(Ipmux **l, Ipmux *f)
+{
+	for(; *l; l = &(*l)->yes)
+		if(ipmuxcmp(f, *l) < 0)
+			break;
+	f->yes = *l;
+	*l = f;
+}
+
+/*
+ *  copy a tree
+ */
+static Ipmux*
+ipmuxcopy(Ipmux *f)
+{
+	Ipmux *nf;
+
+	if(f == nil)
+		return nil;
+	nf = smalloc(sizeof *nf);
+	*nf = *f;
+	nf->no = ipmuxcopy(f->no);
+	nf->yes = ipmuxcopy(f->yes);
+	nf->val = smalloc(f->n*f->len);
+	nf->e = nf->val + f->len*f->n;
+	memmove(nf->val, f->val, f->n*f->len);
+	return nf;
+}
+
+static void
+ipmuxfree(Ipmux *f)
+{
+	if(f->val != nil)
+		free(f->val);
+	free(f);
+}
+
+static void
+ipmuxtreefree(Ipmux *f)
+{
+	if(f == nil)
+		return;
+	if(f->no != nil)
+		ipmuxfree(f->no);
+	if(f->yes != nil)
+		ipmuxfree(f->yes);
+	ipmuxfree(f);
+}
+
+/*
+ *  merge two trees
+ */
+static Ipmux*
+ipmuxmerge(Ipmux *a, Ipmux *b)
+{
+	int n;
+	Ipmux *f;
+
+	if(a == nil)
+		return b;
+	if(b == nil)
+		return a;
+	n = ipmuxcmp(a, b);
+	if(n < 0){
+		f = ipmuxcopy(b);
+		a->yes = ipmuxmerge(a->yes, b);
+		a->no = ipmuxmerge(a->no, f);
+		return a;
+	}
+	if(n > 0){
+		f = ipmuxcopy(a);
+		b->yes = ipmuxmerge(b->yes, a);
+		b->no = ipmuxmerge(b->no, f);
+		return b;
+	}
+	if(ipmuxvalcmp(a, b) == 0){
+		a->yes = ipmuxmerge(a->yes, b->yes);
+		a->no = ipmuxmerge(a->no, b->no);
+		a->ref++;
+		ipmuxfree(b);
+		return a;
+	}
+	a->no = ipmuxmerge(a->no, b);
+	return a;
+}
+
+/*
+ *  remove a chain from a demux tree.  This is like merging accept that
+ *  we remove instead of insert.
+ */
+static int
+ipmuxremove(Ipmux **l, Ipmux *f)
+{
+	int n, rv;
+	Ipmux *ft;
+
+	if(f == nil)
+		return 0;		/* we've removed it all */
+	if(*l == nil)
+		return -1;
+
+	ft = *l;
+	n = ipmuxcmp(ft, f);
+	if(n < 0){
+		/* *l is maching an earlier field, descend both paths */
+		rv = ipmuxremove(&ft->yes, f);
+		rv += ipmuxremove(&ft->no, f);
+		return rv;
+	}
+	if(n > 0){
+		/* f represents an earlier field than *l, this should be impossible */
+		return -1;
+	}
+
+	/* if we get here f and *l are comparing the same fields */
+	if(ipmuxvalcmp(ft, f) != 0){
+		/* different values mean mutually exclusive */
+		return ipmuxremove(&ft->no, f);
+	}
+
+	/* we found a match */
+	if(--(ft->ref) == 0){
+		/*
+		 *  a dead node implies the whole yes side is also dead.
+		 *  since our chain is constrained to be on that side,
+		 *  we're done.
+		 */
+		ipmuxtreefree(ft->yes);
+		*l = ft->no;
+		ipmuxfree(ft);
+		return 0;
+	}
+
+	/*
+	 *  free the rest of the chain.  it is constrained to match the
+	 *  yes side.
+	 */
+	return ipmuxremove(&ft->yes, f->yes);
+}
+
+/*
+ *  connection request is a semi separated list of filters
+ *  e.g. proto=17;dat[0:4]=11aa22bb;ifc=135.104.9.2&255.255.255.0
+ *
+ *  there's no protection against overlapping specs.
+ */
+static char*
+ipmuxconnect(Conv *c, char **argv, int argc)
+{
+	int i, n;
+	char *field[10];
+	Ipmux *mux, *chain;
+	Ipmuxrock *r;
+	Fs *f;
+
+	f = c->p->f;
+
+	if(argc != 2)
+		return Ebadarg;
+
+	n = getfields(argv[1], field, nelem(field), 1, ";");
+	if(n <= 0)
+		return Ebadarg;
+
+	chain = nil;
+	mux = nil;
+	for(i = 0; i < n; i++){
+		mux = parsemux(field[i]);
+		if(mux == nil){
+			ipmuxtreefree(chain);
+			return Ebadarg;
+		}
+		ipmuxchain(&chain, mux);
+	}
+	if(chain == nil)
+		return Ebadarg;
+	mux->conv = c;
+
+	/* save a copy of the chain so we can later remove it */
+	mux = ipmuxcopy(chain);
+	r = (Ipmuxrock*)(c->ptcl);
+	r->chain = chain;
+
+	/* add the chain to the protocol demultiplexor tree */
+	wlock(f);
+	f->ipmux->priv = ipmuxmerge(f->ipmux->priv, mux);
+	wunlock(f);
+
+	Fsconnected(c, nil);
+	return nil;
+}
+
+static int
+ipmuxstate(Conv *c, char *state, int n)
+{
+	Ipmuxrock *r;
+	
+	r = (Ipmuxrock*)(c->ptcl);
+	return ipmuxsprint(r->chain, 0, state, n);
+}
+
+static void
+ipmuxcreate(Conv *c)
+{
+	Ipmuxrock *r;
+
+	c->rq = qopen(64*1024, Qmsg, 0, c);
+	c->wq = qopen(64*1024, Qkick, ipmuxkick, c);
+	r = (Ipmuxrock*)(c->ptcl);
+	r->chain = nil;
+}
+
+static char*
+ipmuxannounce(Conv*, char**, int)
+{
+	return "ipmux does not support announce";
+}
+
+static void
+ipmuxclose(Conv *c)
+{
+	Ipmuxrock *r;
+	Fs *f = c->p->f;
+
+	r = (Ipmuxrock*)(c->ptcl);
+
+	qclose(c->rq);
+	qclose(c->wq);
+	qclose(c->eq);
+	ipmove(c->laddr, IPnoaddr);
+	ipmove(c->raddr, IPnoaddr);
+	c->lport = 0;
+	c->rport = 0;
+
+	wlock(f);
+	ipmuxremove(&(c->p->priv), r->chain);
+	wunlock(f);
+	ipmuxtreefree(r->chain);
+	r->chain = nil;
+}
+
+/*
+ *  takes a fully formed ip packet and just passes it down
+ *  the stack
+ */
+static void
+ipmuxkick(void *x)
+{
+	Conv *c = x;
+	Block *bp;
+
+	bp = qget(c->wq);
+	if(bp == nil)
+		return;
+	else {
+		Ip4hdr *ih4 = (Ip4hdr*)(bp->rp);
+		if((ih4->vihl)&0xF0 != 0x60)
+			ipoput4(c->p->f, bp, 0, ih4->ttl, ih4->tos, nil);
+		else {
+			Ip6hdr *ih6 = (Ip6hdr*)(bp->rp);
+			ipoput6(c->p->f, bp, 0, ih6->ttl, 0, nil);
+		}
+	}
+}
+
+static void
+ipmuxiput(Proto *p, Ipifc *ifc, Block *bp)
+{
+	int len, hl;
+	Fs *f = p->f;
+	uchar *m, *h, *v, *e, *ve, *hp;
+	Conv *c;
+	Ipmux *mux;
+	Ip4hdr *ip;
+	Ip6hdr *ip6;
+
+	ip = (Ip4hdr*)bp->rp;
+	hl = (ip->vihl&0x0F)<<2;
+
+	if(p->priv == nil)
+		goto nomatch;
+
+	h = bp->rp;
+	len = BLEN(bp);
+
+	/* run the v4 filter */
+	rlock(f);
+	c = nil;
+	mux = f->ipmux->priv;
+	while(mux != nil){
+		if(mux->eoff > len){
+			mux = mux->no;
+			continue;
+		}
+		hp = h + mux->off + ((int)mux->skiphdr)*hl;
+		switch(mux->ctype){
+		case Cbyte:
+			if(*mux->val == *hp)
+				goto yes;
+			break;
+		case Cmbyte:
+			if((*hp & *mux->mask) == *mux->val)
+				goto yes;
+			break;
+		case Cshort:
+			if(*((ushort*)mux->val) == *(ushort*)hp)
+				goto yes;
+			break;
+		case Cmshort:
+			if((*(ushort*)hp & (*((ushort*)mux->mask))) == *((ushort*)mux->val))
+				goto yes;
+			break;
+		case Clong:
+			if(*((ulong*)mux->val) == *(ulong*)hp)
+				goto yes;
+			break;
+		case Cmlong:
+			if((*(ulong*)hp & (*((ulong*)mux->mask))) == *((ulong*)mux->val))
+				goto yes;
+			break;
+		case Cifc:
+			if(*((ulong*)mux->val) == *(ulong*)(ifc->lifc->local + IPv4off))
+				goto yes;
+			break;
+		case Cmifc:
+			if((*(ulong*)(ifc->lifc->local + IPv4off) & (*((ulong*)mux->mask))) == *((ulong*)mux->val))
+				goto yes;
+			break;
+		default:
+			v = mux->val;
+			for(e = mux->e; v < e; v = ve){
+				m = mux->mask;
+				hp = h + mux->off;
+				for(ve = v + mux->len; v < ve; v++){
+					if((*hp++ & *m++) != *v)
+						break;
+				}
+				if(v == ve)
+					goto yes;
+			}
+		}
+		mux = mux->no;
+		continue;
+yes:
+		if(mux->conv != nil)
+			c = mux->conv;
+		mux = mux->yes;
+	}
+	runlock(f);
+
+	if(c != nil){
+		/* tack on interface address */
+		bp = padblock(bp, IPaddrlen);
+		ipmove(bp->rp, ifc->lifc->local);
+		bp = concatblock(bp);
+		if(bp != nil)
+			if(qpass(c->rq, bp) < 0)
+				print("Q");
+		return;
+	}
+
+nomatch:
+	/* doesn't match any filter, hand it to the specific protocol handler */
+	ip = (Ip4hdr*)bp->rp;
+	if((ip->vihl&0xF0)==0x40) {
+		p = f->t2p[ip->proto];
+	} else {
+		ip6 = (Ip6hdr*)bp->rp;
+		p = f->t2p[ip6->proto];
+	}
+	if(p && p->rcv)
+		(*p->rcv)(p, ifc, bp);
+	else
+		freeblist(bp);
+	return;
+}
+
+static int
+ipmuxsprint(Ipmux *mux, int level, char *buf, int len)
+{
+	int i, j, n;
+	uchar *v;
+
+	n = 0;
+	for(i = 0; i < level; i++)
+		n += snprint(buf+n, len-n, " ");
+	if(mux == nil){
+		n += snprint(buf+n, len-n, "\n");
+		return n;
+	}
+	n += snprint(buf+n, len-n, "h[%d:%d]&", 
+               mux->off+((int)mux->skiphdr)*((int)offsetof(Ip4hdr, data[0])), 
+               mux->off+(((int)mux->skiphdr)*((int)offsetof(Ip4hdr, data[0])))+mux->len-1);
+	for(i = 0; i < mux->len; i++)
+		n += snprint(buf+n, len - n, "%2.2ux", mux->mask[i]);
+	n += snprint(buf+n, len-n, "=");
+	v = mux->val;
+	for(j = 0; j < mux->n; j++){
+		for(i = 0; i < mux->len; i++)
+			n += snprint(buf+n, len - n, "%2.2ux", *v++);
+		n += snprint(buf+n, len-n, "|");
+	}
+	n += snprint(buf+n, len-n, "\n");
+	level++;
+	n += ipmuxsprint(mux->no, level, buf+n, len-n);
+	n += ipmuxsprint(mux->yes, level, buf+n, len-n);
+	return n;
+}
+
+static int
+ipmuxstats(Proto *p, char *buf, int len)
+{
+	int n;
+	Fs *f = p->f;
+
+	rlock(f);
+	n = ipmuxsprint(p->priv, 0, buf, len);
+	runlock(f);
+
+	return n;
+}
+
+void
+ipmuxinit(Fs *f)
+{
+	Proto *ipmux;
+
+	ipmux = smalloc(sizeof(Proto));
+	ipmux->priv = nil;
+	ipmux->name = "ipmux";
+	ipmux->connect = ipmuxconnect;
+	ipmux->announce = ipmuxannounce;
+	ipmux->state = ipmuxstate;
+	ipmux->create = ipmuxcreate;
+	ipmux->close = ipmuxclose;
+	ipmux->rcv = ipmuxiput;
+	ipmux->ctl = nil;
+	ipmux->advise = nil;
+	ipmux->stats = ipmuxstats;
+	ipmux->ipproto = -1;
+	ipmux->nc = 64;
+	ipmux->ptclsize = sizeof(Ipmuxrock);
+
+	f->ipmux = ipmux;			/* hack for Fsrcvpcol */
+
+	Fsproto(f, ipmux);
+}
--- /dev/null
+++ b/os/ip/iproute.c
@@ -1,0 +1,852 @@
+#include	"u.h"
+#include	"../port/lib.h"
+#include	"mem.h"
+#include	"dat.h"
+#include	"fns.h"
+#include	"../port/error.h"
+
+#include	"ip.h"
+
+static void	walkadd(Fs*, Route**, Route*);
+static void	addnode(Fs*, Route**, Route*);
+static void	calcd(Route*);
+
+/* these are used for all instances of IP */
+Route*	v4freelist;
+Route*	v6freelist;
+RWlock	routelock;
+ulong	v4routegeneration, v6routegeneration;
+
+static void
+freeroute(Route *r)
+{
+	Route **l;
+
+	r->left = nil;
+	r->right = nil;
+	if(r->type & Rv4)
+		l = &v4freelist;
+	else
+		l = &v6freelist;
+	r->mid = *l;
+	*l = r;
+}
+
+static Route*
+allocroute(int type)
+{
+	Route *r;
+	int n;
+	Route **l;
+
+	if(type & Rv4){
+		n = sizeof(RouteTree) + sizeof(V4route);
+		l = &v4freelist;
+	} else {
+		n = sizeof(RouteTree) + sizeof(V6route);
+		l = &v6freelist;
+	}
+
+	r = *l;
+	if(r != nil){
+		*l = r->mid;
+	} else {
+		r = malloc(n);
+		if(r == nil)
+			panic("out of routing nodes");
+	}
+	memset(r, 0, n);
+	r->type = type;
+	r->ifc = nil;
+	r->ref = 1;
+
+	return r;
+}
+
+static void
+addqueue(Route **q, Route *r)
+{
+	Route *l;
+
+	if(r == nil)
+		return;
+
+	l = allocroute(r->type);
+	l->mid = *q;
+	*q = l;
+	l->left = r;
+}
+
+/*
+ *   compare 2 v6 addresses
+ */
+static int
+lcmp(ulong *a, ulong *b)
+{
+	int i;
+
+	for(i = 0; i < IPllen; i++){
+		if(a[i] > b[i])
+			return 1;
+		if(a[i] < b[i])
+			return -1;
+	}
+	return 0;
+}
+
+/*
+ *  compare 2 v4 or v6 ranges
+ */
+enum
+{
+	Rpreceeds,
+	Rfollows,
+	Requals,
+	Rcontains,
+	Rcontained,
+};
+
+static int
+rangecompare(Route *a, Route *b)
+{
+	if(a->type & Rv4){
+		if(a->v4.endaddress < b->v4.address)
+			return Rpreceeds;
+
+		if(a->v4.address > b->v4.endaddress)
+			return Rfollows;
+
+		if(a->v4.address <= b->v4.address
+		&& a->v4.endaddress >= b->v4.endaddress){
+			if(a->v4.address == b->v4.address
+			&& a->v4.endaddress == b->v4.endaddress)
+				return Requals;
+			return Rcontains;
+		}
+		return Rcontained;
+	}
+
+	if(lcmp(a->v6.endaddress, b->v6.address) < 0)
+		return Rpreceeds;
+
+	if(lcmp(a->v6.address, b->v6.endaddress) > 0)
+		return Rfollows;
+
+	if(lcmp(a->v6.address, b->v6.address) <= 0
+	&& lcmp(a->v6.endaddress, b->v6.endaddress) >= 0){
+		if(lcmp(a->v6.address, b->v6.address) == 0
+		&& lcmp(a->v6.endaddress, b->v6.endaddress) == 0)
+				return Requals;
+		return Rcontains;
+	}
+
+	return Rcontained;
+}
+
+static void
+copygate(Route *old, Route *new)
+{
+	if(new->type & Rv4)
+		memmove(old->v4.gate, new->v4.gate, IPv4addrlen);
+	else
+		memmove(old->v6.gate, new->v6.gate, IPaddrlen);
+}
+
+/*
+ *  walk down a tree adding nodes back in
+ */
+static void
+walkadd(Fs *f, Route **root, Route *p)
+{
+	Route *l, *r;
+
+	l = p->left;
+	r = p->right;
+	p->left = 0;
+	p->right = 0;
+	addnode(f, root, p);
+	if(l)
+		walkadd(f, root, l);
+	if(r)
+		walkadd(f, root, r);
+}
+
+/*
+ *  calculate depth
+ */
+static void
+calcd(Route *p)
+{
+	Route *q;
+	int d;
+
+	if(p) {
+		d = 0;
+		q = p->left;
+		if(q)
+			d = q->depth;
+		q = p->right;
+		if(q && q->depth > d)
+			d = q->depth;
+		q = p->mid;
+		if(q && q->depth > d)
+			d = q->depth;
+		p->depth = d+1;
+	}
+}
+
+/*
+ *  balance the tree at the current node
+ */
+static void
+balancetree(Route **cur)
+{
+	Route *p, *l, *r;
+	int dl, dr;
+
+	/*
+	 * if left and right are
+	 * too out of balance,
+	 * rotate tree node
+	 */
+	p = *cur;
+	dl = 0; if(l = p->left) dl = l->depth;
+	dr = 0; if(r = p->right) dr = r->depth;
+
+	if(dl > dr+1) {
+		p->left = l->right;
+		l->right = p;
+		*cur = l;
+		calcd(p);
+		calcd(l);
+	} else
+	if(dr > dl+1) {
+		p->right = r->left;
+		r->left = p;
+		*cur = r;
+		calcd(p);
+		calcd(r);
+	} else
+		calcd(p);
+}
+
+/*
+ *  add a new node to the tree
+ */
+static void
+addnode(Fs *f, Route **cur, Route *new)
+{
+	Route *p;
+
+	p = *cur;
+	if(p == 0) {
+		*cur = new;
+		new->depth = 1;
+		return;
+	}
+
+	switch(rangecompare(new, p)){
+	case Rpreceeds:
+		addnode(f, &p->left, new);
+		break;
+	case Rfollows:
+		addnode(f, &p->right, new);
+		break;
+	case Rcontains:
+		/*
+		 *  if new node is superset
+		 *  of tree node,
+		 *  replace tree node and
+		 *  queue tree node to be
+		 *  merged into root.
+		 */
+		*cur = new;
+		new->depth = 1;
+		addqueue(&f->queue, p);
+		break;
+	case Requals:
+		/*
+		 *  supercede the old entry if the old one isn't
+		 *  a local interface.
+		 */
+		if((p->type & Rifc) == 0){
+			p->type = new->type;
+			p->ifcid = -1;
+			copygate(p, new);
+		} else if(new->type & Rifc)
+			p->ref++;
+		freeroute(new);
+		break;
+	case Rcontained:
+		addnode(f, &p->mid, new);
+		break;
+	}
+	
+	balancetree(cur);
+}
+
+#define	V4H(a)	((a&0x07ffffff)>>(32-Lroot-5))
+
+void
+v4addroute(Fs *f, char *tag, uchar *a, uchar *mask, uchar *gate, int type)
+{
+	Route *p;
+	ulong sa;
+	ulong m;
+	ulong ea;
+	int h, eh;
+
+	m = nhgetl(mask);
+	sa = nhgetl(a) & m;
+	ea = sa | ~m;
+
+	eh = V4H(ea);
+	for(h=V4H(sa); h<=eh; h++) {
+		p = allocroute(Rv4 | type);
+		p->v4.address = sa;
+		p->v4.endaddress = ea;
+		memmove(p->v4.gate, gate, sizeof(p->v4.gate));
+		memmove(p->tag, tag, sizeof(p->tag));
+
+		wlock(&routelock);
+		addnode(f, &f->v4root[h], p);
+		while(p = f->queue) {
+			f->queue = p->mid;
+			walkadd(f, &f->v4root[h], p->left);
+			freeroute(p);
+		}
+		wunlock(&routelock);
+	}
+	v4routegeneration++;
+
+	ipifcaddroute(f, Rv4, a, mask, gate, type);
+}
+
+#define	V6H(a)	(((a)[IPllen-1] & 0x07ffffff)>>(32-Lroot-5))
+#define ISDFLT(a, mask, tag) ((ipcmp((a),v6Unspecified)==0) && (ipcmp((mask),v6Unspecified)==0) && (strcmp((tag), "ra")!=0))
+
+void
+v6addroute(Fs *f, char *tag, uchar *a, uchar *mask, uchar *gate, int type)
+{
+	Route *p;
+	ulong sa[IPllen], ea[IPllen];
+	ulong x, y;
+	int h, eh;
+
+	/*
+	if(ISDFLT(a, mask, tag))
+		f->v6p->cdrouter = -1;
+	*/
+
+
+	for(h = 0; h < IPllen; h++){
+		x = nhgetl(a+4*h);
+		y = nhgetl(mask+4*h);
+		sa[h] = x & y;
+		ea[h] = x | ~y;
+	}
+
+	eh = V6H(ea);
+	for(h = V6H(sa); h <= eh; h++) {
+		p = allocroute(type);
+		memmove(p->v6.address, sa, IPaddrlen);
+		memmove(p->v6.endaddress, ea, IPaddrlen);
+		memmove(p->v6.gate, gate, IPaddrlen);
+		memmove(p->tag, tag, sizeof(p->tag));
+
+		wlock(&routelock);
+		addnode(f, &f->v6root[h], p);
+		while(p = f->queue) {
+			f->queue = p->mid;
+			walkadd(f, &f->v6root[h], p->left);
+			freeroute(p);
+		}
+		wunlock(&routelock);
+	}
+	v6routegeneration++;
+
+	ipifcaddroute(f, 0, a, mask, gate, type);
+}
+
+Route**
+looknode(Route **cur, Route *r)
+{
+	Route *p;
+
+	for(;;){
+		p = *cur;
+		if(p == 0)
+			return 0;
+	
+		switch(rangecompare(r, p)){
+		case Rcontains:
+			return 0;
+		case Rpreceeds:
+			cur = &p->left;
+			break;
+		case Rfollows:
+			cur = &p->right;
+			break;
+		case Rcontained:
+			cur = &p->mid;
+			break;
+		case Requals:
+			return cur;
+		}
+	}
+}
+
+void
+v4delroute(Fs *f, uchar *a, uchar *mask, int dolock)
+{
+	Route **r, *p;
+	Route rt;
+	int h, eh;
+	ulong m;
+
+	m = nhgetl(mask);
+	rt.v4.address = nhgetl(a) & m;
+	rt.v4.endaddress = rt.v4.address | ~m;
+	rt.type = Rv4;
+
+	eh = V4H(rt.v4.endaddress);
+	for(h=V4H(rt.v4.address); h<=eh; h++) {
+		if(dolock)
+			wlock(&routelock);
+		r = looknode(&f->v4root[h], &rt);
+		if(r) {
+			p = *r;
+			if(--(p->ref) == 0){
+				*r = 0;
+				addqueue(&f->queue, p->left);
+				addqueue(&f->queue, p->mid);
+				addqueue(&f->queue, p->right);
+				freeroute(p);
+				while(p = f->queue) {
+					f->queue = p->mid;
+					walkadd(f, &f->v4root[h], p->left);
+					freeroute(p);
+				}
+			}
+		}
+		if(dolock)
+			wunlock(&routelock);
+	}
+	v4routegeneration++;
+
+	ipifcremroute(f, Rv4, a, mask);
+}
+
+void
+v6delroute(Fs *f, uchar *a, uchar *mask, int dolock)
+{
+	Route **r, *p;
+	Route rt;
+	int h, eh;
+	ulong x, y;
+
+	for(h = 0; h < IPllen; h++){
+		x = nhgetl(a+4*h);
+		y = nhgetl(mask+4*h);
+		rt.v6.address[h] = x & y;
+		rt.v6.endaddress[h] = x | ~y;
+	}
+	rt.type = 0;
+
+	eh = V6H(rt.v6.endaddress);
+	for(h=V6H(rt.v6.address); h<=eh; h++) {
+		if(dolock)
+			wlock(&routelock);
+		r = looknode(&f->v6root[h], &rt);
+		if(r) {
+			p = *r;
+			if(--(p->ref) == 0){
+				*r = 0;
+				addqueue(&f->queue, p->left);
+				addqueue(&f->queue, p->mid);
+				addqueue(&f->queue, p->right);
+				freeroute(p);
+				while(p = f->queue) {
+					f->queue = p->mid;
+					walkadd(f, &f->v6root[h], p->left);
+					freeroute(p);
+				}
+			}
+		}
+		if(dolock)
+			wunlock(&routelock);
+	}
+	v6routegeneration++;
+
+	ipifcremroute(f, 0, a, mask);
+}
+
+Route*
+v4lookup(Fs *f, uchar *a, Conv *c)
+{
+	Route *p, *q;
+	ulong la;
+	uchar gate[IPaddrlen];
+	Ipifc *ifc;
+
+	if(c != nil && c->r != nil && c->r->ifc != nil && c->rgen == v4routegeneration)
+		return c->r;
+
+	la = nhgetl(a);
+	q = nil;
+	for(p=f->v4root[V4H(la)]; p;)
+		if(la >= p->v4.address) {
+			if(la <= p->v4.endaddress) {
+				q = p;
+				p = p->mid;
+			} else
+				p = p->right;
+		} else
+			p = p->left;
+
+	if(q && (q->ifc == nil || q->ifcid != q->ifc->ifcid)){
+		if(q->type & Rifc) {
+			hnputl(gate+IPv4off, q->v4.address);
+			memmove(gate, v4prefix, IPv4off);
+		} else
+			v4tov6(gate, q->v4.gate);
+		ifc = findipifc(f, gate, q->type);
+		if(ifc == nil)
+			return nil;
+		q->ifc = ifc;
+		q->ifcid = ifc->ifcid;
+	}
+
+	if(c != nil){
+		c->r = q;
+		c->rgen = v4routegeneration;
+	}
+
+	return q;
+}
+
+Route*
+v6lookup(Fs *f, uchar *a, Conv *c)
+{
+	Route *p, *q;
+	ulong la[IPllen];
+	int h;
+	ulong x, y;
+	uchar gate[IPaddrlen];
+	Ipifc *ifc;
+
+	if(memcmp(a, v4prefix, IPv4off) == 0){
+		q = v4lookup(f, a+IPv4off, c);
+		if(q != nil)
+			return q;
+	}
+
+	if(c != nil && c->r != nil && c->r->ifc != nil && c->rgen == v6routegeneration)
+		return c->r;
+
+	for(h = 0; h < IPllen; h++)
+		la[h] = nhgetl(a+4*h);
+
+	q = 0;
+	for(p=f->v6root[V6H(la)]; p;){
+		for(h = 0; h < IPllen; h++){
+			x = la[h];
+			y = p->v6.address[h];
+			if(x == y)
+				continue;
+			if(x < y){
+				p = p->left;
+				goto next;
+			}
+			break;
+		}
+		for(h = 0; h < IPllen; h++){
+			x = la[h];
+			y = p->v6.endaddress[h];
+			if(x == y)
+				continue;
+			if(x > y){
+				p = p->right;
+				goto next;
+			}
+			break;
+		}
+		q = p;
+		p = p->mid;
+next:		;
+	}
+
+	if(q && (q->ifc == nil || q->ifcid != q->ifc->ifcid)){
+		if(q->type & Rifc) {
+			for(h = 0; h < IPllen; h++)
+				hnputl(gate+4*h, q->v6.address[h]);
+			ifc = findipifc(f, gate, q->type);
+		} else
+			ifc = findipifc(f, q->v6.gate, q->type);
+		if(ifc == nil)
+			return nil;
+		q->ifc = ifc;
+		q->ifcid = ifc->ifcid;
+	}
+	if(c != nil){
+		c->r = q;
+		c->rgen = v6routegeneration;
+	}
+	
+	return q;
+}
+
+void
+routetype(int type, char *p)
+{
+	memset(p, ' ', 4);
+	p[4] = 0;
+	if(type & Rv4)
+		*p++ = '4';
+	else
+		*p++ = '6';
+	if(type & Rifc)
+		*p++ = 'i';
+	if(type & Runi)
+		*p++ = 'u';
+	else if(type & Rbcast)
+		*p++ = 'b';
+	else if(type & Rmulti)
+		*p++ = 'm';
+	if(type & Rptpt)
+		*p = 'p';
+}
+
+char *rformat = "%-15I %-4M %-15I %4.4s %4.4s %3s\n";
+
+void
+convroute(Route *r, uchar *addr, uchar *mask, uchar *gate, char *t, int *nifc)
+{
+	int i;
+
+	if(r->type & Rv4){
+		memmove(addr, v4prefix, IPv4off);
+		hnputl(addr+IPv4off, r->v4.address);
+		memset(mask, 0xff, IPv4off);
+		hnputl(mask+IPv4off, ~(r->v4.endaddress ^ r->v4.address));
+		memmove(gate, v4prefix, IPv4off);
+		memmove(gate+IPv4off, r->v4.gate, IPv4addrlen);
+	} else {
+		for(i = 0; i < IPllen; i++){
+			hnputl(addr + 4*i, r->v6.address[i]);
+			hnputl(mask + 4*i, ~(r->v6.endaddress[i] ^ r->v6.address[i]));
+		}
+		memmove(gate, r->v6.gate, IPaddrlen);
+	}
+
+	routetype(r->type, t);
+
+	if(r->ifc)
+		*nifc = r->ifc->conv->x;
+	else
+		*nifc = -1;
+}
+
+/*
+ *  this code is not in rr to reduce stack size
+ */
+static void
+sprintroute(Route *r, Routewalk *rw)
+{
+	int nifc, n;
+	char t[5], *iname, ifbuf[5];
+	uchar addr[IPaddrlen], mask[IPaddrlen], gate[IPaddrlen];
+	char *p;
+
+	convroute(r, addr, mask, gate, t, &nifc);
+	iname = "-";
+	if(nifc != -1) {
+		iname = ifbuf;
+		snprint(ifbuf, sizeof ifbuf, "%d", nifc);
+	}
+	p = seprint(rw->p, rw->e, rformat, addr, mask, gate, t, r->tag, iname);
+	if(rw->o < 0){
+		n = p - rw->p;
+		if(n > -rw->o){
+			memmove(rw->p, rw->p-rw->o, n+rw->o);
+			rw->p = p + rw->o;
+		}
+		rw->o += n;
+	} else
+		rw->p = p;
+}
+
+/*
+ *  recurse descending tree, applying the function in Routewalk
+ */
+static int
+rr(Route *r, Routewalk *rw)
+{
+	int h;
+
+	if(rw->e <= rw->p)
+		return 0;
+	if(r == nil)
+		return 1;
+
+	if(rr(r->left, rw) == 0)
+		return 0;
+
+	if(r->type & Rv4)
+		h = V4H(r->v4.address);
+	else
+		h = V6H(r->v6.address);
+
+	if(h == rw->h)
+		rw->walk(r, rw);
+
+	if(rr(r->mid, rw) == 0)
+		return 0;
+
+	return rr(r->right, rw);
+}
+
+void
+ipwalkroutes(Fs *f, Routewalk *rw)
+{
+	rlock(&routelock);
+	if(rw->e > rw->p) {
+		for(rw->h = 0; rw->h < nelem(f->v4root); rw->h++)
+			if(rr(f->v4root[rw->h], rw) == 0)
+				break;
+	}
+	if(rw->e > rw->p) {
+		for(rw->h = 0; rw->h < nelem(f->v6root); rw->h++)
+			if(rr(f->v6root[rw->h], rw) == 0)
+				break;
+	}
+	runlock(&routelock);
+}
+
+long
+routeread(Fs *f, char *p, ulong offset, int n)
+{
+	Routewalk rw;
+
+	rw.p = p;
+	rw.e = p+n;
+	rw.o = -offset;
+	rw.walk = sprintroute;
+
+	ipwalkroutes(f, &rw);
+
+	return rw.p - p;
+}
+
+/*
+ *  this code is not in routeflush to reduce stack size
+ */
+void
+delroute(Fs *f, Route *r, int dolock)
+{
+	uchar addr[IPaddrlen];
+	uchar mask[IPaddrlen];
+	uchar gate[IPaddrlen];
+	char t[5];
+	int nifc;
+
+	convroute(r, addr, mask, gate, t, &nifc);
+	if(r->type & Rv4)
+		v4delroute(f, addr+IPv4off, mask+IPv4off, dolock);
+	else
+		v6delroute(f, addr, mask, dolock);
+}
+
+/*
+ *  recurse until one route is deleted
+ *    returns 0 if nothing is deleted, 1 otherwise
+ */
+int
+routeflush(Fs *f, Route *r, char *tag)
+{
+	if(r == nil)
+		return 0;
+	if(routeflush(f, r->mid, tag))
+		return 1;
+	if(routeflush(f, r->left, tag))
+		return 1;
+	if(routeflush(f, r->right, tag))
+		return 1;
+	if((r->type & Rifc) == 0){
+		if(tag == nil || strncmp(tag, r->tag, sizeof(r->tag)) == 0){
+			delroute(f, r, 0);
+			return 1;
+		}
+	}
+	return 0;
+}
+
+long
+routewrite(Fs *f, Chan *c, char *p, int n)
+{
+	int h, changed;
+	char *tag;
+	Cmdbuf *cb;
+	uchar addr[IPaddrlen];
+	uchar mask[IPaddrlen];
+	uchar gate[IPaddrlen];
+	IPaux *a, *na;
+
+	cb = parsecmd(p, n);
+	if(waserror()){
+		free(cb);
+		nexterror();
+	}
+
+	if(strcmp(cb->f[0], "flush") == 0){
+		tag = cb->f[1];
+		for(h = 0; h < nelem(f->v4root); h++)
+			for(changed = 1; changed;){
+				wlock(&routelock);
+				changed = routeflush(f, f->v4root[h], tag);
+				wunlock(&routelock);
+			}
+		for(h = 0; h < nelem(f->v6root); h++)
+			for(changed = 1; changed;){
+				wlock(&routelock);
+				changed = routeflush(f, f->v6root[h], tag);
+				wunlock(&routelock);
+			}
+	} else if(strcmp(cb->f[0], "remove") == 0){
+		if(cb->nf < 3)
+			error(Ebadarg);
+		parseip(addr, cb->f[1]);
+		parseipmask(mask, cb->f[2]);
+		if(memcmp(addr, v4prefix, IPv4off) == 0)
+			v4delroute(f, addr+IPv4off, mask+IPv4off, 1);
+		else
+			v6delroute(f, addr, mask, 1);
+	} else if(strcmp(cb->f[0], "add") == 0){
+		if(cb->nf < 4)
+			error(Ebadarg);
+		parseip(addr, cb->f[1]);
+		parseipmask(mask, cb->f[2]);
+		parseip(gate, cb->f[3]);
+		tag = "none";
+		if(c != nil){
+			a = c->aux;
+			tag = a->tag;
+		}
+		if(memcmp(addr, v4prefix, IPv4off) == 0)
+			v4addroute(f, tag, addr+IPv4off, mask+IPv4off, gate+IPv4off, 0);
+		else
+			v6addroute(f, tag, addr, mask, gate, 0);
+	} else if(strcmp(cb->f[0], "tag") == 0) {
+		if(cb->nf < 2)
+			error(Ebadarg);
+
+		a = c->aux;
+		na = newipaux(a->owner, cb->f[1]);
+		c->aux = na;
+		free(a);
+	}
+
+	poperror();
+	free(cb);
+	return n;
+}
--- /dev/null
+++ b/os/ip/iprouter.c
@@ -1,0 +1,56 @@
+#include	"u.h"
+#include	"../port/lib.h"
+#include	"mem.h"
+#include	"dat.h"
+#include	"fns.h"
+#include	"../port/error.h"
+#include	"../ip/ip.h"
+
+IProuter iprouter;
+
+/*
+ *  User level routing.  Ip packets we don't know what to do with
+ *  come here.
+ */
+void
+useriprouter(Fs *f, Ipifc *ifc, Block *bp)
+{
+	qlock(&f->iprouter);
+	if(f->iprouter.q != nil){
+		bp = padblock(bp, IPaddrlen);
+		if(bp == nil)
+			return;
+		ipmove(bp->rp, ifc->lifc->local);
+		qpass(f->iprouter.q, bp);
+	}else
+		freeb(bp);
+	qunlock(&f->iprouter);
+}
+
+void
+iprouteropen(Fs *f)
+{
+	qlock(&f->iprouter);
+	f->iprouter.opens++;
+	if(f->iprouter.q == nil)
+		f->iprouter.q = qopen(64*1024, 0, 0, 0);
+	else if(f->iprouter.opens == 1)
+		qreopen(f->iprouter.q);
+	qunlock(&f->iprouter);
+}
+
+void
+iprouterclose(Fs *f)
+{
+	qlock(&f->iprouter);
+	f->iprouter.opens--;
+	if(f->iprouter.opens == 0)
+		qclose(f->iprouter.q);
+	qunlock(&f->iprouter);
+}
+
+long
+iprouterread(Fs *f, void *a, int n)
+{
+	return qread(f->iprouter.q, a, n);
+}
--- /dev/null
+++ b/os/ip/ipv6.c
@@ -1,0 +1,747 @@
+#include	"u.h"
+#include	"../port/lib.h"
+#include	"mem.h"
+#include	"dat.h"
+#include	"fns.h"
+#include	"../port/error.h"
+
+#include	"ip.h"
+#include	"ipv6.h"
+
+enum
+{
+	IP4HDR		= 20,		/* sizeof(Ip4hdr) */
+	IP6HDR		= 40,		/* sizeof(Ip6hdr) */
+	IP_HLEN4	= 0x05,		/* Header length in words */
+	IP_DF		= 0x4000,	/* Don't fragment */
+	IP_MF		= 0x2000,	/* More fragments */
+	IP6FHDR		= 8, 		/* sizeof(Fraghdr6) */
+	IP_MAX		= (32*1024),	/* Maximum Internet packet size */
+};
+
+#define IPV6CLASS(hdr) ((hdr->vcf[0]&0x0F)<<2 | (hdr->vcf[1]&0xF0)>>2)
+#define BLKIPVER(xp)	(((Ip6hdr*)((xp)->rp))->vcf[0]&0xF0)
+/*
+ * This sleazy macro is stolen shamelessly from ip.c, see comment there.
+ */
+#define BKFG(xp)	((Ipfrag*)((xp)->base))
+
+typedef struct	IP	IP;
+typedef struct	Fragment4	Fragment4;
+typedef struct	Fragment6	Fragment6;
+typedef struct	Ipfrag	Ipfrag;
+
+Block*		ip6reassemble(IP*, int, Block*, Ip6hdr*);
+void		ipfragfree6(IP*, Fragment6*);
+Fragment6*	ipfragallo6(IP*);
+static Block*		procxtns(IP *ip, Block *bp, int doreasm);
+int		unfraglen(Block *bp, uchar *nexthdr, int setfh);
+Block*		procopts(Block *bp);
+
+/* MIB II counters */
+enum
+{
+	Forwarding,
+	DefaultTTL,
+	InReceives,
+	InHdrErrors,
+	InAddrErrors,
+	ForwDatagrams,
+	InUnknownProtos,
+	InDiscards,
+	InDelivers,
+	OutRequests,
+	OutDiscards,
+	OutNoRoutes,
+	ReasmTimeout,
+	ReasmReqds,
+	ReasmOKs,
+	ReasmFails,
+	FragOKs,
+	FragFails,
+	FragCreates,
+
+	Nstats,
+};
+
+static char *statnames[] =
+{
+[Forwarding]	"Forwarding",
+[DefaultTTL]	"DefaultTTL",
+[InReceives]	"InReceives",
+[InHdrErrors]	"InHdrErrors",
+[InAddrErrors]	"InAddrErrors",
+[ForwDatagrams]	"ForwDatagrams",
+[InUnknownProtos]	"InUnknownProtos",
+[InDiscards]	"InDiscards",
+[InDelivers]	"InDelivers",
+[OutRequests]	"OutRequests",
+[OutDiscards]	"OutDiscards",
+[OutNoRoutes]	"OutNoRoutes",
+[ReasmTimeout]	"ReasmTimeout",
+[ReasmReqds]	"ReasmReqds",
+[ReasmOKs]	"ReasmOKs",
+[ReasmFails]	"ReasmFails",
+[FragOKs]	"FragOKs",
+[FragFails]	"FragFails",
+[FragCreates]	"FragCreates",
+};
+
+struct Fragment4
+{
+	Block*	blist;
+	Fragment4*	next;
+	ulong 	src;
+	ulong 	dst;
+	ushort	id;
+	ulong 	age;
+};
+
+struct Fragment6
+{
+	Block*	blist;
+	Fragment6*	next;
+	uchar 	src[IPaddrlen];
+	uchar 	dst[IPaddrlen];
+	uint	id;
+	ulong 	age;
+};
+
+struct Ipfrag
+{
+	ushort	foff;
+	ushort	flen;
+};
+
+/* an instance of IP */
+struct IP
+{
+	ulong		stats[Nstats];
+
+	QLock		fraglock4;
+	Fragment4*	flisthead4;
+	Fragment4*	fragfree4;
+	Ref		id4;
+
+	QLock		fraglock6;
+	Fragment6*	flisthead6;
+	Fragment6*	fragfree6;
+	Ref		id6;
+
+	int		iprouting;	/* true if we route like a gateway */
+};
+
+int
+ipoput6(Fs *f, Block *bp, int gating, int ttl, int tos, Conv *c)
+{
+	int tentative;
+	Ipifc *ifc;
+	uchar *gate, nexthdr;
+	Ip6hdr *eh;
+	int medialen, len, chunk, uflen, flen, seglen, lid, offset, fragoff, morefrags, blklen;
+	Route *r, *sr;
+	Fraghdr6 fraghdr;
+	Block *xp, *nb;
+	IP *ip;
+	int rv = 0;
+
+	ip = f->ip;
+
+	/* Fill out the ip header */
+	eh = (Ip6hdr*)(bp->rp);
+
+	ip->stats[OutRequests]++;
+
+	/* Number of uchars in data and ip header to write */
+	len = blocklen(bp);
+	
+	tentative = iptentative(f, eh->src);
+	if(tentative){
+		netlog(f, Logip, "reject tx of packet with tentative src address\n");
+		goto free;
+	}
+
+	if(gating){
+		chunk = nhgets(eh->ploadlen);
+		if(chunk > len){
+			ip->stats[OutDiscards]++;
+			netlog(f, Logip, "short gated packet\n");
+			goto free;
+		}
+		if(chunk + IPV6HDR_LEN < len)
+			len = chunk + IPV6HDR_LEN;
+	}
+
+	if(len >= IP_MAX){
+//		print("len > IP_MAX, free\n");
+		ip->stats[OutDiscards]++;
+		netlog(f, Logip, "exceeded ip max size %I\n", eh->dst);
+		goto free;
+	}
+
+	r = v6lookup(f, eh->dst, c);
+	if(r == nil){
+//		print("no route for %I, src %I free\n", eh->dst, eh->src);
+		ip->stats[OutNoRoutes]++;
+		netlog(f, Logip, "no interface %I\n", eh->dst);
+		rv = -1;
+		goto free;
+	}
+
+	ifc = r->ifc;
+	if(r->type & (Rifc|Runi))
+		gate = eh->dst;
+	else
+	if(r->type & (Rbcast|Rmulti)) {
+		gate = eh->dst;
+		sr = v6lookup(f, eh->src, nil);
+		if(sr != nil && (sr->type & Runi))
+			ifc = sr->ifc;
+	}
+	else
+		gate = r->v6.gate;
+
+	if(!gating)
+		eh->vcf[0] = IP_VER6;
+	eh->ttl = ttl;
+	if(!gating) {
+		eh->vcf[0] |= (tos >> 4);
+		eh->vcf[1] = (tos << 4);
+	}
+
+	if(!canrlock(ifc)) {
+		goto free;
+	}
+
+	if(waserror()){
+		runlock(ifc);
+		nexterror();
+	}
+
+	if(ifc->m == nil) {
+		goto raise;
+	}
+
+	/* If we dont need to fragment just send it */
+	medialen = ifc->maxtu - ifc->m->hsize;
+	if(len <= medialen) {
+		hnputs(eh->ploadlen, len-IPV6HDR_LEN);
+		ifc->m->bwrite(ifc, bp, V6, gate);
+		runlock(ifc);
+		poperror();
+		return 0;
+	}
+
+	if(gating) 
+	if(ifc->reassemble <= 0) {
+
+		/* v6 intermediate nodes are not supposed to fragment pkts;
+		   we fragment if ifc->reassemble is turned on; an exception
+		   needed for nat.
+		 */
+
+		ip->stats[OutDiscards]++;
+		icmppkttoobig6(f, ifc, bp);
+		netlog(f, Logip, "%I: gated pkts not fragmented\n", eh->dst);
+		goto raise;
+	}
+		
+	/* start v6 fragmentation */
+	uflen = unfraglen(bp, &nexthdr, 1);
+	if(uflen > medialen) {
+		ip->stats[FragFails]++;
+		ip->stats[OutDiscards]++;
+		netlog(f, Logip, "%I: unfragmentable part too big\n", eh->dst);
+		goto raise;
+	}
+
+	flen = len - uflen;
+	seglen = (medialen - (uflen + IP6FHDR)) & ~7;
+	if(seglen < 8) {
+		ip->stats[FragFails]++;
+		ip->stats[OutDiscards]++;
+		netlog(f, Logip, "%I: seglen < 8\n", eh->dst);
+		goto raise;
+	}
+
+	lid = incref(&ip->id6);
+	fraghdr.nexthdr = nexthdr;
+	fraghdr.res = 0;
+	hnputl(fraghdr.id, lid);
+
+	xp = bp;
+	offset = uflen;
+	while (xp != nil && offset && offset >= BLEN(xp)) {
+		offset -= BLEN(xp);
+		xp = xp->next;
+	}
+	xp->rp += offset;
+
+	fragoff = 0; 
+	morefrags = 1;
+
+	for(; fragoff < flen; fragoff += seglen) {
+		nb = allocb(uflen + IP6FHDR + seglen);
+
+		if(fragoff + seglen >= flen) {
+			seglen = flen - fragoff;
+			morefrags = 0;
+		}
+
+		hnputs(eh->ploadlen, seglen+IP6FHDR);
+		memmove(nb->wp, eh, uflen);
+		nb->wp += uflen;
+
+		hnputs(fraghdr.offsetRM, fragoff); // last 3 bits must be 0
+		fraghdr.offsetRM[1] |= morefrags;
+		memmove(nb->wp, &fraghdr, IP6FHDR);
+		nb->wp += IP6FHDR;
+
+		/* Copy data */
+		chunk = seglen;
+		while (chunk) {
+			if(!xp) {
+				ip->stats[OutDiscards]++;
+				ip->stats[FragFails]++;
+				freeblist(nb);
+				netlog(f, Logip, "!xp: chunk in v6%d\n", chunk);
+				goto raise;
+			}
+			blklen = chunk;
+			if(BLEN(xp) < chunk)
+				blklen = BLEN(xp);
+			memmove(nb->wp, xp->rp, blklen);
+
+			nb->wp += blklen;
+			xp->rp += blklen;
+			chunk -= blklen;
+			if(xp->rp == xp->wp)
+				xp = xp->next; 
+		}
+
+		ifc->m->bwrite(ifc, nb, V6, gate);
+		ip->stats[FragCreates]++;
+	}
+	ip->stats[FragOKs]++;
+
+raise:
+	runlock(ifc);
+	poperror();
+free:
+	freeblist(bp);	
+	return rv;
+}
+
+void
+ipiput6(Fs *f, Ipifc *ifc, Block *bp)
+{
+	int hl;
+	int hop, tos;
+	uchar proto;
+	Ip6hdr *h;
+	Proto *p;
+	int notforme;
+	int tentative;
+	uchar v6dst[IPaddrlen];
+	IP *ip;
+	Route *r, *sr;
+
+	ip = f->ip;
+	ip->stats[InReceives]++;
+
+	/*
+	 *  Ensure we have all the header info in the first
+	 *  block.  Make life easier for other protocols by
+	 *  collecting up to the first 64 bytes in the first block.
+	 */
+	if(BLEN(bp) < 64) {
+		hl = blocklen(bp);
+		if(hl < IP6HDR)
+			hl = IP6HDR;
+		if(hl > 64)
+			hl = 64;
+		bp = pullupblock(bp, hl);
+		if(bp == nil)
+			return;
+	}
+
+	h = (Ip6hdr *)(bp->rp);
+
+	memmove(&v6dst[0], &(h->dst)[0], IPaddrlen);
+	notforme = ipforme(f, v6dst) == 0;
+	tentative = iptentative(f, v6dst);
+  
+	if(tentative && (h->proto != ICMPv6)) {
+		print("tentative addr, drop\n");
+		freeblist(bp);
+		return;
+	}
+
+	/* Check header version */
+	if(BLKIPVER(bp) != IP_VER6) {
+		ip->stats[InHdrErrors]++;
+		netlog(f, Logip, "ip: bad version %ux\n", (h->vcf[0]&0xF0)>>2);
+		freeblist(bp);
+		return;
+	}
+
+	/* route */
+	if(notforme) {
+		if(!ip->iprouting){
+			freeb(bp);
+			return;
+		}
+		/* don't forward to source's network */
+		sr = v6lookup(f, h->src, nil);
+		r = v6lookup(f, h->dst, nil);
+
+		if(r == nil || sr == r){
+			ip->stats[OutDiscards]++;
+			freeblist(bp);
+			return;
+		}
+
+		/* don't forward if packet has timed out */
+		hop = h->ttl;
+		if(hop < 1) {
+			ip->stats[InHdrErrors]++;
+			icmpttlexceeded6(f, ifc, bp);
+			freeblist(bp);
+			return;
+		}
+
+		/* process headers & reassemble if the interface expects it */
+		bp = procxtns(ip, bp, r->ifc->reassemble);
+
+		if(bp == nil)
+			return;
+
+		ip->stats[ForwDatagrams]++;
+		h = (Ip6hdr *) (bp->rp);
+		tos = IPV6CLASS(h);
+		hop = h->ttl;
+		ipoput6(f, bp, 1, hop-1, tos, nil);
+		return;
+	}
+
+	/* reassemble & process headers if needed */
+	bp = procxtns(ip, bp, 1);
+
+	if(bp == nil)
+		return;
+
+	h = (Ip6hdr *) (bp->rp);
+	proto = h->proto;
+	p = Fsrcvpcol(f, proto);
+	if(p != nil && p->rcv != nil) {
+		ip->stats[InDelivers]++;
+		(*p->rcv)(p, ifc, bp);
+		return;
+	}
+
+	ip->stats[InDiscards]++;
+	ip->stats[InUnknownProtos]++;
+	freeblist(bp);
+}
+
+/*
+ * ipfragfree6 - copied from ipfragfree4 - assume hold fraglock6
+ */
+void
+ipfragfree6(IP *ip, Fragment6 *frag)
+{
+	Fragment6 *fl, **l;
+
+	if(frag->blist)
+		freeblist(frag->blist);
+
+	memset(frag->src, 0, IPaddrlen);
+	frag->id = 0;
+	frag->blist = nil;
+
+	l = &ip->flisthead6;
+	for(fl = *l; fl; fl = fl->next) {
+		if(fl == frag) {
+			*l = frag->next;
+			break;
+		}
+		l = &fl->next;
+	}
+
+	frag->next = ip->fragfree6;
+	ip->fragfree6 = frag;
+
+}
+
+/*
+ * ipfragallo6 - copied from ipfragalloc4
+ */
+Fragment6*
+ipfragallo6(IP *ip)
+{
+	Fragment6 *f;
+
+	while(ip->fragfree6 == nil) {
+		/* free last entry on fraglist */
+		for(f = ip->flisthead6; f->next; f = f->next)
+			;
+		ipfragfree6(ip, f);
+	}
+	f = ip->fragfree6;
+	ip->fragfree6 = f->next;
+	f->next = ip->flisthead6;
+	ip->flisthead6 = f;
+	f->age = NOW + 30000;
+
+	return f;
+}
+
+static Block*
+procxtns(IP *ip, Block *bp, int doreasm) {
+
+	int offset;
+	uchar proto;
+	Ip6hdr *h;
+
+	h = (Ip6hdr *) (bp->rp);
+	offset = unfraglen(bp, &proto, 0);
+
+	if((proto == FH) && (doreasm != 0)) {
+		bp = ip6reassemble(ip, offset, bp, h);
+		if(bp == nil) 
+			return nil; 
+		offset = unfraglen(bp, &proto, 0);
+	}
+
+	if(proto == DOH || offset > IP6HDR) 
+		bp = procopts(bp);
+
+	return bp;
+}
+
+
+/*	returns length of "Unfragmentable part", i.e., sum of lengths of ipv6 hdr,
+ *	hop-by-hop & routing headers if present; *nexthdr is set to nexthdr value
+ *	of the last header in the "Unfragmentable part"; if setfh != 0, nexthdr
+ *	field of the last header in the "Unfragmentable part" is set to FH.
+ */
+int
+unfraglen(Block *bp, uchar *nexthdr, int setfh)
+{
+	uchar *p, *q;
+	int ufl, hs;
+
+	p = bp->rp;
+	q = p+6;	/* proto, = p+sizeof(Ip6hdr.vcf)+sizeof(Ip6hdr.ploadlen) */
+	*nexthdr = *q;
+	ufl = IP6HDR;
+	p += ufl;
+
+	for(;;) {
+		if(*nexthdr == HBH || *nexthdr == RH) {
+			*nexthdr = *p;
+			hs = ((int)*(p+1) + 1) * 8;
+			ufl += hs;
+			q = p;
+			p += hs;
+		}
+		else
+			break;
+	}
+
+	if(*nexthdr == FH)
+		*q = *p;
+
+	if(setfh)
+		*q = FH;
+
+	return ufl;
+}
+
+Block*
+procopts(Block *bp)
+{
+	return bp;
+}
+
+Block*
+ip6reassemble(IP* ip, int uflen, Block* bp, Ip6hdr* ih)
+{
+
+	int fend, offset;
+	uint id;
+	Fragment6 *f, *fnext;
+	Fraghdr6 *fraghdr;
+	uchar src[IPaddrlen], dst[IPaddrlen];
+	Block *bl, **l, *last, *prev;
+	int ovlap, len, fragsize, pktposn;
+
+	fraghdr = (Fraghdr6 *) (bp->rp + uflen);
+	memmove(src, ih->src, IPaddrlen);
+	memmove(dst, ih->dst, IPaddrlen);
+	id = nhgetl(fraghdr->id);
+	offset = nhgets(fraghdr->offsetRM) & ~7;
+
+	/*
+	 *  block lists are too hard, pullupblock into a single block
+	 */
+	if(bp->next){
+		bp = pullupblock(bp, blocklen(bp));
+		ih = (Ip6hdr *)(bp->rp);
+	}
+
+
+	qlock(&ip->fraglock6);
+
+	/*
+	 *  find a reassembly queue for this fragment
+	 */
+	for(f = ip->flisthead6; f; f = fnext){
+		fnext = f->next;
+		if(ipcmp(f->src, src)==0 && ipcmp(f->dst, dst)==0 && f->id == id)
+			break;
+		if(f->age < NOW){
+			ip->stats[ReasmTimeout]++;
+			ipfragfree6(ip, f);
+		}
+	}
+
+
+	/*
+	 *  if this isn't a fragmented packet, accept it
+	 *  and get rid of any fragments that might go
+	 *  with it.
+	 */
+	if(nhgets(fraghdr->offsetRM)==0) {	// first frag is also the last
+		if(f != nil) {
+			ipfragfree6(ip, f);
+			ip->stats[ReasmFails]++;
+		}
+		qunlock(&ip->fraglock6);
+		return bp;
+	}
+
+	if(bp->base+sizeof(Ipfrag) >= bp->rp){
+		bp = padblock(bp, sizeof(Ipfrag));
+		bp->rp += sizeof(Ipfrag);
+	}
+
+	BKFG(bp)->foff = offset;
+	BKFG(bp)->flen = nhgets(ih->ploadlen) + IP6HDR - uflen - IP6FHDR;
+
+	/* First fragment allocates a reassembly queue */
+	if(f == nil) {
+		f = ipfragallo6(ip);
+		f->id = id;
+		memmove(f->src, src, IPaddrlen);
+		memmove(f->dst, dst, IPaddrlen);
+
+		f->blist = bp;
+
+		qunlock(&ip->fraglock6);
+		ip->stats[ReasmReqds]++;
+		return nil;
+	}
+
+	/*
+	 *  find the new fragment's position in the queue
+	 */
+	prev = nil;
+	l = &f->blist;
+	bl = f->blist;
+	while(bl != nil && BKFG(bp)->foff > BKFG(bl)->foff) {
+		prev = bl;
+		l = &bl->next;
+		bl = bl->next;
+	}
+
+	/* Check overlap of a previous fragment - trim away as necessary */
+	if(prev) {
+		ovlap = BKFG(prev)->foff + BKFG(prev)->flen - BKFG(bp)->foff;
+		if(ovlap > 0) {
+			if(ovlap >= BKFG(bp)->flen) {
+				freeblist(bp);
+				qunlock(&ip->fraglock6);
+				return nil;
+			}
+			BKFG(prev)->flen -= ovlap;
+		}
+	}
+
+	/* Link onto assembly queue */
+	bp->next = *l;
+	*l = bp;
+
+	/* Check to see if succeeding segments overlap */
+	if(bp->next) {
+		l = &bp->next;
+		fend = BKFG(bp)->foff + BKFG(bp)->flen;
+
+		/* Take completely covered segments out */
+
+		while(*l) {
+			ovlap = fend - BKFG(*l)->foff;
+
+			if(ovlap <= 0) 
+				break; 
+			if(ovlap < BKFG(*l)->flen) {
+				BKFG(*l)->flen -= ovlap;
+				BKFG(*l)->foff += ovlap;
+				/* move up ih hdrs */
+				memmove((*l)->rp + ovlap, (*l)->rp, uflen);
+				(*l)->rp += ovlap;
+				break;
+			}
+			last = (*l)->next;
+			(*l)->next = nil;
+			freeblist(*l);
+			*l = last;
+		}
+	}
+
+	/*
+	 *  look for a complete packet.  if we get to a fragment
+	 *  with the trailing bit of fraghdr->offsetRM[1] set, we're done.
+	 */
+	pktposn = 0;
+	for(bl = f->blist; bl; bl = bl->next) {
+		if(BKFG(bl)->foff != pktposn)
+			break;
+	
+		fraghdr = (Fraghdr6 *) (bl->rp + uflen);
+		if((fraghdr->offsetRM[1] & 1) == 0) {
+
+			bl = f->blist;
+
+			/* get rid of frag header in first fragment */
+
+			memmove(bl->rp + IP6FHDR, bl->rp, uflen);
+			bl->rp += IP6FHDR;
+			len = nhgets(((Ip6hdr*)(bl->rp))->ploadlen) - IP6FHDR;
+			bl->wp = bl->rp + len + IP6HDR;
+
+			/* Pullup all the fragment headers and
+			 * return a complete packet
+			 */
+			for(bl = bl->next; bl; bl = bl->next) {
+				fragsize = BKFG(bl)->flen;
+				len += fragsize;
+				bl->rp += uflen + IP6FHDR;
+				bl->wp = bl->rp + fragsize;
+			}
+
+			bl = f->blist;
+			f->blist = nil;
+			ipfragfree6(ip, f);
+			ih = (Ip6hdr*)(bl->rp);
+			hnputs(ih->ploadlen, len);
+			qunlock(&ip->fraglock6);
+			ip->stats[ReasmOKs]++;
+			return bl;		
+		}
+		pktposn += BKFG(bl)->flen;
+	}
+	qunlock(&ip->fraglock6);
+	return nil;
+}
+
--- /dev/null
+++ b/os/ip/ipv6.h
@@ -1,0 +1,185 @@
+#define MIN(a, b) ((a) <= (b) ? (a) : (b))
+
+/* rfc 3513 defines the address prefices */
+#define isv6mcast(addr)	  ((addr)[0] == 0xff)
+#define islinklocal(addr) ((addr)[0] == 0xfe && ((addr)[1] & 0xc0) == 0x80)
+#define issitelocal(addr) ((addr)[0] == 0xfe && ((addr)[1] & 0xc0) == 0xc0)
+#define isv6global(addr) (((addr)[0] & 0xe0) == 0x20)
+
+#define optexsts(np) (nhgets((np)->ploadlen) > 24)
+#define issmcast(addr) (memcmp((addr), v6solicitednode, 13) == 0)
+
+/* from RFC 2460 */
+
+typedef struct Ip6hdr     Ip6hdr;
+typedef struct Opthdr     Opthdr;
+typedef struct Routinghdr Routinghdr;
+typedef struct Fraghdr6    Fraghdr6;
+
+struct Ip6hdr {
+	uchar vcf[4];       	// version:4, traffic class:8, flow label:20
+	uchar ploadlen[2];  	// payload length: packet length - 40
+	uchar proto;		// next header type
+	uchar ttl;          	// hop limit
+	uchar src[IPaddrlen];
+	uchar dst[IPaddrlen];
+};
+
+struct Opthdr {
+	uchar nexthdr;
+	uchar len;
+};
+
+struct Routinghdr {
+	uchar nexthdr;
+	uchar len;
+	uchar rtetype;
+	uchar segrem;
+};
+
+struct Fraghdr6 {
+	uchar nexthdr;
+	uchar res;
+	uchar offsetRM[2];	// Offset, Res, M flag
+	uchar id[4];
+};
+
+
+enum {			/* Header Types */
+	HBH		= 0,	//?
+	ICMP		= 1,
+	IGMP		= 2,
+	GGP		= 3,
+	IPINIP		= 4,
+	ST		= 5,
+	TCP		= 6,
+	UDP		= 17,
+	ISO_TP4		= 29,
+	RH		= 43,
+	FH		= 44,
+	IDRP		= 45,
+	RSVP		= 46,
+	AH		= 51,
+	ESP		= 52,
+	ICMPv6		= 58,
+	NNH		= 59,
+	DOH		= 60,
+	ISO_IP		= 80,
+	IGRP		= 88,
+	OSPF		= 89,
+
+	Maxhdrtype	= 256,
+};
+
+
+enum {
+	//	multicast flgs and scop
+
+	well_known_flg				= 0,
+	transient_flg				= 1,
+
+	node_local_scop 			= 1,
+	link_local_scop 			= 2,
+	site_local_scop 			= 5,
+	org_local_scop				= 8,
+	global_scop				= 14,
+
+	//	various prefix lengths
+
+	SOLN_PREF_LEN				= 13,
+
+	//	icmpv6 unreach codes
+	icmp6_no_route				= 0,
+	icmp6_ad_prohib				= 1,
+	icmp6_unassigned			= 2,
+	icmp6_adr_unreach			= 3,
+	icmp6_port_unreach			= 4,
+	icmp6_unkn_code				= 5,
+
+	// 	various flags & constants
+
+	v6MINTU      				= 1280,
+	HOP_LIMIT    				= 255,
+	ETHERHDR_LEN 				= 14,
+	IPV6HDR_LEN  				= 40,
+	IPV4HDR_LEN  				= 20,
+
+	// 	option types
+
+	SRC_LLADDRESS    			= 1,
+	TARGET_LLADDRESS 			= 2,
+	PREFIX_INFO      			= 3,
+	REDIR_HEADER     			= 4,
+	MTU_OPTION       			= 5,
+
+	SRC_UNSPEC  				= 0,
+	SRC_UNI     				= 1,
+	TARG_UNI    				= 2,
+	TARG_MULTI  				= 3,
+
+	t_unitent   				= 1,
+	t_uniproxy  				= 2,
+	t_unirany   				= 3,
+
+	//	Router constants (all times in milliseconds)
+
+	MAX_INITIAL_RTR_ADVERT_INTERVAL 	= 16000,
+	MAX_INITIAL_RTR_ADVERTISEMENTS  	= 3,
+	MAX_FINAL_RTR_ADVERTISEMENTS    	= 3,
+	MIN_DELAY_BETWEEN_RAS 			= 3000,
+	MAX_RA_DELAY_TIME     			= 500,
+
+	//	Host constants
+
+	MAX_RTR_SOLICITATION_DELAY 		= 1000,
+	RTR_SOLICITATION_INTERVAL  		= 4000,
+	MAX_RTR_SOLICITATIONS      		= 3,
+
+	//	Node constants
+
+	MAX_MULTICAST_SOLICIT   		= 3,
+	MAX_UNICAST_SOLICIT     		= 3,
+	MAX_ANYCAST_DELAY_TIME  		= 1000,
+	MAX_NEIGHBOR_ADVERTISEMENT 		= 3,
+	REACHABLE_TIME 				= 30000,
+	RETRANS_TIMER  				= 1000,
+	DELAY_FIRST_PROBE_TIME 			= 5000,
+
+};
+
+extern void ipv62smcast(uchar *, uchar *);
+extern void icmpns(Fs *f, uchar* src, int suni, uchar* targ, int tuni, uchar* mac);
+extern void icmpna(Fs *f, uchar* src, uchar* dst, uchar* targ, uchar* mac, uchar flags);
+extern void icmpttlexceeded6(Fs *f, Ipifc *ifc, Block *bp);
+extern void icmppkttoobig6(Fs *f, Ipifc *ifc, Block *bp);
+extern void icmphostunr(Fs *f, Ipifc *ifc, Block *bp, int code, int free);
+
+extern uchar v6allnodesN[IPaddrlen];
+extern uchar v6allnodesL[IPaddrlen];
+extern uchar v6allroutersN[IPaddrlen];
+extern uchar v6allroutersL[IPaddrlen];
+extern uchar v6allnodesNmask[IPaddrlen];
+extern uchar v6allnodesLmask[IPaddrlen];
+extern uchar v6allroutersS[IPaddrlen];
+extern uchar v6solicitednode[IPaddrlen];
+extern uchar v6solicitednodemask[IPaddrlen];
+extern uchar v6Unspecified[IPaddrlen];
+extern uchar v6loopback[IPaddrlen];
+extern uchar v6loopbackmask[IPaddrlen];
+extern uchar v6linklocal[IPaddrlen];
+extern uchar v6linklocalmask[IPaddrlen];
+extern uchar v6sitelocal[IPaddrlen];
+extern uchar v6sitelocalmask[IPaddrlen];
+extern uchar v6glunicast[IPaddrlen];
+extern uchar v6multicast[IPaddrlen];
+extern uchar v6multicastmask[IPaddrlen];
+
+extern int v6llpreflen;
+extern int v6slpreflen;
+extern int v6lbpreflen;
+extern int v6mcpreflen;
+extern int v6snpreflen;
+extern int v6aNpreflen;
+extern int v6aLpreflen;
+
+extern int ReTransTimer;
--- /dev/null
+++ b/os/ip/kernel.h
@@ -1,0 +1,10 @@
+extern	int	kclose(int);
+extern	int	kdial(char*, char*, char*, int*);
+extern	int	kannounce(char*, char*);
+extern	void	kerrstr(char*);
+extern	void	kgerrstr(char*);
+extern	int	kopen(char*, int);
+extern	long	kread(int, void*, long);
+extern	long	kseek(int, vlong, int);
+extern	long	kwrite(int, void*, long);
+extern	void	kwerrstr(char *, ...);
--- /dev/null
+++ b/os/ip/loopbackmedium.c
@@ -1,0 +1,121 @@
+#include "u.h"
+#include "../port/lib.h"
+#include "mem.h"
+#include "dat.h"
+#include "fns.h"
+#include "../port/error.h"
+
+#include "ip.h"
+
+enum
+{
+	Maxtu=	16*1024,
+};
+
+typedef struct LB LB;
+struct LB
+{
+	Proc	*readp;
+	Queue	*q;
+	Fs	*f;
+};
+
+static void loopbackread(void *a);
+
+static void
+loopbackbind(Ipifc *ifc, int, char**)
+{
+	LB *lb;
+
+	lb = smalloc(sizeof(*lb));
+	lb->f = ifc->conv->p->f;
+	/* TO DO: make queue size a function of kernel memory */
+	lb->q = qopen(128*1024, Qmsg, nil, nil);
+	ifc->arg = lb;
+	ifc->mbps = 1000;
+
+	kproc("loopbackread", loopbackread, ifc, 0);
+
+}
+
+static void
+loopbackunbind(Ipifc *ifc)
+{
+	LB *lb = ifc->arg;
+
+	if(lb->readp)
+		postnote(lb->readp, 1, "unbind", 0);
+
+	/* wait for reader to die */
+	while(lb->readp != 0)
+		tsleep(&up->sleep, return0, 0, 300);
+
+	/* clean up */
+	qfree(lb->q);
+	free(lb);
+}
+
+static void
+loopbackbwrite(Ipifc *ifc, Block *bp, int, uchar*)
+{
+	LB *lb;
+
+	lb = ifc->arg;
+	if(qpass(lb->q, bp) < 0)
+		ifc->outerr++;
+	ifc->out++;
+}
+
+static void
+loopbackread(void *a)
+{
+	Ipifc *ifc;
+	Block *bp;
+	LB *lb;
+
+	ifc = a;
+	lb = ifc->arg;
+	lb->readp = up;	/* hide identity under a rock for unbind */
+	if(waserror()){
+		lb->readp = 0;
+		pexit("hangup", 1);
+	}
+	for(;;){
+		bp = qbread(lb->q, Maxtu);
+		if(bp == nil)
+			continue;
+		ifc->in++;
+		if(!canrlock(ifc)){
+			freeb(bp);
+			continue;
+		}
+		if(waserror()){
+			runlock(ifc);
+			nexterror();
+		}
+		if(ifc->lifc == nil)
+			freeb(bp);
+		else
+			ipiput4(lb->f, ifc, bp);
+		runlock(ifc);
+		poperror();
+	}
+}
+
+Medium loopbackmedium =
+{
+.hsize=		0,
+.mintu=		0,
+.maxtu=		Maxtu,
+.maclen=	0,
+.name=		"loopback",
+.bind=		loopbackbind,
+.unbind=	loopbackunbind,
+.bwrite=	loopbackbwrite,
+};
+
+void
+loopbackmediumlink(void)
+{
+	addipmedium(&loopbackmedium);
+}
--- /dev/null
+++ b/os/ip/netdevmedium.c
@@ -1,0 +1,153 @@
+#include "u.h"
+#include "../port/lib.h"
+#include "mem.h"
+#include "dat.h"
+#include "fns.h"
+#include "../port/error.h"
+
+#include "ip.h"
+
+static void	netdevbind(Ipifc *ifc, int argc, char **argv);
+static void	netdevunbind(Ipifc *ifc);
+static void	netdevbwrite(Ipifc *ifc, Block *bp, int version, uchar *ip);
+static void	netdevread(void *a);
+
+typedef struct	Netdevrock Netdevrock;
+struct Netdevrock
+{
+	Fs	*f;		/* file system we belong to */
+	Proc	*readp;		/* reading process */
+	Chan	*mchan;		/* Data channel */
+};
+
+Medium netdevmedium =
+{
+.name=		"netdev",
+.hsize=		0,
+.mintu=	0,
+.maxtu=	64000,
+.maclen=	0,
+.bind=		netdevbind,
+.unbind=	netdevunbind,
+.bwrite=	netdevbwrite,
+.unbindonclose=	0,
+};
+
+/*
+ *  called to bind an IP ifc to a generic network device
+ *  called with ifc qlock'd
+ */
+static void
+netdevbind(Ipifc *ifc, int argc, char **argv)
+{
+	Chan *mchan;
+	Netdevrock *er;
+
+	if(argc < 2)
+		error(Ebadarg);
+
+	mchan = namec(argv[2], Aopen, ORDWR, 0);
+
+	er = smalloc(sizeof(*er));
+	er->mchan = mchan;
+	er->f = ifc->conv->p->f;
+
+	ifc->arg = er;
+
+	kproc("netdevread", netdevread, ifc, 0);
+}
+
+/*
+ *  called with ifc wlock'd
+ */
+static void
+netdevunbind(Ipifc *ifc)
+{
+	Netdevrock *er = ifc->arg;
+
+	if(er->readp != nil)
+		postnote(er->readp, 1, "unbind", 0);
+
+	/* wait for readers to die */
+	while(er->readp != nil)
+		tsleep(&up->sleep, return0, 0, 300);
+
+	if(er->mchan != nil)
+		cclose(er->mchan);
+
+	free(er);
+}
+
+/*
+ *  called by ipoput with a single block to write
+ */
+static void
+netdevbwrite(Ipifc *ifc, Block *bp, int, uchar*)
+{
+	Netdevrock *er = ifc->arg;
+
+	if(bp->next)
+		bp = concatblock(bp);
+	if(BLEN(bp) < ifc->mintu)
+		bp = adjustblock(bp, ifc->mintu);
+
+	devtab[er->mchan->type]->bwrite(er->mchan, bp, 0);
+	ifc->out++;
+}
+
+/*
+ *  process to read from the device
+ */
+static void
+netdevread(void *a)
+{
+	Ipifc *ifc;
+	Block *bp;
+	Netdevrock *er;
+	char *argv[1];
+
+	ifc = a;
+	er = ifc->arg;
+	er->readp = up;	/* hide identity under a rock for unbind */
+	if(waserror()){
+		er->readp = nil;
+		pexit("hangup", 1);
+	}
+	for(;;){
+		bp = devtab[er->mchan->type]->bread(er->mchan, ifc->maxtu, 0);
+		if(bp == nil){
+			/*
+			 * get here if mchan is a pipe and other side hangs up
+			 * clean up this interface & get out
+ZZZ is this a good idea?
+			 */
+			poperror();
+			er->readp = nil;
+			argv[0] = "unbind";
+			if(!waserror())
+				ifc->conv->p->ctl(ifc->conv, argv, 1);
+			pexit("hangup", 1);
+		}
+		if(!canrlock(ifc)){
+			freeb(bp);
+			continue;
+		}
+		if(waserror()){
+			runlock(ifc);
+			nexterror();
+		}
+		ifc->in++;
+		if(ifc->lifc == nil)
+			freeb(bp);
+		else
+			ipiput4(er->f, ifc, bp);
+		runlock(ifc);
+		poperror();
+	}
+}
+
+void
+netdevmediumlink(void)
+{
+	addipmedium(&netdevmedium);
+}
--- /dev/null
+++ b/os/ip/netlog.c
@@ -1,0 +1,263 @@
+#include	"u.h"
+#include	"../port/lib.h"
+#include	"mem.h"
+#include	"dat.h"
+#include	"fns.h"
+#include	"../port/error.h"
+#include	"../ip/ip.h"
+
+enum {
+	Nlog		= 4*1024,
+};
+
+/*
+ *  action log
+ */
+struct Netlog {
+	Lock;
+	int	opens;
+	char*	buf;
+	char	*end;
+	char	*rptr;
+	int	len;
+
+	int	logmask;			/* mask of things to debug */
+	uchar	iponly[IPaddrlen];		/* ip address to print debugging for */
+	int	iponlyset;
+
+	QLock;
+	Rendez;
+};
+
+typedef struct Netlogflag {
+	char*	name;
+	int	mask;
+} Netlogflag;
+
+static Netlogflag flags[] =
+{
+	{ "ppp",	Logppp, },
+	{ "ip",		Logip, },
+	{ "fs",		Logfs, },
+	{ "tcp",	Logtcp, },
+	{ "il",		Logil, },
+	{ "icmp",	Logicmp, },
+	{ "udp",	Logudp, },
+	{ "compress",	Logcompress, },
+	{ "ilmsg",	Logil|Logilmsg, },
+	{ "gre",	Loggre, },
+	{ "tcpwin",	Logtcp|Logtcpwin, },
+	{ "tcprxmt",	Logtcp|Logtcprxmt, },
+	{ "udpmsg",	Logudp|Logudpmsg, },
+	{ "ipmsg",	Logip|Logipmsg, },
+	{ "esp",	Logesp, },
+	{ nil,		0, },
+};
+
+char Ebadnetctl[] = "too few arguments for netlog control message";
+
+enum
+{
+	CMset,
+	CMclear,
+	CMonly,
+};
+
+static
+Cmdtab routecmd[] = {
+	CMset,		"set",		0,
+	CMclear,	"clear",	0,
+	CMonly,		"only",		0,
+};
+
+void
+netloginit(Fs *f)
+{
+	f->alog = smalloc(sizeof(Netlog));
+}
+
+void
+netlogopen(Fs *f)
+{
+	lock(f->alog);
+	if(waserror()){
+		unlock(f->alog);
+		nexterror();
+	}
+	if(f->alog->opens == 0){
+		if(f->alog->buf == nil)
+			f->alog->buf = malloc(Nlog);
+		f->alog->rptr = f->alog->buf;
+		f->alog->end = f->alog->buf + Nlog;
+	}
+	f->alog->opens++;
+	unlock(f->alog);
+	poperror();
+}
+
+void
+netlogclose(Fs *f)
+{
+	lock(f->alog);
+	if(waserror()){
+		unlock(f->alog);
+		nexterror();
+	}
+	f->alog->opens--;
+	if(f->alog->opens == 0){
+		free(f->alog->buf);
+		f->alog->buf = nil;
+	}
+	unlock(f->alog);
+	poperror();
+}
+
+static int
+netlogready(void *a)
+{
+	Fs *f = a;
+
+	return f->alog->len;
+}
+
+long
+netlogread(Fs *f, void *a, ulong, long n)
+{
+	int i, d;
+	char *p, *rptr;
+
+	qlock(f->alog);
+	if(waserror()){
+		qunlock(f->alog);
+		nexterror();
+	}
+
+	for(;;){
+		lock(f->alog);
+		if(f->alog->len){
+			if(n > f->alog->len)
+				n = f->alog->len;
+			d = 0;
+			rptr = f->alog->rptr;
+			f->alog->rptr += n;
+			if(f->alog->rptr >= f->alog->end){
+				d = f->alog->rptr - f->alog->end;
+				f->alog->rptr = f->alog->buf + d;
+			}
+			f->alog->len -= n;
+			unlock(f->alog);
+
+			i = n-d;
+			p = a;
+			memmove(p, rptr, i);
+			memmove(p+i, f->alog->buf, d);
+			break;
+		}
+		else
+			unlock(f->alog);
+
+		sleep(f->alog, netlogready, f);
+	}
+
+	qunlock(f->alog);
+	poperror();
+
+	return n;
+}
+
+void
+netlogctl(Fs *f, char* s, int n)
+{
+	int i, set;
+	Netlogflag *fp;
+	Cmdbuf *cb;
+	Cmdtab *ct;
+
+	cb = parsecmd(s, n);
+	if(waserror()){
+		free(cb);
+		nexterror();
+	}
+
+	if(cb->nf < 2)
+		error(Ebadnetctl);
+
+	ct = lookupcmd(cb, routecmd, nelem(routecmd));
+
+	SET(set);
+
+	switch(ct->index){
+	case CMset:
+		set = 1;
+		break;
+
+	case CMclear:
+		set = 0;
+		break;
+
+	case CMonly:
+		parseip(f->alog->iponly, cb->f[1]);
+		if(ipcmp(f->alog->iponly, IPnoaddr) == 0)
+			f->alog->iponlyset = 0;
+		else
+			f->alog->iponlyset = 1;
+		free(cb);
+		return;
+
+	default:
+		cmderror(cb, "unknown ip control message");
+	}
+
+	for(i = 1; i < cb->nf; i++){
+		for(fp = flags; fp->name; fp++)
+			if(strcmp(fp->name, cb->f[i]) == 0)
+				break;
+		if(fp->name == nil)
+			continue;
+		if(set)
+			f->alog->logmask |= fp->mask;
+		else
+			f->alog->logmask &= ~fp->mask;
+	}
+
+	free(cb);
+	poperror();
+}
+
+void
+netlog(Fs *f, int mask, char *fmt, ...)
+{
+	char buf[128], *t, *fp;
+	int i, n;
+	va_list arg;
+
+	if(!(f->alog->logmask & mask))
+		return;
+
+	if(f->alog->opens == 0)
+		return;
+
+	va_start(arg, fmt);
+	n = vseprint(buf, buf+sizeof(buf), fmt, arg) - buf;
+	va_end(arg);
+
+	lock(f->alog);
+	i = f->alog->len + n - Nlog;
+	if(i > 0){
+		f->alog->len -= i;
+		f->alog->rptr += i;
+		if(f->alog->rptr >= f->alog->end)
+			f->alog->rptr = f->alog->buf + (f->alog->rptr - f->alog->end);
+	}
+	t = f->alog->rptr + f->alog->len;
+	fp = buf;
+	f->alog->len += n;
+	while(n-- > 0){
+		if(t >= f->alog->end)
+			t = f->alog->buf + (t - f->alog->end);
+		*t++ = *fp++;
+	}
+	unlock(f->alog);
+
+	wakeup(f->alog);
+}
--- /dev/null
+++ b/os/ip/nullmedium.c
@@ -1,0 +1,39 @@
+#include "u.h"
+#include "../port/lib.h"
+#include "mem.h"
+#include "dat.h"
+#include "fns.h"
+#include "../port/error.h"
+
+#include "ip.h"
+
+static void
+nullbind(Ipifc*, int, char**)
+{
+	error("cannot bind null device");
+}
+
+static void
+nullunbind(Ipifc*)
+{
+}
+
+static void
+nullbwrite(Ipifc*, Block*, int, uchar*)
+{
+	error("nullbwrite");
+}
+
+Medium nullmedium =
+{
+.name=		"null",
+.bind=		nullbind,
+.unbind=	nullunbind,
+.bwrite=	nullbwrite,
+};
+
+void
+nullmediumlink(void)
+{
+	addipmedium(&nullmedium);
+}
--- /dev/null
+++ b/os/ip/pktmedium.c
@@ -1,0 +1,79 @@
+#include "u.h"
+#include "../port/lib.h"
+#include "mem.h"
+#include "dat.h"
+#include "fns.h"
+#include "../port/error.h"
+
+#include "ip.h"
+
+
+static void	pktbind(Ipifc*, int, char**);
+static void	pktunbind(Ipifc*);
+static void	pktbwrite(Ipifc*, Block*, int, uchar*);
+static void	pktin(Fs*, Ipifc*, Block*);
+
+Medium pktmedium =
+{
+.name=		"pkt",
+.hsize=		14,
+.mintu=		40,
+.maxtu=		4*1024,
+.maclen=	6,
+.bind=		pktbind,
+.unbind=	pktunbind,
+.bwrite=	pktbwrite,
+.pktin=		pktin,
+.unbindonclose=	1,
+};
+
+/*
+ *  called to bind an IP ifc to an ethernet device
+ *  called with ifc wlock'd
+ */
+static void
+pktbind(Ipifc*, int, char**)
+{
+}
+
+/*
+ *  called with ifc wlock'd
+ */
+static void
+pktunbind(Ipifc*)
+{
+}
+
+/*
+ *  called by ipoput with a single packet to write
+ */
+static void
+pktbwrite(Ipifc *ifc, Block *bp, int, uchar*)
+{
+	/* enqueue onto the conversation's rq */
+	bp = concatblock(bp);
+	if(ifc->conv->snoopers.ref > 0)
+		qpass(ifc->conv->sq, copyblock(bp, BLEN(bp)));
+	qpass(ifc->conv->rq, bp);
+}
+
+/*
+ *  called with ifc rlocked when someone write's to 'data'
+ */
+static void
+pktin(Fs *f, Ipifc *ifc, Block *bp)
+{
+	if(ifc->lifc == nil)
+		freeb(bp);
+	else {
+		if(ifc->conv->snoopers.ref > 0)
+			qpass(ifc->conv->sq, copyblock(bp, BLEN(bp)));
+		ipiput4(f, ifc, bp);
+	}
+}
+
+void
+pktmediumlink(void)
+{
+	addipmedium(&pktmedium);
+}
--- /dev/null
+++ b/os/ip/plan9.c
@@ -1,0 +1,36 @@
+#include	"u.h"
+#include	"../port/lib.h"
+#include	"mem.h"
+#include	"dat.h"
+#include	"fns.h"
+#include	"../port/error.h"
+#include	"ip.h"
+
+/*
+ *  some hacks for commonality twixt inferno and plan9
+ */
+
+char*
+commonuser(void)
+{
+	return up->env->user;
+}
+
+Chan*
+commonfdtochan(int fd, int mode, int a, int b)
+{
+	return fdtochan(up->env->fgrp, fd, mode, a, b);
+}
+
+char*
+commonerror(void)
+{
+	return up->env->errstr;
+}
+
+int
+postnote(Proc *p, int, char *, int)
+{
+	swiproc(p, 0);
+	return 0;
+}
--- /dev/null
+++ b/os/ip/ppp.c
@@ -1,0 +1,1656 @@
+#include	"u.h"
+#include	"../port/lib.h"
+#include	"mem.h"
+#include	"dat.h"
+#include	"fns.h"
+#include	"../port/error.h"
+#include	<libcrypt.h>
+#include	<kernel.h>
+#include	"ip.h"
+#include	"ppp.h"
+
+int	nocompress;
+Ipaddr	pppdns[2];
+
+/*
+ * Calculate FCS - rfc 1331
+ */
+ushort fcstab[256] =
+{
+      0x0000, 0x1189, 0x2312, 0x329b, 0x4624, 0x57ad, 0x6536, 0x74bf,
+      0x8c48, 0x9dc1, 0xaf5a, 0xbed3, 0xca6c, 0xdbe5, 0xe97e, 0xf8f7,
+      0x1081, 0x0108, 0x3393, 0x221a, 0x56a5, 0x472c, 0x75b7, 0x643e,
+      0x9cc9, 0x8d40, 0xbfdb, 0xae52, 0xdaed, 0xcb64, 0xf9ff, 0xe876,
+      0x2102, 0x308b, 0x0210, 0x1399, 0x6726, 0x76af, 0x4434, 0x55bd,
+      0xad4a, 0xbcc3, 0x8e58, 0x9fd1, 0xeb6e, 0xfae7, 0xc87c, 0xd9f5,
+      0x3183, 0x200a, 0x1291, 0x0318, 0x77a7, 0x662e, 0x54b5, 0x453c,
+      0xbdcb, 0xac42, 0x9ed9, 0x8f50, 0xfbef, 0xea66, 0xd8fd, 0xc974,
+      0x4204, 0x538d, 0x6116, 0x709f, 0x0420, 0x15a9, 0x2732, 0x36bb,
+      0xce4c, 0xdfc5, 0xed5e, 0xfcd7, 0x8868, 0x99e1, 0xab7a, 0xbaf3,
+      0x5285, 0x430c, 0x7197, 0x601e, 0x14a1, 0x0528, 0x37b3, 0x263a,
+      0xdecd, 0xcf44, 0xfddf, 0xec56, 0x98e9, 0x8960, 0xbbfb, 0xaa72,
+      0x6306, 0x728f, 0x4014, 0x519d, 0x2522, 0x34ab, 0x0630, 0x17b9,
+      0xef4e, 0xfec7, 0xcc5c, 0xddd5, 0xa96a, 0xb8e3, 0x8a78, 0x9bf1,
+      0x7387, 0x620e, 0x5095, 0x411c, 0x35a3, 0x242a, 0x16b1, 0x0738,
+      0xffcf, 0xee46, 0xdcdd, 0xcd54, 0xb9eb, 0xa862, 0x9af9, 0x8b70,
+      0x8408, 0x9581, 0xa71a, 0xb693, 0xc22c, 0xd3a5, 0xe13e, 0xf0b7,
+      0x0840, 0x19c9, 0x2b52, 0x3adb, 0x4e64, 0x5fed, 0x6d76, 0x7cff,
+      0x9489, 0x8500, 0xb79b, 0xa612, 0xd2ad, 0xc324, 0xf1bf, 0xe036,
+      0x18c1, 0x0948, 0x3bd3, 0x2a5a, 0x5ee5, 0x4f6c, 0x7df7, 0x6c7e,
+      0xa50a, 0xb483, 0x8618, 0x9791, 0xe32e, 0xf2a7, 0xc03c, 0xd1b5,
+      0x2942, 0x38cb, 0x0a50, 0x1bd9, 0x6f66, 0x7eef, 0x4c74, 0x5dfd,
+      0xb58b, 0xa402, 0x9699, 0x8710, 0xf3af, 0xe226, 0xd0bd, 0xc134,
+      0x39c3, 0x284a, 0x1ad1, 0x0b58, 0x7fe7, 0x6e6e, 0x5cf5, 0x4d7c,
+      0xc60c, 0xd785, 0xe51e, 0xf497, 0x8028, 0x91a1, 0xa33a, 0xb2b3,
+      0x4a44, 0x5bcd, 0x6956, 0x78df, 0x0c60, 0x1de9, 0x2f72, 0x3efb,
+      0xd68d, 0xc704, 0xf59f, 0xe416, 0x90a9, 0x8120, 0xb3bb, 0xa232,
+      0x5ac5, 0x4b4c, 0x79d7, 0x685e, 0x1ce1, 0x0d68, 0x3ff3, 0x2e7a,
+      0xe70e, 0xf687, 0xc41c, 0xd595, 0xa12a, 0xb0a3, 0x8238, 0x93b1,
+      0x6b46, 0x7acf, 0x4854, 0x59dd, 0x2d62, 0x3ceb, 0x0e70, 0x1ff9,
+      0xf78f, 0xe606, 0xd49d, 0xc514, 0xb1ab, 0xa022, 0x92b9, 0x8330,
+      0x7bc7, 0x6a4e, 0x58d5, 0x495c, 0x3de3, 0x2c6a, 0x1ef1, 0x0f78
+};
+
+static char *snames[] =
+{
+	"Sclosed",
+	"Sclosing",
+	"Sreqsent",
+	"Sackrcvd",
+	"Sacksent",
+	"Sopened",
+};
+
+static void	init(PPP*);
+static void	setphase(PPP*, int);
+static void	pinit(PPP*, Pstate*);
+static void	ppptimer(void*);
+static void	ptimer(PPP*, Pstate*);
+static int	getframe(PPP*, Block**);
+static Block*	putframe(PPP*, int, Block*);
+static uchar*	escapebyte(PPP*, ulong, uchar*, ushort*);
+static void	config(PPP*, Pstate*, int);
+static int	getopts(PPP*, Pstate*, Block*);
+static void	rejopts(PPP*, Pstate*, Block*, int);
+static void	newstate(PPP*, Pstate*, int);
+static void	rcv(PPP*, Pstate*, Block*);
+static void	getchap(PPP*, Block*);
+static void	getpap(PPP*, Block*);
+static void	sendpap(PPP*);
+static void	getlqm(PPP*, Block*);
+static void	putlqm(PPP*);
+static void	hangup(PPP*);
+static void	remove(PPP*);
+
+static	int		validv4(Ipaddr);
+static	void		invalidate(Ipaddr);
+static	void		ipconnect(PPP *);
+static	void		setdefroute(PPP *, Ipaddr);
+static	void		printopts(PPP *, Pstate*, Block*, int);
+static	void		sendtermreq(PPP*, Pstate*);
+
+static void
+errlog(PPP *ppp, char *err)
+{
+	int n;
+	char msg[64];
+
+	n = snprint(msg, sizeof(msg), "%s\n", err);
+	qproduce(ppp->ifc->conv->eq, msg, n);
+}
+
+static void
+init(PPP* ppp)
+{
+	if(ppp->inbuf == nil){
+		ppp->inbuf = allocb(4096);
+		ppp->outbuf = allocb(4096);
+
+		ppp->lcp = malloc(sizeof(Pstate));
+		ppp->ipcp = malloc(sizeof(Pstate));
+		if(ppp->lcp == nil || ppp->ipcp == nil)
+			error("ppp init: malloc");
+
+		ppp->lcp->proto = Plcp;
+		ppp->lcp->state = Sclosed;
+		ppp->ipcp->proto = Pipcp;
+		ppp->ipcp->state = Sclosed;
+
+		kproc("ppptimer", ppptimer, ppp, KPDUPPG|KPDUPFDG);
+	}
+
+	pinit(ppp, ppp->lcp);
+	setphase(ppp, Plink);
+}
+
+static void
+setphase(PPP *ppp, int phase)
+{
+	int oldphase;
+
+	oldphase = ppp->phase;
+
+	ppp->phase = phase;
+	switch(phase){
+	default:
+		panic("ppp: unknown phase %d", phase);
+	case Pdead:
+		/* restart or exit? */
+		pinit(ppp, ppp->lcp);
+		setphase(ppp, Plink);
+		break;
+	case Plink:
+		/* link down */
+		switch(oldphase) {
+		case Pnet:
+			newstate(ppp, ppp->ipcp, Sclosed);
+		}
+		break;
+	case Pauth:
+		if(ppp->usepap)
+			sendpap(ppp);
+		else if(!ppp->usechap)
+			setphase(ppp, Pnet);
+		break;
+	case Pnet:
+		pinit(ppp, ppp->ipcp);
+		break;
+	case Pterm:
+		/* what? */
+		break;
+	}
+}
+
+static void
+pinit(PPP *ppp, Pstate *p)
+{
+	p->timeout = 0;
+
+	switch(p->proto){
+	case Plcp:
+		ppp->magic = TK2MS(MACHP(0)->ticks);
+		ppp->xctlmap = 0xffffffff;
+		ppp->period = 0;
+		p->optmask = 0xffffffff;
+		ppp->rctlmap = 0;
+		ppp->ipcp->state = Sclosed;
+		ppp->ipcp->optmask = 0xffffffff;
+
+		/* quality goo */
+		ppp->timeout = 0;
+		memset(&ppp->in, 0, sizeof(ppp->in));
+		memset(&ppp->out, 0, sizeof(ppp->out));
+		memset(&ppp->pin, 0, sizeof(ppp->pin));
+		memset(&ppp->pout, 0, sizeof(ppp->pout));
+		memset(&ppp->sin, 0, sizeof(ppp->sin));
+		break;
+	case Pipcp:
+		if(ppp->localfrozen == 0)
+			invalidate(ppp->local);
+		if(ppp->remotefrozen == 0)
+			invalidate(ppp->remote);
+		p->optmask = 0xffffffff;
+		ppp->ctcp = compress_init(ppp->ctcp);
+		ppp->usedns = 3;
+		invalidate(ppp->dns1);
+		invalidate(ppp->dns2);
+		break;
+	}
+	p->confid = p->rcvdconfid = -1;
+	config(ppp, p, 1);
+	newstate(ppp, p, Sreqsent);
+}
+
+/*
+ *  change protocol to a new state.
+ */
+static void
+newstate(PPP *ppp, Pstate *p, int state)
+{
+	netlog(ppp->f, Logppp, "%ux %ux %s->%s ctlmap %lux/%lux flags %ux mtu %d mru %d\n", ppp, p->proto,
+		snames[p->state], snames[state], ppp->rctlmap, ppp->xctlmap, p->flags,
+		ppp->mtu, ppp->mru);
+
+	if(p->proto == Plcp) {
+		if(state == Sopened)
+			setphase(ppp, Pauth);
+		else if(state == Sclosed)
+			setphase(ppp, Pdead);
+		else if(p->state == Sopened)
+			setphase(ppp, Plink);
+	}
+
+	if(p->proto == Pipcp && state == Sopened && validv4(ppp->local) && validv4(ppp->remote)){
+		netlog(ppp->f, Logppp, "pppnewstate: local %I remote %I\n", ppp->local, ppp->remote);
+		ipmove(pppdns[0], ppp->dns1);
+		ipmove(pppdns[1], ppp->dns2);
+		ipconnect(ppp);
+		/* if this is the only network, set up a default route */
+//		if(ppp->ifc->link==nil)		/* how??? */
+			setdefroute(ppp, ppp->remote);
+		errlog(ppp, Enoerror);
+	}
+
+	p->state = state;
+}
+
+static void
+remove(PPP *ppp)
+{
+	free(ppp->ipcp);
+	ppp->ipcp = 0;
+	free(ppp->ctcp);
+	ppp->ctcp = 0;
+	free(ppp->lcp);
+	ppp->lcp = 0;
+	if (ppp->inbuf) {
+		freeb(ppp->inbuf);
+		ppp->inbuf = nil;
+	}
+	if (ppp->outbuf) {
+		freeb(ppp->outbuf);
+		ppp->outbuf = nil;
+	}
+	free(ppp);
+}
+
+void
+pppclose(PPP *ppp)
+{
+	hangup(ppp);
+	remove(ppp);
+}
+
+static void
+dumpblock(Block *b)
+{
+	char x[256];
+	int i;
+
+	for(i = 0; i < (sizeof(x)-1)/3 && b->rp+i < b->wp; i++)
+		sprint(&x[3*i], "%2.2ux ", b->rp[i]);
+	print("%s\n", x);
+}
+
+/* returns (protocol, information) */
+static int
+getframe(PPP *ppp, Block **info)
+{
+	uchar *p, *from, *to;
+	int n, len, proto;
+	ulong c;
+	ushort fcs;
+	Block *buf, *b;
+
+	buf = ppp->inbuf;
+	for(;;){
+		/* read till we hit a frame byte or run out of room */
+		for(p = buf->rp; buf->wp < buf->lim;){
+			for(; p < buf->wp; p++)
+				if(*p == HDLC_frame)
+					goto break2;
+
+			len = buf->lim - buf->wp;
+			n = 0;
+			if(ppp->dchan != nil)
+				n = kchanio(ppp->dchan, buf->wp, len, OREAD);
+				netlog(ppp->f, Logppp, "ppp kchanio %d bytes\n", n);
+			if(n <= 0){
+				buf->wp = buf->rp;
+//				if(n < 0)
+//					print("ppp kchanio(%s) returned %d: %r",
+//						ppp->dchan->path->elem, n);
+				*info = nil;
+				return 0;
+			}
+			buf->wp += n;
+		}
+break2:
+
+		/* copy into block, undoing escapes, and caculating fcs */
+		fcs = PPP_initfcs;
+		b = allocb(p - buf->rp);
+		to = b->wp;
+		for(from = buf->rp; from != p;){
+			c = *from++;
+			if(c == HDLC_esc){
+				if(from == p)
+					break;
+				c = *from++ ^ 0x20;
+			} else if((c < 0x20) && (ppp->rctlmap & (1 << c)))
+				continue;
+			*to++ = c;
+			fcs = (fcs >> 8) ^ fcstab[(fcs ^ c) & 0xff];
+		}
+
+		/* copy down what's left in buffer */
+		p++;
+		memmove(buf->rp, p, buf->wp - p);
+		n = p - buf->rp;
+		buf->wp -= n;
+		b->wp = to - 2;
+
+		/* return to caller if checksum matches */
+		if(fcs == PPP_goodfcs){
+			if(b->rp[0] == PPP_addr && b->rp[1] == PPP_ctl)
+				b->rp += 2;
+			proto = *b->rp++;
+			if((proto & 0x1) == 0)
+				proto = (proto<<8) | *b->rp++;
+			if(b->rp < b->wp){
+				ppp->in.bytes += n;
+				ppp->in.packets++;
+				*info = b;
+				return proto;
+			}
+		} else if(BLEN(b) > 0){
+			ppp->ifc->inerr++;
+			ppp->in.discards++;
+			netlog(ppp->f, Logppp, "len %d/%d cksum %ux (%ux %ux %ux %ux)\n",
+				BLEN(b), BLEN(buf), fcs, b->rp[0],
+				b->rp[1], b->rp[2], b->rp[3]);
+		}
+
+		freeblist(b);
+	}
+	*info = nil;
+	return 0;
+}
+
+/* send a PPP frame */
+static Block *
+putframe(PPP *ppp, int proto, Block *b)
+{
+	Block *buf;
+	uchar *to, *from;
+	ushort fcs;
+	ulong ctlmap;
+	int c;
+	Block *bp;
+
+	if(ppp->dchan == nil){
+		netlog(ppp->f, Logppp, "putframe: dchan down\n");
+		errlog(ppp, Ehungup);
+		return b;
+	}
+	netlog(ppp->f, Logppp, "putframe %ux %d %d (%d bytes)\n", proto, b->rp[0], b->rp[1], BLEN(b));
+
+	ppp->out.packets++;
+
+	if(proto == Plcp)
+		ctlmap = 0xffffffff;
+	else
+		ctlmap = ppp->xctlmap;
+
+	/* make sure we have head room */
+	if(b->rp - b->base < 4){
+		b = padblock(b, 4);
+		b->rp += 4;
+	}
+
+	/* add in the protocol and address, we'd better have left room */
+	from = b->rp;
+	*--from = proto;
+	if(!(ppp->lcp->flags&Fpc) || proto > 0x100 || proto == Plcp)
+		*--from = proto>>8;
+	if(!(ppp->lcp->flags&Fac) || proto == Plcp){
+		*--from = PPP_ctl;
+		*--from = PPP_addr;
+	}
+
+	qlock(&ppp->outlock);
+	buf = ppp->outbuf;
+
+	/* escape and checksum the body */
+	fcs = PPP_initfcs;
+	to = buf->rp;
+
+	*to++ = HDLC_frame;
+
+	for(bp = b; bp; bp = bp->next){
+		if(bp != b)
+			from = bp->rp;
+		for(; from < bp->wp; from++){
+			c = *from;
+			if(c == HDLC_frame || c == HDLC_esc
+			   || (c < 0x20 && ((1<<c) & ctlmap))){
+				*to++ = HDLC_esc;
+				*to++ = c ^ 0x20;
+			} else 
+				*to++ = c;
+			fcs = (fcs >> 8) ^ fcstab[(fcs ^ c) & 0xff];
+		}
+	}
+
+	/* add on and escape the checksum */
+	fcs = ~fcs;
+	c = fcs;
+	if(c == HDLC_frame || c == HDLC_esc
+	   || (c < 0x20 && ((1<<c) & ctlmap))){
+		*to++ = HDLC_esc;
+		*to++ = c ^ 0x20;
+	} else 
+		*to++ = c;
+	c = fcs>>8;
+	if(c == HDLC_frame || c == HDLC_esc
+	   || (c < 0x20 && ((1<<c) & ctlmap))){
+		*to++ = HDLC_esc;
+		*to++ = c ^ 0x20;
+	} else 
+		*to++ = c;
+
+	/* add frame marker and send */
+	*to++ = HDLC_frame;
+	buf->wp = to;
+	if(ppp->dchan == nil){
+		netlog(ppp->f, Logppp, "putframe: dchan down\n");
+		errlog(ppp, Ehungup);
+	}else{
+		kchanio(ppp->dchan, buf->rp, BLEN(buf), OWRITE);
+		ppp->out.bytes += BLEN(buf);
+	}
+
+	qunlock(&ppp->outlock);
+	return b;
+}
+
+#define IPB2LCP(b) ((Lcpmsg*)((b)->wp-4))
+
+static Block*
+alloclcp(int code, int id, int len)
+{
+	Block *b;
+	Lcpmsg *m;
+
+	/*
+	 *  leave room for header
+	 */
+	b = allocb(len);
+
+	m = (Lcpmsg*)b->wp;
+	m->code = code;
+	m->id = id;
+	b->wp += 4;
+
+	return b;
+}
+
+static void
+putao(Block *b, int type, int aproto, int alg)
+{
+	*b->wp++ = type;
+	*b->wp++ = 5;
+	hnputs(b->wp, aproto);
+	b->wp += 2;
+	*b->wp++ = alg;
+}
+
+static void
+putlo(Block *b, int type, ulong val)
+{
+	*b->wp++ = type;
+	*b->wp++ = 6;
+	hnputl(b->wp, val);
+	b->wp += 4;
+}
+
+static void
+putv4o(Block *b, int type, Ipaddr val)
+{
+	*b->wp++ = type;
+	*b->wp++ = 6;
+	if(v6tov4(b->wp, val) < 0){
+		/*panic("putv4o")*/;
+	}
+	b->wp += 4;
+}
+
+static void
+putso(Block *b, int type, ulong val)
+{
+	*b->wp++ = type;
+	*b->wp++ = 4;
+	hnputs(b->wp, val);
+	b->wp += 2;
+}
+
+static void
+puto(Block *b, int type)
+{
+	*b->wp++ = type;
+	*b->wp++ = 2;
+}
+
+/*
+ *  send configuration request
+ */
+static void
+config(PPP *ppp, Pstate *p, int newid)
+{
+	Block *b;
+	Lcpmsg *m;
+	int id;
+
+	if(newid){
+		id = ++(p->id);
+		p->confid = id;
+		p->timeout = Timeout;
+	} else
+		id = p->confid;
+	b = alloclcp(Lconfreq, id, 256);
+	m = IPB2LCP(b);
+	USED(m);
+
+	switch(p->proto){
+	case Plcp:
+		if(p->optmask & Fmagic)
+			putlo(b, Omagic, ppp->magic);
+		if(p->optmask & Fmtu)
+			putso(b, Omtu, ppp->mru);
+		if(p->optmask & Fac)
+			puto(b, Oac);
+		if(p->optmask & Fpc)
+			puto(b, Opc);
+		if(p->optmask & Fctlmap)
+			putlo(b, Octlmap, 0);	/* we don't want anything escaped */
+		break;
+	case Pipcp:
+		if((p->optmask & Fipaddr) /*&& validv4(ppp->local)*/)
+			putv4o(b, Oipaddr, ppp->local);
+		if(!nocompress && (p->optmask & Fipcompress)){
+			*b->wp++ = Oipcompress;
+			*b->wp++ = 6;
+			hnputs(b->wp, Pvjctcp);
+			b->wp += 2;
+			*b->wp++ = MAX_STATES-1;
+			*b->wp++ = 1;
+		}
+		if(ppp->usedns & 1)
+			putlo(b, Oipdns, 0);
+		if(ppp->usedns & 2)
+			putlo(b, Oipdns2, 0);
+		break;
+	}
+
+	hnputs(m->len, BLEN(b));
+	b = putframe(ppp, p->proto, b);
+	freeblist(b);
+}
+
+/*
+ *  parse configuration request, sends an ack or reject packet
+ *
+ *	returns:	-1 if request was syntacticly incorrect
+ *			 0 if packet was accepted
+ *			 1 if packet was rejected
+ */
+static int
+getopts(PPP *ppp, Pstate *p, Block *b)
+{
+	Lcpmsg *m, *repm;	
+	Lcpopt *o;
+	uchar *cp;
+	ulong rejecting, nacking, flags, proto;
+	ulong mtu, ctlmap, period;
+	ulong x;
+	Block *repb;
+	Ipaddr ipaddr;
+
+	rejecting = 0;
+	nacking = 0;
+	flags = 0;
+
+	/* defaults */
+	invalidate(ipaddr);
+	mtu = ppp->mtu;
+
+	ctlmap = 0xffffffff;
+	period = 0;
+
+	m = (Lcpmsg*)b->rp;
+	repb = alloclcp(Lconfack, m->id, BLEN(b));
+	repm = IPB2LCP(repb);
+
+	/* copy options into ack packet */
+	memmove(repm->data, m->data, b->wp - m->data);
+	repb->wp += b->wp - m->data;
+
+	/* look for options we don't recognize or like */
+	for(cp = m->data; cp < b->wp; cp += o->len){
+		o = (Lcpopt*)cp;
+		if(cp + o->len > b->wp || o->len == 0){
+			freeblist(repb);
+			netlog(ppp->f, Logppp, "ppp %s: bad option length %ux\n", ppp->ifc->dev,
+				o->type);
+			return -1;
+		}
+
+		switch(p->proto){
+		case Plcp:
+			switch(o->type){
+			case Oac:
+				flags |= Fac;
+				continue;
+			case Opc:
+				flags |= Fpc;
+				continue;
+			case Omtu:
+				mtu = nhgets(o->data);
+				if(mtu < ppp->ifc->m->mintu){
+					netlog(ppp->f, Logppp, "bogus mtu %d\n", mtu);
+					mtu = ppp->ifc->m->mintu;
+				}
+				continue;
+			case Omagic:
+				if(ppp->magic == nhgetl(o->data))
+					netlog(ppp->f, Logppp, "ppp: possible loop\n");
+				continue;
+			case Octlmap:
+				ctlmap = nhgetl(o->data);
+				continue;
+			case Oquality:
+				proto = nhgets(o->data);
+				if(proto != Plqm)
+					break;
+				x = nhgetl(o->data+2)*10;
+				period = (x+Period-1)/Period;
+				continue;
+			case Oauth:
+				proto = nhgets(o->data);
+				if(proto == Ppap && ppp->chapname[0] && ppp->secret[0]){
+					ppp->usepap = 1;
+					netlog(ppp->f, Logppp, "PPP %s: select PAP\n", ppp->ifc->dev);
+					continue;
+				}
+				if(proto != Pchap || o->data[2] != APmd5){
+					if(!nacking){
+						nacking = 1;
+						repb->wp = repm->data;
+						repm->code = Lconfnak;
+					}
+					putao(repb, Oauth, Pchap, APmd5);
+				}
+				else
+					ppp->usechap = 1;
+				ppp->usepap = 0;
+				continue;
+			}
+			break;
+		case Pipcp:
+			switch(o->type){
+			case Oipaddr:	
+				v4tov6(ipaddr, o->data);
+				if(!validv4(ppp->remote))
+					continue;
+				if(!validv4(ipaddr) && !rejecting){
+					/* other side requesting an address */
+					if(!nacking){
+						nacking = 1;
+						repb->wp = repm->data;
+						repm->code = Lconfnak;
+					}
+					putv4o(repb, Oipaddr, ppp->remote);
+				}
+				continue;
+			case Oipcompress:
+				proto = nhgets(o->data);
+				if(nocompress || proto != Pvjctcp || compress_negotiate(ppp->ctcp, o->data+2) < 0)
+					break;
+				flags |= Fipcompress;
+				continue;
+			}
+			break;
+		}
+
+		/* come here if option is not recognized */
+		if(!rejecting){
+			rejecting = 1;
+			repb->wp = repm->data;
+			repm->code = Lconfrej;
+		}
+		netlog(ppp->f, Logppp, "ppp %s: bad %ux option %d\n", ppp->ifc->dev, p->proto, o->type);
+		memmove(repb->wp, o, o->len);
+		repb->wp += o->len;
+	}
+
+	/* permanent changes only after we know that we liked the packet */
+	if(!rejecting && !nacking){
+		switch(p->proto){
+		case Plcp:
+			netlog(ppp->f, Logppp, "Plcp: mtu: %d %d x:%lux/r:%lux %lux\n", mtu, ppp->mtu, ppp->xctlmap, ppp->rctlmap, ctlmap);
+			ppp->period = period;
+			ppp->xctlmap = ctlmap;
+			if(mtu > Maxmtu)
+				mtu = Maxmtu;
+			if(mtu < Minmtu)
+				mtu = Minmtu;
+			ppp->mtu = mtu;
+			break;
+		case Pipcp:
+			if(validv4(ipaddr) && ppp->remotefrozen == 0)
+ 				ipmove(ppp->remote, ipaddr);
+			break;
+		}
+		p->flags = flags;
+	}
+
+	hnputs(repm->len, BLEN(repb));
+	repb = putframe(ppp, p->proto, repb);
+	freeblist(repb);
+
+	return rejecting || nacking;
+}
+
+/*
+ *  parse configuration rejection, just stop sending anything that they
+ *  don't like (except for ipcp address nak).
+ */
+static void
+rejopts(PPP *ppp, Pstate *p, Block *b, int code)
+{
+	Lcpmsg *m;
+	Lcpopt *o;
+
+	/* just give up trying what the other side doesn't like */
+	m = (Lcpmsg*)b->rp;
+	for(b->rp = m->data; b->rp < b->wp; b->rp += o->len){
+		o = (Lcpopt*)b->rp;
+		if(b->rp + o->len > b->wp || o->len == 0){
+			netlog(ppp->f, Logppp, "ppp %s: bad roption length %ux\n", ppp->ifc->dev,
+				o->type);
+			return;
+		}
+
+		if(code == Lconfrej){
+			if(o->type < 8*sizeof(p->optmask))
+				p->optmask &= ~(1<<o->type);
+			if(o->type == Oipdns)
+				ppp->usedns &= ~1;
+			else if(o->type == Oipdns2)
+				ppp->usedns &= ~2;
+			netlog(ppp->f, Logppp, "ppp %s: %ux rejecting %d\n", ppp->ifc->dev, p->proto,
+				o->type);
+			continue;
+		}
+
+		switch(p->proto){
+		case Plcp:
+			switch(o->type){
+			case Octlmap:
+				ppp->rctlmap = nhgetl(o->data);
+				break;
+			default:
+				if(o->type < 8*sizeof(p->optmask))
+					p->optmask &= ~(1<<o->type);
+				break;
+			};
+		case Pipcp:
+			switch(o->type){
+			case Oipaddr:
+				if(!validv4(ppp->local))
+					v4tov6(ppp->local, o->data);
+//				if(o->type < 8*sizeof(p->optmask))
+//					p->optmask &= ~(1<<o->type);
+				break;
+			case Oipdns:
+				if(!validv4(ppp->dns1))
+					v4tov6(ppp->dns1, o->data);
+				ppp->usedns &= ~1;
+				break;
+			case Oipdns2:
+				if(!validv4(ppp->dns2))
+					v4tov6(ppp->dns2, o->data);
+				ppp->usedns &= ~2;
+				break;
+			default:
+				if(o->type < 8*sizeof(p->optmask))
+					p->optmask &= ~(1<<o->type);
+				break;
+			}
+			break;
+		}
+	}
+}
+
+
+/*
+ *  put a messages through the lcp or ipcp state machine.  They are
+ *  very similar.
+ */
+static void
+rcv(PPP *ppp, Pstate *p, Block *b)
+{
+	ulong len;
+	int err;
+	Lcpmsg *m;
+
+	if(BLEN(b) < 4){
+		netlog(ppp->f, Logppp, "ppp %s: short lcp message\n", ppp->ifc->dev);
+		freeblist(b);
+		return;
+	}
+	m = (Lcpmsg*)b->rp;
+	len = nhgets(m->len);
+	if(BLEN(b) < len){
+		netlog(ppp->f, Logppp, "ppp %s: short lcp message\n", ppp->ifc->dev);
+		freeblist(b);
+		return;
+	}
+
+	netlog(ppp->f, Logppp, "ppp: %ux rcv %d len %d id %d/%d/%d\n",
+		p->proto, m->code, len, m->id, p->confid, p->id);
+
+	if(p->proto != Plcp && ppp->lcp->state != Sopened){
+		netlog(ppp->f, Logppp, "ppp: non-lcp with lcp not open\n");
+		freeb(b);
+		return;
+	}
+
+	qlock(ppp);
+	switch(m->code){
+	case Lconfreq:
+		/* flush the output queue */
+		if(p->state == Sopened && p->proto == Plcp)
+			kchanio(ppp->cchan, "f", 1, OWRITE);
+
+		printopts(ppp, p, b, 0);
+		err = getopts(ppp, p, b);
+		if(err < 0)
+			break;
+
+		if(m->id == p->rcvdconfid)
+			break;			/* don't change state for duplicates */
+		p->rcvdconfid = m->id;
+
+		switch(p->state){
+		case Sackrcvd:
+			if(err)
+				break;
+			newstate(ppp, p, Sopened);
+			break;
+		case Sclosed:
+		case Sopened:
+			config(ppp, p, 1);
+			if(err == 0)
+				newstate(ppp, p, Sacksent);
+			else
+				newstate(ppp, p, Sreqsent);
+			break;
+			break;
+		case Sreqsent:
+		case Sacksent:
+			if(err == 0)
+				newstate(ppp, p, Sacksent);
+			else
+				newstate(ppp, p, Sreqsent);
+			break;
+		}
+		break;
+	case Lconfack:
+		if(p->confid != m->id){
+			/* ignore if it isn't the message we're sending */
+			netlog(ppp->f, Logppp, "ppp: dropping confack\n");
+			break;
+		}
+		p->confid = -1;		/* ignore duplicates */
+		p->id++;		/* avoid sending duplicates */
+
+		switch(p->state){
+		case Sopened:
+		case Sackrcvd:
+			config(ppp, p, 1);
+			newstate(ppp, p, Sreqsent);
+			break;
+		case Sreqsent:
+			newstate(ppp, p, Sackrcvd);
+			break;
+		case Sacksent:
+			newstate(ppp, p, Sopened);
+			break;
+		}
+		break;
+	case Lconfrej:
+	case Lconfnak:
+		if(p->confid != m->id) {
+			/* ignore if it isn't the message we're sending */
+			netlog(ppp->f, Logppp, "ppp: dropping confrej or confnak\n");
+			break;
+		}
+		p->confid = -1;		/* ignore duplicates */
+		p->id++;		/* avoid sending duplicates */
+
+		switch(p->state){
+		case Sopened:
+		case Sackrcvd:
+			config(ppp, p, 1);
+			newstate(ppp, p, Sreqsent);
+			break;
+		case Sreqsent:
+		case Sacksent:
+			printopts(ppp, p, b, 0);
+			rejopts(ppp, p, b, m->code);
+			config(ppp, p, 1);
+			break;
+		}
+		break;
+	case Ltermreq:
+		m->code = Ltermack;
+		b = putframe(ppp, p->proto, b);
+
+		switch(p->state){
+		case Sackrcvd:
+		case Sacksent:
+			newstate(ppp, p, Sreqsent);
+			break;
+		case Sopened:
+			newstate(ppp, p, Sclosing);
+			break;
+		}
+		break;
+	case Ltermack:
+		if(p->termid != m->id)	/* ignore if it isn't the message we're sending */
+			break;
+
+		if(p->proto == Plcp)
+			ppp->ipcp->state = Sclosed;
+		switch(p->state){
+		case Sclosing:
+			newstate(ppp, p, Sclosed);
+			break;
+		case Sackrcvd:
+			newstate(ppp, p, Sreqsent);
+			break;
+		case Sopened:
+			config(ppp, p, 0);
+			newstate(ppp, p, Sreqsent);
+			break;
+		}
+		break;
+	case Lcoderej:
+		netlog(ppp->f, Logppp, "ppp %s: code reject %d\n", ppp->ifc->dev, m->data[0]);
+		break;
+	case Lprotorej:
+		netlog(ppp->f, Logppp, "ppp %s: proto reject %lux\n", ppp->ifc->dev, nhgets(m->data));
+		break;
+	case Lechoreq:
+		m->code = Lechoack;
+		b = putframe(ppp, p->proto, b);
+		break;
+	case Lechoack:
+	case Ldiscard:
+		/* nothing to do */
+		break;
+	}
+
+	qunlock(ppp);
+	freeblist(b);
+}
+
+/*
+ *  timer for protocol state machine
+ */
+static void
+ptimer(PPP *ppp, Pstate *p)
+{
+	if(p->state == Sopened || p->state == Sclosed)
+		return;
+
+	p->timeout--;
+	switch(p->state){
+	case Sclosing:
+		sendtermreq(ppp, p);
+		break;
+	case Sreqsent:
+	case Sacksent:
+		if(p->timeout <= 0){
+			if(p->proto && ppp->cchan != nil)
+				kchanio(ppp->cchan, "f", 1, OWRITE); /* flush output queue */
+			newstate(ppp, p, Sclosed);
+		} else {
+			config(ppp, p, 0);
+		}
+		break;
+	case Sackrcvd:
+		if(p->timeout <= 0){
+			if(p->proto && ppp->cchan != nil)
+				kchanio(ppp->cchan, "f", 1, OWRITE); /* flush output queue */
+			newstate(ppp, p, Sclosed);
+		}
+		else {
+			config(ppp, p, 0);
+			newstate(ppp, p, Sreqsent);
+		}
+		break;
+	}
+}
+
+/*
+ *  timer for ppp
+ */
+static void
+ppptimer(void *arg)
+{
+	PPP *ppp;
+
+	ppp = arg;
+	ppp->timep = up;
+	if(waserror()){
+		netlog(ppp->f, Logppp, "ppptimer: %I: %s\n", ppp->local, up->env->errstr);
+		ppp->timep = 0;
+		pexit("hangup", 1);
+	}
+	for(;;){
+		tsleep(&up->sleep, return0, nil, Period);
+		if(ppp->pppup){
+			qlock(ppp);
+
+			ptimer(ppp, ppp->lcp);
+			if(ppp->lcp->state == Sopened)
+				ptimer(ppp, ppp->ipcp);
+
+			if(ppp->period && --(ppp->timeout) <= 0){
+				ppp->timeout = ppp->period;
+				putlqm(ppp);
+			}
+
+			qunlock(ppp);
+		}
+	}
+}
+
+static void
+setdefroute(PPP *ppp, Ipaddr gate)
+{
+	int fd, n;
+	char path[128], msg[128];
+
+	snprint(path, sizeof path, "#I%d/iproute", ppp->f->dev);
+	fd = kopen(path, ORDWR);
+	if(fd < 0)
+		return;
+	n = snprint(msg, sizeof(msg), "add 0 0 %I", gate);
+	kwrite(fd, msg, n);
+	kclose(fd);
+}
+
+static void
+ipconnect(PPP *ppp)
+{
+	int fd, n;
+	char path[128], msg[128];
+
+	snprint(path, sizeof path, "#I%d/ipifc/%d/ctl", ppp->f->dev, ppp->ifc->conv->x);
+	fd = kopen(path, ORDWR);
+	if(fd < 0)
+		return;
+	n = snprint(msg, sizeof(msg), "connect %I 255.255.255.255 %I", ppp->local, ppp->remote);
+	if (kwrite(fd, msg, n) != n)
+		print("ppp ipconnect: %s: %r\n", msg);
+	kclose(fd);
+}
+
+PPP*
+pppopen(PPP *ppp, char *dev,
+	Ipaddr ipaddr, Ipaddr remip,
+	int mtu, int framing,
+	char *chapname, char *secret)
+{
+	int fd, cfd;
+	char ctl[Maxpath];
+
+	invalidate(ppp->remote);
+	invalidate(ppp->local);
+	invalidate(ppp->dns1);
+	invalidate(ppp->dns2);
+	ppp->mtu = Defmtu;
+	ppp->mru = mtu;
+	ppp->framing = framing;
+
+	if(remip != nil && validv4(remip)){
+		ipmove(ppp->remote, remip);
+		ppp->remotefrozen = 1;
+	}
+	if(ipaddr != nil && validv4(ipaddr)){
+		ipmove(ppp->local, ipaddr);
+		ppp->localfrozen = 1;
+	}
+
+	/* authentication goo */
+	ppp->secret[0] = 0;
+	if(secret != nil)
+		strncpy(ppp->secret, secret, sizeof(ppp->secret));
+	ppp->chapname[0] = 0;
+	if(chapname != nil)
+		strncpy(ppp->chapname, chapname, sizeof(ppp->chapname));
+
+	if(strchr(dev, '!'))
+		fd = kdial(dev, nil, nil, nil);
+	else
+		fd = kopen(dev, ORDWR);
+	if(fd < 0){
+		netlog(ppp->f, Logppp, "ppp: can't open %s\n", dev);
+		return nil;
+	}
+	ppp->dchan = fdtochan(up->env->fgrp, fd, ORDWR, 0, 1);
+	kclose(fd);
+
+	/* set up serial line */
+/* XXX this stuff belongs in application, not driver */
+	sprint(ctl, "%sctl", dev);
+	cfd = kopen(ctl, ORDWR);
+	if(cfd >= 0){
+		ppp->cchan = fdtochan(up->env->fgrp, cfd, ORDWR, 0, 1);
+		kclose(cfd);
+		kchanio(ppp->cchan, "m1", 2, OWRITE);	/* cts/rts flow control/fifo's) on */
+		kchanio(ppp->cchan, "q64000", 6, OWRITE);/* increas q size to 64k */
+		kchanio(ppp->cchan, "n1", 2, OWRITE);	/* nonblocking writes on */
+		kchanio(ppp->cchan, "r1", 2, OWRITE);	/* rts on */
+		kchanio(ppp->cchan, "d1", 2, OWRITE);	/* dtr on */
+	}
+
+	ppp->pppup = 1;
+	init(ppp);
+	return ppp;
+}
+
+static void
+hangup(PPP *ppp)
+{
+	qlock(ppp);
+	if(waserror()){
+		qunlock(ppp);
+		nexterror();
+	}
+	netlog(ppp->f, Logppp, "PPP Hangup\n");
+	errlog(ppp, Ehungup);
+	if(ppp->pppup && ppp->cchan != nil){
+		kchanio(ppp->cchan, "f", 1, OWRITE);	/* flush */
+		kchanio(ppp->cchan, "h", 1, OWRITE);	/* hangup */
+	}
+	cclose(ppp->dchan);
+	cclose(ppp->cchan);
+	ppp->dchan = nil;
+	ppp->cchan = nil;
+	ppp->pppup = 0;
+	qunlock(ppp);
+	poperror();
+}
+
+/* return next input IP packet */
+Block*
+pppread(PPP *ppp)
+{
+	Block *b;
+	int proto;
+	Lcpmsg *m;
+
+	for(;;){
+		proto = getframe(ppp, &b);
+		if(b == nil)
+			return nil;
+		netlog(ppp->f, Logppp, "ppp: read proto %d len %d\n", proto, blocklen(b));
+		switch(proto){
+		case Plcp:
+			rcv(ppp, ppp->lcp, b);
+			break;
+		case Pipcp:
+			rcv(ppp, ppp->ipcp, b);
+			break;
+		case Pip:
+			if(ppp->ipcp->state == Sopened)
+				return b;
+			freeblist(b);
+			break;
+		case Plqm:
+			getlqm(ppp, b);
+			break;
+		case Pchap:
+			getchap(ppp, b);
+			break;
+		case Ppap:
+			getpap(ppp, b);
+			break;
+		case Pvjctcp:
+		case Pvjutcp:
+			if(ppp->ipcp->state == Sopened){
+				b = tcpuncompress(ppp->ctcp, b, proto, ppp->f);
+				if(b != nil)
+					return b;
+			}
+			freeblist(b);
+			break;
+		default:
+			netlog(ppp->f, Logppp, "unknown proto %ux\n", proto);
+			if(ppp->lcp->state == Sopened){
+				/* reject the protocol */
+				b->rp -= 6;
+				m = (Lcpmsg*)b->rp;
+				m->code = Lprotorej;
+				m->id = ++ppp->lcp->id;
+				hnputs(m->data, proto);
+				hnputs(m->len, BLEN(b));
+				b = putframe(ppp, Plcp, b);
+			}
+			freeblist(b);
+			break;
+		}
+	}
+	return nil;		/* compiler confused */
+}
+
+/* transmit an IP packet */
+int
+pppwrite(PPP *ppp, Block *b)
+{
+	ushort proto;
+	int r;
+
+	qlock(ppp);
+
+	/* can't send ip packets till we're established */
+	if(ppp->ipcp->state != Sopened)
+		goto ret;
+
+	/* link hung up */
+	if(ppp->dchan == nil)
+		goto ret;
+
+	b = concatblock(b);		/* or else compression will barf */
+
+	proto = Pip;
+	if(ppp->ipcp->flags & Fipcompress)
+		proto = compress(ppp->ctcp, b, ppp->f);
+	b = putframe(ppp, proto, b);
+
+
+ret:
+	qunlock(ppp);
+
+	r = blocklen(b);
+	netlog(ppp->f, Logppp, "ppp wrt len %d\n", r);
+
+	freeblist(b);
+	return r;
+}
+
+/*
+ *  link quality management
+ */
+static void
+getlqm(PPP *ppp, Block *b)
+{
+	Qualpkt *p;
+
+	p = (Qualpkt*)b->rp;
+	if(BLEN(b) == sizeof(Qualpkt)){
+		ppp->in.reports++;
+		ppp->pout.reports = nhgetl(p->peeroutreports);
+		ppp->pout.packets = nhgetl(p->peeroutpackets);
+		ppp->pout.bytes = nhgetl(p->peeroutbytes);
+		ppp->pin.reports = nhgetl(p->peerinreports);
+		ppp->pin.packets = nhgetl(p->peerinpackets);
+		ppp->pin.discards = nhgetl(p->peerindiscards);
+		ppp->pin.errors = nhgetl(p->peerinerrors);
+		ppp->pin.bytes = nhgetl(p->peerinbytes);
+
+		/* save our numbers at time of reception */
+		memmove(&ppp->sin, &ppp->in, sizeof(Qualstats));
+
+	}
+	freeblist(b);
+	if(ppp->period == 0)
+		putlqm(ppp);
+
+}
+static void
+putlqm(PPP *ppp)
+{
+	Qualpkt *p;
+	Block *b;
+
+	b = allocb(sizeof(Qualpkt));
+	b->wp += sizeof(Qualpkt);
+	p = (Qualpkt*)b->rp;
+	hnputl(p->magic, 0);
+
+	/* heresay (what he last told us) */
+	hnputl(p->lastoutreports, ppp->pout.reports);
+	hnputl(p->lastoutpackets, ppp->pout.packets);
+	hnputl(p->lastoutbytes, ppp->pout.bytes);
+
+	/* our numbers at time of last reception */
+	hnputl(p->peerinreports, ppp->sin.reports);
+	hnputl(p->peerinpackets, ppp->sin.packets);
+	hnputl(p->peerindiscards, ppp->sin.discards);
+	hnputl(p->peerinerrors, ppp->sin.errors);
+	hnputl(p->peerinbytes, ppp->sin.bytes);
+
+	/* our numbers now */
+	hnputl(p->peeroutreports, ppp->out.reports+1);
+	hnputl(p->peeroutpackets, ppp->out.packets+1);
+	hnputl(p->peeroutbytes, ppp->out.bytes+53/*hack*/);
+
+	b = putframe(ppp, Plqm, b);
+	freeblist(b);
+	ppp->out.reports++;
+}
+
+/*
+ *  challenge response dialog
+ */
+static void
+getchap(PPP *ppp, Block *b)
+{
+	Lcpmsg *m;
+	int len, vlen, n;
+	char md5buf[512];
+
+	m = (Lcpmsg*)b->rp;
+	len = nhgets(m->len);
+	if(BLEN(b) < len){
+		netlog(ppp->f, Logppp, "ppp %s: short chap message\n", ppp->ifc->dev);
+		freeblist(b);
+		return;
+	}
+
+	switch(m->code){
+	case Cchallenge:
+		vlen = m->data[0];
+		if(vlen > len - 5){
+			netlog(ppp->f, Logppp, "PPP %s: bad challenge len\n", ppp->ifc->dev);
+			freeblist(b);
+			break;
+		}
+
+		netlog(ppp->f, Logppp, "PPP %s: CHAP Challenge\n", ppp->ifc->dev);
+netlog(ppp->f, Logppp, "(secret %s chapname %s id %d)\n", ppp->secret, ppp->chapname, m->id);
+		/* create string to hash */
+		md5buf[0] = m->id;
+		strcpy(md5buf+1, ppp->secret);
+		n = strlen(ppp->secret) + 1;
+		memmove(md5buf+n, m->data+1, vlen);
+		n += vlen;
+		freeblist(b);
+
+		/* send reply */
+		len = 4 + 1 + 16 + strlen(ppp->chapname);
+		b = alloclcp(2, md5buf[0], len);
+		m = IPB2LCP(b);
+		m->data[0] = 16;
+		md5((uchar*)md5buf, n, m->data+1, 0);
+		memmove((char*)m->data+17, ppp->chapname, strlen(ppp->chapname));
+		hnputs(m->len, len);
+		b->wp += len-4;
+		b = putframe(ppp, Pchap, b);
+		break;
+	case Cresponse:
+		netlog(ppp->f, Logppp, "PPP %s: chap response?\n", ppp->ifc->dev);
+		break;
+	case Csuccess:
+		netlog(ppp->f, Logppp, "PPP %s: chap succeeded\n", ppp->ifc->dev);
+		setphase(ppp, Pnet);
+		break;
+	case Cfailure:
+		netlog(ppp->f, Logppp, "PPP %s: chap failed: %.*s\n", ppp->ifc->dev, len-4, m->data);
+		errlog(ppp, Eperm);
+		break;
+	default:
+		netlog(ppp->f, Logppp, "PPP %s: chap code %d?\n", ppp->ifc->dev, m->code);
+		break;
+	}
+	freeblist(b);
+}
+
+/*
+ *  password authentication protocol dialog
+ *	-- obsolete but all we know how to use with NT just now
+ */
+static void
+sendpap(PPP *ppp)
+{
+	Lcpmsg *m;
+	int clen, slen, len;
+	Block *b;
+	uchar *p;
+
+	clen = strlen(ppp->chapname);
+	slen = strlen(ppp->secret);
+	len = 4 + 1 + clen + 1 + slen;
+	ppp->papid = ++ppp->lcp->id;
+	b = alloclcp(Cpapreq, ppp->papid, len);
+	m = IPB2LCP(b);
+	p = m->data;
+	p[0] = clen;
+	memmove(p+1, ppp->chapname, clen);
+	p += clen + 1;
+	p[0] = slen;
+	memmove(p+1, ppp->secret, slen);
+	hnputs(m->len, len);
+	b->wp += len-4;
+	b = putframe(ppp, Ppap, b);
+	netlog(ppp->f, Logppp, "PPP %s: sent pap auth req (%d)\n", ppp->ifc->dev, len);
+	freeblist(b);
+}
+
+static void
+getpap(PPP *ppp, Block *b)
+{
+	Lcpmsg *m;
+	int len;
+
+	m = (Lcpmsg*)b->rp;
+	len = nhgets(m->len);
+	if(BLEN(b) < len){
+		netlog(ppp->f, Logppp, "ppp %s: short pap message\n", ppp->ifc->dev);
+		freeblist(b);
+		return;
+	}
+
+	switch(m->code){
+	case Cpapreq:
+		netlog(ppp->f, Logppp, "PPP %s: pap request?\n", ppp->ifc->dev);
+		break;
+	case Cpapack:
+		netlog(ppp->f, Logppp, "PPP %s: PAP succeeded\n", ppp->ifc->dev);
+		setphase(ppp, Pnet);
+		break;
+	case Cpapnak:
+		if(m->data[0])
+			netlog(ppp->f, Logppp, "PPP %s: PAP failed: %.*s\n", ppp->ifc->dev, len-5, m->data+1);
+		else
+			netlog(ppp->f, Logppp, "PPP %s: PAP failed\n", ppp->ifc->dev);
+		errlog(ppp, Eperm);
+		break;
+	default:
+		netlog(ppp->f, Logppp, "PPP %s: pap code %d?\n", ppp->ifc->dev, m->code);
+		break;
+	}
+	freeblist(b);
+}
+
+static void
+printopts(PPP *ppp, Pstate *p, Block *b, int send)
+{
+	Lcpmsg *m;	
+	Lcpopt *o;
+	int proto, x, period;
+	uchar *cp;
+	char *code, *dir;
+
+	m = (Lcpmsg*)b->rp;
+	switch(m->code) {
+	default: code = "<unknown>"; break;
+	case Lconfreq: code = "confrequest"; break;
+	case Lconfack: code = "confack"; break;
+	case Lconfnak: code = "confnak"; break;
+	case Lconfrej: code = "confreject"; break;
+	}
+
+	if(send)
+		dir = "send";
+	else
+		dir = "recv";
+
+	netlog(ppp->f, Logppp, "ppp: %s %s: id=%d\n", dir, code, m->id);
+
+	for(cp = m->data; cp < b->wp; cp += o->len){
+		o = (Lcpopt*)cp;
+		if(cp + o->len > b->wp || o->len == 0){
+			netlog(ppp->f, Logppp, "\tbad option length %ux\n", o->type);
+			return;
+		}
+
+		switch(p->proto){
+		case Plcp:
+			switch(o->type){
+			default:
+				netlog(ppp->f, Logppp, "\tunknown %d len=%d\n", o->type, o->len);
+				break;
+			case Omtu:
+				netlog(ppp->f, Logppp, "\tmtu = %d\n", nhgets(o->data));
+				break;
+			case Octlmap:
+				netlog(ppp->f, Logppp, "\tctlmap = %ux\n", nhgetl(o->data));
+				break;
+			case Oauth:
+				netlog(ppp->f, Logppp, "\tauth = ", nhgetl(o->data));
+				proto = nhgets(o->data);
+				switch(proto) {
+				default:
+					netlog(ppp->f, Logppp, "unknown auth proto %d\n", proto);
+					break;
+				case Ppap:
+					netlog(ppp->f, Logppp, "password\n");
+					break;
+				case Pchap:
+					netlog(ppp->f, Logppp, "chap %ux\n", o->data[2]);
+					break;
+				}
+				break;
+			case Oquality:
+				proto = nhgets(o->data);
+				switch(proto) {
+				default:
+					netlog(ppp->f, Logppp, "\tunknown quality proto %d\n", proto);
+					break;
+				case Plqm:
+					x = nhgetl(o->data+2)*10;
+					period = (x+Period-1)/Period;
+					netlog(ppp->f, Logppp, "\tlqm period = %d\n", period);
+					break;
+				}
+			case Omagic:
+				netlog(ppp->f, Logppp, "\tmagic = %ux\n", nhgetl(o->data));
+				break;
+			case Opc:
+				netlog(ppp->f, Logppp, "\tprotocol compress\n");
+				break;
+			case Oac:
+				netlog(ppp->f, Logppp, "\taddr compress\n");
+				break;
+			}
+			break;
+		case Pccp:
+			switch(o->type){
+			default:
+				netlog(ppp->f, Logppp, "\tunknown %d len=%d\n", o->type, o->len);
+				break;
+			case Ocoui:	
+				netlog(ppp->f, Logppp, "\tOUI\n");
+				break;
+			case Ocstac:
+				netlog(ppp->f, Logppp, "\tstac LZS\n");
+				break;
+			case Ocmppc:	
+				netlog(ppp->f, Logppp, "\tMicrosoft PPC len=%d %ux\n", o->len, nhgetl(o->data));
+				break;
+			}
+			break;
+		case Pecp:
+			switch(o->type){
+			default:
+				netlog(ppp->f, Logppp, "\tunknown %d len=%d\n", o->type, o->len);
+				break;
+			case Oeoui:	
+				netlog(ppp->f, Logppp, "\tOUI\n");
+				break;
+			case Oedese:
+				netlog(ppp->f, Logppp, "\tDES\n");
+				break;
+			}
+			break;
+		case Pipcp:
+			switch(o->type){
+			default:
+				netlog(ppp->f, Logppp, "\tunknown %d len=%d\n", o->type, o->len);
+				break;
+			case Oipaddrs:	
+				netlog(ppp->f, Logppp, "\tip addrs - deprecated\n");
+				break;
+			case Oipcompress:
+				netlog(ppp->f, Logppp, "\tip compress\n");
+				break;
+			case Oipaddr:	
+				netlog(ppp->f, Logppp, "\tip addr %V\n", o->data);
+				break;
+			case Oipdns:
+				netlog(ppp->f, Logppp, "\tdns addr %V\n", o->data);
+				break;
+			case Oipwins:	
+				netlog(ppp->f, Logppp, "\twins addr %V\n", o->data);
+				break;
+			case Oipdns2:
+				netlog(ppp->f, Logppp, "\tdns2 addr %V\n", o->data);
+				break;
+			case Oipwins2:	
+				netlog(ppp->f, Logppp, "\twins2 addr %V\n", o->data);
+				break;
+			}
+			break;
+		}
+	}
+}
+
+static void
+sendtermreq(PPP *ppp, Pstate *p)
+{
+	Block *b;
+	Lcpmsg *m;
+
+	p->termid = ++(p->id);
+	b = alloclcp(Ltermreq, p->termid, 4);
+	m = IPB2LCP(b);
+	hnputs(m->len, 4);
+	putframe(ppp, p->proto, b);
+	freeb(b);
+	newstate(ppp, p, Sclosing);
+}
+
+static void
+sendechoreq(PPP *ppp, Pstate *p)
+{
+	Block *b;
+	Lcpmsg *m;
+
+	p->termid = ++(p->id);
+	b = alloclcp(Lechoreq, p->id, 4);
+	m = IPB2LCP(b);
+	hnputs(m->len, 4);
+	putframe(ppp, p->proto, b);
+	freeb(b);
+}
+
+/*
+ *  return non-zero if this is a valid v4 address
+ */
+static int
+validv4(Ipaddr addr)
+{
+	return memcmp(addr, v4prefix, IPv4off) == 0;
+}
+
+static void
+invalidate(Ipaddr addr)
+{
+	ipmove(addr, IPnoaddr);
+}
--- /dev/null
+++ b/os/ip/ppp.h
@@ -1,0 +1,258 @@
+typedef struct PPP	PPP;
+typedef struct Pstate	Pstate;
+typedef struct Lcpmsg	Lcpmsg;
+typedef struct Lcpopt	Lcpopt;
+typedef struct Qualpkt	Qualpkt;
+typedef struct Qualstats Qualstats;
+typedef struct Tcpc	Tcpc;
+
+typedef uchar Ipaddr[IPaddrlen];
+
+enum
+{
+	HDLC_frame=	0x7e,
+	HDLC_esc=	0x7d,
+
+	/* PPP frame fields */
+	PPP_addr=	0xff,
+	PPP_ctl=	0x3,
+	PPP_initfcs=	0xffff,
+	PPP_goodfcs=	0xf0b8,
+
+	/* PPP phases */
+	Pdead=		0,	
+	Plink,				/* doing LCP */
+	Pauth,				/* doing chap */
+	Pnet,				/* doing IPCP, CCP */
+	Pterm,				/* closing down */
+
+	/* PPP protocol types */
+	Pip=		0x21,		/* internet */
+	Pvjctcp=	0x2d,		/* compressing van jacobson tcp */
+	Pvjutcp=	0x2f,		/* uncompressing van jacobson tcp */
+	Pcdata=		0xfd,		/* compressed datagram */
+	Pipcp=		0x8021,		/* ip control */
+	Pecp=		0x8053,		/* encryption control */
+	Pccp=		0x80fd,		/* compressed datagram control */
+	Plcp=		0xc021,		/* link control */
+	Ppap=		0xc023,		/* password auth. protocol */
+	Plqm=		0xc025,		/* link quality monitoring */
+	Pchap=		0xc223,		/* challenge/response */
+
+	/* LCP codes */
+	Lconfreq=	1,
+	Lconfack=	2,
+	Lconfnak=	3,
+	Lconfrej=	4,
+	Ltermreq=	5,
+	Ltermack=	6,
+	Lcoderej=	7,
+	Lprotorej=	8,
+	Lechoreq=	9,
+	Lechoack=	10,
+	Ldiscard=	11,
+
+	/* Lcp configure options */
+	Omtu=		1,
+	Octlmap=	2,
+	Oauth=		3,
+	Oquality=	4,
+	Omagic=		5,
+	Opc=		7,
+	Oac=		8,
+	Obad=		12,		/* for testing */
+
+	/* authentication protocols */
+	APmd5=		5,
+
+	/* lcp flags */
+	Fmtu=		1<<Omtu,
+	Fctlmap=	1<<Octlmap,
+	Fauth=		1<<Oauth,
+	Fquality=	1<<Oquality,
+	Fmagic=		1<<Omagic,
+	Fpc=		1<<Opc,
+	Fac=		1<<Oac,
+	Fbad=		1<<Obad,
+
+	/* Chap codes */
+	Cchallenge=	1,
+	Cresponse=	2,
+	Csuccess=	3,
+	Cfailure=	4,
+
+	/* Pap codes */
+	Cpapreq=		1,
+	Cpapack=		2,
+	Cpapnak=		3,
+
+	/* link states */
+	Sclosed=		0,
+	Sclosing,
+	Sreqsent,
+	Sackrcvd,
+	Sacksent,
+	Sopened,
+
+	/* ccp configure options */
+	Ocoui=		0,	/* proprietary compression */
+	Ocstac=		17,	/* stac electronics LZS */
+	Ocmppc=		18,	/* microsoft ppc */
+
+	/* ccp flags */
+	Fcoui=		1<<Ocoui,
+	Fcstac=		1<<Ocstac,
+	Fcmppc=		1<<Ocmppc,
+
+	/* ecp configure options */
+	Oeoui=		0,	/* proprietary compression */
+	Oedese=		1,	/* DES */
+
+	/* ecp flags */
+	Feoui=		1<<Oeoui,
+	Fedese=		1<<Oedese,
+
+	/* ipcp configure options */
+	Oipaddrs=	1,
+	Oipcompress=	2,
+	Oipaddr=	3,
+	Oipdns=		129,
+	Oipwins=	130,
+	Oipdns2=	131,
+	Oipwins2=	132,
+
+	/* ipcp flags */
+	Fipaddrs=	1<<Oipaddrs,
+	Fipcompress=	1<<Oipcompress,
+	Fipaddr=	1<<Oipaddr,
+
+	Period=		3*1000,	/* period of retransmit process (in ms) */
+	Timeout=	10,	/* xmit timeout (in Periods) */
+
+	MAX_STATES	= 16,		/* van jacobson compression states */
+	Defmtu=		1450,		/* default that we will ask for */
+	Minmtu=		128,		/* minimum that we will accept */
+	Maxmtu=		2000,		/* maximum that we will accept */
+};
+
+
+struct Pstate
+{
+	int	proto;		/* protocol type */
+	int	timeout;		/* for current state */
+	int	rxtimeout;	/* for current retransmit */
+	ulong	flags;		/* options received */
+	uchar	id;		/* id of current message */
+	uchar	confid;		/* id of current config message */
+	uchar	termid;		/* id of current termination message */
+	uchar	rcvdconfid;	/* id of last conf message received */
+	uchar	state;		/* PPP link state */
+	ulong	optmask;		/* which options to request */
+	int	echoack;	/* recieved echo ack */
+	int	echotimeout;	/* echo timeout */
+};
+
+struct Qualstats
+{
+	ulong	reports;
+	ulong	packets;
+	ulong	bytes;
+	ulong	discards;
+	ulong	errors;
+};
+
+struct PPP
+{
+	QLock;
+
+	Chan*	dchan;			/* serial line */
+	Chan*	cchan;			/* serial line control */
+	int		framing;	/* non-zero to use framing characters */
+	Ipaddr	local;
+	int		localfrozen;
+	Ipaddr	remote;
+	int		remotefrozen;
+
+	int	pppup;
+	Fs	*f;		/* file system we belong to */
+	Ipifc*	ifc;
+	Proc*	readp;			/* reading process */
+	Proc*	timep;			/* timer process */
+	Block*	inbuf;			/* input buffer */
+	Block*	outbuf;			/* output buffer */
+	QLock	outlock;		/*  and its lock */
+
+	ulong	magic;			/* magic number to detect loop backs */
+	ulong	rctlmap;		/* map of chars to ignore in rcvr */
+	ulong	xctlmap;		/* map of chars to excape in xmit */
+	int		phase;		/* PPP phase */
+	Pstate*	lcp;			/* lcp state */
+	Pstate*	ipcp;			/* ipcp state */
+	char	secret[256];		/* md5 key */
+	char	chapname[256];		/* chap system name */
+	Tcpc*	ctcp;
+	ulong		mtu;		/* maximum xmit size */
+	ulong		mru;		/* maximum recv size */
+
+	int	baud;
+	int	usepap;	/* authentication is PAP in every sense, not CHAP */
+	int	papid;
+	int	usechap;
+
+	/* rfc */
+	int	usedns;
+	Ipaddr	dns1;
+	Ipaddr	dns2;
+
+	/* link quality monitoring */
+	int		period;		/* lqm period */
+	int		timeout;	/* time to next lqm packet */
+	Qualstats	in;		/* local */
+	Qualstats	out;
+	Qualstats	pin;		/* peer */
+	Qualstats	pout;
+	Qualstats	sin;		/* saved */
+};
+
+PPP*		pppopen(PPP*, char*, Ipaddr, Ipaddr, int, int, char*, char*);
+Block*	pppread(PPP*);
+int		pppwrite(PPP*, Block*);
+void		pppclose(PPP*);
+
+struct Lcpmsg
+{
+	uchar	code;
+	uchar	id;
+	uchar	len[2];
+	uchar	data[1];
+};
+
+struct Lcpopt
+{
+	uchar	type;
+	uchar	len;
+	uchar	data[1];
+};
+
+struct Qualpkt
+{
+	uchar	magic[4];
+
+	uchar	lastoutreports[4];
+	uchar	lastoutpackets[4];
+	uchar	lastoutbytes[4];
+	uchar	peerinreports[4];
+	uchar	peerinpackets[4];
+	uchar	peerindiscards[4];
+	uchar	peerinerrors[4];
+	uchar	peerinbytes[4];
+	uchar	peeroutreports[4];
+	uchar	peeroutpackets[4];
+	uchar	peeroutbytes[4];
+};
+
+ushort	compress(Tcpc*, Block*, Fs*);
+Tcpc*	compress_init(Tcpc*);
+int		compress_negotiate(Tcpc*, uchar*);
+ushort	tcpcompress(Tcpc*, Block*, Fs*);
+Block*	tcpuncompress(Tcpc*, Block*, ushort, Fs*);
--- /dev/null
+++ b/os/ip/pppmedium.c
@@ -1,0 +1,192 @@
+#include "u.h"
+#include "../port/lib.h"
+#include "mem.h"
+#include "dat.h"
+#include "fns.h"
+#include "../port/error.h"
+
+#include "ip.h"
+#include "kernel.h"
+#include "ppp.h"
+
+static void	pppreader(void *a);
+static void	pppbind(Ipifc *ifc, int argc, char **argv);
+static void	pppunbind(Ipifc *ifc);
+static void	pppbwrite(Ipifc *ifc, Block *bp, int version, uchar *ip);
+static void	deadremote(Ipifc *ifc);
+
+Medium pppmedium =
+{
+.name=	"ppp",
+.hsize=	4,
+.mintu=	Minmtu,
+.maxtu=	Maxmtu,
+.maclen=	0,
+.bind=	pppbind,
+.unbind=	pppunbind,
+.bwrite=	pppbwrite,
+.unbindonclose=	0,		/* don't unbind on last close */
+};
+
+/*
+ *  called to bind an IP ifc to an ethernet device
+ *  called with ifc wlock'd
+ */
+static void
+pppbind(Ipifc *ifc, int argc, char **argv)
+{
+	PPP *ppp;
+	Ipaddr ipaddr, remip;
+	int mtu, framing;
+	char *chapname, *secret;
+
+	if(argc < 3)
+		error(Ebadarg);
+
+	ipmove(ipaddr, IPnoaddr);
+	ipmove(remip, IPnoaddr);
+	mtu = Defmtu;
+	framing = 1;
+	chapname = nil;
+	secret = nil;
+
+	switch(argc){
+	default:
+	case 9:
+		if(argv[8][0] != '-')
+			secret = argv[8];
+	case 8:
+		if(argv[7][0] != '-')
+			chapname = argv[7];
+	case 7:
+		if(argv[6][0] != '-')
+			framing = strtoul(argv[6], 0, 0);
+	case 6:
+		if(argv[5][0] != '-')
+			mtu = strtoul(argv[5], 0, 0);
+	case 5:
+		if(argv[4][0] != '-')
+			parseip(remip, argv[4]);
+	case 4:
+		if(argv[3][0] != '-')
+			parseip(ipaddr, argv[3]);
+	case 3:
+		break;
+	}
+
+	ppp = smalloc(sizeof(*ppp));
+	ppp->ifc = ifc;
+	ppp->f = ifc->conv->p->f;
+	ifc->arg = ppp;
+	if(waserror()){
+		pppunbind(ifc);
+		nexterror();
+	}
+	if(pppopen(ppp, argv[2], ipaddr, remip, mtu, framing, chapname, secret) == nil)
+		error("ppp open failed");
+	poperror();
+	kproc("pppreader", pppreader, ifc, KPDUPPG|KPDUPFDG);
+}
+
+static void
+pppreader(void *a)
+{
+	Ipifc *ifc;
+	Block *bp;
+	PPP *ppp;
+
+	ifc = a;
+	ppp = ifc->arg;
+	ppp->readp = up;	/* hide identity under a rock for unbind */
+	setpri(PriHi);
+
+	if(waserror()){
+		netlog(ppp->f, Logppp, "pppreader: %I: %s\n", ppp->local, up->env->errstr);
+		ppp->readp = 0;
+		deadremote(ifc);
+		pexit("hangup", 1);
+	}
+
+	for(;;){
+		bp = pppread(ppp);
+		if(bp == nil)
+			error("hungup");
+		if(!canrlock(ifc)){
+			freeb(bp);
+			continue;
+		}
+		if(waserror()){
+			runlock(ifc);
+			nexterror();
+		}
+		ifc->in++;
+		if(ifc->lifc == nil)
+			freeb(bp);
+		else
+			ipiput(ppp->f, ifc, bp);
+		runlock(ifc);
+		poperror();
+	}
+}
+
+/*
+ *  called with ifc wlock'd
+ */
+static void
+pppunbind(Ipifc *ifc)
+{
+	PPP *ppp = ifc->arg;
+
+	if(ppp == nil)
+		return;
+	if(ppp->readp)
+		postnote(ppp->readp, 1, "unbind", 0);
+	if(ppp->timep)
+		postnote(ppp->timep, 1, "unbind", 0);
+
+	/* wait for kprocs to die */
+	while(ppp->readp != 0 || ppp->timep != 0)
+		tsleep(&up->sleep, return0, 0, 300);
+
+	pppclose(ppp);
+	qclose(ifc->conv->eq);
+	ifc->arg = nil;
+}
+
+/*
+ *  called by ipoput with a single packet to write with ifc rlock'd
+ */
+static void
+pppbwrite(Ipifc *ifc, Block *bp, int, uchar*)
+{
+	PPP *ppp = ifc->arg;
+
+	pppwrite(ppp, bp);
+	ifc->out++;
+}
+
+/*
+ *	If the other end hangs up, we have to unbind the interface.  An extra
+ *	unbind (in the case where we are hanging up) won't do any harm.
+ */
+static void
+deadremote(Ipifc *ifc)
+{
+	int fd;
+	char path[128];
+	PPP *ppp;
+
+	ppp = ifc->arg;
+	snprint(path, sizeof path, "#I%d/ipifc/%d/ctl", ppp->f->dev, ifc->conv->x);
+	fd = kopen(path, ORDWR);
+	if(fd < 0)
+		return;
+	kwrite(fd, "unbind", sizeof("unbind")-1);
+	kclose(fd);
+}
+
+void
+pppmediumlink(void)
+{
+	addipmedium(&pppmedium);
+}
--- /dev/null
+++ b/os/ip/ptclbsum.c
@@ -1,0 +1,72 @@
+#include	"u.h"
+#include	"../port/lib.h"
+#include	"mem.h"
+#include	"dat.h"
+#include	"fns.h"
+#include	"../port/error.h"
+#include	"ip.h"
+
+static	short	endian	= 1;
+static	uchar*	aendian	= (uchar*)&endian;
+#define	LITTLE	*aendian
+
+ushort
+ptclbsum(uchar *addr, int len)
+{
+	ulong losum, hisum, mdsum, x;
+	ulong t1, t2;
+
+	losum = 0;
+	hisum = 0;
+	mdsum = 0;
+
+	x = 0;
+	if((ulong)addr & 1) {
+		if(len) {
+			hisum += addr[0];
+			len--;
+			addr++;
+		}
+		x = 1;
+	}
+	while(len >= 16) {
+		t1 = *(ushort*)(addr+0);
+		t2 = *(ushort*)(addr+2);	mdsum += t1;
+		t1 = *(ushort*)(addr+4);	mdsum += t2;
+		t2 = *(ushort*)(addr+6);	mdsum += t1;
+		t1 = *(ushort*)(addr+8);	mdsum += t2;
+		t2 = *(ushort*)(addr+10);	mdsum += t1;
+		t1 = *(ushort*)(addr+12);	mdsum += t2;
+		t2 = *(ushort*)(addr+14);	mdsum += t1;
+		mdsum += t2;
+		len -= 16;
+		addr += 16;
+	}
+	while(len >= 2) {
+		mdsum += *(ushort*)addr;
+		len -= 2;
+		addr += 2;
+	}
+	if(x) {
+		if(len)
+			losum += addr[0];
+		if(LITTLE)
+			losum += mdsum;
+		else
+			hisum += mdsum;
+	} else {
+		if(len)
+			hisum += addr[0];
+		if(LITTLE)
+			hisum += mdsum;
+		else
+			losum += mdsum;
+	}
+
+	losum += hisum >> 8;
+	losum += (hisum & 0xff) << 8;
+	while(hisum = losum>>16)
+		losum = hisum + (losum & 0xffff);
+
+	return losum & 0xffff;
+}
--- /dev/null
+++ b/os/ip/rudp.c
@@ -1,0 +1,1085 @@
+/*
+ *  This protocol is compatible with UDP's packet format.
+ *  It could be done over UDP if need be.
+ */
+#include	"u.h"
+#include	"../port/lib.h"
+#include	"mem.h"
+#include	"dat.h"
+#include	"fns.h"
+#include	"../port/error.h"
+
+#include	"ip.h"
+
+#define DEBUG	0
+#define DPRINT if(DEBUG)print
+
+#define SEQDIFF(a,b) ( (a)>=(b)?\
+			(a)-(b):\
+			0xffffffffUL-((b)-(a)) )
+#define INSEQ(a,start,end) ( (start)<=(end)?\
+				((a)>(start)&&(a)<=(end)):\
+				((a)>(start)||(a)<=(end)) )
+#define UNACKED(r) SEQDIFF(r->sndseq, r->ackrcvd)
+#define NEXTSEQ(a) ( (a)+1 == 0 ? 1 : (a)+1 )
+
+enum
+{
+	UDP_HDRSIZE	= 20,	/* pseudo header + udp header */
+	UDP_PHDRSIZE	= 12,	/* pseudo header */
+	UDP_RHDRSIZE	= 36,	/* pseudo header + udp header + rudp header */
+	UDP_IPHDR	= 8,	/* ip header */
+	IP_UDPPROTO	= 254,
+	UDP_USEAD7	= 52,
+	UDP_USEAD6	= 36,
+	UDP_USEAD4	= 12,
+
+	Rudprxms	= 200,
+	Rudptickms	= 50,
+	Rudpmaxxmit	= 10,
+	Maxunacked	= 100,
+
+};
+
+#define Hangupgen	0xffffffff	/* used only in hangup messages */
+
+typedef struct Udphdr Udphdr;
+struct Udphdr
+{
+	/* ip header */
+	uchar	vihl;		/* Version and header length */
+	uchar	tos;		/* Type of service */
+	uchar	length[2];	/* packet length */
+	uchar	id[2];		/* Identification */
+	uchar	frag[2];	/* Fragment information */
+
+	/* pseudo header starts here */
+	uchar	Unused;
+	uchar	udpproto;	/* Protocol */
+	uchar	udpplen[2];	/* Header plus data length */
+	uchar	udpsrc[4];	/* Ip source */
+	uchar	udpdst[4];	/* Ip destination */
+
+	/* udp header */
+	uchar	udpsport[2];	/* Source port */
+	uchar	udpdport[2];	/* Destination port */
+	uchar	udplen[2];	/* data length */
+	uchar	udpcksum[2];	/* Checksum */
+};
+
+typedef struct Rudphdr Rudphdr;
+struct Rudphdr
+{
+	/* ip header */
+	uchar	vihl;		/* Version and header length */
+	uchar	tos;		/* Type of service */
+	uchar	length[2];	/* packet length */
+	uchar	id[2];		/* Identification */
+	uchar	frag[2];	/* Fragment information */
+
+	/* pseudo header starts here */
+	uchar	Unused;
+	uchar	udpproto;	/* Protocol */
+	uchar	udpplen[2];	/* Header plus data length */
+	uchar	udpsrc[4];	/* Ip source */
+	uchar	udpdst[4];	/* Ip destination */
+
+	/* udp header */
+	uchar	udpsport[2];	/* Source port */
+	uchar	udpdport[2];	/* Destination port */
+	uchar	udplen[2];	/* data length (includes rudp header) */
+	uchar	udpcksum[2];	/* Checksum */
+
+	/* rudp header */
+	uchar	relseq[4];	/* id of this packet (or 0) */
+	uchar	relsgen[4];	/* generation/time stamp */
+	uchar	relack[4];	/* packet being acked (or 0) */
+	uchar	relagen[4];	/* generation/time stamp */
+};
+
+
+/*
+ *  one state structure per destination
+ */
+typedef struct Reliable Reliable;
+struct Reliable
+{
+	Ref;
+
+	Reliable *next;
+
+	uchar	addr[IPaddrlen];	/* always V6 when put here */
+	ushort	port;
+
+	Block	*unacked;	/* unacked msg list */
+	Block	*unackedtail;	/*  and its tail */
+
+	int	timeout;	/* time since first unacked msg sent */
+	int	xmits;		/* number of times first unacked msg sent */
+
+	ulong	sndseq;		/* next packet to be sent */
+	ulong	sndgen;		/*  and its generation */
+
+	ulong	rcvseq;		/* last packet received */
+	ulong	rcvgen;		/*  and its generation */
+
+	ulong	acksent;	/* last ack sent */
+	ulong	ackrcvd;	/* last msg for which ack was rcvd */
+
+	/* flow control */
+	QLock	lock;
+	Rendez	vous;
+	int	blocked;
+};
+
+
+
+/* MIB II counters */
+typedef struct Rudpstats Rudpstats;
+struct Rudpstats
+{
+	ulong	rudpInDatagrams;
+	ulong	rudpNoPorts;
+	ulong	rudpInErrors;
+	ulong	rudpOutDatagrams;
+};
+
+typedef struct Rudppriv Rudppriv;
+struct Rudppriv
+{
+	Ipht	ht;
+
+	/* MIB counters */
+	Rudpstats	ustats;
+
+	/* non-MIB stats */
+	ulong	csumerr;		/* checksum errors */
+	ulong	lenerr;			/* short packet */
+	ulong	rxmits;			/* # of retransmissions */
+	ulong	orders;			/* # of out of order pkts */
+
+	/* keeping track of the ack kproc */
+	int	ackprocstarted;
+	QLock	apl;
+};
+
+
+static ulong generation = 0;
+static Rendez rend;
+
+/*
+ *  protocol specific part of Conv
+ */
+typedef struct Rudpcb Rudpcb;
+struct Rudpcb
+{
+	QLock;
+	uchar	headers;
+	uchar	randdrop;
+	Reliable *r;
+};
+
+/*
+ * local functions 
+ */
+void	relsendack(Conv*, Reliable*, int);
+int	reliput(Conv*, Block*, uchar*, ushort);
+Reliable *relstate(Rudpcb*, uchar*, ushort, char*);
+void	relput(Reliable*);
+void	relforget(Conv *, uchar*, int, int);
+void	relackproc(void *);
+void	relackq(Reliable *, Block*);
+void	relhangup(Conv *, Reliable*);
+void	relrexmit(Conv *, Reliable*);
+void	relput(Reliable*);
+void	rudpkick(void *x);
+
+static void
+rudpstartackproc(Proto *rudp)
+{
+	Rudppriv *rpriv;
+	char kpname[KNAMELEN];
+
+	rpriv = rudp->priv;
+	if(rpriv->ackprocstarted == 0){
+		qlock(&rpriv->apl);
+		if(rpriv->ackprocstarted == 0){
+			sprint(kpname, "#I%drudpack", rudp->f->dev);
+			kproc(kpname, relackproc, rudp, 0);
+			rpriv->ackprocstarted = 1;
+		}
+		qunlock(&rpriv->apl);
+	}
+}
+
+static char*
+rudpconnect(Conv *c, char **argv, int argc)
+{
+	char *e;
+	Rudppriv *upriv;
+
+	upriv = c->p->priv;
+	rudpstartackproc(c->p);
+	e = Fsstdconnect(c, argv, argc);
+	Fsconnected(c, e);
+	iphtadd(&upriv->ht, c);
+
+	return e;
+}
+
+
+static int
+rudpstate(Conv *c, char *state, int n)
+{
+	Rudpcb *ucb;
+	Reliable *r;
+	int m;
+
+	m = snprint(state, n, "%s", c->inuse?"Open":"Closed");
+	ucb = (Rudpcb*)c->ptcl;
+	qlock(ucb);
+	for(r = ucb->r; r; r = r->next)
+		m += snprint(state+m, n-m, " %I/%ld", r->addr, UNACKED(r));
+	qunlock(ucb);
+	return m;
+}
+
+static char*
+rudpannounce(Conv *c, char** argv, int argc)
+{
+	char *e;
+	Rudppriv *upriv;
+
+	upriv = c->p->priv;
+	rudpstartackproc(c->p);
+	e = Fsstdannounce(c, argv, argc);
+	if(e != nil)
+		return e;
+	Fsconnected(c, nil);
+	iphtadd(&upriv->ht, c);
+
+	return nil;
+}
+
+static void
+rudpcreate(Conv *c)
+{
+	c->rq = qopen(64*1024, Qmsg, 0, 0);
+	c->wq = qopen(64*1024, Qkick, rudpkick, c);
+}
+
+static void
+rudpclose(Conv *c)
+{
+	Rudpcb *ucb;
+	Reliable *r, *nr;
+	Rudppriv *upriv;
+
+	upriv = c->p->priv;
+	iphtrem(&upriv->ht, c);
+
+	/* force out any delayed acks */
+	ucb = (Rudpcb*)c->ptcl;
+	qlock(ucb);
+	for(r = ucb->r; r; r = r->next){
+		if(r->acksent != r->rcvseq)
+			relsendack(c, r, 0);
+	}
+	qunlock(ucb);
+
+	qclose(c->rq);
+	qclose(c->wq);
+	qclose(c->eq);
+	ipmove(c->laddr, IPnoaddr);
+	ipmove(c->raddr, IPnoaddr);
+	c->lport = 0;
+	c->rport = 0;
+
+	ucb->headers = 0;
+	ucb->randdrop = 0;
+	qlock(ucb);
+	for(r = ucb->r; r; r = nr){
+		if(r->acksent != r->rcvseq)
+			relsendack(c, r, 0);
+		nr = r->next;
+		relhangup(c, r);
+		relput(r);
+	}
+	ucb->r = 0;
+
+	qunlock(ucb);
+}
+
+/*
+ *  randomly don't send packets
+ */
+static void
+doipoput(Conv *c, Fs *f, Block *bp, int x, int ttl, int tos)
+{
+	Rudpcb *ucb;
+
+	ucb = (Rudpcb*)c->ptcl;
+	if(ucb->randdrop && nrand(100) < ucb->randdrop)
+		freeblist(bp);
+	else
+		ipoput4(f, bp, x, ttl, tos, nil);
+}
+
+int
+flow(void *v)
+{
+	Reliable *r = v;
+
+	return UNACKED(r) <= Maxunacked;
+}
+
+void
+rudpkick(void *x)
+{
+	Conv *c = x;
+	Udphdr *uh;
+	ushort rport;
+	uchar laddr[IPaddrlen], raddr[IPaddrlen];
+	Block *bp;
+	Rudpcb *ucb;
+	Rudphdr *rh;
+	Reliable *r;
+	int dlen, ptcllen;
+	Rudppriv *upriv;
+	Fs *f;
+
+	upriv = c->p->priv;
+	f = c->p->f;
+
+	netlog(c->p->f, Logrudp, "rudp: kick\n");
+	bp = qget(c->wq);
+	if(bp == nil)
+		return;
+
+	ucb = (Rudpcb*)c->ptcl;
+	switch(ucb->headers) {
+	case 7:
+		/* get user specified addresses */
+		bp = pullupblock(bp, UDP_USEAD7);
+		if(bp == nil)
+			return;
+		ipmove(raddr, bp->rp);
+		bp->rp += IPaddrlen;
+		ipmove(laddr, bp->rp);
+		bp->rp += IPaddrlen;
+		/* pick interface closest to dest */
+		if(ipforme(f, laddr) != Runi)
+			findlocalip(f, laddr, raddr);
+		bp->rp += IPaddrlen;		/* Ignore ifc address */
+		rport = nhgets(bp->rp);
+		bp->rp += 2+2;			/* Ignore local port */
+		break;
+	case 6:
+		/* get user specified addresses */
+		bp = pullupblock(bp, UDP_USEAD6);
+		if(bp == nil)
+			return;
+		ipmove(raddr, bp->rp);
+		bp->rp += IPaddrlen;
+		ipmove(laddr, bp->rp);
+		bp->rp += IPaddrlen;
+		/* pick interface closest to dest */
+		if(ipforme(f, laddr) != Runi)
+			findlocalip(f, laddr, raddr);
+		rport = nhgets(bp->rp);
+
+		bp->rp += 4;			/* Igonore local port */
+		break;
+	default:
+		ipmove(raddr, c->raddr);
+		ipmove(laddr, c->laddr);
+		rport = c->rport;
+
+		break;
+	}
+
+	dlen = blocklen(bp);
+
+	/* Make space to fit rudp & ip header */
+	bp = padblock(bp, UDP_IPHDR+UDP_RHDRSIZE);
+	if(bp == nil)
+		return;
+
+	uh = (Udphdr *)(bp->rp);
+	uh->vihl = IP_VER4;
+
+	rh = (Rudphdr*)uh;
+
+	ptcllen = dlen + (UDP_RHDRSIZE-UDP_PHDRSIZE);
+	uh->Unused = 0;
+	uh->udpproto = IP_UDPPROTO;
+	uh->frag[0] = 0;
+	uh->frag[1] = 0;
+	hnputs(uh->udpplen, ptcllen);
+	switch(ucb->headers){
+	case 6:
+	case 7:
+		v6tov4(uh->udpdst, raddr);
+		hnputs(uh->udpdport, rport);
+		v6tov4(uh->udpsrc, laddr);
+		break;
+	default:
+		v6tov4(uh->udpdst, c->raddr);
+		hnputs(uh->udpdport, c->rport);
+		if(ipcmp(c->laddr, IPnoaddr) == 0)
+			findlocalip(f, c->laddr, c->raddr);
+		v6tov4(uh->udpsrc, c->laddr);
+		break;
+	}
+	hnputs(uh->udpsport, c->lport);
+	hnputs(uh->udplen, ptcllen);
+	uh->udpcksum[0] = 0;
+	uh->udpcksum[1] = 0;
+
+	qlock(ucb);
+	r = relstate(ucb, raddr, rport, "kick");
+	r->sndseq = NEXTSEQ(r->sndseq);
+	hnputl(rh->relseq, r->sndseq);
+	hnputl(rh->relsgen, r->sndgen);
+
+	hnputl(rh->relack, r->rcvseq);  /* ACK last rcvd packet */
+	hnputl(rh->relagen, r->rcvgen);
+
+	if(r->rcvseq != r->acksent)
+		r->acksent = r->rcvseq;
+
+	hnputs(uh->udpcksum, ptclcsum(bp, UDP_IPHDR, dlen+UDP_RHDRSIZE));
+
+	relackq(r, bp);
+	qunlock(ucb);
+
+	upriv->ustats.rudpOutDatagrams++;
+
+	DPRINT("sent: %lud/%lud, %lud/%lud\n", 
+		r->sndseq, r->sndgen, r->rcvseq, r->rcvgen);
+
+	doipoput(c, f, bp, 0, c->ttl, c->tos);
+
+	if(waserror()) {
+		relput(r);
+		qunlock(&r->lock);
+		nexterror();
+	}
+
+	/* flow control of sorts */
+	qlock(&r->lock);
+	if(UNACKED(r) > Maxunacked){
+		r->blocked = 1;
+		sleep(&r->vous, flow, r);
+		r->blocked = 0;
+	}
+
+	qunlock(&r->lock);
+	relput(r);
+	poperror();
+}
+
+void
+rudpiput(Proto *rudp, Ipifc *ifc, Block *bp)
+{
+	int len, olen, ottl;
+	Udphdr *uh;
+	Conv *c;
+	Rudpcb *ucb;
+	uchar raddr[IPaddrlen], laddr[IPaddrlen];
+	ushort rport, lport;
+	Rudppriv *upriv;
+	Fs *f;
+	uchar *p;
+
+	upriv = rudp->priv;
+	f = rudp->f;
+
+	upriv->ustats.rudpInDatagrams++;
+
+	uh = (Udphdr*)(bp->rp);
+
+	/* Put back pseudo header for checksum 
+	 * (remember old values for icmpnoconv()) 
+	 */
+	ottl = uh->Unused;
+	uh->Unused = 0;
+	len = nhgets(uh->udplen);
+	olen = nhgets(uh->udpplen);
+	hnputs(uh->udpplen, len);
+
+	v4tov6(raddr, uh->udpsrc);
+	v4tov6(laddr, uh->udpdst);
+	lport = nhgets(uh->udpdport);
+	rport = nhgets(uh->udpsport);
+
+	if(nhgets(uh->udpcksum)) {
+		if(ptclcsum(bp, UDP_IPHDR, len+UDP_PHDRSIZE)) {
+			upriv->ustats.rudpInErrors++;
+			upriv->csumerr++;
+			netlog(f, Logrudp, "rudp: checksum error %I\n", raddr);
+			DPRINT("rudp: checksum error %I\n", raddr);
+			freeblist(bp);
+			return;
+		}
+	}
+
+	qlock(rudp);
+
+	c = iphtlook(&upriv->ht, raddr, rport, laddr, lport);
+	if(c == nil){
+		/* no converstation found */
+		upriv->ustats.rudpNoPorts++;
+		qunlock(rudp);
+		netlog(f, Logudp, "udp: no conv %I!%d -> %I!%d\n", raddr, rport,
+			laddr, lport);
+		uh->Unused = ottl;
+		hnputs(uh->udpplen, olen);
+		icmpnoconv(f, bp);
+		freeblist(bp);
+		return;
+	}
+	ucb = (Rudpcb*)c->ptcl;
+	qlock(ucb);
+	qunlock(rudp);
+
+	if(reliput(c, bp, raddr, rport) < 0){
+		qunlock(ucb);
+		freeb(bp);
+		return;
+	}
+
+	/*
+	 * Trim the packet down to data size
+	 */
+
+	len -= (UDP_RHDRSIZE-UDP_PHDRSIZE);
+	bp = trimblock(bp, UDP_IPHDR+UDP_RHDRSIZE, len);
+	if(bp == nil) {
+		netlog(f, Logrudp, "rudp: len err %I.%d -> %I.%d\n", 
+			raddr, rport, laddr, lport);
+		DPRINT("rudp: len err %I.%d -> %I.%d\n", 
+			raddr, rport, laddr, lport);
+		upriv->lenerr++;
+		return;
+	}
+
+	netlog(f, Logrudpmsg, "rudp: %I.%d -> %I.%d l %d\n", 
+		raddr, rport, laddr, lport, len);
+
+	switch(ucb->headers){
+	case 7:
+		/* pass the src address */
+		bp = padblock(bp, UDP_USEAD7);
+		p = bp->rp;
+		ipmove(p, raddr); p += IPaddrlen;
+		ipmove(p, laddr); p += IPaddrlen;
+		ipmove(p, ifc->lifc->local); p += IPaddrlen;
+		hnputs(p, rport); p += 2;
+		hnputs(p, lport);
+		break;
+	case 6:
+		/* pass the src address */
+		bp = padblock(bp, UDP_USEAD6);
+		p = bp->rp;
+		ipmove(p, raddr); p += IPaddrlen;
+		ipmove(p, ipforme(f, laddr)==Runi ? laddr : ifc->lifc->local); p += IPaddrlen;
+		hnputs(p, rport); p += 2;
+		hnputs(p, lport);
+		break;
+	default:
+		/* connection oriented rudp */
+		if(ipcmp(c->raddr, IPnoaddr) == 0){
+			/* save the src address in the conversation */
+		 	ipmove(c->raddr, raddr);
+			c->rport = rport;
+
+			/* reply with the same ip address (if not broadcast) */
+			if(ipforme(f, laddr) == Runi)
+				ipmove(c->laddr, laddr);
+			else
+				v4tov6(c->laddr, ifc->lifc->local);
+		}
+		break;
+	}
+	if(bp->next)
+		bp = concatblock(bp);
+
+	if(qfull(c->rq)) {
+		netlog(f, Logrudp, "rudp: qfull %I.%d -> %I.%d\n", raddr, rport,
+			laddr, lport);
+		freeblist(bp);
+	}
+	else
+		qpass(c->rq, bp);
+	
+	qunlock(ucb);
+}
+
+static char *rudpunknown = "unknown rudp ctl request";
+
+char*
+rudpctl(Conv *c, char **f, int n)
+{
+	Rudpcb *ucb;
+	uchar ip[IPaddrlen];
+	int x;
+
+	ucb = (Rudpcb*)c->ptcl;
+	if(n < 1)
+		return rudpunknown;
+
+	if(strcmp(f[0], "headers++4") == 0){
+		ucb->headers = 7;
+		return nil;
+	} else if(strcmp(f[0], "headers") == 0){
+		ucb->headers = 6;
+		return nil;
+	} else if(strcmp(f[0], "hangup") == 0){
+		if(n < 3)
+			return "bad syntax";
+		parseip(ip, f[1]);
+		x = atoi(f[2]);
+		qlock(ucb);
+		relforget(c, ip, x, 1);
+		qunlock(ucb);
+		return nil;
+	} else if(strcmp(f[0], "randdrop") == 0){
+		x = 10;		/* default is 10% */
+		if(n > 1)
+			x = atoi(f[1]);
+		if(x > 100 || x < 0)
+			return "illegal rudp drop rate";
+		ucb->randdrop = x;
+		return nil;
+	}
+	return rudpunknown;
+}
+
+void
+rudpadvise(Proto *rudp, Block *bp, char *msg)
+{
+	Udphdr *h;
+	uchar source[IPaddrlen], dest[IPaddrlen];
+	ushort psource, pdest;
+	Conv *s, **p;
+
+	h = (Udphdr*)(bp->rp);
+
+	v4tov6(dest, h->udpdst);
+	v4tov6(source, h->udpsrc);
+	psource = nhgets(h->udpsport);
+	pdest = nhgets(h->udpdport);
+
+	/* Look for a connection */
+	for(p = rudp->conv; *p; p++) {
+		s = *p;
+		if(s->rport == pdest)
+		if(s->lport == psource)
+		if(ipcmp(s->raddr, dest) == 0)
+		if(ipcmp(s->laddr, source) == 0){
+			qhangup(s->rq, msg);
+			qhangup(s->wq, msg);
+			break;
+		}
+	}
+	freeblist(bp);
+}
+
+int
+rudpstats(Proto *rudp, char *buf, int len)
+{
+	Rudppriv *upriv;
+
+	upriv = rudp->priv;
+	return snprint(buf, len, "%lud %lud %lud %lud %lud %lud\n",
+		upriv->ustats.rudpInDatagrams,
+		upriv->ustats.rudpNoPorts,
+		upriv->ustats.rudpInErrors,
+		upriv->ustats.rudpOutDatagrams,
+		upriv->rxmits,
+		upriv->orders);
+}
+
+void
+rudpinit(Fs *fs)
+{
+
+	Proto *rudp;
+
+	rudp = smalloc(sizeof(Proto));
+	rudp->priv = smalloc(sizeof(Rudppriv));
+	rudp->name = "rudp";
+	rudp->connect = rudpconnect;
+	rudp->announce = rudpannounce;
+	rudp->ctl = rudpctl;
+	rudp->state = rudpstate;
+	rudp->create = rudpcreate;
+	rudp->close = rudpclose;
+	rudp->rcv = rudpiput;
+	rudp->advise = rudpadvise;
+	rudp->stats = rudpstats;
+	rudp->ipproto = IP_UDPPROTO;
+	rudp->nc = 16;
+	rudp->ptclsize = sizeof(Rudpcb);
+
+	Fsproto(fs, rudp);
+}
+
+/*********************************************/
+/* Here starts the reliable helper functions */
+/*********************************************/
+/*
+ *  Enqueue a copy of an unacked block for possible retransmissions
+ */
+void
+relackq(Reliable *r, Block *bp)
+{
+	Block *np;
+
+	np = copyblock(bp, blocklen(bp));
+	if(r->unacked)
+		r->unackedtail->list = np;
+	else {
+		/* restart timer */
+		r->timeout = 0;
+		r->xmits = 1;
+		r->unacked = np;
+	}
+	r->unackedtail = np;
+	np->list = nil;
+}
+
+/*
+ *  retransmit unacked blocks
+ */
+void
+relackproc(void *a)
+{
+	Rudpcb *ucb;
+	Proto *rudp;
+	Reliable *r;
+	Conv **s, *c;
+
+	rudp = (Proto *)a;
+
+loop:
+	tsleep(&up->sleep, return0, 0, Rudptickms);
+
+	for(s = rudp->conv; *s; s++) {
+		c = *s;
+		ucb = (Rudpcb*)c->ptcl;
+		qlock(ucb);
+
+		for(r = ucb->r; r; r = r->next) {
+			if(r->unacked != nil){
+				r->timeout += Rudptickms;
+				if(r->timeout > Rudprxms*r->xmits)
+					relrexmit(c, r);
+			}
+			if(r->acksent != r->rcvseq)
+				relsendack(c, r, 0);
+		}
+		qunlock(ucb);
+	}
+	goto loop;
+}
+
+/*
+ *  get the state record for a conversation
+ */
+Reliable*
+relstate(Rudpcb *ucb, uchar *addr, ushort port, char *from)
+{
+	Reliable *r, **l;
+
+	l = &ucb->r;
+	for(r = *l; r; r = *l){
+		if(memcmp(addr, r->addr, IPaddrlen) == 0 && 
+		    port == r->port)
+			break;
+		l = &r->next;
+	}
+
+	/* no state for this addr/port, create some */
+	if(r == nil){
+		while(generation == 0)
+			generation = rand();
+
+		DPRINT("from %s new state %lud for %I!%ud\n", 
+		        from, generation, addr, port);
+
+		r = smalloc(sizeof(Reliable));
+		memmove(r->addr, addr, IPaddrlen);
+		r->port = port;
+		r->unacked = 0;
+		if(generation == Hangupgen)
+			generation++;
+		r->sndgen = generation++;
+		r->sndseq = 0;
+		r->ackrcvd = 0;
+		r->rcvgen = 0;
+		r->rcvseq = 0;
+		r->acksent = 0;
+		r->xmits = 0;
+		r->timeout = 0;
+		r->ref = 0;
+		incref(r);	/* one reference for being in the list */
+
+		*l = r;
+	}
+
+	incref(r);
+	return r;
+}
+
+void
+relput(Reliable *r)
+{
+	if(decref(r) == 0)
+		free(r);
+}
+
+/*
+ *  forget a Reliable state
+ */
+void
+relforget(Conv *c, uchar *ip, int port, int originator)
+{
+	Rudpcb *ucb;
+	Reliable *r, **l;
+
+	ucb = (Rudpcb*)c->ptcl;
+
+	l = &ucb->r;
+	for(r = *l; r; r = *l){
+		if(ipcmp(ip, r->addr) == 0 && port == r->port){
+			*l = r->next;
+			if(originator)
+				relsendack(c, r, 1);
+			relhangup(c, r);
+			relput(r);	/* remove from the list */
+			break;
+		}
+		l = &r->next;
+	}
+}
+
+/* 
+ *  process a rcvd reliable packet. return -1 if not to be passed to user process,
+ *  0 therwise.
+ *
+ *  called with ucb locked.
+ */
+int
+reliput(Conv *c, Block *bp, uchar *addr, ushort port)
+{
+	Block *nbp;
+	Rudpcb *ucb;
+	Rudppriv *upriv;
+	Udphdr *uh;
+	Reliable *r;
+	Rudphdr *rh;
+	ulong seq, ack, sgen, agen, ackreal;
+	int rv = -1;
+
+	/* get fields */
+	uh = (Udphdr*)(bp->rp);
+	rh = (Rudphdr*)uh;
+	seq = nhgetl(rh->relseq);
+	sgen = nhgetl(rh->relsgen);
+	ack = nhgetl(rh->relack);
+	agen = nhgetl(rh->relagen);
+
+	upriv = c->p->priv;
+	ucb = (Rudpcb*)c->ptcl;
+	r = relstate(ucb, addr, port, "input");
+
+	DPRINT("rcvd %lud/%lud, %lud/%lud, r->sndgen = %lud\n", 
+		seq, sgen, ack, agen, r->sndgen);
+
+	/* if acking an incorrect generation, ignore */
+	if(ack && agen != r->sndgen)
+		goto out;
+
+	/* Look for a hangup */
+	if(sgen == Hangupgen) {
+		if(agen == r->sndgen)
+			relforget(c, addr, port, 0);
+		goto out;
+	}
+
+	/* make sure we're not talking to a new remote side */
+	if(r->rcvgen != sgen){
+		if(seq != 0 && seq != 1)
+			goto out;
+
+		/* new connection */
+		if(r->rcvgen != 0){
+			DPRINT("new con r->rcvgen = %lud, sgen = %lud\n", r->rcvgen, sgen);
+			relhangup(c, r);
+		}
+		r->rcvgen = sgen;
+	}
+
+	/* dequeue acked packets */
+	if(ack && agen == r->sndgen){
+		ackreal = 0;
+		while(r->unacked != nil && INSEQ(ack, r->ackrcvd, r->sndseq)){
+			nbp = r->unacked;
+			r->unacked = nbp->list;
+			DPRINT("%lud/%lud acked, r->sndgen = %lud\n", 
+			       ack, agen, r->sndgen);
+			freeb(nbp);
+			r->ackrcvd = NEXTSEQ(r->ackrcvd);
+			ackreal = 1;
+		}
+
+		/* flow control */
+		if(UNACKED(r) < Maxunacked/8 && r->blocked)
+			wakeup(&r->vous);
+
+		/*
+		 *  retransmit next packet if the acked packet
+		 *  was transmitted more than once
+		 */
+		if(ackreal && r->unacked != nil){
+			r->timeout = 0;
+			if(r->xmits > 1){
+				r->xmits = 1;
+				relrexmit(c, r);
+			}
+		}
+		
+	}
+
+	/* no message or input queue full */
+	if(seq == 0 || qfull(c->rq))
+		goto out;
+
+	/* refuse out of order delivery */
+	if(seq != NEXTSEQ(r->rcvseq)){
+		relsendack(c, r, 0);	/* tell him we got it already */
+		upriv->orders++;
+		DPRINT("out of sequence %lud not %lud\n", seq, NEXTSEQ(r->rcvseq));
+		goto out;
+	}
+	r->rcvseq = seq;
+
+	rv = 0;
+out:
+	relput(r);
+	return rv;
+}
+
+void
+relsendack(Conv *c, Reliable *r, int hangup)
+{
+	Udphdr *uh;
+	Block *bp;
+	Rudphdr *rh;
+	int ptcllen;
+	Fs *f;
+
+	bp = allocb(UDP_IPHDR + UDP_RHDRSIZE);
+	if(bp == nil)
+		return;
+	bp->wp += UDP_IPHDR + UDP_RHDRSIZE;
+	f = c->p->f;
+	uh = (Udphdr *)(bp->rp);
+	uh->vihl = IP_VER4;
+	rh = (Rudphdr*)uh;
+
+	ptcllen = (UDP_RHDRSIZE-UDP_PHDRSIZE);
+	uh->Unused = 0;
+	uh->udpproto = IP_UDPPROTO;
+	uh->frag[0] = 0;
+	uh->frag[1] = 0;
+	hnputs(uh->udpplen, ptcllen);
+
+	v6tov4(uh->udpdst, r->addr);
+	hnputs(uh->udpdport, r->port);
+	hnputs(uh->udpsport, c->lport);
+	if(ipcmp(c->laddr, IPnoaddr) == 0)
+		findlocalip(f, c->laddr, c->raddr);
+	v6tov4(uh->udpsrc, c->laddr);
+	hnputs(uh->udplen, ptcllen);
+
+	if(hangup)
+		hnputl(rh->relsgen, Hangupgen);
+	else
+		hnputl(rh->relsgen, r->sndgen);
+	hnputl(rh->relseq, 0);
+	hnputl(rh->relagen, r->rcvgen);
+	hnputl(rh->relack, r->rcvseq);
+
+	if(r->acksent < r->rcvseq)
+		r->acksent = r->rcvseq;
+
+	uh->udpcksum[0] = 0;
+	uh->udpcksum[1] = 0;
+	hnputs(uh->udpcksum, ptclcsum(bp, UDP_IPHDR, UDP_RHDRSIZE));
+
+	DPRINT("sendack: %lud/%lud, %lud/%lud\n", 0L, r->sndgen, r->rcvseq, r->rcvgen);
+	doipoput(c, f, bp, 0, c->ttl, c->tos);
+}
+
+
+/*
+ *  called with ucb locked (and c locked if user initiated close)
+ */
+void
+relhangup(Conv *c, Reliable *r)
+{
+	int n;
+	Block *bp;
+	char hup[ERRMAX];
+
+	n = snprint(hup, sizeof(hup), "hangup %I!%d", r->addr, r->port);
+	qproduce(c->eq, hup, n);
+
+	/*
+	 *  dump any unacked outgoing messages
+	 */
+	for(bp = r->unacked; bp != nil; bp = r->unacked){
+		r->unacked = bp->list;
+		bp->list = nil;
+		freeb(bp);
+	}
+
+	r->rcvgen = 0;
+	r->rcvseq = 0;
+	r->acksent = 0;
+	if(generation == Hangupgen)
+		generation++;
+	r->sndgen = generation++;
+	r->sndseq = 0;
+	r->ackrcvd = 0;
+	r->xmits = 0;
+	r->timeout = 0;
+	wakeup(&r->vous);
+}
+
+/*
+ *  called with ucb locked
+ */
+void
+relrexmit(Conv *c, Reliable *r)
+{
+	Rudppriv *upriv;
+	Block *np;
+	Fs *f;
+
+	upriv = c->p->priv;
+	f = c->p->f;
+	r->timeout = 0;
+	if(r->xmits++ > Rudpmaxxmit){
+		relhangup(c, r);
+		return;
+	}
+
+	upriv->rxmits++;
+	np = copyblock(r->unacked, blocklen(r->unacked));
+	DPRINT("rxmit r->ackrvcd+1 = %lud\n", r->ackrcvd+1);
+	doipoput(c, f, np, 0, c->ttl, c->tos);
+}
--- /dev/null
+++ b/os/ip/tcp.c
@@ -1,0 +1,3194 @@
+#include	"u.h"
+#include	"../port/lib.h"
+#include	"mem.h"
+#include	"dat.h"
+#include	"fns.h"
+#include	"../port/error.h"
+
+#include	"ip.h"
+
+enum
+{
+	QMAX		= 64*1024-1,
+	IP_TCPPROTO	= 6,
+
+	TCP4_IPLEN	= 8,
+	TCP4_PHDRSIZE	= 12,
+	TCP4_HDRSIZE	= 20,
+	TCP4_TCBPHDRSZ	= 40,
+	TCP4_PKT	= TCP4_IPLEN+TCP4_PHDRSIZE,
+
+	TCP6_IPLEN	= 0,
+	TCP6_PHDRSIZE	= 40,
+	TCP6_HDRSIZE	= 20,
+	TCP6_TCBPHDRSZ	= 60,
+	TCP6_PKT	= TCP6_IPLEN+TCP6_PHDRSIZE,
+
+	TcptimerOFF	= 0,
+	TcptimerON	= 1,
+	TcptimerDONE	= 2,
+	MAX_TIME 	= (1<<20),	/* Forever */
+	TCP_ACK		= 50,		/* Timed ack sequence in ms */
+	MAXBACKMS	= 9*60*1000,	/* longest backoff time (ms) before hangup */
+
+	URG		= 0x20,		/* Data marked urgent */
+	ACK		= 0x10,		/* Acknowledge is valid */
+	PSH		= 0x08,		/* Whole data pipe is pushed */
+	RST		= 0x04,		/* Reset connection */
+	SYN		= 0x02,		/* Pkt. is synchronise */
+	FIN		= 0x01,		/* Start close down */
+
+	EOLOPT		= 0,
+	NOOPOPT		= 1,
+	MSSOPT		= 2,
+	MSS_LENGTH	= 4,		/* Mean segment size */
+	WSOPT		= 3,
+	WS_LENGTH	= 3,		/* Bits to scale window size by */
+	MSL2		= 10,
+	MSPTICK		= 50,		/* Milliseconds per timer tick */
+	DEF_MSS		= 1460,		/* Default mean segment */
+	DEF_MSS6	= 1280,		/* Default mean segment (min) for v6 */
+	DEF_RTT		= 500,		/* Default round trip */
+	DEF_KAT		= 120000,	/* Default time (ms) between keep alives */
+	TCP_LISTEN	= 0,		/* Listen connection */
+	TCP_CONNECT	= 1,		/* Outgoing connection */
+	SYNACK_RXTIMER	= 250,		/* ms between SYNACK retransmits */
+
+	TCPREXMTTHRESH	= 3,		/* dupack threshhold for rxt */
+
+	FORCE		= 1,
+	CLONE		= 2,
+	RETRAN		= 4,
+	ACTIVE		= 8,
+	SYNACK		= 16,
+
+	LOGAGAIN	= 3,
+	LOGDGAIN	= 2,
+
+	Closed		= 0,		/* Connection states */
+	Listen,
+	Syn_sent,
+	Syn_received,
+	Established,
+	Finwait1,
+	Finwait2,
+	Close_wait,
+	Closing,
+	Last_ack,
+	Time_wait,
+
+	Maxlimbo	= 1000,		/* maximum procs waiting for response to SYN ACK */
+	NLHT		= 256,		/* hash table size, must be a power of 2 */
+	LHTMASK		= NLHT-1,
+
+	HaveWS		= 1<<8,
+};
+
+/* Must correspond to the enumeration above */
+char *tcpstates[] =
+{
+	"Closed", 	"Listen", 	"Syn_sent", "Syn_received",
+	"Established", 	"Finwait1",	"Finwait2", "Close_wait",
+	"Closing", 	"Last_ack", 	"Time_wait"
+};
+
+typedef struct Tcptimer Tcptimer;
+struct Tcptimer
+{
+	Tcptimer	*next;
+	Tcptimer	*prev;
+	Tcptimer	*readynext;
+	int	state;
+	int	start;
+	int	count;
+	void	(*func)(void*);
+	void	*arg;
+};
+
+/*
+ *  v4 and v6 pseudo headers used for
+ *  checksuming tcp
+ */
+typedef struct Tcp4hdr Tcp4hdr;
+struct Tcp4hdr
+{
+	uchar	vihl;		/* Version and header length */
+	uchar	tos;		/* Type of service */
+	uchar	length[2];	/* packet length */
+	uchar	id[2];		/* Identification */
+	uchar	frag[2];	/* Fragment information */
+	uchar	Unused;
+	uchar	proto;
+	uchar	tcplen[2];
+	uchar	tcpsrc[4];
+	uchar	tcpdst[4];
+	uchar	tcpsport[2];
+	uchar	tcpdport[2];
+	uchar	tcpseq[4];
+	uchar	tcpack[4];
+	uchar	tcpflag[2];
+	uchar	tcpwin[2];
+	uchar	tcpcksum[2];
+	uchar	tcpurg[2];
+	/* Options segment */
+	uchar	tcpopt[1];
+};
+
+typedef struct Tcp6hdr Tcp6hdr;
+struct Tcp6hdr
+{
+	uchar	vcf[4];
+	uchar	ploadlen[2];
+	uchar	proto;
+	uchar	ttl;
+	uchar	tcpsrc[IPaddrlen];
+	uchar	tcpdst[IPaddrlen];
+	uchar	tcpsport[2];
+	uchar	tcpdport[2];
+	uchar	tcpseq[4];
+	uchar	tcpack[4];
+	uchar	tcpflag[2];
+	uchar	tcpwin[2];
+	uchar	tcpcksum[2];
+	uchar	tcpurg[2];
+	/* Options segment */
+	uchar	tcpopt[1];
+};
+
+/*
+ *  this represents the control info
+ *  for a single packet.  It is derived from
+ *  a packet in ntohtcp{4,6}() and stuck into
+ *  a packet in htontcp{4,6}().
+ */
+typedef struct Tcp Tcp;
+struct	Tcp
+{
+	ushort	source;
+	ushort	dest;
+	ulong	seq;
+	ulong	ack;
+	uchar	flags;
+	ushort	ws;	/* window scale option (if not zero) */
+	ulong	wnd;
+	ushort	urg;
+	ushort	mss;	/* max segment size option (if not zero) */
+	ushort	len;	/* size of data */
+};
+
+/*
+ *  this header is malloc'd to thread together fragments
+ *  waiting to be coalesced
+ */
+typedef struct Reseq Reseq;
+struct Reseq
+{
+	Reseq	*next;
+	Tcp	seg;
+	Block	*bp;
+	ushort	length;
+};
+
+/*
+ *  the qlock in the Conv locks this structure
+ */
+typedef struct Tcpctl Tcpctl;
+struct Tcpctl
+{
+	uchar	state;			/* Connection state */
+	uchar	type;			/* Listening or active connection */
+	uchar	code;			/* Icmp code */
+	struct {
+		ulong	una;		/* Unacked data pointer */
+		ulong	nxt;		/* Next sequence expected */
+		ulong	ptr;		/* Data pointer */
+		ulong	wnd;		/* Tcp send window */
+		ulong	urg;		/* Urgent data pointer */
+		ulong	wl2;
+		int	scale;		/* how much to right shift window in xmitted packets */
+		/* to implement tahoe and reno TCP */
+		ulong	dupacks;	/* number of duplicate acks rcvd */
+		int	recovery;	/* loss recovery flag */
+		ulong	rxt;		/* right window marker for recovery */
+	} snd;
+	struct {
+		ulong	nxt;		/* Receive pointer to next uchar slot */
+		ulong	wnd;		/* Receive window incoming */
+		ulong	urg;		/* Urgent pointer */
+		int	blocked;
+		int	una;		/* unacked data segs */
+		int	scale;		/* how much to left shift window in rcved packets */
+	} rcv;
+	ulong	iss;			/* Initial sequence number */
+	int	sawwsopt;		/* true if we saw a wsopt on the incoming SYN */
+	ulong	cwind;			/* Congestion window */
+	int	scale;			/* desired snd.scale */
+	ushort	ssthresh;		/* Slow start threshold */
+	int	resent;			/* Bytes just resent */
+	int	irs;			/* Initial received squence */
+	ushort	mss;			/* Mean segment size */
+	int	rerecv;			/* Overlap of data rerecevived */
+	ulong	window;			/* Recevive window */
+	uchar	backoff;		/* Exponential backoff counter */
+	int	backedoff;		/* ms we've backed off for rexmits */
+	uchar	flags;			/* State flags */
+	Reseq	*reseq;			/* Resequencing queue */
+	Tcptimer	timer;			/* Activity timer */
+	Tcptimer	acktimer;		/* Acknowledge timer */
+	Tcptimer	rtt_timer;		/* Round trip timer */
+	Tcptimer	katimer;		/* keep alive timer */
+	ulong	rttseq;			/* Round trip sequence */
+	int	srtt;			/* Shortened round trip */
+	int	mdev;			/* Mean deviation of round trip */
+	int	kacounter;		/* count down for keep alive */
+	uint	sndsyntime;		/* time syn sent */
+	ulong	time;			/* time Finwait2 or Syn_received was sent */
+	int	nochecksum;		/* non-zero means don't send checksums */
+	int	flgcnt;			/* number of flags in the sequence (FIN,SEQ) */
+
+	union {
+		Tcp4hdr	tcp4hdr;
+		Tcp6hdr	tcp6hdr;
+	} protohdr;		/* prototype header */
+};
+
+/*
+ *  New calls are put in limbo rather than having a conversation structure
+ *  allocated.  Thus, a SYN attack results in lots of limbo'd calls but not
+ *  any real Conv structures mucking things up.  Calls in limbo rexmit their
+ *  SYN ACK every SYNACK_RXTIMER ms up to 4 times, i.e., they disappear after 1 second.
+ *
+ *  In particular they aren't on a listener's queue so that they don't figure
+ *  in the input queue limit.
+ *
+ *  If 1/2 of a T3 was attacking SYN packets, we'ld have a permanent queue
+ *  of 70000 limbo'd calls.  Not great for a linear list but doable.  Therefore
+ *  there is no hashing of this list.
+ */
+typedef struct Limbo Limbo;
+struct Limbo
+{
+	Limbo	*next;
+
+	uchar	laddr[IPaddrlen];
+	uchar	raddr[IPaddrlen];
+	ushort	lport;
+	ushort	rport;
+	ulong	irs;		/* initial received sequence */
+	ulong	iss;		/* initial sent sequence */
+	ushort	mss;		/* mss from the other end */
+	ushort	rcvscale;	/* how much to scale rcvd windows */
+	ushort	sndscale;	/* how much to scale sent windows */
+	ulong	lastsend;	/* last time we sent a synack */
+	uchar	version;	/* v4 or v6 */
+	uchar	rexmits;	/* number of retransmissions */
+};
+
+int	tcp_irtt = DEF_RTT;	/* Initial guess at round trip time */
+ushort	tcp_mss = DEF_MSS;	/* Maximum segment size to be sent */
+
+enum {
+	/* MIB stats */
+	MaxConn,
+	ActiveOpens,
+	PassiveOpens,
+	EstabResets,
+	CurrEstab,
+	InSegs,
+	OutSegs,
+	RetransSegs,
+	RetransTimeouts,
+	InErrs,
+	OutRsts,
+
+	/* non-MIB stats */
+	CsumErrs,
+	HlenErrs,
+	LenErrs,
+	OutOfOrder,
+
+	Nstats
+};
+
+static char *statnames[] =
+{
+[MaxConn]	"MaxConn",
+[ActiveOpens]	"ActiveOpens",
+[PassiveOpens]	"PassiveOpens",
+[EstabResets]	"EstabResets",
+[CurrEstab]	"CurrEstab",
+[InSegs]	"InSegs",
+[OutSegs]	"OutSegs",
+[RetransSegs]	"RetransSegs",
+[RetransTimeouts]	"RetransTimeouts",
+[InErrs]	"InErrs",
+[OutRsts]	"OutRsts",
+[CsumErrs]	"CsumErrs",
+[HlenErrs]	"HlenErrs",
+[LenErrs]	"LenErrs",
+[OutOfOrder]	"OutOfOrder",
+};
+
+typedef struct Tcppriv Tcppriv;
+struct Tcppriv
+{
+	/* List of active timers */
+	QLock 	tl;
+	Tcptimer *timers;
+
+	/* hash table for matching conversations */
+	Ipht	ht;
+
+	/* calls in limbo waiting for an ACK to our SYN ACK */
+	int	nlimbo;
+	Limbo	*lht[NLHT];
+
+	/* for keeping track of tcpackproc */
+	QLock	apl;
+	int	ackprocstarted;
+
+	ulong	stats[Nstats];
+};
+
+/*
+ *  Setting tcpporthogdefense to non-zero enables Dong Lin's
+ *  solution to hijacked systems staking out port's as a form
+ *  of DoS attack.
+ *
+ *  To avoid stateless Conv hogs, we pick a sequence number at random.  If
+ *  it that number gets acked by the other end, we shut down the connection.
+ *  Look for tcpporthogedefense in the code.
+ */
+int tcpporthogdefense = 0;
+
+int	addreseq(Tcpctl*, Tcppriv*, Tcp*, Block*, ushort);
+void	getreseq(Tcpctl*, Tcp*, Block**, ushort*);
+void	localclose(Conv*, char*);
+void	procsyn(Conv*, Tcp*);
+void	tcpiput(Proto*, Ipifc*, Block*);
+void	tcpoutput(Conv*);
+int	tcptrim(Tcpctl*, Tcp*, Block**, ushort*);
+void	tcpstart(Conv*, int);
+void	tcptimeout(void*);
+void	tcpsndsyn(Conv*, Tcpctl*);
+void	tcprcvwin(Conv*);
+void	tcpacktimer(void*);
+void	tcpkeepalive(void*);
+void	tcpsetkacounter(Tcpctl*);
+void	tcprxmit(Conv*);
+void	tcpsettimer(Tcpctl*);
+void	tcpsynackrtt(Conv*);
+void	tcpsetscale(Conv*, Tcpctl*, ushort, ushort);
+
+static void limborexmit(Proto*);
+static void limbo(Conv*, uchar*, uchar*, Tcp*, int);
+
+void
+tcpsetstate(Conv *s, uchar newstate)
+{
+	Tcpctl *tcb;
+	uchar oldstate;
+	Tcppriv *tpriv;
+
+	tpriv = s->p->priv;
+
+	tcb = (Tcpctl*)s->ptcl;
+
+	oldstate = tcb->state;
+	if(oldstate == newstate)
+		return;
+
+	if(oldstate == Established)
+		tpriv->stats[CurrEstab]--;
+	if(newstate == Established)
+		tpriv->stats[CurrEstab]++;
+
+	/**
+	print( "%d/%d %s->%s CurrEstab=%d\n", s->lport, s->rport,
+		tcpstates[oldstate], tcpstates[newstate], tpriv->tstats.tcpCurrEstab );
+	**/
+
+	switch(newstate) {
+	case Closed:
+		qclose(s->rq);
+		qclose(s->wq);
+		qclose(s->eq);
+		break;
+
+	case Close_wait:		/* Remote closes */
+		qhangup(s->rq, nil);
+		break;
+	}
+
+	tcb->state = newstate;
+
+	if(oldstate == Syn_sent && newstate != Closed)
+		Fsconnected(s, nil);
+}
+
+static char*
+tcpconnect(Conv *c, char **argv, int argc)
+{
+	char *e;
+
+	e = Fsstdconnect(c, argv, argc);
+	if(e != nil)
+		return e;
+	tcpstart(c, TCP_CONNECT);
+
+	return nil;
+}
+
+static int
+tcpstate(Conv *c, char *state, int n)
+{
+	Tcpctl *s;
+
+	s = (Tcpctl*)(c->ptcl);
+
+	return snprint(state, n,
+		"%s qin %d qout %d srtt %d mdev %d cwin %lud swin %lud>>%d rwin %lud>>%d timer.start %d timer.count %d rerecv %d katimer.start %d katimer.count %d\n",
+		tcpstates[s->state],
+		c->rq ? qlen(c->rq) : 0,
+		c->wq ? qlen(c->wq) : 0,
+		s->srtt, s->mdev,
+		s->cwind, s->snd.wnd, s->rcv.scale, s->rcv.wnd, s->snd.scale,
+		s->timer.start, s->timer.count, s->rerecv,
+		s->katimer.start, s->katimer.count);
+}
+
+static int
+tcpinuse(Conv *c)
+{
+	Tcpctl *s;
+
+	s = (Tcpctl*)(c->ptcl);
+	return s->state != Closed;
+}
+
+static char*
+tcpannounce(Conv *c, char **argv, int argc)
+{
+	char *e;
+
+	e = Fsstdannounce(c, argv, argc);
+	if(e != nil)
+		return e;
+	tcpstart(c, TCP_LISTEN);
+	Fsconnected(c, nil);
+
+	return nil;
+}
+
+/*
+ *  tcpclose is always called with the q locked
+ */
+static void
+tcpclose(Conv *c)
+{
+	Tcpctl *tcb;
+
+	tcb = (Tcpctl*)c->ptcl;
+
+	qhangup(c->rq, nil);
+	qhangup(c->wq, nil);
+	qhangup(c->eq, nil);
+	qflush(c->rq);
+
+	switch(tcb->state) {
+	case Listen:
+		/*
+		 *  reset any incoming calls to this listener
+		 */
+		Fsconnected(c, "Hangup");
+
+		localclose(c, nil);
+		break;
+	case Closed:
+	case Syn_sent:
+		localclose(c, nil);
+		break;
+	case Syn_received:
+	case Established:
+		tcb->flgcnt++;
+		tcb->snd.nxt++;
+		tcpsetstate(c, Finwait1);
+		tcpoutput(c);
+		break;
+	case Close_wait:
+		tcb->flgcnt++;
+		tcb->snd.nxt++;
+		tcpsetstate(c, Last_ack);
+		tcpoutput(c);
+		break;
+	}
+}
+
+void
+tcpkick(void *x)
+{
+	Conv *s = x;
+	Tcpctl *tcb;
+
+	tcb = (Tcpctl*)s->ptcl;
+
+	if(waserror()){
+		qunlock(s);
+		nexterror();
+	}
+	qlock(s);
+
+	switch(tcb->state) {
+	case Syn_sent:
+	case Syn_received:
+	case Established:
+	case Close_wait:
+		/*
+		 * Push data
+		 */
+		tcprcvwin(s);
+		tcpoutput(s);
+		break;
+	default:
+		localclose(s, "Hangup");
+		break;
+	}
+
+	qunlock(s);
+	poperror();
+}
+
+void
+tcprcvwin(Conv *s)				/* Call with tcb locked */
+{
+	int w;
+	Tcpctl *tcb;
+
+	tcb = (Tcpctl*)s->ptcl;
+	w = tcb->window - qlen(s->rq);
+	if(w < 0)
+		w = 0;
+	tcb->rcv.wnd = w;
+	if(w == 0)
+		tcb->rcv.blocked = 1;
+}
+
+void
+tcpacktimer(void *v)
+{
+	Tcpctl *tcb;
+	Conv *s;
+
+	s = v;
+	tcb = (Tcpctl*)s->ptcl;
+
+	if(waserror()){
+		qunlock(s);
+		nexterror();
+	}
+	qlock(s);
+	if(tcb->state != Closed){
+		tcb->flags |= FORCE;
+		tcprcvwin(s);
+		tcpoutput(s);
+	}
+	qunlock(s);
+	poperror();
+}
+
+static void
+tcpcreate(Conv *c)
+{
+	c->rq = qopen(QMAX, Qcoalesce, tcpacktimer, c);
+	c->wq = qopen((3*QMAX)/2, Qkick, tcpkick, c);
+}
+
+static void
+timerstate(Tcppriv *priv, Tcptimer *t, int newstate)
+{
+	if(newstate != TcptimerON){
+		if(t->state == TcptimerON){
+			// unchain
+			if(priv->timers == t){
+				priv->timers = t->next;
+				if(t->prev != nil)
+					panic("timerstate1");
+			}
+			if(t->next)
+				t->next->prev = t->prev;
+			if(t->prev)
+				t->prev->next = t->next;
+			t->next = t->prev = nil;
+		}
+	} else {
+		if(t->state != TcptimerON){
+			// chain
+			if(t->prev != nil || t->next != nil)
+				panic("timerstate2");
+			t->prev = nil;
+			t->next = priv->timers;
+			if(t->next)
+				t->next->prev = t;
+			priv->timers = t;
+		}
+	}
+	t->state = newstate;
+}
+
+void
+tcpackproc(void *a)
+{
+	Tcptimer *t, *tp, *timeo;
+	Proto *tcp;
+	Tcppriv *priv;
+	int loop;
+
+	tcp = a;
+	priv = tcp->priv;
+
+	for(;;) {
+		tsleep(&up->sleep, return0, 0, MSPTICK);
+
+		qlock(&priv->tl);
+		timeo = nil;
+		loop = 0;
+		for(t = priv->timers; t != nil; t = tp) {
+			if(loop++ > 10000)
+				panic("tcpackproc1");
+			tp = t->next;
+ 			if(t->state == TcptimerON) {
+				t->count--;
+				if(t->count == 0) {
+					timerstate(priv, t, TcptimerDONE);
+					t->readynext = timeo;
+					timeo = t;
+				}
+			}
+		}
+		qunlock(&priv->tl);
+
+		loop = 0;
+		for(t = timeo; t != nil; t = t->readynext) {
+			if(loop++ > 10000)
+				panic("tcpackproc2");
+			if(t->state == TcptimerDONE && t->func != nil && !waserror()){
+				(*t->func)(t->arg);
+				poperror();
+			}
+		}
+
+		limborexmit(tcp);
+	}
+}
+
+void
+tcpgo(Tcppriv *priv, Tcptimer *t)
+{
+	if(t == nil || t->start == 0)
+		return;
+
+	qlock(&priv->tl);
+	t->count = t->start;
+	timerstate(priv, t, TcptimerON);
+	qunlock(&priv->tl);
+}
+
+void
+tcphalt(Tcppriv *priv, Tcptimer *t)
+{
+	if(t == nil)
+		return;
+
+	qlock(&priv->tl);
+	timerstate(priv, t, TcptimerOFF);
+	qunlock(&priv->tl);
+}
+
+int
+backoff(int n)
+{
+	return 1 << n;
+}
+
+void
+localclose(Conv *s, char *reason)	/* called with tcb locked */
+{
+	Tcpctl *tcb;
+	Reseq *rp,*rp1;
+	Tcppriv *tpriv;
+
+	tpriv = s->p->priv;
+	tcb = (Tcpctl*)s->ptcl;
+
+	iphtrem(&tpriv->ht, s);
+
+	tcphalt(tpriv, &tcb->timer);
+	tcphalt(tpriv, &tcb->rtt_timer);
+	tcphalt(tpriv, &tcb->acktimer);
+	tcphalt(tpriv, &tcb->katimer);
+
+	/* Flush reassembly queue; nothing more can arrive */
+	for(rp = tcb->reseq; rp != nil; rp = rp1) {
+		rp1 = rp->next;
+		freeblist(rp->bp);
+		free(rp);
+	}
+	tcb->reseq = nil;
+
+	if(tcb->state == Syn_sent)
+		Fsconnected(s, reason);
+	if(s->state == Announced)
+		wakeup(&s->listenr);
+
+	qhangup(s->rq, reason);
+	qhangup(s->wq, reason);
+
+	tcpsetstate(s, Closed);
+}
+
+/* mtu (- TCP + IP hdr len) of 1st hop */
+int
+tcpmtu(Proto *tcp, uchar *addr, int version, int *scale)
+{
+	Ipifc *ifc;
+	int mtu;
+
+	ifc = findipifc(tcp->f, addr, 0);
+	switch(version){
+	default:
+	case V4:
+		mtu = DEF_MSS;
+		if(ifc != nil)
+			mtu = ifc->maxtu - ifc->m->hsize - (TCP4_PKT + TCP4_HDRSIZE);
+		break;
+	case V6:
+		mtu = DEF_MSS6;
+		if(ifc != nil)
+			mtu = ifc->maxtu - ifc->m->hsize - (TCP6_PKT + TCP6_HDRSIZE);
+		break;
+	}
+	if(ifc != nil){
+		if(ifc->mbps > 100)
+			*scale = HaveWS | 3;
+		else if(ifc->mbps > 10)
+			*scale = HaveWS | 1;
+		else
+			*scale = HaveWS | 0;
+	} else
+		*scale = HaveWS | 0;
+
+	return mtu;
+}
+
+void
+inittcpctl(Conv *s, int mode)
+{
+	Tcpctl *tcb;
+	Tcp4hdr* h4;
+	Tcp6hdr* h6;
+	int mss;
+
+	tcb = (Tcpctl*)s->ptcl;
+
+	memset(tcb, 0, sizeof(Tcpctl));
+
+	tcb->ssthresh = 65535;
+	tcb->srtt = tcp_irtt<<LOGAGAIN;
+	tcb->mdev = 0;
+
+	/* setup timers */
+	tcb->timer.start = tcp_irtt / MSPTICK;
+	tcb->timer.func = tcptimeout;
+	tcb->timer.arg = s;
+	tcb->rtt_timer.start = MAX_TIME;
+	tcb->acktimer.start = TCP_ACK / MSPTICK;
+	tcb->acktimer.func = tcpacktimer;
+	tcb->acktimer.arg = s;
+	tcb->katimer.start = DEF_KAT / MSPTICK;
+	tcb->katimer.func = tcpkeepalive;
+	tcb->katimer.arg = s;
+
+	mss = DEF_MSS;
+
+	/* create a prototype(pseudo) header */
+	if(mode != TCP_LISTEN){
+		if(ipcmp(s->laddr, IPnoaddr) == 0)
+			findlocalip(s->p->f, s->laddr, s->raddr);
+
+		switch(s->ipversion){
+		case V4:
+			h4 = &tcb->protohdr.tcp4hdr;
+			memset(h4, 0, sizeof(*h4));
+			h4->proto = IP_TCPPROTO;
+			hnputs(h4->tcpsport, s->lport);
+			hnputs(h4->tcpdport, s->rport);
+			v6tov4(h4->tcpsrc, s->laddr);
+			v6tov4(h4->tcpdst, s->raddr);
+			break;
+		case V6:
+			h6 = &tcb->protohdr.tcp6hdr;
+			memset(h6, 0, sizeof(*h6));
+			h6->proto = IP_TCPPROTO;
+			hnputs(h6->tcpsport, s->lport);
+			hnputs(h6->tcpdport, s->rport);
+			ipmove(h6->tcpsrc, s->laddr);
+			ipmove(h6->tcpdst, s->raddr);
+			mss = DEF_MSS6;
+			break;
+		default:
+			panic("inittcpctl: version %d", s->ipversion);
+		}
+	}
+
+	tcb->mss = tcb->cwind = mss;
+
+	/* default is no window scaling */
+	tcb->window = QMAX;
+	tcb->rcv.wnd = QMAX;
+	tcb->rcv.scale = 0;
+	tcb->snd.scale = 0;
+	qsetlimit(s->rq, QMAX);
+}
+
+/*
+ *  called with s qlocked
+ */
+void
+tcpstart(Conv *s, int mode)
+{
+	Tcpctl *tcb;
+	Tcppriv *tpriv;
+	char kpname[KNAMELEN];
+
+	tpriv = s->p->priv;
+
+	if(tpriv->ackprocstarted == 0){
+		qlock(&tpriv->apl);
+		if(tpriv->ackprocstarted == 0){
+			sprint(kpname, "#I%dtcpack", s->p->f->dev);
+			kproc(kpname, tcpackproc, s->p, 0);
+			tpriv->ackprocstarted = 1;
+		}
+		qunlock(&tpriv->apl);
+	}
+
+	tcb = (Tcpctl*)s->ptcl;
+
+	inittcpctl(s, mode);
+
+	iphtadd(&tpriv->ht, s);
+	switch(mode) {
+	case TCP_LISTEN:
+		tpriv->stats[PassiveOpens]++;
+		tcb->flags |= CLONE;
+		tcpsetstate(s, Listen);
+		break;
+
+	case TCP_CONNECT:
+		tpriv->stats[ActiveOpens]++;
+		tcb->flags |= ACTIVE;
+		tcpsndsyn(s, tcb);
+		tcpsetstate(s, Syn_sent);
+		tcpoutput(s);
+		break;
+	}
+}
+
+static char*
+tcpflag(ushort flag)
+{
+	static char buf[128];
+
+	sprint(buf, "%d", flag>>10);	/* Head len */
+	if(flag & URG)
+		strcat(buf, " URG");
+	if(flag & ACK)
+		strcat(buf, " ACK");
+	if(flag & PSH)
+		strcat(buf, " PSH");
+	if(flag & RST)
+		strcat(buf, " RST");
+	if(flag & SYN)
+		strcat(buf, " SYN");
+	if(flag & FIN)
+		strcat(buf, " FIN");
+
+	return buf;
+}
+
+Block *
+htontcp6(Tcp *tcph, Block *data, Tcp6hdr *ph, Tcpctl *tcb)
+{
+	int dlen;
+	Tcp6hdr *h;
+	ushort csum;
+	ushort hdrlen, optpad = 0;
+	uchar *opt;
+
+	hdrlen = TCP6_HDRSIZE;
+	if(tcph->flags & SYN){
+		if(tcph->mss)
+			hdrlen += MSS_LENGTH;
+		if(tcph->ws)
+			hdrlen += WS_LENGTH;
+		optpad = hdrlen & 3;
+		if(optpad)
+			optpad = 4 - optpad;
+		hdrlen += optpad;
+	}
+
+	if(data) {
+		dlen = blocklen(data);
+		data = padblock(data, hdrlen + TCP6_PKT);
+		if(data == nil)
+			return nil;
+	}
+	else {
+		dlen = 0;
+		data = allocb(hdrlen + TCP6_PKT + 64);	/* the 64 pad is to meet mintu's */
+		if(data == nil)
+			return nil;
+		data->wp += hdrlen + TCP6_PKT;
+	}
+
+	/* copy in pseudo ip header plus port numbers */
+	h = (Tcp6hdr *)(data->rp);
+	memmove(h, ph, TCP6_TCBPHDRSZ);
+
+	/* compose pseudo tcp header, do cksum calculation */
+	hnputl(h->vcf, hdrlen + dlen);
+	h->ploadlen[0] = h->ploadlen[1] = h->proto = 0;
+	h->ttl = ph->proto;
+
+	/* copy in variable bits */
+	hnputl(h->tcpseq, tcph->seq);
+	hnputl(h->tcpack, tcph->ack);
+	hnputs(h->tcpflag, (hdrlen<<10) | tcph->flags);
+	hnputs(h->tcpwin, tcph->wnd>>(tcb != nil ? tcb->snd.scale : 0));
+	hnputs(h->tcpurg, tcph->urg);
+
+	if(tcph->flags & SYN){
+		opt = h->tcpopt;
+		if(tcph->mss != 0){
+			*opt++ = MSSOPT;
+			*opt++ = MSS_LENGTH;
+			hnputs(opt, tcph->mss);
+			opt += 2;
+		}
+		if(tcph->ws != 0){
+			*opt++ = WSOPT;
+			*opt++ = WS_LENGTH;
+			*opt++ = tcph->ws;
+		}
+		while(optpad-- > 0)
+			*opt++ = NOOPOPT;
+	}
+
+	if(tcb != nil && tcb->nochecksum){
+		h->tcpcksum[0] = h->tcpcksum[1] = 0;
+	} else {
+		csum = ptclcsum(data, TCP6_IPLEN, hdrlen+dlen+TCP6_PHDRSIZE);
+		hnputs(h->tcpcksum, csum);
+	}
+
+	/* move from pseudo header back to normal ip header */
+	memset(h->vcf, 0, 4);
+	h->vcf[0] = IP_VER6;
+	hnputs(h->ploadlen, hdrlen+dlen);
+	h->proto = ph->proto;
+
+	return data;
+}
+
+Block *
+htontcp4(Tcp *tcph, Block *data, Tcp4hdr *ph, Tcpctl *tcb)
+{
+	int dlen;
+	Tcp4hdr *h;
+	ushort csum;
+	ushort hdrlen, optpad = 0;
+	uchar *opt;
+
+	hdrlen = TCP4_HDRSIZE;
+	if(tcph->flags & SYN){
+		if(tcph->mss)
+			hdrlen += MSS_LENGTH;
+		if(tcph->ws)
+			hdrlen += WS_LENGTH;
+		optpad = hdrlen & 3;
+		if(optpad)
+			optpad = 4 - optpad;
+		hdrlen += optpad;
+	}
+
+	if(data) {
+		dlen = blocklen(data);
+		data = padblock(data, hdrlen + TCP4_PKT);
+		if(data == nil)
+			return nil;
+	}
+	else {
+		dlen = 0;
+		data = allocb(hdrlen + TCP4_PKT + 64);	/* the 64 pad is to meet mintu's */
+		if(data == nil)
+			return nil;
+		data->wp += hdrlen + TCP4_PKT;
+	}
+
+	/* copy in pseudo ip header plus port numbers */
+	h = (Tcp4hdr *)(data->rp);
+	memmove(h, ph, TCP4_TCBPHDRSZ);
+
+	/* copy in variable bits */
+	hnputs(h->tcplen, hdrlen + dlen);
+	hnputl(h->tcpseq, tcph->seq);
+	hnputl(h->tcpack, tcph->ack);
+	hnputs(h->tcpflag, (hdrlen<<10) | tcph->flags);
+	hnputs(h->tcpwin, tcph->wnd>>(tcb != nil ? tcb->snd.scale : 0));
+	hnputs(h->tcpurg, tcph->urg);
+
+	if(tcph->flags & SYN){
+		opt = h->tcpopt;
+		if(tcph->mss != 0){
+			*opt++ = MSSOPT;
+			*opt++ = MSS_LENGTH;
+			hnputs(opt, tcph->mss);
+			opt += 2;
+		}
+		if(tcph->ws != 0){
+			*opt++ = WSOPT;
+			*opt++ = WS_LENGTH;
+			*opt++ = tcph->ws;
+		}
+		while(optpad-- > 0)
+			*opt++ = NOOPOPT;
+	}
+
+	if(tcb != nil && tcb->nochecksum){
+		h->tcpcksum[0] = h->tcpcksum[1] = 0;
+	} else {
+		csum = ptclcsum(data, TCP4_IPLEN, hdrlen+dlen+TCP4_PHDRSIZE);
+		hnputs(h->tcpcksum, csum);
+	}
+
+	return data;
+}
+
+int
+ntohtcp6(Tcp *tcph, Block **bpp)
+{
+	Tcp6hdr *h;
+	uchar *optr;
+	ushort hdrlen;
+	ushort optlen;
+	int n;
+
+	*bpp = pullupblock(*bpp, TCP6_PKT+TCP6_HDRSIZE);
+	if(*bpp == nil)
+		return -1;
+
+	h = (Tcp6hdr *)((*bpp)->rp);
+	tcph->source = nhgets(h->tcpsport);
+	tcph->dest = nhgets(h->tcpdport);
+	tcph->seq = nhgetl(h->tcpseq);
+	tcph->ack = nhgetl(h->tcpack);
+	hdrlen = (h->tcpflag[0]>>2) & ~3;
+	if(hdrlen < TCP6_HDRSIZE) {
+		freeblist(*bpp);
+		return -1;
+	}
+
+	tcph->flags = h->tcpflag[1];
+	tcph->wnd = nhgets(h->tcpwin);
+	tcph->urg = nhgets(h->tcpurg);
+	tcph->mss = 0;
+	tcph->ws = 0;
+	tcph->len = nhgets(h->ploadlen) - hdrlen;
+
+	*bpp = pullupblock(*bpp, hdrlen+TCP6_PKT);
+	if(*bpp == nil)
+		return -1;
+
+	optr = h->tcpopt;
+	n = hdrlen - TCP6_HDRSIZE;
+	while(n > 0 && *optr != EOLOPT) {
+		if(*optr == NOOPOPT) {
+			n--;
+			optr++;
+			continue;
+		}
+		optlen = optr[1];
+		if(optlen < 2 || optlen > n)
+			break;
+		switch(*optr) {
+		case MSSOPT:
+			if(optlen == MSS_LENGTH)
+				tcph->mss = nhgets(optr+2);
+			break;
+		case WSOPT:
+			if(optlen == WS_LENGTH && *(optr+2) <= 14)
+				tcph->ws = HaveWS | *(optr+2);
+			break;
+		}
+		n -= optlen;
+		optr += optlen;
+	}
+	return hdrlen;
+}
+
+int
+ntohtcp4(Tcp *tcph, Block **bpp)
+{
+	Tcp4hdr *h;
+	uchar *optr;
+	ushort hdrlen;
+	ushort optlen;
+	int n;
+
+	*bpp = pullupblock(*bpp, TCP4_PKT+TCP4_HDRSIZE);
+	if(*bpp == nil)
+		return -1;
+
+	h = (Tcp4hdr *)((*bpp)->rp);
+	tcph->source = nhgets(h->tcpsport);
+	tcph->dest = nhgets(h->tcpdport);
+	tcph->seq = nhgetl(h->tcpseq);
+	tcph->ack = nhgetl(h->tcpack);
+
+	hdrlen = (h->tcpflag[0]>>2) & ~3;
+	if(hdrlen < TCP4_HDRSIZE) {
+		freeblist(*bpp);
+		return -1;
+	}
+
+	tcph->flags = h->tcpflag[1];
+	tcph->wnd = nhgets(h->tcpwin);
+	tcph->urg = nhgets(h->tcpurg);
+	tcph->mss = 0;
+	tcph->ws = 0;
+	tcph->len = nhgets(h->length) - (hdrlen + TCP4_PKT);
+
+	*bpp = pullupblock(*bpp, hdrlen+TCP4_PKT);
+	if(*bpp == nil)
+		return -1;
+
+	optr = h->tcpopt;
+	n = hdrlen - TCP4_HDRSIZE;
+	while(n > 0 && *optr != EOLOPT) {
+		if(*optr == NOOPOPT) {
+			n--;
+			optr++;
+			continue;
+		}
+		optlen = optr[1];
+		if(optlen < 2 || optlen > n)
+			break;
+		switch(*optr) {
+		case MSSOPT:
+			if(optlen == MSS_LENGTH)
+				tcph->mss = nhgets(optr+2);
+			break;
+		case WSOPT:
+			if(optlen == WS_LENGTH && *(optr+2) <= 14)
+				tcph->ws = HaveWS | *(optr+2);
+			break;
+		}
+		n -= optlen;
+		optr += optlen;
+	}
+	return hdrlen;
+}
+
+/*
+ *  For outgiing calls, generate an initial sequence
+ *  number and put a SYN on the send queue
+ */
+void
+tcpsndsyn(Conv *s, Tcpctl *tcb)
+{
+	tcb->iss = (nrand(1<<16)<<16)|nrand(1<<16);
+	tcb->rttseq = tcb->iss;
+	tcb->snd.wl2 = tcb->iss;
+	tcb->snd.una = tcb->iss;
+	tcb->snd.ptr = tcb->rttseq;
+	tcb->snd.nxt = tcb->rttseq;
+	tcb->flgcnt++;
+	tcb->flags |= FORCE;
+	tcb->sndsyntime = NOW;
+
+	/* set desired mss and scale */
+	tcb->mss = tcpmtu(s->p, s->laddr, s->ipversion, &tcb->scale);
+}
+
+void
+sndrst(Proto *tcp, uchar *source, uchar *dest, ushort length, Tcp *seg, uchar version, char *reason)
+{
+	Block *hbp;
+	uchar rflags;
+	Tcppriv *tpriv;
+	Tcp4hdr ph4;
+	Tcp6hdr ph6;
+
+	netlog(tcp->f, Logtcp, "sndrst: %s", reason);
+
+	tpriv = tcp->priv;
+
+	if(seg->flags & RST)
+		return;
+
+	/* make pseudo header */
+	switch(version) {
+	case V4:
+		memset(&ph4, 0, sizeof(ph4));
+		ph4.vihl = IP_VER4;
+		v6tov4(ph4.tcpsrc, dest);
+		v6tov4(ph4.tcpdst, source);
+		ph4.proto = IP_TCPPROTO;
+		hnputs(ph4.tcplen, TCP4_HDRSIZE);
+		hnputs(ph4.tcpsport, seg->dest);
+		hnputs(ph4.tcpdport, seg->source);
+		break;
+	case V6:
+		memset(&ph6, 0, sizeof(ph6));
+		ph6.vcf[0] = IP_VER6;
+		ipmove(ph6.tcpsrc, dest);
+		ipmove(ph6.tcpdst, source);
+		ph6.proto = IP_TCPPROTO;
+		hnputs(ph6.ploadlen, TCP6_HDRSIZE);
+		hnputs(ph6.tcpsport, seg->dest);
+		hnputs(ph6.tcpdport, seg->source);
+		break;
+	default:
+		panic("sndrst: version %d", version);
+	}
+
+	tpriv->stats[OutRsts]++;
+	rflags = RST;
+
+	/* convince the other end that this reset is in band */
+	if(seg->flags & ACK) {
+		seg->seq = seg->ack;
+		seg->ack = 0;
+	}
+	else {
+		rflags |= ACK;
+		seg->ack = seg->seq;
+		seg->seq = 0;
+		if(seg->flags & SYN)
+			seg->ack++;
+		seg->ack += length;
+		if(seg->flags & FIN)
+			seg->ack++;
+	}
+	seg->flags = rflags;
+	seg->wnd = 0;
+	seg->urg = 0;
+	seg->mss = 0;
+	seg->ws = 0;
+	switch(version) {
+	case V4:
+		hbp = htontcp4(seg, nil, &ph4, nil);
+		if(hbp == nil)
+			return;
+		ipoput4(tcp->f, hbp, 0, MAXTTL, DFLTTOS, nil);
+		break;
+	case V6:
+		hbp = htontcp6(seg, nil, &ph6, nil);
+		if(hbp == nil)
+			return;
+		ipoput6(tcp->f, hbp, 0, MAXTTL, DFLTTOS, nil);
+		break;
+	default:
+		panic("sndrst2: version %d", version);
+	}
+}
+
+/*
+ *  send a reset to the remote side and close the conversation
+ *  called with s qlocked
+ */
+char*
+tcphangup(Conv *s)
+{
+	Tcp seg;
+	Tcpctl *tcb;
+	Block *hbp;
+
+	tcb = (Tcpctl*)s->ptcl;
+	if(waserror())
+		return commonerror();
+	if(ipcmp(s->raddr, IPnoaddr) != 0) {
+		if(!waserror()){
+			memset(&seg, 0, sizeof seg);
+			seg.flags = RST | ACK;
+			seg.ack = tcb->rcv.nxt;
+			tcb->rcv.una = 0;
+			seg.seq = tcb->snd.ptr;
+			seg.wnd = 0;
+			seg.urg = 0;
+			seg.mss = 0;
+			seg.ws = 0;
+			switch(s->ipversion) {
+			case V4:
+				tcb->protohdr.tcp4hdr.vihl = IP_VER4;
+				hbp = htontcp4(&seg, nil, &tcb->protohdr.tcp4hdr, tcb);
+				ipoput4(s->p->f, hbp, 0, s->ttl, s->tos, s);
+				break;
+			case V6:
+				tcb->protohdr.tcp6hdr.vcf[0] = IP_VER6;
+				hbp = htontcp6(&seg, nil, &tcb->protohdr.tcp6hdr, tcb);
+				ipoput6(s->p->f, hbp, 0, s->ttl, s->tos, s);
+				break;
+			default:
+				panic("tcphangup: version %d", s->ipversion);
+			}
+			poperror();
+		}
+	}
+	localclose(s, nil);
+	poperror();
+	return nil;
+}
+
+/*
+ *  (re)send a SYN ACK
+ */
+int
+sndsynack(Proto *tcp, Limbo *lp)
+{
+	Block *hbp;
+	Tcp4hdr ph4;
+	Tcp6hdr ph6;
+	Tcp seg;
+	int scale;
+
+	/* make pseudo header */
+	switch(lp->version) {
+	case V4:
+		memset(&ph4, 0, sizeof(ph4));
+		ph4.vihl = IP_VER4;
+		v6tov4(ph4.tcpsrc, lp->laddr);
+		v6tov4(ph4.tcpdst, lp->raddr);
+		ph4.proto = IP_TCPPROTO;
+		hnputs(ph4.tcplen, TCP4_HDRSIZE);
+		hnputs(ph4.tcpsport, lp->lport);
+		hnputs(ph4.tcpdport, lp->rport);
+		break;
+	case V6:
+		memset(&ph6, 0, sizeof(ph6));
+		ph6.vcf[0] = IP_VER6;
+		ipmove(ph6.tcpsrc, lp->laddr);
+		ipmove(ph6.tcpdst, lp->raddr);
+		ph6.proto = IP_TCPPROTO;
+		hnputs(ph6.ploadlen, TCP6_HDRSIZE);
+		hnputs(ph6.tcpsport, lp->lport);
+		hnputs(ph6.tcpdport, lp->rport);
+		break;
+	default:
+		panic("sndrst: version %d", lp->version);
+	}
+
+	seg.seq = lp->iss;
+	seg.ack = lp->irs+1;
+	seg.flags = SYN|ACK;
+	seg.urg = 0;
+	seg.mss = tcpmtu(tcp, lp->laddr, lp->version, &scale);
+	seg.wnd = QMAX;
+
+	/* if the other side set scale, we should too */
+	if(lp->rcvscale){
+		seg.ws = scale;
+		lp->sndscale = scale;
+	} else {
+		seg.ws = 0;
+		lp->sndscale = 0;
+	}
+
+	switch(lp->version) {
+	case V4:
+		hbp = htontcp4(&seg, nil, &ph4, nil);
+		if(hbp == nil)
+			return -1;
+		ipoput4(tcp->f, hbp, 0, MAXTTL, DFLTTOS, nil);
+		break;
+	case V6:
+		hbp = htontcp6(&seg, nil, &ph6, nil);
+		if(hbp == nil)
+			return -1;
+		ipoput6(tcp->f, hbp, 0, MAXTTL, DFLTTOS, nil);
+		break;
+	default:
+		panic("sndsnack: version %d", lp->version);
+	}
+	lp->lastsend = NOW;
+	return 0;
+}
+
+#define hashipa(a, p) ( ( (a)[IPaddrlen-2] + (a)[IPaddrlen-1] + p )&LHTMASK )
+
+/*
+ *  put a call into limbo and respond with a SYN ACK
+ *
+ *  called with proto locked
+ */
+static void
+limbo(Conv *s, uchar *source, uchar *dest, Tcp *seg, int version)
+{
+	Limbo *lp, **l;
+	Tcppriv *tpriv;
+	int h;
+
+	tpriv = s->p->priv;
+	h = hashipa(source, seg->source);
+
+	for(l = &tpriv->lht[h]; *l != nil; l = &lp->next){
+		lp = *l;
+		if(lp->lport != seg->dest || lp->rport != seg->source || lp->version != version)
+			continue;
+		if(ipcmp(lp->raddr, source) != 0)
+			continue;
+		if(ipcmp(lp->laddr, dest) != 0)
+			continue;
+
+		/* each new SYN restarts the retransmits */
+		lp->irs = seg->seq;
+		break;
+	}
+	lp = *l;
+	if(lp == nil){
+		if(tpriv->nlimbo >= Maxlimbo && tpriv->lht[h]){
+			lp = tpriv->lht[h];
+			tpriv->lht[h] = lp->next;
+			lp->next = nil;
+		} else {
+			lp = malloc(sizeof(*lp));
+			if(lp == nil)
+				return;
+			tpriv->nlimbo++;
+		}
+		*l = lp;
+		lp->version = version;
+		ipmove(lp->laddr, dest);
+		ipmove(lp->raddr, source);
+		lp->lport = seg->dest;
+		lp->rport = seg->source;
+		lp->mss = seg->mss;
+		lp->rcvscale = seg->ws;
+		lp->irs = seg->seq;
+		lp->iss = (nrand(1<<16)<<16)|nrand(1<<16);
+	}
+
+	if(sndsynack(s->p, lp) < 0){
+		*l = lp->next;
+		tpriv->nlimbo--;
+		free(lp);
+	}
+}
+
+/*
+ *  resend SYN ACK's once every SYNACK_RXTIMER ms.
+ */
+static void
+limborexmit(Proto *tcp)
+{
+	Tcppriv *tpriv;
+	Limbo **l, *lp;
+	int h;
+	int seen;
+	ulong now;
+
+	tpriv = tcp->priv;
+
+	if(!canqlock(tcp))
+		return;
+	seen = 0;
+	now = NOW;
+	for(h = 0; h < NLHT && seen < tpriv->nlimbo; h++){
+		for(l = &tpriv->lht[h]; *l != nil && seen < tpriv->nlimbo; ){
+			lp = *l;
+			seen++;
+			if(now - lp->lastsend < (lp->rexmits+1)*SYNACK_RXTIMER)
+				continue;
+
+			/* time it out after 1 second */
+			if(++(lp->rexmits) > 5){
+				tpriv->nlimbo--;
+				*l = lp->next;
+				free(lp);
+				continue;
+			}
+
+			/* if we're being attacked, don't bother resending SYN ACK's */
+			if(tpriv->nlimbo > 100)
+				continue;
+
+			if(sndsynack(tcp, lp) < 0){
+				tpriv->nlimbo--;
+				*l = lp->next;
+				free(lp);
+				continue;
+			}
+
+			l = &lp->next;
+		}
+	}
+	qunlock(tcp);
+}
+
+/*
+ *  lookup call in limbo.  if found, throw it out.
+ *
+ *  called with proto locked
+ */
+static void
+limborst(Conv *s, Tcp *segp, uchar *src, uchar *dst, uchar version)
+{
+	Limbo *lp, **l;
+	int h;
+	Tcppriv *tpriv;
+
+	tpriv = s->p->priv;
+
+	/* find a call in limbo */
+	h = hashipa(src, segp->source);
+	for(l = &tpriv->lht[h]; *l != nil; l = &lp->next){
+		lp = *l;
+		if(lp->lport != segp->dest || lp->rport != segp->source || lp->version != version)
+			continue;
+		if(ipcmp(lp->laddr, dst) != 0)
+			continue;
+		if(ipcmp(lp->raddr, src) != 0)
+			continue;
+
+		/* RST can only follow the SYN */
+		if(segp->seq == lp->irs+1){
+			tpriv->nlimbo--;
+			*l = lp->next;
+			free(lp);
+		}
+		break;
+	}
+}
+
+/*
+ *  come here when we finally get an ACK to our SYN-ACK.
+ *  lookup call in limbo.  if found, create a new conversation
+ *
+ *  called with proto locked
+ */
+static Conv*
+tcpincoming(Conv *s, Tcp *segp, uchar *src, uchar *dst, uchar version)
+{
+	Conv *new;
+	Tcpctl *tcb;
+	Tcppriv *tpriv;
+	Tcp4hdr *h4;
+	Tcp6hdr *h6;
+	Limbo *lp, **l;
+	int h;
+
+	/* unless it's just an ack, it can't be someone coming out of limbo */
+	if((segp->flags & SYN) || (segp->flags & ACK) == 0)
+		return nil;
+
+	tpriv = s->p->priv;
+
+	/* find a call in limbo */
+	h = hashipa(src, segp->source);
+	for(l = &tpriv->lht[h]; (lp = *l) != nil; l = &lp->next){
+		netlog(s->p->f, Logtcp, "tcpincoming s %I,%ux/%I,%ux d %I,%ux/%I,%ux v %d/%d",
+			src, segp->source, lp->raddr, lp->rport,
+			dst, segp->dest, lp->laddr, lp->lport,
+			version, lp->version
+ 		);
+
+		if(lp->lport != segp->dest || lp->rport != segp->source || lp->version != version)
+			continue;
+		if(ipcmp(lp->laddr, dst) != 0)
+			continue;
+		if(ipcmp(lp->raddr, src) != 0)
+			continue;
+
+		/* we're assuming no data with the initial SYN */
+		if(segp->seq != lp->irs+1 || segp->ack != lp->iss+1){
+			netlog(s->p->f, Logtcp, "tcpincoming s %lux/%lux a %lux %lux",
+				segp->seq, lp->irs+1, segp->ack, lp->iss+1);
+			lp = nil;
+		} else {
+			tpriv->nlimbo--;
+			*l = lp->next;
+		}
+		break;
+	}
+	if(lp == nil)
+		return nil;
+
+	new = Fsnewcall(s, src, segp->source, dst, segp->dest, version);
+	if(new == nil)
+		return nil;
+
+	memmove(new->ptcl, s->ptcl, sizeof(Tcpctl));
+	tcb = (Tcpctl*)new->ptcl;
+	tcb->flags &= ~CLONE;
+	tcb->timer.arg = new;
+	tcb->timer.state = TcptimerOFF;
+	tcb->acktimer.arg = new;
+	tcb->acktimer.state = TcptimerOFF;
+	tcb->katimer.arg = new;
+	tcb->katimer.state = TcptimerOFF;
+	tcb->rtt_timer.arg = new;
+	tcb->rtt_timer.state = TcptimerOFF;
+
+	tcb->irs = lp->irs;
+	tcb->rcv.nxt = tcb->irs+1;
+	tcb->rcv.urg = tcb->rcv.nxt;
+
+	tcb->iss = lp->iss;
+	tcb->rttseq = tcb->iss;
+	tcb->snd.wl2 = tcb->iss;
+	tcb->snd.una = tcb->iss+1;
+	tcb->snd.ptr = tcb->iss+1;
+	tcb->snd.nxt = tcb->iss+1;
+	tcb->flgcnt = 0;
+	tcb->flags |= SYNACK;
+
+	/* our sending max segment size cannot be bigger than what he asked for */
+	if(lp->mss != 0 && lp->mss < tcb->mss)
+		tcb->mss = lp->mss;
+
+	/* window scaling */
+	tcpsetscale(new, tcb, lp->rcvscale, lp->sndscale);
+
+	/* the congestion window always starts out as a single segment */
+	tcb->snd.wnd = segp->wnd;
+	tcb->cwind = tcb->mss;
+
+	/* set initial round trip time */
+	tcb->sndsyntime = lp->lastsend+lp->rexmits*SYNACK_RXTIMER;
+	tcpsynackrtt(new);
+
+	free(lp);
+
+	/* set up proto header */
+	switch(version){
+	case V4:
+		h4 = &tcb->protohdr.tcp4hdr;
+		memset(h4, 0, sizeof(*h4));
+		h4->proto = IP_TCPPROTO;
+		hnputs(h4->tcpsport, new->lport);
+		hnputs(h4->tcpdport, new->rport);
+		v6tov4(h4->tcpsrc, dst);
+		v6tov4(h4->tcpdst, src);
+		break;
+	case V6:
+		h6 = &tcb->protohdr.tcp6hdr;
+		memset(h6, 0, sizeof(*h6));
+		h6->proto = IP_TCPPROTO;
+		hnputs(h6->tcpsport, new->lport);
+		hnputs(h6->tcpdport, new->rport);
+		ipmove(h6->tcpsrc, dst);
+		ipmove(h6->tcpdst, src);
+		break;
+	default:
+		panic("tcpincoming: version %d", new->ipversion);
+	}
+
+	tcpsetstate(new, Established);
+
+	iphtadd(&tpriv->ht, new);
+
+	return new;
+}
+
+int
+seq_within(ulong x, ulong low, ulong high)
+{
+	if(low <= high){
+		if(low <= x && x <= high)
+			return 1;
+	}
+	else {
+		if(x >= low || x <= high)
+			return 1;
+	}
+	return 0;
+}
+
+int
+seq_lt(ulong x, ulong y)
+{
+	return (int)(x-y) < 0;
+}
+
+int
+seq_le(ulong x, ulong y)
+{
+	return (int)(x-y) <= 0;
+}
+
+int
+seq_gt(ulong x, ulong y)
+{
+	return (int)(x-y) > 0;
+}
+
+int
+seq_ge(ulong x, ulong y)
+{
+	return (int)(x-y) >= 0;
+}
+
+/*
+ *  use the time between the first SYN and it's ack as the
+ *  initial round trip time
+ */
+void
+tcpsynackrtt(Conv *s)
+{
+	Tcpctl *tcb;
+	int delta;
+	Tcppriv *tpriv;
+
+	tcb = (Tcpctl*)s->ptcl;
+	tpriv = s->p->priv;
+
+	delta = NOW - tcb->sndsyntime;
+	tcb->srtt = delta<<LOGAGAIN;
+	tcb->mdev = delta<<LOGDGAIN;
+
+	/* halt round trip timer */
+	tcphalt(tpriv, &tcb->rtt_timer);
+}
+
+void
+update(Conv *s, Tcp *seg)
+{
+	int rtt, delta;
+	Tcpctl *tcb;
+	ulong acked;
+	ulong expand;
+	Tcppriv *tpriv;
+
+	tpriv = s->p->priv;
+	tcb = (Tcpctl*)s->ptcl;
+
+	/* if everything has been acked, force output(?) */
+	if(seq_gt(seg->ack, tcb->snd.nxt)) {
+		tcb->flags |= FORCE;
+		return;
+	}
+
+	/* added by Dong Lin for fast retransmission */
+	if(seg->ack == tcb->snd.una
+	&& tcb->snd.una != tcb->snd.nxt
+	&& seg->len == 0
+	&& seg->wnd == tcb->snd.wnd) {
+
+		/* this is a pure ack w/o window update */
+		netlog(s->p->f, Logtcprxmt, "dupack %lud ack %lud sndwnd %d advwin %d\n",
+			tcb->snd.dupacks, seg->ack, tcb->snd.wnd, seg->wnd);
+
+		if(++tcb->snd.dupacks == TCPREXMTTHRESH) {
+			/*
+			 *  tahoe tcp rxt the packet, half sshthresh,
+ 			 *  and set cwnd to one packet
+			 */
+			tcb->snd.recovery = 1;
+			tcb->snd.rxt = tcb->snd.nxt;
+			netlog(s->p->f, Logtcprxmt, "fast rxt %lud, nxt %lud\n", tcb->snd.una, tcb->snd.nxt);
+			tcprxmit(s);
+		} else {
+			/* do reno tcp here. */
+		}
+	}
+
+	/*
+	 *  update window
+	 */
+	if(seq_gt(seg->ack, tcb->snd.wl2)
+	|| (tcb->snd.wl2 == seg->ack && seg->wnd > tcb->snd.wnd)){
+		tcb->snd.wnd = seg->wnd;
+		tcb->snd.wl2 = seg->ack;
+	}
+
+	if(!seq_gt(seg->ack, tcb->snd.una)){
+		/*
+		 *  don't let us hangup if sending into a closed window and
+		 *  we're still getting acks
+		 */
+		if((tcb->flags&RETRAN) && tcb->snd.wnd == 0){
+			tcb->backedoff = MAXBACKMS/4;
+		}
+		return;
+	}
+
+	/*
+	 *  any positive ack turns off fast rxt,
+	 *  (should we do new-reno on partial acks?)
+	 */
+	if(!tcb->snd.recovery || seq_ge(seg->ack, tcb->snd.rxt)) {
+		tcb->snd.dupacks = 0;
+		tcb->snd.recovery = 0;
+	} else
+		netlog(s->p->f, Logtcp, "rxt next %lud, cwin %ud\n", seg->ack, tcb->cwind);
+
+	/* Compute the new send window size */
+	acked = seg->ack - tcb->snd.una;
+
+	/* avoid slow start and timers for SYN acks */
+	if((tcb->flags & SYNACK) == 0) {
+		tcb->flags |= SYNACK;
+		acked--;
+		tcb->flgcnt--;
+		goto done;
+	}
+
+	/* slow start as long as we're not recovering from lost packets */
+	if(tcb->cwind < tcb->snd.wnd && !tcb->snd.recovery) {
+		if(tcb->cwind < tcb->ssthresh) {
+			expand = tcb->mss;
+			if(acked < expand)
+				expand = acked;
+		}
+		else
+			expand = ((int)tcb->mss * tcb->mss) / tcb->cwind;
+
+		if(tcb->cwind + expand < tcb->cwind)
+			expand = tcb->snd.wnd - tcb->cwind;
+		if(tcb->cwind + expand > tcb->snd.wnd)
+			expand = tcb->snd.wnd - tcb->cwind;
+		tcb->cwind += expand;
+	}
+
+	/* Adjust the timers according to the round trip time */
+	if(tcb->rtt_timer.state == TcptimerON && seq_ge(seg->ack, tcb->rttseq)) {
+		tcphalt(tpriv, &tcb->rtt_timer);
+		if((tcb->flags&RETRAN) == 0) {
+			tcb->backoff = 0;
+			tcb->backedoff = 0;
+			rtt = tcb->rtt_timer.start - tcb->rtt_timer.count;
+			if(rtt == 0)
+				rtt = 1;	/* otherwise all close systems will rexmit in 0 time */
+			rtt *= MSPTICK;
+			if(tcb->srtt == 0) {
+				tcb->srtt = rtt << LOGAGAIN;
+				tcb->mdev = rtt << LOGDGAIN;
+			} else {
+				delta = rtt - (tcb->srtt>>LOGAGAIN);
+				tcb->srtt += delta;
+				if(tcb->srtt <= 0)
+					tcb->srtt = 1;
+
+				delta = abs(delta) - (tcb->mdev>>LOGDGAIN);
+				tcb->mdev += delta;
+				if(tcb->mdev <= 0)
+					tcb->mdev = 1;
+			}
+			tcpsettimer(tcb);
+		}
+	}
+
+done:
+	if(qdiscard(s->wq, acked) < acked)
+		tcb->flgcnt--;
+
+	tcb->snd.una = seg->ack;
+	if(seq_gt(seg->ack, tcb->snd.urg))
+		tcb->snd.urg = seg->ack;
+
+	if(tcb->snd.una != tcb->snd.nxt)
+		tcpgo(tpriv, &tcb->timer);
+	else
+		tcphalt(tpriv, &tcb->timer);
+
+	if(seq_lt(tcb->snd.ptr, tcb->snd.una))
+		tcb->snd.ptr = tcb->snd.una;
+
+	tcb->flags &= ~RETRAN;
+	tcb->backoff = 0;
+	tcb->backedoff = 0;
+}
+
+void
+tcpiput(Proto *tcp, Ipifc*, Block *bp)
+{
+	Tcp seg;
+	Tcp4hdr *h4;
+	Tcp6hdr *h6;
+	int hdrlen;
+	Tcpctl *tcb;
+	ushort length;
+	uchar source[IPaddrlen], dest[IPaddrlen];
+	Conv *s;
+	Fs *f;
+	Tcppriv *tpriv;
+	uchar version;
+
+	f = tcp->f;
+	tpriv = tcp->priv;
+
+	tpriv->stats[InSegs]++;
+
+	h4 = (Tcp4hdr*)(bp->rp);
+	h6 = (Tcp6hdr*)(bp->rp);
+
+	if((h4->vihl&0xF0)==IP_VER4) {
+		version = V4;
+		length = nhgets(h4->length);
+		v4tov6(dest, h4->tcpdst);
+		v4tov6(source, h4->tcpsrc);
+
+		h4->Unused = 0;
+		hnputs(h4->tcplen, length-TCP4_PKT);
+		if(!(bp->flag & Btcpck) && (h4->tcpcksum[0] || h4->tcpcksum[1]) &&
+			ptclcsum(bp, TCP4_IPLEN, length-TCP4_IPLEN)) {
+			tpriv->stats[CsumErrs]++;
+			tpriv->stats[InErrs]++;
+			netlog(f, Logtcp, "bad tcp proto cksum\n");
+			freeblist(bp);
+			return;
+		}
+
+		hdrlen = ntohtcp4(&seg, &bp);
+		if(hdrlen < 0){
+			tpriv->stats[HlenErrs]++;
+			tpriv->stats[InErrs]++;
+			netlog(f, Logtcp, "bad tcp hdr len\n");
+			return;
+		}
+
+		/* trim the packet to the size claimed by the datagram */
+		length -= hdrlen+TCP4_PKT;
+		bp = trimblock(bp, hdrlen+TCP4_PKT, length);
+		if(bp == nil){
+			tpriv->stats[LenErrs]++;
+			tpriv->stats[InErrs]++;
+			netlog(f, Logtcp, "tcp len < 0 after trim\n");
+			return;
+		}
+	}
+	else {
+		int ttl = h6->ttl;
+		int proto = h6->proto;
+
+		version = V6;
+		length = nhgets(h6->ploadlen);
+		ipmove(dest, h6->tcpdst);
+		ipmove(source, h6->tcpsrc);
+
+		h6->ploadlen[0] = h6->ploadlen[1] = h6->proto = 0;
+		h6->ttl = proto;
+		hnputl(h6->vcf, length);
+		if((h6->tcpcksum[0] || h6->tcpcksum[1]) &&
+			ptclcsum(bp, TCP6_IPLEN, length+TCP6_PHDRSIZE)) {
+			tpriv->stats[CsumErrs]++;
+			tpriv->stats[InErrs]++;
+			netlog(f, Logtcp, "bad tcp proto cksum\n");
+			freeblist(bp);
+			return;
+		}
+		h6->ttl = ttl;
+		h6->proto = proto;
+		hnputs(h6->ploadlen, length);
+
+		hdrlen = ntohtcp6(&seg, &bp);
+		if(hdrlen < 0){
+			tpriv->stats[HlenErrs]++;
+			tpriv->stats[InErrs]++;
+			netlog(f, Logtcp, "bad tcp hdr len\n");
+			return;
+		}
+
+		/* trim the packet to the size claimed by the datagram */
+		length -= hdrlen;
+		bp = trimblock(bp, hdrlen+TCP6_PKT, length);
+		if(bp == nil){
+			tpriv->stats[LenErrs]++;
+			tpriv->stats[InErrs]++;
+			netlog(f, Logtcp, "tcp len < 0 after trim\n");
+			return;
+		}
+	}
+
+	/* lock protocol while searching for a conversation */
+	qlock(tcp);
+
+	/* Look for a matching conversation */
+	s = iphtlook(&tpriv->ht, source, seg.source, dest, seg.dest);
+	if(s == nil){
+		netlog(f, Logtcp, "iphtlook failed");
+reset:
+		qunlock(tcp);
+		sndrst(tcp, source, dest, length, &seg, version, "no conversation");
+		freeblist(bp);
+		return;
+	}
+
+	/* if it's a listener, look for the right flags and get a new conv */
+	tcb = (Tcpctl*)s->ptcl;
+	if(tcb->state == Listen){
+		if(seg.flags & RST){
+			limborst(s, &seg, source, dest, version);
+			qunlock(tcp);
+			freeblist(bp);
+			return;
+		}
+
+		/* if this is a new SYN, put the call into limbo */
+		if((seg.flags & SYN) && (seg.flags & ACK) == 0){
+			limbo(s, source, dest, &seg, version);
+			qunlock(tcp);
+			freeblist(bp);
+			return;
+		}
+
+		/*
+		 *  if there's a matching call in limbo, tcpincoming will
+		 *  return it in state Syn_received
+		 */
+		s = tcpincoming(s, &seg, source, dest, version);
+		if(s == nil)
+			goto reset;
+	}
+
+	/* The rest of the input state machine is run with the control block
+	 * locked and implements the state machine directly out of the RFC.
+	 * Out-of-band data is ignored - it was always a bad idea.
+	 */
+	tcb = (Tcpctl*)s->ptcl;
+	if(waserror()){
+		qunlock(s);
+		nexterror();
+	}
+	qlock(s);
+	qunlock(tcp);
+
+	/* fix up window */
+	seg.wnd <<= tcb->rcv.scale;
+
+	/* every input packet in puts off the keep alive time out */
+	tcpsetkacounter(tcb);
+
+	switch(tcb->state) {
+	case Closed:
+		sndrst(tcp, source, dest, length, &seg, version, "sending to Closed");
+		goto raise;
+	case Syn_sent:
+		if(seg.flags & ACK) {
+			if(!seq_within(seg.ack, tcb->iss+1, tcb->snd.nxt)) {
+				sndrst(tcp, source, dest, length, &seg, version,
+					 "bad seq in Syn_sent");
+				goto raise;
+			}
+		}
+		if(seg.flags & RST) {
+			if(seg.flags & ACK)
+				localclose(s, Econrefused);
+			goto raise;
+		}
+
+		if(seg.flags & SYN) {
+			procsyn(s, &seg);
+			if(seg.flags & ACK){
+				update(s, &seg);
+				tcpsynackrtt(s);
+				tcpsetstate(s, Established);
+				tcpsetscale(s, tcb, seg.ws, tcb->scale);
+			}
+			else {
+				tcb->time = NOW;
+				tcpsetstate(s, Syn_received);	/* DLP - shouldn't this be a reset? */
+			}
+
+			if(length != 0 || (seg.flags & FIN))
+				break;
+
+			freeblist(bp);
+			goto output;
+		}
+		else
+			freeblist(bp);
+
+		qunlock(s);
+		poperror();
+		return;
+	case Syn_received:
+		/* doesn't matter if it's the correct ack, we're just trying to set timing */
+		if(seg.flags & ACK)
+			tcpsynackrtt(s);
+		break;
+	}
+
+	/*
+	 *  One DOS attack is to open connections to us and then forget about them,
+	 *  thereby tying up a conv at no long term cost to the attacker.
+	 *  This is an attempt to defeat these stateless DOS attacks.  See
+	 *  corresponding code in tcpsendka().
+	 */
+	if(tcb->state != Syn_received && (seg.flags & RST) == 0){
+		if(tcpporthogdefense
+		&& seq_within(seg.ack, tcb->snd.una-(1<<31), tcb->snd.una-(1<<29))){
+			print("stateless hog %I.%d->%I.%d f %ux %lux - %lux - %lux\n",
+				source, seg.source, dest, seg.dest, seg.flags,
+				tcb->snd.una-(1<<31), seg.ack, tcb->snd.una-(1<<29));
+			localclose(s, "stateless hog");
+		}
+	}
+
+	/* Cut the data to fit the receive window */
+	if(tcptrim(tcb, &seg, &bp, &length) == -1) {
+		netlog(f, Logtcp, "tcp len < 0, %lud %d\n", seg.seq, length);
+		update(s, &seg);
+		if(qlen(s->wq)+tcb->flgcnt == 0 && tcb->state == Closing) {
+			tcphalt(tpriv, &tcb->rtt_timer);
+			tcphalt(tpriv, &tcb->acktimer);
+			tcphalt(tpriv, &tcb->katimer);
+			tcpsetstate(s, Time_wait);
+			tcb->timer.start = MSL2*(1000 / MSPTICK);
+			tcpgo(tpriv, &tcb->timer);
+		}
+		if(!(seg.flags & RST)) {
+			tcb->flags |= FORCE;
+			goto output;
+		}
+		qunlock(s);
+		poperror();
+		return;
+	}
+
+	/* Cannot accept so answer with a rst */
+	if(length && tcb->state == Closed) {
+		sndrst(tcp, source, dest, length, &seg, version, "sending to Closed");
+		goto raise;
+	}
+
+	/* The segment is beyond the current receive pointer so
+	 * queue the data in the resequence queue
+	 */
+	if(seg.seq != tcb->rcv.nxt)
+	if(length != 0 || (seg.flags & (SYN|FIN))) {
+		update(s, &seg);
+		if(addreseq(tcb, tpriv, &seg, bp, length) < 0)
+			print("reseq %I.%d -> %I.%d\n", s->raddr, s->rport, s->laddr, s->lport);
+		tcb->flags |= FORCE;
+		goto output;
+	}
+
+	/*
+	 *  keep looping till we've processed this packet plus any
+	 *  adjacent packets in the resequence queue
+	 */
+	for(;;) {
+		if(seg.flags & RST) {
+			if(tcb->state == Established) {
+				tpriv->stats[EstabResets]++;
+				if(tcb->rcv.nxt != seg.seq)
+					print("out of order RST rcvd: %I.%d -> %I.%d, rcv.nxt %lux seq %lux\n", s->raddr, s->rport, s->laddr, s->lport, tcb->rcv.nxt, seg.seq);
+			}
+			localclose(s, Econrefused);
+			goto raise;
+		}
+
+		if((seg.flags&ACK) == 0)
+			goto raise;
+
+		switch(tcb->state) {
+		case Syn_received:
+			if(!seq_within(seg.ack, tcb->snd.una+1, tcb->snd.nxt)){
+				sndrst(tcp, source, dest, length, &seg, version,
+					"bad seq in Syn_received");
+				goto raise;
+			}
+			update(s, &seg);
+			tcpsetstate(s, Established);
+		case Established:
+		case Close_wait:
+			update(s, &seg);
+			break;
+		case Finwait1:
+			update(s, &seg);
+			if(qlen(s->wq)+tcb->flgcnt == 0){
+				tcphalt(tpriv, &tcb->rtt_timer);
+				tcphalt(tpriv, &tcb->acktimer);
+				tcpsetkacounter(tcb);
+				tcb->time = NOW;
+				tcpsetstate(s, Finwait2);
+				tcb->katimer.start = MSL2 * (1000 / MSPTICK);
+				tcpgo(tpriv, &tcb->katimer);
+			}
+			break;
+		case Finwait2:
+			update(s, &seg);
+			break;
+		case Closing:
+			update(s, &seg);
+			if(qlen(s->wq)+tcb->flgcnt == 0) {
+				tcphalt(tpriv, &tcb->rtt_timer);
+				tcphalt(tpriv, &tcb->acktimer);
+				tcphalt(tpriv, &tcb->katimer);
+				tcpsetstate(s, Time_wait);
+				tcb->timer.start = MSL2*(1000 / MSPTICK);
+				tcpgo(tpriv, &tcb->timer);
+			}
+			break;
+		case Last_ack:
+			update(s, &seg);
+			if(qlen(s->wq)+tcb->flgcnt == 0) {
+				localclose(s, nil);
+				goto raise;
+			}
+		case Time_wait:
+			tcb->flags |= FORCE;
+			if(tcb->timer.state != TcptimerON)
+				tcpgo(tpriv, &tcb->timer);
+		}
+
+		if((seg.flags&URG) && seg.urg) {
+			if(seq_gt(seg.urg + seg.seq, tcb->rcv.urg)) {
+				tcb->rcv.urg = seg.urg + seg.seq;
+				pullblock(&bp, seg.urg);
+			}
+		}
+		else
+		if(seq_gt(tcb->rcv.nxt, tcb->rcv.urg))
+			tcb->rcv.urg = tcb->rcv.nxt;
+
+		if(length == 0) {
+			if(bp != nil)
+				freeblist(bp);
+		}
+		else {
+			switch(tcb->state){
+			default:
+				/* Ignore segment text */
+				if(bp != nil)
+					freeblist(bp);
+				break;
+
+			case Syn_received:
+			case Established:
+			case Finwait1:
+				/* If we still have some data place on
+				 * receive queue
+				 */
+				if(bp) {
+					bp = packblock(bp);
+					if(bp == nil)
+						panic("tcp packblock");
+					qpassnolim(s->rq, bp);
+					bp = nil;
+
+					/*
+					 *  Force an ack every 2 data messages.  This is
+					 *  a hack for rob to make his home system run
+					 *  faster.
+					 *
+					 *  this also keeps the standard TCP congestion
+					 *  control working since it needs an ack every
+					 *  2 max segs worth.  This is not quite that,
+					 *  but under a real stream is equivalent since
+					 *  every packet has a max seg in it.
+					 */
+					if(++(tcb->rcv.una) >= 2)
+						tcb->flags |= FORCE;
+				}
+				tcb->rcv.nxt += length;
+
+				/*
+				 *  update our rcv window
+				 */
+				tcprcvwin(s);
+
+				/*
+				 *  turn on the acktimer if there's something
+				 *  to ack
+				 */
+				if(tcb->acktimer.state != TcptimerON)
+					tcpgo(tpriv, &tcb->acktimer);
+
+				break;
+			case Finwait2:
+				/* no process to read the data, send a reset */
+				if(bp != nil)
+					freeblist(bp);
+				sndrst(tcp, source, dest, length, &seg, version,
+					"send to Finwait2");
+				qunlock(s);
+				poperror();
+				return;
+			}
+		}
+
+		if(seg.flags & FIN) {
+			tcb->flags |= FORCE;
+
+			switch(tcb->state) {
+			case Syn_received:
+			case Established:
+				tcb->rcv.nxt++;
+				tcpsetstate(s, Close_wait);
+				break;
+			case Finwait1:
+				tcb->rcv.nxt++;
+				if(qlen(s->wq)+tcb->flgcnt == 0) {
+					tcphalt(tpriv, &tcb->rtt_timer);
+					tcphalt(tpriv, &tcb->acktimer);
+					tcphalt(tpriv, &tcb->katimer);
+					tcpsetstate(s, Time_wait);
+					tcb->timer.start = MSL2*(1000/MSPTICK);
+					tcpgo(tpriv, &tcb->timer);
+				}
+				else
+					tcpsetstate(s, Closing);
+				break;
+			case Finwait2:
+				tcb->rcv.nxt++;
+				tcphalt(tpriv, &tcb->rtt_timer);
+				tcphalt(tpriv, &tcb->acktimer);
+				tcphalt(tpriv, &tcb->katimer);
+				tcpsetstate(s, Time_wait);
+				tcb->timer.start = MSL2 * (1000/MSPTICK);
+				tcpgo(tpriv, &tcb->timer);
+				break;
+			case Close_wait:
+			case Closing:
+			case Last_ack:
+				break;
+			case Time_wait:
+				tcpgo(tpriv, &tcb->timer);
+				break;
+			}
+		}
+
+		/*
+		 *  get next adjacent segment from the resequence queue.
+		 *  dump/trim any overlapping segments
+		 */
+		for(;;) {
+			if(tcb->reseq == nil)
+				goto output;
+
+			if(seq_ge(tcb->rcv.nxt, tcb->reseq->seg.seq) == 0)
+				goto output;
+
+			getreseq(tcb, &seg, &bp, &length);
+
+			if(tcptrim(tcb, &seg, &bp, &length) == 0)
+				break;
+		}
+	}
+output:
+	tcpoutput(s);
+	qunlock(s);
+	poperror();
+	return;
+raise:
+	qunlock(s);
+	poperror();
+	freeblist(bp);
+	tcpkick(s);
+}
+
+/*
+ *  always enters and exits with the s locked.  We drop
+ *  the lock to ipoput the packet so some care has to be
+ *  taken by callers.
+ */
+void
+tcpoutput(Conv *s)
+{
+	Tcp seg;
+	int msgs;
+	Tcpctl *tcb;
+	Block *hbp, *bp;
+	int sndcnt, n;
+	ulong ssize, dsize, usable, sent;
+	Fs *f;
+	Tcppriv *tpriv;
+	uchar version;
+
+	f = s->p->f;
+	tpriv = s->p->priv;
+	version = s->ipversion;
+
+	for(msgs = 0; msgs < 100; msgs++) {
+		tcb = (Tcpctl*)s->ptcl;
+
+		switch(tcb->state) {
+		case Listen:
+		case Closed:
+		case Finwait2:
+			return;
+		}
+
+		/* force an ack when a window has opened up */
+		if(tcb->rcv.blocked && tcb->rcv.wnd > 0){
+			tcb->rcv.blocked = 0;
+			tcb->flags |= FORCE;
+		}
+
+		sndcnt = qlen(s->wq)+tcb->flgcnt;
+		sent = tcb->snd.ptr - tcb->snd.una;
+
+		/* Don't send anything else until our SYN has been acked */
+		if(tcb->snd.ptr != tcb->iss && (tcb->flags & SYNACK) == 0)
+			break;
+
+		/* Compute usable segment based on offered window and limit
+		 * window probes to one
+		 */
+		if(tcb->snd.wnd == 0){
+			if(sent != 0) {
+				if((tcb->flags&FORCE) == 0)
+					break;
+//				tcb->snd.ptr = tcb->snd.una;
+			}
+			usable = 1;
+		}
+		else {
+			usable = tcb->cwind;
+			if(tcb->snd.wnd < usable)
+				usable = tcb->snd.wnd;
+			usable -= sent;
+		}
+		ssize = sndcnt-sent;
+		if(ssize && usable < 2)
+			netlog(s->p->f, Logtcp, "throttled snd.wnd %lud cwind %lud\n",
+				tcb->snd.wnd, tcb->cwind);
+		if(usable < ssize)
+			ssize = usable;
+		if(tcb->mss < ssize)
+			ssize = tcb->mss;
+		dsize = ssize;
+		seg.urg = 0;
+
+		if(ssize == 0)
+		if((tcb->flags&FORCE) == 0)
+			break;
+
+		tcb->flags &= ~FORCE;
+		tcprcvwin(s);
+
+		/* By default we will generate an ack */
+		tcphalt(tpriv, &tcb->acktimer);
+		tcb->rcv.una = 0;
+		seg.source = s->lport;
+		seg.dest = s->rport;
+		seg.flags = ACK;
+		seg.mss = 0;
+		seg.ws = 0;
+		switch(tcb->state){
+		case Syn_sent:
+			seg.flags = 0;
+			if(tcb->snd.ptr == tcb->iss){
+				seg.flags |= SYN;
+				dsize--;
+				seg.mss = tcb->mss;
+				seg.ws = tcb->scale;
+			}
+			break;
+		case Syn_received:
+			/*
+			 *  don't send any data with a SYN/ACK packet
+			 *  because Linux rejects the packet in its
+			 *  attempt to solve the SYN attack problem
+			 */
+			if(tcb->snd.ptr == tcb->iss){
+				seg.flags |= SYN;
+				dsize = 0;
+				ssize = 1;
+				seg.mss = tcb->mss;
+				seg.ws = tcb->scale;
+			}
+			break;
+		}
+		seg.seq = tcb->snd.ptr;
+		seg.ack = tcb->rcv.nxt;
+		seg.wnd = tcb->rcv.wnd;
+
+		/* Pull out data to send */
+		bp = nil;
+		if(dsize != 0) {
+			bp = qcopy(s->wq, dsize, sent);
+			if(BLEN(bp) != dsize) {
+				seg.flags |= FIN;
+				dsize--;
+			}
+		}
+
+		if(sent+dsize == sndcnt)
+			seg.flags |= PSH;
+
+		/* keep track of balance of resent data */
+		if(seq_lt(tcb->snd.ptr, tcb->snd.nxt)) {
+			n = tcb->snd.nxt - tcb->snd.ptr;
+			if(ssize < n)
+				n = ssize;
+			tcb->resent += n;
+			netlog(f, Logtcp, "rexmit: %I.%d -> %I.%d ptr %lux nxt %lux\n",
+				s->raddr, s->rport, s->laddr, s->lport, tcb->snd.ptr, tcb->snd.nxt);
+			tpriv->stats[RetransSegs]++;
+		}
+
+		tcb->snd.ptr += ssize;
+
+		/* Pull up the send pointer so we can accept acks
+		 * for this window
+		 */
+		if(seq_gt(tcb->snd.ptr,tcb->snd.nxt))
+			tcb->snd.nxt = tcb->snd.ptr;
+
+		/* Build header, link data and compute cksum */
+		switch(version){
+		case V4:
+			tcb->protohdr.tcp4hdr.vihl = IP_VER4;
+			hbp = htontcp4(&seg, bp, &tcb->protohdr.tcp4hdr, tcb);
+			if(hbp == nil) {
+				freeblist(bp);
+				return;
+			}
+			break;
+		case V6:
+			tcb->protohdr.tcp6hdr.vcf[0] = IP_VER6;
+			hbp = htontcp6(&seg, bp, &tcb->protohdr.tcp6hdr, tcb);
+			if(hbp == nil) {
+				freeblist(bp);
+				return;
+			}
+			break;
+		default:
+			hbp = nil;	/* to suppress a warning */
+			panic("tcpoutput: version %d", version);
+		}
+
+		/* Start the transmission timers if there is new data and we
+		 * expect acknowledges
+		 */
+		if(ssize != 0){
+			if(tcb->timer.state != TcptimerON)
+				tcpgo(tpriv, &tcb->timer);
+
+			/*  If round trip timer isn't running, start it.
+			 *  measure the longest packet only in case the
+			 *  transmission time dominates RTT
+			 */
+			if(tcb->rtt_timer.state != TcptimerON)
+			if(ssize == tcb->mss) {
+				tcpgo(tpriv, &tcb->rtt_timer);
+				tcb->rttseq = tcb->snd.ptr;
+			}
+		}
+
+		tpriv->stats[OutSegs]++;
+
+		/* put off the next keep alive */
+		tcpgo(tpriv, &tcb->katimer);
+
+		switch(version){
+		case V4:
+			if(ipoput4(f, hbp, 0, s->ttl, s->tos, s) < 0){
+				/* a negative return means no route */
+				localclose(s, "no route");
+			}
+			break;
+		case V6:
+			if(ipoput6(f, hbp, 0, s->ttl, s->tos, s) < 0){
+				/* a negative return means no route */
+				localclose(s, "no route");
+			}
+			break;
+		default:
+			panic("tcpoutput2: version %d", version);
+		}
+		if((msgs%4) == 1){
+			qunlock(s);
+			sched();
+			qlock(s);
+		}
+	}
+}
+
+/*
+ *  the BSD convention (hack?) for keep alives.  resend last uchar acked.
+ */
+void
+tcpsendka(Conv *s)
+{
+	Tcp seg;
+	Tcpctl *tcb;
+	Block *hbp,*dbp;
+
+	tcb = (Tcpctl*)s->ptcl;
+
+	dbp = nil;
+	seg.urg = 0;
+	seg.source = s->lport;
+	seg.dest = s->rport;
+	seg.flags = ACK|PSH;
+	seg.mss = 0;
+	seg.ws = 0;
+	if(tcpporthogdefense)
+		seg.seq = tcb->snd.una-(1<<30)-nrand(1<<20);
+	else
+		seg.seq = tcb->snd.una-1;
+	seg.ack = tcb->rcv.nxt;
+	tcb->rcv.una = 0;
+	seg.wnd = tcb->rcv.wnd;
+	if(tcb->state == Finwait2){
+		seg.flags |= FIN;
+	} else {
+		dbp = allocb(1);
+		dbp->wp++;
+	}
+
+	if(isv4(s->raddr)) {
+		/* Build header, link data and compute cksum */
+		tcb->protohdr.tcp4hdr.vihl = IP_VER4;
+		hbp = htontcp4(&seg, dbp, &tcb->protohdr.tcp4hdr, tcb);
+		if(hbp == nil) {
+			freeblist(dbp);
+			return;
+		}
+		ipoput4(s->p->f, hbp, 0, s->ttl, s->tos, s);
+	}
+	else {
+		/* Build header, link data and compute cksum */
+		tcb->protohdr.tcp6hdr.vcf[0] = IP_VER6;
+		hbp = htontcp6(&seg, dbp, &tcb->protohdr.tcp6hdr, tcb);
+		if(hbp == nil) {
+			freeblist(dbp);
+			return;
+		}
+		ipoput6(s->p->f, hbp, 0, s->ttl, s->tos, s);
+	}
+}
+
+/*
+ *  set connection to time out after 12 minutes
+ */
+void
+tcpsetkacounter(Tcpctl *tcb)
+{
+	tcb->kacounter = (12 * 60 * 1000) / (tcb->katimer.start*MSPTICK);
+	if(tcb->kacounter < 3)
+		tcb->kacounter = 3;
+}
+
+/*
+ *  if we've timed out, close the connection
+ *  otherwise, send a keepalive and restart the timer
+ */
+void
+tcpkeepalive(void *v)
+{
+	Tcpctl *tcb;
+	Conv *s;
+
+	s = v;
+	tcb = (Tcpctl*)s->ptcl;
+	if(waserror()){
+		qunlock(s);
+		nexterror();
+	}
+	qlock(s);
+	if(tcb->state != Closed){
+		if(--(tcb->kacounter) <= 0) {
+			localclose(s, Etimedout);
+		} else {
+			tcpsendka(s);
+			tcpgo(s->p->priv, &tcb->katimer);
+		}
+	}
+	qunlock(s);
+	poperror();
+}
+
+/*
+ *  start keepalive timer
+ */
+char*
+tcpstartka(Conv *s, char **f, int n)
+{
+	Tcpctl *tcb;
+	int x;
+
+	tcb = (Tcpctl*)s->ptcl;
+	if(tcb->state != Established)
+		return "connection must be in Establised state";
+	if(n > 1){
+		x = atoi(f[1]);
+		if(x >= MSPTICK)
+			tcb->katimer.start = x/MSPTICK;
+	}
+	tcpsetkacounter(tcb);
+	tcpgo(s->p->priv, &tcb->katimer);
+
+	return nil;
+}
+
+/*
+ *  turn checksums on/off
+ */
+char*
+tcpsetchecksum(Conv *s, char **f, int)
+{
+	Tcpctl *tcb;
+
+	tcb = (Tcpctl*)s->ptcl;
+	tcb->nochecksum = !atoi(f[1]);
+
+	return nil;
+}
+
+void
+tcprxmit(Conv *s)
+{
+	Tcpctl *tcb;
+
+	tcb = (Tcpctl*)s->ptcl;
+
+	tcb->flags |= RETRAN|FORCE;
+	tcb->snd.ptr = tcb->snd.una;
+
+	/*
+	 *  We should be halving the slow start threshhold (down to one
+	 *  mss) but leaving it at mss seems to work well enough
+	 */
+ 	tcb->ssthresh = tcb->mss;
+
+	/*
+	 *  pull window down to a single packet
+	 */
+	tcb->cwind = tcb->mss;
+	tcpoutput(s);
+}
+
+void
+tcptimeout(void *arg)
+{
+	Conv *s;
+	Tcpctl *tcb;
+	int maxback;
+	Tcppriv *tpriv;
+
+	s = (Conv*)arg;
+	tpriv = s->p->priv;
+	tcb = (Tcpctl*)s->ptcl;
+
+	if(waserror()){
+		qunlock(s);
+		nexterror();
+	}
+	qlock(s);
+	switch(tcb->state){
+	default:
+		tcb->backoff++;
+		if(tcb->state == Syn_sent)
+			maxback = MAXBACKMS/2;
+		else
+			maxback = MAXBACKMS;
+		tcb->backedoff += tcb->timer.start * MSPTICK;
+		if(tcb->backedoff >= maxback) {
+			localclose(s, Etimedout);
+			break;
+		}
+		netlog(s->p->f, Logtcprxmt, "timeout rexmit 0x%lux %d/%d\n", tcb->snd.una, tcb->timer.start, NOW);
+		tcpsettimer(tcb);
+		tcprxmit(s);
+		tpriv->stats[RetransTimeouts]++;
+		tcb->snd.dupacks = 0;
+		break;
+	case Time_wait:
+		localclose(s, nil);
+		break;
+	case Closed:
+		break;
+	}
+	qunlock(s);
+	poperror();
+}
+
+int
+inwindow(Tcpctl *tcb, int seq)
+{
+	return seq_within(seq, tcb->rcv.nxt, tcb->rcv.nxt+tcb->rcv.wnd-1);
+}
+
+/*
+ *  set up state for a received SYN (or SYN ACK) packet
+ */
+void
+procsyn(Conv *s, Tcp *seg)
+{
+	Tcpctl *tcb;
+
+	tcb = (Tcpctl*)s->ptcl;
+	tcb->flags |= FORCE;
+
+	tcb->rcv.nxt = seg->seq + 1;
+	tcb->rcv.urg = tcb->rcv.nxt;
+	tcb->irs = seg->seq;
+
+	/* our sending max segment size cannot be bigger than what he asked for */
+	if(seg->mss != 0 && seg->mss < tcb->mss)
+		tcb->mss = seg->mss;
+
+	/* the congestion window always starts out as a single segment */
+	tcb->snd.wnd = seg->wnd;
+	tcb->cwind = tcb->mss;
+}
+
+int
+addreseq(Tcpctl *tcb, Tcppriv *tpriv, Tcp *seg, Block *bp, ushort length)
+{
+	Reseq *rp, *rp1;
+	int i, rqlen, qmax;
+
+	rp = malloc(sizeof(Reseq));
+	if(rp == nil){
+		freeblist(bp);	/* bp always consumed by add_reseq */
+		return 0;
+	}
+
+	rp->seg = *seg;
+	rp->bp = bp;
+	rp->length = length;
+
+	/* Place on reassembly list sorting by starting seq number */
+	rp1 = tcb->reseq;
+	if(rp1 == nil || seq_lt(seg->seq, rp1->seg.seq)) {
+		rp->next = rp1;
+		tcb->reseq = rp;
+		if(rp->next != nil)
+			tpriv->stats[OutOfOrder]++;
+		return 0;
+	}
+
+	rqlen = 0;
+	for(i = 0;; i++) {
+		rqlen += rp1->length;
+		if(rp1->next == nil || seq_lt(seg->seq, rp1->next->seg.seq)) {
+			rp->next = rp1->next;
+			rp1->next = rp;
+			if(rp->next != nil)
+				tpriv->stats[OutOfOrder]++;
+			break;
+		}
+		rp1 = rp1->next;
+	}
+	qmax = QMAX<<tcb->rcv.scale;
+	if(rqlen > qmax){
+		print("resequence queue > window: %d > %d\n", rqlen, qmax);
+		i = 0;
+	  	for(rp1 = tcb->reseq; rp1 != nil; rp1 = rp1->next){
+	  		print("%#lux %#lux %#ux\n", rp1->seg.seq,
+	  			rp1->seg.ack, rp1->seg.flags);
+			if(i++ > 10){
+				print("...\n");
+				break;
+			}
+		}
+
+		// delete entire reassembly queue; wait for retransmit.
+		// - should we be smarter and only delete the tail?
+		for(rp = tcb->reseq; rp != nil; rp = rp1){
+			rp1 = rp->next;
+			freeblist(rp->bp);
+			free(rp);
+		}
+		tcb->reseq = nil;
+
+	  	return -1;
+	}
+	return 0;
+}
+
+void
+getreseq(Tcpctl *tcb, Tcp *seg, Block **bp, ushort *length)
+{
+	Reseq *rp;
+
+	rp = tcb->reseq;
+	if(rp == nil)
+		return;
+
+	tcb->reseq = rp->next;
+
+	*seg = rp->seg;
+	*bp = rp->bp;
+	*length = rp->length;
+
+	free(rp);
+}
+
+int
+tcptrim(Tcpctl *tcb, Tcp *seg, Block **bp, ushort *length)
+{
+	ushort len;
+	uchar accept;
+	int dupcnt, excess;
+
+	accept = 0;
+	len = *length;
+	if(seg->flags & SYN)
+		len++;
+	if(seg->flags & FIN)
+		len++;
+
+	if(tcb->rcv.wnd == 0) {
+		if(len == 0 && seg->seq == tcb->rcv.nxt)
+			return 0;
+	}
+	else {
+		/* Some part of the segment should be in the window */
+		if(inwindow(tcb,seg->seq))
+			accept++;
+		else
+		if(len != 0) {
+			if(inwindow(tcb, seg->seq+len-1) ||
+			seq_within(tcb->rcv.nxt, seg->seq,seg->seq+len-1))
+				accept++;
+		}
+	}
+	if(!accept) {
+		freeblist(*bp);
+		return -1;
+	}
+	dupcnt = tcb->rcv.nxt - seg->seq;
+	if(dupcnt > 0){
+		tcb->rerecv += dupcnt;
+		if(seg->flags & SYN){
+			seg->flags &= ~SYN;
+			seg->seq++;
+
+			if(seg->urg > 1)
+				seg->urg--;
+			else
+				seg->flags &= ~URG;
+			dupcnt--;
+		}
+		if(dupcnt > 0){
+			pullblock(bp, (ushort)dupcnt);
+			seg->seq += dupcnt;
+			*length -= dupcnt;
+
+			if(seg->urg > dupcnt)
+				seg->urg -= dupcnt;
+			else {
+				seg->flags &= ~URG;
+				seg->urg = 0;
+			}
+		}
+	}
+	excess = seg->seq + *length - (tcb->rcv.nxt + tcb->rcv.wnd);
+	if(excess > 0) {
+		tcb->rerecv += excess;
+		*length -= excess;
+		*bp = trimblock(*bp, 0, *length);
+		if(*bp == nil)
+			panic("presotto is a boofhead");
+		seg->flags &= ~FIN;
+	}
+	return 0;
+}
+
+void
+tcpadvise(Proto *tcp, Block *bp, char *msg)
+{
+	Tcp4hdr *h4;
+	Tcp6hdr *h6;
+	Tcpctl *tcb;
+	uchar source[IPaddrlen];
+	uchar dest[IPaddrlen];
+	ushort psource, pdest;
+	Conv *s, **p;
+
+	h4 = (Tcp4hdr*)(bp->rp);
+	h6 = (Tcp6hdr*)(bp->rp);
+
+	if((h4->vihl&0xF0)==IP_VER4) {
+		v4tov6(dest, h4->tcpdst);
+		v4tov6(source, h4->tcpsrc);
+		psource = nhgets(h4->tcpsport);
+		pdest = nhgets(h4->tcpdport);
+	}
+	else {
+		ipmove(dest, h6->tcpdst);
+		ipmove(source, h6->tcpsrc);
+		psource = nhgets(h6->tcpsport);
+		pdest = nhgets(h6->tcpdport);
+	}
+
+	/* Look for a connection */
+	qlock(tcp);
+	for(p = tcp->conv; *p; p++) {
+		s = *p;
+		tcb = (Tcpctl*)s->ptcl;
+		if(s->rport == pdest)
+		if(s->lport == psource)
+		if(tcb->state != Closed)
+		if(ipcmp(s->raddr, dest) == 0)
+		if(ipcmp(s->laddr, source) == 0){
+			qlock(s);
+			qunlock(tcp);
+			switch(tcb->state){
+			case Syn_sent:
+				localclose(s, msg);
+				break;
+			}
+			qunlock(s);
+			freeblist(bp);
+			return;
+		}
+	}
+	qunlock(tcp);
+	freeblist(bp);
+}
+
+static char*
+tcpporthogdefensectl(char *val)
+{
+	if(strcmp(val, "on") == 0)
+		tcpporthogdefense = 1;
+	else if(strcmp(val, "off") == 0)
+		tcpporthogdefense = 0;
+	else
+		return "unknown value for tcpporthogdefense";
+	return nil;
+}
+
+/* called with c qlocked */
+char*
+tcpctl(Conv* c, char** f, int n)
+{
+	if(n == 1 && strcmp(f[0], "hangup") == 0)
+		return tcphangup(c);
+	if(n >= 1 && strcmp(f[0], "keepalive") == 0)
+		return tcpstartka(c, f, n);
+	if(n >= 1 && strcmp(f[0], "checksum") == 0)
+		return tcpsetchecksum(c, f, n);
+	if(n >= 1 && strcmp(f[0], "tcpporthogdefense") == 0)
+		return tcpporthogdefensectl(f[1]);
+	return "unknown control request";
+}
+
+int
+tcpstats(Proto *tcp, char *buf, int len)
+{
+	Tcppriv *priv;
+	char *p, *e;
+	int i;
+
+	priv = tcp->priv;
+	p = buf;
+	e = p+len;
+	for(i = 0; i < Nstats; i++)
+		p = seprint(p, e, "%s: %lud\n", statnames[i], priv->stats[i]);
+	return p - buf;
+}
+
+/*
+ *  garbage collect any stale conversations:
+ *	- SYN received but no SYN-ACK after 5 seconds (could be the SYN attack)
+ *	- Finwait2 after 5 minutes
+ *
+ *  this is called whenever we run out of channels.  Both checks are
+ *  of questionable validity so we try to use them only when we're
+ *  up against the wall.
+ */
+int
+tcpgc(Proto *tcp)
+{
+	Conv *c, **pp, **ep;
+	int n;
+	Tcpctl *tcb;
+
+
+	n = 0;
+	ep = &tcp->conv[tcp->nc];
+	for(pp = tcp->conv; pp < ep; pp++) {
+		c = *pp;
+		if(c == nil)
+			break;
+		if(!canqlock(c))
+			continue;
+		tcb = (Tcpctl*)c->ptcl;
+		switch(tcb->state){
+		case Syn_received:
+			if(NOW - tcb->time > 5000){
+				localclose(c, "timed out");
+				n++;
+			}
+			break;
+		case Finwait2:
+			if(NOW - tcb->time > 5*60*1000){
+				localclose(c, "timed out");
+				n++;
+			}
+			break;
+		}
+		qunlock(c);
+	}
+	return n;
+}
+
+void
+tcpsettimer(Tcpctl *tcb)
+{
+	int x;
+
+	/* round trip dependency */
+	x = backoff(tcb->backoff) *
+		(tcb->mdev + (tcb->srtt>>LOGAGAIN) + MSPTICK) / MSPTICK;
+
+	/* bounded twixt 1/2 and 64 seconds */
+	if(x < 500/MSPTICK)
+		x = 500/MSPTICK;
+	else if(x > (64000/MSPTICK))
+		x = 64000/MSPTICK;
+	tcb->timer.start = x;
+}
+
+void
+tcpinit(Fs *fs)
+{
+	Proto *tcp;
+	Tcppriv *tpriv;
+
+	tcp = smalloc(sizeof(Proto));
+	tpriv = tcp->priv = smalloc(sizeof(Tcppriv));
+	tcp->name = "tcp";
+	tcp->connect = tcpconnect;
+	tcp->announce = tcpannounce;
+	tcp->ctl = tcpctl;
+	tcp->state = tcpstate;
+	tcp->create = tcpcreate;
+	tcp->close = tcpclose;
+	tcp->rcv = tcpiput;
+	tcp->advise = tcpadvise;
+	tcp->stats = tcpstats;
+	tcp->inuse = tcpinuse;
+	tcp->gc = tcpgc;
+	tcp->ipproto = IP_TCPPROTO;
+	tcp->nc = scalednconv();
+	tcp->ptclsize = sizeof(Tcpctl);
+	tpriv->stats[MaxConn] = tcp->nc;
+
+	Fsproto(fs, tcp);
+}
+
+void
+tcpsetscale(Conv *s, Tcpctl *tcb, ushort rcvscale, ushort sndscale)
+{
+	if(rcvscale){
+		tcb->rcv.scale = rcvscale & 0xff;
+		tcb->snd.scale = sndscale & 0xff;
+		tcb->window = QMAX<<tcb->snd.scale;
+		qsetlimit(s->rq, tcb->window);
+	} else {
+		tcb->rcv.scale = 0;
+		tcb->snd.scale = 0;
+		tcb->window = QMAX;
+		qsetlimit(s->rq, tcb->window);
+	}
+}
--- /dev/null
+++ b/os/ip/udp.c
@@ -1,0 +1,649 @@
+#include	"u.h"
+#include	"../port/lib.h"
+#include	"mem.h"
+#include	"dat.h"
+#include	"fns.h"
+#include	"../port/error.h"
+
+#include	"ip.h"
+#include	"ipv6.h"
+
+
+#define DPRINT if(0)print
+
+enum
+{
+	UDP_UDPHDR_SZ	= 8,
+
+	UDP4_PHDR_OFF = 8,
+	UDP4_PHDR_SZ = 12,
+	UDP4_IPHDR_SZ = 20,
+	UDP6_IPHDR_SZ = 40,
+	UDP6_PHDR_SZ = 40,
+	UDP6_PHDR_OFF = 0,
+
+	IP_UDPPROTO	= 17,
+	UDP_USEAD7	= 52,
+	UDP_USEAD6	= 36,
+
+	Udprxms		= 200,
+	Udptickms	= 100,
+	Udpmaxxmit	= 10,
+};
+
+typedef struct Udp4hdr Udp4hdr;
+struct Udp4hdr
+{
+	/* ip header */
+	uchar	vihl;		/* Version and header length */
+	uchar	tos;		/* Type of service */
+	uchar	length[2];	/* packet length */
+	uchar	id[2];		/* Identification */
+	uchar	frag[2];	/* Fragment information */
+	uchar	Unused;	
+	uchar	udpproto;	/* Protocol */
+	uchar	udpplen[2];	/* Header plus data length */
+	uchar	udpsrc[IPv4addrlen];	/* Ip source */
+	uchar	udpdst[IPv4addrlen];	/* Ip destination */
+
+	/* udp header */
+	uchar	udpsport[2];	/* Source port */
+	uchar	udpdport[2];	/* Destination port */
+	uchar	udplen[2];	/* data length */
+	uchar	udpcksum[2];	/* Checksum */
+};
+
+typedef struct Udp6hdr Udp6hdr;
+struct Udp6hdr {
+	uchar viclfl[4];
+	uchar len[2];
+	uchar nextheader;
+	uchar hoplimit;
+	uchar udpsrc[IPaddrlen];
+	uchar udpdst[IPaddrlen];
+
+	/* udp header */
+	uchar	udpsport[2];	/* Source port */
+	uchar	udpdport[2];	/* Destination port */
+	uchar	udplen[2];	/* data length */
+	uchar	udpcksum[2];	/* Checksum */
+};
+
+/* MIB II counters */
+typedef struct Udpstats Udpstats;
+struct Udpstats
+{
+	ulong	udpInDatagrams;
+	ulong	udpNoPorts;
+	ulong	udpInErrors;
+	ulong	udpOutDatagrams;
+};
+
+typedef struct Udppriv Udppriv;
+struct Udppriv
+{
+	Ipht		ht;
+
+	/* MIB counters */
+	Udpstats	ustats;
+
+	/* non-MIB stats */
+	ulong		csumerr;		/* checksum errors */
+	ulong		lenerr;			/* short packet */
+};
+
+void (*etherprofiler)(char *name, int qlen);
+void udpkick(void *x, Block *bp);
+
+/*
+ *  protocol specific part of Conv
+ */
+typedef struct Udpcb Udpcb;
+struct Udpcb
+{
+	QLock;
+	uchar	headers;
+};
+
+static char*
+udpconnect(Conv *c, char **argv, int argc)
+{
+	char *e;
+	Udppriv *upriv;
+
+	upriv = c->p->priv;
+	e = Fsstdconnect(c, argv, argc);
+	Fsconnected(c, e);
+	if(e != nil)
+		return e;
+
+	iphtadd(&upriv->ht, c);
+	return nil;
+}
+
+
+static int
+udpstate(Conv *c, char *state, int n)
+{
+	return snprint(state, n, "%s qin %d qout %d",
+		c->inuse ? "Open" : "Closed",
+		c->rq ? qlen(c->rq) : 0,
+		c->wq ? qlen(c->wq) : 0
+	);
+}
+
+static char*
+udpannounce(Conv *c, char** argv, int argc)
+{
+	char *e;
+	Udppriv *upriv;
+
+	upriv = c->p->priv;
+	e = Fsstdannounce(c, argv, argc);
+	if(e != nil)
+		return e;
+	Fsconnected(c, nil);
+	iphtadd(&upriv->ht, c);
+
+	return nil;
+}
+
+static void
+udpcreate(Conv *c)
+{
+	c->rq = qopen(64*1024, Qmsg, 0, 0);
+	c->wq = qbypass(udpkick, c);
+}
+
+static void
+udpclose(Conv *c)
+{
+	Udpcb *ucb;
+	Udppriv *upriv;
+
+	upriv = c->p->priv;
+	iphtrem(&upriv->ht, c);
+
+	c->state = 0;
+	qclose(c->rq);
+	qclose(c->wq);
+	qclose(c->eq);
+	ipmove(c->laddr, IPnoaddr);
+	ipmove(c->raddr, IPnoaddr);
+	c->lport = 0;
+	c->rport = 0;
+
+	ucb = (Udpcb*)c->ptcl;
+	ucb->headers = 0;
+
+	qunlock(c);
+}
+
+void
+udpkick(void *x, Block *bp)
+{
+	Conv *c = x;
+	Udp4hdr *uh4;
+	Udp6hdr *uh6;
+	ushort rport;
+	uchar laddr[IPaddrlen], raddr[IPaddrlen];
+	Udpcb *ucb;
+	int dlen, ptcllen;
+	Udppriv *upriv;
+	Fs *f;
+	int version;
+	Conv *rc;
+
+	upriv = c->p->priv;
+	f = c->p->f;
+
+	netlog(c->p->f, Logudp, "udp: kick\n");
+	if(bp == nil)
+		return;
+
+	ucb = (Udpcb*)c->ptcl;
+	switch(ucb->headers) {
+	case 7:
+		/* get user specified addresses */
+		bp = pullupblock(bp, UDP_USEAD7);
+		if(bp == nil)
+			return;
+		ipmove(raddr, bp->rp);
+		bp->rp += IPaddrlen;
+		ipmove(laddr, bp->rp);
+		bp->rp += IPaddrlen;
+		/* pick interface closest to dest */
+		if(ipforme(f, laddr) != Runi)
+			findlocalip(f, laddr, raddr);
+		bp->rp += IPaddrlen;		/* Ignore ifc address */
+		rport = nhgets(bp->rp);
+		bp->rp += 2+2;			/* Ignore local port */
+		break;
+	case 6:
+		/* get user specified addresses */
+		bp = pullupblock(bp, UDP_USEAD6);
+		if(bp == nil)
+			return;
+		ipmove(raddr, bp->rp);
+		bp->rp += IPaddrlen;
+		ipmove(laddr, bp->rp);
+		bp->rp += IPaddrlen;
+		/* pick interface closest to dest */
+		if(ipforme(f, laddr) != Runi)
+			findlocalip(f, laddr, raddr);
+		rport = nhgets(bp->rp);
+		bp->rp += 2+2;			/* Ignore local port */
+		break;
+	default:
+		rport = 0;
+		break;
+	}
+
+	if(ucb->headers) {
+		if(memcmp(laddr, v4prefix, IPv4off) == 0 ||
+		    ipcmp(laddr, IPnoaddr) == 0)
+			version = V4;
+		else
+			version = V6;
+	} else {
+		if( (memcmp(c->raddr, v4prefix, IPv4off) == 0 &&
+			memcmp(c->laddr, v4prefix, IPv4off) == 0)
+			|| ipcmp(c->raddr, IPnoaddr) == 0)
+			version = V4;
+		else
+			version = V6;
+	}
+
+	dlen = blocklen(bp);
+
+	/* fill in pseudo header and compute checksum */
+	switch(version){
+	case V4:
+		bp = padblock(bp, UDP4_IPHDR_SZ+UDP_UDPHDR_SZ);
+		if(bp == nil)
+			return;
+
+		uh4 = (Udp4hdr *)(bp->rp);
+		ptcllen = dlen + UDP_UDPHDR_SZ;
+		uh4->Unused = 0;
+		uh4->udpproto = IP_UDPPROTO;
+		uh4->frag[0] = 0;
+		uh4->frag[1] = 0;
+		hnputs(uh4->udpplen, ptcllen);
+		if(ucb->headers) {
+			v6tov4(uh4->udpdst, raddr);
+			hnputs(uh4->udpdport, rport);
+			v6tov4(uh4->udpsrc, laddr);
+			rc = nil;
+		} else {
+			v6tov4(uh4->udpdst, c->raddr);
+			hnputs(uh4->udpdport, c->rport);
+			if(ipcmp(c->laddr, IPnoaddr) == 0)
+				findlocalip(f, c->laddr, c->raddr);
+			v6tov4(uh4->udpsrc, c->laddr);
+			rc = c;
+		}
+		hnputs(uh4->udpsport, c->lport);
+		hnputs(uh4->udplen, ptcllen);
+		uh4->udpcksum[0] = 0;
+		uh4->udpcksum[1] = 0;
+		hnputs(uh4->udpcksum, 
+		       ptclcsum(bp, UDP4_PHDR_OFF, dlen+UDP_UDPHDR_SZ+UDP4_PHDR_SZ));
+		uh4->vihl = IP_VER4;
+		ipoput4(f, bp, 0, c->ttl, c->tos, rc);
+		break;
+
+	case V6:
+		bp = padblock(bp, UDP6_IPHDR_SZ+UDP_UDPHDR_SZ);
+		if(bp == nil)
+			return;
+
+		// using the v6 ip header to create pseudo header 
+		// first then reset it to the normal ip header
+		uh6 = (Udp6hdr *)(bp->rp);
+		memset(uh6, 0, 8);
+		ptcllen = dlen + UDP_UDPHDR_SZ;
+		hnputl(uh6->viclfl, ptcllen);
+		uh6->hoplimit = IP_UDPPROTO;
+		if(ucb->headers) {
+			ipmove(uh6->udpdst, raddr);
+			hnputs(uh6->udpdport, rport);
+			ipmove(uh6->udpsrc, laddr);
+			rc = nil;
+		} else {
+			ipmove(uh6->udpdst, c->raddr);
+			hnputs(uh6->udpdport, c->rport);
+			if(ipcmp(c->laddr, IPnoaddr) == 0)
+				findlocalip(f, c->laddr, c->raddr);
+			ipmove(uh6->udpsrc, c->laddr);
+			rc = c;
+		}
+		hnputs(uh6->udpsport, c->lport);
+		hnputs(uh6->udplen, ptcllen);
+		uh6->udpcksum[0] = 0;
+		uh6->udpcksum[1] = 0;
+		hnputs(uh6->udpcksum, 
+		       ptclcsum(bp, UDP6_PHDR_OFF, dlen+UDP_UDPHDR_SZ+UDP6_PHDR_SZ));
+		memset(uh6, 0, 8);
+		uh6->viclfl[0] = IP_VER6;
+		hnputs(uh6->len, ptcllen);
+		uh6->nextheader = IP_UDPPROTO;
+		ipoput6(f, bp, 0, c->ttl, c->tos, rc);
+		break;
+
+	default:
+		panic("udpkick: version %d", version);
+	}
+	upriv->ustats.udpOutDatagrams++;
+}
+
+void
+udpiput(Proto *udp, Ipifc *ifc, Block *bp)
+{
+	int len;
+	Udp4hdr *uh4;
+	Udp6hdr *uh6;
+	Conv *c;
+	Udpcb *ucb;
+	uchar raddr[IPaddrlen], laddr[IPaddrlen];
+	ushort rport, lport;
+	Udppriv *upriv;
+	Fs *f;
+	int version;
+	int ottl, oviclfl, olen;
+	uchar *p;
+
+	upriv = udp->priv;
+	f = udp->f;
+	upriv->ustats.udpInDatagrams++;
+
+	uh4 = (Udp4hdr*)(bp->rp);
+	version = ((uh4->vihl&0xF0)==IP_VER6) ? V6 : V4;
+
+	/*
+	 * Put back pseudo header for checksum 
+	 * (remember old values for icmpnoconv())
+	 */
+	switch(version) {
+	case V4:
+		ottl = uh4->Unused;
+		uh4->Unused = 0;
+		len = nhgets(uh4->udplen);
+		olen = nhgets(uh4->udpplen);
+		hnputs(uh4->udpplen, len);
+
+		v4tov6(raddr, uh4->udpsrc);
+		v4tov6(laddr, uh4->udpdst);
+		lport = nhgets(uh4->udpdport);
+		rport = nhgets(uh4->udpsport);
+
+		if(nhgets(uh4->udpcksum)) {
+			if(ptclcsum(bp, UDP4_PHDR_OFF, len+UDP4_PHDR_SZ)) {
+				upriv->ustats.udpInErrors++;
+				netlog(f, Logudp, "udp: checksum error %I\n", raddr);
+				DPRINT("udp: checksum error %I\n", raddr);
+				freeblist(bp);
+				return;
+			}
+		}
+		uh4->Unused = ottl;
+		hnputs(uh4->udpplen, olen);
+		break;
+	case V6:
+		uh6 = (Udp6hdr*)(bp->rp);
+		len = nhgets(uh6->udplen);
+		oviclfl = nhgetl(uh6->viclfl);
+		olen = nhgets(uh6->len);
+		ottl = uh6->hoplimit;
+		ipmove(raddr, uh6->udpsrc);
+		ipmove(laddr, uh6->udpdst);
+		lport = nhgets(uh6->udpdport);
+		rport = nhgets(uh6->udpsport);
+		memset(uh6, 0, 8);
+		hnputl(uh6->viclfl, len);
+		uh6->hoplimit = IP_UDPPROTO;
+		if(ptclcsum(bp, UDP6_PHDR_OFF, len+UDP6_PHDR_SZ)) {
+			upriv->ustats.udpInErrors++;
+			netlog(f, Logudp, "udp: checksum error %I\n", raddr);
+			DPRINT("udp: checksum error %I\n", raddr);
+			freeblist(bp);
+			return;
+		}
+		hnputl(uh6->viclfl, oviclfl);
+		hnputs(uh6->len, olen);
+		uh6->nextheader = IP_UDPPROTO;
+		uh6->hoplimit = ottl;
+		break;
+	default:
+		panic("udpiput: version %d", version);
+		return;	/* to avoid a warning */
+	}
+
+	qlock(udp);
+
+	c = iphtlook(&upriv->ht, raddr, rport, laddr, lport);
+	if(c == nil){
+		/* no converstation found */
+		upriv->ustats.udpNoPorts++;
+		qunlock(udp);
+		netlog(f, Logudp, "udp: no conv %I!%d -> %I!%d\n", raddr, rport,
+		       laddr, lport);
+
+		switch(version){
+		case V4:
+			icmpnoconv(f, bp);
+			break;
+		case V6:
+			icmphostunr(f, ifc, bp, icmp6_port_unreach, 0);
+			break;
+		default:
+			panic("udpiput2: version %d", version);
+		}
+
+		freeblist(bp);
+		return;
+	}
+	ucb = (Udpcb*)c->ptcl;
+
+	if(c->state == Announced){
+		if(ucb->headers == 0){
+			/* create a new conversation */
+			if(ipforme(f, laddr) != Runi) {
+				switch(version){
+				case V4:
+					v4tov6(laddr, ifc->lifc->local);
+					break;
+				case V6:
+					ipmove(laddr, ifc->lifc->local);
+					break;
+				default:
+					panic("udpiput3: version %d", version);
+				}
+			}
+			c = Fsnewcall(c, raddr, rport, laddr, lport, version);
+			if(c == nil){
+				qunlock(udp);
+				freeblist(bp);
+				return;
+			}
+			iphtadd(&upriv->ht, c);
+			ucb = (Udpcb*)c->ptcl;
+		}
+	}
+
+	qlock(c);
+	qunlock(udp);
+
+	/*
+	 * Trim the packet down to data size
+	 */
+	len -= UDP_UDPHDR_SZ;
+	switch(version){
+	case V4:
+		bp = trimblock(bp, UDP4_IPHDR_SZ+UDP_UDPHDR_SZ, len);
+		break;
+	case V6:
+		bp = trimblock(bp, UDP6_IPHDR_SZ+UDP_UDPHDR_SZ, len);
+		break;
+	default:
+		bp = nil;
+		panic("udpiput4: version %d", version);
+	}
+	if(bp == nil){
+		qunlock(c);
+		netlog(f, Logudp, "udp: len err %I.%d -> %I.%d\n", raddr, rport,
+		       laddr, lport);
+		upriv->lenerr++;
+		return;
+	}
+
+	netlog(f, Logudpmsg, "udp: %I.%d -> %I.%d l %d\n", raddr, rport,
+	       laddr, lport, len);
+
+	switch(ucb->headers){
+	case 7:
+		/* pass the src address */
+		bp = padblock(bp, UDP_USEAD7);
+		p = bp->rp;
+		ipmove(p, raddr); p += IPaddrlen;
+		ipmove(p, laddr); p += IPaddrlen;
+		ipmove(p, ifc->lifc->local); p += IPaddrlen;
+		hnputs(p, rport); p += 2;
+		hnputs(p, lport);
+		break;
+	case 6:
+		/* pass the src address */
+		bp = padblock(bp, UDP_USEAD6);
+		p = bp->rp;
+		ipmove(p, raddr); p += IPaddrlen;
+		ipmove(p, ipforme(f, laddr)==Runi ? laddr : ifc->lifc->local); p += IPaddrlen;
+		hnputs(p, rport); p += 2;
+		hnputs(p, lport);
+		break;
+	}
+
+	if(bp->next)
+		bp = concatblock(bp);
+
+	if(qfull(c->rq)){
+		qunlock(c);
+		netlog(f, Logudp, "udp: qfull %I.%d -> %I.%d\n", raddr, rport,
+		       laddr, lport);
+		freeblist(bp);
+		return;
+	}
+
+	qpass(c->rq, bp);
+	qunlock(c);
+
+}
+
+char*
+udpctl(Conv *c, char **f, int n)
+{
+	Udpcb *ucb;
+
+	ucb = (Udpcb*)c->ptcl;
+	if(n == 1){
+		if(strcmp(f[0], "oldheaders") == 0){
+			ucb->headers = 6;
+			return nil;
+		} else if(strcmp(f[0], "headers") == 0){
+			ucb->headers = 7;
+			return nil;
+		}
+	}
+	return "unknown control request";
+}
+
+void
+udpadvise(Proto *udp, Block *bp, char *msg)
+{
+	Udp4hdr *h4;
+	Udp6hdr *h6;
+	uchar source[IPaddrlen], dest[IPaddrlen];
+	ushort psource, pdest;
+	Conv *s, **p;
+	int version;
+
+	h4 = (Udp4hdr*)(bp->rp);
+	version = ((h4->vihl&0xF0)==IP_VER6) ? V6 : V4;
+
+	switch(version) {
+	case V4:
+		v4tov6(dest, h4->udpdst);
+		v4tov6(source, h4->udpsrc);
+		psource = nhgets(h4->udpsport);
+		pdest = nhgets(h4->udpdport);
+		break;
+	case V6:
+		h6 = (Udp6hdr*)(bp->rp);
+		ipmove(dest, h6->udpdst);
+		ipmove(source, h6->udpsrc);
+		psource = nhgets(h6->udpsport);
+		pdest = nhgets(h6->udpdport);
+		break;
+	default:
+		panic("udpadvise: version %d", version);
+		return;  /* to avoid a warning */
+	}
+
+	/* Look for a connection */
+	qlock(udp);
+	for(p = udp->conv; *p; p++) {
+		s = *p;
+		if(s->rport == pdest)
+		if(s->lport == psource)
+		if(ipcmp(s->raddr, dest) == 0)
+		if(ipcmp(s->laddr, source) == 0){
+			if(s->ignoreadvice)
+				break;
+			qlock(s);
+			qunlock(udp);
+			qhangup(s->rq, msg);
+			qhangup(s->wq, msg);
+			qunlock(s);
+			freeblist(bp);
+			return;
+		}
+	}
+	qunlock(udp);
+	freeblist(bp);
+}
+
+int
+udpstats(Proto *udp, char *buf, int len)
+{
+	Udppriv *upriv;
+
+	upriv = udp->priv;
+	return snprint(buf, len, "InDatagrams: %lud\nNoPorts: %lud\nInErrors: %lud\nOutDatagrams: %lud\n",
+		upriv->ustats.udpInDatagrams,
+		upriv->ustats.udpNoPorts,
+		upriv->ustats.udpInErrors,
+		upriv->ustats.udpOutDatagrams);
+}
+
+void
+udpinit(Fs *fs)
+{
+	Proto *udp;
+
+	udp = smalloc(sizeof(Proto));
+	udp->priv = smalloc(sizeof(Udppriv));
+	udp->name = "udp";
+	udp->connect = udpconnect;
+	udp->announce = udpannounce;
+	udp->ctl = udpctl;
+	udp->state = udpstate;
+	udp->create = udpcreate;
+	udp->close = udpclose;
+	udp->rcv = udpiput;
+	udp->advise = udpadvise;
+	udp->stats = udpstats;
+	udp->ipproto = IP_UDPPROTO;
+	udp->nc = Nchans;
+	udp->ptclsize = sizeof(Udpcb);
+
+	Fsproto(fs, udp);
+}
--- /dev/null
+++ b/os/ipaq1110/Mk
@@ -1,0 +1,8 @@
+#!/bin/rc
+rfork ne
+ROOT=/usr/inferno
+fn cd
+NPROC=3
+path=(/usr/inferno/Plan9/$cputype/bin $path)
+#bind /usr/inferno/mkconfig.dist /usr/inferno/mkconfig
+exec mk $*
--- /dev/null
+++ b/os/ipaq1110/NOTICE
@@ -1,0 +1,6 @@
+Inferno® Copyright © 1996-1999 Lucent Technologies Inc.  All rights reserved.
+iPAQ 36xx Inferno port Copyright © 2000-2003 Vita Nuova Holdings Limited.  All rights reserved.
+
+This software is subject to the terms of ../NOTICE, except for the following:
+	devaudio.c came from Plan 9 and is covered by the Lucent Public License 1.02
+	The etherwavelan.c driver was contributed to Plan 9 by nemo@gsyc.escet.urjc.es
--- /dev/null
+++ b/os/ipaq1110/README
@@ -1,0 +1,65 @@
+Installing 4th Edition Inferno on an IPAQ (21 August 2003)
+
+This Inferno kernel software is only for the iPAQ 36xx,
+colour models only.
+
+Of course, since it's Inferno, the existing
+applications, and yours, will run (give or take kernel bugs).
+Suspend/resume is implemented except for PCMCIA.
+(We are doing the work to make it more general.)
+Otherwise the system tries to conserve power in the usual
+ways by going into idle to wait for interrupts and powering
+devices up and down on open and close.
+
+The following describes loading the Inferno kernel in to the iPAQ.
+This preliminary version has some things hard-wired in to the code
+to run on our wavelan network (see the end of archipaq.c).
+
+os/init/ipaqinit.b will support a file system (currently dossrv for simplicity)
+running over a Flash Translation Layer on the iPAQ flash.
+Setting up a local file system and loading that onto the iPAQ
+is not discussed here.  A separate package will deal with that.
+
+For development, we generally take the software over the net from
+an Inferno file service (ie, svc/net) running in emu.
+We are providing this iPAQ kernel package to subscribers earlier,
+for the benefit of those subscribers that can make use of it now
+(eg, modify the networking setup to suit their own network).
+A basic local file system package should be available shortly.
+Until it is, if you are not confident you can set up the networking
+or prepare a local file system, you should wait.
+
+1. You must first prepare the iPAQ with the handhelds.org bootloader,
+version 2.18.54 (later ones probably work but we haven't yet tried them).
+Use the iPAQ H3600 Linux installation instructions:
+	ftp://ftp.handhelds.org/pub/linux/compaq/ipaq/stable/install.html
+
+Following that procedure will eliminate Windows/CE from the device: if you will need it
+in future, be sure to save the flash images as
+described in the handhelds.org instructions.
+Note that you will also be trusting that they can get you
+back to a working WinCE machine, so be sure to read the handhelds.org
+documents thoroughly in that regard.
+
+2. At the end, the instructions say ``At this point you have a working
+bootloader and you are ready to install a Linux distribution''.
+You can install Inferno instead, or several other systems, and
+you can later install them instead of Inferno, since they all use
+the same boot loader.
+
+3. A ready-made Inferno kernel is in the file k.gz in this directory
+(os/ipaq1110/k.gz in the Inferno structure).  With a 115k serial connection
+to the bootloader established, as described for loading Linux,
+when you tell the bootloader to `load kernel', send k.gz as the
+data file (using the XMODEM protocol as described by Handhelds.org).
+
+4. Once the system is running, you can update kernels using Inferno,
+when your file system is taken as `remote' over a wireless connection.
+In an Inferno shell on the device (perhaps using the console serial port
+as a keyboard):
+	bind -a '#F' /dev
+	echo erase all >/dev/flash/kernelctl
+	dd </os/ipaq1110/k.gz -conv sync >/dev/flash/kernel
+or just run
+	/os/ipaq1110/upd
+which does that.
--- /dev/null
+++ b/os/ipaq1110/archipaq.c
@@ -1,0 +1,378 @@
+/*
+ * ipaq
+ */
+#include	"u.h"
+#include	"../port/lib.h"
+#include	"mem.h"
+#include	"dat.h"
+#include	"fns.h"
+#include	"../port/error.h"
+#include	"io.h"
+#include	"draw.h"
+#include	<memdraw.h>
+#include	"screen.h"
+
+#include "../port/netif.h"
+#include "etherif.h"
+#include	"../port/flashif.h"
+
+#define	EGPIOADDR		0x49000000	/* physical and virtual address of write only register in CS5 space */
+
+static ulong egpiocopy;
+
+static void
+egpiosc(ulong set, ulong clr)
+{
+	int s;
+
+	s = splhi();
+	egpiocopy = (egpiocopy & ~clr) | set;
+	*(ulong*)EGPIOADDR = egpiocopy;
+	splx(s);
+}
+
+void
+archreset(void)
+{
+	GpioReg *g;
+	PpcReg *p;
+
+	g = GPIOREG;
+	g->grer = 0;
+	g->gfer = 0;
+	g->gedr = g->gedr;
+	g->gpcr = ~0;
+	g->gpdr = GPIO_CLK_SET0_o | GPIO_CLK_SET1_o;	// | GPIO_LDD8_15_o;
+	g->gafr |= GPIO_SYS_CLK_i;	// | GPIO_LDD8_15_o;
+	p = PPCREG;
+	p->ppdr |= PPC_TXD4 | PPC_SCLK | PPC_SFRM;	/* not sure about PPC_TXD4 here */
+	p->ppsr &= ~(PPC_TXD4 | PPC_SCLK | PPC_SFRM);
+
+	archuartpower(3, 1);	/* allow console to work sooner rather than later */
+	L3init();
+}
+
+void
+archpowerdown(void)
+{
+	PmgrReg *p = PMGRREG;
+
+	p->pwer = GPIO_PWR_ON_i;	/* only power button for now, not RTC */
+	p->pcfr = PCFR_opde;	/* stop 3.6MHz oscillator, and drive pcmcia and chip select low */
+	p->pgsr = 0;	/* drive all outputs to zero in sleep */
+	PPCREG->psdr = 0;	/* all peripheral pins as outputs during sleep */
+}
+
+void
+archpowerup(void)
+{
+	GpioReg *g;
+	int i;
+
+	g = GPIOREG;
+	g->gpdr |= GPIO_COM_RTS_o;	/* just in case it's off */
+	g->gpsr = GPIO_COM_RTS_o;
+	*(ulong*)EGPIOADDR = egpiocopy;
+	for(i=0; i<50*1000; i++)
+		;
+	while((g->gplr & GPIO_PWR_ON_i) == 0)
+		;	/* wait for it to come up */
+}
+
+void
+archconfinit(void)
+{
+	int w;
+
+	conf.topofmem = 0xC0000000+32*MB;
+	w = PMGRREG->ppcr & 0x1f;
+	m->cpuhz = CLOCKFREQ*(w*4+16);
+
+	conf.useminicache = 1;
+	conf.portrait = 1;	/* should take from param flash or allow dynamic change */
+}
+
+void
+kbdinit(void)
+{
+	addclock0link(kbdclock, MS2HZ);
+}
+
+static LCDmode lcd320x240x16tft =
+{
+//	.x = 320, .y = 240, .depth = 16, .hz = 70,
+//	.pbs = 2, .dual = 0, .mono = 0, .active = 1,
+//	.hsync_wid = 4-2, .sol_wait = 12-1, .eol_wait = 17-1,
+//	.vsync_hgt = 3-1, .soft_wait = 10, .eof_wait = 1,
+//	.lines_per_int = 0, .palette_delay = 0, .acbias_lines = 0,
+//	.obits = 16,
+//	.vsynclow = 1, .hsynclow = 1,
+	320, 240, 16, 70,
+	2, 0, 0, 1,
+	4-2, 12-1, 17-1,
+	3-1, 10, 1,
+	0, 0, 0,
+	16,
+	1, 1,
+};
+
+int
+archlcdmode(LCDmode *m)
+{
+	*m = lcd320x240x16tft;
+	return 0;
+}
+
+void
+archlcdenable(int on)
+{
+	if(on)
+		egpiosc(EGPIO_LCD_ON|EGPIO_LCD_PCI|EGPIO_LCD_5V_ON|EGPIO_LVDD_ON, 0);
+	else
+		egpiosc(0, EGPIO_LCD_ON|EGPIO_LCD_PCI|EGPIO_LCD_5V_ON|EGPIO_LVDD_ON);
+}
+
+void
+archconsole(void)
+{
+	uartspecial(0, 115200, 'n', &kbdq, &printq, kbdcr2nl);
+}
+
+void
+archuartpower(int port, int on)
+{
+	int s;
+
+	if(port != 3)
+		return;
+	s = splhi();
+	GPIOREG->gpdr |= GPIO_COM_RTS_o;	/* should be done elsewhere */
+	GPIOREG->gpsr = GPIO_COM_RTS_o;
+	splx(s);
+	if(on)
+		egpiosc(EGPIO_RS232_ON, 0);
+	else
+		egpiosc(0, EGPIO_RS232_ON);
+}
+
+void
+archreboot(void)
+{
+	dcflushall();
+	GPIOREG->gedr = 1<<0;
+	mmuputctl(mmugetctl() & ~CpCaltivec);	/* restore bootstrap's vectors */
+	RESETREG->rsrr = 1;	/* software reset */
+	for(;;)
+		spllo();
+}
+
+void
+archflashwp(Flash*, int wp)
+{
+	if(wp)
+		egpiosc(0, EGPIO_VPEN);
+	else
+		egpiosc(EGPIO_VPEN, 0);
+}
+
+/*
+ * for ../port/devflash.c:/^flashreset
+ * retrieve flash type, virtual base and length and return 0;
+ * return -1 on error (no flash)
+ */
+int
+archflashreset(int bank, Flash *f)
+{
+	if(bank != 0)
+		return -1;
+	f->type = "cfi16";
+	f->addr = KADDR(FLASHMEM);
+	if((MEMCFGREG->msc0 & (1<<2)) == 0){
+		f->interleave = 1;
+		f->width = 4;
+	}else
+		f->width = 2;
+	return 0;
+}
+
+int
+archaudiopower(int on)
+{
+	int s;
+
+	if(on)
+		egpiosc(EGPIO_CODEC_RESET | EGPIO_AUD_PWR_ON, 0);
+	else
+		egpiosc(0, EGPIO_CODEC_RESET | EGPIO_AUD_ON | EGPIO_AUD_PWR_ON | EGPIO_QMUTE);
+	s = splhi();
+	GPIOREG->gafr |= GPIO_SYS_CLK_i;
+	GPIOREG->gpdr |= GPIO_CLK_SET0_o | GPIO_CLK_SET1_o;
+	GPIOREG->gpsr = GPIO_CLK_SET0_o;
+	GPIOREG->gpcr = GPIO_CLK_SET1_o;
+	splx(s);
+	return 0;
+}
+
+void
+archcodecreset(void)
+{
+//	egpiosc(0, EGPIO_CODEC_RESET);
+//	egpiosc(EGPIO_CODEC_RESET, 0);
+}
+
+void
+archaudiomute(int on)
+{
+	if(on)
+		egpiosc(EGPIO_QMUTE, 0);
+	else
+		egpiosc(0, EGPIO_QMUTE);
+}
+
+void
+archaudioamp(int on)
+{
+	if(on)
+		egpiosc(EGPIO_AUD_ON, 0);
+	else
+		egpiosc(0, EGPIO_AUD_ON);
+}
+
+enum {
+	Fs512 = 0,
+	Fs384 = 1,
+	Fs256 = 2,
+
+	MHz4_096 = GPIO_CLK_SET1_o,
+	MHz5_6245 = GPIO_CLK_SET1_o|GPIO_CLK_SET0_o,
+	MHz11_2896 = GPIO_CLK_SET0_o,
+	MHz12_288 = 0
+};
+
+typedef struct Csel Csel;
+struct Csel{
+	int	speed;
+	int	cfs;		/* codec system clock multiplier */
+	int	gclk;		/* gpio clock generator setting */
+	int	div;		/* ssp clock divisor */
+};
+static Csel csel[] = {
+	{8000, Fs512, MHz4_096, 16},
+	{11025, Fs512, MHz5_6245, 16},
+	{16000, Fs256 , MHz4_096, 8},
+	{22050, Fs512, MHz11_2896, 16},
+	{32000, Fs384, MHz12_288, 12},
+	{44100, Fs256, MHz11_2896, 8},
+	{48000, Fs256, MHz12_288, 8},
+	{0},
+};
+
+int
+archaudiospeed(int clock, int set)
+{
+	GpioReg *g;
+	SspReg *ssp;
+	Csel *cs;
+	int s, div, cr0;
+
+	for(cs = csel; cs->speed > 0; cs++)
+		if(cs->speed == clock){
+			if(!set)
+				return cs->cfs;
+			div = cs->div;
+			if(div == 0)
+				div = 4;
+			div = div/2 - 1;
+			s = splhi();
+			g = GPIOREG;
+			g->gpsr = cs->gclk;
+			g->gpcr = ~cs->gclk & (GPIO_CLK_SET0_o|GPIO_CLK_SET1_o);
+			ssp = SSPREG;
+			cr0 = (div<<8) | 0x1f;	/* 16 bits, TI frames, not enabled */
+			ssp->sscr0 = cr0;
+			ssp->sscr1 = 0x0020;	/* ext clock */
+			ssp->sscr0 = cr0 | 0x80;	/* enable */
+			splx(s);
+			return cs->cfs;
+		}
+	return -1;
+}
+
+/*
+ * pcmcia
+ */
+int
+pcmpowered(int slotno)
+{
+	if(slotno)
+		return 0;
+	if(egpiocopy & EGPIO_OPT_PWR_ON)
+		return 3;
+	return 0;
+}
+
+void
+pcmpower(int slotno, int on)
+{
+	USED(slotno);	/* the pack powers both or none */
+	if(on){
+		if((egpiocopy & EGPIO_OPT_PWR_ON) == 0){
+			egpiosc(EGPIO_OPT_PWR_ON | EGPIO_OPT_ON, 0);
+			delay(200);
+		}
+	}else
+		egpiosc(0, EGPIO_OPT_PWR_ON | EGPIO_OPT_ON);
+}
+
+void
+pcmreset(int slot)
+{
+	USED(slot);
+	egpiosc(EGPIO_CARD_RESET, 0);
+	delay(100);	// microdelay(10);
+	egpiosc(0, EGPIO_CARD_RESET);
+}
+
+int
+pcmpin(int slot, int type)
+{
+	switch(type){
+	case PCMready:
+		return slot==0? 21: 11;
+	case PCMeject:
+		return slot==0? 17: 10;
+	case PCMstschng:
+		return -1;
+	default:
+		return -1;
+	}
+}
+
+void
+pcmsetvpp(int slot, int vpp)
+{
+	USED(slot, vpp);
+}
+
+/*
+ * set ether parameters: the contents should be derived from EEPROM or NVRAM
+ */
+int
+archether(int ctlno, Ether *ether)
+{
+	static char opt[128];
+
+	if(ctlno == 1){
+		sprint(ether->type, "EC2T");
+		return 1;
+	}
+	if(ctlno > 0)
+		return -1;
+	sprint(ether->type, "wavelan");
+	if(0)
+		strcpy(opt, "mode=0 essid=Limbo station=ipaq1 crypt=off");	/* peertopeer */
+	else
+		strcpy(opt, "mode=managed essid=Vitanuova1 station=ipaq1 crypt=off");
+	ether->nopt = tokenize(opt, ether->opt, nelem(ether->opt));
+	return 1;
+}
--- /dev/null
+++ b/os/ipaq1110/dat.h
@@ -1,0 +1,135 @@
+typedef struct Conf	Conf;
+typedef struct Dma	Dma;
+typedef struct FPU	FPU;
+typedef struct FPenv	FPenv;
+typedef struct Label	Label;
+typedef struct Lock	Lock;
+typedef struct Mach	Mach;
+typedef struct Ureg	Ureg;
+typedef struct ISAConf	ISAConf;
+typedef struct PCMmap	PCMmap;
+typedef struct PCMslot	PCMslot;
+
+typedef ulong Instr;
+
+struct Conf
+{
+	ulong	nmach;			/* processors */
+	ulong	nproc;			/* processes */
+	ulong	npage0;			/* total physical pages of memory */
+	ulong	npage1;			/* total physical pages of memory */
+	ulong	topofmem;		/* highest physical address + 1 */
+	ulong	npage;			/* total physical pages of memory */
+	ulong	base0;			/* base of bank 0 */
+	ulong	base1;			/* base of bank 1 */
+	ulong	ialloc;			/* max interrupt time allocation in bytes */
+
+	int		useminicache;		/* use mini cache: screen.c/lcd.c */
+	int		textwrite;			/* writeable text segment, for debug */
+	int		portrait;	/* display orientation */
+};
+
+#define NISAOPT 8
+struct ISAConf {
+	char	type[KNAMELEN];
+	ulong	port;
+	ulong	irq;
+	int	itype;
+	ulong	dma;
+	ulong	mem;
+	ulong	size;
+	ulong	freq;
+
+	int	nopt;
+	char	*opt[NISAOPT];
+};
+
+/*
+ * FPenv.status
+ */
+enum
+{
+	FPINIT,
+	FPACTIVE,
+	FPINACTIVE,
+};
+
+struct	FPenv
+{
+	ulong	status;
+	ulong	control;
+	ushort	fpistate;	/* emulated fp */
+	ulong	regs[8][3];	/* emulated fp */	
+};
+
+/*
+ * This structure must agree with fpsave and fprestore asm routines
+ */
+struct	FPU
+{
+	FPenv	env;
+};
+
+struct Label
+{
+	ulong	sp;
+	ulong	pc;
+};
+
+struct Lock
+{
+	ulong	key;
+	ulong	sr;
+	ulong	pc;
+	int	pri;
+};
+
+#include "../port/portdat.h"
+
+/*
+ *  machine dependent definitions not used by ../port/portdat.h
+ */
+struct Mach
+{
+	/* OFFSETS OF THE FOLLOWING KNOWN BY l.s */
+	ulong	splpc;		/* pc of last caller to splhi */
+
+	/* ordering from here on irrelevant */
+
+	int	machno;		/* physical id of processor */
+	ulong	ticks;			/* of the clock since boot time */
+	Proc	*proc;			/* current process on this processor */
+	Label	sched;			/* scheduler wakeup */
+	Lock	alarmlock;		/* access to alarm list */
+	void	*alarm;			/* alarms bound to this clock */
+	ulong	cpuhz;
+
+	/* stacks for exceptions */
+	ulong	fiqstack[4];
+	ulong	irqstack[4];
+	ulong	abtstack[4];
+	ulong	undstack[4];
+
+	int	stack[1];
+};
+
+#define	MACHP(n)	(n == 0 ? (Mach*)(MACHADDR) : (Mach*)0)
+
+extern Mach *m;
+extern Proc *up;
+
+typedef struct MemBank {
+	uint	pbase;
+	uint	plimit;
+	uint	vbase;
+	uint	vlimit;
+} MemBank;
+
+/*
+ * Layout at virtual address 0.
+ */
+typedef struct Vectorpage {
+	void	(*vectors[8])(void);
+	uint	vtable[8];
+} Vectorpage;
+extern Vectorpage *page0;
--- /dev/null
+++ b/os/ipaq1110/defont.c
@@ -1,0 +1,290 @@
+#include "lib9.h"
+#include "draw.h"
+
+/*
+ * /tmp/latin1.6x13, in uncompressed form
+ */
+uchar
+defontdata[] =
+{
+ 0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x30,0x20,0x20,0x20,0x20,0x20,
+ 0x20,0x20,0x20,0x20,0x20,0x20,0x30,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,
+ 0x20,0x20,0x30,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x31,0x35,0x33,0x36,0x20,
+ 0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x31,0x33,0x20,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x2a,0xff,0xff,0xff,0xff,
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,
+ 0xff,0xff,0xff,0xff,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x18,0x00,0x00,0x00,0x00,0x00,0x41,0x0e,0x00,0x60,0xc2,0x0a,0x00,
+ 0x00,0x00,0x60,0xc2,0x00,0x60,0xc2,0x00,0x00,0xa6,0x0c,0x20,0xa0,0x00,0x01,0x83,
+ 0x08,0x00,0xc0,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x20,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x14,0xff,0xff,0xff,0xff,
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,
+ 0xff,0xff,0xff,0xff,0x0c,0x00,0x00,0x00,0x00,0x0c,0x00,0x03,0x00,0x00,0x00,0x3f,
+ 0x30,0x03,0x1c,0x30,0x00,0x00,0x00,0x83,0x00,0x41,0x02,0x00,0x31,0x85,0x14,0x51,
+ 0xc0,0x00,0x31,0x85,0x14,0x31,0x85,0x14,0x01,0x43,0x18,0x51,0x45,0x00,0x08,0xc6,
+ 0x14,0x51,0x86,0x1c,0x00,0x02,0x00,0x00,0x00,0x00,0x00,0x02,0x00,0x00,0x02,0x00,
+ 0x00,0x00,0x00,0x20,0x00,0x00,0x00,0x00,0x08,0x00,0x0c,0x00,0x73,0xe0,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x85,0x00,0x71,0x24,0x0c,0x11,0x00,0x00,0x00,0x00,0x02,
+ 0x20,0x87,0x3e,0x13,0xe7,0x3e,0x71,0xc0,0x00,0x08,0x08,0x1c,0x70,0x8f,0x1c,0xf3,
+ 0xef,0x9c,0x89,0xc3,0xa2,0x82,0x28,0x9c,0xf1,0xcf,0x1c,0xfa,0x28,0xa2,0x8a,0x2f,
+ 0x9c,0x81,0xc2,0x00,0x30,0x08,0x00,0x08,0x03,0x00,0x80,0x00,0x20,0x60,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x06,0x23,0x04,0xaa,0x8f,0xff,0xff,0xff,
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,
+ 0xff,0xff,0xff,0xff,0x0c,0x80,0x00,0x02,0x22,0x12,0x00,0xc0,0x80,0x00,0x03,0x00,
+ 0x48,0x04,0x82,0x60,0x06,0xc0,0x00,0x84,0x80,0x49,0x24,0x08,0x00,0x08,0x80,0x01,
+ 0x40,0x1c,0x00,0x00,0x00,0x00,0x00,0x00,0xf0,0x00,0x00,0x88,0x00,0x00,0x70,0x00,
+ 0x00,0x00,0x04,0x22,0x60,0xc5,0x0a,0x00,0x00,0x00,0x60,0xc5,0x00,0x60,0xc5,0x00,
+ 0x09,0x46,0x0c,0x51,0x40,0x00,0x01,0x83,0x14,0x00,0xc8,0x00,0x1b,0xe0,0x00,0x03,
+ 0x84,0x00,0xc0,0x00,0x00,0xf0,0x0e,0x38,0xc3,0x0c,0x30,0xc2,0x4e,0x38,0x63,0x8e,
+ 0x38,0xe3,0x8c,0x28,0x00,0x85,0x14,0xa2,0xaa,0x08,0x20,0x82,0x00,0x00,0x00,0x02,
+ 0x51,0x88,0x82,0x12,0x08,0x82,0x8a,0x20,0x00,0x10,0x04,0x22,0x89,0x44,0xa2,0x4a,
+ 0x08,0x22,0x88,0x81,0x22,0x82,0x2c,0xa2,0x8a,0x28,0xa2,0x22,0x28,0xa2,0x8a,0x20,
+ 0x90,0x80,0x45,0x00,0x10,0x08,0x00,0x08,0x04,0x80,0x80,0x81,0x20,0x20,0x00,0x00,
+ 0x00,0x00,0x00,0x40,0x00,0x00,0x00,0x00,0x08,0x20,0x8a,0x94,0x77,0xff,0xff,0x1e,
+ 0xff,0xcf,0xff,0xff,0xc3,0xfc,0x71,0xcf,0x3c,0xf3,0xcf,0x6c,0x71,0xe7,0x1c,0x71,
+ 0xc7,0x3c,0x73,0xd7,0x00,0x02,0x00,0x8a,0x22,0x10,0x51,0x23,0x82,0x00,0x04,0x80,
+ 0x48,0x01,0x0c,0x00,0x0a,0x80,0x00,0x84,0xa0,0x51,0x42,0x80,0x71,0xc7,0x1c,0x71,
+ 0xc7,0xa2,0xfb,0xef,0xbe,0x71,0xc7,0x1c,0x4a,0x27,0x1c,0x71,0xc7,0x00,0x9a,0x28,
+ 0xa2,0x8a,0x27,0x22,0x31,0x88,0x94,0x51,0xc0,0x00,0x31,0x88,0x94,0x31,0x88,0x94,
+ 0x7e,0x83,0x18,0x8a,0x85,0x0c,0x00,0xc6,0x22,0x51,0x88,0x14,0x9f,0xee,0x38,0xe2,
+ 0x0a,0x08,0xa2,0x88,0x22,0x81,0xc8,0x20,0xa2,0x8a,0x28,0xa3,0x48,0x20,0x92,0x08,
+ 0x20,0x82,0x0a,0x28,0x00,0x85,0x14,0xa1,0x4a,0x10,0x20,0x8a,0x88,0x00,0x00,0x04,
+ 0x8a,0x88,0x84,0x32,0x08,0x04,0x8a,0x22,0x08,0x20,0x02,0x22,0x8a,0x24,0xa0,0x4a,
+ 0x08,0x20,0x88,0x81,0x24,0x83,0x6c,0xa2,0x8a,0x28,0xa0,0x22,0x28,0xa2,0x51,0x41,
+ 0x10,0x40,0x48,0x80,0x08,0x08,0x00,0x08,0x04,0x00,0x80,0x00,0x20,0x20,0x00,0x00,
+ 0x00,0x00,0x00,0x40,0x00,0x00,0x00,0x00,0x08,0x20,0x89,0x2a,0x74,0x71,0xc7,0x7d,
+ 0x7d,0xd7,0x5d,0xf7,0x5f,0x8d,0xf7,0xd7,0x5d,0x75,0xd7,0x2d,0xf7,0xdb,0x7d,0xf7,
+ 0xdf,0x5d,0xf5,0xd7,0x00,0x82,0x0c,0x71,0x42,0x10,0x02,0x14,0x84,0x00,0x08,0x40,
+ 0x48,0x82,0x02,0x00,0x0a,0x80,0x00,0x83,0x10,0x20,0x8d,0x08,0x8a,0x28,0xa2,0x8a,
+ 0x2a,0x20,0x82,0x08,0x20,0x20,0x82,0x08,0x4b,0x28,0xa2,0x8a,0x28,0x80,0x9a,0x28,
+ 0xa2,0x89,0x44,0xa6,0x00,0x00,0x00,0x01,0x40,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x08,0x00,0x00,0x00,0x00,0x0c,0x08,0x00,0x00,0x00,0x08,0x00,0xff,0xe8,0x20,0x83,
+ 0x8a,0x1c,0xc2,0x88,0x22,0xe2,0x0e,0x38,0xa2,0x8a,0x28,0xa2,0xce,0x30,0x83,0x8e,
+ 0x38,0xe2,0x8c,0x28,0x00,0x80,0x3e,0x70,0x44,0x00,0x40,0x4f,0x88,0x00,0x00,0x04,
+ 0x88,0x80,0x88,0x52,0xc8,0x04,0x8a,0x27,0x1c,0x43,0xe1,0x02,0x9a,0x24,0xa0,0x4a,
+ 0x08,0x20,0x88,0x81,0x28,0x82,0xaa,0xa2,0x8a,0x28,0xa0,0x22,0x28,0xa2,0x51,0x41,
+ 0x10,0x40,0x40,0x00,0x01,0xcf,0x1c,0x79,0xc4,0x1c,0xb1,0x83,0x24,0x23,0x4b,0x1c,
+ 0xf1,0xeb,0x1c,0xf2,0x28,0xa2,0x8a,0x2f,0x88,0x20,0x80,0x14,0xf5,0xf7,0xdf,0x1d,
+ 0x78,0xcf,0x5d,0xf7,0x47,0x7c,0x71,0xd7,0x5d,0x75,0xd7,0x4c,0x73,0xdf,0x1c,0x71,
+ 0xc7,0x3d,0x73,0xd7,0x00,0x87,0x12,0x51,0x42,0x1c,0x02,0xd4,0x8a,0xf8,0x0b,0x40,
+ 0x30,0x87,0x9c,0x01,0x2a,0x80,0x00,0x80,0x28,0x49,0x42,0x88,0x8a,0x28,0xa2,0x8a,
+ 0x2a,0x20,0x82,0x08,0x20,0x20,0x82,0x08,0x4b,0x28,0xa2,0x8a,0x28,0xa1,0xaa,0x28,
+ 0xa2,0x89,0x44,0xac,0x71,0xc7,0x1c,0x71,0xcf,0x1c,0x71,0xc7,0x1c,0x61,0x86,0x18,
+ 0x7a,0xc7,0x1c,0x71,0xc7,0x00,0x72,0x28,0xa2,0x8a,0x2f,0x22,0x1b,0xee,0x38,0xc2,
+ 0x0e,0x1c,0xa3,0x88,0x14,0x82,0x02,0x08,0xa2,0x8a,0x28,0xa2,0x42,0x20,0x92,0x02,
+ 0x20,0x82,0x8a,0x28,0x00,0x80,0x14,0x28,0x8a,0x00,0x40,0x47,0x3e,0x03,0xe0,0x08,
+ 0x88,0x81,0x1c,0x53,0x2f,0x08,0x71,0xe2,0x08,0x80,0x00,0x84,0xaa,0x27,0x20,0x4b,
+ 0xcf,0x20,0xf8,0x81,0x30,0x82,0xaa,0xa2,0xf2,0x2f,0x1c,0x22,0x25,0x2a,0x20,0x82,
+ 0x10,0x20,0x40,0x00,0x00,0x28,0xa2,0x8a,0x2f,0x22,0xc8,0x81,0x28,0x22,0xac,0xa2,
+ 0x8a,0x2c,0xa2,0x42,0x28,0xa2,0x52,0x21,0x30,0x20,0x60,0x2a,0xec,0x71,0xcf,0x7c,
+ 0x78,0xd7,0x1d,0xfa,0xdf,0x7f,0x7d,0xd7,0x5d,0x75,0xd7,0x6f,0x77,0xdb,0x7f,0x77,
+ 0xdf,0x5d,0x75,0xd7,0x00,0x8a,0x90,0x50,0x80,0x12,0x02,0x93,0x94,0x09,0xea,0x40,
+ 0x03,0xe0,0x00,0x01,0x26,0x8c,0x00,0x07,0x94,0x9a,0xa5,0x90,0x8a,0x28,0xa2,0x8a,
+ 0x2b,0xa0,0xf3,0xcf,0x3c,0x20,0x82,0x08,0xea,0xa8,0xa2,0x8a,0x28,0x92,0xaa,0x28,
+ 0xa2,0x88,0x85,0xa6,0x08,0x20,0x82,0x08,0x22,0xa2,0x8a,0x28,0xa2,0x20,0x82,0x08,
+ 0x8b,0x28,0xa2,0x8a,0x28,0x9e,0x9a,0x28,0xa2,0x8a,0x28,0xa2,0x3b,0xe2,0x20,0x83,
+ 0x8a,0x14,0xc2,0x8e,0x08,0x81,0xce,0x38,0xc3,0x0c,0x30,0xc2,0x4e,0x38,0x63,0x8e,
+ 0x38,0x83,0x8a,0x10,0x00,0x80,0x3e,0x29,0x09,0x80,0x40,0x4f,0x88,0x00,0x00,0x10,
+ 0x88,0x82,0x02,0x90,0x28,0x88,0x88,0x20,0x00,0x40,0x01,0x08,0xab,0xe4,0xa0,0x4a,
+ 0x08,0x26,0x88,0x81,0x28,0x82,0x29,0xa2,0x82,0x2a,0x02,0x22,0x25,0x2a,0x50,0x84,
+ 0x10,0x10,0x40,0x00,0x01,0xe8,0xa0,0x8b,0xe4,0x22,0x88,0x81,0x30,0x22,0xa8,0xa2,
+ 0x8a,0x28,0x18,0x42,0x28,0xaa,0x22,0x22,0x08,0x20,0x80,0x14,0xdf,0x77,0xdf,0x1d,
+ 0x7a,0xcf,0x5c,0x7d,0xdf,0x8c,0x71,0xcf,0x3c,0xf3,0xcf,0x6c,0x71,0xe7,0x1c,0x71,
+ 0xdf,0x5c,0x75,0xef,0x00,0x8a,0x3c,0x53,0xe2,0x0e,0x02,0xd0,0x28,0x09,0xea,0x40,
+ 0x00,0x80,0x00,0x01,0x22,0x8c,0x00,0x00,0x0a,0x28,0x2a,0xa0,0xfb,0xef,0xbe,0xfb,
+ 0xee,0x20,0x82,0x08,0x20,0x20,0x82,0x08,0x4a,0xa8,0xa2,0x8a,0x28,0x8c,0xaa,0x28,
+ 0xa2,0x88,0x86,0x22,0x79,0xe7,0x9e,0x79,0xe7,0xa0,0xfb,0xef,0xbe,0x20,0x82,0x08,
+ 0x8a,0x28,0xa2,0x8a,0x28,0x9e,0xaa,0x28,0xa2,0x8a,0x28,0xa2,0x33,0xee,0x38,0xf0,
+ 0xc5,0x3e,0x72,0x87,0x00,0x79,0xc0,0x00,0x20,0x01,0x0e,0x18,0xa4,0x98,0x49,0x16,
+ 0x1c,0x71,0xc7,0x1c,0x00,0x80,0x14,0x71,0x49,0x00,0x20,0x8a,0x88,0x00,0x00,0x10,
+ 0x88,0x84,0x02,0xf8,0x28,0x90,0x88,0x20,0x00,0x23,0xe2,0x08,0xb2,0x24,0xa0,0x4a,
+ 0x08,0x22,0x88,0x81,0x24,0x82,0x29,0xa2,0x82,0x29,0x02,0x22,0x25,0x2a,0x50,0x84,
+ 0x10,0x10,0x40,0x00,0x02,0x28,0xa0,0x8a,0x04,0x22,0x88,0x81,0x28,0x22,0xa8,0xa2,
+ 0x8a,0x28,0x04,0x42,0x25,0x2a,0x22,0x64,0x08,0x20,0x80,0x2a,0xdc,0x71,0xc3,0xce,
+ 0xb0,0x63,0x5e,0x3f,0xe1,0x8f,0xff,0xf7,0xff,0xbc,0x79,0xd6,0xd9,0xed,0xba,0x78,
+ 0xe3,0x8e,0x38,0xe3,0x00,0x8a,0x08,0x50,0x82,0x02,0x02,0x17,0x94,0x08,0x08,0x40,
+ 0x00,0x80,0x00,0x01,0x22,0x80,0x00,0x00,0x14,0x48,0x44,0xa2,0x8a,0x28,0xa2,0x8a,
+ 0x2a,0x20,0x82,0x08,0x20,0x20,0x82,0x08,0x4a,0x68,0xa2,0x8a,0x28,0x8c,0xca,0x28,
+ 0xa2,0x88,0x84,0x22,0x8a,0x28,0xa2,0x8a,0x2a,0x20,0x82,0x08,0x20,0x20,0x82,0x08,
+ 0x8a,0x28,0xa2,0x8a,0x28,0x80,0xaa,0x28,0xa2,0x8a,0x68,0xa6,0x33,0xe4,0x92,0x41,
+ 0x25,0x08,0x41,0xc4,0x3e,0x41,0x23,0x04,0x20,0x42,0x82,0x28,0xa6,0x94,0x69,0xb5,
+ 0x10,0x41,0x04,0x10,0x00,0x00,0x14,0x22,0xa6,0x80,0x20,0x82,0x00,0x30,0x02,0x20,
+ 0x50,0x88,0x22,0x12,0x28,0x90,0x8a,0x22,0x0c,0x10,0x04,0x00,0x82,0x24,0xa2,0x4a,
+ 0x08,0x22,0x88,0x89,0x22,0x82,0x28,0xa2,0x82,0xa8,0xa2,0x22,0x22,0x36,0x88,0x88,
+ 0x10,0x08,0x40,0x00,0x02,0x28,0xa2,0x8a,0x24,0x1e,0x88,0x81,0x24,0x22,0xa8,0xa2,
+ 0xf1,0xe8,0x22,0x4a,0x65,0x2a,0x51,0xa8,0x08,0x20,0x80,0x14,0xfe,0xdb,0x6f,0xb6,
+ 0xbd,0xef,0x8e,0xf0,0x6f,0xb7,0x3e,0xf7,0xef,0x5f,0x75,0xd6,0x5a,0xe5,0x92,0xbb,
+ 0xef,0xbe,0xfb,0xef,0x00,0x8a,0xbc,0x73,0xe2,0x02,0x01,0x20,0x0a,0x00,0x04,0x80,
+ 0x03,0xe0,0x00,0x01,0x62,0x80,0x00,0x00,0x28,0x78,0x87,0xa2,0x8a,0x28,0xa2,0x8a,
+ 0x2a,0x22,0x82,0x08,0x20,0x20,0x82,0x08,0x4a,0x68,0xa2,0x8a,0x28,0x92,0xca,0x28,
+ 0xa2,0x88,0x84,0x26,0x8a,0x28,0xa2,0x8a,0x2a,0xa2,0x8a,0x28,0xa2,0x20,0x82,0x08,
+ 0x8a,0x28,0xa2,0x8a,0x28,0x8c,0xca,0x69,0xa6,0x99,0xa8,0x9a,0x03,0xe3,0x0c,0x61,
+ 0x26,0x00,0x70,0x86,0x08,0x71,0xc4,0x84,0x20,0x41,0x04,0x48,0xc5,0x98,0x59,0x56,
+ 0x1c,0x71,0xc7,0x1c,0x00,0x80,0x00,0x02,0x40,0x00,0x11,0x00,0x00,0x20,0x07,0x20,
+ 0x23,0xef,0x9c,0x11,0xc7,0x10,0x71,0xc7,0x08,0x08,0x08,0x08,0x7a,0x2f,0x1c,0xf3,
+ 0xe8,0x1c,0x89,0xc6,0x22,0xfa,0x28,0x9c,0x81,0xc8,0x9c,0x21,0xc2,0x22,0x88,0x8f,
+ 0x9c,0x09,0xc0,0x00,0x01,0xef,0x1c,0x79,0xc4,0x02,0x89,0xc9,0x22,0x72,0x28,0x9c,
+ 0x80,0x28,0x1c,0x31,0xa2,0x14,0x88,0x2f,0x86,0x23,0x00,0x2a,0xdf,0x3c,0xe7,0xb6,
+ 0x7f,0xe3,0xde,0x7d,0xe3,0x8e,0xde,0xf7,0xef,0xbe,0xed,0xce,0x99,0xe9,0xaa,0x78,
+ 0xe3,0x8e,0x38,0xe3,0xc0,0x87,0x2a,0x88,0x82,0x12,0x00,0xc0,0x04,0x00,0x03,0x00,
+ 0x00,0x00,0x00,0x01,0xa2,0x80,0x10,0x00,0x10,0x08,0xe0,0x9c,0x8a,0x28,0xa2,0x8a,
+ 0x2b,0x9c,0xfb,0xef,0xbe,0x71,0xc7,0x1c,0xf2,0x27,0x1c,0x71,0xc7,0x21,0x71,0xc7,
+ 0x1c,0x70,0x84,0x2c,0x79,0xe7,0x9e,0x79,0xe7,0x1c,0x71,0xc7,0x1c,0x71,0xc7,0x1c,
+ 0x7a,0x27,0x1c,0x71,0xc7,0x0c,0x71,0xa6,0x9a,0x68,0x2f,0x02,0x03,0xe3,0x0c,0x40,
+ 0xc5,0x00,0x10,0x84,0x08,0x41,0x44,0x84,0x20,0x42,0x02,0x7c,0xa4,0x94,0x49,0x15,
+ 0x04,0x10,0x41,0x04,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x40,0x02,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x02,0x10,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x20,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x3e,0x00,0x00,0x00,0x00,0x00,0x22,0x00,0x09,0x00,0x00,0x00,0x00,
+ 0x80,0x20,0x00,0x00,0x00,0x00,0x02,0x20,0x00,0x00,0x00,0x14,0xff,0x3c,0xef,0xce,
+ 0xbf,0xfb,0xde,0xfd,0xef,0xae,0xde,0xf7,0xef,0x7f,0x60,0xd6,0xda,0xed,0xba,0xbe,
+ 0xfb,0xef,0xbe,0xfb,0xc0,0x02,0x38,0x00,0x00,0x0c,0x00,0x00,0x02,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x02,0x00,0x00,0x10,0x00,0x20,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x04,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x80,0x00,
+ 0x00,0x00,0x00,0x20,0x00,0x00,0x00,0x00,0x00,0x04,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x80,0x00,0x00,0x02,0x28,0x22,0x00,0x04,0x92,0x40,
+ 0x24,0x80,0x70,0x84,0x08,0x41,0x23,0x04,0x38,0x43,0x8e,0x08,0x94,0x98,0x49,0x16,
+ 0x1c,0x71,0xc7,0x1c,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x1c,0x00,0x06,0x00,0x00,0x00,0x00,
+ 0x80,0x20,0x00,0x00,0x00,0x00,0x01,0xc0,0x00,0x00,0x00,0x2a,0x02,0xdb,0x6f,0xf6,
+ 0xdf,0xe3,0xde,0xfd,0xef,0xb7,0x3e,0xf1,0xef,0x1c,0x7d,0xda,0xd9,0xed,0xba,0x78,
+ 0xe3,0x8e,0x38,0xe3,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x70,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x1c,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x1c,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x01,0xc8,0x1c,
+ 0x20,0x20,0x20,
+ 0x20,0x20,0x20,0x20,0x20,0x32,0x35,0x36,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,
+ 0x20,0x20,0x31,0x33,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x31,0x31,
+ 0x20,0x00,0x00,0x02,0x0a,0x00,0x06,0x06,0x00,0x02,0x0c,0x00,0x06,0x0c,0x00,0x04,
+ 0x0d,0x00,0x06,0x12,0x00,0x04,0x0d,0x00,0x06,0x18,0x00,0x04,0x0d,0x00,0x06,0x1e,
+ 0x00,0x03,0x0d,0x00,0x06,0x24,0x00,0x03,0x0d,0x00,0x06,0x2a,0x00,0x04,0x0a,0x00,
+ 0x06,0x30,0x00,0x03,0x0d,0x00,0x06,0x36,0x00,0x04,0x0d,0x00,0x06,0x3c,0x00,0x04,
+ 0x0d,0x00,0x06,0x42,0x00,0x04,0x0d,0x00,0x06,0x48,0x00,0x03,0x0d,0x00,0x06,0x4e,
+ 0x00,0x04,0x0d,0x00,0x06,0x54,0x00,0x03,0x0d,0x00,0x06,0x5a,0x00,0x03,0x0d,0x00,
+ 0x06,0x60,0x00,0x03,0x0d,0x00,0x06,0x66,0x00,0x03,0x0d,0x00,0x06,0x6c,0x00,0x03,
+ 0x0d,0x00,0x06,0x72,0x00,0x03,0x0d,0x00,0x06,0x78,0x00,0x03,0x0d,0x00,0x06,0x7e,
+ 0x00,0x03,0x0d,0x00,0x06,0x84,0x00,0x03,0x0d,0x00,0x06,0x8a,0x00,0x03,0x0d,0x00,
+ 0x06,0x90,0x00,0x03,0x0d,0x00,0x06,0x96,0x00,0x03,0x0d,0x00,0x06,0x9c,0x00,0x03,
+ 0x0d,0x00,0x06,0xa2,0x00,0x03,0x0d,0x00,0x06,0xa8,0x00,0x03,0x0d,0x00,0x06,0xae,
+ 0x00,0x03,0x0d,0x00,0x06,0xb4,0x00,0x03,0x0d,0x00,0x06,0xba,0x00,0x03,0x0d,0x00,
+ 0x06,0xc0,0x00,0x00,0x00,0x00,0x06,0xc6,0x00,0x02,0x0b,0x00,0x06,0xcc,0x00,0x02,
+ 0x05,0x00,0x06,0xd2,0x00,0x03,0x0a,0x00,0x06,0xd8,0x00,0x01,0x0a,0x00,0x06,0xde,
+ 0x00,0x02,0x0b,0x00,0x06,0xe4,0x00,0x02,0x0a,0x00,0x06,0xea,0x00,0x02,0x05,0x00,
+ 0x06,0xf0,0x00,0x02,0x0b,0x00,0x06,0xf6,0x00,0x02,0x0b,0x00,0x06,0xfc,0x00,0x03,
+ 0x0a,0x00,0x06,0x02,0x01,0x04,0x09,0x00,0x06,0x08,0x01,0x09,0x0c,0x00,0x06,0x0e,
+ 0x01,0x06,0x07,0x00,0x06,0x14,0x01,0x09,0x0c,0x00,0x06,0x1a,0x01,0x02,0x0b,0x00,
+ 0x06,0x20,0x01,0x02,0x0b,0x00,0x06,0x26,0x01,0x02,0x0b,0x00,0x06,0x2c,0x01,0x02,
+ 0x0b,0x00,0x06,0x32,0x01,0x02,0x0b,0x00,0x06,0x38,0x01,0x02,0x0b,0x00,0x06,0x3e,
+ 0x01,0x02,0x0b,0x00,0x06,0x44,0x01,0x02,0x0b,0x00,0x06,0x4a,0x01,0x02,0x0b,0x00,
+ 0x06,0x50,0x01,0x02,0x0b,0x00,0x06,0x56,0x01,0x02,0x0b,0x00,0x06,0x5c,0x01,0x00,
+ 0x0d,0x00,0x06,0x62,0x01,0x00,0x0d,0x00,0x06,0x68,0x01,0x02,0x0b,0x00,0x06,0x6e,
+ 0x01,0x00,0x0d,0x00,0x06,0x74,0x01,0x02,0x0b,0x00,0x06,0x7a,0x01,0x02,0x0b,0x00,
+ 0x06,0x80,0x01,0x02,0x0b,0x00,0x06,0x86,0x01,0x02,0x0b,0x00,0x06,0x8c,0x01,0x02,
+ 0x0b,0x00,0x06,0x92,0x01,0x02,0x0b,0x00,0x06,0x98,0x01,0x02,0x0b,0x00,0x06,0x9e,
+ 0x01,0x02,0x0b,0x00,0x06,0xa4,0x01,0x02,0x0b,0x00,0x06,0xaa,0x01,0x02,0x0b,0x00,
+ 0x06,0xb0,0x01,0x02,0x0b,0x00,0x06,0xb6,0x01,0x02,0x0b,0x00,0x06,0xbc,0x01,0x02,
+ 0x0b,0x00,0x06,0xc2,0x01,0x02,0x0b,0x00,0x06,0xc8,0x01,0x02,0x0b,0x00,0x06,0xce,
+ 0x01,0x02,0x0b,0x00,0x06,0xd4,0x01,0x02,0x0b,0x00,0x06,0xda,0x01,0x02,0x0b,0x00,
+ 0x06,0xe0,0x01,0x02,0x0b,0x00,0x06,0xe6,0x01,0x02,0x0c,0x00,0x06,0xec,0x01,0x02,
+ 0x0b,0x00,0x06,0xf2,0x01,0x02,0x0b,0x00,0x06,0xf8,0x01,0x02,0x0b,0x00,0x06,0xfe,
+ 0x01,0x02,0x0b,0x00,0x06,0x04,0x02,0x02,0x0b,0x00,0x06,0x0a,0x02,0x02,0x0b,0x00,
+ 0x06,0x10,0x02,0x02,0x0b,0x00,0x06,0x16,0x02,0x02,0x0b,0x00,0x06,0x1c,0x02,0x02,
+ 0x0b,0x00,0x06,0x22,0x02,0x02,0x0b,0x00,0x06,0x28,0x02,0x02,0x0b,0x00,0x06,0x2e,
+ 0x02,0x02,0x0b,0x00,0x06,0x34,0x02,0x02,0x05,0x00,0x06,0x3a,0x02,0x0b,0x0c,0x00,
+ 0x06,0x40,0x02,0x02,0x05,0x00,0x06,0x46,0x02,0x05,0x0b,0x00,0x06,0x4c,0x02,0x02,
+ 0x0b,0x00,0x06,0x52,0x02,0x05,0x0b,0x00,0x06,0x58,0x02,0x02,0x0b,0x00,0x06,0x5e,
+ 0x02,0x05,0x0b,0x00,0x06,0x64,0x02,0x02,0x0b,0x00,0x06,0x6a,0x02,0x05,0x0d,0x00,
+ 0x06,0x70,0x02,0x02,0x0b,0x00,0x06,0x76,0x02,0x03,0x0b,0x00,0x06,0x7c,0x02,0x03,
+ 0x0d,0x00,0x06,0x82,0x02,0x02,0x0b,0x00,0x06,0x88,0x02,0x02,0x0b,0x00,0x06,0x8e,
+ 0x02,0x05,0x0b,0x00,0x06,0x94,0x02,0x05,0x0b,0x00,0x06,0x9a,0x02,0x05,0x0b,0x00,
+ 0x06,0xa0,0x02,0x05,0x0d,0x00,0x06,0xa6,0x02,0x05,0x0d,0x00,0x06,0xac,0x02,0x05,
+ 0x0b,0x00,0x06,0xb2,0x02,0x05,0x0b,0x00,0x06,0xb8,0x02,0x03,0x0b,0x00,0x06,0xbe,
+ 0x02,0x05,0x0b,0x00,0x06,0xc4,0x02,0x05,0x0b,0x00,0x06,0xca,0x02,0x05,0x0b,0x00,
+ 0x06,0xd0,0x02,0x05,0x0b,0x00,0x06,0xd6,0x02,0x05,0x0d,0x00,0x06,0xdc,0x02,0x05,
+ 0x0b,0x00,0x06,0xe2,0x02,0x02,0x0b,0x00,0x06,0xe8,0x02,0x02,0x0b,0x00,0x06,0xee,
+ 0x02,0x02,0x0b,0x00,0x06,0xf4,0x02,0x02,0x05,0x00,0x06,0xfa,0x02,0x00,0x0d,0x00,
+ 0x06,0x00,0x03,0x00,0x0c,0x00,0x06,0x06,0x03,0x00,0x0d,0x00,0x06,0x0c,0x03,0x00,
+ 0x0d,0x00,0x06,0x12,0x03,0x00,0x0d,0x00,0x06,0x18,0x03,0x00,0x0d,0x00,0x06,0x1e,
+ 0x03,0x00,0x0d,0x00,0x06,0x24,0x03,0x00,0x0d,0x00,0x06,0x2a,0x03,0x00,0x0d,0x00,
+ 0x06,0x30,0x03,0x00,0x0d,0x00,0x06,0x36,0x03,0x00,0x0d,0x00,0x06,0x3c,0x03,0x00,
+ 0x0d,0x00,0x06,0x42,0x03,0x00,0x0d,0x00,0x06,0x48,0x03,0x00,0x0d,0x00,0x06,0x4e,
+ 0x03,0x00,0x0d,0x00,0x06,0x54,0x03,0x00,0x0d,0x00,0x06,0x5a,0x03,0x00,0x0d,0x00,
+ 0x06,0x60,0x03,0x00,0x0d,0x00,0x06,0x66,0x03,0x00,0x0d,0x00,0x06,0x6c,0x03,0x00,
+ 0x0d,0x00,0x06,0x72,0x03,0x00,0x0d,0x00,0x06,0x78,0x03,0x00,0x0d,0x00,0x06,0x7e,
+ 0x03,0x00,0x0d,0x00,0x06,0x84,0x03,0x00,0x0d,0x00,0x06,0x8a,0x03,0x00,0x0d,0x00,
+ 0x06,0x90,0x03,0x00,0x0d,0x00,0x06,0x96,0x03,0x00,0x0d,0x00,0x06,0x9c,0x03,0x00,
+ 0x0d,0x00,0x06,0xa2,0x03,0x00,0x0d,0x00,0x06,0xa8,0x03,0x00,0x0d,0x00,0x00,0xae,
+ 0x03,0x00,0x0d,0x00,0x00,0xb4,0x03,0x00,0x0d,0x00,0x00,0xba,0x03,0x00,0x0d,0x00,
+ 0x00,0xc0,0x03,0x01,0x0c,0x00,0x06,0xc6,0x03,0x02,0x0b,0x00,0x06,0xcc,0x03,0x03,
+ 0x0c,0x00,0x06,0xd2,0x03,0x04,0x0c,0x00,0x06,0xd8,0x03,0x03,0x0b,0x00,0x06,0xde,
+ 0x03,0x02,0x0b,0x00,0x06,0xe4,0x03,0x02,0x0b,0x00,0x06,0xea,0x03,0x01,0x0c,0x00,
+ 0x06,0xf0,0x03,0x03,0x04,0x00,0x06,0xf6,0x03,0x02,0x0b,0x00,0x06,0xfc,0x03,0x01,
+ 0x09,0x00,0x06,0x02,0x04,0x03,0x0c,0x00,0x06,0x08,0x04,0x05,0x09,0x00,0x06,0x0e,
+ 0x04,0x06,0x08,0x00,0x06,0x14,0x04,0x02,0x0b,0x00,0x06,0x1a,0x04,0x01,0x02,0x00,
+ 0x06,0x20,0x04,0x01,0x06,0x00,0x06,0x26,0x04,0x04,0x0a,0x00,0x06,0x2c,0x04,0x01,
+ 0x06,0x00,0x06,0x32,0x04,0x01,0x06,0x00,0x06,0x38,0x04,0x00,0x03,0x00,0x06,0x3e,
+ 0x04,0x05,0x0c,0x00,0x06,0x44,0x04,0x02,0x0b,0x00,0x06,0x4a,0x04,0x06,0x08,0x00,
+ 0x06,0x50,0x04,0x0a,0x0d,0x00,0x06,0x56,0x04,0x01,0x06,0x00,0x06,0x5c,0x04,0x01,
+ 0x07,0x00,0x06,0x62,0x04,0x03,0x0c,0x00,0x06,0x68,0x04,0x00,0x0b,0x00,0x06,0x6e,
+ 0x04,0x00,0x0b,0x00,0x06,0x74,0x04,0x00,0x0b,0x00,0x06,0x7a,0x04,0x02,0x0b,0x00,
+ 0x06,0x80,0x04,0x00,0x0b,0x00,0x06,0x86,0x04,0x00,0x0b,0x00,0x06,0x8c,0x04,0x00,
+ 0x0b,0x00,0x06,0x92,0x04,0x00,0x0b,0x00,0x06,0x98,0x04,0x01,0x0b,0x00,0x06,0x9e,
+ 0x04,0x01,0x0b,0x00,0x06,0xa4,0x04,0x03,0x0b,0x00,0x06,0xaa,0x04,0x02,0x0d,0x00,
+ 0x06,0xb0,0x04,0x00,0x0b,0x00,0x06,0xb6,0x04,0x00,0x0b,0x00,0x06,0xbc,0x04,0x00,
+ 0x0b,0x00,0x06,0xc2,0x04,0x01,0x0b,0x00,0x06,0xc8,0x04,0x00,0x0b,0x00,0x06,0xce,
+ 0x04,0x00,0x0b,0x00,0x06,0xd4,0x04,0x00,0x0b,0x00,0x06,0xda,0x04,0x01,0x0b,0x00,
+ 0x06,0xe0,0x04,0x02,0x0b,0x00,0x06,0xe6,0x04,0x00,0x0b,0x00,0x06,0xec,0x04,0x00,
+ 0x0b,0x00,0x06,0xf2,0x04,0x00,0x0b,0x00,0x06,0xf8,0x04,0x00,0x0b,0x00,0x06,0xfe,
+ 0x04,0x00,0x0b,0x00,0x06,0x04,0x05,0x01,0x0b,0x00,0x06,0x0a,0x05,0x05,0x0b,0x00,
+ 0x06,0x10,0x05,0x01,0x0c,0x00,0x06,0x16,0x05,0x00,0x0b,0x00,0x06,0x1c,0x05,0x00,
+ 0x0b,0x00,0x06,0x22,0x05,0x00,0x0b,0x00,0x06,0x28,0x05,0x01,0x0b,0x00,0x06,0x2e,
+ 0x05,0x00,0x0b,0x00,0x06,0x34,0x05,0x01,0x0b,0x00,0x06,0x3a,0x05,0x01,0x0c,0x00,
+ 0x06,0x40,0x05,0x02,0x0b,0x00,0x06,0x46,0x05,0x02,0x0b,0x00,0x06,0x4c,0x05,0x01,
+ 0x0b,0x00,0x06,0x52,0x05,0x02,0x0b,0x00,0x06,0x58,0x05,0x03,0x0b,0x00,0x06,0x5e,
+ 0x05,0x03,0x0b,0x00,0x06,0x64,0x05,0x05,0x0b,0x00,0x06,0x6a,0x05,0x05,0x0d,0x00,
+ 0x06,0x70,0x05,0x02,0x0b,0x00,0x06,0x76,0x05,0x02,0x0b,0x00,0x06,0x7c,0x05,0x01,
+ 0x0b,0x00,0x06,0x82,0x05,0x03,0x0b,0x00,0x06,0x88,0x05,0x02,0x0b,0x00,0x06,0x8e,
+ 0x05,0x02,0x0b,0x00,0x06,0x94,0x05,0x01,0x0b,0x00,0x06,0x9a,0x05,0x03,0x0b,0x00,
+ 0x06,0xa0,0x05,0x02,0x0b,0x00,0x06,0xa6,0x05,0x02,0x0b,0x00,0x06,0xac,0x05,0x02,
+ 0x0b,0x00,0x06,0xb2,0x05,0x02,0x0b,0x00,0x06,0xb8,0x05,0x01,0x0b,0x00,0x06,0xbe,
+ 0x05,0x02,0x0b,0x00,0x06,0xc4,0x05,0x03,0x0b,0x00,0x06,0xca,0x05,0x03,0x0b,0x00,
+ 0x06,0xd0,0x05,0x04,0x0c,0x00,0x06,0xd6,0x05,0x02,0x0b,0x00,0x06,0xdc,0x05,0x02,
+ 0x0b,0x00,0x06,0xe2,0x05,0x01,0x0b,0x00,0x06,0xe8,0x05,0x03,0x0b,0x00,0x06,0xee,
+ 0x05,0x02,0x0d,0x00,0x06,0xf4,0x05,0x01,0x0d,0x00,0x06,0xfa,0x05,0x03,0x0d,0x00,
+ 0x06,0x00,0x06,0x00,0x00,0x00,0x00,
+ 
+
+};
+
+int	sizeofdefont = sizeof defontdata;
+
+void
+_unpackinfo(Fontchar *fc, uchar *p, int n)
+{
+	int j;
+
+	for(j=0;  j<=n;  j++){
+		fc->x = p[0]|(p[1]<<8);
+		fc->top = p[2];
+		fc->bottom = p[3];
+		fc->left = p[4];
+		fc->width = p[5];
+		fc++;
+		p += 6;
+	}
+}
--- /dev/null
+++ b/os/ipaq1110/devaudio.c
@@ -1,0 +1,1056 @@
+/*
+ *	SAC/UDA 1341 Audio driver for the Bitsy
+ *
+ *	This code is covered by the Lucent Public Licence 1.02 (http://plan9.bell-labs.com/plan9dist/license.html);
+ *	see the file NOTICE in the current directory.  Modifications for the Inferno environment by Vita Nuova.
+ *
+ *	The Philips UDA 1341 sound chip is accessed through the Serial Audio
+ *	Controller (SAC) of the StrongARM SA-1110.
+ *
+ *	The code morphs Nicolas Pitre's <nico@cam.org> Linux controller
+ *	and Ken's Soundblaster controller.
+ *
+ *	The interface should be identical to that of devaudio.c
+ */
+#include	"u.h"
+#include	"../port/lib.h"
+#include	"mem.h"
+#include	"dat.h"
+#include	"fns.h"
+#include	"../port/error.h"
+#include	"io.h"
+
+static int debug = 0;
+
+/* UDA 1341 Registers */
+enum {
+	/* Status0 register */
+	UdaStatusDC		= 0,	/* 1 bit */
+	UdaStatusIF		= 1,	/* 3 bits */
+	UdaStatusSC		= 4,	/* 2 bits */
+	UdaStatusRST		= 6,	/* 1 bit */
+};
+
+enum {
+	/* Status1 register */
+	UdaStatusPC	= 0,	/* 2 bits */
+	UdaStatusDS	= 2,	/* 1 bit */
+	UdaStatusPDA	= 3,	/* 1 bit */
+	UdaStatusPAD	= 4,	/* 1 bit */
+	UdaStatusIGS	= 5,	/* 1 bit */
+	UdaStatusOGS	= 6,	/* 1 bit */
+};
+
+/*
+ * UDA1341 L3 address and command types
+ */
+
+enum {
+	UDA1341_DATA0 =	0,
+	UDA1341_DATA1,
+	UDA1341_STATUS,
+	UDA1341_L3Addr = 0x14,
+};
+
+typedef struct	AQueue	AQueue;
+typedef struct	Buf	Buf;
+typedef struct	IOstate IOstate;
+
+enum
+{
+	Qdir		= 0,
+	Qaudio,
+	Qvolume,
+	Qstatus,
+	Qaudioctl,
+
+	Fmono		= 1,
+	Fin			= 2,
+	Fout		= 4,
+
+	Aclosed		= 0,
+	Aread,
+	Awrite,
+
+	Vaudio		= 0,
+	Vmic,
+	Vtreb,
+	Vbass,
+	Vspeed,
+	Vfilter,
+	Vinvert,
+	Nvol,
+
+	Bufsize		= 4*1024,	/* 46 ms each */
+	Nbuf		= 32,		/* 1.5 seconds total */
+
+	Speed		= 44100,
+	Ncmd		= 50,		/* max volume command words */
+};
+
+Dirtab
+audiodir[] =
+{
+	".",		{Qdir, 0, QTDIR},	0,	0555,
+	"audio",	{Qaudio},		0,	0666,
+	"volume",	{Qvolume},		0,	0666,
+	"audioctl", {Qaudioctl},		0,	0666,
+	"audiostat",{Qstatus},		0,	0444,
+};
+
+struct	Buf
+{
+	uchar*	virt;
+	ulong	phys;
+	uint	nbytes;
+};
+
+struct	IOstate
+{
+	QLock;
+	Lock			ilock;
+	Rendez			vous;
+	Chan			*chan;			/* chan of open */
+	Dma*				dma;			/* dma chan, alloc on open, free on close */
+	int				bufinit;		/* boolean, if buffers allocated */
+	Buf				buf[Nbuf];		/* buffers and queues */
+	volatile Buf	*current;		/* next dma to finish */
+	volatile Buf	*next;			/* next candidate for dma */
+	volatile Buf	*filling;		/* buffer being filled */
+/* just be be cute (and to have defines like linux, a real operating system) */
+#define emptying filling
+};
+
+static	struct
+{
+	QLock;
+	int		amode;			/* Aclosed/Aread/Awrite for /audio */
+	int		intr;			/* boolean an interrupt has happened */
+	int		rivol[Nvol];	/* right/left input/output volumes */
+	int		livol[Nvol];
+	int		rovol[Nvol];
+	int		lovol[Nvol];
+	uvlong	totcount;		/* how many bytes processed since open */
+	vlong	tottime;		/* time at which totcount bytes were processed */
+	int	clockout;	/* need steady output to provide input clock */
+	IOstate	i;
+	IOstate	o;
+} audio;
+
+static struct
+{
+	ulong	bytes;
+	ulong	totaldma;
+	ulong	idledma;
+	ulong	faildma;
+	ulong	samedma;
+} iostats;
+
+static	struct
+{
+	char*	name;
+	int	flag;
+	int	ilval;		/* initial values */
+	int	irval;
+} volumes[] =
+{
+[Vaudio]	{"audio",	Fout|Fmono,	 80,	 80},
+[Vmic]		{"mic",		Fin|Fmono,	  0,	  0},
+[Vtreb]		{"treb",	Fout|Fmono,	 50,	 50},
+[Vbass]		{"bass",	Fout|Fmono, 	 50,	 50},
+[Vspeed]	{"speed",	Fin|Fout|Fmono,	Speed,	Speed},
+[Vfilter]	{"filter",	Fout|Fmono,	  0,	  0},
+[Vinvert]	{"invert",	Fin|Fout|Fmono,	  0,	  0},
+[Nvol]		{0}
+};
+
+static void	setreg(char *name, int val, int n);
+
+static	char	Emode[]		= "illegal open mode";
+static	char	Evolume[]	= "illegal volume specifier";
+
+static void
+bufinit(IOstate *b)
+{
+	int i;
+
+	if (debug) print("bufinit\n");
+	for (i = 0; i < Nbuf; i++) {
+		b->buf[i].virt = xspanalloc(Bufsize, CACHELINESZ, 0);
+		b->buf[i].phys = PADDR(b->buf[i].virt);
+	}
+	b->bufinit = 1;
+};
+
+static void
+setempty(IOstate *b)
+{
+	int i;
+
+	if (debug) print("setempty\n");
+	for (i = 0; i < Nbuf; i++) {
+		b->buf[i].nbytes = 0;
+	}
+	b->filling = b->buf;
+	b->current = b->buf;
+	b->next = b->buf;
+}
+
+static int
+audioqnotempty(void *x)
+{
+	IOstate *s = x;
+
+	return dmaidle(s->dma) || s->emptying != s->current;
+}
+
+static int
+audioqnotfull(void *x)
+{
+	IOstate *s = x;
+
+	return dmaidle(s->dma) || s->filling != s->current;
+}
+
+static void
+audioreset(void)
+{
+	/* Turn MCP operations off */
+	MCPREG->mccr = 0;
+}
+
+uchar	status0[1]		= {0x22};
+uchar	status1[1]		= {0x80};
+uchar	data00[1]		= {0x00};		/* volume control, bits 0 – 5 */
+uchar	data01[1]		= {0x40};
+uchar	data02[1]		= {0x80};
+uchar	data0e0[2]	= {0xc0, 0xe0};
+uchar	data0e1[2]	= {0xc1, 0xe0};
+uchar	data0e2[2]	= {0xc2, 0xf2};
+/* there is no data0e3 */
+uchar	data0e4[2]	= {0xc4, 0xe0};
+uchar	data0e5[2]	= {0xc5, 0xe0};
+uchar	data0e6[2]	= {0xc6, 0xe3};
+
+static void
+enable(void)
+{
+	uchar	data[1];
+	int cs;
+
+	L3init();
+
+	PPCREG->ppar &= ~PPAR_SPR;
+
+	/* external clock and ssp configured for current samples/sec */
+	cs = archaudiospeed(audio.livol[Vspeed], 1);
+	status0[0] = (status0[0] & ~(3<<4)) | (cs<<4);
+
+	/* Enable the audio power */
+	archaudiopower(1);
+//	egpiobits(EGPIO_audio_ic_power | EGPIO_codec_reset, 1);
+
+	/* Wait for the UDA1341 to wake up */
+	delay(100);
+
+	/* Reset the chip */
+	data[0] = status0[0] | 1<<UdaStatusRST;
+	L3write(UDA1341_L3Addr | UDA1341_STATUS, data, 1 );
+	archcodecreset();
+
+	/* write uda 1341 status[0] */
+	L3write(UDA1341_L3Addr | UDA1341_STATUS, status0, 1 );
+	L3write(UDA1341_L3Addr | UDA1341_STATUS, status1, 1);
+	L3write(UDA1341_L3Addr | UDA1341_DATA0, data02, 1);
+	L3write(UDA1341_L3Addr | UDA1341_DATA0, data0e2, 2);
+	L3write(UDA1341_L3Addr | UDA1341_DATA0, data0e6, 2 );
+
+	if (debug) {
+		print("enable:	status0	= 0x%2.2ux\n", status0[0]);
+		print("enable:	status1	= 0x%2.2ux\n", status1[0]);
+		print("enable:	data02	= 0x%2.2ux\n", data02[0]);
+		print("enable:	data0e2	= 0x%4.4ux\n", data0e2[0] | data0e2[1]<<8);
+		print("enable:	data0e4	= 0x%4.4ux\n", data0e4[0] | data0e4[1]<<8);
+		print("enable:	data0e6	= 0x%4.4ux\n", data0e6[0] | data0e6[1]<<8);
+	}
+}
+
+static void
+disable(void)
+{
+	SSPREG->sscr0 = 0x031f;	/* disable */
+}
+
+static void
+resetlevel(void)
+{
+	int i;
+
+	for(i=0; volumes[i].name; i++) {
+		audio.lovol[i] = volumes[i].ilval;
+		audio.rovol[i] = volumes[i].irval;
+		audio.livol[i] = volumes[i].ilval;
+		audio.rivol[i] = volumes[i].irval;
+	}
+}
+
+static void
+mxvolume(void) {
+	int *left, *right;
+	int cs;
+
+	cs = archaudiospeed(audio.livol[Vspeed], 1);
+	status0[0] = (status0[0] & ~(3<<4)) | (cs<<4);
+	L3write(UDA1341_L3Addr | UDA1341_STATUS, status0, 1);
+	if(debug)
+		print("mxvolume:	status0	= %2.2ux\n", status0[0]);
+	if(audio.amode & Aread){
+		left = audio.livol;
+		right = audio.rivol;
+		if (left[Vmic]+right[Vmic] == 0) {
+			/* Turn on automatic gain control (AGC) */
+			data0e4[1] |= 0x10;
+			L3write(UDA1341_L3Addr | UDA1341_DATA0, data0e4, 2 );
+		} else {
+			int v;
+			/* Turn on manual gain control */
+			v = ((left[Vmic]+right[Vmic])*0x7f/200)&0x7f;
+			data0e4[1] &= ~0x13;
+			data0e5[1] &= ~0x1f;
+			data0e4[1] |= v & 0x3;
+			data0e5[0] |= (v & 0x7c)<<6;
+			data0e5[1] |= (v & 0x7c)>>2;
+			L3write(UDA1341_L3Addr | UDA1341_DATA0, data0e4, 2 );
+			L3write(UDA1341_L3Addr | UDA1341_DATA0, data0e5, 2 );
+		}
+		if (left[Vinvert]+right[Vinvert] == 0)
+			status1[0] &= ~0x10;
+		else
+			status1[0] |= 0x10;
+		L3write(UDA1341_L3Addr | UDA1341_STATUS, status1, 1);
+		if (debug) {
+			print("mxvolume:	status1	= 0x%2.2ux\n", status1[0]);
+			print("mxvolume:	data0e4	= 0x%4.4ux\n", data0e4[0]|data0e4[0]<<8);
+			print("mxvolume:	data0e5	= 0x%4.4ux\n", data0e5[0]|data0e5[0]<<8);
+		}
+	}
+	if(audio.amode & Awrite){
+		left = audio.lovol;
+		right = audio.rovol;
+		data00[0] &= ~0x3f;
+		data00[0] |= ((200-left[Vaudio]-right[Vaudio])*0x3f/200)&0x3f;
+		if (left[Vtreb]+right[Vtreb] <= 100
+		 && left[Vbass]+right[Vbass] <= 100)
+			/* settings neutral */
+			data02[0] &= ~0x03;
+		else {
+			data02[0] |= 0x03;
+			data01[0] &= ~0x3f;
+			data01[0] |= ((left[Vtreb]+right[Vtreb]-100)*0x3/100)&0x03;
+			data01[0] |= (((left[Vbass]+right[Vbass]-100)*0xf/100)&0xf)<<2;
+		}
+		if (left[Vfilter]+right[Vfilter] == 0)
+			data02[0] &= ~0x10;
+		else
+			data02[0]|= 0x10;
+		if (left[Vinvert]+right[Vinvert] == 0)
+			status1[0] &= ~0x8;
+		else
+			status1[0] |= 0x8;
+		L3write(UDA1341_L3Addr | UDA1341_STATUS, status1, 1);
+		L3write(UDA1341_L3Addr | UDA1341_DATA0, data00, 1);
+		L3write(UDA1341_L3Addr | UDA1341_DATA0, data01, 1);
+		L3write(UDA1341_L3Addr | UDA1341_DATA0, data02, 1);
+		if (debug) {
+			print("mxvolume:	status1	= 0x%2.2ux\n", status1[0]);
+			print("mxvolume:	data00	= 0x%2.2ux\n", data00[0]);
+			print("mxvolume:	data01	= 0x%2.2ux\n", data01[0]);
+			print("mxvolume:	data02	= 0x%2.2ux\n", data02[0]);
+		}
+	}
+}
+
+static void
+setreg(char *name, int val, int n)
+{
+	uchar x[2];
+	int i;
+
+	if(strcmp(name, "pause") == 0){
+		for(i = 0; i < n; i++)
+			microdelay(val);
+		return;
+	}
+
+	x[0] = val;
+	x[1] = val>>8;
+
+	switch(n){
+	case 1:
+	case 2:
+		break;
+	default:
+		error("setreg");
+	}
+
+	if(strcmp(name, "status") == 0){
+		L3write(UDA1341_L3Addr | UDA1341_STATUS, x, n);
+	} else if(strcmp(name, "data0") == 0){
+		L3write(UDA1341_L3Addr | UDA1341_DATA0, x, n);
+	} else if(strcmp(name, "data1") == 0){
+		L3write(UDA1341_L3Addr | UDA1341_DATA1, x, n);
+	} else
+		error("setreg");
+}
+
+static void
+outenable(void) {
+	/* turn on DAC, set output gain switch */
+	archaudioamp(1);
+	archaudiomute(0);
+	status1[0] |= 0x41;
+	L3write(UDA1341_L3Addr | UDA1341_STATUS, status1, 1);
+	/* set volume */
+	data00[0] |= 0xf;
+	L3write(UDA1341_L3Addr | UDA1341_DATA0, data00, 1);
+	if (debug) {
+		print("outenable:	status1	= 0x%2.2ux\n", status1[0]);
+		print("outenable:	data00	= 0x%2.2ux\n", data00[0]);
+	}
+}
+
+static void
+outdisable(void) {
+	archaudiomute(1);
+	dmastop(audio.o.dma);
+	/* turn off DAC, clear output gain switch */
+	archaudioamp(0);
+	status1[0] &= ~0x41;
+	L3write(UDA1341_L3Addr | UDA1341_STATUS, status1, 1);
+	if (debug) {
+		print("outdisable:	status1	= 0x%2.2ux\n", status1[0]);
+	}
+//	egpiobits(EGPIO_audio_power, 0);
+}
+
+static void
+inenable(void) {
+	/* turn on ADC, set input gain switch */
+	status1[0] |= 0x22;
+	L3write(UDA1341_L3Addr | UDA1341_STATUS, status1, 1);
+	if (debug) {
+		print("inenable:	status1	= 0x%2.2ux\n", status1[0]);
+	}
+}
+
+static void
+indisable(void) {
+	dmastop(audio.i.dma);
+	/* turn off ADC, clear input gain switch */
+	status1[0] &= ~0x22;
+	L3write(UDA1341_L3Addr | UDA1341_STATUS, status1, 1);
+	if (debug) {
+		print("indisable:	status1	= 0x%2.2ux\n", status1[0]);
+	}
+}
+
+static void
+sendaudio(IOstate *s) {
+	/* interrupt routine calls this too */
+	int n;
+
+	if (debug > 1) print("#A: sendaudio\n");
+	ilock(&s->ilock);
+	while (s->next != s->filling) {
+		assert(s->next->nbytes);
+		if ((n = dmastart(s->dma, (void*)s->next->phys, s->next->nbytes)) == 0) {
+			iostats.faildma++;
+			break;
+		}
+		iostats.totaldma++;
+		switch (n) {
+		case 1:
+			iostats.idledma++;
+			break;
+		case 3:
+			iostats.faildma++;
+			break;
+		}
+		if (debug) {
+			if (debug > 1)
+				print("dmastart @%p\n", s->next);
+			else
+				iprint("+");
+		}
+		s->next->nbytes = 0;
+		s->next++;
+		if (s->next == &s->buf[Nbuf])
+			s->next = &s->buf[0];
+	}
+	iunlock(&s->ilock);
+}
+
+static void
+recvaudio(IOstate *s) {
+	/* interrupt routine calls this too */
+	int n;
+
+	if (debug > 1) print("#A: recvaudio\n");
+	ilock(&s->ilock);
+	while (s->next != s->emptying) {
+		assert(s->next->nbytes == 0);
+		if ((n = dmastart(s->dma, (void*)s->next->phys, Bufsize)) == 0) {
+			iostats.faildma++;
+			break;
+		}
+		iostats.totaldma++;
+		switch (n) {
+		case 1:
+			iostats.idledma++;
+			break;
+		case 3:
+			iostats.faildma++;
+			break;
+		}
+		if (debug) {
+			if (debug > 1)
+				print("dmastart @%p\n", s->next);
+			else
+				iprint("+");
+		}
+		s->next++;
+		if (s->next == &s->buf[Nbuf])
+			s->next = &s->buf[0];
+	}
+	iunlock(&s->ilock);
+}
+
+static void
+audiopower(int flag) {
+	IOstate *s;
+
+	if (debug) {
+		iprint("audiopower %d\n", flag);
+	}
+	if (flag) {
+		/* power on only when necessary */
+		if (audio.amode) {
+			archaudiopower(1);
+			enable();
+			if (audio.amode & Aread) {
+				inenable();
+				s = &audio.i;
+				dmastop(s->dma);
+				recvaudio(s);
+			}
+			if (audio.amode & Awrite) {
+				outenable();
+				s = &audio.o;
+				dmastop(s->dma);
+				sendaudio(s);
+			}
+			mxvolume();
+		}
+	} else {
+		/* power off */
+		if (audio.amode & Aread)
+			indisable();
+		if (audio.amode & Awrite)
+			outdisable();
+		disable();
+		archaudiopower(0);
+	}
+}
+
+static void
+audiointr(void *x, ulong ndma) {
+	IOstate *s = x;
+
+	if (debug) {
+		if (debug > 1)
+			iprint("#A: audio interrupt @%p\n", s->current);
+		else
+			iprint("-");
+	}
+	/* Only interrupt routine touches s->current */
+	s->current++;
+	if (s->current == &s->buf[Nbuf])
+		s->current = &s->buf[0];
+	if (ndma > 0) {
+		if (s == &audio.o)
+			sendaudio(s);
+		else if (s == &audio.i)
+			recvaudio(s);
+	}
+	wakeup(&s->vous);
+}
+
+static void
+audioinit(void)
+{
+	audio.amode = Aclosed;
+	resetlevel();
+//	powerenable(audiopower);
+}
+
+static Chan*
+audioattach(char *param)
+{
+	return devattach('A', param);
+}
+
+static Walkqid*
+audiowalk(Chan *c, Chan *nc, char **name, int nname)
+{
+	return devwalk(c, nc, name, nname, audiodir, nelem(audiodir), devgen);
+}
+
+static int
+audiostat(Chan *c, uchar *db, int n)
+{
+	return devstat(c, db, n, audiodir, nelem(audiodir), devgen);
+}
+
+static Chan*
+audioopen(Chan *c, int mode)
+{
+	IOstate *s;
+	int omode = mode;
+
+	switch((ulong)c->qid.path) {
+	default:
+		error(Eperm);
+		break;
+
+	case Qstatus:
+		if((omode&7) != OREAD)
+			error(Eperm);
+	case Qvolume:
+	case Qaudioctl:
+	case Qdir:
+		break;
+
+	case Qaudio:
+		omode = (omode & 0x7) + 1;
+		if (omode & ~(Aread | Awrite))
+			error(Ebadarg);
+		qlock(&audio);
+		if(audio.amode & omode){
+			qunlock(&audio);
+			error(Einuse);
+		}
+		enable();
+		memset(&iostats, 0, sizeof(iostats));
+		if (omode & Aread) {
+			inenable();
+			s = &audio.i;
+			if(s->bufinit == 0)
+				bufinit(s);
+			setempty(s);
+			s->emptying = &s->buf[Nbuf-1];
+			s->chan = c;
+			s->dma = dmasetup(DmaSSP, 1, 0, audiointr, (void*)s);
+			audio.amode |= Aread;
+			audio.clockout = 1;
+		}
+		if (omode & Awrite) {
+			outenable();
+			s = &audio.o;
+			audio.amode |= Awrite;
+			if(s->bufinit == 0)
+				bufinit(s);
+			setempty(s);
+			s->chan = c;
+			s->dma = dmasetup(DmaSSP, 0, 0, audiointr, (void*)s);
+			audio.amode |= Awrite;
+		}
+		mxvolume();
+		qunlock(&audio);
+		if (debug) print("open done\n");
+		break;
+	}
+	c = devopen(c, mode, audiodir, nelem(audiodir), devgen);
+	c->mode = openmode(mode);
+	c->flag |= COPEN;
+	c->offset = 0;
+
+	return c;
+}
+
+static void
+audioclose(Chan *c)
+{
+	IOstate *s;
+
+	switch((ulong)c->qid.path) {
+	default:
+		error(Eperm);
+		break;
+
+	case Qdir:
+	case Qvolume:
+	case Qaudioctl:
+	case Qstatus:
+		break;
+
+	case Qaudio:
+		if (debug > 1) print("#A: close\n");
+		if(c->flag & COPEN) {
+			qlock(&audio);
+			if(waserror()){
+				qunlock(&audio);
+				nexterror();
+			}
+			if (audio.o.chan == c) {
+				/* closing the write end */
+				audio.amode &= ~Awrite;
+				s = &audio.o;
+				qlock(s);
+				if(waserror()){
+					qunlock(s);
+					nexterror();
+				}
+				if (s->filling->nbytes) {
+					/* send remaining partial buffer */
+					s->filling++;
+					if (s->filling == &s->buf[Nbuf])
+						s->filling = &s->buf[0];
+					sendaudio(s);
+				}
+				dmawait(s->dma);
+				outdisable();
+				setempty(s);
+				dmafree(s->dma);
+				qunlock(s);
+				poperror();
+			}
+			if (audio.i.chan == c) {
+				/* closing the read end */
+				audio.amode &= ~Aread;
+				s = &audio.i;
+				qlock(s);
+				if(waserror()){
+					qunlock(s);
+					nexterror();
+				}
+				indisable();
+				setempty(s);
+				dmafree(s->dma);
+				qunlock(s);
+				poperror();
+			}
+			if (audio.amode == 0) {
+				/* turn audio off */
+				archaudiopower(0);
+			}
+			qunlock(&audio);
+			poperror();
+			if (debug) {
+				print("total dmas: %lud\n", iostats.totaldma);
+				print("dmas while idle: %lud\n", iostats.idledma);
+				print("dmas while busy: %lud\n", iostats.faildma);
+				print("out of order dma: %lud\n", iostats.samedma);
+			}
+		}
+		break;
+	}
+}
+
+static long
+audioread(Chan *c, void *v, long n, vlong off)
+{
+	int liv, riv, lov, rov;
+	long m, n0;
+	char buf[300];
+	int j;
+	ulong offset = off;
+	char *p;
+	IOstate *s;
+
+	n0 = n;
+	p = v;
+	switch((ulong)c->qid.path) {
+	default:
+		error(Eperm);
+		break;
+
+	case Qdir:
+		return devdirread(c, p, n, audiodir, nelem(audiodir), devgen);
+
+	case Qaudio:
+		if (debug > 1) print("#A: read %ld\n", n);
+		if((audio.amode & Aread) == 0)
+			error(Emode);
+		s = &audio.i;
+		qlock(s);
+		if(waserror()){
+			qunlock(s);
+			nexterror();
+		}
+		while(n > 0) {
+			if(s->emptying->nbytes == 0) {
+				if (debug > 1) print("#A: emptied @%p\n", s->emptying);
+				recvaudio(s);
+				s->emptying++;
+				if (s->emptying == &s->buf[Nbuf])
+					s->emptying = s->buf;
+			}
+			/* wait if dma in progress */
+			while (!dmaidle(s->dma) && s->emptying == s->current) {
+				if (debug > 1) print("#A: sleep\n");
+				sleep(&s->vous, audioqnotempty, s);
+			}
+
+			m = Bufsize - s->emptying->nbytes;
+			if(m > n)
+				m = n;
+			memmove(p, s->emptying->virt + s->emptying->nbytes, m);
+
+			s->emptying->nbytes -= m;
+			n -= m;
+			p += m;
+		}
+		poperror();
+		qunlock(s);
+		break;
+		break;
+
+	case Qstatus:
+		buf[0] = 0;
+		snprint(buf, sizeof(buf), "bytes %llud\ntime %lld\n",
+			audio.totcount, audio.tottime);
+		return readstr(offset, p, n, buf);
+
+	case Qvolume:
+	case Qaudioctl:
+		j = 0;
+		buf[0] = 0;
+		for(m=0; volumes[m].name; m++){
+			liv = audio.livol[m];
+			riv = audio.rivol[m];
+			lov = audio.lovol[m];
+			rov = audio.rovol[m];
+			j += snprint(buf+j, sizeof(buf)-j, "%s", volumes[m].name);
+			if((volumes[m].flag & Fmono) || liv==riv && lov==rov){
+				if((volumes[m].flag&(Fin|Fout))==(Fin|Fout) && liv==lov)
+					j += snprint(buf+j, sizeof(buf)-j, " %d", liv);
+				else{
+					if(volumes[m].flag & Fin)
+						j += snprint(buf+j, sizeof(buf)-j,
+							" in %d", liv);
+					if(volumes[m].flag & Fout)
+						j += snprint(buf+j, sizeof(buf)-j,
+							" out %d", lov);
+				}
+			}else{
+				if((volumes[m].flag&(Fin|Fout))==(Fin|Fout) &&
+				    liv==lov && riv==rov)
+					j += snprint(buf+j, sizeof(buf)-j,
+						" left %d right %d",
+						liv, riv);
+				else{
+					if(volumes[m].flag & Fin)
+						j += snprint(buf+j, sizeof(buf)-j,
+							" in left %d right %d",
+							liv, riv);
+					if(volumes[m].flag & Fout)
+						j += snprint(buf+j, sizeof(buf)-j,
+							" out left %d right %d",
+							lov, rov);
+				}
+			}
+			j += snprint(buf+j, sizeof(buf)-j, "\n");
+		}
+		return readstr(offset, p, n, buf);
+	}
+	return n0-n;
+}
+
+static long
+audiowrite(Chan *c, void *vp, long n, vlong)
+{
+	long m, n0;
+	int i, nf, v, left, right, in, out;
+	char buf[255], *field[Ncmd];
+	char *p;
+	IOstate *a;
+
+	p = vp;
+	n0 = n;
+	switch((ulong)c->qid.path) {
+	default:
+		error(Eperm);
+		break;
+
+	case Qvolume:
+	case Qaudioctl:
+		v = Vaudio;
+		left = 1;
+		right = 1;
+		in = 1;
+		out = 1;
+		if(n > sizeof(buf)-1)
+			n = sizeof(buf)-1;
+		memmove(buf, p, n);
+		buf[n] = '\0';
+		n = 0;
+
+		nf = getfields(buf, field, Ncmd, 1, " \t\n");
+		for(i = 0; i < nf; i++){
+			/*
+			 * a number is volume
+			 */
+			if(field[i][0] >= '0' && field[i][0] <= '9') {
+				m = strtoul(field[i], 0, 10);
+				if(v == Vspeed){
+					if(archaudiospeed(m, 0) < 0)
+						error(Evolume);
+				}else
+					if(m < 0 || m > 100)
+						error(Evolume);
+				if(left && out)
+					audio.lovol[v] = m;
+				if(left && in)
+					audio.livol[v] = m;
+				if(right && out)
+					audio.rovol[v] = m;
+				if(right && in)
+					audio.rivol[v] = m;
+				goto cont0;
+			}
+			if(strcmp(field[i], "rate") == 0)
+				field[i] = "speed";	/* honestly ... */
+
+			for(m=0; volumes[m].name; m++) {
+				if(strcmp(field[i], volumes[m].name) == 0) {
+					v = m;
+					in = 1;
+					out = 1;
+					left = 1;
+					right = 1;
+					goto cont0;
+				}
+			}
+			if(strcmp(field[i], "enc") == 0) {
+				if(++i >= nf)
+					error(Evolume);
+				if(strcmp(field[i], "pcm") != 0)
+					error(Evolume);
+				goto cont0;
+			}
+			if(strcmp(field[i], "bits") == 0) {
+				if(++i >= nf)
+					error(Evolume);
+				if(strtol(field[i], 0, 0) != 16)
+					error(Evolume);
+				goto cont0;
+			}
+			if(strcmp(field[i], "chans") == 0) {
+				if(++i >= nf)
+					error(Evolume);
+				if(strtol(field[i], 0, 0) != 2)
+					error(Evolume);
+				goto cont0;
+			}
+			if(strcmp(field[i], "reset") == 0) {
+				resetlevel();
+				goto cont0;
+			}
+			if(strcmp(field[i], "debug") == 0) {
+				debug = debug?0:1;
+				goto cont0;
+			}
+			if(strcmp(field[i], "in") == 0) {
+				in = 1;
+				out = 0;
+				goto cont0;
+			}
+			if(strcmp(field[i], "out") == 0) {
+				in = 0;
+				out = 1;
+				goto cont0;
+			}
+			if(strcmp(field[i], "left") == 0) {
+				left = 1;
+				right = 0;
+				goto cont0;
+			}
+			if(strcmp(field[i], "right") == 0) {
+				left = 0;
+				right = 1;
+				goto cont0;
+			}
+			if(strcmp(field[i], "reg") == 0) {
+				if(nf < 3)
+					error(Evolume);
+				setreg(field[1], atoi(field[2]), nf == 4 ? atoi(field[3]):1);
+				return n0;
+			}
+			error(Evolume);
+			break;
+		cont0:;
+		}
+		mxvolume();
+		break;
+
+	case Qaudio:
+		if (debug > 1) print("#A: write %ld\n", n);
+		if((audio.amode & Awrite) == 0)
+			error(Emode);
+		a = &audio.o;
+		qlock(a);
+		if(waserror()){
+			qunlock(a);
+			nexterror();
+		}
+		while(n > 0) {
+			/* wait if dma in progress */
+			while (!dmaidle(a->dma) && a->filling == a->current) {
+				if (debug > 1) print("#A: sleep\n");
+				sleep(&a->vous, audioqnotfull, a);
+			}
+
+			m = Bufsize - a->filling->nbytes;
+			if(m > n)
+				m = n;
+			memmove(a->filling->virt + a->filling->nbytes, p, m);
+
+			a->filling->nbytes += m;
+			n -= m;
+			p += m;
+			if(a->filling->nbytes >= Bufsize) {
+				if (debug > 1) print("#A: filled @%p\n", a->filling);
+				a->filling++;
+				if (a->filling == &a->buf[Nbuf])
+					a->filling = a->buf;
+				sendaudio(a);
+			}
+		}
+		poperror();
+		qunlock(a);
+		break;
+	}
+	return n0 - n;
+}
+
+Dev audiodevtab = {
+	'A',
+	"audio",
+
+	audioreset,
+	audioinit,
+	devshutdown,
+	audioattach,
+	audiowalk,
+	audiostat,
+	audioopen,
+	devcreate,
+	audioclose,
+	audioread,
+	devbread,
+	audiowrite,
+	devbwrite,
+	devremove,
+	devwstat,
+	audiopower,
+};
--- /dev/null
+++ b/os/ipaq1110/devipaq.c
@@ -1,0 +1,762 @@
+/*
+ *  iPAQ H3650 touch screen and other devices
+ *
+ * Inferno driver derived from sketchy documentation and
+ * information gleaned from linux/char/h3650_ts.c
+ * by Charles Flynn.
+ *
+ * Copyright © 2000,2001 Vita Nuova Holdings Limited.  All rights reserved.
+ */
+#include	"u.h"
+#include	"../port/lib.h"
+#include	"mem.h"
+#include	"dat.h"
+#include	"fns.h"
+#include	"io.h"
+#include	"../port/error.h"
+
+#include "keyboard.h"
+#include <kernel.h>
+
+#include <draw.h>
+#include <memdraw.h>
+#include "screen.h"
+
+#define	DEBUG	0
+
+/*
+ * packet format
+ *
+ * SOF (0x02)
+ * (id<<4) | len	byte length
+ * data[len] bytes
+ * chk	checksum mod 256 excluding SOF
+ */
+
+enum {
+	Csof = 0x02,
+	Ceof = 0x03,
+	Hdrlen = 3,
+
+	/* opcodes */
+
+	Oversion = 0,
+	Okeys = 2,
+	Otouch = 3,
+	Ordeeprom = 4,
+	Owreeprom = 5,
+	Othermal = 6,
+	Oled = 8,
+	Obattery = 9,
+	Ospiread = 11,
+	Ospiwrite = 12,
+	Obacklight = 13,
+	Oextstatus = 0xA1,
+ };
+
+enum {
+	Powerbit = 0,	/* GPIO bit for power on/off key */
+};
+
+enum{
+	Qdir,
+	Qctl,
+	Qtouchctl,
+	Qbattery,
+	Qversion,
+};
+
+static
+Dirtab ipaqtab[]={
+	".",	{Qdir, 0, QTDIR},	0,	0555,
+	"ipaqctl",		{Qctl},		0,	0600,
+	"battery",		{Qbattery},	0,	0444,
+	"version",		{Qversion},	0,	0444,
+	"touchctl",	{Qtouchctl},	0,	0644,
+};
+
+static struct {
+	QLock;
+	Chan*	c;
+
+	Lock	rl;	/* protect cmd, reply */
+	int	cmd;
+	Block*	reply;
+	Rendez	r;
+} atmel;
+
+/* to and from fixed point */
+#define	FX(a,b)	(((a)<<16)/(b))
+#define	XF(v)		((v)>>16)
+
+static struct {
+	Lock;
+	int	rate;
+	int	m[2][3];	/* transformation matrix */
+	Point	avg;
+	Point	diff;
+	Point	pts[4];
+	int	n;	/* number of points in pts */
+	int	p;	/* current index in pts */
+	int	down;
+	int	nout;
+} touch = {
+	{0},
+	.m {{-FX(1,3), 0, FX(346,1)},{0, -FX(1,4), FX(256, 1)}},
+};
+
+/*
+ * map rocker positions to same codes as plan 9
+ */
+static	Rune	rockermap[2][4] ={
+	{Right, Down, Up, Left},	/* landscape */
+	{Up, Right, Left, Down},	/* portrait */
+};
+
+static	Rendez	powerevent;
+
+static	void	cmdack(int, void*, int);
+static	int	cmdio(int, void*, int, void*, int);
+static	void	ipaqreadproc(void*);
+static	void	powerwaitproc(void*);
+static	Block*	rdevent(Block**);
+static	long	touchctl(char*, long);
+static	void	touched(Block*, int);
+static	int	wrcmd(int, void*, int, void*, int);
+static	char*	acstatus(int);
+static	char*	batstatus(int);
+static	void	powerintr(Ureg*, void*);
+
+static void
+ipaqreset(void)
+{
+	intrenable(Powerbit, powerintr, nil, BusGPIOfalling, "power off");
+}
+
+static void
+ipaqinit(void)
+{
+	kproc("powerwait", powerwaitproc, nil, 0);
+}
+
+static Chan*
+ipaqattach(char* spec)
+{
+	int fd;
+
+	qlock(&atmel);
+	if(waserror()){
+		qunlock(&atmel);
+		nexterror();
+	}
+	if(atmel.c == nil){
+		fd = kopen("#t/eia1ctl", ORDWR);
+		if(fd < 0)
+			error(up->env->errstr);
+		kwrite(fd, "b115200", 7);	/* it's already pn, l8 */
+		kclose(fd);
+		fd = kopen("#t/eia1", ORDWR);
+		if(fd < 0)
+			error(up->env->errstr);
+		atmel.c = fdtochan(up->env->fgrp, fd, ORDWR, 0, 1);
+		kclose(fd);
+		atmel.cmd = -1;
+		kproc("ipaqread", ipaqreadproc, nil, 0);
+	}
+	poperror();
+	qunlock(&atmel);
+	return devattach('T', spec);
+}
+
+static Walkqid*
+ipaqwalk(Chan *c, Chan *nc, char **name, int nname)
+{
+	return devwalk(c, nc, name, nname, ipaqtab, nelem(ipaqtab), devgen);
+}
+
+static int
+ipaqstat(Chan* c, uchar *db, int n)
+{
+	return devstat(c, db, n, ipaqtab, nelem(ipaqtab), devgen);
+}
+
+static Chan*
+ipaqopen(Chan* c, int omode)
+{
+	return devopen(c, omode, ipaqtab, nelem(ipaqtab), devgen);
+}
+
+static void
+ipaqclose(Chan*)
+{
+}
+
+static long
+ipaqread(Chan* c, void* a, long n, vlong offset)
+{
+	char *tmp, buf[64];
+	uchar reply[12];
+	int v, p, l;
+
+	switch((ulong)c->qid.path){
+	case Qdir:
+		return devdirread(c, a, n, ipaqtab, nelem(ipaqtab), devgen);
+	case Qtouchctl:
+		tmp = malloc(READSTR);
+		if(waserror()){
+			free(tmp);
+			nexterror();
+		}
+		snprint(tmp, READSTR, "s%d\nr%d\nR%d\nX %d %d %d\nY %d %d %d\n",
+			1000, 0, 1,
+			touch.m[0][0], touch.m[0][1], touch.m[0][2],
+			touch.m[1][0], touch.m[1][1], touch.m[1][2]);
+		n = readstr(offset, a, n, tmp);
+		poperror();
+		free(tmp);
+		break;
+	case Qbattery:
+		cmdio(Obattery, reply, 0, reply, sizeof(reply));
+		tmp = malloc(READSTR);
+		if(waserror()){
+			free(tmp);
+			nexterror();
+		}
+		v = (reply[4]<<8)|reply[3];
+		p = 425*v/1000 - 298;
+		snprint(tmp, READSTR, "voltage: %d %dmV %d%% %d\nac: %s\nstatus: %d %s\nchem: %d\n",
+			v, 1000*v/228, p, 300*p/100, acstatus(reply[1]), reply[5], batstatus(reply[5]), reply[2]);
+		n = readstr(offset, a, n, tmp);
+		poperror();
+		free(tmp);
+		break;
+	case Qversion:
+		l = cmdio(Oversion, reply, 0, reply, sizeof(reply));
+		if(l > 4){
+			l--;
+			memmove(buf, reply+1, 4);
+			if(l > 8){
+				buf[4] = ' ';
+				memmove(buf+5, reply+5, 4);	/* pack version */
+				sprint(buf+9, " %.2x\n", reply[9]);	/* ``boot type'' */
+			}else{
+				buf[4] = '\n';
+				buf[5] = 0;
+			}
+			return readstr(offset, a, n, buf);
+		}
+		n=0;
+		break;
+	default:
+		n=0;
+		break;
+	}
+	return n;
+}
+
+static long
+ipaqwrite(Chan* c, void* a, long n, vlong)
+{
+	char cmd[64], op[32], *fields[6];
+	int nf;
+
+	switch((ulong)c->qid.path){
+	case Qctl:
+		if(n >= sizeof(cmd)-1)
+			n = sizeof(cmd)-1;
+		memmove(cmd, a, n);
+		cmd[n] = 0;
+		nf = getfields(cmd, fields, nelem(fields), 1, " \t\n");
+		if(nf <= 0)
+			error(Ebadarg);
+		if(nf >= 4 && strcmp(fields[0], "light") == 0){
+			op[0] = atoi(fields[1]);	/* mode */
+			op[1] = atoi(fields[2]);	/* power */
+			op[2] = atoi(fields[3]);	/* brightness */
+			cmdack(Obacklight, op, 3);
+		}else if(nf >= 5 && strcmp(fields[0], "led") == 0){
+			op[0] = atoi(fields[1]);
+			op[1] = atoi(fields[2]);
+			op[2] = atoi(fields[3]);
+			op[3] = atoi(fields[4]);
+			cmdack(Oled, op, 4);
+		}else if(strcmp(fields[0], "suspend") == 0){
+			/* let the kproc do it */
+			wakeup(&powerevent);
+		}else
+			error(Ebadarg);
+		break;
+	case Qtouchctl:
+		return touchctl(a, n);
+	default:
+		error(Ebadusefd);
+	}
+	return n;
+}
+
+static void
+powerintr(Ureg*, void*)
+{
+	wakeup(&powerevent);
+}
+
+static void
+cmdack(int id, void *a, int n)
+{
+	uchar reply[16];
+
+	cmdio(id, a, n, reply, sizeof(reply));
+}
+
+static int
+cmdio(int id, void *a, int n, void *reply, int lim)
+{
+	qlock(&atmel);
+	if(waserror()){
+		qunlock(&atmel);
+		nexterror();
+	}
+	n = wrcmd(id, a, n, reply, lim);
+	poperror();
+	qunlock(&atmel);
+	return n;
+}
+
+static int
+havereply(void*)
+{
+	return atmel.reply != nil;
+}
+
+static int
+wrcmd(int id, void *a, int n, void *b, int lim)
+{
+	uchar buf[32];
+	int i, sum;
+	Block *e;
+
+	if(n >= 16)
+		error(Eio);
+	lock(&atmel.rl);
+	atmel.cmd = id;
+	unlock(&atmel.rl);
+	buf[0] = Csof;
+	buf[1] = (id<<4) | (n&0xF);
+	if(n)
+		memmove(buf+2, a, n);
+	sum = 0;
+	for(i=1; i<n+2; i++)
+		sum += buf[i];
+	buf[i++] = sum;
+	if(0){
+		iprint("msg=");
+		for(sum=0; sum<i; sum++)
+			iprint(" %2.2ux", buf[sum]);
+		iprint("\n");
+	}
+	if(kchanio(atmel.c, buf, i, OWRITE) != i)
+		error(Eio);
+	tsleep(&atmel.r, havereply, nil, 500);
+	lock(&atmel.rl);
+	e = atmel.reply;
+	atmel.reply = nil;
+	atmel.cmd = -1;
+	unlock(&atmel.rl);
+	if(e == nil){
+		print("ipaq: no reply\n");
+		error(Eio);
+	}
+	if(waserror()){
+		freeb(e);
+		nexterror();
+	}
+	if(e->rp[0] != id){
+		print("ipaq: rdreply: mismatched reply %d :: %d\n", id, e->rp[0]);
+		error(Eio);
+	}
+	n = BLEN(e);
+	if(n < lim)
+		lim = n;
+	memmove(b, e->rp, lim);
+	poperror();
+	freeb(e);
+	return lim;
+}
+
+static void
+ipaqreadproc(void*)
+{
+	Block *e, *b, *partial;
+	int c, mousemod;
+
+	while(waserror())
+		print("ipaqread: %r\n");
+	partial = nil;
+	mousemod = 0;
+	for(;;){
+		e = rdevent(&partial);
+		if(e == nil){
+			print("ipaqread: rdevent: %r\n");
+			continue;
+		}
+		switch(e->rp[0]){
+		case Otouch:
+			touched(e, mousemod);
+			freeb(e);
+			break;
+		case Okeys:
+			//print("key %2.2ux\n", e->rp[1]);
+			c = e->rp[1] & 0xF;
+			if(c >= 6 && c < 10){	/* rocker */
+				if((e->rp[1] & 0x80) == 0){
+					kbdrepeat(0);
+					kbdputc(kbdq, rockermap[conf.portrait&1][c-6]);
+				}else
+					kbdrepeat(0);
+			}else{
+				/* TO DO: change tkmouse and mousetrack to allow extra buttons */
+				if(--c == 0)
+					c = 5;
+				if(e->rp[1] & 0x80)
+					mousemod &= ~(1<<c);
+				else
+					mousemod |= 1<<c;
+			}
+			freeb(e);
+			break;
+		default:
+			lock(&atmel.rl);
+			if(atmel.cmd == e->rp[0]){
+				b = atmel.reply;
+				atmel.reply = e;
+				unlock(&atmel.rl);
+				wakeup(&atmel.r);
+				if(b != nil)
+					freeb(b);
+			}else{
+				unlock(&atmel.rl);
+				print("ipaqread: discard op %d\n", e->rp[0]);
+				freeb(e);
+			}
+		}
+	}
+}
+
+static Block *
+rdevent(Block **bp)
+{
+	Block *b, *e;
+	int s, c, len, csum;
+	enum {Ssof=16, Sid, Ssum};
+
+	s = Ssof;
+	csum = 0;
+	len = 0;
+	e = nil;
+	if(waserror()){
+		if(e != nil)
+			freeb(e);
+		nexterror();
+	}
+	for(;;){
+		b = *bp;
+		*bp = nil;
+		if(b == nil){
+			b = devtab[atmel.c->type]->bread(atmel.c, 128, 0);
+			if(b == nil)
+				error(Eio);
+			if(DEBUG)
+				iprint("r: %ld\n", BLEN(b));
+		}
+		while(b->rp < b->wp){
+			c = *b->rp++;
+			switch(s){
+			case Ssof:
+				if(c == Csof)
+					s = Sid;
+				else if(1)
+					iprint("!sof: %2.2ux %d\n", c, s);
+				break;
+			case Sid:
+				csum = c;
+				len = c & 0xF;
+				e = allocb(len+1);
+				if(e == nil)
+					error(Eio);
+				*e->wp++ = c>>4;	/* id */
+				if(len)
+					s = 0;
+				else
+					s = Ssum;
+				break;
+			case Ssum:
+				csum &= 0xFF;
+				if(c != csum){
+					iprint("cksum: %2.2ux != %2.2ux\n", c, csum);
+					s = Ssof;	/* try to resynchronise */
+					if(e != nil){
+						freeb(e);
+						e = nil;
+					}
+					break;
+				}
+				if(b->rp < b->wp)
+					*bp = b;
+				else
+					freeb(b);
+				if(DEBUG){
+					int i;
+					iprint("event: [%ld]", BLEN(e));
+					for(i=0; i<BLEN(e);i++)
+						iprint(" %2.2ux", e->rp[i]);
+					iprint("\n");
+				}
+				poperror();
+				return e;
+			default:
+				csum += c;
+				*e->wp++ = c;
+				if(++s >= len)
+					s = Ssum;
+				break;
+			}
+		}
+		freeb(b);
+	}
+	return 0;	/* not reached */
+}
+
+static char *
+acstatus(int x)
+{
+	switch(x){
+	case 0:	return "offline";
+	case 1:	return "online";
+	case 2:	return "backup";
+	}
+	return "unknown";
+}
+
+static char *
+batstatus(int x)
+{
+	if(x & 0x40)
+		return "charging";	/* not in linux but seems to be on mine */
+	switch(x){
+	case 0:		return "ok";
+	case 1:		return "high";
+	case 2:		return "low";
+	case 4:		return "critical";
+	case 8:		return "charging";
+	case 0x80:	return "none";
+	}
+	return "unknown";
+}
+
+static int
+ptmap(int *m, int x, int y)
+{
+	return XF(m[0]*x + m[1]*y + m[2]);
+}
+
+static void
+touched(Block *b, int buttons)
+{
+	int rx, ry, x, y, dx, dy, n;
+	Point op, *lp, cur;
+
+	if(BLEN(b) == 5){
+		/* id Xhi Xlo Yhi Ylo */
+		if(touch.down < 0){
+			touch.down = 0;
+			return;
+		}
+		rx = (b->rp[1]<<8)|b->rp[2];
+		ry = (b->rp[3]<<8)|b->rp[4];
+		if(conf.portrait){
+			dx = rx; rx = ry; ry = dx;
+		}
+		if(touch.down == 0){
+			touch.nout = 0;
+			touch.p = 1;
+			touch.n = 1;
+			touch.avg = Pt(rx, ry);
+			touch.pts[0] = touch.avg;
+			touch.down = 1;
+			return;
+		}
+		n = touch.p-1;
+		if(n < 0)
+			n = nelem(touch.pts)-1;
+		lp = &touch.pts[n];	/* last point */
+		if(touch.n > 0 && (rx-lp->x)*(ry-lp->y) > 50*50){	/* far out */
+			if(++touch.nout > 3){
+				touch.down = 0;
+				touch.n = 0;
+			}
+			return;
+		}
+		op = touch.pts[touch.p];
+		touch.pts[touch.p] = Pt(rx, ry);
+		touch.p = (touch.p+1) % nelem(touch.pts);
+		touch.avg.x += rx;
+		touch.avg.y += ry;
+		if(touch.n < nelem(touch.pts)){
+			touch.n++;
+			return;
+		}
+		touch.avg.x -= op.x;
+		touch.avg.y -= op.y;
+		cur = mousexy();
+		rx = touch.avg.x/touch.n;
+		ry = touch.avg.y/touch.n;
+		x = ptmap(touch.m[0], rx, ry);
+		dx = x-cur.x;
+		y = ptmap(touch.m[1], rx, ry);
+		dy = y-cur.y;
+		if(dx*dx + dy*dy <= 2){
+			dx = 0;
+			dy = 0;
+		}
+		if(buttons == 0)
+			buttons = 1<<0;	/* by default, stylus down implies button 1 */
+		mousetrack(buttons&0x1f, dx, dy, 1);	/* TO DO: allow more than 3 buttons */
+		/* TO DO: swcursupdate(oldx, oldy, x, y); */
+		touch.down = 1;
+	}else{
+		if(touch.down){
+			mousetrack(0, 0, 0, 1);	/* stylus up */
+			touch.down = 0;
+		}else
+			touch.down = -1;
+		touch.n = 0;
+		touch.p = 0;
+		touch.avg.x = 0;
+		touch.avg.y = 0;
+	}
+}
+
+/*
+ * touchctl commands:
+ *	X a b c	- set X transformation
+ *	Y d e f	- set Y transformation
+ *	s<delay>		- set sample delay in millisec per sample
+ *	r<delay>		- set read delay in microsec
+ *	R<l2nr>			- set log2 of number of readings to average
+ */
+static long	 
+touchctl(char* a, long n)
+{
+	char buf[64];
+	char *cp;
+	int n0 = n;
+	int bn;
+	char *field[8];
+	int nf, cmd, pn, m[2][3];
+
+	while(n) {
+		bn = (cp = memchr(a, '\n', n))!=nil ? cp-a+1 : n;
+		n -= bn;
+		cp = a;
+		a += bn;
+		bn = bn > sizeof(buf)-1 ? sizeof(buf)-1 : bn;
+		memmove(buf, cp, bn);
+		buf[bn] = '\0';
+		nf = getfields(buf, field, nelem(field), 1, " \t\n");
+		if(nf <= 0)
+			continue;
+		if(strcmp(field[0], "calibrate") == 0){
+			if(nf == 1){
+				lock(&touch);
+				memset(touch.m, 0, sizeof(touch.m));
+				touch.m[0][0] = FX(1,1);
+				touch.m[1][1] = FX(1,1);
+				unlock(&touch);
+			}else if(nf >= 5){
+				memset(m, 0, sizeof(m));
+				m[0][0] = strtol(field[1], 0, 0);
+				m[1][1] = strtol(field[2], 0, 0);
+				m[0][2] = strtol(field[3], 0, 0);
+				m[1][2] = strtol(field[4], 0, 0);
+				if(nf > 5)
+					m[0][1] = strtol(field[5], 0, 0);
+				if(nf > 6)
+					m[1][0] = strtol(field[6], 0, 0);
+				lock(&touch);
+				memmove(touch.m, m, sizeof(touch.m[0]));
+				unlock(&touch);
+			}else
+				error(Ebadarg);
+			continue;
+		}
+		cmd = *field[0]++;
+		pn = *field[0] == 0;
+		switch(cmd) {
+		case 's':
+			pn = strtol(field[pn], 0, 0);
+			if(pn <= 0)
+				error(Ebadarg);
+			touch.rate = pn;
+			break;
+		case 'r':
+			/* touch read delay */
+			break;
+		case 'X':
+		case 'Y':
+			if(nf < pn+2)
+				error(Ebadarg);
+			m[0][0] = strtol(field[pn], 0, 0);
+			m[0][1] = strtol(field[pn+1], 0, 0);
+			m[0][2] = strtol(field[pn+2], 0, 0);
+			lock(&touch);
+			memmove(touch.m[cmd=='Y'], m[0], sizeof(touch.m[0]));
+			unlock(&touch);
+			break;
+		default:
+			error(Ebadarg);
+		}
+	}
+	return n0-n;
+}
+
+/*
+ * this might belong elsewhere
+ */
+static int
+powerwait(void*)
+{
+	return (GPIOREG->gplr & GPIO_PWR_ON_i) == 0;
+}
+
+static void
+powerwaitproc(void*)
+{
+	for(;;){
+		sleep(&powerevent, powerwait, nil);
+		do{
+			tsleep(&up->sleep, return0, nil, 50);
+		}while((GPIOREG->gplr & GPIO_PWR_ON_i) == 0);
+		powersuspend();
+	}
+}
+
+Dev ipaqdevtab = {
+	'T',
+	"ipaq",
+
+	ipaqreset,
+	ipaqinit,
+	devshutdown,
+	ipaqattach,
+	ipaqwalk,
+	ipaqstat,
+	ipaqopen,
+	devcreate,
+	ipaqclose,
+	ipaqread,
+	devbread,
+	ipaqwrite,
+	devbwrite,
+	devremove,
+	devwstat,
+};
--- /dev/null
+++ b/os/ipaq1110/etherwavelan.c
@@ -1,0 +1,1307 @@
+/*
+	Lucent Wavelan IEEE 802.11 pcmcia.
+	There is almost no documentation for the card.
+	the driver is done using both the FreeBSD, Linux and
+	original Plan 9 drivers as `documentation'.
+
+	Has been used with the card plugged in during all up time.
+	no cards removals/insertions yet.
+
+	For known BUGS see the comments below. Besides,
+	the driver keeps interrupts disabled for just too
+	long. When it gets robust, locks should be revisited.
+
+	BUGS: check endian, alignment and mem/io issues;
+	      multicast;
+	      receive watchdog interrupts.
+	TODO: automatic power management;
+	      improve locking.
+ */
+#include "u.h"
+#include "../port/lib.h"
+#include "mem.h"
+#include "dat.h"
+#include "fns.h"
+#include "io.h"
+#include "../port/error.h"
+#include "../port/netif.h"
+#include "etherif.h"
+
+#define	print	iprint
+#define DEBUG	if(1)iprint
+
+#define SEEKEYS 1
+
+typedef struct Ctlr	Ctlr;
+typedef struct Wltv	Wltv;
+typedef struct WFrame	WFrame;
+typedef struct Stats	Stats;
+typedef struct WStats	WStats;
+typedef struct WKey	WKey;
+
+struct WStats
+{
+	ulong	ntxuframes;		// unicast frames
+	ulong	ntxmframes;		// multicast frames
+	ulong	ntxfrags;		// fragments
+	ulong	ntxubytes;		// unicast bytes
+	ulong	ntxmbytes;		// multicast bytes
+	ulong	ntxdeferred;		// deferred transmits
+	ulong	ntxsretries;		// single retries
+	ulong	ntxmultiretries;	// multiple retries
+	ulong	ntxretrylimit;
+	ulong	ntxdiscards;
+	ulong	nrxuframes;		// unicast frames
+	ulong	nrxmframes;		// multicast frames
+	ulong	nrxfrags;		// fragments
+	ulong	nrxubytes;		// unicast bytes
+	ulong	nrxmbytes;		// multicast bytes
+	ulong	nrxfcserr;
+	ulong	nrxdropnobuf;
+	ulong	nrxdropnosa;
+	ulong	nrxcantdecrypt;
+	ulong	nrxmsgfrag;
+	ulong	nrxmsgbadfrag;
+	ulong	end;
+};
+
+struct WFrame
+{
+	ushort	sts;
+	ushort	rsvd0;
+	ushort	rsvd1;
+	ushort	qinfo;
+	ushort	rsvd2;
+	ushort	rsvd3;
+	ushort	txctl;
+	ushort	framectl;
+	ushort	id;
+	uchar	addr1[Eaddrlen];
+	uchar	addr2[Eaddrlen];
+	uchar	addr3[Eaddrlen];
+	ushort	seqctl;
+	uchar	addr4[Eaddrlen];
+	ushort	dlen;
+	uchar	dstaddr[Eaddrlen];
+	uchar	srcaddr[Eaddrlen];
+	ushort	len;
+	ushort	dat[3];
+	ushort	type;
+};
+
+// Lucent's Length-Type-Value records to talk to the wavelan.
+// most operational parameters are read/set using this.
+enum
+{
+	WTyp_Stats	= 0xf100,
+	WTyp_Ptype	= 0xfc00,
+	WTyp_Mac	= 0xfc01,
+	WTyp_WantName	= 0xfc02,
+	WTyp_Chan	= 0xfc03,
+	WTyp_NetName	= 0xfc04,
+	WTyp_ApDens	= 0xfc06,
+	WTyp_MaxLen	= 0xfc07,
+	WTyp_PM		= 0xfc09,
+	WTyp_PMWait	= 0xfc0c,
+	WTyp_NodeName	= 0xfc0e,
+	WTyp_Crypt	= 0xfc20,
+	WTyp_XClear	= 0xfc22,
+	WTyp_Tick	= 0xfce0,
+	WTyp_RtsThres	= 0xfc83,
+	WTyp_TxRate	= 0xfc84,
+		WTx1Mbps	= 0x0,
+		WTx2Mbps	= 0x1,
+		WTxAuto		= 0x3,
+	WTyp_Prom	= 0xfc85,
+	WTyp_Keys	= 0xfcb0,
+	WTyp_TxKey	= 0xfcb1,
+	WTyp_StationID	= 0xfd20,
+	WTyp_CurName	= 0xfd41,
+	WTyp_BaseID	= 0xfd42,	// ID of the currently connected-to base station
+	WTyp_CurTxRate	= 0xfd44,	// Current TX rate
+	WTyp_HasCrypt	= 0xfd4f,
+};
+
+// Controller
+enum
+{
+	WDfltIRQ	= 3,		// default irq
+	WDfltIOB	= 0x180,	// default IO base
+
+	WIOLen		= 0x40,		// Hermes IO length
+
+	WTmOut		= 65536,	// Cmd time out
+
+	WPTypePeerToPeer	= 0,
+	WPTypeManaged	= 1,
+	WPTypeWDS	= 2,
+	WPTypeAdHoc	= 3,
+	WDfltPType	= WPTypeManaged,
+
+	WDfltApDens	= 1,
+	WDfltRtsThres	= 2347,		// == disabled
+	WDfltTxRate	= WTxAuto,	// 2Mbps
+
+	WMaxLen		= 2304,
+	WNameLen	= 32,
+
+	WNKeys		= 4,
+	WKeyLen		= 14,
+	WMinKeyLen	= 5,
+
+	// Wavelan hermes registers
+	WR_Cmd		= 0x00,
+		WCmdIni		= 0x0000,
+		WCmdEna		= 0x0001,
+		WCmdDis		= 0x0002,
+		WCmdTx		= 0x000b,
+		WCmdMalloc	= 0x000a,
+		WCmdAskStats	= 0x0011,
+		WCmdMsk		= 0x003f,
+		WCmdAccRd	= 0x0021,
+		WCmdReclaim	= 0x0100,
+		WCmdAccWr	= 0x0121,
+		WCmdBusy	= 0x8000,
+	WR_Parm0	= 0x02,
+	WR_Parm1	= 0x04,
+	WR_Parm2	= 0x06,
+	WR_Sts		= 0x08,
+	WR_InfoId	= 0x10,
+	WR_Sel0		= 0x18,
+	WR_Sel1		= 0x1a,
+	WR_Off0		= 0x1c,
+	WR_Off1		= 0x1e,
+		WBusyOff	= 0x8000,
+		WErrOff		= 0x4000,
+		WResSts		= 0x7f00,
+	WR_RXId		= 0x20,
+	WR_Alloc	= 0x22,
+	WR_EvSts	= 0x30,
+	WR_IntEna	= 0x32,
+		WCmdEv		= 0x0010,
+		WRXEv		= 0x0001,
+		WTXEv		= 0x0002,
+		WTxErrEv	= 0x0004,
+		WAllocEv	= 0x0008,
+		WInfoEv		= 0x0080,
+		WIDropEv	= 0x2000,
+		WTickEv		= 0x8000,
+		WEvs		= WRXEv|WTXEv|WAllocEv|WInfoEv|WIDropEv,
+
+	WR_EvAck	= 0x34,
+	WR_Data0	= 0x36,
+	WR_Data1	= 0x38,
+
+	// Frame stuff
+
+	WF_Err		= 0x0003,
+	WF_1042		= 0x2000,
+	WF_Tunnel	= 0x4000,
+	WF_WMP		= 0x6000,
+
+	WF_Data		= 0x0008,
+
+	WSnapK1		= 0xaa,
+	WSnapK2		= 0x00,
+	WSnapCtlr	= 0x03,
+	WSnap0		= (WSnapK1|(WSnapK1<<8)),
+	WSnap1		= (WSnapK2|(WSnapCtlr<<8)),
+	WSnapHdrLen	= 6,
+
+	WF_802_11_Off	= 0x44,
+	WF_802_3_Off	= 0x2e,
+
+};
+
+#define csr_outs(ctlr,r,arg)	outs((ctlr)->iob+(r),(arg))
+#define csr_ins(ctlr,r)		ins((ctlr)->iob+(r))
+#define csr_ack(ctlr,ev)	outs((ctlr)->iob+WR_EvAck,(ev))
+
+struct WKey
+{
+	ushort	len;
+	char	dat[WKeyLen];
+};
+
+struct Wltv
+{
+	ushort	len;
+	ushort	type;
+	union
+	{
+		struct {
+			ushort	val;
+			ushort	pad;
+		};
+		struct {
+			uchar	addr[8];
+		};
+		struct {
+			ushort	slen;
+			char	s[WNameLen];
+		};
+		struct {
+			char	name[WNameLen];
+		};
+		struct {
+			WKey	keys[WNKeys];
+		};
+	};
+};
+
+// What the driver thinks. Not what the card thinks.
+struct Stats
+{
+	ulong	nints;
+	ulong	nrx;
+	ulong	ntx;
+	ulong	ntxrq;
+	ulong	nrxerr;
+	ulong	ntxerr;
+	ulong	nalloc;			// allocation (reclaim) events
+	ulong	ninfo;
+	ulong	nidrop;
+	ulong	nwatchdogs;		// transmit time outs, actually
+	int	ticks;
+	int	tickintr;
+	int	signal;
+	int	noise;
+};
+
+struct Ctlr
+{
+	Lock;
+	Rendez	timer;
+
+	int	attached;
+	int	slot;
+	int	iob;
+	int	ptype;
+	int	apdensity;
+	int	rtsthres;
+	int	txbusy;
+	int	txrate;
+	int	txdid;
+	int	txmid;
+	int	txtmout;
+	int	maxlen;
+	int	chan;
+	int	pmena;
+	int	pmwait;
+
+	char	netname[WNameLen];
+	char	wantname[WNameLen];
+	char	nodename[WNameLen];
+	WFrame	txf;
+	uchar	txbuf[1536];
+
+	int	hascrypt;		// card has encryption
+	int	crypt;			// encryption off/on
+	int	txkey;			// transmit key
+	Wltv	keys;			// default keys
+	int	xclear;			// exclude clear packets off/on
+
+	Stats;
+	WStats;
+};
+
+// w_... routines do not ilock the Ctlr and should
+// be called locked.
+
+static void
+w_intdis(Ctlr* ctlr)
+{
+	csr_outs(ctlr, WR_IntEna, 0);
+	csr_ack(ctlr, 0xffff);
+}
+
+static void
+w_intena(Ctlr* ctlr)
+{
+	csr_outs(ctlr, WR_IntEna, WEvs);
+}
+
+static int
+w_cmd(Ctlr *ctlr, ushort cmd, ushort arg)
+{
+	int i, rc;
+
+	csr_outs(ctlr, WR_Parm0, arg);
+	csr_outs(ctlr, WR_Cmd, cmd);
+	for (i = 0; i<WTmOut; i++){
+		rc = csr_ins(ctlr, WR_EvSts);
+		if ( rc&WCmdEv ){
+			rc = csr_ins(ctlr, WR_Sts);
+			csr_ack(ctlr, WCmdEv);
+			if ((rc&WCmdMsk) != (cmd&WCmdMsk))
+				break;
+			if (rc&WResSts)
+				break;
+			return 0;
+		}
+	}
+
+	return -1;
+}
+
+static int
+w_seek(Ctlr* ctlr, ushort id, ushort offset, int chan)
+{
+	int i, rc;
+	static ushort sel[] = { WR_Sel0, WR_Sel1 };
+	static ushort off[] = { WR_Off0, WR_Off1 };
+
+	if (chan != 0 && chan != 1)
+		panic("wavelan: bad chan\n");
+	csr_outs(ctlr, sel[chan], id);
+	csr_outs(ctlr, off[chan], offset);
+	for (i=0; i<WTmOut; i++){
+		rc = csr_ins(ctlr, off[chan]);
+		if ((rc & (WBusyOff|WErrOff)) == 0)
+			return 0;
+	}
+	return -1;
+}
+
+static int
+w_inltv(Ctlr* ctlr, Wltv* ltv)
+{
+	int len;
+	ushort code;
+
+	if (w_cmd(ctlr, WCmdAccRd, ltv->type)){
+		DEBUG("wavelan: access read failed\n");
+		return -1;
+	}
+	if (w_seek(ctlr,ltv->type,0,1)){
+		DEBUG("wavelan: seek failed\n");
+		return -1;
+	}
+	len = csr_ins(ctlr, WR_Data1);
+	if (len > ltv->len)
+		return -1;
+	ltv->len = len;
+	if ((code=csr_ins(ctlr, WR_Data1)) != ltv->type){
+		DEBUG("wavelan: type %x != code %x\n",ltv->type,code);
+		return -1;
+	}
+	if(ltv->len > 0)
+		inss((ctlr)->iob+(WR_Data1), &ltv->val, ltv->len-1);
+
+	return 0;
+}
+
+static void
+w_outltv(Ctlr* ctlr, Wltv* ltv)
+{
+	if(w_seek(ctlr,ltv->type, 0, 1))
+		return;
+	outss((ctlr)->iob+(WR_Data1), ltv, ltv->len+1);
+	w_cmd(ctlr, WCmdAccWr, ltv->type);
+}
+
+static void
+ltv_outs(Ctlr* ctlr, int type, ushort val)
+{
+	Wltv ltv;
+
+	ltv.len = 2;
+	ltv.type = type;
+	ltv.val = val;
+	w_outltv(ctlr, &ltv);
+}
+
+static int
+ltv_ins(Ctlr* ctlr, int type)
+{
+	Wltv ltv;
+
+	ltv.len = 2;
+	ltv.type = type;
+	ltv.val = 0;
+	if(w_inltv(ctlr, &ltv))
+		return -1;
+	return ltv.val;
+}
+
+static void
+ltv_outstr(Ctlr* ctlr, int type, char* val)
+{
+	Wltv ltv;
+	int len;
+
+	len = strlen(val);
+	if(len > sizeof(ltv.s))
+		len = sizeof(ltv.s);
+	memset(&ltv, 0, sizeof(ltv));
+	ltv.len = (sizeof(ltv.type)+sizeof(ltv.slen)+sizeof(ltv.s))/2;
+	ltv.type = type;
+
+//	This should be ltv.slen = len; according to Axel Belinfante
+	ltv.slen = len;	
+
+	strncpy(ltv.s, val, len);
+	w_outltv(ctlr, &ltv);
+}
+
+static char Unkname[] = "who knows";
+static char Nilname[] = "card does not tell";
+
+static char*
+ltv_inname(Ctlr* ctlr, int type)
+{
+	static Wltv ltv;
+	int len;
+
+	memset(&ltv,0,sizeof(ltv));
+	ltv.len = WNameLen/2+2;
+	ltv.type = type;
+	if (w_inltv(ctlr, &ltv))
+		return Unkname;
+	len = ltv.slen;
+	if(len == 0 || ltv.s[0] == 0)
+		return Nilname;
+	if(len >= sizeof ltv.s)
+		len = sizeof ltv.s - 1;
+	ltv.s[len] = '\0';
+	return ltv.s;
+}
+
+static int
+w_read(Ctlr* ctlr, int type, int off, void* buf, ulong len)
+{
+	if (w_seek(ctlr, type, off, 1)){
+		DEBUG("wavelan: w_read: seek failed");
+		return 0;
+	}
+	inss((ctlr)->iob+(WR_Data1), buf, len/2);
+
+	return len;
+}
+
+static int
+w_write(Ctlr* ctlr, int type, int off, void* buf, ulong len)
+{
+	int tries;
+
+	for (tries=0; tries < WTmOut; tries++){
+		if (w_seek(ctlr, type, off, 0)){
+			DEBUG("wavelan: w_write: seek failed\n");
+			return 0;
+		}
+
+		outss((ctlr)->iob+(WR_Data0), buf, len/2);
+
+		csr_outs(ctlr, WR_Data0, 0xdead);
+		csr_outs(ctlr, WR_Data0, 0xbeef);
+		if (w_seek(ctlr, type, off + len, 0)){
+			DEBUG("wavelan: write seek failed\n");
+			return 0;
+		}
+		if (csr_ins(ctlr, WR_Data0) == 0xdead)
+		if (csr_ins(ctlr, WR_Data0) == 0xbeef)
+			return len;
+		DEBUG("wavelan: Hermes bug byte.\n");
+		return 0;
+	}
+	DEBUG("wavelan: tx timeout\n");
+	return 0;
+}
+
+static int
+w_alloc(Ctlr* ctlr, int len)
+{
+	int rc;
+	int i,j;
+
+	if (w_cmd(ctlr, WCmdMalloc, len)==0)
+		for (i = 0; i<WTmOut; i++)
+			if (csr_ins(ctlr, WR_EvSts) & WAllocEv){
+				csr_ack(ctlr, WAllocEv);
+				rc=csr_ins(ctlr, WR_Alloc);
+				if (w_seek(ctlr, rc, 0, 0))
+					return -1;
+				len = len/2;
+				for (j=0; j<len; j++)
+					csr_outs(ctlr, WR_Data0, 0);
+				return rc;
+			}
+	return -1;
+}
+
+static int
+w_enable(Ether* ether)
+{
+	Wltv ltv;
+	Ctlr* ctlr = (Ctlr*) ether->ctlr;
+
+	if (!ctlr)
+		return -1;
+
+	w_intdis(ctlr);
+	w_cmd(ctlr, WCmdDis, 0);
+	w_intdis(ctlr);
+	if(w_cmd(ctlr, WCmdIni, 0))
+		return -1;
+	w_intdis(ctlr);
+
+	ltv_outs(ctlr, WTyp_Tick, 8);
+	ltv_outs(ctlr, WTyp_MaxLen, ctlr->maxlen);
+	ltv_outs(ctlr, WTyp_Ptype, ctlr->ptype);
+	ltv_outs(ctlr, WTyp_RtsThres, ctlr->rtsthres);
+	ltv_outs(ctlr, WTyp_TxRate, ctlr->txrate);
+	ltv_outs(ctlr, WTyp_ApDens, ctlr->apdensity);
+	ltv_outs(ctlr, WTyp_PMWait, ctlr->pmwait);
+	ltv_outs(ctlr, WTyp_PM, ctlr->pmena);
+	if (*ctlr->netname)
+		ltv_outstr(ctlr, WTyp_NetName, ctlr->netname);
+	if (*ctlr->wantname)
+		ltv_outstr(ctlr, WTyp_WantName, ctlr->wantname);
+	ltv_outs(ctlr, WTyp_Chan, ctlr->chan);
+	if (*ctlr->nodename)
+		ltv_outstr(ctlr, WTyp_NodeName, ctlr->nodename);
+	ltv.len = 4;
+	ltv.type = WTyp_Mac;
+	memmove(ltv.addr, ether->ea, Eaddrlen);
+	w_outltv(ctlr, &ltv);
+
+	ltv_outs(ctlr, WTyp_Prom, (ether->prom?1:0));
+
+	if (ctlr->hascrypt){
+		ltv_outs(ctlr, WTyp_Crypt, ctlr->crypt);
+		ltv_outs(ctlr, WTyp_TxKey, ctlr->txkey);
+		w_outltv(ctlr, &ctlr->keys);
+		ltv_outs(ctlr, WTyp_XClear, ctlr->xclear);
+	}
+
+	// BUG: set multicast addresses
+
+	if (w_cmd(ctlr, WCmdEna, 0)){
+		DEBUG("wavelan: Enable failed");
+		return -1;
+	}
+	ctlr->txdid = w_alloc(ctlr, 1518 + sizeof(WFrame) + 8);
+	ctlr->txmid = w_alloc(ctlr, 1518 + sizeof(WFrame) + 8);
+	if (ctlr->txdid == -1 || ctlr->txmid == -1)
+		DEBUG("wavelan: alloc failed");
+	ctlr->txbusy = 0;
+	w_intena(ctlr);
+	return 0;
+}
+
+static void
+w_rxdone(Ether* ether)
+{
+	Ctlr* ctlr = (Ctlr*) ether->ctlr;
+	int len, sp;
+	WFrame f;
+	Block* bp=0;
+	Etherpkt* ep;
+
+	sp = csr_ins(ctlr, WR_RXId);
+	len = w_read(ctlr, sp, 0, &f, sizeof(f));
+	if (len == 0){
+		DEBUG("wavelan: read frame error\n");
+		goto rxerror;
+	}
+	if (f.sts&WF_Err){
+		goto rxerror;
+	}
+	switch(f.sts){
+	case WF_1042:
+	case WF_Tunnel:
+	case WF_WMP:
+		len = f.dlen + WSnapHdrLen;
+		bp = iallocb(ETHERHDRSIZE + len + 2);
+		if (!bp)
+			goto rxerror;
+		ep = (Etherpkt*) bp->wp;
+		memmove(ep->d, f.addr1, Eaddrlen);
+		memmove(ep->s, f.addr2, Eaddrlen);
+		memmove(ep->type,&f.type,2);
+		bp->wp += ETHERHDRSIZE;
+		if (w_read(ctlr, sp, WF_802_11_Off, bp->wp, len+2) == 0){
+			DEBUG("wavelan: read 802.11 error\n");
+			goto rxerror;
+		}
+		bp->wp = bp->rp+(ETHERHDRSIZE+f.dlen);
+		break;
+	default:
+		len = ETHERHDRSIZE + f.dlen + 2;
+		bp = iallocb(len);
+		if (!bp)
+			goto rxerror;
+		if (w_read(ctlr, sp, WF_802_3_Off, bp->wp, len) == 0){
+			DEBUG("wavelan: read 800.3 error\n");
+			goto rxerror;
+		}
+		bp->wp += len;
+	}
+
+	ctlr->nrx++;
+	etheriq(ether,bp,1);
+	ctlr->signal = ((ctlr->signal*15)+((f.qinfo>>8) & 0xFF))/16;
+	ctlr->noise = ((ctlr->noise*15)+(f.qinfo & 0xFF))/16;
+	return;
+
+rxerror:
+	freeb(bp);
+	ctlr->nrxerr++;
+}
+
+static void
+w_txstart(Ether* ether)
+{
+	Etherpkt *pkt;
+	Ctlr *ctlr;
+	Block *bp;
+	int len, off;
+
+	if((ctlr = ether->ctlr) == nil || ctlr->attached == 0 || ctlr->txbusy)
+		return;
+
+	if((bp = qget(ether->oq)) == nil)
+		return;
+	pkt = (Etherpkt*)bp->rp;
+
+	//
+	// If the packet header type field is > 1500 it is an IP or
+	// ARP datagram, otherwise it is an 802.3 packet. See RFC1042.
+	//
+	memset(&ctlr->txf, 0, sizeof(ctlr->txf));
+	if(((pkt->type[0]<<8)|pkt->type[1]) > 1500){
+		ctlr->txf.framectl = WF_Data;
+		memmove(ctlr->txf.addr1, pkt->d, Eaddrlen);
+		memmove(ctlr->txf.addr2, pkt->s, Eaddrlen);
+		memmove(ctlr->txf.dstaddr, pkt->d, Eaddrlen);
+		memmove(ctlr->txf.srcaddr, pkt->s, Eaddrlen);
+		memmove(&ctlr->txf.type, pkt->type, 2);
+		bp->rp += ETHERHDRSIZE;
+		len = BLEN(bp);
+		off = WF_802_11_Off;
+		ctlr->txf.dlen = len+ETHERHDRSIZE-WSnapHdrLen;
+		hnputs((uchar*)&ctlr->txf.dat[0], WSnap0);
+		hnputs((uchar*)&ctlr->txf.dat[1], WSnap1);
+		hnputs((uchar*)&ctlr->txf.len, len+ETHERHDRSIZE-WSnapHdrLen);
+	}
+	else{
+		len = BLEN(bp);
+		off = WF_802_3_Off;
+		ctlr->txf.dlen = len;
+	}
+	w_write(ctlr, ctlr->txdid, 0, &ctlr->txf, sizeof(ctlr->txf));
+	w_write(ctlr, ctlr->txdid, off, bp->rp, len+2);
+
+	if(w_cmd(ctlr, WCmdReclaim|WCmdTx, ctlr->txdid)){
+		DEBUG("wavelan: transmit failed\n");
+		ctlr->ntxerr++;
+	}
+	else{
+		ctlr->txbusy = 1;
+		ctlr->txtmout = 2;
+	}
+	freeb(bp);
+}
+
+static void
+w_txdone(Ctlr* ctlr, int sts)
+{
+	ctlr->txbusy = 0;
+	ctlr->txtmout = 0;
+	if (sts & WTxErrEv)
+		ctlr->ntxerr++;
+	else
+		ctlr->ntx++;
+}
+
+static int
+w_stats(Ctlr* ctlr)
+{
+	int i, rc, sp;
+	Wltv ltv;
+	ulong* p = (ulong*)&ctlr->WStats;
+	ulong* pend = (ulong*)&ctlr->end;
+
+	sp = csr_ins(ctlr, WR_InfoId);
+	ltv.len = ltv.type = 0;
+	w_read(ctlr, sp, 0, &ltv, 4);
+	if (ltv.type == WTyp_Stats){
+		ltv.len--;
+		for (i = 0; i < ltv.len && p < pend; i++){
+			rc = csr_ins(ctlr, WR_Data1);
+			if (rc > 0xf000)
+				rc = ~rc & 0xffff;
+			p[i] += rc;
+		}
+		return 0;
+	}
+	return -1;
+}
+
+static void
+w_intr(Ether *ether)
+{
+	int rc, txid;
+	Ctlr* ctlr = (Ctlr*) ether->ctlr;
+
+	if (ctlr->attached == 0){
+		csr_ack(ctlr, 0xffff);
+		csr_outs(ctlr, WR_IntEna, 0);
+		return;
+	}
+
+	csr_outs(ctlr, WR_IntEna, 0);
+	rc = csr_ins(ctlr, WR_EvSts);
+	csr_ack(ctlr, ~WEvs);	// Not interested on them
+
+	if (rc & WRXEv){
+		w_rxdone(ether);
+		csr_ack(ctlr, WRXEv);
+	}
+	if (rc & WTXEv){
+		w_txdone(ctlr, rc);
+		csr_ack(ctlr, WTXEv);
+	}
+	if (rc & WAllocEv){
+		ctlr->nalloc++;
+		txid = csr_ins(ctlr, WR_Alloc);
+		csr_ack(ctlr, WAllocEv);
+		if (txid == ctlr->txdid){
+			if ((rc & WTXEv) == 0)
+				w_txdone(ctlr, rc);
+		}
+	}
+	if (rc & WInfoEv){
+		ctlr->ninfo++;
+		w_stats(ctlr);
+		csr_ack(ctlr, WInfoEv);
+	}
+	if (rc & WTxErrEv){
+		w_txdone(ctlr, rc);
+		csr_ack(ctlr, WTxErrEv);
+	}
+	if (rc & WIDropEv){
+		ctlr->nidrop++;
+		csr_ack(ctlr, WIDropEv);
+	}
+
+	w_intena(ctlr);
+	w_txstart(ether);
+}
+
+// Watcher to ensure that the card still works properly and
+// to request WStats updates once a minute.
+// BUG: it runs much more often, see the comment below.
+
+static void
+w_timer(void* arg)
+{
+	Ether* ether = (Ether*) arg;
+	Ctlr* ctlr = (Ctlr*)ether->ctlr;
+
+	for(;;){
+		tsleep(&ctlr->timer, return0, 0, 50);
+		ctlr = (Ctlr*)ether->ctlr;
+		if (ctlr == 0)
+			break;
+		if (ctlr->attached == 0)
+			continue;
+		ctlr->ticks++;
+
+		ilock(ctlr);
+
+		// Seems that the card gets frames BUT does
+		// not send the interrupt; this is a problem because
+		// I suspect it runs out of receive buffers and
+		// stops receiving until a transmit watchdog
+		// reenables the card.
+		// The problem is serious because it leads to
+		// poor rtts.
+		// This can be seen clearly by commenting out
+		// the next if and doing a ping: it will stop
+		// receiving (although the icmp replies are being
+		// issued from the remote) after a few seconds.
+		// Of course this `bug' could be because I'm reading
+		// the card frames in the wrong way; due to the
+		// lack of documentation I cannot know.
+
+//		if (csr_ins(ctlr, WR_EvSts)&WEvs){
+//			ctlr->tickintr++;
+//			w_intr(ether);
+//		}
+
+		if ((ctlr->ticks % 10) == 0) {
+			if (ctlr->txtmout && --ctlr->txtmout == 0){
+				ctlr->nwatchdogs++;
+				w_txdone(ctlr, WTxErrEv);
+				if (w_enable(ether)){
+					DEBUG("wavelan: wdog enable failed\n");
+				}
+				w_txstart(ether);
+			}
+			if ((ctlr->ticks % 120) == 0)
+			if (ctlr->txbusy == 0)
+				w_cmd(ctlr, WCmdAskStats, WTyp_Stats);
+		}
+		iunlock(ctlr);
+	}
+	pexit("terminated",0);
+}
+
+static void
+multicast(void*, uchar*, int)
+{
+	// BUG: to be added.
+}
+
+static void
+attach(Ether* ether)
+{
+	Ctlr* ctlr;
+	char name[64];
+	int rc;
+
+	if (ether->ctlr == 0)
+		return;
+
+	snprint(name, sizeof(name), "#l%dtimer", ether->ctlrno);
+	ctlr = (Ctlr*) ether->ctlr;
+	if (ctlr->attached == 0){
+		ilock(ctlr);
+		rc = w_enable(ether);
+		iunlock(ctlr);
+		if(rc == 0){
+			ctlr->attached = 1;
+			kproc(name, w_timer, ether, 0);
+		} else
+			print("#l%d: enable failed\n",ether->ctlrno);
+	}
+}
+
+#define PRINTSTAT(fmt,val)	l += snprint(p+l, READSTR-l, (fmt), (val))
+#define PRINTSTR(fmt)		l += snprint(p+l, READSTR-l, (fmt))
+
+static long
+ifstat(Ether* ether, void* a, long n, ulong offset)
+{
+	Ctlr *ctlr = (Ctlr*) ether->ctlr;
+	char *k, *p;
+	int i, l, txid;
+
+	ether->oerrs = ctlr->ntxerr;
+	ether->crcs = ctlr->nrxfcserr;
+	ether->frames = 0;
+	ether->buffs = ctlr->nrxdropnobuf;
+	ether->overflows = 0;
+
+	//
+	// Offset must be zero or there's a possibility the
+	// new data won't match the previous read.
+	//
+	if(n == 0 || offset != 0)
+		return 0;
+
+	p = malloc(READSTR);
+	l = 0;
+
+	PRINTSTAT("Signal: %d\n", ctlr->signal-149);
+	PRINTSTAT("Noise: %d\n", ctlr->noise-149);
+	PRINTSTAT("SNR: %ud\n", ctlr->signal-ctlr->noise);
+	PRINTSTAT("Interrupts: %lud\n", ctlr->nints);
+	PRINTSTAT("TxPackets: %lud\n", ctlr->ntx);
+	PRINTSTAT("RxPackets: %lud\n", ctlr->nrx);
+	PRINTSTAT("TxErrors: %lud\n", ctlr->ntxerr);
+	PRINTSTAT("RxErrors: %lud\n", ctlr->nrxerr);
+	PRINTSTAT("TxRequests: %lud\n", ctlr->ntxrq);
+	PRINTSTAT("AllocEvs: %lud\n", ctlr->nalloc);
+	PRINTSTAT("InfoEvs: %lud\n", ctlr->ninfo);
+	PRINTSTAT("InfoDrop: %lud\n", ctlr->nidrop);
+	PRINTSTAT("WatchDogs: %lud\n", ctlr->nwatchdogs);
+	PRINTSTAT("Ticks: %ud\n", ctlr->ticks);
+	PRINTSTAT("TickIntr: %ud\n", ctlr->tickintr);
+	k = ((ctlr->attached) ? "attached" : "not attached");
+	PRINTSTAT("Card %s", k);
+	k = ((ctlr->txbusy)? ", txbusy" : "");
+	PRINTSTAT("%s\n", k);
+
+	if (ctlr->hascrypt){
+		PRINTSTR("Keys: ");
+		for (i = 0; i < WNKeys; i++){
+			if (ctlr->keys.keys[i].len == 0)
+				PRINTSTR("none ");
+			else if (SEEKEYS == 0)
+				PRINTSTR("set ");
+			else
+				PRINTSTAT("%s ", ctlr->keys.keys[i].dat);
+		}
+		PRINTSTR("\n");
+	}
+
+	// real card stats
+	ilock(ctlr);
+	PRINTSTR("\nCard stats: \n");
+	PRINTSTAT("Status: %ux\n", csr_ins(ctlr, WR_Sts));
+	PRINTSTAT("Event status: %ux\n", csr_ins(ctlr, WR_EvSts));
+	i = ltv_ins(ctlr, WTyp_Ptype);
+	PRINTSTAT("Port type: %d\n", i);
+	PRINTSTAT("Transmit rate: %d\n", ltv_ins(ctlr, WTyp_TxRate));
+	PRINTSTAT("Current Transmit rate: %d\n",
+		ltv_ins(ctlr, WTyp_CurTxRate));
+	PRINTSTAT("Channel: %d\n", ltv_ins(ctlr, WTyp_Chan));
+	PRINTSTAT("AP density: %d\n", ltv_ins(ctlr, WTyp_ApDens));
+	PRINTSTAT("Promiscuous mode: %d\n", ltv_ins(ctlr, WTyp_Prom));
+	if(i == 3)
+		PRINTSTAT("SSID name: %s\n", ltv_inname(ctlr, WTyp_NetName));
+	else {
+		Wltv ltv;
+		PRINTSTAT("Current name: %s\n", ltv_inname(ctlr, WTyp_CurName));
+		ltv.type = WTyp_BaseID;
+		ltv.len = 4;
+		if (w_inltv(ctlr, &ltv))
+			print("#l%d: unable to read base station mac addr\n", ether->ctlrno);
+		l += snprint(p+l, READSTR-l, "Base station: %2.2x%2.2x%2.2x%2.2x%2.2x%2.2x\n",
+			ltv.addr[0], ltv.addr[1], ltv.addr[2], ltv.addr[3], ltv.addr[4], ltv.addr[5]);
+	}
+	PRINTSTAT("Net name: %s\n", ltv_inname(ctlr, WTyp_WantName));
+	PRINTSTAT("Node name: %s\n", ltv_inname(ctlr, WTyp_NodeName));
+	if (ltv_ins(ctlr, WTyp_HasCrypt) == 0)
+		PRINTSTR("WEP: not supported\n");
+	else {
+		if (ltv_ins(ctlr, WTyp_Crypt) == 0)
+			PRINTSTR("WEP: disabled\n");
+		else{
+			PRINTSTR("WEP: enabled\n");
+			k = ((ctlr->xclear)? "excluded": "included");
+			PRINTSTAT("Clear packets: %s\n", k);
+			txid = ltv_ins(ctlr, WTyp_TxKey);
+			PRINTSTAT("Transmit key id: %d\n", txid);
+		}
+	}
+	iunlock(ctlr);
+
+	PRINTSTAT("ntxuframes: %lud\n", ctlr->ntxuframes);
+	PRINTSTAT("ntxmframes: %lud\n", ctlr->ntxmframes);
+	PRINTSTAT("ntxfrags: %lud\n", ctlr->ntxfrags);
+	PRINTSTAT("ntxubytes: %lud\n", ctlr->ntxubytes);
+	PRINTSTAT("ntxmbytes: %lud\n", ctlr->ntxmbytes);
+	PRINTSTAT("ntxdeferred: %lud\n", ctlr->ntxdeferred);
+	PRINTSTAT("ntxsretries: %lud\n", ctlr->ntxsretries);
+	PRINTSTAT("ntxmultiretries: %lud\n", ctlr->ntxmultiretries);
+	PRINTSTAT("ntxretrylimit: %lud\n", ctlr->ntxretrylimit);
+	PRINTSTAT("ntxdiscards: %lud\n", ctlr->ntxdiscards);
+	PRINTSTAT("nrxuframes: %lud\n", ctlr->nrxuframes);
+	PRINTSTAT("nrxmframes: %lud\n", ctlr->nrxmframes);
+	PRINTSTAT("nrxfrags: %lud\n", ctlr->nrxfrags);
+	PRINTSTAT("nrxubytes: %lud\n", ctlr->nrxubytes);
+	PRINTSTAT("nrxmbytes: %lud\n", ctlr->nrxmbytes);
+	PRINTSTAT("nrxfcserr: %lud\n", ctlr->nrxfcserr);
+	PRINTSTAT("nrxdropnobuf: %lud\n", ctlr->nrxdropnobuf);
+	PRINTSTAT("nrxdropnosa: %lud\n", ctlr->nrxdropnosa);
+	PRINTSTAT("nrxcantdecrypt: %lud\n", ctlr->nrxcantdecrypt);
+	PRINTSTAT("nrxmsgfrag: %lud\n", ctlr->nrxmsgfrag);
+	PRINTSTAT("nrxmsgbadfrag: %lud\n", ctlr->nrxmsgbadfrag);
+	USED(l);
+	n = readstr(offset, a, n, p);
+	free(p);
+	return n;
+}
+#undef PRINTSTR
+#undef PRINTSTAT
+
+static int
+w_option(Ctlr* ctlr, char* buf, long n)
+{
+	char *p;
+	int i, r;
+	WKey *key;
+	Cmdbuf *cb;
+
+	r = 0;
+
+	cb = parsecmd(buf, n);
+	if(cb->nf < 2)
+		r = -1;
+	else if(cistrcmp(cb->f[0], "essid") == 0){
+		if (cistrcmp(cb->f[1],"default") == 0)
+			p = "";
+		else
+			p = cb->f[1];
+		switch(ctlr->ptype){
+		case 0:
+		case 3:
+			memset(ctlr->netname, 0, sizeof(ctlr->netname));
+			strncpy(ctlr->netname, p, WNameLen);
+			if(ctlr->ptype == 3)
+				break;
+			/* fall through to set both for peer-to-peer */
+		default:
+			memset(ctlr->wantname, 0, sizeof(ctlr->wantname));
+			strncpy(ctlr->wantname, p, WNameLen);
+			break;
+		}
+	}
+	else if(cistrcmp(cb->f[0], "station") == 0){
+		memset(ctlr->nodename, 0, sizeof(ctlr->nodename));
+		strncpy(ctlr->nodename, cb->f[1], WNameLen);
+	}
+	else if(cistrcmp(cb->f[0], "channel") == 0){
+		if((i = atoi(cb->f[1])) >= 1 && i <= 16)
+			ctlr->chan = i;
+		else
+			r = -1;
+	}
+	else if(cistrcmp(cb->f[0], "mode") == 0){
+		if(cistrcmp(cb->f[1], "managed") == 0)
+			ctlr->ptype = WPTypeManaged;
+		else if(cistrcmp(cb->f[1], "wds") == 0)
+			ctlr->ptype = WPTypeWDS;
+		else if(cistrcmp(cb->f[1], "adhoc") == 0)
+			ctlr->ptype = WPTypeAdHoc;
+		else if(cistrcmp(cb->f[1], "peertopeer") == 0)
+			ctlr->ptype = WPTypePeerToPeer;
+		else if((i = atoi(cb->f[1])) >= 0 && i <= 3)
+			ctlr->ptype = i;
+		else
+			r = -1;
+	}
+	else if(cistrcmp(cb->f[0], "crypt") == 0){
+		if(cistrcmp(cb->f[1], "off") == 0)
+			ctlr->crypt = 0;
+		else if(cistrcmp(cb->f[1], "on") == 0 && ctlr->hascrypt)
+			ctlr->crypt = 1;
+		else
+			r = -1;
+	}
+	else if(cistrcmp(cb->f[0], "clear") == 0){
+		if(cistrcmp(cb->f[1], "on") == 0)
+			ctlr->xclear = 0;
+		else if(cistrcmp(cb->f[1], "off") == 0 && ctlr->hascrypt)
+			ctlr->xclear = 1;
+		else
+			r = -1;
+	}
+	else if(strncmp(cb->f[0], "key", 3) == 0){
+		if((i = atoi(cb->f[0]+3)) >= 1 && i <= WNKeys){
+			ctlr->txkey = i-1;
+			key = &ctlr->keys.keys[ctlr->txkey];
+			key->len = strlen(cb->f[1]);
+			if (key->len > WKeyLen)
+				key->len = WKeyLen;
+			memset(key->dat, 0, sizeof(key->dat));
+			memmove(key->dat, cb->f[1], key->len);
+		}
+		else
+			r = -1;
+	}
+	else if(cistrcmp(cb->f[0], "txkey") == 0){
+		if((i = atoi(cb->f[1])) >= 1 && i <= WNKeys)
+			ctlr->txkey = i-1;
+		else
+			r = -1;
+	}
+	else if(cistrcmp(cb->f[0], "pm") == 0){
+		if(cistrcmp(cb->f[1], "off") == 0)
+			ctlr->pmena = 0;
+		else if(cistrcmp(cb->f[1], "on") == 0){
+			ctlr->pmena = 1;
+			if(cb->nf == 3){
+				i = atoi(cb->f[2]);
+				// check range here? what are the units?
+				ctlr->pmwait = i;
+			}
+		}
+		else
+			r = -1;
+	}
+	else
+		r = -2;
+	free(cb);
+
+	return r;
+}
+
+static long
+ctl(Ether* ether, void* buf, long n)
+{
+	Ctlr *ctlr;
+
+	if((ctlr = ether->ctlr) == nil)
+		error(Enonexist);
+	if(ctlr->attached == 0)
+		error(Eshutdown);
+
+	ilock(ctlr);
+	if(w_option(ctlr, buf, n)){
+		iunlock(ctlr);
+		error(Ebadctl);
+	}
+	if(ctlr->txbusy)
+		w_txdone(ctlr, WTxErrEv);
+	w_enable(ether);
+	w_txstart(ether);
+	iunlock(ctlr);
+
+	return n;
+}
+
+static void
+transmit(Ether* ether)
+{
+	Ctlr* ctlr = ether->ctlr;
+
+	if (ctlr == 0)
+		return;
+
+	ilock(ctlr);
+	ctlr->ntxrq++;
+	w_txstart(ether);
+	iunlock(ctlr);
+}
+
+static void
+promiscuous(void* arg, int on)
+{
+	Ether* ether = (Ether*)arg;
+	Ctlr* ctlr = ether->ctlr;
+
+	if (ctlr == nil)
+		error("card not found");
+	if (ctlr->attached == 0)
+		error("card not attached");
+	ilock(ctlr);
+	ltv_outs(ctlr, WTyp_Prom, (on?1:0));
+	iunlock(ctlr);
+}
+
+static void
+interrupt(Ureg* ,void* arg)
+{
+	Ether* ether = (Ether*) arg;
+	Ctlr* ctlr = (Ctlr*) ether->ctlr;
+
+	if (ctlr == 0)
+		return;
+	ilock(ctlr);
+	ctlr->nints++;
+	w_intr(ether);
+	iunlock(ctlr);
+}
+
+static int
+reset(Ether* ether)
+{
+	int i;
+	Wltv ltv;
+	Ctlr* ctlr;
+	int slot;
+	char *p;
+
+	if ((slot = pcmspecial("WaveLAN/IEEE", ether))<0){
+		DEBUG("no wavelan found\n");
+		return -1;
+	}
+
+	if((ctlr = malloc(sizeof(Ctlr))) == nil)
+		return -1;
+
+	ilock(ctlr);
+
+	if (ether->port==0)
+		ether->port = WDfltIOB;
+	ctlr->iob = ether->port;
+	ctlr->slot = slot;
+
+	if (ioalloc(ether->port,WIOLen,0,"wavelan")<0){
+		print("#l%d: port 0x%lx in use\n",
+				ether->ctlrno, ether->port);
+		goto abort;
+	}
+	DEBUG("#l%d: port=0x%lx irq=%ld\n",
+			ether->ctlrno, ether->port, ether->irq);
+
+	w_intdis(ctlr);
+	if (w_cmd(ctlr,WCmdIni,0)){
+		print("#l%d: init failed\n", ether->ctlrno);
+		goto abort;
+	}
+	w_intdis(ctlr);
+	ltv_outs(ctlr, WTyp_Tick, 8);
+
+	ctlr->chan = 0;
+	ctlr->ptype = WDfltPType;
+	ctlr->txkey = 0;
+	ctlr->keys.len = sizeof(WKey)*WNKeys/2 + 1;
+	ctlr->keys.type = WTyp_Keys;
+	if(ctlr->hascrypt = ltv_ins(ctlr, WTyp_HasCrypt))
+		ctlr->crypt = 1;
+	*ctlr->netname = *ctlr->wantname = 0;
+	strcpy(ctlr->nodename, "Plan 9 STA");
+
+	for(i = 0; i < ether->nopt; i++){
+		//
+		// The max. length of an 'opt' is ISAOPTLEN in dat.h.
+		// It should be > 16 to give reasonable name lengths.
+		//
+		if(p = strchr(ether->opt[i], '='))
+			*p = ' ';
+		w_option(ctlr, ether->opt[i], strlen(ether->opt[i]));
+	}
+
+	ctlr->netname[WNameLen-1] = 0;
+	ctlr->wantname[WNameLen-1] = 0;
+	ctlr->nodename[WNameLen-1] =0;
+
+	ltv.type = WTyp_Mac;
+	ltv.len	= 4;
+	if (w_inltv(ctlr, &ltv)){
+		print("#l%d: unable to read mac addr\n",
+			ether->ctlrno);
+		goto abort;
+	}
+	memmove(ether->ea, ltv.addr, Eaddrlen);
+
+	if (ctlr->chan == 0)
+		ctlr->chan = ltv_ins(ctlr, WTyp_Chan);
+	ctlr->apdensity = WDfltApDens;
+	ctlr->rtsthres = WDfltRtsThres;
+	ctlr->txrate = WDfltTxRate;
+	ctlr->maxlen = WMaxLen;
+	ctlr->pmena = 0;
+	ctlr->pmwait = 100;
+	ctlr->signal = 1;
+	ctlr->noise = 1;
+
+	// link to ether
+	ether->ctlr = ctlr;
+	ether->mbps = 10;
+	ether->attach = attach;
+	ether->interrupt = interrupt;
+	ether->transmit = transmit;
+	ether->ifstat = ifstat;
+	ether->ctl = ctl;
+	ether->promiscuous = promiscuous;
+	ether->multicast = multicast;
+	ether->arg = ether;
+
+//	DEBUG("#l%d: irq %ld port %lx type %s",
+//		ether->ctlrno, ether->irq, ether->port,	ether->type);
+//	DEBUG(" %2.2uX%2.2uX%2.2uX%2.2uX%2.2uX%2.2uX\n",
+//		ether->ea[0], ether->ea[1], ether->ea[2],
+//		ether->ea[3], ether->ea[4], ether->ea[5]);
+
+	iunlock(ctlr);
+	return 0;
+
+abort:
+	iunlock(ctlr);
+	free(ctlr);
+	ether->ctlr = nil;
+iprint("wave reset failed\n");
+	return -1;
+}
+
+void
+etherwavelanlink(void)
+{
+	addethercard("wavelan", reset);
+}
--- /dev/null
+++ b/os/ipaq1110/fns.h
@@ -1,0 +1,179 @@
+#include "../port/portfns.h"
+
+ulong	aifinit(uchar *aifarr);
+void	aamloop(int);
+int	archaudiopower(int);
+void	archaudiomute(int);
+void	archaudioamp(int);
+int	archaudiospeed(int, int);
+void	archcodecreset(void);
+void	archconfinit(void);
+void	archconsole(void);
+int	archflash12v(int);
+int	archhooksw(int);
+long	archkprofmicrosecondspertick(void);
+void	archkprofenable(int);
+void	archlcdenable(int);
+void	archpowerdown(void);
+void	archpowerup(void);
+void	archreboot(void);
+void	archreset(void);
+vlong	archrdtsc(void);
+ulong archrdtsc32(void);
+void	archuartpower(int, int);
+void	blankscreen(int);
+ulong	call_apcs(ulong addr, int nargs, ...);
+ulong	call_apcs0(ulong addr);
+ulong	call_apcs1(ulong addr, ulong a1);
+ulong	call_apcs2(ulong addr, ulong a1, ulong a2);
+ulong	call_apcs3(ulong addr, ulong a1, ulong a2, ulong a3);
+void	cisread(int slotno, void (*f)(int, uchar *));
+void	clockcheck(void);
+void	clockinit(void);
+void	clockpoll(void);
+#define	coherence()		/* nothing to do for cache coherence for uniprocessor */
+void	cursorhide(void);
+void	cursorunhide(void);
+void	dcflush(void*, ulong);
+void	dcflushall(void);
+void	dcinval(void);
+int	dmaidle(Dma*);
+Dma*	dmasetup(int device, int direction, int bigend, void(*)(void*,ulong), void*);
+int	dmastart(Dma*, void*, int);
+int	dmacontinue(Dma*, void*, int);
+void	dmastop(Dma*);
+int	dmaerror(Dma*);
+void	dmafree(Dma*);
+void	dmareset(void);
+void	dmawait(Dma*);
+void dumplongs(char *, ulong *, int);
+void	dumpregs(Ureg* ureg);
+void	dumpstack(void);
+int	fpiarm(Ureg*);
+void	fpinit(void);
+ulong	getcallerpc(void*);
+ulong	getcpsr(void);
+ulong	getcpuid(void);
+ulong	getspsr(void);
+void	gotopc(ulong);
+
+void	icflushall(void);
+void	_idlemode(void);
+void	(*idle)(void);
+void	idlehands(void);
+int	inb(ulong);
+int	ins(ulong);
+ulong	inl(ulong);
+void	outb(ulong, int);
+void	outs(ulong, int);
+void	outl(ulong, ulong);
+void inss(ulong, void*, int);
+void outss(ulong, void*, int);
+void	insb(ulong, void*, int);
+void	outsb(ulong, void*, int);
+void	intrdisable(int, void (*)(Ureg*, void*), void*, int, char*);
+void	intrenable(int, void (*)(Ureg*, void*), void*, int, char*);
+void	iofree(int);
+#define	iofree(x)
+void	ioinit(void);
+int	iounused(int, int);
+int	ioalloc(int, int, int, char*);
+#define	ioalloc(a,b,c,d) 0
+int	iprint(char*, ...);
+void	installprof(void (*)(Ureg *, int));
+int	isvalid_va(void*);
+void	kbdinit(void);
+void	L3init(void);
+int	L3read(int, void*, int);
+int	L3write(int, void*, int);
+void	lcd_setbacklight(int);
+void	lcd_sethz(int);
+void	lights(ulong);
+void	links(void);
+ulong	mcpgettfreq(void);
+void	mcpinit(void);
+void	mcpsettfreq(ulong tfreq);
+void	mcpspeaker(int, int);
+void	mcptelecomsetup(ulong hz, int adm, int xint, int rint);
+ushort	mcpadcread(int ts);
+void	mcptouchsetup(int ts);
+void	mcptouchintrenable(void);
+void	mcptouchintrdisable(void);
+void	mcpgpiowrite(ushort mask, ushort data);
+void	mcpgpiosetdir(ushort mask, ushort dir);
+ushort	mcpgpioread(void);
+void*	minicached(void*);
+void	minidcflush(void);
+void	mmuenable(ulong);
+ulong	mmugetctl(void);
+ulong	mmugetdac(void);
+ulong	mmugetfar(void);
+ulong	mmugetfsr(void);
+void	mmuinit(void);
+void*	mmuphysmap(ulong, ulong);
+void	mmuputctl(ulong);
+void	mmuputdac(ulong);
+void	mmuputfsr(ulong);
+void	mmuputttb(ulong);
+void	mmureset(void);
+void	mouseinit(void);
+void	nowriteSeg(void *, void *);
+void*	pa2va(ulong);
+void	pcmcisread(PCMslot*);
+int	pcmcistuple(int, int, int, void*, int);
+PCMmap*	pcmmap(int, ulong, int, int);
+void	pcmunmap(int, PCMmap*);
+int	pcmpin(int slot, int type);
+void	pcmpower(int slotno, int on);
+int	pcmpowered(int);
+void	pcmreset(int);
+void	pcmsetvcc(int, int);
+void	pcmsetvpp(int, int);
+int	pcmspecial(char *idstr, ISAConf *isa);
+void	pcmspecialclose(int slotno);
+void	pcmintrenable(int, void (*)(Ureg*, void*), void*);
+void	powerenable(void (*)(int));
+void	powerdisable(void (*)(int));
+void	powerdown(void);
+void	powerinit(void);
+void	powersuspend(void);
+#define procsave(p)
+#define procrestore(p)
+long	rtctime(void);
+void	screeninit(void);
+void	(*screenputs)(char*, int);
+int	segflush(void*, ulong);
+void	setpanic(void);
+void	setr13(int, void*);
+int	splfhi(void);
+int	splflo(void);
+void	_suspendcode(void);
+void	tlbinvalidate(void);
+void	tlbinvalidateaddr(void*);
+void	trapinit(void);
+void	trapstacks(void);
+void	trapspecial(int (*)(Ureg *, uint));
+int	uartprint(char*, ...);
+void	uartspecial(int, int, char, Queue**, Queue**, int (*)(Queue*, int));
+ulong	va2pa(void*);
+void	vectors(void);
+void	vtable(void);
+#define	waserror()	(up->nerrlab++, setlabel(&up->errlab[up->nerrlab-1]))
+int	wasbusy(int);
+
+#define KADDR(p)	((void *) p)
+#define PADDR(v)	va2pa((void*)(v))
+
+// #define timer_start()	(*OSCR)
+// #define timer_ticks(t)	(*OSCR - (ulong)(t))
+ulong	timer_start(void);
+ulong	timer_ticks(ulong);
+int 	timer_devwait(ulong *adr, ulong mask, ulong val, int ost);
+void 	timer_setwatchdog(int ost);
+void 	timer_delay(int ost);
+ulong	ms2tmr(int ms);
+int	tmr2ms(ulong t);
+void	delay(int ms);
+ulong	us2tmr(int us);
+int	tmr2us(ulong t);
+void 	microdelay(int us);
binary files /dev/null b/os/ipaq1110/inflate differ
--- /dev/null
+++ b/os/ipaq1110/io.h
@@ -1,0 +1,60 @@
+/*
+ * iPAQ 36xx-specific definitions
+ */
+
+/*
+ * GPIO assignment on iPAQ (see H3600 hardware spec).
+ * Following Plan 9, _i is input signal, _o is output, _io is both.
+ */
+enum {
+	GPIO_PWR_ON_i = 1<<0,	/* power on/off (active low)*/
+	GPIO_UP_IRQ_i = 1<<1,	/* microcontroller interrupt (active low) */
+	/* 2-9 are LCD 8-15 */
+	GPIO_CARD_IND1_i = 1<<10,	/* PCMCIA/CF socket 1 card inserted (active low) */
+	GPIO_CARD_IRQ1_i = 1<<11,	/* socket 1 IRQ (active low) */
+	GPIO_CLK_SET0_o = 1<<12,		/* codec clock select 0 */
+	GPIO_CLK_SET1_o = 1<<13,		/* codec clock select 1 */
+	GPIO_L3_SDA_io = 1<<14,		/* L3 data to/from UDA1341 */
+	GPIO_L3_MODE_o = 1<<15,		/* L3 mode to UDA1341 */
+	GPIO_L3_SCLK_o = 1<<16,		/* L3 SCLK to UDA1341 */
+	GPIO_CARD_IND0_i = 1<<17,		/* PCMCIA/CF socket 0 card inserted (active low) */
+	GPIO_KEY_ACT_i = 1<<18,	/* joypad centre button (active low) */
+	GPIO_SYS_CLK_i = 1<<19,	/* codec external clock */
+	GPIO_BAT_FAULT_i = 1<<20,	/* battery fault (active high) */
+	GPIO_CARD_IRQ0_i = 1<<21,	/* socket 0 IRQ (active low) */
+	GPIO_LOCK_i = 1<<22,	/* expansion pack lock/unlock signal (active low) */
+	GPIO_COM_DCD_i = 1<<23,	/* UART3 DCD from cradle (active high) */
+	GPIO_OPT_IRQ_i = 1<<24,	/* expansion pack shared IRQ (all but PCMCIA/CF, active high) */
+	GPIO_COM_CTS_i = 1<<25,	/* UART3 CTS (active high) */
+	GPIO_COM_RTS_o = 1<<26,	/* UART3 RTS (active high) */
+	GPIO_OPT_IND_i = 1<<27,	/* expansion pack inserted (active low) */
+};
+
+/* special EGPIO register, write only*/
+enum {
+	EGPIO_VPEN = 1<<0,	/* flash write enable */
+	EGPIO_CARD_RESET = 1<<1,	/* CF/PCMCIA reset signal */
+	EGPIO_OPT_RESET = 1<<2,	/* expansion pack reset for other than CF/PCMCIA */
+	EGPIO_CODEC_RESET = 1<<3,	/* codec reset signal (active low) */
+	EGPIO_OPT_PWR_ON = 1<<4,	/* enable power to NVRAM in expansion pack */
+	EGPIO_OPT_ON = 1<<5,	/* enable full power to expansion pack */
+	EGPIO_LCD_ON = 1<<6,	/* enable LCD 3.3v supply */
+	EGPIO_RS232_ON = 1<<7,	/* enable RS232 transceiver */
+	EGPIO_LCD_PCI = 1<<8,	/* enable power to LCD control IC */
+	EGPIO_IR_ON = 1<<9,	/* enable power to IR module */
+	EGPIO_AUD_ON = 1<<10,	/* enable power to audio amp */
+	EGPIO_AUD_PWR_ON = 1<<11,	/* enable power to all other audio circuitry */
+	EGPIO_QMUTE = 1<<12,	/* mute audio codec (nb: wastes power if set when audio not powered) */
+	EGPIO_IR_FSEL = 1<<13,	/* FIR mode selection: 1=FIR, 0=SIR */
+	EGPIO_LCD_5V_ON = 1<<14,	/* enable 5V to LCD module */
+	EGPIO_LVDD_ON = 1<<15,	/* enable 9V and -6.5V to LCD module */
+};
+
+/* board-dependent GPIO pin assignment for l3gpio.c */
+enum {
+	L3Data = GPIO_L3_SDA_io,
+	L3Mode = GPIO_L3_MODE_o,
+	L3Clock = GPIO_L3_SCLK_o,
+};
+
+#include "../sa1110/sa1110io.h"
--- /dev/null
+++ b/os/ipaq1110/ipaq
@@ -1,0 +1,175 @@
+dev
+	root
+	cons archipaq	lcd l3gpio
+	env
+	gpio
+	mnt
+	pipe
+	prog
+	rtc
+	srv
+	dup
+	ssl
+	cap
+#	sign
+	draw	screen
+	pointer
+	uart
+	ip	ip ipv6 ipaux iproute arp netlog ptclbsum iprouter plan9 nullmedium pktmedium netaux
+	flash
+	ftl
+	pcmcia	cis
+	ether netif netaux
+
+	audio
+	ipaq	suspend
+	kprof
+
+ip
+	il
+	tcp
+	udp
+#	rudp
+#	igmp
+	ipifc
+	icmp
+	icmp6
+#	ipmux
+
+link	
+	flashcfi16
+	etherwavelan
+	ethermedium
+
+lib
+	interp
+	tk
+	draw
+	memlayer
+	memdraw
+	keyring
+	sec
+	mp
+	math
+	kern
+
+mod
+	math
+	sys
+	draw
+	tk
+	keyring
+	crypt
+	ipints
+
+
+port
+	alarm
+	alloc
+	allocb
+	chan
+	dev
+	dial
+	dis
+	discall
+	exception
+	exportfs
+	inferno
+	latin1
+	nocache
+	nodynld
+	parse
+	pgrp
+	print
+	proc
+	qio
+	qlock
+	random
+	sysfile
+	taslock
+	xalloc
+
+code
+	int kernel_pool_pcnt = 10;
+	int main_pool_pcnt = 33;
+	int heap_pool_pcnt = 34;
+	int image_pool_pcnt = 33;
+	int cflag = 0;	/* for JIT */
+
+	int consoleprint = 1;
+	int redirectconsole = 1;
+	char debug_keys = 1;
+	int panicreset = 0;
+	char *tkfont = "/fonts/lucidasans/unicode.7.font";
+	int	tkstylus = 1;
+
+init
+	ipaqinit
+
+root
+	/chan	/
+	/dev	/
+	/dis
+	/env	/
+	/fd	/
+	/net	/
+	/net.alt	/
+	/nvfs /
+	/prog	/
+	/dis/lib
+	/dis/disk
+	/osinit.dis
+
+# initialisation
+	/dis/touchcal.dis
+
+# dos file system
+	/dis/dossrv.dis
+	/dis/disk/format.dis
+
+# kfs file system
+	/dis/disk/kfs.dis
+	/dis/disk/kfscmd.dis
+
+# used by file systems and commands
+	/dis/lib/arg.dis
+	/dis/lib/styx.dis
+	/dis/lib/string.dis
+	/dis/lib/daytime.dis
+
+# For development work:
+	/dis/sh.dis	/dis/tiny/sh.dis
+	/dis/ls.dis
+	/dis/cat.dis
+	/dis/bind.dis
+	/dis/mount.dis
+	/dis/pwd.dis
+	/dis/echo.dis
+	/dis/cd.dis
+	/dis/xd.dis
+	/dis/cp.dis
+	/dis/mkdir.dis
+	/dis/rm.dis
+	/dis/p.dis
+	/dis/ps.dis
+	/dis/lib/readdir.dis
+	/dis/lib/workdir.dis
+	/dis/lib/daytime.dis
+	/dis/lib/auth.dis
+	/dis/lib/ssl.dis
+	/dis/lib/bufio.dis
+	/dis/lib/string.dis
+#	/dis/pcmcia.dis	/dis/auxi/pcmcia.dis
+# dhcp
+	/dis/lib/dhcpclient.dis	/dis/lib/dhcpclient.dis
+	/dis/lib/ip.dis	/dis/lib/ip.dis
+
+	/n/remote
+	/n/local
+	/n/client
+	/n/rdbg
+	/n/dump
+	/n/disk
+	/n/kfs
+# Authentication
+	/nvfs/default	/usr/inferno/keyring/default
--- /dev/null
+++ b/os/ipaq1110/lcd.c
@@ -1,0 +1,185 @@
+#include	"u.h"
+#include 	"mem.h"
+#include	"../port/lib.h"
+#include 	"dat.h"
+#include 	"draw.h"
+#include	"fns.h"
+#include	"io.h"
+#include	<memdraw.h>
+#include	"screen.h"
+
+#define	DPRINT	if(1)iprint
+
+enum {
+	/* lccr0 */
+	EnableCtlr = 1<<0,	/* controller enable */
+	IsColour = 0<<1,
+	IsMono = 1<<1,
+	SinglePanel = 0<<2,
+	DualPanel = 1<<2,
+	DisableDone = 1<<3,
+	DisableBAU = 1<<4,
+	DisableErr = 1<<5,
+	PassivePanel = 0<<7,
+	ActivePanel = 1<<7,
+	BigEndian = 1<<8,
+	DoublePixel = 1<<9,
+	/* 19:12 is palette dma delay */
+
+	/* lcsr */
+	CtlrReady = 1<<0,
+
+	/* lccr3 */
+	VsyncLow = 1<<20,
+	HsyncLow = 1<<21,
+	PixelClockLow = 1<<22,
+	OELow = 1<<23,
+};
+
+typedef struct {
+	Vdisplay;
+	LCDparam;
+	ushort*	palette;
+	uchar*	upper;
+	uchar*	lower;
+} LCDdisplay;
+
+static LCDdisplay	*vd;	// current active display
+
+void
+lcd_setcolor(ulong p, ulong r, ulong g, ulong b)
+{
+	if(vd->pbs == 0 && p > 15 ||
+	   vd->pbs == 1 && p > 255 ||
+	   vd->pbs == 2)
+		return;
+	vd->palette[p] = (vd->pbs<<12) |
+			((r>>(32-4))<<8) |
+			((g>>(32-4))<<4) |
+			(b>>(32-4));
+}
+
+static void
+disablelcd(void)
+{
+	LcdReg *lcd = LCDREG;
+	int i;
+
+	/* if LCD enabled, turn off and wait for current frame to end */
+	if(lcd->lccr0 & EnableCtlr) {
+		lcd->lccr0 &= ~EnableCtlr;
+		for(i=0; i < 50 && !(lcd->lcsr & CtlrReady); i++)
+			delay(5);
+	}
+}
+
+static void
+setlcdmode(LCDdisplay *vd)
+{
+	LCDmode *p;
+	int ppf, pclk, clockdiv;
+	ulong v, c;
+	LcdReg *lcd = LCDREG;
+	GpioReg *gpio = GPIOREG;
+
+	p = (LCDmode*)&vd->Vmode;
+	ppf = ((((p->x+p->sol_wait+p->eol_wait) *
+		       (p->mono ? 1 : 3)) >> (3-p->mono)) +
+			p->hsync_wid) *
+		       (p->y/(p->dual+1)+p->vsync_hgt+
+			p->sof_wait+p->eof_wait);
+	pclk = ppf*p->hz;
+	clockdiv = ((m->cpuhz/pclk) >> 1)-2;
+	DPRINT(" oclockdiv=%d\n", clockdiv);
+clockdiv=0x10;
+	disablelcd();
+	lcd->lccr0 = 0;	/* reset it */
+
+	DPRINT("  pclk=%d clockdiv=%d\n", pclk, clockdiv);
+	lcd->lccr3 =  (clockdiv << 0) |
+		(p->acbias_lines << 8) |
+		(p->lines_per_int << 16) |
+		VsyncLow | HsyncLow;	/* vsync active low, hsync active low */
+	lcd->lccr2 =  (((p->y/(p->dual+1))-1) << 0) |
+		(p->vsync_hgt << 10) |
+		(p->eof_wait << 16) |
+		(p->sof_wait << 24);
+	lcd->lccr1 =  ((p->x-16) << 0) |
+		(p->hsync_wid << 10) |
+		(p->eol_wait << 16) |
+		(p->sol_wait << 24);
+
+	// enable LCD controller, CODEC, and lower 4/8 data bits (for tft/dual)
+	v = p->obits < 12? 0: p->obits < 16? 0x3c: 0x3fc;
+	c = p->obits == 12? 0x3c0: 0;
+	gpio->gafr |= v;
+	gpio->gpdr |= v | c;
+	gpio->gpcr = c;
+
+	lcd->dbar1 = PADDR(vd->palette);
+	if(vd->dual)
+		lcd->dbar2 = PADDR(vd->lower);
+
+	// Enable LCD
+	lcd->lccr0 = EnableCtlr | (p->mono?IsMono:IsColour)
+		| (p->palette_delay << 12)
+		| (p->dual ? DualPanel : SinglePanel)
+		| (p->active? ActivePanel: PassivePanel)
+		| DisableDone | DisableBAU | DisableErr;
+
+	// recalculate actual HZ
+	pclk = (m->cpuhz/(clockdiv+2)) >> 1;
+	p->hz = pclk/ppf;
+
+	archlcdenable(1);
+iprint("lccr0=%8.8lux lccr1=%8.8lux lccr2=%8.8lux lccr3=%8.8lux\n", lcd->lccr0, lcd->lccr1, lcd->lccr2, lcd->lccr3);
+}
+static LCDdisplay main_display;	/* TO DO: limits us to a single display */
+
+Vdisplay*
+lcd_init(LCDmode *p)
+{
+	int palsize;
+	int fbsize;
+
+	vd = &main_display;
+	vd->Vmode = *p;
+	vd->LCDparam = *p;
+	DPRINT("%dx%dx%d: hz=%d\n", vd->x, vd->y, vd->depth, vd->hz); /* */
+
+	palsize = vd->pbs==1? 256 : 16;
+	fbsize = palsize*2+(((vd->x*vd->y) * vd->depth) >> 3);
+	if((vd->palette = xspanalloc(fbsize+CACHELINESZ+512, CACHELINESZ, 0)) == nil)	/* at least 16-byte alignment */
+		panic("no vidmem, no party...");
+	vd->palette[0] = (vd->pbs<<12);
+	vd->palette = minicached(vd->palette);
+	vd->upper = (uchar*)(vd->palette + palsize);
+	vd->bwid = (vd->x << vd->pbs) >> 1;
+	vd->lower = vd->upper+((vd->bwid*vd->y) >> 1);
+	vd->fb = vd->upper;
+	DPRINT("  fbsize=%d p=%p u=%p l=%p\n", fbsize, vd->palette, vd->upper, vd->lower); /* */
+
+	setlcdmode(vd);
+	return vd;
+}
+
+void
+lcd_flush(void)
+{
+	if(conf.useminicache)
+		minidcflush();
+	else
+		dcflushall();	/* need more precise addresses */
+}
+
+void
+blankscreen(int blank)
+{
+	if (blank) {
+		disablelcd();
+		archlcdenable(0);
+	} else {
+		archlcdenable(1);
+		setlcdmode(&main_display);
+	}
+}
--- /dev/null
+++ b/os/ipaq1110/main.c
@@ -1,0 +1,255 @@
+#include "u.h"
+#include "../port/lib.h"
+#include "mem.h"
+#include "dat.h"
+#include "fns.h"
+#include "../port/error.h"
+#include "io.h"
+#include "version.h"
+
+Mach *m = (Mach*)MACHADDR;
+Proc *up = 0;
+Vectorpage	*page0 = (Vectorpage*)KZERO;	/* doubly-mapped to AIVECADDR */
+Conf conf;
+
+extern ulong kerndate;
+extern int cflag;
+extern int main_pool_pcnt;
+extern int heap_pool_pcnt;
+extern int image_pool_pcnt;
+ulong cpuidlecount;
+
+static void
+poolsizeinit(void)
+{
+	ulong nb;
+
+	nb = conf.npage*BY2PG;
+	poolsize(mainmem, (nb*main_pool_pcnt)/100, 0);
+	poolsize(heapmem, (nb*heap_pool_pcnt)/100, 0);
+	poolsize(imagmem, (nb*image_pool_pcnt)/100, 1);
+}
+
+void
+main(void)
+{
+	memset(edata, 0, end-edata);		/* clear the BSS */
+	memset(m, 0, sizeof(Mach));	/* clear the mach struct */
+	conf.nmach = 1;
+	archreset();
+	dmareset();
+	quotefmtinstall();
+	confinit();
+	xinit();
+	mmuinit();
+	poolinit();
+	poolsizeinit();
+	trapinit();
+	clockinit(); 
+	printinit();
+	screeninit();
+	procinit();
+	links();
+	chandevreset();
+
+	eve = strdup("inferno");
+
+	archconsole();
+	kbdinit();
+
+	print("%ld MHz id %8.8lux\n", (m->cpuhz+500000)/1000000, getcpuid());
+	print("\nInferno %s\n", VERSION);
+	print("Vita Nuova\n");
+	print("conf %s (%lud) jit %d\n\n",conffile, kerndate, cflag);
+
+	userinit();
+	schedinit();
+}
+
+void
+reboot(void)
+{
+	exit(0);
+}
+
+void
+halt(void)
+{
+	spllo();
+	print("cpu halted\n");
+	for(;;){
+		/* nothing to do */
+	}
+}
+
+void
+confinit(void)
+{
+	ulong base;
+
+	archconfinit();
+
+	base = PGROUND((ulong)end);
+	conf.base0 = base;
+
+	conf.base1 = 0;
+	conf.npage1 = 0;
+
+	conf.npage0 = (conf.topofmem - base)/BY2PG;
+
+	conf.npage = conf.npage0 + conf.npage1;
+	conf.ialloc = (((conf.npage*(main_pool_pcnt))/100)/2)*BY2PG;
+
+	conf.nproc = 100 + ((conf.npage*BY2PG)/MB)*5;
+	conf.nmach = 1;
+
+}
+
+void
+init0(void)
+{
+	Osenv *o;
+	char buf[2*KNAMELEN];
+
+	up->nerrlab = 0;
+
+	spllo();
+
+	if(waserror())
+		panic("init0 %r");
+	/*
+	 * These are o.k. because rootinit is null.
+	 * Then early kproc's will have a root and dot.
+	 */
+	o = up->env;
+	o->pgrp->slash = namec("#/", Atodir, 0, 0);
+	cnameclose(o->pgrp->slash->name);
+	o->pgrp->slash->name = newcname("/");
+	o->pgrp->dot = cclone(o->pgrp->slash);
+
+	chandevinit();
+
+	if(!waserror()){
+		ksetenv("cputype", "arm", 0);
+		snprint(buf, sizeof(buf), "arm %s", conffile);
+		ksetenv("terminal", buf, 0);
+		poperror();
+	}
+
+	poperror();
+
+	disinit("/osinit.dis");
+}
+
+void
+userinit(void)
+{
+	Proc *p;
+	Osenv *o;
+
+	p = newproc();
+	o = p->env;
+
+	o->fgrp = newfgrp(nil);
+	o->pgrp = newpgrp();
+	o->egrp = newegrp();
+	kstrdup(&o->user, eve);
+
+	strcpy(p->text, "interp");
+
+	p->fpstate = FPINIT;
+
+	/*
+	 * Kernel Stack
+	 *
+	 * N.B. The -12 for the stack pointer is important.
+	 *	4 bytes for gotolabel's return PC
+	 */
+	p->sched.pc = (ulong)init0;
+	p->sched.sp = (ulong)p->kstack+KSTACK-8;
+
+	ready(p);
+}
+
+void
+exit(int inpanic)
+{
+	up = 0;
+
+	/* Shutdown running devices */
+	chandevshutdown();
+
+	if(inpanic && 0){
+		print("Hit the reset button\n");
+		for(;;)
+			clockpoll();
+	}
+	archreboot();
+}
+
+static void
+linkproc(void)
+{
+	spllo();
+	if (waserror())
+		print("error() underflow: %r\n");
+	else
+		(*up->kpfun)(up->arg);
+	pexit("end proc", 1);
+}
+
+void
+kprocchild(Proc *p, void (*func)(void*), void *arg)
+{
+	p->sched.pc = (ulong)linkproc;
+	p->sched.sp = (ulong)p->kstack+KSTACK-8;
+
+	p->kpfun = func;
+	p->arg = arg;
+}
+
+void
+idlehands(void)
+{
+	cpuidlecount++;
+	INTRREG->iccr = 1;	/* only unmasked interrupts will stop idle mode */
+	idle();
+}
+
+/* stubs */
+void
+setfsr(ulong)
+{
+}
+
+ulong
+getfsr()
+{
+	return 0;
+}
+
+void
+setfcr(ulong)
+{
+}
+
+ulong
+getfcr()
+{
+	return 0;
+}
+
+void
+fpinit(void)
+{
+}
+
+void
+FPsave(void*)
+{
+}
+
+void
+FPrestore(void*)
+{
+}
--- /dev/null
+++ b/os/ipaq1110/mem.h
@@ -1,0 +1,200 @@
+/*
+ * Memory and machine-specific definitions.  Used in C and assembler.
+ */
+
+/*
+ * Sizes
+ */
+#define	BI2BY		8			/* bits per byte */
+#define BI2WD		32			/* bits per word */
+#define	BY2WD		4			/* bytes per word */
+#define	BY2V		8			/* bytes per double word */
+#define	BY2PG		4096			/* bytes per page */
+#define	WD2PG		(BY2PG/BY2WD)		/* words per page */
+#define	PGSHIFT		12			/* log(BY2PG) */
+#define ROUND(s, sz)	(((s)+(sz-1))&~(sz-1))
+#define PGROUND(s)	ROUND(s, BY2PG)
+#define BIT(n)		(1<<n)
+#define BITS(a,b)	((1<<(b+1))-(1<<a))
+
+#define	MAXMACH		1			/* max # cpus system can run */
+
+/*
+ * Time
+ */
+#define	HZ		(100)			/* clock frequency */
+#define	MS2HZ		(1000/HZ)		/* millisec per clock tick */
+#define	TK2SEC(t)	((t)/HZ)		/* ticks to seconds */
+#define	MS2TK(t)	((t)/MS2HZ)		/* milliseconds to ticks */
+
+/*
+ * More accurate time
+ */
+#define CLOCKFREQ	3686400
+#define MS2TMR(t)	((ulong)(((uvlong)(t)*CLOCKFREQ)/1000))
+#define US2TMR(t)	((ulong)(((uvlong)(t)*CLOCKFREQ)/1000000))
+
+/*
+ *  Address spaces
+ *	nearly everything maps 1-1 with physical addresses
+ *	0 to 1Mb is not mapped
+ *	cache strategy varies as needed (see mmu.c)
+ */
+
+#define KZERO		0xC0000000
+#define MACHADDR	(KZERO+0x00001000)
+#define KTTB		(KZERO+0x00004000)
+#define KTZERO	(KZERO+0x00008010)
+#define KSTACK	8192			/* Size of kernel stack */
+#define FLASHMEM	0x50000000	/* map flash at phys 0 to otherwise unused virtual space */
+#define FLUSHMEM	0xE0000000	/* internally decoded zero memory (for cache flushing) */
+#define DCFADDR	FLUSHMEM	/* cached and buffered for cache writeback */
+#define MCFADDR	(FLUSHMEM+(1<<20))	/* cached and unbuffered for minicache writeback */
+#define UCDRAMZERO	0xC8000000	/* base of memory doubly-mapped as uncached */
+#define AIVECADDR	0xFFFF0000	/* alternative interrupt vector address (other is 0) */
+
+/*
+ * Physical addresses
+ */
+#define PHYSFLASH0	0x00000000	/* flash (chip select 0) */
+#define PHYSCS1		0x08000000	/* static chip select 1 */
+#define PHYSCS2		0x10000000	/* static chip select 2 */
+#define PHYSCS3		0x18000000	/* static chip select 3 */
+#define PHYSPCMCIA0	0x20000000	/* PCMCIA socket 0 space */
+#define PHYSPCMCIA1	0x30000000	/* PCMCIA socket 1 space */
+#define PCMCIASIZE		0x10000000	/* they're both huge */
+#define PHYSCS4		0x40000000	/* static chip select 4 */
+#define PHYSCS5		0x48000000	/* static chip select 5 */
+#define PHYSSERIAL(n)	(0x80000000+0x10000*(n))	/* serial devices */
+#define PHYSUSB		0x80000000
+#define PHYSGPCLK		0x80020060
+#define PHYSMCP		0x80060000
+#define PHYSSSP		0x80070060
+#define PHYSOSTMR		0x90000000	/* timers */
+#define PHYSRTC		0x90010000	/* real time clock */
+#define PHYSPOWER		0x90020000	/* power management registers */
+#define PHYSRESET		0x90030000	/* reset controller */
+#define PHYSGPIO		0x90040000
+#define PHYSINTR		0x90050000	/* interrupt controller */
+#define PHYSPPC		0x90060000	/* peripheral pin controller */
+#define PHYSMEMCFG	0xA0000000	/* memory configuration */
+#define PHYSDMA		0xB0000000	/* DMA controller */
+#define PHYSLCD		0xB0100000	/* LCD controller */
+#define PHYSMEM0		0xC0000000
+#define PHYSFLUSH0	0xE0000000	/* internally decoded, for cache flushing */
+
+/*
+ * Memory Interface Control Registers
+ */
+#define 	MDCNFG	(PHYSMEMCFG)	/* memory controller configuration */
+#define	MDCAS0	(PHYSMEMCFG+4)
+#define	MDCAS1	(PHYSMEMCFG+8)
+#define	MDCAS2	(PHYSMEMCFG+0xC)
+#define	MSC0	(PHYSMEMCFG+0x10)
+#define	MSC1	(PHYSMEMCFG+0x14)
+#define	MSC2	(PHYSMEMCFG+0x2C)	/* SA1110, but not SA1100 */
+
+#define	MSCx(RRR, RDN, RDF, RBW, RT)	((((RRR)&0x7)<<13)|(((RDN)&0x1F)<<8)|(((RDF)&0x1F)<<3)|(((RBW)&1)<<2)|((RT)&3))
+
+#define	CACHELINELOG	5
+#define	CACHELINESZ	(1<<CACHELINELOG)
+
+/*
+ * PSR
+ */
+#define PsrMusr		0x10 	/* mode */
+#define PsrMfiq		0x11 
+#define PsrMirq		0x12
+#define PsrMsvc		0x13
+#define PsrMabt		0x17
+#define PsrMund		0x1B
+#define PsrMsys		0x1F
+#define PsrMask		0x1F
+
+#define PsrDfiq		0x00000040	/* disable FIQ interrupts */
+#define PsrDirq		0x00000080	/* disable IRQ interrupts */
+
+#define PsrV		0x10000000	/* overflow */
+#define PsrC		0x20000000	/* carry/borrow/extend */
+#define PsrZ		0x40000000	/* zero */
+#define PsrN		0x80000000	/* negative/less than */
+
+/*
+ * Internal MMU coprocessor registers
+ */
+#define CpCPUID	0		/* R: */
+#define CpControl	1		/* R/W: */
+#define CpTTB		2		/* R/W: translation table base */
+#define CpDAC		3		/* R/W: domain access control */
+#define CpFSR		5		/* R/W: fault status */
+#define CpFAR		6		/* R/W: fault address */
+#define CpCacheCtl	7		/* W: */
+#define CpTLBops	8		/* W: TLB operations */
+#define CpRBops	9		/* W: Read Buffer operations */
+#define CpPID		13		/* R/W: Process ID Virtual Mapping */
+#define CpDebug	14		/* R/W: debug registers */
+#define CpTest		15		/* W: Test, Clock and Idle Control */
+
+/*
+ * Coprocessors
+ */
+#define CpMMU		15
+#define CpPWR		15
+
+/*
+ * CpControl bits
+ */
+#define CpCmmu	0x00000001	/* M: MMU enable */
+#define CpCalign	0x00000002	/* A: alignment fault enable */
+#define CpCDcache	0x00000004	/* C: instruction/data cache on */
+#define CpCwb		0x00000008	/* W: write buffer turned on */
+#define CpCi32		0x00000010	/* P: 32-bit programme space */
+#define CpCd32	0x00000020	/* D: 32-bit data space */
+#define CpCbe		0x00000080	/* B: big-endian operation */
+#define CpCsystem	0x00000100	/* S: system permission */
+#define CpCrom	0x00000200	/* R: ROM permission */
+#define CpCIcache	0x00001000	/* I: Instruction Cache on */
+#define CpCaltivec	0x00002000	/* X: alternative interrupt vectors */
+
+/*
+ * MMU
+ */
+/*
+ * Small pages:
+ *	L1: 12-bit index -> 4096 descriptors -> 16Kb
+ *	L2:  8-bit index ->  256 descriptors ->  1Kb
+ * Each L2 descriptor has access permissions for 4 1Kb sub-pages.
+ *
+ *	TTB + L1Tx gives address of L1 descriptor
+ *	L1 descriptor gives PTBA
+ *	PTBA + L2Tx gives address of L2 descriptor
+ *	L2 descriptor gives PBA
+ */
+#define MmuSection	(1<<20)
+#define MmuLargePage	(1<<16)
+#define MmuSmallPage	(1<<12)
+#define MmuTTB(pa)	((pa) & ~0x3FFF)	/* translation table base */
+#define MmuL1x(pa)	(((pa)>>20) & 0xFFF)	/* L1 table index */
+#define MmuPTBA(pa)	((pa) & ~0x3FF)		/* page table base address */
+#define MmuL2x(pa)	(((pa)>>12) & 0xFF)	/* L2 table index */
+#define MmuPBA(pa)	((pa) & ~0xFFF)		/* page base address */
+#define MmuSBA(pa)	((pa) & ~0xFFFFF)	/* section base address */
+
+#define MmuL1type	0x03
+#define MmuL1page	0x01			/* descriptor is for L2 pages */
+#define MmuL1section	0x02			/* descriptor is for section */
+
+#define MmuL2invalid	0x000
+#define MmuL2large	0x001			/* large */
+#define MmuL2small	0x002			/* small */
+#define MmuWB		0x004			/* data goes through write buffer */
+#define MmuIDC		0x008			/* data placed in cache */
+
+#define MmuDAC(d)	(((d) & 0xF)<<5)	/* L1 domain */
+#define MmuAP(i, v)	((v)<<(((i)*2)+4))	/* access permissions */
+#define MmuL1AP(v)	MmuAP(3, (v))
+#define MmuL2AP(v)	MmuAP(3, (v))|MmuAP(2, (v))|MmuAP(1, (v))|MmuAP(0, (v))
+#define MmuAPsro	0			/* supervisor ro if S|R */
+#define MmuAPsrw	1			/* supervisor rw */
+#define MmuAPuro	2			/* supervisor rw + user ro */
+#define MmuAPurw	3			/* supervisor rw + user rw */
--- /dev/null
+++ b/os/ipaq1110/mkfile
@@ -1,0 +1,103 @@
+<../../mkconfig
+TKSTYLE=std
+
+#Configurable parameters
+
+CONF=ipaq				#default configuration
+CONFLIST=ipaq
+
+SYSTARG=$OSTARG
+OBJTYPE=arm
+INSTALLDIR=$ROOT/Inferno/$OBJTYPE/bin	#path of directory where kernel is installed
+#end configurable parameters
+
+<$ROOT/mkfiles/mkfile-$SYSTARG-$OBJTYPE	#set vars based on target system
+
+<| $SHELLNAME ../port/mkdevlist $CONF	#sets $IP, $DEVS, $ETHERS, $VGAS, $PORT, $MISC, $LIBS, $OTHERS
+
+KTZERO=0xC0008010
+
+OBJ=\
+	l.$O\
+	clock.$O\
+	dma.$O\
+	fpi.$O\
+	fpiarm.$O\
+	fpimem.$O\
+	defont.$O\
+	main.$O\
+	mmu.$O\
+	trap.$O\
+	$CONF.root.$O\
+	$IP\
+	$DEVS\
+	$ETHERS\
+	$LINKS\
+	$PORT\
+	$MISC\
+	$OTHERS\
+
+LIBNAMES=${LIBS:%=lib%.a}
+LIBDIRS=$LIBS
+
+HFILES=\
+	mem.h\
+	dat.h\
+	fns.h\
+	io.h\
+	../sa1110/sa1110io.h\
+	../sa1110/fpi.h\
+
+CFLAGS=-wFV -I$ROOT/Inferno/$OBJTYPE/include -I$ROOT/include -I$ROOT/libinterp -I../sa1110
+KERNDATE=`{$NDATE}
+
+default:V: i$CONF.gz i$CONF.p9 k.gz
+
+install:V: $INSTALLDIR/i$CONF $INSTALLDIR/i$CONF.gz $INSTALLDIR/i$CONF.p9.gz $INSTALLDIR/i$CONF.raw
+
+i$CONF: $OBJ $CONF.c $CONF.root.h $LIBNAMES i$CONF.p9
+	$CC $CFLAGS '-DKERNDATE='$KERNDATE $CONF.c
+	$LD -s -o $target -H5 -T0xC0008010 -R4 -l $OBJ $CONF.$O $LIBFILES
+
+i$CONF.p9: $OBJ $CONF.c $CONF.root.h $LIBNAMES
+	$CC $CFLAGS '-DKERNDATE='$KERNDATE $CONF.c
+	$LD -o $target -T0xC0008010 -R4 -l $OBJ $CONF.$O $LIBFILES
+
+i$CONF.gz: i$CONF
+	rm -f i$CONF.gz
+	gzip -9 <i$CONF >i$CONF.gz
+
+<../port/portmkfile
+CLEANEXTRA=k.gz
+
+../init/$INIT.dis:	../init/$INIT.b
+		cd ../init; mk $INIT.dis
+
+clock.$O:	$ROOT/Inferno/$OBJTYPE/include/ureg.h
+devether.$O:	$ROOT/Inferno/$OBJTYPE/include/ureg.h
+main.$O:	$ROOT/Inferno/$OBJTYPE/include/ureg.h
+trap.$O:	$ROOT/Inferno/$OBJTYPE/include/ureg.h
+
+devether.$O $ETHERS:	../sa1110/etherif.h ../port/netif.h
+$IP devip.$O:		../ip/ip.h
+io.h:N:	../sa1110/sa1110io.h
+
+%.$O:		../sa1110/%.c
+		$CC $CFLAGS -I. ../sa1110/$stem.c
+
+%.$O:		../sa1110/%.s
+	$AS -I. -I../sa1110 ../sa1110/$stem.s
+
+dummy:V:
+
+k.gz:	i$CONF.gz
+	cat inflate i$CONF.gz >k.gz
+	echo burble burble >>k.gz
+
+devaudio.$O:	devaudio.c
+	$CC $CFLAGS devaudio.c
+
+arch$CONF.$O:	../sa1110/etherif.h
+
+devuart.$O:	../sa1110/devuart.c
+	$CC $CFLAGS ../sa1110/devuart.c
--- /dev/null
+++ b/os/ipaq1110/screen.c
@@ -1,0 +1,917 @@
+#include "u.h"
+#include "../port/lib.h"
+#include "mem.h"
+#include "dat.h"
+#include "fns.h"
+#include "../port/error.h"
+#include "io.h"
+
+#include <draw.h>
+#include <memdraw.h>
+#include <cursor.h>
+
+#include "screen.h"
+
+enum {
+	Backgnd = 0xFF,	/* white */
+	Foregnd =	0x00,	/* black */
+};
+
+#define	DPRINT	if(1)iprint
+
+static Memdata xgdata;
+static Memimage xgscreen =
+{
+	{0, 0, 0, 0},	/* r */
+	{0, 0, 0, 0},	/* clipr */
+	8,			/* depth */
+	1,			/* nchan */
+	CMAP8,		/* chan */
+	nil,			/* cmap */
+	&xgdata,		/* data */
+	0,			/* zero */
+	0,			/* width */
+	nil,			/* layer */
+	0,			/* flags */
+};
+
+Memimage *gscreen;
+Memimage *conscol;
+Memimage *back;
+
+Memsubfont *memdefont;
+
+static Point curpos;
+static Rectangle window;
+
+typedef struct SWcursor SWcursor;
+
+static Vdisplay *vd;
+static SWcursor *swc = nil;
+
+SWcursor* swcurs_create(ulong *, int, int, Rectangle, int);
+void swcurs_destroy(SWcursor*);
+void swcurs_enable(SWcursor*);
+void swcurs_disable(SWcursor*);
+void swcurs_hide(SWcursor*);
+void swcurs_unhide(SWcursor*);
+void swcurs_load(SWcursor*, Cursor*);
+void swcursupdate(int, int, int, int);
+
+static char printbuf[1024];
+static int printbufpos = 0;
+static void	lcdscreenputs(char*, int);
+static void screenpbuf(char*, int);
+void (*screenputs)(char*, int) = screenpbuf;
+
+static Cursor arrow = {
+	{ -1, -1 },
+	{ 0xFF, 0xFF, 0x80, 0x01, 0x80, 0x02, 0x80, 0x0C,
+	  0x80, 0x10, 0x80, 0x10, 0x80, 0x08, 0x80, 0x04,
+	  0x80, 0x02, 0x80, 0x01, 0x80, 0x02, 0x8C, 0x04,
+	  0x92, 0x08, 0x91, 0x10, 0xA0, 0xA0, 0xC0, 0x40,
+	},
+	{ 0x00, 0x00, 0x7F, 0xFE, 0x7F, 0xFC, 0x7F, 0xF0,
+	  0x7F, 0xE0, 0x7F, 0xE0, 0x7F, 0xF0, 0x7F, 0xF8,
+	  0x7F, 0xFC, 0x7F, 0xFE, 0x7F, 0xFC, 0x73, 0xF8,
+	  0x61, 0xF0, 0x60, 0xE0, 0x40, 0x40, 0x00, 0x00,
+	},
+};
+
+static	ushort	palette16[256];
+static	void	(*flushpixels)(Rectangle, ulong*, int, ulong*, int);
+static	void	flush8to4(Rectangle, ulong*, int, ulong*, int);
+static	void	flush8to4r(Rectangle, ulong*, int, ulong*, int);
+static	void	flush8to16(Rectangle, ulong*, int, ulong*, int);
+static	void	flush8to16r(Rectangle, ulong*, int, ulong*, int);
+
+/*
+lccr0=000000b9 lccr1=0b100930 lccr2=0a0108ef lccr3=00300010
+	---
+vd->wid=320 bwid=640 gscreen->width=60 fb=d0b7cb80 data=d0ba25c0
+ */
+
+int
+setcolor(ulong p, ulong r, ulong g, ulong b)
+{
+	if(vd->depth >= 8)
+		p &= 0xff;
+	else
+		p &= 0xf;
+	vd->colormap[p][0] = r;
+	vd->colormap[p][1] = g;
+	vd->colormap[p][2] = b;
+	palette16[p] = ((r>>(32-4))<<12)|((g>>(32-4))<<7)|((b>>(32-4))<<1);
+	lcd_setcolor(p, r, g, b);
+	return ~0;
+}
+
+void
+getcolor(ulong p, ulong *pr, ulong *pg, ulong *pb)
+{
+	if(vd->depth >= 8)
+		p = (p&0xff)^0xff;
+	else
+		p = (p&0xf)^0xf;
+	*pr = vd->colormap[p][0];
+	*pg = vd->colormap[p][1];
+	*pb = vd->colormap[p][2];
+}
+
+void
+graphicscmap(int invert)
+{
+	int num, den, i, j;
+	int r, g, b, cr, cg, cb, v, p;
+
+	for(r=0,i=0;r!=4;r++) for(v=0;v!=4;v++,i+=16){
+		for(g=0,j=v-r;g!=4;g++) for(b=0;b!=4;b++,j++){
+			den=r;
+			if(g>den) den=g;
+			if(b>den) den=b;
+			if(den==0)	/* divide check -- pick grey shades */
+				cr=cg=cb=v*17;
+			else{
+				num=17*(4*den+v);
+				cr=r*num/den;
+				cg=g*num/den;
+				cb=b*num/den;
+			}
+			p = (i+(j&15));
+			if(invert)
+				p ^= 0xFF;
+			if(vd->depth == 4) {
+				if((p&0xf) != (p>>4))
+					continue;
+				p &= 0xf;
+			}
+			setcolor(p,
+				cr*0x01010101,
+				cg*0x01010101,
+				cb*0x01010101);
+		}
+	}
+	lcd_flush();
+}
+
+static uchar lum[256]={
+  0,   7,  15,  23,  39,  47,  55,  63,  79,  87,  95, 103, 119, 127, 135, 143,
+154,  17,   9,  17,  25,  49,  59,  62,  68,  89,  98, 107, 111, 129, 138, 146,
+157, 166,  34,  11,  19,  27,  59,  71,  69,  73,  99, 109, 119, 119, 139, 148,
+159, 169, 178,  51,  13,  21,  29,  69,  83,  75,  78, 109, 120, 131, 128, 149,
+ 28,  35,  43,  60,  68,  75,  83, 100, 107, 115, 123, 140, 147, 155, 163,  20,
+ 25,  35,  40,  47,  75,  85,  84,  89, 112, 121, 129, 133, 151, 159, 168, 176,
+190,  30,  42,  44,  50,  90, 102,  94,  97, 125, 134, 144, 143, 163, 172, 181,
+194, 204,  35,  49,  49,  54, 105, 119, 103, 104, 137, 148, 158, 154, 175, 184,
+ 56,  63,  80,  88,  96, 103, 120, 128, 136, 143, 160, 168, 175, 183,  40,  48,
+ 54,  63,  69,  90,  99, 107, 111, 135, 144, 153, 155, 173, 182, 190, 198,  45,
+ 50,  60,  70,  74, 100, 110, 120, 120, 150, 160, 170, 167, 186, 195, 204, 214,
+229,  55,  66,  77,  79, 110, 121, 131, 129, 165, 176, 187, 179, 200, 210, 219,
+ 84, 100, 108, 116, 124, 140, 148, 156, 164, 180, 188, 196, 204,  60,  68,  76,
+ 82,  91, 108, 117, 125, 134, 152, 160, 169, 177, 195, 204, 212, 221,  66,  74,
+ 80,  89,  98, 117, 126, 135, 144, 163, 172, 181, 191, 210, 219, 228, 238,  71,
+ 76,  85,  95, 105, 126, 135, 145, 155, 176, 185, 195, 205, 225, 235, 245, 255,
+};
+
+void flushmemscreen(Rectangle r);
+
+void
+screenclear(void)
+{
+	memimagedraw(gscreen, gscreen->r, memwhite, ZP, memopaque, ZP, SoverD);
+	curpos = window.min;
+	flushmemscreen(gscreen->r);
+}
+
+static void
+setscreen(LCDmode *mode)
+{
+	int h;
+
+	if(swc != nil)
+		swcurs_destroy(swc);
+
+	vd = lcd_init(mode);
+	if(vd == nil)
+		panic("can't initialise LCD");
+
+	if(lum[255] == 255) {
+		int i;
+		for(i=0; i<256; i++)
+			lum[i] >>= 4;	/* could support depths other than 4 */
+	}
+
+	gscreen = &xgscreen;
+	xgdata.ref = 1;
+
+	if(conf.portrait == 0)
+		gscreen->r = Rect(0, 0, vd->x, vd->y);
+	else
+		gscreen->r = Rect(0, 0, vd->y, vd->x);
+	gscreen->clipr = gscreen->r;
+	gscreen->depth = 8;
+	gscreen->width = wordsperline(gscreen->r, gscreen->depth);
+	if(vd->depth == 4 || vd->depth == 16 || conf.portrait) {	/* use 8 to 4 bit fakeout for speed */
+		if((xgdata.bdata = xspanalloc(gscreen->width*gscreen->r.max.y*BY2WD+CACHELINESZ, CACHELINESZ, 0)) == nil)
+			panic("can't alloc vidmem");
+		xgdata.bdata = minicached(xgdata.bdata);
+		if(conf.portrait == 0)
+			flushpixels = vd->depth==4? flush8to4: flush8to16;
+		else
+			flushpixels = vd->depth==4? flush8to4r: flush8to16r;
+	} else{
+		xgdata.bdata = (uchar*)vd->fb;
+		flushpixels = nil;
+	}
+	memimageinit();
+	memdefont = getmemdefont();
+
+	memsetchan(gscreen, CMAP8);	/* TO DO: could now use RGB16 */
+	back = memwhite;
+	conscol = memblack;
+	memimagedraw(gscreen, gscreen->r, memwhite, ZP, memopaque, ZP, SoverD);
+
+	DPRINT("vd->wid=%d bwid=%d gscreen->width=%ld fb=%p data=%p\n",
+		vd->x, vd->bwid, gscreen->width, vd->fb, xgdata.bdata);
+	graphicscmap(0);
+	h = memdefont->height;
+	window = insetrect(gscreen->r, 4);
+	window.max.y = window.min.y+(Dy(window)/h)*h;
+	screenclear();
+
+//	swc = swcurs_create(gscreendata.data, gscreen.width, gscreen.ldepth, gscreen.r, 1);
+
+	drawcursor(nil);
+}
+
+void
+screeninit(void)
+{
+	LCDmode lcd;
+
+	memset(&lcd, 0, sizeof(lcd));
+	if(archlcdmode(&lcd) < 0)
+		return;
+	setscreen(&lcd);
+	screenputs = lcdscreenputs;
+	if(printbufpos)
+		screenputs("", 0);
+	blanktime = 3;	/* minutes */
+}
+
+uchar*
+attachscreen(Rectangle *r, ulong *chan, int* d, int *width, int *softscreen)
+{
+	*r = gscreen->r;
+	*d = gscreen->depth;
+	*chan = gscreen->chan;
+	*width = gscreen->width;
+	*softscreen = (gscreen->data->bdata != (uchar*)vd->fb);
+
+	return (uchar*)gscreen->data->bdata;
+}
+
+void
+detachscreen(void)
+{
+}
+
+static void
+flush8to4(Rectangle r, ulong *s, int sw, ulong *d, int dw)
+{
+	int i, h, w;
+
+/*
+	print("1) s=%ux sw=%d d=%ux dw=%d r=(%d,%d)(%d,%d)\n",
+		s, sw, d, dw, r.min.x, r.min.y, r.max.x, r.max.y);
+*/
+
+	r.min.x &= ~7;
+	r.max.x = (r.max.x + 7) & ~7;
+	s += (r.min.y*sw)+(r.min.x>>2);
+	d += (r.min.y*dw)+(r.min.x>>3);
+	h = Dy(r);
+	w = Dx(r) >> 3;
+	sw -= w*2;
+	dw -= w;
+
+	while(h--) {
+		for(i=w; i; i--) {
+			ulong v1 = *s++;
+			ulong v2 = *s++;
+			*d++ = 	 (lum[v2>>24]<<28)
+				|(lum[(v2>>16)&0xff]<<24)
+				|(lum[(v2>>8)&0xff]<<20)
+				|(lum[v2&0xff]<<16)
+				|(lum[v1>>24]<<12)
+				|(lum[(v1>>16)&0xff]<<8)
+				|(lum[(v1>>8)&0xff]<<4)
+				|(lum[v1&0xff])
+				;
+		}
+		s += sw;
+		d += dw;
+	}
+}
+
+static void
+flush8to16(Rectangle r, ulong *s, int sw, ulong *d, int dw)
+{
+	int i, h, w;
+	ushort *p;
+
+	if(0)
+		iprint("1) s=%p sw=%d d=%p dw=%d r=[%d,%d, %d,%d]\n",
+		s, sw, d, dw, r.min.x, r.min.y, r.max.x, r.max.y);
+
+	r.min.x &= ~3;
+	r.max.x = (r.max.x + 3) & ~3;	/* nearest ulong */
+	s += (r.min.y*sw)+(r.min.x>>2);
+	d += (r.min.y*dw)+(r.min.x>>1);
+	h = Dy(r);
+	w = Dx(r) >> 2;	/* also ulong */
+	sw -= w;
+	dw -= w*2;
+	if(0)
+		iprint("h=%d w=%d sw=%d dw=%d\n", h, w, sw, dw);
+
+	p = palette16;
+	while(--h >= 0){
+		for(i=w; --i>=0;){
+			ulong v = *s++;
+			*d++ = (p[(v>>8)&0xFF]<<16) | p[v & 0xFF];
+			*d++ = (p[v>>24]<<16) | p[(v>>16)&0xFF];
+		}
+		s += sw;
+		d += dw;
+	}
+}
+
+static void
+flush8to4r(Rectangle r, ulong *s, int sw, ulong *d, int dw)
+{
+	flush8to4(r, s, sw, d, dw);	/* rotation not implemented */
+}
+
+static void
+flush8to16r(Rectangle r, ulong *s, int sw, ulong *d, int dw)
+{
+	int x, y, w, dws;
+	ushort *p;
+	ushort *ds;
+
+	if(0)
+		iprint("1) s=%p sw=%d d=%p dw=%d r=[%d,%d, %d,%d]\n",
+		s, sw, d, dw, r.min.x, r.min.y, r.max.x, r.max.y);
+
+	r.min.y &= ~3;
+	r.max.y = (r.max.y+3) & ~3;
+	r.min.x &= ~7;
+	r.max.x = (r.max.x + 7) & ~7;
+	s += (r.min.y*sw)+(r.min.x>>2);
+//	d += (r.min.y*dw)+(r.min.x>>1);
+	w = Dx(r) >> 2;	/* also ulong */
+	sw -= w;
+	dws = dw*2;
+	if(0)
+		iprint("h=%d w=%d sw=%d dw=%d x,y=%d,%d %d\n", Dy(r), w, sw, dw, r.min.x,r.min.y, dws);
+
+	p = palette16;
+	for(y=r.min.y; y<r.max.y; y++){
+		for(x=r.min.x; x<r.max.x; x+=4){
+			ulong v = *s++;
+			ds = (ushort*)(d + x*dw) + (gscreen->r.max.y-(y+1));
+			ds[0] = p[v & 0xFF];
+			ds[dws] = p[(v>>8)&0xFF];
+			ds[dws*2] = p[(v>>16)&0xFF];
+			ds[dws*3] = p[(v>>24)&0xFF];
+		}
+		s += sw;
+	}
+}
+
+void
+flushmemscreen(Rectangle r)
+{
+	if(rectclip(&r, gscreen->r) == 0)
+		return;
+	if(r.min.x >= r.max.x || r.min.y >= r.max.y)
+		return;
+	if(flushpixels != nil)
+		flushpixels(r, (ulong*)gscreen->data->bdata, gscreen->width, (ulong*)vd->fb, vd->bwid >> 2);
+	lcd_flush();
+}
+
+static void
+scroll(void)
+{
+	int o;
+	Point p;
+	Rectangle r;
+
+	o = 4*memdefont->height;
+	r = Rpt(window.min, Pt(window.max.x, window.max.y-o));
+	p = Pt(window.min.x, window.min.y+o);
+	memimagedraw(gscreen, r, gscreen, p, nil, p, SoverD);
+	flushmemscreen(r);
+	r = Rpt(Pt(window.min.x, window.max.y-o), window.max);
+	memimagedraw(gscreen, r, back, ZP, nil, ZP, SoverD);
+	flushmemscreen(r);
+
+	curpos.y -= o;
+}
+
+static void
+clearline(void)
+{
+	Rectangle r;
+	int yloc = curpos.y;
+
+	r = Rpt(Pt(window.min.x, window.min.y + yloc),
+		Pt(window.max.x, window.min.y+yloc+memdefont->height));
+	memimagedraw(gscreen, r, back, ZP, nil, ZP, SoverD);
+}
+
+static void
+screenputc(char *buf)
+{
+	Point p;
+	int h, w, pos;
+	Rectangle r;
+	static int *xp;
+	static int xbuf[256];
+
+	h = memdefont->height;
+	if(xp < xbuf || xp >= &xbuf[sizeof(xbuf)])
+		xp = xbuf;
+
+	switch(buf[0]) {
+	case '\n':
+		if(curpos.y+h >= window.max.y)
+			scroll();
+		curpos.y += h;
+		/* fall through */
+	case '\r':
+		xp = xbuf;
+		curpos.x = window.min.x;
+		break;
+	case '\t':
+		if(curpos.x == window.min.x)
+			clearline();
+		p = memsubfontwidth(memdefont, " ");
+		w = p.x;
+		*xp++ = curpos.x;
+		pos = (curpos.x-window.min.x)/w;
+		pos = 8-(pos%8);
+		r = Rect(curpos.x, curpos.y, curpos.x+pos*w, curpos.y+h);
+		memimagedraw(gscreen, r, back, ZP, nil, ZP, SoverD);
+		flushmemscreen(r);
+		curpos.x += pos*w;
+		break;
+	case '\b':
+		if(xp <= xbuf)
+			break;
+		xp--;
+		r = Rpt(Pt(*xp, curpos.y), Pt(curpos.x, curpos.y + h));
+		memimagedraw(gscreen, r, back, ZP, nil, ZP, SoverD);
+		flushmemscreen(r);
+		curpos.x = *xp;
+		break;
+	case '\0':
+		break;
+	default:
+		p = memsubfontwidth(memdefont, buf);
+		w = p.x;
+
+		if(curpos.x >= window.max.x-w)
+			screenputc("\n");
+
+		if(curpos.x == window.min.x)
+			clearline();
+		if(xp < xbuf+nelem(xbuf))
+			*xp++ = curpos.x;
+		r = Rect(curpos.x, curpos.y, curpos.x+w, curpos.y+h);
+		memimagedraw(gscreen, r, back, ZP, nil, ZP, SoverD);
+		memimagestring(gscreen, curpos, conscol, ZP, memdefont, buf);
+		flushmemscreen(r);
+		curpos.x += w;
+	}
+}
+
+static void
+screenpbuf(char *s, int n)
+{
+	if(printbufpos+n > sizeof(printbuf))
+		n = sizeof(printbuf)-printbufpos;
+	if(n > 0) {
+		memmove(&printbuf[printbufpos], s, n);
+		printbufpos += n;
+	}
+}
+
+static void
+screendoputs(char *s, int n)
+{
+	int i;
+	Rune r;
+	char buf[4];
+
+	while(n > 0) {
+		i = chartorune(&r, s);
+		if(i == 0){
+			s++;
+			--n;
+			continue;
+		}
+		memmove(buf, s, i);
+		buf[i] = 0;
+		n -= i;
+		s += i;
+		screenputc(buf);
+	}
+}
+
+void
+screenflush(void)
+{
+	int j = 0;
+	int k;
+
+	for (k = printbufpos; j < k; k = printbufpos) {
+		screendoputs(printbuf + j, k - j);
+		j = k;
+	}
+	printbufpos = 0;
+}
+
+static void
+lcdscreenputs(char *s, int n)
+{
+	static Proc *me;
+
+	if(!canlock(vd)) {
+		/* don't deadlock trying to print in interrupt */
+		/* don't deadlock trying to print while in print */
+		if(islo() == 0 || up != nil && up == me){
+			/* save it for later... */
+			/* In some cases this allows seeing a panic message
+			  that would be locked out forever */
+			screenpbuf(s, n);
+			return;
+		}
+		lock(vd);
+	}
+
+	me = up;
+	if (printbufpos)
+		screenflush();
+	screendoputs(s, n);
+	if (printbufpos)
+		screenflush();
+	me = nil;
+
+	unlock(vd);
+}
+
+/*
+ *	Software cursor code: done by hand, might be better to use memdraw
+ */
+ 
+typedef struct SWcursor {
+	ulong	*fb;	/* screen frame buffer */
+	Rectangle r;
+	int	d;	/* ldepth of screen */
+	int 	width;	/* width of screen in ulongs */
+	int	x;
+	int	y;
+	int	hotx;
+	int	hoty;
+	uchar	cbwid;	/* cursor byte width */
+	uchar	f;	/* flags */
+	uchar	cwid;
+	uchar	chgt;
+	int	hidecount;
+	uchar	data[CURSWID*CURSHGT];
+	uchar	mask[CURSWID*CURSHGT];
+	uchar	save[CURSWID*CURSHGT];
+} SWcursor;
+
+enum {
+	CUR_ENA = 0x01,		/* cursor is enabled */
+	CUR_DRW = 0x02,		/* cursor is currently drawn */
+	CUR_SWP = 0x10,		/* bit swap */
+};
+
+static Rectangle cursoroffrect;
+static int	cursorisoff;
+
+static void swcursorflush(int, int);
+static void	swcurs_draw_or_undraw(SWcursor *);
+
+static void
+cursorupdate0(void)
+{
+	int inrect, x, y;
+	Point m;
+
+	m = mousexy();
+	x = m.x - swc->hotx;
+	y = m.y - swc->hoty;
+	inrect = (x >= cursoroffrect.min.x && x < cursoroffrect.max.x
+		&& y >= cursoroffrect.min.y && y < cursoroffrect.max.y);
+	if (cursorisoff == inrect)
+		return;
+	cursorisoff = inrect;
+	if (inrect)
+		swcurs_hide(swc);
+	else {
+		swc->hidecount = 0;
+		swcurs_draw_or_undraw(swc);
+	}
+	swcursorflush(m.x, m.y);
+}
+
+void
+cursorupdate(Rectangle r)
+{
+	lock(vd);
+	r.min.x -= 16;
+	r.min.y -= 16;
+	cursoroffrect = r;
+	if (swc != nil)
+		cursorupdate0();
+	unlock(vd);
+}
+
+void
+cursorenable(void)
+{
+	Point m;
+
+	lock(vd);
+	if(swc != nil) {
+		swcurs_enable(swc);
+		m = mousexy();
+		swcursorflush(m.x, m.y);
+	}
+	unlock(vd);
+}
+
+void
+cursordisable(void)
+{
+	Point m;
+
+	lock(vd);
+	if(swc != nil) {
+		swcurs_disable(swc);
+		m = mousexy();
+		swcursorflush(m.x, m.y);
+	}
+	unlock(vd);
+}
+
+void
+swcursupdate(int oldx, int oldy, int x, int y)
+{
+
+	if(!canlock(vd))
+		return;		/* if can't lock, don't wake up stuff */
+
+	if(x < gscreen->r.min.x)
+		x = gscreen->r.min.x;
+	if(x >= gscreen->r.max.x)
+		x = gscreen->r.max.x;
+	if(y < gscreen->r.min.y)
+		y = gscreen->r.min.y;
+	if(y >= gscreen->r.max.y)
+		y = gscreen->r.max.y;
+	if(swc != nil) {
+		swcurs_hide(swc);
+		swc->x = x;
+		swc->y = y;
+		cursorupdate0();
+		swcurs_unhide(swc);
+		swcursorflush(oldx, oldy);
+		swcursorflush(x, y);
+	}
+
+	unlock(vd);
+}
+
+void
+drawcursor(Drawcursor* c)
+{
+	Point p, m;
+	Cursor curs, *cp;
+	int j, i, h, bpl;
+	uchar *bc, *bs, *cclr, *cset;
+
+	if(swc == nil)
+		return;
+
+	/* Set the default system cursor */
+	if(c == nil || c->data == nil){
+		swcurs_disable(swc);
+		return;
+	}
+	else {
+		cp = &curs;
+		p.x = c->hotx;
+		p.y = c->hoty;
+		cp->offset = p;
+		bpl = bytesperline(Rect(c->minx, c->miny, c->maxx, c->maxy), 1);
+
+		h = (c->maxy-c->miny)/2;
+		if(h > 16)
+			h = 16;
+
+		bc = c->data;
+		bs = c->data + h*bpl;
+
+		cclr = cp->clr;
+		cset = cp->set;
+		for(i = 0; i < h; i++) {
+			for(j = 0; j < 2; j++) {
+				cclr[j] = bc[j];
+				cset[j] = bs[j];
+			}
+			bc += bpl;
+			bs += bpl;
+			cclr += 2;
+			cset += 2;
+		}
+	}
+	swcurs_load(swc, cp);
+	m = mousexy();
+	swcursorflush(m.x, m.y);
+	swcurs_enable(swc);
+}
+
+SWcursor*
+swcurs_create(ulong *fb, int width, int ldepth, Rectangle r, int bitswap)
+{
+	SWcursor *swc;
+
+	swc = (SWcursor*)malloc(sizeof(SWcursor));
+	swc->fb = fb;
+	swc->r = r;
+	swc->d = ldepth;
+	swc->width = width;
+	swc->f = bitswap ? CUR_SWP : 0;
+	swc->x = swc->y = 0;
+	swc->hotx = swc->hoty = 0;
+	swc->hidecount = 0;
+	return swc;
+}
+
+void
+swcurs_destroy(SWcursor *swc)
+{
+	swcurs_disable(swc);
+	free(swc);
+}
+
+static void
+swcursorflush(int x, int y)
+{
+	Rectangle r;
+
+	/* XXX a little too paranoid here */
+	r.min.x = x-16;
+	r.min.y = y-16;
+	r.max.x = x+17;
+	r.max.y = y+17;
+	flushmemscreen(r);
+}
+
+static void
+swcurs_draw_or_undraw(SWcursor *swc)
+{
+	uchar *p;
+	uchar *cs;
+	int w, vw;
+	int x1 = swc->r.min.x;
+	int y1 = swc->r.min.y;
+	int x2 = swc->r.max.x;
+	int y2 = swc->r.max.y; 
+	int xp = swc->x - swc->hotx;
+	int yp = swc->y - swc->hoty;
+	int ofs;
+
+	if(((swc->f & CUR_ENA) && (swc->hidecount <= 0))
+			 == ((swc->f & CUR_DRW) != 0))
+		return;
+	w = swc->cbwid*BI2BY/(1 << swc->d);
+	x1 = xp < x1 ? x1 : xp;
+	y1 = yp < y1 ? y1 : yp;
+	x2 = xp+w >= x2 ? x2 : xp+w;
+	y2 = yp+swc->chgt >= y2 ? y2 : yp+swc->chgt;
+	if(x2 <= x1 || y2 <= y1)
+		return;
+	p = (uchar*)(swc->fb + swc->width*y1)
+		+ x1*(1 << swc->d)/BI2BY;
+	y2 -= y1;
+	x2 = (x2-x1)*(1 << swc->d)/BI2BY;
+	vw = swc->width*BY2WD - x2;
+	w = swc->cbwid - x2;
+	ofs = swc->cbwid*(y1-yp)+(x1-xp);
+	cs = swc->save + ofs;
+	if((swc->f ^= CUR_DRW) & CUR_DRW) {
+		uchar *cm = swc->mask + ofs; 
+		uchar *cd = swc->data + ofs;
+		while(y2--) {
+			x1 = x2;
+			while(x1--) {
+				*p = ((*cs++ = *p) & *cm++) ^ *cd++;
+				p++;
+			}
+			cs += w;
+			cm += w;
+			cd += w;
+			p += vw;
+		}
+	} else {
+		while(y2--) {
+			x1 = x2;
+			while(x1--) 
+				*p++ = *cs++;
+			cs += w;
+			p += vw;
+		}
+	}
+}
+
+void
+swcurs_hide(SWcursor *swc)
+{
+	++swc->hidecount;
+	swcurs_draw_or_undraw(swc);
+}
+
+void
+swcurs_unhide(SWcursor *swc)
+{
+	if (--swc->hidecount < 0)
+		swc->hidecount = 0;
+	swcurs_draw_or_undraw(swc);
+}
+
+void
+swcurs_enable(SWcursor *swc)
+{
+	swc->f |= CUR_ENA;
+	swcurs_draw_or_undraw(swc);
+}
+
+void
+swcurs_disable(SWcursor *swc)
+{
+	swc->f &= ~CUR_ENA;
+	swcurs_draw_or_undraw(swc);
+}
+
+void
+swcurs_load(SWcursor *swc, Cursor *c)
+{
+	int i, k;
+	uchar *bc, *bs, *cd, *cm;
+	static uchar bdv[4] = {0,Backgnd,Foregnd,0xff};
+	static uchar bmv[4] = {0xff,0,0,0xff};
+	int bits = 1<<swc->d;
+	uchar mask = (1<<bits)-1;
+	int bswp = (swc->f&CUR_SWP) ? 8-bits : 0;
+
+	bc = c->clr;
+	bs = c->set;
+
+	swcurs_hide(swc);
+	cd = swc->data;
+	cm = swc->mask;
+	swc->hotx = c->offset.x;
+	swc->hoty = c->offset.y;
+	swc->chgt = CURSHGT;
+	swc->cwid = CURSWID;
+	swc->cbwid = CURSWID*(1<<swc->d)/BI2BY;
+	for(i = 0; i < CURSWID/BI2BY*CURSHGT; i++) {
+		uchar bcb = *bc++;
+		uchar bsb = *bs++;
+		for(k=0; k<BI2BY;) {
+			uchar cdv = 0;
+			uchar cmv = 0;
+			int z;
+			for(z=0; z<BI2BY; z += bits) {
+				int n = ((bsb&(0x80))|((bcb&(0x80))<<1))>>7;
+				int s = z^bswp;
+				cdv |= (bdv[n]&mask) << s;
+				cmv |= (bmv[n]&mask) << s;
+				bcb <<= 1;
+				bsb <<= 1;
+				k++;
+			}
+			*cd++ = cdv;
+			*cm++ = cmv;
+		}
+	}
+	swcurs_unhide(swc);
+}
--- /dev/null
+++ b/os/ipaq1110/screen.h
@@ -1,0 +1,64 @@
+typedef struct Cursor Cursor;
+typedef struct LCDmode LCDmode;
+typedef struct LCDparam LCDparam;
+typedef struct Vdisplay Vdisplay;
+typedef struct Vmode Vmode;
+
+#define CURSWID	16
+#define CURSHGT	16
+
+struct Cursor {
+	Point	offset;
+	uchar	clr[CURSWID/BI2BY*CURSHGT];
+	uchar	set[CURSWID/BI2BY*CURSHGT];
+};
+
+struct Vmode {
+	int	x;	/* 0 -> default or any match for all fields */
+	int	y;
+	uchar	depth;
+	uchar	hz;
+};
+
+struct Vdisplay {
+	uchar*	fb;		/* frame buffer */
+	ulong	colormap[256][3];
+	int	bwid;
+	Lock;
+	Vmode; 
+};
+
+struct LCDparam {
+	uchar	pbs;
+	uchar	dual;
+	uchar	mono;
+	uchar	active;
+	uchar	hsync_wid;
+	uchar	sol_wait;
+	uchar	eol_wait;
+	uchar	vsync_hgt;
+	uchar	sof_wait;
+	uchar	eof_wait;
+	uchar	lines_per_int;
+	uchar	palette_delay;
+	uchar	acbias_lines;
+	uchar	obits;
+	uchar	vsynclow;
+	uchar	hsynclow;
+};
+
+struct LCDmode {
+	Vmode;
+	LCDparam;
+};
+
+int	archlcdmode(LCDmode*);
+
+Vdisplay	*lcd_init(LCDmode*);
+void	lcd_setcolor(ulong, ulong, ulong, ulong);
+void	lcd_flush(void);
+
+extern void	blankscreen(int);
+extern void	drawblankscreen(int);
+extern ulong blanktime;
+extern Point mousexy(void);
--- /dev/null
+++ b/os/ipaq1110/tstdraw.b
@@ -1,0 +1,107 @@
+implement Test;
+
+include "sys.m";
+
+include "draw.m";
+
+Test: module
+{
+	init:	fn(ctxt: ref Draw->Context, argv: list of string);
+};
+
+init(ctxt: ref Draw->Context, nil: list of string)
+{
+	sys := load Sys Sys->PATH;
+	draw := load Draw Draw->PATH;
+	Display, Font, Rect, Point, Image, Screen: import draw;
+
+	#
+	# Set up connection to display, or use the existing one
+	# if provided.
+	#
+	display: ref Display;
+	disp: ref Image;
+	if (ctxt == nil) {
+		display = draw->Display.allocate(nil);
+		disp = display.image;
+	} else {
+		display = ctxt.display;
+		disp = ctxt.screen.newwindow(display.image.r, Draw->White);
+	}
+
+	#
+	# Initialize colours.
+	#
+	red := display.color(Draw->Red);
+	blue := display.color(Draw->Blue);
+	white := display.color(Draw->White);
+	yellow := display.color(Draw->Yellow);
+	ones := display.ones;
+
+	#
+	# Paint the screen red.
+	#
+	disp.draw(disp.r, red, ones, disp.r.min);
+	sys->sleep(5000);
+
+	#
+	# Texture a region with rectangular tiles.
+	#
+	texture := display.newimage(((0,0),(2,3)), disp.ldepth, 1, 0);
+	texture.clipr = ((-10000,-10000),(10000,10000));
+	# put something in the texture
+	texture.draw(((0,0),(1,3)), blue, ones, (0,0));
+	texture.draw(((0,0),(2, 1)), blue, ones, (0,0));
+	# use texture as both source and mask to let
+	# destination colour show through
+	disp.draw(((100,100),(200,200)), texture, texture, (0,0));
+	sys->sleep(5000);
+
+	#
+	# White-out a quarter of the pixels in a region,
+	# to make the region appear shaded.
+	#
+	stipple := display.newimage(((0,0),(2,2)), disp.ldepth, 1, 0);
+	stipple.draw(((0,0),(1,1)), ones, ones, (0,0));
+	disp.draw(((100,100),(300,200)), white, stipple, (0,0));
+	sys->sleep(5000);
+
+	#
+	# Draw textured characters.
+	#
+	font := Font.open(display, "*default*");
+	disp.text((100,210), texture, (0,0), font, "Hello world");
+	sys->sleep(5000);
+
+	#
+	# Draw picture in elliptical frame.
+	#
+	delight := display.open("/icons/delight.bit");
+	piccenter := delight.r.min.add(delight.r.max).div(2);
+	disp.fillellipse((200,100), 150, 50, delight, piccenter);
+	disp.ellipse((200,100), 150, 50, 3, yellow, (0,0));
+	sys->sleep(5000);
+
+	#
+	# Draw a parabolic brush stroke using an elliptical brush
+	# to reveal more of the picture, consistent with what's
+	# already visible.
+	#
+	dx : con 15;
+	dy : con 3;
+	brush := display.newimage(((0,0),(2*dx+1,2*dy+1)), disp.ldepth,
+                               0, 0);
+	brush.fillellipse((dx,dy), dx, dy, ones, (0,0));
+	for(x:=delight.r.min.x; x<delight.r.max.x; x++){
+		y := (x-piccenter.x)*(x-piccenter.x)/80;
+		y += 2*dy+1;	# so whole brush is visible at top
+		xx := x+(200-piccenter.x)-dx;
+		yy := y+(100-piccenter.y)-dy;
+		disp.gendraw(((xx,yy),(xx+2*dx+1,yy+2*dy+1)),
+                       delight, (x-dx, y-dy), brush, (0,0));
+	}
+	for (i := 0; i < 500; i++) {
+		disp.draw(disp.r, disp, ones, (0, 10));
+		sys->sleep(5);
+	}
+} 
--- /dev/null
+++ b/os/ipaq1110/upd
@@ -1,0 +1,4 @@
+#!/dis/sh
+bind -a '#F' /dev
+echo erase all >/dev/flash/kernelctl
+dd -conv sync </os/ipaq1110/k.gz >/dev/flash/kernel
--- /dev/null
+++ b/os/ipengine/NOTICE
@@ -1,0 +1,4 @@
+Inferno® Copyright © 1996-1999 Lucent Technologies Inc.  All rights reserved.
+PowerPC support Copyright © 1995-1997 C H Forsyth (forsyth@terzarima.net).  All rights reserved.
+MPC8xx Inferno PowerPC port Copyright © 1998-2003 Vita Nuova Holdings Limited.  All rights reserved.
+ip-Engine1 PowerPC port Copyright © 2000-2003 Vita Nuova Holdings Limited.  All rights reserved.
--- /dev/null
+++ b/os/ipengine/README
@@ -1,0 +1,40 @@
+The ipEngine-1 is a Motorola MPC8xx-based small computer
+made by Bright Star Engineering (www.brightstareng.com)
+which also provides Linux and their own operating system for it.
+It is an interesting hardware platform to do network-oriented Inferno
+applications.
+
+The ipEngine is unusual in including an FPGA (Altera Flex 6016).
+See fpga(3) and fpgaload(8).
+We are also working on software to help program the thing.
+
+
+Booting the ipEngine
+
+0.	serial cable (port 1, 9600 baud), ether, power etc.
+
+1.	make appropriate entries for your site in flash
+	using the BSE monitor:
+
+	set netmask 255.255.255.0
+	set nameserver 200.1.1.11
+	set server 200.1.1.11
+	set serverip 200.1.1.11
+	set gateway 200.1.1.50
+	set hostname stella
+	set domain vitanuova.com
+	set myip 200.1.1.96
+
+2.	add an entry to do the boot by tftp:
+
+	set bootcmd "load /usr/inferno/os/ipengine/iipe 3000; go 3020"
+
+	contrary to the BSE documentation this loads any
+	binary file at the given address.  the -b option
+	doesn't seem to be needed.
+
+3.	reset
+	it should load and start the kernel
+
+Vita Nuova
+16 July 2003
--- /dev/null
+++ b/os/ipengine/archipe.c
@@ -1,0 +1,488 @@
+#include	"u.h"
+#include	"../port/lib.h"
+#include	"mem.h"
+#include	"dat.h"
+#include	"fns.h"
+#include	"io.h"
+
+#include <draw.h>
+#include <memdraw.h>
+#include "screen.h"
+
+#include "../port/netif.h"
+#include "etherif.h"
+#include "../port/flashif.h"
+
+#include	"archipe.h"
+
+/*
+ * board-specific support for the Bright Star Engineering ipEngine-1
+ */
+
+enum {
+	/* sccr */
+	COM3=	IBIT(1)|IBIT(2),	/* clock output disabled */
+	TBS =	IBIT(6),	/* =0, time base is OSCCLK/{4,16}; =1, time base is GCLK2/16 */
+	RTSEL = IBIT(8),	/* =0, select main oscillator (OSCM); =1, select external crystal (EXTCLK) */
+	RTDIV = IBIT(7),	/* =0, divide by 4; =1, divide by 512 */
+	CRQEN = IBIT(9),	/* =1, switch to high frequency when CPM active */
+	PRQEN = IBIT(10),	/* =1, switch to high frequency when interrupt pending */
+
+	/* plprcr */
+	CSRC = IBIT(21),	/* =0, clock is DFNH; =1, clock is DFNL */
+
+	Showports = 0,		/* used to find lightly-documented bits set by bootstrap (eg, PDN) */
+};
+
+static ulong ports[4*4];
+
+/*
+ * called early in main.c, after machinit:
+ * using board and architecture specific registers, initialise
+ * 8xx registers that need it and complete initialisation of the Mach structure.
+ */
+void
+archinit(void)
+{
+	IMM *io;
+	int mf;
+
+	io = m->iomem;	/* run by reset code: no need to lock */
+	m->clockgen = 4000000;	/* crystal frequency */
+	m->oscclk = m->clockgen/MHz;
+	io->plprcrk = KEEP_ALIVE_KEY;
+	io->plprcr &= ~CSRC;	/* general system clock is DFNH */
+	mf = (io->plprcr >> 20)+1;	/* use timing set by bootstrap */
+	io->plprcrk = ~KEEP_ALIVE_KEY;
+	io->sccrk = KEEP_ALIVE_KEY;
+	io->sccr |= CRQEN | PRQEN | RTDIV | COM3;	/* devfpga.c resets COM3 if needed */
+	io->sccrk = ~KEEP_ALIVE_KEY;
+	m->cpuhz = m->clockgen*mf;
+	m->speed = m->cpuhz/MHz;
+	if((io->memc[CLOCKCS].base & 1) == 0){	/* prom hasn't mapped it */
+		io->memc[CLOCKCS].option = 0xFFFF0F24;
+		io->memc[CLOCKCS].base = 0xFF020001;
+	}
+	if(Showports){
+		ports[0] = io->padat;
+		ports[1] = io->padir;
+		ports[2] = io->papar;
+		ports[3] = io->paodr;
+		ports[4] = io->pbdat;
+		ports[5] = io->pbdir;
+		ports[6] = io->pbpar;
+		ports[7] = io->pbodr;
+		ports[8] = io->pcdat;
+		ports[9] = io->pcdir;
+		ports[10] = io->pcpar;
+		ports[11] = io->pcso;
+		ports[12] = io->pddat;
+		ports[13] = io->pddir;
+		ports[14] = io->pdpar;
+		ports[15] = 0;
+	}
+}
+
+static ulong
+banksize(int x, ulong *pa)
+{
+	IMM *io;
+
+	io = m->iomem;
+	if((io->memc[x].base & 1) == 0)
+		return 0;	/* bank not valid */
+	*pa = io->memc[x].base & ~0x7FFF;
+	return -(io->memc[x].option&~0x7FFF);
+}
+
+/*
+ * initialise the kernel's memory configuration:
+ * there are two banks (base0, npage0) and (base1, npage1).
+ * initialise any other values in conf that are board-specific.
+ */
+void
+archconfinit(void)
+{
+	ulong pa, nbytes, ktop;
+
+	conf.nscc = 2;
+	conf.sccuarts = 0;	/* no SCC uarts */
+	conf.smcuarts = (1<<0)|(1<<1);	/* SMC1 (console) and SMC2 */
+
+	nbytes = banksize(DRAMCS, &pa);
+	if(nbytes == 0){	/* force default */
+		nbytes = 16*1024*1024;
+		pa = 0;
+	}
+	conf.npage0 = nbytes/BY2PG;
+	conf.base0 = pa;
+
+	conf.npage1 = 0;
+
+	/* the following assumes the kernel text and/or data is in bank 0 */
+	ktop = PGROUND((ulong)end);
+	ktop = PADDR(ktop) - conf.base0;
+	conf.npage0 -= ktop/BY2PG;
+	conf.base0 += ktop;
+}
+
+void
+cpuidprint(void)
+{
+	ulong v;
+
+	print("PVR: ");
+	switch(m->cputype){
+	case 0x01:	print("MPC601"); break;
+	case 0x03:	print("MPC603"); break;
+	case 0x04:	print("MPC604"); break;
+	case 0x06:	print("MPC603e"); break;
+	case 0x07:	print("MPC603e-v7"); break;
+	case 0x50:	print("MPC8xx"); break;
+	default:	print("PowerPC version #%x", m->cputype); break;
+	}
+	print(", revision #%lux\n", getpvr()&0xffff);
+	print("IMMR: ");
+	v = getimmr() & 0xFFFF;
+	switch(v>>8){
+	case 0x00:	print("MPC860/821"); break;
+	case 0x20:	print("MPC823"); break;
+	case 0x21:	print("MPC823A"); break;
+	default:	print("Type #%lux", v>>8); break;
+	}
+	print(", mask #%lux\n", v&0xFF);
+	print("%lud MHz system\n", m->cpuhz/MHz);
+	print("\n");
+
+	if(Showports){
+
+		print("plprcr=%8.8lux sccr=%8.8lux\n", m->iomem->plprcr, m->iomem->sccr);
+		print("pipr=%8.8lux\n", m->iomem->pipr);
+
+		print("ports:\n");
+		for(v=0;v<nelem(ports);v++)
+			print("%2ld %8.8lux\n", v, ports[v]);
+
+		/* dump the memory configuration while we're at it */
+		for(v=0; v<nelem(m->iomem->memc); v++)
+			if(m->iomem->memc[v].base & 1)
+				print("%ld %8.8lux %8.8lux\n", v, m->iomem->memc[v].base, m->iomem->memc[v].option);
+	}
+}
+
+/*
+ * fetch parameters from flash, as stored by BSE bootstrap,
+ * compensating for a bug in its fset that produces silly entries.
+ */
+
+static int
+envnameok(char *s)
+{
+	if(*s == '*')
+		s++;
+	if(*s >= '0' && *s <= '9' || *s == 0)
+		return 0;
+	for(; *s; s++)
+		if(*s >= '0' && *s <= '9' ||
+		   *s >= 'a' && *s <= 'z' ||
+		   *s >= 'A' && *s <= 'Z' ||
+		   *s == '.' || *s == '_' || *s == '#'){
+			/* ok */
+		}else
+			return 0;
+	return 1;
+}
+
+int
+archconfval(char **names, char **vals, int limit)
+{
+	uchar *b, *e;
+	char *s;
+	int n, v, l, o;
+	static char bootargs[512];
+
+	/* we assume we can access this space before mmuinit() */
+	b = KADDR(PHYSFLASH+0x4000);
+	if(*b & 1){
+		b += 0x2000;	/* try alternative location */
+		if(*b & 1)
+			return 0;
+	}
+	v = (b[2]<<8)|b[3];
+	b += 4;
+	if(v >= 0x2000-4)
+		return 0;
+	n = 0;
+	o = 0;
+	e = b+v;
+	for(; b < e; b += v){
+		v = *b;
+		if(v == 0xFF || n >= limit)
+			break;
+		s = (char*)b+1;
+		if(v >= 0x80){
+			v = ((v&0x7F)<<8) | b[1];
+			s++;
+		}
+		if(envnameok(s)){
+			names[n] = s;
+			s += strlen(s)+1;
+			l = strlen(s)+1;
+			if(o+l > sizeof(bootargs))
+				break;
+			vals[n] = bootargs+o;
+			memmove(vals[n], s, l);
+			o += l;
+			n++;
+		}
+	}
+	return n;
+}
+
+void
+toggleled(int b)
+{
+	int s;
+	s = splhi();
+	m->iomem->pdpar &= ~(0x20<<b);
+	m->iomem->pddir |= 0x20<<b;
+	m->iomem->pddat ^= 0x020<<b;
+	splx(s);
+}
+
+/*
+ * provide value for #r/switch (devrtc.c)
+ */
+int
+archoptionsw(void)
+{
+	return 0;
+}
+
+/*
+ * invoked by clock.c:/^clockintr
+ */
+static void
+twinkle(void)
+{
+	if(m->ticks%MS2TK(1000) == 0 && m->iomem)
+		toggleled(0);
+}
+
+void	(*archclocktick)(void) = twinkle;
+
+/*
+ * invoked by ../port/taslock.c:/^ilock:
+ * reset watchdog timer here, if there is one and it is enabled
+ */
+void
+clockcheck(void)
+{
+}
+
+/*
+ * for ../port/devflash.c:/^flashreset
+ * retrieve flash type, virtual base and length and return 0;
+ * return -1 on error (no flash)
+ */
+int
+archflashreset(int bank, Flash *f)
+{
+	if(bank != 0)
+		return -1;
+	f->type = "Intel28F320B3B";
+	f->addr = KADDR(PHYSFLASH);
+	f->size = 4*1024*1024;
+	f->width = 2;
+	return 0;
+}
+
+void
+archflashwp(Flash*, int)
+{
+}
+
+/*
+ * set ether parameters: the contents should be derived from EEPROM or NVRAM
+ */
+int
+archether(int ctlno, Ether *ether)
+{
+	if(isaconfig("ether", ctlno, ether) == 0 && ctlno > 0)
+		return -1;
+	if(ctlno == 0){
+		ether->type = "SCC";
+		ether->port = 2;
+	}
+	memmove(ether->ea, KADDR(PHYSFLASH+0x3FFA), Eaddrlen);
+	return 1;
+}
+
+/*
+ * enable the clocks for the given SCC ether and reveal them to the caller.
+ * do anything else required to prepare the transceiver (eg, set full-duplex, reset loopback).
+ */
+int
+archetherenable(int cpmid, int *rcs, int *tcs, int mbps, int fd)
+{
+	IMM *io;
+
+	if(cpmid != CPscc2)
+		return -1;
+	USED(mbps);
+	io = ioplock();
+	io->pcpar &= ~EnetLoopback;
+	io->pcso &= ~EnetLoopback;
+	io->pcdir |= EnetLoopback;
+	io->pcdat &= ~EnetLoopback;
+	if(0){	/* TO CHECK: causes ether errors if used */
+		io->pbpar &= ~EnetFullDuplex;
+		io->pbdir |= EnetFullDuplex;
+		if(fd)
+			io->pbdat |= EnetFullDuplex;
+		else
+			io->pbdat &= ~EnetFullDuplex;
+	}
+	io->pbpar &= ~EnableEnet;
+	io->pbdir |= EnableEnet;
+	io->pbdat |= EnableEnet;
+	io->papar |= SIBIT(7)|SIBIT(6);	/* enable CLK1 and CLK2 */
+	io->padir &= ~(SIBIT(7)|SIBIT(6));
+	iopunlock();
+	*rcs = CLK2;
+	*tcs = CLK1;
+	return 0;
+}
+
+/*
+ * do anything extra required to enable the UART on the given CPM port
+ */
+static	ulong	uartsactive;
+
+void
+archenableuart(int id, int irda)
+{
+	IMM *io;
+
+	USED(id);	/* both uarts seem to be controlled by the same bit */
+	USED(irda);	/* no IrDA on ipEngine */
+	io = ioplock();
+	if(uartsactive == 0){
+		io->pbpar &= ~EnableRS232;
+		io->pbdir |= EnableRS232;
+		io->pbdat |= EnableRS232;
+	}
+	uartsactive |= 1<<id;
+	iopunlock();
+}
+
+/*
+ * do anything extra required to disable the UART on the given CPM port
+ */
+void
+archdisableuart(int id)
+{
+	IMM *io;
+
+	io = ioplock();
+	uartsactive &= ~(1<<id);
+	if(uartsactive == 0)
+		io->pbdat &= ~EnableRS232;
+	iopunlock();
+}
+
+/*
+ * enable the external USB transceiver
+ *	speed is 12MHz if highspeed is non-zero; 1.5MHz if zero
+ *	master is non-zero if the node is acting as USB Host and should provide power
+ */
+void
+archenableusb(int highspeed, int master)
+{
+	IMM *io;
+
+	USED(master);
+	io = ioplock();
+	io->pcpar &= ~USBFullSpeed;
+	io->pcso &= ~USBFullSpeed;
+	if(highspeed)
+		io->pcdat |= USBFullSpeed;
+	else
+		io->pcdat &= ~USBFullSpeed;
+	io->pcdir |= USBFullSpeed;
+	iopunlock();
+}
+
+/*
+ * shut down the USB transceiver
+ */
+void
+archdisableusb(void)
+{
+	/* nothing to be done on ipEngine, apparently */
+}
+
+/*
+ * set the external infrared transceiver to the given speed
+ */
+void
+archsetirxcvr(int highspeed)
+{
+	USED(highspeed);
+}
+
+/*
+ * force hardware reset/reboot
+ */
+void
+archreboot(void)
+{
+	IMM *io;
+
+	io = m->iomem;
+	io->plprcrk = KEEP_ALIVE_KEY;
+	io->plprcr |= 1<<7;	/* checkstop reset enable */
+	io->plprcrk = ~KEEP_ALIVE_KEY;
+	eieio();
+	io->sdcr = 1;
+	eieio();
+	io->lccr = 0;	/* switch LCD off */
+	eieio();
+	firmware(0);
+}
+
+/*
+ * enable/disable the LCD panel's backlight
+ */
+void
+archbacklight(int on)
+{
+	USED(on);
+}
+
+/*
+ * set parameters to describe the screen
+ */
+int
+archlcdmode(Mode *m)
+{
+	/* sample parameters in case a panel is attached to the external pins */
+	m->x = 640;
+	m->y = 480;
+	m->d = 3;
+	m->lcd.freq = 25000000;
+	m->lcd.ac = 0;
+	m->lcd.vpw = 1;
+	m->lcd.wbf = 33;
+	m->lcd.wbl = 228;
+	m->lcd.flags = IsColour | IsTFT | OELow | VsyncLow | ClockLow;
+	return -1;	/* there isn't a screen */
+}
+
+/*
+ * there isn't a keyboard port
+ */
+void
+archkbdinit(void)
+{
+}
--- /dev/null
+++ b/os/ipengine/archipe.h
@@ -1,0 +1,32 @@
+/*
+ * values for Brightstar Engineering ipEngine
+ */
+enum {
+	/* CS assignment */
+	BOOTCS = 0,	/* flash */
+	FPGACS = 1,	/* 8Mb FPGA space */
+	DRAMCS = 2,
+	FPGACONFCS = 3,	/* FPGA config */
+	CLOCKCS = 4,	/* clock synth reg */
+};
+
+enum {
+	/* port A pins */
+	VCLK=	SIBIT(5),
+	BCLK=	SIBIT(4),
+
+	/* port B */
+	EnableVCLK=	IBIT(30),
+	EnableEnet=	IBIT(29),
+	EnableRS232=	IBIT(28),
+	EnetFullDuplex=	IBIT(16),
+
+	/* port C */
+	nCONFIG = SIBIT(13),	/* FPGA configuration */
+	USBFullSpeed=	SIBIT(12),
+	PDN=	SIBIT(5),	/* ? seems to control power to FPGA subsystem? */
+	EnetLoopback=	SIBIT(4),
+
+	/* nSTATUS is ip_b1, conf_done is ip_b0 in PIPR (hardware doc wrongly says ip_b2 and ip_b1) */
+
+};
--- /dev/null
+++ b/os/ipengine/dat.h
@@ -1,0 +1,162 @@
+typedef struct Conf	Conf;
+typedef struct FPU	FPU;
+typedef struct FPenv	FPenv;
+typedef struct IMM	IMM;
+typedef struct Irqctl	Irqctl;
+typedef struct ISAConf	ISAConf;
+typedef struct Label	Label;
+typedef struct Lock	Lock;
+typedef struct Mach	Mach;
+typedef struct Map	Map;
+typedef struct Power Power;
+typedef struct RMap RMap;
+typedef struct Ureg	Ureg;
+
+typedef ulong Instr;
+
+#define	MACHP(n)	(n==0? &mach0 : *(Mach**)0)
+
+struct	Lock
+{
+	ulong	key;
+	ulong	pc;
+	ulong	sr;
+	int	pri;
+};
+
+struct	Label
+{
+	ulong	sp;
+	ulong	pc;
+};
+
+/*
+ * Proc.fpstate
+ */
+enum
+{
+	FPINIT,
+	FPACTIVE,
+	FPINACTIVE,
+};
+
+/*
+ * This structure must agree with FPsave and FPrestore asm routines
+ */
+struct FPenv
+{
+	union {
+		double	fpscrd;
+		struct {
+			ulong	pad;
+			ulong	fpscr;
+		};
+	};
+	int	fpistate;	/* emulated fp */
+	ulong	emreg[32][3];	/* emulated fp */
+};
+/*
+ * This structure must agree with fpsave and fprestore asm routines
+ */
+struct	FPU
+{
+	double	fpreg[32];
+	FPenv	env;
+};
+
+struct Conf
+{
+	ulong	nmach;		/* processors */
+	ulong	nproc;		/* processes */
+	ulong	npage0;		/* total physical pages of memory */
+	ulong	npage1;		/* total physical pages of memory */
+	ulong	npage;		/* total physical pages of memory */
+	ulong	base0;		/* base of bank 0 */
+	ulong	base1;		/* base of bank 1 */
+	ulong	ialloc;		/* max interrupt time allocation in bytes */
+
+	int	nscc;	/* number of SCCs implemented */
+	ulong	smcuarts;		/* bits for SMCs to define as eiaN */
+	ulong	sccuarts;		/* bits for SCCs to define as eiaN  */
+	int	nocts2;	/* CTS2 and CD2 aren't connected */
+	uchar*	nvrambase;	/* virtual address of nvram */
+	ulong	nvramsize;	/* size in bytes */
+};
+
+#include "../port/portdat.h"
+
+/*
+ *  machine dependent definitions not used by ../port/dat.h
+ */
+
+struct Mach
+{
+	/* OFFSETS OF THE FOLLOWING KNOWN BY l.s */
+	int	machno;			/* physical id of processor (unused) */
+	ulong	splpc;			/* pc of last caller to splhi (unused) */
+	int	mmask;			/* 1<<m->machno (unused) */
+
+	/* ordering from here on irrelevant */
+	ulong	ticks;			/* of the clock since boot time */
+	Proc	*proc;			/* current process on this processor */
+	Label	sched;			/* scheduler wakeup */
+	Lock	alarmlock;		/* access to alarm list */
+	void	*alarm;			/* alarms bound to this clock */
+	int	nrdy;
+	int	speed;	/* general system clock in MHz */
+	long	oscclk;	/* oscillator frequency (MHz) */
+	long	cpuhz;	/* general system clock (cycles) */
+	long	clockgen;	/* clock generator frequency (cycles) */
+	int	cputype;
+	ulong	delayloop;
+	ulong*	bcsr;
+	IMM*	iomem;	/* MPC8xx internal i/o control memory */
+
+	/* MUST BE LAST */
+	int	stack[1];
+};
+extern	Mach	mach0;
+
+
+/*
+ *  a parsed .ini line
+ */
+#define NISAOPT		8
+
+struct ISAConf {
+	char*	type;
+	ulong	port;
+	ulong	irq;
+	ulong	mem;
+	int	dma;
+	ulong	size;
+	ulong	freq;
+	uchar	bus;
+
+	int	nopt;
+	char*	opt[NISAOPT];
+};
+
+struct Map {
+	int	size;
+	ulong	addr;
+};
+
+struct RMap {
+	char*	name;
+	Map*	map;
+	Map*	mapend;
+
+	Lock;
+};
+
+struct Power {
+	Dev*	dev;
+	int	(*powerdown)(Power*);
+	int	(*powerup)(Power*);
+	int	state;
+	void*	arg;
+};
+
+extern register Mach	*m;
+extern register Proc	*up;
--- /dev/null
+++ b/os/ipengine/devfpga.c
@@ -1,0 +1,389 @@
+#include	"u.h"
+#include	"../port/lib.h"
+#include	"mem.h"
+#include	"dat.h"
+#include	"fns.h"
+#include	"../port/error.h"
+
+#include	"io.h"
+#include	"archipe.h"
+
+enum {
+	FPGASIZE = 8*1024*1024,
+	FPGATMR = 2-1,	/* BCLK timer number (mapped to origin 0) */
+	TIMERSH = FPGATMR*4,	/* timer field shift */
+
+	COM3=	IBIT(1)|IBIT(2),	/* sccr: clock output disabled */
+
+	ConfDone = 1<<1,
+	nStatus = 1<<0,
+};
+
+/*
+ * provisional FPGA interface for simple development work;
+ * for more complex things, use this to load the device then have a
+ * purpose-built device driver or module
+ */
+
+enum{
+	Qdir,
+	Qmemb,
+	Qmemw,
+	Qprog,
+	Qctl,
+	Qclk,
+	Qstatus,
+};
+
+static struct {
+	QLock;
+	int	clkspeed;
+} fpga;
+
+static void resetfpga(void);
+static void	startfpga(int);
+static int endfpga(void);
+static int fpgastatus(void);
+static void powerfpga(int);
+static void vclkenable(int);
+static void vclkset(char*, char*, char*, char*);
+static void memmovew(ushort*, ushort*, long);
+
+static Dirtab fpgadir[]={
+	".",			{Qdir, 0, QTDIR},	0,	0555,
+	"fpgamemb",		{Qmemb, 0},	FPGASIZE,	0666,
+	"fpgamemw",		{Qmemw, 0},	FPGASIZE, 0666,
+	"fpgaprog",	{Qprog, 0},	0,	0222,
+	"fpgastatus",	{Qstatus, 0},	0,	0444,
+	"fpgactl",		{Qctl, 0},		0,	0666,
+	"fpgaclk",		{Qclk, 0},		0,	0666,
+};
+
+static char Eodd[] = "odd count or offset";
+
+static void
+fpgareset(void)
+{
+	powerfpga(0);
+}
+
+static Chan*
+fpgaattach(char *spec)
+{
+	return devattach('G', spec);
+}
+
+static Walkqid*
+fpgawalk(Chan *c, Chan *nc, char **name, int nname)
+{
+	return devwalk(c, nc, name, nname, fpgadir, nelem(fpgadir), devgen);
+}
+
+static int
+fpgastat(Chan *c, uchar *dp, int n)
+{
+	return devstat(c, dp, n, fpgadir, nelem(fpgadir), devgen);
+}
+
+static Chan*
+fpgaopen(Chan *c, int omode)
+{
+	return devopen(c, omode, fpgadir, nelem(fpgadir), devgen);
+}
+
+static void
+fpgaclose(Chan*)
+{
+}
+
+static long	 
+fpgaread(Chan *c, void *buf, long n, vlong offset)
+{
+	int v;
+	char stat[32], *p;
+
+	if(c->qid.type & QTDIR)
+		return devdirread(c, buf, n, fpgadir, nelem(fpgadir), devgen);
+
+	switch((ulong)c->qid.path){
+	case Qmemb:
+		if(offset >= FPGASIZE)
+			return 0;
+		if(offset+n >= FPGASIZE)
+			n = FPGASIZE-offset;
+		memmove(buf, KADDR(FPGAMEM+offset), n);
+		return n;
+	case Qmemw:
+		if((n | offset) & 1)
+			error(Eodd);
+		if(offset >= FPGASIZE)
+			return 0;
+		if(offset+n >= FPGASIZE)
+			n = FPGASIZE-offset;
+		memmovew((ushort*)buf, (ushort*)KADDR(FPGAMEM+offset), n);
+		return n;
+	case Qstatus:
+		v = fpgastatus();
+		p = seprint(stat, stat+sizeof(stat), "%sconfig", v&ConfDone?"":"!");
+		seprint(p, stat+sizeof(stat), " %sstatus\n", v&nStatus?"":"!");
+		return readstr(offset, buf, n, stat);
+	case Qclk:
+		return readnum(offset, buf, n, fpga.clkspeed, NUMSIZE);
+	case Qctl:
+	case Qprog:
+		return 0;
+	}
+	error(Egreg);
+	return 0;		/* not reached */
+}
+
+static long	 
+fpgawrite(Chan *c, void *buf, long n, vlong offset)
+{
+	int i, j, v;
+	ulong w;
+	Cmdbuf *cb;
+	ulong *cfg;
+	uchar *cp;
+
+	switch((ulong)c->qid.path){
+	case Qmemb:
+		if(offset >= FPGASIZE)
+			return 0;
+		if(offset+n >= FPGASIZE)
+			n = FPGASIZE-offset;
+		memmove(KADDR(FPGAMEM+offset), buf, n);
+		return n;
+	case Qmemw:
+		if((n | offset) & 1)
+			error(Eodd);
+		if(offset >= FPGASIZE)
+			return 0;
+		if(offset+n >= FPGASIZE)
+			n = FPGASIZE-offset;
+		memmovew((ushort*)KADDR(FPGAMEM+offset), (ushort*)buf, n);
+		return n;
+	case Qctl:
+		cb = parsecmd(buf, n);
+		if(waserror()){
+			free(cb);
+			nexterror();
+		}
+		if(cb->nf < 1)
+			error(Ebadarg);
+		if(strcmp(cb->f[0], "reset") == 0)
+			resetfpga();
+		else if(strcmp(cb->f[0], "bclk") == 0){
+			v = 48;
+			if(cb->nf > 1)
+				v = strtoul(cb->f[1], nil, 0);
+			if(v <= 0 || 48%v != 0)
+				error(Ebadarg);
+			startfpga(48/v-1);
+		}else if(strcmp(cb->f[0], "vclk") == 0){
+			if(cb->nf == 5){	/* vclk n m v r */
+				vclkenable(1);
+				vclkset(cb->f[1], cb->f[2], cb->f[3], cb->f[4]);
+			}else
+				vclkenable(cb->nf < 2 || strcmp(cb->f[1], "on") == 0);
+		}else if(strcmp(cb->f[0], "power") == 0)
+			powerfpga(cb->nf < 2 || strcmp(cb->f[1], "off") != 0);
+		else
+			error(Ebadarg);
+		poperror();
+		free(cb);
+		return n;
+	case Qprog:
+		qlock(&fpga);
+		if(waserror()){
+			qunlock(&fpga);
+			nexterror();
+		}
+		powerfpga(1);
+		resetfpga();
+		cfg = KADDR(FPGACR);
+		cp = buf;
+		for(i=0; i<n; i++){
+			w = cp[i];
+			for(j=0; j<8; j++){
+				*cfg = w&1;
+				w >>= 1;
+			}
+		}
+		for(j=0; j<50; j++)	/* Altera note says at least 10 clock cycles, but microblaster uses 50 */
+			*cfg = 0;
+		v = fpgastatus();
+		if(v != (nStatus|ConfDone)){
+			snprint(up->genbuf, sizeof(up->genbuf), "error loading fpga: status %d", v);
+			error(up->genbuf);
+		}
+		poperror();
+		qunlock(&fpga);
+		return n;
+	}
+	error(Egreg);
+	return 0;		/* not reached */
+}
+
+/*
+ * PDN seems to control power to the FPGA subsystem
+ * but it is not documented nor is its scope clear (PLL as well?).
+ * It will not run without it.
+ */
+static void
+powerfpga(int on)
+{
+	IMM *io;
+
+	io = ioplock();
+	if(io->sccr & COM3){
+		io->sccrk = KEEP_ALIVE_KEY;
+		io->sccr &= ~ COM3;	/* FPGA designs can use the clock */
+		io->sccrk = ~KEEP_ALIVE_KEY;
+	}
+	io->pcpar &= ~PDN;
+	io->pcdir |= PDN;
+	if(on)
+		io->pcdat &= ~PDN;	
+	else
+		io->pcdat |= PDN;
+	iopunlock();
+}
+
+static void
+resetfpga(void)
+{
+	IMM *io;
+
+	io = ioplock();
+	io->pcpar &= ~nCONFIG;
+	io->pcdir |= nCONFIG;
+	io->pcdat &= ~nCONFIG;
+	microdelay(200);
+	io->pcdat |= nCONFIG;
+	iopunlock();
+}
+
+static int
+fpgastatus(void)
+{
+	/* isolate status bits IP_B0 and IP_B1 */
+	return (m->iomem->pipr>>14) & (ConfDone|nStatus);
+}
+
+static void
+startfpga(int scale)
+{
+	IMM *io;
+
+	io = ioplock();
+	io->tgcr &= ~(0xF<<TIMERSH);
+	io->tmr2 = ((scale&0xFF)<<8) | 0x2A;
+	io->tcn2 = 0;
+	io->trr2 = 0;
+	io->ter2 = 0xFFFF;
+	io->tgcr |= 0x1<<TIMERSH;
+	io->padir |= BCLK;
+	io->papar |= BCLK;
+	iopunlock();
+}
+
+static void
+vclkenable(int i)
+{
+	IMM *io;
+
+	io = ioplock();
+	io->padir &= ~VCLK;
+	io->papar &= ~VCLK;
+	io->pbdir |= EnableVCLK;
+	io->pbpar &= ~EnableVCLK;
+	if(i)
+		io->pbdat |= EnableVCLK;
+	else
+		io->pbdat &= ~EnableVCLK;
+	iopunlock();
+}
+
+static void
+vclkin(ulong *clk, int v)
+{
+	int i;
+
+	for(i=0; i<7; i++)
+		*clk = (v>>i) & 1;
+}
+
+static void
+vclkset(char *ns, char *ms, char *vs, char *rs)
+{
+	int n, m, v, r;
+	ulong *clk;
+
+	clk = KADDR(CLOCKCR);
+	n = strtol(ns, nil, 0);
+	m = strtol(ms, nil, 0);
+	v = strtol(vs, nil, 0);
+	r = strtol(rs, nil, 0);
+	if(n < 3 || n > 127 || m < 3 || m > 127 || v != 1 && v != 8 ||
+	   r != 1 && r != 2 && r != 4 && r != 8)
+		error(Ebadarg);
+	vclkenable(0);
+	vclkin(clk, n);
+	vclkin(clk, m);
+	*clk = (v==0) & 1;
+	*clk = 1; *clk = 1;
+	*clk = r == 2 || r == 8;
+	*clk = r == 4 || r == 8;
+	*clk = 1;	/* clock out */
+	*clk = 0;	/* disable clk/x */
+	*clk = 1; *clk = 0; *clk = 1;
+	*clk = 0; *clk = 0; *clk = 0;
+	vclkenable(1);
+}
+
+/*
+ * copy data aligned on 16-bit word boundaries.
+ */
+static void
+memmovew(ushort *to, ushort *from, long count)
+{
+	int n;
+
+	if(count <= 0)
+		return;
+	count >>= 1;
+	n = (count+7) >> 3;
+	switch(count&7) {	/* Duff's device */
+	case 0: do {	*to++ = *from++;
+	case 7:		*to++ = *from++;
+	case 6:		*to++ = *from++;
+	case 5:		*to++ = *from++;
+	case 4:		*to++ = *from++;
+	case 3:		*to++ = *from++;
+	case 2:		*to++ = *from++;
+	case 1:		*to++ = *from++;
+		} while(--n > 0);
+	}
+}
+
+Dev fpgadevtab = {
+	'G',
+	"fpga",
+
+	fpgareset,
+	devinit,
+	devshutdown,
+	fpgaattach,
+	fpgawalk,
+	fpgastat,
+	fpgaopen,
+	devcreate,
+	fpgaclose,
+	fpgaread,
+	devbread,
+	fpgawrite,
+	devbwrite,
+	devremove,
+	devwstat,
+};
--- /dev/null
+++ b/os/ipengine/flash28f320b3b.c
@@ -1,0 +1,43 @@
+#include	"u.h"
+#include	"../port/lib.h"
+#include	"mem.h"
+#include	"dat.h"
+#include	"fns.h"
+#include	"io.h"
+#include	"../port/error.h"
+
+#include	"../port/flashif.h"
+
+/*
+ * Intel 28F320B3B in word mode
+ */
+
+#define	I(x)	(x)
+enum {
+	ReadArray = 0x00FF,
+};
+
+#include "../port/flashintel"
+
+static int
+reset(Flash *f)
+{
+	f->id = 0x0089;	/* can't use autoselect: might be running in flash */
+	f->devid = 0x8897;
+	f->write = intelwrite2;
+	f->erasezone = intelerase;
+	f->width = 2;
+	f->cmask = 0x00FF;
+	*(ushort*)f->addr = ClearStatus;
+	*(ushort*)f->addr = ReadArray;
+	f->nr = 2;
+	f->regions[0] = (Flashregion){8, 0, 8*(8*1024), 8*1024, 0};
+	f->regions[1] = (Flashregion){63, 64*1024, 4*1024*1024, 64*1024, 0};
+	return 0;
+}
+
+void
+flash28f320b3blink(void)
+{
+	addflashcard("Intel28F320B3B", reset);
+}
--- /dev/null
+++ b/os/ipengine/fns.h
@@ -1,0 +1,120 @@
+#include "../port/portfns.h"
+
+void	addpower(Power*);
+void	archbacklight(int);
+void	archconfinit(void);
+int	archconfval(char**, char**, int);
+void	archdisableuart(int);
+void	archdisableusb(void);
+void	archdisablevideo(void);
+void	archenableuart(int, int);
+void	archenableusb(int, int);
+void	archenablevideo(void);
+void	archkbdinit(void);
+void	archresetvideo(void);
+int	archetherenable(int, int*, int*, int, int);
+void	archinit(void);
+int	archoptionsw(void);
+void	archreboot(void);
+void	archsetirxcvr(int);
+uchar*	archvideobuffer(long);
+ulong	baudgen(int, int);
+int	brgalloc(void);
+void	brgfree(int);
+int	cistrcmp(char*, char*);
+int	cistrncmp(char*, char*, int);
+void	clockcheck(void);
+void	clockinit(void);
+void	clockintr(Ureg*);
+void	clrfptrap(void);
+#define	coherence()		/* nothing needed for uniprocessor */
+void	cpminit(void);
+void	cpuidprint(void);
+void	dcflush(void*, ulong);
+void	dcinval(void*, ulong);
+void	delay(int);
+void	dtlbmiss(void);
+void	dumplongs(char*, ulong*, int);
+void	dumpregs(Ureg*);
+void	eieio(void);
+void	faultpower(Ureg*);
+void	firmware(int);
+void	fpinit(void);
+int	fpipower(Ureg*);
+void	fpoff(void);
+void	fprestore(FPU*);
+void	fpsave(FPU*);
+ulong	fpstatus(void);
+char*	getconf(char*);
+ulong	getdar(void);
+ulong	getdec(void);
+ulong	getdepn(void);
+ulong	getdsisr(void);
+ulong	getimmr(void);
+ulong	getmsr(void);
+ulong	getpvr(void);
+ulong	gettbl(void);
+ulong	gettbu(void);
+void	gotopc(ulong);
+void	icflush(void*, ulong);
+void	idle(void);
+#define	idlehands()			/* nothing to do in the runproc */
+void	intr(Ureg*);
+void	intrenable(int, void (*)(Ureg*, void*), void*, int, char*);
+void	intrdisable(int, void (*)(Ureg*, void*), void*, int, char*);
+int	intrstats(char*, int);
+void	intrvec(void);
+int	isaconfig(char*, int, ISAConf*);
+int	isvalid_va(void*);
+void	itlbmiss(void);
+void	kbdinit(void);
+void	kbdreset(void);
+void	lcdpanel(int);
+void	links(void);
+void	mapfree(RMap*, ulong, int);
+void	mapinit(RMap*, Map*, int);
+void	mathinit(void);
+void	mmuinit(void);
+ulong*	mmuwalk(ulong*, ulong, int);
+void	pcmenable(void);
+void	pcmintrenable(int, void (*)(Ureg*, void*), void*);
+int	pcmpin(int slot, int type);
+void	pcmpower(int, int);
+int	pcmpowered(int);
+void	pcmsetvcc(int, int);
+void	pcmsetvpp(int, int);
+void	procsave(Proc*);
+void	procsetup(Proc*);
+void	putdec(ulong);
+void	putmsr(ulong);
+void	puttwb(ulong);
+ulong	rmapalloc(RMap*, ulong, int, int);
+void	screeninit(void);
+int	screenprint(char*, ...);			/* debugging */
+void	(*screenputs)(char*, int);
+int	segflush(void*, ulong);
+void	toggleled(int);
+void	setpanic(void);
+long	spioutin(void*, long, void*);
+void	spireset(void);
+ulong	_tas(ulong*);
+void	trapinit(void);
+void	trapvec(void);
+void	uartinstall(void);
+void	uartspecial(int, int, Queue**, Queue**, int (*)(Queue*, int));
+void	uartwait(void);	/* debugging */
+void	videoreset(void);
+void	videotest(void);
+void	wbflush(void);
+
+#define	waserror()	(up->nerrlab++, setlabel(&up->errlab[up->nerrlab-1]))
+ulong	getcallerpc(void*);
+
+#define KADDR(a)	((void*)((ulong)(a)|KZERO))
+#define PADDR(a)	((((ulong)(a)&KSEGM)!=KSEG0)?(ulong)(a):((ulong)(a)&~KZERO))
+
+/* IBM bit field order */
+#define	IBIT(b)	((ulong)1<<(31-(b)))
+#define	SIBIT(n)	((ushort)1<<(15-(n)))
+
+/* compatibility with inf2.1 */
--- /dev/null
+++ b/os/ipengine/io.h
@@ -1,0 +1,1 @@
+#include "../mpc/800io.h"
--- /dev/null
+++ b/os/ipengine/ipe
@@ -1,0 +1,112 @@
+# Bright Star Engineering ipEngine-1
+dev
+	root
+	cons	archipe
+	env
+	mnt
+	pipe
+	prog
+	rtc
+	srv
+	dup
+	ssl
+	cap
+	sign
+
+	ip	bootp ip ipv6 ipaux iproute arp netlog ptclbsum iprouter plan9 nullmedium pktmedium netaux
+	ether netif netaux ethermedium
+	uart
+	flash
+#	i2c	i2c
+
+	ftl
+
+	fpga
+
+ip
+	il
+	tcp
+	udp
+#	rudp
+#	igmp
+	ipifc
+	icmp
+	icmp6
+	ipmux
+
+lib
+	interp
+	keyring
+	sec
+	mp
+	math
+	kern
+
+link
+	etherscc
+	ethermedium
+	flash28f320b3b
+
+mod
+	math
+	sys
+	keyring
+	crypt
+	ipints
+
+
+port
+	alarm
+	alloc
+	allocb
+	chan
+	dev
+	dial
+	dis
+	discall
+	exception
+	exportfs
+	inferno
+	latin1
+	nocache
+	nodynld
+	parse
+	pgrp
+	print
+	proc
+	qio
+	qlock
+	random
+	sysfile
+	taslock
+	xalloc
+
+code
+	int cflag = 0;
+	int consoleprint = 1;
+	int panicreset = 0;
+	int main_pool_pcnt = 50;
+	int heap_pool_pcnt = 50;
+	int image_pool_pcnt = 0;
+	void	screeninit(void){}
+
+init
+	ipeinit
+
+root
+	/chan	/
+	/dev	/
+	/dis	/
+	/env	/
+	/fd	/
+	/n	/
+	/net	/
+	/nvfs /
+	/prog	/
+	/osinit.dis
+	/dis/lib/auth.dis
+	/dis/lib/ssl.dis
+	/n/local /
+	/n/remote /
+# authentication
+	/nvfs/default	/usr/inferno/keyring/default
--- /dev/null
+++ b/os/ipengine/main.c
@@ -1,0 +1,348 @@
+#include	"u.h"
+#include	"../port/lib.h"
+#include	"mem.h"
+#include	"dat.h"
+#include	"fns.h"
+#include	"io.h"
+#include	"ureg.h"
+#include "version.h"
+
+#define	MAXCONF		32
+
+extern ulong kerndate;
+extern int cflag;
+int	remotedebug;
+
+extern int main_pool_pcnt;
+extern int heap_pool_pcnt;
+extern int image_pool_pcnt;
+
+char *confname[MAXCONF];
+char *confval[MAXCONF];
+int nconf;
+
+void addconf(char *, char *);
+
+static void
+options(void)
+{
+	nconf = archconfval(confname, confval, sizeof(confname));
+}
+
+void
+doc(char *m)
+{
+	USED(m);
+	//iprint("%s...\n", m);
+}
+
+static void
+poolsizeinit(void)
+{
+	ulong nb;
+
+	nb = conf.npage*BY2PG;
+	poolsize(mainmem, (nb*main_pool_pcnt)/100, 0);
+	poolsize(heapmem, (nb*heap_pool_pcnt)/100, 0);
+	poolsize(imagmem, (nb*image_pool_pcnt)/100, 1);
+}
+
+static void
+serialconsole(void)
+{
+	char *p;
+	int port, baud;
+
+	p = getconf("console");
+	if(p == nil)
+		p = "0";
+	if(p != nil && !remotedebug){
+		port = strtol(p, nil, 0);
+		baud = 9600;
+		p = getconf("baud");
+		if(p != nil){
+			baud = strtol(p, nil, 0);
+			if(baud < 9600)
+				baud = 9600;
+		}
+		uartspecial(port, baud, &kbdq, &printq, kbdcr2nl);
+	}
+}
+
+void
+main(void)
+{
+	machinit();
+	options();
+	archinit();
+	quotefmtinstall();
+	confinit();
+	cpminit();
+	xinit();
+	poolsizeinit();
+	trapinit();
+	mmuinit();
+	printinit();
+	uartinstall();
+	serialconsole();
+	doc("screeninit");
+	screeninit();
+	doc("kbdinit");
+	kbdinit();
+	doc("clockinit");
+	clockinit();
+	doc("procinit");
+	procinit();
+	cpuidprint();
+	doc("links");
+	links();
+	doc("chandevreset");
+	chandevreset();
+
+	eve = strdup("inferno");
+
+	print("\nInferno %s\n", VERSION);
+	print("Vita Nuova\n");
+	print("conf %s (%lud) jit %d\n\n",conffile, kerndate, cflag);
+
+	doc("userinit");
+	userinit();
+	doc("schedinit");
+	schedinit();
+}
+
+void
+machinit(void)
+{
+	int n;
+
+	n = m->machno;
+	memset(m, 0, sizeof(Mach));
+	m->machno = n;
+	m->mmask = 1<<m->machno;
+	m->iomem = KADDR(getimmr() & ~0xFFFF);
+	m->cputype = getpvr()>>16;
+	m->delayloop = 20000;	/* initial estimate only; set by clockinit */
+	m->speed = 50;	/* initial estimate only; set by archinit */
+}
+
+void
+init0(void)
+{
+	Osenv *o;
+	int i;
+	char buf[2*KNAMELEN];
+
+	up->nerrlab = 0;
+
+	spllo();
+
+	if(waserror())
+		panic("init0");
+	/*
+	 * These are o.k. because rootinit is null.
+	 * Then early kproc's will have a root and dot.
+	 */
+	o = up->env;
+	o->pgrp->slash = namec("#/", Atodir, 0, 0);
+	cnameclose(o->pgrp->slash->name);
+	o->pgrp->slash->name = newcname("/");
+	o->pgrp->dot = cclone(o->pgrp->slash);
+
+	chandevinit();
+
+	if(!waserror()){
+		ksetenv("cputype", "power", 0);
+		snprint(buf, sizeof(buf), "power %s", conffile);
+		ksetenv("terminal", buf, 0);
+		poperror();
+	}
+	for(i = 0; i < nconf; i++)
+		if(confname[i][0] != '*'){
+			if(!waserror()){
+				ksetenv(confname[i], confval[i], 0);
+				poperror();
+			}
+		}
+
+	poperror();
+	disinit("/osinit.dis");
+}
+
+void
+userinit(void)
+{
+	Proc *p;
+	Osenv *o;
+
+	p = newproc();
+	o = p->env;
+
+	o->fgrp = newfgrp(nil);
+	o->pgrp = newpgrp();
+	o->egrp = newegrp();
+	kstrdup(&o->user, eve);
+
+	strcpy(p->text, "interp");
+
+	/*
+	 * Kernel Stack
+	 */
+	p->sched.pc = (ulong)init0;
+	p->sched.sp = (ulong)p->kstack+KSTACK;
+
+	ready(p);
+}
+
+Conf	conf;
+
+void
+addconf(char *name, char *val)
+{
+	if(nconf >= MAXCONF)
+		return;
+	confname[nconf] = name;
+	confval[nconf] = val;
+	nconf++;
+}
+
+char*
+getconf(char *name)
+{
+	int i;
+
+	for(i = 0; i < nconf; i++)
+		if(cistrcmp(confname[i], name) == 0)
+			return confval[i];
+	return 0;
+}
+
+void
+confinit(void)
+{
+	char *p;
+	int pcnt;
+
+	if(p = getconf("*kernelpercent"))
+		pcnt = 100 - strtol(p, 0, 0);
+	else
+		pcnt = 0;
+
+	conf.nscc = 4;
+	conf.smcuarts = (1<<1)|(1<<0);	/* SMC2, SMC1 (usual console) */
+	conf.sccuarts = 0;	/* SCC2 not available by default (it's Ether) */
+
+	archconfinit();
+	
+	conf.npage = conf.npage0 + conf.npage1;
+	if(pcnt < 10)
+		pcnt = 70;
+	conf.ialloc = (((conf.npage*(100-pcnt))/100)/2)*BY2PG;
+
+	conf.nproc = 100 + ((conf.npage*BY2PG)/MB)*5;
+	conf.nmach = MAXMACH;
+}
+
+void
+exit(int ispanic)
+{
+	up = 0;
+	toggleled(3);
+	spllo();
+	print("cpu %d exiting\n", m->machno);
+
+	/* Shutdown running devices */
+	chandevshutdown();
+
+	delay(1000);
+	splhi();
+	if(ispanic)
+		for(;;);
+	archreboot();
+}
+
+void
+reboot(void)
+{
+	exit(0);
+}
+
+void
+halt(void)
+{
+	print("cpu halted\n");
+	microdelay(1000);
+	for(;;)
+		;
+}
+
+int
+isaconfig(char *class, int ctlrno, ISAConf *isa)
+{
+	char cc[KNAMELEN], *p;
+	int i;
+
+	snprint(cc, sizeof cc, "%s%d", class, ctlrno);
+	p = getconf(cc);
+	if(p == nil)
+		return 0;
+
+	isa->nopt = tokenize(p, isa->opt, NISAOPT);
+	for(i = 0; i < isa->nopt; i++){
+		p = isa->opt[i];
+		if(cistrncmp(p, "type=", 5) == 0)
+			isa->type = p + 5;
+		else if(cistrncmp(p, "port=", 5) == 0)
+			isa->port = strtoul(p+5, &p, 0);
+		else if(cistrncmp(p, "irq=", 4) == 0)
+			isa->irq = strtoul(p+4, &p, 0);
+		else if(cistrncmp(p, "mem=", 4) == 0)
+			isa->mem = strtoul(p+4, &p, 0);
+		else if(cistrncmp(p, "size=", 5) == 0)
+			isa->size = strtoul(p+5, &p, 0);
+		else if(cistrncmp(p, "freq=", 5) == 0)
+			isa->freq = strtoul(p+5, &p, 0);
+		else if(cistrncmp(p, "dma=", 4) == 0)
+			isa->dma = strtoul(p+4, &p, 0);
+	}
+	return 1;
+}
+
+/*
+ *  Save the mach dependent part of the process state.
+ */
+void
+procsave(Proc*)
+{
+}
+
+void
+uartputs(char *s, int n)
+{
+//	screenputs(buf, n);
+	putstrn(s, n);
+	uartwait();
+}
+
+/* stubs */
+void
+setfsr(ulong)
+{
+}
+
+ulong
+getfsr()
+{
+	return 0;
+}
+
+void
+setfcr(ulong)
+{
+}
+
+ulong
+getfcr()
+{
+	return 0;
+}
--- /dev/null
+++ b/os/ipengine/mem.h
@@ -1,0 +1,156 @@
+/*
+ * Memory and machine-specific definitions.  Used in C and assembler.
+ */
+
+/*
+ * Sizes
+ */
+
+#define	BI2BY		8			/* bits per byte */
+#define BI2WD		32			/* bits per word */
+#define	BY2WD		4			/* bytes per word */
+#define	BY2V		8			/* bytes per double word */
+#define	BY2PG		4096			/* bytes per page */
+#define	WD2PG		(BY2PG/BY2WD)		/* words per page */
+#define	PGSHIFT		12			/* log(BY2PG) */
+#define ROUND(s, sz)	(((s)+(sz-1))&~(sz-1))
+#define PGROUND(s)	ROUND(s, BY2PG)
+#define	CACHELINELOG	4
+#define CACHELINESZ	(1<<CACHELINELOG)
+
+#define	MAXMACH		1			/* max # cpus system can run */
+#define	MACHSIZE	BY2PG
+
+/*
+ * Time
+ */
+#define HZ		100			/* clock frequency */
+#define	MS2HZ		(1000/HZ)		/* millisec per clock tick */
+#define	TK2SEC(t)	((t)/HZ)		/* ticks to seconds */
+#define	MS2TK(t)	((t)/MS2HZ)		/* milliseconds to ticks */
+#define	MHz	1000000
+
+/*
+ * MSR bits
+ */
+
+#define	POW	0x40000	/* enable power mgmt */
+#define	TGPR	0x20000	/* GPR0-3 remapped; 603/603e specific */
+#define	ILE	0x10000	/* interrupts little endian */
+#define	EE	0x08000	/* enable external/decrementer interrupts */
+#define	PR	0x04000	/* =1, user mode */
+#define	FPE	0x02000	/* enable floating point */
+#define	ME	0x01000	/* enable machine check exceptions */
+#define	FE0	0x00800
+#define	SE	0x00400	/* single-step trace */
+#define	BE	0x00200	/* branch trace */
+#define	FE1	0x00100
+#define	MSR_IP	0x00040	/* =0, vector to nnnnn; =1, vector to FFFnnnnn */
+#define	IR	0x00020	/* enable instruction address translation */
+#define	DR	0x00010	/* enable data address translation */
+#define	RI	0x00002	/* exception is recoverable */
+#define	LE	0x00001	/* little endian mode */
+
+#define	KMSR	(ME|FE0|FE1|FPE)
+#define	UMSR	(KMSR|PR|EE|IR|DR)
+
+/*
+ * Magic registers
+ */
+
+#define	MACH		30		/* R30 is m-> */
+#define	USER		29		/* R29 is up-> */
+#define	IOMEMR		28		/* R28 will be iomem-> */
+
+/*
+ * Fundamental addresses
+ */
+
+#define	UREGSIZE	((8+32)*4)
+
+/*
+ * MMU
+ */
+
+/* L1 table entry and Mx_TWC flags */
+#define PTEVALID	(1<<0)
+#define PTEWT		(1<<1)	/* write through */
+#define PTE4K		(0<<2)
+#define PTE512K	(1<<2)
+#define PTE8MB	(3<<2)
+#define PTEG		(1<<4)	/* guarded */
+
+/* L2 table entry and Mx_RPN flags (also PTEVALID) */
+#define PTECI		(1<<1)	/*  cache inhibit */
+#define PTESH		(1<<2)	/* page is shared; ASID ignored */
+#define PTELPS		(1<<3)	/* large page size */
+#define PTEWRITE	0x9F0
+
+/* TLB and MxEPN flag */
+#define	TLBVALID	(1<<9)
+
+/*
+ * Address spaces
+ */
+
+#define	KUSEG	0x00000000
+#define KSEG0	0x20000000
+#define	KSEGM	0xE0000000	/* mask to check which seg */
+
+#define	KZERO	KSEG0			/* base of kernel address space */
+#define	KTZERO	(KZERO+0x3000)	/* first address in kernel text */
+#define	KSTACK	8192	/* Size of kernel stack */
+
+/*
+ * Exception codes (trap vectors)
+ */
+#define	CRESET	0x01
+#define	CMCHECK 0x02
+#define	CDSI	0x03
+#define	CISI	0x04
+#define	CEI	0x05
+#define	CALIGN	0x06
+#define	CPROG	0x07
+#define	CFPU	0x08
+#define	CDEC	0x09
+#define	CSYSCALL 0x0C
+#define	CTRACE	0x0D
+#define	CFPA	0x0E
+/* rest are power-implementation dependent (8xx) */
+#define	CEMU	0x10
+#define	CIMISS	0x11
+#define	CDMISS	0x12
+#define	CITLBE	0x13
+#define	CDTLBE	0x14
+#define	CDBREAK	0x1C
+#define	CIBREAK	0x1D
+#define	CPBREAK	0x1E
+#define	CDPORT	0x1F
+
+/*
+ * MPC8xx physical addresses (ipEngine)
+ */
+#define	PHYSDRAM	0x00000000
+#define	PHYSIMM	0xFF000000
+#define	PHYSFLASH	0xFE000000
+#define	FPGAMEM	0xFC000000
+
+/* extra addresses on ipEngine-1 */
+#define	FPGACR	0xFF010000	/* FPGA control */
+#define	CLOCKCR	0xFF020000	/* Clock synthesiser register */
+
+/* remaining ones are our choice */
+#define	PHYSPCMCIA	0x40000000
+#define	PCMCIALEN	(8*MB)	/* chosen to allow mapping by single TLB entry */
+#define	ISAIO	PHYSPCMCIA	/* for inb.s */
+
+/*
+ * MPC8xx dual-ported CPM memory physical addresses
+ */
+#define	PHYSDPRAM	(PHYSIMM+0x2000)
+#define	DPLEN1	0x200
+#define	DPLEN2	0x400
+#define	DPLEN3	0x800
+#define	DPBASE	(PHYSDPRAM+DPLEN1)
+
+#define KEEP_ALIVE_KEY 0x55ccaa33	/* clock and rtc register key */
--- /dev/null
+++ b/os/ipengine/mkfile
@@ -1,0 +1,105 @@
+SYSTARG=Inferno
+OBJTYPE=power
+<../../mkconfig
+
+#Configurable parameters
+
+CONF=ipe			#default configuration
+CONFLIST=ipe
+KZERO=0x20003020
+
+SYSTARG=$OSTARG
+OBJTYPE=power
+INSTALLDIR=$ROOT/Inferno/$OBJTYPE/bin	#path of directory where kernel is installed
+#INSTALLDIR=/$OBJTYPE
+
+#end configurable parameters
+
+<$ROOT/mkfiles/mkfile-$SYSTARG-$OBJTYPE	#set vars based on target system
+
+<| $SHELLNAME ../port/mkdevlist $CONF	#sets $IP, $DEVS, $ETHERS, $VGAS, $PORT, $MISC, $LIBS, $OTHERS
+
+OBJ=\
+	l.$O\
+	tlb.$O\
+	nofp.$O\
+	clock.$O\
+	cpm.$O\
+	faultpower.$O\
+	fpi.$O\
+	fpimem.$O\
+	fpipower.$O\
+	kbd.$O\
+	main.$O\
+	mmu.$O\
+	rmap.$O\
+	trap.$O\
+	$CONF.root.$O\
+	$IP\
+	$DEVS\
+	$ETHERS\
+	$LINKS\
+	$VGAS\
+	$PORT\
+	$MISC\
+	$OTHERS\
+
+LIBNAMES=${LIBS:%=lib%.a}
+
+HFILES=\
+	mem.h\
+	dat.h\
+	fns.h\
+	io.h\
+	../mpc/800io.h\
+
+CFLAGS=-wFV -I. -I../mpc -I../port -I$ROOT/Inferno/$OBJTYPE/include -I$ROOT/include -I$ROOT/libinterp
+KERNDATE=`{$NDATE}
+
+#default:V: i$CONF.sq
+default:V:	i$CONF
+
+i$CONF:	$OBJ $CONF.c $CONF.root.h $LIBNAMES
+	$CC $CFLAGS '-DKERNDATE='$KERNDATE $CONF.c
+	$LD -o $target -T$KZERO -l -R4 $OBJ $CONF.$O $LIBFILES
+	$KSIZE $target
+
+i$CONF.sq:	i$CONF
+	sqz -w i$CONF >$target
+
+install:V: i$CONF # i$CONF.sq
+	cp i$CONF $INSTALLDIR/i$CONF
+	#cp i$CONF.sq $INSTALLDIR/i$CONF.sq
+
+uninstall:V:
+	rm -f $ROOT/$OBJDIR/bin/i$CONF
+	rm -f $ROOT/$OBJDIR/bin/i$CONF.sq
+
+<../port/portmkfile
+
+%.$O:		../mpc/%.c
+		$CC $CFLAGS -I. ../mpc/$stem.c
+
+%.$O:		../mpc/%.s
+	$AS -I. -I../mpc ../mpc/$stem.s
+
+../init/$INIT.dis:	../init/$INIT.b
+		cd ../init; mk $INIT.dis
+
+clock.$O:	$ROOT/Inferno/$OBJTYPE/include/ureg.h
+devether.$O:	$ROOT/Inferno/$OBJTYPE/include/ureg.h
+faultpower.$O:	$ROOT/Inferno/$OBJTYPE/include/ureg.h
+main.$O:	$ROOT/Inferno/$OBJTYPE/include/ureg.h
+trap.$O:	$ROOT/Inferno/$OBJTYPE/include/ureg.h
+
+devether.$O $ETHERS:	../mpc/etherif.h ../port/netif.h
+archipe.$O:	../mpc/screen.h archipe.h
+screen.$O:	../mpc/screen.h
+
+$IP devip.$O:		../ip/ip.h
+
+devboot.$O:	devboot.c
+	$CC $CFLAGS devboot.c
+
+devuart.$O:	../mpc/devuart.c
+	$CC $CFLAGS ../mpc/devuart.c
--- /dev/null
+++ b/os/ipengine/mmu.c
@@ -1,0 +1,20 @@
+#include	"u.h"
+#include	"../port/lib.h"
+#include	"mem.h"
+#include	"dat.h"
+#include	"fns.h"
+
+void
+mmuinit(void)
+{
+	/* the l.s initial TLB settings do all that's required */
+}
+
+int
+segflush(void *a, ulong n)
+{
+	/* flush dcache then invalidate icache */
+	dcflush(a, n);
+	icflush(a, n);
+	return 0;
+}
--- /dev/null
+++ b/os/ipengine/tlb.s
@@ -1,0 +1,21 @@
+#include	"mem.h"
+#define	MB	(1024*1024)
+
+/*
+ * TLB prototype entries, loaded once-for-all at startup,
+ * remaining unchanged thereafter.
+ * Limit the table to at most 8 entries to ensure
+ * it works on the 823 (other 8xx processors allow up to 32 TLB entries).
+ */
+#define	TLBE(epn,rpn,twc)	WORD	$(epn);  WORD	$(twc); WORD	$(rpn)
+
+TEXT	tlbtab(SB), $-4
+
+	/* epn, rpn, twc */
+	TLBE(KZERO|PHYSDRAM|TLBVALID, PHYSDRAM|PTEWRITE|PTELPS|PTESH|PTEVALID, PTE8MB|PTEVALID)	/* DRAM, 8M */
+	TLBE(KZERO|(PHYSDRAM+8*MB)|TLBVALID, (PHYSDRAM+8*MB)|PTEWRITE|PTELPS|PTESH|PTEVALID, PTE8MB|PTEVALID)	/* DRAM, 8M */
+	TLBE(KZERO|PHYSIMM|TLBVALID, PHYSIMM|PTEWRITE|PTELPS|PTESH|PTECI|PTEVALID, PTE512K|PTEVALID)	/* IMMR, 512K (includes FPGA control and clock synth) */
+	TLBE(KZERO|PHYSFLASH|TLBVALID, PHYSFLASH|PTEWRITE|PTELPS|PTESH|PTECI|PTEVALID, PTE8MB|PTEWT|PTEVALID)	/* Flash, 8M */
+	TLBE(KZERO|FPGAMEM|TLBVALID, FPGAMEM|PTEWRITE|PTELPS|PTESH|PTECI|PTEVALID, PTE8MB|PTEG|PTEVALID)	/* FPGA mem, 8M */
+TEXT	tlbtabe(SB), $-4
+	RETURN
--- /dev/null
+++ b/os/js/README
@@ -1,0 +1,10 @@
+This port was done in a matter of days by Tad Hunt, Eric van Hensbergen
+and others when they were in the Lucent Inferno Business Unit, with a little
+bit of advice from Vita Nuova about various Sparc details (since one
+of us had done ports to micro-Sparcs).  It is included mainly to
+allow another micro-Sparc port to be developed.   Javastation-1s
+can still be found (but we haven't got one).  A good cheap reference
+board would be good to have.
+
+The Javastation running Java and JavaOS was sluggish; running Limbo
+under Inferno it was actually quite snappy!
--- /dev/null
+++ b/os/js/audio.h
@@ -1,0 +1,9 @@
+enum
+{
+	Bufsize		= 16*1024,	/* 92 ms each */
+	Nbuf		= 16,		/* 1.5 seconds total */
+	Rdma		= 666,		/* XXX - Tad: fixme */
+	Wdma		= 666,		/* XXX - Tad: fixme */
+};
+
+#define UNCACHED(type, v)	(type*)((ulong)(v))
--- /dev/null
+++ b/os/js/clock.c
@@ -1,0 +1,104 @@
+#include	"u.h"
+#include	"../port/lib.h"
+#include	"mem.h"
+#include	"dat.h"
+#include	"fns.h"
+#include	"io.h"
+
+#include	"ureg.h"
+
+typedef struct Clock0link Clock0link;
+typedef struct Clock0link {
+	void		(*clock)(void);
+	Clock0link*	link;
+} Clock0link;
+
+static Clock0link *clock0link;
+static Lock clock0lock;
+
+void
+microdelay(int ms)
+{
+	int i;
+
+	ms *= 13334;		/* experimentally indetermined */
+	for(i=0; i<ms; i++)
+		;
+}
+
+typedef struct Ctr Ctr;
+struct Ctr
+{
+	ulong	lim;
+	ulong	ctr;
+	ulong	limnr;	/* non-resetting */
+	ulong	ctl;
+};
+Ctr	*ctr;
+
+void
+clockinit(void)
+{
+	KMap *k;
+
+	putphys(TIMECONFIG, 0);	/* it's a processor counter */
+	k = kmappa(CLOCK, PTENOCACHE|PTEIO);
+	ctr = (Ctr*)VA(k);
+	ctr->lim = (CLOCKFREQ/HZ)<<10;
+}
+
+void
+clock(Ureg *ur)
+{
+	Clock0link *lp;
+	ulong i;
+
+	USED(ur);
+
+	i = ctr->lim;	/* clear interrupt */
+	USED(i);
+	 /* is this needed? page 6-43 801-3137-10 suggests so */
+	ctr->lim = (CLOCKFREQ/HZ)<<10;
+
+	m->ticks++;
+
+	if(up)
+		up->pc = ur->pc;
+
+	checkalarms();
+
+	lock(&clock0lock);
+	for(lp = clock0link; lp; lp = lp->link)
+		lp->clock();
+	unlock(&clock0lock);
+
+	if(up && up->state == Running) {
+		if(anyready())
+			sched();
+	}
+}
+
+Timer*
+addclock0link(void (*clockfunc)(void), int)
+{
+	Clock0link *lp;
+
+	if((lp = malloc(sizeof(Clock0link))) == 0){
+		print("addclock0link: too many links\n");
+		return nil;
+	}
+	ilock(&clock0lock);
+	lp->clock = clockfunc;
+	lp->link = clock0link;
+	clock0link = lp;
+	iunlock(&clock0lock);
+	return nil;
+}
+
+uvlong
+fastticks(uvlong *hz)
+{
+	if(hz)
+		*hz = HZ;
+	return m->ticks;
+}
--- /dev/null
+++ b/os/js/cs4231.h
@@ -1,0 +1,20 @@
+#define IN(x)		inb(csdev.port+(x))
+#define OUT(x,v)	outb(csdev.port+(x),(v))
+
+void
+cs4231install(void)
+{
+	KMap *k;
+	static int installed=0;
+
+	if(installed)
+		return;
+
+	k = kmappa(AUDIO_PHYS_PAGE, PTEIO|PTENOCACHE);
+
+	csdev.port = VA(k)+AUDIO_INDEX_OFFSET;
+	dmasize(Wdma, 8);
+	dmasize(Rdma, 8);
+
+	installed=1;
+}
--- /dev/null
+++ b/os/js/dat.h
@@ -1,0 +1,163 @@
+typedef struct Conf	Conf;
+typedef struct FPenv	FPenv;
+typedef struct FPU	FPU;
+typedef struct Label	Label;
+typedef struct Lock	Lock;
+typedef struct Mach	Mach;
+typedef struct Ureg	Ureg;
+typedef struct Lance	Lance;
+typedef struct Lancemem	Lancemem;
+typedef struct Etherpkt	Etherpkt;
+typedef struct Lancepkt	Lancepkt;
+
+typedef	ulong	Instr;
+
+struct Conf
+{
+	int	nmach;		/* processors */
+	int	nproc;		/* processes */
+	ulong	monitor;	/* graphics monitor id; 0 for none */
+	char	ss2;		/* is a sparcstation 2 */
+	char	ss2cachebug;	/* has sparcstation2 cache bug */
+	int	ncontext;	/* in mmu */
+	int	vacsize;	/* size of virtual address cache, in bytes */
+	int	vaclinesize;	/* size of cache line */
+	ulong	npage0;		/* total physical pages of memory, bank 0 */
+	ulong	npage1;		/* total physical pages of memory, bank 1 */
+	ulong	base0;		/* base of bank 0 */
+	ulong	base1;		/* base of bank 1 */
+	ulong	ialloc;		/* max interrupt time allocation in bytes */
+	ulong	npage;		/* total physical pages of memory */
+	int	copymode;	/* 0 is copy on write, 1 is copy on reference */
+	ulong	ipif;		/* Ip protocol interfaces */
+	ulong	ip;		/* Ip conversations per interface */
+	ulong	arp;		/* Arp table size */
+	ulong	frag;		/* Ip fragment assemble queue size */
+};
+
+
+/*
+ * FPenv.status
+ */
+enum
+{
+	FPINIT,
+	FPACTIVE,
+	FPINACTIVE,
+};
+
+struct	FPenv
+{
+	ulong	status;
+	ulong	pad;
+};
+
+/*
+ * This structure must agree with fpsave and fprestore asm routines
+ */
+struct	FPU
+{
+
+	double	regs[17];	/* floating point registers */
+	FPenv	env;
+};
+
+/*
+ *  machine dependent definitions used by ../port/dat.h
+ */
+
+struct Label
+{
+	ulong	sp;
+	ulong	pc;
+};
+
+struct Lock
+{
+	ulong	key;
+	ulong	pc;
+	ulong	sr;
+	int	pri;
+};
+
+#include "../port/portdat.h"
+
+/*
+ *  machine dependent definitions not used by ../port/dat.h
+ */
+
+struct Mach
+{
+	ulong	ticks;			/* of the clock since boot time */
+	int		machno;			/* physical id of this processor */
+	Proc	*proc;			/* current process on this processor */
+	Label	sched;			/* scheduler wakeup */
+	Lock	alarmlock;		/* access to alarm list */
+	void	*alarm;			/* alarms bound to this clock */
+	ulong	*contexts;		/* hardware context table */
+	ulong	*ctx;			/* the context */
+	int	fptrap;			/* FP trap occurred while unsave */
+
+	int	nrdy;
+
+	int	stack[1];
+};
+
+/*
+ * XXX - Eric: It just works....
+ */
+
+/*
+ *  LANCE CSR3 (bus control bits)
+ */
+#define BSWP	0x4
+#define ACON	0x2
+#define BCON	0x1
+
+struct Lancepkt
+{
+	uchar	d[6];
+	uchar	s[6];
+	uchar	type[2];
+	uchar	data[1500];
+	uchar	crc[4];
+};
+
+/*
+ *  system dependent lance stuff
+ *  filled by lancesetup() 
+ */
+struct Lance
+{
+	ushort	lognrrb;	/* log2 number of receive ring buffers */
+	ushort	logntrb;	/* log2 number of xmit ring buffers */
+	ushort	nrrb;		/* number of receive ring buffers */
+	ushort	ntrb;		/* number of xmit ring buffers */
+	ushort	*rap;		/* lance address register */
+	ushort	*rdp;		/* lance data register */
+	ushort	busctl;		/* bus control bits */
+	uchar	ea[6];		/* our ether addr */
+	int	sep;		/* separation between shorts in lance ram
+				    as seen by host */
+	ushort	*lanceram;	/* start of lance ram as seen by host */
+	Lancemem *lm;		/* start of lance ram as seen by lance */
+	Lancepkt *rp;		/* receive buffers (host address) */
+	Lancepkt *tp;		/* transmit buffers (host address) */
+	Lancepkt *lrp;		/* receive buffers (lance address) */
+	Lancepkt *ltp;		/* transmit buffers (lance address) */
+};
+
+/*
+ * Fake kmap
+ */
+typedef void		KMap;
+#define	VA(k)		((ulong)(k))
+#define	kmap(p)		(KMap*)((p)->pa|KZERO)
+#define	kunmap(k)
+#define	MACHP(n)	(n==0? &mach0 : *(Mach**)0)
+
+extern Mach *m;
+extern Proc *up;
+extern Mach mach0;
+
+#define	swcursor	1
--- /dev/null
+++ b/os/js/devcs4231.c
@@ -1,0 +1,1186 @@
+/*
+ * preliminary Crystal CS4231 audio driver,
+ * initially based on SB16 driver, and therefore needs work.
+ * for instance, i suspect the linked-list buffering is excessive:
+ * a rolling buffering scheme or double buffering should be fine,
+ * and possibly simpler.
+ *
+ * To do:
+ *	stop/start?
+ *	is the linux mix_cvt ideal?
+ *	ad1845 differences
+ *	adpcm freezing
+ */
+
+#include	"u.h"
+#include	"../port/lib.h"
+#include	"mem.h"
+#include	"dat.h"
+#include	"fns.h"
+#include	"../port/error.h"
+#include	"devtab.h"
+#include	"io.h"
+#include	"audio.h"
+
+#define	DPRINT	if(chatty)print
+
+typedef struct	AChan	AChan;
+typedef struct	AQueue	AQueue;
+typedef struct	Buf	Buf;
+typedef struct	Vol	Vol;
+
+enum
+{
+	Qdir		= 0,
+	Qaudio,
+	Qaudioctl,
+
+	Fmono		= 1,
+	Fin		= 2,
+	Fout		= 4,
+
+	Vaudio		= 0,
+	Vaux1,
+	Vaux2,
+	Vline,
+	Vmic,
+	Vmono,
+	Vspeed,
+	Vchans,
+	Vbits,
+	Nvol,
+
+	Speed		= 22050,
+	Ncmd		= 50,		/* max volume command words */
+};
+
+enum {
+	Paddr=	0,
+		TRD=	1<<5,
+		MCE=	1<<6,
+	Pdata=	1,
+	Pstatus=	2,
+	Pio=		3,
+
+	LeftADC=	0,
+		MGE=	1<<5,
+		ISline=	0<<6,
+		ISaux1=	1<<6,
+		ISmic=	2<<6,
+		ISloop=	3<<6,
+		ISmask=	3<<6,
+	RightADC= 1,
+	LeftAux1= 2,
+		Mute=	1<<7,
+	RightAux1= 3,
+	LeftAux2= 4,
+	RightAux2= 5,
+	LeftDAC= 6,
+	RightDAC= 7,
+	OutFormat=	8,
+		Stereo=	1<<4,
+		Linear8=	0<<5,
+		uLaw=	1<<5,
+		Linear16=	2<<5,
+		aLaw=	3<<5,
+		ADPCM=	5<<5,
+		Fmask=	7<<5,
+	Config=	9,
+		PEN=	1<<0,
+		CEN=	1<<1,
+		Nocal=	0<<3,
+		Convcal=	1<<3,
+		DACcal=	2<<3,
+		Fullcal=	3<<3,
+	PinControl=	10,
+		IEN=		1<<1,
+		DEN=	1<<3,
+		Xctl0=	1<<6,
+		Xctl1=	1<<7,
+	Status=	11,
+		ACI=		1<<5,
+	Mode=	12,
+		Mode2=	1<<6,
+	Loopback=	13,
+		LBE=		1<<0,
+	PlayCount1=	14,
+	PlayCount0=	15,
+	Feature1=	16,
+		PMCE=	1<<4,
+		CMCE=	1<<5,
+	Feature2=	17,
+	LeftLine=	18,
+	RightLine=	19,
+	Timer0=	20,
+	Timer1=	21,
+	Feature3=	23,
+	FeatureStatus=	24,
+		PI=	1<<4,	/* playback interrupt */
+		CI=	1<<5,	/* capture interrupt */
+		TI=	1<<6,	/* timer interrupt */
+	ChipID= 25,
+	MonoCtl=	26,
+		MBY=	1<<5,	/* mono bypass */
+		MOM=	1<<6,
+	InFormat=	28,
+	RecCount1=	30,
+	RecCount0=	31,
+};
+
+#define	csdelay()	microdelay(1)
+
+static Dirtab audiodir[] =
+{
+	"audio",	{Qaudio},		0,	0666,
+	"audioctl",	{Qaudioctl},		0,	0666,
+};
+#define	NPORT		(sizeof audiodir/sizeof(Dirtab))
+
+struct Buf
+{
+	uchar*	virt;
+	int	count;
+	Buf*	next;
+};
+struct AQueue
+{
+	Lock;
+	Buf*	first;
+	Buf*	last;
+};
+struct AChan
+{
+	QLock;
+	Rendez	r;
+	Buf	buf[Nbuf];	/* buffers and queues */
+	AQueue	empty;
+	AQueue	full;
+	Buf*	current;
+	Buf*	filling;
+	int	flushing;
+};
+static struct
+{
+	QLock;
+	int	opened;
+	int	bufinit;	/* boolean if buffers allocated */
+	int	rivol[Nvol];		/* right/left input/output volumes */
+	int	livol[Nvol];
+	int	rovol[Nvol];
+	int	lovol[Nvol];
+	int	loopback;
+
+	AChan	in;
+	AChan	out;
+} audio;
+
+static	char*	encname(int);
+
+static	int	dacload(int, int);
+static	int	auxload(int, int);
+static	int	adcload(int, int);
+static	int	monoload(int, int);
+
+struct Vol
+{
+	char*	name;
+	int	flag;
+	int	ilval;		/* initial values */
+	int	irval;
+	int	reg;
+	int	(*load)(int, int);
+};
+
+static	Vol	volumes[] = {
+[Vaudio]	{"audio",	    Fout, 	50,	50,	LeftDAC, dacload},
+[Vaux1]		{"aux1",		Fin,	0,	0,	LeftAux1, auxload},
+[Vaux2]		{"aux2",		Fin,	0,	0,	LeftAux2, auxload},
+[Vline]		{"line",		Fin,	0,	0,	LeftLine, auxload},
+[Vmono]		{"mono",		Fin|Fout|Fmono,	0,	0,	MonoCtl, monoload},
+[Vmic]		{"mic",		Fin,	0,	0,	LeftADC, adcload},
+
+[Vspeed]	{"rate",	Fin|Fout|Fmono,	Speed,	Speed,},
+[Vchans]	{"chans",	Fin|Fout|Fmono,	2,	2,},
+[Vbits]	{"bits", Fin|Fout|Fmono, 8, 8,},
+	{0},
+};
+
+static struct
+{
+	Lock;
+	int	port;
+	int	irq;
+	uchar	sticky;
+	uchar	regs[32];
+} csdev;
+
+static	void	contininput(void);
+static	void	continoutput(void);
+
+static	char	Evolume[]	= "illegal audioctl specifier";
+
+static	int	chatty;
+
+#include "cs4231.h"
+
+static int
+xin(int r)
+{
+	int i;
+
+	for(i=100; --i >= 0 && IN(Paddr) & 0x80;)
+		csdelay();
+	OUT(Paddr, r|csdev.sticky);
+	csdelay();
+	return IN(Pdata);
+}
+
+static void
+xout(int r, int v)
+{
+	int i;
+
+	for(i=100; --i >= 0 && IN(Paddr) & 0x80;)
+		csdelay();
+	OUT(Paddr, r|csdev.sticky);
+	csdelay();
+	OUT(Pdata, v);
+	//csdelay();
+}
+
+static void
+speaker(int on)
+{
+	int s;
+
+	s = xin(PinControl);
+	if(on)
+		s |= Xctl0;
+	else
+		s &= ~Xctl0;
+	xout(PinControl, s);
+}
+
+static Buf*
+getbuf(AQueue *q)
+{
+	Buf *b;
+
+	ilock(q);
+	b = q->first;
+	if(b)
+		q->first = b->next;
+	iunlock(q);
+
+	return b;
+}
+
+static void
+putbuf(AQueue *q, Buf *b)
+{
+	ilock(q);
+	b->next = 0;
+	if(q->first)
+		q->last->next = b;
+	else
+		q->first = b;
+	q->last = b;
+	iunlock(q);
+}
+
+static void
+achanreset(AChan *ac)
+{
+	int i;
+
+	ac->filling = 0;
+	ac->flushing = 0;
+	ac->current = 0;
+	ac->empty.first = 0;
+	ac->empty.last = 0;
+	ac->full.first = 0;
+	ac->full.last = 0;
+	for(i=0; i<Nbuf; i++){
+		ac->buf[i].count = 0;
+		putbuf(&ac->empty, &ac->buf[i]);
+	}
+}
+
+static void
+startoutput(void)
+{
+	ilock(&csdev);
+	if(audio.out.current == 0)
+		continoutput();
+	iunlock(&csdev);
+}
+
+static void
+continoutput(void)
+{
+	Buf *b;
+	int f;
+	ulong n;
+
+	b = getbuf(&audio.out.full);
+	audio.out.current = b;
+	//xout(Config, xin(Config)&~PEN);
+	if(b){
+		n = b->count;
+		dmasetup(Wdma, b->virt, n, 0);
+		f = xin(OutFormat);
+		if((f & Fmask) == ADPCM)
+			n >>= 2;
+		else{
+			if((f & Fmask) == Linear16)
+				n >>= 1;
+			if(f & Stereo)
+				n >>= 1;
+		}
+		n--;
+		xout(PlayCount0, n);
+		xout(PlayCount1, n>>8);
+		xout(Config, xin(Config)|PEN);
+		DPRINT("cs: out %d\n", n);
+	} else
+		xout(Config, xin(Config)&~PEN);
+}
+
+static void
+startinput(void)
+{
+	ilock(&csdev);
+	if(audio.in.current == 0)
+		contininput();
+	iunlock(&csdev);
+}
+
+static void
+contininput(void)
+{
+	Buf *b;
+	int f;
+	ulong n;
+
+	xout(Config, xin(Config)&~CEN);
+	if(!audio.opened || audio.in.flushing){
+		return;
+	}
+	b = getbuf(&audio.in.empty);
+	audio.in.current = b;
+	if(b){
+		n = Bufsize;
+		dmasetup(Rdma, b->virt, Bufsize, 1);
+		f = xin(InFormat);
+		if((f & Fmask) == ADPCM)
+			n >>= 2;
+		else{
+			if((f & Fmask) == Linear16)
+				n >>= 1;
+			if(f & Stereo)
+				n >>= 1;
+		}
+		n--;
+		xout(RecCount0, n);
+		xout(RecCount1, n>>8);
+		xout(Config, xin(Config)|CEN);
+		DPRINT("cs: in %d\n", n);
+	}
+}
+
+static void
+cswait(void)
+{
+	int i;
+
+	for(i=50; --i >= 0 && IN(Paddr) & 0x80;)
+		microdelay(2000);
+	if(i < 0)
+		print("cswait1\n");
+	for(i=1000; --i >= 0 && (xin(Status) & ACI) == 0;)
+		csdelay();
+	for(i=1000; --i >= 0 && xin(Status) & ACI;)
+		microdelay(2000);
+	/* could give error(Eio) if i < 0 */
+	if(i < 0)
+		print("cswait2\n");
+}
+
+static int
+csspeed(int freq)
+{
+	int i;
+	static int freqtab[] = {	/* p. 33 CFS2-CFS0 */
+		/* xtal1 xtal2 */
+		8000, 5510,
+		16000, 11025,
+		27420, 18900,
+		32000, 22050,
+		0, 37800,
+		0, 44100,
+		48000, 33075,
+		9600, 6620,
+	};
+	for(i=0; i<16; i++)
+		if(freqtab[i] == freq){
+			xout(OutFormat, (xin(OutFormat)&~0xF) | i);
+			return 1;
+		}
+	return 0;
+}
+
+static void
+csformat(int r, int flag, int form, int *vec)
+{
+	int v;
+
+	if(form == Linear8){
+		if(vec[Vbits] == 16)
+			form = Linear16;
+		else if(vec[Vbits] == 4)
+			form = ADPCM;
+	}
+	if(vec[Vchans] == 2)
+		form |= Stereo;
+	DPRINT("csformat(%x,%x,%x)\n", r, flag, form);
+	if((xin(r)&0xF0) != form){
+		v = xin(Feature1);
+		xout(Feature1, v|flag);
+		xout(r, (xin(r)&~0xF0)|form);
+		xout(Feature1, v);
+	}
+	csdev.regs[r] = form;
+}
+
+static void
+cs4231intr(Ureg*, void*)
+{
+	int ir, s;
+	Buf *b;
+
+	lock(&csdev);
+	csdev.sticky |= TRD;
+	ir = IN(Pstatus);
+	s = xin(FeatureStatus);
+	if(s & PI){
+		b = audio.out.current;
+		audio.out.current = 0;
+		dmaend(Wdma);
+		continoutput();
+		if(b)
+			putbuf(&audio.out.empty, b);
+		wakeup(&audio.out.r);
+	}
+	if(s & CI){
+		b = audio.in.current;
+		audio.in.current = 0;
+		dmaend(Rdma);
+		contininput();
+		if(b){
+			b->count = Bufsize;
+			putbuf(&audio.in.full, b);
+		}
+		wakeup(&audio.in.r);
+	}
+	OUT(Pstatus, 0);
+	csdev.sticky &= ~TRD;
+	unlock(&csdev);
+	if(s & 0xF)
+		DPRINT("audiointr: #%x\n", s);
+}
+
+static int
+anybuf(void *p)
+{
+	return ((AChan*)p)->empty.first != 0;
+}
+
+static int
+anyinput(void *p)
+{
+	return ((AChan*)p)->full.first != 0;
+}
+
+static int
+outcomplete(void *p)
+{
+	return ((AChan*)p)->full.first == 0 && ((AChan*)p)->current==0;
+}
+
+static int
+incomplete(void *p)
+{
+	return ((AChan*)p)->current == 0;
+}
+
+static void
+acbufinit(AChan *ac)
+{
+	int i;
+	void *p;
+
+	for(i=0; i<Nbuf; i++) {
+		//p = xspanalloc(Bufsize, CACHELINESZ, 64*1024);
+		//dcflush(p, Bufsize);
+		p = xalloc(Bufsize);
+		ac->buf[i].virt = UNCACHED(uchar, p);
+	}
+}
+
+static void
+setempty(void)
+{
+	ilock(&csdev);
+	achanreset(&audio.in);
+	achanreset(&audio.out);
+	iunlock(&csdev);
+}
+
+void
+cs4231reset(void)
+{
+}
+
+static char mix_cvt[101] = {
+	0, 0,3,7,10,13,16,19,21,23,26,28,30,32,34,35,37,39,40,42,
+	43,45,46,47,49,50,51,52,53,55,56,57,58,59,60,61,62,63,64,65,
+	65,66,67,68,69,70,70,71,72,73,73,74,75,75,76,77,77,78,79,79,
+	80,81,81,82,82,83,84,84,85,85,86,86,87,87,88,88,89,89,90,90,
+	91,91,92,92,93,93,94,94,95,95,96,96,96,97,97,98,98,98,99,99,
+	100
+};
+
+static int
+dacload(int r, int v)
+{
+	USED(r);
+	DPRINT("dacload(%x,%d)\n", r, v);
+	if(v == 0)
+		return Mute;
+	return 63-((v*63)/100);
+}
+
+static int
+monoload(int r, int v)
+{
+	DPRINT("monoload(%x,%d)\n", r, v);
+	if(v == 0)
+		return r|Mute;
+	return (r&~(Mute|MBY))|(15-((v*15)/100));
+}
+
+static int
+auxload(int r, int v)
+{
+	DPRINT("auxload(%x,%d)\n", r, v);
+	USED(r);
+	if(v == 0)
+		return Mute;
+	return 31-(v*31)/100;
+}
+
+static int
+adcload(int r, int v)
+{
+	DPRINT("adcload(%x,%d)\n", r, v);
+	return (r&~0xF)|((v*15)/100)|MGE;
+}
+
+static void
+mxvolume(void)
+{
+	Vol *v;
+	int i, l, r;
+
+	ilock(&csdev);
+	speaker(0);
+	for(i =0; volumes[i].name; i++){
+		v = &volumes[i];
+		if(v->load == 0)
+			continue;
+		if(v->flag & Fin){
+			l = audio.livol[i];
+			r = audio.rivol[i];
+		} else {
+			l = audio.lovol[i];
+			r = audio.rovol[i];
+		}
+		if(l < 0)
+			l = 0;
+		if(r < 0)
+			r = 0;
+		if(l > 100)
+			l = 100;
+		if(r > 100)
+			r = 100;
+		l = mix_cvt[l];
+		r = mix_cvt[r];
+		if((v->flag & Fmono) == 0){
+			xout(v->reg, (*v->load)(xin(v->reg), l));
+			xout(v->reg+1, (*v->load)(xin(v->reg+1), r));
+		} else
+			xout(v->reg, (*v->load)(xin(v->reg), l));
+	}
+	xout(LeftADC, (xin(LeftADC)&~ISmask)|csdev.regs[LeftADC]);
+	xout(RightADC, (xin(RightADC)&~ISmask)|csdev.regs[RightADC]);
+	if(audio.loopback)
+		xout(Loopback, xin(Loopback)|LBE);
+	else
+		xout(Loopback, xin(Loopback)&~LBE);
+	csformat(InFormat, CMCE, csdev.regs[InFormat], audio.livol);
+	csformat(OutFormat, PMCE, csdev.regs[OutFormat], audio.lovol);
+	if(audio.lovol[Vaudio] || audio.rovol[Vaudio])
+		speaker(1);
+	iunlock(&csdev);
+}
+
+static void
+flushinput(void)
+{
+	Buf *b;
+
+	ilock(&csdev);
+	audio.in.flushing = 1;
+	iunlock(&csdev);
+	qlock(&audio.in);
+	if(waserror()){
+		qunlock(&audio.in);
+		nexterror();
+	}
+	sleep(&audio.in.r, incomplete, &audio.in);
+	qunlock(&audio.in);
+	poperror();
+	ilock(&csdev);
+	audio.in.flushing = 0;
+	iunlock(&csdev);
+	if((b = audio.in.filling) != 0){
+		audio.in.filling = 0;
+		putbuf(&audio.in.empty, b);
+	}
+	while((b = getbuf(&audio.in.full)) != 0)
+		putbuf(&audio.in.empty, b);
+}
+
+static void
+waitoutput(void)
+{
+	qlock(&audio.out);
+	if(waserror()){
+		qunlock(&audio.out);
+		nexterror();
+	}
+	startoutput();
+	while(!outcomplete(&audio.out))
+		sleep(&audio.out.r, outcomplete, &audio.out);
+	qunlock(&audio.out);
+	poperror();
+}
+
+static	void
+resetlevel(void)
+{
+	int i;
+
+	for(i=0; volumes[i].name; i++) {
+		audio.lovol[i] = volumes[i].ilval;
+		audio.rovol[i] = volumes[i].irval;
+		audio.livol[i] = volumes[i].ilval;
+		audio.rivol[i] = volumes[i].irval;
+	}
+}
+
+void
+cs4231init(void)
+{
+	cs4231install();
+
+	csdev.regs[LeftADC] = ISmic;
+	csdev.regs[RightADC] = ISmic;
+	dmasize(Wdma, 8);
+	dmasize(Rdma, 8);
+	csdev.sticky = 0;
+	OUT(Paddr, Mode);
+	csdelay();
+	if((IN(Pdata) & 0x8F) != 0x8a){
+		DPRINT("port %x not cs4231a: %x\n", IN(Pdata));
+		return;
+	}
+	print("audio0: cs4231a: port %x irq %d wdma %d rdma %d\n", csdev.port, csdev.irq, Wdma, Rdma);
+
+	resetlevel();
+
+	cswait();
+	OUT(Paddr, Mode);
+	csdelay();
+	OUT(Pdata, Mode2|IN(Pdata));	/* mode2 for all the trimmings */
+	csdelay();
+	cswait();
+
+	csdev.sticky = MCE;
+	xout(Config, Fullcal);
+	csspeed(volumes[Vspeed].ilval);
+	csformat(InFormat, CMCE, Linear8, audio.livol);
+	csformat(OutFormat, PMCE, Linear8, audio.lovol);
+	csdev.sticky &= ~MCE;
+	OUT(Paddr, csdev.sticky);
+	microdelay(10000);
+	cswait();	/* recalibration takes ages */
+
+	xout(FeatureStatus, 0);
+	OUT(Pstatus, 0);
+	setvec(csdev.irq, cs4231intr, 0);
+	xout(PinControl, xin(PinControl)|IEN);
+}
+
+Chan*
+cs4231attach(char *param)
+{
+	return devattach('A', param);
+}
+
+Chan*
+cs4231clone(Chan *c, Chan *nc)
+{
+	return devclone(c, nc);
+}
+
+int
+cs4231walk(Chan *c, char *name)
+{
+	return devwalk(c, name, audiodir, NPORT, devgen);
+}
+
+void
+cs4231stat(Chan *c, char *db)
+{
+	devstat(c, db, audiodir, NPORT, devgen);
+}
+
+Chan*
+cs4231open(Chan *c, int omode)
+{
+	switch(c->qid.path & ~CHDIR) {
+	default:
+		error(Eperm);
+		break;
+
+	case Qaudioctl:
+	case Qdir:
+		break;
+
+	case Qaudio:
+		qlock(&audio);
+		if(audio.opened){
+			qunlock(&audio);
+			error(Einuse);
+		}
+		if(audio.bufinit == 0) {
+			audio.bufinit = 1;
+			acbufinit(&audio.in);
+			acbufinit(&audio.out);
+		}
+		audio.opened = 1;
+		setempty();
+		qunlock(&audio);
+		mxvolume();
+		break;
+	}
+	c = devopen(c, omode, audiodir, NPORT, devgen);
+	c->mode = openmode(omode);
+	c->flag |= COPEN;
+	c->offset = 0;
+
+	return c;
+}
+
+void
+cs4231create(Chan *c, char *name, int omode, ulong perm)
+{
+	USED(c, name, omode, perm);
+	error(Eperm);
+}
+
+void
+cs4231close(Chan *c)
+{
+	Buf *b;
+
+	switch(c->qid.path & ~CHDIR) {
+	default:
+		error(Eperm);
+		break;
+
+	case Qdir:
+	case Qaudioctl:
+		break;
+
+	case Qaudio:
+		if(c->flag & COPEN) {
+			qlock(&audio);
+			audio.opened = 0;
+			if(waserror()){
+				qunlock(&audio);
+				nexterror();
+			}
+			b = audio.out.filling;
+			if(b){
+				audio.out.filling = 0;
+				putbuf(&audio.out.full, b);
+			}
+			waitoutput();
+			flushinput();
+			//tsleep(&up->sleep, return0, 0, 500);
+			//speaker(0);
+			qunlock(&audio);
+			poperror();
+		}
+		break;
+	}
+}
+
+long
+cs4231read(Chan *c, char *a, long n, vlong offset)
+{
+	int liv, riv, lov, rov, ifmt, ofmt;
+	long m, n0;
+	char buf[350];
+	Buf *b;
+	int j;
+
+	n0 = n;
+	switch(c->qid.path & ~CHDIR) {
+	default:
+		error(Eperm);
+		break;
+
+	case Qdir:
+		return devdirread(c, a, n, audiodir, NPORT, devgen);
+
+	case Qaudio:
+		qlock(&audio.in);
+		if(waserror()){
+			qunlock(&audio.in);
+			nexterror();
+		}
+		while(n > 0) {
+			b = audio.in.filling;
+			if(b == 0) {
+				b = getbuf(&audio.in.full);
+				if(b == 0) {
+					startinput();
+					sleep(&audio.in.r, anyinput, &audio.in);
+					continue;
+				}
+				audio.in.filling = b;
+				b->count = 0;
+			}
+			m = Bufsize-b->count;
+			if(m > n)
+				m = n;
+			memmove(a, b->virt+b->count, m);
+
+			b->count += m;
+			n -= m;
+			a += m;
+			if(b->count >= Bufsize) {
+				audio.in.filling = 0;
+				putbuf(&audio.in.empty, b);
+			}
+		}
+		qunlock(&audio.in);
+		poperror();
+		break;
+
+	case Qaudioctl:
+		j = 0;
+		buf[0] = 0;
+		for(m=0; volumes[m].name; m++){
+			liv = audio.livol[m];
+			riv = audio.rivol[m];
+			lov = audio.lovol[m];
+			rov = audio.rovol[m];
+			j += snprint(buf+j, sizeof(buf)-j, "%s", volumes[m].name);
+			if((volumes[m].flag & Fmono) || liv==riv && lov==rov){
+				if((volumes[m].flag&(Fin|Fout))==(Fin|Fout) && liv==lov)
+					j += snprint(buf+j, sizeof(buf)-j, " %d", liv);
+				else{
+					if(volumes[m].flag & Fin)
+						j += snprint(buf+j, sizeof(buf)-j, " in %d", liv);
+					if(volumes[m].flag & Fout)
+						j += snprint(buf+j, sizeof(buf)-j, " out %d", lov);
+				}
+			}else{
+				if((volumes[m].flag&(Fin|Fout))==(Fin|Fout) && liv==lov && riv==rov)
+					j += snprint(buf+j, sizeof(buf)-j, " left %d right %d",
+						liv, riv);
+				else{
+					if(volumes[m].flag & Fin)
+						j += snprint(buf+j, sizeof(buf)-j, " in left %d right %d",
+							liv, riv);
+					if(volumes[m].flag & Fout)
+						j += snprint(buf+j, sizeof(buf)-j, " out left %d right %d",
+							lov, rov);
+				}
+			}
+			j += snprint(buf+j, sizeof(buf)-j, "\n");
+		}
+		ifmt = xin(InFormat);
+		ofmt = xin(OutFormat);
+		if(ifmt != ofmt){
+			j += snprint(buf+j, sizeof(buf)-j, "in enc %s\n", encname(ifmt));
+			j += snprint(buf+j, sizeof(buf)-j, "out enc %s\n", encname(ofmt));
+		} else
+			j += snprint(buf+j, sizeof(buf)-j, "enc %s\n", encname(ifmt));
+		j += snprint(buf+j, sizeof(buf)-j, "loop %d\n", audio.loopback);
+		{int i; for(i=0; i<32; i++){j += snprint(buf+j, sizeof(buf)-j, " %d:%x", i, xin(i)); }j += snprint(buf+j,sizeof(buf)-j,"\n");}
+		USED(j);
+
+		return readstr(offset, a, n, buf);
+	}
+	return n0-n;
+}
+
+Block*
+cs4231bread(Chan *c, long n, ulong offset)
+{
+	return devbread(c, n, offset);
+}
+
+long
+cs4231write(Chan *c, char *a, long n, vlong offset)
+{
+	long m, n0;
+	int i, nf, v, left, right, in, out, fmt, doload;
+	char buf[255], *field[Ncmd];
+	Buf *b;
+
+	USED(offset);
+
+	n0 = n;
+	switch(c->qid.path & ~CHDIR) {
+	default:
+		error(Eperm);
+		break;
+
+	case Qaudioctl:
+		waitoutput();
+		flushinput();
+		qlock(&audio);
+		if(waserror()){
+			qunlock(&audio);
+			nexterror();
+		}
+		v = Vaudio;
+		doload = 0;
+		left = 1;
+		right = 1;
+		in = 1;
+		out = 1;
+		if(n > sizeof(buf)-1)
+			n = sizeof(buf)-1;
+		memmove(buf, a, n);
+		buf[n] = '\0';
+
+		nf = getfields(buf, field, Ncmd, 1, " \t\n,");
+		for(i = 0; i < nf; i++){
+			/*
+			 * a number is volume
+			 */
+			if(field[i][0] >= '0' && field[i][0] <= '9') {
+				m = strtoul(field[i], 0, 10);
+				if(left && out)
+					audio.lovol[v] = m;
+				if(left && in)
+					audio.livol[v] = m;
+				if(right && out)
+					audio.rovol[v] = m;
+				if(right && in)
+					audio.rivol[v] = m;
+				if(v == Vspeed){
+					ilock(&csdev);
+					csdev.sticky = MCE;
+					csspeed(m);
+					csdev.sticky &= ~MCE;
+					OUT(Paddr, csdev.sticky);
+					microdelay(10000);
+					cswait();
+					iunlock(&csdev);
+				} else
+					doload = 1;
+				continue;
+			}
+
+			for(m=0; volumes[m].name; m++) {
+				if(strcmp(field[i], volumes[m].name) == 0) {
+					v = m;
+					in = 1;
+					out = 1;
+					left = 1;
+					right = 1;
+					break;
+				}
+			}
+			if(volumes[m].name)
+				continue;
+
+			if(strcmp(field[i], "chat") == 0){
+				chatty = !chatty;
+				continue;
+			}
+
+			if(strcmp(field[i], "reset") == 0) {
+				resetlevel();
+				doload = 1;
+				continue;
+			}
+			if(strcmp(field[i], "loop") == 0) {
+				if(++i >= nf)
+					error(Evolume);
+				audio.loopback = strtoul(field[i], 0, 10);
+				doload = 1;
+				continue;
+			}
+			if(strcmp(field[i], "enc") == 0) {
+				if(++i >= nf)
+					error(Evolume);
+				fmt = -1;
+				if(strcmp(field[i], "ulaw") == 0)
+					fmt = uLaw;
+				else if(strcmp(field[i], "alaw") == 0)
+					fmt = aLaw;
+				else if(strcmp(field[i], "pcm") == 0)
+					fmt = Linear8;
+				else if(strcmp(field[i], "adpcm") == 0)
+					fmt = ADPCM;
+				else
+					error(Evolume);
+				if(in)
+					csdev.regs[InFormat] = fmt;
+				if(out)
+					csdev.regs[OutFormat] = fmt;
+				doload = 1;
+				continue;
+			}
+			if(strcmp(field[i], "dev") == 0) {
+				if(++i >= nf)
+					error(Evolume);
+				if(in){
+					fmt = -1;
+					if(strcmp(field[i], "mic") == 0)
+						fmt = ISmic;
+					else if(strcmp(field[i], "line") == 0)
+						fmt = ISline;
+					else if(strcmp(field[i], "aux1") == 0)
+						fmt = ISaux1;
+					else if(strcmp(field[i], "loop") == 0)
+						fmt = ISloop;
+					else
+						error(Evolume);
+					if(left)
+						csdev.regs[LeftADC] = fmt;
+					if(right)
+						csdev.regs[RightADC] = fmt;
+					doload = 1;
+				}
+				continue;
+			}
+			if(strcmp(field[i], "in") == 0) {
+				in = 1;
+				out = 0;
+				continue;
+			}
+			if(strcmp(field[i], "out") == 0) {
+				in = 0;
+				out = 1;
+				continue;
+			}
+			if(strcmp(field[i], "left") == 0) {
+				left = 1;
+				right = 0;
+				continue;
+			}
+			if(strcmp(field[i], "right") == 0) {
+				left = 0;
+				right = 1;
+				continue;
+			}
+			error(Evolume);
+		}
+		if(doload)
+			mxvolume();
+		qunlock(&audio);
+		poperror();
+		n=0;
+		break;
+
+	case Qaudio:
+		qlock(&audio.out);
+		if(waserror()){
+			qunlock(&audio.out);
+			nexterror();
+		}
+		while(n > 0) {
+			b = audio.out.filling;
+			if(b == 0) {
+				b = getbuf(&audio.out.empty);
+				if(b == 0) {
+					startoutput();
+					sleep(&audio.out.r, anybuf, &audio.out);
+					continue;
+				}
+				b->count = 0;
+				audio.out.filling = b;
+			}
+
+			m = Bufsize-b->count;
+			if(m > n)
+				m = n;
+			memmove(b->virt+b->count, a, m);
+
+			b->count += m;
+			n -= m;
+			a += m;
+			if(b->count >= Bufsize) {
+				audio.out.filling = 0;
+				putbuf(&audio.out.full, b);
+			}
+		}
+		qunlock(&audio.out);
+		poperror();
+		break;
+	}
+	return n0 - n;
+}
+
+long
+cs4231bwrite(Chan *c, Block *bp, ulong offset)
+{
+	return devbwrite(c, bp, offset);
+}
+
+void
+cs4231remove(Chan *c)
+{
+	USED(c);
+	error(Eperm);
+}
+
+void
+cs4231wstat(Chan *c, char *dp)
+{
+	USED(c, dp);
+	error(Eperm);
+}
+
+static char *
+encname(int v)
+{
+	switch(v & ~(0xF|Stereo)){
+	case uLaw:	return "ulaw";
+	case aLaw:	return "alaw";
+	case Linear8:	return "pcm";
+	case Linear16:	return "pcm16";
+	case ADPCM:	return "adpcm";
+	default:	return "?";
+	}
+}
--- /dev/null
+++ b/os/js/devrtc.c
@@ -1,0 +1,413 @@
+#include	"u.h"
+#include	"../port/lib.h"
+#include	"mem.h"
+#include	"dat.h"
+#include	"fns.h"
+#include	"../port/error.h"
+
+#include	"io.h"
+
+/*
+ * Mostek MK48T12-15 Zeropower/Timekeeper
+ * This driver is actually portable.
+ */
+typedef struct Rtc	Rtc;
+struct Rtc
+{
+	int	sec;
+	int	min;
+	int	hour;
+	int	wday;
+	int	mday;
+	int	mon;
+	int	year;
+};
+
+static uchar	rtcgencksum(void);
+static void	setrtc(Rtc *rtc);
+static long	rtctime(void);
+static int	*yrsize(int yr);
+static int	*yrsize(int yr);
+static ulong	rtc2sec(Rtc *rtc);
+static void	sec2rtc(ulong secs, Rtc *rtc);
+
+static struct
+{
+	uchar	*cksum;
+	uchar	*ram;
+	RTCdev	*rtc;
+}nvr;
+
+enum{
+	Qdir,
+	Qrtc,
+	Qnvram,
+};
+
+QLock	rtclock;		/* mutex on clock operations */
+
+static Dirtab rtcdir[]={
+	".",		{Qdir, 0, QTDIR},	0,	0555,
+	"rtc",		{Qrtc, 0},	0,		0666,
+	"nvram",	{Qnvram, 0},	NVWRITE,	0666,
+};
+#define	NRTC	(sizeof(rtcdir)/sizeof(rtcdir[0]))
+
+static void
+rtcinit(void)
+{
+	KMap *k;
+
+	k = kmappa(NVR_CKSUM_PHYS, PTENOCACHE|PTEIO);
+	nvr.cksum = (uchar*)VA(k);
+
+	k = kmappa(NVR_PHYS, PTENOCACHE|PTEIO);
+	nvr.ram = (uchar*)VA(k);
+	nvr.rtc = (RTCdev*)(VA(k)+RTCOFF);
+
+	rtcgencksum();
+}
+
+static Chan*
+rtcattach(char *spec)
+{
+	return devattach('r',spec);
+}
+
+static Walkqid*
+rtcwalk(Chan *c, Chan *nc, char **name, int nname)
+{
+	return devwalk(c, nc, name, nname, rtcdir, NRTC, devgen);
+}
+
+static int
+rtcstat(Chan *c, uchar *dp, int n)
+{
+	return devstat(c, dp, n, rtcdir, NRTC, devgen);
+}
+
+static Chan*
+rtcopen(Chan *c, int omode)
+{
+	omode = openmode(omode);
+	switch((ulong)c->qid.path){
+	case Qrtc:
+		if(strcmp(up->env->user, eve)!=0 && omode!=OREAD)
+			error(Eperm);
+		break;
+	case Qnvram:
+		if(strcmp(up->env->user, eve)!=0)
+			error(Eperm);
+	}
+	return devopen(c, omode, rtcdir, NRTC, devgen);
+}
+
+static void	 
+rtccreate(Chan *c, char *name, int omode, ulong perm)
+{
+	USED(c, name, omode, perm);
+	error(Eperm);
+}
+
+static void	 
+rtcclose(Chan *c)
+{
+	USED(c);
+}
+
+static long	 
+rtcread(Chan *c, void *buf, long n, vlong offset)
+{
+	ulong t, ot;
+
+	if(c->qid.type & QTDIR)
+		return devdirread(c, buf, n, rtcdir, NRTC, devgen);
+
+	switch((ulong)c->qid.path){
+	case Qrtc:
+		qlock(&rtclock);
+		t = rtctime();
+		do{
+			ot = t;
+			t = rtctime();	/* make sure there's no skew */
+		}while(t != ot);
+		qunlock(&rtclock);
+		n = readnum(offset, buf, n, t, 12);
+		return n;
+	case Qnvram:
+		if(offset > NVREAD)
+			return 0;
+		if(n > NVREAD - offset)
+			n = NVREAD - offset;
+		qlock(&rtclock);
+		memmove(buf, nvr.ram+offset, n);
+		qunlock(&rtclock);
+		return n;
+	}
+	error(Egreg);
+	return 0;		/* not reached */
+}
+
+/*
+ * XXX - Tad: fixme to generate the correct checksum
+ */
+static uchar
+rtcgencksum(void)
+{
+	uchar cksum;
+	int i;
+	static uchar p1cksum = 0;
+	static uchar p1cksumvalid=0;
+
+	if(!p1cksumvalid) {
+		for(i=1; i < 0x1000 ; i++)
+			p1cksum ^= nvr.cksum[i];
+		p1cksumvalid = 1;
+	}
+
+	cksum = p1cksum;
+
+	for(i=0; i < 0xfdf ; i++) {
+		cksum ^= nvr.ram[i];
+	}
+
+	return cksum;
+}
+
+static long	 
+rtcwrite(Chan *c, void *buf, long n, vlong offset)
+{
+	Rtc rtc;
+	ulong secs;
+	char *cp, sbuf[32];
+
+	switch((ulong)c->qid.path){
+	case Qrtc:
+		/*
+		 *  read the time
+		 */
+		if(offset != 0 || n >= sizeof(sbuf)-1)
+			error(Ebadarg);
+		memmove(sbuf, buf, n);
+		sbuf[n] = '\0';
+		cp = sbuf;
+		while(*cp){
+			if(*cp>='0' && *cp<='9')
+				break;
+			cp++;
+		}
+		secs = strtoul(cp, 0, 0);
+		/*
+		 *  convert to bcd
+		 */
+		sec2rtc(secs, &rtc);
+		/*
+		 * write it
+		 */
+		qlock(&rtclock);
+		setrtc(&rtc);
+		qunlock(&rtclock);
+		return n;
+	case Qnvram:
+		if(offset > NVWRITE)
+			return 0;
+		if(n > NVWRITE - offset)
+			n = NVWRITE - offset;
+		qlock(&rtclock);
+		memmove(nvr.ram+offset, buf, n);
+		*nvr.cksum = rtcgencksum();
+		qunlock(&rtclock);
+		return n;
+	}
+	error(Egreg);
+	return 0;		/* not reached */
+}
+
+#define bcd2dec(bcd)	(((((bcd)>>4) & 0x0F) * 10) + ((bcd) & 0x0F))
+#define dec2bcd(dec)	((((dec)/10)<<4)|((dec)%10))
+
+static void
+setrtc(Rtc *rtc)
+{
+	struct RTCdev *dev;
+
+	dev = nvr.rtc;
+	dev->control |= RTCWRITE;
+	wbflush();
+	dev->year = dec2bcd(rtc->year % 100);
+	dev->mon = dec2bcd(rtc->mon);
+	dev->mday = dec2bcd(rtc->mday);
+	dev->hour = dec2bcd(rtc->hour);
+	dev->min = dec2bcd(rtc->min);
+	dev->sec = dec2bcd(rtc->sec);
+	wbflush();
+	dev->control &= ~RTCWRITE;
+	wbflush();
+}
+
+static long
+rtctime(void)
+{
+	struct RTCdev *dev;
+	Rtc rtc;
+
+	dev = nvr.rtc;
+	dev->control |= RTCREAD;
+	wbflush();
+	rtc.sec = bcd2dec(dev->sec) & 0x7F;
+	rtc.min = bcd2dec(dev->min & 0x7F);
+	rtc.hour = bcd2dec(dev->hour & 0x3F);
+	rtc.mday = bcd2dec(dev->mday & 0x3F);
+	rtc.mon = bcd2dec(dev->mon & 0x3F);
+	rtc.year = bcd2dec(dev->year);
+	dev->control &= ~RTCREAD;
+	wbflush();
+
+	if (rtc.mon < 1 || rtc.mon > 12)
+		return 0;
+	/*
+	 *  the world starts Jan 1 1970
+	 */
+	if(rtc.year < 70)
+		rtc.year += 2000;
+	else
+		rtc.year += 1900;
+
+	return rtc2sec(&rtc);
+}
+
+#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 yr)
+{
+	if((yr % 4) == 0)
+		return ldmsize;
+	else
+		return dmsize;
+}
+
+/*
+ *  compute seconds since Jan 1 1970
+ */
+static ulong
+rtc2sec(Rtc *rtc)
+{
+	ulong secs;
+	int i;
+	int *d2m;
+
+	secs = 0;
+
+	/*
+	 *  seconds per year
+	 */
+	for(i = 1970; i < rtc->year; i++){
+		d2m = yrsize(i);
+		secs += d2m[0] * SEC2DAY;
+	}
+
+	/*
+	 *  seconds per month
+	 */
+	d2m = yrsize(rtc->year);
+	for(i = 1; i < rtc->mon; i++)
+		secs += d2m[i] * SEC2DAY;
+
+	secs += (rtc->mday-1) * SEC2DAY;
+	secs += rtc->hour * SEC2HOUR;
+	secs += rtc->min * SEC2MIN;
+	secs += rtc->sec;
+
+	return secs;
+}
+
+/*
+ *  compute rtc from seconds since Jan 1 1970
+ */
+static void
+sec2rtc(ulong secs, Rtc *rtc)
+{
+	int d;
+	long hms, day;
+	int *d2m;
+
+	/*
+	 * break initial number into days
+	 */
+	hms = secs % SEC2DAY;
+	day = secs / SEC2DAY;
+	if(hms < 0) {
+		hms += SEC2DAY;
+		day -= 1;
+	}
+
+	/*
+	 * generate hours:minutes:seconds
+	 */
+	rtc->sec = hms % 60;
+	d = hms / 60;
+	rtc->min = d % 60;
+	d /= 60;
+	rtc->hour = d;
+
+	/*
+	 * year number
+	 */
+	if(day >= 0)
+		for(d = 1970; day >= *yrsize(d); d++)
+			day -= *yrsize(d);
+	else
+		for (d = 1970; day < 0; d--)
+			day += *yrsize(d-1);
+	rtc->year = d;
+
+	/*
+	 * generate month
+	 */
+	d2m = yrsize(rtc->year);
+	for(d = 1; day >= d2m[d]; d++)
+		day -= d2m[d];
+	rtc->mday = day + 1;
+	rtc->mon = d;
+
+	return;
+}
+
+Dev rtcdevtab = {
+	'r',
+	"rtc",
+
+	devreset,
+	rtcinit,
+	devshutdown,
+	rtcattach,
+	rtcwalk,
+	rtcstat,
+	rtcopen,
+	rtccreate,
+	rtcclose,
+	rtcread,
+	devbread,
+	rtcwrite,
+	devbwrite,
+	devremove,
+	devwstat,
+};
--- /dev/null
+++ b/os/js/fns.h
@@ -1,0 +1,110 @@
+#include "../port/portfns.h"
+#define dumplongs(x, y, z)
+#define clockcheck()
+#define setpanic()
+
+void	links(void);
+void	prom_printf(char *format, ...);	/* can't use after mmuinit() */
+void	savefpregs(FPU *);
+void	restfpregs(FPU*);
+void	savefsr(FPenv *);
+void	restfsr(FPenv *);
+void	disabfp(void);
+void	fpinit(void);
+void	fpsave(FPU*);
+void	bootargs(ulong);
+void	cacheinit(void);
+ulong	call_openboot(void*, ...);
+void	clearftt(ulong);
+#define	clearmmucache()
+void	clockinit(void);
+void	clock(Ureg*);
+#define	coherence()		/* nothing to do on uniprocessor */
+void	dcflush(void);
+void	disabfp(void);
+void	enabfp(void);
+char*	excname(ulong);
+
+#define	flushpage(pa)	icflush()
+void	flushtlb(void);
+void	flushtlbctx(void);
+void	flushtlbpage(ulong);
+int	fpcr(int);
+int	fpquiet(void);
+void	fpregrestore(char*);
+void	fpregsave(char*);
+int	fptrap(void);
+int	getfpq(ulong*);
+ulong	getfsr(void);
+ulong	getpcr(void);
+ulong	getphys(ulong);
+ulong	getrmmu(ulong);
+ulong	getpsr(void);
+void	icflush(void);
+int	isvalid_va(void*);
+void	flushicache(void);
+void	flushdcache(void);
+void	flushiline(ulong);
+void	flushdline(ulong);
+#define	idlehands()			/* nothing to do in the runproc */
+void	intrinit(void);
+void	ioinit(void);
+void	kbdclock(void);
+void	kbdrepeat(int);
+void	kbdinit(void);
+void	kbdintr(void);
+#define KADDR(a)	((void*)((ulong)(a)|KZERO))
+#define PADDR(a)	((ulong)(a)&~KZERO)
+void	kmapinit(void);
+void*	kmappa(ulong, ulong);
+ulong	kmapsbus(int);
+ulong	kmapdma(ulong, ulong);
+int	kprint(char*, ...);
+void	kproftimer(ulong);
+void	kunmap(KMap*);
+void	lanceintr(void);
+void	lancesetup(Lance*);
+void	lancetoggle(void);
+void	mmuinit(void);
+void	mousebuttons(int);
+void	printinit(void);
+#define	procrestore(p)
+#define	procsave(p)
+#define	procsetup(x)	((p)->fpstate = FPinit)
+void	putphys(ulong, ulong);
+void	putrmmu(ulong, ulong);
+void	putstr(char*);
+void	puttbr(ulong);
+void	systemreset(void);
+void	screeninit(void);
+void	screenputc(char *);
+void	screenputs(char*, int);
+void	scsiintr(void);
+void	setpcr(ulong);
+void	setpsr(ulong);
+void	spldone(void);
+void	trap(Ureg*);
+void	trapinit(void);
+#define	wbflush()	/* mips compatibility */
+#define	waserror()	(up->nerrlab++, setlabel(&up->errlab[up->nerrlab-1]))
+ulong	getcallerpc(void*);
+void	dumpregs(Ureg*);
+
+void	ns16552special(int,int,Queue**,Queue**,int (*)(Queue*,int));
+char	ns16552dmarcv(int);
+void	ns16552install(void);
+void	ns16552intr(int);
+
+long	dmasetup(int,void*,long,int);
+void	dmaend(int);
+int	dmacount(int);
+
+void	superioinit(ulong va, uchar*, uchar*, uchar*, uchar*);
+ulong	superiova(void);
+
+uchar	superio_readctl(void);
+uchar	superio_readdata(void);
+void	superio_writectl(uchar val);
+void	superio_writedata(uchar val);
+void	outb(ulong, uchar);
+uchar	inb(ulong);
--- /dev/null
+++ b/os/js/fsv.c
@@ -1,0 +1,198 @@
+#include "u.h"
+#include "../port/lib.h"
+#include "mem.h"
+#include "io.h"
+#include "dat.h"
+#include "fns.h"
+
+#include <draw.h>
+#include <memdraw.h>
+#include "screen.h"
+
+extern Video *vid;
+extern Memimage gscreen;
+
+static Vctlr*	init(Vctlr* vctlr, int x, int y, int d);
+static int	setcolour(ulong p, ulong r, ulong g, ulong b);
+static void	enable(void);
+static void	disable(void);
+static void	move(int cx, int cy);
+static void	load(Cursor *c);
+static int	isloaded(void);
+
+extern void*	memcpy(void*, void*, int);
+extern void	cursorupdate0(void);
+
+Vctlr FSV = {
+	"FSV",			/* name */
+	init,			/* init */
+	0,			/* page */
+	setcolour,		/* setcolor */
+
+	enable,			/* enable cursor fn */
+	disable,		/* disable cursor fn */
+	move,			/* move cursor fn */
+	load,			/* load cursor fn */
+	isloaded,		/* is cursor loaded? */
+	0,			/* deprecated */
+
+	1024,			/* screen width (x) */
+	768,			/* screen height (y) */
+	3,			/* depth */
+
+	0,			/* hidecount */
+	0,			/* loaded */
+};
+
+static int lastx=-1;
+static int lasty=-1;
+
+static Vctlr*
+init(Vctlr* vctlr, int x, int y, int d)
+{
+	USED(vctlr,x,y,d);
+
+	return &FSV;
+}
+
+static int
+setcolour(ulong p, ulong r, ulong g, ulong b)
+{
+	if(gscreen.ldepth == 0)
+		return 0;	/* can't change mono screen colormap */
+	else{
+		vid->addr = p << 24;
+		vid->color = r << 24;
+		vid->color = g << 24;
+		vid->color = b << 24;
+		return 1;
+	}
+}
+
+static ulong backingstore[64];
+static Memdata backingstoredata = {
+	nil,
+	backingstore
+};
+
+static ulong backingnocursor[64];
+static Memdata backingnocursordata = {
+	nil,
+	backingnocursor
+};
+
+static ulong backwithcursor[64];
+static Memdata backwithcursordata = {
+	nil,
+	backwithcursor
+};
+
+static Memimage backingnocursormem = {
+	{0,0,16,16},
+	{0,0,16,16},
+	3,
+	0,
+	&backingnocursordata,
+	0,
+	16/4,
+	0,
+	0,
+};
+static Memimage backingmem = {
+	{0,0,16,16},
+	{0,0,16,16},
+	3,
+	0,
+	&backingstoredata,
+	0,
+	16/4,
+	0,
+	0,
+};
+
+static void
+disable(void)
+{
+	if(FSV.hidecount++)
+		return;
+	if(lastx < 0 || lasty < 0)
+		return;
+
+	memimagedraw(&gscreen, Rect(lastx,lasty,lastx+16,lasty+16),
+		     &backingnocursormem, Pt(0,0), memones, Pt(0,0));
+}
+
+static void
+enable(void)
+{
+	uchar *p;
+	uchar mask;
+	uchar *cset;
+	int i;
+
+	if(--FSV.hidecount > 0)
+		return;
+	FSV.hidecount = 0;
+
+	if(lastx < 0 || lasty < 0)
+		return;
+
+	memimagedraw(&backingmem,Rect(0,0,16,16),&gscreen,Pt(lastx,lasty),memones,
+		     Pt(0,0));
+
+	memcpy(backingnocursor,backingstore,256);
+	p = (uchar*)backingmem.data->data;
+
+	cset = FSV.cursor.set;
+
+	for(i=0;i<32;i++) {
+		mask = ~cset[i];
+
+		if(!(mask&(1<<7))) *p = 0xff;
+		++p;
+		if(!(mask&(1<<6))) *p = 0xff;
+		++p;
+		if(!(mask&(1<<5))) *p = 0xff;
+		++p;
+		if(!(mask&(1<<4))) *p = 0xff;
+		++p;
+		if(!(mask&(1<<3))) *p = 0xff;
+		++p;
+		if(!(mask&(1<<2))) *p = 0xff;
+		++p;
+		if(!(mask&(1<<1))) *p = 0xff;
+		++p;
+		if(!(mask&(1<<0))) *p = 0xff;
+		++p;
+	}
+
+	memimagedraw(&gscreen,Rect(lastx,lasty,lastx+16,lasty+16),&backingmem,Pt(0,0),
+		     memones,Pt(0,0));
+}
+
+static void
+move(int cx, int cy)
+{
+	if(!FSV.loaded)
+		return;
+
+	disable();
+	cursorupdate0();
+	lastx = cx;
+	lasty = cy;
+	enable();
+}
+
+
+static void
+load(Cursor *curs)
+{
+	FSV.cursor = *curs;
+	FSV.loaded=1;
+}
+
+static int
+isloaded(void)
+{
+	return FSV.loaded;
+}
--- /dev/null
+++ b/os/js/io.h
@@ -1,0 +1,224 @@
+/*
+ * most device registers are memory mapped, but
+ * a few things are accessed using putphys/getphys
+ */
+#define	SBUS(n)		(0x30000000+(n)*0x10000000)
+#define	FRAMEBUF(n)	SBUS(n)
+#define	FRAMEBUFID(n)	(SBUS(n)+0x000000)
+#define	DISPLAYRAM(n)	(SBUS(n)+0x800000)
+#define	CLOCK		0x71D00000
+#define	CLOCKFREQ	1000000		/* one microsecond increments */
+
+#define SUPERIO_PHYS_PAGE		0x71300000
+#define SUPERIO_INDEX_OFFSET		0x398
+#define SUPERIO_DATA_OFFSET		0x399
+#define SUPERIO_MOUSE_KBD_DATA_PORT	0x60
+#define SUPERIO_MOUSE_KBD_CTL_PORT	0x64
+
+#define AUDIO_PHYS_PAGE		0x66666666
+#define AUDIO_INDEX_OFFSET	0x830
+
+enum
+{
+	Mousevec = 13,
+	Kbdvec = 13
+};
+
+#define	NVR_CKSUM_PHYS	0x71200000	/* non-volatile RAM cksum page */
+#define	NVR_PHYS	0x71201000	/* non-volatile RAM */
+#define DMA		0x78400000	/* SCSI and Ether DMA registers */
+#define SCSI		0x78800000	/* NCR53C90 registers */
+#define	ETHER		0x78C00000	/* RDP, RAP */
+#define	FLOPPY		0x71400000
+#define	SYSINTR		0x71E10000	/* system interrupt control registers */
+
+#define	TIMECONFIG	0x71D10010	/* timer configuration register (phys) */
+#define	AUXIO1		0x71900000
+#define	AUXIO2		0x71910000
+
+typedef struct Sysint Sysint;
+struct Sysint
+{
+	ulong	pending;
+	ulong	mask;
+	ulong	maskclr;
+	ulong	maskset;
+	ulong	target;
+};
+
+enum {
+	MaskAllIntr = 1<<31,
+	MEIntr = 1<<30,
+	MSIIntr = 1<<29,
+	EMCIntr = 1<<28,
+	VideoIntr = 1<<20,	/* supersparc only */
+	Timer10 = 1<<19,
+	EtherIntr = 1<<16,
+	SCCIntr = 1<<15,
+	KbdIntr = 1<<13,
+	/* bits 7 to 13 are SBUS levels 1 to 7 */
+};
+#define	SBUSINTR(x)	(1<<((x)+6))
+
+typedef struct SCCdev	SCCdev;
+struct SCCdev
+{
+	uchar	ptrb;
+	uchar	dummy1;
+	uchar	datab;
+	uchar	dummy2;
+	uchar	ptra;
+	uchar	dummy3;
+	uchar	dataa;
+	uchar	dummy4;
+};
+
+/*
+ *  non-volatile ram
+ */
+#define NVREAD	(4096-32)	/* minus RTC */
+#define NVWRITE	(0x800)		/* */
+#define	IDOFF	(4096-8-32)
+
+/*
+ * real-time clock
+ */
+typedef struct RTCdev	RTCdev;
+struct RTCdev
+{
+	uchar	control;		/* read or write the device */
+	uchar	sec;
+	uchar	min;
+	uchar	hour;
+	uchar	wday;
+	uchar	mday;
+	uchar	mon;
+	uchar	year;
+};
+#define RTCOFF		0xFF8
+#define RTCREAD		(0x40)
+#define RTCWRITE	(0x80)
+
+/*
+ * dma
+ */
+typedef struct DMAdev DMAdev;
+struct DMAdev {
+	/* ESP/SCSI DMA */
+	ulong	csr;			/* Control/Status */
+	ulong	addr;			/* address in 16Mb segment */
+	ulong	count;			/* transfer byte count */
+	ulong	diag;
+
+	/* Ether DMA */
+	ulong	ecsr;			/* Control/Status */
+	ulong	ediag;
+	ulong	cache;			/* cache valid bits */
+	uchar	base;			/* base address (16Mb segment) */
+};
+
+enum {
+	Int_pend	= 0x00000001,	/* interrupt pending */
+	Err_pend	= 0x00000002,	/* error pending */
+	Pack_cnt	= 0x0000000C,	/* pack count (mask) */
+	Int_en		= 0x00000010,	/* interrupt enable */
+	Dma_Flush	= 0x00000020,	/* flush pack end error */
+	Drain		= 0x00000040,	/* drain pack to memory */
+	Dma_Reset	= 0x00000080,	/* hardware reset (sticky) */
+	Write		= 0x00000100,	/* set for device to memory (!) */
+	En_dma		= 0x00000200,	/* enable DMA */
+	Req_pend	= 0x00000400,	/* request pending */
+	Byte_addr	= 0x00001800,	/* next byte addr (mask) */
+	En_cnt		= 0x00002000,	/* enable count */
+	Tc		= 0x00004000,	/* terminal count */
+	Ilacc		= 0x00008000,	/* which ether chip */
+	Dev_id		= 0xF0000000,	/* device ID */
+};
+
+/*
+ *  NCR53C90 SCSI controller (every 4th location)
+ */
+typedef struct SCSIdev	SCSIdev;
+struct SCSIdev {
+	uchar	countlo;		/* byte count, low bits */
+	uchar	pad1[3];
+	uchar	countmi;		/* byte count, middle bits */
+	uchar	pad2[3];
+	uchar	fifo;			/* data fifo */
+	uchar	pad3[3];
+	uchar	cmd;			/* command byte */
+	uchar	pad4[3];
+	union {
+		struct {		/* read only... */
+			uchar	status;		/* status */
+			uchar	pad05[3];
+			uchar	intr;		/* interrupt status */
+			uchar	pad06[3];
+			uchar	step;		/* sequence step */
+			uchar	pad07[3];
+			uchar	fflags;		/* fifo flags */
+			uchar	pad08[3];
+			uchar	config;		/* RW: configuration */
+			uchar	pad09[3];
+			uchar	Reserved1;
+			uchar	pad0A[3];
+			uchar	Reserved2;
+			uchar	pad0B[3];
+			uchar	conf2;		/* RW: configuration */
+			uchar	pad0C[3];
+			uchar	conf3;		/* RW: configuration */
+			uchar	pad0D[3];
+			uchar	partid;		/* unique part id */
+			uchar	pad0E[3];
+			uchar	fbottom;	/* RW: fifo bottom */
+			uchar	pad0F[3];
+		};
+		struct {		/* write only... */
+			uchar	destid;		/* destination id */
+			uchar	pad15[3];
+			uchar	timeout;	/* during selection */
+			uchar	pad16[3];
+			uchar	syncperiod;	/* synchronous xfr period */
+			uchar	pad17[3];
+			uchar	syncoffset;	/* synchronous xfr offset */
+			uchar	pad18[3];
+			uchar	RW0;
+			uchar	pad19[3];
+			uchar	clkconf;
+			uchar	pad1A[3];
+			uchar	test;	
+			uchar	pad1B[3];
+			uchar	RW1;
+			uchar	pad1C[3];
+			uchar	RW2;
+			uchar	pad1D[3];
+			uchar	counthi;	/* byte count, hi bits */
+			uchar	pad1E[3];
+			uchar	RW3;
+			uchar	pad1F[3];
+		};
+	};
+};
+
+/*
+ * DMA2 ENET
+ */
+enum {
+	E_Int_pend	= 0x00000001,	/* interrupt pending */
+	E_Err_pend	= 0x00000002,	/* error pending */
+	E_draining	= 0x0000000C,	/* E-cache draining */
+	E_Int_en	= 0x00000010,	/* interrupt enable */
+	E_Invalidate	= 0x00000020,	/* mark E-cache invalid */
+	E_Slave_err	= 0x00000040,	/* slave access size error (sticky) */
+	E_Reset		= 0x00000080,	/* invalidate cache & reset interface (sticky) */
+	E_Drain		= 0x00000400,	/* force draining of E-cache to memory */
+	E_Dsbl_wr_drn	= 0x00000800,	/* disable E-cache drain on descriptor writes from ENET */
+	E_Dsbl_rd_drn	= 0x00001000,	/* disable E-cache drain on slave reads to ENET */
+	E_Ilacc		= 0x00008000,	/* `modifies ENET DMA cycle' */
+	E_Dsbl_buf_wr	= 0x00010000,	/* disable buffering of slave writes to ENET */
+	E_Dsbl_wr_inval	= 0x00020000,	/* do not invalidate E-cache on slave writes */
+	E_Burst_size	= 0x000C0000,	/* DMA burst size */
+	E_Loop_test	= 0x00200000,	/* loop back mode */
+	E_TP_select	= 0x00400000,	/* zero for AUI mode */
+	E_Dev_id	= 0xF0000000,	/* device ID */
+};
--- /dev/null
+++ b/os/js/iob.c
@@ -1,0 +1,13 @@
+#include "u.h"
+
+void
+outb(ulong addr, uchar val)
+{
+	*(uchar*)addr = val;
+}
+
+uchar
+inb(ulong addr)
+{
+	return *(uchar*)addr;
+}
--- /dev/null
+++ b/os/js/js
@@ -1,0 +1,107 @@
+dev
+	root
+	cons
+	env
+	mnt
+	pipe
+	prog
+	rtc
+	srv
+	dup
+	ssl
+	draw
+	pointer
+
+	ip	bootp ip ipv6 ipaux iproute arp netlog ptclbsum iprouter plan9 nullmedium pktmedium
+	lance netif netaux ethermedium
+
+	ns16552
+	tinyfs
+	cap
+#	cs4231		A
+
+ip
+#	il
+	tcp
+	udp
+#	rudp
+#	igmp
+	ipifc
+	icmp
+	icmp6
+	ipmux
+
+lib
+	interp
+	tk
+	prefab
+	draw
+	memlayer
+	memdraw
+	keyring
+	sec
+	mp
+	kern
+#	math
+
+mod
+	sys
+	draw
+	prefab
+	tk
+	keyring
+	crypt
+	ipints
+
+
+link
+	pppmedium ppp compress
+
+init
+	jsinit
+
+
+port
+	alarm
+	alloc
+	allocb
+	chan
+	dev
+	dial
+	dis
+	discall
+	exception
+	exportfs
+	inferno
+	latin1
+	nocache
+	parse
+	pgrp
+	print
+	proc
+	qio
+	qlock
+	sysfile
+	taslock
+	xalloc
+
+code
+	int kernel_pool_pcnt = 10;
+	int main_pool_pcnt = 40;
+	int heap_pool_pcnt = 20;
+	int image_pool_pcnt = 40;
+	int consoleprint = 1;
+	int cflag = 1;
+
+
+root
+	/chan	/
+	/dev	/
+	/dis
+	/env	/
+	/fd	/
+	/n
+	/net	/
+	/nvfs	/
+	/prog	/
+	/osinit.dis
--- /dev/null
+++ b/os/js/kbd.c
@@ -1,0 +1,482 @@
+#include "u.h"
+#include "../port/lib.h"
+#include "mem.h"
+#include "dat.h"
+#include "fns.h"
+#include "io.h"
+#include "../port/error.h"
+
+enum
+{
+	Data=		0x60,	/* data port */
+
+	Status=		0x64,	/* status port */
+	 Inready=	0x01,	/*  input character ready */
+	 Outbusy=	0x02,	/*  output busy */
+	 Sysflag=	0x04,	/*  system flag */
+	 Cmddata=	0x08,	/*  cmd==0, data==1 */
+	 Inhibit=	0x10,	/*  keyboard/mouse inhibited */
+	 Minready=	0x20,	/*  mouse character ready */
+	 Rtimeout=	0x40,	/*  general timeout */
+	 Parity=	0x80,
+
+	Cmd=		0x64,	/* command port (write only) */
+
+	CTdata=		0x0,	/* chips & Technologies ps2 data port */
+	CTstatus=	0x1,	/* chips & Technologies ps2 status port */
+	 Enable=	1<<7,
+	 Clear=		1<<6,
+	 Error=		1<<5,
+	 Intenable=	1<<4,
+	 Reset=		1<<3,
+	 Tready=	1<<2,
+	 Rready=	1<<1,
+	 Idle=		1<<0,
+
+	Spec=	0x80,
+
+	PF=	Spec|0x20,	/* num pad function key */
+	View=	Spec|0x00,	/* view (shift window up) */
+	KF=	Spec|0x40,	/* function key */
+	Shift=	Spec|0x60,
+	Break=	Spec|0x61,
+	Ctrl=	Spec|0x62,
+	Latin=	Spec|0x63,
+	Caps=	Spec|0x64,
+	Num=	Spec|0x65,
+	Middle=	Spec|0x66,
+	No=	0x00,		/* peter */
+
+	Home=	KF|13,
+	Up=	KF|14,
+	Pgup=	KF|15,
+	Print=	KF|16,
+	Left=	View,
+	Right=	View,
+	End=	'\r',
+	Down=	View,
+	Pgdown=	View,
+	Ins=	KF|20,
+	Del=	0x7F,
+
+	Rbutton=4,
+	Mbutton=2,
+	Lbutton=1,
+};
+
+uchar
+kbtab[] = {
+[0x00]	No,	0x1b,	'1',	'2',	'3',	'4',	'5',	'6',
+[0x08]	'7',	'8',	'9',	'0',	'-',	'=',	'\b',	'\t',
+[0x10]	'q',	'w',	'e',	'r',	't',	'y',	'u',	'i',
+[0x18]	'o',	'p',	'[',	']',	'\n',	Ctrl,	'a',	's',
+[0x20]	'd',	'f',	'g',	'h',	'j',	'k',	'l',	';',
+[0x28]	'\'',	'`',	Shift,	'\\',	'z',	'x',	'c',	'v',
+[0x30]	'b',	'n',	'm',	',',	'.',	'/',	Shift,	'*',
+[0x38]	Latin,	' ',	Ctrl,	KF|1,	KF|2,	KF|3,	KF|4,	KF|5,
+[0x40]	KF|6,	KF|7,	KF|8,	KF|9,	KF|10,	Num,	KF|12,	'7',
+[0x48]	'8',	'9',	'-',	'4',	'5',	'6',	'+',	'1',
+[0x50]	'2',	'3',	'0',	'.',	No,	No,	No,	KF|11,
+[0x58]	KF|12,	No,	No,	No,	No,	No,	No,	No,
+};
+
+uchar
+kbtabshift[] = {
+[0x00]	No,	0x1b,	'!',	'@',	'#',	'$',	'%',	'^',
+[0x08]	'&',	'*',	'(',	')',	'_',	'+',	'\b',	'\t',
+[0x10]	'Q',	'W',	'E',	'R',	'T',	'Y',	'U',	'I',
+[0x18]	'O',	'P',	'{',	'}',	'\n',	Ctrl,	'A',	'S',
+[0x20]	'D',	'F',	'G',	'H',	'J',	'K',	'L',	':',
+[0x28]	'"',	'~',	Shift,	'|',	'Z',	'X',	'C',	'V',
+[0x30]	'B',	'N',	'M',	'<',	'>',	'?',	Shift,	'*',
+[0x38]	Latin,	' ',	Ctrl,	KF|1,	KF|2,	KF|3,	KF|4,	KF|5,
+[0x40]	KF|6,	KF|7,	KF|8,	KF|9,	KF|10,	Num,	KF|12,	'7',
+[0x48]	'8',	'9',	'-',	'4',	'5',	'6',	'+',	'1',
+[0x50]	'2',	'3',	'0',	'.',	No,	No,	No,	KF|11,
+[0x58]	KF|12,	No,	No,	No,	No,	No,	No,	No,
+};
+
+uchar
+kbtabesc1[] = {
+[0x00]	No,	No,	No,	No,	No,	No,	No,	No,
+[0x08]	No,	No,	No,	No,	No,	No,	No,	No,
+[0x10]	No,	No,	No,	No,	No,	No,	No,	No,
+[0x18]	No,	No,	No,	No,	'\n',	Ctrl,	No,	No,
+[0x20]	No,	No,	No,	No,	No,	No,	No,	No,
+[0x28]	No,	No,	Shift,	No,	No,	No,	No,	No,
+[0x30]	No,	No,	No,	No,	No,	'/',	No,	Print,
+[0x38]	Latin,	No,	No,	No,	No,	No,	No,	No,
+[0x40]	No,	No,	No,	No,	No,	No,	Break,	Home,
+[0x48]	Up,	Pgup,	No,	Left,	No,	Right,	No,	End,
+[0x50]	Down,	Pgdown,	Ins,	Del,	No,	No,	No,	No,
+[0x58]	No,	No,	No,	No,	No,	No,	No,	No,
+};
+
+static	int	keybuttons;
+static	uchar	ccc;
+static	int	shift;
+
+enum
+{
+	/* controller command byte */
+	Cscs1=		(1<<6),		/* scan code set 1 */
+	Cmousedis=	(1<<5),		/* mouse disable */
+	Ckbddis=	(1<<4),		/* kbd disable */
+	Csf=		(1<<2),		/* system flag */
+	Cmouseint=	(1<<1),		/* mouse interrupt enable */
+	Ckbdint=	(1<<0),		/* kbd interrupt enable */
+};
+
+/*
+ *  wait for output no longer busy
+ */
+static int
+outready(void)
+{
+	int tries;
+
+	for(tries = 0; (superio_readctl() & Outbusy); tries++){
+		if(tries > 500)
+			return -1;
+		microdelay(2);
+	}
+	return 0;
+}
+
+/*
+ *  wait for input
+ */
+static int
+inready(void)
+{
+	int tries;
+
+	for(tries = 0; !(superio_readctl() & Inready); tries++){
+		if(tries > 500)
+			return -1;
+		microdelay(2);
+	}
+	return 0;
+}
+
+/*
+ *  send a command to the mouse
+ */
+static int
+mousecmd(int cmd)
+{
+	unsigned int c;
+	int tries;
+
+	c = 0;
+	tries = 0;
+	do{
+		if(tries++ > 2)
+			break;
+		if(outready() < 0)
+			break;
+		superio_writectl(0xD4);
+		if(outready() < 0)
+			break;
+		superio_writedata(cmd);
+		if(outready() < 0)
+			break;
+		if(inready() < 0)
+			break;
+		c = superio_readdata();
+	} while(c == 0xFE || c == 0);
+
+	if(c != 0xFA){
+		print("mouse returns %2.2ux to the %2.2ux command\n", c, cmd);
+		return -1;
+	}
+	return 0;
+}
+
+/*
+ *  ask 8042 to enable the use of address bit 20
+ */
+void
+i8042a20(void)
+{
+	outready();
+	superio_writectl(0xD1);
+	outready();
+	superio_writedata(0xDF);
+	outready();
+}
+
+/*
+ *  ask 8042 to reset the machine
+ */
+void
+i8042reset(void)
+{
+	ushort *s = (ushort*)(KZERO|0x472);
+	int i, x;
+
+	*s = 0x1234;		/* BIOS warm-boot flag */
+
+	/*
+	 *  newer reset the machine command
+	 */
+	outready();
+	superio_writectl(0xFE);
+	outready();
+
+	/*
+	 *  Pulse it by hand (old somewhat reliable)
+	 */
+	x = 0xDF;
+	for(i = 0; i < 5; i++){
+		x ^= 1;
+		outready();
+		superio_writectl(0xD1);
+		outready();
+		superio_writedata(x);	/* toggle reset */
+		microdelay(100);
+	}
+}
+
+/*
+ *  ps/2 mouse message is three bytes
+ *
+ *	byte 0 -	0 0 SDY SDX 1 M R L
+ *	byte 1 -	DX
+ *	byte 2 -	DY
+ *
+ *  shift & left button is the same as middle button
+ */
+static int
+ps2mouseputc(int c)
+{
+	static short msg[3];
+	static int nb;
+	static uchar b[] = {0, 1, 4, 5, 2, 3, 6, 7, 0, 1, 2, 5, 2, 3, 6, 7 };
+	int buttons, dx, dy;
+
+	/* 
+	 *  check byte 0 for consistency
+	 */
+	if(nb==0 && (c&0xc8)!=0x08)
+		return 0;
+
+	msg[nb] = c;
+	if(++nb == 3) {
+		nb = 0;
+		if(msg[0] & 0x10)
+			msg[1] |= 0xFF00;
+		if(msg[0] & 0x20)
+			msg[2] |= 0xFF00;
+
+		buttons = b[(msg[0]&7) | (shift ? 8 : 0)] | keybuttons;
+		dx = msg[1];
+		dy = -msg[2];
+		mousetrack(buttons, dx, dy, 1);
+	}
+	return 0;
+}
+
+/*
+ *  keyboard interrupt
+ */
+void
+kbdintr(void)
+{
+	int s, c, i;
+	static int esc1, esc2;
+	static int caps;
+	static int ctl;
+	static int num;
+	static int collecting, nk;
+	static int alt;
+	static Rune kc[5];
+	int keyup;
+
+	/*
+	 *  get status
+	 */
+	s = superio_readctl();
+	if(!(s&Inready))
+		return;
+
+	/*
+	 *  get the character
+	 */
+	c = superio_readdata();
+
+	/*
+	 *  if it's the mouse...
+	 */
+	if(s & Minready) {
+		ps2mouseputc(c);
+		return;
+	}
+
+	/*
+	 *  e0's is the first of a 2 character sequence
+	 */
+	if(c == 0xe0){
+		esc1 = 1;
+		return;
+	} else if(c == 0xe1){
+		esc2 = 2;
+		return;
+	}
+
+	keyup = c&0x80;
+	c &= 0x7f;
+	if(c > sizeof kbtab){
+/*		print("unknown key %ux\n", c|keyup); */
+		return;
+	}
+
+	if(esc1){
+		c = kbtabesc1[c];
+		esc1 = 0;
+	} else if(esc2){
+		esc2--;
+		return;
+	} else if(shift)
+		c = kbtabshift[c];
+	else
+		c = kbtab[c];
+
+	if(caps && c<='z' && c>='a')
+		c += 'A' - 'a';
+
+	/*
+	 *  keyup only important for shifts
+	 */
+	if(keyup){
+		switch(c){
+		case Latin:
+			alt = 0;
+			break;
+		case Shift:
+			shift = 0;
+			break;
+		case Ctrl:
+			ctl = 0;
+			break;
+		}
+		return;
+	}
+
+	/*
+ 	 *  normal character
+	 */
+	if(!(c & Spec)){
+		if(ctl){
+			if(alt && c == Del)
+				exit(0);
+			c &= 0x1f;
+		}
+		if(!collecting){
+			kbdputc(kbdq, c);
+			return;
+		}
+		kc[nk++] = c;
+		c = latin1(kc, nk);
+		if(c < -1)	/* need more keystrokes */
+			return;
+		if(c != -1)	/* valid sequence */
+			kbdputc(kbdq, c);
+		else	/* dump characters */
+			for(i=0; i<nk; i++)
+				kbdputc(kbdq, kc[i]);
+		nk = 0;
+		collecting = 0;
+		return;
+	} else {
+		switch(c){
+		case Caps:
+			caps ^= 1;
+			return;
+		case Num:
+			num ^= 1;
+			return;
+		case Shift:
+			shift = 1;
+			return;
+		case Latin:
+			alt = 1;
+			collecting = 1;
+			nk = 0;
+			return;
+		case Ctrl:
+			ctl = 1;
+			return;
+		}
+	}
+	kbdputc(kbdq, c);
+}
+
+/*
+ *  set up a ps2 mouse
+ */
+static void
+ps2mouse(void)
+{
+	int x;
+
+	/* enable kbd/mouse xfers and interrupts */
+	x = splhi();
+	ccc &= ~Cmousedis;
+	ccc |= Cmouseint;
+	if(outready() < 0)
+		print("mouse init failed\n");
+	superio_writectl(0x60);
+	if(outready() < 0)
+		print("mouse init failed\n");
+	superio_writedata(ccc);
+	if(outready() < 0)
+		print("mouse init failed\n");
+	superio_writectl(0xA8);
+	if(outready() < 0){
+		splx(x);
+		return;
+	}
+
+	/* make mouse streaming, enabled */
+	mousecmd(0xEA);
+	mousecmd(0xF4);
+	splx(x);
+}
+
+void
+kbdinit(void)
+{
+	int c;
+
+	kbdq = qopen(4*1024, 0, 0, 0);
+	qnoblock(kbdq, 1);
+
+	/* wait for a quiescent controller */
+	while((c = superio_readctl()) & (Outbusy | Inready))
+		if(c & Inready)
+			superio_readdata();
+
+	/* get current controller command byte */
+	superio_writectl(0x20);
+	if(inready() < 0){
+		print("kbdinit: can't read ccc\n");
+		ccc = 0;
+	} else
+		ccc = superio_readdata();
+
+	/* enable kbd xfers and interrupts */
+	/* disable mouse */
+	ccc &= ~Ckbddis;
+	ccc |= Csf | Ckbdint | Cscs1 | Cmousedis;
+	if(outready() < 0)
+		print("kbd init failed\n");
+	superio_writectl(0x60);
+	if(outready() < 0)
+		print("kbd init failed\n");
+	superio_writedata(ccc);
+	outready();
+
+	/* Assume ps2 mouse */
+	ps2mouse();
+}
--- /dev/null
+++ b/os/js/l.s
@@ -1,0 +1,560 @@
+#include "mem.h"
+
+#define	SYSPSR	(PSREF|PSRET|PSRSUPER|SPL(15))
+#define	NOOP	ORN R0, R0; ORN R0, R0; ORN R0, R0; ORN R0, R0; ORN R0, R0
+#define	FLUSH	WORD	$0x81d80000	/* IFLUSH (R0) */
+
+#define	MMUASI	0x4
+#define	TLBASI	0x3
+#define	IFLUSHASI 0x36
+#define	DFLUSHASI 0x37
+#define	PHYSASI	0x20	/* MMU bypass */
+#define	CTLASI	0x20	/* 0x2F on bigger sun4M */
+
+TEXT	start(SB), $-4
+	/* get virtual, fast */
+
+	/* copy ROM's L1 page table entries, mapping VA:0 and VA:KZERO to PA:0 */
+	MOVW	$setSB(SB), R2
+
+	MOVW	$CTPR, R7
+	MOVW	(R7, MMUASI), R5
+	SLL	$4, R5, R5
+	MOVW	(R5, PHYSASI), R5	/* context table entry 0 */
+	SRL	$4, R5, R5
+	SLL	$8, R5, R5		/* to physical pointer */
+
+	MOVW	$(((KZERO>>24)&0xFF)*4), R4	/* KZERO base in level 1 ptab */
+	MOVW	(R5, PHYSASI), R6	/* L1 region 0 set by boot */
+	ADD	R4,R5,R10
+	MOVW	R6, (R10, PHYSASI)	/* map KZERO */
+
+	ADD	$4, R5
+	MOVW	(R5, PHYSASI), R6
+	ADD	R4, R5, R10
+	MOVW	R6, (R10, PHYSASI)	/* map KZERO+16Mbytes */
+
+	/* now mapped correctly.  jmpl to where we want to be */
+
+	MOVW	$startvirt(SB), R7
+	JMPL	(R7)
+	RETURN			/* can't get here */
+
+TEXT	startvirt(SB), $-4
+	MOVW	$edata(SB),R9
+	MOVW	$end(SB),R10
+clrbss:
+	MOVW	R0,(R9)
+	ADD	$4, R9
+	CMP	R9, R10
+	BNE	clrbss
+	NOOP
+
+	MOVW	$rom(SB), R7
+	MOVW	R8, (R7)	/* romvec passed in %i0==R8 */
+
+	/* turn off the cache but ensure ITBR enabled */
+	MOVW	(R0, MMUASI), R7
+	ANDN	$(ENABCACHE|ITBRDISABLE), R7
+	MOVW	R7, (R0, MMUASI)
+
+	FLUSH
+	NOOP
+
+
+	MOVW	$MID, R7	/* disable Sbus DMA */
+	MOVW	(R7, PHYSASI), R8
+	ANDN	$(0x1E<<15), R8
+	MOVW	R8, (R7, PHYSASI)
+
+	MOVW	$BOOTSTACK, R1
+
+	MOVW	$(SPL(0xF)|PSREF|PSRSUPER), R7
+ 	OR	$PSRET, R7	/* allow rom use while debugging ... */
+	MOVW	R7, PSR
+
+	MOVW	$(0x35<<22), R7		/* NVM OFM DZM NS */
+	MOVW	R7, fsr+0(SB)
+	MOVW	$fsr+0(SB), R8
+	MOVW	(R8), FSR
+	FMOVD	$0.5, F26		/* 0.5 -> F26 */
+	FSUBD	F26, F26, F24		/* 0.0 -> F24 */
+	FADDD	F26, F26, F28		/* 1.0 -> F28 */
+	FADDD	F28, F28, F30		/* 2.0 -> F30 */
+
+	FMOVD	F24, F0
+	FMOVD	F24, F2
+	FMOVD	F24, F4
+	FMOVD	F24, F6
+	FMOVD	F24, F8
+	FMOVD	F24, F10
+	FMOVD	F24, F12
+	FMOVD	F24, F14
+	FMOVD	F24, F16
+	FMOVD	F24, F18
+	FMOVD	F24, F20
+	FMOVD	F24, F22
+
+	MOVW	$mach0(SB), R(MACH)
+/*	MOVW	$0x8, R7 /**/
+	MOVW	R0, WIM
+
+	JMPL	main(SB)
+	MOVW	(R0), R0
+	RETURN
+
+TEXT	call0(SB), $-4
+	JMPL	(R0)
+	JMPL	(R0)
+	JMPL	(R0)
+	JMPL	(R0)
+	RETURN
+
+TEXT	getcinfo(SB), $0
+	MOVW	(R7, 0x0C), R7
+	RETURN
+
+TEXT	flushiline(SB), $0
+	MOVW	R0, (R7, 0x0C)
+	NOOP
+	RETURN
+
+TEXT	flushdline(SB), $0
+	MOVW	R0, (R7, 0x0E)
+	NOOP
+	RETURN
+
+TEXT	getphys(SB), $0
+
+	MOVW	(R7, PHYSASI), R7
+	RETURN
+
+TEXT	putphys(SB), $0
+
+	MOVW	4(FP), R8
+	MOVW	R8, (R7, PHYSASI)
+	RETURN
+
+TEXT	getrmmu(SB), $0
+
+	MOVW	(R7, MMUASI), R7
+	RETURN
+
+TEXT	putrmmu(SB), $0
+
+	MOVW	4(FP), R8
+	MOVW	R8, (R7, MMUASI)
+	RETURN
+
+TEXT	getpcr(SB), $0
+
+	MOVW	(R0, MMUASI), R7
+	RETURN
+
+TEXT	setpcr(SB), $0
+
+	MOVW	R7, (R0, MMUASI)
+	FLUSH			/* `strongly recommended' after STA to PCR */
+	NOOP
+	RETURN
+
+TEXT	_tas(SB), $0
+
+	TAS	(R7), R7		/* LDSTUB, thank you ken */
+	RETURN
+
+#ifdef notdef
+TEXT	_tas(SB), $0			/* it seems we must be splhi */
+
+	MOVW	PSR, R8
+	MOVW	$SYSPSR, R9
+	MOVW	R9, PSR
+	NOOP
+	TAS	(R7), R7		/* LDSTUB, thank you ken */
+	MOVW	R8, PSR
+	NOOP
+	RETURN
+#endif
+
+TEXT	softtas(SB), $0			/* all software; avoid LDSTUB */
+
+	MOVW	PSR, R8
+	MOVW	$SYSPSR, R9
+	MOVW	R9, PSR
+	NOOP
+	MOVB	(R7), R10
+	CMP	R10, R0
+	BE	gotit
+	/* cache is write through, no need to flush */
+	MOVW	$0xFF, R7
+	MOVW	R8, PSR
+	NOOP
+	RETURN
+
+gotit:
+	MOVW	$0xFF, R10
+	MOVB	R10, (R7)
+	/* cache is write through, no need to flush */
+	MOVW	$0, R7
+	MOVW	R8, PSR
+	NOOP
+	RETURN
+
+TEXT	spllo(SB), $0
+
+	MOVW	PSR, R7
+	MOVW	R7, R10
+	ANDN	$SPL(15), R10
+	MOVW	R10, PSR
+	NOOP
+	RETURN
+
+TEXT	splhi(SB), $0
+
+	MOVW	R15, 4(R(MACH))	/* save PC in m->splpc */
+	MOVW	PSR, R7
+	MOVW	R7, R10
+	OR	$SPL(15), R10
+	MOVW	R10, PSR
+	NOOP
+	RETURN
+
+TEXT	splxpc(SB), $0
+
+	MOVW	R7, PSR		/* BUG: book says this is buggy */
+	NOOP
+	RETURN
+
+
+TEXT	splx(SB), $0
+
+	MOVW	R15, 4(R(MACH))	/* save PC in m->splpc */
+	MOVW	R7, PSR		/* BUG: book says this is buggy */
+	NOOP
+	RETURN
+TEXT	spldone(SB), $0
+
+	RETURN
+
+TEXT	rfnote(SB), $0
+
+	MOVW	R7, R1			/* 1st arg is &uregpointer */
+	ADD	$4, R1			/* point at ureg */
+	JMP	restore
+
+TEXT	traplink(SB), $-4
+
+	/* R8 to R23 are free to play with */
+	/* R17 contains PC, R18 contains nPC */
+	/* R19 has PSR loaded from vector code */
+
+	ANDCC	$PSRPSUPER, R19, R0
+	BE	usertrap
+
+kerneltrap:
+	/*
+	 * Interrupt or fault from kernel
+	 */
+	ANDN	$7, R1, R20			/* dbl aligned */
+	MOVW	R1, (0-(4*(32+6))+(4*1))(R20)	/* save R1=SP */
+	/* really clumsy: store these in Ureg so can be restored below */
+	MOVW	R2, (0-(4*(32+6))+(4*2))(R20)	/* SB */
+	MOVW	R5, (0-(4*(32+6))+(4*5))(R20)	/* USER */
+	MOVW	R6, (0-(4*(32+6))+(4*6))(R20)	/* MACH */
+	SUB	$(4*(32+6)), R20, R1
+
+trap1:
+	MOVW	Y, R20
+	MOVW	R20, (4*(32+0))(R1)		/* Y */
+	MOVW	TBR, R20
+	MOVW	R20, (4*(32+1))(R1)		/* TBR */
+	AND	$~0x1F, R19			/* force CWP=0 */
+	MOVW	R19, (4*(32+2))(R1)		/* PSR */
+	MOVW	R18, (4*(32+3))(R1)		/* nPC */
+	MOVW	R17, (4*(32+4))(R1)		/* PC */
+	MOVW	R0, (4*0)(R1)
+	MOVW	R3, (4*3)(R1)
+	MOVW	R4, (4*4)(R1)
+	MOVW	R7, (4*7)(R1)
+	RESTORE	R0, R0
+	/* now our registers R8-R31 are same as before trap */
+	/* save registers two at a time */
+	MOVD	R8, (4*8)(R1)
+	MOVD	R10, (4*10)(R1)
+	MOVD	R12, (4*12)(R1)
+	MOVD	R14, (4*14)(R1)
+	MOVD	R16, (4*16)(R1)
+	MOVD	R18, (4*18)(R1)
+	MOVD	R20, (4*20)(R1)
+	MOVD	R22, (4*22)(R1)
+	MOVD	R24, (4*24)(R1)
+	MOVD	R26, (4*26)(R1)
+	MOVD	R28, (4*28)(R1)
+	MOVD	R30, (4*30)(R1)
+	/* SP and SB and u and m are already set; away we go */
+	MOVW	R1, R7		/* pointer to Ureg */
+	SUB	$8, R1
+	MOVW	$SYSPSR, R8
+	MOVW	R8, PSR
+	NOOP
+	JMPL	trap(SB)
+
+	ADD	$8, R1
+restore:
+	MOVW	(4*(32+2))(R1), R8		/* PSR */
+	MOVW	R8, PSR
+	NOOP
+
+	MOVD	(4*30)(R1), R30
+	MOVD	(4*28)(R1), R28
+	MOVD	(4*26)(R1), R26
+	MOVD	(4*24)(R1), R24
+	MOVD	(4*22)(R1), R22
+	MOVD	(4*20)(R1), R20
+	MOVD	(4*18)(R1), R18
+	MOVD	(4*16)(R1), R16
+	MOVD	(4*14)(R1), R14
+	MOVD	(4*12)(R1), R12
+	MOVD	(4*10)(R1), R10
+	MOVD	(4*8)(R1), R8
+	SAVE	R0, R0
+	MOVD	(4*6)(R1), R6
+	MOVD	(4*4)(R1), R4
+	MOVD	(4*2)(R1), R2
+	MOVW	(4*(32+0))(R1), R20		/* Y */
+	MOVW	R20, Y
+	MOVW	(4*(32+4))(R1), R17		/* PC */
+	MOVW	(4*(32+3))(R1), R18		/* nPC */
+	MOVW	(4*1)(R1), R1	/* restore R1=SP */
+	RETT	R17, R18
+	
+usertrap:
+	/*
+	 * Interrupt or fault from user
+	 */
+	MOVW	R1, R8
+	MOVW	R2, R9
+	MOVW	$setSB(SB), R2
+	MOVW	$(USERADDR+BY2PG), R1
+	MOVW	R8, (0-(4*(32+6))+(4*1))(R1)	/* save R1=SP */
+	MOVW	R9, (0-(4*(32+6))+(4*2))(R1)	/* save R2=SB */
+	MOVW	R5, (0-(4*(32+6))+(4*5))(R1)	/* save R5=USER */
+	MOVW	R6, (0-(4*(32+6))+(4*6))(R1)	/* save R6=MACH */
+	MOVW	$USERADDR, R(USER)
+	MOVW	$mach0(SB), R(MACH)
+	SUB	$(4*(32+6)), R1
+	JMP	trap1
+
+TEXT	puttbr(SB), $0
+	MOVW	R7, TBR
+	NOOP
+	RETURN
+
+TEXT	gettbr(SB), $0
+
+	MOVW	TBR, R7
+	RETURN
+
+TEXT	r1(SB), $0
+
+	MOVW	R1, R7
+	RETURN
+
+TEXT	getwim(SB), $0
+
+	MOVW	WIM, R7
+	RETURN
+
+TEXT	setlabel(SB), $0
+
+	MOVW	R1, (R7)
+	MOVW	R15, 4(R7)
+	MOVW	$0, R7
+	RETURN
+
+TEXT	gotolabel(SB), $0
+
+	MOVW	(R7), R1
+	MOVW	4(R7), R15
+	MOVW	$1, R7
+	RETURN
+
+TEXT	getpsr(SB), $0
+
+	MOVW	PSR, R7
+	RETURN
+
+TEXT	setpsr(SB), $0
+
+	MOVW	R7, PSR
+	NOOP
+	RETURN
+
+TEXT	savefpregs(SB), $0
+
+	MOVD	F0, (0*4)(R7)
+	MOVD	F2, (2*4)(R7)
+	MOVD	F4, (4*4)(R7)
+	MOVD	F6, (6*4)(R7)
+	MOVD	F8, (8*4)(R7)
+	MOVD	F10, (10*4)(R7)
+	MOVD	F12, (12*4)(R7)
+	MOVD	F14, (14*4)(R7)
+	MOVD	F16, (16*4)(R7)
+	MOVD	F18, (18*4)(R7)
+	MOVD	F20, (20*4)(R7)
+	MOVD	F22, (22*4)(R7)
+	MOVD	F24, (24*4)(R7)
+	MOVD	F26, (26*4)(R7)
+	MOVD	F28, (28*4)(R7)
+	MOVD	F30, (30*4)(R7)
+	MOVW	FSR, (34*4)(R7)
+
+	MOVW	PSR, R8
+	ANDN	$PSREF, R8
+	MOVW	R8, PSR
+	RETURN
+
+TEXT	savefsr(SB), $0
+	MOVW	FSR, 0(R7)
+	RETURN
+
+TEXT	restfsr(SB), $0
+	MOVW	0(R7), FSR
+	RETURN	
+
+
+TEXT	fpinit(SB), $0
+	MOVW	PSR, R8
+	OR	$PSREF, R8
+	MOVW	R8, PSR
+	RETURN
+
+TEXT	disabfp(SB), $0
+
+	MOVW	PSR, R8
+	ANDN	$PSREF, R8
+	MOVW	R8, PSR
+	RETURN
+
+TEXT	restfpregs(SB), $0
+
+	MOVW	PSR, R8
+	OR	$PSREF, R8
+	MOVW	R8, PSR
+
+	NOOP			/* wait for PSR to quiesce */
+
+
+	MOVD	(0*4)(R7), F0
+	MOVD	(2*4)(R7), F2
+	MOVD	(4*4)(R7), F4
+	MOVD	(6*4)(R7), F6
+	MOVD	(8*4)(R7), F8
+	MOVD	(10*4)(R7), F10
+	MOVD	(12*4)(R7), F12
+	MOVD	(14*4)(R7), F14
+	MOVD	(16*4)(R7), F16
+	MOVD	(18*4)(R7), F18
+	MOVD	(20*4)(R7), F20
+	MOVD	(22*4)(R7), F22
+	MOVD	(24*4)(R7), F24
+	MOVD	(26*4)(R7), F26
+	MOVD	(28*4)(R7), F28
+	MOVD	(30*4)(R7), F30
+	MOVW	(34*4)(R7), FSR
+
+	ANDN	$PSREF, R8
+	MOVW	R8, PSR
+	RETURN
+
+TEXT	getfpq(SB), $0
+
+	MOVW	R7, R8	/* must be D aligned */
+	MOVW	$fsr+0(SB), R9
+	MOVW	$0, R7
+getfpq1:
+	MOVW	FSR, (R9)
+	MOVW	(R9), R10
+	ANDCC	$(1<<13), R10		/* queue not empty? */
+	BE	getfpq2
+	MOVW	(R8), R0		/* SS2 bug fix */
+	MOVD	FQ, (R8)
+	ADD	$1, R7
+	ADD	$8, R8
+	BA	getfpq1
+getfpq2:
+	RETURN
+
+TEXT	getfsr(SB), $0
+	MOVW	$fsr+0(SB), R7
+	MOVW	FSR, (R7)
+	MOVW	(R7), R7
+	RETURN
+
+TEXT	clearftt(SB), $0
+	MOVW	R7, fsr+0(SB)
+	MOVW	$fsr+0(SB), R7
+	MOVW	(R7), FSR
+	FMOVF	F0, F0
+	RETURN
+
+TEXT	getcallerpc(SB), $-4
+	MOVW	0(R1), R7
+	RETURN
+
+TEXT	icflush(SB), $-4
+JMPL	(R0)
+	MOVW	R0, (R0, IFLUSHASI)
+	FLUSH		/* flush prefetch */
+	NOOP
+	RETURN
+
+TEXT	dcflush(SB), $0
+
+	MOVW	R0, (R0, DFLUSHASI)	/* can only flush the lot */
+	RETURN
+
+TEXT	flushtlbpage(SB), $0
+
+	AND	$(~(BY2PG-1)), R7	/* type 0 */
+	MOVW	R0, (R7, TLBASI)
+	RETURN
+
+TEXT	flushtlbctx(SB), $0
+
+	MOVW	$0x300, R7
+	MOVW	R0, (R7, TLBASI)
+	RETURN
+
+TEXT	flushtlb(SB), $0
+
+	MOVW	$0x400, R7
+	MOVW	R0, (R7, TLBASI)
+	RETURN
+
+GLOBL	mach0+0(SB), $MACHSIZE
+GLOBL	fsr+0(SB), $BY2WD
+
+/*
+ * Interface to OPEN BOOT ROM.  Must save and restore state because
+ * of different calling conventions.  We don't use it, but it's here
+ * for reference..
+ */
+
+TEXT	call_openboot(SB), $16
+	MOVW	R1, R14		/* save my SP in their SP */
+	MOVW	R2, sb-4(SP)
+	MOVW	R(MACH), mach-8(SP)
+	MOVW	R(USER), user-12(SP)
+	MOVW	param1+4(FP), R8
+	MOVW	param2+8(FP), R9
+	MOVW	param3+12(FP), R10
+	MOVW	param4+16(FP), R11
+	JMPL	(R7)
+	MOVW	R14, R1		/* restore my SP */
+	MOVW	user-12(SP), R(USER)
+	MOVW	mach-8(SP), R(MACH)
+	MOVW	sb-4(SP), R2
+	MOVW	R8, R7		/* move their return value into mine */
+	RETURN
--- /dev/null
+++ b/os/js/main.c
@@ -1,0 +1,564 @@
+#include	"u.h"
+#include	"../port/lib.h"
+#include	"mem.h"
+#include	"dat.h"
+#include	"fns.h"
+#include	"io.h"
+#include	"version.h"
+
+Mach *m = &mach0;
+Proc *up;
+int cflag;
+
+ulong cachetable[1024];
+
+Sysint	*sysintr;
+struct {
+	uchar	format;
+	uchar	type;
+	uchar	ea[6];
+	uchar	pad[32-8];
+} idprom;
+
+int	cpuserver;
+ulong	bank[8];
+uchar	mempres[64];
+char	fbstr[32];
+ulong	fbslot;
+int	usecg6;
+Label	catch;
+uchar	*sp;
+
+int cold=1;
+
+typedef struct Sysparam Sysparam;
+struct Sysparam
+{
+	int	id;		/* Model type from id prom */
+	char	*name;		/* System name */
+	char	ss2;		/* Is Sparcstation 2? */
+	int	vacsize;	/* Cache size */
+	int	vacline;	/* Cache line size */
+	int	ncontext;	/* Number of MMU contexts */
+	char	cachebug;	/* Machine needs cache bug work around */
+	int	nbank;		/* Number of banks of memory */
+	int	banksize;	/* Maximum Mbytes per bank */
+	int	pcnt;		/* percent of mem for kernel? */
+}
+sysparam[] =
+{
+	{ 0xFF, "unknown Sun4M",0, 0, 0,  64, 0, 4, 32 ,0},
+	{ 0x80, "JavaStation uSparcII",0, 0, 0,  256, 0, 4, 32 ,2},
+	{ 0 }
+};
+Sysparam *sparam;
+
+void
+doc(char *m)
+{
+	print("%s\n", m);
+}
+
+static void poolsizeinit(void);
+
+void
+main(void)
+{
+
+
+	machinit();
+	trapinit();
+	quotefmtinstall();
+	confinit();
+	xinit();
+	mmuinit();
+	intrinit();
+	clockinit();
+	printinit();
+	screeninit();
+	ioinit();
+	doc("ioinit..."); 
+	ns16552install();
+	poolsizeinit();
+	doc("ns16552install...");
+	kbdinit();
+	doc("kbdinit...");
+	cacheinit();
+	doc("cacheinit...");
+	procinit();
+	doc("procinit...");
+	putphys(MID, 0x1F<<16);	/* enable arbitration */
+	links();
+	doc("links");
+	chandevreset();
+	doc("chandevreset...");
+
+	print("\nInferno Operating System\n");
+	print("%s-%s \n\n",VERSION, conffile);
+	print("JIT Compilation Mode = %d\n",cflag);
+
+	userinit();
+	doc("userinit...");
+
+	 /* clear pending processor interrupts */
+	putphys(PROCINTCLR, (~0<<17)|(1<<15));
+	print("berore schedinit\n");
+	schedinit();
+}
+
+extern int main_pool_pcnt;
+extern int heap_pool_pcnt;
+extern int image_pool_pcnt;
+
+static void
+poolsizeinit(void)
+{
+	ulong nb = conf.npage*BY2PG;
+
+	print("Total memory available: %ld K\n",nb/1024);
+	poolsize(mainmem, (nb*main_pool_pcnt)/100, 0);
+	poolsize(heapmem, (nb*heap_pool_pcnt)/100, 0);
+	poolsize(imagmem, (nb*image_pool_pcnt)/100, 1);
+}
+
+void
+intrinit(void)
+{
+	KMap *k;
+
+	 /* clear fault status */
+	getphys(AFSR);
+
+	k = kmappa(SYSINTR, PTEIO|PTENOCACHE);
+	sysintr = (Sysint*)VA(k);
+
+	 /* mask all interrupts */
+	sysintr->maskset = ~0;
+
+   	 /* allow these */
+	sysintr->maskclr=MaskAllIntr|MEIntr|MSIIntr|EMCIntr|EtherIntr|KbdIntr;
+
+	 /* clear pending processor interrupts */
+	putphys(PROCINTCLR, (~0<<17)|(1<<15));
+
+}
+
+void
+systemreset(void)
+{
+	microdelay(200);
+	putphys(SYSCTL, getphys(SYSCTL)|1);	/* power on reset */
+}
+
+void
+machinit(void)
+{
+	memset(m, 0, sizeof(Mach));
+}
+
+void
+ioinit(void)
+{
+	KMap *k;
+	uchar *sindex;	/* superio index */
+	uchar *sdata;	/* superio data */
+	uchar *mkctl;	/* superio mouse/kbd ctl register */
+	uchar *mkdata;	/* superio mouse/kbd data register */
+
+
+	 /* enable the uart's on the superio chip */
+	k = kmappa(SUPERIO_PHYS_PAGE, PTEIO|PTENOCACHE);
+	sindex = (uchar*)(VA(k)+SUPERIO_INDEX_OFFSET);
+	sdata = (uchar*)(VA(k)+SUPERIO_DATA_OFFSET);
+	mkdata = (uchar*)(VA(k)+SUPERIO_MOUSE_KBD_DATA_PORT);
+	mkctl = (uchar*)(VA(k)+SUPERIO_MOUSE_KBD_CTL_PORT);
+
+	superioinit(VA(k),sindex,sdata,mkctl,mkdata);
+	doc("superioinit...");
+}
+
+void
+init0(void)
+{
+	Osenv *o;
+
+	up->nerrlab = 0;
+
+	print("before spllo");
+
+	spllo();
+
+	print("Sun Sparc %s\n", sparam->name);
+	print("bank 0: %ldM  1: %ldM\n", bank[0], bank[1]);
+	print("frame buffer id %lux slot %ld %s\n",conf.monitor,fbslot,fbstr);
+
+
+	if(waserror())
+		panic("init0");
+
+	/*
+	 * These are o.k. because rootinit is null.
+	 * Then early kproc's will have a root and dot.
+	 */
+	o = up->env;
+	o->pgrp->slash = namec("#/", Atodir, 0, 0);
+	cnameclose(o->pgrp->slash->name);
+	o->pgrp->slash->name = newcname("/");
+	o->pgrp->dot = cclone(o->pgrp->slash);
+
+	chandevinit();
+	poperror();
+	disinit("/osinit.dis");
+}
+
+
+void
+userinit(void)
+{
+	Proc *p;
+	Osenv *o;
+
+	p = newproc();
+	o = p->env;
+
+	o->fgrp = newfgrp(nil);
+	o->pgrp = newpgrp();
+	kstrdup(&o->user, eve);
+	strcpy(p->text,"interp");
+
+	p->fpstate = FPINIT;
+	fpinit();
+
+	/*
+	 * Kernel Stack
+	 */
+	p->sched.pc = (ulong)init0;
+	p->sched.sp = (ulong)p->kstack+KSTACK-8;
+	p->sched.sp &= ~7;		/* SP must be 8-byte aligned */
+
+	ready(p);
+}
+
+uchar *
+pusharg(char *p)
+{
+	int n;
+
+	n = strlen(p)+1;
+	sp -= n;
+	memmove(sp, p, n);
+	return sp;
+}
+
+void
+exit(int ispanic)
+{
+	USED(ispanic);
+
+	spllo();
+	print("cpu exiting\n");
+
+	/* Shutdown running devices */
+	chandevshutdown();
+
+	microdelay(500);
+	systemreset();
+}
+
+void
+reboot(void)
+{
+	exit(0);
+}
+
+void
+halt(void)
+{
+	spllo();
+	print("cpu halted\n");
+	microdelay(500);
+	for(;;);
+}
+
+int
+probemem(ulong addr)
+{
+	ulong pcr, save0;
+	int works;
+
+	save0 = getphys(0);
+	pcr = getpcr()|NOFAULT;
+	works = 0;
+	setpcr(pcr & ~MEMPCHECK);
+	putphys(addr, ~addr);
+	if(addr)
+		putphys(0, 0x89ABCDEF);
+	if(getphys(addr) == ~addr){
+		setpcr(pcr);
+		putphys(addr, addr);
+		if(addr)
+			putphys(0, 0x89ABCDEF);
+		if(getphys(addr) == addr)
+			works = 1;
+	}
+	setpcr(pcr & ~NOFAULT);
+	putphys(0, save0);
+	getphys(AFSR);	/* clear fault status */
+	getrmmu(SFSR);	/* clear fault status */
+	return works;
+}
+
+/*
+ * this assumes that if a bank is not empty,
+ * its first slot is filled.
+ *
+ * ../port/alloc.c and ../port/page.c
+ * need to be changed to support more than two banks.
+ */
+void
+scanbank(ulong base, uchar *mempres, int n)
+{
+	int i;
+	ulong addr, npg;
+
+	npg = 0;
+	for(i=0; i<n; i++){
+		mempres[i] = 0;
+		addr = base + i*MB;
+		if(!probemem(addr))
+			break;
+		if(addr != base) {
+			/* check for mirrors */
+			putphys(addr, addr);
+			if(getphys(base) == addr)
+				break;
+		}
+		mempres[i] = 1;
+		npg += MB/BY2PG;
+	}
+	if(npg){
+		if(conf.npage0 == 0){
+			conf.base0 = base;
+			conf.npage0 = npg;
+		}else if(conf.npage1 < npg){
+			conf.base1 = base;
+			conf.npage1 = npg;
+		}
+	}
+}
+
+void
+physcopyin(void *d, ulong s, int n)
+{
+	int i, j;
+	ulong w;
+
+	for(i=0; i<n; i+=sizeof(ulong)) {
+		w = getphys(s+i);
+		j = n-i;
+		if(j > sizeof(ulong))
+			j = sizeof(ulong);
+		memmove((uchar*)d+i, &w, j);
+	}
+}
+
+Conf	conf;
+
+void
+confinit(void)
+{
+	ulong i;
+	ulong ktop;
+
+	conf.monitor = 0;
+
+	conf.nmach = 1;
+	if(conf.nmach > MAXMACH)
+		panic("confinit");
+
+	/* fetch ID prom */
+	physcopyin(&idprom, NVR_PHYS+IDOFF, sizeof(idprom));
+	if(idprom.format!=1 || (idprom.type&0xF0)!=0x80)
+		*(ulong*)~0 = 0;	/* not a new generation sparc; die! */
+
+	for(sparam = sysparam; sparam->id; sparam++)
+		if(sparam->id == idprom.type)
+			break;
+
+	/* First entry in the table is the default */
+	if(sparam->id == 0)
+		sparam = sysparam;
+
+	conf.ss2 = sparam->ss2;
+	conf.vacsize = sparam->vacsize;
+	conf.vaclinesize = sparam->vacline;
+	conf.ncontext = sparam->ncontext;
+	conf.ss2cachebug = sparam->cachebug;
+
+	for(i=0; i<sparam->nbank; i++)
+		if(probemem(i*sparam->banksize*MB))
+			scanbank(i*sparam->banksize*MB, mempres,
+				sparam->banksize);
+
+	bank[0] = conf.npage0*BY2PG/MB;
+	bank[1] = conf.npage1*BY2PG/MB;
+
+	if(bank[1] == 0){
+		/*
+		 * This split of memory into 2 banks fools the allocator into
+		 * allocating low memory pages from bank 0 for the ethernet
+		 * since it has only a 24bit address *counter.
+		 * NB. Suns must have at LEAST 8Mbytes.
+		 */
+		conf.npage1 = conf.npage0 - (8*MB)/BY2PG;
+		conf.base1 = conf.base0 + 8*MB;
+		conf.npage0 = (8*MB)/BY2PG;
+		bank[1] = bank[0]-8;
+		bank[0] = 8;
+	}
+
+	conf.npage = conf.npage0+conf.npage1;
+
+	ktop = PGROUND((ulong)end);
+	ktop = PADDR(ktop);
+	conf.npage0 -= ktop/BY2PG;
+	conf.base0 += ktop;
+
+	conf.nproc = 100 + ((conf.npage*BY2PG)/MB)*5;
+	conf.copymode = 0;		/* copy on write */
+	conf.arp = 32;
+	conf.ialloc = (((conf.npage*(100-sparam->pcnt))/100)/2)*BY2PG;
+
+	eve = strdup("inferno");
+
+#ifdef notdef
+        /* XXX - Eric - Autoconfigure memory */
+	/* XXX - Tad: 8 eigths, total... */
+	mainmem->maxsize = (conf.npage*BY2PG)/8;
+	heapmem->maxsize = ((conf.npage*BY2PG)*5)/8;
+	imagmem->maxsize = ((conf.npage*BY2PG)*2)/8;
+#endif
+}
+
+/*
+ *  set up the lance
+ */
+void
+lancesetup(Lance *lp)
+{
+	KMap *k;
+	DMAdev *dma;
+	ulong pa, va;
+	int i;
+
+	k = kmappa(ETHER, PTEIO|PTENOCACHE);
+	lp->rdp = (void*)(VA(k)+0);
+	lp->rap = (void*)(VA(k)+2);
+	for(i=0; i<6; i++)
+		lp->ea[i] = idprom.ea[i];
+
+	lp->lognrrb = 7;
+	lp->logntrb = 7;
+	lp->nrrb = 1<<lp->lognrrb;
+	lp->ntrb = 1<<lp->logntrb;
+	lp->sep = 1;
+	lp->busctl = BSWP | ACON | BCON;
+
+	/*
+	 * Allocate area for lance init block and descriptor rings
+	 */
+	pa = PADDR(xspanalloc(BY2PG, BY2PG, 0));
+
+	/* map at LANCESEGM */
+	va = kmapdma(pa, BY2PG);
+	lp->lanceram = (ushort*)va;
+	lp->lm = (Lancemem*)va;
+
+	/*
+	 * Allocate space in host memory for the io buffers.
+	 */
+	i = (lp->nrrb+lp->ntrb)*sizeof(Lancepkt);
+	i = (i+(BY2PG-1))/BY2PG;
+	pa = PADDR(xspanalloc(i*BY2PG, BY2PG, 0));
+	va = kmapdma(pa, i*BY2PG);
+
+	lp->lrp = (Lancepkt*)va;
+	lp->rp = (Lancepkt*)va;
+	lp->ltp = lp->lrp+lp->nrrb;
+	lp->tp = lp->rp+lp->nrrb;
+
+	k = kmappa(DMA, PTEIO|PTENOCACHE);
+	dma = (DMAdev*)VA(k);
+	dma->base = 0xff;
+
+	/*
+	 * for now, let's assume the ROM has left the results of its
+	 * auto-sensing
+	 */
+#ifdef notdef
+	if(dma->ecsr & E_TP_select)
+		print("Twisted pair ethernet\n");
+	else
+		print("AUI ethernet\n");
+#endif
+	microdelay(1);
+	dma->ecsr |= E_Int_en|E_Invalidate|E_Dsbl_wr_inval|E_Dsbl_rd_drn;
+	microdelay(1);
+}
+
+static void
+linkproc(void)
+{
+	spllo();
+	(*up->kpfun)(up->arg);
+}
+
+void
+kprocchild(Proc *p, void (*func)(void*), void *arg)
+{
+	p->sched.pc = (ulong)linkproc;
+	p->sched.sp = (ulong)p->kstack+KSTACK-8;
+
+	p->kpfun = func;
+	p->arg = arg;
+}
+
+
+void
+FPsave(void *f)		/* f should be a FPenv */
+{
+	savefsr(f);
+}
+
+void
+FPrestore(void *f)	/* f should be a FPenv */
+{
+	restfsr(f);
+}
+
+void
+fpsave(FPU *f)
+{
+	savefpregs( f );	
+}
+
+void
+fprestore(FPU *f)
+{
+	restfpregs(f);
+}
+
+int
+islo(void)
+{
+	int val;
+	val =  (getpsr()&SPL(15)) == 0;
+
+	return val;
+}
+
+void
+setvec(void)
+{
+	/* XXX - Tad: eventually implement this */
+}
--- /dev/null
+++ b/os/js/mem.h
@@ -1,0 +1,149 @@
+/*
+ * Memory and machine-specific definitions.  Used in C and assembler.
+ */
+
+/*
+ * Sizes
+ */
+
+#define	BI2BY		8			/* bits per byte */
+#define BI2WD		32			/* bits per word */
+#define	BY2WD		4			/* bytes per word */
+#define BY2V		8			/* bytes per double word */
+#define	BY2PG		4096			/* bytes per page */
+#define	WD2PG		(BY2PG/BY2WD)		/* words per page */
+#define	PGSHIFT		12			/* log(BY2PG) */
+#define ROUND(s, sz)	(((s)+(sz-1))&~(sz-1))
+#define PGROUND(s)	ROUND(s,BY2PG)
+
+#define	MAXMACH		1			/* max # cpus system can run */
+
+/*
+ * Time
+ */
+#define	HZ		50			/* clock frequency */
+#define	MS2HZ		(1000/HZ)		/* millisec per clock tick */
+#define	TK2SEC(t)	((t)/HZ)		/* ticks to seconds */
+#define	MS2TK(t)	((((ulong)(t))*HZ)/1000)	/* milliseconds to ticks */
+
+/*
+ * PSR bits
+ */
+#define	PSREC		0x00002000
+#define	PSREF		0x00001000
+#define PSRSUPER	0x00000080
+#define PSRPSUPER	0x00000040
+#define	PSRET		0x00000020
+#define SPL(n)		(n<<8)
+
+/*
+ * Magic registers
+ */
+
+#define	MACH		6		/* R6 is m-> */
+#define	USER		5		/* R5 is u-> */
+
+/*
+ * Fundamental addresses
+ */
+
+#define	USERADDR	0xE0000000
+#define	UREGADDR	(USERADDR+BY2PG-((32+6)*BY2WD))
+#define	BOOTSTACK	(KTZERO-0*BY2PG)
+#define	TRAPS		(KTZERO-2*BY2PG)
+
+/*
+ * Reference MMU registers (ASI 4)
+ */
+#define	PCR		0x000
+#define	CTPR		0x100
+#define	CXR		0x200
+#define	SFSR		0x300
+#define	SFAR		0x400
+
+/*
+ * Processor Control Register
+ */
+#define	ITBRDISABLE	(1<<16)
+#define	BOOTMODE	(1<<14)	/* `must be cleared for normal operation' */
+#define	MEMPCHECK	(1<<12)	/* check parity */
+#define	ENABCACHE	(3<<8)	/* I & D caches */
+#define	NOFAULT		(1<<1)	/* no fault */
+
+/*
+ * special MMU regions
+ *	DMA segment for SBus DMA mapping via I/O MMU (hardware fixes location)
+ *	the frame buffer is mapped as one MMU region (16 Mbytes)
+ *	IO segments for device register pages etc.
+ */
+#define	DMARANGE	0
+#define	DMASEGSIZE	((16*MB)<<DMARANGE)
+#define	DMASEGBASE	(0 - DMASEGSIZE)
+#define	FBSEGSIZE	(1*(16*MB))	 /* multiples of 16*MB */
+#define	FBSEGBASE	(DMASEGBASE - DMASEGSIZE)
+#define	IOSEGSIZE	(16*MB)
+#define	IOSEGBASE	(FBSEGBASE - IOSEGSIZE)
+
+/*
+ * MMU entries
+ */
+#define	PTPVALID	1	/* page table pointer */
+#define	PTEVALID	2	/* page table entry */
+#define	PTERONLY	(2<<2)	/* read/execute */
+#define	PTEWRITE	(3<<2)	/* read/write/execute */
+#define	PTEKERNEL	(4<<2)	/* execute only */
+#define	PTENOCACHE	(0<<7)
+#define	PTECACHE	(1<<7)
+#define	PTEACCESS	(1<<5)
+#define	PTEMODIFY	(1<<6)
+#define	PTEMAINMEM	0
+#define	PTEIO		0
+#define PTEPROBEMEM	(PTEVALID|PTEKERNEL|PTENOCACHE|PTEWRITE|PTEMAINMEM)
+#define PTEUNCACHED	PTEACCESS	/* use as software flag for putmmu */
+
+#define	NTLBPID		64	/* limited by microsparc hardware contexts */
+
+#define PTEMAPMEM	(1024*1024)	
+#define	PTEPERTAB	(PTEMAPMEM/BY2PG)
+#define SEGMAPSIZE	128
+
+#define	INVALIDPTE	0
+#define	PPN(pa)		(((ulong)(pa)>>4)&0x7FFFFF0)
+#define	PPT(pn)		((ulong*)KADDR((((ulong)(pn)&~0xF)<<4)))
+
+/*
+ * Virtual addresses
+ */
+#define	VTAG(va)	((va>>22)&0x03F)
+#define	VPN(va)		((va>>13)&0x1FF)
+
+/*
+ * Address spaces
+ */
+#define	KZERO	0xE0000000		/* base of kernel address space */
+#define	KTZERO	(KZERO+4*BY2PG)		/* first address in kernel text */
+#define KSTACK	8192			/* size of kernel stack */
+
+#define	MACHSIZE	4096
+
+/*
+ * control registers in physical address space (ASI 20)
+ */
+#define	IOCR		0x10000000	/* IO MMU control register */
+#define	IBAR		0x10000004	/* IO MMU page table base address */
+#define	AFR		0x10000018	/* address flush register */
+#define	AFSR		0x10001000	/* asynch fault status */
+#define	AFAR		0x10001004	/* asynch fault address */
+#define	SSCR(i)		(0x10001010+(i)*4)	/* Sbus slot i config register */
+#define	MFSR		0x10001020	/* memory fault status register */
+#define	MFAR		0x10001024	/* memory fault address register */
+#define	MID		0x10002000	/* sbus arbitration enable */
+
+#define	SYSCTL		0x71F00000	/* system control & reset register */
+#define	PROCINTCLR	0x71E00004	/* clear pending processor interrupts */
+
+/*
+ * IO MMU page table entry
+ */
+#define	IOPTEVALID	(1<<1)
+#define	IOPTEWRITE	(1<<2)
--- /dev/null
+++ b/os/js/mkfile
@@ -1,0 +1,77 @@
+SYSTARG=Inferno
+OBJTYPE=sparc
+<../../mkconfig
+
+#Configurable parameters
+
+CONF=js			#default configuration
+CONFLIST=js
+
+SYSTARG=$OSTARG
+OBJTYPE=sparc
+INSTALLDIR=$ROOT/Inferno/$OBJTYPE/bin	#path of directory where kernel is installed
+
+#end configurable parameters
+
+<$ROOT/mkfiles/mkfile-$SYSTARG-$OBJTYPE	#set vars based on target system
+
+<| $SHELLNAME ../port/mkdevlist $CONF	#sets $IP, $DEVS, $ETHERS, $VGAS, $PORT, $MISC, $LIBS, $OTHERS
+
+OBJ=\
+	l.$O\
+	clock.$O\
+	main.$O\
+	mmu.$O\
+	fsv.$O\
+	screen.$O\
+	trap.$O\
+	rom.$O\
+	iob.$O\
+	superio.$O\
+	kbd.$O\
+	$CONF.root.$O\
+	$IP\
+	$DEVS\
+	$ETHERS\
+	$LINKS\
+	$VGAS\
+	$PORT\
+	$MISC\
+	$OTHERS\
+
+LIBNAMES=${LIBS:%=lib%.a}
+#LIBDIRS=$LIBS
+
+HFILES=\
+	mem.h\
+	dat.h\
+	fns.h\
+	io.h\
+	audio.h\
+	cs4231.h\
+	ns16552.h\
+	rom.h\
+	screen.h\
+	softcursor.h\
+	ureg.h\
+
+CFLAGS=-wFV -I$ROOT/Inferno/$OBJTYPE/include -I$ROOT/include -I$ROOT/libinterp
+KERNDATE=`{$NDATE}
+
+default:V: i$CONF
+
+i$CONF: $OBJ $CONF.c $CONF.root.h $LIBNAMES
+	$CC $CFLAGS '-DKERNDATE='$KERNDATE $CONF.c
+	$LD -M -o $target -H3 -T0xE0004000 -R0x4 -l $OBJ $CONF.$O $LIBFILES
+
+# "raw" version of kernel for binary comparison testing
+i$CONF.raw: $OBJ $CONF.c $CONF.root.h $LIBNAMES
+	$CC $CFLAGS '-DKERNDATE='0 $CONF.c
+	$LD -s -M -o $target -H3 -T0xE0004000 -R0x4 -l $OBJ $CONF.$O $LIBFILES
+
+install:V: $INSTALLDIR/i$CONF $INSTALLDIR/i$CONF.raw
+
+<../port/portmkfile
+
+%.$O:	io.h
+clock.$O main.$O trap.$O: $ROOT/Inferno/$OBJTYPE/include/ureg.h
--- /dev/null
+++ b/os/js/mmu.c
@@ -1,0 +1,252 @@
+#include	"u.h"
+#include	"../port/lib.h"
+#include	"mem.h"
+#include	"dat.h"
+#include	"fns.h"
+#include	"io.h"
+
+typedef struct Ctx Ctx;
+/*
+ * software description of an MMU context
+ */
+struct Ctx
+{
+	Ctx	*prev;	/* less recently used */
+	Ctx	*next;	/* more recently used */
+	Proc	*proc;	/* process that owns this context */
+	ushort	index;	/* which context this is */
+};
+
+ulong	*ioptes;	/* IO MMU's table (shared by all processors) */
+
+/* offset of x into the three page table levels in a context */
+#define AOFF(x) (((ulong)x)>>24)
+#define BOFF(x)	((((ulong)x)>>18)&(64-1))
+#define COFF(x)	((((ulong)x)>>12)&(64-1))
+#define	ISPTAB(x) ((((ulong)x)&3) == PTPVALID)
+#define	KPN(va) PPN(PADDR(va))
+
+#define	NIOPTE	(DMASEGSIZE/BY2PG)
+
+/*
+ *  allocate kernel page map and enter one mapping.  Return
+ *  address of the mapping.
+ */
+static ulong*
+putkmmu(ulong virt, ulong phys, int level)
+{
+	ulong *a, *b, *c;
+
+
+	a = &PPT(m->contexts[0])[AOFF(virt)];
+	if(level > 1) {
+		if(*a == 0){
+			b = (ulong*)xspanalloc(64*sizeof(ulong),
+					       64*sizeof(ulong), 0);
+			*a = KPN(b) | PTPVALID;
+		} else {
+			if(!ISPTAB(*a))
+				panic("putkmmu virt=%lux *a=%lux", virt, *a);
+			b = PPT(*a);
+		}
+		b = &b[BOFF(virt)];
+		if(level > 2) {
+			if(*b == 0){
+				c = (ulong*)xspanalloc(64*sizeof(ulong),
+						       64*sizeof(ulong), 0);
+				*b = KPN(c) | PTPVALID;
+			} else {
+				if(!ISPTAB(*b))
+					panic("putkmmu virt=%lux *b=%lux",
+					      virt, *b);
+				c = PPT(*b);
+			}
+			c = &c[COFF(virt)];
+			*c = phys;
+			return c;
+		} else {
+			*b = phys;
+			return b;
+		}
+	} else {
+		*a = phys;
+		return a;
+	}
+}
+
+void
+mmuinit(void)
+{
+	int i, n;
+	ulong *a;
+
+	m->contexts = (ulong*)xspanalloc(conf.ncontext*sizeof(ulong),
+					 conf.ncontext*sizeof(ulong),
+					 0);
+
+	/*
+	 * context 0 will have the prototype level 1 entries
+	 */
+	a = (ulong*)xspanalloc(256*sizeof(ulong), 256*sizeof(ulong), 0);
+
+	m->contexts[0] = KPN(a) | PTPVALID;
+
+	/*
+	 * map all memory to KZERO
+	 */
+	n = 128*MB/BY2PG;
+
+	 /* pages to first segment boundary */
+	for(i=0; i<(256*1024/BY2PG); i++)
+		putkmmu(KZERO|(i*BY2PG),
+			PPN(i*BY2PG)|PTEKERNEL|PTEWRITE|PTEVALID|PTECACHE, 3);
+
+	 /* segments to first 16Mb boundary */
+	for(; i<(16*MB)/BY2PG; i += 64)
+		putkmmu(KZERO|(i*BY2PG),
+			PPN(i*BY2PG)|PTEKERNEL|PTEWRITE|PTEVALID|PTECACHE, 2);
+
+	 /* 16 Mbyte regions to end */
+	for(; i<n; i += 64*64)
+		putkmmu(KZERO|(i*BY2PG),
+			PPN(i*BY2PG)|PTEKERNEL|PTEWRITE|PTEVALID|PTECACHE, 1);
+
+	/*
+	 * allocate page table pages for IO mapping
+	 */
+	n = IOSEGSIZE/BY2PG;
+	for(i=0; i<n; i++)
+		putkmmu(IOSEGBASE+(i*BY2PG), 0, 3);
+
+	/*
+	 * load kernel context
+	 */
+
+	putrmmu(CTPR, PADDR(m->contexts)>>4);
+	putrmmu(CXR, 0);
+	flushtlb();
+
+	ioptes = (ulong*)xspanalloc(NIOPTE*sizeof(ulong), DMASEGSIZE/1024, 0);
+	putphys(IBAR, PADDR(ioptes)>>4);
+	putphys(IOCR, (DMARANGE<<2)|1);	/* IO MMU enable */
+}
+
+
+void
+flushicache(void)
+{
+	int i;
+	ulong addr = 0;
+
+	for(i=0;i<512;i++) {
+		flushiline(addr);
+		addr += 1<<5;
+	}
+}
+
+void
+flushdcache(void)
+{
+	int i;
+	ulong addr = 0;
+
+	for(i=0;i<512;i++) {
+		flushdline(addr);
+		addr += 1<<5;
+	}
+}
+
+int
+segflush(void *p, ulong l)
+{
+	USED(p,l);
+	flushicache();
+	return 0;
+}
+
+void
+cacheinit(void)
+{
+	flushdcache();
+	flushicache();
+	setpcr(getpcr()|ENABCACHE);
+}
+
+typedef struct Mregion Mregion;
+struct Mregion
+{
+	ulong	addr;
+	long	size;
+};
+
+struct
+{
+	Mregion	io;
+	Mregion	dma;
+	Lock;
+}kmapalloc = {
+	{IOSEGBASE, IOSEGSIZE},
+	{DMASEGBASE, DMASEGSIZE},
+};
+
+void
+kmapinit(void)
+{
+}
+
+KMap*
+kmappa(ulong pa, ulong flag)
+{
+	ulong k;
+
+	lock(&kmapalloc);
+	k = kmapalloc.io.addr;
+	kmapalloc.io.addr += BY2PG;
+	if((kmapalloc.io.size -= BY2PG) < 0)
+		panic("kmappa");
+	putkmmu(k, PPN(pa)|PTEKERNEL|PTEWRITE|PTEVALID|flag, 3);
+	flushtlbpage(k);
+	unlock(&kmapalloc);
+	return (KMap*)k;
+}
+
+ulong
+kmapdma(ulong pa, ulong n)
+{
+	ulong va0, va;
+	int i, j;
+	
+
+	lock(&kmapalloc);
+	i = (n+(BY2PG-1))/BY2PG;
+	va0 = kmapalloc.dma.addr;
+	kmapalloc.dma.addr += i*BY2PG;
+	if((kmapalloc.dma.size -= i*BY2PG) <= 0)
+		panic("kmapdma");
+	va = va0;
+	for(j=0; j<i; j++) {
+		putkmmu(va, PPN(pa)|PTEKERNEL|PTEVALID|PTEWRITE, 3);
+		flushtlbpage(va);
+		ioptes[(va>>PGSHIFT)&(NIOPTE-1)] = PPN(pa)|IOPTEVALID|IOPTEWRITE;
+		va += BY2PG;
+		pa += BY2PG;
+	}
+	unlock(&kmapalloc);
+	return va0;
+}
+
+/*
+ * map the frame buffer
+ */
+ulong
+kmapsbus(int slot)
+{
+	int i, n;
+
+	lock(&kmapalloc);
+	n = FBSEGSIZE/BY2PG;
+	for(i=0; i<n; i += 64*64)
+		putkmmu(FBSEGBASE+(i*BY2PG), PPN(SBUS(slot)+(i*BY2PG))|PTEKERNEL|PTEWRITE|PTEVALID, 1);
+	unlock(&kmapalloc);
+	return FBSEGBASE;
+}
--- /dev/null
+++ b/os/js/ns16552.h
@@ -1,0 +1,81 @@
+/*
+ *  Javastation specific code for the ns16552 (really the superio chip,
+ *  but it has a serial port that looks like the ns16552).
+ */
+enum
+{
+	UartFREQ= 1843200,
+	TTYABase = 0x2F8
+};
+
+#define uartwrreg(u,r,v)	outb((u)->port + r, (u)->sticky[r] | (v))
+#define uartrdreg(u,r)		inb((u)->port + r)
+
+void	ns16552setup(ulong, ulong, char*);
+
+static void
+uartpower(int, int)
+{
+}
+
+/*
+ *  handle an interrupt to a single uart
+ */
+static void
+ns16552intrx(Ureg *ur, void *arg)
+{
+	USED(ur);
+
+	ns16552intr((ulong)arg);
+}
+
+/*
+ *  install the uarts (called by reset)
+ */
+void
+ns16552install(void)
+{
+	static int already;
+	void uartclock(void);
+
+	if(already)
+		return;
+	already = 1;
+
+	/* first two ports are always there and always the normal frequency */
+	ns16552setup(superiova()+TTYABase, UartFREQ, "eia0");
+	ns16552special(0, 38400, &kbdq, &printq, kbdputc);
+	addclock0link(uartclock, 22);
+}
+
+/*
+ * If the UART's receiver can be connected to a DMA channel,
+ * this function does what is necessary to create the
+ * connection and returns the DMA channel number.
+ * If the UART's receiver cannot be connected to a DMA channel,
+ * a -1 is returned.
+ */
+char
+ns16552dmarcv(int dev)
+{
+ 
+	USED(dev);
+        return -1;
+}
+
+long
+dmasetup(int,void*,long,int)
+{
+	return 0;
+}
+
+void
+dmaend(int)
+{
+}
+
+int
+dmacount(int)
+{
+	return 0;
+}
--- /dev/null
+++ b/os/js/rom.c
@@ -1,0 +1,103 @@
+#include	"u.h"
+#include	"../port/lib.h"
+#include	"mem.h"
+#include	"dat.h"
+#include	"fns.h"
+#include	"io.h"
+
+typedef struct Rom Rom;
+
+struct Rom
+{
+	uint	magic;
+	uint	version;
+	uint	plugin_version;
+	uint	monitor_id;
+
+	void	**physmemlist;
+	void	**virtmemlist;
+	void	**availphysmemlist;
+	void	*config_info;
+
+	char	**bootcmd;
+
+	uint	(*open)();
+	uint	(*close)();
+
+	uint	(*read_blocks)();
+	uint	(*write_blocks)();
+
+	uint	(*transmit_pkt)();
+	uint	(*poll_pkt)();
+
+	uint	(*read_bytes)();
+	uint	(*write_bytes)();
+	uint	(*seek)();
+
+	uchar	*input;
+	uchar	*output;
+
+	uchar	(*getchar)();
+	uchar	(*putchar)();
+	uchar	(*noblock_getchar)();
+	uchar	(*noblock_putchar)();
+
+	uchar	(*fb_writestr)(char*);
+
+	void	(*boot)(char*);
+
+	void	(*printf)(char*,...);
+
+	void	(*some_kbd_thing)();
+	int	*ms_count;
+	void	(*exit)();
+	void	(**vector)();
+	void	(**interpret)(char*,...);
+	void	*bootparam;	
+	uint	(*mac_addr)();
+	char	**v2_bootpath;
+	char	** v2_bootargs;
+	int	*v2_stdin;
+	int	*v2_stdout;
+	void*	(*v2_phandle)();
+	char*	(*v2_allocphys)();
+	char*	(*v2_freephys)();
+	char*	(*v2_map_dev)();
+	char*	(*v2_unmap_dev)();
+	ulong	(*v2_open)();
+	uint	(*v2_close)();
+	uint	(*v2_read)();
+	uint	(*v2_write)();
+	uint	(*v2_seek)();
+	void	(*v2_chain)();
+	void	(*v2_release)();
+	char	*(*v3_alloc)();
+	int	*reserved[14];
+	void	(*setctxsegmap)();
+	int	(*v3_startcpu)();
+	int	(*v3_stopcpu)();
+	int	(*v3_idlecpu)();
+	int	(*v3_resumecpu)();
+};
+
+Rom	*rom;		/* open boot rom vector -- assigned by l.s */
+
+void
+prom_printf(char *format, ...)
+{
+	char buf[512];
+	int l;
+	va_list ap;
+
+	va_start(ap, format);
+	l = vseprint(buf,buf+sizeof(buf),format,ap) - buf;
+	va_end(ap);
+
+	call_openboot(rom->v2_write,*rom->v2_stdout,buf,l);
+}
+
+void
+prom_halt(void)
+{
+	call_openboot(rom->exit,0xfeedface);
+}
--- /dev/null
+++ b/os/js/rom.h
@@ -1,0 +1,57 @@
+typedef struct ROM	ROM;
+typedef struct ROMconf	ROMconf;
+
+struct ROM
+{
+	uint	magic;
+	uint	version;
+	uint	plugversion;
+	uint	monid;
+	uint	pad1[3];
+	ROMconf	*conf;
+	uint	pad2[17];
+	void	(*boot)(void*);
+	uint	pad3[1];
+	void	(*enter)(void);
+	int	*msec;
+	void	(*exit)(void);
+	void	(**callback)(void);
+	uint	(*interpret)(void*);
+	uint	pad4[2];	
+	char	**bootpath;
+	char	**bootargs;
+	uint	*stdin;
+	uint	*stdout;
+	uint	(*phandle)(uint);
+	uint	(*alloc)(void*, uint);
+	void	(*free)(void*);
+	uint	(*map)(void*, uint, uint, uint);
+	void	(*unmap)(void*, uint);
+	uint	(*open)(char*);
+	uint	(*close)(uint);
+	uint	(*read)(uint, void*, int);
+	uint	(*write)(uint, void*, int);
+	uint	(*seek)(uint, uint, uint);
+	void	(*chain)(void*, uint, void*, void*, uint);
+	void	(*release)(void*, uint);
+	uint	pad4[15];
+	void	(*putcxsegm)(int, ulong, int);
+	int	(*startcpu)(uint, uint, uint, uint);
+	int	(*stopcpu)(uint);
+	int	(*idlecpu)(uint);
+	int	(*resumecpu)(uint);
+};
+
+struct ROMconf
+{
+	uint	(*next)(uint);
+	uint	(*child)(uint);
+	int	(*getproplen)(uint, void*);
+	int	(*getprop)(uint, void*, void*);
+	int	(*setprop)(uint, void*, void*);
+	void*	(*nextprop)(uint, void*);	
+};
+
+#define	ROMMAGIC	0x10010407
+
+extern	ROM	*rom;
--- /dev/null
+++ b/os/js/screen.c
@@ -1,0 +1,483 @@
+#include "u.h"
+#include "../port/lib.h"
+#include "mem.h"
+#include "io.h"
+#include "dat.h"
+#include "fns.h"
+#include "../port/error.h"
+
+#include <draw.h>
+#include <memdraw.h>
+#include <memlayer.h>
+#include <cursor.h>
+
+#include "softcursor.h"
+#include "screen.h"
+
+#define	Backgnd		(0xFF)
+
+
+ulong	consbits = 0xC0;
+Memdata consdata = {
+	nil,
+	&consbits
+};
+Memimage conscol =
+{
+	{ 0, 0, 1, 1 },
+	{ -100000, -100000, 100000, 100000 },
+	3,
+	1,
+	&consdata,
+	0,
+	1
+};
+
+ulong	onesbits = ~0;
+Memdata onesdata = {
+	nil,
+	&onesbits,
+};
+Memimage	xones =
+{
+	{ 0, 0, 1, 1 },
+	{ -100000, -100000, 100000, 100000 },
+	3,
+	1,
+	&onesdata,
+	0,
+	1
+};
+Memimage *memones = &xones;
+
+ulong	zerosbits = 0;
+Memdata zerosdata = {
+	nil,
+	&zerosbits,
+};
+Memimage	xzeros =
+{
+	{ 0, 0, 1, 1 },
+	{ -100000, -100000, 100000, 100000 },
+	3,
+	1,
+	&zerosdata,
+	0,
+	1
+};
+Memimage *memzeros = &xzeros;
+
+ulong	backbits = (Backgnd<<24)|(Backgnd<<16)|(Backgnd<<8)|Backgnd;
+Memdata backdata = {
+	nil,
+	&backbits
+};
+Memimage	xback =
+{
+	{ 0, 0, 1, 1 },
+	{ -100000, -100000, 100000, 100000 },
+	3,
+	1,
+	&backdata,
+	0,
+	1
+};
+Memimage *back = &xback;
+
+Video *vid;
+static Memsubfont *memdefont;
+static Lock screenlock;
+Memimage gscreen;
+Memdata gscreendata;
+static Point curpos;
+static Rectangle window;
+
+static Vctlr* vctlr;
+
+static Cursor arrow = {
+	{ -1, -1 },
+	{ 0xFF, 0xFF, 0x80, 0x01, 0x80, 0x02, 0x80, 0x0C, 
+	  0x80, 0x10, 0x80, 0x10, 0x80, 0x08, 0x80, 0x04, 
+	  0x80, 0x02, 0x80, 0x01, 0x80, 0x02, 0x8C, 0x04, 
+	  0x92, 0x08, 0x91, 0x10, 0xA0, 0xA0, 0xC0, 0x40, 
+	},
+	{ 0x00, 0x00, 0x7F, 0xFE, 0x7F, 0xFC, 0x7F, 0xF0, 
+	  0x7F, 0xE0, 0x7F, 0xE0, 0x7F, 0xF0, 0x7F, 0xF8, 
+	  0x7F, 0xFC, 0x7F, 0xFE, 0x7F, 0xFC, 0x73, 0xF8, 
+	  0x61, 0xF0, 0x60, 0xE0, 0x40, 0x40, 0x00, 0x00, 
+	},
+};
+
+
+void
+graphicscmap(int invert)
+{
+	int num, den, i, j;
+	int r, g, b, cr, cg, cb, v;
+
+	if(vctlr->setcolor == nil)
+		return;
+
+	for(r=0,i=0;r!=4;r++) for(v=0;v!=4;v++,i+=16){
+		for(g=0,j=v-r;g!=4;g++) for(b=0;b!=4;b++,j++){
+			den=r;
+			if(g>den) den=g;
+			if(b>den) den=b;
+			if(den==0)	/* divide check -- pick grey shades */
+				cr=cg=cb=v*17;
+			else{
+				num=17*(4*den+v);
+				cr=r*num/den;
+				cg=g*num/den;
+				cb=b*num/den;
+			}
+			if(invert)
+				vctlr->setcolor(255-i-(j&15),
+					cr*0x01010101,
+					cg*0x01010101,
+					cb*0x01010101);
+			else
+				vctlr->setcolor(i+(j&15),
+					cr*0x01010101,
+					cg*0x01010101,
+					cb*0x01010101);
+		}
+	}
+}
+
+static char s1[] =
+{
+	0x00, 0x00, 0xC0, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
+};
+
+
+void
+dacinit(void)
+{
+	int i;
+
+	/* Control registers */
+	vid->addr = 0x01 << 24;
+	vid->color = 0x02 << 24;
+	for(i = 0; i < sizeof s1; i++)
+		vid->cntrl = s1[i] << 24;
+
+	/* Cursor programming */
+	vid->addr = 0x00 << 24;
+	vid->color = 0x03 << 24;
+	vid->cntrl = 0xC0 << 24;
+	for(i = 0; i < 12; i++)
+		vid->cntrl = 0 << 24;
+
+	/* Load Cursor Ram */
+	vid->addr = 0x00 << 24;
+	vid->color = 0x04 << 24;
+	for(i = 0; i < 0x400; i++)
+		vid->cntrl = 0xff << 24;
+
+	graphicscmap(1);
+
+	/* Overlay Palette Ram */
+	vid->addr = 0x00 << 24;
+	vid->color = 0x01 << 24;
+	for(i = 0; i < 0x10; i++) {
+		vid->cntrl = 0xff << 24;
+		vid->cntrl = 0xff << 24;
+		vid->cntrl = 0xff << 24;
+	}
+
+	/* Overlay Palette Ram */
+	vid->addr = 0x81;
+	vid->color = 0x01;
+	for(i = 0; i < 3; i++) {
+		vid->cntrl = 0xff << 24;
+		vid->cntrl = 0xff << 24;
+		vid->cntrl = 0xff << 24;
+	}
+}
+
+void
+vctlrinit(int x, int y, int d)
+{
+	int h;
+	ulong va;
+
+	if(vctlr == nil){
+		/*
+		 * find a controller somehow
+		 * and call its init routine
+		 */
+		extern Vctlr FSV;
+
+		vctlr = FSV.init(0, x, y, d);
+		vctlr->load(&arrow);
+	}
+
+	if(vctlr == nil)
+		panic("%s",Ebadarg);
+
+	gscreen.data = &gscreendata;
+	gscreen.r.min = Pt(0, 0);
+	gscreen.r.max = Pt(vctlr->x, vctlr->y);
+	gscreen.clipr = gscreen.r;
+	gscreen.ldepth = vctlr->d;
+	gscreen.repl = 0;
+	va = kmapsbus(FSVSLOT);			/* FSV is in slot 2 */
+	gscreendata.data = (ulong *)(va+0x800000);	/* Framebuffer Magic */
+	gscreen.width = (vctlr->x *(1<<gscreen.ldepth)+31)/32;
+	
+
+	h = memdefont->height;
+
+	vid = (Video*)(va+0x240000);	/* RAMDAC Magic */
+	memset(gscreendata.data, Backgnd, vctlr->x*vctlr->y);
+	window = gscreen.r;
+	window.max.x = vctlr->x;
+	window.max.y = (vctlr->y/h) * h;
+	curpos = window.min;
+	if (gscreen.ldepth == 3){
+		dacinit();
+	}
+
+	memset(gscreendata.data, Backgnd, vctlr->x*vctlr->y);
+	window = gscreen.r;
+	window.max.x = vctlr->x;
+	window.max.y = (vctlr->y/h) * h;
+	curpos = window.min;
+}
+
+void
+screeninit(void)
+{
+	memdefont = getmemdefont();
+	vctlrinit(1024, 768, 3);
+}
+
+ulong*
+attachscreen(Rectangle *r, int *ld, int *width, int *softscreen)
+{
+	*r = gscreen.r;
+	*ld = gscreen.ldepth;
+	*width = gscreen.width;
+	*softscreen = 0;
+	return gscreendata.data;
+}
+
+void
+detachscreen(void)
+{
+}
+
+void
+flushmemscreen(Rectangle)
+{
+}
+
+static void
+scroll(void)
+{
+	int o;
+	Point p;
+	Rectangle r;
+
+	o = 4*memdefont->height;
+	r = Rpt(window.min, Pt(window.max.x, window.max.y-o));
+	p = Pt(window.min.x, window.min.y+o);
+	memdraw(&gscreen, r, &gscreen, p, memones, p);
+	r = Rpt(Pt(window.min.x, window.max.y-o), window.max);
+	memdraw(&gscreen, r, back, memzeros->r.min, memones, memzeros->r.min);
+
+	curpos.y -= o;
+}
+
+void
+screenputc(char *buf)
+{
+	Point p;
+	int h, w, pos;
+	Rectangle r;
+	static int *xp;
+	static int xbuf[256];
+
+	h = memdefont->height;
+	if(xp < xbuf || xp >= &xbuf[sizeof(xbuf)])
+		xp = xbuf;
+
+	switch(buf[0]) {
+	case '\n':
+		if(curpos.y+h >= window.max.y)
+			scroll();
+		curpos.y += h;
+		screenputc("\r");
+		break;
+	case '\r':
+		xp = xbuf;
+		curpos.x = window.min.x;
+		break;
+	case '\t':
+		p = memsubfontwidth(memdefont, " ");
+		w = p.x;
+		*xp++ = curpos.x;
+		pos = (curpos.x-window.min.x)/w;
+		pos = 8-(pos%8);
+		curpos.x += pos*w;
+		break;
+	case '\b':
+		if(xp <= xbuf)
+			break;
+		xp--;
+		r = Rpt(Pt(*xp, curpos.y), Pt(curpos.x, curpos.y + h));
+		memdraw(&gscreen, r, back, back->r.min, memones, back->r.min);
+		curpos.x = *xp;
+		break;
+	default:
+		p = memsubfontwidth(memdefont, buf);
+		w = p.x;
+
+		if(curpos.x >= window.max.x-w)
+			screenputc("\n");
+
+		*xp++ = curpos.x;
+		memimagestring(&gscreen, curpos, &conscol, memdefont, buf);
+		curpos.x += w;
+	}
+}
+
+void
+screenputs(char *s, int n)
+{
+	int i;
+	Rune r;
+	char buf[4];
+extern int cold;
+
+if(!cold)
+	return;
+
+	if(islo() == 0) {
+		/* don't deadlock trying to print in interrupt */
+		if(!canlock(&screenlock))
+			return;	
+	} else
+		lock(&screenlock);
+
+	while(n > 0) {
+		i = chartorune(&r, s);
+		if(i == 0){
+			s++;
+			--n;
+			continue;
+		}
+		memmove(buf, s, i);
+		buf[i] = 0;
+		n -= i;
+		s += i;
+		screenputc(buf);
+	}
+
+	unlock(&screenlock);
+}
+
+
+void
+cursorenable(void)
+{
+	if(vctlr->enable == nil)
+		return;
+	
+	vctlr->enable();
+
+	if(!vctlr->isloaded())
+		vctlr->load(&arrow);
+}
+
+void
+cursordisable(void)
+{
+	if(vctlr->disable == nil)
+		return;
+
+	vctlr->disable();
+}
+
+static Rectangle cursoroffrect;
+static int	cursorisoff;
+static Point hot;
+
+void
+cursorupdate0(void)
+{
+	int inrect, x, y;
+
+	x = mouse.x - hot.x;
+	y = mouse.y - hot.y;
+	inrect = (x >= cursoroffrect.min.x && x < cursoroffrect.max.x
+		&& y >= cursoroffrect.min.y && y < cursoroffrect.max.y);
+	if (cursorisoff == inrect)
+		return;
+	cursorisoff = inrect;
+	if (inrect)
+		cursordisable();
+	else
+		cursorenable();
+}
+
+void
+cursorupdate(Rectangle r)
+{
+	lock(&screenlock);
+	r.min.x -= 16;
+	r.min.y -= 16;
+	cursoroffrect = r;
+	if (swcursor)
+		cursorupdate0();
+	unlock(&screenlock);
+}
+
+void
+drawcursor(Drawcursor* c)
+{
+	Cursor curs;
+	int j, i, h, bpl;
+	uchar *bc, *bs, *cclr, *cset;
+
+	if(vctlr->load == nil)
+		return;
+
+	/* Set the default system cursor */
+	if(c->data == nil) {
+		lock(&screenlock);
+		vctlr->load(&arrow);
+		unlock(&screenlock);
+		return;
+	}
+
+	hot.x = c->hotx;
+	hot.y = c->hoty;
+	curs.offset = hot;
+	bpl = bytesperline(Rect(c->minx, c->miny, c->maxx, c->maxy), 0);
+
+	h = (c->maxy-c->miny)/2;
+	if(h > 16)
+		h = 16;
+
+	bc = c->data;
+	bs = c->data + h*bpl;
+
+	cclr = curs.clr;
+	cset = curs.set;
+	for(i = 0; i < h; i++) {
+		for(j = 0; j < 2; j++) {
+			cclr[j] = bc[j];
+			cset[j] = bs[j];
+		}
+		bc += bpl;
+		bs += bpl;
+		cclr += 2;
+		cset += 2;
+	}
+	lock(&screenlock);
+	vctlr->load(&curs);
+	unlock(&screenlock);
+}
+
--- /dev/null
+++ b/os/js/screen.h
@@ -1,0 +1,49 @@
+typedef struct Cursor Cursor;
+typedef struct Vctlr Vctlr;
+typedef struct Video Video;
+typedef struct Thc Thc;
+
+#define FSVSLOT 2			/* MrCoffee Hard Coded FB Location */
+
+struct	Cursor
+{
+	Point	offset;
+	uchar	clr[2*16];
+	uchar	set[2*16];
+};
+
+struct Vctlr {
+	char*	name;
+	Vctlr*	(*init)(Vctlr*, int, int, int);
+	void	(*page)(int);
+	int	(*setcolor)(ulong, ulong, ulong, ulong);
+
+	void	(*enable)(void);
+	void	(*disable)(void);
+	void	(*move)(int, int);
+	void	(*load)(Cursor*);
+	int	(*isloaded)(void);
+	int	(*cursorintersectsoff)(Rectangle*);
+
+	int	x;
+	int	y;
+	int	d;
+
+	Vctlr*	link;
+
+	int	hidecount;
+	int	loaded;
+	Cursor	cursor;
+	Lock	l;
+};
+
+
+struct Video
+{
+	/* Brooktree 458/451 */
+	ulong	addr;		/* address register */
+	ulong	color;		/* color palette */
+	ulong	cntrl;		/* control register */
+	ulong	ovrl;		/* overlay palette */
+};
+
--- /dev/null
+++ b/os/js/softcursor.h
@@ -1,0 +1,13 @@
+/*
+ * this should be #define'd to nothing if you have a hardware cursor
+ */
+
+void	cursormaybeoff(Rectangle*, Memimage*, Rectangle, Memimage*, Point*);
+
+/*
+ * also, you should #define cussoron() and cursoroff() to nothing
+ * if you have a hardware cursor.. This isn't as bad as it sounds, because
+ * this file is only included in port/devdraw.c, and it doesn't need to
+ * touch the cursor if it's a hardware cursor
+ *	-Tad
+ */
--- /dev/null
+++ b/os/js/superio.c
@@ -1,0 +1,216 @@
+#include "u.h"
+#include "../port/lib.h"
+#include "mem.h"
+#include "dat.h"
+#include "fns.h"
+#include "io.h"
+
+
+enum
+{
+	 /* superio configuration registers */
+	SioFER =	0x0,		/* function enable register */
+	SioFAR =	0x1,		/* function address register */
+	SioPTR =	0x2,		/* power and test egister */
+	SioFCR =	0x3,		/* function control register */
+	SioPCR =	0x4,		/* printer control register */
+	SioKRR =	0x5,		/* keyboard and RTC control register */
+	SioPMC =	0x6,		/* power mgmt control register */
+	SioTUP =	0x7,		/* tape uart and parallel register */
+	SioSID =	0x8,		/* SuperIO ID register */
+	SioASC =	0x9,		/* Advanced SIO Config register */
+	SioCS0CF0 =	0xA,		/* Chip select 0 config register 0 */
+	SioCS0CF1 =	0xB,		/* Chip select 0 config register 1 */
+	SioCS1CF0 =	0xC,		/* Chip select 1 config register 0 */
+	SioCS1CF1 = 	0xD,		/* Chip select 1 config register 1 */
+
+	 /* FER bits */
+	PPTEnable = 	1<<0,
+	EnableUART1 = 	1<<1,
+	EnableUART2 = 	1<<2,
+	FDCEnable = 	1<<3,
+	FDC4 =	 	1<<4,
+	FDC2ndAddr = 	1<<5,
+	IDEEnable = 	1<<6,
+	IDE2ndAddr = 	1<<7,
+
+	 /* FAR bits */
+	PPTAddr =	3<<0,
+	UART1Addr = 	3<<2,
+	UART2Addr =	3<<4,
+	SelectCom3n4 = 	3<<6,
+
+	 /* PTR bits */
+	PWDN = 		1<<0,
+	ClkPWDN = 	1<<1,
+	PWDNSelect = 	1<<2,
+	IRQSelect = 	1<<3,
+	UART1Test = 	1<<4,
+	UART2Test = 	1<<5,
+	LockConfig = 	1<<6,
+	XtndPPTSelect =	1<<7,
+
+	 /* FCR bits */
+	MediaSense =	1<<0,
+	DatRateSelect =	1<<0,
+	IDENTSelect = 	1<<1,
+	PPTFloat = 	1<<3,
+	LogicalDrvXcg =	1<<4,	/* logical drive exchange */
+	EnaZeroWait = 	1<<5,	/* zero wait state enable *.
+
+	 /* PCR bits */
+	EPPEnable =	1<<0,
+	EPPVersionSel =	1<<1,
+	ECPEnable = 	1<<2,
+	ECPClkFreeze = 	1<<3,
+	PPTIntPolar = 	1<<5,
+	PPTIntIOCtl = 	1<<6,
+	RTCRamMask =	1<<7,
+
+	 /* KRR bits */
+	KBCEnable =	1<<0,
+	KBCSpeedCtl = 	1<<1,
+	EnaProgAccess =	1<<2,
+	RTCEnable = 	1<<3,
+	RTCClkTst = 	1<<4,
+	RAMSEL = 	1<<5,
+	EnaChipSelect =	1<<6,
+	KBCClkSource =	1<<7,
+
+	 /* PMC bits */
+	IDETriStCtl =	1<<0,
+	FDCTriStCtl = 	1<<1,
+	UARTTriStCtl = 	1<<2,
+	SelectiveLock =	1<<5,
+	PPTriStEna = 	1<<6,
+
+	 /* TUP bits */
+	EPPToutIntEna =	1<<2,
+
+	 /* SID bits are just data values */
+
+	 /* ASC bits */
+	IRQ5Select = 	1<<0,
+	DRATE0Select =	1<<0,
+	DRV2Select = 	1<<1,
+	DR23Select = 	1<<1,
+	EnhancedTDR = 	1<<2,
+	ECPCnfgABit3 = 	1<<5,
+	SystemOpMode0 =	1<<6,
+	SystemOpMode1 =	1<<7,
+
+	 /* CS0CF0 bits are LA0-LA7 */
+	 /* CS1CF0 bits are LA0-LA7 */
+	 /* CSxCF1 bits (x=0,1) */
+	HA8 =		1<<0,
+	HA9 = 		1<<1,
+	HA10 = 		1<<2,
+	EnaCSWr = 	1<<4,
+	EnaCSRd =	1<<5,
+	CSAdrDcode =	1<<6,	/* enable full addr decode */
+	CSSelectPin =	1<<7,	/* CS/CS0 and SYSCLK/CS1 select pin */
+};
+
+typedef struct SuperIO SuperIO;
+
+struct SuperIO
+{
+	ulong va;
+	uchar *index;	/* superio index register */
+	uchar *data;	/* superio data register */
+
+	uchar *mkctl;	/* superio mouse/kbd control register */
+	uchar *mkdata;	/* superio mouse/kbd data register */
+};
+
+
+static SuperIO sio;
+
+static void printstatus(uchar status);
+
+void
+superioinit(ulong va, uchar *sindex, uchar *sdata, uchar *mkctl, uchar *mkdata)
+{
+	sio.va = va;
+
+	sio.index = sindex;
+	sio.data = sdata;
+
+	sio.mkctl = mkctl;
+	sio.mkdata = mkdata;
+}
+
+
+ulong
+superiova(void)
+{
+	return sio.va;
+}
+
+enum
+{
+	OBF =		1<<0,
+	IBF =		1<<1,
+	SysFlag =	1<<2,
+	LastWrWasCmd = 	1<<3,
+	KbdEnabled =	1<<4,
+	FromMouse = 	1<<5,
+	Timeout = 	1<<6,
+	ParityError = 	1<<7
+};
+
+uchar
+superio_readctl(void)
+{
+	return *sio.mkctl;
+}
+
+uchar
+superio_readdata(void)
+{
+	return *sio.mkdata;
+}
+
+void
+superio_writectl(uchar val)
+{
+	*sio.mkctl = val;
+}
+
+void
+superio_writedata(uchar val)
+{
+	*sio.mkdata = val;
+}
+
+
+static  void
+printstatus(uchar status)
+{
+	print("0x%2.2ux = <",status);
+	if(status & OBF) print("OBF|");
+	if(status & IBF) print("IBF|");
+	if(status & SysFlag) print("SysFlag|"); 
+	if(status & LastWrWasCmd) print("LastWrWasCmd|");
+	if(status & KbdEnabled) print("KbdEnabled|"); 
+	if(status & FromMouse) print("FromMouse|");
+	if(status & Timeout) print("Timeout|"); 
+	if(status & ParityError) print("ParityErr|"); 
+	print(">");
+}
+
+void
+testit()
+{
+	uchar status;
+	uchar val;
+
+	for(;;) {
+		status = *sio.mkctl;
+		if(status&OBF) {
+			printstatus(status);
+			val = *sio.mkdata;
+			print(", data = 0x%2.2ux\n",val);
+		}
+	}
+}
--- /dev/null
+++ b/os/js/trap.c
@@ -1,0 +1,472 @@
+#include	"u.h"
+#include	"../port/lib.h"
+#include	"mem.h"
+#include	"dat.h"
+#include	"fns.h"
+#include	"ureg.h"
+#include	"io.h"
+#include	"../port/error.h"
+
+void	noted(Ureg**, ulong);
+void	rfnote(Ureg**);
+int	domuldiv(ulong, Ureg*);
+
+extern	Label catch;
+extern	void traplink(void);
+extern	void syslink(void);
+static	void faultsparc(Ureg *ur);
+static	void faultasync(Ureg *ur);
+
+	long	ticks;
+static	char	excbuf[64];	/* BUG: not reentrant! */
+
+char *trapname[]={
+	"reset",
+	"instruction access exception",
+	"illegal instruction",
+	"privileged instruction",
+	"fp: disabled",
+	"window overflow",
+	"window underflow",
+	"unaligned address",
+	"fp: exception",
+	"data access exception",
+	"tag overflow",
+	"watchpoint detected",
+};
+
+char *fptrapname[]={
+	"none",
+	"IEEE 754 exception",
+	"unfinished FP op",
+	"unimplemented FP op",
+	"sequence error",
+	"hardware error",
+	"invalid FP register",
+	"reserved",
+}
+;
+
+char*
+excname(ulong tbr)
+{
+	char xx[64];
+	char *t;
+
+	switch(tbr){
+	case 8:
+		if(up == 0)
+			panic("fptrap in kernel\n");
+		else{
+panic("fptrap not implemented\n");
+#ifdef notdef
+			if(m->fpunsafe==0 && up->p->fpstate!=FPactive)
+				panic("fptrap not active\n"); 							fsr = up->fpsave.env;
+			sprint(excbuf, "fp: %s fppc=0x%lux",
+				fptrapname[(fsr>>14)&7],
+				up->fpsave.q[0].a, fsr);
+#endif
+		}
+		return excbuf;
+	case 36:
+		return "trap: cp disabled";
+	case 37:
+		return "trap: unimplemented instruction";
+	case 40:
+		return "trap: cp exception";
+	case 42:
+		return "trap: divide by zero";
+	case 128:
+		return "syscall";
+	case 129:
+		return "breakpoint";
+	}
+	t = 0;
+	if(tbr < sizeof trapname/sizeof(char*))
+		t = trapname[tbr];
+	if(t == 0){
+		if(tbr >= 130)
+			sprint(xx, "trap instruction %ld", tbr-128);
+		else if(17<=tbr && tbr<=31)
+			sprint(xx, "interrupt level %ld", tbr-16);
+		else
+			sprint(xx, "unknown trap %ld", tbr);
+		t = xx;
+	}
+	if(strncmp(t, "fp: ", 4) == 0)
+		strcpy(excbuf, t);
+	else
+		sprint(excbuf, "trap: %s", t);
+	return excbuf;
+
+}
+
+void
+trap(Ureg *ur)
+{
+	int user;
+	ulong tbr, iw;
+
+	tbr = (ur->tbr&0xFFF)>>4;
+	/*
+	 * Hack to catch bootstrap fault during probe
+	 */
+	if(catch.pc)
+		gotolabel(&catch);
+
+	if(up)
+		up->dbgreg = ur;
+
+	user = !(ur->psr&PSRPSUPER);
+	if(user) {
+		panic("how did we get to user mode???");
+	}
+	if(tbr > 16){			/* interrupt */
+		switch(tbr-16) {
+		case 15:			/* asynch mem err */
+			faultasync(ur);
+			break;
+		case 14:			/* processor counter */
+			clock(ur);
+			break;
+		case 13:			/* keyboard/mouse */
+			ns16552intr(0);
+			kbdintr();
+			break;
+		case 6:				/* lance */
+			lanceintr();
+			break;
+		default:
+			print("unexp intr lev %ld\n", tbr-16);
+			goto Error;
+		}
+	}else{
+		switch(tbr){
+		case 1:				/* instr. access */
+		case 9:				/* data access */
+			if(up && up->fpstate==FPACTIVE) {
+				fpquiet();
+				fpsave(&up->fpsave);
+				up->fpstate = FPINACTIVE;
+			}
+			faultsparc(ur);
+			goto Return;
+		case 2:				/* illegal instr, maybe mul */
+			iw = *(ulong*)ur->pc;
+			if((iw&0xC1500000) == 0x80500000){
+				if(domuldiv(iw, ur))
+					goto Return;
+				tbr = ur->tbr;
+			}
+			break;
+		case 4:				/* floating point disabled */
+panic("some more floating point crapola");
+break;
+#ifdef notdef
+			if(u && u->p){
+				if(up->p->fpstate == FPINIT)
+					restfpregs(initfpp, up->fpsave.fsr);
+				else if(u->p->fpstate == FPinactive)
+					restfpregs(&u->fpsave, u->fpsave.fsr);
+				else
+					break;
+				u->p->fpstate = FPactive;
+				ur->psr |= PSREF;
+				return;
+			}
+			break;
+#endif
+		case 8:				/* floating point exception */
+panic("floating point crapola #3");
+break;
+#ifdef notdef
+			/* if unsafe, trap happened shutting down FPU; just return */
+			if(m->fpunsafe){
+				m->fptrap = (fptrap()==0);
+				return;
+			}
+			if(fptrap())
+				goto Return;	/* handled the problem */
+			break;
+#endif
+		default:
+			break;
+		}
+    Error:
+		panic("kernel trap: %s pc=0x%lux\n", excname(tbr), ur->pc);
+	}
+    Return:
+    	return;
+}
+
+void
+trapinit(void)
+{
+	int i;
+	long t, a;
+
+	a = ((ulong)traplink-TRAPS)>>2;
+	a += 0x40000000;			/* CALL traplink(SB) */
+	t = TRAPS;
+	for(i=0; i<256; i++){
+		*(ulong*)(t+0) = a;		/* CALL traplink(SB) */
+		*(ulong*)(t+4) = 0xa7480000;	/* MOVW PSR, R19 */
+		a -= 16/4;
+		t += 16;
+	}
+
+#ifdef notdef
+	flushpage(TRAPS);
+#else
+	flushicache();
+#endif
+
+	puttbr(TRAPS);
+	setpsr(getpsr()|PSRET|SPL(15));	/* enable traps, not interrupts */
+}
+
+void
+mulu(ulong u1, ulong u2, ulong *lop, ulong *hip)
+{
+	ulong lo1, lo2, hi1, hi2, lo, hi, t1, t2, t;
+
+	lo1 = u1 & 0xffff;
+	lo2 = u2 & 0xffff;
+	hi1 = u1 >> 16;
+	hi2 = u2 >> 16;
+
+	lo = lo1 * lo2;
+	t1 = lo1 * hi2;
+	t2 = lo2 * hi1;
+	hi = hi1 * hi2;
+	t = lo;
+	lo += t1 << 16;
+	if(lo < t)
+		hi++;
+	t = lo;
+	lo += t2 << 16;
+	if(lo < t)
+		hi++;
+	hi += (t1 >> 16) + (t2 >> 16);
+	*lop = lo;
+	*hip = hi;
+}
+
+void
+muls(long l1, long l2, long *lop, long *hip)
+{
+	ulong t, lo, hi;
+	ulong mlo, mhi;
+	int sign;
+
+	sign = 0;
+	if(l1 < 0){
+		sign ^= 1;
+		l1 = -l1;
+	}
+	if(l2 < 0){
+		sign ^= 1;
+		l2 = -l2;
+	}
+	mulu(l1, l2, &mlo, &mhi);
+	lo = mlo;
+	hi = mhi;
+	if(sign){
+		t = lo = ~lo;
+		hi = ~hi;
+		lo++;
+		if(lo < t)
+			hi++;
+	}
+	*lop = lo;
+	*hip = hi;
+}
+
+int
+domuldiv(ulong iw, Ureg *ur)
+{
+	long op1, op2;
+	long *regp;
+	long *regs;
+
+	regs = (long*)ur;
+	if(iw & (1<<13)){	/* signed immediate */
+		op2 = iw & 0x1FFF;
+		if(op2 & 0x1000)
+			op2 |= ~0x1FFF;
+	}else
+		op2 = regs[iw&0x1F];
+	op1 = regs[(iw>>14)&0x1F];
+	regp = &regs[(iw>>25)&0x1F];
+
+	if(iw & (4<<19)){	/* divide */
+		if(ur->y!=0 && ur->y!=~0){
+	unimp:
+			ur->tbr = 37;	/* "unimplemented instruction" */
+			return 0;	/* complex Y is too hard */
+		}
+		if(op2 == 0){
+			ur->tbr = 42;	/* "zero divide" */
+			return 0;
+		}
+		if(iw & (1<<19)){
+			if(ur->y && (op1&(1<<31))==0)
+				goto unimp;	/* Y not sign extension */
+			*regp = op1 / op2;
+		}else{
+			if(ur->y)
+				goto unimp;
+			*regp = (ulong)op1 / (ulong)op2;
+		}
+	}else{
+		if(iw & (1<<19))
+			muls(op1, op2, regp, (long*)&ur->y);
+		else
+			mulu(op1, op2, (ulong*)regp, &ur->y);
+	}
+	if(iw & (16<<19)){	/* set CC */
+		ur->psr &= ~(0xF << 20);
+		if(*regp & (1<<31))
+			ur->psr |= 8 << 20;	/* N */
+		if(*regp == 0)
+			ur->psr |= 4 << 20;	/* Z */
+		/* BUG: don't get overflow right on divide */
+	}
+	ur->pc += 4;
+	ur->npc = ur->pc+4;
+	return 1;
+}
+
+void
+dumpregs(Ureg *ur)
+{
+	int i;
+	ulong *l;
+
+	if(up) {
+		print("registers for %s %ld\n",up->text,up->pid);
+		if(ur->usp < (ulong)up->kstack ||
+		   ur->usp > (ulong)up->kstack+KSTACK-8)
+			print("invalid stack pointer\n");
+	} else
+		print("registers for kernel\n");
+
+	print("PSR=%lux PC=%lux TBR=%lux\n", ur->psr, ur->pc, ur->tbr);
+	l = &ur->r0;
+	for(i=0; i<32; i+=2, l+=2)
+		print("R%d\t%.8lux\tR%d\t%.8lux\n", i, l[0], i+1, l[1]);
+}
+
+
+/* This routine must save the values of registers the user is not permitted to
+ * write from devproc and the restore the saved values before returning
+ */
+void
+setregisters(Ureg *xp, char *pureg, char *uva, int n)
+{
+	ulong psr;
+
+	psr = xp->psr;
+	memmove(pureg, uva, n);
+	xp->psr = psr;
+}
+
+void
+dumpstack(void)
+{
+}
+
+/*
+ * Must only be called splhi() when it is safe to spllo().  Because the FP unit
+ * traps if you touch it when an exception is pending, and because if you
+ * trap with ET==0 you halt, this routine sets some global flags to enable
+ * the rest of the system to handle the trap that might occur here without
+ * upsetting the kernel.  Shouldn't be necessary, but safety first.
+ */
+int
+fpquiet(void)
+{
+	int i, notrap;
+	ulong fsr;
+	char buf[128];
+
+	i = 0;
+	notrap = 1;
+	up->fpstate = FPINACTIVE;
+	for(;;){
+		m->fptrap = 0;
+		fsr = getfsr();
+		if(m->fptrap){
+			/* trap occurred and up->fpsave contains state */
+			sprint(buf, "sys: %s", excname(8));
+#ifdef notdef
+			postnote(u->p, 1, buf, NDebug);
+#else
+			panic(buf);
+#endif
+			notrap = 0;
+			break;
+		}
+		if((fsr&(1<<13)) == 0)
+			break;
+		if(++i > 1000){
+			print("fp not quiescent\n");
+			break;
+		}
+	}
+	up->fpstate = FPACTIVE;
+	return notrap;
+}
+
+enum
+{
+	SE_WRITE	= 4<<5,
+	SE_PROT		= 2<<2,
+};
+
+static void
+faultsparc(Ureg *ur)
+{
+	ulong addr;
+	char buf[ERRMAX];
+	int read;
+	ulong tbr, ser;
+
+	tbr = (ur->tbr&0xFFF)>>4;
+	addr = ur->pc;			/* assume instr. exception */
+	read = 1;
+	if(tbr == 9){			/* data access exception */
+		addr = getrmmu(SFAR);
+		ser = getrmmu(SFSR);
+		if(ser&(SE_WRITE))	/* is SE_PROT needed? */
+			read = 0;
+	}
+
+	up->dbgreg = ur;		/* for remote acid */
+	spllo();
+	sprint(buf, "sys: trap: fault %s addr=0x%lux",
+		read? "read" : "write", addr);
+
+	if(up->type == Interp)
+		disfault(ur,buf);
+	dumpregs(ur);
+	panic("fault: %s", buf);
+}
+
+static void
+faultasync(Ureg *ur)
+{
+	int user;
+
+	print("interrupt 15 AFSR %lux AFAR %lux MFSR %lux MFAR %lux\n",
+		getphys(AFSR), getphys(AFAR), getphys(MFSR), getphys(MFAR));
+	dumpregs(ur);
+	/*
+	 * Clear interrupt
+	 */
+	putphys(PROCINTCLR, 1<<15);
+	user = !(ur->psr&PSRPSUPER);
+	if(user)
+		pexit("Suicide", 0);
+	panic("interrupt 15");
+}
--- /dev/null
+++ b/os/js/ureg.h
@@ -1,0 +1,45 @@
+struct Ureg
+{
+	ulong	r0;			/* unnecessary; just for symmetry */
+	union{
+		ulong	sp;		/* r1 */
+		ulong	usp;		/* r1 */
+		ulong	r1;
+	};
+	ulong	r2;
+	ulong	r3;
+	ulong	r4;
+	ulong	r5;
+	ulong	r6;
+	ulong	r7;
+	ulong	r8;
+	ulong	r9;
+	ulong	r10;
+	ulong	r11;
+	ulong	r12;
+	ulong	r13;
+	ulong	r14;
+	ulong	r15;
+	ulong	r16;
+	ulong	r17;
+	ulong	r18;
+	ulong	r19;
+	ulong	r20;
+	ulong	r21;
+	ulong	r22;
+	ulong	r23;
+	ulong	r24;
+	ulong	r25;
+	ulong	r26;
+	ulong	r27;
+	ulong	r28;
+	ulong	r29;
+	ulong	r30;
+	ulong	r31;
+	ulong	y;
+	ulong	tbr;
+	ulong	psr;
+	ulong	npc;
+	ulong	pc;
+	ulong	pad;	/* so structure is double word aligned */
+};
--- /dev/null
+++ b/os/ks32/Mk
@@ -1,0 +1,7 @@
+#!/bin/rc
+rfork ne
+ROOT=/usr/inferno
+fn cd
+NPROC=3
+path=(/usr/inferno/Plan9/$cputype/bin $path)
+exec mk $*
--- /dev/null
+++ b/os/ks32/NOTICE
@@ -1,0 +1,2 @@
+Evaluator 7t Inferno port Copyright © 2000-2003 Vita Nuova Holdings Limited.
+Originally implemented by Nigel Roles
--- /dev/null
+++ b/os/ks32/archevaluator7t.c
@@ -1,0 +1,161 @@
+#include	"u.h"
+#include	"../port/lib.h"
+#include	"mem.h"
+#include	"dat.h"
+#include	"fns.h"
+#include	"io.h"
+#include	"ureg.h"
+#include	"../port/error.h"
+
+extern int cflag;
+extern int consoleprint;
+extern int redirectconsole;
+extern int main_pool_pcnt;
+extern int heap_pool_pcnt;
+extern int image_pool_pcnt;
+
+void
+archreset(void)
+{
+}
+
+void
+archconfinit(void)
+{
+	conf.topofmem = 512 * 1024;
+	conf.flashbase = 0x01800000;
+	conf.cpuspeed = 50000000;
+
+	conf.useminicache = 1;
+	conf.cansetbacklight = 0;
+	conf.cansetcontrast = 0;
+	conf.remaplo = 0;
+}
+
+void
+archconsole(void)
+{
+	uartspecial(0, 57600, 'n', &kbdq, &printq, kbdcr2nl);
+}
+
+void
+archreboot(void)
+{
+}
+
+void
+setleds(uchar val)
+{
+	ulong leds = IOPDATA;
+	IOPDATA = (leds & ~0xf0) | ((val & 0xf) << 4);
+}
+
+static void
+setled7(uchar val)
+{
+	ulong leds = IOPDATA;
+	IOPDATA = (leds & ~(0x7f << 10)) | ((val & 0x7f) << 10);
+}
+
+#define LEDSEGA	0x01
+#define LEDSEGB	0x02
+#define LEDSEGC	0x04
+#define LEDSEGD	0x08
+#define LEDSEGE	0x10
+#define LEDSEGG	0x20
+#define LEDSEGF	0x40
+
+static uchar led7map[] = {
+[' '] 0,
+['0']	LEDSEGA | LEDSEGB | LEDSEGC | LEDSEGD | LEDSEGE | LEDSEGF,
+['1']	LEDSEGB | LEDSEGC,
+['2']	LEDSEGA | LEDSEGB | LEDSEGD | LEDSEGE | LEDSEGG,
+['3']	LEDSEGA | LEDSEGB | LEDSEGC | LEDSEGD | LEDSEGG,
+['4']	LEDSEGB | LEDSEGC | LEDSEGF | LEDSEGG,
+['5']	LEDSEGA | LEDSEGC | LEDSEGD | LEDSEGF | LEDSEGG,
+['6']	LEDSEGA | LEDSEGC | LEDSEGD | LEDSEGE | LEDSEGF | LEDSEGG,
+['7']	LEDSEGA |LEDSEGB | LEDSEGC,
+['8']	LEDSEGA | LEDSEGB | LEDSEGC | LEDSEGD | LEDSEGE | LEDSEGF | LEDSEGG,
+['9']	LEDSEGA | LEDSEGB | LEDSEGC | LEDSEGD | LEDSEGF | LEDSEGG,
+['A']	LEDSEGA | LEDSEGB | LEDSEGC | LEDSEGE | LEDSEGF | LEDSEGG,
+['B']	LEDSEGC | LEDSEGD | LEDSEGE | LEDSEGF | LEDSEGG,
+['C']	LEDSEGA | LEDSEGD | LEDSEGE | LEDSEGF,
+['D']	LEDSEGB | LEDSEGC | LEDSEGD | LEDSEGE | LEDSEGG,
+['E']	LEDSEGA | LEDSEGD | LEDSEGE | LEDSEGF | LEDSEGG,
+['F']	LEDSEGA | LEDSEGE | LEDSEGF | LEDSEGG,
+['H']	LEDSEGC | LEDSEGE | LEDSEGF | LEDSEGG,
+['P']	LEDSEGA | LEDSEGB | LEDSEGE | LEDSEGF | LEDSEGG,
+['R']	LEDSEGE | LEDSEGG,
+['S']	LEDSEGA | LEDSEGC | LEDSEGD | LEDSEGF | LEDSEGG,
+['T']	LEDSEGD | LEDSEGE | LEDSEGF | LEDSEGG,
+['U']	LEDSEGB | LEDSEGC | LEDSEGD | LEDSEGE | LEDSEGF,
+['~']	LEDSEGB | LEDSEGE | LEDSEGG,
+};
+
+void
+setled7ascii(char c)
+{
+	if (c <= '~')
+		setled7(led7map[c]);
+}
+
+void
+trace(char c)
+{
+	int i;
+//	int x = splfhi();
+	setled7ascii(c);
+	for (i = 0; i < 2000000; i++)
+		;
+//	splx(x);
+}
+
+void
+ttrace()
+{
+	static char c = '6';
+
+	trace(c);
+	c = '6' + '7' -c;
+}
+
+void
+lights(ulong val)
+{
+	IOPDATA = (IOPDATA & (0x7ff << 4)) | ((val & 0x7ff) << 4);
+}
+
+void
+lcd_setbacklight(int)
+{
+}
+
+void
+lcd_setbrightness(ushort)
+{
+}
+
+void
+lcd_setcontrast(ushort)
+{
+}
+
+void
+archflashwp(int /*wp*/)
+{
+}
+
+void
+screenputs(char *, int)
+{
+}
+
+void
+cursorenable(void)
+{
+}
+
+void
+cursordisable(void)
+{
+}
--- /dev/null
+++ b/os/ks32/armv7.h
@@ -1,0 +1,19 @@
+/*
+ * PSR
+ */
+#define PsrMusr		0x10 	/* mode */
+#define PsrMfiq		0x11 
+#define PsrMirq		0x12
+#define PsrMsvc		0x13
+#define PsrMabt		0x17
+#define PsrMund		0x1B
+#define PsrMsys		0x1F
+#define PsrMask		0x1F
+
+#define PsrDfiq		0x00000040	/* disable FIQ interrupts */
+#define PsrDirq		0x00000080	/* disable IRQ interrupts */
+
+#define PsrV		0x10000000	/* overflow */
+#define PsrC		0x20000000	/* carry/borrow/extend */
+#define PsrZ		0x40000000	/* zero */
+#define PsrN		0x80000000	/* negative/less than */
--- /dev/null
+++ b/os/ks32/clock.c
@@ -1,0 +1,287 @@
+#include "u.h"
+#include "../port/lib.h"
+#include "mem.h"
+#include "dat.h"
+#include "fns.h"
+#include "io.h"
+
+#include "ureg.h"
+
+static ulong timer_incr[2] = { 0, 0, };
+
+#define DISABLE(t, x)		t->mod &= (x ? ~0x08 : ~0x01)
+#define ENABLE(t, x)			t->mod = (t->mod & (x ? 0x38 : 0x7)) | (x ? (1 << 3) : (1 << 0))
+
+typedef struct Clock0link Clock0link;
+typedef struct Clock0link {
+	void		(*clock)(void);
+	Clock0link*	link;
+} Clock0link;
+
+static Clock0link *clock0link;
+static Lock clock0lock;
+static void (*prof_fcn)(Ureg *, int);
+
+Timer*
+addclock0link(void (*clock)(void), int)
+{
+	Clock0link *lp;
+
+	if((lp = malloc(sizeof(Clock0link))) == 0){
+		print("addclock0link: too many links\n");
+		return nil;
+	}
+	ilock(&clock0lock);
+	lp->clock = clock;
+	lp->link = clock0link;
+	clock0link = lp;
+	iunlock(&clock0lock);
+	return nil;
+}
+
+static void
+profintr(Ureg *ur, void*)
+{
+#ifdef PROF
+	OstmrReg *ost = OSTMRREG;
+	int t;
+
+	if ((ost->osmr[3] - ost->oscr) < 2*TIMER_HZ)
+	{
+		/* less than 2 seconds before reset, say something */
+		setpanic();
+		clockpoll();
+		dumpregs(ur);
+		panic("Watchdog timer will expire");
+	}
+
+	/* advance the profile clock tick */
+	ost->osmr[2] += timer_incr[2];
+	ost->ossr = (1 << 2); 			/* Clear the SR */
+	t = 1;
+	while((ost->osmr[2] - ost->oscr) > 0x80000000) {
+		ost->osmr[2] += timer_incr[2];
+		t++;
+	}
+	if (prof_fcn)
+		prof_fcn(ur, t);
+#else
+	USED(ur);
+#endif
+}
+
+static void
+clockintr(Ureg*, void*)
+{
+	Clock0link *lp;
+
+	m->ticks++;
+
+	checkalarms();
+
+	if(canlock(&clock0lock)){
+		for(lp = clock0link; lp; lp = lp->link)
+			if (lp->clock)
+				lp->clock();
+		unlock(&clock0lock);
+	}
+	intrclear(TIMERbit(0), 0);
+}
+
+/*
+int
+cticks(void)
+{
+	return m->ticks;
+}
+*/
+
+/*
+ * Synchronize to the next SCLK tick boundary at best SPI rate.
+ */
+void
+spi_tsync(void)
+{
+	/* Why has this been commented out? */
+
+	// don't need to waste any time here
+	//ulong t0;
+
+	//t0 = OSTMR->oscr;
+	//while (OSTMR->oscr == t0);
+}
+
+void
+timerdisable( int timer )
+{
+	TimerReg *t = TIMERREG;
+	if ((timer < 0) || (timer > 1))
+		return;
+	intrmask(TIMERbit(timer), 0);
+	DISABLE(t, timer);
+}
+
+void
+timerenable( int timer, int Hz, void (*f)(Ureg *, void*), void* a)
+{
+	TimerReg *t = TIMERREG;
+	if ((timer < 0) || (timer > 1))
+		return;
+	timerdisable(timer);
+	timer_incr[timer] = TIMER_HZ/Hz;		/* set up freq */
+	t->data[timer] = timer_incr[timer];
+	ENABLE(t, timer);
+	intrenable(TIMERbit(timer), f, a, 0);
+}
+
+void
+installprof(void (*pf)(Ureg *, int))
+{
+#ifdef PROF
+	prof_fcn = pf;
+	timerenable( 2, HZ+1, profintr, 0);
+	timer_incr[2] = timer_incr[0]+63;	/* fine tuning */
+#else
+	USED(pf);
+#endif
+}
+
+void
+clockinit(void)
+{
+	m->ticks = 0;
+	timerenable(0, HZ, clockintr, 0);
+}
+
+void
+clockpoll(void)
+{
+}
+
+void
+clockcheck(void)
+{
+}
+
+// macros for fixed-point math
+
+ulong _mularsv(ulong m0, ulong m1, ulong a, ulong s);
+
+/* truncated: */
+#define FXDPTDIV(a,b,n) ((ulong)(((uvlong)(a) << (n)) / (b)))
+#define MAXMUL(a,n)     ((ulong)((((uvlong)1<<(n))-1)/(a)))
+#define MULDIV(x,a,b,n) (((x)*FXDPTDIV(a,b,n)) >> (n)) 
+#define MULDIV64(x,a,b,n) ((ulong)_mularsv(x, FXDPTDIV(a,b,n), 0, (n)))
+
+/* rounded: */
+#define FXDPTDIVR(a,b,n) ((ulong)((((uvlong)(a) << (n))+((b)/2)) / (b)))
+#define MAXMULR(a,n)     ((ulong)((((uvlong)1<<(n))-1)/(a)))
+#define MULDIVR(x,a,b,n) (((x)*FXDPTDIVR(a,b,n)+(1<<((n)-1))) >> (n)) 
+#define MULDIVR64(x,a,b,n) ((ulong)_mularsv(x, FXDPTDIVR(a,b,n), 1<<((n)-1), (n)))
+
+
+// these routines are all limited to a maximum of 1165 seconds,
+// due to the wrap-around of the OSTIMER
+
+ulong
+timer_start(void)
+{
+	return TIMERREG->data[0];
+}
+
+ulong
+timer_ticks(ulong t0)
+{
+	return TIMERREG->data[0] - t0;
+}
+
+int
+timer_devwait(ulong *adr, ulong mask, ulong val, int ost)
+{
+	int i;
+	ulong t0 = timer_start();
+	while((*adr & mask) != val) 
+		if(timer_ticks(t0) > ost)
+			return ((*adr & mask) == val) ? 0 : -1;
+		else
+			for (i = 0; i < 10; i++);	/* don't pound OSCR too hard! (why not?) */
+	return 0;
+}
+
+void
+timer_setwatchdog(int t)
+{
+	USED(t);
+}
+
+void
+timer_delay(int t)
+{	
+	ulong t0 = timer_start();
+	while(timer_ticks(t0) < t)
+		;
+}
+
+
+ulong
+us2tmr(int us)
+{
+	return MULDIV64(us, TIMER_HZ, 1000000, 24);
+}
+
+int
+tmr2us(ulong t)
+{
+	return MULDIV64(t, 1000000, TIMER_HZ, 24);
+}
+
+void
+microdelay(int us)
+{
+	ulong t0 = timer_start();
+	ulong t = us2tmr(us);
+	while(timer_ticks(t0) <= t)
+		;
+}
+
+
+ulong
+ms2tmr(int ms)
+{
+	return MULDIV64(ms, TIMER_HZ, 1000, 20);
+}
+
+int
+tmr2ms(ulong t)
+{
+	return MULDIV64(t, 1000, TIMER_HZ, 32);
+}
+
+void
+delay(int ms)
+{
+	ulong t0 = timer_start();
+	ulong t = ms2tmr(ms);
+	while(timer_ticks(t0) <= t)
+		clockpoll();
+}
+
+int
+srand()
+{
+	return 0;
+}
+
+int
+time()
+{
+	return 0;
+}
+
+uvlong
+fastticks(uvlong *hz)
+{
+	if(hz)
+		*hz = HZ;
+	return m->ticks;
+}
--- /dev/null
+++ b/os/ks32/dat.h
@@ -1,0 +1,218 @@
+typedef struct Conf	Conf;
+typedef struct FPU	FPU;
+typedef struct FPenv	FPenv;
+typedef struct Label	Label;
+typedef struct Lock	Lock;
+typedef struct Mach	Mach;
+typedef struct Ureg	Ureg;
+typedef struct ISAConf	ISAConf;
+typedef struct PCMmap	PCMmap;
+typedef struct PCIcfg	PCIcfg;
+typedef struct TouchPnt TouchPnt;
+typedef struct TouchTrans TouchTrans;
+typedef struct TouchCal TouchCal;
+typedef struct Vmode Vmode;
+
+typedef ulong Instr;
+
+#define ISAOPTLEN 16
+#define NISAOPT 8
+struct Conf
+{
+	ulong	nmach;			/* processors */
+	ulong	nproc;			/* processes */
+	ulong	npage0;			/* total physical pages of memory */
+	ulong	npage1;			/* total physical pages of memory */
+	ulong	topofmem;		/* highest physical address + 1 */
+	ulong	npage;			/* total physical pages of memory */
+	ulong	base0;			/* base of bank 0 */
+	ulong	base1;			/* base of bank 1 */
+	ulong	ialloc;			/* max interrupt time allocation in bytes */
+	ulong	flashbase;
+	ulong	cpuspeed;
+	ulong	pagetable;
+
+	int		useminicache;		/* screen.c/lcd.c */
+	int		cansetbacklight;	/* screen.c/lcd.c */
+	int		cansetcontrast;		/* screen.c/lcd.c */
+	int		remaplo;			/* use alt ivec */
+	int		textwrite;			/* writeable text segment, for debug */
+};
+
+struct ISAConf {
+	char	type[KNAMELEN];
+	ulong	port;
+	ulong	irq;
+	ulong	sairq;
+	ulong	dma;
+	ulong	mem;
+	ulong	size;
+	ulong	freq;
+
+	int	nopt;
+	char	opt[NISAOPT][ISAOPTLEN];
+};
+
+/*
+ * FPenv.status
+ */
+enum
+{
+	FPINIT,
+	FPACTIVE,
+	FPINACTIVE,
+};
+
+struct	FPenv
+{
+	ulong	status;
+	ulong	control;
+	ushort	fpistate;	/* emulated fp */
+	ulong	regs[8][3];	/* emulated fp */	
+};
+
+/*
+ * This structure must agree with fpsave and fprestore asm routines
+ */
+struct	FPU
+{
+	FPenv	env;
+	uchar	regs[80];	/* floating point registers */
+};
+
+struct Label
+{
+	ulong	sp;
+	ulong	pc;
+};
+
+struct Lock
+{
+	ulong	key;
+	ulong	sr;
+	ulong	pc;
+	int	pri;
+};
+
+#include "../port/portdat.h"
+
+/*
+ *  machine dependent definitions not used by ../port/dat.h
+ */
+struct Mach
+{
+	ulong	ticks;			/* of the clock since boot time */
+	Proc	*proc;			/* current process on this processor */
+	Label	sched;			/* scheduler wakeup */
+	Lock	alarmlock;		/* access to alarm list */
+	void	*alarm;			/* alarms bound to this clock */
+	int	machno;
+	int	nrdy;
+
+	int	stack[1];
+};
+
+#define	MACHP(n)	(n == 0 ? (Mach*)(MACHADDR) : (Mach*)0)
+
+extern Mach Mach0;
+extern Mach *m;
+extern Proc *up;
+
+typedef struct MemBank {
+	uint	pbase;
+	uint	plimit;
+	uint	vbase;
+	uint	vlimit;
+} MemBank;
+
+enum {
+	// DMA configuration parameters
+
+	 // DMA Direction
+	DmaOUT=		0,
+	DmaIN=		1,
+
+	 // dma endianess
+	DmaLittle=	0,
+	DmaBig=		1,
+
+	 // dma devices
+	DmaUDC=		0,
+	DmaSDLC=	2,
+	DmaUART0=	4,
+	DmaHSSP=	6,
+	DmaUART1=	7,	// special case (is really 6)
+	DmaUART2=	8,
+	DmaMCPaudio=	10,
+	DmaMCPtelecom=	12,
+	DmaSSP=		14,
+};
+
+enum touch_source {
+	TOUCH_READ_X1, TOUCH_READ_X2, TOUCH_READ_X3, TOUCH_READ_X4,
+	TOUCH_READ_Y1, TOUCH_READ_Y2, TOUCH_READ_Y3, TOUCH_READ_Y4,
+	TOUCH_READ_P1, TOUCH_READ_P2,
+	TOUCH_READ_RX1, TOUCH_READ_RX2,
+	TOUCH_READ_RY1, TOUCH_READ_RY2,
+	TOUCH_NUMRAWCAL = 10,
+};
+
+struct TouchPnt {
+	int	x;
+	int	y;
+};
+
+struct TouchTrans {
+	int	xxm;
+	int	xym;
+	int	yxm;
+	int	yym;
+	int	xa;
+	int	ya;
+};
+
+struct TouchCal {
+	TouchPnt	p[4];	// screen points
+	TouchPnt	r[4][4];// raw points
+	TouchTrans 	t[4];	// transformations
+	TouchPnt	err;	// maximum error
+	TouchPnt	var;	// usual maximum variance for readings
+	int 		ptp;	// pressure threshold for press
+	int		ptr;	// pressure threshold for release
+};
+
+extern TouchCal touchcal;
+
+struct Vmode {
+	int	wid;	/* 0 -> default or any match for all fields */
+	int	hgt;
+	uchar	d;
+	uchar	hz;
+	ushort	flags;
+};
+
+enum {
+	VMODE_MONO = 0x0001,    /* monochrome display */
+	VMODE_COLOR = 0x0002,   /* color (RGB) display */
+	VMODE_TFT = 0x0004,	/* TFT (active matrix) display */
+	VMODE_STATIC = 0x0010,  /* fixed palette */
+	VMODE_PSEUDO = 0x0020,  /* changeable palette */
+	VMODE_LINEAR = 0x0100,  /* linear frame buffer */
+	VMODE_PAGED = 0x0200,   /* paged frame buffer */
+	VMODE_PLANAR = 0x1000,  /* pixel bits split between planes */
+	VMODE_PACKED = 0x2000,  /* pixel bits packed together */
+	VMODE_LILEND = 0x4000,	/* little endian pixel layout */
+	VMODE_BIGEND = 0x8000,	/* big endian pixel layout */
+};
+
+/*
+ *	Interface to PCMCIA stubs
+ */
+enum {
+	/* argument to pcmpin() */
+	PCMready,
+	PCMeject,
+	PCMstschng,
+};
+
+#define	swcursor	1
--- /dev/null
+++ b/os/ks32/devuart.c
@@ -1,0 +1,719 @@
+#include	"u.h"
+#include	"../port/lib.h"
+#include	"mem.h"
+#include	"dat.h"
+#include	"fns.h"
+#include	"io.h"
+#include	"../port/error.h"
+#include	"../port/netif.h"
+
+/*
+ * currently no DMA or flow control (hardware or software)
+ */
+
+/*
+ * problems fixed from previous vsn:
+ *
+ *	- no kick on queue's, so redirections weren't getting
+ *	  started until the clock tick
+ *
+ *	- lots of unnecessary overhead
+ *
+ *	- initialization sequencing
+ *
+ *	- uart[n] no longer indexed before calling uartinstall()
+ */
+#define DEBUG	if(0)iprint
+
+static void uartintr(Ureg*, void*);
+
+enum
+{
+	Stagesize= 1024,
+	Dmabufsize=Stagesize/2,
+	Nuart=7,		/* max per machine */
+};
+
+typedef struct Uart Uart;
+struct Uart
+{
+	QLock;
+
+	int	opens;
+
+	int	enabled;
+
+	int	port;			/* 0 or 1 */
+	int	kickme;		/* for kick */
+	int	frame;		/* framing errors */
+	int	overrun;	/* rcvr overruns */
+	int	perror;		/* parity error */
+	int	bps;		/* baud rate */
+	uchar	bits;
+	char	parity;
+	uchar	stop;
+
+	int	inters;		/* total interrupt count */
+	int	rinters;	/* interrupts due to read */
+	int	winters;	/* interrupts due to write */
+
+	int	rcount;		/* total read count */
+	int	wcount;		/* total output count */
+
+	/* buffers */
+	int	(*putc)(Queue*, int);
+	Queue	*iq;
+	Queue	*oq;
+
+	UartReg	*reg;
+
+	/* staging areas to avoid some of the per character costs */
+	uchar	*ip;
+	uchar	*ie;
+	uchar	*op;
+	uchar	*oe;
+
+	/* put large buffers last to aid register-offset optimizations: */
+	char	name[KNAMELEN];
+	uchar	istage[Stagesize];
+	uchar	ostage[Stagesize];
+};
+
+#define UCON_ENABLEMASK (UCON_RXMDMASK | UCON_TXMDMASK | UCON_SINTMASK)
+#define UCON_ENABLESET (UCON_RXMDINT | UCON_TXMDINT | UCON_SINTON)
+#define UCON_DISABLESET (UCON_RXMDOFF | UCON_TXMDOFF | UCON_SINTOFF)
+
+static Uart *uart[Nuart];
+static int nuart;
+
+static uchar
+readstatus(Uart *p)
+{
+	UartReg *reg = p->reg;
+	uchar stat = reg->stat;
+	if (stat & USTAT_OV)
+		p->overrun++;
+	if (stat & USTAT_PE)
+		p->perror++;
+	if (stat & USTAT_FE)
+		p->frame++;
+	return stat;
+}
+	
+static void
+uartset(Uart *p)
+{
+	UartReg *reg = p->reg;
+	ulong denom;
+	ulong brdiv;
+	int n;
+	uchar lcon;
+
+	lcon= ULCON_CLOCKMCLK | ULCON_IROFF;
+	lcon |= ULCON_WL5 + (p->bits - 5);
+	lcon |= p->stop == 1 ? ULCON_STOP1 : ULCON_STOP2;
+	switch (p->parity) {
+	default:
+	case 'n':
+		lcon |= ULCON_PMDNONE;
+		break;
+	case 'o':
+		lcon |= ULCON_PMDODD;
+		break;
+	case 'e':
+		lcon |= ULCON_PMDEVEN;
+		break;
+	}
+	reg->lcon = lcon;
+
+	/* clear the break and loopback bits; leave everything else alone */
+	reg->con = (reg->con & ~(UCON_BRKMASK | UCON_LOOPMASK)) | UCON_BRKOFF | UCON_LOOPOFF;
+
+	denom = 2 * 16 * p->bps;
+	brdiv = (TIMER_HZ + denom / 2) / denom - 1;
+	reg->brdiv = brdiv << 4;
+
+	/* set buffer length according to speed, to allow
+	 * at most a 200ms delay before dumping the staging buffer
+	 * into the input queue
+	 */
+	n = p->bps/(10*1000/200);
+	p->ie = &p->istage[n < Stagesize ? n : Stagesize];
+}
+
+/*
+ *  send break
+ */
+static void
+uartbreak(Uart *p, int ms)
+{
+	UartReg *reg = p->reg;
+	if(ms == 0)
+		ms = 200;
+	reg->con |= UCON_BRKON;
+	tsleep(&up->sleep, return0, 0, ms);
+	reg->con &= ~UCON_BRKON;
+}
+
+/*
+ *  turn on a port
+ */
+static void
+uartenable(Uart *p)
+{
+	UartReg *reg = p->reg;
+
+	if(p->enabled)
+		return;
+
+	uartset(p);
+	// enable receive, transmit, and receive interrupt:
+	reg->con = (reg->con & UCON_ENABLEMASK) | UCON_ENABLESET;
+	p->enabled = 1;
+}
+
+/*
+ *  turn off a port
+ */
+static void
+uartdisable(Uart *p)
+{
+	p->reg->con = (p->reg->con & UCON_ENABLEMASK) | UCON_DISABLESET;
+	p->enabled = 0;
+}
+
+/*
+ *  put some bytes into the local queue to avoid calling
+ *  qconsume for every character
+ */
+static int
+stageoutput(Uart *p)
+{
+	int n;
+	Queue *q = p->oq;
+
+	if(!q)
+		return 0;
+	n = qconsume(q, p->ostage, Stagesize);
+	if(n <= 0)
+		return 0;
+	p->op = p->ostage;
+	p->oe = p->ostage + n;
+	return n;
+}
+
+static void
+uartxmit(Uart *p)
+{
+	UartReg *reg = p->reg;
+	ulong gag = 1;
+	while(p->op < p->oe || stageoutput(p)) {	
+		if(readstatus(p) & USTAT_TBE) {
+			DEBUG("T");
+			reg->txbuf = *(p->op++);
+			p->wcount++;
+		} else {
+			DEBUG("F");
+			gag = 0;
+			break;
+		}
+	}
+	if (gag) {
+		DEBUG("G");
+		p->kickme = 1;
+		intrmask(UARTTXbit(p->port), 0);
+	}
+}
+
+static void
+uartrecvq(Uart *p)
+{
+	uchar *cp = p->istage;
+	int n = p->ip - cp;
+
+	if(n == 0)
+		return;
+	if(p->putc)
+		while(n-- > 0) 
+			p->putc(p->iq, *cp++);
+	else if(p->iq) 
+		if(qproduce(p->iq, p->istage, n) < n)
+			print("qproduce flow control");
+	p->ip = p->istage;
+}
+
+static void
+uartrecv(Uart *p)
+{
+	UartReg *reg = p->reg;
+	uchar stat = readstatus(p);
+
+DEBUG("R");
+	if (stat & USTAT_RDR) {
+		int c;
+		c = reg->rxbuf;
+		if (c == '?') {		
+			DEBUG("mod 0x%.8lx\n", INTREG->mod);
+			DEBUG("msk 0x%.8lx\n", INTREG->msk);
+			DEBUG("pnd 0x%.8lx\n", INTREG->pnd);
+		}
+		*p->ip++ = c;
+/*		if(p->ip >= p->ie) */
+			uartrecvq(p);
+		p->rcount++;
+	}
+}
+
+static void
+uartkick(void *a)
+{
+	Uart *p = a;
+	int x = splhi();
+	DEBUG("k");
+	if (p->kickme) {
+		p->kickme = 0;
+		DEBUG("K");
+		intrunmask(UARTTXbit(p->port), 0);
+	}
+	splx(x);
+}
+
+/*
+ *  UART Interrupt Handler
+ */
+static void
+uarttxintr(Ureg*, void* arg)
+{
+	Uart *p = arg;			
+	intrclear(UARTTXbit(p->port), 0);
+	p->inters++;
+	p->winters++;
+	uartxmit(p);
+}
+
+static void
+uartrxintr(Ureg*, void* arg)
+{
+	Uart *p = arg;			
+	intrclear(UARTRXbit(p->port), 0);
+	p->inters++;
+	p->rinters++;
+	uartrecv(p);
+}
+
+
+static void
+uartsetup(ulong port, char *name)
+{
+	Uart *p;
+
+	if(nuart >= Nuart)
+		return;
+
+	p = xalloc(sizeof(Uart));
+	uart[nuart++] = p;
+	strcpy(p->name, name);
+
+	p->reg = &UARTREG[port];
+	p->bps = 9600;
+	p->bits = 8;
+	p->parity = 'n';
+	p->stop = 1;
+	p->kickme = 0;
+	p->port = port;
+
+	p->iq = qopen(4*1024, 0, 0 , p);
+	p->oq = qopen(4*1024, 0, uartkick, p);
+
+	p->ip = p->istage;
+	p->ie = &p->istage[Stagesize];
+	p->op = p->ostage;
+	p->oe = p->ostage;
+
+	intrenable(UARTTXbit(port), uarttxintr, p, 0); 
+	intrenable(UARTRXbit(port), uartrxintr, p, 0); 
+}
+
+static void
+uartinstall(void)
+{
+	static int already;
+
+	if(already)
+		return;
+	already = 1;
+
+	uartsetup(0, "eia0");
+//	uartsetup(1, "eia1");
+}
+
+/*
+ *  called by main() to configure a duart port as a console or a mouse
+ */
+void
+uartspecial(int port, int bps, char parity, Queue **in, Queue **out, int (*putc)(Queue*, int))
+{
+	Uart *p;
+
+	uartinstall();
+	if(port >= nuart) 
+		return;
+	p = uart[port];
+	if(bps) 
+		p->bps = bps;
+	if(parity)
+		p->parity = parity;
+	uartenable(p);
+	p->putc = putc;
+	if(in)
+		*in = p->iq;
+	if(out)
+		*out = p->oq;
+	p->opens++;
+}
+
+Dirtab *uartdir;
+int ndir;
+
+static void
+setlength(int i)
+{
+	Uart *p;
+
+	if(i > 0){
+		p = uart[i];
+		if(p && p->opens && p->iq)
+			uartdir[1+3*i].length = qlen(p->iq);
+	} else for(i = 0; i < nuart; i++){
+		p = uart[i];
+		if(p && p->opens && p->iq)
+			uartdir[1+3*i].length = qlen(p->iq);
+	}
+}
+
+/*
+ *  all uarts must be uartsetup() by this point or inside of uartinstall()
+ */
+static void
+uartreset(void)
+{
+	int i;
+	Dirtab *dp;
+
+	uartinstall();
+
+	ndir = 1+3*nuart;
+	uartdir = xalloc(ndir * sizeof(Dirtab));
+	dp = uartdir;
+	strcpy(dp->name, ".");
+	mkqid(&dp->qid, 0, 0, QTDIR);
+	dp->length = 0;
+	dp->perm = DMDIR|0555;
+	dp++;
+	for(i = 0; i < nuart; i++){
+		/* 3 directory entries per port */
+		strcpy(dp->name, uart[i]->name);
+		dp->qid.path = NETQID(i, Ndataqid);
+		dp->perm = 0660;
+		dp++;
+		sprint(dp->name, "%sctl", uart[i]->name);
+		dp->qid.path = NETQID(i, Nctlqid);
+		dp->perm = 0660;
+		dp++;
+		sprint(dp->name, "%sstatus", uart[i]->name);
+		dp->qid.path = NETQID(i, Nstatqid);
+		dp->perm = 0444;
+		dp++;
+	}
+}
+
+static Chan*
+uartattach(char *spec)
+{
+	return devattach('t', spec);
+}
+
+static Walkqid*
+uartwalk(Chan *c, Chan *nc, char **name, int nname)
+{
+	return devwalk(c, nc, name, nname, uartdir, ndir, devgen);
+}
+
+static int
+uartstat(Chan *c, uchar *dp, int n)
+{
+	if(NETTYPE(c->qid.path) == Ndataqid)
+		setlength(NETID(c->qid.path));
+	return devstat(c, dp, n, uartdir, ndir, devgen);
+}
+
+static Chan*
+uartopen(Chan *c, int omode)
+{
+	Uart *p;
+
+	c = devopen(c, omode, uartdir, ndir, devgen);
+
+	switch(NETTYPE(c->qid.path)){
+	case Nctlqid:
+	case Ndataqid:
+		p = uart[NETID(c->qid.path)];
+		qlock(p);
+		if(p->opens++ == 0){
+			uartenable(p);
+			qreopen(p->iq);
+			qreopen(p->oq);
+		}
+		qunlock(p);
+		break;
+	}
+
+	return c;
+}
+
+static void
+uartclose(Chan *c)
+{
+	Uart *p;
+
+	if(c->qid.type & QTDIR)
+		return;
+	if((c->flag & COPEN) == 0)
+		return;
+	switch(NETTYPE(c->qid.path)){
+	case Ndataqid:
+	case Nctlqid:
+		p = uart[NETID(c->qid.path)];
+		qlock(p);
+		if(--(p->opens) == 0){
+			uartdisable(p);
+			qclose(p->iq);
+			qclose(p->oq);
+			p->ip = p->istage;
+		}
+		qunlock(p);
+		break;
+	}
+}
+
+static long
+uartstatus(Chan *c, Uart *p, void *buf, long n, long offset)
+{
+	char str[256];
+	USED(c);
+
+	str[0] = 0;
+	sprint(str, "opens %d ferr %d oerr %d perr %d baud %d parity %c"
+			" intr %d rintr %d wintr %d"
+			" rcount %d wcount %d",
+		p->opens, p->frame, p->overrun, p->perror, p->bps, p->parity,
+		p->inters, p->rinters, p->winters,
+		p->rcount, p->wcount);
+
+	strcat(str, "\n");
+	return readstr(offset, buf, n, str);
+}
+
+static long
+uartread(Chan *c, void *buf, long n, vlong offset)
+{
+	Uart *p;
+
+	if(c->qid.type & QTDIR){
+		setlength(-1);
+		return devdirread(c, buf, n, uartdir, ndir, devgen);
+	}
+
+	p = uart[NETID(c->qid.path)];
+	switch(NETTYPE(c->qid.path)){
+	case Ndataqid:
+		return qread(p->iq, buf, n);
+	case Nctlqid:
+		return readnum(offset, buf, n, NETID(c->qid.path), NUMSIZE);
+	case Nstatqid:
+		return uartstatus(c, p, buf, n, offset);
+	}
+
+	return 0;
+}
+
+static void
+uartctl(Uart *p, char *cmd)
+{
+	int i, n;
+
+	/* let output drain for a while (up to 4 secs) */
+	for(i = 0; i < 200 && (qlen(p->oq) || (readstatus(p) & USTAT_TC) == 0); i++)
+		tsleep(&up->sleep, return0, 0, 20);
+
+	if(strncmp(cmd, "break", 5) == 0){
+		uartbreak(p, 0);
+		return;
+	}
+
+	n = atoi(cmd+1);
+	switch(*cmd){
+	case 'B':
+	case 'b':
+		if(n <= 0) 
+			error(Ebadarg);
+		p->bps = n;
+		uartset(p);
+		break;
+	case 'f':
+	case 'F':
+		qflush(p->oq);
+		break;
+	case 'H':
+	case 'h':
+		qhangup(p->iq, 0);
+		qhangup(p->oq, 0);
+		break;
+	case 'L':
+	case 'l':
+		if(n < 7 || n > 8)
+			error(Ebadarg);
+		p->bits = n;
+		uartset(p);
+		break;
+	case 'n':
+	case 'N':
+		qnoblock(p->oq, n);
+		break;
+	case 'P':
+	case 'p':
+		p->parity = *(cmd+1);
+		uartset(p);
+		break;
+	case 'K':
+	case 'k':
+		uartbreak(p, n);
+		break;
+	case 'Q':
+	case 'q':
+		qsetlimit(p->iq, n);
+		qsetlimit(p->oq, n);
+		break;
+	case 's':
+	case 'S':
+		if(n < 1 || n > 2)
+			error(Ebadarg);
+		p->stop = n;
+		uartset(p);
+		break;
+	}
+}
+
+static long
+uartwrite(Chan *c, void *buf, long n, vlong offset)
+{
+	Uart *p;
+	char cmd[32];
+
+	USED(offset);
+
+	if(c->qid.type & QTDIR)
+		error(Eperm);
+
+	p = uart[NETID(c->qid.path)];
+
+	switch(NETTYPE(c->qid.path)){
+	case Ndataqid:
+		return qwrite(p->oq, buf, n);
+	case Nctlqid:
+
+		if(n >= sizeof(cmd))
+			n = sizeof(cmd)-1;
+		memmove(cmd, buf, n);
+		cmd[n] = 0;
+		uartctl(p, cmd);
+		return n;
+	}
+}
+
+static int
+uartwstat(Chan *c, uchar *dp, int n)
+{
+	error(Eperm);
+	return 0;
+#ifdef xxx
+	Dir d;
+	Dirtab *dt;
+
+	if(!iseve())
+		error(Eperm);
+	if(c->qid.type & QTDIR)
+		error(Eperm);
+	if(NETTYPE(c->qid.path) == Nstatqid)
+		error(Eperm);
+
+	dt = &uartdir[3 * NETID(c->qid.path)];
+	convM2D(dp, &d);
+	d.mode &= 0666;
+	dt[0].perm = dt[1].perm = d.mode;
+#endif
+}
+
+Dev uartdevtab = {
+	't',
+	"uart",
+
+	uartreset,
+	devinit,
+	devshutdown,
+	uartattach,
+	uartwalk,
+	uartstat,
+	uartopen,
+	devcreate,
+	uartclose,
+	uartread,
+	devbread,
+	uartwrite,
+	devbwrite,
+	devremove,
+	uartwstat,
+};
+
+void
+uartputc(int c)
+{
+	UartReg *u;
+
+	if (!c)
+		return;
+	u = &UARTREG[1];
+	while ((u->stat & USTAT_TBE) == 0)
+		;
+	u->txbuf = c;
+	if (c == '\n')
+		while((u->stat & USTAT_TC) == 0)	/* flush xmit fifo */
+			;
+}
+
+void
+uartputs(char *data, int len)
+{
+	int x;
+
+	clockpoll();
+	x = splfhi();
+	while (len--){
+		if(*data == '\n')
+			uartputc('\r');
+		uartputc(*data++);
+	}
+	splx(x);
+}
+
+int
+uartgetc(void)
+{
+	UartReg *u;
+
+	clockcheck();
+	u = &UARTREG[1];
+	while((u->stat & USTAT_RDR) == 0)
+		clockcheck();
+	return u->rxbuf;
+}
--- /dev/null
+++ b/os/ks32/download.ps
@@ -1,0 +1,1040 @@
+%!PS-Adobe-2.0
+%%Version: 0.1
+%%DocumentFonts: (atend)
+%%Pages: (atend)
+%%EndComments
+%
+% Version 3.3.2 prologue for troff files.
+%
+
+/#copies 1 store
+/aspectratio 1 def
+/formsperpage 1 def
+/landscape false def
+/linewidth .3 def
+/magnification 1 def
+/margin 0 def
+/orientation 0 def
+/resolution 720 def
+/rotation 1 def
+/xoffset 0 def
+/yoffset 0 def
+
+/roundpage true def
+/useclippath true def
+/pagebbox [0 0 612 792] def
+
+/R  /Times-Roman def
+/I  /Times-Italic def
+/B  /Times-Bold def
+/BI /Times-BoldItalic def
+/H  /Helvetica def
+/HI /Helvetica-Oblique def
+/HB /Helvetica-Bold def
+/HX /Helvetica-BoldOblique def
+/CW /Courier def
+/CO /Courier def
+/CI /Courier-Oblique def
+/CB /Courier-Bold def
+/CX /Courier-BoldOblique def
+/PA /Palatino-Roman def
+/PI /Palatino-Italic def
+/PB /Palatino-Bold def
+/PX /Palatino-BoldItalic def
+/Hr /Helvetica-Narrow def
+/Hi /Helvetica-Narrow-Oblique def
+/Hb /Helvetica-Narrow-Bold def
+/Hx /Helvetica-Narrow-BoldOblique def
+/KR /Bookman-Light def
+/KI /Bookman-LightItalic def
+/KB /Bookman-Demi def
+/KX /Bookman-DemiItalic def
+/AR /AvantGarde-Book def
+/AI /AvantGarde-BookOblique def
+/AB /AvantGarde-Demi def
+/AX /AvantGarde-DemiOblique def
+/NR /NewCenturySchlbk-Roman def
+/NI /NewCenturySchlbk-Italic def
+/NB /NewCenturySchlbk-Bold def
+/NX /NewCenturySchlbk-BoldItalic def
+/ZD /ZapfDingbats def
+/ZI /ZapfChancery-MediumItalic def
+/S  /S def
+/S1 /S1 def
+/GR /Symbol def
+
+/inch {72 mul} bind def
+/min {2 copy gt {exch} if pop} bind def
+
+/setup {
+	counttomark 2 idiv {def} repeat pop
+
+	landscape {/orientation 90 orientation add def} if
+	/scaling 72 resolution div def
+	linewidth setlinewidth
+	1 setlinecap
+
+	pagedimensions
+	xcenter ycenter translate
+	orientation rotation mul rotate
+	width 2 div neg height 2 div translate
+	xoffset inch yoffset inch neg translate
+	margin 2 div dup neg translate
+	magnification dup aspectratio mul scale
+	scaling scaling scale
+
+	addmetrics
+	0 0 moveto
+} def
+
+/pagedimensions {
+	useclippath userdict /gotpagebbox known not and {
+		/pagebbox [clippath pathbbox newpath] def
+		roundpage currentdict /roundpagebbox known and {roundpagebbox} if
+	} if
+	pagebbox aload pop
+	4 -1 roll exch 4 1 roll 4 copy
+	landscape {4 2 roll} if
+	sub /width exch def
+	sub /height exch def
+	add 2 div /xcenter exch def
+	add 2 div /ycenter exch def
+	userdict /gotpagebbox true put
+} def
+
+/addmetrics {
+	/Symbol /S null Sdefs cf
+	/Times-Roman /S1 StandardEncoding dup length array copy S1defs cf
+} def
+
+/pagesetup {
+	/page exch def
+	currentdict /pagedict known currentdict page known and {
+		page load pagedict exch get cvx exec
+	} if
+} def
+
+/decodingdefs [
+	{counttomark 2 idiv {y moveto show} repeat}
+	{neg /y exch def counttomark 2 idiv {y moveto show} repeat}
+	{neg moveto {2 index stringwidth pop sub exch div 0 32 4 -1 roll widthshow} repeat}
+	{neg moveto {spacewidth sub 0.0 32 4 -1 roll widthshow} repeat}
+	{counttomark 2 idiv {y moveto show} repeat}
+	{neg setfunnytext}
+] def
+
+/setdecoding {/t decodingdefs 3 -1 roll get bind def} bind def
+
+/w {neg moveto show} bind def
+/m {neg dup /y exch def moveto} bind def
+/done {/lastpage where {pop lastpage} if} def
+
+/f {
+	dup /font exch def findfont exch
+	dup /ptsize exch def scaling div dup /size exch def scalefont setfont
+	linewidth ptsize mul scaling 10 mul div setlinewidth
+	/spacewidth ( ) stringwidth pop def
+} bind def
+
+/changefont {
+	/fontheight exch def
+	/fontslant exch def
+	currentfont [
+		1 0
+		fontheight ptsize div fontslant sin mul fontslant cos div
+		fontheight ptsize div
+		0 0
+	] makefont setfont
+} bind def
+
+/sf {f} bind def
+
+/cf {
+	dup length 2 idiv
+	/entries exch def
+	/chtab exch def
+	/newencoding exch def
+	/newfont exch def
+
+	findfont dup length 1 add dict
+	/newdict exch def
+	{1 index /FID ne {newdict 3 1 roll put}{pop pop} ifelse} forall
+
+	newencoding type /arraytype eq {newdict /Encoding newencoding put} if
+
+	newdict /Metrics entries dict put
+	newdict /Metrics get
+	begin
+		chtab aload pop
+		1 1 entries {pop def} for
+		newfont newdict definefont pop
+	end
+} bind def
+
+%
+% A few arrays used to adjust reference points and character widths in some
+% of the printer resident fonts. If square roots are too high try changing
+% the lines describing /radical and /radicalex to,
+%
+%	/radical	[0 -75 550 0]
+%	/radicalex	[-50 -75 500 0]
+%
+% Move braceleftbt a bit - default PostScript character is off a bit.
+%
+
+/Sdefs [
+	/bracketlefttp		[201 500]
+	/bracketleftbt		[201 500]
+	/bracketrighttp		[-81 380]
+	/bracketrightbt		[-83 380]
+	/braceleftbt		[203 490]
+	/bracketrightex		[220 -125 500 0]
+	/radical		[0 0 550 0]
+	/radicalex		[-50 0 500 0]
+	/parenleftex		[-20 -170 0 0]
+	/integral		[100 -50 500 0]
+	/infinity		[10 -75 730 0]
+] def
+
+/S1defs [
+	/underscore		[0 80 500 0]
+	/endash			[7 90 650 0]
+] def
+%
+% Tries to round clipping path dimensions, as stored in array pagebbox, so they
+% match one of the known sizes in the papersizes array. Lower left coordinates
+% are always set to 0.
+%
+
+/roundpagebbox {
+    7 dict begin
+	/papersizes [8.5 inch 11 inch 14 inch 17 inch] def
+
+	/mappapersize {
+		/val exch def
+		/slop .5 inch def
+		/diff slop def
+		/j 0 def
+		0 1 papersizes length 1 sub {
+			/i exch def
+			papersizes i get val sub abs
+			dup diff le {/diff exch def /j i def} {pop} ifelse
+		} for
+		diff slop lt {papersizes j get} {val} ifelse
+	} def
+
+	pagebbox 0 0 put
+	pagebbox 1 0 put
+	pagebbox dup 2 get mappapersize 2 exch put
+	pagebbox dup 3 get mappapersize 3 exch put
+    end
+} bind def
+
+%%EndProlog
+%%BeginSetup
+mark
+%
+% Encoding vector and redefinition of findfont for the ISO Latin1 standard.
+% The 18 characters missing from ROM based fonts on older printers are noted
+% below.
+%
+
+/ISOLatin1Encoding [
+	/.notdef
+	/.notdef
+	/.notdef
+	/.notdef
+	/.notdef
+	/.notdef
+	/.notdef
+	/.notdef
+	/.notdef
+	/.notdef
+	/.notdef
+	/.notdef
+	/.notdef
+	/.notdef
+	/.notdef
+	/.notdef
+	/.notdef
+	/.notdef
+	/.notdef
+	/.notdef
+	/.notdef
+	/.notdef
+	/.notdef
+	/.notdef
+	/.notdef
+	/.notdef
+	/.notdef
+	/.notdef
+	/.notdef
+	/.notdef
+	/.notdef
+	/.notdef
+	/space
+	/exclam
+	/quotedbl
+	/numbersign
+	/dollar
+	/percent
+	/ampersand
+	/quoteright
+	/parenleft
+	/parenright
+	/asterisk
+	/plus
+	/comma
+	/minus
+	/period
+	/slash
+	/zero
+	/one
+	/two
+	/three
+	/four
+	/five
+	/six
+	/seven
+	/eight
+	/nine
+	/colon
+	/semicolon
+	/less
+	/equal
+	/greater
+	/question
+	/at
+	/A
+	/B
+	/C
+	/D
+	/E
+	/F
+	/G
+	/H
+	/I
+	/J
+	/K
+	/L
+	/M
+	/N
+	/O
+	/P
+	/Q
+	/R
+	/S
+	/T
+	/U
+	/V
+	/W
+	/X
+	/Y
+	/Z
+	/bracketleft
+	/backslash
+	/bracketright
+	/asciicircum
+	/underscore
+	/quoteleft
+	/a
+	/b
+	/c
+	/d
+	/e
+	/f
+	/g
+	/h
+	/i
+	/j
+	/k
+	/l
+	/m
+	/n
+	/o
+	/p
+	/q
+	/r
+	/s
+	/t
+	/u
+	/v
+	/w
+	/x
+	/y
+	/z
+	/braceleft
+	/bar
+	/braceright
+	/asciitilde
+	/.notdef
+	/.notdef
+	/.notdef
+	/.notdef
+	/.notdef
+	/.notdef
+	/.notdef
+	/.notdef
+	/.notdef
+	/.notdef
+	/.notdef
+	/.notdef
+	/.notdef
+	/.notdef
+	/.notdef
+	/.notdef
+	/.notdef
+	/dotlessi
+	/grave
+	/acute
+	/circumflex
+	/tilde
+	/macron
+	/breve
+	/dotaccent
+	/dieresis
+	/.notdef
+	/ring
+	/cedilla
+	/.notdef
+	/hungarumlaut
+	/ogonek
+	/caron
+	/space
+	/exclamdown
+	/cent
+	/sterling
+	/currency
+	/yen
+	/brokenbar		% missing
+	/section
+	/dieresis
+	/copyright
+	/ordfeminine
+	/guillemotleft
+	/logicalnot
+	/hyphen
+	/registered
+	/macron
+	/degree			% missing
+	/plusminus		% missing
+	/twosuperior		% missing
+	/threesuperior		% missing
+	/acute
+	/mu			% missing
+	/paragraph
+	/periodcentered
+	/cedilla
+	/onesuperior		% missing
+	/ordmasculine
+	/guillemotright
+	/onequarter		% missing
+	/onehalf		% missing
+	/threequarters		% missing
+	/questiondown
+	/Agrave
+	/Aacute
+	/Acircumflex
+	/Atilde
+	/Adieresis
+	/Aring
+	/AE
+	/Ccedilla
+	/Egrave
+	/Eacute
+	/Ecircumflex
+	/Edieresis
+	/Igrave
+	/Iacute
+	/Icircumflex
+	/Idieresis
+	/Eth			% missing
+	/Ntilde
+	/Ograve
+	/Oacute
+	/Ocircumflex
+	/Otilde
+	/Odieresis
+	/multiply		% missing
+	/Oslash
+	/Ugrave
+	/Uacute
+	/Ucircumflex
+	/Udieresis
+	/Yacute			% missing
+	/Thorn			% missing
+	/germandbls
+	/agrave
+	/aacute
+	/acircumflex
+	/atilde
+	/adieresis
+	/aring
+	/ae
+	/ccedilla
+	/egrave
+	/eacute
+	/ecircumflex
+	/edieresis
+	/igrave
+	/iacute
+	/icircumflex
+	/idieresis
+	/eth			% missing
+	/ntilde
+	/ograve
+	/oacute
+	/ocircumflex
+	/otilde
+	/odieresis
+	/divide			% missing
+	/oslash
+	/ugrave
+	/uacute
+	/ucircumflex
+	/udieresis
+	/yacute			% missing
+	/thorn			% missing
+	/ydieresis
+] def
+
+/NewFontDirectory FontDirectory maxlength dict def
+
+%
+% Apparently no guarantee findfont is defined in systemdict so the obvious
+%
+%	systemdict /findfont get exec
+%
+% can generate an error. So far the only exception is a VT600 (version 48.0).
+%
+
+userdict /@RealFindfont known not {
+	userdict begin
+		/@RealFindfont systemdict begin /findfont load end def
+	end
+} if
+
+/findfont {
+	dup NewFontDirectory exch known not {
+		dup
+		%dup systemdict /findfont get exec	% not always in systemdict
+		dup userdict /@RealFindfont get exec
+		dup /Encoding get StandardEncoding eq {
+			dup length dict begin
+				{1 index /FID ne {def}{pop pop} ifelse} forall
+				/Encoding ISOLatin1Encoding def
+				currentdict
+			end
+			/DummyFontName exch definefont
+		} if
+		NewFontDirectory 3 1 roll put
+	} if
+	NewFontDirectory exch get
+} bind def
+
+%%Patch from lp
+%%EndPatch from lp
+
+setup
+%%EndSetup
+%%Page: 1 1
+/saveobj save def
+mark
+1 pagesetup
+12 /Times-Bold f
+(Connecting up the) 1322 1220 w
+(Arm) 2293 1220 w
+(Evaluator) 2562 1220 w
+(7t) 3106 1220 w
+(and Downloading Code) 3236 1220 w
+10 /Times-Italic f
+(Nigel Roles) 2648 1404 w
+(Vita Nuova Holdings Limited) 2292 1556 w
+(10th November 2000) 2461 1676 w
+10 /Times-Bold f
+(Introduction) 720 1924 w
+10 /Times-Roman f
+(This is just a quick note on how to download code to the Evaluator and run it.) 970 2082 w
+10 /Times-Bold f
+(Health Warning) 720 2330 w
+10 /Times-Roman f
+(I) 970 2488 w
+10 /Times-Italic f
+(think) 1033 2488 w
+10 /Times-Roman f
+(the) 1263 2488 w
+(interrupt) 1415 2488 w
+(and) 1789 2488 w
+(exception) 1963 2488 w
+(handling) 2382 2488 w
+(is) 2763 2488 w
+(OK;) 2861 2488 w
+(it's) 3064 2488 w
+(just) 3223 2488 w
+(that) 3399 2488 w
+(I) 3580 2488 w
+(had) 3644 2488 w
+(problems) 3819 2488 w
+(with) 4222 2488 w
+(supporting) 4431 2488 w
+(two) 4890 2488 w
+(serial ports, interrupt driven at the same time. You have been warned.) 720 2608 w
+10 /Times-Bold f
+(Connecting it up) 720 2856 w
+10 /Times-Roman f
+(The) 970 3014 w
+(board) 1156 3014 w
+(has) 1414 3014 w
+(Arm's) 1578 3014 w
+(standard) 1864 3014 w
+(Angel) 2233 3014 w
+(debugger) 2508 3014 w
+(monitor) 2910 3014 w
+(on) 3258 3014 w
+(board.) 3389 3014 w
+(You) 3672 3014 w
+(can) 3875 3014 w
+(read) 4044 3014 w
+(all) 4246 3014 w
+(about) 4377 3014 w
+(this) 4631 3014 w
+(in) 4808 3014 w
+(the) 4918 3014 w
+(documentation.) 720 3134 w
+(Whether) 1365 3134 w
+(you) 1734 3134 w
+(go to the trouble of installing the Windows tools is up to you, but do read how) 1910 3134 w
+(to operate the monitor.) 720 3254 w
+(Anyhow,) 970 3412 w
+(the) 1370 3412 w
+(board) 1523 3412 w
+(has) 1781 3412 w
+(two) 1945 3412 w
+(serial) 2126 3412 w
+(ports.) 2373 3412 w
+(The) 2629 3412 w
+(one) 2815 3412 w
+(nearest) 2990 3412 w
+(the) 3304 3412 w
+(LEDs) 3458 3412 w
+(is) 3723 3412 w
+(the) 3822 3412 w
+(debug) 3976 3412 w
+(and) 4252 3412 w
+(download) 4428 3412 w
+(port.) 4854 3412 w
+(The other is the console port when running Inferno.) 720 3532 w
+(You) 970 3690 w
+(only) 1172 3690 w
+(get) 1380 3690 w
+(one) 1532 3690 w
+(cable,) 1706 3690 w
+(so) 1971 3690 w
+(beg) 2090 3690 w
+(borrow) 2264 3690 w
+(or) 2583 3690 w
+(steal) 2697 3690 w
+(another) 2911 3690 w
+(cable.) 3241 3690 w
+(These) 3507 3690 w
+(are) 3776 3690 w
+(straight\255through) 3928 3690 w
+(9) 4603 3690 w
+(way) 4684 3690 w
+(to) 4881 3690 w
+(9) 4990 3690 w
+(way male to female cables. I bought a Belkin one from Staples at some horrid price.) 720 3810 w
+(Connect) 970 3968 w
+(both) 1336 3968 w
+(up) 1547 3968 w
+(to) 1680 3968 w
+(the) 1791 3968 w
+(back) 1946 3968 w
+(of) 2168 3968 w
+(your) 2285 3968 w
+(Plan) 2502 3968 w
+(9) 2714 3968 w
+(machine,) 2798 3968 w
+(and) 3195 3968 w
+(start) 3373 3968 w
+(terminal) 3579 3968 w
+(emulators) 3946 3968 w
+(on) 4374 3968 w
+(both.) 4508 3968 w
+(For) 4745 3968 w
+(the) 4918 3968 w
+(debug) 720 4088 w
+(and) 990 4088 w
+(download) 1160 4088 w
+(you) 1580 4088 w
+(need) 1756 4088 w
+(to) 1970 4088 w
+(run) 2074 4088 w
+(a specially modified version of) 2233 4088 w
+10 /Courier f
+(vt\(1\)) 3495 4088 w
+10 /Times-Roman f
+(which responds to the peculiar) 3820 4088 w
+(answer) 720 4208 w
+(back) 1030 4208 w
+(sequence) 1246 4208 w
+(that) 1639 4208 w
+(the) 1817 4208 w
+(Arm) 1967 4208 w
+(monitor) 2179 4208 w
+(has) 2525 4208 w
+(decided) 2687 4208 w
+(is) 3026 4208 w
+(appropriate) 3122 4208 w
+(for) 3605 4208 w
+(it) 3750 4208 w
+(to) 3835 4208 w
+(sense) 3942 4208 w
+(baud) 4187 4208 w
+(rate.) 4410 4208 w
+(As) 4638 4208 w
+(it) 4778 4208 w
+(hap\255) 4863 4208 w
+(pens,) 720 4328 w
+(I) 968 4328 w
+(have) 1041 4328 w
+(modified) 1269 4328 w
+(the) 1670 4328 w
+(version) 1832 4328 w
+(on) 2166 4328 w
+10 /Courier f
+(doppio) 2306 4328 w
+10 /Times-Roman f
+(.) 2666 4328 w
+(It) 2756 4328 w
+(should) 2856 4328 w
+(be) 3162 4328 w
+(possible) 3295 4328 w
+(via) 3662 4328 w
+(monitor) 3823 4328 w
+(commands) 4179 4328 w
+(to) 4651 4328 w
+(set) 4768 4328 w
+(the) 4918 4328 w
+(default) 720 4448 w
+(baud) 1031 4448 w
+(rate) 1259 4448 w
+(of) 1442 4448 w
+(the) 1559 4448 w
+(board) 1715 4448 w
+(to) 1976 4448 w
+(whatever) 2088 4448 w
+(you) 2487 4448 w
+(like.) 2671 4448 w
+(I) 2880 4448 w
+(lost) 2947 4448 w
+(patience) 3126 4448 w
+(after) 3492 4448 w
+(about) 3708 4448 w
+(a) 3964 4448 w
+(day,) 4042 4448 w
+(and) 4245 4448 w
+(hacked) 4423 4448 w
+10 /Courier f
+(vt\(1\)) 4740 4448 w
+10 /Times-Roman f
+(instead.) 720 4568 w
+(It) 1057 4568 w
+(was) 1146 4568 w
+(easier.) 1329 4568 w
+(So,) 1614 4568 w
+(the) 1773 4568 w
+(board) 1923 4568 w
+(will) 2178 4568 w
+(auto) 2362 4568 w
+(sense) 2562 4568 w
+(baud) 2806 4568 w
+(rate.) 3028 4568 w
+(The) 3230 4568 w
+(highest) 3413 4568 w
+(baud) 3730 4568 w
+(rate) 3952 4568 w
+(which) 4129 4568 w
+(is) 4401 4568 w
+(actually) 4496 4568 w
+(valid) 4840 4568 w
+(and hence will talk to Plan 9 is 57600. So the commands to configure the download window are) 720 4688 w
+9 /Courier-Bold f
+(term%) 1008 4852 w
+9 /Courier f
+(cd /usr/inferno/os/ks32) 1332 4852 w
+9 /Courier-Bold f
+(term%) 1008 4952 w
+9 /Courier f
+(vt) 1332 4952 w
+9 /Courier-Bold f
+(term%) 1008 5052 w
+9 /Courier f
+(con \255R \255b 57600 /dev/eia0) 1332 5052 w
+10 /Times-Roman f
+(You) 720 5236 w
+(can) 926 5236 w
+(choose) 1098 5236 w
+(other) 1409 5236 w
+(speeds,) 1648 5236 w
+(but) 1973 5236 w
+(57600) 2135 5236 w
+(is) 2419 5236 w
+(the) 2520 5236 w
+(maximum.) 2676 5236 w
+(For) 3141 5236 w
+(the) 3314 5236 w
+(console) 3470 5236 w
+(port,) 3809 5236 w
+10 /Courier f
+(vt\(1\)) 4029 5236 w
+10 /Times-Roman f
+(is) 4363 5236 w
+(not) 4465 5236 w
+(necessary.) 4628 5236 w
+(The baud rate is fixed in) 720 5356 w
+10 /Courier f
+(main.c) 1718 5356 w
+10 /Times-Roman f
+(at 57600, so the same) 2103 5356 w
+10 /Courier f
+(con) 2991 5356 w
+10 /Times-Roman f
+(command is appropriate.) 3196 5356 w
+(Now) 970 5514 w
+(plug) 1195 5514 w
+(in) 1404 5514 w
+(the) 1513 5514 w
+(power) 1666 5514 w
+(to) 1946 5514 w
+(the) 2055 5514 w
+(board.) 2208 5514 w
+(You) 2491 5514 w
+(should) 2694 5514 w
+(see) 2992 5514 w
+(the) 3150 5514 w
+(LEDs) 3303 5514 w
+(flick) 3567 5514 w
+(for) 3781 5514 w
+(bit,) 3928 5514 w
+(and) 4091 5514 w
+(then) 4267 5514 w
+(the) 4471 5514 w
+(7) 4625 5514 w
+(segment) 4707 5514 w
+(one shows 11. The download window should show) 720 5634 w
+9 /Courier-Bold f
+(Arm Evaluator7T Boot Monitor PreRelease 1.00) 1008 5798 w
+(Boot:) 1008 5898 w
+10 /Times-Roman f
+(Pressing) 720 6082 w
+(the) 1088 6082 w
+(reset) 1239 6082 w
+(button) 1456 6082 w
+(\(nearer) 1741 6082 w
+(of) 2051 6082 w
+(the) 2163 6082 w
+(two) 2314 6082 w
+(buttons) 2493 6082 w
+(to) 2817 6082 w
+(the) 2924 6082 w
+(CPU\),) 3075 6082 w
+(should) 3357 6082 w
+(cause) 3653 6082 w
+(the) 3903 6082 w
+(same) 4055 6082 w
+(message) 4290 6082 w
+(to) 4658 6082 w
+(be) 4766 6082 w
+(dis\255) 4890 6082 w
+(played.) 720 6202 w
+10 /Times-Bold f
+(Building the code) 720 6450 w
+10 /Courier f
+(mk\(1\)) 970 6608 w
+10 /Times-Roman f
+(in directory) 1295 6608 w
+10 /Courier f
+(/usr/inferno/os/ks32) 1783 6608 w
+10 /Times-Roman f
+(should build the code. You may need to do) 3008 6608 w
+9 /Courier f
+(bind \255b /usr/inferno/Plan9/386/bin /bin) 1008 6772 w
+10 /Times-Roman f
+(to get access to the compilers and tools.) 720 6956 w
+(The) 2357 6956 w
+(makes) 745 7076 w
+(an) 1026 7076 w
+(executable,) 1146 7076 w
+(converts) 1623 7076 w
+(it) 1987 7076 w
+(to) 2069 7076 w
+(the) 2173 7076 w
+(right) 2321 7076 w
+(format,) 2536 7076 w
+(and) 2853 7076 w
+10 /Courier f
+(uuencode) 3024 7076 w
+10 /Times-Roman f
+('s) 3504 7076 w
+(it) 3603 7076 w
+(to) 3686 7076 w
+(create) 3791 7076 w
+10 /Courier f
+(ievaluator7t.txt) 4055 7076 w
+10 /Times-Roman f
+(.) 5015 7076 w
+(Note) 720 7196 w
+(that) 1059 7196 w
+(as) 1354 7196 w
+(shipped,) 1582 7196 w
+(the) 2063 7196 w
+(configuration) 2330 7196 w
+(file) 3013 7196 w
+(\() 3291 7196 w
+10 /Courier f
+(evaluator7t) 3324 7196 w
+10 /Times-Roman f
+(\)) 3984 7196 w
+(includes) 4162 7196 w
+(the) 4640 7196 w
+(file) 4907 7196 w
+(November 22, 1900) 2482 7560 w
+cleartomark
+showpage
+saveobj restore
+%%EndPage: 1 1
+%%Page: 2 2
+/saveobj save def
+mark
+2 pagesetup
+10 /Times-Roman f
+(\255 2 \255) 2797 480 w
+10 /Courier f
+(/usr/inferno/usr/nigel/cb.dis) 720 840 w
+10 /Times-Roman f
+(as) 2504 840 w
+(the) 2631 840 w
+(main) 2797 840 w
+(application.) 3041 840 w
+(This) 3554 840 w
+(is) 3776 840 w
+(John) 3887 840 w
+(Powers') 4121 840 w
+(Crackerbarrel) 4493 840 w
+(program which I used as a benchmark.) 720 960 w
+10 /Times-Bold f
+(Downloading the code) 720 1200 w
+10 /Times-Roman f
+(To download to the Evaluator type the command) 970 1356 w
+9 /Courier-Bold f
+(Boot:) 1008 1516 w
+9 /Courier f
+(download) 1332 1516 w
+9 /Courier-Bold f
+(Ready to download. Use 'transmit' option on terminal emulator to download file.) 1008 1616 w
+10 /Times-Roman f
+(break back to the) 720 1796 w
+10 /Courier f
+(con\(1\)) 1429 1796 w
+10 /Times-Roman f
+(command prompt with Control\255\\ and type the following command) 1814 1796 w
+9 /Courier-Bold f
+(>>>) 1008 1956 w
+9 /Courier f
+(!cat ievaluator7t.txt) 1224 1956 w
+10 /Times-Roman f
+(As a shorthand, you may instead type) 720 2136 w
+9 /Courier-Bold f
+(>>>) 1008 2296 w
+9 /Courier f
+(!squirt) 1224 2296 w
+10 /Times-Roman f
+(The) 970 2476 w
+(red) 1152 2476 w
+(LED) 1306 2476 w
+(should) 1527 2476 w
+(light) 1821 2476 w
+(indicating) 2032 2476 w
+(download.) 2459 2476 w
+(After) 2905 2476 w
+(a) 3142 2476 w
+(couple) 3214 2476 w
+(of) 3508 2476 w
+(minutes) 3644 2476 w
+(the) 3989 2476 w
+(light) 4139 2476 w
+(goes) 4351 2476 w
+(out) 4562 2476 w
+(and) 4718 2476 w
+(you) 4890 2476 w
+(see) 720 2596 w
+9 /Courier-Bold f
+(Loaded file ievaluator7t.aif at address 00008000, size=254200) 1008 2756 w
+(Boot:) 1008 2856 w
+10 /Times-Roman f
+(You will type) 720 3036 w
+9 /Courier-Bold f
+(Boot:) 1008 3196 w
+9 /Courier f
+(gos 8080) 1332 3196 w
+10 /Times-Roman f
+(to run the code at address 0x8080. This) 720 3376 w
+(should) 2316 3376 w
+(cause) 2609 3376 w
+(Inferno) 2856 3376 w
+(to) 3175 3376 w
+(boot) 3279 3376 w
+(and) 3483 3376 w
+(write) 3653 3376 w
+(stuff) 3884 3376 w
+(on) 4093 3376 w
+(the) 4219 3376 w
+(console) 4367 3376 w
+(port) 4698 3376 w
+(\(i.e.) 4885 3376 w
+(the other window\).) 720 3496 w
+10 /Times-Bold f
+(Useful Stuff) 720 3736 w
+10 /Times-Roman f
+(There is some useful debugging code in) 970 3892 w
+10 /Courier f
+(archevaluator7t.c) 2587 3892 w
+10 /Times-Roman f
+(and other places.) 3632 3892 w
+10 /Courier f
+(setled7ascii\(\)) 720 4048 w
+10 /Times-Roman f
+(This) 970 4168 w
+(function) 1177 4168 w
+(takes) 1539 4168 w
+(a) 1773 4168 w
+(single) 1846 4168 w
+(character,) 2115 4168 w
+(and) 2534 4168 w
+(attempts) 2708 4168 w
+(to) 3077 4168 w
+(put) 3185 4168 w
+(it) 3343 4168 w
+(on) 3429 4168 w
+(the) 3559 4168 w
+(LED.) 3711 4168 w
+(Clearly) 3960 4168 w
+(you) 4284 4168 w
+(won't) 4464 4168 w
+(get) 4727 4168 w
+(M's) 4879 4168 w
+(or N's. Read the source.) 970 4288 w
+10 /Courier f
+(iprint\(\)) 720 4444 w
+10 /Times-Roman f
+(Prints on the debug and download port.) 970 4564 w
+(November 22, 1900) 2482 7560 w
+cleartomark
+showpage
+saveobj restore
+%%EndPage: 2 2
+%%Trailer
+done
+%%DocumentFonts: Times-Roman Times-Italic Times-Bold Courier Courier-Bold
+%%Pages: 2
--- /dev/null
+++ b/os/ks32/evaluator7t
@@ -1,0 +1,110 @@
+dev
+	root
+	cons archevaluator7t noscreen not
+#	kbd
+#	gpio
+#	mnt
+#	pipe
+	prog
+#	srv
+#	draw
+	uart
+#	sapcm
+#	flash
+#	touch
+#	ip	bootp ip ipv6 ipaux iproute arp netlog ptclbsum iprouter plan9 nullmedium pktmedium
+#	ether netif netaux ethermedium
+#	ata
+
+ip
+#	il
+#	tcp
+#	udp
+#	rudp
+#	igmp
+#	ipifc
+#	icmp
+#	icmp6
+#	ipmux
+
+lib
+	interp
+#	tk
+#	image
+#	memlayer
+#	memimage
+#	keyring
+	sec
+#	mp
+	kern
+
+mod
+	sys
+#	draw
+#	tk
+#	keyring
+
+port
+	alarm
+	alloc
+	allocb
+	chan
+	dev
+	dial
+	dis
+	discall
+	exception
+	exportfs
+	inferno
+	latin1
+	nocache
+	nodynld
+	noenv
+	parse
+	pgrp
+	print
+	proc
+	qio
+	qlock
+	random
+	sysfile
+	taslock
+	xalloc
+
+link	
+#	lcd
+#	ether589
+#	ethertdk
+#	pppmedium ppp compress
+
+code
+	int main_pool_pcnt = 60;
+	int heap_pool_pcnt = 40;
+	int image_pool_pcnt = 0;
+	int cflag = 0;
+
+	int consoleprint = 1;
+	int redirectconsole = 1;
+	char debug_keys = 1;
+	int panicreset = 0;
+	void pseudoRandomBytes(uchar *a, int n){memset(a, 0, n);}
+	int srvf2c(){return -1;}	/* dummy */
+	Type *Trdchan;
+	Type *Twrchan;
+
+init
+	evalinit
+
+root
+	/chan
+	/dev
+	/dis
+#	/usr/jrf/limbo/cb.dis
+#	/usr/jrf/work/dl/test/test.dis
+#	/o/abc.o
+	/dis/sh.dis
+	/net
+	/prog
+	/osinit.dis
+	/n/remote
+
--- /dev/null
+++ b/os/ks32/fns.h
@@ -1,0 +1,145 @@
+#include "../port/portfns.h"
+
+ulong	aifinit(uchar *aifarr);
+void	aamloop(int);
+void	archconfinit(void);
+int	archflash12v(int);
+void	archflashwp(int);
+void	archreboot(void);
+void	archreset(void);
+void	catchDref(char *s, void *v);
+void	catchDval(char *s, ulong v, ulong m);
+void	catchIref(char *s, void *a);
+void	cisread(int slotno, void (*f)(int, uchar *));
+int	cistrcmp(char *, char *);
+void	cleanDentry(void *);
+void	clockcheck(void);
+void	clockinit(void);
+void	clockpoll(void);
+#define	coherence()		/* nothing to do for cache coherence for uniprocessor */
+uint	cpsrr(void);
+void	cursorhide(void);
+void	cursorunhide(void);
+void	dmasetup(int channel, int device, int direction, int endianess);
+void	dmastart(int channel, void *b1, int b1siz, void *b2, int b2siz);
+int	dmacontinue(int channel, void *buf, int bufsize);
+void	dmastop(int channel);
+int	dmaerror(int channel);
+void	dmareset(void);
+void	drainWBuffer(void);
+void dumplongs(char *, ulong *, int);
+void	dumpregs(Ureg* ureg);
+void	dumpstk(ulong *);
+void	flushDcache(void);
+void	flushIDC(void);
+void	flushIcache(void);
+void	flushDentry(void *);
+void	flushTLB(void);
+int	fpiarm(Ureg*);
+void	fpinit(void);
+ulong	getcallerpc(void*);
+void	gotopc(ulong);
+#define	idlehands()			/* nothing to do in the runproc */
+void	intrenable(int, void (*)(Ureg*, void*), void*, int);
+void intrclear(int, int);
+void intrmask(int, int);
+void intrunmask(int, int);
+int	iprint(char *fmt, ...);
+void	installprof(void (*)(Ureg *, int));
+int	isvalid_va(void*);
+void	kbdinit(void);
+void	lcd_setbacklight(int);
+void	lcd_setbrightness(ushort);
+void	lcd_setcontrast(ushort);
+void	lcd_sethz(int);
+void	lights(ulong);
+void setled7ascii(char);
+void	links(void);
+ulong	mcpgettfreq(void);
+void	mcpinit(void);
+void	mcpsettfreq(ulong tfreq);
+void	mcpspeaker(int, int);
+void	mcptelecomsetup(ulong hz, uchar adm, uchar xint, uchar rint);
+ushort	mcpadcread(int ts);
+void	mcptouchsetup(int ts);
+void	mcptouchintrenable(void);
+void	mcptouchintrdisable(void);
+void	mcpgpiowrite(ushort mask, ushort data);
+void	mcpgpiosetdir(ushort mask, ushort dir);
+ushort	mcpgpioread(void);
+void	mmuinit(void);
+ulong	mmuctlregr(void);
+void	mmuctlregw(ulong);
+ulong	mmuregr(int);
+void	mmuregw(int, ulong);
+void	mmureset(void);
+void	mouseinit(void);
+void	nowriteSeg(void *, void *);
+void*	pa2va(ulong);
+int	pcmpin(int slot, int type);
+void	pcmpower(int slotno, int on);
+int	pcmpowered(int slotno);
+void	pcmsetvcc(int slotno, int vcc);
+void	pcmsetvpp(int slotno, int vpp);
+int	pcmspecial(char *idstr, ISAConf *isa);
+void	pcmspecialclose(int slotno);
+void	pcmintrenable(int, void (*)(Ureg*, void*), void*);
+void	putcsr(ulong);
+#define procsave(p)
+#define procrestore(p)
+void	remaplomem(void);
+long	rtctime(void);
+void*	screenalloc(ulong);
+void	screeninit(void);
+void	screenputs(char*, int);
+int	segflush(void*, ulong);
+void	setpanic(void);
+void	setr13(int, void*);
+uint	spsrr(void);
+void	touchrawcal(int q, int px, int py);
+int	touchcalibrate(void);
+int	touchreadxy(int *fx, int *fy);
+int	touchpressed(void);
+int	touchreleased(void);
+void	touchsetrawcal(int q, int n, int v);
+int	touchgetrawcal(int q, int n);
+void	trapinit(void);
+void	trapspecial(int (*)(Ureg *, uint));
+int	uartprint(char*, ...);
+void	uartspecial(int, int, char, Queue**, Queue**, int (*)(Queue*, int));
+void	umbfree(ulong addr, int size);
+ulong	umbmalloc(ulong addr, int size, int align);
+void	umbscan(void);
+ulong	va2pa(void*);
+void	vectors(void);
+void	vtable(void);
+#define	waserror()	(up->nerrlab++, setlabel(&up->errlab[up->nerrlab-1]))
+int	wasbusy(int);
+void	_vfiqcall(void);
+void	_virqcall(void);
+void	_vundcall(void);
+void	_vsvccall(void);
+void	_vpabcall(void);
+void	_vdabcall(void);
+void	vgaputc(char);
+void	writeBackBDC(void);
+void	writeBackDC(void);
+
+#define KADDR(p)	((void *) p)
+#define PADDR(v)	va2pa((void*)(v))
+
+// #define timer_start()	(*OSCR)
+// #define timer_ticks(t)	(*OSCR - (ulong)(t))
+#define DELAY(ms)	timer_delay(MS2TMR(ms))
+#define MICRODELAY(us)	timer_delay(US2TMR(us))
+ulong	timer_start(void);
+ulong	timer_ticks(ulong);
+int 	timer_devwait(ulong *adr, ulong mask, ulong val, int ost);
+void 	timer_setwatchdog(int ost);
+void 	timer_delay(int ost);
+ulong	ms2tmr(int ms);
+int	tmr2ms(ulong t);
+void	delay(int ms);
+ulong	us2tmr(int us);
+int	tmr2us(ulong t);
+void 	microdelay(int us);
--- /dev/null
+++ b/os/ks32/fpi.h
@@ -1,0 +1,61 @@
+typedef long Word;
+typedef unsigned long Single;
+typedef struct {
+	unsigned long l;
+	unsigned long h;
+} Double;
+
+enum {
+	FractBits	= 28,
+	CarryBit	= 0x10000000,
+	HiddenBit	= 0x08000000,
+	MsBit		= HiddenBit,
+	NGuardBits	= 3,
+	GuardMask	= 0x07,
+	LsBit		= (1<<NGuardBits),
+
+	SingleExpBias	= 127,
+	SingleExpMax	= 255,
+	DoubleExpBias	= 1023,
+	DoubleExpMax	= 2047,
+
+	ExpBias		= DoubleExpBias,
+	ExpInfinity	= DoubleExpMax,
+};
+
+typedef struct {
+	unsigned char s;
+	short e;
+	long l;				/* 0000FFFFFFFFFFFFFFFFFFFFFFFFFGGG */
+	long h;				/* 0000HFFFFFFFFFFFFFFFFFFFFFFFFFFF */
+} Internal;
+
+#define IsWeird(n)	((n)->e >= ExpInfinity)
+#define	IsInfinity(n)	(IsWeird(n) && (n)->h == HiddenBit && (n)->l == 0)
+#define	SetInfinity(n)	((n)->e = ExpInfinity, (n)->h = HiddenBit, (n)->l = 0)
+#define IsNaN(n)	(IsWeird(n) && (((n)->h & ~HiddenBit) || (n)->l))
+#define	SetQNaN(n)	((n)->s = 0, (n)->e = ExpInfinity, 		\
+			 (n)->h = HiddenBit|(LsBit<<1), (n)->l = 0)
+#define IsZero(n)	((n)->e == 1 && (n)->h == 0 && (n)->l == 0)
+#define SetZero(n)	((n)->e = 1, (n)->h = 0, (n)->l = 0)
+
+/*
+ * fpi.c
+ */
+extern void fpiround(Internal *);
+extern void fpiadd(Internal *, Internal *, Internal *);
+extern void fpisub(Internal *, Internal *, Internal *);
+extern void fpimul(Internal *, Internal *, Internal *);
+extern void fpidiv(Internal *, Internal *, Internal *);
+extern int fpicmp(Internal *, Internal *);
+extern void fpinormalise(Internal*);
+
+/*
+ * fpimem.c
+ */
+extern void fpis2i(Internal *, void *);
+extern void fpid2i(Internal *, void *);
+extern void fpiw2i(Internal *, void *);
+extern void fpii2s(void *, Internal *);
+extern void fpii2d(void *, Internal *);
+extern void fpii2w(Word *, Internal *);
--- /dev/null
+++ b/os/ks32/fpiarm.c
@@ -1,0 +1,483 @@
+/*
+ * this doesn't attempt to implement ARM floating-point properties
+ * that aren't visible in the Inferno environment.
+ * all arithmetic is done in double precision.
+ * the FP trap status isn't updated.
+ */
+#include "u.h"
+#include	"../port/lib.h"
+#include	"mem.h"
+#include	"dat.h"
+#include	"fns.h"
+#include	"io.h"
+
+#include	"ureg.h"
+
+#include "fpi.h"
+
+// #define	R13OK	undef this if correct kernel r13 isn't in Ureg; check calculation in fpiarm below
+
+#define	REG(x) (*(long*)(((char*)ur)+roff[(x)]))
+#define	FPENV	(*ufp)
+#define	FR(x) (*(Internal*)ufp->regs[(x)&7])
+
+/* BUG: check fetch (not worthwhile in Inferno) */
+#define	getubyte(a) (*(uchar*)(a))
+#define	getuword(a) (*(ushort*)(a))
+#define	getulong(a) (*(ulong*)(a))
+
+typedef struct FP2 FP2;
+typedef struct FP1 FP1;
+
+struct FP2 {
+	char*	name;
+	void	(*f)(Internal, Internal, Internal*);
+};
+
+struct FP1 {
+	char*	name;
+	void	(*f)(Internal*, Internal*);
+};
+
+enum {
+	N = 1<<31,
+	Z = 1<<30,
+	C = 1<<29,
+	V = 1<<28,
+	REGPC = 15,
+};
+
+int	fpemudebug = 0;
+
+#undef OFR
+#define	OFR(X)	((ulong)&((Ureg*)0)->X)
+
+static	int	roff[] = {
+	OFR(r0), OFR(r1), OFR(r2), OFR(r3),
+	OFR(r4), OFR(r5), OFR(r6), OFR(r7),
+	OFR(r8), OFR(r9), OFR(r10), OFR(r11),
+#ifdef R13OK
+	OFR(r12), OFR(r13), OFR(r14), OFR(pc),
+#else
+	OFR(r12), OFR(type), OFR(r14), OFR(pc),
+#endif
+};
+
+static Internal fpconst[8] = {	/* indexed by op&7 */
+	/* s, e, l, h */
+	{0, 0x1, 0x00000000, 0x00000000}, /* 0.0 */
+	{0, 0x3FF, 0x00000000, 0x08000000},	/* 1.0 */
+	{0, 0x400, 0x00000000, 0x08000000},	/* 2.0 */
+	{0, 0x400, 0x00000000, 0x0C000000},	/* 3.0 */
+	{0, 0x401, 0x00000000, 0x08000000},	/* 4.0 */
+	{0, 0x401, 0x00000000, 0x0A000000},	/* 5.0 */
+	{0, 0x3FE, 0x00000000, 0x08000000},	/* 0.5 */
+	{0, 0x402, 0x00000000, 0x0A000000},	/* 10.0 */
+};
+
+/*
+ * arm binary operations
+ */
+
+static void
+fadd(Internal m, Internal n, Internal *d)
+{
+	(m.s == n.s? fpiadd: fpisub)(&m, &n, d);
+}
+
+static void
+fsub(Internal m, Internal n, Internal *d)
+{
+	m.s ^= 1;
+	(m.s == n.s? fpiadd: fpisub)(&m, &n, d);
+}
+
+static void
+fsubr(Internal m, Internal n, Internal *d)
+{
+	n.s ^= 1;
+	(n.s == m.s? fpiadd: fpisub)(&n, &m, d);
+}
+
+static void
+fmul(Internal m, Internal n, Internal *d)
+{
+	fpimul(&m, &n, d);
+}
+
+static void
+fdiv(Internal m, Internal n, Internal *d)
+{
+	fpidiv(&m, &n, d);
+}
+
+static void
+fdivr(Internal m, Internal n, Internal *d)
+{
+	fpidiv(&n, &m, d);
+}
+
+/*
+ * arm unary operations
+ */
+
+static void
+fmov(Internal *m, Internal *d)
+{
+	*d = *m;
+}
+
+static void
+fmovn(Internal *m, Internal *d)
+{
+	*d = *m;
+	d->s ^= 1;
+}
+
+static void
+fabsf(Internal *m, Internal *d)
+{
+	*d = *m;
+	d->s = 0;
+}
+
+static void
+frnd(Internal *m, Internal *d)
+{
+	short e;
+
+	(m->s? fsub: fadd)(fpconst[6], *m, d);
+	if(IsWeird(d))
+		return;
+	fpiround(d);
+	e = (d->e - ExpBias) + 1;
+	if(e <= 0)
+		SetZero(d);
+	else if(e > FractBits){
+		if(e < 2*FractBits)
+			d->l &= ~((1<<(2*FractBits - e))-1);
+	}else{
+		d->l = 0;
+		if(e < FractBits)
+			d->h &= ~((1<<(FractBits-e))-1);
+	}
+}
+
+static	FP1	optab1[16] = {	/* Fd := OP Fm */
+[0]	{"MOVF",	fmov},
+[1]	{"NEGF",	fmovn},
+[2]	{"ABSF",	fabsf},
+[3]	{"RNDF",	frnd},
+[4]	{"SQTF",	/*fsqt*/0},
+/* LOG, LGN, EXP, SIN, COS, TAN, ASN, ACS, ATN all `deprecated' */
+/* URD and NRM aren't implemented */
+};
+
+static	FP2	optab2[16] = {	/* Fd := Fn OP Fm */
+[0]	{"ADDF",	fadd},
+[1]	{"MULF",	fmul},
+[2]	{"SUBF",	fsub},
+[3]	{"RSUBF",	fsubr},
+[4]	{"DIVF",	fdiv},
+[5]	{"RDIVF",	fdivr},
+/* POW, RPW deprecated */
+[8]	{"REMF",	/*frem*/0},
+[9]	{"FMF",	fmul},	/* fast multiply */
+[10]	{"FDV",	fdiv},	/* fast divide */
+[11]	{"FRD",	fdivr},	/* fast reverse divide */
+/* POL deprecated */
+};
+
+static ulong
+fcmp(Internal *n, Internal *m)
+{
+	int i;
+
+	if(IsWeird(m) || IsWeird(n)){
+		/* BUG: should trap if not masked */
+		return V|C;
+	}
+	i = fpicmp(n, m);
+	if(i > 0)
+		return C;
+	else if(i == 0)
+		return C|Z;
+	else
+		return N;
+}
+
+static void
+fld(void (*f)(Internal*, void*), int d, ulong ea, int n, FPenv *ufp)
+{
+	void *mem;
+
+	mem = (void*)ea;
+	(*f)(&FR(d), mem);
+	if(fpemudebug)
+		print("MOV%c #%lux, F%d\n", n==8? 'D': 'F', ea, d);
+}
+
+static void
+fst(void (*f)(void*, Internal*), ulong ea, int s, int n, FPenv *ufp)
+{
+	Internal tmp;
+	void *mem;
+
+	mem = (void*)ea;
+	tmp = FR(s);
+	if(fpemudebug)
+		print("MOV%c	F%d,#%lux\n", n==8? 'D': 'F', s, ea);
+	(*f)(mem, &tmp);
+}
+
+static int
+condok(int cc, int c)
+{
+	switch(c){
+	case 0:	/* Z set */
+		return cc&Z;
+	case 1:	/* Z clear */
+		return (cc&Z) == 0;
+	case 2:	/* C set */
+		return cc&C;
+	case 3:	/* C clear */
+		return (cc&C) == 0;
+	case 4:	/* N set */
+		return cc&N;
+	case 5:	/* N clear */
+		return (cc&N) == 0;
+	case 6:	/* V set */
+		return cc&V;
+	case 7:	/* V clear */
+		return (cc&V) == 0;
+	case 8:	/* C set and Z clear */
+		return cc&C && (cc&Z) == 0;
+	case 9:	/* C clear or Z set */
+		return (cc&C) == 0 || cc&Z;
+	case 10:	/* N set and V set, or N clear and V clear */
+		return (~cc&(N|V))==0 || (cc&(N|V)) == 0;
+	case 11:	/* N set and V clear, or N clear and V set */
+		return (cc&(N|V))==N || (cc&(N|V))==V;
+	case 12:	/* Z clear, and either N set and V set or N clear and V clear */
+		return (cc&Z) == 0 && ((~cc&(N|V))==0 || (cc&(N|V))==0);
+	case 13:	/* Z set, or N set and V clear or N clear and V set */
+		return (cc&Z) || (cc&(N|V))==N || (cc&(N|V))==V;
+	case 14:	/* always */
+		return 1;
+	case 15:	/* never (reserved) */
+		return 0;
+	}
+	return 0;	/* not reached */
+}
+
+static void
+unimp(ulong pc, ulong op)
+{
+	char buf[60];
+
+	snprint(buf, sizeof(buf), "sys: fp: pc=%lux unimp fp 0x%.8lux", pc, op);
+	if(fpemudebug)
+		print("FPE: %s\n", buf);
+	error(buf);
+	/* no return */
+}
+
+static void
+fpemu(ulong pc, ulong op, Ureg *ur, FPenv *ufp)
+{
+	int rn, rd, tag, o;
+	long off;
+	ulong ea;
+	Internal tmp, *fm, *fn;
+
+	/* note: would update fault status here if we noted numeric exceptions */
+
+	/*
+	 * LDF, STF; 10.1.1
+	 */
+	if(((op>>25)&7) == 6){
+		if(op & (1<<22))
+			unimp(pc, op);	/* packed or extended */
+		rn = (op>>16)&0xF;
+		off = (op&0xFF)<<2;
+		if((op & (1<<23)) == 0)
+			off = -off;
+		ea = REG(rn);
+		if(rn == REGPC)
+			ea += 8;
+		if(op & (1<<24))
+			ea += off;
+		rd = (op>>12)&7;
+		if(op & (1<<20)){
+			if(op & (1<<15))
+				fld(fpid2i, rd, ea, 8, ufp);
+			else
+				fld(fpis2i, rd, ea, 4, ufp);
+		}else{
+			if(op & (1<<15))
+				fst(fpii2d, ea, rd, 8, ufp);
+			else
+				fst(fpii2s, ea, rd, 4, ufp);
+		}
+		if((op & (1<<24)) == 0)
+			ea += off;
+		if(op & (1<<21))
+			REG(rn) = ea;
+		return;
+	}
+
+	/*
+	 * CPRT/transfer, 10.3
+	 */
+	if(op & (1<<4)){
+		rd = (op>>12) & 0xF;
+
+		/*
+		 * compare, 10.3.1
+		 */
+		if(rd == 15 && op & (1<<20)){
+			rn = (op>>16)&7;
+			fn = &FR(rn);
+			if(op & (1<<3)){
+				fm = &fpconst[op&7];
+				tag = 'C';
+			}else{
+				fm = &FR(op&7);
+				tag = 'F';
+			}
+			switch((op>>21)&7){
+			default:
+				unimp(pc, op);
+			case 4:	/* CMF: Fn :: Fm */
+			case 6:	/* CMFE: Fn :: Fm (with exception) */
+				ur->psr &= ~(N|C|Z|V);
+				ur->psr |= fcmp(fn, fm);
+				break;
+			case 5:	/* CNF: Fn :: -Fm */
+			case 7:	/* CNFE: Fn :: -Fm (with exception) */
+				tmp = *fm;
+				tmp.s ^= 1;
+				ur->psr &= ~(N|C|Z|V);
+				ur->psr |= fcmp(fn, &tmp);
+				break;
+			}
+			if(fpemudebug)
+				print("CMPF	%c%d,F%ld =%x\n", tag, rn, op&7, ur->psr>>28);
+			return;
+		}
+
+		/*
+		 * other transfer, 10.3
+		 */
+		switch((op>>20)&0xF){
+		default:
+			unimp(pc, op);
+		case 0:	/* FLT */
+			rn = (op>>16) & 7;
+			fpiw2i(&FR(rn), &REG(rd));
+			if(fpemudebug)
+				print("MOVW[FD]	R%d, F%d\n", rd, rn);
+			break;
+		case 1:	/* FIX */
+			if(op & (1<<3))
+				unimp(pc, op);
+			rn = op & 7;
+			tmp = FR(rn);
+			fpii2w(&REG(rd), &tmp);
+			if(fpemudebug)
+				print("MOV[FD]W	F%d, R%d =%ld\n", rn, rd, REG(rd));
+			break;
+		case 2:	/* FPSR := Rd */
+			FPENV.status = REG(rd);
+			if(fpemudebug)
+				print("MOVW	R%d, FPSR\n", rd);
+			break;
+		case 3:	/* Rd := FPSR */
+			REG(rd) = FPENV.status;
+			if(fpemudebug)
+				print("MOVW	FPSR, R%d\n", rd);
+			break;
+		case 4:	/* FPCR := Rd */
+			FPENV.control = REG(rd);
+			if(fpemudebug)
+				print("MOVW	R%d, FPCR\n", rd);
+			break;
+		case 5:	/* Rd := FPCR */
+			REG(rd) = FPENV.control;
+			if(fpemudebug)
+				print("MOVW	FPCR, R%d\n", rd);
+			break;
+		}
+		return;
+	}
+
+	/*
+	 * arithmetic
+	 */
+
+	if(op & (1<<3)){	/* constant */
+		fm = &fpconst[op&7];
+		tag = 'C';
+	}else{
+		fm = &FR(op&7);
+		tag = 'F';
+	}
+	rd = (op>>12)&7;
+	o = (op>>20)&0xF;
+	if(op & (1<<15)){	/* monadic */
+		FP1 *fp;
+		fp = &optab1[o];
+		if(fp->f == nil)
+			unimp(pc, op);
+		if(fpemudebug)
+			print("%s	%c%ld,F%d\n", fp->name, tag, op&7, rd);
+		(*fp->f)(fm, &FR(rd));
+	} else {
+		FP2 *fp;
+		fp = &optab2[o];
+		if(fp->f == nil)
+			unimp(pc, op);
+		rn = (op>>16)&7;
+		if(fpemudebug)
+			print("%s	%c%ld,F%d,F%d\n", fp->name, tag, op&7, rn, rd);
+		(*fp->f)(*fm, FR(rn), &FR(rd));
+	}
+}
+
+/*
+ * returns the number of FP instructions emulated
+ */
+int
+fpiarm(Ureg *ur)
+{
+	ulong op, o;
+	FPenv *ufp;
+	int n;
+
+#ifndef R13OK
+/*	ur->type = &ur->pc+1;	/* calculate kernel sp/R13 and put it here for roff[13] */
+	ur->type = (ulong)(ur + 1);
+#endif
+	if (up == nil)
+		panic("fpiarm not in a process");
+	ufp = &up->env->fpu;	/* because all the state is in Osenv, it need not be saved/restored */
+	if(ufp->fpistate != FPACTIVE) {
+		ufp->fpistate = FPACTIVE;
+		ufp->control = 0;
+		ufp->status = (0x01<<28)|(1<<12);	/* software emulation, alternative C flag */
+		for(n = 0; n < 8; n++)
+			FR(n) = fpconst[0];
+	}
+	for(n=0;;n++){
+		op = getulong(ur->pc);
+		o = (op>>24) & 0xF;
+		if(((op>>8) & 0xF) != 1 || o != 0xE && (o&~1) != 0xC)
+			break;
+		if(condok(ur->psr, op>>28))
+			fpemu(ur->pc, op, ur, ufp);
+		ur->pc += 4;
+		if(anyhigher())
+			sched();
+	}
+	return n;
+}
--- /dev/null
+++ b/os/ks32/io.h
@@ -1,0 +1,168 @@
+/*
+ * Memory Map for Samsung ks32c50100
+ */
+
+#define SFRbase	0x7ff0000
+
+#define SYSCFG	(*(ulong *)(SFRbase + 0))
+
+#define IOPbase	(SFRbase + 0x5000)
+#define IOPMOD	(*(ulong *)(IOPbase + 0))
+#define IOPCON	(*(ulong *)(IOPbase + 4))
+#define IOPDATA	(*(ulong *)(IOPbase + 8))
+
+#define MaxIRQbit		20			/* Maximum IRQ */
+#define EXT0bit		0
+#define EXT1bit		1
+#define EXT2bit		2
+#define EXT3bit		3
+#define UART0TXbit		4
+#define UART0RXbit		5
+#define UART1TXbit		6
+#define UART1RXbit		7
+#define GDMA0		8
+#define GDMA1		9
+#define TIMER0bit		10
+#define TIMER1bit		11
+#define HDLCATXbit		12
+#define HDLCARXbit		13
+#define HDLCBTXbit		14
+#define HDLCBRXbit		15
+#define ETHBDMATXbit	16
+#define ETHBDMARXbit	17
+#define ETHMACRXint	18
+#define ETHMAXTXint	19
+#define IICbit			20
+
+#define TIMERbit(n) 		(TIMER0bit + n)
+#define UARTTXbit(n)	(UART0TXbit + (n) * 2)
+#define UARTRXbit(n)	(UART0RXbit + (n) * 2)
+
+/*
+  * Interrupt controller
+  */
+
+#define INTbase	(SFRbase + 0x4000)
+#define INTREG	((IntReg *)INTbase)
+
+typedef struct IntReg IntReg;
+struct IntReg {
+	ulong	mod;		/* 00 */
+	ulong	pnd;			/* 04 */
+	ulong	msk;			/* 08 */
+	ulong	pri[6];		/* 0c */
+	ulong	offset;		/* 24 */
+	ulong	pndpri;		/* 28 */
+	ulong	pndtst;		/* 2c */
+	ulong	oset_fiq;		/* 30 */
+	ulong	oset_irq;		/* 34 */
+};
+
+/*
+  * UARTs
+  */
+#define UART0base	(SFRbase + 0xd000)
+#define UART1base	(SFRbase + 0xe000)
+#define UARTREG	((UartReg *)UART0base)
+
+typedef struct UartReg UartReg;
+struct UartReg {
+	ulong	lcon;		/* 00 */
+	ulong	con;		/* 04 */
+	ulong	stat;		/* 08 */
+	ulong	txbuf;	/* 0c */
+	ulong	rxbuf;	/* 10 */
+	ulong	brdiv;	/* 14 */
+	ulong	pad[(UART1base - UART0base - 0x18) / 4];
+};
+
+#define ULCON_WLMASK		0x03
+#define ULCON_WL5		0x00
+#define ULCON_WL6		0x01
+#define ULCON_WL7		0x02
+#define ULCON_WL8		0x03
+
+#define ULCON_STOPMASK	0x04
+#define ULCON_STOP1		0x00
+#define ULCON_STOP2		0x04
+
+#define ULCON_PMDMASK	0x38
+#define ULCON_PMDNONE	0x00
+#define ULCON_PMDODD	(4 << 3)
+#define ULCON_PMDEVEN	(5 << 3)
+#define ULCON_PMDFORCE1	(6 << 3)
+#define ULCON_PMDFORCE0	(7 << 3)
+
+#define ULCON_CLOCKMASK	0x40
+#define ULCON_CLOCKMCLK	0x00
+#define ULCON_CLOCKUCLK	(1 << 6)
+
+#define ULCON_IRMASK		0x80
+#define ULCON_IROFF		0x00
+#define ULCON_IRON		0x80
+
+#define UCON_RXMDMASK	0x03
+#define UCON_RXMDOFF		0x00
+#define UCON_RXMDINT		0x01
+#define UCON_RXMDGDMA0	0x02
+#define UCON_RXMDGDMA1	0x03
+
+#define UCON_SINTMASK	0x04
+#define UCON_SINTOFF		0x00
+#define UCON_SINTON		0x04
+
+#define UCON_TXMDMASK	0x18
+#define UCON_TXMDOFF		(0 << 3)
+#define UCON_TXMDINT		(1 << 3)
+#define UCON_TXMDGDMA0	(2 << 3)
+#define UCON_TXMDGDMA1	(3 << 3)
+
+#define UCON_DSRMASK		0x20
+#define UCON_DSRON		(1 << 5)
+#define UCON_DSROFF		(0 << 5)
+
+#define UCON_BRKMASK		0x40
+#define UCON_BRKON		(1 << 6)
+#define UCON_BRKOFF		(0 << 6)
+
+#define UCON_LOOPMASK	0x80
+#define UCON_LOOPON		0x80
+#define UCON_LOOPOFF		0x00
+
+#define USTAT_OV			0x01
+#define USTAT_PE			0x02
+#define USTAT_FE			0x04
+#define USTAT_BKD			0x08
+#define USTAT_DTR			0x10
+#define USTAT_RDR			0x20
+#define USTAT_TBE			0x40
+#define USTAT_TC			0x80
+
+/*
+  * Timers
+  */
+#define TIMERbase	(SFRbase + 0x6000)
+#define TIMERREG	((TimerReg *)TIMERbase)
+
+typedef struct TimerReg TimerReg;
+struct TimerReg {
+	ulong mod;
+	ulong data[2];
+	ulong cnt[2];
+};
+
+/*
+ *	PC compatibility support for PCMCIA drivers
+ */
+
+extern ulong ins(ulong);		/* return ulong to prevent unecessary compiler shifting */
+void outs(ulong, int);
+#define inb(addr)	(*((uchar*)(addr)))
+#define inl(addr)	(*((ulong*)(addr)))
+ulong ins(ulong);
+#define outb(addr, val)	*((uchar*)(addr)) = (val)
+#define outl(addr, val)	*((ulong*)(addr)) = (val)
+
+void inss(ulong, void*, int);
+void outss(ulong, void*, int);
+
--- /dev/null
+++ b/os/ks32/l.s
@@ -1,0 +1,205 @@
+#include "mem.h"
+
+/*
+ * 		Entered from the boot loader with
+ *		supervisor mode, interrupts disabled;
+ */
+
+TEXT _startup(SB), $-4
+	MOVW		$setR12(SB), R12 	/* static base (SB) */
+	MOVW		$Mach0(SB), R13
+	ADD		$(KSTACK-4), R13	/* leave 4 bytes for link */
+
+	MOVW		$(PsrDirq|PsrDfiq|PsrMsvc), R1	/* Switch to SVC mode */
+	MOVW		R1, CPSR
+
+	BL		main(SB)		/* jump to kernel */
+
+dead:
+	B	dead
+	BL	_div(SB)			/* hack to get _div etc loaded */
+
+GLOBL 		Mach0(SB), $KSTACK
+
+TEXT setr13(SB), $-4
+	MOVW		4(FP), R1
+
+	MOVW		CPSR, R2
+	BIC		$PsrMask, R2, R3
+	ORR		R0, R3
+	MOVW		R3, CPSR
+
+	MOVW		R13, R0
+	MOVW		R1, R13
+
+	MOVW		R2, CPSR
+	RET
+
+TEXT _vundcall(SB), $-4			
+_vund:
+	MOVM.DB		[R0-R3], (R13)
+	MOVW		$PsrMund, R0
+	B		_vswitch
+
+TEXT _vsvccall(SB), $-4				
+_vsvc:
+	MOVW.W		R14, -4(R13)
+	MOVW		CPSR, R14
+	MOVW.W		R14, -4(R13)
+	BIC		$PsrMask, R14
+	ORR		$(PsrDirq|PsrDfiq|PsrMsvc), R14
+	MOVW		R14, CPSR
+	MOVW		$PsrMsvc, R14
+	MOVW.W		R14, -4(R13)
+	B		_vsaveu
+
+TEXT _vpabcall(SB), $-4			
+_vpab:
+	MOVM.DB		[R0-R3], (R13)
+	MOVW		$PsrMabt, R0
+	B		_vswitch
+
+TEXT _vdabcall(SB), $-4	
+_vdab:
+	MOVM.DB		[R0-R3], (R13)
+	MOVW		$(PsrMabt+1), R0
+	B		_vswitch
+
+TEXT _vfiqcall(SB), $-4				/* IRQ */
+_vfiq:		/* FIQ */
+	MOVM.DB		[R0-R3], (R13)
+	MOVW		$PsrMfiq, R0
+	B		_vswitch
+
+TEXT _virqcall(SB), $-4				/* IRQ */
+_virq:
+	MOVM.DB		[R0-R3], (R13)
+	MOVW		$PsrMirq, R0
+
+_vswitch:					/* switch to svc mode */
+	MOVW		SPSR, R1
+	MOVW		R14, R2
+	MOVW		R13, R3
+
+	MOVW		CPSR, R14
+	BIC		$PsrMask, R14
+	ORR		$(PsrDirq|PsrDfiq|PsrMsvc), R14
+	MOVW		R14, CPSR
+
+	MOVM.DB.W 	[R0-R2], (R13)
+	MOVM.DB	  	(R3), [R0-R3]
+
+_vsaveu:						/* Save Registers */
+	MOVW.W		R14, -4(R13)			/* save link */
+/*	MCR		CpMMU, 0, R0, C(0), C(0), 0 */	
+
+	SUB		$8, R13
+	MOVM.DB.W 	[R0-R12], (R13)
+
+	MOVW		R0, R0				/* gratuitous noop */
+
+	MOVW		$setR12(SB), R12		/* static base (SB) */
+	MOVW		R13, R0				/* argument is ureg */
+	SUB		$8, R13				/* space for arg+lnk*/
+	BL		trap(SB)
+
+
+_vrfe:							/* Restore Regs */
+	MOVW		CPSR, R0			/* splhi on return */
+	ORR		$(PsrDirq|PsrDfiq), R0, R1
+	MOVW		R1, CPSR
+	ADD		$(8+4*15), R13		/* [r0-R14]+argument+link */
+	MOVW		(R13), R14			/* restore link */
+	MOVW		8(R13), R0
+	MOVW		R0, SPSR
+	MOVM.DB.S 	(R13), [R0-R14]		/* restore user registers */
+	MOVW		R0, R0				/* gratuitous nop */
+	ADD		$12, R13		/* skip saved link+type+SPSR*/
+	RFE					/* MOVM.IA.S.W (R13), [R15] */
+	
+TEXT splhi(SB), $-4					
+	MOVW		CPSR, R0
+	ORR		$(PsrDirq), R0, R1
+	MOVW		R1, CPSR
+	RET
+
+TEXT spllo(SB), $-4
+	MOVW		CPSR, R0
+	BIC		$(PsrDirq|PsrDfiq), R0, R1
+	MOVW		R1, CPSR
+	RET
+
+TEXT splx(SB), $-4
+	/* BUG - save PC in m->splpc - JB */
+
+TEXT splxpc(SB), $-4
+	MOVW		R0, R1
+	MOVW		CPSR, R0
+	MOVW		R1, CPSR
+	RET
+
+TEXT islo(SB), $-4
+	MOVW		CPSR, R0
+	AND		$(PsrDirq), R0
+	EOR		$(PsrDirq), R0
+	RET
+
+TEXT splfhi(SB), $-4					
+	MOVW		CPSR, R0
+	ORR		$(PsrDfiq|PsrDirq), R0, R1
+	MOVW		R1, CPSR
+	RET
+
+TEXT splflo(SB), $-4
+	MOVW		CPSR, R0
+	BIC		$(PsrDfiq), R0, R1
+	MOVW		R1, CPSR
+	RET
+
+TEXT cpsrr(SB), $-4
+	MOVW		CPSR, R0
+	RET
+
+TEXT spsrr(SB), $-4
+	MOVW		SPSR, R0
+	RET
+
+TEXT getcallerpc(SB), $-4
+	MOVW		0(R13), R0
+	RET
+
+TEXT _tas(SB), $-4
+	MOVW		R0, R1
+	MOVW		$0xDEADDEAD, R2
+	SWPW		R2, (R1), R0
+	RET
+
+TEXT setlabel(SB), $-4
+	MOVW		R13, 0(R0)		/* sp */
+	MOVW		R14, 4(R0)		/* pc */
+	MOVW		$0, R0
+	RET
+
+TEXT gotolabel(SB), $-4
+	MOVW		0(R0), R13		/* sp */
+	MOVW		4(R0), R14		/* pc */
+	MOVW		$1, R0
+	BX			(R14)
+
+TEXT outs(SB), $-4
+	MOVW	4(FP),R1
+	WORD	$0xe1c010b0	/* STR H R1,[R0+0] */
+	RET
+
+TEXT ins(SB), $-4
+	WORD	$0xe1d000b0	/* LDRHU R0,[R0+0] */
+	RET
+
+/* for devboot */
+TEXT	gotopc(SB), $-4
+/*
+	MOVW	R0, R1
+	MOVW	bootparam(SB), R0
+	MOVW	R1, PC
+*/
+	RET
--- /dev/null
+++ b/os/ks32/main.c
@@ -1,0 +1,289 @@
+#include "u.h"
+#include "../port/lib.h"
+#include "mem.h"
+#include "io.h"
+#include "dat.h"
+#include "fns.h"
+#include "../port/error.h"
+#include "version.h"
+
+Mach *m = (Mach*)MACHADDR;
+Proc *up = 0;
+Conf conf;
+
+extern ulong kerndate;
+extern int cflag;
+extern int consoleprint;
+extern int redirectconsole;
+extern int main_pool_pcnt;
+extern int heap_pool_pcnt;
+extern int image_pool_pcnt;
+extern int kernel_pool_pcnt;
+
+int
+segflush(void *p, ulong l)
+{
+	USED(p, l);
+	return 1;
+}
+
+static void
+poolsizeinit(void)
+{
+	ulong nb;
+
+	nb = conf.npage*BY2PG;
+	iprint("free memory %ld\n", nb);
+	poolsize(mainmem, (nb*main_pool_pcnt)/100, 0);
+	poolsize(heapmem, (nb*heap_pool_pcnt)/100, 0);
+	poolsize(imagmem, (nb*image_pool_pcnt)/100, 1);
+}
+
+void
+reboot(void)
+{
+	exit(0);
+}
+
+void
+halt(void)
+{
+	spllo();
+	print("cpu halted\n");
+	while(1);
+}
+
+void
+confinit(void)
+{
+	ulong base;
+
+	archconfinit();
+
+	base = PGROUND((ulong)end);
+	conf.base0 = base;
+
+	conf.base1 = 0;
+	conf.npage1 = 0;
+
+	conf.npage0 = (conf.topofmem - base)/BY2PG;
+
+	conf.npage = conf.npage0 + conf.npage1;
+	conf.ialloc = (((conf.npage*(main_pool_pcnt))/100)/2)*BY2PG;
+
+
+	conf.nproc = 20;
+//	conf.nproc = 100 + ((conf.npage*BY2PG)/MB)*5;
+	conf.nmach = 1;
+}
+
+void
+machinit(void)
+{
+	memset(m, 0, sizeof(Mach));	/* clear the mach struct */
+}
+
+void
+cachemode(int size, int cenable, int wbenable)
+{
+	ulong sc = SYSCFG;
+	int cm;
+
+	switch (size) {
+	case 0:
+	default:
+		cm = 2;
+		break;
+	case 4096:
+		cm = 0;
+		break;
+	case 8192:
+		cm = 1;
+		break;
+	}
+	sc &= ~((3 << 4) | (1 << 2) | (1 << 1));
+	SYSCFG = sc |  (cm << 4) | (cenable << 1) | (wbenable << 2);
+}
+
+void
+serputc()
+{
+	// dummy routine
+}
+
+void
+main(void)
+{
+	long *p, *ep;
+
+	/* clear the BSS by hand */
+	p = (long*)edata;
+	ep = (long*)end;
+	while(p < ep)
+		*p++ = 0;
+	// memset(edata, 0, end-edata);		/* clear the BSS */
+	cachemode(8192, 1, 1);
+	machinit();
+	archreset();
+	confinit();
+	links();
+	xinit();
+	poolinit();
+	poolsizeinit();
+	trapinit(); 
+//	mmuctlregw(mmuctlregr() | CpCDcache | CpCwb | CpCi32 | CpCd32 | CpCIcache);
+	clockinit(); 
+	printinit();
+//	screeninit();
+	procinit();
+	chandevreset();
+
+	eve = strdup("inferno");
+
+	archconsole();
+//	else
+//		kbdinit();
+
+	print("\nInferno %s\n", VERSION);
+	print("conf %s (%lud) jit %d\n\n", conffile, kerndate, cflag);
+	userinit();
+// print("userinit over\n");
+	schedinit();
+}
+
+void
+init0(void)
+{
+	Osenv *o;
+
+// print("init0\n");
+	up->nerrlab = 0;
+	spllo();
+	if(waserror())
+		panic("init0 %r");
+
+	/*
+	 * These are o.k. because rootinit is null.
+	 * Then early kproc's will have a root and dot.
+	 */
+	o = up->env;
+	o->pgrp->slash = namec("#/", Atodir, 0, 0);
+	cnameclose(o->pgrp->slash->name);
+	o->pgrp->slash->name = newcname("/");
+	o->pgrp->dot = cclone(o->pgrp->slash);
+
+	chandevinit();
+	poperror();
+// iprint("init0: disinit\n");
+// print("CXXXYYYYYYYYZZZZZZZ\n");
+	disinit("/osinit.dis");
+}
+
+void
+userinit()
+{
+	Proc *p;
+	Osenv *o;
+
+	p = newproc();
+	o = p->env;
+
+	o->fgrp = newfgrp(nil);
+
+	o->pgrp = newpgrp();
+	kstrdup(&o->user, eve);
+
+	strcpy(p->text, "interp");
+
+	p->fpstate = FPINIT;
+
+	/*
+	 * Kernel Stack
+	 *
+	 * N.B. The -12 for the stack pointer is important.
+	 *	4 bytes for gotolabel's return PC
+	 */
+	p->sched.pc = (ulong)init0;
+	p->sched.sp = (ulong)p->kstack+KSTACK-8;
+
+	ready(p);
+}
+
+void
+exit(int inpanic)
+{
+	up = 0;
+
+	/* Shutdown running devices */
+	chandevshutdown();
+
+	if(inpanic){
+		print("Hit the reset button\n");
+		for(;;)clockpoll();
+	}
+	archreboot();
+}
+
+static void
+linkproc(void)
+{
+	spllo();
+	if (waserror())
+		print("error() underflow: %r\n");
+	else
+		(*up->kpfun)(up->arg);
+	pexit("end proc", 1);
+}
+
+void
+kprocchild(Proc *p, void (*func)(void*), void *arg)
+{
+	p->sched.pc = (ulong)linkproc;
+	p->sched.sp = (ulong)p->kstack+KSTACK-8;
+
+	p->kpfun = func;
+	p->arg = arg;
+}
+
+/* stubs */
+void
+setfsr(ulong x) {
+USED(x);
+}
+
+ulong
+getfsr(){
+return 0;
+}
+
+void
+setfcr(ulong x) {
+USED(x);
+}
+
+ulong
+getfcr(){
+return 0;
+}
+
+void
+fpinit(void)
+{
+}
+
+void
+FPsave(void*)
+{
+}
+
+void
+FPrestore(void*)
+{
+}
+
+ulong
+va2pa(void *v)
+{
+	return (ulong)v;
+}
+
--- /dev/null
+++ b/os/ks32/mem.h
@@ -1,0 +1,54 @@
+/*
+ * Memory and machine-specific definitions.  Used in C and assembler.
+ */
+
+/*
+ * Sizes
+ */
+#define _K_		1024			/* 2^10 -> Kilo */
+#define _M_		1048576			/* 2^20 -> Mega */
+#define _G_		1073741824		/* 2^30 -> Giga */
+#define _T_		1099511627776UL		/* 2^40 -> Tera */
+#define	BI2BY		8			/* bits per byte */
+#define BI2WD		32			/* bits per word */
+#define	BY2WD		4			/* bytes per word */
+#define	BY2V		8			/* bytes per double word */
+#define	BY2PG		4096			/* bytes per page */
+#define	WD2PG		(BY2PG/BY2WD)		/* words per page */
+#define	PGSHIFT		12			/* log(BY2PG) */
+#define ROUND(s, sz)	(((s)+(sz-1))&~(sz-1))
+#define PGROUND(s)	ROUND(s, BY2PG)
+#define BIT(n)		(1<<n)
+#define BITS(a,b)	((1<<(b+1))-(1<<a))
+
+#define	MAXMACH		1			/* max # cpus system can run */
+
+/*
+ * Time
+ */
+#define	HZ		(100)			/* clock frequency */
+#define	MS2HZ		(1000/HZ)		/* millisec per clock tick */
+#define	TK2SEC(t)	((t)/HZ)		/* ticks to seconds */
+#define	MS2TK(t)	((t)/MS2HZ)		/* milliseconds to ticks */
+
+/*
+ * More accurate time
+ */
+#define TIMER_HZ	50000000
+#define MS2TMR(t)	((ulong)(((uvlong)(t)*TIMER_HZ)/1000))
+#define US2TMR(t)	((ulong)(((uvlong)(t)*TIMER_HZ)/1000000))
+
+/*
+ *  Address spaces
+ *
+*/
+
+#define KZERO		0x0
+#define MACHADDR	((ulong)&Mach0)
+/* #define MACHADDR	(KZERO+0x00002000)  /* should come from BootParam, */
+					/* or be automatically allocated */
+/* #define KTTB		(KZERO+0x00004000)  - comes from BootParam now */
+#define KTZERO		bootparam->entry
+#define KSTACK		8192			/* Size of kernel stack */
+
+#include "armv7.h"
--- /dev/null
+++ b/os/ks32/mkfile
@@ -1,0 +1,115 @@
+<../../mkconfig
+
+#Configurable parameters
+
+CONF=evaluator7t				#default configuration
+CONFLIST=evaluator7t
+
+SYSTARG=$OSTARG
+OBJTYPE=arm
+INSTALLDIR=$ROOT/Inferno/$OBJTYPE/bin	#path of directory where kernel is installed
+#end configurable parameters
+
+<$ROOT/mkfiles/mkfile-$SYSTARG-$OBJTYPE	#set vars based on target system
+
+<| $SHELLNAME ../port/mkdevlist $CONF	#sets $IP, $DEVS, $ETHERS, $VGAS, $PORT, $MISC, $LIBS, $OTHERS
+
+KTZERO=0x8080
+
+OBJ=\
+	l.$O\
+	clock.$O\
+	fpi.$O\
+	fpiarm.$O\
+	fpimem.$O\
+	main.$O\
+	trap.$O\
+	$CONF.root.$O\
+	$IP\
+	$DEVS\
+	$ETHERS\
+	$LINKS\
+	$PORT\
+	$MISC\
+	$OTHERS\
+
+LIBNAMES=${LIBS:%=lib%.a}
+LIBDIRS=$LIBS
+
+HFILES=\
+	mem.h\
+	armv7.h\
+	dat.h\
+	fns.h\
+	io.h\
+	fpi.h\
+
+CFLAGS=-wFV -I$ROOT/Inferno/$OBJTYPE/include -I$ROOT/include -I$ROOT/libinterp -r
+KERNDATE=`{$NDATE}
+
+# default:V: i$CONF.gz i$CONF.p9.gz i$CONF.txt
+default:V: i$CONF.txt
+
+install:V: $INSTALLDIR/i$CONF $INSTALLDIR/i$CONF.gz $INSTALLDIR/i$CONF.p9.gz $INSTALLDIR/i$CONF.raw
+
+i$CONF.txt: i$CONF.aif
+	x=/bin/pub/uuencode
+	test -f $x || x=uuencode
+	$x i$CONF.aif i$CONF.aif >i$CONF.txt
+	mv i$CONF.txt xyz
+
+i$CONF: $OBJ $CONF.c $CONF.root.h $LIBNAMES
+	$CC $CFLAGS '-DKERNDATE='$KERNDATE $CONF.c
+	$LD -o $target -T$KTZERO -R4 -l $OBJ $CONF.$O $LIBFILES
+
+trap.t: trap.5
+	cp trap.5 trap.t
+trap.5: trap.c
+	5c $CFLAGS trap.c
+
+# old "plan9" format executables for inf2.1 styxmon/sboot
+i$CONF.p9: $OBJ $CONF.c $CONF.root.h $LIBNAMES
+	$CC $CFLAGS '-DKERNDATE='$KERNDATE $CONF.c
+	$LD -o $target -T0x8020 -R4 -l $OBJ $CONF.$O $LIBFILES
+
+i$CONF.p9.gz: i$CONF.p9
+	rm -f i$CONF.p9.gz
+	gzip -9 <i$CONF.p9 >i$CONF.p9.gz
+
+# "raw" version of kernel for binary comparison testing
+i$CONF.raw: $OBJ $CONF.c $CONF.root.h $LIBNAMES
+	$CC $CFLAGS '-DKERNDATE='0 $CONF.c
+	$LD -s -o $target -T$KTZERO -R4 -l $OBJ $CONF.$O $LIBFILES
+
+i$CONF.aif: i$CONF
+	5cv -s -H1 -T$KTZERO $prereq $target
+
+i$CONF.gz: i$CONF.aif
+	gzip -9 <$prereq >$target
+
+<../port/portmkfile
+
+../init/$INIT.dis:	../init/$INIT.b
+		cd ../init; mk $INIT.dis
+
+clock.$O:	$ROOT/Inferno/$OBJTYPE/include/ureg.h
+devether.$O:	$ROOT/Inferno/$OBJTYPE/include/ureg.h
+devsapcm.$O:	$ROOT/Inferno/$OBJTYPE/include/ureg.h
+fault386.$O:	$ROOT/Inferno/$OBJTYPE/include/ureg.h
+main.$O:	$ROOT/Inferno/$OBJTYPE/include/ureg.h
+trap.$O:	$ROOT/Inferno/$OBJTYPE/include/ureg.h
+
+devether.$O $ETHERS:	etherif.h ../port/netif.h
+$IP devip.$O:		../ip/ip.h
+
+dummy:V:
+
+# to be moved to port/interp 
+bench.h:D: ../../module/bench.m
+	rm -f $target && limbo -a -I../../module ../../module/bench.m > $target
+benchmod.h:D:  ../../module/bench.m
+	rm -f $target && limbo -t Bench -I../../module ../../module/bench.m > $target
+devbench.$O: bench.h benchmod.h
+
+devuart.$O:	devuart.c
+	$CC $CFLAGS devuart.c
--- /dev/null
+++ b/os/ks32/not.c
@@ -1,0 +1,3 @@
+void muxclose(){}
+void mntauth(){}
+void mntversion(){}
--- /dev/null
+++ b/os/ks32/squirt
@@ -1,0 +1,2 @@
+#!/bin/rc
+cat ievaluator7t.txt
--- /dev/null
+++ b/os/ks32/trap.c
@@ -1,0 +1,525 @@
+#include	"u.h"
+#include	"../port/lib.h"
+#include	"mem.h"
+#include	"dat.h"
+#include	"fns.h"
+#include	"io.h"
+#include	"ureg.h"
+#include	"../port/error.h"
+
+#define waslo(sr) (!((sr) & (PsrDirq|PsrDfiq)))
+
+typedef struct IrqEntry {
+		void	(*r)(Ureg*, void*);
+		void	*a;
+		int	v;
+} IrqEntry;
+
+enum {
+	NumIRQbits = MaxIRQbit+1,
+
+};
+
+static IrqEntry Irq[NumIRQbits];
+
+Instr BREAK = 0xE6BAD010;
+
+int (*breakhandler)(Ureg*, Proc*);
+int (*catchdbg)(Ureg *, uint);
+
+void dumperrstk(void);
+/*
+ * Interrupt sources not masked by splhi() -- these are special
+ *  interrupt handlers (e.g. profiler or watchdog), not allowed
+ *  to share regular kernel data structures.  All interrupts are
+ *  masked by splfhi(), which should only be used herein.
+ */
+
+int splfhi(void);	/* disable all */
+int splflo(void);	/* enable FIQ */
+
+static int actIrq = -1;	/* Active Irq handler, 0-31, or -1 if none */
+static int wasIrq = -1;	/* Interrupted Irq handler */
+
+static Proc *iup;	/* Interrupted kproc */
+
+void
+intrmask(int v, int tbdf)
+{
+	USED(tbdf);
+	if(v < 0 || v > MaxIRQbit)
+		panic("intrmask: irq source %d out of range\n", v);
+	INTREG->msk |= (1 << v);
+}
+
+void
+intrunmask(int v, int tbdf)
+{
+	USED(tbdf);
+	if(v < 0 || v > MaxIRQbit)
+		panic("intrunmask: irq source %d out of range\n", v);
+	INTREG->msk &= ~(1 << v);
+}
+
+void
+intrclear(int v, int tbdf)
+{
+	USED(tbdf);
+	if(v < 0 || v > MaxIRQbit)
+		panic("intrclear: irq source %d out of range\n", v);
+	INTREG->pnd = (1 << v);
+}
+
+void
+intrenable(int v, void (*f)(Ureg*, void*), void* a, int tbdf)
+{
+	int x;
+
+	USED(tbdf);
+	if(v < 0 || v > MaxIRQbit)
+		panic("intrenable: irq source %d out of range\n", v);
+	Irq[v].r = f;
+	Irq[v].a = a;
+
+	x = splfhi();
+	/* Enable the interrupt by clearing the mask bit */
+	INTREG->msk &= ~(1 << v);
+	splx(x);
+}
+
+ulong fiqstack[4];
+ulong irqstack[4];
+ulong abtstack[4];
+ulong undstack[4];
+
+static void
+safeintr(Ureg *, void *a)
+{
+	int v = (int)a;
+	int x;
+
+	/* No handler - clear the mask so we don't loop */
+	x = splfhi();
+	intrmask(v, 0);
+	splx(x);
+	iprint("SPURIOUS INTERRUPT %d\n", v);
+}
+
+static void
+trapv(int off, void (*f)(void))
+{
+	ulong *vloc;
+	int offset;
+
+	vloc = (ulong *)off;
+	offset = (((ulong *) f) - vloc)-2;
+	*vloc = (0xea << 24) | offset;
+}
+
+static void
+maskallints(void)
+{
+	INTREG->msk = 0x3fffff;	/* mask out all interrupts */
+}
+
+void
+trapinit(void)
+{
+	int v;
+	IntReg *intr = INTREG;
+
+	intr->mod = 0;			/* all interrupts to be done in IRQ mode */
+
+	/* set up stacks for various exceptions */
+	setr13(PsrMfiq, fiqstack+nelem(fiqstack));
+	setr13(PsrMirq, irqstack+nelem(irqstack));
+	setr13(PsrMabt, abtstack+nelem(abtstack));
+	setr13(PsrMund, undstack+nelem(undstack));
+
+	for (v = 0; v < nelem(Irq); v++) {
+		Irq[v].r = safeintr;
+		Irq[v].a = (void *)v;
+		Irq[v].v = v;
+	}
+
+	trapv(0x0, _vsvccall);
+	trapv(0x4, _vundcall);
+	trapv(0xc, _vpabcall);
+	trapv(0x10, _vdabcall);
+	trapv(0x18, _virqcall);
+	trapv(0x1c, _vfiqcall);
+	trapv(0x8, _vsvccall);
+	serwrite = uartputs;
+}
+
+static char *_trap_str[PsrMask+1] = {
+	[ PsrMfiq ] "Fiq interrupt",
+	[ PsrMirq ] "Mirq interrupt",
+	[ PsrMsvc ] "SVC/SWI Exception",
+	[ PsrMabt ] "Prefetch Abort/Data Abort",
+	[ PsrMabt+1 ] "Data Abort",
+	[ PsrMund ] "Undefined instruction",
+	[ PsrMsys ] "Sys trap"
+};
+
+static char *
+trap_str(int psr)
+{
+	char *str = _trap_str[psr & PsrMask];
+	if (!str)
+		str = "Undefined trap";
+	return(str);
+}
+
+static void
+sys_trap_error(int type)
+{
+	char errbuf[ERRMAX];
+	sprint(errbuf, "sys: trap: %s\n", trap_str(type));
+	error(errbuf);
+}
+
+void
+dflt(Ureg *ureg, ulong far)
+{
+	char buf[ERRMAX];
+
+	dumpregs(ureg);
+	sprint(buf, "trap: fault pc=%8.8lux addr=0x%lux", (ulong)ureg->pc, far);
+	disfault(ureg, buf);
+}
+
+/*
+ *  All traps come here.  It is slower to have all traps ca)
+ *  rather than directly vectoring the handler.
+ *  However, this avoids
+ *  a lot of code dup and possible bugs.
+ *  trap is called splfhi().
+ */
+
+void
+trap(Ureg* ureg)
+{
+	 //
+	 // This is here to make sure that a clock interrupt doesn't
+	 // cause the process we just returned into to get scheduled
+	 // before it single stepped to the next instruction.
+	 //
+	static struct {int callsched;} c = {1};
+	int itype;
+	/*
+	 * All interrupts/exceptions should be resumed at ureg->pc-4,
+	 * except for Data Abort which resumes at ureg->pc-8.
+	 */
+	itype = ureg->type;
+	if(itype == PsrMabt+1)
+		ureg->pc -= 8;
+	else
+		ureg->pc -= 4;
+	ureg->sp = (ulong)(ureg+1);
+	if (itype == PsrMirq || itype == PsrMfiq) {	/* Interrupt Request */
+
+		Proc *saveup;
+		int t;
+
+		SET(t);
+		SET(saveup);
+
+		if (itype == PsrMirq) {
+			splflo();	/* Allow nonmasked interrupts */
+			if (saveup = up) {
+				t = m->ticks;	/* CPU time per proc */
+				saveup->pc = ureg->pc;	/* debug info */
+				saveup->dbgreg = ureg;
+			}
+		} else {
+					 /* for profiler(wasbusy()): */
+			wasIrq = actIrq; /* Save ID of interrupted handler */
+			iup = up;	 /* Save ID of interrupted proc */
+		}
+
+		while (1) {		/* Use up all the active interrupts */
+			ulong hpip;
+			IrqEntry *curIrq;
+			IntReg *intr = INTREG;
+
+			hpip = itype == PsrMirq ? intr->oset_irq : intr->oset_fiq;
+			if (hpip == 0x54)
+				break;
+			curIrq = Irq + (hpip >> 2);
+			actIrq = curIrq->v; /* show active interrupt handler */
+			up = 0;		/* Make interrupted process invisible */
+			curIrq->r(ureg, curIrq->a);	/* Call handler */
+		}
+		if (itype == PsrMirq) {
+			up = saveup;	/* Make interrupted process visible */
+			actIrq = -1;	/* No more interrupt handler running */
+			preemption(m->ticks - t);
+			saveup->dbgreg = nil;
+		} else {
+			actIrq = wasIrq;
+			up = iup;
+		}
+		return;
+	}
+
+	setled7ascii('E');
+	/* All other traps */
+	if (ureg->psr & PsrDfiq)
+		goto faultpanic;
+	if (up)
+		up->dbgreg = ureg;
+//	setled7ascii('0' + itype);
+	switch(itype) {
+
+	case PsrMund:				/* Undefined instruction */
+		if(*(ulong*)ureg->pc == BREAK && breakhandler) {
+			int s;
+			Proc *p;
+
+			p = up;
+			/* if (!waslo(ureg->psr) || (ureg->pc >= (ulong)splhi && ureg->pc < (ulong)islo))
+				p = 0; */
+			s = breakhandler(ureg, p);
+			if(s == BrkSched) {
+				c.callsched = 1;
+				sched();
+			} else if(s == BrkNoSched) {
+				c.callsched = 0;
+				if(up)
+					up->dbgreg = 0;
+				return;
+			}
+			break;
+		}
+		if (!up)
+			goto faultpanic;
+		spllo();
+		if (waserror()) {
+			if(waslo(ureg->psr) && (up->type == Interp))
+				disfault(ureg, up->env->errstr);
+			setpanic();
+			dumpregs(ureg);
+			panic("%s", up->env->errstr);
+		}
+		if (!fpiarm(ureg)) {
+			dumpregs(ureg);
+			sys_trap_error(ureg->type);
+		}
+		poperror();
+		break;
+
+	case PsrMsvc:				/* Jump through 0 or SWI */
+		if (waslo(ureg->psr) && up && (up->type == Interp)) {
+			spllo();
+			dumpregs(ureg);
+			sys_trap_error(ureg->type);
+		}
+		goto faultpanic;
+
+	case PsrMabt:				/* Prefetch abort */
+		if (catchdbg && catchdbg(ureg, 0))
+			break;
+		ureg->pc -= 4;
+	case PsrMabt+1:	{			/* Data abort */
+		if (waslo(ureg->psr) && up && (up->type == Interp)) {
+			spllo();
+			dflt(ureg, 0);
+		}
+		goto faultpanic;
+	}
+	default:				/* ??? */
+faultpanic:
+		setpanic();
+		dumpregs(ureg);
+		panic("exception %uX %s\n", ureg->type, trap_str(ureg->type));
+		break;
+	}
+
+	splhi();
+	if(up)
+		up->dbgreg = 0;		/* becomes invalid after return from trap */
+}
+
+void
+setpanic(void)
+{
+	extern void screenon(int);
+	extern int consoleprint;
+
+	if (breakhandler != 0)	/* don't mess up debugger */
+		return;
+	maskallints();
+//	spllo();
+	/* screenon(!consoleprint); */
+	consoleprint = 1;
+	serwrite = uartputs;
+}
+
+int
+isvalid_wa(void *v)
+{
+	return((ulong)v >= 0x8000 && (ulong)v < conf.topofmem && !((ulong)v & 3));
+}
+
+int
+isvalid_va(void *v)
+{
+	return((ulong)v >= 0x8000 && (ulong)v < conf.topofmem);
+}
+
+void
+dumplongs(char *msg, ulong *v, int n)
+{
+	int	ii;
+	int	ll;
+
+	ll = print("%s at %ulx: ", msg, v);
+	for (ii = 0; ii < n; ii++)
+	{
+		if (ll >= 60)
+		{
+			print("\n");
+			ll = print("    %ulx: ", v);
+		}
+		if (isvalid_va(v))
+			ll += print(" %ulx", *v++);
+		else
+		{
+			ll += print(" invalid");
+			break;
+		}
+	}
+	print("\n");
+	USED(ll);
+}
+
+void
+dumpregs(Ureg* ureg)
+{
+	Proc *p;
+
+	print("TRAP: %s", trap_str(ureg->type));
+	if ((ureg->psr & PsrMask) != PsrMsvc)
+		print(" in %s", trap_str(ureg->psr));
+/*
+	if ((ureg->type == PsrMabt) || (ureg->type == PsrMabt + 1))
+		print(" FSR %8.8luX FAR %8.8luX\n", mmuregr(CpFSR), mmuregr(CpFAR));
+*/
+	print("\n");
+	print("PSR %8.8uX type %2.2uX PC %8.8uX LINK %8.8uX\n",
+		ureg->psr, ureg->type, ureg->pc, ureg->link);
+	print("R14 %8.8uX R13 %8.8uX R12 %8.8uX R11 %8.8uX R10 %8.8uX\n",
+		ureg->r14, ureg->r13, ureg->r12, ureg->r11, ureg->r10);
+	print("R9  %8.8uX R8  %8.8uX R7  %8.8uX R6  %8.8uX R5  %8.8uX\n",
+		ureg->r9, ureg->r8, ureg->r7, ureg->r6, ureg->r5);
+	print("R4  %8.8uX R3  %8.8uX R2  %8.8uX R1  %8.8uX R0  %8.8uX\n",
+		ureg->r4, ureg->r3, ureg->r2, ureg->r1, ureg->r0);
+	print("Stack is at: %8.8luX\n",ureg);
+	print("CPSR %8.8uX SPSR %8.8uX ", cpsrr(), spsrr());
+	print("PC %8.8lux LINK %8.8lux\n", (ulong)ureg->pc, (ulong)ureg->link);
+
+	p = (actIrq >= 0) ? iup : up;
+	if (p != nil)
+		print("Process stack:  %lux-%lux\n",
+			p->kstack, p->kstack+KSTACK-4);
+	else
+		print("System stack: %lux-%lux\n",
+			(ulong)(m+1), (ulong)m+KSTACK-4);
+	dumplongs("stk", (ulong *)(ureg + 1), 16);
+	print("bl's: ");
+	dumpstk((ulong *)(ureg + 1));
+	if (isvalid_wa((void *)ureg->pc))
+		dumplongs("code", (ulong *)ureg->pc - 5, 12);
+
+	dumperrstk();
+	/* for(;;) ; */
+}
+
+void
+dumpstack(void)
+{
+	ulong l;
+
+	if (breakhandler != 0)
+		dumpstk(&l);
+}
+
+void
+dumpstk(ulong *l)
+{
+	ulong *v, i;
+	ulong inst;
+	ulong *estk;
+	uint len;
+
+	len = KSTACK/sizeof *l;
+	if (up == 0)
+		len -= l - (ulong *)m;
+	else
+		len -= l - (ulong *)up->kstack;
+
+	if (len > KSTACK/sizeof *l)
+		len = KSTACK/sizeof *l;
+	else if (len < 0)
+		len = 50;
+
+	i = 0;
+	for(estk = l + len; l<estk; l++) {
+		if (!isvalid_wa(l)) {
+			i += print("invalid(%lux)", l);
+			break;
+		}
+		v = (ulong *)*l;
+		if (isvalid_wa(v)) {
+			inst = *(v - 1);
+			if (	(
+					((inst & 0x0ff0f000) == 0x0280f000)
+					&&
+					((*(v-2) & 0x0ffff000) == 0x028fe000)
+				)
+				||
+				((inst & 0x0f000000) == 0x0b000000)
+			) {
+				i += print("%8.8lux ", v);
+			}
+		}
+		if (i >= 60) {
+			print("\n");
+			i = print("    ");
+		}
+	}
+	if (i)
+		print("\n");
+}
+
+void
+dumperrstk(void)
+{
+	int ii, ll;
+
+	if (!up)
+		return;
+
+	ll = print("err stk: ");
+	for (ii = 0; ii < NERR; ii++) {
+		if (ii == up->nerrlab)
+			ll += print("* ");
+		if (up->errlab[ii].pc) {
+			ll += print(" %lux/%8.8lux",
+				up->errlab[ii].sp, up->errlab[ii].pc);
+			if (ll >= 60) {
+				print("\n");
+				ll = 0;
+			}
+		}
+	}
+	if (ll)
+		print("\n");
+}
+
+void
+trapspecial(int (*f)(Ureg *, uint))
+{
+	catchdbg = f;
+}
--- /dev/null
+++ b/os/manga/Mk
@@ -1,0 +1,8 @@
+#!/bin/rc
+rfork ne
+ROOT=/usr/inferno
+fn cd
+NPROC=3
+path=(/usr/inferno/Plan9/$cputype/bin $path)
+#bind /usr/inferno/mkconfig.dist /usr/inferno/mkconfig
+exec mk $*
--- /dev/null
+++ b/os/manga/archmanga.c
@@ -1,0 +1,164 @@
+/*
+ * Manga Balance Plus
+ */
+#include	"u.h"
+#include	"../port/lib.h"
+#include	"mem.h"
+#include	"dat.h"
+#include	"fns.h"
+#include	"../port/error.h"
+#include	"io.h"
+
+#include "../port/netif.h"
+#include "etherif.h"
+#include	"../port/flashif.h"
+
+enum {
+	/* GPIO assignment ... */
+
+	Maxmac=	4,	/* number of MAC addresses taken from EEPROM */
+};
+
+static uchar	macaddrs[Maxmac][Eaddrlen] = {
+[0] {0x00, 0x10, 0xa1, 0x00, 0x10, 0x01},
+[1] {0x00, 0x11, 0x6E, 0x00, 0x4A, 0xD4},
+[2] {0x00, 0x10, 0xa1, 0x00, 0x20, 0x01},	/* TO DO */
+};
+
+void
+archreset(void)
+{
+	/* TO DO: set GPIO and other key registers? */
+	GPIOREG->iopm |= (1<<GPIO_status_orange_o)|(1<<GPIO_status_green_o);
+	GPIOREG->iopm &= ~(1<<GPIO_button_i);
+	GPIOREG->iopd &= ~(1<<GPIO_status_orange_o);
+	GPIOREG->iopd &= ~(1<<GPIO_status_green_o);
+	GPIOREG->iopc |= 0x8888;
+	m->cpuhz = 166000000;	/* system clock is 125 = 5*CLOCKFREQ */
+	m->delayloop = m->cpuhz/1000;
+/*
+	uartdebuginit();
+*/
+}
+
+void
+ledset(int n)
+{
+	int s;
+
+	s = splhi();
+	if(n)
+		GPIOREG->iopd |= 1<<GPIO_status_green_o;
+	else
+		GPIOREG->iopd &= ~(1<<GPIO_status_green_o);
+	splx(s);
+}
+
+void
+archconfinit(void)
+{
+	ulong *p;
+
+	p = KADDR(PHYSMEMCR+0x30);
+	conf.topofmem = (((p[0]>>22)<<16)|0xFFFF)+1;
+//	w = PMGRREG->ppcr & 0x1f;
+//	m->cpuhz = CLOCKFREQ*(27*2*2);
+}
+
+void
+archuartpower(int, int)
+{
+}
+
+void
+kbdinit(void)
+{
+}
+
+void
+archreboot(void)
+{
+	dcflushall();
+	GPIOREG->iopd |= 1<<GPIO_status_green_o;
+	GPIOREG->iopd &= ~(1<<GPIO_status_orange_o);
+//	mmuputctl(mmugetctl() & ~CpCaltivec);	/* restore bootstrap's vectors */
+//	RESETREG->rsrr = 1;	/* software reset */
+	for(;;)
+		//spllo();
+		splhi();
+}
+
+void
+archflashwp(Flash*, int)
+{
+}
+
+/*
+ * for devflash.c:/^flashreset
+ * retrieve flash type, virtual base and length and return 0;
+ * return -1 on error (no flash)
+ */
+int
+archflashreset(int bank, Flash *f)
+{
+	ulong *p;
+	int w;
+
+	p = KADDR(PHYSMEMCR+0x10);
+iprint("Flash %8.8lux %8.8lux %8.8lux\n", p[0], p[1], p[4]);
+	w = p[4]&3;
+	if(bank > 0 || w == 0)
+		return -1;
+	if(w == 3)
+		w = 4;
+	f->type = "cfi8";
+	f->addr = (void*)FLASHMEM;
+	f->size = 0;
+	f->width = w;
+	f->interleave = 0;
+	return 0;
+}
+
+/*
+ * set ether parameters: the contents should be derived from EEPROM or NVRAM
+ */
+int
+archether(int ctlno, Ether *ether)
+{
+	ether->nopt = 0;
+	ether->itype = IRQ;
+	switch(ctlno){
+	case 0:
+		sprint(ether->type, "ks8695");
+		ether->mem = PHYSWANDMA;
+		ether->port = 0;
+		ether->irq = IRQwmrps;
+		break;
+	case 1:
+		sprint(ether->type, "ks8695");
+		ether->mem = PHYSLANDMA;
+		ether->port = 1;
+		ether->irq = IRQlmrps;
+		ether->maxmtu = ETHERMAXTU+4;	/* 802.1[pQ] tags */
+		break;
+	case 2:
+		sprint(ether->type, "rtl8139");
+		ether->mem = 0;
+		ether->port = 0;
+		ether->irq = -1;
+		break;
+	default:
+		return -1;
+	}
+	memmove(ether->ea, macaddrs[ctlno], Eaddrlen);
+	return 1;
+}
+
+/*
+ * TO DO: extract some boot data from user area of flash
+ */
+
+void
+eepromscan(void)
+{
+}
--- /dev/null
+++ b/os/manga/clock.c
@@ -1,0 +1,166 @@
+#include "u.h"
+#include "../port/lib.h"
+#include "mem.h"
+#include "dat.h"
+#include "fns.h"
+#include "io.h"
+
+#include "ureg.h"
+
+enum {
+	Mclk=	25000000
+};
+
+typedef struct Clock0link Clock0link;
+typedef struct Clock0link {
+	void		(*clock)(void);
+	Clock0link*	link;
+} Clock0link;
+
+static Clock0link *clock0link;
+static Lock clock0lock;
+static void (*prof_fcn)(Ureg *, int);
+
+Timer*
+addclock0link(void (*clock)(void), int)
+{
+	Clock0link *lp;
+
+	if((lp = malloc(sizeof(Clock0link))) == 0){
+		print("addclock0link: too many links\n");
+		return nil;
+	}
+	ilock(&clock0lock);
+	lp->clock = clock;
+	lp->link = clock0link;
+	clock0link = lp;
+	iunlock(&clock0lock);
+	return nil;
+}
+
+static void
+profintr(Ureg *, void*)
+{
+	/* TO DO: watchdog, profile on Timer 0 */
+}
+
+static void
+clockintr(Ureg*, void*)
+{
+	Clock0link *lp;
+	static int blip, led;
+
+	if(++blip >= HZ){
+		blip = 0;
+		ledset(led ^= 1);
+	}
+	m->ticks++;
+
+	checkalarms();
+
+	if(canlock(&clock0lock)){
+		for(lp = clock0link; lp; lp = lp->link)
+			if(lp->clock)
+				lp->clock();
+		unlock(&clock0lock);
+	}
+
+	/* round robin time slice is done by trap.c and proc.c */
+}
+
+void
+installprof(void (*pf)(Ureg *, int))
+{
+	USED(pf);
+}
+
+void
+clockinit(void)
+{
+	TimerReg *tr;
+	IntrReg *ir;
+	ulong l, u;
+
+	m->ticks = 0;
+	tr = TIMERREG;
+	tr->enable = 0;
+	tr->pulse1 = 1;
+
+	/* first tune the delay loop parameter (using a search because the counter doesn't decrement) */
+	ir = INTRREG;
+	tr->count1 = Mclk/1000 - tr->pulse1;	/* millisecond */
+	u = m->cpuhz/(2*1000);	/* over-large estimate for a millisecond */
+	l = 10000;
+	while(l+1 < u){
+		m->delayloop = l + (u-l)/2;
+		ir->st = 1<<IRQtm1;	/* reset edge */
+		tr->enable = 1<<1;
+		delay(1);
+		tr->enable = 0;
+		if(ir->st & (1<<IRQtm1))
+			u = m->delayloop;
+		else
+			l = m->delayloop;
+	}
+
+	intrenable(IRQ, IRQtm1, clockintr, nil, "timer.1");
+	tr->count1 = Mclk/HZ - tr->pulse1;
+	tr->enable = 1<<1;	/* enable only Timer 1 */
+}
+
+void
+clockpoll(void)
+{
+}
+
+void
+clockcheck(void)
+{
+}
+
+uvlong
+fastticks(uvlong *hz)
+{
+	if(hz)
+		*hz = HZ;
+	return m->ticks;
+}
+
+void
+microdelay(int l)
+{
+	int i;
+
+	l *= m->delayloop;
+	l /= 1000;
+	if(l <= 0)
+		l = 1;
+	for(i = 0; i < l; i++)
+		;
+}
+
+void
+delay(int l)
+{
+	ulong i, j;
+
+	j = m->delayloop;
+	while(l-- > 0)
+		for(i=0; i < j; i++)
+			;
+}
+
+/*
+ * for devkprof.c
+ */
+long
+archkprofmicrosecondspertick(void)
+{
+	return MS2HZ*1000;
+}
+
+void
+archkprofenable(int)
+{
+	/* TO DO */
+}
--- /dev/null
+++ b/os/manga/dat.h
@@ -1,0 +1,128 @@
+typedef struct Conf	Conf;
+typedef struct Dma	Dma;
+typedef struct FPU	FPU;
+typedef struct FPenv	FPenv;
+typedef struct Label	Label;
+typedef struct Lock	Lock;
+typedef struct Mach	Mach;
+typedef struct Ureg	Ureg;
+typedef struct ISAConf	ISAConf;
+typedef struct Pcidev Pcidev;
+
+typedef ulong Instr;
+
+struct Conf
+{
+	ulong	nmach;			/* processors */
+	ulong	nproc;			/* processes */
+	ulong	npage0;			/* total physical pages of memory */
+	ulong	npage1;			/* total physical pages of memory */
+	ulong	topofmem;		/* highest physical address + 1 */
+	ulong	npage;			/* total physical pages of memory */
+	ulong	base0;			/* base of bank 0 */
+	ulong	base1;			/* base of bank 1 */
+	ulong	ialloc;			/* max interrupt time allocation in bytes */
+
+	int		useminicache;		/* use mini cache: screen.c/lcd.c */
+	int		textwrite;			/* writeable text segment, for debug */
+	int		portrait;	/* display orientation */
+};
+
+#define NISAOPT 8
+struct ISAConf {
+	char	type[KNAMELEN];
+	ulong	port;
+	ulong	irq;
+	int	itype;
+	ulong	dma;
+	ulong	mem;
+	ulong	size;
+	ulong	freq;
+
+	int	nopt;
+	char	*opt[NISAOPT];
+};
+
+/*
+ * FPenv.status
+ */
+enum
+{
+	FPINIT,
+	FPACTIVE,
+	FPINACTIVE,
+};
+
+struct	FPenv
+{
+	ulong	status;
+	ulong	control;
+	ushort	fpistate;	/* emulated fp */
+	ulong	regs[8][3];	/* emulated fp */	
+};
+
+/*
+ * This structure must agree with fpsave and fprestore asm routines
+ */
+struct	FPU
+{
+	FPenv	env;
+};
+
+struct Label
+{
+	ulong	sp;
+	ulong	pc;
+};
+
+struct Lock
+{
+	ulong	key;
+	ulong	sr;
+	ulong	pc;
+	int	pri;
+};
+
+#include "../port/portdat.h"
+
+/*
+ *  machine dependent definitions not used by ../port/portdat.h
+ */
+struct Mach
+{
+	/* OFFSETS OF THE FOLLOWING KNOWN BY l.s */
+	ulong	splpc;		/* pc of last caller to splhi */
+
+	/* ordering from here on irrelevant */
+
+	int	machno;		/* physical id of processor */
+	ulong	ticks;			/* of the clock since boot time */
+	Proc	*proc;			/* current process on this processor */
+	Label	sched;			/* scheduler wakeup */
+	Lock	alarmlock;		/* access to alarm list */
+	void	*alarm;			/* alarms bound to this clock */
+	ulong	cpuhz;
+	ulong	delayloop;
+
+	/* stacks for exceptions */
+	ulong	fiqstack[4];
+	ulong	irqstack[4];
+	ulong	abtstack[4];
+	ulong	undstack[4];
+
+	int	stack[1];
+};
+
+#define	MACHP(n)	(n == 0 ? (Mach*)(MACHADDR) : (Mach*)0)
+
+extern Mach *m;
+extern Proc *up;
+
+/*
+ * Layout at virtual address 0.
+ */
+typedef struct Vectorpage {
+	void	(*vectors[8])(void);
+	uint	vtable[8];
+} Vectorpage;
+extern Vectorpage *page0;
--- /dev/null
+++ b/os/manga/devether.c
@@ -1,0 +1,765 @@
+#include "u.h"
+#include "../port/lib.h"
+#include "mem.h"
+#include "dat.h"
+#include "fns.h"
+#include "io.h"
+#include "ureg.h"
+#include "../port/error.h"
+#include "../port/netif.h"
+
+#include "etherif.h"
+
+enum {
+	Type8021Q=	0x8100,	/* value of type field for 802.1[pQ] tags */
+};
+
+static Ether *etherxx[MaxEther];	/* real controllers */
+static Ether*	vlanalloc(Ether*, int);
+static void	vlanoq(Ether*, Block*);
+
+Chan*
+etherattach(char* spec)
+{
+	ulong ctlrno;
+	char *p;
+	Chan *chan;
+	Ether *ether, *vlan;
+	int vlanid;
+
+	ctlrno = 0;
+	vlanid = 0;
+	if(spec && *spec){
+		ctlrno = strtoul(spec, &p, 0);
+		if(ctlrno == 0 && p == spec || ctlrno >= MaxEther || *p && *p != '.')
+			error(Ebadarg);
+		if(*p == '.'){	/* vlan */
+			vlanid = strtoul(p+1, &p, 0);
+			if(vlanid <= 0 || vlanid > 0xFFF || *p)
+				error(Ebadarg);
+		}
+	}
+	if((ether = etherxx[ctlrno]) == 0)
+		error(Enodev);
+	rlock(ether);
+	if(waserror()){
+		runlock(ether);
+		nexterror();
+	}
+	if(vlanid){
+		if(ether->maxmtu < ETHERMAXTU+4)
+			error("interface cannot support 802.1 tags");
+		vlan = vlanalloc(ether, vlanid);
+		chan = devattach('l', spec);
+		chan->dev = ctlrno  + (vlanid<<8);
+		chan->aux = vlan;
+		poperror();
+		runlock(ether);
+		return chan;
+	}
+	chan = devattach('l', spec);
+	chan->dev = ctlrno;
+	chan->aux = ether;
+	if(ether->attach)
+		ether->attach(ether);
+	poperror();
+	runlock(ether);
+	return chan;
+}
+
+static void
+ethershutdown(void)
+{
+	Ether *ether;
+	int i;
+
+	for(i=0; i<MaxEther; i++){
+		ether = etherxx[i];
+		if(ether != nil && ether->detach != nil)
+			ether->detach(ether);
+	}
+}
+
+static Walkqid*
+etherwalk(Chan* chan, Chan *nchan, char **name, int nname)
+{
+	Walkqid *wq;
+	Ether *ether;
+
+	ether = chan->aux;
+	rlock(ether);
+	if(waserror()) {
+		runlock(ether);
+		nexterror();
+	}
+	wq = netifwalk(ether, chan, nchan, name, nname);
+	if(wq && wq->clone != nil && wq->clone != chan)
+		wq->clone->aux = ether;
+	poperror();
+	runlock(ether);
+	return wq;
+}
+
+static int
+etherstat(Chan* chan, uchar* dp, int n)
+{
+	int s;
+	Ether *ether;
+
+	ether = chan->aux;
+	rlock(ether);
+	if(waserror()) {
+		runlock(ether);
+		nexterror();
+	}
+	s = netifstat(ether, chan, dp, n);
+	poperror();
+	runlock(ether);
+	return s;
+}
+
+static Chan*
+etheropen(Chan* chan, int omode)
+{
+	Chan *c;
+	Ether *ether;
+
+	ether = chan->aux;
+	rlock(ether);
+	if(waserror()) {
+		runlock(ether);
+		nexterror();
+	}
+	c = netifopen(ether, chan, omode);
+	poperror();
+	runlock(ether);
+	return c;
+}
+
+static void
+etherclose(Chan* chan)
+{
+	Ether *ether;
+
+	ether = chan->aux;
+	rlock(ether);
+	if(waserror()) {
+		runlock(ether);
+		nexterror();
+	}
+	netifclose(ether, chan);
+	poperror();
+	runlock(ether);
+}
+
+static long
+etherread(Chan* chan, void* buf, long n, vlong off)
+{
+	Ether *ether;
+	ulong offset = off;
+	long r;
+
+	ether = chan->aux;
+	rlock(ether);
+	if(waserror()) {
+		runlock(ether);
+		nexterror();
+	}
+	if((chan->qid.type & QTDIR) == 0 && ether->ifstat){
+		/*
+		 * With some controllers it is necessary to reach
+		 * into the chip to extract statistics.
+		 */
+		if(NETTYPE(chan->qid.path) == Nifstatqid){
+			r = ether->ifstat(ether, buf, n, offset);
+			goto out;
+		}
+		if(NETTYPE(chan->qid.path) == Nstatqid)
+			ether->ifstat(ether, buf, 0, offset);
+	}
+	r = netifread(ether, chan, buf, n, offset);
+out:
+	poperror();
+	runlock(ether);
+	return r;
+}
+
+static Block*
+etherbread(Chan* chan, long n, ulong offset)
+{
+	Block *b;
+	Ether *ether;
+
+	ether = chan->aux;
+	rlock(ether);
+	if(waserror()) {
+		runlock(ether);
+		nexterror();
+	}
+	b = netifbread(ether, chan, n, offset);
+	poperror();
+	runlock(ether);
+	return b;
+}
+
+static int
+etherwstat(Chan* chan, uchar* dp, int n)
+{
+	Ether *ether;
+	int r;
+
+	ether = chan->aux;
+	rlock(ether);
+	if(waserror()) {
+		runlock(ether);
+		nexterror();
+	}
+	r = netifwstat(ether, chan, dp, n);
+	poperror();
+	runlock(ether);
+	return r;
+}
+
+static void
+etherrtrace(Netfile* f, Etherpkt* pkt, int len)
+{
+	int i, n;
+	Block *bp;
+
+	if(qwindow(f->in) <= 0)
+		return;
+	if(len > 58)
+		n = 58;
+	else
+		n = len;
+	bp = iallocb(64);
+	if(bp == nil)
+		return;
+	memmove(bp->wp, pkt->d, n);
+	i = TK2MS(MACHP(0)->ticks);
+	bp->wp[58] = len>>8;
+	bp->wp[59] = len;
+	bp->wp[60] = i>>24;
+	bp->wp[61] = i>>16;
+	bp->wp[62] = i>>8;
+	bp->wp[63] = i;
+	bp->wp += 64;
+	qpass(f->in, bp);
+}
+
+Block*
+etheriq(Ether* ether, Block* bp, int fromwire)
+{
+	Etherpkt *pkt;
+	ushort type;
+	int len, multi, tome, fromme, vlanid, i;
+	Netfile **ep, *f, **fp, *fx;
+	Block *xbp;
+	Ether *vlan;
+
+	ether->inpackets++;
+
+	pkt = (Etherpkt*)bp->rp;
+	len = BLEN(bp);
+	type = (pkt->type[0]<<8)|pkt->type[1];
+	if(type == Type8021Q && ether->nvlan){
+		vlanid = nhgets(bp->rp+2*Eaddrlen+2) & 0xFFF;
+		if(vlanid){
+			for(i = 0; i < nelem(ether->vlans); i++){
+				vlan = ether->vlans[i];
+				if(vlan != nil && vlan->vlanid == vlanid){
+					memmove(bp->rp+4, bp->rp, 2*Eaddrlen);
+					bp->rp += 4;
+					return etheriq(vlan, bp, fromwire);
+				}
+			}
+			/* allow normal type handling to accept or discard it */
+		}
+	}
+
+	fx = 0;
+	ep = &ether->f[Ntypes];
+
+	multi = pkt->d[0] & 1;
+	/* check for valid multcast addresses */
+	if(multi && memcmp(pkt->d, ether->bcast, sizeof(pkt->d)) != 0 && ether->prom == 0){
+		if(!activemulti(ether, pkt->d, sizeof(pkt->d))){
+			if(fromwire){
+				freeb(bp);
+				bp = 0;
+			}
+			return bp;
+		}
+	}
+
+	/* is it for me? */
+	tome = memcmp(pkt->d, ether->ea, sizeof(pkt->d)) == 0;
+	fromme = memcmp(pkt->s, ether->ea, sizeof(pkt->s)) == 0;
+
+	/*
+	 * Multiplex the packet to all the connections which want it.
+	 * If the packet is not to be used subsequently (fromwire != 0),
+	 * attempt to simply pass it into one of the connections, thereby
+	 * saving a copy of the data (usual case hopefully).
+	 */
+	for(fp = ether->f; fp < ep; fp++){
+		if((f = *fp) && (f->type == type || f->type < 0))
+		if(tome || multi || f->prom){
+			/* Don't want to hear bridged packets */
+			if(f->bridge && !fromwire && !fromme)
+				continue;
+			if(!f->headersonly){
+				if(fromwire && fx == 0)
+					fx = f;
+				else if(xbp = iallocb(len)){
+					memmove(xbp->wp, pkt, len);
+					xbp->wp += len;
+					if(qpass(f->in, xbp) < 0)
+						ether->soverflows++;
+				}
+				else
+					ether->soverflows++;
+			}
+			else
+				etherrtrace(f, pkt, len);
+		}
+	}
+
+	if(fx){
+		if(qpass(fx->in, bp) < 0)
+			ether->soverflows++;
+		return 0;
+	}
+	if(fromwire){
+		freeb(bp);
+		return 0;
+	}
+
+	return bp;
+}
+
+static int
+etheroq(Ether* ether, Block* bp)
+{
+	int len, loopback, s;
+	Etherpkt *pkt;
+
+	ether->outpackets++;
+
+	/*
+	 * Check if the packet has to be placed back onto the input queue,
+	 * i.e. if it's a loopback or broadcast packet or the interface is
+	 * in promiscuous mode.
+	 * If it's a loopback packet indicate to etheriq that the data isn't
+	 * needed and return, etheriq will pass-on or free the block.
+	 * To enable bridging to work, only packets that were originated
+	 * by this interface are fed back.
+	 */
+	pkt = (Etherpkt*)bp->rp;
+	len = BLEN(bp);
+	loopback = memcmp(pkt->d, ether->ea, sizeof(pkt->d)) == 0;
+	if(loopback || memcmp(pkt->d, ether->bcast, sizeof(pkt->d)) == 0 || ether->prom){
+		s = splhi();
+		etheriq(ether, bp, 0);
+		splx(s);
+	}
+
+	if(!loopback){
+		if(ether->vlanid){
+			/* add tag */
+			bp = padblock(bp, 2+2);
+			memmove(bp->rp, bp->rp+4, 2*Eaddrlen);
+			hnputs(bp->rp+2*Eaddrlen, Type8021Q);
+			hnputs(bp->rp+2*Eaddrlen+2, ether->vlanid & 0xFFF);	/* prio:3 0:1 vid:12 */
+			ether = ether->ctlr;
+		}
+		qbwrite(ether->oq, bp);
+		if(ether->transmit != nil)
+			ether->transmit(ether);
+	}else
+		freeb(bp);
+
+	return len;
+}
+
+static long
+etherwrite(Chan* chan, void* buf, long n, vlong)
+{
+	Ether *ether;
+	Block *bp;
+	int onoff;
+	Cmdbuf *cb;
+	long l;
+
+	ether = chan->aux;
+	rlock(ether);
+	if(waserror()) {
+		runlock(ether);
+		nexterror();
+	}
+	if(NETTYPE(chan->qid.path) != Ndataqid) {
+		l = netifwrite(ether, chan, buf, n);
+		if(l >= 0)
+			goto out;
+		cb = parsecmd(buf, n);
+		if(strcmp(cb->f[0], "nonblocking") == 0){
+			if(cb->nf <= 1)
+				onoff = 1;
+			else
+				onoff = atoi(cb->f[1]);
+			if(ether->oq != nil)
+				qnoblock(ether->oq, onoff);
+			free(cb);
+			goto out;
+		}
+		free(cb);
+		if(ether->ctl!=nil){
+			l = ether->ctl(ether,buf,n);
+			goto out;
+		}
+		error(Ebadctl);
+	}
+
+	if(n > ether->maxmtu)
+		error(Etoobig);
+	if(n < ether->minmtu)
+		error(Etoosmall);
+	bp = allocb(n);
+	if(waserror()){
+		freeb(bp);
+		nexterror();
+	}
+	memmove(bp->rp, buf, n);
+	memmove(bp->rp+Eaddrlen, ether->ea, Eaddrlen);
+	bp->wp += n;
+	poperror();
+
+	l = etheroq(ether, bp);
+out:
+	poperror();
+	runlock(ether);
+	return l;
+}
+
+static long
+etherbwrite(Chan* chan, Block* bp, ulong)
+{
+	Ether *ether;
+	long n;
+
+	n = BLEN(bp);
+	if(NETTYPE(chan->qid.path) != Ndataqid){
+		if(waserror()) {
+			freeb(bp);
+			nexterror();
+		}
+		n = etherwrite(chan, bp->rp, n, 0);
+		poperror();
+		freeb(bp);
+		return n;
+	}
+	ether = chan->aux;
+	rlock(ether);
+	if(waserror()) {
+		runlock(ether);
+		nexterror();
+	}
+	if(n > ether->maxmtu){
+		freeb(bp);
+		error(Etoobig);
+	}
+	if(n < ether->minmtu){
+		freeb(bp);
+		error(Etoosmall);
+	}
+	n = etheroq(ether, bp);
+	poperror();
+	runlock(ether);
+	return n;
+}
+
+static void
+nop(Ether*)
+{
+}
+
+static long
+vlanctl(Ether *ether, void *buf, long n)
+{
+	uchar ea[Eaddrlen];
+	Ether *master;
+	Cmdbuf *cb;
+	int i;
+
+	cb = parsecmd(buf, n);
+	if(cb->nf >= 2
+	&& strcmp(cb->f[0], "ea")==0
+	&& parseether(ea, cb->f[1]) == 0){
+		free(cb);
+		memmove(ether->ea, ea, Eaddrlen);
+		memmove(ether->addr, ether->ea, Eaddrlen);
+		return 0;
+	}
+	if(cb->nf == 1 && strcmp(cb->f[0], "disable") == 0){
+		master = ether->ctlr;
+		qlock(&master->vlq);
+		for(i = 0; i < nelem(master->vlans); i++)
+			if(master->vlans[i] == ether){
+				ether->vlanid = 0;
+				master->nvlan--;
+				break;
+			}
+		qunlock(&master->vlq);
+		free(cb);
+		return 0;
+	}
+	free(cb);
+	error(Ebadctl);
+	return -1;	/* not reached */
+}
+
+static Ether*
+vlanalloc(Ether *ether, int id)
+{
+	Ether *vlan;
+	int i, fid;
+	char name[KNAMELEN];
+
+	qlock(&ether->vlq);
+	if(waserror()){
+		qunlock(&ether->vlq);
+		nexterror();
+	}
+	fid = -1;
+	for(i = 0; i < nelem(ether->vlans); i++){
+		vlan = ether->vlans[i];
+		if(vlan != nil && vlan->vlanid == id){
+			poperror();
+			qunlock(&ether->vlq);
+			return vlan;
+		}
+		if(fid < 0 && (vlan == nil || vlan->vlanid == 0))
+			fid = i;
+	}
+	if(fid < 0)
+		error(Enoifc);
+	snprint(name, sizeof(name), "ether%d.%d", ether->ctlrno, id);
+	vlan = ether->vlans[fid];
+	if(vlan == nil){
+		vlan = mallocz(sizeof(Ether), 1);
+		if(vlan == nil)
+			error(Enovmem);
+		netifinit(vlan, name, Ntypes, ether->limit);
+		ether->vlans[fid] = vlan;	/* id is still zero, can't be matched */
+		ether->nvlan++;
+	}else
+		memmove(vlan->name, name, KNAMELEN-1);
+	vlan->attach = nop;
+	vlan->transmit = nil;
+	vlan->ctl = vlanctl;
+	vlan->irq = -1;
+//	vlan->promiscuous = ether->promiscuous;
+//	vlan->multicast = ether->multicast;
+	vlan->arg = vlan;
+	vlan->mbps = ether->mbps;
+	vlan->fullduplex = ether->fullduplex;
+	vlan->encry = ether->encry;
+	vlan->minmtu = ether->minmtu;
+	vlan->maxmtu = ether->maxmtu;
+	vlan->ctlrno = ether->ctlrno;
+	vlan->vlanid = id;
+	vlan->alen = Eaddrlen;
+	memmove(vlan->addr, ether->addr, sizeof(vlan->addr));
+	memmove(vlan->bcast, ether->bcast, sizeof(ether->bcast));
+	vlan->oq = nil;
+	vlan->ctlr = ether;
+	vlan->vlanid = id;
+	poperror();
+	qunlock(&ether->vlq);
+	return vlan;
+}
+
+static struct {
+	char*	type;
+	int	(*reset)(Ether*);
+} cards[MaxEther+1];
+
+void
+addethercard(char* t, int (*r)(Ether*))
+{
+	static int ncard;
+
+	if(ncard == MaxEther)
+		panic("too many ether cards");
+	cards[ncard].type = t;
+	cards[ncard].reset = r;
+	ncard++;
+}
+
+int
+parseether(uchar *to, char *from)
+{
+	char nip[4];
+	char *p;
+	int i;
+
+	p = from;
+	for(i = 0; i < Eaddrlen; i++){
+		if(*p == 0)
+			return -1;
+		nip[0] = *p++;
+		if(*p == 0)
+			return -1;
+		nip[1] = *p++;
+		nip[2] = 0;
+		to[i] = strtoul(nip, 0, 16);
+		if(*p == ':')
+			p++;
+	}
+	return 0;
+}
+
+static void
+etherreset(void)
+{
+	Ether *ether;
+	int i, n, ctlrno;
+	char name[KNAMELEN], buf[128];
+
+	for(ether = 0, ctlrno = 0; ctlrno < MaxEther; ctlrno++){
+		if(ether == 0)
+			ether = malloc(sizeof(Ether));
+		memset(ether, 0, sizeof(Ether));
+		ether->ctlrno = ctlrno;
+		ether->mbps = 10;
+		ether->minmtu = ETHERMINTU;
+		ether->maxmtu = ETHERMAXTU;
+		ether->itype = -1;
+
+		if(archether(ctlrno, ether) <= 0)
+			continue;
+
+		for(n = 0; cards[n].type; n++){
+			if(cistrcmp(cards[n].type, ether->type))
+				continue;
+			for(i = 0; i < ether->nopt; i++){
+				if(cistrncmp(ether->opt[i], "ea=", 3) == 0){
+					if(parseether(ether->ea, &ether->opt[i][3]) == -1)
+						memset(ether->ea, 0, Eaddrlen);
+				}else if(cistrcmp(ether->opt[i], "fullduplex") == 0 ||
+					cistrcmp(ether->opt[i], "10BASE-TFD") == 0)
+					ether->fullduplex = 1;
+				else if(cistrcmp(ether->opt[i], "100BASE-TXFD") == 0)
+					ether->mbps = 100;
+			}
+			if(cards[n].reset(ether))
+				break;
+			snprint(name, sizeof(name), "ether%d", ctlrno);
+
+			if(ether->interrupt != nil)
+				intrenable(ether->itype, ether->irq, ether->interrupt, ether, name);
+
+			i = sprint(buf, "#l%d: %s: %dMbps port 0x%luX irq %lud",
+				ctlrno, ether->type, ether->mbps, ether->port, ether->irq);
+			if(ether->mem)
+				i += sprint(buf+i, " addr 0x%luX", PADDR(ether->mem));
+			if(ether->size)
+				i += sprint(buf+i, " size 0x%luX", ether->size);
+			i += sprint(buf+i, ": %2.2uX%2.2uX%2.2uX%2.2uX%2.2uX%2.2uX",
+				ether->ea[0], ether->ea[1], ether->ea[2],
+				ether->ea[3], ether->ea[4], ether->ea[5]);
+			sprint(buf+i, "\n");
+			iprint(buf);
+
+			if(ether->mbps == 100){
+				netifinit(ether, name, Ntypes, 256*1024);
+				if(ether->oq == 0)
+					ether->oq = qopen(256*1024, Qmsg, 0, 0);
+			}
+			else{
+				netifinit(ether, name, Ntypes, 64*1024);
+				if(ether->oq == 0)
+					ether->oq = qopen(64*1024, Qmsg, 0, 0);
+			}
+			if(ether->oq == 0)
+				panic("etherreset %s", name);
+			ether->alen = Eaddrlen;
+			memmove(ether->addr, ether->ea, Eaddrlen);
+			memset(ether->bcast, 0xFF, Eaddrlen);
+
+			etherxx[ctlrno] = ether;
+			ether = 0;
+			break;
+		}
+	}
+	if(ether)
+		free(ether);
+}
+
+static void
+etherpower(int on)
+{
+	int i;
+	Ether *ether;
+
+	for(i = 0; i < MaxEther; i++){
+		if((ether = etherxx[i]) == nil || ether->power == nil)
+			continue;
+		if(on){
+			if(canrlock(ether))
+				continue;
+			if(ether->power != nil)
+				ether->power(ether, on);
+			wunlock(ether);
+		}else{
+			if(ether->readers)
+				continue;
+			wlock(ether);
+			if(ether->power != nil)
+				ether->power(ether, on);
+			/* Keep locked until power goes back on */
+		}
+	}
+}
+
+#define POLY 0xedb88320
+
+/* really slow 32 bit crc for ethers */
+ulong
+ethercrc(uchar *p, int len)
+{
+	int i, j;
+	ulong crc, b;
+
+	crc = 0xffffffff;
+	for(i = 0; i < len; i++){
+		b = *p++;
+		for(j = 0; j < 8; j++){
+			crc = (crc>>1) ^ (((crc^b) & 1) ? POLY : 0);
+			b >>= 1;
+		}
+	}
+	return crc;
+}
+
+Dev etherdevtab = {
+	'l',
+	"ether",
+
+	etherreset,
+	devinit,
+	ethershutdown,
+	etherattach,
+	etherwalk,
+	etherstat,
+	etheropen,
+	devcreate,
+	etherclose,
+	etherread,
+	etherbread,
+	etherwrite,
+	etherbwrite,
+	devremove,
+	etherwstat,
+	etherpower,
+};
--- /dev/null
+++ b/os/manga/devusb.c
@@ -1,0 +1,931 @@
+#include	"u.h"
+#include	"../port/lib.h"
+#include	"mem.h"
+#include	"dat.h"
+#include	"fns.h"
+#include	"io.h"
+#include	"../port/error.h"
+
+#include	"usb.h"
+
+static int debug = 0;
+
+#define Chatty	1
+#define DPRINT if(Chatty)print
+#define XPRINT if(debug)iprint
+
+Usbhost*	usbhost[MaxUsb];
+
+static char *devstates[] = {
+	[Disabled]		"Disabled",
+	[Attached]	"Attached",
+	[Enabled]		"Enabled",
+	[Assigned]	"Assigned",
+	[Configured]	"Configured",
+};
+
+static	char	Ebadusbmsg[] = "invalid parameters to USB ctl message";
+
+enum
+{
+	Qtopdir = 0,
+	Q2nd,
+	Qnew,
+	Qport,
+	Q3rd,
+	Qctl,
+	Qstatus,
+	Qep0,
+	/* other endpoint files */
+};
+
+/*
+ * Qid path is:
+ *	8 bits of file type (qids above)
+ *	8 bits of slot number; default address 0 used for per-controller files
+ *	4 bits of controller number
+ */
+enum {
+	TYPEBITS	= 8,
+	SLOTBITS	= 8,
+	CTLRBITS	= 4,
+
+	SLOTSHIFT	= TYPEBITS,
+	CTLRSHIFT	= SLOTSHIFT+SLOTBITS,
+
+	TYPEMASK	= (1<<TYPEBITS)-1,
+	SLOTMASK	= (1<<SLOTBITS)-1,
+	CTLRMASK	= (1<<CTLRBITS)-1,
+};
+
+#define	TYPE(q)		(((ulong)(q).path)&TYPEMASK)
+#define	SLOT(q)		((((ulong)(q).path)>>SLOTSHIFT)&SLOTMASK)
+#define	CTLR(q)		((((ulong)(q).path)>>CTLRSHIFT)&CTLRMASK)
+#define	PATH(t, s, c)	((t)|((s)<<SLOTSHIFT)|((c)<<CTLRSHIFT))
+
+static Dirtab usbdir2[] = {
+	"new",	{Qnew},			0,	0666,
+	"port",	{Qport},			0,	0666,
+};
+
+static Dirtab usbdir3[]={
+	"ctl",		{Qctl},			0,	0666,
+	"status",	{Qstatus},			0,	0444,
+	"setup",	{Qep0},			0,	0666,
+	/* epNdata names are generated on demand */
+};
+
+enum
+{
+	PMdisable,
+	PMenable,
+	PMreset,
+};
+
+enum
+{
+	CMclass,
+	CMdata,
+	CMdebug,
+	CMep,
+	CMmaxpkt,
+	CMadjust,
+	CMspeed,
+	CMunstall,
+};
+
+static Cmdtab usbportmsg[] =
+{
+	PMdisable,	"disable",	2,
+	PMenable,		"enable",	2,
+	PMreset,		"reset",	2,
+};
+
+static Cmdtab usbctlmsg[] =
+{
+	CMclass,		"class",	0,
+	CMdata,		"data",	3,
+	CMdebug,		"debug",	3,
+	CMep,		"ep",		6,
+	CMmaxpkt,	"maxpkt",	3,
+	CMadjust,		"adjust",	3,
+	CMspeed,		"speed",	2,
+	CMunstall,	"unstall",	2,
+};
+
+static struct
+{
+	char*	type;
+	int	(*reset)(Usbhost*);
+} usbtypes[MaxUsb+1];
+
+void
+addusbtype(char* t, int (*r)(Usbhost*))
+{
+	static int ntype;
+
+	if(ntype == MaxUsb)
+		panic("too many USB host interface types");
+	usbtypes[ntype].type = t;
+	usbtypes[ntype].reset = r;
+	ntype++;
+}
+
+static Udev*
+usbdeviceofslot(Usbhost *uh, int s)
+{
+	if(s < 0 || s > nelem(uh->dev))
+		return nil;
+	return uh->dev[s];
+}
+
+static Udev*
+usbdevice(Chan *c)
+{
+	int bus;
+	Udev *d;
+	Usbhost *uh;
+
+	bus = CTLR(c->qid);
+	if(bus > nelem(usbhost) || (uh = usbhost[bus]) == nil) {
+		error(Egreg);
+		return nil;		/* for compiler */
+	}
+	d = usbdeviceofslot(uh, SLOT(c->qid));
+	if(d == nil || d->id != c->qid.vers || d->state == Disabled)
+		error(Ehungup);
+	return d;
+}
+
+static Endpt *
+devendpt(Udev *d, int id, int add)
+{
+	Usbhost *uh;
+	Endpt *e, **p;
+
+	p = &d->ep[id&0xF];
+	lock(d);
+	e = *p;
+	if(e != nil){
+		incref(e);
+		XPRINT("incref(0x%p) in devendpt, new value %ld\n", e, e->ref);
+		unlock(d);
+		return e;
+	}
+	unlock(d);
+	if(!add)
+		return nil;
+
+	e = mallocz(sizeof(*e), 1);
+	e->ref = 1;
+	e->x = id&0xF;
+	e->id = id;
+	e->sched = -1;
+	e->maxpkt = 8;
+	e->nbuf = 1;
+	e->dev = d;
+	e->active = 0;
+
+	uh = d->uh;
+	uh->epalloc(uh, e);
+
+	lock(d);
+	if(*p != nil){
+		incref(*p);
+		XPRINT("incref(0x%p) in devendpt, new value %ld\n", *p, (*p)->ref);
+		unlock(d);
+		uh->epfree(uh, e);
+		free(e);
+		return *p;
+	}
+	*p = e;
+	unlock(d);
+	e->rq = qopen(8*1024, 0, nil, e);
+	e->wq = qopen(8*1024, 0, nil, e);
+	return e;
+}
+
+static void
+freept(Endpt *e)
+{
+	Usbhost *uh;
+
+	if(e != nil && decref(e) == 0){
+		XPRINT("freept(%d,%d)\n", e->dev->x, e->x);
+		uh = e->dev->uh;
+		uh->epclose(uh, e);
+		e->dev->ep[e->x] = nil;
+		uh->epfree(uh, e);
+		free(e);
+	}
+}
+
+static Udev*
+usbnewdevice(Usbhost *uh)
+{
+	int i;
+	Udev *d;
+	Endpt *e;
+
+	d = nil;
+	qlock(uh);
+	if(waserror()){
+		qunlock(uh);
+		nexterror();
+	}
+	for(i=0; i<nelem(uh->dev); i++)
+		if(uh->dev[i] == nil){
+			uh->idgen++;
+			d = mallocz(sizeof(*d), 1);
+			d->uh = uh;
+			d->ref = 1;
+			d->x = i;
+			d->id = (uh->idgen << 8) | i;
+			d->state = Enabled;
+			XPRINT("calling devendpt in usbnewdevice\n");
+			e = devendpt(d, 0, 1);	/* always provide control endpoint 0 */
+			e->mode = ORDWR;
+			e->iso = 0;
+			e->sched = -1;
+			uh->dev[i] = d;
+			break;
+		}
+	poperror();
+	qunlock(uh);
+	return d;
+}
+
+static void
+freedev(Udev *d, int ept)
+{
+	int i;
+	Endpt *e;
+	Usbhost *uh;
+
+	uh = d->uh;
+	if(decref(d) == 0){
+		XPRINT("freedev 0x%p, 0\n", d);
+		for(i=0; i<nelem(d->ep); i++)
+			freept(d->ep[i]);
+		if(d->x >= 0)
+			uh->dev[d->x] = nil;
+		free(d);
+	} else {
+		if(ept >= 0 && ept < nelem(d->ep)){
+			e = d->ep[ept];
+			XPRINT("freedev, freept 0x%p\n", e);
+			if(e != nil)
+				uh->epclose(uh, e);
+		}
+	}	
+}
+
+static int
+usbgen(Chan *c, char *, Dirtab*, int, int s, Dir *dp)
+{
+	Qid q;
+	Udev *d;
+	Endpt *e;
+	Dirtab *tab;
+	Usbhost *uh;
+	int t, bus, slot, perm;
+
+	/*
+	 * Top level directory contains the controller names.
+	 */
+	if(c->qid.path == Qtopdir){
+		if(s == DEVDOTDOT){
+			mkqid(&q, Qtopdir, 0, QTDIR);
+			devdir(c, q, "#U", 0, eve, 0555, dp);
+			return 1;
+		}
+		if(s >= nelem(usbhost) || usbhost[s] == nil)
+			return -1;
+		mkqid(&q, PATH(Q2nd, 0, s), 0, QTDIR);
+		snprint(up->genbuf, sizeof up->genbuf, "usb%d", s);
+		devdir(c, q, up->genbuf, 0, eve, 0555, dp);
+		return 1;
+	}
+	bus = CTLR(c->qid);
+	if(bus >= nelem(usbhost) || (uh = usbhost[bus]) == nil)
+			return -1;
+
+	/*
+	 * Second level contains "new", "port", and a numbered
+	 * directory for each enumerated device on the bus.
+	 */
+	t = TYPE(c->qid);
+	if(t < Q3rd){
+		if(s == DEVDOTDOT){
+			mkqid(&q, Qtopdir, 0, QTDIR);
+			devdir(c, q, "#U", 0, eve, 0555, dp);
+			return 1;
+		}
+		if(s < nelem(usbdir2)){
+			d = uh->dev[0];
+			if(d == nil)
+				return -1;
+			tab = &usbdir2[s];
+			mkqid(&q, PATH(tab->qid.path, 0, bus), d->id, QTFILE);
+			devdir(c, q, tab->name, tab->length, eve, tab->perm, dp);
+			return 1;
+		}
+		s -= nelem(usbdir2);
+		if(s >= 0 && s < nelem(uh->dev)) {
+			d = uh->dev[s];
+			if(d == nil)
+				return 0;
+			sprint(up->genbuf, "%d", s);
+			mkqid(&q, PATH(Q3rd, s, bus), d->id, QTDIR);
+			devdir(c, q, up->genbuf, 0, eve, 0555, dp);
+			return 1;
+		}
+		return -1;
+	}
+
+	/*
+	 * Third level.
+	 */
+	slot = SLOT(c->qid);
+	if(s == DEVDOTDOT) {
+		mkqid(&q, PATH(Q2nd, 0, bus), c->qid.vers, QTDIR);
+		snprint(up->genbuf, sizeof up->genbuf, "usb%d", bus);
+		devdir(c, q, up->genbuf, 0, eve, 0555, dp);
+		return 1;
+	}
+	if(s < nelem(usbdir3)) {
+		tab = &usbdir3[s];
+		mkqid(&q, PATH(tab->qid.path, slot, bus), c->qid.vers, QTFILE);
+		devdir(c, q, tab->name, tab->length, eve, tab->perm, dp);
+		return 1;
+	}
+	s -= nelem(usbdir3);
+
+	/* active endpoints */
+	d = usbdeviceofslot(uh, slot);
+	if(d == nil || s >= nelem(d->ep))
+		return -1;
+	if(s == 0 || (e = d->ep[s]) == nil)	/* ep0data is called "setup" */
+		return 0;
+	sprint(up->genbuf, "ep%ddata", s);
+	mkqid(&q, PATH(Qep0+s, slot, bus), c->qid.vers, QTFILE);
+	switch(e->mode) {
+	case OREAD:
+		perm = 0444;
+		break;
+	case OWRITE:
+		perm = 0222;
+		break;
+	default:
+		perm = 0666;
+		break;
+	}
+	devdir(c, q, up->genbuf, e->buffered, eve, perm, dp);
+	return 1;
+}
+
+static Usbhost*
+usbprobe(int cardno, int ctlrno)
+{
+	Usbhost *uh;
+	char buf[128], *ebuf, name[64], *p, *type;
+
+	if(cardno < 0)
+		return nil;
+	uh = malloc(sizeof(Usbhost));
+	memset(uh, 0, sizeof(Usbhost));
+	uh->tbdf = BUSUNKNOWN;
+
+	if(cardno >= MaxUsb || usbtypes[cardno].type == nil){
+		free(uh);
+		return nil;
+	}
+	if(usbtypes[cardno].reset(uh) < 0){
+		free(uh);
+		return nil;
+	}
+
+	snprint(name, sizeof(name), "usb%d", ctlrno);
+	intrenable(IRQ, uh->irq, uh->interrupt, uh, name);
+
+	ebuf = buf + sizeof buf;
+	p = seprint(buf, ebuf, "#U/usb%d: %s: port 0x%luX irq %ld", ctlrno, usbtypes[cardno].type, uh->port, uh->irq);
+	if(uh->mem)
+		p = seprint(p, ebuf, " addr 0x%luX", PADDR(uh->mem));
+	if(uh->size)
+		seprint(p, ebuf, " size 0x%luX", uh->size);
+	print("%s\n", buf);
+
+	return uh;
+}
+
+static void
+usbreset(void)
+{
+	int cardno, ctlrno;
+	Usbhost *uh;
+
+	for(ctlrno = 0; ctlrno < MaxUsb; ctlrno++){
+		if((uh = usbprobe(-1, ctlrno)) == nil)
+			continue;
+		usbhost[ctlrno] = uh;
+	}
+
+	if(getconf("*nousbprobe"))
+		return;
+
+	cardno = ctlrno = 0;
+	while(usbtypes[cardno].type != nil && ctlrno < MaxUsb){
+		if(usbhost[ctlrno] != nil){
+			ctlrno++;
+			continue;
+		}
+		if((uh = usbprobe(cardno, ctlrno)) == nil){
+			cardno++;
+			continue;
+		}
+		usbhost[ctlrno] = uh;
+		ctlrno++;
+	}
+}
+
+void
+usbinit(void)
+{
+	Udev *d;
+	int ctlrno;
+	Usbhost *uh;
+
+	for(ctlrno = 0; ctlrno < MaxUsb; ctlrno++){
+		uh = usbhost[ctlrno];
+		if(uh == nil)
+			continue;
+		if(uh->init != 0)
+			uh->init(uh);
+
+		/* reserve device for configuration */
+		d = usbnewdevice(uh);
+		incref(d);
+		d->state = Attached;
+	}
+}
+
+Chan *
+usbattach(char *spec)
+{
+	return devattach('U', spec);
+}
+
+static Walkqid*
+usbwalk(Chan *c, Chan *nc, char **name, int nname)
+{
+	return devwalk(c, nc, name, nname, nil, 0, usbgen);
+}
+
+static int
+usbstat(Chan *c, uchar *db, int n)
+{
+	return devstat(c, db, n, nil, 0, usbgen);
+}
+
+Chan*
+usbopen(Chan *c, int omode)
+{
+	Udev *d;
+	Endpt *e;
+	int f, s, type;
+	Usbhost *uh;
+
+	if(c->qid.type == QTDIR)
+		return devopen(c, omode, nil, 0, usbgen);
+
+	f = 0;
+	type = TYPE(c->qid);
+	if(type == Qnew){
+		d = usbdevice(c);
+		d = usbnewdevice(d->uh);
+		XPRINT("usbopen, new dev 0x%p\n", d);
+		if(d == nil) {
+			XPRINT("usbopen failed (usbnewdevice)\n");
+			error(Enodev);
+		}
+		type = Qctl;
+		mkqid(&c->qid, PATH(type, d->x, CTLR(c->qid)), d->id, QTFILE);
+		f = 1;
+	}
+
+	if(type < Q3rd){
+		XPRINT("usbopen, devopen < Q3rd\n");
+		return devopen(c, omode, nil, 0, usbgen);
+	}
+
+	d = usbdevice(c);
+	uh = d->uh;
+	qlock(uh);
+	if(waserror()){
+		qunlock(uh);
+		nexterror();
+	}
+
+	switch(type){
+	case Qctl:
+		if(0&&d->busy)
+			error(Einuse);
+		d->busy = 1;
+		if(!f)
+			incref(d);
+		XPRINT("usbopen, Qctl 0x%p\n", d);
+		break;
+
+	default:
+		s = type - Qep0;
+		XPRINT("usbopen, default 0x%p, %d\n", d, s);
+		if(s >= 0 && s < nelem(d->ep)){
+			if((e = d->ep[s]) == nil) {
+				XPRINT("usbopen failed (endpoint)\n");
+				error(Enodev);
+			}
+			XPRINT("usbopen: dev 0x%p, ept 0x%p\n", d, e);
+			uh->epopen(uh, e);
+			e->foffset = 0;
+			e->toffset = 0;
+			e->poffset = 0;
+			e->buffered = 0;
+		}
+		incref(d);
+		break;
+	}
+	poperror();
+	qunlock(uh);
+	c->mode = openmode(omode);
+	c->flag |= COPEN;
+	c->offset = 0;
+	return c;
+}
+
+void
+usbclose(Chan *c)
+{
+	Udev *d;
+	int ept, type;
+	Usbhost *uh;
+
+	type = TYPE(c->qid);
+	if(c->qid.type == QTDIR || type < Q3rd)
+		return;
+	d = usbdevice(c);
+	uh = d->uh;
+	qlock(uh);
+	if(waserror()){
+		qunlock(uh);
+		nexterror();
+	}
+	if(type == Qctl)
+		d->busy = 0;
+	XPRINT("usbclose: dev 0x%p\n", d);
+	if(c->flag & COPEN){
+		ept = (type != Qctl) ? type - Qep0 : -1;
+		XPRINT("usbclose: freedev 0x%p\n", d);
+		freedev(d, ept);
+	}
+	poperror();
+	qunlock(uh);
+}
+
+static char *
+epstatus(char *s, char *se, Endpt *e, int i)
+{
+	char *p;
+
+	p = seprint(s, se, "%2d %#6.6lux %10lud bytes %10lud blocks\n", i, e->csp, e->nbytes, e->nblocks);
+	if(e->iso){
+		p = seprint(p, se, "bufsize %6d buffered %6d", e->maxpkt, e->buffered);
+		if(e->toffset)
+			p = seprint(p, se, " offset  %10lud time %19lld\n", e->toffset, e->time);
+		p = seprint(p, se, "\n");
+	}
+	return p;
+}
+
+long
+usbread(Chan *c, void *a, long n, vlong offset)
+{
+	int t, i;
+	Udev *d;
+	Endpt *e;
+	Usbhost *uh;
+	char *s, *se, *p;
+
+	if(c->qid.type == QTDIR)
+		return devdirread(c, a, n, nil, 0, usbgen);
+
+	d = usbdevice(c);
+	uh = d->uh;
+	t = TYPE(c->qid);
+
+	if(t >= Qep0) {
+		t -= Qep0;
+		if(t >= nelem(d->ep))
+			error(Eio);
+		e = d->ep[t];
+		if(e == nil || e->mode == OWRITE)
+			error(Egreg);
+		if(t == 0) {
+			if(e->iso)
+				error(Egreg);
+			e->data01 = 1;
+			n = uh->read(uh, e, a, n, 0LL);
+			if(e->setin){
+				e->setin = 0;
+				e->data01 = 1;
+				uh->write(uh, e, "", 0, 0LL, TokOUT);
+			}
+			return n;
+		}
+		return uh->read(uh, e, a, n, offset);
+	}
+
+	s = smalloc(READSTR);
+	se = s+READSTR;
+	if(waserror()){
+		free(s);
+		nexterror();
+	}
+	switch(t){
+	case Qport:
+		uh->portinfo(uh, s, se);
+		break;
+
+	case Qctl:
+		seprint(s, se, "%11d %11d\n", d->x, d->id);
+		break;
+
+	case Qstatus:
+		if (d->did || d->vid)
+			p = seprint(s, se, "%s %#6.6lux %#4.4ux %#4.4ux\n", devstates[d->state], d->csp, d->vid, d->did);
+		else
+			p = seprint(s, se, "%s %#6.6lux\n", devstates[d->state], d->csp);
+		for(i=0; i<nelem(d->ep); i++) {
+			e = d->ep[i];
+			if(e == nil)
+				continue;
+			/* TO DO: freeze e */
+			p = epstatus(p, se, e, i);
+		}
+	}
+	n = readstr(offset, a, n, s);
+	poperror();
+	free(s);
+	return n;
+}
+
+long
+usbwrite(Chan *c, void *a, long n, vlong offset)
+{
+	Udev *d;
+	Endpt *e;
+	Cmdtab *ct;
+	Cmdbuf *cb;
+	Usbhost *uh;
+	int id, nw, t, i;
+	char cmd[50];
+
+	if(c->qid.type == QTDIR)
+		error(Egreg);
+	d = usbdevice(c);
+	uh = d->uh;
+	t = TYPE(c->qid);
+	switch(t){
+	case Qport:
+		cb = parsecmd(a, n);
+		if(waserror()){
+			free(cb);
+			nexterror();
+		}
+
+		ct = lookupcmd(cb, usbportmsg, nelem(usbportmsg));
+		id = strtol(cb->f[1], nil, 0);
+		switch(ct->index){
+		case PMdisable:
+			uh->portenable(uh, id, 0);
+			break;
+		case PMenable:
+			uh->portenable(uh, id, 1);
+			break;
+		case PMreset:
+			uh->portreset(uh, id);
+			break;
+		}
+	
+		poperror();
+		free(cb);
+		return n;
+	case Qctl:
+		cb = parsecmd(a, n);
+		if(waserror()){
+			free(cb);
+			nexterror();
+		}
+
+		ct = lookupcmd(cb, usbctlmsg, nelem(usbctlmsg));
+		switch(ct->index){
+		case CMspeed:
+			d->ls = strtoul(cb->f[1], nil, 0) == 0;
+			break;
+		case CMclass:
+			if (cb->nf != 4 && cb->nf != 6)
+				cmderror(cb, Ebadusbmsg);
+			/* class #ifc ept csp ( == class subclass proto) [vendor product] */
+			d->npt = strtoul(cb->f[1], nil, 0);	/* # of interfaces */
+			i = strtoul(cb->f[2], nil, 0);		/* endpoint */
+			if (i < 0 || i >= nelem(d->ep)
+			 || d->npt > nelem(d->ep) || i >= d->npt)
+				cmderror(cb, Ebadusbmsg);
+			if (cb->nf == 6) {
+				d->vid = strtoul(cb->f[4], nil, 0);
+				d->did = strtoul(cb->f[5], nil, 0);
+			}
+			if (i == 0)
+				d->csp = strtoul(cb->f[3], nil, 0);
+			if(d->ep[i] == nil){
+				XPRINT("calling devendpt in usbwrite (CMclass)\n");
+				d->ep[i] = devendpt(d, i, 1);
+			}
+			d->ep[i]->csp = strtoul(cb->f[3], nil, 0);
+			break;
+		case CMdata:
+			i = strtoul(cb->f[1], nil, 0);
+			if(i < 0 || i >= nelem(d->ep) || d->ep[i] == nil)
+				error(Ebadusbmsg);
+			e = d->ep[i];
+			e->data01 = strtoul(cb->f[2], nil, 0) != 0;
+			break;
+		case CMmaxpkt:
+			i = strtoul(cb->f[1], nil, 0);
+			if(i < 0 || i >= nelem(d->ep) || d->ep[i] == nil)
+				error(Ebadusbmsg);
+			e = d->ep[i];
+			e->maxpkt = strtoul(cb->f[2], nil, 0);
+			if(e->maxpkt > 1500)
+				e->maxpkt = 1500;
+			break;
+		case CMadjust:
+			i = strtoul(cb->f[1], nil, 0);
+			if(i < 0 || i >= nelem(d->ep) || d->ep[i] == nil)
+				error(Ebadusbmsg);
+			e = d->ep[i];
+			if (e->iso == 0)
+				error(Eperm);
+			i = strtoul(cb->f[2], nil, 0);
+			/* speed may not result in change of maxpkt */
+			if (i < (e->maxpkt-1)/e->samplesz * 1000/e->pollms
+			  || i > e->maxpkt/e->samplesz * 1000/e->pollms){
+				snprint(cmd, sizeof(cmd), "%d < %d < %d?",
+					(e->maxpkt-1)/e->samplesz * 1000/e->pollms,
+					i,
+					e->maxpkt/e->samplesz * 1000/e->pollms);
+				error(cmd);
+			}
+			e->hz = i;
+			break;
+		case CMdebug:
+			i = strtoul(cb->f[1], nil, 0);
+			if(i < -1 || i >= nelem(d->ep) || d->ep[i] == nil)
+				error(Ebadusbmsg);
+			if (i == -1)
+				debug = 0;
+			else {
+				debug = 1;
+				e = d->ep[i];
+				e->debug = strtoul(cb->f[2], nil, 0);
+			}
+			break;
+		case CMunstall:
+			i = strtoul(cb->f[1], nil, 0);
+			if(i < 0 || i >= nelem(d->ep) || d->ep[i] == nil)
+				error(Ebadusbmsg);
+			e = d->ep[i];
+			e->err = nil;
+			break;
+		case CMep:
+			/* ep n `bulk' mode maxpkt nbuf     OR
+			 * ep n period mode samplesize Hz
+			 */
+			i = strtoul(cb->f[1], nil, 0);
+			if(i < 0 || i >= nelem(d->ep)) {
+				XPRINT("field 1: 0 <= %d < %d\n", i, nelem(d->ep));
+				error(Ebadarg);
+			}
+			if((e = d->ep[i]) == nil){
+				XPRINT("calling devendpt in usbwrite (CMep)\n");
+				e = devendpt(d, i, 1);
+			}
+			qlock(uh);
+			if(waserror()){
+				freept(e);
+				qunlock(uh);
+				nexterror();
+			}
+			if(e->active)
+				error(Eperm);
+			if(strcmp(cb->f[2], "bulk") == 0){
+				/* ep n `bulk' mode maxpkt nbuf */
+				e->iso = 0;
+				i = strtoul(cb->f[4], nil, 0);
+				if(i < 8 || i > 1023)
+					i = 8;
+				e->maxpkt = i;
+				i = strtoul(cb->f[5], nil, 0);
+				if(i >= 1 && i <= 32)
+					e->nbuf = i;
+			} else {
+				/* ep n period mode samplesize Hz */
+				i = strtoul(cb->f[2], nil, 0);
+				if(i > 0 && i <= 1000){
+					e->pollms = i;
+				}else {
+					XPRINT("field 4: 0 <= %d <= 1000\n", i);
+					error(Ebadarg);
+				}
+				i = strtoul(cb->f[4], nil, 0);
+				if(i >= 1 && i <= 8){
+					e->samplesz = i;
+				}else {
+					XPRINT("field 4: 0 < %d <= 8\n", i);
+					error(Ebadarg);
+				}
+				i = strtoul(cb->f[5], nil, 0);
+				if(i >= 1 && i*e->samplesz <= 12*1000*1000){
+					/* Hz */
+					e->hz = i;
+					e->remain = 0;
+				}else {
+					XPRINT("field 5: 1 < %d <= 100000 Hz\n", i);
+					error(Ebadarg);
+				}
+				e->maxpkt = (e->hz * e->pollms + 999)/1000 * e->samplesz;
+				e->iso = 1;
+			}
+			e->mode = strcmp(cb->f[3],"r") == 0? OREAD :
+				  	strcmp(cb->f[3],"w") == 0? OWRITE : ORDWR;
+			uh->epmode(uh, e);
+			poperror();
+			qunlock(uh);
+		}
+	
+		poperror();
+		free(cb);
+		return n;
+
+	case Qep0:	/* SETUP endpoint 0 */
+		/* should canqlock etc */
+		e = d->ep[0];
+		if(e == nil || e->iso)
+			error(Egreg);
+		if(n < 8)
+			error(Eio);
+		nw = *(uchar*)a & RD2H;
+		e->data01 = 0;
+		n = uh->write(uh, e, a, n, 0LL, TokSETUP);
+		if(nw == 0) {	/* host to device: use IN[DATA1] to ack */
+			e->data01 = 1;
+			nw = uh->read(uh, e, cmd, 0LL, 8);
+			if(nw != 0)
+				error(Eio);	/* could provide more status */
+		}else
+			e->setin = 1;	/* two-phase */
+		break;
+
+	default:	/* sends DATA[01] */
+		t -= Qep0;
+		if(t < 0 || t >= nelem(d->ep))
+			error(Egreg);
+		e = d->ep[t];
+		if(e == nil || e->mode == OREAD)
+			error(Egreg);
+		n = uh->write(uh, e, a, n, offset, TokOUT);
+		break;
+	}
+	return n;
+}
+
+Dev usbdevtab = {
+	'U',
+	"usb",
+
+	usbreset,
+	usbinit,
+	devshutdown,
+	usbattach,
+	usbwalk,
+	usbstat,
+	usbopen,
+	devcreate,
+	usbclose,
+	usbread,
+	devbread,
+	usbwrite,
+	devbwrite,
+	devremove,
+	devwstat,
+};
--- /dev/null
+++ b/os/manga/eswnotes
@@ -1,0 +1,41 @@
+- switch level
+	- VlanEn vlan enable
+	- disable tx/rx flow control
+	- buffer sharing control
+	- unicast port-VLAN mismatch discard
+	- fair flow control on/off
+	- priority buffer reserved
+	- high prio first, 10:1, 5:1 2:1
+	- tag mask enabled
+	- enable/disable switch?
+	- TPID mode for direct forwarding from port 5
+	- replace null VID with default port VID
+	- 802.1p base priority
+
+- port level
+	- auto negotiation
+	- spanning tree tx/rx/learn on/off
+	- priority classification on/off
+	- diffserve priority classification on/off
+	- 802.1p classification on/off
+		- some of those are possibly mutually exclusive
+	- priority function enabled
+	- default tag: userprio (3 bits), CFI (mbz), 12-bit VID
+	- VLAN related
+		- ingress filter (discard packets from port not in VLAN)
+		- discard non pvid (discard tagged packets not matching port's default VID)
+	- receive rate control
+		- high priority 8-bits, low priority 8-bits
+	- enable rate and/or flow control, high and low priority, tx/rx
+
+- FID management
+
+- static MAC management
+- VLAN table (16 entries)
+	- port membership
+	- FID
+	- 802.1Q 12-bit VID
+
+- stats
+
+- 8100 or 810x
--- /dev/null
+++ b/os/manga/ether8139.c
@@ -1,0 +1,744 @@
+/*
+ * Realtek 8139 (but not the 8129).
+ * Error recovery for the various over/under -flow conditions
+ * may need work.
+ */
+#include "u.h"
+#include "../port/lib.h"
+#include "mem.h"
+#include "dat.h"
+#include "fns.h"
+#include "io.h"
+#include "../port/error.h"
+#include "../port/netif.h"
+
+#include "etherif.h"
+
+enum {					/* registers */
+	Idr0		= 0x0000,	/* MAC address */
+	Mar0		= 0x0008,	/* Multicast address */
+	Tsd0		= 0x0010,	/* Transmit Status Descriptor0 */
+	Tsad0		= 0x0020,	/* Transmit Start Address Descriptor0 */
+	Rbstart		= 0x0030,	/* Receive Buffer Start Address */
+	Erbcr		= 0x0034,	/* Early Receive Byte Count */
+	Ersr		= 0x0036,	/* Early Receive Status */
+	Cr		= 0x0037,	/* Command Register */
+	Capr		= 0x0038,	/* Current Address of Packet Read */
+	Cbr		= 0x003A,	/* Current Buffer Address */
+	Imr		= 0x003C,	/* Interrupt Mask */
+	Isr		= 0x003E,	/* Interrupt Status */
+	Tcr		= 0x0040,	/* Transmit Configuration */
+	Rcr		= 0x0044,	/* Receive Configuration */
+	Tctr		= 0x0048,	/* Timer Count */
+	Mpc		= 0x004C,	/* Missed Packet Counter */
+	Cr9346		= 0x0050,	/* 9346 Command Register */
+	Config0		= 0x0051,	/* Configuration Register 0 */
+	Config1		= 0x0052,	/* Configuration Register 1 */
+	TimerInt	= 0x0054,	/* Timer Interrupt */
+	Msr		= 0x0058,	/* Media Status */
+	Config3		= 0x0059,	/* Configuration Register 3 */
+	Config4		= 0x005A,	/* Configuration Register 4 */
+	Mulint		= 0x005C,	/* Multiple Interrupt Select */
+	RerID		= 0x005E,	/* PCI Revision ID */
+	Tsad		= 0x0060,	/* Transmit Status of all Descriptors */
+
+	Bmcr		= 0x0062,	/* Basic Mode Control */
+	Bmsr		= 0x0064,	/* Basic Mode Status */
+	Anar		= 0x0066,	/* Auto-Negotiation Advertisment */
+	Anlpar		= 0x0068,	/* Auto-Negotiation Link Partner */
+	Aner		= 0x006A,	/* Auto-Negotiation Expansion */
+	Dis		= 0x006C,	/* Disconnect Counter */
+	Fcsc		= 0x006E,	/* False Carrier Sense Counter */
+	Nwaytr		= 0x0070,	/* N-way Test */
+	Rec		= 0x0072,	/* RX_ER Counter */
+	Cscr		= 0x0074,	/* CS Configuration */
+	Phy1parm	= 0x0078,	/* PHY Parameter 1 */
+	Twparm		= 0x007C,	/* Twister Parameter */
+	Phy2parm	= 0x0080,	/* PHY Parameter 2 */
+};
+
+enum {					/* Cr */
+	Bufe		= 0x01,		/* Rx Buffer Empty */
+	Te		= 0x04,		/* Transmitter Enable */
+	Re		= 0x08,		/* Receiver Enable */
+	Rst		= 0x10,		/* Software Reset */
+};
+
+enum {					/* Imr/Isr */
+	Rok		= 0x0001,	/* Receive OK */
+	Rer		= 0x0002,	/* Receive Error */
+	Tok		= 0x0004,	/* Transmit OK */
+	Ter		= 0x0008,	/* Transmit Error */
+	Rxovw		= 0x0010,	/* Receive Buffer Overflow */
+	PunLc		= 0x0020,	/* Packet Underrun or Link Change */
+	Fovw		= 0x0040,	/* Receive FIFO Overflow */
+	Clc		= 0x2000,	/* Cable Length Change */
+	Timerbit	= 0x4000,	/* Timer */
+	Serr		= 0x8000,	/* System Error */
+};
+
+enum {					/* Tcr */
+	Clrabt		= 0x00000001,	/* Clear Abort */
+	TxrrSHIFT	= 4,		/* Transmit Retry Count */
+	TxrrMASK	= 0x000000F0,
+	MtxdmaSHIFT	= 8,		/* Max. DMA Burst Size */
+	MtxdmaMASK	= 0x00000700,
+	Mtxdma2048	= 0x00000700,
+	Acrc		= 0x00010000,	/* Append CRC (not) */
+	LbkSHIFT	= 17,		/* Loopback Test */
+	LbkMASK		= 0x00060000,
+	Rtl8139ArevG	= 0x00800000,	/* RTL8139A Rev. G ID */
+	IfgSHIFT	= 24,		/* Interframe Gap */
+	IfgMASK		= 0x03000000,
+	HwveridSHIFT	= 26,		/* Hardware Version ID */
+	HwveridMASK	= 0x7C000000,
+};
+
+enum {					/* Rcr */
+	Aap		= 0x00000001,	/* Accept All Packets */
+	Apm		= 0x00000002,	/* Accept Physical Match */
+	Am		= 0x00000004,	/* Accept Multicast */
+	Ab		= 0x00000008,	/* Accept Broadcast */
+	Ar		= 0x00000010,	/* Accept Runt */
+	Aer		= 0x00000020,	/* Accept Error */
+	Sel9356		= 0x00000040,	/* 9356 EEPROM used */
+	Wrap		= 0x00000080,	/* Rx Buffer Wrap Control */
+	MrxdmaSHIFT	= 8,		/* Max. DMA Burst Size */
+	MrxdmaMASK	= 0x00000700,
+	Mrxdmaunlimited	= 0x00000700,
+	RblenSHIFT	= 11,		/* Receive Buffer Length */
+	RblenMASK	= 0x00001800,
+	Rblen8K		= 0x00000000,	/* 8KB+16 */
+	Rblen16K	= 0x00000800,	/* 16KB+16 */
+	Rblen32K	= 0x00001000,	/* 32KB+16 */
+	Rblen64K	= 0x00001800,	/* 64KB+16 */
+	RxfthSHIFT	= 13,		/* Receive Buffer Length */
+	RxfthMASK	= 0x0000E000,
+	Rxfth256	= 0x00008000,
+	Rxfthnone	= 0x0000E000,
+	Rer8		= 0x00010000,	/* Accept Error Packets > 8 bytes */
+	MulERINT	= 0x00020000,	/* Multiple Early Interrupt Select */
+	ErxthSHIFT	= 24,		/* Early Rx Threshold */
+	ErxthMASK	= 0x0F000000,
+	Erxthnone	= 0x00000000,
+};
+
+enum {					/* Received Packet Status */
+	Rcok		= 0x0001,	/* Receive Completed OK */
+	Fae		= 0x0002,	/* Frame Alignment Error */
+	Crc		= 0x0004,	/* CRC Error */
+	Long		= 0x0008,	/* Long Packet */
+	Runt		= 0x0010,	/* Runt Packet Received */
+	Ise		= 0x0020,	/* Invalid Symbol Error */
+	Bar		= 0x2000,	/* Broadcast Address Received */
+	Pam		= 0x4000,	/* Physical Address Matched */
+	Mar		= 0x8000,	/* Multicast Address Received */
+};
+
+enum {					/* Media Status Register */
+	Rxpf		= 0x01,		/* Pause Flag */
+	Txpf		= 0x02,		/* Pause Flag */
+	Linkb		= 0x04,		/* Inverse of Link Status */
+	Speed10		= 0x08,		/* 10Mbps */
+	Auxstatus	= 0x10,		/* Aux. Power Present Status */
+	Rxfce		= 0x40,		/* Receive Flow Control Enable */
+	Txfce		= 0x80,		/* Transmit Flow Control Enable */
+};
+
+typedef struct Td Td;
+struct Td {			/* Soft Transmit Descriptor */
+	int	tsd;
+	int	tsad;
+	uchar*	data;
+	Block*	bp;
+};
+
+enum {					/* Tsd0 */
+	SizeSHIFT	= 0,		/* Descriptor Size */
+	SizeMASK	= 0x00001FFF,
+	Own		= 0x00002000,
+	Tun		= 0x00004000,	/* Transmit FIFO Underrun */
+	Tcok		= 0x00008000,	/* Transmit COmpleted OK */
+	EtxthSHIFT	= 16,		/* Early Tx Threshold */
+	EtxthMASK	= 0x001F0000,
+	NccSHIFT	= 24,		/* Number of Collisions Count */
+	NccMASK		= 0x0F000000,
+	Cdh		= 0x10000000,	/* CD Heartbeat */
+	Owc		= 0x20000000,	/* Out of Window Collision */
+	Tabt		= 0x40000000,	/* Transmit Abort */
+	Crs		= 0x80000000,	/* Carrier Sense Lost */
+};
+
+enum {
+	Rblen		= Rblen64K,	/* Receive Buffer Length */
+	Ntd		= 4,		/* Number of Transmit Descriptors */
+	Tdbsz		= ROUNDUP(sizeof(Etherpkt), 4),
+};
+
+typedef struct Ctlr Ctlr;
+typedef struct Ctlr {
+	int	port;
+	Pcidev*	pcidev;
+	Ctlr*	next;
+	int	active;
+	int	id;
+
+	QLock	alock;			/* attach */
+	Lock	ilock;			/* init */
+	void*	alloc;			/* base of per-Ctlr allocated data */
+
+	int	rcr;			/* receive configuration register */
+	uchar*	rbstart;		/* receive buffer */
+	int	rblen;			/* receive buffer length */
+	int	ierrs;			/* receive errors */
+
+	Lock	tlock;			/* transmit */
+	Td	td[Ntd];
+	int	ntd;			/* descriptors active */
+	int	tdh;			/* host index into td */
+	int	tdi;			/* interface index into td */
+	int	etxth;			/* early transmit threshold */
+	int	taligned;		/* packet required no alignment */
+	int	tunaligned;		/* packet required alignment */
+
+	int	dis;			/* disconnect counter */
+	int	fcsc;			/* false carrier sense counter */
+	int	rec;			/* RX_ER counter */
+} Ctlr;
+
+static Ctlr* ctlrhead;
+static Ctlr* ctlrtail;
+
+#define csr8r(c, r)	(inb((c)->port+(r)))
+#define csr16r(c, r)	(ins((c)->port+(r)))
+#define csr32r(c, r)	(inl((c)->port+(r)))
+#define csr8w(c, r, b)	(outb((c)->port+(r), (int)(b)))
+#define csr16w(c, r, w)	(outs((c)->port+(r), (ushort)(w)))
+#define csr32w(c, r, l)	(outl((c)->port+(r), (ulong)(l)))
+
+static void
+rtl8139promiscuous(void* arg, int on)
+{
+	Ether *edev;
+	Ctlr * ctlr;
+
+	edev = arg;
+	ctlr = edev->ctlr;
+	ilock(&ctlr->ilock);
+
+	if(on)
+		ctlr->rcr |= Aap;
+	else
+		ctlr->rcr &= ~Aap;
+	csr32w(ctlr, Rcr, ctlr->rcr);
+	iunlock(&ctlr->ilock);
+}
+
+static long
+rtl8139ifstat(Ether* edev, void* a, long n, ulong offset)
+{
+	int l;
+	char *p;
+	Ctlr *ctlr;
+
+	ctlr = edev->ctlr;
+	p = malloc(READSTR);
+	l = snprint(p, READSTR, "rcr %8.8uX\n", ctlr->rcr);
+	l += snprint(p+l, READSTR-l, "ierrs %d\n", ctlr->ierrs);
+	l += snprint(p+l, READSTR-l, "etxth %d\n", ctlr->etxth);
+	l += snprint(p+l, READSTR-l, "taligned %d\n", ctlr->taligned);
+	l += snprint(p+l, READSTR-l, "tunaligned %d\n", ctlr->tunaligned);
+	ctlr->dis += csr16r(ctlr, Dis);
+	l += snprint(p+l, READSTR-l, "dis %d\n", ctlr->dis);
+	ctlr->fcsc += csr16r(ctlr, Fcsc);
+	l += snprint(p+l, READSTR-l, "fcscnt %d\n", ctlr->fcsc);
+	ctlr->rec += csr16r(ctlr, Rec);
+	l += snprint(p+l, READSTR-l, "rec %d\n", ctlr->rec);
+
+	l += snprint(p+l, READSTR-l, "Tcr %8.8luX\n", csr32r(ctlr, Tcr));
+	l += snprint(p+l, READSTR-l, "Config0 %2.2uX\n", csr8r(ctlr, Config0));
+	l += snprint(p+l, READSTR-l, "Config1 %2.2uX\n", csr8r(ctlr, Config1));
+	l += snprint(p+l, READSTR-l, "Msr %2.2uX\n", csr8r(ctlr, Msr));
+	l += snprint(p+l, READSTR-l, "Config3 %2.2uX\n", csr8r(ctlr, Config3));
+	l += snprint(p+l, READSTR-l, "Config4 %2.2uX\n", csr8r(ctlr, Config4));
+
+	l += snprint(p+l, READSTR-l, "Bmcr %4.4uX\n", csr16r(ctlr, Bmcr));
+	l += snprint(p+l, READSTR-l, "Bmsr %4.4uX\n", csr16r(ctlr, Bmsr));
+	l += snprint(p+l, READSTR-l, "Anar %4.4uX\n", csr16r(ctlr, Anar));
+	l += snprint(p+l, READSTR-l, "Anlpar %4.4uX\n", csr16r(ctlr, Anlpar));
+	l += snprint(p+l, READSTR-l, "Aner %4.4uX\n", csr16r(ctlr, Aner));
+	l += snprint(p+l, READSTR-l, "Nwaytr %4.4uX\n", csr16r(ctlr, Nwaytr));
+	snprint(p+l, READSTR-l, "Cscr %4.4uX\n", csr16r(ctlr, Cscr));
+	n = readstr(offset, a, n, p);
+	free(p);
+
+	return n;
+}
+
+static int
+rtl8139reset(Ctlr* ctlr)
+{
+	int timeo;
+
+	/*
+	 * Soft reset the controller.
+	 */
+	csr8w(ctlr, Cr, Rst);
+	for(timeo = 0; timeo < 1000; timeo++){
+		if(!(csr8r(ctlr, Cr) & Rst))
+			return 0;
+		delay(1);
+	}
+
+	return -1;
+}
+
+static void
+rtl8139halt(Ctlr* ctlr)
+{
+	int i;
+
+	csr8w(ctlr, Cr, 0);
+	csr16w(ctlr, Imr, 0);
+	csr16w(ctlr, Isr, ~0);
+
+	for(i = 0; i < Ntd; i++){
+		if(ctlr->td[i].bp == nil)
+			continue;
+		freeb(ctlr->td[i].bp);
+		ctlr->td[i].bp = nil;
+	}
+}
+
+static void
+rtl8139init(Ether* edev)
+{
+	int i;
+	ulong r;
+	Ctlr *ctlr;
+	uchar *alloc;
+
+	ctlr = edev->ctlr;
+	ilock(&ctlr->ilock);
+
+	rtl8139halt(ctlr);
+
+	/*
+	 * MAC Address.
+	 */
+	r = (edev->ea[3]<<24)|(edev->ea[2]<<16)|(edev->ea[1]<<8)|edev->ea[0];
+	csr32w(ctlr, Idr0, r);
+	r = (edev->ea[5]<<8)|edev->ea[4];
+	csr32w(ctlr, Idr0+4, r);
+
+	/*
+	 * Receiver
+	 */
+	alloc = mmucacheinhib((char*)ROUNDUP((ulong)ctlr->alloc, CACHELINESZ), ctlr->rblen+16 + Ntd*Tdbsz);
+	ctlr->rbstart = alloc;
+	alloc += ctlr->rblen+16;
+	memset(ctlr->rbstart, 0, ctlr->rblen+16);
+	csr32w(ctlr, Rbstart, PCIWADDR(ctlr->rbstart));
+	ctlr->rcr = Rxfth256|Rblen|Mrxdmaunlimited|Ab|Apm;
+
+	/*
+	 * Transmitter.
+	 */
+	for(i = 0; i < Ntd; i++){
+		ctlr->td[i].tsd = Tsd0+i*4;
+		ctlr->td[i].tsad = Tsad0+i*4;
+		ctlr->td[i].data = alloc;
+		alloc += Tdbsz;
+		ctlr->td[i].bp = nil;
+	}
+	ctlr->ntd = ctlr->tdh = ctlr->tdi = 0;
+	ctlr->etxth = 128/32;
+
+	/*
+	 * Interrupts.
+	 */
+	csr32w(ctlr, TimerInt, 0);
+	csr16w(ctlr, Imr, Serr|Timerbit|Fovw|PunLc|Rxovw|Ter|Tok|Rer|Rok);
+	csr32w(ctlr, Mpc, 0);
+
+	/*
+	 * Enable receiver/transmitter.
+	 * Need to enable before writing the Rcr or it won't take.
+	 */
+	csr8w(ctlr, Cr, Te|Re);
+	csr32w(ctlr, Tcr, Mtxdma2048);
+	csr32w(ctlr, Rcr, ctlr->rcr);
+
+	iunlock(&ctlr->ilock);
+}
+
+static void
+rtl8139attach(Ether* edev)
+{
+	Ctlr *ctlr;
+
+	ctlr = edev->ctlr;
+	qlock(&ctlr->alock);
+	if(ctlr->alloc == nil){
+		ctlr->rblen = 1<<((Rblen>>RblenSHIFT)+13);
+		ctlr->alloc = mallocz(ctlr->rblen+16 + Ntd*Tdbsz + CACHELINESZ, 0);
+		rtl8139init(edev);
+	}
+	qunlock(&ctlr->alock);
+}
+
+static void
+rtl8139txstart(Ether* edev)
+{
+	Td *td;
+	int size;
+	Block *bp;
+	Ctlr *ctlr;
+
+	ctlr = edev->ctlr;
+	while(ctlr->ntd < Ntd){
+		bp = qget(edev->oq);
+		if(bp == nil)
+			break;
+		size = BLEN(bp);
+
+		td = &ctlr->td[ctlr->tdh];
+		if(((int)bp->rp) & 0x03){
+			memmove(td->data, bp->rp, size);
+			dcflush(td->data, size);
+			freeb(bp);
+			csr32w(ctlr, td->tsad, PCIWADDR(td->data));
+			ctlr->tunaligned++;
+		}
+		else{
+			td->bp = bp;
+			csr32w(ctlr, td->tsad, PCIWADDR(bp->rp));
+			dcflush(bp->rp, size);
+			ctlr->taligned++;
+		}
+		csr32w(ctlr, td->tsd, (ctlr->etxth<<EtxthSHIFT)|size);
+
+		ctlr->ntd++;
+		ctlr->tdh = NEXT(ctlr->tdh, Ntd);
+	}
+}
+
+static void
+rtl8139transmit(Ether* edev)
+{
+	Ctlr *ctlr;
+
+	ctlr = edev->ctlr;
+	ilock(&ctlr->tlock);
+	rtl8139txstart(edev);
+	iunlock(&ctlr->tlock);
+}
+
+static void
+rtl8139receive(Ether* edev)
+{
+	Block *bp;
+	Ctlr *ctlr;
+	ushort capr;
+	uchar cr, *p;
+	int l, length, status;
+
+	ctlr = edev->ctlr;
+
+	/*
+	 * Capr is where the host is reading from,
+	 * Cbr is where the NIC is currently writing.
+	 */
+	capr = (csr16r(ctlr, Capr)+16) % ctlr->rblen;
+	while(!(csr8r(ctlr, Cr) & Bufe)){
+		p = ctlr->rbstart+capr;
+
+		/*
+		 * Apparently the packet length may be 0xFFF0 if
+		 * the NIC is still copying the packet into memory.
+		 */
+		length = (*(p+3)<<8)|*(p+2);
+		if(length == 0xFFF0)
+			break;
+		status = (*(p+1)<<8)|*p;
+		if(!(status & Rcok)){
+			if(status & (Ise|Fae))
+				edev->frames++;
+			if(status & Crc)
+				edev->crcs++;
+			if(status & (Runt|Long))
+				edev->buffs++;
+
+			/*
+			 * Reset the receiver.
+			 * Also may have to restore the multicast list
+			 * here too if it ever gets used.
+			 */
+			cr = csr8r(ctlr, Cr);
+			csr8w(ctlr, Cr, cr & ~Re);
+			csr32w(ctlr, Rbstart, PCIWADDR(ctlr->rbstart));
+			csr8w(ctlr, Cr, cr);
+			csr32w(ctlr, Rcr, ctlr->rcr);
+
+			continue;
+		}
+
+		/*
+		 * Receive Completed OK.
+		 * Very simplistic; there are ways this could be done
+		 * without copying, but the juice probably isn't worth
+		 * the squeeze.
+		 * The packet length includes a 4 byte CRC on the end.
+		 */
+		capr = (capr+4) % ctlr->rblen;
+		p = ctlr->rbstart+capr;
+		capr = (capr+length) % ctlr->rblen;
+
+		if((bp = iallocb(length)) != nil){
+			if(p+length >= ctlr->rbstart+ctlr->rblen){
+				l = ctlr->rbstart+ctlr->rblen - p;
+				memmove(bp->wp, p, l);
+				bp->wp += l;
+				length -= l;
+				p = ctlr->rbstart;
+			}
+			if(length > 0){
+				memmove(bp->wp, p, length);
+				bp->wp += length;
+			}
+			bp->wp -= 4;
+			etheriq(edev, bp, 1);
+		}
+
+		capr = ROUNDUP(capr, 4);
+		csr16w(ctlr, Capr, capr-16);
+	}
+}
+
+static void
+rtl8139interrupt(Ureg*, void* arg)
+{
+	Td *td;
+	Ctlr *ctlr;
+	Ether *edev;
+	int isr, msr, tsd;
+
+	edev = arg;
+	ctlr = edev->ctlr;
+
+	while((isr = csr16r(ctlr, Isr)) != 0){
+		csr16w(ctlr, Isr, isr);
+		if(isr & (Fovw|PunLc|Rxovw|Rer|Rok)){
+			rtl8139receive(edev);
+			if(!(isr & Rok))
+				ctlr->ierrs++;
+			isr &= ~(Fovw|Rxovw|Rer|Rok);
+		}
+
+		if(isr & (Ter|Tok)){
+			ilock(&ctlr->tlock);
+			while(ctlr->ntd){
+				td = &ctlr->td[ctlr->tdi];
+				tsd = csr32r(ctlr, td->tsd);
+				if(!(tsd & (Tabt|Tun|Tcok)))
+					break;
+
+				if(!(tsd & Tcok)){
+					if(tsd & Tun){
+						if(ctlr->etxth < ETHERMAXTU/32)
+							ctlr->etxth++;
+					}
+					edev->oerrs++;
+				}
+
+				if(td->bp != nil){
+					freeb(td->bp);
+					td->bp = nil;
+				}
+
+				ctlr->ntd--;
+				ctlr->tdi = NEXT(ctlr->tdi, Ntd);
+			}
+			rtl8139txstart(edev);
+			iunlock(&ctlr->tlock);
+			isr &= ~(Ter|Tok);
+		}
+
+		if(isr & PunLc){
+			/*
+			 * Maybe the link changed - do we care very much?
+			 */
+			msr = csr8r(ctlr, Msr);
+			if(!(msr & Linkb)){
+				if(!(msr & Speed10) && edev->mbps != 100){
+					edev->mbps = 100;
+					qsetlimit(edev->oq, 256*1024);
+				}
+				else if((msr & Speed10) && edev->mbps != 10){
+					edev->mbps = 10;
+					qsetlimit(edev->oq, 65*1024);
+				}
+			}
+			isr &= ~(Clc|PunLc);
+		}
+
+		/*
+		 * Only Serr|Timer should be left by now.
+		 * Should anything be done to tidy up? TimerInt isn't
+		 * used so that can be cleared. A PCI bus error is indicated
+		 * by Serr, that's pretty serious; is there anyhing to do
+		 * other than try to reinitialise the chip?
+		 */
+		if(isr != 0){
+			iprint("rtl8139interrupt: imr %4.4uX isr %4.4uX\n",
+				csr16r(ctlr, Imr), isr);
+			if(isr & Timerbit)
+				csr32w(ctlr, TimerInt, 0);
+			if(isr & Serr)
+				rtl8139init(edev);
+		}
+	}
+}
+
+static Ctlr*
+rtl8139match(Ether* edev, int id)
+{
+	int port;
+	Pcidev *p;
+	Ctlr *ctlr;
+
+	/*
+	 * Any adapter matches if no edev->port is supplied,
+	 * otherwise the ports must match.
+	 */
+	for(ctlr = ctlrhead; ctlr != nil; ctlr = ctlr->next){
+		if(ctlr->active)
+			continue;
+		p = ctlr->pcidev;
+		if(((p->did<<16)|p->vid) != id)
+			continue;
+		port = p->mem[0].bar & ~0x01;
+		if(edev->port != 0 && edev->port != port)
+			continue;
+
+		if(ioalloc(port, p->mem[0].size, 0, "rtl8139") < 0){
+			print("rtl8139: port 0x%uX in use\n", port);
+			continue;
+		}
+
+		ctlr->port = port;
+		if(rtl8139reset(ctlr))
+			continue;
+		pcisetbme(p);
+
+		ctlr->active = 1;
+		return ctlr;
+	}
+	return nil;
+}
+
+static struct {
+	char*	name;
+	int	id;
+} rtl8139pci[] = {
+	{ "rtl8139",	(0x8139<<16)|0x10EC, },	/* generic */
+	{ "smc1211",	(0x1211<<16)|0x1113, },	/* SMC EZ-Card */
+	{ "dfe-538tx",	(0x1300<<16)|0x1186, }, /* D-Link DFE-538TX */
+	{ "dfe-560txd",	(0x1340<<16)|0x1186, }, /* D-Link DFE-560TXD */
+	{ nil },
+};
+
+static int
+rtl8139pnp(Ether* edev)
+{
+	int i, id;
+	Pcidev *p;
+	Ctlr *ctlr;
+	uchar ea[Eaddrlen];
+
+	/*
+	 * Make a list of all ethernet controllers
+	 * if not already done.
+	 */
+	if(ctlrhead == nil){
+		p = nil;
+		while(p = pcimatch(p, 0, 0)){
+			if(p->ccrb != 0x02 || p->ccru != 0)
+				continue;
+			ctlr = malloc(sizeof(Ctlr));
+			ctlr->pcidev = p;
+			ctlr->id = (p->did<<16)|p->vid;
+
+			if(ctlrhead != nil)
+				ctlrtail->next = ctlr;
+			else
+				ctlrhead = ctlr;
+			ctlrtail = ctlr;
+		}
+	}
+
+	/*
+	 * Is it an RTL8139 under a different name?
+	 * Normally a search is made through all the found controllers
+	 * for one which matches any of the known vid+did pairs.
+	 * If a vid+did pair is specified a search is made for that
+	 * specific controller only.
+	 */
+	id = 0;
+	for(i = 0; i < edev->nopt; i++){
+		if(cistrncmp(edev->opt[i], "id=", 3) == 0)
+			id = strtol(&edev->opt[i][3], nil, 0);
+	}
+
+	ctlr = nil;
+	if(id != 0)
+		ctlr = rtl8139match(edev, id);
+	else for(i = 0; rtl8139pci[i].name; i++){
+		if((ctlr = rtl8139match(edev, rtl8139pci[i].id)) != nil)
+			break;
+	}
+	if(ctlr == nil)
+		return -1;
+
+	edev->ctlr = ctlr;
+	edev->port = ctlr->port;
+	edev->irq = ctlr->pcidev->intl;
+	edev->tbdf = ctlr->pcidev->tbdf;
+
+	/*
+	 * Check if the adapter's station address is to be overridden.
+	 * If not, read it from the device and set in edev->ea.
+	 */
+	memset(ea, 0, Eaddrlen);
+	if(memcmp(ea, edev->ea, Eaddrlen) == 0){
+		i = csr32r(ctlr, Idr0);
+		edev->ea[0] = i;
+		edev->ea[1] = i>>8;
+		edev->ea[2] = i>>16;
+		edev->ea[3] = i>>24;
+		i = csr32r(ctlr, Idr0+4);
+		edev->ea[4] = i;
+		edev->ea[5] = i>>8;
+	}
+	edev->attach = rtl8139attach;
+	edev->transmit = rtl8139transmit;
+	edev->interrupt = rtl8139interrupt;
+	edev->ifstat = rtl8139ifstat;
+
+	edev->arg = edev;
+	edev->promiscuous = rtl8139promiscuous;
+
+	/*
+	 * This should be much more dynamic but will do for now.
+	 */
+	if((csr8r(ctlr, Msr) & (Speed10|Linkb)) == 0)
+		edev->mbps = 100;
+
+	return 0;
+}
+
+void
+ether8139link(void)
+{
+	addethercard("rtl8139", rtl8139pnp);
+}
--- /dev/null
+++ b/os/manga/etherif.h
@@ -1,0 +1,44 @@
+enum {
+	MaxEther	= 4,
+	MaxFID=	16,
+	Ntypes		= 8,
+};
+
+typedef struct Ether Ether;
+
+struct Ether {
+RWlock;	/* TO DO */
+	ISAConf;			/* hardware info */
+	int	ctlrno;
+	int	tbdf;			/* type+busno+devno+funcno */
+	int	minmtu;
+	int	maxmtu;
+	uchar	ea[Eaddrlen];
+	int	encry;
+
+	void	(*attach)(Ether*);	/* filled in by reset routine */
+	void	(*closed)(Ether*);
+	void	(*detach)(Ether*);
+	void	(*transmit)(Ether*);
+	void	(*interrupt)(Ureg*, void*);
+	long	(*ifstat)(Ether*, void*, long, ulong);
+	long	(*ctl)(Ether*, void*, long); /* custom ctl messages */
+	void	(*power)(Ether*, int);	/* power on/off */
+	void	(*shutdown)(Ether*);	/* shutdown hardware before reboot */
+	void	*ctlr;
+	int	pcmslot;		/* PCMCIA */
+	int	fullduplex;	/* non-zero if full duplex */
+	int	vlanid;	/* non-zero if vlan */
+
+	Queue*	oq;
+
+	QLock	vlq;	/* array change */
+	int	nvlan;
+	Ether*	vlans[MaxFID];
+
+	Netif;
+};
+
+extern Block* etheriq(Ether*, Block*, int);
+extern void addethercard(char*, int(*)(Ether*));
+extern int archether(int, Ether*);
--- /dev/null
+++ b/os/manga/etherks8695.c
@@ -1,0 +1,1169 @@
+/*
+ * KS8695P ethernet
+ *	WAN port, LAN port to 4-port switch
+ */
+
+#include "u.h"
+#include "../port/lib.h"
+#include "mem.h"
+#include "dat.h"
+#include "fns.h"
+#include "io.h"
+#include "../port/error.h"
+#include "../port/netif.h"
+
+#include "etherif.h"
+#include "ureg.h"
+
+#define	DBG	if(0)iprint
+#define	MIIDBG	if(0)iprint
+
+enum {
+	Nrdre		= 64,	/* receive descriptor ring entries */
+	Ntdre		= 32,	/* transmit descriptor ring entries */
+
+	Rbsize		= ROUNDUP(ETHERMAXTU+4, 4),		/* ring buffer size (+4 for CRC), must be multiple of 4 */
+	Bufsize		= ROUNDUP(Rbsize, CACHELINESZ),	/* keep start and end at cache lines */
+};
+
+typedef struct DmaReg DmaReg;
+struct DmaReg {
+	ulong	dtxc;		/* transmit control register */
+	ulong	drxc;	/* receive control register */
+	ulong	dtsc;		/* transmit start command register */
+	ulong	drsc;		/* receive start command register */
+	ulong	tdlb;		/* transmit descriptor list base address */
+	ulong	rdlb;		/* receive descriptor list base address */
+	ulong	mal;		/* mac address low (4 bytes) */
+	ulong	mah;		/* mac address high (2 bytes) */
+	ulong	pad[0x80-0x20];
+
+	/* pad to 0x80 for */
+	ulong	maal[16][2];	/* additional mac addresses */
+};
+
+enum {
+	/* dtxc */
+	TxSoftReset=	1<<31,
+	/* 29:24 is burst size in words; 0, 1, 2, 4, 8, 16, 32; 0=unlimited */
+	TxUDPck=	1<<18,	/* generate UDP, TCP, IP check sum */
+	TxTCPck=		1<<17,
+	TxIPck=		1<<16,
+	TxFCE=		1<<9,	/* transmit flow control enable */
+	TxLB=		1<<8,	/* loop back */
+	TxEP=		1<<2,	/* enable padding */
+	TxCrc=		1<<1,	/* add CRC */
+	TxEnable=	1<<0,	/* enable Tx block */
+
+	/* drxc */
+	/* 29:24 is burst size in words */
+	RxUDPck=	1<<18,	/* check UDP, TCP, IP check sum */
+	RxTCPck=		1<<17,
+	RxIPck=		1<<16,
+	RxFCE=		1<<9,	/* flow control enable */
+	RxRB=		1<<6,	/* receive broadcast */
+	RxRM=		1<<5,	/* receive multicast (including broadcast) */
+	RxRU=		1<<4,	/* receive unicast */
+	RxAE=		1<<3,	/* receive error frames */
+	RxRA=		1<<2,	/* receive all */
+	RxEnable=	1<<0,	/* enable Rx block */
+
+};
+
+typedef struct WanPhy WanPhy;
+struct WanPhy {
+	ulong	did;		/* device ID */
+	ulong	rid;		/* revision ID */
+	ulong	pad0;	/* miscellaneous control in plain 8695 (not P or X) */
+	ulong	wmc;	/* WAN miscellaneous control */
+	ulong	wppm;	/* phy power management */
+	ulong	wpc;		/* phys ctl */
+	ulong	wps;		/* phys status */
+	ulong	pps;		/* phy power save */
+};
+
+enum {
+	/* wmc */
+	WAnc=	1<<30,	/* auto neg complete */
+	WAnr=	1<<29,	/* auto neg restart */
+	WAnaP=	1<<28,	/* advertise pause */
+	WAna100FD=	1<<27,	/* advertise 100BASE-TX FD */
+	WAna100HD=	1<<26,	/* advertise 100BASE-TX */
+	WAna10FD=	1<<25,	/* advertise 10BASE-TX FD */
+	WAna10HD=	1<<24,	/* advertise 10BASE-TX */
+	WLs=	1<<23,	/* link status */
+	WDs=	1<<22,	/* duplex status (resolved) */
+	WSs=	1<<21,	/* speed status (resolved) */
+	WLparP=	1<<20,	/* link partner pause */
+	WLpar100FD=	1<<19,	/* link partner 100BASE-TX FD */
+	WLpar100HD=	1<<18,
+	WLpar10FD=	1<<17,
+	WLpar10HD=	1<<16,
+	WAnDis=	1<<15,	/* auto negotiation disable */
+	WForce100=	1<<14,
+	WForceFD=	1<<13,
+	/* 6:4 LED1 select */
+	/* 2:0 LED0 select */
+
+	/* LED select */
+	LedSpeed=	0,
+	LedLink,
+	LedFD,		/* full duplex */
+	LedColl,		/* collision */
+	LedTxRx,		/* activity */
+	LedFDColl,	/* FD/collision */
+	LedLinkTxRx,	/* link and activity */
+
+	/* ppm */
+	WLpbk=	1<<14,	/* local (MAC) loopback */
+	WRlpblk=	1<<13,	/* remote (PHY) loopback */
+	WPhyIso=	1<<12,	/* isolate PHY from MII and Tx+/Tx- */
+	WPhyLink=	1<<10,	/* force link in PHY */
+	WMdix=	1<<9,	/* =1, MDIX, =0, MDX */
+	WFef=	1<<8,	/* far end fault */
+	WAmdixp=	1<<7,	/* disable IEEE spec for auto-neg MDIX */
+	WTxdis=	1<<6,	/* disable port's transmitter */
+	WDfef=	1<<5,	/* disable far end fault detection */
+	Wpd=	1<<4,	/* power down */
+	WDmdx=	1<<3,	/* disable auto MDI/MDIX */
+	WFmdx=	1<<2,	/* if auto disabled, force MDIX */
+	WMlpbk=	1<<1,	/* local loopback */
+
+	/* pps */
+	Ppsm=	1<<0,	/* enable PHY power save mode */
+};
+
+#define	DMABURST(n)	((n)<<24)
+
+typedef struct {
+	Lock;
+	int	port;
+	int	init;
+	int	active;
+	int	reading;		/* device read process is active */
+	ulong	anap;	/* auto negotiate result */
+	DmaReg*	regs;
+	WanPhy*	wphy;
+
+	Ring;
+
+	ulong	interrupts;			/* statistics */
+	ulong	deferred;
+	ulong	heartbeat;
+	ulong	latecoll;
+	ulong	retrylim;
+	ulong	underrun;
+	ulong	overrun;
+	ulong	carrierlost;
+	ulong	retrycount;
+} Ctlr;
+
+static void	switchinit(uchar*);
+static void switchdump(void);
+
+static void
+attach(Ether *ether)
+{
+	Ctlr *ctlr;
+
+	ctlr = ether->ctlr;
+	ilock(ctlr);
+	if(!ctlr->active){
+		/* TO DO: rx/tx enable */
+		ctlr->regs->dtxc |= TxEnable;
+		ctlr->regs->drxc |= RxEnable;
+		microdelay(10);
+		ctlr->regs->drsc = 1;	/* start read process */
+		microdelay(10);
+		ctlr->reading = (INTRREG->st & (1<<IRQwmrps)) == 0;
+		ctlr->active = 1;
+	}
+	iunlock(ctlr);
+}
+
+static void
+closed(Ether *ether)
+{
+	Ctlr *ctlr;
+
+	ctlr = ether->ctlr;
+	if(ctlr->active){
+		ilock(ctlr);
+iprint("ether closed\n");
+		ctlr->regs->dtxc &= ~TxEnable;
+		ctlr->regs->drxc &= ~RxEnable;
+		/* TO DO: reset ring? */
+		/* TO DO: could wait? */
+		ctlr->active = 0;
+		iunlock(ctlr);
+	}
+}
+
+static void
+promiscuous(void* arg, int on)
+{
+	Ether *ether;
+	Ctlr *ctlr;
+	ulong w;
+
+	ether = (Ether*)arg;
+	ctlr = ether->ctlr;
+
+	ilock(ctlr);
+	/* TO DO: must disable reader */
+	w = ctlr->regs->drxc;
+	if(on != ((w&RxRA)!=0)){
+		/* TO DO: must disable reader */
+		ctlr->regs->drxc = w ^ RxRA;
+		/* TO DO: restart reader */
+	}
+	iunlock(ctlr);
+}
+
+static void
+multicast(void* arg, uchar *addr, int on)
+{
+	Ether *ether;
+	Ctlr *ctlr;
+
+	USED(addr, on);	/* if on, could SetGroupAddress; if !on, it's hard */
+
+	ether = (Ether*)arg;
+	ctlr = ether->ctlr;
+
+	ilock(ctlr);
+	/* TO DO: must disable reader */
+	/* TO DO: use internal multicast tables? (probably needs LRU or some such) */
+	if(ether->nmaddr)
+		ctlr->regs->drxc |= RxRM;
+	else
+		ctlr->regs->drxc &= ~RxRM;
+	iunlock(ctlr);
+}
+
+static void
+txstart(Ether *ether)
+{
+	int len;
+	Ctlr *ctlr;
+	Block *b;
+	BD *dre;
+
+	ctlr = ether->ctlr;
+	while(ctlr->ntq < ctlr->ntdre-1){
+		b = qget(ether->oq);
+		if(b == 0)
+			break;
+
+		dre = &ctlr->tdr[ctlr->tdrh];
+		if(dre->ctrl & BdBusy)
+			panic("ether: txstart");
+
+		/*
+		 * Give ownership of the descriptor to the chip, increment the
+		 * software ring descriptor pointer and tell the chip to poll.
+		 */
+		len = BLEN(b);
+		if(ctlr->txb[ctlr->tdrh] != nil)
+			panic("etherks8695: txstart");
+		ctlr->txb[ctlr->tdrh] = b;
+		dcflush(b->rp, len);
+		dre->addr = PADDR(b->rp);
+		dre->size = TxIC|TxFS|TxLS | len;
+		dre->ctrl = BdBusy;
+		ctlr->regs->dtsc = 1;	/* go for it */
+		ctlr->ntq++;
+		ctlr->tdrh = NEXT(ctlr->tdrh, ctlr->ntdre);
+	}
+}
+
+static void
+transmit(Ether* ether)
+{
+	Ctlr *ctlr;
+
+	ctlr = ether->ctlr;
+	ilock(ctlr);
+	txstart(ether);
+	iunlock(ctlr);
+}
+
+/*
+ * allocate receive buffer space on cache-line boundaries
+ */
+static Block*
+clallocb(void)
+{
+	Block *b;
+
+	b = iallocb(Bufsize+CACHELINESZ-1);
+	if(b == nil)
+		return b;
+	dcflush(b->base, BALLOC(b));
+	b->wp = b->rp = (uchar*)(((ulong)b->base + CACHELINESZ - 1) & ~(CACHELINESZ-1));
+	return b;
+}
+
+
+static void
+rxring(Ureg*, void *arg)
+{
+	Ether *ether;
+	ulong status;
+	Ctlr *ctlr;
+	BD *dre;
+	Block *b, *rb;
+
+	ether = arg;
+	ctlr = ether->ctlr;
+	ctlr->interrupts++;
+
+	/*
+	 * Receiver interrupt: run round the descriptor ring logging
+	 * errors and passing valid receive data up to the higher levels
+	 * until we encounter a descriptor still owned by the chip.
+	 * We rely on the descriptor accesses being uncached.
+	 */
+	dre = &ctlr->rdr[ctlr->rdrx];
+	while(((status = dre->ctrl) & BdBusy) == 0){
+		if(status & RxES || (status & (RxFS|RxLS)) != (RxFS|RxLS)){
+			if(status & (RxRF|RxTL))
+				ether->buffs++;
+			if(status & RxRE)
+				ether->frames++;
+			if(status & RxCE)
+				ether->crcs++;
+			//if(status & RxOverrun)
+			//	ether->overflows++;
+			iprint("eth rx: %lux\n", status);
+		}else{
+			/*
+			 * We have a packet. Read it in.
+			 */
+			b = clallocb();
+			if(b != nil){
+				rb = ctlr->rxb[ctlr->rdrx];
+				rb->wp += (dre->ctrl & RxFL)-4;
+				etheriq(ether, rb, 1);
+				ctlr->rxb[ctlr->rdrx] = b;
+				dre->addr = PADDR(b->wp);
+			}else
+				ether->soverflows++;
+		}
+
+		/*
+		 * Finished with this descriptor,
+		 * give it back to the chip, then on to the next...
+		 */
+		dre->ctrl = BdBusy;
+
+		ctlr->rdrx = NEXT(ctlr->rdrx, ctlr->nrdre);
+		dre = &ctlr->rdr[ctlr->rdrx];
+	}
+}
+
+static void
+txring(Ureg*, void *arg)
+{
+	Ether *ether;
+	Ctlr *ctlr;
+	BD *dre;
+	Block *b;
+
+	ether = arg;
+	ctlr = ether->ctlr;
+	ctlr->interrupts++;
+
+	/*
+	 * Transmitter interrupt: handle anything queued for a free descriptor.
+	 */
+	lock(ctlr);
+	while(ctlr->ntq){
+		dre = &ctlr->tdr[ctlr->tdri];
+		if(dre->ctrl & BdBusy)
+			break;
+		/* statistics are kept inside the device, but only for LAN */
+		/* there seems to be no per-packet error status for transmission */
+		b = ctlr->txb[ctlr->tdri];
+		if(b == nil)
+			panic("etherks8695: bufp");
+		ctlr->txb[ctlr->tdri] = nil;
+		freeb(b);
+		ctlr->ntq--;
+		ctlr->tdri = NEXT(ctlr->tdri, ctlr->ntdre);
+	}
+	txstart(ether);
+	unlock(ctlr);
+}
+
+/*
+ * receive buffer unavailable (overrun)
+ */
+static void
+rbuintr(Ureg*, void *arg)
+{
+	Ether *ether;
+	Ctlr *ctlr;
+
+	ether = arg;
+	ctlr = ether->ctlr;
+
+	ctlr->interrupts++;
+	if(ctlr->active)
+		ctlr->overrun++;
+	ctlr->reading = 0;
+}
+
+/*
+ * read process (in device) stopped
+ */
+static void
+rxstopintr(Ureg*, void *arg)
+{
+	Ether *ether;
+	Ctlr *ctlr;
+
+	ether = arg;
+	ctlr = ether->ctlr;
+
+	ctlr->interrupts++;
+	if(!ctlr->active)
+		return;
+
+iprint("rxstopintr\n");
+	ctlr->regs->drsc = 1;
+	/* just restart it?  need to fiddle with ring? */
+}
+
+static void
+txstopintr(Ureg*, void *arg)
+{
+	Ether *ether;
+	Ctlr *ctlr;
+
+	ether = arg;
+	ctlr = ether->ctlr;
+
+	ctlr->interrupts++;
+	if(!ctlr->active)
+		return;
+
+iprint("txstopintr\n");
+	ctlr->regs->dtsc = 1;
+	/* just restart it?  need to fiddle with ring? */
+}
+
+
+static void
+linkchangeintr(Ureg*, void*)
+{
+	iprint("link change\n");
+}
+
+static long
+ifstat(Ether* ether, void* a, long n, ulong offset)
+{
+	char *p;
+	int len;
+	Ctlr *ctlr;
+
+	if(n == 0)
+		return 0;
+
+	ctlr = ether->ctlr;
+
+	p = malloc(READSTR);
+	len = snprint(p, READSTR, "interrupts: %lud\n", ctlr->interrupts);
+	len += snprint(p+len, READSTR-len, "carrierlost: %lud\n", ctlr->carrierlost);
+	len += snprint(p+len, READSTR-len, "heartbeat: %lud\n", ctlr->heartbeat);
+	len += snprint(p+len, READSTR-len, "retrylimit: %lud\n", ctlr->retrylim);
+	len += snprint(p+len, READSTR-len, "retrycount: %lud\n", ctlr->retrycount);
+	len += snprint(p+len, READSTR-len, "latecollisions: %lud\n", ctlr->latecoll);
+	len += snprint(p+len, READSTR-len, "rxoverruns: %lud\n", ctlr->overrun);
+	len += snprint(p+len, READSTR-len, "txunderruns: %lud\n", ctlr->underrun);
+{DmaReg *d = ctlr->regs; len += snprint(p+len, READSTR-len, "dtxc=%8.8lux drxc=%8.8lux\n", d->dtxc, d->drxc);}
+	snprint(p+len, READSTR-len, "framesdeferred: %lud\n", ctlr->deferred);
+	n = readstr(offset, a, n, p);
+	free(p);
+
+	if(ctlr->port == 1)
+		switchdump();
+	return n;
+}
+
+static void
+physinit(Ether *ether, int force)
+{
+	Ctlr *ctlr;
+	WanPhy *p;
+	ulong anap;
+	int i;
+
+	ctlr = ether->ctlr;
+	p = ctlr->wphy;
+	if(p == nil){
+		if(ctlr->port){
+			ether->mbps = 100;
+			ether->fullduplex = 1;
+			switchinit(nil);
+		}
+		return;
+	}
+	iprint("phy%d: wmc=%8.8lux wpm=%8.8lux wpc=%8.8lux wps=%8.8lux pps=%8.8lux\n", ctlr->port, p->wmc, p->wppm, p->wpc, p->wps, p->pps);
+
+	p->wppm = 0;	/* enable power, other defaults seem fine */
+	if(p->rid & 7)
+		p->wpc = 0x0200b000;	/* magic */
+	else
+		p->wpc = 0xb000;
+	if(p->wppm & WFef)
+		iprint("ether%d: far end fault\n", ctlr->port);
+
+	if((p->wmc & WLs) == 0){
+		iprint("ether%d: no link\n", ctlr->port);
+		ether->mbps = 100;	/* could use 10, but this is 2005 */
+		ether->fullduplex = 0;
+		return;
+	}
+
+	if((p->wmc & WAnc) == 0 || force){
+		p->wmc = WAnr | WAnaP | WAna100FD | WAna100HD | WAna10FD | WAna10HD | (p->wmc & 0x7F);
+		microdelay(10);
+		if(p->wmc & WLs){
+			for(i=0;; i++){
+				if(i > 600){
+					iprint("ether%d: auto negotiation failed\n", ctlr->port);
+					ether->mbps = 10;	/* we'll assume it's stupid */
+					ether->fullduplex = 0;
+					return;
+				}
+				if(p->wmc & WAnc){
+					microdelay(10);
+					break;
+				}
+				delay(1);
+			}
+		}
+	}
+	anap = p->wmc;
+	ether->mbps = anap & WSs? 100: 10;
+	if(anap & (WLpar100FD|WLpar10FD) && anap & WDs)
+		ether->fullduplex = 1;
+	else
+		ether->fullduplex = 0;
+	ctlr->anap = anap;
+
+	iprint("ks8695%d mii: fd=%d speed=%d wmc=%8.8lux\n", ctlr->port, ether->fullduplex, ether->mbps, anap);
+}
+
+static void
+ctlrinit(Ctlr *ctlr, Ether *ether)
+{
+	int i;
+	DmaReg *em;
+	ulong mode;
+
+	em = ctlr->regs;
+
+	/* soft reset */
+	em->dtxc = TxSoftReset;
+	microdelay(10);
+	for(i=0; em->dtxc & TxSoftReset; i++){
+		if(i > 20){
+			iprint("etherks8695.%d: soft reset failed\n", ctlr->port);
+			i=0;
+		}
+		microdelay(100);
+	}
+iprint("%d: rx=%8.8lux tx=%8.8lux\n", ctlr->port, PADDR(ctlr->rdr), PADDR(ctlr->tdr));
+
+	physinit(ether, 0);
+
+	/* set ether address */
+	em->mah = (ether->ea[0]<<8) | ether->ea[1];
+	em->mal = (ether->ea[2]<<24) | (ether->ea[3]<<16) | (ether->ea[4]<<8) | ether->ea[5];
+	if(ctlr->port == 0){
+		/* clear other addresses for now */
+		for(i=0; i<nelem(em->maal); i++){
+			em->maal[i][0] = 0;
+			em->maal[i][1] = 0;
+		}
+	}
+
+	/* transmitter, enabled later by attach  */
+	em->tdlb = PADDR(ctlr->tdr);
+	em->dtxc = DMABURST(8) | TxFCE | TxCrc;	/* don't set TxEP: there is a h/w bug and it's anyway done by higher levels */
+
+	/* receiver, enabled later by attach */
+	em->rdlb = PADDR(ctlr->rdr);
+	mode = DMABURST(8) | RxRB | RxRU | RxAE;	/* RxAE just there for testing */
+	if(ether->fullduplex)
+		mode |= RxFCE;
+	em->drxc = mode;
+
+	/* tx/rx enable is deferred until attach */
+}
+
+static int
+reset(Ether* ether)
+{
+	uchar ea[Eaddrlen];
+	char name[KNAMELEN];
+	Ctlr *ctlr;
+	int i, irqdelta;
+
+	snprint(name, sizeof(name), "ether%d", ether->ctlrno);
+
+	/*
+	 * Insist that the platform-specific code provide the Ethernet address
+	 */
+	memset(ea, 0, Eaddrlen);
+	if(memcmp(ea, ether->ea, Eaddrlen) == 0){
+		print("%s (%s %ld): no ether address", name, ether->type, ether->port);
+		return -1;
+	}
+
+	ctlr = malloc(sizeof(*ctlr));
+	ctlr->port = ether->port;
+
+	switch(ether->port){
+	case 0:
+		ctlr->regs = KADDR(PHYSWANDMA);
+		ctlr->wphy = KADDR(PHYSMISC);
+		ctlr->wphy->wmc = (ctlr->wphy->wmc & ~0x7F) | (LedLinkTxRx<<0) | (LedSpeed<<4);
+		break;
+	case 1:
+		ctlr->regs = KADDR(PHYSLANDMA);
+		ctlr->wphy = nil;
+		break;
+	default:
+		print("%s: %s ether: no port %lud\n", name, ether->type, ether->port);
+		free(ctlr);
+		return -1;
+	}
+
+	ether->ctlr = ctlr;
+	irqdelta = ether->irq - IRQwmrps;
+
+	physinit(ether, 0);
+
+	if(ioringinit(ctlr, Nrdre, Ntdre) < 0)
+		panic("etherks8695 initring");
+
+	for(i = 0; i < ctlr->nrdre; i++){
+		if(ctlr->rxb[i] == nil)
+			ctlr->rxb[i] = clallocb();
+		ctlr->rdr[i].addr = PADDR(ctlr->rxb[i]->wp);
+		ctlr->rdr[i].size = Rbsize;
+		ctlr->rdr[i].ctrl = BdBusy;
+	}
+
+	ctlrinit(ctlr, ether);
+
+	ether->attach = attach;
+	ether->closed = closed;
+	ether->transmit = transmit;
+	ether->ifstat = ifstat;
+
+	/* there is more than one interrupt: we must enable some ourselves */
+	ether->irq = irqdelta + IRQwmrs;	/* set main IRQ to receive status */
+	ether->interrupt = rxring;
+	intrenable(IRQ, irqdelta+IRQwmts, txring, ether, "ethertx");
+//	intrenable(IRQ, irqdelta+IRQwmtbu, tbuintr, ether, "ethertbu");	/* don't care? */
+	intrenable(IRQ, irqdelta+IRQwmrbu, rbuintr, ether, "etherrbu");
+	intrenable(IRQ, irqdelta+IRQwmrps, rxstopintr, ether, "etherrps");
+	intrenable(IRQ, irqdelta+IRQwmtps, txstopintr, ether, "ethertps");
+	if(ether->port == 0)
+		intrenable(IRQ, IRQwmlc, linkchangeintr, ether, "etherwanlink");
+
+	ether->arg = ether;
+	ether->promiscuous = promiscuous;
+	ether->multicast = multicast;
+
+	return 0;
+}
+
+/*
+ * switch engine registers
+ *	a 10 microsecond delay is required after each (write?) access
+ */
+typedef struct Switch Switch;
+struct Switch {
+	ulong	sec0;	/* control register 0 */
+	ulong	sec1;	/* control register 1 */
+	ulong	sec2;	/* control register 2, factory default, do not change */
+	ulong	cfg[5][3];		/* port configuration registers */
+	ulong	an[2];	/* ports 1 to 4 auto negotiation [1,2][3,4] */
+	ulong	seiac;	/* indirect access control register */
+	ulong	seiadh2;	/* indirect access data register 2 (4:0 is 68-64 of data) */
+	ulong	seiadh1;	/* indirect access data register 1 (63-32 of data) */
+	ulong	seiadl;	/* indirect access data register low */
+	ulong	seafc;	/* advanced feature control */
+	ulong	scph;	/* services code priority high (ie, TOS priority) */
+	ulong	scpl;		/* services code priority low */
+	ulong	mah;		/* switch MAC address high */
+	ulong	mal;		/* switch MAC address low */
+	ulong	ppm[2];	/* ports 1 to 4 PHY power management */
+};
+
+enum {
+	/* Sec0 */
+	Nbe=	1<<31,	/* new backoff (designed for UNH) enable */
+	/* 30:28 802.1p base priority */
+	/* 27:25 LAN LED1 select */
+	/* 24:22 LAN LED0 select */
+	Unh=	1<<21,	/* =1, drop packets with type 8808 or DA=0180c2000001; =0, drop flow control */
+	Lca=		1<<20,	/* link change age: faster aging for link->no link transition */
+	Paf=		1<<19,	/* pass all frames, including bad ones */
+	Sfce=	1<<18,	/* switch MII full-duplex flow control enable */
+	Flfc=		1<<17,	/* frame length field check in IEEE (drop invalid ones) */
+	Bsm=	1<<16,	/* =1, share all buffers; =0, use only 1/5 of pool */
+	Age=	1<<15,	/* enable age function */
+	Agef=	1<<14,	/* enable fast ageing */
+	Aboe=	1<<13,	/* aggressive backoff enable */
+	Uvmd=	1<<12,	/* unicast port-VLAN mismatch discard */
+	Mspd=	1<<11,	/* multicast storm protection disable */
+	Bpm=	1<<10,	/* =1, carrier sense backpressure; =0, collision backpressure */
+	Fair=		1<<9,	/* fair flow control and back pressure */
+	Ncd=	1<<8,	/* no excessive collision drop */
+	Lmpsd=	1<<7,	/* 1=, drop packet sizes over 1536 bytes; =0, 1522 for tagged, 1518 untagged */
+	Pbr=		1<<6,	/* priority buffer reserved */
+	Sbpe=	1<<5,	/* switch back pressure enable */
+	Shdm=	1<<4,	/* switch half duplex mode */
+	PrioHi=	0<<2,	/* always deliver high priority first */
+	Prio10_1= 1<<2,	/* high/low at 10:1 */
+	Prio5_1=	2<<2,	/* high/low at 5:1 */
+	Prio2_1=	3<<2,	/* high/low at 2:1 */
+	Etm=	1<<1,	/* enable tag mask */
+	Esf=		1<<0,	/* enable switch function */
+
+	/* sec1 */
+	/* 31:21 */	/* broadcast storm protection, byte count */
+	IEEEneg=	1<<11,	/* follow IEEE spec for auto neg */
+	Tpid=	1<<10,	/* special TPID mode used for direct forwarding from port 5 */
+	PhyEn=	1<<8,	/* enable PHY MII */
+	TfcDis=	1<<7,	/* disable IEEE transmit flow control */
+	RfcDis=	1<<6,	/* disable IEEE receive flow control */
+	Hps=	1<<5,	/* huge packet support: allow packets up to 1916 bytes */
+	VlanEn=	1<<4,	/* 802.1Q VLAN enable; recommended when priority queue on */
+	Sw10BT=	1<<1,	/* switch in 10 Mbps mode not 100 Mbps */
+	VIDrep=	1<<0,	/* replace null VID with port VID (otherwise no replacement) */
+
+};
+#define	BASEPRIO(n)	(((n)&7)<<28)
+
+
+enum {
+	/* cfg[n][0] (SEP1C1-SEP4C1) p. 89 */
+	/* 31:16	default tag: 31:29=userprio, 28=CFI bit, 27:16=VID[11:0] */
+	AnegDis=	1<<15,	/* disable auto negotiation */
+	Force100=	1<<14,	/* force 100BT when auto neg is disabled */
+	ForceFD=	1<<13,	/* force full duplex when auto neg is disabled */
+	/* 12:8	port VLAN membership: bit 8 is port 1, bit 12 is port 5, 1=member */
+	STTxEn=	1<<7,	/* spanning tree transmit enable */
+	STRxEn=	1<<6,	/* spanning tree receive enable */
+	STLnDis=	1<<5,	/* spanning tree learn disnable */
+	Bsp=		1<<4,	/* enable broadcast storm protection */
+	Pce=		1<<3,	/* priority classification enable */
+	Dpce=	1<<2,	/* diffserv priority classification enable */
+	IEEEpce=	1<<1,	/* IEEE (802.1p) classification enable */
+	PrioEn=	1<<0,	/* enable priority function on port */
+
+	/* cfg[n][1] (SEP1C2-SEP4C2) p. 91*/
+	IngressFilter=	1<<28,	/* discard packets from ingress port not in VLAN */
+	DiscardNonPVID=	1<<27,	/* discard packets whose VID does not match port default VID */
+	ForcePortFC=	1<<26,	/* force flow control */
+	EnablePortBP=	1<<25,	/* enable back pressure */
+	/* 23:12 transmit high priority rate control */
+	/* 11:0 transmit low priority rate control */
+
+	/* cfg[n][2] */
+	/* 13:20	receive high priority rate control */
+	/* 19:8	receive low priority rate control */
+	Rdprc=	1<<7,	/* receive differential priority rate control */
+	Lprrc=	1<<6,	/* low priority receive rate control */
+	Hprrc=	1<<5,	/* high priority receive rate control */
+	Lprfce=	1<<4,	/* low priority receive flow control enable */
+	Hprfce=	1<<3,	/* high priority ... */
+	Tdprc=	1<<2,	/* transmit differential priority rate control */
+	Lptrc=	1<<1,	/* low priority transmit rate control */
+	Hptrc=	1<<0,	/* high priority transmit rate control */
+
+	/* seiac */
+	Cread=	1<<12,
+	Cwrite=	0<<12,
+	  StaticMacs=	0<<10,	/* static mac address table used */
+	  VLANs=		1<<10,	/* VLAN table */
+	  DynMacs=	2<<10,	/* dynamic address table */
+	  MibCounter=	3<<10,	/* MIB counter selected */
+	/* 0:9, table index */
+
+	/* seafc */
+	/* 26:22	1<<(n+22-1) = removal for port 0 to 4 */
+};
+
+/*
+ * indirect access to
+ *	static MAC address table (3.10.23, p. 107)
+ *	VLAN table (3.10.24, p. 108)
+ *	dynamic MAC address table (3.10.25, p. 109)
+ *	MIB counters (3.10.26, p. 110)
+ */
+enum {
+	/* VLAN table */
+	VlanValid=	1<<21,	/* entry is valid */
+	/* 20:16 are bits for VLAN membership */
+	/* 15:12 are bits for FID (filter id) for up to 16 active VLANs */
+	/* 11:0 has 802.1Q 12 bit VLAN ID */
+
+	/* Dynamic MAC table (1024 entries) */
+	MACempty=	1<<(68-2*32),
+	/* 67:58 is number of valid entries-1 */
+	/* 57:56 ageing time stamp */
+	NotReady=	1<<(55-32),
+	/* 54:52 source port 0 to 5 */
+	/* 51:48 FID */
+	/* 47:0 MAC */
+
+	NVlans=	16,
+	NSMacs=	8,
+};
+
+/*
+ * per-port counters, table 3, 3.10.26, p. 110
+ * cleared when read
+ * port counters at n*0x20 [n=0-3]
+ */
+static char* portmibnames[] = {
+	"RxLoPriorityByte",
+	"RxHiPriorityByte",
+	"RxUndersizePkt",
+	"RxFragments",
+	"RxOversize",
+	"RxJabbers",
+	"RxSymbolError",
+	"RxCRCerror",
+	"RxAlignmentError",
+	"RxControl8808Pkts",
+	"RxPausePkts",
+	"RxBroadcast",
+	"RxMulticast",
+	"RxUnicast",
+	"Rx64Octets",
+	"Rx65to127Octets",
+	"Rx128to255Octets",
+	"Rx256to511Octets",
+	"Rx512to1023Octets",
+	"Rx1024to1522Octets",
+	"TxLoPriorityByte",
+	"TxHiPriorityByte",
+	"TxLateCollision",
+	"TxPausePkts",
+	"TxBroadcastPkts",
+	"TxMulticastPkts",
+	"TxUnicastPkts",
+	"TxDeferred",
+	"TxTotalCollision",	/* like, totally */
+	"TxExcessiveCollision",
+	"TxSingleCollision",
+	"TxMultipleCollision",
+};
+enum {
+	/* per-port MIB counter format */
+	MibOverflow=	1<<31,
+	MibValid=		1<<30,
+	/* 29:0 counter value */
+};
+
+/*
+ * 16 bit `all port' counters, not automatically cleared
+ *	offset 0x100 and up
+ */
+
+static char* allportnames[] = {
+	"Port1TxDropPackets",
+	"Port2TxDropPackets",
+	"Port3TxDropPackets",
+	"Port4TxDropPackets",
+	"LanTxDropPackets",	/* ie, internal port 5 */
+	"Port1RxDropPackets",
+	"Port2RxDropPackets",
+	"Port3RxDropPackets",
+	"Port4RxDropPackets",
+	"LanRxDropPackets",
+};
+
+static void
+switchinit(uchar *ea)
+{
+	Switch *sw;
+	int i;
+	ulong an;
+
+	/* TO DO: LED gpio setting */
+
+	GPIOREG->iopm |= 0xF0;	/* bits 4-7 are LAN(?) */
+iprint("switch init...\n");
+	sw = KADDR(PHYSSWITCH);
+	if(sw->sec0 & Esf){
+		iprint("already inited\n");
+		return;
+	}
+	sw->seafc = 0;
+	microdelay(10);
+	sw->scph = 0;
+	microdelay(10);
+	sw->scpl = 0;
+	microdelay(10);
+	if(ea != nil){
+		sw->mah = (ea[0]<<8) | ea[1];
+		microdelay(10);
+		sw->mal = (ea[2]<<24) | (ea[3]<<16) | (ea[4]<<8) | ea[5];
+		microdelay(10);
+	}
+	for(i = 0; i < 5; i++){
+		sw->cfg[i][0] = (0x1F<<8) | STTxEn | STRxEn | Bsp;	/* port is member of all vlans */
+		microdelay(10);
+		sw->cfg[i][1] = 0;
+		microdelay(10);
+		sw->cfg[i][2] = 0;
+		microdelay(10);
+	}
+	sw->ppm[0] = 0;	/* perhaps soft reset? */
+	microdelay(10);
+	sw->ppm[1] = 0;
+	microdelay(10);
+	an = WAnr | WAnaP | WAna100FD | WAna100HD | WAna10FD | WAna10HD;
+	sw->an[0] = an | (an >> 16);
+	microdelay(10);
+	sw->an[1] = an | (an >> 16);
+	microdelay(10);
+	sw->sec1 = (0x4A<<21) | PhyEn;
+	microdelay(10);
+	sw->sec0 = Nbe | (0<<28) | (LedSpeed<<25) | (LedLinkTxRx<<22) | Sfce | Bsm | Age | Aboe | Bpm | Fair | Sbpe | Shdm | Esf;
+	microdelay(10);
+
+	/* off we go */
+}
+
+typedef struct Vidmap Vidmap;
+struct Vidmap {
+	uchar	ports;	/* bit mask for ports 0 to 4 */
+	uchar	fid;	/* switch's filter id */
+	ushort	vid;	/* 802.1Q vlan id; 0=not valid */
+};
+
+static Vidmap
+getvidmap(Switch *sw, int i)
+{
+	ulong w;
+	Vidmap v;
+
+	v.ports = 0;
+	v.fid = 0;
+	v.vid = 0;
+	if(i < 0 || i >= NVlans)
+		return v;
+	sw->seiac = Cread | VLANs | i;
+	microdelay(10);
+	w = sw->seiadl;
+	if((w & VlanValid) == 0)
+		return v;
+	v.vid = w & 0xFFFF;
+	v.fid = (w>>12) & 0xF;
+	v.ports = (w>>16) & 0x1F;
+	return v;
+}
+
+static void
+putvidmap(Switch *sw, int i, Vidmap v)
+{
+	ulong w;
+
+	w = ((v.ports & 0x1F)<<16) | ((v.fid & 0xF)<<12) | (v.vid & 0xFFFF);
+	if(v.vid != 0)
+		w |= VlanValid;
+	sw->seiadl = w;
+	microdelay(10);
+	sw->seiac = Cwrite | VLANs | i;
+	microdelay(10);
+}
+
+typedef struct StaticMac StaticMac;
+struct StaticMac {
+	uchar	valid;
+	uchar	fid;
+	uchar	usefid;
+	uchar	override;	/* override spanning tree tx/rx disable */
+	uchar	ports;	/* forward to this set of ports */
+	uchar	mac[Eaddrlen];
+};
+
+static StaticMac
+getstaticmac(Switch *sw, int i)
+{
+	StaticMac s;
+	ulong w;
+
+	memset(&s, 0, sizeof(s));
+	if(i < 0 || i >= NSMacs)
+		return s;
+	sw->seiac = Cread | StaticMacs | i;
+	microdelay(10);
+	w = sw->seiadh1;
+	if((w & (1<<(53-32))) == 0)
+		return s;	/* entry not valid */
+	s.valid = 1;
+	s.fid= (w>>(57-32)) & 0xF;
+	s.usefid = (w & (1<<(56-32))) != 0;
+	s.override = (w & (1<<(54-32))) != 0;
+	s.ports = (w>>(48-32)) & 0x1F;
+	s.mac[5] = w >> 8;
+	s.mac[4] = w;
+	w = sw->seiadl;
+	s.mac[3] = w>>24;
+	s.mac[2] = w>>16;
+	s.mac[1] = w>>8;
+	s.mac[0] = w;
+	return s;
+}
+
+static void
+putstaticmac(Switch *sw, int i, StaticMac s)
+{
+	ulong w;
+
+	if(s.valid){
+		w = 1<<(53-32);	/* entry valid */
+		if(s.usefid)
+			w |= 1<<(55-32);
+		if(s.override)
+			w |= 1<<(54-32);
+		w |= (s.fid & 0xF) << (56-32);
+		w |= (s.ports & 0x1F) << (48-32);
+		w |= (s.mac[5] << 8) | s.mac[4];
+		sw->seiadh1 = w;
+		microdelay(10);
+		w = (s.mac[3]<<24) | (s.mac[2]<<16) | (s.mac[1]<<8) | s.mac[0];
+		sw->seiadl = w;
+		microdelay(10);
+	}else{
+		sw->seiadh1 = 0;	/* valid bit is 0; rest doesn't matter */
+		microdelay(10);
+	}
+	sw->seiac = Cwrite | StaticMacs | i;
+	microdelay(10);
+}
+
+typedef struct DynMac DynMac;
+struct DynMac {
+	ushort	nentry;
+	uchar	valid;
+	uchar	age;
+	uchar	port;		/* source port (0 origin) */
+	uchar	fid;		/* filter id */
+	uchar	mac[Eaddrlen];
+};
+
+static DynMac
+getdynmac(Switch *sw, int i)
+{
+	DynMac d;
+	ulong w;
+	int n, l;
+
+	memset(&d, 0, sizeof d);
+	l = 0;
+	do{
+		if(++l > 100)
+			return d;
+		sw->seiac = Cread | DynMacs | i;
+		microdelay(10);
+		w = sw->seiadh2;
+		/* peculiar encoding of table size */
+		if(w & MACempty)
+			return d;
+		n = w & 0xF;
+		w = sw->seiadh1;
+	}while(w & NotReady);	/* TO DO: how long might it delay? */
+	d.nentry = ((n<<6) | (w>>(58-32))) + 1;
+	if(i < 0 || i >= d.nentry)
+		return d;
+	d.valid = 1;
+	d.age = (w>>(56-32)) & 3;
+	d.port = (w>>(52-32)) & 7;
+	d.fid = (w>>(48-32)) & 0xF;
+	d.mac[5] = w>>8;
+	d.mac[4] = w;
+	w = sw->seiadl;
+	d.mac[3] = w>>24;
+	d.mac[2] = w>>16;
+	d.mac[1] = w>>8;
+	d.mac[0] = w;
+	return d;
+}
+
+static void
+switchdump(void)
+{
+	Switch *sw;
+	int i, j;
+	ulong w;
+
+	sw = KADDR(PHYSSWITCH);
+	iprint("sec0 %8.8lux\n", sw->sec0);
+	iprint("sec1 %8.8lux\n", sw->sec1);
+	for(i = 0; i < 5; i++){
+		iprint("cfg%d", i);
+		for(j = 0; j < 3; j++){
+			w = sw->cfg[i][j];
+			iprint(" %8.8lux", w);
+		}
+		iprint("\n");
+		if(i < 2){
+			w = sw->an[i];
+			iprint(" an=%8.8lux pm=%8.8lux\n", w, sw->ppm[i]);
+		}
+	}
+	for(i = 0; i < 8; i++){
+		sw->seiac = Cread | DynMacs | i;
+		microdelay(10);
+		w = sw->seiadh2;
+		microdelay(10);
+		iprint("dyn%d: %8.8lux", i, w);
+		w = sw->seiadh1;
+		microdelay(10);
+		iprint(" %8.8lux", w);
+		w = sw->seiadl;
+		microdelay(10);
+		iprint(" %8.8lux\n", w);
+	}
+	for(i=0; i<0x20; i++){
+		sw->seiac = Cread | MibCounter | i;
+		microdelay(10);
+		w = sw->seiadl;
+		microdelay(10);
+		if(w & (1<<30))
+			iprint("%.2ux: %s: %lud\n", i, portmibnames[i], w & ~(3<<30));
+	}
+}
+
+static void
+switchstatproc(void*)
+{
+	for(;;){
+		tsleep(&up->sleep, return0, nil, 30*1000);
+	}
+}
+
+void
+etherks8695link(void)
+{
+	addethercard("ks8695", reset);
+}
+
+/*
+ * notes:
+ *	switch control
+ *	read stats every 30 seconds or so
+ */
--- /dev/null
+++ b/os/manga/flashif.h
@@ -1,0 +1,82 @@
+typedef struct Flash Flash;
+
+/*
+ * structure defining a flash memory card
+ */
+struct Flash {
+	QLock;	/* interlock on flash operations */
+	Flash*	next;
+
+	/* the following are filled in by devflash before Flash.reset called */
+	char*	name;
+	void*	addr;
+	ulong	size;
+	void *	archdata;
+	int	(*reset)(Flash*);
+
+	/* the following are filled in by the reset routine */
+	int	(*eraseall)(Flash*);
+	int	(*erasezone)(Flash*, int);
+	int	(*read)(Flash*, ulong, void*, long);	/* reads of correct width and alignment */
+	int	(*write)(Flash*, ulong, void*, long);	/* writes of correct width and alignment */
+	int	(*suspend)(Flash*);
+	int	(*resume)(Flash*);
+	int	(*attach)(Flash*);
+
+	uchar	id;	/* flash manufacturer ID */
+	uchar	devid;	/* flash device ID */
+	int	width;	/* bytes per flash line */
+	int	erasesize;	/* size of erasable unit (accounting for width) */
+	void*	data;		/* flash type routines' private storage, or nil */
+	ulong	unusable;	/* bit mask of unusable sections */
+};
+
+/*
+ * called by link routine of driver for specific flash type: arguments are
+ * conventional name for card type/model, and card driver's reset routine.
+ */
+void	addflashcard(char*, int (*)(Flash*));
+
+/*
+ * called by devflash.c:/^flashreset; if flash exists,
+ * sets type, address, and size in bytes of flash
+ * and returns 0; returns -1 if flash doesn't exist
+ */
+int	archflashreset(int instance, char*, int, void**, long*, void **archdata);
+
+int	archflash12v(int);
+void	archflashwp(void *archdata, int);
+
+/*
+ * Architecture specific routines for managing nand devices
+ */
+
+/*
+ * do any device spcific initialisation
+ */
+void archnand_init(void *archdata);
+
+/*
+ * if claim is 1, claim device exclusively, and enable it (power it up)
+ * if claim is 0, release, and disable it (power it down)
+ * claiming may be as simple as a qlock per device
+ */
+void archnand_claim(void *archdata, int claim);
+
+/*
+ * set command latch enable (CLE) and address latch enable (ALE)
+ * appropriately
+ */
+void archnand_setCLEandALE(void *archdata, int cle, int ale);
+
+/*
+ * write a sequence of bytes to the device
+ */
+void archnand_write(void *archdata, void *buf, int len);
+
+/*
+ * read a sequence of bytes from the device
+ * if buf is 0, throw away the data
+ */
+void archnand_read(void *archdata, void *buf, int len);
+
--- /dev/null
+++ b/os/manga/fns.h
@@ -1,0 +1,163 @@
+#include "../port/portfns.h"
+
+ulong	aifinit(uchar *aifarr);
+int	archaudiopower(int);
+void	archaudiomute(int);
+void	archaudioamp(int);
+int	archaudiospeed(int, int);
+void	archconfinit(void);
+void	archconsole(void);
+int	archflash12v(int);
+long	archkprofmicrosecondspertick(void);
+void	archkprofenable(int);
+void	archpowerdown(void);
+void	archpowerup(void);
+void	archreboot(void);
+void	archreset(void);
+vlong	archrdtsc(void);
+ulong archrdtsc32(void);
+void	archuartpower(int, int);
+void	blankscreen(int);
+void	clockcheck(void);
+void	clockinit(void);
+void	clockpoll(void);
+#define	coherence()		/* nothing to do for cache coherence for uniprocessor */
+void	cursorhide(void);
+void	cursorunhide(void);
+void	dcflush(void*, ulong);
+void	dcflushall(void);
+void	dcinval(void);
+int	dmaidle(Dma*);
+Dma*	dmasetup(int device, void(*)(void*,ulong), void*, ulong);
+int	dmastart(Dma*, void*, void*, int);
+int	dmacontinue(Dma*, void*, int);
+void	dmastop(Dma*);
+int	dmaerror(Dma*);
+void	dmafree(Dma*);
+void	dmareset(void);
+void	dmawait(Dma*);
+void dumplongs(char *, ulong *, int);
+void	dumpregs(Ureg* ureg);
+void	dumpstack(void);
+int	fpiarm(Ureg*);
+void	fpinit(void);
+ulong	getcallerpc(void*);
+ulong	getcclkcfg(void);
+char*	getconf(char*);
+ulong	getcpsr(void);
+ulong	getcpuid(void);
+ulong	getspsr(void);
+void	gotopc(ulong);
+
+void	icflush(void*, ulong);
+void	icflushall(void);
+void	idle(void);
+void	idlehands(void);
+int	inb(ulong);
+int	ins(ulong);
+ulong	inl(ulong);
+void	outb(ulong, int);
+void	outs(ulong, int);
+void	outl(ulong, ulong);
+void inss(ulong, void*, int);
+void outss(ulong, void*, int);
+void	insb(ulong, void*, int);
+void	outsb(ulong, void*, int);
+void	intrdisable(int, int, void (*)(Ureg*, void*), void*, char*);
+void	intrenable(int, int, void (*)(Ureg*, void*), void*, char*);
+void	iofree(int);
+#define	iofree(x)
+void	ioinit(void);
+int	iounused(int, int);
+int	ioalloc(int, int, int, char*);
+#define	ioalloc(a,b,c,d) 0
+int	iprint(char*, ...);
+void	installprof(void (*)(Ureg *, int));
+int	isvalid_va(void*);
+void	kbdinit(void);
+void	ledset(int);
+void	links(void);
+void	mmuenable(ulong);
+void*	mmucacheinhib(void*, ulong);
+ulong	mmugetctl(void);
+ulong	mmugetdac(void);
+ulong	mmugetfar(void);
+ulong	mmugetfsr(void);
+void	mmuinit(void);
+void*	mmukaddr(ulong);
+void*	mmuphysmap(void*, ulong, ulong);
+void	mmuputctl(ulong);
+void	mmuputdac(ulong);
+void	mmuputfsr(ulong);
+void	mmuputttb(ulong);
+void	mmureset(void);
+void	mouseinit(void);
+void*	pa2va(ulong);
+void	pcimapinit(void);
+int	pciscan(int, Pcidev **);
+ulong	pcibarsize(Pcidev *, int);
+int	pcicfgr8(Pcidev*, int);
+int	pcicfgr16(Pcidev*, int);
+int	pcicfgr32(Pcidev*, int);
+void	pcicfgw8(Pcidev*, int, int);
+void	pcicfgw16(Pcidev*, int, int);
+void	pcicfgw32(Pcidev*, int, int);
+void	pciclrbme(Pcidev*);
+void	pcihinv(Pcidev*);
+uchar	pciipin(Pcidev *, uchar);
+Pcidev* pcimatch(Pcidev*, int, int);
+Pcidev* pcimatchtbdf(int);
+void	pcireset(void);
+void	pcisetbme(Pcidev*);
+void	powerenable(void (*)(int));
+void	powerdisable(void (*)(int));
+void	powerdown(void);
+void	powerinit(void);
+void	powersuspend(void);
+#define procsave(p)
+#define procrestore(p)
+void	putcclkcfg(ulong);
+long	rtctime(void);
+void	screeninit(void);
+void	(*screenputs)(char*, int);
+int	segflush(void*, ulong);
+void	setpanic(void);
+void	setr13(int, void*);
+int	splfhi(void);
+int	splflo(void);
+void	_suspendcode(void);
+void	tlbinvalidateall(void);
+void	tlbinvalidateaddr(void*);
+void	trapinit(void);
+void	trapstacks(void);
+void	trapspecial(int (*)(Ureg *, uint));
+void	uartconsole(void);
+void	uartinstall(void);
+int	uartprint(char*, ...);
+ulong	va2pa(void*);
+void	vectors(void);
+void	vtable(void);
+#define	waserror()	(up->nerrlab++, setlabel(&up->errlab[up->nerrlab-1]))
+int	wasbusy(int);
+
+#define	KADDR(p)	mmukaddr((ulong)(p))
+#define PADDR(v)	va2pa((void*)(v))
+
+ulong	timer_start(void);
+ulong	timer_ticks(ulong);
+int 	timer_devwait(ulong *adr, ulong mask, ulong val, int ost);
+void 	timer_setwatchdog(int ost);
+void 	timer_delay(int ost);
+ulong	ms2tmr(int ms);
+int	tmr2ms(ulong t);
+void	delay(int ms);
+ulong	us2tmr(int us);
+int	tmr2us(ulong t);
+void 	microdelay(int us);
+
+#define	archuartclock(p,rate)	14745600
+
+/* debugging */
+extern	void	serialputs(char*, int);
+extern	void	serialputc(int);
+extern	void	xdelay(int);
--- /dev/null
+++ b/os/manga/fpi.h
@@ -1,0 +1,61 @@
+typedef long Word;
+typedef unsigned long Single;
+typedef struct {
+	unsigned long l;
+	unsigned long h;
+} Double;
+
+enum {
+	FractBits	= 28,
+	CarryBit	= 0x10000000,
+	HiddenBit	= 0x08000000,
+	MsBit		= HiddenBit,
+	NGuardBits	= 3,
+	GuardMask	= 0x07,
+	LsBit		= (1<<NGuardBits),
+
+	SingleExpBias	= 127,
+	SingleExpMax	= 255,
+	DoubleExpBias	= 1023,
+	DoubleExpMax	= 2047,
+
+	ExpBias		= DoubleExpBias,
+	ExpInfinity	= DoubleExpMax,
+};
+
+typedef struct {
+	unsigned char s;
+	short e;
+	long l;				/* 0000FFFFFFFFFFFFFFFFFFFFFFFFFGGG */
+	long h;				/* 0000HFFFFFFFFFFFFFFFFFFFFFFFFFFF */
+} Internal;
+
+#define IsWeird(n)	((n)->e >= ExpInfinity)
+#define	IsInfinity(n)	(IsWeird(n) && (n)->h == HiddenBit && (n)->l == 0)
+#define	SetInfinity(n)	((n)->e = ExpInfinity, (n)->h = HiddenBit, (n)->l = 0)
+#define IsNaN(n)	(IsWeird(n) && (((n)->h & ~HiddenBit) || (n)->l))
+#define	SetQNaN(n)	((n)->s = 0, (n)->e = ExpInfinity, 		\
+			 (n)->h = HiddenBit|(LsBit<<1), (n)->l = 0)
+#define IsZero(n)	((n)->e == 1 && (n)->h == 0 && (n)->l == 0)
+#define SetZero(n)	((n)->e = 1, (n)->h = 0, (n)->l = 0)
+
+/*
+ * fpi.c
+ */
+extern void fpiround(Internal *);
+extern void fpiadd(Internal *, Internal *, Internal *);
+extern void fpisub(Internal *, Internal *, Internal *);
+extern void fpimul(Internal *, Internal *, Internal *);
+extern void fpidiv(Internal *, Internal *, Internal *);
+extern int fpicmp(Internal *, Internal *);
+extern void fpinormalise(Internal*);
+
+/*
+ * fpimem.c
+ */
+extern void fpis2i(Internal *, void *);
+extern void fpid2i(Internal *, void *);
+extern void fpiw2i(Internal *, void *);
+extern void fpii2s(void *, Internal *);
+extern void fpii2d(void *, Internal *);
+extern void fpii2w(Word *, Internal *);
--- /dev/null
+++ b/os/manga/fpiarm.c
@@ -1,0 +1,483 @@
+/*
+ * this doesn't attempt to implement ARM floating-point properties
+ * that aren't visible in the Inferno environment.
+ * all arithmetic is done in double precision.
+ * the FP trap status isn't updated.
+ */
+#include "u.h"
+#include	"../port/lib.h"
+#include	"mem.h"
+#include	"dat.h"
+#include	"fns.h"
+#include	"io.h"
+
+#include	"ureg.h"
+
+#include "fpi.h"
+
+// #define	R13OK	undef this if correct kernel r13 isn't in Ureg; check calculation in fpiarm below
+
+#define	REG(x) (*(long*)(((char*)(ur))+roff[(x)]))
+#define	FPENV	(*(ufp))
+#define	FR(x) (*(Internal*)(ufp)->regs[(x)&7])
+
+/* BUG: check fetch (not worthwhile in Inferno) */
+#define	getubyte(a) (*(uchar*)(a))
+#define	getuword(a) (*(ushort*)(a))
+#define	getulong(a) (*(ulong*)(a))
+
+typedef struct FP2 FP2;
+typedef struct FP1 FP1;
+
+struct FP2 {
+	char*	name;
+	void	(*f)(Internal, Internal, Internal*);
+};
+
+struct FP1 {
+	char*	name;
+	void	(*f)(Internal*, Internal*);
+};
+
+enum {
+	N = 1<<31,
+	Z = 1<<30,
+	C = 1<<29,
+	V = 1<<28,
+	REGPC = 15,
+};
+
+int	fpemudebug = 0;
+
+#undef OFR
+#define	OFR(X)	((ulong)&((Ureg*)0)->X)
+
+static	int	roff[] = {
+	OFR(r0), OFR(r1), OFR(r2), OFR(r3),
+	OFR(r4), OFR(r5), OFR(r6), OFR(r7),
+	OFR(r8), OFR(r9), OFR(r10), OFR(r11),
+#ifdef R13OK
+	OFR(r12), OFR(r13), OFR(r14), OFR(pc),
+#else
+	OFR(r12), OFR(type), OFR(r14), OFR(pc),
+#endif
+};
+
+static Internal fpconst[8] = {	/* indexed by op&7 */
+	/* s, e, l, h */
+	{0, 0x1, 0x00000000, 0x00000000}, /* 0.0 */
+	{0, 0x3FF, 0x00000000, 0x08000000},	/* 1.0 */
+	{0, 0x400, 0x00000000, 0x08000000},	/* 2.0 */
+	{0, 0x400, 0x00000000, 0x0C000000},	/* 3.0 */
+	{0, 0x401, 0x00000000, 0x08000000},	/* 4.0 */
+	{0, 0x401, 0x00000000, 0x0A000000},	/* 5.0 */
+	{0, 0x3FE, 0x00000000, 0x08000000},	/* 0.5 */
+	{0, 0x402, 0x00000000, 0x0A000000},	/* 10.0 */
+};
+
+/*
+ * arm binary operations
+ */
+
+static void
+fadd(Internal m, Internal n, Internal *d)
+{
+	(m.s == n.s? fpiadd: fpisub)(&m, &n, d);
+}
+
+static void
+fsub(Internal m, Internal n, Internal *d)
+{
+	m.s ^= 1;
+	(m.s == n.s? fpiadd: fpisub)(&m, &n, d);
+}
+
+static void
+fsubr(Internal m, Internal n, Internal *d)
+{
+	n.s ^= 1;
+	(n.s == m.s? fpiadd: fpisub)(&n, &m, d);
+}
+
+static void
+fmul(Internal m, Internal n, Internal *d)
+{
+	fpimul(&m, &n, d);
+}
+
+static void
+fdiv(Internal m, Internal n, Internal *d)
+{
+	fpidiv(&m, &n, d);
+}
+
+static void
+fdivr(Internal m, Internal n, Internal *d)
+{
+	fpidiv(&n, &m, d);
+}
+
+/*
+ * arm unary operations
+ */
+
+static void
+fmov(Internal *m, Internal *d)
+{
+	*d = *m;
+}
+
+static void
+fmovn(Internal *m, Internal *d)
+{
+	*d = *m;
+	d->s ^= 1;
+}
+
+static void
+fabsf(Internal *m, Internal *d)
+{
+	*d = *m;
+	d->s = 0;
+}
+
+static void
+frnd(Internal *m, Internal *d)
+{
+	short e;
+
+	(m->s? fsub: fadd)(fpconst[6], *m, d);
+	if(IsWeird(d))
+		return;
+	fpiround(d);
+	e = (d->e - ExpBias) + 1;
+	if(e <= 0)
+		SetZero(d);
+	else if(e > FractBits){
+		if(e < 2*FractBits)
+			d->l &= ~((1<<(2*FractBits - e))-1);
+	}else{
+		d->l = 0;
+		if(e < FractBits)
+			d->h &= ~((1<<(FractBits-e))-1);
+	}
+}
+
+static	FP1	optab1[16] = {	/* Fd := OP Fm */
+[0]	{"MOVF",	fmov},
+[1]	{"NEGF",	fmovn},
+[2]	{"ABSF",	fabsf},
+[3]	{"RNDF",	frnd},
+[4]	{"SQTF",	/*fsqt*/0},
+/* LOG, LGN, EXP, SIN, COS, TAN, ASN, ACS, ATN all `deprecated' */
+/* URD and NRM aren't implemented */
+};
+
+static	FP2	optab2[16] = {	/* Fd := Fn OP Fm */
+[0]	{"ADDF",	fadd},
+[1]	{"MULF",	fmul},
+[2]	{"SUBF",	fsub},
+[3]	{"RSUBF",	fsubr},
+[4]	{"DIVF",	fdiv},
+[5]	{"RDIVF",	fdivr},
+/* POW, RPW deprecated */
+[8]	{"REMF",	/*frem*/0},
+[9]	{"FMF",	fmul},	/* fast multiply */
+[10]	{"FDV",	fdiv},	/* fast divide */
+[11]	{"FRD",	fdivr},	/* fast reverse divide */
+/* POL deprecated */
+};
+
+static ulong
+fcmp(Internal *n, Internal *m)
+{
+	int i;
+
+	if(IsWeird(m) || IsWeird(n)){
+		/* BUG: should trap if not masked */
+		return V|C;
+	}
+	i = fpicmp(n, m);
+	if(i > 0)
+		return C;
+	else if(i == 0)
+		return C|Z;
+	else
+		return N;
+}
+
+static void
+fld(void (*f)(Internal*, void*), int d, ulong ea, int n, FPenv *ufp)
+{
+	void *mem;
+
+	mem = (void*)ea;
+	(*f)(&FR(d), mem);
+	if(fpemudebug)
+		print("MOV%c #%lux, F%d\n", n==8? 'D': 'F', ea, d);
+}
+
+static void
+fst(void (*f)(void*, Internal*), ulong ea, int s, int n, FPenv *ufp)
+{
+	Internal tmp;
+	void *mem;
+
+	mem = (void*)ea;
+	tmp = FR(s);
+	if(fpemudebug)
+		print("MOV%c	F%d,#%lux\n", n==8? 'D': 'F', s, ea);
+	(*f)(mem, &tmp);
+}
+
+static int
+condok(int cc, int c)
+{
+	switch(c){
+	case 0:	/* Z set */
+		return cc&Z;
+	case 1:	/* Z clear */
+		return (cc&Z) == 0;
+	case 2:	/* C set */
+		return cc&C;
+	case 3:	/* C clear */
+		return (cc&C) == 0;
+	case 4:	/* N set */
+		return cc&N;
+	case 5:	/* N clear */
+		return (cc&N) == 0;
+	case 6:	/* V set */
+		return cc&V;
+	case 7:	/* V clear */
+		return (cc&V) == 0;
+	case 8:	/* C set and Z clear */
+		return cc&C && (cc&Z) == 0;
+	case 9:	/* C clear or Z set */
+		return (cc&C) == 0 || cc&Z;
+	case 10:	/* N set and V set, or N clear and V clear */
+		return (~cc&(N|V))==0 || (cc&(N|V)) == 0;
+	case 11:	/* N set and V clear, or N clear and V set */
+		return (cc&(N|V))==N || (cc&(N|V))==V;
+	case 12:	/* Z clear, and either N set and V set or N clear and V clear */
+		return (cc&Z) == 0 && ((~cc&(N|V))==0 || (cc&(N|V))==0);
+	case 13:	/* Z set, or N set and V clear or N clear and V set */
+		return (cc&Z) || (cc&(N|V))==N || (cc&(N|V))==V;
+	case 14:	/* always */
+		return 1;
+	case 15:	/* never (reserved) */
+		return 0;
+	}
+	return 0;	/* not reached */
+}
+
+static void
+unimp(ulong pc, ulong op)
+{
+	char buf[60];
+
+	snprint(buf, sizeof(buf), "sys: fp: pc=%lux unimp fp 0x%.8lux", pc, op);
+	if(fpemudebug)
+		print("FPE: %s\n", buf);
+	error(buf);
+	/* no return */
+}
+
+static void
+fpemu(ulong pc, ulong op, Ureg *ur, FPenv *ufp)
+{
+	int rn, rd, tag, o;
+	long off;
+	ulong ea;
+	Internal tmp, *fm, *fn;
+
+	/* note: would update fault status here if we noted numeric exceptions */
+
+	/*
+	 * LDF, STF; 10.1.1
+	 */
+	if(((op>>25)&7) == 6){
+		if(op & (1<<22))
+			unimp(pc, op);	/* packed or extended */
+		rn = (op>>16)&0xF;
+		off = (op&0xFF)<<2;
+		if((op & (1<<23)) == 0)
+			off = -off;
+		ea = REG(rn);
+		if(rn == REGPC)
+			ea += 8;
+		if(op & (1<<24))
+			ea += off;
+		rd = (op>>12)&7;
+		if(op & (1<<20)){
+			if(op & (1<<15))
+				fld(fpid2i, rd, ea, 8, ufp);
+			else
+				fld(fpis2i, rd, ea, 4, ufp);
+		}else{
+			if(op & (1<<15))
+				fst(fpii2d, ea, rd, 8, ufp);
+			else
+				fst(fpii2s, ea, rd, 4, ufp);
+		}
+		if((op & (1<<24)) == 0)
+			ea += off;
+		if(op & (1<<21))
+			REG(rn) = ea;
+		return;
+	}
+
+	/*
+	 * CPRT/transfer, 10.3
+	 */
+	if(op & (1<<4)){
+		rd = (op>>12) & 0xF;
+
+		/*
+		 * compare, 10.3.1
+		 */
+		if(rd == 15 && op & (1<<20)){
+			rn = (op>>16)&7;
+			fn = &FR(rn);
+			if(op & (1<<3)){
+				fm = &fpconst[op&7];
+				tag = 'C';
+			}else{
+				fm = &FR(op&7);
+				tag = 'F';
+			}
+			switch((op>>21)&7){
+			default:
+				unimp(pc, op);
+			case 4:	/* CMF: Fn :: Fm */
+			case 6:	/* CMFE: Fn :: Fm (with exception) */
+				ur->psr &= ~(N|C|Z|V);
+				ur->psr |= fcmp(fn, fm);
+				break;
+			case 5:	/* CNF: Fn :: -Fm */
+			case 7:	/* CNFE: Fn :: -Fm (with exception) */
+				tmp = *fm;
+				tmp.s ^= 1;
+				ur->psr &= ~(N|C|Z|V);
+				ur->psr |= fcmp(fn, &tmp);
+				break;
+			}
+			if(fpemudebug)
+				print("CMPF	%c%d,F%ld =%x\n", tag, rn, op&7, ur->psr>>28);
+			return;
+		}
+
+		/*
+		 * other transfer, 10.3
+		 */
+		switch((op>>20)&0xF){
+		default:
+			unimp(pc, op);
+		case 0:	/* FLT */
+			rn = (op>>16) & 7;
+			fpiw2i(&FR(rn), &REG(rd));
+			if(fpemudebug)
+				print("MOVW[FD]	R%d, F%d\n", rd, rn);
+			break;
+		case 1:	/* FIX */
+			if(op & (1<<3))
+				unimp(pc, op);
+			rn = op & 7;
+			tmp = FR(rn);
+			fpii2w(&REG(rd), &tmp);
+			if(fpemudebug)
+				print("MOV[FD]W	F%d, R%d =%ld\n", rn, rd, REG(rd));
+			break;
+		case 2:	/* FPSR := Rd */
+			FPENV.status = REG(rd);
+			if(fpemudebug)
+				print("MOVW	R%d, FPSR\n", rd);
+			break;
+		case 3:	/* Rd := FPSR */
+			REG(rd) = FPENV.status;
+			if(fpemudebug)
+				print("MOVW	FPSR, R%d\n", rd);
+			break;
+		case 4:	/* FPCR := Rd */
+			FPENV.control = REG(rd);
+			if(fpemudebug)
+				print("MOVW	R%d, FPCR\n", rd);
+			break;
+		case 5:	/* Rd := FPCR */
+			REG(rd) = FPENV.control;
+			if(fpemudebug)
+				print("MOVW	FPCR, R%d\n", rd);
+			break;
+		}
+		return;
+	}
+
+	/*
+	 * arithmetic
+	 */
+
+	if(op & (1<<3)){	/* constant */
+		fm = &fpconst[op&7];
+		tag = 'C';
+	}else{
+		fm = &FR(op&7);
+		tag = 'F';
+	}
+	rd = (op>>12)&7;
+	o = (op>>20)&0xF;
+	if(op & (1<<15)){	/* monadic */
+		FP1 *fp;
+		fp = &optab1[o];
+		if(fp->f == nil)
+			unimp(pc, op);
+		if(fpemudebug)
+			print("%s	%c%ld,F%d\n", fp->name, tag, op&7, rd);
+		(*fp->f)(fm, &FR(rd));
+	} else {
+		FP2 *fp;
+		fp = &optab2[o];
+		if(fp->f == nil)
+			unimp(pc, op);
+		rn = (op>>16)&7;
+		if(fpemudebug)
+			print("%s	%c%ld,F%d,F%d\n", fp->name, tag, op&7, rn, rd);
+		(*fp->f)(*fm, FR(rn), &FR(rd));
+	}
+}
+
+/*
+ * returns the number of FP instructions emulated
+ */
+int
+fpiarm(Ureg *ur)
+{
+	ulong op, o;
+	FPenv *ufp;
+	int n;
+
+#ifndef R13OK
+/*	ur->type = &ur->pc+1;	/* calculate kernel sp/R13 and put it here for roff[13] */
+	ur->type = (ulong)(ur + 1);
+#endif
+	if (up == nil)
+		panic("fpiarm not in a process");
+	ufp = &up->env->fpu;	/* because all the state is in Osenv, it need not be saved/restored */
+	if(ufp->fpistate != FPACTIVE) {
+		ufp->fpistate = FPACTIVE;
+		ufp->control = 0;
+		ufp->status = (0x01<<28)|(1<<12);	/* software emulation, alternative C flag */
+		for(n = 0; n < 8; n++)
+			FR(n) = fpconst[0];
+	}
+	for(n=0;;n++){
+		op = getulong(ur->pc);
+		o = (op>>24) & 0xF;
+		if(((op>>8) & 0xF) != 1 || o != 0xE && (o&~1) != 0xC)
+			break;
+		if(condok(ur->psr, op>>28))
+			fpemu(ur->pc, op, ur, ufp);
+		ur->pc += 4;
+		if(anyhigher())
+			sched();
+	}
+	return n;
+}
--- /dev/null
+++ b/os/manga/gpio.c
@@ -1,0 +1,75 @@
+#include	"u.h"
+#include 	"mem.h"
+#include	"../port/lib.h"
+#include 	"dat.h"
+#include	"fns.h"
+#include	"io.h"
+
+static ulong gpioreserved;
+static Lock gpiolock;
+
+void
+gpioreserve(int n)
+{
+	ulong mask;
+
+	mask = 1<<n;
+	ilock(&gpiolock);
+	if(gpioreserved & mask)
+		panic("gpioreserve: duplicate use of GPIO %d", n);
+	gpioreserved |= mask;
+	iunlock(&gpiolock);
+}
+
+/*
+ * set direction and alternative function bits in the GPIO control register,
+ * following the configuration bits in cfg.
+ */
+void
+gpioconfig(int n, ulong cfg)
+{
+	GpioReg *g;
+
+	ilock(&gpiolock);
+	g = GPIOREG;
+	if(cfg & Gpio_out)
+		g->iopm |= 1<<n;
+	else
+		g->iopm &= ~(1<<n);
+	iunlock(&gpiolock);
+}
+
+ulong
+gpioget(int n)
+{
+	return GPIOREG->iopd & (1<<n);
+}
+
+void
+gpioset(int n, int v)
+{
+	GpioReg *g;
+	ulong mask;
+
+	mask = 1<<n;
+	ilock(&gpiolock);
+	g = GPIOREG;
+	if(v)
+		g->iopd |= mask;
+	else
+		g->iopd &= ~mask;
+	iunlock(&gpiolock);
+}
+
+void
+gpiorelease(int n)
+{
+	ulong mask;
+
+	mask = 1<<n;
+	ilock(&gpiolock);
+	if((gpioreserved & mask) != mask)
+		panic("gpiorelease: unexpected release of GPIO %d", n);
+	gpioreserved &= ~mask;
+	iunlock(&gpiolock);
+}
--- /dev/null
+++ b/os/manga/inb.c
@@ -1,0 +1,85 @@
+#include "u.h"
+#include "../port/lib.h"
+#include "mem.h"
+#include "dat.h"
+#include "fns.h"
+#include "io.h"
+#include "../port/error.h"
+
+#define	KIOP(port)	KADDR(PHYSPCIIO+(port))
+
+int
+inb(ulong p)
+{
+	return *(uchar*)KIOP(p);
+}
+
+int
+ins(ulong p)
+{
+	return *(ushort*)KIOP(p);
+}
+
+ulong
+inl(ulong p)
+{
+	return *(ulong*)KIOP(p);
+}
+
+void
+outb(ulong p, int v)
+{
+	*(uchar*)KIOP(p) = v;
+}
+
+void
+outs(ulong p, int v)
+{
+	*(ushort*)KIOP(p) = v;
+}
+
+void
+outl(ulong p, ulong v)
+{
+	*(ulong*)KIOP(p) = v;
+}
+
+void
+inss(ulong p, void* buf, int ns)
+{
+	ushort *addr;
+
+	addr = (ushort*)buf;
+	for(;ns > 0; ns--)
+		*addr++ = *(ushort*)KIOP(p);
+}
+
+void
+outss(ulong p, void* buf, int ns)
+{
+	ushort *addr;
+
+	addr = (ushort*)buf;
+	for(;ns > 0; ns--)
+		*(ushort*)KIOP(p) = *addr++;
+}
+
+void
+insb(ulong p, void* buf, int ns)
+{
+	uchar *addr;
+
+	addr = (uchar*)buf;
+	for(;ns > 0; ns--)
+		*addr++ = *(uchar*)KIOP(p);
+}
+
+void
+outsb(ulong p, void* buf, int ns)
+{
+	uchar *addr;
+
+	addr = (uchar*)buf;
+	for(;ns > 0; ns--)
+		*(uchar*)KIOP(p) = *addr++;
+}
--- /dev/null
+++ b/os/manga/io.h
@@ -1,0 +1,320 @@
+typedef struct BD BD;
+typedef struct Ring Ring;
+
+/*
+ *  types of interrupts
+ */
+enum
+{
+	/* some flags to change polarity and sensitivity */
+	IRQmask=		0xFF,	/* actual vector address */
+	IRQactivelow=	0<<8,
+	IRQactivehigh=	1<<8,
+	IRQrising=	2<<8,
+	IRQfalling=	4<<8,
+	IRQmode=	IRQactivelow | IRQactivehigh | IRQrising | IRQfalling,
+	IRQsoft=	1<<11,	/* configure ext0 to ext3 as GPIO output */
+	IRQ=	0,	/* notional bus */
+};
+
+enum {
+	IRQwmlc=	31,	/* WAN link changed (edge) */
+	IRQwmts=	30,	/* WAN MAC transmit status (edge) */
+	IRQwmrs=	29,	/* WAN MAC receive status (edge) */
+	IRQwmtbu=	28,	/* WAN MAC transmit buffer unavailable (edge) */
+	IRQwmrbu=	27,	/* WAN MAC receive buffer unavailable (edge) */
+	IRQwmtps=	26,	/* WAN MAC transmit process stopped (edge) */
+	IRQwmrps=	25,	/* WAN MAC receive process stopped (edge) */
+	IRQaber=	24,		/* AMBA bus error (level) */
+	IRQlmts=	17,		/* LAN MAC transmit status (edge) */
+	IRQlmrs=	16,		/* LAN MAC receive status (edge) */
+	IRQlmtbu=	15,	/* LAN AMC transmit buffer unavailable (edge) */
+	IRQlmrbu=	14,	/* LAN MAC receive buffer unavailable (edge) */
+	IRQlmtps=	13,	/* LAN MAC transmit process stopped (edge) */
+	IRQlmrps=	12,	/* LAN MAC receive process stopped (edge) */
+	IRQums=	11,		/* UART modem status (level) */
+	IRQule=	10,		/* UART line status (level) */
+	IRQurs=	9,		/* UART receive status (level) */
+	IRQuts=	8,		/* UART transmit status (level) */
+	IRQtm1=	7,		/* timer 1 (edge) */
+	IRQtm0=	6,		/* timer 0 (edge) */
+	IRQext3=	5,		/* external interrupts (gpio control selects edge or level) */
+	IRQext2=	4,
+	IRQext1=	3,
+	IRQext0=	2,
+	IRQccts=	1,		/* comms channel transmit status (level) */
+	IRQccrs=	0,		/* comms channel receive status (level) */
+};
+
+/*
+ * these are defined to keep the interface compatible with other
+ * architectures, but only BUSUNKNOWN is currently used
+ */
+#define MKBUS(t,b,d,f)	(((t)<<24)|(((b)&0xFF)<<16)|(((d)&0x1F)<<11)|(((f)&0x07)<<8))
+#define BUSFNO(tbdf)	(((tbdf)>>8)&0x07)
+#define BUSDNO(tbdf)	(((tbdf)>>11)&0x1F)
+#define BUSBNO(tbdf)	(((tbdf)>>16)&0xFF)
+#define BUSTYPE(tbdf)	((tbdf)>>24)
+#define BUSBDF(tbdf)	((tbdf)&0x00FFFF00)
+#define BUSUNKNOWN	(-1)
+
+enum {
+	BusIRQ = IRQ,
+	BusPCI,
+	MaxBus
+};
+
+#define INTRREG 	((IntrReg*)PHYSINTR)
+typedef struct IntrReg IntrReg;
+struct IntrReg {
+	ulong	mc;	/* mode control */
+	ulong	en;	/* enable */
+	ulong	st;		/* status */
+	ulong	pw;	/* priority for WAN */
+	ulong	pad0;
+	ulong	pl;		/* priority for LAN */
+	ulong	pt;	/* priority for timer */
+	ulong	pu;	/* priority for UART */
+	ulong	pe;	/* priority for external */
+	ulong	pc;	/* priority for comms channel */
+	ulong	pbe;	/* priority for bus error response */
+	ulong	ms;	/* mask status */
+	ulong	hpf;	/* highest priority for FIQ */
+	ulong	hpi;	/* highest priority for IRQ */
+};
+
+#define TIMERREG	((TimerReg*)PHYSTIMER)
+typedef struct TimerReg TimerReg;
+struct TimerReg {
+	ulong	enable;	/* 1<<n to enable timer n */
+	ulong	count1;
+	ulong	count0;	/* 0 becomes watchdog if byte 0 is 0xFF */
+	ulong	pulse1;
+	ulong	pulse0;
+};
+
+#define GPIOREG		((GpioReg*)PHYSGPIO)
+typedef struct GpioReg GpioReg;
+struct GpioReg {
+	ulong	iopm;	/* mode (1=output) */
+	ulong	iopc;		/* control */
+	ulong	iopd;		/* data */
+};
+
+enum {
+	/* WLAN and BT values are probably wrong */
+	GPIO_WLAN_act_o=	7,
+	GPIO_WLAN_100_o=	8,
+	GPIO_BT_act_o=	9,
+	GPIO_BT_100_o=	10,
+	GPIO_status_orange_o=	11,
+	GPIO_status_green_o=	12,
+	GPIO_button_i=	15,	/* reset button, active low */
+	GPIO_misc_mask_o=	(1<<13)|(1<<14)|(1<<15)|(1<<4)|(1<<5)|(1<<6),	/* no idea */
+};
+
+void	gpioreserve(int);
+void	gpioconfig(int, ulong);
+ulong	gpioget(int);
+void	gpioset(int, int);
+void	gpiorelease(int);
+
+enum {
+	/* software configuration bits for gpioconfig */
+	Gpio_in=		0<<4,
+	Gpio_out=	1<<4,
+};
+
+/*
+ * Host Communication buffer descriptors
+ */
+
+struct BD  {
+	ulong	ctrl;		/* BdBusy and rx flags */
+	ulong	size;		/* buffer size, also BdLast and tx flags */
+	ulong	addr;
+	ulong	next;		/* next descriptor address */
+};
+
+enum {
+	/* ctrl */
+	BdBusy=	1<<31,	/* device owns it */
+
+	RxFS=	1<<30,	/* first buffer of frame */
+	RxLS=	1<<29,	/* last buffer of frame */
+	RxIPE=	1<<28,	/* IP checksum error */
+	RxTCPE=	1<<27,	/* TCP checksum error */
+	RxUDPE=	1<<26,	/* UDP checksum error */
+	RxES=	1<<25,	/* error summary */
+	RxMF=	1<<24,	/* multicast */
+	RxRE=	1<<19,	/* physical level reported error */
+	RxTL=	1<<18,	/* frame too long */
+	RxRF=	1<<17,	/* runt */
+	RxCE=	1<<16,	/* CRC error */
+	RxFT=	1<<15,	/* =0, Ether; =1, 802.3 */
+	RxFL=	0x7FF,	/* frame length */
+
+	/* size and tx flags */
+	BdWrap=	1<<25,	/* wrap to base of ring */
+	TxIC=	1<<31,	/* interrupt on completion */
+	TxFS=	1<<30,	/* first segment */
+	TxLS=	1<<29,	/* last segment */
+	TxIPG=	1<<28,	/* generate IP checksum */
+	TxTCPG=	1<<27,	/* generate tcp/ip checksum */
+	TxUDPG=	1<<26,	/* generate udp/ip checksum */
+};
+
+BD*	bdalloc(ulong);
+void	bdfree(BD*, int);
+void	dumpbd(char*, BD*, int);
+
+struct Ring {
+	BD*	rdr;				/* receive descriptor ring */
+	Block**	rxb;			/* receive ring buffers */
+	int	rdrx;				/* index into rdr */
+	int	nrdre;			/* length of rdr */
+
+	BD*	tdr;				/* transmit descriptor ring */
+	Block**	txb;			/* transmit ring buffers */
+	int	tdrh;				/* host index into tdr */
+	int	tdri;				/* interface index into tdr */
+	int	ntdre;			/* length of tdr */
+	int	ntq;				/* pending transmit requests */
+};
+
+#define NEXT(x, l)	(((x)+1)%(l))
+#define PREV(x, l)	(((x) == 0) ? (l)-1: (x)-1)
+#define	HOWMANY(x, y)	(((x)+((y)-1))/(y))
+#define ROUNDUP(x, y)	(HOWMANY((x), (y))*(y))
+
+int	ioringinit(Ring*, int, int);
+
+enum {
+	/*  DMA configuration parameters */
+
+	 /*  DMA Direction */
+	DmaOut=		0,
+	DmaIn=		1,
+};
+
+/*
+ * PCI support code.
+ */
+enum {					/* type 0 and type 1 pre-defined header */
+	PciVID		= 0x00,		/* vendor ID */
+	PciDID		= 0x02,		/* device ID */
+	PciPCR		= 0x04,		/* command */
+	PciPSR		= 0x06,		/* status */
+	PciRID		= 0x08,		/* revision ID */
+	PciCCRp		= 0x09,		/* programming interface class code */
+	PciCCRu		= 0x0A,		/* sub-class code */
+	PciCCRb		= 0x0B,		/* base class code */
+	PciCLS		= 0x0C,		/* cache line size */
+	PciLTR		= 0x0D,		/* latency timer */
+	PciHDT		= 0x0E,		/* header type */
+	PciBST		= 0x0F,		/* BIST */
+
+	PciBAR0		= 0x10,		/* base address */
+	PciBAR1		= 0x14,
+
+	PciINTL		= 0x3C,		/* interrupt line */
+	PciINTP		= 0x3D,		/* interrupt pin */
+};
+
+enum {					/* type 0 pre-defined header */
+	PciBAR2		= 0x18,
+	PciBAR3		= 0x1C,
+	PciBAR4		= 0x20,
+	PciBAR5		= 0x24,
+	PciCIS		= 0x28,		/* cardbus CIS pointer */
+	PciSVID		= 0x2C,		/* subsystem vendor ID */
+	PciSID		= 0x2E,		/* cardbus CIS pointer */
+	PciEBAR0	= 0x30,		/* expansion ROM base address */
+	PciMGNT		= 0x3E,		/* burst period length */
+	PciMLT		= 0x3F,		/* maximum latency between bursts */
+};
+
+enum {					/* type 1 pre-defined header */
+	PciPBN		= 0x18,		/* primary bus number */
+	PciSBN		= 0x19,		/* secondary bus number */
+	PciUBN		= 0x1A,		/* subordinate bus number */
+	PciSLTR		= 0x1B,		/* secondary latency timer */
+	PciIBR		= 0x1C,		/* I/O base */
+	PciILR		= 0x1D,		/* I/O limit */
+	PciSPSR		= 0x1E,		/* secondary status */
+	PciMBR		= 0x20,		/* memory base */
+	PciMLR		= 0x22,		/* memory limit */
+	PciPMBR		= 0x24,		/* prefetchable memory base */
+	PciPMLR		= 0x26,		/* prefetchable memory limit */
+	PciPUBR		= 0x28,		/* prefetchable base upper 32 bits */
+	PciPULR		= 0x2C,		/* prefetchable limit upper 32 bits */
+	PciIUBR		= 0x30,		/* I/O base upper 16 bits */
+	PciIULR		= 0x32,		/* I/O limit upper 16 bits */
+	PciEBAR1	= 0x28,		/* expansion ROM base address */
+	PciBCR		= 0x3E,		/* bridge control register */
+};
+
+enum {					/* type 2 pre-defined header */
+	PciCBExCA	= 0x10,
+	PciCBSPSR	= 0x16,
+	PciCBPBN	= 0x18,		/* primary bus number */
+	PciCBSBN	= 0x19,		/* secondary bus number */
+	PciCBUBN	= 0x1A,		/* subordinate bus number */
+	PciCBSLTR	= 0x1B,		/* secondary latency timer */
+	PciCBMBR0	= 0x1C,
+	PciCBMLR0	= 0x20,
+	PciCBMBR1	= 0x24,
+	PciCBMLR1	= 0x28,
+	PciCBIBR0	= 0x2C,		/* I/O base */
+	PciCBILR0	= 0x30,		/* I/O limit */
+	PciCBIBR1	= 0x34,		/* I/O base */
+	PciCBILR1	= 0x38,		/* I/O limit */
+	PciCBSVID	= 0x40,		/* subsystem vendor ID */
+	PciCBSID	= 0x42,		/* subsystem ID */
+	PciCBLMBAR	= 0x44,		/* legacy mode base address */
+};
+
+typedef struct Pcisiz Pcisiz;
+struct Pcisiz
+{
+	Pcidev*	dev;
+	int	siz;
+	int	bar;
+};
+
+typedef struct Pcidev Pcidev;
+struct Pcidev
+{
+	int	tbdf;			/* type+bus+device+function */
+	ushort	vid;			/* vendor ID */
+	ushort	did;			/* device ID */
+
+	uchar	rid;
+	uchar	ccrp;
+	uchar	ccru;
+	uchar	ccrb;
+
+	struct {
+		ulong	bar;		/* base address */
+		int	size;
+	} mem[6];
+
+	struct {
+		ulong	bar;	
+		int	size;
+	} rom;
+	uchar	intl;			/* interrupt line */
+
+	Pcidev*	list;
+	Pcidev*	link;			/* next device on this bno */
+
+	Pcidev*	bridge;			/* down a bus */
+	struct {
+		ulong	bar;
+		int	size;
+	} ioa, mema;
+	ulong	pcr;
+};
+
+#define PCIWINDOW	0x80000000
+#define PCIWADDR(va)	(PADDR(va)+PCIWINDOW)
--- /dev/null
+++ b/os/manga/ioring.c
@@ -1,0 +1,72 @@
+#include "u.h"
+#include "../port/lib.h"
+#include "mem.h"
+#include "dat.h"
+#include "fns.h"
+#include "io.h"
+
+/*
+ * Host Communication buffer rings
+ */
+
+/*
+ * initialise receive and transmit buffer rings
+ *
+ * the ring entries must be uncached
+ */
+
+BD*
+bdalloc(ulong nd)
+{
+	BD *b;
+
+	b = xspanalloc(nd*sizeof(*b), CACHELINESZ, 0);
+	if(b == nil)
+		panic("bdalloc");
+	return mmucacheinhib(b, nd*sizeof(*b));
+}
+
+int
+ioringinit(Ring* r, int nrdre, int ntdre)
+{
+	int i;
+
+	r->nrdre = nrdre;
+	if(r->rdr == nil)
+		r->rdr = bdalloc(nrdre);
+	if(r->rxb == nil)
+		r->rxb = malloc(nrdre*sizeof(Block*));
+	if(r->rdr == nil || r->rxb == nil)
+		return -1;
+	for(i = 0; i < nrdre; i++){
+		r->rxb[i] = nil;
+		r->rdr[i].ctrl = 0;
+		r->rdr[i].size = 0;
+		r->rdr[i].addr = 0;
+		if(i)
+			r->rdr[i-1].next = PADDR(&r->rdr[i]);
+	}
+	r->rdr[i-1].next = PADDR(&r->rdr[0]);
+	r->rdrx = 0;
+
+	r->ntdre = ntdre;
+	if(r->tdr == nil)
+		r->tdr = bdalloc(ntdre);
+	if(r->txb == nil)
+		r->txb = malloc(ntdre*sizeof(Block*));
+	if(r->tdr == nil || r->txb == nil)
+		return -1;
+	for(i = 0; i < ntdre; i++){
+		r->txb[i] = nil;
+		r->tdr[i].ctrl = 0;
+		r->tdr[i].size = 0;
+		r->tdr[i].addr = 0;
+		if(i)
+			r->tdr[i-1].next = PADDR(&r->tdr[i]);
+	}
+	r->tdr[i-1].next = PADDR(&r->tdr[0]);
+	r->tdrh = 0;
+	r->tdri = 0;
+	r->ntq = 0;
+	return 0;
+}
--- /dev/null
+++ b/os/manga/l.s
@@ -1,0 +1,404 @@
+#include "mem.h"
+
+#define	CPWAIT
+
+/*
+ * Entered here from the boot loader with
+ *	supervisor mode, interrupts disabled;
+ *	MMU and caches disabled
+ */
+
+#define	LED	\
+	MOVW	$(PHYSGPIO+8), R6;\
+	MOVW	(R6), R7;\
+	EOR	$(1<<12), R7;\
+	MOVW	R7, (R6)
+
+TEXT _startup(SB), $-4
+	MOVW		$setR12(SB), R12 	/* static base (SB) */
+	MOVW		$(PsrDirq|PsrDfiq|PsrMsvc), R1	/* ensure SVC mode with interrupts disabled */
+	MOVW		R1, CPSR
+
+	/* build a temporary translation table at 4MB */
+	MOVW	$0x400000, R0
+	MCR	CpMMU, 0, R0, C(CpTTB), C(0), 0	/* set TTB */
+	MOVW	$4096, R1
+	MOVW	$0, R3
+	ORR	$(1<<4), R3	/* must be one */
+	ORR	$(3<<10), R3	/* supervisor rw */
+	ORR	$(2<<0), R3	/* section */
+startup0:
+	BIC	$0xFC000000, R3	/* wraps round, at least for 0xC00... */
+	MOVW	R3, (R0)
+	ADD	$4, R0
+	ADD	$(1<<20), R3
+	SUB	$1, R1
+	CMP	$0, R1
+	BNE	startup0
+	MRC		CpMMU, 0, R0, C(CpControl), C(0), 0
+	ORR	$CpCmmu, R0
+
+	MOVW	$3, R1
+	MCR	CpMMU, 0, R1, C(CpDAC), C(0)	/* set domain 0 to manager */
+	BL	mmuenable(SB)
+
+	MOVW		$(MACHADDR+BY2PG-4), R13	/* stack; 4 bytes for link */
+	BL	_relocate(SB)
+	BL		main(SB)
+dead:
+	B	dead
+	BL	_div(SB)			/* hack to get _div etc loaded */
+
+TEXT _relocate(SB), $-4
+	ORR	$KZERO, R14
+	RET
+
+TEXT	getcpuid(SB), $-4
+	MRC		CpMMU, 0, R0, C(CpCPUID), C(0)
+	RET
+
+TEXT	getcacheid(SB), $-4
+	MRC		CpMMU, 0, R0, C(CpCacheID), C(1)
+	RET
+
+TEXT mmugetctl(SB), $-4
+	MRC		CpMMU, 0, R0, C(CpControl), C(0)
+	RET	
+
+TEXT	mmugetdac(SB), $-4
+	MRC		CpMMU, 0, R0, C(CpDAC), C(0)
+	RET
+
+TEXT	mmugetfar(SB), $-4
+	MRC		CpMMU, 0, R0, C(CpFAR), C(0)
+	RET
+
+TEXT	mmugetfsr(SB), $-4
+	MRC		CpMMU, 0, R0, C(CpFSR), C(0)
+	RET
+
+TEXT	mmuputdac(SB), $-4
+	MCR		CpMMU, 0, R0, C(CpDAC), C(0)
+	CPWAIT
+	RET
+
+TEXT	mmuputfsr(SB), $-4
+	MCR		CpMMU, 0, R0, C(CpFSR), C(0)
+	CPWAIT
+	RET
+
+TEXT	mmuputttb(SB), $-4
+	MCR		CpMMU, 0, R0, C(CpTTB), C(0)
+	CPWAIT
+	RET
+
+TEXT mmuputctl(SB), $-4
+	MCR		CpMMU, 0, R0, C(CpControl), C(0)
+
+	/* drain prefetch */
+	MOVW	R0,R0						
+	MOVW	R0,R0
+	RET	
+
+TEXT tlbinvalidateall(SB), $-4
+	MCR		CpMMU, 0, R0, C(CpTLBops), C(7)
+	CPWAIT
+	RET
+
+TEXT itlbinvalidate(SB), $-4
+	MCR		CpMMU, 0, R0, C(CpTLBops), C(5), 1
+	CPWAIT
+	RET
+
+TEXT dtlbinvalidate(SB), $-4
+	MCR		CpMMU, 0, R0, C(CpTLBops), C(6), 1
+	CPWAIT
+	RET
+
+TEXT mmuenable(SB), $-4
+
+	/* disable and invalidate all caches and TLB's before enabling MMU */
+	MCR		CpMMU, 0, R1, C(CpControl), C(0)
+	BIC	$(CpCDcache | CpCIcache), R1
+	MRC		CpMMU, 0, R1, C(CpControl), C(0)
+	CPWAIT
+
+	MOVW	$0, R1				/* disable everything */
+	MCR	CpMMU, 0, R1, C(CpCacheCtl), C(7), 0	/* invalidate I&D Caches and BTB */
+	MCR	CpMMU, 0, R1, C(CpCacheCtl), C(10), 4	/* drain write buffer */
+	MCR	CpMMU, 0, R1, C(CpTLBops), C(7), 0	/* invalidate I&D TLB */
+
+	/* enable desired mmu mode (R0) */
+	MCR	CpMMU, 0, R0, C(CpControl), C(0)
+
+	/* drain prefetch */
+	MOVW	R0,R0						
+	MOVW	R0,R0
+	RET				/* start running in remapped area */
+
+TEXT setr13(SB), $-4
+	MOVW		4(FP), R1
+
+	MOVW		CPSR, R2
+	BIC		$PsrMask, R2, R3
+	ORR		R0, R3
+	MOVW		R3, CPSR
+
+	MOVW		R13, R0
+	MOVW		R1, R13
+
+	MOVW		R2, CPSR
+	RET
+
+TEXT vectors(SB), $-4
+	MOVW	0x18(R15), R15			/* reset */
+	MOVW	0x18(R15), R15			/* undefined */
+	MOVW	0x18(R15), R15			/* SWI */
+	MOVW	0x18(R15), R15			/* prefetch abort */
+	MOVW	0x18(R15), R15			/* data abort */
+	MOVW	0x18(R15), R15			/* reserved */
+	MOVW	0x18(R15), R15			/* IRQ */
+	MOVW	0x18(R15), R15			/* FIQ */
+
+TEXT vtable(SB), $-4
+	WORD	$_vsvc(SB)			/* reset, in svc mode already */
+	WORD	$_vund(SB)			/* undefined, switch to svc mode */
+	WORD	$_vsvc(SB)			/* swi, in svc mode already */
+	WORD	$_vpab(SB)			/* prefetch abort, switch to svc mode */
+	WORD	$_vdab(SB)			/* data abort, switch to svc mode */
+	WORD	$_vsvc(SB)			/* reserved */
+	WORD	$_virq(SB)			/* IRQ, switch to svc mode */
+	WORD	$_vfiq(SB)			/* FIQ, switch to svc mode */
+
+TEXT _vund(SB), $-4			
+	MOVM.DB		[R0-R3], (R13)
+	MOVW		$PsrMund, R0
+	B		_vswitch
+
+TEXT _vsvc(SB), $-4				
+	MOVW.W		R14, -4(R13)
+	MOVW		CPSR, R14
+	MOVW.W		R14, -4(R13)
+	BIC		$PsrMask, R14
+	ORR		$(PsrDirq|PsrDfiq|PsrMsvc), R14
+	MOVW		R14, CPSR
+	MOVW		$PsrMsvc, R14
+	MOVW.W		R14, -4(R13)
+	B		_vsaveu
+
+TEXT _vpab(SB), $-4			
+	MOVM.DB		[R0-R3], (R13)
+	MOVW		$PsrMabt, R0
+	B		_vswitch
+
+TEXT _vdab(SB), $-4	
+	MOVM.DB		[R0-R3], (R13)
+	MOVW		$(PsrMabt+1), R0
+	B		_vswitch
+
+TEXT _vfiq(SB), $-4				/* FIQ */
+	MOVM.DB		[R0-R3], (R13)
+	MOVW		$PsrMfiq, R0
+	B		_vswitch
+
+TEXT _virq(SB), $-4				/* IRQ */
+	MOVM.DB		[R0-R3], (R13)
+	MOVW		$PsrMirq, R0
+
+_vswitch:					/* switch to svc mode */
+	MOVW		SPSR, R1
+	MOVW		R14, R2
+	MOVW		R13, R3
+
+	MOVW		CPSR, R14
+	BIC		$PsrMask, R14
+	ORR		$(PsrDirq|PsrDfiq|PsrMsvc), R14
+	MOVW		R14, CPSR
+
+	MOVM.DB.W 	[R0-R2], (R13)
+	MOVM.DB	  	(R3), [R0-R3]
+
+_vsaveu:						/* Save Registers */
+	MOVW.W		R14, -4(R13)			/* save link */
+	MCR		CpMMU, 0, R0, C(0), C(0), 0	
+
+	SUB		$8, R13
+	MOVM.DB.W 	[R0-R12], (R13)
+
+	MOVW		R0, R0				/* gratuitous noop */
+
+	MOVW		$setR12(SB), R12		/* static base (SB) */
+	MOVW		R13, R0				/* argument is ureg */
+	SUB		$8, R13				/* space for arg+lnk*/
+	BL		trap(SB)
+
+_vrfe:							/* Restore Regs */
+	MOVW		CPSR, R0			/* splhi on return */
+	ORR		$(PsrDirq|PsrDfiq), R0, R1
+	MOVW		R1, CPSR
+	ADD		$(8+4*15), R13		/* [r0-R14]+argument+link */
+	MOVW		(R13), R14			/* restore link */
+	MOVW		8(R13), R0
+	MOVW		R0, SPSR
+	MOVM.DB.S 	(R13), [R0-R14]		/* restore user registers */
+	MOVW		R0, R0				/* gratuitous nop */
+	ADD		$12, R13		/* skip saved link+type+SPSR*/
+	RFE					/* MOVM.IA.S.W (R13), [R15] */
+	
+TEXT splhi(SB), $-4					
+	MOVW		CPSR, R0
+	ORR		$(PsrDirq), R0, R1
+	MOVW		R1, CPSR
+	MOVW	$(MACHADDR), R6
+	MOVW	R14, (R6)	/* m->splpc */
+	RET
+
+TEXT spllo(SB), $-4
+	MOVW		CPSR, R0
+	BIC		$(PsrDirq|PsrDfiq), R0, R1
+	MOVW		R1, CPSR
+	RET
+
+TEXT splx(SB), $-4
+	MOVW	$(MACHADDR), R6
+	MOVW	R14, (R6)	/* m->splpc */
+
+TEXT splxpc(SB), $-4
+	MOVW		R0, R1
+	MOVW		CPSR, R0
+	MOVW		R1, CPSR
+	RET
+
+TEXT spldone(SB), $-4
+	RET
+
+TEXT islo(SB), $-4
+	MOVW		CPSR, R0
+	AND		$(PsrDirq), R0
+	EOR		$(PsrDirq), R0
+	RET
+
+TEXT splfhi(SB), $-4					
+	MOVW		CPSR, R0
+	ORR		$(PsrDfiq|PsrDirq), R0, R1
+	MOVW		R1, CPSR
+	RET
+
+TEXT splflo(SB), $-4
+	MOVW		CPSR, R0
+	BIC		$(PsrDfiq), R0, R1
+	MOVW		R1, CPSR
+	RET
+
+TEXT getcpsr(SB), $-4
+	MOVW		CPSR, R0
+	RET
+
+TEXT getspsr(SB), $-4
+	MOVW		SPSR, R0
+	RET
+
+TEXT getcallerpc(SB), $-4
+	MOVW		0(R13), R0
+	RET
+
+TEXT _tas(SB), $-4
+	MOVW		R0, R1
+	MOVW		$0xDEADDEAD, R2
+	SWPW		R2, (R1), R0
+	RET
+
+TEXT setlabel(SB), $-4
+	MOVW		R13, 0(R0)		/* sp */
+	MOVW		R14, 4(R0)		/* pc */
+	MOVW		$0, R0
+	RET
+
+TEXT gotolabel(SB), $-4
+	MOVW		0(R0), R13		/* sp */
+	MOVW		4(R0), R14		/* pc */
+	MOVW		$1, R0
+	RET
+
+/*
+ * flush (invalidate) the whole icache
+ */
+TEXT icflushall(SB), $-4
+_icflushall:
+	MCR	 	CpMMU, 0, R0, C(CpCacheCtl), C(5), 0	/* invalidate i-cache */
+	CPWAIT
+	RET
+
+/*
+ * invalidate part of i-cache
+ */
+TEXT	icflush(SB), $-4
+	MOVW	4(FP), R1
+	CMP		$(CACHESIZE/2), R1
+	BGE		_icflushall		/* might as well do the lot */
+	ADD		R0, R1
+	BIC		$(CACHELINESZ-1), R0
+icflush1:
+	MCR		CpMMU, 0, R0, C(CpCacheCtl), C(5), 1	/* invalidate entry by address */
+	ADD		$CACHELINESZ, R0
+	CMP		R1, R0
+	BLO	icflush1
+	RET
+
+/*
+ * write back whole data cache, invalidate, and drain write buffer
+ */
+TEXT dcflushall(SB), $-4
+_dcflushall:
+	MOVW	$(63<<26), R1	/* index, segment 0 */
+dcflushall0:
+	MCR	CpMMU, 0, R1, C(CpCacheCtl), C(14), 2	/* clean and invalidate, using index */
+	ADD	$(1<<5), R1	/* segment 1 */
+	MCR	CpMMU, 0, R1, C(CpCacheCtl), C(14), 2
+	ADD	$(1<<5), R1	/* segment 2 */
+	MCR	CpMMU, 0, R1, C(CpCacheCtl), C(14), 2
+	ADD	$(1<<5), R1	/* segment 3 */
+	MCR	CpMMU, 0, R1, C(CpCacheCtl), C(14), 2
+	EOR	$(3<<5), R1	/* back to 0 */
+	SUB.S	$(1<<26), R1
+	BCS	dcflushall0
+	MCR		CpMMU, 0, R0, C(CpCacheCtl), C(10), 4	/* drain write buffer */
+	CPWAIT
+	RET
+
+/*
+ * write back a given region, inavlidate it, and drain write buffer
+ */
+TEXT	dcflush(SB), $-4
+	MOVW	4(FP), R1
+	CMP		$(CACHESIZE/2), R1
+	BGE		_dcflushall
+	ADD		R0, R1
+	BIC		$(CACHELINESZ-1), R0
+dcflush1:
+	MCR		CpMMU, 0, R0, C(CpCacheCtl), C(14), 1	/* clean and invalidate entry by address */
+	ADD		$CACHELINESZ, R0
+	CMP		R1, R0
+	BLO	dcflush1
+	MCR		CpMMU, 0, R0, C(CpCacheCtl), C(10), 4	/* drain write buffer */
+	CPWAIT
+	RET
+
+/*
+ * invalidate data cache
+ */
+TEXT dcinval(SB), $-4
+	MCR		CpMMU, 0, R0, C(CpCacheCtl), C(6), 0
+	CPWAIT
+	RET
+
+/* for devboot */
+TEXT	gotopc(SB), $-4
+	MOVW	R0, R1
+	MOVW	$0, R0
+	MOVW	R1, PC
+	RET
+
+TEXT	idle(SB), $-4
+	MCR		CpMMU, 0, R0, C(7), C(0), 4
+	RET
--- /dev/null
+++ b/os/manga/main.c
@@ -1,0 +1,317 @@
+#include "u.h"
+#include "../port/lib.h"
+#include "mem.h"
+#include "dat.h"
+#include "fns.h"
+#include "../port/error.h"
+#include "io.h"
+#include "version.h"
+
+#define	MAXCONF		32
+
+Mach *m = (Mach*)MACHADDR;
+Proc *up = 0;
+Vectorpage	*page0 = (Vectorpage*)KZERO;	/* doubly-mapped to AIVECADDR */
+Conf conf;
+
+extern ulong kerndate;
+extern int cflag;
+extern int main_pool_pcnt;
+extern int heap_pool_pcnt;
+extern int image_pool_pcnt;
+ulong cpuidlecount;
+
+char *confname[MAXCONF];
+char *confval[MAXCONF];
+int nconf;
+
+void addconf(char *, char *);
+void	eepromscan(void);
+char*	getconf(char*);
+
+void
+doc(char *m)
+{
+	USED(m);
+	print("%s...\n", m);
+}
+
+void
+idoc(char *m)
+{
+	serialputs(m, strlen(m)); //xdelay(1);
+}
+
+static void
+poolsizeinit(void)
+{
+	ulong nb;
+
+	nb = conf.npage*BY2PG;
+	poolsize(mainmem, (nb*main_pool_pcnt)/100, 0);
+	poolsize(heapmem, (nb*heap_pool_pcnt)/100, 0);
+	poolsize(imagmem, (nb*image_pool_pcnt)/100, 1);
+}
+
+static char *hello = "Inferno\n";
+
+void
+main(void)
+{
+	memset(edata, 0, end-edata);		/* clear the BSS */
+	memset(m, 0, sizeof(Mach));	/* clear the mach struct */
+	conf.nmach = 1;
+	archreset();
+	idoc(hello);
+	/* TO DO: clock speed */
+	quotefmtinstall();
+	idoc("confinit...\n");
+	confinit();
+	idoc("xinit...\n");
+	xinit();
+	idoc("mmuinit...\n");
+	mmuinit();
+	poolsizeinit();
+	poolinit();
+	idoc("trapinit...\n");
+	trapinit();
+//	dmareset();
+	idoc("printinit...\n");
+	printinit();
+	idoc("uartconsole...\n");
+	uartconsole();
+	eepromscan();
+	pcimapinit();
+	doc("clockinit");
+	clockinit();
+	doc("procinit");
+	procinit();
+//	cpuidprint();
+	doc("links");
+	links();
+	doc("chandevreset");
+	chandevreset();
+iprint("delayloop = %lud\n", m->delayloop);
+
+	eve = strdup("inferno");
+
+	kbdinit();
+
+	print("%ld MHz id %8.8lux\n", (m->cpuhz+500000)/1000000, getcpuid());
+	print("\nInferno %s\n", VERSION);
+	print("Vita Nuova\n");
+	print("conf %s (%lud) jit %d\n\n",conffile, kerndate, cflag);
+
+	userinit();
+	schedinit();
+}
+
+void
+reboot(void)
+{
+	exit(0);
+}
+
+void
+halt(void)
+{
+	spllo();
+	print("cpu halted\n");
+	for(;;){
+		/* nothing to do */
+	}
+}
+
+Conf	conf;
+
+void
+addconf(char *name, char *val)
+{
+	if(nconf >= MAXCONF)
+		return;
+	confname[nconf] = name;
+	confval[nconf] = val;
+	nconf++;
+}
+
+char*
+getconf(char *name)
+{
+	int i;
+
+	for(i = 0; i < nconf; i++)
+		if(cistrcmp(confname[i], name) == 0)
+			return confval[i];
+	return 0;
+}
+
+void
+confinit(void)
+{
+	ulong base;
+
+	archconfinit();
+
+	base = PGROUND((ulong)end);
+	conf.base0 = base;
+
+	conf.base1 = 0;
+	conf.npage1 = 0;
+
+	conf.npage0 = (conf.topofmem - base)/BY2PG;
+
+	conf.npage = conf.npage0 + conf.npage1;
+	conf.ialloc = (((conf.npage*(main_pool_pcnt))/100)/2)*BY2PG;
+
+	conf.nproc = 100 + ((conf.npage*BY2PG)/MB)*5;
+	conf.nmach = 1;
+
+}
+
+void
+init0(void)
+{
+	Osenv *o;
+	char buf[2*KNAMELEN];
+
+	up->nerrlab = 0;
+
+	spllo();
+
+	if(waserror())
+		panic("init0 %r");
+	/*
+	 * These are o.k. because rootinit is null.
+	 * Then early kproc's will have a root and dot.
+	 */
+	o = up->env;
+	o->pgrp->slash = namec("#/", Atodir, 0, 0);
+	cnameclose(o->pgrp->slash->name);
+	o->pgrp->slash->name = newcname("/");
+	o->pgrp->dot = cclone(o->pgrp->slash);
+
+	chandevinit();
+
+	if(!waserror()){
+		ksetenv("cputype", "arm", 0);
+		snprint(buf, sizeof(buf), "arm %s", conffile);
+		ksetenv("terminal", buf, 0);
+		poperror();
+	}
+
+	poperror();
+
+	disinit("/osinit.dis");
+}
+
+void
+userinit(void)
+{
+	Proc *p;
+	Osenv *o;
+
+	p = newproc();
+	o = p->env;
+
+	o->fgrp = newfgrp(nil);
+	o->pgrp = newpgrp();
+	o->egrp = newegrp();
+	kstrdup(&o->user, eve);
+
+	strcpy(p->text, "interp");
+
+	p->fpstate = FPINIT;
+
+	/*
+	 * Kernel Stack
+	 *
+	 * N.B. The -12 for the stack pointer is important.
+	 *	4 bytes for gotolabel's return PC
+	 */
+	p->sched.pc = (ulong)init0;
+	p->sched.sp = (ulong)p->kstack+KSTACK-8;
+
+	ready(p);
+}
+
+void
+exit(int inpanic)
+{
+	up = 0;
+
+	/* Shutdown running devices */
+	chandevshutdown();
+
+	if(inpanic && 0){
+		print("Hit the reset button\n");
+		for(;;)
+			clockpoll();
+	}
+	archreboot();
+}
+
+static void
+linkproc(void)
+{
+	spllo();
+	if (waserror())
+		print("error() underflow: %r\n");
+	else
+		(*up->kpfun)(up->arg);
+	pexit("end proc", 1);
+}
+
+void
+kprocchild(Proc *p, void (*func)(void*), void *arg)
+{
+	p->sched.pc = (ulong)linkproc;
+	p->sched.sp = (ulong)p->kstack+KSTACK-8;
+
+	p->kpfun = func;
+	p->arg = arg;
+}
+
+void
+idlehands(void)
+{
+	cpuidlecount++;
+	idle();
+}
+
+/* stubs */
+void
+setfsr(ulong)
+{
+}
+
+ulong
+getfsr()
+{
+	return 0;
+}
+
+void
+setfcr(ulong)
+{
+}
+
+ulong
+getfcr()
+{
+	return 0;
+}
+
+void
+fpinit(void)
+{
+}
+
+void
+FPsave(void*)
+{
+}
+
+void
+FPrestore(void*)
+{
+}
--- /dev/null
+++ b/os/manga/manga
@@ -1,0 +1,140 @@
+dev
+	root
+	cons	archmanga
+#	gpio
+	mnt
+	pipe
+	prog
+	srv
+	dup
+	ssl
+#	cap
+#	sign
+	uart
+	ip	ip ipv6 ipaux iproute arp netlog ptclbsum iprouter plan9 nullmedium pktmedium netaux
+	flash
+	ether netif netaux
+	env
+	pci	pci inb
+	usb	pci
+
+ip
+	il
+	tcp
+	udp
+#	rudp
+#	igmp
+	ipifc
+	icmp
+	icmp6
+	ipmux
+
+lib
+	interp
+	keyring
+	sec
+	mp
+	math
+	kern
+
+mod
+	math
+	sys
+	keyring
+	crypt
+	ipints
+
+
+port
+	alarm
+	alloc
+	allocb
+	chan
+	dev
+	dial
+	dis
+	discall
+	exception
+	exportfs
+	inferno
+	latin1
+	nocache
+	nodynld
+	parse
+	pgrp
+	print
+	proc
+	qio
+	qlock
+	random
+	sysfile
+	taslock
+	xalloc
+
+misc
+	uartks8695
+
+link	
+	flashcfi8
+	ether8139
+	etherks8695
+	ethermedium
+	usbuhci
+
+code
+	int main_pool_pcnt = 50;
+	int heap_pool_pcnt = 50;
+	int image_pool_pcnt = 0;
+	int cflag = 0;	/* for JIT */
+
+	int consoleprint = 1;
+	int panicreset = 0;
+
+init
+	cerf405
+
+root
+	/chan	/
+	/dev	/
+	/boot	/
+	/env	/
+	/fd	/
+	/net	/
+	/prog	/
+	/root	/
+	/nvfs	/
+	/osinit.dis
+	/tmp /
+
+# files used by osinit.dis during bootstrap
+	/boot/n	/
+	/boot/n/local	/
+	/boot/n/remote	/
+
+# authentication
+	/boot/nvfs/default	/usr/inferno/keyring/default
+	/boot/dis/lib/auth.dis	/dis/lib/auth.dis
+	/boot/dis/lib/ssl.dis	/dis/lib/ssl.dis
+# dhcp
+	/boot/dis/lib/dhcpclient.dis	/dis/lib/dhcpclient.dis
+	/boot/dis/lib/ip.dis	/dis/lib/ip.dis
+
+# and other files used to poke round during development
+	/boot/dis/cat.dis /dis/cat.dis
+	/boot/dis/echo.dis /dis/echo.dis
+	/boot/dis/lib/arg.dis /dis/lib/arg.dis
+
+	/boot/dis/sh.dis /dis/sh.dis
+	/boot/dis/lib/bufio.dis /dis/lib/bufio.dis
+	/boot/dis/lib/filepat.dis /dis/lib/filepat.dis
+	/boot/dis/lib/readdir.dis /dis/lib/readdir.dis
+	/boot/dis/lib/string.dis /dis/lib/string.dis
+
+	/boot/dis/cd.dis /dis/cd.dis
+	/boot/dis/bind.dis /dis/bind.dis
+	/boot/dis/dd.dis /dis/dd.dis
+	/boot/dis/p.dis /dis/p.dis
+	/boot/dis/ls.dis /dis/ls.dis
+	/boot/dis/lib/daytime.dis /dis/lib/daytime.dis
+	/boot/dis/time.dis /dis/time.dis
+	/boot/dis/xd.dis /dis/xd.dis
--- /dev/null
+++ b/os/manga/mem.h
@@ -1,0 +1,133 @@
+/*
+ * Memory and machine-specific definitions.  Used in C and assembler.
+ */
+
+/*
+ * Sizes
+ */
+#define	BI2BY		8			/* bits per byte */
+#define BI2WD		32			/* bits per word */
+#define	BY2WD		4			/* bytes per word */
+#define	BY2V		8			/* bytes per double word */
+#define	BY2PG		4096			/* bytes per page */
+#define	WD2PG		(BY2PG/BY2WD)		/* words per page */
+#define	PGSHIFT		12			/* log(BY2PG) */
+#define ROUND(s, sz)	(((s)+(sz-1))&~(sz-1))
+#define PGROUND(s)	ROUND(s, BY2PG)
+#define BIT(n)		(1<<n)
+#define BITS(a,b)	((1<<(b+1))-(1<<a))
+
+#define	MAXMACH		1			/* max # cpus system can run */
+
+/*
+ * Time
+ */
+#define	HZ		(100)			/* clock frequency */
+#define	MS2HZ		(1000/HZ)		/* millisec per clock tick */
+#define	TK2SEC(t)	((t)/HZ)		/* ticks to seconds */
+#define	MS2TK(t)	((t)/MS2HZ)		/* milliseconds to ticks */
+
+/*
+ * More accurate time
+ */
+#define CLOCKFREQ	25000000
+#define MS2TMR(t)	((ulong)(((uvlong)(t)*CLOCKFREQ)/1000))
+#define US2TMR(t)	((ulong)(((uvlong)(t)*CLOCKFREQ)/1000000))
+
+/*
+ *  Address spaces
+ *	nearly everything maps 1-1 with physical addresses
+ *	0 to 1Mb is not mapped
+ *	cache strategy varies as needed (see mmu.c)
+ */
+
+#define KZERO		0xC0000000
+#define MACHADDR	(KZERO+0x00001000)
+#define KTTB		(KZERO+0x00004000)
+#define KTZERO	(KZERO+0x00008010)
+#define KSTACK	8192			/* Size of kernel stack */
+#define FLASHMEM	0x50000000	/* map flash to otherwise unused virtual space */
+#define UCDRAMZERO	(KZERO+0x08000000)	/* base of memory doubly-mapped as uncached */
+#define AIVECADDR	0xFFFF0000	/* alternative interrupt vector address (other is 0) */
+
+/*
+ * Physical addresses
+ */
+#define PHYSDRAM0	0x00000000	/* where firmware puts it */
+#define PHYSFLASH0	0x02800000	/* where firmware puts it */
+#define PHYSSCRINIT	0x03FF0000	/* address at reset */
+#define PHYSSCR		PHYSSCRINIT	/* where it ends up after manga firmware */
+#define PHYSBRIDGE	(PHYSSCR+0x2000)	/* PCI-AHB bridge configuration */
+#define PHYSMEMCR	(PHYSSCR+0x4000)	/* memory controller interface */
+#define PHYSWANDMA	(PHYSSCR+0x6000)	/* WAN DMA registers */
+#define PHYSLANDMA	(PHYSSCR+0x8000)	/* LAN DMA registers */
+#define PHYSUART		(PHYSSCR+0xE000)
+#define PHYSINTR		(PHYSSCR+0xE200)	/* interrupt controller */
+#define PHYSTIMER		(PHYSSCR+0xE400)	/* timer registers */
+#define PHYSGPIO		(PHYSSCR+0xE600)
+#define PHYSSWITCH	(PHYSSCR+0xE800)	/* switch engine configuration */
+#define PHYSMISC		(PHYSSCR+0xEA00)	/* ``miscellaneous'' registers */
+
+#define PHYSPCIBRIDGE	0x80000000	/* physical address that maps to PCI 0 */
+#define PHYSPCIIO		0x10000000	/* physical address that maps to PCI I/O space */
+
+#define	CACHELINELOG	5
+#define	CACHELINESZ	(1<<CACHELINELOG)
+#define	CACHESIZE	(8*1024)		/* I & D caches are the same size, 4 segment, 64-way associative */
+
+/*
+ * PSR
+ */
+#define PsrMusr		0x10 	/* mode */
+#define PsrMfiq		0x11 
+#define PsrMirq		0x12
+#define PsrMsvc		0x13
+#define PsrMabt		0x17
+#define PsrMund		0x1B
+#define PsrMsys		0x1F
+#define PsrMask		0x1F
+
+#define PsrDfiq		0x00000040	/* disable FIQ interrupts */
+#define PsrDirq		0x00000080	/* disable IRQ interrupts */
+
+#define PsrV		0x10000000	/* overflow */
+#define PsrC		0x20000000	/* carry/borrow/extend */
+#define PsrZ		0x40000000	/* zero */
+#define PsrN		0x80000000	/* negative/less than */
+
+/*
+ * Internal MMU coprocessor registers (ARM 922)
+ */
+#define CpCPUID	0		/* R: opcode_2 is 0*/
+#define CpCacheID	0		/* R: opcode_2 is 1 */
+#define CpControl	1		/* R/W: control (opcode_2 is 0) */
+#define CpTTB		2		/* R/W: translation table base */
+#define CpDAC		3		/* R/W: domain access control */
+#define CpFSR		5		/* R/W: fault status */
+#define CpFAR		6		/* R/W: fault address */
+#define CpCacheCtl	7		/* W: */
+#define CpTLBops	8		/* W: TLB operations */
+#define CpCacheLk	9		/* W: cache lock down */
+#define CpPID		13		/* R/W: Process ID Virtual Mapping */
+#define CpTest		15		/* R/W: test configuration */
+
+/*
+ * Coprocessors
+ */
+#define CpMMU		15
+
+/*
+ * CpControl bits
+ */
+#define CpCmmu	(1<<0)	/* M: MMU enable */
+#define CpCalign	(1<<1)	/* A: alignment fault enable */
+#define CpCDcache	(1<<2)	/* C: data cache on */
+#define CpCwpd	(15<<3)	/* W, P, D, must be one */
+#define CpCbe		(1<<7)	/* B: big-endian operation */
+#define CpCsystem	(1<<8)	/* S: system permission */
+#define CpCrom	(1<<9)	/* R: ROM permission */
+#define CpCIcache	(1<<12)	/* I: Instruction Cache on */
+#define CpCaltivec	(1<<13)	/* X: exception vector relocation */
+#define CpCrrobin	(1<<14)	/* RR: round robin replacement */
+#define CpCnotFast	(1<<30)	/* nF: notFastBus select */
+#define CpCasync	(1<<31)	/* iA: asynchronous clock select */
--- /dev/null
+++ b/os/manga/mkfile
@@ -1,0 +1,89 @@
+<../../mkconfig
+
+#Configurable parameters
+
+CONF=manga				#default configuration
+CONFLIST=manga boot
+
+SYSTARG=$OSTARG
+OBJTYPE=arm
+INSTALLDIR=$ROOT/Inferno/$OBJTYPE	#path of directory where kernel is installed
+#end configurable parameters
+
+<$ROOT/mkfiles/mkfile-$SYSTARG-$OBJTYPE	#set vars based on target system
+
+<| $SHELLNAME ../port/mkdevlist $CONF	#sets $IP, $DEVS, $ETHERS, $VGAS, $PORT, $MISC, $LIBS, $OTHERS
+
+KTZERO=0xC0008010
+
+OBJ=\
+	l.$O\
+	clock.$O\
+	fpi.$O\
+	fpiarm.$O\
+	fpimem.$O\
+	gpio.$O\
+	ioring.$O\
+	main.$O\
+	mmu.$O\
+	trap.$O\
+	$CONF.root.$O\
+	$IP\
+	$DEVS\
+	$ETHERS\
+	$LINKS\
+	$PORT\
+	$MISC\
+	$OTHERS\
+
+LIBNAMES=${LIBS:%=lib%.a}
+LIBDIRS=$LIBS
+
+HFILES=\
+	mem.h\
+	dat.h\
+	io.h\
+
+CFLAGS=-wFV -I$ROOT/Inferno/$OBJTYPE/include -I$ROOT/include -I$ROOT/libinterp
+KERNDATE=`{$NDATE}
+
+default:V: i$CONF i$CONF.p9 k.gz
+
+install:V: $INSTALLDIR/i$CONF $INSTALLDIR/i$CONF.gz $INSTALLDIR/i$CONF.p9.gz
+
+i$CONF: $OBJ $CONF.c $CONF.root.h $LIBNAMES
+	$CC $CFLAGS '-DKERNDATE='$KERNDATE $CONF.c
+	$LD -s -o $target -H5 -T$KTZERO -R4 -l $OBJ $CONF.$O $LIBFILES
+
+i$CONF.p9: $OBJ $CONF.c $CONF.root.h $LIBNAMES
+	$CC $CFLAGS '-DKERNDATE='$KERNDATE $CONF.c
+	$LD -o $target -T$KTZERO -R4 -l $OBJ $CONF.$O $LIBFILES
+
+i$CONF.gz: i$CONF
+	rm -f i$CONF.gz
+	gzip -9 <i$CONF >i$CONF.gz
+
+<../port/portmkfile
+CLEANEXTRA=k.gz
+
+../init/$INIT.dis:	../init/$INIT.b
+		cd ../init; mk $INIT.dis
+
+clock.$O:	$ROOT/Inferno/$OBJTYPE/include/ureg.h
+devether.$O:	$ROOT/Inferno/$OBJTYPE/include/ureg.h
+main.$O:	$ROOT/Inferno/$OBJTYPE/include/ureg.h
+trap.$O:	$ROOT/Inferno/$OBJTYPE/include/ureg.h
+
+$IP devip.$O:		../ip/ip.h
+
+
+devuart.$O:	../port/devuart.c ../port/uart.h
+	$CC $CFLAGS ../port/devuart.c
+
+k.gz:	i$CONF.gz pinflate
+	cat pinflate i$CONF.gz >k.gz
+	echo burble burble >>k.gz
+
+# pepinflate: /sys/src/boot/pep/pinflate
+
+dummy:V:
--- /dev/null
+++ b/os/manga/mmu.c
@@ -1,0 +1,244 @@
+#include "u.h"
+#include "../port/lib.h"
+#include "mem.h"
+#include "dat.h"
+#include "fns.h"
+
+/*
+ * Small pages:
+ *	L1: 12-bit index -> 4096 descriptors -> 16Kb
+ *	L2:  8-bit index ->  256 descriptors ->  1Kb
+ * Each L2 descriptor has access permissions for 4 1Kb sub-pages.
+ *
+ *	TTB + L1Tx gives address of L1 descriptor
+ *	L1 descriptor gives PTBA
+ *	PTBA + L2Tx gives address of L2 descriptor
+ *	L2 descriptor gives PBA
+ *
+ * C & B are interpreted as follows:
+ *	C=0 B=0	uncached, unbuffered, stall until data access complete
+ *	C=0 B=1	uncached, buffered
+ *	C=1 B=0	write-through cachable
+ *	C=1 B=1	write-back cachable
+ * and the i-cache uses only the C bit (cached if non-zero).
+ */
+#define TTB(pa)	((pa) & ~0x3FFF)	/* translation table base */
+#define L1x(pa)	(((pa)>>20) & 0xFFF)	/* L1 table index */
+#define PTBA(pa)	((pa) & ~0x3FF)		/* page table base address */
+#define L2x(pa)	(((pa)>>12) & 0xFF)	/* L2 table index */
+#define PBA(pa)	((pa) & ~0xFFF)		/* page base address */
+#define SBA(pa)	((pa) & ~0xFFFFF)	/* section base address */
+
+enum {
+	/* sizes */
+	Section=	1<<20,
+	LargePage=	1<<16,
+	SmallPage=	1<<12,
+	EsmallPage=	1<<10,
+	SectionPages = Section/SmallPage,
+	PtAlign = 1<<10,
+
+	/* L1 descriptor format */
+	L1type= 	3<<0,	/* mask for type */
+	L1page= 	1<<0,		/* descriptor is for L2 pages */
+	L1section= 2<<0,			/* descriptor is for section */
+	L1fpage=	3<<0,	/* descriptor is for fine (1k) L2 pages */
+	L1buffered=	1<<2,
+	L1cached=	1<<3,
+	L1mbo=		1<<4,	/* must be one */
+
+	/* L2 descriptor format for coarse page table */
+	L2type=	3<<0,	/* mask for type */
+	L2invalid=	0<<0,
+	L2large=	1<<0,			/* large page */
+	L2small=	2<<0,			/* small page */
+	L2esmall=	3<<0,	/* extended small page */
+	L2buffered=	1<<2,
+	L2cached=	1<<3,
+	/* then access permissions */
+	L2smallX=	1<<6,
+	L2largeX=	1<<12,
+
+	/* domains */
+	Dnone=	0,
+	Dclient=	1,
+	Dmanager=	3,
+
+	/* access permissions */
+	APsro=	0,	/* supervisor ro if S|R */
+	APsrw=	1,	/* supervisor rw */
+	APuro=	2,	/* supervisor rw + user ro */
+	APurw=	3,	/* supervisor rw + user rw */
+};
+
+#define L1dom(d)	(((d) & 0xF)<<5)	/* L1 domain */
+#define AP(i, v)	((v)<<(((i)*2)+4))	/* access permissions */
+#define L1AP(v)	AP(3, (v))
+#define L2AP(v)	AP(3, (v))|AP(2, (v))|AP(1, (v))|AP(0, (v))
+
+#define L1krw	(L1AP(APsrw) | L1dom(0))
+
+/*
+ * return physical address corresponding to a given virtual address,
+ * or 0 if there is no such address
+ */
+ulong
+va2pa(void *v)
+{
+	int idx;
+	ulong pte, ste, *ttb;
+
+	idx = L1x((ulong)v);
+	ttb = (ulong*)KTTB;
+	ste = ttb[idx];
+	switch(ste & L1type) {
+	case L1section:
+		return SBA(ste)|((ulong)v & 0x000fffff);
+	case L1page:
+		pte = ((ulong *)PTBA(ste))[L2x((ulong)v)]; 
+		switch(pte & 3) {
+		case L2large:
+			return (pte & 0xffff0000)|((ulong)v & 0x0000ffff);
+		case L2small:
+			return (pte & 0xfffff000)|((ulong)v & 0x00000fff);
+		}
+	}
+	return 0;
+}
+
+/* for debugging */
+void
+prs(char *s)
+{
+	serialputs(s, strlen(s));
+}
+
+void
+pr16(ulong n)
+{
+	int i, c;
+
+	for(i=28; i>=0; i-=4){
+		c = (n>>i) & 0xF;
+		if(c >= 0 && c <= 9)
+			c += '0';
+		else
+			c += 'A'-10;
+		serialputc(c);
+	}
+}
+
+void
+xdelay(int n)
+{
+	int j;
+
+	for(j=0; j<1000000/4; j++)
+		n++;
+	USED(n);
+}
+
+void*
+mmuphysmap(void *va, ulong pa, ulong nbytes)
+{
+	ulong *ttb;
+	ulong p, o;
+
+	if(va == nil)
+		va = KADDR(pa);
+	p = (ulong)va;
+	if((pa|p) & (Section-1))
+		panic("kmapphys");
+	ttb = (ulong*)KTTB;
+	nbytes = (nbytes+Section-1)&~(Section-1);
+	for(o = 0; o < nbytes; o += Section)
+		ttb[L1x(p+o)] = (pa+o) | (1<<4) | L1krw | L1section;
+	return va;
+}
+
+void*
+mmukaddr(ulong pa)
+{
+	if(pa >= PHYSDRAM0 && pa < conf.topofmem)
+		return (void*)(KZERO+(pa-PHYSDRAM0));
+	return (void*)pa;
+}
+
+/*
+ * Set a 1-1 map of virtual to physical memory, except:
+ *	kernel is mapped to KZERO
+ *	doubly-map page0 at the alternative interrupt vector address,
+ * 	doubly-map physical memory at KZERO+256*MB as uncached but buffered,
+ *	map flash to virtual space away from 0,
+ *	disable access to 0 (nil pointers).
+ *
+ * Other section maps are added later as required by mmuphysmap.
+ */
+void
+mmuinit(void)
+{
+	int i;
+	ulong *ttb, *ptable;
+
+	ttb = (ulong*)KTTB;
+	memset(ttb, 0, 16384);
+
+	/* assume flash is first in special physical space */
+	for(i = L1x(PHYSFLASH0); i < 0x1000; i++)
+		ttb[i] = (i<<20) | L1krw | (1<<4) | L1section;
+
+	/* cached dram at normal kernel addresses */
+	for(i = 0; i < 32*MB; i += MB)
+		ttb[L1x(KZERO+i)] = (PHYSDRAM0+i) | (1<<4) | L1krw | L1section | L1cached | L1buffered;
+
+	/* aliases for uncached dram */
+	for(i = 0; i < 64*MB; i += MB)
+		ttb[L1x(UCDRAMZERO+i)] = (PHYSDRAM0+i) | L1krw | (1<<4) | L1section;
+
+	/* TO DO: make the text read only */
+
+	/* remap flash */
+	for(i=0; i<8*MB; i+=MB)
+		ttb[L1x(FLASHMEM+i)] = (PHYSFLASH0+i) | L1krw | (1<<4) | L1section;	/* we'll make flash uncached for now */
+
+	/*
+	 * build page table for alternative vector page, mapping trap vectors in *page0
+	 */
+	ptable = xspanalloc(SectionPages*sizeof(*ptable), PtAlign, 0);
+	ptable[L2x(AIVECADDR)] = PADDR(page0) | L2AP(APsrw) | L2cached | L2buffered | L2small;
+	ttb[L1x(AIVECADDR)] = PADDR(ptable) | (1<<4) | L1page;
+
+	mmuputttb(KTTB & ~KZERO);
+	mmuputdac(Dclient);
+	mmuputctl(mmugetctl() | CpCaltivec | CpCIcache | CpCsystem | CpCwpd | CpCDcache | CpCmmu);
+	tlbinvalidateall();
+}
+
+/*
+ * flush data in a given address range to memory
+ * and invalidate the region in the instruction cache.
+ */
+int
+segflush(void *a, ulong n)
+{
+	dcflush(a, n);
+	icflush(a, n);
+	return 0;
+}
+
+/*
+ * return an uncached alias for the memory at a
+ */
+void*
+mmucacheinhib(void *a, ulong nb)
+{
+	ulong p;
+
+	if(a == nil)
+		return nil;
+	p = PADDR(a);
+	if(p & (CACHELINESZ-1))
+		panic("mmucacheinhib");
+	dcflush(a, nb);
+	return (void*)(UCDRAMZERO|PADDR(a));
+}
--- /dev/null
+++ b/os/manga/pci.c
@@ -1,0 +1,1007 @@
+/*
+ * PCI support code.
+ */
+#include "u.h"
+#include "../port/lib.h"
+#include "mem.h"
+#include "dat.h"
+#include "fns.h"
+#include "io.h"
+#include "../port/error.h"
+
+#define DBG	if(0) pcilog
+#undef DBG
+#define DBG	if(1) iprint
+
+typedef struct Pcicfg Pcicfg;
+struct Pcicfg {
+	ulong	addr;
+	ulong	data;
+};
+
+static Pcicfg*	pcicfg;
+static ulong*	pciack;
+static ulong*	pcimem;
+
+struct
+{
+	char	output[16384];
+	int	ptr;
+}PCICONS;
+
+int
+pcilog(char *fmt, ...)
+{
+	int n;
+	va_list arg;
+	char buf[PRINTSIZE];
+
+	va_start(arg, fmt);
+	n = vseprint(buf, buf+sizeof(buf), fmt, arg) - buf;
+	va_end(arg);
+
+	memmove(PCICONS.output+PCICONS.ptr, buf, n);
+	PCICONS.ptr += n;
+	return n;
+}
+
+enum
+{					/* configuration mechanism #1 */
+	MaxFNO		= 7,
+	MaxUBN		= 255,
+};
+
+enum
+{					/* command register */
+	IOen		= (1<<0),
+	MEMen		= (1<<1),
+	MASen		= (1<<2),
+	MemWrInv	= (1<<4),
+	PErrEn		= (1<<6),
+	SErrEn		= (1<<8),
+};
+
+static Lock pcicfglock;
+static QLock pcicfginitlock;
+static int pcicfgmode = -1;
+static int pcimaxbno = 7;
+static int pcimaxdno;
+static Pcidev* pciroot;
+static Pcidev* pcilist;
+static Pcidev* pcitail;
+
+static int pcicfgrw32(int, int, int, int);
+static int pcicfgrw8(int, int, int, int);
+static void pcirouting(void);
+static void pcirootmap(Pcidev*);
+static void pcidumpdev(ulong);
+
+static char* bustypes[] = {
+[BusIRQ]	"IRQ",
+[BusPCI]	"PCI",
+};
+
+#pragma	varargck	type	"Y"	int
+
+static int
+tbdffmt(Fmt* fmt)
+{
+	char *p;
+	int l, r, type, tbdf;
+
+	if((p = malloc(READSTR)) == nil)
+		return fmtstrcpy(fmt, "(tbdfconv)");
+		
+	switch(fmt->r){
+	case 'T':
+	case 'Y':
+		tbdf = va_arg(fmt->args, int);
+		type = BUSTYPE(tbdf);
+		if(type < nelem(bustypes))
+			l = snprint(p, READSTR, bustypes[type]);
+		else
+			l = snprint(p, READSTR, "%d", type);
+		snprint(p+l, READSTR-l, ".%d.%d.%d",
+			BUSBNO(tbdf), BUSDNO(tbdf), BUSFNO(tbdf));
+		break;
+
+	default:
+		snprint(p, READSTR, "(tbdfconv)");
+		break;
+	}
+	r = fmtstrcpy(fmt, p);
+	free(p);
+
+	return r;
+}
+
+ulong
+pcibarsize(Pcidev *p, int rno)
+{
+	ulong v, size;
+
+	v = pcicfgrw32(p->tbdf, rno, 0, 1);
+	pcicfgrw32(p->tbdf, rno, 0xFFFFFFF0, 0);
+	size = pcicfgrw32(p->tbdf, rno, 0, 1);
+	if(v & 1)
+		size |= 0xFFFF0000;
+	pcicfgrw32(p->tbdf, rno, v, 0);
+
+	return -(size & ~0x0F);
+}
+
+static int
+pcisizcmp(void *a, void *b)
+{
+	Pcisiz *aa, *bb;
+
+	aa = a;
+	bb = b;
+	return aa->siz - bb->siz;
+}
+
+static ulong
+pcimask(ulong v)
+{
+	ulong m;
+
+	m = BI2BY*sizeof(v);
+	for(m = 1<<(m-1); m != 0; m >>= 1) {
+		if(m & v)
+			break;
+	}
+
+	m--;
+	if((v & m) == 0)
+		return v;
+
+	v |= m;
+	return v+1;
+}
+
+static void
+pcibusmap(Pcidev *root, ulong *pmema, ulong *pioa, int wrreg)
+{
+	Pcidev *p;
+	int ntb, i, size, rno, hole;
+	ulong v, mema, ioa, sioa, smema, base, limit;
+	Pcisiz *table, *tptr, *mtb, *itb;
+	extern void qsort(void*, long, long, int (*)(void*, void*));
+
+	ioa = *pioa;
+	mema = *pmema;
+
+	DBG("pcibusmap wr=%d %Y mem=%luX io=%luX\n", 
+		wrreg, root->tbdf, mema, ioa);
+
+	ntb = 0;
+	for(p = root; p != nil; p = p->link)
+		ntb++;
+
+	ntb *= (PciCIS-PciBAR0)/4;
+	table = malloc(2*ntb*sizeof(Pcisiz));
+	itb = table;
+	mtb = table+ntb;
+
+	/*
+	 * Build a table of sizes
+	 */
+	for(p = root; p != nil; p = p->link) {
+		if(p->ccrb == 0x06) {
+			if(p->ccru == 0x04 && p->bridge != nil) {
+				sioa = ioa;
+				smema = mema;
+				pcibusmap(p->bridge, &smema, &sioa, 0);
+	
+				hole = pcimask(smema-mema);
+				if(hole < (1<<20))
+					hole = 1<<20;
+				p->mema.size = hole;
+	
+				hole = pcimask(sioa-ioa);
+				if(hole < (1<<12))
+					hole = 1<<12;
+	
+				p->ioa.size = hole;
+	
+				itb->dev = p;
+				itb->bar = -1;
+				itb->siz = p->ioa.size;
+				itb++;
+	
+				mtb->dev = p;
+				mtb->bar = -1;
+				mtb->siz = p->mema.size;
+				mtb++;
+			}
+			if((pcicfgr8(p, PciHDT)&0x7f) != 0)
+				continue;
+		}
+
+		for(i = 0; i <= 5; i++) {
+			rno = PciBAR0 + i*4;
+			v = pcicfgrw32(p->tbdf, rno, 0, 1);
+			size = pcibarsize(p, rno);
+			if(size == 0)
+				continue;
+
+			if(v & 1) {
+				itb->dev = p;
+				itb->bar = i;
+				itb->siz = size;
+				itb++;
+			}
+			else {
+				mtb->dev = p;
+				mtb->bar = i;
+				mtb->siz = size;
+				mtb++;
+			}
+
+			p->mem[i].size = size;
+		}
+	}
+
+	/*
+	 * Sort both tables IO smallest first, Memory largest
+	 */
+	qsort(table, itb-table, sizeof(Pcisiz), pcisizcmp);
+	tptr = table+ntb;
+	qsort(tptr, mtb-tptr, sizeof(Pcisiz), pcisizcmp);
+
+	/*
+	 * Allocate IO address space on this bus
+	 */
+	for(tptr = table; tptr < itb; tptr++) {
+		hole = tptr->siz;
+		if(tptr->bar == -1)
+			hole = 1<<12;
+		ioa = (ioa+hole-1) & ~(hole-1);
+
+		p = tptr->dev;
+		if(tptr->bar == -1)
+			p->ioa.bar = ioa;
+		else {
+			p->pcr |= IOen;
+			p->mem[tptr->bar].bar = ioa|1;
+			if(wrreg)
+				pcicfgrw32(p->tbdf, PciBAR0+(tptr->bar*4), ioa|1, 0);
+		}
+
+		ioa += tptr->siz;
+	}
+
+	/*
+	 * Allocate Memory address space on this bus
+	 */
+	for(tptr = table+ntb; tptr < mtb; tptr++) {
+		hole = tptr->siz;
+		if(tptr->bar == -1)
+			hole = 1<<20;
+		mema = (mema+hole-1) & ~(hole-1);
+
+		p = tptr->dev;
+		if(tptr->bar == -1)
+			p->mema.bar = mema;
+		else {
+			p->pcr |= MEMen;
+			p->mem[tptr->bar].bar = mema;
+			if(wrreg)
+				pcicfgrw32(p->tbdf, PciBAR0+(tptr->bar*4), mema, 0);
+		}
+		mema += tptr->siz;
+	}
+
+	*pmema = mema;
+	*pioa = ioa;
+	free(table);
+
+	if(wrreg == 0)
+		return;
+
+	/*
+	 * Finally set all the bridge addresses & registers
+	 */
+	for(p = root; p != nil; p = p->link) {
+		if(p->bridge == nil) {
+			pcicfgrw8(p->tbdf, PciLTR, 64, 0);
+
+			p->pcr |= MASen;
+			pcicfgrw32(p->tbdf, PciPCR, p->pcr, 0);
+			continue;
+		}
+
+		base = p->ioa.bar;
+		limit = base+p->ioa.size-1;
+		v = pcicfgrw32(p->tbdf, PciBAR3, 0, 1);
+		v = (v&0xFFFF0000)|(limit & 0xF000)|((base & 0xF000)>>8);
+		pcicfgrw32(p->tbdf, PciBAR3, v, 0);
+		v = (limit & 0xFFFF0000)|(base>>16);
+		pcicfgrw32(p->tbdf, 0x30, v, 0);
+
+		base = p->mema.bar;
+		limit = base+p->mema.size-1;
+		v = (limit & 0xFFF00000)|((base & 0xFFF00000)>>16);
+		pcicfgrw32(p->tbdf, PciBAR4, v, 0);
+
+		/*
+		 * Disable memory prefetch
+		 */
+		pcicfgrw32(p->tbdf, PciBAR5, 0x0000FFFF, 0);
+		pcicfgrw8(p->tbdf, PciLTR, 64, 0);
+
+		/*
+		 * Enable the bridge
+		 */
+		v = 0xFFFF0000 | IOen | MEMen | MASen;
+		pcicfgrw32(p->tbdf, PciPCR, v, 0);
+
+		sioa = p->ioa.bar;
+		smema = p->mema.bar;
+		pcibusmap(p->bridge, &smema, &sioa, 1);
+	}
+}
+
+static int
+pcilscan(int bno, Pcidev** list)
+{
+	Pcidev *p, *head, *tail;
+	int dno, fno, i, hdt, l, maxfno, maxubn, rno, sbn, tbdf, ubn;
+
+	maxubn = bno;
+	head = nil;
+	tail = nil;
+	for(dno = 0; dno <= pcimaxdno; dno++){
+		maxfno = 0;
+		for(fno = 0; fno <= maxfno; fno++){
+			/*
+			 * For this possible device, form the
+			 * bus+device+function triplet needed to address it
+			 * and try to read the vendor and device ID.
+			 * If successful, allocate a device struct and
+			 * start to fill it in with some useful information
+			 * from the device's configuration space.
+			 */
+			tbdf = MKBUS(BusPCI, bno, dno, fno);
+			l = pcicfgrw32(tbdf, PciVID, 0, 1);
+			if(l == 0xFFFFFFFF || l == 0)
+				continue;
+			p = malloc(sizeof(*p));
+			p->tbdf = tbdf;
+			p->vid = l;
+			p->did = l>>16;
+
+			if(pcilist != nil)
+				pcitail->list = p;
+			else
+				pcilist = p;
+			pcitail = p;
+
+			p->rid = pcicfgr8(p, PciRID);
+			p->ccrp = pcicfgr8(p, PciCCRp);
+			p->ccru = pcicfgr8(p, PciCCRu);
+			p->ccrb = pcicfgr8(p, PciCCRb);
+			p->pcr = pcicfgr32(p, PciPCR);
+
+			p->intl = pcicfgr8(p, PciINTL);
+
+			/*
+			 * If the device is a multi-function device adjust the
+			 * loop count so all possible functions are checked.
+			 */
+			hdt = pcicfgr8(p, PciHDT);
+			if(hdt & 0x80)
+				maxfno = MaxFNO;
+
+			/*
+			 * If appropriate, read the base address registers
+			 * and work out the sizes.
+			 */
+			switch(p->ccrb) {
+			case 0x01:		/* mass storage controller */
+			case 0x02:		/* network controller */
+			case 0x03:		/* display controller */
+			case 0x04:		/* multimedia device */
+			case 0x06:		/* bridge device */
+			case 0x07:		/* simple comm. controllers */
+			case 0x08:		/* base system peripherals */
+			case 0x09:		/* input devices */
+			case 0x0A:		/* docking stations */
+			case 0x0B:		/* processors */
+			case 0x0C:		/* serial bus controllers */
+				if((hdt & 0x7F) != 0)
+					break;
+				rno = PciBAR0 - 4;
+				for(i = 0; i < nelem(p->mem); i++) {
+					rno += 4;
+					p->mem[i].bar = pcicfgr32(p, rno);
+					p->mem[i].size = pcibarsize(p, rno);
+				}
+				break;
+
+			case 0x00:
+			case 0x05:		/* memory controller */
+			default:
+				break;
+			}
+
+			if(head != nil)
+				tail->link = p;
+			else
+				head = p;
+			tail = p;
+		}
+	}
+
+	*list = head;
+	for(p = head; p != nil; p = p->link){
+		/*
+		 * Find PCI-PCI bridges and recursively descend the tree.
+		 */
+		if(p->ccrb != 0x06 || p->ccru != 0x04)
+			continue;
+
+		/*
+		 * If the secondary or subordinate bus number is not
+		 * initialised try to do what the PCI BIOS should have
+		 * done and fill in the numbers as the tree is descended.
+		 * On the way down the subordinate bus number is set to
+		 * the maximum as it's not known how many buses are behind
+		 * this one; the final value is set on the way back up.
+		 */
+		sbn = pcicfgr8(p, PciSBN);
+		ubn = pcicfgr8(p, PciUBN);
+
+		if(sbn == 0 || ubn == 0) {
+			sbn = maxubn+1;
+			/*
+			 * Make sure memory, I/O and master enables are
+			 * off, set the primary, secondary and subordinate
+			 * bus numbers and clear the secondary status before
+			 * attempting to scan the secondary bus.
+			 *
+			 * Initialisation of the bridge should be done here.
+			 */
+			pcicfgw32(p, PciPCR, 0xFFFF0000);
+			l = (MaxUBN<<16)|(sbn<<8)|bno;
+			pcicfgw32(p, PciPBN, l);
+			pcicfgw16(p, PciSPSR, 0xFFFF);
+			maxubn = pcilscan(sbn, &p->bridge);
+			l = (maxubn<<16)|(sbn<<8)|bno;
+
+			pcicfgw32(p, PciPBN, l);
+		}
+		else {
+			maxubn = ubn;
+			pcilscan(sbn, &p->bridge);
+		}
+	}
+
+	return maxubn;
+}
+
+int
+pciscan(int bno, Pcidev **list)
+{
+	int ubn;
+
+	qlock(&pcicfginitlock);
+	ubn = pcilscan(bno, list);
+	qunlock(&pcicfginitlock);
+	return ubn;
+}
+
+static void
+pcicfginit(void)
+{
+	char *p;
+	int bno;
+	Pcidev **list;
+	ulong mema, ioa;
+
+	qlock(&pcicfginitlock);
+	if(pcicfgmode != -1)
+		goto out;
+
+	//pcimmap();
+
+	pcicfgmode = 1;
+	pcimaxdno = 31;
+
+	fmtinstall('Y', tbdffmt);
+
+	if(p = getconf("*pcimaxbno"))
+		pcimaxbno = strtoul(p, 0, 0);
+	if(p = getconf("*pcimaxdno"))
+		pcimaxdno = strtoul(p, 0, 0);
+
+
+	list = &pciroot;
+	for(bno = 0; bno <= pcimaxbno; bno++) {
+		int sbno = bno;
+		bno = pcilscan(bno, list);
+
+		while(*list)
+			list = &(*list)->link;
+
+		if (sbno == 0) {
+			Pcidev *pci;
+
+			/*
+			  * If we have found a PCI-to-Cardbus bridge, make sure
+			  * it has no valid mappings anymore.  
+			  */
+			pci = pciroot;
+			while (pci) {
+				if (pci->ccrb == 6 && pci->ccru == 7) {
+					ushort bcr;
+
+					/* reset the cardbus */
+					bcr = pcicfgr16(pci, PciBCR);
+					pcicfgw16(pci, PciBCR, 0x40 | bcr);
+					delay(50);
+				}
+				pci = pci->link;
+			}
+		}
+	}
+
+	if(pciroot == nil)
+		goto out;
+
+	/*
+	 * Work out how big the top bus is
+	 */
+	mema = 0;
+	ioa = 0;
+	pcibusmap(pciroot, &mema, &ioa, 0);
+
+	DBG("Sizes: mem=%8.8lux size=%8.8lux io=%8.8lux\n",
+		mema, pcimask(mema), ioa);
+
+	/*
+	 * Align the windows and map it
+	 */
+	ioa = 0x1000;
+	mema = 0;
+
+	pcilog("Mask sizes: mem=%lux io=%lux\n", mema, ioa);
+
+	pcibusmap(pciroot, &mema, &ioa, 1);
+	DBG("Sizes2: mem=%lux io=%lux\n", mema, ioa);
+
+	pcirootmap(pciroot);
+
+	pcirouting();
+
+	if(1){
+		iprint("pci bridge':\n");
+		pcidumpdev(pciroot->tbdf);
+	}
+	if(1){
+		/* see we've left */
+		ulong *p;
+		int i;
+
+		p = KADDR(PHYSBRIDGE+0x200);
+		iprint("PCI:\n");
+		for(i=0; i<10; i++)
+			iprint("%8.8lux: %8.8lux\n", p+i, p[i]);
+	}
+
+out:
+	qunlock(&pcicfginitlock);
+}
+
+static int
+pcicfgrw8(int tbdf, int rno, int data, int read)
+{
+	int o, x;
+
+	if(pcicfgmode == -1)
+		pcicfginit();
+
+	x = -1;
+	if(BUSDNO(tbdf) > pcimaxdno)
+		return x;
+
+	lock(&pcicfglock);
+	o = (rno & 0x03)<<3;
+	rno &= ~0x03;
+	pcicfg->addr = 0x80000000|BUSBDF(tbdf)|rno;
+	if(read)
+		x = (pcicfg->data>>o) & 0xFF;
+	else
+		pcicfg->data = (pcicfg->data & ~(0xFF<<o)) | ((data & 0xFF) << o);
+	pcicfg->addr = 0;
+	unlock(&pcicfglock);
+
+	return x;
+}
+
+int
+pcicfgr8(Pcidev* pcidev, int rno)
+{
+	return pcicfgrw8(pcidev->tbdf, rno, 0, 1);
+}
+
+void
+pcicfgw8(Pcidev* pcidev, int rno, int data)
+{
+	pcicfgrw8(pcidev->tbdf, rno, data, 0);
+}
+
+static int
+pcicfgrw16(int tbdf, int rno, int data, int read)
+{
+	int o, x;
+
+	if(pcicfgmode == -1)
+		pcicfginit();
+
+	x = -1;
+	if(BUSDNO(tbdf) > pcimaxdno)
+		return x;
+
+	lock(&pcicfglock);
+	o = ((rno >> 1) & 1)<<4;
+	rno &= ~0x03;
+	pcicfg->addr = 0x80000000|BUSBDF(tbdf)|rno;
+	if(read)
+		x = (pcicfg->data>>o) & 0xFFFF;
+	else
+		pcicfg->data = (pcicfg->data & ~(0xFFFF<<o)) | ((data&0xFFFF)<<o);
+	pcicfg->addr = 0;
+	unlock(&pcicfglock);
+
+	return x;
+}
+
+int
+pcicfgr16(Pcidev* pcidev, int rno)
+{
+	return pcicfgrw16(pcidev->tbdf, rno, 0, 1);
+}
+
+void
+pcicfgw16(Pcidev* pcidev, int rno, int data)
+{
+	pcicfgrw16(pcidev->tbdf, rno, data, 0);
+}
+
+static int
+pcicfgrw32(int tbdf, int rno, int data, int read)
+{
+	int x;
+
+	if(pcicfgmode == -1)
+		pcicfginit();
+
+	x = -1;
+	if(BUSDNO(tbdf) > pcimaxdno)
+		return x;
+
+	lock(&pcicfglock);
+	rno &= ~0x03;
+	pcicfg->addr = 0x80000000|BUSBDF(tbdf)|rno;
+	if(read)
+		x = pcicfg->data;
+	else
+		pcicfg->data = data;
+	pcicfg->addr = 0;
+	unlock(&pcicfglock);
+
+	return x;
+}
+
+int
+pcicfgr32(Pcidev* pcidev, int rno)
+{
+	return pcicfgrw32(pcidev->tbdf, rno, 0, 1);
+}
+
+void
+pcicfgw32(Pcidev* pcidev, int rno, int data)
+{
+	pcicfgrw32(pcidev->tbdf, rno, data, 0);
+}
+
+Pcidev*
+pcimatch(Pcidev* prev, int vid, int did)
+{
+	if(pcicfgmode == -1)
+		pcicfginit();
+
+	if(prev == nil)
+		prev = pcilist;
+	else
+		prev = prev->list;
+
+	while(prev != nil){
+		if((vid == 0 || prev->vid == vid)
+		&& (did == 0 || prev->did == did))
+			break;
+		prev = prev->list;
+	}
+	return prev;
+}
+
+Pcidev*
+pcimatchtbdf(int tbdf)
+{
+	Pcidev *pcidev;
+
+	if(pcicfgmode == -1)
+		pcicfginit();
+
+	for(pcidev = pcilist; pcidev != nil; pcidev = pcidev->list) {
+		if(pcidev->tbdf == tbdf)
+			break;
+	}
+	return pcidev;
+}
+
+uchar
+pciipin(Pcidev *pci, uchar pin)
+{
+	if (pci == nil)
+		pci = pcilist;
+
+	while (pci) {
+		uchar intl;
+
+		if (pcicfgr8(pci, PciINTP) == pin && pci->intl != 0 && pci->intl != 0xff)
+			return pci->intl;
+
+		if (pci->bridge && (intl = pciipin(pci->bridge, pin)) != 0)
+			return intl;
+
+		pci = pci->list;
+	}
+	return 0;
+}
+
+static void
+pcilhinv(Pcidev* p)
+{
+	int i;
+	Pcidev *t;
+
+	if(p == nil) {
+		putstrn(PCICONS.output, PCICONS.ptr);
+		p = pciroot;
+		print("bus dev type vid  did intl memory\n");
+	}
+	for(t = p; t != nil; t = t->link) {
+		print("%d  %2d/%d %.2ux %.2ux %.2ux %.4ux %.4ux %3d  ",
+			BUSBNO(t->tbdf), BUSDNO(t->tbdf), BUSFNO(t->tbdf),
+			t->ccrb, t->ccru, t->ccrp, t->vid, t->did, t->intl);
+
+		for(i = 0; i < nelem(p->mem); i++) {
+			if(t->mem[i].size == 0)
+				continue;
+			print("%d:%.8lux %d ", i,
+				t->mem[i].bar, t->mem[i].size);
+		}
+		if(t->ioa.bar || t->ioa.size)
+			print("ioa:%.8lux %d ", t->ioa.bar, t->ioa.size);
+		if(t->mema.bar || t->mema.size)
+			print("mema:%.8lux %d ", t->mema.bar, t->mema.size);
+		if(t->bridge)
+			print("->%d", BUSBNO(t->bridge->tbdf));
+		print("\n");
+	}
+	while(p != nil) {
+		if(p->bridge != nil)
+			pcilhinv(p->bridge);
+		p = p->link;
+	}	
+}
+
+void
+pcihinv(Pcidev* p)
+{
+	if(pcicfgmode == -1)
+		pcicfginit();
+	qlock(&pcicfginitlock);
+	pcilhinv(p);
+	qunlock(&pcicfginitlock);
+}
+
+void
+pcishutdown(void)
+{
+	Pcidev *p;
+
+	if(pcicfgmode == -1)
+		pcicfginit();
+
+	for(p = pcilist; p != nil; p = p->list){
+		/* don't mess with the bridges */
+		if(p->ccrb == 0x06)
+			continue;
+		pciclrbme(p);
+	}
+}
+
+void
+pcisetbme(Pcidev* p)
+{
+	int pcr;
+
+	pcr = pcicfgr16(p, PciPCR);
+	pcr |= MASen;
+	pcicfgw16(p, PciPCR, pcr);
+}
+
+void
+pciclrbme(Pcidev* p)
+{
+	int pcr;
+
+	pcr = pcicfgr16(p, PciPCR);
+	pcr &= ~MASen;
+	pcicfgw16(p, PciPCR, pcr);
+}
+
+/*
+ * KS8695P specific
+ */
+
+typedef struct Pciahb Pciahb;
+struct Pciahb {
+	ulong	pbm;	/* bridge mode */
+	ulong	pbcs;	/* control and status */
+	ulong	pmba;	/* memory base address */
+	ulong	pmbac;	/* memory base address control */
+	ulong	pmbam;	/* memory base address mask */
+	ulong	pmbat;	/* memory base address translation */
+	ulong	pioba;	/* i/o base address */
+	ulong	piobac;	/* i/o base address control */
+	ulong	piobam;	/* i/o base address mask */
+	ulong	piobat;	/* i/o base address translation */
+};
+
+enum {
+	/* pbm */
+	PciHost=	1<<31,	/* host bridge mode */
+	PciModePCI=	0<<29,
+	PciModeMini=	1<<29,
+	PciModeCbus=	2<<29,
+
+	/* pbcs */
+	PciReset=	1<<31,
+	PciPF4=	0<<29,	/* prefetch 4, 8 or 16 words */
+	PciPF8=	1<<29,
+	PciPF16=	2<<29,
+
+	/* pmbac, piobac */
+	PciTranslate=	1<<31,	/* enable downstream address translation */
+};
+
+static void
+pcidumpdev(ulong tbdf)
+{
+	int i;
+
+	for(i=0; i<0x40; i+=4)
+		iprint("[%.2x]=%.8ux\n", i, pcicfgrw32(tbdf, i, 0, 1));
+}
+
+void
+pcimapinit(void)
+{
+	Pciahb *pm;
+	int i;
+
+	pm = KADDR(PHYSBRIDGE+0x200);
+	if(1){
+		/* see what the bootstrap left */
+		ulong *p;
+
+		p = (ulong*)pm;
+		iprint("PCI:\n");
+		for(i=0; i<10; i++)
+			iprint("%8.8lux: %8.8lux\n", p+i, p[i]);
+	}
+#ifdef NOT
+	/* TO DO: soft reset */
+	putdcr(Cpc0Srr, Rpci);
+	delay(1);
+	putdcr(Cpc0Srr, 0);
+#endif
+	pm->pbcs = 0x30000000;	/* prefetch limit 8 words; pci config access disable */
+
+	pcicfg = KADDR(PHYSBRIDGE+0x100);
+
+	/*
+	 * AHB addresses between PHYSPCIBRIDGE and PHYSPCIBRIDGE+64Mb
+	 * are mapped to PCI memory at 0.
+	 */
+	pcimem = mmuphysmap(KADDR(PHYSPCIBRIDGE), PHYSPCIBRIDGE, 0x4000000);
+	pm->pmbac = 0;	/* disable during update */
+	pm->pmba = PHYSPCIBRIDGE;
+	pm->pmbam = 0xFC000000;
+	pm->pmbat = 0;	/* TO DO: check */
+	pm->pmbac = PciTranslate;	/* enable */
+
+	/*
+	 * AHB addresses between PHYSPCIIO and PHYSPCIIO+64Mb
+	 * are mapped to physical memory.
+ 	*/
+	mmuphysmap(KADDR(PHYSPCIIO), PHYSPCIIO, 64*1024);
+	pm->piobac = 0;	/* disable during update */
+	pm->pioba = PHYSPCIIO;
+	pm->piobam = ~(64*1024-1);
+	pm->piobat = 0;	/* TO DO: check */
+	pm->piobac = PciTranslate;	/* enable */
+
+	pcicfgmode = -1;
+}
+
+static void
+pcirootmap(Pcidev *bridge)
+{
+	ulong pcsr;
+
+	/*
+	 * addresses presented by a PCI device between PCIWINDOW and PCIWINDOW+64Mb
+	 * are mapped to physical memory.
+ 	*/
+	pcsr = pcicfgr32(bridge, PciPSR);
+iprint("pcsr0=%8.8lux\n", pcsr);
+	pcicfgw32(bridge, PciPSR, pcsr);	/* reset error status */
+	pcsr = pcicfgr32(bridge, PciPSR);
+iprint("pcsr1=%8.8lux\n", pcsr);
+	pcicfgw32(bridge, PciBAR0, PCIWINDOW);
+}
+
+typedef struct Pciroute Pciroute;
+struct Pciroute {
+	int	slot;
+	int	pin;
+	int	irq;
+};
+
+static Pciroute pciroutes[] = {
+	{0,	1,	IRQext0},	/* bridge */
+	{5,	1,	IRQext3},	/* USB */
+	{5,	2,	IRQext1},
+	{5,	3,	IRQext2},
+//	{6,	0,	IRQext3},	/* miniPCI0 (or RTL8139 for extra WAN?) */
+	{6,	1,	IRQext0},	/* RTL8139 for extra WAN */
+	{7,	0,	IRQext3},	/* miniPCI1 */
+	{-1,	0,	IRQext0},
+};
+
+static void
+pcirouting(void)
+{
+	int i, pin, irq;
+	Pcidev *pci;
+	Pciroute *r;
+
+	for(pci = pcilist; pci != nil; pci = pci->list){
+		pin = pcicfgr8(pci, PciINTP);
+		if(pin == 0 || pin == 0xff) 
+			continue;
+		irq = -1;
+		for(i=0; i<nelem(pciroutes); i++){
+			r = &pciroutes[i];
+			if(r->slot < 0 || r->slot == BUSDNO(pci->tbdf) && (r->pin == 0 || r->pin == pin)){
+				irq = r->irq;
+				break;
+			}
+		}
+		if(irq < 0)
+			continue;
+		irq |= IRQactivelow;
+		iprint("pcirouting: %Y at pin %d ", pci->tbdf, pin);
+		if(pci->intl != 0 && pci->intl != 0xFF && pci->intl != irq)
+			iprint("irq %d -> %d\n", pci->intl, irq);
+		else
+			iprint("irq %d\n", irq);
+		pcicfgw8(pci, PciINTL, irq);
+		pci->intl = irq;
+	}
+}
binary files /dev/null b/os/manga/pinflate differ
--- /dev/null
+++ b/os/manga/trap.c
@@ -1,0 +1,498 @@
+#include	"u.h"
+#include	"../port/lib.h"
+#include	"mem.h"
+#include	"dat.h"
+#include	"fns.h"
+#include	"io.h"
+#include	"ureg.h"
+#include	"../port/error.h"
+
+#define waslo(sr) (!((sr) & (PsrDirq|PsrDfiq)))
+
+enum 
+{
+	MaxVector=	32,	/* determined by bits per word */
+	Maxhandler=	MaxVector+5		/* max number of interrupt handlers, assuming a few shared */
+};
+
+typedef struct Handler Handler;
+struct Handler {
+	void	(*r)(Ureg*, void*);
+	void*	a;
+	char	name[KNAMELEN];
+	Handler*	next;
+	int	edge;
+	ulong	nintr;
+	ulong	ticks;
+	int	maxtick;
+};
+
+static Lock veclock;
+
+static struct
+{
+	Handler	*ivec[MaxVector];
+	Handler	h[Maxhandler];
+	int	free;
+	Handler*	freelist;
+} halloc;
+
+Instr BREAK = 0xE6BAD010;
+
+int (*breakhandler)(Ureg*, Proc*);
+int (*catchdbg)(Ureg *, uint);
+
+extern void (*serwrite)(char *, int);
+
+void
+intrenable(int sort, int v, void (*r)(Ureg*, void*), void* a, char *name)
+{
+	int o, x;
+	ulong f;
+	GpioReg *g;
+	Handler *h;
+
+	USED(sort);
+	f = v;
+	v &= IRQmask;
+	if(v >= nelem(halloc.ivec))
+		panic("intrenable(%d)", v);
+	ilock(&veclock);
+	if(v >= IRQext0 && v <= IRQtm1){
+		/* need to switch GPIO pins, set mode */
+		g = GPIOREG;
+		if(v <= IRQext3){	/* choice of interrupt type */
+			o = (v-IRQext0)*4;	/* b mmm */
+			g->iopc = (g->iopc & ~(7<<o)) | (1<<(o+3)) | ((f>>8)&7)<<o;
+			o = v - IRQext0;
+			if(f & IRQsoft)
+				g->iopm |= 1<<o;	/* soft interrupt uses GPIO as output */
+			else
+				g->iopm &= ~(1<<o);
+		}else
+			g->iopc |= 1<<(16+(v-IRQtm0));
+		if(0)
+			iprint("v=%d iopc=%8.8lux iopm=%8.8lux\n", v, g->iopc, g->iopm);
+	}
+	if((h = halloc.freelist) == nil){
+		if(halloc.free >= Maxhandler){
+			iunlock(&veclock);
+			panic("out of interrupt handlers");	/* can't happen */
+		}
+		h = &halloc.h[halloc.free++];
+	}else
+		halloc.freelist = h->next;
+	h->r = r;
+	h->a = a;
+	strncpy(h->name, name, KNAMELEN-1);
+	h->name[KNAMELEN-1] = 0;
+	h->next = halloc.ivec[v];
+	halloc.ivec[v] = h;
+
+	/* enable the corresponding interrupt in the controller */
+	x = splfhi();
+	INTRREG->st = 1<<v;
+	INTRREG->en |= 1<<v;
+	splx(x);
+	iunlock(&veclock);
+}
+
+void
+intrdisable(int sort, int v, void (*r)(Ureg*, void*), void* a, char *name)
+{
+	int x, o;
+	GpioReg *g;
+	Handler *h, **hp;
+
+	USED(sort);
+	v &= IRQmask;
+	if(v >= nelem(halloc.ivec))
+		panic("intrdisable(%d)", v);
+	ilock(&veclock);
+	for(hp = &halloc.ivec[v]; (h = *hp) != nil; hp = &h->next)
+		if(h->r == r && h->a == a && strcmp(h->name, name) == 0){
+			*hp = h->next;
+			h->r = nil;
+			h->next = halloc.freelist;
+			halloc.freelist = h;
+			break;
+		}
+	if(halloc.ivec[v] == nil){
+		if(v >= IRQext0 && v <= IRQtm1){
+			/* need to reset GPIO pins */
+			g = GPIOREG;
+			if(v <= IRQext3){	/* choice of interrupt type */
+				o = (v-IRQext0)*4;	/* b mmm */
+				g->iopc &= ~(0xF<<o);
+				g->iopm &= ~(v-IRQext0);	/* force to input */
+			}else
+				g->iopc &= ~(1<<(16+(v-IRQtm0)));
+		}
+		x = splfhi();
+		INTRREG->en &= ~(1<<v);
+		splx(x);
+	}
+	iunlock(&veclock);
+}
+
+static void
+intrs(Ureg *ur, ulong ibits)
+{
+	Handler *h;
+	int i, s;
+
+	for(i=0; i<nelem(halloc.ivec) && ibits; i++)
+		if(ibits & (1<<i)){
+			h = halloc.ivec[i];
+			for(; h != nil; h = h->next){
+				INTRREG->st = 1<<i;		/* reset edge; has no effect on level interrupts */
+				h->r(ur, h->a);
+				ibits &= ~(1<<i);
+			}
+		}
+	if(ibits != 0){
+		iprint("spurious irq interrupt: %8.8lux\n", ibits);
+		s = splfhi();
+		INTRREG->en &= ~ibits;
+		splx(s);
+	}
+}
+
+/*
+ * initialise R13 in each trap mode, at the start and after suspend reset.
+ */
+void
+trapstacks(void)
+{
+	setr13(PsrMfiq, m->fiqstack+nelem(m->fiqstack));
+	setr13(PsrMirq, m->irqstack+nelem(m->irqstack));
+	setr13(PsrMabt, m->abtstack+nelem(m->abtstack));
+	setr13(PsrMund, m->undstack+nelem(m->undstack));
+}
+
+void
+trapinit(void)
+{
+	IntrReg *intr;
+
+	intr = INTRREG;
+	intr->mc = 0;	/* all IRQ not FIQ */
+	intr->en = 0;	/* disable everything */
+	intr->st = intr->st;	/* reset edges */
+
+	trapstacks();
+
+	memmove(page0->vectors, vectors, sizeof(page0->vectors));
+	memmove(page0->vtable, vtable, sizeof(page0->vtable));
+	dcflush(page0, sizeof(*page0));
+
+	icflushall();
+}
+
+static char *trapnames[PsrMask+1] = {
+	[ PsrMfiq ] "Fiq interrupt",
+	[ PsrMirq ] "Mirq interrupt",
+	[ PsrMsvc ] "SVC/SWI Exception",
+	[ PsrMabt ] "Prefetch Abort/Data Abort",
+	[ PsrMabt+1 ] "Data Abort",
+	[ PsrMund ] "Undefined instruction",
+	[ PsrMsys ] "Sys trap"
+};
+
+static char *
+trapname(int psr)
+{
+	char *s;
+
+	s = trapnames[psr & PsrMask];
+	if(s == nil)
+		s = "Undefined trap";
+	return s;
+}
+
+static void
+sys_trap_error(int type)
+{
+	char errbuf[ERRMAX];
+	sprint(errbuf, "sys: trap: %s\n", trapname(type));
+	error(errbuf);
+}
+
+static void
+faultarm(Ureg *ureg, ulong far)
+{
+	char buf[ERRMAX];
+
+	sprint(buf, "sys: trap: fault pc=%8.8lux addr=0x%lux", (ulong)ureg->pc, far);
+	if(1){
+		iprint("%s\n", buf);
+		dumpregs(ureg);
+	}
+	if(far == ~0)
+		disfault(ureg, "dereference of nil");
+	disfault(ureg, buf);
+}
+
+/*
+ *  All traps come here.  It might be slightly slower to have all traps call trap
+ *  rather than directly vectoring the handler.
+ *  However, this avoids a lot of code duplication and possible bugs.
+ *  trap is called splfhi().
+ */
+void
+trap(Ureg* ureg)
+{
+	ulong far, fsr;
+	int t, itype;
+	Proc *oup;
+
+	/*
+	 * All interrupts/exceptions should be resumed at ureg->pc-4,
+	 * except for Data Abort which resumes at ureg->pc-8.
+	 */
+	itype = ureg->type;
+	if(itype == PsrMabt+1)
+		ureg->pc -= 8;
+	else
+		ureg->pc -= 4;
+	ureg->sp = (ulong)(ureg+1);
+	if(itype == PsrMfiq){	/* fast interrupt (eg, profiler) */
+		oup = up;
+		up = nil;
+		intrs(ureg, INTRREG->ms & INTRREG->mc);	/* just FIQ ones */
+		up = oup;
+		return;
+	}
+
+	/* All other traps */
+
+	if(up){
+		up->pc = ureg->pc;
+		up->dbgreg = ureg;
+	}
+	switch(itype) {
+	case PsrMirq:
+		t = m->ticks;	/* CPU time per proc */
+		up = nil;		/* no process at interrupt level */
+		splflo();	/* allow fast interrupts */
+		intrs(ureg, INTRREG->ms & ~INTRREG->mc);	/* just IRQ */
+		up = m->proc;
+		preemption(m->ticks - t);
+		break;
+
+	case PsrMund:				/* Undefined instruction */
+		if(*(ulong*)ureg->pc == BREAK && breakhandler) {
+			int s;
+			Proc *p;
+
+			p = up;
+			/* if(!waslo(ureg->psr) || ureg->pc >= (ulong)splhi && ureg->pc < (ulong)islo)
+				p = 0; */
+			s = breakhandler(ureg, p);
+			if(s == BrkSched) {
+				p->preempted = 0;
+				sched();
+			} else if(s == BrkNoSched) {
+				p->preempted = 1;	/* stop it being preempted until next instruction */
+				if(up)
+					up->dbgreg = 0;
+				return;
+			}
+			break;
+		}
+		if(up == nil)
+			goto faultpanic;
+		spllo();
+		if(waserror()) {
+			if(waslo(ureg->psr) && up->type == Interp)
+				disfault(ureg, up->env->errstr);
+			setpanic();
+			dumpregs(ureg);
+			panic("%s", up->env->errstr);
+		}
+		if(!fpiarm(ureg)) {
+			dumpregs(ureg);
+			sys_trap_error(ureg->type);
+		}
+		poperror();
+		break;
+
+	case PsrMsvc:				/* Jump through 0 or SWI */
+		if(waslo(ureg->psr) && up && up->type == Interp) {
+			spllo();
+			dumpregs(ureg);
+			sys_trap_error(ureg->type);
+		}
+		setpanic();
+		dumpregs(ureg);
+		panic("SVC/SWI exception");
+		break;
+
+	case PsrMabt:				/* Prefetch abort */
+		if(catchdbg && catchdbg(ureg, 0))
+			break;
+		/* FALL THROUGH */
+	case PsrMabt+1:			/* Data abort */
+		fsr = mmugetfsr();
+		far = mmugetfar();
+		if(fsr & (1<<9)) {
+			mmuputfsr(fsr & ~(1<<9));
+			if(catchdbg && catchdbg(ureg, fsr))
+				break;
+			print("Debug/");
+		}
+		if(waslo(ureg->psr) && up && up->type == Interp) {
+			spllo();
+			faultarm(ureg, far);
+		}
+		iprint("Data Abort: FSR %8.8luX FAR %8.8luX\n", fsr, far); xdelay(500);serialputs("\n", 1);
+		/* FALL THROUGH */
+
+	default:				/* ??? */
+faultpanic:
+		setpanic();
+		dumpregs(ureg);
+		panic("exception %uX %s\n", ureg->type, trapname(ureg->type));
+		break;
+	}
+
+	splhi();
+	if(up)
+		up->dbgreg = 0;		/* becomes invalid after return from trap */
+}
+
+void
+setpanic(void)
+{
+	if(breakhandler != 0)	/* don't mess up debugger */
+		return;
+/*
+	INTRREG->en = 0;
+	spllo();
+*/
+	splhi();
+	GPIOREG->iopd &= ~(1<<GPIO_status_orange_o);
+	consoleprint = 1;
+	serwrite = serialputs;
+}
+
+int
+isvalid_va(void *v)
+{
+	return (ulong)v >= KZERO && (ulong)v <= (ulong)KADDR(conf.topofmem-1);
+}
+
+void
+dumplongs(char *msg, ulong *v, int n)
+{
+	int i, l;
+
+	l = 0;
+	iprint("%s at %.8p: ", msg, v);
+	for(i=0; i<n; i++){
+		if(l >= 4){
+			iprint("\n    %.8p: ", v);
+			l = 0;
+		}
+		if(isvalid_va(v)){
+			iprint(" %.8lux", *v++);
+			l++;
+		}else{
+			iprint(" invalid");
+			break;
+		}
+	}
+	iprint("\n");
+}
+
+static void
+_dumpstack(Ureg *ureg)
+{
+	ulong v, *l, *estack;
+	int i;
+
+	l = (ulong*)(ureg+1);
+	if((ulong)l & 3){
+		iprint("invalid ureg/stack: %.8p\n", l);
+		return;
+	}
+	iprint("dumpstack\n");
+	print("ktrace /kernel/path %.8ux %.8ux %.8ux\n", ureg->pc, ureg->sp, ureg->r14);
+	if(up != nil && l >= (ulong*)up->kstack && l <= (ulong*)(up->kstack+KSTACK-4))
+		estack = (ulong*)(up->kstack+KSTACK);
+	else if(l >= (ulong*)m->stack && l <= (ulong*)((ulong)m+BY2PG-4))
+		estack = (ulong*)((ulong)m+BY2PG-4);
+	else{
+		iprint("unknown stack %8.8p\n", l);
+		return;
+	}
+	iprint("estackx %8.8p\n", estack);
+	i = 0;
+	for(; l<estack; l++) {
+		v = *l;
+		if(KTZERO < v && v < (ulong)etext){
+			iprint("%8.8p=%8.8lux ", l, v);
+			if(i++ == 4){
+				iprint("\n");
+				i = 0;
+			}
+		}
+	}
+	if(i)
+		print("\n");
+}
+
+void
+dumpregs(Ureg* ureg)
+{
+	print("TRAP: %s", trapname(ureg->type));
+	if((ureg->psr & PsrMask) != PsrMsvc)
+		print(" in %s", trapname(ureg->psr));
+	print("\n");
+	print("PSR %8.8uX type %2.2uX PC %8.8uX LINK %8.8uX\n",
+		ureg->psr, ureg->type, ureg->pc, ureg->link);
+	print("R14 %8.8uX R13 %8.8uX R12 %8.8uX R11 %8.8uX R10 %8.8uX\n",
+		ureg->r14, ureg->r13, ureg->r12, ureg->r11, ureg->r10);
+	print("R9  %8.8uX R8  %8.8uX R7  %8.8uX R6  %8.8uX R5  %8.8uX\n",
+		ureg->r9, ureg->r8, ureg->r7, ureg->r6, ureg->r5);
+	print("R4  %8.8uX R3  %8.8uX R2  %8.8uX R1  %8.8uX R0  %8.8uX\n",
+		ureg->r4, ureg->r3, ureg->r2, ureg->r1, ureg->r0);
+	print("Stack is at: %8.8luX\n", ureg);
+	print("PC %8.8lux LINK %8.8lux\n", (ulong)ureg->pc, (ulong)ureg->link);
+
+	if(up)
+		print("Process stack:  %8.8lux-%8.8lux\n",
+			up->kstack, up->kstack+KSTACK-4);
+	else
+		print("System stack: %8.8lux-%8.8lux\n",
+			(ulong)(m+1), (ulong)m+BY2PG-4);
+	dumplongs("stack", (ulong *)(ureg + 1), 16);
+	_dumpstack(ureg);
+}
+
+/*
+ * Fill in enough of Ureg to get a stack trace, and call a function.
+ * Used by debugging interface rdb.
+ */
+void
+callwithureg(void (*fn)(Ureg*))
+{
+	Ureg ureg;
+	ureg.pc = getcallerpc(&fn);
+	ureg.sp = (ulong)&fn;
+	ureg.r14 = 0;
+	fn(&ureg);
+}
+
+void
+dumpstack(void)
+{
+return;
+	callwithureg(_dumpstack);
+}
+
+void
+trapspecial(int (*f)(Ureg *, uint))
+{
+	catchdbg = f;
+}
--- /dev/null
+++ b/os/manga/uartks8695.c
@@ -1,0 +1,629 @@
+#include "u.h"
+#include "../port/lib.h"
+#include "mem.h"
+#include "dat.h"
+#include "fns.h"
+#include "io.h"
+#include "../port/error.h"
+
+#include "../port/uart.h"
+
+/*
+ * KS8695 uart; similar to 8250 etc but registers are slightly different,
+ * and interrupt control is quite different
+ */
+enum {
+	UartFREQ	= CLOCKFREQ,
+};
+
+/*
+ * similar to i8250/16450/16550 (slight differences)
+ */
+
+enum {					/* I/O ports */
+	Rbr		= 0,		/* Receiver Buffer (RO) */
+	Thr		= 1,		/* Transmitter Holding (WO) */
+	Fcr		= 2,		/* FIFO Control  */
+	Lcr		= 3,		/* Line Control */
+	Mcr		= 4,		/* Modem Control */
+	Lsr		= 5,		/* Line Status */
+	Msr		= 6,		/* Modem Status */
+	Div		= 7,		/* Divisor  */
+	Usr		= 8,		/* Status */
+};
+
+enum {					/* Fcr */
+	FIFOena		= 0x01,		/* FIFO enable */
+	FIFOrclr	= 0x02,		/* clear Rx FIFO */
+	FIFOtclr	= 0x04,		/* clear Tx FIFO */
+	FIFO1		= 0x00,		/* Rx FIFO trigger level 1 byte */
+	FIFO4		= 0x40,		/*	4 bytes */
+	FIFO8		= 0x80,		/*	8 bytes */
+	FIFO14		= 0xC0,		/*	14 bytes */
+};
+
+enum {					/* Lcr */
+	Wls5		= 0x00,		/* Word Length Select 5 bits/byte */
+	Wls6		= 0x01,		/*	6 bits/byte */
+	Wls7		= 0x02,		/*	7 bits/byte */
+	Wls8		= 0x03,		/*	8 bits/byte */
+	WlsMASK		= 0x03,
+	Stb		= 0x04,		/* 2 stop bits */
+	Pen		= 0x08,		/* Parity Enable */
+	Eps		= 0x10,		/* Even Parity Select */
+	Stp		= 0x20,		/* Stick Parity */
+	Brk		= 0x40,		/* Break */
+	Dlab		= 0x80,		/* Divisor Latch Access Bit */
+};
+
+enum {					/* Mcr */
+	Dtr		= 0x01,		/* Data Terminal Ready */
+	Rts		= 0x02,		/* Ready To Send */
+	Out1		= 0x04,		/* UART OUT1 asserted */
+	Out2		= 0x08,		/* UART OUT2 asserted */
+	Dm		= 0x10,		/* Diagnostic Mode loopback */
+};
+
+enum {					/* Lsr */
+	Dr		= 0x01,		/* Data Ready */
+	Oe		= 0x02,		/* Overrun Error */
+	Pe		= 0x04,		/* Parity Error */
+	Fe		= 0x08,		/* Framing Error */
+	Bi		= 0x10,		/* Break Interrupt */
+	Thre		= 0x20,		/* Thr Empty */
+	Temt		= 0x40,		/* Tramsmitter Empty */
+	FIFOerr		= 0x80,		/* error in receiver FIFO */
+	LsrInput		= FIFOerr|Oe|Pe|Fe|Dr|Bi,	/* input status only */
+};
+
+enum {					/* Msr */
+	Dcts		= 0x01,		/* Delta Cts */
+	Ddsr		= 0x02,		/* Delta Dsr */
+	Teri		= 0x04,		/* Trailing Edge of Ri */
+	Ddcd		= 0x08,		/* Delta Dcd */
+	Cts		= 0x10,		/* Clear To Send */
+	Dsr		= 0x20,		/* Data Set Ready */
+	Ri		= 0x40,		/* Ring Indicator */
+	Dcd		= 0x80,		/* Data Set Ready */
+};
+
+enum {					/* Usr */
+	Uti		= 0x01,		/* INTST[9]=1=> =1, interrupt is timeout; =0, receive FIFO trigger */
+};
+
+typedef struct Ctlr {
+	ulong*	regs;
+	int	irq;
+	int	iena;
+
+	Lock;
+	int	fena;
+} Ctlr;
+
+extern PhysUart ks8695physuart;
+
+
+static Ctlr ks8695_ctlr[1] = {
+{	.regs	= (ulong*)PHYSUART,
+	.irq	= IRQuts,	/* base: ts then rs, ls, ms */
+},
+};
+
+static Uart ks8695_uart[1] = {
+{	.regs	= &ks8695_ctlr[0],
+	.name	= "eia0",
+	.freq	= UartFREQ,
+	.phys	= &ks8695physuart,
+	.special= 0,
+	.next	= nil, },
+};
+
+#define csr8r(c, r)	((c)->regs[(r)])
+#define csr8w(c, r, v)	((c)->regs[(r)] = (v))
+
+static long
+ks8695_status(Uart* uart, void* buf, long n, long offset)
+{
+	char *p;
+	Ctlr *ctlr;
+	uchar ier, lcr, mcr, msr;
+
+	ctlr = uart->regs;
+	p = malloc(READSTR);
+	mcr = csr8r(ctlr, Mcr);
+	msr = csr8r(ctlr, Msr);
+	ier = INTRREG->en;
+	lcr = csr8r(ctlr, Lcr);
+	snprint(p, READSTR,
+		"b%d c%d d%d e%d l%d m%d p%c r%d s%d i%d ier=%ux\n"
+		"dev(%d) type(%d) framing(%d) overruns(%d)%s%s%s%s\n",
+
+		uart->baud,
+		uart->hup_dcd, 
+		(msr & Dsr) != 0,
+		uart->hup_dsr,
+		(lcr & WlsMASK) + 5,
+		(ier & (1<<IRQums)) != 0, 
+		(lcr & Pen) ? ((lcr & Eps) ? 'e': 'o'): 'n',
+		(mcr & Rts) != 0,
+		(lcr & Stb) ? 2: 1,
+		ctlr->fena,
+		ier,
+
+		uart->dev,
+		uart->type,
+		uart->ferr,
+		uart->oerr, 
+		(msr & Cts) ? " cts": "",
+		(msr & Dsr) ? " dsr": "",
+		(msr & Dcd) ? " dcd": "",
+		(msr & Ri) ? " ring": ""
+	);
+	n = readstr(offset, buf, n, p);
+	free(p);
+
+	return n;
+}
+
+static void
+ks8695_fifo(Uart* uart, int level)
+{
+	Ctlr *ctlr;
+
+	ctlr = uart->regs;
+
+	/*
+	 * Changing the FIFOena bit in Fcr flushes data
+	 * from both receive and transmit FIFOs; there's
+	 * no easy way to guarantee not losing data on
+	 * the receive side, but it's possible to wait until
+	 * the transmitter is really empty.
+	 */
+	ilock(ctlr);
+	while(!(csr8r(ctlr, Lsr) & Temt))
+		;
+
+	/*
+	 * Set the trigger level, default is the max.
+	 * value.
+	 */
+	ctlr->fena = level;
+	switch(level){
+	case 0:
+		break;
+	case 1:
+		level = FIFO1|FIFOena;
+		break;
+	case 4:
+		level = FIFO4|FIFOena;
+		break;
+	case 8:
+		level = FIFO8|FIFOena;
+		break;
+	default:
+		level = FIFO14|FIFOena;
+		break;
+	}
+	csr8w(ctlr, Fcr, level);
+	iunlock(ctlr);
+}
+
+static void
+ks8695_dtr(Uart* uart, int on)
+{
+	Ctlr *ctlr;
+	int r;
+
+	/*
+	 * Toggle DTR.
+	 */
+	ctlr = uart->regs;
+	r = csr8r(ctlr, Mcr);
+	if(on)
+		r |= Dtr;
+	else
+		r &= ~Dtr;
+	csr8w(ctlr, Mcr, r);
+}
+
+static void
+ks8695_rts(Uart* uart, int on)
+{
+	Ctlr *ctlr;
+	int r;
+
+	/*
+	 * Toggle RTS.
+	 */
+	ctlr = uart->regs;
+	r = csr8r(ctlr, Mcr);
+	if(on)
+		r |= Rts;
+	else
+		r &= ~Rts;
+	csr8w(ctlr, Mcr, r);
+}
+
+static void
+ks8695_modemctl(Uart* uart, int on)
+{
+	Ctlr *ctlr;
+
+	ctlr = uart->regs;
+	ilock(&uart->tlock);
+	if(on){
+		INTRREG->en |= 1<<IRQums;	/* TO DO */
+		uart->modem = 1;
+		uart->cts = csr8r(ctlr, Msr) & Cts;
+	}
+	else{
+		INTRREG->en &= ~(1<<IRQums);
+		uart->modem = 0;
+		uart->cts = 1;
+	}
+	iunlock(&uart->tlock);
+
+	/* modem needs fifo */
+	(*uart->phys->fifo)(uart, on);
+}
+
+static int
+ks8695_parity(Uart* uart, int parity)
+{
+	int lcr;
+	Ctlr *ctlr;
+
+	ctlr = uart->regs;
+	lcr = csr8r(ctlr, Lcr) & ~(Eps|Pen);
+
+	switch(parity){
+	case 'e':
+		lcr |= Eps|Pen;
+		break;
+	case 'o':
+		lcr |= Pen;
+		break;
+	case 'n':
+	default:
+		break;
+	}
+	csr8w(ctlr, Lcr, lcr);
+
+	uart->parity = parity;
+
+	return 0;
+}
+
+static int
+ks8695_stop(Uart* uart, int stop)
+{
+	int lcr;
+	Ctlr *ctlr;
+
+	ctlr = uart->regs;
+	lcr = csr8r(ctlr, Lcr);
+	switch(stop){
+	case 1:
+		lcr &= ~Stb;
+		break;
+	case 2:
+		lcr |= Stb;
+		break;
+	default:
+		return -1;
+	}
+	csr8w(ctlr, Lcr, lcr);
+	uart->stop = stop;
+	return 0;
+}
+
+static int
+ks8695_bits(Uart* uart, int bits)
+{
+	int lcr;
+	Ctlr *ctlr;
+
+	ctlr = uart->regs;
+	lcr = csr8r(ctlr, Lcr) & ~WlsMASK;
+
+	switch(bits){
+	case 5:
+		lcr |= Wls5;
+		break;
+	case 6:
+		lcr |= Wls6;
+		break;
+	case 7:
+		lcr |= Wls7;
+		break;
+	case 8:
+		lcr |= Wls8;
+		break;
+	default:
+		return -1;
+	}
+	csr8w(ctlr, Lcr, lcr);
+
+	uart->bits = bits;
+
+	return 0;
+}
+
+static int
+ks8695_baud(Uart* uart, int baud)
+{
+	ulong bgc;
+	Ctlr *ctlr;
+
+	if(uart->freq == 0 || baud <= 0)
+		return -1;
+	ctlr = uart->regs;
+	bgc = (uart->freq+baud-1)/baud;
+	csr8w(ctlr, Div, bgc);
+	uart->baud = baud;
+	return 0;
+}
+
+static void
+ks8695_break(Uart* uart, int ms)
+{
+	Ctlr *ctlr;
+	int lcr;
+
+	/*
+	 * Send a break.
+	 */
+	if(ms == 0)
+		ms = 200;
+
+	ctlr = uart->regs;
+	lcr = csr8r(ctlr, Lcr);
+	csr8w(ctlr, Lcr, lcr|Brk);
+	tsleep(&up->sleep, return0, 0, ms);
+	csr8w(ctlr, Lcr, lcr);
+}
+
+static void
+ks8695_kick(Uart* uart)
+{
+	int i;
+	Ctlr *ctlr;
+
+	if(uart->cts == 0 || uart->blocked)
+		return;
+
+	ctlr = uart->regs;
+	for(i = 0; i < 16; i++){
+		if(!(csr8r(ctlr, Lsr) & Thre))
+			break;
+		if(uart->op >= uart->oe && uartstageoutput(uart) == 0)
+			break;
+		csr8w(ctlr, Thr, *uart->op++);
+	}
+}
+
+static void
+ks8695_modemintr(Ureg*, void *arg)
+{
+	Ctlr *ctlr;
+	Uart *uart;
+	int old, r;
+
+	uart = arg;
+	ctlr = uart->regs;
+	r = csr8r(ctlr, Msr);
+	if(r & Dcts){
+		ilock(&uart->tlock);
+		old = uart->cts;
+		uart->cts = r & Cts;
+		if(old == 0 && uart->cts)
+			uart->ctsbackoff = 2;
+		iunlock(&uart->tlock);
+	}
+ 	if(r & Ddsr){
+		old = r & Dsr;
+		if(uart->hup_dsr && uart->dsr && !old)
+			uart->dohup = 1;
+		uart->dsr = old;
+	}
+ 	if(r & Ddcd){
+		old = r & Dcd;
+		if(uart->hup_dcd && uart->dcd && !old)
+			uart->dohup = 1;
+		uart->dcd = old;
+	}
+}
+
+static void
+ks8695_rxintr(Ureg*, void* arg)
+{
+	Ctlr *ctlr;
+	Uart *uart;
+	int lsr, r;
+
+	/* handle line error status here as well */
+	uart = arg;
+	ctlr = uart->regs;
+	while((lsr = csr8r(ctlr, Lsr) & LsrInput) != 0){
+		/*
+		 * Consume any received data.
+		 * If the received byte came in with a break,
+		 * parity or framing error, throw it away;
+		 * overrun is an indication that something has
+		 * already been tossed.
+		 */
+		if(lsr & (FIFOerr|Oe))
+			uart->oerr++;
+		if(lsr & Pe)
+			uart->perr++;
+		if(lsr & Fe)
+			uart->ferr++;
+		if(lsr & Dr){
+			r = csr8r(ctlr, Rbr);
+			if(!(lsr & (Bi|Fe|Pe)))
+				uartrecv(uart, r);
+		}
+	}
+}
+
+static void
+ks8695_txintr(Ureg*, void* arg)
+{
+	uartkick(arg);
+}
+
+static void
+ks8695_disable(Uart* uart)
+{
+	Ctlr *ctlr;
+
+	/*
+ 	 * Turn off DTR and RTS, disable interrupts and fifos.
+	 */
+	(*uart->phys->dtr)(uart, 0);
+	(*uart->phys->rts)(uart, 0);
+	(*uart->phys->fifo)(uart, 0);
+
+	ctlr = uart->regs;
+
+	if(ctlr->iena != 0){
+		intrdisable(IRQ, ctlr->irq, ks8695_txintr, uart, uart->name);
+		intrdisable(IRQ, ctlr->irq+1, ks8695_rxintr, uart, uart->name);
+		intrdisable(IRQ, ctlr->irq+2, ks8695_rxintr, uart, uart->name);
+		intrdisable(IRQ, ctlr->irq+3, ks8695_modemintr, uart, uart->name);
+		ctlr->iena = 0;
+	}
+}
+
+static void
+ks8695_enable(Uart* uart, int ie)
+{
+	Ctlr *ctlr;
+
+	ctlr = uart->regs;
+
+	/*
+ 	 * Enable interrupts and turn on DTR and RTS.
+	 * Be careful if this is called to set up a polled serial line
+	 * early on not to try to enable interrupts as interrupt-
+	 * -enabling mechanisms might not be set up yet.
+	 */
+	if(ctlr->iena == 0 && ie){
+		intrenable(IRQ, ctlr->irq, ks8695_txintr, uart, uart->name);
+		intrenable(IRQ, ctlr->irq+1, ks8695_rxintr, uart, uart->name);
+		intrenable(IRQ, ctlr->irq+2, ks8695_rxintr, uart, uart->name);
+		intrenable(IRQ, ctlr->irq+3, ks8695_modemintr, uart, uart->name);
+		ctlr->iena = 1;
+	}
+
+	(*uart->phys->dtr)(uart, 1);
+	(*uart->phys->rts)(uart, 1);
+}
+
+static Uart*
+ks8695_pnp(void)
+{
+	return ks8695_uart;
+}
+
+static int
+ks8695_getc(Uart *uart)
+{
+	Ctlr *ctlr;
+
+	ctlr = uart->regs;
+	while(!(csr8r(ctlr, Lsr)&Dr))
+		delay(1);
+	return csr8r(ctlr, Rbr);
+}
+
+static void
+ks8695_putc(Uart *uart, int c)
+{
+	serialputc(c);
+#ifdef ROT
+	int i;
+	Ctlr *ctlr;
+
+	ctlr = uart->regs;
+	for(i = 0; !(csr8r(ctlr, Lsr)&Thre) && i < 256; i++)
+		delay(1);
+	csr8w(ctlr, Thr, c);
+	if(c == '\n')
+		while((csr8r(ctlr, Lsr) & Temt) == 0){	/* let fifo drain */
+			/* skip */
+		}
+#endif
+}
+
+PhysUart ks8695physuart = {
+	.name		= "ks8695",
+	.pnp		= ks8695_pnp,
+	.enable		= ks8695_enable,
+	.disable	= ks8695_disable,
+	.kick		= ks8695_kick,
+	.dobreak	= ks8695_break,
+	.baud		= ks8695_baud,
+	.bits		= ks8695_bits,
+	.stop		= ks8695_stop,
+	.parity		= ks8695_parity,
+	.modemctl	= ks8695_modemctl,
+	.rts		= ks8695_rts,
+	.dtr		= ks8695_dtr,
+	.status		= ks8695_status,
+	.fifo		= ks8695_fifo,
+	.getc		= ks8695_getc,
+	.putc		= ks8695_putc,
+};
+
+void
+uartconsole(void)
+{
+	Uart *uart;
+
+	uart = &ks8695_uart[0];
+	(*uart->phys->enable)(uart, 0);
+	uartctl(uart, "b38400 l8 pn s1");
+	consuart = uart;
+	uart->console = 1;
+}
+
+#define	UR(p,r)	((ulong*)(p))[r]
+
+void
+serialputc(int c)
+{
+	ulong *p;
+
+	if(c == 0)
+		return;
+	p = (ulong*)PHYSUART;
+	while((UR(p,Lsr) & Thre) == 0){
+		/* skip */
+	}
+	UR(p,Thr) = c;
+	if(c == '\n')
+		while((UR(p,Lsr) & Temt) == 0){	/* let fifo drain */
+			/* skip */
+		}
+}
+
+/*
+ *  for iprint, just write it
+ */
+void
+serialputs(char *data, int len)
+{
+	ulong *p;
+
+	p = (ulong*)PHYSUART;
+	while(--len >= 0){
+		if(*data == '\n')
+			serialputc('\r');
+		serialputc(*data++);
+	}
+	while((UR(p,Lsr) & Temt) == 0){	/* let fifo drain */
+		/* skip */
+	}
+}
+void (*serwrite)(char*, int) = serialputs;
--- /dev/null
+++ b/os/manga/usb.h
@@ -1,0 +1,160 @@
+typedef struct Ctlr Ctlr;
+typedef struct Endpt Endpt;
+typedef struct Udev Udev;
+typedef struct Usbhost Usbhost;
+
+enum
+{
+	MaxUsb = 4,		/* max number of USB Host Controller Interfaces (Usbhost*) */
+	MaxUsbDev = 32,	/* max number of attached USB devices, including root hub (Udev*) */
+
+	/*
+	 * USB packet definitions...
+ 	*/
+	TokIN = 0x69,
+	TokOUT = 0xE1,
+	TokSETUP = 0x2D,
+
+	/* request type */
+	RH2D = 0<<7,
+	RD2H = 1<<7,
+	Rstandard = 0<<5,
+	Rclass = 1<<5,
+	Rvendor = 2<<5,
+	Rdevice = 0,
+	Rinterface = 1,
+	Rendpt = 2,
+	Rother = 3,
+};
+
+#define Class(csp)		((csp)&0xff)
+#define Subclass(csp)	(((csp)>>8)&0xff)
+#define Proto(csp)		(((csp)>>16)&0xff)
+#define CSP(c, s, p)	((c) | ((s)<<8) | ((p)<<16))
+
+/*
+ * device endpoint
+ */
+struct Endpt
+{
+	Ref;
+	Lock;
+	int		x;		/* index in Udev.ep */
+	int		id;		/* hardware endpoint address */
+	int		maxpkt;	/* maximum packet size (from endpoint descriptor) */
+	int		data01;	/* 0=DATA0, 1=DATA1 */
+	uchar	eof;
+	ulong	csp;
+	uchar	mode;	/* OREAD, OWRITE, ORDWR */
+	uchar	nbuf;	/* number of buffers allowed */
+	uchar	iso;
+	uchar	debug;
+	uchar	active;	/* listed for examination by interrupts */
+	int		setin;
+	/* ISO related: */
+	int		hz;
+	int		remain;	/* for packet size calculations */
+	int		samplesz;
+	int		sched;	/* schedule index; -1 if undefined or aperiodic */
+	int		pollms;	/* polling interval in msec */
+	int		psize;	/* (remaining) size of this packet */
+	int		off;		/* offset into packet */
+	/* Real-time iso stuff */
+	ulong	foffset;	/* file offset (to detect seeks) */
+	ulong	poffset;	/* offset of next packet to be queued */
+	ulong	toffset;	/* offset associated with time */
+	vlong	time;		/* timeassociated with offset */
+	int		buffered;	/* bytes captured but unread, or written but unsent */
+	/* end ISO stuff */
+
+	Udev*	dev;	/* owning device */
+
+	ulong	nbytes;
+	ulong	nblocks;
+
+	void	*private;
+
+	// all the rest could (should?) move to the driver private structure; except perhaps err
+	QLock	rlock;
+	Rendez	rr;
+	Queue*	rq;
+	QLock	wlock;
+	Rendez	wr;
+	Queue*	wq;
+
+	int		ntd;
+	char*	err;		// needs to be global for unstall; fix?
+
+	Endpt*	activef;	/* active endpoint list */
+};
+
+/* device parameters */
+enum
+{
+	/* Udev.state */
+	Disabled = 0,
+	Attached,
+	Enabled,
+	Assigned,
+	Configured,
+
+	/* Udev.class */
+	Noclass = 0,
+	Hubclass = 9,
+};
+
+/*
+ * active USB device
+ */
+struct Udev
+{
+	Ref;
+	Lock;
+	Usbhost	*uh;
+	int		x;		/* index in usbdev[] */
+	int		busy;
+	int		state;
+	int		id;
+	uchar	port;		/* port number on connecting hub */
+	ulong	csp;
+	ushort	vid;		/* vendor id */
+	ushort	did;		/* product id */
+	int		ls;
+	int		npt;
+	Endpt*	ep[16];	/* active end points */
+	Udev*	ports;	/* active ports, if hub */
+	Udev*	next;		/* next device on this hub */
+};
+
+/*
+ * One of these per active Host Controller Interface (HCI)
+ */
+struct Usbhost
+{
+	ISAConf;					/* hardware info */
+	int	tbdf;					/* type+busno+devno+funcno */
+
+	QLock;					/* protects namespace state */
+	int		idgen;			/* version number to distinguish new connections */
+	Udev*	dev[MaxUsbDev];	/* device endpoints managed by this HCI */
+
+	void	(*init)(Usbhost*);
+	void	(*interrupt)(Ureg*, void*);
+
+	void	(*portinfo)(Usbhost*, char*, char*);
+	void	(*portreset)(Usbhost*, int);
+	void	(*portenable)(Usbhost*, int, int);
+
+	void	(*epalloc)(Usbhost*, Endpt*);
+	void	(*epfree)(Usbhost*, Endpt*);
+	void	(*epopen)(Usbhost*, Endpt*);
+	void	(*epclose)(Usbhost*, Endpt*);
+	void	(*epmode)(Usbhost*, Endpt*);
+
+	long	(*read)(Usbhost*, Endpt*, void*, long, vlong);
+	long	(*write)(Usbhost*, Endpt*, void*, long, vlong, int);
+
+	void	*ctlr;
+};
+
+extern void addusbtype(char*, int(*)(Usbhost*));
--- /dev/null
+++ b/os/manga/usbuhci.c
@@ -1,0 +1,1556 @@
+#include	"u.h"
+#include	"../port/lib.h"
+#include	"mem.h"
+#include	"dat.h"
+#include	"fns.h"
+#include	"io.h"
+#include	"../port/error.h"
+
+#include	"usb.h"
+
+#define	todget(x)	0	/* TO DO */
+#define XPRINT if(debug)iprint
+
+static int Chatty = 0;
+static int debug = 0;
+
+static	char	Estalled[] = "usb endpoint stalled";
+
+/*
+ * UHCI interface registers and bits
+ */
+enum
+{
+	/* i/o space */
+	Cmd = 0,
+	Status = 2,
+	Usbintr = 4,
+	Frnum = 6,
+	Flbaseadd = 8,
+	SOFMod = 0xC,
+	Portsc0 = 0x10,
+	Portsc1 = 0x12,
+
+	/* port status */
+	Suspend =		1<<12,
+	PortReset =		1<<9,
+	SlowDevice =		1<<8,
+	ResumeDetect =	1<<6,
+	PortChange =		1<<3,	/* write 1 to clear */
+	PortEnable =		1<<2,
+	StatusChange =	1<<1,	/* write 1 to clear */
+	DevicePresent =	1<<0,
+
+	NFRAME = 	1024,
+	FRAMESIZE=	NFRAME*sizeof(ulong),	/* fixed by hardware; aligned to same */
+
+	Vf =			1<<2,	/* TD only */
+	IsQH =		1<<1,
+	Terminate =	1<<0,
+
+	/* TD.status */
+	SPD =		1<<29,
+	ErrLimit0 =	0<<27,
+	ErrLimit1 =	1<<27,
+	ErrLimit2 =	2<<27,
+	ErrLimit3 =	3<<27,
+	LowSpeed =	1<<26,
+	IsoSelect =	1<<25,
+	IOC =		1<<24,
+	Active =		1<<23,
+	Stalled =		1<<22,
+	DataBufferErr =	1<<21,
+	Babbling =	1<<20,
+	NAKed =		1<<19,
+	CRCorTimeout = 1<<18,
+	BitstuffErr =	1<<17,
+	AnyError = (Stalled | DataBufferErr | Babbling | NAKed | CRCorTimeout | BitstuffErr),
+
+	/* TD.dev */
+	IsDATA1 =	1<<19,
+
+	/* TD.flags (software) */
+	CancelTD=	1<<0,
+	IsoClean=		1<<2,
+};
+
+static struct
+{
+	int	bit;
+	char	*name;
+}
+portstatus[] =
+{
+	{ Suspend,		"suspend", },
+	{ PortReset,		"reset", },
+	{ SlowDevice,		"lowspeed", },
+	{ ResumeDetect,	"resume", },
+	{ PortChange,		"portchange", },
+	{ PortEnable,		"enable", },
+	{ StatusChange,	"statuschange", },
+	{ DevicePresent,	"present", },
+};
+
+typedef struct Ctlr Ctlr;
+typedef struct Endptx Endptx;
+typedef struct QH QH;
+typedef struct TD TD;
+
+/*
+ * software structures
+ */
+struct Ctlr
+{
+	Lock;	/* protects state shared with interrupt (eg, free list) */
+	Ctlr*	next;
+	Pcidev*	pcidev;
+	int	active;
+
+	int	io;
+	ulong*	frames;	/* frame list */
+	ulong*	frameld;	/* real time load on each of the frame list entries */
+	QLock	resetl;	/* lock controller during USB reset */
+
+	TD*	tdpool;
+	TD*	freetd;
+	QH*	qhpool;
+	QH*	freeqh;
+
+	QH*	ctlq;	/* queue for control i/o */
+	QH*	bwsop;	/* empty bandwidth sop (to PIIX4 errata specifications) */
+	QH*	bulkq;	/* queue for bulk i/o (points back to bandwidth sop) */
+	QH*	recvq;	/* receive queues for bulk i/o */
+
+	Udev*	ports[2];
+
+	struct {
+		Lock;
+		Endpt*	f;
+	} activends;
+
+	long		usbints;	/* debugging */
+	long		framenumber;
+	long		frameptr;
+	long		usbbogus;
+};
+
+#define	IN(x)	ins(ctlr->io+(x))
+#define	OUT(x, v)	outs(ctlr->io+(x), (v))
+
+static Ctlr* ctlrhead;
+static Ctlr* ctlrtail;
+
+struct Endptx
+{
+	QH*		epq;	/* queue of TDs for this endpoint */
+
+	/* ISO related: */
+	void*	tdalloc;
+	void*	bpalloc;
+	uchar*	bp0;		/* first block in array */
+	TD	*	td0;		/* first td in array */
+	TD	*	etd;		/* pointer into circular list of TDs for isochronous ept */
+	TD	*	xtd;		/* next td to be cleaned */
+};
+
+/*
+ * UHCI hardware structures, aligned on 16-byte boundary
+ */
+struct TD
+{
+	ulong	link;
+	ulong	status;	/* controller r/w */
+	ulong	dev;
+	ulong	buffer;
+
+	/* software */
+	ulong	flags;
+	union{
+		Block*	bp;		/* non-iso */
+		ulong	offset;	/* iso */
+	};
+	Endpt*	ep;
+	TD*		next;
+};
+#define	TFOL(p)	((TD*)KADDR((ulong)(p) & ~(0xF|PCIWINDOW)))
+
+struct QH
+{
+	ulong	head;
+	ulong	entries;	/* address of next TD or QH to process (updated by controller) */
+
+	/* software */
+	QH*		hlink;
+	TD*		first;
+	QH*		next;		/* free list */
+	TD*		last;
+	ulong	_d1;		/* fillers */
+	ulong	_d2;
+};
+#define	QFOL(p)	((QH*)KADDR((ulong)(p) & ~(0xF|PCIWINDOW)))
+
+static TD *
+alloctd(Ctlr *ctlr)
+{
+	TD *t;
+
+	ilock(ctlr);
+	t = ctlr->freetd;
+	if(t == nil)
+		panic("alloctd");	/* TO DO */
+	ctlr->freetd = t->next;
+	t->next = nil;
+	iunlock(ctlr);
+	t->ep = nil;
+	t->bp = nil;
+	t->status = 0;
+	t->link = Terminate;
+	t->buffer = 0;
+	t->flags = 0;
+	return t;
+}
+
+static void
+freetd(Ctlr *ctlr, TD *t)
+{
+	t->ep = nil;
+	if(t->bp)
+		freeb(t->bp);
+	t->bp = nil;
+	ilock(ctlr);
+	t->buffer = 0xdeadbeef;
+	t->next = ctlr->freetd;
+	ctlr->freetd = t;
+	iunlock(ctlr);
+}
+
+static void
+dumpdata(Block *b, int n)
+{
+	int i;
+
+	XPRINT("\tb %8.8lux[%d]: ", (ulong)b->rp, n);
+	if(n > 16)
+		n = 16;
+	for(i=0; i<n; i++)
+		XPRINT(" %2.2ux", b->rp[i]);
+	XPRINT("\n");
+}
+
+static void
+dumptd(TD *t, int follow)
+{
+	int i, n;
+	char buf[20], *s;
+	TD *t0;
+
+	t0 = t;
+	while(t){
+		i = t->dev & 0xFF;
+		if(i == TokOUT || i == TokSETUP)
+			n = ((t->dev>>21) + 1) & 0x7FF;
+		else if((t->status & Active) == 0)
+			n = (t->status + 1) & 0x7FF;
+		else
+			n = 0;
+		s = buf;
+		if(t->status & Active)
+			*s++ = 'A';
+		if(t->status & Stalled)
+			*s++ = 'S';
+		if(t->status & DataBufferErr)
+			*s++ = 'D';
+		if(t->status & Babbling)
+			*s++ = 'B';
+		if(t->status & NAKed)
+			*s++ = 'N';
+		if(t->status & CRCorTimeout)
+			*s++ = 'T';
+		if(t->status & BitstuffErr)
+			*s++ = 'b';
+		if(t->status & LowSpeed)
+			*s++ = 'L';
+		*s = 0;
+		XPRINT("td %8.8lux: ", t);
+		XPRINT("l=%8.8lux s=%8.8lux d=%8.8lux b=%8.8lux %8.8lux f=%8.8lux\n",
+			t->link, t->status, t->dev, t->buffer, t->bp?(ulong)t->bp->rp:0, t->flags);
+		XPRINT("\ts=%s,ep=%ld,d=%ld,D=%ld\n",
+			buf, (t->dev>>15)&0xF, (t->dev>>8)&0xFF, (t->dev>>19)&1);
+		if(debug && t->bp && (t->flags & CancelTD) == 0)
+			dumpdata(t->bp, n);
+		if(!follow || t->link & Terminate || t->link & IsQH)
+			break;
+		t = TFOL(t->link);
+		if(t == t0)
+			break;	/* looped */
+	}
+}
+
+static TD *
+alloctde(Ctlr *ctlr, Endpt *e, int pid, int n)
+{
+	TD *t;
+	int tog, id;
+
+	t = alloctd(ctlr);
+	id = (e->x<<7)|(e->dev->x&0x7F);
+	tog = 0;
+	if(e->data01 && pid != TokSETUP)
+		tog = IsDATA1;
+	t->ep = e;
+	t->status = ErrLimit3 | Active | IOC;	/* or put IOC only on last? */
+	if(e->dev->ls)
+		t->status |= LowSpeed;
+	t->dev = ((n-1)<<21) | ((id&0x7FF)<<8) | pid | tog;
+	return t;
+}
+
+static QH *
+allocqh(Ctlr *ctlr)
+{
+	QH *qh;
+
+	ilock(ctlr);
+	qh = ctlr->freeqh;
+	if(qh == nil)
+		panic("allocqh");	/* TO DO */
+	ctlr->freeqh = qh->next;
+	qh->next = nil;
+	iunlock(ctlr);
+	qh->head = Terminate;
+	qh->entries = Terminate;
+	qh->hlink = nil;
+	qh->first = nil;
+	qh->last = nil;
+	return qh;
+}
+
+static void
+freeqh(Ctlr *ctlr, QH *qh)
+{
+	ilock(ctlr);
+	qh->next = ctlr->freeqh;
+	ctlr->freeqh = qh;
+	iunlock(ctlr);
+}
+
+static void
+dumpqh(QH *q)
+{
+	int i;
+	QH *q0;
+
+	q0 = q;
+	for(i = 0; q != nil && i < 10; i++){
+		XPRINT("qh %8.8lux: %8.8lux %8.8lux\n", q, q->head, q->entries);
+		if((q->entries & Terminate) == 0)
+			dumptd(TFOL(q->entries), 1);
+		if(q->head & Terminate)
+			break;
+		if((q->head & IsQH) == 0){
+			XPRINT("head:");
+			dumptd(TFOL(q->head), 1);
+			break;
+		}
+		q = QFOL(q->head);
+		if(q == q0)
+			break;	/* looped */
+	}
+}
+
+static void
+queuetd(Ctlr *ctlr, QH *q, TD *t, int vf, char *why)
+{
+	TD *lt;
+
+	for(lt = t; lt->next != nil; lt = lt->next)
+		lt->link = PCIWADDR(lt->next) | vf;
+	lt->link = Terminate;
+	ilock(ctlr);
+	XPRINT("queuetd %s: t=%p lt=%p q=%p first=%p last=%p entries=%.8lux\n",
+		why, t, lt, q, q->first, q->last, q->entries);
+	if(q->first != nil){
+		q->last->link = PCIWADDR(t) | vf;
+		q->last->next = t;
+	}else{
+		q->first = t;
+		q->entries = PCIWADDR(t);
+	}
+	q->last = lt;
+	XPRINT("	t=%p q=%p first=%p last=%p entries=%.8lux\n",
+		t, q, q->first, q->last, q->entries);
+	dumpqh(q);
+	iunlock(ctlr);
+}
+
+static void
+cleantd(Ctlr *ctlr, TD *t, int discard)
+{
+	Block *b;
+	int n, err;
+
+	XPRINT("cleanTD: %8.8lux %8.8lux %8.8lux %8.8lux\n", t->link, t->status, t->dev, t->buffer);
+	if(t->ep != nil && t->ep->debug)
+		dumptd(t, 0);
+	if(t->status & Active)
+		panic("cleantd Active");
+	err = t->status & (AnyError&~NAKed);
+	/* TO DO: on t->status&AnyError, q->entries will not have advanced */
+	if (err) {
+		XPRINT("cleanTD: Error %8.8lux %8.8lux %8.8lux %8.8lux\n", t->link, t->status, t->dev, t->buffer);
+//		print("cleanTD: Error %8.8lux %8.8lux %8.8lux %8.8lux\n", t->link, t->status, t->dev, t->buffer);
+	}
+	switch(t->dev&0xFF){
+	case TokIN:
+		if(discard || (t->flags & CancelTD) || t->ep == nil || t->ep->x!=0&&err){
+			if(t->ep != nil){
+				if(err != 0)
+					t->ep->err = err==Stalled? Estalled: Eio;
+				wakeup(&t->ep->rr);	/* in case anyone cares */
+			}
+			break;
+		}
+		b = t->bp;
+		n = (t->status + 1) & 0x7FF;
+		if(n > b->lim - b->wp)
+			n = 0;
+		b->wp += n;
+		if(Chatty)
+			dumpdata(b, n);
+		t->bp = nil;
+		t->ep->nbytes += n;
+		t->ep->nblocks++;
+		qpass(t->ep->rq, b);	/* TO DO: flow control */
+		wakeup(&t->ep->rr);	/* TO DO */
+		break;
+	case TokSETUP:
+		XPRINT("cleanTD: TokSETUP %lux\n", &t->ep);
+		/* don't really need to wakeup: subsequent IN or OUT gives status */
+		if(t->ep != nil) {
+			wakeup(&t->ep->wr);	/* TO DO */
+			XPRINT("cleanTD: wakeup %lux\n", &t->ep->wr);
+		}
+		break;
+	case TokOUT:
+		/* TO DO: mark it done somewhere */
+		XPRINT("cleanTD: TokOut %lux\n", &t->ep);
+		if(t->ep != nil){
+			if(t->bp){
+				n = BLEN(t->bp);
+				t->ep->nbytes += n;
+				t->ep->nblocks++;
+			}
+			if(t->ep->x!=0 && err != 0)
+				t->ep->err = err==Stalled? Estalled: Eio;
+			if(--t->ep->ntd < 0)
+				panic("cleantd ntd");
+			wakeup(&t->ep->wr);	/* TO DO */
+			XPRINT("cleanTD: wakeup %lux\n", &t->ep->wr);
+		}
+		break;
+	}
+	freetd(ctlr, t);
+}
+
+static void
+cleanq(Ctlr *ctlr, QH *q, int discard, int vf)
+{
+	TD *t, *tp;
+
+	ilock(ctlr);
+	tp = nil;
+	for(t = q->first; t != nil;){
+		XPRINT("cleanq: %8.8lux %8.8lux %8.8lux %8.8lux %8.8lux %8.8lux\n", t->link, t->status, t->dev, t->buffer, t->flags, t->next);
+		if(t->status & Active){
+			if(t->status & NAKed){
+				t->status = (t->status & ~NAKed) | IOC;	/* ensure interrupt next frame */
+				tp = t;
+				t = t->next;
+				continue;
+			}
+			if(t->flags & CancelTD){
+				XPRINT("cancelTD: %8.8lux\n", (ulong)t);
+				t->status = (t->status & ~Active) | IOC;	/* ensure interrupt next frame */
+				tp = t;
+				t = t->next;
+				continue;
+			}
+			tp = t;
+			t = t->next;
+			continue;
+		}
+		t->status &= ~IOC;
+		if (tp == nil) {
+			q->first = t->next;
+			if(q->first != nil)
+				q->entries = PCIWADDR(q->first);
+			else
+				q->entries = Terminate;
+		} else {
+			tp->next = t->next;
+			if (t->next != nil)
+				tp->link = PCIWADDR(t->next) | vf;
+			else
+				tp->link = Terminate;
+		}
+		if (q->last == t)
+			q->last = tp;
+		iunlock(ctlr);
+		cleantd(ctlr, t, discard);
+		ilock(ctlr);
+		if (tp)
+			t = tp->next;
+		else
+			t = q->first;
+		XPRINT("t = %8.8lux\n", t);
+		dumpqh(q);
+	}
+	if(q->first && q->entries != PCIWADDR(q->first)){
+		ctlr->usbbogus++;
+		q->entries = PCIWADDR(q->first);
+	}
+	iunlock(ctlr);
+}
+
+static void
+canceltds(Ctlr *ctlr, QH *q, Endpt *e)
+{
+	TD *t;
+
+	if(q != nil){
+		ilock(ctlr);
+		for(t = q->first; t != nil; t = t->next)
+			if(t->ep == e)
+				t->flags |= CancelTD;
+		iunlock(ctlr);
+		XPRINT("cancel:\n");
+		dumpqh(q);
+	}
+}
+
+static void
+eptcancel(Ctlr *ctlr, Endpt *e)
+{
+	Endptx *x;
+
+	if(e == nil)
+		return;
+	x = e->private;
+	canceltds(ctlr, x->epq, e);
+	canceltds(ctlr, ctlr->ctlq, e);
+	canceltds(ctlr, ctlr->bulkq, e);
+}
+
+static void
+eptactivate(Ctlr *ctlr, Endpt *e)
+{
+	ilock(&ctlr->activends);
+	if(e->active == 0){
+		XPRINT("activate 0x%p\n", e);
+		e->active = 1;
+		e->activef = ctlr->activends.f;
+		ctlr->activends.f = e;
+	}
+	iunlock(&ctlr->activends);
+}
+
+static void
+eptdeactivate(Ctlr *ctlr, Endpt *e)
+{
+	Endpt **l;
+
+	/* could be O(1) but not worth it yet */
+	ilock(&ctlr->activends);
+	if(e->active){
+		e->active = 0;
+		XPRINT("deactivate 0x%p\n", e);
+		for(l = &ctlr->activends.f; *l != e; l = &(*l)->activef)
+			if(*l == nil){
+				iunlock(&ctlr->activends);
+				panic("usb eptdeactivate");
+			}
+		*l = e->activef;
+	}
+	iunlock(&ctlr->activends);
+}
+
+static void
+queueqh(Ctlr *ctlr, QH *qh)
+{
+	QH *q;
+
+	// See if it's already queued
+	for (q = ctlr->recvq->next; q; q = q->hlink)
+		if (q == qh)
+			return;
+	if ((qh->hlink = ctlr->recvq->next) == nil)
+		qh->head = Terminate;
+	else
+		qh->head = PCIWADDR(ctlr->recvq->next) | IsQH;
+	ctlr->recvq->next = qh;
+	ctlr->recvq->entries = PCIWADDR(qh) | IsQH;
+}
+
+static QH*
+qxmit(Ctlr *ctlr, Endpt *e, Block *b, int pid)
+{
+	TD *t;
+	int n, vf;
+	QH *qh;
+	Endptx *x;
+
+	x = e->private;
+	if(b != nil){
+		n = BLEN(b);
+		dcflush(b->rp, n);
+		t = alloctde(ctlr, e, pid, n);
+		t->bp = b;
+		t->buffer = PCIWADDR(b->rp);
+	}else
+		t = alloctde(ctlr, e, pid, 0);
+	ilock(ctlr);
+	e->ntd++;
+	iunlock(ctlr);
+	if(e->debug) pprint("QTD: %8.8lux n=%ld\n", t, b?BLEN(b): 0);
+	vf = 0;
+	if(e->x == 0){
+		qh = ctlr->ctlq;
+		vf = 0;
+	}else if((qh = x->epq) == nil || e->mode != OWRITE){
+		qh = ctlr->bulkq;
+		vf = Vf;
+	}
+	queuetd(ctlr, qh, t, vf, "qxmit");
+	return qh;
+}
+
+/*
+ * allocate receive buffer space on cache-line boundaries
+ */
+static Block*
+clallocb(long n)
+{
+	Block *b;
+
+	b = allocb(n+CACHELINESZ-1);
+	if(b == nil)
+		return b;
+	dcflush(b->base, BALLOC(b));
+	b->wp = b->rp = (uchar*)(((ulong)b->base + CACHELINESZ - 1) & ~(CACHELINESZ-1));
+	return b;
+}
+
+static QH*
+qrcv(Ctlr *ctlr, Endpt *e)
+{
+	TD *t;
+	Block *b;
+	QH *qh;
+	int vf;
+	Endptx *x;
+
+	x = e->private;
+	t = alloctde(ctlr, e, TokIN, e->maxpkt);
+	b = clallocb(e->maxpkt);
+	t->bp = b;
+	t->buffer = PCIWADDR(b->wp);
+	vf = 0;
+	if(e->x == 0){
+		qh = ctlr->ctlq;
+	}else if((qh = x->epq) == nil || e->mode != OREAD){
+		qh = ctlr->bulkq;
+		vf = Vf;
+	}
+	queuetd(ctlr, qh, t, vf, "qrcv");
+	return qh;
+}
+
+static int
+usbsched(Ctlr *ctlr, int pollms, ulong load)
+{
+	int i, d, q;
+	ulong best, worst;
+
+	best = 1000000;
+	q = -1;
+	for (d = 0; d < pollms; d++){
+		worst = 0;
+		for (i = d; i < NFRAME; i++){
+			if (ctlr->frameld[i] + load > worst)
+				worst = ctlr->frameld[i] + load;
+		}
+		if (worst < best){
+			best = worst;
+			q = d;
+		}
+	}
+	return q;
+}
+
+static int
+schedendpt(Ctlr *ctlr, Endpt *e)
+{
+	TD *td;
+	Endptx *x;
+	uchar *bp;
+	int i, id, ix, size, frnum;
+
+	if(!e->iso || e->sched >= 0)
+		return 0;
+
+	if (e->active){
+		return -1;
+	}
+	e->off = 0;
+	e->sched = usbsched(ctlr, e->pollms, e->maxpkt);
+	if(e->sched < 0)
+		return -1;
+
+	x = e->private;
+	if (x->tdalloc || x->bpalloc)
+		panic("usb: tdalloc/bpalloc");
+	x->tdalloc = mallocz(0x10 + NFRAME*sizeof(TD) + CACHELINESZ, 1);
+	x->bpalloc = mallocz(0x10 + e->maxpkt*NFRAME/e->pollms + CACHELINESZ, 1);
+	x->td0 = mmucacheinhib((TD*)(((ulong)x->tdalloc + CACHELINESZ-1) & ~(CACHELINESZ-1)), NFRAME*sizeof(TD) + 0x10);
+	x->bp0 = mmucacheinhib((uchar *)(((ulong)x->bpalloc + CACHELINESZ-1) & ~(CACHELINESZ-1)), e->maxpkt*NFRAME/e->pollms + 0x10);
+	frnum = (IN(Frnum) + 1) & 0x3ff;
+	frnum = (frnum & ~(e->pollms - 1)) + e->sched;
+	x->xtd = &x->td0[(frnum+8)&0x3ff];	/* Next td to finish */
+	x->etd = nil;
+	e->remain = 0;
+	e->nbytes = 0;
+	td = x->td0;
+	for(i = e->sched; i < NFRAME; i += e->pollms){
+		bp = x->bp0 + e->maxpkt*i/e->pollms;
+		td->buffer = PCIWADDR(bp);
+		td->ep = e;
+		td->next = &td[1];
+		ctlr->frameld[i] += e->maxpkt;
+		td++;
+	}
+	td[-1].next = x->td0;
+	for(i = e->sched; i < NFRAME; i += e->pollms){
+		ix = (frnum+i) & 0x3ff;
+		td = &x->td0[ix];
+
+		id = (e->x<<7)|(e->dev->x&0x7F);
+		if (e->mode == OREAD)
+			/* enable receive on this entry */
+			td->dev = ((e->maxpkt-1)<<21) | ((id&0x7FF)<<8) | TokIN;
+		else{
+			size = (e->hz + e->remain)*e->pollms/1000;
+			e->remain = (e->hz + e->remain)*e->pollms%1000;
+			size *= e->samplesz;
+			td->dev = ((size-1)<<21) | ((id&0x7FF)<<8) | TokOUT;
+		}
+		td->status = ErrLimit1 | Active | IsoSelect | IOC;
+		td->link = ctlr->frames[ix];
+		td->flags |= IsoClean;
+		ctlr->frames[ix] = PCIWADDR(td);
+	}
+	return 0;
+}
+
+static void
+unschedendpt(Ctlr *ctlr, Endpt *e)
+{
+	int q;
+	TD *td;
+	Endptx *x;
+	ulong *addr;
+
+	if(!e->iso || e->sched < 0)
+		return;
+
+	x = e->private;
+	if (x->tdalloc == nil)
+		panic("tdalloc");
+	for (q = e->sched; q < NFRAME; q += e->pollms){
+		td = x->td0++;
+		addr = &ctlr->frames[q];
+		while(*addr != PADDR(td)) {
+			if(*addr & IsQH)
+				panic("usb: TD expected");
+			addr = &TFOL(*addr)->link;
+		}
+		*addr = td->link;
+		ctlr->frameld[q] -= e->maxpkt;
+	}
+	free(x->tdalloc);
+	free(x->bpalloc);
+	x->tdalloc = nil;
+	x->bpalloc = nil;
+	x->etd = nil;
+	x->td0 = nil;
+	e->sched = -1;
+}
+
+static void
+epalloc(Usbhost *uh, Endpt *e)
+{
+	Endptx *x;
+
+	x = malloc(sizeof(Endptx));
+	e->private = x;
+	x->epq = allocqh(uh->ctlr);
+	if(x->epq == nil)
+		panic("devendptx");
+}
+
+static void
+epfree(Usbhost *uh, Endpt *e)
+{
+	Ctlr *ctlr;
+	Endptx *x;
+
+	ctlr = uh->ctlr;
+	x = e->private;
+	if(x->epq != nil)
+		freeqh(ctlr, x->epq);
+}
+
+static void
+epopen(Usbhost *uh, Endpt *e)
+{
+	Ctlr *ctlr;
+
+	ctlr = uh->ctlr;
+	if(e->iso && e->active)
+		error("already open");
+	if(schedendpt(ctlr, e) < 0){
+		if(e->active)
+			error("cannot schedule USB endpoint, active");
+		else
+			error("cannot schedule USB endpoint");
+	}
+	eptactivate(ctlr, e);
+}
+
+static void
+epclose(Usbhost *uh, Endpt *e)
+{
+	Ctlr *ctlr;
+
+	ctlr = uh->ctlr;
+	eptdeactivate(ctlr, e);
+	unschedendpt(ctlr, e);
+}
+
+static void
+epmode(Usbhost *uh, Endpt *e)
+{
+	Ctlr *ctlr;
+	Endptx *x;
+
+	ctlr = uh->ctlr;
+	x = e->private;
+	if(e->iso) {
+		if(x->epq != nil) {
+			freeqh(ctlr, x->epq);
+			x->epq = nil;
+		}
+	}
+	else {
+		/* Each bulk device gets a queue head hanging off the
+		 * bulk queue head
+		 */
+		if(x->epq == nil) {
+			x->epq = allocqh(ctlr);
+			if(x->epq == nil)
+				panic("epbulk: allocqh");
+		}
+		queueqh(ctlr, x->epq);
+	}
+}
+
+static	int	ioport[] = {-1, Portsc0, Portsc1};
+
+static void
+portreset(Usbhost *uh, int port)
+{
+	int i, p;
+	Ctlr *ctlr;
+
+	ctlr = uh->ctlr;
+	if(port != 1 && port != 2)
+		error(Ebadarg);
+
+	/* should check that device not being configured on other port? */
+	p = ioport[port];
+	qlock(&ctlr->resetl);
+	if(waserror()){
+		qunlock(&ctlr->resetl);
+		nexterror();
+	}
+	XPRINT("r: %x\n", IN(p));
+	ilock(ctlr);
+	OUT(p, PortReset);
+	delay(12);	/* BUG */
+	XPRINT("r2: %x\n", IN(p));
+	OUT(p, IN(p) & ~PortReset);
+	XPRINT("r3: %x\n", IN(p));
+	OUT(p, IN(p) | PortEnable);
+	microdelay(64);
+	for(i=0; i<1000 && (IN(p) & PortEnable) == 0; i++)
+		;
+	XPRINT("r': %x %d\n", IN(p), i);
+	OUT(p, (IN(p) & ~PortReset)|PortEnable);
+	iunlock(ctlr);
+	poperror();
+	qunlock(&ctlr->resetl);
+}
+
+static void
+portenable(Usbhost *uh, int port, int on)
+{
+	int w, p;
+	Ctlr *ctlr;
+
+	ctlr = uh->ctlr;
+	if(port != 1 && port != 2)
+		error(Ebadarg);
+
+	/* should check that device not being configured on other port? */
+	p = ioport[port];
+	qlock(&ctlr->resetl);
+	if(waserror()){
+		qunlock(&ctlr->resetl);
+		nexterror();
+	}
+	ilock(ctlr);
+	w = IN(p);
+	if(on)
+		w |= PortEnable;
+	else
+		w &= ~PortEnable;
+	OUT(p, w);
+	microdelay(64);
+	iunlock(ctlr);
+	XPRINT("e: %x\n", IN(p));
+	poperror();
+	qunlock(&ctlr->resetl);
+}
+
+static void
+portinfo(Usbhost *uh, char *s, char *se)
+{
+	int x, i, j;
+	Ctlr *ctlr;
+
+	ctlr = uh->ctlr;
+	for(i = 1; i <= 2; i++) {
+		ilock(ctlr);
+		x = IN(ioport[i]);
+		if((x & (PortChange|StatusChange)) != 0)
+			OUT(ioport[i], x);
+		iunlock(ctlr);
+		s = seprint(s, se, "%d %ux", i, x);
+		for(j = 0; j < nelem(portstatus); j++) {
+			if((x & portstatus[j].bit) != 0)
+				s = seprint(s, se, " %s", portstatus[j].name);
+		}
+		s = seprint(s, se, "\n");
+	}
+}
+
+static void
+cleaniso(Endpt *e, int frnum)
+{
+	TD *td;
+	int id, n, i;
+	Endptx *x;
+	uchar *bp;
+
+	x = e->private;
+	td = x->xtd;
+	if (td->status & Active)
+		return;
+	id = (e->x<<7)|(e->dev->x&0x7F);
+	do {
+		if (td->status & AnyError)
+			XPRINT("usbisoerror 0x%lux\n", td->status);
+		n = (td->status + 1) & 0x3ff;
+		e->nbytes += n;
+		if ((td->flags & IsoClean) == 0)
+			e->nblocks++;
+		if (e->mode == OREAD){
+			e->buffered += n;
+			e->poffset += (td->status + 1) & 0x3ff;
+			td->offset = e->poffset;
+			td->dev = ((e->maxpkt -1)<<21) | ((id&0x7FF)<<8) | TokIN;
+			e->toffset = td->offset;
+		}else{
+			if ((td->flags & IsoClean) == 0){
+				e->buffered -= n;
+				if (e->buffered < 0){
+//					print("e->buffered %d?\n", e->buffered);
+					e->buffered = 0;
+				}
+			}
+			e->toffset = td->offset;
+			n = (e->hz + e->remain)*e->pollms/1000;
+			e->remain = (e->hz + e->remain)*e->pollms%1000;
+			n *= e->samplesz;
+			td->dev = ((n -1)<<21) | ((id&0x7FF)<<8) | TokOUT;
+			td->offset = e->poffset;
+			e->poffset += n;
+		}
+		td = td->next;
+		if (x->xtd == td){
+			XPRINT("@");
+			break;
+		}
+	} while ((td->status & Active) == 0);
+	e->time = todget(nil);
+	x->xtd = td;
+	for (n = 2; n < 4; n++){
+		i = ((frnum + n)&0x3ff);
+		td = x->td0 + i;
+		bp = x->bp0 + e->maxpkt*i/e->pollms;
+		if (td->status & Active)
+			continue;
+
+		if (e->mode == OWRITE){
+			if (td == x->etd) {
+				XPRINT("*");
+				memset(bp+e->off, 0, e->maxpkt-e->off);
+				if (e->off == 0)
+					td->flags |= IsoClean;
+				else
+					e->buffered += (((td->dev>>21) +1) & 0x3ff) - e->off;
+				x->etd = nil;
+			}else if ((td->flags & IsoClean) == 0){
+				XPRINT("-");
+				memset(bp, 0, e->maxpkt);
+				td->flags |= IsoClean;
+			}
+		} else {
+			/* Unread bytes are now lost */
+			e->buffered -= (td->status + 1) & 0x3ff;
+		}
+		td->status = ErrLimit1 | Active | IsoSelect | IOC;
+	}
+	wakeup(&e->wr);
+}
+
+static void
+interrupt(Ureg*, void *a)
+{
+	QH *q;
+	Ctlr *ctlr;
+	Endpt *e;
+	Endptx *x;
+	int s, frnum;
+	Usbhost *uh;
+
+	uh = a;
+	ctlr = uh->ctlr;
+	s = IN(Status);
+	ctlr->frameptr = inl(ctlr->io+Flbaseadd);
+	ctlr->framenumber = IN(Frnum) & 0x3ff;
+	OUT(Status, s);
+	if ((s & 0x1f) == 0)
+		return;
+	ctlr->usbints++;
+	frnum = IN(Frnum) & 0x3ff;
+	if (s & 0x1a) {
+		XPRINT("cmd #%x sofmod #%x\n", IN(Cmd), inb(ctlr->io+SOFMod));
+		XPRINT("sc0 #%x sc1 #%x\n", IN(Portsc0), IN(Portsc1));
+	}
+
+	ilock(&ctlr->activends);
+	for(e = ctlr->activends.f; e != nil; e = e->activef) {
+		x = e->private;
+		if(!e->iso && x->epq != nil) {
+			XPRINT("cleanq(ctlr, x->epq, 0, 0)\n");
+			cleanq(ctlr, x->epq, 0, 0);
+		}
+		if(e->iso) {
+			XPRINT("cleaniso(e)\n");
+			cleaniso(e, frnum);
+		}
+	}
+	iunlock(&ctlr->activends);
+	XPRINT("cleanq(ctlr, ctlr->ctlq, 0, 0)\n");
+	cleanq(ctlr, ctlr->ctlq, 0, 0);
+	XPRINT("cleanq(ctlr, ctlr->bulkq, 0, Vf)\n");
+	cleanq(ctlr, ctlr->bulkq, 0, Vf);
+	XPRINT("clean recvq\n");
+	for (q = ctlr->recvq->next; q; q = q->hlink) {
+		XPRINT("cleanq(ctlr, q, 0, Vf)\n");
+		cleanq(ctlr, q, 0, Vf);
+	}
+}
+
+static int
+eptinput(void *arg)
+{
+	Endpt *e;
+
+	e = arg;
+	return e->eof || e->err || qcanread(e->rq);
+}
+
+static int
+isoreadyx(Endptx *x)
+{
+	return x->etd == nil || (x->etd != x->xtd && (x->etd->status & Active) == 0);
+}
+
+static int
+isoready(void *arg)
+{
+	int ret;
+	Ctlr *ctlr;
+	Endpt *e;
+	Endptx *x;
+
+	e = arg;
+	ctlr = e->dev->uh->ctlr;
+	x = e->private;
+	ilock(&ctlr->activends);
+	ret = isoreadyx(x);
+	iunlock(&ctlr->activends);
+	return ret;
+}
+
+static long
+isoio(Ctlr *ctlr, Endpt *e, void *a, long n, ulong offset, int w)
+{
+	TD *td;
+	Endptx *x;
+	int i, frnum;
+	uchar *p, *q, *bp;
+	volatile int isolock;
+
+	x = e->private;
+	qlock(&e->rlock);
+	isolock = 0;
+	if(waserror()){
+		if (isolock){
+			isolock = 0;
+			iunlock(&ctlr->activends);
+		}
+		qunlock(&e->rlock);
+		eptcancel(ctlr, e);
+		nexterror();
+	}
+	p = a;
+	if (offset != 0 && offset != e->foffset){
+		iprint("offset %lud, foffset %lud\n", offset, e->foffset);
+		/* Seek to a specific position */
+		frnum = (IN(Frnum) + 8) & 0x3ff;
+		td = x->td0 +frnum;
+		if (offset < td->offset)
+			error("ancient history");
+		while (offset > e->toffset){
+			tsleep(&e->wr, return0, 0, 500);
+		}
+		while (offset >= td->offset + ((w?(td->dev >> 21):td->status) + 1) & 0x7ff){
+			td = td->next;
+			if (td == x->xtd)
+				iprint("trouble\n");
+		}
+		ilock(&ctlr->activends);
+		isolock = 1;
+		e->off = td->offset - offset;
+		if (e->off >= e->maxpkt){
+			iprint("I can't program: %d\n", e->off);
+			e->off = 0;
+		}
+		x->etd = td;
+		e->foffset = offset;
+	}
+	do {
+		if (isolock == 0){
+			ilock(&ctlr->activends);
+			isolock = 1;
+		}
+		td = x->etd;
+		if (td == nil || e->off == 0){
+			if (td == nil){
+				XPRINT("0");
+				if (w){
+					frnum = (IN(Frnum) + 1) & 0x3ff;
+					td = x->td0 + frnum;
+					while(td->status & Active)
+						td = td->next;
+				}else{
+					frnum = (IN(Frnum) - 4) & 0x3ff;
+					td = x->td0 + frnum;
+					while(td->next != x->xtd)
+						td = td->next;
+				}
+				x->etd = td;
+				e->off = 0;
+			}else{
+				/* New td, make sure it's ready */
+				while (isoreadyx(x) == 0){
+					isolock = 0;
+					iunlock(&ctlr->activends);
+					sleep(&e->wr, isoready, e);
+					ilock(&ctlr->activends);
+					isolock = 1;
+				}
+				if (x->etd == nil){
+					XPRINT("!");
+					continue;
+				}
+			}
+			if (w)
+				e->psize = ((td->dev >> 21) + 1) & 0x7ff;
+			else
+				e->psize = (x->etd->status + 1) & 0x7ff;
+			if(e->psize > e->maxpkt)
+				panic("packet size > maximum");
+		}
+		if((i = n) >= e->psize)
+			i = e->psize;
+		if (w)
+			e->buffered += i;
+		else{
+			e->buffered -= i;
+			if (e->buffered < 0)
+				e->buffered = 0;
+		}
+		isolock = 0;
+		iunlock(&ctlr->activends);
+		td->flags &= ~IsoClean;
+		bp = x->bp0 + (td - x->td0) * e->maxpkt / e->pollms;
+		q = bp + e->off;
+		if (w){
+			memmove(q, p, i);
+		}else{
+			memmove(p, q, i);
+		}
+		p += i;
+		n -= i;
+		e->off += i;
+		e->psize -= i;
+		if (e->psize){
+			if (n != 0)
+				panic("usb iso: can't happen");
+			break;
+		}
+		if(w)
+			td->offset = offset + (p-(uchar*)a) - (((td->dev >> 21) + 1) & 0x7ff);
+		td->status = ErrLimit3 | Active | IsoSelect | IOC;
+		x->etd = td->next;
+		e->off = 0;
+	} while(n > 0);
+	n = p-(uchar*)a;
+	e->foffset += n;
+	poperror();
+	if (isolock)
+		iunlock(&ctlr->activends);
+	qunlock(&e->rlock);
+	return n;
+}
+
+static long
+read(Usbhost *uh, Endpt *e, void *a, long n, vlong offset)
+{
+	long l, i;
+	Block *b;
+	Ctlr *ctlr;
+	uchar *p;
+
+	ctlr = uh->ctlr;
+	if(e->iso)
+		return isoio(ctlr, e, a, n, (ulong)offset, 0);
+
+	XPRINT("qlock(%p)\n", &e->rlock);
+	qlock(&e->rlock);
+	XPRINT("got qlock(%p)\n", &e->rlock);
+	if(waserror()){
+		qunlock(&e->rlock);
+		eptcancel(ctlr, e);
+		nexterror();
+	}
+	p = a;
+	do {
+		if(e->eof) {
+			XPRINT("e->eof\n");
+			break;
+		}
+		if(e->err)
+			error(e->err);
+		qrcv(ctlr, e);
+		if(!e->iso)
+			e->data01 ^= 1;
+		sleep(&e->rr, eptinput, e);
+		if(e->err)
+			error(e->err);
+		b = qget(e->rq);	/* TO DO */
+		if(b == nil) {
+			XPRINT("b == nil\n");
+			break;
+		}
+		if(waserror()){
+			freeb(b);
+			nexterror();
+		}
+		l = BLEN(b);
+		if((i = l) > n)
+			i = n;
+		if(i > 0){
+			memmove(p, b->rp, i);
+			p += i;
+		}
+		poperror();
+		freeb(b);
+		n -= i;
+		if (l != e->maxpkt)
+			break;
+	} while (n > 0);
+	poperror();
+	qunlock(&e->rlock);
+	return p-(uchar*)a;
+}
+
+static int
+qisempty(void *arg)
+{
+	return ((QH*)arg)->entries & Terminate;
+}
+
+static long
+write(Usbhost *uh, Endpt *e, void *a, long n, vlong offset, int tok)
+{
+	int i, j;
+	QH *qh;
+	Block *b;
+	Ctlr *ctlr;
+	uchar *p;
+
+	ctlr = uh->ctlr;
+	if(e->iso)
+		return isoio(ctlr, e, a, n, (ulong)offset, 1);
+
+	p = a;
+	qlock(&e->wlock);
+	if(waserror()){
+		qunlock(&e->wlock);
+		eptcancel(ctlr, e);
+		nexterror();
+	}
+	do {
+		if(e->err)
+			error(e->err);
+		if((i = n) >= e->maxpkt)
+			i = e->maxpkt;
+		b = allocb(i);
+		if(waserror()){
+			freeb(b);
+			nexterror();
+		}
+		XPRINT("out [%d]", i);
+		for (j = 0; j < i; j++) XPRINT(" %.2x", p[j]);
+		XPRINT("\n");
+		memmove(b->wp, p, i);
+		b->wp += i;
+		p += i;
+		n -= i;
+		poperror();
+		qh = qxmit(ctlr, e, b, tok);
+		tok = TokOUT;
+		e->data01 ^= 1;
+		if(e->ntd >= e->nbuf) {
+XPRINT("qh %s: q=%p first=%p last=%p entries=%.8lux\n",
+ "writeusb sleep", qh, qh->first, qh->last, qh->entries);
+			XPRINT("write: sleep %lux\n", &e->wr);
+			sleep(&e->wr, qisempty, qh);
+			XPRINT("write: awake\n");
+		}
+	} while(n > 0);
+	poperror();
+	qunlock(&e->wlock);
+	return p-(uchar*)a;
+}
+
+static void
+init(Usbhost* uh)
+{
+	Ctlr *ctlr;
+
+	ctlr = uh->ctlr;
+	ilock(ctlr);
+	outl(ctlr->io+Flbaseadd, PCIWADDR(ctlr->frames));
+	OUT(Frnum, 0);
+	OUT(Usbintr, 0xF);	/* enable all interrupts */
+	XPRINT("cmd 0x%x sofmod 0x%x\n", IN(Cmd), inb(ctlr->io+SOFMod));
+	XPRINT("sc0 0x%x sc1 0x%x\n", IN(Portsc0), IN(Portsc1));
+	if((IN(Cmd)&1)==0)
+		OUT(Cmd, 1);	/* run */
+//	pprint("at: c=%x s=%x c0=%x\n", IN(Cmd), IN(Status), IN(Portsc0));
+	iunlock(ctlr);
+}
+
+static void
+scanpci(void)
+{
+	int io;
+	Ctlr *ctlr;
+	Pcidev *p;
+	static int already = 0;
+
+	if(already)
+		return;
+	already = 1;
+	p = nil;
+	while(p = pcimatch(p, 0, 0)) {
+		/*
+		 * Find UHCI controllers.  Class = 12 (serial controller),
+		 * Sub-class = 3 (USB) and Programming Interface = 0.
+		 */
+		if(p->ccrb != 0x0C || p->ccru != 0x03 || p->ccrp != 0x00)
+			continue;
+		io = p->mem[4].bar & ~0x0F;
+		if(io == 0) {
+			print("usbuhci: failed to map registers\n");
+			continue;
+		}
+		if(ioalloc(io, p->mem[4].size, 0, "usbuhci") < 0){
+			print("usbuhci: port %d in use\n", io);
+			continue;
+		}
+		if(p->intl == 0xFF || p->intl == 0) {
+			print("usbuhci: no irq assigned for port %d\n", io);
+			continue;
+		}
+
+		XPRINT("usbuhci: %x/%x port 0x%ux size 0x%x irq %d\n",
+			p->vid, p->did, io, p->mem[4].size, p->intl);
+
+		ctlr = malloc(sizeof(Ctlr));
+		ctlr->pcidev = p;
+		ctlr->io = io;
+		if(ctlrhead != nil)
+			ctlrtail->next = ctlr;
+		else
+			ctlrhead = ctlr;
+		ctlrtail = ctlr;
+	}
+}
+
+static int
+reset(Usbhost *uh)
+{
+	int i;
+	TD *t;
+	ulong io;
+	Ctlr *ctlr;
+	Pcidev *p;
+
+	scanpci();
+
+	/*
+	 * Any adapter matches if no uh->port is supplied,
+	 * otherwise the ports must match.
+	 */
+	for(ctlr = ctlrhead; ctlr != nil; ctlr = ctlr->next){
+		if(ctlr->active)
+			continue;
+		if(uh->port == 0 || uh->port == ctlr->io){
+			ctlr->active = 1;
+			break;
+		}
+	}
+	if(ctlr == nil)
+		return -1;
+
+	io = ctlr->io;
+	p = ctlr->pcidev;
+
+	uh->ctlr = ctlr;
+	uh->port = io;
+	uh->irq = p->intl;
+	uh->tbdf = p->tbdf;
+
+	XPRINT("usbcmd\t0x%.4x\nusbsts\t0x%.4x\nusbintr\t0x%.4x\nfrnum\t0x%.2x\n",
+		IN(Cmd), IN(Status), IN(Usbintr), inb(io+Frnum));
+	XPRINT("frbaseadd\t0x%.4x\nsofmod\t0x%x\nportsc1\t0x%.4x\nportsc2\t0x%.4x\n",
+		IN(Flbaseadd), inb(io+SOFMod), IN(Portsc0), IN(Portsc1));
+
+	OUT(Cmd, 0);					/* stop */
+	while((IN(Status) & (1<<5)) == 0)	/* wait for halt */
+		;
+	OUT(Status, 0xFF);				/* clear pending interrupts */
+	pcicfgw16(p, 0xc0, 0x2000);		/* legacy support register: turn off lunacy mode */
+
+	if(0){
+		i = inb(io+SOFMod);
+		OUT(Cmd, 4);	/* global reset */
+		delay(15);
+		OUT(Cmd, 0);	/* end reset */
+		delay(4);
+		outb(io+SOFMod, i);
+	}
+
+	ctlr->tdpool = mmucacheinhib(xspanalloc(128*sizeof(TD), /*16*/CACHELINESZ, 0), 128*sizeof(TD));
+	for(i=128; --i>=0;){
+		ctlr->tdpool[i].next = ctlr->freetd;
+		ctlr->freetd = &ctlr->tdpool[i];
+	}
+	ctlr->qhpool = mmucacheinhib(xspanalloc(64*sizeof(QH), /*16*/CACHELINESZ, 0), 64*sizeof(QH));
+	for(i=64; --i>=0;){
+		ctlr->qhpool[i].next = ctlr->freeqh;
+		ctlr->freeqh = &ctlr->qhpool[i];
+	}
+
+	/*
+	 * the last entries of the periodic (interrupt & isochronous) scheduling TD entries
+	 * points to the control queue and the bandwidth sop for bulk traffic.
+	 * this is looped following the instructions in PIIX4 errata 29773804.pdf:
+	 * a QH links to a looped but inactive TD as its sole entry,
+	 * with its head entry leading on to the bulk traffic, the last QH of which
+	 * links back to the empty QH.
+	 */
+	ctlr->ctlq = allocqh(ctlr);
+	ctlr->bwsop = allocqh(ctlr);
+	ctlr->bulkq = allocqh(ctlr);
+	ctlr->recvq = allocqh(ctlr);
+	t = alloctd(ctlr);	/* inactive TD, looped */
+	t->link = PCIWADDR(t);
+	ctlr->bwsop->entries = PCIWADDR(t);
+
+	ctlr->ctlq->head = PCIWADDR(ctlr->bulkq) | IsQH;
+	ctlr->bulkq->head = PCIWADDR(ctlr->recvq) | IsQH;
+	ctlr->recvq->head = PCIWADDR(ctlr->bwsop) | IsQH;
+	if (1)	/* don't use loop back */
+ 		ctlr->bwsop->head = Terminate;
+	else	/* set up loop back */
+		ctlr->bwsop->head = PCIWADDR(ctlr->bwsop) | IsQH;
+
+	ctlr->frames = mmucacheinhib(xspanalloc(FRAMESIZE, FRAMESIZE, 0), FRAMESIZE);
+	ctlr->frameld = xallocz(FRAMESIZE, 1);
+	for (i = 0; i < NFRAME; i++)
+		ctlr->frames[i] = PCIWADDR(ctlr->ctlq) | IsQH;
+
+	/*
+	 * Linkage to the generic USB driver.
+	 */
+	uh->init = init;
+	uh->interrupt = interrupt;
+
+	uh->portinfo = portinfo;
+	uh->portreset = portreset;
+	uh->portenable = portenable;
+
+	uh->epalloc = epalloc;
+	uh->epfree = epfree;
+	uh->epopen = epopen;
+	uh->epclose = epclose;
+	uh->epmode = epmode;
+
+	uh->read = read;
+	uh->write = write;
+
+	return 0;
+}
+
+void
+usbuhcilink(void)
+{
+	addusbtype("uhci", reset);
+}
--- /dev/null
+++ b/os/mpc/800io.h
@@ -1,0 +1,666 @@
+typedef struct BD BD;
+typedef struct CPMdev CPMdev;
+typedef struct GTimer GTimer;
+typedef struct I2Cdev I2Cdev;
+typedef struct PCMconftab PCMconftab;
+typedef struct PCMmap	PCMmap;
+typedef struct PCMslot	PCMslot;
+typedef struct Ring Ring;
+
+/*
+ * MPC800 series IO structures
+ */
+
+enum
+{
+	/* interrupt vectors (SIU and CPM) */
+	VectorPIC= 0,	/* level 0 to level 7, assigned by software */
+		/* vector assignments are determined by the assignments here */
+		PITlevel=	2,
+		CPIClevel=	4,
+		PCMCIAio=	5,
+		PCMCIAstatus=	6,
+		RTClevel=	7,
+	VectorIRQ=	VectorPIC+8,	/* IRQ0 to IRQ7 */
+	VectorCPIC=	VectorIRQ+8,	/* 32 CPM interrupts: 0 (error) to 0x1F (PC15) */
+	MaxVector=	VectorCPIC+32,
+};
+
+/*
+ * these are defined to keep the interface compatible with other
+ * architectures, but only BUSUNKNOWN is currently used
+ */
+#define MKBUS(t,b,d,f)	(((t)<<24)|(((b)&0xFF)<<16)|(((d)&0x1F)<<11)|(((f)&0x07)<<8))
+#define BUSFNO(tbdf)	(((tbdf)>>8)&0x07)
+#define BUSDNO(tbdf)	(((tbdf)>>11)&0x1F)
+#define BUSBNO(tbdf)	(((tbdf)>>16)&0xFF)
+#define BUSTYPE(tbdf)	((tbdf)>>24)
+#define BUSBDF(tbdf)	((tbdf)&0x00FFFF00)
+#define BUSUNKNOWN	(-1)
+
+/*
+ * Buffer Descriptors and IO Rings
+ */
+
+struct BD {
+	ushort	status;
+	ushort	length;
+	ulong	addr;
+};
+
+BD*	bdalloc(int);
+void	bdfree(BD*, int);
+void	dumpbd(char*, BD*, int);
+
+enum {
+	/* Rx BDs, bits common to all protocols */
+	BDEmpty=	1<<15,
+	BDWrap=		1<<13,
+	BDInt=		1<<12,
+	BDLast=		1<<11,
+	BDFirst=		1<<10,
+
+	/* Tx BDs */
+	BDReady=		1<<15,
+	/* BDWrap, BDInt, BDLast */
+};
+
+
+struct Ring {
+	BD*	rdr;				/* receive descriptor ring */
+	void*	rrb;				/* receive ring buffers */
+	int	rdrx;				/* index into rdr */
+	int	nrdre;			/* length of rdr */
+
+	BD*	tdr;				/* transmit descriptor ring */
+	Block**	txb;				/* corresponding transmit ring buffers */
+	int	tdrh;				/* host index into tdr */
+	int	tdri;				/* interface index into tdr */
+	int	ntdre;			/* length of tdr */
+	int	ntq;				/* pending transmit requests */
+};
+
+#define NEXT(x, l)	(((x)+1)%(l))
+#define PREV(x, l)	(((x) == 0) ? (l)-1: (x)-1)
+#define	HOWMANY(x, y)	(((x)+((y)-1))/(y))
+#define ROUNDUP(x, y)	(HOWMANY((x), (y))*(y))
+
+int	ioringinit(Ring*, int, int, int);
+
+/*
+ * CPM
+ */
+enum {
+	/* commands */
+	InitRxTx =	0,
+	InitRx =		1,
+	InitTx =		2,
+	EnterHunt=	3,
+	StopTx=		4,
+	GracefulStopTx = 5,
+	InitIDMA =	5,
+	RestartTx =	6,
+	CloseRxBD =	7,
+	SetGroupAddr = 8,
+	SetTimer =	8,
+	GCITimeout =	9,
+	GCIAbort =	10,
+	StopIDMA =	11,
+	StartDSP = 	12,
+	ArmIDMA =	13,
+	InitDSP =		13,
+	USBCmd =	15,
+
+	/* bgcr */
+	BaudEnable = 1<<16,
+
+	/* sicr */
+	CLK1 = 4,		/* SCC1,2 */
+	CLK2 = 5,
+	CLK3 = 6,
+	CLK4 = 7,
+	CLK5 = CLK1,	/* SCC3,4 */
+	CLK6 = CLK2,
+	CLK7 = CLK3,
+	CLK8 = CLK4,
+
+	/* logical channel IDs mapped to channel ID by cpm.c */
+	CPnone = 0,
+	CPscc1,
+	CPscc2,
+	CPscc3,
+	CPscc4,
+	CPsmc1,
+	CPsmc2,
+	CPdsp1,
+	CPdsp2,
+	CPidma1,
+	CPidma2,
+	CPtimer,
+	CPspi,
+	CPi2c,
+	CPmax,
+};
+
+struct CPMdev {
+	int	id;	/* CPM channel number */
+	int	irq;	/* CPIC interrupt number */
+	int	rbase;	/* register offset in IO mem */
+	int	pbase;	/* parameter offset in IO mem */
+	void*	regs;	/* kernel address of registers */
+	void*	param;	/* kernel address of parameters */
+};
+
+CPMdev*	cpmdev(int);
+void	cpmop(CPMdev*, int, int);
+void*	cpmalloc(int, int);
+void	cpmfree(void*, int);
+IMM*	ioplock(void);
+void	iopunlock(void);
+
+int	cpmidopen(int, void*);
+void	cpmidclose(int);
+void	sccnmsi(int, int, int);
+void	sccxstop(CPMdev*);
+void	smcnmsi(int, int);
+void	smcxstop(CPMdev*);
+
+/*
+ * CPM timers
+ */
+enum {
+	/* timer modes */
+	CaptureRise=	1<<6,
+	CaptureFall=	2<<6,
+	CaptureEdge=	3<<6,
+	TimerToggle=	1<<5,	/* toggle TOUTx* pin */
+	TimerORI=	1<<4,	/* Output Reference Interrupt */
+	TimerRestart=	1<<3,
+	TimerSclk=	1<<1,
+	TimerSclk16=	2<<1,
+	TimerTIN=	3<<1,	/* clock by falling edge of TINx */
+	TimerGate=	1<<0,	/* TGATE1* controls timer */
+
+	/* timer events */
+	TimerREF=	1<<1,
+	TimerCAP=	1<<0
+};
+
+struct GTimer{
+	int	x;
+	int	inuse;
+	int	event;
+	ushort*	tmr;
+	ushort*	trr;
+	ushort*	tcr;
+	ushort*	tcn;
+	ushort*	ter;
+	void*	arg;
+	void	(*interrupt)(Ureg*, void*, GTimer*);
+};
+GTimer*	gtimer(ushort, ushort, void (*)(Ureg*,void*,GTimer*), void*);
+void	gtimerset(GTimer*, ushort, int);
+void	gtimerstart(GTimer*);
+void	gtimerstop(GTimer*);
+void	gtimerfree(GTimer*);
+
+/*
+ * the structures below follow hardware/firmware layouts in the 8xx manuals:
+ * mind the data types, offsets and alignment
+ */
+
+/*
+ * basic IO controller parameters (SMC and SCC)
+ */
+typedef struct IOCparam IOCparam;
+struct IOCparam {
+	ushort	rbase;
+	ushort	tbase;
+	uchar	rfcr;
+	uchar	tfcr;
+	ushort	mrblr;
+	ulong	rstate;
+	ulong	rptr;
+	ushort	rbptr;
+	ushort	rcnt;
+	ulong	rtmp;
+	ulong	tstate;
+	ulong	tptr;
+	ushort	tbptr;
+	ushort	tcnt;
+	ulong	ttmp;
+};
+
+typedef struct SCCparam SCCparam;
+struct SCCparam {
+	IOCparam;
+	ulong	rcrc;
+	ulong	tcrc;
+};
+
+typedef struct SCC SCC;
+struct SCC {
+	ulong	gsmrl;
+	ulong	gsmrh;
+	ushort	psmr;
+	uchar	rsvscc0[2];
+	ushort	todr;
+	ushort	dsr;
+	ushort	scce;
+	uchar	rsvscc1[2];
+	ushort	sccm;
+	uchar	rsvscc3;
+	uchar	sccs;
+	ushort	irmode;
+	ushort	irsip;
+};
+
+typedef struct SMC SMC;
+struct SMC {
+	uchar	pad1[2];
+	ushort	smcmr;
+	uchar	pad2[2];
+	uchar	smce;
+	uchar	pad3[3];
+	uchar	smcm;
+	uchar	pad4[5];
+};
+
+typedef struct SPI SPI;
+struct SPI {
+	ushort	spmode;
+	uchar	res1[4];
+	uchar	spie;
+	uchar	res2[3];
+	uchar	spim;
+	uchar	res3[2];
+	uchar	spcom;
+	uchar	res4[10];
+};
+
+typedef struct USB USB;
+struct USB {	/* 823 only */
+	uchar	usmod;
+	uchar	usadr;
+	uchar	uscom;
+	uchar	rsvu1;
+	ushort	usep[4];
+	uchar	rsvu2[4];
+	ushort	usber;
+	uchar	rsvu3[2];
+	ushort	usbmr;
+	uchar	rsvu4;
+	uchar	usbs;
+	uchar	rsvu5[8];
+};
+
+typedef struct IMM IMM;
+struct IMM {
+	struct {	/* general SIU */
+		ulong	siumcr;
+		ulong	sypcr;
+		uchar	rsv0[0xE-0x8];
+		ushort	swsr;
+		ulong	sipend;
+		ulong	simask;
+		ulong	siel;
+		uchar	sivec;
+		uchar	padv[3];
+		ulong	tesr;
+		uchar	rsv1[0x30-0x24];
+		ulong	sdcr;
+		uchar	rsv2[0x80-0x34];
+	};
+	struct {	/* PCMCIA */
+		struct {
+			ulong	base;
+			ulong	option;
+		} pcmr[8];
+		uchar	rsv3[0xe0-0xc0];
+		ulong	pgcr[2];
+		ulong	pscr;
+		uchar	rsv4[0xf0-0xec];
+		ulong	pipr;
+		uchar	rsv5[4];
+		ulong	per;
+		uchar	rsv6[4];
+	};
+	struct {	/* MEMC */
+		struct {
+			ulong	base;
+			ulong	option;
+		} memc[8];
+		uchar	rsv7a[0x24];
+		ulong	mar;
+		ulong	mcr;
+		uchar	rsv7b[4];
+		ulong	mamr;
+		ulong	mbmr;
+		ushort	mstat;
+		ushort	mptpr;
+		ulong	mdr;
+		uchar	rsv7c[0x80];
+	};
+	struct {	/* system integration timers */
+		ushort	tbscr;
+		uchar	rsv8a[2];
+		ulong	tbrefu;
+		ulong	tbrefl;
+		uchar	rsv8b[0x14];
+		ushort	rtcsc;
+		uchar	rsv8c[2];
+		ulong	rtc;
+		ulong	rtsec;
+		ulong	rtcal;
+		uchar	rsv8d[0x10];
+		ushort	piscr;
+		ushort	rsv8e;
+		ulong	pitc;
+		ulong	pitr;
+		uchar	rsv8f[0x34];
+	};
+	struct {	/* 280: clocks and resets */
+		ulong	sccr;
+		ulong	plprcr;
+		ulong	rsr;
+		uchar	rsv9[0x300-0x28c];
+	};
+	struct {	/* 300: system integration timers keys */
+		ulong	tbscrk;
+		ulong	tbrefuk;
+		ulong	tbreflk;
+		ulong	tbk;
+		uchar	rsv10a[0x10];
+		ulong	rtcsck;
+		ulong	rtck;
+		ulong	rtseck;
+		ulong	rtcalk;
+		uchar	rsv10b[0x10];
+		ulong	piscrk;
+		ulong	pitck;
+		uchar	rsv10c[0x38];
+	};
+	struct {	/* 380: clocks and resets keys */
+		ulong	sccrk;
+		ulong	plprcrk;
+		ulong	rsrk;
+		uchar	rsv11[0x800-0x38C];
+	};
+	struct {	/* 800: video controller */
+		ushort	vccr;
+		ushort	pad11a;
+		uchar	vsr;
+		uchar	pad11b;
+		uchar	vcmr;
+		uchar	pad11c;
+		ulong	vbcb;
+		ulong	pad11d;
+		ulong	vfcr0;
+		ulong	vfaa0;
+		ulong	vfba0;
+		ulong	vfcr1;
+		ulong	vfaa1;
+		ulong	vfba1;
+		uchar	rsv11a[0x840-0x828];
+	};
+	struct {	/* 840: LCD */
+		ulong	lccr;
+		ulong	lchcr;
+		ulong	lcvcr;
+		ulong	rsv11b;
+		ulong	lcfaa;
+		ulong	lcfba;
+		uchar	lcsr;
+		uchar	rsv11c[0x860-0x859];
+	};
+	struct {	/* 860: I2C */
+		uchar	i2mod;
+		uchar	rsv12a[3];
+		uchar	i2add;
+		uchar	rsv12b[3];
+		uchar	i2brg;
+		uchar	rsv12c[3];
+		uchar	i2com;
+		uchar	rsv12d[3];
+		uchar	i2cer;
+		uchar	rsv12e[3];
+		uchar	i2cmr;
+		uchar	rsv12[0x900-0x875];
+	};
+	struct {	/* 900: DMA */
+		uchar	rsv13[4];
+		ulong	sdar;
+		uchar	sdsr;
+		uchar	pad1[3];
+		uchar	sdmr;
+		uchar	pad2[3];
+		uchar	idsr1;
+		uchar	pad3[3];
+		uchar	idmr1;
+		uchar	pad4[3];
+		uchar	idsr2;
+		uchar	pad5[3];
+		uchar	idmr2;
+		uchar	pad6[0x930-0x91D];
+	};
+	struct {	/* CPM interrupt control */
+		ushort	civr;
+		uchar	pad7[0x940-0x932];
+		ulong	cicr;
+		ulong	cipr;
+		ulong	cimr;
+		ulong	cisr;
+	};
+	struct {	/* input/output port */
+		ushort	padir;
+		ushort	papar;
+		ushort	paodr;
+		ushort	padat;
+		uchar	pad8[8];
+		ushort	pcdir;
+		ushort	pcpar;
+		ushort	pcso;
+		ushort	pcdat;
+		ushort	pcint;
+		uchar	pad9[6];
+		ushort	pddir;
+		ushort	pdpar;
+		ushort	rsv14a;
+		ushort	pddat;
+		uchar	rsv14[0x980-0x978];
+	};
+	struct {	/* CPM timers */
+		ushort	tgcr;
+		uchar	rsv15a[0x990-0x982];
+		ushort	tmr1;
+		ushort	tmr2;
+		ushort	trr1;
+		ushort	trr2;
+		ushort	tcr1;
+		ushort	tcr2;
+		ushort	tcn1;
+		ushort	tcn2;
+		ushort	tmr3;
+		ushort	tmr4;
+		ushort	trr3;
+		ushort	trr4;
+		ushort	tcr3;
+		ushort	tcr4;
+		ushort	tcn3;
+		ushort	tcn4;
+		ushort	ter1;
+		ushort	ter2;
+		ushort	ter3;
+		ushort	ter4;
+		uchar	rsv15[0x9C0-0x9B8];
+	};
+	struct {	/* CPM */
+		ushort	cpcr;
+		uchar	res0[2];
+		ushort	rccr;
+		uchar	res1;
+		uchar	rmds;
+		uchar	res2a[4];
+		ushort	rctr1;
+		ushort	rctr2;
+		ushort	rctr3;
+		ushort	rctr4;
+		uchar	res2[2];
+		ushort	rter;
+		uchar	res3[2];
+		ushort	rtmr;
+		uchar	rsv16[0x9F0-0x9DC];
+	};
+	union {	/* BRG */
+		struct {
+			ulong	brgc1;
+			ulong	brgc2;
+			ulong	brgc3;
+			ulong	brgc4;
+		};
+		ulong	brgc[4];
+	};
+	uchar	skip0[0xAB2-0xA00];	/* USB, SCC, SMC, SPI: address using cpmdev(CP...)->regs */
+	struct {	/* PIP */
+		ushort	pipc;		/* not 823 */
+		ushort	ptpr;		/* not 823 */
+		ulong	pbdir;
+		ulong	pbpar;
+		uchar	pad10[2];
+		ushort	pbodr;
+		ulong	pbdat;
+		uchar	pad11[0xAE0-0xAC8];
+	};
+	struct {	/* SI */
+		ulong	simode;
+		uchar	sigmr;
+		uchar	pad12;
+		uchar	sistr;
+		uchar	sicmr;
+		uchar	pad13[4];
+		ulong	sicr;
+		ulong	sirp;
+		uchar	pad14[0xB00-0xAF4];
+	};
+	ulong	vcram[64];
+	ushort	siram[256];
+	ushort	lcdmap[256];
+};
+
+/*
+ * PCMCIA structures known by both ../port/cis.c and the pcmcia driver
+ */
+
+/*
+ * Map between physical memory space and PCMCIA card memory space.
+ */
+struct PCMmap {
+	ulong	ca;			/* card address */
+	ulong	cea;			/* card end address */
+	ulong	isa;			/* local virtual address */
+	int	len;			/* length of the ISA area */
+	int	attr;			/* attribute memory */
+	int	slotno;			/* owning slot */
+	int	ref;
+};
+
+/*
+ *  a PCMCIA configuration entry
+ */
+struct PCMconftab
+{
+	int	index;
+	ushort	irqs;		/* legal irqs */
+	uchar	irqtype;
+	uchar	bit16;		/* true for 16 bit access */
+	uchar	nlines;
+	struct {
+		ulong	start;
+		ulong	len;
+		PCMmap*	map;
+	} io[16];
+	int	nio;
+	int	vcc;
+	int	vpp1;
+	int	vpp2;
+	uchar	memwait;
+	ulong	maxwait;
+	ulong	readywait;
+	ulong	otherwait;
+};
+
+/*
+ *  PCMCIA card slot
+ */
+struct PCMslot
+{
+//	RWlock;
+
+//	Ref	ref;
+Ref;
+
+	void*	ctlr;	/* controller for this slot */
+
+	long	memlen;		/* memory length */
+	uchar	slotno;		/* slot number */
+	uchar	slotshift;	/* >> register to meet mask; << mask to meet register */
+	void	*regs;		/* i/o registers */
+	void	*mem;		/* memory */
+	void	*attr;		/* attribute memory */
+
+	/* status */
+	uchar	occupied;	/* card in the slot */
+	uchar	configed;	/* card configured */
+	uchar	busy;
+	uchar	powered;
+	uchar	battery;
+	uchar	wrprot;
+	uchar	enabled;
+	uchar	special;
+	uchar	dsize;
+	uchar	v3_3;
+	uchar	voltage;
+
+	/* cis info */
+	int	cisread;	/* set when the cis has been read */
+	char	verstr[512];	/* version string */
+	uchar	cpresent;	/* config registers present */
+	ulong	caddr;		/* relative address of config registers */
+	int	nctab;		/* number of config table entries */
+	PCMconftab	ctab[8];
+	PCMconftab	*def;		/* default conftab */
+
+	/* maps are fixed */
+	PCMmap memmap;
+	PCMmap attrmap;
+
+	struct {
+		void	(*f)(Ureg*, void*);
+		void	*arg;
+	} intr;
+	struct {
+		void	(*f)(void*, int);
+		void	*arg;
+	} notify;
+};
+
+/* ../port/cis.c */
+void	pcmcisread(PCMslot*);
+int	pcmcistuple(int, int, int, void*, int);
+
+/* devpcmcia.c */
+PCMmap*	pcmmap(int, ulong, int, int);
+void	pcmunmap(int, PCMmap*);
+
+/*
+ * used by ../port/devi2c.c and i2c.c
+ */
+struct I2Cdev {
+	int	addr;
+	int	salen;	/* length in bytes of subaddress, if used; 0 otherwise */
+	int	tenbit;	/* 10-bit addresses */
+};
+
+long	i2crecv(I2Cdev*, void*, long, ulong);
+long	i2csend(I2Cdev*, void*, long, ulong);
+void	i2csetup(int);
--- /dev/null
+++ b/os/mpc/NOTICE
@@ -1,0 +1,3 @@
+Inferno® Copyright © 1996-1999 Lucent Technologies Inc.  All rights reserved.
+PowerPC support Copyright © 1995-1997 C H Forsyth (forsyth@terzarima.net).  All rights reserved.
+MPC8xx Inferno PowerPC port Copyright © 1998-2003 Vita Nuova Holdings Limited.  All rights reserved.
--- /dev/null
+++ b/os/mpc/clock.c
@@ -1,0 +1,145 @@
+#include	"u.h"
+#include	"../port/lib.h"
+#include	"mem.h"
+#include	"dat.h"
+#include	"fns.h"
+#include	"io.h"
+#include	"ureg.h"
+
+#include	<isa.h>
+#include	<interp.h>
+
+typedef struct Clock0link Clock0link;
+typedef struct Clock0link {
+	void		(*clock)(void);
+	Clock0link*	link;
+} Clock0link;
+
+static Clock0link *clock0link;
+static Lock clock0lock;
+ulong	clkrelinq;
+void	(*kproftick)(ulong);	/* set by devkprof.c when active */
+void	(*archclocktick)(void);	/* set by arch*.c when desired */
+
+Timer*
+addclock0link(void (*clock)(void), int)
+{
+	Clock0link *lp;
+
+	if((lp = malloc(sizeof(Clock0link))) == 0){
+		print("addclock0link: too many links\n");
+		return nil;
+	}
+	ilock(&clock0lock);
+	lp->clock = clock;
+	lp->link = clock0link;
+	clock0link = lp;
+	iunlock(&clock0lock);
+	return nil;
+}
+
+void
+delay(int l)
+{
+	ulong i, j;
+
+	j = m->delayloop;
+	while(l-- > 0)
+		for(i=0; i < j; i++)
+			;
+}
+
+void
+microdelay(int l)
+{
+	ulong i;
+
+	l *= m->delayloop;
+	l /= 1000;
+	if(l <= 0)
+		l = 1;
+	for(i = 0; i < l; i++)
+		;
+}
+
+enum {
+	Timebase = 4,	/* system clock cycles per time base cycle */
+};
+
+static	ulong	clkreload;
+
+void
+clockinit(void)
+{
+	long x;
+
+	m->delayloop = m->cpuhz/1000;	/* initial estimate */
+	do {
+		x = gettbl();
+		delay(10);
+		x = gettbl() - x;
+	} while(x < 0);
+
+	/*
+	 *  fix count
+	 */
+	m->delayloop = ((vlong)m->delayloop*(10*m->clockgen/1000))/(x*Timebase);
+	if(m->delayloop == 0)
+		m->delayloop = 1;
+
+	clkreload = (m->clockgen/Timebase)/HZ-1;
+	putdec(clkreload);
+}
+
+void
+clockintr(Ureg *ur)
+{
+	Clock0link *lp;
+	long v;
+
+	v = -getdec();
+	if(v > clkreload/2){
+		if(v > clkreload)
+			m->ticks += v/clkreload;
+		v = 0;
+	}
+	putdec(clkreload-v);
+
+	/* watchdog */
+	if(m->iomem->sypcr & (1<<2)){
+		m->iomem->swsr = 0x556c;
+		m->iomem->swsr = 0xaa39;
+	}
+
+	m->ticks++;
+	if(archclocktick != nil)
+		archclocktick();
+
+	if(up)
+		up->pc = ur->pc;
+
+	checkalarms();
+	if(m->machno == 0) {
+		if(kproftick != nil)
+			(*kproftick)(ur->pc);
+		if(canlock(&clock0lock)){
+			for(lp = clock0link; lp; lp = lp->link)
+				lp->clock();
+			unlock(&clock0lock);
+		}
+	}
+
+	if(up && up->state == Running){
+		if(cflag && up->type == Interp && tready(nil))
+			ur->cr |= 1;	/* set flag in condition register for ../../libinterp/comp-power.c:/^schedcheck */
+	}
+	/* other preemption checks are done by trap.c */
+}
+
+uvlong
+fastticks(uvlong *hz)
+{
+	if(hz)
+		*hz = HZ;
+	return m->ticks;
+}
--- /dev/null
+++ b/os/mpc/cpm.c
@@ -1,0 +1,695 @@
+#include "u.h"
+#include "lib.h"
+#include "mem.h"
+#include "dat.h"
+#include "fns.h"
+#include "io.h"
+
+typedef struct Chanuse Chanuse;
+struct Chanuse {
+	Lock;
+	void*	owner;
+} ;
+
+enum {
+	BDSIZE=	1024,	/* IO memory reserved for buffer descriptors */
+	CPMSIZE=	1024,	/* IO memory reserved for other uses */
+
+	/* channel IDs */
+	SCC1ID=	0,
+	I2CID=	1,
+	IDMA1ID= 1,
+	SCC2ID=	4,
+	SPIID=	5,
+	IDMA2ID= 5,
+	TIMERID=	5,
+	SCC3ID=	8,
+	SMC1ID=	9,
+	DSP1ID=	9,
+	SCC4ID=	12,
+	SMC2ID=	13,
+	DSP2ID=	13,
+	NCPMID=	16,
+
+	NSCC = 4,
+
+	/* SCC.gsmr_l */
+	ENR = 1<<5,	/* enable receiver */
+	ENT = 1<<4,	/* enable transmitter */
+
+	NSMC = 2,
+
+	/* SMC.smcmr */
+	TEN = 1<<1,	/* transmitter enable */
+	REN = 1<<0,	/* receiver enable */
+};
+
+static	Map	bdmapv[BDSIZE/sizeof(BD)];
+static	RMap	bdmap = {"buffer descriptors"};
+
+static	Map	cpmmapv[CPMSIZE/sizeof(ulong)];
+static	RMap	cpmmap = {"CPM memory"};
+
+static	Lock	cpmlock;
+
+static struct {
+	Lock;
+	ulong	avail;
+} brgens;
+
+static	Chanuse	cpmids[NCPMID];
+static	CPMdev	cpmdevinfo[] = {
+	[CPscc1] {SCC1ID, 0x1E, 0xA00, 0x3C00},
+	[CPscc2] {SCC2ID, 0x1D, 0xA20, 0x3D00},
+	[CPscc3] {SCC3ID, 0x1C, 0xA40, 0x3E00},
+	[CPscc4] {SCC4ID, 0x1B, 0xA60, 0x3F00},
+	[CPsmc1] {SMC1ID, 0x04, 0xA80, 0x3E80},
+	[CPsmc2] {SMC2ID, 0x03, 0xA90, 0x3F80},
+	[CPdsp1] {DSP1ID, 0x16, 0, 0x3EC0},
+	[CPdsp2] {DSP2ID, 0x16, 0, 0x3FC0},
+	[CPidma1] {IDMA1ID, 0x15, 0, 0x3CC0},
+	[CPidma2] {IDMA2ID, 0x14, 0, 0x3DC0},
+	[CPtimer] {TIMERID, 0x11, 0, 0x3DB0},
+	[CPspi] {SPIID, 0x05, 0xAA0, 0x3D80},	/* parameters relocated below */
+	[CPi2c] {I2CID, 0x10, 0x860, 0x3C80},	/* parameters relocated below */
+};
+
+static	void	i2cspireloc(void);
+static	void*	relocateparam(ulong, int);
+
+/*
+ * initialise the communications processor module
+ * and associated device registers
+ */
+void
+cpminit(void)
+{
+	IMM *io;
+
+	io = m->iomem;
+	io->sdcr = 1;
+	io->rccr = 0;
+	io->rmds = 0;
+	io->lccr = 0;	/* disable LCD */
+	io->vccr = 0;	/* disable video */
+	io->i2mod = 0;	/* stop I2C */
+	io->pcint = 0;	/* disable all port C interrupts */
+	io->pcso = 0;
+	io->pcdir =0;
+	io->pcpar = 0;
+	io->pcdat = 0;
+	io->papar = 0;
+	io->padir = 0;
+	io->paodr = 0;
+	io->padat = 0;
+	io->pbpar = 0;
+	io->pbdir = 0;
+	io->pbodr = 0;
+	io->pbdat = 0;
+	io->tgcr = 0x2222;	/* reset timers, low-power stop */
+	eieio();
+
+	for(io->cpcr = 0x8001; io->cpcr & 1;)	/* reset all CPM channels */
+		eieio();
+
+	mapinit(&bdmap, bdmapv, sizeof(bdmapv));
+	mapfree(&bdmap, DPBASE, BDSIZE);
+	mapinit(&cpmmap, cpmmapv, sizeof(cpmmapv));
+	mapfree(&cpmmap, DPBASE+BDSIZE, CPMSIZE);
+
+	if(m->cputype == 0x50 && (getimmr() & 0xFFFF) <= 0x2001)
+		brgens.avail = 0x3;
+	else
+		brgens.avail = 0xF;
+	i2cspireloc();
+}
+
+/*
+ * return parameters defining a CPM device, given logical ID
+ */
+CPMdev*
+cpmdev(int n)
+{
+	CPMdev *d;
+
+	if(n < 0 || n >= nelem(cpmdevinfo))
+		panic("cpmdev");
+	d = &cpmdevinfo[n];
+	if(d->param == nil && d->pbase != 0){
+		if((n == CPi2c || n == CPspi)){
+			d->param = relocateparam(d->pbase, 0xB0-0x80);	/* relocate */
+			if(d->param == nil)
+				return nil;
+		} else
+			d->param = (char*)m->iomem+d->pbase;
+	}
+	if(d->rbase != 0)
+		d->regs = (char*)m->iomem+d->rbase;
+	return d;
+}
+
+/*
+ * issue a request to a CPM device
+ */
+void
+cpmop(CPMdev *cpd, int op, int param)
+{
+	IMM *io;
+
+	ilock(&cpmlock);
+	io = m->iomem;
+	while(io->cpcr & 1)
+		eieio();
+	io->cpcr = (op<<8)|(cpd->id<<4)|(param<<1)|1;
+	eieio();
+	while(io->cpcr & 1)
+		eieio();
+	iunlock(&cpmlock);
+}
+
+/*
+ * lock the shared IO memory and return a reference to it
+ */
+IMM*
+ioplock(void)
+{
+	ilock(&cpmlock);
+	return m->iomem;
+}
+
+/*
+ * release the lock on the shared IO memory
+ */
+void
+iopunlock(void)
+{
+	eieio();
+	iunlock(&cpmlock);
+}
+
+/*
+ * connect SCCx clocks in NSMI mode (x=1 for USB)
+ */
+void
+sccnmsi(int x, int rcs, int tcs)
+{
+	IMM *io;
+	ulong v;
+	int sh;
+
+	sh = (x-1)*8;	/* each SCCx field in sicr is 8 bits */
+	v = (((rcs&7)<<3) | (tcs&7)) << sh;
+	io = ioplock();
+	io->sicr = (io->sicr & ~(0xFF<<sh)) | v;
+	iopunlock();
+}
+
+/*
+ * connect SMCx clock in NSMI mode
+ */
+void
+smcnmsi(int x, int cs)
+{
+	IMM *io;
+	ulong v;
+	int sh;
+
+	if(x == 1)
+		sh = 0;
+	else
+		sh = 16;
+	v = cs << (12+sh);
+	io = ioplock();
+	io->simode = (io->simode & ~(0xF000<<sh)) | v;	/* SMCx to NMSI mode, set Tx/Rx clock */
+	iopunlock();
+}
+
+/*
+ * claim the use of a CPM ID (SCC, SMC) that might be used by two mutually exclusive devices,
+ * for the caller determined by the given parameter (which must be unique).
+ * returns non-zero if the resource is already in use.
+ */
+int
+cpmidopen(int id, void *owner)
+{
+	Chanuse *use;
+
+	use = &cpmids[id];
+	ilock(use);
+	if(use->owner != nil && use->owner != owner){
+		iunlock(use);
+		return -1;
+	}
+	use->owner = owner;
+	iunlock(use);
+	return 0;
+}
+
+/*
+ * release a previously claimed CPM ID
+ */
+void
+cpmidclose(int id)
+{
+	Chanuse *use;
+
+	use = &cpmids[id];
+	ilock(use);
+	use->owner = nil;
+	iunlock(use);
+}
+
+/*
+ * if SCC d is currently enabled, shut it down
+ */
+void
+sccxstop(CPMdev *d)
+{
+	SCC *scc;
+
+	if(d == nil)
+		return;
+	scc = d->regs;
+	if(scc->gsmrl & (ENT|ENR)){
+		if(scc->gsmrl & ENT)
+			cpmop(d, GracefulStopTx, 0);
+		if(scc->gsmrl & ENR)
+			cpmop(d, CloseRxBD, 0);
+		delay(1);
+		scc->gsmrl &= ~(ENT|ENR);	/* disable current use */
+		eieio();
+	}
+	scc->sccm = 0;	/* mask interrupts */
+}
+
+/*
+ * if SMC d is currently enabled, shut it down
+ */
+void
+smcxstop(CPMdev *d)
+{
+	SMC *smc;
+
+	if(d == nil)
+		return;
+	smc = d->regs;
+	if(smc->smcmr & (TEN|REN)){
+		if(smc->smcmr & TEN)
+			cpmop(d, StopTx, 0);
+		if(smc->smcmr & REN)
+			cpmop(d, CloseRxBD, 0);
+		delay(1);
+		smc->smcmr &= ~(TEN|REN);
+		eieio();
+	}
+	smc->smcm = 0;	/* mask interrupts */
+}
+
+/*
+ * allocate a buffer descriptor
+ */
+BD *
+bdalloc(int n)
+{
+	ulong a;
+
+	a = rmapalloc(&bdmap, 0, n*sizeof(BD), sizeof(BD));
+	if(a == 0)
+		panic("bdalloc");
+	return KADDR(a);
+}
+
+/*
+ * free a buffer descriptor
+ */
+void
+bdfree(BD *b, int n)
+{
+	if(b){
+		eieio();
+		mapfree(&bdmap, PADDR(b), n*sizeof(BD));
+	}
+}
+
+/*
+ * print a buffer descriptor and its data (when debugging)
+ */
+void
+dumpbd(char *name, BD *b, int maxn)
+{
+	uchar *d;
+	int i;
+
+	print("%s #%4.4lux: s=#%4.4ux l=%ud a=#%8.8lux", name, PADDR(b)&0xFFFF, b->status, b->length, b->addr);
+	if(maxn > b->length)
+		maxn = b->length;
+	if(b->addr != 0){
+		d = KADDR(b->addr);
+		for(i=0; i<maxn; i++)
+			print(" %2.2ux", d[i]);
+		if(i < b->length)
+			print(" ...");
+	}
+	print("\n");
+}
+
+/*
+ * allocate memory from the shared IO memory space
+ */
+void *
+cpmalloc(int n, int align)
+{
+	ulong a;
+
+	a = rmapalloc(&cpmmap, 0, n, align);
+	if(a == 0)
+		panic("cpmalloc");
+	return KADDR(a);
+}
+
+/*
+ * free previously allocated shared memory
+ */
+void
+cpmfree(void *p, int n)
+{
+	if(p != nil && n > 0){
+		eieio();
+		mapfree(&cpmmap, PADDR(p), n);
+	}
+}
+
+/*
+ * allocate a baud rate generator, returning its index
+ * (or -1 if none is available)
+ */
+int
+brgalloc(void)
+{
+	int n;
+
+	lock(&brgens);
+	for(n=0; brgens.avail!=0; n++)
+		if(brgens.avail & (1<<n)){
+			brgens.avail &= ~(1<<n);
+			unlock(&brgens);
+			return n;
+		}
+	unlock(&brgens);
+	return -1;
+}
+
+/*
+ * free a previously allocated baud rate generator
+ */
+void
+brgfree(int n)
+{
+	if(n >= 0){
+		if(n > 3 || brgens.avail & (1<<n))
+			panic("brgfree");
+		lock(&brgens);
+		brgens.avail |= 1 << n;
+		unlock(&brgens);
+	}
+}
+
+/*
+ * return a value suitable for loading into a baud rate
+ * generator to produce the given rate if the generator
+ * is prescaled by the given amount (typically 16).
+ * the value must be or'd with BaudEnable to start the generator.
+ */
+ulong
+baudgen(int rate, int scale)
+{
+	int d;
+
+	rate *= scale;
+	d = (2*m->cpuhz+rate)/(2*rate) - 1;
+	if(d < 0)
+		d = 0;
+	if(d >= (1<<12))
+		return ((d+15)>>(4-1))|1;	/* divider too big: enable prescale by 16 */
+	return d<<1;
+}
+
+/*
+ * initialise receive and transmit buffer rings.
+ */
+int
+ioringinit(Ring* r, int nrdre, int ntdre, int bufsize)
+{
+	int i, x;
+
+	/* the ring entries must be aligned on sizeof(BD) boundaries */
+	r->nrdre = nrdre;
+	if(r->rdr == nil)
+		r->rdr = bdalloc(nrdre);
+	/* the buffer size must align with cache lines since the cache doesn't snoop */
+	bufsize = (bufsize+CACHELINESZ-1)&~(CACHELINESZ-1);
+	if(r->rrb == nil)
+		r->rrb = malloc(nrdre*bufsize);
+	if(r->rdr == nil || r->rrb == nil)
+		return -1;
+	dcflush(r->rrb, nrdre*bufsize);
+	x = PADDR(r->rrb);
+	for(i = 0; i < nrdre; i++){
+		r->rdr[i].length = 0;
+		r->rdr[i].addr = x;
+		r->rdr[i].status = BDEmpty|BDInt;
+		x += bufsize;
+	}
+	r->rdr[i-1].status |= BDWrap;
+	r->rdrx = 0;
+
+	r->ntdre = ntdre;
+	if(r->tdr == nil)
+		r->tdr = bdalloc(ntdre);
+	if(r->txb == nil)
+		r->txb = malloc(ntdre*sizeof(Block*));
+	if(r->tdr == nil || r->txb == nil)
+		return -1;
+	for(i = 0; i < ntdre; i++){
+		r->txb[i] = nil;
+		r->tdr[i].addr = 0;
+		r->tdr[i].length = 0;
+		r->tdr[i].status = 0;
+	}
+	r->tdr[i-1].status |= BDWrap;
+	r->tdrh = 0;
+	r->tdri = 0;
+	r->ntq = 0;
+	return 0;
+}
+
+/*
+ * Allocate a new parameter block for I2C or SPI,
+ * and plant a pointer to it for the microcode, returning the kernel address.
+ * See Motorola errata and microcode package:
+ * the design botch is that the parameters for the SCC2 ethernet overlap the
+ * SPI/I2C parameter space; this compensates by relocating the latter.
+ * This routine may be used iff i2cspireloc is used (and it is, above).
+ */
+static void*
+relocateparam(ulong olda, int nb)
+{
+	void *p;
+
+	if(olda < (ulong)m->iomem)
+		olda += (ulong)m->iomem;
+	p = cpmalloc(nb, 32);	/* ``RPBASE must be multiple of 32'' */
+	if(p == nil)
+		return p;
+	*(ushort*)KADDR(olda+0x2C) = PADDR(p);	/* set RPBASE */
+	eieio();
+	return p;
+}
+
+/*
+ * I2C/SPI microcode package from Motorola
+ * (to relocate I2C/SPI parameters), which was distributed
+ * on their web site in S-record format.
+ *
+ *	May 1998
+ */
+
+/*S00600004844521B*/
+static	ulong	ubase1 = 0x2000;
+static	ulong	ucode1[] = {
+ /* #02202000 */ 0x7FFFEFD9,
+ /* #02202004 */ 0x3FFD0000,
+ /* #02202008 */ 0x7FFB49F7,
+ /* #0220200C */ 0x7FF90000,
+ /* #02202010 */ 0x5FEFADF7,
+ /* #02202014 */ 0x5F89ADF7,
+ /* #02202018 */ 0x5FEFAFF7,
+ /* #0220201C */ 0x5F89AFF7,
+ /* #02202020 */ 0x3A9CFBC8,
+ /* #02202024 */ 0xE7C0EDF0,
+ /* #02202028 */ 0x77C1E1BB,
+ /* #0220202C */ 0xF4DC7F1D,
+ /* #02202030 */ 0xABAD932F,
+ /* #02202034 */ 0x4E08FDCF,
+ /* #02202038 */ 0x6E0FAFF8,
+ /* #0220203C */ 0x7CCF76CF,
+ /* #02202040 */ 0xFD1FF9CF,
+ /* #02202044 */ 0xABF88DC6,
+ /* #02202048 */ 0xAB5679F7,
+ /* #0220204C */ 0xB0937383,
+ /* #02202050 */ 0xDFCE79F7,
+ /* #02202054 */ 0xB091E6BB,
+ /* #02202058 */ 0xE5BBE74F,
+ /* #0220205C */ 0xB3FA6F0F,
+ /* #02202060 */ 0x6FFB76CE,
+ /* #02202064 */ 0xEE0DF9CF,
+ /* #02202068 */ 0x2BFBEFEF,
+ /* #0220206C */ 0xCFEEF9CF,
+ /* #02202070 */ 0x76CEAD24,
+ /* #02202074 */ 0x90B2DF9A,
+ /* #02202078 */ 0x7FDDD0BF,
+ /* #0220207C */ 0x4BF847FD,
+ /* #02202080 */ 0x7CCF76CE,
+ /* #02202084 */ 0xCFEF7E1F,
+ /* #02202088 */ 0x7F1D7DFD,
+ /* #0220208C */ 0xF0B6EF71,
+ /* #02202090 */ 0x7FC177C1,
+ /* #02202094 */ 0xFBC86079,
+ /* #02202098 */ 0xE722FBC8,
+ /* #0220209C */ 0x5FFFDFFF,
+ /* #022020A0 */ 0x5FB2FFFB,
+ /* #022020A4 */ 0xFBC8F3C8,
+ /* #022020A8 */ 0x94A67F01,
+ /* #022020AC */ 0x7F1D5F39,
+ /* #022020B0 */ 0xAFE85F5E,
+ /* #022020B4 */ 0xFFDFDF96,
+ /* #022020B8 */ 0xCB9FAF7D,
+ /* #022020BC */ 0x5FC1AFED,
+ /* #022020C0 */ 0x8C1C5FC1,
+ /* #022020C4 */ 0xAFDD5FC3,
+ /* #022020C8 */ 0xDF9A7EFD,
+ /* #022020CC */ 0xB0B25FB2,
+ /* #022020D0 */ 0xFFFEABAD,
+ /* #022020D4 */ 0x5FB2FFFE,
+ /* #022020D8 */ 0x5FCE600B,
+ /* #022020DC */ 0xE6BB600B,
+ /* #022020E0 */ 0x5FCEDFC6,
+ /* #022020E4 */ 0x27FBEFDF,
+ /* #022020E8 */ 0x5FC8CFDE,
+ /* #022020EC */ 0x3A9CE7C0,
+ /* #022020F0 */ 0xEDF0F3C8,
+ /* #022020F4 */ 0x7F0154CD,
+ /* #022020F8 */ 0x7F1D2D3D,
+ /* #022020FC */ 0x363A7570,
+ /* #02202100 */ 0x7E0AF1CE,
+ /* #02202104 */ 0x37EF2E68,
+ /* #02202108 */ 0x7FEE10EC,
+ /* #0220210C */ 0xADF8EFDE,
+ /* #02202110 */ 0xCFEAE52F,
+ /* #02202114 */ 0x7D0FE12B,
+ /* #02202118 */ 0xF1CE5F65,
+ /* #0220211C */ 0x7E0A4DF8,
+ /* #02202120 */ 0xCFEA5F72,
+ /* #02202124 */ 0x7D0BEFEE,
+ /* #02202128 */ 0xCFEA5F74,
+ /* #0220212C */ 0xE522EFDE,
+ /* #02202130 */ 0x5F74CFDA,
+ /* #02202134 */ 0x0B627385,
+ /* #02202138 */ 0xDF627E0A,
+ /* #0220213C */ 0x30D8145B,
+ /* #02202140 */ 0xBFFFF3C8,
+ /* #02202144 */ 0x5FFFDFFF,
+ /* #02202148 */ 0xA7F85F5E,
+ /* #0220214C */ 0xBFFE7F7D,
+ /* #02202150 */ 0x10D31450,
+ /* #02202154 */ 0x5F36BFFF,
+ /* #02202158 */ 0xAF785F5E,
+ /* #0220215C */ 0xBFFDA7F8,
+ /* #02202160 */ 0x5F36BFFE,
+ /* #02202164 */ 0x77FD30C0,
+ /* #02202168 */ 0x4E08FDCF,
+ /* #0220216C */ 0xE5FF6E0F,
+ /* #02202170 */ 0xAFF87E1F,
+ /* #02202174 */ 0x7E0FFD1F,
+ /* #02202178 */ 0xF1CF5F1B,
+ /* #0220217C */ 0xABF80D5E,
+ /* #02202180 */ 0x5F5EFFEF,
+ /* #02202184 */ 0x79F730A2,
+ /* #02202188 */ 0xAFDD5F34,
+ /* #0220218C */ 0x47F85F34,
+ /* #02202190 */ 0xAFED7FDD,
+ /* #02202194 */ 0x50B24978,
+ /* #02202198 */ 0x47FD7F1D,
+ /* #0220219C */ 0x7DFD70AD,
+ /* #022021A0 */ 0xEF717EC1,
+ /* #022021A4 */ 0x6BA47F01,
+ /* #022021A8 */ 0x2D267EFD,
+ /* #022021AC */ 0x30DE5F5E,
+ /* #022021B0 */ 0xFFFD5F5E,
+ /* #022021B4 */ 0xFFEF5F5E,
+ /* #022021B8 */ 0xFFDF0CA0,
+ /* #022021BC */ 0xAFED0A9E,
+ /* #022021C0 */ 0xAFDD0C3A,
+ /* #022021C4 */ 0x5F3AAFBD,
+ /* #022021C8 */ 0x7FBDB082,
+ /* #022021CC */ 0x5F8247F8,
+};
+
+/*S00600004844521B*/
+static	ulong	ubase2 = 0x2F00;
+static	ulong	ucode2[] = {
+ /* #02202F00 */ 0x3E303430,
+ /* #02202F04 */ 0x34343737,
+ /* #02202F08 */ 0xABF7BF9B,
+ /* #02202F0C */ 0x994B4FBD,
+ /* #02202F10 */ 0xBD599493,
+ /* #02202F14 */ 0x349FFF37,
+ /* #02202F18 */ 0xFB9B177D,
+ /* #02202F1C */ 0xD9936956,
+ /* #02202F20 */ 0xBBFDD697,
+ /* #02202F24 */ 0xBDD2FD11,
+ /* #02202F28 */ 0x31DB9BB3,
+ /* #02202F2C */ 0x63139637,
+ /* #02202F30 */ 0x93733693,
+ /* #02202F34 */ 0x193137F7,
+ /* #02202F38 */ 0x331737AF,
+ /* #02202F3C */ 0x7BB9B999,
+ /* #02202F40 */ 0xBB197957,
+ /* #02202F44 */ 0x7FDFD3D5,
+ /* #02202F48 */ 0x73B773F7,
+ /* #02202F4C */ 0x37933B99,
+ /* #02202F50 */ 0x1D115316,
+ /* #02202F54 */ 0x99315315,
+ /* #02202F58 */ 0x31694BF4,
+ /* #02202F5C */ 0xFBDBD359,
+ /* #02202F60 */ 0x31497353,
+ /* #02202F64 */ 0x76956D69,
+ /* #02202F68 */ 0x7B9D9693,
+ /* #02202F6C */ 0x13131979,
+ /* #02202F70 */ 0x79376935,
+};
+
+/*
+ * compensate for chip design botch by installing
+ * microcode to relocate I2C and SPI parameters away
+ * from the ethernet parameters
+ */
+static void
+i2cspireloc(void)
+{
+	IMM *io;
+	static int done;
+
+	if(done)
+		return;
+	io = m->iomem;
+	io->rccr &= ~3;
+	memmove((uchar*)m->iomem+ubase1, ucode1, sizeof(ucode1));
+	memmove((uchar*)m->iomem+ubase2, ucode2, sizeof(ucode2));
+	io->rctr1 = 0x802a;	/* relocate SPI */
+	io->rctr2 = 0x8028;	/* relocate SPI */
+	io->rctr3 = 0x802e;	/* relocate I2C */
+	io->rctr4 = 0x802c;	/* relocate I2C */
+	io->rccr |= 1;
+	done = 1;
+}
--- /dev/null
+++ b/os/mpc/cpmtimer.c
@@ -1,0 +1,179 @@
+#include "u.h"
+#include "lib.h"
+#include "mem.h"
+#include "dat.h"
+#include "fns.h"
+#include "io.h"
+
+enum {
+	Ntimer = 4	/* maximum allowed by hardware */
+};
+
+static struct {
+	Lock;
+	int	init;
+	int	ntimer;	/* actual timers on this chip revision */
+	GTimer	t[Ntimer];
+} cpmtimers;
+
+static	uchar	timerirq[] = {0x19, 0x12, 0x0C, 0x07};
+
+static	void	gtimerinit(int, ushort*, ushort*);
+
+static void
+gtimerreset(void)
+{
+	IMM *io;
+	int i;
+
+	ilock(&cpmtimers);
+	if(!cpmtimers.init){
+		if(m->cputype == 0x50 && (getimmr() & 0xFFFF) <= 0x2001)
+			cpmtimers.ntimer = 2;
+		else
+			cpmtimers.ntimer = Ntimer;
+		io = m->iomem;
+		io->tgcr = 0x2222;	/* reset timers, low-power stop */
+		for(i=0; i<cpmtimers.ntimer; i++)
+			gtimerinit(i, &io->tmr1+i, &io->ter1+i);
+		cpmtimers.init = 1;
+	}
+	iunlock(&cpmtimers);
+}
+
+static void
+gtimerintr(Ureg *ur, void *arg)
+{
+	GTimer *t;
+
+	t = arg;
+	t->event = *t->ter;
+	*t->ter = t->event;
+	if(t->inuse && t->interrupt != nil)
+		t->interrupt(ur, t->arg, t);
+}
+
+static void
+gtimerinit(int i, ushort *tmr, ushort *ter)
+{
+	GTimer *t;
+	char name[KNAMELEN];
+
+	snprint(name, sizeof(name), "timer.%d", i);
+	t = &cpmtimers.t[i];
+	t->x = i*4;	/* field in tgcr */
+	t->inuse = 0;
+	t->interrupt = nil;
+	t->tmr = tmr;
+	t->trr = tmr+2;
+	t->tcr = tmr+4;
+	t->tcn = tmr+6;
+	t->ter = ter;
+	intrenable(VectorCPIC+timerirq[i], gtimerintr, t, BUSUNKNOWN, name);
+}
+
+GTimer*
+gtimer(ushort mode, ushort ref, void (*intr)(Ureg*,void*,GTimer*), void *arg)
+{
+	GTimer *t;
+	int i;
+
+	t = cpmtimers.t;
+	if(!cpmtimers.init)
+		gtimerreset();
+	ilock(&cpmtimers);
+	for(i=0; ; i++){
+		if(i >= cpmtimers.ntimer){
+			iunlock(&cpmtimers);
+			return nil;
+		}
+		if(t->inuse == 0)
+			break;
+		t++;
+	}
+	t->inuse = 1;
+	t->interrupt = intr;
+	t->arg = arg;
+	m->iomem->tgcr &= ~(0xF<<t->x);	/* reset */
+	*t->tmr = mode;
+	*t->tcn = 0;
+	*t->trr = ref;
+	*t->ter = 0xFFFF;
+	iunlock(&cpmtimers);
+	return t;
+}
+
+void
+gtimerset(GTimer *t, ushort mode, int usec)
+{
+	ulong ref, ps;
+	int clk;
+
+	if(usec <= 0)
+		return;
+	ref = usec*m->speed;
+	clk = mode & (3<<1);
+	if(ref >= 0x1000000 && clk == TimerSclk){
+		mode = (mode & ~clk) | TimerSclk16;
+		ref >>= 4;
+	} else if(clk == TimerSclk16)
+		ref >>= 4;
+	ps = (ref+(1<<16))/(1<<16);	/* round up */
+	ref /= ps;
+	*t->tmr = ((ps-1)<<8) | (mode&0xFF);
+	*t->trr = ref;
+}
+
+void
+gtimerstart(GTimer *t)
+{
+	if(t){
+		ilock(&cpmtimers);
+		m->iomem->tgcr = (m->iomem->tgcr & ~(0xF<<t->x)) | (1<<t->x);	/* enable */
+		iunlock(&cpmtimers);
+	}
+}
+
+void
+gtimerstop(GTimer *t)
+{
+	if(t){
+		ilock(&cpmtimers);
+		m->iomem->tgcr |= 2<<t->x;	/* stop */
+		iunlock(&cpmtimers);
+	}
+}
+
+void
+gtimerfree(GTimer *t)
+{
+	if(t){
+		ilock(&cpmtimers);
+		t->inuse = 0;
+		*t->tmr = 0;	/* disable interrupts */
+		*t->ter = 0xFFFF;
+		m->iomem->tgcr = (m->iomem->tgcr & ~(0xF<<t->x)) | (2<<t->x);	/* reset and stop */
+		iunlock(&cpmtimers);
+	}
+}
+
+#ifdef GTIMETEST
+static void
+gtintr(Ureg*, void*, GTimer*)
+{
+	m->bcsr[4] ^= DisableVideoLamp;	/* toggle an LED */
+}
+
+void
+gtimetest(void)
+{
+	GTimer *g;
+
+	g = gtimer(0, 0, gtintr, nil);
+	gtimerset(g, TimerORI|TimerRestart|TimerSclk, 64000);
+	gtimerstart(g);
+	delay(1);
+print("started timer: #%4.4ux #%4.4ux %8.8lux #%4.4ux #%4.4ux\n", *g->tmr, *g->trr, m->iomem->tgcr, *g->tcn, *g->ter);
+print("ter=#%8.8lux tmr=#%8.8lux trr=#%8.8lux\n", g->ter, g->tmr, g->trr);
+}
+#endif
--- /dev/null
+++ b/os/mpc/devata.c
@@ -1,0 +1,1194 @@
+#include	"u.h"
+#include	"../port/lib.h"
+#include	"mem.h"
+#include	"dat.h"
+#include	"fns.h"
+#include	"io.h"
+#include	"../port/error.h"
+
+#include	"pcmcia.h"
+
+#define DPRINT if(0)print
+
+typedef	struct Drive		Drive;
+typedef	struct Ident		Ident;
+typedef	struct Controller	Controller;
+typedef struct Partition	Partition;
+typedef struct Repl		Repl;
+
+enum
+{
+	/* ports */
+	Pbase=		0x1F0,
+	Pdata=		0,	/* data port (16 bits) */
+	Perror=		1,	/* error port (read) */
+	Pprecomp=	1,	/* buffer mode port (write) */
+	Pcount=		2,	/* sector count port */
+	Psector=	3,	/* sector number port */
+	Pcyllsb=	4,	/* least significant byte cylinder # */
+	Pcylmsb=	5,	/* most significant byte cylinder # */
+	Pdh=		6,	/* drive/head port */
+	Pstatus=	7,	/* status port (read) */
+	 Sbusy=		 (1<<7),
+	 Sready=	 (1<<6),
+	 Sdrq=		 (1<<3),
+	 Serr=		 (1<<0),
+	Pcmd=		7,	/* cmd port (write) */
+
+	/* commands */
+	Crecal=		0x10,
+	Cread=		0x20,
+	Cwrite=		0x30,
+	Cident=		0xEC,
+	Cident2=	0xFF,	/* pseudo command for post Cident interrupt */
+	Csetbuf=	0xEF,
+	Cinitparam=	0x91,
+
+	/* conner specific commands */
+	Cstandby=	0xE2,
+	Cidle=		0xE1,
+	Cpowerdown=	0xE3,
+
+	/* disk states */
+	Sspinning,
+	Sstandby,
+	Sidle,
+	Spowerdown,
+
+	/* something we have to or into the drive/head reg */
+	DHmagic=	0xA0,
+
+	/* file types */
+	Qdir=		0,
+
+	Maxxfer=	BY2PG,		/* maximum transfer size/cmd */
+	Npart=		8+2,		/* 8 sub partitions, disk, and partition */
+	Nrepl=		64,		/* maximum replacement blocks */
+};
+#define PART(x)		((x)&0xF)
+#define DRIVE(x)	(((x)>>4)&0x7)
+#define MKQID(d,p)	(((d)<<4) | (p))
+
+struct Partition
+{
+	ulong	start;
+	ulong	end;
+	char	name[NAMELEN+1];
+};
+
+struct Repl
+{
+	Partition *p;
+	int	nrepl;
+	ulong	blk[Nrepl];
+};
+
+#define PARTMAGIC	"plan9 partitions"
+#define REPLMAGIC	"block replacements"
+
+/*
+ *  an ata drive
+ */
+struct Drive
+{
+	QLock;
+
+	Controller *cp;
+	int	drive;
+	int	confused;	/* needs to be recalibrated (or worse) */
+	int	online;
+	int	npart;		/* number of real partitions */
+	Partition p[Npart];
+	Repl	repl;
+	ulong	usetime;
+	int	state;
+	char	vol[NAMELEN];
+
+	ulong	cap;		/* total bytes */
+	int	bytes;		/* bytes/sector */
+	int	sectors;	/* sectors/track */
+	int	heads;		/* heads/cyl */
+	long	cyl;		/* cylinders/drive */
+
+	char	lba;		/* true if drive has logical block addressing */
+	char	multi;		/* non-zero if drive does multiple block xfers */
+};
+
+/*
+ *  a controller for 2 drives
+ */
+struct Controller
+{
+	QLock;			/* exclusive access to the controller */
+	ISAConf;		/* interface to pcmspecial */
+
+	Lock	reglock;	/* exclusive access to the registers */
+
+	int	confused;	/* needs to be recalibrated (or worse) */
+	ulong	pbase;		/* base port (copied from ISAConf) */
+
+	/*
+	 *  current operation
+	 */
+	int	cmd;		/* current command */
+	int	lastcmd;	/* debugging info */
+	Rendez	r;		/* wait here for command termination */
+	char	*buf;		/* xfer buffer */
+	int	nsecs;		/* length of transfer (sectors) */
+	int	sofar;		/* sectors transferred so far */
+	int	status;
+	int	error;
+	Drive	*dp;		/* drive being accessed */
+};
+
+Controller	*atac;
+Drive		*ata;
+static char*	ataerr;
+static int	nhard;
+static int	spindowntime;
+
+static void	ataintr(Ureg*, void*);
+static long	ataxfer(Drive*, Partition*, int, long, long, char*);
+static void	ataident(Drive*);
+static void	atasetbuf(Drive*, int);
+static void	ataparams(Drive*);
+static void	atapart(Drive*);
+static int	ataprobe(Drive*, int, int, int);
+
+static int
+atagen(Chan *c, Dirtab*, int, int s, Dir *dirp)
+{
+	Qid qid;
+	int drive;
+	Drive *dp;
+	Partition *pp;
+	ulong l;
+
+	qid.vers = 0;
+	drive = s/Npart;
+	s = s % Npart;
+	if(drive >= nhard)
+		return -1;
+	dp = &ata[drive];
+
+	if(dp->online == 0 || s >= dp->npart)
+		return 0;
+
+	pp = &dp->p[s];
+	sprint(up->genbuf, "%s%s", dp->vol, pp->name);
+	qid.path = MKQID(drive, s);
+	l = (pp->end - pp->start) * dp->bytes;
+	devdir(c, qid, up->genbuf, l, eve, 0660, dirp);
+	return 1;
+}
+
+static void
+atainit(void)
+{
+	Drive *dp;
+	Controller *cp;
+	uchar equip;
+	int pcmslot;
+
+	if (atac)
+		return;		/* already done */
+
+	equip = 0x10;		/* hard coded */
+
+	print("ata init\n");
+	cp = malloc(sizeof(*cp));
+	if (!cp)
+		error(Enomem);
+
+	cp->port = Pbase;
+	cp->irq = 14;
+
+	if((pcmslot = pcmspecial("SunDisk", cp)) < 0) {
+		print("No ATA card\n");
+		free(cp);
+		ataerr = Enoifc;
+		return;
+	}
+	ata = malloc(2 * sizeof(*ata));
+	if(ata == nil) {
+		pcmspecialclose(pcmslot);
+		free(cp);
+		error(Enomem);
+	}
+
+	atac = cp;
+	cp->buf = 0;
+	cp->lastcmd = cp->cmd;
+	cp->cmd = 0;
+	cp->pbase = cp->port;
+	pcmintrenable(pcmslot, ataintr, cp);
+
+	dp = ata;
+	if(equip & 0xf0){
+		dp->drive = 0;
+		dp->online = 0;
+		dp->cp = cp;
+		dp++;
+	}
+	if((equip & 0x0f)){
+		dp->drive = 1;
+		dp->online = 0;
+		dp->cp = cp;
+		dp++;
+	}
+	nhard = dp - ata;
+	
+	spindowntime = 1;
+}
+
+
+/*
+ *  Get the characteristics of each drive.  Mark unresponsive ones
+ *  off line.
+ */
+static Chan*
+ataattach(char *spec)
+{
+	Drive *dp;
+
+	atainit();
+	if (!ata)
+		error(ataerr ? ataerr : Enoifc);
+	for(dp = ata; dp < &ata[nhard]; dp++){
+		if(waserror()){
+			dp->online = 0;
+			qunlock(dp);
+			continue;
+		}
+		qlock(dp);
+		if(!dp->online){
+			/*
+			 * Make sure ataclock() doesn't
+			 * interfere.
+			 */
+			dp->usetime = m->ticks;
+			ataparams(dp);
+			dp->online = 1;
+			atasetbuf(dp, 1);
+		}
+
+		/*
+		 *  read Plan 9 partition table
+		 */
+		atapart(dp);
+		qunlock(dp);
+		poperror();
+	}
+	return devattach('H', spec);
+}
+
+static int
+atawalk(Chan *c, char *name)
+{
+	return devwalk(c, name, 0, 0, atagen);
+}
+
+static void
+atastat(Chan *c, char *dp)
+{
+	devstat(c, dp, 0, 0, atagen);
+}
+
+static Chan*
+ataopen(Chan *c, int omode)
+{
+	return devopen(c, omode, 0, 0, atagen);
+}
+
+static void
+ataclose(Chan *c)
+{
+	Drive *d;
+	Partition *p;
+
+	if(c->mode != OWRITE && c->mode != ORDWR)
+		return;
+
+	d = &ata[DRIVE(c->qid.path)];
+	p = &d->p[PART(c->qid.path)];
+	if(strcmp(p->name, "partition") != 0)
+		return;
+
+	if(waserror()){
+		qunlock(d);
+		nexterror();
+	}
+	qlock(d);
+	atapart(d);
+	qunlock(d);
+	poperror();
+}
+
+static long
+ataread(Chan *c, void *a, long n, vlong offset)
+{
+	Drive *dp;
+	long rv, i;
+	int skip;
+	uchar *aa = a;
+	Partition *pp;
+	char *buf;
+
+	if(c->qid.path == CHDIR)
+		return devdirread(c, a, n, 0, 0, atagen);
+
+	buf = smalloc(Maxxfer);
+	if(waserror()){
+		free(buf);
+		nexterror();
+	}
+
+	dp = &ata[DRIVE(c->qid.path)];
+	pp = &dp->p[PART(c->qid.path)];
+
+	skip = offset % dp->bytes;
+	for(rv = 0; rv < n; rv += i){
+		i = ataxfer(dp, pp, Cread, offset+rv-skip, n-rv+skip, buf);
+		if(i == 0)
+			break;
+		i -= skip;
+		if(i > n - rv)
+			i = n - rv;
+		memmove(aa+rv, buf + skip, i);
+		skip = 0;
+	}
+
+	free(buf);
+	poperror();
+
+	return rv;
+}
+
+static long
+atawrite(Chan *c, void *a, long n, vlong offset)
+{
+	Drive *dp;
+	long rv, i, partial;
+	uchar *aa = a;
+	Partition *pp;
+	char *buf;
+
+	if(c->qid.path == CHDIR)
+		error(Eisdir);
+
+	dp = &ata[DRIVE(c->qid.path)];
+	pp = &dp->p[PART(c->qid.path)];
+	buf = smalloc(Maxxfer);
+	if(waserror()){
+		free(buf);
+		nexterror();
+	}
+
+	/*
+	 *  if not starting on a sector boundary,
+	 *  read in the first sector before writing
+	 *  it out.
+	 */
+	partial = offset % dp->bytes;
+	if(partial){
+		ataxfer(dp, pp, Cread, offset-partial, dp->bytes, buf);
+		if(partial+n > dp->bytes)
+			rv = dp->bytes - partial;
+		else
+			rv = n;
+		memmove(buf+partial, aa, rv);
+		ataxfer(dp, pp, Cwrite, offset-partial, dp->bytes, buf);
+	} else
+		rv = 0;
+
+	/*
+	 *  write out the full sectors
+	 */
+	partial = (n - rv) % dp->bytes;
+	n -= partial;
+	for(; rv < n; rv += i){
+		i = n - rv;
+		if(i > Maxxfer)
+			i = Maxxfer;
+		memmove(buf, aa+rv, i);
+		i = ataxfer(dp, pp, Cwrite, offset+rv, i, buf);
+		if(i == 0)
+			break;
+	}
+
+	/*
+	 *  if not ending on a sector boundary,
+	 *  read in the last sector before writing
+	 *  it out.
+	 */
+	if(partial){
+		ataxfer(dp, pp, Cread, offset+rv, dp->bytes, buf);
+		memmove(buf, aa+rv, partial);
+		ataxfer(dp, pp, Cwrite, offset+rv, dp->bytes, buf);
+		rv += partial;
+	}
+
+	free(buf);
+	poperror();
+
+	return rv;
+}
+
+/*
+ *  did an interrupt happen?
+ */
+static int
+cmddone(void *a)
+{
+	Controller *cp = a;
+
+	return cp->cmd == 0;
+}
+
+/*
+ * Wait for the controller to be ready to accept a command.
+ * This is protected from intereference by ataclock() by
+ * setting dp->usetime before it is called.
+ */
+static void
+cmdreadywait(Drive *dp)
+{
+	long start;
+	int period;
+	Controller *cp = dp->cp;
+
+	/* give it 2 seconds to spin down and up */
+	if(dp->state == Sspinning)
+		period = 10;
+	else
+		period = 2000;
+
+	start = m->ticks;
+	while((inb(cp->pbase+Pstatus) & (Sready|Sbusy)) != Sready)
+		if(TK2MS(m->ticks - start) > period){
+			DPRINT("cmdreadywait failed\n");
+			error(Eio);
+		}
+}
+
+static void
+atarepl(Drive *dp, long bblk)
+{
+	int i;
+
+	if(dp->repl.p == 0)
+		return;
+	for(i = 0; i < dp->repl.nrepl; i++){
+		if(dp->repl.blk[i] == bblk)
+			DPRINT("found bblk %ld at offset %ld\n", bblk, i);
+	}
+}
+
+static void
+atasleep(Controller *cp, int ms)
+{
+        tsleep(&cp->r, cmddone, cp, ms);
+	if(cp->cmd && cp->cmd != Cident2){
+		DPRINT("ata: cmd 0x%uX timeout, status=%lux\n",
+				cp->cmd, inb(cp->pbase+Pstatus));
+		error("ata drive timeout");
+	}
+}
+
+/*
+ *  transfer a number of sectors.  ataintr will perform all the iterative
+ *  parts.
+ */
+static long
+ataxfer(Drive *dp, Partition *pp, int cmd, long start, long len, char *buf)
+{
+	Controller *cp;
+	long lblk;
+	int cyl, sec, head;
+	int loop, stat;
+
+	if(dp->online == 0)
+		error(Eio);
+
+	/*
+	 *  cut transfer size down to disk buffer size
+	 */
+	start = start / dp->bytes;
+	if(len > Maxxfer)
+		len = Maxxfer;
+	len = (len + dp->bytes - 1) / dp->bytes;
+	if(len == 0)
+		return 0;
+
+	/*
+	 *  calculate physical address
+	 */
+	lblk = start + pp->start;
+	if(lblk >= pp->end)
+		return 0;
+	if(lblk+len > pp->end)
+		len = pp->end - lblk;
+	if(dp->lba){
+		sec = lblk & 0xff;
+		cyl = (lblk>>8) & 0xffff;
+		head = (lblk>>24) & 0xf;
+	} else {
+		cyl = lblk/(dp->sectors*dp->heads);
+		sec = (lblk % dp->sectors) + 1;
+		head = ((lblk/dp->sectors) % dp->heads);
+	}
+
+	DPRINT("<%s %d>", (cmd == Cwrite) ? "W" : "R", lblk);
+	cp = dp->cp;
+	qlock(cp);
+	if(waserror()){
+		cp->buf = 0;
+		qunlock(cp);
+		nexterror();
+	}
+
+	/*
+	 * Make sure hardclock() doesn't
+	 * interfere.
+	 */
+	dp->usetime = m->ticks;
+	cmdreadywait(dp);
+
+	ilock(&cp->reglock);
+	cp->sofar = 0;
+	cp->buf = buf;
+	cp->nsecs = len;
+	cp->cmd = cmd;
+	cp->dp = dp;
+	cp->status = 0;
+
+	outb(cp->pbase+Pcount, cp->nsecs);
+	outb(cp->pbase+Psector, sec);
+	outb(cp->pbase+Pdh, DHmagic | (dp->drive<<4) | (dp->lba<<6) | head);
+	outb(cp->pbase+Pcyllsb, cyl);
+	outb(cp->pbase+Pcylmsb, cyl>>8);
+	outb(cp->pbase+Pcmd, cmd);
+
+	if(cmd == Cwrite){
+		loop = 0;
+		microdelay(1);
+		while((stat = inb(cp->pbase+Pstatus) & (Serr|Sdrq)) == 0)
+			if(++loop > 10000)
+				panic("ataxfer");
+		outss(cp->pbase+Pdata, cp->buf, dp->bytes/2);
+	} else
+		stat = 0;
+	iunlock(&cp->reglock);
+
+	if(stat & Serr)
+		error(Eio);
+
+	/*
+	 *  wait for command to complete.  if we get a note,
+	 *  remember it but keep waiting to let the disk finish
+	 *  the current command.
+	 */
+	loop = 0;
+	while(waserror()){
+		DPRINT("interrupted ataxfer\n");
+		if(loop++ > 10){
+			print("ata disk error\n");
+			nexterror();
+		}
+	}
+	atasleep(cp, 3000);
+	dp->state = Sspinning;
+	dp->usetime = m->ticks;
+	poperror();
+	if(loop)
+		nexterror();
+
+	if(cp->status & Serr){
+		DPRINT("hd%d err: lblk %ld status %lux, err %lux\n",
+			dp-ata, lblk, cp->status, cp->error);
+		DPRINT("\tcyl %d, sec %d, head %d\n", cyl, sec, head);
+		DPRINT("\tnsecs %d, sofar %d\n", cp->nsecs, cp->sofar);
+		atarepl(dp, lblk+cp->sofar);
+		error(Eio);
+	}
+	cp->buf = 0;
+	len = cp->sofar*dp->bytes;
+	qunlock(cp);
+	poperror();
+
+	return len;
+}
+
+/*
+ *  set read ahead mode
+ */
+static void
+atasetbuf(Drive *dp, int on)
+{
+	Controller *cp = dp->cp;
+
+	qlock(cp);
+	if(waserror()){
+		qunlock(cp);
+		nexterror();
+	}
+
+	cmdreadywait(dp);
+
+	ilock(&cp->reglock);
+	cp->cmd = Csetbuf;
+	outb(cp->pbase+Pprecomp, on ? 0xAA : 0x55);	/* read look ahead */
+	outb(cp->pbase+Pdh, DHmagic | (dp->drive<<4));
+	outb(cp->pbase+Pcmd, Csetbuf);
+	iunlock(&cp->reglock);
+
+	atasleep(cp, 5000);
+
+/*	if(cp->status & Serr)
+		DPRINT("hd%d setbuf err: status %lux, err %lux\n",
+			dp-ata, cp->status, cp->error);/**/
+
+	poperror();
+	qunlock(cp);
+}
+
+/*
+ *  ident sector from drive.  this is from ANSI X3.221-1994
+ */
+struct Ident
+{
+	ushort	config;		/* general configuration info */
+	ushort	cyls;		/* # of cylinders (default) */
+	ushort	reserved0;
+	ushort	heads;		/* # of heads (default) */
+	ushort	b2t;		/* unformatted bytes/track */
+	ushort	b2s;		/* unformated bytes/sector */
+	ushort	s2t;		/* sectors/track (default) */
+	ushort	reserved1[3];
+/* 10 */
+	ushort	serial[10];	/* serial number */
+	ushort	type;		/* buffer type */
+	ushort	bsize;		/* buffer size/512 */
+	ushort	ecc;		/* ecc bytes returned by read long */
+	ushort	firm[4];	/* firmware revision */
+	ushort	model[20];	/* model number */
+/* 47 */
+	ushort	s2i;		/* number of sectors/interrupt */
+	ushort	dwtf;		/* double word transfer flag */
+	ushort	capabilities;
+	ushort	reserved2;
+	ushort	piomode;
+	ushort	dmamode;
+	ushort	cvalid;		/* (cvald&1) if next 4 words are valid */
+	ushort	ccyls;		/* current # cylinders */
+	ushort	cheads;		/* current # heads */
+	ushort	cs2t;		/* current sectors/track */
+	ushort	ccap[2];	/* current capacity in sectors */
+	ushort	cs2i;		/* current number of sectors/interrupt */
+/* 60 */
+	ushort	lbasecs[2];	/* # LBA user addressable sectors */
+	ushort	dmasingle;
+	ushort	dmadouble;
+/* 64 */
+	ushort	reserved3[64];
+	ushort	vendor[32];	/* vendor specific */
+	ushort	reserved4[96];
+};
+
+/*
+ *  get parameters from the drive
+ */
+static void
+ataident(Drive *dp)
+{
+	Controller *cp;
+	char *buf;
+	Ident *ip;
+	char id[21];
+
+	cp = dp->cp;
+	buf = smalloc(Maxxfer);
+	qlock(cp);
+	if(waserror()){
+		cp->buf = 0;
+		qunlock(cp);
+		free(buf);
+		nexterror();
+	}
+
+	cmdreadywait(dp);
+
+	ilock(&cp->reglock);
+	cp->nsecs = 1;
+	cp->sofar = 0;
+	cp->cmd = Cident;
+	cp->dp = dp;
+	cp->buf = buf;
+	outb(cp->pbase+Pdh, DHmagic | (dp->drive<<4));
+	outb(cp->pbase+Pcmd, Cident);
+	iunlock(&cp->reglock);
+
+	atasleep(cp, 5000);
+	if(cp->status & Serr){
+		DPRINT("bad disk ident status\n");
+		error(Eio);
+	}
+	ip = (Ident*)buf;
+
+	/*
+	 * this function appears to respond with an extra interrupt after
+	 * the ident information is read, except on the safari.  The following
+	 * delay gives this extra interrupt a chance to happen while we are quiet.
+	 * Otherwise, the interrupt may come during a subsequent read or write,
+	 * causing a panic and much confusion.
+	 */
+	if (cp->cmd == Cident2)
+		tsleep(&cp->r, return0, 0, 10);
+
+	memmove(id, ip->model, sizeof(id)-1);
+	id[sizeof(id)-1] = 0;
+
+	if(ip->capabilities & (1<<9)){
+		dp->lba = 1;
+		dp->sectors = (ip->lbasecs[0]) | (ip->lbasecs[1]<<16);
+		dp->cap = dp->bytes * dp->sectors;
+/*print("\nata%d model %s with %d lba sectors\n", dp->drive, id, dp->sectors);/**/
+	} else {
+		dp->lba = 0;
+
+		/* use default (unformatted) settings */
+		dp->cyl = ip->cyls;
+		dp->heads = ip->heads;
+		dp->sectors = ip->s2t;
+/*print("\nata%d model %s with default %d cyl %d head %d sec\n", dp->drive,
+			id, dp->cyl, dp->heads, dp->sectors);/**/
+
+		if(ip->cvalid&(1<<0)){
+			/* use current settings */
+			dp->cyl = ip->ccyls;
+			dp->heads = ip->cheads;
+			dp->sectors = ip->cs2t;
+/*print("\tchanged to %d cyl %d head %d sec\n", dp->cyl, dp->heads, dp->sectors);/**/
+		}
+		dp->cap = dp->bytes * dp->cyl * dp->heads * dp->sectors;
+	}
+	cp->lastcmd = cp->cmd;
+	cp->cmd = 0;
+	cp->buf = 0;
+	free(buf);
+	poperror();
+	qunlock(cp);
+}
+
+/*
+ *  probe the given sector to see if it exists
+ */
+static int
+ataprobe(Drive *dp, int cyl, int sec, int head)
+{
+	Controller *cp;
+	char *buf;
+	int rv;
+
+	cp = dp->cp;
+	buf = smalloc(Maxxfer);
+	qlock(cp);
+	if(waserror()){
+		free(buf);
+		qunlock(cp);
+		nexterror();
+	}
+
+	cmdreadywait(dp);
+
+	ilock(&cp->reglock);
+	cp->cmd = Cread;
+	cp->dp = dp;
+	cp->status = 0;
+	cp->nsecs = 1;
+	cp->sofar = 0;
+
+	outb(cp->pbase+Pcount, 1);
+	outb(cp->pbase+Psector, sec+1);
+	outb(cp->pbase+Pdh, DHmagic | head | (dp->lba<<6) | (dp->drive<<4));
+	outb(cp->pbase+Pcyllsb, cyl);
+	outb(cp->pbase+Pcylmsb, cyl>>8);
+	outb(cp->pbase+Pcmd, Cread);
+	iunlock(&cp->reglock);
+
+	atasleep(cp, 5000);
+
+	if(cp->status & Serr)
+		rv = -1;
+	else
+		rv = 0;
+
+	cp->buf = 0;
+	free(buf);
+	poperror();
+	qunlock(cp);
+	return rv;
+}
+
+/*
+ *  figure out the drive parameters
+ */
+static void
+ataparams(Drive *dp)
+{
+	int i, hi, lo;
+
+	/*
+	 *  first try the easy way, ask the drive and make sure it
+	 *  isn't lying.
+	 */
+	dp->bytes = 512;
+	ataident(dp);
+	if(dp->lba){
+		i = dp->sectors - 1;
+		if(ataprobe(dp, (i>>8)&0xffff, (i&0xff)-1, (i>>24)&0xf) == 0)
+			return;
+	} else {
+		if(ataprobe(dp, dp->cyl-1, dp->sectors-1, dp->heads-1) == 0)
+			return;
+	}
+
+	/*
+	 *  the drive lied, determine parameters by seeing which ones
+	 *  work to read sectors.
+	 */
+	dp->lba = 0;
+	for(i = 0; i < 32; i++)
+		if(ataprobe(dp, 0, 0, i) < 0)
+			break;
+	dp->heads = i;
+	for(i = 0; i < 128; i++)
+		if(ataprobe(dp, 0, i, 0) < 0)
+			break;
+	dp->sectors = i;
+	for(i = 512; ; i += 512)
+		if(ataprobe(dp, i, dp->sectors-1, dp->heads-1) < 0)
+			break;
+	lo = i - 512;
+	hi = i;
+	for(; hi-lo > 1;){
+		i = lo + (hi - lo)/2;
+		if(ataprobe(dp, i, dp->sectors-1, dp->heads-1) < 0)
+			hi = i;
+		else
+			lo = i;
+	}
+	dp->cyl = lo + 1;
+	dp->cap = dp->bytes * dp->cyl * dp->heads * dp->sectors;
+}
+
+/*
+ *  Read block replacement table.
+ *  The table is just ascii block numbers.
+ */
+static void
+atareplinit(Drive *dp)
+{
+	char *line[Nrepl+1];
+	char *field[1];
+	ulong n;
+	int i;
+	char *buf;
+
+	/*
+	 *  check the partition is big enough
+	 */
+	if(dp->repl.p->end - dp->repl.p->start < Nrepl+1){
+		dp->repl.p = 0;
+		return;
+	}
+
+	buf = smalloc(Maxxfer);
+	if(waserror()){
+		free(buf);
+		nexterror();
+	}
+
+	/*
+	 *  read replacement table from disk, null terminate
+	 */
+	ataxfer(dp, dp->repl.p, Cread, 0, dp->bytes, buf);
+	buf[dp->bytes-1] = 0;
+
+	/*
+	 *  parse replacement table.
+	 */
+	n = getfields(buf, line, Nrepl+1, 1, "\n");
+	if(strncmp(line[0], REPLMAGIC, sizeof(REPLMAGIC)-1)){
+		dp->repl.p = 0;
+	} else {
+		for(dp->repl.nrepl = 0, i = 1; i < n; i++, dp->repl.nrepl++){
+			if(getfields(line[i], field, 1, 1, " ") != 1)
+				break;
+			dp->repl.blk[dp->repl.nrepl] = strtoul(field[0], 0, 0);
+			if(dp->repl.blk[dp->repl.nrepl] <= 0)
+				break;
+		}
+	}
+	free(buf);
+	poperror();
+}
+
+/*
+ *  read partition table.  The partition table is just ascii strings.
+ */
+static void
+atapart(Drive *dp)
+{
+	Partition *pp;
+	char *line[Npart+1];
+	char *field[3];
+	ulong n;
+	int i;
+	char *buf;
+
+	sprint(dp->vol, "hd%d", dp - ata);
+
+	/*
+	 *  we always have a partition for the whole disk
+	 *  and one for the partition table
+	 */
+	pp = &dp->p[0];
+	strcpy(pp->name, "disk");
+	pp->start = 0;
+	pp->end = dp->cap / dp->bytes;
+	pp++;
+	strcpy(pp->name, "partition");
+	pp->start = dp->p[0].end - 1;
+	pp->end = dp->p[0].end;
+	pp++;
+	dp->npart = 2;
+
+	/*
+	 * initialise the bad-block replacement info
+	 */
+	dp->repl.p = 0;
+
+	buf = smalloc(Maxxfer);
+	if(waserror()){
+		free(buf);
+		nexterror();
+	}
+
+	/*
+	 *  read last sector from disk, null terminate.  This used
+	 *  to be the sector we used for the partition tables.
+	 *  However, this sector is special on some PC's so we've
+	 *  started to use the second last sector as the partition
+	 *  table instead.  To avoid reconfiguring all our old systems
+	 *  we first look to see if there is a valid partition
+	 *  table in the last sector.  If so, we use it.  Otherwise
+	 *  we switch to the second last.
+	 */
+	ataxfer(dp, dp->p+1, Cread, 0, dp->bytes, buf);
+	buf[dp->bytes-1] = 0;
+	n = getfields(buf, line, Npart+1, 1, "\n");
+	if(n == 0 || strncmp(line[0], PARTMAGIC, sizeof(PARTMAGIC)-1)){
+		dp->p[0].end--;
+		dp->p[1].start--;
+		dp->p[1].end--;
+		ataxfer(dp, dp->p+1, Cread, 0, dp->bytes, buf);
+		buf[dp->bytes-1] = 0;
+		n = getfields(buf, line, Npart+1, 1, "\n");
+	}
+
+	/*
+	 *  parse partition table.
+	 */
+	if(n > 0 && strncmp(line[0], PARTMAGIC, sizeof(PARTMAGIC)-1) == 0){
+		for(i = 1; i < n; i++){
+			switch(getfields(line[i], field, 3, 1, " ")) {
+			case 2:
+				if(strcmp(field[0], "unit") == 0)
+					strncpy(dp->vol, field[1], NAMELEN);
+				break;	
+			case 3:
+				strncpy(pp->name, field[0], NAMELEN);
+				if(strncmp(pp->name, "repl", NAMELEN) == 0)
+					dp->repl.p = pp;
+				pp->start = strtoul(field[1], 0, 0);
+				pp->end = strtoul(field[2], 0, 0);
+				if(pp->start > pp->end || pp->end > dp->p[0].end)
+					break;
+				dp->npart++;
+				pp++;
+			}
+		}
+	}
+	free(buf);
+	poperror();
+
+	if(dp->repl.p)
+		atareplinit(dp);
+}
+
+enum
+{
+	Maxloop=	10000,
+};
+
+/*
+ *  we get an interrupt for every sector transferred
+ */
+static void
+ataintr(Ureg*, void *arg)
+{
+	Controller *cp;
+	Drive *dp;
+	long loop;
+	char *addr;
+
+	cp = arg;
+	dp = cp->dp;
+
+	ilock(&cp->reglock);
+
+	loop = 0;
+	while((cp->status = inb(cp->pbase+Pstatus)) & Sbusy){
+		if(++loop > Maxloop) {
+			DPRINT("cmd=%lux status=%lux\n",
+				cp->cmd, inb(cp->pbase+Pstatus));
+			panic("ataintr: wait busy");
+		}
+	}
+
+	switch(cp->cmd){
+	case Cwrite:
+		if(cp->status & Serr){
+			cp->lastcmd = cp->cmd;
+			cp->cmd = 0;
+			cp->error = inb(cp->pbase+Perror);
+			wakeup(&cp->r);
+			break;
+		}
+		cp->sofar++;
+		if(cp->sofar < cp->nsecs){
+			loop = 0;
+			while(((cp->status = inb(cp->pbase+Pstatus)) & Sdrq) == 0)
+				if(++loop > Maxloop) {
+					DPRINT("cmd=%lux status=%lux\n",
+						cp->cmd, inb(cp->pbase+Pstatus));
+					panic("ataintr: write");
+				}
+			addr = cp->buf;
+			if(addr){
+				addr += cp->sofar*dp->bytes;
+				outss(cp->pbase+Pdata, addr, dp->bytes/2);
+			}
+		} else{
+			cp->lastcmd = cp->cmd;
+			cp->cmd = 0;
+			wakeup(&cp->r);
+		}
+		break;
+	case Cread:
+	case Cident:
+		loop = 0;
+		while((cp->status & (Serr|Sdrq)) == 0){
+			if(++loop > Maxloop) {
+				DPRINT("cmd=%lux status=%lux\n",
+					cp->cmd, inb(cp->pbase+Pstatus));
+				panic("ataintr: read/ident");
+			}
+			cp->status = inb(cp->pbase+Pstatus);
+		}
+		if(cp->status & Serr){
+			cp->lastcmd = cp->cmd;
+			cp->cmd = 0;
+			cp->error = inb(cp->pbase+Perror);
+			wakeup(&cp->r);
+			break;
+		}
+		addr = cp->buf;
+		if(addr){
+			addr += cp->sofar*dp->bytes;
+			inss(cp->pbase+Pdata, addr, dp->bytes/2);
+		}
+		cp->sofar++;
+		if(cp->sofar > cp->nsecs)
+			print("ataintr %d %d\n", cp->sofar, cp->nsecs);
+		if(cp->sofar >= cp->nsecs){
+			cp->lastcmd = cp->cmd;
+			if (cp->cmd == Cread)
+				cp->cmd = 0;
+			else
+				cp->cmd = Cident2;
+			wakeup(&cp->r);
+		}
+		break;
+	case Cinitparam:
+	case Csetbuf:
+	case Cidle:
+	case Cstandby:
+	case Cpowerdown:
+		cp->lastcmd = cp->cmd;
+		cp->cmd = 0;
+		wakeup(&cp->r);
+		break;
+	case Cident2:
+		cp->lastcmd = cp->cmd;
+		cp->cmd = 0;
+		break;
+	default:
+		print("weird disk interrupt, cmd=%.2ux, lastcmd= %.2ux status=%.2ux\n",
+			cp->cmd, cp->lastcmd, cp->status);
+		break;
+	}
+
+	iunlock(&cp->reglock);
+}
+
+void
+hardclock(void)
+{
+	int drive;
+	Drive *dp;
+	Controller *cp;
+	int diff;
+
+	if(spindowntime <= 0)
+		return;
+
+	for(drive = 0; drive < nhard; drive++){
+		dp = &ata[drive];
+		cp = dp->cp;
+
+		diff = TK2SEC(m->ticks - dp->usetime);
+		if((dp->state == Sspinning) && (diff >= spindowntime)){
+			ilock(&cp->reglock);
+			cp->cmd = Cstandby;
+			outb(cp->pbase+Pcount, 0);
+			outb(cp->pbase+Pdh, DHmagic | (dp->drive<<4) | 0);
+			outb(cp->pbase+Pcmd, cp->cmd);
+			iunlock(&cp->reglock);
+			dp->state = Sstandby;
+		}
+	}
+}
+
+Dev atadevtab = {
+	'H',
+	"ata",
+
+	devreset,
+	atainit,
+	ataattach,
+	devdetach,
+	devclone,
+	atawalk,
+	atastat,
+	ataopen,
+	devcreate,
+	ataclose,
+	ataread,
+	devbread,
+	atawrite,
+	devbwrite,
+	devremove,
+	devwstat,
+};
--- /dev/null
+++ b/os/mpc/devbench.c
@@ -1,0 +1,243 @@
+#include "u.h"
+#include "../port/lib.h"
+#include "mem.h"
+#include "dat.h"
+#include "fns.h"
+#include "io.h"
+#include "../port/error.h"
+
+typedef struct Psync Psync;
+
+enum {
+	Maxprocs=2,
+};
+
+struct Psync {
+	Rendez	r;
+	int	flag;
+};
+static Psync timesync[Maxprocs];
+static Ref nactive;
+static Ref nbusy;
+
+static int
+timev(void *a)
+{
+	return *(int*)a;
+}
+
+static void
+timesched0(void *ap)
+{
+	long tot, t, i, lim, low, max;
+	Psync *ps;
+
+	ps = ap;
+	sleep(&ps->r, timev, &ps->flag);
+	setpri(PriRealtime);
+	incref(&nbusy);
+	while(nbusy.ref < nactive.ref)
+		sched();
+	lim = 1000;
+	low = 64000000;
+	max = 0;
+	tot = 0;
+	for(i=0; i<lim; i++){
+if(i<8)print("%lud\n", up->pid);
+		do{
+			t = gettbl();
+			sched();
+			t = gettbl()-t;
+		}while(t < 0);
+		if(t < low)
+			low = t;
+		if(t > max)
+			max = t;
+		tot += t;
+	}
+	print("%lud %lud %lud %lud %lud\n", up->pid, lim, tot, low, max);
+	decref(&nactive);
+	pexit("", 0);
+}
+
+static void
+timesched(void)
+{
+	int i, np;
+
+	for(np=1; np<=Maxprocs; np++){
+		nactive.ref = np;
+		print("%d procs\n", np);
+		setpri(PriRealtime);
+		for(i=0; i<np; i++)
+			kproc("timesched", timesched0, &timesync[i], 0);
+		for(i=0; i<np; i++){
+			timesync[i].flag = 1;
+			wakeup(&timesync[i].r);
+		}
+		setpri(PriNormal);
+		while(nactive.ref>0)
+			sched();
+	}
+}
+
+typedef struct Ictr Ictr;
+struct Ictr {
+	ulong	base;
+	ulong	sleep;
+	ulong	spllo;
+	ulong	intr;
+	ulong	isave;
+	ulong	arrive;
+	ulong	wakeup;
+	ulong	awake;
+};
+static Ictr counters[100], *curct;
+static int intrwant;
+static Rendez vous;
+int	spltbl;	/* set by spllo */
+int	intrtbl;	/* set by intrvec() */
+int	isavetbl;	/* set by intrvec() */
+
+static void
+intrwake(Ureg*, void*)
+{
+	m->iomem->tgcr &= ~1;	/* reset the timer */
+	curct->spllo = spltbl;
+	curct->intr = intrtbl;
+	curct->isave = isavetbl;
+	curct->arrive = gettbl();
+	intrwant = 0;
+	wakeup(&vous);
+	curct->wakeup = gettbl();
+}
+
+/*
+ * sleep calls intrtest with splhi (under lock):
+ * provoke the interrupt now, so that it is guaranteed
+ * not to happen until sleep has queued the process,
+ * forcing wakeup to do something.
+ */
+static int
+intrtest(void*)
+{
+	m->iomem->tgcr |= 1;		/* enable timer: allow interrupt */
+	curct->sleep = gettbl();
+	return intrwant==0;
+}
+
+static void
+intrtime(void)
+{
+	IMM *io;
+	Ictr *ic;
+	long t;
+	int i;
+
+	sched();
+	curct = counters;
+	io = ioplock();
+	io->tgcr &= ~3;
+	iopunlock();
+	intrenable(VectorCPIC+0x19, intrwake, nil, BUSUNKNOWN, "bench");
+	for(i=0; i<nelem(counters); i++){
+		curct = &counters[i];
+		//puttbl(0);
+		intrwant = 1;
+		io = m->iomem;	/* don't lock, to save time */
+		io->tmr1 = (0<<8)|TimerORI|TimerSclk;
+		io->trr1 = 1;
+		curct->base = gettbl();
+		sleep(&vous, intrtest, nil);
+		curct->awake = gettbl();
+		sched();	/* just to slow it down between trials */
+	}
+	m->iomem->tmr1 = 0;
+	print("interrupt\n");
+	for(i=0; i<20; i++){
+		ic = &counters[i];
+		t = ic->awake - ic->base;
+		ic->awake -= ic->wakeup;
+		ic->wakeup -= ic->arrive;
+		ic->arrive -= ic->isave;
+		ic->isave -= ic->intr;
+		ic->intr -= ic->spllo;
+		ic->spllo -= ic->sleep;
+		ic->sleep -= ic->base;
+		print("%ld\t%ld\t%ld\t%ld\t%ld\t%ld\t%ld\t%ld\n", ic->sleep, ic->spllo, ic->intr, ic->isave, ic->arrive, ic->wakeup, ic->awake, t);
+	}
+}
+
+static Chan*
+benchattach(char *spec)
+{
+	timesched();
+	intrtime();
+	USED(spec);
+	error(Eperm);
+	return nil;
+}
+
+static Walkqid*
+benchwalk(Chan*, Chan*, char**, int)
+{
+	error(Enonexist);
+	return 0;
+}
+
+static Chan*
+benchopen(Chan*, int)
+{
+	error(Eperm);
+	return nil;
+}
+
+static int
+benchstat(Chan*, uchar*, int)
+{
+	error(Eperm);
+	return 0;
+}
+
+static void
+benchclose(Chan*)
+{
+}
+
+static long	 
+benchread(Chan *c, void *buf, long n, vlong offset)
+{
+	USED(c, buf, n, offset);
+	error(Eperm);
+	return 0;
+}
+
+static long	 
+benchwrite(Chan *c, void *buf, long n, vlong offset)
+{
+	USED(c, buf, n, offset);
+	error(Eperm);
+	return 0;
+}
+
+
+Dev	benchdevtab = {
+	'x',
+	"bench",
+
+	devreset,
+	devinit,
+	devshutdown,
+	benchattach,
+	benchwalk,
+	benchstat,
+	benchopen,
+	devcreate,
+	benchclose,
+	benchread,
+	devbread,
+	benchwrite,
+	devbwrite,
+	devremove,
+	devwstat,
+};
--- /dev/null
+++ b/os/mpc/devboot.c
@@ -1,0 +1,132 @@
+#include	"u.h"
+#include	"../port/lib.h"
+#include	"mem.h"
+#include	"dat.h"
+#include	"fns.h"
+#include	"../port/error.h"
+
+enum{
+	Qdir,
+	Qboot,
+	Qmem,
+};
+
+Dirtab bootdir[]={
+	".",			{Qdir,0,QTDIR},	0,	0555,
+	"boot",		{Qboot},	0,	0666,
+	"mem",		{Qmem},		0,	0666,
+};
+
+#define	NBOOT	(sizeof bootdir/sizeof(Dirtab))
+
+static void
+bootreset(void)
+{
+}
+
+static Chan*
+bootattach(char *spec)
+{
+	return devattach('B', spec);
+}
+
+static Walkqid*
+bootwalk(Chan *c, Chan *nc, char **name, int nname)
+{
+	return devwalk(c, nc, name, nname, bootdir, NBOOT, devgen);
+}
+
+static int
+bootstat(Chan *c, uchar *dp, int n)
+{
+	return devstat(c, dp, n, bootdir, NBOOT, devgen);
+}
+
+static Chan*
+bootopen(Chan *c, int omode)
+{
+	return devopen(c, omode, bootdir, NBOOT, devgen);
+}
+
+static void	 
+bootclose(Chan*)
+{
+}
+
+static long	 
+bootread(Chan *c, void *buf, long n, vlong off)
+{
+	ulong offset = off;
+
+	switch((ulong)c->qid.path){
+
+	case Qdir:
+		return devdirread(c, buf, n, bootdir, NBOOT, devgen);
+
+	case Qmem:
+		/* kernel memory */
+		if(offset>=KZERO && offset<KZERO+conf.npage*BY2PG){
+			if(offset+n > KZERO+conf.npage*BY2PG)
+				n = KZERO+conf.npage*BY2PG - offset;
+			memmove(buf, (char*)offset, n);
+			return n;
+		}
+		error(Ebadarg);
+	}
+
+	error(Egreg);
+	return 0;	/* not reached */
+}
+
+static long	 
+bootwrite(Chan *c, void *buf, long n, vlong off)
+{
+	ulong offset = off;
+	ulong pc;
+	uchar *p;
+
+	switch((ulong)c->qid.path){
+	case Qmem:
+		/* kernel memory */
+		if(offset>=KZERO && offset<KZERO+conf.npage*BY2PG){
+			if(offset+n > KZERO+conf.npage*BY2PG)
+				n = KZERO+conf.npage*BY2PG - offset;
+			memmove((char*)offset, buf, n);
+			segflush((void*)offset, n);
+			return n;
+		}
+		error(Ebadarg);
+
+	case Qboot:
+		p = (uchar*)buf;
+		pc = (((((p[0]<<8)|p[1])<<8)|p[2])<<8)|p[3];
+		if(pc < KZERO || pc >= KZERO+conf.npage*BY2PG)
+			error(Ebadarg);
+		splhi();
+		segflush((void*)pc, 64*1024);
+		gotopc(pc);
+	}
+	error(Ebadarg);
+	return 0;	/* not reached */
+}
+
+Dev bootdevtab = {
+	'B',
+	"boot",
+
+	bootreset,
+	devinit,
+	devshutdown,
+	bootattach,
+	bootwalk,
+	bootstat,
+	bootopen,
+	devcreate,
+	bootclose,
+	bootread,
+	devbread,
+	bootwrite,
+	devbwrite,
+	devremove,
+	devwstat,
+};
--- /dev/null
+++ b/os/mpc/devether.c
@@ -1,0 +1,617 @@
+#include "u.h"
+#include "../port/lib.h"
+#include "mem.h"
+#include "dat.h"
+#include "fns.h"
+#include "io.h"
+#include "ureg.h"
+#include "../port/error.h"
+#include "../port/netif.h"
+
+#include "etherif.h"
+
+static Ether *etherxx[MaxEther];
+
+Chan*
+etherattach(char* spec)
+{
+	ulong ctlrno;
+	char *p;
+	Chan *chan;
+	Ether *ether;
+
+	ctlrno = 0;
+	if(spec && *spec){
+		ctlrno = strtoul(spec, &p, 0);
+		if((ctlrno == 0 && p == spec) || *p || (ctlrno >= MaxEther))
+			error(Ebadarg);
+	}
+	if((ether = etherxx[ctlrno]) == 0)
+		error(Enodev);
+	rlock(ether);
+	if(waserror()){
+		runlock(ether);
+		nexterror();
+	}
+	chan = devattach('l', spec);
+	chan->dev = ctlrno;
+	if(ether->attach)
+		ether->attach(etherxx[ctlrno]);
+	poperror();
+	runlock(ether);
+	return chan;
+}
+
+static void
+ethershutdown(void)
+{
+	Ether *ether;
+	int i;
+
+	for(i=0; i<MaxEther; i++){
+		ether = etherxx[i];
+		if(ether != nil && ether->detach != nil)
+			ether->detach(ether);
+	}
+}
+
+static Walkqid*
+etherwalk(Chan* chan, Chan *nchan, char **name, int nname)
+{
+	Walkqid *wq;
+	Ether *ether;
+
+	ether = etherxx[chan->dev];
+	rlock(ether);
+	if(waserror()) {
+		runlock(ether);
+		nexterror();
+	}
+	wq = netifwalk(etherxx[chan->dev], chan, nchan, name, nname);
+	poperror();
+	runlock(ether);
+	return wq;
+}
+
+static int
+etherstat(Chan* chan, uchar* dp, int n)
+{
+	int s;
+	Ether *ether;
+
+	ether = etherxx[chan->dev];
+	rlock(ether);
+	if(waserror()) {
+		runlock(ether);
+		nexterror();
+	}
+	s = netifstat(ether, chan, dp, n);
+	poperror();
+	runlock(ether);
+	return s;
+}
+
+static Chan*
+etheropen(Chan* chan, int omode)
+{
+	Chan *c;
+	Ether *ether;
+
+	ether = etherxx[chan->dev];
+	rlock(ether);
+	if(waserror()) {
+		runlock(ether);
+		nexterror();
+	}
+	c = netifopen(ether, chan, omode);
+	poperror();
+	runlock(ether);
+	return c;
+}
+
+static void
+ethercreate(Chan*, char*, int, ulong)
+{
+}
+
+static void
+etherclose(Chan* chan)
+{
+	Ether *ether;
+
+	ether = etherxx[chan->dev];
+	rlock(ether);
+	if(waserror()) {
+		runlock(ether);
+		nexterror();
+	}
+	netifclose(ether, chan);
+	poperror();
+	runlock(ether);
+}
+
+static long
+etherread(Chan* chan, void* buf, long n, vlong off)
+{
+	Ether *ether;
+	ulong offset = off;
+	long r;
+
+	ether = etherxx[chan->dev];
+	rlock(ether);
+	if(waserror()) {
+		runlock(ether);
+		nexterror();
+	}
+	if((chan->qid.type & QTDIR) == 0 && ether->ifstat){
+		/*
+		 * With some controllers it is necessary to reach
+		 * into the chip to extract statistics.
+		 */
+		if(NETTYPE(chan->qid.path) == Nifstatqid){
+			r = ether->ifstat(ether, buf, n, offset);
+			goto out;
+		}
+		if(NETTYPE(chan->qid.path) == Nstatqid)
+			ether->ifstat(ether, buf, 0, offset);
+	}
+	r = netifread(ether, chan, buf, n, offset);
+out:
+	poperror();
+	runlock(ether);
+	return r;
+}
+
+static Block*
+etherbread(Chan* chan, long n, ulong offset)
+{
+	Block *b;
+	Ether *ether;
+
+	ether = etherxx[chan->dev];
+	rlock(ether);
+	if(waserror()) {
+		runlock(ether);
+		nexterror();
+	}
+	b = netifbread(ether, chan, n, offset);
+	poperror();
+	runlock(ether);
+	return b;
+}
+
+static int
+etherwstat(Chan* chan, uchar* dp, int n)
+{
+	Ether *ether;
+	int r;
+
+	ether = etherxx[chan->dev];
+	rlock(ether);
+	if(waserror()) {
+		runlock(ether);
+		nexterror();
+	}
+	r = netifwstat(ether, chan, dp, n);
+	poperror();
+	runlock(ether);
+	return r;
+}
+
+static void
+etherrtrace(Netfile* f, Etherpkt* pkt, int len)
+{
+	int i, n;
+	Block *bp;
+
+	if(qwindow(f->in) <= 0)
+		return;
+	if(len > 58)
+		n = 58;
+	else
+		n = len;
+	bp = iallocb(64);
+	if(bp == nil)
+		return;
+	memmove(bp->wp, pkt->d, n);
+	i = TK2MS(MACHP(0)->ticks);
+	bp->wp[58] = len>>8;
+	bp->wp[59] = len;
+	bp->wp[60] = i>>24;
+	bp->wp[61] = i>>16;
+	bp->wp[62] = i>>8;
+	bp->wp[63] = i;
+	bp->wp += 64;
+	qpass(f->in, bp);
+}
+
+Block*
+etheriq(Ether* ether, Block* bp, int fromwire)
+{
+	Etherpkt *pkt;
+	ushort type;
+	int len, multi, tome, fromme;
+	Netfile **ep, *f, **fp, *fx;
+	Block *xbp;
+
+	ether->inpackets++;
+
+	pkt = (Etherpkt*)bp->rp;
+	len = BLEN(bp);
+	type = (pkt->type[0]<<8)|pkt->type[1];
+	fx = 0;
+	ep = &ether->f[Ntypes];
+
+	multi = pkt->d[0] & 1;
+	/* check for valid multcast addresses */
+	if(multi && memcmp(pkt->d, ether->bcast, sizeof(pkt->d)) != 0 && ether->prom == 0){
+		if(!activemulti(ether, pkt->d, sizeof(pkt->d))){
+			if(fromwire){
+				freeb(bp);
+				bp = 0;
+			}
+			return bp;
+		}
+	}
+
+	/* is it for me? */
+	tome = memcmp(pkt->d, ether->ea, sizeof(pkt->d)) == 0;
+	fromme = memcmp(pkt->s, ether->ea, sizeof(pkt->s)) == 0;
+
+	/*
+	 * Multiplex the packet to all the connections which want it.
+	 * If the packet is not to be used subsequently (fromwire != 0),
+	 * attempt to simply pass it into one of the connections, thereby
+	 * saving a copy of the data (usual case hopefully).
+	 */
+	for(fp = ether->f; fp < ep; fp++){
+		if((f = *fp) && (f->type == type || f->type < 0))
+		if(tome || multi || f->prom){
+			/* Don't want to hear bridged packets */
+			if(f->bridge && !fromwire && !fromme)
+				continue;
+			if(!f->headersonly){
+				if(fromwire && fx == 0)
+					fx = f;
+				else if(xbp = iallocb(len)){
+					memmove(xbp->wp, pkt, len);
+					xbp->wp += len;
+					if(qpass(f->in, xbp) < 0)
+						ether->soverflows++;
+				}
+				else
+					ether->soverflows++;
+			}
+			else
+				etherrtrace(f, pkt, len);
+		}
+	}
+
+	if(fx){
+		if(qpass(fx->in, bp) < 0)
+			ether->soverflows++;
+		return 0;
+	}
+	if(fromwire){
+		freeb(bp);
+		return 0;
+	}
+
+	return bp;
+}
+
+static int
+etheroq(Ether* ether, Block* bp)
+{
+	int len, loopback, s;
+	Etherpkt *pkt;
+
+	ether->outpackets++;
+
+	/*
+	 * Check if the packet has to be placed back onto the input queue,
+	 * i.e. if it's a loopback or broadcast packet or the interface is
+	 * in promiscuous mode.
+	 * If it's a loopback packet indicate to etheriq that the data isn't
+	 * needed and return, etheriq will pass-on or free the block.
+	 * To enable bridging to work, only packets that were originated
+	 * by this interface are fed back.
+	 */
+	pkt = (Etherpkt*)bp->rp;
+	len = BLEN(bp);
+	loopback = memcmp(pkt->d, ether->ea, sizeof(pkt->d)) == 0;
+	if(loopback || memcmp(pkt->d, ether->bcast, sizeof(pkt->d)) == 0 || ether->prom){
+		s = splhi();
+		etheriq(ether, bp, 0);
+		splx(s);
+	}
+
+	if(!loopback){
+		qbwrite(ether->oq, bp);
+		if(ether->transmit != nil)
+			ether->transmit(ether);
+	}else
+		freeb(bp);
+
+	return len;
+}
+
+static long
+etherwrite(Chan* chan, void* buf, long n, vlong)
+{
+	Ether *ether;
+	Block *bp;
+	int onoff;
+	Cmdbuf *cb;
+	long l;
+
+	ether = etherxx[chan->dev];
+	rlock(ether);
+	if(waserror()) {
+		runlock(ether);
+		nexterror();
+	}
+	if(NETTYPE(chan->qid.path) != Ndataqid) {
+		l = netifwrite(ether, chan, buf, n);
+		if(l >= 0)
+			goto out;
+		cb = parsecmd(buf, n);
+		if(strcmp(cb->f[0], "nonblocking") == 0){
+			if(cb->nf <= 1)
+				onoff = 1;
+			else
+				onoff = atoi(cb->f[1]);
+			qnoblock(ether->oq, onoff);
+			free(cb);
+			goto out;
+		}
+		free(cb);
+		if(ether->ctl!=nil){
+			l = ether->ctl(ether,buf,n);
+			goto out;
+		}
+		error(Ebadctl);
+	}
+
+	if(n > ether->maxmtu)
+		error(Etoobig);
+	if(n < ether->minmtu)
+		error(Etoosmall);
+	bp = allocb(n);
+	if(waserror()){
+		freeb(bp);
+		nexterror();
+	}
+	memmove(bp->rp, buf, n);
+	memmove(bp->rp+Eaddrlen, ether->ea, Eaddrlen);
+	bp->wp += n;
+	poperror();
+
+	l = etheroq(ether, bp);
+out:
+	poperror();
+	runlock(ether);
+	return l;
+}
+
+static long
+etherbwrite(Chan* chan, Block* bp, ulong)
+{
+	Ether *ether;
+	long n;
+
+	n = BLEN(bp);
+	if(NETTYPE(chan->qid.path) != Ndataqid){
+		if(waserror()) {
+			freeb(bp);
+			nexterror();
+		}
+		n = etherwrite(chan, bp->rp, n, 0);
+		poperror();
+		freeb(bp);
+		return n;
+	}
+	ether = etherxx[chan->dev];
+	rlock(ether);
+	if(waserror()) {
+		runlock(ether);
+		nexterror();
+	}
+	if(n > ether->maxmtu){
+		freeb(bp);
+		error(Etoobig);
+	}
+	if(n < ether->minmtu){
+		freeb(bp);
+		error(Etoosmall);
+	}
+	n = etheroq(ether, bp);
+	poperror();
+	runlock(ether);
+	return n;
+}
+
+static struct {
+	char*	type;
+	int	(*reset)(Ether*);
+} cards[MaxEther+1];
+
+void
+addethercard(char* t, int (*r)(Ether*))
+{
+	static int ncard;
+
+	if(ncard == MaxEther)
+		panic("too many ether cards");
+	cards[ncard].type = t;
+	cards[ncard].reset = r;
+	ncard++;
+}
+
+int
+parseether(uchar *to, char *from)
+{
+	char nip[4];
+	char *p;
+	int i;
+
+	p = from;
+	for(i = 0; i < Eaddrlen; i++){
+		if(*p == 0)
+			return -1;
+		nip[0] = *p++;
+		if(*p == 0)
+			return -1;
+		nip[1] = *p++;
+		nip[2] = 0;
+		to[i] = strtoul(nip, 0, 16);
+		if(*p == ':')
+			p++;
+	}
+	return 0;
+}
+
+static void
+etherreset(void)
+{
+	Ether *ether;
+	int i, n, ctlrno;
+	char name[KNAMELEN], buf[128];
+
+	for(ether = 0, ctlrno = 0; ctlrno < MaxEther; ctlrno++){
+		if(ether == 0)
+			ether = malloc(sizeof(Ether));
+		memset(ether, 0, sizeof(Ether));
+		ether->ctlrno = ctlrno;
+		ether->mbps = 10;
+		ether->minmtu = ETHERMINTU;
+		ether->maxmtu = ETHERMAXTU;
+		ether->tbdf = BUSUNKNOWN;
+
+		if(archether(ctlrno, ether) <= 0)
+			continue;
+
+		for(n = 0; cards[n].type; n++){
+			if(cistrcmp(cards[n].type, ether->type))
+				continue;
+			for(i = 0; i < ether->nopt; i++){
+				if(cistrncmp(ether->opt[i], "ea=", 3) == 0){
+					if(parseether(ether->ea, &ether->opt[i][3]) == -1)
+						memset(ether->ea, 0, Eaddrlen);
+				}else if(cistrcmp(ether->opt[i], "fullduplex") == 0 ||
+					cistrcmp(ether->opt[i], "10BASE-TFD") == 0)
+					ether->fullduplex = 1;
+				else if(cistrcmp(ether->opt[i], "100BASE-TXFD") == 0)
+					ether->mbps = 100;
+			}
+			if(cards[n].reset(ether))
+				break;
+			snprint(name, sizeof(name), "ether%d", ctlrno);
+
+			if(ether->interrupt != nil)
+				intrenable(ether->irq, ether->interrupt, ether, ether->tbdf, name);
+
+			i = sprint(buf, "#l%d: %s: %dMbps port 0x%luX irq %lud",
+				ctlrno, ether->type, ether->mbps, ether->port, ether->irq);
+			if(ether->mem)
+				i += sprint(buf+i, " addr 0x%luX", PADDR(ether->mem));
+			if(ether->size)
+				i += sprint(buf+i, " size 0x%luX", ether->size);
+			i += sprint(buf+i, ": %2.2uX%2.2uX%2.2uX%2.2uX%2.2uX%2.2uX",
+				ether->ea[0], ether->ea[1], ether->ea[2],
+				ether->ea[3], ether->ea[4], ether->ea[5]);
+			sprint(buf+i, "\n");
+			print(buf);
+
+			if(ether->mbps == 100){
+				netifinit(ether, name, Ntypes, 256*1024);
+				if(ether->oq == 0)
+					ether->oq = qopen(256*1024, Qmsg, 0, 0);
+			}
+			else{
+				netifinit(ether, name, Ntypes, 64*1024);
+				if(ether->oq == 0)
+					ether->oq = qopen(64*1024, Qmsg, 0, 0);
+			}
+			if(ether->oq == 0)
+				panic("etherreset %s", name);
+			ether->alen = Eaddrlen;
+			memmove(ether->addr, ether->ea, Eaddrlen);
+			memset(ether->bcast, 0xFF, Eaddrlen);
+
+			etherxx[ctlrno] = ether;
+			ether = 0;
+			break;
+		}
+	}
+	if(ether)
+		free(ether);
+}
+
+static void
+etherpower(int on)
+{
+	int i;
+	Ether *ether;
+
+	for(i = 0; i < MaxEther; i++){
+		if((ether = etherxx[i]) == nil || ether->power == nil)
+			continue;
+		if(on){
+			if(canrlock(ether))
+				continue;
+			if(ether->power != nil)
+				ether->power(ether, on);
+			wunlock(ether);
+		}else{
+			if(!canrlock(ether))
+				continue;
+			wlock(ether);
+			if(ether->power != nil)
+				ether->power(ether, on);
+			/* Keep locked until power goes back on */
+		}
+	}
+}
+
+#define POLY 0xedb88320
+
+/* really slow 32 bit crc for ethers */
+ulong
+ethercrc(uchar *p, int len)
+{
+	int i, j;
+	ulong crc, b;
+
+	crc = 0xffffffff;
+	for(i = 0; i < len; i++){
+		b = *p++;
+		for(j = 0; j < 8; j++){
+			crc = (crc>>1) ^ (((crc^b) & 1) ? POLY : 0);
+			b >>= 1;
+		}
+	}
+	return crc;
+}
+
+Dev etherdevtab = {
+	'l',
+	"ether",
+
+	etherreset,
+	devinit,
+	ethershutdown,
+	etherattach,
+	etherwalk,
+	etherstat,
+	etheropen,
+	ethercreate,
+	etherclose,
+	etherread,
+	etherbread,
+	etherwrite,
+	etherbwrite,
+	devremove,
+	etherwstat,
+	etherpower,
+};
--- /dev/null
+++ b/os/mpc/devpcmcia.c
@@ -1,0 +1,1076 @@
+#include "u.h"
+#include "../port/lib.h"
+#include "mem.h"
+#include "dat.h"
+#include "fns.h"
+#include "io.h"
+#include "../port/error.h"
+
+/*
+ *  MPC821/3 PCMCIA driver (prototype)
+ *
+ * unlike the i82365 adapter, there isn't an offset register:
+ * card addresses are simply the lower order 26 bits of the host address.
+ *
+ * to do:
+ *	split allocation of memory/attrib (all 26 bits valid) and io space (typically 10 or 12 bits)
+ *	correct config
+ *	interrupts and i/o space access
+ *	DMA?
+ *	power control
+ */
+
+enum
+{
+	Maxctlr=	1,
+	Maxslot=	2,
+	Slotashift=	16,
+
+	/* pipr */
+	Cbvs1=	1<<15,
+	Cbvs2=	1<<14,
+	Cbwp=	1<<13,
+	Cbcd2=	1<<12,
+	Cbcd1=	1<<11,
+	Cbbvd2=	1<<10,
+	Cbbvd1=	1<<9,
+	Cbrdy=	1<<8,
+
+	/* pscr and per */
+	Cbvs1_c=	1<<15,
+	Cbvs2_c=	1<<14,
+	Cbwp_c=	1<<13,
+	Cbcd2_c=	1<<12,
+	Cbcd1_c=	1<<11,
+	Cbbvd2_c=	1<<10,
+	Cbbvd1_c=	1<<9,
+	Cbrdy_l=	1<<7,
+	Cbrdy_h=	1<<6,
+	Cbrdy_r=	1<<5,
+	Cbrdy_f=	1<<4,
+
+	/* pgcr[n] */
+	Cbdreq_int=	0<<14,
+	Cbdreq_iois16=	2<<14,
+	Cbdreq_spkr=	3<<14,
+	Cboe=	1<<7,
+	Cbreset=	1<<6,
+
+	/* porN */
+	Rport8=	0<<6,
+	Rport16=	1<<6,
+	Rmtype=	7<<3,	/* memory type field */
+	 Rmem=	0<<3,	/* common memory space */
+	 Rattrib=	2<<3,	/* attribute space */
+	 Rio=		3<<3,
+	 Rdma=	4<<3,	/* normal DMA */
+	 Rdmalx=	5<<3,	/* DMA, last transaction */
+	 RA22_23= 6<<3,	/* ``drive A22 and A23 signals on CE2 and CE1'' */
+	RslotB=	1<<2,	/* select slot B (always, on MPC823) */
+	Rwp=	1<<1,	/* write protect */
+	Rvalid=	1<<0,	/* region valid */
+
+	Nmap=		8,		/* max number of maps to use */
+
+	/*
+	 *  configuration registers - they start at an offset in attribute
+	 *  memory found in the CIS.
+	 */
+	Rconfig=	0,
+	 Creset=	 (1<<7),	/*  reset device */
+	 Clevel=	 (1<<6),	/*  level sensitive interrupt line */
+	Rccsr=	2,
+	 Ciack	= (1<<0),
+	 Cipend	= (1<<1),
+	 Cpwrdown=	(1<<2),
+	 Caudioen=	(1<<3),
+	 Ciois8=	(1<<5),
+	 Cchgena=	(1<<6),
+	 Cchange=	(1<<7),
+	Rpin=	4,	/* pin replacement register */
+	Rscpr=	6,	/* socket and copy register */
+	Riob0=	10,
+	Riob1=	12,
+	Riob2=	14,
+	Riob3=	16,
+	Riolim=	18,
+
+	Maxctab=	8,		/* maximum configuration table entries */
+	MaxCIS = 8192,		/* maximum CIS size in bytes */
+	Mgran = 8192,		/* maximum size of reads and writes */
+
+	Statusbounce=20,	/* msec debounce time */
+};
+
+typedef struct Ctlr Ctlr;
+
+/* a controller (there's only one) */
+struct Ctlr
+{
+	int	dev;
+	int	nslot;
+
+	/* memory maps */
+	Lock	mlock;		/* lock down the maps */
+	PCMmap	mmap[Nmap];	/* maps */
+
+	/* IO port allocation */
+	ulong	nextport;
+};
+static Ctlr controller[Maxctlr];
+
+static PCMslot	*slot;
+static PCMslot	*lastslot;
+static int	nslot;
+
+static	Map	pcmmapv[Nmap+1];
+static	RMap	pcmmaps = {"PCMCIA mappings"};
+
+static void	pcmciaintr(Ureg*, void*);
+static void	pcmciareset(void);
+static int	pcmio(int, ISAConf*);
+static long	pcmread(int, int, void*, long, ulong);
+static long	pcmwrite(int, int, void*, long, ulong);
+static void	slotdis(PCMslot*);
+
+static ulong pcmmalloc(ulong, long);
+static void	pcmfree(ulong, long);
+static void pcmciaproc(void*);
+
+static void pcmciadump(PCMslot*);
+
+/*
+ *  get info about card
+ */
+static void
+slotinfo(PCMslot *pp)
+{
+	ulong pipr;
+
+	pipr = (m->iomem->pipr >> pp->slotshift) & 0xFF00;
+print("pipr=%8.8lux/%lux\n", m->iomem->pipr, pipr);
+	pp->v3_3 = (pipr&Cbvs1)!=0;
+	pp->voltage = (((pipr & Cbvs2)!=0)<<1) | ((pipr & Cbvs1)!=0);
+	pp->occupied = (pipr&(Cbcd1|Cbcd2))==0;
+	pp->powered = pcmpowered(pp->slotno);
+	pp->battery = (pipr & (Cbbvd1|Cbbvd2))>>9;
+	pp->wrprot = (pipr&Cbwp)!=0;
+	pp->busy = (pipr&Cbrdy)==0;
+}
+
+static void
+pcmdelay(int ms)
+{
+	if(up == nil)
+		delay(ms);
+	else
+		tsleep(&up->sleep, return0, nil, ms);
+}
+
+/*
+ *  enable the slot card
+ */
+static void
+slotena(PCMslot *pp)
+{
+	IMM *io;
+
+	if(pp->enabled)
+		return;
+	m->iomem->pgcr[pp->slotno] &= ~Cboe;
+	pcmpower(pp->slotno, 1);
+	eieio();
+	pcmdelay(300);
+	io = m->iomem;
+	io->pgcr[pp->slotno] |= Cbreset;	/* active high */
+	eieio();
+	pcmdelay(100);
+	io->pgcr[pp->slotno] &= ~Cbreset;
+	eieio();
+	pcmdelay(500);	/* ludicrous delay */
+
+	/* get configuration */
+	slotinfo(pp);
+	if(pp->occupied){
+		if(pp->cisread == 0){
+			pcmcisread(pp);
+			pp->cisread = 1;
+		}
+		pp->enabled = 1;
+	} else{
+		print("empty slot\n");
+		slotdis(pp);
+	}
+}
+
+/*
+ *  disable the slot card
+ */
+static void
+slotdis(PCMslot *pp)
+{
+	int i;
+	Ctlr *ctlr;
+	PCMmap *pm;
+
+iprint("slotdis %d\n", pp->slotno);
+	pcmpower(pp->slotno, 0);
+	m->iomem->pgcr[pp->slotno] |= Cboe;
+	ctlr = pp->ctlr;
+	for(i = 0; i < nelem(ctlr->mmap); i++){
+		pm = &ctlr->mmap[i];
+		if(m->iomem->pcmr[i].option & Rvalid && pm->slotno == pp->slotno)
+			pcmunmap(pp->slotno, pm);
+	}
+	pp->enabled = 0;
+	pp->cisread = 0;
+}
+
+static void
+pcmciardy(Ureg *ur, void *a)
+{
+	PCMslot *pp;
+	ulong w;
+
+	pp = a;
+	w = (m->iomem->pipr >> pp->slotshift) & 0xFF00;
+	if(pp->occupied && (w & Cbrdy) == 0){ /* interrupt */
+print("PCM.%dI#%lux|", pp->slotno, w);
+		if(pp->intr.f != nil)
+			pp->intr.f(ur, pp->intr.arg);
+	}
+}
+
+void
+pcmintrenable(int slotno, void (*f)(Ureg*, void*), void *arg)
+{
+	PCMslot *pp;
+	IMM *io;
+	char name[KNAMELEN];
+
+	if(slotno < 0 || slotno >= nslot)
+		panic("pcmintrenable");
+	snprint(name, sizeof(name), "pcmcia.irq%d", slotno);
+	io = ioplock();
+	pp = slot+slotno;
+	pp->intr.f = f;
+	pp->intr.arg = arg;
+	intrenable(PCMCIAio, pcmciardy, pp, BUSUNKNOWN, name);
+	io->per |= Cbrdy_l;	/* assumes used for irq, not rdy; assumes level interrupt always right */
+	iopunlock();
+}
+
+void
+pcmintrdisable(int slotno, void (*f)(Ureg*, void*), void *arg)
+{
+	PCMslot *pp;
+	IMM *io;
+	char name[KNAMELEN];
+
+	if(slotno < 0 || slotno >= nslot)
+		panic("pcmintrdisable");
+	snprint(name, sizeof(name), "pcmcia.irq%d", slotno);
+	io = ioplock();
+	pp = slot+slotno;
+	if(pp->intr.f == f && pp->intr.arg == arg){
+		pp->intr.f = nil;
+		pp->intr.arg = nil;
+		intrdisable(PCMCIAio, pcmciardy, pp, BUSUNKNOWN, name);
+		io->per &= ~Cbrdy_l;
+	}
+	iopunlock();
+}
+
+/*
+ *  status change interrupt
+ *
+ * this wakes a monitoring process to read the CIS,
+ * rather than holding other interrupts out here.
+ */
+
+static Rendez pcmstate;
+
+static int
+statechanged(void *a)
+{
+	PCMslot *pp;
+	int in;
+
+	pp = a;
+	in = (m->iomem->pipr & (Cbcd1|Cbcd2))==0;
+	return in != pp->occupied;
+}
+
+static void
+pcmciaintr(Ureg*, void*)
+{
+	ulong events;
+
+	if(slot == 0)
+		return;
+	events = m->iomem->pscr & (Cbvs1_c|Cbvs2_c|Cbwp_c|Cbcd2_c|Cbcd1_c|Cbbvd2_c|Cbbvd1_c);
+	eieio();
+	m->iomem->pscr = events;
+	/* TO DO: other slot */
+iprint("PCM: #%lux|", events);
+iprint("pipr=#%lux|", m->iomem->pipr & 0xFF00);
+	wakeup(&pcmstate);
+}
+
+static void
+pcmciaproc(void*)
+{
+	ulong csc;
+	PCMslot *pp;
+	int was;
+
+	for(;;){
+		sleep(&pcmstate, statechanged, slot+1);
+		tsleep(&up->sleep, return0, nil, Statusbounce);
+		/*
+		 * voltage change 1,2
+		 * write protect change
+		 * card detect 1,2
+		 * battery voltage 1 change (or SPKR-bar)
+		 * battery voltage 2 change (or STSCHG-bar)
+		 * card B rdy / IRQ-bar low
+		 * card B rdy / IRQ-bar high
+		 * card B rdy / IRQ-bar rising edge
+		 * card B rdy / IRQ-bar falling edge
+		 *
+		 * TO DO: currently only handle card-present changes
+		 */
+
+		for(pp = slot; pp < lastslot; pp++){
+			if(pp->memlen == 0)
+				continue;
+			csc = (m->iomem->pipr>>pp->slotshift) & (Cbcd1|Cbcd2);
+			was = pp->occupied;
+			slotinfo(pp);
+			if(csc == 0 && was != pp->occupied){
+				if(!pp->occupied){
+					slotdis(pp);
+					if(pp->special && pp->notify.f != nil)
+						pp->notify.f(pp->notify.arg, 1);
+				}
+			}
+		}
+	}
+}
+
+static uchar greycode[] = {
+	0, 1, 3, 2, 6, 7, 5, 4, 014, 015, 017, 016, 012, 013, 011, 010,
+	030, 031, 033, 032, 036, 037, 035, 034, 024, 025, 027
+};
+
+/*
+ *  get a map for pc card region, return corrected len
+ */
+PCMmap*
+pcmmap(int slotno, ulong offset, int len, int attr)
+{
+	Ctlr *ctlr;
+	PCMslot *pp;
+	PCMmap *pm, *nm;
+	IMM *io;
+	int i;
+	ulong e, bsize, code, opt;
+
+	if(0)
+		print("pcmmap: %d #%lux %d #%x\n", slotno, offset, len, attr);
+	pp = slot + slotno;
+	if(!pp->occupied)
+		return nil;
+	if(attr == 1){	/* account for ../port/cis.c's conventions */
+		attr = Rattrib;
+		if(len <= 0)
+			len = MaxCIS*2;	/* TO DO */
+	}
+	ctlr = pp->ctlr;
+
+	/* convert offset to granularity */
+	if(len <= 0)
+		len = 1;
+	e = offset+len;
+	for(i=0;; i++){
+		if(i >= nelem(greycode))
+			return nil;
+		bsize = 1<<i;
+		offset &= ~(bsize-1);
+		if(e <= offset+bsize)
+			break;
+	}
+	code = greycode[i];
+	if(0)
+		print("i=%d bsize=%lud code=0%luo\n", i, bsize, code);
+	e = offset+bsize;
+	len = bsize;
+
+	lock(&ctlr->mlock);
+
+	/* look for an existing map that covers the right area */
+	io = m->iomem;
+	nm = nil;
+	for(i=0; i<Nmap; i++){
+		pm = &ctlr->mmap[i];
+		if(io->pcmr[i].option & Rvalid &&
+		   pm->slotno == slotno &&
+		   pm->attr == attr &&
+		   offset >= pm->ca && e <= pm->cea){
+			pm->ref++;
+			unlock(&ctlr->mlock);
+			return pm;
+		}
+		if(nm == 0 && pm->ref == 0)
+			nm = pm;
+	}
+	pm = nm;
+	if(pm == nil){
+		unlock(&ctlr->mlock);
+		return nil;
+	}
+
+	/* set up new map */
+	pm->isa = pcmmalloc(offset, len);
+	if(pm->isa == 0){
+		/* address not available: in use, or too much to map */
+		unlock(&ctlr->mlock);
+		return 0;
+	}
+	if(0)
+		print("mx=%d isa=#%lux\n", (int)(pm - ctlr->mmap), pm->isa);
+
+	pm->len = len;
+	pm->ca = offset;
+	pm->cea = pm->ca + pm->len;
+	pm->attr = attr;
+	i = pm - ctlr->mmap;
+	io->pcmr[i].option &= ~Rvalid;	/* disable map before changing it */
+	io->pcmr[i].base = pm->isa;
+	opt = attr;
+	opt |= code<<27;
+	if((attr&Rmtype) == Rio){
+		opt |= 4<<12;	/* PSST */
+		opt |= 8<<7;	/* PSL */
+		opt |= 2<<16;	/* PSHT */
+	}else{
+		opt |= 6<<12;	/* PSST */
+		opt |= 24<<7;	/* PSL */
+		opt |= 8<<16;	/* PSHT */
+	}
+	if((attr & Rport16) == 0)
+		opt |= Rport8;
+	if(slotno == 1)
+		opt |= RslotB;
+	io->pcmr[i].option = opt | Rvalid;
+	pm->slotno = slotno;
+	pm->ref = 1;
+
+	unlock(&ctlr->mlock);
+	return pm;
+}
+
+static void
+pcmiomap(PCMslot *pp, PCMconftab *ct, int i)
+{
+	int n, attr;
+	Ctlr *ctlr;
+
+	if(0)
+		print("pcm iomap #%lux %lud\n", ct->io[i].start, ct->io[i].len);
+	if(ct->io[i].len <= 0)
+		return;
+	if(ct->io[i].start == 0){
+		n = 1<<ct->nlines;
+		ctlr = pp->ctlr;
+		lock(&ctlr->mlock);
+		if(ctlr->nextport == 0)
+			ctlr->nextport = 0xF000;
+		ctlr->nextport = (ctlr->nextport + n - 1) & ~(n-1);
+		ct->io[i].start = ctlr->nextport;
+		ct->io[i].len = n;
+		ctlr->nextport += n;
+		unlock(&ctlr->mlock);
+	}
+	attr = Rio;
+	if(ct->bit16)
+		attr |= Rport16;
+	ct->io[i].map = pcmmap(pp->slotno, ct->io[i].start, ct->io[i].len, attr);
+}
+
+void
+pcmunmap(int slotno, PCMmap* pm)
+{
+	int i;
+	PCMslot *pp;
+	Ctlr *ctlr;
+
+	pp = slot + slotno;
+	if(pp->memlen == 0)
+		return;
+	ctlr = pp->ctlr;
+	lock(&ctlr->mlock);
+	if(pp->slotno == pm->slotno && --pm->ref == 0){
+		i = pm - ctlr->mmap;
+		m->iomem->pcmr[i].option = 0;
+		m->iomem->pcmr[i].base = 0;
+		pcmfree(pm->isa, pm->len);
+	}
+	unlock(&ctlr->mlock);
+}
+
+static void
+increfp(PCMslot *pp)
+{
+	if(incref(pp) == 1)
+		slotena(pp);
+}
+
+static void
+decrefp(PCMslot *pp)
+{
+	if(decref(pp) == 0)
+		slotdis(pp);
+}
+
+/*
+ *  look for a card whose version contains 'idstr'
+ */
+int
+pcmspecial(char *idstr, ISAConf *isa)
+{
+	PCMslot *pp;
+	extern char *strstr(char*, char*);
+
+	pcmciareset();
+	for(pp = slot; pp < lastslot; pp++){
+		if(pp->special || pp->memlen == 0)
+			continue;	/* already taken */
+		increfp(pp);
+		if(pp->occupied && strstr(pp->verstr, idstr)){
+			print("PCMslot #%d: Found %s - ",pp->slotno, idstr);
+			if(isa == 0 || pcmio(pp->slotno, isa) == 0){
+				print("ok.\n");
+				pp->special = 1;
+				return pp->slotno;
+			}
+			print("error with isa io\n");
+		}
+		decrefp(pp);
+	}
+	return -1;
+}
+
+void
+pcmspecialclose(int slotno)
+{
+	PCMslot *pp;
+
+	if(slotno >= nslot)
+		panic("pcmspecialclose");
+	pp = slot + slotno;
+	pp->special = 0;
+	decrefp(pp);
+}
+
+void
+pcmnotify(int slotno, void (*f)(void*, int), void* a)
+{
+	PCMslot *pp;
+
+	if(slotno < 0 || slotno >= nslot)
+		panic("pcmnotify");
+	pp = slot + slotno;
+	if(pp->occupied && pp->special){
+		pp->notify.f = f;
+		pp->notify.arg = a;
+	}
+}
+
+/*
+ * reserve pcmcia slot address space [addr, addr+size[,
+ * returning a pointer to it, or nil if the space was already reserved.
+ */
+static ulong
+pcmmalloc(ulong addr, long size)
+{
+	return rmapalloc(&pcmmaps, PHYSPCMCIA+addr, size, size);
+}
+
+static void
+pcmfree(ulong a, long size)
+{
+	if(a != 0 && size > 0)
+		mapfree(&pcmmaps, a, size);
+}
+
+enum
+{
+	Qdir,
+	Qmem,
+	Qattr,
+	Qctl,
+};
+
+#define SLOTNO(c)	((c->qid.path>>8)&0xff)
+#define TYPE(c)		(c->qid.path&0xff)
+#define QID(s,t)	(((s)<<8)|(t))
+
+static int
+pcmgen(Chan *c, char*, Dirtab*, int, int i, Dir *dp)
+{
+	int slotno;
+	Qid qid;
+	long len;
+	PCMslot *pp;
+
+	if(i>=3*nslot)
+		return -1;
+	slotno = i/3;
+	pp = slot + slotno;
+	if(pp->memlen == 0)
+		return 0;
+	len = 0;
+	switch(i%3){
+	case 0:
+		qid.path = QID(slotno, Qmem);
+		sprint(up->genbuf, "pcm%dmem", slotno);
+		len = pp->memlen;
+		break;
+	case 1:
+		qid.path = QID(slotno, Qattr);
+		sprint(up->genbuf, "pcm%dattr", slotno);
+		len = pp->memlen;
+		break;
+	case 2:
+		qid.path = QID(slotno, Qctl);
+		sprint(up->genbuf, "pcm%dctl", slotno);
+		break;
+	}
+	qid.vers = 0;
+	devdir(c, qid, up->genbuf, len, eve, 0660, dp);
+	return 1;
+}
+
+/*
+ * used only when debugging
+ */
+static void
+pcmciadump(PCMslot *)
+{
+	IMM *io;
+	int i;
+
+	io = m->iomem;
+	print("pipr #%4.4lux pscr #%4.4lux per #%4.4lux pgcr[1] #%8.8lux\n",
+		io->pipr & 0xFFFF, io->pscr & 0xFFFF, io->per & 0xFFFF, io->pgcr[1]);
+	for(i=0; i<8; i++)
+		print("pbr%d #%8.8lux por%d #%8.8lux\n", i, io->pcmr[i].base, i, io->pcmr[i].option);
+}
+
+/*
+ *  set up for slot cards
+ */
+static void
+pcmciareset(void)
+{
+	static int already;
+	int i;
+	Ctlr *cp;
+	IMM *io;
+	PCMslot *pp;
+
+	if(already)
+		return;
+	already = 1;
+
+	cp = controller;
+	/* TO DO: set low power mode? ... */
+
+	mapinit(&pcmmaps, pcmmapv, sizeof(pcmmapv));
+	mapfree(&pcmmaps, PHYSPCMCIA, PCMCIALEN);
+
+	io = m->iomem;
+
+	for(i=0; i<8; i++){
+		io->pcmr[i].option = 0;
+		io->pcmr[i].base = 0;
+	}
+
+	io->pscr = ~0;	/* reset status */
+	/* TO DO: Cboe, Cbreset */
+	/* TO DO: two slots except on 823 */
+	pcmenable();
+	/* TO DO: if the card is there turn on 5V power to keep its battery alive */
+	slot = xalloc(Maxslot * sizeof(PCMslot));
+	lastslot = slot;
+	slot[0].slotshift = Slotashift;
+	slot[1].slotshift = 0;
+	for(i=0; i<Maxslot; i++){
+		pp = &slot[i];
+		if(!pcmslotavail(i)){
+			pp->memlen = 0;
+			continue;
+		}
+		io->per |= (Cbvs1_c|Cbvs2_c|Cbwp_c|Cbcd2_c|Cbcd1_c|Cbbvd2_c|Cbbvd1_c)<<pp->slotshift;	/* enable status interrupts */
+		io->pgcr[i] = (1<<(31-PCMCIAio)) | (1<<(23-PCMCIAstatus));
+		pp->slotno = i;
+		pp->memlen = 8*MB;
+		pp->ctlr = cp;
+		//slotdis(pp);
+		lastslot = slot;
+		nslot = i+1;
+	}
+	if(1)
+		pcmciadump(slot);
+	intrenable(PCMCIAstatus, pcmciaintr, cp, BUSUNKNOWN, "pcmcia");
+	print("pcmcia reset\n");
+}
+
+static void
+pcmciainit(void)
+{
+	kproc("pcmcia", pcmciaproc, nil, 0);
+}
+
+static Chan*
+pcmciaattach(char *spec)
+{
+	return devattach('y', spec);
+}
+
+static Walkqid*
+pcmciawalk(Chan *c, Chan *nc, char **name, int nname)
+{
+	return devwalk(c, nc, name, nname, 0, 0, pcmgen);
+}
+
+static int
+pcmciastat(Chan *c, uchar *dp, int n)
+{
+	return devstat(c, dp, n, 0, 0, pcmgen);
+}
+
+static Chan*
+pcmciaopen(Chan *c, int omode)
+{
+	if(c->qid.type & QTDIR){
+		if(omode != OREAD)
+			error(Eperm);
+	} else
+		increfp(slot + SLOTNO(c));
+	c->mode = openmode(omode);
+	c->flag |= COPEN;
+	c->offset = 0;
+	return c;
+}
+
+static void
+pcmciaclose(Chan *c)
+{
+	if(c->flag & COPEN)
+		if((c->qid.type & QTDIR) == 0)
+			decrefp(slot+SLOTNO(c));
+}
+
+/* a memmove using only bytes */
+static void
+memmoveb(uchar *to, uchar *from, int n)
+{
+	while(n-- > 0)
+		*to++ = *from++;
+}
+
+static long
+pcmread(int slotno, int attr, void *a, long n, ulong offset)
+{
+	int i, len;
+	PCMmap *m;
+	void *ka;
+	uchar *ac;
+	PCMslot *pp;
+
+	pp = slot + slotno;
+	if(pp->memlen < offset)
+		return 0;
+	if(pp->memlen < offset + n)
+		n = pp->memlen - offset;
+
+	ac = a;
+	for(len = n; len > 0; len -= i){
+		if((i = len) > Mgran)
+			i = Mgran;
+		m = pcmmap(pp->slotno, offset, i, attr? Rattrib: Rmem);
+		if(m == 0)
+			error("can't map PCMCIA card");
+		if(waserror()){
+			if(m)
+				pcmunmap(pp->slotno, m);
+			nexterror();
+		}
+		if(offset + len > m->cea)
+			i = m->cea - offset;
+		else
+			i = len;
+		ka = (char*)KADDR(m->isa) + (offset - m->ca);
+		memmoveb(ac, ka, i);
+		poperror();
+		pcmunmap(pp->slotno, m);
+		offset += i;
+		ac += i;
+	}
+
+	return n;
+}
+
+static long
+pcmciaread(Chan *c, void *a, long n, vlong offset)
+{
+	char *cp, *buf;
+	ulong p;
+	PCMslot *pp;
+
+	p = TYPE(c);
+	switch(p){
+	case Qdir:
+		return devdirread(c, a, n, 0, 0, pcmgen);
+	case Qmem:
+	case Qattr:
+		return pcmread(SLOTNO(c), p==Qattr, a, n, offset);
+	case Qctl:
+		buf = malloc(READSTR);
+		if(buf == nil)
+			error(Enomem);
+		if(waserror()){
+			free(buf);
+			nexterror();
+		}
+		cp = buf;
+		pp = slot + SLOTNO(c);
+		if(pp->occupied)
+			cp += sprint(cp, "occupied\n");
+		if(pp->enabled)
+			cp += sprint(cp, "enabled\n");
+		if(pp->powered)
+			cp += sprint(cp, "powered\n");
+		if(pp->configed)
+			cp += sprint(cp, "configed\n");
+		if(pp->wrprot)
+			cp += sprint(cp, "write protected\n");
+		if(pp->busy)
+			cp += sprint(cp, "busy\n");
+		if(pp->v3_3)
+			cp += sprint(cp, "3.3v ok\n");
+		cp += sprint(cp, "battery lvl %d\n", pp->battery);
+		cp += sprint(cp, "voltage select %d\n", pp->voltage);
+		/* TO DO: could return pgcr[] values for debugging */
+		*cp = 0;
+		n = readstr(offset, a, n, buf);
+		poperror();
+		free(buf);
+		break;
+	default:
+		n=0;
+		break;
+	}
+	return n;
+}
+
+static long
+pcmwrite(int dev, int attr, void *a, long n, ulong offset)
+{
+	int i, len;
+	PCMmap *m;
+	void *ka;
+	uchar *ac;
+	PCMslot *pp;
+
+	pp = slot + dev;
+	if(pp->memlen < offset)
+		return 0;
+	if(pp->memlen < offset + n)
+		n = pp->memlen - offset;
+
+	ac = a;
+	for(len = n; len > 0; len -= i){
+		if((i = len) > Mgran)
+			i = Mgran;
+		m = pcmmap(pp->slotno, offset, i, attr? Rattrib: Rmem);
+		if(m == 0)
+			error("can't map PCMCIA card");
+		if(waserror()){
+			if(m)
+				pcmunmap(pp->slotno, m);
+			nexterror();
+		}
+		if(offset + len > m->cea)
+			i = m->cea - offset;
+		else
+			i = len;
+		ka = (char*)KADDR(m->isa) + (offset - m->ca);
+		memmoveb(ka, ac, i);
+		poperror();
+		pcmunmap(pp->slotno, m);
+		offset += i;
+		ac += i;
+	}
+
+	return n;
+}
+
+static long
+pcmciawrite(Chan *c, void *a, long n, vlong offset)
+{
+	ulong p;
+	PCMslot *pp;
+	char buf[32];
+
+	p = TYPE(c);
+	switch(p){
+	case Qctl:
+		if(n >= sizeof(buf))
+			n = sizeof(buf) - 1;
+		strncpy(buf, a, n);
+		buf[n] = 0;
+		pp = slot + SLOTNO(c);
+		if(!pp->occupied)
+			error(Eio);
+
+		/* set vpp on card */
+		if(strncmp(buf, "vpp", 3) == 0){
+			p = strtol(buf+3, nil, 0);
+			pcmsetvpp(pp->slotno, p);
+		}
+		break;
+	case Qmem:
+	case Qattr:
+		pp = slot + SLOTNO(c);
+		if(pp->occupied == 0 || pp->enabled == 0)
+			error(Eio);
+		n = pcmwrite(pp->slotno, p == Qattr, a, n, offset);
+		if(n < 0)
+			error(Eio);
+		break;
+	default:
+		error(Ebadusefd);
+	}
+	return n;
+}
+
+Dev pcmciadevtab = {
+	'y',
+	"pcmcia",
+
+	pcmciareset,
+	pcmciainit,
+	devshutdown,
+	pcmciaattach,
+	pcmciawalk,
+	pcmciastat,
+	pcmciaopen,
+	devcreate,
+	pcmciaclose,
+	pcmciaread,
+	devbread,
+	pcmciawrite,
+	devbwrite,
+	devremove,
+	devwstat,
+};
+
+/*
+ *  configure the PCMslot for IO.  We assume very heavily that we can read
+ *  configuration info from the CIS.  If not, we won't set up correctly.
+ */
+static int
+pcmio(int slotno, ISAConf *isa)
+{
+	PCMslot *pp;
+	PCMconftab *ct, *et, *t;
+	PCMmap *pm;
+	uchar *p;
+	int irq, i, x;
+
+	irq = isa->irq;
+	if(irq == 2)
+		irq = 9;
+
+	if(slotno > nslot)
+		return -1;
+	pp = slot + slotno;
+
+	if(!pp->occupied)
+		return -1;
+
+	et = &pp->ctab[pp->nctab];
+
+	/* assume default is right */
+	if(pp->def)
+		ct = pp->def;
+	else
+		ct = pp->ctab;
+	/* try for best match */
+	if(ct->nlines == 0 || ct->io[0].start != isa->port || ((1<<irq) & ct->irqs) == 0){
+		for(t = pp->ctab; t < et; t++)
+			if(t->nlines && t->io[0].start == isa->port && ((1<<irq) & t->irqs)){
+				ct = t;
+				break;
+			}
+	}
+	if(ct->nlines == 0 || ((1<<irq) & ct->irqs) == 0){
+		for(t = pp->ctab; t < et; t++)
+			if(t->nlines && ((1<<irq) & t->irqs)){
+				ct = t;
+				break;
+			}
+	}
+	if(ct->nlines == 0){
+		for(t = pp->ctab; t < et; t++)
+			if(t->nlines){
+				ct = t;
+				break;
+			}
+	}
+print("slot %d: nlines=%d iolen=%lud irq=%d ct->index=%d nport=%d ct->port=#%lux/%lux\n", slotno, ct->nlines, ct->io[0].len, irq, ct->index, ct->nio, ct->io[0].start, isa->port);
+	if(ct == et || ct->nlines == 0)
+		return -1;
+	/* route interrupts */
+	isa->irq = irq;
+	//wrreg(pp, Rigc, irq | Fnotreset | Fiocard);
+	delay(2);
+
+	/* set power and enable device */
+	pcmsetvcc(pp->slotno, ct->vcc);
+	pcmsetvpp(pp->slotno, ct->vpp1);
+
+	delay(2);	/* could poll BSY during power change */
+
+	for(i=0; i<ct->nio; i++)
+		pcmiomap(pp, ct, i);
+
+	if(ct->nio)
+		isa->port = ct->io[0].start;
+
+	/* only touch Rconfig if it is present */
+	if(pp->cpresent & (1<<Rconfig)){
+print("Rconfig present: #%lux\n", pp->caddr+Rconfig);
+		/*  Reset adapter */
+		pm = pcmmap(slotno, pp->caddr + Rconfig, 1, Rattrib);
+		if(pm == nil)
+			return -1;
+
+		p = (uchar*)KADDR(pm->isa) + (pp->caddr + Rconfig - pm->ca);
+
+		/* set configuration and interrupt type */
+		x = ct->index;
+		if((ct->irqtype & 0x20) && ((ct->irqtype & 0x40)==0 || isa->irq>7))
+			x |= Clevel;
+		*p = x;
+		delay(5);
+
+		pcmunmap(pp->slotno, pm);
+print("Adapter reset\n");
+	}
+
+	return 0;
+}
--- /dev/null
+++ b/os/mpc/devrtc.c
@@ -1,0 +1,258 @@
+#include	"u.h"
+#include	"../port/lib.h"
+#include	"mem.h"
+#include	"dat.h"
+#include	"fns.h"
+#include	"../port/error.h"
+
+#include	"io.h"
+
+/*
+ * MPC8xx real time clock
+ * optional board option switch
+ * optional nvram
+ * interrupt statistics
+ */
+
+enum{
+	Qdir,
+	Qrtc,
+	Qswitch,
+	Qnvram,
+	Qintstat,
+
+	Qporta,
+	Qportb,
+	Qportc,
+
+	/* sccr */
+	RTDIV=	1<<24,
+	RTSEL=	1<<23,
+
+	/* rtcsc */
+	RTE=	1<<0,
+	R38K=	1<<4,
+};
+
+static	QLock	rtclock;		/* mutex on clock operations */
+
+static Dirtab rtcdir[]={
+	".",		{Qdir,0,QTDIR},	0,	0555,
+	"rtc",		{Qrtc, 0},	12,	0664,
+	"switch",	{Qswitch, 0}, 0, 0444,
+	"intstat",	{Qintstat, 0}, 0, 0444,
+	"porta",	{Qporta, 0}, 0, 0444,
+	"portb",	{Qportb, 0}, 0, 0444,
+	"portc",	{Qportc, 0}, 0, 0444,
+	"nvram",	{Qnvram, 0},	0,	0660,
+};
+static long nrtc = nelem(rtcdir)-1;	/* excludes nvram */
+
+static	long	readport(int, ulong, char*, long);
+
+static void
+rtcreset(void)
+{
+	IMM *io;
+	int n;
+
+	io = m->iomem;
+	io->rtcsck = KEEP_ALIVE_KEY;
+	n = (RTClevel<<8)|RTE;
+	if(m->clockgen == 5*MHz)
+		n |= R38K;
+	io->rtcsc = n;
+	io->rtcsck = ~KEEP_ALIVE_KEY;
+	if(conf.nvramsize != 0){
+		rtcdir[nrtc].length = conf.nvramsize;
+		nrtc++;
+	}
+}
+
+static Chan*
+rtcattach(char *spec)
+{
+	return devattach('r', spec);
+}
+
+static Walkqid*
+rtcwalk(Chan *c, Chan *nc, char **name, int nname)
+{
+	return devwalk(c, nc, name, nname, rtcdir, nrtc, devgen);
+}
+
+static int
+rtcstat(Chan *c, uchar *dp, int n)
+{
+	return devstat(c, dp, n, rtcdir, nrtc, devgen);
+}
+
+static Chan*
+rtcopen(Chan *c, int omode)
+{
+	return devopen(c, omode, rtcdir, nrtc, devgen);
+}
+
+static void	 
+rtcclose(Chan*)
+{
+}
+
+static long	 
+rtcread(Chan *c, void *buf, long n, vlong off)
+{
+	ulong offset = off;
+	ulong t;
+	char *b;
+
+	if(c->qid.type & QTDIR)
+		return devdirread(c, buf, n, rtcdir, nrtc, devgen);
+
+	switch((ulong)c->qid.path){
+	case Qrtc:
+		t = m->iomem->rtc;
+		n = readnum(offset, buf, n, t, 12);
+		return n;
+	case Qswitch:
+		return readnum(offset, buf, n, archoptionsw(), 12);
+	case Qintstat:
+		b = malloc(2048);
+		if(waserror()){
+			free(b);
+			nexterror();
+		}
+		intrstats(b, 2048);
+		t = readstr(offset, buf, n, b);
+		poperror();
+		free(b);
+		return t;
+	case Qporta:
+	case Qportb:
+	case Qportc:
+		return readport(c->qid.path, offset, buf, n);
+	case Qnvram:
+		if(offset < 0 || offset >= conf.nvramsize)
+			return 0;
+		if(offset + n > conf.nvramsize)
+			n = conf.nvramsize - offset;
+		memmove(buf, (char*)conf.nvrambase+offset, n);
+		return n;
+	}
+	error(Egreg);
+	return 0;		/* not reached */
+}
+
+static long	 
+rtcwrite(Chan *c, void *buf, long n, vlong off)
+{
+	ulong offset = off;
+	ulong secs;
+	char *cp, sbuf[32];
+	IMM *io;
+
+	switch((ulong)c->qid.path){
+	case Qrtc:
+		/*
+		 *  read the time
+		 */
+		if(offset != 0 || n >= sizeof(sbuf)-1)
+			error(Ebadarg);
+		memmove(sbuf, buf, n);
+		sbuf[n] = '\0';
+		cp = sbuf;
+		while(*cp){
+			if(*cp>='0' && *cp<='9')
+				break;
+			cp++;
+		}
+		secs = strtoul(cp, 0, 0);
+		/*
+		 * set it
+		 */
+		io = ioplock();
+		io->rtck = KEEP_ALIVE_KEY;
+		io->rtc = secs;
+		io->rtck = ~KEEP_ALIVE_KEY;
+		iopunlock();
+		return n;
+	case Qnvram:
+		if(offset < 0 || offset >= conf.nvramsize)
+			return 0;
+		if(offset + n > conf.nvramsize)
+			n = conf.nvramsize - offset;
+		memmove((char*)conf.nvrambase+offset, buf, n);
+		return n;
+	}
+	error(Egreg);
+	return 0;		/* not reached */
+}
+
+static long
+readport(int p, ulong offset, char *buf, long n)
+{
+	long t;
+	char *b;
+	int v[4], i;
+	IMM *io;
+
+	io = m->iomem;
+	for(i=0;i<nelem(v); i++)
+		v[i] = 0;
+	switch(p){
+	case Qporta:
+		v[0] = io->padat;
+		v[1] = io->padir;
+		v[2] = io->papar;
+		break;
+	case Qportb:
+		v[0] = io->pbdat;
+		v[1] = io->pbdir;
+		v[2] = io->pbpar;
+		break;
+	case Qportc:
+		v[0] = io->pcdat;
+		v[1] = io->pcdir;
+		v[2] = io->pcpar;
+		v[3] = io->pcso;
+		break;
+	}
+	b = malloc(READSTR);
+	if(waserror()){
+		free(b);
+		nexterror();
+	}
+	t = 0;
+	for(i=0; i<nelem(v); i++)
+		t += snprint(b+t, READSTR-t, " %8.8ux", v[i]);
+	t = readstr(offset, buf, n, b);
+	poperror();
+	free(b);
+	return t;
+}
+
+long
+rtctime(void)
+{
+	return m->iomem->rtc;
+}
+
+Dev rtcdevtab = {
+	'r',
+	"rtc",
+
+	rtcreset,
+	devinit,
+	devshutdown,
+	rtcattach,
+	rtcwalk,
+	rtcstat,
+	rtcopen,
+	devcreate,
+	rtcclose,
+	rtcread,
+	devbread,
+	rtcwrite,
+	devbwrite,
+	devremove,
+	devwstat,
+};
--- /dev/null
+++ b/os/mpc/devtouch.c
@@ -1,0 +1,497 @@
+#include "u.h"
+#include "../port/lib.h"
+#include "mem.h"
+#include "dat.h"
+#include "fns.h"
+#include "io.h"
+#include "../port/error.h"
+
+#define	DPRINT	if(0)print
+#define	THREEBUT	0	/* !=0, enable 3-button emulation (see below) */
+
+/*
+ * DynaPro touch panel and Maxim MAX192 A/D converter on
+ * York Electronics Centre's BRD/98/024 (Version A) interface,
+ * accessed via mpc8xx SPI interface (see spi.c).
+ *
+ * The highest level of the driver is derived from the ARM/UCB touch panel driver,
+ * simplified because of the differences between the panels.
+ */
+
+/*
+ * Values determined by interface board
+ */
+enum {
+	/* MAX192 control words */
+	MeasureX =	(1<<7)|(0<<6)|(1<<3)|(1<<2)|3,	/* start, channel 0, unipolar, single-ended, external clock */
+	MeasureY =	(1<<7)|(1<<6)|(1<<3)|(1<<2)|3,	/* start, channel 1, unipolar, single-ended, external clock */
+
+	/* port B bits */
+	ADselect = IBIT(16),	/* chip select to MAX192, active low */
+
+	/* port C bits */
+	Xenable =	1<<2,	/* PC13: TOUCH_XEN, active low */
+	Yenable =	1<<3,	/* PC12: TOUCH_YEN, active low */
+	Touched = 1<<10,	/* PC5: contact detect, active low */
+
+	/* interrupt control via port C */
+	TouchIRQ=	2,	/* parallel i/o - PC5 */
+	TouchEnable=	1<<TouchIRQ,	/* mask for cimr */
+
+	/* other parameters */
+	Nconverge =	10,	/* maximum iterations for convergence */
+	MaxDelta =	2,	/* acceptable change in X/Y between iterations */
+};
+
+/*
+ * ADC interface via SPI (see MAX192 data sheet)
+ *	select the ADC
+ *	send 8-bit control word and two zero bytes to clock the conversion
+ *	receive three data bytes
+ *	deselect the ADC and return the result
+ */
+static int
+getcoord(int cw)
+{
+	uchar tbuf[3], rbuf[3];
+	IMM *io;
+	int nr;
+
+	tbuf[0] = cw;
+	tbuf[1] = 0;
+	tbuf[2] = 0;
+	io = ioplock();
+	io->pbdat &= ~ADselect;
+	iopunlock();
+	nr = spioutin(tbuf, sizeof(tbuf), rbuf);
+	io = ioplock();
+	io->pbdat |= ADselect;
+	iopunlock();
+	if(nr != 3)
+		return -1;
+	return ((rbuf[1]<<8)|rbuf[2])>>5;
+}
+
+/*
+ * keep reading the a/d until the value stabilises
+ */
+static int
+dejitter(int enable, int cw)
+{
+	int i, diff, prev, v;
+	IMM *io;
+
+	io = ioplock();
+	io->pcdat &= ~enable;	/* active low */
+	iopunlock();
+
+	i = 0;
+	v = getcoord(cw);
+	do{
+		prev = v;
+		v = getcoord(cw);
+		diff = v - prev;
+		if(diff < 0)
+			diff = -diff;
+	}while(diff >= MaxDelta && ++i <= Nconverge);
+
+	io = ioplock();
+	io->pcdat |= enable;
+	iopunlock();
+	return v;
+}
+
+static void
+adcreset(void)
+{
+	IMM *io;
+
+	/* select port pins */
+	io = ioplock();
+	io->pcdir &= ~(Xenable|Yenable);	/* ensure set to input before changing state */
+	io->pcpar &= ~(Xenable|Yenable);
+	io->pcdat |= Xenable|Yenable;
+	io->pcdir |= Xenable;	/* change enable bits to output one at a time to avoid both being low at once (could damage panel) */
+	io->pcdat |= Xenable;	/* ensure it's high after making it an output */
+	io->pcdir |= Yenable;
+	io->pcdat |= Yenable;	/* ensure it's high after making it an output */
+	io->pcso &= ~(Xenable|Yenable);
+	io->pbdat |= ADselect;
+	io->pbpar &= ~ADselect;
+	io->pbdir |= ADselect;
+	iopunlock();
+}
+
+/*
+ * high-level touch panel interface
+ */
+
+/* to and from fixed point */
+#define	FX(n)	((n)<<16)
+#define	XF(v)		((v)>>16)
+
+typedef struct Touch Touch;
+
+struct Touch {
+	Lock;
+	Rendez	r;
+	int	m[2][3];	/* transformation matrix */
+	int	rate;
+	int	down;
+	int	raw_count;
+	int	valid_count;
+	int	wake_time;
+	int	sleep_time;
+};
+
+static Touch touch = {
+	{0},
+	.r {0},
+	.m {{FX(1), 0, 0},{0, FX(1), 0}},	/* default is 1:1 */
+	.rate 20,	/* milliseconds */
+};
+
+/*
+ * panel-touched state and interrupt
+ */
+
+static int
+touching(void)
+{
+	eieio();
+	return (m->iomem->pcdat & Touched) == 0;
+}
+
+static int
+ispendown(void*)
+{
+	return touch.down || touching();
+}
+
+static void
+touchintr(Ureg*, void*)
+{
+	if((m->iomem->pcdat & Touched) == 0){
+		m->iomem->cimr &= ~TouchEnable;	/* mask interrupts when reading pen */
+		touch.down = 1;
+		wakeup(&touch.r);
+	}
+}
+
+static void
+touchenable(void)
+{
+	IMM *io;
+ 
+	io = ioplock();
+	io->cimr |= TouchEnable;
+	iopunlock();
+}
+
+/*
+ * touchctl commands:
+ *	X a b c	- set X transformation
+ *	Y d e f	- set Y transformation
+ *	s<delay>		- set sample delay in millisec per sample
+ *	r<delay>		- set read delay in microsec
+ *	R<l2nr>			- set log2 of number of readings to average
+ */
+
+enum{
+	Qdir,
+	Qtouchctl,
+	Qtouchstat,
+	Qtouch,
+};
+
+Dirtab touchdir[]={
+	".",	{Qdir, 0, QTDIR}, 0, 0555,
+	"touchctl",	{Qtouchctl, 0}, 	0,	0666,
+	"touchstat",	{Qtouchstat, 0}, 	0,	0444,
+	"touch",	{Qtouch, 0},	0,	0444,
+};
+
+static int
+ptmap(int *m, int x, int y)
+{
+	return XF(m[0]*x + m[1]*y + m[2]);
+}
+
+/*
+ * read a point from the touch panel;
+ * returns true iff the point is valid, otherwise x, y aren't changed
+ */
+static int
+touchreadxy(int *fx, int *fy)
+{
+	int rx, ry;
+
+	if(touching()){
+		rx = dejitter(Xenable, MeasureX);
+		ry = dejitter(Yenable, MeasureY);
+		microdelay(40);
+		if(rx >=0 && ry >= 0){
+			if(0)
+				print("touch %d %d\n", rx, ry);
+			*fx = ptmap(touch.m[0], rx, ry);
+			*fy = ptmap(touch.m[1], rx, ry);
+			touch.raw_count++;
+			return 1;
+		}
+	}
+	return 0;
+}
+
+#define	timer_start()	0	/* could use TBL if necessary */
+#define	tmr2us(n)	0
+
+static void
+touchproc(void*)
+{
+	int b, i, x, y;
+	ulong t1, t2;
+
+	t1 = timer_start();
+	b = 1;
+	for(;;) {
+		//setpri(PriHi);
+		do{
+			touch.down = 0;
+			touch.wake_time += (t2 = timer_start())-t1;
+			touchenable();
+			sleep(&touch.r, ispendown, nil);
+			touch.sleep_time += (t1 = timer_start())-t2;
+		}while(!touchreadxy(&x, &y));
+
+		/* 640x480-specific 3-button emulation hack: */
+		if(THREEBUT){
+			if(y > 481) { 
+				b = ((639-x) >> 7);
+				continue;
+			} else if(y < -2) {
+				b = (x >> 7)+3;
+				continue;
+			}
+		}
+
+		DPRINT("#%d %d", x, y);
+		mousetrack(b, x, y, 0);
+		setpri(PriNormal);
+		while(touching()) {
+			for(i=0; i<3; i++)
+				if(touchreadxy(&x, &y)) {
+					DPRINT("*%d %d", x, y);
+					mousetrack(b, x, y, 0);
+					break;
+				}
+			touch.wake_time += (t2 = timer_start())-t1;
+			tsleep(&touch.r, return0, nil, touch.rate);
+			touch.sleep_time += (t1 = timer_start())-t2;
+		}
+		mousetrack(0, x, y, 0);
+		b = 1;	/* go back to just button one for next press */
+	}
+}
+
+static void
+touchreset(void)
+{
+	IMM *io;
+
+	spireset();
+	adcreset();
+	intrenable(VectorCPIC+TouchIRQ, touchintr, &touch, BUSUNKNOWN, "touch");
+
+	/* set i/o pin to interrupt when panel touched */
+	io = ioplock();
+	io->pcdat &= ~Touched;
+	io->pcpar &= ~Touched;
+	io->pcdir &= ~Touched;
+	io->pcso &= ~Touched;
+	io->pcint |= Touched;	/* high-to-low trigger */
+	io->cimr &= ~TouchEnable;	/* touchproc will enable when ready */
+	iopunlock();
+}
+
+static void
+touchinit(void)
+{
+	static int done;
+
+	if(!done){
+		done = 1;
+		kproc( "touchscreen", touchproc, nil, 0);
+	}
+}
+
+static Chan*
+touchattach(char* spec)
+{
+	return devattach('T', spec);
+}
+
+static Walkqid*
+touchwalk(Chan* c, Chan *nc, char **name, int nname)
+{
+	return devwalk(c, nc, name, nname, touchdir, nelem(touchdir), devgen);
+}
+
+static int
+touchstat(Chan* c, uchar* dp, int n)
+{
+	return devstat(c, dp, n, touchdir, nelem(touchdir), devgen);
+}
+
+static Chan*
+touchopen(Chan* c, int omode)
+{
+	omode = openmode(omode);
+	switch((ulong)c->qid.path){
+	case Qtouchctl:
+	case Qtouchstat:
+		if(!iseve())
+			error(Eperm);
+		break;
+	}
+	return devopen(c, omode, touchdir, nelem(touchdir), devgen);
+}
+
+static void	 
+touchclose(Chan*)
+{
+}
+
+static long	 
+touchread(Chan* c, void* buf, long n, vlong offset)
+{
+	char *tmp;
+	int x, y;
+
+	if(c->qid.type & QTDIR)
+		return devdirread(c, buf, n, touchdir, nelem(touchdir), devgen);
+
+	tmp = malloc(READSTR);
+	if(waserror()){
+		free(tmp);
+		nexterror();
+	}
+	switch((ulong)c->qid.path){
+	case Qtouch:
+		if(!touchreadxy(&x, &y))
+			x = y = -1;
+		snprint(tmp, READSTR, "%d %d", x, y);
+		break;
+	case Qtouchctl:
+		snprint(tmp, READSTR, "s%d\nr%d\nR%d\nX %d %d %d\nY %d %d %d\n",
+			touch.rate, 0, 1,
+			touch.m[0][0], touch.m[0][1], touch.m[0][2],
+			touch.m[1][0], touch.m[1][1], touch.m[1][2]);
+		break;
+	case Qtouchstat:
+		snprint(tmp, READSTR, "%d %d\n%d %d\n",
+			touch.raw_count, touch.valid_count, tmr2us(touch.sleep_time), tmr2us(touch.wake_time));
+		touch.raw_count = 0;
+		touch.valid_count = 0;
+		touch.sleep_time = 0;
+		touch.wake_time = 0;
+		break;
+	default:
+		error(Ebadarg);
+		return 0;
+	}
+	n = readstr(offset, buf, n, tmp);
+	poperror();
+	free(tmp);
+	return n;
+}
+
+static void
+dotouchwrite(Chan *c, char *buf)
+{
+	char *field[8];
+	int nf, cmd, pn, m[3], n;
+
+	nf = getfields(buf, field, nelem(field), 1, " \t\n");
+	if(nf <= 0)
+		return;
+	switch((ulong)c->qid.path){
+	case Qtouchctl:
+		cmd = *(field[0])++;
+		pn = *field[0] == 0;
+		switch(cmd) {
+		case 's':
+			n = strtol(field[pn], 0, 0);
+			if(n <= 0)
+				error(Ebadarg);
+			touch.rate = n;
+			break;
+		case 'r':
+			/* touch read delay */
+			break;
+		case 'X':
+		case 'Y':
+			if(nf < pn+2)
+				error(Ebadarg);
+			m[0] = strtol(field[pn], 0, 0);
+			m[1] = strtol(field[pn+1], 0, 0);
+			m[2] = strtol(field[pn+2], 0, 0);
+			memmove(touch.m[cmd=='Y'], m, sizeof(touch.m[0]));
+			break;
+		case 'c':
+		case 'C':
+		case 'v':
+		case 't':
+		case 'e':
+			/* not used */
+			/* break; */
+		default:
+			error(Ebadarg);
+		}
+		break;
+	default:
+		error(Ebadarg);
+		return;
+	}
+}
+
+static long	 
+touchwrite(Chan* c, void* vp, long n, vlong)
+{
+	char buf[64];
+	char *cp, *a;
+	int n0 = n;
+	int bn;
+
+	a = vp;
+	while(n) {
+		bn = (cp = memchr(a, '\n', n))!=nil ? cp-a+1 : n;
+		n -= bn;
+		bn = bn > sizeof(buf)-1 ? sizeof(buf)-1 : bn;
+		memmove(buf, a, bn);
+		buf[bn] = '\0';
+		a = cp;
+		dotouchwrite(c, buf);
+	}
+	return n0-n;
+}
+
+Dev touchdevtab = {
+	'T',
+	"touch",
+
+	touchreset,
+	touchinit,
+	devshutdown,
+	touchattach,
+	touchwalk,
+	touchstat,
+	touchopen,
+	devcreate,
+	touchclose,
+	touchread,
+	devbread,
+	touchwrite,
+	devbwrite,
+	devremove,
+	devwstat,
+};
--- /dev/null
+++ b/os/mpc/devuart.c
@@ -1,0 +1,1450 @@
+#include	"u.h"
+#include	"../port/lib.h"
+#include	"mem.h"
+#include	"dat.h"
+#include	"fns.h"
+#include	"io.h"
+#include	"../port/error.h"
+
+#include	"../port/netif.h"
+
+enum {
+	Nbuf=	2,	/* double buffered */
+	Rbufsize=	512,
+	Bufsize=	(Rbufsize+CACHELINESZ-1)&~(CACHELINESZ-1),
+	Nuart=	2+4,	/* max in any 8xx architecture (2xSMC, 4xSCC) */
+	CTLS=	's'&037,
+	CTLQ=	'q'&037,
+};
+
+enum {
+	/* status bits in SCC receive buffer descriptors */
+	RxBRK=	1<<7,	/* break ended frame (async hdlc) */
+	RxDE=	1<<7,	/* DPLL error (hdlc) */
+	RxBOF=	1<<6,	/* BOF ended frame (async hdlc) */
+	RxLG=	1<<5,	/* frame too large (hdlc) */
+	RxNO=	1<<4,	/* bad bit alignment (hdlc) */
+	RxBR=	1<<5,	/* break received during frame (uart) */
+	RxFR=	1<<4,	/* framing error (uart) */
+	RxPR=	1<<3,	/* parity error (uart) */
+	RxAB=	1<<3,	/* frame aborted (hdlc, async hdlc) */
+	RxCR=	1<<2,	/* bad CRC (hdlc, async hdlc) */
+	RxOV=	1<<1,	/* receiver overrun (all) */
+	RxCD=	1<<0,	/* CD lost (all) */
+
+	/* hdlc-specific Rx/Tx BDs */
+	TxTC=	1<<10,
+};
+
+/*
+ *  SMC in UART mode
+ */
+
+typedef struct Uartsmc Uartsmc;
+struct Uartsmc {
+	IOCparam;
+	ushort	maxidl;
+	ushort	idlc;
+	ushort	brkln;
+	ushort	brkec;
+	ushort	brkcr;
+	ushort	rmask;
+};
+
+/*
+ * SCC2 UART parameters
+ */
+enum {
+	/* special mode bits */
+	SccAHDLC = 1<<0,
+	SccHDLC = 1<<1,
+	SccIR = 1<<2,
+	SccPPP = 1<<3,
+};
+
+typedef struct Uartscc Uartscc;
+struct Uartscc {
+	SCCparam;
+	uchar	rsvd[8];
+	ushort	max_idl;
+	ushort	idlc;
+	ushort	brkcr;
+	ushort	parec;
+	ushort	frmec;
+	ushort	nosec;
+	ushort	brkec;
+	ushort	brkln;
+	ushort	uaddr1;
+	ushort	uaddr2;
+	ushort	rtemp;
+	ushort	toseq;
+	ushort	character[8];
+	ushort	rccm;
+	ushort	rccrp;
+	ushort	rlbc;
+};
+
+typedef struct UartAHDLC UartAHDLC;
+struct UartAHDLC {
+	SCCparam;
+	ulong	rsvd1;
+	ulong	c_mask;
+	ulong	c_pres;
+	ushort	bof;
+	ushort	eof;
+	ushort	esc;
+	ushort	rsvd2[2];
+	ushort	zero;
+	ushort	rsvd3;
+	ushort	rfthr;
+	ushort	resvd4[2];
+	ulong	txctl_tbl;
+	ulong	rxctl_tbl;
+	ushort	nof;
+	ushort	rsvd5;
+};
+
+typedef struct UartHDLC UartHDLC;
+struct UartHDLC {
+	SCCparam;
+	ulong	rsvd1;
+	ulong	c_mask;
+	ulong	c_pres;
+	ushort	disfc;
+	ushort	crcec;
+	ushort	abtsc;
+	ushort	nmarc;
+	ushort	retrc;
+	ushort	mflr;
+	ushort	max_cnt;
+	ushort	rfthr;
+	ushort	rfcnt;
+	ushort	hmask;
+	ushort	haddr[4];
+	ushort	tmp;
+	ushort	tmp_mb;
+};
+
+enum {
+	/* SCC events of possible interest here eventually */
+	AB=	1<<9,	/* autobaud detected */
+	GRA= 1<<7,	/* graceful stop completed */
+	CCR= 1<<3,	/* control character detected */
+
+	/* SCC, SMC common interrupt events */
+	BSY=	1<<2,	/* receive buffer was busy (overrun) */
+	TXB= 1<<1,	/* block sent */
+	RXB= 1<<0,	/* block received */
+
+	/* SCC events */
+	TXE = 1<<4,	/* transmission error */
+	RXF = 1<<3,	/* final block received */
+
+	/* gsmr_l */
+	ENR = 1<<5,	/* enable receiver */
+	ENT = 1<<4,	/* enable transmitter */
+
+	/* port A */
+	RXD1=	SIBIT(15),
+	TXD1=	SIBIT(14),
+
+	/* port B */
+	RTS1B=	IBIT(19),
+
+	/* port C */
+	RTS1C=	SIBIT(15),
+	CTS1=	SIBIT(11),
+	CD1=	SIBIT(10),
+};
+
+typedef struct Uart Uart;
+struct Uart
+{
+	QLock;
+
+	Uart	*elist;		/* next enabled interface */
+	char	name[KNAMELEN];
+
+	int	x;		/* index: x in SMCx or SCCx */
+	int	cpmid;		/* eg, SCC1ID, SMC1ID */
+	CPMdev*	cpm;
+	int	opens;
+	uchar	bpc;	/* bits/char */
+	uchar	parity;
+	uchar	stopb;
+	uchar	setup;
+	uchar	enabled;
+	int	dev;
+
+	ulong	frame;		/* framing errors */
+	ulong	perror;
+	ulong	overrun;	/* rcvr overruns */
+	ulong	crcerr;
+	ulong	interrupts;
+	int	baud;		/* baud rate */
+
+	/* flow control */
+	int	xonoff;		/* software flow control on */
+	int	blocked;
+	int	modem;		/* hardware flow control on */
+	int	cts;		/* ... cts state */
+	int	rts;		/* ... rts state */
+	Rendez	r;
+
+	/* buffers */
+	int	(*putc)(Queue*, int);
+	Queue	*iq;
+	Queue	*oq;
+
+	/* staging areas to avoid some of the per character costs */
+	/* TO DO: should probably use usual Ring */
+	Block*	istage[Nbuf];	/* double buffered */
+	int	rdrx;	/* last buffer read */
+
+	Lock	plock;		/* for output variables */
+	Block*	outb;	/* currently transmitting Block */
+
+	BD*	rxb;
+	BD*	txb;
+
+	SMC*	smc;
+	SCC*	scc;
+	IOCparam*	param;
+	ushort*	brkcr;	/* brkcr location in appropriate block */
+	int	brgc;
+	int	mode;
+	Block*	partial;
+	int	loopback;
+};
+
+static Uart *uart[Nuart];
+static int nuart;
+
+struct Uartalloc {
+	Lock;
+	Uart *elist;	/* list of enabled interfaces */
+} uartalloc;
+
+static void uartintr(Uart*, int);
+static void smcuintr(Ureg*, void*);
+static void sccuintr(Ureg*, void*);
+
+static void
+uartsetbuf(Uart *up)
+{
+	IOCparam *p;
+	BD *bd;
+	int i;
+	Block *bp;
+
+	p = up->param;
+	p->rfcr = 0x18;
+	p->tfcr = 0x18;
+	p->mrblr = Rbufsize;
+
+	if((bd = up->rxb) == nil){
+		bd = bdalloc(Nbuf);
+		up->rxb = bd;
+	}
+	p->rbase = (ushort)bd;
+	for(i=0; i<Nbuf; i++){
+		bd->status = BDEmpty|BDInt;
+		bd->length = 0;
+		if((bp = up->istage[i]) == nil)
+			up->istage[i] = bp = allocb(Bufsize);
+		bd->addr = PADDR(bp->wp);
+		dcflush(bp->wp, Bufsize);
+		bd++;
+	}
+	(bd-1)->status |= BDWrap;
+	up->rdrx = 0;
+
+	if((bd = up->txb) == nil){
+		bd = bdalloc(1);
+		up->txb = bd;
+	}
+	p->tbase = (ushort)bd;
+	bd->status = BDWrap|BDInt;
+	bd->length = 0;
+	bd->addr = 0;
+}
+
+static void
+smcsetup(Uart *up)
+{
+	IMM *io;
+	Uartsmc *p;
+	SMC *smc;
+	ulong txrx;
+
+	archdisableuart(up->cpmid);
+	up->brgc = brgalloc();
+	if(up->brgc < 0)
+		error(Eio);
+	m->iomem->brgc[up->brgc] = baudgen(up->baud, 16) | BaudEnable;
+	smcnmsi(up->x, up->brgc);
+
+	archenableuart(up->cpmid, 0);
+
+	if(up->x == 1)
+		txrx = IBIT(24)|IBIT(25);	/* SMC1 RX/TX */
+	else
+		txrx = IBIT(20)|IBIT(21);	/* SMC2 */
+	io = ioplock();
+	io->pbpar |= txrx;
+	io->pbdir &= ~txrx;
+	iopunlock();
+
+	up->param = up->cpm->param;
+	uartsetbuf(up);
+
+	cpmop(up->cpm, InitRxTx, 0);
+
+	/* SMC protocol parameters */
+	p = (Uartsmc*)up->param;
+	up->brkcr = &p->brkcr;
+	p->maxidl = 1;	/* non-zero so buffer closes when idle before mrblr reached */
+	p->brkln = 0;
+	p->brkec = 0;
+	p->brkcr = 1;
+	smc = up->cpm->regs;
+	smc->smce = 0xff;	/* clear events */
+	smc->smcm = BSY|RXB|TXB;	/* enable all possible interrupts */
+	up->smc = smc;
+	smc->smcmr = ((1+8+1-1)<<11)|(2<<4);	/* 8-bit, 1 stop, no parity; UART mode */
+	intrenable(VectorCPIC+up->cpm->irq, smcuintr, up, BUSUNKNOWN, up->name);
+	/* enable when device opened */
+}
+
+static void
+smcuintr(Ureg*, void *a)
+{
+	Uart *up;
+	int events;
+
+	up = a;
+	events = up->smc->smce;
+	eieio();
+	up->smc->smce = events;
+	uartintr(up, events&(BSY|RXB|TXB));
+}
+
+/*
+ * set the IO ports to enable the control signals for SCCx
+ */
+static void
+sccuartpins(int x, int mode)
+{
+	IMM *io;
+	int i, w;
+
+	x--;
+	io = ioplock();
+	i = 2*x;
+	w = (TXD1|RXD1)<<i;	/* TXDn and RXDn in port A */
+	io->papar |= w;	/* enable TXDn and RXDn pins */
+	io->padir &= ~w;
+	if((mode & SccIR) == 0)
+		io->paodr |= TXD1<<i;
+	else
+		io->paodr &= ~w;	/* not open drain */
+
+	w = (CD1|CTS1)<<i;	/* CDn and CTSn in port C */
+	io->pcpar &= ~w;
+	io->pcdir &= ~w;
+	if(conf.nocts2 || mode)
+		io->pcso &= ~w;	/* force CTS and CD on */
+	else
+		io->pcso |= w;
+
+	w = RTS1B<<x;
+	io->pbpar &= ~w;
+	io->pbdir &= ~w;
+
+	w = RTS1C<<x;	/* RTSn~ */
+	if((mode & SccIR) == 0)
+		io->pcpar |= w;
+	else
+		io->pcpar &= ~w;	/* don't use for IR */
+	iopunlock();
+}
+
+static void
+sccsetup(Uart *up)
+{
+	SCC *scc;
+	int i;
+
+	scc = up->cpm->regs;
+	up->scc = scc;
+	up->param = up->cpm->param;
+	sccxstop(up->cpm);
+	archdisableuart(up->cpmid);
+	if(up->brgc < 0){
+		up->brgc = brgalloc();
+		if(up->brgc < 0)
+			error(Eio);
+	}
+	m->iomem->brgc[up->brgc] = baudgen(up->baud, 16) | BaudEnable;
+	sccnmsi(up->x, up->brgc, up->brgc);
+	sccuartpins(up->x, up->mode);
+
+	uartsetbuf(up);
+
+	cpmop(up->cpm, InitRxTx, 0);
+
+	/* SCC protocol parameters */
+	if((up->mode & (SccAHDLC|SccHDLC)) == 0){
+		Uartscc *sp;
+		sp = (Uartscc*)up->param;
+		sp->max_idl = 1;
+		sp->brkcr = 1;
+		sp->parec = 0;
+		sp->frmec = 0;
+		sp->nosec = 0;
+		sp->brkec = 0;
+		sp->brkln = 0;
+		sp->brkec = 0;
+		sp->uaddr1 = 0;
+		sp->uaddr2 = 0;
+		sp->toseq = 0;
+		for(i=0; i<8; i++)
+			sp->character[i] = 0x8000;
+		sp->rccm = 0xC0FF;
+		up->brkcr = &sp->brkcr;
+		scc->irmode = 0;
+		scc->dsr = ~0;
+		scc->gsmrh = 1<<5;	/* 8-bit oriented receive fifo */
+		scc->gsmrl = 0x28004;	/* UART mode */
+	}else{
+		UartAHDLC *hp;
+		hp = (UartAHDLC*)up->param;
+		hp->c_mask = 0x0000F0B8;
+		hp->c_pres = 0x0000FFFF;
+		if(up->mode & SccIR){
+			hp->bof = 0xC0;
+			hp->eof = 0xC1;
+			//scc->dsr = 0xC0C0;
+			scc->dsr = 0x7E7E;
+		}else{
+			hp->bof = 0x7E;
+			hp->eof = 0x7E;
+			scc->dsr = 0x7E7E;
+		}
+		hp->esc = 0x7D;
+		hp->zero = 0;
+		if(up->mode & SccHDLC)
+			hp->rfthr = 1;
+		else
+			hp->rfthr = 0;	/* receive threshold of 1 doesn't work properly for Async HDLC */
+		hp->txctl_tbl = 0;
+		hp->rxctl_tbl = 0;
+		if(up->mode & SccIR){
+			/* low-speed infrared */
+			hp->nof = 12-1;	/* 12 flags */
+			scc->irsip = 0;
+			scc->irmode = (2<<8) | 1;
+			archsetirxcvr(0);
+			if(up->loopback)
+				scc->irmode = (3<<4)|1;	/* loopback */
+		}else{
+			scc->irmode = 0;
+			hp->txctl_tbl = ~0;
+			hp->rxctl_tbl = ~0;
+			hp->nof = 1-1;	/* one opening flag */
+		}
+		up->brkcr = nil;
+		scc->gsmrh = 1<<5;	/* 8-bit oriented receive fifo */
+		if(up->mode & SccHDLC)
+			scc->gsmrl = 0x28000;	/* HDLC */
+		else
+			scc->gsmrl = 0x28006;	/* async HDLC/IrDA */
+	}
+	archenableuart(up->cpmid, (up->mode&SccIR)!=0);
+	scc->scce = ~0;	/* clear events */
+	scc->sccm = TXE|BSY|RXF|TXB|RXB;	/* enable all interesting interrupts */
+	intrenable(VectorCPIC+up->cpm->irq, sccuintr, up, BUSUNKNOWN, up->name);
+	scc->psmr = 3<<12;	/* 8-bit, 1 stop, no parity; UART mode */
+	if(up->loopback && (up->mode & SccIR) == 0)
+		scc->gsmrl |= 1<<6;	/* internal loop back */
+	scc->gsmrl |= ENT|ENR;	/* enable rx/tx */
+	if(0){
+		print("gsmrl=%8.8lux gsmrh=%8.8lux dsr=%4.4ux irmode=%4.4ux\n", scc->gsmrl, scc->gsmrh, scc->dsr, scc->irmode);
+		for(i=0; i<sizeof(Uartscc); i+=4)
+			print("%2.2ux %8.8lux\n", i, *(ulong*)((uchar*)up->param+i));
+	}
+}
+
+static void
+sccuintr(Ureg*, void *a)
+{
+	Uart *up;
+	int events;
+
+	up = a;
+	if(up->scc == nil)
+		return;
+	events = up->scc->scce;
+	eieio();
+	up->scc->scce = events;
+	if(up->enabled){
+		if(0)
+			print("#%ux|", events);
+		uartintr(up, events);
+	}
+}
+
+static void
+uartsetbaud(Uart *p, int rate)
+{
+	if(rate <= 0 || p->brgc < 0)
+		return;
+	p->baud = rate;
+	m->iomem->brgc[p->brgc] = baudgen(rate, 16) | BaudEnable;
+}
+
+static void
+uartsetmode(Uart *p)
+{
+	int r, clen;
+
+	ilock(&p->plock);
+	clen = p->bpc;
+	if(p->parity == 'e' || p->parity == 'o')
+		clen++;
+	clen++;	/* stop bit */
+	if(p->stopb == 2)
+		clen++;
+	if(p->smc){
+		r = p->smc->smcmr & 0x3F;	/* keep mode, enable bits */
+		r |= (clen<<11);
+		if(p->parity == 'e')
+			r |= 3<<8;
+		else if(p->parity == 'o')
+			r |= 2<<8;
+		if(p->stopb == 2)
+			r |= 1<<10;
+		eieio();
+		p->smc->smcmr = r;
+	}else if(p->scc && p->mode == 0){
+		r = p->scc->psmr & 0x8FE0;	/* keep mode bits */
+		r |= ((p->bpc-5)&3)<<12;
+		if(p->parity == 'e')
+			r |= (6<<2)|2;
+		else if(p->parity == 'o')
+			r |= (4<<2)|0;
+		if(p->stopb == 2)
+			r |= 1<<14;
+		eieio();
+		p->scc->psmr = r;
+	}
+	iunlock(&p->plock);
+}
+
+static void
+uartparity(Uart *p, char type)
+{
+	ilock(&p->plock);
+	p->parity = type;
+	iunlock(&p->plock);
+	uartsetmode(p);
+}
+
+/*
+ *  set bits/character
+ */
+static void
+uartbits(Uart *p, int bits)
+{
+	if(bits < 5 || bits > 14 || bits > 8 && p->scc)
+		error(Ebadarg);
+
+	ilock(&p->plock);
+	p->bpc = bits;
+	iunlock(&p->plock);
+	uartsetmode(p);
+}
+
+
+/*
+ *  toggle DTR
+ */
+static void
+uartdtr(Uart *p, int n)
+{
+	if(p->scc == nil)
+		return;	/* not possible */
+	USED(n);	/* not possible on FADS */
+}
+
+/*
+ *  toggle RTS
+ */
+static void
+uartrts(Uart *p, int n)
+{
+	p->rts = n;
+	if(p->scc == nil)
+		return;	/* not possible */
+	USED(n);	/* not possible on FADS */
+}
+
+/*
+ *  send break
+ */
+static void
+uartbreak(Uart *p, int ms)
+{
+	if(p->brkcr == nil)
+		return;
+
+	if(ms <= 0)
+		ms = 200;
+
+	if(waserror()){
+		ilock(&p->plock);
+		*p->brkcr = 1;
+		cpmop(p->cpm, RestartTx, 0);
+		iunlock(&p->plock);
+		nexterror();
+	}
+	ilock(&p->plock);
+	*p->brkcr = ((p->baud/(p->bpc+2))*ms+500)/1000;
+	cpmop(p->cpm, StopTx, 0);
+	iunlock(&p->plock);
+
+	tsleep(&up->sleep, return0, 0, ms);
+
+	poperror();
+	ilock(&p->plock);
+	*p->brkcr = 1;
+	cpmop(p->cpm, RestartTx, 0);
+	iunlock(&p->plock);
+}
+
+/*
+ *  modem flow control on/off (rts/cts)
+ */
+static void
+uartmflow(Uart *p, int n)
+{
+	if(p->scc == nil)
+		return;	/* not possible */
+	if(n){
+		p->modem = 1;
+		/* enable status interrupts ... */
+		p->scc->psmr |= 1<<15;	/* enable async flow control */
+		p->cts = 1;
+		/* could change maxidl */
+	}else{
+		p->modem = 0;
+		/* stop status interrupts ... */
+		p->scc->psmr &= ~(1<<15);
+		p->cts = 1;
+	}
+}
+
+/*
+ *  turn on a port's interrupts.  set DTR and RTS
+ */
+void
+uartenable(Uart *p)
+{
+	Uart **l;
+
+	if(p->enabled)
+		return;
+
+	if(p->setup == 0){
+		if(p->cpmid == CPsmc1 || p->cpmid == CPsmc2)
+			smcsetup(p);
+		else
+			sccsetup(p);
+		p->setup = 1;
+	}
+
+	/*
+ 	 *  turn on interrupts
+	 */
+	if(p->smc){
+		cpmop(p->cpm, RestartTx, 0);
+		p->smc->smcmr |= 3;
+		p->smc->smcm = BSY|TXB|RXB;
+		eieio();
+	}else if(p->scc){
+		cpmop(p->cpm, RestartTx, 0);
+		p->scc->gsmrl |= ENT|ENR;
+		p->scc->sccm = BSY|TXB|RXB;
+		eieio();
+	}
+
+	/*
+	 *  turn on DTR and RTS
+	 */
+	uartdtr(p, 1);
+	uartrts(p, 1);
+
+	/*
+	 *  assume we can send
+	 */
+	p->cts = 1;
+	p->blocked = 0;
+
+	/*
+	 *  set baud rate to the last used
+	 */
+	uartsetbaud(p, p->baud);
+
+	lock(&uartalloc);
+	for(l = &uartalloc.elist; *l; l = &(*l)->elist){
+		if(*l == p)
+			break;
+	}
+	if(*l == 0){
+		p->elist = uartalloc.elist;
+		uartalloc.elist = p;
+	}
+	p->enabled = 1;
+	unlock(&uartalloc);
+	p->cts = 1;
+	p->blocked = 0;
+	p->xonoff = 0;
+	p->enabled = 1;
+}
+
+/*
+ *  turn off a port's interrupts.  reset DTR and RTS
+ */
+void
+uartdisable(Uart *p)
+{
+	Uart **l;
+
+	/*
+ 	 *  turn off interrpts
+	 */
+	if(p->smc)
+		smcxstop(p->cpm);
+	else if(p->scc)
+		sccxstop(p->cpm);
+
+	/*
+	 *  revert to default settings
+	 */
+	p->bpc = 8;
+	p->parity = 0;
+	p->stopb = 0;
+
+	/*
+	 *  turn off DTR, RTS, hardware flow control & fifo's
+	 */
+	uartdtr(p, 0);
+	uartrts(p, 0);
+	uartmflow(p, 0);
+	p->xonoff = p->blocked = 0;
+
+	lock(&uartalloc);
+	for(l = &uartalloc.elist; *l; l = &(*l)->elist){
+		if(*l == p){
+			*l = p->elist;
+			break;
+		}
+	}
+	p->enabled = 0;
+	unlock(&uartalloc);
+}
+
+/*
+ *  set the next output buffer going
+ */
+static void
+txstart(Uart *p)
+{
+	Block *b;
+	int n, flags;
+
+	if(!p->cts || p->blocked || p->txb->status & BDReady)
+		return;
+	if((b = p->outb) == nil){
+		if((b = qget(p->oq)) == nil)
+			return;
+		if(p->mode & SccPPP &&
+		   p->mode & SccAHDLC &&
+		   BLEN(b) >= 8){	/* strip framing data */
+			UartAHDLC *hp;
+			hp = (UartAHDLC*)p->param;
+			if(hp != nil && (p->mode & SccIR) == 0){
+				hp->txctl_tbl = nhgetl(b->rp);
+				hp->rxctl_tbl = nhgetl(b->rp+4);
+			}
+			b->rp += 8;
+			if(0)
+				print("tx #%lux rx #%lux\n", hp->txctl_tbl, hp->rxctl_tbl);
+		}
+	}
+	n = BLEN(b);
+	if(n <= 0)
+		print("txstart: 0\n");
+	if(p->bpc > 8){
+		/* half-word alignment and length if chars are long */
+		if(PADDR(b->rp)&1){	/* must be even if chars are long */
+			memmove(b->base, b->rp, n);
+			b->rp = b->base;
+			b->wp = b->rp+n;
+		}
+		if(n & 1)
+			n++;
+	}
+	dcflush(b->rp, n);
+	p->outb = b;
+	if(n > 0xFFFF)
+		n = 0xFFFE;
+	if(p->mode & SccHDLC)
+		flags = BDLast | TxTC;
+	else if(p->mode)
+		flags = BDLast;
+	else
+		flags = 0;
+	p->txb->addr = PADDR(b->rp);
+	p->txb->length = n;
+	eieio();
+	p->txb->status = (p->txb->status & BDWrap) | flags | BDReady|BDInt;
+	eieio();
+}
+
+/*
+ *  (re)start output
+ */
+static void
+uartkick(void *v)
+{
+	Uart *p;
+
+	p = v;
+	ilock(&p->plock);
+	if(p->outb == nil)
+		txstart(p);
+	iunlock(&p->plock);
+}
+
+/*
+ *  restart input if it's off
+ */
+static void
+uartflow(void *v)
+{
+	Uart *p;
+
+	p = v;
+	if(p->modem)
+		uartrts(p, 1);
+}
+
+static void
+uartsetup(int x, int lid, char *name)
+{
+	Uart *p;
+
+	if(nuart >= Nuart)
+		return;
+
+	p = xalloc(sizeof(Uart));
+	uart[nuart] = p;
+	strcpy(p->name, name);
+	p->dev = nuart;
+	nuart++;
+	p->x = x;
+	p->cpmid = lid;
+	p->cpm = cpmdev(lid);
+	p->brgc = -1;
+	p->mode = 0;
+
+	/*
+	 *  set rate to 9600 baud.
+	 *  8 bits/character.
+	 *  1 stop bit.
+	 *  interrupts enabled.
+	 */
+	p->bpc = 8;
+	p->parity = 0;
+	p->baud = 9600;
+
+	p->iq = qopen(4*1024, Qcoalesce, uartflow, p);
+	p->oq = qopen(4*1024, 0, uartkick, p);
+}
+
+/*
+ *  called by main() to configure a duart port as a console or a mouse
+ */
+void
+uartspecial(int port, int baud, Queue **in, Queue **out, int (*putc)(Queue*, int))
+{
+	Uart *p;
+
+	if(port < 0 || port >= nuart || (p = uart[port]) == nil)
+		return;	/* specified port not implemented */
+	uartenable(p);
+	if(baud)
+		uartsetbaud(p, baud);
+	p->putc = putc;
+	if(in)
+		*in = p->iq;
+	if(out)
+		*out = p->oq;
+	p->opens++;
+}
+
+static int
+uartinput(Uart *p, BD *bd)
+{
+	int ch, dokick, i, l;
+	uchar *bp;
+
+	dokick = 0;
+	if(bd->status & RxFR)
+		p->frame++;
+	if(bd->status & RxOV)
+		p->overrun++;
+	l = bd->length;
+	if(bd->status & RxPR){
+		p->perror++;
+		l--;	/* it's the last character */
+	}
+	bp = KADDR(bd->addr);
+	if(p->xonoff || p->putc && p->opens==1){
+		for(i=0; i<l; i++){
+			ch = bp[i];
+			if(p->xonoff){
+				if(ch == CTLS){
+					p->blocked = 1;
+					cpmop(p->cpm, StopTx, 0);
+				}else if (ch == CTLQ){
+					p->blocked = 0;
+					dokick = 1;
+				}
+				/* BUG? should discard on/off char? */
+			}
+			if(p->putc)
+				(*p->putc)(p->iq, ch);
+		}
+	}
+	if(l > 0 && (p->putc == nil || p->opens>1))
+		qproduce(p->iq, bp, l);
+	return dokick;
+}
+
+static void
+framedinput(Uart *p, BD *bd)
+{
+	Block *pkt;
+	int l;
+
+	pkt = p->partial;
+	p->partial = nil;
+	if(bd->status & RxOV){
+		p->overrun++;
+		goto Discard;
+	}
+	if(bd->status & (RxAB|RxCR|RxCD|RxLG|RxNO|RxDE|RxBOF|RxBRK)){
+		if(bd->status & RxCR)
+			p->crcerr++;
+		else
+			p->frame++;
+		goto Discard;
+	}
+	if(pkt == nil){
+		pkt = iallocb(1500);	/* TO DO: allocate less if possible */
+		if(pkt == nil)
+			return;
+	}
+	l = bd->length;
+	if(bd->status & BDLast)
+		l -= BLEN(pkt);	/* last one gives size of entire frame */
+	if(l > 0){
+		if(pkt->wp+l > pkt->lim)
+			goto Discard;
+		memmove(pkt->wp, KADDR(bd->addr), l);
+		pkt->wp += l;
+	}
+	if(0)
+		print("#%ux|", bd->status);
+	if(bd->status & BDLast){
+		if(p->mode & (SccHDLC|SccAHDLC)){
+			if(BLEN(pkt) <= 2){
+				p->frame++;
+				goto Discard;
+			}
+			pkt->wp -= 2;	/* strip CRC */
+		}
+		qpass(p->iq, pkt);
+	}else
+		p->partial = pkt;
+	return;
+
+Discard:
+	if(pkt != nil)
+		freeb(pkt);
+}
+
+/*
+ *  handle an interrupt to a single uart
+ */
+static void
+uartintr(Uart *p, int events)
+{
+	int dokick;
+	BD *bd;
+	Block *b;
+
+	if(events & BSY)
+		p->overrun++;
+	p->interrupts++;
+	dokick = 0;
+	while(p->rxb != nil && ((bd = &p->rxb[p->rdrx])->status & BDEmpty) == 0){
+		dcinval(KADDR(bd->addr), bd->length);
+		if(p->mode)
+			framedinput(p, bd);
+		else if(uartinput(p, bd))
+			dokick = 1;
+		bd->status = (bd->status & BDWrap) | BDEmpty|BDInt;
+		eieio();
+		if(++p->rdrx >= Nbuf)
+			p->rdrx = 0;
+	}
+	if((bd = p->txb) != nil){
+		if((bd->status & BDReady) == 0){
+			ilock(&p->plock);
+			if((b = p->outb) != nil){
+				b->rp += bd->length;
+				if(b->rp >= b->wp){
+					p->outb = nil;
+					freeb(b);
+				}
+			}
+			txstart(p);
+			iunlock(&p->plock);
+		}
+	}
+	eieio();
+	/* TO DO: modem status isn't available on 82xFADS */
+	if(dokick && p->cts && !p->blocked){
+		if(p->outb == nil){
+			ilock(&p->plock);
+			txstart(p);
+			iunlock(&p->plock);
+		}
+		cpmop(p->cpm, RestartTx, 0);
+	} else if (events & TXE)
+		cpmop(p->cpm, RestartTx, 0);
+}
+
+/*
+ * used to ensure uart console output when debugging
+ */
+void
+uartwait(void)
+{
+	Uart *p = uart[0];
+	int s;
+
+	while(p && (p->outb||qlen(p->oq))){
+		if(islo())
+			continue;
+		s = splhi();
+		if((p->txb->status & BDReady) == 0){
+			p->blocked = 0;
+			p->cts = 1;
+			if(p->scc == nil)
+				smcuintr(nil, p);
+			else
+				sccuintr(nil, p);
+		}
+		splx(s);
+	}
+}
+
+static Dirtab *uartdir;
+static int ndir;
+
+static void
+setlength(int i)
+{
+	Uart *p;
+
+	if(i >= 0){
+		p = uart[i];
+		if(p && p->opens && p->iq)
+			uartdir[1+4*i].length = qlen(p->iq);
+	} else for(i = 0; i < nuart; i++){
+		p = uart[i];
+		if(p && p->opens && p->iq)
+			uartdir[1+4*i].length = qlen(p->iq);
+	}
+		
+}
+
+void
+uartinstall(void)
+{
+	static int already;
+	int i, n;
+	char name[2*KNAMELEN];
+	if(already)
+		return;
+	already = 1;
+	n = 0;
+	for(i=0; i<2; i++)
+		if(conf.smcuarts & (1<<i)){
+			snprint(name, sizeof(name), "eia%d", n++);
+			uartsetup(i+1, CPsmc1+i, name);
+		}
+	n = 2;
+	for(i=0; i<conf.nscc; i++)
+		if(conf.sccuarts & (1<<i)){
+			snprint(name, sizeof(name), "eia%d", n++);
+			uartsetup(i+1, CPscc1+i, name);
+		}
+}
+
+/*
+ *  all uarts must be uartsetup() by this point or inside of uartinstall()
+ */
+static void
+uartreset(void)
+{
+	int i;
+	Dirtab *dp;
+
+	uartinstall();	/* architecture specific */
+
+	ndir = 1+4*nuart;
+	uartdir = xalloc(ndir * sizeof(Dirtab));
+	dp = uartdir;
+	strcpy(dp->name, ".");
+	mkqid(&dp->qid, 0, 0, QTDIR);
+	dp->length = 0;
+	dp->perm = DMDIR|0555;
+	dp++;
+	for(i = 0; i < nuart; i++){
+		/* 4 directory entries per port */
+		strcpy(dp->name, uart[i]->name);
+		dp->qid.path = NETQID(i, Ndataqid);
+		dp->perm = 0660;
+		dp++;
+		sprint(dp->name, "%sctl", uart[i]->name);
+		dp->qid.path = NETQID(i, Nctlqid);
+		dp->perm = 0660;
+		dp++;
+		sprint(dp->name, "%sstatus", uart[i]->name);
+		dp->qid.path = NETQID(i, Nstatqid);
+		dp->perm = 0444;
+		dp++;
+		sprint(dp->name, "%smode", uart[i]->name);
+		dp->qid.path = NETQID(i, Ntypeqid);
+		dp->perm = 0660;
+		dp++;
+	}
+}
+
+static Chan*
+uartattach(char *spec)
+{
+	return devattach('t', spec);
+}
+
+static Walkqid*
+uartwalk(Chan *c, Chan *nc, char **name, int nname)
+{
+	return devwalk(c, nc, name, nname, uartdir, ndir, devgen);
+}
+
+static int
+uartstat(Chan *c, uchar *dp, int n)
+{
+	if(NETTYPE(c->qid.path) == Ndataqid)
+		setlength(NETID(c->qid.path));
+	return devstat(c, dp, n, uartdir, ndir, devgen);
+}
+
+static Chan*
+uartopen(Chan *c, int omode)
+{
+	Uart *p;
+
+	c = devopen(c, omode, uartdir, ndir, devgen);
+
+	switch(NETTYPE(c->qid.path)){
+	case Nctlqid:
+	case Ndataqid:
+		p = uart[NETID(c->qid.path)];
+		qlock(p);
+		if(p->opens++ == 0){
+			uartenable(p);
+			qreopen(p->iq);
+			qreopen(p->oq);
+		}
+		qunlock(p);
+		break;
+	}
+
+	return c;
+}
+
+static void
+uartclose(Chan *c)
+{
+	Uart *p;
+
+	if(c->qid.type & QTDIR)
+		return;
+	if((c->flag & COPEN) == 0)
+		return;
+	switch(NETTYPE(c->qid.path)){
+	case Ndataqid:
+	case Nctlqid:
+		p = uart[NETID(c->qid.path)];
+		qlock(p);
+		if(--(p->opens) == 0){
+			uartdisable(p);
+			qclose(p->iq);
+			qclose(p->oq);
+		}
+		qunlock(p);
+		break;
+	}
+}
+
+static long
+uartstatus(Chan*, Uart *p, void *buf, long n, long offset)
+{
+	IMM *io;
+	char str[256];
+
+// TO DO: change to standard format for first line:
+//"b%d c%d d%d e%d l%d m%d p%c r%d s%d i%d\n"
+	sprint(str, "opens %d ferr %lud oerr %lud crcerr %lud baud %ud perr %lud intr %lud", p->opens,
+		p->frame, p->overrun, p->crcerr, p->baud, p->perror, p->interrupts);
+	/* TO DO: cts, dsr, ring, dcd, dtr, rts aren't all available on 82xFADS */
+	io = m->iomem;
+	if(p->scc){
+		if((io->pcdat & SIBIT(9)) == 0)
+			strcat(str, " cts");
+		if((io->pcdat & SIBIT(8)) == 0)
+			strcat(str, " dcd");
+		if((io->pbdat & IBIT(22)) == 0)
+			strcat(str, " dtr");
+	}else if(p->smc){
+		if((io->pbdat & IBIT(23)) == 0)
+			strcat(str, " dtr");
+	}
+	strcat(str, "\n");
+	return readstr(offset, buf, n, str);
+}
+
+static long
+uartread(Chan *c, void *buf, long n, vlong offset)
+{
+	Uart *p;
+
+	if(c->qid.type & QTDIR){
+		setlength(-1);
+		return devdirread(c, buf, n, uartdir, ndir, devgen);
+	}
+
+	p = uart[NETID(c->qid.path)];
+	switch(NETTYPE(c->qid.path)){
+	case Ndataqid:
+		return qread(p->iq, buf, n);
+	case Nctlqid:
+		return readnum(offset, buf, n, NETID(c->qid.path), NUMSIZE);
+	case Nstatqid:
+		return uartstatus(c, p, buf, n, offset);
+	case Ntypeqid:
+		return readnum(offset, buf, n, p->mode, NUMSIZE);
+	}
+
+	return 0;
+}
+
+static Block*
+uartbread(Chan *c, long n, ulong offset)
+{
+	if(c->qid.type & QTDIR || NETTYPE(c->qid.path) != Ndataqid)
+		return devbread(c, n, offset);
+	return qbread(uart[NETID(c->qid.path)]->iq, n);
+}
+
+static void
+uartctl(Uart *p, char *cmd)
+{
+	int i, n;
+
+	/* let output drain for a while */
+	for(i = 0; i < 16 && qlen(p->oq); i++)
+		tsleep(&p->r, (int(*)(void*))qlen, p->oq, 125);
+
+	if(strncmp(cmd, "break", 5) == 0){
+		uartbreak(p, 0);
+		return;
+	}
+		
+	n = atoi(cmd+1);
+	switch(*cmd){
+	case 'B':
+	case 'b':
+		uartsetbaud(p, n);
+		break;
+	case 'D':
+	case 'd':
+		uartdtr(p, n);
+		break;
+	case 'f':
+	case 'F':
+		qflush(p->oq);
+		break;
+	case 'H':
+	case 'h':
+		qhangup(p->iq, 0);
+		qhangup(p->oq, 0);
+		break;
+	case 'L':
+	case 'l':
+		uartbits(p, n);
+		break;
+	case 'm':
+	case 'M':
+		uartmflow(p, n);
+		break;
+	case 'n':
+	case 'N':
+		qnoblock(p->oq, n);
+		break;
+	case 'P':
+	case 'p':
+		uartparity(p, *(cmd+1));
+		break;
+	case 'K':
+	case 'k':
+		uartbreak(p, n);
+		break;
+	case 'R':
+	case 'r':
+		uartrts(p, n);
+		break;
+	case 'Q':
+	case 'q':
+		qsetlimit(p->iq, n);
+		qsetlimit(p->oq, n);
+		break;
+	case 'W':
+	case 'w':
+		/* obsolete */
+		break;
+	case 'X':
+	case 'x':
+		p->xonoff = n;
+		break;
+	case 'Z':
+	case 'z':
+		p->loopback = n;
+		break;
+	}
+}
+
+static long
+uartwrite(Chan *c, void *buf, long n, vlong offset)
+{
+	Uart *p;
+	char cmd[32];
+	int m, inuse;
+
+	USED(offset);
+
+	if(c->qid.type & QTDIR)
+		error(Eperm);
+
+	p = uart[NETID(c->qid.path)];
+
+	switch(NETTYPE(c->qid.path)){
+	case Ndataqid:
+		return qwrite(p->oq, buf, n);
+	case Nctlqid:
+		if(n >= sizeof(cmd))
+			n = sizeof(cmd)-1;
+		memmove(cmd, buf, n);
+		cmd[n] = 0;
+		uartctl(p, cmd);
+		return n;
+	case Ntypeqid:
+		if(p->smc || p->putc)
+			error(Ebadarg);
+		if(n >= sizeof(cmd))
+			n = sizeof(cmd)-1;
+		memmove(cmd, buf, n);
+		cmd[n] = 0;
+		m = strtoul(cmd, nil, 0);
+		inuse = 0;
+		qlock(p);
+		if(p->opens == 0){
+			p->mode = m & 0x7F;
+			p->loopback = (m&0x80)!=0;
+			p->setup = 0;
+		}else
+			inuse = 1;
+		qunlock(p);
+		if(inuse)
+			error(Einuse);
+		return n;
+	}
+}
+
+static long
+uartbwrite(Chan *c, Block *bp, ulong offset)
+{
+	if(c->qid.type & QTDIR || NETTYPE(c->qid.path) != Ndataqid)
+		return devbwrite(c, bp, offset);
+	return qbwrite(uart[NETID(c->qid.path)]->oq, bp);
+}
+
+static int
+uartwstat(Chan *c, uchar *dp, int n)
+{
+	Dir d;
+	Dirtab *dt;
+
+	if(!iseve())
+		error(Eperm);
+	if(c->qid.type & QTDIR)
+		error(Eperm);
+	if(NETTYPE(c->qid.path) == Nstatqid)
+		error(Eperm);
+
+	dt = &uartdir[1+4 * NETID(c->qid.path)];
+	n = convM2D(dp, n, &d, nil);
+	if(d.mode != ~0UL){
+		d.mode &= 0666;
+		dt[0].perm = dt[1].perm = d.mode;
+	}
+	return n;
+}
+
+Dev uartdevtab = {
+	't',
+	"uart",
+
+	uartreset,
+	devinit,
+	devshutdown,
+	uartattach,
+	uartwalk,
+	uartstat,
+	uartopen,
+	devcreate,
+	uartclose,
+	uartread,
+	uartbread,
+	uartwrite,
+	uartbwrite,
+	devremove,
+	uartwstat,
+};
--- /dev/null
+++ b/os/mpc/dsp.c
@@ -1,0 +1,289 @@
+/*
+ * DSP support functions
+ */
+
+#include	"u.h"
+#include	"../port/lib.h"
+#include	"mem.h"
+#include	"dat.h"
+#include	"fns.h"
+#include	"../port/error.h"
+
+#include	"io.h"
+
+#include	"dsp.h"
+
+enum {
+	Ndsp = 2,		/* determined by hardware */
+
+	NHOLES=	64
+};
+
+typedef struct DSPparam DSPparam;
+struct DSPparam {
+	ulong	fdbase;	/* function descriptor table physical base address */
+	ulong	fd_ptr;	/* function descriptor pointer */
+	ulong	dstate;	/* DSP state */
+	ulong	resvd[2];
+	ushort	dstatus;	/* current function descriptor status */
+	ushort	i;	/* number of iterations */
+	ushort	tap;	/* number of TAPs */
+	ushort	cbase;
+	ushort	anon1;	/* sample buffer size-1 */
+	ushort	xptr;	/* pointer to sample */
+	ushort	anon2;	/* output buffer size-1 */
+	ushort	yptr;	/* pointer to output */
+	ushort	m;	/* sample buffer size-1 */
+	ushort	anon3;	/* sample buffer pointer */
+	ushort	n;	/* output buffer size -1 */
+	ushort	anon4;	/* output buffer pointer */
+	ushort	k;	/* coefficient buffer size - 1 */
+	ushort	anon5;	/* coefficient buffer pointer */
+};
+
+struct DSP {
+	Lock;	/* protects state */
+	void	(*done)(void*);
+	void*	arg;
+	DSPparam*	par;
+	CPMdev*	cpm;
+
+	QLock;	/* protects busyr */
+	int	busy;
+	Rendez	busyr;
+};
+
+static	DSP	dsps[Ndsp];
+static	Lock	dsplock;
+static	int	dspinit;
+static struct {
+	QLock;
+	ulong	avail;
+	Rendez	wantr;
+} dspalloc;
+
+static	Map	fndmapv[NHOLES];
+static	RMap	fndmap = {"DSP function descriptors"};
+
+static void
+dspinterrupt(Ureg*, void*)
+{
+	int i;
+	ushort events;
+	DSP *dsp;
+
+	events = m->iomem->sdsr;
+	m->iomem->sdsr = events;
+	if(events & (1<<7))
+		panic("dsp: SDMA channel bus error sdar=#%lux", m->iomem->sdar);
+	for(i=0; i<Ndsp; i++)
+		if(events & (1<<i)){
+			dsp = &dsps[i];
+			if(dsp->busy){
+				dsp->busy = 0;
+				if(dsp->done)
+					dsp->done(dsp->arg);
+				else
+					wakeup(&dsp->busyr);
+			}else
+				print("dsp%d: empty interrupt\n", i);
+		}
+}
+
+/*
+ * called by system initialisation to set up the DSPs
+ */
+void
+dspinitialise(void)
+{
+	CPMdev *d;
+
+	ilock(&dsplock);
+	if(dspinit == 0){
+		mapinit(&fndmap, fndmapv, sizeof(fndmapv));
+		d = cpmdev(CPdsp1);
+		dsps[0].cpm = d;
+		dsps[0].par = d->param;
+		d = cpmdev(CPdsp2);
+		dsps[1].cpm = d;
+		dsps[1].par = d->param;
+		intrenable(VectorCPIC+d->irq, dspinterrupt, nil, BUSUNKNOWN, "dsp");
+		dspalloc.avail = (1<<Ndsp)-1;
+		dspinit = 1;
+	}
+	iunlock(&dsplock);
+}
+
+static int
+dspavail(void*)
+{
+	return dspalloc.avail != 0;
+}
+
+/*
+ * wait for a DSP to become available, and return a reference to it.
+ * if done is not nil, it will be called (with the given arg) when that
+ * DSP completes each function (if set to interrupt).
+ */
+DSP*
+dspacquire(void (*done)(void*), void *arg)
+{
+	DSP *dsp;
+	int i;
+
+	if(dspinit == 0)
+		dspinitialise();
+	qlock(&dspalloc);
+	if(waserror()){
+		qunlock(&dspalloc);
+		nexterror();
+	}
+	for(i=0;; i++){
+		if(i >= Ndsp){
+			sleep(&dspalloc.wantr, dspavail, nil);
+			i = 0;
+		}
+		if(dspalloc.avail & (1<<i))
+			break;
+	}
+	dsp = &dsps[i];
+	if(dsp->busy)
+		panic("dspacquire");
+	dsp->done = done;
+	dsp->arg = arg;
+	poperror();
+	qunlock(&dspalloc);
+	return dsp;
+}
+
+/*
+ * relinquish access to the given DSP
+ */
+void
+dsprelease(DSP *dsp)
+{
+	ulong bit;
+
+	if(dsp == nil)
+		return;
+	bit = 1 << (dsp-dsps);
+	if(dspalloc.avail & bit)
+		panic("dsprelease");
+	dspalloc.avail |= bit;
+	wakeup(&dspalloc.wantr);
+}
+
+/*
+ * execute f[0] to f[n-1] on the given DSP
+ */
+void
+dspexec(DSP *dsp, FnD *f, ulong n)
+{
+	dspsetfn(dsp, f, n);
+	dspstart(dsp);
+}
+
+/*
+ * set the DSP to execute f[0] to f[n-1]
+ */
+void
+dspsetfn(DSP *dsp, FnD *f, ulong n)
+{
+	f[n-1].status |= FnWrap;
+	ilock(dsp);
+	dsp->par->fdbase = PADDR(f);
+	iunlock(dsp);
+	cpmop(dsp->cpm, InitDSP, 0);
+}
+
+/*
+ * start execution of the preset function(s)
+ */
+void
+dspstart(DSP *dsp)
+{
+	ilock(dsp);
+	dsp->busy = 1;
+	iunlock(dsp);
+	cpmop(dsp->cpm, StartDSP, 0);
+}
+
+static int
+dspdone(void *a)
+{
+	return ((DSP*)a)->busy;
+}
+
+/*
+ * wait until the DSP has completed execution
+ */
+void
+dspsleep(DSP *dsp)
+{
+	sleep(&dsp->busyr, dspdone, dsp);
+}
+
+/*
+ * allocate n function descriptors
+ */
+FnD*
+fndalloc(ulong n)
+{
+	ulong a, nb, pgn;
+	FnD *f;
+
+	if(n == 0)
+		return nil;
+	if(dspinit == 0)
+		dspinitialise();
+	nb = n*sizeof(FnD);
+	while((a = rmapalloc(&fndmap, 0, nb, sizeof(FnD))) != 0){
+		/* expected to loop just once, but might lose a race with another dsp user */
+		pgn = (nb+BY2PG-1)&~(BY2PG-1);
+		a = PADDR(xspanalloc(pgn, sizeof(FnD), 0));
+		if(a == 0)
+			return nil;
+		mapfree(&fndmap, a, pgn);
+	}
+	f = KADDR(a);
+	f[n-1].status = FnWrap;
+	return f;
+}
+
+/*
+ * free n function descriptors
+ */
+void
+fndfree(FnD *f, ulong n)
+{
+	if(f != nil)
+		mapfree(&fndmap, PADDR(f), n*sizeof(FnD));
+}
+
+/*
+ * allocate an IO buffer region in shared memory for use by the DSP
+ */
+void*
+dspmalloc(ulong n)
+{
+	ulong i;
+
+	n = (n+3)&~4;
+	i = n;
+	if(n & (n-1)){
+		/* align on a power of two */
+		for(i=1; i < n; i <<= 1)
+			;
+	}
+	return cpmalloc(n, i);	/* this seems to be what 16.3.3.2 is trying to say */
+}
+
+/*
+ * free DSP buffer memory
+ */
+void
+dspfree(void *p, ulong n)
+{
+	if(p != nil)
+		cpmfree(p, (n+3)&~4);
+}
--- /dev/null
+++ b/os/mpc/dsp.h
@@ -1,0 +1,62 @@
+/*
+ * MPC82x/QUICC DSP support
+ */
+
+typedef struct DSP DSP;
+typedef struct FnD FnD;
+
+typedef short Real;
+typedef struct Complex Complex;
+
+struct Complex {
+	Real	im;
+	Real	re;
+};
+
+struct FnD {
+	ushort	status;
+	ushort	param[7];
+};
+
+enum {
+	FnDsize =	8*2,	/* each function descriptor is 8 shorts */
+
+	/* standard bits in FnD.status */
+	FnStop = 	1<<15,
+	FnWrap =	1<<13,
+	FnInt =	1<<12,
+
+	/* optional bits */
+	FnZ =	1<<11,	/* FIR[35], MOD */
+	FnIALL =	1<<10,	/* FIRx */
+	FnXinc0 =	0<<8,	/* FIRx, IRR */
+	FnXinc1 =	1<<8,
+	FnXinc2 =	2<<8,
+	FnXinc3 =	3<<8,
+	FnPC =	1<<7,	/* FIRx */
+
+
+	/* DSP functions (table 16-6) */
+	FnFIR1 =	0x01,
+	FnFIR2 =	0x02,
+	FnFIR3 =	0x03,
+	FnFIR5 = 	0x03,
+	FnFIR6 =	0x06,
+	FnIIR =	0x07,
+	FnMOD =	0x08,
+	FnDEMOD = 0x09,
+	FnLMS1 =	0x0A,
+	FnLMS2 =	0x0B,
+	FnWADD = 0x0C,
+};
+
+void	dspinitialise(void);
+DSP*	dspacquire(void (*)(void*), void*);
+void	dspexec(DSP*, FnD*, ulong);
+void*	dspmalloc(ulong);
+void	dspfree(void*, ulong);
+void	dspsetfn(DSP*, FnD*, ulong);
+void	dspstart(DSP*);
+void	dsprelease(DSP*);
+FnD*	fndalloc(ulong);
+void	fndfree(FnD*, ulong);
--- /dev/null
+++ b/os/mpc/etherif.h
@@ -1,0 +1,37 @@
+enum {
+	MaxEther	= 6,
+	Ntypes		= 8,
+};
+
+typedef struct Ether Ether;
+struct Ether {
+RWlock;	/* TO DO */
+	ISAConf;			/* hardware info */
+	int	ctlrno;
+	int	tbdf;			/* type+busno+devno+funcno */
+	int	minmtu;
+	int	maxmtu;
+	uchar	ea[Eaddrlen];
+	int	encry;
+
+	void	(*attach)(Ether*);	/* filled in by reset routine */
+	void	(*closed)(Ether*);
+	void	(*detach)(Ether*);
+	void	(*transmit)(Ether*);
+	void	(*interrupt)(Ureg*, void*);
+	long	(*ifstat)(Ether*, void*, long, ulong);
+	long	(*ctl)(Ether*, void*, long); /* custom ctl messages */
+	void	(*power)(Ether*, int);	/* power on/off */
+	void	(*shutdown)(Ether*);	/* shutdown hardware before reboot */
+	void	*ctlr;
+	int	pcmslot;		/* PCMCIA */
+	int	fullduplex;	/* non-zero if full duplex */
+
+	Queue*	oq;
+
+	Netif;
+};
+
+extern Block* etheriq(Ether*, Block*, int);
+extern void addethercard(char*, int(*)(Ether*));
+extern int archether(int, Ether*);
--- /dev/null
+++ b/os/mpc/etherscc.c
@@ -1,0 +1,528 @@
+/*
+ * SCCn ethernet
+ */
+
+#include "u.h"
+#include "lib.h"
+#include "mem.h"
+#include "dat.h"
+#include "fns.h"
+#include "io.h"
+#include "../port/error.h"
+#include "../port/netif.h"
+
+#include "etherif.h"
+
+enum {
+	Nrdre		= 16,	/* receive descriptor ring entries */
+	Ntdre		= 16,	/* transmit descriptor ring entries */
+
+	Rbsize		= ETHERMAXTU+4,		/* ring buffer size (+4 for CRC) */
+	Bufsize		= (Rbsize+7)&~7,	/* aligned */
+};
+
+enum {
+	/* ether-specific Rx BD bits */
+	RxMiss=		1<<8,
+	RxeLG=		1<<5,
+	RxeNO=		1<<4,
+	RxeSH=		1<<3,
+	RxeCR=		1<<2,
+	RxeOV=		1<<1,
+	RxeCL=		1<<0,
+	RxError=		(RxeLG|RxeNO|RxeSH|RxeCR|RxeOV|RxeCL),	/* various error flags */
+
+	/* ether-specific Tx BD bits */
+	TxPad=		1<<14,	/* pad short frames */
+	TxTC=		1<<10,	/* transmit CRC */
+	TxeDEF=		1<<9,
+	TxeHB=		1<<8,
+	TxeLC=		1<<7,
+	TxeRL=		1<<6,
+	TxeUN=		1<<1,
+	TxeCSL=		1<<0,
+
+	/* scce */
+	RXB=	1<<0,
+	TXB=	1<<1,
+	BSY=		1<<2,
+	RXF=		1<<3,
+	TXE=		1<<4,
+
+	/* psmr */
+	PRO=	1<<9,	/* promiscuous mode */
+
+	/* gsmrl */
+	ENR=	1<<5,
+	ENT=	1<<4,
+
+	/* port A */
+	RXD1=	SIBIT(15),
+	TXD1=	SIBIT(14),
+
+	/* port B */
+	RTS1=	IBIT(19),
+
+	/* port C */
+	CTS1=	SIBIT(11),
+	CD1=	SIBIT(10),
+};
+
+typedef struct Etherparam Etherparam;
+struct Etherparam {
+	SCCparam;
+	ulong	c_pres;		/* preset CRC */
+	ulong	c_mask;		/* constant mask for CRC */
+	ulong	crcec;		/* CRC error counter */
+	ulong	alec;		/* alighnment error counter */
+	ulong	disfc;		/* discard frame counter */
+	ushort	pads;		/* short frame PAD characters */
+	ushort	ret_lim;	/* retry limit threshold */
+	ushort	ret_cnt;	/* retry limit counter */
+	ushort	mflr;		/* maximum frame length reg */
+	ushort	minflr;		/* minimum frame length reg */
+	ushort	maxd1;		/* maximum DMA1 length reg */
+	ushort	maxd2;		/* maximum DMA2 length reg */
+	ushort	maxd;		/* rx max DMA */
+	ushort	dma_cnt;	/* rx dma counter */
+	ushort	max_b;		/* max bd byte count */
+	ushort	gaddr[4];		/* group address filter */
+	ulong	tbuf0_data0;	/* save area 0 - current frm */
+	ulong	tbuf0_data1;	/* save area 1 - current frm */
+	ulong	tbuf0_rba0;
+	ulong	tbuf0_crc;
+	ushort	tbuf0_bcnt;
+	ushort	paddr[3];	/* physical address LSB to MSB increasing */
+	ushort	p_per;		/* persistence */
+	ushort	rfbd_ptr;	/* rx first bd pointer */
+	ushort	tfbd_ptr;	/* tx first bd pointer */
+	ushort	tlbd_ptr;	/* tx last bd pointer */
+	ulong	tbuf1_data0;	/* save area 0 - next frame */
+	ulong	tbuf1_data1;	/* save area 1 - next frame */
+	ulong	tbuf1_rba0;
+	ulong	tbuf1_crc;
+	ushort	tbuf1_bcnt;
+	ushort	tx_len;		/* tx frame length counter */
+	ushort	iaddr[4];		/* individual address filter*/
+	ushort	boff_cnt;	/* back-off counter */
+	ushort	taddr[3];	/* temp address */
+};
+
+typedef struct {
+	Lock;
+	int	port;
+	int	init;
+	int	active;
+	SCC*	scc;
+	CPMdev*	cpm;
+
+	Ring;
+
+	ulong	interrupts;			/* statistics */
+	ulong	deferred;
+	ulong	heartbeat;
+	ulong	latecoll;
+	ulong	retrylim;
+	ulong	underrun;
+	ulong	overrun;
+	ulong	carrierlost;
+	ulong	retrycount;
+} Ctlr;
+
+static	int	sccid[] = {-1, CPscc1, CPscc2, CPscc3, CPscc4};
+
+static void
+attach(Ether *ether)
+{
+	Ctlr *ctlr;
+
+	ctlr = ether->ctlr;
+	ctlr->active = 1;
+	ctlr->scc->gsmrl |= ENR|ENT;
+	eieio();
+}
+
+static void
+closed(Ether *ether)
+{
+	Ctlr *ctlr;
+
+	ctlr = ether->ctlr;
+	if(ctlr->active){
+		sccxstop(ctlr->cpm);
+		ilock(ctlr);
+		ctlr->active = 0;
+		iunlock(ctlr);
+	}
+}
+
+static void
+promiscuous(void* arg, int on)
+{
+	Ether *ether;
+	Ctlr *ctlr;
+
+	ether = (Ether*)arg;
+	ctlr = ether->ctlr;
+
+	ilock(ctlr);
+	if(on || ether->nmaddr)
+		ctlr->scc->psmr |= PRO;
+	else
+		ctlr->scc->psmr &= ~PRO;
+	iunlock(ctlr);
+}
+
+static void
+multicast(void* arg, uchar *addr, int on)
+{
+	Ether *ether;
+	Ctlr *ctlr;
+
+	USED(addr, on);	/* if on, could SetGroupAddress; if !on, it's hard */
+
+	ether = (Ether*)arg;
+	ctlr = ether->ctlr;
+
+	ilock(ctlr);
+	if(ether->prom || ether->nmaddr)
+		ctlr->scc->psmr |= PRO;
+	else
+		ctlr->scc->psmr &= ~PRO;
+	iunlock(ctlr);
+}
+
+static void
+txstart(Ether *ether)
+{
+	int len;
+	Ctlr *ctlr;
+	Block *b;
+	BD *dre;
+
+	ctlr = ether->ctlr;
+	while(ctlr->ntq < Ntdre-1){
+		b = qget(ether->oq);
+		if(b == 0)
+			break;
+
+		dre = &ctlr->tdr[ctlr->tdrh];
+		if(dre->status & BDReady)
+			panic("ether: txstart");
+	
+		/*
+		 * Give ownership of the descriptor to the chip, increment the
+		 * software ring descriptor pointer and tell the chip to poll.
+		 */
+		len = BLEN(b);
+		dcflush(b->rp, len);
+		if(ctlr->txb[ctlr->tdrh] != nil)
+			panic("scc/ether: txstart");
+		ctlr->txb[ctlr->tdrh] = b;
+		if((ulong)b->rp&1)
+			panic("scc/ether: txstart align");	/* TO DO: ensure alignment */
+		dre->addr = PADDR(b->rp);
+		dre->length = len;
+		eieio();
+		dre->status = (dre->status & BDWrap) | BDReady|TxPad|BDInt|BDLast|TxTC;
+		eieio();
+		ctlr->scc->todr = 1<<15;	/* transmit now */
+		eieio();
+		ctlr->ntq++;
+		ctlr->tdrh = NEXT(ctlr->tdrh, Ntdre);
+	}
+}
+
+static void
+transmit(Ether* ether)
+{
+	Ctlr *ctlr;
+
+	ctlr = ether->ctlr;
+	ilock(ctlr);
+	txstart(ether);
+	iunlock(ctlr);
+}
+
+static void
+interrupt(Ureg*, void *arg)
+{
+	Ether *ether;
+	int len, events, status;
+	Ctlr *ctlr;
+	BD *dre;
+	Block *b;
+
+	ether = arg;
+	ctlr = ether->ctlr;
+	if(!ctlr->active)
+		return;	/* not ours */
+
+	/*
+	 * Acknowledge all interrupts and whine about those that shouldn't
+	 * happen.
+	 */
+	events = ctlr->scc->scce;
+	eieio();
+	ctlr->scc->scce = events;
+	eieio();
+	ctlr->interrupts++;
+
+	if(events & (TXE|BSY|RXB)){
+		if(events & RXB)
+			ctlr->overrun++;
+		if(events & TXE)
+			ether->oerrs++;
+		if(0 || events & TXE)
+			print("ETHER.SCC#%d: scce = 0x%uX\n", ether->ctlrno, events);
+	}
+	/*
+	 * Receiver interrupt: run round the descriptor ring logging
+	 * errors and passing valid receive data up to the higher levels
+	 * until we encounter a descriptor still owned by the chip.
+	 */
+	if(events & (RXF|RXB) || 1){
+		dre = &ctlr->rdr[ctlr->rdrx];
+		while(((status = dre->status) & BDEmpty) == 0){
+			if(status & RxError || (status & (BDFirst|BDLast)) != (BDFirst|BDLast)){
+				if(status & (RxeLG|RxeSH))
+					ether->buffs++;
+				if(status & RxeNO)
+					ether->frames++;
+				if(status & RxeCR)
+					ether->crcs++;
+				if(status & RxeOV)
+					ether->overflows++;
+				//print("eth rx: %ux\n", status);
+			}
+			else{
+				/*
+				 * We have a packet. Read it in.
+				 */
+				len = dre->length-4;
+				if((b = iallocb(len)) != 0){
+					dcinval(KADDR(dre->addr), len);
+					memmove(b->wp, KADDR(dre->addr), len);
+					b->wp += len;
+					etheriq(ether, b, 1);
+				}else
+					ether->soverflows++;
+			}
+
+			/*
+			 * Finished with this descriptor, reinitialise it,
+			 * give it back to the chip, then on to the next...
+			 */
+			dre->length = 0;
+			dre->status = (status & BDWrap) | BDEmpty | BDInt;
+			eieio();
+
+			ctlr->rdrx = NEXT(ctlr->rdrx, Nrdre);
+			dre = &ctlr->rdr[ctlr->rdrx];
+		}
+	}
+
+	/*
+	 * Transmitter interrupt: handle anything queued for a free descriptor.
+	 */
+	if(events & TXB){
+		lock(ctlr);
+		while(ctlr->ntq){
+			dre = &ctlr->tdr[ctlr->tdri];
+			status = dre->status;
+			if(status & BDReady)
+				break;
+			if(status & TxeDEF)
+				ctlr->deferred++;
+			if(status & TxeHB)
+				ctlr->heartbeat++;
+			if(status & TxeLC)
+				ctlr->latecoll++;
+			if(status & TxeRL)
+				ctlr->retrylim++;
+			if(status & TxeUN)
+				ctlr->underrun++;
+			if(status & TxeCSL)
+				ctlr->carrierlost++;
+			ctlr->retrycount += (status>>2)&0xF;
+			b = ctlr->txb[ctlr->tdri];
+			if(b == nil)
+				panic("scce/interrupt: bufp");
+			ctlr->txb[ctlr->tdri] = nil;
+			freeb(b);
+			ctlr->ntq--;
+			ctlr->tdri = NEXT(ctlr->tdri, Ntdre);
+		}
+		txstart(ether);
+		unlock(ctlr);
+	}
+	if(events & TXE)
+		cpmop(ctlr->cpm, RestartTx, 0);
+}
+
+static long
+ifstat(Ether* ether, void* a, long n, ulong offset)
+{
+	char *p;
+	int len;
+	Ctlr *ctlr;
+
+	if(n == 0)
+		return 0;
+
+	ctlr = ether->ctlr;
+
+	p = malloc(READSTR);
+	len = snprint(p, READSTR, "interrupts: %lud\n", ctlr->interrupts);
+	len += snprint(p+len, READSTR-len, "carrierlost: %lud\n", ctlr->carrierlost);
+	len += snprint(p+len, READSTR-len, "heartbeat: %lud\n", ctlr->heartbeat);
+	len += snprint(p+len, READSTR-len, "retrylimit: %lud\n", ctlr->retrylim);
+	len += snprint(p+len, READSTR-len, "retrycount: %lud\n", ctlr->retrycount);
+	len += snprint(p+len, READSTR-len, "latecollisions: %lud\n", ctlr->latecoll);
+	len += snprint(p+len, READSTR-len, "rxoverruns: %lud\n", ctlr->overrun);
+	len += snprint(p+len, READSTR-len, "txunderruns: %lud\n", ctlr->underrun);
+	snprint(p+len, READSTR-len, "framesdeferred: %lud\n", ctlr->deferred);
+	n = readstr(offset, a, n, p);
+	free(p);
+
+	return n;
+}
+
+/*
+ * This follows the MPC823 user guide: section16.9.23.7's initialisation sequence,
+ * except that it sets the right bits for the MPC823ADS board when SCC2 is used,
+ * and those for the 860/821 development board for SCC1.
+ */
+static void
+sccsetup(Ctlr *ctlr, SCC *scc, Ether *ether)
+{
+	int i, rcs, tcs, w;
+	Etherparam *p;
+	IMM *io;
+
+
+	i = 2*(ctlr->port-1);
+	io = ioplock();
+	w = (TXD1|RXD1)<<i;	/* TXDn and RXDn in port A */
+	io->papar |= w;	/* enable TXDn and RXDn pins */
+	io->padir &= ~w;
+	io->paodr &= ~w;	/* not open drain */
+
+	w = (CD1|CTS1)<<i;	/* CLSN and RENA: CDn and CTSn in port C */
+	io->pcpar &= ~w;	/* enable CLSN (CTSn) and RENA (CDn) */
+	io->pcdir &= ~w;
+	io->pcso |= w;
+	iopunlock();
+
+	/* clocks and transceiver control: details depend on the board's wiring */
+	archetherenable(sccid[ctlr->port], &rcs, &tcs, ether->mbps, ether->fullduplex);
+
+	sccnmsi(ctlr->port, rcs, tcs);	/* connect the clocks */
+
+	p = ctlr->cpm->param;
+	memset(p, 0, sizeof(*p));
+	p->rfcr = 0x18;
+	p->tfcr = 0x18;
+	p->mrblr = Bufsize;
+	p->rbase = PADDR(ctlr->rdr);
+	p->tbase = PADDR(ctlr->tdr);
+
+	cpmop(ctlr->cpm, InitRxTx, 0);
+
+	p->c_pres = ~0;
+	p->c_mask = 0xDEBB20E3;
+	p->crcec = 0;
+	p->alec = 0;
+	p->disfc = 0;
+	p->pads = 0x8888;
+	p->ret_lim = 0xF;
+	p->mflr = Rbsize;
+	p->minflr = ETHERMINTU+4;
+	p->maxd1 = Bufsize;
+	p->maxd2 = Bufsize;
+	p->p_per = 0;	/* only moderate aggression */
+
+	for(i=0; i<Eaddrlen; i+=2)
+		p->paddr[2-i/2] = (ether->ea[i+1]<<8)|ether->ea[i];	/* it's not the obvious byte order */
+
+	scc->psmr = (2<<10)|(5<<1);	/* 32-bit CRC, ignore 22 bits before SFD */
+	scc->dsr = 0xd555;
+	scc->gsmrh = 0;	/* normal operation */
+	scc->gsmrl = (1<<28)|(4<<21)|(1<<19)|0xC;	/* transmit clock invert, 48 bit preamble, repetitive 10 preamble, ethernet */
+	eieio();
+	scc->scce = ~0;	/* clear all events */
+	eieio();
+	scc->sccm = TXE | RXF | TXB;	/* enable interrupts */
+	eieio();
+
+	io = ioplock();
+	w = RTS1<<(ctlr->port-1);	/* enable TENA pin (RTSn) */
+	io->pbpar |= w;
+	io->pbdir |= w;
+	iopunlock();
+
+	/* gsmrl enable is deferred until attach */
+}
+
+static int
+reset(Ether* ether)
+{
+	uchar ea[Eaddrlen];
+	CPMdev *cpm;
+	Ctlr *ctlr;
+	SCC *scc;
+
+	if(m->speed < 24){
+		print("%s ether: system speed must be >= 24MHz for ether use\n", ether->type);
+		return -1;
+	}
+
+	if(!(ether->port >= 1 && ether->port <= 4)){
+		print("%s ether: no SCC port %lud\n", ether->type, ether->port);
+		return -1;
+	}
+
+	/*
+	 * Insist that the platform-specific code provide the Ethernet address
+	 */
+	memset(ea, 0, Eaddrlen);
+	if(memcmp(ea, ether->ea, Eaddrlen) == 0){
+		print("no ether address");
+		return -1;
+	}
+
+	cpm = cpmdev(sccid[ether->port]);
+	if(cpm == nil)
+		return -1;
+	ether->irq = VectorCPIC + cpm->irq;
+	scc = cpm->regs;
+	ctlr = malloc(sizeof(*ctlr));
+	ether->ctlr = ctlr;
+	memset(ctlr, 0, sizeof(*ctlr));
+	ctlr->cpm = cpm;
+	ctlr->scc = scc;
+	ctlr->port = ether->port;
+
+	if(ioringinit(ctlr, Nrdre, Ntdre, Bufsize) < 0)
+		panic("etherscc init");
+
+	sccsetup(ctlr, scc, ether);
+
+	ether->attach = attach;
+	ether->closed = closed;
+	ether->transmit = transmit;
+	ether->interrupt = interrupt;
+	ether->ifstat = ifstat;
+
+	ether->arg = ether;
+	ether->promiscuous = promiscuous;
+	ether->multicast = multicast;
+
+	return 0;
+}
+
+void
+etherscclink(void)
+{
+	addethercard("SCC", reset);
+}
--- /dev/null
+++ b/os/mpc/faultpower.c
@@ -1,0 +1,49 @@
+#include	"u.h"
+#include	"../port/lib.h"
+#include	"mem.h"
+#include	"dat.h"
+#include	"fns.h"
+#include	"ureg.h"
+#include	"io.h"
+
+enum
+{
+	MC_IFETCH	= (1<<30),
+	MC_STORE	= (1<<11),	/* bit 23 if X-form, bit 3 if D-form => write */
+	DSI_STORE	= (1<<25),
+	DSI_PROT		= (1<<27),
+};
+
+void
+faultpower(Ureg *ur)
+{
+	ulong addr;
+	char buf[ERRMAX];
+	int read, i;
+
+	addr = ur->pc;			/* assume instr. exception */
+	read = 1;
+	i = ur->cause >> 8;
+	if(i == CDSI || i == CDTLBE || i == CMCHECK && (ur->status&MC_IFETCH) == 0) {	/* data access error including machine check load/store */
+		addr = getdar();
+		if(getdsisr() & (DSI_STORE|MC_STORE))
+			read = 0;
+	} else if(i == CDMISS)	/* DTLB miss */
+		addr = getdepn() & ~0x3FF;	/* can't distinguish read/write, but Inferno doesn't care */
+/*
+print("fault %lux %lux %lux %d\n", ur->pc, ur->cause, addr, read);
+print("imiss %lux dmiss %lux hash1 %lux dcmp %lux hash2 %lux\n",
+	getimiss(), getdmiss(), gethash1(), getdcmp(), gethash2());
+print("up %lux %lux %lux\n", m->upage, m->upage->virt, m->upage->phys);
+*/
+
+	up->dbgreg = ur;		/* For remote ACID */
+
+	spllo();
+	sprint(buf, "trap: fault %s pc=0x%lux addr=0x%lux",
+			read ? "read" : "write", ur->pc, addr);
+	if(up->type == Interp)
+		disfault(ur, buf);
+	dumpregs(ur);
+	panic("fault: %s\n", buf);
+}
--- /dev/null
+++ b/os/mpc/fp.s
@@ -1,0 +1,205 @@
+/*
+ * support for floating-point hardware
+ */
+
+#include "mem.h"
+
+/* on some models mtmsr doesn't synchronise enough (eg, 603e) */
+#define	MSRSYNC	SYNC; ISYNC
+
+#define	FPON(X, Y)\
+	MOVW	MSR, X;\
+	OR	$FPE, X, Y;\
+	SYNC;\
+	ISYNC;\
+	MOVW	Y, MSR;\
+	MSRSYNC
+
+#define	FPOFF(X,Y)\
+	MOVW	MSR, X;\
+	RLWNM	$0, X, $~FPE, Y;\
+	SYNC;\
+	ISYNC;\
+	MOVW	Y, MSR;\
+	MSRSYNC
+
+#define	FPPREV(X)\
+	SYNC;\
+	ISYNC;\
+	MOVW	X, MSR;\
+	MSRSYNC
+
+TEXT	kfpinit(SB), $0
+	MOVFL	$0,FPSCR(7)
+	MOVFL	$0xD,FPSCR(6)	/* VE, OE, ZE */
+	MOVFL	$0, FPSCR(5)
+	MOVFL	$0, FPSCR(3)
+	MOVFL	$0, FPSCR(2)
+	MOVFL	$0, FPSCR(1)
+	MOVFL	$0, FPSCR(0)
+
+	FMOVD	$4503601774854144.0, F27
+	FMOVD	$0.5, F29
+	FSUB	F29, F29, F28
+	FADD	F29, F29, F30
+	FADD	F30, F30, F31
+	FMOVD	F28, F0
+	FMOVD	F28, F1
+	FMOVD	F28, F2
+	FMOVD	F28, F3
+	FMOVD	F28, F4
+	FMOVD	F28, F5
+	FMOVD	F28, F6
+	FMOVD	F28, F7
+	FMOVD	F28, F8
+	FMOVD	F28, F9
+	FMOVD	F28, F10
+	FMOVD	F28, F11
+	FMOVD	F28, F12
+	FMOVD	F28, F13
+	FMOVD	F28, F14
+	FMOVD	F28, F15
+	FMOVD	F28, F16
+	FMOVD	F28, F17
+	FMOVD	F28, F18
+	FMOVD	F28, F19
+	FMOVD	F28, F20
+	FMOVD	F28, F21
+	FMOVD	F28, F22
+	FMOVD	F28, F23
+	FMOVD	F28, F24
+	FMOVD	F28, F25
+	FMOVD	F28, F26
+	RETURN
+
+TEXT	getfpscr(SB), $8
+	FPON(R4, R5)
+	MOVFL	FPSCR, F3
+	FMOVD	F3, -8(SP)
+	MOVW	-4(SP), R3
+	FPPREV(R4)
+	RETURN
+
+TEXT	fpsave(SB), $0
+	FPON(R4, R4)
+
+	FMOVD	F0,0(R3)
+	FMOVD	F1,8(R3)
+	FMOVD	F2,16(R3)
+	FMOVD	F3,24(R3)
+	FMOVD	F4,32(R3)
+	FMOVD	F5,40(R3)
+	FMOVD	F6,48(R3)
+	FMOVD	F7,56(R3)
+	FMOVD	F8,64(R3)
+	FMOVD	F9,72(R3)
+	FMOVD	F10,80(R3)
+	FMOVD	F11,88(R3)
+	FMOVD	F12,96(R3)
+	FMOVD	F13,104(R3)
+	FMOVD	F14,112(R3)
+	FMOVD	F15,120(R3)
+	FMOVD	F16,128(R3)
+	FMOVD	F17,136(R3)
+	FMOVD	F18,144(R3)
+	FMOVD	F19,152(R3)
+	FMOVD	F20,160(R3)
+	FMOVD	F21,168(R3)
+	FMOVD	F22,176(R3)
+	FMOVD	F23,184(R3)
+	FMOVD	F24,192(R3)
+	FMOVD	F25,200(R3)
+	FMOVD	F26,208(R3)
+	FMOVD	F27,216(R3)
+	FMOVD	F28,224(R3)
+	FMOVD	F29,232(R3)
+	FMOVD	F30,240(R3)
+	FMOVD	F31,248(R3)
+
+	MOVFL	FPSCR, F0
+	FMOVD	F0, 256(R3)
+	MOVFL	$0,FPSCR(7)
+	MOVFL	$0xD,FPSCR(6)	/* VE, OE, ZE */
+	MOVFL	$0, FPSCR(5)
+	MOVFL	$0, FPSCR(4)
+	MOVFL	$0, FPSCR(3)
+	MOVFL	$0, FPSCR(2)
+	MOVFL	$0, FPSCR(1)
+	MOVFL	$0, FPSCR(0)
+
+	FPOFF(R4, R4)
+	RETURN
+
+TEXT	fprestore(SB), $0
+	FPON(R4, R4)
+
+	FMOVD	256(R3), F0
+	MOVFL	F0, FPSCR
+	FMOVD	0(R3), F0
+	FMOVD	8(R3), F1
+	FMOVD	16(R3), F2
+	FMOVD	24(R3), F3
+	FMOVD	32(R3), F4
+	FMOVD	40(R3), F5
+	FMOVD	48(R3), F6
+	FMOVD	56(R3), F7
+	FMOVD	64(R3), F8
+	FMOVD	72(R3), F9
+	FMOVD	80(R3), F10
+	FMOVD	88(R3), F11
+	FMOVD	96(R3), F12
+	FMOVD	104(R3), F13
+	FMOVD	112(R3), F14
+	FMOVD	120(R3), F15
+	FMOVD	128(R3), F16
+	FMOVD	136(R3), F17
+	FMOVD	144(R3), F18
+	FMOVD	152(R3), F19
+	FMOVD	160(R3), F20
+	FMOVD	168(R3), F21
+	FMOVD	176(R3), F22
+	FMOVD	184(R3), F23
+	FMOVD	192(R3), F24
+	FMOVD	200(R3), F25
+	FMOVD	208(R3), F26
+	FMOVD	216(R3), F27
+	FMOVD	224(R3), F28
+	FMOVD	232(R3), F29
+	FMOVD	240(R3), F30
+	FMOVD	248(R3), F31
+
+	RETURN
+
+TEXT	clrfptrap(SB), $0
+	FPON(R4, R5)
+	MOVFL	$0, FPSCR(5)
+	MOVFL	$0, FPSCR(3)
+	MOVFL	$0, FPSCR(2)
+	MOVFL	$0, FPSCR(1)
+	MOVFL	$0, FPSCR(0)
+	FPPREV(R4)
+	RETURN
+
+TEXT	fpinit(SB), $0
+	FPON(R4, R5)
+	BL	kfpinit(SB)
+	RETURN
+
+TEXT	fpoff(SB), $0
+	FPOFF(R4, R5)
+	RETURN
+
+
+TEXT	FPsave(SB), 1, $0
+	FPON(R4, R5)
+	MOVFL	FPSCR, F0
+	FMOVD	F0, 0(R3)
+	FPPREV(R4)
+	RETURN
+
+TEXT	FPrestore(SB), 1, $0
+	FPON(R4, R5)
+	FMOVD	0(R3), F0
+	MOVFL	F0, FPSCR
+	FPPREV(R4)
+	RETURN
--- /dev/null
+++ b/os/mpc/fpi.h
@@ -1,0 +1,61 @@
+typedef long Word;
+typedef unsigned long Single;
+typedef struct {
+	unsigned long h;
+	unsigned long l;
+} Double;
+
+enum {
+	FractBits	= 28,
+	CarryBit	= 0x10000000,
+	HiddenBit	= 0x08000000,
+	MsBit		= HiddenBit,
+	NGuardBits	= 3,
+	GuardMask	= 0x07,
+	LsBit		= (1<<NGuardBits),
+
+	SingleExpBias	= 127,
+	SingleExpMax	= 255,
+	DoubleExpBias	= 1023,
+	DoubleExpMax	= 2047,
+
+	ExpBias		= DoubleExpBias,
+	ExpInfinity	= DoubleExpMax,
+};
+
+typedef struct {
+	unsigned char s;
+	short e;
+	long l;				/* 0000FFFFFFFFFFFFFFFFFFFFFFFFFGGG */
+	long h;				/* 0000HFFFFFFFFFFFFFFFFFFFFFFFFFFF */
+} Internal;
+
+#define IsWeird(n)	((n)->e >= ExpInfinity)
+#define	IsInfinity(n)	(IsWeird(n) && (n)->h == HiddenBit && (n)->l == 0)
+#define	SetInfinity(n)	((n)->e = ExpInfinity, (n)->h = HiddenBit, (n)->l = 0)
+#define IsNaN(n)	(IsWeird(n) && (((n)->h & ~HiddenBit) || (n)->l))
+#define	SetQNaN(n)	((n)->s = 0, (n)->e = ExpInfinity, 		\
+			 (n)->h = HiddenBit|(LsBit<<1), (n)->l = 0)
+#define IsZero(n)	((n)->e == 1 && (n)->h == 0 && (n)->l == 0)
+#define SetZero(n)	((n)->e = 1, (n)->h = 0, (n)->l = 0)
+
+/*
+ * fpi.c
+ */
+extern void fpiround(Internal *);
+extern void fpiadd(Internal *, Internal *, Internal *);
+extern void fpisub(Internal *, Internal *, Internal *);
+extern void fpimul(Internal *, Internal *, Internal *);
+extern void fpidiv(Internal *, Internal *, Internal *);
+extern int fpicmp(Internal *, Internal *);
+extern void fpinormalise(Internal*);
+
+/*
+ * fpimem.c
+ */
+extern void fpis2i(Internal *, void *);
+extern void fpid2i(Internal *, void *);
+extern void fpiw2i(Internal *, void *);
+extern void fpii2s(void *, Internal *);
+extern void fpii2d(void *, Internal *);
+extern void fpii2w(Word *, Internal *);
--- /dev/null
+++ b/os/mpc/fpipower.c
@@ -1,0 +1,970 @@
+/*
+ * this doesn't attempt to implement Power architecture floating-point properties
+ * that aren't visible in the Inferno environment.
+ * all arithmetic is done in double precision.
+ * the FP trap status isn't updated.
+ */
+#include "u.h"
+#include	"../port/lib.h"
+#include	"mem.h"
+#include	"dat.h"
+#include	"fns.h"
+#include	"io.h"
+
+#include	"ureg.h"
+
+#include	"fpi.h"
+
+#define	REG(x) (*(long*)(((char*)em->ur)+roff[(x)]))
+#define	FR(x) (*(Internal*)em->fr[(x)&0x1F])
+#define	REGSP	1	/* stack pointer */
+
+/* BUG: check fetch (not worthwhile in Inferno) */
+#define	getulong(a) (*(ulong*)(a))
+
+enum {
+	CRLT = 1<<31,
+	CRGT = 1<<30,
+	CREQ = 1<<29,
+	CRSO = 1<<28,
+	CRFU = CRSO,
+
+	CRFX = 1<<27,
+	CRFEX = 1<<26,
+	CRVX = 1<<25,
+	CROX = 1<<24,
+};
+
+#define getCR(x,w) (((w)>>(28-(x*4)))&0xF)
+#define mkCR(x,v) (((v)&0xF)<<(28-(x*4)))
+
+#define simm(xx, ii)	xx = (short)(ii&0xFFFF);
+#define getairr(i)	rd = (i>>21)&0x1f; ra = (i>>16)&0x1f; simm(imm,i)
+#define getarrr(i)	rd = (i>>21)&0x1f; ra = (i>>16)&0x1f; rb = (i>>11)&0x1f;
+#define getop(i) ((i>>26)&0x3F)
+#define getxo(i) ((i>>1)&0x3FF)
+
+#define	FPS_FX	(1<<31)	/* exception summary (sticky) */
+#define	FPS_EX	(1<<30)	/* enabled exception summary */
+#define	FPS_VX	(1<<29)	/* invalid operation exception summary */
+#define	FPS_OX	(1<<28)	/* overflow exception OX (sticky) */
+#define	FPS_UX	(1<<27)	/* underflow exception UX (sticky) */
+#define	FPS_ZX	(1<<26)	/* zero divide exception ZX (sticky) */
+#define	FPS_XX	(1<<25)	/* inexact exception XX (sticky) */
+#define	FPS_VXSNAN (1<<24)	/* invalid operation exception for SNaN (sticky) */
+#define	FPS_VXISI	(1<<23)	/* invalid operation exception for ∞-∞ (sticky) */
+#define	FPS_VXIDI	(1<<22)	/* invalid operation exception for ∞/∞ (sticky) */
+#define	FPS_VXZDZ (1<<21)	/* invalid operation exception for 0/0 (sticky) */
+#define	FPS_VXIMZ	(1<<20)	/* invalid operation exception for ∞*0 (sticky) */
+#define	FPS_VXVC	(1<<19)	/* invalid operation exception for invalid compare (sticky) */
+#define	FPS_FR	(1<<18)	/* fraction rounded */
+#define	FPS_FI	(1<<17)	/* fraction inexact */
+#define	FPS_FPRF	(1<<16)	/* floating point result class */
+#define	FPS_FPCC	(0xF<<12)	/* <, >, =, unordered */
+#define	FPS_VXCVI	(1<<8)	/* enable exception for invalid integer convert (sticky) */
+#define	FPS_VE	(1<<7)	/* invalid operation exception enable */
+#define	FPS_OE	(1<<6)	/* enable overflow exceptions */
+#define	FPS_UE	(1<<5)	/* enable underflow */
+#define	FPS_ZE	(1<<4)	/* enable zero divide */
+#define	FPS_XE	(1<<3)	/* enable inexact exceptions */
+#define	FPS_RN	(3<<0)	/* rounding mode */
+
+typedef struct Emreg Emreg;
+
+struct Emreg {
+	Ureg*	ur;
+	ulong	(*fr)[3];
+	FPenv*	ufp;
+	ulong	ir;
+	char*	name;
+};
+
+int	fpemudebug = 0;
+
+#undef OFR
+#define	OFR(X)	((ulong)&((Ureg*)0)->X)
+
+static	int	roff[] = {
+	OFR(r0), OFR(r1), OFR(r2), OFR(r3),
+	OFR(r4), OFR(r5), OFR(r6), OFR(r7),
+	OFR(r8), OFR(r9), OFR(r10), OFR(r11),
+	OFR(r12), OFR(r13), OFR(r14), OFR(r15),
+	OFR(r16), OFR(r17), OFR(r18), OFR(r19),
+	OFR(r20), OFR(r21), OFR(r22), OFR(r23),
+	OFR(r24), OFR(r25), OFR(r26), OFR(r27),
+	OFR(r28), OFR(r29), OFR(r30), OFR(r31),
+};
+
+/*
+ * initial FP register values assumed by qc's code
+ */
+static Internal fpreginit[] = {
+	/* s, e, l, h */
+	{0, 0x400, 0x00000000, 0x08000000},	/* F31=2.0 */
+	{0, 0x3FF, 0x00000000, 0x08000000},	/* F30=1.0 */
+	{0, 0x3FE, 0x00000000, 0x08000000},	/* F29=0.5 */
+	{0, 0x1, 0x00000000, 0x00000000}, /* F28=0.0 */
+	{0, 0x433, 0x00000000, 0x08000040},	/* F27=FREGCVI */
+};
+
+static void
+fadd(Emreg *em, Internal *d, int ra, int rb)
+{
+	Internal a, b;
+
+	a = FR(ra);
+	b = FR(rb);
+	(a.s == b.s? fpiadd: fpisub)(&b, &a, d);
+}
+
+static void
+fsub(Emreg *em, Internal *d, int ra, int rb)
+{
+	Internal a, b;
+
+	a = FR(ra);
+	b = FR(rb);
+	b.s ^= 1;
+	(b.s == a.s? fpiadd: fpisub)(&b, &a, d);
+}
+
+static void
+fmul(Emreg *em, Internal *d, int ra, int rb)
+{
+	Internal a, b;
+
+	a = FR(ra);
+	b = FR(rb);
+	fpimul(&b, &a, d);
+}
+
+static void
+fdiv(Emreg *em, Internal *d, int ra, int rb)
+{
+	Internal a, b;
+
+	a = FR(ra);
+	b = FR(rb);
+	fpidiv(&b, &a, d);
+}
+
+static void
+fmsub(Emreg *em, Internal *d, int ra, int rc, int rb)
+{
+	Internal a, c, b, t;
+
+	a = FR(ra);
+	c = FR(rc);
+	b = FR(rb);
+	fpimul(&a, &c, &t);
+	b.s ^= 1;
+	(b.s == t.s? fpiadd: fpisub)(&b, &t, d);
+}
+
+static void
+fmadd(Emreg *em, Internal *d, int ra, int rc, int rb)
+{
+	Internal a, c, b, t;
+
+	a = FR(ra);
+	c = FR(rc);
+	b = FR(rb);
+	fpimul(&a, &c, &t);
+	(t.s == b.s? fpiadd: fpisub)(&b, &t, d);
+}
+
+static ulong	setfpscr(Emreg*);
+static void	setfpcc(Emreg*, int);
+
+static void
+unimp(Emreg *em, ulong op)
+{
+	char buf[60];
+
+	snprint(buf, sizeof(buf), "sys: fp: pc=%lux unimp fp 0x%.8lux", em->ur->pc, op);
+	if(fpemudebug)
+		print("FPE: %s\n", buf);
+	error(buf);
+	/* no return */
+}
+
+/*
+ * floating load/store
+ */
+
+static void
+fpeairr(Emreg *em, ulong ir, void **eap, int *rdp)
+{
+	ulong ea;
+	long imm;
+	int ra, rd, upd;
+
+	getairr(ir);
+	ea = imm;
+	upd = (ir&(1L<<26))!=0;
+	if(ra) {
+		ea += REG(ra);
+		if(upd){
+			if(ra == REGSP)
+				panic("fpemu: r1 update");	/* can't do it because we're running on the same stack */
+			REG(ra) = ea;
+		}
+	} else {
+		if(upd)
+			unimp(em, ir);
+	}
+	*rdp = rd;
+	*eap = (void*)ea;
+	if(fpemudebug)
+		print("%8.8lux %s\tf%d,%ld(r%d) ea=%lux upd=%d\n", em->ur->pc, em->name, rd, imm, ra, ea, upd);
+}
+
+static void
+fpearrr(Emreg *em, ulong ir, int upd, void **eap, int *rdp)
+{
+	ulong ea;
+	int ra, rb, rd;
+
+	getarrr(ir);
+	ea = REG(rb);
+	if(ra){
+		ea += REG(ra);
+		if(upd){
+			if(ra == REGSP)
+				panic("fpemu: r1 update");
+			REG(ra) = ea;
+		}
+		if(fpemudebug)
+			print("%8.8lux %s\tf%d,(r%d+r%d) ea=%lux upd=%d\n", em->ur->pc, em->name, rd, ra, rb, ea, upd);
+	} else {
+		if(upd)
+			unimp(em, ir);
+		if(fpemudebug)
+			print("%8.8lux %s\tf%d,(r%d) ea=%lux\n", em->ur->pc, em->name, rd, rb, ea);
+	}
+	*eap = (void*)ea;
+	*rdp = rd;
+}
+
+static void
+lfs(Emreg *em, ulong ir)
+{
+	void *ea;
+	int rd;
+
+	em->name = "lfs";
+	fpeairr(em, ir, &ea, &rd);
+	fpis2i(&FR(rd), (void*)ea);
+}
+
+static void
+lfsx(Emreg *em, ulong ir)
+{
+	void *ea;
+	int rd;
+
+	em->name = "lfsx";
+	fpearrr(em, ir, ((ir>>1)&0x3FF)==567, &ea, &rd);
+	fpis2i(&FR(rd), (void*)ea);
+}
+
+static void
+lfd(Emreg *em, ulong ir)
+{
+	void *ea;
+	int rd;
+
+	em->name = "lfd";
+	fpeairr(em, ir, &ea, &rd);
+	fpid2i(&FR(rd), (void*)ea);
+}
+
+static void
+lfdx(Emreg *em, ulong ir)
+{
+	void *ea;
+	int rd;
+
+	em->name = "lfdx";
+	fpearrr(em, ir, ((ir>>1)&0x3FF)==631, &ea, &rd);
+	fpid2i(&FR(rd), (void*)ea);
+}
+
+static void
+stfs(Emreg *em, ulong ir)
+{
+	void *ea;
+	int rd;
+	Internal tmp;
+
+	em->name = "stfs";
+	fpeairr(em, ir, &ea, &rd);
+	tmp = FR(rd);
+	fpii2s(ea, &tmp);
+}
+
+static void
+stfsx(Emreg *em, ulong ir)
+{
+	void *ea;
+	int rd;
+	Internal tmp;
+
+	em->name = "stfsx";
+	fpearrr(em, ir, getxo(ir)==695, &ea, &rd);
+	tmp = FR(rd);
+	fpii2s(ea, &tmp);
+}
+
+static void
+stfd(Emreg *em, ulong ir)
+{
+	void *ea;
+	int rd;
+	Internal tmp;
+
+	em->name = "stfd";
+	fpeairr(em, ir, &ea, &rd);
+	tmp = FR(rd);
+	fpii2d(ea, &tmp);
+}
+
+static void
+stfdx(Emreg *em, ulong ir)
+{
+	void *ea;
+	int rd;
+	Internal tmp;
+
+	em->name = "stfdx";
+	fpearrr(em, ir, ((ir>>1)&0x3FF)==759, &ea, &rd);
+	tmp = FR(rd);
+	fpii2d(ea, &tmp);
+}
+
+static void
+mcrfs(Emreg *em, ulong ir)
+{
+	int rd, ra, rb;
+	static ulong fpscr0[] ={
+		FPS_FX|FPS_OX,
+		FPS_UX|FPS_ZX|FPS_XX|FPS_VXSNAN,
+		FPS_VXISI|FPS_VXIDI|FPS_VXZDZ|FPS_VXIMZ,
+		FPS_VXVC,
+		0,
+		FPS_VXCVI,
+	};
+
+	getarrr(ir);
+	if(rb || ra&3 || rd&3)
+		unimp(em, ir);
+	ra >>= 2;
+	rd >>= 2;
+	em->ur->cr = (em->ur->cr & ~mkCR(rd, 0xF)) | mkCR(rd, getCR(ra, em->ufp->fpscr));
+	em->ufp->fpscr &= ~fpscr0[ra];
+	if(fpemudebug)
+		print("%8.8lux mcrfs\tcrf%d,crf%d\n", em->ur->pc, rd, ra);
+}
+
+static void
+mffs(Emreg *em, ulong ir)
+{
+	int rd, ra, rb;
+	Double dw;
+
+	getarrr(ir);
+	if(ra || rb)
+		unimp(em, ir);
+	dw.h = 0;
+	dw.l = ((uvlong)0xFFF8000L<<16)|em->ufp->fpscr;
+	fpid2i(&FR(rd), &dw);
+	/* it's anyone's guess how CR1 should be set when ir&1 */
+	em->ur->cr &= ~mkCR(1, 0xE);	/* leave SO, reset others */
+	if(fpemudebug)
+		print("%8.8lux mffs%s\tfr%d\n", em->ur->pc, ir&1?".":"", rd);
+}
+
+static void
+mtfsb1(Emreg *em, ulong ir)
+{
+	int rd, ra, rb;
+
+	getarrr(ir);
+	if(ra || rb)
+		unimp(em, ir);
+	em->ufp->fpscr |= (1L << (31-rd));
+	/* BUG: should set summary bits */
+	if(ir & 1)
+		em->ur->cr &= ~mkCR(1, 0xE);	/* BUG: manual unclear: leave SO, reset others? */
+	if(fpemudebug)
+		print("%8.8lux mtfsb1%s\tfr%d\n", em->ur->pc, ir&1?".":"", rd);
+}
+
+static void
+mtfsb0(Emreg *em, ulong ir)
+{
+	int rd, ra, rb;
+
+	getarrr(ir);
+	if(ra || rb)
+		unimp(em, ir);
+	em->ufp->fpscr &= ~(1L << (31-rd));
+	if(ir & 1)
+		em->ur->cr &= ~mkCR(1, 0xE);		/* BUG: manual unclear: leave SO, reset others? */
+	if(fpemudebug)
+		print("%8.8lux mtfsb0%s\tfr%d\n", em->ur->pc, ir&1?".":"", rd);
+}
+
+static void
+mtfsf(Emreg *em, ulong ir)
+{
+	int fm, rb, i;
+	ulong v;
+	Internal b;
+	Double db;
+
+	if(ir & ((1L << 25)|(1L << 16)))
+		unimp(em, ir);
+	rb = (ir >> 11) & 0x1F;
+	fm = (ir >> 17) & 0xFF;
+	b = FR(rb);
+	fpii2d(&db, &b);	/* reconstruct hi/lo format to recover low word */
+	v = db.l;
+	for(i=0; i<8; i++)
+		if(fm & (1 << (7-i)))
+			em->ufp->fpscr = (em->ufp->fpscr & ~mkCR(i, 0xF)) | mkCR(i, getCR(i, v));
+	/* BUG: should set FEX and VX `according to the usual rule' */
+	if(ir & 1)
+		em->ur->cr &= ~mkCR(1, 0xE);		/* BUG: manual unclear: leave SO, reset others? */
+	if(fpemudebug)
+		print("%8.8lux mtfsf%s\t#%.2x,fr%d\n", em->ur->pc, ir&1?".":"", fm, rb);
+}
+
+static void
+mtfsfi(Emreg *em, ulong ir)
+{
+	int imm, rd;
+
+	if(ir & ((0x7F << 16)|(1L << 11)))
+		unimp(em, ir);
+	rd = (ir >> 23) & 0xF;
+	imm = (ir >> 12) & 0xF;
+	em->ufp->fpscr = (em->ufp->fpscr & ~mkCR(rd, 0xF)) | mkCR(rd, imm);
+	/* BUG: should set FEX and VX `according to the usual rule' */
+	if(ir & 1)
+		em->ur->cr &= ~mkCR(1, 0xE);		/* BUG: manual unclear: leave SO, reset others? */
+	if(fpemudebug)
+		print("%8.8lux mtfsfi%s\tcrf%d,#%x\n", em->ur->pc, ir&1?".":"", rd, imm);
+}
+
+static void
+fcmp(Emreg *em, ulong ir)
+{
+	int fc, rd, ra, rb, sig, i;
+
+	getarrr(ir);
+	if(rd & 3)
+		unimp(em, ir);
+	rd >>= 2;
+	sig = 0;
+	switch(getxo(ir)) {
+	default:
+		unimp(em, ir);
+	case 32:
+		if(fpemudebug)
+			print("%8.8lux fcmpo\tcr%d,f%d,f%d\n", em->ur->pc, rd, ra, rb);
+		sig = 1;
+		break;
+	case 0:
+		if(fpemudebug)
+			print("%8.8lux fcmpu\tcr%d,f%d,f%d\n", em->ur->pc, rd, ra, rb);
+		break;
+	}
+	if(IsWeird(&FR(ra)) || IsWeird(&FR(rb))) {
+		if(sig){
+			;	/* BUG: should trap if not masked ... */
+		}
+		fc = CRFU;
+	} else {
+		i = fpicmp(&FR(ra), &FR(rb));
+		if(i > 0)
+			fc = CRGT;
+		else if(i == 0)
+			fc = CREQ;
+		else
+			fc = CRLT;
+	}
+	fc >>= 28;
+	em->ur->cr = (em->ur->cr & ~mkCR(rd,~0)) | mkCR(rd, fc);
+	em->ufp->fpscr = (em->ufp->fpscr & ~0xF800) | (fc<<11);
+	/* BUG: update FX, VXSNAN, VXVC */
+}
+
+static void
+fariths(Emreg *em, ulong ir)
+{
+	int rd, ra, rb, rc, fmt;
+	char *cc, *n;
+	ulong fpscr;
+	Internal *d;
+
+	fmt = 0;
+	rc = (ir>>6)&0x1F;
+	getarrr(ir);
+	d = &FR(rd);
+	switch(getxo(ir)&0x1F) {	/* partial XO decode */
+	case 22:	/* fsqrts */
+	case 24:	/* fres */
+	default:
+		unimp(em, ir);
+		return;
+	case 18:
+		if(IsZero(&FR(rb))) {
+			em->ufp->fpscr |= FPS_ZX | FPS_FX;
+			error("sys: fp: zero divide");
+		}
+		fdiv(em, d, ra, rb);
+		n = "fdivs";
+		break;
+	case 20:
+		fsub(em, d, ra, rb);
+		n = "fsubs";
+		break;
+	case 21:
+		fadd(em, d, ra, rb);
+		n = "fadds";
+		break;
+	case 25:
+		fmul(em, d, ra, rc);
+		rb = rc;
+		n = "fmuls";
+		break;
+	case 28:
+		fmsub(em, d, ra, rc, rb);
+		fmt = 2;
+		n = "fmsubs";
+		break;
+	case 29:
+		fmadd(em, d, ra, rc, rb);
+		fmt = 2;
+		n = "fmadds";
+		break;
+	case 30:
+		fmsub(em, d, ra, rc, rb);
+		d->s ^= 1;
+		fmt = 2;
+		n = "fnmsubs";
+		break;
+	case 31:
+		fmadd(em, d, ra, rc, rb);
+		d->s ^= 1;
+		fmt = 2;
+		n = "fnmadds";
+		break;
+	}
+	if(fmt==1 && ra)
+		unimp(em, ir);
+	fpscr = setfpscr(em);
+	setfpcc(em, rd);
+	cc = "";
+	if(ir & 1) {
+		cc = ".";
+		em->ur->cr = (em->ur->cr & ~mkCR(1, ~0)) | mkCR(1, (fpscr>>28));
+	}
+	if(fpemudebug) {
+		switch(fmt) {
+		case 0:
+			print("%8.8lux %s%s\tfr%d,fr%d,fr%d\n", em->ur->pc, n, cc, rd, ra, rb);
+			break;
+		case 1:
+			print("%8.8lux %s%s\tfr%d,fr%d\n", em->ur->pc, n, cc, rd, rb);
+			break;
+		case 2:
+			print("%8.8lux %s%s\tfr%d,fr%d,fr%d,fr%d\n", em->ur->pc, n, cc, rd, ra, rc, rb);
+			break;
+		}
+	}
+}
+
+static void
+farith(Emreg *em, ulong ir)
+{
+	Word w;
+	Double dv;
+	int rd, ra, rb, rc, fmt;
+	char *cc, *n;
+	ulong fpscr;
+	int nocc;
+	Internal *d;
+
+	fmt = 0;
+	nocc = 0;
+	rc = (ir>>6)&0x1F;
+	getarrr(ir);
+	d = &FR(rd);
+	switch(getxo(ir)&0x1F) { /* partial XO decode */
+	case 22:	/* frsqrt */
+	case 23:	/* fsel */
+	case 26:	/* fsqrte */
+	default:
+		unimp(em, ir);
+		return;
+	case 12:	/* frsp */
+		*d = FR(rb);	/* BUG: doesn't round to single precision */
+		fmt = 1;
+		n = "frsp";
+		break;
+	case 14:	/* fctiw */	/* BUG: ignores rounding mode */
+	case 15:	/* fctiwz */
+		fpii2w(&w, &FR(rb));
+		dv.h = 0;
+		dv.l = w;
+		fpid2i(d, &dv);
+		fmt = 1;
+		nocc = 1;
+		n = "fctiw";
+		break;
+	case 18:
+		if(IsZero(&FR(rb))) {
+			em->ufp->fpscr |= FPS_ZX | FPS_FX;
+			error("sys: fp: zero divide");
+		}
+		fdiv(em, d, ra, rb);
+		n = "fdiv";
+		break;
+	case 20:
+		fsub(em, d, ra, rb);
+		n = "fsub";
+		break;
+	case 21:
+		fadd(em, d, ra, rb);
+		n = "fadd";
+		break;
+	case 25:
+		fmul(em, d, ra, rc);
+		rb = rc;
+		n = "fmul";
+		break;
+	case 28:
+		fmsub(em, d, ra, rc, rb);
+		fmt = 2;
+		n = "fmsub";
+		break;
+	case 29:
+		fmadd(em, d, ra, rc, rb);
+		fmt = 2;
+		n = "fmadd";
+		break;
+	case 30:
+		fmsub(em, d, ra, rc, rb);
+		d->s ^= 1;
+		fmt = 2;
+		n = "fnmsub";
+		break;
+	case 31:
+		fmadd(em, d, ra, rc, rb);
+		d->s ^= 1;
+		fmt = 2;
+		n = "fnmadd";
+		break;
+	}
+	if(fmt==1 && ra)
+		unimp(em, ir);
+	fpscr = setfpscr(em);
+	if(nocc == 0)
+		setfpcc(em, rd);
+	cc = "";
+	if(ir & 1) {
+		cc = ".";
+		em->ur->cr = (em->ur->cr & ~mkCR(1, ~0)) | mkCR(1, (fpscr>>28));
+	}
+	if(fpemudebug) {
+		switch(fmt) {
+		case 0:
+			print("%8.8lux %s%s\tfr%d,fr%d,fr%d\n", em->ur->pc, n, cc, rd, ra, rb);
+			break;
+		case 1:
+			print("%8.8lux %s%s\tfr%d,fr%d\n", em->ur->pc, n, cc, rd, rb);
+			break;
+		case 2:
+			print("%8.8lux %s%s\tfr%d,fr%d,fr%d,fr%d\n", em->ur->pc, n, cc, rd, ra, rc, rb);
+			break;
+		}
+	}
+}
+
+static void
+farith2(Emreg *em, ulong ir)
+{
+	int rd, ra, rb;
+	char *cc, *n;
+	ulong fpscr;
+	Internal *d, *b;
+
+	getarrr(ir);
+	if(ra)
+		unimp(em, ir);
+	d = &FR(rd);
+	b = &FR(rb);
+	switch(getxo(ir)) { /* full XO decode */
+	default:
+		unimp(em, ir);
+	case 40:
+		*d = *b;
+		d->s ^= 1;
+		n = "fneg";
+		break;
+	case 72:
+		*d = *b;
+		n = "fmr";
+		break;
+	case 136:
+		*d = *b;
+		d->s = 1;
+		n = "fnabs";
+		break;
+	case 264:
+		*d = *b;
+		d->s = 0;
+		n = "fabs";
+		break;
+	}
+	fpscr = setfpscr(em);
+	setfpcc(em, rd);
+	cc = "";
+	if(ir & 1) {
+		cc = ".";
+		em->ur->cr = (em->ur->cr & ~mkCR(1, ~0)) | mkCR(1, (fpscr>>28));
+	}
+	if(fpemudebug)
+		print("%8.8lux %s%s\tfr%d,fr%d\n", em->ur->pc, n, cc, rd, rb);
+}
+
+static ulong
+setfpscr(Emreg *em)
+{
+	ulong fps, fpscr;
+
+	fps = 0;	/* BUG: getfsr() */
+	fpscr = em->ufp->fpscr;
+	if(fps & FPAOVFL)
+		fpscr |= FPS_OX;
+	if(fps & FPAINEX)
+		fpscr |= FPS_XX;
+	if(fps & FPAUNFL)
+		fpscr |= FPS_UX;
+	if(fps & FPAZDIV)
+		fpscr |= FPS_ZX;
+	if(fpscr != em->ufp->fpscr) {
+		fpscr |= FPS_FX;
+		em->ufp->fpscr = fpscr;
+	}
+	return fpscr;
+}
+
+static void
+setfpcc(Emreg *em, int r)
+{
+	int c;
+	Internal *d;
+
+	d = &FR(r);
+	c = 0;
+	if(IsZero(d))
+		c |= 2;
+	else if(d->s == 1)
+		c |= 4;
+	else
+		c |= 8;
+	if(IsNaN(d))
+		c |= 1;
+	em->ufp->fpscr = (em->ufp->fpscr & ~0xF800) | (0<<15) | (c<<11); /* unsure about class bit */
+}
+
+static	uchar	op63flag[32] = {
+[12] 1, [14] 1, [15] 1, [18] 1, [20] 1, [21] 1, [22] 1,
+[23] 1, [25] 1, [26] 1, [28] 1, [29] 1, [30] 1, [31] 1,
+};
+
+/*
+ * returns the number of FP instructions emulated
+ */
+int
+fpipower(Ureg *ur)
+{
+	ulong op;
+	int xo;
+	Emreg emreg, *em;
+	FPenv *ufp;
+	int n;
+
+	ufp = &up->env->fpu;	/* because all the state is in Osenv, it need not be saved/restored */
+	em = &emreg;
+	em->ur = ur;
+	em->fr = ufp->emreg;
+	em->ufp = ufp;
+	em->name = nil;
+	if(em->ufp->fpistate != FPACTIVE) {
+		em->ufp->fpistate = FPACTIVE;
+		em->ufp->fpscr = 0;	/* TO DO */
+		for(n = 0; n < nelem(fpreginit); n++)
+			FR(31-n) = fpreginit[n];
+	}
+	for(n=0;;n++){
+		op = getulong(ur->pc);
+		em->ir = op;
+		if(fpemudebug > 1)
+			print("%8.8lux %8.8lux: ", ur->pc, op);
+		switch(op>>26){
+		default:
+			return n;
+		case 48:	/* lfs */
+		case 49:	/* lfsu */
+			lfs(em, op);
+			break;
+		case 50:	/* lfd */
+		case 51:	/* lfdu */
+			lfd(em, op);
+			break;
+		case 52:	/* stfs */
+		case 53:	/* stfsu */
+			stfs(em, op);
+			break;
+		case 54:	/* stfd */
+		case 55:	/* stfdu */
+			stfd(em, op);
+			break;
+		case 31:	/* indexed load/store */
+			xo = getxo(op);
+			if((xo & 0x300) != 0x200)
+				return n;
+			switch(xo){
+			default:
+				return n;
+			case 535:	/* lfsx */
+			case 567:	/* lfsux */
+				lfsx(em, op);
+				break;
+			case 599:	/* lfdx */
+			case 631:	/* lfdux */
+				lfdx(em, op);
+				break;
+			case 663:	/* stfsx */
+			case 695:	/* stfsux */
+				stfsx(em, op);
+				break;
+			case 727:	/* stfdx */
+			case 759:	/* stfdux */
+				stfdx(em, op);
+				break;
+			}
+			break;
+		case 63:	/* double precision */
+			xo = getxo(op);
+			if(op63flag[xo & 0x1F]){
+				farith(em, op);
+				break;
+			}
+			switch(xo){
+			default:
+				return n;
+			case 0:	/* fcmpu */
+			case 32:	/* fcmpo */
+				fcmp(em, op);
+				break;
+			case 40:	/* fneg */
+			case 72:	/* fmr */
+			case 136:	/* fnabs */
+			case 264:	/* fabs */
+				farith2(em, op);
+				break;
+			case 38:
+				mtfsb1(em, op);
+				break;
+			case 64:
+				mcrfs(em, op);
+				break;
+			case 70:
+				mtfsb0(em, op);
+				break;
+			case 134:
+				mtfsfi(em, op);
+				break;
+			case 583:
+				mffs(em, op);
+				break;
+			case 711:
+				mtfsf(em, op);
+				break;
+			}
+			break;
+		case 59:	/* single precision */
+			fariths(em, op);
+			break;
+		}
+		ur->pc += 4;
+		if(anyhigher())
+			sched();
+	}
+	return n;
+}
+
+/*
+50:	lfd	frD,d(rA)
+51:	lfdu	frD,d(rA)
+31,631:	lfdux	frD,rA,rB
+31,599:	lfdx	frD,rA,rB
+48:	lfs	frD,d(rA)
+49:	lfsu	frD,d(rA)
+31,567:	lfsux	frD,rA,rB
+31,535:	lfsx	frD,rA,rB
+
+54:	stfd	frS,d(rA)
+55:	stfdu	frS,d(rA)
+31,759:	stfdux	frS,rA,rB
+31,727:	stfdx	frS,rA,rB
+52:	stfs	frS,d(rA)
+53:	stfsu	frS,d(rA)
+31,695:	stfsux	frS,rA,rB
+31,663:	stfsx	frS,rA,rB
+
+63,64:	mcrfs	crfD,crfS
+63,583:	mffs[.]	frD
+63,70:	mtfsb0[.]	crbD
+63,38:	mtfsb1[.]	crbD
+63,711:	mtfsf[.]	FM,frB
+63,134:	mtfsfi[.]	crfD,IMM
+*/
+
+/*
+float to int:
+	FMOVD	g+0(SB),F1
+	FCTIWZ	F1,F4
+	FMOVD	F4,.rathole+0(SB)
+	MOVW	.rathole+4(SB),R7
+	MOVW	R7,l+0(SB)
+*/
+
+/*
+int to float:
+	MOVW	$1127219200,R9
+	MOVW	l+0(SB),R7
+	MOVW	R9,.rathole+0(SB)
+	XOR	$-2147483648,R7,R6
+	MOVW	R6,.rathole+4(SB)
+	FMOVD	.rathole+0(SB),F0
+	FSUB	F27,F0
+
+unsigned to float:
+	MOVW	ul+0(SB),R5
+	MOVW	R9,.rathole+0(SB)
+	XOR	$-2147483648,R5,R4
+	MOVW	R4,.rathole+4(SB)
+	FMOVD	.rathole+0(SB),F3
+	FSUB	F27,F3
+	FCMPU	F3,F28
+	BGE	,3(PC)
+	FMOVD	$4.29496729600000000e+09,F2
+	FADD	F2,F3
+	FMOVD	F3,g+0(SB)
+*/
--- /dev/null
+++ b/os/mpc/i2c.c
@@ -1,0 +1,439 @@
+#include "u.h"
+#include "../port/lib.h"
+#include "mem.h"
+#include "dat.h"
+#include "fns.h"
+#include "io.h"
+#include "../port/error.h"
+
+/*
+ * basic read/write interface to mpc8xx I2C bus (master mode)
+ */
+
+typedef struct Ctlr Ctlr;
+typedef struct I2C I2C;
+
+struct I2C {
+	uchar	i2mod;
+	uchar	rsv12a[3];
+	uchar	i2add;
+	uchar	rsv12b[3];
+	uchar	i2brg;
+	uchar	rsv12c[3];
+	uchar	i2com;
+	uchar	rsv12d[3];
+	uchar	i2cer;
+	uchar	rsv12e[3];
+	uchar	i2cmr;
+};
+
+enum {
+	/* i2c-specific BD flags */
+	RxeOV=		1<<1,	/* overrun */
+	TxS=			1<<10,	/* transmit start condition */
+	TxeNAK=		1<<2,	/* last transmitted byte not acknowledged */
+	TxeUN=		1<<1,	/* underflow */
+	TxeCL=		1<<0,	/* collision */
+	TxERR=		(TxeNAK|TxeUN|TxeCL),
+
+	/* i2cmod */
+	REVD=	1<<5,	/* =1, LSB first */
+	GCD=	1<<4,	/* =1, general call address disabled */
+	FLT=		1<<3,	/* =0, not filtered; =1, filtered */
+	PDIV=	3<<1,	/* predivisor field */
+	EN=		1<<0,	/* enable */
+
+	/* i2com */
+	STR=		1<<7,	/* start transmit */
+	I2CM=	1<<0,	/* master */
+	I2CS=	0<<0,	/* slave */
+
+	/* i2cer */
+	TXE =	1<<4,
+	BSY =	1<<2,
+	TXB =	1<<1,
+	RXB =	1<<0,
+
+	/* port B bits */
+	I2CSDA =	IBIT(27),
+	I2CSCL = IBIT(26),
+
+	Rbit =	1<<0,	/* bit in address byte denoting read */
+
+	/* maximum I2C I/O (can change) */
+	MaxIO =	128,
+	MaxSA =	2,	/* longest subaddress */
+	Bufsize =	(MaxIO+MaxSA+1+4)&~3,	/* extra space for subaddress/clock bytes and alignment */
+	Freq =	100000,
+	I2CTimeout = 250,	/* msec */
+
+	Chatty = 0,
+};
+
+#define	DPRINT	if(Chatty)print
+
+/* data cache needn't be flushed if buffers allocated in uncached PHYSIMM */
+#define	DCFLUSH(a,n)
+
+/*
+ * I2C software structures
+ */
+
+struct Ctlr {
+	Lock;
+	QLock	io;
+	int	init;
+	int	busywait;	/* running before system set up */
+	I2C*	i2c;
+	IOCparam*	sp;
+
+	BD*	rd;
+	BD*	td;
+	int	phase;
+	Rendez	r;
+	char*	addr;
+	char*	txbuf;
+	char*	rxbuf;
+};
+
+static	Ctlr	i2ctlr[1];
+
+static	void	interrupt(Ureg*, void*);
+
+static void
+enable(void)
+{
+	I2C *i2c;
+
+	i2c = i2ctlr->i2c;
+	i2c->i2cer = ~0;	/* clear events */
+	eieio();
+	i2c->i2mod |= EN;
+	eieio();
+	i2c->i2cmr = TXE|BSY|TXB|RXB;	/* enable all interrupts */
+	eieio();
+}
+
+static void
+disable(void)
+{
+	I2C *i2c;
+
+	i2c = i2ctlr->i2c;
+	i2c->i2cmr = 0;	/* mask all interrupts */
+	i2c->i2mod &= ~EN;
+}
+
+/*
+ * called by the reset routine of any driver using the I2C
+ */
+void
+i2csetup(int busywait)
+{
+	IMM *io;
+	I2C *i2c;
+	IOCparam *sp;
+	CPMdev *cpm;
+	Ctlr *ctlr;
+	long f, e, emin;
+	int p, d, dmax;
+
+	ctlr = i2ctlr;
+	ctlr->busywait = busywait;
+	if(ctlr->init)
+		return;
+	print("i2c setup...\n");
+	ctlr->init = 1;
+	cpm = cpmdev(CPi2c);
+	i2c = cpm->regs;
+	ctlr->i2c = i2c;
+	sp = cpm->param;
+	if(sp == nil)
+		panic("I2C: can't allocate new parameter memory\n");
+	ctlr->sp = sp;
+	disable();
+
+	if(ctlr->txbuf == nil){
+		ctlr->txbuf = cpmalloc(Bufsize, 2);
+		ctlr->addr = ctlr->txbuf+MaxIO;
+	}
+	if(ctlr->rxbuf == nil)
+		ctlr->rxbuf = cpmalloc(Bufsize, 2);
+	if(ctlr->rd == nil){
+		ctlr->rd = bdalloc(1);
+		ctlr->rd->addr = PADDR(ctlr->rxbuf);
+		ctlr->rd->length = 0;
+		ctlr->rd->status = BDWrap;
+	}
+	if(ctlr->td == nil){
+		ctlr->td = bdalloc(2);
+		ctlr->td->addr = PADDR(ctlr->txbuf);
+		ctlr->td->length = 0;
+		ctlr->td->status = BDWrap|BDLast;
+	}
+
+	/* select port pins */
+	io = ioplock();
+	io->pbdir |= I2CSDA | I2CSCL;
+	io->pbodr |= I2CSDA | I2CSCL;
+	io->pbpar |= I2CSDA | I2CSCL;
+	iopunlock();
+
+	/* explicitly initialise parameters, because InitRxTx can't be used (see i2c/spi relocation errata) */
+	sp = ctlr->sp;
+	sp->rbase = PADDR(ctlr->rd);
+	sp->tbase = PADDR(ctlr->td);
+	sp->rfcr = 0x18;
+	sp->tfcr = 0x18;
+	sp->mrblr = Bufsize;
+	sp->rstate = 0;
+	sp->rptr = 0;
+	sp->rbptr = sp->rbase;
+	sp->rcnt = 0;
+	sp->tstate = 0;
+	sp->tbptr = sp->tbase;
+	sp->tptr = 0;
+	sp->tcnt = 0;
+	eieio();
+
+	i2c->i2com = I2CM;
+	i2c->i2mod = 0;	/* normal mode */
+	i2c->i2add = 0;
+
+	emin = Freq;
+	dmax = (m->cpuhz/Freq)/2-3;
+	for(d=0; d < dmax; d++){
+		for(p=3; p>=0; p--){
+			f = (m->cpuhz>>(p+2))/(2*(d+3));
+			e = Freq - f;
+			if(e < 0)
+				e = -e;
+			if(e < emin){
+				emin = e;
+				i2c->i2brg = d;
+				i2c->i2mod = (i2c->i2mod&~PDIV)|((3-p)<<1); /* set PDIV */
+			}
+		}
+	}
+	//print("i2brg=%d i2mod=#%2.2ux\n", i2c->i2brg, i2c->i2mod);
+	intrenable(VectorCPIC+cpm->irq, interrupt, i2ctlr, BUSUNKNOWN, "i2c");
+}
+
+enum {
+	Idling,
+	Done,
+	Busy,
+		Sending,
+		Recving,
+};
+
+static void
+interrupt(Ureg*, void *arg)
+{
+	int events;
+	Ctlr *ctlr;
+	I2C *i2c;
+
+	ctlr = arg;
+	i2c = ctlr->i2c;
+	events = i2c->i2cer;
+	eieio();
+	i2c->i2cer = events;
+	if(events & (BSY|TXE)){
+		//print("I2C#%x\n", events);
+		if(ctlr->phase != Idling){
+			ctlr->phase = Idling;
+			wakeup(&ctlr->r);
+		}
+	}else{
+		if(events & TXB){
+			//print("i2c: xmt %d %4.4ux %4.4ux\n", ctlr->phase, ctlr->td->status, ctlr->td[1].status);
+			if(ctlr->phase == Sending){
+				ctlr->phase = Done;
+				wakeup(&ctlr->r);
+			}
+		}
+		if(events & RXB){
+			//print("i2c: rcv %d %4.4ux %d\n", ctlr->phase, ctlr->rd->status, ctlr->rd->length);
+			if(ctlr->phase == Recving){
+				ctlr->phase = Done;
+				wakeup(&ctlr->r);
+			}
+		}
+	}
+}
+
+static int
+done(void *a)
+{
+	return ((Ctlr*)a)->phase < Busy;
+}
+
+static void
+i2cwait(Ctlr *ctlr)
+{
+	int i;
+
+	if(up == nil || ctlr->busywait){
+		for(i=0; i < 5 && !done(ctlr); i++){
+			delay(2);
+			interrupt(nil, ctlr);
+		}
+	}else
+		tsleep(&ctlr->r, done, ctlr, I2CTimeout);
+}
+
+static int
+i2cerror(char *s)
+{
+	if(up)
+		error(s);
+	/* no current process, don't call error */
+	DPRINT("i2c error: %s\n", s);
+	return -1;
+}
+
+long
+i2csend(I2Cdev *d, void *buf, long n, ulong offset)
+{
+	Ctlr *ctlr;
+	int i, p, s;
+
+	ctlr = i2ctlr;
+	if(up){
+		if(n > MaxIO)
+			error(Etoobig);
+		qlock(&ctlr->io);
+		if(waserror()){
+			qunlock(&ctlr->io);
+			nexterror();
+		}
+	}
+	ctlr->txbuf[0] = d->addr<<1;
+	i = 1;
+	if(d->salen > 1)
+		ctlr->txbuf[i++] = offset>>8;
+	if(d->salen)
+		ctlr->txbuf[i++] = offset;
+	memmove(ctlr->txbuf+i, buf, n);
+	if(Chatty){
+		print("tx: %8.8lux: ", PADDR(ctlr->txbuf));
+		for(s=0; s<n+i; s++)
+			print(" %.2ux", ctlr->txbuf[s]&0xFF);
+		print("\n");
+	}
+	DCFLUSH(ctlr->txbuf, Bufsize);
+	ilock(ctlr);
+	ctlr->phase = Sending;
+	ctlr->rd->status = BDEmpty|BDWrap|BDInt;
+	ctlr->td->addr = PADDR(ctlr->txbuf);
+	ctlr->td->length = n+i;
+	ctlr->td->status = BDReady|BDWrap|BDLast|BDInt;
+	enable();
+	ctlr->i2c->i2com = STR|I2CM;
+	eieio();
+	iunlock(ctlr);
+	i2cwait(ctlr);
+	disable();
+	p = ctlr->phase;
+	s = ctlr->td->status;
+	if(up){
+		poperror();
+		qunlock(&ctlr->io);
+	}
+	if(s & BDReady)
+		return i2cerror("timed out");
+	if(s & TxERR){
+		sprint(up->genbuf, "write error: status %.4ux", s);
+		return i2cerror(up->genbuf);
+	}
+	if(p != Done)
+		return i2cerror("phase error");
+	return n;
+}
+
+long
+i2crecv(I2Cdev *d, void *buf, long n, ulong offset)
+{
+	Ctlr *ctlr;
+	int p, s, flag, i;
+	BD *td;
+	long nr;
+
+	ctlr = i2ctlr;
+	if(up){
+		if(n > MaxIO)
+			error(Etoobig);
+		qlock(&ctlr->io);
+		if(waserror()){
+			qunlock(&ctlr->io);
+			nexterror();
+		}
+	}
+	ctlr->txbuf[0] = (d->addr<<1)|Rbit;
+	if(d->salen){	/* special write to set address */
+		ctlr->addr[0] = d->addr<<1;
+		i = 1;
+		if(d->salen > 1)
+			ctlr->addr[i++] = offset >> 8;
+		ctlr->addr[i] = offset;
+	}
+	DCFLUSH(ctlr->txbuf, Bufsize);
+	DCFLUSH(ctlr->rxbuf, Bufsize);
+	ilock(ctlr);
+	ctlr->phase = Recving;
+	ctlr->rd->addr = PADDR(ctlr->rxbuf);
+	ctlr->rd->status = BDEmpty|BDWrap|BDInt;
+	flag = 0;
+	td = ctlr->td;
+	td[1].status = 0;
+	if(d->salen){
+		/* special select sequence */
+		td->addr = PADDR(ctlr->addr);
+		i = d->salen+1;
+		if(i > 3)
+			i = 3;
+		td->length = i;
+		/* td->status made BDReady below */
+		td++;
+		flag = TxS;
+	}
+	td->addr = PADDR(ctlr->txbuf);
+	td->length = n+1;
+	td->status = BDReady|BDWrap|BDLast | flag;	/* not BDInt: leave that to receive */
+	if(flag)
+		ctlr->td->status = BDReady;
+	enable();
+	ctlr->i2c->i2com = STR|I2CM;
+	eieio();
+	iunlock(ctlr);
+	i2cwait(ctlr);
+	disable();
+	p = ctlr->phase;
+	s = ctlr->td->status;
+	if(flag)
+		s |= ctlr->td[1].status;
+	nr = ctlr->rd->length;
+	if(up){
+		poperror();
+		qunlock(&ctlr->io);
+	}
+	DPRINT("nr=%ld %4.4ux %8.8lux\n", nr, ctlr->rd->status, ctlr->rd->addr);
+	if(nr > n)
+		nr = n;	/* shouldn't happen */
+	if(s & TxERR){
+		sprint(up->genbuf, "read: tx status: %.4ux", s);
+		return i2cerror(up->genbuf);
+	}
+	if(s & BDReady || ctlr->rd->status & BDEmpty)
+		return i2cerror("timed out");
+	if(p != Done)
+		return i2cerror("phase error");
+	memmove(buf, ctlr->rxbuf, nr);
+	if(Chatty){
+		for(s=0; s<nr; s++)
+			print(" %2.2ux", ctlr->rxbuf[s]&0xFF);
+		print("\n");
+	}
+	return nr;
+}
--- /dev/null
+++ b/os/mpc/i2c_spi.srx
@@ -1,0 +1,149 @@
+S00600004844521B
+S309022020007FFFEFD96E
+S309022020043FFD000074
+S309022020087FFB49F7F2
+S3090220200C7FF9000030
+S309022020105FEFADF7B2
+S309022020145F89ADF714
+S309022020185FEFAFF7A8
+S3090220201C5F89AFF70A
+S309022020203A9CFBC8FB
+S30902202024E7C0EDF00C
+S3090220202877C1E1BBB8
+S3090220202CF4DC7F1D1C
+S30902202030ABAD932F6A
+S309022020344E08FDCF5E
+S309022020386E0FAFF858
+S3090220203C7CCF76CFE8
+S30902202040FD1FF9CF90
+S30902202044ABF88DC67A
+S30902202048AB5679F7FB
+S3090220204CB09373832F
+S30902202050DFCE79F747
+S30902202054B091E6BB7E
+S30902202058E5BBE74F86
+S3090220205CB3FA6F0F2D
+S309022020606FFB76CEA6
+S30902202064EE0DF9CF8D
+S309022020682BFBEFEF48
+S3090220206CCFEEF9CFC3
+S3090220207076CEAD242F
+S3090220207490B2DF9A85
+S309022020787FDDD0BF51
+S3090220207C4BF847FDB1
+S309022020807CCF76CEA5
+S30902202084CFEF7E1FD5
+S309022020887F1D7DFD16
+S3090220208CF0B6EF7122
+S309022020907FC177C1AC
+S30902202094FBC8607984
+S30902202098E722FBC850
+S3090220209C5FFFDFFFDC
+S309022020A05FB2FFFB09
+S309022020A4FBC8F3C892
+S309022020A894A67F0152
+S309022020AC7F1D5F39D4
+S309022020B0AFE85F5EB0
+S309022020B4FFDFDF96AD
+S309022020B8CB9FAF7D66
+S309022020BC5FC1AFED3C
+S309022020C08C1C5FC12C
+S309022020C4AFDD5FC342
+S309022020C8DF9A7EFDF8
+S309022020CCB0B25FB275
+S309022020D0FFFEABAD8F
+S309022020D45FB2FFFED2
+S309022020D85FCE600B44
+S309022020DCE6BB600BCC
+S309022020E05FCEDFC602
+S309022020E427FBEFDFE0
+S309022020E85FC8CFDEF8
+S309022020EC3A9CE7C04B
+S309022020F0EDF0F3C82C
+S309022020F47F0154CD1F
+S309022020F87F1D2D3DB6
+S309022020FC363A757063
+S309022021007E0AF1CE6C
+S3090220210437EF2E68F3
+S309022021087FEE10EC42
+S3090220210CADF8EFDE35
+S30902202110CFEAE52FD6
+S309022021147D0FE12B07
+S30902202118F1CE5F6518
+S3090220211C7E0A4DF8CA
+S30902202120CFEA5F7209
+S309022021247D0BEFEE2A
+S30902202128CFEA5F74FF
+S3090220212CE522EFDEB3
+S309022021305F74CFDA07
+S309022021340B6273851A
+S30902202138DF627E0AB2
+S3090220213C30D8145B00
+S30902202140BFFFF3C8FA
+S309022021445FFFDFFF33
+S30902202148A7F85F5E0F
+S3090220214CBFFE7F7DAE
+S3090220215010D314501C
+S309022021545F36BFFF0C
+S30902202158AF785F5E77
+S3090220215CBFFDA7F8FC
+S309022021605F36BFFE01
+S3090220216477FD30C0EB
+S309022021684E08FDCF29
+S3090220216CE5FF6E0FE6
+S30902202170AFF87E1FFF
+S309022021747E0FFD1F96
+S30902202178F1CF5F1B01
+S3090220217CABF80D5E29
+S309022021805F5EFFEF88
+S3090220218479F730A2ED
+S30902202188AFDD5F340C
+S3090220218C47F85F3455
+S30902202190AFED7FDD2B
+S3090220219450B249785C
+S3090220219847FD7F1D3B
+S3090220219C7DFD70AD80
+S309022021A0EF717EC174
+S309022021A46BA47F0180
+S309022021A82D267EFD3D
+S309022021AC30DE5F5E3C
+S309022021B0FFFD5F5E4A
+S309022021B4FFEF5F5E54
+S309022021B8FFDF0CA071
+S309022021BCAFED0A9EB3
+S309022021C0AFDD0C3A21
+S309022021C45F3AAFBDEA
+S309022021C87FBDB0827D
+S309022021CC5F8247F8C7
+S9030000FC
+S00600004844521B
+S30902202F003E303430D3
+S30902202F0434343737CB
+S30902202F08ABF7BF9BA1
+S30902202F0C994B4FBDA9
+S30902202F10BD59949358
+S30902202F14349FFF3788
+S30902202F18FB9B177D63
+S30902202F1CD99369565E
+S30902202F20BBFDD69760
+S30902202F24BDD2FD11E4
+S30902202F2831DB9BB323
+S30902202F2C6313963736
+S30902202F3093733693A6
+S30902202F34193137F7F9
+S30902202F38331737AF3D
+S30902202F3C7BB9B999E3
+S30902202F40BB197957C1
+S30902202F447FDFD3D55B
+S30902202F4873B773F7C9
+S30902202F4C37933B99BB
+S30902202F501D115316BE
+S30902202F54993153151F
+S30902202F5831694BF474
+S30902202F5CFBDBD35947
+S30902202F603149735305
+S30902202F6476956D6960
+S30902202F687B9D9693FC
+S30902202F6C1313197981
+S30902202F7079376935E7
+S9030000FC
\ No newline at end of file
--- /dev/null
+++ b/os/mpc/inb.s
@@ -1,0 +1,120 @@
+#include "mem.h"
+
+#define	BDNZ	BC	16,0,
+
+TEXT	inb(SB), $0
+	OR	$ISAIO, R3
+	EIEIO
+	MOVBZ	(R3), R3
+	RETURN
+
+TEXT	insb(SB), $0
+	MOVW	v+4(FP), R4
+	MOVW	n+8(FP), R5
+	MOVW	R5, CTR
+	OR	$ISAIO, R3
+	SUB	$1, R4
+insb1:
+	EIEIO
+	MOVBZ	(R3), R7
+	MOVBU	R7, 1(R4)
+	BDNZ	insb1
+	RETURN
+
+TEXT	outb(SB), $0
+	MOVW	v+4(FP), R4
+	OR	$ISAIO, R3
+	EIEIO
+	MOVB	R4, (R3)
+	RETURN
+
+TEXT	outsb(SB), $0
+	MOVW	v+4(FP), R4
+	MOVW	n+8(FP), R5
+	MOVW	R5, CTR
+	OR	$ISAIO, R3
+	SUB	$1, R4
+outsb1:
+	EIEIO
+	MOVBZU	1(R4), R7
+	MOVB	R7, (R3)
+	BDNZ	outsb1
+	RETURN
+
+TEXT	ins(SB), $0
+	OR	$ISAIO, R3
+	EIEIO
+	MOVHBR	(R3), R3
+	RETURN
+
+TEXT	inss(SB), $0
+	MOVW	v+4(FP), R4
+	MOVW	n+8(FP), R5
+	MOVW	R5, CTR
+	OR	$ISAIO, R3
+	SUB	$2, R4
+inss1:
+	EIEIO
+	MOVHZ	(R3), R7
+	MOVHU	R7, 2(R4)
+	BDNZ	inss1
+	RETURN
+
+TEXT	outs(SB), $0
+	MOVW	v+4(FP), R4
+	OR	$ISAIO, R3
+	EIEIO
+	MOVHBR	R4, (R3)
+	RETURN
+
+TEXT	outss(SB), $0
+	MOVW	v+4(FP), R4
+	MOVW	n+8(FP), R5
+	MOVW	R5, CTR
+	OR	$ISAIO, R3
+	SUB	$2, R4
+outss1:
+	EIEIO
+	MOVHZU	2(R4), R7
+	MOVH	R7, (R3)
+	BDNZ	outss1
+	RETURN
+
+TEXT	inl(SB), $0
+	OR	$ISAIO, R3
+	EIEIO
+	MOVWBR	(R3), R3
+	RETURN
+
+TEXT	insl(SB), $0
+	MOVW	v+4(FP), R4
+	MOVW	n+8(FP), R5
+	MOVW	R5, CTR
+	OR	$ISAIO, R3
+	SUB	$4, R4
+insl1:
+	EIEIO
+	MOVW	(R3), R7
+	MOVWU	R7, 4(R4)
+	BDNZ	insl1
+	RETURN
+
+TEXT	outl(SB), $0
+	MOVW	v+4(FP), R4
+	OR	$ISAIO, R3
+	EIEIO
+	MOVWBR	R4, (R3)
+	RETURN
+
+TEXT	outsl(SB), $0
+	MOVW	v+4(FP), R4
+	MOVW	n+8(FP), R5
+	MOVW	R5, CTR
+	OR	$ISAIO, R3
+	SUB	$4, R4
+outsl1:
+	EIEIO
+	MOVWU	4(R4), R7
+	MOVW	R7, (R3)
+	BDNZ	outsl1
+	RETURN
--- /dev/null
+++ b/os/mpc/kbd.c
@@ -1,0 +1,20 @@
+#include	"u.h"
+#include	"../port/lib.h"
+#include	"mem.h"
+#include	"dat.h"
+#include	"fns.h"
+#include	"io.h"
+#include	"../port/error.h"
+
+/*
+ * initialise the keyboard Queue if uartinstall hasn't already done so
+ */
+void
+kbdinit(void)
+{
+	if(kbdq == nil){
+		kbdq = qopen(4*1024, 0, 0, 0);
+		qnoblock(kbdq, 1);
+	}
+	archkbdinit();
+}
--- /dev/null
+++ b/os/mpc/l.s
@@ -1,0 +1,685 @@
+#include	"mem.h"
+
+#define	MB	(1024*1024)
+
+/*
+ * options
+ */
+#undef	MMUTWC		/* we don't map enough memory to need table walk */
+#undef	SHOWCYCLE	/* might be needed for BDM debugger to keep control */
+
+/*
+ * common ppc special purpose registers
+ */
+#define DSISR	18
+#define DAR	19	/* Data Address Register */
+#define DEC	22	/* Decrementer */
+#define SRR0	26	/* Saved Registers (exception) */
+#define SRR1	27
+#define SPRG0	272	/* Supervisor Private Registers */
+#define SPRG1	273
+#define SPRG2	274
+#define SPRG3	275
+#define TBRU	269	/* Time base Upper/Lower (Reading) */
+#define TBRL	268
+#define TBWU	285	/* Time base Upper/Lower (Writing) */
+#define TBWL	284
+#define PVR	287	/* Processor Version */
+
+/*
+ * mpc8xx-specific special purpose registers of interest here
+ */
+#define EIE	80
+#define EID	81
+#define NRI	82
+#define IMMR	638
+#define IC_CSR	560
+#define IC_ADR	561
+#define IC_DAT	562
+#define DC_CSR	568
+#define DC_ADR	569
+#define DC_DAT	570
+#define MI_CTR	784
+#define MI_AP	786
+#define MI_EPN	787
+#define MI_TWC	789
+#define MI_RPN	790
+#define MI_DBCAM	816
+#define MI_DBRAM0	817
+#define MI_DBRAM1	818
+#define MD_CTR	792
+#define M_CASID	793
+#define MD_AP	794
+#define MD_EPN	795
+#define M_TWB	796
+#define MD_TWC	797
+#define MD_RPN	798
+#define	M_TW	799
+#define	MD_DBCAM	824
+#define	MD_DBRAM0	825
+#define	MD_DBRAM1	826
+
+/* use of SPRG registers in save/restore */
+#define	SAVER0	SPRG0
+#define	SAVER1	SPRG1
+#define	SAVELR	SPRG2
+#define	SAVEXX	SPRG3
+
+/* special instruction definitions */
+#define	BDNZ	BC	16,0,
+#define	BDNE	BC	0,2,
+#define	TLBIA	WORD	$((31<<26)|(370<<1))
+#define	MFTB(tbr,d)	WORD	$((31<<26)|((d)<<21)|((tbr&0x1f)<<16)|(((tbr>>5)&0x1f)<<11)|(371<<1))
+
+/* on some models mtmsr doesn't synchronise enough (eg, 603e) */
+#define	MSRSYNC	SYNC; ISYNC
+
+#define	UREGSPACE	(UREGSIZE+8)
+
+/* could define STEP to set an LED to mark progress */
+#define	STEP(x)
+
+/*
+ * Boot first processor
+ */
+	TEXT start(SB), $-4
+
+	MOVW	MSR, R3
+	RLWNM	$0, R3, $~EE, R3
+	RLWNM	$0, R3, $~FPE, R3
+	OR	$ME, R3
+	ISYNC
+	MOVW	R3, MSR	/* turn off interrupts but enable traps */
+	MSRSYNC
+	MOVW	$0, R0	/* except during trap handling, R0 is zero from now on */
+	MOVW	R0, CR
+	MOVW	$setSB(SB), R2
+
+/*
+ * reset the caches and disable them for now
+ */
+	MOVW	SPR(IC_CSR), R4	/* read and clear */
+	MOVW	$(5<<25), R4
+	MOVW	R4, SPR(IC_CSR)	/* unlock all */
+	ISYNC
+	MOVW	$(6<<25), R4
+	MOVW	R4, SPR(IC_CSR)	/* invalidate all */
+	ISYNC
+	MOVW	$(2<<25), R4
+	MOVW	R4, SPR(IC_CSR)	/* disable i-cache */
+	ISYNC
+
+	SYNC
+	MOVW	SPR(DC_CSR), R4	/* read and clear */
+	MOVW	$(10<<24), R4
+	SYNC
+	MOVW	R4, SPR(DC_CSR)	/* unlock all */
+	ISYNC
+	MOVW	$(12<<24), R4
+	SYNC
+	MOVW	R4, SPR(DC_CSR)	/* invalidate all */
+	ISYNC
+	MOVW	$(4<<24), R4
+	SYNC
+	MOVW	R4, SPR(DC_CSR)	/* disable d-cache */
+	ISYNC
+
+#ifdef SHOWCYCLE
+	MOVW	$0, R4
+#else
+	MOVW	$7, R4
+#endif
+	MOVW	R4, SPR(158)		/* cancel `show cycle' for normal instruction execution */
+	ISYNC
+
+/*
+ * set other system configuration values
+ */
+	MOVW	$PHYSIMM, R4
+	MOVW	R4, SPR(IMMR)		/* set internal memory base */
+
+STEP(1)
+
+	BL	kernelmmu(SB)
+
+STEP(2)
+	/* no kfpinit on 82x */
+
+	MOVW	$mach0(SB), R(MACH)
+	ADD	$(MACHSIZE-8), R(MACH), R1
+	SUB	$4, R(MACH), R3
+	ADD	$4, R1, R4
+clrmach:
+	MOVWU	R0, 4(R3)
+	CMP	R3, R4
+	BNE	clrmach
+
+	MOVW	R0, R(USER)
+	MOVW	R0, 0(R(MACH))
+
+	MOVW	$edata(SB), R3
+	MOVW	$end(SB), R4
+	ADD	$4, R4
+	SUB	$4, R3
+clrbss:
+	MOVWU	R0, 4(R3)
+	CMP	R3, R4
+	BNE	clrbss
+
+STEP(3)
+	BL	main(SB)
+	BR	0(PC)
+
+TEXT	kernelmmu(SB), $0
+	TLBIA
+	ISYNC
+
+	MOVW	$0, R4
+	MOVW	R4, SPR(M_CASID)	/* set supervisor space */
+	MOVW	$(0<<29), R4		/* allow i-cache when IR=0 */
+	MOVW	R4, SPR(MI_CTR)	/* i-mmu control */
+	ISYNC
+	MOVW	$((1<<29)|(1<<28)), R4	/* cache inhibit when DR=0, write-through */
+	SYNC
+	MOVW	R4, SPR(MD_CTR)	/* d-mmu control */
+	ISYNC
+	TLBIA
+
+	/* map various things 1:1 */
+	MOVW	$tlbtab-KZERO(SB), R4
+	MOVW	$tlbtabe-KZERO(SB), R5
+	SUB	R4, R5
+	MOVW	$(3*4), R6
+	DIVW	R6, R5
+	SUB	$4, R4
+	MOVW	R5, CTR
+ltlb:
+	MOVWU	4(R4), R5
+	MOVW	R5, SPR(MD_EPN)
+	MOVW	R5, SPR(MI_EPN)
+	MOVWU	4(R4), R5
+	MOVW	R5, SPR(MI_TWC)
+	MOVW	R5, SPR(MD_TWC)
+	MOVWU	4(R4), R5
+	MOVW	R5, SPR(MD_RPN)
+	MOVW	R5, SPR(MI_RPN)
+	BDNZ	ltlb
+
+	MOVW	$(1<<25), R4
+	MOVW	R4, SPR(IC_CSR)	/* enable i-cache */
+	ISYNC
+
+	MOVW	$(3<<24), R4
+	SYNC
+	MOVW	R4, SPR(DC_CSR)	/* clear force write through mode */
+	MOVW	$(2<<24), R4
+	SYNC
+	MOVW	R4, SPR(DC_CSR)	/* enable d-cache */
+	ISYNC
+
+	/* enable MMU and set kernel PC to virtual space */
+	MOVW	$((0<<29)|(0<<28)), R4	/* cache when DR=0, write back */
+	SYNC
+	MOVW	R4, SPR(MD_CTR)	/* d-mmu control */
+	MOVW	LR, R3
+	OR	$KZERO, R3
+	MOVW	R3, SPR(SRR0)
+	MOVW	MSR, R4
+	OR	$(ME|IR|DR), R4	/* had ME|FPE|FE0|FE1 */
+	MOVW	R4, SPR(SRR1)
+	RFI	/* resume in kernel mode in caller */
+
+TEXT	splhi(SB), $0
+	MOVW	MSR, R3
+	RLWNM	$0, R3, $~EE, R4
+	SYNC
+	MOVW	R4, MSR
+	MSRSYNC
+	MOVW	LR, R31
+	MOVW	R31, 4(R(MACH))	/* save PC in m->splpc */
+	RETURN
+
+TEXT	splx(SB), $0
+	MOVW	MSR, R4
+	RLWMI	$0, R3, $EE, R4
+	RLWNMCC	$0, R3, $EE, R5
+	BNE	splx0
+	MOVW	LR, R31
+	MOVW	R31, 4(R(MACH))	/* save PC in m->splpc */
+splx0:
+	SYNC
+	MOVW	R4, MSR
+	MSRSYNC
+	RETURN
+
+TEXT	splxpc(SB), $0
+	MOVW	MSR, R4
+	RLWMI	$0, R3, $EE, R4
+	RLWNMCC	$0, R3, $EE, R5
+	SYNC
+	MOVW	R4, MSR
+	MSRSYNC
+	RETURN
+
+TEXT	spllo(SB), $0
+	MFTB(TBRL, 3)
+	MOVW	R3, spltbl(SB)
+	MOVW	MSR, R3
+	OR	$EE, R3, R4
+	SYNC
+	MOVW	R4, MSR
+	MSRSYNC
+	RETURN
+
+TEXT	spldone(SB), $0
+	RETURN
+
+TEXT	islo(SB), $0
+	MOVW	MSR, R3
+	RLWNM	$0, R3, $EE, R3
+	RETURN
+
+TEXT	setlabel(SB), $-4
+	MOVW	LR, R31
+	MOVW	R1, 0(R3)
+	MOVW	R31, 4(R3)
+	MOVW	$0, R3
+	RETURN
+
+TEXT	gotolabel(SB), $-4
+	MOVW	4(R3), R31
+	MOVW	R31, LR
+	MOVW	0(R3), R1
+	MOVW	$1, R3
+	RETURN
+
+/*
+ * enter with stack set and mapped.
+ * on return, SB (R2) has been set, and R3 has the Ureg*,
+ * the MMU has been re-enabled, kernel text and PC are in KSEG,
+ * R(MACH) has been set, and R0 contains 0.
+ *
+ * this can be simplified in the Inferno regime
+ */
+TEXT	saveureg(SB), $-4
+/*
+ * save state
+ */
+	MOVMW	R2, 48(R1)	/* r2:r31 */
+	MOVW	$setSB(SB), R2
+	MOVW	SPR(SAVER1), R4
+	MOVW	R4, 44(R1)
+	MOVW	SPR(SAVER0), R5
+	MOVW	R5, 40(R1)
+	MOVW	CTR, R6
+	MOVW	R6, 36(R1)
+	MOVW	XER, R4
+	MOVW	R4, 32(R1)
+	MOVW	CR, R5
+	MOVW	R5, 28(R1)
+	MOVW	SPR(SAVELR), R6	/* LR */
+	MOVW	R6, 24(R1)
+	/* pad at 20(R1) */
+	/* old PC(16) and status(12) saved earlier */
+	MOVW	SPR(SAVEXX), R0
+	MOVW	R0, 8(R1)	/* cause/vector */
+	ADD	$8, R1, R3	/* Ureg* */
+	STWCCC	R3, (R1)	/* break any pending reservations */
+	MOVW	$0, R0	/* compiler/linker expect R0 to be zero */
+
+	MOVW	MSR, R5
+	OR	$(IR|DR), R5	/* enable MMU */
+	MOVW	R5, SPR(SRR1)
+	MOVW	LR, R31
+	OR	$KZERO, R31	/* return PC in KSEG0 */
+	MOVW	R31, SPR(SRR0)
+	SYNC
+	ISYNC
+	RFI	/* returns to trap handler */
+
+TEXT	icflush(SB), $-4	/* icflush(virtaddr, count) */
+	MOVW	n+4(FP), R4
+	RLWNM	$0, R3, $~(CACHELINESZ-1), R5
+	SUB	R5, R3
+	ADD	R3, R4
+	ADD		$(CACHELINESZ-1), R4
+	SRAW	$CACHELINELOG, R4
+	MOVW	R4, CTR
+icf0:	ICBI	(R5)
+	ADD	$CACHELINESZ, R5
+	BDNZ	icf0
+	ISYNC
+	RETURN
+
+/*
+ * flush to store and invalidate globally
+ */
+TEXT	dcflush(SB), $-4	/* dcflush(virtaddr, count) */
+	SYNC
+	MOVW	n+4(FP), R4
+	RLWNM	$0, R3, $~(CACHELINESZ-1), R5
+	CMP	R4, $0
+	BLE	dcf1
+	SUB	R5, R3
+	ADD	R3, R4
+	ADD		$(CACHELINESZ-1), R4
+	SRAW	$CACHELINELOG, R4
+	MOVW	R4, CTR
+dcf0:	DCBF	(R5)
+	ADD	$CACHELINESZ, R5
+	BDNZ	dcf0
+	SYNC
+	ISYNC
+dcf1:
+	RETURN
+
+/*
+ * invalidate without flush, globally
+ */
+TEXT	dcinval(SB), $-4	/* dcinval(virtaddr, count) */
+	SYNC
+	MOVW	n+4(FP), R4
+	RLWNM	$0, R3, $~(CACHELINESZ-1), R5
+	CMP	R4, $0
+	BLE	dci1
+	SUB	R5, R3
+	ADD	R3, R4
+	ADD		$(CACHELINESZ-1), R4
+	SRAW	$CACHELINELOG, R4
+	MOVW	R4, CTR
+dci0:	DCBI	(R5)
+	ADD	$CACHELINESZ, R5
+	BDNZ	dci0
+	SYNC
+	ISYNC
+dci1:
+	RETURN
+
+TEXT	_tas(SB), $0
+	SYNC
+	MOVW	R3, R4
+	MOVW	$0xdeaddead,R5
+tas1:
+	DCBF	(R4)	/* fix for 603x bug */
+	LWAR	(R4), R3
+	CMP	R3, $0
+	BNE	tas0
+	STWCCC	R5, (R4)
+	BNE	tas1
+tas0:
+	SYNC
+	ISYNC
+	RETURN
+
+TEXT	gettbl(SB), $0
+	MFTB(TBRL, 3)
+	RETURN
+
+TEXT	gettbu(SB), $0
+	MFTB(TBRU, 3)
+	RETURN
+
+TEXT	getpvr(SB), $0
+	MOVW	SPR(PVR), R3
+	RETURN
+
+TEXT	getimmr(SB), $0
+	MOVW	SPR(IMMR), R3
+	RETURN
+
+TEXT	getdec(SB), $0
+	MOVW	SPR(DEC), R3
+	RETURN
+
+TEXT	putdec(SB), $0
+	MOVW	R3, SPR(DEC)
+	RETURN
+
+TEXT	getcallerpc(SB), $-4
+	MOVW	0(R1), R3
+	RETURN
+
+TEXT getdar(SB), $0
+	MOVW	SPR(DAR), R3
+	RETURN
+
+TEXT getdsisr(SB), $0
+	MOVW	SPR(DSISR), R3
+	RETURN
+
+TEXT	getdepn(SB), $0
+	MOVW	SPR(MD_EPN), R3
+	RETURN
+
+TEXT	getmsr(SB), $0
+	MOVW	MSR, R3
+	RETURN
+
+TEXT	putmsr(SB), $0
+	SYNC
+	MOVW	R3, MSR
+	MSRSYNC
+	RETURN
+
+TEXT	eieio(SB), $0
+	EIEIO
+	RETURN
+
+TEXT	gotopc(SB), $0
+	MOVW	R3, CTR
+	MOVW	LR, R31	/* for trace back */
+	BR	(CTR)
+
+TEXT	firmware(SB), $0
+	MOVW	MSR, R3
+	MOVW	$(EE|ME), R4
+	ANDN	R4, R3
+	OR	$(MSR_IP), R3
+	ISYNC
+	MOVW	R3, MSR	/* turn off interrupts and machine checks */
+	MSRSYNC
+	MOVW	$(RI|IR|DR|ME), R4
+	ANDN	R4, R3
+	MOVW	R3, SPR(SRR1)
+	MOVW	$(0xFF00<<16), R4
+	MOVW	R4, SPR(IMMR)
+	MOVW	$(0x0800<<16), R4
+	MOVW	R4, SPR(SRR0)	/* force bad address */
+	MOVW	R0, SPR(149)	/* ensure checkstop on machine check */
+	MOVW	R4, R1
+	MOVW	R4, R2
+	EIEIO
+	ISYNC
+	RFI
+
+/*
+ * byte swapping of arrays of long and short;
+ * could possibly be avoided with more changes to drivers
+ */
+TEXT	swabl(SB), $0
+	MOVW	v+4(FP), R4
+	MOVW	n+8(FP), R5
+	SRAW	$2, R5, R5
+	MOVW	R5, CTR
+	SUB	$4, R4
+	SUB	$4, R3
+swabl1:
+	ADD	$4, R3
+	MOVWU	4(R4), R7
+	MOVWBR	R7, (R3)
+	BDNZ	swabl1
+	RETURN
+
+TEXT	swabs(SB), $0
+	MOVW	v+4(FP), R4
+	MOVW	n+8(FP), R5
+	SRAW	$1, R5, R5
+	MOVW	R5, CTR
+	SUB	$2, R4
+	SUB	$2, R3
+swabs1:
+	ADD	$2, R3
+	MOVHZU	2(R4), R7
+	MOVHBR	R7, (R3)
+	BDNZ	swabs1
+	RETURN
+
+TEXT	legetl(SB), $0
+	MOVWBR	(R3), R3
+	RETURN
+
+TEXT	lesetl(SB), $0
+	MOVW	v+4(FP), R4
+	MOVWBR	R4, (R3)
+	RETURN
+
+TEXT	legets(SB), $0
+	MOVHBR	(R3), R3
+	RETURN
+
+TEXT	lesets(SB), $0
+	MOVW	v+4(FP), R4
+	MOVHBR	R4, (R3)
+	RETURN
+
+#ifdef MMUTWC
+/*
+ * ITLB miss
+ *	avoid references that might need the right SB value;
+ *	IR and DR are off.
+ */
+TEXT	itlbmiss(SB), $-4
+	MOVW	R1, SPR(M_TW)
+	MOVW	SPR(SRR0), R1	/* instruction miss address */
+	MOVW	R1, SPR(MD_EPN)
+	MOVW	SPR(M_TWB), R1	/* level one pointer */
+	MOVW	(R1), R1
+	MOVW	R1, SPR(MI_TWC)	/* save level one attributes */
+	MOVW	R1, SPR(MD_TWC)	/* save base and attributes */
+	MOVW	SPR(MD_TWC), R1	/* level two pointer */
+	MOVW	(R1), R1	/* level two entry */
+	MOVW	R1, SPR(MI_RPN)	/* write TLB */
+	MOVW	SPR(M_TW), R1
+	RFI
+
+/*
+ * DTLB miss
+ *	avoid references that might need the right SB value;
+ *	IR and DR are off.
+ */
+TEXT	dtlbmiss(SB), $-4
+	MOVW	R1, SPR(M_TW)
+	MOVW	SPR(M_TWB), R1	/* level one pointer */
+	MOVW	(R1), R1	/* level one entry */
+	MOVW	R1, SPR(MD_TWC)	/* save base and attributes */
+	MOVW	SPR(MD_TWC), R1	/* level two pointer */
+	MOVW	(R1), R1	/* level two entry */
+	MOVW	R1, SPR(MD_RPN)	/* write TLB */
+	MOVW	SPR(M_TW), R1
+	RFI
+#else
+TEXT	itlbmiss(SB), $-4
+	BR	traps
+TEXT	dtlbmiss(SB), $-4
+	BR	traps
+#endif
+
+/*
+ * traps force memory mapping off.
+ * this code goes to too much effort (for the Inferno environment) to restore it.
+ */
+TEXT	trapvec(SB), $-4
+traps:
+	MOVW	LR, R0
+
+pagefault:
+
+/*
+ * map data virtually and make space to save
+ */
+	MOVW	R0, SPR(SAVEXX)	/* vector */
+	MOVW	R1, SPR(SAVER1)
+	SYNC
+	ISYNC
+	MOVW	MSR, R0
+	OR	$(DR|ME), R0		/* make data space usable */
+	SYNC
+	MOVW	R0, MSR
+	MSRSYNC
+	SUB	$UREGSPACE, R1
+
+	MOVW	SPR(SRR0), R0	/* save SRR0/SRR1 now, since DLTB might be missing stack page */
+	MOVW	R0, LR
+	MOVW	SPR(SRR1), R0
+	MOVW	R0, 12(R1)	/* save status: could take DLTB miss here */
+	MOVW	LR, R0
+	MOVW	R0, 16(R1)	/* old PC */
+	BL	saveureg(SB)
+	BL	trap(SB)
+	BR	restoreureg
+
+TEXT	intrvec(SB), $-4
+	MOVW	LR, R0
+
+/*
+ * map data virtually and make space to save
+ */
+	MOVW	R0, SPR(SAVEXX)	/* vector */
+	MOVW	R1, SPR(SAVER1)
+	SYNC
+	ISYNC
+	MOVW	MSR, R0
+	OR	$DR, R0		/* make data space usable */
+	SYNC
+	MOVW	R0, MSR
+	MSRSYNC
+	SUB	$UREGSPACE, R1
+
+	MFTB(TBRL, 0)
+	MOVW	R0, intrtbl(SB)
+
+	MOVW	SPR(SRR0), R0
+	MOVW	R0, LR
+	MOVW	SPR(SRR1), R0
+	MOVW	R0, 12(R1)
+	MOVW	LR, R0
+	MOVW	R0, 16(R1)
+	BL	saveureg(SB)
+
+	MFTB(TBRL, 5)
+	MOVW	R5, isavetbl(SB)
+
+	BL	intr(SB)
+
+/*
+ * restore state from Ureg and return from trap/interrupt
+ */
+restoreureg:
+	MOVMW	48(R1), R2	/* r2:r31 */
+	/* defer R1 */
+	MOVW	40(R1), R0
+	MOVW	R0, SPR(SAVER0)
+	MOVW	36(R1), R0
+	MOVW	R0, CTR
+	MOVW	32(R1), R0
+	MOVW	R0, XER
+	MOVW	28(R1), R0
+	MOVW	R0, CR	/* CR */
+	MOVW	24(R1), R0
+	MOVW	R0, SPR(SAVELR)	/* LR */
+	/* pad, skip */
+	MOVW	16(R1), R0
+	MOVW	R0, SPR(SRR0)	/* old PC */
+	MOVW	12(R1), R0
+	MOVW	R0, SPR(SRR1)	/* old MSR */
+	/* cause, skip */
+	MOVW	44(R1), R1	/* old SP */
+	MOVW	SPR(SAVELR), R0
+	MOVW	R0, LR
+	MOVW	SPR(SAVER0), R0
+	RFI
+
+GLOBL	mach0+0(SB), $MACHSIZE
+GLOBL	spltbl+0(SB), $4
+GLOBL	intrtbl+0(SB), $4
+GLOBL	isavetbl+0(SB), $4
--- /dev/null
+++ b/os/mpc/nofp.s
@@ -1,0 +1,31 @@
+/*
+ * stubs when no floating-point hardware
+ */
+
+TEXT	kfpinit(SB), $0
+	RETURN
+
+TEXT	getfpscr(SB), $8
+	MOVW	$0, R3
+	RETURN
+
+TEXT	fpsave(SB), $0
+	RETURN
+
+TEXT	fprestore(SB), $0
+	RETURN
+
+TEXT	clrfptrap(SB), $0
+	RETURN
+
+TEXT	fpinit(SB), $0
+	RETURN
+
+TEXT	fpoff(SB), $0
+	RETURN
+
+TEXT	FPsave(SB), 1, $0
+	RETURN
+
+TEXT	FPrestore(SB), 1, $0
+	RETURN
--- /dev/null
+++ b/os/mpc/pcmcia.h
@@ -1,0 +1,19 @@
+/*
+ * PCMCIA support code.
+ */
+int	inb(int);
+int	inb(int);
+ulong	inl(int);
+ushort	ins(int);
+void	insb(int, void*, int);
+void	insl(int, void*, int);
+void	inss(int, void*, int);
+void	outb(int, int);
+void	outl(int, ulong);
+void	outs(int, ushort);
+void	outsb(int, void*, int);
+void	outsl(int, void*, int);
+void	outss(int, void*, int);
+void	pcmintrenable(int, void(*)(Ureg*,void*), void*);
+int	pcmspecial(char*, ISAConf*);
+void	pcmspecialclose(int);
--- /dev/null
+++ b/os/mpc/pit.c
@@ -1,0 +1,68 @@
+/*
+ * programmable interrupt timer
+ */
+
+#include	"u.h"
+#include	"../port/lib.h"
+#include	"mem.h"
+#include	"dat.h"
+#include	"fns.h"
+#include	"io.h"
+#include	"ureg.h"
+
+enum {
+	/* piscr */
+	PTE =	1<<0,
+	PITF =	1<<1,
+	PIE =	1<<2,
+	PS =	1<<7,
+};
+
+static void
+pitinterrupt(Ureg*, void*)
+{
+	IMM *io;
+
+	io = m->iomem;
+	if(io->piscr & PS){
+		io->piscr |= PS;	/* clear by writing 1 */
+		/* do whatever is required */
+	}
+}
+
+static void
+pitreset(void)
+{
+	IMM *io;
+
+	io = ioplock();
+	io->piscrk = KEEP_ALIVE_KEY;
+	io->piscr = (PITlevel<<8) | PS | PITF;
+	if(0)
+		io->piscrk = ~KEEP_ALIVE_KEY;
+	/* piscrk is left unlocked for interrupt routine */
+	iopunlock();
+	intrenable(PITlevel, pitinterrupt, nil, BUSUNKNOWN, "pit");
+}
+
+static ulong
+pitload(ulong usec)
+{
+	IMM *io;
+	ulong v;
+
+	v = ((usec*m->oscclk)/512);
+	if(v == 0 || v >= (1<<16))
+		return 0;	/* can't do */
+	io = ioplock();
+	io->pitck = KEEP_ALIVE_KEY;
+	io->pitc = (v-1)<<16;
+	io->pitck = ~KEEP_ALIVE_KEY;
+	io->piscrk = KEEP_ALIVE_KEY;
+	io->piscr = (PITlevel<<8) | PS | PIE | PITF | PTE;
+	if(0)
+		io->piscrk = ~KEEP_ALIVE_KEY;
+	/* piscrk is left unlocked for interrupt routine */
+	iopunlock();
+	return (v*512)/m->oscclk;
+}
--- /dev/null
+++ b/os/mpc/powerbreak.c
@@ -1,0 +1,123 @@
+#include	"u.h"
+#include	"../port/lib.h"
+#include	"mem.h"
+#include	"dat.h"
+#include	"fns.h"
+#include	"../port/error.h"
+#include	"ureg.h"
+
+extern int (*breakhandler)(Ureg *ur, Proc*);	/* trap.c */
+extern Instr BREAK;	/* trap.c */
+extern void portbreakinit(void);
+
+#define getop(i) ((i>>26)&0x3F)
+#define getxo(i) ((i>>1)&0x3FF)
+#define getbobi(i)	bo = (i>>21)&0x1f; bi = (i>>16)&0x1f; xx = (i>>11)&0x1f;
+
+void
+machbreakinit(void)
+{
+	portbreakinit();
+	breakhandler = breakhit;
+}
+
+Instr
+machinstr(ulong addr)
+{
+	if (addr < KTZERO)
+		error(Ebadarg);
+	return *(Instr*)addr;
+}
+
+void
+machbreakset(ulong addr)
+{
+	if (addr < KTZERO)
+		error(Ebadarg);
+	*(Instr*)addr = BREAK;
+	segflush((void*)addr, sizeof(Instr));
+}
+
+void
+machbreakclear(ulong addr, Instr i)
+{
+	if (addr < KTZERO)
+		error(Ebadarg);
+	*(Instr*)addr = i;
+	segflush((void*)addr, sizeof(Instr));
+}
+
+static int
+condok(Ureg *ur, ulong ir, int ctrok)
+{
+	int bo, bi, xx;
+	ulong ctrval;
+
+	ctrval = ur->ctr;
+	getbobi(ir);
+	if(xx)
+		return 0;	/* illegal */
+	if((bo & 0x4) == 0) {
+		if(!ctrok)
+			return 0;	/* illegal */
+		ctrval--;
+	}
+	if(bo & 0x4 || (ctrval!=0)^((bo>>1)&1)) {
+		if(bo & 0x10 || (((ur->cr & (1L<<(31-bi))!=0)==((bo>>3)&1))))
+			return 1;
+	}
+	return 0;
+}
+
+/*
+ * Return the address of the instruction that will be executed after the
+ * instruction at ur->pc, accounting for current branch conditions.
+ */
+ulong
+machnextaddr(Ureg *ur)
+{
+	long imm;
+	ulong ir;
+
+	ir = *(ulong*)ur->pc;
+	switch(getop(ir)) {
+	case 18:	/* branch */
+		imm = ir & 0x03FFFFFC;
+		if(ir & 0x02000000)
+			imm |= 0xFC000000;	/* sign extended */
+		if((ir & 2) == 0)	/* relative address */
+			return ur->pc + imm;
+		return imm;
+			
+	case 16:	/* conditional branch */
+		if(condok(ur, ir&0xFFFF0000, 1)){
+			imm = ir & 0xFFFC;
+			if(ir & 0x08000)
+				imm |= 0xFFFF0000;	/* sign extended */
+			if((ir & 2) == 0)	/* relative address */
+				return ur->pc + imm;
+			return imm;
+		}
+		break;
+
+	case 19:	/* conditional branch to register */
+		switch(getxo(ir)){
+		case 528:	/* bcctr */
+			if(condok(ur, ir, 0))
+				return ur->ctr & ~3;
+			break;
+		case 16:	/* bclr */
+			if(condok(ur, ir, 1))
+				return ur->lr & ~3;
+			break;
+		}
+		break;
+	}
+	return ur->pc+4;	/* next instruction */
+}
+
+int
+isvalid_va(void *v)
+{
+	return (ulong)v >= KTZERO;
+}
--- /dev/null
+++ b/os/mpc/rmap.c
@@ -1,0 +1,106 @@
+#include "u.h"
+#include "lib.h"
+#include "mem.h"
+#include "dat.h"
+#include "fns.h"
+
+void
+mapinit(RMap *rmap, Map *map, int size)
+{
+	lock(rmap);
+	rmap->map = map;
+	rmap->mapend = map+(size/sizeof(Map));
+	unlock(rmap);
+}
+
+void
+mapfree(RMap* rmap, ulong addr, int size)
+{
+	Map *mp;
+	ulong t;
+
+	if(size <= 0)
+		return;
+
+	lock(rmap);
+	for(mp = rmap->map; mp->addr <= addr && mp->size; mp++)
+		;
+
+	if(mp > rmap->map && (mp-1)->addr+(mp-1)->size == addr){
+		(mp-1)->size += size;
+		if(addr+size == mp->addr){
+			(mp-1)->size += mp->size;
+			while(mp->size){
+				mp++;
+				(mp-1)->addr = mp->addr;
+				(mp-1)->size = mp->size;
+			}
+		}
+	}
+	else{
+		if(addr+size == mp->addr && mp->size){
+			mp->addr -= size;
+			mp->size += size;
+		}
+		else do{
+			if(mp >= rmap->mapend){
+				print("mapfree: %s: losing 0x%luX, %d\n",
+					rmap->name, addr, size);
+				break;
+			}
+			t = mp->addr;
+			mp->addr = addr;
+			addr = t;
+			t = mp->size;
+			mp->size = size;
+			mp++;
+		}while(size = t);
+	}
+	unlock(rmap);
+}
+
+ulong
+rmapalloc(RMap* rmap, ulong addr, int size, int align)
+{
+	Map *mp;
+	ulong maddr, oaddr;
+
+	lock(rmap);
+	for(mp = rmap->map; mp->size; mp++){
+		maddr = mp->addr;
+
+		if(addr){
+			if(maddr > addr)
+				break;
+			if(maddr+mp->size < addr)
+				continue;
+			if(addr+size > maddr+mp->size)
+				break;
+			maddr = addr;
+		}
+
+		if(align > 0)
+			maddr = ((maddr+align-1)/align)*align;
+		if(mp->addr+mp->size-maddr < size)
+			continue;
+
+		oaddr = mp->addr;
+		mp->addr = maddr+size;
+		mp->size -= maddr-oaddr+size;
+		if(mp->size == 0){
+			do{
+				mp++;
+				(mp-1)->addr = mp->addr;
+			}while((mp-1)->size = mp->size);
+		}
+
+		unlock(rmap);
+		if(oaddr != maddr)
+			mapfree(rmap, oaddr, maddr-oaddr);
+
+		return maddr;
+	}
+	unlock(rmap);
+
+	return 0;
+}
--- /dev/null
+++ b/os/mpc/screen.c
@@ -1,0 +1,832 @@
+#include	"u.h"
+#include	"../port/lib.h"
+#include	"mem.h"
+#include	"dat.h"
+#include	"fns.h"
+#include	"io.h"
+#include	"../port/error.h"
+
+#include	<draw.h>
+#include	<memdraw.h>
+#include	<cursor.h>
+
+#include	"screen.h"
+
+enum {
+	Backgnd = 0xFF,	/* white */
+	Foregnd =	0x00,	/* black */
+};
+
+Cursor	arrow = {
+	{ -1, -1 },
+	{ 0xFF, 0xFF, 0x80, 0x01, 0x80, 0x02, 0x80, 0x0C, 
+	  0x80, 0x10, 0x80, 0x10, 0x80, 0x08, 0x80, 0x04, 
+	  0x80, 0x02, 0x80, 0x01, 0x80, 0x02, 0x8C, 0x04, 
+	  0x92, 0x08, 0x91, 0x10, 0xA0, 0xA0, 0xC0, 0x40, 
+	},
+	{ 0x00, 0x00, 0x7F, 0xFE, 0x7F, 0xFC, 0x7F, 0xF0, 
+	  0x7F, 0xE0, 0x7F, 0xE0, 0x7F, 0xF0, 0x7F, 0xF8, 
+	  0x7F, 0xFC, 0x7F, 0xFE, 0x7F, 0xFC, 0x73, 0xF8, 
+	  0x61, 0xF0, 0x60, 0xE0, 0x40, 0x40, 0x00, 0x00, 
+	},
+};
+
+static Memdata xgdata;
+static Memimage xgscreen =
+{
+	{0, 0, 0, 0},	/* r */
+	{0, 0, 0, 0},	/* clipr */
+	8,			/* depth */
+	1,			/* nchan */
+	CMAP8,		/* chan */
+	nil,			/* cmap */
+	&xgdata,		/* data */
+	0,			/* zero */
+	0,			/* width */
+	nil,			/* layer */
+	0,			/* flags */
+};
+
+int	novgascreen;	/* optionally set by configuration file */
+static	int	lcdpdpar;	/* value to load into io->pdpar */
+
+Memimage *gscreen;
+Memimage *conscol;
+Memimage *back;
+
+static	Memsubfont *memdefont;
+static	Lock	palettelock;			/* access to DAC registers */
+static	Lock	screenlock;
+static	int	h;
+static	Point	curpos;
+static	Rectangle window;
+
+typedef struct SWcursor SWcursor;
+static SWcursor *swc = nil;
+SWcursor* swcurs_create(ulong *, int, int, Rectangle, int);
+void swcurs_destroy(SWcursor*);
+void swcurs_enable(SWcursor*);
+void swcurs_disable(SWcursor*);
+void swcurs_hide(SWcursor*);
+void swcurs_unhide(SWcursor*);
+void swcurs_load(SWcursor*, Cursor*);
+
+static	void	screenputc(char*);
+static	void	scroll(void);
+static	void	setscreen(Mode*);
+static	void	cursorlock(Rectangle);
+static	void	cursorunlock(void);
+static	void	lcdinit(Mode*);
+static	void	lcdsetrgb(int, ulong, ulong, ulong);
+
+/*
+ *  Called by main().
+ */
+void
+screeninit(void)
+{
+	Mode m;
+
+novgascreen=1; return;
+
+	/* default size and parameters */
+	memset(&m.lcd, 0, sizeof(m.lcd));
+	m.x = 640;
+	m.y = 480;
+	m.d = 3;
+	if(novgascreen == 0 && archlcdmode(&m) >= 0){
+		memdefont = getmemdefont();
+		setscreen(&m);
+	}
+}
+
+/*
+ * On 8 bit displays, load the default color map
+ */
+void
+graphicscmap(int invert)
+{
+	int num, den, i, j;
+	int r, g, b, cr, cg, cb, v;
+
+	for(r=0,i=0;r!=4;r++) for(v=0;v!=4;v++,i+=16){
+		for(g=0,j=v-r;g!=4;g++) for(b=0;b!=4;b++,j++){
+			den=r;
+			if(g>den) den=g;
+			if(b>den) den=b;
+			if(den==0)	/* divide check -- pick grey shades */
+				cr=cg=cb=v*17;
+			else{
+				num=17*(4*den+v);
+				cr=r*num/den;
+				cg=g*num/den;
+				cb=b*num/den;
+			}
+			if(invert)
+				setcolor(255-i-(j&15),
+					cr*0x01010101, cg*0x01010101, cb*0x01010101);
+			else
+				setcolor(i+(j&15),
+					cr*0x01010101, cg*0x01010101, cb*0x01010101);
+		}
+	}
+}
+
+/*
+ *  reconfigure screen shape
+ */
+static void
+setscreen(Mode *mode)
+{
+	int h;
+
+	if(swc)
+		swcurs_destroy(swc);
+
+	gscreen = &xgscreen;
+	xgdata.ref = 1;
+	lcdinit(mode);
+	xgdata.bdata = (uchar*)mode->aperture;
+	if(xgdata.bdata == nil)
+		panic("setscreen: vga soft memory");
+
+	gscreen->r = Rect(0, 0, mode->x, mode->y);
+	gscreen->clipr = gscreen->r;
+	gscreen->depth = 1<<mode->d;
+	gscreen->width = wordsperline(gscreen->r, gscreen->depth);
+	memimageinit();
+	memdefont = getmemdefont();
+
+	memsetchan(gscreen, CMAP8);
+	back = memwhite;
+	conscol = memblack;
+	memimagedraw(gscreen, gscreen->r, memwhite, ZP, memopaque, ZP, SoverD);
+	graphicscmap(0);
+
+	/* get size for a system window */
+	h = memdefont->height;
+	window = insetrect(gscreen->r, 4);
+	window.max.y = window.min.y+(Dy(window)/h)*h;
+	curpos = window.min;
+//	screenclear();
+
+	graphicscmap(0);
+
+//	swc = swcurs_create(gscreendata.data, gscreen.width, gscreen.ldepth, gscreen.r, 1);
+
+	drawcursor(nil);
+}
+
+enum {
+	ScreenCached = 1	/* non-zero if screen region not write-through */
+};
+
+void
+flushmemscreen(Rectangle r)
+{
+	if(rectclip(&r, gscreen->r) == 0)
+		return;
+	if(r.min.x >= r.max.x || r.min.y >= r.max.y)
+		return;
+	if(ScreenCached)
+		dcflush((ulong*)gscreen->data->bdata + gscreen->width*r.min.y, gscreen->width*Dy(r));
+}
+
+/* 
+ * export screen to interpreter
+ */
+uchar*
+attachscreen(Rectangle *r, ulong *chan, int* d, int *width, int *softscreen)
+{
+	*r = gscreen->r;
+	*d = gscreen->depth;
+	*chan = gscreen->chan;
+	*width = gscreen->width;
+	*softscreen = ScreenCached;
+
+	return (uchar*)gscreen->data->bdata;
+}
+
+void
+detachscreen(void)
+{
+}
+
+/*
+ *  write a string to the screen
+ */
+void
+screenputs(char *s, int n)
+{
+	int i;
+	Rune r;
+	char buf[4];
+
+	if(novgascreen || xgdata.bdata == nil || memdefont == nil)
+		return;
+	if(islo() == 0) {
+		/* don't deadlock trying to print in interrupt */
+		if(!canlock(&screenlock))
+			return;	
+	} else
+		lock(&screenlock);
+
+	while(n > 0) {
+		i = chartorune(&r, s);
+		if(i == 0){
+			s++;
+			--n;
+			continue;
+		}
+		memmove(buf, s, i);
+		buf[i] = 0;
+		n -= i;
+		s += i;
+		screenputc(buf);
+	}
+	/* Only OK for now */
+	flushmemscreen(gscreen->r);
+
+	unlock(&screenlock);
+}
+
+static void
+scroll(void)
+{
+	int o;
+	Point p;
+	Rectangle r;
+
+	o = 4*memdefont->height;
+	r = Rpt(window.min, Pt(window.max.x, window.max.y-o));
+	p = Pt(window.min.x, window.min.y+o);
+	memimagedraw(gscreen, r, gscreen, p, nil, p, SoverD);
+	flushmemscreen(r);
+	r = Rpt(Pt(window.min.x, window.max.y-o), window.max);
+	memimagedraw(gscreen, r, back, ZP, nil, ZP, SoverD);
+	flushmemscreen(r);
+
+	curpos.y -= o;
+}
+
+static void
+clearline(void)
+{
+	Rectangle r;
+	int yloc = curpos.y;
+
+	r = Rpt(Pt(window.min.x, window.min.y + yloc),
+		Pt(window.max.x, window.min.y+yloc+memdefont->height));
+	memimagedraw(gscreen, r, back, ZP, nil, ZP, SoverD);
+}
+
+static void
+screenputc(char *buf)
+{
+	Point p;
+	int h, w, pos;
+	Rectangle r;
+	static int *xp;
+	static int xbuf[256];
+
+	h = memdefont->height;
+	if(xp < xbuf || xp >= &xbuf[sizeof(xbuf)])
+		xp = xbuf;
+
+	switch(buf[0]) {
+	case '\n':
+		if(curpos.y+h >= window.max.y)
+			scroll();
+		curpos.y += h;
+		/* fall through */
+	case '\r':
+		xp = xbuf;
+		curpos.x = window.min.x;
+		break;
+	case '\t':
+		if(curpos.x == window.min.x)
+			clearline();
+		p = memsubfontwidth(memdefont, " ");
+		w = p.x;
+		*xp++ = curpos.x;
+		pos = (curpos.x-window.min.x)/w;
+		pos = 8-(pos%8);
+		r = Rect(curpos.x, curpos.y, curpos.x+pos*w, curpos.y+h);
+		memimagedraw(gscreen, r, back, ZP, nil, ZP, SoverD);
+		flushmemscreen(r);
+		curpos.x += pos*w;
+		break;
+	case '\b':
+		if(xp <= xbuf)
+			break;
+		xp--;
+		r = Rpt(Pt(*xp, curpos.y), Pt(curpos.x, curpos.y + h));
+		memimagedraw(gscreen, r, back, ZP, nil, ZP, SoverD);
+		flushmemscreen(r);
+		curpos.x = *xp;
+		break;
+	case '\0':
+		break;
+	default:
+		p = memsubfontwidth(memdefont, buf);
+		w = p.x;
+
+		if(curpos.x >= window.max.x-w)
+			screenputc("\n");
+
+		if(curpos.x == window.min.x)
+			clearline();
+		if(xp < xbuf+nelem(xbuf))
+			*xp++ = curpos.x;
+		r = Rect(curpos.x, curpos.y, curpos.x+w, curpos.y+h);
+		memimagedraw(gscreen, r, back, ZP, nil, ZP, SoverD);
+		memimagestring(gscreen, curpos, conscol, ZP, memdefont, buf);
+		flushmemscreen(r);
+		curpos.x += w;
+	}
+}
+
+int
+setcolor(ulong p, ulong r, ulong g, ulong b)
+{
+	ulong x;
+
+	if(gscreen->depth >= 8)
+		x = 0xFF;
+	else
+		x = 0xF;
+	p &= x;
+	p ^= x;
+	lock(&palettelock);
+	lcdsetrgb(p, r, g, b);
+	unlock(&palettelock);
+	return ~0;
+}
+
+void
+getcolor(ulong p, ulong *pr, ulong *pg, ulong *pb)
+{
+	/* TO DO */
+	*pr = *pg = *pb = 0;
+}
+
+/*
+ * See section 5.2.1 (page 5-6) of the MPC823 manual
+ */
+static uchar lcdclock[17] = {	/* (a<<2)|b => divisor of (1<<a)*((b<<1)+1) */
+	0, 0, (1<<2), 1,
+	(2<<2), 2, (1<<2)|1, 3,
+	(3<<2), (1<<2)|2, (1<<2)|2, (2<<2)|1,
+	(2<<2)|1, (1<<2)|3, (1<<2)|3, (4<<2),
+	(4<<2)
+};
+
+enum {
+	/* lccr */
+	Enable = 1<<0,
+
+	/* lchcr */
+	BigEndian = 1<<24,
+	AT7 = 7<<21,	/* access type */
+
+	/* sdcr */
+	LAM = 1<<6,	/* ``LCD aggressive mode'' */
+};
+
+/*
+ * initialise MPC8xx LCD controller incorporating board or display-specific values in Mode.lcd
+ */
+static void
+lcdinit(Mode *mode)
+{
+	IMM *io;
+	int i, d;
+	long hz;
+
+	io = m->iomem;
+	mode->aperture = xspanalloc(mode->x*mode->y, 16, 0);
+	mode->apsize = mode->x*mode->y;
+
+	io->sdcr = 1;	/* MPC823 errata: turn off LAM before disabling controller */
+	eieio();
+	io->lcfaa = PADDR(mode->aperture);
+	io->lccr = (((mode->x*mode->y*(1<<mode->d)+127)/128) << 17) | (mode->d << 5) | mode->lcd.flags;
+	switch(mode->d){
+	default:
+	case 0:
+		/* monochrome/greyscale identity map */
+		for(i=0; i<16; i++)
+			io->lcdmap[i] = i;
+		break;
+	case 2:
+		/* 4-bit grey scale map */
+		for(i=0; i<16; i++)
+			io->lcdmap[0] = (i<<8)|(i<<4)|i;
+		break;
+	case 3:
+		/* 8-bit linear map */
+		for(i=0; i<256; i++)
+			io->lcdmap[i] = (i<<8)|(i<<4)|i;
+		break;
+	}
+
+	io->lcvcr = (mode->y << 11) | (mode->lcd.vpw<<28) | (mode->lcd.ac<<21) | mode->lcd.wbf;
+	io->lchcr = (mode->x<<10) | BigEndian | mode->lcd.wbl;
+
+	hz = m->cpuhz;
+	d = hz/mode->lcd.freq;
+	if(hz/d > mode->lcd.freq)
+		d++;
+	if(d >= 16)
+		d = 16;
+
+	/*
+	 * enable LCD outputs
+	 */
+	io->pddat = 0;
+	lcdpdpar = 0x1fff & ~mode->lcd.notpdpar;
+	io->pdpar = lcdpdpar;
+	io->pddir = 0x1fff;
+	io->pbpar |= IBIT(31) | IBIT(19) | IBIT(17);
+	io->pbdir |= IBIT(31) | IBIT(19) | IBIT(17);
+	io->pbodr &= ~(IBIT(31) | IBIT(19) | IBIT(17));
+ 
+	/*
+	 * with the data cache off, early revisions of the 823 did not require
+	 * the `aggressive' DMA priority to avoid flicker, but flicker is obvious
+	 * on the 823A when the cache is on, so LAM is now set
+	 */
+	io->sdcr = (io->sdcr & ~0xF) | LAM;	/* LAM=1, LAID=0, RAID=0 */
+
+//	gscreen.width = gscreen.width;	/* access external memory before enabling (mpc823 errata) */
+	eieio();
+	io->sccrk = KEEP_ALIVE_KEY;
+	eieio();
+	io->sccr  = (io->sccr & ~0x1F) | lcdclock[d];
+	eieio();
+	io->sccrk= ~KEEP_ALIVE_KEY;
+	io->lcsr = 7;	/* clear status */
+	eieio();
+	io->lccr |= Enable;
+	archbacklight(1);
+}
+
+static void
+lcdsetrgb(int p, ulong r, ulong g, ulong b)
+{
+	r >>= 28;
+	g >>= 28;
+	b >>= 28;
+	m->iomem->lcdmap[p&0xFF] = (r<<8) | (g<<4) | b;
+}
+
+void
+blankscreen(int blank)
+{
+	USED(blank);	/* TO DO */
+}
+
+/*
+ * enable/disable LCD panel (eg, when using video subsystem)
+ */
+void
+lcdpanel(int on)
+{
+	IMM *io;
+
+	if(on){
+		archbacklight(1);
+		io = ioplock();
+		io->pddat = 0;
+		io->pdpar = lcdpdpar;
+		io->pddir = 0x1fff;
+		io->lccr |= Enable;
+		iopunlock();
+	}else{
+		io = ioplock();
+		io->sdcr = 1;	/* MPC823 errata: turn off LAM before disabling controller */
+		eieio();
+		io->pddir = 0;
+		eieio();
+		io->lccr &= ~Enable;
+		iopunlock();
+		archbacklight(0);
+	}
+}
+
+/*
+ *	Software cursor code.  Interim version (for baseline).
+ *	we may want to replace code here by memdraw primitives.
+ */
+
+enum {
+	CUR_ENA = 0x01,		/* cursor is enabled */
+	CUR_DRW = 0x02,		/* cursor is currently drawn */
+	CUR_SWP = 0x10,		/* bit swap */
+	CURSWID	= 16,
+	CURSHGT	= 16,
+};
+
+typedef struct SWcursor {
+	ulong	*fb;	/* screen frame buffer */
+	Rectangle r;
+	int	d;	/* ldepth of screen */
+	int 	width;	/* width of screen in ulongs */
+	int	x;
+	int	y;
+	int	hotx;
+	int	hoty;
+	uchar	cbwid;	/* cursor byte width */
+	uchar	f;	/* flags */
+	uchar	cwid;
+	uchar	chgt;
+	int	hidecount;
+	uchar	data[CURSWID*CURSHGT];
+	uchar	mask[CURSWID*CURSHGT];
+	uchar	save[CURSWID*CURSHGT];
+} SWcursor;
+
+static Rectangle cursoroffrect;
+static int	cursorisoff;
+
+static void swcursorflush(int, int);
+static void	swcurs_draw_or_undraw(SWcursor *);
+
+static void
+cursorupdate0(void)
+{
+	int inrect, x, y;
+	Point m;
+
+	m = mousexy();
+	x = m.x - swc->hotx;
+	y = m.y - swc->hoty;
+	inrect = (x >= cursoroffrect.min.x && x < cursoroffrect.max.x
+		&& y >= cursoroffrect.min.y && y < cursoroffrect.max.y);
+	if (cursorisoff == inrect)
+		return;
+	cursorisoff = inrect;
+	if (inrect)
+		swcurs_hide(swc);
+	else {
+		swc->hidecount = 0;
+		swcurs_draw_or_undraw(swc);
+	}
+	swcursorflush(m.x, m.y);
+}
+
+void
+cursorupdate(Rectangle r)
+{
+	lock(&screenlock);
+	r.min.x -= 16;
+	r.min.y -= 16;
+	cursoroffrect = r;
+	if (swc)
+		cursorupdate0();
+	unlock(&screenlock);
+}
+
+void
+cursorenable(void)
+{
+	Point m;
+
+	lock(&screenlock);
+	if(swc) {
+		swcurs_enable(swc);
+		m = mousexy();
+		swcursorflush(m.x, m.y);
+	}
+	unlock(&screenlock);
+}
+
+void
+cursordisable(void)
+{
+	Point m;
+
+	lock(&screenlock);
+	if(swc) {
+		swcurs_disable(swc);
+		m = mousexy();
+		swcursorflush(m.x, m.y);
+	}
+	unlock(&screenlock);
+}
+
+void
+drawcursor(Drawcursor* c)
+{
+	Point p;
+	Cursor curs, *cp;
+	int j, i, h, bpl;
+	uchar *bc, *bs, *cclr, *cset;
+
+	if(!swc)
+		return;
+
+	/* Set the default system cursor */
+	if(!c || c->data == nil)
+		cp = &arrow /*&crosshair_black*/;
+	else {
+		cp = &curs;
+		p.x = c->hotx;
+		p.y = c->hoty;
+		cp->offset = p;
+		bpl = bytesperline(Rect(c->minx, c->miny, c->maxx, c->maxy), 1);
+
+		h = (c->maxy-c->miny)/2;
+		if(h > 16)
+			h = 16;
+
+		bc = c->data;
+		bs = c->data + h*bpl;
+
+		cclr = cp->clr;
+		cset = cp->set;
+		for(i = 0; i < h; i++) {
+			for(j = 0; j < 2; j++) {
+				cclr[j] = bc[j];
+				cset[j] = bs[j];
+			}
+			bc += bpl;
+			bs += bpl;
+			cclr += 2;
+			cset += 2;
+		}
+	}
+
+	if(swc) {
+		swcurs_load(swc, cp);
+		p = mousexy();
+		swcursorflush(p.x, p.y);
+	}
+}
+
+SWcursor*
+swcurs_create(ulong *fb, int width, int ldepth, Rectangle r, int bitswap)
+{
+	SWcursor *swc = (SWcursor*)malloc(sizeof(SWcursor));
+	swc->fb = fb;
+	swc->r = r;
+	swc->d = ldepth;
+	swc->width = width;
+	swc->f = bitswap ? CUR_SWP : 0;
+	swc->x = swc->y = 0;
+	swc->hotx = swc->hoty = 0;
+	swc->hidecount = 0;
+	return swc;
+}
+
+void
+swcurs_destroy(SWcursor *swc)
+{
+	swcurs_disable(swc);
+	free(swc);
+}
+
+static void
+swcursorflush(int x, int y)
+{
+	Rectangle r;
+
+	/* XXX a little too paranoid here */
+	r.min.x = x-16;
+	r.min.y = y-16;
+	r.max.x = x+17;
+	r.max.y = y+17;
+	flushmemscreen(r);
+}
+
+static void
+swcurs_draw_or_undraw(SWcursor *swc)
+{
+	uchar *p;
+	uchar *cs;
+	int w, vw;
+	int x1 = swc->r.min.x;
+	int y1 = swc->r.min.y;
+	int x2 = swc->r.max.x;
+	int y2 = swc->r.max.y; 
+	int xp = swc->x - swc->hotx;
+	int yp = swc->y - swc->hoty;
+	int ofs;
+
+	if(((swc->f & CUR_ENA) && (swc->hidecount <= 0))
+			 == ((swc->f & CUR_DRW) != 0))
+		return;
+	w = swc->cbwid*BI2BY/(1 << swc->d);
+	x1 = xp < x1 ? x1 : xp;
+	y1 = yp < y1 ? y1 : yp;
+	x2 = xp+w >= x2 ? x2 : xp+w;
+	y2 = yp+swc->chgt >= y2 ? y2 : yp+swc->chgt;
+	if(x2 <= x1 || y2 <= y1)
+		return;
+	p = (uchar*)(swc->fb + swc->width*y1)
+		+ x1*(1 << swc->d)/BI2BY;
+	y2 -= y1;
+	x2 = (x2-x1)*(1 << swc->d)/BI2BY;
+	vw = swc->width*BY2WD - x2;
+	w = swc->cbwid - x2;
+	ofs = swc->cbwid*(y1-yp)+(x1-xp);
+	cs = swc->save + ofs;
+	if((swc->f ^= CUR_DRW) & CUR_DRW) {
+		uchar *cm = swc->mask + ofs; 
+		uchar *cd = swc->data + ofs;
+		while(y2--) {
+			x1 = x2;
+			while(x1--) {
+				*p = ((*cs++ = *p) & *cm++) ^ *cd++;
+				p++;
+			}
+			cs += w;
+			cm += w;
+			cd += w;
+			p += vw;
+		}
+	} else {
+		while(y2--) {
+			x1 = x2;
+			while(x1--) 
+				*p++ = *cs++;
+			cs += w;
+			p += vw;
+		}
+	}
+}
+
+void
+swcurs_hide(SWcursor *swc)
+{
+	++swc->hidecount;
+	swcurs_draw_or_undraw(swc);
+}
+
+void
+swcurs_unhide(SWcursor *swc)
+{
+	if (--swc->hidecount < 0)
+		swc->hidecount = 0;
+	swcurs_draw_or_undraw(swc);
+}
+
+void
+swcurs_enable(SWcursor *swc)
+{
+	swc->f |= CUR_ENA;
+	swcurs_draw_or_undraw(swc);
+}
+
+void
+swcurs_disable(SWcursor *swc)
+{
+	swc->f &= ~CUR_ENA;
+	swcurs_draw_or_undraw(swc);
+}
+
+void
+swcurs_load(SWcursor *swc, Cursor *c)
+{
+	int i, k;
+	uchar *bc, *bs, *cd, *cm;
+	static uchar bdv[4] = {0,Backgnd,Foregnd,0xff};
+	static uchar bmv[4] = {0xff,0,0,0xff};
+	int bits = 1<<swc->d;
+	uchar mask = (1<<bits)-1;
+	int bswp = (swc->f&CUR_SWP) ? 8-bits : 0;
+
+	bc = c->clr;
+	bs = c->set;
+
+	swcurs_hide(swc);
+	cd = swc->data;
+	cm = swc->mask;
+	swc->hotx = c->offset.x;
+	swc->hoty = c->offset.y;
+	swc->chgt = CURSHGT;
+	swc->cwid = CURSWID;
+	swc->cbwid = CURSWID*(1<<swc->d)/BI2BY;
+	for(i = 0; i < CURSWID/BI2BY*CURSHGT; i++) {
+		uchar bcb = *bc++;
+		uchar bsb = *bs++;
+		for(k=0; k<BI2BY;) {
+			uchar cdv = 0;
+			uchar cmv = 0;
+			int z;
+			for(z=0; z<BI2BY; z += bits) {
+				int n = ((bsb&(0x80))|((bcb&(0x80))<<1))>>7;
+				int s = z^bswp;
+				cdv |= (bdv[n]&mask) << s;
+				cmv |= (bmv[n]&mask) << s;
+				bcb <<= 1;
+				bsb <<= 1;
+				k++;
+			}
+			*cd++ = cdv;
+			*cm++ = cmv;
+		}
+	}
+	swcurs_unhide(swc);
+}
+
--- /dev/null
+++ b/os/mpc/screen.h
@@ -1,0 +1,60 @@
+enum {
+	Pcolours	= 256,		/* Palette */
+	Pred		= 0,
+	Pgreen		= 1,
+	Pblue		= 2,
+
+	Pblack		= 0x00,
+	Pwhite		= 0xFF,
+};
+
+typedef struct Cursor Cursor;
+struct	Cursor
+{
+	Point	offset;
+	uchar	clr[2*16];
+	uchar	set[2*16];
+};
+
+/*
+ * MPC8xx LCD controller
+ */
+typedef struct LCDconfig {
+	long	freq;	/* ideal panel frequency in Hz */
+	int	wbl;	/* wait between lines (shift/clk cycles) */
+	int	vpw;	/* vertical sync pulse width (lines) */
+	int	wbf;	/* wait between frames (lines) */
+	int	ac;	/* AC timing (frames) */
+	ulong	flags;
+	ulong	notpdpar;	/* reset mask for pdpar */
+} LCDconfig;
+
+enum {
+	/* lccr flags stored in LCDconfig.flags */
+	ClockLow = 1<<11,
+	OELow = 1<<10,
+	HsyncLow = 1<<9,
+	VsyncLow = 1<<8,
+	DataLow = 1<<7,
+	Passive8 = 1<<4,
+	DualScan = 1<<3,
+	IsColour = 1<<2,
+	IsTFT = 1<<1,
+};
+
+/*
+ * physical graphics device properties set by archlcdmode
+ */
+typedef struct Mode {
+	int	x;
+	int	y;
+	int	d;
+
+	uchar*	aperture;
+	int	apsize;
+	LCDconfig	lcd;
+} Mode;
+
+int	archlcdmode(Mode*);
+extern	Point	mousexy(void);
+extern void	blankscreen(int);
\ No newline at end of file
--- /dev/null
+++ b/os/mpc/spi.c
@@ -1,0 +1,243 @@
+#include "u.h"
+#include "../port/lib.h"
+#include "mem.h"
+#include "dat.h"
+#include "fns.h"
+#include "io.h"
+#include "../port/error.h"
+
+/*
+ * basic read/write interface to mpc8xx Serial Peripheral Interface;
+ * used by devtouch.c and devspi.c
+ */
+
+typedef struct Ctlr Ctlr;
+
+enum {
+	/* spi-specific BD flags */
+	BDContin=	1<<9,	/* continuous mode */
+	RxeOV=		1<<1,	/* overrun */
+	TxeUN=		1<<1,	/* underflow */
+	BDme=		1<<0,	/* multimaster error */
+	BDrxerr=		RxeOV|BDme,
+	BDtxerr=		TxeUN|BDme,
+
+	/* spmod */
+	MLoop=	1<<14,	/* loopback mode */
+	MClockInv= 1<<13,	/* inactive state of SPICLK is high */
+	MClockPhs= 1<<12,	/* SPCLK starts toggling at beginning of transfer */
+	MDiv16=	1<<11,	/* use BRGCLK/16 as input to SPI baud rate */
+	MRev=	1<<10,	/* normal operation */
+	MMaster=	1<<9,
+	MSlave=	0<<9,
+	MEnable=	1<<8,
+	/* LEN, PS fields */
+
+	/* spcom */
+	STR=		1<<7,	/* start transmit */
+
+	/* spie */
+	MME =	1<<5,
+	TXE =	1<<4,
+	BSY =	1<<2,
+	TXB =	1<<1,
+	RXB =	1<<0,
+
+	/* port B bits */
+	SPIMISO =	IBIT(28),	/* master mode input */
+	SPIMOSI = IBIT(29),	/* master mode output */
+	SPICLK = IBIT(30),
+
+	/* maximum SPI I/O (can change) */
+	Bufsize =	64,
+};
+
+/*
+ * SPI software structures
+ */
+
+struct Ctlr {
+	Lock;
+	QLock	io;
+	int	init;
+	SPI*	spi;
+	IOCparam*	sp;
+
+	BD*	rd;
+	BD*	td;
+	int	phase;
+	Rendez	r;
+	char*	txbuf;
+	char*	rxbuf;
+};
+
+static	Ctlr	spictlr[1];
+
+/* dcflush isn't needed if rxbuf and txbuf allocated in uncached IMMR memory */
+#define	DCFLUSH(a,n)
+
+static	void	interrupt(Ureg*, void*);
+
+/*
+ * called by the reset routine of any driver using the SPI
+ */
+void
+spireset(void)
+{
+	IMM *io;
+	SPI *spi;
+	IOCparam *sp;
+	CPMdev *cpm;
+	Ctlr *ctlr;
+
+	ctlr = spictlr;
+	if(ctlr->init)
+		return;
+	ctlr->init = 1;
+	cpm = cpmdev(CPspi);
+	spi = cpm->regs;
+	ctlr->spi = spi;
+	sp = cpm->param;
+	if(sp == nil){
+		print("SPI: can't allocate new parameter memory\n");
+		return;
+	}
+	ctlr->sp = sp;
+
+	if(ctlr->rxbuf == nil)
+		ctlr->rxbuf = cpmalloc(Bufsize, 2);
+	if(ctlr->txbuf == nil)
+		ctlr->txbuf = cpmalloc(Bufsize, 2);
+
+	if(ctlr->rd == nil){
+		ctlr->rd = bdalloc(1);
+		ctlr->rd->addr = PADDR(ctlr->rxbuf);
+		ctlr->rd->length = 0;
+		ctlr->rd->status = BDWrap;
+	}
+	if(ctlr->td == nil){
+		ctlr->td = bdalloc(1);
+		ctlr->td->addr = PADDR(ctlr->txbuf);
+		ctlr->td->length = 0;
+		ctlr->td->status = BDWrap|BDLast;
+	}
+
+	/* select port pins */
+	io = ioplock();
+	io->pbdir |= SPICLK | SPIMOSI | SPIMISO;
+	io->pbpar |= SPICLK | SPIMOSI | SPIMISO;
+	iopunlock();
+
+	/* explicitly initialise parameters, because InitRxTx can't be used (see i2c/spi relocation errata) */
+	sp = ctlr->sp;
+	sp->rbase = PADDR(ctlr->rd);
+	sp->tbase = PADDR(ctlr->td);
+	sp->rfcr = 0x18;
+	sp->tfcr = 0x18;
+	sp->mrblr = Bufsize;
+	sp->rstate = 0;
+	sp->rptr = 0;
+	sp->rbptr = sp->rbase;
+	sp->rcnt = 0;
+	sp->tstate = 0;
+	sp->tbptr = sp->tbase;
+	sp->tptr = 0;
+	sp->tcnt = 0;
+	eieio();
+
+	spi->spmode = MDiv16 | MRev | MMaster | ((8-1)<<4) | 1;	/* 8 bit characters */
+	if(0)
+		spi->spmode |= MLoop;	/* internal loop back mode for testing */
+
+	spi->spie = ~0;	/* clear events */
+	eieio();
+	spi->spim = MME|TXE|BSY|TXB|RXB;
+	eieio();
+	spi->spmode |= MEnable;
+
+	intrenable(VectorCPIC+cpm->irq, interrupt, spictlr, BUSUNKNOWN, "spi");
+
+}
+
+enum {
+	Idling,
+	Waitval,
+	Readyval
+};
+
+static void
+interrupt(Ureg*, void *arg)
+{
+	int events;
+	Ctlr *ctlr;
+	SPI *spi;
+
+	ctlr = arg;
+	spi = ctlr->spi;
+	events = spi->spie;
+	eieio();
+	spi->spie = events;
+	if(events & (MME|BSY|TXE)){
+		print("SPI#%x\n", events);
+		if(ctlr->phase != Idling){
+			ctlr->phase = Idling;
+			wakeup(&ctlr->r);
+		}
+	}else if(events & RXB){
+		if(ctlr->phase == Waitval){
+			ctlr->phase = Readyval;
+			wakeup(&ctlr->r);
+		}
+	}
+}
+
+static int
+done(void *a)
+{
+	return ((Ctlr*)a)->phase != Waitval;
+}
+
+/*
+ * send `nout' bytes on SPI from `out' and read as many bytes of reply into buffer `in';
+ * return the number of bytes received, or -1 on error.
+ */
+long
+spioutin(void *out, long nout, void *in)
+{
+	Ctlr *ctlr;
+	int nb, p;
+
+	ctlr = spictlr;
+	if(nout > Bufsize)
+		return -1;
+	qlock(&ctlr->io);
+	if(waserror()){
+		qunlock(&ctlr->io);
+		return -1;
+	}
+	if(ctlr->phase != Idling)
+		sleep(&ctlr->r, done, ctlr);
+	memmove(ctlr->txbuf, out, nout);
+	DCFLUSH(ctlr->txbuf, Bufsize);
+	DCFLUSH(ctlr->rxbuf, Bufsize);
+	ilock(ctlr);
+	ctlr->phase = Waitval;
+	ctlr->td->length = nout;
+	ctlr->rd->status = BDEmpty|BDWrap|BDInt;
+	ctlr->td->status = BDReady|BDWrap|BDLast;
+	eieio();
+	ctlr->spi->spcom = STR;
+	eieio();
+	iunlock(ctlr);
+	sleep(&ctlr->r, done, ctlr);
+	nb = ctlr->rd->length;
+	if(nb > nout)
+		nb = nout;	/* shouldn't happen */
+	p = ctlr->phase;
+	poperror();
+	qunlock(&ctlr->io);
+	if(p != Readyval)
+		return -1;
+	memmove(in, ctlr->rxbuf, nb);
+	return nb;
+}
--- /dev/null
+++ b/os/mpc/trap.c
@@ -1,0 +1,554 @@
+#include	"u.h"
+#include	"../port/lib.h"
+#include	"mem.h"
+#include	"dat.h"
+#include	"fns.h"
+#include	"ureg.h"
+#include	"io.h"
+#include	"../port/error.h"
+
+
+enum 
+{
+	Maxhandler=	MaxVector		/* max number of interrupt handlers */
+};
+
+typedef struct Handler	Handler;
+struct Handler
+{
+	void	(*r)(Ureg*, void*);
+	void	*arg;
+	char	name[KNAMELEN];
+	Handler	*next;
+	ulong	nintr;
+	ulong	ticks;
+	int	maxtick;
+};
+
+struct
+{
+	Handler	*ivec[MaxVector];
+	Handler	h[Maxhandler];
+	int	free;
+	Handler*	freelist;
+} halloc;
+
+Instr BREAK = 0x7fe00008;
+int (*breakhandler)(Ureg*, Proc*);
+
+void	kernfault(Ureg*, int);
+
+char *excname[] =
+{
+	"reserved 0",
+	"system reset",
+	"machine check",
+	"data access",
+	"instruction access",
+	"external interrupt",
+	"alignment",
+	"program exception",
+	"floating-point unavailable",
+	"decrementer",
+	"i/o controller interface error",
+	"reserved B",
+	"system call",
+	"trace trap",
+	"floating point assist",
+	"reserved F",
+	"software emulation",
+	"ITLB miss",
+	"DTLB miss",
+	"ITLB error",
+	"DTLB error",
+	"reserved 15",
+	"reserved 16",
+	"reserved 17",
+	"reserved 18",
+	"reserved 19",
+	"reserved 1A",
+	"reserved 1B",
+	"data breakpoint",
+	"instruction breakpoint",
+	"peripheral breakpoint",
+	"development port",
+	/* the following are made up on a program exception */
+	"floating point exception",		/* 20: FPEXC */
+	"illegal instruction",	/* 21 */
+	"privileged instruction",	/* 22 */
+	"trap",	/* 23 */
+	"illegal operation",	/* 24 */
+	"breakpoint",	/* 25 */
+};
+
+char *fpcause[] =
+{
+	"inexact operation",
+	"division by zero",
+	"underflow",
+	"overflow",
+	"invalid operation",
+};
+char	*fpexcname(Ureg*, ulong, char*);
+#define FPEXPMASK	0xfff80300		/* Floating exception bits in fpscr */
+
+
+char *regname[]={
+	"CAUSE",	"SRR1",
+	"PC",		"GOK",
+	"LR",		"CR",
+	"XER",	"CTR",
+	"R0",		"R1",
+	"R2",		"R3",
+	"R4",		"R5",
+	"R6",		"R7",
+	"R8",		"R9",
+	"R10",	"R11",
+	"R12",	"R13",
+	"R14",	"R15",
+	"R16",	"R17",
+	"R18",	"R19",
+	"R20",	"R21",
+	"R22",	"R23",
+	"R24",	"R25",
+	"R26",	"R27",
+	"R28",	"R29",
+	"R30",	"R31",
+};
+
+void
+sethvec(int v, void (*r)(void))
+{
+	ulong *vp, pa, o;
+
+	vp = (ulong*)KADDR(v);
+	vp[0] = 0x7c1043a6;	/* MOVW R0, SPR(SPRG0) */
+	vp[1] = 0x7c0802a6;	/* MOVW LR, R0 */
+	vp[2] = 0x7c1243a6;	/* MOVW R0, SPR(SPRG2) */
+	pa = PADDR(r);
+	o = pa >> 25;
+	if(o != 0 && o != 0x7F){
+		/* a branch too far: running from ROM */
+		vp[3] = (15<<26)|(pa>>16);	/* MOVW $r&~0xFFFF, R0 */
+		vp[4] = (24<<26)|(pa&0xFFFF);	/* OR $r&0xFFFF, R0 */
+		vp[5] = 0x7c0803a6;	/* MOVW	R0, LR */
+		vp[6] = 0x4e800021;	/* BL (LR) */
+	}else
+		vp[3] = (18<<26)|(pa&0x3FFFFFC)|3;	/* bla */
+	dcflush(vp, 8*sizeof(ulong));
+}
+
+void
+sethvec2(int v, void (*r)(void))
+{
+	ulong *vp;
+
+	vp = (ulong*)KADDR(v);
+	vp[0] = (18<<26)|((ulong)r&~KSEGM)|2;	/* ba */
+	dcflush(vp, sizeof(*vp));
+}
+
+void
+trap(Ureg *ur)
+{
+	int ecode, s;
+	ulong w;
+	char buf[ERRMAX];
+
+	ecode = ur->cause >> 8;
+	if(ecode < 0 || ecode >= 0x1F)
+		ecode = 0x1F;
+	switch(ecode){
+	case CDEC:
+		clockintr(ur);
+		preemption(1);
+		break;
+
+	case CMCHECK:
+	case CDSI:
+	case CISI:
+	case CIMISS:
+	case CDMISS:
+	case CITLBE:
+	case CDTLBE:
+		faultpower(ur);
+		break;
+
+	case CEMU:
+		if(up == nil)
+			goto Default;
+		if((ulong)(ur+1) != ur->r1)
+			panic("fp emu stack");
+		spllo();
+		if(waserror()){
+			if(up->type == Interp)
+				disfault(ur, up->env->errstr);
+			panic("%s", up->env->errstr);
+		}
+		if(fpipower(ur) == 0){
+			splhi();
+			poperror();
+			print("pc=#%lux op=#%8.8lux\n", ur->pc, *(ulong*)ur->pc);
+			goto Default;
+		}
+		poperror();
+		break;
+
+	case CPROG:
+		if(ur->status & (1<<19)) {
+			ecode = 0x20;
+			w = ur->pc;
+			if(ur->status & (1<<16))
+				w += 4;
+			if(*(ulong*)w == 0x7fe00008){ /* tw 31,0,0 */
+				if(breakhandler){
+					s = (*breakhandler)(ur, up);
+					if(s == BrkSched){
+						if(up){
+							up->preempted = 0;
+							sched();
+							splhi();
+						}
+					}else if(s == BrkNoSched){
+						if(up){
+							up->preempted = 1;	/* stop it being preempted until next instruction */
+							up->dbgreg = 0;
+						}
+					}
+					break;
+				}
+				ecode = 0x1D;	/* breakpoint */
+			}
+		}
+		if(ur->status & (1<<18))
+			ecode = 0x21;
+		if(ur->status & (1<<17))
+			ecode = 0x22;
+		/* FALL THROUGH */
+
+	Default:
+	default:
+		if(up && up->type == Interp) {
+			spllo();
+			snprint(buf, sizeof buf, "sys: trap: %s pc=0x%lux", excname[ecode], ur->pc);
+			error(buf);
+			break;
+		}
+		print("kernel %s pc=0x%lux\n", excname[ecode], ur->pc);
+		dumpregs(ur);
+		dumpstack();
+		if(m->machno == 0)
+			spllo();
+		exit(1);
+	}
+
+	splhi();
+}
+
+void
+spurious(Ureg *ur, void *a)
+{
+	USED(a);
+	print("SPURIOUS interrupt pc=0x%lux cause=0x%lux\n",
+		ur->pc, ur->cause);
+	panic("bad interrupt");
+}
+
+#define	LEV(n)	(((n)<<1)|1)
+#define	IRQ(n)	(((n)<<1)|0)
+
+Lock	veclock;
+
+void
+trapinit(void)
+{
+	int i;
+	IMM *io;
+
+
+	io = m->iomem;
+	io->simask = 0;	/* mask all */
+	io->siel = ~0;	/* edge sensitive, wake on all */
+	io->cicr = 0;	/* disable CPM interrupts */
+	io->cipr = ~0;	/* clear all interrupts */
+	io->cimr = 0;	/* mask all events */
+	io->cicr = (0xE1<<16)|(CPIClevel<<13)|(0x1F<<8);
+	io->cicr |= 1 << 7;	/* enable */
+	io->tbscrk = KEEP_ALIVE_KEY;
+	io->tbscr = 1;	/* TBE */
+	io->simask |= 1<<(31-LEV(CPIClevel));	/* CPM's level */
+	io->tbk = KEEP_ALIVE_KEY;
+	eieio();
+	putdec(~0);
+
+	/*
+	 * set all exceptions to trap
+	 */
+	for(i = 0x0; i < 0x3000; i += 0x100)
+		sethvec(i, trapvec);
+
+	sethvec(CEI<<8, intrvec);
+	//sethvec2(CIMISS<<8, itlbmiss);
+	//sethvec2(CDMISS<<8, dtlbmiss);
+}
+
+void
+intrenable(int v, void (*r)(Ureg*, void*), void *arg, int, char *name)
+{
+	Handler *h;
+	IMM *io;
+
+	v -= VectorPIC;
+	if(v < 0 || v >= nelem(halloc.ivec))
+		panic("intrenable(%d)", v+VectorPIC);
+	ilock(&veclock);
+	if((h = halloc.freelist) == nil){
+		if(halloc.free >= Maxhandler){
+			iunlock(&veclock);
+			panic("out of interrupt handlers");
+		}
+		h = &halloc.h[halloc.free++];
+	}else
+		halloc.freelist = h->next;
+	h->r = r;
+	h->arg = arg;
+	strncpy(h->name, name, KNAMELEN-1);
+	h->name[KNAMELEN-1] = 0;
+	h->next = halloc.ivec[v];
+	halloc.ivec[v] = h;
+
+	/*
+	 * enable corresponding interrupt in SIU/CPM
+	 */
+
+	eieio();
+	io = m->iomem;
+	if(v >= VectorCPIC){
+		v -= VectorCPIC;
+		io->cimr |= 1<<(v&0x1F);
+	}
+	else if(v >= VectorIRQ)
+		io->simask |= 1<<(31-IRQ(v&7));
+	else
+		io->simask |= 1<<(31-LEV(v));
+	eieio();
+	iunlock(&veclock);
+}
+
+static void
+irqdisable(int v)
+{
+	IMM *io;
+
+	io = m->iomem;
+	if(v >= VectorCPIC){
+		v -= VectorCPIC;
+		io->cimr &= ~(1<<(v&0x1F));
+	}
+	else if(v >= VectorIRQ)
+		io->simask &= ~(1<<(31-IRQ(v&7)));
+	else
+		io->simask &= ~(1<<(31-LEV(v)));
+}
+
+void
+intrdisable(int v, void (*r)(Ureg*, void*), void *arg, int, char *name)
+{
+	Handler *h, **hp;
+
+	v -= VectorPIC;
+	if(v < 0 || v >= nelem(halloc.ivec))
+		panic("intrenable(%d)", v+VectorPIC);
+	ilock(&veclock);
+	for(hp = &halloc.ivec[v]; (h = *hp) != nil; hp = &h->next)
+		if(h->r == r && h->arg == arg && strcmp(h->name, name) == 0){
+			*hp = h->next;
+			h->next = halloc.freelist;
+			halloc.freelist = h;
+			break;
+		}
+	if(halloc.ivec[v] == nil)
+		irqdisable(v);
+	iunlock(&veclock);
+}
+
+/*
+ * called directly by l.s:/intrvec.  on a multiprocessor we'd need to lock veclock.
+ */
+void
+intr(Ureg *ur)
+{
+	int b, v;
+	IMM *io;
+	Handler *h;
+	long t0;
+	Proc *oup;
+
+	ur->cause &= ~0xff;
+	io = m->iomem;
+	b = io->sivec>>2;
+	v = b>>1;
+	if(b & 1) {
+		if(v == CPIClevel){
+			io->civr = 1;
+			eieio();
+			v = VectorCPIC+(io->civr>>11);
+		}
+	}else{
+		v += VectorIRQ;
+		if(io->siel & (1<<(31-b))){
+			io->sipend |= 1<<(31-b);
+			eieio();
+		}
+	}
+
+	ur->cause |= v;
+	h = halloc.ivec[v];
+	if(h == nil){
+		iprint("unknown interrupt %d pc=0x%lux\n", v, ur->pc);
+		irqdisable(v);
+		return;
+	}
+
+	/*
+	 *  call the interrupt handlers
+	 */
+	oup = up;
+	up = nil;	/* no process at interrupt level */
+	do {
+		h->nintr++;
+		t0 = getdec();
+		(*h->r)(ur, h->arg);
+		t0 -= getdec();
+		h->ticks += t0;
+		if(h->maxtick < t0)
+			h->maxtick = t0;
+		h = h->next;
+	} while(h != nil);
+	if(v >= VectorCPIC)
+		io->cisr |= 1<<(v-VectorCPIC);
+	eieio();
+	up = oup;
+	preemption(0);
+}
+
+int
+intrstats(char *buf, int bsize)
+{
+	Handler *h;
+	int i, n;
+
+	n = 0;
+	for(i=0; i<nelem(halloc.ivec) && n < bsize; i++)
+		if((h = halloc.ivec[i]) != nil && h->nintr)
+			n += snprint(buf+n, bsize-n, "%3d %lud %lud %ud\n", i, h->nintr, h->ticks, h->maxtick);
+	return n;
+}
+
+char*
+fpexcname(Ureg *ur, ulong fpscr, char *buf)
+{
+	int i;
+	char *s;
+	ulong fppc;
+
+	fppc = ur->pc;
+	s = 0;
+	fpscr >>= 3;		/* trap enable bits */
+	fpscr &= (fpscr>>22);	/* anded with exceptions */
+	for(i=0; i<5; i++)
+		if(fpscr & (1<<i))
+			s = fpcause[i];
+	if(s == 0)
+		return "no floating point exception";
+	sprint(buf, "%s fppc=0x%lux", s, fppc);
+	return buf;
+}
+
+#define KERNPC(x)	(KTZERO<(ulong)(x)&&(ulong)(x)<(ulong)etext)
+
+void
+kernfault(Ureg *ur, int code)
+{
+	Label l;
+
+	print("panic: kfault %s dar=0x%lux\n", excname[code], getdar());
+	print("u=0x%lux status=0x%lux pc=0x%lux sp=0x%lux\n",
+				up, ur->status, ur->pc, ur->sp);
+	dumpregs(ur);
+	l.sp = ur->sp;
+	l.pc = ur->pc;
+	dumpstack();
+	setpri(PriBackground);		/* Let the debugger in */
+	for(;;)
+		sched();
+}
+
+void
+dumpstack(void)
+{
+	ulong l, v;
+	int i;
+
+	if(up == 0)
+		return;
+	i = 0;
+	for(l=(ulong)&l; l<(ulong)(up->kstack+KSTACK); l+=4){
+		v = *(ulong*)l;
+		if(KTZERO < v && v < (ulong)etext){
+			print("%lux=%lux, ", l, v);
+			if(i++ == 4){
+				print("\n");
+				i = 0;
+			}
+		}
+	}
+}
+
+void
+dumpregs(Ureg *ur)
+{
+	int i;
+	ulong *l;
+	if(up) {
+		print("registers for %s %ld\n", up->text, up->pid);
+		if(ur->usp < (ulong)up->kstack ||
+		   ur->usp > (ulong)up->kstack+KSTACK)
+			print("invalid stack ptr\n");
+	}
+	else
+		print("registers for kernel\n");
+
+	l = &ur->cause;
+	for(i=0; i<sizeof regname/sizeof(char*); i+=2, l+=2)
+		print("%s\t%.8lux\t%s\t%.8lux\n", regname[i], l[0], regname[i+1], l[1]);
+}
+
+static void
+linkproc(void)
+{
+	spllo();
+	(*up->kpfun)(up->arg);
+	pexit("", 0);
+}
+
+void
+kprocchild(Proc *p, void (*func)(void*), void *arg)
+{
+	p->sched.pc = (ulong)linkproc;
+	p->sched.sp = (ulong)p->kstack+KSTACK;
+
+	p->kpfun = func;
+	p->arg = arg;
+}
+
+void
+setpanic(void)
+{
+	consoleprint = 1;
+}
+
+void
+dumplongs(char*, ulong*, int)
+{
+}
--- /dev/null
+++ b/os/mpc/usb.h
@@ -1,0 +1,62 @@
+/*
+ * USB packet definitions
+ */
+
+#define	GET2(p)	((((p)[1]&0xFF)<<8)|((p)[0]&0xFF))
+#define	PUT2(p,v)	{((p)[0] = (v)); ((p)[1] = (v)>>8);}
+
+enum {
+	/* request type */
+	RH2D = 0<<7,
+	RD2H = 1<<7,
+	Rstandard = 0<<5,
+	Rclass = 1<<5,
+	Rvendor = 2<<5,
+	Rdevice = 0,
+	Rinterface = 1,
+	Rendpt = 2,
+	Rother = 3,
+
+	/* standard requests */
+	GET_STATUS = 0,
+	CLEAR_FEATURE = 1,
+	SET_FEATURE = 3,
+	SET_ADDRESS = 5,
+	GET_DESCRIPTOR = 6,
+	SET_DESCRIPTOR = 7,
+	GET_CONFIGURATION = 8,
+	SET_CONFIGURATION = 9,
+	GET_INTERFACE = 10,
+	SET_INTERFACE = 11,
+	SYNCH_FRAME = 12,
+
+	/* hub class feature selectors */
+	C_HUB_LOCAL_POWER = 0,
+	C_HUB_OVER_CURRENT,
+	PORT_CONNECTION = 0,
+	PORT_ENABLE = 1,
+	PORT_SUSPEND = 2,
+	PORT_OVER_CURRENT = 3,
+	PORT_RESET = 4,
+	PORT_POWER = 8,
+	PORT_LOW_SPEED = 9,
+	C_PORT_CONNECTION = 16,
+	C_PORT_ENABLE,
+	C_PORT_SUSPEND,
+	C_PORT_OVER_CURRENT,
+	C_PORT_RESET,
+
+	/* descriptor types */
+	DEVICE = 1,
+	CONFIGURATION = 2,
+	STRING = 3,
+	INTERFACE = 4,
+	ENDPOINT = 5,
+	HID = 0x21,
+	REPORT = 0x22,
+	PHYSICAL = 0x23,
+
+	/* feature selectors */
+	DEVICE_REMOTE_WAKEUP = 1,
+	ENDPOINT_STALL = 0,
+};
--- /dev/null
+++ b/os/omap/README
@@ -1,0 +1,4 @@
+TI OMAP platform
+
+This is not currently being distributed (mainly for lack of a commonly-available
+target).  Ask us about it if interested.
--- /dev/null
+++ b/os/pc/NOTICE
@@ -1,0 +1,8 @@
+Most of these files have been issued at some time under the Lucent Public License 1.02,
+except devbench.c, fpi387.c, flashzpc.c, devtv.c, devmouse.c, devmpeg.c (which are Vita Nuova MIT-template).
+
+The copyright is not necessarily Lucent's in all cases (eg, ether83815.c, sd53c8xx.c,
+and much of the USB and wavelan support) because some things were written outside Lucent.
+A more accurate summary will appear here shortly.
+Meanwhile, if you'd like to use the software in some other program,
+you can always ask us to work out the detailed appropriate copyright notice.
--- /dev/null
+++ b/os/pc/README
@@ -1,0 +1,9 @@
+PC kernel
+
+The PC kernel is currently being updated to bring it up-to-date with
+Plan 9 support (the original Inferno PC kernel was derived from an
+extended subset of an earlier edition of Plan 9).  In particular,
+although it can be configured as a server, graphics support is
+still being provided.  (The kernel level code in vga*.c, devvga.c and screen.c
+is probably near the final version, but some Limbo code is needed to
+set the right graphics modes.)
--- /dev/null
+++ b/os/pc/apbootstrap.h
@@ -1,0 +1,15 @@
+uchar apbootstrap[]={
+0xea,0x14,0x10,0x00,0x00,0x90,0x90,0x90,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+0x00,0x00,0x00,0x00,0x8c,0xc8,0x8e,0xd8,0x0f,0x01,0x16,0xac,0x10,0x0f,0x20,0xc0,
+0x83,0xc8,0x01,0x0f,0x22,0xc0,0xeb,0x00,0xb8,0x08,0x00,0x8e,0xd8,0x8e,0xc0,0x8e,
+0xe0,0x8e,0xe8,0x8e,0xd0,0x66,0xea,0x3d,0x10,0x00,0x00,0x10,0x00,0x8b,0x0d,0x0c,
+0x10,0x00,0x00,0x8b,0x91,0x00,0x08,0x00,0x00,0x89,0x11,0x0f,0x22,0xd9,0x0f,0x20,
+0xc2,0x81,0xca,0x00,0x00,0x01,0x80,0x81,0xe2,0xf5,0xff,0xff,0x9f,0xb8,0x67,0x10,
+0x00,0x80,0x0f,0x22,0xc2,0xff,0xe0,0x89,0xc8,0x0d,0x00,0x00,0x00,0x80,0xc7,0x00,
+0x00,0x00,0x00,0x00,0x0f,0x22,0xd9,0xbc,0xfc,0x5f,0x00,0x80,0x31,0xc0,0x50,0x9d,
+0x8b,0x05,0x10,0x10,0x00,0x80,0x89,0x04,0x24,0x8b,0x05,0x08,0x10,0x00,0x80,0xff,
+0xd0,0xf4,0xeb,0xfd,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xff,0xff,0x00,0x00,
+0x00,0x92,0xcf,0x00,0xff,0xff,0x00,0x00,0x00,0x9a,0xcf,0x00,0x17,0x00,0x94,0x10,
+0x00,0x00,
+
+};
--- /dev/null
+++ b/os/pc/apbootstrap.s
@@ -1,0 +1,110 @@
+#include "mem.h"
+
+#define NOP		BYTE $0x90		/* NOP */
+#define LGDT(gdtptr)	BYTE $0x0F;		/* LGDT */			\
+			BYTE $0x01; BYTE $0x16;					\
+			WORD $gdtptr
+#define FARJUMP16(s, o)	BYTE $0xEA;		/* far jump to ptr16:16 */	\
+			WORD $o; WORD $s;					\
+			NOP; NOP; NOP
+#define FARJUMP32(s, o)	BYTE $0x66;		/* far jump to ptr32:16 */	\
+			BYTE $0xEA; LONG $o; WORD $s
+
+#define	DELAY		BYTE $0xEB;		/* JMP .+2 */			\
+			BYTE $0x00
+#define INVD		BYTE $0x0F; BYTE $0x08
+#define WBINVD		BYTE $0x0F; BYTE $0x09
+
+/*
+ * Macros for calculating offsets within the page directory base
+ * and page tables. Note that these are assembler-specific hence
+ * the '<<2'.
+ */
+#define PDO(a)		(((((a))>>22) & 0x03FF)<<2)
+#define PTO(a)		(((((a))>>12) & 0x03FF)<<2)
+
+/*
+ * Start an Application Processor. This must be placed on a 4KB boundary
+ * somewhere in the 1st MB of conventional memory (APBOOTSTRAP). However,
+ * due to some shortcuts below it's restricted further to within the 1st
+ * 64KB. The AP starts in real-mode, with
+ *   CS selector set to the startup memory address/16;
+ *   CS base set to startup memory address;
+ *   CS limit set to 64KB;
+ *   CPL and IP set to 0.
+ */
+TEXT apbootstrap(SB), $0
+	FARJUMP16(0, _apbootstrap(SB))
+TEXT _apvector(SB), $0				/* address APBOOTSTRAP+0x08 */
+	LONG $0
+TEXT _appdb(SB), $0				/* address APBOOTSTRAP+0x0C */
+	LONG $0
+TEXT _apapic(SB), $0				/* address APBOOTSTRAP+0x10 */
+	LONG $0
+TEXT _apbootstrap(SB), $0			/* address APBOOTSTRAP+0x14 */
+	MOVW	CS, AX
+	MOVW	AX, DS				/* initialise DS */
+
+	LGDT(gdtptr(SB))			/* load a basic gdt */
+
+	MOVL	CR0, AX
+	ORL	$1, AX
+	MOVL	AX, CR0				/* turn on protected mode */
+	DELAY					/* JMP .+2 */
+
+	BYTE $0xB8; WORD $SELECTOR(1, SELGDT, 0)/* MOVW $SELECTOR(1, SELGDT, 0), AX */
+	MOVW	AX, DS
+	MOVW	AX, ES
+	MOVW	AX, FS
+	MOVW	AX, GS
+	MOVW	AX, SS
+
+	FARJUMP32(SELECTOR(2, SELGDT, 0), _ap32-KZERO(SB))
+
+/*
+ * For Pentiums and higher, the code that enables paging must come from
+ * pages that are identity mapped. 
+ * To this end double map KZERO at virtual 0 and undo the mapping once virtual
+ * nirvana has been obtained.
+ */
+TEXT _ap32(SB), $0
+	MOVL	_appdb-KZERO(SB), CX		/* physical address of PDB */
+	MOVL	(PDO(KZERO))(CX), DX		/* double-map KZERO at 0 */
+	MOVL	DX, (PDO(0))(CX)
+	MOVL	CX, CR3				/* load and flush the mmu */
+
+	MOVL	CR0, DX
+	ORL	$0x80010000, DX			/* PG|WP */
+	ANDL	$~0x6000000A, DX		/* ~(CD|NW|TS|MP) */
+
+	MOVL	$_appg(SB), AX
+	MOVL	DX, CR0				/* turn on paging */
+	JMP*	AX
+
+TEXT _appg(SB), $0
+	MOVL	CX, AX				/* physical address of PDB */
+	ORL	$KZERO, AX
+	MOVL	$0, (PDO(0))(AX)		/* undo double-map of KZERO at 0 */
+	MOVL	CX, CR3				/* load and flush the mmu */
+
+	MOVL	$(MACHADDR+MACHSIZE-4), SP
+
+	MOVL	$0, AX
+	PUSHL	AX
+	POPFL
+
+	MOVL	_apapic(SB), AX
+	MOVL	AX, (SP)
+	MOVL	_apvector(SB), AX
+	CALL*	AX
+_aphalt:
+	HLT
+	JMP	_aphalt
+
+TEXT gdt(SB), $0
+	LONG $0x0000; LONG $0
+	LONG $0xFFFF; LONG $(SEGG|SEGB|(0xF<<16)|SEGP|SEGPL(0)|SEGDATA|SEGW)
+	LONG $0xFFFF; LONG $(SEGG|SEGD|(0xF<<16)|SEGP|SEGPL(0)|SEGEXEC|SEGR)
+TEXT gdtptr(SB), $0
+	WORD	$(3*8-1)
+	LONG	$gdt-KZERO(SB)
--- /dev/null
+++ b/os/pc/apic.c
@@ -1,0 +1,378 @@
+#include "u.h"
+#include "../port/lib.h"
+#include "mem.h"
+#include "dat.h"
+#include "fns.h"
+#include "io.h"
+
+#include "mp.h"
+
+enum {					/* Local APIC registers */
+	LapicID		= 0x0020,	/* ID */
+	LapicVER	= 0x0030,	/* Version */
+	LapicTPR	= 0x0080,	/* Task Priority */
+	LapicAPR	= 0x0090,	/* Arbitration Priority */
+	LapicPPR	= 0x00A0,	/* Processor Priority */
+	LapicEOI	= 0x00B0,	/* EOI */
+	LapicLDR	= 0x00D0,	/* Logical Destination */
+	LapicDFR	= 0x00E0,	/* Destination Format */
+	LapicSVR	= 0x00F0,	/* Spurious Interrupt Vector */
+	LapicISR	= 0x0100,	/* Interrupt Status (8 registers) */
+	LapicTMR	= 0x0180,	/* Trigger Mode (8 registers) */
+	LapicIRR	= 0x0200,	/* Interrupt Request (8 registers) */
+	LapicESR	= 0x0280,	/* Error Status */
+	LapicICRLO	= 0x0300,	/* Interrupt Command */
+	LapicICRHI	= 0x0310,	/* Interrupt Command [63:32] */
+	LapicTIMER	= 0x0320,	/* Local Vector Table 0 (TIMER) */
+	LapicPCINT	= 0x0340,	/* Performance COunter LVT */
+	LapicLINT0	= 0x0350,	/* Local Vector Table 1 (LINT0) */
+	LapicLINT1	= 0x0360,	/* Local Vector Table 2 (LINT1) */
+	LapicERROR	= 0x0370,	/* Local Vector Table 3 (ERROR) */
+	LapicTICR	= 0x0380,	/* Timer Initial Count */
+	LapicTCCR	= 0x0390,	/* Timer Current Count */
+	LapicTDCR	= 0x03E0,	/* Timer Divide Configuration */
+};
+
+enum {					/* LapicSVR */
+	LapicENABLE	= 0x00000100,	/* Unit Enable */
+	LapicFOCUS	= 0x00000200,	/* Focus Processor Checking Disable */
+};
+
+enum {					/* LapicICRLO */
+					/* [14] IPI Trigger Mode Level (RW) */
+	LapicDEASSERT	= 0x00000000,	/* Deassert level-sensitive interrupt */
+	LapicASSERT	= 0x00004000,	/* Assert level-sensitive interrupt */
+
+					/* [17:16] Remote Read Status */
+	LapicINVALID	= 0x00000000,	/* Invalid */
+	LapicWAIT	= 0x00010000,	/* In-Progress */
+	LapicVALID	= 0x00020000,	/* Valid */
+
+					/* [19:18] Destination Shorthand */
+	LapicFIELD	= 0x00000000,	/* No shorthand */
+	LapicSELF	= 0x00040000,	/* Self is single destination */
+	LapicALLINC	= 0x00080000,	/* All including self */
+	LapicALLEXC	= 0x000C0000,	/* All Excluding self */
+};
+
+enum {					/* LapicESR */
+	LapicSENDCS	= 0x00000001,	/* Send CS Error */
+	LapicRCVCS	= 0x00000002,	/* Receive CS Error */
+	LapicSENDACCEPT	= 0x00000004,	/* Send Accept Error */
+	LapicRCVACCEPT	= 0x00000008,	/* Receive Accept Error */
+	LapicSENDVECTOR	= 0x00000020,	/* Send Illegal Vector */
+	LapicRCVVECTOR	= 0x00000040,	/* Receive Illegal Vector */
+	LapicREGISTER	= 0x00000080,	/* Illegal Register Address */
+};
+
+enum {					/* LapicTIMER */
+					/* [17] Timer Mode (RW) */
+	LapicONESHOT	= 0x00000000,	/* One-shot */
+	LapicPERIODIC	= 0x00020000,	/* Periodic */
+
+					/* [19:18] Timer Base (RW) */
+	LapicCLKIN	= 0x00000000,	/* use CLKIN as input */
+	LapicTMBASE	= 0x00040000,	/* use TMBASE */
+	LapicDIVIDER	= 0x00080000,	/* use output of the divider */
+};
+
+enum {					/* LapicTDCR */
+	LapicX2		= 0x00000000,	/* divide by 2 */
+	LapicX4		= 0x00000001,	/* divide by 4 */
+	LapicX8		= 0x00000002,	/* divide by 8 */
+	LapicX16	= 0x00000003,	/* divide by 16 */
+	LapicX32	= 0x00000008,	/* divide by 32 */
+	LapicX64	= 0x00000009,	/* divide by 64 */
+	LapicX128	= 0x0000000A,	/* divide by 128 */
+	LapicX1		= 0x0000000B,	/* divide by 1 */
+};
+
+static ulong* lapicbase;
+
+struct
+{
+	uvlong	hz;
+	ulong	max;
+	ulong	min;
+	ulong	div;
+} lapictimer;
+
+static int
+lapicr(int r)
+{
+	return *(lapicbase+(r/sizeof(*lapicbase)));
+}
+
+static void
+lapicw(int r, int data)
+{
+	*(lapicbase+(r/sizeof(*lapicbase))) = data;
+	data = *(lapicbase+(LapicID/sizeof(*lapicbase)));
+	USED(data);
+}
+
+void
+lapiconline(void)
+{
+	/*
+	 * Reload the timer to de-synchronise the processors,
+	 * then lower the task priority to allow interrupts to be
+	 * accepted by the APIC.
+	 */
+	microdelay((TK2MS(1)*1000/conf.nmach) * m->machno);
+	lapicw(LapicTICR, lapictimer.max);
+	lapicw(LapicTIMER, LapicCLKIN|LapicPERIODIC|(VectorPIC+IrqTIMER));
+
+	lapicw(LapicTPR, 0);
+}
+
+/*
+ *  use the i8253 clock to figure out our lapic timer rate.
+ */
+static void
+lapictimerinit(void)
+{
+	uvlong x, v, hz;
+
+	v = m->cpuhz/1000;
+	lapicw(LapicTDCR, LapicX1);
+	lapicw(LapicTIMER, ApicIMASK|LapicCLKIN|LapicONESHOT|(VectorPIC+IrqTIMER));
+
+	if(lapictimer.hz == 0ULL){
+		x = fastticks(&hz);
+		x += hz/10;
+		lapicw(LapicTICR, 0xffffffff);
+		do{
+			v = fastticks(nil);
+		}while(v < x);
+
+		lapictimer.hz = (0xffffffffUL-lapicr(LapicTCCR))*10;
+		lapictimer.max = lapictimer.hz/HZ;
+		lapictimer.min = lapictimer.hz/(100*HZ);
+
+		if(lapictimer.hz > hz)
+			panic("lapic clock faster than cpu clock");
+		lapictimer.div = hz/lapictimer.hz;
+	}
+}
+
+void
+lapicinit(Apic* apic)
+{
+	ulong r, lvt;
+
+	if(lapicbase == 0)
+		lapicbase = apic->addr;
+
+	lapicw(LapicDFR, 0xFFFFFFFF);
+	r = (lapicr(LapicID)>>24) & 0xFF;
+	lapicw(LapicLDR, (1<<r)<<24);
+	lapicw(LapicTPR, 0xFF);
+	lapicw(LapicSVR, LapicENABLE|(VectorPIC+IrqSPURIOUS));
+
+	lapictimerinit();
+
+	/*
+	 * Some Pentium revisions have a bug whereby spurious
+	 * interrupts are generated in the through-local mode.
+	 */
+	switch(m->cpuidax & 0xFFF){
+	case 0x526:				/* stepping cB1 */
+	case 0x52B:				/* stepping E0 */
+	case 0x52C:				/* stepping cC0 */
+		wrmsr(0x0E, 1<<14);		/* TR12 */
+		break;
+	}
+
+	/*
+	 * Set the local interrupts. It's likely these should just be
+	 * masked off for SMP mode as some Pentium Pros have problems if
+	 * LINT[01] are set to ExtINT.
+	 * Acknowledge any outstanding interrupts.
+	lapicw(LapicLINT0, apic->lintr[0]);
+	lapicw(LapicLINT1, apic->lintr[1]);
+	 */
+	lapiceoi(0);
+
+	lvt = (lapicr(LapicVER)>>16) & 0xFF;
+	if(lvt >= 4)
+		lapicw(LapicPCINT, ApicIMASK);
+	lapicw(LapicERROR, VectorPIC+IrqERROR);
+	lapicw(LapicESR, 0);
+	lapicr(LapicESR);
+
+	/*
+	 * Issue an INIT Level De-Assert to synchronise arbitration ID's.
+	 */
+	lapicw(LapicICRHI, 0);
+	lapicw(LapicICRLO, LapicALLINC|ApicLEVEL|LapicDEASSERT|ApicINIT);
+	while(lapicr(LapicICRLO) & ApicDELIVS)
+		;
+
+	/*
+	 * Do not allow acceptance of interrupts until all initialisation
+	 * for this processor is done. For the bootstrap processor this can be
+	 * early duing initialisation. For the application processors this should
+	 * be after the bootstrap processor has lowered priority and is accepting
+	 * interrupts.
+	lapicw(LapicTPR, 0);
+	 */
+}
+
+void
+lapicstartap(Apic* apic, int v)
+{
+	int crhi, i;
+
+	crhi = apic->apicno<<24;
+	lapicw(LapicICRHI, crhi);
+	lapicw(LapicICRLO, LapicFIELD|ApicLEVEL|LapicASSERT|ApicINIT);
+	microdelay(200);
+	lapicw(LapicICRLO, LapicFIELD|ApicLEVEL|LapicDEASSERT|ApicINIT);
+	delay(10);
+
+	for(i = 0; i < 2; i++){
+		lapicw(LapicICRHI, crhi);
+		lapicw(LapicICRLO, LapicFIELD|ApicEDGE|ApicSTARTUP|(v/BY2PG));
+		microdelay(200);
+	}
+}
+
+void
+lapicerror(Ureg*, void*)
+{
+	int esr;
+
+	lapicw(LapicESR, 0);
+	esr = lapicr(LapicESR);
+	switch(m->cpuidax & 0xFFF){
+	case 0x526:				/* stepping cB1 */
+	case 0x52B:				/* stepping E0 */
+	case 0x52C:				/* stepping cC0 */
+		return;
+	}
+	print("cpu%d: lapicerror: 0x%8.8uX\n", m->machno, esr);
+}
+
+void
+lapicspurious(Ureg*, void*)
+{
+	print("cpu%d: lapicspurious\n", m->machno);
+}
+
+int
+lapicisr(int v)
+{
+	int isr;
+
+	isr = lapicr(LapicISR + (v/32));
+
+	return isr & (1<<(v%32));
+}
+
+int
+lapiceoi(int v)
+{
+	lapicw(LapicEOI, 0);
+
+	return v;
+}
+
+void
+lapicicrw(int hi, int lo)
+{
+	lapicw(LapicICRHI, hi);
+	lapicw(LapicICRLO, lo);
+}
+
+void
+ioapicrdtr(Apic* apic, int sel, int* hi, int* lo)
+{
+	ulong *iowin;
+
+	iowin = apic->addr+(0x10/sizeof(ulong));
+	sel = IoapicRDT + 2*sel;
+
+	lock(apic);
+	*apic->addr = sel+1;
+	if(hi)
+		*hi = *iowin;
+	*apic->addr = sel;
+	if(lo)
+		*lo = *iowin;
+	unlock(apic);
+}
+
+void
+ioapicrdtw(Apic* apic, int sel, int hi, int lo)
+{
+	ulong *iowin;
+
+	iowin = apic->addr+(0x10/sizeof(ulong));
+	sel = IoapicRDT + 2*sel;
+
+	lock(apic);
+	*apic->addr = sel+1;
+	*iowin = hi;
+	*apic->addr = sel;
+	*iowin = lo;
+	unlock(apic);
+}
+
+void
+ioapicinit(Apic* apic, int apicno)
+{
+	int hi, lo, v;
+	ulong *iowin;
+
+	/*
+	 * Initialise the I/O APIC.
+	 * The MultiProcessor Specification says it is the responsibility
+	 * of the O/S to set the APIC id.
+	 * Make sure interrupts are all masked off for now.
+	 */
+	iowin = apic->addr+(0x10/sizeof(ulong));
+	lock(apic);
+	*apic->addr = IoapicVER;
+	apic->mre = (*iowin>>16) & 0xFF;
+
+	*apic->addr = IoapicID;
+	*iowin = apicno<<24;
+	unlock(apic);
+
+	hi = 0;
+	lo = ApicIMASK;
+	for(v = 0; v <= apic->mre; v++)
+		ioapicrdtw(apic, v, hi, lo);
+}
+
+void
+lapictimerset(uvlong next)
+{
+	vlong period;
+	int x;
+
+	x = splhi();
+	lock(&m->apictimerlock);
+
+	period = lapictimer.max;
+	if(next != 0){
+		period = next - fastticks(nil);
+		period /= lapictimer.div;
+
+		if(period < lapictimer.min)
+			period = lapictimer.min;
+		else if(period > lapictimer.max - lapictimer.min)
+			period = lapictimer.max;
+	}
+	lapicw(LapicTICR, period);
+
+	unlock(&m->apictimerlock);
+	splx(x);
+}
+
+void
+lapicclock(Ureg *u, void*)
+{
+	timerintr(u, 0);
+}
--- /dev/null
+++ b/os/pc/apm.c
@@ -1,0 +1,151 @@
+/*
+ * Interface to Advanced Power Management 1.2 BIOS
+ *
+ * This is, in many ways, a giant hack, and when things settle down 
+ * a bit and standardize, hopefully we can write a driver that deals
+ * more directly with the hardware and thus might be a bit cleaner.
+ * 
+ * ACPI might be the answer, but at the moment this is simpler
+ * and more widespread.
+ */
+
+#include	"u.h"
+#include	"../port/lib.h"
+#include	"mem.h"
+#include	"dat.h"
+#include	"fns.h"
+#include	"io.h"
+#include	"ureg.h"
+
+extern int apmfarcall(ushort, ulong, Ureg*);		/* apmjump.s */
+
+static int
+getreg(ulong *reg, ISAConf *isa, char *name)
+{
+	int i;
+	int nl;
+
+	nl = strlen(name);
+	for(i=0; i<isa->nopt; i++){
+		if(cistrncmp(isa->opt[i], name, nl)==0 && isa->opt[i][nl] == '='){
+			*reg = strtoul(isa->opt[i]+nl+1, nil, 16);
+			return 0;
+		}
+	}
+	return -1;
+}
+
+/*
+ * Segment descriptors look like this.
+ *
+ * d1: [base 31:24] [gran] [is32bit] [0] [unused] [limit 19:16] 
+		[present] [privlev] [type 3:0] [base 23:16]
+ * d0: [base 15:00] [limit 15:00]
+ *
+ * gran is 0 for 1-byte granularity, 1 for 4k granularity
+ * type is 0 for system segment, 1 for code/data.
+ *
+ * clearly we know way too much about the memory unit.
+ * however, knowing this much about the memory unit
+ * means that the memory unit need not know anything
+ * about us.
+ *
+ * what a crock.
+ */
+static void
+setgdt(int sel, ulong base, ulong limit, int flag)
+{
+	if(sel < 0 || sel >= NGDT)
+		panic("setgdt");
+
+	base = (ulong)KADDR(base);
+	m->gdt[sel].d0 = (base<<16) | (limit&0xFFFF);
+	m->gdt[sel].d1 = (base&0xFF000000) | (limit&0x000F0000) |
+			((base>>16)&0xFF) | SEGP | SEGPL(0) | flag;
+}
+
+static	ulong ax, cx, dx, di, ebx, esi;
+static Ureg apmu;
+static long
+apmread(Chan*, void *a, long n, vlong off)
+{
+	if(off < 0)
+		error("badarg");
+
+	if(n+off > sizeof apmu)
+		n = sizeof apmu - off;
+	if(n <= 0)
+		return 0;
+	memmove(a, (char*)&apmu+off, n);
+	return n;
+}
+
+static long
+apmwrite(Chan*, void *a, long n, vlong off)
+{
+	int s;
+	if(off || n != sizeof apmu)
+		error("write a Ureg");
+
+	memmove(&apmu, a, sizeof apmu);
+	s = splhi();
+	apmfarcall(APMCSEL, ebx, &apmu);
+	splx(s);
+	return n;
+}
+
+void
+apmlink(void)
+{
+	ISAConf isa;
+	char *s;
+
+	if(isaconfig("apm", 0, &isa) == 0)
+		return;
+
+	/*
+	 * APM info passed from boot loader.
+	 * Now we need to set up the GDT entries for APM.
+	 *
+	 * AX = 32-bit code segment base address
+	 * EBX = 32-bit code segment offset
+	 * CX = 16-bit code segment base address
+	 * DX = 32-bit data segment base address
+	 * ESI = <16-bit code segment length> <32-bit code segment length> (hi then lo)
+	 * DI = 32-bit data segment length
+	 */
+
+	if(getreg(&ax, &isa, s="ax") < 0
+	|| getreg(&ebx, &isa, s="ebx") < 0
+	|| getreg(&cx, &isa, s="cx") < 0
+	|| getreg(&dx, &isa, s="dx") < 0
+	|| getreg(&esi, &isa, s="esi") < 0
+	|| getreg(&di, &isa, s="di") < 0){
+		print("apm: missing register %s\n", s);
+		return;
+	}
+
+	/*
+	 * The NEC Versa SX bios does not report the correct 16-bit code
+	 * segment length when loaded directly from mbr -> 9load (as compared
+	 * with going through ld.com).  We'll make both code segments 64k-1 bytes.
+	 */
+	esi = 0xFFFFFFFF;
+
+	/*
+	 * We are required by the BIOS to set up three consecutive segments,
+	 * one for the APM 32-bit code, one for the APM 16-bit code, and 
+	 * one for the APM data.  The BIOS handler uses the code segment it
+	 * get called with to determine the other two segment selector.
+	 */
+	setgdt(APMCSEG, ax<<4, ((esi&0xFFFF)-1)&0xFFFF, SEGEXEC|SEGR|SEGD);
+	setgdt(APMCSEG16, cx<<4, ((esi>>16)-1)&0xFFFF, SEGEXEC|SEGR);
+	setgdt(APMDSEG, dx<<4, (di-1)&0xFFFF, SEGDATA|SEGW|SEGD);
+
+	addarchfile("apm", 0660, apmread, apmwrite);
+
+print("apm0: configured cbase %.8lux off %.8lux\n", ax<<4, ebx);
+
+	return;
+}
+
--- /dev/null
+++ b/os/pc/apmjump.s
@@ -1,0 +1,98 @@
+/*
+ * Far call, absolute indirect.
+ * The argument is the offset.
+ * We use a global structure for the jump params,
+ * so this is *not* reentrant or thread safe.
+ */
+
+#include "mem.h"
+
+#define SSOVERRIDE	BYTE $0x36
+#define CSOVERRIDE	BYTE $0x2E
+#define RETF		BYTE $0xCB
+
+GLOBL	apmjumpstruct+0(SB), $8
+
+TEXT fortytwo(SB), $0
+	MOVL	$42, AX
+	RETF
+
+TEXT getcs(SB), $0
+	PUSHL	CS
+	POPL	AX
+	RET
+
+TEXT apmfarcall(SB), $0
+	/*
+	 * We call push and pop ourselves.
+	 * As soon as we do the first push or pop,
+	 * we can't use FP anymore.
+	 */
+	MOVL	off+4(FP), BX
+	MOVL	seg+0(FP), CX
+	MOVL	BX, apmjumpstruct+0(SB)
+	MOVL	CX, apmjumpstruct+4(SB)
+
+	/* load necessary registers from Ureg */
+	MOVL	ureg+8(FP), DI
+	MOVL	28(DI), AX
+	MOVL	16(DI), BX
+	MOVL	24(DI), CX
+	MOVL	20(DI), DX
+
+	/* save registers, segments */
+	PUSHL	DS
+	PUSHL	ES
+	PUSHL	FS
+	PUSHL	GS
+	PUSHL	BP
+	PUSHL	DI
+
+	/*
+	 * paranoia: zero the segments, since it's the
+	 * BIOS's responsibility to initialize them.
+	 * (trick picked up from Linux driver).
+	PUSHL	DX
+	XORL	DX, DX
+	PUSHL	DX
+	POPL	DS
+	PUSHL	DX
+	POPL	ES
+	PUSHL	DX
+	POPL	FS
+	PUSHL	DX
+	POPL	GS
+	POPL	DX
+	 */
+
+	PUSHL	$APMDSEG
+	POPL	DS
+
+	/*
+	 * The actual call.
+	 */
+	CSOVERRIDE; BYTE $0xFF; BYTE $0x1D
+	LONG $apmjumpstruct+0(SB)
+
+	/* restore segments, registers */
+	POPL	DI
+	POPL	BP
+	POPL	GS
+	POPL	FS
+	POPL	ES
+	POPL	DS
+
+	PUSHFL
+	POPL	64(DI)
+
+	/* store interesting registers back in Ureg */
+	MOVL	AX, 28(DI)
+	MOVL	BX, 16(DI)
+	MOVL	CX, 24(DI)
+	MOVL	DX, 20(DI)
+	MOVL	SI, 4(DI)
+
+	PUSHFL
+	POPL	AX
+	ANDL	$1, AX	/* carry flag */
+	RET
--- /dev/null
+++ b/os/pc/archmp.c
@@ -1,0 +1,138 @@
+#include "u.h"
+#include "../port/lib.h"
+#include "mem.h"
+#include "dat.h"
+#include "fns.h"
+#include "io.h"
+
+#include "mp.h"
+
+#define	cpuserver	1
+
+_MP_ *_mp_;
+
+static _MP_*
+mpscan(uchar *addr, int len)
+{
+	uchar *e, *p, sum;
+	int i;
+
+	e = addr+len;
+	for(p = addr; p < e; p += sizeof(_MP_)){
+		if(memcmp(p, "_MP_", 4))
+			continue;
+		sum = 0;
+		for(i = 0; i < sizeof(_MP_); i++)
+			sum += p[i];
+		if(sum == 0)
+			return (_MP_*)p;
+	}
+	return 0;
+}
+
+static _MP_*
+mpsearch(void)
+{
+	uchar *bda;
+	ulong p;
+	_MP_ *mp;
+
+	/*
+	 * Search for the MP Floating Pointer Structure:
+	 * 1) in the first KB of the EBDA;
+	 * 2) in the last KB of system base memory;
+	 * 3) in the BIOS ROM between 0xE0000 and 0xFFFFF.
+	 */
+	bda = KADDR(0x400);
+	if((p = (bda[0x0F]<<8)|bda[0x0E])){
+		if(mp = mpscan(KADDR(p), 1024))
+			return mp;
+	}
+	else{
+		p = ((bda[0x14]<<8)|bda[0x13])*1024;
+		if(mp = mpscan(KADDR(p-1024), 1024))
+			return mp;
+	}
+	return mpscan(KADDR(0xF0000), 0x10000);
+}
+
+static int identify(void);
+
+PCArch archmp = {
+.id=		"_MP_",	
+.ident=		identify,
+.reset=		mpshutdown,
+.intrinit=	mpinit,
+.intrenable=	mpintrenable,
+.fastclock=	i8253read,
+.timerset=	lapictimerset,
+};
+
+static int
+identify(void)
+{
+	PCMP *pcmp;
+	uchar *p, sum;
+	ulong length;
+
+	if(getconf("*nomp"))
+		return 1;
+
+	/*
+	 * Search for an MP configuration table. For now,
+	 * don't accept the default configurations (physaddr == 0).
+	 * Check for correct signature, calculate the checksum and,
+	 * if correct, check the version.
+	 * To do: check extended table checksum.
+	 */
+	if((_mp_ = mpsearch()) == 0 || _mp_->physaddr == 0)
+		return 1;
+
+	pcmp = KADDR(_mp_->physaddr);
+	if(memcmp(pcmp, "PCMP", 4))
+		return 1;
+
+	length = pcmp->length;
+	sum = 0;
+	for(p = (uchar*)pcmp; length; length--)
+		sum += *p++;
+
+	if(sum || (pcmp->version != 1 && pcmp->version != 4))
+		return 1;
+
+	if(cpuserver && m->havetsc)
+		archmp.fastclock = tscticks;
+	return 0;
+}
+
+Lock mpsynclock;
+
+void
+syncclock(void)
+{
+	uvlong x;
+
+	if(arch->fastclock != tscticks)
+		return;
+
+	if(m->machno == 0){
+		wrmsr(0x10, 0);
+		m->tscticks = 0;
+	} else {
+		x = MACHP(0)->tscticks;
+		while(x == MACHP(0)->tscticks)
+			;
+		wrmsr(0x10, MACHP(0)->tscticks);
+		cycles(&m->tscticks);
+	}
+}
+
+uvlong
+tscticks(uvlong *hz)
+{
+	if(hz != nil)
+		*hz = m->cpuhz;
+
+	cycles(&m->tscticks);	/* Uses the rdtsc instruction */
+	return m->tscticks;
+}
--- /dev/null
+++ b/os/pc/audio.h
@@ -1,0 +1,15 @@
+enum
+{
+	Bufsize	= 1024,	/* 5.8 ms each, must be power of two */
+	Nbuf		= 128,	/* .74 seconds total */
+	Dma		= 6,
+	IrqAUDIO	= 7,
+	SBswab	= 0,
+};
+
+#define seteisadma(a, b)	dmainit(a, Bufsize);
+#define CACHELINESZ		8
+#define UNCACHED(type, v)	(type*)((ulong)(v))
+
+#define Int0vec
+#define setvec(v, f, a)		intrenable(v, f, a, BUSUNKNOWN, "audio")
--- /dev/null
+++ b/os/pc/cga.c
@@ -1,0 +1,127 @@
+#include "u.h"
+#include "../port/lib.h"
+#include "mem.h"
+#include "dat.h"
+#include "fns.h"
+#include "../port/error.h"
+
+enum {
+	Black,
+	Blue,
+	Green,
+	Cyan,
+	Red,
+	Magenta,
+	Brown,
+	Grey,
+
+	Bright = 0x08,
+	Blinking = 0x80,
+
+	Yellow = Bright|Brown,
+	White = Bright|Grey,
+};
+	
+enum {
+	Width		= 80*2,
+	Height		= 25,
+
+	Attr		= (Black<<4)|Grey,	/* high nibble background
+						 * low foreground
+						 */
+};
+
+#define CGASCREENBASE	((uchar*)KADDR(0xB8000))
+
+static int cgapos;
+static Lock cgascreenlock;
+
+static uchar
+cgaregr(int index)
+{
+	outb(0x3D4, index);
+	return inb(0x3D4+1) & 0xFF;
+}
+
+static void
+cgaregw(int index, int data)
+{
+	outb(0x3D4, index);
+	outb(0x3D4+1, data);
+}
+
+static void
+movecursor(void)
+{
+	cgaregw(0x0E, (cgapos/2>>8) & 0xFF);
+	cgaregw(0x0F, cgapos/2 & 0xFF);
+	CGASCREENBASE[cgapos+1] = Attr;
+}
+
+static void
+cgascreenputc(int c)
+{
+	int i;
+	uchar *p;
+
+	if(c == '\n'){
+		cgapos = cgapos/Width;
+		cgapos = (cgapos+1)*Width;
+	}
+	else if(c == '\t'){
+		i = 8 - ((cgapos/2)&7);
+		while(i-->0)
+			cgascreenputc(' ');
+	}
+	else if(c == '\b'){
+		if(cgapos >= 2)
+			cgapos -= 2;
+		cgascreenputc(' ');
+		cgapos -= 2;
+	}
+	else{
+		CGASCREENBASE[cgapos++] = c;
+		CGASCREENBASE[cgapos++] = Attr;
+	}
+	if(cgapos >= Width*Height){
+		memmove(CGASCREENBASE, &CGASCREENBASE[Width], Width*(Height-1));
+		p = &CGASCREENBASE[Width*(Height-1)];
+		for(i=0; i<Width/2; i++){
+			*p++ = ' ';
+			*p++ = Attr;
+		}
+		cgapos = Width*(Height-1);
+	}
+	movecursor();
+}
+
+static void
+cgascreenputs(char* s, int n)
+{
+	if(!islo()){
+		/*
+		 * Don't deadlock trying to
+		 * print in an interrupt.
+		 */
+		if(!canlock(&cgascreenlock))
+			return;
+	}
+	else
+		lock(&cgascreenlock);
+
+	while(n-- > 0)
+		cgascreenputc(*s++);
+
+	unlock(&cgascreenlock);
+}
+
+void
+screeninit(void)
+{
+
+	cgapos = cgaregr(0x0E)<<8;
+	cgapos |= cgaregr(0x0F);
+	cgapos *= 2;
+
+	screenputs = cgascreenputs;
+}
--- /dev/null
+++ b/os/pc/cgamemscr.c
@@ -1,0 +1,203 @@
+#include "u.h"
+#include "../port/lib.h"
+#include "mem.h"
+#include "dat.h"
+#include "fns.h"
+#include "io.h"
+#include "../port/error.h"
+
+#include <draw.h>
+#include <memdraw.h>
+#include <memlayer.h>
+
+enum {
+	Width		= 160,
+	Height		= 25,
+
+	Attr		= 7,		/* white on black */
+};
+
+#define CGASCREENBASE	((uchar*)KADDR(0xB8000))
+
+static int cgapos;
+static int screeninitdone;
+static Lock cgascreenlock;
+void (*vgascreenputc)(char*);
+
+static uchar
+cgaregr(int index)
+{
+	outb(0x3D4, index);
+	return inb(0x3D4+1) & 0xFF;
+}
+
+static void
+cgaregw(int index, int data)
+{
+	outb(0x3D4, index);
+	outb(0x3D4+1, data);
+}
+
+static void
+movecursor(void)
+{
+	cgaregw(0x0E, (cgapos/2>>8) & 0xFF);
+	cgaregw(0x0F, cgapos/2 & 0xFF);
+	CGASCREENBASE[cgapos+1] = Attr;
+}
+
+static void
+cgascreenputc(int c)
+{
+	int i;
+
+	if(c == '\n'){
+		cgapos = cgapos/Width;
+		cgapos = (cgapos+1)*Width;
+	}
+	else if(c == '\t'){
+		i = 8 - ((cgapos/2)&7);
+		while(i-->0)
+			cgascreenputc(' ');
+	}
+	else if(c == '\b'){
+		if(cgapos >= 2)
+			cgapos -= 2;
+		cgascreenputc(' ');
+		cgapos -= 2;
+	}
+	else{
+		CGASCREENBASE[cgapos++] = c;
+		CGASCREENBASE[cgapos++] = Attr;
+	}
+	if(cgapos >= Width*Height){
+		memmove(CGASCREENBASE, &CGASCREENBASE[Width], Width*(Height-1));
+		memset(&CGASCREENBASE[Width*(Height-1)], 0, Width);
+		cgapos = Width*(Height-1);
+	}
+	movecursor();
+}
+
+void
+screeninit(void)
+{
+	memimageinit();
+	cgapos = cgaregr(0x0E)<<8;
+	cgapos |= cgaregr(0x0F);
+	cgapos *= 2;
+	screeninitdone = 1;
+}
+
+void
+cgascreenputs(char* s, int n)
+{
+	int i;
+	Rune r;
+	char buf[4];
+
+	if(!islo()){
+		if(!canlock(&cgascreenlock))
+			return;
+	}
+	else
+		lock(&cgascreenlock);
+
+	if(vgascreenputc == nil){
+		while(n-- > 0)
+			cgascreenputc(*s++);
+		unlock(&cgascreenlock);
+		return;
+	}
+
+	while(n > 0) {
+		i = chartorune(&r, s);
+		if(i == 0){
+			s++;
+			--n;
+			continue;
+		}
+		memmove(buf, s, i);
+		buf[i] = 0;
+		n -= i;
+		s += i;
+		vgascreenputc(buf);
+	}
+
+	unlock(&cgascreenlock);
+}
+
+void
+cursorenable(void)
+{
+}
+
+void
+cursordisable(void)
+{
+}
+
+typedef struct Drawcursor Drawcursor;
+
+
+
+void
+cursorupdate(Rectangle r)
+{
+	USED(r);
+}
+
+void
+drawcursor(Drawcursor *c)
+{
+	USED(c);
+}
+
+uchar*
+attachscreen(Rectangle *r, ulong *chan, int* d, int *width, int *softscreen)
+{
+	static Rectangle screenr = {0, 0, 0, 0};
+	static uchar *bdata;
+	if (bdata == nil)
+		if ((bdata = malloc(1)) == nil)
+			return nil;
+	*r = screenr;
+	*chan = RGB24;
+	*d = chantodepth(RGB24);
+	*width = 0;
+	*softscreen = 0;
+	return bdata;
+}
+
+void
+flushmemscreen(Rectangle r)
+{
+	USED(r);
+}
+
+void
+blankscreen(int i)
+{
+	USED(i);
+}
+
+void
+getcolor(ulong p, ulong *pr, ulong *pg, ulong *pb)
+{
+	USED(p);
+	USED(pr);
+	USED(pg);
+	USED(pb);
+
+}
+
+int
+setcolor(ulong p, ulong r, ulong g, ulong b)
+{
+	USED(p);
+	USED(r);
+	USED(g);
+	USED(b);
+	return ~0;
+}
+
+void (*screenputs)(char*, int) = cgascreenputs;
--- /dev/null
+++ b/os/pc/crystal.h
@@ -1,0 +1,1118 @@
+/*
+ * Micro code for the crystal musicam decoder on the Boffin MPEG decoder
+ */
+static
+uchar crystal[] =
+{
+  0x10,0x10,0x06,0x00,0x00,0x00,0x00,0x00,0x03,0xa0,0x00,0x1a,0x9f,0x00,0x39,0x5f,
+  0x00,0xfe,0x9f,0x02,0x84,0x1f,0x03,0x35,0xbf,0x12,0x4e,0x1f,0x24,0xa3,0xc0,0xed,
+  0xb1,0xe1,0x03,0x35,0xbf,0xfd,0x7b,0xe1,0x00,0xfe,0x9f,0xff,0xc6,0xa1,0x00,0x1a,
+  0x9f,0xff,0xfc,0x60,0xff,0xff,0xe0,0x00,0x03,0x40,0x00,0x19,0xff,0x00,0x32,0x1f,
+  0x01,0x01,0xe0,0x02,0x56,0x7f,0x03,0x7b,0xbf,0x11,0x66,0xff,0x24,0x9d,0xff,0xec,
+  0xcb,0x00,0x02,0xe8,0xdf,0xfd,0x4e,0x61,0x00,0xf9,0xff,0xff,0xbf,0x20,0x00,0x1b,
+  0x3f,0xff,0xfc,0x21,0xff,0xff,0xe0,0x00,0x03,0x00,0x00,0x19,0x3f,0x00,0x2b,0x60,
+  0x01,0x03,0xff,0x02,0x29,0x20,0x03,0xba,0xff,0x10,0x7f,0xdf,0x24,0x8c,0xff,0xeb,
+  0xe5,0x01,0x02,0x95,0x00,0xfd,0x21,0x20,0x00,0xf3,0xff,0xff,0xb7,0x61,0x00,0x1b,
+  0xbf,0xff,0xfb,0xa0,0xff,0xff,0xe0,0x00,0x02,0xa0,0x00,0x18,0x80,0x00,0x24,0xc0,
+  0x01,0x04,0xe0,0x01,0xfb,0xe0,0x03,0xf3,0x7f,0x0f,0x99,0x5f,0x24,0x70,0xc0,0xeb,
+  0x00,0x41,0x02,0x3a,0x20,0xfc,0xf4,0x61,0x00,0xec,0xa0,0xff,0xaf,0x60,0x00,0x1c,
+  0x20,0xff,0xfb,0x40,0xff,0xff,0xe0,0x00,0x02,0x60,0x00,0x17,0xc0,0x00,0x1e,0x80,
+  0x01,0x04,0x9f,0x01,0xcf,0x1f,0x04,0x25,0x80,0x0e,0xb3,0xff,0x24,0x49,0x20,0xea,
+  0x1d,0x60,0x01,0xd7,0xff,0xfc,0xc8,0x61,0x00,0xe3,0xc0,0xff,0xa7,0x21,0x00,0x1c,
+  0x5f,0xff,0xfa,0xe1,0xff,0xff,0xe0,0x00,0x02,0x1f,0x00,0x16,0xdf,0x00,0x18,0x9f,
+  0x01,0x03,0x5f,0x01,0xa2,0xdf,0x04,0x50,0xff,0x0d,0xd0,0x20,0x24,0x16,0x7f,0xe9,
+  0x3c,0xe0,0x01,0x6e,0xe0,0xfc,0x9d,0x21,0x00,0xd9,0x5f,0xff,0x9e,0xa0,0x00,0x1c,
+  0x80,0xff,0xfa,0x60,0xff,0xff,0xe0,0x00,0x02,0x00,0x00,0x16,0x00,0x00,0x13,0x20,
+  0x01,0x01,0x1f,0x01,0x77,0x7f,0x04,0x76,0x5f,0x0c,0xee,0x40,0x23,0xd8,0xdf,0xe8,
+  0x5f,0x40,0x00,0xfe,0x9f,0xfc,0x73,0x21,0x00,0xcd,0x7f,0xff,0x96,0x01,0x00,0x1c,
+  0x80,0xff,0xf9,0xe0,0xff,0xff,0xc0,0x00,0x01,0xbf,0x00,0x15,0x1f,0x00,0x0d,0xe0,
+  0x00,0xfd,0xff,0x01,0x4c,0xdf,0x04,0x95,0xa0,0x0c,0x0e,0xbf,0x23,0x90,0x5f,0xe7,
+  0x84,0xe1,0x00,0x87,0x40,0xfc,0x4a,0x60,0x00,0xbf,0xdf,0xff,0x8d,0x21,0x00,0x1c,
+  0x5f,0xff,0xf9,0x60,0xff,0xff,0xc0,0x00,0x01,0x9f,0x00,0x14,0x1f,0x00,0x09,0x00,
+  0x00,0xfa,0x20,0x01,0x23,0x40,0x04,0xaf,0x00,0x0b,0x32,0x1f,0x23,0x3d,0x20,0xe6,
+  0xae,0x61,0x00,0x08,0xbf,0xfc,0x23,0x41,0x00,0xb0,0xc0,0xff,0x84,0x20,0x00,0x1c,
+  0x00,0xff,0xf8,0xc0,0xff,0xff,0xc0,0x00,0x01,0x60,0x00,0x13,0x40,0x00,0x04,0x7f,
+  0x00,0xf5,0x3f,0x00,0xfa,0xc0,0x04,0xc2,0xbf,0x0a,0x58,0x9f,0x22,0xdf,0x80,0xe5,
+  0xdc,0x40,0xff,0x83,0x41,0xfb,0xfd,0xe1,0x00,0xa0,0x00,0xff,0x7b,0x00,0x00,0x1b,
+  0x9f,0xff,0xf8,0x20,0xff,0xff,0xc0,0x00,0x01,0x40,0x00,0x12,0x60,0x00,0x00,0x40,
+  0x00,0xef,0xdf,0x00,0xd3,0x7f,0x04,0xd0,0xe0,0x09,0x82,0xbf,0x22,0x77,0xc0,0xe5,
+  0x0e,0xc0,0xfe,0xf6,0xc1,0xfb,0xda,0xa0,0x00,0x8d,0x5f,0xff,0x71,0xe1,0x00,0x1a,
+  0xe0,0xff,0xf7,0x80,0xff,0xff,0xa1,0x00,0x01,0x1f,0x00,0x11,0x60,0xff,0xfc,0x60,
+  0x00,0xe9,0xc0,0x00,0xad,0x7f,0x04,0xd9,0xdf,0x08,0xb0,0xe0,0x22,0x05,0xdf,0xe4,
+  0x46,0xc1,0xfe,0x63,0x80,0xfb,0xb9,0xa1,0x00,0x79,0x3f,0xff,0x68,0xc0,0x00,0x19,
+  0xff,0xff,0xf6,0xe0,0xff,0xff,0xa1,0x00,0x00,0xff,0x00,0x10,0x7f,0xff,0xf8,0xe0,
+  0x00,0xe3,0x20,0x00,0x88,0xdf,0x04,0xdd,0xc0,0x07,0xe3,0x5f,0x21,0x8a,0x7f,0xe3,
+  0x84,0x61,0xfd,0xc9,0x60,0xfb,0x9b,0x40,0x00,0x63,0x40,0xff,0x5f,0xa1,0x00,0x19,
+  0x00,0xff,0xf6,0x21,0xff,0xff,0x81,0x00,0x00,0xe0,0x00,0x0f,0xa0,0xff,0xf5,0xa1,
+  0x00,0xdb,0xe0,0x00,0x65,0xbf,0x04,0xdc,0xdf,0x07,0x1a,0x7f,0x21,0x05,0xa0,0xe2,
+  0xc8,0x40,0xfd,0x28,0xc0,0xfb,0x7f,0xa1,0x00,0x4b,0x9f,0xff,0x56,0x80,0x00,0x17,
+  0x9f,0xff,0xf5,0x61,0xff,0xff,0x81,0x00,0x00,0xe0,0x00,0x0e,0x9f,0xff,0xf2,0xc0,
+  0x00,0xd4,0x40,0x00,0x44,0x1f,0x04,0xd7,0x7f,0x06,0x56,0x7f,0x20,0x77,0xc0,0xe2,
+  0x12,0xe0,0xfc,0x81,0xc0,0xfb,0x67,0x00,0x00,0x32,0x3f,0xff,0x4d,0x80,0x00,0x16,
+  0x20,0xff,0xf4,0xa0,0xff,0xff,0x60,0x00,0x00,0xc0,0x00,0x0d,0xe0,0xff,0xf0,0x21,
+  0x00,0xcc,0x3f,0x00,0x23,0xff,0x04,0xcd,0xc0,0x05,0x97,0xe0,0x1f,0xe1,0x40,0xe1,
+  0x64,0x80,0xfb,0xd4,0x80,0xfb,0x51,0xe1,0x00,0x17,0x20,0xff,0x44,0xc1,0x00,0x14,
+  0x60,0xff,0xf3,0xe0,0xff,0xff,0x60,0x00,0x00,0xa0,0x00,0x0c,0xff,0xff,0xed,0xc1,
+  0x00,0xc3,0xdf,0x00,0x05,0xa0,0x04,0xbf,0xdf,0x04,0xde,0xe0,0x1f,0x42,0x60,0xe0,
+  0xbd,0xa0,0xfb,0x21,0x20,0xfb,0x40,0x21,0xff,0xfa,0x60,0xff,0x3c,0x21,0x00,0x12,
+  0x3f,0xff,0xf3,0x01,0xff,0xff,0x40,0x00,0x00,0xa0,0x00,0x0c,0x20,0xff,0xeb,0xa0,
+  0x00,0xbb,0x3f,0xff,0xe8,0xe0,0x04,0xae,0x1f,0x04,0x2b,0x80,0x1e,0x9b,0x80,0xe0,
+  0x1e,0xc0,0xfa,0x68,0x20,0xfb,0x32,0x40,0xff,0xdc,0x01,0xff,0x33,0xc1,0x00,0x0f,
+  0xdf,0xff,0xf2,0x20,0xff,0xff,0x20,0x00,0x00,0x7f,0x00,0x0b,0x60,0xff,0xe9,0xe0,
+  0x00,0xb2,0x80,0xff,0xcd,0xc1,0x04,0x99,0x00,0x03,0x7e,0x40,0x1d,0xed,0x20,0xdf,
+  0x88,0x40,0xf9,0xa9,0x81,0xfb,0x28,0x81,0xff,0xbb,0xe1,0xff,0x2b,0xc0,0x00,0x0d,
+  0x40,0xff,0xf1,0x61,0xff,0xff,0x20,0x00,0x00,0x7f,0x00,0x0a,0x9f,0xff,0xe8,0x61,
+  0x00,0xa9,0x80,0xff,0xb4,0x61,0x04,0x80,0x5f,0x02,0xd7,0x40,0x1d,0x37,0xc0,0xde,
+  0xfa,0x60,0xf8,0xe5,0x81,0xfb,0x23,0x21,0xff,0x9a,0x41,0xff,0x24,0x20,0x00,0x0a,
+  0x5f,0xff,0xf0,0x60,0xff,0xff,0x01,0x00,0x00,0x5f,0x00,0x09,0xdf,0xff,0xe7,0x00,
+  0x00,0xa0,0x5f,0xff,0x9c,0xc0,0x04,0x64,0xc0,0x02,0x36,0xa0,0x1c,0x7b,0x9f,0xde,
+  0x75,0x81,0xf8,0x1c,0xa1,0xfb,0x22,0x40,0xff,0x77,0x21,0xff,0x1c,0xe0,0x00,0x07,
+  0x20,0xff,0xef,0x81,0xff,0xfe,0xe1,0x00,0x00,0x5f,0x00,0x09,0x20,0xff,0xe6,0x01,
+  0x00,0x97,0x40,0xff,0x86,0xc1,0x04,0x46,0x5f,0x01,0x9c,0x80,0x1b,0xb9,0x3f,0xdd,
+  0xfa,0x21,0xf7,0x4f,0x20,0xfb,0x26,0x21,0xff,0x52,0x81,0xff,0x16,0x40,0x00,0x03,
+  0xa0,0xff,0xee,0xa0,0xff,0xfe,0xc0,0x00,0x00,0x40,0x00,0x08,0x80,0xff,0xe5,0x20,
+  0x00,0x8e,0x1f,0xff,0x72,0xa1,0x04,0x25,0x60,0x01,0x09,0x3f,0x1a,0xf1,0x40,0xdd,
+  0x88,0x40,0xf6,0x7d,0x41,0xfb,0x2f,0x20,0xff,0x2c,0x81,0xff,0x10,0x21,0xff,0xff,
+  0xc0,0xff,0xed,0xa0,0xff,0xfe,0xa0,0x00,0x00,0x40,0x00,0x07,0xe0,0xff,0xe4,0x61,
+  0x00,0x85,0x00,0xff,0x60,0x00,0x04,0x02,0x1f,0x00,0x7c,0xbf,0x1a,0x23,0xc0,0xdd,
+  0x20,0x80,0xf5,0xa7,0x61,0xfb,0x3d,0x41,0xff,0x05,0x40,0xff,0x0a,0xc1,0xff,0xfb,
+  0x81,0xff,0xec,0xc0,0xff,0xfe,0x61,0x00,0x00,0x40,0x00,0x07,0x40,0xff,0xe4,0x00,
+  0x00,0x7b,0xe0,0xff,0x4f,0x40,0x03,0xdc,0xbf,0xff,0xf7,0x41,0x19,0x51,0x9f,0xdc,
+  0xc2,0xe0,0xf4,0xcd,0xe1,0xfb,0x51,0x00,0xfe,0xdc,0xc0,0xff,0x05,0xe0,0xff,0xf7,
+  0x00,0xff,0xeb,0xe1,0xff,0xfe,0x41,0x00,0x00,0x40,0x00,0x06,0xa0,0xff,0xe3,0xa1,
+  0x00,0x72,0xdf,0xff,0x40,0x21,0x03,0xb5,0xa0,0xff,0x78,0xc0,0x18,0x7b,0x1f,0xdc,
+  0x6f,0xa1,0xf3,0xf1,0x41,0xfb,0x6a,0x60,0xfe,0xb3,0x21,0xff,0x02,0x01,0xff,0xf2,
+  0x20,0xff,0xea,0xe1,0xff,0xfe,0x00,0x00,0x00,0x20,0x00,0x06,0x20,0xff,0xe3,0x80,
+  0x00,0x69,0xff,0xff,0x32,0x81,0x03,0x8c,0xdf,0xff,0x01,0x61,0x17,0xa0,0xc0,0xdc,
+  0x27,0x21,0xf3,0x11,0xc0,0xfb,0x89,0xa1,0xfe,0x88,0x81,0xfe,0xfe,0xe1,0xff,0xec,
+  0xe0,0xff,0xea,0x00,0xff,0xfd,0xe1,0x00,0x00,0x20,0x00,0x05,0xa0,0xff,0xe3,0x80,
+  0x00,0x61,0x60,0xff,0x26,0xa1,0x03,0x62,0xdf,0xfe,0x91,0x20,0x16,0xc3,0x20,0xdb,
+  0xe9,0x81,0xf2,0x2f,0xe0,0xfb,0xaf,0x01,0xfe,0x5d,0x21,0xfe,0xfc,0xa1,0xff,0xe7,
+  0x61,0xff,0xe9,0x21,0xff,0xfd,0xa0,0x00,0x00,0x20,0x00,0x05,0x1f,0xff,0xe3,0xa1,
+  0x00,0x58,0xdf,0xff,0x1c,0x40,0x03,0x37,0x9f,0xfe,0x28,0x01,0x15,0xe2,0xa0,0xdb,
+  0xb6,0xe0,0xf1,0x4c,0x01,0xfb,0xda,0x80,0xfe,0x30,0xe1,0xfe,0xfb,0x61,0xff,0xe1,
+  0x80,0xff,0xe8,0x40,0xff,0xfd,0x60,0x00,0x00,0x20,0x00,0x04,0xc0,0xff,0xe3,0xe0,
+  0x00,0x50,0xa0,0xff,0x13,0x60,0x03,0x0b,0x9f,0xfd,0xc5,0xe0,0x14,0xff,0xbf,0xdb,
+  0x8f,0x40,0xf0,0x66,0xa1,0xfc,0x0c,0x81,0xfe,0x04,0x20,0xfe,0xfb,0x20,0xff,0xdb,
+  0x40,0xff,0xe7,0x80,0xff,0xfd,0x00,0x00,0x00,0x20,0x00,0x04,0x60,0xff,0xe4,0x41,
+  0x00,0x48,0x9f,0xff,0x0c,0x01,0x02,0xde,0xe0,0xfd,0x6b,0x00,0x14,0x1a,0xff,0xdb,
+  0x73,0x01,0xef,0x80,0x21,0xfc,0x45,0x01,0xfd,0xd6,0xe0,0xfe,0xfc,0x01,0xff,0xd4,
+  0xa0,0xff,0xe6,0xc1,0xff,0xfc,0xc0,0x00,0x00,0x20,0x00,0x03,0xdf,0xff,0xe4,0xc1,
+  0x00,0x40,0xe0,0xff,0x06,0x01,0x02,0xb1,0x9f,0xfd,0x17,0x21,0x13,0x35,0x00,0xdb,
+  0x62,0x01,0xee,0x99,0x01,0xfc,0x84,0x41,0xfd,0xa9,0x81,0xfe,0xfe,0x20,0xff,0xcd,
+  0xe1,0xff,0xe6,0x01,0x12,0x28,0x00,0xba,0x00,0x00,0x00,0x00,0x00,0x02,0x00,0x00,
+  0x04,0x00,0x00,0x05,0x00,0x00,0x06,0x00,0x00,0x07,0x00,0x00,0x08,0x00,0x00,0x09,
+  0x00,0x00,0x0a,0x00,0x00,0x0b,0x00,0x00,0x0c,0x00,0x00,0x0d,0x00,0x00,0x0e,0x00,
+  0x00,0x0f,0x00,0x00,0x10,0x00,0x00,0x00,0x00,0x00,0x01,0x00,0x00,0x02,0x00,0x00,
+  0x03,0x00,0x00,0x04,0x00,0x00,0x05,0x00,0x00,0x06,0x00,0x00,0x07,0x00,0x00,0x08,
+  0x00,0x00,0x09,0x00,0x00,0x0a,0x00,0x00,0x0b,0x00,0x00,0x0c,0x00,0x00,0x0d,0x00,
+  0x00,0x10,0x00,0x00,0x00,0x00,0x00,0x01,0x00,0x00,0x02,0x00,0x00,0x03,0x00,0x00,
+  0x04,0x00,0x00,0x05,0x00,0x00,0x10,0x00,0x00,0x00,0x00,0x00,0x01,0x00,0x00,0x10,
+  0x00,0x00,0x00,0x00,0x00,0x01,0x00,0x00,0x03,0x00,0x00,0x04,0x00,0x00,0x05,0x00,
+  0x00,0x06,0x00,0x00,0x07,0x00,0x00,0x08,0x00,0x00,0x09,0x00,0x00,0x0a,0x00,0x00,
+  0x0b,0x00,0x00,0x0c,0x00,0x00,0x0d,0x00,0x00,0x0e,0x00,0x00,0x0f,0x00,0x00,0x00,
+  0x00,0x00,0x01,0x00,0x00,0x03,0x00,0x00,0x04,0x00,0x00,0x05,0x00,0x00,0x06,0x00,
+  0x00,0x07,0x12,0x66,0x00,0xbd,0x40,0x00,0x00,0x32,0xcb,0xfd,0x28,0x51,0x45,0x20,
+  0x00,0x00,0x19,0x65,0xfe,0x14,0x28,0xa2,0x10,0x00,0x00,0x0c,0xb2,0xff,0x0a,0x14,
+  0x51,0x08,0x00,0x00,0x06,0x59,0x7f,0x05,0x0a,0x28,0x04,0x00,0x00,0x03,0x2c,0xbf,
+  0x02,0x85,0x14,0x02,0x00,0x00,0x01,0x96,0x5f,0x01,0x42,0x8a,0x01,0x00,0x00,0x00,
+  0xcb,0x2f,0x00,0xa1,0x45,0x00,0x80,0x00,0x00,0x65,0x97,0x00,0x50,0xa2,0x00,0x40,
+  0x00,0x00,0x32,0xcb,0x00,0x28,0x51,0x00,0x20,0x00,0x00,0x19,0x65,0x00,0x14,0x28,
+  0x00,0x10,0x00,0x00,0x0c,0xb2,0x00,0x0a,0x14,0x00,0x08,0x00,0x00,0x06,0x59,0x00,
+  0x05,0x0a,0x00,0x04,0x00,0x00,0x03,0x2c,0x00,0x02,0x85,0x00,0x02,0x00,0x00,0x01,
+  0x96,0x00,0x01,0x42,0x00,0x01,0x00,0x00,0x00,0xcb,0x00,0x00,0xa1,0x00,0x00,0x80,
+  0x00,0x00,0x65,0x00,0x00,0x50,0x00,0x00,0x40,0x00,0x00,0x32,0x00,0x00,0x28,0x00,
+  0x00,0x1f,0x00,0x00,0x19,0x00,0x00,0x14,0x00,0x00,0x0f,0x00,0x00,0x0c,0x00,0x00,
+  0x0a,0x00,0x00,0x08,0x00,0x00,0x06,0x00,0x00,0x05,0x00,0x00,0x03,0x00,0x00,0x03,
+  0x00,0x00,0x02,0x12,0x10,0x00,0x48,0x00,0x00,0x01,0x00,0x00,0x02,0x00,0x00,0x04,
+  0x00,0x00,0x08,0x00,0x00,0x10,0x00,0x00,0x20,0x00,0x00,0x40,0x00,0x00,0x80,0x00,
+  0x01,0x00,0x00,0x02,0x00,0x00,0x04,0x00,0x00,0x08,0x00,0x00,0x10,0x00,0x00,0x20,
+  0x00,0x00,0x40,0x00,0x00,0x80,0x00,0x01,0x00,0x00,0x02,0x00,0x00,0x04,0x00,0x00,
+  0x08,0x00,0x00,0x10,0x00,0x00,0x20,0x00,0x00,0x40,0x00,0x00,0x80,0x00,0x00,0x12,
+  0xa5,0x00,0x33,0xff,0xff,0xfb,0xff,0xff,0xf9,0x00,0x00,0x03,0xff,0xff,0xf6,0x00,
+  0x00,0x04,0x00,0x00,0x05,0x00,0x00,0x06,0x00,0x00,0x07,0x00,0x00,0x08,0x00,0x00,
+  0x09,0x00,0x00,0x0a,0x00,0x00,0x0b,0x00,0x00,0x0c,0x00,0x00,0x0d,0x00,0x00,0x0e,
+  0x00,0x00,0x0f,0x00,0x00,0x10,0x12,0xb6,0x00,0x7e,0x00,0x00,0x04,0x00,0x00,0x04,
+  0x00,0x00,0x04,0x00,0x00,0x04,0x00,0x00,0x04,0x00,0x00,0x04,0x00,0x00,0x04,0x00,
+  0x00,0x04,0x00,0x00,0x04,0x00,0x00,0x04,0x00,0x00,0x04,0x00,0x00,0x03,0x00,0x00,
+  0x03,0x00,0x00,0x03,0x00,0x00,0x03,0x00,0x00,0x03,0x00,0x00,0x03,0x00,0x00,0x03,
+  0x00,0x00,0x03,0x00,0x00,0x03,0x00,0x00,0x03,0x00,0x00,0x03,0x00,0x00,0x03,0x00,
+  0x00,0x02,0x00,0x00,0x02,0x00,0x00,0x02,0x00,0x00,0x02,0x00,0x00,0x02,0x00,0x00,
+  0x02,0x00,0x00,0x02,0x00,0x00,0x04,0x00,0x00,0x04,0x00,0x00,0x03,0x00,0x00,0x03,
+  0x00,0x00,0x03,0x00,0x00,0x03,0x00,0x00,0x03,0x00,0x00,0x03,0x00,0x00,0x03,0x00,
+  0x00,0x03,0x00,0x00,0x03,0x00,0x00,0x03,0x12,0xe0,0x00,0x0c,0x00,0x00,0x19,0x00,
+  0x00,0x1c,0x00,0x00,0x06,0x00,0x00,0x0a,0x12,0xe4,0x00,0x0c,0x00,0x00,0x03,0x00,
+  0x00,0x07,0x00,0x00,0x0b,0x00,0x00,0x0f,0x12,0xe8,0x00,0x0c,0x00,0x02,0xb6,0x00,
+  0x02,0xb6,0x00,0x02,0xd4,0x00,0x02,0xd4,0x12,0xec,0x00,0x0c,0x00,0x02,0x28,0x00,
+  0x02,0x28,0x00,0x02,0x50,0x00,0x02,0x50,0x12,0xf0,0x00,0x33,0x00,0x00,0x03,0x00,
+  0x00,0x05,0x00,0x00,0x07,0x00,0x00,0x09,0x00,0x00,0x0f,0x00,0x00,0x1f,0x00,0x00,
+  0x3f,0x00,0x00,0x7f,0x00,0x00,0xff,0x00,0x01,0xff,0x00,0x03,0xff,0x00,0x07,0xff,
+  0x00,0x0f,0xff,0x00,0x1f,0xff,0x00,0x3f,0xff,0x00,0x7f,0xff,0x00,0xff,0xff,0x13,
+  0x01,0x00,0x2a,0x00,0x01,0x40,0x00,0x01,0xe0,0x00,0x02,0x30,0x00,0x02,0x80,0x00,
+  0x03,0x20,0x00,0x03,0xc0,0x00,0x04,0x60,0x00,0x05,0x00,0x00,0x06,0x40,0x00,0x07,
+  0x80,0x00,0x08,0xc0,0x00,0x0a,0x00,0x00,0x0c,0x80,0x00,0x0f,0x00,0x13,0x0f,0x00,
+  0x09,0x00,0x01,0xb9,0x00,0x01,0xe0,0x00,0x01,0x40,0x13,0x12,0x00,0x0c,0x00,0x00,
+  0x58,0x00,0x00,0x5e,0x00,0x00,0x1a,0x00,0x00,0x26,0x13,0x16,0x00,0x6f,0x16,0xa0,
+  0x9e,0xe9,0x5f,0x62,0x1d,0x90,0x6b,0xe2,0x6f,0x95,0x0c,0x3e,0xf1,0xf3,0xc1,0x0f,
+  0x1f,0x62,0x97,0xe0,0x9d,0x69,0x1a,0x9b,0x66,0x11,0xc7,0x3b,0xee,0x38,0xc5,0x06,
+  0x3e,0x2e,0x1f,0xd8,0x8d,0x1e,0x9f,0x41,0x1c,0x38,0xb2,0x18,0xbc,0x80,0x14,0x4c,
+  0xf3,0x0f,0x15,0xae,0x09,0x4a,0x03,0x03,0x22,0xf4,0x01,0x91,0xf6,0x17,0xb5,0xdf,
+  0x0d,0xae,0x88,0x1e,0x21,0x21,0x07,0xc6,0x7e,0x1b,0x72,0x83,0x13,0x0f,0xf7,0x1f,
+  0xa7,0x55,0x1f,0xf6,0x21,0x15,0x7d,0x69,0x1c,0xed,0x7a,0x0a,0xc7,0xcd,0x1f,0x0a,
+  0x7e,0x10,0x73,0x87,0x19,0xb3,0xe0,0x04,0xb2,0x04,0x00,0x00,0x00,0x1d,0x40,0x05,
+  0x40,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+  0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+  0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+  0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+  0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+  0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+  0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+  0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+  0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+  0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+  0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+  0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+  0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+  0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+  0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+  0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+  0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+  0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+  0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+  0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+  0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+  0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+  0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+  0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+  0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+  0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+  0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+  0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+  0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+  0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+  0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+  0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+  0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+  0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+  0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+  0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+  0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+  0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+  0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+  0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+  0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+  0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+  0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+  0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+  0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+  0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+  0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+  0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+  0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+  0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+  0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+  0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+  0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+  0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+  0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+  0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+  0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+  0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+  0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+  0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+  0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+  0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+  0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+  0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+  0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+  0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+  0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+  0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+  0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+  0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+  0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+  0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+  0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+  0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+  0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+  0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+  0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+  0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+  0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+  0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+  0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+  0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+  0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+  0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+  0x00,0x1f,0x00,0x03,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+  0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+  0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+  0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+  0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+  0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+  0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+  0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+  0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+  0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+  0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+  0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+  0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+  0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+  0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+  0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+  0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+  0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+  0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+  0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+  0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+  0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+  0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+  0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+  0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+  0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+  0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+  0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+  0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+  0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+  0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+  0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+  0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+  0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+  0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+  0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+  0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+  0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+  0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+  0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+  0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+  0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+  0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+  0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+  0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+  0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+  0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+  0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+  0x00,0x00,0x00,0x00,0x00,0x04,0x00,0x01,0x5f,0x00,0x00,0x01,0x00,0x00,0x03,0x00,
+  0x00,0x07,0x00,0x00,0x0f,0x00,0x00,0x1f,0x00,0x00,0x3f,0x00,0x00,0x7f,0x00,0x00,
+  0xff,0x00,0x01,0xff,0x00,0x03,0xff,0x00,0x07,0xff,0x00,0x0f,0xff,0x00,0x1f,0xff,
+  0x00,0x3f,0xff,0x00,0x7f,0xff,0x00,0xff,0xff,0x00,0x00,0x00,0x00,0x00,0x08,0x00,
+  0x00,0x10,0xff,0xff,0xfd,0xff,0xff,0xff,0xff,0xf0,0x00,0x00,0x00,0x00,0x00,0x00,
+  0x00,0x00,0x00,0x0f,0x00,0x00,0x00,0x00,0x02,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+  0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+  0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+  0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+  0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+  0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+  0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+  0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+  0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x40,0x00,
+  0x00,0x40,0x00,0x00,0x40,0x00,0x00,0x40,0x00,0x00,0x00,0x00,0x00,0x00,0x06,0x00,
+  0x00,0x0d,0x40,0x00,0x00,0x02,0x00,0x00,0x00,0x00,0x05,0xc0,0x00,0x05,0xc0,0x00,
+  0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+  0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x0f,0x00,0x00,0x0f,0x00,0x00,0x0f,0x00,
+  0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+  0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x01,0x00,0x00,0x00,0x00,0x00,0x08,0x00,0x00,
+  0x02,0x00,0x00,0x00,0x00,0x05,0xe0,0x00,0x05,0xe0,0x00,0x00,0x00,0x00,0x00,0x00,
+  0x00,0x00,0xc0,0x00,0x01,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+  0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x06,0x00,0x00,0x00,0x00,0x00,
+  0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+  0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+  0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+  0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+  0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+  0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+  0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+  0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+  0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+  0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+  0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+  0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+  0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+  0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+  0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+  0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+  0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+  0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+  0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+  0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+  0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+  0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+  0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+  0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+  0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+  0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+  0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+  0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+  0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+  0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+  0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+  0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+  0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+  0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+  0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+  0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+  0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+  0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+  0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+  0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+  0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+  0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+  0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+  0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+  0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+  0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+  0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+  0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+  0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+  0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+  0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+  0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+  0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+  0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+  0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+  0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+  0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+  0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+  0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+  0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+  0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+  0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+  0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+  0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+  0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+  0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+  0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+  0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+  0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+  0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+  0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+  0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+  0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+  0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+  0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+  0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+  0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+  0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+  0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+  0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+  0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+  0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+  0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+  0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+  0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+  0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+  0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+  0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+  0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+  0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+  0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+  0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+  0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+  0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+  0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+  0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x02,0x00,0x06,0x00,
+  0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+  0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+  0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+  0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+  0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+  0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+  0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+  0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+  0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+  0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+  0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+  0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+  0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+  0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+  0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+  0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+  0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+  0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+  0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+  0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+  0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+  0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+  0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+  0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+  0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+  0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+  0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+  0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+  0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+  0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+  0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+  0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+  0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+  0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+  0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+  0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+  0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+  0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+  0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+  0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+  0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+  0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+  0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+  0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+  0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+  0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+  0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+  0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+  0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+  0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+  0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+  0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+  0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+  0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+  0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+  0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+  0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+  0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+  0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+  0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+  0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+  0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+  0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+  0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+  0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+  0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+  0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+  0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+  0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+  0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+  0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+  0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+  0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+  0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+  0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+  0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+  0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+  0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+  0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+  0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+  0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+  0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+  0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+  0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+  0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+  0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+  0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+  0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+  0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+  0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+  0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+  0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+  0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+  0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+  0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+  0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+  0x04,0x80,0x00,0x60,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+  0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+  0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+  0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+  0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+  0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+  0x00,0x00,0x00,0x00,0x04,0xa0,0x00,0x60,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+  0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+  0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+  0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+  0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+  0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+  0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x04,0xc0,0x00,0x60,0x00,0x00,0x00,0x00,
+  0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+  0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+  0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+  0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+  0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+  0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x04,0xe0,0x00,0x60,
+  0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+  0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+  0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+  0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+  0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+  0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+  0x05,0x00,0x00,0x60,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+  0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+  0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+  0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+  0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+  0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+  0x00,0x00,0x00,0x00,0x05,0x20,0x00,0x60,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+  0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+  0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+  0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+  0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+  0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+  0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x05,0x40,0x00,0x60,0x00,0x00,0x00,0x00,
+  0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+  0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+  0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+  0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+  0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+  0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x05,0x60,0x00,0x60,
+  0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+  0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+  0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+  0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+  0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+  0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+  0x07,0xc0,0x00,0x60,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+  0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+  0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+  0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+  0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+  0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+  0x00,0x00,0x00,0x00,0x05,0xc0,0x00,0x60,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+  0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+  0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+  0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+  0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+  0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+  0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x05,0xe0,0x00,0x30,0x00,0x00,0x00,0x00,
+  0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+  0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+  0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x06,0x00,0x05,0x40,
+  0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+  0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+  0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+  0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+  0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+  0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+  0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+  0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+  0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+  0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+  0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+  0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+  0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+  0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+  0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+  0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+  0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+  0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+  0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+  0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+  0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+  0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+  0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+  0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+  0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+  0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+  0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+  0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+  0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+  0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+  0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+  0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+  0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+  0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+  0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+  0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+  0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+  0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+  0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+  0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+  0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+  0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+  0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+  0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+  0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+  0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+  0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+  0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+  0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+  0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+  0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+  0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+  0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+  0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+  0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+  0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+  0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+  0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+  0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+  0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+  0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+  0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+  0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+  0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+  0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+  0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+  0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+  0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+  0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+  0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+  0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+  0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+  0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+  0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+  0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+  0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+  0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+  0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+  0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+  0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+  0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+  0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+  0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+  0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+  0x05,0x80,0x00,0x66,0x2a,0xaa,0xaa,0x33,0x33,0x33,0x24,0x92,0x49,0x38,0xe3,0x8e,
+  0x22,0x22,0x22,0x21,0x08,0x42,0x20,0x82,0x08,0x20,0x40,0x81,0x20,0x20,0x20,0x20,
+  0x10,0x08,0x20,0x08,0x02,0x20,0x04,0x00,0x20,0x02,0x00,0x20,0x01,0x00,0x20,0x00,
+  0x80,0x20,0x00,0x40,0x20,0x00,0x20,0x10,0x00,0x00,0x10,0x00,0x00,0x08,0x00,0x00,
+  0x10,0x00,0x00,0x04,0x00,0x00,0x02,0x00,0x00,0x01,0x00,0x00,0x00,0x80,0x00,0x00,
+  0x40,0x00,0x00,0x20,0x00,0x00,0x10,0x00,0x00,0x08,0x00,0x00,0x04,0x00,0x00,0x02,
+  0x00,0x00,0x00,0xff,0x00,0x00,0x80,0x00,0x00,0x40,0x04,0x78,0x00,0x18,0x01,0xe8,
+  0x0d,0x03,0x6c,0x16,0x02,0x40,0x0a,0x00,0x10,0x03,0xa8,0x00,0x00,0x00,0xcc,0x00,
+  0x30,0x00,0x00,0x00,0x00,0x02,0x10,0x00,0x00,0x1b,0xcf,0x83,0x40,0x00,0x00,0x00,
+  0xc7,0x8b,0x17,0x70,0x41,0xc1,0xc7,0x8c,0x26,0xc7,0x8c,0x26,0xc7,0x8b,0x36,0xc7,
+  0x8b,0xf2,0xc7,0x8c,0x52,0x13,0x40,0x1b,0xea,0x97,0x80,0xc0,0x00,0x04,0x00,0x97,
+  0x80,0xc8,0x00,0x04,0x00,0x70,0x78,0xcf,0x70,0x7b,0xcd,0x0f,0xd7,0xc2,0x00,0x00,
+  0x01,0xc9,0x83,0x46,0x70,0x02,0xec,0x97,0x80,0xdc,0x00,0x00,0x05,0x97,0x80,0xe0,
+  0x00,0x07,0xbf,0xd7,0x02,0x8f,0x70,0x7f,0xd7,0x70,0x7c,0xc7,0x70,0x7d,0xcb,0x70,
+  0x7e,0xd5,0x97,0x80,0x4c,0x00,0x06,0x00,0x97,0x80,0x4d,0x00,0x0d,0x40,0x70,0x50,
+  0x51,0x70,0x10,0x52,0x70,0x10,0x55,0x70,0x5a,0x5b,0x70,0x5a,0x5c,0x70,0x4e,0x62,
+  0x70,0x10,0x3e,0x0f,0xcd,0xcd,0xff,0xdf,0xff,0x0f,0x4b,0x4b,0xff,0xff,0xf7,0x70,
+  0x10,0x46,0xd5,0x03,0x3a,0x70,0x10,0xc2,0xd4,0x40,0x00,0xd1,0x80,0x00,0xd5,0x80,
+  0x00,0x70,0x10,0xd4,0xc7,0x8a,0x47,0x70,0x00,0x1e,0x70,0x10,0x1b,0xc7,0x89,0xb0,
+  0x23,0x10,0xc2,0xc9,0x83,0x70,0xcf,0x83,0x5a,0x27,0x1b,0x1b,0x00,0x00,0x01,0x37,
+  0x1b,0xc2,0x00,0x00,0x0c,0xc9,0x83,0x6c,0x70,0x10,0x34,0xc7,0x89,0xb0,0x0b,0x00,
+  0x20,0xc9,0x03,0x6a,0xc7,0x89,0xb0,0x0b,0x00,0xc2,0xc9,0x03,0x6a,0xc7,0x89,0xb0,
+  0x0b,0x00,0xc2,0xc9,0x83,0x7c,0x97,0x80,0x21,0x00,0x00,0x02,0x70,0x00,0x1e,0xc7,
+  0x89,0xb0,0x0b,0x00,0x22,0x70,0x12,0x1e,0xc7,0x89,0xb0,0x0b,0x0f,0x61,0x47,0x61,
+  0x23,0x00,0x10,0x00,0x70,0x24,0x6b,0x47,0x61,0xc2,0x00,0x40,0x00,0x0b,0x01,0x24,
+  0x47,0x61,0xc2,0x00,0x80,0x00,0x0b,0x00,0x25,0x47,0x61,0xc2,0x01,0x00,0x00,0x0b,
+  0x00,0x26,0x47,0x61,0xc2,0x04,0x00,0x00,0x0b,0x01,0x27,0x47,0x61,0xc2,0x10,0x00,
+  0x00,0x0b,0x01,0x28,0x47,0x61,0xc2,0x20,0x00,0x00,0x0b,0x00,0x29,0x47,0x61,0xc2,
+  0x40,0x00,0x00,0x0b,0x00,0x2a,0x0f,0x61,0x2b,0x00,0x00,0x03,0x27,0x24,0xc2,0x00,
+  0x03,0x0f,0xd1,0x40,0x00,0x90,0x00,0x1f,0x4f,0x1f,0x1f,0x00,0x10,0x00,0x27,0x23,
+  0xc2,0x00,0x03,0x00,0xd1,0x40,0x00,0x90,0x00,0x30,0x4f,0x30,0x30,0x00,0x00,0x90,
+  0x40,0x10,0xc2,0x70,0x30,0xc2,0xd6,0x00,0x17,0x7b,0x1f,0xc2,0x58,0x10,0x3d,0x47,
+  0x3d,0x3d,0x00,0x20,0x00,0x4f,0x3d,0x3d,0x00,0x00,0x08,0x4f,0x25,0x25,0x00,0x00,
+  0x08,0x70,0x25,0xc2,0x23,0x3d,0x3d,0x37,0x3d,0x3e,0x00,0x00,0x20,0xca,0x03,0x6a,
+  0x27,0x3e,0xc2,0x00,0x35,0xe0,0xca,0x83,0x6a,0x27,0x23,0xc2,0x00,0x00,0x00,0xc9,
+  0x03,0xeb,0x37,0x27,0xc2,0x00,0x00,0x03,0xc9,0x03,0xcc,0x37,0x23,0x1f,0x00,0x00,
+  0x06,0x37,0x1f,0xc2,0x00,0x00,0x00,0xcb,0x83,0xd9,0xcf,0x83,0xd1,0x37,0x23,0x1f,
+  0x00,0x00,0x02,0x37,0x1f,0xc2,0x00,0x00,0x00,0xcb,0x83,0xd9,0x37,0x24,0xc2,0x00,
+  0x00,0x02,0xc9,0x03,0xd7,0x97,0x80,0x2d,0x00,0x00,0x02,0xcf,0x83,0xef,0x70,0x01,
+  0x2d,0xcf,0x83,0xef,0x37,0x27,0xc2,0x00,0x00,0x03,0xc9,0x03,0xe3,0x37,0x23,0x1f,
+  0x00,0x00,0x0a,0xcb,0x83,0xee,0x37,0x24,0xc2,0x00,0x00,0x01,0xc9,0x03,0xee,0xcf,
+  0x83,0xe9,0x37,0x23,0xc2,0x00,0x00,0x06,0xcb,0x83,0xee,0x37,0x24,0xc2,0x00,0x00,
+  0x01,0xc9,0x03,0xee,0x70,0x00,0x2d,0xcf,0x83,0xef,0x37,0x24,0xc2,0x00,0x00,0x01,
+  0xc9,0x83,0xe9,0x70,0x10,0x2d,0x27,0x22,0xc2,0x00,0x00,0x00,0xc9,0x83,0xf5,0x70,
+  0x12,0x1e,0xc7,0x89,0xb0,0x0b,0x0f,0x2c,0x27,0x62,0x62,0x00,0x00,0x00,0xc9,0x03,
+  0xfb,0x27,0x62,0x62,0xff,0xff,0xff,0xcb,0x83,0x66,0x37,0x27,0xc2,0x00,0x00,0x03,
+  0xc9,0x84,0x01,0x70,0x00,0x2e,0x70,0x00,0x2f,0xcf,0x84,0x0a,0x97,0x80,0x2e,0x00,
+  0x00,0x02,0x37,0x27,0xc2,0x00,0x00,0x01,0xc9,0x84,0x08,0x70,0x01,0x2f,0xcf,0x84,
+  0x0a,0x97,0x80,0x2f,0x00,0x00,0x02,0x70,0x1d,0x37,0x70,0x3e,0x38,0x70,0x1c,0x39,
+  0x70,0x5b,0x5c,0x27,0x2d,0xc2,0x00,0x03,0x12,0xd1,0x40,0x00,0x90,0x00,0x5d,0x37,
+  0x27,0xc2,0x00,0x00,0x03,0xc9,0x04,0x17,0x4f,0x5d,0x5d,0x00,0x00,0x02,0x70,0x2e,
+  0x32,0x27,0x2d,0xc2,0x00,0x02,0xe8,0xd1,0x40,0x00,0x90,0x00,0x30,0x97,0x80,0xd8,
+  0x00,0x04,0x80,0x27,0x28,0xc2,0x00,0x02,0xe4,0xd5,0x40,0x00,0x94,0x00,0x31,0x94,
+  0x00,0xc2,0xd6,0x40,0x00,0xc7,0x89,0x85,0x70,0x2f,0x32,0x27,0x2d,0xc2,0x00,0x02,
+  0xe0,0xd1,0x40,0x00,0x30,0x31,0xc2,0xd6,0x40,0x00,0xc7,0x89,0x85,0x70,0x2e,0x32,
+  0x97,0x80,0xf0,0x00,0x04,0x80,0x97,0x80,0xd8,0x00,0x04,0xa0,0x97,0x80,0x1e,0x00,
+  0x00,0x02,0x27,0x28,0xc2,0x00,0x02,0xe4,0xd5,0x40,0x00,0x94,0x00,0x31,0x94,0x00,
+  0xc2,0xd6,0x40,0x00,0xc7,0x89,0x61,0x70,0x2f,0x32,0x27,0x2d,0xc2,0x00,0x02,0xe0,
+  0xd1,0x40,0x00,0x30,0x31,0xc2,0xd6,0x40,0x00,0xc7,0x89,0x61,0x27,0x64,0xc2,0x00,
+  0x00,0x00,0xc9,0x84,0x6d,0x27,0x22,0xc2,0x00,0x00,0x00,0xc9,0x84,0x6d,0x70,0x37,
+  0x1d,0x70,0x38,0x3e,0x70,0x39,0x1c,0x97,0x80,0x63,0x00,0xff,0xff,0x0f,0x61,0xc2,
+  0x00,0xff,0x00,0xc7,0x8a,0x19,0x4f,0x61,0x5f,0x00,0x01,0x00,0x0f,0x5f,0xc2,0x00,
+  0xff,0x00,0xc7,0x8a,0x19,0x70,0x11,0x1e,0x27,0x5d,0x5d,0xff,0xff,0xf8,0xca,0x84,
+  0x5a,0xc7,0x8a,0x15,0xcf,0x84,0x55,0x70,0x00,0x1e,0x27,0x5d,0xc2,0x00,0x00,0x08,
+  0xc9,0x04,0x61,0x23,0x14,0xc2,0xd6,0x40,0x00,0xc7,0x8a,0x3a,0x0f,0x63,0x63,0x00,
+  0xff,0xff,0x70,0x2c,0xc2,0x33,0x63,0xc2,0xc9,0x04,0x6d,0x0f,0x4b,0x4b,0xff,0xff,
+  0xf7,0x17,0x4b,0x4b,0x00,0x00,0x07,0x27,0x59,0x59,0x00,0x00,0x01,0xcf,0x83,0x66,
+  0x70,0x5c,0x5b,0x70,0x24,0xc2,0x33,0x6b,0xc2,0xc9,0x83,0x5a,0x27,0x24,0xc2,0x00,
+  0x04,0x74,0xcf,0xc0,0x00,0xcf,0x84,0x78,0xcf,0x84,0x7c,0xcf,0x84,0x80,0xcf,0x83,
+  0x5a,0x70,0x79,0xcf,0x0f,0xcd,0xcd,0xff,0xfb,0xff,0xcf,0x84,0x83,0x70,0x78,0xcf,
+  0x0f,0xcd,0xcd,0xff,0xfb,0xff,0xcf,0x84,0x83,0x70,0x7a,0xcf,0x17,0xcd,0xcd,0x00,
+  0x04,0x00,0x0f,0xcd,0xc2,0x00,0x20,0x00,0xc9,0x84,0x8d,0x27,0x57,0xc2,0x00,0x00,
+  0x00,0xc9,0x84,0x8d,0x0f,0x4b,0x4b,0xff,0xff,0xfb,0x17,0x4b,0x4b,0x00,0x00,0x0b,
+  0x70,0x2e,0x32,0x97,0x80,0xf0,0x00,0x04,0x80,0x97,0x80,0xd8,0x00,0x04,0xa0,0x97,
+  0x80,0xe8,0x00,0x07,0xc0,0x97,0x80,0x1e,0x00,0x00,0x06,0x27,0x28,0xc2,0x00,0x02,
+  0xe4,0xd5,0x40,0x00,0x94,0x00,0x31,0x94,0x00,0xc2,0xd6,0x40,0x00,0xc7,0x89,0x06,
+  0x70,0x2f,0x32,0x27,0x2d,0xc2,0x00,0x02,0xe0,0xd1,0x40,0x00,0x30,0x31,0xc2,0xd6,
+  0x40,0x00,0xc7,0x89,0x06,0x70,0x2e,0x32,0x70,0x10,0x1b,0x97,0x80,0xf0,0x00,0x04,
+  0x80,0x27,0x28,0xc2,0x00,0x02,0xe4,0xd5,0x40,0x00,0x94,0x00,0x31,0x94,0x00,0xc2,
+  0xd6,0x40,0x00,0xc7,0x88,0xba,0x70,0x2f,0x32,0x27,0x2d,0xc2,0x00,0x02,0xe0,0xd1,
+  0x40,0x00,0x30,0x31,0xc2,0xd6,0x40,0x00,0xc7,0x88,0xba,0x70,0x2e,0x32,0x70,0x10,
+  0x1b,0x97,0x80,0xf0,0x00,0x04,0x80,0x27,0x28,0xc2,0x00,0x02,0xe4,0xd5,0x40,0x00,
+  0x94,0x00,0x31,0x94,0x00,0xc2,0xd6,0x40,0x00,0xc7,0x86,0xbd,0x70,0x2f,0x32,0x27,
+  0x2d,0xc2,0x00,0x02,0xe0,0xd1,0x40,0x00,0x30,0x31,0xc2,0x90,0x00,0x31,0xd6,0x40,
+  0x00,0xc7,0x86,0xbd,0x37,0x31,0xc2,0x00,0x00,0x1d,0xd6,0x40,0x00,0xc7,0x88,0x74,
+  0x97,0x80,0x1f,0x00,0x00,0x06,0x37,0x27,0xc2,0x00,0x00,0x03,0xc9,0x84,0xd5,0x97,
+  0x80,0x30,0x00,0x00,0x02,0xcf,0x84,0xd6,0x70,0x00,0x30,0x27,0x1f,0xc2,0x00,0x04,
+  0xd9,0xcf,0xc0,0x00,0xcf,0x85,0x18,0xcf,0x85,0x13,0xcf,0x85,0x01,0xcf,0x84,0xfc,
+  0xcf,0x84,0xea,0xcf,0x84,0xe5,0xcf,0x84,0xe0,0x97,0x80,0x37,0x00,0x04,0xc0,0x97,
+  0x80,0x1a,0x00,0x00,0x00,0xcf,0x85,0x34,0x97,0x80,0x37,0x00,0x05,0x20,0x97,0x80,
+  0x1a,0x00,0x02,0x00,0xcf,0x85,0x34,0x22,0x14,0x16,0x0f,0x16,0x16,0x00,0x00,0x0f,
+  0x27,0x17,0x17,0x00,0x00,0x01,0x27,0x19,0x19,0x00,0x00,0x01,0x0f,0x19,0x19,0x00,
+  0x00,0x0f,0x97,0x80,0x37,0x00,0x04,0xe0,0x97,0x80,0x1a,0x00,0x00,0x00,0x27,0x4f,
+  0x4f,0x00,0x00,0x00,0xc9,0x05,0x34,0x70,0x10,0x4f,0xcf,0x85,0x34,0x97,0x80,0x37,
+  0x00,0x05,0x40,0x97,0x80,0x1a,0x00,0x02,0x00,0xcf,0x85,0x34,0x22,0x14,0x16,0x0f,
+  0x16,0x16,0x00,0x00,0x0f,0x27,0x17,0x17,0x00,0x00,0x01,0x27,0x19,0x19,0x00,0x00,
+  0x01,0x0f,0x19,0x19,0x00,0x00,0x0f,0x97,0x80,0x37,0x00,0x05,0x00,0x97,0x80,0x1a,
+  0x00,0x00,0x00,0x27,0x4f,0x4f,0x00,0x00,0x00,0xc9,0x05,0x34,0x70,0x10,0x4f,0xcf,
+  0x85,0x34,0x97,0x80,0x37,0x00,0x05,0x60,0x97,0x80,0x1a,0x00,0x02,0x00,0xcf,0x85,
+  0x34,0x22,0x14,0x16,0x0f,0x16,0x16,0x00,0x00,0x0f,0x27,0x17,0x17,0x00,0x00,0x01,
+  0x27,0x19,0x19,0x00,0x00,0x01,0x0f,0x19,0x19,0x00,0x00,0x0f,0x27,0x4f,0x4f,0x00,
+  0x00,0x00,0xc9,0x05,0x25,0x70,0x10,0x4f,0x97,0x80,0xf0,0x00,0x04,0xc0,0x97,0x80,
+  0xd0,0x00,0x05,0x20,0xd5,0x03,0x3a,0xd1,0x80,0x00,0xd6,0x00,0x02,0xc7,0x8a,0x9f,
+  0xd1,0x80,0x04,0x27,0x34,0x34,0x00,0x00,0x01,0x37,0x34,0xc2,0x00,0x00,0x0c,0xc9,
+  0x84,0xb6,0xcf,0x83,0x66,0x70,0x37,0xf0,0x27,0x37,0xd8,0x00,0x00,0x1f,0xd6,0x00,
+  0x07,0xc7,0x8c,0x5a,0x70,0x37,0xf0,0x27,0x37,0xd8,0x00,0x00,0x0f,0xd6,0x00,0x03,
+  0xc7,0x8c,0x5a,0x27,0x37,0xf0,0x00,0x00,0x1b,0x27,0x37,0xd8,0x00,0x00,0x14,0xd1,
+  0x03,0x16,0xd5,0x03,0x16,0xc7,0x8c,0x61,0xc7,0x8c,0x61,0xc7,0x8c,0x61,0xc7,0x8c,
+  0x61,0x70,0x37,0xf0,0x27,0x37,0xd8,0x00,0x00,0x07,0xd6,0x00,0x01,0xc7,0x8c,0x5a,
+  0x27,0x37,0xf0,0x00,0x00,0x0d,0x27,0x37,0xd8,0x00,0x00,0x0a,0xd6,0x00,0x01,0xc7,
+  0x8c,0x61,0x27,0x37,0xf0,0x00,0x00,0x10,0x27,0x37,0xd8,0x00,0x00,0x17,0xd6,0x00,
+  0x01,0xc7,0x8c,0x5a,0x27,0x37,0xf0,0x00,0x00,0x1c,0x27,0x37,0xd8,0x00,0x00,0x1b,
+  0xd6,0x00,0x01,0xc7,0x8c,0x5a,0x70,0x37,0xf0,0x27,0x37,0xd8,0x00,0x00,0x03,0xc7,
+  0x8c,0x5a,0x27,0x37,0xf0,0x00,0x00,0x06,0x27,0x37,0xd8,0x00,0x00,0x05,0xc7,0x8c,
+  0x61,0x27,0x37,0xf0,0x00,0x00,0x08,0x27,0x37,0xd8,0x00,0x00,0x0b,0xc7,0x8c,0x5a,
+  0x27,0x37,0xf0,0x00,0x00,0x0e,0x27,0x37,0xd8,0x00,0x00,0x0d,0xc7,0x8c,0x5a,0x27,
+  0x37,0xf0,0x00,0x00,0x1d,0x27,0x37,0xd8,0x00,0x00,0x12,0xd1,0x03,0x1a,0xd5,0x03,
+  0x18,0xd6,0x00,0x01,0xc7,0x8c,0x61,0xd1,0x03,0x19,0xd5,0x03,0x1a,0xd6,0x00,0x01,
+  0xc7,0x8c,0x61,0x70,0x37,0xf0,0x27,0x37,0xd8,0x00,0x00,0x01,0xd1,0x03,0x16,0xd5,
+  0x03,0x16,0xc7,0x8c,0x61,0x27,0x37,0xf0,0x00,0x00,0x03,0x27,0x37,0xd8,0x00,0x00,
+  0x02,0xd1,0x03,0x1a,0xd5,0x03,0x18,0xc7,0x8c,0x6e,0x27,0x37,0xf0,0x00,0x00,0x04,
+  0x27,0x37,0xd8,0x00,0x00,0x07,0xc7,0x8c,0x7b,0x27,0x37,0xf0,0x00,0x00,0x0e,0x27,
+  0x37,0xd8,0x00,0x00,0x09,0xd1,0x03,0x1a,0xd5,0x03,0x18,0xc7,0x8c,0x61,0xd1,0x03,
+  0x19,0xd5,0x03,0x1a,0xc7,0x8c,0x61,0x27,0x37,0xf0,0x00,0x00,0x10,0x27,0x37,0xd8,
+  0x00,0x00,0x13,0xc7,0x8c,0x5a,0x27,0x37,0xf0,0x00,0x00,0x16,0x27,0x37,0xd8,0x00,
+  0x00,0x15,0xc7,0x8c,0x5a,0x27,0x37,0xf0,0x00,0x00,0x18,0x27,0x37,0xd8,0x00,0x00,
+  0x1b,0xc7,0x8c,0x5a,0x27,0x37,0xf0,0x00,0x00,0x1e,0x27,0x37,0xd8,0x00,0x00,0x1d,
+  0xc7,0x8c,0x5a,0x27,0x37,0xf0,0x00,0x00,0x07,0x27,0x37,0xd8,0x00,0x00,0x04,0xd1,
+  0x03,0x21,0xd5,0x03,0x1c,0xc7,0x8c,0x6e,0xd1,0x03,0x1e,0xd5,0x03,0x1f,0xc7,0x8c,
+  0x6e,0x27,0x37,0xf0,0x00,0x00,0x08,0x27,0x37,0xd8,0x00,0x00,0x0b,0xc7,0x8c,0x7b,
+  0x27,0x37,0xf0,0x00,0x00,0x0c,0x27,0x37,0xd8,0x00,0x00,0x0f,0xc7,0x8c,0x7b,0x27,
+  0x37,0xf0,0x00,0x00,0x1e,0x27,0x37,0xd8,0x00,0x00,0x11,0xd1,0x03,0x21,0xd5,0x03,
+  0x1c,0xc7,0x8c,0x61,0xd1,0x03,0x1d,0xd5,0x03,0x21,0xc7,0x8c,0x61,0x27,0x37,0xf0,
+  0x00,0x00,0x1a,0x27,0x37,0xd8,0x00,0x00,0x15,0xd1,0x03,0x1e,0xd5,0x03,0x1f,0xc7,
+  0x8c,0x61,0xd1,0x03,0x20,0xd5,0x03,0x1e,0xc7,0x8c,0x61,0x27,0x37,0xf0,0x00,0x00,
+  0x0f,0x27,0x37,0xd8,0x00,0x00,0x08,0xd1,0x03,0x29,0xd5,0x03,0x22,0xc7,0x8c,0x6e,
+  0xd1,0x03,0x25,0xd5,0x03,0x26,0xc7,0x8c,0x6e,0xd1,0x03,0x27,0xd5,0x03,0x24,0xc7,
+  0x8c,0x6e,0xd1,0x03,0x23,0xd5,0x03,0x28,0xc7,0x8c,0x6e,0x27,0x37,0xf0,0x00,0x00,
+  0x10,0x27,0x37,0xd8,0x00,0x00,0x13,0xc7,0x8c,0x7b,0x27,0x37,0xf0,0x00,0x00,0x14,
+  0x27,0x37,0xd8,0x00,0x00,0x17,0xc7,0x8c,0x7b,0x27,0x37,0xf0,0x00,0x00,0x18,0x27,
+  0x37,0xd8,0x00,0x00,0x1b,0xc7,0x8c,0x7b,0x27,0x37,0xf0,0x00,0x00,0x1c,0x27,0x37,
+  0xd8,0x00,0x00,0x1f,0xc7,0x8c,0x7b,0xd1,0x80,0x00,0xd5,0x80,0x00,0x70,0x37,0xf0,
+  0x97,0x80,0xd0,0x00,0x04,0xa0,0x97,0x80,0xd4,0x00,0x00,0x05,0x47,0xb2,0xc2,0x2d,
+  0x41,0x3c,0x57,0x11,0xc2,0x02,0x00,0x00,0x86,0x00,0x96,0xd6,0x00,0x0e,0x70,0xb2,
+  0x96,0x27,0x37,0xd8,0x00,0x00,0x1f,0xd1,0x03,0x2a,0xd5,0x03,0x32,0xd6,0x00,0x07,
+  0xc7,0x8c,0x82,0xd1,0x03,0x31,0xd5,0x03,0x39,0x97,0x00,0xc2,0x13,0x12,0xc2,0xd7,
+  0x40,0x00,0xd6,0x00,0x07,0xc7,0x8c,0x88,0x97,0x00,0xc2,0x1b,0x12,0xc2,0xd7,0x40,
+  0x00,0x97,0x80,0xd4,0x00,0x00,0x09,0x97,0x80,0xd0,0x00,0x04,0xa0,0x70,0x1a,0xc2,
+  0x23,0x19,0xf0,0xc7,0x8b,0x07,0x0f,0x17,0x17,0x00,0x00,0x01,0xc9,0x86,0x27,0x97,
+  0x80,0xd0,0x00,0x04,0xbf,0xd6,0x00,0x1e,0xc7,0x8b,0x03,0xcf,0x86,0x29,0xd6,0x00,
+  0x1e,0xc7,0x8b,0x07,0x70,0x37,0xe8,0xd1,0x80,0x04,0xd5,0x80,0x01,0x0f,0x17,0x17,
+  0x00,0x00,0x01,0xc9,0x86,0x44,0x27,0x16,0xc2,0x00,0x00,0x00,0xd1,0x40,0x00,0x27,
+  0x1a,0xf0,0x00,0x01,0x20,0xd6,0x00,0x0f,0xc7,0x86,0x5e,0x27,0x16,0xc2,0x00,0x01,
+  0x10,0xd1,0x40,0x00,0x27,0x1a,0xf0,0x00,0x00,0x01,0xc7,0x86,0x99,0x27,0x16,0xc2,
+  0x00,0x01,0x10,0xd1,0x40,0x00,0x27,0x1a,0xf0,0x00,0x00,0x10,0xd6,0x00,0x0e,0xc7,
+  0x86,0x89,0xcf,0x86,0x57,0x27,0x16,0xc2,0x00,0x00,0x00,0xd1,0x40,0x00,0x27,0x1a,
+  0xf0,0x00,0x01,0x00,0xd6,0x00,0x0f,0xc7,0x86,0x74,0x27,0x16,0xc2,0x00,0x01,0x10,
+  0xd1,0x40,0x00,0x70,0x1a,0xf0,0xc7,0x86,0xab,0x27,0x16,0xc2,0x00,0x01,0x10,0xd1,
+  0x40,0x00,0x27,0x1a,0xf0,0x00,0x02,0x10,0xd6,0x00,0x0e,0xc7,0x86,0x90,0x27,0x1a,
+  0xc2,0x00,0x00,0x00,0xc9,0x06,0x5b,0xcf,0x86,0x5b,0x70,0x1f,0xc2,0x33,0x30,0x1f,
+  0xcf,0x84,0xd6,0x22,0x12,0xc2,0xd1,0x40,0x00,0x27,0xf0,0xf0,0xff,0xff,0xe0,0x41,
+  0xb2,0xc2,0x61,0xb2,0xc2,0x51,0xb2,0xc2,0x61,0xb2,0xc2,0x51,0xb2,0xc2,0x61,0xb2,
+  0xc2,0x51,0xb2,0xc2,0x61,0xb2,0xc2,0x51,0xb2,0xc2,0x61,0xb2,0xc2,0x51,0xb2,0xc2,
+  0x61,0xb2,0xc2,0x51,0xb2,0xc2,0x61,0xb2,0xc2,0x51,0xb2,0xc2,0x61,0xb2,0xc2,0x86,
+  0x00,0xaa,0xdf,0x80,0x0a,0x22,0x12,0xc2,0xd1,0x40,0x00,0x40,0x10,0xc2,0x61,0xb2,
+  0xc2,0x51,0xb2,0xc2,0x61,0xb2,0xc2,0x51,0xb2,0xc2,0x61,0xb2,0xc2,0x51,0xb2,0xc2,
+  0x61,0xb2,0xc2,0x51,0xb2,0xc2,0x61,0xb2,0xc2,0x51,0xb2,0xc2,0x61,0xb2,0xc2,0x51,
+  0xb2,0xc2,0x61,0xb2,0xc2,0x51,0xb2,0xc2,0x61,0xb2,0xc2,0x51,0xb2,0xc2,0x86,0x00,
+  0xaa,0xdf,0x80,0x0a,0x22,0x12,0xc2,0xd1,0x40,0x00,0x40,0x10,0xc2,0xd6,0x00,0x0f,
+  0x61,0xb2,0xc2,0x86,0x00,0xaa,0xdf,0x80,0x0a,0x22,0x12,0xc2,0xd1,0x40,0x00,0x27,
+  0xf0,0xf0,0xff,0xff,0xe0,0x40,0x10,0xc2,0xd6,0x00,0x0f,0x61,0xb2,0xc2,0x86,0x00,
+  0xaa,0xdf,0x80,0x0a,0x41,0x10,0xc2,0x61,0xb2,0xc2,0x61,0xb2,0x39,0x61,0xb2,0xc2,
+  0x61,0xb2,0x39,0x61,0xb2,0xc2,0x61,0xb2,0x39,0x61,0xb2,0xc2,0x61,0xb2,0x39,0x61,
+  0xb2,0xc2,0x61,0xb2,0x39,0x61,0xb2,0xc2,0x61,0xb2,0x39,0x61,0xb2,0xc2,0x61,0xb2,
+  0x39,0x60,0xb2,0xc2,0x86,0x00,0xaa,0xdf,0x80,0x0a,0x40,0x10,0xc2,0x61,0xb2,0xc2,
+  0x61,0xb2,0x39,0x61,0xb2,0xc2,0x61,0xb2,0x39,0x61,0xb2,0xc2,0x61,0xb2,0x39,0x61,
+  0xb2,0xc2,0x61,0xb2,0x39,0x61,0xb2,0xc2,0x61,0xb2,0x39,0x61,0xb2,0xc2,0x61,0xb2,
+  0x39,0x61,0xb2,0xc2,0x61,0xb2,0x39,0x60,0xb2,0xc2,0x86,0x00,0xaa,0xdf,0x80,0x0a,
+  0x0f,0xb0,0x1f,0x00,0x0f,0xff,0xc9,0x06,0xdf,0x27,0x1b,0xc2,0x00,0x07,0xc0,0x23,
+  0x1b,0xe8,0x37,0x34,0xc2,0x00,0x00,0x03,0xca,0x06,0xd1,0x37,0x34,0xc2,0x00,0x00,
+  0x07,0xca,0x06,0xd8,0x47,0xa8,0xc2,0x00,0x01,0x00,0x0b,0x05,0x18,0x27,0x18,0xc2,
+  0x00,0x02,0x66,0xd1,0x40,0x00,0x20,0x10,0x18,0xcf,0x86,0xdf,0x0f,0xa8,0x18,0x00,
+  0x00,0x3f,0x27,0x18,0xc2,0x00,0x02,0x66,0xd1,0x40,0x00,0x20,0x10,0x18,0xcf,0x86,
+  0xdf,0x47,0xa8,0xc2,0x01,0x00,0x00,0x0b,0x05,0x18,0x27,0x18,0xc2,0x00,0x02,0x66,
+  0xd1,0x40,0x00,0x20,0x10,0x18,0x0f,0xb0,0xc2,0xff,0xf0,0x00,0xc9,0x07,0x01,0x27,
+  0x1b,0xc2,0x00,0x07,0xc1,0x23,0x1b,0xe8,0x37,0x34,0xc2,0x00,0x00,0x03,0xca,0x06,
+  0xf3,0x37,0x34,0xc2,0x00,0x00,0x07,0xca,0x06,0xfa,0x47,0xa8,0xc2,0x00,0x01,0x00,
+  0x0b,0x05,0x36,0x27,0x36,0xc2,0x00,0x02,0x66,0xd1,0x40,0x00,0x20,0x10,0x36,0xcf,
+  0x87,0x01,0x0f,0xa8,0x36,0x00,0x00,0x3f,0x27,0x36,0xc2,0x00,0x02,0x66,0xd1,0x40,
+  0x00,0x20,0x10,0x36,0xcf,0x87,0x01,0x47,0xa8,0xc2,0x01,0x00,0x00,0x0b,0x05,0x36,
+  0x27,0x36,0xc2,0x00,0x02,0x66,0xd1,0x40,0x00,0x20,0x10,0x36,0x0f,0xb0,0x1f,0x00,
+  0x0f,0xff,0xc9,0x08,0x23,0x27,0x1b,0x37,0x00,0x04,0xc0,0x27,0x1b,0x38,0x00,0x04,
+  0xe0,0x27,0x1b,0x39,0x00,0x05,0x00,0x27,0x1f,0xc2,0x00,0x02,0xa4,0xd1,0x40,0x00,
+  0x20,0x10,0x1e,0xca,0x87,0x31,0x70,0x37,0xe8,0xc7,0x89,0xb0,0xc7,0x88,0xaa,0x57,
+  0x11,0xc2,0x02,0x00,0x00,0x86,0x00,0xc2,0x23,0x10,0x3a,0x43,0x18,0xc2,0x57,0x11,
+  0xc2,0x02,0x00,0x00,0x86,0x00,0xa8,0x70,0x38,0xe8,0xc7,0x89,0xb0,0xc7,0x88,0xaa,
+  0x57,0x11,0xc2,0x02,0x00,0x00,0x86,0x00,0xc2,0x23,0x10,0x3b,0x43,0x18,0xc2,0x57,
+  0x11,0xc2,0x02,0x00,0x00,0x86,0x00,0xa8,0x70,0x39,0xe8,0xc7,0x89,0xb0,0xc7,0x88,
+  0xaa,0x57,0x11,0xc2,0x02,0x00,0x00,0x86,0x00,0xc2,0x23,0x10,0x3c,0x43,0x18,0xc2,
+  0x57,0x11,0xc2,0x02,0x00,0x00,0x86,0x00,0xa8,0xcf,0x88,0x2c,0x37,0x1e,0x1e,0x00,
+  0x00,0x00,0x27,0x1e,0xd8,0x00,0x03,0xff,0x70,0x37,0xe8,0xc7,0x89,0xb0,0x0b,0x98,
+  0xa8,0x37,0x1f,0xc2,0x00,0x00,0x04,0xc9,0x07,0x89,0x37,0x1f,0xc2,0x00,0x00,0x02,
+  0xc9,0x07,0xd6,0x27,0x10,0x35,0x00,0x00,0x30,0x40,0x10,0xc2,0x70,0xa8,0xc2,0xd6,
+  0x00,0x05,0x7b,0x35,0xc2,0x5f,0x10,0x30,0x00,0x00,0x00,0x4f,0x30,0x30,0x00,0x00,
+  0x02,0x47,0x30,0x30,0x40,0x00,0x00,0x47,0x35,0xc2,0xf0,0x00,0x00,0x4b,0x30,0x35,
+  0x70,0xa8,0xc2,0x23,0x35,0xa8,0xc7,0x88,0x9f,0x57,0x11,0xc2,0x02,0x00,0x00,0x86,
+  0x00,0xc2,0x23,0x10,0x3a,0x43,0x18,0xc2,0x57,0x11,0xc2,0x02,0x00,0x00,0x86,0x00,
+  0xa8,0x70,0x38,0xe8,0x70,0x30,0xa8,0x27,0x10,0x35,0x00,0x00,0x18,0x40,0x10,0xc2,
+  0x70,0x30,0xc2,0xd6,0x00,0x03,0x7b,0x35,0xc2,0x5f,0x10,0x30,0x00,0x00,0x00,0x47,
+  0x35,0xc2,0xe0,0x00,0x00,0x4b,0x30,0x35,0x70,0xa8,0xc2,0x23,0x35,0xa8,0xc7,0x88,
+  0x9f,0x57,0x11,0xc2,0x02,0x00,0x00,0x86,0x00,0xc2,0x23,0x10,0x3b,0x43,0x18,0xc2,
+  0x57,0x11,0xc2,0x02,0x00,0x00,0x86,0x00,0xa8,0x70,0x39,0xe8,0x70,0x30,0xa8,0x27,
+  0x10,0x35,0x00,0x00,0x0c,0x40,0x10,0xc2,0x70,0x30,0xc2,0xd6,0x00,0x02,0x7b,0x35,
+  0xc2,0x5f,0x10,0x30,0x00,0x00,0x00,0x47,0x35,0xc2,0xc0,0x00,0x00,0x4b,0x30,0x35,
+  0x70,0xa8,0xc2,0x23,0x35,0xa8,0xc7,0x88,0x9f,0x57,0x11,0xc2,0x02,0x00,0x00,0x86,
+  0x00,0xc2,0x23,0x10,0x3c,0x43,0x18,0xc2,0x57,0x11,0xc2,0x02,0x00,0x00,0x86,0x00,
+  0xa8,0xcf,0x88,0x2c,0x27,0x10,0x35,0x00,0x04,0x80,0x40,0x10,0xc2,0x70,0xa8,0xc2,
+  0xd6,0x00,0x0a,0x7b,0x35,0xc2,0x5f,0x10,0x30,0x00,0x00,0x00,0x47,0x30,0x30,0x20,
+  0x00,0x00,0x47,0x35,0xc2,0xfe,0x00,0x00,0x4b,0x30,0x35,0x70,0xa8,0xc2,0x23,0x35,
+  0xa8,0xc7,0x88,0x94,0x57,0x11,0xc2,0x02,0x00,0x00,0x86,0x00,0xc2,0x23,0x10,0x3a,
+  0x43,0x18,0xc2,0x57,0x11,0xc2,0x02,0x00,0x00,0x86,0x00,0xa8,0x70,0x38,0xe8,0x70,
+  0x30,0xa8,0x27,0x10,0x35,0x00,0x00,0x90,0x40,0x10,0xc2,0x70,0x30,0xc2,0xd6,0x00,
+  0x07,0x7b,0x35,0xc2,0x5f,0x10,0x30,0x00,0x00,0x00,0x47,0x30,0x30,0x20,0x00,0x00,
+  0x47,0x35,0xc2,0xf0,0x00,0x00,0x4b,0x30,0x35,0x70,0xa8,0xc2,0x23,0x35,0xa8,0xc7,
+  0x88,0x94,0x57,0x11,0xc2,0x02,0x00,0x00,0x86,0x00,0xc2,0x23,0x10,0x3b,0x43,0x18,
+  0xc2,0x57,0x11,0xc2,0x02,0x00,0x00,0x86,0x00,0xa8,0x70,0x39,0xe8,0x70,0x30,0xa8,
+  0x27,0x10,0x35,0x00,0x00,0x12,0x40,0x10,0xc2,0x70,0x30,0xc2,0xd6,0x00,0x05,0x7b,
+  0x35,0xc2,0x5f,0x10,0x30,0x00,0x00,0x00,0x47,0x30,0x30,0x08,0x00,0x00,0x47,0x35,
+  0xc2,0x80,0x00,0x00,0x4b,0x30,0x35,0x70,0xa8,0xc2,0x23,0x35,0xa8,0xc7,0x88,0x94,
+  0x57,0x11,0xc2,0x02,0x00,0x00,0x86,0x00,0xc2,0x23,0x10,0x3c,0x43,0x18,0xc2,0x57,
+  0x11,0xc2,0x02,0x00,0x00,0x86,0x00,0xa8,0xcf,0x88,0x2c,0x27,0x10,0x35,0x00,0x00,
+  0xa0,0x40,0x10,0xc2,0x70,0xa8,0xc2,0xd6,0x00,0x07,0x7b,0x35,0xc2,0x5f,0x10,0x30,
+  0x00,0x00,0x00,0x47,0x30,0x30,0x40,0x00,0x00,0x47,0x35,0xc2,0xf8,0x00,0x00,0x4b,
+  0x30,0x35,0x70,0xa8,0xc2,0x23,0x35,0xa8,0xc7,0x88,0x89,0x57,0x11,0xc2,0x02,0x00,
+  0x00,0x86,0x00,0xc2,0x23,0x10,0x3a,0x43,0x18,0xc2,0x57,0x11,0xc2,0x02,0x00,0x00,
+  0x86,0x00,0xa8,0x70,0x38,0xe8,0x70,0x30,0xa8,0x27,0x10,0x35,0x00,0x00,0x28,0x40,
+  0x10,0xc2,0x70,0x30,0xc2,0xd6,0x00,0x05,0x7b,0x35,0xc2,0x5f,0x10,0x30,0x00,0x00,
+  0x00,0x47,0x30,0x30,0x40,0x00,0x00,0x47,0x35,0xc2,0xe0,0x00,0x00,0x4b,0x30,0x35,
+  0x70,0xa8,0xc2,0x23,0x35,0xa8,0xc7,0x88,0x89,0x57,0x11,0xc2,0x02,0x00,0x00,0x86,
+  0x00,0xc2,0x23,0x10,0x3b,0x43,0x18,0xc2,0x57,0x11,0xc2,0x02,0x00,0x00,0x86,0x00,
+  0xa8,0x70,0x39,0xe8,0x70,0x30,0xa8,0x27,0x10,0x35,0x00,0x00,0x0a,0x40,0x10,0xc2,
+  0x70,0x30,0xc2,0xd6,0x00,0x03,0x7b,0x35,0xc2,0x5f,0x10,0x30,0x00,0x00,0x00,0x47,
+  0x30,0x30,0x40,0x00,0x00,0x47,0x35,0xc2,0x80,0x00,0x00,0x4b,0x30,0x35,0x70,0xa8,
+  0xc2,0x23,0x35,0xa8,0xc7,0x88,0x89,0x57,0x11,0xc2,0x02,0x00,0x00,0x86,0x00,0xc2,
+  0x23,0x10,0x3c,0x43,0x18,0xc2,0x57,0x11,0xc2,0x02,0x00,0x00,0x86,0x00,0xa8,0xcf,
+  0x88,0x2c,0x27,0x1b,0xe8,0x00,0x04,0xc0,0x70,0x10,0xa8,0x27,0x1b,0xe8,0x00,0x04,
+  0xe0,0x70,0x10,0xa8,0x27,0x1b,0xe8,0x00,0x05,0x00,0x70,0x10,0xa8,0x37,0x32,0xc2,
+  0x00,0x00,0x02,0xc9,0x88,0x44,0x0f,0xe8,0xc2,0x00,0x00,0x20,0xc9,0x88,0x3f,0x27,
+  0x1b,0x37,0x00,0x05,0x20,0x27,0x1b,0x38,0x00,0x05,0x40,0x27,0x1b,0x39,0x00,0x05,
+  0x60,0x70,0x36,0x18,0x0f,0xb0,0x1f,0xff,0xf0,0x00,0x47,0x1f,0x1f,0x00,0x10,0x00,
+  0xc9,0x87,0x0a,0xcf,0x88,0x66,0x27,0xf0,0xf0,0x00,0x00,0x01,0x27,0x1b,0x1b,0x00,
+  0x00,0x01,0xdf,0x80,0x0a,0x37,0x32,0xc2,0x00,0x00,0x03,0xc9,0x88,0x66,0x0f,0xb0,
+  0x1f,0xff,0xf0,0x00,0x47,0x1f,0x1f,0x00,0x10,0x00,0xc9,0x08,0x66,0x70,0x3a,0xc2,
+  0x27,0x1b,0xe8,0x00,0x05,0x20,0x43,0x36,0xc2,0x57,0x11,0xc2,0x02,0x00,0x00,0x86,
+  0x00,0xa8,0x70,0x3b,0xc2,0x27,0x1b,0xe8,0x00,0x05,0x40,0x43,0x36,0xc2,0x57,0x11,
+  0xc2,0x02,0x00,0x00,0x86,0x00,0xa8,0x70,0x3c,0xc2,0x27,0x1b,0xe8,0x00,0x05,0x60,
+  0x43,0x36,0xc2,0x57,0x11,0xc2,0x02,0x00,0x00,0x86,0x00,0xa8,0x27,0x1b,0x1b,0x00,
+  0x00,0x01,0x27,0xf0,0xf0,0x00,0x00,0x01,0xdf,0x80,0x0a,0x27,0x1b,0xe8,0x00,0x05,
+  0x20,0x70,0x10,0xa8,0x27,0x1b,0xe8,0x00,0x05,0x40,0x70,0x10,0xa8,0x27,0x1b,0xe8,
+  0x00,0x05,0x60,0x70,0x10,0xa8,0x27,0x1b,0x1b,0x00,0x00,0x01,0x27,0xf0,0xf0,0x00,
+  0x00,0x01,0xdf,0x80,0x0a,0x27,0x1b,0xe8,0x00,0x04,0xc0,0x70,0x10,0xa8,0x27,0x1b,
+  0xe8,0x00,0x04,0xe0,0x70,0x10,0xa8,0x27,0x1b,0xe8,0x00,0x05,0x00,0x70,0x10,0xa8,
+  0x27,0x1b,0xe8,0x00,0x05,0x20,0x70,0x10,0xa8,0x27,0x1b,0xe8,0x00,0x05,0x40,0x70,
+  0x10,0xa8,0x27,0x1b,0xe8,0x00,0x05,0x60,0x70,0x10,0xa8,0x27,0x1b,0x1b,0x00,0x00,
+  0x01,0xdf,0x80,0x0a,0x27,0xa8,0xa8,0xff,0xff,0xfc,0x4f,0xa8,0xc2,0x08,0x00,0x00,
+  0x27,0x1f,0xf8,0x00,0x05,0x90,0x23,0xb8,0xc2,0x27,0x1f,0xf8,0x00,0x05,0x7f,0x43,
+  0xb8,0xc2,0xdf,0x80,0x0a,0x27,0xa8,0xa8,0xff,0xff,0xf8,0x4f,0xa8,0xc2,0x04,0x00,
+  0x00,0x27,0x1f,0xf8,0x00,0x05,0x90,0x23,0xb8,0xc2,0x27,0x1f,0xf8,0x00,0x05,0x7f,
+  0x43,0xb8,0xc2,0xdf,0x80,0x0a,0x27,0xa8,0xa8,0xff,0xff,0xfe,0x4f,0xa8,0xc2,0x10,
+  0x00,0x00,0x27,0x1f,0xf8,0x00,0x05,0x90,0x23,0xb8,0xc2,0x27,0x1f,0xf8,0x00,0x05,
+  0x7f,0x43,0xb8,0xc2,0xdf,0x80,0x0a,0x93,0x00,0xa8,0x37,0x1e,0xc2,0x00,0x02,0x28,
+  0xd1,0x40,0x00,0x48,0xa8,0xa8,0x1f,0xa8,0xa8,0x80,0x00,0x00,0x47,0xa8,0xc2,0x40,
+  0x00,0x00,0x27,0x1f,0xf8,0x00,0x05,0x90,0x23,0xb8,0xc2,0x27,0x1f,0xf8,0x00,0x05,
+  0x7f,0x43,0xb8,0xc2,0xdf,0x80,0x0a,0x37,0x32,0xc2,0x00,0x00,0x01,0xc9,0x88,0xc6,
+  0x0f,0xb0,0x30,0x00,0x0f,0xff,0x70,0x10,0xb0,0xc9,0x08,0xdb,0xc7,0x88,0xdf,0x20,
+  0x00,0xb2,0x27,0x1b,0x1b,0x00,0x00,0x01,0xdf,0x80,0x0a,0x70,0xb0,0x1f,0x70,0x10,
+  0xb0,0x0f,0x1f,0x30,0x00,0x0f,0xff,0xc9,0x08,0xcd,0xc7,0x88,0xdf,0x20,0x00,0xb0,
+  0x0f,0x1f,0x30,0xff,0xf0,0x00,0xc9,0x08,0xdb,0x47,0x30,0x30,0x00,0x10,0x00,0xc7,
+  0x88,0xdf,0x20,0x00,0x1f,0x47,0x1f,0xc2,0x00,0x10,0x00,0x5f,0xb0,0xb2,0x00,0x00,
+  0x01,0x27,0x1b,0x1b,0x00,0x00,0x01,0xdf,0x80,0x0a,0x70,0xb0,0xb2,0x27,0x1b,0x1b,
+  0x00,0x00,0x01,0xdf,0x80,0x0a,0x37,0x2d,0xc2,0x00,0x00,0x01,0xca,0x88,0xfb,0x37,
+  0x1b,0xc2,0x00,0x00,0x02,0xca,0x08,0xf7,0x37,0x1b,0xc2,0x00,0x00,0x0a,0xca,0x08,
+  0xf3,0x37,0x1b,0xc2,0x00,0x00,0x16,0xca,0x08,0xef,0x27,0x30,0xc2,0x00,0x02,0x4c,
+  0xd1,0x40,0x00,0xdf,0x80,0x0a,0x27,0x30,0xc2,0x00,0x02,0x45,0xd1,0x40,0x00,0xdf,
+  0x80,0x0a,0x27,0x30,0xc2,0x00,0x02,0x36,0xd1,0x40,0x00,0xdf,0x80,0x0a,0x27,0x30,
+  0xc2,0x00,0x02,0x27,0xd1,0x40,0x00,0xdf,0x80,0x0a,0x37,0x1b,0xc2,0x00,0x00,0x01,
+  0xca,0x09,0x02,0x27,0x30,0xc2,0x00,0x02,0x5e,0xd1,0x40,0x00,0xdf,0x80,0x0a,0x27,
+  0x30,0xc2,0x00,0x02,0x4f,0xd1,0x40,0x00,0xdf,0x80,0x0a,0x37,0x32,0xc2,0x00,0x00,
+  0x01,0xc9,0x89,0x13,0x0f,0xb2,0xc2,0x00,0x0f,0xff,0xc9,0x09,0x2b,0x0f,0x98,0x30,
+  0x00,0x0f,0xff,0x27,0x30,0xc2,0x00,0x09,0x2f,0xc7,0xc0,0x00,0xcf,0x89,0x2b,0xdf,
+  0x80,0x0a,0x70,0xb2,0x1f,0x0f,0x1f,0xc2,0x00,0x0f,0xff,0xc9,0x09,0x1c,0x0f,0x98,
+  0x30,0x00,0x0f,0xff,0x27,0x30,0xc2,0x00,0x09,0x2f,0xc7,0xc0,0x00,0x0f,0x1f,0xc2,
+  0xff,0xf0,0x00,0xc9,0x09,0x2b,0x27,0xe8,0xe8,0x00,0x00,0x01,0x0f,0x9a,0x30,0xff,
+  0xf0,0x00,0x47,0x30,0x30,0x00,0x10,0x00,0x27,0x30,0xc2,0x00,0x09,0x2f,0xc7,0xc0,
+  0x00,0x27,0xe8,0xe8,0x00,0x00,0x01,0xdf,0x80,0x0a,0x27,0xe8,0xe8,0x00,0x00,0x02,
+  0x70,0x98,0x9a,0xdf,0x80,0x0a,0xcf,0x89,0x52,0xcf,0x89,0x47,0xcf,0x89,0x3e,0xcf,
+  0x89,0x33,0xc7,0x89,0xb0,0x0b,0x05,0x30,0xc7,0x89,0xb0,0x0b,0x05,0xa8,0x47,0x30,
+  0xc2,0x00,0x00,0x01,0x57,0xa8,0xc2,0x00,0x01,0x00,0x5f,0xa8,0xa8,0x01,0x00,0x00,
+  0xdf,0x80,0x0a,0xc7,0x89,0xb0,0x0b,0x05,0xa8,0x47,0xa8,0xc2,0x00,0x00,0x01,0x57,
+  0xa8,0xc2,0x00,0x01,0x00,0x5f,0xa8,0xa8,0x01,0x00,0x00,0xdf,0x80,0x0a,0xc7,0x89,
+  0xb0,0x0b,0x05,0x30,0xc7,0x89,0xb0,0x0b,0x05,0xa8,0x47,0x30,0xc2,0x00,0x00,0x01,
+  0x57,0x30,0xc2,0x00,0x01,0x00,0x5f,0xa8,0xa8,0x01,0x00,0x00,0xdf,0x80,0x0a,0xc7,
+  0x89,0xb0,0x0b,0x05,0x30,0xc7,0x89,0xb0,0x0b,0x05,0xa8,0x47,0x30,0xc2,0x00,0x00,
+  0x01,0x5f,0xa8,0x30,0x00,0x01,0x00,0xc7,0x89,0xb0,0x0b,0x05,0xa8,0x47,0x30,0xc2,
+  0x00,0x00,0x01,0x5f,0xa8,0xa8,0x01,0x00,0x00,0xdf,0x80,0x0a,0x37,0x32,0xc2,0x00,
+  0x00,0x01,0xc9,0x89,0x6c,0x0f,0xb2,0xc2,0x00,0x0f,0xff,0xc9,0x09,0x83,0xc7,0x89,
+  0xd8,0x0b,0x01,0x9a,0x27,0x5d,0x5d,0x00,0x00,0x02,0xdf,0x80,0x0a,0x70,0xb2,0x1f,
+  0x70,0x10,0x98,0x0f,0x1f,0xc2,0x00,0x0f,0xff,0xc9,0x09,0x75,0xc7,0x89,0xd8,0x0b,
+  0x01,0x98,0x27,0x5d,0x5d,0x00,0x00,0x02,0x0f,0x1f,0xc2,0xff,0xf0,0x00,0xc9,0x09,
+  0x81,0xc7,0x89,0xd8,0x0b,0x01,0x1f,0x47,0x1f,0xc2,0x00,0x10,0x00,0x5f,0x98,0x9a,
+  0x00,0x00,0x01,0x27,0x5d,0x5d,0x00,0x00,0x02,0xdf,0x80,0x0a,0x70,0x98,0x9a,0xdf,
+  0x80,0x0a,0x70,0x10,0x9a,0xdf,0x80,0x0a,0x37,0x32,0xc2,0x00,0x00,0x02,0xc9,0x09,
+  0x94,0x37,0x32,0xc2,0x00,0x00,0x03,0xc9,0x09,0xa3,0x70,0x30,0xc2,0xd1,0x40,0x00,
+  0x21,0x10,0x1e,0x22,0x10,0x30,0x27,0x1e,0xf0,0x00,0x03,0xff,0xc7,0x89,0xd8,0x0b,
+  0xb0,0x9a,0xdf,0x80,0x0a,0x70,0x30,0xc2,0xd1,0x40,0x00,0x21,0x10,0x1e,0x22,0x10,
+  0x30,0x27,0x1e,0xf0,0x00,0x03,0xff,0xc7,0x89,0xd8,0x0b,0xb0,0x98,0xc7,0x89,0xd8,
+  0x0b,0xb0,0x1f,0x47,0x1f,0xc2,0x00,0x10,0x00,0x5f,0x98,0x9a,0x00,0x00,0x01,0xdf,
+  0x80,0x0a,0x70,0x30,0xc2,0xd1,0x40,0x00,0x21,0x10,0x1e,0x22,0x10,0x30,0x27,0x1e,
+  0xf0,0x00,0x03,0xff,0xc7,0x89,0xd8,0x0b,0xb0,0x98,0x47,0x98,0xc2,0x00,0x10,0x00,
+  0x5f,0x98,0x9a,0x00,0x00,0x01,0xdf,0x80,0x0a,0x70,0x1e,0xc2,0x23,0x3e,0x3e,0x27,
+  0x1e,0xc2,0x00,0x02,0x10,0xd1,0x40,0x00,0x70,0x1d,0xc2,0x33,0x1e,0x1d,0xca,0x09,
+  0xd4,0x37,0x1d,0xd0,0x00,0x03,0xff,0x37,0x1d,0xc2,0x00,0x02,0x10,0xd5,0x40,0x00,
+  0x40,0x1c,0x1c,0x70,0x5b,0xc2,0xd1,0x40,0x00,0x32,0x5a,0xc2,0xc9,0x89,0xc4,0xc7,
+  0x8a,0x0d,0xcf,0x89,0xc0,0xc7,0x8a,0x00,0x90,0x00,0x33,0x22,0x00,0x5b,0x37,0x5b,
+  0xc2,0x00,0x0f,0xff,0xca,0x09,0xcc,0x97,0x80,0x5b,0x00,0x0f,0x00,0x44,0x33,0xc2,
+  0x0b,0x90,0xc2,0x13,0x1c,0xc2,0x27,0x1d,0x1d,0x00,0x00,0x18,0x5f,0x10,0x1c,0x00,
+  0x00,0x00,0xdf,0x80,0x0a,0x40,0x1c,0xc2,0x5f,0x10,0x1c,0x00,0x00,0x00,0xdf,0x80,
+  0x0a,0x70,0x1e,0xc2,0x23,0x3e,0x3e,0x27,0x1e,0xc2,0x00,0x02,0x10,0xd1,0x40,0x00,
+  0x70,0x1d,0xc2,0x33,0x1e,0x1d,0xca,0x09,0xfc,0x37,0x1d,0xd0,0x00,0x03,0xff,0x37,
+  0x1d,0xc2,0x00,0x02,0x10,0xd5,0x40,0x00,0x40,0x1c,0x1c,0x70,0x5c,0xc2,0xd1,0x40,
+  0x00,0x32,0x5a,0xc2,0xc9,0x89,0xec,0xc7,0x8a,0x0d,0xcf,0x89,0xe8,0xc7,0x8a,0x00,
+  0x90,0x00,0x33,0x22,0x00,0x5c,0x37,0x5c,0xc2,0x00,0x0f,0xff,0xca,0x09,0xf4,0x97,
+  0x80,0x5c,0x00,0x0f,0x00,0x44,0x33,0xc2,0x0b,0x90,0xc2,0x13,0x1c,0xc2,0x27,0x1d,
+  0x1d,0x00,0x00,0x18,0x5f,0x10,0x1c,0x00,0x00,0x00,0xdf,0x80,0x0a,0x40,0x1c,0xc2,
+  0x5f,0x10,0x1c,0x00,0x00,0x00,0xdf,0x80,0x0a,0x4b,0x6c,0xc2,0xd9,0x00,0x02,0xcb,
+  0x8a,0x04,0x23,0x6e,0xc2,0x33,0x6d,0xc2,0xdb,0x80,0x02,0x0f,0x73,0x73,0x00,0xff,
+  0xff,0x17,0x73,0x58,0x0e,0x00,0x00,0xc7,0x8b,0x0b,0x70,0x10,0x6c,0xdf,0x80,0x0a,
+  0x27,0x6c,0x6c,0x00,0x00,0x00,0xd9,0x00,0x02,0x97,0x80,0x58,0x0e,0x00,0x00,0xc7,
+  0x8b,0x0b,0x70,0x10,0x6c,0xdf,0x80,0x0a,0xc7,0x89,0xb0,0x0b,0x07,0x5e,0x4f,0x5e,
+  0xc2,0x00,0x01,0x00,0x1b,0x63,0x5e,0x0f,0x5e,0x5e,0x00,0xff,0x00,0x47,0x5e,0x60,
+  0x04,0x00,0x00,0x47,0x5e,0xc2,0x02,0x00,0x00,0x1b,0x60,0x60,0x47,0x5e,0x5e,0x01,
+  0x00,0x00,0x70,0x5e,0xc2,0x80,0x00,0xc2,0x1b,0x5e,0x5f,0x80,0x00,0xc2,0x1b,0x5f,
+  0x5f,0x80,0x00,0xc2,0x1b,0x5f,0x5f,0x80,0x00,0xc2,0x1b,0x5f,0x5f,0x80,0x00,0xc2,
+  0x1b,0x5f,0x5f,0x80,0x00,0xc2,0x1b,0x5f,0x5f,0x80,0x00,0xc2,0x1b,0x5f,0xc2,0x0b,
+  0x00,0x5f,0xc9,0x0a,0x36,0x1f,0x60,0x60,0x00,0x80,0x03,0x4f,0x63,0xc2,0x00,0x01,
+  0x00,0x1b,0x60,0x63,0xdf,0x80,0x0a,0xc7,0x89,0xb0,0x0b,0x00,0x5e,0x40,0x10,0xc2,
+  0x70,0x63,0xc2,0x82,0x00,0x63,0x47,0x63,0xc2,0x00,0x01,0x00,0x0b,0x00,0xc2,0x1b,
+  0x5e,0xc2,0xc9,0x0a,0x46,0x1f,0x63,0x63,0x00,0x80,0x05,0xdf,0x80,0x0a,0x70,0x66,
+  0xc2,0x23,0x3e,0xc2,0xcb,0x8a,0x67,0x27,0x66,0xc2,0x00,0x02,0x10,0xd1,0x40,0x00,
+  0x48,0x68,0x68,0x70,0x66,0x1e,0xc7,0x89,0xb0,0x27,0x66,0xd8,0x00,0x03,0xff,0x0b,
+  0x98,0xc2,0x13,0x68,0x68,0xc7,0x8a,0x74,0x70,0x11,0x66,0x70,0x11,0x1e,0x37,0x3e,
+  0xc2,0xff,0xff,0xf8,0xca,0x8a,0x5e,0xc7,0x89,0xb0,0x0b,0x07,0x68,0xc7,0x8a,0x74,
+  0xcf,0x8a,0x57,0x37,0x3e,0x1e,0x00,0x00,0x00,0x37,0x1e,0x66,0x00,0x00,0x08,0xc7,
+  0x89,0xb0,0x27,0x1e,0xd8,0x00,0x03,0xff,0x0b,0x98,0x68,0xdf,0x80,0x0a,0x93,0x00,
+  0x66,0x37,0x3e,0x1e,0x00,0x00,0x00,0x27,0x1e,0xc2,0x00,0x02,0x10,0xd1,0x40,0x00,
+  0x48,0x68,0x68,0xc7,0x89,0xb0,0x27,0x1e,0xd8,0x00,0x03,0xff,0x0b,0x98,0xc2,0x13,
+  0x68,0x68,0xdf,0x80,0x0a,0x70,0x69,0xd8,0x37,0x67,0xc2,0x00,0x00,0x02,0xca,0x83,
+  0x5a,0x27,0x67,0xc2,0x00,0x0a,0x7b,0xcf,0xc0,0x00,0xcf,0x8a,0x7e,0xcf,0x8a,0x94,
+  0xcf,0x8a,0x9a,0x97,0x80,0x67,0x00,0x00,0x02,0x70,0x9a,0xc2,0x0f,0xd8,0xd8,0x00,
+  0x00,0x0f,0x27,0xd8,0xc2,0x00,0x05,0xe0,0x33,0x6a,0xc2,0xc9,0x8a,0x8c,0x70,0x9a,
+  0xc2,0x0f,0xd8,0xd8,0x00,0x00,0x0f,0x27,0xd8,0x6a,0x00,0x05,0xe0,0x70,0x69,0xd8,
+  0x70,0x68,0xc2,0x13,0x98,0x9a,0x0f,0xd8,0xd8,0x00,0x00,0x0f,0x27,0xd8,0x69,0x00,
+  0x05,0xe0,0xdf,0x80,0x0a,0x97,0x80,0x67,0x00,0x00,0x00,0x4f,0x68,0xc2,0x00,0x01,
+  0x00,0x13,0x98,0x98,0xdf,0x80,0x0a,0x97,0x80,0x67,0x00,0x00,0x01,0x4f,0x68,0x98,
+  0x01,0x00,0x00,0xdf,0x80,0x0a,0x70,0x4c,0xc2,0x33,0xe0,0x31,0xcb,0x0a,0xa4,0x27,
+  0x31,0x31,0xff,0xfe,0x40,0x27,0x31,0x31,0x00,0x00,0x20,0xca,0x8a,0xa8,0xcf,0x8a,
+  0x9f,0xc7,0x8a,0xc1,0x70,0x4c,0xf8,0x70,0x46,0xc2,0xd6,0x00,0x1f,0x43,0xb2,0xba,
+  0x70,0xf8,0x4c,0x70,0x4d,0xc2,0xd1,0x40,0x00,0xd6,0x00,0x1f,0xc7,0x8a,0xbe,0x91,
+  0x00,0x4d,0x37,0x4c,0xc2,0x00,0x07,0xc0,0xc9,0x8a,0xb8,0x97,0x80,0x4c,0x00,0x06,
+  0x00,0x37,0x4d,0xc2,0x00,0x0f,0x00,0xc9,0x8a,0xbd,0x97,0x80,0x4d,0x00,0x0d,0x40,
+  0xdf,0x80,0x0a,0x44,0x92,0xc2,0xd0,0xc0,0x00,0xdf,0x80,0x0a,0x0f,0x4b,0xc2,0x00,
+  0x00,0x04,0xc9,0x0a,0xcf,0x70,0x10,0x47,0x70,0x10,0x48,0x27,0x46,0x46,0x00,0x00,
+  0x00,0xc9,0x8a,0xcf,0x24,0x10,0xc2,0xc9,0x8a,0xcf,0x0f,0xcd,0xcd,0xff,0xdf,0xff,
+  0x0f,0x4b,0x4b,0xff,0xff,0xfb,0x0f,0x4b,0xc2,0x00,0x00,0x08,0xc9,0x0a,0xdb,0x0f,
+  0xd7,0xc2,0x00,0x00,0x01,0xc9,0x8a,0xdb,0x70,0x49,0x47,0x70,0x4a,0x48,0x17,0xcd,
+  0xcd,0x00,0x20,0x00,0x0f,0x4b,0x4b,0xff,0xff,0xf7,0x0f,0x4b,0xc2,0x00,0x00,0x01,
+  0xc9,0x0a,0xee,0x70,0x47,0xc2,0x33,0x46,0xc2,0xcb,0x8a,0xe6,0x23,0x0b,0xc2,0xca,
+  0x0a,0xeb,0x27,0x46,0x46,0xff,0xf0,0x00,0xcf,0x8a,0xee,0x33,0x0b,0xc2,0xcb,0x0a,
+  0xeb,0x27,0x46,0x46,0x00,0x0f,0xff,0xcf,0x8a,0xee,0x0f,0x4b,0x4b,0xff,0xff,0xfe,
+  0x70,0x47,0x46,0x0f,0x4b,0xc2,0x00,0x00,0x02,0xc9,0x0b,0x02,0xd5,0x03,0x3a,0x34,
+  0x48,0xc2,0xca,0x8a,0xf9,0x33,0x0b,0xc2,0xcb,0x0a,0xfe,0x24,0x15,0xc2,0xd4,0x40,
+  0x00,0xcf,0x8b,0x02,0x23,0x0b,0xc2,0xca,0x0a,0xfe,0x24,0x0b,0xc2,0xd4,0x40,0x00,
+  0xcf,0x8b,0x02,0x0f,0x4b,0x4b,0xff,0xff,0xfd,0x70,0x48,0xc2,0xd4,0x40,0x00,0xdf,
+  0x80,0x0a,0x70,0x93,0xb0,0x27,0xf0,0xf0,0x00,0x00,0x10,0xdf,0x80,0x0a,0x70,0x92,
+  0xb0,0x27,0xf0,0xf0,0x00,0x00,0x10,0xdf,0x80,0x0a,0x70,0xd8,0x6f,0x70,0x51,0xd8,
+  0x70,0x58,0x9a,0x70,0xd8,0x51,0x0f,0xcb,0xd8,0x00,0x20,0x00,0xd9,0x00,0x02,0x47,
+  0x58,0xc9,0x00,0x01,0x00,0x70,0x00,0x52,0x70,0x6f,0xd8,0xdf,0x80,0x0a,0x93,0x00,
+  0x43,0x91,0x00,0x45,0x70,0xa2,0xc2,0x37,0xe0,0xc2,0x00,0x07,0xc0,0xcb,0x8b,0x1f,
+  0x97,0x80,0xe0,0x00,0x06,0x00,0x27,0xe0,0xc2,0x00,0x07,0x40,0xd1,0x40,0x00,0x32,
+  0x4d,0xc2,0xc9,0x0b,0x2b,0x48,0x11,0x41,0x4f,0xa0,0xc1,0x00,0x00,0x08,0x70,0x45,
+  0xc2,0xd1,0x40,0x00,0x70,0x43,0xc2,0xdf,0xc0,0x0b,0x70,0xa3,0xc2,0x37,0xe0,0xc2,
+  0x00,0x06,0x00,0xcb,0x0b,0x31,0x97,0x80,0xe0,0x00,0x07,0xbf,0x70,0x00,0x4f,0x70,
+  0x45,0xc2,0xd1,0x40,0x00,0x70,0x43,0xc2,0xdf,0xc0,0x0b,0x93,0x00,0x43,0x5b,0x10,
+  0x44,0x70,0xd8,0x45,0x27,0x55,0xc2,0x00,0x0b,0x3c,0xcf,0xc0,0x00,0xcf,0x8b,0x42,
+  0xcf,0x8b,0x47,0xcf,0x8b,0x4e,0xcf,0x8b,0x55,0xcf,0x8b,0x5a,0xcf,0x8b,0x61,0x4f,
+  0xc9,0x53,0x01,0x00,0x00,0x27,0x55,0x55,0x00,0x00,0x01,0xcf,0x8b,0xed,0x4f,0x53,
+  0xc2,0x00,0x00,0x01,0x5f,0xc9,0x53,0x00,0x01,0x00,0x27,0x55,0x55,0x00,0x00,0x01,
+  0xcf,0x8b,0xed,0x4f,0x53,0xc2,0x00,0x00,0x01,0x5f,0xc9,0x53,0x00,0x00,0x01,0x27,
+  0x55,0x55,0x00,0x00,0x01,0xcf,0x8b,0xed,0x4f,0xc9,0x54,0x01,0x00,0x00,0x27,0x55,
+  0x55,0x00,0x00,0x01,0xcf,0x8b,0xed,0x4f,0x54,0xc2,0x00,0x00,0x01,0x5f,0xc9,0x54,
+  0x00,0x01,0x00,0x27,0x55,0x55,0x00,0x00,0x01,0xcf,0x8b,0xed,0x4f,0x54,0xc2,0x00,
+  0x00,0x01,0x5f,0xc9,0x54,0x00,0x00,0x01,0x70,0x53,0xc2,0x33,0x54,0xc2,0xc9,0x8b,
+  0xe2,0x0f,0x53,0x56,0x7f,0x00,0x00,0x37,0x56,0xc2,0x10,0x00,0x00,0xcb,0x0b,0xe2,
+  0x97,0x80,0xc2,0x00,0x0b,0x72,0x57,0x53,0xc2,0x00,0x01,0x00,0xcf,0xc0,0x00,0xcf,
+  0x8b,0x82,0xcf,0x8b,0x88,0xcf,0x8b,0x90,0xcf,0x8b,0xa0,0xcf,0x8b,0xa8,0xcf,0x8b,
+  0xae,0xcf,0x8b,0xb0,0xcf,0x8b,0xb4,0xcf,0x8b,0xb7,0xcf,0x8b,0xbd,0xcf,0x8b,0xce,
+  0xcf,0x8b,0xd0,0xcf,0x8b,0xd3,0xcf,0x8b,0xd7,0xcf,0x8b,0xe2,0xcf,0x8b,0xe0,0x17,
+  0x4b,0x4b,0x00,0x00,0x01,0x4f,0x53,0x47,0x00,0x00,0x80,0x70,0x47,0x49,0xcf,0x8b,
+  0xec,0x17,0x4b,0x4b,0x00,0x00,0x02,0x0f,0x53,0x48,0x00,0xff,0xff,0x4f,0x48,0x48,
+  0x00,0x00,0x80,0x70,0x48,0x4a,0xcf,0x8b,0xec,0x0f,0x53,0xc2,0x00,0x00,0x01,0xc9,
+  0x8b,0x9a,0x27,0x57,0xc2,0x00,0x00,0x00,0xc9,0x0b,0x9f,0x17,0x4b,0x4b,0x00,0x00,
+  0x0b,0x70,0x10,0x57,0xcf,0x8b,0x9f,0x17,0x4b,0x4b,0x00,0x00,0x07,0x70,0x00,0x57,
+  0x70,0x10,0x47,0x70,0x10,0x48,0xcf,0x8b,0xec,0x0f,0x53,0xd8,0x00,0xff,0xff,0x27,
+  0xd8,0xd8,0x00,0x04,0x20,0x17,0x98,0x58,0x03,0x00,0x00,0xc7,0x8b,0x0b,0xcf,0x8b,
+  0xec,0x0f,0x59,0x59,0x00,0xff,0xff,0x17,0x59,0x58,0x04,0x00,0x00,0xc7,0x8b,0x0b,
+  0xcf,0x8b,0xec,0x70,0x10,0x59,0xcf,0x8b,0xec,0x0f,0x53,0xc2,0x00,0x00,0x01,0x93,
+  0x00,0x64,0xcf,0x8b,0xec,0x0f,0x53,0x4e,0x00,0xff,0xff,0xcf,0x8b,0xec,0x0f,0xd7,
+  0x56,0x00,0x00,0x01,0x17,0x56,0x58,0x08,0x00,0x00,0xc7,0x8b,0x0b,0xcf,0x8b,0xec,
+  0x70,0x69,0xc2,0x33,0x6a,0x56,0xca,0x0b,0xc2,0x27,0x56,0x56,0x00,0x00,0x10,0x17,
+  0x56,0x58,0x09,0x00,0x00,0xc7,0x8b,0x0b,0x27,0x56,0xc2,0xff,0xff,0xff,0x70,0xf0,
+  0x56,0x70,0x6a,0xf0,0xd6,0x40,0x00,0xc7,0x8c,0x23,0x70,0xf0,0x6a,0x70,0x56,0xf0,
+  0xcf,0x8b,0xec,0xd7,0x01,0x00,0xcf,0x8b,0xec,0x0f,0x53,0x6d,0x00,0xff,0xff,0xcf,
+  0x8b,0xec,0x0f,0x53,0xc2,0x00,0x00,0x01,0x4b,0x14,0x6c,0xcf,0x8b,0xec,0x70,0x5a,
+  0xc2,0x33,0x5b,0xc2,0xca,0x0b,0xdb,0x23,0x6e,0xc2,0x0b,0x0f,0x56,0x17,0x56,0x58,
+  0x0d,0x00,0x00,0xc7,0x8b,0x0b,0xcf,0x8b,0xec,0x70,0x10,0x55,0xd7,0x00,0x20,0x40,
+  0x10,0xc2,0x4f,0x53,0x53,0x00,0x01,0x00,0x70,0x53,0xc2,0x57,0x54,0x53,0x00,0x01,
+  0x00,0x5b,0x10,0x54,0x27,0x55,0x55,0xff,0xff,0xff,0xcf,0x8b,0xed,0x70,0x10,0x55,
+  0x70,0x45,0xd8,0x47,0x44,0xc2,0x00,0x00,0x01,0x70,0x43,0xc2,0xdf,0xc0,0x0b,0x93,
+  0x00,0x43,0x5b,0x10,0x44,0x70,0xd8,0x45,0x27,0x52,0xc2,0x00,0x00,0x00,0xc9,0x8b,
+  0xfb,0x70,0x51,0xc2,0x33,0x50,0xc2,0xc9,0x0c,0x01,0x27,0x52,0xc2,0x00,0x0b,0xfe,
+  0xcf,0xc0,0x00,0xcf,0x8c,0x06,0xcf,0x8c,0x10,0xcf,0x8c,0x1a,0x70,0x45,0xd8,0x47,
+  0x44,0xc2,0x00,0x00,0x01,0x70,0x43,0xc2,0xdf,0xc0,0x0b,0x70,0x50,0xd8,0x47,0x98,
+  0xc9,0x00,0x01,0x00,0x27,0x52,0x52,0x00,0x00,0x01,0x70,0x45,0xd8,0x47,0x44,0xc2,
+  0x00,0x00,0x01,0x70,0x43,0xc2,0xdf,0xc0,0x0b,0x70,0x50,0xd8,0x47,0x98,0xc9,0x01,
+  0x00,0x00,0x27,0x52,0x52,0x00,0x00,0x01,0x70,0x45,0xd8,0x47,0x44,0xc2,0x00,0x00,
+  0x01,0x70,0x43,0xc2,0xdf,0xc0,0x0b,0x70,0x50,0xd8,0x70,0x9a,0xc9,0x70,0xd8,0x50,
+  0x70,0x10,0x52,0x70,0x45,0xd8,0x47,0x44,0xc2,0x00,0x00,0x01,0x70,0x43,0xc2,0xdf,
+  0xc0,0x0b,0x70,0xb2,0x58,0xc7,0x8b,0x0b,0xdf,0x80,0x0a,0x95,0x00,0x45,0x93,0x00,
+  0x43,0x5b,0x10,0x44,0x70,0x5a,0xc2,0xd5,0x40,0x00,0x27,0x72,0xc2,0x00,0x0c,0x2e,
+  0xcf,0xc0,0x00,0xcf,0x8c,0x31,0xcf,0x8c,0x38,0xcf,0x8c,0x41,0x0f,0xc5,0x70,0x00,
+  0xff,0xff,0x4f,0x70,0x70,0x00,0x01,0x00,0x27,0x72,0x72,0x00,0x00,0x01,0xcf,0x8c,
+  0x4c,0x0f,0xc5,0x71,0x00,0xff,0xff,0x47,0x71,0xc2,0x01,0x00,0x00,0x13,0x70,0xc2,
+  0x5b,0x10,0x70,0x27,0x72,0x72,0x00,0x00,0x01,0xcf,0x8c,0x45,0x0f,0xc5,0xc2,0x00,
+  0xff,0xff,0x13,0x70,0xc2,0x70,0x10,0x72,0xd4,0x40,0x00,0x26,0x00,0x5a,0x37,0x5a,
+  0xc2,0x00,0x0f,0xff,0xca,0x0c,0x4c,0x97,0x80,0x5a,0x00,0x0f,0x00,0x70,0x45,0xc2,
+  0xd5,0x40,0x00,0x47,0x44,0xc2,0x00,0x00,0x01,0x70,0x43,0xc2,0xdf,0xc0,0x0b,0x70,
+  0xd8,0x45,0x0f,0xd7,0xc2,0x00,0x00,0x01,0xc9,0x0c,0x58,0x0f,0xcd,0xcd,0xff,0xdf,
+  0xff,0x70,0x45,0xd8,0xdf,0xc0,0x0b,0x70,0xb0,0xc2,0x23,0x98,0xb2,0x33,0x98,0x9b,
+  0x70,0xb0,0xc2,0x23,0x98,0xb2,0x33,0x98,0x9b,0xdf,0x80,0x0a,0x70,0xb0,0x38,0x70,
+  0x98,0x39,0x40,0x38,0xc2,0x64,0x39,0xc2,0x57,0x11,0xc2,0x02,0x00,0x00,0x86,0x00,
+  0x9a,0x44,0x38,0xc2,0x50,0x39,0xc2,0x57,0x11,0xc2,0x02,0x00,0x00,0x86,0x00,0xb3,
+  0xdf,0x80,0x0a,0x70,0xb0,0x38,0x70,0x98,0x39,0x40,0x38,0xc2,0x64,0x39,0xc2,0x57,
+  0x11,0xc2,0x02,0x00,0x00,0x86,0x00,0xb3,0x44,0x38,0xc2,0x50,0x39,0xc2,0x57,0x11,
+  0xc2,0x02,0x00,0x00,0x86,0x00,0x9a,0xdf,0x80,0x0a,0x70,0xb2,0xc2,0x23,0xb3,0xb2,
+  0x33,0xb0,0xb0,0x70,0x9b,0xc2,0x23,0x9a,0x9b,0x33,0x98,0x98,0xdf,0x80,0x0a,0x41,
+  0xb2,0xc2,0x55,0x9b,0xc2,0x57,0x11,0xc2,0x02,0x00,0x00,0x86,0x00,0x96,0xdf,0x80,
+  0x0a,0x41,0xb2,0xc2,0x65,0x9b,0xc2,0x57,0x11,0xc2,0x02,0x00,0x00,0x86,0x00,0x96,
+  0xdf,0x80,0x0a,0xff,0xff,0x30,0xdf,0xde
+};
--- /dev/null
+++ b/os/pc/dat.h
@@ -1,0 +1,258 @@
+typedef struct Conf	Conf;
+typedef struct FPU	FPU;
+typedef struct FPenv	FPenv;
+typedef ulong Instr;
+typedef struct ISAConf	ISAConf;
+typedef struct Label	Label;
+typedef struct Lock	Lock;
+typedef struct MMU	MMU;
+typedef struct Mach	Mach;
+typedef struct Notsave	Notsave;
+typedef struct PCArch	PCArch;
+typedef struct Pcidev	Pcidev;
+typedef struct PCMmap	PCMmap;
+typedef struct PCMslot	PCMslot;
+typedef struct Page	Page;
+typedef struct PMMU	PMMU;
+typedef struct Segdesc	Segdesc;
+typedef struct Ureg	Ureg;
+typedef struct Vctl	Vctl;
+
+#pragma incomplete Ureg
+#pragma incomplete Vctl
+
+
+struct Lock
+{
+	ulong	key;
+	ulong	sr;
+	ulong	pc;
+	ulong	pri;
+};
+
+struct Label
+{
+	ulong	sp;
+	ulong	pc;
+};
+
+/*
+ * FPenv.status
+ */
+enum
+{
+	FPINIT,
+	FPACTIVE,
+	FPINACTIVE,
+};
+
+/*
+ * This structure must agree with FPsave and FPrestore asm routines
+ */
+struct FPenv
+{
+	ushort	control;
+	ushort	r1;
+	ushort	status;
+	ushort	r2;
+	ushort	tag;
+	ushort	r3;
+	ulong	pc;
+	ushort	selector;
+	ushort	r4;
+	ulong	operand;
+	ushort	oselector;
+	ushort	r5;
+};
+
+/*
+ * This structure must agree with fpsave and fprestore asm routines
+ */
+struct	FPU
+{
+	FPenv	env;
+	uchar	regs[80];	/* floating point registers */
+};
+
+struct Conf
+{
+	ulong	nmach;		/* processors */
+	ulong	nproc;		/* processes */
+	ulong	monitor;	/* has monitor? */
+	ulong	npage0;		/* total physical pages of memory */
+	ulong	npage1;		/* total physical pages of memory */
+	ulong	npage;		/* total physical pages of memory */
+	ulong	base0;		/* base of bank 0 */
+	ulong	base1;		/* base of bank 1 */
+	ulong	copymode;	/* 0 is copy on write, 1 is copy on reference */
+	ulong	ialloc;		/* max interrupt time allocation in bytes */
+	ulong	pipeqsize;	/* size in bytes of pipe queues */
+	int	nuart;		/* number of uart devices */
+};
+
+#include "../port/portdat.h"
+
+typedef struct {
+	ulong	link;			/* link (old TSS selector) */
+	ulong	esp0;			/* privilege level 0 stack pointer */
+	ulong	ss0;			/* privilege level 0 stack selector */
+	ulong	esp1;			/* privilege level 1 stack pointer */
+	ulong	ss1;			/* privilege level 1 stack selector */
+	ulong	esp2;			/* privilege level 2 stack pointer */
+	ulong	ss2;			/* privilege level 2 stack selector */
+	ulong	cr3;			/* page directory base register */
+	ulong	eip;			/* instruction pointer */
+	ulong	eflags;			/* flags register */
+	ulong	eax;			/* general registers */
+	ulong 	ecx;
+	ulong	edx;
+	ulong	ebx;
+	ulong	esp;
+	ulong	ebp;
+	ulong	esi;
+	ulong	edi;
+	ulong	es;			/* segment selectors */
+	ulong	cs;
+	ulong	ss;
+	ulong	ds;
+	ulong	fs;
+	ulong	gs;
+	ulong	ldt;			/* selector for task's LDT */
+	ulong	iomap;			/* I/O map base address + T-bit */
+} Tss;
+
+struct Segdesc
+{
+	ulong	d0;
+	ulong	d1;
+};
+
+struct Mach
+{
+	int	machno;			/* physical id of processor (KNOWN TO ASSEMBLY) */
+	ulong	splpc;		/* pc of last caller to splhi */
+
+	ulong*	pdb;		/* page directory base for this processor (va) */
+	Tss*	tss;		/* tss for this processor */
+	Segdesc	*gdt;		/* gdt for this processor */
+
+	Proc*	externup;		/* extern register Proc *up */
+
+	ulong	ticks;		/* of the clock since boot time */
+	Proc*	proc;		/* current process on this processor */
+	Label	sched;		/* scheduler wakeup */
+	Lock	alarmlock;	/* access to alarm list */
+	void*	alarm;		/* alarms bound to this clock */
+	int	inclockintr;
+
+	int	nrdy;
+	int	ilockdepth;
+
+	int	loopconst;
+
+	Lock	apictimerlock;
+	int	cpumhz;
+	uvlong	cyclefreq;		/* Frequency of user readable cycle counter */
+	uvlong	cpuhz;
+	int	cpuidax;
+	int	cpuiddx;
+	char	cpuidid[16];
+	char*	cpuidtype;
+	int	havetsc;
+	int	havepge;
+	uvlong	tscticks;
+	uvlong	tscoff;
+	int	intr;
+	ulong	spuriousintr;
+	int	lastintr;
+
+	vlong	mtrrcap;
+	vlong	mtrrdef;
+	vlong	mtrrfix[11];
+	vlong	mtrrvar[32];		/* 256 max. */
+
+	int	stack[1];
+};
+
+struct
+{
+	Lock;
+	int	machs;			/* bitmap of active CPUs */
+	int	exiting;		/* shutdown */
+	int	ispanic;		/* shutdown in response to a panic */
+	int	thunderbirdsarego;	/* lets the added processors continue to schedinit */
+}active;
+
+
+/*
+ *  routines for things outside the PC model, like power management
+ */
+struct PCArch
+{
+	char*	id;
+	int	(*ident)(void);		/* this should be in the model */
+	void	(*reset)(void);		/* this should be in the model */
+	int	(*serialpower)(int);	/* 1 == on, 0 == off */
+	int	(*modempower)(int);	/* 1 == on, 0 == off */
+
+	void	(*intrinit)(void);
+	int	(*intrenable)(Vctl*);
+	int	(*intrvecno)(int);
+	int	(*intrdisable)(int);
+
+	void	(*clockenable)(void);
+	uvlong	(*fastclock)(uvlong*);
+	void	(*timerset)(uvlong);
+};
+
+/*
+ *  a parsed plan9.ini line
+ */
+#define NISAOPT		8
+
+struct ISAConf {
+	char	*type;
+	ulong	port;
+	int	irq;
+	ulong	dma;
+	ulong	mem;
+	ulong	size;
+	ulong	freq;
+
+	int	nopt;
+	char	*opt[NISAOPT];
+};
+
+extern PCArch	*arch;			/* PC architecture */
+
+/*
+ * Each processor sees its own Mach structure at address MACHADDR.
+ * However, the Mach structures must also be available via the per-processor
+ * MMU information array machp, mainly for disambiguation and access to
+ * the clock which is only maintained by the bootstrap processor (0).
+ */
+Mach* machp[MAXMACH];
+
+#define	MACHP(n)	(machp[n])
+
+extern Mach	*m;
+//extern Proc	*up;
+#define up	(((Mach*)MACHADDR)->externup)
+
+extern int swcursor;
+
+/*
+ *  hardware info about a device
+ */
+typedef struct {
+	ulong	port;	
+	int	size;
+} Devport;
+
+struct DevConf
+{
+	ulong	intnum;			/* interrupt number */
+	char	*type;			/* card type, malloced */
+	int	nports;			/* Number of ports */
+	Devport	*ports;			/* The ports themselves */
+};
--- /dev/null
+++ b/os/pc/devarch.c
@@ -1,0 +1,940 @@
+#include "u.h"
+#include "../port/lib.h"
+#include "mem.h"
+#include "dat.h"
+#include "fns.h"
+#include "io.h"
+#include "ureg.h"
+#include "../port/error.h"
+
+typedef struct IOMap IOMap;
+struct IOMap
+{
+	IOMap	*next;
+	int	reserved;
+	char	tag[13];
+	ulong	start;
+	ulong	end;
+};
+
+static struct
+{
+	Lock;
+	IOMap	*m;
+	IOMap	*free;
+	IOMap	maps[32];		// some initial free maps
+
+	QLock	ql;			// lock for reading map
+} iomap;
+
+enum {
+	Qdir = 0,
+	Qioalloc = 1,
+	Qiob,
+	Qiow,
+	Qiol,
+	Qbase,
+
+	Qmax = 16,
+};
+
+typedef long Rdwrfn(Chan*, void*, long, vlong);
+
+static Rdwrfn *readfn[Qmax];
+static Rdwrfn *writefn[Qmax];
+
+static Dirtab archdir[Qmax] = {
+	".",		{ Qdir, 0, QTDIR },	0,	0555,
+	"ioalloc",	{ Qioalloc, 0 },	0,	0444,
+	"iob",		{ Qiob, 0 },		0,	0660,
+	"iow",		{ Qiow, 0 },		0,	0660,
+	"iol",		{ Qiol, 0 },		0,	0660,
+};
+Lock archwlock;	/* the lock is only for changing archdir */
+int narchdir = Qbase;
+int (*_pcmspecial)(char*, ISAConf*);
+void (*_pcmspecialclose)(int);
+
+static int doi8253set = 1;
+
+/*
+ * Add a file to the #P listing.  Once added, you can't delete it.
+ * You can't add a file with the same name as one already there,
+ * and you get a pointer to the Dirtab entry so you can do things
+ * like change the Qid version.  Changing the Qid path is disallowed.
+ */
+Dirtab*
+addarchfile(char *name, int perm, Rdwrfn *rdfn, Rdwrfn *wrfn)
+{
+	int i;
+	Dirtab d;
+	Dirtab *dp;
+
+	memset(&d, 0, sizeof d);
+	strcpy(d.name, name);
+	d.perm = perm;
+
+	lock(&archwlock);
+	if(narchdir >= Qmax){
+		unlock(&archwlock);
+		return nil;
+	}
+
+	for(i=0; i<narchdir; i++)
+		if(strcmp(archdir[i].name, name) == 0){
+			unlock(&archwlock);
+			return nil;
+		}
+
+	d.qid.path = narchdir;
+	archdir[narchdir] = d;
+	readfn[narchdir] = rdfn;
+	writefn[narchdir] = wrfn;
+	dp = &archdir[narchdir++];
+	unlock(&archwlock);
+
+	return dp;
+}
+
+void
+ioinit(void)
+{
+	char *excluded;
+	int i;
+
+	for(i = 0; i < nelem(iomap.maps)-1; i++)
+		iomap.maps[i].next = &iomap.maps[i+1];
+	iomap.maps[i].next = nil;
+	iomap.free = iomap.maps;
+
+	/*
+	 * This is necessary to make the IBM X20 boot.
+	 * Have not tracked down the reason.
+	 */
+	ioalloc(0x0fff, 1, 0, "dummy");	// i82557 is at 0x1000, the dummy
+					// entry is needed for swappable devs.
+
+	if ((excluded = getconf("ioexclude")) != nil) {
+		char *s;
+
+		s = excluded;
+		while (s && *s != '\0' && *s != '\n') {
+			char *ends;
+			int io_s, io_e;
+
+			io_s = (int)strtol(s, &ends, 0);
+			if (ends == nil || ends == s || *ends != '-') {
+				print("ioinit: cannot parse option string\n");
+				break;
+			}
+			s = ++ends;
+
+			io_e = (int)strtol(s, &ends, 0);
+			if (ends && *ends == ',')
+				*ends++ = '\0';
+			s = ends;
+
+			ioalloc(io_s, io_e - io_s + 1, 0, "pre-allocated");
+		}
+	}
+
+}
+
+// Reserve a range to be ioalloced later. 
+// This is in particular useful for exchangable cards, such
+// as pcmcia and cardbus cards.
+int
+ioreserve(int, int size, int align, char *tag)
+{
+	IOMap *m, **l;
+	int i, port;
+
+	lock(&iomap);
+	// find a free port above 0x400 and below 0x1000
+	port = 0x400;
+	for(l = &iomap.m; *l; l = &(*l)->next){
+		m = *l;
+		if (m->start < 0x400) continue;
+		i = m->start - port;
+		if(i > size)
+			break;
+		if(align > 0)
+			port = ((port+align-1)/align)*align;
+		else
+			port = m->end;
+	}
+	if(*l == nil){
+		unlock(&iomap);
+		return -1;
+	}
+	m = iomap.free;
+	if(m == nil){
+		print("ioalloc: out of maps");
+		unlock(&iomap);
+		return port;
+	}
+	iomap.free = m->next;
+	m->next = *l;
+	m->start = port;
+	m->end = port + size;
+	m->reserved = 1;
+	strncpy(m->tag, tag, sizeof(m->tag));
+	m->tag[sizeof(m->tag)-1] = 0;
+	*l = m;
+
+	archdir[0].qid.vers++;
+
+	unlock(&iomap);
+	return m->start;
+}
+
+//
+//	alloc some io port space and remember who it was
+//	alloced to.  if port < 0, find a free region.
+//
+int
+ioalloc(int port, int size, int align, char *tag)
+{
+	IOMap *m, **l;
+	int i;
+
+	lock(&iomap);
+	if(port < 0){
+		// find a free port above 0x400 and below 0x1000
+		port = 0x400;
+		for(l = &iomap.m; *l; l = &(*l)->next){
+			m = *l;
+			if (m->start < 0x400) continue;
+			i = m->start - port;
+			if(i > size)
+				break;
+			if(align > 0)
+				port = ((port+align-1)/align)*align;
+			else
+				port = m->end;
+		}
+		if(*l == nil){
+			unlock(&iomap);
+			return -1;
+		}
+	} else {
+		// Only 64KB I/O space on the x86.
+		if((port+size) > 0x10000){
+			unlock(&iomap);
+			return -1;
+		}
+		// see if the space clashes with previously allocated ports
+		for(l = &iomap.m; *l; l = &(*l)->next){
+			m = *l;
+			if(m->end <= port)
+				continue;
+			if(m->reserved && m->start == port && m->end == port + size) {
+				m->reserved = 0;
+				unlock(&iomap);
+				return m->start;
+			}
+			if(m->start >= port+size)
+				break;
+			unlock(&iomap);
+			return -1;
+		}
+	}
+	m = iomap.free;
+	if(m == nil){
+		print("ioalloc: out of maps");
+		unlock(&iomap);
+		return port;
+	}
+	iomap.free = m->next;
+	m->next = *l;
+	m->start = port;
+	m->end = port + size;
+	strncpy(m->tag, tag, sizeof(m->tag));
+	m->tag[sizeof(m->tag)-1] = 0;
+	*l = m;
+
+	archdir[0].qid.vers++;
+
+	unlock(&iomap);
+	return m->start;
+}
+
+void
+iofree(int port)
+{
+	IOMap *m, **l;
+
+	lock(&iomap);
+	for(l = &iomap.m; *l; l = &(*l)->next){
+		if((*l)->start == port){
+			m = *l;
+			*l = m->next;
+			m->next = iomap.free;
+			iomap.free = m;
+			break;
+		}
+		if((*l)->start > port)
+			break;
+	}
+	archdir[0].qid.vers++;
+	unlock(&iomap);
+}
+
+int
+iounused(int start, int end)
+{
+	IOMap *m;
+
+	for(m = iomap.m; m; m = m->next){
+		if(start >= m->start && start < m->end
+		|| start <= m->start && end > m->start)
+			return 0; 
+	}
+	return 1;
+}
+
+static void
+checkport(int start, int end)
+{
+	/* standard vga regs are OK */
+	if(start >= 0x2b0 && end <= 0x2df+1)
+		return;
+	if(start >= 0x3c0 && end <= 0x3da+1)
+		return;
+
+	if(iounused(start, end))
+		return;
+	error(Eperm);
+}
+
+static Chan*
+archattach(char* spec)
+{
+	return devattach('P', spec);
+}
+
+Walkqid*
+archwalk(Chan* c, Chan *nc, char** name, int nname)
+{
+	return devwalk(c, nc, name, nname, archdir, narchdir, devgen);
+}
+
+static int
+archstat(Chan* c, uchar* dp, int n)
+{
+	return devstat(c, dp, n, archdir, narchdir, devgen);
+}
+
+static Chan*
+archopen(Chan* c, int omode)
+{
+	return devopen(c, omode, archdir, narchdir, devgen);
+}
+
+static void
+archclose(Chan*)
+{
+}
+
+enum
+{
+	Linelen= 31,
+};
+
+static long
+archread(Chan *c, void *a, long n, vlong offset)
+{
+	char *buf, *p;
+	int port;
+	ushort *sp;
+	ulong *lp;
+	IOMap *m;
+	Rdwrfn *fn;
+
+	switch((ulong)c->qid.path){
+
+	case Qdir:
+		return devdirread(c, a, n, archdir, narchdir, devgen);
+
+	case Qiob:
+		port = offset;
+		checkport(offset, offset+n);
+		for(p = a; port < offset+n; port++)
+			*p++ = inb(port);
+		return n;
+
+	case Qiow:
+		if(n & 1)
+			error(Ebadarg);
+		checkport(offset, offset+n);
+		sp = a;
+		for(port = offset; port < offset+n; port += 2)
+			*sp++ = ins(port);
+		return n;
+
+	case Qiol:
+		if(n & 3)
+			error(Ebadarg);
+		checkport(offset, offset+n);
+		lp = a;
+		for(port = offset; port < offset+n; port += 4)
+			*lp++ = inl(port);
+		return n;
+
+	case Qioalloc:
+		break;
+
+	default:
+		if(c->qid.path < narchdir && (fn = readfn[c->qid.path]))
+			return fn(c, a, n, offset);
+		error(Eperm);
+		break;
+	}
+
+	if((buf = malloc(n)) == nil)
+		error(Enomem);
+	p = buf;
+	n = n/Linelen;
+	offset = offset/Linelen;
+
+	lock(&iomap);
+	for(m = iomap.m; n > 0 && m != nil; m = m->next){
+		if(offset-- > 0)
+			continue;
+		sprint(p, "%8lux %8lux %-12.12s\n", m->start, m->end-1, m->tag);
+		p += Linelen;
+		n--;
+	}
+	unlock(&iomap);
+
+	n = p - buf;
+	memmove(a, buf, n);
+	free(buf);
+
+	return n;
+}
+
+static long
+archwrite(Chan *c, void *a, long n, vlong offset)
+{
+	char *p;
+	int port;
+	ushort *sp;
+	ulong *lp;
+	Rdwrfn *fn;
+
+	switch((ulong)c->qid.path){
+
+	case Qiob:
+		p = a;
+		checkport(offset, offset+n);
+		for(port = offset; port < offset+n; port++)
+			outb(port, *p++);
+		return n;
+
+	case Qiow:
+		if(n & 1)
+			error(Ebadarg);
+		checkport(offset, offset+n);
+		sp = a;
+		for(port = offset; port < offset+n; port += 2)
+			outs(port, *sp++);
+		return n;
+
+	case Qiol:
+		if(n & 3)
+			error(Ebadarg);
+		checkport(offset, offset+n);
+		lp = a;
+		for(port = offset; port < offset+n; port += 4)
+			outl(port, *lp++);
+		return n;
+
+	default:
+		if(c->qid.path < narchdir && (fn = writefn[c->qid.path]))
+			return fn(c, a, n, offset);
+		error(Eperm);
+		break;
+	}
+	return 0;
+}
+
+Dev archdevtab = {
+	'P',
+	"arch",
+
+	devreset,
+	devinit,
+	devshutdown,
+	archattach,
+	archwalk,
+	archstat,
+	archopen,
+	devcreate,
+	archclose,
+	archread,
+	devbread,
+	archwrite,
+	devbwrite,
+	devremove,
+	devwstat,
+};
+
+/*
+ *  the following is a generic version of the
+ *  architecture specific stuff
+ */
+
+static int
+unimplemented(int)
+{
+	return 0;
+}
+
+static void
+nop(void)
+{
+}
+
+/*
+ * On a uniprocessor, you'd think that coherence could be nop,
+ * but it can't.  We still need a barrier when using coherence() in
+ * device drivers.
+ *
+ * On VMware, it's safe (and a huge win) to set this to nop.
+ * Aux/vmware does this via the #P/archctl file.
+ */
+void (*coherence)(void) = nop;
+
+PCArch* arch;
+extern PCArch* knownarch[];
+
+PCArch archgeneric = {
+.id=		"generic",
+.ident=		0,
+.reset=		i8042reset,
+.serialpower=	unimplemented,
+.modempower=	unimplemented,
+
+.intrinit=	i8259init,
+.intrenable=	i8259enable,
+.intrvecno=	i8259vecno,
+.intrdisable=	i8259disable,
+
+.clockenable=	i8253enable,
+.fastclock=	i8253read,
+.timerset=	i8253timerset,
+};
+
+typedef struct X86type X86type;
+struct X86type {
+	int	family;
+	int	model;
+	int	aalcycles;
+	char*	name;
+};
+
+static X86type x86intel[] =
+{
+	{ 4,	0,	22,	"486DX", },	/* known chips */
+	{ 4,	1,	22,	"486DX50", },
+	{ 4,	2,	22,	"486SX", },
+	{ 4,	3,	22,	"486DX2", },
+	{ 4,	4,	22,	"486SL", },
+	{ 4,	5,	22,	"486SX2", },
+	{ 4,	7,	22,	"DX2WB", },	/* P24D */
+	{ 4,	8,	22,	"DX4", },	/* P24C */
+	{ 4,	9,	22,	"DX4WB", },	/* P24CT */
+	{ 5,	0,	23,	"P5", },
+	{ 5,	1,	23,	"P5", },
+	{ 5,	2,	23,	"P54C", },
+	{ 5,	3,	23,	"P24T", },
+	{ 5,	4,	23,	"P55C MMX", },
+	{ 5,	7,	23,	"P54C VRT", },
+	{ 6,	1,	16,	"PentiumPro", },/* trial and error */
+	{ 6,	3,	16,	"PentiumII", },
+	{ 6,	5,	16,	"PentiumII/Xeon", },
+	{ 6,	6,	16,	"Celeron", },
+	{ 6,	7,	16,	"PentiumIII/Xeon", },
+	{ 6,	8,	16,	"PentiumIII/Xeon", },
+	{ 6,	0xB,	16,	"PentiumIII/Xeon", },
+	{ 0xF,	1,	16,	"P4", },	/* P4 */
+	{ 0xF,	2,	16,	"PentiumIV/Xeon", },
+
+	{ 3,	-1,	32,	"386", },	/* family defaults */
+	{ 4,	-1,	22,	"486", },
+	{ 5,	-1,	23,	"P5", },
+	{ 6,	-1,	16,	"P6", },
+	{ 0xF,	-1,	16,	"P4", },	/* P4 */
+
+	{ -1,	-1,	16,	"unknown", },	/* total default */
+};
+
+/*
+ * The AMD processors all implement the CPUID instruction.
+ * The later ones also return the processor name via functions
+ * 0x80000002, 0x80000003 and 0x80000004 in registers AX, BX, CX
+ * and DX:
+ *	K5	"AMD-K5(tm) Processor"
+ *	K6	"AMD-K6tm w/ multimedia extensions"
+ *	K6 3D	"AMD-K6(tm) 3D processor"
+ *	K6 3D+	?
+ */
+static X86type x86amd[] =
+{
+	{ 5,	0,	23,	"AMD-K5", },	/* guesswork */
+	{ 5,	1,	23,	"AMD-K5", },	/* guesswork */
+	{ 5,	2,	23,	"AMD-K5", },	/* guesswork */
+	{ 5,	3,	23,	"AMD-K5", },	/* guesswork */
+	{ 5,	6,	11,	"AMD-K6", },	/* trial and error */
+	{ 5,	7,	11,	"AMD-K6", },	/* trial and error */
+	{ 5,	8,	11,	"AMD-K6-2", },	/* trial and error */
+	{ 5,	9,	11,	"AMD-K6-III", },/* trial and error */
+
+	{ 6,	1,	11,	"AMD-Athlon", },/* trial and error */
+	{ 6,	2,	11,	"AMD-Athlon", },/* trial and error */
+
+	{ 4,	-1,	22,	"Am486", },	/* guesswork */
+	{ 5,	-1,	23,	"AMD-K5/K6", },	/* guesswork */
+	{ 6,	-1,	11,	"AMD-Athlon", },/* guesswork */
+	{ 0xF,	-1,	11,	"AMD64", },	/* guesswork */
+
+	{ -1,	-1,	11,	"unknown", },	/* total default */
+};
+
+/*
+ * WinChip 240MHz
+ */
+static X86type x86winchip[] =
+{
+	{5,	4,	23,	"Winchip",},	/* guesswork */
+	{6,	7,	23,	"Via C3 Samuel 2 or Ezra",},
+	{6,	8,	23,	"Via C3 Ezra-T",},
+	{ -1,	-1,	23,	"unknown", },	/* total default */
+};
+
+/*
+ * SiS 55x
+ */
+static X86type x86sis[] =
+{
+	{5,	0,	23,	"SiS 55x",},	/* guesswork */
+	{ -1,	-1,	23,	"unknown", },	/* total default */
+};
+
+static X86type *cputype;
+
+static void	simplecycles(uvlong*);
+void	(*cycles)(uvlong*) = simplecycles;
+void	_cycles(uvlong*);	/* in l.s */
+
+static void
+simplecycles(uvlong*x)
+{
+	*x = m->ticks;
+}
+
+void
+cpuidprint(void)
+{
+	int i;
+	char buf[128];
+
+	i = sprint(buf, "cpu%d: %dMHz ", m->machno, m->cpumhz);
+	if(m->cpuidid[0])
+		i += sprint(buf+i, "%12.12s ", m->cpuidid);
+	sprint(buf+i, "%s (cpuid: AX 0x%4.4uX DX 0x%4.4uX)\n",
+		m->cpuidtype, m->cpuidax, m->cpuiddx);
+	print(buf);
+}
+
+/*
+ *  figure out:
+ *	- cpu type
+ *	- whether or not we have a TSC (cycle counter)
+ *	- whether or not it supports page size extensions
+ *		(if so turn it on)
+ *	- whether or not it supports machine check exceptions
+ *		(if so turn it on)
+ *	- whether or not it supports the page global flag
+ *		(if so turn it on)
+ */
+int
+cpuidentify(void)
+{
+	char *p;
+	int family, model, nomce;
+	X86type *t, *tab;
+	ulong cr4;
+	vlong mca, mct;
+
+	cpuid(m->cpuidid, &m->cpuidax, &m->cpuiddx);
+	if(strncmp(m->cpuidid, "AuthenticAMD", 12) == 0)
+		tab = x86amd;
+	else if(strncmp(m->cpuidid, "CentaurHauls", 12) == 0)
+		tab = x86winchip;
+	else if(strncmp(m->cpuidid, "SiS SiS SiS ", 12) == 0)
+		tab = x86sis;
+	else
+		tab = x86intel;
+	
+	family = X86FAMILY(m->cpuidax);
+	model = X86MODEL(m->cpuidax);
+	for(t=tab; t->name; t++)
+		if((t->family == family && t->model == model)
+		|| (t->family == family && t->model == -1)
+		|| (t->family == -1))
+			break;
+
+	m->cpuidtype = t->name;
+
+	/*
+	 *  if there is one, set tsc to a known value
+	 */
+	if(m->cpuiddx & 0x10){
+		m->havetsc = 1;
+		cycles = _cycles;
+		if(m->cpuiddx & 0x20)
+			wrmsr(0x10, 0);
+	}
+
+	/*
+ 	 *  use i8253 to guess our cpu speed
+	 */
+	guesscpuhz(t->aalcycles);
+
+	/*
+	 * If machine check exception, page size extensions or page global bit
+	 * are supported enable them in CR4 and clear any other set extensions.
+	 * If machine check was enabled clear out any lingering status.
+	 */
+	if(m->cpuiddx & 0x2088){
+		cr4 = 0;
+		if(m->cpuiddx & 0x08)
+			cr4 |= 0x10;		/* page size extensions */
+		if(p = getconf("*nomce"))
+			nomce = strtoul(p, 0, 0);
+		else
+			nomce = 0;
+		if((m->cpuiddx & 0x80) && !nomce){
+			cr4 |= 0x40;		/* machine check enable */
+			if(family == 5){
+				rdmsr(0x00, &mca);
+				rdmsr(0x01, &mct);
+			}
+		}
+	
+		/*
+		 * Detect whether the chip supports the global bit
+		 * in page directory and page table entries.  When set
+		 * in a particular entry, it means ``don't bother removing
+		 * this from the TLB when CR3 changes.''  
+		 * 
+		 * We flag all kernel pages with this bit.  Doing so lessens the
+		 * overhead of switching processes on bare hardware,
+		 * even more so on VMware.  See mmu.c:/^memglobal.
+		 *
+		 * For future reference, should we ever need to do a
+		 * full TLB flush, it can be accomplished by clearing
+		 * the PGE bit in CR4, writing to CR3, and then
+		 * restoring the PGE bit.
+		 */
+		if(m->cpuiddx & 0x2000){
+			cr4 |= 0x80;		/* page global enable bit */
+			m->havepge = 1;
+		}
+
+		putcr4(cr4);
+		if(m->cpuiddx & 0x80)
+			rdmsr(0x01, &mct);
+	}
+
+	cputype = t;
+	return t->family;
+}
+
+static long
+cputyperead(Chan*, void *a, long n, vlong offset)
+{
+	char str[32];
+	ulong mhz;
+
+	mhz = (m->cpuhz+999999)/1000000;
+
+	snprint(str, sizeof(str), "%s %lud\n", cputype->name, mhz);
+	return readstr(offset, a, n, str);
+}
+
+static long
+archctlread(Chan*, void *a, long nn, vlong offset)
+{
+	char buf[256];
+	int n;
+	
+	n = snprint(buf, sizeof buf, "cpu %s %lud%s\n",
+		cputype->name, (ulong)(m->cpuhz+999999)/1000000,
+		m->havepge ? " pge" : "");
+	n += snprint(buf+n, sizeof buf-n, "pge %s\n", getcr4()&0x80 ? "on" : "off");
+	n += snprint(buf+n, sizeof buf-n, "coherence ");
+	if(coherence == mb386)
+		n += snprint(buf+n, sizeof buf-n, "mb386\n");
+	else if(coherence == mb586)
+		n += snprint(buf+n, sizeof buf-n, "mb586\n");
+	else if(coherence == nop)
+		n += snprint(buf+n, sizeof buf-n, "nop\n");
+	else
+		n += snprint(buf+n, sizeof buf-n, "0x%p\n", coherence);
+	n += snprint(buf+n, sizeof buf-n, "i8253set %s\n", doi8253set ? "on" : "off");
+	buf[n] = 0;
+	return readstr(offset, a, nn, buf);
+}
+
+enum
+{
+	CMpge,
+	CMcoherence,
+	CMi8253set,
+};
+
+static Cmdtab archctlmsg[] =
+{
+	CMpge,		"pge",		2,
+	CMcoherence,	"coherence",	2,
+	CMi8253set,	"i8253set",	2,
+};
+
+static long
+archctlwrite(Chan*, void *a, long n, vlong)
+{
+	Cmdbuf *cb;
+	Cmdtab *ct;
+
+	cb = parsecmd(a, n);
+	if(waserror()){
+		free(cb);
+		nexterror();
+	}
+	ct = lookupcmd(cb, archctlmsg, nelem(archctlmsg));
+	switch(ct->index){
+	case CMpge:
+		if(!m->havepge)
+			error("processor does not support pge");
+		if(strcmp(cb->f[1], "on") == 0)
+			putcr4(getcr4() | 0x80);
+		else if(strcmp(cb->f[1], "off") == 0)
+			putcr4(getcr4() & ~0x80);
+		else
+			cmderror(cb, "invalid pge ctl");
+		break;
+	case CMcoherence:
+		if(strcmp(cb->f[1], "mb386") == 0)
+			coherence = mb386;
+		else if(strcmp(cb->f[1], "mb586") == 0){
+			if(X86FAMILY(m->cpuidax) < 5)
+				error("invalid coherence ctl on this cpu family");
+			coherence = mb586;
+		}
+		else if(strcmp(cb->f[1], "nop") == 0){
+			/* only safe on vmware */
+			if(conf.nmach > 1)
+				error("cannot disable coherence on a multiprocessor");
+			coherence = nop;
+		}else
+			cmderror(cb, "invalid coherence ctl");
+		break;
+	case CMi8253set:
+		if(strcmp(cb->f[1], "on") == 0)
+			doi8253set = 1;
+		else if(strcmp(cb->f[1], "off") == 0){
+			doi8253set = 0;
+			(*arch->timerset)(0);
+		}else
+			cmderror(cb, "invalid i2853set ctl");
+		break;
+	}
+	free(cb);
+	poperror();
+	return n;
+}
+
+void
+archinit(void)
+{
+	PCArch **p;
+
+	arch = 0;
+	for(p = knownarch; *p; p++){
+		if((*p)->ident && (*p)->ident() == 0){
+			arch = *p;
+			break;
+		}
+	}
+	if(arch == 0)
+		arch = &archgeneric;
+	else{
+		if(arch->id == 0)
+			arch->id = archgeneric.id;
+		if(arch->reset == 0)
+			arch->reset = archgeneric.reset;
+		if(arch->serialpower == 0)
+			arch->serialpower = archgeneric.serialpower;
+		if(arch->modempower == 0)
+			arch->modempower = archgeneric.modempower;
+		if(arch->intrinit == 0)
+			arch->intrinit = archgeneric.intrinit;
+		if(arch->intrenable == 0)
+			arch->intrenable = archgeneric.intrenable;
+	}
+
+	/*
+	 *  Decide whether to use copy-on-reference (386 and mp).
+	 *  We get another chance to set it in mpinit() for a
+	 *  multiprocessor.
+	 */
+	if(X86FAMILY(m->cpuidax) == 3)
+		conf.copymode = 1;
+
+	if(X86FAMILY(m->cpuidax) >= 5)
+		coherence = mb586;
+
+	addarchfile("cputype", 0444, cputyperead, nil);
+	addarchfile("archctl", 0664, archctlread, archctlwrite);
+}
+
+/*
+ *  call either the pcmcia or pccard device setup
+ */
+int
+pcmspecial(char *idstr, ISAConf *isa)
+{
+	return (_pcmspecial != nil)? _pcmspecial(idstr, isa): -1;
+}
+
+/*
+ *  call either the pcmcia or pccard device teardown
+ */
+void
+pcmspecialclose(int a)
+{
+	if (_pcmspecialclose != nil)
+		_pcmspecialclose(a);
+}
+
+/*
+ *  return value and speed of timer set in arch->clockenable
+ */
+uvlong
+fastticks(uvlong *hz)
+{
+	return (*arch->fastclock)(hz);
+}
+
+/*
+ *  set next timer interrupt
+ */
+void
+timerset(uvlong x)
+{
+	if(doi8253set)
+		(*arch->timerset)(x);
+}
--- /dev/null
+++ b/os/pc/devds1620.c
@@ -1,0 +1,368 @@
+#include	"u.h"
+#include	"../port/lib.h"
+#include	"mem.h"
+#include	"dat.h"
+#include	"fns.h"
+#include	"../port/error.h"
+#include	"io.h"
+
+enum {
+	// Ziatech 5512 Digital I/O ASIC register info
+	PortSelect =	0xE7,
+	Port =		0xE1,
+	 DQ =		1<<0,
+	 CLK =		1<<1,
+	 RST =		1<<2,
+	 TL =		1<<3,
+	 TH =		1<<4,
+
+	// ds1620 Masks
+	Mread =		0xA0,
+	Mwrite =	0,
+
+	// ds1620 Registers
+	Rtemp =		0x0A,
+	Rcounter =	0x00,
+	Rslope = 	0x09,
+	Rhi =		0x01,
+	Rlo =		0x02,
+	Rconfig =	0x0C,
+	 Cdone =	1<<7,	// conversion done
+	 Cthf =		1<<6,	// temp >= Rhi
+	 Ctlf =		1<<5,	// temp <= Rlo
+	 Cnvb =		1<<4,	// e^2 nvram busy (write may take up to 10ms)
+	 Ccpu =		1<<1,	// cpu use (0=clk starts conversion when rst lo)
+	 C1shot =	1<<0,	// perform one conversion then stop
+
+	// ds1620 Commands
+	Startconv =	0xEE,
+	Stopconv =	0x22,
+
+	ALOTEMP = 	0,
+	AHITEMP =	1,
+};
+
+#define send(v)	outb(Port, v); delay(1)
+#define recv()	(!(inb(Port) & 1))
+
+enum {
+	Qdir = 0,
+	Qtemp,
+	Qalarm,
+};
+
+Dirtab ds1620tab[]={
+	"temp",		{Qtemp, 0},	0,	0666,
+	"alarm",	{Qalarm, 0},	0,	0444,
+};
+
+typedef struct Temp Temp;
+struct Temp
+{
+	Lock;
+	int lo;
+	int cur;
+	int hi;
+
+	int alo;
+	int ahi;
+	int atime;
+	Queue *aq;
+};
+
+static Temp t;
+
+static void
+sendreg(int r)
+{
+	int d, i;
+
+	r = ~r;
+	for(i=0;i<8;i++) {
+		d = (r >> i) & 1;
+		send(CLK|d);
+		send(d);
+		send(CLK);
+	}
+}
+
+static int
+ds1620rdreg(int r, int nb)
+{
+	int i, s;
+
+	s = splhi();
+
+	outb(PortSelect, 0);
+	send(RST|CLK);
+	sendreg(r|Mread);
+	r = 0;
+	for(i=0; i < nb; i++) {
+		r |= recv() << i;
+		delay(1);
+		send(0);
+		send(CLK);
+	}
+	send(RST);
+
+	splx(s);
+	return r;
+}
+
+static void
+ds1620wrreg(int r, int v, int nb)
+{
+	int d, i, s;
+
+	s = splhi();
+
+	outb(PortSelect, 0);
+	send(RST|CLK);
+	sendreg(r|Mwrite);
+	v = ~v;
+	for(i=0; i < nb; i++) {
+		d = (v >> i) & 1;
+		send(CLK|d);
+		send(0);
+		send(CLK);
+	}
+	send(RST);
+
+	splx(s);
+}
+
+static void
+ds1620cmd(int r)
+{
+	int s;
+
+	s = splhi();
+	outb(PortSelect, 0);
+	send(RST|CLK);
+	sendreg(r);
+	send(RST);
+	splx(s);
+}
+
+static char*
+t2s(int t)
+{
+	static char s[16];
+
+	sprint(s, "%4d.", t>>1);
+	if(t&1)
+		strcat(s, "5");
+	else
+		strcat(s, "0");
+	return s;
+}
+
+static int
+s2t(char *s)
+{
+	int v;
+	char *p;
+	p = strchr(s, '.');
+	if(p != nil)
+		*p++ = '\0';
+	v = strtoul(s, nil, 0);
+	v <<= 1;
+	if(p != nil && *p != '\0' && *p >= '5')
+		v |= 1;
+	return v;
+}
+
+static void
+alarm(int code, Temp *tt)
+{
+	char buf[256], *end;
+	int s;
+
+	s = seconds();
+
+	if(s - tt->atime < 60)
+		return;
+	tt->atime = s;
+
+	end = buf;
+	end += sprint(buf, "(alarm) %8.8uX %uld temp ", code, seconds());
+	switch(code) {
+	case ALOTEMP:
+		end += sprint(end, "%s below threshold ", t2s(tt->lo));
+		end += sprint(end, "%s.\n", t2s(tt->alo));
+		break;
+	case AHITEMP:
+		end += sprint(end, "%s above threshold ", t2s(tt->hi));
+		end += sprint(end, "%s.\n", t2s(tt->ahi));
+		break;
+	}
+
+	qproduce(tt->aq, buf, end-buf);
+}
+
+void
+tmon(void *a)
+{
+	int r;
+	Temp *t;
+
+	t = a;
+	r = ds1620rdreg(Rtemp, 9);
+	lock(t);
+	t->lo = t->cur = t->hi = r;
+	unlock(t);
+	for(;;) {
+		tsleep(&up->sleep, return0, nil, 1000);
+		r = ds1620rdreg(Rtemp, 9);
+		lock(t);
+		t->cur = r;
+		if(r < t->lo)
+			t->lo = r;
+		if(r > t->hi)
+			t->hi = r;
+		if(t->lo < t->alo)
+			alarm(ALOTEMP, t);
+		if(t->hi > t->ahi)
+			alarm(AHITEMP, t);
+		unlock(t);
+	}
+	pexit("", 0);
+}
+
+static void
+ds1620init(void)
+{
+	int r;
+
+	t.aq = qopen(8*1024, Qmsg, nil, nil);
+	if(t.aq == nil)
+		error(Enomem);
+
+	ds1620wrreg(Rconfig, Ccpu, 8);	// continuous sample mode
+	ds1620cmd(Startconv);
+	r = ds1620rdreg(Rtemp, 9);
+	t.alo = ds1620rdreg(Rlo, 9);	
+	t.ahi = ds1620rdreg(Rhi, 9);
+
+	print("#L: temp %s (c) ", t2s(r));
+	print("low threshold %s (c) ", t2s(t.alo));
+	print("high threshold %s (c)\n", t2s(t.ahi));
+
+	kproc("tempmon", tmon, &t, 0);
+}
+
+static Chan*
+ds1620attach(char *spec)
+{
+	return devattach('L', spec);
+}
+
+static int
+ds1620walk(Chan *c, char* name)
+{
+	return devwalk(c, name, ds1620tab, nelem(ds1620tab), devgen);
+}
+
+static void
+ds1620stat(Chan *c, char* db)
+{
+	ds1620tab[1].length = qlen(t.aq);
+	devstat(c, db, ds1620tab, nelem(ds1620tab), devgen);
+}
+
+static Chan*
+ds1620open(Chan *c, int omode)
+{
+	return devopen(c, omode, ds1620tab, nelem(ds1620tab), devgen);
+}
+
+static void
+ds1620close(Chan*)
+{
+}
+
+static long
+ds1620read(Chan *c, void *a, long n, vlong offset)
+{
+	Temp tt;
+	char buf[64];
+	char *s;
+	if(c->qid.path & CHDIR)
+		return devdirread(c, a, n, ds1620tab, nelem(ds1620tab), devgen);
+	buf[0] = 0;
+	switch(c->qid.path) {
+	case Qtemp:
+		lock(&t);
+		tt = t;
+		unlock(&t);
+		s = buf;
+		s+= sprint(s, "%s ", t2s(tt.lo));
+		s+= sprint(s, "%s ", t2s(tt.cur));
+		s+= sprint(s, "%s ", t2s(tt.hi));
+		s+= sprint(s, "%s ", t2s(tt.alo));
+		sprint(s, "%s", t2s(tt.ahi));
+		return readstr(offset, a, n, buf);
+	case Qalarm:
+		return qread(t.aq, a, n);
+	default:
+		error(Egreg);
+		return 0;
+	}
+}
+
+static long
+ds1620write(Chan *c, void *a, long n, vlong)
+{
+	char buf[64];
+	char *f[2];
+	int lo, hi;
+	int nf;
+
+	if(c->qid.path & CHDIR)
+		error(Eperm);
+
+	if(c->qid.path == Qtemp) {
+		if(n >= sizeof(buf))
+			n = sizeof(buf)-1;
+		memmove(buf, a, n);
+		buf[n] = '\0';
+		nf = getfields(buf, f, 2, 1, " \t");
+		if(nf != 2)
+			error(Ebadarg);
+		lo = s2t(f[0]);
+		hi = s2t(f[1]);
+		lock(&t);
+		t.alo = lo;
+		t.ahi = hi;
+		t.atime = 0;
+		ds1620wrreg(Rlo, lo, 9);
+		delay(1);
+		ds1620wrreg(Rhi, hi, 9);
+		unlock(&t);
+		return n;
+	} else
+		error(Eio);
+	return 0;
+
+}
+
+Dev ds1620devtab = {
+	'L',
+	"ds1620",
+	devreset,
+	ds1620init,
+	ds1620attach,
+	devdetach,
+	devclone,
+	ds1620walk,
+	ds1620stat,
+	ds1620open,
+	devcreate,
+	ds1620close,
+	ds1620read,
+	devbread,
+	ds1620write,
+	devbwrite,
+	devremove,
+	devwstat,
+};
--- /dev/null
+++ b/os/pc/devether.c
@@ -1,0 +1,539 @@
+#include "u.h"
+#include "../port/lib.h"
+#include "mem.h"
+#include "dat.h"
+#include "fns.h"
+#include "io.h"
+#include "ureg.h"
+#include "../port/error.h"
+#include "../port/netif.h"
+
+#include "etherif.h"
+
+static Ether *etherxx[MaxEther];
+
+Chan*
+etherattach(char* spec)
+{
+	ulong ctlrno;
+	char *p;
+	Chan *chan;
+
+	ctlrno = 0;
+	if(spec && *spec){
+		ctlrno = strtoul(spec, &p, 0);
+		if((ctlrno == 0 && p == spec) || *p || (ctlrno >= MaxEther))
+			error(Ebadarg);
+	}
+	if(etherxx[ctlrno] == 0)
+		error(Enodev);
+
+	chan = devattach('l', spec);
+	chan->dev = ctlrno;
+	if(etherxx[ctlrno]->attach)
+		etherxx[ctlrno]->attach(etherxx[ctlrno]);
+	return chan;
+}
+
+static Walkqid*
+etherwalk(Chan* chan, Chan* nchan, char** name, int nname)
+{
+	return netifwalk(etherxx[chan->dev], chan, nchan, name, nname);
+}
+
+static int
+etherstat(Chan* chan, uchar* dp, int n)
+{
+	return netifstat(etherxx[chan->dev], chan, dp, n);
+}
+
+static Chan*
+etheropen(Chan* chan, int omode)
+{
+	return netifopen(etherxx[chan->dev], chan, omode);
+}
+
+static void
+ethercreate(Chan*, char*, int, ulong)
+{
+}
+
+static void
+etherclose(Chan* chan)
+{
+	netifclose(etherxx[chan->dev], chan);
+}
+
+static long
+etherread(Chan* chan, void* buf, long n, vlong off)
+{
+	Ether *ether;
+	ulong offset = off;
+
+	ether = etherxx[chan->dev];
+	if((chan->qid.type & QTDIR) == 0 && ether->ifstat){
+		/*
+		 * With some controllers it is necessary to reach
+		 * into the chip to extract statistics.
+		 */
+		if(NETTYPE(chan->qid.path) == Nifstatqid)
+			return ether->ifstat(ether, buf, n, offset);
+		else if(NETTYPE(chan->qid.path) == Nstatqid)
+			ether->ifstat(ether, buf, 0, offset);
+	}
+
+	return netifread(ether, chan, buf, n, offset);
+}
+
+static Block*
+etherbread(Chan* chan, long n, ulong offset)
+{
+	return netifbread(etherxx[chan->dev], chan, n, offset);
+}
+
+static int
+etherwstat(Chan* chan, uchar* dp, int n)
+{
+	return netifwstat(etherxx[chan->dev], chan, dp, n);
+}
+
+static void
+etherrtrace(Netfile* f, Etherpkt* pkt, int len)
+{
+	int i, n;
+	Block *bp;
+
+	if(qwindow(f->in) <= 0)
+		return;
+	if(len > 58)
+		n = 58;
+	else
+		n = len;
+	bp = iallocb(64);
+	if(bp == nil)
+		return;
+	memmove(bp->wp, pkt->d, n);
+	i = TK2MS(MACHP(0)->ticks);
+	bp->wp[58] = len>>8;
+	bp->wp[59] = len;
+	bp->wp[60] = i>>24;
+	bp->wp[61] = i>>16;
+	bp->wp[62] = i>>8;
+	bp->wp[63] = i;
+	bp->wp += 64;
+	qpass(f->in, bp);
+}
+
+Block*
+etheriq(Ether* ether, Block* bp, int fromwire)
+{
+	Etherpkt *pkt;
+	ushort type;
+	int len, multi, tome, fromme;
+	Netfile **ep, *f, **fp, *fx;
+	Block *xbp;
+
+	ether->inpackets++;
+
+	pkt = (Etherpkt*)bp->rp;
+	len = BLEN(bp);
+	type = (pkt->type[0]<<8)|pkt->type[1];
+	fx = 0;
+	ep = &ether->f[Ntypes];
+
+	multi = pkt->d[0] & 1;
+	/* check for valid multcast addresses */
+	if(multi && memcmp(pkt->d, ether->bcast, sizeof(pkt->d)) != 0 && ether->prom == 0){
+		if(!activemulti(ether, pkt->d, sizeof(pkt->d))){
+			if(fromwire){
+				freeb(bp);
+				bp = 0;
+			}
+			return bp;
+		}
+	}
+
+	/* is it for me? */
+	tome = memcmp(pkt->d, ether->ea, sizeof(pkt->d)) == 0;
+	fromme = memcmp(pkt->s, ether->ea, sizeof(pkt->s)) == 0;
+
+	/*
+	 * Multiplex the packet to all the connections which want it.
+	 * If the packet is not to be used subsequently (fromwire != 0),
+	 * attempt to simply pass it into one of the connections, thereby
+	 * saving a copy of the data (usual case hopefully).
+	 */
+	for(fp = ether->f; fp < ep; fp++){
+		if(f = *fp)
+		if(f->type == type || f->type < 0)
+		if(tome || multi || f->prom){
+			/* Don't want to hear bridged packets */
+			if(f->bridge && !fromwire && !fromme)
+				continue;
+			if(!f->headersonly){
+				if(fromwire && fx == 0)
+					fx = f;
+				else if(xbp = iallocb(len)){
+					memmove(xbp->wp, pkt, len);
+					xbp->wp += len;
+					if(qpass(f->in, xbp) < 0)
+						ether->soverflows++;
+				}
+				else
+					ether->soverflows++;
+			}
+			else
+				etherrtrace(f, pkt, len);
+		}
+	}
+
+	if(fx){
+		if(qpass(fx->in, bp) < 0)
+			ether->soverflows++;
+		return 0;
+	}
+	if(fromwire){
+		freeb(bp);
+		return 0;
+	}
+
+	return bp;
+}
+
+static int
+etheroq(Ether* ether, Block* bp)
+{
+	int len, loopback, s;
+	Etherpkt *pkt;
+
+	ether->outpackets++;
+
+	/*
+	 * Check if the packet has to be placed back onto the input queue,
+	 * i.e. if it's a loopback or broadcast packet or the interface is
+	 * in promiscuous mode.
+	 * If it's a loopback packet indicate to etheriq that the data isn't
+	 * needed and return, etheriq will pass-on or free the block.
+	 * To enable bridging to work, only packets that were originated
+	 * by this interface are fed back.
+	 */
+	pkt = (Etherpkt*)bp->rp;
+	len = BLEN(bp);
+	loopback = memcmp(pkt->d, ether->ea, sizeof(pkt->d)) == 0;
+	if(loopback || memcmp(pkt->d, ether->bcast, sizeof(pkt->d)) == 0 || ether->prom){
+		s = splhi();
+		etheriq(ether, bp, 0);
+		splx(s);
+	}
+
+	if(!loopback){
+		qbwrite(ether->oq, bp);
+		if(ether->transmit != nil)
+			ether->transmit(ether);
+	} else
+		freeb(bp);
+
+	return len;
+}
+
+static long
+etherwrite(Chan* chan, void* buf, long n, vlong)
+{
+	Ether *ether;
+	Block *bp;
+	int nn, onoff;
+	Cmdbuf *cb;
+
+	ether = etherxx[chan->dev];
+	if(NETTYPE(chan->qid.path) != Ndataqid) {
+		nn = netifwrite(ether, chan, buf, n);
+		if(nn >= 0)
+			return nn;
+		cb = parsecmd(buf, n);
+		if(strcmp(cb->f[0], "nonblocking") == 0){
+			if(cb->nf <= 1)
+				onoff = 1;
+			else
+				onoff = atoi(cb->f[1]);
+			qnoblock(ether->oq, onoff);
+			free(cb);
+			return n;
+		}
+		free(cb);
+		if(ether->ctl!=nil)
+			return ether->ctl(ether,buf,n);
+			
+		error(Ebadctl);
+	}
+
+	if(n > ether->maxmtu)
+		error(Etoobig);
+	if(n < ether->minmtu)
+		error(Etoosmall);
+
+	bp = allocb(n);
+	if(waserror()){
+		freeb(bp);
+		nexterror();
+	}
+	memmove(bp->rp, buf, n);
+	memmove(bp->rp+Eaddrlen, ether->ea, Eaddrlen);
+	poperror();
+	bp->wp += n;
+
+	return etheroq(ether, bp);
+}
+
+static long
+etherbwrite(Chan* chan, Block* bp, ulong)
+{
+	Ether *ether;
+	long n;
+
+	n = BLEN(bp);
+	if(NETTYPE(chan->qid.path) != Ndataqid){
+		if(waserror()) {
+			freeb(bp);
+			nexterror();
+		}
+		n = etherwrite(chan, bp->rp, n, 0);
+		poperror();
+		freeb(bp);
+		return n;
+	}
+	ether = etherxx[chan->dev];
+
+	if(n > ether->maxmtu){
+		freeb(bp);
+		error(Etoobig);
+	}
+	if(n < ether->minmtu){
+		freeb(bp);
+		error(Etoosmall);
+	}
+
+	return etheroq(ether, bp);
+}
+
+static struct {
+	char*	type;
+	int	(*reset)(Ether*);
+} cards[MaxEther+1];
+
+void
+addethercard(char* t, int (*r)(Ether*))
+{
+	static int ncard;
+
+	if(ncard == MaxEther)
+		panic("too many ether cards");
+	cards[ncard].type = t;
+	cards[ncard].reset = r;
+	ncard++;
+}
+
+int
+parseether(uchar *to, char *from)
+{
+	char nip[4];
+	char *p;
+	int i;
+
+	p = from;
+	for(i = 0; i < Eaddrlen; i++){
+		if(*p == 0)
+			return -1;
+		nip[0] = *p++;
+		if(*p == 0)
+			return -1;
+		nip[1] = *p++;
+		nip[2] = 0;
+		to[i] = strtoul(nip, 0, 16);
+		if(*p == ':')
+			p++;
+	}
+	return 0;
+}
+
+static Ether*
+etherprobe(int cardno, int ctlrno)
+{
+	int i;
+	Ether *ether;
+	char buf[128], name[32];
+
+	ether = malloc(sizeof(Ether));
+	memset(ether, 0, sizeof(Ether));
+	ether->ctlrno = ctlrno;
+	ether->tbdf = BUSUNKNOWN;
+	ether->mbps = 10;
+	ether->minmtu = ETHERMINTU;
+	ether->maxmtu = ETHERMAXTU;
+
+	if(cardno < 0){
+		if(isaconfig("ether", ctlrno, ether) == 0){
+			free(ether);
+			return nil;
+		}
+		for(cardno = 0; cards[cardno].type; cardno++){
+			if(cistrcmp(cards[cardno].type, ether->type))
+				continue;
+			for(i = 0; i < ether->nopt; i++){
+				if(strncmp(ether->opt[i], "ea=", 3))
+					continue;
+				if(parseether(ether->ea, &ether->opt[i][3]))
+					memset(ether->ea, 0, Eaddrlen);
+			}
+			break;
+		}
+	}
+
+	if(cardno >= MaxEther || cards[cardno].type == nil){
+		free(ether);
+		return nil;
+	}
+	if(cards[cardno].reset(ether) < 0){
+		free(ether);
+		return nil;
+	}
+
+	/*
+	 * IRQ2 doesn't really exist, it's used to gang the interrupt
+	 * controllers together. A device set to IRQ2 will appear on
+	 * the second interrupt controller as IRQ9.
+	 */
+	if(ether->irq == 2)
+		ether->irq = 9;
+	snprint(name, sizeof(name), "ether%d", ctlrno);
+
+	/*
+	 * If ether->irq is <0, it is a hack to indicate no interrupt
+	 * used by ethersink.
+	 */
+	if(ether->irq >= 0)
+		intrenable(ether->irq, ether->interrupt, ether, ether->tbdf, name);
+
+	i = sprint(buf, "#l%d: %s: %dMbps port 0x%luX irq %d",
+		ctlrno, cards[cardno].type, ether->mbps, ether->port, ether->irq);
+	if(ether->mem)
+		i += sprint(buf+i, " addr 0x%luX", PADDR(ether->mem));
+	if(ether->size)
+		i += sprint(buf+i, " size 0x%luX", ether->size);
+	i += sprint(buf+i, ": %2.2ux%2.2ux%2.2ux%2.2ux%2.2ux%2.2ux",
+		ether->ea[0], ether->ea[1], ether->ea[2],
+		ether->ea[3], ether->ea[4], ether->ea[5]);
+	sprint(buf+i, "\n");
+	print(buf);
+
+	if (ether->mbps >= 1000) {
+		netifinit(ether, name, Ntypes, 512*1024);
+		if(ether->oq == 0)
+			ether->oq = qopen(512*1024, Qmsg, 0, 0);
+	} else if(ether->mbps >= 100){
+		netifinit(ether, name, Ntypes, 256*1024);
+		if(ether->oq == 0)
+			ether->oq = qopen(256*1024, Qmsg, 0, 0);
+	}
+	else{
+		netifinit(ether, name, Ntypes, 128*1024);
+		if(ether->oq == 0)
+			ether->oq = qopen(128*1024, Qmsg, 0, 0);
+	}
+	if(ether->oq == 0)
+		panic("etherreset %s", name);
+	ether->alen = Eaddrlen;
+	memmove(ether->addr, ether->ea, Eaddrlen);
+	memset(ether->bcast, 0xFF, Eaddrlen);
+
+	return ether;
+}
+
+static void
+etherreset(void)
+{
+	Ether *ether;
+	int cardno, ctlrno;
+
+	for(ctlrno = 0; ctlrno < MaxEther; ctlrno++){
+		if((ether = etherprobe(-1, ctlrno)) == nil)
+			continue;
+		etherxx[ctlrno] = ether;
+	}
+
+	if(getconf("*noetherprobe"))
+		return;
+
+	cardno = ctlrno = 0;
+	while(cards[cardno].type != nil && ctlrno < MaxEther){
+		if(etherxx[ctlrno] != nil){
+			ctlrno++;
+			continue;
+		}
+		if((ether = etherprobe(cardno, ctlrno)) == nil){
+			cardno++;
+			continue;
+		}
+		etherxx[ctlrno] = ether;
+		ctlrno++;
+	}
+}
+
+static void
+ethershutdown(void)
+{
+	Ether *ether;
+	int i;
+
+	for(i = 0; i < MaxEther; i++){
+		ether = etherxx[i];
+		if(ether == nil)
+			continue;
+		if(ether->shutdown == nil) {
+			print("#l%d: no shutdown fuction\n", i);
+			continue;
+		}
+		(*ether->shutdown)(ether);
+	}
+}
+
+
+#define POLY 0xedb88320
+
+/* really slow 32 bit crc for ethers */
+ulong
+ethercrc(uchar *p, int len)
+{
+	int i, j;
+	ulong crc, b;
+
+	crc = 0xffffffff;
+	for(i = 0; i < len; i++){
+		b = *p++;
+		for(j = 0; j < 8; j++){
+			crc = (crc>>1) ^ (((crc^b) & 1) ? POLY : 0);
+			b >>= 1;
+		}
+	}
+	return crc;
+}
+
+Dev etherdevtab = {
+	'l',
+	"ether",
+
+	etherreset,
+	devinit,
+	ethershutdown,
+	etherattach,
+	etherwalk,
+	etherstat,
+	etheropen,
+	ethercreate,
+	etherclose,
+	etherread,
+	etherbread,
+	etherwrite,
+	etherbwrite,
+	devremove,
+	etherwstat,
+};
--- /dev/null
+++ b/os/pc/devfloppy.c
@@ -1,0 +1,1082 @@
+#include	"u.h"
+#include	"../port/lib.h"
+#include	"mem.h"
+#include	"dat.h"
+#include	"fns.h"
+#include	"io.h"
+#include	"../port/error.h"
+
+#include	"floppy.h"
+
+/* Intel 82077A (8272A compatible) floppy controller */
+
+/* This module expects the following functions to be defined
+ * elsewhere: 
+ * 
+ * inb()
+ * outb()
+ * floppyexec()
+ * floppyeject() 
+ * floppysetup0()
+ * floppysetup1()
+ * dmainit()
+ * dmasetup()
+ * dmaend()
+ * 
+ * On DMA systems, floppyexec() should be an empty function; 
+ * on non-DMA systems, dmaend() should be an empty function; 
+ * dmasetup() may enforce maximum transfer sizes. 
+ */
+
+enum {
+	/* file types */
+	Qdir=		0, 
+	Qdata=		(1<<2),
+	Qctl=		(2<<2),
+	Qmask=		(3<<2),
+
+	DMAchan=	2,	/* floppy dma channel */
+};
+
+#define DPRINT if(floppydebug)print
+int floppydebug = 0;
+
+/*
+ *  types of drive (from PC equipment byte)
+ */
+enum
+{
+	Tnone=		0,
+	T360kb=		1,
+	T1200kb=	2,
+	T720kb=		3,
+	T1440kb=	4,
+};
+
+FType floppytype[] =
+{
+ { "3½HD",	T1440kb, 512, 18, 2, 1, 80, 0x1B, 0x54,	0, },
+ { "3½DD",	T1440kb, 512,  9, 2, 1, 80, 0x1B, 0x54, 2, },
+ { "3½DD",	T720kb,  512,  9, 2, 1, 80, 0x1B, 0x54, 2, },
+ { "5¼HD",	T1200kb, 512, 15, 2, 1, 80, 0x2A, 0x50, 0, },
+ { "5¼DD",	T1200kb, 512,  9, 2, 2, 40, 0x2A, 0x50, 1, },
+ { "ATT3B1",	T1200kb, 512,  8, 2, 2, 48, 0x2A, 0x50, 1, },
+ { "5¼DD",	T360kb,  512,  9, 2, 1, 40, 0x2A, 0x50, 2, },
+};
+
+/*
+ *  bytes per sector encoding for the controller.
+ *  - index for b2c is is (bytes per sector/128).
+ *  - index for c2b is code from b2c
+ */
+static int b2c[] =
+{
+[1]	0,
+[2]	1,
+[4]	2,
+[8]	3,
+};
+static int c2b[] =
+{
+	128,
+	256,
+	512,
+	1024,
+};
+
+FController	fl;
+
+#define MOTORBIT(i)	(1<<((i)+4))
+
+/*
+ *  predeclared
+ */
+static int	cmddone(void*);
+static void	floppyformat(FDrive*, Cmdbuf*);
+static void	floppykproc(void*);
+static void	floppypos(FDrive*,long);
+static int	floppyrecal(FDrive*);
+static int	floppyresult(void);
+static void	floppyrevive(void);
+static long	floppyseek(FDrive*, long);
+static int	floppysense(void);
+static void	floppywait(int);
+static long	floppyxfer(FDrive*, int, void*, long, long);
+
+Dirtab floppydir[]={
+	".",		{Qdir, 0, QTDIR},	0,	0550,
+	"fd0disk",		{Qdata + 0},	0,	0660,
+	"fd0ctl",		{Qctl + 0},	0,	0660,
+	"fd1disk",		{Qdata + 1},	0,	0660,
+	"fd1ctl",		{Qctl + 1},	0,	0660,
+	"fd2disk",		{Qdata + 2},	0,	0660,
+	"fd2ctl",		{Qctl + 2},	0,	0660,
+	"fd3disk",		{Qdata + 3},	0,	0660,
+	"fd3ctl",		{Qctl + 3},	0,	0660,
+};
+#define NFDIR	2	/* directory entries/drive */
+
+enum
+{
+	CMdebug,
+	CMnodebug,
+	CMeject,
+	CMformat,
+	CMreset,
+};
+
+static Cmdtab floppyctlmsg[] =
+{
+	CMdebug,	"debug",	1,
+	CMnodebug,	"nodebug", 1,
+	CMeject,	"eject",	1,
+	CMformat,	"format",	0,
+	CMreset,	"reset",	1,
+};
+
+static void
+fldump(void)
+{
+	DPRINT("sra %ux srb %ux dor %ux msr %ux dir %ux\n", inb(Psra), inb(Psrb),
+		inb(Pdor), inb(Pmsr), inb(Pdir));
+}
+
+/*
+ *  set floppy drive to its default type
+ */
+static void
+floppysetdef(FDrive *dp)
+{
+	FType *t;
+
+	for(t = floppytype; t < &floppytype[nelem(floppytype)]; t++)
+		if(dp->dt == t->dt){
+			dp->t = t;
+			floppydir[1+NFDIR*dp->dev].length = dp->t->cap;
+			break;
+		}
+}
+
+static void
+floppyreset(void)
+{
+	FDrive *dp;
+	FType *t;
+	ulong maxtsize;
+	
+	floppysetup0(&fl);
+	if(fl.ndrive == 0)
+		return;
+
+	/*
+	 *  init dependent parameters
+	 */
+	maxtsize = 0;
+	for(t = floppytype; t < &floppytype[nelem(floppytype)]; t++){
+		t->cap = t->bytes * t->heads * t->sectors * t->tracks;
+		t->bcode = b2c[t->bytes/128];
+		t->tsize = t->bytes * t->sectors;
+		if(maxtsize < t->tsize)
+			maxtsize = t->tsize;
+	}
+
+	dmainit(DMAchan, maxtsize);
+
+	/*
+	 *  allocate the drive storage
+	 */
+	fl.d = xalloc(fl.ndrive*sizeof(FDrive));
+	fl.selected = fl.d;
+
+	/*
+	 *  stop the motors
+	 */
+	fl.motor = 0;
+	delay(10);
+	outb(Pdor, fl.motor | Fintena | Fena);
+	delay(10);
+
+	/*
+	 *  init drives
+	 */
+	for(dp = fl.d; dp < &fl.d[fl.ndrive]; dp++){
+		dp->dev = dp - fl.d;
+		dp->dt = T1440kb;
+		floppysetdef(dp);
+		dp->cyl = -1;			/* because we don't know */
+		dp->cache = (uchar*)xspanalloc(maxtsize, BY2PG, 64*1024);
+		dp->ccyl = -1;
+		dp->vers = 0;
+	}
+
+	/*
+	 *  first operation will recalibrate
+	 */
+	fl.confused = 1;
+
+	floppysetup1(&fl);
+}
+
+static Chan*
+floppyattach(char *spec)
+{
+	static int kstarted;
+
+	if(fl.ndrive == 0)
+		error(Enodev);
+
+	if(kstarted == 0){
+		/*
+		 *  watchdog to turn off the motors
+		 */
+		kstarted = 1;
+		kproc("floppy", floppykproc, 0, 0);
+	}
+	return devattach('f', spec);
+}
+
+static Walkqid*
+floppywalk(Chan *c, Chan *nc, char **name, int nname)
+{
+	return devwalk(c, nc, name, nname, floppydir, 1+fl.ndrive*NFDIR, devgen);
+}
+
+static int
+floppystat(Chan *c, uchar *dp, int n)
+{
+	return devstat(c, dp, n, floppydir, 1+fl.ndrive*NFDIR, devgen);
+}
+
+static Chan*
+floppyopen(Chan *c, int omode)
+{
+	return devopen(c, omode, floppydir, 1+fl.ndrive*NFDIR, devgen);
+}
+
+static void
+floppyclose(Chan *)
+{
+}
+
+static void
+islegal(ulong offset, long n, FDrive *dp)
+{
+	if(offset % dp->t->bytes)
+		error(Ebadarg);
+	if(n % dp->t->bytes)
+		error(Ebadarg);
+}
+
+/*
+ *  check if the floppy has been replaced under foot.  cause
+ *  an error if it has.
+ *
+ *  a seek and a read clears the condition.  this was determined
+ *  experimentally, there has to be a better way.
+ *
+ *  if the read fails, cycle through the possible floppy
+ *  density till one works or we've cycled through all
+ *  possibilities for this drive.
+ */
+static void
+changed(Chan *c, FDrive *dp)
+{
+	ulong old;
+	FType *start;
+
+	/*
+	 *  if floppy has changed or first time through
+	 */
+	if((inb(Pdir)&Fchange) || dp->vers == 0){
+		DPRINT("changed\n");
+		fldump();
+		dp->vers++;
+		start = dp->t;
+		dp->maxtries = 3;	/* limit it when we're probing */
+
+		/* floppyon will fail if there's a controller but no drive */
+		dp->confused = 1;	/* make floppyon recal */
+		if(floppyon(dp) < 0)
+			error(Eio);
+
+		/* seek to the first track */
+		floppyseek(dp, dp->t->heads*dp->t->tsize);
+		while(waserror()){
+			/*
+			 *  if first attempt doesn't reset changed bit, there's
+			 *  no floppy there
+			 */
+			if(inb(Pdir)&Fchange)
+				nexterror();
+
+			while(++dp->t){
+				if(dp->t == &floppytype[nelem(floppytype)])
+					dp->t = floppytype;
+				if(dp->dt == dp->t->dt)
+					break;
+			}
+			floppydir[1+NFDIR*dp->dev].length = dp->t->cap;
+
+			/* floppyon will fail if there's a controller but no drive */
+			if(floppyon(dp) < 0)
+				error(Eio);
+
+			DPRINT("changed: trying %s\n", dp->t->name);
+			fldump();
+			if(dp->t == start)
+				nexterror();
+		}
+
+		/* if the read succeeds, we've got the density right */
+		floppyxfer(dp, Fread, dp->cache, 0, dp->t->tsize);
+		poperror();
+		dp->maxtries = 20;
+	}
+
+	old = c->qid.vers;
+	c->qid.vers = dp->vers;
+	if(old && old != dp->vers)
+		error(Eio);
+}
+
+static int
+readtrack(FDrive *dp, int cyl, int head)
+{
+	int i, nn, sofar;
+	ulong pos;
+
+	nn = dp->t->tsize;
+	if(dp->ccyl==cyl && dp->chead==head)
+		return nn;
+	pos = (cyl*dp->t->heads+head) * nn;
+	for(sofar = 0; sofar < nn; sofar += i){
+		dp->ccyl = -1;
+		i = floppyxfer(dp, Fread, dp->cache + sofar, pos + sofar, nn - sofar);
+		if(i <= 0)
+			return -1;
+	}
+	dp->ccyl = cyl;
+	dp->chead = head;
+	return nn;
+}
+
+static long
+floppyread(Chan *c, void *a, long n, vlong off)
+{
+	FDrive *dp;
+	long rv;
+	int sec, head, cyl;
+	long len;
+	uchar *aa;
+	ulong offset = off;
+
+	if(c->qid.type & QTDIR)
+		return devdirread(c, a, n, floppydir, 1+fl.ndrive*NFDIR, devgen);
+
+	rv = 0;
+	dp = &fl.d[c->qid.path & ~Qmask];
+	switch ((int)(c->qid.path & Qmask)) {
+	case Qdata:
+		islegal(offset, n, dp);
+		aa = a;
+
+		qlock(&fl);
+		if(waserror()){
+			qunlock(&fl);
+			nexterror();
+		}
+		floppyon(dp);
+		changed(c, dp);
+		for(rv = 0; rv < n; rv += len){
+			/*
+			 *  all xfers come out of the track cache
+			 */
+			dp->len = n - rv;
+			floppypos(dp, offset+rv);
+			cyl = dp->tcyl;
+			head = dp->thead;
+			len = dp->len;
+			sec = dp->tsec;
+			if(readtrack(dp, cyl, head) < 0)
+				break;
+			memmove(aa+rv, dp->cache + (sec-1)*dp->t->bytes, len);
+		}
+		qunlock(&fl);
+		poperror();
+
+		break;
+	case Qctl:
+		return readstr(offset, a, n, dp->t->name);
+	default:
+		panic("floppyread: bad qid");
+	}
+
+	return rv;
+}
+
+static long
+floppywrite(Chan *c, void *a, long n, vlong off)
+{
+	FDrive *dp;
+	long rv, i;
+	char *aa = a;
+	Cmdbuf *cb;
+	Cmdtab *ct;
+	ulong offset = off;
+
+	rv = 0;
+	dp = &fl.d[c->qid.path & ~Qmask];
+	switch ((int)(c->qid.path & Qmask)) {
+	case Qdata:
+		islegal(offset, n, dp);
+		qlock(&fl);
+		if(waserror()){
+			qunlock(&fl);
+			nexterror();
+		}
+		floppyon(dp);
+		changed(c, dp);
+		for(rv = 0; rv < n; rv += i){
+			floppypos(dp, offset+rv);
+			if(dp->tcyl == dp->ccyl)
+				dp->ccyl = -1;
+			i = floppyxfer(dp, Fwrite, aa+rv, offset+rv, n-rv);
+			if(i < 0)
+				break;
+			if(i == 0)
+				error(Eio);
+		}
+		qunlock(&fl);
+		poperror();
+		break;
+	case Qctl:
+		rv = n;
+		cb = parsecmd(a, n);
+		if(waserror()){
+			free(cb);
+			nexterror();
+		}
+		qlock(&fl);
+		if(waserror()){
+			qunlock(&fl);
+			nexterror();
+		}
+		ct = lookupcmd(cb, floppyctlmsg, nelem(floppyctlmsg));
+		switch(ct->index){
+		case CMeject:
+			floppyeject(dp);
+			break;
+		case CMformat:
+			floppyformat(dp, cb);
+			break;
+		case CMreset:
+			fl.confused = 1;
+			floppyon(dp);
+			break;
+		case CMdebug:
+			floppydebug = 1;
+			break;
+		case CMnodebug:
+			floppydebug = 0;
+			break;
+		}
+		poperror();
+		qunlock(&fl);
+		poperror();
+		free(cb);
+		break;
+	default:
+		panic("floppywrite: bad qid");
+	}
+
+	return rv;
+}
+
+static void
+floppykproc(void *)
+{
+	FDrive *dp;
+
+	while(waserror())
+		;
+	for(;;){
+		for(dp = fl.d; dp < &fl.d[fl.ndrive]; dp++){
+			if((fl.motor&MOTORBIT(dp->dev))
+			&& TK2SEC(m->ticks - dp->lasttouched) > 5
+			&& canqlock(&fl)){
+				if(TK2SEC(m->ticks - dp->lasttouched) > 5)
+					floppyoff(dp);
+				qunlock(&fl);
+			}
+		}
+		tsleep(&up->sleep, return0, 0, 1000);
+	}
+}
+
+/*
+ *  start a floppy drive's motor.
+ */
+static int
+floppyon(FDrive *dp)
+{
+	int alreadyon;
+	int tries;
+
+	if(fl.confused)
+		floppyrevive();
+
+	/* start motor and select drive */
+	alreadyon = fl.motor & MOTORBIT(dp->dev);
+	fl.motor |= MOTORBIT(dp->dev);
+	outb(Pdor, fl.motor | Fintena | Fena | dp->dev);
+	if(!alreadyon){
+		/* wait for drive to spin up */
+		tsleep(&up->sleep, return0, 0, 750);
+
+		/* clear any pending interrupts */
+		floppysense();
+	}
+
+	/* set transfer rate */
+	if(fl.rate != dp->t->rate){
+		fl.rate = dp->t->rate;
+		outb(Pdsr, fl.rate);
+	}
+
+	/* get drive to a known cylinder */
+	if(dp->confused)
+		for(tries = 0; tries < 4; tries++)
+			if(floppyrecal(dp) >= 0)
+				break;
+	dp->lasttouched = m->ticks;
+	fl.selected = dp;
+
+	/* return -1 if this didn't work */
+	if(dp->confused)
+		return -1;
+	return 0;
+}
+
+/*
+ *  stop the floppy if it hasn't been used in 5 seconds
+ */
+static void
+floppyoff(FDrive *dp)
+{
+	fl.motor &= ~MOTORBIT(dp->dev);
+	outb(Pdor, fl.motor | Fintena | Fena | dp->dev);
+}
+
+/*
+ *  send a command to the floppy
+ */
+static int
+floppycmd(void)
+{
+	int i;
+	int tries;
+
+	fl.nstat = 0;
+	for(i = 0; i < fl.ncmd; i++){
+		for(tries = 0; ; tries++){
+			if((inb(Pmsr)&(Ffrom|Fready)) == Fready)
+				break;
+			if(tries > 1000){
+				DPRINT("cmd %ux can't be sent (%d)\n", fl.cmd[0], i);
+				fldump();
+
+				/* empty fifo, might have been a bad command */
+				floppyresult();
+				return -1;
+			}
+			microdelay(8);	/* for machine independence */
+		}
+		outb(Pfdata, fl.cmd[i]);
+	}
+	return 0;
+}
+
+/*
+ *  get a command result from the floppy
+ *
+ *  when the controller goes ready waiting for a command
+ *  (instead of sending results), we're done
+ * 
+ */
+static int
+floppyresult(void)
+{
+	int i, s;
+	int tries;
+
+	/* get the result of the operation */
+	for(i = 0; i < sizeof(fl.stat); i++){
+		/* wait for status byte */
+		for(tries = 0; ; tries++){
+			s = inb(Pmsr)&(Ffrom|Fready);
+			if(s == Fready){
+				fl.nstat = i;
+				return fl.nstat;
+			}
+			if(s == (Ffrom|Fready))
+				break;
+			if(tries > 1000){
+				DPRINT("floppyresult: %d stats\n", i);
+				fldump();
+				fl.confused = 1;
+				return -1;
+			}
+			microdelay(8);	/* for machine independence */
+		}
+		fl.stat[i] = inb(Pfdata);
+	}
+	fl.nstat = sizeof(fl.stat);
+	return fl.nstat;
+}
+
+/*
+ *  calculate physical address of a logical byte offset into the disk
+ *
+ *  truncate dp->length if it crosses a track boundary
+ */
+static void
+floppypos(FDrive *dp, long off)
+{
+	int lsec;
+	int ltrack;
+	int end;
+
+	lsec = off/dp->t->bytes;
+	ltrack = lsec/dp->t->sectors;
+	dp->tcyl = ltrack/dp->t->heads;
+	dp->tsec = (lsec % dp->t->sectors) + 1;
+	dp->thead = (lsec/dp->t->sectors) % dp->t->heads;
+
+	/*
+	 *  can't read across track boundaries.
+	 *  if so, decrement the bytes to be read.
+	 */
+	end = (ltrack+1)*dp->t->sectors*dp->t->bytes;
+	if(off+dp->len > end)
+		dp->len = end - off;
+}
+
+/*
+ *  get the interrupt cause from the floppy.
+ */
+static int
+floppysense(void)
+{
+	fl.ncmd = 0;
+	fl.cmd[fl.ncmd++] = Fsense;
+	if(floppycmd() < 0)
+		return -1;
+	if(floppyresult() < 2){
+		DPRINT("can't read sense response\n");
+		fldump();
+		fl.confused = 1;
+		return -1;
+	}
+	return 0;
+}
+
+static int
+cmddone(void *)
+{
+	return fl.ncmd == 0;
+}
+
+/*
+ *  Wait for a floppy interrupt.  If none occurs in 5 seconds, we
+ *  may have missed one.  This only happens on some portables which
+ *  do power management behind our backs.  Call the interrupt
+ *  routine to try to clear any conditions.
+ */
+static void
+floppywait(int slow)
+{
+	tsleep(&fl.r, cmddone, 0, slow ? 5000 : 1000);
+	if(!cmddone(0)){
+		floppyintr(0);
+		fl.confused = 1;
+	}
+}
+
+/*
+ *  we've lost the floppy position, go to cylinder 0.
+ */
+static int
+floppyrecal(FDrive *dp)
+{
+	dp->ccyl = -1;
+	dp->cyl = -1;
+
+	fl.ncmd = 0;
+	fl.cmd[fl.ncmd++] = Frecal;
+	fl.cmd[fl.ncmd++] = dp->dev;
+	if(floppycmd() < 0)
+		return -1;
+	floppywait(1);
+	if(fl.nstat < 2){
+		DPRINT("recalibrate: confused %ux\n", inb(Pmsr));
+		fl.confused = 1;
+		return -1;
+	}
+	if((fl.stat[0] & (Codemask|Seekend)) != Seekend){
+		DPRINT("recalibrate: failed\n");
+		dp->confused = 1;
+		return -1;
+	}
+	dp->cyl = fl.stat[1];
+	if(dp->cyl != 0){
+		DPRINT("recalibrate: wrong cylinder %d\n", dp->cyl);
+		dp->cyl = -1;
+		dp->confused = 1;
+		return -1;
+	}
+
+	dp->confused = 0;
+	return 0;
+}
+
+/*
+ *  if the controller or a specific drive is in a confused state,
+ *  reset it and get back to a known state
+ */
+static void
+floppyrevive(void)
+{
+	FDrive *dp;
+
+	/*
+	 *  reset the controller if it's confused
+	 */
+	if(fl.confused){
+		DPRINT("floppyrevive in\n");
+		fldump();
+
+		/* reset controller and turn all motors off */
+		splhi();
+		fl.ncmd = 1;
+		fl.cmd[0] = 0;
+		outb(Pdor, 0);
+		delay(10);
+		outb(Pdor, Fintena|Fena);
+		delay(10);
+		spllo();
+		fl.motor = 0;
+		fl.confused = 0;
+		floppywait(0);
+
+		/* mark all drives in an unknown state */
+		for(dp = fl.d; dp < &fl.d[fl.ndrive]; dp++)
+			dp->confused = 1;
+
+		/* set rate to a known value */
+		outb(Pdsr, 0);
+		fl.rate = 0;
+
+		DPRINT("floppyrevive out\n");
+		fldump();
+	}
+}
+
+/*
+ *  seek to the target cylinder
+ *
+ *	interrupt, no results
+ */
+static long
+floppyseek(FDrive *dp, long off)
+{
+	floppypos(dp, off);
+	if(dp->cyl == dp->tcyl)
+		return dp->tcyl;
+	dp->cyl = -1;
+
+	fl.ncmd = 0;
+	fl.cmd[fl.ncmd++] = Fseek;
+	fl.cmd[fl.ncmd++] = (dp->thead<<2) | dp->dev;
+	fl.cmd[fl.ncmd++] = dp->tcyl * dp->t->steps;
+	if(floppycmd() < 0)
+		return -1;
+	floppywait(1);
+	if(fl.nstat < 2){
+		DPRINT("seek: confused\n");
+		fl.confused = 1;
+		return -1;
+	}
+	if((fl.stat[0] & (Codemask|Seekend)) != Seekend){
+		DPRINT("seek: failed\n");
+		dp->confused = 1;
+		return -1;
+	}
+
+	dp->cyl = dp->tcyl;
+	return dp->tcyl;
+}
+
+/*
+ *  read or write to floppy.  try up to three times.
+ */
+static long
+floppyxfer(FDrive *dp, int cmd, void *a, long off, long n)
+{
+	long offset;
+	int tries;
+
+	if(off >= dp->t->cap)
+		return 0;
+	if(off + n > dp->t->cap)
+		n = dp->t->cap - off;
+
+	/* retry on error (until it gets ridiculous) */
+	tries = 0;
+	while(waserror()){
+		if(tries++ >= dp->maxtries)
+			nexterror();
+		DPRINT("floppyxfer: retrying\n");
+	}
+
+	dp->len = n;
+	if(floppyseek(dp, off) < 0){
+		DPRINT("xfer: seek failed\n");
+		dp->confused = 1;
+		error(Eio);
+	}
+
+	/*
+	 *  set up the dma (dp->len may be trimmed)
+	 */
+	if(waserror()){
+		dmaend(DMAchan);
+		nexterror();
+	}
+	dp->len = dmasetup(DMAchan, a, dp->len, cmd==Fread);
+	if(dp->len < 0)
+		error(Eio);
+
+	/*
+	 *  start operation
+	 */
+	fl.ncmd = 0;
+	fl.cmd[fl.ncmd++] = cmd | (dp->t->heads > 1 ? Fmulti : 0);
+	fl.cmd[fl.ncmd++] = (dp->thead<<2) | dp->dev;
+	fl.cmd[fl.ncmd++] = dp->tcyl;
+	fl.cmd[fl.ncmd++] = dp->thead;
+	fl.cmd[fl.ncmd++] = dp->tsec;
+	fl.cmd[fl.ncmd++] = dp->t->bcode;
+	fl.cmd[fl.ncmd++] = dp->t->sectors;
+	fl.cmd[fl.ncmd++] = dp->t->gpl;
+	fl.cmd[fl.ncmd++] = 0xFF;
+	if(floppycmd() < 0)
+		error(Eio);
+
+	/* Poll ready bits and transfer data */
+	floppyexec((char*)a, dp->len, cmd==Fread);
+
+	/*
+	 *  give bus to DMA, floppyintr() will read result
+	 */
+	floppywait(0);
+	dmaend(DMAchan);
+	poperror();
+
+	/*
+	 *  check for errors
+	 */
+	if(fl.nstat < 7){
+		DPRINT("xfer: confused\n");
+		fl.confused = 1;
+		error(Eio);
+	}
+	if((fl.stat[0] & Codemask)!=0 || fl.stat[1] || fl.stat[2]){
+		DPRINT("xfer: failed %ux %ux %ux\n", fl.stat[0],
+			fl.stat[1], fl.stat[2]);
+		DPRINT("offset %lud len %ld\n", off, dp->len);
+		if((fl.stat[0]&Codemask)==Cmdexec && fl.stat[1]==Overrun){
+			DPRINT("DMA overrun: retry\n");
+		} else
+			dp->confused = 1;
+		error(Eio);
+	}
+
+	/*
+	 *  check for correct cylinder
+	 */
+	offset = fl.stat[3] * dp->t->heads + fl.stat[4];
+	offset = offset*dp->t->sectors + fl.stat[5] - 1;
+	offset = offset * c2b[fl.stat[6]];
+	if(offset != off+dp->len){
+		DPRINT("xfer: ends on wrong cyl\n");
+		dp->confused = 1;
+		error(Eio);
+	}
+	poperror();
+
+	dp->lasttouched = m->ticks;
+	return dp->len;
+}
+
+/*
+ *  format a track
+ */
+static void
+floppyformat(FDrive *dp, Cmdbuf *cb)
+{
+ 	int cyl, h, sec;
+	ulong track;
+	uchar *buf, *bp;
+	FType *t;
+
+	/*
+	 *  set the type
+	 */
+	if(cb->nf == 2){
+		for(t = floppytype; t < &floppytype[nelem(floppytype)]; t++){
+			if(strcmp(cb->f[1], t->name)==0 && t->dt==dp->dt){
+				dp->t = t;
+				floppydir[1+NFDIR*dp->dev].length = dp->t->cap;
+				break;
+			}
+		}
+		if(t >= &floppytype[nelem(floppytype)])
+			error(Ebadarg);
+	} else if(cb->nf == 1){
+		floppysetdef(dp);
+		t = dp->t;
+	} else {
+		cmderror(cb, "invalid floppy format command");
+		SET(t);
+	}
+
+	/*
+	 *  buffer for per track info
+	 */
+	buf = smalloc(t->sectors*4);
+	if(waserror()){
+		free(buf);
+		nexterror();
+	}
+
+	/* force a recalibrate to cylinder 0 */
+	dp->confused = 1;
+	if(!waserror()){
+		floppyon(dp);
+		poperror();
+	}
+
+	/*
+	 *  format a track at time
+	 */
+	for(track = 0; track < t->tracks*t->heads; track++){
+		cyl = track/t->heads;
+		h = track % t->heads;
+
+		/*
+		 *  seek to track, ignore errors
+		 */
+		floppyseek(dp, track*t->tsize);
+		dp->cyl = cyl;
+		dp->confused = 0;
+
+		/*
+		 *  set up the dma (dp->len may be trimmed)
+		 */
+		bp = buf;
+		for(sec = 1; sec <= t->sectors; sec++){
+			*bp++ = cyl;
+			*bp++ = h;
+			*bp++ = sec;
+			*bp++ = t->bcode;
+		}
+		if(waserror()){
+			dmaend(DMAchan);
+			nexterror();
+		}
+		if(dmasetup(DMAchan, buf, bp-buf, 0) < 0)
+			error(Eio);
+
+		/*
+		 *  start operation
+		 */
+		fl.ncmd = 0;
+		fl.cmd[fl.ncmd++] = Fformat;
+		fl.cmd[fl.ncmd++] = (h<<2) | dp->dev;
+		fl.cmd[fl.ncmd++] = t->bcode;
+		fl.cmd[fl.ncmd++] = t->sectors;
+		fl.cmd[fl.ncmd++] = t->fgpl;
+		fl.cmd[fl.ncmd++] = 0x5a;
+		if(floppycmd() < 0)
+			error(Eio);
+
+		/* Poll ready bits and transfer data */
+		floppyexec((char *)buf, bp-buf, 0);
+
+		/*
+		 *  give bus to DMA, floppyintr() will read result
+		 */
+		floppywait(1);
+		dmaend(DMAchan);
+		poperror();
+
+		/*
+		 *  check for errors
+		 */
+		if(fl.nstat < 7){
+			DPRINT("format: confused\n");
+			fl.confused = 1;
+			error(Eio);
+		}
+		if((fl.stat[0]&Codemask)!=0 || fl.stat[1]|| fl.stat[2]){
+			DPRINT("format: failed %ux %ux %ux\n",
+				fl.stat[0], fl.stat[1], fl.stat[2]);
+			dp->confused = 1;
+			error(Eio);
+		}
+	}
+	free(buf);
+	dp->confused = 1;
+	poperror();
+}
+
+static void
+floppyintr(Ureg *)
+{
+	switch(fl.cmd[0]&~Fmulti){
+	case Fread:
+	case Fwrite:
+	case Fformat:
+	case Fdumpreg: 
+		floppyresult();
+		break;
+	case Fseek:
+	case Frecal:
+	default:
+		floppysense();	/* to clear interrupt */
+		break;
+	}
+	fl.ncmd = 0;
+	wakeup(&fl.r);
+}
+
+Dev floppydevtab = {
+	'f',
+	"floppy",
+
+	floppyreset,
+	devinit,
+	devshutdown,
+	floppyattach,
+	floppywalk,
+	floppystat,
+	floppyopen,
+	devcreate,
+	floppyclose,
+	floppyread,
+	devbread,
+	floppywrite,
+	devbwrite,
+	devremove,
+	devwstat,
+};
--- /dev/null
+++ b/os/pc/devi82365.c
@@ -1,0 +1,1044 @@
+#include "u.h"
+#include "../port/lib.h"
+#include "mem.h"
+#include "dat.h"
+#include "fns.h"
+#include "../port/error.h"
+#include "io.h"
+
+/*
+ *  Intel 82365SL PCIC controller and compatibles.
+ */
+enum
+{
+	/*
+	 *  registers indices
+	 */
+	Rid=		0x0,		/* identification and revision */
+	Ris=		0x1,		/* interface status */
+	Rpc=	 	0x2,		/* power control */
+	 Foutena=	 (1<<7),	/*  output enable */
+	 Fautopower=	 (1<<5),	/*  automatic power switching */
+	 Fcardena=	 (1<<4),	/*  PC card enable */
+	Rigc= 		0x3,		/* interrupt and general control */
+	 Fiocard=	 (1<<5),	/*  I/O card (vs memory) */
+	 Fnotreset=	 (1<<6),	/*  reset if not set */	
+	 FSMIena=	 (1<<4),	/*  enable change interrupt on SMI */ 
+	Rcsc= 		0x4,		/* card status change */
+	Rcscic= 	0x5,		/* card status change interrupt config */
+	 Fchangeena=	 (1<<3),	/*  card changed */
+	 Fbwarnena=	 (1<<1),	/*  card battery warning */
+	 Fbdeadena=	 (1<<0),	/*  card battery dead */
+	Rwe= 		0x6,		/* address window enable */
+	 Fmem16=	 (1<<5),	/*  use A23-A12 to decode address */
+	Rio= 		0x7,		/* I/O control */
+	 Fwidth16=	 (1<<0),	/*  16 bit data width */
+	 Fiocs16=	 (1<<1),	/*  IOCS16 determines data width */
+	 Fzerows=	 (1<<2),	/*  zero wait state */
+	 Ftiming=	 (1<<3),	/*  timing register to use */
+	Riobtm0lo=	0x8,		/* I/O address 0 start low byte */
+	Riobtm0hi=	0x9,		/* I/O address 0 start high byte */
+	Riotop0lo=	0xa,		/* I/O address 0 stop low byte */
+	Riotop0hi=	0xb,		/* I/O address 0 stop high byte */
+	Riobtm1lo=	0xc,		/* I/O address 1 start low byte */
+	Riobtm1hi=	0xd,		/* I/O address 1 start high byte */
+	Riotop1lo=	0xe,		/* I/O address 1 stop low byte */
+	Riotop1hi=	0xf,		/* I/O address 1 stop high byte */
+	Rmap=		0x10,		/* map 0 */
+
+	/*
+	 *  CL-PD67xx extension registers
+	 */
+	Rmisc1=		0x16,		/* misc control 1 */
+	 F5Vdetect=	 (1<<0),
+	 Fvcc3V=	 (1<<1),
+	 Fpmint=	 (1<<2),
+	 Fpsirq=	 (1<<3),
+	 Fspeaker=	 (1<<4),
+	 Finpack=	 (1<<7),
+	Rfifo=		0x17,		/* fifo control */
+	 Fflush=	 (1<<7),	/*  flush fifo */
+	Rmisc2=		0x1E,		/* misc control 2 */
+	 Flowpow=	 (1<<1),	/*  low power mode */
+	Rchipinfo=	0x1F,		/* chip information */
+	Ratactl=	0x26,		/* ATA control */
+
+	/*
+	 *  offsets into the system memory address maps
+	 */
+	Mbtmlo=		0x0,		/* System mem addr mapping start low byte */
+	Mbtmhi=		0x1,		/* System mem addr mapping start high byte */
+	 F16bit=	 (1<<7),	/*  16-bit wide data path */
+	Mtoplo=		0x2,		/* System mem addr mapping stop low byte */
+	Mtophi=		0x3,		/* System mem addr mapping stop high byte */
+	 Ftimer1=	 (1<<6),	/*  timer set 1 */
+	Mofflo=		0x4,		/* Card memory offset address low byte */
+	Moffhi=		0x5,		/* Card memory offset address high byte */
+	 Fregactive=	 (1<<6),	/*  attribute memory */
+
+	/*
+	 *  configuration registers - they start at an offset in attribute
+	 *  memory found in the CIS.
+	 */
+	Rconfig=	0,
+	 Creset=	 (1<<7),	/*  reset device */
+	 Clevel=	 (1<<6),	/*  level sensitive interrupt line */
+	 Cirq=		 (1<<2),	/*  IRQ enable */
+	 Cdecode=	 (1<<1),	/*  address decode */
+	 Cfunc=		 (1<<0),	/*  function enable */
+	Riobase0=	5,
+	Riobase1=	6,
+	Riosize=	9,
+};
+
+#define MAP(x,o)	(Rmap + (x)*0x8 + o)
+
+typedef struct I82365	I82365;
+
+/* a controller */
+enum
+{
+	Ti82365,
+	Tpd6710,
+	Tpd6720,
+	Tvg46x,
+};
+struct I82365
+{
+	int	type;
+	int	dev;
+	int	nslot;
+	int	xreg;		/* index register address */
+	int	dreg;		/* data register address */
+	int	irq;
+};
+static I82365 *controller[4];
+static int ncontroller;
+static PCMslot	*slot;
+static PCMslot	*lastslot;
+static nslot;
+
+static void	i82365intr(Ureg*, void*);
+static int	pcmio(int, ISAConf*);
+static long	pcmread(int, int, void*, long, vlong);
+static long	pcmwrite(int, int, void*, long, vlong);
+
+static void i82365dump(PCMslot*);
+
+/*
+ *  reading and writing card registers
+ */
+static uchar
+rdreg(PCMslot *pp, int index)
+{
+	outb(((I82365*)pp->cp)->xreg, pp->base + index);
+	return inb(((I82365*)pp->cp)->dreg);
+}
+static void
+wrreg(PCMslot *pp, int index, uchar val)
+{
+	outb(((I82365*)pp->cp)->xreg, pp->base + index);
+	outb(((I82365*)pp->cp)->dreg, val);
+}
+
+/*
+ *  get info about card
+ */
+static void
+slotinfo(PCMslot *pp)
+{
+	uchar isr;
+
+	isr = rdreg(pp, Ris);
+	pp->occupied = (isr & (3<<2)) == (3<<2);
+	pp->powered = isr & (1<<6);
+	pp->battery = (isr & 3) == 3;
+	pp->wrprot = isr & (1<<4);
+	pp->busy = isr & (1<<5);
+	pp->msec = TK2MS(MACHP(0)->ticks);
+}
+
+static int
+vcode(int volt)
+{
+	switch(volt){
+	case 5:
+		return 1;
+	case 12:
+		return 2;
+	default:
+		return 0;
+	}
+}
+
+/*
+ *  enable the slot card
+ */
+static void
+slotena(PCMslot *pp)
+{
+	if(pp->enabled)
+		return;
+
+	/* power up and unreset, wait's are empirical (???) */
+	wrreg(pp, Rpc, Fautopower|Foutena|Fcardena);
+	delay(300);
+	wrreg(pp, Rigc, 0);
+	delay(100);
+	wrreg(pp, Rigc, Fnotreset);
+	delay(5000);
+
+	/* get configuration */
+	slotinfo(pp);
+	if(pp->occupied){
+		pcmcisread(pp);
+		pp->enabled = 1;
+	} else
+		wrreg(pp, Rpc, Fautopower);
+}
+
+/*
+ *  disable the slot card
+ */
+static void
+slotdis(PCMslot *pp)
+{
+	wrreg(pp, Rpc, 0);	/* turn off card power */
+	wrreg(pp, Rwe, 0);	/* no windows */
+	pp->enabled = 0;
+}
+
+/*
+ *  status change interrupt
+ */
+static void
+i82365intr(Ureg *, void *)
+{
+	uchar csc, was;
+	PCMslot *pp;
+
+	if(slot == 0)
+		return;
+
+	for(pp = slot; pp < lastslot; pp++){
+		csc = rdreg(pp, Rcsc);
+		was = pp->occupied;
+		slotinfo(pp);
+		if(csc & (1<<3) && was != pp->occupied){
+			if(!pp->occupied)
+				slotdis(pp);
+		}
+	}
+}
+
+enum
+{
+	Mshift=	12,
+	Mgran=	(1<<Mshift),	/* granularity of maps */
+	Mmask=	~(Mgran-1),	/* mask for address bits important to the chip */
+};
+
+/*
+ *  get a map for pc card region, return corrected len
+ */
+PCMmap*
+pcmmap(int slotno, ulong offset, int len, int attr)
+{
+	PCMslot *pp;
+	uchar we, bit;
+	PCMmap *m, *nm;
+	int i;
+	ulong e;
+
+	pp = slot + slotno;
+	lock(&pp->mlock);
+
+	/* convert offset to granularity */
+	if(len <= 0)
+		len = 1;
+	e = ROUND(offset+len, Mgran);
+	offset &= Mmask;
+	len = e - offset;
+
+	/* look for a map that covers the right area */
+	we = rdreg(pp, Rwe);
+	bit = 1;
+	nm = 0;
+	for(m = pp->mmap; m < &pp->mmap[nelem(pp->mmap)]; m++){
+		if((we & bit))
+		if(m->attr == attr)
+		if(offset >= m->ca && e <= m->cea){
+
+			m->ref++;
+			unlock(&pp->mlock);
+			return m;
+		}
+		bit <<= 1;
+		if(nm == 0 && m->ref == 0)
+			nm = m;
+	}
+	m = nm;
+	if(m == 0){
+		unlock(&pp->mlock);
+		return 0;
+	}
+
+	/* if isa space isn't big enough, free it and get more */
+	if(m->len < len){
+		if(m->isa){
+			umbfree(m->isa, m->len);
+			m->len = 0;
+		}
+		m->isa = PADDR(umbmalloc(0, len, Mgran));
+		if(m->isa == 0){
+			print("pcmmap: out of isa space\n");
+			unlock(&pp->mlock);
+			return 0;
+		}
+		m->len = len;
+	}
+
+	/* set up new map */
+	m->ca = offset;
+	m->cea = m->ca + m->len;
+	m->attr = attr;
+	i = m-pp->mmap;
+	bit = 1<<i;
+	wrreg(pp, Rwe, we & ~bit);		/* disable map before changing it */
+	wrreg(pp, MAP(i, Mbtmlo), m->isa>>Mshift);
+	wrreg(pp, MAP(i, Mbtmhi), (m->isa>>(Mshift+8)) | F16bit);
+	wrreg(pp, MAP(i, Mtoplo), (m->isa+m->len-1)>>Mshift);
+	wrreg(pp, MAP(i, Mtophi), ((m->isa+m->len-1)>>(Mshift+8)));
+	offset -= m->isa;
+	offset &= (1<<25)-1;
+	offset >>= Mshift;
+	wrreg(pp, MAP(i, Mofflo), offset);
+	wrreg(pp, MAP(i, Moffhi), (offset>>8) | (attr ? Fregactive : 0));
+	wrreg(pp, Rwe, we | bit);		/* enable map */
+	m->ref = 1;
+
+	unlock(&pp->mlock);
+	return m;
+}
+
+void
+pcmunmap(int slotno, PCMmap* m)
+{
+	PCMslot *pp;
+
+	pp = slot + slotno;
+	lock(&pp->mlock);
+	m->ref--;
+	unlock(&pp->mlock);
+}
+
+static void
+increfp(PCMslot *pp)
+{
+	lock(pp);
+	if(pp->ref++ == 0)
+		slotena(pp);
+	unlock(pp);
+}
+
+static void
+decrefp(PCMslot *pp)
+{
+	lock(pp);
+	if(pp->ref-- == 1)
+		slotdis(pp);
+	unlock(pp);
+}
+
+/*
+ *  look for a card whose version contains 'idstr'
+ */
+static int
+pcmcia_pcmspecial(char *idstr, ISAConf *isa)
+{
+	PCMslot *pp;
+	extern char *strstr(char*, char*);
+	int enabled;
+
+	for(pp = slot; pp < lastslot; pp++){
+		if(pp->special)
+			continue;	/* already taken */
+
+		/*
+		 *  make sure we don't power on cards when we already know what's
+		 *  in them.  We'll reread every two minutes if necessary
+		 */
+		enabled = 0;
+		if (pp->msec == ~0 || TK2MS(MACHP(0)->ticks) - pp->msec > 120000){
+			increfp(pp);
+			enabled++;
+		}
+
+		if(pp->occupied) {
+			if(strstr(pp->verstr, idstr)){
+				if (!enabled){
+					enabled = 1;
+					increfp(pp);
+				}
+				if(isa == 0 || pcmio(pp->slotno, isa) == 0){
+					pp->special = 1;
+					return pp->slotno;
+				}
+			}
+		} else
+			pp->special = 1;
+		if (enabled)
+			decrefp(pp);
+	}
+	return -1;
+}
+
+static void
+pcmcia_pcmspecialclose(int slotno)
+{
+	PCMslot *pp;
+
+	if(slotno >= nslot)
+		panic("pcmspecialclose");
+	pp = slot + slotno;
+	pp->special = 0;
+	decrefp(pp);
+}
+
+enum
+{
+	Qdir,
+	Qmem,
+	Qattr,
+	Qctl,
+
+	Nents = 3,
+};
+
+#define SLOTNO(c)	((ulong)((c->qid.path>>8)&0xff))
+#define TYPE(c)	((ulong)(c->qid.path&0xff))
+#define QID(s,t)	(((s)<<8)|(t))
+
+static int
+pcmgen(Chan *c, char*, Dirtab *, int , int i, Dir *dp)
+{
+	int slotno;
+	Qid qid;
+	long len;
+	PCMslot *pp;
+
+	if(i == DEVDOTDOT){
+		mkqid(&qid, Qdir, 0, QTDIR);
+		devdir(c, qid, "#y", 0, eve, 0555, dp);
+		return 1;
+	}
+
+	if(i >= Nents*nslot)
+		return -1;
+	slotno = i/Nents;
+	pp = slot + slotno;
+	len = 0;
+	switch(i%Nents){
+	case 0:
+		qid.path = QID(slotno, Qmem);
+		snprint(up->genbuf, sizeof up->genbuf, "pcm%dmem", slotno);
+		len = pp->memlen;
+		break;
+	case 1:
+		qid.path = QID(slotno, Qattr);
+		snprint(up->genbuf, sizeof up->genbuf, "pcm%dattr", slotno);
+		len = pp->memlen;
+		break;
+	case 2:
+		qid.path = QID(slotno, Qctl);
+		snprint(up->genbuf, sizeof up->genbuf, "pcm%dctl", slotno);
+		break;
+	}
+	qid.vers = 0;
+	qid.type = QTFILE;
+	devdir(c, qid, up->genbuf, len, eve, 0660, dp);
+	return 1;
+}
+
+static char *chipname[] =
+{
+[Ti82365]	"Intel 82365SL",
+[Tpd6710]	"Cirrus Logic CL-PD6710",
+[Tpd6720]	"Cirrus Logic CL-PD6720",
+[Tvg46x]	"Vadem VG-46x",
+};
+
+static I82365*
+i82365probe(int x, int d, int dev)
+{
+	uchar c, id;
+	I82365 *cp;
+	ISAConf isa;
+	int i, nslot;
+
+	outb(x, Rid + (dev<<7));
+	id = inb(d);
+	if((id & 0xf0) != 0x80)
+		return 0;		/* not a memory & I/O card */
+	if((id & 0x0f) == 0x00)
+		return 0;		/* no revision number, not possible */
+
+	cp = xalloc(sizeof(I82365));
+	cp->xreg = x;
+	cp->dreg = d;
+	cp->dev = dev;
+	cp->type = Ti82365;
+	cp->nslot = 2;
+
+	switch(id){
+	case 0x82:
+	case 0x83:
+	case 0x84:
+		/* could be a cirrus */
+		outb(x, Rchipinfo + (dev<<7));
+		outb(d, 0);
+		c = inb(d);
+		if((c & 0xc0) != 0xc0)
+			break;
+		c = inb(d);
+		if((c & 0xc0) != 0x00)
+			break;
+		if(c & 0x20){
+			cp->type = Tpd6720;
+		} else {
+			cp->type = Tpd6710;
+			cp->nslot = 1;
+		}
+
+		/* low power mode */
+		outb(x, Rmisc2 + (dev<<7));
+		c = inb(d);
+		outb(d, c & ~Flowpow);
+		break;
+	}
+
+	/* if it's not a Cirrus, it could be a Vadem... */
+	if(cp->type == Ti82365){
+		/* unlock the Vadem extended regs */
+		outb(x, 0x0E + (dev<<7));
+		outb(x, 0x37 + (dev<<7));
+
+		/* make the id register show the Vadem id */
+		outb(x, 0x3A + (dev<<7));
+		c = inb(d);
+		outb(d, c|0xC0);
+		outb(x, Rid + (dev<<7));
+		c = inb(d);
+		if(c & 0x08)
+			cp->type = Tvg46x;
+
+		/* go back to Intel compatible id */
+		outb(x, 0x3A + (dev<<7));
+		c = inb(d);
+		outb(d, c & ~0xC0);
+	}
+
+	memset(&isa, 0, sizeof(ISAConf));
+	if(isaconfig("pcmcia", ncontroller, &isa) && isa.irq)
+		cp->irq = isa.irq;
+	else
+		cp->irq = IrqPCMCIA;
+
+	for(i = 0; i < isa.nopt; i++){
+		if(cistrncmp(isa.opt[i], "nslot=", 6))
+			continue;
+		nslot = strtol(&isa.opt[i][6], nil, 0);
+		if(nslot > 0 && nslot <= 2)
+			cp->nslot = nslot;
+	}
+
+	controller[ncontroller++] = cp;
+	return cp;
+}
+
+static void
+i82365dump(PCMslot *pp)
+{
+	int i;
+
+	for(i = 0; i < 0x40; i++){
+		if((i&0x0F) == 0)
+			print("\n%2.2uX:	", i);
+		print("%2.2uX ", rdreg(pp, i));
+		if(((i+1) & 0x0F) == 0x08)
+			print(" - ");
+	}
+	print("\n");
+}
+
+/*
+ *  set up for slot cards
+ */
+void
+devi82365link(void)
+{
+	static int already;
+	int i, j;
+	I82365 *cp;
+	PCMslot *pp;
+	char buf[32], *p;
+
+	if(already)
+		return;
+	already = 1;
+
+	if((p=getconf("pcmcia0")) && strncmp(p, "disabled", 8)==0)
+		return;
+
+	if(_pcmspecial)
+		return;
+	
+	/* look for controllers if the ports aren't already taken */
+	if(ioalloc(0x3E0, 2, 0, "i82365.0") >= 0){
+		i82365probe(0x3E0, 0x3E1, 0);
+		i82365probe(0x3E0, 0x3E1, 1);
+		if(ncontroller == 0)
+			iofree(0x3E0);
+	}
+	if(ioalloc(0x3E2, 2, 0, "i82365.1") >= 0){
+		i = ncontroller;
+		i82365probe(0x3E2, 0x3E3, 0);
+		i82365probe(0x3E2, 0x3E3, 1);
+		if(ncontroller == i)
+			iofree(0x3E2);
+	}
+
+	if(ncontroller == 0)
+		return;
+
+	_pcmspecial = pcmcia_pcmspecial;
+	_pcmspecialclose = pcmcia_pcmspecialclose;
+
+	for(i = 0; i < ncontroller; i++)
+		nslot += controller[i]->nslot;
+	slot = xalloc(nslot * sizeof(PCMslot));
+
+	lastslot = slot;
+	for(i = 0; i < ncontroller; i++){
+		cp = controller[i];
+		print("#y%d: %d slot %s: port 0x%uX irq %d\n",
+			i, cp->nslot, chipname[cp->type], cp->xreg, cp->irq);
+		for(j = 0; j < cp->nslot; j++){
+			pp = lastslot++;
+			pp->slotno = pp - slot;
+			pp->memlen = 64*MB;
+			pp->base = (cp->dev<<7) | (j<<6);
+			pp->cp = cp;
+			pp->msec = ~0;
+			pp->verstr[0] = 0;
+			slotdis(pp);
+
+			/* interrupt on status change */
+			wrreg(pp, Rcscic, (cp->irq<<4) | Fchangeena);
+			rdreg(pp, Rcsc);
+		}
+
+		/* for card management interrupts */
+		snprint(buf, sizeof buf, "i82365.%d", i);
+		intrenable(cp->irq, i82365intr, 0, BUSUNKNOWN, buf);
+	}
+}
+
+static Chan*
+i82365attach(char *spec)
+{
+	return devattach('y', spec);
+}
+
+static Walkqid*
+i82365walk(Chan *c, Chan *nc, char **name, int nname)
+{
+	return devwalk(c, nc, name, nname, 0, 0, pcmgen);
+}
+
+static int
+i82365stat(Chan *c, uchar *db, int n)
+{
+	return devstat(c, db, n, 0, 0, pcmgen);
+}
+
+static Chan*
+i82365open(Chan *c, int omode)
+{
+	if(c->qid.type & QTDIR){
+		if(omode != OREAD)
+			error(Eperm);
+	} else
+		increfp(slot + SLOTNO(c));
+	c->mode = openmode(omode);
+	c->flag |= COPEN;
+	c->offset = 0;
+	return c;
+}
+
+static void
+i82365close(Chan *c)
+{
+	if(c->flag & COPEN)
+		if((c->qid.type & QTDIR) == 0)
+			decrefp(slot+SLOTNO(c));
+}
+
+/* a memmove using only bytes */
+static void
+memmoveb(uchar *to, uchar *from, int n)
+{
+	while(n-- > 0)
+		*to++ = *from++;
+}
+
+/* a memmove using only shorts & bytes */
+static void
+memmoves(uchar *to, uchar *from, int n)
+{
+	ushort *t, *f;
+
+	if((((ulong)to) & 1) || (((ulong)from) & 1) || (n & 1)){
+		while(n-- > 0)
+			*to++ = *from++;
+	} else {
+		n = n/2;
+		t = (ushort*)to;
+		f = (ushort*)from;
+		while(n-- > 0)
+			*t++ = *f++;
+	}
+}
+
+static long
+pcmread(int slotno, int attr, void *a, long n, vlong off)
+{
+	int i, len;
+	PCMmap *m;
+	uchar *ac;
+	PCMslot *pp;
+	ulong offset = off;
+
+	pp = slot + slotno;
+	if(pp->memlen < offset)
+		return 0;
+	if(pp->memlen < offset + n)
+		n = pp->memlen - offset;
+
+	m = 0;
+	if(waserror()){
+		if(m)
+			pcmunmap(pp->slotno, m);
+		nexterror();
+	}
+
+	ac = a;
+	for(len = n; len > 0; len -= i){
+		m = pcmmap(pp->slotno, offset, 0, attr);
+		if(m == 0)
+			error("cannot map PCMCIA card");
+		if(offset + len > m->cea)
+			i = m->cea - offset;
+		else
+			i = len;
+		memmoveb(ac, KADDR(m->isa + offset - m->ca), i);
+		pcmunmap(pp->slotno, m);
+		offset += i;
+		ac += i;
+	}
+
+	poperror();
+	return n;
+}
+
+static long
+i82365read(Chan *c, void *a, long n, vlong off)
+{
+	char *p, *buf, *e;
+	PCMslot *pp;
+	ulong offset = off;
+
+	switch(TYPE(c)){
+	case Qdir:
+		return devdirread(c, a, n, 0, 0, pcmgen);
+	case Qmem:
+	case Qattr:
+		return pcmread(SLOTNO(c), TYPE(c) == Qattr, a, n, off);
+	case Qctl:
+		buf = p = malloc(READSTR);
+		e = p + READSTR;
+		pp = slot + SLOTNO(c);
+
+		buf[0] = 0;
+		if(pp->occupied){
+			p = seprint(p, e, "occupied\n");
+			if(pp->verstr[0])
+				p = seprint(p, e, "version %s\n", pp->verstr);
+		}
+		if(pp->enabled)
+			p = seprint(p, e, "enabled\n");
+		if(pp->powered)
+			p = seprint(p, e, "powered\n");
+		if(pp->configed)
+			p = seprint(p, e, "configed\n");
+		if(pp->wrprot)
+			p = seprint(p, e, "write protected\n");
+		if(pp->busy)
+			p = seprint(p, e, "busy\n");
+		seprint(p, e, "battery lvl %d\n", pp->battery);
+
+		n = readstr(offset, a, n, buf);
+		free(buf);
+
+		return n;
+	}
+	error(Ebadarg);
+	return -1;	/* not reached */
+}
+
+static long
+pcmwrite(int dev, int attr, void *a, long n, vlong off)
+{
+	int i, len;
+	PCMmap *m;
+	uchar *ac;
+	PCMslot *pp;
+	ulong offset = off;
+
+	pp = slot + dev;
+	if(pp->memlen < offset)
+		return 0;
+	if(pp->memlen < offset + n)
+		n = pp->memlen - offset;
+
+	m = 0;
+	if(waserror()){
+		if(m)
+			pcmunmap(pp->slotno, m);
+		nexterror();
+	}
+
+	ac = a;
+	for(len = n; len > 0; len -= i){
+		m = pcmmap(pp->slotno, offset, 0, attr);
+		if(m == 0)
+			error("cannot map PCMCIA card");
+		if(offset + len > m->cea)
+			i = m->cea - offset;
+		else
+			i = len;
+		memmoveb(KADDR(m->isa + offset - m->ca), ac, i);
+		pcmunmap(pp->slotno, m);
+		offset += i;
+		ac += i;
+	}
+
+	poperror();
+	return n;
+}
+
+static long
+i82365write(Chan *c, void *a, long n, vlong off)
+{
+	PCMslot *pp;
+	char buf[32];
+
+	switch(TYPE(c)){
+	case Qctl:
+		if(n >= sizeof(buf))
+			n = sizeof(buf) - 1;
+		strncpy(buf, a, n);
+		buf[n] = 0;
+		pp = slot + SLOTNO(c);
+		if(!pp->occupied)
+			error(Eio);
+
+		/* set vpp on card */
+		if(strncmp(buf, "vpp", 3) == 0)
+			wrreg(pp, Rpc, vcode(atoi(buf+3))|Fautopower|Foutena|Fcardena);
+		return n;
+	case Qmem:
+	case Qattr:
+		pp = slot + SLOTNO(c);
+		if(pp->occupied == 0 || pp->enabled == 0)
+			error(Eio);
+		n = pcmwrite(pp->slotno, TYPE(c) == Qattr, a, n, off);
+		if(n < 0)
+			error(Eio);
+		return n;
+	}
+	error(Ebadarg);
+	return -1;	/* not reached */
+}
+
+Dev i82365devtab = {
+	'y',
+	"i82365",
+
+	devreset,
+	devinit,
+	devshutdown,
+	i82365attach,
+	i82365walk,
+	i82365stat,
+	i82365open,
+	devcreate,
+	i82365close,
+	i82365read,
+	devbread,
+	i82365write,
+	devbwrite,
+	devremove,
+	devwstat,
+};
+
+/*
+ *  configure the PCMslot for IO.  We assume very heavily that we can read
+ *  configuration info from the CIS.  If not, we won't set up correctly.
+ */
+static int
+pcmio(int slotno, ISAConf *isa)
+{
+	uchar we, x, *p;
+	PCMslot *pp;
+	PCMconftab *ct, *et, *t;
+	PCMmap *m;
+	int i, index, irq;
+	char *cp;
+
+	irq = isa->irq;
+	if(irq == 2)
+		irq = 9;
+
+	if(slotno > nslot)
+		return -1;
+	pp = slot + slotno;
+
+	if(!pp->occupied)
+		return -1;
+
+	et = &pp->ctab[pp->nctab];
+
+	ct = 0;
+	for(i = 0; i < isa->nopt; i++){
+		if(strncmp(isa->opt[i], "index=", 6))
+			continue;
+		index = strtol(&isa->opt[i][6], &cp, 0);
+		if(cp == &isa->opt[i][6] || index >= pp->nctab)
+			return -1;
+		ct = &pp->ctab[index];
+	}
+
+	if(ct == 0){
+		/* assume default is right */
+		if(pp->def)
+			ct = pp->def;
+		else
+			ct = pp->ctab;
+	
+		/* try for best match */
+		if(ct->nio == 0
+		|| ct->io[0].start != isa->port || ((1<<irq) & ct->irqs) == 0){
+			for(t = pp->ctab; t < et; t++)
+				if(t->nio
+				&& t->io[0].start == isa->port
+				&& ((1<<irq) & t->irqs)){
+					ct = t;
+					break;
+				}
+		}
+		if(ct->nio == 0 || ((1<<irq) & ct->irqs) == 0){
+			for(t = pp->ctab; t < et; t++)
+				if(t->nio && ((1<<irq) & t->irqs)){
+					ct = t;
+					break;
+				}
+		}
+		if(ct->nio == 0){
+			for(t = pp->ctab; t < et; t++)
+				if(t->nio){
+					ct = t;
+					break;
+				}
+		}
+	}
+
+	if(ct == et || ct->nio == 0)
+		return -1;
+	if(isa->port == 0 && ct->io[0].start == 0)
+		return -1;
+
+	/* route interrupts */
+	isa->irq = irq;
+	wrreg(pp, Rigc, irq | Fnotreset | Fiocard);
+
+	/* set power and enable device */
+	x = vcode(ct->vpp1);
+	wrreg(pp, Rpc, x|Fautopower|Foutena|Fcardena);
+
+	/* 16-bit data path */
+	if(ct->bit16)
+		x = Ftiming|Fiocs16|Fwidth16;
+	else
+		x = Ftiming;
+	if(ct->nio == 2 && ct->io[1].start)
+		x |= x<<4;
+	wrreg(pp, Rio, x);
+
+	/*
+	 * enable io port map 0
+	 * the 'top' register value includes the last valid address
+	 */
+	if(isa->port == 0)
+		isa->port = ct->io[0].start;
+	we = rdreg(pp, Rwe);
+	wrreg(pp, Riobtm0lo, isa->port);
+	wrreg(pp, Riobtm0hi, isa->port>>8);
+	i = isa->port+ct->io[0].len-1;
+	wrreg(pp, Riotop0lo, i);
+	wrreg(pp, Riotop0hi, i>>8);
+	we |= 1<<6;
+	if(ct->nio >= 2 && ct->io[1].start){
+		wrreg(pp, Riobtm1lo, ct->io[1].start);
+		wrreg(pp, Riobtm1hi, ct->io[1].start>>8);
+		i = ct->io[1].start+ct->io[1].len-1;
+		wrreg(pp, Riotop1lo, i);
+		wrreg(pp, Riotop1hi, i>>8);
+		we |= 1<<7;
+	}
+	wrreg(pp, Rwe, we);
+
+	/* only touch Rconfig if it is present */
+	m = pcmmap(slotno, pp->cfg[0].caddr + Rconfig, 0x20, 1);
+	p = KADDR(m->isa + pp->cfg[0].caddr - m->ca);
+	if(pp->cfg[0].cpresent & (1<<Rconfig)){
+		/*  Reset adapter */
+
+		/*  set configuration and interrupt type.
+		 *  if level is possible on the card, use it.
+		 */
+		x = ct->index;
+		if(ct->irqtype & 0x20)
+			x |= Clevel;
+
+		/*  enable the device, enable address decode and
+		 *  irq enable.
+		 */
+		x |= Cfunc|Cdecode|Cirq;
+
+		p[0] = x;
+		//delay(5);
+		microdelay(40);
+	}
+
+	if(pp->cfg[0].cpresent & (1<<Riobase0)){
+		/* set up the iobase 0 */
+		p[Riobase0 << 1] = isa->port;
+		p[Riobase1 << 1] = isa->port >> 8;
+	}
+
+	if(pp->cfg[0].cpresent & (1<<Riosize))
+		p[Riosize << 1] = ct->io[0].len;
+	pcmunmap(slotno, m);
+	return 0;
+}
--- /dev/null
+++ b/os/pc/devlm78.c
@@ -1,0 +1,346 @@
+#include "u.h"
+#include "../port/lib.h"
+#include "mem.h"
+#include "dat.h"
+#include "fns.h"
+#include "io.h"
+#include "ureg.h"
+#include "../port/error.h"
+
+/* this driver doesn't implement the management interrupts.  we
+ * leave the LM78 interrupts set to whatever the BIOS did.  we do
+ * allow reading and writing the the readouts and alarm values.
+ * Read(2)ing or write(2)ing at offset 0x0-0x1f, is
+ * equivalent to reading or writing lm78 registers 0x20-0x3f.
+ */
+enum
+{
+	/*  address of chip on serial interface */
+	Serialaddr=	0x2d,
+
+	/*  parallel access registers */
+	Rpaddr=		0x5,
+	Bbusy=		 (1<<7),
+	Rpdata=		0x6,
+
+	/*  internal register addresses */
+	Rconfig=	0x40,
+	Bstart=		 (1<<0),
+	Bsmiena=	 (1<<1),
+	Birqena=	 (1<<2),
+	Bintclr=	 (1<<3),
+	Breset=		 (1<<4),
+	Bnmi=		 (1<<5),	/*  if set, use nmi, else irq */
+	Bpowbypass=	 (1<<6),
+	Binit=		 (1<<7),
+	Ristat1=	0x41,
+	Ristat2=	0x42,
+	Rsmimask1=	0x43,
+	Rsmimask2=	0x44,
+	Rnmimask1=	0x45,
+	Rnmimask2=	0x46,
+	Rvidfan=	0x47,		/*  set fan counter, and read voltage level */
+	Mvid=		 0x0f,
+	Mfan=		 0xf0,
+	Raddr=		0x48,		/*  address used on serial bus */
+	Rresetid=	0x49,		/*  chip reset and ID register */
+	Rpost=		0x00,		/*  start of post ram */
+	Rvalue=		0x20,		/*  start of value ram */
+
+	VRsize=		0x20,		/*  size of value ram */
+};
+
+enum
+{
+	Qdir,
+	Qlm78vram,
+};
+
+static Dirtab lm78dir[] = {
+	".",			{ Qdir, 0, QTDIR},	0,	0555,
+	"lm78vram",	{ Qlm78vram, 0 },	0,	0444,
+};
+
+/*  interface type */
+enum
+{
+	None=	0,
+	Smbus,
+	Parallel,
+};
+
+static struct {
+	QLock;
+	int	probed;
+	int 	ifc;	/*  which interface is connected */
+	SMBus	*smbus;	/*  serial interface */
+	int	port;	/*  parallel interface */
+} lm78;
+
+extern SMBus*	piix4smbus(void);
+
+/*  wait for device to become quiescent and then set the */
+/*  register address */
+static void
+setreg(int reg)
+{
+	int tries;
+
+	for(tries = 0; tries < 1000000; tries++)
+		if((inb(lm78.port+Rpaddr) & Bbusy) == 0){
+			outb(lm78.port+Rpaddr, reg);
+			return;
+		}
+	error("lm78 broken");
+}
+
+/*  routines that actually touch the device */
+static void
+lm78wrreg(int reg, uchar val)
+{
+	if(waserror()){
+		qunlock(&lm78);
+		nexterror();
+	}
+	qlock(&lm78);
+
+	switch(lm78.ifc){
+	case Smbus:
+		lm78.smbus->transact(lm78.smbus, SMBbytewrite, Serialaddr, reg, &val);
+		break;
+	case Parallel:
+		setreg(reg);
+		outb(lm78.port+Rpdata, val);
+		break;
+	default:
+		error(Enodev);
+		break;
+	}
+
+	qunlock(&lm78);
+	poperror();
+}
+
+static int
+lm78rdreg(int reg)
+{
+	uchar val;
+
+	if(waserror()){
+		qunlock(&lm78);
+		nexterror();
+	}
+	qlock(&lm78);
+
+	switch(lm78.ifc){
+	case Smbus:
+		lm78.smbus->transact(lm78.smbus, SMBsend, Serialaddr, reg, nil);
+		lm78.smbus->transact(lm78.smbus, SMBrecv, Serialaddr, 0, &val);
+		break;
+	case Parallel:
+		setreg(reg);
+		val = inb(lm78.port+Rpdata);
+		break;
+	default:
+		error(Enodev);
+		break;
+	}
+
+	qunlock(&lm78);
+	poperror();
+	return val;
+}
+
+/*  start the chip monitoring but don't change any smi 
+ *  interrupts and/or alarms that the BIOS may have set up. 
+ *  this isn't locked because it's thought to be idempotent 
+ */
+static void
+lm78enable(void)
+{
+	uchar config;
+
+	if(lm78.ifc == None)
+		error(Enodev);
+
+	if(lm78.probed == 0){
+		/*  make sure its really there */
+		if(lm78rdreg(Raddr) != Serialaddr){
+			lm78.ifc = None;
+			error(Enodev);
+		} else {
+			/*  start the sampling */
+			config = lm78rdreg(Rconfig);
+			config = (config | Bstart) & ~(Bintclr|Binit);
+			lm78wrreg(Rconfig, config);
+pprint("Rvidfan %2.2ux\n", lm78rdreg(Rconfig), lm78rdreg(Rvidfan));
+		}
+		lm78.probed = 1;
+	}
+}
+
+enum
+{
+	IntelVendID=	0x8086,
+	PiixID=		0x122E,
+	Piix3ID=	0x7000,
+
+	Piix4PMID=	0x7113,		/*  PIIX4 power management function */
+
+	PCSC=		0x78,		/*  programmable chip select control register */
+	PCSC8bytes=	0x01,
+};
+
+/*  figure out what kind of interface we could have */
+void
+lm78reset(void)
+{
+	int pcs;
+	Pcidev *p;
+
+	lm78.ifc = None;
+	p = nil;
+	while((p = pcimatch(p, IntelVendID, 0)) != nil){
+		switch(p->did){
+		/*  these bridges use the PCSC to map the lm78 into port space. */
+		/*  for this case the lm78's CS# select is connected to the PIIX's */
+		/*  PCS# output and the bottom 3 bits of address are passed to the */
+		/*  LM78's A0-A2 inputs. */
+		case PiixID:
+		case Piix3ID:
+			pcs = pcicfgr16(p, PCSC);
+			if(pcs & 3) {
+				/* already enabled */
+				lm78.port = pcs & ~3;
+				lm78.ifc = Parallel;
+				return;	
+			}
+
+			/*  enable the chip, use default address 0x50 */
+			pcicfgw16(p, PCSC, 0x50|PCSC8bytes);
+			pcs = pcicfgr16(p, PCSC);
+			lm78.port = pcs & ~3;
+			lm78.ifc = Parallel;
+			return;
+
+		/*  this bridge puts the lm78's serial interface on the smbus */
+		case Piix4PMID:
+			lm78.smbus = piix4smbus();
+			if(lm78.smbus == nil)
+				continue;
+			print("found piix4 smbus, base %lud\n", lm78.smbus->base);
+			lm78.ifc = Smbus;
+			return;
+		}
+	}
+}
+
+Walkqid *
+lm78walk(Chan* c, Chan *nc, char** name, int nname)
+{
+	return devwalk(c, nc, name, nname, lm78dir, nelem(lm78dir), devgen);
+}
+
+static int
+lm78stat(Chan* c, uchar* dp, int n)
+{
+	return devstat(c, dp, n, lm78dir, nelem(lm78dir), devgen);
+}
+
+static Chan*
+lm78open(Chan* c, int omode)
+{
+	return devopen(c, omode, lm78dir, nelem(lm78dir), devgen);
+}
+
+static void
+lm78close(Chan*)
+{
+}
+
+enum
+{
+	Linelen= 25,
+};
+
+static long
+lm78read(Chan *c, void *a, long n, vlong offset)
+{
+	uchar *va = a;
+	int off, e;
+
+	off = offset;
+
+	switch((ulong)c->qid.path){
+	case Qdir:
+		return devdirread(c, a, n, lm78dir, nelem(lm78dir), devgen);
+
+	case Qlm78vram:
+		if(off >=  VRsize)
+			return 0;
+		e = off + n;
+		if(e > VRsize)
+			e = VRsize;
+		for(; off < e; off++)
+			*va++ = lm78rdreg(Rvalue+off);
+		return (int)(va - (uchar*)a);
+	}
+	return 0;
+}
+
+static long
+lm78write(Chan *c, void *a, long n, vlong offset)
+{
+	uchar *va = a;
+	int off, e;
+
+	off = offset;
+
+	switch((ulong)c->qid.path){
+	default:
+		error(Eperm);
+
+	case Qlm78vram:
+		if(off >=  VRsize)
+			return 0;
+		e = off + n;
+		if(e > VRsize)
+			e = VRsize;
+		for(; off < e; off++)
+			lm78wrreg(Rvalue+off, *va++);
+		return va - (uchar*)a;
+	}
+	return 0;
+}
+
+extern Dev lm78devtab;
+
+static Chan*
+lm78attach(char* spec)
+{
+	lm78enable();
+
+	return devattach(lm78devtab.dc, spec);
+}
+
+Dev lm78devtab = {
+	'T',
+	"lm78",
+
+	lm78reset,
+	devinit,
+	devshutdown,
+	lm78attach,
+	lm78walk,
+	lm78stat,
+	lm78open,
+	devcreate,
+	lm78close,
+	lm78read,
+	devbread,
+	lm78write,
+	devbwrite,
+	devremove,
+	devwstat,
+};
+
--- /dev/null
+++ b/os/pc/devlpt.c
@@ -1,0 +1,245 @@
+#include	"u.h"
+#include	"../port/lib.h"
+#include	"mem.h"
+#include	"dat.h"
+#include	"fns.h"
+#include	"io.h"
+#include	"../port/error.h"
+
+/* Centronix parallel (printer) port */
+
+/* base addresses */
+static int lptbase[] = {
+	0x378,	/* lpt1 */
+	0x3bc,	/* lpt2 */
+	0x278	/* lpt3 (sic) */
+};
+#define NDEV	nelem(lptbase)
+static int lptallocd[NDEV];
+
+/* offsets, and bits in the registers */
+enum
+{
+	Qdir=		0x8000,
+	/* data latch register */
+	Qdlr=		0x0,
+	/* printer status register */
+	Qpsr=		0x1,
+	Fnotbusy=	0x80,
+	Fack=		0x40,
+	Fpe=		0x20,
+	Fselect=	0x10,
+	Fnoerror=	0x08,
+	/* printer control register */
+	Qpcr=		0x2,
+	Fie=		0x10,
+	Fselectin=	0x08,
+	Finitbar=	0x04,
+	Faf=		0x02,
+	Fstrobe=	0x01,
+	/* fake `data register' */
+	Qdata=		0x3,
+};
+
+static int	lptready(void*);
+static void	outch(int, int);
+static void	lptintr(Ureg*, void*);
+
+static Rendez	lptrendez;
+
+Dirtab lptdir[]={
+	".",	{Qdir, 0, QTDIR},	0,	DMDIR|0555,
+	"dlr",	{Qdlr},			1,	0666,
+	"psr",	{Qpsr},			5,	0444,
+	"pcr",	{Qpcr},			0,	0222,
+	"data",	{Qdata},		0,	0222,
+};
+
+static int
+lptgen(Chan *c, char*, Dirtab *tab, int ntab, int i, Dir *dp)
+{
+	Qid qid;
+
+	if(i == DEVDOTDOT){
+		mkqid(&qid, Qdir, 0, QTDIR);
+		devdir(c, qid, ".", 0, eve, 0555, dp);
+		return 1;
+	}
+	i++; /* skip first element for . itself */
+	if(tab==0 || i>=ntab)
+		return -1;
+	tab += i;
+	qid = tab->qid;
+	qid.path &= ~Qdir;
+	if(qid.path < Qdata)
+		qid.path += lptbase[c->dev];
+	qid.vers = c->dev;
+	sprint(up->genbuf, "lpt%lud%s", c->dev+1, tab->name);
+	devdir(c, qid, up->genbuf, tab->length, eve, tab->perm, dp);
+	return 1;
+}
+
+static Chan*
+lptattach(char *spec)
+{
+	Chan *c;
+	int i  = (spec && *spec) ? strtol(spec, 0, 0) : 1;
+	char name[5];
+	static int set;
+
+	if(!set){
+		outb(lptbase[i-1]+Qpcr, 0);	/* turn off interrupts */
+		set = 1;
+		intrenable(IrqLPT, lptintr, 0, BUSUNKNOWN, "lpt");
+	}
+	if(i < 1 || i > NDEV)
+		error(Ebadarg);
+	if(lptallocd[i-1] == 0){
+		int ecr;
+		sprint(name, "lpt%d", i-1);
+		if(ioalloc(lptbase[i-1], 3, 0, name) < 0)
+			error("lpt port space in use");
+		lptallocd[i-1] = 1;
+		// Detect ECP - if found, put into PS/2 mode to suit style of driver
+		ecr = lptbase[i-1] + 0x402;
+		if ((inb(ecr) & 3) == 1) {
+			outb(ecr, 0x34);
+			if (inb(ecr) == 0x35) {
+				outb(ecr, (inb(ecr) & 0x1f) | (1 << 5));
+				if(ioalloc(ecr, 1, 0, name) < 0)
+					error("lpt ecr port space in use");
+			}
+		}
+	}
+	c = devattach('L', spec);
+	c->qid.path = Qdir;
+	c->dev = i-1;
+	return c;
+}
+
+static Walkqid*
+lptwalk(Chan *c, Chan *nc, char **name, int nname)
+{
+	return devwalk(c, nc, name, nname, lptdir, nelem(lptdir), lptgen);
+}
+
+static int
+lptstat(Chan *c, uchar *dp, int n)
+{
+	return devstat(c, dp, n, lptdir, nelem(lptdir), lptgen);
+}
+
+static Chan*
+lptopen(Chan *c, int omode)
+{
+	return devopen(c, omode, lptdir, nelem(lptdir), lptgen);
+}
+
+static void
+lptclose(Chan *)
+{
+}
+
+static long
+lptread(Chan *c, void *a, long n, vlong)
+{
+	char str[16];
+	int size;
+	ulong o;
+
+	if(c->qid.path == Qdir)
+		return devdirread(c, a, n, lptdir, nelem(lptdir), lptgen);
+	size = sprint(str, "0x%2.2ux\n", inb(c->qid.path));
+	o = c->offset;
+	if(o >= size)
+		return 0;
+	if(o+n > size)
+		n = size-c->offset;
+	memmove(a, str+o, n);
+	return n;
+}
+
+static long
+lptwrite(Chan *c, void *a, long n, vlong)
+{
+	char str[16], *p;
+	long base, k;
+
+	if(n <= 0)
+		return 0;
+	if(c->qid.path != Qdata){
+		if(n > sizeof str-1)
+			n = sizeof str-1;
+		memmove(str, a, n);
+		str[n] = 0;
+		outb(c->qid.path, strtoul(str, 0, 0));
+		return n;
+	}
+	p = a;
+	k = n;
+	base = lptbase[c->dev];
+	if(waserror()){
+		outb(base+Qpcr, Finitbar);
+		nexterror();
+	}
+	while(--k >= 0)
+		outch(base, *p++);
+	poperror();
+	return n;
+}
+
+static void
+outch(int base, int c)
+{
+	int status, tries;
+
+	for(tries=0;; tries++) {
+		status = inb(base+Qpsr);
+		if(status&Fnotbusy)
+			break;
+		if((status&Fpe)==0 && (status&(Fselect|Fnoerror)) != (Fselect|Fnoerror))
+			error(Eio);
+		if(tries < 10)
+			tsleep(&lptrendez, return0, nil, 1);
+		else {
+			outb(base+Qpcr, Finitbar|Fie);
+			tsleep(&lptrendez, lptready, (void *)base, 100);
+		}
+	}
+	outb(base+Qdlr, c);
+	outb(base+Qpcr, Finitbar|Fstrobe);
+	outb(base+Qpcr, Finitbar);
+}
+
+static int
+lptready(void *base)
+{
+	return inb((int)base+Qpsr)&Fnotbusy;
+}
+
+static void
+lptintr(Ureg *, void *)
+{
+	wakeup(&lptrendez);
+}
+
+Dev lptdevtab = {
+	'L',
+	"lpt",
+
+	devreset,
+	devinit,
+	devshutdown,
+	lptattach,
+	lptwalk,
+	lptstat,
+	lptopen,
+	devcreate,
+	lptclose,
+	lptread,
+	devbread,
+	lptwrite,
+	devbwrite,
+	devremove,
+	devwstat,
+};
--- /dev/null
+++ b/os/pc/devmouse.c
@@ -1,0 +1,672 @@
+#include	"u.h"
+#include	"../port/lib.h"
+#include	"mem.h"
+#include	"dat.h"
+#include	"fns.h"
+#include	"io.h"
+#include	"../port/error.h"
+
+/*
+ * TODO
+ * - shift key should modify right button with non-serial mice
+ * + intellimouse implementation
+ * - acceleration for all mouse types
+ * + spurious interrupt 7 after probing for ps2 mouse for the first time...?
+ * - test with ms busmouse
+ * - test with logitech serial mouse
+ */
+
+/*
+ *  mouse types
+ */
+enum
+{
+	Mouseother,
+	Mouseserial,
+	MousePS2,
+	Mousebus,
+	Mouseintelli,
+	Mousemsbus,
+};
+
+static int mousetype;
+static int mouseswap;
+static int mouseport;		/* port for serial mice, irq for bus mice */
+static int mousesubtype;
+static int accelerated;
+static QLock mouselock;
+
+static int msbusmousedetect(void);
+static int busmousedetect(void);
+static void mousectl(char *buf);
+static void mouseprobe(char *buf, int len);
+static void mousestatus(char *buf, int len);
+
+enum{
+	Qdir,
+	Qmousectl,
+	Qmouseprobe,
+};
+
+static
+Dirtab mousetab[]={
+	"mousectl",		{Qmousectl, 0},	0,	0600,
+	"mouseprobe",	{Qmouseprobe, 0}, 0, 0400,
+};
+
+static Chan*
+mouseattach(char* spec)
+{
+	return devattach('m', spec);
+}
+
+static int
+mousewalk(Chan* c, char* name)
+{
+	return devwalk(c, name, mousetab, nelem(mousetab), devgen);
+}
+
+static void
+mousestat(Chan* c, char* db)
+{
+	devstat(c, db, mousetab, nelem(mousetab), devgen);
+}
+
+static Chan*
+mouseopen(Chan* c, int omode)
+{
+	return devopen(c, omode, mousetab, nelem(mousetab), devgen);
+}
+
+static void
+mouseclose(Chan* c)
+{
+	USED(c);
+}
+
+static long
+mouseread(Chan* c, void* a, long n, vlong offset)
+{
+	char buf[64];
+	USED(offset);
+
+	switch(c->qid.path & ~CHDIR){
+	case Qdir:
+		return devdirread(c, a, n, mousetab, nelem(mousetab), devgen);
+	case Qmousectl:
+		qlock(&mouselock);
+		mousestatus(buf, sizeof(buf));
+		qunlock(&mouselock);
+		n = readstr(offset, a, n, buf);
+		break;
+	case Qmouseprobe:
+		if (mousetype)
+			error(Emouseset);
+		mouseprobe(buf, sizeof(buf));
+		n = readstr(offset, a, n, buf);
+		break;
+	default:
+		n=0;
+		break;
+	}
+	return n;
+}
+
+static long
+mousewrite(Chan* c, void *a, long n, vlong)
+{
+	char buf[64];
+	if ((c->qid.path & ~CHDIR) != Qmousectl)
+		error(Ebadusefd);
+	if (n >= sizeof(buf))
+		n = sizeof(buf) - 1;
+	strncpy(buf, a, n);
+	buf[n] = 0;
+
+	qlock(&mouselock);
+	if (waserror()) {
+		qunlock(&mouselock);
+		nexterror();
+	}
+	mousectl(buf);
+	poperror();
+	qunlock(&mouselock);
+	return n;
+}
+
+static void
+track(int b, int dx, int dy)
+{
+	static uchar map[8] = {0,4,2,6,1,5,3,7};
+	if (mouseswap)
+		b = map[b&7];
+	mousetrack(b, dx, dy);
+}
+
+static void
+setintellimouse(void)
+{
+	i8042auxcmd(0xF3);	/* set sample */
+	i8042auxcmd(0xC8);
+	i8042auxcmd(0xF3);	/* set sample */
+	i8042auxcmd(0x64);
+	i8042auxcmd(0xF3);	/* set sample */
+	i8042auxcmd(0x50);
+}
+
+/*
+ * check for an Intellimouse.
+ * this is only used when we know there's an 8042 aux device
+ */
+static int
+intellimousedetect(void)
+{
+	int id;
+	setintellimouse();
+	/* check whether the mouse is now in extended mode */
+	id = i8042auxcmdval(0xf2);		/* identify device */
+	if (id != 3) {
+		/*
+		 * set back to standard sample rate (100 per sec)
+		 */
+		i8042auxcmd(0xf3);
+		i8042auxcmd(0x64);
+		return 0;
+	}
+	return 1;
+}
+
+static void
+mouseprobe(char *buf, int len)
+{
+	USED(len);
+	/*
+	 * bus mice are easiest, so probe them first
+	 */
+	if (busmousedetect())
+		sprint(buf, "bus\n");
+	else if (msbusmousedetect())
+		sprint(buf, "msbus\n");
+	else if (i8042auxdetect()) {
+		if (intellimousedetect())
+			sprint(buf, "ps2intellimouse\n");
+		else
+			sprint(buf, "ps2\n");
+	}
+	else
+		*buf = 0;
+}
+
+
+static void
+mousestatus(char *buf, int len)
+{
+	char *s;
+	USED(len);
+	s = buf;
+	switch (mousetype) {
+	case Mouseserial:
+		if (mousesubtype)
+			s += sprint(s, "serial %d %c\n", mouseport, mousesubtype);
+		else
+			s += sprint(s, "serial %d\n", mouseport);
+		break;
+	case MousePS2:
+		s += sprint(s, "ps2\n");
+		break;
+	case Mousebus:
+		s += sprint(s, "bus %d\n", mouseport);
+		break;
+	case Mouseintelli:
+		s += sprint(s, "intelli\n");
+		break;
+	case Mousemsbus:
+		s += sprint(s, "msbus %d\n", mouseport);
+		break;
+	default:
+	case Mouseother:
+		s += sprint(s, "unknown\n");
+		break;
+	}
+	if (accelerated)
+		s += sprint(s, "accelerated\n");
+	if (mouseswap)
+		sprint(s, "swap\n");
+}
+
+/*
+ *  Logitech 5 byte packed binary mouse format, 8 bit bytes
+ *
+ *  shift & right button is the same as middle button (for 2 button mice)
+ */
+static int
+logitechmouseputc(Queue *q, int c)
+{
+	static short msg[5];
+	static int nb;
+	static uchar b[] = {0, 4, 2, 6, 1, 5, 3, 7, 0, 2, 2, 6, 1, 5, 3, 7};
+	int dx, dy, newbuttons;
+	int mouseshifted;
+
+	USED(q);
+	if((c&0xF0) == 0x80)
+		nb=0;
+	msg[nb] = c;
+	if(c & 0x80)
+		msg[nb] |= ~0xFF;	/* sign extend */
+	if(++nb == 5){
+		mouseshifted = 0;	/* XXX should be from keyboard shift key */
+		newbuttons = b[((msg[0]&7)^7) | (mouseshifted ? 8 : 0)];
+		dx = msg[1]+msg[3];
+		dy = -(msg[2]+msg[4]);
+		track(newbuttons, dx, dy);
+		nb = 0;
+	}
+	return 0;
+}
+
+/*
+ *  microsoft 3 button, 7 bit bytes
+ *
+ *	byte 0 -	1  L  R Y7 Y6 X7 X6
+ *	byte 1 -	0 X5 X4 X3 X2 X1 X0
+ *	byte 2 -	0 Y5 Y4 Y3 Y2 Y1 Y0
+ *	byte 3 -	0  M  x  x  x  x  x	(optional)
+ *
+ *  shift & right button is the same as middle button (for 2 button mice)
+ */
+static int
+m3mouseputc(Queue*, int c)
+{
+	static uchar msg[3];
+	static int nb;
+	static int middle;
+	static uchar b[] = { 0, 4, 1, 5, 0, 2, 1, 5 };
+	short x;
+	int dx, dy, buttons;
+
+	/* 
+	 *  check bit 6 for consistency
+	 */
+	if(nb==0){
+		if((c&0x40) == 0){
+			/* an extra byte gets sent for the middle button */
+			if(c & 0x1c)
+				return 0;
+			middle = (c&0x20) ? 2 : 0;
+			buttons = (mouse.b & ~2) | middle;
+			track(buttons, 0, 0);
+			return 0;
+		}
+	}
+	msg[nb] = c&0x3f;
+	if(++nb == 3){
+		nb = 0;
+		buttons = middle | b[(msg[0]>>4)&3];
+		x = (msg[0]&0x3)<<14;
+		dx = (x>>8) | msg[1];
+		x = (msg[0]&0xc)<<12;
+		dy = (x>>8) | msg[2];
+		track(buttons, dx, dy);
+	}
+	return 0;
+}
+
+static void
+serialmouse(int port, char *type, int setspeed)
+{
+	int (*putc)(Queue *, int) = 0;
+	char pn[KNAMELEN];
+
+	if(mousetype)
+		error(Emouseset);
+
+	if(port >= 2 || port < 0)
+		error(Ebadarg);
+
+	if (type == 0)
+		putc = logitechmouseputc;
+	else if (*type == 'M')
+		putc = m3mouseputc;
+	else
+		error(Ebadarg);
+	snprint(pn, sizeof(pn), "%d", port);
+	i8250mouse(pn, putc, setspeed);
+	mousetype = Mouseserial;
+	mouseport = port;
+	mousesubtype = (type && *type == 'M') ? 'M' : 0;
+}
+
+/*
+ *  ps/2 mouse message is three bytes
+ *
+ *	byte 0 -	0 0 SDY SDX 1 M R L
+ *	byte 1 -	DX
+ *	byte 2 -	DY
+ *
+ *  shift & left button is the same as middle button
+ */
+static void
+ps2mouseputc(int c, int shift)
+{
+	static short msg[3];
+	static int nb;
+	static uchar b[] = {0, 1, 4, 5, 2, 3, 6, 7, 0, 1, 2, 5, 2, 3, 6, 7 };
+	int buttons, dx, dy;
+
+	/* 
+	 *  check byte 0 for consistency
+	 */
+	if(nb==0 && (c&0xc8)!=0x08)
+		return;
+
+	msg[nb] = c;
+	if(++nb == 3){
+		nb = 0;
+		if(msg[0] & 0x10)
+			msg[1] |= 0xFF00;
+		if(msg[0] & 0x20)
+			msg[2] |= 0xFF00;
+
+		buttons = b[(msg[0]&7) | (shift ? 8 : 0)];
+		dx = msg[1];
+		dy = -msg[2];
+		track(buttons, dx, dy);
+	}
+	return;
+}
+
+/*
+ *  set up a ps2 mouse
+ */
+static void
+ps2mouse(void)
+{
+	if(mousetype)
+		error(Emouseset);
+
+	i8042auxenable(ps2mouseputc);
+	/* make mouse streaming, enabled */
+	i8042auxcmd(0xEA);
+	i8042auxcmd(0xF4);
+
+	mousetype = MousePS2;
+}
+
+/* logitech bus mouse ports and commands */
+enum {
+	/* ports */
+	BMdatap	= 0x23c,
+	BMsigp	= 0x23d,
+	BMctlp	= 0x23e,
+	BMintrp	= 0x23e,
+	BMconfigp	= 0x23f,
+
+	/* commands */
+	BMintron = 0x0,
+	BMintroff = 0x10,
+	BMrxlo	= 0x80,
+	BMrxhi	= 0xa0,
+	BMrylo	= 0xc0,
+	BMryhi	= 0xe0,
+
+	BMconfig	= 0x91,
+	BMdefault	= 0x90,
+
+	BMsigval	= 0xa5
+};
+
+static void
+busmouseintr(Ureg *, void *)
+{
+	char dx, dy;
+	uchar b;
+	static uchar oldb;
+	static Lock intrlock;
+	ilock(&intrlock);
+	outb(BMintrp, BMintroff);
+	outb(BMctlp, BMrxlo);
+	dx = inb(BMdatap) & 0xf;
+	outb(BMctlp, BMrxhi);
+	dx |= (inb(BMdatap) & 0xf) << 4;
+	outb(BMctlp, BMrylo);
+	dy = inb(BMdatap) & 0xf;
+	outb(BMctlp, BMryhi);
+	b = inb(BMdatap);
+	dy |= (b & 0xf) << 4;
+	b = ~(b >> 5) & 7;
+	if (dx || dy || b != oldb) {
+		oldb = b;
+		track((b>>2)|(b&0x02)|((b&0x01)<<2), dx, dy);
+	}
+	iunlock(&intrlock);
+	outb(BMintrp, BMintron);
+}
+
+static int
+busmousedetect(void)
+{
+	outb(BMconfigp, BMconfig);
+	outb(BMsigp, BMsigval);
+	delay(2);
+	if (inb(BMsigp) != BMsigval)
+		return 0;
+	outb(BMconfigp, BMdefault);
+	return 1;
+}
+
+/*
+ * set up a logitech bus mouse
+ */
+static void
+busmouse(int irq)
+{
+	if (mousetype)
+		error(Emouseset);
+	if (!busmousedetect())
+		error(Enodev);
+
+	intrenable(irq >= 0 ? irq+VectorPIC : VectorBUSMOUSE, busmouseintr, 0, BUSUNKNOWN);
+	outb(BMintrp, BMintron);
+	mousetype = Mousebus;
+	mouseport = irq >= 0 ? irq : VectorBUSMOUSE-VectorPIC;
+}
+
+/* microsoft bus mouse ports and commands */
+enum {
+	MBMdatap=	0x23d,
+	MBMsigp=	0x23e,
+	MBMctlp=	0x23c,
+	MBMconfigp=	0x23f,
+
+	MBMintron=	0x11,
+	MBMintroff=	0x10,
+	MBMrbuttons= 0x00,
+	MBMrx=		0x01,
+	MBMry=		0x02,
+	MBMstart=	0x80,
+	MBMcmd=		0x07,
+};
+
+static void
+msbusmouseintr(Ureg *, void *)
+{
+	char dx, dy;
+	uchar b;
+	static uchar oldb;
+	static Lock intrlock;
+	ilock(&intrlock);
+	outb(MBMctlp, MBMcmd);
+	outb(MBMdatap, inb(MBMdatap)|0x20);
+
+	outb(MBMctlp, MBMrx);
+	dx = inb(MBMdatap);
+
+	outb(MBMctlp, MBMry);
+	dy = inb(MBMdatap);
+
+	outb(MBMctlp, MBMrbuttons);
+	b = inb(MBMdatap) & 0x7;
+
+	outb(MBMctlp, MBMcmd);
+	outb(MBMdatap, inb(MBMdatap)&0xdf);
+
+	if (dx != 0 || dy != 0 || b != oldb) {
+		oldb = b;
+		/* XXX this is almost certainly wrong */
+		track((b>>2)|(b&0x02)|((b&0x01)<<2), dx, dy);
+	}
+	iunlock(&intrlock);
+}
+
+static int
+msbusmousedetect(void)
+{
+	if (inb(MBMsigp) == 0xde) {
+		int v, i;
+		delay(1);
+		v = inb(MBMsigp);
+		delay(1);
+		for (i = 0; i < 4; i++) {
+			if (inb(MBMsigp) != 0xde)
+				break;
+			delay(1);
+			if (inb(MBMsigp) != v)
+				break;
+			delay(1);
+		}
+		if (i == 4) {
+			outb(MBMctlp, MBMcmd);
+			return 1;
+		}
+	}
+	return 0;
+}
+
+static void
+msbusmouse(int irq)
+{
+	if (mousetype)
+		error(Emouseset);
+	if (!msbusmousedetect())
+		error(Enodev);
+	mousetype = Mousemsbus;
+	mouseport = irq >= 0 ? irq : VectorBUSMOUSE-VectorPIC;
+	intrenable(irq >= 0 ? irq+VectorPIC : VectorBUSMOUSE, msbusmouseintr, 0, BUSUNKNOWN);
+	outb(MBMdatap, MBMintron);
+}
+
+static void
+mousectl(char *buf)
+{
+	int nf, x;
+	char *field[10];
+	nf = getfields(buf, field, 10, 1, " \t\n");
+	if (nf < 1)
+		return;
+	if(strncmp(field[0], "serial", 6) == 0){
+		switch(nf){
+		/* the difference between these two cases is intriguing - wrtp */
+		case 1:
+			serialmouse(atoi(field[0]+6), 0, 1);
+			break;
+		case 2:
+			serialmouse(atoi(field[1]), 0, 0);
+			break;
+		case 3:
+		default:
+			serialmouse(atoi(field[1]), field[2], 0);
+			break;
+		}
+	} else if(strcmp(field[0], "ps2") == 0){
+		ps2mouse();
+	} else if (strcmp(field[0], "ps2intellimouse") == 0) {
+		ps2mouse();
+		setintellimouse();
+	} else if (strncmp(field[0], "bus", 3) == 0 || strncmp(field[0], "msbus", 5) == 0) {
+		int irq, isms;
+
+		isms = (field[0][0] == 'm');
+		if (nf == 1)
+			irq = atoi(field[0] + (isms ? 5 : 3));
+		else
+			irq = atoi(field[1]);
+		if (irq < 1)
+			irq = -1;
+		if (isms)
+			msbusmouse(irq);
+		else
+			busmouse(irq);
+	} else if(strcmp(field[0], "accelerated") == 0){
+		switch(mousetype){
+		case MousePS2:
+			x = splhi();
+			i8042auxcmd(0xE7);
+			splx(x);
+			accelerated = 1;
+			break;
+		}
+	} else if(strcmp(field[0], "linear") == 0){
+		switch(mousetype){
+		case MousePS2:
+			x = splhi();
+			i8042auxcmd(0xE6);
+			splx(x);
+			accelerated = 0;
+			break;
+		}
+	} else if(strcmp(field[0], "res") == 0){
+		int n,m;
+		switch(nf){
+		default:
+			n = 0x02;
+			m = 0x23;
+			break;
+		case 2:
+			n = atoi(field[1])&0x3;
+			m = 0x7;
+			break;
+		case 3:
+			n = atoi(field[1])&0x3;
+			m = atoi(field[2])&0x7;
+			break;
+		}
+			
+		switch(mousetype){
+		case MousePS2:
+			x = splhi();
+			i8042auxcmd(0xE8);
+			i8042auxcmd(n);
+			i8042auxcmd(0x5A);
+			i8042auxcmd(0x30|m);
+			i8042auxcmd(0x5A);
+			i8042auxcmd(0x20|(m>>1));
+			splx(x);
+			break;
+		}
+	} else if(strcmp(field[0], "swap") == 0)
+		mouseswap ^= 1;
+}
+
+Dev mousedevtab = {					/* defaults in dev.c */
+	'm',
+	"mouse",
+
+	devreset,					/* devreset */
+	devinit,					/* devinit */
+	mouseattach,
+	devdetach,
+	devclone,					/* devclone */
+	mousewalk,
+	mousestat,
+	mouseopen,
+	devcreate,					/* devcreate */
+	mouseclose,
+	mouseread,
+	devbread,					/* devbread */
+	mousewrite,
+	devbwrite,					/* devbwrite */
+	devremove,					/* devremove */
+	devwstat,					/* devwstat */
+};
+
--- /dev/null
+++ b/os/pc/devmpeg.c
@@ -1,0 +1,1063 @@
+/*
+ *  Boffin MPEG decoder
+ */
+#include	"u.h"
+#include	"../port/lib.h"
+#include	"mem.h"
+#include	"dat.h"
+#include	"fns.h"
+#include	"../port/error.h"
+#include	"zoran.h"
+#include	"crystal.h"
+#include	"io.h"
+
+enum
+{
+
+	CPUACCCTRL      = 0x20,	/* Trident Window Chip control registers */
+	CPUACCMD        = 0x21,
+	BNKADR          = 0x22,
+	SYSCONFIG       = 0x23,
+	VGACOMP         = 0x24,
+	VGAMASK         = 0x25,
+	VIDCOMPL        = 0x26,
+	VIDCOMPH        = 0x27,
+	MOS             = 0x28,
+	DISPCTRL        = 0x29,
+	CAPCTRL         = 0x2a,
+	OVLKT           = 0x2b,
+	OVLWINHSTRT     = 0x2c,
+	OVLWINVSTRT     = 0x2d,
+	OVLWINHEND      = 0x2e,
+	OVLWINVEND      = 0x2f,
+	RESERVED1       = 0x30,
+	RESERVED2       = 0x31,
+	DISPWINVSTRT1   = 0x32,
+	DISPWINVSTRT2   = 0x33,
+	DISPWINVEND     = 0x34,
+	DISPWINHSTRT1   = 0x35,
+	DISPWINHSTRT2   = 0x36,
+	DISPWINHEND     = 0x37,
+	CAPWINVSTRT     = 0x38,
+	CAPWINHSTRT     = 0x39,
+	CAPWINVMF       = 0x3a,
+	CAPWINHMF       = 0x3b,
+	RESERVED3       = 0x3c,
+	CAPMASK         = 0x3d,
+	BNKPOLATION     = 0x3e,
+	SYNCPOL         = 0x3f,
+	DISPVTOTAL      = 0x40,
+	DISPHTOTAL      = 0x41,
+	DISPVSTRT       = 0x42,
+	DISPVEND        = 0x43,
+	DISPHSTRT       = 0x44,
+	DISPHEND        = 0x45,
+	DISPSYNCW       = 0x46,
+	DISPCRTCCTRL    = 0x47,
+	CAPVTOTAL       = 0x48,
+	CAPHTOTAL       = 0x49,
+	CAPVSTRT        = 0x4a,
+	CAPVEND         = 0x4b,
+	CAPHSTRT        = 0x4c,
+	CAPHEND         = 0x4d,
+	CAPSYNCW        = 0x4e,
+	CAPCRTCCTRL     = 0x4f,
+	VIDLUTDACRW     = 0x50,
+	VIDLUTDACRW0    = (VIDLUTDACRW),
+	VIDLUTDACRW1    = (VIDLUTDACRW+1),
+	VIDLUTDACRW2    = (VIDLUTDACRW+2),
+	VIDLUTDACRW3    = (VIDLUTDACRW+3),
+	VIDLUTDACRW4    = (VIDLUTDACRW+4),
+	VIDLUTDACRW5    = (VIDLUTDACRW+5),
+	VIDLUTDACRW6    = (VIDLUTDACRW+6),
+	VIDLUTDACRW7    = (VIDLUTDACRW+7),
+	VGALUTDACRW     = 0x58,
+	VGALUTDACRW0    = (VGALUTDACRW),
+	VGALUTDACRW1    = (VGALUTDACRW+1),
+	VGALUTDACRW2    = (VGALUTDACRW+2),
+	VGALUTDACRW3    = (VGALUTDACRW+3),
+	VGALUTDACRW4    = (VGALUTDACRW+4),
+	VGALUTDACRW5    = (VGALUTDACRW+5),
+	VGALUTDACRW6    = (VGALUTDACRW+6),
+	VGALUTDACRW7    = (VGALUTDACRW+7),
+	HZOOMF          = 0x60,
+	VZOOMF          = 0x61,
+	DELAY1          = 0x62,
+	DELAY2          = 0x63,
+
+	TRILO      	= 0,
+	TRIHI     	= 1,
+	TRIINDEX    	= 2,
+
+	SCL             = 0x02,
+	SDA             = 0x01,
+	I2CR		= 0x2B,
+	SAA7110		= 0x9c,
+	WRITE_C		= 0x00,
+	I2DLY		= 5,
+};
+
+enum
+{
+	ZR36100		= 0x1e0,
+	ZRIRQ		= 15,
+	ZRDMA		= 6,
+
+	ZRIDREG		= 4,				/* offset */
+	ZRMACH210   	= 6,				/* offset */
+	ZRREG0      	= 8,				/* offset */
+	ZRREG1		= 10,				/* offset */
+	ZRSR		= ZRREG1,			/* offset */
+	ZRRDY		= (1<<3),
+	ZRIDLE		= (1<<2),
+	ZRREG2		= 12,				/* offset */
+	ZRREG3		= 14,				/* offset */
+
+	SIFwidth	= 320,
+	SIFheight	= 240,
+
+	IDPCOUNT	= 3064,
+	PMDPCOUNT	= 2048,
+	SVMDPCOUNT	= 2048,
+
+	HIWAT		= 2*128*1024,
+	DMABLK		= 16384,
+};
+
+static struct {
+	int	zrport;
+	int	irq;
+	int	dma;
+	int	trport;
+} mpegconf;
+
+static	char Evmode[] = "video format not supported";
+static	char Eaudio[] = "invalid audio layer";
+static	char Earate[] = "bad audio sample rate";
+
+/* Status bits depend on board revision */
+static	short	STDBY;
+static	short	VIDSEL;
+static	short	VSNIRQn;
+static	short	INTENAn;
+static	short	DSPBOOT;
+static	short	DSPRST;
+static	short	MPGRST;
+static	int	machsr;
+static	int	dopen;
+static	int	started;
+static	int	stop;
+static	int	pause;
+static	int	sp2br;
+static	int	sp2cd;
+static	char	properties[] = "video mpeg1,sif\naudio musicam,I musicam,II\n";
+static	void	inittrident(void);
+static	int	initzoran(void);
+static	void	initcrystal(void);
+static	void	mpegintr(Ureg*, void*);
+static	void	setwindow(int, char**);
+static	void	freebufs(void);
+static	int	mkbuf(char*, int);
+
+typedef struct Buf Buf;
+struct Buf
+{
+	int	nchar;
+	uchar*	ptr;
+	Buf*	link;
+	uchar	data[1];
+};
+
+static struct
+{
+	Lock;
+	int	qlen;
+	Buf*	head;
+	Buf*	tail;
+	Rendez	flow;
+} bqueue;
+
+static int
+zrstatus(void)
+{
+	return ins(mpegconf.zrport+ZRSR) & 0xf;
+}
+
+static int
+zrwaitrdy(int timo, char *msg)
+{
+	int i;
+
+	for(i = 0; i < timo; i++)
+		if(ins(mpegconf.zrport+ZRSR) & ZRRDY)
+			return 0;
+
+	print("devmpeg: device not ready %s\n", msg);
+	return 1;
+}
+
+static void
+zrdma(Buf *b)
+{
+	int n;
+
+	n = dmasetup(mpegconf.dma, b->ptr, b->nchar, 0);
+	b->ptr += n;
+	b->nchar -= n;
+	bqueue.qlen -= n;
+}
+
+static void
+triwr(int reg, int val)
+{
+	outb(mpegconf.trport+TRIINDEX, reg);
+	outb(mpegconf.trport+TRILO, val);
+	outb(mpegconf.trport+TRIHI, val>>8);
+}
+
+static int
+trird(int reg)
+{
+	int v;
+
+	outb(mpegconf.trport+TRIINDEX, reg);
+	v = inb(mpegconf.trport+TRILO);
+	v |= inb(mpegconf.trport+TRIHI)<<8;
+
+	return v;
+}
+
+enum
+{
+	Qdir,
+	Qdata,
+	Qctl,
+};
+static Dirtab mpegtab[]=
+{
+	"mpeg",		{Qdata, 0},	0,	0666,
+	"mpegctl",	{Qctl,  0},	0,	0666,
+};
+
+static void
+mpegreset(void)
+{
+	ISAConf isa;
+
+	mpegconf.zrport = ZR36100;
+	mpegconf.irq = ZRIRQ;
+	mpegconf.dma = ZRDMA;
+
+	memset(&isa, 0, sizeof(isa));
+	if(isaconfig("mpeg", 0, &isa) == 0)
+		return;	
+	if(isa.port)
+		mpegconf.zrport = isa.port;
+	if(isa.irq)
+		mpegconf.irq = isa.irq;
+	if(isa.dma)
+		mpegconf.dma = isa.dma;
+	dmainit(mpegconf.dma, 64*1024);
+	print("mpeg0: port 0x%uX, irq %d, dma %d\n",
+		mpegconf.zrport, mpegconf.irq, mpegconf.dma);
+	mpegconf.trport = mpegconf.zrport+0x100;
+	intrenable(VectorPIC+mpegconf.irq, mpegintr, 0, BUSUNKNOWN);
+}
+
+static void
+mpeginit(void)
+{
+	if(mpegconf.trport == 0)
+		return;
+
+	inittrident();
+	setwindow(0, 0);
+}
+
+static Chan*
+mpegattach(char *spec)
+{
+	if(mpegconf.trport == 0)
+		error(Enodev);
+
+	return devattach('E', spec);
+}
+
+static int
+mpegwalk(Chan *c, char *name)
+{
+	return devwalk(c, name, mpegtab, nelem(mpegtab), devgen);
+}
+
+static void
+mpegstat(Chan *c, char *db)
+{
+	devstat(c, db, mpegtab, nelem(mpegtab), devgen);
+}
+
+static Chan*
+mpegopen(Chan *c, int omode)
+{
+	switch(c->qid.path) {
+	default:
+		break;
+	case Qdata:
+		if(dopen)
+			error(Einuse);
+		dopen = 1;
+		break;
+	}
+	return devopen(c, omode, mpegtab, nelem(mpegtab), devgen);
+}
+
+static void
+mpegclose(Chan *c)
+{
+	int i;
+
+	switch(c->qid.path) {
+	default:
+		break;
+	case Qdata:
+		if((c->flag & COPEN) == 0)
+			break;
+		if(started) {
+			for(i = 0; i < 50; i++) {
+				if(ins(mpegconf.zrport+ZRSR) & ZRIDLE)
+					break;
+				tsleep(&up->sleep, return0, 0, 100);
+			}
+		}
+		if(stop != 0)
+			outs(mpegconf.zrport+ZRREG1, 0x1000);
+		microdelay(15);
+		outs(mpegconf.zrport+ZRREG1, 0x8000);
+		freebufs();
+		dopen = 0;
+	}
+}
+
+static long
+mpegread(Chan *c, void *a, long n, ulong off)
+{
+	switch(c->qid.path & ~CHDIR){
+	default:
+		error(Eperm);
+	case Qdir:
+		return devdirread(c, a, n, mpegtab, nelem(mpegtab), devgen);
+	case Qctl:
+		return readstr(off, a, n, properties);
+	}
+	return 0;
+}
+
+#define SCALE(a, b)	((((a)<<10)/(b))-1024)
+enum
+{
+	CWINVF = 0x3ff,
+	CWINHF = 0x1da,
+};
+
+static void
+setwindow(int nf, char **field)
+{
+	int minx, miny, maxx, maxy, width, height;
+
+	if(field == 0) {
+		minx = 0;
+		miny = 0;
+		maxx = 0;
+		maxy = 0;
+	}
+	else {
+		if(nf != 5)
+			error(Ebadarg);
+
+		minx = strtoul(field[1], 0, 0);
+		miny = strtoul(field[2], 0, 0);
+		maxx = strtoul(field[3], 0, 0) + 8;
+		maxy = strtoul(field[4], 0, 0);
+	}
+
+	triwr(OVLWINHSTRT, minx);
+	triwr(OVLWINVSTRT, miny);
+	triwr(OVLWINHEND, maxx+12);
+	triwr(OVLWINVEND, maxy);
+
+	width = maxx - minx;
+	height = maxy - miny;
+	if(width >= SIFwidth) {
+		triwr(HZOOMF, SCALE(width, SIFwidth));
+		triwr(CAPWINHMF, CWINHF);
+	}
+	else {
+		triwr(HZOOMF, SCALE(SIFwidth, SIFwidth));
+		triwr(CAPWINHMF, width*CWINHF/SIFwidth);
+	}
+	if(height >= SIFheight) {
+		triwr(VZOOMF, SCALE(height, SIFheight));
+		triwr(CAPWINVMF, CWINVF);
+	}
+	else {
+		triwr(VZOOMF, SCALE(SIFheight, SIFheight));
+		triwr(CAPWINVMF, height*CWINVF/SIFheight);
+	}
+}
+
+static int
+mpegflow(void*)
+{
+	return bqueue.qlen < HIWAT || stop;
+}
+
+static int
+mkbuf(char *d, int n)
+{
+	Buf *b;
+
+	b = malloc(sizeof(Buf)+n);
+	if(b == 0)
+		return 0;
+
+	memmove(b->data, d, n);
+	b->ptr = b->data;
+	b->nchar = n;
+	b->link = 0;
+
+	ilock(&bqueue);
+	bqueue.qlen += n;
+	if(bqueue.head)
+		bqueue.tail->link = b;
+	else
+		bqueue.head = b;
+	bqueue.tail = b;
+	iunlock(&bqueue);
+
+	return 1;
+}
+
+static void
+freebufs(void)
+{
+	Buf *next;
+
+	ilock(&bqueue);
+	bqueue.qlen = 0;
+	while(bqueue.head) {
+		next = bqueue.head->link;
+		free(bqueue.head);
+		bqueue.head = next;
+	}
+	iunlock(&bqueue);
+}
+
+typedef struct Audio Audio;
+struct Audio {
+	int rate;
+	int cd;
+	int br;
+};
+
+static Audio AudioclkI[] = 
+{
+	 64000, 0x000000bb, 0x00071797,
+	 96000, 0x0000007d, 0x00071c71,
+	128000, 0x0000005d, 0x00070de1,
+	160000, 0x0000004b, 0x00071c71,
+	192000, 0x0000003e, 0x00070de1,
+	224000, 0x00000035, 0x00070906,
+	256000, 0x0000002e, 0x0006fa76,
+	288000, 0x00000029, 0x0006ff51,
+	320000, 0x00000025, 0x0007042b,
+	352000, 0x00000022, 0x00071797,
+	384000, 0x0000001f, 0x00070de1,
+	416000, 0x0000001c, 0x0006e70b,
+	448000, 0x0000001a, 0x0006e70b,
+};
+
+static Audio  AudioclkII[] = 
+{
+	 48000, 0x000000fa, 0x00071c71,
+	 56000, 0x000000d6, 0x00071a04,
+	 64000, 0x000000bb, 0x00071797,
+	 80000, 0x00000096, 0x00071c71,
+	 96000, 0x0000007d, 0x00071c71,
+	112000, 0x0000006b, 0x00071a04,
+	128000, 0x0000005d, 0x00070de1,
+	160000, 0x0000004b, 0x00071c71,
+	192000, 0x0000003e, 0x00070de1,
+	224000, 0x00000035, 0x00070906,
+	256000, 0x0000002e, 0x0006fa76,
+	320000, 0x00000025, 0x0007042b,
+	384000, 0x0000001f, 0x00070de1,
+};
+
+static long
+mpegwrite(Chan *c, char *a, long n, vlong)
+{
+	Audio *t;
+	int i, nf, l, x;
+	char buf[128], *field[10];
+
+	switch(c->qid.path & ~CHDIR) {
+	case Qctl:
+		if(n > sizeof(buf)-1)
+			n = sizeof(buf)-1;
+		memmove(buf, a, n);
+		buf[n] = '\0';
+
+		nf = getfields(buf, field, nelem(field), 1, " \t\n");
+		if(nf < 1)
+			error(Ebadarg);
+
+		if(strcmp(field[0], "stop") == 0) {
+			if(started == 0)
+				error("not started");
+			if(pause) {
+				pause = 0;
+				outs(mpegconf.zrport+ZRREG1, 0x9000);
+			}
+			stop = 1;
+			outs(mpegconf.zrport+ZRREG1, 0x1000);
+			microdelay(15);
+			outs(mpegconf.zrport+ZRREG1, 0x8000);
+			wakeup(&bqueue.flow);
+			return n;
+		}
+		if(strcmp(field[0], "pause") == 0) {
+			if(started == 0)
+				error("not started");
+			if(pause == 0) {
+				pause = 1;
+				outs(mpegconf.zrport+ZRREG1, 0x1000);
+			}
+			else {
+				pause = 0;
+				outs(mpegconf.zrport+ZRREG1, 0x9000);
+			}
+			return n;
+		}
+		if(strcmp(field[0], "window") == 0) {
+			setwindow(nf, field);
+			return n;
+		}
+		if(strcmp(field[0], "audio") == 0) {
+			if(nf < 3)
+				error(Ebadarg);
+			t = 0;
+			if(strcmp(field[1], "musicam,I") == 0)
+				t = AudioclkI;
+			else
+			if(strcmp(field[1], "musicam,II") == 0)
+				t = AudioclkII;
+			else
+				error(Eaudio);
+			x = strtoul(field[2], 0, 0);
+			for(i = 0; t[i].rate != 0; i++) {
+				if(t[i].rate == x) {
+					sp2cd = t[i].cd;
+					sp2br = t[i].br;
+					return n;
+				}
+			}
+			error(Earate);
+		}
+		if(strcmp(field[0], "video") == 0) {
+			if(nf != 3)
+				error(Ebadarg);
+			if(strcmp(field[1], "iso11172") != 0)
+				error(Evmode);
+			if(strcmp(field[2], "mpeg1,sif") != 0)
+				error(Evmode);
+			return n;
+		}
+		if(strcmp(field[0], "init") == 0) {
+			inittrident();
+			for(i = 0; i < 3; i++)
+				if(initzoran() != -1)
+					break;
+			initcrystal();
+			started = 0;
+			stop = 0;
+			pause = 0;
+			return n;
+		}
+		error(Ebadarg);
+	case Qdata:
+		if(n & 1)
+			error("odd write");
+
+		while(!mpegflow(0))
+			sleep(&bqueue.flow, mpegflow, 0);
+		
+		if(stop)
+			error("stopped");
+
+		x = n;
+		while(x) {
+			l = x;
+			if(l > DMABLK)
+				l = DMABLK;
+			if(mkbuf(a, l) == 0)
+				error(Enomem);
+			x -= l;
+			a += l;
+		}
+		if(started || bqueue.qlen < (HIWAT*3)/4)
+			break;
+
+		zrdma(bqueue.head);
+		outs(mpegconf.zrport+ZRREG1, 0x0000);
+		outs(mpegconf.zrport+ZRREG1, 0x0000);
+		started = 1;
+		break;
+	default:
+		error(Ebadusefd);
+	}
+	return n;
+}
+
+Dev mpegdevtab = {
+	'E',
+	"mpeg",
+
+	mpegreset,
+	mpeginit,
+	mpegattach,
+	devdetach,
+	devclone,
+	mpegwalk,
+	mpegstat,
+	mpegopen,
+	devcreate,
+	mpegclose,
+	mpegread,
+	devbread,
+	mpegwrite,
+	devbwrite,
+	devremove,
+	devwstat,
+};
+
+static void
+initctl(void)
+{
+	int boardid;
+	static int done;
+
+	if(done)
+		return;
+
+	boardid = ins(mpegconf.zrport+ZRIDREG);
+	if(boardid == 0xE3E3) {		/* REV c/d */
+		STDBY   = 0x0000;
+		VIDSEL  = 0x2020;
+		VSNIRQn = 0x1010;
+		INTENAn = 0x0808;
+		DSPBOOT = 0x0404;
+		DSPRST  = 0x0202;
+		MPGRST  = 0x0101;
+	}
+	else {				/* REV b */
+		STDBY   = 0x0404;
+		VIDSEL  = 0x1010;
+		VSNIRQn = 0x8080;
+		INTENAn = 0x4040;
+		DSPBOOT = 0x0202;
+		DSPRST  = 0x0101;
+		MPGRST  = 0x2020;
+	}
+	done = 1;
+
+}
+
+/*
+ * nbl (reg 0x1[ab]) was 0x0022, nblf (reg 1[cd]) was 0x0006
+ */
+static uchar
+zrparam[] = 
+{
+/* 00 */  0xEF, 0x01, 0x01, 0x01, 0x80, 0x0E, 0x31, 0x00,
+/* 08 */  0x01, 0x60, 0x00, 0x00, 0x03, 0x5A, 0x00, 0x7A,
+/* 10 */  0x00, 0x10, 0x00, 0x08, 0x00, 0xF0, 0x00, 0x00,
+/* 18 */  0x02, 0x0D, 0x00, 0x1e, 0x00, 0x0a, 0x00, 0x02, 
+/* 20 */  0x40, 0x06, 0x80, 0x00, 0x80, 0x00, 0x05, 0x9B, 
+/* 28 */  0x07, 0x16, 0xFD, 0x25, 0xFE, 0xA0, 0x00, 0x00,
+/* 30 */  0x00, 0x07, 0x0d, 0xe1, 0x00, 0x00, 0x00, 0x3E,
+/* 38 */  0x00, 0x00, 0x09, 0x51, 0x00, 0x00, 0xCD, 0xFE,
+/* 40 */  0x60, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 
+/* 48 */  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+/* 50 */  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+/* 58 */  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+/* 60 */  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+/* 68 */  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+/* 70 */  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+/* 78 */  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
+};
+
+static int
+initzoran(void)
+{
+	int i, nbytes, zrs;
+
+	initctl();
+	freebufs();
+
+	machsr = DSPRST|VSNIRQn;
+	outs(mpegconf.zrport+ZRMACH210, machsr);
+	microdelay(4000);
+
+	machsr |= STDBY;
+	outs(mpegconf.zrport+ZRMACH210, machsr);
+	microdelay(4000);
+
+	machsr |= MPGRST;
+	outs(mpegconf.zrport+ZRMACH210, machsr);
+	microdelay(4000);
+	machsr &= ~MPGRST;
+	outs(mpegconf.zrport+ZRMACH210, machsr);
+	microdelay(4000);
+	machsr |= MPGRST;
+	outs(mpegconf.zrport+ZRMACH210, machsr);
+	microdelay(4000);
+
+	if(zrwaitrdy(2000, "load IDP"))
+		return -1;
+
+	for(i = 0; i < IDPCOUNT; i++)
+		outb(mpegconf.zrport+ZRREG2, zrmpeg1[i]);
+
+	if(((zrs = zrstatus()) & 3) != 3) {
+/*		print("devmpeg: error loading IDP sr=%2.2ux\n", zrs);	*/
+		USED(zrs);
+		return -1;
+	}
+
+	if(zrwaitrdy(2000, "load PMDP"))
+		return -1;
+
+	for(i = 0; i < PMDPCOUNT; i++)
+		outb(mpegconf.zrport+ZRREG3, zrmpeg2[i]);
+
+	if(((zrs = zrstatus()) & 3) != 3) {
+/*		print("devmpeg: error loading PMDP sr=%2.2ux\n", zrs);	*/
+		USED(zrs);
+		return -1;
+	}
+
+	zrparam[0x36] = sp2cd>>8;
+	zrparam[0x37] = sp2cd>>0;
+	zrparam[0x31] = sp2br>>16;
+	zrparam[0x32] = sp2br>>8;
+	zrparam[0x33] = sp2br>>0;
+
+	nbytes = 16;
+	for(i = 0; i < 128; i++) {
+		if(nbytes >= 16) {
+			if(zrwaitrdy(2000, "load parameters"))
+				return -1;
+			nbytes = 0;
+		}
+		outb(mpegconf.zrport+ZRREG0, zrparam[i]);
+		nbytes++;
+	}
+
+	if(zrwaitrdy(2000, "load SVMDP"))
+		return -1;
+
+	for(i = 0; i < SVMDPCOUNT; i++)
+		outb(mpegconf.zrport+ZRREG3, zrmpeg3s[i]);
+
+	if(((zrs = zrstatus()) & 3) != 3) {
+/*		print("devmpeg: error loading SVMDP sr=%2.2ux\n", zrs);	*/
+		USED(zrs);
+		return -1;
+	}
+	return 0;
+}
+
+static struct
+{
+	short	reg;
+	ushort	val;
+} trireg[] =
+{
+	0x20, 0x0400,
+	0x21, 0x00e9,
+	0x22, 0x0000,
+	0x23, 0x07ee,
+	0x24, 0x0005,
+	0x25, 0xff00,
+	0x26, 0x0000,
+	0x27, 0x7fff,
+	0x28, 0x0004,
+	0x29, 0x88a0,
+	0x2a, 0x0011,
+	0x2b, 0x8540,
+	0x2c, 0x00c4,
+	0x2d, 0x00ac,
+	0x2e, 0x020f,
+	0x2f, 0x019d,
+	0x30, 0x00bd,
+	0x31, 0x00ff,
+	0x32, 0x0000,
+	0x33, 0x0000,
+	0x34, 0x03ff,
+	0x35, 0x0000,
+	0x36, 0x0000,
+	0x37, 0x03ff,
+	0x38, 0x0000,
+	0x39, 0x0000,
+	0x3a, 0x03ff,
+	0x3b, 0x01da,
+	0x3c, 0xe8ce,
+	0x3d, 0x2ac0,
+	0x3e, 0x891f,
+	0x3f, 0x3e25,
+	0x40, 0x03ff,
+	0x41, 0x01ff,
+	0x42, 0x001f,
+	0x43, 0x01ff,
+	0x44, 0x003b,
+	0x45, 0x0186,
+	0x46, 0x1d06,
+	0x47, 0x1a4f,
+	0x48, 0x020d,
+	0x49, 0x01ad,
+	0x4a, 0x001b,
+	0x4b, 0x01fd,
+	0x4c, 0x003a,
+	0x4d, 0x034b,
+	0x4e, 0x2006,
+	0x4f, 0x0083,
+	0x50, 0xef08,
+	0x51, 0xef3a,
+	0x52, 0xefff,
+	0x53, 0xef08,
+	0x54, 0xef08,
+	0x55, 0xef15,
+	0x56, 0xefc0,
+	0x57, 0xef08,
+	0x58, 0xefef,
+	0x59, 0xefef,
+	0x5a, 0xefef,
+	0x5b, 0xefef,
+	0x5c, 0xefef,
+	0x5d, 0xefef,
+	0x5e, 0xefef,
+	0x5f, 0xefef,
+	0x60, 0x0000,
+	0x61, 0x0004,
+	0x62, 0x0020,
+	0x63, 0x8080,
+	0x64, 0x0300,
+	-1
+};
+
+static void
+clrI2C(uchar b)
+{
+	uchar t;
+
+	outb(mpegconf.trport+TRIINDEX, I2CR);
+	t = inb(mpegconf.trport+TRIHI);
+	t &= ~b;
+	outb(mpegconf.trport+TRIHI, t);
+}
+
+static void
+setI2C(uchar b)
+{
+	uchar t;
+
+	outb(mpegconf.trport+TRIINDEX, I2CR);
+	t = inb(mpegconf.trport+TRIHI);
+	t |= b;
+	outb(mpegconf.trport+TRIHI, t);
+}
+
+static void
+startI2C(void)
+{
+	setI2C(SDA);
+	setI2C(SCL);
+	microdelay(I2DLY);
+	clrI2C(SDA);
+	microdelay(I2DLY);
+	clrI2C(SCL);
+	microdelay(I2DLY);
+}
+
+static void
+endI2C(void)
+{
+	clrI2C(SDA);
+	clrI2C(SCL);
+	microdelay(I2DLY);
+	setI2C(SCL);
+	microdelay(I2DLY);
+	setI2C(SDA);
+	microdelay(I2DLY);
+}
+
+static void
+wrI2Cbit(uchar b)
+{
+	clrI2C(SDA);
+	clrI2C(SCL);
+	microdelay(I2DLY);
+	if(b & 1) {
+		setI2C(SDA);
+		microdelay(I2DLY);
+		setI2C(SCL);
+		microdelay(I2DLY);
+		clrI2C(SCL);
+		microdelay(I2DLY);
+		clrI2C(SDA);
+		microdelay(I2DLY);
+	}
+	else {
+		setI2C(SCL);
+		microdelay(I2DLY);
+		clrI2C(SCL);
+		microdelay(I2DLY);
+	}
+}
+
+static void
+wrI2CB(unsigned char data)
+{
+	int i;
+
+	for(i = 0; i < 8; i++)
+		wrI2Cbit(data >>(7-i));
+}
+
+static int
+rdI2CBit(void)
+{
+	int bit = 1;
+
+	setI2C(SDA);
+	clrI2C(SCL);
+	setI2C(SCL);
+	outb(mpegconf.trport+TRIINDEX, I2CR);
+	if(inb(mpegconf.trport+TRIHI) & SDA)
+		bit = 0;
+	clrI2C(SDA);
+	clrI2C(SCL);
+
+	return bit;
+}
+
+static int
+wrI2CD(uchar data)
+{
+	int r;
+	ulong s;
+
+	s = splhi();
+	wrI2CB(data);
+	r = rdI2CBit();
+	splx(s);
+	return r;
+}
+
+static uchar
+setupSAA7110[] =
+{
+	/* Digital */
+	0x4c, 0x3c, 0x0d, 0xef, 0xbd, 0xf0, 0x40, 0x03, 
+	0xf8, 0xf8, 0x90, 0x90, 0x00, 0x02, 0x10, 0x77,
+	0x00, 0x2c, 0x40, 0x40, 0x3b, 0x10, 0xfc, 0xd2,
+	0xf0, 0x80,
+
+	/* Analog */
+	0xd9, 0x16, 0x40, 0x40, 0x80, 0x40, 0x80, 0x4f,
+	0xfe, 0x01, 0xcf, 0x0f, 0x03, 0x01, 0x81, 0x0a,
+	0x40, 0x35, 0x02, 0x8c, 0x03
+};
+
+static void
+addrI2CB(int addr, int val)
+{
+	ulong s;
+
+	s = splhi();
+	startI2C();
+	wrI2CD(SAA7110|WRITE_C);
+	wrI2CD(addr);
+	wrI2CD(val);
+	endI2C();
+	splx(s);
+}
+
+static void
+inittrident(void)
+{
+	int i;
+
+	for(i = 0; trireg[i].reg != -1; i++)
+		triwr(trireg[i].reg, trireg[i].val);
+
+	for(i = 0; i < 47; i++)
+		addrI2CB(i, setupSAA7110[i]); 
+}
+
+static void
+initcrystal(void)
+{
+	int i;
+	static int done;
+
+	if(done)
+		return;
+
+	done = 1;
+
+	initctl();
+
+	/* Reboot the Musicam decoder */
+	clrI2C(SCL);
+	clrI2C(SDA);
+	machsr |= DSPRST;
+	outs(mpegconf.zrport+ZRMACH210, machsr);
+	machsr |= DSPBOOT;
+	outs(mpegconf.zrport+ZRMACH210, machsr);
+	machsr &= ~DSPRST;
+	outs(mpegconf.zrport+ZRMACH210, machsr);
+	machsr |= DSPRST;
+	outs(mpegconf.zrport+ZRMACH210, machsr);
+	machsr &= ~DSPBOOT;
+	outs(mpegconf.zrport+ZRMACH210, machsr);
+
+	startI2C();
+	wrI2CD(0);
+	for(i = 0; i < sizeof(crystal); i++ ) 
+		wrI2CD(crystal[i]);
+	endI2C();
+}
+
+static void
+mpegintr(Ureg*, void*)
+{
+	Buf *b;
+
+	b = bqueue.head;
+	if(b == 0 || dmadone(mpegconf.dma) == 0)
+		return;
+
+	dmaend(mpegconf.dma);
+	if(b->nchar == 0) {
+		bqueue.head = b->link;
+		free(b);
+
+		b = bqueue.head;
+		if(b == 0) {
+			started = 0;
+			return;
+		}
+	}
+	zrdma(b);
+	wakeup(&bqueue.flow);
+}
--- /dev/null
+++ b/os/pc/devpccard.c
@@ -1,0 +1,1949 @@
+/* 
+     cardbus and pcmcia (grmph) support.
+*/
+#include "u.h"
+#include "../port/lib.h"
+#include "mem.h"
+#include "dat.h"
+#include "fns.h"
+#include "../port/error.h"
+#include "io.h"
+
+#define MAP(x,o)	(Rmap + (x)*0x8 + o)
+
+enum {
+	TI_vid = 0x104c,
+	TI_1131_did = 0xAC15,
+	TI_1250_did = 0xAC16,
+	TI_1450_did = 0xAC1B,
+	TI_1251A_did = 0xAC1D,
+	TI_1420_did = 0xAC51,
+
+	Ricoh_vid = 0x1180,
+	Ricoh_475_did = 0x0475,
+	Ricoh_476_did = 0x0476,
+	Ricoh_478_did = 0x0478,
+
+	Nslots = 4,		/* Maximum number of CardBus slots to use */
+
+	K = 1024,
+	M = K * K,
+
+	LegacyAddr = 0x3e0,
+	NUMEVENTS = 10,
+
+	TI1131xSC = 0x80,		// system control
+		TI122X_SC_INTRTIE	= 1 << 29,
+	TI12xxIM = 0x8c,		// 
+	TI1131xCC = 0x91,		// card control
+		TI113X_CC_RIENB = 1 << 7,
+		TI113X_CC_ZVENABLE = 1 << 6,
+		TI113X_CC_PCI_IRQ_ENA = 1 << 5,
+		TI113X_CC_PCI_IREQ = 1 << 4,
+		TI113X_CC_PCI_CSC = 1 << 3,
+		TI113X_CC_SPKROUTEN = 1 << 1,
+		TI113X_CC_IFG = 1 << 0,
+	TI1131xDC = 0x92,		// device control
+};
+
+typedef struct Variant Variant;
+struct Variant {
+	ushort	vid;
+	ushort	did;
+	char		*name;
+};
+
+static Variant variant[] = {
+{	Ricoh_vid,	Ricoh_475_did,	"Ricoh 475 PCI/Cardbus bridge",	},
+{	Ricoh_vid,	Ricoh_476_did,	"Ricoh 476 PCI/Cardbus bridge",	},
+{	Ricoh_vid,	Ricoh_478_did,	"Ricoh 478 PCI/Cardbus bridge",	},
+{	TI_vid,		TI_1131_did,	"TI PCI-1131 Cardbus Controller", },
+{	TI_vid,		TI_1250_did,	"TI PCI-1250 Cardbus Controller", },
+{	TI_vid,		TI_1450_did,	"TI PCI-1450 Cardbus Controller", },
+{	TI_vid,		TI_1251A_did,	"TI PCI-1251A Cardbus Controller", },
+{	TI_vid,		TI_1420_did,	"TI PCI-1420 Cardbus Controller", },
+};
+
+/* Cardbus registers */
+enum {
+	SocketEvent = 0,
+		SE_CCD = 3 << 1,
+		SE_POWER = 1 << 3,
+	SocketMask = 1,
+	SocketState = 2,
+		SS_CCD = 3 << 1,
+		SS_POWER = 1 << 3,
+		SS_PC16 = 1 << 4,
+		SS_CBC = 1 << 5,
+		SS_NOTCARD = 1 << 7,
+		SS_BADVCC = 1 << 9,
+		SS_5V = 1 << 10,
+		SS_3V = 1 << 11,
+	SocketForce = 3,
+	SocketControl = 4,
+		SC_5V = 0x22,
+		SC_3V = 0x33,
+};
+
+enum {
+	PciPCR_IO = 1 << 0,
+	PciPCR_MEM = 1 << 1,
+	PciPCR_Master = 1 << 2,
+
+	PciPMC = 0xa4,
+
+	Nbars = 6,
+	Ncmd = 10,
+	CBIRQ = 9,
+
+	PC16,
+	PC32,
+};
+
+enum {
+	Ti82365,
+	Tpd6710,
+	Tpd6720,
+	Tvg46x,
+};
+
+static char *chipname[] = {
+[Ti82365]		"Intel 82365SL",
+[Tpd6710]	"Cirrus Logic PD6710",
+[Tpd6720]	"Cirrus Logic PD6720",
+[Tvg46x]		"Vadem VG-46x",
+};
+
+/*
+ *  Intel 82365SL PCIC controller for the PCMCIA or
+ *  Cirrus Logic PD6710/PD6720 which is mostly register compatible
+ */
+enum
+{
+	/*
+	 *  registers indices
+	 */
+	Rid=		0x0,		/* identification and revision */
+	Ris=		0x1,		/* interface status */
+	Rpc=	 	0x2,		/* power control */
+	 Foutena=	 (1<<7),	/*  output enable */
+	 Fautopower=	 (1<<5),	/*  automatic power switching */
+	 Fcardena=	 (1<<4),	/*  PC card enable */
+	Rigc= 		0x3,		/* interrupt and general control */
+	 Fiocard=	 (1<<5),	/*  I/O card (vs memory) */
+	 Fnotreset=	 (1<<6),	/*  reset if not set */	
+	 FSMIena=	 (1<<4),	/*  enable change interrupt on SMI */ 
+	Rcsc= 		0x4,		/* card status change */
+	Rcscic= 	0x5,		/* card status change interrupt config */
+	 Fchangeena=	 (1<<3),	/*  card changed */
+	 Fbwarnena=	 (1<<1),	/*  card battery warning */
+	 Fbdeadena=	 (1<<0),	/*  card battery dead */
+	Rwe= 		0x6,		/* address window enable */
+	 Fmem16=	 (1<<5),	/*  use A23-A12 to decode address */
+	Rio= 		0x7,		/* I/O control */
+	 Fwidth16=	 (1<<0),	/*  16 bit data width */
+	 Fiocs16=	 (1<<1),	/*  IOCS16 determines data width */
+	 Fzerows=	 (1<<2),	/*  zero wait state */
+	 Ftiming=	 (1<<3),	/*  timing register to use */
+	Riobtm0lo=	0x8,		/* I/O address 0 start low byte */
+	Riobtm0hi=	0x9,		/* I/O address 0 start high byte */
+	Riotop0lo=	0xa,		/* I/O address 0 stop low byte */
+	Riotop0hi=	0xb,		/* I/O address 0 stop high byte */
+	Riobtm1lo=	0xc,		/* I/O address 1 start low byte */
+	Riobtm1hi=	0xd,		/* I/O address 1 start high byte */
+	Riotop1lo=	0xe,		/* I/O address 1 stop low byte */
+	Riotop1hi=	0xf,		/* I/O address 1 stop high byte */
+	Rmap=		0x10,		/* map 0 */
+
+	/*
+	 *  CL-PD67xx extension registers
+	 */
+	Rmisc1=		0x16,		/* misc control 1 */
+	 F5Vdetect=	 (1<<0),
+	 Fvcc3V=	 (1<<1),
+	 Fpmint=	 (1<<2),
+	 Fpsirq=	 (1<<3),
+	 Fspeaker=	 (1<<4),
+	 Finpack=	 (1<<7),
+	Rfifo=		0x17,		/* fifo control */
+	 Fflush=	 (1<<7),	/*  flush fifo */
+	Rmisc2=		0x1E,		/* misc control 2 */
+	 Flowpow=	 (1<<1),	/*  low power mode */
+	Rchipinfo=	0x1F,		/* chip information */
+	Ratactl=	0x26,		/* ATA control */
+
+	/*
+	 *  offsets into the system memory address maps
+	 */
+	Mbtmlo=		0x0,		/* System mem addr mapping start low byte */
+	Mbtmhi=		0x1,		/* System mem addr mapping start high byte */
+	 F16bit=	 (1<<7),	/*  16-bit wide data path */
+	Mtoplo=		0x2,		/* System mem addr mapping stop low byte */
+	Mtophi=		0x3,		/* System mem addr mapping stop high byte */
+	 Ftimer1=	 (1<<6),	/*  timer set 1 */
+	Mofflo=		0x4,		/* Card memory offset address low byte */
+	Moffhi=		0x5,		/* Card memory offset address high byte */
+	 Fregactive=	 (1<<6),	/*  attribute memory */
+
+	/*
+	 *  configuration registers - they start at an offset in attribute
+	 *  memory found in the CIS.
+	 */
+	Rconfig=	0,
+	 Creset=	 (1<<7),	/*  reset device */
+	 Clevel=	 (1<<6),	/*  level sensitive interrupt line */
+};
+
+/*
+ *  read and crack the card information structure enough to set
+ *  important parameters like power
+ */
+/* cis memory walking */
+typedef struct Cisdat Cisdat;
+struct Cisdat {
+	uchar		*cisbase;
+	int			cispos;
+	int			cisskip;
+	int			cislen;
+};
+
+typedef struct Pcminfo Pcminfo;
+struct Pcminfo {
+	char			verstr[512];		/* Version string */
+	PCMmap		mmap[4];		/* maps, last is always for the kernel */
+	ulong		conf_addr;		/* Config address */
+	uchar		conf_present;	/* Config register present */
+	int			nctab;			/* In use configuration tables */
+	PCMconftab	ctab[8];		/* Configuration tables */
+	PCMconftab	*defctab;		/* Default conftab */
+
+	int			port;			/* Actual port usage */
+	int			irq;			/* Actual IRQ usage */
+};
+
+typedef struct Cardbus Cardbus;
+struct Cardbus {
+	Lock;
+	Variant		*variant;		/* Which CardBus chipset */
+	Pcidev		*pci;			/* The bridge itself */
+	ulong		*regs;			/* Cardbus registers */
+	int			ltype;			/* Legacy type */
+	int			lindex;		/* Legacy port index address */
+	int			ldata;			/* Legacy port data address */
+	int			lbase;			/* Base register for this socket */
+
+	int			state;			/* Current state of card */
+	int			type;			/* Type of card */
+	Pcminfo		linfo;			/* PCMCIA slot info */
+
+	int			special;		/* card is allocated to a driver */
+
+	int			refs;			/* Number of refs to slot */
+	Lock		refslock;		/* inc/dev ref lock */
+};
+
+static int managerstarted;
+
+enum {
+	Mshift=	12,
+	Mgran=	(1<<Mshift),	/* granularity of maps */
+	Mmask=	~(Mgran-1),	/* mask for address bits important to the chip */
+};
+
+static Cardbus cbslots[Nslots];
+static int nslots;
+
+static ulong exponent[8] = { 
+	1, 10, 100, 1000, 10000, 100000, 1000000, 10000000, 
+};
+
+static ulong vmant[16] = {
+	10, 12, 13, 15, 20, 25, 30, 35, 40, 45, 50, 55, 60, 70, 80, 90,
+};
+
+static ulong mantissa[16] = { 
+	0, 10, 12, 13, 15, 20, 25, 30, 35, 40, 45, 50, 55, 60, 70, 80, 
+};
+
+static char Enocard[] = "No card in slot";
+
+enum
+{
+	CMdown,
+	CMpower,
+};
+
+static Cmdtab pccardctlmsg[] =
+{
+	CMdown,		"down",	2,
+	CMpower,	"power",	1,
+};
+
+static void cbint(Ureg *, void *);
+static int powerup(Cardbus *);
+static void configure(Cardbus *);
+static void managecard(Cardbus *);
+static void cardmanager(void *);
+static void eject(Cardbus *);
+static void interrupt(Ureg *, void *);
+static void powerdown(Cardbus *cb);
+static void unconfigure(Cardbus *cb);
+
+static void i82365probe(Cardbus *cb, int lindex, int ldata);
+static void i82365configure(Cardbus *cb);
+static PCMmap *isamap(Cardbus *cb, ulong offset, int len, int attr);
+static void isaunmap(PCMmap* m);
+static uchar rdreg(Cardbus *cb, int index);
+static void wrreg(Cardbus *cb, int index, uchar val);
+static int readc(Cisdat *cis, uchar *x);
+static void tvers1(Cardbus *cb, Cisdat *cis, int );
+static void tcfig(Cardbus *cb, Cisdat *cis, int );
+static void tentry(Cardbus *cb, Cisdat *cis, int );
+static int vcode(int volt);
+static int pccard_pcmspecial(char *idstr, ISAConf *isa);
+static void pccard_pcmspecialclose(int slotno);
+
+enum {
+	CardDetected,
+	CardPowered,
+	CardEjected,
+	CardConfigured,
+};
+
+static char *messages[] = {
+[CardDetected]		"CardDetected",
+[CardPowered]		"CardPowered",
+[CardEjected]		"CardEjected",
+[CardConfigured]	"CardConfigured",
+};
+
+enum {
+	SlotEmpty,
+	SlotFull,
+	SlotPowered,
+	SlotConfigured,
+};
+
+static char *states[] = {
+[SlotEmpty]		"SlotEmpty",
+[SlotFull]			"SlotFull",
+[SlotPowered]		"SlotPowered",
+[SlotConfigured]	"SlotConfigured",
+};
+
+static void
+engine(Cardbus *cb, int message)
+{
+	//print("engine(%d): %s(%s)\n", 
+	//	 (int)(cb - cbslots), states[cb->state], messages[message]);
+	switch (cb->state) {
+	case SlotEmpty:
+
+		switch (message) {
+		case CardDetected:
+			cb->state = SlotFull;
+			powerup(cb);
+			break;
+		case CardEjected:
+			break;
+		default:
+			//print("#Y%d: Invalid message %s in SlotEmpty state\n",
+			//	(int)(cb - cbslots), messages[message]);
+			break;
+		}
+		break;
+
+	case SlotFull:
+
+		switch (message) {
+		case CardPowered:
+			cb->state = SlotPowered;
+			configure(cb);
+			break;
+		case CardEjected:
+			cb->state = SlotEmpty;
+			powerdown(cb);
+			break;
+		default:
+			//print("#Y%d: Invalid message %s in SlotFull state\n",
+			//	(int)(cb - cbslots), messages[message]);
+			break;
+		}
+		break;
+
+	case SlotPowered:
+
+		switch (message) {
+		case CardConfigured:
+			cb->state = SlotConfigured;
+			break;
+		case CardEjected:
+			cb->state = SlotEmpty;
+			unconfigure(cb);
+			powerdown(cb);
+			break;
+		default:
+			//print("#Y%d: Invalid message %s in SlotPowered state\n",
+			//	(int)(cb - cbslots), messages[message]);
+			break;
+		}
+		break;
+
+	case SlotConfigured:
+
+		switch (message) {
+		case CardEjected:
+			cb->state = SlotEmpty;
+			unconfigure(cb);
+			powerdown(cb);
+			break;
+		default:
+			//print("#Y%d: Invalid message %s in SlotConfigured state\n",
+			//	(int)(cb - cbslots), messages[message]);
+			break;
+		}
+		break;
+	}
+}
+
+static void
+qengine(Cardbus *cb, int message)
+{
+	lock(cb);
+	engine(cb, message);
+	unlock(cb);
+}
+
+typedef struct Events Events;
+struct Events {
+	Cardbus	*cb;
+	int	message;
+};
+
+static Lock levents;
+static Events events[NUMEVENTS];
+static Rendez revents;
+static int nevents;
+
+static void
+iengine(Cardbus *cb, int message)
+{
+	if (nevents >= NUMEVENTS) {
+		print("#Y: Too many events queued, discarding request\n");
+		return;
+	}
+	ilock(&levents);
+	events[nevents].cb = cb;
+	events[nevents].message = message;
+	nevents++;
+	iunlock(&levents);
+	wakeup(&revents);
+}
+
+static int
+eventoccured(void)
+{
+	return nevents > 0;
+}
+
+static void
+processevents(void *)
+{
+	while (1) {
+		int message;
+		Cardbus *cb;
+
+		sleep(&revents, (int (*)(void *))eventoccured, nil);
+
+		cb = nil;
+		message = 0;
+		ilock(&levents);
+		if (nevents > 0) {
+			cb = events[0].cb;
+			message = events[0].message;
+			nevents--;
+			if (nevents > 0)
+				memmove(events, &events[1], nevents * sizeof(Events));
+		}
+		iunlock(&levents);
+
+		if (cb)
+			qengine(cb, message);
+	}
+}
+
+static void
+cbinterrupt(Ureg *, void *)
+{
+	int i;
+
+	for (i = 0; i != nslots; i++) {
+		Cardbus *cb = &cbslots[i];
+		ulong event, state;
+
+		event= cb->regs[SocketEvent];
+		state = cb->regs[SocketState];
+		rdreg(cb, Rcsc);	/* Ack the interrupt */
+
+		//print("interrupt: slot %d, event %.8lX, state %.8lX, (%s)\n", 
+		//	(int)(cb - cbslots), event, state, states[cb->state]);
+
+		if (event & SE_CCD) {
+			cb->regs[SocketEvent] |= SE_CCD;	/* Ack interrupt */
+			if (state & SE_CCD) {
+				if (cb->state != SlotEmpty) {
+					print("#Y: take cardejected interrupt\n");
+					iengine(cb, CardEjected);
+				}
+			}
+			else
+				iengine(cb, CardDetected);
+		}
+
+		if (event & SE_POWER) {
+			cb->regs[SocketEvent] |= SE_POWER;	/* Ack interrupt */
+			iengine(cb, CardPowered);
+		}
+	}
+}
+
+void
+devpccardlink(void)
+{
+	static int initialized;
+	Pcidev *pci;
+	int i;
+	uchar intl;
+	char *p;
+
+	if (initialized) 
+		return;
+	initialized = 1;
+
+	if((p=getconf("pccard0")) && strncmp(p, "disabled", 8)==0)
+		return;
+
+	if(_pcmspecial)
+		return;
+
+	/* Allocate legacy space */
+	if (ioalloc(LegacyAddr, 2, 0, "i82365.0") < 0)
+		print("#Y: WARNING: Cannot allocate legacy ports\n");
+
+	/* Find all CardBus controllers */
+	pci = nil;
+	intl = (uchar)-1;
+	while ((pci = pcimatch(pci, 0, 0)) != nil) {
+		ulong baddr;
+		Cardbus *cb;
+		int slot;
+		uchar pin;
+
+		for (i = 0; i != nelem(variant); i++)
+			if (pci->vid == variant[i].vid && pci->did == variant[i].did)
+				break;
+		if (i == nelem(variant))
+			continue;
+
+		/* initialize this slot */
+		slot = nslots++;
+		cb = &cbslots[slot];
+
+		cb->pci = pci;
+		cb->variant = &variant[i];
+		
+		if (pci->vid != TI_vid) {
+			// Gross hack, needs a fix.  Inherit the mappings from 9load
+			// for the TIs (pb)
+			pcicfgw32(pci, PciCBMBR0, 0xffffffff);
+			pcicfgw32(pci, PciCBMLR0, 0);
+			pcicfgw32(pci, PciCBMBR1, 0xffffffff);
+			pcicfgw32(pci, PciCBMLR1, 0);
+			pcicfgw32(pci, PciCBIBR0, 0xffffffff);
+			pcicfgw32(pci, PciCBILR0, 0);
+			pcicfgw32(pci, PciCBIBR1, 0xffffffff);
+			pcicfgw32(pci, PciCBILR1, 0);
+		}
+
+		// Set up PCI bus numbers if needed.
+		if (pcicfgr8(pci, PciSBN) == 0) {
+			static int busbase = 0x20;
+
+			pcicfgw8(pci, PciSBN, busbase);
+			pcicfgw8(pci, PciUBN, busbase + 2);
+			busbase += 3;
+		}
+
+		// Patch up intl if needed.
+		if ((pin = pcicfgr8(pci, PciINTP)) != 0 && 
+		    (pci->intl == 0xff || pci->intl == 0)) {
+			pci->intl = pciipin(nil, pin);
+			pcicfgw8(pci, PciINTL, pci->intl);
+
+			if (pci->intl == 0xff || pci->intl == 0)
+				print("#Y%d: No interrupt?\n", (int)(cb - cbslots));
+		}
+
+		// Don't you love standards!
+		if (pci->vid == TI_vid) {
+			if (pci->did <= TI_1131_did) {
+				uchar cc;
+
+				cc = pcicfgr8(pci, TI1131xCC);
+				cc &= ~(TI113X_CC_PCI_IRQ_ENA |
+						TI113X_CC_PCI_IREQ | 
+						TI113X_CC_PCI_CSC |
+						TI113X_CC_ZVENABLE);
+				cc |= TI113X_CC_PCI_IRQ_ENA | 
+						TI113X_CC_PCI_IREQ | 
+						TI113X_CC_SPKROUTEN;
+				pcicfgw8(pci, TI1131xCC, cc);
+
+				// PCI interrupts only
+				pcicfgw8(pci, TI1131xDC, 
+						pcicfgr8(pci, TI1131xDC) & ~6);
+
+				// CSC ints to PCI bus.
+				wrreg(cb, Rigc, rdreg(cb, Rigc) | 0x10);
+			}
+			else if (pci->did == TI_1250_did) {
+				print("No support yet for the TI_1250_did, prod pb\n");
+			}
+			else if (pci->did == TI_1420_did) {
+				// Disable Vcc protection
+				pcicfgw32(cb->pci, 0x80, 
+					pcicfgr32(cb->pci, 0x80) | (1 << 21));
+			}
+			
+			pcicfgw16(cb->pci, PciPMC, pcicfgr16(cb->pci, PciPMC) & ~3);
+		}
+
+		if (intl != -1 && intl != pci->intl)
+			intrenable(pci->intl, cbinterrupt, cb, pci->tbdf, "cardbus");
+		intl = pci->intl;
+
+		if ((baddr = pcicfgr32(cb->pci, PciBAR0)) == 0) {
+			int align = (pci->did == Ricoh_478_did)? 0x10000: 0x1000;
+
+			baddr = upamalloc(baddr, align, align);
+			pcicfgw32(cb->pci, PciBAR0, baddr);
+			cb->regs = (ulong *)KADDR(baddr);
+		}
+		else
+			cb->regs = (ulong *)KADDR(upamalloc(baddr, 4096, 0));
+		cb->state = SlotEmpty;
+
+		/* Don't really know what to do with this... */
+		i82365probe(cb, LegacyAddr, LegacyAddr + 1);
+
+		print("#Y%ld: %s, %.8ulX intl %d\n", cb - cbslots, 
+			 variant[i].name, baddr, pci->intl);
+	}
+
+	if (nslots == 0){
+		iofree(LegacyAddr);
+		return;
+	}
+
+	_pcmspecial = pccard_pcmspecial;
+	_pcmspecialclose = pccard_pcmspecialclose;
+
+	for (i = 0; i != nslots; i++) {
+		Cardbus *cb = &cbslots[i];
+
+		if ((cb->regs[SocketState] & SE_CCD) == 0)
+			engine(cb, CardDetected);
+	}
+
+	delay(500);			/* Allow time for power up */
+
+	for (i = 0; i != nslots; i++) {
+		Cardbus *cb = &cbslots[i];
+
+		if (cb->regs[SocketState] & SE_POWER)
+			engine(cb, CardPowered);
+
+		/* Ack and enable interrupts on all events */
+		// cb->regs[SocketEvent] = cb->regs[SocketEvent];
+		cb->regs[SocketMask] |= 0xF;	
+		wrreg(cb, Rcscic, 0xC);
+	}
+}
+
+static int
+powerup(Cardbus *cb)
+{
+	ulong state;
+	ushort bcr;
+
+	state = cb->regs[SocketState];
+	if (state & SS_PC16) {
+	
+		// print("#Y%ld: Probed a PC16 card, powering up card\n", cb - cbslots);
+		cb->type = PC16;
+		memset(&cb->linfo, 0, sizeof(Pcminfo));
+
+		/* power up and unreset, wait's are empirical (???) */
+		wrreg(cb, Rpc, Fautopower|Foutena|Fcardena);
+		delay(300);
+		wrreg(cb, Rigc, 0);
+		delay(100);
+		wrreg(cb, Rigc, Fnotreset);
+		delay(500);
+
+		return 1;
+	}
+
+	if (state & SS_CCD)
+		return 0;
+
+	if (state & SS_NOTCARD) {
+		print("#Y%ld: Not a card inserted\n", cb - cbslots);
+		return 0;
+	}
+
+	if ((state & SS_3V) == 0 && (state & SS_5V) == 0) {
+		print("#Y%ld: Unsupported voltage, powering down card!\n", 
+			cb - cbslots);
+		cb->regs[SocketControl] = 0;
+		return 0;
+	}
+
+	//print("#Y%ld: card %spowered at %d volt\n", cb - cbslots, 
+	//	(state & SS_POWER)? "": "not ", 
+	//	(state & SS_3V)? 3: (state & SS_5V)? 5: -1);
+
+	/* Power up the card
+	 * and make sure the secondary bus is not in reset.
+	 */
+	cb->regs[SocketControl] = (state & SS_5V)? SC_5V: SC_3V;
+	delay(50);
+	bcr = pcicfgr16(cb->pci, PciBCR);
+	bcr &= ~0x40;
+	pcicfgw16(cb->pci, PciBCR, bcr);
+	delay(100);
+
+	cb->type = (state & SS_PC16)? PC16: PC32;
+	return 1;
+}
+
+static void
+powerdown(Cardbus *cb)
+{
+	ushort bcr;
+
+	if (cb->type == PC16) {
+
+		wrreg(cb, Rpc, 0);	/* turn off card power */
+		wrreg(cb, Rwe, 0);	/* no windows */
+
+		cb->type = -1;
+		return;
+	}
+
+	bcr = pcicfgr16(cb->pci, PciBCR);
+	bcr |= 0x40;
+	pcicfgw16(cb->pci, PciBCR, bcr);
+	cb->regs[SocketControl] = 0;
+	cb->type = -1;
+}
+
+static void
+configure(Cardbus *cb)
+{
+	int i;
+	Pcidev *pci;
+
+	//print("configuring slot %d (%s)\n", (int)(cb - cbslots), states[cb->state]);
+	if (cb->state == SlotConfigured)
+		return;
+	engine(cb, CardConfigured);
+
+	delay(50);					/* Emperically established */
+
+	if (cb->type == PC16) {
+		i82365configure(cb);
+		return;
+	}
+
+	/* Scan the CardBus for new PCI devices */
+	pciscan(pcicfgr8(cb->pci, PciSBN), &cb->pci->bridge);
+	pci = cb->pci->bridge;
+	while (pci) {
+		ulong size, bar;
+		int memindex, ioindex;
+
+		pcicfgw16(pci, PciPCR, 
+				pcicfgr16(pci, PciPCR) & ~(PciPCR_IO|PciPCR_MEM));
+
+		/* Treat the found device as an ordinary PCI card.  It seems that the 
+		     CIS is not always present in CardBus cards.  XXX, need to support 
+		     multifunction cards */
+		memindex = ioindex = 0;
+		for (i = 0; i != Nbars; i++) {
+
+			if (pci->mem[i].size == 0) continue;
+			if (pci->mem[i].bar & 1) {
+
+				// Allocate I/O space
+				if (ioindex > 1) {
+					print("#Y%ld: WARNING: Can only configure 2 I/O slots\n", cb - cbslots);
+					continue;
+				}
+				bar = ioreserve(-1, pci->mem[i].size, 0, "cardbus");
+
+				pci->mem[i].bar = bar | 1;
+				pcicfgw32(pci, PciBAR0 + i * sizeof(ulong), 
+					          pci->mem[i].bar);
+				pcicfgw16(cb->pci, PciCBIBR0 + ioindex * 8, bar);
+				pcicfgw16(cb->pci, PciCBILR0 + ioindex * 8, 
+						 bar + pci->mem[i].size - 1);
+				//print("ioindex[%d] %.8uX (%d)\n", 
+				//	ioindex, bar, pci->mem[i].size);
+				ioindex++;
+				continue;
+			}
+
+			// Allocating memory space
+			if (memindex > 1) {
+				print("#Y%ld: WARNING: Can only configure 2 memory slots\n", cb - cbslots);
+				continue;
+			}
+
+			bar = upamalloc(0, pci->mem[i].size, BY2PG);
+			pci->mem[i].bar = bar | (pci->mem[i].bar & 0x80);
+			pcicfgw32(pci, PciBAR0 + i * sizeof(ulong), pci->mem[i].bar);
+			pcicfgw32(cb->pci, PciCBMBR0 + memindex * 8, bar);
+			pcicfgw32(cb->pci, PciCBMLR0 + memindex * 8, 
+					  bar + pci->mem[i].size - 1);
+
+			if (pci->mem[i].bar & 0x80)
+				/* Enable prefetch */
+				pcicfgw16(cb->pci, PciBCR, 
+						 pcicfgr16(cb->pci, PciBCR) | 
+							          (1 << (8 + memindex)));
+
+			//print("memindex[%d] %.8uX (%d)\n", 
+			//	  memindex, bar, pci->mem[i].size);
+			memindex++;
+		}
+
+		if ((size = pcibarsize(pci, PciEBAR0)) > 0) {
+
+			if (memindex > 1)
+				print("#Y%ld: WARNING: Too many memory spaces, not mapping ROM space\n",
+					cb - cbslots);
+			else {
+				pci->rom.bar = upamalloc(0, size, BY2PG);
+				pci->rom.size = size;
+
+				pcicfgw32(pci, PciEBAR0, pci->rom.bar);
+				pcicfgw32(cb->pci, PciCBMBR0 + memindex * 8,
+						 pci->rom.bar);
+				pcicfgw32(cb->pci, PciCBMLR0 + memindex * 8, 
+						 pci->rom.bar + pci->rom.size - 1);
+			}
+		}
+
+		/* Set the basic PCI registers for the device */
+		pci->pcr = pcicfgr16(pci, PciPCR) | PciPCR_IO|PciPCR_MEM|PciPCR_Master;
+		pci->cls = 8;
+		pci->ltr = 64;
+		pcicfgw16(pci, PciPCR, pci->pcr);
+		pcicfgw8(pci, PciCLS, pci->cls);
+		pcicfgw8(pci, PciLTR, pci->ltr);
+
+		if (pcicfgr8(pci, PciINTP)) {
+			pci->intl = pcicfgr8(cb->pci, PciINTL);
+			pcicfgw8(pci, PciINTL, pci->intl);
+
+			/* Route interrupts to INTA#/B# */
+			pcicfgw16(cb->pci, PciBCR, 
+					  pcicfgr16(cb->pci, PciBCR) & ~(1 << 7));
+		}
+			
+		pci = pci->list;
+	}
+}
+
+static void
+unconfigure(Cardbus *cb)
+{
+	Pcidev *pci;
+	int i, ioindex, memindex;
+
+	if (cb->type == PC16) {
+		print("#Y%d: Don't know how to unconfigure a PC16 card\n",
+			 (int)(cb - cbslots));
+
+		memset(&cb->linfo, 0, sizeof(Pcminfo));
+		return;
+	}
+
+	pci = cb->pci->bridge;
+	if (pci == nil) 
+		return;		/* Not configured */
+	cb->pci->bridge = nil;		
+
+	memindex = ioindex = 0;
+	while (pci) {
+		Pcidev *_pci;
+
+		for (i = 0; i != Nbars; i++) {
+			if (pci->mem[i].size == 0) continue;
+			if (pci->mem[i].bar & 1) {
+				iofree(pci->mem[i].bar & ~1);
+				pcicfgw16(cb->pci, PciCBIBR0 + ioindex * 8, 
+						 (ushort)-1);
+				pcicfgw16(cb->pci, PciCBILR0 + ioindex * 8, 0);
+				ioindex++;
+				continue;
+			}
+
+			upafree(pci->mem[i].bar & ~0xF, pci->mem[i].size);
+			pcicfgw32(cb->pci, PciCBMBR0 + memindex * 8, 
+				          (ulong)-1);
+			pcicfgw32(cb->pci, PciCBMLR0 + memindex * 8, 0);
+			pcicfgw16(cb->pci, PciBCR, 
+					 pcicfgr16(cb->pci, PciBCR) & 
+							       ~(1 << (8 + memindex)));
+			memindex++;
+		}
+
+		if (pci->rom.bar && memindex < 2) {
+			upafree(pci->rom.bar & ~0xF, pci->rom.size);
+			pcicfgw32(cb->pci, PciCBMBR0 + memindex * 8, 
+					  (ulong)-1);
+			pcicfgw32(cb->pci, PciCBMLR0 + memindex * 8, 0);
+			memindex++;
+		}
+
+		_pci = pci->list;
+		free(_pci);
+		pci = _pci;
+	}
+}
+
+static void
+i82365configure(Cardbus *cb)
+{
+	int this;
+	Cisdat cis;
+	PCMmap *m;
+	uchar type, link;
+
+	/*
+	 * Read all tuples in attribute space.
+	 */
+	m = isamap(cb, 0, 0, 1);
+	if(m == 0)
+		return;
+
+	cis.cisbase = KADDR(m->isa);
+	cis.cispos = 0;
+	cis.cisskip = 2;
+	cis.cislen = m->len;
+
+	/* loop through all the tuples */
+	for(;;){
+		this = cis.cispos;
+		if(readc(&cis, &type) != 1)
+			break;
+		if(type == 0xFF)
+			break;
+		if(readc(&cis, &link) != 1)
+			break;
+
+		switch(type){
+		default:
+			break;
+		case 0x15:
+			tvers1(cb, &cis, type);
+			break;
+		case 0x1A:
+			tcfig(cb, &cis, type);
+			break;
+		case 0x1B:
+			tentry(cb, &cis, type);
+			break;
+		}
+
+		if(link == 0xFF)
+			break;
+		cis.cispos = this + (2+link);
+	}
+	isaunmap(m);
+}
+
+/*
+ *  look for a card whose version contains 'idstr'
+ */
+static int
+pccard_pcmspecial(char *idstr, ISAConf *isa)
+{
+	int i, irq;
+	PCMconftab *ct, *et;
+	Pcminfo *pi;
+	Cardbus *cb;
+	uchar x, we, *p;
+
+	cb = nil;
+	for (i = 0; i != nslots; i++) {
+		cb = &cbslots[i];
+
+		lock(cb);
+		if (cb->state == SlotConfigured &&
+		    cb->type == PC16 && 
+		    !cb->special &&
+		    strstr(cb->linfo.verstr, idstr)) 
+			break;
+		unlock(cb);
+	}
+
+	if (i == nslots) {
+		// print("#Y: %s not found\n", idstr);
+		return -1;
+	}
+
+	pi = &cb->linfo;
+
+	/*
+ 	  *  configure the PCMslot for IO.  We assume very heavily that we can read
+ 	  *  configuration info from the CIS.  If not, we won't set up correctly.
+ 	  */
+	irq = isa->irq;
+	if(irq == 2)
+		irq = 9;
+
+	et = &pi->ctab[pi->nctab];
+	ct = nil;
+	for(i = 0; i < isa->nopt; i++){
+		int index;
+		char *cp;
+
+		if(strncmp(isa->opt[i], "index=", 6))
+			continue;
+		index = strtol(&isa->opt[i][6], &cp, 0);
+		if(cp == &isa->opt[i][6] || index >= pi->nctab) {
+			unlock(cb);
+			print("#Y%d: Cannot find index %d in conf table\n", 
+				 (int)(cb - cbslots), index);
+			return -1;
+		}
+		ct = &pi->ctab[index];
+	}
+
+	if(ct == nil){
+		PCMconftab *t;
+
+		/* assume default is right */
+		if(pi->defctab)
+			ct = pi->defctab;
+		else
+			ct = pi->ctab;
+	
+		/* try for best match */
+		if(ct->nio == 0
+		|| ct->io[0].start != isa->port || ((1<<irq) & ct->irqs) == 0){
+			for(t = pi->ctab; t < et; t++)
+				if(t->nio
+				&& t->io[0].start == isa->port
+				&& ((1<<irq) & t->irqs)){
+					ct = t;
+					break;
+				}
+		}
+		if(ct->nio == 0 || ((1<<irq) & ct->irqs) == 0){
+			for(t = pi->ctab; t < et; t++)
+				if(t->nio && ((1<<irq) & t->irqs)){
+					ct = t;
+					break;
+				}
+		}
+		if(ct->nio == 0){
+			for(t = pi->ctab; t < et; t++)
+				if(t->nio){
+					ct = t;
+					break;
+				}
+		}
+	}
+
+	if(ct == et || ct->nio == 0) {
+		unlock(cb);
+		print("#Y%d: No configuration?\n", (int)(cb - cbslots));
+		return -1;
+	}
+	if(isa->port == 0 && ct->io[0].start == 0) {
+		unlock(cb);
+		print("#Y%d: No part or start address\n", (int)(cb - cbslots));
+		return -1;
+	}
+
+	cb->special = 1;	/* taken */
+
+	/* route interrupts */
+	isa->irq = irq;
+	wrreg(cb, Rigc, irq | Fnotreset | Fiocard);
+
+	/* set power and enable device */
+	x = vcode(ct->vpp1);
+	wrreg(cb, Rpc, x|Fautopower|Foutena|Fcardena);
+
+	/* 16-bit data path */
+	if(ct->bit16)
+		x = Ftiming|Fiocs16|Fwidth16;
+	else
+		x = Ftiming;
+	if(ct->nio == 2 && ct->io[1].start)
+		x |= x<<4;
+	wrreg(cb, Rio, x);
+
+	/*
+	 * enable io port map 0
+	 * the 'top' register value includes the last valid address
+	 */
+	if(isa->port == 0)
+		isa->port = ct->io[0].start;
+	we = rdreg(cb, Rwe);
+	wrreg(cb, Riobtm0lo, isa->port);
+	wrreg(cb, Riobtm0hi, isa->port>>8);
+	i = isa->port+ct->io[0].len-1;
+	wrreg(cb, Riotop0lo, i);
+	wrreg(cb, Riotop0hi, i>>8);
+	we |= 1<<6;
+	if(ct->nio == 2 && ct->io[1].start){
+		wrreg(cb, Riobtm1lo, ct->io[1].start);
+		wrreg(cb, Riobtm1hi, ct->io[1].start>>8);
+		i = ct->io[1].start+ct->io[1].len-1;
+		wrreg(cb, Riotop1lo, i);
+		wrreg(cb, Riotop1hi, i>>8);
+		we |= 1<<7;
+	}
+	wrreg(cb, Rwe, we);
+
+	/* only touch Rconfig if it is present */
+	if(pi->conf_present & (1<<Rconfig)){
+		PCMmap *m;
+
+		/*  Reset adapter */
+		m = isamap(cb, pi->conf_addr + Rconfig, 1, 1);
+		p = KADDR(m->isa + pi->conf_addr + Rconfig - m->ca);
+
+		/* set configuration and interrupt type */
+		x = ct->index;
+		if(ct->irqtype & 0x20)
+			x |= Clevel;
+		*p = x;
+		delay(5);
+
+		isaunmap(m);
+	}
+
+	pi->port = isa->port;
+	pi->irq = isa->irq;
+	unlock(cb);
+
+	print("#Y%d: %s irq %d, port %lX\n", (int)(cb - cbslots), pi->verstr, isa->irq, isa->port);
+	return (int)(cb - cbslots);
+}
+
+static void
+pccard_pcmspecialclose(int slotno)
+{
+	Cardbus *cb = &cbslots[slotno];
+
+	wrreg(cb, Rwe, 0);	/* no windows */
+	cb->special = 0;
+}
+
+static int
+xcistuple(int slotno, int tuple, int subtuple, void *v, int nv, int attr)
+{
+	PCMmap *m;
+	Cisdat cis;
+	int i, l;
+	uchar *p;
+	uchar type, link, n, c;
+	int this, subtype;
+	Cardbus *cb = &cbslots[slotno];
+
+	m = isamap(cb, 0, 0, attr);
+	if(m == 0)
+		return -1;
+
+	cis.cisbase = KADDR(m->isa);
+	cis.cispos = 0;
+	cis.cisskip = attr ? 2 : 1;
+	cis.cislen = m->len;
+
+	/* loop through all the tuples */
+	for(i = 0; i < 1000; i++){
+		this = cis.cispos;
+		if(readc(&cis, &type) != 1)
+			break;
+		if(type == 0xFF)
+			break;
+		if(readc(&cis, &link) != 1)
+			break;
+		if(link == 0xFF)
+			break;
+
+		n = link;
+		if (link > 1 && subtuple != -1) {
+			if (readc(&cis, &c) != 1)
+				break;
+			subtype = c;
+			n--;
+		} else
+			subtype = -1;
+
+		if(type == tuple && subtype == subtuple) {
+			p = v;
+			for(l=0; l<nv && l<n; l++)
+				if(readc(&cis, p++) != 1)
+					break;
+			isaunmap(m);
+			return nv;
+		}
+		cis.cispos = this + (2+link);
+	}
+	isaunmap(m);
+	return -1;
+}
+
+static Chan*
+pccardattach(char *spec)
+{
+	if (!managerstarted) {
+		managerstarted = 1;
+		kproc("cardbus", processevents, nil);
+	}
+	return devattach('Y', spec);
+}
+
+enum
+{
+	Qdir,
+	Qctl,
+
+	Nents = 1,
+};
+
+#define SLOTNO(c)	((ulong)((c->qid.path>>8)&0xff))
+#define TYPE(c)	((ulong)(c->qid.path&0xff))
+#define QID(s,t)	(((s)<<8)|(t))
+
+static int
+pccardgen(Chan *c, char*, Dirtab *, int , int i, Dir *dp)
+{
+	int slotno;
+	Qid qid;
+	long len;
+	int entry;
+
+	if(i == DEVDOTDOT){
+		mkqid(&qid, Qdir, 0, QTDIR);
+		devdir(c, qid, "#Y", 0, eve, 0555, dp);
+		return 1;
+	}
+
+	len = 0;
+	if(i >= Nents * nslots) return -1;
+	slotno = i / Nents;
+	entry = i % Nents;
+	if (entry == 0) {
+		qid.path = QID(slotno, Qctl);
+		snprint(up->genbuf, sizeof up->genbuf, "cb%dctl", slotno);
+	}
+	else {
+		/* Entries for memory regions.  I'll implement them when 
+		     needed. (pb) */
+	}
+	qid.vers = 0;	
+	qid.type = QTFILE;
+	devdir(c, qid, up->genbuf, len, eve, 0660, dp);
+	return 1;
+}
+
+static Walkqid*
+pccardwalk(Chan *c, Chan *nc, char **name, int nname)
+{
+	return devwalk(c, nc, name, nname, 0, 0, pccardgen);
+}
+
+static int
+pccardstat(Chan *c, uchar *db, int n)
+{
+	return devstat(c, db, n, 0, 0, pccardgen);
+}
+
+static void
+increfp(Cardbus *cb)
+{
+	lock(&cb->refslock);
+	cb->refs++;
+	unlock(&cb->refslock);
+}
+
+static void
+decrefp(Cardbus *cb)
+{
+	lock(&cb->refslock);
+	cb->refs--;
+	unlock(&cb->refslock);
+}
+
+static Chan*
+pccardopen(Chan *c, int omode)
+{
+	if (c->qid.type & QTDIR){
+		if(omode != OREAD)
+			error(Eperm);
+	} else
+		increfp(&cbslots[SLOTNO(c)]);
+	c->mode = openmode(omode);
+	c->flag |= COPEN;
+	c->offset = 0;
+	return c;
+}
+
+static void
+pccardclose(Chan *c)
+{
+	if(c->flag & COPEN)
+		if((c->qid.type & QTDIR) == 0)
+			decrefp(&cbslots[SLOTNO(c)]);
+}
+
+static long
+pccardread(Chan *c, void *a, long n, vlong offset)
+{
+	Cardbus *cb;
+	char *buf, *p, *e;
+	int i;
+
+	switch(TYPE(c)){
+	case Qdir:
+		return devdirread(c, a, n, 0, 0, pccardgen);
+
+	case Qctl:
+		buf = p = malloc(READSTR);
+		buf[0] = 0;
+		e = p + READSTR;
+	
+		cb = &cbslots[SLOTNO(c)];
+		lock(cb);
+		p = seprint(p, e, "slot %ld: %s; ", cb - cbslots, states[cb->state]);
+
+		switch (cb->type) {
+		case -1:
+			seprint(p, e, "\n");
+			break;
+
+		case PC32:
+			if (cb->pci->bridge) {
+				Pcidev *pci = cb->pci->bridge;
+				int i;
+
+				while (pci) {
+					p = seprint(p, e, "%.4uX %.4uX; irq %d\n", 
+							  pci->vid, pci->did, pci->intl);
+					for (i = 0; i != Nbars; i++)
+						if (pci->mem[i].size)
+							p = seprint(p, e, 
+									  "\tmem[%d] %.8ulX (%.8uX)\n",
+									  i, pci->mem[i].bar, 
+									  pci->mem[i].size);
+					if (pci->rom.size)
+						p = seprint(p, e, "\tROM %.8ulX (%.8uX)\n",
+								  pci->rom.bar, pci->rom.size);
+					pci = pci->list;
+				}
+			}
+			break;
+
+		case PC16:
+			if (cb->state == SlotConfigured) {
+				Pcminfo *pi = &cb->linfo;
+
+				p = seprint(p, e, "%s port %X; irq %d;\n",
+						  pi->verstr, pi->port,
+						  pi->irq);
+				for (i = 0; i != pi->nctab; i++) {
+					PCMconftab *ct;
+					int j;
+
+					ct = &pi->ctab[i];
+					p = seprint(p, e, 
+						"\tconfiguration[%d] irqs %.4uX; vpp %d, %d; %s\n",
+							  i, ct->irqs, ct->vpp1, ct->vpp2,
+							  (ct == pi->defctab)? "(default);": "");
+					for (j = 0; j != ct->nio; j++)
+						if (ct->io[j].len > 0)
+							p = seprint(p, e, "\t\tio[%d] %.8ulX %uld\n",
+									  j, ct->io[j].start, ct->io[j].len);
+				}
+			}
+			break;
+		}
+		unlock(cb);
+			
+		n = readstr(offset, a, n, buf);
+		free(buf);
+		return n;
+	}
+	return 0;
+}
+
+static long
+pccardwrite(Chan *c, void *v, long n, vlong)
+{
+	Rune r;
+	ulong n0;
+	char *device;
+	Cmdbuf *cbf;
+	Cmdtab *ct;
+	Cardbus *cb;
+
+	n0 = n;
+	switch(TYPE(c)){
+	case Qctl:
+		cb = &cbslots[SLOTNO(c)];
+
+		cbf = parsecmd(v, n);
+		if(waserror()){
+			free(cbf);
+			nexterror();
+		}
+		ct = lookupcmd(cbf, pccardctlmsg, nelem(pccardctlmsg));
+		switch(ct->index){
+		case CMdown:
+			device = cbf->f[1];
+			device += chartorune(&r, device);
+			if ((n = devno(r, 1)) >= 0 && devtab[n]->config)
+				devtab[n]->config(0, device, nil);
+			qengine(cb, CardEjected);
+			break;
+		case CMpower:
+			if ((cb->regs[SocketState] & SS_CCD) == 0)
+				qengine(cb, CardDetected);
+			break;
+		}
+		poperror();
+		free(cbf);
+		break;
+	}
+	return n0 - n;
+}
+
+Dev pccarddevtab = {
+	'Y',
+	"cardbus",
+
+	devreset,
+	devinit,	
+	devshutdown,
+	pccardattach,
+	pccardwalk,
+	pccardstat,
+	pccardopen,
+	devcreate,
+	pccardclose,
+	pccardread,
+	devbread,
+	pccardwrite,
+	devbwrite,
+	devremove,
+	devwstat,
+};
+
+static PCMmap *
+isamap(Cardbus *cb, ulong offset, int len, int attr)
+{
+	uchar we, bit;
+	PCMmap *m, *nm;
+	Pcminfo *pi;
+	int i;
+	ulong e;
+
+	pi = &cb->linfo;
+
+	/* convert offset to granularity */
+	if(len <= 0)
+		len = 1;
+	e = ROUND(offset+len, Mgran);
+	offset &= Mmask;
+	len = e - offset;
+
+	/* look for a map that covers the right area */
+	we = rdreg(cb, Rwe);
+	bit = 1;
+	nm = 0;
+	for(m = pi->mmap; m < &pi->mmap[nelem(pi->mmap)]; m++){
+		if((we & bit))
+		if(m->attr == attr)
+		if(offset >= m->ca && e <= m->cea){
+
+			m->ref++;
+			return m;
+		}
+		bit <<= 1;
+		if(nm == 0 && m->ref == 0)
+			nm = m;
+	}
+	m = nm;
+	if(m == 0)
+		return 0;
+
+	/* if isa space isn't big enough, free it and get more */
+	if(m->len < len){
+		if(m->isa){
+			umbfree(m->isa, m->len);
+			m->len = 0;
+		}
+		m->isa = PADDR(umbmalloc(0, len, Mgran));
+		if(m->isa == 0){
+			print("isamap: out of isa space\n");
+			return 0;
+		}
+		m->len = len;
+	}
+
+	/* set up new map */
+	m->ca = offset;
+	m->cea = m->ca + m->len;
+	m->attr = attr;
+	i = m - pi->mmap;
+	bit = 1<<i;
+	wrreg(cb, Rwe, we & ~bit);		/* disable map before changing it */
+	wrreg(cb, MAP(i, Mbtmlo), m->isa>>Mshift);
+	wrreg(cb, MAP(i, Mbtmhi), (m->isa>>(Mshift+8)) | F16bit);
+	wrreg(cb, MAP(i, Mtoplo), (m->isa+m->len-1)>>Mshift);
+	wrreg(cb, MAP(i, Mtophi), ((m->isa+m->len-1)>>(Mshift+8)));
+	offset -= m->isa;
+	offset &= (1<<25)-1;
+	offset >>= Mshift;
+	wrreg(cb, MAP(i, Mofflo), offset);
+	wrreg(cb, MAP(i, Moffhi), (offset>>8) | (attr ? Fregactive : 0));
+	wrreg(cb, Rwe, we | bit);		/* enable map */
+	m->ref = 1;
+
+	return m;
+}
+
+static void
+isaunmap(PCMmap* m)
+{
+	m->ref--;
+}
+
+/*
+ *  reading and writing card registers
+ */
+static uchar
+rdreg(Cardbus *cb, int index)
+{
+	outb(cb->lindex, cb->lbase + index);
+	return inb(cb->ldata);
+}
+
+static void
+wrreg(Cardbus *cb, int index, uchar val)
+{
+	outb(cb->lindex, cb->lbase + index);
+	outb(cb->ldata, val);
+}
+
+static int
+readc(Cisdat *cis, uchar *x)
+{
+	if(cis->cispos >= cis->cislen)
+		return 0;
+	*x = cis->cisbase[cis->cisskip*cis->cispos];
+	cis->cispos++;
+	return 1;
+}
+
+static ulong
+getlong(Cisdat *cis, int size)
+{
+	uchar c;
+	int i;
+	ulong x;
+
+	x = 0;
+	for(i = 0; i < size; i++){
+		if(readc(cis, &c) != 1)
+			break;
+		x |= c<<(i*8);
+	}
+	return x;
+}
+
+static void
+tcfig(Cardbus *cb, Cisdat *cis, int )
+{
+	uchar size, rasize, rmsize;
+	uchar last;
+	Pcminfo *pi;
+
+	if(readc(cis, &size) != 1)
+		return;
+	rasize = (size&0x3) + 1;
+	rmsize = ((size>>2)&0xf) + 1;
+	if(readc(cis, &last) != 1)
+		return;
+
+	pi = &cb->linfo;
+	pi->conf_addr = getlong(cis, rasize);
+	pi->conf_present = getlong(cis, rmsize);
+}
+
+static void
+tvers1(Cardbus *cb, Cisdat *cis, int )
+{
+	uchar c, major, minor, last;
+	int  i;
+	Pcminfo *pi;
+
+	pi = &cb->linfo;
+	if(readc(cis, &major) != 1)
+		return;
+	if(readc(cis, &minor) != 1)
+		return;
+	last = 0;
+	for(i = 0; i < sizeof(pi->verstr) - 1; i++){
+		if(readc(cis, &c) != 1)
+			return;
+		if(c == 0)
+			c = ';';
+		if(c == '\n')
+			c = ';';
+		if(c == 0xff)
+			break;
+		if(c == ';' && last == ';')
+			continue;
+		pi->verstr[i] = c;
+		last = c;
+	}
+	pi->verstr[i] = 0;
+}
+
+static ulong
+microvolt(Cisdat *cis)
+{
+	uchar c;
+	ulong microvolts;
+	ulong exp;
+
+	if(readc(cis, &c) != 1)
+		return 0;
+	exp = exponent[c&0x7];
+	microvolts = vmant[(c>>3)&0xf]*exp;
+	while(c & 0x80){
+		if(readc(cis, &c) != 1)
+			return 0;
+		switch(c){
+		case 0x7d:
+			break;		/* high impedence when sleeping */
+		case 0x7e:
+		case 0x7f:
+			microvolts = 0;	/* no connection */
+			break;
+		default:
+			exp /= 10;
+			microvolts += exp*(c&0x7f);
+		}
+	}
+	return microvolts;
+}
+
+static ulong
+nanoamps(Cisdat *cis)
+{
+	uchar c;
+	ulong nanoamps;
+
+	if(readc(cis, &c) != 1)
+		return 0;
+	nanoamps = exponent[c&0x7]*vmant[(c>>3)&0xf];
+	while(c & 0x80){
+		if(readc(cis, &c) != 1)
+			return 0;
+		if(c == 0x7d || c == 0x7e || c == 0x7f)
+			nanoamps = 0;
+	}
+	return nanoamps;
+}
+
+/*
+ * only nominal voltage (feature 1) is important for config,
+ * other features must read card to stay in sync.
+ */
+static ulong
+power(Cisdat *cis)
+{
+	uchar feature;
+	ulong mv;
+
+	mv = 0;
+	if(readc(cis, &feature) != 1)
+		return 0;
+	if(feature & 1)
+		mv = microvolt(cis);
+	if(feature & 2)
+		microvolt(cis);
+	if(feature & 4)
+		microvolt(cis);
+	if(feature & 8)
+		nanoamps(cis);
+	if(feature & 0x10)
+		nanoamps(cis);
+	if(feature & 0x20)
+		nanoamps(cis);
+	if(feature & 0x40)
+		nanoamps(cis);
+	return mv/1000000;
+}
+
+static ulong
+ttiming(Cisdat *cis, int scale)
+{
+	uchar unscaled;
+	ulong nanosecs;
+
+	if(readc(cis, &unscaled) != 1)
+		return 0;
+	nanosecs = (mantissa[(unscaled>>3)&0xf]*exponent[unscaled&7])/10;
+	nanosecs = nanosecs * exponent[scale];
+	return nanosecs;
+}
+
+static void
+timing(Cisdat *cis, PCMconftab *ct)
+{
+	uchar c, i;
+
+	if(readc(cis, &c) != 1)
+		return;
+	i = c&0x3;
+	if(i != 3)
+		ct->maxwait = ttiming(cis, i);		/* max wait */
+	i = (c>>2)&0x7;
+	if(i != 7)
+		ct->readywait = ttiming(cis, i);		/* max ready/busy wait */
+	i = (c>>5)&0x7;
+	if(i != 7)
+		ct->otherwait = ttiming(cis, i);		/* reserved wait */
+}
+
+static void
+iospaces(Cisdat *cis, PCMconftab *ct)
+{
+	uchar c;
+	int i, nio;
+
+	ct->nio = 0;
+	if(readc(cis, &c) != 1)
+		return;
+
+	ct->bit16 = ((c>>5)&3) >= 2;
+	if(!(c & 0x80)){
+		ct->io[0].start = 0;
+		ct->io[0].len = 1<<(c&0x1f);
+		ct->nio = 1;
+		return;
+	}
+
+	if(readc(cis, &c) != 1)
+		return;
+
+	/*
+	 * For each of the range descriptions read the
+	 * start address and the length (value is length-1).
+	 */
+	nio = (c&0xf)+1;
+	for(i = 0; i < nio; i++){
+		ct->io[i].start = getlong(cis, (c>>4)&0x3);
+		ct->io[i].len = getlong(cis, (c>>6)&0x3)+1;
+	}
+	ct->nio = nio;
+}
+
+static void
+irq(Cisdat *cis, PCMconftab *ct)
+{
+	uchar c;
+
+	if(readc(cis, &c) != 1)
+		return;
+	ct->irqtype = c & 0xe0;
+	if(c & 0x10)
+		ct->irqs = getlong(cis, 2);
+	else
+		ct->irqs = 1<<(c&0xf);
+	ct->irqs &= 0xDEB8;		/* levels available to card */
+}
+
+static void
+memspace(Cisdat *cis, int asize, int lsize, int host)
+{
+	ulong haddress, address, len;
+
+	len = getlong(cis, lsize)*256;
+	address = getlong(cis, asize)*256;
+	USED(len, address);
+	if(host){
+		haddress = getlong(cis, asize)*256;
+		USED(haddress);
+	}
+}
+
+static void
+tentry(Cardbus *cb, Cisdat *cis, int )
+{
+	uchar c, i, feature;
+	PCMconftab *ct;
+	Pcminfo *pi;
+
+	pi = &cb->linfo;
+	if(pi->nctab >= nelem(pi->ctab))
+		return;
+	if(readc(cis, &c) != 1)
+		return;
+	ct = &pi->ctab[pi->nctab++];
+
+	/* copy from last default config */
+	if(pi->defctab)
+		*ct = *pi->defctab;
+
+	ct->index = c & 0x3f;
+
+	/* is this the new default? */
+	if(c & 0x40)
+		pi->defctab = ct;
+
+	/* memory wait specified? */
+	if(c & 0x80){
+		if(readc(cis, &i) != 1)
+			return;
+		if(i&0x80)
+			ct->memwait = 1;
+	}
+
+	if(readc(cis, &feature) != 1)
+		return;
+	switch(feature&0x3){
+	case 1:
+		ct->vpp1 = ct->vpp2 = power(cis);
+		break;
+	case 2:
+		power(cis);
+		ct->vpp1 = ct->vpp2 = power(cis);
+		break;
+	case 3:
+		power(cis);
+		ct->vpp1 = power(cis);
+		ct->vpp2 = power(cis);
+		break;
+	default:
+		break;
+	}
+	if(feature&0x4)
+		timing(cis, ct);
+	if(feature&0x8)
+		iospaces(cis, ct);
+	if(feature&0x10)
+		irq(cis, ct);
+	switch((feature>>5)&0x3){
+	case 1:
+		memspace(cis, 0, 2, 0);
+		break;
+	case 2:
+		memspace(cis, 2, 2, 0);
+		break;
+	case 3:
+		if(readc(cis, &c) != 1)
+			return;
+		for(i = 0; i <= (c&0x7); i++)
+			memspace(cis, (c>>5)&0x3, (c>>3)&0x3, c&0x80);
+		break;
+	}
+}
+
+static void
+i82365probe(Cardbus *cb, int lindex, int ldata)
+{
+	uchar c, id;
+	int dev = 0;	/* According to the Ricoh spec 00->3F _and_ 80->BF seem
+				     to be the same socket A (ditto for B). */
+
+	outb(lindex, Rid + (dev<<7));
+	id = inb(ldata);
+	if((id & 0xf0) != 0x80)
+		return;		/* not a memory & I/O card */
+	if((id & 0x0f) == 0x00)
+		return;		/* no revision number, not possible */
+
+	cb->lindex = lindex;
+	cb->ldata = ldata;
+	cb->ltype = Ti82365;
+	cb->lbase = (int)(cb - cbslots) * 0x40;
+
+	switch(id){
+	case 0x82:
+	case 0x83:
+	case 0x84:
+		/* could be a cirrus */
+		outb(cb->lindex, Rchipinfo + (dev<<7));
+		outb(cb->ldata, 0);
+		c = inb(cb->ldata);
+		if((c & 0xc0) != 0xc0)
+			break;
+		c = inb(cb->ldata);
+		if((c & 0xc0) != 0x00)
+			break;
+		if(c & 0x20){
+			cb->ltype = Tpd6720;
+		} else {
+			cb->ltype = Tpd6710;
+		}
+		break;
+	}
+
+	/* if it's not a Cirrus, it could be a Vadem... */
+	if(cb->ltype == Ti82365){
+		/* unlock the Vadem extended regs */
+		outb(cb->lindex, 0x0E + (dev<<7));
+		outb(cb->lindex, 0x37 + (dev<<7));
+
+		/* make the id register show the Vadem id */
+		outb(cb->lindex, 0x3A + (dev<<7));
+		c = inb(cb->ldata);
+		outb(cb->ldata, c|0xC0);
+		outb(cb->lindex, Rid + (dev<<7));
+		c = inb(cb->ldata);
+		if(c & 0x08)
+			cb->ltype = Tvg46x;
+
+		/* go back to Intel compatible id */
+		outb(cb->lindex, 0x3A + (dev<<7));
+		c = inb(cb->ldata);
+		outb(cb->ldata, c & ~0xC0);
+	}
+}
+
+static int
+vcode(int volt)
+{
+	switch(volt){
+	case 5:
+		return 1;
+	case 12:
+		return 2;
+	default:
+		return 0;
+	}
+}
+
--- /dev/null
+++ b/os/pc/devpnp.c
@@ -1,0 +1,652 @@
+/*
+ *	ISA PNP 1.0 support + access to PCI configuration space
+ *
+ *	TODO
+ *		- implement PNP card configuration (setting io bases etc)
+ *		- write user program to drive PNP configuration...
+ *		- extend PCI raw access to configuration space (writes, byte/short access?)
+ *		- implement PCI access to memory/io space/BIOS ROM
+ *		- use c->aux instead of performing lookup on each read/write?
+ */
+#include	"u.h"
+#include	"../port/lib.h"
+#include	"mem.h"
+#include	"dat.h"
+#include	"fns.h"
+#include	"io.h"
+#include	"../port/error.h"
+
+typedef struct Pnp Pnp;
+typedef struct Card Card;
+
+struct Pnp
+{
+	QLock;
+	int		rddata;
+	int		debug;
+	Card		*cards;
+};
+
+struct Card
+{
+	int		csn;
+	ulong	id1;
+	ulong	id2;
+	char		*cfgstr;
+	int		ncfg;
+	Card*	next;
+};
+
+static Pnp	pnp;
+
+#define	DPRINT	if(pnp.debug) print
+#define	XPRINT	if(1) print
+
+enum {
+	Address = 0x279,
+	WriteData = 0xa79,
+
+	Qtopdir = 0,
+
+	Qpnpdir,
+	Qpnpctl,
+	Qcsnctl,
+	Qcsnraw,
+
+	Qpcidir,
+	Qpcictl,
+	Qpciraw,
+};
+
+#define TYPE(q)		((ulong)(q).path & 0x0F)
+#define CSN(q)		(((ulong)(q).path>>4) & 0xFF)
+#define QID(c, t)	(((c)<<4)|(t))
+
+static Dirtab topdir[] = {
+	".",	{ Qtopdir, 0, QTDIR },	0,	0555,
+	"pnp",	{ Qpnpdir, 0, QTDIR },	0,	0555,
+	"pci",	{ Qpcidir, 0, QTDIR },	0,	0555,
+};
+
+static Dirtab pnpdir[] = {
+	".",	{ Qpnpdir, 0, QTDIR },	0,	0555,
+	"ctl",	{ Qpnpctl, 0, 0 },	0,	0666,
+};
+
+extern Dev pnpdevtab;
+static int wrconfig(Card*, char*);
+
+static char key[32] =
+{
+	0x6A, 0xB5, 0xDA, 0xED, 0xF6, 0xFB, 0x7D, 0xBE,
+	0xDF, 0x6F, 0x37, 0x1B, 0x0D, 0x86, 0xC3, 0x61,
+	0xB0, 0x58, 0x2C, 0x16, 0x8B, 0x45, 0xA2, 0xD1,
+	0xE8, 0x74, 0x3A, 0x9D, 0xCE, 0xE7, 0x73, 0x39,
+};
+
+static void
+cmd(int reg, int val)
+{
+	outb(Address, reg);
+	outb(WriteData, val);
+}
+
+/* Send initiation key, putting each card in Sleep state */
+static void
+initiation(void)
+{
+	int i;
+
+	/* ensure each card's LFSR is reset */
+	outb(Address, 0x00);
+	outb(Address, 0x00);
+
+	/* send initiation key */
+	for (i = 0; i < 32; i++)
+		outb(Address, key[i]);
+}
+
+/* isolation protocol... */
+static int
+readbit(int rddata)
+{
+	int r1, r2;
+
+	r1 = inb(rddata);
+	r2 = inb(rddata);
+	microdelay(250);
+	return (r1 == 0x55) && (r2 == 0xaa);
+}
+
+static int
+isolate(int rddata, ulong *id1, ulong *id2)
+{
+	int i, csum, bit;
+	uchar *p, id[9];
+
+	outb(Address, 0x01);	/* point to serial isolation register */
+	delay(1);
+	csum = 0x6a;
+	for(i = 0; i < 64; i++){
+		bit = readbit(rddata);
+		csum = (csum>>1) | (((csum&1) ^ ((csum>>1)&1) ^ bit)<<7);
+		p = &id[i>>3];
+		*p = (*p>>1) | (bit<<7);
+	}
+	for(; i < 72; i++){
+		p = &id[i>>3];
+		*p = (*p>>1) | (readbit(rddata)<<7);
+	}
+	*id1 = (id[3]<<24)|(id[2]<<16)|(id[1]<<8)|id[0];
+	*id2 = (id[7]<<24)|(id[6]<<16)|(id[5]<<8)|id[4];
+	if(*id1 == 0)
+		return 0;
+	if(id[8] != csum)
+		DPRINT("pnp: bad checksum id1 %lux id2 %lux csum %x != %x\n", *id1, *id2, csum, id[8]); /**/
+	return id[8] == csum;
+}
+
+static int
+getresbyte(int rddata)
+{
+	int tries = 0;
+
+	outb(Address, 0x05);
+	while ((inb(rddata) & 1) == 0)
+		if (tries++ > 1000000)
+			error("pnp: timeout waiting for resource data\n");
+	outb(Address, 0x04);
+	return inb(rddata);
+}
+
+static char *
+serial(ulong id1, ulong id2)
+{
+	int i1, i2, i3;
+	ulong x;
+	static char buf[20];
+
+	i1 = (id1>>2)&31;
+	i2 = ((id1<<3)&24)+((id1>>13)&7);
+	i3 = (id1>>8)&31;
+	x = (id1>>8)&0xff00|(id1>>24)&0x00ff;
+	if (i1 > 0 && i1 < 27 && i2 > 0 && i2 < 27 && i3 > 0 && i3 < 27 && (id1 & (1<<7)) == 0)
+		snprint(buf, sizeof(buf), "%c%c%c%.4lux.%lux", 'A'+i1-1, 'A'+i2-1, 'A'+i3-1, x, id2);
+	else
+		snprint(buf, sizeof(buf), "%.4lux%.4lux.%lux", (id1<<8)&0xff00|(id1>>8)&0x00ff, x, id2);
+	return buf;
+}
+
+static Card *
+findcsn(int csn, int create, int dolock)
+{
+	Card *c, *nc, **l;
+
+	if(dolock)
+		qlock(&pnp);
+	l = &pnp.cards;
+	for(c = *l; c != nil; c = *l) {
+		if(c->csn == csn)
+			goto done;
+		if(c->csn > csn)
+			break;
+		l = &c->next;
+	}
+	if(create) {
+		*l = nc = malloc(sizeof(Card));
+		nc->next = c;
+		nc->csn = csn;
+		c = nc;
+	}
+done:
+	if(dolock)
+		qunlock(&pnp);
+	return c;
+}
+
+static int
+newcsn(void)
+{
+	int csn;
+	Card *c;
+
+	csn = 1;
+	for(c = pnp.cards; c != nil; c = c->next) {
+		if(c->csn > csn)
+			break;
+		csn = c->csn+1;
+	}
+	return csn;
+}
+
+static int
+pnpncfg(int rddata)
+{
+	int i, n, x, ncfg, n1, n2;
+
+	ncfg = 0;
+	for (;;) {
+		x = getresbyte(rddata);
+		if((x & 0x80) == 0) {
+			n = (x&7)+1;
+			for(i = 1; i < n; i++)
+				getresbyte(rddata);
+		}
+		else {
+			n1 = getresbyte(rddata);
+			n2 = getresbyte(rddata);
+			n = (n2<<8)|n1 + 3;
+			for (i = 3; i < n; i++)
+				getresbyte(rddata);
+		}
+		ncfg += n;
+		if((x>>3) == 0x0f)
+			break;
+	}
+	return ncfg;
+}
+
+/* look for cards, and assign them CSNs */
+static int
+pnpscan(int rddata, int dawn)
+{
+	Card *c;
+	int csn;
+	ulong id1, id2;
+
+	initiation();				/* upsilon sigma */
+	cmd(0x02, 0x04+0x01);		/* reset CSN on all cards and reset logical devices */
+	delay(1);					/* delay after resetting cards */
+
+	cmd(0x03, 0);				/* Wake all cards with a CSN of 0 */
+	cmd(0x00, rddata>>2);		/* Set the READ_DATA port on all cards */
+	while(isolate(rddata, &id1, &id2)) {
+		for(c = pnp.cards; c != nil; c = c->next)
+			if(c->id1 == id1 && c->id2 == id2)
+				break;
+		if(c == nil) {
+			csn = newcsn();
+			c = findcsn(csn, 1, 0);
+			c->id1 = id1;
+			c->id2 = id2;
+		}
+		else if(c->cfgstr != nil) {
+			if(!wrconfig(c, c->cfgstr))
+				print("pnp%d: bad cfg: %s\n", c->csn, c->cfgstr);
+			c->cfgstr = nil;
+		}
+		cmd(0x06, c->csn);		/* set the card's csn */
+		if(dawn)
+			print("pnp%d: %s\n", c->csn, serial(id1, id2));
+		c->ncfg = pnpncfg(rddata);
+		cmd(0x03, 0);		/* Wake all cards with a CSN of 0, putting this card to sleep */
+	}
+	cmd(0x02, 0x02);			/* return cards to Wait for Key state */
+	if(pnp.cards != 0) {
+		pnp.rddata = rddata;
+		return 1;
+	}
+	return 0;
+}
+
+static void
+pnpreset(void)
+{
+	Card *c;
+	ulong id1, id2;
+	int csn, i1, i2, i3, x;
+	char *s, *p, buf[20];
+	ISAConf isa;
+
+	memset(&isa, 0, sizeof(ISAConf));
+	pnp.rddata = -1;
+	if (isaconfig("pnp", 0, &isa) == 0)
+		return;
+	if(isa.port < 0x203 || isa.port > 0x3ff)
+		return;
+	for(csn = 1; csn < 256; csn++) {
+		sprint(buf, "pnp%d", csn);
+		s = getconf(buf);
+		if(s == 0)
+			continue;
+		if(strlen(s) < 8 || s[7] != '.' || s[0] < 'A' || s[0] > 'Z' || s[1] < 'A' || s[1] > 'Z' || s[2] < 'A' || s[2] > 'Z') {
+bad:
+			print("pnp%d: bad conf string %s\n", csn, s);
+			continue;	
+		}
+		i1 = s[0]-'A'+1;
+		i2 = s[1]-'A'+1;
+		i3 = s[2]-'A'+1;
+		x = strtoul(&s[3], 0, 16);
+		id1 = (i1<<2)|((i2>>3)&3)|((i2&7)<<13)|(i3<<8)|((x&0xff)<<24)|((x&0xff00)<<8);
+		id2 = strtoul(&s[8], &p, 16);
+		if(*p == ' ')
+			p++;
+		else if(*p == '\0')
+			p = nil;
+		else
+			goto bad;
+		c = findcsn(csn, 1, 0);
+		c->id1 = id1;
+		c->id2 = id2;
+		c->cfgstr = p;
+	}
+	pnpscan(isa.port, 1);
+}
+
+static int
+csngen(Chan *c, int t, int csn, Card *cp, Dir *dp)
+{
+	Qid q;
+
+	switch(t) {
+	case Qcsnctl:
+		q = (Qid){QID(csn, Qcsnctl), 0, 0};
+		sprint(up->genbuf, "csn%dctl", csn);
+		devdir(c, q, up->genbuf, 0, eve, 0664, dp);
+		return 1;
+	case Qcsnraw:
+		q = (Qid){QID(csn, Qcsnraw), 0, 0};
+		sprint(up->genbuf, "csn%draw", csn);
+		devdir(c, q, up->genbuf, cp->ncfg, eve, 0444, dp);
+		return 1;
+	}
+	return -1;
+}
+
+static int
+pcigen(Chan *c, int t, int tbdf, Dir *dp)
+{
+	Qid q;
+
+	q = (Qid){BUSBDF(tbdf)|t, 0, 0};
+	switch(t) {
+	case Qpcictl:
+		sprint(up->genbuf, "%d.%d.%dctl", BUSBNO(tbdf), BUSDNO(tbdf), BUSFNO(tbdf));
+		devdir(c, q, up->genbuf, 0, eve, 0444, dp);
+		return 1;
+	case Qpciraw:
+		sprint(up->genbuf, "%d.%d.%draw", BUSBNO(tbdf), BUSDNO(tbdf), BUSFNO(tbdf));
+		devdir(c, q, up->genbuf, 128, eve, 0444, dp);
+		return 1;
+	}
+	return -1;
+}
+
+static int
+pnpgen(Chan *c, char *, Dirtab*, int, int s, Dir *dp)
+{
+	Qid q;
+	Card *cp;
+	Pcidev *p;
+	int csn, tbdf;
+
+	switch(TYPE(c->qid)){
+	case Qtopdir:
+		if(s == DEVDOTDOT){
+			q = (Qid){QID(0, Qtopdir), 0, QTDIR};
+			sprint(up->genbuf, "#%C", pnpdevtab.dc);
+			devdir(c, q, up->genbuf, 0, eve, 0555, dp);
+			return 1;
+		}
+		return devgen(c, nil, topdir, nelem(topdir), s, dp);
+	case Qpnpdir:
+		if(s == DEVDOTDOT){
+			q = (Qid){QID(0, Qtopdir), 0, QTDIR};
+			sprint(up->genbuf, "#%C", pnpdevtab.dc);
+			devdir(c, q, up->genbuf, 0, eve, 0555, dp);
+			return 1;
+		}
+		if(s < nelem(pnpdir)-1)
+			return devgen(c, nil, pnpdir, nelem(pnpdir), s, dp);
+		s -= nelem(pnpdir)-1;
+		qlock(&pnp);
+		cp = pnp.cards;
+		while(s >= 2 && cp != nil) {
+			s -= 2;
+			cp = cp->next;
+		}
+		qunlock(&pnp);
+		if(cp == nil)
+			return -1;
+		return csngen(c, s+Qcsnctl, cp->csn, cp, dp);
+	case Qpnpctl:
+		return devgen(c, nil, pnpdir, nelem(pnpdir), s, dp);
+	case Qcsnctl:
+	case Qcsnraw:
+		csn = CSN(c->qid);
+		cp = findcsn(csn, 0, 1);
+		if(cp == nil)
+			return -1;
+		return csngen(c, TYPE(c->qid), csn, cp, dp);
+	case Qpcidir:
+		if(s == DEVDOTDOT){
+			q = (Qid){QID(0, Qtopdir), 0, QTDIR};
+			sprint(up->genbuf, "#%C", pnpdevtab.dc);
+			devdir(c, q, up->genbuf, 0, eve, 0555, dp);
+			return 1;
+		}
+		p = pcimatch(nil, 0, 0);
+		while(s >= 2 && p != nil) {
+			p = pcimatch(p, 0, 0);
+			s -= 2;
+		}
+		if(p == nil)
+			return -1;
+		return pcigen(c, s+Qpcictl, p->tbdf, dp);
+	case Qpcictl:
+	case Qpciraw:
+		tbdf = MKBUS(BusPCI, 0, 0, 0)|BUSBDF((ulong)c->qid.path);
+		p = pcimatchtbdf(tbdf);
+		if(p == nil)
+			return -1;
+		return pcigen(c, TYPE(c->qid), tbdf, dp);
+	default:
+		break;
+	}
+	return -1;
+}
+
+static Chan*
+pnpattach(char *spec)
+{
+	return devattach(pnpdevtab.dc, spec);
+}
+
+Walkqid*
+pnpwalk(Chan* c, Chan *nc, char** name, int nname)
+{
+	return devwalk(c, nc, name, nname, (Dirtab *)0, 0, pnpgen);
+}
+
+static int
+pnpstat(Chan* c, uchar* dp, int n)
+{
+	return devstat(c, dp, n, (Dirtab *)0, 0L, pnpgen);
+}
+
+static Chan*
+pnpopen(Chan *c, int omode)
+{
+	c = devopen(c, omode, (Dirtab*)0, 0, pnpgen);
+	switch(TYPE(c->qid)){
+	default:
+		break;
+	}
+	return c;
+}
+
+static void
+pnpclose(Chan*)
+{
+}
+
+static long
+pnpread(Chan *c, void *va, long n, vlong offset)
+{
+	ulong x;
+	Card *cp;
+	Pcidev *p;
+	char buf[256], *ebuf, *w;
+	char *a = va;
+	int csn, i, tbdf, r;
+
+	switch(TYPE(c->qid)){
+	case Qtopdir:
+	case Qpnpdir:
+	case Qpcidir:
+		return devdirread(c, a, n, (Dirtab *)0, 0L, pnpgen);
+	case Qpnpctl:
+		if(pnp.rddata > 0)
+			sprint(up->genbuf, "enabled 0x%x\n", pnp.rddata);
+		else
+			sprint(up->genbuf, "disabled\n");
+		return readstr(offset, a, n, up->genbuf);
+	case Qcsnraw:
+		csn = CSN(c->qid);
+		cp = findcsn(csn, 0, 1);
+		if(cp == nil)
+			error(Egreg);
+		if(offset+n > cp->ncfg)
+			n = cp->ncfg - offset;
+		qlock(&pnp);
+		initiation();
+		cmd(0x03, csn);				/* Wake up the card */
+		for(i = 0; i < offset+9; i++)		/* 9 == skip serial + csum */
+			getresbyte(pnp.rddata);
+		for(i = 0; i < n; i++)
+			a[i] = getresbyte(pnp.rddata);
+		cmd(0x03, 0);					/* Wake all cards with a CSN of 0, putting this card to sleep */
+		cmd(0x02, 0x02);				/* return cards to Wait for Key state */
+		qunlock(&pnp);
+		break;
+	case Qcsnctl:
+		csn = CSN(c->qid);
+		cp = findcsn(csn, 0, 1);
+		if(cp == nil)
+			error(Egreg);
+		sprint(up->genbuf, "%s\n", serial(cp->id1, cp->id2));
+		return readstr(offset, a, n, up->genbuf);
+	case Qpcictl:
+		tbdf = MKBUS(BusPCI, 0, 0, 0)|BUSBDF((ulong)c->qid.path);
+		p = pcimatchtbdf(tbdf);
+		if(p == nil)
+			error(Egreg);
+		ebuf = buf+sizeof buf-1;	/* -1 for newline */
+		w = seprint(buf, ebuf, "%.2x.%.2x.%.2x %.4x/%.4x %3d",
+			p->ccrb, p->ccru, p->ccrp, p->vid, p->did, p->intl);
+		for(i=0; i<nelem(p->mem); i++){
+			if(p->mem[i].size == 0)
+				continue;
+			w = seprint(w, ebuf, " %d:%.8lux %d", i, p->mem[i].bar, p->mem[i].size);
+		}
+		*w++ = '\n';
+		*w = '\0';
+		return readstr(offset, a, n, buf);
+	case Qpciraw:
+		tbdf = MKBUS(BusPCI, 0, 0, 0)|BUSBDF((ulong)c->qid.path);
+		p = pcimatchtbdf(tbdf);
+		if(p == nil)
+			error(Egreg);
+		if(offset > 256)
+			return 0;
+		if(n+offset > 256)
+			n = 256-offset;
+		if(offset%4)
+			error(Ebadarg);
+		r = offset;
+		for(i = 0; i+4 <= n; i+=4) {
+			x = pcicfgr32(p, r);
+			a[0] = x;
+			a[1] = (x>>8);
+			a[2] = (x>>16);
+			a[3] = (x>>24);
+			a += 4;
+			r += 4;
+		}
+		return i;
+	default:
+		error(Egreg);
+	}
+	return n;
+}
+
+static long
+pnpwrite(Chan *c, void *a, long n, vlong)
+{
+	int csn;
+	Card *cp;
+	ulong port;
+	char buf[256];
+
+	if(n >= sizeof(buf))
+		n = sizeof(buf)-1;
+	strncpy(buf, a, n);
+	buf[n] = 0;
+
+	switch(TYPE(c->qid)){
+	case Qpnpctl:
+		if(strncmp(buf, "port ", 5) == 0) {
+			port = strtoul(buf+5, 0, 0);
+			if(port < 0x203 || port > 0x3ff)
+				error("bad value for rddata port");
+			qlock(&pnp);
+			if(waserror()) {
+				qunlock(&pnp);
+				nexterror();
+			}
+			if(pnp.rddata > 0)
+				error("pnp port already set");
+			if(!pnpscan(port, 0))
+				error("no cards found");
+			qunlock(&pnp);
+			poperror();
+		}
+		else if(strncmp(buf, "debug ", 6) == 0)
+			pnp.debug = strtoul(buf+6, 0, 0);
+		else
+			error(Ebadctl);
+		break;
+	case Qcsnctl:
+		csn = CSN(c->qid);
+		cp = findcsn(csn, 0, 1);
+		if(cp == nil)
+			error(Egreg);
+		if(!wrconfig(cp, buf))
+			error(Ebadctl);
+		break;
+	default:
+		error(Egreg);
+	}
+	return n;
+}
+
+static int
+wrconfig(Card *c, char *cmd)
+{
+	/* This should implement setting of I/O bases, etc */
+	USED(c, cmd);
+	return 1;
+}
+
+
+Dev pnpdevtab = {
+	'$',
+	"pnp",
+
+	pnpreset,
+	devinit,
+	devshutdown,
+	pnpattach,
+	pnpwalk,
+	pnpstat,
+	pnpopen,
+	devcreate,
+	pnpclose,
+	pnpread,
+	devbread,
+	pnpwrite,
+	devbwrite,
+	devremove,
+	devwstat,
+};
--- /dev/null
+++ b/os/pc/devrtc.c
@@ -1,0 +1,461 @@
+#include	"u.h"
+#include	"../port/lib.h"
+#include	"mem.h"
+#include	"dat.h"
+#include	"fns.h"
+#include	"../port/error.h"
+
+/*
+ *  real time clock and non-volatile ram
+ */
+
+enum {
+	Paddr=		0x70,	/* address port */
+	Pdata=		0x71,	/* data port */
+
+	Seconds=	0x00,
+	Minutes=	0x02,
+	Hours=		0x04, 
+	Mday=		0x07,
+	Month=		0x08,
+	Year=		0x09,
+	Status=		0x0A,
+
+	Nvoff=		128,	/* where usable nvram lives */
+	Nvsize=		256,
+
+	Nbcd=		6,
+};
+
+typedef struct Rtc	Rtc;
+struct Rtc
+{
+	int	sec;
+	int	min;
+	int	hour;
+	int	mday;
+	int	mon;
+	int	year;
+};
+
+
+enum{
+	Qdir = 0,
+	Qrtc,
+	Qnvram,
+};
+
+Dirtab rtcdir[]={
+	".",	{Qdir, 0, QTDIR},	0,	0555,
+	"nvram",	{Qnvram, 0},	Nvsize,	0664,
+	"rtc",		{Qrtc, 0},	0,	0664,
+};
+
+static ulong rtc2sec(Rtc*);
+static void sec2rtc(ulong, Rtc*);
+
+void
+rtcinit(void)
+{
+	if(ioalloc(Paddr, 2, 0, "rtc/nvr") < 0)
+		panic("rtcinit: ioalloc failed");
+}
+
+static Chan*
+rtcattach(char* spec)
+{
+	return devattach('r', spec);
+}
+
+static Walkqid*	 
+rtcwalk(Chan* c, Chan *nc, char** name, int nname)
+{
+	return devwalk(c, nc, name, nname, rtcdir, nelem(rtcdir), devgen);
+}
+
+static int	 
+rtcstat(Chan* c, uchar* dp, int n)
+{
+	return devstat(c, dp, n, rtcdir, nelem(rtcdir), devgen);
+}
+
+static Chan*
+rtcopen(Chan* c, int omode)
+{
+	omode = openmode(omode);
+	switch((ulong)c->qid.path){
+	case Qrtc:
+		if(strcmp(up->env->user, eve)!=0 && omode!=OREAD)
+			error(Eperm);
+		break;
+	case Qnvram:
+		if(strcmp(up->env->user, eve)!=0)
+			error(Eperm);
+	}
+	return devopen(c, omode, rtcdir, nelem(rtcdir), devgen);
+}
+
+static void	 
+rtcclose(Chan*)
+{
+}
+
+#define GETBCD(o) ((bcdclock[o]&0xf) + 10*(bcdclock[o]>>4))
+
+static long	 
+_rtctime(void)
+{
+	uchar bcdclock[Nbcd];
+	Rtc rtc;
+	int i;
+
+	/* don't do the read until the clock is no longer busy */
+	for(i = 0; i < 10000; i++){
+		outb(Paddr, Status);
+		if(inb(Pdata) & 0x80)
+			continue;
+
+		/* read clock values */
+		outb(Paddr, Seconds);	bcdclock[0] = inb(Pdata);
+		outb(Paddr, Minutes);	bcdclock[1] = inb(Pdata);
+		outb(Paddr, Hours);	bcdclock[2] = inb(Pdata);
+		outb(Paddr, Mday);	bcdclock[3] = inb(Pdata);
+		outb(Paddr, Month);	bcdclock[4] = inb(Pdata);
+		outb(Paddr, Year);	bcdclock[5] = inb(Pdata);
+
+		outb(Paddr, Status);
+		if((inb(Pdata) & 0x80) == 0)
+			break;
+	}
+
+	/*
+	 *  convert from BCD
+	 */
+	rtc.sec = GETBCD(0);
+	rtc.min = GETBCD(1);
+	rtc.hour = GETBCD(2);
+	rtc.mday = GETBCD(3);
+	rtc.mon = GETBCD(4);
+	rtc.year = GETBCD(5);
+
+	/*
+	 *  the world starts jan 1 1970
+	 */
+	if(rtc.year < 70)
+		rtc.year += 2000;
+	else
+		rtc.year += 1900;
+	return rtc2sec(&rtc);
+}
+
+static Lock nvrtlock;
+
+long
+rtctime(void)
+{
+	int i;
+	long t, ot;
+
+	ilock(&nvrtlock);
+
+	/* loop till we get two reads in a row the same */
+	t = _rtctime();
+	for(i = 0; i < 100; i++){
+		ot = t;
+		t = _rtctime();
+		if(ot == t)
+			break;
+	}
+	if(i == 100) print("we are boofheads\n");
+
+	iunlock(&nvrtlock);
+
+	return t;
+}
+
+static long	 
+rtcread(Chan* c, void* buf, long n, vlong off)
+{
+	ulong t;
+	char *a, *start;
+	ulong offset = off;
+
+	if(c->qid.type & QTDIR)
+		return devdirread(c, buf, n, rtcdir, nelem(rtcdir), devgen);
+
+	switch((ulong)c->qid.path){
+	case Qrtc:
+		t = rtctime();
+		n = readnum(offset, buf, n, t, 12);
+		return n;
+	case Qnvram:
+		if(n == 0)
+			return 0;
+		if(n > Nvsize)
+			n = Nvsize;
+		a = start = smalloc(n);
+
+		ilock(&nvrtlock);
+		for(t = offset; t < offset + n; t++){
+			if(t >= Nvsize)
+				break;
+			outb(Paddr, Nvoff+t);
+			*a++ = inb(Pdata);
+		}
+		iunlock(&nvrtlock);
+
+		if(waserror()){
+			free(start);
+			nexterror();
+		}
+		memmove(buf, start, t - offset);
+		poperror();
+
+		free(start);
+		return t - offset;
+	}
+	error(Ebadarg);
+	return 0;
+}
+
+#define PUTBCD(n,o) bcdclock[o] = (n % 10) | (((n / 10) % 10)<<4)
+
+static long	 
+rtcwrite(Chan* c, void* buf, long n, vlong off)
+{
+	int t;
+	char *a, *start;
+	Rtc rtc;
+	ulong secs;
+	uchar bcdclock[Nbcd];
+	char *cp, *ep;
+	ulong offset = off;
+
+	if(offset!=0)
+		error(Ebadarg);
+
+
+	switch((ulong)c->qid.path){
+	case Qrtc:
+		/*
+		 *  read the time
+		 */
+		cp = ep = buf;
+		ep += n;
+		while(cp < ep){
+			if(*cp>='0' && *cp<='9')
+				break;
+			cp++;
+		}
+		secs = strtoul(cp, 0, 0);
+	
+		/*
+		 *  convert to bcd
+		 */
+		sec2rtc(secs, &rtc);
+		PUTBCD(rtc.sec, 0);
+		PUTBCD(rtc.min, 1);
+		PUTBCD(rtc.hour, 2);
+		PUTBCD(rtc.mday, 3);
+		PUTBCD(rtc.mon, 4);
+		PUTBCD(rtc.year, 5);
+
+		/*
+		 *  write the clock
+		 */
+		ilock(&nvrtlock);
+		outb(Paddr, Seconds);	outb(Pdata, bcdclock[0]);
+		outb(Paddr, Minutes);	outb(Pdata, bcdclock[1]);
+		outb(Paddr, Hours);	outb(Pdata, bcdclock[2]);
+		outb(Paddr, Mday);	outb(Pdata, bcdclock[3]);
+		outb(Paddr, Month);	outb(Pdata, bcdclock[4]);
+		outb(Paddr, Year);	outb(Pdata, bcdclock[5]);
+		iunlock(&nvrtlock);
+		return n;
+	case Qnvram:
+		if(n == 0)
+			return 0;
+		if(n > Nvsize)
+			n = Nvsize;
+	
+		start = a = smalloc(n);
+		if(waserror()){
+			free(start);
+			nexterror();
+		}
+		memmove(a, buf, n);
+		poperror();
+
+		ilock(&nvrtlock);
+		for(t = offset; t < offset + n; t++){
+			if(t >= Nvsize)
+				break;
+			outb(Paddr, Nvoff+t);
+			outb(Pdata, *a++);
+		}
+		iunlock(&nvrtlock);
+
+		free(start);
+		return t - offset;
+	}
+	error(Ebadarg);
+	return 0;
+}
+
+Dev rtcdevtab = {
+	'r',
+	"rtc",
+
+	devreset,
+	rtcinit,
+	devshutdown,
+	rtcattach,
+	rtcwalk,
+	rtcstat,
+	rtcopen,
+	devcreate,
+	rtcclose,
+	rtcread,
+	devbread,
+	rtcwrite,
+	devbwrite,
+	devremove,
+	devwstat,
+};
+
+#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
+ */
+static ulong
+rtc2sec(Rtc *rtc)
+{
+	ulong secs;
+	int i;
+	int *d2m;
+
+	secs = 0;
+
+	/*
+	 *  seconds per year
+	 */
+	for(i = 1970; i < rtc->year; i++){
+		d2m = yrsize(i);
+		secs += d2m[0] * SEC2DAY;
+	}
+
+	/*
+	 *  seconds per month
+	 */
+	d2m = yrsize(rtc->year);
+	for(i = 1; i < rtc->mon; i++)
+		secs += d2m[i] * SEC2DAY;
+
+	secs += (rtc->mday-1) * SEC2DAY;
+	secs += rtc->hour * SEC2HOUR;
+	secs += rtc->min * SEC2MIN;
+	secs += rtc->sec;
+
+	return secs;
+}
+
+/*
+ *  compute rtc from seconds since Jan 1 1970
+ */
+static void
+sec2rtc(ulong secs, Rtc *rtc)
+{
+	int d;
+	long hms, day;
+	int *d2m;
+
+	/*
+	 * break initial number into days
+	 */
+	hms = secs % SEC2DAY;
+	day = secs / SEC2DAY;
+	if(hms < 0) {
+		hms += SEC2DAY;
+		day -= 1;
+	}
+
+	/*
+	 * generate hours:minutes:seconds
+	 */
+	rtc->sec = hms % 60;
+	d = hms / 60;
+	rtc->min = d % 60;
+	d /= 60;
+	rtc->hour = d;
+
+	/*
+	 * year number
+	 */
+	if(day >= 0)
+		for(d = 1970; day >= *yrsize(d); d++)
+			day -= *yrsize(d);
+	else
+		for (d = 1970; day < 0; d--)
+			day += *yrsize(d-1);
+	rtc->year = d;
+
+	/*
+	 * generate month
+	 */
+	d2m = yrsize(rtc->year);
+	for(d = 1; day >= d2m[d]; d++)
+		day -= d2m[d];
+	rtc->mday = day + 1;
+	rtc->mon = d;
+
+	return;
+}
+
+uchar
+nvramread(int addr)
+{
+	uchar data;
+
+	ilock(&nvrtlock);
+	outb(Paddr, addr);
+	data = inb(Pdata);
+	iunlock(&nvrtlock);
+
+	return data;
+}
+
+void
+nvramwrite(int addr, uchar data)
+{
+	ilock(&nvrtlock);
+	outb(Paddr, addr);
+	outb(Pdata, data);
+	iunlock(&nvrtlock);
+}
--- /dev/null
+++ b/os/pc/devtv.c
@@ -1,0 +1,1826 @@
+/*
+ * Driver for Hauppage TV board
+ *
+ * Control commands:
+ *
+ *	init
+ *	window %d %d %d %d
+ *	colorkey %d %d %d %d %d %d
+ *	capture %d %d %d %d
+ *	capbrightness %d
+ *	capcontrast %d
+ *	capsaturation %d
+ *	caphue %d
+ *	capbw %d
+ *	brightness %d
+ *	contrast %d
+ *	saturation %d
+ *	source %d
+ *	svideo %d
+ *	format %d
+ *	channel %d %d
+ *	signal
+ *	volume %d [ %d ]
+ *	bass %d
+ *	treble %d
+ *	freeze %d
+ */
+#include	"u.h"
+#include	"../port/lib.h"
+#include	"mem.h"
+#include	"dat.h"
+#include	"fns.h"
+#include	"../port/error.h"
+#include	"tv.h"
+
+#include	<draw.h>
+
+enum {
+	MemSize=			1,
+	MemAddr=			0xB8000,
+
+	CompressReg=			-14,
+
+	/* smart lock registers */
+	SLReg1=				-2,
+	SLReg2=				-1,
+
+	/* the Bt812 registers */
+	Bt812Index=			-5,
+	Bt812Data=			-6,
+
+	Bt2VideoPresent=		0x40,
+	Bt4ColorBars=			0x40,
+	Bt5YCFormat=			0x80,
+	Bt7TriState=			0x0C,
+
+	/* VxP 500 registers */
+	Vxp500Index=			0,
+	Vxp500Data=			1,
+
+	/* video controller registers */
+	MemoryWindowBaseAddrA=		0x14,
+	MemoryWindowBaseAddrB=		0x15,
+	MemoryPageReg=			0x16,
+	MemoryConfReg=			0x18,
+	ISAControl=			0x30,
+	I2CControl=			0x34,
+	InputVideoConfA=		0x38,
+	InputVideoConfB=		0x39,
+	ISASourceWindowWidthA=		0x3A,
+	ISASourceWindowWidthB=		0x3B,
+	ISASourceWindowHeightA=		0x3C,
+	ISASourceWindowHeightB=		0x3D,
+	InputHorzCropLeftA=		0x40,
+	InputHorzCropLeftB=		0x41,
+	InputHorzCropRightA=		0x44,
+	InputHorzCropRightB=		0x45,
+	InputHorzCropTopA=		0x48,
+	InputHorzCropTopB=		0x49,
+	InputHorzCropBottomA=		0x4C,
+	InputHorzCropBottomB=		0x4D,
+	InputHorzFilter=		0x50,
+	InputHorzScaleControlA=		0x54,
+	InputHorzScaleControlB=		0x55,
+	InputVertInterpolControl=	0x58,
+	InputVertScaleControlA=		0x5C,
+	InputVertScaleControlB=		0x5D,
+	InputFieldPixelBufStatus=	0x64,
+	VideoInputFrameBufDepthA=	0x68,
+	VideoInputFrameBufDepthB=	0x69,
+	AcquisitionControl=		0x6C,
+	AcquisitionAddrA=		0x70,
+	AcquisitionAddrB=		0x71,
+	AcquisitionAddrC=		0x72,
+	VideoBufferLayoutControl=	0x73,
+	CaptureControl=			0x80,
+	CaptureViewPortAddrA=		0x81,
+	CaptureViewPortAddrB=		0x82,
+	CaptureViewPortAddrC=		0x83,
+	CaptureViewPortWidthA=		0x84,
+	CaptureViewPortWidthB=		0x85,
+	CaptureViewPortHeightA=		0x86,
+	CaptureViewPortHeightB=		0x87,
+	CapturePixelBufLow=		0x88,
+	CapturePixelBufHigh=		0x89,
+	CaptureMultiBufDepthA=		0x8A,
+	CaptureMultiBufDepthB=		0x8B,
+	DisplayControl=			0x92,
+	VGAControl=			0x94,
+	OutputProcControlA=		0x96,
+	OutputProcControlB=		0x97,
+        DisplayViewPortStartAddrA=	0xA0,
+        DisplayViewPortStartAddrB=	0xA1,
+        DisplayViewPortStartAddrC=	0xA2,
+	DisplayViewPortWidthA=		0xA4,
+	DisplayViewPortWidthB=		0xA5,
+	DisplayViewPortHeightA=		0xA6,
+	DisplayViewPortHeightB=		0xA7,
+	DisplayViewPortOrigTopA=	0xA8,
+	DisplayViewPortOrigTopB=	0xA9,
+	DisplayViewPortOrigLeftA=	0xAA,
+	DisplayViewPortOrigLeftB=	0xAB,
+	DisplayWindowLeftA=		0xB0,
+	DisplayWindowLeftB=		0xB1,
+	DisplayWindowRightA=		0xB4,
+	DisplayWindowRightB=		0xB5,
+	DisplayWindowTopA=		0xB8,
+	DisplayWindowTopB=		0xB9,
+	DisplayWindowBottomA=		0xBC,
+	DisplayWindowBottomB=		0xBD,
+	OutputVertZoomControlA=		0xC0,
+	OutputVertZoomControlB=		0xC1,
+	OutputHorzZoomControlA=		0xC4,
+	OutputHorzZoomControlB=		0xC5,
+	BrightnessControl=		0xC8,
+	ContrastControl=		0xC9,
+	SaturationControl=		0xCA,
+	VideoOutIntrStatus=		0xD3,
+
+	/* smart lock bits */
+	PixelClk=			0x03,
+	SmartLock=			0x00,
+	FeatureConnector=		0x01,
+	Divider=			0x02,
+	Window=				0x08,
+	KeyWindow=			0x0C,
+	HSyncLow=			0x20,
+	VSyncLow=			0x40,
+
+	ClkBit=				0x01,
+	DataBit=			0x02,
+	HoldBit=			0x04,
+	SelBit=				0x08,
+	DivControl=			0x40,
+
+	/* i2c bus control bits */
+	I2C_Clock=			0x02,
+	I2C_Data=			0x08,
+	I2C_RdClock=			0x10,
+	I2C_RdData=			0x20,
+	I2C_RdData_D=			0x40,
+
+	/* I2C bus addresses */
+	Adr5249=			0x22,	/* teletext decoder */
+	Adr8444=			0x48,	/* 6-bit DAC (TDA 8444) */
+	Adr6300=			0x80,	/* sound fader (TEA 6300) */
+	Adr6320=			0x80,	/* sound fader (TEA 6320T) */
+	AdrTuner=			0xC0,
+
+	/* Philips audio chips */
+	TEA6300=			0,
+	TEA6320T=			1,
+
+	/* input formats */
+	NTSC_M = 0,
+	NTSC_443 = 1,
+	External = 2,
+
+	NTSCCropLeft= 			36,	/* NTSC 3.6 usec */
+	NTSCCropRight=			558,	/* NTSC 55.8 usec */
+
+	/* color control indices */
+	Vxp500Brightness=		1,
+	Vxp500Contrast=			2,
+	Vxp500Saturation=		3,
+	Bt812Brightness=		4,
+	Bt812Contrast=			5,
+	Bt812Saturation=		6,
+	Bt812Hue=			7,
+	Bt812BW=			8,
+
+	/* board revision numbers */
+	RevisionPP=			0,
+	RevisionA=			1,
+	HighQ=				2,
+
+	/* VGA controller registers */
+	VGAMiscOut=			0x3CC,
+	VGAIndex=			0x3D4,
+	VGAData=			0x3D5,
+	VGAHorzTotal=			0x00,
+};
+
+enum {
+	Qdir,
+	Qdata,
+	Qctl,
+};
+
+static
+Dirtab tvtab[]={
+	".",		{Qdir, 0, QTDIR},	0,	0555,
+	"tv",		{Qdata, 0},	0,	0666,
+	"tvctl",	{Qctl, 0},	0,	0666,
+};
+
+static
+int ports[] = {	/* board addresses */
+	0x51C, 0x53C, 0x55C, 0x57C,
+	0x59C, 0x5BC, 0x5DC, 0x5FC
+};
+
+/*
+ * Default settings, settings between 0..100
+ */
+static
+int defaults[] = {
+	Vxp500Brightness,	0,
+	Vxp500Contrast,		54,
+	Vxp500Saturation,	54,
+	Bt812Brightness,	13,
+	Bt812Contrast,		57,
+	Bt812Saturation,	51,
+	Bt812Hue,		0,
+	Bt812BW,		0,
+};
+
+static int port;
+static int soundchip;
+static int boardrev;
+static int left, right;
+static int vsync, hsync;
+static ulong xtalfreq;
+static ushort cropleft, cropright;
+static ushort cropbottom, croptop;
+static Rectangle window, capwindow;
+
+static void setreg(int, int);
+static void setbt812reg(int, int);
+static void videoinit(void);
+static void createwindow(Rectangle);
+static void setcontrols(int, uchar);
+static void setcolorkey(int, int, int, int, int, int);
+static void soundinit(void);
+static void setvolume(int, int);
+static void setbass(int);
+static void settreble(int);
+static void setsoundsource(int);
+static void tunerinit(void);
+static void settuner(int, int);
+static void setvideosource(int);
+static int waitvideosignal(void);
+static void freeze(int);
+static void setsvideo(int);
+static void setinputformat(int);
+static void enablevideo(void);
+static void *saveframe(int *);
+
+static int
+min(int a, int b)
+{
+	return a < b ? a : b;
+}
+
+static int
+max(int a, int b)
+{
+	return a < b ? b : a;
+}
+
+static int
+present(int port)
+{
+	outb(port+Vxp500Index, 0xAA);
+	if (inb(port+Vxp500Index) != 0xAA)
+		return 0;
+	outb(port+Vxp500Index, 0x55);
+	outb(port+Vxp500Data, 0xAA);
+	if (inb(port+Vxp500Index) != 0x55)
+		return 0;
+	if (inb(port+Vxp500Data) != 0xAA)
+		return 0;
+	outb(port+Vxp500Data, 0x55);
+	if (inb(port+Vxp500Index) != 0x55)
+		return 0;
+	if (inb(port+Vxp500Data) != 0x55)
+		return 0;
+	return 1;
+}
+
+static int
+getvsync(void)
+{
+	int vslow, vshigh, s;
+	ushort timo;
+
+	s = splhi();
+
+	outb(port+Vxp500Index, VideoOutIntrStatus);
+
+	/* wait for VSync to go high then low */
+	for (timo = ~0; timo; timo--)
+		if (inb(port+Vxp500Data) & 2) break;
+	for (timo = ~0; timo; timo--)
+		if ((inb(port+Vxp500Data) & 2) == 0) break;
+
+	/* count how long it stays low and how long it stays high */
+	for (vslow = 0, timo = ~0; timo; timo--, vslow++)
+		if (inb(port+Vxp500Data) & 2) break;
+	for (vshigh = 0, timo = ~0; timo; timo--, vshigh++)
+		if ((inb(port+Vxp500Data) & 2) == 0) break;
+	splx(s);
+
+	return vslow < vshigh;
+}
+
+static int
+gethsync(void)
+{
+	int hslow, hshigh, s;
+	ushort timo;
+
+	s = splhi();
+
+	outb(port+Vxp500Index, VideoOutIntrStatus);
+
+	/* wait for HSync to go high then low */
+	for (timo = ~0; timo; timo--)
+		if (inb(port+Vxp500Data) & 1) break;
+	for (timo = ~0; timo; timo--)
+		if ((inb(port+Vxp500Data) & 1) == 0) break;
+
+	/* count how long it stays low and how long it stays high */
+	for (hslow = 0, timo = ~0; timo; timo--, hslow++)
+		if (inb(port+Vxp500Data) & 1) break;
+	for (hshigh = 0, timo = ~0; timo; timo--, hshigh++)
+		if ((inb(port+Vxp500Data) & 1) == 0) break;
+	splx(s);
+
+	return hslow < hshigh;
+}
+
+static void
+tvinit(void)
+{
+	int i;
+
+	for (i = 0, port = 0; i < nelem(ports); i++) {
+		if (present(ports[i])) {
+			port = ports[i];
+			break;
+		}
+	}
+	if (i == nelem(ports))
+		return;
+
+	/*
+	 * the following routines are the prefered way to
+	 * find out the sync polarities. Unfortunately, it
+	 * doesn't always work.
+	 */
+#ifndef VSync
+	vsync = getvsync();
+	hsync = gethsync();
+#else
+	vsync = VSync;
+	hsync = HSync;
+#endif
+	left = right = 80;
+	soundinit();
+	tunerinit();
+	videoinit();
+}
+
+static Chan*
+tvattach(char *spec)
+{
+	if (port == 0)
+		error(Enonexist);
+	return devattach('V', spec);
+}
+
+static Walkqid*
+tvwalk(Chan *c, Chan *nc, char **name, int nname)
+{
+	return devwalk(c, nc, name, nname, tvtab, nelem(tvtab), devgen);
+}
+
+static int
+tvstat(Chan *c, uchar *db, int n)
+{
+	return devstat(c, db, n, tvtab, nelem(tvtab), devgen);
+}
+
+static Chan*
+tvopen(Chan *c, int omode)
+{
+	return devopen(c, omode, tvtab, nelem(tvtab), devgen);
+}
+
+static void
+tvclose(Chan *)
+{
+}
+
+static long
+tvread(Chan *c, void *a, long n, vlong offset)
+{
+	static void *frame;
+	static int size;
+
+	USED(offset);
+
+	switch((ulong)c->qid.path){
+	case Qdir:
+		return devdirread(c, a, n, tvtab, nelem(tvtab), devgen);
+	case Qdata:
+		if (eqrect(capwindow, Rect(0, 0, 0, 0)))
+			error(Ebadarg);
+		if (offset == 0)
+			frame = saveframe(&size);
+		if (frame) {
+			if (n > size - offset)
+				n = size - offset;
+			memmove(a, (char *)frame + offset, n);
+		} else
+			error(Enovmem);
+		break;
+	default:
+		n=0;
+		break;
+	}
+	return n;
+}
+
+static long
+tvwrite(Chan *c, void *vp, long n, vlong offset)
+{
+	char buf[128], *field[10], *a;
+	int i, nf, source;
+	static Rectangle win;
+	static int hsize, size = 0;
+	static void *frame;
+
+	USED(offset);
+
+	a = vp;
+	switch((ulong)c->qid.path){
+	case Qctl:
+		if (n > sizeof(buf)-1)
+			n = sizeof(buf)-1;
+		memmove(buf, a, n);
+		buf[n] = '\0';
+
+		nf = getfields(buf, field, nelem(field), 1, " \t");
+		if (nf < 1) error(Ebadarg);
+
+		if (strcmp(field[0], "init") == 0) {
+			window = Rect(0, 0, 0, 0);
+			capwindow = Rect(0, 0, 0, 0);
+			source = 0; /* video 0 input */
+			setvideosource(source);
+			left = right = 80;
+			setsoundsource(source);
+			for (i = 0; i < nelem(defaults); i += 2)
+				setcontrols(defaults[i], defaults[i+1]);
+		} else if (strcmp(field[0], "colorkey") == 0) {
+			if (nf < 7) error(Ebadarg);
+			setcolorkey(strtoul(field[1], 0, 0), strtoul(field[2], 0, 0),
+				strtoul(field[3], 0, 0), strtoul(field[4], 0, 0),
+				strtoul(field[5], 0, 0), strtoul(field[6], 0, 0));
+		} else if (strcmp(field[0], "window") == 0) {
+			if (nf < 5) error(Ebadarg);
+			createwindow(Rect(strtoul(field[1], 0, 0), strtoul(field[2], 0, 0),
+				strtoul(field[3], 0, 0), strtoul(field[4], 0, 0)));
+			setvolume(left, right);
+		} else if (strcmp(field[0], "capture") == 0) {
+			if (nf < 5) error(Ebadarg);
+			capwindow = Rect(strtoul(field[1], 0, 0), strtoul(field[2], 0, 0),
+				strtoul(field[3], 0, 0), strtoul(field[4], 0, 0));
+		} else if (strcmp(field[0], "freeze") == 0) {
+			if (nf < 2) error(Ebadarg);
+			freeze(strtoul(field[1], 0, 0));
+		} else if (strcmp(field[0], "capbrightness") == 0) {
+			if (nf < 2) error(Ebadarg);
+			setcontrols(Bt812Brightness, strtoul(field[1], 0, 0));
+		} else if (strcmp(field[0], "capcontrast") == 0) {
+			if (nf < 2) error(Ebadarg);
+			setcontrols(Bt812Contrast, strtoul(field[1], 0, 0));
+		} else if (strcmp(field[0], "capsaturation") == 0) {
+			if (nf < 2) error(Ebadarg);
+			setcontrols(Bt812Saturation, strtoul(field[1], 0, 0));
+		} else if (strcmp(field[0], "caphue") == 0) {
+			if (nf < 2) error(Ebadarg);
+			setcontrols(Bt812Hue, strtoul(field[1], 0, 0));
+		} else if (strcmp(field[0], "capbw") == 0) {
+			if (nf < 2) error(Ebadarg);
+			setcontrols(Bt812BW, strtoul(field[1], 0, 0));
+		} else if (strcmp(field[0], "brightness") == 0) {
+			if (nf < 2) error(Ebadarg);
+			setcontrols(Vxp500Brightness, strtoul(field[1], 0, 0));
+		} else if (strcmp(field[0], "contrast") == 0) {
+			if (nf < 2) error(Ebadarg);
+			setcontrols(Vxp500Contrast, strtoul(field[1], 0, 0));
+		} else if (strcmp(field[0], "saturation") == 0) {
+			if (nf < 2) error(Ebadarg);
+			setcontrols(Vxp500Saturation, strtoul(field[1], 0, 0));
+		} else if (strcmp(field[0], "source") == 0) {
+			if (nf < 2) error(Ebadarg);
+			source = strtoul(field[1], 0, 0);
+			setvideosource(source);
+			setsoundsource(source);	
+		} else if (strcmp(field[0], "svideo") == 0) {
+			if (nf < 2) error(Ebadarg);
+			setsvideo(strtoul(field[1], 0, 0));
+		} else if (strcmp(field[0], "format") == 0) {
+			if (nf < 2) error(Ebadarg);
+			setinputformat(strtoul(field[1], 0, 0));
+		} else if (strcmp(field[0], "channel") == 0) {
+			if (nf < 3) error(Ebadarg);
+			setvolume(0, 0);
+			settuner(strtoul(field[1], 0, 0), strtoul(field[2], 0, 0));
+			tsleep(&up->sleep, return0, 0, 300);
+			setvolume(left, right);
+		} else if (strcmp(field[0], "signal") == 0) {
+			if (!waitvideosignal())
+				error(Etimedout);
+		} else if (strcmp(field[0], "volume") == 0) {
+			if (nf < 2) error(Ebadarg);
+			left = strtoul(field[1], 0, 0);
+			if (nf < 3)
+				right = left;
+			else
+				right = strtoul(field[2], 0, 0);
+			setvolume(left, right);
+		} else if (strcmp(field[0], "bass") == 0) {
+			if (nf < 2) error(Ebadarg);
+			setbass(strtoul(field[1], 0, 0));
+		} else if (strcmp(field[0], "treble") == 0) {
+			if (nf < 2) error(Ebadarg);
+			settreble(strtoul(field[1], 0, 0));
+		} else
+			error(Ebadctl);
+		break;		
+	default:
+		error(Ebadusefd);
+	}
+	return n;
+}
+
+
+Dev tvdevtab = {
+	'V',
+	"tv",
+
+	devreset,
+	tvinit,
+	devshutdown,
+	tvattach,
+	tvwalk,
+	tvstat,
+	tvopen,
+	devcreate,
+	tvclose,
+	tvread,
+	devbread,
+	tvwrite,
+	devbwrite,
+	devremove,
+	devwstat,
+};
+
+static void
+setreg(int index, int data)
+{
+	outb(port+Vxp500Index, index);
+	outb(port+Vxp500Data, data);
+}
+
+static unsigned int
+getreg(int index)
+{
+	outb(port+Vxp500Index, index);
+	return inb(port+Vxp500Data);
+}
+
+/*
+ * I2C routines
+ */
+static void
+delayi2c(void)
+{
+	int i, val;
+
+	/* delay for 4.5 usec to guarantee clock time */
+	for (i = 0; i < 75; i++) {	/* was 50 */
+		val = inb(port+Vxp500Data);
+		USED(val);
+	}
+}
+
+static int
+waitSDA(void)
+{
+	ushort timo;
+
+	/* wait for i2c clock to float high */
+	for (timo = ~0; timo; timo--)
+		if (inb(port+Vxp500Data) & I2C_RdData)
+			break;
+	if (!timo) print("devtv: waitSDA fell out of loop\n");
+	return !timo;
+}
+
+static int
+waitSCL(void)
+{
+	ushort timo;
+
+	/* wait for i2c clock to float high */
+	for (timo = ~0; timo; timo--)
+		if (inb(port+Vxp500Data) & I2C_RdClock)
+			break;
+	delayi2c();
+	if (!timo) print("devtv: waitSCL fell out of loop\n");
+	return !timo;
+}
+
+static int
+seti2cdata(int data)
+{
+	int b, reg, val;
+	int error;
+
+	error = 0;
+	reg = inb(port+Vxp500Data);
+	for (b = 0x80; b; b >>= 1) {
+		if (data & b)
+			reg |= I2C_Data;
+		else
+			reg &= ~I2C_Data;
+		outb(port+Vxp500Data, reg);
+		reg |= I2C_Clock;
+		outb(port+Vxp500Data, reg);
+		error |= waitSCL();
+		reg &= ~I2C_Clock;
+		outb(port+Vxp500Data, reg);
+		delayi2c();
+	}
+	reg |= I2C_Data;
+	outb(port+Vxp500Data, reg);
+	reg |= I2C_Clock;
+	outb(port+Vxp500Data, reg);
+	error |= waitSCL();
+	val = inb(port+Vxp500Data);
+	USED(val);
+	reg &= ~I2C_Clock;
+	outb(port+Vxp500Data, reg);
+	delayi2c();
+	return error;
+}
+
+static int
+seti2creg(int id, int index, int data)
+{
+	int reg, error;
+
+	error = 0;
+        /* set i2c control register to enable i2c clock and data lines */
+	setreg(I2CControl, I2C_Data|I2C_Clock);
+	error |= waitSDA();
+	error |= waitSCL();
+	outb(port+Vxp500Data, I2C_Clock);
+	delayi2c();
+	outb(port+Vxp500Data, 0);
+	delayi2c();
+
+	error |= seti2cdata(id);
+	error |= seti2cdata(index);
+	error |= seti2cdata(data);
+
+	reg = inb(port+Vxp500Data);
+	reg &= ~I2C_Data;
+	outb(port+Vxp500Data, reg);
+	reg |= I2C_Clock;
+	outb(port+Vxp500Data, reg);
+	error |= waitSCL();
+	reg |= I2C_Data;
+	outb(port+Vxp500Data, reg);
+	error |= waitSDA();
+	return error;
+}
+
+static int
+seti2cregs(int id, int index, int n, uchar *data)
+{
+	int reg, error;
+
+	error = 0;
+        /* set i2c control register to enable i2c clock and data lines */
+	setreg(I2CControl, I2C_Data|I2C_Clock);
+	error |= waitSDA();
+	error |= waitSCL();
+	outb(port+Vxp500Data, I2C_Clock);
+	delayi2c();
+	outb(port+Vxp500Data, 0);
+	delayi2c();
+
+	/* send data */
+	error |= seti2cdata(id);
+	error |= seti2cdata(index);
+	while (n--)
+		error |= seti2cdata(*data++);
+
+	/* send stop */
+	reg = inb(port+Vxp500Data);
+	reg &= ~I2C_Data;
+	outb(port+Vxp500Data, reg);
+	reg |= I2C_Clock;
+	outb(port+Vxp500Data, reg);
+	error |= waitSCL();
+	reg |= I2C_Data;
+	outb(port+Vxp500Data, reg);
+	error |= waitSDA();
+	return error;
+}
+
+/*
+ * Audio routines
+ */
+static void
+setvolume(int left, int right)
+{
+	int vol, loudness = 0;
+
+	if (soundchip == TEA6300) {
+		seti2creg(Adr6300, 0, (63L * left) / 100);
+		seti2creg(Adr6300, 1, (63L * right) / 100);
+		vol = (15L * max(left, right)) / 100;
+		seti2creg(Adr6300, 4, 0x30 | vol);
+	} else {
+		vol = (63L * max(left, right)) / 100;
+		seti2creg(Adr6320, 0, vol | (loudness << 6));
+		seti2creg(Adr6320, 1, (63L * right) / 100);
+		seti2creg(Adr6320, 2, (63L * left) / 100);
+	}
+}
+
+static void
+setbass(int bass)
+{
+	if (soundchip == TEA6300)
+		seti2creg(Adr6300, 2, (15L * bass) / 100);
+	else
+		seti2creg(Adr6320, 5, max((31L * bass) / 100, 4));
+}
+
+static void
+settreble(int treble)
+{
+	if (soundchip == TEA6300)
+		seti2creg(Adr6300, 3, (15L * treble) / 100);
+	else
+		seti2creg(Adr6320, 6, max((31L * treble) / 100, 7));
+
+}
+
+static void
+setsoundsource(int source)
+{
+	if (soundchip == TEA6300)
+		seti2creg(Adr6300, 5, 1 << source);
+	else
+		seti2creg(Adr6320, 7, source);
+	setbass(50);
+	settreble(50);
+	setvolume(left, right);
+}
+
+static void
+soundinit(void)
+{
+	if (seti2creg(Adr6320, 7, 0) && seti2creg(Adr6300, 4, 0))
+		print("devtv: Audio init failed\n");
+
+	soundchip = AudioChip;
+	setvolume(0, 0);
+}
+
+/*
+ * Tuner routines
+ */
+static
+long hrcfreq[] = {	/* HRC CATV frequencies */
+	    0,  7200,  5400,  6000,  6600,  7800,  8400, 17400,
+	18000, 18600, 19200, 19800, 20400, 21000, 12000, 12600,
+	13200, 13800, 14400, 15000, 15600, 16200, 16800, 21600,
+	22200, 22800, 23400, 24000, 24600, 25200, 25800, 26400,
+	27000, 27600, 28200, 28800, 29400, 30000, 30600, 31200,
+	31800, 32400, 33000, 33600, 34200, 34800, 35400, 36000,
+	36600, 37200, 37800, 38400, 39000, 39600, 40200, 40800,
+	41400, 42000, 42600, 43200, 43800, 44400, 45000, 45600,
+	46200, 46800, 47400, 48000, 48600, 49200, 49800, 50400,
+	51000, 51600, 52200, 52800, 53400, 54000, 54600, 55200,
+	55800, 56400, 57000, 57600, 58200, 58800, 59400, 60000,
+	60600, 61200, 61800, 62400, 63000, 63600, 64200,  9000,
+	 9600, 10200, 10800, 11400, 64800, 65400, 66000, 66600,
+	67200, 67800, 68400, 69000, 69600, 70200, 70800, 71400,
+	72000, 72600, 73200, 73800, 74400, 75000, 75600, 76200,
+	76800, 77400, 78000, 78600, 79200, 79800,
+};
+
+static void
+settuner(int channel, int finetune)
+{
+	static long lastfreq;
+	uchar data[3];
+	long freq;
+	int cw2, n, sa;
+
+	if (channel < 0 || channel > nelem(hrcfreq))
+		error(Ebadarg);
+
+	freq = hrcfreq[channel];
+
+	/* these settings are all for (FS936E) USA Tuners */
+	if (freq < 16025) /* low band */
+		cw2 = 0xA4;
+	else if (freq < 45425) /* mid band */
+		cw2 = 0x94;
+	else
+		cw2 = 0x34;
+
+	/*
+	 * Channels are stored are 1/100 MHz resolutions, but
+	 * the tuner wants stuff in MHZ, so divide by 100, we
+	 * then have to shift by 4 to get the prog. div. value
+	 */
+	n = ((freq + 4575L) * 16) / 100L + finetune;
+
+	if (freq > lastfreq) {
+		sa = (n >> 8) & 0xFF;
+		data[0] = n & 0xFF;
+		data[1] = 0x8E;
+		data[2] = cw2;
+	} else {
+		sa = 0x8E;
+		data[0] = cw2;
+		data[1] = (n >> 8) & 0xFF;
+		data[2] = n & 0xFF;
+	}
+	lastfreq = freq;
+	seti2cregs(AdrTuner, sa, 3, data);
+}
+
+static void
+tunerinit(void)
+{
+	if (seti2creg(AdrTuner, 0, 0))
+		print("devtv: Tuner init failed\n");
+}
+
+/*
+ * Video routines
+ */
+static int slreg1 = 0;
+static int slreg2 = 0;
+static int vcogain = 0;
+static int phdetgain = 2;
+static int plln1 = 2;
+static int pllp2 = 1;
+
+static void
+waitforretrace(void)
+{
+	ushort timo;
+
+	for (timo = ~0; (getreg(VideoOutIntrStatus) & 2) == 0 && timo; timo--)
+		/* wait for VSync inactive */;
+	for (timo = ~0; (getreg(VideoOutIntrStatus) & 2) && timo; timo--)
+		/* wait for VSync active */;
+}
+
+static void
+updateshadowregs(void)
+{
+	int val;
+
+	setreg(InputVideoConfA, getreg(InputVideoConfA) | 0x40);
+	val = getreg(OutputProcControlB);
+	setreg(OutputProcControlB, val & 0x7F);
+	setreg(OutputProcControlB, val | 0x80);
+}
+
+static void
+setvgareg(int data)
+{
+	/* set HSync & VSync first, to make sure VSync works properly */
+	setreg(VGAControl,  (getreg(VGAControl) & ~0x06) | (data & 0x06));
+
+	/* wait for VSync and set the whole register */
+	waitforretrace();
+	setreg(VGAControl, data);
+}
+
+static void
+setbt812reg(int index, int data)
+{
+	outb(port+Bt812Index, index);
+	outb(port+Bt812Data, data);
+}
+
+static int
+getbt812reg(int index)
+{
+	outb(port+Bt812Index, index);
+	return inb(port+Bt812Data);
+}
+
+static void
+setbt812regpair(int index, ushort data)
+{
+	outb(port+Bt812Index, index);
+	outb(port+Bt812Data, data);
+	outb(port+Bt812Data, data >> 8);
+}
+
+static void
+setvideosource(int source)
+{
+	int s;
+
+	source &= 7;
+	s = source & 3;
+	setbt812reg(0, ((s << 2) | s) << 3);
+	s = (source & 4) << 4;
+	setbt812reg(4, (getbt812reg(4) & ~Bt4ColorBars) | s);
+}
+
+static void
+setsvideo(int enable)
+{
+	if (enable)
+		setbt812reg(5, getbt812reg(5) | Bt5YCFormat);
+	else
+		setbt812reg(5, getbt812reg(5) & ~Bt5YCFormat);
+}
+
+static int
+waitvideosignal(void)
+{
+	ushort timo;
+
+	for (timo = ~0; timo; timo--)
+		if (getbt812reg(2) & Bt2VideoPresent)
+			return 1;
+	return 0;
+}
+
+/*
+ * ICS1572 Programming Configuration
+ *
+ *	R  = 1
+ *	M  = x
+ *	A  = x
+ *	N1 = 4
+ *	N2 = internal divide ratio
+ */
+static
+uchar ICSbits[7] = {
+	0x01,			/* bits  8 - 1	00000001 */
+	0x05,			/* bits 16 - 9	00000101 */
+	0xFF,			/* bits 24 - 17	11111111 */
+	0x8C,			/* bits 32 - 25	10001100 */
+	0xBF,			/* bits 40 - 33	10111111 */
+	0x00,			/* bits 48 - 41	00000000 */
+	0x00,			/* bits 56 - 49	00000000 */
+};
+
+static void
+sendbit(int val, int hold)
+{
+	slreg2 &= ~(HoldBit|DataBit|ClkBit);
+	if (val) slreg2 |= DataBit;
+	if (hold) slreg2 |= HoldBit;
+	outb(port+SLReg2, slreg2);
+	outb(port+SLReg2, slreg2|ClkBit);
+	outb(port+SLReg2, slreg2);
+}
+
+static void
+load1572(int select)
+{
+	int reg;
+	uchar mask;
+
+	if (select)
+		slreg2 |= SelBit;
+	else
+		slreg2 &= ~SelBit;
+	outb(port+SLReg2, slreg2);
+
+	for (reg = 0; reg < sizeof(ICSbits); reg++) {
+		for (mask = 1; mask != 0; mask <<= 1) {
+			if (reg == sizeof(ICSbits)-1 && mask == 0x80) {
+				sendbit(ICSbits[reg] & mask, 1);
+			} else
+				sendbit(ICSbits[reg] & mask, 0);
+		}
+	}
+}
+
+static void
+smartlockdiv(int count, int vcogain, int phdetgain, int n1, int p2)
+{
+	int extdiv, intdiv;
+	int nslreg2, external; 
+
+	nslreg2 = slreg2;
+	extdiv = ((count - 1) / 512) + 1;
+	intdiv = (count / extdiv);
+	nslreg2 &= ~0xC0;
+	switch (extdiv) {
+	case 1: external = 0; break;
+	case 2: external = 1; break;
+	case 3: external = 1; nslreg2 |= 0x40; break;
+	case 4:	external = 1; nslreg2 |= 0x80; break;
+	default: return;
+	}
+	if ((slreg1 & PixelClk) == 0) {
+		slreg2 = nslreg2;
+		outb(port+SLReg2, slreg2);
+	}
+
+	/* set PLL divider */
+	ICSbits[0] &= ~0x07;
+	ICSbits[0] |= n1 & 0x07;
+	ICSbits[3] &= ~0xB7;
+	ICSbits[3] |= vcogain & 0x07;
+	ICSbits[3] |= (phdetgain & 0x03) << 4;
+	ICSbits[3] |= p2 << 7;
+	if (external)
+		ICSbits[1] |= 0x04;		/* set EXTFBKEN	 */
+	else
+		ICSbits[1] &= ~0x04;		/* clear EXTFBKEN */
+	intdiv--;
+	ICSbits[2] = intdiv;			/* set N2 */
+	ICSbits[3] &= ~ 0x08;
+	ICSbits[3] |= (intdiv >> 5) & 0x08;
+	load1572(1);
+}
+
+static void
+disablecolorkey(void)
+{
+	setreg(DisplayControl, getreg(DisplayControl) & 0xFE);
+	updateshadowregs();			
+}
+
+static 
+uchar colorkeylimit[6] = {
+	15,		/* upper limit green */
+	255,		/* lower limit green */
+	63,		/* upper limit red */
+	63,		/* upper limit blue */
+	15,		/* lower limit red */
+	15,		/* lower limit blue */
+};
+
+static void
+enablecolorkey(int enable)
+{
+	int i;
+
+	if (enable) {
+		for (i = 0; i < 6; i++)
+			seti2creg(Adr8444, 0xF0 | i, colorkeylimit[i]);
+		slreg1 &= ~0x1C;
+		if (colorkeylimit[4] == 255)
+			slreg1 |= 0x04;		/* disable red lower limit */
+		if (colorkeylimit[1] == 255)
+			slreg1 |= 0x08;		/* disable green lower limit */
+		if (colorkeylimit[5] == 255)
+			slreg1 |= 0x10;		/* disable blue lower limit */
+	} else {
+		for (i = 0; i < 6; i++)
+			seti2creg(Adr8444, 0xF0 | i, 63);
+		slreg1 |= 0x1C;
+	}
+	outb(port+SLReg1, slreg1);
+	disablecolorkey();
+}
+
+static void
+setcolorkey(int rl, int rh, int gl, int gh, int bl, int bh)
+{
+	colorkeylimit[0] = gh;
+	colorkeylimit[1] = gl;
+	colorkeylimit[2] = rh;
+	colorkeylimit[3] = bh;
+	colorkeylimit[4] = rl;
+	colorkeylimit[5] = bl;
+	enablecolorkey(1);
+}
+
+static void
+waitvideoframe(void)
+{
+	ushort timo;
+	int val;
+
+	/* clear status bits and wait for start of an even field */
+	val = getreg(InputFieldPixelBufStatus);
+	USED(val);
+	for (timo = ~0; timo; timo--)
+		if ((getreg(InputFieldPixelBufStatus) & 2) == 0)
+			break;
+	if (!timo) print("devtv: Wait for video frame failed\n");
+}
+
+static void
+freeze(int enable)
+{
+	ushort timo;
+	int reg;
+
+	if (enable) {
+		waitvideoframe();
+		waitvideoframe();
+
+		setreg(InputVideoConfB, getreg(InputVideoConfB) | 0x08);
+		updateshadowregs();
+
+		for (timo = ~0; timo; timo--)
+			if (getreg(InputVideoConfB) & 0x80) break;
+		waitvideoframe();
+		
+		reg = getreg(OutputProcControlB);
+		if ((reg & 0x20) == 0) {
+			setreg(ISAControl, 0x80);
+			setreg(OutputProcControlB, getreg(OutputProcControlB) | 0x20);
+			setreg(ISAControl, 0x42);
+
+			reg = getreg(OutputProcControlB);
+			setreg(OutputProcControlB, reg & 0x7F);
+			setreg(OutputProcControlB, reg | 0x80);
+		}
+	} else {
+		setreg(InputVideoConfB, getreg(InputVideoConfB) & ~0x08);
+		updateshadowregs();
+
+		for (timo = ~0; timo; timo--)
+			if (getreg(InputVideoConfB) & 0x40) break;
+		waitvideoframe();
+		reg = getreg(InputFieldPixelBufStatus);
+		USED(reg);
+	}
+}
+
+static void
+enablevideo(void)
+{
+	setreg(DisplayControl, 0x04);
+	updateshadowregs();
+}
+
+static void
+disablevideo(void)
+{
+	setreg(DisplayControl, 0x18);
+	updateshadowregs();
+}
+
+static
+uchar vxp500init[] = { /* video register initialization in (index,data) hex pairs */
+	0x30, 0x82, 0x39, 0x40, 0x58, 0x0C, 0x73, 0x02, 0x80, 0x00, 0x25, 0x0F,
+	0x26, 0x0F, 0x38, 0x46, 0x30, 0x03, 0x12, 0x3B, 0x97, 0x20, 0x13, 0x00,
+	0x14, 0x34, 0x15, 0x04, 0x16, 0x00, 0x17, 0x53, 0x18, 0x04, 0x19, 0x62,
+	0x1C, 0x00, 0x1D, 0x00, 0x34, 0x3A, 0x38, 0x06, 0x3A, 0x00, 0x3B, 0x00,
+	0x3C, 0x00, 0x3D, 0x00, 0x40, 0x40, 0x41, 0x40, 0x44, 0xFF, 0x45, 0xFF,
+	0x48, 0x40, 0x49, 0x40, 0x4C, 0xFF, 0x4D, 0xFF, 0x50, 0xF0, 0x54, 0x30,
+	0x55, 0x00, 0x5C, 0x04, 0x5D, 0x00, 0x60, 0x00, 0x68, 0x00, 0x69, 0x00,
+	0x6C, 0x06, 0x6D, 0x00, 0x70, 0x00, 0x71, 0x00, 0x72, 0x00, 0x78, 0x01,
+	0x79, 0x0C, 0x80, 0x10, 0x81, 0x00, 0x82, 0x00, 0x83, 0x00, 0x84, 0x00,
+	0x85, 0x00, 0x86, 0x00, 0x87, 0x00, 0x88, 0x04, 0x89, 0x10, 0x8A, 0x00,
+	0x8B, 0x00, 0x90, 0x05, 0x91, 0x0C, 0x92, 0x18, 0x93, 0x00, 0x96, 0x18,
+	0x9A, 0x30, 0x9C, 0x2D, 0x9D, 0x00, 0xA0, 0x00, 0xA1, 0x00, 0xA2, 0x00,
+	0xA4, 0x50, 0xA5, 0x50, 0xA6, 0xF0, 0xA7, 0xF0, 0xA8, 0x19, 0xA9, 0x18,
+	0xAA, 0x64, 0xAB, 0x64, 0xB0, 0x64, 0xB1, 0x64, 0xB4, 0xA4, 0xB5, 0xA5,
+	0xB8, 0x19, 0xB9, 0x18, 0xBC, 0x09, 0xBD, 0x09, 0xC0, 0x00, 0xC1, 0x02,
+	0xC4, 0x00, 0xC5, 0x00, 0xC8, 0x00, 0xC9, 0x08, 0xCA, 0x08, 0xCE, 0x00,
+	0xCF, 0x00, 0xD0, 0x00, 0xD1, 0x00, 0xD2, 0x00, 0xD8, 0x00, 0xD9, 0x00,
+	0xDA, 0x00, 0xDB, 0x00, 0xDC, 0x00, 0xDD, 0x00, 0x38, 0x46, 0x97, 0xA0,
+	0x97, 0x20, 0x97, 0xA0,
+};
+
+static
+uchar bt812init[] = {	/* bt812 initializations */
+	0xFF, 0x00, 0x00, 0x00, 0x01, 0x00, 0x02, 0x00, 0x03, 0xC0,
+	0x04, 0x08, 0x05, 0x00, 0x06, 0x40, 0x07, 0x00, 0x08, 0x10,
+	0x09, 0x90, 0x0A, 0x80, 0x0B, 0x00, 0x0C, 0x0C, 0x0D, 0x03,
+	0x0E, 0x66, 0x0F, 0x00, 0x10, 0x80, 0x11, 0x02, 0x12, 0x16,
+	0x13, 0x00, 0x14, 0xE5, 0x15, 0x01, 0x16, 0xAB, 0x17, 0xAA,
+	0x18, 0x12, 0x19, 0x51, 0x1A, 0x46, 0x1B, 0x00, 0x1C, 0x00,
+	0x1D, 0x37,
+};
+
+static ushort actpixs = 720;
+static ulong Hdesired = 13500000L;
+
+/*			   NTSC-M  NTSC-443  EXTERNAL */
+static ushort horzfreq[] =	{   15734,    15625,        0 };
+static ushort Vdelay[]	=	{      22,       25,       25 };
+static ushort s2b[] =		{      90,       90,        0 };
+static ushort actlines[] =	{     485,      485,      575 };
+static ulong subcarfreq[] =	{ 3579545,  4433619,  4433619 };
+
+static 
+unsigned int framewidth[5][4] = {
+	1024,  512,  512, 512,      /* mode 0 - single, double, single, quad */
+	1536,  768,  768, 384,      /* mode 1 - single, double, single, quad */
+	2048, 1024, 1024, 512,      /* mode 2 - single, double, single, quad */
+	1024,  512,  512, 512,      /* mode 3 - single, double, single, quad */
+	1536,  768,  768, 384       /* mode 4 - single, double, single, quad */
+};
+
+static 
+unsigned int frameheight[5][4] = {
+	512, 512, 1024, 512,        /* mode 0 - single, double, single, quad */
+	512, 512, 1024, 512,        /* mode 1 - single, double, single, quad */
+	512, 512, 1024, 512,        /* mode 2 - single, double, single, quad */
+	512, 512, 1024, 512,        /* mode 3 - single, double, single, quad */
+	512, 512, 1024, 256         /* mode 4 - single, double, single, quad */
+};
+
+static
+uchar horzfilter[] = { 3, 3, 2, 2, 1, 1, 0, 0 };
+
+static
+uchar interleave[] = { 2, 3, 4, 2, 3 };
+
+#define	ADJUST(n)	(((n) * hrsmult + hrsdiv - 1) / hrsdiv)
+
+static int q = 100;
+static int ilv = 2;
+static int hrsmult = 1;
+static int hrsdiv = 2;
+
+static ushort panmask[] = { 0xFFFE, 0xFFFC, 0xFFFF, 0xFFFF, 0xFFFE };
+
+
+static void
+cropwindow(int left, int right, int top, int bottom)
+{
+	top &= 0x3FE;
+	bottom &= 0x3FE;
+        setreg(InputHorzCropLeftA, left);
+        setreg(InputHorzCropLeftB, left >> 8);
+        setreg(InputHorzCropRightA, right);
+        setreg(InputHorzCropRightB, right >> 8);
+        setreg(InputHorzCropTopA, top);
+        setreg(InputHorzCropTopB, top >> 8);
+        setreg(InputHorzCropBottomA, bottom);
+        setreg(InputHorzCropBottomB, bottom >> 8);
+}
+
+static void
+setinputformat(int format)
+{
+	ushort hclock, hclockdesired;
+	ulong subcarrier;
+	int cr7;
+
+	cr7 = getbt812reg(7) & ~Bt7TriState;
+	if (format == External)
+		cr7 |= Bt7TriState;
+	setbt812reg(7, cr7);
+	setbt812reg(5, getbt812reg(5) & 2);
+
+	hclock = (xtalfreq >> 1) / horzfreq[format];
+	setbt812regpair(0x0C, hclock);
+	setbt812regpair(0x0E,
+		(ushort)(s2b[format] * (Hdesired / 10) / 1000000L) | 1);
+	setbt812regpair(0x10, actpixs);
+	setbt812regpair(0x12, Vdelay[format]);
+	setbt812regpair(0x14, actlines[format]);
+
+	subcarrier = (ulong)
+		((((long long)subcarfreq[format] * 0x1000000) / xtalfreq + 1) / 2);
+	setbt812regpair(0x16, (int)(subcarrier & 0xFFFF)); /* subcarrier */
+	setbt812reg(0x18, (int)(subcarrier >> 16));
+
+	setbt812reg(0x19, (uchar)(((xtalfreq / 200) * 675) / 1000000L + 8));
+	setbt812reg(0x1A, (uchar)((xtalfreq * 65) / 20000000L - 10));
+	hclockdesired = (ushort) (Hdesired / horzfreq[format]);
+	setbt812regpair(0x1B,
+		(ushort)(((hclock - hclockdesired) * 65536L) / hclockdesired));
+}
+
+static ushort
+vgadivider(void)
+{
+	ushort horztotal;
+
+	outb(VGAIndex, VGAHorzTotal);
+	horztotal = (inb(VGAData) << 3) + 40;
+	if (horztotal > ScreenWidth && horztotal < ((ScreenWidth * 3 ) / 2))
+		return horztotal;
+	else
+		return (ScreenWidth * 5) / 4;
+}
+
+static void
+videoinit(void)
+{
+	int i, reg, width, tuner;
+
+	/* early PLL smart lock initialization */
+	if (ScreenWidth == 640) {
+		slreg1 = Window|HSyncLow|VSyncLow;
+		slreg2 = 0x0D;
+	} else {
+		slreg1 = Window;
+		slreg2 = 0x0C;
+	}
+	outb(port+CompressReg, 2);
+	outb(port+SLReg1, slreg1);
+	outb(port+SLReg2, slreg2);
+	smartlockdiv((vgadivider() * hrsmult)/hrsdiv, vcogain, phdetgain, 2, 1);
+
+	/* program the VxP-500 chip (disables video) */
+	waitforretrace();
+	for (i = 0; i < sizeof(vxp500init); i += 2)
+		setreg(vxp500init[i], vxp500init[i+1]);
+
+	/* set memory base for frame capture */
+	setreg(MemoryWindowBaseAddrA, MemAddr >> 14);
+	setreg(MemoryWindowBaseAddrB, ((MemAddr >> 22) & 3) | (MemSize << 2));
+	setreg(MemoryPageReg, 0);
+
+	/* generic 422 decoder, mode 3 and 4 */
+	setreg(MemoryConfReg, ilv+1);
+
+	setreg(AcquisitionAddrA, 0);
+	setreg(AcquisitionAddrB, 0);
+	setreg(AcquisitionAddrC, 0);
+
+	/* program VxP-500 for correct sync polarity */
+	reg = ScreenWidth > 1023 ? 0x01 : 0x00;
+	reg |= (vsync << 1) | (hsync << 2);
+	setvgareg(reg);
+	setreg(VGAControl, reg);
+
+	setreg(VideoBufferLayoutControl, 0); /* for ilv = 2 */
+
+	/* set sync polarities to get proper blanking */
+	if (vsync)
+		slreg1 |= VSyncLow;
+	if (!hsync) {
+		slreg1 ^= HSyncLow;
+		setreg(VGAControl, reg | 4);
+	}
+	outb(port+SLReg1, slreg1);
+
+	if ((slreg1 & PixelClk) == 0) { /* smart lock active */
+		enablecolorkey(1);
+		setreg(VGAControl, getreg(VGAControl) & 6);
+	} else
+		enablecolorkey(0);
+
+	/* color key initializations */
+	if ((slreg1 & PixelClk) == 0)
+		setreg(VGAControl, getreg(VGAControl) & 7);	
+
+	/* initialize Bt812 */
+	for (i = 0; i < sizeof(bt812init); i += 2)
+		setbt812reg(bt812init[i], bt812init[i+1]);
+
+	/* figure out clock source (Xtal or Oscillator) and revision */
+	setbt812reg(6, 0x40);
+	reg = getreg(InputFieldPixelBufStatus) & 3;
+	if ((getreg(InputFieldPixelBufStatus) & 3) == reg) {
+		/* crystal - could be revision PP if R34 is installed */
+		setbt812reg(6, 0x00);
+		reg = inb(port+SLReg1);
+		if (reg & 0x20) {
+			if ((reg & 0xE0) == 0xE0)
+				boardrev = HighQ;
+			else
+				boardrev = RevisionA;
+		} else
+			boardrev = RevisionPP;
+	} else /* revision A or newer with 27 MHz oscillator */
+		boardrev = RevisionA;
+
+	/* figure out xtal frequency */
+	if (xtalfreq == 0) {
+		if (boardrev == RevisionPP) {
+			tuner = (inb(port+SLReg1) >> 6) & 3;
+			if (tuner == 0) /* NTSC */
+				xtalfreq = 24545400L;
+			else
+				xtalfreq = 29500000L;
+		} else if (boardrev == HighQ)
+			xtalfreq = 29500000L;
+		else
+			xtalfreq = 27000000L;
+	}
+
+//	print("Hauppage revision %d (xtalfreq %ld)\n", boardrev, xtalfreq);
+
+	/* on RevPP boards set early sync, on rev A and newer clear it */
+	if (boardrev == RevisionPP)
+		setreg(InputVideoConfA, getreg(InputVideoConfA) | 4);
+	else
+		setreg(InputVideoConfA, getreg(InputVideoConfA) & ~4);
+
+	switch (xtalfreq) {
+	case 24545400L:
+		actpixs = 640;
+		break;
+	case 29500000L:
+		actpixs = 768;
+		break;
+	default:
+		actpixs = 720;
+		break;
+	}
+
+	/* set crop window (these values are for NTSC!) */
+	if (boardrev == RevisionPP) {
+		Hdesired = xtalfreq / 2;
+		cropleft = (NTSCCropLeft * ((Hdesired / 10))) / 1000000L;
+		cropright = (NTSCCropRight * ((Hdesired / 10))) / 1000000L;
+	} else {
+		cropleft = actpixs / 100;
+		cropright = actpixs - cropleft;
+	}
+	width = ((cropright - cropleft + ilv) / ilv) * ilv;
+	cropright = cropleft + width + 1;
+	croptop = 26;
+	cropbottom = 505;
+	cropwindow(cropleft, cropright, croptop, cropbottom);
+
+	/* set input format */
+	setinputformat(NTSC_M);
+	setsvideo(0);
+}
+
+static void
+panwindow(Point p)
+{
+	int memmode, ilv, frw;
+	ulong pos;
+
+	memmode = getreg(MemoryConfReg) & 7;
+	ilv = interleave[memmode];
+	frw = framewidth[memmode][getreg(VideoBufferLayoutControl) & 3];
+
+	pos = (p.y * (frw/ilv)) + ((p.x/ilv) & panmask[memmode]);
+	setreg(DisplayViewPortStartAddrA, (uchar) pos);
+	setreg(DisplayViewPortStartAddrB, (uchar) (pos >> 8));
+	setreg(DisplayViewPortStartAddrC, (uchar) (pos >> 16) & 0x03);
+	updateshadowregs();
+}
+
+static int
+testqfactor(void)
+{
+	ulong timo;
+	int reg;
+
+	waitvideoframe();
+	for (reg = 0, timo = ~0; timo; timo--) {
+		reg |= getreg(InputFieldPixelBufStatus);
+		if (reg & 0xE) break;
+	}
+	if (reg & 0xC) return 0;
+
+	waitvideoframe();
+	for (reg = 0, timo = ~0; timo; timo--) {
+		reg |= getreg(InputFieldPixelBufStatus);
+		if (reg & 0xE) break;
+	}
+	return (reg & 0xC) == 0;
+}
+
+static void
+newwindow(Rectangle r)
+{
+	unsigned ww, wh, dx, dy, xs, ys, xe, ye;
+	unsigned scalex, scaley;
+	int frwidth, frheight, vidwidth, vidheight;
+	int memmode, layout;
+	int width, height;
+	int filter, changed, val;
+
+	changed = r.min.x != window.min.x || r.min.y != window.min.y ||
+		  r.max.x != window.max.x || r.max.y != window.max.y;
+	if (changed) window = r;
+
+	if (r.min.x < 0) r.min.x = 0;
+	if (r.max.x > ScreenWidth) r.max.x = ScreenWidth;
+	if (r.min.y < 0) r.min.y = 0;
+	if (r.max.y > ScreenHeight) r.max.y = ScreenHeight;
+
+	if ((dx = r.max.x - r.min.x) <= 0) dx = 1;
+	if ((dy = r.max.y - r.min.y) <= 0) dy = 1;
+
+	wh = dy;
+	ww = dx = ADJUST(dx);
+	r.min.x = (r.min.x * hrsmult) / hrsdiv;
+
+	memmode = getreg(MemoryConfReg) & 7;
+	layout = getreg(VideoBufferLayoutControl) & 3;
+	vidwidth = cropright - cropleft + 1;
+	vidheight = (cropbottom & 0x3FE) - (croptop & 0x3FE) + 1;
+	frwidth = min(framewidth[memmode][layout], vidwidth);
+	frheight = min(frameheight[memmode][layout], vidheight);
+
+	/* round up scale width to nearest multiple of interleave factor */
+	dx = ((ulong)dx * q) / 100;
+	dx = ilv * ((dx + ilv - 1) / ilv);
+
+	scalex = (((ulong)dx * 1024L) + vidwidth - 2) / (vidwidth - 1);
+	if (dy > frheight) dy = frheight - 1;
+	scaley = (((ulong)dy * 1024L) + vidheight - 2) / (vidheight - 1);
+
+	setreg(InputHorzScaleControlA, (scalex << 6) & 0xC0);
+	setreg(InputHorzScaleControlB, (scalex >> 2) & 0xFF);
+	setreg(InputVertScaleControlA, (scaley << 6) & 0xC0);
+	setreg(InputVertScaleControlB, (scaley >> 2) & 0xFF);
+
+	/* turn on horizontal filtering if we are scaling down */
+	setreg(InputHorzFilter, horzfilter[((scalex - 1) >> 7) & 7]);
+
+	/* set vertical interpolation */
+	filter = scaley > 512 ? (ScreenWidth == 640 ? 0x44 : 0xC5) : 0x46; /* magic */
+	if ((getreg(InputVertInterpolControl) & 0x1F) != (filter & 0x1F)) {
+		setreg(ISAControl, 0x80);
+		setreg(InputVertInterpolControl, filter & 0x1F);
+		setreg(ISAControl, 0x42);
+	}
+	setreg(AcquisitionControl, ((filter >> 6) ^ 3) | 0x04);
+
+	/* set viewport position and size */
+	width = ((ulong)ww * q) / 100;
+	if (width >= frwidth - ilv)
+		width = frwidth - ilv;
+	width = ((width + ilv - 1) / ilv) + 2;
+
+	height = ((ulong)wh * dy + wh - 1) / wh;
+	if (height >= frheight)
+		height = frheight - 3;
+	height += 2;
+
+	xs = r.min.x + XCorrection;
+	if (xs < 0) xs = 2;
+	ys = r.min.y + YCorrection;
+	if (ys < 0) ys = 2;
+	if (ScreenWidth > 1023) ys |= 1;
+
+	setreg(DisplayViewPortWidthA, width);
+	setreg(DisplayViewPortWidthB, width >> 8);
+	setreg(DisplayViewPortHeightA, height);
+	setreg(DisplayViewPortHeightB, height >> 8);
+	setreg(DisplayViewPortOrigTopA, ys);
+	setreg(DisplayViewPortOrigTopB, ys >> 8);
+	setreg(DisplayViewPortOrigLeftA, xs);
+	setreg(DisplayViewPortOrigLeftB, xs >> 8);
+
+	xe = r.min.x + ww - 1 + XCorrection;
+	if (xe < 0) xe = 2;
+	ye = r.min.y + wh - 1 + YCorrection;
+	if (ye < 0) ye = 2;
+
+	setreg(DisplayWindowLeftA, xs);
+	setreg(DisplayWindowLeftB, xs >> 8);
+	setreg(DisplayWindowRightA, xe);
+	setreg(DisplayWindowRightB, xe >> 8);
+	setreg(DisplayWindowTopA, ys);
+	setreg(DisplayWindowTopB, ys >> 8);
+	setreg(DisplayWindowBottomA, ye);
+	setreg(DisplayWindowBottomB, ye >> 8);
+
+	if (dx < ww) { /* horizontal zoom */
+		int zoom = ((ulong) (dx - 1) * 2048) / ww;
+		setreg(OutputProcControlA, getreg(OutputProcControlA) | 6);
+		setreg(OutputHorzZoomControlA, zoom);
+		setreg(OutputHorzZoomControlB, zoom >> 8);
+	} else
+		setreg(OutputProcControlA, getreg(OutputProcControlA) & 0xF9);
+
+	if (dy < wh) { /* vertical zoom */
+		int zoom = ((ulong) (dy - 1) * 2048) / wh;
+		setreg(OutputProcControlB, getreg(OutputProcControlB) | 1);
+		setreg(OutputVertZoomControlA, zoom);
+		setreg(OutputVertZoomControlB, zoom >> 8);
+	} else
+		setreg(OutputProcControlB, getreg(OutputProcControlB) & 0xFE);
+
+	setreg(OutputProcControlB, getreg(OutputProcControlB) | 0x20);
+	updateshadowregs();
+
+	if (changed) {
+		setreg(OutputProcControlA, getreg(OutputProcControlA) & 0xDF);
+	} else {
+		val = getreg(InputFieldPixelBufStatus);
+		USED(val);
+	}
+
+	panwindow(Pt(0, 0));
+}
+
+static void
+createwindow(Rectangle r)
+{
+	for (q = 100; q >= 30; q -= 10) {
+		newwindow(r);
+		if (testqfactor())
+			break;
+	}
+	enablevideo();
+}
+
+static void
+setcontrols(int index, uchar val)
+{
+	switch (index) {
+	case Vxp500Brightness:
+		setreg(BrightnessControl, (127L * val) / 100);
+		updateshadowregs();
+		break;
+	case Vxp500Contrast:
+		setreg(ContrastControl, (15L * val) / 100);
+		updateshadowregs();
+		break;	
+	case Vxp500Saturation:
+		setreg(SaturationControl, (15L * val) / 100);
+		updateshadowregs();
+		break;
+	case Bt812Brightness:
+		setbt812reg(0x08, ((126L * val) / 100) & 0xFE);
+		break;
+	case Bt812Contrast:
+		setbt812reg(0x09, ((254L * val) / 100) & 0xFE);
+		break;
+	case Bt812Saturation:
+		setbt812reg(0x0A, ((254L * val) / 100) & 0xFE);
+		break;
+	case Bt812Hue:
+		setbt812reg(0x0B, ((254L * val) / 100) & 0xFE);
+		break;
+	case Bt812BW:
+		setbt812reg(0x05, (getbt812reg(0x5) & ~2) | ((val << 1) & 2));
+		break;
+	}
+}
+
+static void
+enablememwindow(void)
+{
+	setreg(MemoryWindowBaseAddrB, getreg(MemoryWindowBaseAddrB) | 0x20);
+}
+
+static void
+disablememwindow(void)
+{
+	setreg(MemoryWindowBaseAddrB, getreg(MemoryWindowBaseAddrB) & ~0x20);
+}
+
+volatile static ushort *fb = (ushort *)EISA(MemAddr);
+static uchar yuvpadbound[] = { 4, 12, 4, 2, 6 }; 
+
+/*
+ * Capture a frame in UY0, VY1 format
+ */
+static void *
+saveframe(int *nb)
+{
+	int memmode, layout, ilv;
+	int frwidth, frheight;
+	int bound, n, val, toggle;
+	unsigned save58, save6C;
+	int x, y, w, h, width, height;
+	ulong pos, size;
+	char *p;
+	static void *frame = 0;
+	static ulong framesize = 0;
+
+	width = capwindow.max.x - capwindow.min.x;
+	height = capwindow.max.y - capwindow.min.y;
+
+	memmode = getreg(MemoryConfReg) & 7;
+	if (memmode <= 2) {
+		print("devtv: cannot handle YUV411\n");
+		error(Egreg); /* actually, Eleendert */
+	}
+	layout = getreg(VideoBufferLayoutControl) & 3;
+	ilv = interleave[memmode];
+	frwidth = framewidth[memmode][layout];
+	frheight = frameheight[memmode][layout];
+
+	pos = getreg(AcquisitionAddrA) +
+		(getreg(AcquisitionAddrB) << 8) + (getreg(AcquisitionAddrC) & 3) << 16;
+
+	x = capwindow.min.x + (pos % frwidth);
+	y = capwindow.min.y + (pos / frwidth);
+	if (x > frwidth || y > frheight)
+		return 0;
+	if (x + width > frwidth)
+		width = frwidth - x;
+	if (y + height > frheight)
+		height = frheight - y;
+
+	pos = y * (frwidth / ilv) + (x / ilv);
+
+	/* compute padding for each scan line */
+	bound = yuvpadbound[memmode];
+	switch (bound) {
+	case 2:
+		width = (width + 1) & ~1;
+		break;
+	case 4:
+		width = (width + 3) & ~3;
+		break;
+	default:
+		width = (width + (bound - 1)) / bound;
+		break;
+	}
+
+	size = width * height * sizeof(ushort);
+	if (size != framesize) {
+		framesize = 0;
+		if (frame)
+			free(frame);
+		frame = malloc(size + 256);
+	}
+	if (frame == 0)
+		return 0;
+
+	memset(frame, 0, size + 256);
+
+	framesize = size;
+	p = (char *) frame + snprint(frame, 256,
+		"TYPE=ccir601\nWINDOW=%d %d %d %d\n\n",
+		capwindow.min.x, capwindow.min.y, 
+		capwindow.min.x+width, capwindow.min.y+height);
+
+	freeze(1);
+
+	save58 = getreg(InputVertInterpolControl);
+	save6C = getreg(AcquisitionControl);
+
+	waitforretrace();
+	setreg(ISAControl, 0xC0); /* global reset */
+	setreg(InputVertInterpolControl, 0x0D);
+	setreg(AcquisitionControl, 0x04);
+	setreg(CaptureControl, 0x80); /* set capture mode */
+	setreg(VideoInputFrameBufDepthA, 0xFF);
+	setreg(VideoInputFrameBufDepthB, 0x03);
+	setreg(InputVideoConfA, getreg(InputVideoConfA) | 0x40);
+	setreg(ISAControl, 0x44); /* tight decode, global reset off */
+
+	setreg(CaptureViewPortAddrA, (int) pos & 0xFF);
+	setreg(CaptureViewPortAddrB, (int) (pos >> 8) & 0xFF);
+	setreg(CaptureViewPortAddrC, (int) (pos >> 16) & 0x03);
+	n = (width / ilv) - 1;
+	setreg(CaptureViewPortWidthA, n & 0xFF);
+	setreg(CaptureViewPortWidthB, n >> 8);
+	setreg(CaptureViewPortHeightA, (height-1) & 0xFF);
+	setreg(CaptureViewPortHeightB, (height-1) >> 8);
+	setreg(CapturePixelBufLow, 0x04); /* pix buffer low */
+	setreg(CapturePixelBufHigh, 0x0E); /* pix buffer high */
+	setreg(CaptureMultiBufDepthA, 0xFF); /* multi buffer depth maximum */
+	setreg(CaptureMultiBufDepthB, 0x03);
+	updateshadowregs();
+
+	setreg(CaptureControl, 0x90); /* capture reset */
+	val = getreg(InputFieldPixelBufStatus);	/* clear read status */
+	USED(val);
+
+	toggle = !(getreg(OutputProcControlA) & 0x01) ? 0x8000 : 0x0000;
+	setreg(CaptureControl, 0xC0); /* capture enable, active */
+
+	while ((getreg(InputFieldPixelBufStatus) & 0x10) == 0)
+		/* wait for capture FIFO to become ready */;
+
+	enablememwindow();
+	for (h = height; h > 0; h--) {
+		for (w = width; w > 0; w -= 2) {
+			ushort uy0 = swab16(fb[0]) ^ toggle;
+			ushort vy1 = swab16(fb[1]) ^ toggle;
+			/* unfortunately p may not be properly aligned */
+			*p++ = uy0 >> 8;
+			*p++ = uy0;
+			*p++ = vy1 >> 8;
+			*p++ = vy1;	
+		}
+	}
+	disablememwindow();
+
+	waitforretrace();
+	setreg(ISAControl, 0xC0); /* global reset */
+	setreg(CaptureControl, 0); /* clear capture mode */
+	setreg(InputVertInterpolControl, save58);
+	setreg(AcquisitionControl, save6C);
+	setreg(InputVideoConfA, getreg(InputVideoConfA) | 0x40);
+	setreg(ISAControl, 0x40); /* clear global reset */
+	updateshadowregs();
+
+	freeze(0);
+
+	*nb = p - (char *) frame;
+	return frame;
+}
--- /dev/null
+++ b/os/pc/devusb.c
@@ -1,0 +1,951 @@
+#include	"u.h"
+#include	"../port/lib.h"
+#include	"mem.h"
+#include	"dat.h"
+#include	"fns.h"
+#include	"io.h"
+#include	"../port/error.h"
+
+#include	"usb.h"
+
+static int debug = 0;
+
+#define Chatty	1
+#define DPRINT if(Chatty)print
+#define XPRINT if(debug)iprint
+
+Usbhost*	usbhost[MaxUsb];
+
+static char *devstates[] = {
+	[Disabled]		"Disabled",
+	[Attached]	"Attached",
+	[Enabled]		"Enabled",
+	[Assigned]	"Assigned",
+	[Configured]	"Configured",
+};
+
+static	char	Ebadusbmsg[] = "invalid parameters to USB ctl message";
+
+enum
+{
+	Qtopdir = 0,
+	Q2nd,
+	Qnew,
+	Qport,
+	Q3rd,
+	Qctl,
+	Qstatus,
+	Qep0,
+	/* other endpoint files */
+};
+
+/*
+ * Qid path is:
+ *	8 bits of file type (qids above)
+ *	8 bits of slot number; default address 0 used for per-controller files
+ *	4 bits of controller number
+ */
+enum {
+	TYPEBITS	= 8,
+	SLOTBITS	= 8,
+	CTLRBITS	= 4,
+
+	SLOTSHIFT	= TYPEBITS,
+	CTLRSHIFT	= SLOTSHIFT+SLOTBITS,
+
+	TYPEMASK	= (1<<TYPEBITS)-1,
+	SLOTMASK	= (1<<SLOTBITS)-1,
+	CTLRMASK	= (1<<CTLRBITS)-1,
+};
+
+#define	TYPE(q)		(((ulong)(q).path)&TYPEMASK)
+#define	SLOT(q)		((((ulong)(q).path)>>SLOTSHIFT)&SLOTMASK)
+#define	CTLR(q)		((((ulong)(q).path)>>CTLRSHIFT)&CTLRMASK)
+#define	PATH(t, s, c)	((t)|((s)<<SLOTSHIFT)|((c)<<CTLRSHIFT))
+
+static Dirtab usbdir2[] = {
+	"new",	{Qnew},			0,	0666,
+	"port",	{Qport},			0,	0666,
+};
+
+static Dirtab usbdir3[]={
+	"ctl",		{Qctl},			0,	0666,
+	"status",	{Qstatus},			0,	0444,
+	"setup",	{Qep0},			0,	0666,
+	/* epNdata names are generated on demand */
+};
+
+enum
+{
+	PMdisable,
+	PMenable,
+	PMreset,
+};
+
+enum
+{
+	CMclass,
+	CMdata,
+	CMdebug,
+	CMep,
+	CMmaxpkt,
+	CMadjust,
+	CMspeed,
+	CMunstall,
+};
+
+static Cmdtab usbportmsg[] =
+{
+	PMdisable,	"disable",	2,
+	PMenable,		"enable",	2,
+	PMreset,		"reset",	2,
+};
+
+static Cmdtab usbctlmsg[] =
+{
+	CMclass,		"class",	0,
+	CMdata,		"data",	3,
+	CMdebug,		"debug",	3,
+	CMep,		"ep",		6,
+	CMmaxpkt,	"maxpkt",	3,
+	CMadjust,		"adjust",	3,
+	CMspeed,		"speed",	2,
+	CMunstall,	"unstall",	2,
+};
+
+static struct
+{
+	char*	type;
+	int	(*reset)(Usbhost*);
+} usbtypes[MaxUsb+1];
+
+void
+addusbtype(char* t, int (*r)(Usbhost*))
+{
+	static int ntype;
+
+	if(ntype == MaxUsb)
+		panic("too many USB host interface types");
+	usbtypes[ntype].type = t;
+	usbtypes[ntype].reset = r;
+	ntype++;
+}
+
+static Udev*
+usbdeviceofslot(Usbhost *uh, int s)
+{
+	if(s < 0 || s > nelem(uh->dev))
+		return nil;
+	return uh->dev[s];
+}
+
+static Udev*
+usbdevice(Chan *c)
+{
+	int bus;
+	Udev *d;
+	Usbhost *uh;
+
+	bus = CTLR(c->qid);
+	if(bus > nelem(usbhost) || (uh = usbhost[bus]) == nil) {
+		error(Egreg);
+		return nil;		/* for compiler */
+	}
+	d = usbdeviceofslot(uh, SLOT(c->qid));
+	if(d == nil || d->id != c->qid.vers || d->state == Disabled)
+		error(Ehungup);
+	return d;
+}
+
+static Endpt *
+devendpt(Udev *d, int id, int add)
+{
+	Usbhost *uh;
+	Endpt *e, **p;
+
+	p = &d->ep[id&0xF];
+	lock(d);
+	e = *p;
+	if(e != nil){
+		incref(e);
+		XPRINT("incref(0x%p) in devendpt, new value %ld\n", e, e->ref);
+		unlock(d);
+		return e;
+	}
+	unlock(d);
+	if(!add)
+		return nil;
+
+	e = mallocz(sizeof(*e), 1);
+	e->ref = 1;
+	e->x = id&0xF;
+	e->id = id;
+	e->sched = -1;
+	e->maxpkt = 8;
+	e->nbuf = 1;
+	e->dev = d;
+	e->active = 0;
+
+	uh = d->uh;
+	uh->epalloc(uh, e);
+
+	lock(d);
+	if(*p != nil){
+		incref(*p);
+		XPRINT("incref(0x%p) in devendpt, new value %ld\n", *p, (*p)->ref);
+		unlock(d);
+		uh->epfree(uh, e);
+		free(e);
+		return *p;
+	}
+	*p = e;
+	unlock(d);
+	e->rq = qopen(8*1024, 0, nil, e);
+	e->wq = qopen(8*1024, 0, nil, e);
+	return e;
+}
+
+static void
+freept(Endpt *e)
+{
+	Usbhost *uh;
+
+	if(e != nil && decref(e) == 0){
+		XPRINT("freept(%d,%d)\n", e->dev->x, e->x);
+		uh = e->dev->uh;
+		uh->epclose(uh, e);
+		e->dev->ep[e->x] = nil;
+		uh->epfree(uh, e);
+		free(e);
+	}
+}
+
+static Udev*
+usbnewdevice(Usbhost *uh)
+{
+	int i;
+	Udev *d;
+	Endpt *e;
+
+	d = nil;
+	qlock(uh);
+	if(waserror()){
+		qunlock(uh);
+		nexterror();
+	}
+	for(i=0; i<nelem(uh->dev); i++)
+		if(uh->dev[i] == nil){
+			uh->idgen++;
+			d = mallocz(sizeof(*d), 1);
+			d->uh = uh;
+			d->ref = 1;
+			d->x = i;
+			d->id = (uh->idgen << 8) | i;
+			d->state = Enabled;
+			XPRINT("calling devendpt in usbnewdevice\n");
+			e = devendpt(d, 0, 1);	/* always provide control endpoint 0 */
+			e->mode = ORDWR;
+			e->iso = 0;
+			e->sched = -1;
+			uh->dev[i] = d;
+			break;
+		}
+	poperror();
+	qunlock(uh);
+	return d;
+}
+
+static void
+freedev(Udev *d, int ept)
+{
+	int i;
+	Endpt *e;
+	Usbhost *uh;
+
+	uh = d->uh;
+	if(decref(d) == 0){
+		XPRINT("freedev 0x%p, 0\n", d);
+		for(i=0; i<nelem(d->ep); i++)
+			freept(d->ep[i]);
+		if(d->x >= 0)
+			uh->dev[d->x] = nil;
+		free(d);
+	} else {
+		if(ept >= 0 && ept < nelem(d->ep)){
+			e = d->ep[ept];
+			XPRINT("freedev, freept 0x%p\n", e);
+			if(e != nil)
+				uh->epclose(uh, e);
+		}
+	}	
+}
+
+static int
+usbgen(Chan *c, char *, Dirtab*, int, int s, Dir *dp)
+{
+	Qid q;
+	Udev *d;
+	Endpt *e;
+	Dirtab *tab;
+	Usbhost *uh;
+	int t, bus, slot, perm;
+
+	/*
+	 * Top level directory contains the controller names.
+	 */
+	if(c->qid.path == Qtopdir){
+		if(s == DEVDOTDOT){
+			mkqid(&q, Qtopdir, 0, QTDIR);
+			devdir(c, q, "#U", 0, eve, 0555, dp);
+			return 1;
+		}
+		if(s >= nelem(usbhost) || usbhost[s] == nil)
+			return -1;
+		mkqid(&q, PATH(Q2nd, 0, s), 0, QTDIR);
+		snprint(up->genbuf, sizeof up->genbuf, "usb%d", s);
+		devdir(c, q, up->genbuf, 0, eve, 0555, dp);
+		return 1;
+	}
+	bus = CTLR(c->qid);
+	if(bus >= nelem(usbhost) || (uh = usbhost[bus]) == nil)
+			return -1;
+
+	/*
+	 * Second level contains "new", "port", and a numbered
+	 * directory for each enumerated device on the bus.
+	 */
+	t = TYPE(c->qid);
+	if(t < Q3rd){
+		if(s == DEVDOTDOT){
+			mkqid(&q, Qtopdir, 0, QTDIR);
+			devdir(c, q, "#U", 0, eve, 0555, dp);
+			return 1;
+		}
+		if(s < nelem(usbdir2)){
+			d = uh->dev[0];
+			if(d == nil)
+				return -1;
+			tab = &usbdir2[s];
+			mkqid(&q, PATH(tab->qid.path, 0, bus), d->id, QTFILE);
+			devdir(c, q, tab->name, tab->length, eve, tab->perm, dp);
+			return 1;
+		}
+		s -= nelem(usbdir2);
+		if(s >= 0 && s < nelem(uh->dev)) {
+			d = uh->dev[s];
+			if(d == nil)
+				return 0;
+			sprint(up->genbuf, "%d", s);
+			mkqid(&q, PATH(Q3rd, s, bus), d->id, QTDIR);
+			devdir(c, q, up->genbuf, 0, eve, 0555, dp);
+			return 1;
+		}
+		return -1;
+	}
+
+	/*
+	 * Third level.
+	 */
+	slot = SLOT(c->qid);
+	if(s == DEVDOTDOT) {
+		mkqid(&q, PATH(Q2nd, 0, bus), c->qid.vers, QTDIR);
+		snprint(up->genbuf, sizeof up->genbuf, "usb%d", bus);
+		devdir(c, q, up->genbuf, 0, eve, 0555, dp);
+		return 1;
+	}
+	if(s < nelem(usbdir3)) {
+		tab = &usbdir3[s];
+		mkqid(&q, PATH(tab->qid.path, slot, bus), c->qid.vers, QTFILE);
+		devdir(c, q, tab->name, tab->length, eve, tab->perm, dp);
+		return 1;
+	}
+	s -= nelem(usbdir3);
+
+	/* active endpoints */
+	d = usbdeviceofslot(uh, slot);
+	if(d == nil || s >= nelem(d->ep))
+		return -1;
+	if(s == 0 || (e = d->ep[s]) == nil)	/* ep0data is called "setup" */
+		return 0;
+	sprint(up->genbuf, "ep%ddata", s);
+	mkqid(&q, PATH(Qep0+s, slot, bus), c->qid.vers, QTFILE);
+	switch(e->mode) {
+	case OREAD:
+		perm = 0444;
+		break;
+	case OWRITE:
+		perm = 0222;
+		break;
+	default:
+		perm = 0666;
+		break;
+	}
+	devdir(c, q, up->genbuf, e->buffered, eve, perm, dp);
+	return 1;
+}
+
+static Usbhost*
+usbprobe(int cardno, int ctlrno)
+{
+	Usbhost *uh;
+	char buf[128], *ebuf, name[64], *p, *type;
+
+	uh = malloc(sizeof(Usbhost));
+	memset(uh, 0, sizeof(Usbhost));
+	uh->tbdf = BUSUNKNOWN;
+
+	if(cardno < 0){
+		if(isaconfig("usb", ctlrno, uh) == 0){
+			free(uh);
+			return nil;
+		}
+		for(cardno = 0; usbtypes[cardno].type; cardno++){
+			type = uh->type;
+			if(type==nil || *type==0)
+				type = "uhci";
+			if(cistrcmp(usbtypes[cardno].type, type))
+				continue;
+			break;
+		}
+	}
+
+	if(cardno >= MaxUsb || usbtypes[cardno].type == nil){
+		free(uh);
+		return nil;
+	}
+	if(usbtypes[cardno].reset(uh) < 0){
+		free(uh);
+		return nil;
+	}
+
+	/*
+	 * IRQ2 doesn't really exist, it's used to gang the interrupt
+	 * controllers together. A device set to IRQ2 will appear on
+	 * the second interrupt controller as IRQ9.
+	 */
+	if(uh->irq == 2)
+		uh->irq = 9;
+	snprint(name, sizeof(name), "usb%d", ctlrno);
+	intrenable(uh->irq, uh->interrupt, uh, uh->tbdf, name);
+
+	ebuf = buf + sizeof buf;
+	p = seprint(buf, ebuf, "#U/usb%d: %s: port 0x%luX irq %d", ctlrno, usbtypes[cardno].type, uh->port, uh->irq);
+	if(uh->mem)
+		p = seprint(p, ebuf, " addr 0x%luX", PADDR(uh->mem));
+	if(uh->size)
+		seprint(p, ebuf, " size 0x%luX", uh->size);
+	print("%s\n", buf);
+
+	return uh;
+}
+
+static void
+usbreset(void)
+{
+	int cardno, ctlrno;
+	Usbhost *uh;
+
+	for(ctlrno = 0; ctlrno < MaxUsb; ctlrno++){
+		if((uh = usbprobe(-1, ctlrno)) == nil)
+			continue;
+		usbhost[ctlrno] = uh;
+	}
+
+	if(getconf("*nousbprobe"))
+		return;
+
+	cardno = ctlrno = 0;
+	while(usbtypes[cardno].type != nil && ctlrno < MaxUsb){
+		if(usbhost[ctlrno] != nil){
+			ctlrno++;
+			continue;
+		}
+		if((uh = usbprobe(cardno, ctlrno)) == nil){
+			cardno++;
+			continue;
+		}
+		usbhost[ctlrno] = uh;
+		ctlrno++;
+	}
+}
+
+void
+usbinit(void)
+{
+	Udev *d;
+	int ctlrno;
+	Usbhost *uh;
+
+	for(ctlrno = 0; ctlrno < MaxUsb; ctlrno++){
+		uh = usbhost[ctlrno];
+		if(uh == nil)
+			continue;
+		if(uh->init != 0)
+			uh->init(uh);
+
+		/* reserve device for configuration */
+		d = usbnewdevice(uh);
+		incref(d);
+		d->state = Attached;
+	}
+}
+
+Chan *
+usbattach(char *spec)
+{
+	return devattach('U', spec);
+}
+
+static Walkqid*
+usbwalk(Chan *c, Chan *nc, char **name, int nname)
+{
+	return devwalk(c, nc, name, nname, nil, 0, usbgen);
+}
+
+static int
+usbstat(Chan *c, uchar *db, int n)
+{
+	return devstat(c, db, n, nil, 0, usbgen);
+}
+
+Chan*
+usbopen(Chan *c, int omode)
+{
+	Udev *d;
+	Endpt *e;
+	int f, s, type;
+	Usbhost *uh;
+
+	if(c->qid.type == QTDIR)
+		return devopen(c, omode, nil, 0, usbgen);
+
+	f = 0;
+	type = TYPE(c->qid);
+	if(type == Qnew){
+		d = usbdevice(c);
+		d = usbnewdevice(d->uh);
+		XPRINT("usbopen, new dev 0x%p\n", d);
+		if(d == nil) {
+			XPRINT("usbopen failed (usbnewdevice)\n");
+			error(Enodev);
+		}
+		type = Qctl;
+		mkqid(&c->qid, PATH(type, d->x, CTLR(c->qid)), d->id, QTFILE);
+		f = 1;
+	}
+
+	if(type < Q3rd){
+		XPRINT("usbopen, devopen < Q3rd\n");
+		return devopen(c, omode, nil, 0, usbgen);
+	}
+
+	d = usbdevice(c);
+	uh = d->uh;
+	qlock(uh);
+	if(waserror()){
+		qunlock(uh);
+		nexterror();
+	}
+
+	switch(type){
+	case Qctl:
+		if(0&&d->busy)
+			error(Einuse);
+		d->busy = 1;
+		if(!f)
+			incref(d);
+		XPRINT("usbopen, Qctl 0x%p\n", d);
+		break;
+
+	default:
+		s = type - Qep0;
+		XPRINT("usbopen, default 0x%p, %d\n", d, s);
+		if(s >= 0 && s < nelem(d->ep)){
+			if((e = d->ep[s]) == nil) {
+				XPRINT("usbopen failed (endpoint)\n");
+				error(Enodev);
+			}
+			XPRINT("usbopen: dev 0x%p, ept 0x%p\n", d, e);
+			uh->epopen(uh, e);
+			e->foffset = 0;
+			e->toffset = 0;
+			e->poffset = 0;
+			e->buffered = 0;
+		}
+		incref(d);
+		break;
+	}
+	poperror();
+	qunlock(uh);
+	c->mode = openmode(omode);
+	c->flag |= COPEN;
+	c->offset = 0;
+	return c;
+}
+
+void
+usbclose(Chan *c)
+{
+	Udev *d;
+	int ept, type;
+	Usbhost *uh;
+
+	type = TYPE(c->qid);
+	if(c->qid.type == QTDIR || type < Q3rd)
+		return;
+	d = usbdevice(c);
+	uh = d->uh;
+	qlock(uh);
+	if(waserror()){
+		qunlock(uh);
+		nexterror();
+	}
+	if(type == Qctl)
+		d->busy = 0;
+	XPRINT("usbclose: dev 0x%p\n", d);
+	if(c->flag & COPEN){
+		ept = (type != Qctl) ? type - Qep0 : -1;
+		XPRINT("usbclose: freedev 0x%p\n", d);
+		freedev(d, ept);
+	}
+	poperror();
+	qunlock(uh);
+}
+
+static char *
+epstatus(char *s, char *se, Endpt *e, int i)
+{
+	char *p;
+
+	p = seprint(s, se, "%2d %#6.6lux %10lud bytes %10lud blocks\n", i, e->csp, e->nbytes, e->nblocks);
+	if(e->iso){
+		p = seprint(p, se, "bufsize %6d buffered %6d", e->maxpkt, e->buffered);
+		if(e->toffset)
+			p = seprint(p, se, " offset  %10lud time %19lld\n", e->toffset, e->time);
+		p = seprint(p, se, "\n");
+	}
+	return p;
+}
+
+long
+usbread(Chan *c, void *a, long n, vlong offset)
+{
+	int t, i;
+	Udev *d;
+	Endpt *e;
+	Usbhost *uh;
+	char *s, *se, *p;
+
+	if(c->qid.type == QTDIR)
+		return devdirread(c, a, n, nil, 0, usbgen);
+
+	d = usbdevice(c);
+	uh = d->uh;
+	t = TYPE(c->qid);
+
+	if(t >= Qep0) {
+		t -= Qep0;
+		if(t >= nelem(d->ep))
+			error(Eio);
+		e = d->ep[t];
+		if(e == nil || e->mode == OWRITE)
+			error(Egreg);
+		if(t == 0) {
+			if(e->iso)
+				error(Egreg);
+			e->data01 = 1;
+			n = uh->read(uh, e, a, n, 0LL);
+			if(e->setin){
+				e->setin = 0;
+				e->data01 = 1;
+				uh->write(uh, e, "", 0, 0LL, TokOUT);
+			}
+			return n;
+		}
+		return uh->read(uh, e, a, n, offset);
+	}
+
+	s = smalloc(READSTR);
+	se = s+READSTR;
+	if(waserror()){
+		free(s);
+		nexterror();
+	}
+	switch(t){
+	case Qport:
+		uh->portinfo(uh, s, se);
+		break;
+
+	case Qctl:
+		seprint(s, se, "%11d %11d\n", d->x, d->id);
+		break;
+
+	case Qstatus:
+		if (d->did || d->vid)
+			p = seprint(s, se, "%s %#6.6lux %#4.4ux %#4.4ux\n", devstates[d->state], d->csp, d->vid, d->did);
+		else
+			p = seprint(s, se, "%s %#6.6lux\n", devstates[d->state], d->csp);
+		for(i=0; i<nelem(d->ep); i++) {
+			e = d->ep[i];
+			if(e == nil)
+				continue;
+			/* TO DO: freeze e */
+			p = epstatus(p, se, e, i);
+		}
+	}
+	n = readstr(offset, a, n, s);
+	poperror();
+	free(s);
+	return n;
+}
+
+long
+usbwrite(Chan *c, void *a, long n, vlong offset)
+{
+	Udev *d;
+	Endpt *e;
+	Cmdtab *ct;
+	Cmdbuf *cb;
+	Usbhost *uh;
+	int id, nw, t, i;
+	char cmd[50];
+
+	if(c->qid.type == QTDIR)
+		error(Egreg);
+	d = usbdevice(c);
+	uh = d->uh;
+	t = TYPE(c->qid);
+	switch(t){
+	case Qport:
+		cb = parsecmd(a, n);
+		if(waserror()){
+			free(cb);
+			nexterror();
+		}
+
+		ct = lookupcmd(cb, usbportmsg, nelem(usbportmsg));
+		id = strtol(cb->f[1], nil, 0);
+		switch(ct->index){
+		case PMdisable:
+			uh->portenable(uh, id, 0);
+			break;
+		case PMenable:
+			uh->portenable(uh, id, 1);
+			break;
+		case PMreset:
+			uh->portreset(uh, id);
+			break;
+		}
+	
+		poperror();
+		free(cb);
+		return n;
+	case Qctl:
+		cb = parsecmd(a, n);
+		if(waserror()){
+			free(cb);
+			nexterror();
+		}
+
+		ct = lookupcmd(cb, usbctlmsg, nelem(usbctlmsg));
+		switch(ct->index){
+		case CMspeed:
+			d->ls = strtoul(cb->f[1], nil, 0) == 0;
+			break;
+		case CMclass:
+			if (cb->nf != 4 && cb->nf != 6)
+				cmderror(cb, Ebadusbmsg);
+			/* class #ifc ept csp ( == class subclass proto) [vendor product] */
+			d->npt = strtoul(cb->f[1], nil, 0);	/* # of interfaces */
+			i = strtoul(cb->f[2], nil, 0);		/* endpoint */
+			if (i < 0 || i >= nelem(d->ep)
+			 || d->npt > nelem(d->ep) || i >= d->npt)
+				cmderror(cb, Ebadusbmsg);
+			if (cb->nf == 6) {
+				d->vid = strtoul(cb->f[4], nil, 0);
+				d->did = strtoul(cb->f[5], nil, 0);
+			}
+			if (i == 0)
+				d->csp = strtoul(cb->f[3], nil, 0);
+			if(d->ep[i] == nil){
+				XPRINT("calling devendpt in usbwrite (CMclass)\n");
+				d->ep[i] = devendpt(d, i, 1);
+			}
+			d->ep[i]->csp = strtoul(cb->f[3], nil, 0);
+			break;
+		case CMdata:
+			i = strtoul(cb->f[1], nil, 0);
+			if(i < 0 || i >= nelem(d->ep) || d->ep[i] == nil)
+				error(Ebadusbmsg);
+			e = d->ep[i];
+			e->data01 = strtoul(cb->f[2], nil, 0) != 0;
+			break;
+		case CMmaxpkt:
+			i = strtoul(cb->f[1], nil, 0);
+			if(i < 0 || i >= nelem(d->ep) || d->ep[i] == nil)
+				error(Ebadusbmsg);
+			e = d->ep[i];
+			e->maxpkt = strtoul(cb->f[2], nil, 0);
+			if(e->maxpkt > 1500)
+				e->maxpkt = 1500;
+			break;
+		case CMadjust:
+			i = strtoul(cb->f[1], nil, 0);
+			if(i < 0 || i >= nelem(d->ep) || d->ep[i] == nil)
+				error(Ebadusbmsg);
+			e = d->ep[i];
+			if (e->iso == 0)
+				error(Eperm);
+			i = strtoul(cb->f[2], nil, 0);
+			/* speed may not result in change of maxpkt */
+			if (i < (e->maxpkt-1)/e->samplesz * 1000/e->pollms
+			  || i > e->maxpkt/e->samplesz * 1000/e->pollms){
+				snprint(cmd, sizeof(cmd), "%d < %d < %d?",
+					(e->maxpkt-1)/e->samplesz * 1000/e->pollms,
+					i,
+					e->maxpkt/e->samplesz * 1000/e->pollms);
+				error(cmd);
+			}
+			e->hz = i;
+			break;
+		case CMdebug:
+			i = strtoul(cb->f[1], nil, 0);
+			if(i < -1 || i >= nelem(d->ep) || d->ep[i] == nil)
+				error(Ebadusbmsg);
+			if (i == -1)
+				debug = 0;
+			else {
+				debug = 1;
+				e = d->ep[i];
+				e->debug = strtoul(cb->f[2], nil, 0);
+			}
+			break;
+		case CMunstall:
+			i = strtoul(cb->f[1], nil, 0);
+			if(i < 0 || i >= nelem(d->ep) || d->ep[i] == nil)
+				error(Ebadusbmsg);
+			e = d->ep[i];
+			e->err = nil;
+			break;
+		case CMep:
+			/* ep n `bulk' mode maxpkt nbuf     OR
+			 * ep n period mode samplesize Hz
+			 */
+			i = strtoul(cb->f[1], nil, 0);
+			if(i < 0 || i >= nelem(d->ep)) {
+				XPRINT("field 1: 0 <= %d < %d\n", i, nelem(d->ep));
+				error(Ebadarg);
+			}
+			if((e = d->ep[i]) == nil){
+				XPRINT("calling devendpt in usbwrite (CMep)\n");
+				e = devendpt(d, i, 1);
+			}
+			qlock(uh);
+			if(waserror()){
+				freept(e);
+				qunlock(uh);
+				nexterror();
+			}
+			if(e->active)
+				error(Eperm);
+			if(strcmp(cb->f[2], "bulk") == 0){
+				/* ep n `bulk' mode maxpkt nbuf */
+				e->iso = 0;
+				i = strtoul(cb->f[4], nil, 0);
+				if(i < 8 || i > 1023)
+					i = 8;
+				e->maxpkt = i;
+				i = strtoul(cb->f[5], nil, 0);
+				if(i >= 1 && i <= 32)
+					e->nbuf = i;
+			} else {
+				/* ep n period mode samplesize Hz */
+				i = strtoul(cb->f[2], nil, 0);
+				if(i > 0 && i <= 1000){
+					e->pollms = i;
+				}else {
+					XPRINT("field 4: 0 <= %d <= 1000\n", i);
+					error(Ebadarg);
+				}
+				i = strtoul(cb->f[4], nil, 0);
+				if(i >= 1 && i <= 8){
+					e->samplesz = i;
+				}else {
+					XPRINT("field 4: 0 < %d <= 8\n", i);
+					error(Ebadarg);
+				}
+				i = strtoul(cb->f[5], nil, 0);
+				if(i >= 1 && i*e->samplesz <= 12*1000*1000){
+					/* Hz */
+					e->hz = i;
+					e->remain = 0;
+				}else {
+					XPRINT("field 5: 1 < %d <= 100000 Hz\n", i);
+					error(Ebadarg);
+				}
+				e->maxpkt = (e->hz * e->pollms + 999)/1000 * e->samplesz;
+				e->iso = 1;
+			}
+			e->mode = strcmp(cb->f[3],"r") == 0? OREAD :
+				  	strcmp(cb->f[3],"w") == 0? OWRITE : ORDWR;
+			uh->epmode(uh, e);
+			poperror();
+			qunlock(uh);
+		}
+	
+		poperror();
+		free(cb);
+		return n;
+
+	case Qep0:	/* SETUP endpoint 0 */
+		/* should canqlock etc */
+		e = d->ep[0];
+		if(e == nil || e->iso)
+			error(Egreg);
+		if(n < 8)
+			error(Eio);
+		nw = *(uchar*)a & RD2H;
+		e->data01 = 0;
+		n = uh->write(uh, e, a, n, 0LL, TokSETUP);
+		if(nw == 0) {	/* host to device: use IN[DATA1] to ack */
+			e->data01 = 1;
+			nw = uh->read(uh, e, cmd, 0LL, 8);
+			if(nw != 0)
+				error(Eio);	/* could provide more status */
+		}else
+			e->setin = 1;	/* two-phase */
+		break;
+
+	default:	/* sends DATA[01] */
+		t -= Qep0;
+		if(t < 0 || t >= nelem(d->ep))
+			error(Egreg);
+		e = d->ep[t];
+		if(e == nil || e->mode == OREAD)
+			error(Egreg);
+		n = uh->write(uh, e, a, n, offset, TokOUT);
+		break;
+	}
+	return n;
+}
+
+Dev usbdevtab = {
+	'U',
+	"usb",
+
+	usbreset,
+	usbinit,
+	devshutdown,
+	usbattach,
+	usbwalk,
+	usbstat,
+	usbopen,
+	devcreate,
+	usbclose,
+	usbread,
+	devbread,
+	usbwrite,
+	devbwrite,
+	devremove,
+	devwstat,
+};
--- /dev/null
+++ b/os/pc/devvga.c
@@ -1,0 +1,620 @@
+/*
+ * VGA controller
+ */
+#include "u.h"
+#include "../port/lib.h"
+#include "mem.h"
+#include "dat.h"
+#include "fns.h"
+#include "../port/error.h"
+
+#define	Image	IMAGE
+#include <draw.h>
+#include <memdraw.h>
+#include <cursor.h>
+#include "screen.h"
+
+typedef struct Vgaseg Vgaseg;
+struct Vgaseg {
+	QLock;
+	ulong	pa;
+	ulong	len;
+	void*	va;
+};
+
+enum {
+	Nvgaseg = 4,
+
+	Qdir = 0,
+	Qvgactl,
+	Qvgaovl,
+	Qvgaovlctl,
+
+	Qsegs,
+	Qmax = Qsegs+Nvgaseg
+};
+
+static Dirtab vgadir[Qmax] = {
+	".",	{ Qdir, 0, QTDIR },		0,	0550,
+	"vgactl",		{ Qvgactl, 0 },		0,	0660,
+	"vgaovl",		{ Qvgaovl, 0 },		0,	0660,
+	"vgaovlctl",	{ Qvgaovlctl, 0 },	0, 	0660,
+	/* dynamically-created memory segments are added here */
+};
+
+static Vgaseg vgasegs[Nvgaseg];
+static Lock vgadirlock;
+static int nvgadir = Qsegs;
+
+enum {
+	CMactualsize,
+	CMblank,
+	CMblanktime,
+	CMdrawinit,
+	CMhwaccel,
+	CMhwblank,
+	CMhwgc,
+	CMlinear,
+	CMpalettedepth,
+	CMpanning,
+	CMsize,
+	CMtype,
+	CMunblank,
+};
+
+static Cmdtab vgactlmsg[] = {
+	CMactualsize,	"actualsize",	2,
+	CMblank,	"blank",	1,
+	CMblanktime,	"blanktime",	2,
+	CMdrawinit,	"drawinit",	1,
+	CMhwaccel,	"hwaccel",	2,
+	CMhwblank,	"hwblank",	2,
+	CMhwgc,		"hwgc",		2,
+	CMlinear,	"linear",	0,
+	CMpalettedepth,	"palettedepth",	2,
+	CMpanning,	"panning",	2,
+	CMsize,		"size",		3,
+	CMtype,		"type",		2,
+	CMunblank,	"unblank",	1,
+};
+
+static void
+vgareset(void)
+{
+	/* reserve the 'standard' vga registers */
+	if(ioalloc(0x2b0, 0x2df-0x2b0+1, 0, "vga") < 0)
+		panic("vga ports already allocated"); 
+	if(ioalloc(0x3c0, 0x3da-0x3c0+1, 0, "vga") < 0)
+		panic("vga ports already allocated"); 
+	conf.monitor = 1;
+}
+
+void
+addvgaseg(char *name, ulong pa, ulong size)
+{
+	int i;
+	Dirtab d;
+	Vgaseg *s;
+	ulong va;
+
+	va = mmukmap(pa, 0, size);
+	if(va == 0)
+		return;
+	memset(&d, 0, sizeof(d));
+	strecpy(d.name, d.name+sizeof(name), name);
+	lock(&vgadirlock);
+	for(i=0; i<nvgadir; i++)
+		if(strcmp(vgadir[i].name, name) == 0){
+			unlock(&vgadirlock);
+			print("devvga: duplicate segment %s\n", name);
+			return;
+		}
+	if(nvgadir >= nelem(vgadir)){
+		unlock(&vgadirlock);
+		print("devvga: segment %s: too many segments\n", name);
+		return;
+	}
+	d.qid.path = nvgadir;
+	d.perm = 0660;
+	d.length = size;
+	s = &vgasegs[nvgadir-Qsegs];
+	s->pa = pa;
+	s->len = size;
+	s->va = (void*)va;
+	vgadir[nvgadir] = d;
+	nvgadir++;
+	unlock(&vgadirlock);
+}
+
+static long
+vgasegrd(Vgaseg *s, uchar *buf, long n, ulong offset)
+{
+	int i;
+	uchar *a, *d;
+	ulong v;
+
+	if(offset >= s->len)
+		return 0;
+	if(offset+n > s->len)
+		n = s->len - offset;
+	d = (uchar*)s->va + offset;
+	qlock(s);
+	if(waserror()){
+		qunlock(s);
+		nexterror();
+	}
+	a = buf;
+	while(n > 0){
+		i = 4 - ((ulong)d & 3);
+		if(i > n)
+			i = n;
+		if(i == 3)
+			i = 2;
+		switch(i){
+		case 4:
+			v = (a[3]<<24) | (a[2]<<16) | (a[1]<<8) | a[0];
+			*(ulong*)d = v;
+			break;
+		case 2:
+			v = (a[1]<<8) | a[0];
+			*(ushort*)d = v;
+			break;
+		case 1:
+			*d = *a;
+			break;
+		}
+		d += i;
+		a += i;
+		n -= i;
+	}
+	poperror();
+	qunlock(s);
+	return a-buf;
+}
+
+static long
+vgasegwr(Vgaseg *s, uchar *buf, long n, ulong offset)
+{
+	int i;
+	uchar *a, *r;
+	ulong v;
+
+	if(offset >= s->len)
+		return 0;
+	if(offset+n > s->len)
+		n = s->len - offset;
+	r = (uchar*)s->va + offset;
+	qlock(s);
+	if(waserror()){
+		qunlock(s);
+		nexterror();
+	}
+	a = buf;
+	while(n > 0){
+		i = 4 - ((ulong)r & 3);
+		if(i > n)
+			i = n;
+		if(i == 3)
+			i = 2;
+		switch(i){
+		case 4:
+			v = *(ulong*)r;
+			a[0] = v;
+			a[1] = v>>8;
+			a[2] = v>>16;
+			a[3] = v>>24;
+			break;
+		case 2:
+			v = *(ushort*)r;
+			a[0] = v;
+			a[1] = v>>8;
+			break;
+		case 1:
+			*a = *r;
+			break;
+		}
+		r += i;
+		a += i;
+		n -= i;
+	}
+	poperror();
+	qunlock(s);
+	return a-buf;
+}
+
+static Chan*
+vgaattach(char* spec)
+{
+	if(*spec && strcmp(spec, "0"))
+		error(Eio);
+	return devattach('v', spec);
+}
+
+Walkqid*
+vgawalk(Chan* c, Chan *nc, char** name, int nname)
+{
+	return devwalk(c, nc, name, nname, vgadir, nvgadir, devgen);
+}
+
+static int
+vgastat(Chan* c, uchar* dp, int n)
+{
+	return devstat(c, dp, n, vgadir, nvgadir, devgen);
+}
+
+static Chan*
+vgaopen(Chan* c, int omode)
+{
+	VGAscr *scr;
+	static char *openctl = "openctl\n";
+
+	scr = &vgascreen[0];
+	if ((ulong)c->qid.path == Qvgaovlctl) {
+		if (scr->dev && scr->dev->ovlctl)
+			scr->dev->ovlctl(scr, c, openctl, strlen(openctl));
+		else 
+			error(Enonexist);
+	}
+	return devopen(c, omode, vgadir, nvgadir, devgen);
+}
+
+static void
+vgaclose(Chan* c)
+{
+	VGAscr *scr;
+	static char *closectl = "closectl\n";
+
+	scr = &vgascreen[0];
+	if((ulong)c->qid.path == Qvgaovlctl)
+		if(scr->dev && scr->dev->ovlctl){
+			if(waserror()){
+				print("ovlctl error: %s\n", up->env->errstr);
+				return;
+			}
+			scr->dev->ovlctl(scr, c, closectl, strlen(closectl));
+			poperror();
+		}
+}
+
+static void
+checkport(int start, int end)
+{
+	/* standard vga regs are OK */
+	if(start >= 0x2b0 && end <= 0x2df+1)
+		return;
+	if(start >= 0x3c0 && end <= 0x3da+1)
+		return;
+
+	if(iounused(start, end))
+		return;
+	error(Eperm);
+}
+
+static long
+vgaread(Chan* c, void* a, long n, vlong off)
+{
+	int len;
+	char *p, *s;
+	VGAscr *scr;
+	ulong offset = off;
+	char chbuf[30];
+
+	switch((ulong)c->qid.path){
+
+	case Qdir:
+		return devdirread(c, a, n, vgadir, nvgadir, devgen);
+
+	case Qvgactl:
+		scr = &vgascreen[0];
+
+		p = malloc(READSTR);
+		if(waserror()){
+			free(p);
+			nexterror();
+		}
+
+		len = 0;
+
+		if(scr->dev)
+			s = scr->dev->name;
+		else
+			s = "cga";
+		len += snprint(p+len, READSTR-len, "type %s\n", s);
+
+		if(scr->gscreen) {
+			len += snprint(p+len, READSTR-len, "size %dx%dx%d %s\n",
+				scr->gscreen->r.max.x, scr->gscreen->r.max.y,
+				scr->gscreen->depth, chantostr(chbuf, scr->gscreen->chan));
+
+			if(Dx(scr->gscreen->r) != Dx(physgscreenr) 
+			|| Dy(scr->gscreen->r) != Dy(physgscreenr))
+				len += snprint(p+len, READSTR-len, "actualsize %dx%d\n",
+					physgscreenr.max.x, physgscreenr.max.y);
+		}
+
+		len += snprint(p+len, READSTR-len, "blank time %lud idle %d state %s\n",
+			blanktime, drawidletime(), scr->isblank ? "off" : "on");
+		len += snprint(p+len, READSTR-len, "hwaccel %s\n", hwaccel ? "on" : "off");
+		len += snprint(p+len, READSTR-len, "hwblank %s\n", hwblank ? "on" : "off");
+		len += snprint(p+len, READSTR-len, "panning %s\n", panning ? "on" : "off");
+		snprint(p+len, READSTR-len, "addr 0x%lux\n", scr->aperture);
+		n = readstr(offset, a, n, p);
+		poperror();
+		free(p);
+
+		return n;
+
+	case Qvgaovl:
+	case Qvgaovlctl:
+		error(Ebadusefd);
+		break;
+
+	default:
+		if(c->qid.path < nvgadir)
+			return vgasegrd(&vgasegs[c->qid.path], a, n, offset);
+		error(Egreg);
+		break;
+	}
+
+	return 0;
+}
+
+static char Ebusy[] = "vga already configured";
+
+static void
+vgactl(Cmdbuf *cb)
+{
+	int align, i, size, x, y, z;
+	char *chanstr, *p;
+	ulong chan;
+	Cmdtab *ct;
+	VGAscr *scr;
+	extern VGAdev *vgadev[];
+	extern VGAcur *vgacur[];
+
+	scr = &vgascreen[0];
+	ct = lookupcmd(cb, vgactlmsg, nelem(vgactlmsg));
+	switch(ct->index){
+	case CMhwgc:
+		if(strcmp(cb->f[1], "off") == 0){
+			lock(&cursor);
+			if(scr->cur){
+				if(scr->cur->disable)
+					scr->cur->disable(scr);
+				scr->cur = nil;
+			}
+			unlock(&cursor);
+			return;
+		}
+
+		for(i = 0; vgacur[i]; i++){
+			if(strcmp(cb->f[1], vgacur[i]->name))
+				continue;
+			lock(&cursor);
+			if(scr->cur && scr->cur->disable)
+				scr->cur->disable(scr);
+			scr->cur = vgacur[i];
+			if(scr->cur->enable)
+				scr->cur->enable(scr);
+			unlock(&cursor);
+			return;
+		}
+		break;
+
+	case CMtype:
+		for(i = 0; vgadev[i]; i++){
+			if(strcmp(cb->f[1], vgadev[i]->name))
+				continue;
+			if(scr->dev && scr->dev->disable)
+				scr->dev->disable(scr);
+			scr->dev = vgadev[i];
+			if(scr->dev->enable)
+				scr->dev->enable(scr);
+			return;
+		}
+		break;
+
+	case CMsize:
+		if(drawhasclients())
+			error(Ebusy);
+
+		x = strtoul(cb->f[1], &p, 0);
+		if(x == 0 || x > 2048)
+			error(Ebadarg);
+		if(*p)
+			p++;
+
+		y = strtoul(p, &p, 0);
+		if(y == 0 || y > 2048)
+			error(Ebadarg);
+		if(*p)
+			p++;
+
+		z = strtoul(p, &p, 0);
+
+		chanstr = cb->f[2];
+		if((chan = strtochan(chanstr)) == 0)
+			error("bad channel");
+
+		if(chantodepth(chan) != z)
+			error("depth, channel do not match");
+
+		cursoroff(1);
+		deletescreenimage();
+		if(screensize(x, y, z, chan))
+			error(Egreg);
+		vgascreenwin(scr);
+		cursoron(1);
+		return;
+
+	case CMactualsize:
+		if(scr->gscreen == nil)
+			error("set the screen size first");
+
+		x = strtoul(cb->f[1], &p, 0);
+		if(x == 0 || x > 2048)
+			error(Ebadarg);
+		if(*p)
+			p++;
+
+		y = strtoul(p, nil, 0);
+		if(y == 0 || y > 2048)
+			error(Ebadarg);
+
+		if(x > scr->gscreen->r.max.x || y > scr->gscreen->r.max.y)
+			error("physical screen bigger than virtual");
+
+		physgscreenr = Rect(0,0,x,y);
+		scr->gscreen->clipr = physgscreenr;
+		return;
+	
+	case CMpalettedepth:
+		x = strtoul(cb->f[1], &p, 0);
+		if(x != 8 && x != 6)
+			error(Ebadarg);
+
+		scr->palettedepth = x;
+		return;
+
+	case CMdrawinit:
+		memimagedraw(scr->gscreen, scr->gscreen->r, memblack, ZP, nil, ZP, S);
+		if(scr && scr->dev && scr->dev->drawinit)
+			scr->dev->drawinit(scr);
+		return;
+	
+	case CMlinear:
+		if(cb->nf!=2 && cb->nf!=3)
+			error(Ebadarg);
+		size = strtoul(cb->f[1], 0, 0);
+		if(cb->nf == 2)
+			align = 0;
+		else
+			align = strtoul(cb->f[2], 0, 0);
+		if(screenaperture(size, align))
+			error("not enough free address space");
+		return;
+
+	case CMblank:
+		drawblankscreen(1);
+		return;
+	
+	case CMunblank:
+		drawblankscreen(0);
+		return;
+	
+	case CMblanktime:
+		blanktime = strtoul(cb->f[1], 0, 0);
+		return;
+
+	case CMpanning:
+		if(strcmp(cb->f[1], "on") == 0){
+			if(scr == nil || scr->cur == nil)
+				error("set screen first");
+			if(!scr->cur->doespanning)
+				error("panning not supported");
+			scr->gscreen->clipr = scr->gscreen->r;
+			panning = 1;
+		}
+		else if(strcmp(cb->f[1], "off") == 0){
+			scr->gscreen->clipr = physgscreenr;
+			panning = 0;
+		}else
+			break;
+		return;
+
+	case CMhwaccel:
+		if(strcmp(cb->f[1], "on") == 0)
+			hwaccel = 1;
+		else if(strcmp(cb->f[1], "off") == 0)
+			hwaccel = 0;
+		else
+			break;
+		return;
+	
+	case CMhwblank:
+		if(strcmp(cb->f[1], "on") == 0)
+			hwblank = 1;
+		else if(strcmp(cb->f[1], "off") == 0)
+			hwblank = 0;
+		else
+			break;
+		return;
+	}
+
+	cmderror(cb, "bad VGA control message");
+}
+
+char Enooverlay[] = "No overlay support";
+
+static long
+vgawrite(Chan* c, void* a, long n, vlong off)
+{
+	ulong offset = off;
+	Cmdbuf *cb;
+	VGAscr *scr;
+
+	switch((ulong)c->qid.path){
+
+	case Qdir:
+		error(Eperm);
+
+	case Qvgactl:
+		if(offset || n >= READSTR)
+			error(Ebadarg);
+		cb = parsecmd(a, n);
+		if(waserror()){
+			free(cb);
+			nexterror();
+		}
+		vgactl(cb);
+		poperror();
+		free(cb);
+		return n;
+
+	case Qvgaovl:
+		scr = &vgascreen[0];
+		if (scr->dev == nil || scr->dev->ovlwrite == nil) {
+			error(Enooverlay);
+			break;
+		}
+		return scr->dev->ovlwrite(scr, a, n, off);
+
+	case Qvgaovlctl:
+		scr = &vgascreen[0];
+		if (scr->dev == nil || scr->dev->ovlctl == nil) {
+			error(Enooverlay);
+			break;
+		}
+		scr->dev->ovlctl(scr, c, a, n);
+		return n;
+
+	default:
+		if(c->qid.path < nvgadir)
+			return vgasegwr(&vgasegs[c->qid.path], a, n, offset);
+		error(Egreg);
+		break;
+	}
+
+	return 0;
+}
+
+Dev vgadevtab = {
+	'v',
+	"vga",
+
+	vgareset,
+	devinit,
+	devshutdown,
+	vgaattach,
+	vgawalk,
+	vgastat,
+	vgaopen,
+	devcreate,
+	vgaclose,
+	vgaread,
+	devbread,
+	vgawrite,
+	devbwrite,
+	devremove,
+	devwstat,
+};
--- /dev/null
+++ b/os/pc/devzt5512.c
@@ -1,0 +1,308 @@
+/*
+ *  Namespace Interface for Ziatech 5512 System Registers
+ */
+#include	"u.h"
+#include	"../port/lib.h"
+#include	"mem.h"
+#include	"dat.h"
+#include	"fns.h"
+#include	"../port/error.h"
+
+
+enum{
+	Qdir,
+	Qsysid,
+	Qwatchdog,
+	Qledctl,
+	Qpower,
+	Qswitch,
+	Qstat,
+};
+
+static
+Dirtab zttab[]={
+	".",			{Qdir,0,QTDIR},		0,	0555,
+	"id",			{Qsysid, 0},		0,	0444,
+	"watchdog",	{Qwatchdog, 0}, 	0,	0600,
+	"ledctl",		{Qledctl, 0},		0,	0666,
+	"powerstat",	{Qpower, 0},		0,	0444,
+	"switch",		{Qswitch, 0},		0,	0444,
+	"stat",		{Qstat, 0},			0,	0444,
+};
+
+extern int watchdog;
+void
+watchdog_strobe(void)
+{
+	uchar sysreg;
+
+	sysreg = inb(0x78);
+	sysreg &= (~1);
+	outb(0x78, sysreg);	/* disable/strobe watchdog */
+	sysreg |= 1;
+	outb(0x78, sysreg);	/* enable watchdog */
+}
+
+static void
+ztreset(void)						/* default in dev.c */
+{
+	uchar sysreg;
+
+	if(watchdog)
+		addclock0link(watchdog_strobe);
+	/* clear status LEDs */
+	sysreg = inb(0xe2);
+	sysreg &= ~3;		/* clear usr1 */
+	sysreg &= ~(3 << 2); /* clear usr2 */
+	outb(0xe2, sysreg);
+}
+
+static Chan*
+ztattach(char* spec)
+{
+	return devattach('Z', spec);
+}
+
+static int
+ztwalk(Chan* c, char* name)
+{
+	return devwalk(c, name, zttab, nelem(zttab), devgen);
+}
+
+static void
+ztstat(Chan* c, char* db)
+{
+	devstat(c, db, zttab, nelem(zttab), devgen);
+}
+
+static Chan*
+ztopen(Chan* c, int omode)
+{
+	return devopen(c, omode, zttab, nelem(zttab), devgen);
+}
+
+static void
+ztclose(Chan* c)
+{
+	USED(c);
+}
+
+static long
+ztread(Chan* c, void* a, long n, vlong offset)
+{
+	uchar sysreg;
+	char buf[256];
+
+	USED(offset);
+
+	switch(c->qid.path & ~CHDIR) {
+	case Qdir:
+		return devdirread(c, a, n, zttab, nelem(zttab), devgen);
+	case Qsysid: {
+		ulong rev;
+		sysreg = inb(0xe3);
+		rev = (ulong) (sysreg & 0x7f);
+		sysreg = inb(0xe2);
+		sprint(buf, "Board Rev: %lud\nSerial #: %lud\n", rev, (ulong)(sysreg >> 4));
+		return readstr(offset, a, n, buf);
+		};
+	case Qwatchdog:
+		sysreg = inb(0x78);
+		if((sysreg & 1) == 1) {
+			n = readstr(offset, a, n, "enabled");
+		} else {
+			n = readstr(offset, a, n, "disabled");
+		}
+		return n;
+	case Qledctl:
+		{
+		char usr1[6], usr2[6];
+		sysreg = inb(0xe2);
+		switch( sysreg & 3 ) {
+			case 0:
+			case 3:
+				sprint(usr1, "off");
+				break;
+			case 1:
+				sprint(usr1, "red");
+				break;
+			case 2:
+				sprint(usr1, "green");
+		};
+		switch( (sysreg >> 2) & 3) {
+			case 0:
+			case 3:
+				sprint(usr2, "off");
+				break;
+			case 1:
+				sprint(usr2, "red");
+				break;
+			case 2:
+				sprint(usr2, "green");
+		};
+		sprint(buf, "usr1: %s\nusr2: %s\n",usr1, usr2);
+		return readstr(offset, a, n, buf);
+		};
+	case Qpower:
+		sysreg = inb(0xe4);
+		sprint(buf, "DEG#: %d\nFAL#: %d\n", (sysreg & 2), (sysreg & 1));
+		return readstr(offset, a, n, buf);
+	case Qswitch:
+		sysreg = inb(0xe4);
+		sprint(buf, "%d %d %d %d", (sysreg & (1<<6)), (sysreg & (1<<5)), (sysreg & (1<<4)), (sysreg & (1<<3)));
+		return readstr(offset, a, n, buf);
+	case Qstat: {
+		char bus[10],cpu[20], mode[20], boot[20];
+
+		sysreg = inb(0xe5);
+		switch (sysreg & 0x7) {
+			case 1: 
+				sprint(bus, "66 MHz");
+				break;
+			case 2:
+				sprint(bus, "60 MHz");
+				break;
+			case 3:
+				sprint(bus, "50 MHz");
+				break;
+			default:
+				sprint(bus, "unknown");
+		};
+		switch ((sysreg>>3)&0x7) {
+			case 0:
+				sprint(cpu, "75, 90, 100 MHz");
+				break;
+			case 1:
+				sprint(cpu, "120, 133 MHz");
+				break;
+			case 2:
+				sprint(cpu, "180, 200 MHz");
+				break;
+			case 3:
+				sprint(cpu, "150, 166 MHz");
+			default:
+				sprint(cpu, "unknown");
+		};
+		if(sysreg & (1<<6)) 
+			sprint(mode, "Port 80 test mode");
+		else
+			sprint(mode, "Normal decode");
+		if(sysreg & (1<<7))
+			sprint(boot,"EEPROM");
+		else
+			sprint(boot,"Flash");
+		sprint(buf,"Bus Frequency: %s\nPentium: %s\nTest Mode Status: %s\nBIOS Boot ROM: %s\n",
+				bus, cpu, mode, boot);
+		return readstr(offset, a, n, buf);
+		};
+	default:
+		n=0;
+		break;
+	}
+	return n;
+}
+
+
+
+static long
+ztwrite(Chan* c, void *vp, long n, vlong offset)
+{
+	uchar sysreg;
+	char buf[256];
+	char *a;
+	int nf;
+	char *fields[3];
+
+	a = vp;
+	if(n >= sizeof(buf))
+		n = sizeof(buf)-1;
+	strncpy(buf, a, n);
+	buf[n] = 0;	
+
+	USED(a, offset);
+
+	switch(c->qid.path & ~CHDIR){
+	case Qwatchdog:
+		sysreg = inb(0x78);
+
+		if(strncmp(buf, "enable", 6) == 0) {
+			if((sysreg & 1) != 1)
+		 		addclock0link(watchdog_strobe);
+			break;
+		}
+		n = 0;
+		error(Ebadarg);
+	case Qledctl:
+		nf = getfields(buf, fields, 3, 1, " \t\n");
+		if(nf < 2) {
+			error(Ebadarg);
+			n = 0;
+			break;
+		}
+		sysreg = inb(0xe2);
+		USED(sysreg);
+		if(strncmp(fields[0],"usr1", 4)==0) {
+			sysreg &= ~3;
+			if(strncmp(fields[1], "off", 3)==0) {
+				outb(0xe2, sysreg);
+				break;
+			} 
+			if(strncmp(fields[1], "red", 3)==0) {
+				sysreg |= 1;
+				outb(0xe2, sysreg);
+				break;
+			} 
+			if(strncmp(fields[1], "green", 5)==0) {
+				sysreg |= 2;
+				outb(0xe2, sysreg);
+				break;
+			} 		
+		}
+		if(strncmp(fields[0],"usr2", 4)==0) {
+			sysreg &= ~(3 << 2);
+			if(strncmp(fields[1], "off", 3)==0) {
+				outb(0xe2, sysreg);
+				break;
+			} 
+			if(strncmp(fields[1], "red", 3)==0) {
+				sysreg |= (1 << 2);
+				outb(0xe2, sysreg);
+				break;
+			} 
+			if(strncmp(fields[1], "green", 5)==0) {
+				sysreg |= (2 << 2);
+				outb(0xe2, sysreg);
+				break;
+			} 		
+		}
+		n = 0;
+		error(Ebadarg);
+	default:
+		error(Ebadusefd);
+	}
+	return n;
+}
+
+
+
+Dev zt5512devtab = {				/* defaults in dev.c */
+	'Z',
+	"Ziatech5512",
+
+	ztreset,						/* devreset */
+	devinit,						/* devinit */
+	ztattach,
+	devdetach,
+	devclone,						/* devclone */
+	ztwalk,
+	ztstat,
+	ztopen,
+	devcreate,					/* devcreate */
+	ztclose,
+	ztread,
+	devbread,						/* devbread */
+	ztwrite,
+	devbwrite,					/* devbwrite */
+	devremove,					/* devremove */
+	devwstat,						/* devwstat */
+};
--- /dev/null
+++ b/os/pc/dma.c
@@ -1,0 +1,237 @@
+#include	"u.h"
+#include	"../port/lib.h"
+#include	"mem.h"
+#include	"dat.h"
+#include	"fns.h"
+
+typedef struct DMAport	DMAport;
+typedef struct DMA	DMA;
+typedef struct DMAxfer	DMAxfer;
+
+/*
+ *  state of a dma transfer
+ */
+struct DMAxfer
+{
+	ulong	bpa;		/* bounce buffer physical address */
+	void*	bva;		/* bounce buffer virtual address */
+	int	blen;		/* bounce buffer length */
+	void*	va;		/* virtual address destination/src */
+	long	len;		/* bytes to be transferred */
+	int	isread;
+};
+
+/*
+ *  the dma controllers.  the first half of this structure specifies
+ *  the I/O ports used by the DMA controllers.
+ */
+struct DMAport
+{
+	uchar	addr[4];	/* current address (4 channels) */
+	uchar	count[4];	/* current count (4 channels) */
+	uchar	page[4];	/* page registers (4 channels) */
+	uchar	cmd;		/* command status register */
+	uchar	req;		/* request registers */
+	uchar	sbm;		/* single bit mask register */
+	uchar	mode;		/* mode register */
+	uchar	cbp;		/* clear byte pointer */
+	uchar	mc;		/* master clear */
+	uchar	cmask;		/* clear mask register */
+	uchar	wam;		/* write all mask register bit */
+};
+
+struct DMA
+{
+	DMAport;
+	int	shift;
+	Lock;
+	DMAxfer	x[4];
+};
+
+DMA dma[2] = {
+	{ 0x00, 0x02, 0x04, 0x06,
+	  0x01, 0x03, 0x05, 0x07,
+	  0x87, 0x83, 0x81, 0x82,
+	  0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f,
+	 0 },
+
+	{ 0xc0, 0xc4, 0xc8, 0xcc,
+	  0xc2, 0xc6, 0xca, 0xce,
+	  0x8f, 0x8b, 0x89, 0x8a,
+	  0xd0, 0xd2, 0xd4, 0xd6, 0xd8, 0xda, 0xdc, 0xde,
+	 1 },
+};
+
+/*
+ *  DMA must be in the first 16MB.  This gets called early by the
+ *  initialisation routines of any devices which require DMA to ensure
+ *  the allocated bounce buffers are below the 16MB limit.
+ */
+int
+dmainit(int chan, int maxtransfer)
+{
+	DMA *dp;
+	DMAxfer *xp;
+	static int once;
+
+	if(once == 0){
+		if(ioalloc(0x00, 0x10, 0, "dma") < 0
+		|| ioalloc(0x80, 0x10, 0, "dma") < 0
+		|| ioalloc(0xd0, 0x10, 0, "dma") < 0)
+			panic("dmainit");
+		once = 1;
+	}
+
+	if(maxtransfer > 64*1024)
+		maxtransfer = 64*1024;
+
+	dp = &dma[(chan>>2)&1];
+	chan = chan & 3;
+	xp = &dp->x[chan];
+	if(xp->bva != nil){
+		if(xp->blen < maxtransfer)
+			return 1;
+		return 0;
+	}
+
+	xp->bva = xspanalloc(maxtransfer, BY2PG, 64*1024);
+	if(xp->bva == nil)
+		return 1;
+	xp->bpa = PADDR(xp->bva);
+	if(xp->bpa >= 16*MB){
+		/*
+		 * This will panic with the current
+		 * implementation of xspanalloc().
+		xfree(xp->bva);
+		 */
+		xp->bva = nil;
+		return 1;
+	}
+	xp->blen = maxtransfer;
+	xp->len = 0;
+	xp->isread = 0;
+
+	return 0;
+}
+
+/*
+ *  setup a dma transfer.  if the destination is not in kernel
+ *  memory, allocate a page for the transfer.
+ *
+ *  we assume BIOS has set up the command register before we
+ *  are booted.
+ *
+ *  return the updated transfer length (we can't transfer across 64k
+ *  boundaries)
+ */
+long
+dmasetup(int chan, void *va, long len, int isread)
+{
+	DMA *dp;
+	ulong pa;
+	uchar mode;
+	DMAxfer *xp;
+
+	dp = &dma[(chan>>2)&1];
+	chan = chan & 3;
+	xp = &dp->x[chan];
+
+	/*
+	 *  if this isn't kernel memory or crossing 64k boundary or above 16 meg
+	 *  use the bounce buffer.
+	 */
+	pa = PADDR(va);
+	if((((ulong)va)&0xF0000000) != KZERO
+	|| (pa&0xFFFF0000) != ((pa+len)&0xFFFF0000)
+	|| pa >= 16*MB) {
+		if(xp->bva == nil)
+			return -1;
+		if(len > xp->blen)
+			len = xp->blen;
+		if(!isread)
+			memmove(xp->bva, va, len);
+		xp->va = va;
+		xp->len = len;
+		xp->isread = isread;
+		pa = xp->bpa;
+	}
+	else
+		xp->len = 0;
+
+	/*
+	 * this setup must be atomic
+	 */
+	ilock(dp);
+	mode = (isread ? 0x44 : 0x48) | chan;
+	outb(dp->mode, mode);	/* single mode dma (give CPU a chance at mem) */
+	outb(dp->page[chan], pa>>16);
+	outb(dp->cbp, 0);		/* set count & address to their first byte */
+	outb(dp->addr[chan], pa>>dp->shift);		/* set address */
+	outb(dp->addr[chan], pa>>(8+dp->shift));
+	outb(dp->count[chan], (len>>dp->shift)-1);		/* set count */
+	outb(dp->count[chan], ((len>>dp->shift)-1)>>8);
+	outb(dp->sbm, chan);		/* enable the channel */
+	iunlock(dp);
+
+	return len;
+}
+
+int
+dmadone(int chan)
+{
+	DMA *dp;
+
+	dp = &dma[(chan>>2)&1];
+	chan = chan & 3;
+
+	return inb(dp->cmd) & (1<<chan);
+}
+
+/*
+ *  this must be called after a dma has been completed.
+ *
+ *  if a page has been allocated for the dma,
+ *  copy the data into the actual destination
+ *  and free the page.
+ */
+void
+dmaend(int chan)
+{
+	DMA *dp;
+	DMAxfer *xp;
+
+	dp = &dma[(chan>>2)&1];
+	chan = chan & 3;
+
+	/*
+	 *  disable the channel
+	 */
+	ilock(dp);
+	outb(dp->sbm, 4|chan);
+	iunlock(dp);
+
+	xp = &dp->x[chan];
+	if(xp->len == 0 || !xp->isread)
+		return;
+
+	/*
+	 *  copy out of temporary page
+	 */
+	memmove(xp->va, xp->bva, xp->len);
+	xp->len = 0;
+}
+
+/*
+int
+dmacount(int chan)
+{
+	int     retval;
+	DMA     *dp;
+ 
+	dp = &dma[(chan>>2)&1];
+	outb(dp->cbp, 0);
+	retval = inb(dp->count[chan]);
+	retval |= inb(dp->count[chan]) << 8;
+	return((retval<<dp->shift)+1);
+}
+ */
--- /dev/null
+++ b/os/pc/ether2000.c
@@ -1,0 +1,232 @@
+#include "u.h"
+#include "../port/lib.h"
+#include "mem.h"
+#include "dat.h"
+#include "fns.h"
+#include "io.h"
+#include "../port/error.h"
+#include "../port/netif.h"
+
+#include "etherif.h"
+#include "ether8390.h"
+
+/*
+ * Driver written for the 'Notebook Computer Ethernet LAN Adapter',
+ * a plug-in to the bus-slot on the rear of the Gateway NOMAD 425DXL
+ * laptop. The manual says NE2000 compatible.
+ * The interface appears to be pretty well described in the National
+ * Semiconductor Local Area Network Databook (1992) as one of the
+ * AT evaluation cards.
+ *
+ * The NE2000 is really just a DP8390[12] plus a data port
+ * and a reset port.
+ */
+enum {
+	Data		= 0x10,		/* offset from I/O base of data port */
+	Reset		= 0x1F,		/* offset from I/O base of reset port */
+};
+
+typedef struct Ctlr Ctlr;
+typedef struct Ctlr {
+	Pcidev*	pcidev;
+	Ctlr*	next;
+	int	active;
+} Ctlr;
+
+static Ctlr* ctlrhead;
+static Ctlr* ctlrtail;
+
+static struct {
+	char*	name;
+	int	id;
+} ne2000pci[] = {
+	{ "Realtek 8029",	(0x8029<<16)|0x10EC, },
+	{ "Winbond 89C940",	(0x0940<<16)|0x1050, },
+	{ nil },
+};
+
+static Ctlr*
+ne2000match(Ether* edev, int id)
+{
+	int port;
+	Pcidev *p;
+	Ctlr *ctlr;
+
+	/*
+	 * Any adapter matches if no edev->port is supplied,
+	 * otherwise the ports must match.
+	 */
+	for(ctlr = ctlrhead; ctlr != nil; ctlr = ctlr->next){
+		if(ctlr->active)
+			continue;
+		p = ctlr->pcidev;
+		if(((p->did<<16)|p->vid) != id)
+			continue;
+		port = p->mem[0].bar & ~0x01;
+		if(edev->port != 0 && edev->port != port)
+			continue;
+
+		/*
+		 * It suffices to fill these in,
+		 * the rest is gleaned from the card.
+		 */
+		edev->port = port;
+		edev->irq = p->intl;
+
+		ctlr->active = 1;
+
+		return ctlr;
+	}
+
+	return nil;
+}
+
+static void
+ne2000pnp(Ether* edev)
+{
+	int i, id;
+	Pcidev *p;
+	Ctlr *ctlr;
+
+	/*
+	 * Make a list of all ethernet controllers
+	 * if not already done.
+	 */
+	if(ctlrhead == nil){
+		p = nil;
+		while(p = pcimatch(p, 0, 0)){
+			if(p->ccrb != 0x02 || p->ccru != 0)
+				continue;
+			ctlr = malloc(sizeof(Ctlr));
+			ctlr->pcidev = p;
+
+			if(ctlrhead != nil)
+				ctlrtail->next = ctlr;
+			else
+				ctlrhead = ctlr;
+			ctlrtail = ctlr;
+		}
+	}
+
+	/*
+	 * Is it a card with an unrecognised vid+did?
+	 * Normally a search is made through all the found controllers
+	 * for one which matches any of the known vid+did pairs.
+	 * If a vid+did pair is specified a search is made for that
+	 * specific controller only.
+	 */
+	id = 0;
+	for(i = 0; i < edev->nopt; i++){
+		if(cistrncmp(edev->opt[i], "id=", 3) == 0)
+			id = strtol(&edev->opt[i][3], nil, 0);
+	}
+
+	if(id != 0)
+		ne2000match(edev, id);
+	else for(i = 0; ne2000pci[i].name; i++){
+		if(ne2000match(edev, ne2000pci[i].id) != nil)
+			break;
+	}
+}
+
+static int
+ne2000reset(Ether* edev)
+{
+	ushort buf[16];
+	ulong port;
+	Dp8390 *dp8390;
+	int i;
+	uchar ea[Eaddrlen];
+
+	if(edev->port == 0)
+		ne2000pnp(edev);
+
+	/*
+	 * Set up the software configuration.
+	 * Use defaults for irq, mem and size
+	 * if not specified.
+	 * Must have a port, no more default.
+	 */
+	if(edev->port == 0)
+		return -1;
+	if(edev->irq == 0)
+		edev->irq = 2;
+	if(edev->mem == 0)
+		edev->mem = 0x4000;
+	if(edev->size == 0)
+		edev->size = 16*1024;
+	port = edev->port;
+
+	if(ioalloc(edev->port, 0x20, 0, "ne2000") < 0)
+		return -1;
+
+	edev->ctlr = malloc(sizeof(Dp8390));
+	dp8390 = edev->ctlr;
+	dp8390->width = 2;
+	dp8390->ram = 0;
+
+	dp8390->port = port;
+	dp8390->data = port+Data;
+
+	dp8390->tstart = HOWMANY(edev->mem, Dp8390BufSz);
+	dp8390->pstart = dp8390->tstart + HOWMANY(sizeof(Etherpkt), Dp8390BufSz);
+	dp8390->pstop = dp8390->tstart + HOWMANY(edev->size, Dp8390BufSz);
+
+	dp8390->dummyrr = 1;
+	for(i = 0; i < edev->nopt; i++){
+		if(strcmp(edev->opt[i], "nodummyrr"))
+			continue;
+		dp8390->dummyrr = 0;
+		break;
+	}
+
+	/*
+	 * Reset the board. This is done by doing a read
+	 * followed by a write to the Reset address.
+	 */
+	buf[0] = inb(port+Reset);
+	delay(2);
+	outb(port+Reset, buf[0]);
+	delay(2);
+	
+	/*
+	 * Init the (possible) chip, then use the (possible)
+	 * chip to read the (possible) PROM for ethernet address
+	 * and a marker byte.
+	 * Could just look at the DP8390 command register after
+	 * initialisation has been tried, but that wouldn't be
+	 * enough, there are other ethernet boards which could
+	 * match.
+	 * Parallels has buf[0x0E] == 0x00 whereas real hardware
+	 * usually has 0x57.
+	 */
+	dp8390reset(edev);
+	memset(buf, 0, sizeof(buf));
+	dp8390read(dp8390, buf, 0, sizeof(buf));
+	i = buf[0x0E] & 0xFF;
+	if((i != 0x00 && i != 0x57) || (buf[0x0F] & 0xFF) != 0x57){
+		iofree(edev->port);
+		free(edev->ctlr);
+		return -1;
+	}
+
+	/*
+	 * Stupid machine. Shorts were asked for,
+	 * shorts were delivered, although the PROM is a byte array.
+	 * Set the ethernet address.
+	 */
+	memset(ea, 0, Eaddrlen);
+	if(memcmp(ea, edev->ea, Eaddrlen) == 0){
+		for(i = 0; i < sizeof(edev->ea); i++)
+			edev->ea[i] = buf[i];
+	}
+	dp8390setea(edev);
+
+	return 0;
+}
+
+void
+ether2000link(void)
+{
+	addethercard("NE2000", ne2000reset);
+}
--- /dev/null
+++ b/os/pc/ether2114x.c
@@ -1,0 +1,1830 @@
+/*
+ * Digital Semiconductor DECchip 2114x PCI Fast Ethernet LAN Controller.
+ * To do:
+ *	thresholds;
+ *	ring sizing;
+ *	handle more error conditions;
+ *	tidy setup packet mess;
+ *	push initialisation back to attach;
+ *	full SROM decoding.
+ */
+#include "u.h"
+#include "../port/lib.h"
+#include "mem.h"
+#include "dat.h"
+#include "fns.h"
+#include "io.h"
+#include "../port/error.h"
+#include "../port/netif.h"
+
+#include "etherif.h"
+
+#define DEBUG		(0)
+#define debug		if(DEBUG)print
+
+enum {
+	Nrde		= 64,
+	Ntde		= 64,
+};
+
+#define Rbsz		ROUNDUP(sizeof(Etherpkt)+4, 4)
+
+enum {					/* CRS0 - Bus Mode */
+	Swr		= 0x00000001,	/* Software Reset */
+	Bar		= 0x00000002,	/* Bus Arbitration */
+	Dsl		= 0x0000007C,	/* Descriptor Skip Length (field) */
+	Ble		= 0x00000080,	/* Big/Little Endian */
+	Pbl		= 0x00003F00,	/* Programmable Burst Length (field) */
+	Cal		= 0x0000C000,	/* Cache Alignment (field) */
+	Cal8		= 0x00004000,	/* 8 longword boundary alignment */
+	Cal16		= 0x00008000,	/* 16 longword boundary alignment */
+	Cal32		= 0x0000C000,	/* 32 longword boundary alignment */
+	Tap		= 0x000E0000,	/* Transmit Automatic Polling (field) */
+	Dbo		= 0x00100000,	/* Descriptor Byte Ordering Mode */
+	Rml		= 0x00200000,	/* Read Multiple */
+}; 
+
+enum {					/* CSR[57] - Status and Interrupt Enable */
+	Ti		= 0x00000001,	/* Transmit Interrupt */
+	Tps		= 0x00000002,	/* Transmit Process Stopped */
+	Tu		= 0x00000004,	/* Transmit buffer Unavailable */
+	Tjt		= 0x00000008,	/* Transmit Jabber Timeout */
+	Unf		= 0x00000020,	/* transmit UNderFlow */
+	Ri		= 0x00000040,	/* Receive Interrupt */
+	Ru		= 0x00000080,	/* Receive buffer Unavailable */
+	Rps		= 0x00000100,	/* Receive Process Stopped */
+	Rwt		= 0x00000200,	/* Receive Watchdog Timeout */
+	Eti		= 0x00000400,	/* Early Transmit Interrupt */
+	Gte		= 0x00000800,	/* General purpose Timer Expired */
+	Fbe		= 0x00002000,	/* Fatal Bit Error */
+	Ais		= 0x00008000,	/* Abnormal Interrupt Summary */
+	Nis		= 0x00010000,	/* Normal Interrupt Summary */
+	Rs		= 0x000E0000,	/* Receive process State (field) */
+	Ts		= 0x00700000,	/* Transmit process State (field) */
+	Eb		= 0x03800000,	/* Error bits */
+};
+
+enum {					/* CSR6 - Operating Mode */
+	Hp		= 0x00000001,	/* Hash/Perfect receive filtering mode */
+	Sr		= 0x00000002,	/* Start/stop Receive */
+	Ho		= 0x00000004,	/* Hash-Only filtering mode */
+	Pb		= 0x00000008,	/* Pass Bad frames */
+	If		= 0x00000010,	/* Inverse Filtering */
+	Sb		= 0x00000020,	/* Start/stop Backoff counter */
+	Pr		= 0x00000040,	/* Promiscuous Mode */
+	Pm		= 0x00000080,	/* Pass all Multicast */
+	Fd		= 0x00000200,	/* Full Duplex mode */
+	Om		= 0x00000C00,	/* Operating Mode (field) */
+	Fc		= 0x00001000,	/* Force Collision */
+	St		= 0x00002000,	/* Start/stop Transmission Command */
+	Tr		= 0x0000C000,	/* ThReshold control bits (field) */
+	Tr128		= 0x00000000,
+	Tr256		= 0x00004000,
+	Tr512		= 0x00008000,
+	Tr1024		= 0x0000C000,
+	Ca		= 0x00020000,	/* CApture effect enable */
+	Ps		= 0x00040000,	/* Port Select */
+	Hbd		= 0x00080000,	/* HeartBeat Disable */
+	Imm		= 0x00100000,	/* IMMediate mode */
+	Sf		= 0x00200000,	/* Store and Forward */
+	Ttm		= 0x00400000,	/* Transmit Threshold Mode */
+	Pcs		= 0x00800000,	/* PCS function */
+	Scr		= 0x01000000,	/* SCRambler mode */
+	Mbo		= 0x02000000,	/* Must Be One */
+	Ra		= 0x40000000,	/* Receive All */
+	Sc		= 0x80000000,	/* Special Capture effect enable */
+
+	TrMODE		= Tr512,	/* default transmission threshold */
+};
+
+enum {					/* CSR9 - ROM and MII Management */
+	Scs		= 0x00000001,	/* serial ROM chip select */
+	Sclk		= 0x00000002,	/* serial ROM clock */
+	Sdi		= 0x00000004,	/* serial ROM data in */
+	Sdo		= 0x00000008,	/* serial ROM data out */
+	Ss		= 0x00000800,	/* serial ROM select */
+	Wr		= 0x00002000,	/* write */
+	Rd		= 0x00004000,	/* read */
+
+	Mdc		= 0x00010000,	/* MII management clock */
+	Mdo		= 0x00020000,	/* MII management write data */
+	Mii		= 0x00040000,	/* MII management operation mode (W) */
+	Mdi		= 0x00080000,	/* MII management data in */
+};
+
+enum {					/* CSR12 - General-Purpose Port */
+	Gpc		= 0x00000100,	/* General Purpose Control */
+};
+
+typedef struct Des {
+	int	status;
+	int	control;
+	ulong	addr;
+	Block*	bp;
+} Des;
+
+enum {					/* status */
+	Of		= 0x00000001,	/* Rx: OverFlow */
+	Ce		= 0x00000002,	/* Rx: CRC Error */
+	Db		= 0x00000004,	/* Rx: Dribbling Bit */
+	Re		= 0x00000008,	/* Rx: Report on MII Error */
+	Rw		= 0x00000010,	/* Rx: Receive Watchdog */
+	Ft		= 0x00000020,	/* Rx: Frame Type */
+	Cs		= 0x00000040,	/* Rx: Collision Seen */
+	Tl		= 0x00000080,	/* Rx: Frame too Long */
+	Ls		= 0x00000100,	/* Rx: Last deScriptor */
+	Fs		= 0x00000200,	/* Rx: First deScriptor */
+	Mf		= 0x00000400,	/* Rx: Multicast Frame */
+	Rf		= 0x00000800,	/* Rx: Runt Frame */
+	Dt		= 0x00003000,	/* Rx: Data Type (field) */
+	De		= 0x00004000,	/* Rx: Descriptor Error */
+	Fl		= 0x3FFF0000,	/* Rx: Frame Length (field) */
+	Ff		= 0x40000000,	/* Rx: Filtering Fail */
+
+	Def		= 0x00000001,	/* Tx: DEFerred */
+	Uf		= 0x00000002,	/* Tx: UnderFlow error */
+	Lf		= 0x00000004,	/* Tx: Link Fail report */
+	Cc		= 0x00000078,	/* Tx: Collision Count (field) */
+	Hf		= 0x00000080,	/* Tx: Heartbeat Fail */
+	Ec		= 0x00000100,	/* Tx: Excessive Collisions */
+	Lc		= 0x00000200,	/* Tx: Late Collision */
+	Nc		= 0x00000400,	/* Tx: No Carrier */
+	Lo		= 0x00000800,	/* Tx: LOss of carrier */
+	To		= 0x00004000,	/* Tx: Transmission jabber timeOut */
+
+	Es		= 0x00008000,	/* [RT]x: Error Summary */
+	Own		= 0x80000000,	/* [RT]x: OWN bit */
+};
+
+enum {					/* control */
+	Bs1		= 0x000007FF,	/* [RT]x: Buffer 1 Size */
+	Bs2		= 0x003FF800,	/* [RT]x: Buffer 2 Size */
+
+	Ch		= 0x01000000,	/* [RT]x: second address CHained */
+	Er		= 0x02000000,	/* [RT]x: End of Ring */
+
+	Ft0		= 0x00400000,	/* Tx: Filtering Type 0 */
+	Dpd		= 0x00800000,	/* Tx: Disabled PaDding */
+	Ac		= 0x04000000,	/* Tx: Add CRC disable */
+	Set		= 0x08000000,	/* Tx: SETup packet */
+	Ft1		= 0x10000000,	/* Tx: Filtering Type 1 */
+	Fseg		= 0x20000000,	/* Tx: First SEGment */
+	Lseg		= 0x40000000,	/* Tx: Last SEGment */
+	Ic		= 0x80000000,	/* Tx: Interrupt on Completion */
+};
+
+enum {					/* PHY registers */
+	Bmcr		= 0,		/* Basic Mode Control */
+	Bmsr		= 1,		/* Basic Mode Status */
+	Phyidr1		= 2,		/* PHY Identifier #1 */
+	Phyidr2		= 3,		/* PHY Identifier #2 */
+	Anar		= 4,		/* Auto-Negotiation Advertisment */
+	Anlpar		= 5,		/* Auto-Negotiation Link Partner Ability */
+	Aner		= 6,		/* Auto-Negotiation Expansion */
+};
+
+enum {					/* Variants */
+	Tulip0		= (0x0009<<16)|0x1011,
+	Tulip1		= (0x0014<<16)|0x1011,
+	Tulip3		= (0x0019<<16)|0x1011,
+	Pnic		= (0x0002<<16)|0x11AD,
+	Pnic2		= (0xC115<<16)|0x11AD,
+	CentaurP	= (0x0985<<16)|0x1317,
+	CentaurPcb	= (0x1985<<16)|0x1317,
+};
+
+typedef struct Ctlr Ctlr;
+typedef struct Ctlr {
+	int	port;
+	Pcidev*	pcidev;
+	Ctlr*	next;
+	int	active;
+	int	id;			/* (pcidev->did<<16)|pcidev->vid */
+
+	uchar*	srom;
+	int	sromsz;			/* address size in bits */
+	uchar*	sromea;			/* MAC address */
+	uchar*	leaf;
+	int	sct;			/* selected connection type */
+	int	k;			/* info block count */
+	uchar*	infoblock[16];
+	int	sctk;			/* sct block index */
+	int	curk;			/* current block index */
+	uchar*	type5block;
+
+	int	phy[32];		/* logical to physical map */
+	int	phyreset;		/* reset bitmap */
+	int	curphyad;
+	int	fdx;
+	int	ttm;
+
+	uchar	fd;			/* option */
+	int	medium;			/* option */
+
+	int	csr6;			/* CSR6 - operating mode */
+	int	mask;			/* CSR[57] - interrupt mask */
+	int	mbps;
+
+	Lock	lock;
+
+	Des*	rdr;			/* receive descriptor ring */
+	int	nrdr;			/* size of rdr */
+	int	rdrx;			/* index into rdr */
+
+	Lock	tlock;
+	Des*	tdr;			/* transmit descriptor ring */
+	int	ntdr;			/* size of tdr */
+	int	tdrh;			/* host index into tdr */
+	int	tdri;			/* interface index into tdr */
+	int	ntq;			/* descriptors active */
+	int	ntqmax;
+	Block*	setupbp;
+
+	ulong	of;			/* receive statistics */
+	ulong	ce;
+	ulong	cs;
+	ulong	tl;
+	ulong	rf;
+	ulong	de;
+
+	ulong	ru;
+	ulong	rps;
+	ulong	rwt;
+
+	ulong	uf;			/* transmit statistics */
+	ulong	ec;
+	ulong	lc;
+	ulong	nc;
+	ulong	lo;
+	ulong	to;
+
+	ulong	tps;
+	ulong	tu;
+	ulong	tjt;
+	ulong	unf;
+} Ctlr;
+
+static Ctlr* ctlrhead;
+static Ctlr* ctlrtail;
+
+#define csr32r(c, r)	(inl((c)->port+((r)*8)))
+#define csr32w(c, r, l)	(outl((c)->port+((r)*8), (ulong)(l)))
+
+static void
+promiscuous(void* arg, int on)
+{
+	Ctlr *ctlr;
+
+	ctlr = ((Ether*)arg)->ctlr;
+	ilock(&ctlr->lock);
+	if(on)
+		ctlr->csr6 |= Pr;
+	else
+		ctlr->csr6 &= ~Pr;
+	csr32w(ctlr, 6, ctlr->csr6);
+	iunlock(&ctlr->lock);
+}
+
+/* multicast already on, don't need to do anything */
+static void
+multicast(void*, uchar*, int)
+{
+}
+
+static void
+attach(Ether* ether)
+{
+	Ctlr *ctlr;
+
+	ctlr = ether->ctlr;
+	ilock(&ctlr->lock);
+	if(!(ctlr->csr6 & Sr)){
+		ctlr->csr6 |= Sr;
+		csr32w(ctlr, 6, ctlr->csr6);
+	}
+	iunlock(&ctlr->lock);
+}
+
+static long
+ifstat(Ether* ether, void* a, long n, ulong offset)
+{
+	Ctlr *ctlr;
+	char *buf, *p;
+	int i, l, len;
+
+	ctlr = ether->ctlr;
+
+	ether->crcs = ctlr->ce;
+	ether->frames = ctlr->rf+ctlr->cs;
+	ether->buffs = ctlr->de+ctlr->tl;
+	ether->overflows = ctlr->of;
+
+	if(n == 0)
+		return 0;
+
+	p = malloc(READSTR);
+	l = snprint(p, READSTR, "Overflow: %lud\n", ctlr->of);
+	l += snprint(p+l, READSTR-l, "Ru: %lud\n", ctlr->ru);
+	l += snprint(p+l, READSTR-l, "Rps: %lud\n", ctlr->rps);
+	l += snprint(p+l, READSTR-l, "Rwt: %lud\n", ctlr->rwt);
+	l += snprint(p+l, READSTR-l, "Tps: %lud\n", ctlr->tps);
+	l += snprint(p+l, READSTR-l, "Tu: %lud\n", ctlr->tu);
+	l += snprint(p+l, READSTR-l, "Tjt: %lud\n", ctlr->tjt);
+	l += snprint(p+l, READSTR-l, "Unf: %lud\n", ctlr->unf);
+	l += snprint(p+l, READSTR-l, "CRC Error: %lud\n", ctlr->ce);
+	l += snprint(p+l, READSTR-l, "Collision Seen: %lud\n", ctlr->cs);
+	l += snprint(p+l, READSTR-l, "Frame Too Long: %lud\n", ctlr->tl);
+	l += snprint(p+l, READSTR-l, "Runt Frame: %lud\n", ctlr->rf);
+	l += snprint(p+l, READSTR-l, "Descriptor Error: %lud\n", ctlr->de);
+	l += snprint(p+l, READSTR-l, "Underflow Error: %lud\n", ctlr->uf);
+	l += snprint(p+l, READSTR-l, "Excessive Collisions: %lud\n", ctlr->ec);
+	l += snprint(p+l, READSTR-l, "Late Collision: %lud\n", ctlr->lc);
+	l += snprint(p+l, READSTR-l, "No Carrier: %lud\n", ctlr->nc);
+	l += snprint(p+l, READSTR-l, "Loss of Carrier: %lud\n", ctlr->lo);
+	l += snprint(p+l, READSTR-l, "Transmit Jabber Timeout: %lud\n",
+		ctlr->to);
+	l += snprint(p+l, READSTR-l, "csr6: %luX %uX\n", csr32r(ctlr, 6),
+		ctlr->csr6);
+	snprint(p+l, READSTR-l, "ntqmax: %d\n", ctlr->ntqmax);
+	ctlr->ntqmax = 0;
+	buf = a;
+	len = readstr(offset, buf, n, p);
+	if(offset > l)
+		offset -= l;
+	else
+		offset = 0;
+	buf += len;
+	n -= len;
+
+	l = snprint(p, READSTR, "srom:");
+	for(i = 0; i < (1<<(ctlr->sromsz)*sizeof(ushort)); i++){
+		if(i && ((i & 0x0F) == 0))
+			l += snprint(p+l, READSTR-l, "\n     ");
+		l += snprint(p+l, READSTR-l, " %2.2uX", ctlr->srom[i]);
+	}
+
+	snprint(p+l, READSTR-l, "\n");
+	len += readstr(offset, buf, n, p);
+	free(p);
+
+	return len;
+}
+
+static void
+txstart(Ether* ether)
+{
+	Ctlr *ctlr;
+	Block *bp;
+	Des *des;
+	int control;
+
+	ctlr = ether->ctlr;
+	while(ctlr->ntq < (ctlr->ntdr-1)){
+		if(ctlr->setupbp){
+			bp = ctlr->setupbp;
+			ctlr->setupbp = 0;
+			control = Ic|Set|BLEN(bp);
+		}
+		else{
+			bp = qget(ether->oq);
+			if(bp == nil)
+				break;
+			control = Ic|Lseg|Fseg|BLEN(bp);
+		}
+
+		ctlr->tdr[PREV(ctlr->tdrh, ctlr->ntdr)].control &= ~Ic;
+		des = &ctlr->tdr[ctlr->tdrh];
+		des->bp = bp;
+		des->addr = PCIWADDR(bp->rp);
+		des->control |= control;
+		ctlr->ntq++;
+		coherence();
+		des->status = Own;
+		csr32w(ctlr, 1, 0);
+		ctlr->tdrh = NEXT(ctlr->tdrh, ctlr->ntdr);
+	}
+
+	if(ctlr->ntq > ctlr->ntqmax)
+		ctlr->ntqmax = ctlr->ntq;
+}
+
+static void
+transmit(Ether* ether)
+{
+	Ctlr *ctlr;
+
+	ctlr = ether->ctlr;
+	ilock(&ctlr->tlock);
+	txstart(ether);
+	iunlock(&ctlr->tlock);
+}
+
+static void
+interrupt(Ureg*, void* arg)
+{
+	Ctlr *ctlr;
+	Ether *ether;
+	int len, status;
+	Des *des;
+	Block *bp;
+
+	ether = arg;
+	ctlr = ether->ctlr;
+
+	while((status = csr32r(ctlr, 5)) & (Nis|Ais)){
+		/*
+		 * Acknowledge the interrupts and mask-out
+		 * the ones that are implicitly handled.
+		 */
+		csr32w(ctlr, 5, status);
+		status &= (ctlr->mask & ~(Nis|Ti));
+
+		if(status & Ais){
+			if(status & Tps)
+				ctlr->tps++;
+			if(status & Tu)
+				ctlr->tu++;
+			if(status & Tjt)
+				ctlr->tjt++;
+			if(status & Ru)
+				ctlr->ru++;
+			if(status & Rps)
+				ctlr->rps++;
+			if(status & Rwt)
+				ctlr->rwt++;
+			status &= ~(Ais|Rwt|Rps|Ru|Tjt|Tu|Tps);
+		}
+
+		/*
+		 * Received packets.
+		 */
+		if(status & Ri){
+			des = &ctlr->rdr[ctlr->rdrx];
+			while(!(des->status & Own)){
+				if(des->status & Es){
+					if(des->status & Of)
+						ctlr->of++;
+					if(des->status & Ce)
+						ctlr->ce++;
+					if(des->status & Cs)
+						ctlr->cs++;
+					if(des->status & Tl)
+						ctlr->tl++;
+					if(des->status & Rf)
+						ctlr->rf++;
+					if(des->status & De)
+						ctlr->de++;
+				}
+				else if(bp = iallocb(Rbsz)){
+					len = ((des->status & Fl)>>16)-4;
+					des->bp->wp = des->bp->rp+len;
+					etheriq(ether, des->bp, 1);
+					des->bp = bp;
+					des->addr = PCIWADDR(bp->rp);
+				}
+
+				des->control &= Er;
+				des->control |= Rbsz;
+				coherence();
+				des->status = Own;
+
+				ctlr->rdrx = NEXT(ctlr->rdrx, ctlr->nrdr);
+				des = &ctlr->rdr[ctlr->rdrx];
+			}
+			status &= ~Ri;
+		}
+
+		/*
+		 * Check the transmit side:
+		 *	check for Transmit Underflow and Adjust
+		 *	the threshold upwards;
+		 *	free any transmitted buffers and try to
+		 *	top-up the ring.
+		 */
+		if(status & Unf){
+			ctlr->unf++;
+			ilock(&ctlr->lock);
+			csr32w(ctlr, 6, ctlr->csr6 & ~St);
+			switch(ctlr->csr6 & Tr){
+			case Tr128:
+				len = Tr256;
+				break;
+			case Tr256:
+				len = Tr512;
+				break;
+			case Tr512:
+				len = Tr1024;
+				break;
+			default:
+			case Tr1024:
+				len = Sf;
+				break;
+			}
+			ctlr->csr6 = (ctlr->csr6 & ~Tr)|len;
+			csr32w(ctlr, 6, ctlr->csr6);
+			iunlock(&ctlr->lock);
+			csr32w(ctlr, 5, Tps);
+			status &= ~(Unf|Tps);
+		}
+
+		ilock(&ctlr->tlock);
+		while(ctlr->ntq){
+			des = &ctlr->tdr[ctlr->tdri];
+			if(des->status & Own)
+				break;
+
+			if(des->status & Es){
+				if(des->status & Uf)
+					ctlr->uf++;
+				if(des->status & Ec)
+					ctlr->ec++;
+				if(des->status & Lc)
+					ctlr->lc++;
+				if(des->status & Nc)
+					ctlr->nc++;
+				if(des->status & Lo)
+					ctlr->lo++;
+				if(des->status & To)
+					ctlr->to++;
+				ether->oerrs++;
+			}
+
+			freeb(des->bp);
+			des->control &= Er;
+
+			ctlr->ntq--;
+			ctlr->tdri = NEXT(ctlr->tdri, ctlr->ntdr);
+		}
+		txstart(ether);
+		iunlock(&ctlr->tlock);
+
+		/*
+		 * Anything left not catered for?
+		 */
+		if(status)
+			panic("#l%d: status %8.8uX\n", ether->ctlrno, status);
+	}
+}
+
+static void
+ctlrinit(Ether* ether)
+{
+	Ctlr *ctlr;
+	Des *des;
+	Block *bp;
+	int i;
+	uchar bi[Eaddrlen*2];
+
+	ctlr = ether->ctlr;
+
+	/*
+	 * Allocate and initialise the receive ring;
+	 * allocate and initialise the transmit ring;
+	 * unmask interrupts and start the transmit side;
+	 * create and post a setup packet to initialise
+	 * the physical ethernet address.
+	 */
+	ctlr->rdr = xspanalloc(ctlr->nrdr*sizeof(Des), 8*sizeof(ulong), 0);
+	for(des = ctlr->rdr; des < &ctlr->rdr[ctlr->nrdr]; des++){
+		des->bp = iallocb(Rbsz);
+		if(des->bp == nil)
+			panic("can't allocate ethernet receive ring\n");
+		des->status = Own;
+		des->control = Rbsz;
+		des->addr = PCIWADDR(des->bp->rp);
+	}
+	ctlr->rdr[ctlr->nrdr-1].control |= Er;
+	ctlr->rdrx = 0;
+	csr32w(ctlr, 3, PCIWADDR(ctlr->rdr));
+
+	ctlr->tdr = xspanalloc(ctlr->ntdr*sizeof(Des), 8*sizeof(ulong), 0);
+	ctlr->tdr[ctlr->ntdr-1].control |= Er;
+	ctlr->tdrh = 0;
+	ctlr->tdri = 0;
+	csr32w(ctlr, 4, PCIWADDR(ctlr->tdr));
+
+	/*
+	 * Clear any bits in the Status Register (CSR5) as
+	 * the PNIC has a different reset value from a true 2114x.
+	 */
+	ctlr->mask = Nis|Ais|Fbe|Rwt|Rps|Ru|Ri|Unf|Tjt|Tps|Ti;
+	csr32w(ctlr, 5, ctlr->mask);
+	csr32w(ctlr, 7, ctlr->mask);
+	ctlr->csr6 |= St|Pm;
+	csr32w(ctlr, 6, ctlr->csr6);
+
+	for(i = 0; i < Eaddrlen/2; i++){
+		bi[i*4] = ether->ea[i*2];
+		bi[i*4+1] = ether->ea[i*2+1];
+		bi[i*4+2] = ether->ea[i*2+1];
+		bi[i*4+3] = ether->ea[i*2];
+	}
+	bp = iallocb(Eaddrlen*2*16);
+	if(bp == nil)
+		panic("can't allocate ethernet setup buffer\n");
+	memset(bp->rp, 0xFF, sizeof(bi));
+	for(i = sizeof(bi); i < sizeof(bi)*16; i += sizeof(bi))
+		memmove(bp->rp+i, bi, sizeof(bi));
+	bp->wp += sizeof(bi)*16;
+
+	ctlr->setupbp = bp;
+	ether->oq = qopen(256*1024, Qmsg, 0, 0);
+	transmit(ether);
+}
+
+static void
+csr9w(Ctlr* ctlr, int data)
+{
+	csr32w(ctlr, 9, data);
+	microdelay(1);
+}
+
+static int
+miimdi(Ctlr* ctlr, int n)
+{
+	int data, i;
+
+	/*
+	 * Read n bits from the MII Management Register.
+	 */
+	data = 0;
+	for(i = n-1; i >= 0; i--){
+		if(csr32r(ctlr, 9) & Mdi)
+			data |= (1<<i);
+		csr9w(ctlr, Mii|Mdc);
+		csr9w(ctlr, Mii);
+	}
+	csr9w(ctlr, 0);
+
+	return data;
+}
+
+static void
+miimdo(Ctlr* ctlr, int bits, int n)
+{
+	int i, mdo;
+
+	/*
+	 * Write n bits to the MII Management Register.
+	 */
+	for(i = n-1; i >= 0; i--){
+		if(bits & (1<<i))
+			mdo = Mdo;
+		else
+			mdo = 0;
+		csr9w(ctlr, mdo);
+		csr9w(ctlr, mdo|Mdc);
+		csr9w(ctlr, mdo);
+	}
+}
+
+static int
+miir(Ctlr* ctlr, int phyad, int regad)
+{
+	int data, i;
+
+	if(ctlr->id == Pnic){
+		i = 1000;
+		csr32w(ctlr, 20, 0x60020000|(phyad<<23)|(regad<<18));
+		do{
+			microdelay(1);
+			data = csr32r(ctlr, 20);
+		}while((data & 0x80000000) && --i);
+
+		if(i == 0)
+			return -1;
+		return data & 0xFFFF;
+	}
+
+	/*
+	 * Preamble;
+	 * ST+OP+PHYAD+REGAD;
+	 * TA + 16 data bits.
+	 */
+	miimdo(ctlr, 0xFFFFFFFF, 32);
+	miimdo(ctlr, 0x1800|(phyad<<5)|regad, 14);
+	data = miimdi(ctlr, 18);
+
+	if(data & 0x10000)
+		return -1;
+
+	return data & 0xFFFF;
+}
+
+static void
+miiw(Ctlr* ctlr, int phyad, int regad, int data)
+{
+	/*
+	 * Preamble;
+	 * ST+OP+PHYAD+REGAD+TA + 16 data bits;
+	 * Z.
+	 */
+	miimdo(ctlr, 0xFFFFFFFF, 32);
+	data &= 0xFFFF;
+	data |= (0x05<<(5+5+2+16))|(phyad<<(5+2+16))|(regad<<(2+16))|(0x02<<16);
+	miimdo(ctlr, data, 32);
+	csr9w(ctlr, Mdc);
+	csr9w(ctlr, 0);
+}
+
+static int
+sromr(Ctlr* ctlr, int r)
+{
+	int i, op, data, size;
+
+	if(ctlr->id == Pnic){
+		i = 1000;
+		csr32w(ctlr, 19, 0x600|r);
+		do{
+			microdelay(1);
+			data = csr32r(ctlr, 19);
+		}while((data & 0x80000000) && --i);
+
+		if(ctlr->sromsz == 0)
+			ctlr->sromsz = 6;
+
+		return csr32r(ctlr, 9) & 0xFFFF;
+	}
+
+	/*
+	 * This sequence for reading a 16-bit register 'r'
+	 * in the EEPROM is taken (pretty much) straight from Section
+	 * 7.4 of the 21140 Hardware Reference Manual.
+	 */
+reread:
+	csr9w(ctlr, Rd|Ss);
+	csr9w(ctlr, Rd|Ss|Scs);
+	csr9w(ctlr, Rd|Ss|Sclk|Scs);
+	csr9w(ctlr, Rd|Ss);
+
+	op = 0x06;
+	for(i = 3-1; i >= 0; i--){
+		data = Rd|Ss|(((op>>i) & 0x01)<<2)|Scs;
+		csr9w(ctlr, data);
+		csr9w(ctlr, data|Sclk);
+		csr9w(ctlr, data);
+	}
+
+	/*
+	 * First time through must work out the EEPROM size.
+	 * This doesn't seem to work on the 21041 as implemented
+	 * in Virtual PC for the Mac, so wire any 21041 to 6,
+	 * it's the only 21041 this code will ever likely see.
+	 */
+	if((size = ctlr->sromsz) == 0){
+		if(ctlr->id == Tulip1)
+			ctlr->sromsz = size = 6;
+		else
+			size = 8;
+	}
+
+	for(size = size-1; size >= 0; size--){
+		data = Rd|Ss|(((r>>size) & 0x01)<<2)|Scs;
+		csr9w(ctlr, data);
+		csr9w(ctlr, data|Sclk);
+		csr9w(ctlr, data);
+		microdelay(1);
+		if(ctlr->sromsz == 0 && !(csr32r(ctlr, 9) & Sdo))
+			break;
+	}
+
+	data = 0;
+	for(i = 16-1; i >= 0; i--){
+		csr9w(ctlr, Rd|Ss|Sclk|Scs);
+		if(csr32r(ctlr, 9) & Sdo)
+			data |= (1<<i);
+		csr9w(ctlr, Rd|Ss|Scs);
+	}
+
+	csr9w(ctlr, 0);
+
+	if(ctlr->sromsz == 0){
+		ctlr->sromsz = 8-size;
+		goto reread;
+	}
+
+	return data & 0xFFFF;
+}
+
+static void
+shutdown(Ether* ether)
+{
+	Ctlr *ctlr = ether->ctlr;
+
+print("ether2114x shutting down\n");
+	csr32w(ctlr, 0, Swr);
+}
+
+static void
+softreset(Ctlr* ctlr)
+{
+	/*
+	 * Soft-reset the controller and initialise bus mode.
+	 * Delay should be >= 50 PCI cycles (2×S @ 25MHz).
+	 */
+	csr32w(ctlr, 0, Swr);
+	microdelay(10);
+	csr32w(ctlr, 0, Rml|Cal16);
+	delay(1);
+}
+
+static int
+type5block(Ctlr* ctlr, uchar* block)
+{
+	int csr15, i, len;
+
+	/*
+	 * Reset or GPR sequence. Reset should be once only,
+	 * before the GPR sequence.
+	 * Note 'block' is not a pointer to the block head but
+	 * a pointer to the data in the block starting at the
+	 * reset length value so type5block can be used for the
+	 * sequences contained in type 1 and type 3 blocks.
+	 * The SROM docs state the 21140 type 5 block is the
+	 * same as that for the 21143, but the two controllers
+	 * use different registers and sequence-element lengths
+	 * so the 21140 code here is a guess for a real type 5
+	 * sequence.
+	 */
+	len = *block++;
+	if(ctlr->id != Tulip3){
+		for(i = 0; i < len; i++){
+			csr32w(ctlr, 12, *block);
+			block++;
+		}
+		return len;
+	}
+
+	for(i = 0; i < len; i++){
+		csr15 = *block++<<16;
+		csr15 |= *block++<<24;
+		csr32w(ctlr, 15, csr15);
+		debug("%8.8uX ", csr15);
+	}
+	return 2*len;
+}
+
+static int
+typephylink(Ctlr* ctlr, uchar*)
+{
+	int an, bmcr, bmsr, csr6, x;
+
+	/*
+	 * Fail if
+	 *	auto-negotiataion enabled but not complete;
+	 *	no valid link established.
+	 */
+	bmcr = miir(ctlr, ctlr->curphyad, Bmcr);
+	miir(ctlr, ctlr->curphyad, Bmsr);
+	bmsr = miir(ctlr, ctlr->curphyad, Bmsr);
+	debug("bmcr 0x%2.2uX bmsr 0x%2.2uX\n", bmcr, bmsr);
+	if(((bmcr & 0x1000) && !(bmsr & 0x0020)) || !(bmsr & 0x0004))
+		return 0;
+
+	if(bmcr & 0x1000){
+		an = miir(ctlr, ctlr->curphyad, Anar);
+		an &= miir(ctlr, ctlr->curphyad, Anlpar) & 0x3E0;
+		debug("an 0x%2.uX 0x%2.2uX 0x%2.2uX\n",
+	    		miir(ctlr, ctlr->curphyad, Anar),
+			miir(ctlr, ctlr->curphyad, Anlpar),
+			an);
+	
+		if(an & 0x0100)
+			x = 0x4000;
+		else if(an & 0x0080)
+			x = 0x2000;
+		else if(an & 0x0040)
+			x = 0x1000;
+		else if(an & 0x0020)
+			x = 0x0800;
+		else
+			x = 0;
+	}
+	else if((bmcr & 0x2100) == 0x2100)
+		x = 0x4000;
+	else if(bmcr & 0x2000){
+		/*
+		 * If FD capable, force it if necessary.
+		 */
+		if((bmsr & 0x4000) && ctlr->fd){
+			miiw(ctlr, ctlr->curphyad, Bmcr, 0x2100);
+			x = 0x4000;
+		}
+		else
+			x = 0x2000;
+	}
+	else if(bmcr & 0x0100)
+		x = 0x1000;
+	else
+		x = 0x0800;
+
+	csr6 = Sc|Mbo|Hbd|Ps|Ca|TrMODE|Sb;
+	if(ctlr->fdx & x)
+		csr6 |= Fd;
+	if(ctlr->ttm & x)
+		csr6 |= Ttm;
+	debug("csr6 0x%8.8uX 0x%8.8uX 0x%8.8luX\n",
+		csr6, ctlr->csr6, csr32r(ctlr, 6));
+	if(csr6 != ctlr->csr6){
+		ctlr->csr6 = csr6;
+		csr32w(ctlr, 6, csr6);
+	}
+
+	return 1;
+}
+
+static int
+typephymode(Ctlr* ctlr, uchar* block, int wait)
+{
+	uchar *p;
+	int len, mc, nway, phyx, timeo;
+
+	if(DEBUG){
+		int i;
+
+		len = (block[0] & ~0x80)+1;
+		for(i = 0; i < len; i++)
+			debug("%2.2uX ", block[i]);
+		debug("\n");
+	}
+
+	if(block[1] == 1)
+		len = 1;
+	else if(block[1] == 3)
+		len = 2;
+	else
+		return -1;
+
+	/*
+	 * Snarf the media capabilities, nway advertisment,
+	 * FDX and TTM bitmaps.
+	 */
+	p = &block[5+len*block[3]+len*block[4+len*block[3]]];
+	mc = *p++;
+	mc |= *p++<<8;
+	nway = *p++;
+	nway |= *p++<<8;
+	ctlr->fdx = *p++;
+	ctlr->fdx |= *p++<<8;
+	ctlr->ttm = *p++;
+	ctlr->ttm |= *p<<8;
+	debug("mc %4.4uX nway %4.4uX fdx %4.4uX ttm %4.4uX\n",
+		mc, nway, ctlr->fdx, ctlr->ttm);
+	USED(mc);
+
+	phyx = block[2];
+	ctlr->curphyad = ctlr->phy[phyx];
+
+	ctlr->csr6 = 0;//Sc|Mbo|Hbd|Ps|Ca|TrMODE|Sb;
+	//csr32w(ctlr, 6, ctlr->csr6);
+	if(typephylink(ctlr, block))
+		return 0;
+
+	if(!(ctlr->phyreset & (1<<phyx))){
+		debug("reset seq: len %d: ", block[3]);
+		if(ctlr->type5block)
+			type5block(ctlr, &ctlr->type5block[2]);
+		else
+			type5block(ctlr, &block[4+len*block[3]]);
+		debug("\n");
+		ctlr->phyreset |= (1<<phyx);
+	}
+
+	/*
+	 * GPR sequence.
+	 */
+	debug("gpr seq: len %d: ", block[3]);
+	type5block(ctlr, &block[3]);
+	debug("\n");
+
+	ctlr->csr6 = 0;//Sc|Mbo|Hbd|Ps|Ca|TrMODE|Sb;
+	//csr32w(ctlr, 6, ctlr->csr6);
+	if(typephylink(ctlr, block))
+		return 0;
+
+	/*
+	 * Turn off auto-negotiation, set the auto-negotiation
+	 * advertisment register then start the auto-negotiation
+	 * process again.
+	 */
+	miiw(ctlr, ctlr->curphyad, Bmcr, 0);
+	miiw(ctlr, ctlr->curphyad, Anar, nway|1);
+	miiw(ctlr, ctlr->curphyad, Bmcr, 0x1000);
+
+	if(!wait)
+		return 0;
+
+	for(timeo = 0; timeo < 45; timeo++){
+		if(typephylink(ctlr, block))
+			return 0;
+		delay(100);
+	}
+
+	return -1;
+}
+
+static int
+typesymmode(Ctlr *ctlr, uchar *block, int wait)
+{
+	uint gpmode, gpdata, command;
+
+	USED(wait);
+	gpmode = block[3] | ((uint) block[4] << 8);
+	gpdata = block[5] | ((uint) block[6] << 8);
+	command = (block[7] | ((uint) block[8] << 8)) & 0x71;
+	if (command & 0x8000) {
+		print("ether2114x.c: FIXME: handle type 4 mode blocks where cmd.active_invalid != 0\n");
+		return -1;
+	}
+	csr32w(ctlr, 15, gpmode);
+	csr32w(ctlr, 15, gpdata);
+	ctlr->csr6 = (command & 0x71) << 18;
+	csr32w(ctlr, 6, ctlr->csr6);
+	return 0;
+}
+
+static int
+type2mode(Ctlr* ctlr, uchar* block, int)
+{
+	uchar *p;
+	int csr6, csr13, csr14, csr15, gpc, gpd;
+
+	csr6 = Sc|Mbo|Ca|TrMODE|Sb;
+	debug("type2mode: medium 0x%2.2uX\n", block[2]);
+
+	/*
+	 * Don't attempt full-duplex
+	 * unless explicitly requested.
+	 */
+	if((block[2] & 0x3F) == 0x04){	/* 10BASE-TFD */
+		if(!ctlr->fd)
+			return -1;
+		csr6 |= Fd;
+	}
+
+	/*
+	 * Operating mode programming values from the datasheet
+	 * unless media specific data is explicitly given.
+	 */
+	p = &block[3];
+	if(block[2] & 0x40){
+		csr13 = (block[4]<<8)|block[3];
+		csr14 = (block[6]<<8)|block[5];
+		csr15 = (block[8]<<8)|block[7];
+		p += 6;
+	}
+	else switch(block[2] & 0x3F){
+	default:
+		return -1;
+	case 0x00:			/* 10BASE-T */
+		csr13 = 0x00000001;
+		csr14 = 0x00007F3F;
+		csr15 = 0x00000008;
+		break;
+	case 0x01:			/* 10BASE-2 */
+		csr13 = 0x00000009;
+		csr14 = 0x00000705;
+		csr15 = 0x00000006;
+		break;
+	case 0x02:			/* 10BASE-5 (AUI) */
+		csr13 = 0x00000009;
+		csr14 = 0x00000705;
+		csr15 = 0x0000000E;
+		break;
+	case 0x04:			/* 10BASE-TFD */
+		csr13 = 0x00000001;
+		csr14 = 0x00007F3D;
+		csr15 = 0x00000008;
+		break;
+	}
+	gpc = *p++<<16;
+	gpc |= *p++<<24;
+	gpd = *p++<<16;
+	gpd |= *p<<24;
+
+	csr32w(ctlr, 13, 0);
+	csr32w(ctlr, 14, csr14);
+	csr32w(ctlr, 15, gpc|csr15);
+	delay(10);
+	csr32w(ctlr, 15, gpd|csr15);
+	csr32w(ctlr, 13, csr13);
+
+	ctlr->csr6 = csr6;
+	csr32w(ctlr, 6, ctlr->csr6);
+
+	debug("type2mode: csr13 %8.8uX csr14 %8.8uX csr15 %8.8uX\n",
+		csr13, csr14, csr15);
+	debug("type2mode: gpc %8.8uX gpd %8.8uX csr6 %8.8uX\n",
+		gpc, gpd, csr6);
+
+	return 0;
+}
+
+static int
+type0link(Ctlr* ctlr, uchar* block)
+{
+	int m, polarity, sense;
+
+	m = (block[3]<<8)|block[2];
+	sense = 1<<((m & 0x000E)>>1);
+	if(m & 0x0080)
+		polarity = sense;
+	else
+		polarity = 0;
+
+	return (csr32r(ctlr, 12) & sense)^polarity;
+}
+
+static int
+type0mode(Ctlr* ctlr, uchar* block, int wait)
+{
+	int csr6, m, timeo;
+
+	csr6 = Sc|Mbo|Hbd|Ca|TrMODE|Sb;
+debug("type0: medium 0x%uX, fd %d: 0x%2.2uX 0x%2.2uX 0x%2.2uX 0x%2.2uX\n",
+    ctlr->medium, ctlr->fd, block[0], block[1], block[2], block[3]); 
+	switch(block[0]){
+	default:
+		break;
+
+	case 0x04:			/* 10BASE-TFD */
+	case 0x05:			/* 100BASE-TXFD */
+	case 0x08:			/* 100BASE-FXFD */
+		/*
+		 * Don't attempt full-duplex
+		 * unless explicitly requested.
+		 */
+		if(!ctlr->fd)
+			return -1;
+		csr6 |= Fd;
+		break;
+	}
+
+	m = (block[3]<<8)|block[2];
+	if(m & 0x0001)
+		csr6 |= Ps;
+	if(m & 0x0010)
+		csr6 |= Ttm;
+	if(m & 0x0020)
+		csr6 |= Pcs;
+	if(m & 0x0040)
+		csr6 |= Scr;
+
+	csr32w(ctlr, 12, block[1]);
+	microdelay(10);
+	csr32w(ctlr, 6, csr6);
+	ctlr->csr6 = csr6;
+
+	if(!wait)
+		return 0;
+
+	for(timeo = 0; timeo < 30; timeo++){
+		if(type0link(ctlr, block))
+			return 0;
+		delay(100);
+	}
+
+	return -1;
+}
+
+static int
+media21041(Ether* ether, int wait)
+{
+	Ctlr* ctlr;
+	uchar *block;
+	int csr6, csr13, csr14, csr15, medium, timeo;
+
+	ctlr = ether->ctlr;
+	block = ctlr->infoblock[ctlr->curk];
+	debug("media21041: block[0] %2.2uX, medium %4.4uX sct %4.4uX\n",
+		block[0], ctlr->medium, ctlr->sct);
+
+	medium = block[0] & 0x3F;
+	if(ctlr->medium >= 0 && medium != ctlr->medium)
+		return 0;
+	if(ctlr->sct != 0x0800 && (ctlr->sct & 0x3F) != medium)
+		return 0;
+
+	csr6 = Sc|Mbo|Ca|TrMODE|Sb;
+	if(block[0] & 0x40){
+		csr13 = (block[2]<<8)|block[1];
+		csr14 = (block[4]<<8)|block[3];
+		csr15 = (block[6]<<8)|block[5];
+	}
+	else switch(medium){
+	default:
+		return -1;
+	case 0x00:		/* 10BASE-T */
+		csr13 = 0xEF01;
+		csr14 = 0xFF3F;
+		csr15 = 0x0008;
+		break;
+	case 0x01:		/* 10BASE-2 */
+		csr13 = 0xEF09;
+		csr14 = 0xF73D;
+		csr15 = 0x0006;
+		break;
+	case 0x02:		/* 10BASE-5 */
+		csr13 = 0xEF09;
+		csr14 = 0xF73D;
+		csr15 = 0x000E;
+		break;
+	case 0x04:		/* 10BASE-TFD */
+		csr13 = 0xEF01;
+		csr14 = 0xFF3D;
+		csr15 = 0x0008;
+		break;
+	}
+
+	csr32w(ctlr, 13, 0);
+	csr32w(ctlr, 14, csr14);
+	csr32w(ctlr, 15, csr15);
+	csr32w(ctlr, 13, csr13);
+	delay(10);
+
+	if(medium == 0x04)
+		csr6 |= Fd;
+	ctlr->csr6 = csr6;
+	csr32w(ctlr, 6, ctlr->csr6);
+
+	debug("media21041: csr6 %8.8uX csr13 %4.4uX csr14 %4.4uX csr15 %4.4uX\n",
+		csr6, csr13, csr14, csr15);
+
+	if(!wait)
+		return 0;
+
+	for(timeo = 0; timeo < 30; timeo++){
+		if(!(csr32r(ctlr, 12) & 0x0002)){
+			debug("media21041: ok: csr12 %4.4luX timeo %d\n",
+				csr32r(ctlr, 12), timeo);
+			return 10;
+		}
+		delay(100);
+	}
+	debug("media21041: !ok: csr12 %4.4luX\n", csr32r(ctlr, 12));
+
+	return -1;
+}
+
+static int
+mediaxx(Ether* ether, int wait)
+{
+	Ctlr* ctlr;
+	uchar *block;
+
+	ctlr = ether->ctlr;
+	block = ctlr->infoblock[ctlr->curk];
+	if(block[0] & 0x80){
+		switch(block[1]){
+		default:
+			return -1;
+		case 0:
+			if(ctlr->medium >= 0 && block[2] != ctlr->medium)
+				return 0;
+/* need this test? */	if(ctlr->sct != 0x0800 && (ctlr->sct & 0x3F) != block[2])
+				return 0;
+			if(type0mode(ctlr, block+2, wait))
+				return 0;
+			break;
+		case 1:
+			if(typephymode(ctlr, block, wait))
+				return 0;
+			break;
+		case 2:
+			debug("type2: medium %d block[2] %d\n",
+				ctlr->medium, block[2]);
+			if(ctlr->medium >= 0 && ((block[2] & 0x3F) != ctlr->medium))
+				return 0;
+			if(type2mode(ctlr, block, wait))
+				return 0;
+			break;
+		case 3:
+			if(typephymode(ctlr, block, wait))
+				return 0;
+			break;
+		case 4:
+			debug("type4: medium %d block[2] %d\n",
+				ctlr->medium, block[2]);
+			if(ctlr->medium >= 0 && ((block[2] & 0x3F) != ctlr->medium))
+				return 0;
+			if(typesymmode(ctlr, block, wait))
+				return 0;
+			break;
+		}
+	}
+	else{
+		if(ctlr->medium >= 0 && block[0] != ctlr->medium)
+			return 0;
+/* need this test? */if(ctlr->sct != 0x0800 && (ctlr->sct & 0x3F) != block[0])
+			return 0;
+		if(type0mode(ctlr, block, wait))
+			return 0;
+	}
+
+	if(ctlr->csr6){
+		if(!(ctlr->csr6 & Ps) || (ctlr->csr6 & Ttm))
+			return 10;
+		return 100;
+	}
+
+	return 0;
+}
+
+static int
+media(Ether* ether, int wait)
+{
+	Ctlr* ctlr;
+	int k, mbps;
+
+	ctlr = ether->ctlr;
+	for(k = 0; k < ctlr->k; k++){
+		switch(ctlr->id){
+		default:
+			mbps = mediaxx(ether, wait);
+			break;
+		case Tulip1:			/* 21041 */
+			mbps = media21041(ether, wait);
+			break;
+		}
+		if(mbps > 0)
+			return mbps;
+		if(ctlr->curk == 0)
+			ctlr->curk = ctlr->k-1;
+		else
+			ctlr->curk--;
+	}
+
+	return 0;
+}
+
+static char* mediatable[9] = {
+	"10BASE-T",				/* TP */
+	"10BASE-2",				/* BNC */
+	"10BASE-5",				/* AUI */
+	"100BASE-TX",
+	"10BASE-TFD",
+	"100BASE-TXFD",
+	"100BASE-T4",
+	"100BASE-FX",
+	"100BASE-FXFD",
+};
+
+static uchar en1207[] = {		/* Accton EN1207-COMBO */
+	0x00, 0x00, 0xE8,		/* [0]  vendor ethernet code */
+	0x00,				/* [3]  spare */
+
+	0x00, 0x08,			/* [4]  connection (LSB+MSB = 0x0800) */
+	0x1F,				/* [6]  general purpose control */
+	2,				/* [7]  block count */
+
+	0x00,				/* [8]  media code (10BASE-TX) */
+	0x0B,				/* [9]  general purpose port data */
+	0x9E, 0x00,			/* [10] command (LSB+MSB = 0x009E) */
+
+	0x03,				/* [8]  media code (100BASE-TX) */
+	0x1B,				/* [9]  general purpose port data */
+	0x6D, 0x00,			/* [10] command (LSB+MSB = 0x006D) */
+
+					/* There is 10BASE-2 as well, but... */
+};
+
+static uchar ana6910fx[] = {		/* Adaptec (Cogent) ANA-6910FX */
+	0x00, 0x00, 0x92,		/* [0]  vendor ethernet code */
+	0x00,				/* [3]  spare */
+
+	0x00, 0x08,			/* [4]  connection (LSB+MSB = 0x0800) */
+	0x3F,				/* [6]  general purpose control */
+	1,				/* [7]  block count */
+
+	0x07,				/* [8]  media code (100BASE-FX) */
+	0x03,				/* [9]  general purpose port data */
+	0x2D, 0x00			/* [10] command (LSB+MSB = 0x000D) */
+};
+
+static uchar smc9332[] = {		/* SMC 9332 */
+	0x00, 0x00, 0xC0,		/* [0]  vendor ethernet code */
+	0x00,				/* [3]  spare */
+
+	0x00, 0x08,			/* [4]  connection (LSB+MSB = 0x0800) */
+	0x1F,				/* [6]  general purpose control */
+	2,				/* [7]  block count */
+
+	0x00,				/* [8]  media code (10BASE-TX) */
+	0x00,				/* [9]  general purpose port data */
+	0x9E, 0x00,			/* [10] command (LSB+MSB = 0x009E) */
+
+	0x03,				/* [8]  media code (100BASE-TX) */
+	0x09,				/* [9]  general purpose port data */
+	0x6D, 0x00,			/* [10] command (LSB+MSB = 0x006D) */
+};
+
+static uchar* leaf21140[] = {
+	en1207,				/* Accton EN1207-COMBO */
+	ana6910fx,			/* Adaptec (Cogent) ANA-6910FX */
+	smc9332,			/* SMC 9332 */
+	nil,
+};
+
+/*
+ * Copied to ctlr->srom at offset 20.
+ */
+static uchar leafpnic[] = {
+	0x00, 0x00, 0x00, 0x00,		/* MAC address */
+	0x00, 0x00,
+	0x00,				/* controller 0 device number */
+	0x1E, 0x00,			/* controller 0 info leaf offset */
+	0x00,				/* reserved */
+	0x00, 0x08,			/* selected connection type */
+	0x00,				/* general purpose control */
+	0x01,				/* block count */
+
+	0x8C,				/* format indicator and count */
+	0x01,				/* block type */
+	0x00,				/* PHY number */
+	0x00,				/* GPR sequence length */
+	0x00,				/* reset sequence length */
+	0x00, 0x78,			/* media capabilities */
+	0xE0, 0x01,			/* Nway advertisment */
+	0x00, 0x50,			/* FDX bitmap */
+	0x00, 0x18,			/* TTM bitmap */
+};
+
+static int
+srom(Ctlr* ctlr)
+{
+	int i, k, oui, phy, x;
+	uchar *p;
+
+	/*
+	 * This is a partial decoding of the SROM format described in
+	 * 'Digital Semiconductor 21X4 Serial ROM Format, Version 4.05,
+	 * 2-Mar-98'. Only the 2114[03] are handled, support for other
+	 * controllers can be added as needed.
+	 * Do a dummy read first to get the size and allocate ctlr->srom.
+	 */
+	sromr(ctlr, 0);
+	if(ctlr->srom == nil)
+		ctlr->srom = malloc((1<<ctlr->sromsz)*sizeof(ushort));
+	for(i = 0; i < (1<<ctlr->sromsz); i++){
+		x = sromr(ctlr, i);
+		ctlr->srom[2*i] = x;
+		ctlr->srom[2*i+1] = x>>8;
+	}
+
+	if(DEBUG){
+		print("srom:");
+		for(i = 0; i < ((1<<ctlr->sromsz)*sizeof(ushort)); i++){
+			if(i && ((i & 0x0F) == 0))
+				print("\n     ");
+			print(" %2.2uX", ctlr->srom[i]);
+		}
+		print("\n");
+	}
+
+	/*
+	 * There are at least 2 SROM layouts:
+	 *	e.g. Digital EtherWORKS	station address at offset 20;
+	 *				this complies with the 21140A SROM
+	 *				application note from Digital;
+	 * 	e.g. SMC9332		station address at offset 0 followed by
+	 *				2 additional bytes, repeated at offset
+	 *				6; the 8 bytes are also repeated in
+	 *				reverse order at offset 8.
+	 * To check which it is, read the SROM and check for the repeating
+	 * patterns of the non-compliant cards; if that fails use the one at
+	 * offset 20.
+	 */
+	ctlr->sromea = ctlr->srom;
+	for(i = 0; i < 8; i++){
+		x = ctlr->srom[i];
+		if(x != ctlr->srom[15-i] || x != ctlr->srom[16+i]){
+			ctlr->sromea = &ctlr->srom[20];
+			break;
+		}
+	}
+
+	/*
+	 * Fake up the SROM for the PNIC and AMDtek.
+	 * They look like a 21140 with a PHY.
+	 * The MAC address is byte-swapped in the orginal
+	 * PNIC SROM data.
+	 */
+	if(ctlr->id == Pnic){
+		memmove(&ctlr->srom[20], leafpnic, sizeof(leafpnic));
+		for(i = 0; i < Eaddrlen; i += 2){
+			ctlr->srom[20+i] = ctlr->srom[i+1];
+			ctlr->srom[20+i+1] = ctlr->srom[i];
+		}
+	}
+	if(ctlr->id == CentaurP || ctlr->id == CentaurPcb){
+		memmove(&ctlr->srom[20], leafpnic, sizeof(leafpnic));
+		for(i = 0; i < Eaddrlen; i += 2){
+			ctlr->srom[20+i] = ctlr->srom[8+i];
+			ctlr->srom[20+i+1] = ctlr->srom[8+i+1];
+		}
+	}
+
+	/*
+	 * Next, try to find the info leaf in the SROM for media detection.
+	 * If it's a non-conforming card try to match the vendor ethernet code
+	 * and point p at a fake info leaf with compact 21140 entries.
+	 */
+	if(ctlr->sromea == ctlr->srom){
+		p = nil;
+		for(i = 0; leaf21140[i] != nil; i++){
+			if(memcmp(leaf21140[i], ctlr->sromea, 3) == 0){
+				p = &leaf21140[i][4];
+				break;
+			}
+		}
+		if(p == nil)
+			return -1;
+	}
+	else
+		p = &ctlr->srom[(ctlr->srom[28]<<8)|ctlr->srom[27]];
+
+	/*
+	 * Set up the info needed for later media detection.
+	 * For the 21140, set the general-purpose mask in CSR12.
+	 * The info block entries are stored in order of increasing
+	 * precedence, so detection will work backwards through the
+	 * stored indexes into ctlr->srom.
+	 * If an entry is found which matches the selected connection
+	 * type, save the index. Otherwise, start at the last entry.
+	 * If any MII entries are found (type 1 and 3 blocks), scan
+	 * for PHYs.
+	 */
+	ctlr->leaf = p;
+	ctlr->sct = *p++;
+	ctlr->sct |= *p++<<8;
+	if(ctlr->id != Tulip3 && ctlr->id != Tulip1){
+		csr32w(ctlr, 12, Gpc|*p++);
+		delay(200);
+	}
+	ctlr->k = *p++;
+	if(ctlr->k >= nelem(ctlr->infoblock))
+		ctlr->k = nelem(ctlr->infoblock)-1;
+	ctlr->sctk = ctlr->k-1;
+	phy = 0;
+	for(k = 0; k < ctlr->k; k++){
+		ctlr->infoblock[k] = p;
+		if(ctlr->id == Tulip1){
+			debug("type21041: 0x%2.2uX\n", p[0]); 
+			if(ctlr->sct != 0x0800 && *p == (ctlr->sct & 0xFF))
+				ctlr->sctk = k;
+			if(*p & 0x40)
+				p += 7;
+			else
+				p += 1;
+		}
+		/*
+		 * The RAMIX PMC665 has a badly-coded SROM,
+		 * hence the test for 21143 and type 3.
+		 */
+		else if((*p & 0x80) || (ctlr->id == Tulip3 && *(p+1) == 3)){
+			*p |= 0x80;
+			if(*(p+1) == 1 || *(p+1) == 3)
+				phy = 1;
+			if(*(p+1) == 5)
+				ctlr->type5block = p;
+			p += (*p & ~0x80)+1;
+		}
+		else{
+			debug("type0: 0x%2.2uX 0x%2.2uX 0x%2.2uX 0x%2.2uX\n",
+				p[0], p[1], p[2], p[3]); 
+			if(ctlr->sct != 0x0800 && *p == (ctlr->sct & 0xFF))
+				ctlr->sctk = k;
+			p += 4;
+		}
+	}
+	ctlr->curk = ctlr->sctk;
+	debug("sct 0x%uX medium 0x%uX k %d curk %d phy %d\n",
+		ctlr->sct, ctlr->medium, ctlr->k, ctlr->curk, phy);
+
+	if(phy){
+		x = 0;
+		for(k = 0; k < nelem(ctlr->phy); k++){
+			if((ctlr->id == CentaurP || ctlr->id == CentaurPcb) && k != 1)
+				continue;
+			if((oui = miir(ctlr, k, 2)) == -1 || oui == 0)
+				continue;
+			debug("phy reg 2 %4.4uX\n", oui);
+			if(DEBUG){
+				oui = (oui & 0x3FF)<<6;
+				oui |= miir(ctlr, k, 3)>>10;
+				miir(ctlr, k, 1);
+				debug("phy%d: index %d oui %uX reg1 %uX\n",
+					x, k, oui, miir(ctlr, k, 1));
+				USED(oui);
+			}
+			ctlr->phy[x] = k;
+		}
+	}
+
+	ctlr->fd = 0;
+	ctlr->medium = -1;
+
+	return 0;
+}
+
+static void
+dec2114xpci(void)
+{
+	Ctlr *ctlr;
+	Pcidev *p;
+	int x;
+
+	p = nil;
+	while(p = pcimatch(p, 0, 0)){
+		if(p->ccrb != 0x02 || p->ccru != 0)
+			continue;
+		switch((p->did<<16)|p->vid){
+		default:
+			continue;
+
+		case Tulip3:			/* 21143 */
+			/*
+			 * Exit sleep mode.
+			 */
+			x = pcicfgr32(p, 0x40);
+			x &= ~0xC0000000;
+			pcicfgw32(p, 0x40, x);
+			/*FALLTHROUGH*/
+
+		case Tulip0:			/* 21140 */
+		case Tulip1:			/* 21041 */
+		case Pnic:			/* PNIC */
+		case Pnic2:			/* PNIC-II */
+		case CentaurP:			/* ADMtek */
+		case CentaurPcb:		/* ADMtek CardBus */
+			break;
+		}
+
+		/*
+		 * bar[0] is the I/O port register address and
+		 * bar[1] is the memory-mapped register address.
+		 */
+		ctlr = malloc(sizeof(Ctlr));
+		ctlr->port = p->mem[0].bar & ~0x01;
+		ctlr->pcidev = p;
+		ctlr->id = (p->did<<16)|p->vid;
+
+		if(ioalloc(ctlr->port, p->mem[0].size, 0, "dec2114x") < 0){
+			print("dec2114x: port 0x%uX in use\n", ctlr->port);
+			free(ctlr);
+			continue;
+		}
+
+		/*
+		 * Some cards (e.g. ANA-6910FX) seem to need the Ps bit
+		 * set or they don't always work right after a hardware
+		 * reset.
+		 */
+		csr32w(ctlr, 6, Mbo|Ps);
+		softreset(ctlr);
+
+		if(srom(ctlr)){
+			iofree(ctlr->port);
+			free(ctlr);
+			continue;
+		}
+
+		switch(ctlr->id){
+		default:
+			break;
+		case Pnic:			/* PNIC */
+			/*
+			 * Turn off the jabber timer.
+			 */
+			csr32w(ctlr, 15, 0x00000001);
+			break;
+		case CentaurP:
+		case CentaurPcb:
+			/*
+			 * Nice - the register offsets change from *8 to *4
+			 * for CSR16 and up...
+			 * CSR25/26 give the MAC address read from the SROM.
+			 * Don't really need to use this other than as a check,
+			 * the SROM will be read in anyway so the value there
+			 * can be used directly.
+			 */
+			debug("csr25 %8.8luX csr26 %8.8luX\n",
+				inl(ctlr->port+0xA4), inl(ctlr->port+0xA8));
+			debug("phyidr1 %4.4luX phyidr2 %4.4luX\n",
+				inl(ctlr->port+0xBC), inl(ctlr->port+0xC0));
+			break;
+		}
+
+		if(ctlrhead != nil)
+			ctlrtail->next = ctlr;
+		else
+			ctlrhead = ctlr;
+		ctlrtail = ctlr;
+	}
+}
+
+static int
+reset(Ether* ether)
+{
+	Ctlr *ctlr;
+	int i, x;
+	uchar ea[Eaddrlen];
+	static int scandone;
+
+	if(scandone == 0){
+		dec2114xpci();
+		scandone = 1;
+	}
+
+	/*
+	 * Any adapter matches if no ether->port is supplied,
+	 * otherwise the ports must match.
+	 */
+	for(ctlr = ctlrhead; ctlr != nil; ctlr = ctlr->next){
+		if(ctlr->active)
+			continue;
+		if(ether->port == 0 || ether->port == ctlr->port){
+			ctlr->active = 1;
+			break;
+		}
+	}
+	if(ctlr == nil)
+		return -1;
+
+	ether->ctlr = ctlr;
+	ether->port = ctlr->port;
+	ether->irq = ctlr->pcidev->intl;
+	ether->tbdf = ctlr->pcidev->tbdf;
+
+	/*
+	 * Check if the adapter's station address is to be overridden.
+	 * If not, read it from the EEPROM and set in ether->ea prior to
+	 * loading the station address in the hardware.
+	 */
+	memset(ea, 0, Eaddrlen);
+	if(memcmp(ea, ether->ea, Eaddrlen) == 0)
+		memmove(ether->ea, ctlr->sromea, Eaddrlen);
+
+	/*
+	 * Look for a medium override in case there's no autonegotiation
+	 * (no MII) or the autonegotiation fails.
+	 */
+	for(i = 0; i < ether->nopt; i++){
+		if(cistrcmp(ether->opt[i], "FD") == 0){
+			ctlr->fd = 1;
+			continue;
+		}
+		for(x = 0; x < nelem(mediatable); x++){
+			debug("compare <%s> <%s>\n", mediatable[x],
+				ether->opt[i]);
+			if(cistrcmp(mediatable[x], ether->opt[i]))
+				continue;
+			ctlr->medium = x;
+	
+			switch(ctlr->medium){
+			default:
+				ctlr->fd = 0;
+				break;
+	
+			case 0x04:		/* 10BASE-TFD */
+			case 0x05:		/* 100BASE-TXFD */
+			case 0x08:		/* 100BASE-FXFD */
+				ctlr->fd = 1;
+				break;
+			}
+			break;
+		}
+	}
+
+	ether->mbps = media(ether, 1);
+
+	/*
+	 * Initialise descriptor rings, ethernet address.
+	 */
+	ctlr->nrdr = Nrde;
+	ctlr->ntdr = Ntde;
+	pcisetbme(ctlr->pcidev);
+	ctlrinit(ether);
+
+	/*
+	 * Linkage to the generic ethernet driver.
+	 */
+	ether->attach = attach;
+	ether->transmit = transmit;
+	ether->interrupt = interrupt;
+	ether->ifstat = ifstat;
+
+	ether->arg = ether;
+	ether->shutdown = shutdown;
+	ether->multicast = multicast;
+	ether->promiscuous = promiscuous;
+
+	return 0;
+}
+
+void
+ether2114xlink(void)
+{
+	addethercard("2114x",  reset);
+	addethercard("21140",  reset);
+}
--- /dev/null
+++ b/os/pc/ether589.c
@@ -1,0 +1,214 @@
+/*
+ * 3C589 and 3C562.
+ * To do:
+ *	check xcvr10Base2 still works (is GlobalReset necessary?).
+ */
+#include "u.h"
+#include "../port/lib.h"
+#include "mem.h"
+#include "dat.h"
+#include "fns.h"
+#include "io.h"
+#include "../port/error.h"
+#include "../port/netif.h"
+
+#include "etherif.h"
+
+enum {						/* all windows */
+	CommandR		= 0x000E,
+	IntStatusR		= 0x000E,
+};
+
+enum {						/* Commands */
+	GlobalReset		= 0x0000,
+	SelectRegisterWindow	= 0x0001,
+	RxReset			= 0x0005,
+	TxReset			= 0x000B,
+	AcknowledgeInterrupt	= 0x000D,
+};
+
+enum {						/* IntStatus bits */
+	commandInProgress	= 0x1000,
+};
+
+#define COMMAND(port, cmd, a)	outs((port)+CommandR, ((cmd)<<11)|(a))
+#define STATUS(port)		ins((port)+IntStatusR)
+
+enum {						/* Window 0 - setup */
+	Wsetup			= 0x0000,
+						/* registers */
+	ManufacturerID		= 0x0000,	/* 3C5[08]*, 3C59[27] */
+	ProductID		= 0x0002,	/* 3C5[08]*, 3C59[27] */
+	ConfigControl		= 0x0004,	/* 3C5[08]*, 3C59[27] */
+	AddressConfig		= 0x0006,	/* 3C5[08]*, 3C59[27] */
+	ResourceConfig		= 0x0008,	/* 3C5[08]*, 3C59[27] */
+	EepromCommand		= 0x000A,
+	EepromData		= 0x000C,
+						/* AddressConfig Bits */
+	autoSelect9		= 0x0080,
+	xcvrMask9		= 0xC000,
+						/* ConfigControl bits */
+	Ena			= 0x0001,
+	base10TAvailable9	= 0x0200,
+	coaxAvailable9		= 0x1000,
+	auiAvailable9		= 0x2000,
+						/* EepromCommand bits */
+	EepromReadRegister	= 0x0080,
+	EepromBusy		= 0x8000,
+};
+
+enum {						/* Window 1 - operating set */
+	Wop			= 0x0001,
+};
+
+enum {						/* Window 3 - FIFO management */
+	Wfifo			= 0x0003,
+						/* registers */
+	InternalConfig		= 0x0000,	/* 3C509B, 3C589, 3C59[0257] */
+						/* InternalConfig bits */
+	xcvr10BaseT		= 0x00000000,
+	xcvr10Base2		= 0x00300000,
+};
+
+enum {						/* Window 4 - diagnostic */
+	Wdiagnostic		= 0x0004,
+						/* registers */
+	MediaStatus		= 0x000A,
+						/* MediaStatus bits */
+	linkBeatDetect		= 0x0800,
+};
+
+extern int etherelnk3reset(Ether*);
+
+static char *tcmpcmcia[] = {
+	"3C589",			/* 3COM 589[ABCD] */
+	"3C562",			/* 3COM 562 */
+	"589E",				/* 3COM Megahertz 589E */
+	nil,
+};
+
+static int
+configASIC(Ether* ether, int port, int xcvr)
+{
+	int x;
+
+	/* set Window 0 configuration registers */
+	COMMAND(port, SelectRegisterWindow, Wsetup);
+	outs(port+ConfigControl, Ena);
+
+	/* IRQ must be 3 on 3C589/3C562 */
+	outs(port + ResourceConfig, 0x3F00);
+
+	x = ins(port+AddressConfig) & ~xcvrMask9;
+	x |= (xcvr>>20)<<14;
+	outs(port+AddressConfig, x);
+
+	COMMAND(port, TxReset, 0);
+	while(STATUS(port) & commandInProgress)
+		;
+	COMMAND(port, RxReset, 0);
+	while(STATUS(port) & commandInProgress)
+		;
+
+	return etherelnk3reset(ether);
+}
+
+static int
+reset(Ether* ether)
+{
+	int i, t, slot;
+	char *type;
+	int port;
+	enum { WantAny, Want10BT, Want10B2 };
+	int want;
+	uchar ea[6];
+	char *p;
+
+	if(ether->irq == 0)
+		ether->irq = 10;
+	if(ether->port == 0)
+		ether->port = 0x240;
+	port = ether->port;
+
+	if(ioalloc(port, 0x10, 0, "3C589") < 0)
+		return -1;
+
+	type = nil;
+	slot = -1;
+	for(i = 0; tcmpcmcia[i] != nil; i++){
+		type = tcmpcmcia[i];
+		if((slot = pcmspecial(type, ether)) >= 0)
+			break;
+	}
+	if(slot < 0){
+		iofree(port);
+		return -1;
+	}
+
+	/*
+	 * Read Ethernet address from card memory
+	 * on 3C562, but only if the user has not 
+	 * overridden it.
+	 */
+	memset(ea, 0, sizeof ea);
+	if(memcmp(ea, ether->ea, 6) == 0 && strcmp(type, "3C562") == 0) {
+		if(pcmcistuple(slot, 0x88, -1, ea, 6) == 6) {
+			for(i = 0; i < 6; i += 2){
+				t = ea[i];
+				ea[i] = ea[i+1];
+				ea[i+1] = t;
+			}
+			memmove(ether->ea, ea, 6);
+		}
+	}
+	/*
+	 * Allow user to specify desired media in plan9.ini
+	 */
+	want = WantAny;
+	for(i = 0; i < ether->nopt; i++){
+		if(cistrncmp(ether->opt[i], "media=", 6) != 0)
+			continue;
+		p = ether->opt[i]+6;
+		if(cistrcmp(p, "10base2") == 0)
+			want = Want10B2;
+		else if(cistrcmp(p, "10baseT") == 0)
+			want = Want10BT;
+	}
+	
+	/* try configuring as a 10BaseT */
+	if(want==WantAny || want==Want10BT){
+		if(configASIC(ether, port, xcvr10BaseT) < 0){
+			pcmspecialclose(slot);
+			iofree(port);
+			return -1;
+		}
+		delay(100);
+		COMMAND(port, SelectRegisterWindow, Wdiagnostic);
+		if((ins(port+MediaStatus)&linkBeatDetect) || want==Want10BT){
+			COMMAND(port, SelectRegisterWindow, Wop);
+			print("#l%d: xcvr10BaseT %s\n", ether->ctlrno, type);
+			return 0;
+		}
+	}
+
+	/* try configuring as a 10base2 */
+	if(want==WantAny || want==Want10B2){
+		COMMAND(port, GlobalReset, 0);
+		if(configASIC(ether, port, xcvr10Base2) < 0){
+			pcmspecialclose(slot);
+			iofree(port);
+			return -1;
+		}
+		print("#l%d: xcvr10Base2 %s\n", ether->ctlrno, type);
+		return 0;
+	}
+	return -1;		/* not reached */
+}
+
+void
+ether589link(void)
+{
+	addethercard("3C589", reset);
+	addethercard("3C562", reset);
+	addethercard("589E", reset);
+}
--- /dev/null
+++ b/os/pc/ether79c960.c
@@ -1,0 +1,523 @@
+/*
+ * AM79C960
+ * PCnet Single-Chip Ethernet Controller for ISA Bus
+ * To do:
+ *	only issue transmit interrupt if necessary?
+ *	dynamically increase rings as necessary?
+ *	use Blocks as receive buffers?
+ *	currently hardwires 10Base-T
+ */
+#include "u.h"
+#include "../port/lib.h"
+#include "mem.h"
+#include "dat.h"
+#include "fns.h"
+#include "io.h"
+#include "../port/error.h"
+#include "../port/netif.h"
+
+#include "etherif.h"
+
+#define	chatty 1
+#define	DPRINT	if(chatty)print
+
+enum {
+	Lognrdre	= 6,
+	Nrdre		= (1<<Lognrdre),	/* receive descriptor ring entries */
+	Logntdre	= 4,
+	Ntdre		= (1<<Logntdre),	/* transmit descriptor ring entries */
+
+	Rbsize		= ETHERMAXTU+4,		/* ring buffer size (+4 for CRC) */
+};
+
+enum {						/* I/O resource map */
+	Aprom		= 0x0000,		/* physical address */
+	Rdp		= 0x0010,		/* register data port */
+	Rap		= 0x0012,		/* register address port */
+	Sreset		= 0x0014,		/* software reset */
+	/*Bdp		= 0x001C,		/* bus configuration register data port */
+	Idp		= 0x0016,		/* ISA data port */
+};
+
+enum {						/* ISACSR2 */
+	Isa10		= 0x0001,		/* 10base-T */
+	Isamedia		= 0x0003,		/* media selection mask */
+	Isaawake		= 0x0004,		/* Auto-Wake */
+};
+
+enum {						/* CSR0 */
+	Init		= 0x0001,		/* begin initialisation */
+	Strt		= 0x0002,		/* enable chip */
+	Stop		= 0x0004,		/* disable chip */
+	Tdmd		= 0x0008,		/* transmit demand */
+	Txon		= 0x0010,		/* transmitter on */
+	Rxon		= 0x0020,		/* receiver on */
+	Iena		= 0x0040,		/* interrupt enable */
+	Intr		= 0x0080,		/* interrupt flag */
+	Idon		= 0x0100,		/* initialisation done */
+	Tint		= 0x0200,		/* transmit interrupt */
+	Rint		= 0x0400,		/* receive interrupt */
+	Merr		= 0x0800,		/* memory error */
+	Miss		= 0x1000,		/* missed frame */
+	Cerr		= 0x2000,		/* collision */
+	Babl		= 0x4000,		/* transmitter timeout */
+	Err		= 0x8000,		/* Babl|Cerr|Miss|Merr */
+};
+	
+enum {						/* CSR3 */
+	Emba		= 0x0008,		/* enable modified back-off algorithm */
+	Dxmt2pd		= 0x0010,		/* disable transmit two part deferral */
+	Lappen		= 0x0020,		/* look-ahead packet processing enable */
+	Idonm		= 0x0100,		/* initialisation done mask */
+	Tintm		= 0x0200,		/* transmit interrupt mask */
+	Rintm		= 0x0400,		/* receive interrupt mask */
+	Merrm		= 0x0800,		/* memory error mask */
+	Missm		= 0x1000,		/* missed frame mask */
+	Bablm		= 0x4000,		/* babl mask */
+};
+
+enum {						/* CSR4 */
+	ApadXmt		= 0x0800,		/* auto pad transmit */
+};
+
+enum {						/* CSR15 */
+	Prom		= 0x8000,		/* promiscuous mode */
+	TenBaseT		= 0x0080,		/* 10Base-T */
+};
+
+typedef struct {				/* Initialisation Block */
+	ushort	mode;
+	uchar	padr[6];
+	uchar	ladr[8];
+	ushort	rdra0;			/* bits 0-15 */
+	uchar	rdra16;			/* bits 16-23 */
+	uchar	rlen;				/* upper 3 bits */
+	ushort	tdra0;			/* bits 0-15 */
+	uchar	tdra16;			/* bits 16-23 */
+	uchar	tlen;				/* upper 3 bits */
+} Iblock;
+
+typedef struct {				/* receive descriptor ring entry */
+	ushort	rbadr;				/* buffer address 0-15 */
+	ushort	rmd1;				/* status|buffer address 16-23 */
+	ushort	rmd2;				/* bcnt */
+	ushort	rmd3;				/* mcnt */
+} Rdre;
+
+typedef struct {				/* transmit descriptor ring entry */
+	ushort	tbadr;				/* buffer address 0-15 */
+	ushort	tmd1;				/* status|buffer address 16-23 */
+	ushort	tmd2;				/* bcnt */
+	ushort	tmd3;				/* errors */
+} Tdre;
+
+enum {						/* [RT]dre status bits */
+	Enp		= 0x0100,		/* end of packet */
+	Stp		= 0x0200,		/* start of packet */
+	RxBuff		= 0x0400,		/* buffer error */
+	TxDef		= 0x0400,		/* deferred */
+	RxCrc		= 0x0800,		/* CRC error */
+	TxOne		= 0x0800,		/* one retry needed */
+	RxOflo		= 0x1000,		/* overflow error */
+	TxMore		= 0x1000,		/* more than one retry needed */
+	Fram		= 0x2000,		/* framing error */
+	RxErr		= 0x4000,		/* Fram|Oflo|Crc|RxBuff */
+	TxErr		= 0x4000,		/* Uflo|Lcol|Lcar|Rtry */
+	Own			= 0x8000,
+};
+
+typedef struct {
+	Lock;
+
+	int	init;			/* initialisation in progress */
+	Iblock	iblock;
+
+	Rdre*	rdr;				/* receive descriptor ring */
+	void*	rrb;				/* receive ring buffers */
+	int	rdrx;				/* index into rdr */
+
+	Tdre*	tdr;				/* transmit descriptor ring */
+	void*	trb;				/* transmit ring buffers */
+	int	tdrx;				/* index into tdr */
+} Ctlr;
+
+static void
+attach(Ether* ether)
+{
+	Ctlr *ctlr;
+	int port;
+
+	ctlr = ether->ctlr;
+	ilock(ctlr);
+	if(ctlr->init){
+		iunlock(ctlr);
+		return;
+	}
+	port = ether->port;
+	outs(port+Rdp, Iena|Strt);
+	iunlock(ctlr);
+}
+
+static void
+ringinit(Ctlr* ctlr)
+{
+	int i, x;
+
+	/*
+	 * Initialise the receive and transmit buffer rings. The ring
+	 * entries must be aligned on 16-byte boundaries.
+	 *
+	 * This routine is protected by ctlr->init.
+	 */
+	if(ctlr->rdr == 0)
+		ctlr->rdr = xspanalloc(Nrdre*sizeof(Rdre), 0x10, 0);
+	if(ctlr->rrb == 0)
+		ctlr->rrb = xalloc(Nrdre*Rbsize);
+
+	x = PADDR(ctlr->rrb);
+	if ((x >> 24)&0xFF)
+		panic("ether79c960: address>24bit");
+	for(i = 0; i < Nrdre; i++){
+		ctlr->rdr[i].rbadr = x&0xFFFF;
+		ctlr->rdr[i].rmd1 = Own|(x>>16)&0xFF;
+		x += Rbsize;
+		ctlr->rdr[i].rmd2 = 0xF000|-Rbsize&0x0FFF;
+		ctlr->rdr[i].rmd3 = 0;
+	}
+	ctlr->rdrx = 0;
+
+	if(ctlr->tdr == 0)
+		ctlr->tdr = xspanalloc(Ntdre*sizeof(Tdre), 0x10, 0);
+	if(ctlr->trb == 0)
+		ctlr->trb = xalloc(Ntdre*Rbsize);
+
+	x = PADDR(ctlr->trb);
+	if ((x >> 24)&0xFF)
+		panic("ether79c960: address>24bit");
+	for(i = 0; i < Ntdre; i++){
+		ctlr->tdr[i].tbadr = x&0xFFFF;
+		ctlr->tdr[i].tmd1 = (x>>16)&0xFF;
+		x += Rbsize;
+		ctlr->tdr[i].tmd2 = 0xF000|-Rbsize&0x0FFF;
+	}
+	ctlr->tdrx = 0;
+}
+
+static void
+promiscuous(void* arg, int on)
+{
+	Ether *ether;
+	int port, x;
+	Ctlr *ctlr;
+
+	ether = arg;
+	port = ether->port;
+	ctlr = ether->ctlr;
+
+	/*
+	 * Put the chip into promiscuous mode. First we must wait until
+	 * anyone transmitting is done, then we can stop the chip and put
+	 * it in promiscuous mode. Restarting is made harder by the chip
+	 * reloading the transmit and receive descriptor pointers with their
+	 * base addresses when Strt is set (unlike the older Lance chip),
+	 * so the rings must be re-initialised.
+	 */
+	ilock(ctlr);
+	if(ctlr->init){
+		iunlock(ctlr);
+		return;
+	}
+	ctlr->init = 1;
+	iunlock(ctlr);
+
+	outs(port+Rdp, Stop);
+
+	outs(port+Rap, 15);
+	x = ins(port+Rdp) & ~Prom;
+	if(on)
+		x |= Prom;	/* BUG: multicast ... */
+	outs(port+Rdp, x);
+	outs(port+Rap, 0);
+
+	ringinit(ctlr);
+
+	ilock(ctlr);
+	ctlr->init = 0;
+	outs(port+Rdp, Iena|Strt);
+	iunlock(ctlr);
+}
+
+static int
+owntdre(void* arg)
+{
+	return (((Tdre*)arg)->tmd1 & Own) == 0;
+}
+
+static void
+txstart(Ether *ether)
+{
+	int port;
+	Ctlr *ctlr;
+	Tdre *tdre;
+	Etherpkt *pkt;
+	Block *bp;
+	int n;
+
+	port = ether->port;
+	ctlr = ether->ctlr;
+
+	if(ctlr->init)
+		return;
+
+	/*
+	 * Take the next transmit buffer, if it is free.
+	 */
+	tdre = &ctlr->tdr[ctlr->tdrx];
+	if(owntdre(tdre) == 0)
+		return;
+	bp = qget(ether->oq);
+	if(bp == nil)
+		return;
+
+	/*
+	 * Copy the packet to the transmit buffer and fill in our
+	 * source ethernet address. There's no need to pad to ETHERMINTU
+	 * here as we set ApadXmit in CSR4.
+	 */
+	n = BLEN(bp);
+	pkt = KADDR(tdre->tbadr|(tdre->tmd1&0xFF)<<16);
+	memmove(pkt->d, bp->rp, n);
+	memmove(pkt->s, ether->ea, sizeof(pkt->s));
+	freeb(bp);
+
+	/*
+	 * Give ownership of the descriptor to the chip, increment the
+	 * software ring descriptor pointer and tell the chip to poll.
+	 */
+	tdre->tmd3 = 0;
+	tdre->tmd2 = 0xF000|(-n)&0x0FFF;
+	tdre->tmd1 |= Own|Stp|Enp;
+	ctlr->tdrx = NEXT(ctlr->tdrx, Ntdre);
+	outs(port+Rdp, Iena|Tdmd);
+
+	ether->outpackets++;
+}
+
+static void
+transmit(Ether *ether)
+{
+	Ctlr *ctlr;
+
+	ctlr = ether->ctlr;
+
+	ilock(ctlr);
+	txstart(ether);
+	iunlock(ctlr);
+}
+
+static void
+interrupt(Ureg*, void* arg)
+{
+	Ether *ether;
+	int port, csr0, status;
+	Ctlr *ctlr;
+	Rdre *rdre;
+	Etherpkt *pkt;
+	Block *bp;
+	int len;
+
+	ether = arg;
+	port = ether->port;
+	ctlr = ether->ctlr;
+
+	/*
+	 * Acknowledge all interrupts and whine about those that shouldn't
+	 * happen.
+	 */
+	csr0 = ins(port+Rdp);
+	outs(port+Rdp, Babl|Cerr|Miss|Merr|Rint|Tint|Iena);
+	if(csr0 & (Babl|Miss|Merr))
+		print("AMD70C960#%d: csr0 = 0x%uX\n", ether->ctlrno, csr0);
+
+	/*
+	 * Receiver interrupt: run round the descriptor ring logging
+	 * errors and passing valid receive data up to the higher levels
+	 * until we encounter a descriptor still owned by the chip.
+	 */
+	if(csr0 & Rint){
+		rdre = &ctlr->rdr[ctlr->rdrx];
+		while(((status = rdre->rmd1) & Own) == 0){
+			if(status & RxErr){
+				if(status & RxBuff)
+					ether->buffs++;
+				if(status & RxCrc)
+					ether->crcs++;
+				if(status & RxOflo)
+					ether->overflows++;
+			}
+			else {
+				len = (rdre->rmd3 & 0x0FFF)-4;
+				if((bp = iallocb(len)) != nil){
+					ether->inpackets++;
+					pkt = KADDR(rdre->rbadr|(rdre->rmd1&0xFF)<<16);
+					memmove(bp->wp, pkt, len);
+					bp->wp += len;
+					etheriq(ether, bp, 1);
+				}
+			}
+
+			/*
+			 * Finished with this descriptor, reinitialise it,
+			 * give it back to the chip, then on to the next...
+			 */
+			rdre->rmd3 = 0;
+			rdre->rmd2 = 0xF000|-Rbsize&0x0FFF;
+			rdre->rmd1 |= Own;	
+
+			ctlr->rdrx = NEXT(ctlr->rdrx, Nrdre);
+			rdre = &ctlr->rdr[ctlr->rdrx];
+		}
+	}
+
+	/*
+	 * Transmitter interrupt: start next block if waiting for free descriptor.
+	 */
+	if(csr0 & Tint){
+		lock(ctlr);
+		txstart(ether);
+		unlock(ctlr);
+	}
+}
+
+static int
+reset(Ether* ether)
+{
+	int port, x, i;
+	uchar ea[Eaddrlen];
+	Ctlr *ctlr;
+
+	if(ether->port == 0)
+		ether->port = 0x300;
+	if(ether->irq == 0)
+		ether->irq = 10;
+	if(ether->irq == 2)
+		ether->irq = 9;
+	if(ether->dma == 0)
+		ether->dma = 5;
+	port = ether->port;
+
+	if(port == 0 || ether->dma == 0)
+		return -1;
+
+	/*
+	 * Allocate a controller structure and start to fill in the
+	 * initialisation block (must be DWORD aligned).
+	 */
+	ether->ctlr = malloc(sizeof(Ctlr));
+	ctlr = ether->ctlr;
+
+	ilock(ctlr);
+	ctlr->init = 1;
+
+	/*
+	 * Set the auto pad transmit in CSR4.
+	 */
+	/*outs(port+Rdp, 0x00);/**/
+	ins(port+Sreset); /**/
+	delay(1);
+	outs(port+Rap, 0);
+	outs(port+Rdp, Stop);
+
+	outs(port+Rap, 4);
+	x = ins(port+Rdp) & 0xFFFF;
+	outs(port+Rdp, ApadXmt|x);
+
+	outs(port+Rap, 0);
+
+	/*
+	 * Check if we are going to override the adapter's station address.
+	 * If not, read it from the I/O-space and set in ether->ea prior to loading the
+	 * station address in the initialisation block.
+	 */
+	memset(ea, 0, Eaddrlen);
+	if(memcmp(ea, ether->ea, Eaddrlen) == 0){
+		for(i=0; i<6; i++)
+			ether->ea[i] = inb(port + Aprom + i);
+	}
+
+	ctlr->iblock.rlen = Lognrdre<<5;
+	ctlr->iblock.tlen = Logntdre<<5;
+	memmove(ctlr->iblock.padr, ether->ea, sizeof(ctlr->iblock.padr));
+
+	ringinit(ctlr);
+
+	x = PADDR(ctlr->rdr);
+	ctlr->iblock.rdra0 = x&0xFFFF;
+	ctlr->iblock.rdra16 = (x >> 16)&0xFF;
+	x = PADDR(ctlr->tdr);
+	ctlr->iblock.tdra0 = x&0xFFFF;
+	ctlr->iblock.tdra16 = (x >> 16)&0xFF;
+
+	/*
+	 * set the DMA controller to cascade mode for bus master
+	 */
+	switch(ether->dma){
+	case 5:
+		outb(0xd6, 0xc1); outb(0xd4, 1); break;
+	case 6:
+		outb(0xd6, 0xc2); outb(0xd4, 2); break;
+	case 7:
+		outb(0xd6, 0xc3); outb(0xd4, 3); break;
+	}
+
+	/*
+	  * Ensure 10Base-T (for now)
+	  */
+	ctlr->iblock.mode = TenBaseT;
+	outs(port+Rap, 2);
+	x = ins(port+Idp);
+	x &= ~Isamedia;
+	x |= Isa10;
+	x |= Isaawake;
+	outs(port+Idp, x);
+
+	/*
+	 * Point the chip at the initialisation block and tell it to go.
+	 * Mask the Idon interrupt and poll for completion. Strt and interrupt
+	 * enables will be set later when we're ready to attach to the network.
+	 */
+	x = PADDR(&ctlr->iblock);
+	if((x>>24)&0xFF)
+		panic("ether79c960: address>24bit");
+	outs(port+Rap, 1);
+	outs(port+Rdp, x & 0xFFFF);
+	outs(port+Rap, 2);
+	outs(port+Rdp, (x>>16) & 0xFF);
+	outs(port+Rap, 3);
+	outs(port+Rdp, Idonm);
+	outs(port+Rap, 0);
+	outs(port+Rdp, Init);
+
+	while((ins(port+Rdp) & Idon) == 0)
+		;
+	outs(port+Rdp, Idon|Stop);
+	ctlr->init = 0;
+	iunlock(ctlr);
+
+	ether->port = port;
+	ether->attach = attach;
+	ether->transmit = transmit;
+	ether->interrupt = interrupt;
+	ether->ifstat = 0;
+
+	ether->promiscuous = promiscuous;
+	ether->arg = ether;
+
+	return 0;
+}
+
+void
+ether79c960link(void)
+{
+	addethercard("AMD79C960",  reset);
+}
--- /dev/null
+++ b/os/pc/ether79c970.c
@@ -1,0 +1,646 @@
+/*
+ * AMD79C970
+ * PCnet-PCI Single-Chip Ethernet Controller for PCI Local Bus
+ * To do:
+ *	finish this rewrite
+ */
+#include "u.h"
+#include "../port/lib.h"
+#include "mem.h"
+#include "dat.h"
+#include "fns.h"
+#include "io.h"
+#include "../port/error.h"
+#include "../port/netif.h"
+
+#include "etherif.h"
+
+enum {
+	Lognrdre	= 6,
+	Nrdre		= (1<<Lognrdre),/* receive descriptor ring entries */
+	Logntdre	= 4,
+	Ntdre		= (1<<Logntdre),/* transmit descriptor ring entries */
+
+	Rbsize		= ETHERMAXTU+4,	/* ring buffer size (+4 for CRC) */
+};
+
+enum {					/* DWIO I/O resource map */
+	Aprom		= 0x0000,	/* physical address */
+	Rdp		= 0x0010,	/* register data port */
+	Rap		= 0x0014,	/* register address port */
+	Sreset		= 0x0018,	/* software reset */
+	Bdp		= 0x001C,	/* bus configuration register data port */
+};
+
+enum {					/* CSR0 */
+	Init		= 0x0001,	/* begin initialisation */
+	Strt		= 0x0002,	/* enable chip */
+	Stop		= 0x0004,	/* disable chip */
+	Tdmd		= 0x0008,	/* transmit demand */
+	Txon		= 0x0010,	/* transmitter on */
+	Rxon		= 0x0020,	/* receiver on */
+	Iena		= 0x0040,	/* interrupt enable */
+	Intr		= 0x0080,	/* interrupt flag */
+	Idon		= 0x0100,	/* initialisation done */
+	Tint		= 0x0200,	/* transmit interrupt */
+	Rint		= 0x0400,	/* receive interrupt */
+	Merr		= 0x0800,	/* memory error */
+	Miss		= 0x1000,	/* missed frame */
+	Cerr		= 0x2000,	/* collision */
+	Babl		= 0x4000,	/* transmitter timeout */
+	Err		= 0x8000,	/* Babl|Cerr|Miss|Merr */
+};
+	
+enum {					/* CSR3 */
+	Bswp		= 0x0004,	/* byte swap */
+	Emba		= 0x0008,	/* enable modified back-off algorithm */
+	Dxmt2pd		= 0x0010,	/* disable transmit two part deferral */
+	Lappen		= 0x0020,	/* look-ahead packet processing enable */
+};
+
+enum {					/* CSR4 */
+	ApadXmt		= 0x0800,	/* auto pad transmit */
+};
+
+enum {					/* CSR15 */
+	Prom		= 0x8000,	/* promiscuous mode */
+};
+
+typedef struct Iblock Iblock;
+struct Iblock {			/* Initialisation Block */
+	ushort	mode;
+	uchar	rlen;			/* upper 4 bits */
+	uchar	tlen;			/* upper 4 bits */
+	uchar	padr[6];
+	uchar	res[2];
+	uchar	ladr[8];
+	ulong	rdra;
+	ulong	tdra;
+};
+
+typedef struct Dre Dre;
+struct Dre {			/* descriptor ring entry */
+	ulong	addr;
+	ulong	md1;			/* status|bcnt */
+	ulong	md2;			/* rcc|rpc|mcnt */
+	Block*	bp;
+};
+
+enum {					/* md1 */
+	Enp		= 0x01000000,	/* end of packet */
+	Stp		= 0x02000000,	/* start of packet */
+	RxBuff		= 0x04000000,	/* buffer error */
+	Def		= 0x04000000,	/* deferred */
+	Crc		= 0x08000000,	/* CRC error */
+	One		= 0x08000000,	/* one retry needed */
+	Oflo		= 0x10000000,	/* overflow error */
+	More		= 0x10000000,	/* more than one retry needed */
+	Fram		= 0x20000000,	/* framing error */
+	RxErr		= 0x40000000,	/* Fram|Oflo|Crc|RxBuff */
+	TxErr		= 0x40000000,	/* Uflo|Lcol|Lcar|Rtry */
+	Own		= 0x80000000,
+};
+
+enum {					/* md2 */
+	Rtry		= 0x04000000,	/* failed after repeated retries */
+	Lcar		= 0x08000000,	/* loss of carrier */
+	Lcol		= 0x10000000,	/* late collision */
+	Uflo		= 0x40000000,	/* underflow error */
+	TxBuff		= 0x80000000,	/* buffer error */
+};
+
+typedef struct Ctlr Ctlr;
+struct Ctlr {
+	Lock;
+	int	port;
+	Pcidev*	pcidev;
+	Ctlr*	next;
+	int	active;
+
+	int	init;			/* initialisation in progress */
+	Iblock	iblock;
+
+	Dre*	rdr;			/* receive descriptor ring */
+	int	rdrx;
+
+	Dre*	tdr;			/* transmit descriptor ring */
+	int	tdrh;			/* host index into tdr */
+	int	tdri;			/* interface index into tdr */
+	int	ntq;			/* descriptors active */
+
+	ulong	rxbuff;			/* receive statistics */
+	ulong	crc;
+	ulong	oflo;
+	ulong	fram;
+
+	ulong	rtry;			/* transmit statistics */
+	ulong	lcar;
+	ulong	lcol;
+	ulong	uflo;
+	ulong	txbuff;
+
+	ulong	merr;			/* bobf is such a whiner */
+	ulong	miss;
+	ulong	babl;
+
+	int	(*ior)(Ctlr*, int);
+	void	(*iow)(Ctlr*, int, int);
+};
+
+static Ctlr* ctlrhead;
+static Ctlr* ctlrtail;
+
+/*
+ * The Rdp, Rap, Sreset, Bdp ports are 32-bit port offset in the enumeration above.
+ * To get to 16-bit offsets, scale down with 0x10 staying the same.
+ */
+static int
+io16r(Ctlr *c, int r)
+{
+	if(r >= Rdp)
+		r = (r-Rdp)/2+Rdp;
+	return ins(c->port+r);
+}
+
+static void
+io16w(Ctlr *c, int r, int v)
+{
+	if(r >= Rdp)
+		r = (r-Rdp)/2+Rdp;
+	outs(c->port+r, v);
+}
+
+static int
+io32r(Ctlr *c, int r)
+{
+	return inl(c->port+r);
+}
+
+static void
+io32w(Ctlr *c, int r, int v)
+{
+	outl(c->port+r, v);
+}
+
+static void
+attach(Ether*)
+{
+}
+
+static long
+ifstat(Ether* ether, void* a, long n, ulong offset)
+{
+	char *p;
+	int len;
+	Ctlr *ctlr;
+
+	ctlr = ether->ctlr;
+
+	ether->crcs = ctlr->crc;
+	ether->frames = ctlr->fram;
+	ether->buffs = ctlr->rxbuff+ctlr->txbuff;
+	ether->overflows = ctlr->oflo;
+
+	if(n == 0)
+		return 0;
+
+	p = malloc(READSTR);
+	len = snprint(p, READSTR, "Rxbuff: %ld\n", ctlr->rxbuff);
+	len += snprint(p+len, READSTR-len, "Crc: %ld\n", ctlr->crc);
+	len += snprint(p+len, READSTR-len, "Oflo: %ld\n", ctlr->oflo);
+	len += snprint(p+len, READSTR-len, "Fram: %ld\n", ctlr->fram);
+	len += snprint(p+len, READSTR-len, "Rtry: %ld\n", ctlr->rtry);
+	len += snprint(p+len, READSTR-len, "Lcar: %ld\n", ctlr->lcar);
+	len += snprint(p+len, READSTR-len, "Lcol: %ld\n", ctlr->lcol);
+	len += snprint(p+len, READSTR-len, "Uflo: %ld\n", ctlr->uflo);
+	len += snprint(p+len, READSTR-len, "Txbuff: %ld\n", ctlr->txbuff);
+	len += snprint(p+len, READSTR-len, "Merr: %ld\n", ctlr->merr);
+	len += snprint(p+len, READSTR-len, "Miss: %ld\n", ctlr->miss);
+	snprint(p+len, READSTR-len, "Babl: %ld\n", ctlr->babl);
+
+	n = readstr(offset, a, n, p);
+	free(p);
+
+	return n;
+}
+
+static void
+ringinit(Ctlr* ctlr)
+{
+	Dre *dre;
+
+	/*
+	 * Initialise the receive and transmit buffer rings.
+	 * The ring entries must be aligned on 16-byte boundaries.
+	 *
+	 * This routine is protected by ctlr->init.
+	 */
+	if(ctlr->rdr == 0){
+		ctlr->rdr = xspanalloc(Nrdre*sizeof(Dre), 0x10, 0);
+		for(dre = ctlr->rdr; dre < &ctlr->rdr[Nrdre]; dre++){
+			dre->bp = iallocb(Rbsize);
+			if(dre->bp == nil)
+				panic("can't allocate ethernet receive ring\n");
+			dre->addr = PADDR(dre->bp->rp);
+			dre->md2 = 0;
+			dre->md1 = Own|(-Rbsize & 0xFFFF);
+		}
+	}
+	ctlr->rdrx = 0;
+
+	if(ctlr->tdr == 0)
+		ctlr->tdr = xspanalloc(Ntdre*sizeof(Dre), 0x10, 0);
+	memset(ctlr->tdr, 0, Ntdre*sizeof(Dre));
+	ctlr->tdrh = ctlr->tdri = 0;
+}
+
+static void
+promiscuous(void* arg, int on)
+{
+	Ether *ether;
+	int x;
+	Ctlr *ctlr;
+
+	ether = arg;
+	ctlr = ether->ctlr;
+
+	/*
+	 * Put the chip into promiscuous mode. First must wait until
+	 * anyone transmitting is done, then stop the chip and put
+	 * it in promiscuous mode. Restarting is made harder by the chip
+	 * reloading the transmit and receive descriptor pointers with their
+	 * base addresses when Strt is set (unlike the older Lance chip),
+	 * so the rings must be re-initialised.
+	 */
+	ilock(ctlr);
+	if(ctlr->init){
+		iunlock(ctlr);
+		return;
+	}
+	ctlr->init = 1;
+	iunlock(ctlr);
+
+	while(ctlr->ntq)
+		;
+
+	ctlr->iow(ctlr, Rdp, Stop);
+
+	ctlr->iow(ctlr, Rap, 15);
+	x = ctlr->ior(ctlr, Rdp) & ~Prom;
+	if(on)
+		x |= Prom;
+	ctlr->iow(ctlr, Rdp, x);
+	ctlr->iow(ctlr, Rap, 0);
+
+	ringinit(ctlr);
+
+	ilock(ctlr);
+	ctlr->init = 0;
+	ctlr->iow(ctlr, Rdp, Iena|Strt);
+	iunlock(ctlr);
+}
+
+static void
+multicast(void* arg, uchar*, int)
+{
+	promiscuous(arg, 1);
+}
+
+static void
+txstart(Ether* ether)
+{
+	Ctlr *ctlr;
+	Block *bp;
+	Dre *dre;
+
+	ctlr = ether->ctlr;
+
+	if(ctlr->init)
+		return;
+
+	while(ctlr->ntq < (Ntdre-1)){
+		bp = qget(ether->oq);
+		if(bp == nil)
+			break;
+
+		/*
+		 * Give ownership of the descriptor to the chip,
+		 * increment the software ring descriptor pointer
+		 * and tell the chip to poll.
+		 * There's no need to pad to ETHERMINTU
+		 * here as ApadXmt is set in CSR4.
+		 */
+		dre = &ctlr->tdr[ctlr->tdrh];
+		dre->bp = bp;
+		dre->addr = PADDR(bp->rp);
+		dre->md2 = 0;
+		dre->md1 = Own|Stp|Enp|(-BLEN(bp) & 0xFFFF);
+		ctlr->ntq++;
+		ctlr->iow(ctlr, Rdp, Iena|Tdmd);
+		ctlr->tdrh = NEXT(ctlr->tdrh, Ntdre);
+	}
+}
+
+static void
+transmit(Ether* ether)
+{
+	Ctlr *ctlr;
+
+	ctlr = ether->ctlr;
+	ilock(ctlr);
+	txstart(ether);
+	iunlock(ctlr);
+}
+
+static void
+interrupt(Ureg*, void* arg)
+{
+	Ctlr *ctlr;
+	Ether *ether;
+	int csr0, len;
+	Dre *dre;
+	Block *bp;
+
+	ether = arg;
+	ctlr = ether->ctlr;
+
+	/*
+	 * Acknowledge all interrupts and whine about those that shouldn't
+	 * happen.
+	 */
+intrloop:
+	csr0 = ctlr->ior(ctlr, Rdp) & 0xFFFF;
+	ctlr->iow(ctlr, Rdp, Babl|Cerr|Miss|Merr|Rint|Tint|Iena);
+	if(csr0 & Merr)
+		ctlr->merr++;
+	if(csr0 & Miss)
+		ctlr->miss++;
+	if(csr0 & Babl)
+		ctlr->babl++;
+	//if(csr0 & (Babl|Miss|Merr))
+	//	print("#l%d: csr0 = 0x%uX\n", ether->ctlrno, csr0);
+	if(!(csr0 & (Rint|Tint)))
+		return;
+
+	/*
+	 * Receiver interrupt: run round the descriptor ring logging
+	 * errors and passing valid receive data up to the higher levels
+	 * until a descriptor is encountered still owned by the chip.
+	 */
+	if(csr0 & Rint){
+		dre = &ctlr->rdr[ctlr->rdrx];
+		while(!(dre->md1 & Own)){
+			if(dre->md1 & RxErr){
+				if(dre->md1 & RxBuff)
+					ctlr->rxbuff++;
+				if(dre->md1 & Crc)
+					ctlr->crc++;
+				if(dre->md1 & Oflo)
+					ctlr->oflo++;
+				if(dre->md1 & Fram)
+					ctlr->fram++;
+			}
+			else if(bp = iallocb(Rbsize)){
+				len = (dre->md2 & 0x0FFF)-4;
+				dre->bp->wp = dre->bp->rp+len;
+				etheriq(ether, dre->bp, 1);
+				dre->bp = bp;
+				dre->addr = PADDR(bp->rp);
+			}
+
+			/*
+			 * Finished with this descriptor, reinitialise it,
+			 * give it back to the chip, then on to the next...
+			 */
+			dre->md2 = 0;
+			dre->md1 = Own|(-Rbsize & 0xFFFF);
+
+			ctlr->rdrx = NEXT(ctlr->rdrx, Nrdre);
+			dre = &ctlr->rdr[ctlr->rdrx];
+		}
+	}
+
+	/*
+	 * Transmitter interrupt: wakeup anyone waiting for a free descriptor.
+	 */
+	if(csr0 & Tint){
+		lock(ctlr);
+		while(ctlr->ntq){
+			dre = &ctlr->tdr[ctlr->tdri];
+			if(dre->md1 & Own)
+				break;
+	
+			if(dre->md1 & TxErr){
+				if(dre->md2 & Rtry)
+					ctlr->rtry++;
+				if(dre->md2 & Lcar)
+					ctlr->lcar++;
+				if(dre->md2 & Lcol)
+					ctlr->lcol++;
+				if(dre->md2 & Uflo)
+					ctlr->uflo++;
+				if(dre->md2 & TxBuff)
+					ctlr->txbuff++;
+				ether->oerrs++;
+			}
+	
+			freeb(dre->bp);
+	
+			ctlr->ntq--;
+			ctlr->tdri = NEXT(ctlr->tdri, Ntdre);
+		}
+		txstart(ether);
+		unlock(ctlr);
+	}
+	goto intrloop;
+}
+
+static void
+amd79c970pci(void)
+{
+	int port;
+	Ctlr *ctlr;
+	Pcidev *p;
+
+	p = nil;
+	while(p = pcimatch(p, 0x1022, 0x2000)){
+		port = p->mem[0].bar & ~0x01;
+		if(ioalloc(port, p->mem[0].size, 0, "amd79c970") < 0){
+			print("amd79c970: port 0x%uX in use\n", port);
+			continue;
+		}
+		ctlr = malloc(sizeof(Ctlr));
+		ctlr->port = p->mem[0].bar & ~0x01;
+		ctlr->pcidev = p;
+
+		if(ctlrhead != nil)
+			ctlrtail->next = ctlr;
+		else
+			ctlrhead = ctlr;
+		ctlrtail = ctlr;
+	}
+}
+
+static int
+reset(Ether* ether)
+{
+	int x;
+	uchar ea[Eaddrlen];
+	Ctlr *ctlr;
+
+	if(ctlrhead == nil)
+		amd79c970pci();
+
+	/*
+	 * Any adapter matches if no port is supplied,
+	 * otherwise the ports must match.
+	 */
+	for(ctlr = ctlrhead; ctlr != nil; ctlr = ctlr->next){
+		if(ctlr->active)
+			continue;
+		if(ether->port == 0 || ether->port == ctlr->port){
+			ctlr->active = 1;
+			break;
+		}
+	}
+	if(ctlr == nil)
+		return -1;
+
+	/*
+	 * Allocate a controller structure and start to initialise it.
+	 */
+	ether->ctlr = ctlr;
+	ether->port = ctlr->port;
+	ether->irq = ctlr->pcidev->intl;
+	ether->tbdf = ctlr->pcidev->tbdf;
+	pcisetbme(ctlr->pcidev);
+	ilock(ctlr);
+	ctlr->init = 1;
+
+	io32r(ctlr, Sreset);
+	io16r(ctlr, Sreset);
+
+	if(io16w(ctlr, Rap, 0), io16r(ctlr, Rdp) == 4){
+		ctlr->ior = io16r;
+		ctlr->iow = io16w;
+	}else if(io32w(ctlr, Rap, 0), io32r(ctlr, Rdp) == 4){
+		ctlr->ior = io32r;
+		ctlr->iow = io32w;
+	}else{
+		print("#l%d: card doesn't talk right\n", ether->ctlrno);
+		iunlock(ctlr);
+		return -1;
+	}
+
+	ctlr->iow(ctlr, Rap, 88);
+	x = ctlr->ior(ctlr, Rdp);
+	ctlr->iow(ctlr, Rap, 89);
+	x |= ctlr->ior(ctlr, Rdp)<<16;
+
+	switch(x&0xFFFFFFF){
+	case 0x2420003:	/* PCnet/PCI 79C970 */
+	case 0x2621003:	/* PCnet/PCI II 79C970A */
+	case 0x2625003: /* PCnet/FAST III 79C973 */
+		break;
+	default:
+		print("#l%d: unknown PCnet card version %.7ux\n",
+			ether->ctlrno, x&0xFFFFFFF);
+		iunlock(ctlr);
+		return -1;
+	}
+
+	/*
+	 * Set the software style in BCR20 to be PCnet-PCI to ensure 32-bit access.
+	 * Set the auto pad transmit in CSR4.
+	 */
+	ctlr->iow(ctlr, Rap, 20);
+	ctlr->iow(ctlr, Bdp, 0x0002);
+
+	ctlr->iow(ctlr, Rap, 4);
+	x = ctlr->ior(ctlr, Rdp) & 0xFFFF;
+	ctlr->iow(ctlr, Rdp, ApadXmt|x);
+
+	ctlr->iow(ctlr, Rap, 0);
+
+	/*
+	 * Check if the adapter's station address is to be overridden.
+	 * If not, read it from the I/O-space and set in ether->ea prior to
+	 * loading the station address in the initialisation block.
+	 */
+	memset(ea, 0, Eaddrlen);
+	if(!memcmp(ea, ether->ea, Eaddrlen)){
+		x = ctlr->ior(ctlr, Aprom);
+		ether->ea[0] = x;
+		ether->ea[1] = x>>8;
+		if(ctlr->ior == io16r)
+			x = ctlr->ior(ctlr, Aprom+2);
+		else
+			x >>= 16;
+		ether->ea[2] = x;
+		ether->ea[3] = x>>8;
+		x = ctlr->ior(ctlr, Aprom+4);
+		ether->ea[4] = x;
+		ether->ea[5] = x>>8;
+	}
+
+	/*
+	 * Start to fill in the initialisation block
+	 * (must be DWORD aligned).
+	 */
+	ctlr->iblock.rlen = Lognrdre<<4;
+	ctlr->iblock.tlen = Logntdre<<4;
+	memmove(ctlr->iblock.padr, ether->ea, sizeof(ctlr->iblock.padr));
+
+	ringinit(ctlr);
+	ctlr->iblock.rdra = PADDR(ctlr->rdr);
+	ctlr->iblock.tdra = PADDR(ctlr->tdr);
+
+	/*
+	 * Point the chip at the initialisation block and tell it to go.
+	 * Mask the Idon interrupt and poll for completion. Strt and interrupt
+	 * enables will be set later when attaching to the network.
+	 */
+	x = PADDR(&ctlr->iblock);
+	ctlr->iow(ctlr, Rap, 1);
+	ctlr->iow(ctlr, Rdp, x & 0xFFFF);
+	ctlr->iow(ctlr, Rap, 2);
+	ctlr->iow(ctlr, Rdp, (x>>16) & 0xFFFF);
+	ctlr->iow(ctlr, Rap, 3);
+	ctlr->iow(ctlr, Rdp, Idon);
+	ctlr->iow(ctlr, Rap, 0);
+	ctlr->iow(ctlr, Rdp, Init);
+
+	while(!(ctlr->ior(ctlr, Rdp) & Idon))
+		;
+
+	/*
+	 * We used to set CSR0 to Idon|Stop here, and then
+	 * in attach change it to Iena|Strt.  Apparently the simulated
+	 * 79C970 in VMware never enables after a write of Idon|Stop,
+	 * so we enable the device here now.
+	 */
+	ctlr->iow(ctlr, Rdp, Iena|Strt);
+	ctlr->init = 0;
+	iunlock(ctlr);
+
+	/*
+	 * Linkage to the generic ethernet driver.
+	 */
+	ether->attach = attach;
+	ether->transmit = transmit;
+	ether->interrupt = interrupt;
+	ether->ifstat = ifstat;
+
+	ether->arg = ether;
+	ether->promiscuous = promiscuous;
+	ether->multicast = multicast;
+//	ether->shutdown = shutdown;
+
+	return 0;
+}
+
+void
+ether79c970link(void)
+{
+	addethercard("AMD79C970",  reset);
+}
--- /dev/null
+++ b/os/pc/ether8003.c
@@ -1,0 +1,271 @@
+#include "u.h"
+#include "../port/lib.h"
+#include "mem.h"
+#include "dat.h"
+#include "fns.h"
+#include "io.h"
+#include "../port/error.h"
+#include "../port/netif.h"
+
+#include "etherif.h"
+#include "ether8390.h"
+
+/*
+ * Western Digital/Standard Microsystems Corporation cards (WD80[01]3).
+ * Also handles 8216 cards (Elite Ultra).
+ * Configuration code based on that provided by SMC a long time ago.
+ */
+enum {					/* 83C584 Bus Interface Controller */
+	Msr		= 0x00,		/* Memory Select Register */
+	Icr		= 0x01,		/* Interface Configuration Register */
+	Iar		= 0x02,		/* I/O Address Register */
+	Bio		= 0x03,		/* BIOS ROM Address Register */
+	Ear		= 0x03,		/* EEROM Address Register (shared with Bio) */
+	Irr		= 0x04,		/* Interrupt Request Register */
+	Hcr		= 0x04,		/* 8216 hardware control */
+	Laar		= 0x05,		/* LA Address Register */
+	Ijr		= 0x06,		/* Initialisation Jumpers */
+	Gp2		= 0x07,		/* General Purpose Data Register */
+	Lar		= 0x08,		/* LAN Address Registers */
+	Id		= 0x0E,		/* Card ID byte */
+	Cksum		= 0x0F,		/* Checksum */
+};
+
+enum {					/* Msr */
+	Rst		= 0x80,		/* software reset */
+	Menb		= 0x40,		/* memory enable */
+};
+
+enum {					/* Icr */
+	Bit16		= 0x01,		/* 16-bit bus */
+	Other		= 0x02,		/* other register access */
+	Ir2		= 0x04,		/* IR2 */
+	Msz		= 0x08,		/* SRAM size */
+	Rla		= 0x10,		/* recall LAN address */
+	Rx7		= 0x20,		/* recall all but I/O and LAN address */
+	Rio		= 0x40,		/* recall I/O address from EEROM */
+	Sto		= 0x80,		/* non-volatile EEROM store */
+};
+
+enum {					/* Laar */
+	ZeroWS16	= 0x20,		/* zero wait states for 16-bit ops */
+	L16en		= 0x40,		/* enable 16-bit LAN operation */
+	M16en		= 0x80,		/* enable 16-bit memory access */
+};
+
+enum {					/* Ijr */
+	Ienable		= 0x01,		/* 8216 interrupt enable */
+};
+
+/*
+ * Mapping from configuration bits to interrupt level.
+ */
+static int irq8003[8] = {
+	9, 3, 5, 7, 10, 11, 15, 4,
+};
+
+static int irq8216[8] = {
+	0, 9, 3, 5, 7, 10, 11, 15,
+};
+
+static void
+reset8003(Ether* ether, uchar ea[Eaddrlen], uchar ic[8])
+{
+	Dp8390 *ctlr;
+	ulong port;
+
+	ctlr = ether->ctlr;
+	port = ether->port;
+
+	/*
+	 * Check for old, dumb 8003E, which doesn't have an interface
+	 * chip. Only Msr exists out of the 1st eight registers, reads
+	 * of the others just alias the 2nd eight registers, the LAN
+	 * address ROM. Can check Icr, Irr and Laar against the ethernet
+	 * address read above and if they match it's an 8003E (or an
+	 * 8003EBT, 8003S, 8003SH or 8003WT, doesn't matter), in which
+	 * case the default irq gets used.
+	 */
+	if(memcmp(&ea[1], &ic[1], 5) == 0){
+		memset(ic, 0, sizeof(ic));
+		ic[Msr] = (((ulong)ether->mem)>>13) & 0x3F;
+	}
+	else{
+		/*
+		 * As a final sanity check for the 8013EBT, which doesn't have
+		 * the 83C584 interface chip, but has 2 real registers, write Gp2
+		 * and if it reads back the same, it's not an 8013EBT.
+		 */
+		outb(port+Gp2, 0xAA);
+		inb(port+Msr);				/* wiggle bus */
+		if(inb(port+Gp2) != 0xAA){
+			memset(ic, 0, sizeof(ic));
+			ic[Msr] = (((ulong)ether->mem)>>13) & 0x3F;
+		}
+		else
+			ether->irq = irq8003[((ic[Irr]>>5) & 0x3)|(ic[Icr] & 0x4)];
+
+		/*
+		 * Check if 16-bit card.
+		 * If Bit16 is read/write, then it's an 8-bit card.
+		 * If Bit16 is set, it's in a 16-bit slot.
+		 */
+		outb(port+Icr, ic[Icr]^Bit16);
+		inb(port+Msr);				/* wiggle bus */
+		if((inb(port+Icr) & Bit16) == (ic[Icr] & Bit16)){
+			ctlr->width = 2;
+			ic[Icr] &= ~Bit16;
+		}
+		outb(port+Icr, ic[Icr]);
+
+		if(ctlr->width == 2 && (inb(port+Icr) & Bit16) == 0)
+			ctlr->width = 1;
+	}
+
+	ether->mem = (ulong)KADDR((ic[Msr] & 0x3F)<<13);
+	if(ctlr->width == 2)
+		ether->mem |= (ic[Laar] & 0x1F)<<19;
+	else
+		ether->mem |= 0x80000;
+
+	if(ic[Icr] & (1<<3))
+		ether->size = 32*1024;
+	if(ctlr->width == 2)
+		ether->size <<= 1;
+
+	/*
+	 * Enable interface RAM, set interface width.
+	 */
+	outb(port+Msr, ic[Msr]|Menb);
+	if(ctlr->width == 2)
+		outb(port+Laar, ic[Laar]|L16en|M16en|ZeroWS16);
+}
+
+static void
+reset8216(Ether* ether, uchar[8])
+{
+	uchar hcr, irq, x;
+	ulong addr, port;
+	Dp8390 *ctlr;
+
+	ctlr = ether->ctlr;
+	port = ether->port;
+
+	ctlr->width = 2;
+
+	/*
+	 * Switch to the alternate register set and retrieve the memory
+	 * and irq information.
+	 */
+	hcr = inb(port+Hcr);
+	outb(port+Hcr, 0x80|hcr);
+	addr = inb(port+0x0B) & 0xFF;
+	irq = inb(port+0x0D);
+	outb(port+Hcr, hcr);
+
+	ether->mem = (ulong)KADDR(0xC0000+((((addr>>2) & 0x30)|(addr & 0x0F))<<13));
+	ether->size = 8192*(1<<((addr>>4) & 0x03));
+	ether->irq = irq8216[((irq>>4) & 0x04)|((irq>>2) & 0x03)];
+
+	/*
+	 * Enable interface RAM, set interface width, and enable interrupts.
+	 */
+	x = inb(port+Msr) & ~Rst;
+	outb(port+Msr, Menb|x);
+	x = inb(port+Laar);
+	outb(port+Laar, M16en|x);
+	outb(port+Ijr, Ienable);
+}
+
+/*
+ * Get configuration parameters, enable memory.
+ * There are opportunities here for buckets of code, try to resist.
+ */
+static int
+reset(Ether* ether)
+{
+	int i;
+	uchar ea[Eaddrlen], ic[8], id, nullea[Eaddrlen], sum;
+	ulong port;
+	Dp8390 *ctlr;
+
+	/*
+	 * Set up the software configuration.
+	 * Use defaults for port, irq, mem and size if not specified.
+	 * Defaults are set for the dumb 8003E which can't be
+	 * autoconfigured.
+	 */
+	if(ether->port == 0)
+		ether->port = 0x280;
+	if(ether->irq == 0)
+		ether->irq = 3;
+	if(ether->mem == 0)
+		ether->mem = 0xD0000;
+	if(ether->size == 0)
+		ether->size = 8*1024;
+	if(ioalloc(ether->port, 0x20, 0, "wd8003") < 0)
+		return -1;
+
+	/*
+	 * Look for the interface. Read the LAN address ROM
+	 * and validate the checksum - the sum of all 8 bytes
+	 * should be 0xFF.
+	 * At the same time, get the (possible) interface chip
+	 * registers, they'll be used later to check for aliasing.
+	 */
+	port = ether->port;
+	sum = 0;
+	for(i = 0; i < sizeof(ea); i++){
+		ea[i] = inb(port+Lar+i);
+		sum += ea[i];
+		ic[i] = inb(port+i);
+	}
+	id = inb(port+Id);
+	sum += id;
+	sum += inb(port+Cksum);
+	if(sum != 0xFF){
+		iofree(ether->port);
+		return -1;
+	}
+
+	ether->ctlr = malloc(sizeof(Dp8390));
+	ctlr = ether->ctlr;
+	ctlr->ram = 1;
+
+	if((id & 0xFE) == 0x2A)
+		reset8216(ether, ic);
+	else
+		reset8003(ether, ea, ic);
+
+	/*
+	 * Set the DP8390 ring addresses.
+	 */
+	ctlr->port = port+0x10;
+	ctlr->tstart = 0;
+	ctlr->pstart = HOWMANY(sizeof(Etherpkt), Dp8390BufSz);
+	ctlr->pstop = HOWMANY(ether->size, Dp8390BufSz);
+
+	/*
+	 * Finally, init the 8390, set the ethernet address
+	 * and claim the memory used.
+	 */
+	dp8390reset(ether);
+	memset(nullea, 0, Eaddrlen);
+	if(memcmp(nullea, ether->ea, Eaddrlen) == 0){
+		for(i = 0; i < sizeof(ether->ea); i++)
+			ether->ea[i] = ea[i];
+	}
+	dp8390setea(ether);
+
+	if(umbrwmalloc(PADDR(ether->mem), ether->size, 0) == 0)
+		print("ether8003: warning - 0x%luX unavailable\n",
+			PADDR(ether->mem));
+
+	return 0;
+}
+
+void
+ether8003link(void)
+{
+	addethercard("WD8003", reset);
+}
--- /dev/null
+++ b/os/pc/ether8139.c
@@ -1,0 +1,765 @@
+/*
+ * Realtek 8139 (but not the 8129).
+ * Error recovery for the various over/under -flow conditions
+ * may need work.
+ */
+#include "u.h"
+#include "../port/lib.h"
+#include "mem.h"
+#include "dat.h"
+#include "fns.h"
+#include "io.h"
+#include "../port/error.h"
+#include "../port/netif.h"
+
+#include "etherif.h"
+
+enum {					/* registers */
+	Idr0		= 0x0000,	/* MAC address */
+	Mar0		= 0x0008,	/* Multicast address */
+	Tsd0		= 0x0010,	/* Transmit Status Descriptor0 */
+	Tsad0		= 0x0020,	/* Transmit Start Address Descriptor0 */
+	Rbstart		= 0x0030,	/* Receive Buffer Start Address */
+	Erbcr		= 0x0034,	/* Early Receive Byte Count */
+	Ersr		= 0x0036,	/* Early Receive Status */
+	Cr		= 0x0037,	/* Command Register */
+	Capr		= 0x0038,	/* Current Address of Packet Read */
+	Cbr		= 0x003A,	/* Current Buffer Address */
+	Imr		= 0x003C,	/* Interrupt Mask */
+	Isr		= 0x003E,	/* Interrupt Status */
+	Tcr		= 0x0040,	/* Transmit Configuration */
+	Rcr		= 0x0044,	/* Receive Configuration */
+	Tctr		= 0x0048,	/* Timer Count */
+	Mpc		= 0x004C,	/* Missed Packet Counter */
+	Cr9346		= 0x0050,	/* 9346 Command Register */
+	Config0		= 0x0051,	/* Configuration Register 0 */
+	Config1		= 0x0052,	/* Configuration Register 1 */
+	TimerInt	= 0x0054,	/* Timer Interrupt */
+	Msr		= 0x0058,	/* Media Status */
+	Config3		= 0x0059,	/* Configuration Register 3 */
+	Config4		= 0x005A,	/* Configuration Register 4 */
+	Mulint		= 0x005C,	/* Multiple Interrupt Select */
+	RerID		= 0x005E,	/* PCI Revision ID */
+	Tsad		= 0x0060,	/* Transmit Status of all Descriptors */
+
+	Bmcr		= 0x0062,	/* Basic Mode Control */
+	Bmsr		= 0x0064,	/* Basic Mode Status */
+	Anar		= 0x0066,	/* Auto-Negotiation Advertisment */
+	Anlpar		= 0x0068,	/* Auto-Negotiation Link Partner */
+	Aner		= 0x006A,	/* Auto-Negotiation Expansion */
+	Dis		= 0x006C,	/* Disconnect Counter */
+	Fcsc		= 0x006E,	/* False Carrier Sense Counter */
+	Nwaytr		= 0x0070,	/* N-way Test */
+	Rec		= 0x0072,	/* RX_ER Counter */
+	Cscr		= 0x0074,	/* CS Configuration */
+	Phy1parm	= 0x0078,	/* PHY Parameter 1 */
+	Twparm		= 0x007C,	/* Twister Parameter */
+	Phy2parm	= 0x0080,	/* PHY Parameter 2 */
+};
+
+enum {					/* Cr */
+	Bufe		= 0x01,		/* Rx Buffer Empty */
+	Te		= 0x04,		/* Transmitter Enable */
+	Re		= 0x08,		/* Receiver Enable */
+	Rst		= 0x10,		/* Software Reset */
+};
+
+enum {					/* Imr/Isr */
+	Rok		= 0x0001,	/* Receive OK */
+	Rer		= 0x0002,	/* Receive Error */
+	Tok		= 0x0004,	/* Transmit OK */
+	Ter		= 0x0008,	/* Transmit Error */
+	Rxovw		= 0x0010,	/* Receive Buffer Overflow */
+	PunLc		= 0x0020,	/* Packet Underrun or Link Change */
+	Fovw		= 0x0040,	/* Receive FIFO Overflow */
+	Clc		= 0x2000,	/* Cable Length Change */
+	Timerbit	= 0x4000,	/* Timer */
+	Serr		= 0x8000,	/* System Error */
+};
+
+enum {					/* Tcr */
+	Clrabt		= 0x00000001,	/* Clear Abort */
+	TxrrSHIFT	= 4,		/* Transmit Retry Count */
+	TxrrMASK	= 0x000000F0,
+	MtxdmaSHIFT	= 8,		/* Max. DMA Burst Size */
+	MtxdmaMASK	= 0x00000700,
+	Mtxdma2048	= 0x00000700,
+	Acrc		= 0x00010000,	/* Append CRC (not) */
+	LbkSHIFT	= 17,		/* Loopback Test */
+	LbkMASK		= 0x00060000,
+	Rtl8139ArevG	= 0x00800000,	/* RTL8139A Rev. G ID */
+	IfgSHIFT	= 24,		/* Interframe Gap */
+	IfgMASK		= 0x03000000,
+	HwveridSHIFT	= 26,		/* Hardware Version ID */
+	HwveridMASK	= 0x7C000000,
+};
+
+enum {					/* Rcr */
+	Aap		= 0x00000001,	/* Accept All Packets */
+	Apm		= 0x00000002,	/* Accept Physical Match */
+	Am		= 0x00000004,	/* Accept Multicast */
+	Ab		= 0x00000008,	/* Accept Broadcast */
+	Ar		= 0x00000010,	/* Accept Runt */
+	Aer		= 0x00000020,	/* Accept Error */
+	Sel9356		= 0x00000040,	/* 9356 EEPROM used */
+	Wrap		= 0x00000080,	/* Rx Buffer Wrap Control */
+	MrxdmaSHIFT	= 8,		/* Max. DMA Burst Size */
+	MrxdmaMASK	= 0x00000700,
+	Mrxdmaunlimited	= 0x00000700,
+	RblenSHIFT	= 11,		/* Receive Buffer Length */
+	RblenMASK	= 0x00001800,
+	Rblen8K		= 0x00000000,	/* 8KB+16 */
+	Rblen16K	= 0x00000800,	/* 16KB+16 */
+	Rblen32K	= 0x00001000,	/* 32KB+16 */
+	Rblen64K	= 0x00001800,	/* 64KB+16 */
+	RxfthSHIFT	= 13,		/* Receive Buffer Length */
+	RxfthMASK	= 0x0000E000,
+	Rxfth256	= 0x00008000,
+	Rxfthnone	= 0x0000E000,
+	Rer8		= 0x00010000,	/* Accept Error Packets > 8 bytes */
+	MulERINT	= 0x00020000,	/* Multiple Early Interrupt Select */
+	ErxthSHIFT	= 24,		/* Early Rx Threshold */
+	ErxthMASK	= 0x0F000000,
+	Erxthnone	= 0x00000000,
+};
+
+enum {					/* Received Packet Status */
+	Rcok		= 0x0001,	/* Receive Completed OK */
+	Fae		= 0x0002,	/* Frame Alignment Error */
+	Crc		= 0x0004,	/* CRC Error */
+	Long		= 0x0008,	/* Long Packet */
+	Runt		= 0x0010,	/* Runt Packet Received */
+	Ise		= 0x0020,	/* Invalid Symbol Error */
+	Bar		= 0x2000,	/* Broadcast Address Received */
+	Pam		= 0x4000,	/* Physical Address Matched */
+	Mar		= 0x8000,	/* Multicast Address Received */
+};
+
+enum {					/* Media Status Register */
+	Rxpf		= 0x01,		/* Pause Flag */
+	Txpf		= 0x02,		/* Pause Flag */
+	Linkb		= 0x04,		/* Inverse of Link Status */
+	Speed10		= 0x08,		/* 10Mbps */
+	Auxstatus	= 0x10,		/* Aux. Power Present Status */
+	Rxfce		= 0x40,		/* Receive Flow Control Enable */
+	Txfce		= 0x80,		/* Transmit Flow Control Enable */
+};
+
+typedef struct Td Td;
+struct Td {			/* Soft Transmit Descriptor */
+	int	tsd;
+	int	tsad;
+	uchar*	data;
+	Block*	bp;
+};
+
+enum {					/* Tsd0 */
+	SizeSHIFT	= 0,		/* Descriptor Size */
+	SizeMASK	= 0x00001FFF,
+	Own		= 0x00002000,
+	Tun		= 0x00004000,	/* Transmit FIFO Underrun */
+	Tcok		= 0x00008000,	/* Transmit COmpleted OK */
+	EtxthSHIFT	= 16,		/* Early Tx Threshold */
+	EtxthMASK	= 0x001F0000,
+	NccSHIFT	= 24,		/* Number of Collisions Count */
+	NccMASK		= 0x0F000000,
+	Cdh		= 0x10000000,	/* CD Heartbeat */
+	Owc		= 0x20000000,	/* Out of Window Collision */
+	Tabt		= 0x40000000,	/* Transmit Abort */
+	Crs		= 0x80000000,	/* Carrier Sense Lost */
+};
+
+enum {
+	Rblen		= Rblen64K,	/* Receive Buffer Length */
+	Ntd		= 4,		/* Number of Transmit Descriptors */
+	Tdbsz		= ROUNDUP(sizeof(Etherpkt), 4),
+};
+
+typedef struct Ctlr Ctlr;
+typedef struct Ctlr {
+	int	port;
+	Pcidev*	pcidev;
+	Ctlr*	next;
+	int	active;
+	int	id;
+
+	QLock	alock;			/* attach */
+	Lock	ilock;			/* init */
+	void*	alloc;			/* base of per-Ctlr allocated data */
+
+	int	rcr;			/* receive configuration register */
+	uchar*	rbstart;		/* receive buffer */
+	int	rblen;			/* receive buffer length */
+	int	ierrs;			/* receive errors */
+
+	Lock	tlock;			/* transmit */
+	Td	td[Ntd];
+	int	ntd;			/* descriptors active */
+	int	tdh;			/* host index into td */
+	int	tdi;			/* interface index into td */
+	int	etxth;			/* early transmit threshold */
+	int	taligned;		/* packet required no alignment */
+	int	tunaligned;		/* packet required alignment */
+
+	int	dis;			/* disconnect counter */
+	int	fcsc;			/* false carrier sense counter */
+	int	rec;			/* RX_ER counter */
+} Ctlr;
+
+static Ctlr* ctlrhead;
+static Ctlr* ctlrtail;
+
+#define csr8r(c, r)	(inb((c)->port+(r)))
+#define csr16r(c, r)	(ins((c)->port+(r)))
+#define csr32r(c, r)	(inl((c)->port+(r)))
+#define csr8w(c, r, b)	(outb((c)->port+(r), (int)(b)))
+#define csr16w(c, r, w)	(outs((c)->port+(r), (ushort)(w)))
+#define csr32w(c, r, l)	(outl((c)->port+(r), (ulong)(l)))
+
+static void
+rtl8139promiscuous(void* arg, int on)
+{
+	Ether *edev;
+	Ctlr * ctlr;
+
+	edev = arg;
+	ctlr = edev->ctlr;
+	ilock(&ctlr->ilock);
+
+	if(on)
+		ctlr->rcr |= Aap;
+	else
+		ctlr->rcr &= ~Aap;
+	csr32w(ctlr, Rcr, ctlr->rcr);
+	iunlock(&ctlr->ilock);
+}
+
+static void
+rtl8139multicast(void* arg, uchar*, int)
+{
+	rtl8139promiscuous(arg, 1);
+}
+
+static long
+rtl8139ifstat(Ether* edev, void* a, long n, ulong offset)
+{
+	int l;
+	char *p;
+	Ctlr *ctlr;
+
+	ctlr = edev->ctlr;
+	p = malloc(READSTR);
+	l = snprint(p, READSTR, "rcr %#8.8ux\n", ctlr->rcr);
+	l += snprint(p+l, READSTR-l, "ierrs %d\n", ctlr->ierrs);
+	l += snprint(p+l, READSTR-l, "etxth %d\n", ctlr->etxth);
+	l += snprint(p+l, READSTR-l, "taligned %d\n", ctlr->taligned);
+	l += snprint(p+l, READSTR-l, "tunaligned %d\n", ctlr->tunaligned);
+	ctlr->dis += csr16r(ctlr, Dis);
+	l += snprint(p+l, READSTR-l, "dis %d\n", ctlr->dis);
+	ctlr->fcsc += csr16r(ctlr, Fcsc);
+	l += snprint(p+l, READSTR-l, "fcscnt %d\n", ctlr->fcsc);
+	ctlr->rec += csr16r(ctlr, Rec);
+	l += snprint(p+l, READSTR-l, "rec %d\n", ctlr->rec);
+
+	l += snprint(p+l, READSTR-l, "Tcr %#8.8lux\n", csr32r(ctlr, Tcr));
+	l += snprint(p+l, READSTR-l, "Config0 %#2.2ux\n", csr8r(ctlr, Config0));
+	l += snprint(p+l, READSTR-l, "Config1 %#2.2ux\n", csr8r(ctlr, Config1));
+	l += snprint(p+l, READSTR-l, "Msr %#2.2ux\n", csr8r(ctlr, Msr));
+	l += snprint(p+l, READSTR-l, "Config3 %#2.2ux\n", csr8r(ctlr, Config3));
+	l += snprint(p+l, READSTR-l, "Config4 %#2.2ux\n", csr8r(ctlr, Config4));
+
+	l += snprint(p+l, READSTR-l, "Bmcr %#4.4ux\n", csr16r(ctlr, Bmcr));
+	l += snprint(p+l, READSTR-l, "Bmsr %#4.4ux\n", csr16r(ctlr, Bmsr));
+	l += snprint(p+l, READSTR-l, "Anar %#4.4ux\n", csr16r(ctlr, Anar));
+	l += snprint(p+l, READSTR-l, "Anlpar %#4.4ux\n", csr16r(ctlr, Anlpar));
+	l += snprint(p+l, READSTR-l, "Aner %#4.4ux\n", csr16r(ctlr, Aner));
+	l += snprint(p+l, READSTR-l, "Nwaytr %#4.4ux\n", csr16r(ctlr, Nwaytr));
+	snprint(p+l, READSTR-l, "Cscr %#4.4ux\n", csr16r(ctlr, Cscr));
+	n = readstr(offset, a, n, p);
+	free(p);
+
+	return n;
+}
+
+static int
+rtl8139reset(Ctlr* ctlr)
+{
+	int timeo;
+
+	/*
+	 * Soft reset the controller.
+	 */
+	csr8w(ctlr, Cr, Rst);
+	for(timeo = 0; timeo < 1000; timeo++){
+		if(!(csr8r(ctlr, Cr) & Rst))
+			return 0;
+		delay(1);
+	}
+
+	return -1;
+}
+
+static void
+rtl8139halt(Ctlr* ctlr)
+{
+	int i;
+
+	csr8w(ctlr, Cr, 0);
+	csr16w(ctlr, Imr, 0);
+	csr16w(ctlr, Isr, ~0);
+
+	for(i = 0; i < Ntd; i++){
+		if(ctlr->td[i].bp == nil)
+			continue;
+		freeb(ctlr->td[i].bp);
+		ctlr->td[i].bp = nil;
+	}
+}
+
+static void
+rtl8139init(Ether* edev)
+{
+	int i;
+	ulong r;
+	Ctlr *ctlr;
+	uchar *alloc;
+
+	ctlr = edev->ctlr;
+	ilock(&ctlr->ilock);
+
+	rtl8139halt(ctlr);
+
+	/*
+	 * MAC Address.
+	 */
+	r = (edev->ea[3]<<24)|(edev->ea[2]<<16)|(edev->ea[1]<<8)|edev->ea[0];
+	csr32w(ctlr, Idr0, r);
+	r = (edev->ea[5]<<8)|edev->ea[4];
+	csr32w(ctlr, Idr0+4, r);
+
+	/*
+	 * Receiver
+	 */
+	alloc = (uchar*)ROUNDUP((ulong)ctlr->alloc, 32);
+	ctlr->rbstart = alloc;
+	alloc += ctlr->rblen+16;
+	memset(ctlr->rbstart, 0, ctlr->rblen+16);
+	csr32w(ctlr, Rbstart, PCIWADDR(ctlr->rbstart));
+	ctlr->rcr = Rxfth256|Rblen|Mrxdmaunlimited|Ab|Apm;
+
+	/*
+	 * Transmitter.
+	 */
+	for(i = 0; i < Ntd; i++){
+		ctlr->td[i].tsd = Tsd0+i*4;
+		ctlr->td[i].tsad = Tsad0+i*4;
+		ctlr->td[i].data = alloc;
+		alloc += Tdbsz;
+		ctlr->td[i].bp = nil;
+	}
+	ctlr->ntd = ctlr->tdh = ctlr->tdi = 0;
+	ctlr->etxth = 128/32;
+
+	/*
+	 * Interrupts.
+	 */
+	csr32w(ctlr, TimerInt, 0);
+	csr16w(ctlr, Imr, Serr|Timerbit|Fovw|PunLc|Rxovw|Ter|Tok|Rer|Rok);
+	csr32w(ctlr, Mpc, 0);
+
+	/*
+	 * Enable receiver/transmitter.
+	 * Need to enable before writing the Rcr or it won't take.
+	 */
+	csr8w(ctlr, Cr, Te|Re);
+	csr32w(ctlr, Tcr, Mtxdma2048);
+	csr32w(ctlr, Rcr, ctlr->rcr);
+
+	iunlock(&ctlr->ilock);
+}
+
+static void
+rtl8139attach(Ether* edev)
+{
+	Ctlr *ctlr;
+
+	ctlr = edev->ctlr;
+	qlock(&ctlr->alock);
+	if(ctlr->alloc == nil){
+		ctlr->rblen = 1<<((Rblen>>RblenSHIFT)+13);
+		ctlr->alloc = mallocz(ctlr->rblen+16 + Ntd*Tdbsz + 32, 0);
+		rtl8139init(edev);
+	}
+	qunlock(&ctlr->alock);
+}
+
+static void
+rtl8139txstart(Ether* edev)
+{
+	Td *td;
+	int size;
+	Block *bp;
+	Ctlr *ctlr;
+
+	ctlr = edev->ctlr;
+	while(ctlr->ntd < Ntd){
+		bp = qget(edev->oq);
+		if(bp == nil)
+			break;
+		size = BLEN(bp);
+
+		td = &ctlr->td[ctlr->tdh];
+		if(((int)bp->rp) & 0x03){
+			memmove(td->data, bp->rp, size);
+			freeb(bp);
+			csr32w(ctlr, td->tsad, PCIWADDR(td->data));
+			ctlr->tunaligned++;
+		}
+		else{
+			td->bp = bp;
+			csr32w(ctlr, td->tsad, PCIWADDR(bp->rp));
+			ctlr->taligned++;
+		}
+		csr32w(ctlr, td->tsd, (ctlr->etxth<<EtxthSHIFT)|size);
+
+		ctlr->ntd++;
+		ctlr->tdh = NEXT(ctlr->tdh, Ntd);
+	}
+}
+
+static void
+rtl8139transmit(Ether* edev)
+{
+	Ctlr *ctlr;
+
+	ctlr = edev->ctlr;
+	ilock(&ctlr->tlock);
+	rtl8139txstart(edev);
+	iunlock(&ctlr->tlock);
+}
+
+static void
+rtl8139receive(Ether* edev)
+{
+	Block *bp;
+	Ctlr *ctlr;
+	ushort capr;
+	uchar cr, *p;
+	int l, length, status;
+
+	ctlr = edev->ctlr;
+
+	/*
+	 * Capr is where the host is reading from,
+	 * Cbr is where the NIC is currently writing.
+	 */
+	capr = (csr16r(ctlr, Capr)+16) % ctlr->rblen;
+	while(!(csr8r(ctlr, Cr) & Bufe)){
+		p = ctlr->rbstart+capr;
+
+		/*
+		 * Apparently the packet length may be 0xFFF0 if
+		 * the NIC is still copying the packet into memory.
+		 */
+		length = (*(p+3)<<8)|*(p+2);
+		if(length == 0xFFF0)
+			break;
+		status = (*(p+1)<<8)|*p;
+
+		if(!(status & Rcok)){
+			if(status & (Ise|Fae))
+				edev->frames++;
+			if(status & Crc)
+				edev->crcs++;
+			if(status & (Runt|Long))
+				edev->buffs++;
+
+			/*
+			 * Reset the receiver.
+			 * Also may have to restore the multicast list
+			 * here too if it ever gets used.
+			 */
+			cr = csr8r(ctlr, Cr);
+			csr8w(ctlr, Cr, cr & ~Re);
+			csr32w(ctlr, Rbstart, PCIWADDR(ctlr->rbstart));
+			csr8w(ctlr, Cr, cr);
+			csr32w(ctlr, Rcr, ctlr->rcr);
+
+			continue;
+		}
+
+		/*
+		 * Receive Completed OK.
+		 * Very simplistic; there are ways this could be done
+		 * without copying, but the juice probably isn't worth
+		 * the squeeze.
+		 * The packet length includes a 4 byte CRC on the end.
+		 */
+		capr = (capr+4) % ctlr->rblen;
+		p = ctlr->rbstart+capr;
+		capr = (capr+length) % ctlr->rblen;
+
+		if((bp = iallocb(length)) != nil){
+			if(p+length >= ctlr->rbstart+ctlr->rblen){
+				l = ctlr->rbstart+ctlr->rblen - p;
+				memmove(bp->wp, p, l);
+				bp->wp += l;
+				length -= l;
+				p = ctlr->rbstart;
+			}
+			if(length > 0){
+				memmove(bp->wp, p, length);
+				bp->wp += length;
+			}
+			bp->wp -= 4;
+			etheriq(edev, bp, 1);
+		}
+
+		capr = ROUNDUP(capr, 4);
+		csr16w(ctlr, Capr, capr-16);
+	}
+}
+
+static void
+rtl8139interrupt(Ureg*, void* arg)
+{
+	Td *td;
+	Ctlr *ctlr;
+	Ether *edev;
+	int isr, msr, tsd;
+
+	edev = arg;
+	ctlr = edev->ctlr;
+
+	while((isr = csr16r(ctlr, Isr)) != 0){
+		csr16w(ctlr, Isr, isr);
+		if(isr & (Fovw|PunLc|Rxovw|Rer|Rok)){
+			rtl8139receive(edev);
+			if(!(isr & Rok))
+				ctlr->ierrs++;
+			isr &= ~(Fovw|Rxovw|Rer|Rok);
+		}
+
+		if(isr & (Ter|Tok)){
+			ilock(&ctlr->tlock);
+			while(ctlr->ntd){
+				td = &ctlr->td[ctlr->tdi];
+				tsd = csr32r(ctlr, td->tsd);
+				if(!(tsd & (Tabt|Tun|Tcok)))
+					break;
+
+				if(!(tsd & Tcok)){
+					if(tsd & Tun){
+						if(ctlr->etxth < ETHERMAXTU/32)
+							ctlr->etxth++;
+					}
+					edev->oerrs++;
+				}
+
+				if(td->bp != nil){
+					freeb(td->bp);
+					td->bp = nil;
+				}
+
+				ctlr->ntd--;
+				ctlr->tdi = NEXT(ctlr->tdi, Ntd);
+			}
+			rtl8139txstart(edev);
+			iunlock(&ctlr->tlock);
+			isr &= ~(Ter|Tok);
+		}
+
+		if(isr & PunLc){
+			/*
+			 * Maybe the link changed - do we care very much?
+			 */
+			msr = csr8r(ctlr, Msr);
+			if(!(msr & Linkb)){
+				if(!(msr & Speed10) && edev->mbps != 100){
+					edev->mbps = 100;
+					qsetlimit(edev->oq, 256*1024);
+				}
+				else if((msr & Speed10) && edev->mbps != 10){
+					edev->mbps = 10;
+					qsetlimit(edev->oq, 65*1024);
+				}
+			}
+			isr &= ~(Clc|PunLc);
+		}
+
+		/*
+		 * Only Serr|Timerbit should be left by now.
+		 * Should anything be done to tidy up? TimerInt isn't
+		 * used so that can be cleared. A PCI bus error is indicated
+		 * by Serr, that's pretty serious; is there anyhing to do
+		 * other than try to reinitialise the chip?
+		 */
+		if((isr & (Serr|Timerbit)) != 0){
+			iprint("rtl8139interrupt: imr %#4.4ux isr %#4.4ux\n",
+				csr16r(ctlr, Imr), isr);
+			if(isr & Timerbit)
+				csr32w(ctlr, TimerInt, 0);
+			if(isr & Serr)
+				rtl8139init(edev);
+		}
+	}
+}
+
+static Ctlr*
+rtl8139match(Ether* edev, int id)
+{
+	Pcidev *p;
+	Ctlr *ctlr;
+	int i, port;
+
+	/*
+	 * Any adapter matches if no edev->port is supplied,
+	 * otherwise the ports must match.
+	 */
+	for(ctlr = ctlrhead; ctlr != nil; ctlr = ctlr->next){
+		if(ctlr->active)
+			continue;
+		p = ctlr->pcidev;
+		if(((p->did<<16)|p->vid) != id)
+			continue;
+		port = p->mem[0].bar & ~0x01;
+		if(edev->port != 0 && edev->port != port)
+			continue;
+
+		if(ioalloc(port, p->mem[0].size, 0, "rtl8139") < 0){
+			print("rtl8139: port %#ux in use\n", port);
+			continue;
+		}
+
+		if(pcigetpms(p) > 0){
+			pcisetpms(p, 0);
+	
+			for(i = 0; i < 6; i++)
+				pcicfgw32(p, PciBAR0+i*4, p->mem[i].bar);
+			pcicfgw8(p, PciINTL, p->intl);
+			pcicfgw8(p, PciLTR, p->ltr);
+			pcicfgw8(p, PciCLS, p->cls);
+			pcicfgw16(p, PciPCR, p->pcr);
+		}
+
+		ctlr->port = port;
+		if(rtl8139reset(ctlr)) {
+			iofree(port);
+			continue;
+		}
+		pcisetbme(p);
+
+		ctlr->active = 1;
+		return ctlr;
+	}
+	return nil;
+}
+
+static struct {
+	char*	name;
+	int	id;
+} rtl8139pci[] = {
+	{ "rtl8139",	(0x8139<<16)|0x10EC, },	/* generic */
+	{ "smc1211",	(0x1211<<16)|0x1113, },	/* SMC EZ-Card */
+	{ "dfe-538tx",	(0x1300<<16)|0x1186, }, /* D-Link DFE-538TX */
+	{ "dfe-560txd",	(0x1340<<16)|0x1186, }, /* D-Link DFE-560TXD */
+	{ nil },
+};
+
+static int
+rtl8139pnp(Ether* edev)
+{
+	int i, id;
+	Pcidev *p;
+	Ctlr *ctlr;
+	uchar ea[Eaddrlen];
+
+	/*
+	 * Make a list of all ethernet controllers
+	 * if not already done.
+	 */
+	if(ctlrhead == nil){
+		p = nil;
+		while(p = pcimatch(p, 0, 0)){
+			if(p->ccrb != 0x02 || p->ccru != 0)
+				continue;
+			ctlr = malloc(sizeof(Ctlr));
+			ctlr->pcidev = p;
+			ctlr->id = (p->did<<16)|p->vid;
+
+			if(ctlrhead != nil)
+				ctlrtail->next = ctlr;
+			else
+				ctlrhead = ctlr;
+			ctlrtail = ctlr;
+		}
+	}
+
+	/*
+	 * Is it an RTL8139 under a different name?
+	 * Normally a search is made through all the found controllers
+	 * for one which matches any of the known vid+did pairs.
+	 * If a vid+did pair is specified a search is made for that
+	 * specific controller only.
+	 */
+	id = 0;
+	for(i = 0; i < edev->nopt; i++){
+		if(cistrncmp(edev->opt[i], "id=", 3) == 0)
+			id = strtol(&edev->opt[i][3], nil, 0);
+	}
+
+	ctlr = nil;
+	if(id != 0)
+		ctlr = rtl8139match(edev, id);
+	else for(i = 0; rtl8139pci[i].name; i++){
+		if((ctlr = rtl8139match(edev, rtl8139pci[i].id)) != nil)
+			break;
+	}
+	if(ctlr == nil)
+		return -1;
+
+	edev->ctlr = ctlr;
+	edev->port = ctlr->port;
+	edev->irq = ctlr->pcidev->intl;
+	edev->tbdf = ctlr->pcidev->tbdf;
+
+	/*
+	 * Check if the adapter's station address is to be overridden.
+	 * If not, read it from the device and set in edev->ea.
+	 */
+	memset(ea, 0, Eaddrlen);
+	if(memcmp(ea, edev->ea, Eaddrlen) == 0){
+		i = csr32r(ctlr, Idr0);
+		edev->ea[0] = i;
+		edev->ea[1] = i>>8;
+		edev->ea[2] = i>>16;
+		edev->ea[3] = i>>24;
+		i = csr32r(ctlr, Idr0+4);
+		edev->ea[4] = i;
+		edev->ea[5] = i>>8;
+	}
+
+	edev->attach = rtl8139attach;
+	edev->transmit = rtl8139transmit;
+	edev->interrupt = rtl8139interrupt;
+	edev->ifstat = rtl8139ifstat;
+
+	edev->arg = edev;
+	edev->promiscuous = rtl8139promiscuous;
+	edev->multicast = rtl8139multicast;
+//	edev->shutdown = rtl8139shutdown;
+
+	/*
+	 * This should be much more dynamic but will do for now.
+	 */
+	if((csr8r(ctlr, Msr) & (Speed10|Linkb)) == 0)
+		edev->mbps = 100;
+
+	return 0;
+}
+
+void
+ether8139link(void)
+{
+	addethercard("rtl8139", rtl8139pnp);
+}
--- /dev/null
+++ b/os/pc/ether82543gc.c
@@ -1,0 +1,1367 @@
+/*
+ * Intel RS-82543GC Gigabit Ethernet Controller
+ * as found on the Intel PRO/1000[FT] Server Adapter.
+ * The older non-[FT] cards use the 82542 (LSI L2A1157) chip; no attempt
+ * is made to handle the older chip although it should be possible.
+ * The datasheet is not very clear about running on a big-endian system
+ * and this driver assumes little-endian throughout.
+ * To do:
+ *	GMII/MII
+ *	receive tuning
+ *	transmit tuning
+ */
+#include "u.h"
+#include "../port/lib.h"
+#include "mem.h"
+#include "dat.h"
+#include "fns.h"
+#include "io.h"
+#include "../port/error.h"
+#include "../port/netif.h"
+
+#include "etherif.h"
+
+enum {
+	Ctrl		= 0x00000000,	/* Device Control */
+	Status		= 0x00000008,	/* Device Status */
+	Eecd		= 0x00000010,	/* EEPROM/Flash Control/Data */
+	Ctrlext		= 0x00000018,	/* Extended Device Control */
+	Mdic		= 0x00000020,	/* MDI Control */
+	Fcal		= 0x00000028,	/* Flow Control Address Low */
+	Fcah		= 0x0000002C,	/* Flow Control Address High */
+	Fct		= 0x00000030,	/* Flow Control Type */
+	Icr		= 0x000000C0,	/* Interrupt Cause Read */
+	Ics		= 0x000000C8,	/* Interrupt Cause Set */
+	Ims		= 0x000000D0,	/* Interrupt Mask Set/Read */
+	Imc		= 0x000000D8,	/* Interrupt mask Clear */
+	Rctl		= 0x00000100,	/* Receive Control */
+	Fcttv		= 0x00000170,	/* Flow Control Transmit Timer Value */
+	Txcw		= 0x00000178,	/* Transmit configuration word reg. */
+	Rxcw		= 0x00000180,	/* Receive configuration word reg. */
+	Tctl		= 0x00000400,	/* Transmit Control */
+	Tipg		= 0x00000410,	/* Transmit IPG */
+	Tbt		= 0x00000448,	/* Transmit Burst Timer */
+	Ait		= 0x00000458,	/* Adaptive IFS Throttle */
+	Fcrtl		= 0x00002160,	/* Flow Control RX Threshold Low */
+	Fcrth		= 0x00002168,	/* Flow Control Rx Threshold High */
+	Rdfh		= 0x00002410,	/* Receive data fifo head */
+	Rdft		= 0x00002418,	/* Receive data fifo tail */
+	Rdfhs		= 0x00002420,	/* Receive data fifo head saved */
+	Rdfts		= 0x00002428,	/* Receive data fifo tail saved */
+	Rdfpc		= 0x00002430,	/* Receive data fifo packet count */
+	Rdbal		= 0x00002800,	/* Rdesc Base Address Low */
+	Rdbah		= 0x00002804,	/* Rdesc Base Address High */
+	Rdlen		= 0x00002808,	/* Receive Descriptor Length */
+	Rdh		= 0x00002810,	/* Receive Descriptor Head */
+	Rdt		= 0x00002818,	/* Receive Descriptor Tail */
+	Rdtr		= 0x00002820,	/* Receive Descriptor Timer Ring */
+	Rxdctl		= 0x00002828,	/* Receive Descriptor Control */
+	Txdmac		= 0x00003000,	/* Transfer DMA Control */
+	Ett		= 0x00003008,	/* Early Transmit Control */
+	Tdfh		= 0x00003410,	/* Transmit data fifo head */
+	Tdft		= 0x00003418,	/* Transmit data fifo tail */
+	Tdfhs		= 0x00003420,	/* Transmit data Fifo Head saved */
+	Tdfts		= 0x00003428,	/* Transmit data fifo tail saved */
+	Tdfpc		= 0x00003430,	/* Trasnmit data Fifo packet count */
+	Tdbal		= 0x00003800,	/* Tdesc Base Address Low */
+	Tdbah		= 0x00003804,	/* Tdesc Base Address High */
+	Tdlen		= 0x00003808,	/* Transmit Descriptor Length */
+	Tdh		= 0x00003810,	/* Transmit Descriptor Head */
+	Tdt		= 0x00003818,	/* Transmit Descriptor Tail */
+	Tidv		= 0x00003820,	/* Transmit Interrupt Delay Value */
+	Txdctl		= 0x00003828,	/* Transmit Descriptor Control */
+
+	Statistics	= 0x00004000,	/* Start of Statistics Area */
+	Gorcl		= 0x88/4,	/* Good Octets Received Count */
+	Gotcl		= 0x90/4,	/* Good Octets Transmitted Count */
+	Torl		= 0xC0/4,	/* Total Octets Received */
+	Totl		= 0xC8/4,	/* Total Octets Transmitted */
+	Nstatistics	= 64,
+
+	Rxcsum		= 0x00005000,	/* Receive Checksum Control */
+	Mta		= 0x00005200,	/* Multicast Table Array */
+	Ral		= 0x00005400,	/* Receive Address Low */
+	Rah		= 0x00005404,	/* Receive Address High */
+};
+
+enum {					/* Ctrl */
+	Bem		= 0x00000002,	/* Big Endian Mode */
+	Prior		= 0x00000004,	/* Priority on the PCI bus */
+	Lrst		= 0x00000008,	/* Link Reset */
+	Asde		= 0x00000020,	/* Auto-Speed Detection Enable */
+	Slu		= 0x00000040,	/* Set Link Up */
+	Ilos		= 0x00000080,	/* Invert Loss of Signal (LOS) */
+	Frcspd		= 0x00000800,	/* Force Speed */
+	Frcdplx		= 0x00001000,	/* Force Duplex */
+	Swdpinslo	= 0x003C0000,	/* Software Defined Pins - lo nibble */
+	Swdpin0		= 0x00040000,
+	Swdpin1		= 0x00080000,
+	Swdpin2		= 0x00100000,
+	Swdpin3		= 0x00200000,
+	Swdpiolo	= 0x03C00000,	/* Software Defined I/O Pins */
+	Swdpio0		= 0x00400000,
+	Swdpio1		= 0x00800000,
+	Swdpio2		= 0x01000000,
+	Swdpio3		= 0x02000000,
+	Devrst		= 0x04000000,	/* Device Reset */
+	Rfce		= 0x08000000,	/* Receive Flow Control Enable */
+	Tfce		= 0x10000000,	/* Transmit Flow Control Enable */
+	Vme		= 0x40000000,	/* VLAN Mode Enable */
+};
+
+enum {					/* Status */
+	Lu		= 0x00000002,	/* Link Up */
+	Tckok		= 0x00000004,	/* Transmit clock is running */
+	Rbcok		= 0x00000008,	/* Receive clock is running */
+	Txoff		= 0x00000010,	/* Transmission Paused */
+	Tbimode		= 0x00000020,	/* TBI Mode Indication */
+	SpeedMASK	= 0x000000C0,
+	Speed10		= 0x00000000,	/* 10Mb/s */
+	Speed100	= 0x00000040,	/* 100Mb/s */
+	Speed1000	= 0x00000080,	/* 1000Mb/s */
+	Mtxckok		= 0x00000400,	/* MTX clock is running */
+	Pci66		= 0x00000800,	/* PCI Bus speed indication */
+	Bus64		= 0x00001000,	/* PCI Bus width indication */
+};
+
+enum {					/* Ctrl and Status */
+	Fd		= 0x00000001,	/* Full-Duplex */
+	AsdvMASK	= 0x00000300,
+	Asdv10		= 0x00000000,	/* 10Mb/s */
+	Asdv100		= 0x00000100,	/* 100Mb/s */
+	Asdv1000	= 0x00000200,	/* 1000Mb/s */
+};
+
+enum {					/* Eecd */
+	Sk		= 0x00000001,	/* Clock input to the EEPROM */
+	Cs		= 0x00000002,	/* Chip Select */
+	Di		= 0x00000004,	/* Data Input to the EEPROM */
+	Do		= 0x00000008,	/* Data Output from the EEPROM */
+};
+
+enum {					/* Ctrlext */
+	Gpien		= 0x0000000F,	/* General Purpose Interrupt Enables */
+	Swdpinshi	= 0x000000F0,	/* Software Defined Pins - hi nibble */
+	Swdpiohi	= 0x00000F00,	/* Software Defined Pins - I or O */
+	Asdchk		= 0x00001000,	/* ASD Check */
+	Eerst		= 0x00002000,	/* EEPROM Reset */
+	Ips		= 0x00004000,	/* Invert Power State */
+	Spdbyps		= 0x00008000,	/* Speed Select Bypass */
+};
+
+enum {					/* EEPROM content offsets */
+	Ea		= 0x00,		/* Ethernet Address */
+	Cf		= 0x03,		/* Compatibility Field */
+	Pba		= 0x08,		/* Printed Board Assembly number */
+	Icw1		= 0x0A,		/* Initialization Control Word 1 */
+	Sid		= 0x0B,		/* Subsystem ID */
+	Svid		= 0x0C,		/* Subsystem Vendor ID */
+	Did		= 0x0D,		/* Device ID */
+	Vid		= 0x0E,		/* Vendor ID */
+	Icw2		= 0x0F,		/* Initialization Control Word 2 */
+};
+
+enum {					/* Mdic */
+	MDIdMASK	= 0x0000FFFF,	/* Data */
+	MDIdSHIFT	= 0,
+	MDIrMASK	= 0x001F0000,	/* PHY Register Address */
+	MDIrSHIFT	= 16,
+	MDIpMASK	= 0x03E00000,	/* PHY Address */
+	MDIpSHIFT	= 21,
+	MDIwop		= 0x04000000,	/* Write Operation */
+	MDIrop		= 0x08000000,	/* Read Operation */
+	MDIready	= 0x10000000,	/* End of Transaction */
+	MDIie		= 0x20000000,	/* Interrupt Enable */
+	MDIe		= 0x40000000,	/* Error */
+};
+
+enum {					/* Icr, Ics, Ims, Imc */
+	Txdw		= 0x00000001,	/* Transmit Descriptor Written Back */
+	Txqe		= 0x00000002,	/* Transmit Queue Empty */
+	Lsc		= 0x00000004,	/* Link Status Change */
+	Rxseq		= 0x00000008,	/* Receive Sequence Error */
+	Rxdmt0		= 0x00000010,	/* Rdesc Minimum Threshold Reached */
+	Rxo		= 0x00000040,	/* Receiver Overrun */
+	Rxt0		= 0x00000080,	/* Receiver Timer Interrupt */
+	Mdac		= 0x00000200,	/* MDIO Access Completed */
+	Rxcfg		= 0x00000400,	/* Receiving /C/ ordered sets */
+	Gpi0		= 0x00000800,	/* General Purpose Interrupts */
+	Gpi1		= 0x00001000,
+	Gpi2		= 0x00002000,
+	Gpi3		= 0x00004000,
+};
+
+enum {					/* Txcw */
+	Ane		= 0x80000000,	/* Autonegotiate enable */
+	Np		= 0x00008000,	/* Next Page */
+	As		= 0x00000100,	/* Asymmetric Flow control desired */
+	Ps		= 0x00000080,	/* Pause supported */
+	Hd		= 0x00000040,	/* Half duplex supported */
+	TxcwFd		= 0x00000020,	/* Full Duplex supported */
+};
+
+enum {					/* Rxcw */
+	Rxword		= 0x0000FFFF,	/* Data from auto-negotiation process */
+	Rxnocarrier	= 0x04000000,	/* Carrier Sense indication */
+	Rxinvalid	= 0x08000000,	/* Invalid Symbol during configuration */
+	Rxchange	= 0x10000000,	/* Change to the Rxword indication */
+	Rxconfig	= 0x20000000,	/* /C/ order set reception indication */
+	Rxsync		= 0x40000000,	/* Lost bit synchronization indication */
+	Anc		= 0x80000000,	/* Auto Negotiation Complete */
+};
+
+enum {					/* Rctl */
+	Rrst		= 0x00000001,	/* Receiver Software Reset */
+	Ren		= 0x00000002,	/* Receiver Enable */
+	Sbp		= 0x00000004,	/* Store Bad Packets */
+	Upe		= 0x00000008,	/* Unicast Promiscuous Enable */
+	Mpe		= 0x00000010,	/* Multicast Promiscuous Enable */
+	Lpe		= 0x00000020,	/* Long Packet Reception Enable */
+	LbmMASK		= 0x000000C0,	/* Loopback Mode */
+	LbmOFF		= 0x00000000,	/* No Loopback */
+	LbmTBI		= 0x00000040,	/* TBI Loopback */
+	LbmMII		= 0x00000080,	/* GMII/MII Loopback */
+	LbmXCVR		= 0x000000C0,	/* Transceiver Loopback */
+	RdtmsMASK	= 0x00000300,	/* Rdesc Minimum Threshold Size */
+	RdtmsHALF	= 0x00000000,	/* Threshold is 1/2 Rdlen */
+	RdtmsQUARTER	= 0x00000100,	/* Threshold is 1/4 Rdlen */
+	RdtmsEIGHTH	= 0x00000200,	/* Threshold is 1/8 Rdlen */
+	MoMASK		= 0x00003000,	/* Multicast Offset */
+	Bam		= 0x00008000,	/* Broadcast Accept Mode */
+	BsizeMASK	= 0x00030000,	/* Receive Buffer Size */
+	Bsize2048	= 0x00000000,	/* Bsex = 0 */
+	Bsize1024	= 0x00010000,	/* Bsex = 0 */
+	Bsize512	= 0x00020000,	/* Bsex = 0 */
+	Bsize256	= 0x00030000,	/* Bsex = 0 */
+	Bsize16384	= 0x00010000,	/* Bsex = 1 */
+	Vfe		= 0x00040000,	/* VLAN Filter Enable */
+	Cfien		= 0x00080000,	/* Canonical Form Indicator Enable */
+	Cfi		= 0x00100000,	/* Canonical Form Indicator value */
+	Dpf		= 0x00400000,	/* Discard Pause Frames */
+	Pmcf		= 0x00800000,	/* Pass MAC Control Frames */
+	Bsex		= 0x02000000,	/* Buffer Size Extension */
+	Secrc		= 0x04000000,	/* Strip CRC from incoming packet */
+};
+
+enum {					/* Tctl */
+	Trst		= 0x00000001,	/* Transmitter Software Reset */
+	Ten		= 0x00000002,	/* Transmit Enable */
+	Psp		= 0x00000008,	/* Pad Short Packets */
+	CtMASK		= 0x00000FF0,	/* Collision Threshold */
+	CtSHIFT		= 4,
+	ColdMASK	= 0x003FF000,	/* Collision Distance */
+	ColdSHIFT	= 12,
+	Swxoff		= 0x00400000,	/* Sofware XOFF Transmission */
+	Pbe		= 0x00800000,	/* Packet Burst Enable */
+	Rtlc		= 0x01000000,	/* Re-transmit on Late Collision */
+	Nrtu		= 0x02000000,	/* No Re-transmit on Underrrun */
+};
+
+enum {					/* [RT]xdctl */
+	PthreshMASK	= 0x0000003F,	/* Prefetch Threshold */
+	PthreshSHIFT	= 0,
+	HthreshMASK	= 0x00003F00,	/* Host Threshold */
+	HthreshSHIFT	= 8,
+	WthreshMASK	= 0x003F0000,	/* Writeback Threshold */
+	WthreshSHIFT	= 16,
+	Gran		= 0x00000000,	/* Granularity */
+	RxGran		= 0x01000000,	/* Granularity */
+};
+
+enum {					/* Rxcsum */
+	PcssMASK	= 0x000000FF,	/* Packet Checksum Start */
+	PcssSHIFT	= 0,
+	Ipofl		= 0x00000100,	/* IP Checksum Off-load Enable */
+	Tuofl		= 0x00000200,	/* TCP/UDP Checksum Off-load Enable */
+};
+
+enum {					/* Receive Delay Timer Ring */
+	Fpd		= 0x80000000,	/* Flush partial Descriptor Block */
+};
+
+typedef struct Rdesc {			/* Receive Descriptor */
+	uint	addr[2];
+	ushort	length;
+	ushort	checksum;
+	uchar	status;
+	uchar	errors;
+	ushort	special;
+} Rdesc;
+
+enum {					/* Rdesc status */
+	Rdd		= 0x01,		/* Descriptor Done */
+	Reop		= 0x02,		/* End of Packet */
+	Ixsm		= 0x04,		/* Ignore Checksum Indication */
+	Vp		= 0x08,		/* Packet is 802.1Q (matched VET) */
+	Tcpcs		= 0x20,		/* TCP Checksum Calculated on Packet */
+	Ipcs		= 0x40,		/* IP Checksum Calculated on Packet */
+	Pif		= 0x80,		/* Passed in-exact filter */
+};
+
+enum {					/* Rdesc errors */
+	Ce		= 0x01,		/* CRC Error or Alignment Error */
+	Se		= 0x02,		/* Symbol Error */
+	Seq		= 0x04,		/* Sequence Error */
+	Cxe		= 0x10,		/* Carrier Extension Error */
+	Tcpe		= 0x20,		/* TCP/UDP Checksum Error */
+	Ipe		= 0x40,		/* IP Checksum Error */
+	Rxe		= 0x80,		/* RX Data Error */
+};
+
+typedef struct Tdesc {			/* Legacy+Normal Transmit Descriptor */
+	uint	addr[2];
+	uint	control;		/* varies with descriptor type */
+	uint	status;			/* varies with descriptor type */
+} Tdesc;
+
+enum {					/* Tdesc control */
+	CsoMASK		= 0x00000F00,	/* Checksum Offset */
+	CsoSHIFT	= 16,
+	Teop		= 0x01000000,	/* End of Packet */
+	Ifcs		= 0x02000000,	/* Insert FCS */
+	Ic		= 0x04000000,	/* Insert Checksum (Dext == 0) */
+	Tse		= 0x04000000,	/* TCP Segmentaion Enable (Dext == 1) */
+	Rs		= 0x08000000,	/* Report Status */
+	Rps		= 0x10000000,	/* Report Status Sent */
+	Dext		= 0x20000000,	/* Extension (!legacy) */
+	Vle		= 0x40000000,	/* VLAN Packet Enable */
+	Ide		= 0x80000000,	/* Interrupt Delay Enable */
+};
+
+enum {					/* Tdesc status */
+	Tdd		= 0x00000001,	/* Descriptor Done */
+	Ec		= 0x00000002,	/* Excess Collisions */
+	Lc		= 0x00000004,	/* Late Collision */
+	Tu		= 0x00000008,	/* Transmit Underrun */
+	CssMASK		= 0x0000FF00,	/* Checksum Start Field */
+	CssSHIFT	= 8,
+};
+
+enum {
+	Nrdesc		= 256,		/* multiple of 8 */
+	Ntdesc		= 256,		/* multiple of 8 */
+	Nblocks		= 4098,		/* total number of blocks to use */
+
+	SBLOCKSIZE	= 2048,
+	JBLOCKSIZE	= 16384,
+
+	NORMAL		= 1,
+	JUMBO		= 2,
+};
+
+typedef struct Ctlr Ctlr;
+typedef struct Ctlr {
+	int	port;
+	Pcidev*	pcidev;
+	Ctlr*	next;
+	int	active;
+	int	started;
+	int	id;
+	ushort	eeprom[0x40];
+
+	int*	nic;
+	int	im;			/* interrupt mask */
+
+	Lock	slock;
+	uint	statistics[Nstatistics];
+
+	Lock	rdlock;
+	Rdesc*	rdba;			/* receive descriptor base address */
+	Block*	rb[Nrdesc];		/* receive buffers */
+	int	rdh;			/* receive descriptor head */
+	int	rdt;			/* receive descriptor tail */
+	Block**	freehead;		/* points to long or short head */
+
+	Lock	tdlock;
+	Tdesc*	tdba;			/* transmit descriptor base address */
+	Block*	tb[Ntdesc];		/* transmit buffers */
+	int	tdh;			/* transmit descriptor head */
+	int	tdt;			/* transmit descriptor tail */
+	int	txstalled;		/* count of times unable to send */
+
+	int	txcw;
+	int	fcrtl;
+	int	fcrth;
+
+	ulong	multimask[128];		/* bit mask for multicast addresses */
+} Ctlr;
+
+static Ctlr* gc82543ctlrhead;
+static Ctlr* gc82543ctlrtail;
+
+static Lock freelistlock;
+static Block* freeShortHead;
+static Block* freeJumboHead;
+
+#define csr32r(c, r)	(*((c)->nic+((r)/4)))
+#define csr32w(c, r, v)	(*((c)->nic+((r)/4)) = (v))
+
+static void gc82543watchdog(void* arg);
+
+static void
+gc82543attach(Ether* edev)
+{
+	int ctl;
+	Ctlr *ctlr;
+	char name[KNAMELEN];
+
+	/*
+	 * To do here:
+	 *	one-time stuff;
+	 *		adjust queue length depending on speed;
+	 *		flow control.
+	 *	more needed here...
+	 */
+	ctlr = edev->ctlr;
+	lock(&ctlr->slock);
+	if(ctlr->started == 0){
+		ctlr->started = 1;
+		snprint(name, KNAMELEN, "#l%d82543", edev->ctlrno);
+		kproc(name, gc82543watchdog, edev, 0);
+	}
+	unlock(&ctlr->slock);
+
+	ctl = csr32r(ctlr, Rctl)|Ren;
+	csr32w(ctlr, Rctl, ctl);
+	ctl = csr32r(ctlr, Tctl)|Ten;
+	csr32w(ctlr, Tctl, ctl);
+
+	csr32w(ctlr, Ims, ctlr->im);
+}
+
+static char* statistics[Nstatistics] = {
+	"CRC Error",
+	"Alignment Error",
+	"Symbol Error",
+	"RX Error",
+	"Missed Packets",
+	"Single Collision",
+	"Excessive Collisions",
+	"Multiple Collision",
+	"Late Collisions",
+	nil,
+	"Collision",
+	"Transmit Underrun",
+	"Defer",
+	"Transmit - No CRS",
+	"Sequence Error",
+	"Carrier Extension Error",
+	"Receive Error Length",
+	nil,
+	"XON Received",
+	"XON Transmitted",
+	"XOFF Received",
+	"XOFF Transmitted",
+	"FC Received Unsupported",
+	"Packets Received (64 Bytes)",
+	"Packets Received (65-127 Bytes)",
+	"Packets Received (128-255 Bytes)",
+	"Packets Received (256-511 Bytes)",
+	"Packets Received (512-1023 Bytes)",
+	"Packets Received (1024-1522 Bytes)",
+	"Good Packets Received",
+	"Broadcast Packets Received",
+	"Multicast Packets Received",
+	"Good Packets Transmitted",
+	nil,
+	"Good Octets Received",
+	nil,
+	"Good Octets Transmitted",
+	nil,
+	nil,
+	nil,
+	"Receive No Buffers",
+	"Receive Undersize",
+	"Receive Fragment",
+	"Receive Oversize",
+	"Receive Jabber",
+	nil,
+	nil,
+	nil,
+	"Total Octets Received",
+	nil,
+	"Total Octets Transmitted",
+	nil,
+	"Total Packets Received",
+	"Total Packets Transmitted",
+	"Packets Transmitted (64 Bytes)",
+	"Packets Transmitted (65-127 Bytes)",
+	"Packets Transmitted (128-255 Bytes)",
+	"Packets Transmitted (256-511 Bytes)",
+	"Packets Transmitted (512-1023 Bytes)",
+	"Packets Transmitted (1024-1522 Bytes)",
+	"Multicast Packets Transmitted",
+	"Broadcast Packets Transmitted",
+	"TCP Segmentation Context Transmitted",
+	"TCP Segmentation Context Fail",
+};
+
+static long
+gc82543ifstat(Ether* edev, void* a, long n, ulong offset)
+{
+	Ctlr *ctlr;
+	char *p, *s;
+	int i, l, r;
+	uvlong tuvl, ruvl;
+
+	ctlr = edev->ctlr;
+	lock(&ctlr->slock);
+	p = malloc(2*READSTR);
+	l = 0;
+	for(i = 0; i < Nstatistics; i++){
+		r = csr32r(ctlr, Statistics+i*4);
+		if((s = statistics[i]) == nil)
+			continue;
+		switch(i){
+		case Gorcl:
+		case Gotcl:
+		case Torl:
+		case Totl:
+			ruvl = r;
+			ruvl += ((uvlong)csr32r(ctlr, Statistics+(i+1)*4))<<32;
+			tuvl = ruvl;
+			tuvl += ctlr->statistics[i];
+			tuvl += ((uvlong)ctlr->statistics[i+1])<<32;
+			if(tuvl == 0)
+				continue;
+			ctlr->statistics[i] = tuvl;
+			ctlr->statistics[i+1] = tuvl>>32;
+			l += snprint(p+l, 2*READSTR-l, "%s: %llud %llud\n",
+				s, tuvl, ruvl);
+			i++;
+			break;
+
+		default:
+			ctlr->statistics[i] += r;
+			if(ctlr->statistics[i] == 0)
+				continue;
+			l += snprint(p+l, 2*READSTR-l, "%s: %ud %ud\n",
+				s, ctlr->statistics[i], r);
+			break;
+		}
+	}
+
+	l += snprint(p+l, 2*READSTR-l, "eeprom:");
+	for(i = 0; i < 0x40; i++){
+		if(i && ((i & 0x07) == 0))
+			l += snprint(p+l, 2*READSTR-l, "\n       ");
+		l += snprint(p+l, 2*READSTR-l, " %4.4uX", ctlr->eeprom[i]);
+	}
+
+	snprint(p+l, 2*READSTR-l, "\ntxstalled %d\n", ctlr->txstalled);
+	n = readstr(offset, a, n, p);
+	free(p);
+	unlock(&ctlr->slock);
+
+	return n;
+}
+
+static void
+gc82543promiscuous(void* arg, int on)
+{
+	int rctl;
+	Ctlr *ctlr;
+	Ether *edev;
+
+	edev = arg;
+	ctlr = edev->ctlr;
+
+	rctl = csr32r(ctlr, Rctl);
+	rctl &= ~MoMASK;		/* make sure we're using bits 47:36 */
+	if(on)
+		rctl |= Upe|Mpe;
+	else
+		rctl &= ~(Upe|Mpe);
+	csr32w(ctlr, Rctl, rctl);
+}
+
+static void
+gc82543multicast(void* arg, uchar* addr, int on)
+{
+	int bit, x;
+	Ctlr *ctlr;
+	Ether *edev;
+
+	edev = arg;
+	ctlr = edev->ctlr;
+	x = addr[5]>>1;
+	bit = ((addr[5] & 1)<<4)|(addr[4]>>4);
+	if(on)
+		ctlr->multimask[x] |= 1<<bit;
+	else
+		ctlr->multimask[x] &= ~(1<<bit);
+	
+	csr32w(ctlr, Mta+x*4, ctlr->multimask[x]);
+}
+
+static long
+gc82543ctl(Ether* edev, void* buf, long n)
+{
+	Cmdbuf *cb;
+	Ctlr *ctlr;
+	int ctrl, i, r;
+
+	ctlr = edev->ctlr;
+	if(ctlr == nil)
+		error(Enonexist);
+
+	lock(&ctlr->slock);
+	r = 0;
+	cb = parsecmd(buf, n);
+	if(cb->nf < 2)
+		r = -1;
+	else if(cistrcmp(cb->f[0], "auto") == 0){
+		ctrl = csr32r(ctlr, Ctrl);
+		if(cistrcmp(cb->f[1], "off") == 0){
+			csr32w(ctlr, Txcw, ctlr->txcw & ~Ane);
+			ctrl |= (Slu|Fd);
+			if(ctlr->txcw & As)
+				ctrl |= Rfce;
+			if(ctlr->txcw & Ps)
+				ctrl |= Tfce;
+			csr32w(ctlr, Ctrl, ctrl);
+		}
+		else if(cistrcmp(cb->f[1], "on") == 0){
+			csr32w(ctlr, Txcw, ctlr->txcw);
+			ctrl &= ~(Slu|Fd);
+			csr32w(ctlr, Ctrl, ctrl);
+		}
+		else
+			r = -1;
+	}
+	else if(cistrcmp(cb->f[0], "clear") == 0){
+		if(cistrcmp(cb->f[1], "stats") == 0){
+			for(i = 0; i < Nstatistics; i++)
+				ctlr->statistics[i] = 0;
+		}
+		else
+			r = -1;
+	}
+	else
+		r = -1;
+	unlock(&ctlr->slock);
+
+	free(cb);
+	return (r == 0) ? n : r;
+}
+
+static void
+gc82543txinit(Ctlr* ctlr)
+{
+	int i;
+	int tdsize;
+	Block *bp, **bpp;
+
+	tdsize = ROUND(Ntdesc*sizeof(Tdesc), 4096);
+
+	if(ctlr->tdba == nil)
+		ctlr->tdba = xspanalloc(tdsize, 32, 0);
+
+	for(i = 0; i < Ntdesc; i++){
+		bpp = &ctlr->tb[i];
+		bp = *bpp;
+		if(bp != nil){
+			*bpp = nil;
+			freeb(bp);
+		}
+		memset(&ctlr->tdba[i], 0, sizeof(Tdesc));
+	}
+
+	csr32w(ctlr, Tdbal, PCIWADDR(ctlr->tdba));
+	csr32w(ctlr, Tdbah, 0);
+	csr32w(ctlr, Tdlen, Ntdesc*sizeof(Tdesc));
+
+	/*
+	 * set the ring head and tail pointers.
+	 */
+	ctlr->tdh = 0;
+	csr32w(ctlr, Tdh, ctlr->tdh);
+	ctlr->tdt = 0;
+	csr32w(ctlr, Tdt, ctlr->tdt);
+
+	csr32w(ctlr, Tipg, (6<<20)|(8<<10)|6);
+	csr32w(ctlr, Tidv, 128);
+	csr32w(ctlr, Ait, 0);
+	csr32w(ctlr, Txdmac, 0);
+	csr32w(ctlr, Txdctl, Gran|(4<<WthreshSHIFT)|(1<<HthreshSHIFT)|16);
+	csr32w(ctlr, Tctl, (0x0F<<CtSHIFT)|Psp|(6<<ColdSHIFT));
+
+	ctlr->im |= Txdw;
+}
+
+static void
+gc82543transmit(Ether* edev)
+{
+	Block *bp, **bpp;
+	Ctlr *ctlr;
+	Tdesc *tdesc;
+	int tdh, tdt, s;
+
+	ctlr = edev->ctlr;
+
+	ilock(&ctlr->tdlock);
+	tdh = ctlr->tdh;
+	for(;;){
+		/*
+		 * Free any completed packets
+		 */
+		tdesc = &ctlr->tdba[tdh];
+		if(!(tdesc->status & Tdd))
+			break;
+		memset(tdesc, 0, sizeof(Tdesc));
+		bpp = &ctlr->tb[tdh];
+		bp = *bpp;
+		if(bp != nil){
+			*bpp = nil;
+			freeb(bp);
+		}
+		tdh = NEXT(tdh, Ntdesc);
+	}
+	ctlr->tdh = tdh;
+	s = csr32r(ctlr, Status);
+
+	/*
+	 * Try to fill the ring back up
+	 * but only if link is up and transmission isn't paused.
+	 */
+	if((s & (Txoff|Lu)) == Lu){
+		tdt = ctlr->tdt;
+		while(NEXT(tdt, Ntdesc) != tdh){
+			if((bp = qget(edev->oq)) == nil)
+				break;
+
+			tdesc = &ctlr->tdba[tdt];
+			tdesc->addr[0] = PCIWADDR(bp->rp);
+			tdesc->control = Ide|Rs|Ifcs|Teop|BLEN(bp);
+			ctlr->tb[tdt] = bp;
+			tdt = NEXT(tdt, Ntdesc);
+		}
+
+		if(tdt != ctlr->tdt){
+			ctlr->tdt = tdt;
+			csr32w(ctlr, Tdt, tdt);
+		}
+	}
+	else
+		ctlr->txstalled++;
+
+	iunlock(&ctlr->tdlock);
+}
+
+static Block *
+gc82543allocb(Ctlr* ctlr)
+{
+	Block *bp;
+
+	ilock(&freelistlock);
+	if((bp = *(ctlr->freehead)) != nil){
+		*(ctlr->freehead) = bp->next;
+		bp->next = nil;
+	}
+	iunlock(&freelistlock);
+	return bp;
+}
+
+static void
+gc82543replenish(Ctlr* ctlr)
+{
+	int rdt;
+	Block *bp;
+	Rdesc *rdesc;
+
+	ilock(&ctlr->rdlock);
+	rdt = ctlr->rdt;
+	while(NEXT(rdt, Nrdesc) != ctlr->rdh){
+		rdesc = &ctlr->rdba[rdt];
+		if(ctlr->rb[rdt] == nil){
+			bp = gc82543allocb(ctlr);
+			if(bp == nil){
+				iprint("no available buffers\n");
+				break;
+			}
+			ctlr->rb[rdt] = bp;
+			rdesc->addr[0] = PCIWADDR(bp->rp);
+			rdesc->addr[1] = 0;
+		}
+		coherence();
+		rdesc->status = 0;
+		rdt = NEXT(rdt, Nrdesc);
+	}
+	ctlr->rdt = rdt;
+	csr32w(ctlr, Rdt, rdt);
+	iunlock(&ctlr->rdlock);
+}
+
+static void
+gc82543rxinit(Ctlr* ctlr)
+{
+	int rdsize, i;
+
+	csr32w(ctlr, Rctl, Dpf|Bsize2048|Bam|RdtmsHALF);
+
+	/*
+	 * Allocate the descriptor ring and load its
+	 * address and length into the NIC.
+	 */
+	rdsize = ROUND(Nrdesc*sizeof(Rdesc), 4096);
+	if(ctlr->rdba == nil)
+		ctlr->rdba = xspanalloc(rdsize, 32, 0);
+	memset(ctlr->rdba, 0, rdsize);
+
+	ctlr->rdh = 0;
+	ctlr->rdt = 0;
+
+	csr32w(ctlr, Rdtr, Fpd|64);
+	csr32w(ctlr, Rdbal, PCIWADDR(ctlr->rdba));
+	csr32w(ctlr, Rdbah, 0);
+	csr32w(ctlr, Rdlen, Nrdesc*sizeof(Rdesc));
+	csr32w(ctlr, Rdh, 0);
+	csr32w(ctlr, Rdt, 0);
+	for(i = 0; i < Nrdesc; i++){
+		if(ctlr->rb[i] != nil){
+			freeb(ctlr->rb[i]);
+			ctlr->rb[i] = nil;
+		}
+	}
+	gc82543replenish(ctlr);
+
+	csr32w(ctlr, Rxdctl, RxGran|(8<<WthreshSHIFT)|(4<<HthreshSHIFT)|1);
+	ctlr->im |= Rxt0|Rxo|Rxdmt0|Rxseq;
+}
+
+static void
+gc82543recv(Ether* edev, int icr)
+{
+	Block *bp;
+	Ctlr *ctlr;
+	Rdesc *rdesc;
+	int rdh;
+
+	ctlr = edev->ctlr;
+
+	rdh = ctlr->rdh;
+	for(;;){
+		rdesc = &ctlr->rdba[rdh];
+
+		if(!(rdesc->status & Rdd))
+			break;
+
+		if((rdesc->status & Reop) && rdesc->errors == 0){
+			bp = ctlr->rb[rdh];
+			ctlr->rb[rdh] = nil;
+			bp->wp += rdesc->length;
+			bp->next = nil;
+			etheriq(edev, bp, 1);
+		}
+
+		if(ctlr->rb[rdh] != nil){
+			/* either non eop packet, or error */
+			freeb(ctlr->rb[rdh]);
+			ctlr->rb[rdh] = nil;
+		}
+		memset(rdesc, 0, sizeof(Rdesc));
+		coherence();
+		rdh = NEXT(rdh, Nrdesc);
+	}
+	ctlr->rdh = rdh;
+
+	if(icr & Rxdmt0)
+		gc82543replenish(ctlr);
+}
+
+static void
+freegc82543short(Block *bp)
+{
+	ilock(&freelistlock);
+	/* reset read/write pointer to proper positions */
+	bp->rp = bp->lim - ROUND(SBLOCKSIZE, BLOCKALIGN);
+	bp->wp = bp->rp;
+	bp->next = freeShortHead;
+	freeShortHead = bp;
+	iunlock(&freelistlock);
+}
+
+static void
+freegc82532jumbo(Block *bp)
+{
+	ilock(&freelistlock);
+	/* reset read/write pointer to proper positions */
+	bp->rp = bp->lim - ROUND(JBLOCKSIZE, BLOCKALIGN);
+	bp->wp = bp->rp;
+	bp->next = freeJumboHead;
+	freeJumboHead = bp;
+	iunlock(&freelistlock);
+}
+
+static void
+linkintr(Ctlr* ctlr)
+{
+	int ctrl;
+
+	ctrl = csr32r(ctlr, Ctrl);
+
+	if((ctrl & Swdpin1) ||
+	  ((csr32r(ctlr, Rxcw) & Rxconfig) && !(csr32r(ctlr, Txcw) & Ane))){
+ 		csr32w(ctlr, Txcw, ctlr->txcw);
+		ctrl &= ~(Slu|Fd|Frcdplx);
+		csr32w(ctlr, Ctrl, ctrl);
+	}
+}
+
+static void
+gc82543interrupt(Ureg*, void* arg)
+{
+	Ctlr *ctlr;
+	Ether *edev;
+	int icr;
+
+	edev = arg;
+	ctlr = edev->ctlr;
+
+	while((icr = csr32r(ctlr, Icr) & ctlr->im) != 0){
+		/*
+		 * Link status changed.
+		 */
+		if(icr & (Lsc|Rxseq))
+			linkintr(ctlr);
+
+		/*
+		 * Process recv buffers.
+		 */
+		gc82543recv(edev, icr);
+
+		/*
+		 * Refill transmit ring and free packets.
+		 */
+		gc82543transmit(edev);
+	}
+}
+
+static int
+gc82543init(Ether* edev)
+{
+	int csr, i;
+	Block *bp;
+	Ctlr *ctlr;
+
+	ctlr = edev->ctlr;
+
+	/*
+	 * Allocate private buffer pool to use for receiving packets.
+	 */
+	ilock(&freelistlock);
+	if (ctlr->freehead == nil){
+		for(i = 0; i < Nblocks; i++){
+			bp = iallocb(SBLOCKSIZE);
+			if(bp != nil){
+				bp->next = freeShortHead;
+				bp->free = freegc82543short;
+				freeShortHead = bp;
+			}
+			else{
+				print("82543gc: no memory\n");
+				break;
+			}
+		}
+		ctlr->freehead = &freeShortHead;
+	}
+	iunlock(&freelistlock);
+
+	/*
+	 * Set up the receive addresses.
+	 * There are 16 addresses. The first should be the MAC address.
+	 * The others are cleared and not marked valid (MS bit of Rah).
+	 */
+	csr = (edev->ea[3]<<24)|(edev->ea[2]<<16)|(edev->ea[1]<<8)|edev->ea[0];
+	csr32w(ctlr, Ral, csr);
+	csr = 0x80000000|(edev->ea[5]<<8)|edev->ea[4];
+	csr32w(ctlr, Rah, csr);
+	for(i = 1; i < 16; i++){
+		csr32w(ctlr, Ral+i*8, 0);
+		csr32w(ctlr, Rah+i*8, 0);
+	}
+
+	/*
+	 * Clear the Multicast Table Array.
+	 * It's a 4096 bit vector accessed as 128 32-bit registers.
+	 */
+	for(i = 0; i < 128; i++)
+		csr32w(ctlr, Mta+i*4, 0);
+
+	gc82543txinit(ctlr);
+	gc82543rxinit(ctlr);
+
+	return 0;
+}
+
+static int
+at93c46io(Ctlr* ctlr, char* op, int data)
+{
+	char *lp, *p;
+	int i, loop, eecd, r;
+
+	eecd = csr32r(ctlr, Eecd);
+
+	r = 0;
+	loop = -1;
+	lp = nil;
+	for(p = op; *p != '\0'; p++){
+		switch(*p){
+		default:
+			return -1;
+		case ' ':
+			continue;
+		case ':':			/* start of loop */
+			if(lp != nil){
+				if(p != (lp+1) || loop != 7)
+					return -1;
+				lp = p;
+				loop = 15;
+				continue;
+			}
+			lp = p;
+			loop = 7;
+			continue;
+		case ';':			/* end of loop */
+			if(lp == nil)
+				return -1;
+			loop--;
+			if(loop >= 0)
+				p = lp;
+			else
+				lp = nil;
+			continue;
+		case 'C':			/* assert clock */
+			eecd |= Sk;
+			break;
+		case 'c':			/* deassert clock */
+			eecd &= ~Sk;
+			break;
+		case 'D':			/* next bit in 'data' byte */
+			if(loop < 0)
+				return -1;
+			if(data & (1<<loop))
+				eecd |= Di;
+			else
+				eecd &= ~Di;
+			break;
+		case 'O':			/* collect data output */
+			i = (csr32r(ctlr, Eecd) & Do) != 0;
+			if(loop >= 0)
+				r |= (i<<loop);
+			else
+				r = i;
+			continue;
+		case 'I':			/* assert data input */
+			eecd |= Di;
+			break;
+		case 'i':			/* deassert data input */
+			eecd &= ~Di;
+			break;
+		case 'S':			/* enable chip select */
+			eecd |= Cs;
+			break;
+		case 's':			/* disable chip select */
+			eecd &= ~Cs;
+			break;
+		}
+		csr32w(ctlr, Eecd, eecd);
+		microdelay(1);
+	}
+	if(loop >= 0)
+		return -1;
+	return r;
+}
+
+static int
+at93c46r(Ctlr* ctlr)
+{
+	ushort sum;
+	int addr, data;
+
+	sum = 0;
+	for(addr = 0; addr < 0x40; addr++){
+		/*
+		 * Read a word at address 'addr' from the Atmel AT93C46
+		 * 3-Wire Serial EEPROM or compatible. The EEPROM access is
+		 * controlled by 4 bits in Eecd. See the AT93C46 datasheet
+		 * for protocol details.
+		 */
+		if(at93c46io(ctlr, "S ICc :DCc;", (0x02<<6)|addr) != 0)
+			break;
+		data = at93c46io(ctlr, "::COc;", 0);
+		at93c46io(ctlr, "sic", 0);
+		ctlr->eeprom[addr] = data;
+		sum += data;
+	}
+
+	return sum;
+}
+
+static void
+gc82543detach(Ctlr* ctlr)
+{
+	/*
+	 * Perform a device reset to get the chip back to the
+	 * power-on state, followed by an EEPROM reset to read
+	 * the defaults for some internal registers.
+	 */
+	csr32w(ctlr, Imc, ~0);
+	csr32w(ctlr, Rctl, 0);
+	csr32w(ctlr, Tctl, 0);
+
+	delay(10);
+
+	csr32w(ctlr, Ctrl, Devrst);
+	while(csr32r(ctlr, Ctrl) & Devrst)
+		;
+
+	csr32w(ctlr, Ctrlext, Eerst);
+	while(csr32r(ctlr, Ctrlext) & Eerst)
+		;
+
+	csr32w(ctlr, Imc, ~0);
+	while(csr32r(ctlr, Icr))
+		;
+}
+
+static void
+gc82543checklink(Ctlr* ctlr)
+{
+	int ctrl, status, rxcw;
+
+	ctrl = csr32r(ctlr, Ctrl);
+	status = csr32r(ctlr, Status);
+	rxcw = csr32r(ctlr, Rxcw);
+
+	if(!(status & Lu)){
+		if(!(ctrl & (Swdpin1|Slu)) && !(rxcw & Rxconfig)){
+			csr32w(ctlr, Txcw, ctlr->txcw & ~Ane);
+			ctrl |= (Slu|Fd);
+			if(ctlr->txcw & As)
+				ctrl |= Rfce;
+			if(ctlr->txcw & Ps)
+				ctrl |= Tfce;
+			csr32w(ctlr, Ctrl, ctrl);
+		}
+	}
+	else if((ctrl & Slu) && (rxcw & Rxconfig)){
+		csr32w(ctlr, Txcw, ctlr->txcw);
+		ctrl &= ~(Slu|Fd);
+		csr32w(ctlr, Ctrl, ctrl);
+	}
+}
+
+static void
+gc82543shutdown(Ether* ether)
+{
+	gc82543detach(ether->ctlr);
+}
+
+static int
+gc82543reset(Ctlr* ctlr)
+{
+	int ctl;
+	int te;
+
+	/*
+	 * Read the EEPROM, validate the checksum
+	 * then get the device back to a power-on state.
+	 */
+	if(at93c46r(ctlr) != 0xBABA)
+		return -1;
+
+	gc82543detach(ctlr);
+
+	te = ctlr->eeprom[Icw2];
+	if((te & 0x3000) == 0){
+		ctlr->fcrtl = 0x00002000;
+		ctlr->fcrth = 0x00004000;
+		ctlr->txcw = Ane|TxcwFd;
+	}
+	else if((te & 0x3000) == 0x2000){
+		ctlr->fcrtl = 0;
+		ctlr->fcrth = 0;
+		ctlr->txcw = Ane|TxcwFd|As;
+	}
+	else{
+		ctlr->fcrtl = 0x00002000;
+		ctlr->fcrth = 0x00004000;
+		ctlr->txcw = Ane|TxcwFd|As|Ps;
+	}
+
+	csr32w(ctlr, Txcw, ctlr->txcw);
+
+	csr32w(ctlr, Ctrlext, (te & 0x00f0)<<4);
+
+	csr32w(ctlr, Tctl, csr32r(ctlr, Tctl)|(64<<ColdSHIFT));
+
+	te = ctlr->eeprom[Icw1];
+	ctl = ((te & 0x01E0)<<17)|(te & 0x0010)<<3;
+	csr32w(ctlr, Ctrl, ctl);
+
+	delay(10);
+
+	/*
+	 * Flow control - values from the datasheet.
+	 */
+	csr32w(ctlr, Fcal, 0x00C28001);
+	csr32w(ctlr, Fcah, 0x00000100);
+	csr32w(ctlr, Fct, 0x00008808);
+	csr32w(ctlr, Fcttv, 0x00000100);
+
+	csr32w(ctlr, Fcrtl, ctlr->fcrtl);
+	csr32w(ctlr, Fcrth, ctlr->fcrth);
+
+	ctlr->im = Lsc;
+	gc82543checklink(ctlr);
+
+	return 0;
+}
+
+static void
+gc82543watchdog(void* arg)
+{
+	Ether *edev;
+	Ctlr *ctlr;
+
+	edev = arg;
+	for(;;){
+		tsleep(&up->sleep, return0, 0, 1000);
+
+		ctlr = edev->ctlr;
+		if(ctlr == nil){
+			print("%s: exiting\n", up->text);
+			pexit("disabled", 0);
+		}
+
+		gc82543checklink(ctlr);
+		gc82543replenish(ctlr);
+	}
+}
+
+static void
+gc82543pci(void)
+{
+	int cls;
+	void *mem;
+	Pcidev *p;
+	Ctlr *ctlr;
+
+	p = nil;
+	while(p = pcimatch(p, 0, 0)){
+		if(p->ccrb != 0x02 || p->ccru != 0)
+			continue;
+
+		switch((p->did<<16)|p->vid){
+		case (0x1000<<16)|0x8086:	/* LSI L2A1157 (82542) */
+		case (0x1004<<16)|0x8086:	/* Intel PRO/1000 T */
+		case (0x1008<<16)|0x8086:	/* Intel PRO/1000 XT */
+		default:
+			continue;
+		case (0x1001<<16)|0x8086:	/* Intel PRO/1000 F */
+			break;
+		}
+
+		mem = vmap(p->mem[0].bar & ~0x0F, p->mem[0].size);
+		if(mem == 0){
+			print("gc82543: can't map %8.8luX\n", p->mem[0].bar);
+			continue;
+		}
+		cls = pcicfgr8(p, PciCLS);
+		switch(cls){
+			case 0x00:
+			case 0xFF:
+				print("82543gc: unusable cache line size\n");
+				continue;
+			case 0x08:
+				break;
+			default:
+				print("82543gc: cache line size %d, expected 32\n",
+					cls*4);
+		}
+		ctlr = malloc(sizeof(Ctlr));
+		ctlr->port = p->mem[0].bar & ~0x0F;
+		ctlr->pcidev = p;
+		ctlr->id = (p->did<<16)|p->vid;
+		ctlr->nic = mem;
+
+		if(gc82543reset(ctlr)){
+			free(ctlr);
+			continue;
+		}
+
+		if(gc82543ctlrhead != nil)
+			gc82543ctlrtail->next = ctlr;
+		else
+			gc82543ctlrhead = ctlr;
+		gc82543ctlrtail = ctlr;
+	}
+}
+
+static int
+gc82543pnp(Ether* edev)
+{
+	int i;
+	Ctlr *ctlr;
+	uchar ea[Eaddrlen];
+
+	if(gc82543ctlrhead == nil)
+		gc82543pci();
+
+	/*
+	 * Any adapter matches if no edev->port is supplied,
+	 * otherwise the ports must match.
+	 */
+	for(ctlr = gc82543ctlrhead; ctlr != nil; ctlr = ctlr->next){
+		if(ctlr->active)
+			continue;
+		if(edev->port == 0 || edev->port == ctlr->port){
+			ctlr->active = 1;
+			break;
+		}
+	}
+	if(ctlr == nil)
+		return -1;
+
+	edev->ctlr = ctlr;
+	edev->port = ctlr->port;
+	edev->irq = ctlr->pcidev->intl;
+	edev->tbdf = ctlr->pcidev->tbdf;
+	edev->mbps = 1000;
+
+	/*
+	 * Check if the adapter's station address is to be overridden.
+	 * If not, read it from the EEPROM and set in ether->ea prior to
+	 * loading the station address in the hardware.
+	 */
+	memset(ea, 0, Eaddrlen);
+	if(memcmp(ea, edev->ea, Eaddrlen) == 0){
+		for(i = Ea; i < Eaddrlen/2; i++){
+			edev->ea[2*i] = ctlr->eeprom[i];
+			edev->ea[2*i+1] = ctlr->eeprom[i]>>8;
+		}
+	}
+	gc82543init(edev);
+
+	/*
+	 * Linkage to the generic ethernet driver.
+	 */
+	edev->attach = gc82543attach;
+	edev->transmit = gc82543transmit;
+	edev->interrupt = gc82543interrupt;
+	edev->ifstat = gc82543ifstat;
+	edev->shutdown = gc82543shutdown;
+	edev->ctl = gc82543ctl;
+	edev->arg = edev;
+	edev->promiscuous = gc82543promiscuous;
+	edev->multicast = gc82543multicast;
+
+	return 0;
+}
+
+void
+ether82543gclink(void)
+{
+	addethercard("82543GC", gc82543pnp);
+}
--- /dev/null
+++ b/os/pc/ether82557.c
@@ -1,0 +1,1327 @@
+/*
+ * Intel 82557 Fast Ethernet PCI Bus LAN Controller
+ * as found on the Intel EtherExpress PRO/100B. This chip is full
+ * of smarts, unfortunately they're not all in the right place.
+ * To do:
+ *	the PCI scanning code could be made common to other adapters;
+ *	auto-negotiation, full-duplex;
+ *	optionally use memory-mapped registers;
+ *	detach for PCI reset problems (also towards loadable drivers).
+ */
+#include "u.h"
+#include "../port/lib.h"
+#include "mem.h"
+#include "dat.h"
+#include "fns.h"
+#include "io.h"
+#include "../port/error.h"
+#include "../port/netif.h"
+
+#include "etherif.h"
+
+enum {
+	Nrfd		= 64,		/* receive frame area */
+	Ncb		= 64,		/* maximum control blocks queued */
+
+	NullPointer	= 0xFFFFFFFF,	/* 82557 NULL pointer */
+};
+
+enum {					/* CSR */
+	Status		= 0x00,		/* byte or word (word includes Ack) */
+	Ack		= 0x01,		/* byte */
+	CommandR	= 0x02,		/* byte or word (word includes Interrupt) */
+	Interrupt	= 0x03,		/* byte */
+	General		= 0x04,		/* dword */
+	Port		= 0x08,		/* dword */
+	Fcr		= 0x0C,		/* Flash control register */
+	Ecr		= 0x0E,		/* EEPROM control register */
+	Mcr		= 0x10,		/* MDI control register */
+	Gstatus		= 0x1D,		/* General status register */
+};
+
+enum {					/* Status */
+	RUidle		= 0x0000,
+	RUsuspended	= 0x0004,
+	RUnoresources	= 0x0008,
+	RUready		= 0x0010,
+	RUrbd		= 0x0020,	/* bit */
+	RUstatus	= 0x003F,	/* mask */
+
+	CUidle		= 0x0000,
+	CUsuspended	= 0x0040,
+	CUactive	= 0x0080,
+	CUstatus	= 0x00C0,	/* mask */
+
+	StatSWI		= 0x0400,	/* SoftWare generated Interrupt */
+	StatMDI		= 0x0800,	/* MDI r/w done */
+	StatRNR		= 0x1000,	/* Receive unit Not Ready */
+	StatCNA		= 0x2000,	/* Command unit Not Active (Active->Idle) */
+	StatFR		= 0x4000,	/* Finished Receiving */
+	StatCX		= 0x8000,	/* Command eXecuted */
+	StatTNO		= 0x8000,	/* Transmit NOT OK */
+};
+
+enum {					/* Command (byte) */
+	CUnop		= 0x00,
+	CUstart		= 0x10,
+	CUresume	= 0x20,
+	LoadDCA		= 0x40,		/* Load Dump Counters Address */
+	DumpSC		= 0x50,		/* Dump Statistical Counters */
+	LoadCUB		= 0x60,		/* Load CU Base */
+	ResetSA		= 0x70,		/* Dump and Reset Statistical Counters */
+
+	RUstart		= 0x01,
+	RUresume	= 0x02,
+	RUabort		= 0x04,
+	LoadHDS		= 0x05,		/* Load Header Data Size */
+	LoadRUB		= 0x06,		/* Load RU Base */
+	RBDresume	= 0x07,		/* Resume frame reception */
+};
+
+enum {					/* Interrupt (byte) */
+	InterruptM	= 0x01,		/* interrupt Mask */
+	InterruptSI	= 0x02,		/* Software generated Interrupt */
+};
+
+enum {					/* Ecr */
+	EEsk		= 0x01,		/* serial clock */
+	EEcs		= 0x02,		/* chip select */
+	EEdi		= 0x04,		/* serial data in */
+	EEdo		= 0x08,		/* serial data out */
+
+	EEstart		= 0x04,		/* start bit */
+	EEread		= 0x02,		/* read opcode */
+};
+
+enum {					/* Mcr */
+	MDIread		= 0x08000000,	/* read opcode */
+	MDIwrite	= 0x04000000,	/* write opcode */
+	MDIready	= 0x10000000,	/* ready bit */
+	MDIie		= 0x20000000,	/* interrupt enable */
+};
+
+typedef struct Rfd {
+	int	field;
+	ulong	link;
+	ulong	rbd;
+	ushort	count;
+	ushort	size;
+
+	uchar	data[1700];
+} Rfd;
+
+enum {					/* field */
+	RfdCollision	= 0x00000001,
+	RfdIA		= 0x00000002,	/* IA match */
+	RfdRxerr	= 0x00000010,	/* PHY character error */
+	RfdType		= 0x00000020,	/* Type frame */
+	RfdRunt		= 0x00000080,
+	RfdOverrun	= 0x00000100,
+	RfdBuffer	= 0x00000200,
+	RfdAlignment	= 0x00000400,
+	RfdCRC		= 0x00000800,
+
+	RfdOK		= 0x00002000,	/* frame received OK */
+	RfdC		= 0x00008000,	/* reception Complete */
+	RfdSF		= 0x00080000,	/* Simplified or Flexible (1) Rfd */
+	RfdH		= 0x00100000,	/* Header RFD */
+
+	RfdI		= 0x20000000,	/* Interrupt after completion */
+	RfdS		= 0x40000000,	/* Suspend after completion */
+	RfdEL		= 0x80000000,	/* End of List */
+};
+
+enum {					/* count */
+	RfdF		= 0x4000,
+	RfdEOF		= 0x8000,
+};
+
+typedef struct Cb Cb;
+typedef struct Cb {
+	ushort	status;
+	ushort	command;
+	ulong	link;
+	union {
+		uchar	data[24];	/* CbIAS + CbConfigure */
+		struct {
+			ulong	tbd;
+			ushort	count;
+			uchar	threshold;
+			uchar	number;
+
+			ulong	tba;
+			ushort	tbasz;
+			ushort	pad;
+		};
+	};
+
+	Block*	bp;
+	Cb*	next;
+} Cb;
+
+enum {					/* action command */
+	CbU		= 0x1000,	/* transmit underrun */
+	CbOK		= 0x2000,	/* DMA completed OK */
+	CbC		= 0x8000,	/* execution Complete */
+
+	CbNOP		= 0x0000,
+	CbIAS		= 0x0001,	/* Individual Address Setup */
+	CbConfigure	= 0x0002,
+	CbMAS		= 0x0003,	/* Multicast Address Setup */
+	CbTransmit	= 0x0004,
+	CbDump		= 0x0006,
+	CbDiagnose	= 0x0007,
+	CbCommand	= 0x0007,	/* mask */
+
+	CbSF		= 0x0008,	/* Flexible-mode CbTransmit */
+
+	CbI		= 0x2000,	/* Interrupt after completion */
+	CbS		= 0x4000,	/* Suspend after completion */
+	CbEL		= 0x8000,	/* End of List */
+};
+
+enum {					/* CbTransmit count */
+	CbEOF		= 0x8000,
+};
+
+typedef struct Ctlr Ctlr;
+typedef struct Ctlr {
+	Lock	slock;			/* attach */
+	int	state;
+
+	int	port;
+	Pcidev*	pcidev;
+	Ctlr*	next;
+	int	active;
+
+	int	eepromsz;		/* address size in bits */
+	ushort*	eeprom;
+
+	Lock	miilock;
+
+	int	tick;
+
+	Lock	rlock;			/* registers */
+	int	command;		/* last command issued */
+
+	Block*	rfdhead;		/* receive side */
+	Block*	rfdtail;
+	int	nrfd;
+
+	Lock	cblock;			/* transmit side */
+	int	action;
+	int	nop;
+	uchar	configdata[24];
+	int	threshold;
+	int	ncb;
+	Cb*	cbr;
+	Cb*	cbhead;
+	Cb*	cbtail;
+	int	cbq;
+	int	cbqmax;
+	int	cbqmaxhw;
+
+	Lock	dlock;			/* dump statistical counters */
+	ulong	dump[17];
+} Ctlr;
+
+static Ctlr* ctlrhead;
+static Ctlr* ctlrtail;
+
+static uchar configdata[24] = {
+	0x16,				/* byte count */
+	0x08,				/* Rx/Tx FIFO limit */
+	0x00,				/* adaptive IFS */
+	0x00,	
+	0x00,				/* Rx DMA maximum byte count */
+//	0x80,				/* Tx DMA maximum byte count */
+	0x00,				/* Tx DMA maximum byte count */
+	0x32,				/* !late SCB, CNA interrupts */
+	0x03,				/* discard short Rx frames */
+	0x00,				/* 503/MII */
+
+	0x00,	
+	0x2E,				/* normal operation, NSAI */
+	0x00,				/* linear priority */
+	0x60,				/* inter-frame spacing */
+	0x00,	
+	0xF2,	
+	0xC8,				/* 503, promiscuous mode off */
+	0x00,	
+	0x40,	
+	0xF3,				/* transmit padding enable */
+	0x80,				/* full duplex pin enable */
+	0x3F,				/* no Multi IA */
+	0x05,				/* no Multi Cast ALL */
+};
+
+#define csr8r(c, r)	(inb((c)->port+(r)))
+#define csr16r(c, r)	(ins((c)->port+(r)))
+#define csr32r(c, r)	(inl((c)->port+(r)))
+#define csr8w(c, r, b)	(outb((c)->port+(r), (int)(b)))
+#define csr16w(c, r, w)	(outs((c)->port+(r), (ushort)(w)))
+#define csr32w(c, r, l)	(outl((c)->port+(r), (ulong)(l)))
+
+static void
+command(Ctlr* ctlr, int c, int v)
+{
+	int timeo;
+
+	ilock(&ctlr->rlock);
+
+	/*
+	 * Only back-to-back CUresume can be done
+	 * without waiting for any previous command to complete.
+	 * This should be the common case.
+	 * Unfortunately there's a chip errata where back-to-back
+	 * CUresumes can be lost, the fix is to always wait.
+	if(c == CUresume && ctlr->command == CUresume){
+		csr8w(ctlr, CommandR, c);
+		iunlock(&ctlr->rlock);
+		return;
+	}
+	 */
+
+	for(timeo = 0; timeo < 100; timeo++){
+		if(!csr8r(ctlr, CommandR))
+			break;
+		microdelay(1);
+	}
+	if(timeo >= 100){
+		ctlr->command = -1;
+		iunlock(&ctlr->rlock);
+		iprint("i82557: command %#ux %#ux timeout\n", c, v);
+		return;
+	}
+
+	switch(c){
+
+	case CUstart:
+	case LoadDCA:
+	case LoadCUB:
+	case RUstart:
+	case LoadHDS:
+	case LoadRUB:
+		csr32w(ctlr, General, v);
+		break;
+
+	/*
+	case CUnop:
+	case CUresume:
+	case DumpSC:
+	case ResetSA:
+	case RUresume:
+	case RUabort:
+	 */
+	default:
+		break;
+	}
+	csr8w(ctlr, CommandR, c);
+	ctlr->command = c;
+
+	iunlock(&ctlr->rlock);
+}
+
+static Block*
+rfdalloc(ulong link)
+{
+	Block *bp;
+	Rfd *rfd;
+
+	if(bp = iallocb(sizeof(Rfd))){
+		rfd = (Rfd*)bp->rp;
+		rfd->field = 0;
+		rfd->link = link;
+		rfd->rbd = NullPointer;
+		rfd->count = 0;
+		rfd->size = sizeof(Etherpkt);
+	}
+
+	return bp;
+}
+
+static void
+watchdog(void* arg)
+{
+	Ether *ether;
+	Ctlr *ctlr;
+	static void txstart(Ether*);
+
+	ether = arg;
+	for(;;){
+		tsleep(&up->sleep, return0, 0, 4000);
+
+		/*
+		 * Hmmm. This doesn't seem right. Currently
+		 * the device can't be disabled but it may be in
+		 * the future.
+		 */
+		ctlr = ether->ctlr;
+		if(ctlr == nil || ctlr->state == 0){
+			print("%s: exiting\n", up->text);
+			pexit("disabled", 0);
+		}
+
+		ilock(&ctlr->cblock);
+		if(ctlr->tick++){
+			ctlr->action = CbMAS;
+			txstart(ether);
+		}
+		iunlock(&ctlr->cblock);
+	}
+}
+
+static void
+attach(Ether* ether)
+{
+	Ctlr *ctlr;
+	char name[KNAMELEN];
+
+	ctlr = ether->ctlr;
+	lock(&ctlr->slock);
+	if(ctlr->state == 0){
+		ilock(&ctlr->rlock);
+		csr8w(ctlr, Interrupt, 0);
+		iunlock(&ctlr->rlock);
+		command(ctlr, RUstart, PADDR(ctlr->rfdhead->rp));
+		ctlr->state = 1;
+
+		/*
+		 * Start the watchdog timer for the receive lockup errata
+		 * unless the EEPROM compatibility word indicates it may be
+		 * omitted.
+		 */
+		if((ctlr->eeprom[0x03] & 0x0003) != 0x0003){
+			snprint(name, KNAMELEN, "#l%dwatchdog", ether->ctlrno);
+			kproc(name, watchdog, ether, 0);
+		}
+	}
+	unlock(&ctlr->slock);
+}
+
+static long
+ifstat(Ether* ether, void* a, long n, ulong offset)
+{
+	char *p;
+	int i, len, phyaddr;
+	Ctlr *ctlr;
+	ulong dump[17];
+
+	ctlr = ether->ctlr;
+	lock(&ctlr->dlock);
+
+	/*
+	 * Start the command then
+	 * wait for completion status,
+	 * should be 0xA005.
+	 */
+	ctlr->dump[16] = 0;
+	command(ctlr, DumpSC, 0);
+	while(ctlr->dump[16] == 0)
+		;
+
+	ether->oerrs = ctlr->dump[1]+ctlr->dump[2]+ctlr->dump[3];
+	ether->crcs = ctlr->dump[10];
+	ether->frames = ctlr->dump[11];
+	ether->buffs = ctlr->dump[12]+ctlr->dump[15];
+	ether->overflows = ctlr->dump[13];
+
+	if(n == 0){
+		unlock(&ctlr->dlock);
+		return 0;
+	}
+
+	memmove(dump, ctlr->dump, sizeof(dump));
+	unlock(&ctlr->dlock);
+
+	p = malloc(READSTR);
+	len = snprint(p, READSTR, "transmit good frames: %lud\n", dump[0]);
+	len += snprint(p+len, READSTR-len, "transmit maximum collisions errors: %lud\n", dump[1]);
+	len += snprint(p+len, READSTR-len, "transmit late collisions errors: %lud\n", dump[2]);
+	len += snprint(p+len, READSTR-len, "transmit underrun errors: %lud\n", dump[3]);
+	len += snprint(p+len, READSTR-len, "transmit lost carrier sense: %lud\n", dump[4]);
+	len += snprint(p+len, READSTR-len, "transmit deferred: %lud\n", dump[5]);
+	len += snprint(p+len, READSTR-len, "transmit single collisions: %lud\n", dump[6]);
+	len += snprint(p+len, READSTR-len, "transmit multiple collisions: %lud\n", dump[7]);
+	len += snprint(p+len, READSTR-len, "transmit total collisions: %lud\n", dump[8]);
+	len += snprint(p+len, READSTR-len, "receive good frames: %lud\n", dump[9]);
+	len += snprint(p+len, READSTR-len, "receive CRC errors: %lud\n", dump[10]);
+	len += snprint(p+len, READSTR-len, "receive alignment errors: %lud\n", dump[11]);
+	len += snprint(p+len, READSTR-len, "receive resource errors: %lud\n", dump[12]);
+	len += snprint(p+len, READSTR-len, "receive overrun errors: %lud\n", dump[13]);
+	len += snprint(p+len, READSTR-len, "receive collision detect errors: %lud\n", dump[14]);
+	len += snprint(p+len, READSTR-len, "receive short frame errors: %lud\n", dump[15]);
+	len += snprint(p+len, READSTR-len, "nop: %d\n", ctlr->nop);
+	if(ctlr->cbqmax > ctlr->cbqmaxhw)
+		ctlr->cbqmaxhw = ctlr->cbqmax;
+	len += snprint(p+len, READSTR-len, "cbqmax: %d\n", ctlr->cbqmax);
+	ctlr->cbqmax = 0;
+	len += snprint(p+len, READSTR-len, "threshold: %d\n", ctlr->threshold);
+
+	len += snprint(p+len, READSTR-len, "eeprom:");
+	for(i = 0; i < (1<<ctlr->eepromsz); i++){
+		if(i && ((i & 0x07) == 0))
+			len += snprint(p+len, READSTR-len, "\n       ");
+		len += snprint(p+len, READSTR-len, " %4.4ux", ctlr->eeprom[i]);
+	}
+
+	if((ctlr->eeprom[6] & 0x1F00) && !(ctlr->eeprom[6] & 0x8000)){
+		phyaddr = ctlr->eeprom[6] & 0x00FF;
+		len += snprint(p+len, READSTR-len, "\nphy %2d:", phyaddr);
+		for(i = 0; i < 6; i++){
+			static int miir(Ctlr*, int, int);
+
+			len += snprint(p+len, READSTR-len, " %4.4ux",
+				miir(ctlr, phyaddr, i));
+		}
+	}
+
+	snprint(p+len, READSTR-len, "\n");
+	n = readstr(offset, a, n, p);
+	free(p);
+
+	return n;
+}
+
+static void
+txstart(Ether* ether)
+{
+	Ctlr *ctlr;
+	Block *bp;
+	Cb *cb;
+
+	ctlr = ether->ctlr;
+	while(ctlr->cbq < (ctlr->ncb-1)){
+		cb = ctlr->cbhead->next;
+		if(ctlr->action == 0){
+			bp = qget(ether->oq);
+			if(bp == nil)
+				break;
+
+			cb->command = CbS|CbSF|CbTransmit;
+			cb->tbd = PADDR(&cb->tba);
+			cb->count = 0;
+			cb->threshold = ctlr->threshold;
+			cb->number = 1;
+			cb->tba = PADDR(bp->rp);
+			cb->bp = bp;
+			cb->tbasz = BLEN(bp);
+		}
+		else if(ctlr->action == CbConfigure){
+			cb->command = CbS|CbConfigure;
+			memmove(cb->data, ctlr->configdata, sizeof(ctlr->configdata));
+			ctlr->action = 0;
+		}
+		else if(ctlr->action == CbIAS){
+			cb->command = CbS|CbIAS;
+			memmove(cb->data, ether->ea, Eaddrlen);
+			ctlr->action = 0;
+		}
+		else if(ctlr->action == CbMAS){
+			cb->command = CbS|CbMAS;
+			memset(cb->data, 0, sizeof(cb->data));
+			ctlr->action = 0;
+		}
+		else{
+			print("#l%d: action %#ux\n", ether->ctlrno, ctlr->action);
+			ctlr->action = 0;
+			break;
+		}
+		cb->status = 0;
+
+		coherence();
+		ctlr->cbhead->command &= ~CbS;
+		ctlr->cbhead = cb;
+		ctlr->cbq++;
+	}
+
+	/*
+	 * Workaround for some broken HUB chips
+	 * when connected at 10Mb/s half-duplex.
+	 */
+	if(ctlr->nop){
+		command(ctlr, CUnop, 0);
+		microdelay(1);
+	}
+	command(ctlr, CUresume, 0);
+
+	if(ctlr->cbq > ctlr->cbqmax)
+		ctlr->cbqmax = ctlr->cbq;
+}
+
+static void
+configure(Ether* ether, int promiscuous)
+{
+	Ctlr *ctlr;
+
+	ctlr = ether->ctlr;
+	ilock(&ctlr->cblock);
+	if(promiscuous){
+		ctlr->configdata[6] |= 0x80;		/* Save Bad Frames */
+		//ctlr->configdata[6] &= ~0x40;		/* !Discard Overrun Rx Frames */
+		ctlr->configdata[7] &= ~0x01;		/* !Discard Short Rx Frames */
+		ctlr->configdata[15] |= 0x01;		/* Promiscuous mode */
+		ctlr->configdata[18] &= ~0x01;		/* (!Padding enable?), !stripping enable */
+		ctlr->configdata[21] |= 0x08;		/* Multi Cast ALL */
+	}
+	else{
+		ctlr->configdata[6] &= ~0x80;
+		//ctlr->configdata[6] |= 0x40;
+		ctlr->configdata[7] |= 0x01;
+		ctlr->configdata[15] &= ~0x01;
+		ctlr->configdata[18] |= 0x01;		/* 0x03? */
+		ctlr->configdata[21] &= ~0x08;
+	}
+	ctlr->action = CbConfigure;
+	txstart(ether);
+	iunlock(&ctlr->cblock);
+}
+
+static void
+promiscuous(void* arg, int on)
+{
+	configure(arg, on);
+}
+
+static void
+multicast(void* arg, uchar *addr, int on)
+{
+	USED(addr, on);
+	configure(arg, 1);
+}
+
+static void
+transmit(Ether* ether)
+{
+	Ctlr *ctlr;
+
+	ctlr = ether->ctlr;
+	ilock(&ctlr->cblock);
+	txstart(ether);
+	iunlock(&ctlr->cblock);
+}
+
+static void
+receive(Ether* ether)
+{
+	Rfd *rfd;
+	Ctlr *ctlr;
+	int count;
+	Block *bp, *pbp, *xbp;
+
+	ctlr = ether->ctlr;
+	bp = ctlr->rfdhead;
+	for(rfd = (Rfd*)bp->rp; rfd->field & RfdC; rfd = (Rfd*)bp->rp){
+		/*
+		 * If it's an OK receive frame
+		 * 1) save the count 
+		 * 2) if it's small, try to allocate a block and copy
+		 *    the data, then adjust the necessary fields for reuse;
+		 * 3) if it's big, try to allocate a new Rfd and if
+		 *    successful
+		 *	adjust the received buffer pointers for the
+		 *	  actual data received;
+		 *	initialise the replacement buffer to point to
+		 *	  the next in the ring;
+		 *	initialise bp to point to the replacement;
+		 * 4) if there's a good packet, pass it on for disposal.
+		 */
+		if(rfd->field & RfdOK){
+			pbp = nil;
+			count = rfd->count & 0x3FFF;
+			if((count < ETHERMAXTU/4) && (pbp = iallocb(count))){
+				memmove(pbp->rp, bp->rp+offsetof(Rfd, data[0]), count);
+				pbp->wp = pbp->rp + count;
+
+				rfd->count = 0;
+				rfd->field = 0;
+			}
+			else if(xbp = rfdalloc(rfd->link)){
+				bp->rp += offsetof(Rfd, data[0]);
+				bp->wp = bp->rp + count;
+
+				xbp->next = bp->next;
+				bp->next = 0;
+
+				pbp = bp;
+				bp = xbp;
+			}
+			if(pbp != nil)
+				etheriq(ether, pbp, 1);
+		}
+		else{
+			rfd->count = 0;
+			rfd->field = 0;
+		}
+
+		/*
+		 * The ring tail pointer follows the head with with one
+		 * unused buffer in between to defeat hardware prefetch;
+		 * once the tail pointer has been bumped on to the next
+		 * and the new tail has the Suspend bit set, it can be
+		 * removed from the old tail buffer.
+		 * As a replacement for the current head buffer may have
+		 * been allocated above, ensure that the new tail points
+		 * to it (next and link).
+		 */
+		rfd = (Rfd*)ctlr->rfdtail->rp;
+		ctlr->rfdtail = ctlr->rfdtail->next;
+		ctlr->rfdtail->next = bp;
+		((Rfd*)ctlr->rfdtail->rp)->link = PADDR(bp->rp);
+		((Rfd*)ctlr->rfdtail->rp)->field |= RfdS;
+		coherence();
+		rfd->field &= ~RfdS;
+
+		/*
+		 * Finally done with the current (possibly replaced)
+		 * head, move on to the next and maintain the sentinel
+		 * between tail and head.
+		 */
+		ctlr->rfdhead = bp->next;
+		bp = ctlr->rfdhead;
+	}
+}
+
+static void
+interrupt(Ureg*, void* arg)
+{
+	Cb* cb;
+	Ctlr *ctlr;
+	Ether *ether;
+	int status;
+
+	ether = arg;
+	ctlr = ether->ctlr;
+
+	for(;;){
+		ilock(&ctlr->rlock);
+		status = csr16r(ctlr, Status);
+		csr8w(ctlr, Ack, (status>>8) & 0xFF);
+		iunlock(&ctlr->rlock);
+
+		if(!(status & (StatCX|StatFR|StatCNA|StatRNR|StatMDI|StatSWI)))
+			break;
+
+		/*
+		 * If the watchdog timer for the receiver lockup errata is running,
+		 * let it know the receiver is active.
+		 */
+		if(status & (StatFR|StatRNR)){
+			ilock(&ctlr->cblock);
+			ctlr->tick = 0;
+			iunlock(&ctlr->cblock);
+		}
+
+		if(status & StatFR){
+			receive(ether);
+			status &= ~StatFR;
+		}
+
+		if(status & StatRNR){
+			command(ctlr, RUresume, 0);
+			status &= ~StatRNR;
+		}
+
+		if(status & StatCNA){
+			ilock(&ctlr->cblock);
+
+			cb = ctlr->cbtail;
+			while(ctlr->cbq){
+				if(!(cb->status & CbC))
+					break;
+				if(cb->bp){
+					freeb(cb->bp);
+					cb->bp = nil;
+				}
+				if((cb->status & CbU) && ctlr->threshold < 0xE0)
+					ctlr->threshold++;
+
+				ctlr->cbq--;
+				cb = cb->next;
+			}
+			ctlr->cbtail = cb;
+
+			txstart(ether);
+			iunlock(&ctlr->cblock);
+
+			status &= ~StatCNA;
+		}
+
+		if(status & (StatCX|StatFR|StatCNA|StatRNR|StatMDI|StatSWI))
+			panic("#l%d: status %#ux\n", ether->ctlrno, status);
+	}
+}
+
+static void
+ctlrinit(Ctlr* ctlr)
+{
+	int i;
+	Block *bp;
+	Rfd *rfd;
+	ulong link;
+
+	/*
+	 * Create the Receive Frame Area (RFA) as a ring of allocated
+	 * buffers.
+	 * A sentinel buffer is maintained between the last buffer in
+	 * the ring (marked with RfdS) and the head buffer to defeat the
+	 * hardware prefetch of the next RFD and allow dynamic buffer
+	 * allocation.
+	 */
+	link = NullPointer;
+	for(i = 0; i < Nrfd; i++){
+		bp = rfdalloc(link);
+		if(ctlr->rfdhead == nil)
+			ctlr->rfdtail = bp;
+		bp->next = ctlr->rfdhead;
+		ctlr->rfdhead = bp;
+		link = PADDR(bp->rp);
+	}
+	ctlr->rfdtail->next = ctlr->rfdhead;
+	rfd = (Rfd*)ctlr->rfdtail->rp;
+	rfd->link = PADDR(ctlr->rfdhead->rp);
+	rfd->field |= RfdS;
+	ctlr->rfdhead = ctlr->rfdhead->next;
+
+	/*
+	 * Create a ring of control blocks for the
+	 * transmit side.
+	 */
+	ilock(&ctlr->cblock);
+	ctlr->cbr = malloc(ctlr->ncb*sizeof(Cb));
+	for(i = 0; i < ctlr->ncb; i++){
+		ctlr->cbr[i].status = CbC|CbOK;
+		ctlr->cbr[i].command = CbS|CbNOP;
+		ctlr->cbr[i].link = PADDR(&ctlr->cbr[NEXT(i, ctlr->ncb)].status);
+		ctlr->cbr[i].next = &ctlr->cbr[NEXT(i, ctlr->ncb)];
+	}
+	ctlr->cbhead = ctlr->cbr;
+	ctlr->cbtail = ctlr->cbr;
+	ctlr->cbq = 0;
+
+	memmove(ctlr->configdata, configdata, sizeof(configdata));
+	ctlr->threshold = 80;
+	ctlr->tick = 0;
+
+	iunlock(&ctlr->cblock);
+}
+
+static int
+miir(Ctlr* ctlr, int phyadd, int regadd)
+{
+	int mcr, timo;
+
+	lock(&ctlr->miilock);
+	csr32w(ctlr, Mcr, MDIread|(phyadd<<21)|(regadd<<16));
+	mcr = 0;
+	for(timo = 64; timo; timo--){
+		mcr = csr32r(ctlr, Mcr);
+		if(mcr & MDIready)
+			break;
+		microdelay(1);
+	}
+	unlock(&ctlr->miilock);
+
+	if(mcr & MDIready)
+		return mcr & 0xFFFF;
+
+	return -1;
+}
+
+static int
+miiw(Ctlr* ctlr, int phyadd, int regadd, int data)
+{
+	int mcr, timo;
+
+	lock(&ctlr->miilock);
+	csr32w(ctlr, Mcr, MDIwrite|(phyadd<<21)|(regadd<<16)|(data & 0xFFFF));
+	mcr = 0;
+	for(timo = 64; timo; timo--){
+		mcr = csr32r(ctlr, Mcr);
+		if(mcr & MDIready)
+			break;
+		microdelay(1);
+	}
+	unlock(&ctlr->miilock);
+
+	if(mcr & MDIready)
+		return 0;
+
+	return -1;
+}
+
+static int
+hy93c46r(Ctlr* ctlr, int r)
+{
+	int data, i, op, size;
+
+	/*
+	 * Hyundai HY93C46 or equivalent serial EEPROM.
+	 * This sequence for reading a 16-bit register 'r'
+	 * in the EEPROM is taken straight from Section
+	 * 3.3.4.2 of the Intel 82557 User's Guide.
+	 */
+reread:
+	csr16w(ctlr, Ecr, EEcs);
+	op = EEstart|EEread;
+	for(i = 2; i >= 0; i--){
+		data = (((op>>i) & 0x01)<<2)|EEcs;
+		csr16w(ctlr, Ecr, data);
+		csr16w(ctlr, Ecr, data|EEsk);
+		microdelay(1);
+		csr16w(ctlr, Ecr, data);
+		microdelay(1);
+	}
+
+	/*
+	 * First time through must work out the EEPROM size.
+	 */
+	if((size = ctlr->eepromsz) == 0)
+		size = 8;
+
+	for(size = size-1; size >= 0; size--){
+		data = (((r>>size) & 0x01)<<2)|EEcs;
+		csr16w(ctlr, Ecr, data);
+		csr16w(ctlr, Ecr, data|EEsk);
+		delay(1);
+		csr16w(ctlr, Ecr, data);
+		microdelay(1);
+		if(!(csr16r(ctlr, Ecr) & EEdo))
+			break;
+	}
+
+	data = 0;
+	for(i = 15; i >= 0; i--){
+		csr16w(ctlr, Ecr, EEcs|EEsk);
+		microdelay(1);
+		if(csr16r(ctlr, Ecr) & EEdo)
+			data |= (1<<i);
+		csr16w(ctlr, Ecr, EEcs);
+		microdelay(1);
+	}
+
+	csr16w(ctlr, Ecr, 0);
+
+	if(ctlr->eepromsz == 0){
+		ctlr->eepromsz = 8-size;
+		ctlr->eeprom = malloc((1<<ctlr->eepromsz)*sizeof(ushort));
+		goto reread;
+	}
+
+	return data;
+}
+
+static void
+i82557pci(void)
+{
+	Pcidev *p;
+	Ctlr *ctlr;
+	int i, nop, port;
+
+	p = nil;
+	nop = 0;
+	while(p = pcimatch(p, 0x8086, 0)){
+		switch(p->did){
+		default:
+			continue;
+		case 0x1031:		/* Intel 82562EM */
+		case 0x1050:		/* Intel 82562EZ */
+		case 0x1039:		/* Intel 82801BD PRO/100 VE */
+		case 0x103A:		/* Intel 82562 PRO/100 VE */
+		case 0x103D:		/* Intel 82562 PRO/100 VE */
+		case 0x1064:		/* Intel 82562 PRO/100 VE */
+		case 0x2449:		/* Intel 82562ET */
+			nop = 1;
+			/*FALLTHROUGH*/
+		case 0x1209:		/* Intel 82559ER */
+		case 0x1229:		/* Intel 8255[789] */
+		case 0x1030:		/* Intel 82559 InBusiness 10/100  */
+			break;
+		}
+
+		if(pcigetpms(p) > 0){
+			pcisetpms(p, 0);
+	
+			for(i = 0; i < 6; i++)
+				pcicfgw32(p, PciBAR0+i*4, p->mem[i].bar);
+			pcicfgw8(p, PciINTL, p->intl);
+			pcicfgw8(p, PciLTR, p->ltr);
+			pcicfgw8(p, PciCLS, p->cls);
+			pcicfgw16(p, PciPCR, p->pcr);
+		}
+
+		/*
+		 * bar[0] is the memory-mapped register address (4KB),
+		 * bar[1] is the I/O port register address (32 bytes) and
+		 * bar[2] is for the flash ROM (1MB).
+		 */
+		port = p->mem[1].bar & ~0x01;
+		if(ioalloc(port, p->mem[1].size, 0, "i82557") < 0){
+			print("i82557: port %#ux in use\n", port);
+			continue;
+		}
+
+		ctlr = malloc(sizeof(Ctlr));
+		ctlr->port = port;
+		ctlr->pcidev = p;
+		ctlr->nop = nop;
+
+		if(ctlrhead != nil)
+			ctlrtail->next = ctlr;
+		else
+			ctlrhead = ctlr;
+		ctlrtail = ctlr;
+
+		pcisetbme(p);
+	}
+}
+
+static char* mediatable[9] = {
+	"10BASE-T",				/* TP */
+	"10BASE-2",				/* BNC */
+	"10BASE-5",				/* AUI */
+	"100BASE-TX",
+	"10BASE-TFD",
+	"100BASE-TXFD",
+	"100BASE-T4",
+	"100BASE-FX",
+	"100BASE-FXFD",
+};
+
+static int
+scanphy(Ctlr* ctlr)
+{
+	int i, oui, x;
+
+	for(i = 0; i < 32; i++){
+		if((oui = miir(ctlr, i, 2)) == -1 || oui == 0 || oui == 0xFFFF)
+			continue;
+		oui <<= 6;
+		x = miir(ctlr, i, 3);
+		oui |= x>>10;
+		//print("phy%d: oui %#ux reg1 %#ux\n", i, oui, miir(ctlr, i, 1));
+
+		ctlr->eeprom[6] = i;
+		if(oui == 0xAA00)
+			ctlr->eeprom[6] |= 0x07<<8;
+		else if(oui == 0x80017){
+			if(x & 0x01)
+				ctlr->eeprom[6] |= 0x0A<<8;
+			else
+				ctlr->eeprom[6] |= 0x04<<8;
+		}
+		return i;
+	}
+	return -1;
+}
+
+static void
+shutdown(Ether* ether)
+{
+	Ctlr *ctlr = ether->ctlr;
+
+print("ether82557 shutting down\n");
+	csr32w(ctlr, Port, 0);
+	delay(1);
+	csr8w(ctlr, Interrupt, InterruptM);
+}
+
+
+static int
+reset(Ether* ether)
+{
+	int anar, anlpar, bmcr, bmsr, i, k, medium, phyaddr, x;
+	unsigned short sum;
+	uchar ea[Eaddrlen];
+	Ctlr *ctlr;
+
+	if(ctlrhead == nil)
+		i82557pci();
+
+	/*
+	 * Any adapter matches if no ether->port is supplied,
+	 * otherwise the ports must match.
+	 */
+	for(ctlr = ctlrhead; ctlr != nil; ctlr = ctlr->next){
+		if(ctlr->active)
+			continue;
+		if(ether->port == 0 || ether->port == ctlr->port){
+			ctlr->active = 1;
+			break;
+		}
+	}
+	if(ctlr == nil)
+		return -1;
+
+	/*
+	 * Initialise the Ctlr structure.
+	 * Perform a software reset after which should ensure busmastering
+	 * is still enabled. The EtherExpress PRO/100B appears to leave
+	 * the PCI configuration alone (see the 'To do' list above) so punt
+	 * for now.
+	 * Load the RUB and CUB registers for linear addressing (0).
+	 */
+	ether->ctlr = ctlr;
+	ether->port = ctlr->port;
+	ether->irq = ctlr->pcidev->intl;
+	ether->tbdf = ctlr->pcidev->tbdf;
+
+	ilock(&ctlr->rlock);
+	csr32w(ctlr, Port, 0);
+	delay(1);
+	csr8w(ctlr, Interrupt, InterruptM);
+	iunlock(&ctlr->rlock);
+
+	command(ctlr, LoadRUB, 0);
+	command(ctlr, LoadCUB, 0);
+	command(ctlr, LoadDCA, PADDR(ctlr->dump));
+
+	/*
+	 * Initialise the receive frame, transmit ring and configuration areas.
+	 */
+	ctlr->ncb = Ncb;
+	ctlrinit(ctlr);
+
+	/*
+	 * Read the EEPROM.
+	 * Do a dummy read first to get the size
+	 * and allocate ctlr->eeprom.
+	 */
+	hy93c46r(ctlr, 0);
+	sum = 0;
+	for(i = 0; i < (1<<ctlr->eepromsz); i++){
+		x = hy93c46r(ctlr, i);
+		ctlr->eeprom[i] = x;
+		sum += x;
+	}
+	if(sum != 0xBABA)
+		print("#l%d: EEPROM checksum - %#4.4ux\n", ether->ctlrno, sum);
+
+	/*
+	 * Eeprom[6] indicates whether there is a PHY and whether
+	 * it's not 10Mb-only, in which case use the given PHY address
+	 * to set any PHY specific options and determine the speed.
+	 * Unfortunately, sometimes the EEPROM is blank except for
+	 * the ether address and checksum; in this case look at the
+	 * controller type and if it's am 82558 or 82559 it has an
+	 * embedded PHY so scan for that.
+	 * If no PHY, assume 82503 (serial) operation.
+	 */
+	if((ctlr->eeprom[6] & 0x1F00) && !(ctlr->eeprom[6] & 0x8000))
+		phyaddr = ctlr->eeprom[6] & 0x00FF;
+	else
+	switch(ctlr->pcidev->rid){
+	case 0x01:			/* 82557 A-step */
+	case 0x02:			/* 82557 B-step */
+	case 0x03:			/* 82557 C-step */
+	default:
+		phyaddr = -1;
+		break;
+	case 0x04:			/* 82558 A-step */
+	case 0x05:			/* 82558 B-step */
+	case 0x06:			/* 82559 A-step */
+	case 0x07:			/* 82559 B-step */
+	case 0x08:			/* 82559 C-step */
+	case 0x09:			/* 82559ER A-step */
+		phyaddr = scanphy(ctlr);
+		break;
+	}
+	if(phyaddr >= 0){
+		/*
+		 * Resolve the highest common ability of the two
+		 * link partners. In descending order:
+		 *	0x0100		100BASE-TX Full Duplex
+		 *	0x0200		100BASE-T4
+		 *	0x0080		100BASE-TX
+		 *	0x0040		10BASE-T Full Duplex
+		 *	0x0020		10BASE-T
+		 */
+		anar = miir(ctlr, phyaddr, 0x04);
+		anlpar = miir(ctlr, phyaddr, 0x05) & 0x03E0;
+		anar &= anlpar;
+		bmcr = 0;
+		if(anar & 0x380)
+			bmcr = 0x2000;
+		if(anar & 0x0140)
+			bmcr |= 0x0100;
+
+		switch((ctlr->eeprom[6]>>8) & 0x001F){
+
+		case 0x04:				/* DP83840 */
+		case 0x0A:				/* DP83840A */
+			/*
+			 * The DP83840[A] requires some tweaking for
+			 * reliable operation.
+			 * The manual says bit 10 should be unconditionally
+			 * set although it supposedly only affects full-duplex
+			 * operation (an & 0x0140).
+			 */
+			x = miir(ctlr, phyaddr, 0x17) & ~0x0520;
+			x |= 0x0420;
+			for(i = 0; i < ether->nopt; i++){
+				if(cistrcmp(ether->opt[i], "congestioncontrol"))
+					continue;
+				x |= 0x0100;
+				break;
+			}
+			miiw(ctlr, phyaddr, 0x17, x);
+
+			/*
+			 * If the link partner can't autonegotiate, determine
+			 * the speed from elsewhere.
+			 */
+			if(anlpar == 0){
+				miir(ctlr, phyaddr, 0x01);
+				bmsr = miir(ctlr, phyaddr, 0x01);
+				x = miir(ctlr, phyaddr, 0x19);
+				if((bmsr & 0x0004) && !(x & 0x0040))
+					bmcr = 0x2000;
+			}
+			break;
+
+		case 0x07:				/* Intel 82555 */
+			/*
+			 * Auto-negotiation may fail if the other end is
+			 * a DP83840A and the cable is short.
+			 */
+			miir(ctlr, phyaddr, 0x01);
+			bmsr = miir(ctlr, phyaddr, 0x01);
+			if((miir(ctlr, phyaddr, 0) & 0x1000) && !(bmsr & 0x0020)){
+				miiw(ctlr, phyaddr, 0x1A, 0x2010);
+				x = miir(ctlr, phyaddr, 0);
+				miiw(ctlr, phyaddr, 0, 0x0200|x);
+				for(i = 0; i < 3000; i++){
+					delay(1);
+					if(miir(ctlr, phyaddr, 0x01) & 0x0020)
+						break;
+				}
+				miiw(ctlr, phyaddr, 0x1A, 0x2000);
+					
+				anar = miir(ctlr, phyaddr, 0x04);
+				anlpar = miir(ctlr, phyaddr, 0x05) & 0x03E0;
+				anar &= anlpar;
+				bmcr = 0;
+				if(anar & 0x380)
+					bmcr = 0x2000;
+				if(anar & 0x0140)
+					bmcr |= 0x0100;
+			}
+			break;
+		}
+
+		/*
+		 * Force speed and duplex if no auto-negotiation.
+		 */
+		if(anlpar == 0){
+			medium = -1;
+			for(i = 0; i < ether->nopt; i++){
+				for(k = 0; k < nelem(mediatable); k++){
+					if(cistrcmp(mediatable[k], ether->opt[i]))
+						continue;
+					medium = k;
+					break;
+				}
+		
+				switch(medium){
+				default:
+					break;
+
+				case 0x00:			/* 10BASE-T */
+				case 0x01:			/* 10BASE-2 */
+				case 0x02:			/* 10BASE-5 */
+					bmcr &= ~(0x2000|0x0100);
+					ctlr->configdata[19] &= ~0x40;
+					break;
+
+				case 0x03:			/* 100BASE-TX */
+				case 0x06:			/* 100BASE-T4 */
+				case 0x07:			/* 100BASE-FX */
+					ctlr->configdata[19] &= ~0x40;
+					bmcr |= 0x2000;
+					break;
+
+				case 0x04:			/* 10BASE-TFD */
+					bmcr = (bmcr & ~0x2000)|0x0100;
+					ctlr->configdata[19] |= 0x40;
+					break;
+
+				case 0x05:			/* 100BASE-TXFD */
+				case 0x08:			/* 100BASE-FXFD */
+					bmcr |= 0x2000|0x0100;
+					ctlr->configdata[19] |= 0x40;
+					break;
+				}
+			}
+			if(medium != -1)
+				miiw(ctlr, phyaddr, 0x00, bmcr);
+		}
+
+		if(bmcr & 0x2000)
+			ether->mbps = 100;
+
+		ctlr->configdata[8] = 1;
+		ctlr->configdata[15] &= ~0x80;
+	}
+	else{
+		ctlr->configdata[8] = 0;
+		ctlr->configdata[15] |= 0x80;
+	}
+
+	/*
+	 * Workaround for some broken HUB chips when connected at 10Mb/s
+	 * half-duplex.
+	 * This is a band-aid, but as there's no dynamic auto-negotiation
+	 * code at the moment, only deactivate the workaround code in txstart
+	 * if the link is 100Mb/s.
+	 */
+	if(ether->mbps != 10)
+		ctlr->nop = 0;
+
+	/*
+	 * Load the chip configuration and start it off.
+	 */
+	if(ether->oq == 0)
+		ether->oq = qopen(256*1024, Qmsg, 0, 0);
+	configure(ether, 0);
+	command(ctlr, CUstart, PADDR(&ctlr->cbr->status));
+
+	/*
+	 * Check if the adapter's station address is to be overridden.
+	 * If not, read it from the EEPROM and set in ether->ea prior to loading
+	 * the station address with the Individual Address Setup command.
+	 */
+	memset(ea, 0, Eaddrlen);
+	if(memcmp(ea, ether->ea, Eaddrlen) == 0){
+		for(i = 0; i < Eaddrlen/2; i++){
+			x = ctlr->eeprom[i];
+			ether->ea[2*i] = x;
+			ether->ea[2*i+1] = x>>8;
+		}
+	}
+
+	ilock(&ctlr->cblock);
+	ctlr->action = CbIAS;
+	txstart(ether);
+	iunlock(&ctlr->cblock);
+
+	/*
+	 * Linkage to the generic ethernet driver.
+	 */
+	ether->attach = attach;
+	ether->transmit = transmit;
+	ether->interrupt = interrupt;
+	ether->ifstat = ifstat;
+	ether->shutdown = shutdown;
+
+	ether->promiscuous = promiscuous;
+	ether->multicast = multicast;
+	ether->arg = ether;
+
+	return 0;
+}
+
+void
+ether82557link(void)
+{
+	addethercard("i82557",  reset);
+}
--- /dev/null
+++ b/os/pc/ether83815.c
@@ -1,0 +1,1119 @@
+/*
+ * National Semiconductor DP83815
+ *
+ * Supports only internal PHY and has been tested on:
+ *	Netgear FA311TX (using Netgear DS108 10/100 hub)
+ * To do:
+ *	check Ethernet address;
+ *	test autonegotiation on 10 Mbit, and 100 Mbit full duplex;
+ *	external PHY via MII (should be common code for MII);
+ *	thresholds;
+ *	ring sizing;
+ *	physical link changes/disconnect;
+ *	push initialisation back to attach.
+ *
+ * C H Forsyth, forsyth@vitanuova.com, 18th June 2001.
+ */
+
+#include "u.h"
+#include "../port/lib.h"
+#include "mem.h"
+#include "dat.h"
+#include "fns.h"
+#include "io.h"
+#include "../port/error.h"
+#include "../port/netif.h"
+
+#include "etherif.h"
+
+#define DEBUG		(0)
+#define debug		if(DEBUG)print
+
+enum {
+	Nrde		= 64,
+	Ntde		= 64,
+};
+
+#define Rbsz		ROUNDUP(sizeof(Etherpkt)+4, 4)
+
+typedef struct Des {
+	ulong	next;
+	int	cmdsts;
+	ulong	addr;
+	Block*	bp;
+} Des;
+
+enum {	/* cmdsts */
+	Own	= 1<<31,	/* set by data producer to hand to consumer */
+	More	= 1<<30,	/* more of packet in next descriptor */
+	Intr	= 1<<29,	/* interrupt when device is done with it */
+	Supcrc	= 1<<28,	/* suppress crc on transmit */
+	Inccrc	= 1<<28,	/* crc included on receive (always) */
+	Ok	= 1<<27,	/* packet ok */
+	Size	= 0xFFF,	/* packet size in bytes */
+
+	/* transmit */
+	Txa	= 1<<26,	/* transmission aborted */
+	Tfu	= 1<<25,	/* transmit fifo underrun */
+	Crs	= 1<<24,	/* carrier sense lost */
+	Td	= 1<<23,	/* transmission deferred */
+	Ed	= 1<<22,	/* excessive deferral */
+	Owc	= 1<<21,	/* out of window collision */
+	Ec	= 1<<20,	/* excessive collisions */
+	/* 19-16 collision count */
+
+	/* receive */
+	Rxa	= 1<<26,	/* receive aborted (same as Rxo) */
+	Rxo	= 1<<25,	/* receive overrun */
+	Dest	= 3<<23,	/* destination class */
+	  Drej=	0<<23,		/* packet was rejected */
+	  Duni=	1<<23,		/* unicast */
+	  Dmulti=	2<<23,		/* multicast */
+	  Dbroad=	3<<23,		/* broadcast */
+	Long = 1<<22,		/* too long packet received */
+	Runt =  1<<21,		/* packet less than 64 bytes */
+	Ise =	1<<20,		/* invalid symbol */
+	Crce =	1<<19,		/* invalid crc */
+	Fae =	1<<18,		/* frame alignment error */
+	Lbp =	1<<17,		/* loopback packet */
+	Col =	1<<16,		/* collision during receive */
+};
+
+enum {				/* PCI vendor & device IDs */
+	Nat83815	= (0x0020<<16)|0x100B,
+	SiS = 	0x1039,
+	SiS900 =	(0x0900<<16)|SiS,
+	SiS7016 =	(0x7016<<16)|SiS,
+
+	SiS630bridge	= 0x0008,
+
+	/* SiS 900 PCI revision codes */
+	SiSrev630s =	0x81,
+	SiSrev630e =	0x82,
+	SiSrev630ea1 =	0x83,
+
+	SiSeenodeaddr =	8,		/* short addr of SiS eeprom mac addr */
+	SiS630eenodeaddr =	9,	/* likewise for the 630 */
+	Nseenodeaddr =	6,		/* " for NS eeprom */
+};
+
+typedef struct Ctlr Ctlr;
+typedef struct Ctlr {
+	int	port;
+	Pcidev*	pcidev;
+	Ctlr*	next;
+	int	active;
+	int	id;			/* (pcidev->did<<16)|pcidev->vid */
+
+	ushort	srom[0xB+1];
+	uchar	sromea[Eaddrlen];	/* MAC address */
+
+	uchar	fd;			/* option or auto negotiation */
+
+	int	mbps;
+
+	Lock	lock;
+
+	Des*	rdr;			/* receive descriptor ring */
+	int	nrdr;			/* size of rdr */
+	int	rdrx;			/* index into rdr */
+
+	Lock	tlock;
+	Des*	tdr;			/* transmit descriptor ring */
+	int	ntdr;			/* size of tdr */
+	int	tdrh;			/* host index into tdr */
+	int	tdri;			/* interface index into tdr */
+	int	ntq;			/* descriptors active */
+	int	ntqmax;
+
+	ulong	rxa;			/* receive statistics */
+	ulong	rxo;
+	ulong	rlong;
+	ulong	runt;
+	ulong	ise;
+	ulong	crce;
+	ulong	fae;
+	ulong	lbp;
+	ulong	col;
+	ulong	rxsovr;
+	ulong	rxorn;
+
+	ulong	txa;			/* transmit statistics */
+	ulong	tfu;
+	ulong	crs;
+	ulong	td;
+	ulong	ed;
+	ulong	owc;
+	ulong	ec;
+	ulong	txurn;
+
+	ulong	dperr;			/* system errors */
+	ulong	rmabt;
+	ulong	rtabt;
+	ulong	sserr;
+	ulong	rxsover;
+} Ctlr;
+
+static Ctlr* ctlrhead;
+static Ctlr* ctlrtail;
+
+enum {
+	/* registers (could memory map) */
+	Rcr=	0x00,		/* command register */
+	  Rst=		1<<8,
+	  Rxr=		1<<5,	/* receiver reset */
+	  Txr=		1<<4,	/* transmitter reset */
+	  Rxd=		1<<3,	/* receiver disable */
+	  Rxe=		1<<2,	/* receiver enable */
+	  Txd=		1<<1,	/* transmitter disable */
+	  Txe=		1<<0,	/* transmitter enable */
+	Rcfg=	0x04,		/* configuration */
+	  Lnksts=	1<<31,	/* link good */
+	  Speed100=	1<<30,	/* 100 Mb/s link */
+	  Fdup=		1<<29,	/* full duplex */
+	  Pol=		1<<28,	/* polarity reversal (10baseT) */
+	  Aneg_dn=	1<<27,	/* autonegotiation done */
+	  Pint_acen=	1<<17,	/* PHY interrupt auto clear enable */
+	  Pause_adv=	1<<16,	/* advertise pause during auto neg */
+	  Paneg_ena=	1<<13,	/* auto negotiation enable */
+	  Paneg_all=	7<<13,	/* auto negotiation enable 10/100 half & full */
+	  Ext_phy=	1<<12,	/* enable MII for external PHY */
+	  Phy_rst=	1<<10,	/* reset internal PHY */
+	  Phy_dis=	1<<9,	/* disable internal PHY (eg, low power) */
+	  Req_alg=	1<<7,	/* PCI bus request: set means less aggressive */
+	  Sb=		1<<6,	/* single slot back-off not random */
+	  Pow=		1<<5,	/* out of window timer selection */
+	  Exd=		1<<4,	/* disable excessive deferral timer */
+	  Pesel=	1<<3,	/* parity error algorithm selection */
+	  Brom_dis=	1<<2,	/* disable boot rom interface */
+	  Bem=		1<<0,	/* big-endian mode */
+	Rmear=	0x08,		/* eeprom access */
+	  Mdc=		1<<6,	/* MII mangement check */
+	  Mddir=	1<<5,	/* MII management direction */
+	  Mdio=		1<<4,	/* MII mangement data */
+	  Eesel=	1<<3,	/* EEPROM chip select */
+	  Eeclk=	1<<2,	/* EEPROM clock */
+	  Eedo=		1<<1,	/* EEPROM data out (from chip) */
+	  Eedi=		1<<0,	/* EEPROM data in (to chip) */
+	Rptscr=	0x0C,		/* pci test control */
+	Risr=	0x10,		/* interrupt status */
+	  Txrcmp=	1<<25,	/* transmit reset complete */
+	  Rxrcmp=	1<<24,	/* receiver reset complete */
+	  Dperr=	1<<23,	/* detected parity error */
+	  Sserr=	1<<22,	/* signalled system error */
+	  Rmabt=	1<<21,	/* received master abort */
+	  Rtabt=	1<<20,	/* received target abort */
+	  Rxsovr=	1<<16,	/* RX status FIFO overrun */
+	  Hiberr=	1<<15,	/* high bits error set (OR of 25-16) */
+	  Phy=		1<<14,	/* PHY interrupt */
+	  Pme=		1<<13,	/* power management event (wake online) */
+	  Swi=		1<<12,	/* software interrupt */
+	  Mib=		1<<11,	/* MIB service */
+	  Txurn=	1<<10,	/* TX underrun */
+	  Txidle=	1<<9,	/* TX idle */
+	  Txerr=	1<<8,	/* TX packet error */
+	  Txdesc=	1<<7,	/* TX descriptor (with Intr bit done) */
+	  Txok=		1<<6,	/* TX ok */
+	  Rxorn=	1<<5,	/* RX overrun */
+	  Rxidle=	1<<4,	/* RX idle */
+	  Rxearly=	1<<3,	/* RX early threshold */
+	  Rxerr=	1<<2,	/* RX packet error */
+	  Rxdesc=	1<<1,	/* RX descriptor (with Intr bit done) */
+	  Rxok=		1<<0,	/* RX ok */
+	Rimr=	0x14,		/* interrupt mask */
+	Rier=	0x18,		/* interrupt enable */
+	  Ie=		1<<0,	/* interrupt enable */
+	Rtxdp=	0x20,		/* transmit descriptor pointer */
+	Rtxcfg=	0x24,		/* transmit configuration */
+	  Csi=		1<<31,	/* carrier sense ignore (needed for full duplex) */
+	  Hbi=		1<<30,	/* heartbeat ignore (needed for full duplex) */
+	  Atp=		1<<28,	/* automatic padding of runt packets */
+	  Mxdma=	7<<20,	/* maximum dma transfer field */
+	  Mxdma32=	4<<20,	/* 4x32-bit words (32 bytes) */
+	  Mxdma64=	5<<20,	/* 8x32-bit words (64 bytes) */
+	  Flth=		0x3F<<8,/* Tx fill threshold, units of 32 bytes (must be > Mxdma) */
+	  Drth=		0x3F<<0,/* Tx drain threshold (units of 32 bytes) */
+	  Flth128=	4<<8,	/* fill at 128 bytes */
+	  Drth512=	16<<0,	/* drain at 512 bytes */
+	Rrxdp=	0x30,		/* receive descriptor pointer */
+	Rrxcfg=	0x34,		/* receive configuration */
+	  Atx=		1<<28,	/* accept transmit packets (needed for full duplex) */
+	  Rdrth=	0x1F<<1,/* Rx drain threshold (units of 32 bytes) */
+	  Rdrth64=	2<<1,	/* drain at 64 bytes */
+	Rccsr=	0x3C,		/* CLKRUN control/status */
+	  Pmests=	1<<15,	/* PME status */
+	Rwcsr=	0x40,		/* wake on lan control/status */
+	Rpcr=	0x44,		/* pause control/status */
+	Rrfcr=	0x48,		/* receive filter/match control */
+	  Rfen=		1<<31,	/* receive filter enable */
+	  Aab=		1<<30,	/* accept all broadcast */
+	  Aam=		1<<29,	/* accept all multicast */
+	  Aau=		1<<28,	/* accept all unicast */
+	  Apm=		1<<27,	/* accept on perfect match */
+	  Apat=		0xF<<23,/* accept on pattern match */
+	  Aarp=		1<<22,	/* accept ARP */
+	  Mhen=		1<<21,	/* multicast hash enable */
+	  Uhen=		1<<20,	/* unicast hash enable */
+	  Ulm=		1<<19,	/* U/L bit mask */
+				/* bits 0-9 are rfaddr */
+	Rrfdr=	0x4C,		/* receive filter/match data */
+	Rbrar=	0x50,		/* boot rom address */
+	Rbrdr=	0x54,		/* boot rom data */
+	Rsrr=	0x58,		/* silicon revision */
+	Rmibc=	0x5C,		/* MIB control */
+				/* 60-78 MIB data */
+
+	/* PHY registers */
+	Rbmcr=	0x80,		/* basic mode configuration */
+	  Reset=	1<<15,
+	  Sel100=	1<<13,	/* select 100Mb/sec if no auto neg */
+	  Anena=	1<<12,	/* auto negotiation enable */
+	  Anrestart=	1<<9,	/* restart auto negotiation */
+	  Selfdx=	1<<8,	/* select full duplex if no auto neg */
+	Rbmsr=	0x84,		/* basic mode status */
+	  Ancomp=	1<<5,	/* autonegotiation complete */
+	Rphyidr1= 0x88,
+	Rphyidr2= 0x8C,
+	Ranar=	0x90,		/* autonegotiation advertisement */
+	Ranlpar= 0x94,		/* autonegotiation link partner ability */
+	Raner=	0x98,		/* autonegotiation expansion */
+	Rannptr= 0x9C,		/* autonegotiation next page TX */
+	Rphysts= 0xC0,		/* PHY status */
+	Rmicr=	0xC4,		/* MII control */
+	  Inten=	1<<1,	/* PHY interrupt enable */
+	Rmisr=	0xC8,		/* MII status */
+	Rfcscr=	0xD0,		/* false carrier sense counter */
+	Rrecr=	0xD4,		/* receive error counter */
+	Rpcsr=	0xD8,		/* 100Mb config/status */
+	Rphycr=	0xE4,		/* PHY control */
+	Rtbscr=	0xE8,		/* 10BaseT status/control */
+};
+
+/*
+ * eeprom addresses
+ * 	7 to 9 (16 bit words): mac address, shifted and reversed
+ */
+
+#define csr32r(c, r)	(inl((c)->port+(r)))
+#define csr32w(c, r, l)	(outl((c)->port+(r), (ulong)(l)))
+#define csr16r(c, r)	(ins((c)->port+(r)))
+#define csr16w(c, r, l)	(outs((c)->port+(r), (ulong)(l)))
+
+static void
+dumpcregs(Ctlr *ctlr)
+{
+	int i;
+
+	for(i=0; i<=0x5C; i+=4)
+		print("%2.2ux %8.8lux\n", i, csr32r(ctlr, i));
+}
+
+static void
+promiscuous(void* arg, int on)
+{
+	Ctlr *ctlr;
+	ulong w;
+
+	ctlr = ((Ether*)arg)->ctlr;
+	ilock(&ctlr->lock);
+	w = csr32r(ctlr, Rrfcr);
+	if(on != ((w&Aau)!=0)){
+		csr32w(ctlr, Rrfcr, w & ~Rfen);
+		csr32w(ctlr, Rrfcr, Rfen | (w ^ Aau));
+	}
+	iunlock(&ctlr->lock);
+}
+
+static void
+attach(Ether* ether)
+{
+	Ctlr *ctlr;
+
+	ctlr = ether->ctlr;
+	ilock(&ctlr->lock);
+	if(0)
+		dumpcregs(ctlr);
+	csr32w(ctlr, Rcr, Rxe);
+	iunlock(&ctlr->lock);
+}
+
+static long
+ifstat(Ether* ether, void* a, long n, ulong offset)
+{
+	Ctlr *ctlr;
+	char *buf, *p;
+	int i, l, len;
+
+	ctlr = ether->ctlr;
+
+	ether->crcs = ctlr->crce;
+	ether->frames = ctlr->runt+ctlr->ise+ctlr->rlong+ctlr->fae;
+	ether->buffs = ctlr->rxorn+ctlr->tfu;
+	ether->overflows = ctlr->rxsovr;
+
+	if(n == 0)
+		return 0;
+
+	p = malloc(READSTR);
+	l = snprint(p, READSTR, "Rxa: %lud\n", ctlr->rxa);
+	l += snprint(p+l, READSTR-l, "Rxo: %lud\n", ctlr->rxo);
+	l += snprint(p+l, READSTR-l, "Rlong: %lud\n", ctlr->rlong);
+	l += snprint(p+l, READSTR-l, "Runt: %lud\n", ctlr->runt);
+	l += snprint(p+l, READSTR-l, "Ise: %lud\n", ctlr->ise);
+	l += snprint(p+l, READSTR-l, "Fae: %lud\n", ctlr->fae);
+	l += snprint(p+l, READSTR-l, "Lbp: %lud\n", ctlr->lbp);
+	l += snprint(p+l, READSTR-l, "Tfu: %lud\n", ctlr->tfu);
+	l += snprint(p+l, READSTR-l, "Txa: %lud\n", ctlr->txa);
+	l += snprint(p+l, READSTR-l, "CRC Error: %lud\n", ctlr->crce);
+	l += snprint(p+l, READSTR-l, "Collision Seen: %lud\n", ctlr->col);
+	l += snprint(p+l, READSTR-l, "Frame Too Long: %lud\n", ctlr->rlong);
+	l += snprint(p+l, READSTR-l, "Runt Frame: %lud\n", ctlr->runt);
+	l += snprint(p+l, READSTR-l, "Rx Underflow Error: %lud\n", ctlr->rxorn);
+	l += snprint(p+l, READSTR-l, "Tx Underrun: %lud\n", ctlr->txurn);
+	l += snprint(p+l, READSTR-l, "Excessive Collisions: %lud\n", ctlr->ec);
+	l += snprint(p+l, READSTR-l, "Late Collision: %lud\n", ctlr->owc);
+	l += snprint(p+l, READSTR-l, "Loss of Carrier: %lud\n", ctlr->crs);
+	l += snprint(p+l, READSTR-l, "Parity: %lud\n", ctlr->dperr);
+	l += snprint(p+l, READSTR-l, "Aborts: %lud\n", ctlr->rmabt+ctlr->rtabt);
+	l += snprint(p+l, READSTR-l, "RX Status overrun: %lud\n", ctlr->rxsover);
+	snprint(p+l, READSTR-l, "ntqmax: %d\n", ctlr->ntqmax);
+	ctlr->ntqmax = 0;
+	buf = a;
+	len = readstr(offset, buf, n, p);
+	if(offset > l)
+		offset -= l;
+	else
+		offset = 0;
+	buf += len;
+	n -= len;
+
+	l = snprint(p, READSTR, "srom:");
+	for(i = 0; i < nelem(ctlr->srom); i++){
+		if(i && ((i & 0x0F) == 0))
+			l += snprint(p+l, READSTR-l, "\n     ");
+		l += snprint(p+l, READSTR-l, " %4.4uX", ctlr->srom[i]);
+	}
+
+	snprint(p+l, READSTR-l, "\n");
+	len += readstr(offset, buf, n, p);
+	free(p);
+
+	return len;
+}
+
+static void
+txstart(Ether* ether)
+{
+	Ctlr *ctlr;
+	Block *bp;
+	Des *des;
+	int started;
+
+	ctlr = ether->ctlr;
+	started = 0;
+	while(ctlr->ntq < ctlr->ntdr-1){
+		bp = qget(ether->oq);
+		if(bp == nil)
+			break;
+		des = &ctlr->tdr[ctlr->tdrh];
+		des->bp = bp;
+		des->addr = PADDR(bp->rp);
+		ctlr->ntq++;
+		coherence();
+		des->cmdsts = Own | BLEN(bp);
+		ctlr->tdrh = NEXT(ctlr->tdrh, ctlr->ntdr);
+		started = 1;
+	}
+	if(started){
+		coherence();
+		csr32w(ctlr, Rcr, Txe);	/* prompt */
+	}
+
+	if(ctlr->ntq > ctlr->ntqmax)
+		ctlr->ntqmax = ctlr->ntq;
+}
+
+static void
+transmit(Ether* ether)
+{
+	Ctlr *ctlr;
+
+	ctlr = ether->ctlr;
+	ilock(&ctlr->tlock);
+	txstart(ether);
+	iunlock(&ctlr->tlock);
+}
+
+static void
+txrxcfg(Ctlr *ctlr, int txdrth)
+{
+	ulong rx, tx;
+
+	rx = csr32r(ctlr, Rrxcfg);
+	tx = csr32r(ctlr, Rtxcfg);
+	if(ctlr->fd){
+		rx |= Atx;
+		tx |= Csi | Hbi;
+	}else{
+		rx &= ~Atx;
+		tx &= ~(Csi | Hbi);
+	}
+	tx &= ~(Mxdma|Drth|Flth);
+	tx |= Mxdma64 | Flth128 | txdrth;
+	csr32w(ctlr, Rtxcfg, tx);
+	rx &= ~(Mxdma|Rdrth);
+	rx |= Mxdma64 | Rdrth64;
+	csr32w(ctlr, Rrxcfg, rx);
+}
+
+static void
+interrupt(Ureg*, void* arg)
+{
+	int len, status, cmdsts, n;
+	Ctlr *ctlr;
+	Ether *ether;
+	Des *des;
+	Block *bp;
+
+	ether = arg;
+	ctlr = ether->ctlr;
+
+	while((status = csr32r(ctlr, Risr)) != 0){
+
+		status &= ~(Pme|Mib);
+
+		if(status & Hiberr){
+			if(status & Rxsovr)
+				ctlr->rxsover++;
+			if(status & Sserr)
+				ctlr->sserr++;
+			if(status & Dperr)
+				ctlr->dperr++;
+			if(status & Rmabt)
+				ctlr->rmabt++;
+			if(status & Rtabt)
+				ctlr->rtabt++;
+			status &= ~(Hiberr|Txrcmp|Rxrcmp|Rxsovr|Dperr|Sserr|Rmabt|Rtabt);
+		}
+
+		/* update link state */
+		if(status&Phy){
+			status &= ~Phy;
+			csr32r(ctlr, Rcfg);
+			n = csr32r(ctlr, Rcfg);
+//			iprint("83815 phy %x %x\n", n, n&Lnksts);
+			ether->link = (n&Lnksts) != 0;
+		}
+
+		/*
+		 * Received packets.
+		 */
+		if(status & (Rxdesc|Rxok|Rxerr|Rxearly|Rxorn)){
+			des = &ctlr->rdr[ctlr->rdrx];
+			while((cmdsts = des->cmdsts) & Own){
+				if((cmdsts&Ok) == 0){
+					if(cmdsts & Rxa)
+						ctlr->rxa++;
+					if(cmdsts & Rxo)
+						ctlr->rxo++;
+					if(cmdsts & Long)
+						ctlr->rlong++;
+					if(cmdsts & Runt)
+						ctlr->runt++;
+					if(cmdsts & Ise)
+						ctlr->ise++;
+					if(cmdsts & Crce)
+						ctlr->crce++;
+					if(cmdsts & Fae)
+						ctlr->fae++;
+					if(cmdsts & Lbp)
+						ctlr->lbp++;
+					if(cmdsts & Col)
+						ctlr->col++;
+				}
+				else if(bp = iallocb(Rbsz)){
+					len = (cmdsts&Size)-4;
+					if(len <= 0){
+						debug("ns83815: packet len %d <=0\n", len);
+						freeb(des->bp);
+					}else{
+						des->bp->wp = des->bp->rp+len;
+						etheriq(ether, des->bp, 1);
+					}
+					des->bp = bp;
+					des->addr = PADDR(bp->rp);
+					coherence();
+				}else{
+					debug("ns83815: interrupt: iallocb for input buffer failed\n");
+					des->bp->next = 0;
+				}
+
+				des->cmdsts = Rbsz;
+				coherence();
+
+				ctlr->rdrx = NEXT(ctlr->rdrx, ctlr->nrdr);
+				des = &ctlr->rdr[ctlr->rdrx];
+			}
+			status &= ~(Rxdesc|Rxok|Rxerr|Rxearly|Rxorn);
+		}
+
+		/*
+		 * Check the transmit side:
+		 *	check for Transmit Underflow and Adjust
+		 *	the threshold upwards;
+		 *	free any transmitted buffers and try to
+		 *	top-up the ring.
+		 */
+		if(status & Txurn){
+			ctlr->txurn++;
+			ilock(&ctlr->lock);
+			/* change threshold */
+			iunlock(&ctlr->lock);
+			status &= ~(Txurn);
+		}
+
+		ilock(&ctlr->tlock);
+		while(ctlr->ntq){
+			des = &ctlr->tdr[ctlr->tdri];
+			cmdsts = des->cmdsts;
+			if(cmdsts & Own)
+				break;
+
+			if((cmdsts & Ok) == 0){
+				if(cmdsts & Txa)
+					ctlr->txa++;
+				if(cmdsts & Tfu)
+					ctlr->tfu++;
+				if(cmdsts & Td)
+					ctlr->td++;
+				if(cmdsts & Ed)
+					ctlr->ed++;
+				if(cmdsts & Owc)
+					ctlr->owc++;
+				if(cmdsts & Ec)
+					ctlr->ec++;
+				ether->oerrs++;
+			}
+
+			freeb(des->bp);
+			des->bp = nil;
+			des->cmdsts = 0;
+
+			ctlr->ntq--;
+			ctlr->tdri = NEXT(ctlr->tdri, ctlr->ntdr);
+		}
+		txstart(ether);
+		iunlock(&ctlr->tlock);
+
+		status &= ~(Txurn|Txidle|Txerr|Txdesc|Txok);
+
+		/*
+		 * Anything left not catered for?
+		 */
+		if(status)
+			print("#l%d: status %8.8uX\n", ether->ctlrno, status);
+	}
+}
+
+static void
+ctlrinit(Ether* ether)
+{
+	Ctlr *ctlr;
+	Des *des, *last;
+
+	ctlr = ether->ctlr;
+
+	/*
+	 * Allocate suitable aligned descriptors
+	 * for the transmit and receive rings;
+	 * initialise the receive ring;
+	 * initialise the transmit ring;
+	 * unmask interrupts and start the transmit side.
+	 */
+	des = xspanalloc((ctlr->nrdr+ctlr->ntdr)*sizeof(Des), 32, 0);
+	ctlr->tdr = des;
+	ctlr->rdr = des+ctlr->ntdr;
+
+	last = nil;
+	for(des = ctlr->rdr; des < &ctlr->rdr[ctlr->nrdr]; des++){
+		des->bp = iallocb(Rbsz);
+		if(des->bp == nil)
+			error(Enomem);
+		des->cmdsts = Rbsz;
+		des->addr = PADDR(des->bp->rp);
+		if(last != nil)
+			last->next = PADDR(des);
+		last = des;
+	}
+	ctlr->rdr[ctlr->nrdr-1].next = PADDR(ctlr->rdr);
+	ctlr->rdrx = 0;
+	csr32w(ctlr, Rrxdp, PADDR(ctlr->rdr));
+
+	last = nil;
+	for(des = ctlr->tdr; des < &ctlr->tdr[ctlr->ntdr]; des++){
+		des->cmdsts = 0;
+		des->bp = nil;
+		des->addr = ~0;
+		if(last != nil)
+			last->next = PADDR(des);
+		last = des;
+	}
+	ctlr->tdr[ctlr->ntdr-1].next = PADDR(ctlr->tdr);
+	ctlr->tdrh = 0;
+	ctlr->tdri = 0;
+	csr32w(ctlr, Rtxdp, PADDR(ctlr->tdr));
+
+	txrxcfg(ctlr, Drth512);
+
+	csr32w(ctlr, Rimr, Dperr|Sserr|Rmabt|Rtabt|Rxsovr|Hiberr|Txurn|Txerr|
+		Txdesc|Txok|Rxorn|Rxerr|Rxdesc|Rxok);	/* Phy|Pme|Mib */
+	csr32w(ctlr, Rmicr, Inten);	/* enable phy interrupts */
+	csr32r(ctlr, Risr);		/* clear status */
+	csr32w(ctlr, Rier, Ie);
+}
+
+static void
+eeclk(Ctlr *ctlr, int clk)
+{
+	csr32w(ctlr, Rmear, Eesel | clk);
+	microdelay(2);
+}
+
+static void
+eeidle(Ctlr *ctlr)
+{
+	int i;
+
+	eeclk(ctlr, 0);
+	eeclk(ctlr, Eeclk);
+	for(i=0; i<25; i++){
+		eeclk(ctlr, 0);
+		eeclk(ctlr, Eeclk);
+	}
+	eeclk(ctlr, 0);
+	csr32w(ctlr, Rmear, 0);
+	microdelay(2);
+}
+
+static int
+eegetw(Ctlr *ctlr, int a)
+{
+	int d, i, w, v;
+
+	eeidle(ctlr);
+	eeclk(ctlr, 0);
+	eeclk(ctlr, Eeclk);
+	d = 0x180 | a;
+	for(i=0x400; i; i>>=1){
+		v = (d & i) ? Eedi : 0;
+		eeclk(ctlr, v);
+		eeclk(ctlr, Eeclk|v);
+	}
+	eeclk(ctlr, 0);
+
+	w = 0;
+	for(i=0x8000; i; i >>= 1){
+		eeclk(ctlr, Eeclk);
+		if(csr32r(ctlr, Rmear) & Eedo)
+			w |= i;
+		microdelay(2);
+		eeclk(ctlr, 0);
+	}
+	eeidle(ctlr);
+	return w;
+}
+
+static void
+resetctlr(Ctlr *ctlr)
+{
+	int i;
+
+	csr32w(ctlr, Rcr, Rst);
+	for(i=0;; i++){
+		if(i > 100)
+			panic("ns83815: soft reset did not complete");
+		microdelay(250);
+		if((csr32r(ctlr, Rcr) & Rst) == 0)
+			break;
+		delay(1);
+	}
+}
+
+static void
+shutdown(Ether* ether)
+{
+	Ctlr *ctlr = ether->ctlr;
+
+print("ether83815 shutting down\n");
+	csr32w(ctlr, Rcr, Rxd|Txd);	/* disable transceiver */
+	resetctlr(ctlr);
+}
+
+static void
+softreset(Ctlr* ctlr, int resetphys)
+{
+	int i, w;
+
+	/*
+	 * Soft-reset the controller
+	 */
+	resetctlr(ctlr);
+	csr32w(ctlr, Rccsr, Pmests);
+	csr32w(ctlr, Rccsr, 0);
+	csr32w(ctlr, Rcfg, csr32r(ctlr, Rcfg) | Pint_acen);
+
+	if(resetphys){
+		/*
+		 * Soft-reset the PHY
+		 */
+		csr32w(ctlr, Rbmcr, Reset);
+		for(i=0;; i++){
+			if(i > 100)
+				panic("ns83815: PHY soft reset time out");
+			if((csr32r(ctlr, Rbmcr) & Reset) == 0)
+				break;
+			delay(1);
+		}
+	}
+
+	/*
+	 * Initialisation values, in sequence (see 4.4 Recommended Registers Configuration)
+	 */
+	csr16w(ctlr, 0xCC, 0x0001);	/* PGSEL */
+	csr16w(ctlr, 0xE4, 0x189C);	/* PMCCSR */
+	csr16w(ctlr, 0xFC, 0x0000);	/* TSTDAT */
+	csr16w(ctlr, 0xF4, 0x5040);	/* DSPCFG */
+	csr16w(ctlr, 0xF8, 0x008C);	/* SDCFG */
+
+	/*
+	 * Auto negotiate
+	 */
+	w = csr16r(ctlr, Rbmsr);	/* clear latched bits */
+	debug("anar: %4.4ux\n", csr16r(ctlr, Ranar));
+	csr16w(ctlr, Rbmcr, Anena);
+	if(csr16r(ctlr, Ranar) == 0 || (csr32r(ctlr, Rcfg) & Aneg_dn) == 0){
+		csr16w(ctlr, Rbmcr, Anena|Anrestart);
+		for(i=0;; i++){
+			if(i > 3000){
+				print("ns83815: auto neg timed out\n");
+				break;
+			}
+			if((w = csr16r(ctlr, Rbmsr)) & Ancomp)
+				break;
+			delay(1);
+		}
+		debug("%d ms\n", i);
+		w &= 0xFFFF;
+		debug("bmsr: %4.4ux\n", w);
+	}
+	USED(w);
+	debug("anar: %4.4ux\n", csr16r(ctlr, Ranar));
+	debug("anlpar: %4.4ux\n", csr16r(ctlr, Ranlpar));
+	debug("aner: %4.4ux\n", csr16r(ctlr, Raner));
+	debug("physts: %4.4ux\n", csr16r(ctlr, Rphysts));
+	debug("tbscr: %4.4ux\n", csr16r(ctlr, Rtbscr));
+}
+
+static int
+media(Ether* ether)
+{
+	Ctlr* ctlr;
+	ulong cfg;
+
+	ctlr = ether->ctlr;
+	cfg = csr32r(ctlr, Rcfg);
+	ctlr->fd = (cfg & Fdup) != 0;
+	ether->link = (cfg&Lnksts) != 0;
+	return (cfg&(Lnksts|Speed100)) == Lnksts? 10: 100;
+}
+
+static char* mediatable[9] = {
+	"10BASE-T",				/* TP */
+	"10BASE-2",				/* BNC */
+	"10BASE-5",				/* AUI */
+	"100BASE-TX",
+	"10BASE-TFD",
+	"100BASE-TXFD",
+	"100BASE-T4",
+	"100BASE-FX",
+	"100BASE-FXFD",
+};
+
+static int
+is630(ulong id, Pcidev *p)
+{
+	if(id == SiS900)
+		switch (p->rid) {
+		case SiSrev630s:
+		case SiSrev630e:
+	  	case SiSrev630ea1:
+			return 1;
+		}
+	return 0;
+}
+
+enum {
+	MagicReg = 0x48,
+	MagicRegSz = 1,
+	Magicrden = 0x40,	/* read enable, apparently */
+	Paddr=		0x70,	/* address port */
+	Pdata=		0x71,	/* data port */
+};
+
+/* rcmos() originally from LANL's SiS 900 driver's rcmos() */
+static int
+sisrdcmos(Ctlr *ctlr)
+{
+	int i;
+	unsigned reg;
+	ulong port;
+	Pcidev *p;
+
+	debug("ns83815: SiS 630 rev. %ux reading mac address from cmos\n", ctlr->pcidev->rid);
+	p = pcimatch(nil, SiS, SiS630bridge);
+	if(p == nil) {
+		print("ns83815: no SiS 630 rev. %ux bridge for mac addr\n",
+			ctlr->pcidev->rid);
+		return 0;
+	}
+	port = p->mem[0].bar & ~0x01;
+	debug("ns83815: SiS 630 rev. %ux reading mac addr from cmos via bridge at port 0x%lux\n", ctlr->pcidev->rid, port);
+
+	reg = pcicfgr8(p, MagicReg);
+	pcicfgw8(p, MagicReg, reg|Magicrden);
+
+	for (i = 0; i < Eaddrlen; i++) {
+		outb(port+Paddr, SiS630eenodeaddr + i);
+		ctlr->sromea[i] = inb(port+Pdata);
+	}
+
+	pcicfgw8(p, MagicReg, reg & ~Magicrden);
+	return 1;
+}
+
+/*
+ * If this is a SiS 630E chipset with an embedded SiS 900 controller,
+ * we have to read the MAC address from the APC CMOS RAM. - sez freebsd.
+ * However, CMOS *is* NVRAM normally.  See devrtc.c:440, memory.c:88.
+ */
+static void
+sissrom(Ctlr *ctlr)
+{
+	union {
+		uchar	eaddr[Eaddrlen];
+		ushort	alignment;
+	} ee;
+	int i, off = SiSeenodeaddr, cnt = sizeof ee.eaddr / sizeof(short);
+	ushort *shp = (ushort *)ee.eaddr;
+
+	if(!is630(ctlr->id, ctlr->pcidev) || !sisrdcmos(ctlr)) {
+		for (i = 0; i < cnt; i++)
+			*shp++ = eegetw(ctlr, off++);
+		memmove(ctlr->sromea, ee.eaddr, sizeof ctlr->sromea);
+	}
+}
+
+static void
+nssrom(Ctlr* ctlr)
+{
+	int i, j;
+
+	for(i = 0; i < nelem(ctlr->srom); i++)
+		ctlr->srom[i] = eegetw(ctlr, i);
+
+	/*
+	 * the MAC address is reversed, straddling word boundaries
+	 */
+	j = Nseenodeaddr*16 + 15;
+	for(i=0; i<48; i++){
+		ctlr->sromea[i>>3] |= ((ctlr->srom[j>>4] >> (15-(j&0xF))) & 1) << (i&7);
+		j++;
+	}
+}
+
+static void
+srom(Ctlr* ctlr)
+{
+	memset(ctlr->sromea, 0, sizeof(ctlr->sromea));
+	switch (ctlr->id) {
+	case SiS900:
+	case SiS7016:
+		sissrom(ctlr);
+		break;
+	case Nat83815:
+		nssrom(ctlr);
+		break;
+	default:
+		print("ns83815: srom: unknown id 0x%ux\n", ctlr->id);
+		break;
+	}
+}
+
+static void
+scanpci83815(void)
+{
+	Ctlr *ctlr;
+	Pcidev *p;
+	ulong id;
+
+	p = nil;
+	while(p = pcimatch(p, 0, 0)){
+		if(p->ccrb != Pcibcnet || p->ccru != 0)
+			continue;
+		id = (p->did<<16)|p->vid;
+		switch(id){
+		default:
+			continue;
+
+		case Nat83815:
+			break;
+		case SiS900:
+			break;
+		}
+
+		/*
+		 * bar[0] is the I/O port register address and
+		 * bar[1] is the memory-mapped register address.
+		 */
+		ctlr = malloc(sizeof(Ctlr));
+		ctlr->port = p->mem[0].bar & ~0x01;
+		ctlr->pcidev = p;
+		ctlr->id = id;
+
+		if(ioalloc(ctlr->port, p->mem[0].size, 0, "ns83815") < 0){
+			print("ns83815: port 0x%uX in use\n", ctlr->port);
+			free(ctlr);
+			continue;
+		}
+
+		softreset(ctlr, 0);
+		srom(ctlr);
+
+		if(ctlrhead != nil)
+			ctlrtail->next = ctlr;
+		else
+			ctlrhead = ctlr;
+		ctlrtail = ctlr;
+	}
+}
+
+/* multicast already on, don't need to do anything */
+static void
+multicast(void*, uchar*, int)
+{
+}
+
+static int
+reset(Ether* ether)
+{
+	Ctlr *ctlr;
+	int i, x;
+	ulong ctladdr;
+	uchar ea[Eaddrlen];
+	static int scandone;
+
+	if(scandone == 0){
+		scanpci83815();
+		scandone = 1;
+	}
+
+	/*
+	 * Any adapter matches if no ether->port is supplied,
+	 * otherwise the ports must match.
+	 */
+	for(ctlr = ctlrhead; ctlr != nil; ctlr = ctlr->next){
+		if(ctlr->active)
+			continue;
+		if(ether->port == 0 || ether->port == ctlr->port){
+			ctlr->active = 1;
+			break;
+		}
+	}
+	if(ctlr == nil)
+		return -1;
+
+	ether->ctlr = ctlr;
+	ether->port = ctlr->port;
+	ether->irq = ctlr->pcidev->intl;
+	ether->tbdf = ctlr->pcidev->tbdf;
+
+	/*
+	 * Check if the adapter's station address is to be overridden.
+	 * If not, read it from the EEPROM and set in ether->ea prior to
+	 * loading the station address in the hardware.
+	 */
+	memset(ea, 0, Eaddrlen);
+	if(memcmp(ea, ether->ea, Eaddrlen) == 0)
+		memmove(ether->ea, ctlr->sromea, Eaddrlen);
+	for(i=0; i<Eaddrlen; i+=2){
+		x = ether->ea[i] | (ether->ea[i+1]<<8);
+		ctladdr = (ctlr->id == Nat83815? i: i<<15);
+		csr32w(ctlr, Rrfcr, ctladdr);
+		csr32w(ctlr, Rrfdr, x);
+	}
+	csr32w(ctlr, Rrfcr, Rfen|Apm|Aab|Aam);
+
+	ether->mbps = media(ether);
+
+	/*
+	 * Look for a medium override in case there's no autonegotiation
+	 * the autonegotiation fails.
+	 */
+
+	for(i = 0; i < ether->nopt; i++){
+		if(cistrcmp(ether->opt[i], "FD") == 0){
+			ctlr->fd = 1;
+			continue;
+		}
+		for(x = 0; x < nelem(mediatable); x++){
+			debug("compare <%s> <%s>\n", mediatable[x],
+				ether->opt[i]);
+			if(cistrcmp(mediatable[x], ether->opt[i]) == 0){
+				if(x != 4 && x >= 3)
+					ether->mbps = 100;
+				else
+					ether->mbps = 10;
+				switch(x){
+				default:
+					ctlr->fd = 0;
+					break;
+
+				case 0x04:		/* 10BASE-TFD */
+				case 0x05:		/* 100BASE-TXFD */
+				case 0x08:		/* 100BASE-FXFD */
+					ctlr->fd = 1;
+					break;
+				}
+				break;
+			}
+		}
+	}
+
+	/*
+	 * Initialise descriptor rings, ethernet address.
+	 */
+	ctlr->nrdr = Nrde;
+	ctlr->ntdr = Ntde;
+	pcisetbme(ctlr->pcidev);
+	ctlrinit(ether);
+
+	/*
+	 * Linkage to the generic ethernet driver.
+	 */
+	ether->attach = attach;
+	ether->transmit = transmit;
+	ether->interrupt = interrupt;
+	ether->ifstat = ifstat;
+
+	ether->arg = ether;
+	ether->promiscuous = promiscuous;
+	ether->multicast = multicast;
+	ether->shutdown = shutdown;
+	return 0;
+}
+
+void
+ether83815link(void)
+{
+	addethercard("83815",  reset);
+}
--- /dev/null
+++ b/os/pc/ether8390.c
@@ -1,0 +1,812 @@
+/*
+ * National Semiconductor DP8390 and clone
+ * Network Interface Controller.
+ */
+#include "u.h"
+#include "../port/lib.h"
+#include "mem.h"
+#include "dat.h"
+#include "fns.h"
+#include "io.h"
+#include "../port/error.h"
+#include "../port/netif.h"
+
+#include "etherif.h"
+#include "ether8390.h"
+
+enum {					/* NIC core registers */
+	Cr		= 0x00,		/* command register, all pages */
+
+					/* Page 0, read */
+	Clda0		= 0x01,		/* current local DMA address 0 */
+	Clda1		= 0x02,		/* current local DMA address 1 */
+	Bnry		= 0x03,		/* boundary pointer (R/W) */
+	Tsr		= 0x04,		/* transmit status register */
+	Ncr		= 0x05,		/* number of collisions register */
+	Fifo		= 0x06,		/* FIFO */
+	Isr		= 0x07,		/* interrupt status register (R/W) */
+	Crda0		= 0x08,		/* current remote DMA address 0 */
+	Crda1		= 0x09,		/* current remote DMA address 1 */
+	Rsr		= 0x0C,		/* receive status register */
+	Ref0		= 0x0D,		/* frame alignment errors */
+	Ref1		= 0x0E,		/* CRC errors */
+	Ref2		= 0x0F,		/* missed packet errors */
+
+					/* Page 0, write */
+	Pstart		= 0x01,		/* page start register */
+	Pstop		= 0x02,		/* page stop register */
+	Tpsr		= 0x04,		/* transmit page start address */
+	Tbcr0		= 0x05,		/* transmit byte count register 0 */
+	Tbcr1		= 0x06,		/* transmit byte count register 1 */
+	Rsar0		= 0x08,		/* remote start address register 0 */
+	Rsar1		= 0x09,		/* remote start address register 1 */
+	Rbcr0		= 0x0A,		/* remote byte count register 0 */
+	Rbcr1		= 0x0B,		/* remote byte count register 1 */
+	Rcr		= 0x0C,		/* receive configuration register */
+	Tcr		= 0x0D,		/* transmit configuration register */
+	Dcr		= 0x0E,		/* data configuration register */
+	Imr		= 0x0F,		/* interrupt mask */
+
+					/* Page 1, read/write */
+	Par0		= 0x01,		/* physical address register 0 */
+	Curr		= 0x07,		/* current page register */
+	Mar0		= 0x08,		/* multicast address register 0 */
+};
+
+enum {					/* Cr */
+	Stp		= 0x01,		/* stop */
+	Sta		= 0x02,		/* start */
+	Txp		= 0x04,		/* transmit packet */
+	Rd0		= 0x08,		/* remote DMA command */
+	Rd1		= 0x10,	
+	Rd2		= 0x20,
+	RdREAD		= Rd0,		/* remote read */
+	RdWRITE		= Rd1,		/* remote write */
+	RdSEND		= Rd1|Rd0,	/* send packet */
+	RdABORT		= Rd2,		/* abort/complete remote DMA */
+	Ps0		= 0x40,		/* page select */
+	Ps1		= 0x80,
+	Page0		= 0x00,
+	Page1		= Ps0,
+	Page2		= Ps1,
+};
+
+enum {					/* Isr/Imr */
+	Prx		= 0x01,		/* packet received */
+	Ptx		= 0x02,		/* packet transmitted */
+	Rxe		= 0x04,		/* receive error */
+	Txe		= 0x08,		/* transmit error */
+	Ovw		= 0x10,		/* overwrite warning */
+	Cnt		= 0x20,		/* counter overflow */
+	Rdc		= 0x40,		/* remote DMA complete */
+	Rst		= 0x80,		/* reset status */
+};
+
+enum {					/* Dcr */
+	Wts		= 0x01,		/* word transfer select */
+	Bos		= 0x02,		/* byte order select */
+	Las		= 0x04,		/* long address select */
+	Ls		= 0x08,		/* loopback select */
+	Arm		= 0x10,		/* auto-initialise remote */
+	Ft0		= 0x20,		/* FIFO threshold select */
+	Ft1		= 0x40,
+	Ft1WORD		= 0x00,
+	Ft2WORD		= Ft0,
+	Ft4WORD		= Ft1,
+	Ft6WORD		= Ft1|Ft0,
+};
+
+enum {					/* Tcr */
+	Crc		= 0x01,		/* inhibit CRC */
+	Lb0		= 0x02,		/* encoded loopback control */
+	Lb1		= 0x04,
+	LpbkNORMAL	= 0x00,		/* normal operation */
+	LpbkNIC		= Lb0,		/* internal NIC module loopback */
+	LpbkENDEC	= Lb1,		/* internal ENDEC module loopback */
+	LpbkEXTERNAL	= Lb1|Lb0,	/* external loopback */
+	Atd		= 0x08,		/* auto transmit disable */
+	Ofst		= 0x10,		/* collision offset enable */
+};
+
+enum {					/* Tsr */
+	Ptxok		= 0x01,		/* packet transmitted */
+	Col		= 0x04,		/* transmit collided */
+	Abt		= 0x08,		/* tranmit aborted */
+	Crs		= 0x10,		/* carrier sense lost */
+	Fu		= 0x20,		/* FIFO underrun */
+	Cdh		= 0x40,		/* CD heartbeat */
+	Owc		= 0x80,		/* out of window collision */
+};
+
+enum {					/* Rcr */
+	Sep		= 0x01,		/* save errored packets */
+	Ar		= 0x02,		/* accept runt packets */
+	Ab		= 0x04,		/* accept broadcast */
+	Am		= 0x08,		/* accept multicast */
+	Pro		= 0x10,		/* promiscuous physical */
+	Mon		= 0x20,		/* monitor mode */
+};
+
+enum {					/* Rsr */
+	Prxok		= 0x01,		/* packet received intact */
+	Crce		= 0x02,		/* CRC error */
+	Fae		= 0x04,		/* frame alignment error */
+	Fo		= 0x08,		/* FIFO overrun */
+	Mpa		= 0x10,		/* missed packet */
+	Phy		= 0x20,		/* physical/multicast address */
+	Dis		= 0x40,		/* receiver disabled */
+	Dfr		= 0x80,		/* deferring */
+};
+
+typedef struct Hdr Hdr;
+struct Hdr {
+	uchar	status;
+	uchar	next;
+	uchar	len0;
+	uchar	len1;
+};
+
+void
+dp8390getea(Ether* ether, uchar* ea)
+{
+	Dp8390 *ctlr;
+	uchar cr;
+	int i;
+
+	ctlr = ether->ctlr;
+
+	/*
+	 * Get the ethernet address from the chip.
+	 * Take care to restore the command register
+	 * afterwards.
+	 */
+	ilock(ctlr);
+	cr = regr(ctlr, Cr) & ~Txp;
+	regw(ctlr, Cr, Page1|(~(Ps1|Ps0) & cr));
+	for(i = 0; i < Eaddrlen; i++)
+		ea[i] = regr(ctlr, Par0+i);
+	regw(ctlr, Cr, cr);
+	iunlock(ctlr);
+}
+
+void
+dp8390setea(Ether* ether)
+{
+	int i;
+	uchar cr;
+	Dp8390 *ctlr;
+
+	ctlr = ether->ctlr;
+
+	/*
+	 * Set the ethernet address into the chip.
+	 * Take care to restore the command register
+	 * afterwards. Don't care about multicast
+	 * addresses as multicast is never enabled
+	 * (currently).
+	 */
+	ilock(ctlr);
+	cr = regr(ctlr, Cr) & ~Txp;
+	regw(ctlr, Cr, Page1|(~(Ps1|Ps0) & cr));
+	for(i = 0; i < Eaddrlen; i++)
+		regw(ctlr, Par0+i, ether->ea[i]);
+	regw(ctlr, Cr, cr);
+	iunlock(ctlr);
+}
+
+static void*
+_dp8390read(Dp8390* ctlr, void* to, ulong from, ulong len)
+{
+	uchar cr;
+	int timo;
+
+	/*
+	 * Read some data at offset 'from' in the card's memory
+	 * using the DP8390 remote DMA facility, and place it at
+	 * 'to' in main memory, via the I/O data port.
+	 */
+	cr = regr(ctlr, Cr) & ~Txp;
+	regw(ctlr, Cr, Page0|RdABORT|Sta);
+	regw(ctlr, Isr, Rdc);
+
+	/*
+	 * Set up the remote DMA address and count.
+	 */
+	len = ROUNDUP(len, ctlr->width);
+	regw(ctlr, Rbcr0, len & 0xFF);
+	regw(ctlr, Rbcr1, (len>>8) & 0xFF);
+	regw(ctlr, Rsar0, from & 0xFF);
+	regw(ctlr, Rsar1, (from>>8) & 0xFF);
+
+	/*
+	 * Start the remote DMA read and suck the data
+	 * out of the I/O port.
+	 */
+	regw(ctlr, Cr, Page0|RdREAD|Sta);
+	rdread(ctlr, to, len);
+
+	/*
+	 * Wait for the remote DMA to complete. The timeout
+	 * is necessary because this routine may be called on
+	 * a non-existent chip during initialisation and, due
+	 * to the miracles of the bus, it's possible to get this
+	 * far and still be talking to a slot full of nothing.
+	 */
+	for(timo = 10000; (regr(ctlr, Isr) & Rdc) == 0 && timo; timo--)
+			;
+
+	regw(ctlr, Isr, Rdc);
+	regw(ctlr, Cr, cr);
+
+	return to;
+}
+
+void*
+dp8390read(Dp8390* ctlr, void* to, ulong from, ulong len)
+{
+	void *v;
+
+	ilock(ctlr);
+	v = _dp8390read(ctlr, to, from, len);
+	iunlock(ctlr);
+
+	return v;
+}
+
+static void*
+dp8390write(Dp8390* ctlr, ulong to, void* from, ulong len)
+{
+	ulong crda;
+	uchar cr;
+	int timo, width;
+
+top:
+	/*
+	 * Write some data to offset 'to' in the card's memory
+	 * using the DP8390 remote DMA facility, reading it at
+	 * 'from' in main memory, via the I/O data port.
+	 */
+	cr = regr(ctlr, Cr) & ~Txp;
+	regw(ctlr, Cr, Page0|RdABORT|Sta);
+	regw(ctlr, Isr, Rdc);
+
+	len = ROUNDUP(len, ctlr->width);
+
+	/*
+	 * Set up the remote DMA address and count.
+	 * This is straight from the DP8390[12D] datasheet,
+	 * hence the initial set up for read.
+	 * Assumption here that the A7000 EtherV card will
+	 * never need a dummyrr.
+	 */
+	if(ctlr->dummyrr && (ctlr->width == 1 || ctlr->width == 2)){
+		if(ctlr->width == 2)
+			width = 1;
+		else
+			width = 0;
+		crda = to-1-width;
+		regw(ctlr, Rbcr0, (len+1+width) & 0xFF);
+		regw(ctlr, Rbcr1, ((len+1+width)>>8) & 0xFF);
+		regw(ctlr, Rsar0, crda & 0xFF);
+		regw(ctlr, Rsar1, (crda>>8) & 0xFF);
+		regw(ctlr, Cr, Page0|RdREAD|Sta);
+	
+		for(timo=0;; timo++){
+			if(timo > 10000){
+				print("ether8390: dummyrr timeout; assuming nodummyrr\n");
+				ctlr->dummyrr = 0;
+				goto top;
+			}
+			crda = regr(ctlr, Crda0);
+			crda |= regr(ctlr, Crda1)<<8;
+			if(crda == to){
+				/*
+				 * Start the remote DMA write and make sure
+				 * the registers are correct.
+				 */
+				regw(ctlr, Cr, Page0|RdWRITE|Sta);
+	
+				crda = regr(ctlr, Crda0);
+				crda |= regr(ctlr, Crda1)<<8;
+				if(crda != to)
+					panic("crda write %lud to %lud\n", crda, to);
+	
+				break;
+			}
+		}
+	}
+	else{
+		regw(ctlr, Rsar0, to & 0xFF);
+		regw(ctlr, Rsar1, (to>>8) & 0xFF);
+		regw(ctlr, Rbcr0, len & 0xFF);
+		regw(ctlr, Rbcr1, (len>>8) & 0xFF);
+		regw(ctlr, Cr, Page0|RdWRITE|Sta);
+	}
+
+	/*
+	 * Pump the data into the I/O port
+	 * then wait for the remote DMA to finish.
+	 */
+	rdwrite(ctlr, from, len);
+	for(timo = 10000; (regr(ctlr, Isr) & Rdc) == 0 && timo; timo--)
+			;
+
+	regw(ctlr, Isr, Rdc);
+	regw(ctlr, Cr, cr);
+
+	return (void*)to;
+}
+
+static void
+ringinit(Dp8390* ctlr)
+{
+	regw(ctlr, Pstart, ctlr->pstart);
+	regw(ctlr, Pstop, ctlr->pstop);
+	regw(ctlr, Bnry, ctlr->pstop-1);
+
+	regw(ctlr, Cr, Page1|RdABORT|Stp);
+	regw(ctlr, Curr, ctlr->pstart);
+	regw(ctlr, Cr, Page0|RdABORT|Stp);
+
+	ctlr->nxtpkt = ctlr->pstart;
+}
+
+static uchar
+getcurr(Dp8390* ctlr)
+{
+	uchar cr, curr;
+
+	cr = regr(ctlr, Cr) & ~Txp;
+	regw(ctlr, Cr, Page1|(~(Ps1|Ps0) & cr));
+	curr = regr(ctlr, Curr);
+	regw(ctlr, Cr, cr);
+
+	return curr;
+}
+
+static void
+receive(Ether* ether)
+{
+	Dp8390 *ctlr;
+	uchar curr, *p;
+	Hdr hdr;
+	ulong count, data, len;
+	Block *bp;
+
+	ctlr = ether->ctlr;
+	for(curr = getcurr(ctlr); ctlr->nxtpkt != curr; curr = getcurr(ctlr)){
+		data = ctlr->nxtpkt*Dp8390BufSz;
+		if(ctlr->ram)
+			memmove(&hdr, (void*)(ether->mem+data), sizeof(Hdr));
+		else
+			_dp8390read(ctlr, &hdr, data, sizeof(Hdr));
+
+		/*
+		 * Don't believe the upper byte count, work it
+		 * out from the software next-page pointer and
+		 * the current next-page pointer.
+		 */
+		if(hdr.next > ctlr->nxtpkt)
+			len = hdr.next - ctlr->nxtpkt - 1;
+		else
+			len = (ctlr->pstop-ctlr->nxtpkt) + (hdr.next-ctlr->pstart) - 1;
+		if(hdr.len0 > (Dp8390BufSz-sizeof(Hdr)))
+			len--;
+
+		len = ((len<<8)|hdr.len0)-4;
+
+		/*
+		 * Chip is badly scrogged, reinitialise the ring.
+		 */
+		if(hdr.next < ctlr->pstart || hdr.next >= ctlr->pstop
+		  || len < 60 || len > sizeof(Etherpkt)){
+			print("dp8390: H%2.2ux+%2.2ux+%2.2ux+%2.2ux,%lud\n",
+				hdr.status, hdr.next, hdr.len0, hdr.len1, len);
+			regw(ctlr, Cr, Page0|RdABORT|Stp);
+			ringinit(ctlr);
+			regw(ctlr, Cr, Page0|RdABORT|Sta);
+
+			return;
+		}
+
+		/*
+		 * If it's a good packet read it in to the software buffer.
+		 * If the packet wraps round the hardware ring, read it in
+		 * two pieces.
+		 */
+		if((hdr.status & (Fo|Fae|Crce|Prxok)) == Prxok && (bp = iallocb(len))){
+			p = bp->rp;
+			bp->wp = p+len;
+			data += sizeof(Hdr);
+
+			if((data+len) >= ctlr->pstop*Dp8390BufSz){
+				count = ctlr->pstop*Dp8390BufSz - data;
+				if(ctlr->ram)
+					memmove(p, (void*)(ether->mem+data), count);
+				else
+					_dp8390read(ctlr, p, data, count);
+				p += count;
+				data = ctlr->pstart*Dp8390BufSz;
+				len -= count;
+			}
+			if(len){
+				if(ctlr->ram)
+					memmove(p, (void*)(ether->mem+data), len);
+				else
+					_dp8390read(ctlr, p, data, len);
+			}
+
+			/*
+			 * Copy the packet to whoever wants it.
+			 */
+			etheriq(ether, bp, 1);
+		}
+
+		/*
+		 * Finished with this packet, update the
+		 * hardware and software ring pointers.
+		 */
+		ctlr->nxtpkt = hdr.next;
+
+		hdr.next--;
+		if(hdr.next < ctlr->pstart)
+			hdr.next = ctlr->pstop-1;
+		regw(ctlr, Bnry, hdr.next);
+	}
+}
+
+static void
+txstart(Ether* ether)
+{
+	int len;
+	Dp8390 *ctlr;
+	Block *bp;
+	uchar minpkt[ETHERMINTU], *rp;
+
+	ctlr = ether->ctlr;
+
+	/*
+	 * This routine is called both from the top level and from interrupt
+	 * level and expects to be called with ctlr already locked.
+	 */
+	if(ctlr->txbusy)
+		return;
+	bp = qget(ether->oq);
+	if(bp == nil)
+		return;
+
+	/*
+	 * Make sure the packet is of minimum length;
+	 * copy it to the card's memory by the appropriate means;
+	 * start the transmission.
+	 */
+	len = BLEN(bp);
+	rp = bp->rp;
+	if(len < ETHERMINTU){
+		rp = minpkt;
+		memmove(rp, bp->rp, len);
+		memset(rp+len, 0, ETHERMINTU-len);
+		len = ETHERMINTU;
+	}
+
+	if(ctlr->ram)
+		memmove((void*)(ether->mem+ctlr->tstart*Dp8390BufSz), rp, len);
+	else
+		dp8390write(ctlr, ctlr->tstart*Dp8390BufSz, rp, len);
+	freeb(bp);
+
+	regw(ctlr, Tbcr0, len & 0xFF);
+	regw(ctlr, Tbcr1, (len>>8) & 0xFF);
+	regw(ctlr, Cr, Page0|RdABORT|Txp|Sta);
+
+	ether->outpackets++;
+	ctlr->txbusy = 1;
+}
+
+static void
+transmit(Ether* ether)
+{
+	Dp8390 *ctlr;
+
+	ctlr = ether->ctlr;
+
+	ilock(ctlr);
+	txstart(ether);
+	iunlock(ctlr);
+}
+
+static void
+overflow(Ether *ether)
+{
+	Dp8390 *ctlr;
+	uchar txp;
+	int resend;
+
+	ctlr = ether->ctlr;
+
+	/*
+	 * The following procedure is taken from the DP8390[12D] datasheet,
+	 * it seems pretty adamant that this is what has to be done.
+	 */
+	txp = regr(ctlr, Cr) & Txp;
+	regw(ctlr, Cr, Page0|RdABORT|Stp);
+	delay(2);
+	regw(ctlr, Rbcr0, 0);
+	regw(ctlr, Rbcr1, 0);
+
+	resend = 0;
+	if(txp && (regr(ctlr, Isr) & (Txe|Ptx)) == 0)
+		resend = 1;
+
+	regw(ctlr, Tcr, LpbkNIC);
+	regw(ctlr, Cr, Page0|RdABORT|Sta);
+	receive(ether);
+	regw(ctlr, Isr, Ovw);
+	regw(ctlr, Tcr, LpbkNORMAL);
+
+	if(resend)
+		regw(ctlr, Cr, Page0|RdABORT|Txp|Sta);
+}
+
+static void
+interrupt(Ureg*, void* arg)
+{
+	Ether *ether;
+	Dp8390 *ctlr;
+	uchar isr, r;
+
+	ether = arg;
+	ctlr = ether->ctlr;
+
+	/*
+	 * While there is something of interest,
+	 * clear all the interrupts and process.
+	 */
+	ilock(ctlr);
+	regw(ctlr, Imr, 0x00);
+	while(isr = (regr(ctlr, Isr) & (Cnt|Ovw|Txe|Rxe|Ptx|Prx))){
+		if(isr & Ovw){
+			overflow(ether);
+			regw(ctlr, Isr, Ovw);
+			ether->overflows++;
+		}
+
+		/*
+		 * Packets have been received.
+		 * Take a spin round the ring.
+		 */
+		if(isr & (Rxe|Prx)){
+			receive(ether);
+			regw(ctlr, Isr, Rxe|Prx);
+		}
+
+		/*
+		 * A packet completed transmission, successfully or
+		 * not. Start transmission on the next buffered packet,
+		 * and wake the output routine.
+		 */
+		if(isr & (Txe|Ptx)){
+			r = regr(ctlr, Tsr);
+			if((isr & Txe) && (r & (Cdh|Fu|Crs|Abt))){
+				print("dp8390: Tsr %#2.2ux", r);
+				ether->oerrs++;
+			}
+
+			regw(ctlr, Isr, Txe|Ptx);
+
+			if(isr & Ptx)
+				ether->outpackets++;
+			ctlr->txbusy = 0;
+			txstart(ether);
+		}
+
+		if(isr & Cnt){
+			ether->frames += regr(ctlr, Ref0);
+			ether->crcs += regr(ctlr, Ref1);
+			ether->buffs += regr(ctlr, Ref2);
+			regw(ctlr, Isr, Cnt);
+		}
+	}
+	regw(ctlr, Imr, Cnt|Ovw|Txe|Rxe|Ptx|Prx);
+	iunlock(ctlr);
+}
+
+static uchar allmar[8] = { 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff };
+
+static void
+setfilter(Ether *ether, Dp8390 *ctlr)
+{
+	uchar r, cr;
+	int i;
+	uchar *mar;
+
+	r = Ab;
+	mar = 0;
+	if(ether->prom){
+		r |= Pro|Am;
+		mar = allmar;
+	} else if(ether->nmaddr){
+		r |= Am;
+		mar = ctlr->mar;
+	}
+	if(mar){
+		cr = regr(ctlr, Cr) & ~Txp;
+		regw(ctlr, Cr, Page1|(~(Ps1|Ps0) & cr));
+		for(i = 0; i < 8; i++)
+			regw(ctlr, Mar0+i, *(mar++));
+		regw(ctlr, Cr, cr);
+	}
+	regw(ctlr, Rcr, r);
+}
+
+static void
+promiscuous(void *arg, int )
+{
+	Ether *ether;
+	Dp8390 *ctlr;
+
+	ether = arg;
+	ctlr = ether->ctlr;
+
+	ilock(ctlr);
+	setfilter(ether, ctlr);
+	iunlock(ctlr);
+}
+
+static void
+setbit(Dp8390 *ctlr, int bit, int on)
+{
+	int i, h;
+
+	i = bit/8;
+	h = bit%8;
+	if(on){
+		if(++(ctlr->mref[bit]) == 1)
+			ctlr->mar[i] |= 1<<h;
+	} else {
+		if(--(ctlr->mref[bit]) <= 0){
+			ctlr->mref[bit] = 0;
+			ctlr->mar[i] &= ~(1<<h);
+		}
+	}
+}
+
+static uchar reverse[64];
+
+static void
+multicast(void* arg, uchar *addr, int on)
+{
+	Ether *ether;
+	Dp8390 *ctlr;
+	int i;
+	ulong h;
+
+	ether = arg;
+	ctlr = ether->ctlr;
+	if(reverse[1] == 0){
+		for(i = 0; i < 64; i++)
+			reverse[i] = ((i&1)<<5) | ((i&2)<<3) | ((i&4)<<1)
+				   | ((i&8)>>1) | ((i&16)>>3) | ((i&32)>>5);
+	}
+
+	/*
+	 *  change filter bits
+	 */
+	h = ethercrc(addr, 6);
+	ilock(ctlr);
+	setbit(ctlr, reverse[h&0x3f], on);
+	setfilter(ether, ctlr);
+	iunlock(ctlr);
+}
+
+static void
+attach(Ether* ether)
+{
+	Dp8390 *ctlr;
+	uchar r;
+
+	ctlr = ether->ctlr;
+
+	/*
+	 * Enable the chip for transmit/receive.
+	 * The init routine leaves the chip in monitor
+	 * mode. Clear the missed-packet counter, it
+	 * increments while in monitor mode.
+	 * Sometimes there's an interrupt pending at this
+	 * point but there's nothing in the Isr, so
+	 * any pending interrupts are cleared and the
+	 * mask of acceptable interrupts is enabled here.
+	 */
+	r = Ab;
+	if(ether->prom)
+		r |= Pro;
+	if(ether->nmaddr)
+		r |= Am;
+	ilock(ctlr);
+	regw(ctlr, Isr, 0xFF);
+	regw(ctlr, Imr, Cnt|Ovw|Txe|Rxe|Ptx|Prx);
+	regw(ctlr, Rcr, r);
+	r = regr(ctlr, Ref2);
+	regw(ctlr, Tcr, LpbkNORMAL);
+	iunlock(ctlr);
+	USED(r);
+}
+
+static void
+disable(Dp8390* ctlr)
+{
+	int timo;
+
+	/*
+	 * Stop the chip. Set the Stp bit and wait for the chip
+	 * to finish whatever was on its tiny mind before it sets
+	 * the Rst bit.
+	 * The timeout is needed because there may not be a real
+	 * chip there if this is called when probing for a device
+	 * at boot.
+	 */
+	regw(ctlr, Cr, Page0|RdABORT|Stp);
+	regw(ctlr, Rbcr0, 0);
+	regw(ctlr, Rbcr1, 0);
+	for(timo = 10000; (regr(ctlr, Isr) & Rst) == 0 && timo; timo--)
+			;
+}
+
+int
+dp8390reset(Ether* ether)
+{
+	Dp8390 *ctlr;
+
+	ctlr = ether->ctlr;
+
+	/*
+	 * This is the initialisation procedure described
+	 * as 'mandatory' in the datasheet, with references
+	 * to the 3C503 technical reference manual.
+	 */ 
+	disable(ctlr);
+	if(ctlr->width != 1)
+		regw(ctlr, Dcr, Ft4WORD|Ls|Wts);
+	else
+		regw(ctlr, Dcr, Ft4WORD|Ls);
+
+	regw(ctlr, Rbcr0, 0);
+	regw(ctlr, Rbcr1, 0);
+
+	regw(ctlr, Tcr, LpbkNIC);
+	regw(ctlr, Rcr, Mon);
+
+	/*
+	 * Init the ring hardware and software ring pointers.
+	 * Can't initialise ethernet address as it may not be
+	 * known yet.
+	 */
+	ringinit(ctlr);
+	regw(ctlr, Tpsr, ctlr->tstart);
+
+	/*
+	 * Clear any pending interrupts and mask then all off.
+	 */
+	regw(ctlr, Isr, 0xFF);
+	regw(ctlr, Imr, 0);
+
+	/*
+	 * Leave the chip initialised,
+	 * but in monitor mode.
+	 */
+	regw(ctlr, Cr, Page0|RdABORT|Sta);
+
+	/*
+	 * Set up the software configuration.
+	 */
+	ether->attach = attach;
+	ether->transmit = transmit;
+	ether->interrupt = interrupt;
+	ether->ifstat = 0;
+
+	ether->promiscuous = promiscuous;
+	ether->multicast = multicast;
+	ether->arg = ether;
+
+	return 0;
+}
--- /dev/null
+++ b/os/pc/ether8390.h
@@ -1,0 +1,74 @@
+/*
+ * Ctlr for the boards using the National Semiconductor DP8390
+ * and SMC 83C90 Network Interface Controller.
+ * Common code is in ether8390.c.
+ */
+typedef struct {
+	Lock;
+
+	ulong	port;			/* I/O address of 8390 */
+	ulong	data;			/* I/O data port if no shared memory */
+
+	uchar	width;			/* data transfer width in bytes */
+	uchar	ram;			/* true if card has shared memory */
+	uchar	dummyrr;		/* do dummy remote read */
+
+	uchar	nxtpkt;			/* receive: software bndry */
+	uchar	pstart;
+	uchar	pstop;
+
+	int	txbusy;			/* transmit */
+	uchar	tstart;			/* 8390 ring addresses */
+
+	uchar	mar[8];			/* shadow multicast address registers */
+	int	mref[64];		/* reference counts for multicast groups */
+} Dp8390;
+
+#define Dp8390BufSz	256
+
+extern int dp8390reset(Ether*);
+extern void *dp8390read(Dp8390*, void*, ulong, ulong);
+extern void dp8390getea(Ether*, uchar*);
+extern void dp8390setea(Ether*);
+
+/*
+ * x86-specific code.
+ */
+#define regr(c, r)	inb((c)->port+(r))
+#define regw(c, r, v)	outb((c)->port+(r), (v))
+
+static void
+rdread(Dp8390* ctlr, void* to, int len)
+{
+	switch(ctlr->width){
+	default:
+		panic("dp8390 rdread: width %d\n", ctlr->width);
+		break;
+
+	case 2:
+		inss(ctlr->data, to, len/2);
+		break;
+
+	case 1:
+		insb(ctlr->data, to, len);
+		break;
+	}
+}
+
+static void
+rdwrite(Dp8390* ctlr, void* from, int len)
+{
+	switch(ctlr->width){
+	default:
+		panic("dp8390 rdwrite: width %d\n", ctlr->width);
+		break;
+
+	case 2:
+		outss(ctlr->data, from, len/2);
+		break;
+
+	case 1:
+		outsb(ctlr->data, from, len);
+		break;
+	}
+}
--- /dev/null
+++ b/os/pc/etherdp83820.c
@@ -1,0 +1,1246 @@
+/*
+ * National Semiconductor DP83820
+ * 10/100/1000 Mb/s Ethernet Network Interface Controller
+ * (Gig-NIC).
+ * Driver assumes little-endian and 32-bit host throughout.
+ */
+#include "u.h"
+#include "../port/lib.h"
+#include "mem.h"
+#include "dat.h"
+#include "fns.h"
+#include "io.h"
+#include "../port/error.h"
+#include "../port/netif.h"
+
+#include "etherif.h"
+#include "ethermii.h"
+
+enum {					/* Registers */
+	Cr		= 0x00,		/* Command */
+	Cfg		= 0x04,		/* Configuration and Media Status */
+	Mear		= 0x08,		/* MII/EEPROM Access */
+	Ptscr		= 0x0C,		/* PCI Test Control */
+	Isr		= 0x10,		/* Interrupt Status */
+	Imr		= 0x14,		/* Interrupt Mask */
+	Ier		= 0x18,		/* Interrupt Enable */
+	Ihr		= 0x1C,		/* Interrupt Holdoff */
+	Txdp		= 0x20,		/* Transmit Descriptor Pointer */
+	Txdphi		= 0x24,		/* Transmit Descriptor Pointer Hi */
+	Txcfg		= 0x28,		/* Transmit Configuration */
+	Gpior		= 0x2C,		/* General Purpose I/O Control */
+	Rxdp		= 0x30,		/* Receive Descriptor Pointer */
+	Rxdphi		= 0x34,		/* Receive Descriptor Pointer Hi */
+	Rxcfg		= 0x38,		/* Receive Configuration */
+	Pqcr		= 0x3C,		/* Priority Queueing Control */
+	Wcsr		= 0x40,		/* Wake on LAN Control/Status */
+	Pcr		= 0x44,		/* Pause Control/Status */
+	Rfcr		= 0x48,		/* Receive Filter/Match Control */
+	Rfdr		= 0x4C,		/* Receive Filter/Match Data */
+	Brar		= 0x50,		/* Boot ROM Address */
+	Brdr		= 0x54,		/* Boot ROM Data */
+	Srr		= 0x58,		/* Silicon Revision */
+	Mibc		= 0x5C,		/* MIB Control */
+	Mibd		= 0x60,		/* MIB Data */
+	Txdp1		= 0xA0,		/* Txdp Priority 1 */
+	Txdp2		= 0xA4,		/* Txdp Priority 2 */
+	Txdp3		= 0xA8,		/* Txdp Priority 3 */
+	Rxdp1		= 0xB0,		/* Rxdp Priority 1 */
+	Rxdp2		= 0xB4,		/* Rxdp Priority 2 */
+	Rxdp3		= 0xB8,		/* Rxdp Priority 3 */
+	Vrcr		= 0xBC,		/* VLAN/IP Receive Control */
+	Vtcr		= 0xC0,		/* VLAN/IP Transmit Control */
+	Vdr		= 0xC4,		/* VLAN Data */
+	Ccsr		= 0xCC,		/* Clockrun Control/Status */
+	Tbicr		= 0xE0,		/* TBI Control */
+	Tbisr		= 0xE4,		/* TBI Status */
+	Tanar		= 0xE8,		/* TBI ANAR */
+	Tanlpar		= 0xEC,		/* TBI ANLPAR */
+	Taner		= 0xF0,		/* TBI ANER */
+	Tesr		= 0xF4,		/* TBI ESR */
+};
+
+enum {					/* Cr */
+	Txe		= 0x00000001,	/* Transmit Enable */
+	Txd		= 0x00000002,	/* Transmit Disable */
+	Rxe		= 0x00000004,	/* Receiver Enable */
+	Rxd		= 0x00000008,	/* Receiver Disable */
+	Txr		= 0x00000010,	/* Transmitter Reset */
+	Rxr		= 0x00000020,	/* Receiver Reset */
+	Swien		= 0x00000080,	/* Software Interrupt Enable */
+	Rst		= 0x00000100,	/* Reset */
+	TxpriSHFT	= 9,		/* Tx Priority Queue Select */
+	TxpriMASK	= 0x00001E00,
+	RxpriSHFT	= 13,		/* Rx Priority Queue Select */
+	RxpriMASK	= 0x0001E000,
+};
+
+enum {					/* Configuration and Media Status */
+	Bem		= 0x00000001,	/* Big Endian Mode */
+	Ext125		= 0x00000002,	/* External 125MHz reference Select */
+	Bromdis		= 0x00000004,	/* Disable Boot ROM interface */
+	Pesel		= 0x00000008,	/* Parity Error Detection Action */
+	Exd		= 0x00000010,	/* Excessive Deferral Abort */
+	Pow		= 0x00000020,	/* Program Out of Window Timer */
+	Sb		= 0x00000040,	/* Single Back-off */
+	Reqalg		= 0x00000080,	/* PCI Bus Request Algorithm */
+	Extstsen	= 0x00000100,	/* Extended Status Enable */
+	Phydis		= 0x00000200,	/* Disable PHY */
+	Phyrst		= 0x00000400,	/* Reset PHY */
+	M64addren	= 0x00000800,	/* Master 64-bit Addressing Enable */
+	Data64en	= 0x00001000,	/* 64-bit Data Enable */
+	Pci64det	= 0x00002000,	/* PCI 64-bit Bus Detected */
+	T64addren	= 0x00004000,	/* Target 64-bit Addressing Enable */
+	Mwidis		= 0x00008000,	/* MWI Disable */
+	Mrmdis		= 0x00010000,	/* MRM Disable */
+	Tmrtest		= 0x00020000,	/* Timer Test Mode */
+	Spdstsien	= 0x00040000,	/* PHY Spdsts Interrupt Enable */
+	Lnkstsien	= 0x00080000,	/* PHY Lnksts Interrupt Enable */
+	Dupstsien	= 0x00100000,	/* PHY Dupsts Interrupt Enable */
+	Mode1000	= 0x00400000,	/* 1000Mb/s Mode Control */
+	Tbien		= 0x01000000,	/* Ten-Bit Interface Enable */
+	Dupsts		= 0x10000000,	/* Full Duplex Status */
+	Spdsts100	= 0x20000000,	/* SPEED100 Input Pin Status */
+	Spdsts1000	= 0x40000000,	/* SPEED1000 Input Pin Status */
+	Lnksts		= 0x80000000,	/* Link Status */
+};
+
+enum {					/* MII/EEPROM Access */
+	Eedi		= 0x00000001,	/* EEPROM Data In */
+	Eedo		= 0x00000002,	/* EEPROM Data Out */
+	Eeclk		= 0x00000004,	/* EEPROM Serial Clock */
+	Eesel		= 0x00000008,	/* EEPROM Chip Select */
+	Mdio		= 0x00000010,	/* MII Management Data */
+	Mddir		= 0x00000020,	/* MII Management Direction */
+	Mdc		= 0x00000040,	/* MII Management Clock */
+};
+
+enum {					/* Interrupts */
+	Rxok		= 0x00000001,	/* Rx OK */
+	Rxdesc		= 0x00000002,	/* Rx Descriptor */
+	Rxerr		= 0x00000004,	/* Rx Packet Error */
+	Rxearly		= 0x00000008,	/* Rx Early Threshold */
+	Rxidle		= 0x00000010,	/* Rx Idle */
+	Rxorn		= 0x00000020,	/* Rx Overrun */
+	Txok		= 0x00000040,	/* Tx Packet OK */
+	Txdesc		= 0x00000080,	/* Tx Descriptor */
+	Txerr		= 0x00000100,	/* Tx Packet Error */
+	Txidle		= 0x00000200,	/* Tx Idle */
+	Txurn		= 0x00000400,	/* Tx Underrun */
+	Mib		= 0x00000800,	/* MIB Service */
+	Swi		= 0x00001000,	/* Software Interrupt */
+	Pme		= 0x00002000,	/* Power Management Event */
+	Phy		= 0x00004000,	/* PHY Interrupt */
+	Hibint		= 0x00008000,	/* High Bits Interrupt Set */
+	Rxsovr		= 0x00010000,	/* Rx Status FIFO Overrun */
+	Rtabt		= 0x00020000,	/* Received Target Abort */
+	Rmabt		= 0x00040000,	/* Received Master Abort */
+	Sserr		= 0x00080000,	/* Signalled System Error */
+	Dperr		= 0x00100000,	/* Detected Parity Error */
+	Rxrcmp		= 0x00200000,	/* Receive Reset Complete */
+	Txrcmp		= 0x00400000,	/* Transmit Reset Complete */
+	Rxdesc0		= 0x00800000,	/* Rx Descriptor for Priority Queue 0 */
+	Rxdesc1		= 0x01000000,	/* Rx Descriptor for Priority Queue 1 */
+	Rxdesc2		= 0x02000000,	/* Rx Descriptor for Priority Queue 2 */
+	Rxdesc3		= 0x04000000,	/* Rx Descriptor for Priority Queue 3 */
+	Txdesc0		= 0x08000000,	/* Tx Descriptor for Priority Queue 0 */
+	Txdesc1		= 0x10000000,	/* Tx Descriptor for Priority Queue 1 */
+	Txdesc2		= 0x20000000,	/* Tx Descriptor for Priority Queue 2 */
+	Txdesc3		= 0x40000000,	/* Tx Descriptor for Priority Queue 3 */
+};
+
+enum {					/* Interrupt Enable */
+	Ien		= 0x00000001,	/* Interrupt Enable */
+};
+
+enum {					/* Interrupt Holdoff */
+	IhSHFT		= 0,		/* Interrupt Holdoff */
+	IhMASK		= 0x000000FF,
+	Ihctl		= 0x00000100,	/* Interrupt Holdoff Control */
+};
+
+enum {					/* Transmit Configuration */
+	TxdrthSHFT	= 0,		/* Tx Drain Threshold */
+	TxdrthMASK	= 0x000000FF,
+	FlthSHFT	= 16,		/* Tx Fill Threshold */
+	FlthMASK	= 0x0000FF00,
+	Brstdis		= 0x00080000,	/* 1000Mb/s Burst Disable */
+	MxdmaSHFT	= 20,		/* Max Size per Tx DMA Burst */
+	MxdmaMASK	= 0x00700000,
+	Ecretryen	= 0x00800000,	/* Excessive Collision Retry Enable */
+	Atp		= 0x10000000,	/* Automatic Transmit Padding */
+	Mlb		= 0x20000000,	/* MAC Loopback */
+	Hbi		= 0x40000000,	/* Heartbeat Ignore */
+	Csi		= 0x80000000,	/* Carrier Sense Ignore */
+};
+
+enum {					/* Receive Configuration */
+	RxdrthSHFT	= 1,		/* Rx Drain Threshold */
+	RxdrthMASK	= 0x0000003E,
+	Airl		= 0x04000000,	/* Accept In-Range Length Errored */
+	Alp		= 0x08000000,	/* Accept Long Packets */
+	Rxfd		= 0x10000000,	/* Receive Full Duplex */
+	Stripcrc	= 0x20000000,	/* Strip CRC */
+	Arp		= 0x40000000,	/* Accept Runt Packets */
+	Aep		= 0x80000000,	/* Accept Errored Packets */
+};
+
+enum {					/* Priority Queueing Control */
+	Txpqen		= 0x00000001,	/* Transmit Priority Queuing Enable */
+	Txfairen	= 0x00000002,	/* Transmit Fairness Enable */
+	RxpqenSHFT	= 2,		/* Receive Priority Queue Enable */
+	RxpqenMASK	= 0x0000000C,
+};
+
+enum {					/* Pause Control/Status */
+	PscntSHFT	= 0,		/* Pause Counter Value */
+	PscntMASK	= 0x0000FFFF,
+	Pstx		= 0x00020000,	/* Transmit Pause Frame */
+	PsffloSHFT	= 18,		/* Rx Data FIFO Lo Threshold */
+	PsffloMASK	= 0x000C0000,
+	PsffhiSHFT	= 20,		/* Rx Data FIFO Hi Threshold */
+	PsffhiMASK	= 0x00300000,
+	PsstloSHFT	= 22,		/* Rx Stat FIFO Hi Threshold */
+	PsstloMASK	= 0x00C00000,
+	PssthiSHFT	= 24,		/* Rx Stat FIFO Hi Threshold */
+	PssthiMASK	= 0x03000000,
+	Psrcvd		= 0x08000000,	/* Pause Frame Received */
+	Psact		= 0x10000000,	/* Pause Active */
+	Psda		= 0x20000000,	/* Pause on Destination Address */
+	Psmcast		= 0x40000000,	/* Pause on Multicast */
+	Psen		= 0x80000000,	/* Pause Enable */
+};
+
+enum {					/* Receive Filter/Match Control */
+	RfaddrSHFT	= 0,		/* Extended Register Address */
+	RfaddrMASK	= 0x000003FF,
+	Ulm		= 0x00080000,	/* U/L bit mask */
+	Uhen		= 0x00100000,	/* Unicast Hash Enable */
+	Mhen		= 0x00200000,	/* Multicast Hash Enable */
+	Aarp		= 0x00400000,	/* Accept ARP Packets */
+	ApatSHFT	= 23,		/* Accept on Pattern Match */
+	ApatMASK	= 0x07800000,
+	Apm		= 0x08000000,	/* Accept on Perfect Match */
+	Aau		= 0x10000000,	/* Accept All Unicast */
+	Aam		= 0x20000000,	/* Accept All Multicast */
+	Aab		= 0x40000000,	/* Accept All Broadcast */
+	Rfen		= 0x80000000,	/* Rx Filter Enable */
+};
+
+enum {					/* Receive Filter/Match Data */
+	RfdataSHFT	= 0,		/* Receive Filter Data */
+	RfdataMASK	= 0x0000FFFF,
+	BmaskSHFT	= 16,		/* Byte Mask */
+	BmaskMASK	= 0x00030000,
+};
+
+enum {					/* MIB Control */
+	Wrn		= 0x00000001,	/* Warning Test Indicator */
+	Frz		= 0x00000002,	/* Freeze All Counters */
+	Aclr		= 0x00000004,	/* Clear All Counters */
+	Mibs		= 0x00000008,	/* MIB Counter Strobe */
+};
+
+enum {					/* MIB Data */
+	Nmibd		= 11,		/* Number of MIB Data Registers */
+};
+
+enum {					/* VLAN/IP Receive Control */
+	Vtden		= 0x00000001,	/* VLAN Tag Detection Enable */
+	Vtren		= 0x00000002,	/* VLAN Tag Removal Enable */
+	Dvtf		= 0x00000004,	/* Discard VLAN Tagged Frames */
+	Dutf		= 0x00000008,	/* Discard Untagged Frames */
+	Ipen		= 0x00000010,	/* IP Checksum Enable */
+	Ripe		= 0x00000020,	/* Reject IP Checksum Errors */
+	Rtcpe		= 0x00000040,	/* Reject TCP Checksum Errors */
+	Rudpe		= 0x00000080,	/* Reject UDP Checksum Errors */
+};
+
+enum {					/* VLAN/IP Transmit Control */
+	Vgti		= 0x00000001,	/* VLAN Global Tag Insertion */
+	Vppti		= 0x00000002,	/* VLAN Per-Packet Tag Insertion */
+	Gchk		= 0x00000004,	/* Global Checksum Generation */
+	Ppchk		= 0x00000008,	/* Per-Packet Checksum Generation */
+};
+
+enum {					/* VLAN Data */
+	VtypeSHFT	= 0,		/* VLAN Type Field */
+	VtypeMASK	= 0x0000FFFF,
+	VtciSHFT	= 16,		/* VLAN Tag Control Information */
+	VtciMASK	= 0xFFFF0000,
+};
+
+enum {					/* Clockrun Control/Status */
+	Clkrunen	= 0x00000001,	/* CLKRUN Enable */
+	Pmeen		= 0x00000100,	/* PME Enable */
+	Pmests		= 0x00008000,	/* PME Status */
+};
+
+typedef struct {
+	u32int	link;			/* Link to the next descriptor */
+	u32int	bufptr;			/* pointer to data Buffer */
+	int	cmdsts;			/* Command/Status */
+	int	extsts;			/* optional Extended Status */
+
+	Block*	bp;			/* Block containing bufptr */
+	u32int	unused;			/* pad to 64-bit */
+} Desc;
+
+enum {					/* Common cmdsts bits */
+	SizeMASK	= 0x0000FFFF,	/* Descriptor Byte Count */
+	SizeSHFT	= 0,
+	Ok		= 0x08000000,	/* Packet OK */
+	Crc		= 0x10000000,	/* Suppress/Include CRC */
+	Intr		= 0x20000000,	/* Interrupt on ownership transfer */
+	More		= 0x40000000,	/* not last descriptor in a packet */
+	Own		= 0x80000000,	/* Descriptor Ownership */
+};
+
+enum {					/* Transmit cmdsts bits */
+	CcntMASK	= 0x000F0000,	/* Collision Count */
+	CcntSHFT	= 16,
+	Ec		= 0x00100000,	/* Excessive Collisions */
+	Owc		= 0x00200000,	/* Out of Window Collision */
+	Ed		= 0x00400000,	/* Excessive Deferral */
+	Td		= 0x00800000,	/* Transmit Deferred */
+	Crs		= 0x01000000,	/* Carrier Sense Lost */
+	Tfu		= 0x02000000,	/* Transmit FIFO Underrun */
+	Txa		= 0x04000000,	/* Transmit Abort */
+};
+
+enum {					/* Receive cmdsts bits */
+	Irl		= 0x00010000,	/* In-Range Length Error */
+	Lbp		= 0x00020000,	/* Loopback Packet */
+	Fae		= 0x00040000,	/* Frame Alignment Error */
+	Crce		= 0x00080000,	/* CRC Error */
+	Ise		= 0x00100000,	/* Invalid Symbol Error */
+	Runt		= 0x00200000,	/* Runt Packet Received */
+	Long		= 0x00400000,	/* Too Long Packet Received */
+	DestMASK	= 0x01800000,	/* Destination Class */
+	DestSHFT	= 23,
+	Rxo		= 0x02000000,	/* Receive Overrun */
+	Rxa		= 0x04000000,	/* Receive Aborted */
+};
+
+enum {					/* extsts bits */
+	EvtciMASK	= 0x0000FFFF,	/* VLAN Tag Control Information */
+	EvtciSHFT	= 0,
+	Vpkt		= 0x00010000,	/* VLAN Packet */
+	Ippkt		= 0x00020000,	/* IP Packet */
+	Iperr		= 0x00040000,	/* IP Checksum Error */
+	Tcppkt		= 0x00080000,	/* TCP Packet */
+	Tcperr		= 0x00100000,	/* TCP Checksum Error */
+	Udppkt		= 0x00200000,	/* UDP Packet */
+	Udperr		= 0x00400000,	/* UDP Checksum Error */
+};
+
+enum {
+	Nrd		= 256,
+	Nrb		= 4*Nrd,
+	Rbsz		= ROUNDUP(sizeof(Etherpkt)+8, 8),
+	Ntd		= 128,
+};
+
+typedef struct Ctlr Ctlr;
+typedef struct Ctlr {
+	int	port;
+	Pcidev*	pcidev;
+	Ctlr*	next;
+	int	active;
+	int	id;
+
+	int	eepromsz;		/* address size in bits */
+	ushort*	eeprom;
+
+	int*	nic;
+	int	cfg;
+	int	imr;
+
+	QLock	alock;			/* attach */
+	Lock	ilock;			/* init */
+	void*	alloc;			/* base of per-Ctlr allocated data */
+
+	Mii*	mii;
+
+	Lock	rdlock;			/* receive */
+	Desc*	rd;
+	int	nrd;
+	int	nrb;
+	int	rdx;
+	int	rxcfg;
+
+	Lock	tlock;			/* transmit */
+	Desc*	td;
+	int	ntd;
+	int	tdh;
+	int	tdt;
+	int	ntq;
+	int	txcfg;
+
+	int	rxidle;
+
+	uint	mibd[Nmibd];
+
+	int	ec;
+	int	owc;
+	int	ed;
+	int	crs;
+	int	tfu;
+	int	txa;
+} Ctlr;
+
+#define csr32r(c, r)	(*((c)->nic+((r)/4)))
+#define csr32w(c, r, v)	(*((c)->nic+((r)/4)) = (v))
+
+static Ctlr* dp83820ctlrhead;
+static Ctlr* dp83820ctlrtail;
+
+static Lock dp83820rblock;		/* free receive Blocks */
+static Block* dp83820rbpool;
+
+static char* dp83820mibs[Nmibd] = {
+	"RXErroredPkts",
+	"RXFCSErrors",
+	"RXMsdPktErrors",
+	"RXFAErrors",
+	"RXSymbolErrors",
+	"RXFrameToLong",
+	"RXIRLErrors",
+	"RXBadOpcodes",
+	"RXPauseFrames",
+	"TXPauseFrames",
+	"TXSQEErrors",
+};
+
+static int
+mdior(Ctlr* ctlr, int n)
+{
+	int data, i, mear, r;
+
+	mear = csr32r(ctlr, Mear);
+	r = ~(Mdc|Mddir) & mear;
+	data = 0;
+	for(i = n-1; i >= 0; i--){
+		if(csr32r(ctlr, Mear) & Mdio)
+			data |= (1<<i);
+		csr32w(ctlr, Mear, Mdc|r);
+		csr32w(ctlr, Mear, r);
+	}
+	csr32w(ctlr, Mear, mear);
+
+	return data;
+}
+
+static void
+mdiow(Ctlr* ctlr, int bits, int n)
+{
+	int i, mear, r;
+
+	mear = csr32r(ctlr, Mear);
+	r = Mddir|(~Mdc & mear);
+	for(i = n-1; i >= 0; i--){
+		if(bits & (1<<i))
+			r |= Mdio;
+		else
+			r &= ~Mdio;
+		csr32w(ctlr, Mear, r);
+		csr32w(ctlr, Mear, Mdc|r);
+	}
+	csr32w(ctlr, Mear, mear);
+}
+
+static int
+dp83820miimir(Mii* mii, int pa, int ra)
+{
+	int data;
+	Ctlr *ctlr;
+
+	ctlr = mii->ctlr;
+
+	/*
+	 * MII Management Interface Read.
+	 *
+	 * Preamble;
+	 * ST+OP+PA+RA;
+	 * LT + 16 data bits.
+	 */
+	mdiow(ctlr, 0xFFFFFFFF, 32);
+	mdiow(ctlr, 0x1800|(pa<<5)|ra, 14);
+	data = mdior(ctlr, 18);
+
+	if(data & 0x10000)
+		return -1;
+
+	return data & 0xFFFF;
+}
+
+static int
+dp83820miimiw(Mii* mii, int pa, int ra, int data)
+{
+	Ctlr *ctlr;
+
+	ctlr = mii->ctlr;
+
+	/*
+	 * MII Management Interface Write.
+	 *
+	 * Preamble;
+	 * ST+OP+PA+RA+LT + 16 data bits;
+	 * Z.
+	 */
+	mdiow(ctlr, 0xFFFFFFFF, 32);
+	data &= 0xFFFF;
+	data |= (0x05<<(5+5+2+16))|(pa<<(5+2+16))|(ra<<(2+16))|(0x02<<16);
+	mdiow(ctlr, data, 32);
+
+	return 0;
+}
+
+static Block *
+dp83820rballoc(Desc* desc)
+{
+	Block *bp;
+
+	if(desc->bp == nil){
+		ilock(&dp83820rblock);
+		if((bp = dp83820rbpool) == nil){
+			iunlock(&dp83820rblock);
+			desc->bp = nil;
+			desc->cmdsts = Own;
+			return nil;
+		}
+		dp83820rbpool = bp->next;
+		bp->next = nil;
+		iunlock(&dp83820rblock);
+	
+		desc->bufptr = PCIWADDR(bp->rp);
+		desc->bp = bp;
+	}
+	else{
+		bp = desc->bp;
+		bp->rp = bp->lim - Rbsz;
+		bp->wp = bp->rp;
+	}
+
+	coherence();
+	desc->cmdsts = Intr|Rbsz;
+
+	return bp;
+}
+
+static void
+dp83820rbfree(Block *bp)
+{
+	bp->rp = bp->lim - Rbsz;
+	bp->wp = bp->rp;
+
+	ilock(&dp83820rblock);
+	bp->next = dp83820rbpool;
+	dp83820rbpool = bp;
+	iunlock(&dp83820rblock);
+}
+
+static void
+dp83820halt(Ctlr* ctlr)
+{
+	int i, timeo;
+
+	ilock(&ctlr->ilock);
+	csr32w(ctlr, Imr, 0);
+	csr32w(ctlr, Ier, 0);
+	csr32w(ctlr, Cr, Rxd|Txd);
+	for(timeo = 0; timeo < 1000; timeo++){
+		if(!(csr32r(ctlr, Cr) & (Rxe|Txe)))
+			break;
+		microdelay(1);
+	}
+	csr32w(ctlr, Mibc, Frz);
+	iunlock(&ctlr->ilock);
+
+	if(ctlr->rd != nil){
+		for(i = 0; i < ctlr->nrd; i++){
+			if(ctlr->rd[i].bp == nil)
+				continue;
+			freeb(ctlr->rd[i].bp);
+			ctlr->rd[i].bp = nil;
+		}
+	}
+	if(ctlr->td != nil){
+		for(i = 0; i < ctlr->ntd; i++){
+			if(ctlr->td[i].bp == nil)
+				continue;
+			freeb(ctlr->td[i].bp);
+			ctlr->td[i].bp = nil;
+		}
+	}
+}
+
+static void
+dp83820cfg(Ctlr* ctlr)
+{
+	int cfg;
+
+	/*
+	 * Don't know how to deal with a TBI yet.
+	 */
+	if(ctlr->mii == nil)
+		return;
+
+	/*
+	 * The polarity of these bits is at the mercy
+	 * of the board designer.
+	 * The correct answer for all speed and duplex questions
+	 * should be to query the phy.
+	 */
+	cfg = csr32r(ctlr, Cfg);
+	if(!(cfg & Dupsts)){
+		ctlr->rxcfg |= Rxfd;
+		ctlr->txcfg |= Csi|Hbi;
+		iprint("83820: full duplex, ");
+	}
+	else{
+		ctlr->rxcfg &= ~Rxfd;
+		ctlr->txcfg &= ~(Csi|Hbi);
+		iprint("83820: half duplex, ");
+	}
+	csr32w(ctlr, Rxcfg, ctlr->rxcfg);
+	csr32w(ctlr, Txcfg, ctlr->txcfg);
+
+	switch(cfg & (Spdsts1000|Spdsts100)){
+	case Spdsts1000:		/* 100Mbps */
+	default:			/* 10Mbps */
+		ctlr->cfg &= ~Mode1000;
+		if((cfg & (Spdsts1000|Spdsts100)) == Spdsts1000)
+			iprint("100Mb/s\n");
+		else
+			iprint("10Mb/s\n");
+		break;
+	case Spdsts100:			/* 1Gbps */
+		ctlr->cfg |= Mode1000;
+		iprint("1Gb/s\n");
+		break;
+	}
+	csr32w(ctlr, Cfg, ctlr->cfg);
+}
+
+static void
+dp83820init(Ether* edev)
+{
+	int i;
+	Ctlr *ctlr;
+	Desc *desc;
+	uchar *alloc;
+
+	ctlr = edev->ctlr;
+
+	dp83820halt(ctlr);
+
+	/*
+	 * Receiver
+	 */
+	alloc = (uchar*)ROUNDUP((ulong)ctlr->alloc, 8);
+	ctlr->rd = (Desc*)alloc;
+	alloc += ctlr->nrd*sizeof(Desc);
+	memset(ctlr->rd, 0, ctlr->nrd*sizeof(Desc));
+	ctlr->rdx = 0;
+	for(i = 0; i < ctlr->nrd; i++){
+		desc = &ctlr->rd[i];
+		desc->link = PCIWADDR(&ctlr->rd[NEXT(i, ctlr->nrd)]);
+		if(dp83820rballoc(desc) == nil)
+			continue;
+	}
+	csr32w(ctlr, Rxdphi, 0);
+	csr32w(ctlr, Rxdp, PCIWADDR(ctlr->rd));
+
+	for(i = 0; i < Eaddrlen; i += 2){
+		csr32w(ctlr, Rfcr, i);
+		csr32w(ctlr, Rfdr, (edev->ea[i+1]<<8)|edev->ea[i]);
+	}
+	csr32w(ctlr, Rfcr, Rfen|Aab|Aam|Apm);
+
+	ctlr->rxcfg = Stripcrc|(((2*(ETHERMINTU+4))/8)<<RxdrthSHFT);
+	ctlr->imr |= Rxorn|Rxidle|Rxearly|Rxdesc|Rxok;
+
+	/*
+	 * Transmitter.
+	 */
+	ctlr->td = (Desc*)alloc;
+	memset(ctlr->td, 0, ctlr->ntd*sizeof(Desc));
+	ctlr->tdh = ctlr->tdt = ctlr->ntq = 0;
+	for(i = 0; i < ctlr->ntd; i++){
+		desc = &ctlr->td[i];
+		desc->link = PCIWADDR(&ctlr->td[NEXT(i, ctlr->ntd)]);
+	}
+	csr32w(ctlr, Txdphi, 0);
+	csr32w(ctlr, Txdp, PCIWADDR(ctlr->td));
+
+	ctlr->txcfg = Atp|(((2*(ETHERMINTU+4))/32)<<FlthSHFT)|((4096/32)<<TxdrthSHFT);
+	ctlr->imr |= Txurn|Txidle|Txdesc|Txok;
+
+	ilock(&ctlr->ilock);
+
+	dp83820cfg(ctlr);
+
+	csr32w(ctlr, Mibc, Aclr);
+	ctlr->imr |= Mib;
+
+	csr32w(ctlr, Imr, ctlr->imr);
+
+	/* try coalescing adjacent interrupts; use hold-off interval of 100µs */
+	csr32w(ctlr, Ihr, Ihctl|(1<<IhSHFT));
+
+	csr32w(ctlr, Ier, Ien);
+	csr32w(ctlr, Cr, Rxe|Txe);
+
+	iunlock(&ctlr->ilock);
+}
+
+static void
+dp83820attach(Ether* edev)
+{
+	Block *bp;
+	Ctlr *ctlr;
+
+	ctlr = edev->ctlr;
+	qlock(&ctlr->alock);
+	if(ctlr->alloc != nil){
+		qunlock(&ctlr->alock);
+		return;
+	}
+
+	if(waserror()){
+		if(ctlr->mii != nil){
+			free(ctlr->mii);
+			ctlr->mii = nil;
+		}
+		if(ctlr->alloc != nil){
+			free(ctlr->alloc);
+			ctlr->alloc = nil;
+		}
+		qunlock(&ctlr->alock);
+		nexterror();
+	}
+
+	if(!(ctlr->cfg & Tbien)){
+		if((ctlr->mii = malloc(sizeof(Mii))) == nil)
+			error(Enomem);
+		ctlr->mii->ctlr = ctlr;
+		ctlr->mii->mir = dp83820miimir;
+		ctlr->mii->miw = dp83820miimiw;
+		if(mii(ctlr->mii, ~0) == 0)
+			error("no PHY");
+		ctlr->cfg |= Dupstsien|Lnkstsien|Spdstsien;
+		ctlr->imr |= Phy;
+	}
+
+	ctlr->nrd = Nrd;
+	ctlr->nrb = Nrb;
+	ctlr->ntd = Ntd;
+	ctlr->alloc = mallocz((ctlr->nrd+ctlr->ntd)*sizeof(Desc) + 7, 0);
+	if(ctlr->alloc == nil)
+		error(Enomem);
+
+	for(ctlr->nrb = 0; ctlr->nrb < Nrb; ctlr->nrb++){
+		if((bp = allocb(Rbsz)) == nil)
+			break;
+		bp->free = dp83820rbfree;
+		dp83820rbfree(bp);
+	}
+
+	dp83820init(edev);
+
+	qunlock(&ctlr->alock);
+	poperror();
+}
+
+static void
+dp83820transmit(Ether* edev)
+{
+	Block *bp;
+	Ctlr *ctlr;
+	Desc *desc;
+	int cmdsts, r, x;
+
+	ctlr = edev->ctlr;
+
+	ilock(&ctlr->tlock);
+
+	bp = nil;
+	for(x = ctlr->tdh; ctlr->ntq; x = NEXT(x, ctlr->ntd)){
+		desc = &ctlr->td[x];
+		if((cmdsts = desc->cmdsts) & Own)
+			break;
+		if(!(cmdsts & Ok)){
+			if(cmdsts & Ec)
+				ctlr->ec++;
+			if(cmdsts & Owc)
+				ctlr->owc++;
+			if(cmdsts & Ed)
+				ctlr->ed++;
+			if(cmdsts & Crs)
+				ctlr->crs++;
+			if(cmdsts & Tfu)
+				ctlr->tfu++;
+			if(cmdsts & Txa)
+				ctlr->txa++;
+			edev->oerrs++;
+		}
+		desc->bp->next = bp;
+		bp = desc->bp;
+		desc->bp = nil;
+
+		ctlr->ntq--;
+	}
+	ctlr->tdh = x;
+	if(bp != nil)
+		freeblist(bp);
+
+	x = ctlr->tdt;
+	while(ctlr->ntq < (ctlr->ntd-1)){
+		if((bp = qget(edev->oq)) == nil)
+			break;
+
+		desc = &ctlr->td[x];
+		desc->bufptr = PCIWADDR(bp->rp);
+		desc->bp = bp;
+		ctlr->ntq++;
+		coherence();
+		desc->cmdsts = Own|Intr|BLEN(bp);
+
+		x = NEXT(x, ctlr->ntd);
+	}
+	if(x != ctlr->tdt){
+		ctlr->tdt = x;
+		r = csr32r(ctlr, Cr);
+		csr32w(ctlr, Cr, Txe|r);
+	}
+
+	iunlock(&ctlr->tlock);
+}
+
+static void
+dp83820interrupt(Ureg*, void* arg)
+{
+	Block *bp;
+	Ctlr *ctlr;
+	Desc *desc;
+	Ether *edev;
+	int cmdsts, i, isr, r, x;
+
+	edev = arg;
+	ctlr = edev->ctlr;
+
+	for(isr = csr32r(ctlr, Isr); isr & ctlr->imr; isr = csr32r(ctlr, Isr)){
+		if(isr & (Rxorn|Rxidle|Rxearly|Rxerr|Rxdesc|Rxok)){
+			x = ctlr->rdx;
+			desc = &ctlr->rd[x];
+			while((cmdsts = desc->cmdsts) & Own){
+				if((cmdsts & Ok) && desc->bp != nil){
+					bp = desc->bp;
+					desc->bp = nil;
+					bp->wp += cmdsts & SizeMASK;
+					etheriq(edev, bp, 1);
+				}
+				//else if(!(cmdsts & Ok)){
+				//	iprint("dp83820: rx %8.8uX:", cmdsts);
+				//	bp = desc->bp;
+				//	for(i = 0; i < 20; i++)
+				//		iprint(" %2.2uX", bp->rp[i]);
+				//	iprint("\n");
+				//}
+				dp83820rballoc(desc);
+
+				x = NEXT(x, ctlr->nrd);
+				desc = &ctlr->rd[x];
+			}
+			ctlr->rdx = x;
+
+			if(isr & Rxidle){
+				r = csr32r(ctlr, Cr);
+				csr32w(ctlr, Cr, Rxe|r);
+				ctlr->rxidle++;
+			}
+
+			isr &= ~(Rxorn|Rxidle|Rxearly|Rxerr|Rxdesc|Rxok);
+		}
+
+		if(isr & Txurn){
+			x = (ctlr->txcfg & TxdrthMASK)>>TxdrthSHFT;
+			r = (ctlr->txcfg & FlthMASK)>>FlthSHFT;
+			if(x < ((TxdrthMASK)>>TxdrthSHFT)
+			&& x < (2048/32 - r)){
+				ctlr->txcfg &= ~TxdrthMASK;
+				x++;
+				ctlr->txcfg |= x<<TxdrthSHFT;
+				csr32w(ctlr, Txcfg, ctlr->txcfg);
+			}
+		}
+
+		if(isr & (Txurn|Txidle|Txdesc|Txok)){
+			dp83820transmit(edev);
+			isr &= ~(Txurn|Txidle|Txdesc|Txok);
+		}
+
+		if(isr & Mib){
+			for(i = 0; i < Nmibd; i++){
+				r = csr32r(ctlr, Mibd+(i*sizeof(int)));
+				ctlr->mibd[i] += r & 0xFFFF;
+			}
+			isr &= ~Mib;
+		}
+
+		if((isr & Phy) && ctlr->mii != nil){
+			ctlr->mii->mir(ctlr->mii, 1, Bmsr);
+			print("phy: cfg %8.8uX bmsr %4.4uX\n",
+				csr32r(ctlr, Cfg),
+				ctlr->mii->mir(ctlr->mii, 1, Bmsr));
+			dp83820cfg(ctlr);
+			isr &= ~Phy;
+		}
+		if(isr)
+			iprint("dp83820: isr %8.8uX\n", isr);
+	}
+}
+
+static long
+dp83820ifstat(Ether* edev, void* a, long n, ulong offset)
+{
+	char *p;
+	Ctlr *ctlr;
+	int i, l, r;
+
+	ctlr = edev->ctlr;
+
+	edev->crcs = ctlr->mibd[Mibd+(1*sizeof(int))];
+	edev->frames = ctlr->mibd[Mibd+(3*sizeof(int))];
+	edev->buffs = ctlr->mibd[Mibd+(5*sizeof(int))];
+	edev->overflows = ctlr->mibd[Mibd+(2*sizeof(int))];
+
+	if(n == 0)
+		return 0;
+
+	p = malloc(READSTR);
+	l = 0;
+	for(i = 0; i < Nmibd; i++){
+		r = csr32r(ctlr, Mibd+(i*sizeof(int)));
+		ctlr->mibd[i] += r & 0xFFFF;
+		if(ctlr->mibd[i] != 0 && dp83820mibs[i] != nil)
+			l += snprint(p+l, READSTR-l, "%s: %ud %ud\n",
+				dp83820mibs[i], ctlr->mibd[i], r);
+	}
+	l += snprint(p+l, READSTR-l, "rxidle %d\n", ctlr->rxidle);
+	l += snprint(p+l, READSTR-l, "ec %d\n", ctlr->ec);
+	l += snprint(p+l, READSTR-l, "owc %d\n", ctlr->owc);
+	l += snprint(p+l, READSTR-l, "ed %d\n", ctlr->ed);
+	l += snprint(p+l, READSTR-l, "crs %d\n", ctlr->crs);
+	l += snprint(p+l, READSTR-l, "tfu %d\n", ctlr->tfu);
+	l += snprint(p+l, READSTR-l, "txa %d\n", ctlr->txa);
+
+	l += snprint(p+l, READSTR, "rom:");
+	for(i = 0; i < 0x10; i++){
+		if(i && ((i & 0x07) == 0))
+			l += snprint(p+l, READSTR-l, "\n    ");
+		l += snprint(p+l, READSTR-l, " %4.4uX", ctlr->eeprom[i]);
+	}
+	l += snprint(p+l, READSTR-l, "\n");
+
+	if(ctlr->mii != nil && ctlr->mii->curphy != nil){
+		l += snprint(p+l, READSTR, "phy:");
+		for(i = 0; i < NMiiPhyr; i++){
+			if(i && ((i & 0x07) == 0))
+				l += snprint(p+l, READSTR-l, "\n    ");
+			r = miimir(ctlr->mii, i);
+			l += snprint(p+l, READSTR-l, " %4.4uX", r);
+		}
+		snprint(p+l, READSTR-l, "\n");
+	}
+
+	n = readstr(offset, a, n, p);
+	free(p);
+
+	return n;
+}
+
+static void
+dp83820promiscuous(void* arg, int on)
+{
+	USED(arg, on);
+}
+
+/* multicast already on, don't need to do anything */
+static void
+dp83820multicast(void*, uchar*, int)
+{
+}
+
+static int
+dp83820detach(Ctlr* ctlr)
+{
+	/*
+	 * Soft reset the controller.
+	 */
+	csr32w(ctlr, Cr, Rst);
+	delay(1);
+	while(csr32r(ctlr, Cr) & Rst)
+		delay(1);
+	return 0;
+}
+
+static void
+dp83820shutdown(Ether* ether)
+{
+print("dp83820shutdown\n");
+	dp83820detach(ether->ctlr);
+}
+
+static int
+atc93c46r(Ctlr* ctlr, int address)
+{
+	int data, i, mear, r, size;
+
+	/*
+	 * Analog Technology, Inc. ATC93C46
+	 * or equivalent serial EEPROM.
+	 */
+	mear = csr32r(ctlr, Mear);
+	mear &= ~(Eesel|Eeclk|Eedo|Eedi);
+	r = Eesel|mear;
+
+reread:
+	csr32w(ctlr, Mear, r);
+	data = 0x06;
+	for(i = 3-1; i >= 0; i--){
+		if(data & (1<<i))
+			r |= Eedi;
+		else
+			r &= ~Eedi;
+		csr32w(ctlr, Mear, r);
+		csr32w(ctlr, Mear, Eeclk|r);
+		microdelay(1);
+		csr32w(ctlr, Mear, r);
+		microdelay(1);
+	}
+
+	/*
+	 * First time through must work out the EEPROM size.
+	 */
+	if((size = ctlr->eepromsz) == 0)
+		size = 8;
+
+	for(size = size-1; size >= 0; size--){
+		if(address & (1<<size))
+			r |= Eedi;
+		else
+			r &= ~Eedi;
+		csr32w(ctlr, Mear, r);
+		microdelay(1);
+		csr32w(ctlr, Mear, Eeclk|r);
+		microdelay(1);
+		csr32w(ctlr, Mear, r);
+		microdelay(1);
+		if(!(csr32r(ctlr, Mear) & Eedo))
+			break;
+	}
+	r &= ~Eedi;
+
+	data = 0;
+	for(i = 16-1; i >= 0; i--){
+		csr32w(ctlr, Mear, Eeclk|r);
+		microdelay(1);
+		if(csr32r(ctlr, Mear) & Eedo)
+			data |= (1<<i);
+		csr32w(ctlr, Mear, r);
+		microdelay(1);
+	}
+
+	csr32w(ctlr, Mear, mear);
+
+	if(ctlr->eepromsz == 0){
+		ctlr->eepromsz = 8-size;
+		ctlr->eeprom = malloc((1<<ctlr->eepromsz)*sizeof(ushort));
+		goto reread;
+	}
+
+	return data;
+}
+
+static int
+dp83820reset(Ctlr* ctlr)
+{
+	int i, r;
+	unsigned char sum;
+
+	/*
+	 * Soft reset the controller;
+	 * read the EEPROM to get the initial settings
+	 * of the Cfg and Gpior bits which should be cleared by
+	 * the reset.
+	 */
+	dp83820detach(ctlr);
+
+	atc93c46r(ctlr, 0);
+	if(ctlr->eeprom == nil) {
+		print("dp83820reset: no eeprom\n");
+		return -1;
+	}
+	sum = 0;
+	for(i = 0; i < 0x0E; i++){
+		r = atc93c46r(ctlr, i);
+		ctlr->eeprom[i] = r;
+		sum += r;
+		sum += r>>8;
+	}
+
+	if(sum != 0){
+		print("dp83820reset: bad EEPROM checksum\n");
+		return -1;
+	}
+
+#ifdef notdef
+	csr32w(ctlr, Gpior, ctlr->eeprom[4]);
+
+	cfg = Extstsen|Exd;
+	r = csr32r(ctlr, Cfg);
+	if(ctlr->eeprom[5] & 0x0001)
+		cfg |= Ext125;
+	if(ctlr->eeprom[5] & 0x0002)
+		cfg |= M64addren;
+	if((ctlr->eeprom[5] & 0x0004) && (r & Pci64det))
+		cfg |= Data64en;
+	if(ctlr->eeprom[5] & 0x0008)
+		cfg |= T64addren;
+	if(!(pcicfgr16(ctlr->pcidev, PciPCR) & 0x10))
+		cfg |= Mwidis;
+	if(ctlr->eeprom[5] & 0x0020)
+		cfg |= Mrmdis;
+	if(ctlr->eeprom[5] & 0x0080)
+		cfg |= Mode1000;
+	if(ctlr->eeprom[5] & 0x0200)
+		cfg |= Tbien|Mode1000;
+	/*
+	 * What about RO bits we might have destroyed with Rst?
+	 * What about Exd, Tmrtest, Extstsen, Pintctl?
+	 * Why does it think it has detected a 64-bit bus when
+	 * it hasn't?
+	 */
+#else
+	//r = csr32r(ctlr, Cfg);
+	//r &= ~(Mode1000|T64addren|Data64en|M64addren);
+	//csr32w(ctlr, Cfg, r);
+	//csr32w(ctlr, Cfg, 0x2000);
+#endif /* notdef */
+	ctlr->cfg = csr32r(ctlr, Cfg);
+print("cfg %8.8uX pcicfg %8.8uX\n", ctlr->cfg, pcicfgr32(ctlr->pcidev, PciPCR));
+	ctlr->cfg &= ~(T64addren|Data64en|M64addren);
+	csr32w(ctlr, Cfg, ctlr->cfg);
+	csr32w(ctlr, Mibc, Aclr|Frz);
+
+	return 0;
+}
+
+static void
+dp83820pci(void)
+{
+	void *mem;
+	Pcidev *p;
+	Ctlr *ctlr;
+
+	p = nil;
+	while(p = pcimatch(p, 0, 0)){
+		if(p->ccrb != Pcibcnet || p->ccru != Pciscether)
+			continue;
+
+		switch((p->did<<16)|p->vid){
+		default:
+			continue;
+		case (0x0022<<16)|0x100B:	/* DP83820 (Gig-NIC) */
+			break;
+		}
+
+		mem = vmap(p->mem[1].bar & ~0x0F, p->mem[1].size);
+		if(mem == 0){
+			print("DP83820: can't map %8.8luX\n", p->mem[1].bar);
+			continue;
+		}
+
+		ctlr = malloc(sizeof(Ctlr));
+		ctlr->port = p->mem[1].bar & ~0x0F;
+		ctlr->pcidev = p;
+		ctlr->id = (p->did<<16)|p->vid;
+
+		ctlr->nic = mem;
+		if(dp83820reset(ctlr)){
+			free(ctlr);
+			continue;
+		}
+		pcisetbme(p);
+
+		if(dp83820ctlrhead != nil)
+			dp83820ctlrtail->next = ctlr;
+		else
+			dp83820ctlrhead = ctlr;
+		dp83820ctlrtail = ctlr;
+	}
+}
+
+static int
+dp83820pnp(Ether* edev)
+{
+	int i;
+	Ctlr *ctlr;
+	uchar ea[Eaddrlen];
+
+	if(dp83820ctlrhead == nil)
+		dp83820pci();
+
+	/*
+	 * Any adapter matches if no edev->port is supplied,
+	 * otherwise the ports must match.
+	 */
+	for(ctlr = dp83820ctlrhead; ctlr != nil; ctlr = ctlr->next){
+		if(ctlr->active)
+			continue;
+		if(edev->port == 0 || edev->port == ctlr->port){
+			ctlr->active = 1;
+			break;
+		}
+	}
+	if(ctlr == nil)
+		return -1;
+
+	edev->ctlr = ctlr;
+	edev->port = ctlr->port;
+	edev->irq = ctlr->pcidev->intl;
+	edev->tbdf = ctlr->pcidev->tbdf;
+	edev->mbps = 1000;
+
+	/*
+	 * Check if the adapter's station address is to be overridden.
+	 * If not, read it from the EEPROM and set in ether->ea prior to
+	 * loading the station address in the hardware.
+	 */
+	memset(ea, 0, Eaddrlen);
+	if(memcmp(ea, edev->ea, Eaddrlen) == 0)
+		for(i = 0; i < Eaddrlen/2; i++){
+			edev->ea[2*i] = ctlr->eeprom[0x0C-i];
+			edev->ea[2*i+1] = ctlr->eeprom[0x0C-i]>>8;
+		}
+
+	edev->attach = dp83820attach;
+	edev->transmit = dp83820transmit;
+	edev->interrupt = dp83820interrupt;
+	edev->ifstat = dp83820ifstat;
+
+	edev->arg = edev;
+	edev->promiscuous = dp83820promiscuous;
+	edev->multicast = dp83820multicast;
+	edev->shutdown = dp83820shutdown;
+
+	return 0;
+}
+
+void
+etherdp83820link(void)
+{
+	addethercard("DP83820", dp83820pnp);
+}
--- /dev/null
+++ b/os/pc/etherec2t.c
@@ -1,0 +1,174 @@
+/*
+ * Supposed NE2000 PCMCIA clones, see the comments in ether2000.c
+ */
+#include "u.h"
+#include "../port/lib.h"
+#include "mem.h"
+#include "dat.h"
+#include "fns.h"
+#include "io.h"
+#include "../port/error.h"
+#include "../port/netif.h"
+
+#include "etherif.h"
+#include "ether8390.h"
+
+enum {
+	Data		= 0x10,		/* offset from I/O base of data port */
+	Reset		= 0x1F,		/* offset from I/O base of reset port */
+};
+
+typedef struct Ec2t {
+	char*	name;
+	int	iochecksum;
+} Ec2t;
+
+static Ec2t ec2tpcmcia[] = {
+	{ "EC2T", 0, },			/* Linksys Combo PCMCIA EthernetCard */
+	{ "PCMPC100", 1, },		/* EtherFast 10/100 PC Card */
+	{ "PCM100", 1, },		/* EtherFast PCM100 Card */
+	{ "EN2216", 0, },		/* Accton EtherPair-PCMCIA */
+	{ "FA410TX", 1, },		/* Netgear FA410TX */
+	{ "Network Everywhere", 0, },	/* Linksys NP10T 10BaseT Card */
+	{ "10/100 Port Attached", 1, },	/* SMC 8040TX */
+	{ "8041TX-10/100-PC-Card-V2", 0 }, /* SMC 8041TX */
+	{ "FA411", 0 },			/* Netgear FA411 PCMCIA */
+	{ nil, 0, },
+};
+
+static int
+reset(Ether* ether)
+{
+	ushort buf[16];
+	ulong port;
+	Dp8390 *ctlr;
+	int i, slot;
+	uchar ea[Eaddrlen], sum, x;
+	Ec2t *ec2t, tmpec2t;
+
+	/*
+	 * Set up the software configuration.
+	 * Use defaults for port, irq, mem and size
+	 * if not specified.
+	 * The manual says 16KB memory, the box
+	 * says 32KB. The manual seems to be correct.
+	 */
+	if(ether->port == 0)
+		ether->port = 0x300;
+	if(ether->irq == 0)
+		ether->irq = 9;
+	if(ether->mem == 0)
+		ether->mem = 0x4000;
+	if(ether->size == 0)
+		ether->size = 16*1024;
+	port = ether->port;
+
+	if(ioalloc(ether->port, 0x20, 0, "ec2t") < 0)
+		return -1;
+	slot = -1;
+	for(ec2t = ec2tpcmcia; ec2t->name != nil; ec2t++){
+		if((slot = pcmspecial(ec2t->name, ether)) >= 0)
+			break;
+	}
+	if(ec2t->name == nil){
+		ec2t = &tmpec2t;
+		ec2t->name = nil;
+		ec2t->iochecksum = 0;
+		for(i = 0; i < ether->nopt; i++){
+			if(cistrncmp(ether->opt[i], "id=", 3) == 0){
+				ec2t->name = &ether->opt[i][3];
+				slot = pcmspecial(ec2t->name, ether);
+			}
+			else if(cistrncmp(ether->opt[i], "iochecksum", 10) == 0)
+				ec2t->iochecksum = 1;
+		}
+	}
+	if(slot < 0){
+		iofree(port);
+		return -1;
+	}
+
+	ether->ctlr = malloc(sizeof(Dp8390));
+	ctlr = ether->ctlr;
+	ctlr->width = 2;
+	ctlr->ram = 0;
+
+	ctlr->port = port;
+	ctlr->data = port+Data;
+
+	ctlr->tstart = HOWMANY(ether->mem, Dp8390BufSz);
+	ctlr->pstart = ctlr->tstart + HOWMANY(sizeof(Etherpkt), Dp8390BufSz);
+	ctlr->pstop = ctlr->tstart + HOWMANY(ether->size, Dp8390BufSz);
+
+	ctlr->dummyrr = 0;
+	for(i = 0; i < ether->nopt; i++){
+		if(cistrcmp(ether->opt[i], "nodummyrr") == 0)
+			ctlr->dummyrr = 0;
+		else if(cistrncmp(ether->opt[i], "dummyrr=", 8) == 0)
+			ctlr->dummyrr = strtol(&ether->opt[i][8], nil, 0);
+	}
+
+	/*
+	 * Reset the board. This is done by doing a read
+	 * followed by a write to the Reset address.
+	 */
+	buf[0] = inb(port+Reset);
+	delay(2);
+	outb(port+Reset, buf[0]);
+	delay(2);
+
+	/*
+	 * Init the (possible) chip, then use the (possible)
+	 * chip to read the (possible) PROM for ethernet address
+	 * and a marker byte.
+	 * Could just look at the DP8390 command register after
+	 * initialisation has been tried, but that wouldn't be
+	 * enough, there are other ethernet boards which could
+	 * match.
+	 */
+	dp8390reset(ether);
+	sum = 0;
+	if(ec2t->iochecksum){
+		/*
+		 * These cards have the ethernet address in I/O space.
+		 * There's a checksum over 8 bytes which sums to 0xFF.
+		 */
+		for(i = 0; i < 8; i++){
+			x = inb(port+0x14+i);
+			sum += x;
+			buf[i] = (x<<8)|x;
+		}
+	}
+	else{
+		memset(buf, 0, sizeof(buf));
+		dp8390read(ctlr, buf, 0, sizeof(buf));
+		if((buf[0x0E] & 0xFF) == 0x57 && (buf[0x0F] & 0xFF) == 0x57)
+			sum = 0xFF;
+	}
+	if(sum != 0xFF){
+		pcmspecialclose(slot);
+		iofree(ether->port);
+		free(ether->ctlr);
+		return -1;
+	}
+
+	/*
+	 * Stupid machine. Shorts were asked for,
+	 * shorts were delivered, although the PROM is a byte array.
+	 * Set the ethernet address.
+	 */
+	memset(ea, 0, Eaddrlen);
+	if(memcmp(ea, ether->ea, Eaddrlen) == 0){
+		for(i = 0; i < sizeof(ether->ea); i++)
+			ether->ea[i] = buf[i];
+	}
+	dp8390setea(ether);
+
+	return 0;
+}
+
+void
+etherec2tlink(void)
+{
+	addethercard("EC2T", reset);
+}
--- /dev/null
+++ b/os/pc/etherelnk3.c
@@ -1,0 +1,2134 @@
+/*
+ * Etherlink III, Fast EtherLink and Fast EtherLink XL adapters.
+ * To do:
+ *	check robustness in the face of errors (e.g. busmaster & rxUnderrun);
+ *	RxEarly and busmaster;
+ *	autoSelect;
+ *	PCI latency timer and master enable;
+ *	errata list;
+ *	rewrite all initialisation.
+ */
+#include "u.h"
+#include "../port/lib.h"
+#include "mem.h"
+#include "dat.h"
+#include "fns.h"
+#include "io.h"
+#include "../port/error.h"
+#include "../port/netif.h"
+
+#include "etherif.h"
+
+#define XCVRDEBUG		if(0)print
+
+enum {
+	IDport			= 0x0110,	/* anywhere between 0x0100 and 0x01F0 */
+};
+
+enum {						/* all windows */
+	CommandR		= 0x000E,
+	IntStatusR		= 0x000E,
+};
+
+enum {						/* Commands */
+	GlobalReset		= 0x0000,
+	SelectRegisterWindow	= 0x0001,
+	EnableDcConverter	= 0x0002,
+	RxDisable		= 0x0003,
+	RxEnable		= 0x0004,
+	RxReset			= 0x0005,
+	Stall			= 0x0006,	/* 3C90x */
+	TxDone			= 0x0007,
+	RxDiscard		= 0x0008,
+	TxEnable		= 0x0009,
+	TxDisable		= 0x000A,
+	TxReset			= 0x000B,
+	RequestInterrupt	= 0x000C,
+	AcknowledgeInterrupt	= 0x000D,
+	SetInterruptEnable	= 0x000E,
+	SetIndicationEnable	= 0x000F,	/* SetReadZeroMask */
+	SetRxFilter		= 0x0010,
+	SetRxEarlyThresh	= 0x0011,
+	SetTxAvailableThresh	= 0x0012,
+	SetTxStartThresh	= 0x0013,
+	StartDma		= 0x0014,	/* initiate busmaster operation */
+	StatisticsEnable	= 0x0015,
+	StatisticsDisable	= 0x0016,
+	DisableDcConverter	= 0x0017,
+	SetTxReclaimThresh	= 0x0018,	/* PIO-only adapters */
+	PowerUp			= 0x001B,	/* not all adapters */
+	PowerDownFull		= 0x001C,	/* not all adapters */
+	PowerAuto		= 0x001D,	/* not all adapters */
+};
+
+enum {						/* (Global|Rx|Tx)Reset command bits */
+	tpAuiReset		= 0x0001,	/* 10BaseT and AUI transceivers */
+	endecReset		= 0x0002,	/* internal Ethernet encoder/decoder */
+	networkReset		= 0x0004,	/* network interface logic */
+	fifoReset		= 0x0008,	/* FIFO control logic */
+	aismReset		= 0x0010,	/* autoinitialise state-machine logic */
+	hostReset		= 0x0020,	/* bus interface logic */
+	dmaReset		= 0x0040,	/* bus master logic */
+	vcoReset		= 0x0080,	/* on-board 10Mbps VCO */
+	updnReset		= 0x0100,	/* upload/download (Rx/TX) logic */
+
+	resetMask		= 0x01FF,
+};
+
+enum {						/* Stall command bits */
+	upStall			= 0x0000,
+	upUnStall		= 0x0001,
+	dnStall			= 0x0002,
+	dnUnStall		= 0x0003,
+};
+
+enum {						/* SetRxFilter command bits */
+	receiveIndividual	= 0x0001,	/* match station address */
+	receiveMulticast	= 0x0002,
+	receiveBroadcast	= 0x0004,
+	receiveAllFrames	= 0x0008,	/* promiscuous */
+};
+
+enum {						/* StartDma command bits */
+	Upload			= 0x0000,	/* transfer data from adapter to memory */
+	Download		= 0x0001,	/* transfer data from memory to adapter */
+};
+
+enum {						/* IntStatus bits */
+	interruptLatch		= 0x0001,
+	hostError		= 0x0002,	/* Adapter Failure */
+	txComplete		= 0x0004,
+	txAvailable		= 0x0008,
+	rxComplete		= 0x0010,
+	rxEarly			= 0x0020,
+	intRequested		= 0x0040,
+	updateStats		= 0x0080,
+	transferInt		= 0x0100,	/* Bus Master Transfer Complete */
+	dnComplete		= 0x0200,
+	upComplete		= 0x0400,
+	busMasterInProgress	= 0x0800,
+	commandInProgress	= 0x1000,
+
+	interruptMask		= 0x07FE,
+};
+
+#define COMMAND(port, cmd, a)	outs((port)+CommandR, ((cmd)<<11)|(a))
+#define STATUS(port)		ins((port)+IntStatusR)
+
+enum {						/* Window 0 - setup */
+	Wsetup			= 0x0000,
+						/* registers */
+	ManufacturerID		= 0x0000,	/* 3C5[08]*, 3C59[27] */
+	ProductID		= 0x0002,	/* 3C5[08]*, 3C59[27] */
+	ConfigControl		= 0x0004,	/* 3C5[08]*, 3C59[27] */
+	AddressConfig		= 0x0006,	/* 3C5[08]*, 3C59[27] */
+	ResourceConfig		= 0x0008,	/* 3C5[08]*, 3C59[27] */
+	EepromCommand		= 0x000A,
+	EepromData		= 0x000C,
+						/* AddressConfig Bits */
+	autoSelect9		= 0x0080,
+	xcvrMask9		= 0xC000,
+						/* ConfigControl bits */
+	Ena			= 0x0001,
+	base10TAvailable9	= 0x0200,
+	coaxAvailable9		= 0x1000,
+	auiAvailable9		= 0x2000,
+						/* EepromCommand bits */
+	EepromReadRegister	= 0x0080,
+	EepromReadOffRegister	= 0x00B0,
+	EepromRead8bRegister	= 0x0230,
+	EepromBusy		= 0x8000,
+};
+
+#define EEPROMCMD(port, cmd, a)	outs((port)+EepromCommand, (cmd)|(a))
+#define EEPROMBUSY(port)	(ins((port)+EepromCommand) & EepromBusy)
+#define EEPROMDATA(port)	ins((port)+EepromData)
+
+enum {						/* Window 1 - operating set */
+	Wop			= 0x0001,
+						/* registers */
+	Fifo			= 0x0000,
+	RxError			= 0x0004,	/* 3C59[0257] only */
+	RxStatus		= 0x0008,
+	TIMER			= 0x000A,
+	TxStatus		= 0x000B,
+	TxFree			= 0x000C,
+						/* RxError bits */
+	rxOverrun		= 0x0001,
+	runtFrame		= 0x0002,
+	alignmentError		= 0x0004,	/* Framing */
+	crcError		= 0x0008,
+	oversizedFrame		= 0x0010,
+	dribbleBits		= 0x0080,
+						/* RxStatus bits */
+	rxBytes			= 0x1FFF,	/* 3C59[0257] mask */
+	rxBytes9		= 0x07FF,	/* 3C5[078]9 mask */
+	rxError9		= 0x3800,	/* 3C5[078]9 error mask */
+	rxOverrun9		= 0x0000,
+	oversizedFrame9		= 0x0800,
+	dribbleBits9		= 0x1000,
+	runtFrame9		= 0x1800,
+	alignmentError9		= 0x2000,	/* Framing */
+	crcError9		= 0x2800,
+	rxError			= 0x4000,
+	rxIncomplete		= 0x8000,
+						/* TxStatus Bits */
+	txStatusOverflow	= 0x0004,
+	maxCollisions		= 0x0008,
+	txUnderrun		= 0x0010,
+	txJabber		= 0x0020,
+	interruptRequested	= 0x0040,
+	txStatusComplete	= 0x0080,
+};
+
+enum {						/* Window 2 - station address */
+	Wstation		= 0x0002,
+
+	ResetOp905B		= 0x000C,
+};
+
+enum {						/* Window 3 - FIFO management */
+	Wfifo			= 0x0003,
+						/* registers */
+	InternalConfig		= 0x0000,	/* 3C509B, 3C589, 3C59[0257] */
+	OtherInt		= 0x0004,	/* 3C59[0257] */
+	RomControl		= 0x0006,	/* 3C509B, 3C59[27] */
+	MacControl		= 0x0006,	/* 3C59[0257] */
+	ResetOptions		= 0x0008,	/* 3C59[0257] */
+	MediaOptions		= 0x0008,	/* 3C905B */
+	RxFree			= 0x000A,
+						/* InternalConfig bits */
+	disableBadSsdDetect	= 0x00000100,
+	ramLocation		= 0x00000200,	/* 0 external, 1 internal */
+	ramPartition5to3	= 0x00000000,
+	ramPartition3to1	= 0x00010000,
+	ramPartition1to1	= 0x00020000,
+	ramPartition3to5	= 0x00030000,
+	ramPartitionMask	= 0x00030000,
+	xcvr10BaseT		= 0x00000000,
+	xcvrAui			= 0x00100000,	/* 10BASE5 */
+	xcvr10Base2		= 0x00300000,
+	xcvr100BaseTX		= 0x00400000,
+	xcvr100BaseFX		= 0x00500000,
+	xcvrMii			= 0x00600000,
+	xcvrMask		= 0x00700000,
+	autoSelect		= 0x01000000,
+						/* MacControl bits */
+	deferExtendEnable	= 0x0001,
+	deferTIMERSelect	= 0x001E,	/* mask */
+	fullDuplexEnable	= 0x0020,
+	allowLargePackets	= 0x0040,
+	extendAfterCollision	= 0x0080,	/* 3C90xB */
+	flowControlEnable	= 0x0100,	/* 3C90xB */
+	vltEnable		= 0x0200,	/* 3C90xB */
+						/* ResetOptions bits */
+	baseT4Available		= 0x0001,
+	baseTXAvailable		= 0x0002,
+	baseFXAvailable		= 0x0004,
+	base10TAvailable	= 0x0008,
+	coaxAvailable		= 0x0010,
+	auiAvailable		= 0x0020,
+	miiConnector		= 0x0040,
+};
+
+enum {						/* Window 4 - diagnostic */
+	Wdiagnostic		= 0x0004,
+						/* registers */
+	VcoDiagnostic		= 0x0002,
+	FifoDiagnostic		= 0x0004,
+	NetworkDiagnostic	= 0x0006,
+	PhysicalMgmt		= 0x0008,
+	MediaStatus		= 0x000A,
+	BadSSD			= 0x000C,
+	UpperBytesOk		= 0x000D,
+						/* FifoDiagnostic bits */
+	txOverrun		= 0x0400,
+	rxUnderrun		= 0x2000,
+	receiving		= 0x8000,
+						/* PhysicalMgmt bits */
+	mgmtClk			= 0x0001,
+	mgmtData		= 0x0002,
+	mgmtDir			= 0x0004,
+	cat5LinkTestDefeat	= 0x8000,
+						/* MediaStatus bits */
+	dataRate100		= 0x0002,
+	crcStripDisable		= 0x0004,
+	enableSqeStats		= 0x0008,
+	collisionDetect		= 0x0010,
+	carrierSense		= 0x0020,
+	jabberGuardEnable	= 0x0040,
+	linkBeatEnable		= 0x0080,
+	jabberDetect		= 0x0200,
+	polarityReversed	= 0x0400,
+	linkBeatDetect		= 0x0800,
+	txInProg		= 0x1000,
+	dcConverterEnabled	= 0x4000,
+	auiDisable		= 0x8000,	/* 10BaseT transceiver selected */
+};
+
+enum {						/* Window 5 - internal state */
+	Wstate			= 0x0005,
+						/* registers */
+	TxStartThresh		= 0x0000,
+	TxAvailableThresh	= 0x0002,
+	RxEarlyThresh		= 0x0006,
+	RxFilter		= 0x0008,
+	InterruptEnable		= 0x000A,
+	IndicationEnable	= 0x000C,
+};
+
+enum {						/* Window 6 - statistics */
+	Wstatistics		= 0x0006,
+						/* registers */
+	CarrierLost		= 0x0000,
+	SqeErrors		= 0x0001,
+	MultipleColls		= 0x0002,
+	SingleCollFrames	= 0x0003,
+	LateCollisions		= 0x0004,
+	RxOverruns		= 0x0005,
+	FramesXmittedOk		= 0x0006,
+	FramesRcvdOk		= 0x0007,
+	FramesDeferred		= 0x0008,
+	UpperFramesOk		= 0x0009,
+	BytesRcvdOk		= 0x000A,
+	BytesXmittedOk		= 0x000C,
+};
+
+enum {						/* Window 7 - bus master operations */
+	Wmaster			= 0x0007,
+						/* registers */
+	MasterAddress		= 0x0000,
+	MasterLen		= 0x0006,
+	MasterStatus		= 0x000C,
+						/* MasterStatus bits */
+	masterAbort		= 0x0001,
+	targetAbort		= 0x0002,
+	targetRetry		= 0x0004,
+	targetDisc		= 0x0008,
+	masterDownload		= 0x1000,
+	masterUpload		= 0x4000,
+	masterInProgress	= 0x8000,
+
+	masterMask		= 0xD00F,
+};
+
+enum {						/* 3C90x extended register set */
+	TIMER905		= 0x001A,	/* 8-bits */
+	TxStatus905		= 0x001B,	/* 8-bits */
+	PktStatus		= 0x0020,	/* 32-bits */
+	DnListPtr		= 0x0024,	/* 32-bits, 8-byte aligned */
+	FragAddr		= 0x0028,	/* 32-bits */
+	FragLen			= 0x002C,	/* 16-bits */
+	ListOffset		= 0x002E,	/* 8-bits */
+	TxFreeThresh		= 0x002F,	/* 8-bits */
+	UpPktStatus		= 0x0030,	/* 32-bits */
+	FreeTIMER		= 0x0034,	/* 16-bits */
+	UpListPtr		= 0x0038,	/* 32-bits, 8-byte aligned */
+
+						/* PktStatus bits */
+	fragLast		= 0x00000001,
+	dnCmplReq		= 0x00000002,
+	dnStalled		= 0x00000004,
+	upCompleteX		= 0x00000008,
+	dnCompleteX		= 0x00000010,
+	upRxEarlyEnable		= 0x00000020,
+	armCountdown		= 0x00000040,
+	dnInProg		= 0x00000080,
+	counterSpeed		= 0x00000010,	/* 0 3.2uS, 1 320nS */
+	countdownMode		= 0x00000020,
+						/* UpPktStatus bits (dpd->control) */
+	upPktLenMask		= 0x00001FFF,
+	upStalled		= 0x00002000,
+	upError			= 0x00004000,
+	upPktComplete		= 0x00008000,
+	upOverrun		= 0x00010000,	/* RxError<<16 */
+	upRuntFrame		= 0x00020000,
+	upAlignmentError	= 0x00040000,
+	upCRCError		= 0x00080000,
+	upOversizedFrame	= 0x00100000,
+	upDribbleBits		= 0x00800000,
+	upOverflow		= 0x01000000,
+
+	dnIndicate		= 0x80000000,	/* FrameStartHeader (dpd->control) */
+
+	updnLastFrag		= 0x80000000,	/* (dpd->len) */
+
+	Nup			= 32,
+	Ndn			= 64,
+};
+
+/*
+ * Up/Dn Packet Descriptors.
+ * The hardware info (np, control, addr, len) must be 8-byte aligned
+ * and this structure size must be a multiple of 8.
+ */
+typedef struct Pd Pd;
+typedef struct Pd {
+	ulong	np;			/* next pointer */
+	ulong	control;		/* FSH or UpPktStatus */
+	ulong	addr;
+	ulong	len;
+
+	Pd*	next;
+	Block*	bp;
+} Pd;
+
+typedef struct Ctlr Ctlr;
+typedef struct Ctlr {
+	int	port;
+	Pcidev*	pcidev;
+	int	irq;
+	Ctlr*	next;
+	int	active;
+	int	did;
+
+	Lock	wlock;			/* window access */
+
+	int	attached;
+	int	busmaster;
+	Block*	rbp;			/* receive buffer */
+
+	Block*	txbp;			/* FIFO -based transmission */
+	int	txthreshold;
+	int	txbusy;
+
+	int	nup;			/* full-busmaster -based reception */
+	void*	upbase;
+	Pd*	upr;
+	Pd*	uphead;
+
+	int	ndn;			/* full-busmaster -based transmission */
+	void*	dnbase;
+	Pd*	dnr;
+	Pd*	dnhead;
+	Pd*	dntail;
+	int	dnq;
+
+	long	interrupts;		/* statistics */
+	long	bogusinterrupts;
+	long	timer[2];
+	long	stats[BytesRcvdOk+3];
+
+	int	upqmax;
+	int	upqmaxhw;
+	ulong	upinterrupts;
+	ulong	upqueued;
+	ulong	upstalls;
+	int	dnqmax;
+	int	dnqmaxhw;
+	ulong	dninterrupts;
+	ulong	dnqueued;
+
+	int	xcvr;			/* transceiver type */
+	int	eepromcmd;		/* EEPROM read command */
+	int	rxstatus9;		/* old-style RxStatus register */
+	int	rxearly;		/* RxEarlyThreshold */
+	int	ts;			/* threshold shift */
+	int	upenabled;
+	int	dnenabled;
+	ulong	cbfnpa;			/* CardBus functions */
+	ulong*	cbfn;
+} Ctlr;
+
+static Ctlr* ctlrhead;
+static Ctlr* ctlrtail;
+
+static void
+init905(Ctlr* ctlr)
+{
+	Block *bp;
+	Pd *pd, *prev;
+
+	/*
+	 * Create rings for the receive and transmit sides.
+	 * Take care with alignment:
+	 *	make sure ring base is 8-byte aligned;
+	 *	make sure each entry is 8-byte aligned.
+	 */
+	ctlr->upbase = malloc((ctlr->nup+1)*sizeof(Pd));
+	ctlr->upr = (Pd*)ROUNDUP((ulong)ctlr->upbase, 8);
+
+	prev = ctlr->upr;
+	for(pd = &ctlr->upr[ctlr->nup-1]; pd >= ctlr->upr; pd--){
+		pd->np = PADDR(&prev->np);
+		pd->control = 0;
+		bp = iallocb(sizeof(Etherpkt));
+		if(bp == nil)
+			panic("can't allocate ethernet receive ring");
+		pd->addr = PADDR(bp->rp);
+		pd->len = updnLastFrag|sizeof(Etherpkt);
+
+		pd->next = prev;
+		prev = pd;
+		pd->bp = bp;
+	}
+	ctlr->uphead = ctlr->upr;
+
+	ctlr->dnbase = malloc((ctlr->ndn+1)*sizeof(Pd));
+	ctlr->dnr = (Pd*)ROUNDUP((ulong)ctlr->dnbase, 8);
+
+	prev = ctlr->dnr;
+	for(pd = &ctlr->dnr[ctlr->ndn-1]; pd >= ctlr->dnr; pd--){
+		pd->next = prev;
+		prev = pd;
+	}
+	ctlr->dnhead = ctlr->dnr;
+	ctlr->dntail = ctlr->dnr;
+	ctlr->dnq = 0;
+}
+
+static Block*
+rbpalloc(Block* (*f)(int))
+{
+	Block *bp;
+	ulong addr;
+
+	/*
+	 * The receive buffers must be on a 32-byte
+	 * boundary for EISA busmastering.
+	 */
+	if(bp = f(ROUNDUP(sizeof(Etherpkt), 4) + 31)){
+		addr = (ulong)bp->base;
+		addr = ROUNDUP(addr, 32);
+		bp->rp = (uchar*)addr;
+	}
+
+	return bp;
+}
+
+static uchar*
+startdma(Ether* ether, ulong address)
+{
+	int port, status, w;
+	uchar *wp;
+
+	port = ether->port;
+
+	w = (STATUS(port)>>13) & 0x07;
+	COMMAND(port, SelectRegisterWindow, Wmaster);
+
+	wp = KADDR(inl(port+MasterAddress));
+	status = ins(port+MasterStatus);
+	if(status & (masterInProgress|targetAbort|masterAbort))
+		print("#l%d: BM status 0x%uX\n", ether->ctlrno, status);
+	outs(port+MasterStatus, masterMask);
+	outl(port+MasterAddress, address);
+	outs(port+MasterLen, sizeof(Etherpkt));
+	COMMAND(port, StartDma, Upload);
+
+	COMMAND(port, SelectRegisterWindow, w);
+	return wp;
+}
+
+static void
+promiscuous(void* arg, int on)
+{
+	int filter, port;
+	Ether *ether;
+
+	ether = (Ether*)arg;
+	port = ether->port;
+
+	filter = receiveBroadcast|receiveIndividual;
+	if(ether->nmaddr)
+		filter |= receiveMulticast;
+	if(on)
+		filter |= receiveAllFrames;
+	COMMAND(port, SetRxFilter, filter);
+}
+
+static void
+multicast(void* arg, uchar *addr, int on)
+{
+	int filter, port;
+	Ether *ether;
+
+	USED(addr, on);
+
+	ether = (Ether*)arg;
+	port = ether->port;
+
+	filter = receiveBroadcast|receiveIndividual;
+	if(ether->nmaddr)
+		filter |= receiveMulticast;
+	if(ether->prom)
+		filter |= receiveAllFrames;
+	COMMAND(port, SetRxFilter, filter);
+}
+
+/* On the 575B and C, interrupts need to be acknowledged in CardBus memory space */
+static void
+intrackcb(ulong *cbfn)
+{
+	cbfn[1] = 0x8000;
+}
+
+static void
+attach(Ether* ether)
+{
+	int port, x;
+	Ctlr *ctlr;
+
+	ctlr = ether->ctlr;
+	ilock(&ctlr->wlock);
+	if(ctlr->attached){
+		iunlock(&ctlr->wlock);
+		return;
+	}
+
+	port = ether->port;
+
+	/*
+	 * Set the receiver packet filter for this and broadcast addresses,
+	 * set the interrupt masks for all interrupts, enable the receiver
+	 * and transmitter.
+	 */
+	promiscuous(ether, ether->prom);
+
+	x = interruptMask;
+	if(ctlr->busmaster == 1)
+		x &= ~(rxEarly|rxComplete);
+	else{
+		if(ctlr->dnenabled)
+			x &= ~transferInt;
+		if(ctlr->upenabled)
+			x &= ~(rxEarly|rxComplete);
+	}
+	COMMAND(port, SetIndicationEnable, x);
+	COMMAND(port, SetInterruptEnable, x);
+	COMMAND(port, RxEnable, 0);
+	COMMAND(port, TxEnable, 0);
+
+	/*
+	 * If this is a CardBus card, acknowledge any interrupts.
+	 */
+	if(ctlr->cbfn != nil)
+		intrackcb(ctlr->cbfn);
+		
+	/*
+	 * Prime the busmaster channel for receiving directly into a
+	 * receive packet buffer if necessary.
+	 */
+	if(ctlr->busmaster == 1)
+		startdma(ether, PADDR(ctlr->rbp->rp));
+	else{
+		if(ctlr->upenabled)
+			outl(port+UpListPtr, PADDR(&ctlr->uphead->np));
+	}
+
+	ctlr->attached = 1;
+	iunlock(&ctlr->wlock);
+}
+
+static void
+statistics(Ether* ether)
+{
+	int port, i, u, w;
+	Ctlr *ctlr;
+
+	port = ether->port;
+	ctlr = ether->ctlr;
+
+	/*
+	 * 3C59[27] require a read between a PIO write and
+	 * reading a statistics register.
+	 */
+	w = (STATUS(port)>>13) & 0x07;
+	COMMAND(port, SelectRegisterWindow, Wstatistics);
+	STATUS(port);
+
+	for(i = 0; i < UpperFramesOk; i++)
+		ctlr->stats[i] += inb(port+i) & 0xFF;
+	u = inb(port+UpperFramesOk) & 0xFF;
+	ctlr->stats[FramesXmittedOk] += (u & 0x30)<<4;
+	ctlr->stats[FramesRcvdOk] += (u & 0x03)<<8;
+	ctlr->stats[BytesRcvdOk] += ins(port+BytesRcvdOk) & 0xFFFF;
+	ctlr->stats[BytesRcvdOk+1] += ins(port+BytesXmittedOk) & 0xFFFF;
+
+	switch(ctlr->xcvr){
+
+	case xcvrMii:
+	case xcvr100BaseTX:
+	case xcvr100BaseFX:
+		COMMAND(port, SelectRegisterWindow, Wdiagnostic);
+		STATUS(port);
+		ctlr->stats[BytesRcvdOk+2] += inb(port+BadSSD);
+		break;
+	}
+
+	COMMAND(port, SelectRegisterWindow, w);
+}
+
+static void
+txstart(Ether* ether)
+{
+	int port, len;
+	Ctlr *ctlr;
+	Block *bp;
+
+	port = ether->port;
+	ctlr = ether->ctlr;
+
+	/*
+	 * Attempt to top-up the transmit FIFO. If there's room simply
+	 * stuff in the packet length (unpadded to a dword boundary), the
+	 * packet data (padded) and remove the packet from the queue.
+	 * If there's no room post an interrupt for when there is.
+	 * This routine is called both from the top level and from interrupt
+	 * level and expects to be called with ctlr->wlock already locked
+	 * and the correct register window (Wop) in place.
+	 */
+	for(;;){
+		if(ctlr->txbp){
+			bp = ctlr->txbp;
+			ctlr->txbp = 0;
+		}
+		else{
+			bp = qget(ether->oq);
+			if(bp == nil)
+				break;
+		}
+
+		len = ROUNDUP(BLEN(bp), 4);
+		if(len+4 <= ins(port+TxFree)){
+			outl(port+Fifo, BLEN(bp));
+			outsl(port+Fifo, bp->rp, len/4);
+
+			freeb(bp);
+
+			ether->outpackets++;
+		}
+		else{
+			ctlr->txbp = bp;
+			if(ctlr->txbusy == 0){
+				ctlr->txbusy = 1;
+				COMMAND(port, SetTxAvailableThresh, len>>ctlr->ts);
+			}
+			break;
+		}
+	}
+}
+
+static void
+txstart905(Ether* ether)
+{
+	Ctlr *ctlr;
+	int port, stalled, timeo;
+	Block *bp;
+	Pd *pd;
+
+	ctlr = ether->ctlr;
+	port = ether->port;
+
+	/*
+	 * Free any completed packets.
+	 */
+	pd = ctlr->dntail;
+	while(ctlr->dnq){
+		if(PADDR(&pd->np) == inl(port+DnListPtr))
+			break;
+		if(pd->bp){
+			freeb(pd->bp);
+			pd->bp = nil;
+		}
+		ctlr->dnq--;
+		pd = pd->next;
+	}
+	ctlr->dntail = pd;
+
+	stalled = 0;
+	while(ctlr->dnq < (ctlr->ndn-1)){
+		bp = qget(ether->oq);
+		if(bp == nil)
+			break;
+
+		pd = ctlr->dnhead->next;
+		pd->np = 0;
+		pd->control = dnIndicate|BLEN(bp);
+		pd->addr = PADDR(bp->rp);
+		pd->len = updnLastFrag|BLEN(bp);
+		pd->bp = bp;
+
+		if(stalled == 0 && ctlr->dnq && inl(port+DnListPtr)){
+			COMMAND(port, Stall, dnStall);
+			for(timeo = 100; (STATUS(port) & commandInProgress) && timeo; timeo--)
+				;
+			if(timeo == 0)
+				print("#l%d: dnstall %d\n", ether->ctlrno, timeo);
+			stalled = 1;
+		}
+
+		coherence();
+		ctlr->dnhead->np = PADDR(&pd->np);
+		ctlr->dnhead->control &= ~dnIndicate;
+		ctlr->dnhead = pd;
+		if(ctlr->dnq == 0)
+			ctlr->dntail = pd;
+		ctlr->dnq++;
+
+		ctlr->dnqueued++;
+	}
+
+	if(ctlr->dnq > ctlr->dnqmax)
+		ctlr->dnqmax = ctlr->dnq;
+
+	/*
+	 * If the adapter is not currently processing anything
+	 * and there is something on the queue, start it processing.
+	 */
+	if(inl(port+DnListPtr) == 0 && ctlr->dnq)
+		outl(port+DnListPtr, PADDR(&ctlr->dnhead->np));
+	if(stalled)
+		COMMAND(port, Stall, dnUnStall);
+}
+
+static void
+transmit(Ether* ether)
+{
+	Ctlr *ctlr;
+	int port, w;
+
+	port = ether->port;
+	ctlr = ether->ctlr;
+
+	ilock(&ctlr->wlock);
+	if(ctlr->dnenabled)
+		txstart905(ether);
+	else{
+		w = (STATUS(port)>>13) & 0x07;
+		COMMAND(port, SelectRegisterWindow, Wop);
+		txstart(ether);
+		COMMAND(port, SelectRegisterWindow, w);
+	}
+	iunlock(&ctlr->wlock);
+}
+
+static void
+receive905(Ether* ether)
+{
+	Ctlr *ctlr;
+	int len, port, q;
+	Pd *pd;
+	Block *bp;
+
+	ctlr = ether->ctlr;
+	port = ether->port;
+
+	if(inl(port+UpPktStatus) & upStalled)
+		ctlr->upstalls++;
+	q = 0;
+	for(pd = ctlr->uphead; pd->control & upPktComplete; pd = pd->next){
+		if(pd->control & upError){
+			if(pd->control & upOverrun)
+				ether->overflows++;
+			if(pd->control & (upOversizedFrame|upRuntFrame))
+				ether->buffs++;
+			if(pd->control & upAlignmentError)
+				ether->frames++;
+			if(pd->control & upCRCError)
+				ether->crcs++;
+		}
+		else if(bp = iallocb(sizeof(Etherpkt)+4)){
+			len = pd->control & rxBytes;
+			pd->bp->wp = pd->bp->rp+len;
+			etheriq(ether, pd->bp, 1);
+			pd->bp = bp;
+			pd->addr = PADDR(bp->rp);
+			coherence();
+		}
+
+		pd->control = 0;
+		COMMAND(port, Stall, upUnStall);
+
+		q++;
+	}
+	ctlr->uphead = pd;
+
+	ctlr->upqueued += q;
+	if(q > ctlr->upqmax)
+		ctlr->upqmax = q;
+}
+
+static void
+receive(Ether* ether)
+{
+	int len, port, rxerror, rxstatus;
+	Ctlr *ctlr;
+	Block *bp;
+
+	port = ether->port;
+	ctlr = ether->ctlr;
+
+	while(((rxstatus = ins(port+RxStatus)) & rxIncomplete) == 0){
+		if(ctlr->busmaster == 1 && (STATUS(port) & busMasterInProgress))
+			break;
+
+		/*
+		 * If there was an error, log it and continue.
+		 * Unfortunately the 3C5[078]9 has the error info in the status register
+		 * and the 3C59[0257] implement a separate RxError register.
+		 */
+		if(rxstatus & rxError){
+			if(ctlr->rxstatus9){
+				switch(rxstatus & rxError9){
+
+				case rxOverrun9:
+					ether->overflows++;
+					break;
+
+				case oversizedFrame9:
+				case runtFrame9:
+					ether->buffs++;
+					break;
+
+				case alignmentError9:
+					ether->frames++;
+					break;
+
+				case crcError9:
+					ether->crcs++;
+					break;
+
+				}
+			}
+			else{
+				rxerror = inb(port+RxError);
+				if(rxerror & rxOverrun)
+					ether->overflows++;
+				if(rxerror & (oversizedFrame|runtFrame))
+					ether->buffs++;
+				if(rxerror & alignmentError)
+					ether->frames++;
+				if(rxerror & crcError)
+					ether->crcs++;
+			}
+		}
+
+		/*
+		 * If there was an error or a new receive buffer can't be
+		 * allocated, discard the packet and go on to the next.
+		 */
+		if((rxstatus & rxError) || (bp = rbpalloc(iallocb)) == 0){
+			COMMAND(port, RxDiscard, 0);
+			while(STATUS(port) & commandInProgress)
+				;
+
+			if(ctlr->busmaster == 1)
+				startdma(ether, PADDR(ctlr->rbp->rp));
+
+			continue;
+		}
+
+		/*
+		 * A valid receive packet awaits:
+		 *	if using PIO, read it into the buffer;
+		 *	discard the packet from the FIFO;
+		 *	if using busmastering, start a new transfer for
+		 *	  the next packet and as a side-effect get the
+		 *	  end-pointer of the one just received;
+		 *	pass the packet on to whoever wants it.
+		 */
+		if(ctlr->busmaster == 0 || ctlr->busmaster == 2){
+			len = (rxstatus & rxBytes9);
+			ctlr->rbp->wp = ctlr->rbp->rp + len;
+			insl(port+Fifo, ctlr->rbp->rp, HOWMANY(len, 4));
+		}
+
+		COMMAND(port, RxDiscard, 0);
+		while(STATUS(port) & commandInProgress)
+			;
+
+		if(ctlr->busmaster == 1)
+			ctlr->rbp->wp = startdma(ether, PADDR(bp->rp));
+
+		etheriq(ether, ctlr->rbp, 1);
+		ctlr->rbp = bp;
+	}
+}
+
+static int
+ejectable(int did)
+{
+	switch (did) {
+	case 0x5157:
+		return 1;
+
+	default:
+		return 0;
+	}
+}
+
+static void
+interrupt(Ureg*, void* arg)
+{
+	Ether *ether;
+	int port, status, s, txstatus, w, x;
+	Ctlr *ctlr;
+
+	ether = arg;
+	port = ether->port;
+	ctlr = ether->ctlr;
+
+	ilock(&ctlr->wlock);
+	status = STATUS(port);
+	if(!(status & (interruptMask|interruptLatch))){
+		ctlr->bogusinterrupts++;
+		iunlock(&ctlr->wlock);
+		return;
+	}
+	w = (status>>13) & 0x07;
+	COMMAND(port, SelectRegisterWindow, Wop);
+
+	ctlr->interrupts++;
+	if(ctlr->busmaster == 2)
+		ctlr->timer[0] += inb(port+TIMER905) & 0xFF;
+	else
+		ctlr->timer[0] += inb(port+TIMER) & 0xFF;
+
+	do{
+		if(status & hostError){
+			/*
+			 * Adapter failure, try to find out why, reset if
+			 * necessary. What happens if Tx is active and a reset
+			 * occurs, need to retransmit? This probably isn't right.
+			 */
+			COMMAND(port, SelectRegisterWindow, Wdiagnostic);
+			x = ins(port+FifoDiagnostic);
+			COMMAND(port, SelectRegisterWindow, Wop);
+	
+			if (status == 0xFFFF && x == 0xFFFF && ejectable(ctlr->did)) {
+				print("#l%d: Card ejected?\n", ether->ctlrno);
+				iunlock(&ctlr->wlock);
+				return;
+			}
+
+			print("#l%d: status 0x%uX, diag 0x%uX\n",
+			    ether->ctlrno, status, x);
+
+			if(x & txOverrun){
+				if(ctlr->busmaster == 0)
+					COMMAND(port, TxReset, 0);
+				else
+					COMMAND(port, TxReset, (updnReset|dmaReset));
+				COMMAND(port, TxEnable, 0);
+			}
+
+			if(x & rxUnderrun){
+				/*
+				 * This shouldn't happen...
+				 * Reset the receiver and restore the filter and RxEarly
+				 * threshold before re-enabling.
+				 * Need to restart any busmastering?
+				 */
+				COMMAND(port, SelectRegisterWindow, Wstate);
+				s = (port+RxFilter) & 0x000F;
+				COMMAND(port, SelectRegisterWindow, Wop);
+				COMMAND(port, RxReset, 0);
+				while(STATUS(port) & commandInProgress)
+					;
+				COMMAND(port, SetRxFilter, s);
+				COMMAND(port, SetRxEarlyThresh, ctlr->rxearly>>ctlr->ts);
+				COMMAND(port, RxEnable, 0);
+			}
+
+			status &= ~hostError;
+		}
+
+		if(status & (transferInt|rxComplete)){
+			receive(ether);
+			status &= ~(transferInt|rxComplete);
+		}
+
+		if(status & (upComplete)){
+			COMMAND(port, AcknowledgeInterrupt, upComplete);
+			receive905(ether);
+			status &= ~upComplete;
+			ctlr->upinterrupts++;
+		}
+
+		if(status & txComplete){
+			/*
+			 * Pop the TxStatus stack, accumulating errors.
+			 * Adjust the TX start threshold if there was an underrun.
+			 * If there was a Jabber or Underrun error, reset
+			 * the transmitter, taking care not to reset the dma logic
+			 * as a busmaster receive may be in progress.
+			 * For all conditions enable the transmitter.
+			 */
+			if(ctlr->busmaster == 2)
+				txstatus = port+TxStatus905;
+			else
+				txstatus = port+TxStatus;
+			s = 0;
+			do{
+				if(x = inb(txstatus))
+					outb(txstatus, 0);
+				s |= x;
+			}while(STATUS(port) & txComplete);
+
+			if(s & txUnderrun){
+				if(ctlr->dnenabled){
+					while(inl(port+PktStatus) & dnInProg)
+						;
+				}
+				COMMAND(port, SelectRegisterWindow, Wdiagnostic);
+				while(ins(port+MediaStatus) & txInProg)
+					;
+				COMMAND(port, SelectRegisterWindow, Wop);
+				if(ctlr->txthreshold < ETHERMAXTU)
+					ctlr->txthreshold += ETHERMINTU;
+			}
+
+			/*
+			 * According to the manual, maxCollisions does not require
+			 * a TxReset, merely a TxEnable. However, evidence points to
+			 * it being necessary on the 3C905. The jury is still out.
+			 * On busy or badly configured networks maxCollisions can
+			 * happen frequently enough for messages to be annoying so
+			 * keep quiet about them by popular request.
+			 */
+			if(s & (txJabber|txUnderrun|maxCollisions)){
+				if(ctlr->busmaster == 0)
+					COMMAND(port, TxReset, 0);
+				else
+					COMMAND(port, TxReset, (updnReset|dmaReset));
+				while(STATUS(port) & commandInProgress)
+					;
+				COMMAND(port, SetTxStartThresh, ctlr->txthreshold>>ctlr->ts);
+				if(ctlr->busmaster == 2)
+					outl(port+TxFreeThresh, HOWMANY(ETHERMAXTU, 256));
+				if(ctlr->dnenabled)
+					status |= dnComplete;
+			}
+
+			if(s & ~(txStatusComplete|maxCollisions))
+				print("#l%d: txstatus 0x%uX, threshold %d\n",
+			    		ether->ctlrno, s, ctlr->txthreshold);
+			COMMAND(port, TxEnable, 0);
+			ether->oerrs++;
+			status &= ~txComplete;
+			status |= txAvailable;
+		}
+
+		if(status & txAvailable){
+			COMMAND(port, AcknowledgeInterrupt, txAvailable);
+			ctlr->txbusy = 0;
+			txstart(ether);
+			status &= ~txAvailable;
+		}
+
+		if(status & dnComplete){
+			COMMAND(port, AcknowledgeInterrupt, dnComplete);
+			txstart905(ether);
+			status &= ~dnComplete;
+			ctlr->dninterrupts++;
+		}
+
+		if(status & updateStats){
+			statistics(ether);
+			status &= ~updateStats;
+		}
+
+		/*
+		 * Currently, this shouldn't happen.
+		 */
+		if(status & rxEarly){
+			COMMAND(port, AcknowledgeInterrupt, rxEarly);
+			status &= ~rxEarly;
+		}
+
+		/*
+		 * Panic if there are any interrupts not dealt with.
+		 */
+		if(status & interruptMask)
+			panic("#l%d: interrupt mask 0x%uX\n", ether->ctlrno, status);
+
+		COMMAND(port, AcknowledgeInterrupt, interruptLatch);
+		if(ctlr->cbfn != nil)
+			intrackcb(ctlr->cbfn);
+
+	}while((status = STATUS(port)) & (interruptMask|interruptLatch));
+
+	if(ctlr->busmaster == 2)
+		ctlr->timer[1] += inb(port+TIMER905) & 0xFF;
+	else
+		ctlr->timer[1] += inb(port+TIMER) & 0xFF;
+
+	COMMAND(port, SelectRegisterWindow, w);
+	iunlock(&ctlr->wlock);
+}
+
+static long
+ifstat(Ether* ether, void* a, long n, ulong offset)
+{
+	char *p;
+	int len;
+	Ctlr *ctlr;
+
+	if(n == 0)
+		return 0;
+
+	ctlr = ether->ctlr;
+
+	ilock(&ctlr->wlock);
+	statistics(ether);
+	iunlock(&ctlr->wlock);
+
+	p = malloc(READSTR);
+	len = snprint(p, READSTR, "interrupts: %lud\n", ctlr->interrupts);
+	len += snprint(p+len, READSTR-len, "bogusinterrupts: %lud\n", ctlr->bogusinterrupts);
+	len += snprint(p+len, READSTR-len, "timer: %lud %lud\n",
+		ctlr->timer[0], ctlr->timer[1]);
+	len += snprint(p+len, READSTR-len, "carrierlost: %lud\n",
+		ctlr->stats[CarrierLost]);
+	len += snprint(p+len, READSTR-len, "sqeerrors: %lud\n",
+		ctlr->stats[SqeErrors]);
+	len += snprint(p+len, READSTR-len, "multiplecolls: %lud\n",
+		ctlr->stats[MultipleColls]);
+	len += snprint(p+len, READSTR-len, "singlecollframes: %lud\n",
+		ctlr->stats[SingleCollFrames]);
+	len += snprint(p+len, READSTR-len, "latecollisions: %lud\n",
+		ctlr->stats[LateCollisions]);
+	len += snprint(p+len, READSTR-len, "rxoverruns: %lud\n",
+		ctlr->stats[RxOverruns]);
+	len += snprint(p+len, READSTR-len, "framesxmittedok: %lud\n",
+		ctlr->stats[FramesXmittedOk]);
+	len += snprint(p+len, READSTR-len, "framesrcvdok: %lud\n",
+		ctlr->stats[FramesRcvdOk]);
+	len += snprint(p+len, READSTR-len, "framesdeferred: %lud\n",
+		ctlr->stats[FramesDeferred]);
+	len += snprint(p+len, READSTR-len, "bytesrcvdok: %lud\n",
+		ctlr->stats[BytesRcvdOk]);
+	len += snprint(p+len, READSTR-len, "bytesxmittedok: %lud\n",
+		ctlr->stats[BytesRcvdOk+1]);
+
+	if(ctlr->upenabled){
+		if(ctlr->upqmax > ctlr->upqmaxhw)
+			ctlr->upqmaxhw = ctlr->upqmax;
+		len += snprint(p+len, READSTR-len, "up: q %lud i %lud m %d h %d s %lud\n",
+			ctlr->upqueued, ctlr->upinterrupts,
+			ctlr->upqmax, ctlr->upqmaxhw, ctlr->upstalls);
+		ctlr->upqmax = 0;
+	}
+	if(ctlr->dnenabled){
+		if(ctlr->dnqmax > ctlr->dnqmaxhw)
+			ctlr->dnqmaxhw = ctlr->dnqmax;
+		len += snprint(p+len, READSTR-len, "dn: q %lud i %lud m %d h %d\n",
+			ctlr->dnqueued, ctlr->dninterrupts, ctlr->dnqmax, ctlr->dnqmaxhw);
+		ctlr->dnqmax = 0;
+	}
+
+	snprint(p+len, READSTR-len, "badssd: %lud\n", ctlr->stats[BytesRcvdOk+2]);
+
+	n = readstr(offset, a, n, p);
+	free(p);
+
+	return n;
+}
+
+static void
+txrxreset(int port)
+{
+	COMMAND(port, TxReset, 0);
+	while(STATUS(port) & commandInProgress)
+		;
+	COMMAND(port, RxReset, 0);
+	while(STATUS(port) & commandInProgress)
+		;
+}
+
+static Ctlr*
+tcmadapter(int port, int irq, Pcidev* pcidev)
+{
+	Ctlr *ctlr;
+
+	ctlr = malloc(sizeof(Ctlr));
+	ctlr->port = port;
+	ctlr->irq = irq;
+	ctlr->pcidev = pcidev;
+	ctlr->eepromcmd = EepromReadRegister;
+
+	if(ctlrhead != nil)
+		ctlrtail->next = ctlr;
+	else
+		ctlrhead = ctlr;
+	ctlrtail = ctlr;
+
+	return ctlr;
+}
+
+/*
+ * Write two 0 bytes to identify the IDport and then reset the
+ * ID sequence. Then send the ID sequence to the card to get
+ * the card into command state.
+ */
+static void
+idseq(void)
+{
+	int i;
+	uchar al;
+	static int reset, untag;
+
+	/*
+	 * One time only:
+	 *	reset any adapters listening
+	 */
+	if(reset == 0){
+		outb(IDport, 0);
+		outb(IDport, 0);
+		outb(IDport, 0xC0);
+		delay(20);
+		reset = 1;
+	}
+
+	outb(IDport, 0);
+	outb(IDport, 0);
+	for(al = 0xFF, i = 0; i < 255; i++){
+		outb(IDport, al);
+		if(al & 0x80){
+			al <<= 1;
+			al ^= 0xCF;
+		}
+		else
+			al <<= 1;
+	}
+
+	/*
+	 * One time only:
+	 *	write ID sequence to get the attention of all adapters;
+	 *	untag all adapters.
+	 * If a global reset is done here on all adapters it will confuse
+	 * any ISA cards configured for EISA mode.
+	 */
+	if(untag == 0){
+		outb(IDport, 0xD0);
+		untag = 1;
+	}
+}
+
+static ulong
+activate(void)
+{
+	int i;
+	ushort x, acr;
+
+	/*
+	 * Do the little configuration dance:
+	 *
+	 * 2. write the ID sequence to get to command state.
+	 */
+	idseq();
+
+	/*
+	 * 3. Read the Manufacturer ID from the EEPROM.
+	 *    This is done by writing the IDPort with 0x87 (0x80
+	 *    is the 'read EEPROM' command, 0x07 is the offset of
+	 *    the Manufacturer ID field in the EEPROM).
+	 *    The data comes back 1 bit at a time.
+	 *    A delay seems necessary between reading the bits.
+	 *
+	 * If the ID doesn't match, there are no more adapters.
+	 */
+	outb(IDport, 0x87);
+	delay(20);
+	for(x = 0, i = 0; i < 16; i++){
+		delay(20);
+		x <<= 1;
+		x |= inb(IDport) & 0x01;
+	}
+	if(x != 0x6D50)
+		return 0;
+
+	/*
+	 * 3. Read the Address Configuration from the EEPROM.
+	 *    The Address Configuration field is at offset 0x08 in the EEPROM).
+	 */
+	outb(IDport, 0x88);
+	for(acr = 0, i = 0; i < 16; i++){
+		delay(20);
+		acr <<= 1;
+		acr |= inb(IDport) & 0x01;
+	}
+
+	return (acr & 0x1F)*0x10 + 0x200;
+}
+
+static void
+tcm509isa(void)
+{
+	int irq, port;
+
+	/*
+	 * Attempt to activate all adapters. If adapter is set for
+	 * EISA mode (0x3F0), tag it and ignore. Otherwise, activate
+	 * it fully.
+	 */
+	while(port = activate()){
+		if(ioalloc(port, 0x10, 0, "tcm509isa") < 0){
+			print("tcm509isa: port 0x%uX in use\n", port);
+			continue;
+		}
+
+		/*
+		 * 6. Tag the adapter so it won't respond in future.
+		 */
+		outb(IDport, 0xD1);
+		if(port == 0x3F0){
+			iofree(port);
+			continue;
+		}
+
+		/*
+		 * 6. Activate the adapter by writing the Activate command
+		 *    (0xFF).
+		 */
+		outb(IDport, 0xFF);
+		delay(20);
+
+		/*
+		 * 8. Can now talk to the adapter's I/O base addresses.
+		 *    Use the I/O base address from the acr just read.
+		 *
+		 *    Enable the adapter and clear out any lingering status
+		 *    and interrupts.
+		 */
+		while(STATUS(port) & commandInProgress)
+			;
+		COMMAND(port, SelectRegisterWindow, Wsetup);
+		outs(port+ConfigControl, Ena);
+
+		txrxreset(port);
+		COMMAND(port, AcknowledgeInterrupt, 0xFF);
+
+		irq = (ins(port+ResourceConfig)>>12) & 0x0F;
+		tcmadapter(port, irq, nil);
+	}
+}
+
+static void
+tcm5XXeisa(void)
+{
+	ushort x;
+	int irq, port, slot;
+
+	/*
+	 * Check if this is an EISA machine.
+	 * If not, nothing to do.
+	 */
+	if(strncmp((char*)KADDR(0xFFFD9), "EISA", 4))
+		return;
+
+	/*
+	 * Continue through the EISA slots looking for a match on both
+	 * 3COM as the manufacturer and 3C579-* or 3C59[27]-* as the product.
+	 * If an adapter is found, select window 0, enable it and clear
+	 * out any lingering status and interrupts.
+	 */
+	for(slot = 1; slot < MaxEISA; slot++){
+		port = slot*0x1000;
+		if(ioalloc(port, 0x1000, 0, "tcm5XXeisa") < 0){
+			print("tcm5XXeisa: port 0x%uX in use\n", port);
+			continue;
+		}
+		if(ins(port+0xC80+ManufacturerID) != 0x6D50){
+			iofree(port);
+			continue;
+		}
+		x = ins(port+0xC80+ProductID);
+		if((x & 0xF0FF) != 0x9050 && (x & 0xFF00) != 0x5900){
+			iofree(port);
+			continue;
+		}
+
+		COMMAND(port, SelectRegisterWindow, Wsetup);
+		outs(port+ConfigControl, Ena);
+
+		txrxreset(port);
+		COMMAND(port, AcknowledgeInterrupt, 0xFF);
+
+		irq = (ins(port+ResourceConfig)>>12) & 0x0F;
+		tcmadapter(port, irq, nil);
+	}
+}
+
+static void
+tcm59Xpci(void)
+{
+	Pcidev *p;
+	Ctlr *ctlr;
+	int irq, port;
+
+	p = nil;
+	while(p = pcimatch(p, 0x10B7, 0)){
+		if(p->ccrb != 0x02 || p->ccru != 0)
+			continue;
+		/*
+		 * Not prepared to deal with memory-mapped
+		 * devices yet.
+		 */
+		if(!(p->mem[0].bar & 0x01))
+			continue;
+		port = p->mem[0].bar & ~0x01;
+		if((port = ioalloc((port == 0)? -1: port,  p->mem[0].size, 
+					  0, "tcm59Xpci")) < 0){
+			print("tcm59Xpci: port 0x%uX in use\n", port);
+			continue;
+		}
+		irq = p->intl;
+
+		txrxreset(port);
+		COMMAND(port, AcknowledgeInterrupt, 0xFF);
+
+		ctlr = tcmadapter(port, irq, p);
+		switch(p->did){
+		default:
+			break;
+		case 0x5157:
+			ctlr->eepromcmd = EepromRead8bRegister;
+			ctlr->cbfnpa = p->mem[2].bar&~0x0F;
+			ctlr->cbfn = vmap(p->mem[2].bar&~0x0F, p->mem[2].size);
+			break;
+		case 0x6056:
+			ctlr->eepromcmd = EepromReadOffRegister;
+			ctlr->cbfnpa = p->mem[2].bar&~0x0F;
+			ctlr->cbfn = vmap(p->mem[2].bar&~0x0F, p->mem[2].size);
+			break;
+		}
+		pcisetbme(p);
+	}
+}
+
+static char* tcmpcmcia[] = {
+	"3C589",			/* 3COM 589[ABCD] */
+	"3C562",			/* 3COM 562 */
+	"589E",				/* 3COM Megahertz 589E */
+	nil,
+};
+
+static Ctlr*
+tcm5XXpcmcia(Ether* ether)
+{
+	int i;
+	Ctlr *ctlr;
+
+	if(ether->type == nil)
+		return nil;
+
+	for(i = 0; tcmpcmcia[i] != nil; i++){
+		if(cistrcmp(ether->type, tcmpcmcia[i]))
+			continue;
+		ctlr = tcmadapter(ether->port, ether->irq, nil);
+		ctlr->active = 1;
+		return ctlr;
+	}
+
+	return nil;
+}
+
+static void
+setxcvr(Ctlr* ctlr, int xcvr)
+{
+	int port, x;
+
+	port = ctlr->port;
+	if(ctlr->rxstatus9){
+		COMMAND(port, SelectRegisterWindow, Wsetup);
+		x = ins(port+AddressConfig) & ~xcvrMask9;
+		x |= (xcvr>>20)<<14;
+		outs(port+AddressConfig, x);
+	}
+	else{
+		COMMAND(port, SelectRegisterWindow, Wfifo);
+		x = inl(port+InternalConfig) & ~xcvrMask;
+		x |= xcvr;
+		outl(port+InternalConfig, x);
+	}
+
+	txrxreset(port);
+}
+
+static void
+setfullduplex(int port)
+{
+	int x;
+
+	COMMAND(port, SelectRegisterWindow, Wfifo);
+	x = ins(port+MacControl);
+	outs(port+MacControl, fullDuplexEnable|x);
+
+	txrxreset(port);
+}
+
+static int
+miimdi(int port, int n)
+{
+	int data, i;
+
+	/*
+	 * Read n bits from the MII Management Register.
+	 */
+	data = 0;
+	for(i = n-1; i >= 0; i--){
+		if(ins(port) & mgmtData)
+			data |= (1<<i);
+		microdelay(1);
+		outs(port, mgmtClk);
+		microdelay(1);
+		outs(port, 0);
+		microdelay(1);
+	}
+
+	return data;
+}
+
+static void
+miimdo(int port, int bits, int n)
+{
+	int i, mdo;
+
+	/*
+	 * Write n bits to the MII Management Register.
+	 */
+	for(i = n-1; i >= 0; i--){
+		if(bits & (1<<i))
+			mdo = mgmtDir|mgmtData;
+		else
+			mdo = mgmtDir;
+		outs(port, mdo);
+		microdelay(1);
+		outs(port, mdo|mgmtClk);
+		microdelay(1);
+		outs(port, mdo);
+		microdelay(1);
+	}
+}
+
+static int
+miir(int port, int phyad, int regad)
+{
+	int data, w;
+
+	w = (STATUS(port)>>13) & 0x07;
+	COMMAND(port, SelectRegisterWindow, Wdiagnostic);
+	port += PhysicalMgmt;
+
+	/*
+	 * Preamble;
+	 * ST+OP+PHYAD+REGAD;
+	 * TA + 16 data bits.
+	 */
+	miimdo(port, 0xFFFFFFFF, 32);
+	miimdo(port, 0x1800|(phyad<<5)|regad, 14);
+	data = miimdi(port, 18);
+
+	port -= PhysicalMgmt;
+	COMMAND(port, SelectRegisterWindow, w);
+
+	if(data & 0x10000)
+		return -1;
+
+	return data & 0xFFFF;
+}
+
+static int
+scanphy(int port)
+{
+	int i, x;
+
+	for(i = 0; i < 32; i++){
+		if((x = miir(port, i, 2)) == -1 || x == 0)
+			continue;
+		x <<= 6;
+		x |= miir(port, i, 3)>>10;
+		XCVRDEBUG("phy%d: oui %uX reg1 %uX\n", i, x, miir(port, i, 1));
+		USED(x);
+
+		return i;
+	}
+	return 24;
+}
+
+static struct {
+	char *name;
+	int avail;
+	int xcvr;
+} media[] = {
+	"10BaseT",	base10TAvailable,	xcvr10BaseT,
+	"10Base2",	coaxAvailable,		xcvr10Base2,
+	"100BaseTX",	baseTXAvailable,	xcvr100BaseTX,
+	"100BaseFX",	baseFXAvailable,	xcvr100BaseFX,
+	"aui",		auiAvailable,		xcvrAui,
+	"mii",		miiConnector,		xcvrMii
+};
+
+static int
+autoselect(Ctlr* ctlr)
+{
+	int media, port, x;
+
+	/*
+	 * Pathetic attempt at automatic media selection.
+	 * Really just to get the Fast Etherlink 10BASE-T/100BASE-TX
+	 * cards operational.
+	 * It's a bonus if it works for anything else.
+	 */
+	port = ctlr->port;
+	if(ctlr->rxstatus9){
+		COMMAND(port, SelectRegisterWindow, Wsetup);
+		x = ins(port+ConfigControl);
+		media = 0;
+		if(x & base10TAvailable9)
+			media |= base10TAvailable;
+		if(x & coaxAvailable9)
+			media |= coaxAvailable;
+		if(x & auiAvailable9)
+			media |= auiAvailable;
+	}
+	else{
+		COMMAND(port, SelectRegisterWindow, Wfifo);
+		media = ins(port+ResetOptions);
+	}
+	XCVRDEBUG("autoselect: media %uX\n", media);
+
+	if(media & miiConnector)
+		return xcvrMii;
+
+	COMMAND(port, SelectRegisterWindow, Wdiagnostic);
+	XCVRDEBUG("autoselect: media status %uX\n", ins(port+MediaStatus));
+
+	if(media & baseTXAvailable){
+		/*
+		 * Must have InternalConfig register.
+		 */
+		setxcvr(ctlr, xcvr100BaseTX);
+
+		COMMAND(port, SelectRegisterWindow, Wdiagnostic);
+		x = ins(port+MediaStatus) & ~(dcConverterEnabled|jabberGuardEnable);
+		outs(port+MediaStatus, linkBeatEnable|x);
+		delay(10);
+
+		if(ins(port+MediaStatus) & linkBeatDetect)
+			return xcvr100BaseTX;
+		outs(port+MediaStatus, x);
+	}
+
+	if(media & base10TAvailable){
+		setxcvr(ctlr, xcvr10BaseT);
+
+		COMMAND(port, SelectRegisterWindow, Wdiagnostic);
+		x = ins(port+MediaStatus) & ~dcConverterEnabled;
+		outs(port+MediaStatus, linkBeatEnable|jabberGuardEnable|x);
+		delay(100);
+
+		XCVRDEBUG("autoselect: 10BaseT media status %uX\n", ins(port+MediaStatus));
+		if(ins(port+MediaStatus) & linkBeatDetect)
+			return xcvr10BaseT;
+		outs(port+MediaStatus, x);
+	}
+
+	/*
+	 * Botch.
+	 */
+	return autoSelect;
+}
+
+static int
+eepromdata(Ctlr* ctlr, int offset)
+{
+	int port;
+
+	port = ctlr->port;
+
+	COMMAND(port, SelectRegisterWindow, Wsetup);
+	while(EEPROMBUSY(port))
+		;
+	EEPROMCMD(port, ctlr->eepromcmd, offset);
+	while(EEPROMBUSY(port))
+		;
+	return EEPROMDATA(port);
+}
+
+static void
+resetctlr(Ctlr *ctlr)
+{
+	int x, port = ctlr->port;
+
+	txrxreset(port);
+	x = ins(port+ResetOp905B);
+	XCVRDEBUG("905[BC] reset ops 0x%uX\n", x);
+	x &= ~0x4010;
+	if(ctlr->did == 0x5157){
+		x |= 0x0010;			/* Invert LED */
+		outs(port+ResetOp905B, x);
+	}
+	if(ctlr->did == 0x6056){
+		x |= 0x4000;
+		outs(port+ResetOp905B, x);
+
+		COMMAND(port, SelectRegisterWindow, Wsetup);
+		outs(port, 0x0800);
+	}
+}
+
+static void
+shutdown(Ether *ether)
+{
+print("etherelnk3 shutting down\n");
+	resetctlr(ether->ctlr);
+}
+
+int
+etherelnk3reset(Ether* ether)
+{
+	char *p;
+	Ctlr *ctlr;
+	uchar ea[Eaddrlen];
+	static int scandone;
+	int anar, anlpar, i, j, phyaddr, phystat, port, timeo, x;
+
+	/*
+	 * Scan for adapter on PCI, EISA and finally
+	 * using the little ISA configuration dance.
+	 */
+	if(scandone == 0){
+		tcm59Xpci();
+		tcm5XXeisa();
+		tcm509isa();
+		scandone = 1;
+	}
+
+	/*
+	 * Any adapter matches if no ether->port is supplied,
+	 * otherwise the ports must match.
+	 */
+	for(ctlr = ctlrhead; ctlr != nil; ctlr = ctlr->next){
+		if(ctlr->active)
+			continue;
+		if(ether->port == 0 || ether->port == ctlr->port){
+			ctlr->active = 1;
+			break;
+		}
+	}
+	if(ctlr == nil && (ctlr = tcm5XXpcmcia(ether)) == 0)
+		return -1;
+
+	ether->ctlr = ctlr;
+	port = ctlr->port;
+	ether->port = port;
+	ether->irq = ctlr->irq;
+	if(ctlr->pcidev != nil)
+		ether->tbdf = ctlr->pcidev->tbdf;
+	else
+		ether->tbdf = BUSUNKNOWN;
+
+	/*
+	 * Read the DeviceID from the EEPROM, it's at offset 0x03,
+	 * and do something depending on capabilities.
+	 */
+	switch(ctlr->did = eepromdata(ctlr, 0x03)){
+	case 0x5157:		/* 3C575 Cyclone */
+	case 0x6056:
+		/*FALLTHROUGH*/
+	case 0x4500:		/* 3C450 HomePNA Tornado */
+	case 0x7646:		/* 3CSOHO100-TX */
+	case 0x9055:		/* 3C905B-TX */
+	case 0x9200:		/* 3C905C-TX */
+	case 0x9201:		/* 3C920 */
+	case 0x9805:		/* 3C9805: 3C980-TX Python-T 10/100baseTX */
+		/*FALLTHROUGH*/
+	case 0x9000:		/* 3C900-TPO */
+	case 0x9001:		/* 3C900-COMBO */
+	case 0x9005:		/* 3C900B-COMBO */
+	case 0x9050:		/* 3C905-TX */
+	case 0x9051:		/* 3C905-T4 */
+		if(BUSTYPE(ether->tbdf) != BusPCI)
+			goto buggery;
+		ctlr->busmaster = 2;
+		goto vortex;
+	case 0x5900:		/* 3C590-[TP|COMBO|TPO] */
+	case 0x5920:		/* 3C592-[TP|COMBO|TPO] */
+	case 0x5950:		/* 3C595-TX */
+	case 0x5951:		/* 3C595-T4 */
+	case 0x5952:		/* 3C595-MII */
+	case 0x5970:		/* 3C597-TX */
+	case 0x5971:		/* 3C597-T4 */
+	case 0x5972:		/* 3C597-MII */
+		ctlr->busmaster = 1;
+	vortex:
+		COMMAND(port, SelectRegisterWindow, Wfifo);
+		ctlr->xcvr = inl(port+InternalConfig) & (autoSelect|xcvrMask);
+		ctlr->rxearly = 8188;
+		ctlr->rxstatus9 = 0;
+		break;
+	buggery:
+	default:
+		ctlr->busmaster = 0;
+		COMMAND(port, SelectRegisterWindow, Wsetup);
+		x = ins(port+AddressConfig);
+		ctlr->xcvr = ((x & xcvrMask9)>>14)<<20;
+		if(x & autoSelect9)
+			ctlr->xcvr |= autoSelect;
+		ctlr->rxearly = 2044;
+		ctlr->rxstatus9 = 1;
+		break;
+	}
+	if(ctlr->rxearly >= 2048)
+		ctlr->ts = 2;
+
+	/*
+	 * Check if the adapter's station address is to be overridden.
+	 * If not, read it from the EEPROM and set in ether->ea prior to
+	 * loading the station address in Wstation.
+	 * The EEPROM returns 16-bits at a time.
+	 */
+	memset(ea, 0, Eaddrlen);
+	if(memcmp(ea, ether->ea, Eaddrlen) == 0){
+		for(i = 0; i < Eaddrlen/2; i++){
+			x = eepromdata(ctlr, i);
+			ether->ea[2*i] = x>>8;
+			ether->ea[2*i+1] = x;
+		}
+	}
+
+	COMMAND(port, SelectRegisterWindow, Wstation);
+	for(i = 0; i < Eaddrlen; i++)
+		outb(port+i, ether->ea[i]);
+
+	/*
+	 * Enable the transceiver if necessary and determine whether
+	 * busmastering can be used. Due to bugs in the first revision
+	 * of the 3C59[05], don't use busmastering at 10Mbps.
+	 */
+	XCVRDEBUG("reset: xcvr %uX\n", ctlr->xcvr);
+
+	/*
+	 * Allow user to specify desired media in plan9.ini
+	 */
+	for(i = 0; i < ether->nopt; i++){
+		if(cistrncmp(ether->opt[i], "media=", 6) != 0)
+			continue;
+		p = ether->opt[i]+6;
+		for(j = 0; j < nelem(media); j++)
+			if(cistrcmp(p, media[j].name) == 0)
+				ctlr->xcvr = media[j].xcvr;
+	}
+	
+	/*
+	 * forgive me, but i am weak
+	 */
+	switch(ctlr->did){
+	default:
+		if(ctlr->xcvr & autoSelect)
+			ctlr->xcvr = autoselect(ctlr);
+		break;
+	case 0x5157:
+	case 0x6056:
+	case 0x4500:
+	case 0x7646:
+	case 0x9055:
+	case 0x9200:
+	case 0x9201:
+	case 0x9805:
+		ctlr->xcvr = xcvrMii;
+		resetctlr(ctlr);
+		break;
+	}
+	XCVRDEBUG("xcvr selected: %uX, did 0x%uX\n", ctlr->xcvr, ctlr->did);
+
+	switch(ctlr->xcvr){
+	case xcvrMii:
+		/*
+		 * Quick hack.
+		 */
+		if(ctlr->did == 0x5157)
+			phyaddr = 0;
+		else if(ctlr->did == 0x6056)
+			phyaddr = scanphy(port);
+		else
+			phyaddr = 24;
+		for(i = 0; i < 7; i++)
+			XCVRDEBUG(" %2.2uX", miir(port, phyaddr, i));
+			XCVRDEBUG("\n");
+
+		for(timeo = 0; timeo < 30; timeo++){
+			phystat = miir(port, phyaddr, 0x01);
+			if(phystat & 0x20)
+				break;
+			XCVRDEBUG(" %2.2uX", phystat);
+			delay(100);
+		}
+		XCVRDEBUG(" %2.2uX", miir(port, phyaddr, 0x01));
+		XCVRDEBUG("\n");
+
+		anar = miir(port, phyaddr, 0x04);
+		anlpar = miir(port, phyaddr, 0x05) & 0x03E0;
+		anar &= anlpar;
+		miir(port, phyaddr, 0x00);
+		XCVRDEBUG("mii an: %uX anlp: %uX r0:%uX r1:%uX\n",
+			anar, anlpar, miir(port, phyaddr, 0x00),
+			miir(port, phyaddr, 0x01));
+		for(i = 0; i < ether->nopt; i++){
+			if(cistrcmp(ether->opt[i], "fullduplex") == 0)
+				anar |= 0x0100;
+			else if(cistrcmp(ether->opt[i], "100BASE-TXFD") == 0)
+				anar |= 0x0100;
+			else if(cistrcmp(ether->opt[i], "force100") == 0)
+				anar |= 0x0080;
+		}
+		XCVRDEBUG("mii anar: %uX\n", anar);
+		if(anar & 0x0100){		/* 100BASE-TXFD */
+			ether->mbps = 100;
+			setfullduplex(port);
+		}
+		else if(anar & 0x0200){		/* 100BASE-T4 */
+			/* nothing to do */
+		}
+		else if(anar & 0x0080)		/* 100BASE-TX */
+			ether->mbps = 100;
+		else if(anar & 0x0040)		/* 10BASE-TFD */
+			setfullduplex(port);
+		else{				/* 10BASE-T */
+			/* nothing to do */
+		}
+		break;
+	case xcvr100BaseTX:
+	case xcvr100BaseFX:
+		COMMAND(port, SelectRegisterWindow, Wfifo);
+		x = inl(port+InternalConfig) & ~ramPartitionMask;
+		outl(port+InternalConfig, x|ramPartition1to1);
+
+		COMMAND(port, SelectRegisterWindow, Wdiagnostic);
+		x = ins(port+MediaStatus) & ~(dcConverterEnabled|jabberGuardEnable);
+		x |= linkBeatEnable;
+		outs(port+MediaStatus, x);
+
+		if(x & dataRate100)
+			ether->mbps = 100;
+		break;
+	case xcvr10BaseT:
+		/*
+		 * Enable Link Beat and Jabber to start the
+		 * transceiver.
+		 */
+		COMMAND(port, SelectRegisterWindow, Wdiagnostic);
+		x = ins(port+MediaStatus) & ~dcConverterEnabled;
+		x |= linkBeatEnable|jabberGuardEnable;
+		outs(port+MediaStatus, x);
+
+		if((ctlr->did & 0xFF00) == 0x5900)
+			ctlr->busmaster = 0;
+		break;
+	case xcvr10Base2:
+		COMMAND(port, SelectRegisterWindow, Wdiagnostic);
+		x = ins(port+MediaStatus) & ~(linkBeatEnable|jabberGuardEnable);
+		outs(port+MediaStatus, x);
+
+		/*
+		 * Start the DC-DC converter.
+		 * Wait > 800 microseconds.
+		 */
+		COMMAND(port, EnableDcConverter, 0);
+		delay(1);
+		break;
+	}
+
+	/*
+	 * Wop is the normal operating register set.
+	 * The 3C59[0257] adapters allow access to more than one register window
+	 * at a time, but there are situations where switching still needs to be
+	 * done, so just do it.
+	 * Clear out any lingering Tx status.
+	 */
+	COMMAND(port, SelectRegisterWindow, Wop);
+	if(ctlr->busmaster == 2)
+		x = port+TxStatus905;
+	else
+		x = port+TxStatus;
+	while(inb(x))
+		outb(x, 0);
+
+	/*
+	 * Clear out the
+	 * adapter statistics, clear the statistics logged into ctlr
+	 * and enable statistics collection.
+	 */
+	ilock(&ctlr->wlock);
+	statistics(ether);
+	memset(ctlr->stats, 0, sizeof(ctlr->stats));
+
+	COMMAND(port, StatisticsEnable, 0);
+
+	/*
+	 * Allocate any receive buffers.
+	 */
+	switch(ctlr->busmaster){
+	case 2:
+		ctlr->dnenabled = 1;
+
+		/*
+		 * 10MUpldBug.
+		 * Disabling is too severe, can use receive busmastering at
+		 * 100Mbps OK, but how to tell which rate is actually being used -
+		 * the 3c905 always seems to have dataRate100 set?
+		 * Believe the bug doesn't apply if upRxEarlyEnable is set
+		 * and the threshold is set such that uploads won't start
+		 * until the whole packet has been received.
+		 */
+		ctlr->upenabled = 1;
+		x = eepromdata(ctlr, 0x0F);
+		if(!(x & 0x01))
+			outl(port+PktStatus, upRxEarlyEnable);
+
+		if(ctlr->upenabled || ctlr->dnenabled){
+			ctlr->nup = Nup;
+			ctlr->ndn = Ndn;
+			init905(ctlr);
+		}
+		else {
+			ctlr->rbp = rbpalloc(iallocb);
+			if(ctlr->rbp == nil)
+				panic("can't reset ethernet: out of memory");
+		}
+		outl(port+TxFreeThresh, HOWMANY(ETHERMAXTU, 256));
+		break;
+	default:
+		ctlr->rbp = rbpalloc(iallocb);
+		if(ctlr->rbp == nil)
+			panic("can't reset ethernet: out of memory");
+		break;
+	}
+
+	/*
+	 * Set a base TxStartThresh which will be incremented
+	 * if any txUnderrun errors occur and ensure no RxEarly
+	 * interrupts happen.
+	 */
+	ctlr->txthreshold = ETHERMAXTU/2;
+	COMMAND(port, SetTxStartThresh, ctlr->txthreshold>>ctlr->ts);
+	COMMAND(port, SetRxEarlyThresh, ctlr->rxearly>>ctlr->ts);
+
+	iunlock(&ctlr->wlock);
+
+	/*
+	 * Linkage to the generic ethernet driver.
+	 */
+	ether->attach = attach;
+	ether->transmit = transmit;
+	ether->interrupt = interrupt;
+	ether->ifstat = ifstat;
+
+	ether->promiscuous = promiscuous;
+	ether->multicast = multicast;
+	ether->shutdown = shutdown;
+	ether->arg = ether;
+
+	return 0;
+}
+
+void
+etherelnk3link(void)
+{
+	addethercard("elnk3", etherelnk3reset);
+	addethercard("3C509", etherelnk3reset);
+	addethercard("3C575", etherelnk3reset);
+}
--- /dev/null
+++ b/os/pc/etherga620.c
@@ -1,0 +1,1275 @@
+/*
+ * Netgear GA620 Gigabit Ethernet Card.
+ * Specific for the Alteon Tigon 2 and Intel Pentium or later.
+ * To Do:
+ *	cache alignment for PCI Write-and-Invalidate
+ *	mini ring (what size)?
+ *	tune coalescing values
+ *	statistics formatting
+ *	don't update Spi if nothing to send
+ *	receive ring alignment
+ *	watchdog for link management?
+ */
+#include "u.h"
+#include "../port/lib.h"
+#include "mem.h"
+#include "dat.h"
+#include "fns.h"
+#include "io.h"
+#include "../port/error.h"
+#include "../port/netif.h"
+
+#define malign(n)	xspanalloc((n), 32, 0)
+
+#include "etherif.h"
+#include "etherga620fw.h"
+
+enum {
+	Mhc		= 0x0040,	/* Miscellaneous Host Control */
+	Mlc		= 0x0044,	/* Miscellaneous Local Control */
+	Mc		= 0x0050,	/* Miscellaneous Configuration */
+	Ps		= 0x005C,	/* PCI State */
+	Wba		= 0x0068,	/* Window Base Address */
+	Wd		= 0x006C,	/* Window Data */
+
+	DMAas		= 0x011C,	/* DMA Assist State */
+
+	CPUAstate	= 0x0140,	/* CPU A State */
+	CPUApc		= 0x0144,	/* CPU A Programme Counter */
+
+	CPUBstate	= 0x0240,	/* CPU B State */
+
+	Hi		= 0x0504,	/* Host In Interrupt Handler */
+	Cpi		= 0x050C,	/* Command Producer Index */
+	Spi		= 0x0514,	/* Send Producer Index */
+	Rspi		= 0x051C,	/* Receive Standard Producer Index */
+	Rjpi		= 0x0524,	/* Receive Jumbo Producer Index */
+	Rmpi		= 0x052C,	/* Receive Mini Producer Index */
+
+	Mac		= 0x0600,	/* MAC Address */
+	Gip		= 0x0608,	/* General Information Pointer */
+	Om		= 0x0618,	/* Operating Mode */
+	DMArc		= 0x061C,	/* DMA Read Configuration */
+	DMAwc		= 0x0620,	/* DMA Write Configuration */
+	Tbr		= 0x0624,	/* Transmit Buffer Ratio */
+	Eci		= 0x0628,	/* Event Consumer Index */
+	Cci		= 0x062C,	/* Command Consumer Index */
+
+	Rct		= 0x0630,	/* Receive Coalesced Ticks */
+	Sct		= 0x0634,	/* Send Coalesced Ticks */
+	St		= 0x0638,	/* Stat Ticks */
+	SmcBD		= 0x063C,	/* Send Max. Coalesced BDs */
+	RmcBD		= 0x0640,	/* Receive Max. Coalesced BDs */
+	Nt		= 0x0644,	/* NIC Tracing */
+	Gln		= 0x0648,	/* Gigabit Link Negotiation */
+	Fln		= 0x064C,	/* 10/100 Link Negotiation */
+	Ifx		= 0x065C,	/* Interface Index */
+	IfMTU		= 0x0660,	/* Interface MTU */
+	Mi		= 0x0664,	/* Mask Interrupts */
+	Gls		= 0x0668,	/* Gigabit Link State */
+	Fls		= 0x066C,	/* 10/100 Link State */
+
+	Cr		= 0x0700,	/* Command Ring */
+
+	Lmw		= 0x0800,	/* Local Memory Window */
+};
+
+enum {					/* Mhc */
+	Is		= 0x00000001,	/* Interrupt State */
+	Ci		= 0x00000002,	/* Clear Interrupt */
+	Hr		= 0x00000008,	/* Hard Reset */
+	Eebs		= 0x00000010,	/* Enable Endian Byte Swap */
+	Eews		= 0x00000020,	/* Enable Endian Word (64-bit) swap */
+	Mpio		= 0x00000040,	/* Mask PCI Interrupt Output */
+};
+
+enum {					/* Mlc */
+	SRAM512		= 0x00000200,	/* SRAM Bank Size of 512KB */
+	SRAMmask	= 0x00000300,
+	EEclk		= 0x00100000,	/* Serial EEPROM Clock Output */
+	EEdoe		= 0x00200000,	/* Serial EEPROM Data Out Enable */
+	EEdo		= 0x00400000,	/* Serial EEPROM Data Out Value */
+	EEdi		= 0x00800000,	/* Serial EEPROM Data Input */
+};
+
+enum {					/* Mc */
+	SyncSRAM	= 0x00100000,	/* Set Synchronous SRAM Timing */
+};
+
+enum {					/* Ps */
+	PCIwm32		= 0x000000C0,	/* Write Max DMA 32 */
+	PCImrm		= 0x00020000,	/* Use Memory Read Multiple Command */
+	PCI66		= 0x00080000,
+	PCI32		= 0x00100000,
+	PCIrcmd		= 0x06000000,	/* PCI Read Command */
+	PCIwcmd		= 0x70000000,	/* PCI Write Command */
+};
+
+enum {					/* CPUAstate */
+	CPUrf		= 0x00000010,	/* ROM Fail */
+	CPUhalt		= 0x00010000,	/* Halt the internal CPU */
+	CPUhie		= 0x00040000,	/* HALT instruction executed */
+};
+
+enum {					/* Om */
+	BswapBD		= 0x00000002,	/* Byte Swap Buffer Descriptors */
+	WswapBD		= 0x00000004,	/* Word Swap Buffer Descriptors */
+	Warn		= 0x00000008,
+	BswapDMA	= 0x00000010,	/* Byte Swap DMA Data */
+	Only1DMA	= 0x00000040,	/* Only One DMA Active at a time */
+	NoJFrag		= 0x00000200,	/* Don't Fragment Jumbo Frames */
+	Fatal		= 0x40000000,
+};
+
+enum {					/* Lmw */
+	Lmwsz		= 2*1024,	/* Local Memory Window Size */
+
+	/*
+	 * legal values are 0x3800 iff Nsr is 128, 0x3000 iff Nsr is 256,
+	 * or 0x2000 iff Nsr is 512.
+	 */
+	Sr		= 0x2000,	/* Send Ring (accessed via Lmw) */
+};
+
+enum {					/* Link */
+	Lpref		= 0x00008000,	/* Preferred Link */
+	L10MB		= 0x00010000,
+	L100MB		= 0x00020000,
+	L1000MB		= 0x00040000,
+	Lfd		= 0x00080000,	/* Full Duplex */
+	Lhd		= 0x00100000,	/* Half Duplex */
+	Lefc		= 0x00200000,	/* Emit Flow Control Packets */
+	Lofc		= 0x00800000,	/* Obey Flow Control Packets */
+	Lean		= 0x20000000,	/* Enable Autonegotiation/Sensing */
+	Le		= 0x40000000,	/* Link Enable */
+};
+
+typedef struct Host64 {
+	uint	hi;
+	uint	lo;
+} Host64;
+
+typedef struct Ere {			/* Event Ring Element */
+	int	event;			/* event<<24 | code<<12 | index */
+	int	unused;
+} Ere;
+
+typedef int Cmd;			/* cmd<<24 | flags<<12 | index */
+
+typedef struct Rbd {			/* Receive Buffer Descriptor */
+	Host64	addr;
+	int	indexlen;		/* ring-index<<16 | buffer-length */
+	int	flags;			/* only lower 16-bits */
+	int	checksum;		/* ip<<16 | tcp/udp */
+	int	error;			/* only upper 16-bits */
+	int	reserved;
+	void*	opaque;			/* passed to receive return ring */
+} Rbd;
+
+typedef struct Sbd {			/* Send Buffer Descriptor */
+	Host64	addr;
+	int	lenflags;		/* len<<16 | flags */
+	int	reserved;
+} Sbd;
+
+enum {					/* Buffer Descriptor Flags */
+	Fend		= 0x00000004,	/* Frame Ends in this Buffer */
+	Frjr		= 0x00000010,	/* Receive Jumbo Ring Buffer */
+	Funicast	= 0x00000020,	/* Unicast packet (2-bit field) */
+	Fmulticast	= 0x00000040,	/* Multicast packet */
+	Fbroadcast	= 0x00000060,	/* Broadcast packet */
+	Ferror		= 0x00000400,	/* Frame Has Error */
+	Frmr		= 0x00001000,	/* Receive Mini Ring Buffer */
+};
+
+enum {					/* Buffer Error Flags */
+	Ecrc		= 0x00010000,	/* bad CRC */
+	Ecollision	= 0x00020000,	/* collision */
+	Elink		= 0x00040000,	/* link lost */
+	Ephy		= 0x00080000,	/* unspecified PHY frame decode error */
+	Eodd		= 0x00100000,	/* odd number of nibbles */
+	Emac		= 0x00200000,	/* unspecified MAC abort */
+	Elen64		= 0x00400000,	/* short packet */
+	Eresources	= 0x00800000,	/* MAC out of internal resources */
+	Egiant		= 0x01000000,	/* packet too big */
+};
+
+typedef struct Rcb {			/* Ring Control Block */
+	Host64	addr;			/* points to the Rbd ring */
+	int	control;		/* max_len<<16 | flags */
+	int	unused;
+} Rcb;
+
+enum {
+	TcpUdpCksum	= 0x0001,	/* Perform TCP or UDP checksum */
+	IpCksum		= 0x0002,	/* Perform IP checksum */
+	NoPseudoHdrCksum= 0x0008,	/* Don't include the pseudo header */
+	VlanAssist	= 0x0010,	/* Enable VLAN tagging */
+	CoalUpdateOnly	= 0x0020,	/* Coalesce transmit interrupts */
+	HostRing	= 0x0040,	/* Sr in host memory */
+	SnapCksum	= 0x0080,	/* Parse + offload 802.3 SNAP frames */
+	UseExtRxBd	= 0x0100,	/* Extended Rbd for Jumbo frames */
+	RingDisabled	= 0x0200,	/* Jumbo or Mini RCB only */
+};
+
+typedef struct Gib {			/* General Information Block */
+	int	statistics[256];	/* Statistics */
+	Rcb	ercb;			/* Event Ring */
+	Rcb	crcb;			/* Command Ring */
+	Rcb	srcb;			/* Send Ring */
+	Rcb	rsrcb;			/* Receive Standard Ring */
+	Rcb	rjrcb;			/* Receive Jumbo Ring */
+	Rcb	rmrcb;			/* Receive Mini Ring */
+	Rcb	rrrcb;			/* Receive Return Ring */
+	Host64	epp;			/* Event Producer */
+	Host64	rrrpp;			/* Receive Return Ring Producer */
+	Host64	scp;			/* Send Consumer */
+	Host64	rsp;			/* Refresh Stats */
+} Gib;
+
+/*
+ * these sizes are all fixed in the card,
+ * except for Nsr, which has only 3 valid sizes.
+ */
+enum {					/* Host/NIC Interface ring sizes */
+	Ner		= 256,		/* event ring */
+	Ncr		= 64,		/* command ring */
+	Nsr		= 512,		/* send ring: 128, 256 or 512 */
+	Nrsr		= 512,		/* receive standard ring */
+	Nrjr		= 256,		/* receive jumbo ring */
+	Nrmr		= 1024,		/* receive mini ring, optional */
+	Nrrr		= 2048,		/* receive return ring */
+};
+
+enum {
+	NrsrHI		= 72,		/* Fill-level of Rsr (m.b. < Nrsr) */
+	NrsrLO		= 54,		/* Level at which to top-up ring */
+	NrjrHI		= 0,		/* Fill-level of Rjr (m.b. < Nrjr) */
+	NrjrLO		= 0,		/* Level at which to top-up ring */
+	NrmrHI		= 0,		/* Fill-level of Rmr (m.b. < Nrmr) */
+	NrmrLO		= 0,		/* Level at which to top-up ring */
+};
+
+typedef struct Ctlr Ctlr;
+struct Ctlr {
+	int	port;
+	Pcidev*	pcidev;
+	Ctlr*	next;
+	int	active;
+	int	id;
+
+	uchar	ea[Eaddrlen];
+
+	int*	nic;
+	Gib*	gib;
+
+	Ere*	er;
+
+	Lock	srlock;
+	Sbd*	sr;
+	Block**	srb;
+	int	nsr;			/* currently in send ring */
+
+	Rbd*	rsr;
+	int	nrsr;			/* currently in Receive Standard Ring */
+	Rbd*	rjr;
+	int	nrjr;			/* currently in Receive Jumbo Ring */
+	Rbd*	rmr;
+	int	nrmr;			/* currently in Receive Mini Ring */
+	Rbd*	rrr;
+	int	rrrci;			/* Receive Return Ring Consumer Index */
+
+	int	epi[2];			/* Event Producer Index */
+	int	rrrpi[2];		/* Receive Return Ring Producer Index */
+	int	sci[3];			/* Send Consumer Index ([2] is host) */
+
+	int	interrupts;		/* statistics */
+	int	mi;
+	uvlong	ticks;
+
+	int	coalupdateonly;		/* tuning */
+	int	hardwarecksum;
+	int	rct;			/* Receive Coalesce Ticks */
+	int	sct;			/* Send Coalesce Ticks */
+	int	st;			/* Stat Ticks */
+	int	smcbd;			/* Send Max. Coalesced BDs */
+	int	rmcbd;			/* Receive Max. Coalesced BDs */
+};
+
+static Ctlr* ctlrhead;
+static Ctlr* ctlrtail;
+
+#define csr32r(c, r)	(*((c)->nic+((r)/4)))
+#define csr32w(c, r, v)	(*((c)->nic+((r)/4)) = (v))
+
+static void
+sethost64(Host64* host64, void* addr)
+{
+	uvlong uvl;
+
+	uvl = PCIWADDR(addr);
+	host64->hi = uvl>>32;
+	host64->lo = uvl & 0xFFFFFFFFL;
+}
+
+static void
+ga620command(Ctlr* ctlr, int cmd, int flags, int index)
+{
+	int cpi;
+
+	cpi = csr32r(ctlr, Cpi);
+	csr32w(ctlr, Cr+(cpi*4), cmd<<24 | flags<<12 | index);
+	cpi = NEXT(cpi, Ncr);
+	csr32w(ctlr, Cpi, cpi);
+}
+
+static void
+ga620attach(Ether* edev)
+{
+	Ctlr *ctlr;
+
+	ctlr = edev->ctlr;
+	USED(ctlr);
+}
+
+static long
+ga620ifstat(Ether* edev, void* a, long n, ulong offset)
+{
+	char *p;
+	Ctlr *ctlr;
+	int i, l, r;
+
+	ctlr = edev->ctlr;
+
+	if(n == 0)
+		return 0;
+	p = malloc(READSTR);
+	l = 0;
+	for(i = 0; i < 256; i++){
+		if((r = ctlr->gib->statistics[i]) == 0)
+			continue;
+		l += snprint(p+l, READSTR-l, "%d: %ud\n", i, r);
+	}
+
+	l += snprint(p+l, READSTR-l, "interrupts: %ud\n", ctlr->interrupts);
+	l += snprint(p+l, READSTR-l, "mi: %ud\n", ctlr->mi);
+	l += snprint(p+l, READSTR-l, "ticks: %llud\n", ctlr->ticks);
+	l += snprint(p+l, READSTR-l, "coalupdateonly: %d\n", ctlr->coalupdateonly);
+	l += snprint(p+l, READSTR-l, "hardwarecksum: %d\n", ctlr->hardwarecksum);
+	l += snprint(p+l, READSTR-l, "rct: %d\n", ctlr->rct);
+	l += snprint(p+l, READSTR-l, "sct: %d\n", ctlr->sct);
+	l += snprint(p+l, READSTR-l, "smcbd: %d\n", ctlr->smcbd);
+	snprint(p+l, READSTR-l, "rmcbd: %d\n", ctlr->rmcbd);
+
+	n = readstr(offset, a, n, p);
+	free(p);
+
+	return n;
+}
+
+static long
+ga620ctl(Ether* edev, void* buf, long n)
+{
+	char *p;
+	Cmdbuf *cb;
+	Ctlr *ctlr;
+	int control, i, r;
+
+	ctlr = edev->ctlr;
+	if(ctlr == nil)
+		error(Enonexist);
+	r = 0;
+	cb = parsecmd(buf, n);
+	if(cb->nf < 2)
+		r = -1;
+	else if(cistrcmp(cb->f[0], "coalupdateonly") == 0){
+		if(cistrcmp(cb->f[1], "off") == 0){
+			control = ctlr->gib->srcb.control;
+			control &= ~CoalUpdateOnly;
+			ctlr->gib->srcb.control = control;
+			ctlr->coalupdateonly = 0;
+		}
+		else if(cistrcmp(cb->f[1], "on") == 0){
+			control = ctlr->gib->srcb.control;
+			control |= CoalUpdateOnly;
+			ctlr->gib->srcb.control = control;
+			ctlr->coalupdateonly = 1;
+		}
+		else
+			r = -1;
+	}
+	else if(cistrcmp(cb->f[0], "hardwarecksum") == 0){
+		if(cistrcmp(cb->f[1], "off") == 0){
+			control = ctlr->gib->srcb.control;
+			control &= ~(TcpUdpCksum|NoPseudoHdrCksum);
+			ctlr->gib->srcb.control = control;
+
+			control = ctlr->gib->rsrcb.control;
+			control &= ~(TcpUdpCksum|NoPseudoHdrCksum);
+			ctlr->gib->rsrcb.control = control;
+
+			ctlr->hardwarecksum = 0;
+		}
+		else if(cistrcmp(cb->f[1], "on") == 0){
+			control = ctlr->gib->srcb.control;
+			control |= (TcpUdpCksum|NoPseudoHdrCksum);
+			ctlr->gib->srcb.control = control;
+
+			control = ctlr->gib->rsrcb.control;
+			control |= (TcpUdpCksum|NoPseudoHdrCksum);
+			ctlr->gib->rsrcb.control = control;
+
+			ctlr->hardwarecksum = 1;
+		}
+		else
+			r = -1;
+	}
+	else if(cistrcmp(cb->f[0], "rct") == 0){
+		i = strtol(cb->f[1], &p, 0);
+		if(i < 0 || p == cb->f[1])
+			r = -1;
+		else{
+			ctlr->rct = i;
+			csr32w(ctlr, Rct, ctlr->rct);
+		}
+	}
+	else if(cistrcmp(cb->f[0], "sct") == 0){
+		i = strtol(cb->f[1], &p, 0);
+		if(i < 0 || p == cb->f[1])
+			r = -1;
+		else{
+			ctlr->sct = i;
+			csr32w(ctlr, Sct, ctlr->sct);
+		}
+	}
+	else if(cistrcmp(cb->f[0], "st") == 0){
+		i = strtol(cb->f[1], &p, 0);
+		if(i < 0 || p == cb->f[1])
+			r = -1;
+		else{
+			ctlr->st = i;
+			csr32w(ctlr, St, ctlr->st);
+		}
+	}
+	else if(cistrcmp(cb->f[0], "smcbd") == 0){
+		i = strtol(cb->f[1], &p, 0);
+		if(i < 0 || p == cb->f[1])
+			r = -1;
+		else{
+			ctlr->smcbd = i;
+			csr32w(ctlr, SmcBD, ctlr->smcbd);
+		}
+	}
+	else if(cistrcmp(cb->f[0], "rmcbd") == 0){
+		i = strtol(cb->f[1], &p, 0);
+		if(i < 0 || p == cb->f[1])
+			r = -1;
+		else{
+			ctlr->rmcbd = i;
+			csr32w(ctlr, RmcBD, ctlr->rmcbd);
+		}
+	}
+	else
+		r = -1;
+
+	free(cb);
+	if(r == 0)
+		return n;
+	return r;
+}
+
+static int
+_ga620transmit(Ether* edev)
+{
+	Sbd *sbd;
+	Block *bp;
+	Ctlr *ctlr;
+	int sci, spi, work;
+
+	/*
+	 * For now there are no smarts here, just empty the
+	 * ring and try to fill it back up. Tuning comes later.
+	 */
+	ctlr = edev->ctlr;
+	ilock(&ctlr->srlock);
+
+	/*
+	 * Free any completed packets.
+	 * Ctlr->sci[0] is where the NIC has got to consuming the ring.
+	 * Ctlr->sci[2] is where the host has got to tidying up after the
+	 * NIC has done with the packets.
+	 */
+	work = 0;
+	for(sci = ctlr->sci[2]; sci != ctlr->sci[0]; sci = NEXT(sci, Nsr)){
+		if(ctlr->srb[sci] == nil)
+			continue;
+		freeb(ctlr->srb[sci]);
+		ctlr->srb[sci] = nil;
+		work++;
+	}
+	ctlr->sci[2] = sci;
+
+	sci = PREV(sci, Nsr);
+	for(spi = csr32r(ctlr, Spi); spi != sci; spi = NEXT(spi, Nsr)){
+		if((bp = qget(edev->oq)) == nil)
+			break;
+
+		sbd = &ctlr->sr[spi];
+		sethost64(&sbd->addr, bp->rp);
+		sbd->lenflags = BLEN(bp)<<16 | Fend;
+
+		ctlr->srb[spi] = bp;
+		work++;
+	}
+	csr32w(ctlr, Spi, spi);
+
+	iunlock(&ctlr->srlock);
+
+	return work;
+}
+
+static void
+ga620transmit(Ether* edev)
+{
+	_ga620transmit(edev);
+}
+
+static void
+ga620replenish(Ctlr* ctlr)
+{
+	Rbd *rbd;
+	int rspi;
+	Block *bp;
+
+	rspi = csr32r(ctlr, Rspi);
+	while(ctlr->nrsr < NrsrHI){
+		if((bp = iallocb(ETHERMAXTU+4)) == nil)
+			break;
+		rbd = &ctlr->rsr[rspi];
+		sethost64(&rbd->addr, bp->rp);
+		rbd->indexlen = rspi<<16 | (ETHERMAXTU+4);
+		rbd->flags = 0;
+		rbd->opaque = bp;
+
+		rspi = NEXT(rspi, Nrsr);
+		ctlr->nrsr++;
+	}
+	csr32w(ctlr, Rspi, rspi);
+}
+
+static void
+ga620event(Ether *edev, int eci, int epi)
+{
+	unsigned event, code;
+	Ctlr *ctlr;
+
+	ctlr = edev->ctlr;
+	while(eci != epi){
+		event = ctlr->er[eci].event;
+		code = (event >> 12) & ((1<<12)-1);
+		switch(event>>24){
+		case 0x01:		/* firmware operational */
+			/* host stack (us) is up.  3rd arg of 2 means down. */
+			ga620command(ctlr, 0x01, 0x01, 0x00);
+			/*
+			 * link negotiation: any speed is okay.
+			 * 3rd arg of 1 selects gigabit only; 2 10/100 only.
+			 */
+			ga620command(ctlr, 0x0B, 0x00, 0x00);
+			print("#l%d: ga620: port %8.8uX: firmware is up\n",
+				edev->ctlrno, ctlr->port);
+			break;
+		case 0x04:		/* statistics updated */
+			break;
+		case 0x06:		/* link state changed */
+			switch (code) {
+			case 1:
+				edev->mbps = 1000;
+				break;
+			case 2:
+				print("#l%d: link down\n", edev->ctlrno);
+				break;
+			case 3:
+				edev->mbps = 100;	/* it's 10 or 100 */
+				break;
+			}
+			if (code != 2)
+				print("#l%d: %dMbps link up\n",
+					edev->ctlrno, edev->mbps);
+			break;
+		case 0x07:		/* event error */
+		default:
+			print("#l%d: ga620: er[%d] = %8.8uX\n", edev->ctlrno,
+				eci, event);
+			break;
+		}
+		eci = NEXT(eci, Ner);
+	}
+	csr32w(ctlr, Eci, eci);
+}
+
+static void
+ga620receive(Ether* edev)
+{
+	int len;
+	Rbd *rbd;
+	Block *bp;
+	Ctlr* ctlr;
+
+	ctlr = edev->ctlr;
+	while(ctlr->rrrci != ctlr->rrrpi[0]){
+		rbd = &ctlr->rrr[ctlr->rrrci];
+		/*
+		 * Errors are collected in the statistics block so
+		 * no need to tally them here, let ifstat do the work.
+		 */
+		len = rbd->indexlen & 0xFFFF;
+		if(!(rbd->flags & Ferror) && len != 0){
+			bp = rbd->opaque;
+			bp->wp = bp->rp+len;
+			etheriq(edev, bp, 1);
+		}
+		else
+			freeb(rbd->opaque);
+		rbd->opaque = nil;
+
+		if(rbd->flags & Frjr)
+			ctlr->nrjr--;
+		else if(rbd->flags & Frmr)
+			ctlr->nrmr--;
+		else
+			ctlr->nrsr--;
+
+		ctlr->rrrci = NEXT(ctlr->rrrci, Nrrr);
+	}
+}
+
+static void
+ga620interrupt(Ureg*, void* arg)
+{
+	int csr, ie, work;
+	Ctlr *ctlr;
+	Ether *edev;
+	uvlong tsc0, tsc1;
+
+	edev = arg;
+	ctlr = edev->ctlr;
+
+	if(!(csr32r(ctlr, Mhc) & Is))
+		return;
+	cycles(&tsc0);
+
+	ctlr->interrupts++;
+	csr32w(ctlr, Hi, 1);
+
+	ie = 0;
+	work = 0;
+	while(ie < 2){
+		if(ctlr->rrrci != ctlr->rrrpi[0]){
+			ga620receive(edev);
+			work = 1;
+		}
+
+		if(_ga620transmit(edev) != 0)
+			work = 1;
+
+		csr = csr32r(ctlr, Eci);
+		if(csr != ctlr->epi[0]){
+			ga620event(edev, csr, ctlr->epi[0]);
+			work = 1;
+		}
+
+		if(ctlr->nrsr <= NrsrLO)
+			ga620replenish(ctlr);
+		if(work == 0){
+			if(ie == 0)
+				csr32w(ctlr, Hi, 0);
+			ie++;
+		}
+		work = 0;
+	}
+
+	cycles(&tsc1);
+	ctlr->ticks += tsc1-tsc0;
+}
+
+static void
+ga620lmw(Ctlr* ctlr, int addr, int* data, int len)
+{
+	int i, l, lmw, v;
+
+	/*
+	 * Write to or clear ('data' == nil) 'len' bytes of the NIC
+	 * local memory at address 'addr'.
+	 * The destination address and count should be 32-bit aligned.
+	 */
+	v = 0;
+	while(len > 0){
+		/*
+		 * 1) Set the window. The (Lmwsz-1) bits are ignored
+		 *    in Wba when accessing through the local memory window;
+		 * 2) Find the minimum of how many bytes still to
+		 *    transfer and how many left in this window;
+		 * 3) Create the offset into the local memory window in the
+		 *    shared memory space then copy (or zero) the data;
+		 * 4) Bump the counts.
+		 */
+		csr32w(ctlr, Wba, addr);
+
+		l = ROUNDUP(addr+1, Lmwsz) - addr;
+		if(l > len)
+			l = len;
+
+		lmw = Lmw + (addr & (Lmwsz-1));
+		for(i = 0; i < l; i += 4){
+			if(data != nil)
+				v = *data++;
+			csr32w(ctlr, lmw+i, v);
+		}
+
+		len -= l;
+		addr += l;
+	}
+}
+
+static int
+ga620init(Ether* edev)
+{
+	Ctlr *ctlr;
+	Host64 host64;
+	int csr, ea, i, flags;
+
+	ctlr = edev->ctlr;
+
+	/*
+	 * Load the MAC address.
+	 */
+	ea = edev->ea[0]<<8 | edev->ea[1];
+	csr32w(ctlr, Mac, ea);
+	ea = edev->ea[2]<<24 | edev->ea[3]<<16 | edev->ea[4]<<8 | edev->ea[5];
+	csr32w(ctlr, Mac+4, ea);
+
+	/*
+	 * General Information Block.
+	 */
+	ctlr->gib = malloc(sizeof(Gib));
+	sethost64(&host64, ctlr->gib);
+	csr32w(ctlr, Gip, host64.hi);
+	csr32w(ctlr, Gip+4, host64.lo);
+
+	/*
+	 * Event Ring.
+	 * This is located in host memory. Allocate the ring,
+	 * tell the NIC where it is and initialise the indices.
+	 */
+	ctlr->er = malign(sizeof(Ere)*Ner);
+	sethost64(&ctlr->gib->ercb.addr, ctlr->er);
+	sethost64(&ctlr->gib->epp, ctlr->epi);
+	csr32w(ctlr, Eci, 0);
+
+	/*
+	 * Command Ring.
+	 * This is located in the General Communications Region
+	 * and so the value placed in the Rcb is unused, the NIC
+	 * knows where it is. Stick in the value according to
+	 * the datasheet anyway.
+	 * Initialise the ring and indices.
+	 */
+	ctlr->gib->crcb.addr.lo = Cr-0x400;
+	for(i = 0; i < Ncr*4; i += 4)
+		csr32w(ctlr, Cr+i, 0);
+	csr32w(ctlr, Cpi, 0);
+	csr32w(ctlr, Cci, 0);
+
+	/*
+	 * Send Ring.
+	 * This ring is either in NIC memory at a fixed location depending
+	 * on how big the ring is or it is in host memory. If in NIC
+	 * memory it is accessed via the Local Memory Window; with a send
+	 * ring size of 128 the window covers the whole ring and then need
+	 * only be set once:
+	 *	ctlr->sr = (uchar*)ctlr->nic+Lmw;
+	 *	ga620lmw(ctlr, Sr, nil, sizeof(Sbd)*Nsr);
+	 *	ctlr->gib->srcb.addr.lo = Sr;
+	 * There is nowhere in the Sbd to hold the Block* associated
+	 * with this entry so an external array must be kept.
+	 */
+	ctlr->sr = malign(sizeof(Sbd)*Nsr);
+	sethost64(&ctlr->gib->srcb.addr, ctlr->sr);
+	if(ctlr->hardwarecksum)
+		flags = TcpUdpCksum|NoPseudoHdrCksum|HostRing;
+	else 
+		flags = HostRing;
+	if(ctlr->coalupdateonly) 
+		flags |= CoalUpdateOnly;
+	ctlr->gib->srcb.control = Nsr<<16 | flags;
+	sethost64(&ctlr->gib->scp, ctlr->sci);
+	csr32w(ctlr, Spi, 0);
+	ctlr->srb = malloc(sizeof(Block*)*Nsr);
+
+	/*
+	 * Receive Standard Ring.
+	 */
+	ctlr->rsr = malign(sizeof(Rbd)*Nrsr);
+	sethost64(&ctlr->gib->rsrcb.addr, ctlr->rsr);
+	if(ctlr->hardwarecksum)
+		flags = TcpUdpCksum|NoPseudoHdrCksum;
+	else
+		flags = 0;
+	ctlr->gib->rsrcb.control = (ETHERMAXTU+4)<<16 | flags;
+	csr32w(ctlr, Rspi, 0);
+
+	/*
+	 * Jumbo and Mini Rings. Unused for now.
+	 */
+	ctlr->gib->rjrcb.control = RingDisabled;
+	ctlr->gib->rmrcb.control = RingDisabled;
+
+	/*
+	 * Receive Return Ring.
+	 * This is located in host memory. Allocate the ring,
+	 * tell the NIC where it is and initialise the indices.
+	 */
+	ctlr->rrr = malign(sizeof(Rbd)*Nrrr);
+	sethost64(&ctlr->gib->rrrcb.addr, ctlr->rrr);
+	ctlr->gib->rrrcb.control = Nrrr<<16 | 0;
+	sethost64(&ctlr->gib->rrrpp, ctlr->rrrpi);
+	ctlr->rrrci = 0;
+
+	/*
+	 * Refresh Stats Pointer.
+	 * For now just point it at the existing statistics block.
+	 */
+	sethost64(&ctlr->gib->rsp, ctlr->gib->statistics);
+
+	/*
+	 * DMA configuration.
+	 * Use the recommended values.
+	 */
+	csr32w(ctlr, DMArc, 0x80);
+	csr32w(ctlr, DMAwc, 0x80);
+
+	/*
+	 * Transmit Buffer Ratio.
+	 * Set to 1/3 of available buffer space (units are 1/64ths)
+	 * if using Jumbo packets, ~64KB otherwise (assume 1MB on NIC).
+	 */
+	if(NrjrHI > 0 || Nsr > 128)
+		csr32w(ctlr, Tbr, 64/3);
+	else
+		csr32w(ctlr, Tbr, 4);
+
+	/*
+	 * Tuneable parameters.
+	 * These defaults are based on the tuning hints in the Alteon
+	 * Host/NIC Software Interface Definition and example software.
+	 */
+	ctlr->rct = 1/*100*/;
+	csr32w(ctlr, Rct, ctlr->rct);
+	ctlr->sct = 0;
+	csr32w(ctlr, Sct, ctlr->sct);
+	ctlr->st = 1000000;
+	csr32w(ctlr, St, ctlr->st);
+	ctlr->smcbd = Nsr/4;
+	csr32w(ctlr, SmcBD, ctlr->smcbd);
+	ctlr->rmcbd = 4/*6*/;
+	csr32w(ctlr, RmcBD, ctlr->rmcbd);
+
+	/*
+	 * Enable DMA Assist Logic.
+	 */
+	csr = csr32r(ctlr, DMAas) & ~0x03;
+	csr32w(ctlr, DMAas, csr|0x01);
+
+	/*
+	 * Link negotiation.
+	 * The bits are set here but the NIC must be given a command
+	 * once it is running to set negotiation in motion.
+	 */
+	csr32w(ctlr, Gln, Le|Lean|Lofc|Lfd|L1000MB|Lpref);
+	csr32w(ctlr, Fln, Le|Lean|Lhd|Lfd|L100MB|L10MB);
+
+	/*
+	 * A unique index for this controller and the maximum packet
+	 * length expected.
+	 * For now only standard packets are expected.
+	 */
+	csr32w(ctlr, Ifx, 1);
+	csr32w(ctlr, IfMTU, ETHERMAXTU+4);
+
+	/*
+	 * Enable Interrupts.
+	 * There are 3 ways to mask interrupts - a bit in the Mhc (which
+	 * is already cleared), the Mi register and the Hi mailbox.
+	 * Writing to the Hi mailbox has the side-effect of clearing the
+	 * PCI interrupt.
+	 */
+	csr32w(ctlr, Mi, 0);
+	csr32w(ctlr, Hi, 0);
+
+	/*
+	 * Start the firmware.
+	 */
+	csr32w(ctlr, CPUApc, tigon2FwStartAddr);
+	csr = csr32r(ctlr, CPUAstate) & ~CPUhalt;
+	csr32w(ctlr, CPUAstate, csr);
+
+	return 0;
+}
+
+static int
+at24c32io(Ctlr* ctlr, char* op, int data)
+{
+	char *lp, *p;
+	int i, loop, mlc, r;
+
+	mlc = csr32r(ctlr, Mlc);
+
+	r = 0;
+	loop = -1;
+	lp = nil;
+	for(p = op; *p != '\0'; p++){
+		switch(*p){
+		default:
+			return -1;
+		case ' ':
+			continue;
+		case ':':			/* start of 8-bit loop */
+			if(lp != nil)
+				return -1;
+			lp = p;
+			loop = 7;
+			continue;
+		case ';':			/* end of 8-bit loop */
+			if(lp == nil)
+				return -1;
+			loop--;
+			if(loop >= 0)
+				p = lp;
+			else
+				lp = nil;
+			continue;
+		case 'C':			/* assert clock */
+			mlc |= EEclk;
+			break;
+		case 'c':			/* deassert clock */
+			mlc &= ~EEclk;
+			break;
+		case 'D':			/* next bit in 'data' byte */
+			if(loop < 0)
+				return -1;
+			if(data & (1<<loop))
+				mlc |= EEdo;
+			else
+				mlc &= ~EEdo;
+			break;
+		case 'E':			/* enable data output */
+			mlc |= EEdoe;
+			break;
+		case 'e':			/* disable data output */
+			mlc &= ~EEdoe;
+			break;
+		case 'I':			/* input bit */
+			i = (csr32r(ctlr, Mlc) & EEdi) != 0;
+			if(loop >= 0)
+				r |= (i<<loop);
+			else
+				r = i;
+			continue;
+		case 'O':			/* assert data output */
+			mlc |= EEdo;
+			break;
+		case 'o':			/* deassert data output */
+			mlc &= ~EEdo;
+			break;
+		}
+		csr32w(ctlr, Mlc, mlc);
+		microdelay(1);
+	}
+	if(loop >= 0)
+		return -1;
+	return r;
+}
+
+static int
+at24c32r(Ctlr* ctlr, int addr)
+{
+	int data;
+
+	/*
+	 * Read a byte at address 'addr' from the Atmel AT24C32
+	 * Serial EEPROM. The 2-wire EEPROM access is controlled
+	 * by 4 bits in Mlc. See the AT24C32 datasheet for
+	 * protocol details.
+	 */
+	/*
+	 * Start condition - a high to low transition of data
+	 * with the clock high must precede any other command.
+	 */
+	at24c32io(ctlr, "OECoc", 0);
+
+	/*
+	 * Perform a random read at 'addr'. A dummy byte
+	 * write sequence is performed to clock in the device
+	 * and data word addresses (0 and 'addr' respectively).
+	 */
+	data = -1;
+	if(at24c32io(ctlr, "oE :DCc; oeCIc", 0xA0) != 0)
+		goto stop;
+	if(at24c32io(ctlr, "oE :DCc; oeCIc", addr>>8) != 0)
+		goto stop;
+	if(at24c32io(ctlr, "oE :DCc; oeCIc", addr) != 0)
+		goto stop;
+
+	/*
+	 * Now send another start condition followed by a
+	 * request to read the device. The EEPROM responds
+	 * by clocking out the data.
+	 */
+	at24c32io(ctlr, "OECoc", 0);
+	if(at24c32io(ctlr, "oE :DCc; oeCIc", 0xA1) != 0)
+		goto stop;
+	data = at24c32io(ctlr, ":CIc;", 0xA1);
+
+stop:
+	/*
+	 * Stop condition - a low to high transition of data
+	 * with the clock high is a stop condition. After a read
+	 * sequence, the stop command will place the EEPROM in
+	 * a standby power mode.
+	 */
+	at24c32io(ctlr, "oECOc", 0);
+
+	return data;
+}
+
+static int
+ga620detach(Ctlr* ctlr)
+{
+	int timeo;
+
+	/*
+	 * Hard reset (don't know which endian so catch both);
+	 * enable for little-endian mode;
+	 * wait for code to be loaded from serial EEPROM or flash;
+	 * make sure CPU A is halted.
+	 */
+	csr32w(ctlr, Mhc, Hr<<24 | Hr);
+	csr32w(ctlr, Mhc, (Eews|Ci)<<24 | Eews|Ci);
+
+	microdelay(1);
+	for(timeo = 0; timeo < 500000; timeo++){
+		if((csr32r(ctlr, CPUAstate) & (CPUhie|CPUrf)) == CPUhie)
+			break;
+		microdelay(1);
+	}
+	if((csr32r(ctlr, CPUAstate) & (CPUhie|CPUrf)) != CPUhie)
+		return -1;
+	csr32w(ctlr, CPUAstate, CPUhalt);
+
+	/*
+	 * After reset, CPU B seems to be stuck in 'CPUrf'.
+	 * Worry about it later.
+	 */
+	csr32w(ctlr, CPUBstate, CPUhalt);
+
+	return 0;
+}
+
+static void
+ga620shutdown(Ether* ether)
+{
+print("ga620shutdown\n");
+	ga620detach(ether->ctlr);
+}
+
+static int
+ga620reset(Ctlr* ctlr)
+{
+	int cls, csr, i, r;
+
+	if(ga620detach(ctlr) < 0)
+		return -1;
+
+	/*
+	 * Tigon 2 PCI NICs have 512KB SRAM per bank.
+	 * Clear out any lingering serial EEPROM state
+	 * bits.
+	 */
+	csr = csr32r(ctlr, Mlc) & ~(EEdi|EEdo|EEdoe|EEclk|SRAMmask);
+	csr32w(ctlr, Mlc, SRAM512|csr);
+	csr = csr32r(ctlr, Mc);
+	csr32w(ctlr, Mc, SyncSRAM|csr);
+
+	/*
+	 * Initialise PCI State register.
+	 * If PCI Write-and-Invalidate is enabled set the max write DMA
+	 * value to the host cache-line size (32 on Pentium or later).
+	 */
+	csr = csr32r(ctlr, Ps) & (PCI32|PCI66);
+	csr |= PCIwcmd|PCIrcmd|PCImrm;
+	if(ctlr->pcidev->pcr & 0x0010){
+		cls = pcicfgr8(ctlr->pcidev, PciCLS) * 4;
+		if(cls != 32)
+			pcicfgw8(ctlr->pcidev, PciCLS, 32/4);
+		csr |= PCIwm32;
+	}
+	csr32w(ctlr, Ps, csr);
+
+	/*
+	 * Operating Mode.
+	 */
+	csr32w(ctlr, Om, Fatal|NoJFrag|BswapDMA|WswapBD);
+
+	/*
+	 * Snarf the MAC address from the serial EEPROM.
+	 */
+	for(i = 0; i < Eaddrlen; i++){
+		if((r = at24c32r(ctlr, 0x8E+i)) == -1)
+			return -1;
+		ctlr->ea[i] = r;
+	}
+
+	/*
+	 * Load the firmware.
+	 */
+	ga620lmw(ctlr, tigon2FwTextAddr, tigon2FwText, tigon2FwTextLen);
+	ga620lmw(ctlr, tigon2FwRodataAddr, tigon2FwRodata, tigon2FwRodataLen);
+	ga620lmw(ctlr, tigon2FwDataAddr, tigon2FwData, tigon2FwDataLen);
+	ga620lmw(ctlr, tigon2FwSbssAddr, nil, tigon2FwSbssLen);
+	ga620lmw(ctlr, tigon2FwBssAddr, nil, tigon2FwBssLen);
+
+	return 0;
+}
+
+static void
+ga620pci(void)
+{
+	void *mem;
+	Pcidev *p;
+	Ctlr *ctlr;
+
+	p = nil;
+	while(p = pcimatch(p, 0, 0)){
+		if(p->ccrb != 0x02 || p->ccru != 0)
+			continue;
+
+		switch(p->did<<16 | p->vid){
+		default:
+			continue;
+		case 0x620A<<16 | 0x1385:	/* Netgear GA620 fiber */
+		case 0x630A<<16 | 0x1385:	/* Netgear GA620T copper */
+		case 0x0001<<16 | 0x12AE:	/* Alteon Acenic fiber
+						 * and DEC DEGPA-SA */
+		case 0x0002<<16 | 0x12AE:	/* Alteon Acenic copper */
+		case 0x0009<<16 | 0x10A9:	/* SGI Acenic */
+			break;
+		}
+
+		mem = vmap(p->mem[0].bar & ~0x0F, p->mem[0].size);
+		if(mem == 0){
+			print("ga620: can't map %8.8luX\n", p->mem[0].bar);
+			continue;
+		}
+
+		ctlr = malloc(sizeof(Ctlr));
+		ctlr->port = p->mem[0].bar & ~0x0F;
+		ctlr->pcidev = p;
+		ctlr->id = p->did<<16 | p->vid;
+
+		ctlr->nic = mem;
+		if(ga620reset(ctlr)){
+			free(ctlr);
+			continue;
+		}
+
+		if(ctlrhead != nil)
+			ctlrtail->next = ctlr;
+		else
+			ctlrhead = ctlr;
+		ctlrtail = ctlr;
+	}
+}
+
+static void
+ga620promiscuous(void *arg, int on)
+{
+	Ether *ether = arg;
+
+	/* 3rd arg: 1 enables, 2 disables */
+	ga620command(ether->ctlr, 0xa, (on? 1: 2), 0);
+}
+
+static void
+ga620multicast(void *arg, uchar *addr, int on)
+{
+	Ether *ether = arg;
+
+	USED(addr);
+	/* 3rd arg: 1 enables, 2 disables */
+	ga620command(ether->ctlr, 0xe, (on? 1: 2), 0);
+}
+
+static int
+ga620pnp(Ether* edev)
+{
+	Ctlr *ctlr;
+	uchar ea[Eaddrlen];
+
+	if(ctlrhead == nil)
+		ga620pci();
+
+	/*
+	 * Any adapter matches if no edev->port is supplied,
+	 * otherwise the ports must match.
+	 */
+	for(ctlr = ctlrhead; ctlr != nil; ctlr = ctlr->next){
+		if(ctlr->active)
+			continue;
+		if(edev->port == 0 || edev->port == ctlr->port){
+			ctlr->active = 1;
+			break;
+		}
+	}
+	if(ctlr == nil)
+		return -1;
+
+	edev->ctlr = ctlr;
+	edev->port = ctlr->port;
+	edev->irq = ctlr->pcidev->intl;
+	edev->tbdf = ctlr->pcidev->tbdf;
+	edev->mbps = 1000;		/* placeholder */
+
+	/*
+	 * Check if the adapter's station address is to be overridden.
+	 * If not, read it from the EEPROM and set in ether->ea prior to
+	 * loading the station address in the hardware.
+	 */
+	memset(ea, 0, Eaddrlen);
+	if(memcmp(ea, edev->ea, Eaddrlen) == 0)
+		memmove(edev->ea, ctlr->ea, Eaddrlen);
+
+	ga620init(edev);
+
+	/*
+	 * Linkage to the generic ethernet driver.
+	 */
+	edev->attach = ga620attach;
+	edev->transmit = ga620transmit;
+	edev->interrupt = ga620interrupt;
+	edev->ifstat = ga620ifstat;
+	edev->ctl = ga620ctl;
+
+	edev->arg = edev;
+	edev->promiscuous = ga620promiscuous;
+	edev->multicast = ga620multicast;
+	edev->shutdown = ga620shutdown;
+
+	return 0;
+}
+
+void
+etherga620link(void)
+{
+	addethercard("GA620", ga620pnp);
+}
--- /dev/null
+++ b/os/pc/etherga620fw.h
@@ -1,0 +1,4858 @@
+/* Generated by genfw.c */
+#define tigon2FwReleaseMajor 0xc
+#define tigon2FwReleaseMinor 0x4
+#define tigon2FwReleaseFix 0xb
+#define tigon2FwStartAddr 0x00004000
+#define tigon2FwTextAddr 0x00004000
+#define tigon2FwTextLen 0x11bc0
+#define tigon2FwRodataAddr 0x00015bc0
+#define tigon2FwRodataLen 0x10d0
+#define tigon2FwDataAddr 0x00016cc0
+#define tigon2FwDataLen 0x1c0
+#define tigon2FwSbssAddr 0x00016e80
+#define tigon2FwSbssLen 0xcc
+#define tigon2FwBssAddr 0x00016f50
+#define tigon2FwBssLen 0x20c0
+static int tigon2FwText[/*(MAX_TEXT_LEN/4) + 1*/] = {
+0x0, 
+0x10000003, 0x0, 0xd, 0xd, 
+0x3c1d0001, 0x8fbd6d20, 0x3a0f021, 0x3c100000, 
+0x26104000, 0xc0010c0, 0x0, 0xd, 
+0x3c1d0001, 0x8fbd6d24, 0x3a0f021, 0x3c100000, 
+0x26104000, 0xc0017e0, 0x0, 0xd, 
+0x0, 0x0, 0x0, 0x0, 
+0x0, 0x0, 0x0, 0x0, 
+0x0, 0x0, 0x0, 0x0, 
+0x0, 0x0, 0x0, 0x0, 
+0x0, 0x0, 0x0, 0x0, 
+0x0, 0x0, 0x0, 0x0, 
+0x0, 0x0, 0x0, 0x0, 
+0x0, 0x0, 0x0, 0x0, 
+0x0, 0x0, 0x0, 0x0, 
+0x0, 0x0, 0x0, 0x0, 
+0x0, 0x0, 0x0, 0x2000008, 
+0x0, 0x800172f, 0x3c0a0001, 0x800172f, 
+0x3c0a0002, 0x800172f, 0x0, 0x8002cac, 
+0x0, 0x8002c4f, 0x0, 0x800172f, 
+0x3c0a0004, 0x800328a, 0x0, 0x8001a52, 
+0x0, 0x800394d, 0x0, 0x80038f4, 
+0x0, 0x800172f, 0x3c0a0006, 0x80039bb, 
+0x3c0a0007, 0x800172f, 0x3c0a0008, 0x800172f, 
+0x3c0a0009, 0x8003a13, 0x0, 0x8002ea6, 
+0x0, 0x800172f, 0x3c0a000b, 0x800172f, 
+0x3c0a000c, 0x800172f, 0x3c0a000d, 0x80028fb, 
+0x0, 0x8002890, 0x0, 0x800172f, 
+0x3c0a000e, 0x800208c, 0x0, 0x8001964, 
+0x0, 0x8001a04, 0x0, 0x8003ca6, 
+0x0, 0x8003c94, 0x0, 0x800172f, 
+0x0, 0x800191a, 0x0, 0x800172f, 
+0x0, 0x800172f, 0x3c0a0013, 0x800172f, 
+0x3c0a0014, 0x0, 0x0, 0x0, 
+0x0, 0x0, 0x0, 0x0, 
+0x0, 0x0, 0x0, 0x0, 
+0x0, 0x0, 0x0, 0x0, 
+0x0, 0x0, 0x0, 0x0, 
+0x0, 0x0, 0x0, 0x0, 
+0x0, 0x0, 0x0, 0x0, 
+0x0, 0x0, 0x0, 0x0, 
+0x0, 0x0, 0x0, 0x0, 
+0x0, 0x0, 0x0, 0x0, 
+0x0, 0x0, 0x0, 0x0, 
+0x0, 0x0, 0x0, 0x0, 
+0x0, 0x0, 0x0, 0x0, 
+0x0, 0x0, 0x0, 0x0, 
+0x0, 0x0, 0x0, 0x0, 
+0x0, 0x0, 0x0, 0x27bdffe0, 
+0x3c1cc000, 0xafbf001c, 0xafb00018, 0x8f820140, 
+0x24030003, 0xaf8300ec, 0x34420004, 0xc002b20, 
+0xaf820140, 0x3c0100c0, 0xc001763, 0xac203ffc, 
+0x401821, 0x3c020010, 0x3c010001, 0xac236e9c, 
+0x10620011, 0x43102b, 0x14400002, 0x3c020020, 
+0x3c020008, 0x1062000c, 0x24050100, 0x3c060001, 
+0x8cc66e9c, 0x3c040001, 0x24845c74, 0x3821, 
+0xafa00010, 0xc002b3b, 0xafa00014, 0x3c020020, 
+0x3c010001, 0xac226e9c, 0x24020008, 0x3c010001, 
+0xac226eb4, 0x2402001f, 0x3c010001, 0xac226ec4, 
+0x24020016, 0x3c010001, 0xac226e98, 0x3c05fffe, 
+0x34a56f08, 0x3c020001, 0x8c426e9c, 0x3c030002, 
+0x24639010, 0x3c040001, 0x8c846cc4, 0x431023, 
+0x14800002, 0x458021, 0x2610fa38, 0x2402f000, 
+0x2028024, 0xc001785, 0x2002021, 0x2022823, 
+0x3c040020, 0x821823, 0x651823, 0x247bb000, 
+0x3c03fffe, 0x3463bf08, 0x363b821, 0x3c0600bf, 
+0x34c6f000, 0x3c070001, 0x8ce76cc0, 0x3c0300bf, 
+0x3463e000, 0x852023, 0x3c010001, 0xac246ea8, 
+0x822023, 0x3c010001, 0xac256e90, 0x52842, 
+0x3c010001, 0xac226e84, 0x27620ffc, 0x3c010001, 
+0xac226d20, 0x27621ffc, 0xdb3023, 0x7b1823, 
+0x3c010001, 0xac246e88, 0x3c010001, 0xac256eac, 
+0x3c010001, 0xac226d24, 0xaf860150, 0x10e00011, 
+0xaf830250, 0x3c1d0001, 0x8fbd6ccc, 0x3a0f021, 
+0xc001749, 0x0, 0x3c020001, 0x8c426cd0, 
+0x3c030001, 0x8c636cd4, 0x2442fe00, 0x24630200, 
+0x3c010001, 0xac226cd0, 0x3c010001, 0x10000004, 
+0xac236cd4, 0x3c1d0001, 0x8fbd6d20, 0x3a0f021, 
+0x3c020001, 0x8c426cc4, 0x1040000d, 0x26fafa38, 
+0x3c020001, 0x8c426cd0, 0x3c030001, 0x8c636cd4, 
+0x3c1a0001, 0x8f5a6cd4, 0x2442fa38, 0x246305c8, 
+0x3c010001, 0xac226cd0, 0x3c010001, 0xac236cd4, 
+0x3c020001, 0x8c426cc8, 0x14400003, 0x0, 
+0x3c010001, 0xac206cd0, 0xc001151, 0x0, 
+0x8fbf001c, 0x8fb00018, 0x3e00008, 0x27bd0020, 
+0x3c020001, 0x8c426cd0, 0x3c030001, 0x8c636cd4, 
+0x27bdff98, 0xafb00048, 0x3c100001, 0x8e1066b8, 
+0xafb20050, 0x3c120000, 0x26524100, 0xafbf0060, 
+0xafbe005c, 0xafb50058, 0xafb30054, 0xafb1004c, 
+0xafa20034, 0xafa30030, 0xafa00010, 0xafa00014, 
+0x8f860040, 0x3c040001, 0x24845c80, 0x24050200, 
+0x3c010001, 0xac326e80, 0xc002b3b, 0x2003821, 
+0x8f830040, 0x3c02f000, 0x621824, 0x3c026000, 
+0x1062000b, 0xa3a0003f, 0x240e0001, 0x3c040001, 
+0x24845c88, 0xa3ae003f, 0xafa00010, 0xafa00014, 
+0x8f860040, 0x24050300, 0xc002b3b, 0x2003821, 
+0x8f820240, 0x3c030001, 0x431025, 0xaf820240, 
+0xaf800048, 0x8f820048, 0x14400005, 0x0, 
+0xaf800048, 0x8f820048, 0x10400004, 0x0, 
+0xaf800048, 0x10000003, 0x2e02021, 0xaf80004c, 
+0x2e02021, 0x3c050001, 0xc002ba8, 0x34a540f8, 
+0x3402021, 0xc002ba8, 0x240505c8, 0x3c020001, 
+0x8c426ea8, 0x3c0d0001, 0x8dad6e88, 0x3c030001, 
+0x8c636e84, 0x3c080001, 0x8d086e90, 0x3c090001, 
+0x8d296eac, 0x3c0a0001, 0x8d4a6eb4, 0x3c0b0001, 
+0x8d6b6ec4, 0x3c0c0001, 0x8d8c6e98, 0x3c040001, 
+0x24845c94, 0x24050400, 0xaf42013c, 0x8f42013c, 
+0x24060001, 0x24070001, 0xaf400000, 0xaf4d0138, 
+0xaf430144, 0xaf480148, 0xaf49014c, 0xaf4a0150, 
+0xaf4b0154, 0xaf4c0158, 0x2442ff80, 0xaf420140, 
+0x24020001, 0xafa20010, 0xc002b3b, 0xafa00014, 
+0x8f420138, 0xafa20010, 0x8f42013c, 0xafa20014, 
+0x8f460144, 0x8f470148, 0x3c040001, 0x24845ca0, 
+0xc002b3b, 0x24050500, 0xafb70010, 0xafba0014, 
+0x8f46014c, 0x8f470150, 0x3c040001, 0x24845cac, 
+0xc002b3b, 0x24050600, 0x3c020001, 0x8c426e9c, 
+0x3603821, 0x3c060002, 0x24c69010, 0x2448ffff, 
+0x1061824, 0xe81024, 0x43102b, 0x10400006, 
+0x24050900, 0x3c040001, 0x24845cb8, 0xafa80010, 
+0xc002b3b, 0xafa00014, 0x8f82000c, 0xafa20010, 
+0x8f82003c, 0xafa20014, 0x8f860000, 0x8f870004, 
+0x3c040001, 0x24845cc4, 0xc002b3b, 0x24051000, 
+0x8c020220, 0x8c030224, 0x8c060218, 0x8c07021c, 
+0x3c040001, 0x24845ccc, 0x24051100, 0xafa20010, 
+0xc002b3b, 0xafa30014, 0xaf800054, 0xaf80011c, 
+0x8c020218, 0x30420002, 0x10400009, 0x0, 
+0x8c020220, 0x3c030002, 0x34630004, 0x431025, 
+0xaf42000c, 0x8c02021c, 0x10000008, 0x34420004, 
+0x8c020220, 0x3c030002, 0x34630006, 0x431025, 
+0xaf42000c, 0x8c02021c, 0x34420006, 0xaf420014, 
+0x8c020218, 0x30420010, 0x1040000a, 0x0, 
+0x8c02021c, 0x34420004, 0xaf420010, 0x8c020220, 
+0x3c03000a, 0x34630004, 0x431025, 0x10000009, 
+0xaf420008, 0x8c020220, 0x3c03000a, 0x34630006, 
+0x431025, 0xaf420008, 0x8c02021c, 0x34420006, 
+0xaf420010, 0x24020001, 0xaf8200a0, 0xaf8200b0, 
+0x8f830054, 0x8f820054, 0xaf8000d0, 0xaf8000c0, 
+0x10000002, 0x24630064, 0x8f820054, 0x621023, 
+0x2c420065, 0x1440fffc, 0x0, 0x8c040208, 
+0x8c05020c, 0x26e20028, 0xaee20020, 0x24020490, 
+0xaee20010, 0xaee40008, 0xaee5000c, 0x26e40008, 
+0x8c820000, 0x8c830004, 0xaf820090, 0xaf830094, 
+0x8c820018, 0xaf8200b4, 0x9482000a, 0xaf82009c, 
+0x8f420014, 0xaf8200b0, 0x8f8200b0, 0x30420004, 
+0x1440fffd, 0x0, 0x8f8200b0, 0x3c03ef00, 
+0x431024, 0x10400021, 0x0, 0x8f8200b4, 
+0xafa20010, 0x8f820090, 0x8f830094, 0x3c040001, 
+0x24845cd4, 0xafa30014, 0x8f8600b0, 0x8f87009c, 
+0x3c050001, 0xc002b3b, 0x34a5200d, 0x3c040001, 
+0x24845ce0, 0x240203c0, 0xafa20010, 0xafa00014, 
+0x8f860144, 0x3c070001, 0x24e75ce8, 0xc002b3b, 
+0x3405dead, 0x8f82011c, 0x34420002, 0xaf82011c, 
+0x8f820220, 0x34420004, 0xaf820220, 0x8f820140, 
+0x3c030001, 0x431025, 0xaf820140, 0x96e20472, 
+0x96e60452, 0x96e70462, 0xafa20010, 0x96e20482, 
+0x3c040001, 0x24845d14, 0x24051200, 0xc002b3b, 
+0xafa20014, 0x96f00452, 0x32020001, 0x10400002, 
+0xb021, 0x24160001, 0x32020002, 0x54400001, 
+0x36d60002, 0x32020008, 0x54400001, 0x36d60004, 
+0x32020010, 0x54400001, 0x36d60008, 0x32020020, 
+0x54400001, 0x36d60010, 0x32020040, 0x54400001, 
+0x36d60020, 0x32020080, 0x54400001, 0x36d60040, 
+0x96e60482, 0x30c20200, 0x54400001, 0x36d64000, 
+0x96e30472, 0x30620200, 0x10400003, 0x30620100, 
+0x10000003, 0x36d62000, 0x54400001, 0x36d61000, 
+0x96f00462, 0x32c24000, 0x14400004, 0x3207009b, 
+0x30c2009b, 0x14e20007, 0x240e0001, 0x32c22000, 
+0x1440000d, 0x32020001, 0x3062009b, 0x10e20009, 
+0x240e0001, 0x3c040001, 0x24845d20, 0x24051300, 
+0x2003821, 0xa3ae003f, 0xafa30010, 0xc002b3b, 
+0xafa00014, 0x32020001, 0x54400001, 0x36d60080, 
+0x32020002, 0x54400001, 0x36d60100, 0x32020008, 
+0x54400001, 0x36d60200, 0x32020010, 0x54400001, 
+0x36d60400, 0x32020080, 0x54400001, 0x36d60800, 
+0x8c020218, 0x30420200, 0x10400002, 0x3c020008, 
+0x2c2b025, 0x8c020218, 0x30420800, 0x10400002, 
+0x3c020080, 0x2c2b025, 0x8c020218, 0x30420400, 
+0x10400002, 0x3c020100, 0x2c2b025, 0x8c020218, 
+0x30420100, 0x10400002, 0x3c020200, 0x2c2b025, 
+0x8c020218, 0x30420080, 0x10400002, 0x3c020400, 
+0x2c2b025, 0x8c020218, 0x30422000, 0x10400002, 
+0x3c020010, 0x2c2b025, 0x8c020218, 0x30424000, 
+0x10400002, 0x3c020020, 0x2c2b025, 0x8c020218, 
+0x30421000, 0x10400002, 0x3c020040, 0x2c2b025, 
+0x8ee20498, 0x8ee3049c, 0xaf420160, 0xaf430164, 
+0x8ee204a0, 0x8ee304a4, 0xaf420168, 0xaf43016c, 
+0x8ee204a8, 0x8ee304ac, 0xaf420170, 0xaf430174, 
+0x8ee20428, 0x8ee3042c, 0xaf420178, 0xaf43017c, 
+0x8ee20448, 0x8ee3044c, 0xaf420180, 0xaf430184, 
+0x8ee20458, 0x8ee3045c, 0xaf420188, 0xaf43018c, 
+0x8ee20468, 0x8ee3046c, 0xaf420190, 0xaf430194, 
+0x8ee20478, 0x8ee3047c, 0xaf420198, 0xaf43019c, 
+0x8ee20488, 0x8ee3048c, 0xaf4201a0, 0xaf4301a4, 
+0x8ee204b0, 0x8ee304b4, 0x24040080, 0xaf4201a8, 
+0xaf4301ac, 0xc002ba8, 0x24050080, 0x8c02025c, 
+0x27440224, 0xaf4201f0, 0x8c020260, 0x24050200, 
+0x24060008, 0xc002bbf, 0xaf4201f8, 0x3c043b9a, 
+0x3484ca00, 0x3821, 0x24020006, 0x24030002, 
+0xaf4201f4, 0x240203e8, 0xaf430204, 0xaf430200, 
+0xaf4401fc, 0xaf420294, 0x24020001, 0xaf430290, 
+0xaf42029c, 0x3c030001, 0x671821, 0x90636cd8, 
+0x3471021, 0x24e70001, 0xa043022c, 0x2ce2000f, 
+0x1440fff8, 0x3471821, 0x24e70001, 0x3c080001, 
+0x350840f8, 0x8f820040, 0x3c040001, 0x24845d2c, 
+0x24051400, 0x21702, 0x24420030, 0xa062022c, 
+0x3471021, 0xa040022c, 0x8c070218, 0x2c03021, 
+0x240205c8, 0xafa20010, 0xc002b3b, 0xafa80014, 
+0x3c040001, 0x24845d38, 0x3c050000, 0x24a55c80, 
+0x24060010, 0x27b10030, 0x2203821, 0x27b30034, 
+0xc0017a3, 0xafb30010, 0x3c030001, 0x8c636cc8, 
+0x1060000a, 0x408021, 0x8fa30030, 0x2405ff00, 
+0x8fa20034, 0x246400ff, 0x852024, 0x831823, 
+0x431023, 0xafa20034, 0xafa40030, 0x3c040001, 
+0x24845d44, 0x3c050000, 0x24a54100, 0x24060108, 
+0x2203821, 0xc0017a3, 0xafb30010, 0x409021, 
+0x32c20003, 0x3c010001, 0xac326e80, 0x10400045, 
+0x2203821, 0x8f820050, 0x3c030010, 0x431024, 
+0x10400016, 0x0, 0x8c020218, 0x30420040, 
+0x1040000f, 0x24020001, 0x8f820050, 0x8c030218, 
+0x240e0001, 0x3c040001, 0x24845d50, 0xa3ae003f, 
+0xafa20010, 0xafa30014, 0x8f870040, 0x24051500, 
+0xc002b3b, 0x2c03021, 0x10000004, 0x0, 
+0x3c010001, 0x370821, 0xa02240f4, 0x3c040001, 
+0x24845d5c, 0x3c050001, 0x24a55b40, 0x3c060001, 
+0x24c65bac, 0xc53023, 0x8f420010, 0x27b30030, 
+0x2603821, 0x27b10034, 0x34420a00, 0xaf420010, 
+0xc0017a3, 0xafb10010, 0x3c040001, 0x24845d70, 
+0x3c050001, 0x24a5b714, 0x3c060001, 0x24c6ba90, 
+0xc53023, 0x2603821, 0xaf420108, 0xc0017a3, 
+0xafb10010, 0x3c040001, 0x24845d8c, 0x3c050001, 
+0x24a5be58, 0x3c060001, 0x24c6c900, 0xc53023, 
+0x2603821, 0x3c010001, 0xac226ef4, 0xc0017a3, 
+0xafb10010, 0x3c040001, 0x24845da4, 0x10000024, 
+0x24051600, 0x3c040001, 0x24845dac, 0x3c050001, 
+0x24a5a10c, 0x3c060001, 0x24c6a238, 0xc53023, 
+0xc0017a3, 0xafb30010, 0x3c040001, 0x24845dbc, 
+0x3c050001, 0x24a5b2b0, 0x3c060001, 0x24c6b70c, 
+0xc53023, 0x2203821, 0xaf420108, 0xc0017a3, 
+0xafb30010, 0x3c040001, 0x24845dd0, 0x3c050001, 
+0x24a5ba98, 0x3c060001, 0x24c6be50, 0xc53023, 
+0x2203821, 0x3c010001, 0xac226ef4, 0xc0017a3, 
+0xafb30010, 0x3c040001, 0x24845de4, 0x24051650, 
+0x2c03021, 0x3821, 0x3c010001, 0xac226ef8, 
+0xafa00010, 0xc002b3b, 0xafa00014, 0x32c20020, 
+0x10400021, 0x27a70030, 0x3c040001, 0x24845df0, 
+0x3c050001, 0x24a5b13c, 0x3c060001, 0x24c6b2a8, 
+0xc53023, 0x24022000, 0xaf42001c, 0x27a20034, 
+0xc0017a3, 0xafa20010, 0x21900, 0x31982, 
+0x3c040800, 0x641825, 0xae430028, 0x24030010, 
+0xaf43003c, 0x96e30450, 0xaf430040, 0x8f430040, 
+0x3c040001, 0x24845e04, 0xafa00014, 0xafa30010, 
+0x8f47001c, 0x24051660, 0x3c010001, 0xac226ef0, 
+0x10000025, 0x32c60020, 0x8ee20448, 0x8ee3044c, 
+0xaf43001c, 0x8f42001c, 0x2442e000, 0x2c422001, 
+0x1440000a, 0x240e0001, 0x3c040001, 0x24845e10, 
+0xa3ae003f, 0xafa00010, 0xafa00014, 0x8f46001c, 
+0x24051700, 0xc002b3b, 0x3821, 0x3c020000, 
+0x24425cbc, 0x21100, 0x21182, 0x3c030800, 
+0x431025, 0xae420028, 0x24020008, 0xaf42003c, 
+0x96e20450, 0xaf420040, 0x8f420040, 0x3c040001, 
+0x24845e1c, 0xafa00014, 0xafa20010, 0x8f47001c, 
+0x24051800, 0x32c60020, 0xc002b3b, 0x0, 
+0x3c050fff, 0x3c030001, 0x8c636ef4, 0x34a5ffff, 
+0x2403021, 0x3c020001, 0x8c426ef8, 0x3c040800, 
+0x651824, 0x31882, 0x641825, 0x451024, 
+0x21082, 0x441025, 0xacc20080, 0x32c20180, 
+0x10400056, 0xacc30020, 0x8f82005c, 0x3c030080, 
+0x431024, 0x1040000d, 0x0, 0x8f820050, 
+0xafa20010, 0x8f82005c, 0x240e0001, 0x3c040001, 
+0x24845e28, 0xa3ae003f, 0xafa20014, 0x8f870040, 
+0x24051900, 0xc002b3b, 0x2c03021, 0x8f820050, 
+0x3c030010, 0x431024, 0x10400016, 0x0, 
+0x8c020218, 0x30420040, 0x1040000f, 0x24020001, 
+0x8f820050, 0x8c030218, 0x240e0001, 0x3c040001, 
+0x24845d50, 0xa3ae003f, 0xafa20010, 0xafa30014, 
+0x8f870040, 0x24052000, 0xc002b3b, 0x2c03021, 
+0x10000004, 0x0, 0x3c010001, 0x370821, 
+0xa02240f4, 0x3c040001, 0x24845e34, 0x3c050001, 
+0x24a55ac0, 0x3c060001, 0x24c65b38, 0xc53023, 
+0x8f420008, 0x27b30030, 0x2603821, 0x27b10034, 
+0x34420e00, 0xaf420008, 0xc0017a3, 0xafb10010, 
+0x3c040001, 0x24845e4c, 0x3c050001, 0x24a5d8b4, 
+0x3c060001, 0x24c6e3c8, 0xc53023, 0x2603821, 
+0xaf42010c, 0xc0017a3, 0xafb10010, 0x3c040001, 
+0x24845e64, 0x3c050001, 0x24a5e9ac, 0x3c060001, 
+0x24c6f0f0, 0xc53023, 0x2603821, 0x3c010001, 
+0xac226f04, 0xc0017a3, 0xafb10010, 0x3c040001, 
+0x24845e7c, 0x10000027, 0x24052100, 0x3c040001, 
+0x24845e84, 0x3c050001, 0x24a59fc8, 0x3c060001, 
+0x24c6a104, 0xc53023, 0x27b10030, 0x2203821, 
+0x27b30034, 0xc0017a3, 0xafb30010, 0x3c040001, 
+0x24845e94, 0x3c050001, 0x24a5cad4, 0x3c060001, 
+0x24c6d8ac, 0xc53023, 0x2203821, 0xaf42010c, 
+0xc0017a3, 0xafb30010, 0x3c040001, 0x24845ea4, 
+0x3c050001, 0x24a5e84c, 0x3c060001, 0x24c6e9a4, 
+0xc53023, 0x2203821, 0x3c010001, 0xac226f04, 
+0xc0017a3, 0xafb30010, 0x3c040001, 0x24845eb8, 
+0x24052150, 0x2c03021, 0x3821, 0x3c010001, 
+0xac226f10, 0xafa00010, 0xc002b3b, 0xafa00014, 
+0x3c110fff, 0x3c030001, 0x8c636f04, 0x3631ffff, 
+0x2409821, 0x3c020001, 0x8c426f10, 0x3c0e0800, 
+0x711824, 0x31882, 0x6e1825, 0x511024, 
+0x21082, 0x4e1025, 0xae630038, 0xae620078, 
+0x8c020218, 0x30420040, 0x14400004, 0x24020001, 
+0x3c010001, 0x370821, 0xa02240f4, 0x3c040001, 
+0x24845ec4, 0x3c050001, 0x24a5e3d0, 0x3c060001, 
+0x24c6e52c, 0xc53023, 0x27be0030, 0x3c03821, 
+0x27b50034, 0xc0017a3, 0xafb50010, 0x3c010001, 
+0xac226efc, 0x511024, 0x21082, 0x3c0e0800, 
+0x4e1025, 0xae620050, 0x32c22000, 0x10400006, 
+0x3c03821, 0x3c020000, 0x24425cbc, 0x2221024, 
+0x1000000f, 0x21082, 0x3c040001, 0x24845ed8, 
+0x3c050001, 0x24a5e534, 0x3c060001, 0x24c6e6e4, 
+0xc53023, 0xc0017a3, 0xafb50010, 0x3c010001, 
+0xac226f14, 0x511024, 0x21082, 0x3c0e0800, 
+0x4e1025, 0xae620048, 0x32c24000, 0x10400005, 
+0x27a70030, 0x3c020000, 0x24425cbc, 0x1000000e, 
+0x21100, 0x3c040001, 0x24845ef0, 0x3c050001, 
+0x24a5e6ec, 0x3c060001, 0x24c6e844, 0xc53023, 
+0x27a20034, 0xc0017a3, 0xafa20010, 0x3c010001, 
+0xac226f08, 0x21100, 0x21182, 0x3c030800, 
+0x431025, 0xae420060, 0x3c040001, 0x24845f08, 
+0x3c050001, 0x24a58230, 0x3c060001, 0x24c68650, 
+0xc53023, 0x27b10030, 0x2203821, 0x27b30034, 
+0xc0017a3, 0xafb30010, 0x3c0e0fff, 0x35ceffff, 
+0x3c040001, 0x24845f14, 0x3c050000, 0x24a56468, 
+0x3c060000, 0x24c66588, 0xc53023, 0x2203821, 
+0x240f021, 0x3c010001, 0xac226edc, 0x4e1024, 
+0x21082, 0x3c150800, 0x551025, 0xafae0044, 
+0xafc200b8, 0xc0017a3, 0xafb30010, 0x3c040001, 
+0x24845f20, 0x3c050000, 0x24a56590, 0x3c060000, 
+0x24c66808, 0x8fae0044, 0xc53023, 0x2203821, 
+0x3c010001, 0xac226ed0, 0x4e1024, 0x21082, 
+0x551025, 0xafc200e8, 0xc0017a3, 0xafb30010, 
+0x3c040001, 0x24845f38, 0x3c050000, 0x24a56810, 
+0x3c060000, 0x24c66940, 0x8fae0044, 0xc53023, 
+0x2203821, 0x3c010001, 0xac226ec8, 0x4e1024, 
+0x21082, 0x551025, 0xafc200c0, 0xc0017a3, 
+0xafb30010, 0x3c040001, 0x24845f50, 0x3c050001, 
+0x24a5fad0, 0x3c060001, 0x24c6fba8, 0x8fae0044, 
+0xc53023, 0x2203821, 0x3c010001, 0xac226ed4, 
+0x4e1024, 0x21082, 0x551025, 0xafc200c8, 
+0xc0017a3, 0xafb30010, 0x3c040001, 0x24845f5c, 
+0x3c050001, 0x24a5c93c, 0x3c060001, 0x24c6ca20, 
+0xc53023, 0x2203821, 0xaf420110, 0xc0017a3, 
+0xafb30010, 0x3c040001, 0x24845f6c, 0x3c050001, 
+0x24a5c910, 0x3c060001, 0x24c6c934, 0xc53023, 
+0x2203821, 0xaf420124, 0xc0017a3, 0xafb30010, 
+0x3c040001, 0x24845f7c, 0x3c050001, 0x24a55a80, 
+0x3c060001, 0x24c65aac, 0xc53023, 0x2203821, 
+0xaf420120, 0xaf420114, 0xc0017a3, 0xafb30010, 
+0x3c040001, 0x24845f88, 0x3c050001, 0x24a5f298, 
+0x3c060001, 0x24c6f6b4, 0xc53023, 0x2203821, 
+0xaf420118, 0xc0017a3, 0xafb30010, 0x8fae0044, 
+0x3c010001, 0xac226f18, 0x4e1024, 0x21082, 
+0x551025, 0xc003fc3, 0xafc200d0, 0xc003c40, 
+0x0, 0xc0027a8, 0x0, 0xac000228, 
+0xac00022c, 0x96e20450, 0x2442ffff, 0xaf420038, 
+0x96e20460, 0xaf420080, 0x32c24000, 0x14400003, 
+0x0, 0x96e20480, 0xaf420084, 0x96e70490, 
+0x50e00001, 0x24070800, 0x24e2ffff, 0xaf420088, 
+0xaf42007c, 0x24020800, 0x10e2000f, 0x32c24000, 
+0x10400003, 0x24020400, 0x10e2000b, 0x0, 
+0x240e0001, 0x3c040001, 0x24845f98, 0xa3ae003f, 
+0x96e60490, 0x24052170, 0x2c03821, 0xafa00010, 
+0xc002b3b, 0xafa00014, 0x8f430138, 0x8f440138, 
+0x24020001, 0xa34205c2, 0xaf430094, 0xaf440098, 
+0xafa00010, 0xafa00014, 0x8f460080, 0x8f470084, 
+0x3c040001, 0x24845fa4, 0xc002b3b, 0x24052200, 
+0xc0024a4, 0x3c110800, 0x3c1433d8, 0x3694cb58, 
+0x3c020800, 0x34420080, 0x3c040001, 0x24845fb0, 
+0x3c050000, 0x24a55d00, 0x3c060000, 0x24c65d1c, 
+0xc53023, 0x27a70030, 0xaf820060, 0x2402ffff, 
+0xaf820064, 0x27a20034, 0xc0017a3, 0xafa20010, 
+0x3c010001, 0xac226eb8, 0x21100, 0x21182, 
+0x511025, 0xc0018fc, 0xae420000, 0x8f820240, 
+0x3c030001, 0x431025, 0xaf820240, 0x3c020000, 
+0x24424034, 0xaf820244, 0xaf800240, 0x8f820060, 
+0x511024, 0x14400005, 0x3c030800, 0x8f820060, 
+0x431024, 0x1040fffd, 0x0, 0xc003c4d, 
+0x8821, 0x3c020100, 0xafa20020, 0x8f530018, 
+0x240200ff, 0x56620001, 0x26710001, 0x8c020228, 
+0x1622000e, 0x1330c0, 0x8f42033c, 0x24420001, 
+0xaf42033c, 0x8f42033c, 0x8c020228, 0x3c040001, 
+0x24845c24, 0x3c050009, 0xafa00014, 0xafa20010, 
+0x8fa60020, 0x1000003f, 0x34a50100, 0xd71021, 
+0x8fa30020, 0x8fa40024, 0xac4304c0, 0xac4404c4, 
+0xc01821, 0x8f440178, 0x8f45017c, 0x1021, 
+0x24070004, 0xafa70010, 0xafb10014, 0x8f48000c, 
+0x24c604c0, 0x2e63021, 0xafa80018, 0x8f48010c, 
+0x24070008, 0xa32821, 0xa3482b, 0x822021, 
+0x100f809, 0x892021, 0x1440000b, 0x24070008, 
+0x8f820120, 0xafa20010, 0x8f820124, 0x3c040001, 
+0x24845c2c, 0x3c050009, 0xafa20014, 0x8fa60020, 
+0x1000001c, 0x34a50200, 0x8f440160, 0x8f450164, 
+0x8f43000c, 0xaf510018, 0x8f860120, 0x24020010, 
+0xafa20010, 0xafb10014, 0xafa30018, 0x8f42010c, 
+0x40f809, 0x24c6001c, 0x14400010, 0x0, 
+0x8f420340, 0x24420001, 0xaf420340, 0x8f420340, 
+0x8f820120, 0xafa20010, 0x8f820124, 0x3c040001, 
+0x24845c34, 0x3c050009, 0xafa20014, 0x8fa60020, 
+0x34a50300, 0xc002b3b, 0x2603821, 0x8f4202e4, 
+0x24420001, 0xaf4202e4, 0x8f4202e4, 0x93a2003f, 
+0x10400069, 0x3c020700, 0x34423000, 0xafa20028, 
+0x8f530018, 0x240200ff, 0x12620002, 0x8821, 
+0x26710001, 0x8c020228, 0x1622000e, 0x1330c0, 
+0x8f42033c, 0x24420001, 0xaf42033c, 0x8f42033c, 
+0x8c020228, 0x3c040001, 0x24845c24, 0x3c050009, 
+0xafa00014, 0xafa20010, 0x8fa60028, 0x1000003f, 
+0x34a50100, 0xd71021, 0x8fa30028, 0x8fa4002c, 
+0xac4304c0, 0xac4404c4, 0xc01821, 0x8f440178, 
+0x8f45017c, 0x1021, 0x24070004, 0xafa70010, 
+0xafb10014, 0x8f48000c, 0x24c604c0, 0x2e63021, 
+0xafa80018, 0x8f48010c, 0x24070008, 0xa32821, 
+0xa3482b, 0x822021, 0x100f809, 0x892021, 
+0x1440000b, 0x24070008, 0x8f820120, 0xafa20010, 
+0x8f820124, 0x3c040001, 0x24845c2c, 0x3c050009, 
+0xafa20014, 0x8fa60028, 0x1000001c, 0x34a50200, 
+0x8f440160, 0x8f450164, 0x8f43000c, 0xaf510018, 
+0x8f860120, 0x24020010, 0xafa20010, 0xafb10014, 
+0xafa30018, 0x8f42010c, 0x40f809, 0x24c6001c, 
+0x14400010, 0x0, 0x8f420340, 0x24420001, 
+0xaf420340, 0x8f420340, 0x8f820120, 0xafa20010, 
+0x8f820124, 0x3c040001, 0x24845c34, 0x3c050009, 
+0xafa20014, 0x8fa60028, 0x34a50300, 0xc002b3b, 
+0x2603821, 0x8f4202f0, 0x24420001, 0xaf4202f0, 
+0x8f4202f0, 0x3c040001, 0x24845fc0, 0xafa00010, 
+0xafa00014, 0x8fa60028, 0x24052300, 0xc002b3b, 
+0x3821, 0x10000004, 0x0, 0x8c020264, 
+0x10400005, 0x0, 0x8f8200a0, 0x30420004, 
+0x1440fffa, 0x0, 0x8f820044, 0x34420004, 
+0xaf820044, 0x8f420308, 0x24420001, 0xaf420308, 
+0x8f420308, 0x8f8200d8, 0x8f8300d4, 0x431023, 
+0x2442ff80, 0xaf420090, 0x8f420090, 0x2842ff81, 
+0x10400006, 0x24020001, 0x8f420090, 0x8f430144, 
+0x431021, 0xaf420090, 0x24020001, 0xaf42008c, 
+0x32c20008, 0x10400006, 0x0, 0x8f820214, 
+0x3c038100, 0x3042ffff, 0x431025, 0xaf820214, 
+0x3c030001, 0x8c636d94, 0x30620002, 0x10400009, 
+0x30620001, 0x3c040001, 0x24845fcc, 0x3c050000, 
+0x24a56d50, 0x3c060000, 0x24c671c8, 0x10000012, 
+0xc53023, 0x10400009, 0x0, 0x3c040001, 
+0x24845fdc, 0x3c050000, 0x24a571d0, 0x3c060000, 
+0x24c67678, 0x10000008, 0xc53023, 0x3c040001, 
+0x24845fec, 0x3c050000, 0x24a56948, 0x3c060000, 
+0x24c66d48, 0xc53023, 0x27a70030, 0x27a20034, 
+0xc0017a3, 0xafa20010, 0x3c010001, 0xac226ecc, 
+0x3c020001, 0x8c426ecc, 0x3c030800, 0x21100, 
+0x21182, 0x431025, 0xae420040, 0x8f8200a0, 
+0xafa20010, 0x8f8200b0, 0xafa20014, 0x8f86005c, 
+0x8f87011c, 0x3c040001, 0x24845ffc, 0x3c010001, 
+0xac366ea4, 0x3c010001, 0xac206e94, 0x3c010001, 
+0xac3c6e8c, 0x3c010001, 0xac3b6ebc, 0x3c010001, 
+0xac376ec0, 0x3c010001, 0xac3a6ea0, 0xc002b3b, 
+0x24052400, 0x8f820200, 0xafa20010, 0x8f820220, 
+0xafa20014, 0x8f860044, 0x8f870050, 0x3c040001, 
+0x24846008, 0xc002b3b, 0x24052500, 0x8f830060, 
+0x74100b, 0x242000a, 0x200f821, 0x0, 
+0xd, 0x8fbf0060, 0x8fbe005c, 0x8fb50058, 
+0x8fb30054, 0x8fb20050, 0x8fb1004c, 0x8fb00048, 
+0x3e00008, 0x27bd0068, 0x27bdffe0, 0x3c040001, 
+0x24846014, 0x24052600, 0x3021, 0x3821, 
+0xafbf0018, 0xafa00010, 0xc002b3b, 0xafa00014, 
+0x8fbf0018, 0x3e00008, 0x27bd0020, 0x3e00008, 
+0x0, 0x3e00008, 0x0, 0x0, 
+0x0, 0x0, 0x0, 0x0, 
+0x3e00008, 0x0, 0x3e00008, 0x0, 
+0x27bdfde0, 0x27a50018, 0x3c04dead, 0x3484beef, 
+0xafbf0218, 0x8f820150, 0x3c03001f, 0x3463ffff, 
+0xafa40018, 0xa22823, 0xa32824, 0x8ca20000, 
+0x1044000a, 0x0, 0xafa50010, 0x8ca20000, 
+0xafa20014, 0x8f860150, 0x8f870250, 0x3c040001, 
+0x2484601c, 0xc002b3b, 0x24052700, 0x8fbf0218, 
+0x3e00008, 0x27bd0220, 0x27bdffe0, 0x3c06abba, 
+0x34c6babe, 0xafb00018, 0x3c100004, 0x3c07007f, 
+0x34e7ffff, 0xafbf001c, 0x102840, 0x8e040000, 
+0x8ca30000, 0xaca00000, 0xae060000, 0x8ca20000, 
+0xaca30000, 0x10460005, 0xae040000, 0xa08021, 
+0xf0102b, 0x1040fff5, 0x102840, 0x3c040001, 
+0x24846028, 0x24052800, 0x2003021, 0x3821, 
+0xafa00010, 0xc002b3b, 0xafa00014, 0x2001021, 
+0x8fbf001c, 0x8fb00018, 0x3e00008, 0x27bd0020, 
+0x8c020224, 0x3047003f, 0x10e00010, 0x803021, 
+0x2821, 0x24030020, 0xe31024, 0x10400002, 
+0x63042, 0xa62821, 0x31842, 0x1460fffb, 
+0xe31024, 0x2402f000, 0xa22824, 0x3402ffff, 
+0x45102b, 0x14400003, 0x3c020001, 0x10000008, 
+0x3c020001, 0x3442ffff, 0x851823, 0x43102b, 
+0x14400003, 0xa01021, 0x3c02fffe, 0x821021, 
+0x3e00008, 0x0, 0x27bdffd0, 0xafb50028, 
+0x8fb50040, 0xafb20020, 0xa09021, 0xafb1001c, 
+0x24c60003, 0xafbf002c, 0xafb30024, 0xafb00018, 
+0x8ea20000, 0x2403fffc, 0xc38024, 0x50102b, 
+0x1440001b, 0xe08821, 0x8e330000, 0xafb00010, 
+0x8ea20000, 0xafa20014, 0x8e270000, 0x24053000, 
+0xc002b3b, 0x2403021, 0x8e230000, 0x702021, 
+0x64102b, 0x10400007, 0x2402821, 0x8ca20000, 
+0xac620000, 0x24630004, 0x64102b, 0x1440fffb, 
+0x24a50004, 0x8ea20000, 0x501023, 0xaea20000, 
+0x8e220000, 0x501021, 0x1000000b, 0xae220000, 
+0x2402002d, 0xa0820000, 0xafb00010, 0x8ea20000, 
+0x2409821, 0xafa20014, 0x8e270000, 0x24053100, 
+0xc002b3b, 0x2603021, 0x2601021, 0x8fbf002c, 
+0x8fb50028, 0x8fb30024, 0x8fb20020, 0x8fb1001c, 
+0x8fb00018, 0x3e00008, 0x27bd0030, 0x27bdffe8, 
+0x3c1cc000, 0x3c05fffe, 0x3c030001, 0x8c636e84, 
+0x3c040001, 0x8c846e90, 0x34a5bf08, 0x24021ffc, 
+0x3c010001, 0xac226cd0, 0x3c0200c0, 0x3c010001, 
+0xac226cd4, 0x3c020020, 0xafbf0010, 0x3c0100c0, 
+0xac201ffc, 0x431023, 0x441023, 0x245bb000, 
+0x365b821, 0x3c1d0001, 0x8fbd6ccc, 0x3a0f021, 
+0x3c0400c0, 0x34840200, 0x3c1a00c0, 0x3c0300c0, 
+0x346307c8, 0x24021dfc, 0x3c010001, 0xac226cd0, 
+0x24021834, 0x3c010001, 0xac246cd4, 0x3c010001, 
+0xac226cd0, 0x3c010001, 0xac236cd4, 0xc00180d, 
+0x375a0200, 0x8fbf0010, 0x3e00008, 0x27bd0018, 
+0x27bdffc8, 0x3c040001, 0x24846034, 0x24053200, 
+0x3c020001, 0x8c426cd0, 0x3c030001, 0x8c636cd4, 
+0x3021, 0x3603821, 0xafbf0030, 0xafb3002c, 
+0xafb20028, 0xafb10024, 0xafb00020, 0xafa2001c, 
+0xafa30018, 0xafb70010, 0xc002b3b, 0xafba0014, 
+0xc001916, 0x0, 0x8f820240, 0x34420004, 
+0xaf820240, 0x24020001, 0xaf420000, 0x3c020001, 
+0x571021, 0x904240f4, 0x10400092, 0x2403fffc, 
+0x3c100001, 0x2610ac73, 0x3c120001, 0x2652a84c, 
+0x2121023, 0x438024, 0x8fa3001c, 0x3c040001, 
+0x24846040, 0x70102b, 0x1440001a, 0x27b30018, 
+0x8fb10018, 0x24053000, 0x2403021, 0xafb00010, 
+0xafa30014, 0xc002b3b, 0x2203821, 0x8fa30018, 
+0x702021, 0x64102b, 0x10400007, 0x2403021, 
+0x8cc20000, 0xac620000, 0x24630004, 0x64102b, 
+0x1440fffb, 0x24c60004, 0x8fa2001c, 0x501023, 
+0xafa2001c, 0x8e620000, 0x501021, 0x1000000a, 
+0xae620000, 0x2408821, 0x24053100, 0xafb00010, 
+0xafa30014, 0x8fa70018, 0x2203021, 0x2402002d, 
+0xc002b3b, 0xa0820000, 0x24070020, 0x8fa3001c, 
+0x3c040001, 0x2484605c, 0x24120020, 0x3c010001, 
+0xac316eb0, 0x2c620020, 0x1440001d, 0x27b10018, 
+0x8fb00018, 0x24053000, 0x3c060001, 0x24c66f50, 
+0xafa70010, 0xafa30014, 0xc002b3b, 0x2003821, 
+0x8fa30018, 0x3c040001, 0x24846f50, 0x24650020, 
+0x65102b, 0x10400007, 0x0, 0x8c820000, 
+0xac620000, 0x24630004, 0x65102b, 0x1440fffb, 
+0x24840004, 0x8fa2001c, 0x521023, 0xafa2001c, 
+0x8e220000, 0x521021, 0x1000000b, 0xae220000, 
+0x3c100001, 0x26106f50, 0x24053100, 0xafa70010, 
+0xafa30014, 0x8fa70018, 0x2003021, 0x2402002d, 
+0xc002b3b, 0xa0820000, 0x24070020, 0x3c040001, 
+0x24846070, 0x8fa3001c, 0x24120020, 0x3c010001, 
+0xac306ee4, 0x2c620020, 0x1440001d, 0x27b10018, 
+0x8fb00018, 0x24053000, 0x3c060001, 0x24c66f70, 
+0xafa70010, 0xafa30014, 0xc002b3b, 0x2003821, 
+0x8fa30018, 0x3c040001, 0x24846f70, 0x24650020, 
+0x65102b, 0x10400007, 0x0, 0x8c820000, 
+0xac620000, 0x24630004, 0x65102b, 0x1440fffb, 
+0x24840004, 0x8fa2001c, 0x521023, 0xafa2001c, 
+0x8e220000, 0x521021, 0x1000000b, 0xae220000, 
+0x3c100001, 0x26106f70, 0x24053100, 0xafa70010, 
+0xafa30014, 0x8fa70018, 0x2003021, 0x2402002d, 
+0xc002b3b, 0xa0820000, 0x3c010001, 0x10000031, 
+0xac306ee0, 0x3c100001, 0x2610821f, 0x3c120001, 
+0x2652809c, 0x2121023, 0x438024, 0x8fa3001c, 
+0x3c040001, 0x24846084, 0x70102b, 0x1440001a, 
+0x27b30018, 0x8fb10018, 0x24053000, 0x2403021, 
+0xafb00010, 0xafa30014, 0xc002b3b, 0x2203821, 
+0x8fa30018, 0x702021, 0x64102b, 0x10400007, 
+0x2403021, 0x8cc20000, 0xac620000, 0x24630004, 
+0x64102b, 0x1440fffb, 0x24c60004, 0x8fa2001c, 
+0x501023, 0xafa2001c, 0x8e620000, 0x501021, 
+0x1000000a, 0xae620000, 0x2408821, 0x24053100, 
+0xafb00010, 0xafa30014, 0x8fa70018, 0x2203021, 
+0x2402002d, 0xc002b3b, 0xa0820000, 0x3c010001, 
+0xac316eb0, 0x3c030001, 0x8c636eb0, 0x24020400, 
+0x60f809, 0xaf820070, 0x8fbf0030, 0x8fb3002c, 
+0x8fb20028, 0x8fb10024, 0x8fb00020, 0x3e00008, 
+0x27bd0038, 0x0, 0x0, 0x8f820040, 
+0x3c03f000, 0x431024, 0x3c036000, 0x14430006, 
+0x0, 0x8f820050, 0x2403ff80, 0x431024, 
+0x34420055, 0xaf820050, 0x8f820054, 0x244203e8, 
+0xaf820058, 0x240201f4, 0xaf4200e0, 0x24020004, 
+0xaf4200e8, 0x24020002, 0xaf4001b0, 0xaf4000e4, 
+0xaf4200dc, 0xaf4000d8, 0xaf4000d4, 0x3e00008, 
+0xaf4000d0, 0x8f820054, 0x24420005, 0x3e00008, 
+0xaf820078, 0x27bdffe8, 0xafbf0010, 0x8f820054, 
+0x244203e8, 0xaf820058, 0x3c020800, 0x2c21024, 
+0x10400004, 0x3c02f7ff, 0x3442ffff, 0x2c2b024, 
+0x36940040, 0x3c020001, 0x8c426da8, 0x10400017, 
+0x3c020200, 0x3c030001, 0x8c636f1c, 0x10600016, 
+0x282a025, 0x3c020001, 0x8c426e44, 0x14400012, 
+0x3c020200, 0x3c020001, 0x8c426d94, 0x30420003, 
+0x1440000d, 0x3c020200, 0x8f830224, 0x3c020002, 
+0x8c428fec, 0x10620008, 0x3c020200, 0xc003daf, 
+0x0, 0x10000004, 0x3c020200, 0xc004196, 
+0x0, 0x3c020200, 0x2c21024, 0x10400003, 
+0x0, 0xc001f4b, 0x0, 0x8f4200d8, 
+0x8f4300dc, 0x24420001, 0xaf4200d8, 0x43102b, 
+0x14400003, 0x0, 0xaf4000d8, 0x36940080, 
+0x8c030238, 0x1060000c, 0x0, 0x8f4201b0, 
+0x244203e8, 0xaf4201b0, 0x43102b, 0x14400006, 
+0x0, 0x934205c5, 0x14400003, 0x0, 
+0xc001da0, 0x0, 0x8fbf0010, 0x3e00008, 
+0x27bd0018, 0x3e00008, 0x0, 0x27bdffd8, 
+0xafbf0020, 0x8f43002c, 0x8f420038, 0x10620059, 
+0x0, 0x3c020001, 0x571021, 0x904240f0, 
+0x10400026, 0x24070008, 0x8f440170, 0x8f450174, 
+0x8f48000c, 0x8f860120, 0x24020020, 0xafa20010, 
+0xafa30014, 0xafa80018, 0x8f42010c, 0x40f809, 
+0x24c6001c, 0x14400011, 0x24020001, 0x3c010001, 
+0x370821, 0xa02240f0, 0x8f820124, 0xafa20010, 
+0x8f820128, 0x3c040001, 0x24846128, 0xafa20014, 
+0x8f46002c, 0x8f870120, 0x3c050009, 0xc002b3b, 
+0x34a50900, 0x1000005c, 0x0, 0x8f420300, 
+0x24420001, 0xaf420300, 0x8f420300, 0x8f42002c, 
+0xa34005c1, 0x10000027, 0xaf420038, 0x8f440170, 
+0x8f450174, 0x8f43002c, 0x8f48000c, 0x8f860120, 
+0x24020080, 0xafa20010, 0xafa30014, 0xafa80018, 
+0x8f42010c, 0x40f809, 0x24c6001c, 0x14400011, 
+0x24020001, 0x3c010001, 0x370821, 0xa02240f1, 
+0x8f820124, 0xafa20010, 0x8f820128, 0x3c040001, 
+0x24846134, 0xafa20014, 0x8f46002c, 0x8f870120, 
+0x3c050009, 0xc002b3b, 0x34a51100, 0x10000036, 
+0x0, 0x8f420300, 0x8f43002c, 0x24420001, 
+0xaf420300, 0x8f420300, 0x24020001, 0xa34205c1, 
+0xaf430038, 0x3c010001, 0x370821, 0xa02040f1, 
+0x3c010001, 0x370821, 0xa02040f0, 0x10000026, 
+0xaf400034, 0x934205c1, 0x1040001d, 0x0, 
+0xa34005c1, 0x8f820040, 0x30420001, 0x14400008, 
+0x2021, 0x8c030104, 0x24020001, 0x50620005, 
+0x24040001, 0x8c020264, 0x10400003, 0x801021, 
+0x24040001, 0x801021, 0x10400006, 0x0, 
+0x8f42030c, 0x24420001, 0xaf42030c, 0x10000008, 
+0x8f42030c, 0x8f820044, 0x34420004, 0xaf820044, 
+0x8f420308, 0x24420001, 0xaf420308, 0x8f420308, 
+0x3c010001, 0x370821, 0xa02040f0, 0x3c010001, 
+0x370821, 0xa02040f1, 0x8f420000, 0x10400007, 
+0x0, 0xaf80004c, 0x8f82004c, 0x1040fffd, 
+0x0, 0x10000005, 0x0, 0xaf800048, 
+0x8f820048, 0x1040fffd, 0x0, 0x8f820060, 
+0x3c03ff7f, 0x3463ffff, 0x431024, 0xaf820060, 
+0x8f420000, 0x10400003, 0x0, 0x10000002, 
+0xaf80004c, 0xaf800048, 0x8fbf0020, 0x3e00008, 
+0x27bd0028, 0x3e00008, 0x0, 0x27bdffd8, 
+0xafbf0020, 0x8f430044, 0x8f42007c, 0x10620029, 
+0x24070008, 0x8f440168, 0x8f45016c, 0x8f48000c, 
+0x8f860120, 0x24020040, 0xafa20010, 0xafa30014, 
+0xafa80018, 0x8f42010c, 0x40f809, 0x24c6001c, 
+0x14400011, 0x24020001, 0x3c010001, 0x370821, 
+0xa02240f2, 0x8f820124, 0xafa20010, 0x8f820128, 
+0x3c040001, 0x2484613c, 0xafa20014, 0x8f460044, 
+0x8f870120, 0x3c050009, 0xc002b3b, 0x34a51300, 
+0x1000000f, 0x0, 0x8f420304, 0x24420001, 
+0xaf420304, 0x8f420304, 0x8f420044, 0xaf42007c, 
+0x3c010001, 0x370821, 0xa02040f2, 0x10000004, 
+0xaf400078, 0x3c010001, 0x370821, 0xa02040f2, 
+0x8f420000, 0x10400007, 0x0, 0xaf80004c, 
+0x8f82004c, 0x1040fffd, 0x0, 0x10000005, 
+0x0, 0xaf800048, 0x8f820048, 0x1040fffd, 
+0x0, 0x8f820060, 0x3c03feff, 0x3463ffff, 
+0x431024, 0xaf820060, 0x8f420000, 0x10400003, 
+0x0, 0x10000002, 0xaf80004c, 0xaf800048, 
+0x8fbf0020, 0x3e00008, 0x27bd0028, 0x3e00008, 
+0x0, 0x3c020001, 0x8c426da8, 0x27bdffa8, 
+0xafbf0050, 0xafbe004c, 0xafb50048, 0xafb30044, 
+0xafb20040, 0xafb1003c, 0xafb00038, 0x104000d5, 
+0x8f900044, 0x8f4200d0, 0x24430001, 0x2842000b, 
+0x144000e4, 0xaf4300d0, 0x8f420004, 0x30420002, 
+0x1440009c, 0xaf4000d0, 0x8f420004, 0x3c030001, 
+0x8c636d98, 0x34420002, 0xaf420004, 0x24020001, 
+0x14620003, 0x3c020600, 0x10000002, 0x34423000, 
+0x34421000, 0xafa20020, 0x8f4a0018, 0xafaa0034, 
+0x27aa0020, 0xafaa002c, 0x8faa0034, 0x240200ff, 
+0x11420002, 0x1821, 0x25430001, 0x8c020228, 
+0x609821, 0x1662000e, 0x3c050009, 0x8f42033c, 
+0x24420001, 0xaf42033c, 0x8f42033c, 0x8c020228, 
+0x8fa70034, 0x3c040001, 0x2484610c, 0xafa00014, 
+0xafa20010, 0x8fa60020, 0x10000070, 0x34a50500, 
+0x8faa0034, 0xa38c0, 0xf71021, 0x8fa30020, 
+0x8fa40024, 0xac4304c0, 0xac4404c4, 0x8f830054, 
+0x8f820054, 0x247103e8, 0x2221023, 0x2c4203e9, 
+0x1040001b, 0xa821, 0xe09021, 0x265e04c0, 
+0x8f440178, 0x8f45017c, 0x2401821, 0x240a0004, 
+0xafaa0010, 0xafb30014, 0x8f48000c, 0x1021, 
+0x2fe3021, 0xafa80018, 0x8f48010c, 0x24070008, 
+0xa32821, 0xa3482b, 0x822021, 0x100f809, 
+0x892021, 0x54400006, 0x24150001, 0x8f820054, 
+0x2221023, 0x2c4203e9, 0x1440ffe9, 0x0, 
+0x32a200ff, 0x54400018, 0xaf530018, 0x8f420378, 
+0x24420001, 0xaf420378, 0x8f420378, 0x8f820120, 
+0x8faa002c, 0x8fa70034, 0xafa20010, 0x8f820124, 
+0x3c040001, 0x24846118, 0xafa20014, 0x8d460000, 
+0x3c050009, 0x10000035, 0x34a50600, 0x8f420308, 
+0x24150001, 0x24420001, 0xaf420308, 0x8f420308, 
+0x1000001e, 0x32a200ff, 0x8f830054, 0x8f820054, 
+0x247103e8, 0x2221023, 0x2c4203e9, 0x10400016, 
+0xa821, 0x3c1e0020, 0x24120010, 0x8f42000c, 
+0x8f440160, 0x8f450164, 0x8f860120, 0xafb20010, 
+0xafb30014, 0x5e1025, 0xafa20018, 0x8f42010c, 
+0x24070008, 0x40f809, 0x24c6001c, 0x1440ffe3, 
+0x0, 0x8f820054, 0x2221023, 0x2c4203e9, 
+0x1440ffee, 0x0, 0x32a200ff, 0x14400011, 
+0x3c050009, 0x8f420378, 0x24420001, 0xaf420378, 
+0x8f420378, 0x8f820120, 0x8faa002c, 0x8fa70034, 
+0xafa20010, 0x8f820124, 0x3c040001, 0x24846120, 
+0xafa20014, 0x8d460000, 0x34a50700, 0xc002b3b, 
+0x0, 0x8f4202ec, 0x24420001, 0xaf4202ec, 
+0x8f4202ec, 0x8f420004, 0x30420001, 0x50400029, 
+0x36100040, 0x3c020400, 0x2c21024, 0x10400013, 
+0x2404ffdf, 0x8f420250, 0x8f430254, 0x8f4401b4, 
+0x14640006, 0x36100040, 0x8f420270, 0x8f430274, 
+0x8f4401b8, 0x10640007, 0x2402ffdf, 0x8f420250, 
+0x8f430254, 0x8f440270, 0x8f450274, 0x10000012, 
+0x3a100020, 0x1000002b, 0x2028024, 0x8f420250, 
+0x8f430254, 0x8f4501b4, 0x14650006, 0x2048024, 
+0x8f420270, 0x8f430274, 0x8f4401b8, 0x50640021, 
+0x36100040, 0x8f420250, 0x8f430254, 0x8f440270, 
+0x8f450274, 0x3a100040, 0xaf4301b4, 0x10000019, 
+0xaf4501b8, 0x8f4200d4, 0x24430001, 0x10000011, 
+0x28420033, 0x8f420004, 0x30420001, 0x10400009, 
+0x3c020400, 0x2c21024, 0x10400004, 0x2402ffdf, 
+0x2028024, 0x1000000b, 0x36100040, 0x10000009, 
+0x36100060, 0x8f4200d4, 0x36100040, 0x24430001, 
+0x284201f5, 0x14400003, 0xaf4300d4, 0xaf4000d4, 
+0x3a100020, 0xaf900044, 0x2402ff7f, 0x282a024, 
+0x8fbf0050, 0x8fbe004c, 0x8fb50048, 0x8fb30044, 
+0x8fb20040, 0x8fb1003c, 0x8fb00038, 0x3e00008, 
+0x27bd0058, 0x3e00008, 0x0, 0x3c020001, 
+0x8c426da8, 0x27bdffb0, 0xafbf0048, 0xafbe0044, 
+0xafb50040, 0xafb3003c, 0xafb20038, 0xafb10034, 
+0x104000c7, 0xafb00030, 0x8f4200d0, 0x24430001, 
+0x2842000b, 0x144000da, 0xaf4300d0, 0x8f420004, 
+0x30420002, 0x14400097, 0xaf4000d0, 0x8f420004, 
+0x3c030001, 0x8c636d98, 0x34420002, 0xaf420004, 
+0x24020001, 0x14620003, 0x3c020600, 0x10000002, 
+0x34423000, 0x34421000, 0xafa20020, 0x1821, 
+0x8f5e0018, 0x27aa0020, 0x240200ff, 0x13c20002, 
+0xafaa002c, 0x27c30001, 0x8c020228, 0x609021, 
+0x1642000e, 0x1e38c0, 0x8f42033c, 0x24420001, 
+0xaf42033c, 0x8f42033c, 0x8c020228, 0x3c040001, 
+0x2484610c, 0x3c050009, 0xafa00014, 0xafa20010, 
+0x8fa60020, 0x1000006d, 0x34a50500, 0xf71021, 
+0x8fa30020, 0x8fa40024, 0xac4304c0, 0xac4404c4, 
+0x8f830054, 0x8f820054, 0x247003e8, 0x2021023, 
+0x2c4203e9, 0x1040001b, 0x9821, 0xe08821, 
+0x263504c0, 0x8f440178, 0x8f45017c, 0x2201821, 
+0x240a0004, 0xafaa0010, 0xafb20014, 0x8f48000c, 
+0x1021, 0x2f53021, 0xafa80018, 0x8f48010c, 
+0x24070008, 0xa32821, 0xa3482b, 0x822021, 
+0x100f809, 0x892021, 0x54400006, 0x24130001, 
+0x8f820054, 0x2021023, 0x2c4203e9, 0x1440ffe9, 
+0x0, 0x326200ff, 0x54400017, 0xaf520018, 
+0x8f420378, 0x24420001, 0xaf420378, 0x8f420378, 
+0x8f820120, 0x8faa002c, 0xafa20010, 0x8f820124, 
+0x3c040001, 0x24846118, 0x3c050009, 0xafa20014, 
+0x8d460000, 0x10000035, 0x34a50600, 0x8f420308, 
+0x24130001, 0x24420001, 0xaf420308, 0x8f420308, 
+0x1000001e, 0x326200ff, 0x8f830054, 0x8f820054, 
+0x247003e8, 0x2021023, 0x2c4203e9, 0x10400016, 
+0x9821, 0x3c150020, 0x24110010, 0x8f42000c, 
+0x8f440160, 0x8f450164, 0x8f860120, 0xafb10010, 
+0xafb20014, 0x551025, 0xafa20018, 0x8f42010c, 
+0x24070008, 0x40f809, 0x24c6001c, 0x1440ffe3, 
+0x0, 0x8f820054, 0x2021023, 0x2c4203e9, 
+0x1440ffee, 0x0, 0x326200ff, 0x14400011, 
+0x0, 0x8f420378, 0x24420001, 0xaf420378, 
+0x8f420378, 0x8f820120, 0x8faa002c, 0xafa20010, 
+0x8f820124, 0x3c040001, 0x24846120, 0x3c050009, 
+0xafa20014, 0x8d460000, 0x34a50700, 0xc002b3b, 
+0x3c03821, 0x8f4202ec, 0x24420001, 0xaf4202ec, 
+0x8f4202ec, 0x8f420004, 0x30420001, 0x10400018, 
+0x24040001, 0x8f420250, 0x8f430254, 0x8f4501b4, 
+0x3c010001, 0x14650006, 0xa0246cf1, 0x8f420270, 
+0x8f430274, 0x8f4401b8, 0x10640021, 0x0, 
+0x8f420250, 0x8f430254, 0x3c040001, 0x90846cf0, 
+0x8f460270, 0x8f470274, 0x38840001, 0xaf4301b4, 
+0xaf4701b8, 0x3c010001, 0x10000025, 0xa0246cf0, 
+0x8f4200d4, 0x3c010001, 0xa0206cf0, 0x24430001, 
+0x28420033, 0x1440001e, 0xaf4300d4, 0x3c020001, 
+0x90426cf1, 0xaf4000d4, 0x10000017, 0x38420001, 
+0x8f420004, 0x30420001, 0x10400008, 0x0, 
+0xc00565a, 0x2021, 0x3c010001, 0xa0206cf1, 
+0x3c010001, 0x1000000e, 0xa0206cf0, 0x8f4200d4, 
+0x3c010001, 0xa0206cf0, 0x24430001, 0x284201f5, 
+0x14400007, 0xaf4300d4, 0x3c020001, 0x90426cf1, 
+0xaf4000d4, 0x421026, 0x3c010001, 0xa0226cf1, 
+0x3c030001, 0x8c636d98, 0x24020002, 0x1462000c, 
+0x3c030002, 0x3c030001, 0x90636cf1, 0x24020001, 
+0x5462001f, 0x2021, 0x3c020001, 0x90426cf0, 
+0x1443001b, 0x24040005, 0x10000019, 0x24040006, 
+0x3c020002, 0x8c428ff4, 0x431024, 0x1040000b, 
+0x24020001, 0x3c030001, 0x90636cf1, 0x54620010, 
+0x2021, 0x3c020001, 0x90426cf0, 0x1443000c, 
+0x24040003, 0x1000000a, 0x24040004, 0x3c030001, 
+0x90636cf1, 0x14620006, 0x2021, 0x3c020001, 
+0x90426cf0, 0x24040001, 0x50440001, 0x24040002, 
+0xc00565a, 0x0, 0x2402ff7f, 0x282a024, 
+0x8fbf0048, 0x8fbe0044, 0x8fb50040, 0x8fb3003c, 
+0x8fb20038, 0x8fb10034, 0x8fb00030, 0x3e00008, 
+0x27bd0050, 0x3e00008, 0x0, 0x3c020001, 
+0x8c426da8, 0x27bdffb0, 0xafbf0048, 0xafbe0044, 
+0xafb50040, 0xafb3003c, 0xafb20038, 0xafb10034, 
+0x104000de, 0xafb00030, 0x8f4200d0, 0x3c040001, 
+0x8c846d98, 0x24430001, 0x2842000b, 0xaf4400e8, 
+0x144000fe, 0xaf4300d0, 0x8f420004, 0x30420002, 
+0x14400095, 0xaf4000d0, 0x8f420004, 0x34420002, 
+0xaf420004, 0x24020001, 0x14820003, 0x3c020600, 
+0x10000002, 0x34423000, 0x34421000, 0xafa20020, 
+0x1821, 0x8f5e0018, 0x27aa0020, 0x240200ff, 
+0x13c20002, 0xafaa002c, 0x27c30001, 0x8c020228, 
+0x609021, 0x1642000e, 0x1e38c0, 0x8f42033c, 
+0x24420001, 0xaf42033c, 0x8f42033c, 0x8c020228, 
+0x3c040001, 0x2484610c, 0x3c050009, 0xafa00014, 
+0xafa20010, 0x8fa60020, 0x1000006d, 0x34a50500, 
+0xf71021, 0x8fa30020, 0x8fa40024, 0xac4304c0, 
+0xac4404c4, 0x8f830054, 0x8f820054, 0x247003e8, 
+0x2021023, 0x2c4203e9, 0x1040001b, 0x9821, 
+0xe08821, 0x263504c0, 0x8f440178, 0x8f45017c, 
+0x2201821, 0x240a0004, 0xafaa0010, 0xafb20014, 
+0x8f48000c, 0x1021, 0x2f53021, 0xafa80018, 
+0x8f48010c, 0x24070008, 0xa32821, 0xa3482b, 
+0x822021, 0x100f809, 0x892021, 0x54400006, 
+0x24130001, 0x8f820054, 0x2021023, 0x2c4203e9, 
+0x1440ffe9, 0x0, 0x326200ff, 0x54400017, 
+0xaf520018, 0x8f420378, 0x24420001, 0xaf420378, 
+0x8f420378, 0x8f820120, 0x8faa002c, 0xafa20010, 
+0x8f820124, 0x3c040001, 0x24846118, 0x3c050009, 
+0xafa20014, 0x8d460000, 0x10000035, 0x34a50600, 
+0x8f420308, 0x24130001, 0x24420001, 0xaf420308, 
+0x8f420308, 0x1000001e, 0x326200ff, 0x8f830054, 
+0x8f820054, 0x247003e8, 0x2021023, 0x2c4203e9, 
+0x10400016, 0x9821, 0x3c150020, 0x24110010, 
+0x8f42000c, 0x8f440160, 0x8f450164, 0x8f860120, 
+0xafb10010, 0xafb20014, 0x551025, 0xafa20018, 
+0x8f42010c, 0x24070008, 0x40f809, 0x24c6001c, 
+0x1440ffe3, 0x0, 0x8f820054, 0x2021023, 
+0x2c4203e9, 0x1440ffee, 0x0, 0x326200ff, 
+0x14400011, 0x0, 0x8f420378, 0x24420001, 
+0xaf420378, 0x8f420378, 0x8f820120, 0x8faa002c, 
+0xafa20010, 0x8f820124, 0x3c040001, 0x24846120, 
+0x3c050009, 0xafa20014, 0x8d460000, 0x34a50700, 
+0xc002b3b, 0x3c03821, 0x8f4202ec, 0x24420001, 
+0xaf4202ec, 0x8f4202ec, 0x8f420004, 0x30420001, 
+0x10400033, 0x3c020400, 0x2c21024, 0x10400017, 
+0x0, 0x934205c0, 0x8f440250, 0x8f450254, 
+0x8f4301b4, 0x34420020, 0x14a30006, 0xa34205c0, 
+0x8f420270, 0x8f430274, 0x8f4401b8, 0x10640008, 
+0x0, 0x8f420250, 0x8f430254, 0x934405c0, 
+0x8f460270, 0x8f470274, 0x10000016, 0x38840040, 
+0x934205c0, 0x10000048, 0x304200bf, 0x934205c0, 
+0x8f440250, 0x8f450254, 0x8f4301b4, 0x304200bf, 
+0x14a30006, 0xa34205c0, 0x8f420270, 0x8f430274, 
+0x8f4401b8, 0x1064000b, 0x0, 0x8f420250, 
+0x8f430254, 0x934405c0, 0x8f460270, 0x8f470274, 
+0x38840020, 0xaf4301b4, 0xaf4701b8, 0x10000033, 
+0xa34405c0, 0x934205c0, 0x1000002f, 0x34420020, 
+0x934205c0, 0x8f4300d4, 0x34420020, 0xa34205c0, 
+0x24620001, 0x10000023, 0x28630033, 0x8f4200e4, 
+0x8f4300e0, 0x24420001, 0xaf4200e4, 0x43102a, 
+0x14400006, 0x24030001, 0x8f4200e8, 0x14430002, 
+0xaf4000e4, 0x24030004, 0xaf4300e8, 0x8f420004, 
+0x30420001, 0x1040000d, 0x3c020400, 0x2c21024, 
+0x10400007, 0x0, 0x934205c0, 0x34420040, 
+0xa34205c0, 0x934205c0, 0x1000000f, 0x304200df, 
+0x934205c0, 0x1000000c, 0x34420060, 0x934205c0, 
+0x8f4300d4, 0x34420020, 0xa34205c0, 0x24620001, 
+0x286300fb, 0x14600005, 0xaf4200d4, 0x934205c0, 
+0xaf4000d4, 0x38420040, 0xa34205c0, 0x934205c0, 
+0x8f4300e8, 0x3042007f, 0xa34205c0, 0x24020001, 
+0x14620005, 0x0, 0x934405c0, 0x42102, 
+0x10000003, 0x348400f0, 0x934405c0, 0x3484000f, 
+0xc005640, 0x0, 0x2402ff7f, 0x282a024, 
+0x8fbf0048, 0x8fbe0044, 0x8fb50040, 0x8fb3003c, 
+0x8fb20038, 0x8fb10034, 0x8fb00030, 0x3e00008, 
+0x27bd0050, 0x3e00008, 0x0, 0x27bdffb0, 
+0x274401c0, 0x26e30028, 0x24650400, 0x65102b, 
+0xafbf0048, 0xafbe0044, 0xafb50040, 0xafb3003c, 
+0xafb20038, 0xafb10034, 0x10400007, 0xafb00030, 
+0x8c820000, 0xac620000, 0x24630004, 0x65102b, 
+0x1440fffb, 0x24840004, 0x8c020080, 0xaee20044, 
+0x8c0200c0, 0xaee20040, 0x8c020084, 0xaee20030, 
+0x8c020084, 0xaee2023c, 0x8c020088, 0xaee20240, 
+0x8c02008c, 0xaee20244, 0x8c020090, 0xaee20248, 
+0x8c020094, 0xaee2024c, 0x8c020098, 0xaee20250, 
+0x8c02009c, 0xaee20254, 0x8c0200a0, 0xaee20258, 
+0x8c0200a4, 0xaee2025c, 0x8c0200a8, 0xaee20260, 
+0x8c0200ac, 0xaee20264, 0x8c0200b0, 0xaee20268, 
+0x8c0200b4, 0xaee2026c, 0x8c0200b8, 0xaee20270, 
+0x8c0200bc, 0x24040001, 0xaee20274, 0xaee00034, 
+0x41080, 0x571021, 0x8ee30034, 0x8c42023c, 
+0x24840001, 0x621821, 0x2c82000f, 0xaee30034, 
+0x1440fff8, 0x41080, 0x8c0200cc, 0xaee20048, 
+0x8c0200d0, 0xaee2004c, 0x8c0200e0, 0xaee201f8, 
+0x8c0200e4, 0xaee201fc, 0x8c0200e8, 0xaee20200, 
+0x8c0200ec, 0xaee20204, 0x8c0200f0, 0xaee20208, 
+0x8ee400c0, 0x8ee500c4, 0x8c0200fc, 0x45102b, 
+0x1040000b, 0x0, 0x8ee200c0, 0x8ee300c4, 
+0x24040001, 0x24050000, 0x651821, 0x65302b, 
+0x441021, 0x461021, 0xaee200c0, 0xaee300c4, 
+0x8c0200fc, 0x8ee400c0, 0x8ee500c4, 0x2408ffff, 
+0x24090000, 0x401821, 0x1021, 0x882024, 
+0xa92824, 0x822025, 0xa32825, 0xaee400c0, 
+0xaee500c4, 0x8ee400d0, 0x8ee500d4, 0x8c0200f4, 
+0x45102b, 0x1040000b, 0x0, 0x8ee200d0, 
+0x8ee300d4, 0x24040001, 0x24050000, 0x651821, 
+0x65302b, 0x441021, 0x461021, 0xaee200d0, 
+0xaee300d4, 0x8c0200f4, 0x8ee400d0, 0x8ee500d4, 
+0x401821, 0x1021, 0x882024, 0xa92824, 
+0x822025, 0xa32825, 0xaee400d0, 0xaee500d4, 
+0x8ee400c8, 0x8ee500cc, 0x8c0200f8, 0x45102b, 
+0x1040000b, 0x0, 0x8ee200c8, 0x8ee300cc, 
+0x24040001, 0x24050000, 0x651821, 0x65302b, 
+0x441021, 0x461021, 0xaee200c8, 0xaee300cc, 
+0x8c0200f8, 0x8ee400c8, 0x8ee500cc, 0x401821, 
+0x1021, 0x882024, 0xa92824, 0x822025, 
+0xa32825, 0x24020008, 0xaee400c8, 0xaee500cc, 
+0xafa20010, 0xafa00014, 0x8f42000c, 0x8c040208, 
+0x8c05020c, 0xafa20018, 0x8f42010c, 0x26e60028, 
+0x40f809, 0x24070400, 0x104000f0, 0x3c020400, 
+0xafa20020, 0x934205c6, 0x10400089, 0x1821, 
+0x8f5e0018, 0x27aa0020, 0x240200ff, 0x13c20002, 
+0xafaa002c, 0x27c30001, 0x8c020228, 0x609021, 
+0x1642000e, 0x1e38c0, 0x8f42033c, 0x24420001, 
+0xaf42033c, 0x8f42033c, 0x8c020228, 0x3c040001, 
+0x2484610c, 0x3c050009, 0xafa00014, 0xafa20010, 
+0x8fa60020, 0x1000006b, 0x34a50500, 0xf71021, 
+0x8fa30020, 0x8fa40024, 0xac4304c0, 0xac4404c4, 
+0x8f830054, 0x8f820054, 0x247003e8, 0x2021023, 
+0x2c4203e9, 0x1040001b, 0x9821, 0xe08821, 
+0x263504c0, 0x8f440178, 0x8f45017c, 0x2201821, 
+0x240a0004, 0xafaa0010, 0xafb20014, 0x8f48000c, 
+0x1021, 0x2f53021, 0xafa80018, 0x8f48010c, 
+0x24070008, 0xa32821, 0xa3482b, 0x822021, 
+0x100f809, 0x892021, 0x54400006, 0x24130001, 
+0x8f820054, 0x2021023, 0x2c4203e9, 0x1440ffe9, 
+0x0, 0x326200ff, 0x54400017, 0xaf520018, 
+0x8f420378, 0x24420001, 0xaf420378, 0x8f420378, 
+0x8f820120, 0x8faa002c, 0xafa20010, 0x8f820124, 
+0x3c040001, 0x24846118, 0x3c050009, 0xafa20014, 
+0x8d460000, 0x10000033, 0x34a50600, 0x8f420308, 
+0x24130001, 0x24420001, 0xaf420308, 0x8f420308, 
+0x1000001c, 0x326200ff, 0x8f830054, 0x8f820054, 
+0x247003e8, 0x2021023, 0x2c4203e9, 0x10400014, 
+0x9821, 0x24110010, 0x8f42000c, 0x8f440160, 
+0x8f450164, 0x8f860120, 0xafb10010, 0xafb20014, 
+0xafa20018, 0x8f42010c, 0x24070008, 0x40f809, 
+0x24c6001c, 0x1440ffe5, 0x0, 0x8f820054, 
+0x2021023, 0x2c4203e9, 0x1440ffef, 0x0, 
+0x326200ff, 0x54400012, 0x24020001, 0x8f420378, 
+0x24420001, 0xaf420378, 0x8f420378, 0x8f820120, 
+0x8faa002c, 0xafa20010, 0x8f820124, 0x3c040001, 
+0x24846120, 0x3c050009, 0xafa20014, 0x8d460000, 
+0x34a50700, 0xc002b3b, 0x3c03821, 0x1021, 
+0x1440005b, 0x24020001, 0x10000065, 0x0, 
+0x8f510018, 0x240200ff, 0x12220002, 0x8021, 
+0x26300001, 0x8c020228, 0x1602000e, 0x1130c0, 
+0x8f42033c, 0x24420001, 0xaf42033c, 0x8f42033c, 
+0x8c020228, 0x3c040001, 0x248460f4, 0x3c050009, 
+0xafa00014, 0xafa20010, 0x8fa60020, 0x1000003f, 
+0x34a50100, 0xd71021, 0x8fa30020, 0x8fa40024, 
+0xac4304c0, 0xac4404c4, 0xc01821, 0x8f440178, 
+0x8f45017c, 0x1021, 0x24070004, 0xafa70010, 
+0xafb00014, 0x8f48000c, 0x24c604c0, 0x2e63021, 
+0xafa80018, 0x8f48010c, 0x24070008, 0xa32821, 
+0xa3482b, 0x822021, 0x100f809, 0x892021, 
+0x1440000b, 0x24070008, 0x8f820120, 0xafa20010, 
+0x8f820124, 0x3c040001, 0x248460fc, 0x3c050009, 
+0xafa20014, 0x8fa60020, 0x1000001c, 0x34a50200, 
+0x8f440160, 0x8f450164, 0x8f43000c, 0xaf500018, 
+0x8f860120, 0x24020010, 0xafa20010, 0xafb00014, 
+0xafa30018, 0x8f42010c, 0x40f809, 0x24c6001c, 
+0x54400011, 0x24020001, 0x8f420340, 0x24420001, 
+0xaf420340, 0x8f420340, 0x8f820120, 0xafa20010, 
+0x8f820124, 0x3c040001, 0x24846104, 0x3c050009, 
+0xafa20014, 0x8fa60020, 0x34a50300, 0xc002b3b, 
+0x2203821, 0x1021, 0x1040000d, 0x24020001, 
+0x8f4202e8, 0xa34005c6, 0xaf4001b0, 0x24420001, 
+0xaf4202e8, 0x8f4202e8, 0x8ee20150, 0x24420001, 
+0xaee20150, 0x10000003, 0x8ee20150, 0x24020001, 
+0xa34205c6, 0x8fbf0048, 0x8fbe0044, 0x8fb50040, 
+0x8fb3003c, 0x8fb20038, 0x8fb10034, 0x8fb00030, 
+0x3e00008, 0x27bd0050, 0x27bdffd8, 0xafbf0020, 
+0x8f8200b0, 0x30420004, 0x10400068, 0x0, 
+0x8f430128, 0x8f820104, 0x14620005, 0x0, 
+0x8f430130, 0x8f8200b4, 0x10620006, 0x0, 
+0x8f820104, 0xaf420128, 0x8f8200b4, 0x1000005b, 
+0xaf420130, 0x8f8200b0, 0x3c030080, 0x431024, 
+0x1040000d, 0x0, 0x8f82011c, 0x34420002, 
+0xaf82011c, 0x8f8200b0, 0x2403fffb, 0x431024, 
+0xaf8200b0, 0x8f82011c, 0x2403fffd, 0x431024, 
+0x1000004a, 0xaf82011c, 0x8f430128, 0x8f820104, 
+0x14620005, 0x0, 0x8f430130, 0x8f8200b4, 
+0x10620010, 0x0, 0x8f820104, 0xaf420128, 
+0x8f8200b4, 0x8f430128, 0xaf420130, 0xafa30010, 
+0x8f420130, 0x3c040001, 0x24846144, 0xafa20014, 
+0x8f86011c, 0x8f8700b0, 0x3c050005, 0x10000031, 
+0x34a50900, 0x8f420128, 0xafa20010, 0x8f420130, 
+0x3c040001, 0x24846150, 0xafa20014, 0x8f86011c, 
+0x8f8700b0, 0x3c050005, 0xc002b3b, 0x34a51000, 
+0x8f82011c, 0x34420002, 0xaf82011c, 0x8f830104, 
+0x8f8200b0, 0x34420001, 0xaf8200b0, 0x24020008, 
+0xaf830104, 0xafa20010, 0xafa00014, 0x8f42000c, 
+0x8c040208, 0x8c05020c, 0xafa20018, 0x8f42010c, 
+0x26e60028, 0x40f809, 0x24070400, 0x8f82011c, 
+0x2403fffd, 0x431024, 0xaf82011c, 0x8ee201dc, 
+0x24420001, 0xaee201dc, 0x8ee201dc, 0x8f420128, 
+0xafa20010, 0x8f420130, 0x3c040001, 0x2484615c, 
+0xafa20014, 0x8f86011c, 0x8f8700b0, 0x3c050005, 
+0x34a51100, 0xc002b3b, 0x0, 0x8f8200a0, 
+0x30420004, 0x10400069, 0x0, 0x8f43012c, 
+0x8f820124, 0x14620005, 0x0, 0x8f430134, 
+0x8f8200a4, 0x10620006, 0x0, 0x8f820124, 
+0xaf42012c, 0x8f8200a4, 0x1000005c, 0xaf420134, 
+0x8f8200a0, 0x3c030080, 0x431024, 0x1040000d, 
+0x0, 0x8f82011c, 0x34420002, 0xaf82011c, 
+0x8f8200a0, 0x2403fffb, 0x431024, 0xaf8200a0, 
+0x8f82011c, 0x2403fffd, 0x431024, 0x1000004b, 
+0xaf82011c, 0x8f43012c, 0x8f820124, 0x14620005, 
+0x0, 0x8f430134, 0x8f8200a4, 0x10620010, 
+0x0, 0x8f820124, 0xaf42012c, 0x8f8200a4, 
+0x8f43012c, 0xaf420134, 0xafa30010, 0x8f420134, 
+0x3c040001, 0x24846168, 0xafa20014, 0x8f86011c, 
+0x8f8700a0, 0x3c050005, 0x10000032, 0x34a51200, 
+0x8f42012c, 0xafa20010, 0x8f420134, 0x3c040001, 
+0x24846174, 0xafa20014, 0x8f86011c, 0x8f8700a0, 
+0x3c050005, 0xc002b3b, 0x34a51300, 0x8f82011c, 
+0x34420002, 0xaf82011c, 0x8f830124, 0x8f8200a0, 
+0x34420001, 0xaf8200a0, 0x24020080, 0xaf830124, 
+0xafa20010, 0xafa00014, 0x8f420014, 0x8c040208, 
+0x8c05020c, 0xafa20018, 0x8f420108, 0x3c060001, 
+0x24c66ed8, 0x40f809, 0x24070004, 0x8f82011c, 
+0x2403fffd, 0x431024, 0xaf82011c, 0x8ee201dc, 
+0x24420001, 0xaee201dc, 0x8ee201dc, 0x8f42012c, 
+0xafa20010, 0x8f420134, 0x3c040001, 0x24846180, 
+0xafa20014, 0x8f86011c, 0x8f8700a0, 0x3c050005, 
+0x34a51400, 0xc002b3b, 0x0, 0x8fbf0020, 
+0x3e00008, 0x27bd0028, 0x3c081000, 0x24070001, 
+0x3c060080, 0x3c050100, 0x8f820070, 0x481024, 
+0x1040fffd, 0x0, 0x8f820054, 0x24420005, 
+0xaf820078, 0x8c040234, 0x10800016, 0x1821, 
+0x3c020001, 0x571021, 0x8c4240e8, 0x24420005, 
+0x3c010001, 0x370821, 0xac2240e8, 0x3c020001, 
+0x571021, 0x8c4240e8, 0x44102b, 0x14400009, 
+0x0, 0x3c030080, 0x3c010001, 0x370821, 
+0xac2040e8, 0x3c010001, 0x370821, 0x1000000b, 
+0xa02740f0, 0x3c020001, 0x571021, 0x904240f0, 
+0x54400006, 0x661825, 0x3c020001, 0x571021, 
+0x904240f1, 0x54400001, 0x661825, 0x8c040230, 
+0x10800013, 0x0, 0x3c020001, 0x571021, 
+0x8c4240ec, 0x24420005, 0x3c010001, 0x370821, 
+0xac2240ec, 0x3c020001, 0x571021, 0x8c4240ec, 
+0x44102b, 0x14400006, 0x0, 0x3c010001, 
+0x370821, 0xac2040ec, 0x10000006, 0x651825, 
+0x3c020001, 0x571021, 0x904240f2, 0x54400001, 
+0x651825, 0x1060ffbc, 0x0, 0x8f420000, 
+0x10400007, 0x0, 0xaf80004c, 0x8f82004c, 
+0x1040fffd, 0x0, 0x10000005, 0x0, 
+0xaf800048, 0x8f820048, 0x1040fffd, 0x0, 
+0x8f820060, 0x431025, 0xaf820060, 0x8f420000, 
+0x10400003, 0x0, 0x1000ffa7, 0xaf80004c, 
+0x1000ffa5, 0xaf800048, 0x3e00008, 0x0, 
+0x0, 0x0, 0x0, 0x27bdffe0, 
+0xafbf0018, 0x8f860064, 0x30c20004, 0x10400025, 
+0x24040004, 0x8c020114, 0xaf420020, 0xaf840064, 
+0x8f4202fc, 0x24420001, 0xaf4202fc, 0x8f4202fc, 
+0x8f820064, 0x30420004, 0x14400005, 0x0, 
+0x8c030114, 0x8f420020, 0x1462fff2, 0x0, 
+0x8f420000, 0x10400007, 0x8f43003c, 0xaf80004c, 
+0x8f82004c, 0x1040fffd, 0x0, 0x10000005, 
+0x0, 0xaf800048, 0x8f820048, 0x1040fffd, 
+0x0, 0x8f820060, 0x431025, 0xaf820060, 
+0x8f420000, 0x10400073, 0x0, 0x1000006f, 
+0x0, 0x30c20008, 0x10400020, 0x24040008, 
+0x8c02011c, 0xaf420048, 0xaf840064, 0x8f4202a8, 
+0x24420001, 0xaf4202a8, 0x8f4202a8, 0x8f820064, 
+0x30420008, 0x14400005, 0x0, 0x8c03011c, 
+0x8f420048, 0x1462fff2, 0x0, 0x8f420000, 
+0x10400007, 0x0, 0xaf80004c, 0x8f82004c, 
+0x1040fffd, 0x0, 0x10000005, 0x0, 
+0xaf800048, 0x8f820048, 0x1040fffd, 0x0, 
+0x8f820060, 0x1000ffd9, 0x34420200, 0x30c20020, 
+0x10400023, 0x24040020, 0x8c02012c, 0xaf420068, 
+0xaf840064, 0x8f4202d8, 0x24420001, 0xaf4202d8, 
+0x8f4202d8, 0x8f820064, 0x30420020, 0x14400005, 
+0x32c24000, 0x8c03012c, 0x8f420068, 0x1462fff2, 
+0x32c24000, 0x14400002, 0x3c020001, 0x2c2b025, 
+0x8f420000, 0x10400007, 0x0, 0xaf80004c, 
+0x8f82004c, 0x1040fffd, 0x0, 0x10000005, 
+0x0, 0xaf800048, 0x8f820048, 0x1040fffd, 
+0x0, 0x8f820060, 0x1000ffb4, 0x34420800, 
+0x30c20010, 0x10400029, 0x24040010, 0x8c020124, 
+0xaf420058, 0xaf840064, 0x8f4202d4, 0x24420001, 
+0xaf4202d4, 0x8f4202d4, 0x8f820064, 0x30420010, 
+0x14400005, 0x32c22000, 0x8c030124, 0x8f420058, 
+0x1462fff2, 0x32c22000, 0x50400001, 0x36d68000, 
+0x8f420000, 0x10400007, 0x0, 0xaf80004c, 
+0x8f82004c, 0x1040fffd, 0x0, 0x10000005, 
+0x0, 0xaf800048, 0x8f820048, 0x1040fffd, 
+0x0, 0x8f820060, 0x34420100, 0xaf820060, 
+0x8f420000, 0x10400003, 0x0, 0x1000006c, 
+0xaf80004c, 0x1000006a, 0xaf800048, 0x30c20001, 
+0x10400004, 0x24020001, 0xaf820064, 0x10000064, 
+0x0, 0x30c20002, 0x1440000b, 0x3c050003, 
+0x3c040001, 0x24846244, 0x34a50500, 0x3821, 
+0xafa00010, 0xc002b3b, 0xafa00014, 0x2402ffc0, 
+0x10000057, 0xaf820064, 0x8c05022c, 0x8c02010c, 
+0x10a20048, 0x51080, 0x8c460300, 0x24a20001, 
+0x3045003f, 0x24020003, 0xac05022c, 0x61e02, 
+0x10620005, 0x24020010, 0x1062001d, 0x30c20fff, 
+0x10000039, 0x0, 0x8f4302a8, 0x8f440000, 
+0x30c20fff, 0xaf420048, 0x24630001, 0xaf4302a8, 
+0x10800007, 0x8f4202a8, 0xaf80004c, 0x8f82004c, 
+0x1040fffd, 0x0, 0x10000005, 0x0, 
+0xaf800048, 0x8f820048, 0x1040fffd, 0x0, 
+0x8f820060, 0x34420200, 0xaf820060, 0x8f420000, 
+0x1040001f, 0x0, 0x1000001b, 0x0, 
+0xaf420058, 0x32c22000, 0x50400001, 0x36d68000, 
+0x8f4202d4, 0x8f430000, 0x24420001, 0xaf4202d4, 
+0x10600007, 0x8f4202d4, 0xaf80004c, 0x8f82004c, 
+0x1040fffd, 0x0, 0x10000005, 0x0, 
+0xaf800048, 0x8f820048, 0x1040fffd, 0x0, 
+0x8f820060, 0x34420100, 0xaf820060, 0x8f420000, 
+0x10400003, 0x0, 0x10000006, 0xaf80004c, 
+0x10000004, 0xaf800048, 0xc002196, 0xc02021, 
+0x402821, 0x8c02010c, 0x14a20002, 0x24020002, 
+0xaf820064, 0x8f820064, 0x30420002, 0x14400004, 
+0x0, 0x8c02010c, 0x14a2ffac, 0x0, 
+0x8fbf0018, 0x3e00008, 0x27bd0020, 0x3e00008, 
+0x0, 0x27bdffa0, 0xafb00040, 0x808021, 
+0x101602, 0x2442ffff, 0x304300ff, 0x2c620013, 
+0xafbf0058, 0xafbe0054, 0xafb50050, 0xafb3004c, 
+0xafb20048, 0xafb10044, 0x104001f3, 0xafa50034, 
+0x31080, 0x3c010001, 0x220821, 0x8c226288, 
+0x400008, 0x0, 0x101302, 0x30440fff, 
+0x24020001, 0x10820005, 0x24020002, 0x1082000c, 
+0x2402fffe, 0x10000024, 0x3c050003, 0x8f430004, 
+0x3c020001, 0x8c426f04, 0xaf440200, 0xaf440204, 
+0x3c040001, 0x8c846e80, 0x10000009, 0x34630001, 
+0x8f430004, 0xaf440200, 0xaf440204, 0x3c040001, 
+0x8c846e80, 0x621824, 0x3c020001, 0x2442ca28, 
+0x21100, 0x21182, 0xaf430004, 0x3c030800, 
+0x431025, 0xac820038, 0x8f840054, 0x41442, 
+0x41c82, 0x431021, 0x41cc2, 0x431023, 
+0x41d02, 0x431021, 0x41d42, 0x431023, 
+0x10000009, 0xaf420208, 0x3c040001, 0x24846250, 
+0x34a51000, 0x2003021, 0x3821, 0xafa00010, 
+0xc002b3b, 0xafa00014, 0x8f4202a0, 0x24420001, 
+0xaf4202a0, 0x1000021f, 0x8f4202a0, 0x27b00028, 
+0x2002021, 0x24050210, 0xc002bbf, 0x24060008, 
+0xc002518, 0x2002021, 0x10000216, 0x0, 
+0x8faa0034, 0x27a40028, 0xa1880, 0x25420001, 
+0x3042003f, 0xafa20034, 0x8c650300, 0x8faa0034, 
+0x21080, 0x8c430300, 0x25420001, 0x3042003f, 
+0xafa20034, 0xac02022c, 0xafa50028, 0xc002518, 
+0xafa3002c, 0x10000203, 0x0, 0x27b00028, 
+0x2002021, 0x24050210, 0xc002bbf, 0x24060008, 
+0xc002657, 0x2002021, 0x100001fa, 0x0, 
+0x8faa0034, 0x27a40028, 0xa1880, 0x25420001, 
+0x3042003f, 0xafa20034, 0x8c650300, 0x8faa0034, 
+0x21080, 0x8c430300, 0x25420001, 0x3042003f, 
+0xafa20034, 0xac02022c, 0xafa50028, 0xc002657, 
+0xafa3002c, 0x100001e7, 0x0, 0x101302, 
+0x30430fff, 0x24020001, 0x10620005, 0x24020002, 
+0x1062001e, 0x3c020002, 0x10000033, 0x3c050003, 
+0x3c030002, 0x2c31024, 0x54400037, 0x2c3b025, 
+0x8f820228, 0x3c010001, 0x370821, 0xac2238d8, 
+0x8f82022c, 0x3c010001, 0x370821, 0xac2238dc, 
+0x8f820230, 0x3c010001, 0x370821, 0xac2238e0, 
+0x8f820234, 0x3c010001, 0x370821, 0xac2238e4, 
+0x2402ffff, 0xaf820228, 0xaf82022c, 0xaf820230, 
+0xaf820234, 0x10000020, 0x2c3b025, 0x2c21024, 
+0x10400012, 0x3c02fffd, 0x3c020001, 0x571021, 
+0x8c4238d8, 0xaf820228, 0x3c020001, 0x571021, 
+0x8c4238dc, 0xaf82022c, 0x3c020001, 0x571021, 
+0x8c4238e0, 0xaf820230, 0x3c020001, 0x571021, 
+0x8c4238e4, 0xaf820234, 0x3c02fffd, 0x3442ffff, 
+0x10000009, 0x2c2b024, 0x3c040001, 0x2484625c, 
+0x34a51100, 0x2003021, 0x3821, 0xafa00010, 
+0xc002b3b, 0xafa00014, 0x8f4202cc, 0x24420001, 
+0xaf4202cc, 0x1000019f, 0x8f4202cc, 0x101302, 
+0x30450fff, 0x24020001, 0x10a20005, 0x24020002, 
+0x10a2000d, 0x3c0408ff, 0x10000014, 0x3c050003, 
+0x3c0208ff, 0x3442ffff, 0x8f830220, 0x3c040004, 
+0x2c4b025, 0x621824, 0x34630008, 0xaf830220, 
+0x10000012, 0xaf450298, 0x3484fff7, 0x3c03fffb, 
+0x8f820220, 0x3463ffff, 0x2c3b024, 0x441024, 
+0xaf820220, 0x10000009, 0xaf450298, 0x3c040001, 
+0x24846268, 0x34a51200, 0x2003021, 0x3821, 
+0xafa00010, 0xc002b3b, 0xafa00014, 0x8f4202bc, 
+0x24420001, 0xaf4202bc, 0x10000176, 0x8f4202bc, 
+0x27840208, 0x24050200, 0xc002bbf, 0x24060008, 
+0x27440224, 0x24050200, 0xc002bbf, 0x24060008, 
+0x8f4202c4, 0x24420001, 0xaf4202c4, 0x10000169, 
+0x8f4202c4, 0x101302, 0x30430fff, 0x24020001, 
+0x10620011, 0x28620002, 0x50400005, 0x24020002, 
+0x10600007, 0x0, 0x10000017, 0x0, 
+0x1062000f, 0x0, 0x10000013, 0x0, 
+0x8c060248, 0x2021, 0xc005104, 0x24050004, 
+0x10000007, 0x0, 0x8c060248, 0x2021, 
+0xc005104, 0x24050004, 0x10000010, 0x0, 
+0x8c06024c, 0x2021, 0xc005104, 0x24050001, 
+0x1000000a, 0x0, 0x3c040001, 0x24846274, 
+0x3c050003, 0x34a51300, 0x2003021, 0x3821, 
+0xafa00010, 0xc002b3b, 0xafa00014, 0x8f4202c0, 
+0x24420001, 0xaf4202c0, 0x1000013a, 0x8f4202c0, 
+0xc002426, 0x0, 0x10000136, 0x0, 
+0x24020001, 0xa34205c5, 0x24100100, 0x8f4401a8, 
+0x8f4501ac, 0xafb00010, 0xafa00014, 0x8f420014, 
+0xafa20018, 0x8f420108, 0x26e60028, 0x40f809, 
+0x24070400, 0x1040fff5, 0x0, 0x10000125, 
+0x0, 0x3c03ffff, 0x34637fff, 0x8f420368, 
+0x8f440360, 0x2c3b024, 0x1821, 0xaf400058, 
+0xaf40005c, 0xaf400060, 0xaf400064, 0x441023, 
+0xaf420368, 0x3c020900, 0xaf400360, 0xafa20020, 
+0x8f5e0018, 0x27aa0020, 0x240200ff, 0x13c20002, 
+0xafaa003c, 0x27c30001, 0x8c020228, 0x609021, 
+0x1642000e, 0x1e38c0, 0x8f42033c, 0x24420001, 
+0xaf42033c, 0x8f42033c, 0x8c020228, 0x3c040001, 
+0x2484620c, 0x3c050009, 0xafa00014, 0xafa20010, 
+0x8fa60020, 0x1000006b, 0x34a50500, 0xf71021, 
+0x8fa30020, 0x8fa40024, 0xac4304c0, 0xac4404c4, 
+0x8f830054, 0x8f820054, 0x247003e8, 0x2021023, 
+0x2c4203e9, 0x1040001b, 0x9821, 0xe08821, 
+0x263504c0, 0x8f440178, 0x8f45017c, 0x2201821, 
+0x240a0004, 0xafaa0010, 0xafb20014, 0x8f48000c, 
+0x1021, 0x2f53021, 0xafa80018, 0x8f48010c, 
+0x24070008, 0xa32821, 0xa3482b, 0x822021, 
+0x100f809, 0x892021, 0x54400006, 0x24130001, 
+0x8f820054, 0x2021023, 0x2c4203e9, 0x1440ffe9, 
+0x0, 0x326200ff, 0x54400017, 0xaf520018, 
+0x8f420378, 0x24420001, 0xaf420378, 0x8f420378, 
+0x8f820120, 0x8faa003c, 0xafa20010, 0x8f820124, 
+0x3c040001, 0x24846218, 0x3c050009, 0xafa20014, 
+0x8d460000, 0x10000033, 0x34a50600, 0x8f420308, 
+0x24130001, 0x24420001, 0xaf420308, 0x8f420308, 
+0x1000001c, 0x326200ff, 0x8f830054, 0x8f820054, 
+0x247003e8, 0x2021023, 0x2c4203e9, 0x10400014, 
+0x9821, 0x24110010, 0x8f42000c, 0x8f440160, 
+0x8f450164, 0x8f860120, 0xafb10010, 0xafb20014, 
+0xafa20018, 0x8f42010c, 0x24070008, 0x40f809, 
+0x24c6001c, 0x1440ffe5, 0x0, 0x8f820054, 
+0x2021023, 0x2c4203e9, 0x1440ffef, 0x0, 
+0x326200ff, 0x14400011, 0x0, 0x8f420378, 
+0x24420001, 0xaf420378, 0x8f420378, 0x8f820120, 
+0x8faa003c, 0xafa20010, 0x8f820124, 0x3c040001, 
+0x24846220, 0x3c050009, 0xafa20014, 0x8d460000, 
+0x34a50700, 0xc002b3b, 0x3c03821, 0x8f4202b0, 
+0x24420001, 0xaf4202b0, 0x8f4202b0, 0x8f4202f8, 
+0x24420001, 0xaf4202f8, 0x1000008a, 0x8f4202f8, 
+0x8c02025c, 0x27440224, 0xaf4201f0, 0x8c020260, 
+0x24050200, 0x24060008, 0xc002bbf, 0xaf4201f8, 
+0x8f820220, 0x30420008, 0x14400002, 0x24020001, 
+0x24020002, 0xaf420298, 0x8f4202ac, 0x24420001, 
+0xaf4202ac, 0x10000077, 0x8f4202ac, 0x3c0200ff, 
+0x3442ffff, 0x2021824, 0x32c20180, 0x14400006, 
+0x3402fffb, 0x43102b, 0x14400003, 0x0, 
+0x1000006c, 0xaf4300bc, 0x3c040001, 0x24846280, 
+0x3c050003, 0x34a51500, 0x2003021, 0x3821, 
+0xafa00010, 0xc002b3b, 0xafa00014, 0x3c020700, 
+0x34421000, 0x101e02, 0x621825, 0xafa30020, 
+0x8f510018, 0x240200ff, 0x12220002, 0x8021, 
+0x26300001, 0x8c020228, 0x1602000e, 0x1130c0, 
+0x8f42033c, 0x24420001, 0xaf42033c, 0x8f42033c, 
+0x8c020228, 0x3c040001, 0x248461f4, 0x3c050009, 
+0xafa00014, 0xafa20010, 0x8fa60020, 0x1000003f, 
+0x34a50100, 0xd71021, 0x8fa30020, 0x8fa40024, 
+0xac4304c0, 0xac4404c4, 0xc01821, 0x8f440178, 
+0x8f45017c, 0x1021, 0x24070004, 0xafa70010, 
+0xafb00014, 0x8f48000c, 0x24c604c0, 0x2e63021, 
+0xafa80018, 0x8f48010c, 0x24070008, 0xa32821, 
+0xa3482b, 0x822021, 0x100f809, 0x892021, 
+0x1440000b, 0x24070008, 0x8f820120, 0xafa20010, 
+0x8f820124, 0x3c040001, 0x248461fc, 0x3c050009, 
+0xafa20014, 0x8fa60020, 0x1000001c, 0x34a50200, 
+0x8f440160, 0x8f450164, 0x8f43000c, 0xaf500018, 
+0x8f860120, 0x24020010, 0xafa20010, 0xafb00014, 
+0xafa30018, 0x8f42010c, 0x40f809, 0x24c6001c, 
+0x14400010, 0x0, 0x8f420340, 0x24420001, 
+0xaf420340, 0x8f420340, 0x8f820120, 0xafa20010, 
+0x8f820124, 0x3c040001, 0x24846204, 0x3c050009, 
+0xafa20014, 0x8fa60020, 0x34a50300, 0xc002b3b, 
+0x2203821, 0x8f4202e0, 0x24420001, 0xaf4202e0, 
+0x8f4202e0, 0x8f4202f0, 0x24420001, 0xaf4202f0, 
+0x8f4202f0, 0x8fa20034, 0x8fbf0058, 0x8fbe0054, 
+0x8fb50050, 0x8fb3004c, 0x8fb20048, 0x8fb10044, 
+0x8fb00040, 0x3e00008, 0x27bd0060, 0x27bdfff8, 
+0x2408ffff, 0x10a00014, 0x4821, 0x3c0aedb8, 
+0x354a8320, 0x90870000, 0x24840001, 0x3021, 
+0x1071026, 0x30420001, 0x10400002, 0x81842, 
+0x6a1826, 0x604021, 0x24c60001, 0x2cc20008, 
+0x1440fff7, 0x73842, 0x25290001, 0x125102b, 
+0x1440fff0, 0x0, 0x1001021, 0x3e00008, 
+0x27bd0008, 0x27bdffb0, 0xafbf0048, 0xafbe0044, 
+0xafb50040, 0xafb3003c, 0xafb20038, 0xafb10034, 
+0xafb00030, 0x8f870220, 0xafa70024, 0x8f870200, 
+0xafa7002c, 0x8f820220, 0x3c0308ff, 0x3463ffff, 
+0x431024, 0x34420004, 0xaf820220, 0x8f820200, 
+0x3c03c0ff, 0x3463ffff, 0x431024, 0x34420004, 
+0xaf820200, 0x8f530358, 0x8f55035c, 0x8f5e0360, 
+0x8f470364, 0xafa70014, 0x8f470368, 0xafa7001c, 
+0x8f4202d0, 0x274401c0, 0x24420001, 0xaf4202d0, 
+0x8f5002d0, 0x8f510204, 0x8f520200, 0xc002ba8, 
+0x24050400, 0xaf530358, 0xaf55035c, 0xaf5e0360, 
+0x8fa70014, 0xaf470364, 0x8fa7001c, 0xaf470368, 
+0xaf5002d0, 0xaf510204, 0xaf520200, 0x8c02025c, 
+0x27440224, 0xaf4201f0, 0x8c020260, 0x24050200, 
+0x24060008, 0xaf4201f8, 0x24020006, 0xc002bbf, 
+0xaf4201f4, 0x3c023b9a, 0x3442ca00, 0xaf4201fc, 
+0x240203e8, 0x24040002, 0x24030001, 0xaf420294, 
+0xaf440290, 0xaf43029c, 0x8f820220, 0x30420008, 
+0x10400004, 0x0, 0xaf430298, 0x10000003, 
+0x3021, 0xaf440298, 0x3021, 0x3c030001, 
+0x661821, 0x90636d00, 0x3461021, 0x24c60001, 
+0xa043022c, 0x2cc2000f, 0x1440fff8, 0x3461821, 
+0x24c60001, 0x8f820040, 0x24040080, 0x24050080, 
+0x21702, 0x24420030, 0xa062022c, 0x3461021, 
+0xc002ba8, 0xa040022c, 0x8fa70024, 0x30e20004, 
+0x14400006, 0x0, 0x8f820220, 0x3c0308ff, 
+0x3463fffb, 0x431024, 0xaf820220, 0x8fa7002c, 
+0x30e20004, 0x14400006, 0x0, 0x8f820200, 
+0x3c03c0ff, 0x3463fffb, 0x431024, 0xaf820200, 
+0x8fbf0048, 0x8fbe0044, 0x8fb50040, 0x8fb3003c, 
+0x8fb20038, 0x8fb10034, 0x8fb00030, 0x3e00008, 
+0x27bd0050, 0x0, 0x0, 0xaf400104, 
+0x24040001, 0x410c0, 0x2e21821, 0x24820001, 
+0x3c010001, 0x230821, 0xa42234d0, 0x402021, 
+0x2c820080, 0x1440fff8, 0x410c0, 0x24020001, 
+0x3c010001, 0x370821, 0xa42038d0, 0xaf420100, 
+0xaf800228, 0xaf80022c, 0xaf800230, 0xaf800234, 
+0x3e00008, 0x0, 0x27bdffe8, 0xafbf0014, 
+0xafb00010, 0x8f420104, 0x28420005, 0x10400026, 
+0x808021, 0x3c020001, 0x8f430104, 0x344230d0, 
+0x2e22021, 0x318c0, 0x621821, 0x2e31821, 
+0x83102b, 0x10400015, 0x1021, 0x96070000, 
+0x24840006, 0x24660006, 0x9482fffc, 0x14470009, 
+0x2821, 0x9483fffe, 0x96020002, 0x14620006, 
+0xa01021, 0x94820000, 0x96030004, 0x431026, 
+0x2c450001, 0xa01021, 0x14400009, 0x24840008, 
+0x86102b, 0x1440fff0, 0x1021, 0x304200ff, 
+0x14400030, 0x24020001, 0x1000002e, 0x1021, 
+0x1000fffa, 0x24020001, 0x2002021, 0xc00240c, 
+0x24050006, 0x3042007f, 0x218c0, 0x2e31021, 
+0x3c010001, 0x220821, 0x942230d0, 0x1040fff2, 
+0x2e31021, 0x3c060001, 0xc23021, 0x94c630d0, 
+0x10c0ffed, 0x3c080001, 0x350834d2, 0x96070000, 
+0x610c0, 0x572021, 0x882021, 0x94820000, 
+0x14470009, 0x2821, 0x94830002, 0x96020002, 
+0x14620006, 0xa01021, 0x94820004, 0x96030004, 
+0x431026, 0x2c450001, 0xa01021, 0x14400007, 
+0x610c0, 0x2e21021, 0x3c060001, 0xc23021, 
+0x94c634d0, 0x14c0ffeb, 0x610c0, 0x10c0ffd2, 
+0x24020001, 0x8fbf0014, 0x8fb00010, 0x3e00008, 
+0x27bd0018, 0x3e00008, 0x0, 0x27bdffb0, 
+0x801021, 0xafb00030, 0x24500002, 0x2002021, 
+0x24050006, 0xafb10034, 0x408821, 0xafbf0048, 
+0xafbe0044, 0xafb50040, 0xafb3003c, 0xc00240c, 
+0xafb20038, 0x3047007f, 0x710c0, 0x2e21021, 
+0x3c050001, 0xa22821, 0x94a530d0, 0x50a0001c, 
+0xa03021, 0x3c090001, 0x352934d2, 0x96280002, 
+0x510c0, 0x572021, 0x892021, 0x94820000, 
+0x14480009, 0x3021, 0x94830002, 0x96020002, 
+0x14620006, 0xc01021, 0x94820004, 0x96030004, 
+0x431026, 0x2c460001, 0xc01021, 0x14400007, 
+0x510c0, 0x2e21021, 0x3c050001, 0xa22821, 
+0x94a534d0, 0x14a0ffeb, 0x510c0, 0xa03021, 
+0x10c00014, 0x610c0, 0x571821, 0x3c010001, 
+0x230821, 0x8c2334d0, 0x571021, 0xafa30010, 
+0x3c010001, 0x220821, 0x8c2234d4, 0x3c040001, 
+0x24846394, 0xafa20014, 0x8e260000, 0x8e270004, 
+0x3c050004, 0xc002b3b, 0x34a50400, 0x10000063, 
+0x3c020800, 0x8f450100, 0x10a00006, 0x510c0, 
+0x2e21021, 0x3c010001, 0x220821, 0x942234d0, 
+0xaf420100, 0xa03021, 0x14c00011, 0x628c0, 
+0x710c0, 0x2e21021, 0xafa70010, 0x3c010001, 
+0x220821, 0x942230d0, 0x3c040001, 0x248463a0, 
+0xafa20014, 0x8e260000, 0x8e270004, 0x3c050004, 
+0xc002b3b, 0x34a50500, 0x10000048, 0x3c020800, 
+0xb71821, 0x3c020001, 0x96040000, 0x344234d2, 
+0x621821, 0xa4640000, 0x8e020002, 0x720c0, 
+0xac620002, 0x2e41021, 0x3c030001, 0x621821, 
+0x946330d0, 0x2e51021, 0x3c010001, 0x220821, 
+0xa42334d0, 0x2e41021, 0x3c010001, 0x220821, 
+0xa42630d0, 0x8f420104, 0x24420001, 0x28420080, 
+0x1040000f, 0x3c020002, 0x8f420104, 0x3c040001, 
+0x348430d2, 0x96030000, 0x210c0, 0x571021, 
+0x441021, 0xa4430000, 0x8e030002, 0xac430002, 
+0x8f420104, 0x24420001, 0xaf420104, 0x3c020002, 
+0x2c21024, 0x10400011, 0x72142, 0x3c030001, 
+0x346338d8, 0x24020003, 0x441023, 0x21080, 
+0x572021, 0x832021, 0x571021, 0x431021, 
+0x30e5001f, 0x8c430000, 0x24020001, 0xa21004, 
+0x621825, 0x1000000c, 0xac830000, 0x24020003, 
+0x441023, 0x21080, 0x5c2821, 0x5c1021, 
+0x30e4001f, 0x8c430228, 0x24020001, 0x821004, 
+0x621825, 0xaca30228, 0x3c020800, 0x34421000, 
+0x1821, 0xafa20020, 0x8f5e0018, 0x27aa0020, 
+0x240200ff, 0x13c20002, 0xafaa002c, 0x27c30001, 
+0x8c020228, 0x609021, 0x1642000e, 0x1e38c0, 
+0x8f42033c, 0x24420001, 0xaf42033c, 0x8f42033c, 
+0x8c020228, 0x3c040001, 0x2484635c, 0x3c050009, 
+0xafa00014, 0xafa20010, 0x8fa60020, 0x1000006b, 
+0x34a50500, 0xf71021, 0x8fa30020, 0x8fa40024, 
+0xac4304c0, 0xac4404c4, 0x8f830054, 0x8f820054, 
+0x247003e8, 0x2021023, 0x2c4203e9, 0x1040001b, 
+0x9821, 0xe08821, 0x263504c0, 0x8f440178, 
+0x8f45017c, 0x2201821, 0x240a0004, 0xafaa0010, 
+0xafb20014, 0x8f48000c, 0x1021, 0x2f53021, 
+0xafa80018, 0x8f48010c, 0x24070008, 0xa32821, 
+0xa3482b, 0x822021, 0x100f809, 0x892021, 
+0x54400006, 0x24130001, 0x8f820054, 0x2021023, 
+0x2c4203e9, 0x1440ffe9, 0x0, 0x326200ff, 
+0x54400017, 0xaf520018, 0x8f420378, 0x24420001, 
+0xaf420378, 0x8f420378, 0x8f820120, 0x8faa002c, 
+0xafa20010, 0x8f820124, 0x3c040001, 0x24846368, 
+0x3c050009, 0xafa20014, 0x8d460000, 0x10000033, 
+0x34a50600, 0x8f420308, 0x24130001, 0x24420001, 
+0xaf420308, 0x8f420308, 0x1000001c, 0x326200ff, 
+0x8f830054, 0x8f820054, 0x247003e8, 0x2021023, 
+0x2c4203e9, 0x10400014, 0x9821, 0x24110010, 
+0x8f42000c, 0x8f440160, 0x8f450164, 0x8f860120, 
+0xafb10010, 0xafb20014, 0xafa20018, 0x8f42010c, 
+0x24070008, 0x40f809, 0x24c6001c, 0x1440ffe5, 
+0x0, 0x8f820054, 0x2021023, 0x2c4203e9, 
+0x1440ffef, 0x0, 0x326200ff, 0x14400011, 
+0x0, 0x8f420378, 0x24420001, 0xaf420378, 
+0x8f420378, 0x8f820120, 0x8faa002c, 0xafa20010, 
+0x8f820124, 0x3c040001, 0x24846370, 0x3c050009, 
+0xafa20014, 0x8d460000, 0x34a50700, 0xc002b3b, 
+0x3c03821, 0x8f4202b4, 0x24420001, 0xaf4202b4, 
+0x8f4202b4, 0x8f4202f4, 0x24420001, 0xaf4202f4, 
+0x8f4202f4, 0x8fbf0048, 0x8fbe0044, 0x8fb50040, 
+0x8fb3003c, 0x8fb20038, 0x8fb10034, 0x8fb00030, 
+0x3e00008, 0x27bd0050, 0x27bdffa0, 0x801021, 
+0xafb00040, 0x24500002, 0x2002021, 0x24050006, 
+0xafb10044, 0x408821, 0xafbf0058, 0xafbe0054, 
+0xafb50050, 0xafb3004c, 0xc00240c, 0xafb20048, 
+0x3048007f, 0x810c0, 0x2e21021, 0x3c060001, 
+0xc23021, 0x94c630d0, 0x10c0001c, 0x3821, 
+0x3c0a0001, 0x354a34d2, 0x96290002, 0x610c0, 
+0x572021, 0x8a2021, 0x94820000, 0x14490009, 
+0x2821, 0x94830002, 0x96020002, 0x14620006, 
+0xa01021, 0x94820004, 0x96030004, 0x431026, 
+0x2c450001, 0xa01021, 0x14400008, 0x610c0, 
+0xc03821, 0x2e21021, 0x3c060001, 0xc23021, 
+0x94c634d0, 0x14c0ffea, 0x610c0, 0x14c00011, 
+0xafa70028, 0x810c0, 0x2e21021, 0xafa80010, 
+0x3c010001, 0x220821, 0x942230d0, 0x3c040001, 
+0x248463ac, 0xafa20014, 0x8e260000, 0x8e270004, 
+0x3c050004, 0xc002b3b, 0x34a50900, 0x10000075, 
+0x3c020800, 0x10e0000c, 0x610c0, 0x2e21021, 
+0x3c030001, 0x621821, 0x946334d0, 0x710c0, 
+0x2e21021, 0x3c010001, 0x220821, 0xa42334d0, 
+0x1000000b, 0x3c040001, 0x2e21021, 0x3c030001, 
+0x621821, 0x946334d0, 0x810c0, 0x2e21021, 
+0x3c010001, 0x220821, 0xa42330d0, 0x3c040001, 
+0x348430d0, 0x8f430100, 0x610c0, 0x2e21021, 
+0x3c010001, 0x220821, 0xa42334d0, 0x8f420104, 
+0x2e43821, 0x2821, 0x18400029, 0xaf460100, 
+0x24e60006, 0x94c3fffc, 0x96020000, 0x14620009, 
+0x2021, 0x94c3fffe, 0x96020002, 0x14620006, 
+0x801021, 0x94c20000, 0x96030004, 0x431026, 
+0x2c440001, 0x801021, 0x50400014, 0x24a50001, 
+0x8f420104, 0x2442ffff, 0xa2102a, 0x1040000b, 
+0x24e40004, 0x94820006, 0x8c830008, 0xa482fffe, 
+0xac830000, 0x8f420104, 0x24a50001, 0x2442ffff, 
+0xa2102a, 0x1440fff7, 0x24840008, 0x8f420104, 
+0x2442ffff, 0x10000006, 0xaf420104, 0x8f420104, 
+0x24c60008, 0xa2102a, 0x1440ffda, 0x24e70008, 
+0x810c0, 0x2e21021, 0x3c010001, 0x220821, 
+0x942230d0, 0x14400023, 0x3c020800, 0x3c020002, 
+0x2c21024, 0x10400012, 0x82142, 0x3c030001, 
+0x346338d8, 0x24020003, 0x441023, 0x21080, 
+0x572021, 0x832021, 0x571021, 0x431021, 
+0x3105001f, 0x24030001, 0x8c420000, 0xa31804, 
+0x31827, 0x431024, 0x1000000d, 0xac820000, 
+0x24020003, 0x441023, 0x21080, 0x5c2821, 
+0x5c1021, 0x3104001f, 0x24030001, 0x8c420228, 
+0x831804, 0x31827, 0x431024, 0xaca20228, 
+0x3c020800, 0x34422000, 0x1821, 0xafa20020, 
+0x8f5e0018, 0x27ab0020, 0x240200ff, 0x13c20002, 
+0xafab0034, 0x27c30001, 0x8c020228, 0x609021, 
+0x1642000e, 0x1e38c0, 0x8f42033c, 0x24420001, 
+0xaf42033c, 0x8f42033c, 0x8c020228, 0x3c040001, 
+0x2484635c, 0x3c050009, 0xafa00014, 0xafa20010, 
+0x8fa60020, 0x1000006b, 0x34a50500, 0xf71021, 
+0x8fa30020, 0x8fa40024, 0xac4304c0, 0xac4404c4, 
+0x8f830054, 0x8f820054, 0x247003e8, 0x2021023, 
+0x2c4203e9, 0x1040001b, 0x9821, 0xe08821, 
+0x263504c0, 0x8f440178, 0x8f45017c, 0x2201821, 
+0x240b0004, 0xafab0010, 0xafb20014, 0x8f48000c, 
+0x1021, 0x2f53021, 0xafa80018, 0x8f48010c, 
+0x24070008, 0xa32821, 0xa3482b, 0x822021, 
+0x100f809, 0x892021, 0x54400006, 0x24130001, 
+0x8f820054, 0x2021023, 0x2c4203e9, 0x1440ffe9, 
+0x0, 0x326200ff, 0x54400017, 0xaf520018, 
+0x8f420378, 0x24420001, 0xaf420378, 0x8f420378, 
+0x8f820120, 0x8fab0034, 0xafa20010, 0x8f820124, 
+0x3c040001, 0x24846368, 0x3c050009, 0xafa20014, 
+0x8d660000, 0x10000033, 0x34a50600, 0x8f420308, 
+0x24130001, 0x24420001, 0xaf420308, 0x8f420308, 
+0x1000001c, 0x326200ff, 0x8f830054, 0x8f820054, 
+0x247003e8, 0x2021023, 0x2c4203e9, 0x10400014, 
+0x9821, 0x24110010, 0x8f42000c, 0x8f440160, 
+0x8f450164, 0x8f860120, 0xafb10010, 0xafb20014, 
+0xafa20018, 0x8f42010c, 0x24070008, 0x40f809, 
+0x24c6001c, 0x1440ffe5, 0x0, 0x8f820054, 
+0x2021023, 0x2c4203e9, 0x1440ffef, 0x0, 
+0x326200ff, 0x14400011, 0x0, 0x8f420378, 
+0x24420001, 0xaf420378, 0x8f420378, 0x8f820120, 
+0x8fab0034, 0xafa20010, 0x8f820124, 0x3c040001, 
+0x24846370, 0x3c050009, 0xafa20014, 0x8d660000, 
+0x34a50700, 0xc002b3b, 0x3c03821, 0x8f4202b8, 
+0x24420001, 0xaf4202b8, 0x8f4202b8, 0x8f4202f4, 
+0x24420001, 0xaf4202f4, 0x8f4202f4, 0x8fbf0058, 
+0x8fbe0054, 0x8fb50050, 0x8fb3004c, 0x8fb20048, 
+0x8fb10044, 0x8fb00040, 0x3e00008, 0x27bd0060, 
+0x0, 0x0, 0x0, 0x27bdffe0, 
+0x27644000, 0xafbf0018, 0xc002ba8, 0x24051000, 
+0x3c030001, 0x34632cc0, 0x3c040001, 0x34842ec8, 
+0x24020020, 0xaf82011c, 0x2e31021, 0xaf800100, 
+0xaf800104, 0xaf800108, 0xaf800110, 0xaf800114, 
+0xaf800118, 0xaf800120, 0xaf800124, 0xaf800128, 
+0xaf800130, 0xaf800134, 0xaf800138, 0xaf4200ec, 
+0x2e31021, 0xaf4200f0, 0x2e41021, 0xaf4200f4, 
+0x2e41021, 0xaf4200f8, 0x3c020001, 0x571021, 
+0x904240f4, 0x1440001c, 0x3c050001, 0x8f82011c, 
+0x3c040001, 0x24846470, 0x3c050001, 0x34420001, 
+0xaf82011c, 0xafa00010, 0xafa00014, 0x8f86011c, 
+0x34a50100, 0xc002b3b, 0x3821, 0x8c020218, 
+0x30420040, 0x10400014, 0x0, 0x8f82011c, 
+0x3c040001, 0x2484647c, 0x3c050001, 0x34420004, 
+0xaf82011c, 0xafa00010, 0xafa00014, 0x8f86011c, 
+0x10000007, 0x34a50200, 0x3c040001, 0x24846484, 
+0xafa00010, 0xafa00014, 0x8f86011c, 0x34a50300, 
+0xc002b3b, 0x3821, 0x8fbf0018, 0x3e00008, 
+0x27bd0020, 0x8fa90010, 0x8f83012c, 0x8faa0014, 
+0x8fab0018, 0x1060000a, 0x27624fe0, 0x14620002, 
+0x24680020, 0x27684800, 0x8f820128, 0x11020004, 
+0x0, 0x8f820124, 0x15020007, 0x0, 
+0x8f430334, 0x1021, 0x24630001, 0xaf430334, 
+0x10000039, 0x8f430334, 0xac640000, 0xac650004, 
+0xac660008, 0xa467000e, 0xac690018, 0xac6a001c, 
+0xac6b0010, 0xac620014, 0xaf880120, 0x8f4200fc, 
+0x8f4400f4, 0x2442ffff, 0xaf4200fc, 0x8c820000, 
+0x10490005, 0x3042ff8f, 0x10400019, 0x3122ff8f, 
+0x10400018, 0x3c020001, 0x8c830004, 0x2c620010, 
+0x10400013, 0x3c020001, 0x24630001, 0xac830004, 
+0x8f4300f8, 0x344230c8, 0x2e21021, 0x54620004, 
+0x24620008, 0x3c020001, 0x34422ec8, 0x2e21021, 
+0x14440015, 0x24020001, 0x8f820128, 0x24420020, 
+0xaf820128, 0x8f820128, 0x1000000f, 0x24020001, 
+0x3c020001, 0x344230c8, 0x2e21021, 0x54820004, 
+0x24820008, 0x3c020001, 0x34422ec8, 0x2e21021, 
+0x402021, 0x24020001, 0xaf4400f4, 0xac890000, 
+0xac820004, 0x24020001, 0x3e00008, 0x0, 
+0x3e00008, 0x0, 0x8fa90010, 0x8f83010c, 
+0x8faa0014, 0x8fab0018, 0x1060000a, 0x276247e0, 
+0x14620002, 0x24680020, 0x27684000, 0x8f820108, 
+0x11020004, 0x0, 0x8f820104, 0x15020007, 
+0x0, 0x8f430338, 0x1021, 0x24630001, 
+0xaf430338, 0x10000035, 0x8f430338, 0xac640000, 
+0xac650004, 0xac660008, 0xa467000e, 0xac690018, 
+0xac6a001c, 0xac6b0010, 0xac620014, 0xaf880100, 
+0x8f4400ec, 0x8c820000, 0x30420006, 0x10400019, 
+0x31220006, 0x10400018, 0x3c020001, 0x8c830004, 
+0x2c620010, 0x10400013, 0x3c020001, 0x24630001, 
+0xac830004, 0x8f4300f0, 0x34422ec0, 0x2e21021, 
+0x54620004, 0x24620008, 0x3c020001, 0x34422cc0, 
+0x2e21021, 0x14440015, 0x24020001, 0x8f820108, 
+0x24420020, 0xaf820108, 0x8f820108, 0x1000000f, 
+0x24020001, 0x3c020001, 0x34422ec0, 0x2e21021, 
+0x54820004, 0x24820008, 0x3c020001, 0x34422cc0, 
+0x2e21021, 0x402021, 0x24020001, 0xaf4400ec, 
+0xac890000, 0xac820004, 0x24020001, 0x3e00008, 
+0x0, 0x3e00008, 0x0, 0x27bdffd8, 
+0x3c040001, 0x2484648c, 0x3c050001, 0xafbf0024, 
+0xafb20020, 0xafb1001c, 0xafb00018, 0x8f900104, 
+0x8f9100b0, 0x8f92011c, 0x34a52500, 0x8f820100, 
+0x2403021, 0x2203821, 0xafa20010, 0xc002b3b, 
+0xafb00014, 0x8e020008, 0xafa20010, 0x8e02000c, 
+0x3c040001, 0x24846498, 0xafa20014, 0x8e060000, 
+0x8e070004, 0x3c050001, 0xc002b3b, 0x34a52510, 
+0x8e020018, 0xafa20010, 0x8e02001c, 0x3c040001, 
+0x248464a4, 0xafa20014, 0x8e060010, 0x8e070014, 
+0x3c050001, 0xc002b3b, 0x34a52520, 0x3c027f00, 
+0x2221024, 0x3c030800, 0x54430016, 0x3c030200, 
+0x8f82009c, 0x3042ffff, 0x14400012, 0x3c030200, 
+0x3c040001, 0x248464b0, 0x3c050002, 0x34a5f030, 
+0x3021, 0x3821, 0x36420002, 0xaf82011c, 
+0x36220001, 0xaf8200b0, 0xaf900104, 0xaf92011c, 
+0xafa00010, 0xc002b3b, 0xafa00014, 0x10000024, 
+0x0, 0x2c31024, 0x1040000d, 0x2231024, 
+0x1040000b, 0x36420002, 0xaf82011c, 0x36220001, 
+0xaf8200b0, 0xaf900104, 0xaf92011c, 0x8f420330, 
+0x24420001, 0xaf420330, 0x10000015, 0x8f420330, 
+0x3c040001, 0x248464b8, 0x240202a9, 0xafa20010, 
+0xafa00014, 0x8f860144, 0x3c070001, 0x24e764c0, 
+0xc002b3b, 0x3405dead, 0x8f82011c, 0x34420002, 
+0xaf82011c, 0x8f820220, 0x34420004, 0xaf820220, 
+0x8f820140, 0x3c030001, 0x431025, 0xaf820140, 
+0x8fbf0024, 0x8fb20020, 0x8fb1001c, 0x8fb00018, 
+0x3e00008, 0x27bd0028, 0x27bdffd8, 0x3c040001, 
+0x248464e8, 0x3c050001, 0xafbf0024, 0xafb20020, 
+0xafb1001c, 0xafb00018, 0x8f900124, 0x8f9100a0, 
+0x8f92011c, 0x34a52600, 0x8f820120, 0x2403021, 
+0x2203821, 0xafa20010, 0xc002b3b, 0xafb00014, 
+0x8e020008, 0xafa20010, 0x8e02000c, 0x3c040001, 
+0x248464f4, 0xafa20014, 0x8e060000, 0x8e070004, 
+0x3c050001, 0xc002b3b, 0x34a52610, 0x8e020018, 
+0xafa20010, 0x8e02001c, 0x3c040001, 0x24846500, 
+0xafa20014, 0x8e060010, 0x8e070014, 0x3c050001, 
+0xc002b3b, 0x34a52620, 0x3c027f00, 0x2221024, 
+0x3c030800, 0x54430016, 0x3c030200, 0x8f8200ac, 
+0x3042ffff, 0x14400012, 0x3c030200, 0x3c040001, 
+0x2484650c, 0x3c050001, 0x34a5f030, 0x3021, 
+0x3821, 0x36420002, 0xaf82011c, 0x36220001, 
+0xaf8200a0, 0xaf900124, 0xaf92011c, 0xafa00010, 
+0xc002b3b, 0xafa00014, 0x10000024, 0x0, 
+0x2c31024, 0x1040000d, 0x2231024, 0x1040000b, 
+0x36420002, 0xaf82011c, 0x36220001, 0xaf8200a0, 
+0xaf900124, 0xaf92011c, 0x8f42032c, 0x24420001, 
+0xaf42032c, 0x10000015, 0x8f42032c, 0x3c040001, 
+0x248464b8, 0x240202e2, 0xafa20010, 0xafa00014, 
+0x8f860144, 0x3c070001, 0x24e764c0, 0xc002b3b, 
+0x3405dead, 0x8f82011c, 0x34420002, 0xaf82011c, 
+0x8f820220, 0x34420004, 0xaf820220, 0x8f820140, 
+0x3c030001, 0x431025, 0xaf820140, 0x8fbf0024, 
+0x8fb20020, 0x8fb1001c, 0x8fb00018, 0x3e00008, 
+0x27bd0028, 0x6021, 0x5021, 0x3021, 
+0x2821, 0x6821, 0x4821, 0x7821, 
+0x7021, 0x8f880124, 0x8f870104, 0x1580002e, 
+0x8f8b011c, 0x11a00014, 0x31620800, 0x8f820120, 
+0x10460029, 0x0, 0x3c040001, 0x8c846ee4, 
+0x8cc20000, 0x8cc30004, 0xac820000, 0xac830004, 
+0x8cc20008, 0xac820008, 0x94c2000e, 0xa482000e, 
+0x8cc20010, 0x240c0001, 0xac820010, 0x8cc20014, 
+0x10000012, 0x24c60020, 0x10400017, 0x0, 
+0x3c040001, 0x8c846ee4, 0x8d020000, 0x8d030004, 
+0xac820000, 0xac830004, 0x8d020008, 0xac820008, 
+0x9502000e, 0xa482000e, 0x8d020010, 0x25060020, 
+0xac820010, 0x8d020014, 0x240c0001, 0xc01821, 
+0xac820014, 0x27624fe0, 0x43102b, 0x54400001, 
+0x27634800, 0x603021, 0x1540002f, 0x31620100, 
+0x11200014, 0x31628000, 0x8f820100, 0x1045002a, 
+0x31620100, 0x3c040001, 0x8c846ee0, 0x8ca20000, 
+0x8ca30004, 0xac820000, 0xac830004, 0x8ca20008, 
+0xac820008, 0x94a2000e, 0xa482000e, 0x8ca20010, 
+0x240a0001, 0xac820010, 0x8ca20014, 0x10000012, 
+0x24a50020, 0x10400018, 0x31620100, 0x3c040001, 
+0x8c846ee0, 0x8ce20000, 0x8ce30004, 0xac820000, 
+0xac830004, 0x8ce20008, 0xac820008, 0x94e2000e, 
+0xa482000e, 0x8ce20010, 0x24e50020, 0xac820010, 
+0x8ce20014, 0x240a0001, 0xa01821, 0xac820014, 
+0x276247e0, 0x43102b, 0x54400001, 0x27634000, 
+0x602821, 0x31620100, 0x5440001d, 0x31621000, 
+0x11a00009, 0x31a20800, 0x10400004, 0x25020020, 
+0x8f8200a8, 0xa5e20000, 0x25020020, 0xaf820124, 
+0x8f880124, 0x6821, 0x11800011, 0x31621000, 
+0x3c040001, 0x8c846ee4, 0x8c820000, 0x8c830004, 
+0xaf820080, 0xaf830084, 0x8c820008, 0xaf8200a4, 
+0x9482000e, 0xaf8200ac, 0x8c820010, 0x6021, 
+0xaf8200a0, 0x8c8d0010, 0x8c8f0014, 0x31621000, 
+0x1440ff82, 0x0, 0x1120000f, 0x31220800, 
+0x10400004, 0x3c020002, 0x8f8200b8, 0xa5c20000, 
+0x3c020002, 0x1221024, 0x10400004, 0x24e20020, 
+0x8f8200b4, 0xaf8200d4, 0x24e20020, 0xaf820104, 
+0x8f870104, 0x4821, 0x1140ff70, 0x0, 
+0x3c040001, 0x8c846ee0, 0x8c820000, 0x8c830004, 
+0xaf820090, 0xaf830094, 0x8c820008, 0xaf8200b4, 
+0x9482000e, 0xaf82009c, 0x8c820010, 0x5021, 
+0xaf8200b0, 0x8c890010, 0x1000ff60, 0x8c8e0014, 
+0x3e00008, 0x0, 0x6021, 0x5821, 
+0x3021, 0x2821, 0x6821, 0x5021, 
+0x7821, 0x7021, 0x8f880124, 0x8f870104, 
+0x3c180100, 0x1580002e, 0x8f89011c, 0x11a00014, 
+0x31220800, 0x8f820120, 0x10460029, 0x0, 
+0x3c040001, 0x8c846ee4, 0x8cc20000, 0x8cc30004, 
+0xac820000, 0xac830004, 0x8cc20008, 0xac820008, 
+0x94c2000e, 0xa482000e, 0x8cc20010, 0x240c0001, 
+0xac820010, 0x8cc20014, 0x10000012, 0x24c60020, 
+0x10400017, 0x0, 0x3c040001, 0x8c846ee4, 
+0x8d020000, 0x8d030004, 0xac820000, 0xac830004, 
+0x8d020008, 0xac820008, 0x9502000e, 0xa482000e, 
+0x8d020010, 0x25060020, 0xac820010, 0x8d020014, 
+0x240c0001, 0xc01821, 0xac820014, 0x27624fe0, 
+0x43102b, 0x54400001, 0x27634800, 0x603021, 
+0x1560002f, 0x31220100, 0x11400014, 0x31228000, 
+0x8f820100, 0x1045002a, 0x31220100, 0x3c040001, 
+0x8c846ee0, 0x8ca20000, 0x8ca30004, 0xac820000, 
+0xac830004, 0x8ca20008, 0xac820008, 0x94a2000e, 
+0xa482000e, 0x8ca20010, 0x240b0001, 0xac820010, 
+0x8ca20014, 0x10000012, 0x24a50020, 0x10400018, 
+0x31220100, 0x3c040001, 0x8c846ee0, 0x8ce20000, 
+0x8ce30004, 0xac820000, 0xac830004, 0x8ce20008, 
+0xac820008, 0x94e2000e, 0xa482000e, 0x8ce20010, 
+0x24e50020, 0xac820010, 0x8ce20014, 0x240b0001, 
+0xa01821, 0xac820014, 0x276247e0, 0x43102b, 
+0x54400001, 0x27634000, 0x602821, 0x31220100, 
+0x5440001d, 0x31221000, 0x11a00009, 0x31a20800, 
+0x10400004, 0x25020020, 0x8f8200a8, 0xa5e20000, 
+0x25020020, 0xaf820124, 0x8f880124, 0x6821, 
+0x11800011, 0x31221000, 0x3c040001, 0x8c846ee4, 
+0x8c820000, 0x8c830004, 0xaf820080, 0xaf830084, 
+0x8c820008, 0xaf8200a4, 0x9482000e, 0xaf8200ac, 
+0x8c820010, 0x6021, 0xaf8200a0, 0x8c8d0010, 
+0x8c8f0014, 0x31221000, 0x14400022, 0x0, 
+0x1140000f, 0x31420800, 0x10400004, 0x3c020002, 
+0x8f8200b8, 0xa5c20000, 0x3c020002, 0x1421024, 
+0x10400004, 0x24e20020, 0x8f8200b4, 0xaf8200d4, 
+0x24e20020, 0xaf820104, 0x8f870104, 0x5021, 
+0x11600010, 0x0, 0x3c040001, 0x8c846ee0, 
+0x8c820000, 0x8c830004, 0xaf820090, 0xaf830094, 
+0x8c820008, 0xaf8200b4, 0x9482000e, 0xaf82009c, 
+0x8c820010, 0x5821, 0xaf8200b0, 0x8c8a0010, 
+0x8c8e0014, 0x8f820070, 0x3c031000, 0x431024, 
+0x1040ff5c, 0x0, 0x8f820054, 0x24420005, 
+0xaf820078, 0x8c040234, 0x10800016, 0x1821, 
+0x3c020001, 0x571021, 0x8c4240e8, 0x24420005, 
+0x3c010001, 0x370821, 0xac2240e8, 0x3c020001, 
+0x571021, 0x8c4240e8, 0x44102b, 0x14400009, 
+0x24020001, 0x3c030080, 0x3c010001, 0x370821, 
+0xac2040e8, 0x3c010001, 0x370821, 0x1000000c, 
+0xa02240f0, 0x3c020001, 0x571021, 0x904240f0, 
+0x14400006, 0x3c020080, 0x3c020001, 0x571021, 
+0x904240f1, 0x10400002, 0x3c020080, 0x621825, 
+0x8c040230, 0x10800013, 0x0, 0x3c020001, 
+0x571021, 0x8c4240ec, 0x24420005, 0x3c010001, 
+0x370821, 0xac2240ec, 0x3c020001, 0x571021, 
+0x8c4240ec, 0x44102b, 0x14400006, 0x0, 
+0x3c010001, 0x370821, 0xac2040ec, 0x10000006, 
+0x781825, 0x3c020001, 0x571021, 0x904240f2, 
+0x54400001, 0x781825, 0x1060ff1a, 0x0, 
+0x8f420000, 0x10400007, 0x0, 0xaf80004c, 
+0x8f82004c, 0x1040fffd, 0x0, 0x10000005, 
+0x0, 0xaf800048, 0x8f820048, 0x1040fffd, 
+0x0, 0x8f820060, 0x431025, 0xaf820060, 
+0x8f420000, 0x10400003, 0x0, 0x1000ff05, 
+0xaf80004c, 0x1000ff03, 0xaf800048, 0x3e00008, 
+0x0, 0x0, 0x0, 0x3c020001, 
+0x8c426d28, 0x27bdffe8, 0xafbf0014, 0x14400012, 
+0xafb00010, 0x3c100001, 0x26106f90, 0x2002021, 
+0xc002ba8, 0x24052000, 0x26021fe0, 0x3c010001, 
+0xac226eec, 0x3c010001, 0xac226ee8, 0xac020250, 
+0x24022000, 0xac100254, 0xac020258, 0x24020001, 
+0x3c010001, 0xac226d28, 0x8fbf0014, 0x8fb00010, 
+0x3e00008, 0x27bd0018, 0x3c090001, 0x8d296eec, 
+0x8c820000, 0x8fa30010, 0x8fa80014, 0xad220000, 
+0x8c820004, 0xad250008, 0xad220004, 0x8f820054, 
+0xad260010, 0xad270014, 0xad230018, 0xad28001c, 
+0xad22000c, 0x2529ffe0, 0x3c020001, 0x24426f90, 
+0x122102b, 0x10400003, 0x0, 0x3c090001, 
+0x8d296ee8, 0x3c020001, 0x8c426d10, 0xad220000, 
+0x3c020001, 0x8c426d10, 0x3c010001, 0xac296eec, 
+0xad220004, 0xac090250, 0x3e00008, 0x0, 
+0x27bdffd0, 0xafb00010, 0x3c100001, 0x8e106eec, 
+0x3c020001, 0x8c426d10, 0xafb10014, 0x808821, 
+0xafbe0024, 0x8fbe0040, 0x8fa40048, 0xafb20018, 
+0xa09021, 0xafbf0028, 0xafb50020, 0xafb3001c, 
+0xae020000, 0x3c020001, 0x8c426d10, 0xc09821, 
+0xe0a821, 0x10800006, 0xae020004, 0x26050008, 
+0xc002bb3, 0x24060018, 0x10000005, 0x2610ffe0, 
+0x26040008, 0xc002ba8, 0x24050018, 0x2610ffe0, 
+0x3c030001, 0x24636f90, 0x203102b, 0x10400003, 
+0x0, 0x3c100001, 0x8e106ee8, 0x8e220000, 
+0xae020000, 0x8e220004, 0xae120008, 0xae020004, 
+0x8f820054, 0xae130010, 0xae150014, 0xae1e0018, 
+0x8fa80044, 0xae08001c, 0xae02000c, 0x2610ffe0, 
+0x203102b, 0x10400003, 0x0, 0x3c100001, 
+0x8e106ee8, 0x3c020001, 0x8c426d10, 0xae020000, 
+0x3c020001, 0x8c426d10, 0x3c010001, 0xac306eec, 
+0xae020004, 0xac100250, 0x8fbf0028, 0x8fbe0024, 
+0x8fb50020, 0x8fb3001c, 0x8fb20018, 0x8fb10014, 
+0x8fb00010, 0x3e00008, 0x27bd0030, 0x851821, 
+0x83102b, 0x10400006, 0x0, 0xac800000, 
+0x24840004, 0x83102b, 0x5440fffd, 0xac800000, 
+0x3e00008, 0x0, 0xa61821, 0xa3102b, 
+0x10400007, 0x0, 0x8c820000, 0xaca20000, 
+0x24a50004, 0xa3102b, 0x1440fffb, 0x24840004, 
+0x3e00008, 0x0, 0x861821, 0x83102b, 
+0x10400007, 0x0, 0x8ca20000, 0xac820000, 
+0x24840004, 0x83102b, 0x1440fffb, 0x24a50004, 
+0x3e00008, 0x0, 0x63080, 0x861821, 
+0x83102b, 0x10400006, 0x0, 0xac850000, 
+0x24840004, 0x83102b, 0x5440fffd, 0xac850000, 
+0x3e00008, 0x0, 0x0, 0x26e50028, 
+0xa03021, 0x274301c0, 0x8f4d0358, 0x8f47035c, 
+0x8f480360, 0x8f490364, 0x8f4a0368, 0x8f4b0204, 
+0x8f4c0200, 0x24640400, 0x64102b, 0x10400008, 
+0x3c0208ff, 0x8cc20000, 0xac620000, 0x24630004, 
+0x64102b, 0x1440fffb, 0x24c60004, 0x3c0208ff, 
+0x3442ffff, 0x3c03c0ff, 0xaf4d0358, 0xaf47035c, 
+0xaf480360, 0xaf490364, 0xaf4a0368, 0xaf4b0204, 
+0xaf4c0200, 0x8f840220, 0x3463ffff, 0x8f860200, 
+0x821024, 0x34420004, 0xc31824, 0x34630004, 
+0xaf820220, 0xaf830200, 0x8ca20214, 0xac020084, 
+0x8ca20218, 0xac020088, 0x8ca2021c, 0xac02008c, 
+0x8ca20220, 0xac020090, 0x8ca20224, 0xac020094, 
+0x8ca20228, 0xac020098, 0x8ca2022c, 0xac02009c, 
+0x8ca20230, 0xac0200a0, 0x8ca20234, 0xac0200a4, 
+0x8ca20238, 0xac0200a8, 0x8ca2023c, 0xac0200ac, 
+0x8ca20240, 0xac0200b0, 0x8ca20244, 0xac0200b4, 
+0x8ca20248, 0xac0200b8, 0x8ca2024c, 0xac0200bc, 
+0x8ca2001c, 0xac020080, 0x8ca20018, 0xac0200c0, 
+0x8ca20020, 0xac0200cc, 0x8ca20024, 0xac0200d0, 
+0x8ca201d0, 0xac0200e0, 0x8ca201d4, 0xac0200e4, 
+0x8ca201d8, 0xac0200e8, 0x8ca201dc, 0xac0200ec, 
+0x8ca201e0, 0xac0200f0, 0x8ca20098, 0x8ca3009c, 
+0xac0300fc, 0x8ca200a8, 0x8ca300ac, 0xac0300f4, 
+0x8ca200a0, 0x8ca300a4, 0x30840004, 0xac0300f8, 
+0x14800007, 0x30c20004, 0x8f820220, 0x3c0308ff, 
+0x3463fffb, 0x431024, 0xaf820220, 0x30c20004, 
+0x14400006, 0x0, 0x8f820200, 0x3c03c0ff, 
+0x3463fffb, 0x431024, 0xaf820200, 0x8f4202dc, 
+0xa34005c5, 0x24420001, 0xaf4202dc, 0x8f4202dc, 
+0x3e00008, 0x0, 0x27bdffd8, 0xafbf0024, 
+0xafb00020, 0x8f430024, 0x8f420020, 0x10620038, 
+0x0, 0x8f430020, 0x8f420024, 0x622023, 
+0x4810003, 0x0, 0x8f420040, 0x822021, 
+0x8f430030, 0x8f420024, 0x43102b, 0x14400005, 
+0x0, 0x8f430040, 0x8f420024, 0x10000005, 
+0x621023, 0x8f420030, 0x8f430024, 0x431023, 
+0x2442ffff, 0x406021, 0x8c102a, 0x54400001, 
+0x806021, 0x8f4a0024, 0x8f490040, 0x8f480024, 
+0x8f440180, 0x8f450184, 0x8f460024, 0x8f4b001c, 
+0x24070001, 0xafa70010, 0x84100, 0x1001821, 
+0x14c5021, 0x2529ffff, 0x1498024, 0xafb00014, 
+0x8f470014, 0x1021, 0x63100, 0xafa70018, 
+0xa32821, 0xa3382b, 0x822021, 0x872021, 
+0x8f420108, 0x1663021, 0x40f809, 0xc3900, 
+0x54400001, 0xaf500024, 0x8f430024, 0x8f420020, 
+0x14620018, 0x0, 0x8f420000, 0x10400007, 
+0x0, 0xaf80004c, 0x8f82004c, 0x1040fffd, 
+0x0, 0x10000005, 0x0, 0xaf800048, 
+0x8f820048, 0x1040fffd, 0x0, 0x8f820060, 
+0x2403ffef, 0x431024, 0xaf820060, 0x8f420000, 
+0x10400003, 0x0, 0x10000002, 0xaf80004c, 
+0xaf800048, 0x8fbf0024, 0x8fb00020, 0x3e00008, 
+0x27bd0028, 0x3e00008, 0x0, 0x27bdffc0, 
+0x32c20020, 0xafbf0038, 0xafb30034, 0xafb20030, 
+0xafb1002c, 0x10400004, 0xafb00028, 0x8f530028, 
+0x10000002, 0x0, 0x8f530020, 0x8f420030, 
+0x105300eb, 0x21100, 0x8f43001c, 0x628021, 
+0x8e040000, 0x8e050004, 0x96120008, 0x8f420090, 
+0x9611000a, 0x3246ffff, 0x46102a, 0x10400017, 
+0x0, 0x8f8200d8, 0x8f430098, 0x431023, 
+0x2442dcbe, 0xaf420090, 0x8f420090, 0x2842dcbf, 
+0x10400005, 0x0, 0x8f420090, 0x8f430144, 
+0x431021, 0xaf420090, 0x8f420090, 0x46102a, 
+0x10400006, 0x0, 0x8f420348, 0x24420001, 
+0xaf420348, 0x100000e1, 0x8f420348, 0x8f8200fc, 
+0x14400006, 0x0, 0x8f420344, 0x24420001, 
+0xaf420344, 0x100000d9, 0x8f420344, 0x934205c2, 
+0x1040000b, 0x32c20008, 0x10400008, 0x32220200, 
+0x10400006, 0x3c034000, 0x9602000e, 0xaf4300ac, 
+0x21400, 0x10000002, 0xaf4200b0, 0xaf4000ac, 
+0x32220004, 0x1040007f, 0x32220800, 0x10400003, 
+0x3247ffff, 0x10000002, 0x24020020, 0x24020004, 
+0xafa20010, 0x8f420030, 0xafa20014, 0x8f420010, 
+0x3c030002, 0x431025, 0xafa20018, 0x8f460098, 
+0x8f420108, 0x40f809, 0x0, 0x104000b7, 
+0x0, 0x8f42009c, 0x8f430094, 0x2421021, 
+0xaf42009c, 0xae03000c, 0x8f4200ac, 0x10400008, 
+0x3c034000, 0x8f420094, 0x431025, 0xafa20020, 
+0x8f42009c, 0x8f4300b0, 0x10000004, 0x431025, 
+0x8f420094, 0xafa20020, 0x8f42009c, 0xafa20024, 
+0x8f8200fc, 0x8fa30020, 0x8fa40024, 0xac430000, 
+0xac440004, 0x24420008, 0xaf8200f0, 0x8f42009c, 
+0x8f440270, 0x8f450274, 0x401821, 0x1021, 
+0xa32821, 0xa3302b, 0x822021, 0x862021, 
+0x32230060, 0x24020040, 0xaf440270, 0xaf450274, 
+0x10620017, 0x2c620041, 0x10400005, 0x24020020, 
+0x10620008, 0x24020001, 0x10000026, 0x0, 
+0x24020060, 0x10620019, 0x24020001, 0x10000021, 
+0x0, 0x8f420278, 0x8f43027c, 0x24630001, 
+0x2c640001, 0x441021, 0xaf420278, 0xaf43027c, 
+0x8f420278, 0x8f43027c, 0x10000016, 0x24020001, 
+0x8f420280, 0x8f430284, 0x24630001, 0x2c640001, 
+0x441021, 0xaf420280, 0xaf430284, 0x8f420280, 
+0x8f430284, 0x1000000b, 0x24020001, 0x8f420288, 
+0x8f43028c, 0x24630001, 0x2c640001, 0x441021, 
+0xaf420288, 0xaf43028c, 0x8f420288, 0x8f43028c, 
+0x24020001, 0xa34205c2, 0x8f420098, 0x3244ffff, 
+0x2406fff8, 0x8f45013c, 0x441021, 0x24420007, 
+0x461024, 0x24840007, 0xaf420094, 0x8f420090, 
+0x8f430094, 0x862024, 0x441023, 0x65182b, 
+0x14600005, 0xaf420090, 0x8f420094, 0x8f430144, 
+0x431023, 0xaf420094, 0x8f420094, 0x10000023, 
+0xaf40009c, 0x3247ffff, 0x50e00022, 0x32c20020, 
+0x14400002, 0x24020010, 0x24020002, 0xafa20010, 
+0x8f420030, 0xafa20014, 0x8f420010, 0xafa20018, 
+0x8f460098, 0x8f420108, 0x40f809, 0x0, 
+0x1040003a, 0x3245ffff, 0x8f420098, 0x8f430090, 
+0x8f46013c, 0x451021, 0xaf420098, 0x8f42009c, 
+0x8f440098, 0xa34005c2, 0x651823, 0xaf430090, 
+0x451021, 0x86202b, 0x14800005, 0xaf42009c, 
+0x8f420098, 0x8f430144, 0x431023, 0xaf420098, 
+0x32c20020, 0x10400005, 0x0, 0x8f420358, 
+0x2442ffff, 0xaf420358, 0x8f420358, 0x8f420030, 
+0x8f430040, 0x24420001, 0x2463ffff, 0x431024, 
+0xaf420030, 0x8f420030, 0x14530018, 0x0, 
+0x8f420000, 0x10400007, 0x0, 0xaf80004c, 
+0x8f82004c, 0x1040fffd, 0x0, 0x10000005, 
+0x0, 0xaf800048, 0x8f820048, 0x1040fffd, 
+0x0, 0x8f820060, 0x2403fff7, 0x431024, 
+0xaf820060, 0x8f420000, 0x10400003, 0x0, 
+0x10000002, 0xaf80004c, 0xaf800048, 0x8fbf0038, 
+0x8fb30034, 0x8fb20030, 0x8fb1002c, 0x8fb00028, 
+0x3e00008, 0x27bd0040, 0x3e00008, 0x0, 
+0x27bdffd0, 0x32c20020, 0xafbf002c, 0xafb20028, 
+0xafb10024, 0x10400004, 0xafb00020, 0x8f520028, 
+0x10000002, 0x0, 0x8f520020, 0x8f420030, 
+0x105200b5, 0x21100, 0x8f43001c, 0x628021, 
+0x8e040000, 0x8e050004, 0x96110008, 0x8f420090, 
+0x9607000a, 0x3226ffff, 0x46102a, 0x10400017, 
+0x0, 0x8f8200d8, 0x8f430098, 0x431023, 
+0x2442dc46, 0xaf420090, 0x8f420090, 0x2842dc47, 
+0x10400005, 0x0, 0x8f420090, 0x8f430144, 
+0x431021, 0xaf420090, 0x8f420090, 0x46102a, 
+0x10400006, 0x0, 0x8f420348, 0x24420001, 
+0xaf420348, 0x100000ab, 0x8f420348, 0x8f8600fc, 
+0x10c0000c, 0x0, 0x8f8200f4, 0x2403fff8, 
+0x431024, 0x461023, 0x218c3, 0x58600001, 
+0x24630100, 0x8f42008c, 0x43102b, 0x14400006, 
+0x712c2, 0x8f420344, 0x24420001, 0xaf420344, 
+0x10000098, 0x8f420344, 0x934305c2, 0x1060000f, 
+0x30460001, 0x8f420010, 0x34480400, 0x32c20008, 
+0x10400008, 0x30e20200, 0x10400006, 0x3c034000, 
+0x9602000e, 0xaf4300ac, 0x21400, 0x10000004, 
+0xaf4200b0, 0x10000002, 0xaf4000ac, 0x8f480010, 
+0x30e20004, 0x10400045, 0x3227ffff, 0x8f4900ac, 
+0x11200005, 0x30c200ff, 0x14400006, 0x24020040, 
+0x10000004, 0x24020008, 0x14400002, 0x24020020, 
+0x24020004, 0xafa20010, 0x8f430030, 0x11200004, 
+0xafa30014, 0x8f4200b0, 0x621025, 0xafa20014, 
+0x3c020002, 0x1021025, 0xafa20018, 0x8f460098, 
+0x8f420108, 0x40f809, 0x0, 0x10400069, 
+0x3224ffff, 0x8f42008c, 0x8f430094, 0x24420001, 
+0xaf42008c, 0x24020001, 0xae03000c, 0xa34205c2, 
+0x8f420098, 0x2406fff8, 0x8f45013c, 0x441021, 
+0x24420007, 0x461024, 0x24840007, 0xaf420094, 
+0x8f420090, 0x8f430094, 0x862024, 0x441023, 
+0x65182b, 0x14600005, 0xaf420090, 0x8f420094, 
+0x8f430144, 0x431023, 0xaf420094, 0x8f430094, 
+0x8f420140, 0x43102b, 0x10400009, 0x0, 
+0x8f43013c, 0x8f440094, 0x8f420090, 0x8f450138, 
+0x641823, 0x431023, 0xaf420090, 0xaf450094, 
+0x8f420094, 0x1000001f, 0xaf420098, 0x10e0001d, 
+0x30c200ff, 0x14400002, 0x24020010, 0x24020002, 
+0xafa20010, 0x8f420030, 0xafa80018, 0xafa20014, 
+0x8f460098, 0x8f420108, 0x40f809, 0x0, 
+0x10400030, 0x3225ffff, 0x8f420098, 0x8f44013c, 
+0x451021, 0xaf420098, 0x8f420090, 0x8f430098, 
+0xa34005c2, 0x451023, 0x64182b, 0x14600005, 
+0xaf420090, 0x8f420098, 0x8f430144, 0x431023, 
+0xaf420098, 0x8f420030, 0x8f430040, 0x24420001, 
+0x2463ffff, 0x431024, 0xaf420030, 0x8f420030, 
+0x14520018, 0x0, 0x8f420000, 0x10400007, 
+0x0, 0xaf80004c, 0x8f82004c, 0x1040fffd, 
+0x0, 0x10000005, 0x0, 0xaf800048, 
+0x8f820048, 0x1040fffd, 0x0, 0x8f820060, 
+0x2403fff7, 0x431024, 0xaf820060, 0x8f420000, 
+0x10400003, 0x0, 0x10000002, 0xaf80004c, 
+0xaf800048, 0x8fbf002c, 0x8fb20028, 0x8fb10024, 
+0x8fb00020, 0x3e00008, 0x27bd0030, 0x3e00008, 
+0x0, 0x27bdffd8, 0x3c020001, 0x34422ec0, 
+0xafbf0020, 0x8f4300f0, 0x8f840108, 0x2e21021, 
+0x54620004, 0x24620008, 0x3c020001, 0x34422cc0, 
+0x2e21021, 0x401821, 0xaf4300f0, 0xac600000, 
+0x8f4200ec, 0x8c660004, 0x14620004, 0x3c020001, 
+0x24820020, 0x1000000f, 0xaf820108, 0x8f4300f0, 
+0x34422ec0, 0x2e21021, 0x54620004, 0x24620008, 
+0x3c020001, 0x34422cc0, 0x2e21021, 0x401821, 
+0x8c620004, 0x21140, 0x821021, 0xaf820108, 
+0xac600000, 0x8c850018, 0x30a20036, 0x1040006c, 
+0x30a20001, 0x8c82001c, 0x8f430040, 0x8f440034, 
+0x24420001, 0x2463ffff, 0x431024, 0x862021, 
+0xaf42002c, 0x30a20030, 0x14400006, 0xaf440034, 
+0x8f420034, 0x8c03023c, 0x43102b, 0x144000b4, 
+0x0, 0x32c20010, 0x10400028, 0x24070008, 
+0x8f440170, 0x8f450174, 0x8f43002c, 0x8f48000c, 
+0x8f860120, 0x24020080, 0xafa20010, 0xafa30014, 
+0xafa80018, 0x8f42010c, 0x40f809, 0x24c6001c, 
+0x14400011, 0x24020001, 0x3c010001, 0x370821, 
+0xa02240f1, 0x8f820124, 0xafa20010, 0x8f820128, 
+0x3c040001, 0x248467c4, 0xafa20014, 0x8f46002c, 
+0x8f870120, 0x3c050009, 0xc002b3b, 0x34a51100, 
+0x10000036, 0x0, 0x8f420300, 0x8f43002c, 
+0x24420001, 0xaf420300, 0x8f420300, 0x24020001, 
+0xa34205c1, 0x10000026, 0xaf430038, 0x8f440170, 
+0x8f450174, 0x8f43002c, 0x8f48000c, 0x8f860120, 
+0x24020020, 0xafa20010, 0xafa30014, 0xafa80018, 
+0x8f42010c, 0x40f809, 0x24c6001c, 0x14400011, 
+0x24020001, 0x3c010001, 0x370821, 0xa02240f0, 
+0x8f820124, 0xafa20010, 0x8f820128, 0x3c040001, 
+0x248467b8, 0xafa20014, 0x8f46002c, 0x8f870120, 
+0x3c050009, 0xc002b3b, 0x34a50900, 0x1000000f, 
+0x0, 0x8f420300, 0x24420001, 0xaf420300, 
+0x8f420300, 0x8f42002c, 0xa34005c1, 0xaf420038, 
+0x3c010001, 0x370821, 0xa02040f1, 0x3c010001, 
+0x370821, 0xa02040f0, 0xaf400034, 0x8f420314, 
+0x24420001, 0xaf420314, 0x10000059, 0x8f420314, 
+0x10400022, 0x30a27000, 0x8c85001c, 0x8f420028, 
+0xa22023, 0x4810003, 0x0, 0x8f420040, 
+0x822021, 0x8f420358, 0x8f430000, 0xaf450028, 
+0x441021, 0x10600007, 0xaf420358, 0xaf80004c, 
+0x8f82004c, 0x1040fffd, 0x0, 0x10000005, 
+0x0, 0xaf800048, 0x8f820048, 0x1040fffd, 
+0x0, 0x8f820060, 0x34420008, 0xaf820060, 
+0x8f420000, 0x10400003, 0x0, 0x10000038, 
+0xaf80004c, 0x10000036, 0xaf800048, 0x1040002f, 
+0x30a21000, 0x1040000c, 0x30a24000, 0x8c83001c, 
+0x8f420050, 0x622023, 0x4820001, 0x24840200, 
+0x8f42035c, 0x441021, 0xaf42035c, 0x8f420368, 
+0x1000001a, 0xaf430050, 0x1040000c, 0x32c28000, 
+0x8c83001c, 0x8f420070, 0x622023, 0x4820001, 
+0x24840400, 0x8f420364, 0x441021, 0xaf420364, 
+0x8f420368, 0x1000000d, 0xaf430070, 0x1040000e, 
+0x3c020800, 0x8c83001c, 0x8f420060, 0x622023, 
+0x4820001, 0x24840100, 0x8f420360, 0x441021, 
+0xaf420360, 0x8f420368, 0xaf430060, 0x441021, 
+0xaf420368, 0x3c020800, 0x2c21024, 0x50400008, 
+0x36940040, 0x10000006, 0x0, 0x30a20100, 
+0x10400003, 0x0, 0xc002bd8, 0x0, 
+0x8fbf0020, 0x3e00008, 0x27bd0028, 0x3e00008, 
+0x0, 0x27bdffa8, 0xafbf0050, 0xafbe004c, 
+0xafb50048, 0xafb30044, 0xafb20040, 0xafb1003c, 
+0xafb00038, 0x8f910108, 0x26220020, 0xaf820108, 
+0x8e320018, 0xa821, 0x32420024, 0x104001ba, 
+0xf021, 0x8e26001c, 0x8f43001c, 0x61100, 
+0x621821, 0x8c70000c, 0x9604000c, 0x962d0016, 
+0x9473000a, 0x2c8305dd, 0x38828870, 0x2c420001, 
+0x621825, 0x10600015, 0x2821, 0x32c20040, 
+0x10400015, 0x24020800, 0x96030014, 0x14620012, 
+0x3402aaaa, 0x9603000e, 0x14620007, 0x2021, 
+0x96030010, 0x24020300, 0x14620004, 0x801021, 
+0x96020012, 0x2c440001, 0x801021, 0x54400006, 
+0x24050016, 0x10000004, 0x0, 0x24020800, 
+0x50820001, 0x2405000e, 0x934205c3, 0x14400008, 
+0x5821, 0x240b0001, 0x32620180, 0xaf4500a8, 
+0xaf5000a0, 0x10400002, 0xaf4600a4, 0xa34b05c3, 
+0x10a00085, 0x2054021, 0x91020000, 0x3821, 
+0x3042000f, 0x25080, 0x32c20002, 0x10400012, 
+0x10a1821, 0x32620002, 0x10400010, 0x32c20001, 
+0x1002021, 0x94820000, 0x24840002, 0xe23821, 
+0x83102b, 0x1440fffb, 0x30e2ffff, 0x71c02, 
+0x623821, 0x71c02, 0x30e2ffff, 0x623821, 
+0x71027, 0xa502000a, 0x32c20001, 0x1040006a, 
+0x32620001, 0x10400068, 0x0, 0x8f4200a8, 
+0x10400065, 0x0, 0x8f4200a0, 0x8f4300a8, 
+0x431021, 0x904c0009, 0x318900ff, 0x39230006, 
+0x3182b, 0x39220011, 0x2102b, 0x621824, 
+0x1060000c, 0x3c050006, 0x8f4200a4, 0x3c040001, 
+0x248467d4, 0xafa20010, 0x8f4200a0, 0x34a54600, 
+0x1203821, 0xc002b3b, 0xafa20014, 0x1000004e, 
+0x0, 0x32c20004, 0x14400013, 0x2821, 
+0x316200ff, 0x14400004, 0x0, 0x95020002, 
+0x1000000d, 0x4a2823, 0x9505000c, 0x9502000e, 
+0x95030010, 0xa22821, 0xa32821, 0x95030012, 
+0x91040009, 0x95020002, 0xa32821, 0xa42821, 
+0x4a1023, 0xa22821, 0x2002021, 0x94820000, 
+0x24840002, 0xe23821, 0x88102b, 0x1440fffb, 
+0x71c02, 0x30e2ffff, 0x623821, 0x71c02, 
+0x30e2ffff, 0x623821, 0x1a52821, 0x51c02, 
+0x30a2ffff, 0x622821, 0x51c02, 0x30a2ffff, 
+0x622821, 0xa72823, 0x51402, 0xa22821, 
+0x30a5ffff, 0x50a00001, 0x3405ffff, 0x316200ff, 
+0x14400008, 0x318300ff, 0x8f4300a0, 0x8f4200a8, 
+0x624021, 0x91020000, 0x3042000f, 0x25080, 
+0x318300ff, 0x24020006, 0x14620003, 0x10a1021, 
+0x10000002, 0x24440010, 0x24440006, 0x316200ff, 
+0x14400006, 0x0, 0x94820000, 0xa22821, 
+0x51c02, 0x30a2ffff, 0x622821, 0x934205c3, 
+0x10400003, 0x32620100, 0x50400003, 0xa4850000, 
+0x52827, 0xa4850000, 0x9622000e, 0x8f43009c, 
+0x621821, 0x32a200ff, 0x10400007, 0xaf43009c, 
+0x3c024000, 0x2021025, 0xafa20020, 0x8f42009c, 
+0x10000003, 0x5e1025, 0xafb00020, 0x8f42009c, 
+0xafa20024, 0x32620080, 0x10400010, 0x32620100, 
+0x8f4200b4, 0x24430001, 0x210c0, 0x571021, 
+0xaf4300b4, 0x8fa30020, 0x8fa40024, 0x3c010001, 
+0x220821, 0xac2338e8, 0x3c010001, 0x220821, 
+0xac2438ec, 0x100000a5, 0x32c20020, 0x10400064, 
+0x0, 0x8f4200b4, 0x24430001, 0x210c0, 
+0x571021, 0xaf4300b4, 0x8fa30020, 0x8fa40024, 
+0x3c010001, 0x220821, 0xac2338e8, 0x3c010001, 
+0x220821, 0xac2438ec, 0x8f4200b4, 0x10400051, 
+0x3821, 0x3c090001, 0x352938e8, 0x3c08001f, 
+0x3508ffff, 0x240bffff, 0x340affff, 0x710c0, 
+0x571021, 0x491021, 0x8c430000, 0x8c440004, 
+0xafa30028, 0xafa4002c, 0x8f8200fc, 0x8fa30028, 
+0x8fa4002c, 0xac430000, 0xac440004, 0x24420008, 
+0xaf8200f0, 0x8f42008c, 0x2442ffff, 0xaf42008c, 
+0x97a2002e, 0x8f440270, 0x8f450274, 0x401821, 
+0x1021, 0xa32821, 0xa3302b, 0x822021, 
+0x862021, 0xaf440270, 0xaf450274, 0x8fa20028, 
+0x481024, 0x90430000, 0x30630001, 0x1460000b, 
+0x402021, 0x8f420278, 0x8f43027c, 0x24630001, 
+0x2c640001, 0x441021, 0xaf420278, 0xaf43027c, 
+0x8f420278, 0x1000001a, 0x8f43027c, 0x8c820000, 
+0x144b000e, 0x0, 0x94820004, 0x144a000b, 
+0x0, 0x8f420288, 0x8f43028c, 0x24630001, 
+0x2c640001, 0x441021, 0xaf420288, 0xaf43028c, 
+0x8f420288, 0x1000000a, 0x8f43028c, 0x8f420280, 
+0x8f430284, 0x24630001, 0x2c640001, 0x441021, 
+0xaf420280, 0xaf430284, 0x8f420280, 0x8f430284, 
+0x8f4200b4, 0x24e70001, 0xe2102b, 0x1440ffb8, 
+0x710c0, 0xa34005c3, 0x1000003f, 0xaf4000b4, 
+0x8f8200fc, 0x8fa30020, 0x8fa40024, 0xac430000, 
+0xac440004, 0x24420008, 0xaf8200f0, 0x8f42009c, 
+0x8f46008c, 0x8f440270, 0x8f450274, 0x401821, 
+0x1021, 0x24c6ffff, 0xaf46008c, 0xa32821, 
+0xa3302b, 0x822021, 0x862021, 0xaf440270, 
+0xaf450274, 0x92020000, 0x30420001, 0x1440000c, 
+0x2402ffff, 0x8f420278, 0x8f43027c, 0x24630001, 
+0x2c640001, 0x441021, 0xaf420278, 0xaf43027c, 
+0x8f420278, 0x8f43027c, 0x1000001c, 0x32c20020, 
+0x8e030000, 0x1462000f, 0x3402ffff, 0x96030004, 
+0x1462000c, 0x0, 0x8f420288, 0x8f43028c, 
+0x24630001, 0x2c640001, 0x441021, 0xaf420288, 
+0xaf43028c, 0x8f420288, 0x8f43028c, 0x1000000b, 
+0x32c20020, 0x8f420280, 0x8f430284, 0x24630001, 
+0x2c640001, 0x441021, 0xaf420280, 0xaf430284, 
+0x8f420280, 0x8f430284, 0x32c20020, 0x10400005, 
+0xaf40009c, 0x8f420358, 0x2442ffff, 0xaf420358, 
+0x8f420358, 0x8e22001c, 0x8f430040, 0x24420001, 
+0x2463ffff, 0x431024, 0xaf42002c, 0x32420060, 
+0x14400008, 0x32c20010, 0x8f420034, 0x24420001, 
+0xaf420034, 0x8c03023c, 0x43102b, 0x14400102, 
+0x32c20010, 0x10400018, 0x24070008, 0x8f440170, 
+0x8f450174, 0x8f43002c, 0x8f48000c, 0x8f860120, 
+0x24020080, 0xafa20010, 0xafa30014, 0xafa80018, 
+0x8f42010c, 0x40f809, 0x24c6001c, 0x10400047, 
+0x24020001, 0x8f420300, 0x8f43002c, 0x24420001, 
+0xaf420300, 0x8f420300, 0x24020001, 0xa34205c1, 
+0x1000007c, 0xaf430038, 0x8f440170, 0x8f450174, 
+0x8f43002c, 0x8f48000c, 0x8f860120, 0x24020020, 
+0xafa20010, 0xafa30014, 0xafa80018, 0x8f42010c, 
+0x40f809, 0x24c6001c, 0x10400057, 0x24020001, 
+0x10000065, 0x0, 0x32420012, 0x10400075, 
+0x32420001, 0x9622000e, 0x8f43009c, 0x621821, 
+0x32c20020, 0x10400005, 0xaf43009c, 0x8f420358, 
+0x2442ffff, 0xaf420358, 0x8f420358, 0x8e22001c, 
+0x8f430040, 0x24420001, 0x2463ffff, 0x431024, 
+0xaf42002c, 0x32420010, 0x14400008, 0x32c20010, 
+0x8f420034, 0x24420001, 0xaf420034, 0x8c03023c, 
+0x43102b, 0x144000bc, 0x32c20010, 0x10400028, 
+0x24070008, 0x8f440170, 0x8f450174, 0x8f43002c, 
+0x8f48000c, 0x8f860120, 0x24020080, 0xafa20010, 
+0xafa30014, 0xafa80018, 0x8f42010c, 0x40f809, 
+0x24c6001c, 0x14400011, 0x24020001, 0x3c010001, 
+0x370821, 0xa02240f1, 0x8f820124, 0xafa20010, 
+0x8f820128, 0x3c040001, 0x248467c4, 0xafa20014, 
+0x8f46002c, 0x8f870120, 0x3c050009, 0xc002b3b, 
+0x34a51100, 0x10000036, 0x0, 0x8f420300, 
+0x8f43002c, 0x24420001, 0xaf420300, 0x8f420300, 
+0x24020001, 0xa34205c1, 0x10000026, 0xaf430038, 
+0x8f440170, 0x8f450174, 0x8f43002c, 0x8f48000c, 
+0x8f860120, 0x24020020, 0xafa20010, 0xafa30014, 
+0xafa80018, 0x8f42010c, 0x40f809, 0x24c6001c, 
+0x14400011, 0x24020001, 0x3c010001, 0x370821, 
+0xa02240f0, 0x8f820124, 0xafa20010, 0x8f820128, 
+0x3c040001, 0x248467b8, 0xafa20014, 0x8f46002c, 
+0x8f870120, 0x3c050009, 0xc002b3b, 0x34a50900, 
+0x1000000f, 0x0, 0x8f420300, 0x24420001, 
+0xaf420300, 0x8f420300, 0x8f42002c, 0xa34005c1, 
+0xaf420038, 0x3c010001, 0x370821, 0xa02040f1, 
+0x3c010001, 0x370821, 0xa02040f0, 0xaf400034, 
+0x8f420314, 0x24420001, 0xaf420314, 0x10000062, 
+0x8f420314, 0x10400022, 0x32427000, 0x8e25001c, 
+0x8f420028, 0xa22023, 0x4810003, 0x0, 
+0x8f420040, 0x822021, 0x8f420358, 0x8f430000, 
+0xaf450028, 0x441021, 0x10600007, 0xaf420358, 
+0xaf80004c, 0x8f82004c, 0x1040fffd, 0x0, 
+0x10000005, 0x0, 0xaf800048, 0x8f820048, 
+0x1040fffd, 0x0, 0x8f820060, 0x34420008, 
+0xaf820060, 0x8f420000, 0x10400003, 0x0, 
+0x10000041, 0xaf80004c, 0x1000003f, 0xaf800048, 
+0x1040002f, 0x32421000, 0x1040000c, 0x32424000, 
+0x8e23001c, 0x8f420050, 0x622023, 0x4820001, 
+0x24840200, 0x8f42035c, 0x441021, 0xaf42035c, 
+0x8f420368, 0x1000001a, 0xaf430050, 0x1040000c, 
+0x32c28000, 0x8e23001c, 0x8f420070, 0x622023, 
+0x4820001, 0x24840400, 0x8f420364, 0x441021, 
+0xaf420364, 0x8f420368, 0x1000000d, 0xaf430070, 
+0x1040000e, 0x3c020800, 0x8e23001c, 0x8f420060, 
+0x622023, 0x4820001, 0x24840100, 0x8f420360, 
+0x441021, 0xaf420360, 0x8f420368, 0xaf430060, 
+0x441021, 0xaf420368, 0x3c020800, 0x2c21024, 
+0x50400011, 0x36940040, 0x1000000f, 0x0, 
+0x32420048, 0x10400007, 0x24150001, 0x8e22001c, 
+0x3c03ffff, 0x43f024, 0x3042ffff, 0x1000fd75, 
+0xae22001c, 0x32420100, 0x10400003, 0x0, 
+0xc002bd8, 0x0, 0x8fbf0050, 0x8fbe004c, 
+0x8fb50048, 0x8fb30044, 0x8fb20040, 0x8fb1003c, 
+0x8fb00038, 0x3e00008, 0x27bd0058, 0x3e00008, 
+0x0, 0x0, 0x0, 0x8f8300e4, 
+0x8f8200e0, 0x2404fff8, 0x441024, 0x621026, 
+0x2102b, 0x21023, 0x3e00008, 0x621024, 
+0x3e00008, 0x0, 0x27bdffe0, 0xafbf001c, 
+0xafb00018, 0x8f8600c4, 0x8f8400e0, 0x8f8500e4, 
+0x2402fff8, 0x821824, 0x10a30009, 0x27623ff8, 
+0x14a20002, 0x24a20008, 0x27623000, 0x408021, 
+0x16030005, 0x30820004, 0x10400004, 0xc02021, 
+0x10000022, 0x1021, 0x8e040000, 0x8f42011c, 
+0x14a20003, 0x0, 0x8f420120, 0xaf420114, 
+0x8ca30000, 0x8f420148, 0x831823, 0x43102b, 
+0x10400003, 0x0, 0x8f420148, 0x621821, 
+0x94a20006, 0x24420050, 0x62102b, 0x1440000f, 
+0xa01021, 0xafa40010, 0xafa30014, 0x8ca60000, 
+0x8ca70004, 0x3c040001, 0xc002b3b, 0x24846894, 
+0x8f42020c, 0x24420001, 0xaf42020c, 0x8f42020c, 
+0x1021, 0xaf9000e8, 0xaf9000e4, 0x8fbf001c, 
+0x8fb00018, 0x3e00008, 0x27bd0020, 0x3e00008, 
+0x0, 0x8f8400e0, 0x8f8800c4, 0x8f8300e8, 
+0x2402fff8, 0x823824, 0xe32023, 0x2c821000, 
+0x50400001, 0x24841000, 0x420c2, 0x801821, 
+0x8f440258, 0x8f45025c, 0x1021, 0xa32821, 
+0xa3302b, 0x822021, 0x862021, 0xaf440258, 
+0xaf45025c, 0x8f8300c8, 0x8f420148, 0x1032023, 
+0x82102b, 0x14400004, 0x801821, 0x8f420148, 
+0x822021, 0x801821, 0x8f440250, 0x8f450254, 
+0x1021, 0xa32821, 0xa3302b, 0x822021, 
+0x862021, 0xaf440250, 0xaf450254, 0xaf8800c8, 
+0xaf8700e4, 0xaf8700e8, 0x3e00008, 0x0, 
+0x27bdff30, 0x240a0001, 0xafbf00c8, 0xafbe00c4, 
+0xafb500c0, 0xafb300bc, 0xafb200b8, 0xafb100b4, 
+0xafb000b0, 0xa3a00097, 0xafa00044, 0xafaa005c, 
+0x934205c4, 0xa7a0008e, 0x1040000a, 0xa7a00086, 
+0x8f4b00c4, 0xafab0064, 0x8f4a00c0, 0xafaa006c, 
+0x8f4b00cc, 0xafab0074, 0x8f4a00c8, 0x10000129, 
+0xafaa007c, 0x8f420114, 0x40f809, 0x0, 
+0x403021, 0x10c0034f, 0x0, 0x8cc20000, 
+0x8cc30004, 0xafa20020, 0xafa30024, 0x8fab0024, 
+0x8faa0020, 0x3162ffff, 0x2442fffc, 0xafa2006c, 
+0x3c020006, 0x2c21024, 0xafab007c, 0x14400015, 
+0xafaa0064, 0x91420000, 0x30420001, 0x10400011, 
+0x2402ffff, 0x8d430000, 0x14620004, 0x3402ffff, 
+0x95430004, 0x1062000b, 0x0, 0xc0024bb, 
+0x8fa40064, 0x304200ff, 0x14400006, 0x0, 
+0x8f420118, 0x40f809, 0x0, 0x1000032d, 
+0x0, 0x8fa20024, 0x3c03ffbf, 0x3463ffff, 
+0x431024, 0x3c03ffff, 0x431824, 0x14600003, 
+0xafa20024, 0x10000040, 0x1821, 0x3c020080, 
+0x621024, 0x10400007, 0x0, 0x8f42038c, 
+0x24420001, 0xaf42038c, 0x8f42038c, 0x10000036, 
+0x24030001, 0x8f420210, 0x24420001, 0xaf420210, 
+0x8f420210, 0x3c020001, 0x621024, 0x10400006, 
+0x3c020002, 0x8f4201c4, 0x24420001, 0xaf4201c4, 
+0x8f4201c4, 0x3c020002, 0x621024, 0x10400006, 
+0x3c020004, 0x8f42037c, 0x24420001, 0xaf42037c, 
+0x8f42037c, 0x3c020004, 0x621024, 0x10400006, 
+0x3c020008, 0x8f420380, 0x24420001, 0xaf420380, 
+0x8f420380, 0x3c020008, 0x621024, 0x10400006, 
+0x3c020010, 0x8f420384, 0x24420001, 0xaf420384, 
+0x8f420384, 0x3c020010, 0x621024, 0x10400006, 
+0x3c020020, 0x8f4201c0, 0x24420001, 0xaf4201c0, 
+0x8f4201c0, 0x3c020020, 0x621024, 0x10400006, 
+0x24030001, 0x8f420388, 0x24420001, 0xaf420388, 
+0x8f420388, 0x24030001, 0x8c020260, 0x8fab006c, 
+0x4b102b, 0x10400014, 0x307000ff, 0x8f4201e8, 
+0x24420001, 0xaf4201e8, 0x8f4201e8, 0x8faa007c, 
+0x8f8200e0, 0x354a0100, 0xafaa007c, 0xafa20010, 
+0x8f8200e4, 0x24100001, 0x3c040001, 0x248468a0, 
+0xafa20014, 0x8fa60020, 0x8fa70024, 0x3c050007, 
+0xc002b3b, 0x34a50800, 0x12000010, 0x3c020080, 
+0x2c21024, 0x1440000e, 0x32c20400, 0x8fab007c, 
+0x3c020080, 0x34420100, 0x1621024, 0x10400005, 
+0x0, 0x8f42020c, 0x24420001, 0xaf42020c, 
+0x8f42020c, 0x100002b0, 0x8fa3006c, 0x32c20400, 
+0x10400015, 0x34028100, 0x8faa0064, 0x9543000c, 
+0x14620012, 0x3c020100, 0x240b0200, 0xa7ab008e, 
+0x9542000e, 0x8d430008, 0x8d440004, 0x8d450000, 
+0x8faa006c, 0x8fab0064, 0x254afffc, 0xafaa006c, 
+0xa7a20086, 0xad63000c, 0xad640008, 0xad650004, 
+0x256b0004, 0xafab0064, 0x3c020100, 0x2c21024, 
+0x10400004, 0x0, 0x8faa006c, 0x254a0004, 
+0xafaa006c, 0x8f4200bc, 0x5040000a, 0xafa00074, 
+0x8fab006c, 0x4b102b, 0x50400006, 0xafa00074, 
+0x8f4200bc, 0x1621023, 0xafa20074, 0x8f4a00bc, 
+0xafaa006c, 0x8f420080, 0x8fab006c, 0x4b102b, 
+0x10400056, 0x32c28000, 0x1040005e, 0x240a0003, 
+0x32c21000, 0x1040005b, 0xafaa005c, 0x10000058, 
+0x240b0004, 0x8f420350, 0x2403ffbf, 0x283a024, 
+0x24420001, 0xaf420350, 0x1000024f, 0x8f420350, 
+0x2c2b025, 0x2402ffbf, 0x282a024, 0x8f830128, 
+0x3c040001, 0x248468d0, 0x26620001, 0xafa20014, 
+0xafa30010, 0x8f860120, 0x8f870124, 0x3c050007, 
+0xc002b3b, 0x34a52250, 0x1000023f, 0x0, 
+0x2c2b025, 0x2402ffbf, 0x282a024, 0x8f830128, 
+0x3c040001, 0x248468d0, 0x24020002, 0xafa20014, 
+0xafa30010, 0x8f860120, 0x8f870124, 0x3c050007, 
+0xc002b3b, 0x34a52450, 0x1000022f, 0x0, 
+0x8ea20000, 0x8ea30004, 0x3c040001, 0x248468e8, 
+0xafb00010, 0xafbe0014, 0x8ea70018, 0x34a52800, 
+0xc002b3b, 0x603021, 0x10000223, 0x0, 
+0xa6b1000a, 0x8f820124, 0x3c040001, 0x248468f0, 
+0xafbe0014, 0xafa20010, 0x8f460044, 0x8f870120, 
+0x3c050007, 0xc002b3b, 0x34a53000, 0x10000216, 
+0x0, 0xa6b1000a, 0xa6b2000e, 0x8f820124, 
+0x3c040001, 0x248468fc, 0xafbe0014, 0xafa20010, 
+0x8f460044, 0x8f870120, 0x3c050007, 0xc002b3b, 
+0x34a53200, 0x10000208, 0x0, 0x8f420084, 
+0x8faa006c, 0x4a102b, 0x14400007, 0x3c020001, 
+0x2c21024, 0x10400004, 0x0, 0x240b0002, 
+0xafab005c, 0x8faa006c, 0x1140021b, 0x27ab0020, 
+0xafab00a4, 0x3c0a001f, 0x354affff, 0xafaa009c, 
+0x8fab005c, 0x240a0001, 0x556a0021, 0x240a0002, 
+0x8f430054, 0x8f420050, 0x1062000b, 0x274b0054, 
+0x8f5e0054, 0x3403ecc0, 0xafab004c, 0x27c20001, 
+0x304201ff, 0xafa20054, 0x1e1140, 0x431021, 
+0x1000006b, 0x2e2a821, 0x8f420044, 0x8faa006c, 
+0x3c040001, 0x248468ac, 0xafaa0014, 0xafa20010, 
+0x8f460054, 0x8f470050, 0x3c050007, 0xc002b3b, 
+0x34a51300, 0x8f430350, 0x2402ffbf, 0x282a024, 
+0x24630001, 0xaf430350, 0x100001d3, 0x8f420350, 
+0x156a001d, 0x0, 0x8f430074, 0x8f420070, 
+0x1062000a, 0x274b0074, 0x8f5e0074, 0xafab004c, 
+0x27c20001, 0x304203ff, 0xafa20054, 0x1e1140, 
+0x24426cc0, 0x1000004a, 0x2e2a821, 0x8f420044, 
+0x8faa006c, 0x3c040001, 0x248468b8, 0x3c050007, 
+0xafaa0014, 0xafa20010, 0x8f460074, 0x8f470070, 
+0x34a51500, 0x240b0001, 0xc002b3b, 0xafab005c, 
+0x1000ffc3, 0x0, 0x8f430064, 0x8f420060, 
+0x1062001a, 0x274a0064, 0x8f5e0064, 0x8fab005c, 
+0xafaa004c, 0x27c20001, 0x304200ff, 0xafa20054, 
+0x24020004, 0x1562000e, 0x1e1140, 0x1e1180, 
+0x24420cc0, 0x2e21021, 0xafa20044, 0x9442002a, 
+0x8faa0044, 0x8fab006c, 0x4b102b, 0x10400024, 
+0x25550020, 0x240a0001, 0x10000021, 0xa3aa0097, 
+0x24424cc0, 0x1000001e, 0x2e2a821, 0x8f420044, 
+0x8fab006c, 0x3c040001, 0x248468c4, 0xafab0014, 
+0xafa20010, 0x8f460064, 0x8f470060, 0x3c050007, 
+0xc002b3b, 0x34a51800, 0x3c020008, 0x2c21024, 
+0x1440ff34, 0x0, 0x8f420370, 0x240a0001, 
+0xafaa005c, 0x24420001, 0xaf420370, 0x1000ff90, 
+0x8f420370, 0x27a30036, 0x131040, 0x621821, 
+0x94620000, 0x441021, 0x10000020, 0xa4620000, 
+0x8fab0064, 0xaeab0018, 0x93a20097, 0x10400072, 
+0x9821, 0x8faa0044, 0x8fa4006c, 0x8fa300a4, 
+0x25420020, 0xafa20028, 0x25420008, 0xafa20030, 
+0x25420010, 0xafaa002c, 0xafa20034, 0x9542002a, 
+0xa7a20038, 0x95420018, 0xa7a2003a, 0x9542001a, 
+0xa7a2003c, 0x9542001c, 0xa7a2003e, 0x94620018, 
+0x24630002, 0x822023, 0x1880ffde, 0x26730001, 
+0x2e620004, 0x1440fff9, 0x0, 0x8f4200fc, 
+0x26650001, 0xa2102a, 0x1440002b, 0x24030001, 
+0x8f83012c, 0x10600023, 0x0, 0x8f820124, 
+0x431023, 0x22143, 0x58800001, 0x24840040, 
+0x8f820128, 0x431023, 0x21943, 0x58600001, 
+0x24630040, 0x64102a, 0x54400001, 0x602021, 
+0xaf4400fc, 0x8f4200fc, 0xa2102a, 0x10400011, 
+0x24030001, 0x10000015, 0x306200ff, 0x8fab0064, 
+0x96070018, 0xafab0010, 0x8e220008, 0x3c040001, 
+0x248468dc, 0x8c430004, 0x8c420000, 0x34a52400, 
+0x2403021, 0xc002b3b, 0xafa30014, 0x1000002b, 
+0x0, 0x8f420334, 0x1821, 0x24420001, 
+0xaf420334, 0x8f420334, 0x306200ff, 0x5040fedc, 
+0x3c020800, 0x12600021, 0x9021, 0x8fb100a4, 
+0x2208021, 0x8e220008, 0x96070018, 0x8fa60064, 
+0x8c440000, 0x8c450004, 0x240a0001, 0xafaa0010, 
+0xafbe0014, 0x8f420008, 0xafa20018, 0x8f42010c, 
+0x40f809, 0x0, 0x1040ffd8, 0x3c050007, 
+0x96020018, 0x8fab0064, 0x8faa009c, 0x1625821, 
+0x14b102b, 0x10400004, 0xafab0064, 0x8f420148, 
+0x1625823, 0xafab0064, 0x26100002, 0x26520001, 
+0x253102b, 0x1440ffe3, 0x26310004, 0x8fb0006c, 
+0x10000036, 0x97b10038, 0x8f4200fc, 0x24050002, 
+0xa2102a, 0x1440001b, 0x24030001, 0x8f83012c, 
+0x10600013, 0x0, 0x8f820124, 0x431023, 
+0x22143, 0x58800001, 0x24840040, 0x8f820128, 
+0x431023, 0x21943, 0x58600001, 0x24630040, 
+0x64102a, 0x54400001, 0x602021, 0xaf4400fc, 
+0x8f4200fc, 0xa2102a, 0x14400006, 0x24030001, 
+0x8f420334, 0x1821, 0x24420001, 0xaf420334, 
+0x8f420334, 0x306200ff, 0x1040fea5, 0x3c020800, 
+0x96b1000a, 0x8fb0006c, 0x3223ffff, 0x70102b, 
+0x54400001, 0x608021, 0x8ea40000, 0x8ea50004, 
+0x240b0001, 0xafab0010, 0xafbe0014, 0x8f420008, 
+0x8fa60064, 0xafa20018, 0x8f42010c, 0x40f809, 
+0x2003821, 0x1040fea2, 0x3c050007, 0x96a3000e, 
+0x97aa008e, 0x11400007, 0x609021, 0x934205c4, 
+0x14400004, 0x0, 0x97ab0086, 0x6a1825, 
+0xa6ab0016, 0x8faa007c, 0x3c02ffff, 0x1421024, 
+0x10400003, 0xa1402, 0x34630400, 0xa6a20014, 
+0x8fab006c, 0x560b0072, 0xa6a3000e, 0x34620004, 
+0xa6a2000e, 0x8faa0074, 0x16a1021, 0xa6a2000a, 
+0x8f430044, 0x8f4401a0, 0x8f4501a4, 0x34028000, 
+0xafa20010, 0x8f420044, 0x2a03021, 0x24070020, 
+0xafa20014, 0x8f42000c, 0x31940, 0x604821, 
+0xafa20018, 0x8f42010c, 0x4021, 0xa92821, 
+0xa9182b, 0x882021, 0x40f809, 0x832021, 
+0x5040fe7f, 0xa6b2000e, 0x8f420368, 0xafa0006c, 
+0xa34005c4, 0x2442ffff, 0xaf420368, 0x8fab005c, 
+0x240a0001, 0x8f420368, 0x156a0006, 0x240a0002, 
+0x8f42035c, 0x2442ffff, 0xaf42035c, 0x1000000c, 
+0x8f42035c, 0x156a0006, 0x0, 0x8f420364, 
+0x2442ffff, 0xaf420364, 0x10000005, 0x8f420364, 
+0x8f420360, 0x2442ffff, 0xaf420360, 0x8f420360, 
+0x8faa0054, 0x8fab004c, 0xad6a0000, 0x8f420044, 
+0x8f440088, 0x8f430078, 0x24420001, 0x441024, 
+0x24630001, 0xaf420044, 0xaf430078, 0x8c020240, 
+0x62182b, 0x14600075, 0x24070008, 0x8f440168, 
+0x8f45016c, 0x8f430044, 0x8f48000c, 0x8f860120, 
+0x24020040, 0xafa20010, 0xafa30014, 0xafa80018, 
+0x8f42010c, 0x40f809, 0x24c6001c, 0x14400011, 
+0x240b0001, 0x3c010001, 0x370821, 0xa02b40f2, 
+0x8f820124, 0xafa20010, 0x8f820128, 0x3c040001, 
+0x2484688c, 0xafa20014, 0x8f460044, 0x8f870120, 
+0x3c050009, 0xc002b3b, 0x34a51300, 0x1000000b, 
+0x0, 0x8f420304, 0x24420001, 0xaf420304, 
+0x8f420304, 0x8f420044, 0xaf42007c, 0x3c010001, 
+0x370821, 0xa02040f2, 0xaf400078, 0x8f420318, 
+0x24420001, 0xaf420318, 0x10000048, 0x8f420318, 
+0xa6b0000a, 0x8f430044, 0x8f4401a0, 0x8f4501a4, 
+0x34028000, 0xafa20010, 0x8f420044, 0x2a03021, 
+0x24070020, 0xafa20014, 0x8f42000c, 0x31940, 
+0x604821, 0xafa20018, 0x8f42010c, 0x4021, 
+0xa92821, 0xa9182b, 0x882021, 0x40f809, 
+0x832021, 0x1040fe1f, 0x240a0001, 0xa34a05c4, 
+0x8fab006c, 0x8faa0064, 0x1705823, 0xafab006c, 
+0x8fab009c, 0x1505021, 0x16a102b, 0x10400004, 
+0xafaa0064, 0x8f420148, 0x1425023, 0xafaa0064, 
+0x8f420368, 0x2442ffff, 0xaf420368, 0x8faa005c, 
+0x240b0001, 0x8f420368, 0x154b0006, 0x240b0002, 
+0x8f42035c, 0x2442ffff, 0xaf42035c, 0x1000000c, 
+0x8f42035c, 0x114b0006, 0x0, 0x8f420360, 
+0x2442ffff, 0xaf420360, 0x10000005, 0x8f420360, 
+0x8f420364, 0x2442ffff, 0xaf420364, 0x8f420364, 
+0x8fab0054, 0x8faa004c, 0xad4b0000, 0x8f420044, 
+0x8f440088, 0x8f430078, 0x24420001, 0x441024, 
+0x24630001, 0xaf420044, 0xaf430078, 0x8faa006c, 
+0x1540fe0b, 0x0, 0x8fab006c, 0x1160001e, 
+0x0, 0x934205c4, 0x10400009, 0x0, 
+0x8faa0064, 0xaf4a00c4, 0xaf4b00c0, 0x8fab007c, 
+0xaf4b00c8, 0x8faa0074, 0x1000000e, 0xaf4a00cc, 
+0x97ab008e, 0x1160000b, 0x34038100, 0x8fa20020, 
+0x8c46000c, 0xa443000c, 0x97aa0086, 0x8c440004, 
+0x8c450008, 0xa44a000e, 0xac440000, 0xac450004, 
+0xac460008, 0x8f42034c, 0x24420001, 0xaf42034c, 
+0x10000010, 0x8f42034c, 0x8fab007c, 0x3164ffff, 
+0x2484fffc, 0x801821, 0x8f440250, 0x8f450254, 
+0x8f460118, 0x1021, 0xa32821, 0xa3382b, 
+0x822021, 0x872021, 0xaf440250, 0xc0f809, 
+0xaf450254, 0x8fbf00c8, 0x8fbe00c4, 0x8fb500c0, 
+0x8fb300bc, 0x8fb200b8, 0x8fb100b4, 0x8fb000b0, 
+0x3e00008, 0x27bd00d0, 0x3e00008, 0x0, 
+0x27bdff38, 0x240b0001, 0xafbf00c0, 0xafbe00bc, 
+0xafb500b8, 0xafb300b4, 0xafb200b0, 0xafb100ac, 
+0xafb000a8, 0xa3a00087, 0xafa00044, 0xafab005c, 
+0x934205c4, 0xa7a00076, 0x10400007, 0xa7a0007e, 
+0x8f4c00c0, 0xafac0064, 0x8f4b00c8, 0x8f5e00c4, 
+0x10000130, 0xafab006c, 0x8f420114, 0x40f809, 
+0x0, 0x403021, 0x10c002a1, 0x0, 
+0x8cc20000, 0x8cc30004, 0xafa20020, 0xafa30024, 
+0x8fac0024, 0x8fbe0020, 0x3182ffff, 0x2442fffc, 
+0xafa20064, 0x3c020006, 0x2c21024, 0x14400015, 
+0xafac006c, 0x93c20000, 0x30420001, 0x10400011, 
+0x2402ffff, 0x8fc30000, 0x14620004, 0x3402ffff, 
+0x97c30004, 0x1062000b, 0x0, 0xc0024bb, 
+0x3c02021, 0x304200ff, 0x14400006, 0x0, 
+0x8f420118, 0x40f809, 0x0, 0x10000280, 
+0x0, 0x8fa20024, 0x3c03ffbf, 0x3463ffff, 
+0x431024, 0x3c03ffff, 0x431824, 0x14600003, 
+0xafa20024, 0x10000040, 0x8021, 0x3c020080, 
+0x621024, 0x10400007, 0x0, 0x8f42038c, 
+0x24420001, 0xaf42038c, 0x8f42038c, 0x10000036, 
+0x24100001, 0x8f420210, 0x24420001, 0xaf420210, 
+0x8f420210, 0x3c020001, 0x621024, 0x10400006, 
+0x3c020002, 0x8f4201c4, 0x24420001, 0xaf4201c4, 
+0x8f4201c4, 0x3c020002, 0x621024, 0x10400006, 
+0x3c020004, 0x8f42037c, 0x24420001, 0xaf42037c, 
+0x8f42037c, 0x3c020004, 0x621024, 0x10400006, 
+0x3c020008, 0x8f420380, 0x24420001, 0xaf420380, 
+0x8f420380, 0x3c020008, 0x621024, 0x10400006, 
+0x3c020010, 0x8f420384, 0x24420001, 0xaf420384, 
+0x8f420384, 0x3c020010, 0x621024, 0x10400006, 
+0x3c020020, 0x8f4201c0, 0x24420001, 0xaf4201c0, 
+0x8f4201c0, 0x3c020020, 0x621024, 0x10400006, 
+0x24100001, 0x8f420388, 0x24420001, 0xaf420388, 
+0x8f420388, 0x24100001, 0x8c020260, 0x8fab0064, 
+0x4b102b, 0x10400015, 0x320200ff, 0x8f4201e8, 
+0x24420001, 0xaf4201e8, 0x8f4201e8, 0x8fac006c, 
+0x8f8200e0, 0x358c0100, 0xafac006c, 0xafa20010, 
+0x8f8200e4, 0x24100001, 0x3c040001, 0x248468a0, 
+0xafa20014, 0x8fa60020, 0x8fa70024, 0x3c050007, 
+0xc002b3b, 0x34a53600, 0x320200ff, 0x10400010, 
+0x3c020080, 0x2c21024, 0x1440000e, 0x32c20400, 
+0x8fab006c, 0x3c020080, 0x34420100, 0x1621024, 
+0x10400005, 0x0, 0x8f42020c, 0x24420001, 
+0xaf42020c, 0x8f42020c, 0x10000202, 0x8fa30064, 
+0x32c20400, 0x10400012, 0x34028100, 0x97c3000c, 
+0x1462000f, 0x0, 0x240c0200, 0xa7ac0076, 
+0x97c2000e, 0x8fc30008, 0x8fc40004, 0x8fab0064, 
+0x8fc50000, 0x256bfffc, 0xafab0064, 0xa7a2007e, 
+0xafc3000c, 0xafc40008, 0xafc50004, 0x27de0004, 
+0x8fa70064, 0x320200ff, 0x14400034, 0x3c020100, 
+0x97c4000c, 0x2c8305dd, 0x38828870, 0x2c420001, 
+0x621825, 0x10600015, 0x2821, 0x32c20800, 
+0x10400015, 0x24020800, 0x97c30014, 0x14620012, 
+0x3402aaaa, 0x97c3000e, 0x14620007, 0x2021, 
+0x97c30010, 0x24020300, 0x14620004, 0x801021, 
+0x97c20012, 0x2c440001, 0x801021, 0x54400006, 
+0x24050016, 0x10000004, 0x0, 0x24020800, 
+0x50820001, 0x2405000e, 0x10a00013, 0x3c52021, 
+0x24830009, 0x3c02001f, 0x3442ffff, 0x43102b, 
+0x10400003, 0x0, 0x8f420148, 0x621823, 
+0x90620000, 0x38430006, 0x2c630001, 0x38420011, 
+0x2c420001, 0x621825, 0x10600004, 0x3c020100, 
+0x94820002, 0x453821, 0x3c020100, 0x2c21024, 
+0x5040000e, 0xafa70064, 0x8fac0064, 0x10ec0008, 
+0x3c050007, 0x3c040001, 0x24846908, 0x8fa60064, 
+0x34a54000, 0xafa00010, 0xc002b3b, 0xafa00014, 
+0x8fab0064, 0x256b0004, 0xafab0064, 0x8f420080, 
+0x8fac0064, 0x4c102b, 0x1040002c, 0x32c28000, 
+0x10400034, 0x240b0003, 0x32c21000, 0x10400031, 
+0xafab005c, 0x1000002e, 0x240c0004, 0x8f420350, 
+0x2403ffbf, 0x283a024, 0x24420001, 0xaf420350, 
+0x10000173, 0x8f420350, 0x3c020800, 0x2c2b025, 
+0x2402ffbf, 0x282a024, 0x8f830128, 0x3c040001, 
+0x248468d0, 0x26620001, 0xafa20014, 0xafa30010, 
+0x8f860120, 0x8f870124, 0x3c050007, 0xc002b3b, 
+0x34a55300, 0x10000162, 0x0, 0x8ea20000, 
+0x8ea30004, 0x3c040001, 0x248468e8, 0xafb00010, 
+0xafb10014, 0x8ea70018, 0x34a55900, 0xc002b3b, 
+0x603021, 0x10000156, 0x0, 0x8f420084, 
+0x8fab0064, 0x4b102b, 0x14400007, 0x3c020001, 
+0x2c21024, 0x10400004, 0x0, 0x240c0002, 
+0xafac005c, 0x8fab0064, 0x11600166, 0x27ac0020, 
+0xafac008c, 0x8fab005c, 0x240c0001, 0x556c0021, 
+0x240c0002, 0x8f430054, 0x8f420050, 0x1062000b, 
+0x274b0054, 0x8f510054, 0x3403ecc0, 0xafab004c, 
+0x26220001, 0x304201ff, 0xafa20054, 0x111140, 
+0x431021, 0x1000006b, 0x2e2a821, 0x8f420044, 
+0x8fac0064, 0x3c040001, 0x248468ac, 0xafac0014, 
+0xafa20010, 0x8f460054, 0x8f470050, 0x3c050007, 
+0xc002b3b, 0x34a54300, 0x8f430350, 0x2402ffbf, 
+0x282a024, 0x24630001, 0xaf430350, 0x10000124, 
+0x8f420350, 0x156c001d, 0x0, 0x8f430074, 
+0x8f420070, 0x1062000a, 0x274b0074, 0x8f510074, 
+0xafab004c, 0x26220001, 0x304203ff, 0xafa20054, 
+0x111140, 0x24426cc0, 0x1000004a, 0x2e2a821, 
+0x8f420044, 0x8fac0064, 0x3c040001, 0x248468b8, 
+0x3c050007, 0xafac0014, 0xafa20010, 0x8f460074, 
+0x8f470070, 0x34a54500, 0x240b0001, 0xc002b3b, 
+0xafab005c, 0x1000ffc3, 0x0, 0x8f430064, 
+0x8f420060, 0x1062001a, 0x274c0064, 0x8f510064, 
+0x8fab005c, 0xafac004c, 0x26220001, 0x304200ff, 
+0xafa20054, 0x24020004, 0x1562000e, 0x111140, 
+0x111180, 0x24420cc0, 0x2e21021, 0xafa20044, 
+0x9442002a, 0x8fac0044, 0x8fab0064, 0x4b102b, 
+0x10400024, 0x25950020, 0x240c0001, 0x10000021, 
+0xa3ac0087, 0x24424cc0, 0x1000001e, 0x2e2a821, 
+0x8f420044, 0x8fab0064, 0x3c040001, 0x248468c4, 
+0xafab0014, 0xafa20010, 0x8f460064, 0x8f470060, 
+0x3c050007, 0xc002b3b, 0x34a54800, 0x3c020008, 
+0x2c21024, 0x1440ff61, 0x0, 0x8f420370, 
+0x240c0001, 0xafac005c, 0x24420001, 0xaf420370, 
+0x1000ff90, 0x8f420370, 0x27a30036, 0x131040, 
+0x621821, 0x94620000, 0x441021, 0x1000001f, 
+0xa4620000, 0xaebe0018, 0x93a20087, 0x10400084, 
+0x9821, 0x8fab0044, 0x8fa40064, 0x8fa3008c, 
+0x25620020, 0xafa20028, 0x25620008, 0xafa20030, 
+0x25620010, 0xafab002c, 0xafa20034, 0x9562002a, 
+0xa7a20038, 0x95620018, 0xa7a2003a, 0x9562001a, 
+0xa7a2003c, 0x9562001c, 0xa7a2003e, 0x94620018, 
+0x24630002, 0x822023, 0x1880ffdf, 0x26730001, 
+0x2e620004, 0x1440fff9, 0x0, 0x8f4200fc, 
+0x262102a, 0x14400030, 0x24030001, 0x8f83012c, 
+0x10600028, 0x0, 0x8f820124, 0x431023, 
+0x22143, 0x58800001, 0x24840040, 0x8f820128, 
+0x431023, 0x21943, 0x58600001, 0x24630040, 
+0x64102a, 0x54400001, 0x602021, 0xaf4400fc, 
+0x8f4200fc, 0x262102a, 0x10400016, 0x24030001, 
+0x1000001a, 0x306200ff, 0x8fac008c, 0x101040, 
+0x4c1021, 0x94470018, 0x101080, 0x4c1021, 
+0xafbe0010, 0x8c420008, 0x3c040001, 0x248468dc, 
+0x3c050007, 0x8c430004, 0x8c420000, 0x34a55500, 
+0x2003021, 0xc002b3b, 0xafa30014, 0x10000039, 
+0x0, 0x8f420334, 0x1821, 0x24420001, 
+0xaf420334, 0x8f420334, 0x306200ff, 0x1040ff06, 
+0x8021, 0x8f430008, 0x2402fbff, 0x1260002d, 
+0x625024, 0x3c0b4000, 0x22b4025, 0x8fb1008c, 
+0x2669ffff, 0x2209021, 0x8e420008, 0x96270018, 
+0x8c440000, 0x8c450004, 0x56090004, 0x240b0001, 
+0x240c0002, 0x10000002, 0xafac0010, 0xafab0010, 
+0x16000004, 0xafa80014, 0x8f420008, 0x10000002, 
+0xafa20018, 0xafaa0018, 0x8f42010c, 0x3c03021, 
+0xafa80098, 0xafa9009c, 0x40f809, 0xafaa00a0, 
+0x8fa80098, 0x8fa9009c, 0x8faa00a0, 0x1040ffc2, 
+0x3c02001f, 0x96230018, 0x3442ffff, 0x3c3f021, 
+0x5e102b, 0x10400003, 0x26310002, 0x8f420148, 
+0x3c2f023, 0x26100001, 0x213102b, 0x1440ffda, 
+0x26520004, 0x8fb00064, 0x1000001a, 0x0, 
+0x96a3000a, 0x8fb00064, 0x70102b, 0x54400001, 
+0x608021, 0x8ea40000, 0x8ea50004, 0x8fab005c, 
+0x240c0002, 0xafac0010, 0x934305c4, 0xb1700, 
+0x10600003, 0x2223025, 0x3c020800, 0xc23025, 
+0xafa60014, 0x8f420008, 0xafa20018, 0x8f42010c, 
+0x3c03021, 0x40f809, 0x2003821, 0x1040fecb, 
+0x3c050007, 0x97ac0076, 0x11800007, 0x96a3000e, 
+0x934205c4, 0x14400004, 0x0, 0x97ab007e, 
+0x6c1825, 0xa6ab0016, 0x8fac006c, 0x3c02ffff, 
+0x1821024, 0x10400003, 0xc1402, 0x34630400, 
+0xa6a20014, 0xa6b0000a, 0x8fab0064, 0x560b0006, 
+0x3d0f021, 0x34620004, 0xafa00064, 0xa6a2000e, 
+0x1000000d, 0xa34005c4, 0x8fac0064, 0x3c02001f, 
+0x3442ffff, 0x5e102b, 0x1906023, 0xafac0064, 
+0xa6a3000e, 0x240b0001, 0x10400003, 0xa34b05c4, 
+0x8f420148, 0x3c2f023, 0x8fab0054, 0x8fac004c, 
+0xad8b0000, 0x8fac0064, 0x1580feba, 0x0, 
+0x8fab0064, 0x1160001b, 0x0, 0x934205c4, 
+0x10400006, 0x0, 0xaf5e00c4, 0xaf4b00c0, 
+0x8fac006c, 0x1000000e, 0xaf4c00c8, 0x97ab0076, 
+0x1160000b, 0x34038100, 0x8fa20020, 0x8c46000c, 
+0xa443000c, 0x97ac007e, 0x8c440004, 0x8c450008, 
+0xa44c000e, 0xac440000, 0xac450004, 0xac460008, 
+0x8f42034c, 0x24420001, 0xaf42034c, 0x10000010, 
+0x8f42034c, 0x8fab006c, 0x3164ffff, 0x2484fffc, 
+0x801821, 0x8f440250, 0x8f450254, 0x8f460118, 
+0x1021, 0xa32821, 0xa3382b, 0x822021, 
+0x872021, 0xaf440250, 0xc0f809, 0xaf450254, 
+0x8fbf00c0, 0x8fbe00bc, 0x8fb500b8, 0x8fb300b4, 
+0x8fb200b0, 0x8fb100ac, 0x8fb000a8, 0x3e00008, 
+0x27bd00c8, 0x3e00008, 0x0, 0x27bdffd8, 
+0xafbf0024, 0xafb00020, 0x8f43004c, 0x8f420048, 
+0x10620034, 0x0, 0x8f430048, 0x8f42004c, 
+0x622023, 0x4820001, 0x24840200, 0x8f430054, 
+0x8f42004c, 0x43102b, 0x14400004, 0x24020200, 
+0x8f43004c, 0x10000005, 0x431023, 0x8f420054, 
+0x8f43004c, 0x431023, 0x2442ffff, 0x405021, 
+0x8a102a, 0x54400001, 0x805021, 0x8f49004c, 
+0x8f48004c, 0x8f440188, 0x8f45018c, 0x8f46004c, 
+0x24071000, 0xafa70010, 0x84140, 0x1001821, 
+0x12a4821, 0x313001ff, 0xafb00014, 0x8f470014, 
+0x1021, 0x63140, 0xafa70018, 0xa32821, 
+0xa3382b, 0x822021, 0x872021, 0x3402ecc0, 
+0xc23021, 0x8f420108, 0x2e63021, 0x40f809, 
+0xa3940, 0x54400001, 0xaf50004c, 0x8f43004c, 
+0x8f420048, 0x14620018, 0x0, 0x8f420000, 
+0x10400007, 0x0, 0xaf80004c, 0x8f82004c, 
+0x1040fffd, 0x0, 0x10000005, 0x0, 
+0xaf800048, 0x8f820048, 0x1040fffd, 0x0, 
+0x8f820060, 0x2403fdff, 0x431024, 0xaf820060, 
+0x8f420000, 0x10400003, 0x0, 0x10000002, 
+0xaf80004c, 0xaf800048, 0x8fbf0024, 0x8fb00020, 
+0x3e00008, 0x27bd0028, 0x3e00008, 0x0, 
+0x27bdffd8, 0xafbf0024, 0xafb00020, 0x8f43005c, 
+0x8f420058, 0x10620049, 0x0, 0x8f430058, 
+0x8f42005c, 0x622023, 0x4820001, 0x24840100, 
+0x8f430064, 0x8f42005c, 0x43102b, 0x14400004, 
+0x24020100, 0x8f43005c, 0x10000005, 0x431023, 
+0x8f420064, 0x8f43005c, 0x431023, 0x2442ffff, 
+0x403821, 0x87102a, 0x54400001, 0x803821, 
+0x8f42005c, 0x471021, 0x305000ff, 0x32c21000, 
+0x10400015, 0x24082000, 0x8f49005c, 0x8f440190, 
+0x8f450194, 0x8f46005c, 0x73980, 0xafa80010, 
+0xafb00014, 0x8f480014, 0x94980, 0x1201821, 
+0x1021, 0xa32821, 0xa3482b, 0x822021, 
+0x892021, 0x63180, 0xafa80018, 0x8f420108, 
+0x10000014, 0x24c60cc0, 0x8f49005c, 0x8f440190, 
+0x8f450194, 0x8f46005c, 0x73940, 0xafa80010, 
+0xafb00014, 0x8f480014, 0x94940, 0x1201821, 
+0x1021, 0xa32821, 0xa3482b, 0x822021, 
+0x892021, 0x63140, 0xafa80018, 0x8f420108, 
+0x24c64cc0, 0x40f809, 0x2e63021, 0x54400001, 
+0xaf50005c, 0x8f43005c, 0x8f420058, 0x14620018, 
+0x0, 0x8f420000, 0x10400007, 0x0, 
+0xaf80004c, 0x8f82004c, 0x1040fffd, 0x0, 
+0x10000005, 0x0, 0xaf800048, 0x8f820048, 
+0x1040fffd, 0x0, 0x8f820060, 0x2403feff, 
+0x431024, 0xaf820060, 0x8f420000, 0x10400003, 
+0x0, 0x10000002, 0xaf80004c, 0xaf800048, 
+0x8fbf0024, 0x8fb00020, 0x3e00008, 0x27bd0028, 
+0x3e00008, 0x0, 0x27bdffd8, 0xafbf0024, 
+0xafb00020, 0x8f43006c, 0x8f420068, 0x10620033, 
+0x0, 0x8f430068, 0x8f42006c, 0x622023, 
+0x4820001, 0x24840400, 0x8f430074, 0x8f42006c, 
+0x43102b, 0x14400004, 0x24020400, 0x8f43006c, 
+0x10000005, 0x431023, 0x8f420074, 0x8f43006c, 
+0x431023, 0x2442ffff, 0x405021, 0x8a102a, 
+0x54400001, 0x805021, 0x8f49006c, 0x8f48006c, 
+0x8f440198, 0x8f45019c, 0x8f46006c, 0x24074000, 
+0xafa70010, 0x84140, 0x1001821, 0x12a4821, 
+0x313003ff, 0xafb00014, 0x8f470014, 0x1021, 
+0x63140, 0x24c66cc0, 0xafa70018, 0xa32821, 
+0xa3382b, 0x822021, 0x872021, 0x8f420108, 
+0x2e63021, 0x40f809, 0xa3940, 0x54400001, 
+0xaf50006c, 0x8f43006c, 0x8f420068, 0x14620018, 
+0x0, 0x8f420000, 0x10400007, 0x0, 
+0xaf80004c, 0x8f82004c, 0x1040fffd, 0x0, 
+0x10000005, 0x0, 0xaf800048, 0x8f820048, 
+0x1040fffd, 0x0, 0x8f820060, 0x2403f7ff, 
+0x431024, 0xaf820060, 0x8f420000, 0x10400003, 
+0x0, 0x10000002, 0xaf80004c, 0xaf800048, 
+0x8fbf0024, 0x8fb00020, 0x3e00008, 0x27bd0028, 
+0x3e00008, 0x0, 0x8f4200fc, 0x3c030001, 
+0x8f4400f8, 0x346330c8, 0x24420001, 0xaf4200fc, 
+0x8f850128, 0x2e31021, 0x54820004, 0x24820008, 
+0x3c020001, 0x34422ec8, 0x2e21021, 0x401821, 
+0xaf4300f8, 0xac600000, 0x8f4200f4, 0x14620004, 
+0x3c020001, 0x24a20020, 0x1000000f, 0xaf820128, 
+0x8f4300f8, 0x344230c8, 0x2e21021, 0x54620004, 
+0x24620008, 0x3c020001, 0x34422ec8, 0x2e21021, 
+0x401821, 0x8c620004, 0x21140, 0xa21021, 
+0xaf820128, 0xac600000, 0x8ca30018, 0x30620070, 
+0x1040002d, 0x30620020, 0x10400004, 0x3c020010, 
+0x2c21024, 0x1040000d, 0x0, 0x30620040, 
+0x10400004, 0x3c020020, 0x2c21024, 0x10400007, 
+0x0, 0x30620010, 0x1040001f, 0x3c020040, 
+0x2c21024, 0x1440001c, 0x0, 0x8f820040, 
+0x30420001, 0x14400008, 0x2021, 0x8c030104, 
+0x24020001, 0x50620005, 0x24040001, 0x8c020264, 
+0x10400003, 0x801021, 0x24040001, 0x801021, 
+0x10400006, 0x0, 0x8f42030c, 0x24420001, 
+0xaf42030c, 0x10000008, 0x8f42030c, 0x8f820044, 
+0x34420004, 0xaf820044, 0x8f420308, 0x24420001, 
+0xaf420308, 0x8f420308, 0x3e00008, 0x0, 
+0x3e00008, 0x0, 0x27bdff98, 0xafbf0060, 
+0xafbe005c, 0xafb50058, 0xafb30054, 0xafb20050, 
+0xafb1004c, 0xafb00048, 0x8f4200fc, 0x24420001, 
+0xaf4200fc, 0x8f880128, 0x25020020, 0xaf820128, 
+0x8d030018, 0x30620070, 0x1040002e, 0x30620020, 
+0x10400004, 0x3c020010, 0x2c21024, 0x1040000d, 
+0x0, 0x30620040, 0x10400004, 0x3c020020, 
+0x2c21024, 0x10400007, 0x0, 0x30620010, 
+0x104001a9, 0x3c020040, 0x2c21024, 0x144001a6, 
+0x0, 0x8f820040, 0x30420001, 0x14400008, 
+0x2021, 0x8c030104, 0x24020001, 0x50620005, 
+0x24040001, 0x8c020264, 0x10400003, 0x801021, 
+0x24040001, 0x801021, 0x10400006, 0x0, 
+0x8f42030c, 0x24420001, 0xaf42030c, 0x10000192, 
+0x8f42030c, 0x8f820044, 0x34420004, 0xaf820044, 
+0x8f420308, 0x24420001, 0xaf420308, 0x1000018a, 
+0x8f420308, 0x30620002, 0x1040014b, 0x3c020800, 
+0x8d1e001c, 0x1e5702, 0xafaa0034, 0x950a0016, 
+0x3c22024, 0xafaa0024, 0x8faa0034, 0x24020001, 
+0x15420006, 0x33deffff, 0x1e1140, 0x3403ecc0, 
+0x431021, 0x10000010, 0x2e2a821, 0x24020002, 
+0x15420005, 0x24020003, 0x1e1140, 0x24426cc0, 
+0x10000009, 0x2e2a821, 0x15420005, 0x1e1180, 
+0x1e1140, 0x24424cc0, 0x10000003, 0x2e2a821, 
+0x571021, 0x24550ce0, 0x96a2000e, 0x304afffc, 
+0x30420400, 0x10400003, 0xafaa002c, 0x100000e1, 
+0x8821, 0x10800004, 0x8821, 0x97b10026, 
+0x100000dd, 0xa6b10012, 0x8eb30018, 0x966a000c, 
+0xa7aa003e, 0x97a5003e, 0x2ca305dd, 0x38a28870, 
+0x2c420001, 0x621825, 0x10600015, 0x2021, 
+0x32c20800, 0x10400015, 0x24020800, 0x96630014, 
+0x14620012, 0x3402aaaa, 0x9663000e, 0x14620007, 
+0x2821, 0x96630010, 0x24020300, 0x14620004, 
+0xa01021, 0x96620012, 0x2c450001, 0xa01021, 
+0x54400006, 0x24040016, 0x10000004, 0x0, 
+0x24020800, 0x50a20001, 0x2404000e, 0x108000b9, 
+0x2649021, 0x92420000, 0x3042000f, 0x28080, 
+0x32c20100, 0x10400020, 0x2501821, 0x3c020020, 
+0x43102b, 0x1440000e, 0x2402021, 0x2821, 
+0x94820000, 0x24840002, 0xa22821, 0x83102b, 
+0x1440fffb, 0x30a2ffff, 0x51c02, 0x622821, 
+0x51c02, 0x30a2ffff, 0x10000009, 0x622821, 
+0x8f470148, 0x8f420110, 0x102842, 0x3c060020, 
+0x40f809, 0xafa80040, 0x3045ffff, 0x8fa80040, 
+0x50a00001, 0x3405ffff, 0x8faa002c, 0x354a0002, 
+0x10000002, 0xafaa002c, 0x2821, 0x32c20080, 
+0x10400090, 0xa6a50010, 0x26430009, 0x3c02001f, 
+0x3442ffff, 0x43102b, 0x10400003, 0x0, 
+0x8f420148, 0x621823, 0x90660000, 0x30c200ff, 
+0x38430006, 0x2c630001, 0x38420011, 0x2c420001, 
+0x621825, 0x1060007f, 0x24020800, 0x8821, 
+0x97a3003e, 0x1462000f, 0x2602021, 0x96710000, 
+0x96620002, 0x96630004, 0x96640006, 0x2228821, 
+0x2238821, 0x2248821, 0x96620008, 0x9663000a, 
+0x9664000c, 0x2228821, 0x2238821, 0x10000007, 
+0x2248821, 0x94820000, 0x24840002, 0x2228821, 
+0x92102b, 0x1440fffb, 0x0, 0x111c02, 
+0x3222ffff, 0x628821, 0x111c02, 0x3222ffff, 
+0x628821, 0x32c20200, 0x10400003, 0x26440006, 
+0x1000003e, 0x8021, 0x3c05001f, 0x34a5ffff, 
+0xa4102b, 0x10400003, 0x0, 0x8f420148, 
+0x822023, 0x94820000, 0x30421fff, 0x10400004, 
+0x2644000c, 0x96420002, 0x10000030, 0x508023, 
+0x96420002, 0x26430014, 0x508023, 0x3c020020, 
+0x43102b, 0x1440000a, 0xd08021, 0x9642000c, 
+0x2028021, 0x9642000e, 0x96430010, 0x96440012, 
+0x2028021, 0x2038021, 0x10000020, 0x2048021, 
+0xa4102b, 0x10400003, 0x0, 0x8f420148, 
+0x822023, 0x94820000, 0x24840002, 0x2028021, 
+0xa4102b, 0x10400003, 0x0, 0x8f420148, 
+0x822023, 0x94820000, 0x24840002, 0x2028021, 
+0xa4102b, 0x10400003, 0x0, 0x8f420148, 
+0x822023, 0x94820000, 0x24840002, 0x2028021, 
+0xa4102b, 0x10400003, 0x0, 0x8f420148, 
+0x822023, 0x94820000, 0x2028021, 0x3c020100, 
+0x2c21024, 0x1040000e, 0x0, 0x8faa002c, 
+0x31420004, 0x1040000a, 0x0, 0x9504000e, 
+0x2642021, 0xc003eec, 0x2484fffc, 0x3042ffff, 
+0x2228821, 0x111c02, 0x3222ffff, 0x628821, 
+0x8faa0024, 0x1518823, 0x111402, 0x2228821, 
+0x2308821, 0x111402, 0x2228821, 0x3231ffff, 
+0x52200001, 0x3411ffff, 0x8faa002c, 0x354a0001, 
+0xafaa002c, 0xa6b10012, 0x97aa002e, 0xa6aa000e, 
+0x8faa002c, 0x31420004, 0x10400002, 0x24091000, 
+0x34098000, 0x8f480044, 0x8f4401a0, 0x8f4501a4, 
+0xafa90010, 0x8f490044, 0x84140, 0x1001821, 
+0xafa90014, 0x8f48000c, 0x2a03021, 0x24070020, 
+0xafa80018, 0x8f48010c, 0x1021, 0xa32821, 
+0xa3482b, 0x822021, 0x100f809, 0x892021, 
+0x1440000b, 0x0, 0x8f820128, 0x3c040001, 
+0x24846914, 0xafbe0014, 0xafa20010, 0x8f860124, 
+0x8f870120, 0x3c050007, 0xc002b3b, 0x34a59920, 
+0x8f420368, 0x2442ffff, 0xaf420368, 0x8f420044, 
+0x8f430088, 0x24420001, 0x431024, 0xaf420044, 
+0x8faa0034, 0x8f440368, 0x24020001, 0x15420006, 
+0x24020002, 0x8f42035c, 0x2442ffff, 0xaf42035c, 
+0x10000049, 0x8f42035c, 0x15420006, 0x0, 
+0x8f420364, 0x2442ffff, 0xaf420364, 0x10000042, 
+0x8f420364, 0x8f420360, 0x2442ffff, 0xaf420360, 
+0x1000003d, 0x8f420360, 0x30621000, 0x10400005, 
+0x30628000, 0x8f420078, 0x24420001, 0x10000036, 
+0xaf420078, 0x10400034, 0x0, 0x8f420078, 
+0x24420001, 0xaf420078, 0x8c030240, 0x43102b, 
+0x1440002d, 0x24070008, 0x8f440168, 0x8f45016c, 
+0x8f430044, 0x8f48000c, 0x8f860120, 0x24020040, 
+0xafa20010, 0xafa30014, 0xafa80018, 0x8f42010c, 
+0x40f809, 0x24c6001c, 0x14400011, 0x24020001, 
+0x3c010001, 0x370821, 0xa02240f2, 0x8f820124, 
+0xafa20010, 0x8f820128, 0x3c040001, 0x2484688c, 
+0xafa20014, 0x8f460044, 0x8f870120, 0x3c050009, 
+0xc002b3b, 0x34a51300, 0x1000000b, 0x0, 
+0x8f420304, 0x24420001, 0xaf420304, 0x8f420304, 
+0x8f420044, 0xaf42007c, 0x3c010001, 0x370821, 
+0xa02040f2, 0xaf400078, 0x8f420318, 0x24420001, 
+0xaf420318, 0x8f420318, 0x8fbf0060, 0x8fbe005c, 
+0x8fb50058, 0x8fb30054, 0x8fb20050, 0x8fb1004c, 
+0x8fb00048, 0x3e00008, 0x27bd0068, 0x3e00008, 
+0x0, 0x0, 0x0, 0x8f42013c, 
+0xaf8200c0, 0x8f42013c, 0xaf8200c4, 0x8f42013c, 
+0xaf8200c8, 0x8f420138, 0xaf8200d0, 0x8f420138, 
+0xaf8200d4, 0x8f420138, 0x3e00008, 0xaf8200d8, 
+0x27bdffe0, 0x27840208, 0x24050200, 0xafbf0018, 
+0xc002bbf, 0x24060008, 0x8c020204, 0xc004012, 
+0xaf820210, 0x3c020001, 0x8c426d94, 0x30420002, 
+0x1040000e, 0x2021, 0x8c060248, 0x24020002, 
+0x3c010001, 0xac226d98, 0xc005104, 0x24050002, 
+0x2021, 0x8c060248, 0x24020001, 0x3c010001, 
+0xac226d98, 0x10000011, 0x24050001, 0x8c060248, 
+0x24020004, 0x3c010001, 0xac226d98, 0xc005104, 
+0x24050004, 0x3c020001, 0x8c426d94, 0x30420001, 
+0x10400008, 0x24020001, 0x3c010001, 0xac226d98, 
+0x2021, 0x24050001, 0x3c06601b, 0xc005104, 
+0x0, 0x3c040001, 0x248469d0, 0x8f420150, 
+0x8f430154, 0x3c050008, 0x8f460158, 0x21640, 
+0x31940, 0x34630403, 0x431025, 0x633c0, 
+0x461025, 0xaf82021c, 0xafa00010, 0xafa00014, 
+0x8f86021c, 0x34a50200, 0xc002b3b, 0x3821, 
+0x3c010001, 0xac206d90, 0x3c010001, 0xac206da8, 
+0x8fbf0018, 0x3e00008, 0x27bd0020, 0x27bdffe0, 
+0x3c050008, 0x34a50300, 0xafbf0018, 0xafa00010, 
+0xafa00014, 0x8f860200, 0x3c040001, 0x248469dc, 
+0xc002b3b, 0x3821, 0x8f420410, 0x24420001, 
+0xaf420410, 0x8f420410, 0x8fbf0018, 0x3e00008, 
+0x27bd0020, 0x27bdffd8, 0xafbf0020, 0xafb1001c, 
+0xafb00018, 0x8f4203a4, 0x24420001, 0xaf4203a4, 
+0x8f4203a4, 0x8f900220, 0x8f8200e0, 0xafa20010, 
+0x8f8200e4, 0xafa20014, 0x8f8600c4, 0x8f8700c8, 
+0x3c040001, 0x248469e8, 0xc002b3b, 0x2002821, 
+0x3c044000, 0x2041024, 0x504000b4, 0x3c040100, 
+0x8f4203bc, 0x24420001, 0xaf4203bc, 0x8f4203bc, 
+0x8f8700c4, 0x8f8300c8, 0x8f420148, 0x671823, 
+0x43102b, 0x10400003, 0x0, 0x8f420148, 
+0x621821, 0x10600005, 0x0, 0x8f42014c, 
+0x43102b, 0x1040000b, 0x0, 0x8f8200e0, 
+0x8f430124, 0xaf42011c, 0xaf430114, 0x8f820220, 
+0x3c0308ff, 0x3463fffb, 0x431024, 0x100000ce, 
+0x441025, 0x8f820220, 0x3c0308ff, 0x3463ffff, 
+0x431024, 0x34420004, 0xaf820220, 0x8f8200e0, 
+0x8f430124, 0xaf42011c, 0xaf430114, 0x8f8600c8, 
+0x8f840120, 0x8f830124, 0x10000005, 0x2821, 
+0x14620002, 0x24620020, 0x27624800, 0x401821, 
+0x1064000c, 0x30a200ff, 0x8c620018, 0x30420003, 
+0x1040fff7, 0x27624fe0, 0x8f4203d0, 0x24050001, 
+0x24420001, 0xaf4203d0, 0x8f4203d0, 0x8c660008, 
+0x30a200ff, 0x14400058, 0x0, 0x934205c4, 
+0x14400055, 0x0, 0x8f8700c4, 0x8f8800e0, 
+0x8f8400e4, 0x2402fff8, 0x1024024, 0x1041023, 
+0x218c3, 0x4620001, 0x24630200, 0x10600005, 
+0x24020001, 0x10620009, 0x0, 0x1000001f, 
+0x0, 0x8f4203c0, 0xe03021, 0x24420001, 
+0xaf4203c0, 0x10000040, 0x8f4203c0, 0x8f4203c4, 
+0x24420001, 0xaf4203c4, 0x8c860000, 0x8f420148, 
+0x8f4303c4, 0xe61823, 0x43102b, 0x10400004, 
+0x2c62233f, 0x8f420148, 0x621821, 0x2c62233f, 
+0x14400031, 0x0, 0x8f42020c, 0x24420001, 
+0xaf42020c, 0x8f42020c, 0xe03021, 0x24820008, 
+0xaf8200e4, 0x10000028, 0xaf8200e8, 0x8f4203c8, 
+0x24420001, 0xaf4203c8, 0x8f4203c8, 0x8c850000, 
+0x8f420148, 0xa71823, 0x43102b, 0x10400003, 
+0x0, 0x8f420148, 0x621821, 0x8f42014c, 
+0x43102b, 0x5440000a, 0xa03021, 0x8f42020c, 
+0x24420001, 0xaf42020c, 0x8f42020c, 0x24820008, 
+0xaf8200e4, 0x8f8400e4, 0x1488ffec, 0xaf8400e8, 
+0x1488000d, 0x27623000, 0x14820002, 0x2482fff8, 
+0x27623ff8, 0x94430006, 0x3c02001f, 0x3442ffff, 
+0xc33021, 0x46102b, 0x10400003, 0x0, 
+0x8f420148, 0xc23023, 0xaf8600c8, 0x8f8300c4, 
+0x8f420148, 0xc31823, 0x43102b, 0x10400003, 
+0x0, 0x8f420148, 0x621821, 0x10600005, 
+0x0, 0x8f42014c, 0x43102b, 0x50400008, 
+0x3c02fdff, 0x8f820220, 0x3c0308ff, 0x3463fffb, 
+0x431024, 0x3c034000, 0x1000003f, 0x431025, 
+0x8f4303cc, 0x3442ffff, 0x282a024, 0x24630001, 
+0xaf4303cc, 0x10000039, 0x8f4203cc, 0x2041024, 
+0x1040000e, 0x3c110200, 0x8f4203a8, 0x24420001, 
+0xaf4203a8, 0x8f4203a8, 0x8f820220, 0x3c0308ff, 
+0x3463ffff, 0x431024, 0x441025, 0xc003daf, 
+0xaf820220, 0x10000029, 0x0, 0x2111024, 
+0x50400008, 0x3c110400, 0x8f4203ac, 0x24420001, 
+0xaf4203ac, 0xc003daf, 0x8f4203ac, 0x10000019, 
+0x0, 0x2111024, 0x1040001c, 0x0, 
+0x8f830224, 0x24021402, 0x14620009, 0x3c050008, 
+0x3c040001, 0x248469f4, 0xafa00010, 0xafa00014, 
+0x8f860224, 0x34a50500, 0xc002b3b, 0x3821, 
+0x8f4203b0, 0x24420001, 0xaf4203b0, 0x8f4203b0, 
+0x8f820220, 0x2002021, 0x34420002, 0xc004e9c, 
+0xaf820220, 0x8f820220, 0x3c0308ff, 0x3463ffff, 
+0x431024, 0x511025, 0xaf820220, 0x8fbf0020, 
+0x8fb1001c, 0x8fb00018, 0x3e00008, 0x27bd0028, 
+0x3e00008, 0x0, 0x3c020001, 0x8c426da8, 
+0x27bdffb0, 0xafbf0048, 0xafbe0044, 0xafb50040, 
+0xafb3003c, 0xafb20038, 0xafb10034, 0x1040000f, 
+0xafb00030, 0x3c040001, 0x24846a00, 0x3c050008, 
+0xafa00010, 0xafa00014, 0x8f860220, 0x34a50600, 
+0x24020001, 0x3c010001, 0xac206da8, 0x3c010001, 
+0xac226d9c, 0xc002b3b, 0x3821, 0x3c037fff, 
+0x8c020268, 0x3463ffff, 0x3c04fdff, 0x431024, 
+0xac020268, 0x8f420004, 0x3484ffff, 0x30420002, 
+0x10400092, 0x284a024, 0x3c040600, 0x34842000, 
+0x8f420004, 0x2821, 0x2403fffd, 0x431024, 
+0xaf420004, 0xafa40020, 0x8f5e0018, 0x27aa0020, 
+0x240200ff, 0x13c20002, 0xafaa002c, 0x27c50001, 
+0x8c020228, 0xa09021, 0x1642000e, 0x1e38c0, 
+0x8f42033c, 0x24420001, 0xaf42033c, 0x8f42033c, 
+0x8c020228, 0x3c040001, 0x24846998, 0x3c050009, 
+0xafa00014, 0xafa20010, 0x8fa60020, 0x1000006d, 
+0x34a50500, 0xf71021, 0x8fa30020, 0x8fa40024, 
+0xac4304c0, 0xac4404c4, 0x8f830054, 0x8f820054, 
+0x247003e8, 0x2021023, 0x2c4203e9, 0x1040001b, 
+0x9821, 0xe08821, 0x263504c0, 0x8f440178, 
+0x8f45017c, 0x2201821, 0x240a0004, 0xafaa0010, 
+0xafb20014, 0x8f48000c, 0x1021, 0x2f53021, 
+0xafa80018, 0x8f48010c, 0x24070008, 0xa32821, 
+0xa3482b, 0x822021, 0x100f809, 0x892021, 
+0x54400006, 0x24130001, 0x8f820054, 0x2021023, 
+0x2c4203e9, 0x1440ffe9, 0x0, 0x326200ff, 
+0x54400017, 0xaf520018, 0x8f420378, 0x24420001, 
+0xaf420378, 0x8f420378, 0x8f820120, 0x8faa002c, 
+0xafa20010, 0x8f820124, 0x3c040001, 0x248469a4, 
+0x3c050009, 0xafa20014, 0x8d460000, 0x10000035, 
+0x34a50600, 0x8f420308, 0x24130001, 0x24420001, 
+0xaf420308, 0x8f420308, 0x1000001e, 0x326200ff, 
+0x8f830054, 0x8f820054, 0x247003e8, 0x2021023, 
+0x2c4203e9, 0x10400016, 0x9821, 0x3c150020, 
+0x24110010, 0x8f42000c, 0x8f440160, 0x8f450164, 
+0x8f860120, 0xafb10010, 0xafb20014, 0x551025, 
+0xafa20018, 0x8f42010c, 0x24070008, 0x40f809, 
+0x24c6001c, 0x1440ffe3, 0x0, 0x8f820054, 
+0x2021023, 0x2c4203e9, 0x1440ffee, 0x0, 
+0x326200ff, 0x14400011, 0x0, 0x8f420378, 
+0x24420001, 0xaf420378, 0x8f420378, 0x8f820120, 
+0x8faa002c, 0xafa20010, 0x8f820124, 0x3c040001, 
+0x248469ac, 0x3c050009, 0xafa20014, 0x8d460000, 
+0x34a50700, 0xc002b3b, 0x3c03821, 0x8f4202ec, 
+0x24420001, 0xaf4202ec, 0x8f4202ec, 0x8fbf0048, 
+0x8fbe0044, 0x8fb50040, 0x8fb3003c, 0x8fb20038, 
+0x8fb10034, 0x8fb00030, 0x3e00008, 0x27bd0050, 
+0x3c020001, 0x8c426da8, 0x27bdffe0, 0x1440000d, 
+0xafbf0018, 0x3c040001, 0x24846a0c, 0x3c050008, 
+0xafa00010, 0xafa00014, 0x8f860220, 0x34a50700, 
+0x24020001, 0x3c010001, 0xac226da8, 0xc002b3b, 
+0x3821, 0x3c020004, 0x2c21024, 0x10400007, 
+0x0, 0x8f820220, 0x3c0308ff, 0x3463ffff, 
+0x431024, 0x34420008, 0xaf820220, 0x3c050001, 
+0x8ca56d98, 0x24020001, 0x14a20007, 0x2021, 
+0xc00529b, 0x24050001, 0xac02026c, 0x8c03026c, 
+0x10000006, 0x3c020007, 0xc00529b, 0x2021, 
+0xac020268, 0x8c030268, 0x3c020007, 0x621824, 
+0x3c020002, 0x5062000d, 0x3c0205f5, 0x43102b, 
+0x14400006, 0x3c020004, 0x3c020001, 0x10620009, 
+0x3c020098, 0x1000000b, 0x0, 0x14620009, 
+0x3c023b9a, 0x10000004, 0x3442ca00, 0x10000002, 
+0x3442e100, 0x34429680, 0xaf4201fc, 0x8f4201fc, 
+0xaee20064, 0x8fbf0018, 0x3e00008, 0x27bd0020, 
+0x0, 0x0, 0x0, 0x86102b, 
+0x50400001, 0x872023, 0xc41023, 0x24843, 
+0x125102b, 0x1040001b, 0x91040, 0x824021, 
+0x88102b, 0x10400007, 0x1821, 0x94820000, 
+0x24840002, 0x621821, 0x88102b, 0x1440fffb, 
+0x0, 0x602021, 0xc73023, 0xa91023, 
+0x21040, 0xc22821, 0xc5102b, 0x10400007, 
+0x1821, 0x94c20000, 0x24c60002, 0x621821, 
+0xc5102b, 0x1440fffb, 0x0, 0x1000000d, 
+0x832021, 0x51040, 0x822821, 0x85102b, 
+0x10400007, 0x1821, 0x94820000, 0x24840002, 
+0x621821, 0x85102b, 0x1440fffb, 0x0, 
+0x602021, 0x41c02, 0x3082ffff, 0x622021, 
+0x41c02, 0x3082ffff, 0x622021, 0x3e00008, 
+0x3082ffff, 0x3e00008, 0x0, 0x802821, 
+0x30a20001, 0x1040002b, 0x3c03001f, 0x3463ffff, 
+0x24a20004, 0x62102b, 0x54400007, 0x65102b, 
+0x90a20001, 0x90a40003, 0x90a30000, 0x90a50002, 
+0x1000002a, 0x441021, 0x10400003, 0x0, 
+0x8f420148, 0xa22823, 0x90a40000, 0x24a50001, 
+0x65102b, 0x10400003, 0x0, 0x8f420148, 
+0xa22823, 0x90a20000, 0x24a50001, 0x21200, 
+0x822021, 0x65102b, 0x10400003, 0x0, 
+0x8f420148, 0xa22823, 0x90a20000, 0x24a50001, 
+0x822021, 0x65102b, 0x10400003, 0x0, 
+0x8f420148, 0xa22823, 0x90a20000, 0x1000002d, 
+0x21200, 0x3463ffff, 0x24a20004, 0x62102b, 
+0x5440000a, 0x65102b, 0x90a20000, 0x90a40002, 
+0x90a30001, 0x90a50003, 0x441021, 0x21200, 
+0x651821, 0x10000020, 0x432021, 0x10400003, 
+0x0, 0x8f420148, 0xa22823, 0x90a20000, 
+0x24a50001, 0x22200, 0x65102b, 0x10400003, 
+0x0, 0x8f420148, 0xa22823, 0x90a20000, 
+0x24a50001, 0x822021, 0x65102b, 0x10400003, 
+0x0, 0x8f420148, 0xa22823, 0x90a20000, 
+0x24a50001, 0x21200, 0x822021, 0x65102b, 
+0x10400003, 0x0, 0x8f420148, 0xa22823, 
+0x90a20000, 0x822021, 0x41c02, 0x3082ffff, 
+0x622021, 0x41c02, 0x3082ffff, 0x622021, 
+0x3e00008, 0x3082ffff, 0x0, 0x8f820220, 
+0x34420002, 0xaf820220, 0x3c020002, 0x8c428ff8, 
+0x30424000, 0x10400054, 0x24040001, 0x8f820200, 
+0x24067fff, 0x8f830200, 0x30450002, 0x2402fffd, 
+0x621824, 0xaf830200, 0xaf840204, 0x8f830054, 
+0x8f820054, 0x10000002, 0x24630001, 0x8f820054, 
+0x621023, 0x2c420002, 0x1440fffc, 0x0, 
+0x8f820224, 0x1444004d, 0x42040, 0xc4102b, 
+0x1040fff1, 0x0, 0x8f820200, 0x451025, 
+0xaf820200, 0x8f820220, 0x34428000, 0xaf820220, 
+0x8f830054, 0x8f820054, 0x10000002, 0x24630001, 
+0x8f820054, 0x621023, 0x2c420002, 0x1440fffc, 
+0x0, 0x8f820220, 0x3c030004, 0x431024, 
+0x1440000f, 0x0, 0x8f820220, 0x3c03ffff, 
+0x34637fff, 0x431024, 0xaf820220, 0x8f830054, 
+0x8f820054, 0x10000002, 0x24630001, 0x8f820054, 
+0x621023, 0x2c420002, 0x1440fffc, 0x0, 
+0x8f820220, 0x3c030004, 0x431024, 0x1440000d, 
+0x0, 0x8f820220, 0x34428000, 0xaf820220, 
+0x8f830054, 0x8f820054, 0x10000002, 0x24630001, 
+0x8f820054, 0x621023, 0x2c420002, 0x1440fffc, 
+0x0, 0x8f820220, 0x3c030004, 0x431024, 
+0x1040001b, 0x1021, 0x8f830220, 0x24020001, 
+0x10000015, 0x3c04f700, 0x8f820220, 0x3c04f700, 
+0x441025, 0xaf820220, 0x8f820220, 0x2403fffd, 
+0x431024, 0xaf820220, 0x8f820220, 0x3c030300, 
+0x431024, 0x14400003, 0x0, 0x10000008, 
+0x1021, 0x8f820220, 0x34420002, 0xaf820220, 
+0x8f830220, 0x24020001, 0x641825, 0xaf830220, 
+0x3e00008, 0x0, 0x2021, 0x3c050100, 
+0x24020001, 0xaf80021c, 0xaf820200, 0xaf820220, 
+0x27625000, 0xaf8200c0, 0x27625000, 0xaf8200c4, 
+0x27625000, 0xaf8200c8, 0x27625000, 0xaf8200d0, 
+0x27625000, 0xaf8200d4, 0x27625000, 0xaf8200d8, 
+0x27623000, 0xaf8200e0, 0x27623000, 0xaf8200e4, 
+0x27623000, 0xaf8200e8, 0x27622800, 0xaf8200f0, 
+0x27622800, 0xaf8200f4, 0x27622800, 0xaf8200f8, 
+0x418c0, 0x24840001, 0x3631021, 0xac453004, 
+0x3631021, 0xac403000, 0x28820200, 0x1440fff9, 
+0x418c0, 0x2021, 0x418c0, 0x24840001, 
+0x3631021, 0xac402804, 0x3631021, 0xac402800, 
+0x28820100, 0x1440fff9, 0x418c0, 0xaf80023c, 
+0x24030080, 0x24040100, 0xac600000, 0x24630004, 
+0x64102b, 0x5440fffd, 0xac600000, 0x8f830040, 
+0x3c02f000, 0x621824, 0x3c025000, 0x1062000c, 
+0x43102b, 0x14400006, 0x3c026000, 0x3c024000, 
+0x10620008, 0x24020800, 0x10000008, 0x0, 
+0x10620004, 0x24020800, 0x10000004, 0x0, 
+0x24020700, 0x3c010001, 0xac226dac, 0x3e00008, 
+0x0, 0x3c020001, 0x8c426dbc, 0x27bdffd0, 
+0xafbf002c, 0xafb20028, 0xafb10024, 0xafb00020, 
+0x3c010001, 0x10400005, 0xac206d94, 0xc004d9e, 
+0x0, 0x3c010001, 0xac206dbc, 0x8f830054, 
+0x8f820054, 0x10000002, 0x24630064, 0x8f820054, 
+0x621023, 0x2c420065, 0x1440fffc, 0x0, 
+0xc004db9, 0x0, 0x24040001, 0x2821, 
+0x27a60018, 0x34028000, 0xc0045be, 0xa7a20018, 
+0x8f830054, 0x8f820054, 0x10000002, 0x24630064, 
+0x8f820054, 0x621023, 0x2c420065, 0x1440fffc, 
+0x24040001, 0x24050001, 0xc00457c, 0x27a60018, 
+0x8f830054, 0x8f820054, 0x10000002, 0x24630064, 
+0x8f820054, 0x621023, 0x2c420065, 0x1440fffc, 
+0x24040001, 0x24050001, 0xc00457c, 0x27a60018, 
+0x8f830054, 0x8f820054, 0x10000002, 0x24630064, 
+0x8f820054, 0x621023, 0x2c420065, 0x1440fffc, 
+0x24040001, 0x3c060001, 0x24c66f24, 0xc00457c, 
+0x24050002, 0x8f830054, 0x8f820054, 0x10000002, 
+0x24630064, 0x8f820054, 0x621023, 0x2c420065, 
+0x1440fffc, 0x24040001, 0x24050003, 0x3c100001, 
+0x26106f26, 0xc00457c, 0x2003021, 0x97a60018, 
+0x3c070001, 0x94e76f24, 0x3c040001, 0x24846ae0, 
+0xafa00014, 0x96020000, 0x3c05000d, 0x34a50100, 
+0xc002b3b, 0xafa20010, 0x97a20018, 0x1040004d, 
+0x24036040, 0x96020000, 0x3042fff0, 0x1443000c, 
+0x24020020, 0x3c030001, 0x94636f24, 0x1462000b, 
+0x24027830, 0x24020003, 0x3c010001, 0xac226d94, 
+0x24020005, 0x3c010001, 0x1000003f, 0xac226f34, 
+0x3c030001, 0x94636f24, 0x24027830, 0x1462000c, 
+0x24030010, 0x3c020001, 0x94426f26, 0x3042fff0, 
+0x14430007, 0x24020003, 0x3c010001, 0xac226d94, 
+0x24020006, 0x3c010001, 0x1000002f, 0xac226f34, 
+0x3c020001, 0x8c426d94, 0x3c030001, 0x94636f24, 
+0x34420001, 0x3c010001, 0xac226d94, 0x24020015, 
+0x1462000b, 0x0, 0x3c020001, 0x94426f26, 
+0x3042fff0, 0x3843f420, 0x2c630001, 0x3842f430, 
+0x2c420001, 0x621825, 0x1460001b, 0x24020003, 
+0x3c030001, 0x94636f24, 0x24027810, 0x14620016, 
+0x24020002, 0x3c020001, 0x94426f26, 0x3042fff0, 
+0x14400011, 0x24020002, 0x1000000f, 0x24020004, 
+0x3c020001, 0x8c426d94, 0x34420008, 0x3c010001, 
+0xac226d94, 0x1000005e, 0x24020004, 0x3c020001, 
+0x8c426d94, 0x34420004, 0x3c010001, 0x100000af, 
+0xac226d94, 0x24020001, 0x3c010001, 0xac226f40, 
+0x3c020001, 0x8c426d94, 0x30420002, 0x144000b2, 
+0x3c09fff0, 0x24020e00, 0xaf820238, 0x8f840054, 
+0x8f820054, 0x24030008, 0x3c010001, 0xac236d98, 
+0x10000002, 0x248401f4, 0x8f820054, 0x821023, 
+0x2c4201f5, 0x1440fffc, 0x3c0200c8, 0x344201fb, 
+0xaf820238, 0x8f830054, 0x8f820054, 0x10000002, 
+0x246301f4, 0x8f820054, 0x621023, 0x2c4201f5, 
+0x1440fffc, 0x8021, 0x24120001, 0x24110009, 
+0xc004482, 0x0, 0x3c010001, 0xac326db4, 
+0xc004547, 0x0, 0x3c020001, 0x8c426db4, 
+0x1451fffb, 0x3c0200c8, 0x344201f6, 0xaf820238, 
+0x8f830054, 0x8f820054, 0x10000002, 0x2463000a, 
+0x8f820054, 0x621023, 0x2c42000b, 0x1440fffc, 
+0x0, 0x8f820220, 0x24040001, 0x34420002, 
+0xaf820220, 0x8f830200, 0x24057fff, 0x2402fffd, 
+0x621824, 0xaf830200, 0xaf840204, 0x8f830054, 
+0x8f820054, 0x10000002, 0x24630001, 0x8f820054, 
+0x621023, 0x2c420002, 0x1440fffc, 0x0, 
+0x8f820224, 0x14440005, 0x34028000, 0x42040, 
+0xa4102b, 0x1040fff0, 0x34028000, 0x1082ffa0, 
+0x26100001, 0x2e020014, 0x1440ffcd, 0x24020004, 
+0x3c010001, 0xac226d98, 0x8021, 0x24120009, 
+0x3c11ffff, 0x36313f7f, 0xc004482, 0x0, 
+0x24020001, 0x3c010001, 0xac226db4, 0xc004547, 
+0x0, 0x3c020001, 0x8c426db4, 0x1452fffb, 
+0x0, 0x8f820044, 0x511024, 0x34425080, 
+0xaf820044, 0x8f830054, 0x8f820054, 0x10000002, 
+0x2463000a, 0x8f820054, 0x621023, 0x2c42000b, 
+0x1440fffc, 0x0, 0x8f820044, 0x511024, 
+0x3442f080, 0xaf820044, 0x8f830054, 0x8f820054, 
+0x10000002, 0x2463000a, 0x8f820054, 0x621023, 
+0x2c42000b, 0x1440fffc, 0x0, 0x8f820220, 
+0x3c03f700, 0x431025, 0xaf820220, 0x8f830054, 
+0x8f820054, 0x10000002, 0x24630064, 0x8f820054, 
+0x621023, 0x2c420065, 0x1440fffc, 0x0, 
+0x8f820220, 0x24040001, 0x34420002, 0xaf820220, 
+0x8f830200, 0x24057fff, 0x2402fffd, 0x621824, 
+0xaf830200, 0xaf840204, 0x8f830054, 0x8f820054, 
+0x10000002, 0x24630001, 0x8f820054, 0x621023, 
+0x2c420002, 0x1440fffc, 0x0, 0x8f820224, 
+0x14440005, 0x34028000, 0x42040, 0xa4102b, 
+0x1040fff0, 0x34028000, 0x1082ff50, 0x26100001, 
+0x2e020064, 0x1440ffb0, 0x0, 0x3c020001, 
+0x8c426d94, 0x30420004, 0x14400007, 0x3c09fff0, 
+0x8f820044, 0x3c03ffff, 0x34633f7f, 0x431024, 
+0xaf820044, 0x3c09fff0, 0x3529bdc0, 0x3c060001, 
+0x8cc66d94, 0x3c040001, 0x24846ae0, 0x24020001, 
+0x3c010001, 0xac226d9c, 0x8f820054, 0x3c070001, 
+0x8ce76f40, 0x3c030001, 0x94636f24, 0x3c080001, 
+0x95086f26, 0x3c05000d, 0x34a50100, 0x3c010001, 
+0xac206d98, 0x491021, 0x3c010001, 0xac226f30, 
+0xafa30010, 0xc002b3b, 0xafa80014, 0x8fbf002c, 
+0x8fb20028, 0x8fb10024, 0x8fb00020, 0x3e00008, 
+0x27bd0030, 0x27bdffe8, 0x3c050001, 0x8ca56d98, 
+0x24060004, 0x24020001, 0x14a20014, 0xafbf0010, 
+0x3c020002, 0x8c428ffc, 0x30428000, 0x10400005, 
+0x3c04000f, 0x3c030001, 0x8c636f40, 0x10000005, 
+0x34844240, 0x3c040004, 0x3c030001, 0x8c636f40, 
+0x348493e0, 0x24020005, 0x14620016, 0x0, 
+0x3c04003d, 0x10000013, 0x34840900, 0x3c020002, 
+0x8c428ff8, 0x30428000, 0x10400005, 0x3c04001e, 
+0x3c030001, 0x8c636f40, 0x10000005, 0x34848480, 
+0x3c04000f, 0x3c030001, 0x8c636f40, 0x34844240, 
+0x24020005, 0x14620003, 0x0, 0x3c04007a, 
+0x34841200, 0x3c020001, 0x8c426f30, 0x8f830054, 
+0x441021, 0x431023, 0x44102b, 0x1440004c, 
+0x0, 0x3c020001, 0x8c426da0, 0x14400048, 
+0x0, 0x3c010001, 0x10c00025, 0xac206db0, 
+0x3c090001, 0x8d296d94, 0x24070001, 0x3c044000, 
+0x3c080002, 0x25088ffc, 0x250afffc, 0x52842, 
+0x14a00002, 0x24c6ffff, 0x24050008, 0xa91024, 
+0x10400010, 0x0, 0x14a70008, 0x0, 
+0x8d020000, 0x441024, 0x1040000a, 0x0, 
+0x3c010001, 0x10000007, 0xac256db0, 0x8d420000, 
+0x441024, 0x10400003, 0x0, 0x3c010001, 
+0xac276db0, 0x3c020001, 0x8c426db0, 0x6182b, 
+0x2c420001, 0x431024, 0x5440ffe5, 0x52842, 
+0x8f820054, 0x3c030001, 0x8c636db0, 0x3c010001, 
+0xac226f30, 0x1060003b, 0x24020005, 0x3c030001, 
+0x8c636f40, 0x3c010001, 0xac256d98, 0x14620012, 
+0x24020001, 0x3c020002, 0x8c428ff8, 0x3c032000, 
+0x34635000, 0x431024, 0x14400006, 0x24020001, 
+0x3c010001, 0xac206f1c, 0x3c010001, 0xac226d98, 
+0x24020001, 0x3c010001, 0xac226e24, 0x3c010001, 
+0xac226da4, 0x24020001, 0x3c010001, 0xac226d9c, 
+0x3c020001, 0x8c426db0, 0x1040001e, 0x0, 
+0x3c020001, 0x8c426d9c, 0x10400008, 0x24020001, 
+0x3c010001, 0xac206d9c, 0xaee204b8, 0x3c010001, 
+0xac206e1c, 0x3c010001, 0xac226dd4, 0x8ee304b8, 
+0x24020008, 0x10620005, 0x24020001, 0xc004239, 
+0x0, 0x1000000b, 0x0, 0x3c030001, 
+0x8c636d98, 0x10620007, 0x2402000e, 0x3c030002, 
+0x8c638f90, 0x10620003, 0x0, 0xc004e9c, 
+0x8f840220, 0x8fbf0010, 0x3e00008, 0x27bd0018, 
+0x27bdffe0, 0x3c03fdff, 0x3c040001, 0x8c846d98, 
+0x3c020001, 0x8c426dc0, 0x3463ffff, 0x283a024, 
+0x14820006, 0xafbf0018, 0x8ee304b8, 0x3c020001, 
+0x8c426dc4, 0x10620006, 0x0, 0x8ee204b8, 
+0x3c010001, 0xac246dc0, 0x3c010001, 0xac226dc4, 
+0x3c030001, 0x8c636d98, 0x24020002, 0x1062019c, 
+0x2c620003, 0x10400005, 0x24020001, 0x1062000a, 
+0x0, 0x10000226, 0x0, 0x24020004, 
+0x106200b6, 0x24020008, 0x1062010a, 0x24020001, 
+0x1000021f, 0x0, 0x8ee204b8, 0x2443ffff, 
+0x2c620008, 0x1040021c, 0x31080, 0x3c010001, 
+0x220821, 0x8c226af8, 0x400008, 0x0, 
+0x3c030001, 0x8c636f40, 0x24020005, 0x14620010, 
+0x0, 0x3c020001, 0x8c426da4, 0x10400008, 
+0x24020003, 0xc004482, 0x0, 0x24020002, 
+0xaee204b8, 0x3c010001, 0x10000002, 0xac206da4, 
+0xaee204b8, 0x3c010001, 0x10000203, 0xac206d30, 
+0xc004482, 0x0, 0x3c020001, 0x8c426da4, 
+0x3c010001, 0xac206d30, 0x1440017a, 0x24020002, 
+0x1000019d, 0x24020007, 0x3c030001, 0x8c636f40, 
+0x24020005, 0x14620003, 0x24020001, 0x3c010001, 
+0xac226dd0, 0xc0045ff, 0x0, 0x3c030001, 
+0x8c636dd0, 0x10000174, 0x24020011, 0x3c050001, 
+0x8ca56d98, 0x3c060002, 0x8cc68ffc, 0xc005104, 
+0x2021, 0x24020005, 0x3c010001, 0xac206da4, 
+0x100001e1, 0xaee204b8, 0x3c040001, 0x24846aec, 
+0x3c05000f, 0x34a50100, 0x3021, 0x3821, 
+0xafa00010, 0xc002b3b, 0xafa00014, 0x100001d6, 
+0x0, 0x8f820220, 0x3c030004, 0x431024, 
+0x14400175, 0x24020007, 0x8f830054, 0x3c020001, 
+0x8c426f28, 0x2463d8f0, 0x431023, 0x2c422710, 
+0x14400003, 0x24020001, 0x3c010001, 0xac226d9c, 
+0x3c020002, 0x8c428ffc, 0x30425000, 0x104001c2, 
+0x0, 0x8f820220, 0x30428000, 0x1040017d, 
+0x0, 0x10000175, 0x0, 0x3c050001, 
+0x8ca56d98, 0xc00529b, 0x2021, 0xc00551b, 
+0x2021, 0x3c030002, 0x8c638ff4, 0x46101b0, 
+0x24020001, 0x3c020008, 0x621024, 0x10400006, 
+0x0, 0x8f820214, 0x3c03ffff, 0x431024, 
+0x10000005, 0x3442251f, 0x8f820214, 0x3c03ffff, 
+0x431024, 0x3442241f, 0xaf820214, 0x8f820220, 
+0x3c030200, 0x34420002, 0xaf820220, 0x24020008, 
+0xaee204b8, 0x8f820220, 0x283a025, 0x3c030004, 
+0x431024, 0x14400016, 0x0, 0x3c020002, 
+0x8c428ffc, 0x30425000, 0x1040000d, 0x0, 
+0x8f820220, 0x30428000, 0x10400006, 0x0, 
+0x8f820220, 0x3c03ffff, 0x34637fff, 0x10000003, 
+0x431024, 0x8f820220, 0x34428000, 0xaf820220, 
+0x8f820220, 0x3c03f700, 0x431025, 0xaf820220, 
+0x3c030001, 0x8c636f40, 0x24020005, 0x1462000a, 
+0x0, 0x3c020001, 0x94426f26, 0x24429fbc, 
+0x2c420004, 0x10400004, 0x24040018, 0x24050002, 
+0xc004ddb, 0x24060020, 0xc003e6d, 0x0, 
+0x3c010001, 0x10000170, 0xac206e20, 0x8ee204b8, 
+0x2443ffff, 0x2c620008, 0x1040016b, 0x31080, 
+0x3c010001, 0x220821, 0x8c226b18, 0x400008, 
+0x0, 0xc004547, 0x0, 0x3c030001, 
+0x8c636db4, 0x100000e8, 0x24020009, 0x3c020002, 
+0x8c428ff8, 0x30424000, 0x10400004, 0x0, 
+0x8f820044, 0x10000006, 0x3442f080, 0x8f820044, 
+0x3c03ffff, 0x34633f7f, 0x431024, 0x3442a080, 
+0xaf820044, 0x8f830054, 0x100000ea, 0x24020004, 
+0x8f830054, 0x3c020001, 0x8c426f28, 0x2463d8f0, 
+0x431023, 0x2c422710, 0x14400147, 0x24020005, 
+0x100000d8, 0x0, 0x8f820220, 0x3c03f700, 
+0x431025, 0xaf820220, 0xaf800204, 0x3c010002, 
+0x100000d6, 0xac208fe0, 0x8f830054, 0x3c020001, 
+0x8c426f28, 0x2463fff6, 0x431023, 0x2c42000a, 
+0x14400135, 0x24020007, 0x100000d7, 0x0, 
+0xc003f50, 0x0, 0x1040012d, 0x24020001, 
+0x8f820214, 0x3c03ffff, 0x3c040001, 0x8c846f1c, 
+0x431024, 0x3442251f, 0xaf820214, 0x24020008, 
+0x10800005, 0xaee204b8, 0x3c020001, 0x8c426e44, 
+0x10400064, 0x24020001, 0x8f820220, 0x3c030008, 
+0x431024, 0x1040006a, 0x3c020200, 0x10000078, 
+0x0, 0x8ee204b8, 0x2443ffff, 0x2c620007, 
+0x10400115, 0x31080, 0x3c010001, 0x220821, 
+0x8c226b38, 0x400008, 0x0, 0xc003daf, 
+0x0, 0x3c010001, 0xac206d9c, 0xaf800204, 
+0x3c010002, 0xc004482, 0xac208fe0, 0x24020001, 
+0x3c010001, 0xac226db4, 0x24020002, 0x10000102, 
+0xaee204b8, 0xc004547, 0x0, 0x3c030001, 
+0x8c636db4, 0x10000084, 0x24020009, 0x3c020002, 
+0x8c428ff8, 0x30424000, 0x10400003, 0x3c0200c8, 
+0x10000002, 0x344201f6, 0x344201fe, 0xaf820238, 
+0x8f830054, 0x1000008b, 0x24020004, 0x8f830054, 
+0x3c020001, 0x8c426f28, 0x2463d8f0, 0x431023, 
+0x2c422710, 0x144000e8, 0x24020005, 0x10000079, 
+0x0, 0x8f820220, 0x3c03f700, 0x431025, 
+0xaf820220, 0xaf800204, 0x3c010002, 0x10000077, 
+0xac208fe0, 0x8f830054, 0x3c020001, 0x8c426f28, 
+0x2463fff6, 0x431023, 0x2c42000a, 0x144000d6, 
+0x24020007, 0x10000078, 0x0, 0xc003f50, 
+0x0, 0x104000ce, 0x24020001, 0x8f820214, 
+0x3c03ffff, 0x3c040001, 0x8c846f1c, 0x431024, 
+0x3442251f, 0xaf820214, 0x24020008, 0x1080000f, 
+0xaee204b8, 0x3c020001, 0x8c426e44, 0x1440000b, 
+0x0, 0x8f820220, 0x34420002, 0xaf820220, 
+0x24020001, 0x3c010002, 0xac228f90, 0xc004e9c, 
+0x8f840220, 0x10000016, 0x0, 0x8f820220, 
+0x3c030008, 0x431024, 0x14400011, 0x3c020200, 
+0x282a025, 0x2402000e, 0x3c010002, 0xac228f90, 
+0xc00551b, 0x2021, 0x8f820220, 0x34420002, 
+0xc003e6d, 0xaf820220, 0x3c050001, 0x8ca56d98, 
+0xc00529b, 0x2021, 0x100000a3, 0x0, 
+0x3c020001, 0x8c426e44, 0x1040009f, 0x0, 
+0x3c020001, 0x8c426e40, 0x2442ffff, 0x3c010001, 
+0xac226e40, 0x14400098, 0x24020002, 0x3c010001, 
+0xac206e44, 0x3c010001, 0x10000093, 0xac226e40, 
+0x8ee204b8, 0x2443ffff, 0x2c620007, 0x1040008e, 
+0x31080, 0x3c010001, 0x220821, 0x8c226b58, 
+0x400008, 0x0, 0x3c020001, 0x8c426da4, 
+0x10400018, 0x24020005, 0xc004482, 0x0, 
+0x24020002, 0xaee204b8, 0x3c010001, 0x1000007e, 
+0xac206da4, 0xc004963, 0x0, 0x3c030001, 
+0x8c636dd4, 0x24020006, 0x14620077, 0x24020003, 
+0x10000075, 0xaee204b8, 0x3c050001, 0x8ca56d98, 
+0x3c060002, 0x8cc68ff8, 0xc005104, 0x2021, 
+0x24020005, 0x1000006c, 0xaee204b8, 0x8f820220, 
+0x3c03f700, 0x431025, 0xaf820220, 0x8f830054, 
+0x24020006, 0xaee204b8, 0x3c010001, 0x10000062, 
+0xac236f28, 0x8f820220, 0x3c030004, 0x431024, 
+0x10400003, 0x24020007, 0x1000005b, 0xaee204b8, 
+0x8f830054, 0x3c020001, 0x8c426f28, 0x2463d8f0, 
+0x431023, 0x2c422710, 0x14400003, 0x24020001, 
+0x3c010001, 0xac226d9c, 0x3c020002, 0x8c428ff8, 
+0x30425000, 0x1040004c, 0x0, 0x8f820220, 
+0x30428000, 0x10400007, 0x0, 0x8f820220, 
+0x3c03ffff, 0x34637fff, 0x431024, 0x10000042, 
+0xaf820220, 0x8f820220, 0x34428000, 0x1000003e, 
+0xaf820220, 0x3c050001, 0x8ca56d98, 0xc00529b, 
+0x2021, 0xc00551b, 0x2021, 0x3c020002, 
+0x8c428ff0, 0x4410032, 0x24020001, 0x8f820214, 
+0x3c03ffff, 0x431024, 0x3442251f, 0xaf820214, 
+0x24020008, 0xaee204b8, 0x8f820220, 0x34420002, 
+0xaf820220, 0x8f820220, 0x3c030004, 0x431024, 
+0x14400016, 0x0, 0x3c020002, 0x8c428ff8, 
+0x30425000, 0x1040000d, 0x0, 0x8f820220, 
+0x30428000, 0x10400006, 0x0, 0x8f820220, 
+0x3c03ffff, 0x34637fff, 0x10000003, 0x431024, 
+0x8f820220, 0x34428000, 0xaf820220, 0x8f820220, 
+0x3c03f700, 0x431025, 0xaf820220, 0x3c020001, 
+0x94426f26, 0x24429fbc, 0x2c420004, 0x10400004, 
+0x24040018, 0x24050002, 0xc004ddb, 0x24060020, 
+0xc003e6d, 0x0, 0x10000003, 0x0, 
+0x3c010001, 0xac226d9c, 0x8fbf0018, 0x3e00008, 
+0x27bd0020, 0x8f820200, 0x8f820220, 0x8f820220, 
+0x34420004, 0xaf820220, 0x8f820200, 0x3c050001, 
+0x8ca56d98, 0x34420004, 0xaf820200, 0x24020002, 
+0x10a2004b, 0x2ca20003, 0x10400005, 0x24020001, 
+0x10a2000a, 0x0, 0x100000b1, 0x0, 
+0x24020004, 0x10a20072, 0x24020008, 0x10a20085, 
+0x3c02f0ff, 0x100000aa, 0x0, 0x8f830050, 
+0x3c02f0ff, 0x3442ffff, 0x3c040001, 0x8c846f40, 
+0x621824, 0x3c020700, 0x621825, 0x24020e00, 
+0x2484fffb, 0x2c840002, 0xaf830050, 0xaf850200, 
+0xaf850220, 0x14800006, 0xaf820238, 0x8f820044, 
+0x3c03ffff, 0x34633f7f, 0x431024, 0xaf820044, 
+0x3c030001, 0x8c636f40, 0x24020005, 0x14620004, 
+0x0, 0x8f820044, 0x34425000, 0xaf820044, 
+0x3c020001, 0x8c426d88, 0x3c030001, 0x8c636f40, 
+0x34420022, 0x2463fffc, 0x2c630002, 0x1460000c, 
+0xaf820200, 0x3c020001, 0x8c426dac, 0x3c030001, 
+0x8c636d90, 0x3c040001, 0x8c846d8c, 0x34428000, 
+0x621825, 0x641825, 0x1000000a, 0x34620002, 
+0x3c020001, 0x8c426d90, 0x3c030001, 0x8c636dac, 
+0x3c040001, 0x8c846d8c, 0x431025, 0x441025, 
+0x34420002, 0xaf820220, 0x1000002f, 0x24020001, 
+0x24020e01, 0xaf820238, 0x8f830050, 0x3c02f0ff, 
+0x3442ffff, 0x3c040001, 0x8c846f1c, 0x621824, 
+0x3c020d00, 0x621825, 0x24020001, 0xaf830050, 
+0xaf820200, 0xaf820220, 0x10800005, 0x3c033f00, 
+0x3c020001, 0x8c426d80, 0x10000004, 0x34630070, 
+0x3c020001, 0x8c426d80, 0x34630072, 0x431025, 
+0xaf820200, 0x3c030001, 0x8c636d84, 0x3c02f700, 
+0x621825, 0x3c020001, 0x8c426d90, 0x3c040001, 
+0x8c846dac, 0x3c050001, 0x8ca56f40, 0x431025, 
+0x441025, 0xaf820220, 0x24020005, 0x14a20006, 
+0x24020001, 0x8f820044, 0x2403afff, 0x431024, 
+0xaf820044, 0x24020001, 0x1000003d, 0xaf820238, 
+0x8f830050, 0x3c02f0ff, 0x3442ffff, 0x3c040001, 
+0x8c846f1c, 0x621824, 0x3c020a00, 0x621825, 
+0x24020001, 0xaf830050, 0xaf820200, 0x1080001e, 
+0xaf820220, 0x3c020001, 0x8c426e44, 0x1440001a, 
+0x3c033f00, 0x3c020001, 0x8c426d80, 0x1000001a, 
+0x346300e0, 0x8f830050, 0x3c040001, 0x8c846f1c, 
+0x3442ffff, 0x621824, 0x1080000f, 0xaf830050, 
+0x3c020001, 0x8c426e44, 0x1440000b, 0x3c043f00, 
+0x3c030001, 0x8c636d80, 0x348400e0, 0x24020001, 
+0xaf820200, 0xaf820220, 0x641825, 0xaf830200, 
+0x10000008, 0x3c05f700, 0x3c020001, 0x8c426d80, 
+0x3c033f00, 0x346300e2, 0x431025, 0xaf820200, 
+0x3c05f700, 0x34a58000, 0x3c030001, 0x8c636d84, 
+0x3c020001, 0x8c426d90, 0x3c040001, 0x8c846dac, 
+0x651825, 0x431025, 0x441025, 0xaf820220, 
+0x3e00008, 0x0, 0x3c030001, 0x8c636db4, 
+0x3c020001, 0x8c426db8, 0x10620003, 0x24020002, 
+0x3c010001, 0xac236db8, 0x1062001d, 0x2c620003, 
+0x10400025, 0x24020001, 0x14620023, 0x24020004, 
+0x3c030001, 0x8c636d98, 0x10620006, 0x24020008, 
+0x1462000c, 0x3c0200c8, 0x344201fb, 0x10000009, 
+0xaf820238, 0x24020e01, 0xaf820238, 0x8f820044, 
+0x3c03ffff, 0x34633f7f, 0x431024, 0x34420080, 
+0xaf820044, 0x8f830054, 0x24020002, 0x3c010001, 
+0xac226db4, 0x3c010001, 0x1000000b, 0xac236f2c, 
+0x8f830054, 0x3c020001, 0x8c426f2c, 0x2463d8f0, 
+0x431023, 0x2c422710, 0x14400003, 0x24020009, 
+0x3c010001, 0xac226db4, 0x3e00008, 0x0, 
+0x0, 0x0, 0x0, 0x27bdffd8, 
+0xafb20018, 0x809021, 0xafb3001c, 0xa09821, 
+0xafb10014, 0xc08821, 0xafb00010, 0x8021, 
+0xafbf0020, 0xa6200000, 0xc004d78, 0x24040001, 
+0x26100001, 0x2e020020, 0x1440fffb, 0x0, 
+0xc004d78, 0x2021, 0xc004d78, 0x24040001, 
+0xc004d78, 0x24040001, 0xc004d78, 0x2021, 
+0x24100010, 0x2501024, 0x10400002, 0x2021, 
+0x24040001, 0xc004d78, 0x108042, 0x1600fffa, 
+0x2501024, 0x24100010, 0x2701024, 0x10400002, 
+0x2021, 0x24040001, 0xc004d78, 0x108042, 
+0x1600fffa, 0x2701024, 0xc004db9, 0x34108000, 
+0xc004db9, 0x0, 0xc004d58, 0x0, 
+0x50400005, 0x108042, 0x96220000, 0x501025, 
+0xa6220000, 0x108042, 0x1600fff7, 0x0, 
+0xc004db9, 0x0, 0x8fbf0020, 0x8fb3001c, 
+0x8fb20018, 0x8fb10014, 0x8fb00010, 0x3e00008, 
+0x27bd0028, 0x27bdffd8, 0xafb10014, 0x808821, 
+0xafb20018, 0xa09021, 0xafb3001c, 0xc09821, 
+0xafb00010, 0x8021, 0xafbf0020, 0xc004d78, 
+0x24040001, 0x26100001, 0x2e020020, 0x1440fffb, 
+0x0, 0xc004d78, 0x2021, 0xc004d78, 
+0x24040001, 0xc004d78, 0x2021, 0xc004d78, 
+0x24040001, 0x24100010, 0x2301024, 0x10400002, 
+0x2021, 0x24040001, 0xc004d78, 0x108042, 
+0x1600fffa, 0x2301024, 0x24100010, 0x2501024, 
+0x10400002, 0x2021, 0x24040001, 0xc004d78, 
+0x108042, 0x1600fffa, 0x2501024, 0xc004d78, 
+0x24040001, 0xc004d78, 0x2021, 0x34108000, 
+0x96620000, 0x501024, 0x10400002, 0x2021, 
+0x24040001, 0xc004d78, 0x108042, 0x1600fff8, 
+0x0, 0xc004db9, 0x0, 0x8fbf0020, 
+0x8fb3001c, 0x8fb20018, 0x8fb10014, 0x8fb00010, 
+0x3e00008, 0x27bd0028, 0x3c040001, 0x8c846dd0, 
+0x3c020001, 0x8c426e18, 0x27bdffd8, 0xafbf0020, 
+0xafb1001c, 0x10820003, 0xafb00018, 0x3c010001, 
+0xac246e18, 0x3c030001, 0x8c636f40, 0x24020005, 
+0x14620005, 0x2483ffff, 0xc004963, 0x0, 
+0x1000034c, 0x0, 0x2c620013, 0x10400349, 
+0x31080, 0x3c010001, 0x220821, 0x8c226b80, 
+0x400008, 0x0, 0xc004db9, 0x8021, 
+0x34028000, 0xa7a20010, 0x27b10010, 0xc004d78, 
+0x24040001, 0x26100001, 0x2e020020, 0x1440fffb, 
+0x0, 0xc004d78, 0x2021, 0xc004d78, 
+0x24040001, 0xc004d78, 0x2021, 0xc004d78, 
+0x24040001, 0x24100010, 0x32020001, 0x10400002, 
+0x2021, 0x24040001, 0xc004d78, 0x108042, 
+0x1600fffa, 0x32020001, 0x24100010, 0xc004d78, 
+0x2021, 0x108042, 0x1600fffc, 0x0, 
+0xc004d78, 0x24040001, 0xc004d78, 0x2021, 
+0x34108000, 0x96220000, 0x501024, 0x10400002, 
+0x2021, 0x24040001, 0xc004d78, 0x108042, 
+0x1600fff8, 0x0, 0xc004db9, 0x0, 
+0x1000030e, 0x24020002, 0x27b10010, 0xa7a00010, 
+0x8021, 0xc004d78, 0x24040001, 0x26100001, 
+0x2e020020, 0x1440fffb, 0x0, 0xc004d78, 
+0x2021, 0xc004d78, 0x24040001, 0xc004d78, 
+0x24040001, 0xc004d78, 0x2021, 0x24100010, 
+0x32020001, 0x10400002, 0x2021, 0x24040001, 
+0xc004d78, 0x108042, 0x1600fffa, 0x32020001, 
+0x24100010, 0xc004d78, 0x2021, 0x108042, 
+0x1600fffc, 0x0, 0xc004db9, 0x34108000, 
+0xc004db9, 0x0, 0xc004d58, 0x0, 
+0x50400005, 0x108042, 0x96220000, 0x501025, 
+0xa6220000, 0x108042, 0x1600fff7, 0x0, 
+0xc004db9, 0x0, 0x97a20010, 0x30428000, 
+0x144002dc, 0x24020003, 0x100002d8, 0x0, 
+0x24021200, 0xa7a20010, 0x27b10010, 0x8021, 
+0xc004d78, 0x24040001, 0x26100001, 0x2e020020, 
+0x1440fffb, 0x0, 0xc004d78, 0x2021, 
+0xc004d78, 0x24040001, 0xc004d78, 0x2021, 
+0xc004d78, 0x24040001, 0x24100010, 0x32020001, 
+0x10400002, 0x2021, 0x24040001, 0xc004d78, 
+0x108042, 0x1600fffa, 0x32020001, 0x24100010, 
+0xc004d78, 0x2021, 0x108042, 0x1600fffc, 
+0x0, 0xc004d78, 0x24040001, 0xc004d78, 
+0x2021, 0x34108000, 0x96220000, 0x501024, 
+0x10400002, 0x2021, 0x24040001, 0xc004d78, 
+0x108042, 0x1600fff8, 0x0, 0xc004db9, 
+0x0, 0x8f830054, 0x10000296, 0x24020004, 
+0x8f830054, 0x3c020001, 0x8c426f3c, 0x2463ff9c, 
+0x431023, 0x2c420064, 0x1440029e, 0x24020002, 
+0x3c030001, 0x8c636f40, 0x10620297, 0x2c620003, 
+0x14400296, 0x24020011, 0x24020003, 0x10620005, 
+0x24020004, 0x10620291, 0x2402000f, 0x1000028f, 
+0x24020011, 0x1000028d, 0x24020005, 0x24020014, 
+0xa7a20010, 0x27b10010, 0x8021, 0xc004d78, 
+0x24040001, 0x26100001, 0x2e020020, 0x1440fffb, 
+0x0, 0xc004d78, 0x2021, 0xc004d78, 
+0x24040001, 0xc004d78, 0x2021, 0xc004d78, 
+0x24040001, 0x24100010, 0x32020001, 0x10400002, 
+0x2021, 0x24040001, 0xc004d78, 0x108042, 
+0x1600fffa, 0x32020001, 0x24100010, 0x32020012, 
+0x10400002, 0x2021, 0x24040001, 0xc004d78, 
+0x108042, 0x1600fffa, 0x32020012, 0xc004d78, 
+0x24040001, 0xc004d78, 0x2021, 0x34108000, 
+0x96220000, 0x501024, 0x10400002, 0x2021, 
+0x24040001, 0xc004d78, 0x108042, 0x1600fff8, 
+0x0, 0xc004db9, 0x0, 0x8f830054, 
+0x10000248, 0x24020006, 0x8f830054, 0x3c020001, 
+0x8c426f3c, 0x2463ff9c, 0x431023, 0x2c420064, 
+0x14400250, 0x24020007, 0x1000024c, 0x0, 
+0x24020006, 0xa7a20010, 0x27b10010, 0x8021, 
+0xc004d78, 0x24040001, 0x26100001, 0x2e020020, 
+0x1440fffb, 0x0, 0xc004d78, 0x2021, 
+0xc004d78, 0x24040001, 0xc004d78, 0x2021, 
+0xc004d78, 0x24040001, 0x24100010, 0x32020001, 
+0x10400002, 0x2021, 0x24040001, 0xc004d78, 
+0x108042, 0x1600fffa, 0x32020001, 0x24100010, 
+0x32020013, 0x10400002, 0x2021, 0x24040001, 
+0xc004d78, 0x108042, 0x1600fffa, 0x32020013, 
+0xc004d78, 0x24040001, 0xc004d78, 0x2021, 
+0x34108000, 0x96220000, 0x501024, 0x10400002, 
+0x2021, 0x24040001, 0xc004d78, 0x108042, 
+0x1600fff8, 0x0, 0xc004db9, 0x0, 
+0x8f830054, 0x10000207, 0x24020008, 0x8f830054, 
+0x3c020001, 0x8c426f3c, 0x2463ff9c, 0x431023, 
+0x2c420064, 0x1440020f, 0x24020009, 0x1000020b, 
+0x0, 0x27b10010, 0xa7a00010, 0x8021, 
+0xc004d78, 0x24040001, 0x26100001, 0x2e020020, 
+0x1440fffb, 0x0, 0xc004d78, 0x2021, 
+0xc004d78, 0x24040001, 0xc004d78, 0x24040001, 
+0xc004d78, 0x2021, 0x24100010, 0x32020001, 
+0x10400002, 0x2021, 0x24040001, 0xc004d78, 
+0x108042, 0x1600fffa, 0x32020001, 0x24100010, 
+0x32020018, 0x10400002, 0x2021, 0x24040001, 
+0xc004d78, 0x108042, 0x1600fffa, 0x32020018, 
+0xc004db9, 0x34108000, 0xc004db9, 0x0, 
+0xc004d58, 0x0, 0x50400005, 0x108042, 
+0x96220000, 0x501025, 0xa6220000, 0x108042, 
+0x1600fff7, 0x0, 0xc004db9, 0x8021, 
+0x97a20010, 0x27b10010, 0x34420001, 0xa7a20010, 
+0xc004d78, 0x24040001, 0x26100001, 0x2e020020, 
+0x1440fffb, 0x0, 0xc004d78, 0x2021, 
+0xc004d78, 0x24040001, 0xc004d78, 0x2021, 
+0xc004d78, 0x24040001, 0x24100010, 0x32020001, 
+0x10400002, 0x2021, 0x24040001, 0xc004d78, 
+0x108042, 0x1600fffa, 0x32020001, 0x24100010, 
+0x32020018, 0x10400002, 0x2021, 0x24040001, 
+0xc004d78, 0x108042, 0x1600fffa, 0x32020018, 
+0xc004d78, 0x24040001, 0xc004d78, 0x2021, 
+0x34108000, 0x96220000, 0x501024, 0x10400002, 
+0x2021, 0x24040001, 0xc004d78, 0x108042, 
+0x1600fff8, 0x0, 0xc004db9, 0x0, 
+0x8f830054, 0x10000193, 0x2402000a, 0x8f830054, 
+0x3c020001, 0x8c426f3c, 0x2463ff9c, 0x431023, 
+0x2c420064, 0x1440019b, 0x2402000b, 0x10000197, 
+0x0, 0x27b10010, 0xa7a00010, 0x8021, 
+0xc004d78, 0x24040001, 0x26100001, 0x2e020020, 
+0x1440fffb, 0x0, 0xc004d78, 0x2021, 
+0xc004d78, 0x24040001, 0xc004d78, 0x24040001, 
+0xc004d78, 0x2021, 0x24100010, 0x32020001, 
+0x10400002, 0x2021, 0x24040001, 0xc004d78, 
+0x108042, 0x1600fffa, 0x32020001, 0x24100010, 
+0x32020017, 0x10400002, 0x2021, 0x24040001, 
+0xc004d78, 0x108042, 0x1600fffa, 0x32020017, 
+0xc004db9, 0x34108000, 0xc004db9, 0x0, 
+0xc004d58, 0x0, 0x50400005, 0x108042, 
+0x96220000, 0x501025, 0xa6220000, 0x108042, 
+0x1600fff7, 0x0, 0xc004db9, 0x8021, 
+0x97a20010, 0x27b10010, 0x34420700, 0xa7a20010, 
+0xc004d78, 0x24040001, 0x26100001, 0x2e020020, 
+0x1440fffb, 0x0, 0xc004d78, 0x2021, 
+0xc004d78, 0x24040001, 0xc004d78, 0x2021, 
+0xc004d78, 0x24040001, 0x24100010, 0x32020001, 
+0x10400002, 0x2021, 0x24040001, 0xc004d78, 
+0x108042, 0x1600fffa, 0x32020001, 0x24100010, 
+0x32020017, 0x10400002, 0x2021, 0x24040001, 
+0xc004d78, 0x108042, 0x1600fffa, 0x32020017, 
+0xc004d78, 0x24040001, 0xc004d78, 0x2021, 
+0x34108000, 0x96220000, 0x501024, 0x10400002, 
+0x2021, 0x24040001, 0xc004d78, 0x108042, 
+0x1600fff8, 0x0, 0xc004db9, 0x0, 
+0x8f830054, 0x1000011f, 0x2402000c, 0x8f830054, 
+0x3c020001, 0x8c426f3c, 0x2463ff9c, 0x431023, 
+0x2c420064, 0x14400127, 0x24020012, 0x10000123, 
+0x0, 0x27b10010, 0xa7a00010, 0x8021, 
+0xc004d78, 0x24040001, 0x26100001, 0x2e020020, 
+0x1440fffb, 0x0, 0xc004d78, 0x2021, 
+0xc004d78, 0x24040001, 0xc004d78, 0x24040001, 
+0xc004d78, 0x2021, 0x24100010, 0x32020001, 
+0x10400002, 0x2021, 0x24040001, 0xc004d78, 
+0x108042, 0x1600fffa, 0x32020001, 0x24100010, 
+0x32020014, 0x10400002, 0x2021, 0x24040001, 
+0xc004d78, 0x108042, 0x1600fffa, 0x32020014, 
+0xc004db9, 0x34108000, 0xc004db9, 0x0, 
+0xc004d58, 0x0, 0x50400005, 0x108042, 
+0x96220000, 0x501025, 0xa6220000, 0x108042, 
+0x1600fff7, 0x0, 0xc004db9, 0x8021, 
+0x97a20010, 0x27b10010, 0x34420010, 0xa7a20010, 
+0xc004d78, 0x24040001, 0x26100001, 0x2e020020, 
+0x1440fffb, 0x0, 0xc004d78, 0x2021, 
+0xc004d78, 0x24040001, 0xc004d78, 0x2021, 
+0xc004d78, 0x24040001, 0x24100010, 0x32020001, 
+0x10400002, 0x2021, 0x24040001, 0xc004d78, 
+0x108042, 0x1600fffa, 0x32020001, 0x24100010, 
+0x32020014, 0x10400002, 0x2021, 0x24040001, 
+0xc004d78, 0x108042, 0x1600fffa, 0x32020014, 
+0xc004d78, 0x24040001, 0xc004d78, 0x2021, 
+0x34108000, 0x96220000, 0x501024, 0x10400002, 
+0x2021, 0x24040001, 0xc004d78, 0x108042, 
+0x1600fff8, 0x0, 0xc004db9, 0x0, 
+0x8f830054, 0x100000ab, 0x24020013, 0x8f830054, 
+0x3c020001, 0x8c426f3c, 0x2463ff9c, 0x431023, 
+0x2c420064, 0x144000b3, 0x2402000d, 0x100000af, 
+0x0, 0x27b10010, 0xa7a00010, 0x8021, 
+0xc004d78, 0x24040001, 0x26100001, 0x2e020020, 
+0x1440fffb, 0x0, 0xc004d78, 0x2021, 
+0xc004d78, 0x24040001, 0xc004d78, 0x24040001, 
+0xc004d78, 0x2021, 0x24100010, 0x32020001, 
+0x10400002, 0x2021, 0x24040001, 0xc004d78, 
+0x108042, 0x1600fffa, 0x32020001, 0x24100010, 
+0x32020018, 0x10400002, 0x2021, 0x24040001, 
+0xc004d78, 0x108042, 0x1600fffa, 0x32020018, 
+0xc004db9, 0x34108000, 0xc004db9, 0x0, 
+0xc004d58, 0x0, 0x50400005, 0x108042, 
+0x96220000, 0x501025, 0xa6220000, 0x108042, 
+0x1600fff7, 0x0, 0xc004db9, 0x8021, 
+0x97a20010, 0x27b10010, 0x3042fffe, 0xa7a20010, 
+0xc004d78, 0x24040001, 0x26100001, 0x2e020020, 
+0x1440fffb, 0x0, 0xc004d78, 0x2021, 
+0xc004d78, 0x24040001, 0xc004d78, 0x2021, 
+0xc004d78, 0x24040001, 0x24100010, 0x32020001, 
+0x10400002, 0x2021, 0x24040001, 0xc004d78, 
+0x108042, 0x1600fffa, 0x32020001, 0x24100010, 
+0x32020018, 0x10400002, 0x2021, 0x24040001, 
+0xc004d78, 0x108042, 0x1600fffa, 0x32020018, 
+0xc004d78, 0x24040001, 0xc004d78, 0x2021, 
+0x34108000, 0x96220000, 0x501024, 0x10400002, 
+0x2021, 0x24040001, 0xc004d78, 0x108042, 
+0x1600fff8, 0x0, 0xc004db9, 0x0, 
+0x8f830054, 0x10000037, 0x2402000e, 0x24020840, 
+0xa7a20010, 0x27b10010, 0x8021, 0xc004d78, 
+0x24040001, 0x26100001, 0x2e020020, 0x1440fffb, 
+0x0, 0xc004d78, 0x2021, 0xc004d78, 
+0x24040001, 0xc004d78, 0x2021, 0xc004d78, 
+0x24040001, 0x24100010, 0x32020001, 0x10400002, 
+0x2021, 0x24040001, 0xc004d78, 0x108042, 
+0x1600fffa, 0x32020001, 0x24100010, 0x32020013, 
+0x10400002, 0x2021, 0x24040001, 0xc004d78, 
+0x108042, 0x1600fffa, 0x32020013, 0xc004d78, 
+0x24040001, 0xc004d78, 0x2021, 0x34108000, 
+0x96220000, 0x501024, 0x10400002, 0x2021, 
+0x24040001, 0xc004d78, 0x108042, 0x1600fff8, 
+0x0, 0xc004db9, 0x0, 0x8f830054, 
+0x24020010, 0x3c010001, 0xac226dd0, 0x3c010001, 
+0x1000000c, 0xac236f3c, 0x8f830054, 0x3c020001, 
+0x8c426f3c, 0x2463ff9c, 0x431023, 0x2c420064, 
+0x14400004, 0x0, 0x24020011, 0x3c010001, 
+0xac226dd0, 0x8fbf0020, 0x8fb1001c, 0x8fb00018, 
+0x3e00008, 0x27bd0028, 0x3c030001, 0x8c636d98, 
+0x27bdffc8, 0x24020002, 0xafbf0034, 0xafb20030, 
+0xafb1002c, 0x14620004, 0xafb00028, 0x3c120002, 
+0x10000003, 0x8e528ff8, 0x3c120002, 0x8e528ffc, 
+0x3c030001, 0x8c636dd4, 0x3c020001, 0x8c426e1c, 
+0x50620004, 0x2463ffff, 0x3c010001, 0xac236e1c, 
+0x2463ffff, 0x2c620006, 0x10400377, 0x31080, 
+0x3c010001, 0x220821, 0x8c226bd8, 0x400008, 
+0x0, 0x2021, 0x2821, 0xc004ddb, 
+0x34068000, 0x24040010, 0x24050002, 0x24060002, 
+0x24020002, 0xc004ddb, 0xa7a20018, 0x24020002, 
+0x3c010001, 0x10000364, 0xac226dd4, 0x27b10018, 
+0xa7a00018, 0x8021, 0xc004d78, 0x24040001, 
+0x26100001, 0x2e020020, 0x1440fffb, 0x0, 
+0xc004d78, 0x2021, 0xc004d78, 0x24040001, 
+0xc004d78, 0x24040001, 0xc004d78, 0x2021, 
+0x24100010, 0x32020001, 0x10400002, 0x2021, 
+0x24040001, 0xc004d78, 0x108042, 0x1600fffa, 
+0x32020001, 0x24100010, 0xc004d78, 0x2021, 
+0x108042, 0x1600fffc, 0x0, 0xc004db9, 
+0x34108000, 0xc004db9, 0x0, 0xc004d58, 
+0x0, 0x50400005, 0x108042, 0x96220000, 
+0x501025, 0xa6220000, 0x108042, 0x1600fff7, 
+0x0, 0xc004db9, 0x0, 0x97a20018, 
+0x30428000, 0x14400004, 0x24020003, 0x3c010001, 
+0xac226dd4, 0x24020003, 0x3c010001, 0x1000032a, 
+0xac226dd4, 0x24040010, 0x24050002, 0x24060002, 
+0x24020002, 0xc004ddb, 0xa7a20018, 0x3c030001, 
+0x8c636e20, 0x24020001, 0x146201e1, 0x8021, 
+0x27b10018, 0xa7a00018, 0xc004d78, 0x24040001, 
+0x26100001, 0x2e020020, 0x1440fffb, 0x0, 
+0xc004d78, 0x2021, 0xc004d78, 0x24040001, 
+0xc004d78, 0x24040001, 0xc004d78, 0x2021, 
+0x24100010, 0x32020001, 0x10400002, 0x2021, 
+0x24040001, 0xc004d78, 0x108042, 0x1600fffa, 
+0x32020001, 0x24100010, 0x32020018, 0x10400002, 
+0x2021, 0x24040001, 0xc004d78, 0x108042, 
+0x1600fffa, 0x32020018, 0xc004db9, 0x34108000, 
+0xc004db9, 0x0, 0xc004d58, 0x0, 
+0x50400005, 0x108042, 0x96220000, 0x501025, 
+0xa6220000, 0x108042, 0x1600fff7, 0x0, 
+0xc004db9, 0x8021, 0x27b10018, 0xa7a00018, 
+0xc004d78, 0x24040001, 0x26100001, 0x2e020020, 
+0x1440fffb, 0x0, 0xc004d78, 0x2021, 
+0xc004d78, 0x24040001, 0xc004d78, 0x24040001, 
+0xc004d78, 0x2021, 0x24100010, 0x32020001, 
+0x10400002, 0x2021, 0x24040001, 0xc004d78, 
+0x108042, 0x1600fffa, 0x32020001, 0x24100010, 
+0x32020018, 0x10400002, 0x2021, 0x24040001, 
+0xc004d78, 0x108042, 0x1600fffa, 0x32020018, 
+0xc004db9, 0x34108000, 0xc004db9, 0x0, 
+0xc004d58, 0x0, 0x50400005, 0x108042, 
+0x96220000, 0x501025, 0xa6220000, 0x108042, 
+0x1600fff7, 0x0, 0xc004db9, 0x8021, 
+0x24040018, 0x2821, 0xc004ddb, 0x24060404, 
+0xa7a0001a, 0xc004d78, 0x24040001, 0x26100001, 
+0x2e020020, 0x1440fffb, 0x0, 0xc004d78, 
+0x2021, 0xc004d78, 0x24040001, 0xc004d78, 
+0x24040001, 0xc004d78, 0x2021, 0x24100010, 
+0x32020001, 0x10400002, 0x2021, 0x24040001, 
+0xc004d78, 0x108042, 0x1600fffa, 0x32020001, 
+0x24100010, 0x32020018, 0x10400002, 0x2021, 
+0x24040001, 0xc004d78, 0x108042, 0x1600fffa, 
+0x32020018, 0xc004db9, 0x34108000, 0xc004db9, 
+0x0, 0xc004d58, 0x0, 0x50400005, 
+0x108042, 0x97a2001a, 0x501025, 0xa7a2001a, 
+0x108042, 0x1600fff7, 0x0, 0xc004db9, 
+0x8021, 0xa7a0001a, 0xc004d78, 0x24040001, 
+0x26100001, 0x2e020020, 0x1440fffb, 0x0, 
+0xc004d78, 0x2021, 0xc004d78, 0x24040001, 
+0xc004d78, 0x24040001, 0xc004d78, 0x2021, 
+0x24100010, 0x32020001, 0x10400002, 0x2021, 
+0x24040001, 0xc004d78, 0x108042, 0x1600fffa, 
+0x32020001, 0x24100010, 0x32020018, 0x10400002, 
+0x2021, 0x24040001, 0xc004d78, 0x108042, 
+0x1600fffa, 0x32020018, 0xc004db9, 0x34108000, 
+0xc004db9, 0x0, 0xc004d58, 0x0, 
+0x50400005, 0x108042, 0x97a2001a, 0x501025, 
+0xa7a2001a, 0x108042, 0x1600fff7, 0x0, 
+0xc004db9, 0x8021, 0xa7a0001c, 0xc004d78, 
+0x24040001, 0x26100001, 0x2e020020, 0x1440fffb, 
+0x0, 0xc004d78, 0x2021, 0xc004d78, 
+0x24040001, 0xc004d78, 0x24040001, 0xc004d78, 
+0x2021, 0x24100010, 0xc004d78, 0x2021, 
+0x108042, 0x1600fffc, 0x0, 0x24100010, 
+0x3202001e, 0x10400002, 0x2021, 0x24040001, 
+0xc004d78, 0x108042, 0x1600fffa, 0x3202001e, 
+0xc004db9, 0x34108000, 0xc004db9, 0x0, 
+0xc004d58, 0x0, 0x50400005, 0x108042, 
+0x97a2001c, 0x501025, 0xa7a2001c, 0x108042, 
+0x1600fff7, 0x0, 0xc004db9, 0x8021, 
+0xa7a0001c, 0xc004d78, 0x24040001, 0x26100001, 
+0x2e020020, 0x1440fffb, 0x0, 0xc004d78, 
+0x2021, 0xc004d78, 0x24040001, 0xc004d78, 
+0x24040001, 0xc004d78, 0x2021, 0x24100010, 
+0xc004d78, 0x2021, 0x108042, 0x1600fffc, 
+0x0, 0x24100010, 0x3202001e, 0x10400002, 
+0x2021, 0x24040001, 0xc004d78, 0x108042, 
+0x1600fffa, 0x3202001e, 0xc004db9, 0x34108000, 
+0xc004db9, 0x0, 0xc004d58, 0x0, 
+0x50400005, 0x108042, 0x97a2001c, 0x501025, 
+0xa7a2001c, 0x108042, 0x1600fff7, 0x0, 
+0xc004db9, 0x8021, 0x24020002, 0xa7a2001e, 
+0xc004d78, 0x24040001, 0x26100001, 0x2e020020, 
+0x1440fffb, 0x0, 0xc004d78, 0x2021, 
+0xc004d78, 0x24040001, 0xc004d78, 0x2021, 
+0xc004d78, 0x24040001, 0x24100010, 0xc004d78, 
+0x2021, 0x108042, 0x1600fffc, 0x0, 
+0x24100010, 0x3202001e, 0x10400002, 0x2021, 
+0x24040001, 0xc004d78, 0x108042, 0x1600fffa, 
+0x3202001e, 0xc004d78, 0x24040001, 0xc004d78, 
+0x2021, 0x34108000, 0x97a2001e, 0x501024, 
+0x10400002, 0x2021, 0x24040001, 0xc004d78, 
+0x108042, 0x1600fff8, 0x0, 0xc004db9, 
+0x8021, 0xa7a00020, 0xc004d78, 0x24040001, 
+0x26100001, 0x2e020020, 0x1440fffb, 0x0, 
+0xc004d78, 0x2021, 0xc004d78, 0x24040001, 
+0xc004d78, 0x24040001, 0xc004d78, 0x2021, 
+0x24100010, 0xc004d78, 0x2021, 0x108042, 
+0x1600fffc, 0x0, 0x24100010, 0x3202001e, 
+0x10400002, 0x2021, 0x24040001, 0xc004d78, 
+0x108042, 0x1600fffa, 0x3202001e, 0xc004db9, 
+0x34108000, 0xc004db9, 0x0, 0xc004d58, 
+0x0, 0x50400005, 0x108042, 0x97a20020, 
+0x501025, 0xa7a20020, 0x108042, 0x1600fff7, 
+0x0, 0xc004db9, 0x8021, 0xa7a00020, 
+0xc004d78, 0x24040001, 0x26100001, 0x2e020020, 
+0x1440fffb, 0x0, 0xc004d78, 0x2021, 
+0xc004d78, 0x24040001, 0xc004d78, 0x24040001, 
+0xc004d78, 0x2021, 0x24100010, 0xc004d78, 
+0x2021, 0x108042, 0x1600fffc, 0x0, 
+0x24100010, 0x3202001e, 0x10400002, 0x2021, 
+0x24040001, 0xc004d78, 0x108042, 0x1600fffa, 
+0x3202001e, 0xc004db9, 0x34108000, 0xc004db9, 
+0x0, 0xc004d58, 0x0, 0x50400005, 
+0x108042, 0x97a20020, 0x501025, 0xa7a20020, 
+0x108042, 0x1600fff7, 0x0, 0xc004db9, 
+0x8021, 0xa7a00022, 0xc004d78, 0x24040001, 
+0x26100001, 0x2e020020, 0x1440fffb, 0x0, 
+0xc004d78, 0x2021, 0xc004d78, 0x24040001, 
+0xc004d78, 0x2021, 0xc004d78, 0x24040001, 
+0x24100010, 0xc004d78, 0x2021, 0x108042, 
+0x1600fffc, 0x0, 0x24100010, 0xc004d78, 
+0x2021, 0x108042, 0x1600fffc, 0x0, 
+0xc004d78, 0x24040001, 0xc004d78, 0x2021, 
+0x34108000, 0x97a20022, 0x501024, 0x10400002, 
+0x2021, 0x24040001, 0xc004d78, 0x108042, 
+0x1600fff8, 0x0, 0xc004db9, 0x0, 
+0x24040018, 0x24050002, 0xc004ddb, 0x24060004, 
+0x3c100001, 0x8e106e24, 0x24020001, 0x1602011d, 
+0x0, 0x3c020001, 0x94426f26, 0x3c010001, 
+0xac206e24, 0x24429fbc, 0x2c420004, 0x1040000c, 
+0x24040009, 0x24050001, 0xc004ddb, 0x24060400, 
+0x24040018, 0x24050001, 0xc004ddb, 0x24060020, 
+0x24040018, 0x24050001, 0xc004ddb, 0x24062000, 
+0x3c024000, 0x2421024, 0x10400123, 0x3c022000, 
+0x2421024, 0x10400004, 0x0, 0x3c010001, 
+0x10000003, 0xac306f1c, 0x3c010001, 0xac206f1c, 
+0x3c030001, 0x8c636f34, 0x24020005, 0x146200f9, 
+0x0, 0x3c020001, 0x8c426f1c, 0x10400067, 
+0x3c020004, 0x2421024, 0x10400011, 0xa7a00018, 
+0x3c020008, 0x2421024, 0x10400002, 0x24020200, 
+0xa7a20018, 0x3c020010, 0x2421024, 0x10400004, 
+0x0, 0x97a20018, 0x34420100, 0xa7a20018, 
+0x97a60018, 0x24040009, 0x10000004, 0x2821, 
+0x24040009, 0x2821, 0x3021, 0xc004ddb, 
+0x0, 0x24020001, 0xa7a2001a, 0x3c020008, 
+0x2421024, 0x1040000c, 0x3c020002, 0x2421024, 
+0x10400002, 0x24020101, 0xa7a2001a, 0x3c020001, 
+0x2421024, 0x10400005, 0x3c020010, 0x97a2001a, 
+0x34420040, 0xa7a2001a, 0x3c020010, 0x2421024, 
+0x1040000e, 0x3c020002, 0x2421024, 0x10400005, 
+0x3c020001, 0x97a2001a, 0x34420080, 0xa7a2001a, 
+0x3c020001, 0x2421024, 0x10400005, 0x3c0300a0, 
+0x97a2001a, 0x34420020, 0xa7a2001a, 0x3c0300a0, 
+0x2431024, 0x54430004, 0x3c020020, 0x97a2001a, 
+0x1000000c, 0x34420400, 0x2421024, 0x50400004, 
+0x3c020080, 0x97a2001a, 0x10000006, 0x34420800, 
+0x2421024, 0x10400004, 0x0, 0x97a2001a, 
+0x34420c00, 0xa7a2001a, 0x97a6001a, 0x24040004, 
+0xc004ddb, 0x2821, 0x3c020004, 0x2421024, 
+0x10400004, 0xa7a0001c, 0x32425000, 0x14400004, 
+0x0, 0x32424000, 0x10400005, 0x2021, 
+0xc004cf9, 0x2402021, 0x10000096, 0x0, 
+0x97a6001c, 0x2821, 0x34c61200, 0xc004ddb, 
+0xa7a6001c, 0x1000008f, 0x0, 0x2421024, 
+0x10400004, 0xa7a00018, 0x32425000, 0x14400004, 
+0x0, 0x32424000, 0x10400005, 0x3c020010, 
+0xc004cf9, 0x2402021, 0x10000019, 0xa7a0001a, 
+0x2421024, 0x10400004, 0x0, 0x97a20018, 
+0x10000004, 0xa7a20018, 0x97a20018, 0x34420100, 
+0xa7a20018, 0x3c020001, 0x2421024, 0x10400004, 
+0x0, 0x97a20018, 0x10000004, 0xa7a20018, 
+0x97a20018, 0x34422000, 0xa7a20018, 0x97a60018, 
+0x2021, 0xc004ddb, 0x2821, 0xa7a0001a, 
+0x8021, 0xc004d78, 0x24040001, 0x26100001, 
+0x2e020020, 0x1440fffb, 0x0, 0xc004d78, 
+0x2021, 0xc004d78, 0x24040001, 0xc004d78, 
+0x24040001, 0xc004d78, 0x2021, 0x24100010, 
+0x32020001, 0x10400002, 0x2021, 0x24040001, 
+0xc004d78, 0x108042, 0x1600fffa, 0x32020001, 
+0x24100010, 0xc004d78, 0x2021, 0x108042, 
+0x1600fffc, 0x0, 0xc004db9, 0x34108000, 
+0xc004db9, 0x0, 0xc004d58, 0x0, 
+0x50400005, 0x108042, 0x97a2001a, 0x501025, 
+0xa7a2001a, 0x108042, 0x1600fff7, 0x0, 
+0xc004db9, 0x8021, 0xa7a0001a, 0xc004d78, 
+0x24040001, 0x26100001, 0x2e020020, 0x1440fffb, 
+0x0, 0xc004d78, 0x2021, 0xc004d78, 
+0x24040001, 0xc004d78, 0x24040001, 0xc004d78, 
+0x2021, 0x24100010, 0x32020001, 0x10400002, 
+0x2021, 0x24040001, 0xc004d78, 0x108042, 
+0x1600fffa, 0x32020001, 0x24100010, 0xc004d78, 
+0x2021, 0x108042, 0x1600fffc, 0x0, 
+0xc004db9, 0x34108000, 0xc004db9, 0x0, 
+0xc004d58, 0x0, 0x50400005, 0x108042, 
+0x97a2001a, 0x501025, 0xa7a2001a, 0x108042, 
+0x1600fff7, 0x0, 0xc004db9, 0x0, 
+0x3c040001, 0x24846bcc, 0x97a60018, 0x97a7001a, 
+0x3c020001, 0x8c426d98, 0x3c030001, 0x8c636f1c, 
+0x3c05000d, 0x34a50205, 0xafa20010, 0xc002b3b, 
+0xafa30014, 0x8f830054, 0x24020004, 0x3c010001, 
+0xac226dd4, 0x3c010001, 0x10000017, 0xac236f38, 
+0x8f830054, 0x3c020001, 0x8c426f38, 0x2463ff9c, 
+0x431023, 0x2c420064, 0x1440000f, 0x0, 
+0x8f820220, 0x24030005, 0x3c010001, 0xac236dd4, 
+0x3c03f700, 0x431025, 0x10000007, 0xaf820220, 
+0x24020006, 0x3c010001, 0xac226dd4, 0x24020011, 
+0x3c010001, 0xac226dd0, 0x8fbf0034, 0x8fb20030, 
+0x8fb1002c, 0x8fb00028, 0x3e00008, 0x27bd0038, 
+0x27bdffd8, 0xafb00018, 0x808021, 0xafb1001c, 
+0x8821, 0x32024000, 0x10400013, 0xafbf0020, 
+0x3c020010, 0x2021024, 0x2c420001, 0x21023, 
+0x30434100, 0x3c020001, 0x2021024, 0x14400006, 
+0x34714000, 0x3c020002, 0x2021024, 0x14400002, 
+0x34716000, 0x34714040, 0x2021, 0x2821, 
+0x10000036, 0x2203021, 0x32021000, 0x10400035, 
+0x2021, 0x2821, 0xc004ddb, 0x24060040, 
+0x24040018, 0x2821, 0xc004ddb, 0x24060c00, 
+0x24040017, 0x2821, 0xc004ddb, 0x24060400, 
+0x24040016, 0x2821, 0xc004ddb, 0x24060006, 
+0x24040017, 0x2821, 0xc004ddb, 0x24062500, 
+0x24040016, 0x2821, 0xc004ddb, 0x24060006, 
+0x24040017, 0x2821, 0xc004ddb, 0x24064600, 
+0x24040016, 0x2821, 0xc004ddb, 0x24060006, 
+0x24040017, 0x2821, 0xc004ddb, 0x24066700, 
+0x24040016, 0x2821, 0xc004ddb, 0x24060006, 
+0x2404001f, 0x2821, 0xc004ddb, 0x24060010, 
+0x24040009, 0x2821, 0xc004ddb, 0x24061500, 
+0x24040009, 0x2821, 0x24061d00, 0xc004ddb, 
+0x0, 0x3c040001, 0x24846bf0, 0x3c05000e, 
+0x34a50100, 0x2003021, 0x2203821, 0xafa00010, 
+0xc002b3b, 0xafa00014, 0x8fbf0020, 0x8fb1001c, 
+0x8fb00018, 0x3e00008, 0x27bd0028, 0x8f850044, 
+0x8f820044, 0x3c030001, 0x431025, 0x3c030008, 
+0xaf820044, 0x8f840054, 0x8f820054, 0xa32824, 
+0x10000002, 0x24840001, 0x8f820054, 0x821023, 
+0x2c420002, 0x1440fffc, 0x0, 0x8f820044, 
+0x3c03fffe, 0x3463ffff, 0x431024, 0xaf820044, 
+0x8f830054, 0x8f820054, 0x10000002, 0x24630001, 
+0x8f820054, 0x621023, 0x2c420002, 0x1440fffc, 
+0x0, 0x3e00008, 0xa01021, 0x8f830044, 
+0x3c02fff0, 0x3442ffff, 0x42480, 0x621824, 
+0x3c020002, 0x822025, 0x641825, 0xaf830044, 
+0x8f820044, 0x3c03fffe, 0x3463ffff, 0x431024, 
+0xaf820044, 0x8f830054, 0x8f820054, 0x10000002, 
+0x24630001, 0x8f820054, 0x621023, 0x2c420002, 
+0x1440fffc, 0x0, 0x8f820044, 0x3c030001, 
+0x431025, 0xaf820044, 0x8f830054, 0x8f820054, 
+0x10000002, 0x24630001, 0x8f820054, 0x621023, 
+0x2c420002, 0x1440fffc, 0x0, 0x3e00008, 
+0x0, 0x8f820044, 0x2403ff7f, 0x431024, 
+0xaf820044, 0x8f830054, 0x8f820054, 0x10000002, 
+0x24630001, 0x8f820054, 0x621023, 0x2c420002, 
+0x1440fffc, 0x0, 0x8f820044, 0x34420080, 
+0xaf820044, 0x8f830054, 0x8f820054, 0x10000002, 
+0x24630001, 0x8f820054, 0x621023, 0x2c420002, 
+0x1440fffc, 0x0, 0x3e00008, 0x0, 
+0x8f820044, 0x3c03fff0, 0x3463ffff, 0x431024, 
+0xaf820044, 0x8f820044, 0x3c030001, 0x431025, 
+0xaf820044, 0x8f830054, 0x8f820054, 0x10000002, 
+0x24630001, 0x8f820054, 0x621023, 0x2c420002, 
+0x1440fffc, 0x0, 0x8f820044, 0x3c03fffe, 
+0x3463ffff, 0x431024, 0xaf820044, 0x8f830054, 
+0x8f820054, 0x10000002, 0x24630001, 0x8f820054, 
+0x621023, 0x2c420002, 0x1440fffc, 0x0, 
+0x3e00008, 0x0, 0x27bdffc8, 0xafb30024, 
+0x809821, 0xafbe002c, 0xa0f021, 0xafb20020, 
+0xc09021, 0x33c2ffff, 0xafbf0030, 0xafb50028, 
+0xafb1001c, 0xafb00018, 0x14400034, 0xa7b20010, 
+0x3271ffff, 0x27b20010, 0x8021, 0xc004d78, 
+0x24040001, 0x26100001, 0x2e020020, 0x1440fffb, 
+0x0, 0xc004d78, 0x2021, 0xc004d78, 
+0x24040001, 0xc004d78, 0x2021, 0xc004d78, 
+0x24040001, 0x24100010, 0x32020001, 0x10400002, 
+0x2021, 0x24040001, 0xc004d78, 0x108042, 
+0x1600fffa, 0x32020001, 0x24100010, 0x2301024, 
+0x10400002, 0x2021, 0x24040001, 0xc004d78, 
+0x108042, 0x1600fffa, 0x2301024, 0xc004d78, 
+0x24040001, 0xc004d78, 0x2021, 0x34108000, 
+0x96420000, 0x501024, 0x10400002, 0x2021, 
+0x24040001, 0xc004d78, 0x108042, 0x12000075, 
+0x0, 0x1000fff6, 0x0, 0x3275ffff, 
+0x27b10010, 0xa7a00010, 0x8021, 0xc004d78, 
+0x24040001, 0x26100001, 0x2e020020, 0x1440fffb, 
+0x0, 0xc004d78, 0x2021, 0xc004d78, 
+0x24040001, 0xc004d78, 0x24040001, 0xc004d78, 
+0x2021, 0x24100010, 0x32020001, 0x10400002, 
+0x2021, 0x24040001, 0xc004d78, 0x108042, 
+0x1600fffa, 0x32020001, 0x24100010, 0x2b01024, 
+0x10400002, 0x2021, 0x24040001, 0xc004d78, 
+0x108042, 0x1600fffa, 0x2b01024, 0xc004db9, 
+0x34108000, 0xc004db9, 0x0, 0xc004d58, 
+0x0, 0x50400005, 0x108042, 0x96220000, 
+0x501025, 0xa6220000, 0x108042, 0x1600fff7, 
+0x0, 0xc004db9, 0x0, 0x33c5ffff, 
+0x24020001, 0x54a20004, 0x24020002, 0x97a20010, 
+0x10000006, 0x521025, 0x14a20006, 0x3271ffff, 
+0x97a20010, 0x121827, 0x431024, 0xa7a20010, 
+0x3271ffff, 0x27b20010, 0x8021, 0xc004d78, 
+0x24040001, 0x26100001, 0x2e020020, 0x1440fffb, 
+0x0, 0xc004d78, 0x2021, 0xc004d78, 
+0x24040001, 0xc004d78, 0x2021, 0xc004d78, 
+0x24040001, 0x24100010, 0x32020001, 0x10400002, 
+0x2021, 0x24040001, 0xc004d78, 0x108042, 
+0x1600fffa, 0x32020001, 0x24100010, 0x2301024, 
+0x10400002, 0x2021, 0x24040001, 0xc004d78, 
+0x108042, 0x1600fffa, 0x2301024, 0xc004d78, 
+0x24040001, 0xc004d78, 0x2021, 0x34108000, 
+0x96420000, 0x501024, 0x10400002, 0x2021, 
+0x24040001, 0xc004d78, 0x108042, 0x1600fff8, 
+0x0, 0xc004db9, 0x0, 0x8fbf0030, 
+0x8fbe002c, 0x8fb50028, 0x8fb30024, 0x8fb20020, 
+0x8fb1001c, 0x8fb00018, 0x3e00008, 0x27bd0038, 
+0x0, 0x0, 0x0, 0x27bdffe8, 
+0xafbf0010, 0x8ee304b8, 0x24020008, 0x146201e0, 
+0x0, 0x3c020001, 0x8c426f1c, 0x14400005, 
+0x0, 0xc003daf, 0x8f840224, 0x100001d8, 
+0x0, 0x8f820220, 0x3c030008, 0x431024, 
+0x10400026, 0x24020001, 0x8f840224, 0x8f820220, 
+0x3c030400, 0x431024, 0x10400006, 0x0, 
+0x3c010002, 0xac208fa0, 0x3c010002, 0x1000000b, 
+0xac208fc0, 0x3c030002, 0x24638fa0, 0x8c620000, 
+0x24420001, 0xac620000, 0x2c420002, 0x14400003, 
+0x24020001, 0x3c010002, 0xac228fc0, 0x3c020002, 
+0x8c428fc0, 0x10400006, 0x30820040, 0x10400004, 
+0x24020001, 0x3c010002, 0x10000003, 0xac228fc4, 
+0x3c010002, 0xac208fc4, 0x3c010002, 0xac248f9c, 
+0x3c010002, 0x1000000b, 0xac208fd0, 0x3c010002, 
+0xac228fd0, 0x3c010002, 0xac208fc0, 0x3c010002, 
+0xac208fa0, 0x3c010002, 0xac208fc4, 0x3c010002, 
+0xac208f9c, 0x3c030002, 0x8c638f90, 0x3c020002, 
+0x8c428f94, 0x50620004, 0x2463ffff, 0x3c010002, 
+0xac238f94, 0x2463ffff, 0x2c62000e, 0x10400194, 
+0x31080, 0x3c010001, 0x220821, 0x8c226c00, 
+0x400008, 0x0, 0x24020002, 0x3c010002, 
+0xac208fc0, 0x3c010002, 0xac208fa0, 0x3c010002, 
+0xac208f9c, 0x3c010002, 0xac208fc4, 0x3c010002, 
+0xac208fb8, 0x3c010002, 0xac208fb0, 0xaf800224, 
+0x3c010002, 0xac228f90, 0x3c020002, 0x8c428fd0, 
+0x1440004f, 0x3c02fdff, 0x3442ffff, 0xc003daf, 
+0x282a024, 0xaf800204, 0x8f820200, 0x2403fffd, 
+0x431024, 0xaf820200, 0x3c010002, 0xac208fe0, 
+0x8f830054, 0x3c020002, 0x8c428fb8, 0x24040001, 
+0x3c010002, 0xac248fcc, 0x24420001, 0x3c010002, 
+0xac228fb8, 0x2c420004, 0x3c010002, 0xac238fb4, 
+0x14400006, 0x24020003, 0x3c010001, 0xac246d9c, 
+0x3c010002, 0x1000015e, 0xac208fb8, 0x3c010002, 
+0x1000015b, 0xac228f90, 0x8f830054, 0x3c020002, 
+0x8c428fb4, 0x2463d8f0, 0x431023, 0x2c422710, 
+0x14400003, 0x24020004, 0x3c010002, 0xac228f90, 
+0x3c020002, 0x8c428fd0, 0x14400021, 0x3c02fdff, 
+0x3442ffff, 0x1000014a, 0x282a024, 0x3c040001, 
+0x8c846f20, 0x3c010002, 0xc005084, 0xac208fa8, 
+0x3c020002, 0x8c428fdc, 0xaf820204, 0x3c020002, 
+0x8c428fd0, 0x14400012, 0x3c03fdff, 0x8f820204, 
+0x3463ffff, 0x30420030, 0x1440012f, 0x283a024, 
+0x3c030002, 0x8c638fdc, 0x24020005, 0x3c010002, 
+0xac228f90, 0x3c010002, 0x10000131, 0xac238fe0, 
+0x3c020002, 0x8c428fd0, 0x10400010, 0x3c02fdff, 
+0x3c020001, 0x8c426e3c, 0x24420001, 0x3c010001, 
+0xac226e3c, 0x2c420002, 0x14400125, 0x24020001, 
+0x3c010001, 0xac226e44, 0x3c010001, 0xac206e3c, 
+0x3c010001, 0x1000011e, 0xac226d9c, 0x3c030002, 
+0x8c638fc0, 0x3442ffff, 0x10600119, 0x282a024, 
+0x3c020002, 0x8c428f9c, 0x10400115, 0x0, 
+0x3c010002, 0xac228fc8, 0x24020003, 0x3c010002, 
+0xac228fa0, 0x100000b8, 0x24020006, 0x3c010002, 
+0xac208fa8, 0x8f820204, 0x34420040, 0xaf820204, 
+0x3c020002, 0x8c428fe0, 0x24030007, 0x3c010002, 
+0xac238f90, 0x34420040, 0x3c010002, 0xac228fe0, 
+0x3c020002, 0x8c428fc0, 0x10400005, 0x0, 
+0x3c020002, 0x8c428f9c, 0x104000f0, 0x24020002, 
+0x3c050002, 0x24a58fa0, 0x8ca20000, 0x2c424e21, 
+0x104000ea, 0x24020002, 0x3c020002, 0x8c428fc4, 
+0x104000ef, 0x2404ffbf, 0x3c020002, 0x8c428f9c, 
+0x3c030002, 0x8c638fc8, 0x441024, 0x641824, 
+0x10430004, 0x24020001, 0x3c010002, 0x100000e4, 
+0xac228f90, 0x24020003, 0xaca20000, 0x24020008, 
+0x3c010002, 0xac228f90, 0x3c020002, 0x8c428fcc, 
+0x1040000c, 0x24020001, 0x3c040002, 0xc005091, 
+0x8c848f9c, 0x3c020002, 0x8c428fe8, 0x14400005, 
+0x24020001, 0x3c020002, 0x8c428fe4, 0x10400006, 
+0x24020001, 0x3c010001, 0xac226d9c, 0x3c010002, 
+0x100000cb, 0xac208fb8, 0x3c020002, 0x8c428fb0, 
+0x3c030002, 0x8c638f9c, 0x2c420001, 0x210c0, 
+0x30630008, 0x3c010002, 0xac228fb0, 0x3c010002, 
+0xac238fac, 0x8f830054, 0x24020009, 0x3c010002, 
+0xac228f90, 0x3c010002, 0x100000b9, 0xac238fb4, 
+0x8f830054, 0x3c020002, 0x8c428fb4, 0x2463d8f0, 
+0x431023, 0x2c422710, 0x1440009f, 0x0, 
+0x3c020002, 0x8c428fc0, 0x10400005, 0x0, 
+0x3c020002, 0x8c428f9c, 0x104000a0, 0x24020002, 
+0x3c030002, 0x24638fa0, 0x8c620000, 0x2c424e21, 
+0x1040009a, 0x24020002, 0x3c020002, 0x8c428fcc, 
+0x1040000e, 0x0, 0x3c020002, 0x8c428f9c, 
+0x3c010002, 0xac208fcc, 0x30420080, 0x1040002f, 
+0x2402000c, 0x8f820204, 0x30420080, 0x1440000c, 
+0x24020003, 0x10000029, 0x2402000c, 0x3c020002, 
+0x8c428f9c, 0x30420080, 0x14400005, 0x24020003, 
+0x8f820204, 0x30420080, 0x1040001f, 0x24020003, 
+0xac620000, 0x2402000a, 0x3c010002, 0xac228f90, 
+0x3c040002, 0x24848fd8, 0x8c820000, 0x3c030002, 
+0x8c638fb0, 0x431025, 0xaf820204, 0x8c830000, 
+0x3c040002, 0x8c848fb0, 0x2402000b, 0x3c010002, 
+0xac228f90, 0x641825, 0x3c010002, 0xac238fe0, 
+0x3c050002, 0x24a58fa0, 0x8ca20000, 0x2c424e21, 
+0x10400066, 0x24020002, 0x3c020002, 0x8c428fd0, 
+0x10400005, 0x0, 0x2402000c, 0x3c010002, 
+0x10000067, 0xac228f90, 0x3c020002, 0x8c428fc0, 
+0x10400063, 0x0, 0x3c040002, 0x8c848f9c, 
+0x10800055, 0x30820008, 0x3c030002, 0x8c638fac, 
+0x1062005b, 0x24020003, 0x3c010002, 0xac248fc8, 
+0xaca20000, 0x24020006, 0x3c010002, 0x10000054, 
+0xac228f90, 0x8f820200, 0x34420002, 0xaf820200, 
+0x8f830054, 0x2402000d, 0x3c010002, 0xac228f90, 
+0x3c010002, 0xac238fb4, 0x8f830054, 0x3c020002, 
+0x8c428fb4, 0x2463d8f0, 0x431023, 0x2c422710, 
+0x14400031, 0x0, 0x3c020002, 0x8c428fd0, 
+0x10400020, 0x2402000e, 0x3c030002, 0x8c638fe4, 
+0x3c010002, 0x14600015, 0xac228f90, 0xc003e6d, 
+0x0, 0x3c050001, 0x8ca56d98, 0xc00529b, 
+0x2021, 0x3c030001, 0x8c636d98, 0x24020004, 
+0x14620005, 0x2403fffb, 0x3c020001, 0x8c426d94, 
+0x10000003, 0x2403fff7, 0x3c020001, 0x8c426d94, 
+0x431024, 0x3c010001, 0xac226d94, 0x8f830224, 
+0x3c020200, 0x3c010002, 0xac238fec, 0x10000020, 
+0x282a025, 0x3c020002, 0x8c428fc0, 0x10400005, 
+0x0, 0x3c020002, 0x8c428f9c, 0x1040000f, 
+0x24020002, 0x3c020002, 0x8c428fa0, 0x2c424e21, 
+0x1040000a, 0x24020002, 0x3c020002, 0x8c428fc0, 
+0x1040000f, 0x0, 0x3c020002, 0x8c428f9c, 
+0x1440000b, 0x0, 0x24020002, 0x3c010002, 
+0x10000007, 0xac228f90, 0x3c020002, 0x8c428fc0, 
+0x10400003, 0x0, 0xc003daf, 0x0, 
+0x8f820220, 0x3c03f700, 0x431025, 0xaf820220, 
+0x8fbf0010, 0x3e00008, 0x27bd0018, 0x3c030002, 
+0x24638fe8, 0x8c620000, 0x10400005, 0x34422000, 
+0x3c010002, 0xac228fdc, 0x10000003, 0xac600000, 
+0x3c010002, 0xac248fdc, 0x3e00008, 0x0, 
+0x27bdffe0, 0x30820030, 0xafbf0018, 0x3c010002, 
+0xac228fe4, 0x14400067, 0x3c02ffff, 0x34421f0e, 
+0x821024, 0x14400061, 0x24020030, 0x30822000, 
+0x1040005d, 0x30838000, 0x31a02, 0x30820001, 
+0x21200, 0x3c040001, 0x8c846f20, 0x621825, 
+0x331c2, 0x3c030001, 0x24636e48, 0x30828000, 
+0x21202, 0x30840001, 0x42200, 0x441025, 
+0x239c2, 0x61080, 0x431021, 0x471021, 
+0x90430000, 0x24020001, 0x10620025, 0x0, 
+0x10600007, 0x24020002, 0x10620013, 0x24020003, 
+0x1062002c, 0x3c05000f, 0x10000037, 0x0, 
+0x8f820200, 0x2403feff, 0x431024, 0xaf820200, 
+0x8f820220, 0x3c03fffe, 0x3463ffff, 0x431024, 
+0xaf820220, 0x3c010002, 0xac209004, 0x3c010002, 
+0x10000034, 0xac20900c, 0x8f820200, 0x34420100, 
+0xaf820200, 0x8f820220, 0x3c03fffe, 0x3463ffff, 
+0x431024, 0xaf820220, 0x24020100, 0x3c010002, 
+0xac229004, 0x3c010002, 0x10000026, 0xac20900c, 
+0x8f820200, 0x2403feff, 0x431024, 0xaf820200, 
+0x8f820220, 0x3c030001, 0x431025, 0xaf820220, 
+0x3c010002, 0xac209004, 0x3c010002, 0x10000019, 
+0xac23900c, 0x8f820200, 0x34420100, 0xaf820200, 
+0x8f820220, 0x3c030001, 0x431025, 0xaf820220, 
+0x24020100, 0x3c010002, 0xac229004, 0x3c010002, 
+0x1000000c, 0xac23900c, 0x34a5ffff, 0x3c040001, 
+0x24846c38, 0xafa30010, 0xc002b3b, 0xafa00014, 
+0x10000004, 0x0, 0x24020030, 0x3c010002, 
+0xac228fe8, 0x8fbf0018, 0x3e00008, 0x27bd0020, 
+0x0, 0x0, 0x0, 0x27bdffc8, 
+0xafb20028, 0x809021, 0xafb3002c, 0xa09821, 
+0xafb00020, 0xc08021, 0x3c040001, 0x24846c50, 
+0x3c050009, 0x3c020001, 0x8c426d98, 0x34a59001, 
+0x2403021, 0x2603821, 0xafbf0030, 0xafb10024, 
+0xa7a0001a, 0xafb00014, 0xc002b3b, 0xafa20010, 
+0x24020002, 0x12620083, 0x2e620003, 0x10400005, 
+0x24020001, 0x1262000a, 0x0, 0x10000173, 
+0x0, 0x24020004, 0x126200f8, 0x24020008, 
+0x126200f7, 0x3c02ffec, 0x1000016c, 0x0, 
+0x3c020001, 0x8c426d94, 0x30420002, 0x14400004, 
+0x128940, 0x3c02fffb, 0x3442ffff, 0x2028024, 
+0x3c010002, 0x310821, 0xac308ffc, 0x3c024000, 
+0x2021024, 0x1040004e, 0x1023c2, 0x30840030, 
+0x101382, 0x3042001c, 0x3c030001, 0x24636dd8, 
+0x431021, 0x823821, 0x3c020020, 0x2021024, 
+0x10400006, 0x24020100, 0x3c010002, 0x310821, 
+0xac229000, 0x10000005, 0x3c020080, 0x3c010002, 
+0x310821, 0xac209000, 0x3c020080, 0x2021024, 
+0x10400006, 0x121940, 0x3c020001, 0x3c010002, 
+0x230821, 0x10000005, 0xac229008, 0x121140, 
+0x3c010002, 0x220821, 0xac209008, 0x94e40000, 
+0x3c030001, 0x8c636f40, 0x24020005, 0x10620010, 
+0xa7a40018, 0x32024000, 0x10400002, 0x34824000, 
+0xa7a20018, 0x24040001, 0x94e20002, 0x24050004, 
+0x24e60002, 0x34420001, 0xc0045be, 0xa4e20002, 
+0x24040001, 0x2821, 0xc0045be, 0x27a60018, 
+0x3c020001, 0x8c426d98, 0x24110001, 0x3c010001, 
+0xac316da4, 0x14530004, 0x32028000, 0xc003daf, 
+0x0, 0x32028000, 0x1040011c, 0x0, 
+0xc003daf, 0x0, 0x3c030001, 0x8c636f40, 
+0x24020005, 0x10620115, 0x24020002, 0x3c010001, 
+0xac316d9c, 0x3c010001, 0x10000110, 0xac226d98, 
+0x24040001, 0x24050004, 0x27b0001a, 0xc0045be, 
+0x2003021, 0x24040001, 0x2821, 0xc0045be, 
+0x2003021, 0x3c020002, 0x511021, 0x8c428ff4, 
+0x3c040001, 0x8c846d98, 0x3c03bfff, 0x3463ffff, 
+0x3c010001, 0xac336da4, 0x431024, 0x3c010002, 
+0x310821, 0x109300f7, 0xac228ff4, 0x100000f7, 
+0x0, 0x3c022000, 0x2021024, 0x10400005, 
+0x24020001, 0x3c010001, 0xac226f1c, 0x10000004, 
+0x128940, 0x3c010001, 0xac206f1c, 0x128940, 
+0x3c010002, 0x310821, 0xac308ff8, 0x3c024000, 
+0x2021024, 0x14400014, 0x0, 0x3c020001, 
+0x8c426f1c, 0x10400006, 0x24040004, 0x24050001, 
+0xc004ddb, 0x24062000, 0x24020001, 0xaee204b8, 
+0x3c020002, 0x511021, 0x8c428ff0, 0x3c03bfff, 
+0x3463ffff, 0x431024, 0x3c010002, 0x310821, 
+0x100000d0, 0xac228ff0, 0x3c020001, 0x8c426f1c, 
+0x10400028, 0x3c0300a0, 0x2031024, 0x5443000d, 
+0x3c020020, 0x3c020001, 0x8c426f20, 0x24030100, 
+0x3c010002, 0x310821, 0xac239004, 0x3c030001, 
+0x3c010002, 0x310821, 0xac23900c, 0x10000015, 
+0x34420400, 0x2021024, 0x10400008, 0x24030100, 
+0x3c020001, 0x8c426f20, 0x3c010002, 0x310821, 
+0xac239004, 0x1000000b, 0x34420800, 0x3c020080, 
+0x2021024, 0x1040002e, 0x3c030001, 0x3c020001, 
+0x8c426f20, 0x3c010002, 0x310821, 0xac23900c, 
+0x34420c00, 0x3c010001, 0xac226f20, 0x10000025, 
+0x24040001, 0x3c020020, 0x2021024, 0x10400006, 
+0x24020100, 0x3c010002, 0x310821, 0xac229004, 
+0x10000005, 0x3c020080, 0x3c010002, 0x310821, 
+0xac209004, 0x3c020080, 0x2021024, 0x10400007, 
+0x121940, 0x3c020001, 0x3c010002, 0x230821, 
+0xac22900c, 0x10000006, 0x24040001, 0x121140, 
+0x3c010002, 0x220821, 0xac20900c, 0x24040001, 
+0x2821, 0x27b0001e, 0xc00457c, 0x2003021, 
+0x24040001, 0x2821, 0xc00457c, 0x2003021, 
+0x24040001, 0x24050001, 0x27b0001c, 0xc00457c, 
+0x2003021, 0x24040001, 0x24050001, 0xc00457c, 
+0x2003021, 0x10000077, 0x0, 0x3c02ffec, 
+0x3442ffff, 0x2028024, 0x3c020008, 0x2028025, 
+0x121140, 0x3c010002, 0x220821, 0xac308ff8, 
+0x3c022000, 0x2021024, 0x10400009, 0x0, 
+0x3c020001, 0x8c426e44, 0x14400005, 0x24020001, 
+0x3c010001, 0xac226f1c, 0x10000004, 0x3c024000, 
+0x3c010001, 0xac206f1c, 0x3c024000, 0x2021024, 
+0x1440001d, 0x24020e01, 0x3c030001, 0x8c636f1c, 
+0xaf820238, 0x3c010001, 0xac206db0, 0x10600005, 
+0x24022020, 0x3c010001, 0xac226f20, 0x24020001, 
+0xaee204b8, 0x3c04bfff, 0x121940, 0x3c020002, 
+0x431021, 0x8c428ff0, 0x3c050001, 0x8ca56d98, 
+0x3484ffff, 0x441024, 0x3c010002, 0x230821, 
+0xac228ff0, 0x24020001, 0x10a20044, 0x0, 
+0x10000040, 0x0, 0x3c020001, 0x8c426f1c, 
+0x1040001c, 0x24022000, 0x3c010001, 0xac226f20, 
+0x3c0300a0, 0x2031024, 0x14430005, 0x121140, 
+0x3402a000, 0x3c010001, 0x1000002d, 0xac226f20, 
+0x3c030002, 0x621821, 0x8c638ff8, 0x3c020020, 
+0x621024, 0x10400004, 0x24022001, 0x3c010001, 
+0x10000023, 0xac226f20, 0x3c020080, 0x621024, 
+0x1040001f, 0x3402a001, 0x3c010001, 0x1000001c, 
+0xac226f20, 0x3c020020, 0x2021024, 0x10400007, 
+0x121940, 0x24020100, 0x3c010002, 0x230821, 
+0xac229004, 0x10000006, 0x3c020080, 0x121140, 
+0x3c010002, 0x220821, 0xac209004, 0x3c020080, 
+0x2021024, 0x10400006, 0x121940, 0x3c020001, 
+0x3c010002, 0x230821, 0x10000005, 0xac22900c, 
+0x121140, 0x3c010002, 0x220821, 0xac20900c, 
+0x3c030001, 0x8c636d98, 0x24020001, 0x10620003, 
+0x0, 0xc003daf, 0x0, 0x8fbf0030, 
+0x8fb3002c, 0x8fb20028, 0x8fb10024, 0x8fb00020, 
+0x3e00008, 0x27bd0038, 0x27bdffb0, 0xafb3003c, 
+0x9821, 0xafb50040, 0xa821, 0xafb10034, 
+0x8821, 0x24020002, 0xafbf0048, 0xafbe0044, 
+0xafb20038, 0xafb00030, 0xafa4002c, 0xa7a0001a, 
+0xa7a00018, 0xa7a00020, 0xa7a0001e, 0xa7a00022, 
+0x10a20130, 0xa7a0001c, 0x2ca20003, 0x10400005, 
+0x24020001, 0x10a2000a, 0x3c024000, 0x1000025d, 
+0x2201021, 0x24020004, 0x10a2020a, 0x24020008, 
+0x10a20208, 0x2201021, 0x10000256, 0x0, 
+0x8fa8002c, 0x88140, 0x3c030002, 0x701821, 
+0x8c638ffc, 0x621024, 0x14400009, 0x24040001, 
+0x3c027fff, 0x3442ffff, 0x628824, 0x3c010002, 
+0x300821, 0xac318ff4, 0x10000246, 0x2201021, 
+0x24050001, 0xc00457c, 0x27a60018, 0x24040001, 
+0x24050001, 0xc00457c, 0x27a60018, 0x97a20018, 
+0x30420004, 0x104000d9, 0x3c114000, 0x3c020001, 
+0x8c426f40, 0x2443ffff, 0x2c620006, 0x104000d9, 
+0x31080, 0x3c010001, 0x220821, 0x8c226c68, 
+0x400008, 0x0, 0x24040001, 0x24050011, 
+0x27b0001a, 0xc00457c, 0x2003021, 0x24040001, 
+0x24050011, 0xc00457c, 0x2003021, 0x97a3001a, 
+0x30624000, 0x10400002, 0x3c150010, 0x3c150008, 
+0x30628000, 0x104000aa, 0x3c130001, 0x100000a8, 
+0x3c130002, 0x24040001, 0x24050014, 0x27b0001a, 
+0xc00457c, 0x2003021, 0x24040001, 0x24050014, 
+0xc00457c, 0x2003021, 0x97a3001a, 0x30621000, 
+0x10400002, 0x3c150010, 0x3c150008, 0x30620800, 
+0x10400097, 0x3c130001, 0x10000095, 0x3c130002, 
+0x24040001, 0x24050019, 0x27b0001c, 0xc00457c, 
+0x2003021, 0x24040001, 0x24050019, 0xc00457c, 
+0x2003021, 0x97a2001c, 0x30430700, 0x24020400, 
+0x10620027, 0x28620401, 0x1040000e, 0x24020200, 
+0x1062001f, 0x28620201, 0x10400005, 0x24020100, 
+0x5062001e, 0x3c130001, 0x1000001e, 0x24040001, 
+0x24020300, 0x50620019, 0x3c130002, 0x10000019, 
+0x24040001, 0x24020600, 0x1062000d, 0x28620601, 
+0x10400005, 0x24020500, 0x5062000b, 0x3c130002, 
+0x10000010, 0x24040001, 0x24020700, 0x1462000d, 
+0x24040001, 0x3c130004, 0x1000000a, 0x3c150008, 
+0x10000006, 0x3c130004, 0x10000005, 0x3c150008, 
+0x3c130001, 0x10000002, 0x3c150008, 0x3c150010, 
+0x24040001, 0x24050018, 0x27b0001e, 0xc00457c, 
+0x2003021, 0x24040001, 0x24050018, 0xc00457c, 
+0x2003021, 0x8fa8002c, 0x97a7001e, 0x81140, 
+0x3c060002, 0xc23021, 0x8cc68ff4, 0x97a20022, 
+0x3c100001, 0x26106c5c, 0x2002021, 0xafa20010, 
+0x97a2001c, 0x3c05000c, 0x34a50303, 0xc002b3b, 
+0xafa20014, 0x3c020004, 0x16620010, 0x3c020001, 
+0x8f840054, 0x24030001, 0x24020002, 0x3c010001, 
+0xac236d9c, 0x3c010001, 0xac226d98, 0x3c010001, 
+0xac236da4, 0x3c010001, 0xac236e24, 0x3c010001, 
+0xac246f30, 0x1000004f, 0x2b38825, 0x16620039, 
+0x3c028000, 0x3c020001, 0x8c426e20, 0x1440001e, 
+0x24040018, 0x2021, 0x2821, 0xc004ddb, 
+0x34068000, 0x8f830054, 0x8f820054, 0x2b38825, 
+0x10000002, 0x24630032, 0x8f820054, 0x621023, 
+0x2c420033, 0x1440fffc, 0x0, 0x8f830054, 
+0x24020001, 0x3c010001, 0xac226e20, 0x3c010001, 
+0xac226d9c, 0x3c010001, 0xac226d98, 0x3c010001, 
+0xac226da4, 0x3c010001, 0xac226e24, 0x3c010001, 
+0x1000002c, 0xac236f30, 0x2821, 0xc004ddb, 
+0x24060404, 0x2021, 0x2405001e, 0x27a60018, 
+0x24020002, 0xc0045be, 0xa7a20018, 0x2021, 
+0x2821, 0x27a60018, 0xc0045be, 0xa7a00018, 
+0x24040018, 0x24050002, 0xc004ddb, 0x24060004, 
+0x3c028000, 0x2221025, 0x2b31825, 0x10000015, 
+0x438825, 0x2221025, 0x2751825, 0x438825, 
+0x2002021, 0x97a6001c, 0x3c070001, 0x8ce76d98, 
+0x3c05000c, 0x34a50326, 0xafb30010, 0xc002b3b, 
+0xafb10014, 0x10000007, 0x0, 0x3c110002, 
+0x2308821, 0x8e318ffc, 0x3c027fff, 0x3442ffff, 
+0x2228824, 0x3c020001, 0x8c426da8, 0x1040001e, 
+0x0, 0x3c020001, 0x8c426f1c, 0x10400002, 
+0x3c022000, 0x2228825, 0x8fa8002c, 0x81140, 
+0x3c010002, 0x220821, 0x8c229000, 0x10400003, 
+0x3c020020, 0x10000005, 0x2228825, 0x3c02ffdf, 
+0x3442ffff, 0x2228824, 0x8fa8002c, 0x81140, 
+0x3c010002, 0x220821, 0x8c229008, 0x10400003, 
+0x3c020080, 0x10000004, 0x2228825, 0x3c02ff7f, 
+0x3442ffff, 0x2228824, 0x8fa8002c, 0x81140, 
+0x3c010002, 0x220821, 0xac318ff4, 0x10000135, 
+0x2201021, 0x8fa8002c, 0x8f140, 0x3c030002, 
+0x7e1821, 0x8c638ff8, 0x3c024000, 0x621024, 
+0x14400009, 0x24040001, 0x3c027fff, 0x3442ffff, 
+0x628824, 0x3c010002, 0x3e0821, 0xac318ff0, 
+0x10000124, 0x2201021, 0x2821, 0xc00457c, 
+0x27a60018, 0x24040001, 0x2821, 0xc00457c, 
+0x27a60018, 0x24040001, 0x24050001, 0x27b20020, 
+0xc00457c, 0x2403021, 0x24040001, 0x24050001, 
+0xc00457c, 0x2403021, 0x24040001, 0x24050004, 
+0x27b1001e, 0xc00457c, 0x2203021, 0x24040001, 
+0x24050004, 0xc00457c, 0x2203021, 0x24040001, 
+0x24050005, 0x27b00022, 0xc00457c, 0x2003021, 
+0x24040001, 0x24050005, 0xc00457c, 0x2003021, 
+0x24040001, 0x24050010, 0xc00457c, 0x27a60018, 
+0x24040001, 0x24050010, 0xc00457c, 0x27a60018, 
+0x24040001, 0x2405000a, 0xc00457c, 0x2403021, 
+0x24040001, 0x2405000a, 0xc00457c, 0x2403021, 
+0x24040001, 0x24050018, 0xc00457c, 0x2203021, 
+0x24040001, 0x24050018, 0xc00457c, 0x2203021, 
+0x24040001, 0x24050001, 0xc00457c, 0x27a60018, 
+0x24040001, 0x24050001, 0xc00457c, 0x27a60018, 
+0x97a20018, 0x30420004, 0x10400066, 0x3c114000, 
+0x3c030001, 0x8c636f34, 0x24020005, 0x14620067, 
+0x24040001, 0x24050019, 0x27b0001c, 0xc00457c, 
+0x2003021, 0x24040001, 0x24050019, 0xc00457c, 
+0x2003021, 0x97a2001c, 0x30430700, 0x24020400, 
+0x10620027, 0x28620401, 0x1040000e, 0x24020200, 
+0x1062001f, 0x28620201, 0x10400005, 0x24020100, 
+0x5062001e, 0x3c130001, 0x1000001e, 0x3c020004, 
+0x24020300, 0x50620019, 0x3c130002, 0x10000019, 
+0x3c020004, 0x24020600, 0x1062000d, 0x28620601, 
+0x10400005, 0x24020500, 0x5062000b, 0x3c130002, 
+0x10000010, 0x3c020004, 0x24020700, 0x1462000d, 
+0x3c020004, 0x3c130004, 0x1000000a, 0x3c150008, 
+0x10000006, 0x3c130004, 0x10000005, 0x3c150008, 
+0x3c130001, 0x10000002, 0x3c150008, 0x3c150010, 
+0x3c020004, 0x12620017, 0x3c028000, 0x8f820054, 
+0x24100001, 0x3c010001, 0xac306d9c, 0x3c010001, 
+0xac306d98, 0x3c010001, 0xac306da4, 0x3c010001, 
+0xac306e24, 0x3c010001, 0xac226f30, 0x3c020001, 
+0x16620022, 0x2758825, 0x2021, 0x2821, 
+0xc004ddb, 0x34068000, 0x3c010001, 0x1000001b, 
+0xac306e20, 0x2221025, 0x2b31825, 0x438825, 
+0x97a6001c, 0x3c020001, 0x8c426f1c, 0x3c070001, 
+0x8ce76d98, 0x3c040001, 0x24846c5c, 0xafa20010, 
+0x97a2001e, 0x3c05000c, 0x34a50323, 0x3c010001, 
+0xac206e20, 0xc002b3b, 0xafa20014, 0x10000007, 
+0x0, 0x3c110002, 0x23e8821, 0x8e318ff0, 
+0x3c027fff, 0x3442ffff, 0x2228824, 0x3c020001, 
+0x8c426da8, 0x10400069, 0x0, 0x3c020001, 
+0x8c426f1c, 0x10400002, 0x3c022000, 0x2228825, 
+0x8fa8002c, 0x81140, 0x3c010002, 0x220821, 
+0x8c229004, 0x10400003, 0x3c020020, 0x10000005, 
+0x2228825, 0x3c02ffdf, 0x3442ffff, 0x2228824, 
+0x8fa8002c, 0x81140, 0x3c010002, 0x220821, 
+0x8c22900c, 0x10400003, 0x3c020080, 0x1000004f, 
+0x2228825, 0x3c02ff7f, 0x3442ffff, 0x1000004b, 
+0x2228824, 0x8fa8002c, 0x82940, 0x3c030002, 
+0x651821, 0x8c638ff8, 0x3c024000, 0x621024, 
+0x14400008, 0x3c027fff, 0x3442ffff, 0x628824, 
+0x3c010002, 0x250821, 0xac318ff0, 0x10000041, 
+0x2201021, 0x3c020001, 0x8c426da8, 0x10400034, 
+0x3c11c00c, 0x3c020001, 0x8c426e44, 0x3c04c00c, 
+0x34842000, 0x3c030001, 0x8c636f1c, 0x2102b, 
+0x21023, 0x441024, 0x10600003, 0x518825, 
+0x3c022000, 0x2228825, 0x3c020002, 0x451021, 
+0x8c429004, 0x10400003, 0x3c020020, 0x10000004, 
+0x2228825, 0x3c02ffdf, 0x3442ffff, 0x2228824, 
+0x8fa8002c, 0x81140, 0x3c010002, 0x220821, 
+0x8c22900c, 0x10400003, 0x3c020080, 0x10000004, 
+0x2228825, 0x3c02ff7f, 0x3442ffff, 0x2228824, 
+0x3c020001, 0x8c426e30, 0x10400002, 0x3c020800, 
+0x2228825, 0x3c020001, 0x8c426e34, 0x10400002, 
+0x3c020400, 0x2228825, 0x3c020001, 0x8c426e38, 
+0x10400006, 0x3c020100, 0x10000004, 0x2228825, 
+0x3c027fff, 0x3442ffff, 0x628824, 0x8fa8002c, 
+0x81140, 0x3c010002, 0x220821, 0xac318ff0, 
+0x2201021, 0x8fbf0048, 0x8fbe0044, 0x8fb50040, 
+0x8fb3003c, 0x8fb20038, 0x8fb10034, 0x8fb00030, 
+0x3e00008, 0x27bd0050, 0x27bdffd0, 0xafb20028, 
+0x809021, 0xafbf002c, 0xafb10024, 0xafb00020, 
+0x8f840200, 0x3c100001, 0x8e106d98, 0x8f860220, 
+0x24020002, 0x1202005c, 0x2e020003, 0x10400005, 
+0x24020001, 0x1202000a, 0x121940, 0x1000010c, 
+0x0, 0x24020004, 0x120200bf, 0x24020008, 
+0x120200be, 0x128940, 0x10000105, 0x0, 
+0x3c050002, 0xa32821, 0x8ca58ffc, 0x3c100002, 
+0x2038021, 0x8e108ff4, 0x3c024000, 0xa21024, 
+0x10400038, 0x3c020008, 0x2021024, 0x10400020, 
+0x34840002, 0x3c020002, 0x431021, 0x8c429000, 
+0x10400005, 0x34840020, 0x34840100, 0x3c020020, 
+0x10000006, 0x2028025, 0x2402feff, 0x822024, 
+0x3c02ffdf, 0x3442ffff, 0x2028024, 0x121140, 
+0x3c010002, 0x220821, 0x8c229008, 0x10400005, 
+0x3c020001, 0xc23025, 0x3c020080, 0x10000016, 
+0x2028025, 0x3c02fffe, 0x3442ffff, 0xc23024, 
+0x3c02ff7f, 0x3442ffff, 0x1000000f, 0x2028024, 
+0x2402fedf, 0x822024, 0x3c02fffe, 0x3442ffff, 
+0xc23024, 0x3c02ff5f, 0x3442ffff, 0x2028024, 
+0x3c010002, 0x230821, 0xac209000, 0x3c010002, 
+0x230821, 0xac209008, 0xaf840200, 0xaf860220, 
+0x8f820220, 0x34420002, 0xaf820220, 0x1000000a, 
+0x121140, 0x3c02bfff, 0x3442ffff, 0x8f830200, 
+0x2028024, 0x2402fffd, 0x621824, 0xc003daf, 
+0xaf830200, 0x121140, 0x3c010002, 0x220821, 
+0x100000b7, 0xac308ff4, 0x3c020001, 0x8c426f1c, 
+0x10400069, 0x24050004, 0x24040001, 0xc00457c, 
+0x27a60018, 0x24040001, 0x24050005, 0xc00457c, 
+0x27a6001a, 0x97a30018, 0x97a2001a, 0x3c040001, 
+0x24846e48, 0x30630c00, 0x31a82, 0x30420c00, 
+0x21282, 0xa7a2001a, 0x21080, 0x441021, 
+0x431021, 0xa7a30018, 0x90480000, 0x24020001, 
+0x3103ffff, 0x10620029, 0x28620002, 0x10400005, 
+0x0, 0x10600009, 0x0, 0x1000003d, 
+0x0, 0x10700013, 0x24020003, 0x1062002c, 
+0x0, 0x10000037, 0x0, 0x8f820200, 
+0x2403feff, 0x431024, 0xaf820200, 0x8f820220, 
+0x3c03fffe, 0x3463ffff, 0x431024, 0xaf820220, 
+0x3c010002, 0xac209004, 0x3c010002, 0x10000032, 
+0xac20900c, 0x8f820200, 0x34420100, 0xaf820200, 
+0x8f820220, 0x3c03fffe, 0x3463ffff, 0x431024, 
+0xaf820220, 0x24020100, 0x3c010002, 0xac229004, 
+0x3c010002, 0x10000024, 0xac20900c, 0x8f820200, 
+0x2403feff, 0x431024, 0xaf820200, 0x8f820220, 
+0x3c030001, 0x431025, 0xaf820220, 0x3c010002, 
+0xac209004, 0x3c010002, 0x10000017, 0xac23900c, 
+0x8f820200, 0x34420100, 0xaf820200, 0x8f820220, 
+0x3c030001, 0x431025, 0xaf820220, 0x24020100, 
+0x3c010002, 0xac229004, 0x3c010002, 0x1000000a, 
+0xac23900c, 0x3c040001, 0x24846c80, 0x97a6001a, 
+0x97a70018, 0x3c050001, 0x34a5ffff, 0xafa80010, 
+0xc002b3b, 0xafa00014, 0x8f820200, 0x34420002, 
+0x1000004b, 0xaf820200, 0x128940, 0x3c050002, 
+0xb12821, 0x8ca58ff8, 0x3c100002, 0x2118021, 
+0x8e108ff0, 0x3c024000, 0xa21024, 0x14400010, 
+0x0, 0x3c020001, 0x8c426f1c, 0x14400005, 
+0x3c02bfff, 0x8f820200, 0x34420002, 0xaf820200, 
+0x3c02bfff, 0x3442ffff, 0xc003daf, 0x2028024, 
+0x3c010002, 0x310821, 0x10000031, 0xac308ff0, 
+0x3c020001, 0x8c426f1c, 0x10400005, 0x3c020020, 
+0x3c020001, 0x8c426e44, 0x10400025, 0x3c020020, 
+0xa21024, 0x10400007, 0x34840020, 0x24020100, 
+0x3c010002, 0x310821, 0xac229004, 0x10000006, 
+0x34840100, 0x3c010002, 0x310821, 0xac209004, 
+0x2402feff, 0x822024, 0x3c020080, 0xa21024, 
+0x10400007, 0x121940, 0x3c020001, 0x3c010002, 
+0x230821, 0xac22900c, 0x10000008, 0xc23025, 
+0x121140, 0x3c010002, 0x220821, 0xac20900c, 
+0x3c02fffe, 0x3442ffff, 0xc23024, 0xaf840200, 
+0xaf860220, 0x8f820220, 0x34420002, 0xaf820220, 
+0x121140, 0x3c010002, 0x220821, 0xac308ff0, 
+0x8fbf002c, 0x8fb20028, 0x8fb10024, 0x8fb00020, 
+0x3e00008, 0x27bd0030, 0x0, 0x1821, 
+0x308400ff, 0x2405ffdf, 0x2406ffbf, 0x641007, 
+0x30420001, 0x10400004, 0x0, 0x8f820044, 
+0x10000003, 0x34420040, 0x8f820044, 0x461024, 
+0xaf820044, 0x8f820044, 0x34420020, 0xaf820044, 
+0x8f820044, 0x451024, 0xaf820044, 0x24630001, 
+0x28620008, 0x5440ffee, 0x641007, 0x3e00008, 
+0x0, 0x2c820008, 0x1040001b, 0x0, 
+0x2405ffdf, 0x2406ffbf, 0x41880, 0x3c020001, 
+0x24426e60, 0x621821, 0x24640004, 0x90620000, 
+0x10400004, 0x0, 0x8f820044, 0x10000003, 
+0x34420040, 0x8f820044, 0x461024, 0xaf820044, 
+0x8f820044, 0x34420020, 0xaf820044, 0x8f820044, 
+0x451024, 0xaf820044, 0x24630001, 0x64102b, 
+0x1440ffee, 0x0, 0x3e00008, 0x0, 
+0x0, 0x0, 0x0, 0x8f8400c4, 
+0x8f8600e0, 0x8f8700e4, 0x2402fff8, 0xc22824, 
+0x10e5001a, 0x27623ff8, 0x14e20002, 0x24e80008, 
+0x27683000, 0x55050004, 0x8d0a0000, 0x30c20004, 
+0x14400012, 0x805021, 0x8ce90000, 0x8f42013c, 
+0x1494823, 0x49182b, 0x94eb0006, 0x10600002, 
+0x25630050, 0x494821, 0x123182b, 0x50400003, 
+0x8f4201fc, 0x3e00008, 0xe01021, 0xaf8800e8, 
+0x24420001, 0xaf4201fc, 0xaf8800e4, 0x3e00008, 
+0x1021, 0x3e00008, 0x0, 0x8f8300e4, 
+0x27623ff8, 0x10620004, 0x24620008, 0xaf8200e8, 
+0x3e00008, 0xaf8200e4, 0x27623000, 0xaf8200e8, 
+0x3e00008, 0xaf8200e4, 0x3e00008, 0x0, 
+0x0, 0x0, 0x0, 0x8f880120, 
+0x27624fe0, 0x8f830128, 0x15020002, 0x25090020, 
+0x27694800, 0x11230012, 0x8fa20010, 0xad040000, 
+0xad050004, 0xad060008, 0xa507000e, 0x8fa30014, 
+0xad020018, 0x8fa20018, 0xad03001c, 0x25030016, 
+0xad020010, 0xad030014, 0xaf890120, 0x8f4300fc, 
+0x24020001, 0x2463ffff, 0x3e00008, 0xaf4300fc, 
+0x8f430324, 0x1021, 0x24630001, 0x3e00008, 
+0xaf430324, 0x3e00008, 0x0, 0x8f880100, 
+0x276247e0, 0x8f830108, 0x15020002, 0x25090020, 
+0x27694000, 0x1123000f, 0x8fa20010, 0xad040000, 
+0xad050004, 0xad060008, 0xa507000e, 0x8fa30014, 
+0xad020018, 0x8fa20018, 0xad03001c, 0x25030016, 
+0xad020010, 0xad030014, 0xaf890100, 0x3e00008, 
+0x24020001, 0x8f430328, 0x1021, 0x24630001, 
+0x3e00008, 0xaf430328, 0x3e00008, 0x0, 
+0x0, 0x0, 0x0, 0x0 };
+static int tigon2FwRodata[/*(MAX_RODATA_LEN/4) + 1*/] = {
+0x24486561, 0x6465723a, 0x202f7072, 
+0x6f6a6563, 0x74732f72, 0x63732f73, 0x772f6765, 
+0x2f2e2f6e, 0x69632f66, 0x77322f63, 0x6f6d6d6f, 
+0x6e2f6677, 0x6d61696e, 0x2e632c76, 0x20312e31, 
+0x2e322e34, 0x35203139, 0x39392f30, 0x312f3234, 
+0x2030303a, 0x31303a35, 0x35207368, 0x75616e67, 
+0x20457870, 0x20240000, 0x65767452, 0x6e674600, 
+0x51657674, 0x46000000, 0x51657674, 0x505f4600, 
+0x4d657674, 0x526e6746, 0x0, 0x4d516576, 
+0x74460000, 0x4d516576, 0x505f4600, 0x5173436f, 
+0x6e495f46, 0x0, 0x5173436f, 0x6e734600, 
+0x51725072, 0x6f644600, 0x6261644d, 0x656d537a, 
+0x0, 0x68775665, 0x72000000, 0x62616448, 
+0x77566572, 0x0, 0x2a2a4441, 0x574e5f41, 
+0x0, 0x74785278, 0x4266537a, 0x0, 
+0x62664174, 0x6e4d726b, 0x0, 0x7265645a, 
+0x6f6e6531, 0x0, 0x70636943, 0x6f6e6600, 
+0x67656e43, 0x6f6e6600, 0x2a646d61, 0x5244666c, 
+0x0, 0x2a50414e, 0x49432a00, 0x2e2e2f2e, 
+0x2e2f2e2e, 0x2f2e2e2f, 0x2e2e2f73, 0x72632f6e, 
+0x69632f66, 0x77322f63, 0x6f6d6d6f, 0x6e2f6677, 
+0x6d61696e, 0x2e630000, 0x72636246, 0x6c616773, 
+0x0, 0x62616452, 0x78526362, 0x0, 
+0x676c6f62, 0x466c6773, 0x0, 0x2b5f6469, 
+0x73705f6c, 0x6f6f7000, 0x2b65765f, 0x68616e64, 
+0x6c657200, 0x63616e74, 0x31446d61, 0x0, 
+0x2b715f64, 0x6d615f74, 0x6f5f6e69, 0x635f636b, 
+0x73756d00, 0x2b685f73, 0x656e645f, 0x64617461, 
+0x5f726561, 0x64795f63, 0x6b73756d, 0x0, 
+0x2b685f64, 0x6d615f72, 0x645f6173, 0x73697374, 
+0x5f636b73, 0x756d0000, 0x74436b73, 0x6d4f6e00, 
+0x2b715f64, 0x6d615f74, 0x6f5f6e69, 0x63000000, 
+0x2b685f73, 0x656e645f, 0x64617461, 0x5f726561, 
+0x64790000, 0x2b685f64, 0x6d615f72, 0x645f6173, 
+0x73697374, 0x0, 0x74436b73, 0x6d4f6666, 
+0x0, 0x2b685f73, 0x656e645f, 0x62645f72, 
+0x65616479, 0x0, 0x68737453, 0x52696e67, 
+0x0, 0x62616453, 0x52696e67, 0x0, 
+0x6e696353, 0x52696e67, 0x0, 0x77446d61, 
+0x416c6c41, 0x0, 0x2b715f64, 0x6d615f74, 
+0x6f5f686f, 0x73745f63, 0x6b73756d, 0x0, 
+0x2b685f6d, 0x61635f72, 0x785f636f, 0x6d705f63, 
+0x6b73756d, 0x0, 0x2b685f64, 0x6d615f77, 
+0x725f6173, 0x73697374, 0x5f636b73, 0x756d0000, 
+0x72436b73, 0x6d4f6e00, 0x2b715f64, 0x6d615f74, 
+0x6f5f686f, 0x73740000, 0x2b685f6d, 0x61635f72, 
+0x785f636f, 0x6d700000, 0x2b685f64, 0x6d615f77, 
+0x725f6173, 0x73697374, 0x0, 0x72436b73, 
+0x6d4f6666, 0x0, 0x2b685f72, 0x6563765f, 
+0x62645f72, 0x65616479, 0x0, 0x2b685f72, 
+0x6563765f, 0x6a756d62, 0x6f5f6264, 0x5f726561, 
+0x64790000, 0x2b685f72, 0x6563765f, 0x6d696e69, 
+0x5f62645f, 0x72656164, 0x79000000, 0x2b6d685f, 
+0x636f6d6d, 0x616e6400, 0x2b685f74, 0x696d6572, 
+0x0, 0x2b685f64, 0x6f5f7570, 0x64617465, 
+0x5f74785f, 0x636f6e73, 0x0, 0x2b685f64, 
+0x6f5f7570, 0x64617465, 0x5f72785f, 0x70726f64, 
+0x0, 0x2b636b73, 0x756d3136, 0x0, 
+0x2b706565, 0x6b5f6d61, 0x635f7278, 0x5f776100, 
+0x2b706565, 0x6b5f6d61, 0x635f7278, 0x0, 
+0x2b646571, 0x5f6d6163, 0x5f727800, 0x2b685f6d, 
+0x61635f72, 0x785f6174, 0x746e0000, 0x62616452, 
+0x6574537a, 0x0, 0x72784264, 0x4266537a, 
+0x0, 0x2b6e756c, 0x6c5f6861, 0x6e646c65, 
+0x72000000, 0x66774f70, 0x4661696c, 0x0, 
+0x2b685f75, 0x70646174, 0x655f6c65, 0x64340000, 
+0x2b685f75, 0x70646174, 0x655f6c65, 0x64360000, 
+0x2b685f75, 0x70646174, 0x655f6c65, 0x64320000, 
+0x696e7453, 0x74617465, 0x0, 0x2a2a696e, 
+0x69744370, 0x0, 0x23736372, 0x65616d00, 
+0x69537461, 0x636b4572, 0x0, 0x70726f62, 
+0x654d656d, 0x0, 0x2a2a4441, 0x574e5f42, 
+0x0, 0x2b73775f, 0x646d615f, 0x61737369, 
+0x73745f70, 0x6c75735f, 0x74696d65, 0x72000000, 
+0x2b267072, 0x656c6f61, 0x645f7772, 0x5f646573, 
+0x63720000, 0x2b267072, 0x656c6f61, 0x645f7264, 
+0x5f646573, 0x63720000, 0x2b685f68, 0x665f7469, 
+0x6d657200, 0x24486561, 0x6465723a, 0x202f7072, 
+0x6f6a6563, 0x74732f72, 0x63732f73, 0x772f6765, 
+0x2f2e2f6e, 0x69632f66, 0x77322f63, 0x6f6d6d6f, 
+0x6e2f7469, 0x6d65722e, 0x632c7620, 0x312e312e, 
+0x322e3335, 0x20313939, 0x392f3031, 0x2f323720, 
+0x31393a30, 0x393a3530, 0x20686179, 0x65732045, 
+0x78702024, 0x0, 0x65767452, 0x6e674600, 
+0x51657674, 0x46000000, 0x51657674, 0x505f4600, 
+0x4d657674, 0x526e6746, 0x0, 0x4d516576, 
+0x74460000, 0x4d516576, 0x505f4600, 0x5173436f, 
+0x6e495f46, 0x0, 0x5173436f, 0x6e734600, 
+0x51725072, 0x6f644600, 0x542d446d, 0x61526432, 
+0x0, 0x542d446d, 0x61526431, 0x0, 
+0x542d446d, 0x61526442, 0x0, 0x542d446d, 
+0x61577232, 0x0, 0x542d446d, 0x61577231, 
+0x0, 0x542d446d, 0x61577242, 0x0, 
+0x0, 0x24486561, 0x6465723a, 0x202f7072, 
+0x6f6a6563, 0x74732f72, 0x63732f73, 0x772f6765, 
+0x2f2e2f6e, 0x69632f66, 0x77322f63, 0x6f6d6d6f, 
+0x6e2f636f, 0x6d6d616e, 0x642e632c, 0x7620312e, 
+0x312e322e, 0x32382031, 0x3939392f, 0x30312f32, 
+0x30203139, 0x3a34393a, 0x34392073, 0x6875616e, 
+0x67204578, 0x70202400, 0x65767452, 0x6e674600, 
+0x51657674, 0x46000000, 0x51657674, 0x505f4600, 
+0x4d657674, 0x526e6746, 0x0, 0x4d516576, 
+0x74460000, 0x4d516576, 0x505f4600, 0x5173436f, 
+0x6e495f46, 0x0, 0x5173436f, 0x6e734600, 
+0x51725072, 0x6f644600, 0x3f48636d, 0x644d6278, 
+0x0, 0x3f636d64, 0x48737453, 0x0, 
+0x3f636d64, 0x4d634d64, 0x0, 0x3f636d64, 
+0x50726f6d, 0x0, 0x3f636d64, 0x4c696e6b, 
+0x0, 0x3f636d64, 0x45727200, 0x86ac, 
+0x8e5c, 0x8e5c, 0x8de4, 0x8b78, 
+0x8e30, 0x8e5c, 0x8790, 0x8800, 
+0x8990, 0x8a68, 0x8a34, 0x8e5c, 
+0x8870, 0x8b24, 0x8e5c, 0x8b34, 
+0x87b4, 0x8824, 0x0, 0x0, 
+0x0, 0x24486561, 0x6465723a, 0x202f7072, 
+0x6f6a6563, 0x74732f72, 0x63732f73, 0x772f6765, 
+0x2f2e2f6e, 0x69632f66, 0x77322f63, 0x6f6d6d6f, 
+0x6e2f6d63, 0x6173742e, 0x632c7620, 0x312e312e, 
+0x322e3820, 0x31393938, 0x2f31322f, 0x30382030, 
+0x323a3336, 0x3a333620, 0x73687561, 0x6e672045, 
+0x78702024, 0x0, 0x65767452, 0x6e674600, 
+0x51657674, 0x46000000, 0x51657674, 0x505f4600, 
+0x4d657674, 0x526e6746, 0x0, 0x4d516576, 
+0x74460000, 0x4d516576, 0x505f4600, 0x5173436f, 
+0x6e495f46, 0x0, 0x5173436f, 0x6e734600, 
+0x51725072, 0x6f644600, 0x6164644d, 0x63447570, 
+0x0, 0x6164644d, 0x6346756c, 0x0, 
+0x64656c4d, 0x634e6f45, 0x0, 0x0, 
+0x0, 0x24486561, 0x6465723a, 0x202f7072, 
+0x6f6a6563, 0x74732f72, 0x63732f73, 0x772f6765, 
+0x2f2e2f6e, 0x69632f66, 0x77322f63, 0x6f6d6d6f, 
+0x6e2f646d, 0x612e632c, 0x7620312e, 0x312e322e, 
+0x32342031, 0x3939382f, 0x31322f32, 0x31203030, 
+0x3a33333a, 0x30392073, 0x6875616e, 0x67204578, 
+0x70202400, 0x65767452, 0x6e674600, 0x51657674, 
+0x46000000, 0x51657674, 0x505f4600, 0x4d657674, 
+0x526e6746, 0x0, 0x4d516576, 0x74460000, 
+0x4d516576, 0x505f4600, 0x5173436f, 0x6e495f46, 
+0x0, 0x5173436f, 0x6e734600, 0x51725072, 
+0x6f644600, 0x7377446d, 0x614f6666, 0x0, 
+0x31446d61, 0x4f6e0000, 0x7377446d, 0x614f6e00, 
+0x2372446d, 0x6141544e, 0x0, 0x72446d61, 
+0x41544e30, 0x0, 0x72446d61, 0x41544e31, 
+0x0, 0x72446d61, 0x34476200, 0x2a50414e, 
+0x49432a00, 0x2e2e2f2e, 0x2e2f2e2e, 0x2f2e2e2f, 
+0x2e2e2f73, 0x72632f6e, 0x69632f66, 0x77322f63, 
+0x6f6d6d6f, 0x6e2f646d, 0x612e6300, 0x2377446d, 
+0x6141544e, 0x0, 0x77446d61, 0x41544e30, 
+0x0, 0x77446d61, 0x41544e31, 0x0, 
+0x77446d61, 0x34476200, 0x0, 0x0, 
+0x0, 0x24486561, 0x6465723a, 0x202f7072, 
+0x6f6a6563, 0x74732f72, 0x63732f73, 0x772f6765, 
+0x2f2e2f6e, 0x69632f66, 0x77322f63, 0x6f6d6d6f, 
+0x6e2f7472, 0x6163652e, 0x632c7620, 0x312e312e, 
+0x322e3520, 0x31393938, 0x2f30392f, 0x33302031, 
+0x383a3530, 0x3a323820, 0x73687561, 0x6e672045, 
+0x78702024, 0x0, 0x0, 0x0, 
+0x0, 0x24486561, 0x6465723a, 0x202f7072, 
+0x6f6a6563, 0x74732f72, 0x63732f73, 0x772f6765, 
+0x2f2e2f6e, 0x69632f66, 0x77322f63, 0x6f6d6d6f, 
+0x6e2f6461, 0x74612e63, 0x2c762031, 0x2e312e32, 
+0x2e313220, 0x31393939, 0x2f30312f, 0x32302031, 
+0x393a3439, 0x3a353120, 0x73687561, 0x6e672045, 
+0x78702024, 0x0, 0x46575f56, 0x45525349, 
+0x4f4e3a20, 0x23312046, 0x72692041, 0x70722037, 
+0x2031373a, 0x35373a35, 0x32205044, 0x54203230, 
+0x30300000, 0x46575f43, 0x4f4d5049, 0x4c455f54, 
+0x494d453a, 0x2031373a, 0x35373a35, 0x32000000, 
+0x46575f43, 0x4f4d5049, 0x4c455f42, 0x593a2064, 
+0x65767263, 0x73000000, 0x46575f43, 0x4f4d5049, 
+0x4c455f48, 0x4f53543a, 0x20636f6d, 0x70757465, 
+0x0, 0x46575f43, 0x4f4d5049, 0x4c455f44, 
+0x4f4d4149, 0x4e3a2065, 0x6e672e61, 0x6374656f, 
+0x6e2e636f, 0x6d000000, 0x46575f43, 0x4f4d5049, 
+0x4c45523a, 0x20676363, 0x20766572, 0x73696f6e, 
+0x20322e37, 0x2e320000, 0x0, 0x12041100, 
+0x0, 0x24486561, 0x6465723a, 0x202f7072, 
+0x6f6a6563, 0x74732f72, 0x63732f73, 0x772f6765, 
+0x2f2e2f6e, 0x69632f66, 0x77322f63, 0x6f6d6d6f, 
+0x6e2f6d65, 0x6d2e632c, 0x7620312e, 0x312e322e, 
+0x35203139, 0x39382f30, 0x392f3330, 0x2031383a, 
+0x35303a30, 0x38207368, 0x75616e67, 0x20457870, 
+0x20240000, 0x24486561, 0x6465723a, 0x202f7072, 
+0x6f6a6563, 0x74732f72, 0x63732f73, 0x772f6765, 
+0x2f2e2f6e, 0x69632f66, 0x77322f63, 0x6f6d6d6f, 
+0x6e2f7365, 0x6e642e63, 0x2c762031, 0x2e312e32, 
+0x2e343420, 0x31393938, 0x2f31322f, 0x32312030, 
+0x303a3333, 0x3a313820, 0x73687561, 0x6e672045, 
+0x78702024, 0x0, 0x65767452, 0x6e674600, 
+0x51657674, 0x46000000, 0x51657674, 0x505f4600, 
+0x4d657674, 0x526e6746, 0x0, 0x4d516576, 
+0x74460000, 0x4d516576, 0x505f4600, 0x5173436f, 
+0x6e495f46, 0x0, 0x5173436f, 0x6e734600, 
+0x51725072, 0x6f644600, 0x69736e74, 0x54637055, 
+0x0, 0x24486561, 0x6465723a, 0x202f7072, 
+0x6f6a6563, 0x74732f72, 0x63732f73, 0x772f6765, 
+0x2f2e2f6e, 0x69632f66, 0x77322f63, 0x6f6d6d6f, 
+0x6e2f7265, 0x63762e63, 0x2c762031, 0x2e312e32, 
+0x2e353320, 0x31393939, 0x2f30312f, 0x31362030, 
+0x323a3535, 0x3a343320, 0x73687561, 0x6e672045, 
+0x78702024, 0x0, 0x65767452, 0x6e674600, 
+0x51657674, 0x46000000, 0x51657674, 0x505f4600, 
+0x4d657674, 0x526e6746, 0x0, 0x4d516576, 
+0x74460000, 0x4d516576, 0x505f4600, 0x5173436f, 
+0x6e495f46, 0x0, 0x5173436f, 0x6e734600, 
+0x51725072, 0x6f644600, 0x724d6163, 0x43686b30, 
+0x0, 0x72784672, 0x6d324c67, 0x0, 
+0x72784e6f, 0x53744264, 0x0, 0x72784e6f, 
+0x4d694264, 0x0, 0x72784e6f, 0x4a6d4264, 
+0x0, 0x7278436b, 0x446d6146, 0x0, 
+0x72785144, 0x6d457846, 0x0, 0x72785144, 
+0x6d614600, 0x72785144, 0x4c426446, 0x0, 
+0x72785144, 0x6d426446, 0x0, 0x72784372, 
+0x63506164, 0x0, 0x72536d51, 0x446d6146, 
+0x0, 0x24486561, 0x6465723a, 0x202f7072, 
+0x6f6a6563, 0x74732f72, 0x63732f73, 0x772f6765, 
+0x2f2e2f6e, 0x69632f66, 0x77322f63, 0x6f6d6d6f, 
+0x6e2f6d61, 0x632e632c, 0x7620312e, 0x312e322e, 
+0x32322031, 0x3939382f, 0x31322f30, 0x38203032, 
+0x3a33363a, 0x33302073, 0x6875616e, 0x67204578, 
+0x70202400, 0x65767452, 0x6e674600, 0x51657674, 
+0x46000000, 0x51657674, 0x505f4600, 0x4d657674, 
+0x526e6746, 0x0, 0x4d516576, 0x74460000, 
+0x4d516576, 0x505f4600, 0x5173436f, 0x6e495f46, 
+0x0, 0x5173436f, 0x6e734600, 0x51725072, 
+0x6f644600, 0x6d616354, 0x68726573, 0x0, 
+0x23744d61, 0x6341544e, 0x0, 0x23724d61, 
+0x6341544e, 0x0, 0x72656d41, 0x73737274, 
+0x0, 0x6c696e6b, 0x444f574e, 0x0, 
+0x6c696e6b, 0x55500000, 0x0, 0x0, 
+0x0, 0x24486561, 0x6465723a, 0x202f7072, 
+0x6f6a6563, 0x74732f72, 0x63732f73, 0x772f6765, 
+0x2f2e2f6e, 0x69632f66, 0x77322f63, 0x6f6d6d6f, 
+0x6e2f636b, 0x73756d2e, 0x632c7620, 0x312e312e, 
+0x322e3920, 0x31393939, 0x2f30312f, 0x31342030, 
+0x303a3033, 0x3a343820, 0x73687561, 0x6e672045, 
+0x78702024, 0x0, 0x65767452, 0x6e674600, 
+0x51657674, 0x46000000, 0x51657674, 0x505f4600, 
+0x4d657674, 0x526e6746, 0x0, 0x4d516576, 
+0x74460000, 0x4d516576, 0x505f4600, 0x5173436f, 
+0x6e495f46, 0x0, 0x5173436f, 0x6e734600, 
+0x51725072, 0x6f644600, 0x0, 0x0, 
+0x0, 0x50726f62, 0x65506879, 0x0, 
+0x6c6e6b41, 0x53535254, 0x0, 0x109a4, 
+0x10a1c, 0x10a50, 0x10a7c, 0x11050, 
+0x10aa8, 0x10b10, 0x111fc, 0x10dc0, 
+0x10c68, 0x10c80, 0x10cc4, 0x10cec, 
+0x10d0c, 0x10d34, 0x111fc, 0x10dc0, 
+0x10df8, 0x10e10, 0x10e40, 0x10e68, 
+0x10e88, 0x10eb0, 0x0, 0x10fdc, 
+0x11008, 0x1102c, 0x111fc, 0x11050, 
+0x11078, 0x11108, 0x0, 0x0, 
+0x0, 0x1186c, 0x1193c, 0x11a14, 
+0x11ae4, 0x11b40, 0x11c1c, 0x11c44, 
+0x11d20, 0x11d48, 0x11ef0, 0x11f18, 
+0x120c0, 0x122b8, 0x1254c, 0x12460, 
+0x1254c, 0x12578, 0x120e8, 0x12290, 
+0x7273745f, 0x676d6969, 0x0, 0x12608, 
+0x12640, 0x12728, 0x13374, 0x133b4, 
+0x133cc, 0x7365746c, 0x6f6f7000, 0x0, 
+0x0, 0x13bbc, 0x13bfc, 0x13c8c, 
+0x13cd0, 0x13d34, 0x13dc0, 0x13df4, 
+0x13e7c, 0x13f14, 0x13fe4, 0x14024, 
+0x140a8, 0x140cc, 0x141dc, 0x646f4261, 
+0x73655067, 0x0, 0x0, 0x0, 
+0x0, 0x73746d61, 0x634c4e4b, 0x0, 
+0x6765746d, 0x636c6e6b, 0x0, 0x14ed8, 
+0x14ed8, 0x14b8c, 0x14bd8, 0x14c24, 
+0x14ed8, 0x7365746d, 0x61636163, 0x74000000, 
+0x0, 0x0 };
+static int tigon2FwData[/*(MAX_DATA_LEN/4) + 1*/] = {
+0x1, 
+0x1, 0x1, 0xc001fc, 0x3ffc, 
+0xc00000, 0x416c7465, 0x6f6e2041, 0x63654e49, 
+0x43205600, 0x0, 0x0, 0x0, 
+0x0, 0x0, 0x0, 0x416c7465, 
+0x6f6e2041, 0x63654e49, 0x43205600, 0x42424242, 
+0x0, 0x0, 0x0, 0x1ffffc, 
+0x1fff7c, 0x0, 0x0, 0x0, 
+0x0, 0x0, 0x0, 0x60cf00, 
+0x60, 0xcf000000, 0x0, 0x0, 
+0x0, 0x0, 0x0, 0x0, 
+0x0, 0x0, 0x0, 0x0, 
+0x0, 0x0, 0x0, 0x0, 
+0x0, 0x0, 0x0, 0x0, 
+0x0, 0x0, 0x3, 0x0, 
+0x1, 0x0, 0x0, 0x0, 
+0x1, 0x0, 0x1, 0x0, 
+0x0, 0x0, 0x0, 0x1, 
+0x1, 0x0, 0x0, 0x0, 
+0x0, 0x0, 0x1000000, 0x21000000, 
+0x12000140, 0x0, 0x0, 0x20000000, 
+0x120000a0, 0x0, 0x12000060, 0x12000180, 
+0x120001e0, 0x0, 0x0, 0x0, 
+0x1, 0x0, 0x0, 0x0, 
+0x0, 0x0, 0x0, 0x2, 
+0x0, 0x0, 0x30001, 0x1, 
+0x30201, 0x0, 0x0, 0x1010101, 
+0x1010100, 0x10100, 0x1010001, 0x10001, 
+0x1000101, 0x101, 0x0, 0x0 };
--- /dev/null
+++ b/os/pc/etherif.h
@@ -1,0 +1,39 @@
+enum {
+	MaxEther	= 24,
+	Ntypes		= 8,
+};
+
+typedef struct Ether Ether;
+struct Ether {
+	ISAConf;			/* hardware info */
+
+	int	ctlrno;
+	int	tbdf;			/* type+busno+devno+funcno */
+	int	minmtu;
+	int 	maxmtu;
+	uchar	ea[Eaddrlen];
+
+	void	(*attach)(Ether*);	/* filled in by reset routine */
+	void	(*detach)(Ether*);
+	void	(*transmit)(Ether*);
+	void	(*interrupt)(Ureg*, void*);
+	long	(*ifstat)(Ether*, void*, long, ulong);
+	long 	(*ctl)(Ether*, void*, long); /* custom ctl messages */
+	void	(*power)(Ether*, int);	/* power on/off */
+	void	(*shutdown)(Ether*);	/* shutdown hardware before reboot */
+	void	*ctlr;
+
+	Queue*	oq;
+
+	Netif;
+};
+
+extern Block* etheriq(Ether*, Block*, int);
+extern void addethercard(char*, int(*)(Ether*));
+extern ulong ethercrc(uchar*, int);
+extern int parseether(uchar*, char*);
+
+#define NEXT(x, l)	(((x)+1)%(l))
+#define PREV(x, l)	(((x) == 0) ? (l)-1: (x)-1)
+#define	HOWMANY(x, y)	(((x)+((y)-1))/(y))
+#define ROUNDUP(x, y)	(HOWMANY((x), (y))*(y))
--- /dev/null
+++ b/os/pc/etherigbe.c
@@ -1,0 +1,1989 @@
+/*
+ * Intel 8254[340]NN Gigabit Ethernet Controller
+ * as found on the Intel PRO/1000 series of adapters:
+ *	82543GC	Intel PRO/1000 T
+ *	82544EI Intel PRO/1000 XT
+ *	82540EM Intel PRO/1000 MT
+ *	82541[GP]I
+ *	82547GI
+ *	82546GB
+ *	82546EB
+ * To Do:
+ *	finish autonegotiation code;
+ *	integrate fiber stuff back in (this ONLY handles
+ *	the CAT5 cards at the moment);
+ *	add checksum-offload;
+ *	add tuning control via ctl file;
+ *	this driver is little-endian specific.
+ */
+#include "u.h"
+#include "../port/lib.h"
+#include "mem.h"
+#include "dat.h"
+#include "fns.h"
+#include "io.h"
+#include "../port/error.h"
+#include "../port/netif.h"
+
+#include "etherif.h"
+#include "ethermii.h"
+
+enum {
+	i82542		= (0x1000<<16)|0x8086,
+	i82543gc	= (0x1004<<16)|0x8086,
+	i82544ei	= (0x1008<<16)|0x8086,
+	i82547ei	= (0x1019<<16)|0x8086,
+	i82540em	= (0x100E<<16)|0x8086,
+	i82540eplp	= (0x101E<<16)|0x8086,
+	i82545gmc	= (0x1026<<16)|0x8086,
+	i82547gi	= (0x1075<<16)|0x8086,
+	i82541gi	= (0x1076<<16)|0x8086,
+	i82541gi2	= (0x1077<<16)|0x8086,
+	i82546gb	= (0x1079<<16)|0x8086,
+	i82541pi	= (0x107c<<16)|0x8086,
+	i82546eb	= (0x1010<<16)|0x8086,
+};
+
+enum {
+	Ctrl		= 0x00000000,	/* Device Control */
+	Ctrldup		= 0x00000004,	/* Device Control Duplicate */
+	Status		= 0x00000008,	/* Device Status */
+	Eecd		= 0x00000010,	/* EEPROM/Flash Control/Data */
+	Ctrlext		= 0x00000018,	/* Extended Device Control */
+	Mdic		= 0x00000020,	/* MDI Control */
+	Fcal		= 0x00000028,	/* Flow Control Address Low */
+	Fcah		= 0x0000002C,	/* Flow Control Address High */
+	Fct		= 0x00000030,	/* Flow Control Type */
+	Icr		= 0x000000C0,	/* Interrupt Cause Read */
+	Ics		= 0x000000C8,	/* Interrupt Cause Set */
+	Ims		= 0x000000D0,	/* Interrupt Mask Set/Read */
+	Imc		= 0x000000D8,	/* Interrupt mask Clear */
+	Rctl		= 0x00000100,	/* Receive Control */
+	Fcttv		= 0x00000170,	/* Flow Control Transmit Timer Value */
+	Txcw		= 0x00000178,	/* Transmit Configuration Word */
+	Rxcw		= 0x00000180,	/* Receive Configuration Word */
+	Tctl		= 0x00000400,	/* Transmit Control */
+	Tipg		= 0x00000410,	/* Transmit IPG */
+	Tbt		= 0x00000448,	/* Transmit Burst Timer */
+	Ait		= 0x00000458,	/* Adaptive IFS Throttle */
+	Fcrtl		= 0x00002160,	/* Flow Control RX Threshold Low */
+	Fcrth		= 0x00002168,	/* Flow Control Rx Threshold High */
+	Rdfh		= 0x00002410,	/* Receive data fifo head */
+	Rdft		= 0x00002418,	/* Receive data fifo tail */
+	Rdfhs		= 0x00002420,	/* Receive data fifo head saved */
+	Rdfts		= 0x00002428,	/* Receive data fifo tail saved */
+	Rdfpc		= 0x00002430,	/* Receive data fifo packet count */
+	Rdbal		= 0x00002800,	/* Rd Base Address Low */
+	Rdbah		= 0x00002804,	/* Rd Base Address High */
+	Rdlen		= 0x00002808,	/* Receive Descriptor Length */
+	Rdh		= 0x00002810,	/* Receive Descriptor Head */
+	Rdt		= 0x00002818,	/* Receive Descriptor Tail */
+	Rdtr		= 0x00002820,	/* Receive Descriptor Timer Ring */
+	Rxdctl		= 0x00002828,	/* Receive Descriptor Control */
+	Radv		= 0x0000282C,	/* Receive Interrupt Absolute Delay Timer */
+	Txdmac		= 0x00003000,	/* Transfer DMA Control */
+	Ett		= 0x00003008,	/* Early Transmit Control */
+	Tdfh		= 0x00003410,	/* Transmit data fifo head */
+	Tdft		= 0x00003418,	/* Transmit data fifo tail */
+	Tdfhs		= 0x00003420,	/* Transmit data Fifo Head saved */
+	Tdfts		= 0x00003428,	/* Transmit data fifo tail saved */
+	Tdfpc		= 0x00003430,	/* Trasnmit data Fifo packet count */
+	Tdbal		= 0x00003800,	/* Td Base Address Low */
+	Tdbah		= 0x00003804,	/* Td Base Address High */
+	Tdlen		= 0x00003808,	/* Transmit Descriptor Length */
+	Tdh		= 0x00003810,	/* Transmit Descriptor Head */
+	Tdt		= 0x00003818,	/* Transmit Descriptor Tail */
+	Tidv		= 0x00003820,	/* Transmit Interrupt Delay Value */
+	Txdctl		= 0x00003828,	/* Transmit Descriptor Control */
+	Tadv		= 0x0000382C,	/* Transmit Interrupt Absolute Delay Timer */
+
+	Statistics	= 0x00004000,	/* Start of Statistics Area */
+	Gorcl		= 0x88/4,	/* Good Octets Received Count */
+	Gotcl		= 0x90/4,	/* Good Octets Transmitted Count */
+	Torl		= 0xC0/4,	/* Total Octets Received */
+	Totl		= 0xC8/4,	/* Total Octets Transmitted */
+	Nstatistics	= 64,
+
+	Rxcsum		= 0x00005000,	/* Receive Checksum Control */
+	Mta		= 0x00005200,	/* Multicast Table Array */
+	Ral		= 0x00005400,	/* Receive Address Low */
+	Rah		= 0x00005404,	/* Receive Address High */
+	Manc		= 0x00005820,	/* Management Control */
+};
+
+enum {					/* Ctrl */
+	Bem		= 0x00000002,	/* Big Endian Mode */
+	Prior		= 0x00000004,	/* Priority on the PCI bus */
+	Lrst		= 0x00000008,	/* Link Reset */
+	Asde		= 0x00000020,	/* Auto-Speed Detection Enable */
+	Slu		= 0x00000040,	/* Set Link Up */
+	Ilos		= 0x00000080,	/* Invert Loss of Signal (LOS) */
+	SspeedMASK	= 0x00000300,	/* Speed Selection */
+	SspeedSHIFT	= 8,
+	Sspeed10	= 0x00000000,	/* 10Mb/s */
+	Sspeed100	= 0x00000100,	/* 100Mb/s */
+	Sspeed1000	= 0x00000200,	/* 1000Mb/s */
+	Frcspd		= 0x00000800,	/* Force Speed */
+	Frcdplx		= 0x00001000,	/* Force Duplex */
+	SwdpinsloMASK	= 0x003C0000,	/* Software Defined Pins - lo nibble */
+	SwdpinsloSHIFT	= 18,
+	SwdpioloMASK	= 0x03C00000,	/* Software Defined Pins - I or O */
+	SwdpioloSHIFT	= 22,
+	Devrst		= 0x04000000,	/* Device Reset */
+	Rfce		= 0x08000000,	/* Receive Flow Control Enable */
+	Tfce		= 0x10000000,	/* Transmit Flow Control Enable */
+	Vme		= 0x40000000,	/* VLAN Mode Enable */
+};
+
+enum {					/* Status */
+	Lu		= 0x00000002,	/* Link Up */
+	Tckok		= 0x00000004,	/* Transmit clock is running */
+	Rbcok		= 0x00000008,	/* Receive clock is running */
+	Txoff		= 0x00000010,	/* Transmission Paused */
+	Tbimode		= 0x00000020,	/* TBI Mode Indication */
+	LspeedMASK	= 0x000000C0,	/* Link Speed Setting */
+	LspeedSHIFT	= 6,
+	Lspeed10	= 0x00000000,	/* 10Mb/s */
+	Lspeed100	= 0x00000040,	/* 100Mb/s */
+	Lspeed1000	= 0x00000080,	/* 1000Mb/s */
+	Mtxckok		= 0x00000400,	/* MTX clock is running */
+	Pci66		= 0x00000800,	/* PCI Bus speed indication */
+	Bus64		= 0x00001000,	/* PCI Bus width indication */
+	Pcixmode	= 0x00002000,	/* PCI-X mode */
+	PcixspeedMASK	= 0x0000C000,	/* PCI-X bus speed */
+	PcixspeedSHIFT	= 14,
+	Pcix66		= 0x00000000,	/* 50-66MHz */
+	Pcix100		= 0x00004000,	/* 66-100MHz */
+	Pcix133		= 0x00008000,	/* 100-133MHz */
+};
+
+enum {					/* Ctrl and Status */
+	Fd		= 0x00000001,	/* Full-Duplex */
+	AsdvMASK	= 0x00000300,
+	AsdvSHIFT	= 8,
+	Asdv10		= 0x00000000,	/* 10Mb/s */
+	Asdv100		= 0x00000100,	/* 100Mb/s */
+	Asdv1000	= 0x00000200,	/* 1000Mb/s */
+};
+
+enum {					/* Eecd */
+	Sk		= 0x00000001,	/* Clock input to the EEPROM */
+	Cs		= 0x00000002,	/* Chip Select */
+	Di		= 0x00000004,	/* Data Input to the EEPROM */
+	Do		= 0x00000008,	/* Data Output from the EEPROM */
+	Areq		= 0x00000040,	/* EEPROM Access Request */
+	Agnt		= 0x00000080,	/* EEPROM Access Grant */
+	Eepresent	= 0x00000100,	/* EEPROM Present */
+	Eesz256		= 0x00000200,	/* EEPROM is 256 words not 64 */
+	Eeszaddr	= 0x00000400,	/* EEPROM size for 8254[17] */
+	Spi		= 0x00002000,	/* EEPROM is SPI not Microwire */
+};
+
+enum {					/* Ctrlext */
+	Gpien		= 0x0000000F,	/* General Purpose Interrupt Enables */
+	SwdpinshiMASK	= 0x000000F0,	/* Software Defined Pins - hi nibble */
+	SwdpinshiSHIFT	= 4,
+	SwdpiohiMASK	= 0x00000F00,	/* Software Defined Pins - I or O */
+	SwdpiohiSHIFT	= 8,
+	Asdchk		= 0x00001000,	/* ASD Check */
+	Eerst		= 0x00002000,	/* EEPROM Reset */
+	Ips		= 0x00004000,	/* Invert Power State */
+	Spdbyps		= 0x00008000,	/* Speed Select Bypass */
+};
+
+enum {					/* EEPROM content offsets */
+	Ea		= 0x00,		/* Ethernet Address */
+	Cf		= 0x03,		/* Compatibility Field */
+	Pba		= 0x08,		/* Printed Board Assembly number */
+	Icw1		= 0x0A,		/* Initialization Control Word 1 */
+	Sid		= 0x0B,		/* Subsystem ID */
+	Svid		= 0x0C,		/* Subsystem Vendor ID */
+	Did		= 0x0D,		/* Device ID */
+	Vid		= 0x0E,		/* Vendor ID */
+	Icw2		= 0x0F,		/* Initialization Control Word 2 */
+};
+
+enum {					/* Mdic */
+	MDIdMASK	= 0x0000FFFF,	/* Data */
+	MDIdSHIFT	= 0,
+	MDIrMASK	= 0x001F0000,	/* PHY Register Address */
+	MDIrSHIFT	= 16,
+	MDIpMASK	= 0x03E00000,	/* PHY Address */
+	MDIpSHIFT	= 21,
+	MDIwop		= 0x04000000,	/* Write Operation */
+	MDIrop		= 0x08000000,	/* Read Operation */
+	MDIready	= 0x10000000,	/* End of Transaction */
+	MDIie		= 0x20000000,	/* Interrupt Enable */
+	MDIe		= 0x40000000,	/* Error */
+};
+
+enum {					/* Icr, Ics, Ims, Imc */
+	Txdw		= 0x00000001,	/* Transmit Descriptor Written Back */
+	Txqe		= 0x00000002,	/* Transmit Queue Empty */
+	Lsc		= 0x00000004,	/* Link Status Change */
+	Rxseq		= 0x00000008,	/* Receive Sequence Error */
+	Rxdmt0		= 0x00000010,	/* Rd Minimum Threshold Reached */
+	Rxo		= 0x00000040,	/* Receiver Overrun */
+	Rxt0		= 0x00000080,	/* Receiver Timer Interrupt */
+	Mdac		= 0x00000200,	/* MDIO Access Completed */
+	Rxcfg		= 0x00000400,	/* Receiving /C/ ordered sets */
+	Gpi0		= 0x00000800,	/* General Purpose Interrupts */
+	Gpi1		= 0x00001000,
+	Gpi2		= 0x00002000,
+	Gpi3		= 0x00004000,
+};
+
+/*
+ * The Mdic register isn't implemented on the 82543GC,
+ * the software defined pins are used instead.
+ * These definitions work for the Intel PRO/1000 T Server Adapter.
+ * The direction pin bits are read from the EEPROM.
+ */
+enum {
+	Mdd		= ((1<<2)<<SwdpinsloSHIFT),	/* data */
+	Mddo		= ((1<<2)<<SwdpioloSHIFT),	/* pin direction */
+	Mdc		= ((1<<3)<<SwdpinsloSHIFT),	/* clock */
+	Mdco		= ((1<<3)<<SwdpioloSHIFT),	/* pin direction */
+	Mdr		= ((1<<0)<<SwdpinshiSHIFT),	/* reset */
+	Mdro		= ((1<<0)<<SwdpiohiSHIFT),	/* pin direction */
+};
+
+enum {					/* Txcw */
+	TxcwFd		= 0x00000020,	/* Full Duplex */
+	TxcwHd		= 0x00000040,	/* Half Duplex */
+	TxcwPauseMASK	= 0x00000180,	/* Pause */
+	TxcwPauseSHIFT	= 7,
+	TxcwPs		= (1<<TxcwPauseSHIFT),	/* Pause Supported */
+	TxcwAs		= (2<<TxcwPauseSHIFT),	/* Asymmetric FC desired */
+	TxcwRfiMASK	= 0x00003000,	/* Remote Fault Indication */
+	TxcwRfiSHIFT	= 12,
+	TxcwNpr		= 0x00008000,	/* Next Page Request */
+	TxcwConfig	= 0x40000000,	/* Transmit COnfig Control */
+	TxcwAne		= 0x80000000,	/* Auto-Negotiation Enable */
+};
+
+enum {					/* Rxcw */
+	Rxword		= 0x0000FFFF,	/* Data from auto-negotiation process */
+	Rxnocarrier	= 0x04000000,	/* Carrier Sense indication */
+	Rxinvalid	= 0x08000000,	/* Invalid Symbol during configuration */
+	Rxchange	= 0x10000000,	/* Change to the Rxword indication */
+	Rxconfig	= 0x20000000,	/* /C/ order set reception indication */
+	Rxsync		= 0x40000000,	/* Lost bit synchronization indication */
+	Anc		= 0x80000000,	/* Auto Negotiation Complete */
+};
+
+enum {					/* Rctl */
+	Rrst		= 0x00000001,	/* Receiver Software Reset */
+	Ren		= 0x00000002,	/* Receiver Enable */
+	Sbp		= 0x00000004,	/* Store Bad Packets */
+	Upe		= 0x00000008,	/* Unicast Promiscuous Enable */
+	Mpe		= 0x00000010,	/* Multicast Promiscuous Enable */
+	Lpe		= 0x00000020,	/* Long Packet Reception Enable */
+	LbmMASK		= 0x000000C0,	/* Loopback Mode */
+	LbmOFF		= 0x00000000,	/* No Loopback */
+	LbmTBI		= 0x00000040,	/* TBI Loopback */
+	LbmMII		= 0x00000080,	/* GMII/MII Loopback */
+	LbmXCVR		= 0x000000C0,	/* Transceiver Loopback */
+	RdtmsMASK	= 0x00000300,	/* Rd Minimum Threshold Size */
+	RdtmsHALF	= 0x00000000,	/* Threshold is 1/2 Rdlen */
+	RdtmsQUARTER	= 0x00000100,	/* Threshold is 1/4 Rdlen */
+	RdtmsEIGHTH	= 0x00000200,	/* Threshold is 1/8 Rdlen */
+	MoMASK		= 0x00003000,	/* Multicast Offset */
+	Mo47b36		= 0x00000000,	/* bits [47:36] of received address */
+	Mo46b35		= 0x00001000,	/* bits [46:35] of received address */
+	Mo45b34		= 0x00002000,	/* bits [45:34] of received address */
+	Mo43b32		= 0x00003000,	/* bits [43:32] of received address */
+	Bam		= 0x00008000,	/* Broadcast Accept Mode */
+	BsizeMASK	= 0x00030000,	/* Receive Buffer Size */
+	Bsize2048	= 0x00000000,	/* Bsex = 0 */
+	Bsize1024	= 0x00010000,	/* Bsex = 0 */
+	Bsize512	= 0x00020000,	/* Bsex = 0 */
+	Bsize256	= 0x00030000,	/* Bsex = 0 */
+	Bsize16384	= 0x00010000,	/* Bsex = 1 */
+	Vfe		= 0x00040000,	/* VLAN Filter Enable */
+	Cfien		= 0x00080000,	/* Canonical Form Indicator Enable */
+	Cfi		= 0x00100000,	/* Canonical Form Indicator value */
+	Dpf		= 0x00400000,	/* Discard Pause Frames */
+	Pmcf		= 0x00800000,	/* Pass MAC Control Frames */
+	Bsex		= 0x02000000,	/* Buffer Size Extension */
+	Secrc		= 0x04000000,	/* Strip CRC from incoming packet */
+};
+
+enum {					/* Tctl */
+	Trst		= 0x00000001,	/* Transmitter Software Reset */
+	Ten		= 0x00000002,	/* Transmit Enable */
+	Psp		= 0x00000008,	/* Pad Short Packets */
+	CtMASK		= 0x00000FF0,	/* Collision Threshold */
+	CtSHIFT		= 4,
+	ColdMASK	= 0x003FF000,	/* Collision Distance */
+	ColdSHIFT	= 12,
+	Swxoff		= 0x00400000,	/* Sofware XOFF Transmission */
+	Pbe		= 0x00800000,	/* Packet Burst Enable */
+	Rtlc		= 0x01000000,	/* Re-transmit on Late Collision */
+	Nrtu		= 0x02000000,	/* No Re-transmit on Underrrun */
+};
+
+enum {					/* [RT]xdctl */
+	PthreshMASK	= 0x0000003F,	/* Prefetch Threshold */
+	PthreshSHIFT	= 0,
+	HthreshMASK	= 0x00003F00,	/* Host Threshold */
+	HthreshSHIFT	= 8,
+	WthreshMASK	= 0x003F0000,	/* Writeback Threshold */
+	WthreshSHIFT	= 16,
+	Gran		= 0x01000000,	/* Granularity */
+	LthreshMASK	= 0xFE000000,	/* Low Threshold */
+	LthreshSHIFT	= 25,
+};
+
+enum {					/* Rxcsum */
+	PcssMASK	= 0x000000FF,	/* Packet Checksum Start */
+	PcssSHIFT	= 0,
+	Ipofl		= 0x00000100,	/* IP Checksum Off-load Enable */
+	Tuofl		= 0x00000200,	/* TCP/UDP Checksum Off-load Enable */
+};
+
+enum {					/* Manc */
+	Arpen		= 0x00002000,	/* Enable ARP Request Filtering */
+};
+
+enum {					/* Receive Delay Timer Ring */
+	DelayMASK	= 0x0000FFFF,	/* delay timer in 1.024nS increments */
+	DelaySHIFT	= 0,
+	Fpd		= 0x80000000,	/* Flush partial Descriptor Block */
+};
+
+typedef struct Rd {			/* Receive Descriptor */
+	uint	addr[2];
+	ushort	length;
+	ushort	checksum;
+	uchar	status;
+	uchar	errors;
+	ushort	special;
+} Rd;
+
+enum {					/* Rd status */
+	Rdd		= 0x01,		/* Descriptor Done */
+	Reop		= 0x02,		/* End of Packet */
+	Ixsm		= 0x04,		/* Ignore Checksum Indication */
+	Vp		= 0x08,		/* Packet is 802.1Q (matched VET) */
+	Tcpcs		= 0x20,		/* TCP Checksum Calculated on Packet */
+	Ipcs		= 0x40,		/* IP Checksum Calculated on Packet */
+	Pif		= 0x80,		/* Passed in-exact filter */
+};
+
+enum {					/* Rd errors */
+	Ce		= 0x01,		/* CRC Error or Alignment Error */
+	Se		= 0x02,		/* Symbol Error */
+	Seq		= 0x04,		/* Sequence Error */
+	Cxe		= 0x10,		/* Carrier Extension Error */
+	Tcpe		= 0x20,		/* TCP/UDP Checksum Error */
+	Ipe		= 0x40,		/* IP Checksum Error */
+	Rxe		= 0x80,		/* RX Data Error */
+};
+
+typedef struct Td Td;
+struct Td {				/* Transmit Descriptor */
+	union {
+		uint	addr[2];	/* Data */
+		struct {		/* Context */
+			uchar	ipcss;
+			uchar	ipcso;
+			ushort	ipcse;
+			uchar	tucss;
+			uchar	tucso;
+			ushort	tucse;
+		};
+	};
+	uint	control;
+	uint	status;
+};
+
+enum {					/* Td control */
+	LenMASK		= 0x000FFFFF,	/* Data/Packet Length Field */
+	LenSHIFT	= 0,
+	DtypeCD		= 0x00000000,	/* Data Type 'Context Descriptor' */
+	DtypeDD		= 0x00100000,	/* Data Type 'Data Descriptor' */
+	PtypeTCP	= 0x01000000,	/* TCP/UDP Packet Type (CD) */
+	Teop		= 0x01000000,	/* End of Packet (DD) */
+	PtypeIP		= 0x02000000,	/* IP Packet Type (CD) */
+	Ifcs		= 0x02000000,	/* Insert FCS (DD) */
+	Tse		= 0x04000000,	/* TCP Segmentation Enable */
+	Rs		= 0x08000000,	/* Report Status */
+	Rps		= 0x10000000,	/* Report Status Sent */
+	Dext		= 0x20000000,	/* Descriptor Extension */
+	Vle		= 0x40000000,	/* VLAN Packet Enable */
+	Ide		= 0x80000000,	/* Interrupt Delay Enable */
+};
+
+enum {					/* Td status */
+	Tdd		= 0x00000001,	/* Descriptor Done */
+	Ec		= 0x00000002,	/* Excess Collisions */
+	Lc		= 0x00000004,	/* Late Collision */
+	Tu		= 0x00000008,	/* Transmit Underrun */
+	Iixsm		= 0x00000100,	/* Insert IP Checksum */
+	Itxsm		= 0x00000200,	/* Insert TCP/UDP Checksum */
+	HdrlenMASK	= 0x0000FF00,	/* Header Length (Tse) */
+	HdrlenSHIFT	= 8,
+	VlanMASK	= 0x0FFF0000,	/* VLAN Identifier */
+	VlanSHIFT	= 16,
+	Tcfi		= 0x10000000,	/* Canonical Form Indicator */
+	PriMASK		= 0xE0000000,	/* User Priority */
+	PriSHIFT	= 29,
+	MssMASK		= 0xFFFF0000,	/* Maximum Segment Size (Tse) */
+	MssSHIFT	= 16,
+};
+
+enum {
+	Nrd		= 256,		/* multiple of 8 */
+	Ntd		= 64,		/* multiple of 8 */
+	Nrb		= 1024,		/* private receive buffers per Ctlr */
+	Rbsz		= 2048,
+};
+
+typedef struct Ctlr Ctlr;
+typedef struct Ctlr {
+	int	port;
+	Pcidev*	pcidev;
+	Ctlr*	next;
+	Ether*	edev;
+	int	active;
+	int	started;
+	int	id;
+	int	cls;
+	ushort	eeprom[0x40];
+
+	QLock	alock;			/* attach */
+	void*	alloc;			/* receive/transmit descriptors */
+	int	nrd;
+	int	ntd;
+	int	nrb;			/* how many this Ctlr has in the pool */
+
+	int*	nic;
+	Lock	imlock;
+	int	im;			/* interrupt mask */
+
+	Mii*	mii;
+	Rendez	lrendez;
+	int	lim;
+
+	int	link;
+
+	QLock	slock;
+	uint	statistics[Nstatistics];
+	uint	lsleep;
+	uint	lintr;
+	uint	rsleep;
+	uint	rintr;
+	uint	txdw;
+	uint	tintr;
+	uint	ixsm;
+	uint	ipcs;
+	uint	tcpcs;
+
+	uchar	ra[Eaddrlen];		/* receive address */
+	ulong	mta[128];		/* multicast table array */
+
+	Rendez	rrendez;
+	int	rim;
+	int	rdfree;
+	Rd*	rdba;			/* receive descriptor base address */
+	Block**	rb;			/* receive buffers */
+	int	rdh;			/* receive descriptor head */
+	int	rdt;			/* receive descriptor tail */
+	int	rdtr;			/* receive delay timer ring value */
+
+	Lock	tlock;
+	int	tbusy;
+	int	tdfree;
+	Td*	tdba;			/* transmit descriptor base address */
+	Block**	tb;			/* transmit buffers */
+	int	tdh;			/* transmit descriptor head */
+	int	tdt;			/* transmit descriptor tail */
+
+	int	txcw;
+	int	fcrtl;
+	int	fcrth;
+} Ctlr;
+
+#define csr32r(c, r)	(*((c)->nic+((r)/4)))
+#define csr32w(c, r, v)	(*((c)->nic+((r)/4)) = (v))
+
+static Ctlr* igbectlrhead;
+static Ctlr* igbectlrtail;
+
+static Lock igberblock;		/* free receive Blocks */
+static Block* igberbpool;	/* receive Blocks for all igbe controllers */
+
+static char* statistics[Nstatistics] = {
+	"CRC Error",
+	"Alignment Error",
+	"Symbol Error",
+	"RX Error",
+	"Missed Packets",
+	"Single Collision",
+	"Excessive Collisions",
+	"Multiple Collision",
+	"Late Collisions",
+	nil,
+	"Collision",
+	"Transmit Underrun",
+	"Defer",
+	"Transmit - No CRS",
+	"Sequence Error",
+	"Carrier Extension Error",
+	"Receive Error Length",
+	nil,
+	"XON Received",
+	"XON Transmitted",
+	"XOFF Received",
+	"XOFF Transmitted",
+	"FC Received Unsupported",
+	"Packets Received (64 Bytes)",
+	"Packets Received (65-127 Bytes)",
+	"Packets Received (128-255 Bytes)",
+	"Packets Received (256-511 Bytes)",
+	"Packets Received (512-1023 Bytes)",
+	"Packets Received (1024-1522 Bytes)",
+	"Good Packets Received",
+	"Broadcast Packets Received",
+	"Multicast Packets Received",
+	"Good Packets Transmitted",
+	nil,
+	"Good Octets Received",
+	nil,
+	"Good Octets Transmitted",
+	nil,
+	nil,
+	nil,
+	"Receive No Buffers",
+	"Receive Undersize",
+	"Receive Fragment",
+	"Receive Oversize",
+	"Receive Jabber",
+	nil,
+	nil,
+	nil,
+	"Total Octets Received",
+	nil,
+	"Total Octets Transmitted",
+	nil,
+	"Total Packets Received",
+	"Total Packets Transmitted",
+	"Packets Transmitted (64 Bytes)",
+	"Packets Transmitted (65-127 Bytes)",
+	"Packets Transmitted (128-255 Bytes)",
+	"Packets Transmitted (256-511 Bytes)",
+	"Packets Transmitted (512-1023 Bytes)",
+	"Packets Transmitted (1024-1522 Bytes)",
+	"Multicast Packets Transmitted",
+	"Broadcast Packets Transmitted",
+	"TCP Segmentation Context Transmitted",
+	"TCP Segmentation Context Fail",
+};
+
+static long
+igbeifstat(Ether* edev, void* a, long n, ulong offset)
+{
+	Ctlr *ctlr;
+	char *p, *s;
+	int i, l, r;
+	uvlong tuvl, ruvl;
+
+	ctlr = edev->ctlr;
+	qlock(&ctlr->slock);
+	p = malloc(2*READSTR);
+	l = 0;
+	for(i = 0; i < Nstatistics; i++){
+		r = csr32r(ctlr, Statistics+i*4);
+		if((s = statistics[i]) == nil)
+			continue;
+		switch(i){
+		case Gorcl:
+		case Gotcl:
+		case Torl:
+		case Totl:
+			ruvl = r;
+			ruvl += ((uvlong)csr32r(ctlr, Statistics+(i+1)*4))<<32;
+			tuvl = ruvl;
+			tuvl += ctlr->statistics[i];
+			tuvl += ((uvlong)ctlr->statistics[i+1])<<32;
+			if(tuvl == 0)
+				continue;
+			ctlr->statistics[i] = tuvl;
+			ctlr->statistics[i+1] = tuvl>>32;
+			l += snprint(p+l, 2*READSTR-l, "%s: %llud %llud\n",
+				s, tuvl, ruvl);
+			i++;
+			break;
+
+		default:
+			ctlr->statistics[i] += r;
+			if(ctlr->statistics[i] == 0)
+				continue;
+			l += snprint(p+l, 2*READSTR-l, "%s: %ud %ud\n",
+				s, ctlr->statistics[i], r);
+			break;
+		}
+	}
+
+	l += snprint(p+l, 2*READSTR-l, "lintr: %ud %ud\n",
+		ctlr->lintr, ctlr->lsleep);
+	l += snprint(p+l, 2*READSTR-l, "rintr: %ud %ud\n",
+		ctlr->rintr, ctlr->rsleep);
+	l += snprint(p+l, 2*READSTR-l, "tintr: %ud %ud\n",
+		ctlr->tintr, ctlr->txdw);
+	l += snprint(p+l, 2*READSTR-l, "ixcs: %ud %ud %ud\n",
+		ctlr->ixsm, ctlr->ipcs, ctlr->tcpcs);
+	l += snprint(p+l, 2*READSTR-l, "rdtr: %ud\n", ctlr->rdtr);
+	l += snprint(p+l, 2*READSTR-l, "Ctrlext: %08x\n", csr32r(ctlr, Ctrlext));
+
+	l += snprint(p+l, 2*READSTR-l, "eeprom:");
+	for(i = 0; i < 0x40; i++){
+		if(i && ((i & 0x07) == 0))
+			l += snprint(p+l, 2*READSTR-l, "\n       ");
+		l += snprint(p+l, 2*READSTR-l, " %4.4uX", ctlr->eeprom[i]);
+	}
+	l += snprint(p+l, 2*READSTR-l, "\n");
+
+	if(ctlr->mii != nil && ctlr->mii->curphy != nil){
+		l += snprint(p+l, 2*READSTR, "phy:   ");
+		for(i = 0; i < NMiiPhyr; i++){
+			if(i && ((i & 0x07) == 0))
+				l += snprint(p+l, 2*READSTR-l, "\n       ");
+			r = miimir(ctlr->mii, i);
+			l += snprint(p+l, 2*READSTR-l, " %4.4uX", r);
+		}
+		snprint(p+l, 2*READSTR-l, "\n");
+	}
+	n = readstr(offset, a, n, p);
+	free(p);
+	qunlock(&ctlr->slock);
+
+	return n;
+}
+
+enum {
+	CMrdtr,
+};
+
+static Cmdtab igbectlmsg[] = {
+	CMrdtr,	"rdtr",	2,
+};
+
+static long
+igbectl(Ether* edev, void* buf, long n)
+{
+	int v;
+	char *p;
+	Ctlr *ctlr;
+	Cmdbuf *cb;
+	Cmdtab *ct;
+
+	if((ctlr = edev->ctlr) == nil)
+		error(Enonexist);
+
+	cb = parsecmd(buf, n);
+	if(waserror()){
+		free(cb);
+		nexterror();
+	}
+
+	ct = lookupcmd(cb, igbectlmsg, nelem(igbectlmsg));
+	switch(ct->index){
+	case CMrdtr:
+		v = strtol(cb->f[1], &p, 0);
+		if(v < 0 || p == cb->f[1] || v > 0xFFFF)
+			error(Ebadarg);
+		ctlr->rdtr = v;;
+		csr32w(ctlr, Rdtr, Fpd|v);
+		break;
+	}
+	free(cb);
+	poperror();
+
+	return n;
+}
+
+static void
+igbepromiscuous(void* arg, int on)
+{
+	int rctl;
+	Ctlr *ctlr;
+	Ether *edev;
+
+	edev = arg;
+	ctlr = edev->ctlr;
+
+	rctl = csr32r(ctlr, Rctl);
+	rctl &= ~MoMASK;
+	rctl |= Mo47b36;
+	if(on)
+		rctl |= Upe|Mpe;
+	else
+		rctl &= ~(Upe|Mpe);
+	csr32w(ctlr, Rctl, rctl);
+}
+
+static void
+igbemulticast(void* arg, uchar* addr, int on)
+{
+	int bit, x;
+	Ctlr *ctlr;
+	Ether *edev;
+
+	edev = arg;
+	ctlr = edev->ctlr;
+
+	x = addr[5]>>1;
+	bit = ((addr[5] & 1)<<4)|(addr[4]>>4);
+	if(on)
+		ctlr->mta[x] |= 1<<bit;
+	else
+		ctlr->mta[x] &= ~(1<<bit);
+
+	csr32w(ctlr, Mta+x*4, ctlr->mta[x]);
+}
+
+static Block*
+igberballoc(void)
+{
+	Block *bp;
+
+	ilock(&igberblock);
+	if((bp = igberbpool) != nil){
+		igberbpool = bp->next;
+		bp->next = nil;
+	}
+	iunlock(&igberblock);
+
+	return bp;
+}
+
+static void
+igberbfree(Block* bp)
+{
+	bp->rp = bp->lim - Rbsz;
+	bp->wp = bp->rp;
+
+	ilock(&igberblock);
+	bp->next = igberbpool;
+	igberbpool = bp;
+	iunlock(&igberblock);
+}
+
+static void
+igbeim(Ctlr* ctlr, int im)
+{
+	ilock(&ctlr->imlock);
+	ctlr->im |= im;
+	csr32w(ctlr, Ims, ctlr->im);
+	iunlock(&ctlr->imlock);
+}
+
+static int
+igbelim(void* ctlr)
+{
+	return ((Ctlr*)ctlr)->lim != 0;
+}
+
+static void
+igbelproc(void* arg)
+{
+	Ctlr *ctlr;
+	Ether *edev;
+	MiiPhy *phy;
+	int ctrl, r;
+
+	edev = arg;
+	ctlr = edev->ctlr;
+	for(;;){
+		if(ctlr->mii == nil || ctlr->mii->curphy == nil)
+			continue;
+
+		/*
+		 * To do:
+		 *	logic to manage status change,
+		 *	this is incomplete but should work
+		 *	one time to set up the hardware.
+		 *
+		 *	MiiPhy.speed, etc. should be in Mii.
+		 */
+		if(miistatus(ctlr->mii) < 0)
+			//continue;
+			goto enable;
+
+		phy = ctlr->mii->curphy;
+		ctrl = csr32r(ctlr, Ctrl);
+
+		switch(ctlr->id){
+		case i82543gc:
+		case i82544ei:
+		default:
+			if(!(ctrl & Asde)){
+				ctrl &= ~(SspeedMASK|Ilos|Fd);
+				ctrl |= Frcdplx|Frcspd;
+				if(phy->speed == 1000)
+					ctrl |= Sspeed1000;
+				else if(phy->speed == 100)
+					ctrl |= Sspeed100;
+				if(phy->fd)
+					ctrl |= Fd;
+			}
+			break;
+
+		case i82540em:
+		case i82540eplp:
+		case i82547gi:
+		case i82541gi:
+		case i82541gi2:
+		case i82541pi:
+			break;
+		}
+
+		/*
+		 * Collision Distance.
+		 */
+		r = csr32r(ctlr, Tctl);
+		r &= ~ColdMASK;
+		if(phy->fd)
+			r |= 64<<ColdSHIFT;
+		else
+			r |= 512<<ColdSHIFT;
+		csr32w(ctlr, Tctl, r);
+
+		/*
+		 * Flow control.
+		 */
+		if(phy->rfc)
+			ctrl |= Rfce;
+		if(phy->tfc)
+			ctrl |= Tfce;
+		csr32w(ctlr, Ctrl, ctrl);
+
+enable:
+		ctlr->lim = 0;
+		igbeim(ctlr, Lsc);
+
+		ctlr->lsleep++;
+		sleep(&ctlr->lrendez, igbelim, ctlr);
+	}
+}
+
+static void
+igbetxinit(Ctlr* ctlr)
+{
+	int i, r;
+	Block *bp;
+
+	csr32w(ctlr, Tctl, (0x0F<<CtSHIFT)|Psp|(66<<ColdSHIFT));
+	switch(ctlr->id){
+	default:
+		r = 6;
+		break;
+	case i82543gc:
+	case i82544ei:
+	case i82547ei:
+	case i82540em:
+	case i82540eplp:
+	case i82541gi:
+	case i82541gi2:
+	case i82541pi:
+	case i82545gmc:
+	case i82546gb:
+	case i82546eb:
+	case i82547gi:
+		r = 8;
+		break;
+	}
+	csr32w(ctlr, Tipg, (6<<20)|(8<<10)|r);
+	csr32w(ctlr, Ait, 0);
+	csr32w(ctlr, Txdmac, 0);
+
+	csr32w(ctlr, Tdbal, PCIWADDR(ctlr->tdba));
+	csr32w(ctlr, Tdbah, 0);
+	csr32w(ctlr, Tdlen, ctlr->ntd*sizeof(Td));
+	ctlr->tdh = PREV(0, ctlr->ntd);
+	csr32w(ctlr, Tdh, 0);
+	ctlr->tdt = 0;
+	csr32w(ctlr, Tdt, 0);
+
+	for(i = 0; i < ctlr->ntd; i++){
+		if((bp = ctlr->tb[i]) != nil){
+			ctlr->tb[i] = nil;
+			freeb(bp);
+		}
+		memset(&ctlr->tdba[i], 0, sizeof(Td));
+	}
+	ctlr->tdfree = ctlr->ntd;
+
+	csr32w(ctlr, Tidv, 128);
+	r = (4<<WthreshSHIFT)|(4<<HthreshSHIFT)|(8<<PthreshSHIFT);
+
+	switch(ctlr->id){
+	default:
+		break;
+	case i82540em:
+	case i82540eplp:
+	case i82547gi:
+	case i82545gmc:
+	case i82546gb:
+	case i82546eb:
+	case i82541gi:
+	case i82541gi2:
+	case i82541pi:
+		r = csr32r(ctlr, Txdctl);
+		r &= ~WthreshMASK;
+		r |= Gran|(4<<WthreshSHIFT);
+
+		csr32w(ctlr, Tadv, 64);
+		break;
+	}
+
+	csr32w(ctlr, Txdctl, r);
+
+	r = csr32r(ctlr, Tctl);
+	r |= Ten;
+	csr32w(ctlr, Tctl, r);
+}
+
+static void
+igbetransmit(Ether* edev)
+{
+	Td *td;
+	Block *bp;
+	Ctlr *ctlr;
+	int tdh, tdt;
+
+	ctlr = edev->ctlr;
+
+	ilock(&ctlr->tlock);
+
+	/*
+	 * Free any completed packets
+	 */
+	tdh = ctlr->tdh;
+	while(NEXT(tdh, ctlr->ntd) != csr32r(ctlr, Tdh)){
+		if((bp = ctlr->tb[tdh]) != nil){
+			ctlr->tb[tdh] = nil;
+			freeb(bp);
+		}
+		memset(&ctlr->tdba[tdh], 0, sizeof(Td));
+		tdh = NEXT(tdh, ctlr->ntd);
+	}
+	ctlr->tdh = tdh;
+
+	/*
+	 * Try to fill the ring back up.
+	 */
+	tdt = ctlr->tdt;
+	while(NEXT(tdt, ctlr->ntd) != tdh){
+		if((bp = qget(edev->oq)) == nil)
+			break;
+		td = &ctlr->tdba[tdt];
+		td->addr[0] = PCIWADDR(bp->rp);
+		td->control = ((BLEN(bp) & LenMASK)<<LenSHIFT);
+		td->control |= Dext|Ifcs|Teop|DtypeDD;
+		ctlr->tb[tdt] = bp;
+		tdt = NEXT(tdt, ctlr->ntd);
+		if(NEXT(tdt, ctlr->ntd) == tdh){
+			td->control |= Rs;
+			ctlr->txdw++;
+			ctlr->tdt = tdt;
+			csr32w(ctlr, Tdt, tdt);
+			igbeim(ctlr, Txdw);
+			break;
+		}
+		ctlr->tdt = tdt;
+		csr32w(ctlr, Tdt, tdt);
+	}
+
+	iunlock(&ctlr->tlock);
+}
+
+static void
+igbereplenish(Ctlr* ctlr)
+{
+	Rd *rd;
+	int rdt;
+	Block *bp;
+
+	rdt = ctlr->rdt;
+	while(NEXT(rdt, ctlr->nrd) != ctlr->rdh){
+		rd = &ctlr->rdba[rdt];
+		if(ctlr->rb[rdt] == nil){
+			bp = igberballoc();
+			if(bp == nil){
+				iprint("#l%d: igbereplenish: no available buffers\n",
+					ctlr->edev->ctlrno);
+				break;
+			}
+			ctlr->rb[rdt] = bp;
+			rd->addr[0] = PCIWADDR(bp->rp);
+			rd->addr[1] = 0;
+		}
+		coherence();
+		rd->status = 0;
+		rdt = NEXT(rdt, ctlr->nrd);
+		ctlr->rdfree++;
+	}
+	ctlr->rdt = rdt;
+	csr32w(ctlr, Rdt, rdt);
+}
+
+static void
+igberxinit(Ctlr* ctlr)
+{
+	int i;
+	Block *bp;
+
+	csr32w(ctlr, Rctl, Dpf|Bsize2048|Bam|RdtmsHALF);
+
+	csr32w(ctlr, Rdbal, PCIWADDR(ctlr->rdba));
+	csr32w(ctlr, Rdbah, 0);
+	csr32w(ctlr, Rdlen, ctlr->nrd*sizeof(Rd));
+	ctlr->rdh = 0;
+	csr32w(ctlr, Rdh, 0);
+	ctlr->rdt = 0;
+	csr32w(ctlr, Rdt, 0);
+	ctlr->rdtr = 0;
+	csr32w(ctlr, Rdtr, Fpd|0);
+
+	for(i = 0; i < ctlr->nrd; i++){
+		if((bp = ctlr->rb[i]) != nil){
+			ctlr->rb[i] = nil;
+			freeb(bp);
+		}
+	}
+	igbereplenish(ctlr);
+
+	switch(ctlr->id){
+	case i82540em:
+	case i82540eplp:
+	case i82541gi:
+	case i82541gi2:
+	case i82541pi:
+	case i82545gmc:
+	case i82546gb:
+	case i82546eb:
+	case i82547gi:
+		csr32w(ctlr, Radv, 64);
+		break;
+	}
+	csr32w(ctlr, Rxdctl, (8<<WthreshSHIFT)|(8<<HthreshSHIFT)|4);
+
+	/*
+	 * Enable checksum offload.
+	 */
+	csr32w(ctlr, Rxcsum, Tuofl|Ipofl|(ETHERHDRSIZE<<PcssSHIFT));
+}
+
+static int
+igberim(void* ctlr)
+{
+	return ((Ctlr*)ctlr)->rim != 0;
+}
+
+static void
+igberproc(void* arg)
+{
+	Rd *rd;
+	Block *bp;
+	Ctlr *ctlr;
+	int r, rdh;
+	Ether *edev;
+
+	edev = arg;
+	ctlr = edev->ctlr;
+
+	igberxinit(ctlr);
+	r = csr32r(ctlr, Rctl);
+	r |= Ren;
+	csr32w(ctlr, Rctl, r);
+
+	for(;;){
+		ctlr->rim = 0;
+		igbeim(ctlr, Rxt0|Rxo|Rxdmt0|Rxseq);
+		ctlr->rsleep++;
+		sleep(&ctlr->rrendez, igberim, ctlr);
+
+		rdh = ctlr->rdh;
+		for(;;){
+			rd = &ctlr->rdba[rdh];
+
+			if(!(rd->status & Rdd))
+				break;
+
+			/*
+			 * Accept eop packets with no errors.
+			 * With no errors and the Ixsm bit set,
+			 * the descriptor status Tpcs and Ipcs bits give
+			 * an indication of whether the checksums were
+			 * calculated and valid.
+			 */
+			if((rd->status & Reop) && rd->errors == 0){
+				bp = ctlr->rb[rdh];
+				ctlr->rb[rdh] = nil;
+				bp->wp += rd->length;
+				bp->next = nil;
+				if(!(rd->status & Ixsm)){
+					ctlr->ixsm++;
+					if(rd->status & Ipcs){
+						/*
+						 * IP checksum calculated
+						 * (and valid as errors == 0).
+						 */
+						ctlr->ipcs++;
+						bp->flag |= Bipck;
+					}
+					if(rd->status & Tcpcs){
+						/*
+						 * TCP/UDP checksum calculated
+						 * (and valid as errors == 0).
+						 */
+						ctlr->tcpcs++;
+						bp->flag |= Btcpck|Budpck;
+					}
+					bp->checksum = rd->checksum;
+					bp->flag |= Bpktck;
+				}
+				etheriq(edev, bp, 1);
+			}
+			else if(ctlr->rb[rdh] != nil){
+				freeb(ctlr->rb[rdh]);
+				ctlr->rb[rdh] = nil;
+			}
+
+			memset(rd, 0, sizeof(Rd));
+			coherence();
+			ctlr->rdfree--;
+			rdh = NEXT(rdh, ctlr->nrd);
+		}
+		ctlr->rdh = rdh;
+
+		if(ctlr->rdfree < ctlr->nrd/2 || (ctlr->rim & Rxdmt0))
+			igbereplenish(ctlr);
+	}
+}
+
+static void
+igbeattach(Ether* edev)
+{
+	Block *bp;
+	Ctlr *ctlr;
+	char name[KNAMELEN];
+
+	ctlr = edev->ctlr;
+	ctlr->edev = edev;			/* point back to Ether* */
+	qlock(&ctlr->alock);
+	if(ctlr->alloc != nil){
+		qunlock(&ctlr->alock);
+		return;
+	}
+
+	ctlr->nrd = ROUND(Nrd, 8);
+	ctlr->ntd = ROUND(Ntd, 8);
+	ctlr->alloc = malloc(ctlr->nrd*sizeof(Rd)+ctlr->ntd*sizeof(Td) + 127);
+	if(ctlr->alloc == nil){
+		qunlock(&ctlr->alock);
+		return;
+	}
+	ctlr->rdba = (Rd*)ROUNDUP((uintptr)ctlr->alloc, 128);
+	ctlr->tdba = (Td*)(ctlr->rdba+ctlr->nrd);
+
+	ctlr->rb = malloc(ctlr->nrd*sizeof(Block*));
+	ctlr->tb = malloc(ctlr->ntd*sizeof(Block*));
+
+	if(waserror()){
+		while(ctlr->nrb > 0){
+			bp = igberballoc();
+			bp->free = nil;
+			freeb(bp);
+			ctlr->nrb--;
+		}
+		free(ctlr->tb);
+		ctlr->tb = nil;
+		free(ctlr->rb);
+		ctlr->rb = nil;
+		free(ctlr->alloc);
+		ctlr->alloc = nil;
+		qunlock(&ctlr->alock);
+		nexterror();
+	}
+
+	for(ctlr->nrb = 0; ctlr->nrb < Nrb; ctlr->nrb++){
+		if((bp = allocb(Rbsz)) == nil)
+			break;
+		bp->free = igberbfree;
+		freeb(bp);
+	}
+
+	snprint(name, KNAMELEN, "#l%dlproc", edev->ctlrno);
+	kproc(name, igbelproc, edev, 0);
+
+	snprint(name, KNAMELEN, "#l%drproc", edev->ctlrno);
+	kproc(name, igberproc, edev, 0);
+
+	igbetxinit(ctlr);
+
+	qunlock(&ctlr->alock);
+	poperror();
+}
+
+static void
+igbeinterrupt(Ureg*, void* arg)
+{
+	Ctlr *ctlr;
+	Ether *edev;
+	int icr, im, txdw;
+
+	edev = arg;
+	ctlr = edev->ctlr;
+
+	ilock(&ctlr->imlock);
+	csr32w(ctlr, Imc, ~0);
+	im = ctlr->im;
+	txdw = 0;
+
+	while((icr = csr32r(ctlr, Icr) & ctlr->im) != 0){
+		if(icr & Lsc){
+			im &= ~Lsc;
+			ctlr->lim = icr & Lsc;
+			wakeup(&ctlr->lrendez);
+			ctlr->lintr++;
+		}
+		if(icr & (Rxt0|Rxo|Rxdmt0|Rxseq)){
+			im &= ~(Rxt0|Rxo|Rxdmt0|Rxseq);
+			ctlr->rim = icr & (Rxt0|Rxo|Rxdmt0|Rxseq);
+			wakeup(&ctlr->rrendez);
+			ctlr->rintr++;
+		}
+		if(icr & Txdw){
+			im &= ~Txdw;
+			txdw++;
+			ctlr->tintr++;
+		}
+	}
+
+	ctlr->im = im;
+	csr32w(ctlr, Ims, im);
+	iunlock(&ctlr->imlock);
+
+	if(txdw)
+		igbetransmit(edev);
+}
+
+static int
+i82543mdior(Ctlr* ctlr, int n)
+{
+	int ctrl, data, i, r;
+
+	/*
+	 * Read n bits from the Management Data I/O Interface.
+	 */
+	ctrl = csr32r(ctlr, Ctrl);
+	r = (ctrl & ~Mddo)|Mdco;
+	data = 0;
+	for(i = n-1; i >= 0; i--){
+		if(csr32r(ctlr, Ctrl) & Mdd)
+			data |= (1<<i);
+		csr32w(ctlr, Ctrl, Mdc|r);
+		csr32w(ctlr, Ctrl, r);
+	}
+	csr32w(ctlr, Ctrl, ctrl);
+
+	return data;
+}
+
+static int
+i82543mdiow(Ctlr* ctlr, int bits, int n)
+{
+	int ctrl, i, r;
+
+	/*
+	 * Write n bits to the Management Data I/O Interface.
+	 */
+	ctrl = csr32r(ctlr, Ctrl);
+	r = Mdco|Mddo|ctrl;
+	for(i = n-1; i >= 0; i--){
+		if(bits & (1<<i))
+			r |= Mdd;
+		else
+			r &= ~Mdd;
+		csr32w(ctlr, Ctrl, Mdc|r);
+		csr32w(ctlr, Ctrl, r);
+	}
+	csr32w(ctlr, Ctrl, ctrl);
+
+	return 0;
+}
+
+static int
+i82543miimir(Mii* mii, int pa, int ra)
+{
+	int data;
+	Ctlr *ctlr;
+
+	ctlr = mii->ctlr;
+
+	/*
+	 * MII Management Interface Read.
+	 *
+	 * Preamble;
+	 * ST+OP+PHYAD+REGAD;
+	 * TA + 16 data bits.
+	 */
+	i82543mdiow(ctlr, 0xFFFFFFFF, 32);
+	i82543mdiow(ctlr, 0x1800|(pa<<5)|ra, 14);
+	data = i82543mdior(ctlr, 18);
+
+	if(data & 0x10000)
+		return -1;
+
+	return data & 0xFFFF;
+}
+
+static int
+i82543miimiw(Mii* mii, int pa, int ra, int data)
+{
+	Ctlr *ctlr;
+
+	ctlr = mii->ctlr;
+
+	/*
+	 * MII Management Interface Write.
+	 *
+	 * Preamble;
+	 * ST+OP+PHYAD+REGAD+TA + 16 data bits;
+	 * Z.
+	 */
+	i82543mdiow(ctlr, 0xFFFFFFFF, 32);
+	data &= 0xFFFF;
+	data |= (0x05<<(5+5+2+16))|(pa<<(5+2+16))|(ra<<(2+16))|(0x02<<16);
+	i82543mdiow(ctlr, data, 32);
+
+	return 0;
+}
+
+static int
+igbemiimir(Mii* mii, int pa, int ra)
+{
+	Ctlr *ctlr;
+	int mdic, timo;
+
+	ctlr = mii->ctlr;
+
+	csr32w(ctlr, Mdic, MDIrop|(pa<<MDIpSHIFT)|(ra<<MDIrSHIFT));
+	mdic = 0;
+	for(timo = 64; timo; timo--){
+		mdic = csr32r(ctlr, Mdic);
+		if(mdic & (MDIe|MDIready))
+			break;
+		microdelay(1);
+	}
+
+	if((mdic & (MDIe|MDIready)) == MDIready)
+		return mdic & 0xFFFF;
+	return -1;
+}
+
+static int
+igbemiimiw(Mii* mii, int pa, int ra, int data)
+{
+	Ctlr *ctlr;
+	int mdic, timo;
+
+	ctlr = mii->ctlr;
+
+	data &= MDIdMASK;
+	csr32w(ctlr, Mdic, MDIwop|(pa<<MDIpSHIFT)|(ra<<MDIrSHIFT)|data);
+	mdic = 0;
+	for(timo = 64; timo; timo--){
+		mdic = csr32r(ctlr, Mdic);
+		if(mdic & (MDIe|MDIready))
+			break;
+		microdelay(1);
+	}
+	if((mdic & (MDIe|MDIready)) == MDIready)
+		return 0;
+	return -1;
+}
+
+static int
+igbemii(Ctlr* ctlr)
+{
+	MiiPhy *phy;
+	int ctrl, p, r;
+
+	r = csr32r(ctlr, Status);
+	if(r & Tbimode)
+		return -1;
+	if((ctlr->mii = malloc(sizeof(Mii))) == nil)
+		return -1;
+	ctlr->mii->ctlr = ctlr;
+
+	ctrl = csr32r(ctlr, Ctrl);
+	ctrl |= Slu;
+
+	switch(ctlr->id){
+	case i82543gc:
+		ctrl |= Frcdplx|Frcspd;
+		csr32w(ctlr, Ctrl, ctrl);
+
+		/*
+		 * The reset pin direction (Mdro) should already
+		 * be set from the EEPROM load.
+		 * If it's not set this configuration is unexpected
+		 * so bail.
+		 */
+		r = csr32r(ctlr, Ctrlext);
+		if(!(r & Mdro))
+			return -1;
+		csr32w(ctlr, Ctrlext, r);
+		delay(20);
+		r = csr32r(ctlr, Ctrlext);
+		r &= ~Mdr;
+		csr32w(ctlr, Ctrlext, r);
+		delay(20);
+		r = csr32r(ctlr, Ctrlext);
+		r |= Mdr;
+		csr32w(ctlr, Ctrlext, r);
+		delay(20);
+
+		ctlr->mii->mir = i82543miimir;
+		ctlr->mii->miw = i82543miimiw;
+		break;
+	case i82544ei:
+	case i82547ei:
+	case i82540em:
+	case i82540eplp:
+	case i82547gi:
+	case i82541gi:
+	case i82541gi2:
+	case i82541pi:
+	case i82545gmc:
+	case i82546gb:
+	case i82546eb:
+		ctrl &= ~(Frcdplx|Frcspd);
+		csr32w(ctlr, Ctrl, ctrl);
+		ctlr->mii->mir = igbemiimir;
+		ctlr->mii->miw = igbemiimiw;
+		break;
+	default:
+		free(ctlr->mii);
+		ctlr->mii = nil;
+		return -1;
+	}
+
+	if(mii(ctlr->mii, ~0) == 0 || (phy = ctlr->mii->curphy) == nil){
+		free(ctlr->mii);
+		ctlr->mii = nil;
+		return -1;
+	}
+	USED(phy);
+	// print("oui %X phyno %d\n", phy->oui, phy->phyno);
+
+	/*
+	 * 8254X-specific PHY registers not in 802.3:
+	 *	0x10	PHY specific control
+	 *	0x14	extended PHY specific control
+	 * Set appropriate values then reset the PHY to have
+	 * changes noted.
+	 */
+	switch(ctlr->id){
+	case i82547gi:
+	case i82541gi:
+	case i82541gi2:
+	case i82541pi:
+	case i82545gmc:
+	case i82546gb:
+	case i82546eb:
+		break;
+	default:
+		r = miimir(ctlr->mii, 16);
+		r |= 0x0800;			/* assert CRS on Tx */
+		r |= 0x0060;			/* auto-crossover all speeds */
+		r |= 0x0002;			/* polarity reversal enabled */
+		miimiw(ctlr->mii, 16, r);
+
+		r = miimir(ctlr->mii, 20);
+		r |= 0x0070;			/* +25MHz clock */
+		r &= ~0x0F00;
+		r |= 0x0100;			/* 1x downshift */
+		miimiw(ctlr->mii, 20, r);
+
+		miireset(ctlr->mii);
+		p = 0;
+		if(ctlr->txcw & TxcwPs)
+			p |= AnaP;
+		if(ctlr->txcw & TxcwAs)
+			p |= AnaAP;
+		miiane(ctlr->mii, ~0, p, ~0);
+		break;
+	}
+	return 0;
+}
+
+static int
+at93c46io(Ctlr* ctlr, char* op, int data)
+{
+	char *lp, *p;
+	int i, loop, eecd, r;
+
+	eecd = csr32r(ctlr, Eecd);
+
+	r = 0;
+	loop = -1;
+	lp = nil;
+	for(p = op; *p != '\0'; p++){
+		switch(*p){
+		default:
+			return -1;
+		case ' ':
+			continue;
+		case ':':			/* start of loop */
+			loop = strtol(p+1, &lp, 0)-1;
+			lp--;
+			if(p == lp)
+				loop = 7;
+			p = lp;
+			continue;
+		case ';':			/* end of loop */
+			if(lp == nil)
+				return -1;
+			loop--;
+			if(loop >= 0)
+				p = lp;
+			else
+				lp = nil;
+			continue;
+		case 'C':			/* assert clock */
+			eecd |= Sk;
+			break;
+		case 'c':			/* deassert clock */
+			eecd &= ~Sk;
+			break;
+		case 'D':			/* next bit in 'data' byte */
+			if(loop < 0)
+				return -1;
+			if(data & (1<<loop))
+				eecd |= Di;
+			else
+				eecd &= ~Di;
+			break;
+		case 'O':			/* collect data output */
+			i = (csr32r(ctlr, Eecd) & Do) != 0;
+			if(loop >= 0)
+				r |= (i<<loop);
+			else
+				r = i;
+			continue;
+		case 'I':			/* assert data input */
+			eecd |= Di;
+			break;
+		case 'i':			/* deassert data input */
+			eecd &= ~Di;
+			break;
+		case 'S':			/* enable chip select */
+			eecd |= Cs;
+			break;
+		case 's':			/* disable chip select */
+			eecd &= ~Cs;
+			break;
+		}
+		csr32w(ctlr, Eecd, eecd);
+		microdelay(50);
+	}
+	if(loop >= 0)
+		return -1;
+	return r;
+}
+
+static int
+at93c46r(Ctlr* ctlr)
+{
+	ushort sum;
+	char rop[20];
+	int addr, areq, bits, data, eecd, i;
+
+	eecd = csr32r(ctlr, Eecd);
+	if(eecd & Spi){
+		print("igbe: SPI EEPROM access not implemented\n");
+		return 0;
+	}
+	if(eecd & (Eeszaddr|Eesz256))
+		bits = 8;
+	else
+		bits = 6;
+
+	sum = 0;
+
+	switch(ctlr->id){
+	default:
+		areq = 0;
+		break;
+	case i82541gi:
+	case i82547gi:
+	case i82540em:
+	case i82540eplp:
+	case i82541pi:
+	case i82541gi2:
+	case i82545gmc:
+	case i82546gb:
+	case i82546eb:
+		areq = 1;
+		csr32w(ctlr, Eecd, eecd|Areq);
+		for(i = 0; i < 1000; i++){
+			if((eecd = csr32r(ctlr, Eecd)) & Agnt)
+				break;
+			microdelay(5);
+		}
+		if(!(eecd & Agnt)){
+			print("igbe: not granted EEPROM access\n");
+			goto release;
+		}
+		break;
+	}
+	snprint(rop, sizeof(rop), "S :%dDCc;", bits+3);
+
+	for(addr = 0; addr < 0x40; addr++){
+		/*
+		 * Read a word at address 'addr' from the Atmel AT93C46
+		 * 3-Wire Serial EEPROM or compatible. The EEPROM access is
+		 * controlled by 4 bits in Eecd. See the AT93C46 datasheet
+		 * for protocol details.
+		 */
+		if(at93c46io(ctlr, rop, (0x06<<bits)|addr) != 0){
+			print("igbe: can't set EEPROM address 0x%2.2X\n", addr);
+			goto release;
+		}
+		data = at93c46io(ctlr, ":16COc;", 0);
+		at93c46io(ctlr, "sic", 0);
+		ctlr->eeprom[addr] = data;
+		sum += data;
+	}
+
+release:
+	if(areq)
+		csr32w(ctlr, Eecd, eecd & ~Areq);
+	return sum;
+}
+
+static int
+igbedetach(Ctlr* ctlr)
+{
+	int r, timeo;
+
+	/*
+	 * Perform a device reset to get the chip back to the
+	 * power-on state, followed by an EEPROM reset to read
+	 * the defaults for some internal registers.
+	 */
+	csr32w(ctlr, Imc, ~0);
+	csr32w(ctlr, Rctl, 0);
+	csr32w(ctlr, Tctl, 0);
+
+	delay(10);
+
+	csr32w(ctlr, Ctrl, Devrst);
+	delay(1);
+	for(timeo = 0; timeo < 1000; timeo++){
+		if(!(csr32r(ctlr, Ctrl) & Devrst))
+			break;
+		delay(1);
+	}
+	if(csr32r(ctlr, Ctrl) & Devrst)
+		return -1;
+	r = csr32r(ctlr, Ctrlext);
+	csr32w(ctlr, Ctrlext, r|Eerst);
+	delay(1);
+	for(timeo = 0; timeo < 1000; timeo++){
+		if(!(csr32r(ctlr, Ctrlext) & Eerst))
+			break;
+		delay(1);
+	}
+	if(csr32r(ctlr, Ctrlext) & Eerst)
+		return -1;
+
+	switch(ctlr->id){
+	default:
+		break;
+	case i82540em:
+	case i82540eplp:
+	case i82541gi:
+	case i82541pi:
+	case i82547gi:
+	case i82541gi2:
+	case i82545gmc:
+	case i82546gb:
+	case i82546eb:
+		r = csr32r(ctlr, Manc);
+		r &= ~Arpen;
+		csr32w(ctlr, Manc, r);
+		break;
+	}
+
+	csr32w(ctlr, Imc, ~0);
+	delay(1);
+	for(timeo = 0; timeo < 1000; timeo++){
+		if(!csr32r(ctlr, Icr))
+			break;
+		delay(1);
+	}
+	if(csr32r(ctlr, Icr))
+		return -1;
+
+	return 0;
+}
+
+static void
+igbeshutdown(Ether* ether)
+{
+	igbedetach(ether->ctlr);
+}
+
+static int
+igbereset(Ctlr* ctlr)
+{
+	int ctrl, i, pause, r, swdpio, txcw;
+
+	if(igbedetach(ctlr))
+		return -1;
+
+	/*
+	 * Read the EEPROM, validate the checksum
+	 * then get the device back to a power-on state.
+	 */
+	if((r = at93c46r(ctlr)) != 0xBABA){
+		print("igbe: bad EEPROM checksum - 0x%4.4uX\n", r);
+		return -1;
+	}
+
+	/*
+	 * Snarf and set up the receive addresses.
+	 * There are 16 addresses. The first should be the MAC address.
+	 * The others are cleared and not marked valid (MS bit of Rah).
+	 */
+	if ((ctlr->id == i82546gb || ctlr->id == i82546eb) && BUSFNO(ctlr->pcidev->tbdf) == 1)
+		ctlr->eeprom[Ea+2] += 0x100;	// second interface
+	for(i = Ea; i < Eaddrlen/2; i++){
+if(i == Ea && ctlr->id == i82541gi && ctlr->eeprom[i] == 0xFFFF)
+    ctlr->eeprom[i] = 0xD000;
+		ctlr->ra[2*i] = ctlr->eeprom[i];
+		ctlr->ra[2*i+1] = ctlr->eeprom[i]>>8;
+	}
+	r = (ctlr->ra[3]<<24)|(ctlr->ra[2]<<16)|(ctlr->ra[1]<<8)|ctlr->ra[0];
+	csr32w(ctlr, Ral, r);
+	r = 0x80000000|(ctlr->ra[5]<<8)|ctlr->ra[4];
+	csr32w(ctlr, Rah, r);
+	for(i = 1; i < 16; i++){
+		csr32w(ctlr, Ral+i*8, 0);
+		csr32w(ctlr, Rah+i*8, 0);
+	}
+
+	/*
+	 * Clear the Multicast Table Array.
+	 * It's a 4096 bit vector accessed as 128 32-bit registers.
+	 */
+	memset(ctlr->mta, 0, sizeof(ctlr->mta));
+	for(i = 0; i < 128; i++)
+		csr32w(ctlr, Mta+i*4, 0);
+
+	/*
+	 * Just in case the Eerst didn't load the defaults
+	 * (doesn't appear to fully on the 82543GC), do it manually.
+	 */
+	if (ctlr->id == i82543gc) {
+		txcw = csr32r(ctlr, Txcw);
+		txcw &= ~(TxcwAne|TxcwPauseMASK|TxcwFd);
+		ctrl = csr32r(ctlr, Ctrl);
+		ctrl &= ~(SwdpioloMASK|Frcspd|Ilos|Lrst|Fd);
+
+		if(ctlr->eeprom[Icw1] & 0x0400){
+			ctrl |= Fd;
+			txcw |= TxcwFd;
+		}
+		if(ctlr->eeprom[Icw1] & 0x0200)
+			ctrl |= Lrst;
+		if(ctlr->eeprom[Icw1] & 0x0010)
+			ctrl |= Ilos;
+		if(ctlr->eeprom[Icw1] & 0x0800)
+			ctrl |= Frcspd;
+		swdpio = (ctlr->eeprom[Icw1] & 0x01E0)>>5;
+		ctrl |= swdpio<<SwdpioloSHIFT;
+		csr32w(ctlr, Ctrl, ctrl);
+
+		ctrl = csr32r(ctlr, Ctrlext);
+		ctrl &= ~(Ips|SwdpiohiMASK);
+		swdpio = (ctlr->eeprom[Icw2] & 0x00F0)>>4;
+		if(ctlr->eeprom[Icw1] & 0x1000)
+			ctrl |= Ips;
+		ctrl |= swdpio<<SwdpiohiSHIFT;
+		csr32w(ctlr, Ctrlext, ctrl);
+
+		if(ctlr->eeprom[Icw2] & 0x0800)
+			txcw |= TxcwAne;
+		pause = (ctlr->eeprom[Icw2] & 0x3000)>>12;
+		txcw |= pause<<TxcwPauseSHIFT;
+		switch(pause){
+		default:
+			ctlr->fcrtl = 0x00002000;
+			ctlr->fcrth = 0x00004000;
+			txcw |= TxcwAs|TxcwPs;
+			break;
+		case 0:
+			ctlr->fcrtl = 0x00002000;
+			ctlr->fcrth = 0x00004000;
+			break;
+		case 2:
+			ctlr->fcrtl = 0;
+			ctlr->fcrth = 0;
+			txcw |= TxcwAs;
+			break;
+		}
+		ctlr->txcw = txcw;
+		csr32w(ctlr, Txcw, txcw);
+	}
+
+
+	/*
+	 * Flow control - values from the datasheet.
+	 */
+	csr32w(ctlr, Fcal, 0x00C28001);
+	csr32w(ctlr, Fcah, 0x00000100);
+	csr32w(ctlr, Fct, 0x00008808);
+	csr32w(ctlr, Fcttv, 0x00000100);
+
+	csr32w(ctlr, Fcrtl, ctlr->fcrtl);
+	csr32w(ctlr, Fcrth, ctlr->fcrth);
+
+	if(!(csr32r(ctlr, Status) & Tbimode) && igbemii(ctlr) < 0)
+		return -1;
+
+	return 0;
+}
+
+static void
+igbepci(void)
+{
+	int cls;
+	Pcidev *p;
+	Ctlr *ctlr;
+	void *mem;
+
+	p = nil;
+	while(p = pcimatch(p, 0, 0)){
+		if(p->ccrb != 0x02 || p->ccru != 0)
+			continue;
+
+		switch((p->did<<16)|p->vid){
+		default:
+			continue;
+		case i82543gc:
+		case i82544ei:
+		case i82547ei:
+		case i82540em:
+		case i82540eplp:
+		case i82541gi:
+		case i82547gi:
+		case i82541gi2:
+		case i82541pi:
+		case i82545gmc:
+		case i82546gb:
+		case i82546eb:
+			break;
+		}
+
+		mem = vmap(p->mem[0].bar & ~0x0F, p->mem[0].size);
+		if(mem == nil){
+			print("igbe: can't map %8.8luX\n", p->mem[0].bar);
+			continue;
+		}
+		cls = pcicfgr8(p, PciCLS);
+		switch(cls){
+			default:
+				print("igbe: unexpected CLS - %d\n", cls*4);
+				break;
+			case 0x00:
+			case 0xFF:
+				print("igbe: unusable CLS\n");
+				continue;
+			case 0x08:
+			case 0x10:
+				break;
+		}
+		ctlr = malloc(sizeof(Ctlr));
+		ctlr->port = p->mem[0].bar & ~0x0F;
+		ctlr->pcidev = p;
+		ctlr->id = (p->did<<16)|p->vid;
+		ctlr->cls = cls*4;
+		ctlr->nic = mem;
+
+		if(igbereset(ctlr)){
+			free(ctlr);
+			vunmap(mem, p->mem[0].size);
+			continue;
+		}
+		pcisetbme(p);
+
+		if(igbectlrhead != nil)
+			igbectlrtail->next = ctlr;
+		else
+			igbectlrhead = ctlr;
+		igbectlrtail = ctlr;
+	}
+}
+
+static int
+igbepnp(Ether* edev)
+{
+	Ctlr *ctlr;
+
+	if(igbectlrhead == nil)
+		igbepci();
+
+	/*
+	 * Any adapter matches if no edev->port is supplied,
+	 * otherwise the ports must match.
+	 */
+	for(ctlr = igbectlrhead; ctlr != nil; ctlr = ctlr->next){
+		if(ctlr->active)
+			continue;
+		if(edev->port == 0 || edev->port == ctlr->port){
+			ctlr->active = 1;
+			break;
+		}
+	}
+	if(ctlr == nil)
+		return -1;
+
+	edev->ctlr = ctlr;
+	edev->port = ctlr->port;
+	edev->irq = ctlr->pcidev->intl;
+	edev->tbdf = ctlr->pcidev->tbdf;
+	edev->mbps = 1000;
+	memmove(edev->ea, ctlr->ra, Eaddrlen);
+
+	/*
+	 * Linkage to the generic ethernet driver.
+	 */
+	edev->attach = igbeattach;
+	edev->transmit = igbetransmit;
+	edev->interrupt = igbeinterrupt;
+	edev->ifstat = igbeifstat;
+	edev->ctl = igbectl;
+
+	edev->arg = edev;
+	edev->promiscuous = igbepromiscuous;
+	edev->shutdown = igbeshutdown;
+	edev->multicast = igbemulticast;
+
+	return 0;
+}
+
+void
+etherigbelink(void)
+{
+	addethercard("i82543", igbepnp);
+	addethercard("igbe", igbepnp);
+}
+
--- /dev/null
+++ b/os/pc/etherrhine.c
@@ -1,0 +1,734 @@
+ /*
+	Via Rhine driver, written for VT6102.
+	Uses the ethermii to control PHY.
+
+	Currently always copies on both, tx and rx.
+	rx side could be copy-free, and tx-side might be made
+	(almost) copy-free by using (possibly) two descriptors (if it allows
+	arbitrary tx lengths, which it should..): first for alignment and
+	second for rest of the frame. Rx-part should be worth doing.
+*/
+
+#include "u.h"
+#include "../port/lib.h"
+#include "mem.h"
+#include "dat.h"
+#include "fns.h"
+#include "io.h"
+#include "../port/error.h"
+#include "../port/netif.h"
+#include "etherif.h"
+
+#include "ethermii.h"
+
+typedef struct Desc Desc;
+typedef struct Ctlr Ctlr;
+
+enum {
+	Ntxd = 16,
+	Nrxd = 64,
+	Nwait = 50,
+	Ntxstats = 9,
+	Nrxstats = 8,
+	BIGSTR = 8192,
+};
+
+struct Desc {
+	ulong stat;
+	ulong size;
+	ulong addr;
+	ulong next;
+	char *buf;
+	ulong pad[3];
+};
+
+struct Ctlr {
+	Pcidev *pci;
+	int attached;
+	int txused;
+	int txhead;
+	int txtail;
+	int rxtail;
+	ulong port;
+
+	Mii mii;
+
+	ulong txstats[Ntxstats];
+	ulong rxstats[Nrxstats];
+
+	Desc *txd;	/* wants to be aligned on 16-byte boundary */
+	Desc *rxd;
+
+	QLock attachlck;
+	Lock lock;
+};
+
+#define ior8(c, r)	(inb((c)->port+(r)))
+#define ior16(c, r)	(ins((c)->port+(r)))
+#define ior32(c, r)	(inl((c)->port+(r)))
+#define iow8(c, r, b)	(outb((c)->port+(r), (int)(b)))
+#define iow16(c, r, w)	(outs((c)->port+(r), (ushort)(w)))
+#define iow32(c, r, l)	(outl((c)->port+(r), (ulong)(l)))
+
+enum Regs {
+	Eaddr = 0x0,
+	Rcr = 0x6,
+	Tcr = 0x7,
+	Cr = 0x8,
+	Isr = 0xc,
+	Imr = 0xe,
+	McastAddr = 0x10,
+	RxdAddr = 0x18,
+	TxdAddr = 0x1C,
+	Bcr = 0x6e,
+	RhineMiiPhy = 0x6C,
+	RhineMiiSr = 0x6D,
+	RhineMiiCr = 0x70,
+	RhineMiiAddr = 0x71,
+	RhineMiiData = 0x72,
+	Eecsr = 0x74,
+	ConfigB = 0x79,
+	ConfigD = 0x7B,
+	MiscCr = 0x80,
+	HwSticky = 0x83,
+	MiscIsr = 0x84,
+	MiscImr = 0x86,
+	WolCrSet = 0xA0,
+	WolCfgSet = 0xA1,
+	WolCgSet = 0xA3,
+	WolCrClr = 0xA4,
+	PwrCfgClr = 0xA5,
+	WolCgClr = 0xA7,
+};
+
+enum Rcrbits {
+	RxErrX = 1<<0,
+	RxSmall = 1<<1,
+	RxMcast = 1<<2,
+	RxBcast = 1<<3,
+	RxProm = 1<<4,
+	RxFifo64 = 0<<5, RxFifo32 = 1<<5, RxFifo128 = 2<<5, RxFifo256 = 3<<5,
+	RxFifo512 = 4<<5, RxFifo768 = 5<<5, RxFifo1024 = 6<<5,
+	RxFifoStoreForward = 7<<5,
+};
+
+enum Tcrbits {
+	TxLoopback0 = 1<<1,
+	TxLoopback1 = 1<<2,
+	TxBackoff = 1<<3,
+	TxFifo128 = 0<<5, TxFifo256 = 1<<5, TxFifo512 = 2<<5, TxFifo1024 = 3<<5,
+	TxFifoStoreForward = 7<<5,
+};
+
+enum Crbits {
+	Init = 1<<0,
+	Start = 1<<1,
+	Stop = 1<<2,
+	RxOn = 1<<3,
+	TxOn = 1<<4,
+	Tdmd = 1<<5,
+	Rdmd = 1<<6,
+	EarlyRx = 1<<8,
+	Reserved0 = 1<<9,
+	FullDuplex = 1<<10,
+	NoAutoPoll = 1<<11,
+	Reserved1 = 1<<12,
+	Tdmd1 = 1<<13,
+	Rdmd1 = 1<<14,
+	Reset = 1<<15,
+};
+
+enum Isrbits {
+	RxOk = 1<<0,
+	TxOk = 1<<1,
+	RxErr = 1<<2,
+	TxErr = 1<<3,
+	TxBufUdf = 1<<4,
+	RxBufLinkErr = 1<<5,
+	BusErr = 1<<6,
+	CrcOvf = 1<<7,
+	EarlyRxInt = 1<<8,
+	TxFifoUdf = 1<<9,
+	RxFifoOvf = 1<<10,
+	TxPktRace = 1<<11,
+	NoRxbuf = 1<<12,
+	TxCollision = 1<<13,
+	PortCh = 1<<14,
+	GPInt = 1<<15
+};
+
+enum Bcrbits {
+	Dma32 = 0<<0, Dma64 = 1<<0, Dma128 = 2<<0,
+	Dma256 = 3<<0, Dma512 = 4<<0, Dma1024 = 5<<0,
+	DmaStoreForward = 7<<0,
+	DupRxFifo0 = 1<<3, DupRxFifo1 = 1<<4, DupRxFifo2 = 1<<5,
+	ExtraLed = 1<<6,
+	MediumSelect = 1<<7,
+	PollTimer0 = 1<<8, PollTimer1 = 1<<9, PollTimer2 = 1<<10,
+	DupTxFifo0 = 1<<11, DupTxFifo1 = 1<<12, DupTxFifo2 = 1<<13,
+};
+
+enum Eecsrbits {
+	EeAutoLoad = 1<<5,
+};
+
+enum MiscCrbits {
+	Timer0Enable= 1<<0,
+	Timer0Suspend = 1<<1,
+	HalfDuplexFlowControl = 1<<2,
+	FullDuplexFlowControl = 1<<3,
+	Timer1Enable = 1<<8,
+	ForceSoftReset = 1<<14,
+};
+
+enum HwStickybits {
+	StickyDS0 = 1<<0,
+	StickyDS1 = 1<<1,
+	WOLEna = 1<<2,
+	WOLStat = 1<<3,
+};
+
+enum WolCgbits {
+	PmeOvr = 1<<7,
+};
+
+enum Descbits {
+	OwnNic = 1<<31,		/* stat */
+	TxAbort = 1<<8,		/* stat */
+	TxError = 1<<15,		/* stat */
+	RxChainbuf = 1<<10,	/* stat */
+	RxChainStart = 1<<9,	/* stat */
+	RxChainEnd = 1<<8,		/* stat */
+	Chainbuf = 1<<15,		/* size rx & tx*/
+	TxDisableCrc = 1<<16,	/* size */
+	TxChainStart = 1<<21,	/* size */
+	TxChainEnd = 1<<22,	/* size */
+	TxInt = 1<<23,			/* size */
+};
+
+enum ConfigDbits {
+	BackoffOptional = 1<<0,
+	BackoffAMD = 1<<1,
+	BackoffDEC = 1<<2,
+	BackoffRandom = 1<<3,
+	PmccTestMode = 1<<4,
+	PciReadlineCap = 1<<5,
+	DiagMode = 1<<6,
+	MmioEnable = 1<<7,
+};
+
+enum ConfigBbits {
+	LatencyTimer = 1<<0,
+	WriteWaitState = 1<<1,
+	ReadWaitState = 1<<2,
+	RxArbit = 1<<3,
+	TxArbit = 1<<4,
+	NoMemReadline = 1<<5,
+	NoParity = 1<<6,
+	NoTxQueuing = 1<<7,
+};
+
+enum RhineMiiCrbits {
+	Mdc = 1<<0,
+	Mdi = 1<<1,
+	Mdo = 1<<2,
+	Mdout = 1<<3,
+	Mdpm = 1<<4,
+	Wcmd = 1<<5,
+	Rcmd = 1<<6,
+	Mauto = 1<<7,
+};
+
+enum RhineMiiSrbits {
+	Speed10M = 1<<0,
+	LinkFail = 1<<1,
+	PhyError = 1<<3,
+	DefaultPhy = 1<<4,
+	ResetPhy = 1<<7,
+};
+
+enum RhineMiiAddrbits {
+	Mdone = 1<<5,
+	Msrcen = 1<<6,
+	Midle = 1<<7,
+};
+
+static char *
+txstatnames[Ntxstats] = {
+	"aborts (excess collisions)",
+	"out of window collisions",
+	"carrier sense losses",
+	"fifo underflows",
+	"invalid descriptor format or underflows",
+	"system errors",
+	"reserved",
+	"transmit errors",
+	"collisions",
+};
+
+static char *
+rxstatnames[Nrxstats] = {
+	"receiver errors",
+	"crc errors",
+	"frame alignment errors",
+	"fifo overflows",
+	"long packets",
+	"run packets",
+	"system errors",
+	"buffer underflows",
+};
+
+static void
+attach(Ether *edev)
+{
+	Ctlr *ctlr;
+	Desc *txd, *rxd, *td, *rd;
+	Mii *mi;
+	MiiPhy *phy;
+	int i, s;
+	
+	ctlr = edev->ctlr;
+	qlock(&ctlr->attachlck);
+	if (ctlr->attached == 0) {
+		txd = ctlr->txd;
+		rxd = ctlr->rxd;
+		for (i = 0; i < Ntxd; ++i) {
+			td = &txd[i];
+			td->next = PCIWADDR(&txd[(i+1) % Ntxd]);
+			td->buf = xspanalloc(sizeof(Etherpkt)+4, 4, 0);
+			td->addr = PCIWADDR(td->buf);
+			td->size = 0;
+			coherence();
+			td->stat = 0;
+		}
+		for (i = 0; i < Nrxd; ++i) {
+			rd = &rxd[i];
+			rd->next = PCIWADDR(&rxd[(i+1) % Nrxd]);
+			rd->buf = xspanalloc(sizeof(Etherpkt)+4, 4, 0);
+			rd->addr = PCIWADDR(rd->buf);
+			rd->size = sizeof(Etherpkt)+4;
+			coherence();
+			rd->stat = OwnNic;
+		}
+
+		ctlr->txhead = ctlr->txtail = ctlr->rxtail = 0;
+		mi = &ctlr->mii;
+		miistatus(mi);
+		phy = mi->curphy;
+		s = splhi();
+		iow32(ctlr, TxdAddr, PCIWADDR(&txd[0]));
+		iow32(ctlr, RxdAddr, PCIWADDR(&rxd[0]));
+		iow16(ctlr, Cr, (phy->fd ? FullDuplex : 0) | NoAutoPoll | TxOn | RxOn | Start | Rdmd);
+		iow16(ctlr, Isr, 0xFFFF);
+		iow16(ctlr, Imr, 0xFFFF);
+		iow8(ctlr, MiscIsr, 0xFF);
+		iow8(ctlr, MiscImr, ~(3<<5));
+		splx(s);
+	}
+	ctlr->attached++;
+	qunlock(&ctlr->attachlck);
+}
+
+static void
+txstart(Ether *edev)
+{
+	Ctlr *ctlr;
+	Desc *txd, *td;
+	Block *b;
+	int i, txused, n;
+	ulong size;
+
+	ctlr = edev->ctlr;
+
+	txd = ctlr->txd;
+	i = ctlr->txhead;
+	txused = ctlr->txused;
+	n = 0;
+	while (txused < Ntxd) {
+		if ((b = qget(edev->oq)) == nil)
+			break;
+
+		td = &txd[i];
+
+		size = BLEN(b);
+		memmove(td->buf, b->rp, size);
+		freeb(b);
+		td->size = size | TxChainStart | TxChainEnd | TxInt; /* could reduce number of ints here */
+		coherence();
+		td->stat = OwnNic;
+		i = (i + 1) % Ntxd;
+		txused++;
+		n++;
+	}
+	if (n)
+		iow16(ctlr, Cr, ior16(ctlr, Cr) | Tdmd);
+
+	ctlr->txhead = i;
+	ctlr->txused = txused;
+}
+
+static void
+transmit(Ether *edev)
+{
+	Ctlr *ctlr;
+	ctlr = edev->ctlr;
+	ilock(&ctlr->lock);
+	txstart(edev);
+	iunlock(&ctlr->lock);
+}
+
+static void
+txcomplete(Ether *edev)
+{
+	Ctlr *ctlr;
+	Desc *txd, *td;
+	int i, txused, j;
+	ulong stat;
+
+	ctlr = edev->ctlr;
+ 	txd = ctlr->txd;
+	txused = ctlr->txused;
+	i = ctlr->txtail;
+	while (txused > 0) {
+		td = &txd[i];
+		stat = td->stat;
+
+		if (stat & OwnNic)
+			break;
+
+		ctlr->txstats[Ntxstats-1] += stat & 0xF;
+		for (j = 0; j < Ntxstats-1; ++j)
+			if (stat & (1<<(j+8)))
+				ctlr->txstats[j]++;
+
+		i = (i + 1) % Ntxd;
+		txused--;
+	}
+	ctlr->txused = txused;
+	ctlr->txtail = i;
+
+	if (txused <= Ntxd/2)
+		txstart(edev);
+}
+
+static void
+interrupt(Ureg *, void *arg)
+{
+	Ether *edev;
+	Ctlr *ctlr;
+	ushort  isr, misr;
+	ulong stat;
+	Desc *rxd, *rd;
+	int i, n, j;
+
+	edev = (Ether*)arg;
+	ctlr = edev->ctlr;
+	iow16(ctlr, Imr, 0);
+	isr = ior16(ctlr, Isr);
+	iow16(ctlr, Isr, 0xFFFF);
+	misr = ior16(ctlr, MiscIsr) & ~(3<<5); /* don't care about used defined ints */
+
+	if (isr & RxOk) {
+		Block *b;
+		int size;
+		rxd = ctlr->rxd;
+		i = ctlr->rxtail;
+
+		n = 0;
+		while ((rxd[i].stat & OwnNic) == 0) {
+			rd = &rxd[i];
+			stat = rd->stat;
+			for (j = 0; j < Nrxstats; ++j)
+				if (stat & (1<<j))
+					ctlr->rxstats[j]++;
+
+			if (stat & 0xFF)
+				iprint("rx: %lux\n", stat & 0xFF);
+
+			size = ((rd->stat>>16) & 2047) - 4;
+			b = iallocb(sizeof(Etherpkt));
+			memmove(b->wp, rd->buf, size);
+			b->wp += size;
+			etheriq(edev, b, 1);
+			rd->size = sizeof(Etherpkt)+4;
+			coherence();
+			rd->stat = OwnNic;
+			i = (i + 1) % Nrxd;
+			n++;
+		}
+		if (n)
+			iow16(ctlr, Cr, ior16(ctlr, Cr) | Rdmd);
+		ctlr->rxtail = i;
+		isr &= ~RxOk;
+	}
+	if (isr & TxOk) {
+		txcomplete(edev);
+		isr &= ~TxOk;
+	}
+	if (isr | misr)
+		iprint("etherrhine: unhandled irq(s). isr:%x misr:%x\n", isr, misr);
+
+	iow16(ctlr, Imr, 0xFFFF);
+}
+
+static void
+promiscuous(void *arg, int enable)
+{
+	Ether *edev;
+	Ctlr *ctlr;
+
+	edev = arg;
+	ctlr = edev->ctlr;
+	ilock(&ctlr->lock);
+	iow8(ctlr, Rcr, (ior8(ctlr, Rcr) & ~(RxProm|RxBcast)) |
+		(enable ? RxProm : RxBcast));
+	iunlock(&ctlr->lock);
+}
+
+static int
+miiread(Mii *mii, int phy, int reg)
+{
+	Ctlr *ctlr;
+	int n;
+
+	ctlr = mii->ctlr;
+	
+	n = Nwait;
+	while (n-- && ior8(ctlr, RhineMiiCr) & (Rcmd | Wcmd))
+		microdelay(1);
+	if (n == Nwait)
+		iprint("etherrhine: miiread: timeout\n");
+
+	iow8(ctlr, RhineMiiCr, 0);
+	iow8(ctlr, RhineMiiPhy, phy);
+	iow8(ctlr, RhineMiiAddr, reg);
+	iow8(ctlr, RhineMiiCr, Rcmd);
+
+	n = Nwait;
+	while (n-- && ior8(ctlr, RhineMiiCr) & Rcmd)
+		microdelay(1);
+	if (n == Nwait)
+		iprint("etherrhine: miiread: timeout\n");
+
+	n = ior16(ctlr, RhineMiiData);
+
+	return n;
+}
+
+static int
+miiwrite(Mii *mii, int phy, int reg, int data)
+{
+	int n;
+	Ctlr *ctlr;
+
+	ctlr = mii->ctlr;
+
+	n = Nwait;
+	while (n-- && ior8(ctlr, RhineMiiCr) & (Rcmd | Wcmd))
+		microdelay(1);
+	if (n == Nwait)
+		iprint("etherrhine: miiwrite: timeout\n");
+
+	iow8(ctlr, RhineMiiCr, 0);
+	iow8(ctlr, RhineMiiPhy, phy);
+	iow8(ctlr, RhineMiiAddr, reg);
+	iow16(ctlr, RhineMiiData, data);
+	iow8(ctlr, RhineMiiCr, Wcmd);
+
+	n = Nwait;
+	while (n-- && ior8(ctlr, RhineMiiCr) & Wcmd)
+		microdelay(1);
+	if (n == Nwait)
+		iprint("etherrhine: miiwrite: timeout\n");
+
+	return 0;
+}
+
+/* multicast already on, don't need to do anything */
+static void
+multicast(void*, uchar*, int)
+{
+}
+
+static void
+shutdown(Ether *edev)
+{
+	int i;
+	Ctlr *ctlr = edev->ctlr;
+
+	ilock(&ctlr->lock);
+	pcisetbme(ctlr->pci);
+
+	iow16(ctlr, Cr, ior16(ctlr, Cr) | Stop);
+	iow16(ctlr, Cr, ior16(ctlr, Cr) | Reset);
+
+	for (i = 0; i < Nwait; ++i) {
+		if ((ior16(ctlr, Cr) & Reset) == 0)
+			break;
+		delay(5);
+	}
+	if (i == Nwait)
+		iprint("etherrhine: reset timeout\n");
+	iunlock(&ctlr->lock);
+}
+
+static void
+init(Ether *edev)
+{
+	Ctlr *ctlr;
+	MiiPhy *phy;
+	int i;
+
+	shutdown(edev);
+
+	ctlr = edev->ctlr;
+	ilock(&ctlr->lock);
+	iow8(ctlr, Eecsr, ior8(ctlr, Eecsr) | EeAutoLoad);
+	for (i = 0; i < Nwait; ++i) {
+		if ((ior8(ctlr, Eecsr) & EeAutoLoad) == 0)
+			break;
+		delay(5);
+	}
+	if (i == Nwait)
+		iprint("etherrhine: eeprom autoload timeout\n");
+
+	for (i = 0; i < Eaddrlen; ++i)
+		edev->ea[i] = ior8(ctlr, Eaddr + i);
+
+	ctlr->mii.mir = miiread;
+	ctlr->mii.miw = miiwrite;
+	ctlr->mii.ctlr = ctlr;
+
+	if(mii(&ctlr->mii, ~0) == 0 || ctlr->mii.curphy == nil){
+		iprint("etherrhine: init mii failure\n");
+		return;
+	}
+	for (i = 0; i < NMiiPhy; ++i)
+		if (ctlr->mii.phy[i])
+			if (ctlr->mii.phy[i]->oui != 0xFFFFF)
+				ctlr->mii.curphy = ctlr->mii.phy[i];
+
+	miistatus(&ctlr->mii);
+	phy = ctlr->mii.curphy;
+	edev->mbps = phy->speed;
+
+	iow16(ctlr, Imr, 0);
+	iow16(ctlr, Cr, ior16(ctlr, Cr) | Stop);
+	iow8(ctlr, Rcr, ior8(ctlr, Rcr) | RxMcast);
+
+	iunlock(&ctlr->lock);
+}
+
+static Pcidev *
+rhinematch(ulong)
+{
+	static int nrhines = 0;
+	int nfound = 0;
+	Pcidev *p = nil;
+
+	while (p = pcimatch(p, 0x1106, 0))
+		if (p->did == 0x3065)
+			if (++nfound > nrhines) {
+				nrhines++;
+				break;
+			}
+	return p;
+}
+static long
+ifstat(Ether* edev, void* a, long n, ulong offset)
+{
+	int l = 0, i;
+	char *p;
+	Ctlr *ctlr;
+	ctlr = edev->ctlr;
+	p = malloc(BIGSTR);
+
+	for (i = 0; i < Ntxstats; ++i)
+		if (txstatnames[i])
+			l += snprint(p+l, BIGSTR - l, "tx: %s: %lud\n", txstatnames[i], ctlr->txstats[i]);
+
+	for (i = 0; i < Nrxstats; ++i)
+		if (rxstatnames[i])
+			l += snprint(p+l, BIGSTR - l, "rx: %s: %lud\n", rxstatnames[i], ctlr->rxstats[i]);
+
+/*
+	for (i = 0; i < NMiiPhyr; ++i) {
+		if ((i % 8) == 0)
+			l += snprint(p + l, BIGSTR - l, "\nmii 0x%02x:", i);
+		reg=miimir(&ctlr->mii, i);
+		reg=miimir(&ctlr->mii, i);
+		l += snprint(p + l, BIGSTR - l, " %4ux", reg);
+	}
+
+	for (i = 0; i < 0x100; i+=1) {
+		if ((i % 16) == 0)
+			l += snprint(p + l, BIGSTR - l, "\nreg 0x%02x:", i);
+		else if ((i % 2) == 0)
+			l += snprint(p + l, BIGSTR - l, " ");
+		reg=ior8(ctlr, i);
+		l += snprint(p + l, BIGSTR - l, "%02x", reg);
+	}
+	l += snprint(p + l, BIGSTR - l, " \n");
+*/
+
+
+	n = readstr(offset, a, n, p);
+	free(p);
+
+	return n;
+}
+
+static int
+pnp(Ether *edev)
+{
+	Pcidev *p;
+	Ctlr *ctlr;
+	ulong port;
+	ulong size;
+
+	p = rhinematch(edev->port);
+	if (p == nil)
+		return -1;
+
+	port = p->mem[0].bar & ~1;
+	size = p->mem[0].size;
+	if (ioalloc(port, size, 0, "rhine") < 0) {
+		print("etherrhine: couldn't allocate port %lud\n", port);
+		return -1;
+	}
+
+	if ((ctlr = malloc(sizeof(Ctlr))) == nil) {
+		print("etherrhine: couldn't allocate memory for ctlr\n");
+		return -1;
+	}
+	memset(ctlr, 0, sizeof(Ctlr));
+	ctlr->txd = xspanalloc(sizeof(Desc) * Ntxd, 16, 0);
+	ctlr->rxd = xspanalloc(sizeof(Desc) * Nrxd, 16, 0);
+		
+	ctlr->pci = p;
+	ctlr->port = port;
+
+	edev->ctlr = ctlr;
+	edev->port = ctlr->port;
+	edev->irq = p->intl;
+	edev->tbdf = p->tbdf;
+
+	init(edev);
+
+	edev->interrupt = interrupt;
+	edev->arg = edev;
+
+	edev->attach = attach;
+	edev->transmit = transmit;
+	edev->ifstat = ifstat;
+	edev->promiscuous = promiscuous;
+	edev->multicast = multicast;
+	edev->shutdown = shutdown;
+	return 0;
+}
+
+void
+etherrhinelink(void)
+{
+	addethercard("rhine", pnp);
+}
--- /dev/null
+++ b/os/pc/ethersmc.c
@@ -1,0 +1,781 @@
+/*
+ * SMC EtherEZ (SMC91cXX chip) PCMCIA card support.
+ */
+
+#include "u.h"
+#include "../port/lib.h"
+#include "mem.h"
+#include "dat.h"
+#include "fns.h"
+#include "io.h"
+#include "../port/error.h"
+#include "../port/netif.h"
+#include "etherif.h"
+
+enum {
+	IoSize		= 0x10,		/* port pool size */
+	TxTimeout	= 150,
+};
+
+enum {	/* PCMCIA related */
+	TupleFunce	= 0x22,
+	TfNodeId	= 0x04,
+};
+
+enum {	/* bank 0 registers */
+	Tcr		= 0x0000,	/* transmit control */
+	Eph		= 0x0002,	/* ethernet protocol handler */
+	Rcr		= 0x0004,	/* receiver control */
+	Counter		= 0x0006,	/* statistics counter */
+	MemInfo		= 0x0008,
+	MemCfg		= 0x000A,
+};
+
+enum {	/* bank 1 registers */
+	Config		= 0x0000,
+	BaseAddr	= 0x0002,
+	Addr0		= 0x0004,	/* ethernet address */
+	Addr1		= 0x0006,
+	Addr2		= 0x0008,
+	General		= 0x000A,
+	Control		= 0x000C,
+};
+
+enum {	/* bank 2 registers */
+	MmuCmd		= 0x0000,
+	PktNo		= 0x0002,
+	AllocRes	= 0x0003,
+	FifoPorts	= 0x0004,
+	Pointer		= 0x0006,
+	Data1		= 0x0008,
+	Interrupt	= 0x000C,
+	IntrMask	= 0x000D,
+};
+
+enum {	/* bank 3 registers */
+	Mcast0		= 0x0000,
+	Mcast2		= 0x0002,
+	Mcast4		= 0x0004,
+	Mcast6		= 0x0006,
+	Revision	= 0x000A,
+};
+
+enum {
+	BankSelect	= 0x000E	/* bank select register */
+};
+
+enum {
+	BsrMask		= 0xFF00,	/* mask for chip identification */
+	BsrId		= 0x3300,
+};
+
+
+enum {	/* Tcr values */
+	TcrClear	= 0x0000,
+	TcrEnable	= 0x0001,	/* enable transmit */
+	TcrLoop		= 0x0002,	/* enable internal analogue loopback */
+	TcrForceCol	= 0x0004,	/* force collision on next tx */
+	TcrPadEn	= 0x0080,	/* pad short packets to 64 bytes */
+	TcrNoCrc	= 0x0100,	/* do not append CRC */
+	TcrMonCns	= 0x0400,	/* monitor carrier status */
+	TcrFduplx	= 0x0800,
+	TcrStpSqet	= 0x1000,
+	TcrEphLoop	= 0x2000,
+	TcrNormal	= TcrEnable,
+};
+
+enum {	/* Eph values */
+	EphTxOk		= 0x0001,
+	Eph1Col		= 0x0002,	/* single collision */
+	EphMCol		= 0x0004,	/* multiple collisions */  
+	EphTxMcast	= 0x0008,	/* multicast transmit */
+	Eph16Col	= 0x0010,	/* 16 collisions, tx disabled */
+	EphSqet		= 0x0020,	/* SQE test failed, tx disabled */
+	EphTxBcast	= 0x0040,	/* broadcast tx */
+	EphDefr		= 0x0080,	/* deffered tx */
+	EphLatCol	= 0x0200,	/* late collision, tx disabled */
+	EphLostCarr	= 0x0400,	/* lost carrier, tx disabled */
+	EphExcDefr	= 0x0800,	/* excessive defferals */
+	EphCntRol	= 0x1000,	/* ECR counter(s) rolled over */
+	EphRxOvrn	= 0x2000,	/* receiver overrun, packets dropped */
+	EphLinkOk	= 0x4000,
+	EphTxUnrn	= 0x8000,	/* tx underrun */
+};
+
+enum {	/* Rcr values */
+	RcrClear	= 0x0000,
+	RcrPromisc	= 0x0002,
+	RcrAllMcast	= 0x0004,
+	RcrEnable	= 0x0100,
+	RcrStripCrc	= 0x0200,
+	RcrSoftReset	= 0x8000,
+	RcrNormal	= RcrStripCrc | RcrEnable,
+};
+
+enum { /* Counter value masks */
+	CntColMask	= 0x000F,	/* collisions */
+	CntMColMask	= 0x00F0,	/* multiple collisions */
+	CntDtxMask	= 0x0F00,	/* deferred transmits */
+	CntExDtxMask	= 0xF000,	/* excessively deferred transmits */
+
+	CntColShr	= 1,
+	CntMColShr	= 4,
+	CntDtxShr	= 8,
+};
+
+enum { /* MemInfo value masks */
+	MirTotalMask	= 0x00FF,
+	MirFreeMask	= 0xFF00,
+};
+
+enum {	/* Config values */
+	CfgIrqSel0	= 0x0002,
+	CfgIrqSel1	= 0x0004,
+	CfgDisLink	= 0x0040,	/* disable 10BaseT link test */
+	Cfg16Bit	= 0x0080,
+	CfgAuiSelect	= 0x0100,
+	CfgSetSqlch	= 0x0200,
+	CfgFullStep	= 0x0400,
+	CfgNoWait	= 0x1000,
+	CfgMiiSelect	= 0x8000,
+};
+
+enum {	/* Control values */
+	CtlStore	= 0x0001,	/* store to EEPROM */
+	CtlReload	= 0x0002,	/* reload EEPROM into registers */
+	CtlEeSelect	= 0x0004,	/* select registers for reload/store */
+	CtlTeEnable	= 0x0020,	/* tx error detection via eph irq */
+	CtlCrEnable	= 0x0040,	/* counter rollover via eph irq */
+	CtlLeEnable	= 0x0080,	/* link error detection via eph irq*/
+	CtlAutoRls	= 0x0800,	/* auto release mode */
+	CtlPowerDn	= 0x2000,
+};
+
+enum {	/* MmuCmd values */
+	McBusy		= 0x0001,
+	McAlloc		= 0x0020,	/* | with number of 256 byte packets - 1 */
+	McReset		= 0x0040,
+	McRelease	= 0x0080,	/* dequeue (but not free) current rx packet */
+	McFreePkt	= 0x00A0,	/* dequeue and free current rx packet */
+	McEnqueue	= 0x00C0,	/* enqueue the packet for tx */
+	McTxReset	= 0x00E0,	/* reset transmit queues */
+};
+
+enum { /* AllocRes values */
+	ArFailed	= 0x80,
+};
+	  
+enum {	/* FifoPorts values */
+	FpTxEmpty	= 0x0080,
+	FpRxEmpty	= 0x8000,
+	FpTxMask	= 0x007F,
+	FpRxMask	= 0x7F00,
+};
+
+enum {	/* Pointer values */
+	PtrRead		= 0x2000,
+	PtrAutoInc	= 0x4000,
+	PtrRcv		= 0x8000,
+};
+
+enum {	/* Interrupt values */
+	IntRcv		= 0x0001,
+	IntTxError	= 0x0002,
+	IntTxEmpty	= 0x0004,
+	IntAlloc	= 0x0008,
+	IntRxOvrn	= 0x0010,
+	IntEph		= 0x0020,
+};
+
+enum { /* transmit status bits */
+	TsSuccess	= 0x0001,
+	Ts16Col		= 0x00A0,
+	TsLatCol	= 0x0200,
+	TsLostCar	= 0x0400,
+};
+
+enum { /* receive status bits */
+	RsMcast		= 0x0001,
+	RsTooShort	= 0x0400,
+	RsTooLong	= 0x0800,
+	RsOddFrame	= 0x1000,
+	RsBadCrc	= 0x2000,
+	RsAlgnErr	= 0x8000,
+	RsError		= RsAlgnErr | RsBadCrc | RsTooLong | RsTooShort,
+};
+
+enum {
+	RxLenMask	= 0x07FF,	/* significant rx len bits */
+	HdrSize		= 6,		/* packet header length */
+	PageSize	= 256,		/* page length */
+};
+
+typedef struct Smc91xx Smc91xx;
+struct Smc91xx {
+	Lock;
+	ushort rev;
+	int attached;
+	Block *txbp;
+	ulong txtime;
+
+	ulong rovrn;
+	ulong lcar;
+	ulong col;
+	ulong scol;
+	ulong mcol;
+	ulong lcol;
+	ulong dfr;
+};
+
+#define SELECT_BANK(x) outs(port + BankSelect, x)
+
+static int
+readnodeid(int slot, Ether* ether)
+{
+	uchar data[Eaddrlen + 1];
+	int len;
+
+	len = sizeof(data);
+	if (pcmcistuple(slot, TupleFunce, TfNodeId, data, len) != len)
+		return -1;
+
+	if (data[0] != Eaddrlen)
+		return -1;
+
+	memmove(ether->ea, &data[1], Eaddrlen);
+	return 0;
+}
+
+static void
+chipreset(Ether* ether)
+{
+	int port;
+	int i;
+
+	port = ether->port;
+
+	/* reset the chip */
+	SELECT_BANK(0);
+	outs(port + Rcr, RcrSoftReset);
+	delay(1);
+	outs(port + Rcr, RcrClear);
+	outs(port + Tcr, TcrClear);
+	SELECT_BANK(1);
+	outs(port + Control, CtlAutoRls | CtlTeEnable |
+		CtlCrEnable);
+
+	for(i = 0; i < 6; i++) {
+		outb(port + Addr0 +  i, ether->ea[i]);
+	}
+
+	SELECT_BANK(2);
+	outs(port + MmuCmd, McReset);
+}
+
+static void
+chipenable(Ether* ether)
+{
+	int port;
+
+	port = ether->port;
+	SELECT_BANK(0);
+	outs(port + Tcr, TcrNormal);
+	outs(port + Rcr, RcrNormal);
+	SELECT_BANK(2);
+	outb(port + IntrMask, IntEph | IntRxOvrn | IntRcv);
+}
+
+static void
+attach(Ether *ether)
+{
+	Smc91xx* ctlr;
+
+	ctlr = ether->ctlr;
+	ilock(ctlr);
+	
+	if (ctlr->attached) {
+		iunlock(ctlr);
+		return;
+	}
+
+	chipenable(ether);
+	ctlr->attached = 1;
+	iunlock(ctlr);
+}
+
+static void
+txstart(Ether* ether)
+{
+	int port;
+	Smc91xx* ctlr;
+	Block* bp;
+	int len, npages;
+	int pno;
+
+	/* assumes ctlr is locked and bank 2 is selected */
+	/* leaves bank 2 selected on return */
+	port = ether->port;
+	ctlr = ether->ctlr;
+
+	if (ctlr->txbp) {
+		bp = ctlr->txbp;
+		ctlr->txbp = 0;
+	} else {
+		bp = qget(ether->oq);
+		if (bp == 0)
+			return;
+
+		len = BLEN(bp);
+		npages = (len + HdrSize) / PageSize;
+		outs(port + MmuCmd, McAlloc | npages);
+	}
+
+	pno = inb(port + AllocRes);
+	if (pno & ArFailed) {
+		outb(port + IntrMask, inb(port + IntrMask) | IntAlloc);
+		ctlr->txbp = bp;
+		ctlr->txtime = MACHP(0)->ticks;
+		return;
+	}
+
+	outb(port + PktNo, pno);
+	outs(port + Pointer, PtrAutoInc);
+
+	len = BLEN(bp);
+	outs(port + Data1, 0);
+	outb(port + Data1, (len + HdrSize) & 0xFF);
+	outb(port + Data1, (len + HdrSize) >> 8);
+	outss(port + Data1, bp->rp, len / 2);
+	if ((len & 1) == 0) {
+		outs(port + Data1, 0);
+	} else {
+		outb(port + Data1, bp->rp[len - 1]);
+		outb(port + Data1, 0x20);	/* no info what 0x20 means */
+	}
+
+	outb(port + IntrMask, inb(port + IntrMask) |
+			IntTxError | IntTxEmpty);
+
+	outs(port + MmuCmd, McEnqueue);
+	freeb(bp);
+}
+
+static void
+receive(Ether* ether)
+{
+	int port;
+	Block* bp;
+	int pktno, status, len;
+
+	/* assumes ctlr is locked and bank 2 is selected */
+	/* leaves bank 2 selected on return */
+	port = ether->port;
+
+	pktno = ins(port + FifoPorts);
+	if (pktno & FpRxEmpty) {
+		return;
+	}
+
+	outs(port + Pointer, PtrRead | PtrRcv | PtrAutoInc);
+	status = ins(port + Data1);
+	len = ins(port + Data1) & RxLenMask - HdrSize;
+	
+	if (status & RsOddFrame)
+		len++;
+	
+	if ((status & RsError) || (bp = iallocb(len)) == 0) {
+
+		if (status & RsAlgnErr)
+			ether->frames++;
+		if (status & (RsTooShort | RsTooLong))
+			ether->buffs++;
+		if (status & RsBadCrc)
+			ether->crcs++;
+
+		outs(port + MmuCmd, McRelease);
+		return;
+	}
+
+	/* packet length is padded to word */
+	inss(port + Data1, bp->rp, len / 2);
+	bp->wp = bp->rp + (len & ~1);
+	
+	if (len & 1) {
+		*bp->wp = inb(port + Data1);
+		bp->wp++;
+	}
+	  
+	etheriq(ether, bp, 1);
+	ether->inpackets++;
+	outs(port + MmuCmd, McRelease);
+}
+
+static void
+txerror(Ether* ether)
+{
+	int port;
+	Smc91xx* ctlr;
+	int save_pkt;
+	int pktno, status;
+
+	/* assumes ctlr is locked and bank 2 is selected */
+	/* leaves bank 2 selected on return */
+	port = ether->port;
+	ctlr = ether->ctlr;
+
+	save_pkt = inb(port + PktNo);
+
+	pktno = ins(port + FifoPorts) & FpTxMask;
+	outb(port + PktNo, pktno);
+	outs(port + Pointer, PtrAutoInc | PtrRead);
+	status = ins(port + Data1);
+	
+	if (status & TsLostCar)
+		ctlr->lcar++;
+
+	if (status & TsLatCol)
+		ctlr->lcol++;
+
+	if (status & Ts16Col)
+		ctlr->scol++;
+
+	ether->oerrs++;
+	
+	SELECT_BANK(0);
+	outs(port + Tcr, ins(port + Tcr) | TcrEnable);
+	
+	SELECT_BANK(2);
+	outs(port + MmuCmd, McFreePkt);
+
+	outb(port + PktNo, save_pkt);
+}
+
+static void
+eph_irq(Ether* ether)
+{
+	int port;
+	Smc91xx* ctlr;
+	ushort status;
+	int n;
+
+	/* assumes ctlr is locked and bank 2 is selected */
+	/* leaves bank 2 selected on return */
+	port = ether->port;
+	ctlr = ether->ctlr;
+
+	SELECT_BANK(0);
+	status = ins(port + Eph);
+
+	if (status & EphCntRol) {
+		/* read the counter register even if we don't need it */
+		/* otherwise we will keep getting this interrupt */
+		n = ins(port + Counter);
+		ctlr->col += (n & CntColMask) >> CntColShr;
+		ctlr->mcol += (n & CntMColMask) >> CntMColShr;
+		ctlr->dfr += (n & CntDtxMask) >> CntDtxShr;
+	}
+
+	/* if there was a transmit error, Tcr is disabled */
+	outs(port + Tcr, ins(port + Tcr) | TcrEnable);
+
+	/* clear a link error interrupt */
+	SELECT_BANK(1);
+	outs(port + Control, CtlAutoRls);
+	outs(port + Control, CtlAutoRls | CtlTeEnable | CtlCrEnable);
+
+	SELECT_BANK(2);
+}
+
+static void
+transmit(Ether* ether)
+{
+	Smc91xx* ctlr;
+	int port, n;
+
+	ctlr = ether->ctlr;
+	port = ether->port;
+	ilock(ctlr);
+
+	if (ctlr->txbp) {
+		n = TK2MS(MACHP(0)->ticks - ctlr->txtime);
+		if (n > TxTimeout) {
+			chipreset(ether);
+			chipenable(ether);
+			freeb(ctlr->txbp);
+			ctlr->txbp = 0;
+		}
+		iunlock(ctlr);
+		return;
+	}
+
+	SELECT_BANK(2);
+	txstart(ether);
+	iunlock(ctlr);
+}
+
+static void
+interrupt(Ureg*, void *arg)
+{
+	int port;
+	Smc91xx* ctlr;
+	Ether* ether;
+	int save_bank;
+	int save_pointer;
+	int mask, status;
+
+	ether = arg;
+	port = ether->port;
+	ctlr = ether->ctlr;
+	
+	ilock(ctlr);
+	save_bank = ins(port + BankSelect);
+	SELECT_BANK(2);
+	save_pointer = ins(port + Pointer);
+	
+	mask = inb(port + IntrMask);
+	outb(port + IntrMask, 0);
+
+	while ((status = inb(port + Interrupt) & mask) != 0) {
+		if (status & IntRcv) {
+			receive(ether);
+		}
+
+		if (status & IntTxError) {
+			txerror(ether);
+		}
+
+		if (status & IntTxEmpty) {
+			outb(port + Interrupt, IntTxEmpty);
+			outb(port + IntrMask, mask & ~IntTxEmpty);
+			txstart(ether);
+			mask = inb(port + IntrMask);
+		}
+
+		if (status & IntAlloc) {
+			outb(port + IntrMask, mask & ~IntAlloc);
+			txstart(ether);;
+			mask = inb(port + IntrMask);
+		}
+
+		if (status & IntRxOvrn) {
+			ctlr->rovrn++;
+			ether->misses++;
+			outb(port + Interrupt,IntRxOvrn);
+		}
+
+		if (status & IntEph)
+			eph_irq(ether);
+	}
+	
+	outb(port + IntrMask, mask);
+	outs(port + Pointer, save_pointer);
+	outs(port + BankSelect, save_bank);
+	iunlock(ctlr);
+}
+
+static void
+promiscuous(void* arg, int on)
+{
+	int port;
+	Smc91xx *ctlr;
+	Ether* ether;
+	ushort x;
+
+	ether = arg;
+	port = ether->port;
+	ctlr = ether->ctlr;
+
+	ilock(ctlr);
+	SELECT_BANK(0);
+	x = ins(port + Rcr);
+	if (on)
+		x |= RcrPromisc;
+	else
+		x &= ~RcrPromisc;
+	
+	outs(port + Rcr, x);
+	iunlock(ctlr);
+}
+
+static void
+multicast(void* arg, uchar *addr, int on)
+{
+	int port;
+	Smc91xx*ctlr;
+	Ether *ether;
+	ushort x;
+	
+	USED(addr, on);
+
+	ether = arg;
+	port = ether->port;
+	ctlr = ether->ctlr;
+	ilock(ctlr);
+	
+	SELECT_BANK(0);
+	x = ins(port + Rcr);
+	
+	if (ether->nmaddr)
+		x |= RcrAllMcast;
+	else
+		x &= ~RcrAllMcast;
+	
+	outs(port + Rcr, x);
+	iunlock(ctlr);
+}
+
+static long
+ifstat(Ether* ether, void* a, long n, ulong offset)
+{
+	static char *chiprev[] = {
+		[3] 	"92",
+		[5]	"95",
+		[7]	"100",
+		[8]	"100-FD",
+		[9]	"110",
+	};
+
+	Smc91xx* ctlr;
+	char* p;
+	int r, len;
+	char* s;
+	
+	if (n == 0)
+		return 0;
+
+	ctlr = ether->ctlr;
+	p = malloc(READSTR);
+
+	s = 0;
+	if (ctlr->rev > 0) {
+		r = ctlr->rev >> 4;
+		if (r < nelem(chiprev))
+			s = chiprev[r];
+
+		if (r == 4) {
+			if ((ctlr->rev & 0x0F) >= 6)
+				s = "96";
+			else
+				s = "94";
+		}
+	}
+
+	len = snprint(p, READSTR, "rev: 91c%s\n", (s) ? s : "???");
+	len += snprint(p + len, READSTR - len, "rxovrn: %uld\n", ctlr->rovrn);
+	len += snprint(p + len, READSTR - len, "lcar: %uld\n", ctlr->lcar);
+	len += snprint(p + len, READSTR - len, "col: %uld\n", ctlr->col);
+	len += snprint(p + len, READSTR - len, "16col: %uld\n", ctlr->scol);
+	len += snprint(p + len, READSTR - len, "mcol: %uld\n", ctlr->mcol);
+	len += snprint(p + len, READSTR - len, "lcol: %uld\n", ctlr->lcol);
+	len += snprint(p + len, READSTR - len, "dfr: %uld\n", ctlr->dfr);
+	USED(len);
+
+	n = readstr(offset, a, n, p);
+	free(p);
+	
+	return n;
+}
+
+static int
+reset(Ether* ether)
+{
+	int port;
+	int i, x;
+	char* type;
+	Smc91xx* ctlr;
+	int slot;
+	uchar ea[Eaddrlen];
+
+	if (ether->irq == 0)
+		ether->irq = 9;
+
+	if (ether->port == 0)
+		ether->port = 0x100;
+
+	type = "8020";
+	for(i = 0; i < ether->nopt; i++) {
+		if (cistrncmp(ether->opt[i], "id=", 3))
+			continue;
+		type = &ether->opt[i][3];
+		break;
+	}
+
+	if ((slot = pcmspecial(type, ether)) < 0)
+		return -1;
+
+	if (ioalloc(ether->port, IoSize, 0, "smc91cXX") < 0) {
+		pcmspecialclose(slot);
+		return -1;
+	}
+
+	ether->ctlr = malloc(sizeof(Smc91xx));
+	ctlr = ether->ctlr;
+	if (ctlr == 0) {
+		iofree(ether->port);
+		pcmspecialclose(slot);
+		return -1;
+	}
+
+	ilock(ctlr);
+	ctlr->rev = 0;
+	ctlr->txbp = nil;
+	ctlr->attached = 0;
+	ctlr->rovrn = 0;
+	ctlr->lcar = 0;
+	ctlr->col = 0;
+	ctlr->scol = 0;
+	ctlr->mcol = 0;
+	ctlr->lcol = 0;
+	ctlr->dfr = 0;
+
+	port = ether->port;
+
+	SELECT_BANK(1);
+	if ((ins(port + BankSelect) & BsrMask) != BsrId) {
+		outs(port + Control, 0);	/* try powering up the chip */
+		delay(55);
+	}
+
+	outs(port + Config, ins(port + Config) | Cfg16Bit);
+	x = ins(port + BaseAddr);
+
+	if (((ins(port + BankSelect) & BsrMask) != BsrId) ||
+		((x >> 8) == (x & 0xFF))) {
+		iunlock(ctlr);
+		iofree(port);
+		pcmspecialclose(slot);
+		return -1;
+	}
+
+	SELECT_BANK(3);
+	ctlr->rev = ins(port + Revision) & 0xFF;
+
+	memset(ea, 0, Eaddrlen);
+	if (memcmp(ea, ether->ea, Eaddrlen) == 0) {
+		if (readnodeid(slot, ether) < 0) {
+			print("Smc91cXX: cannot find ethernet address\n");
+			iunlock(ctlr);
+			iofree(port);
+			pcmspecialclose(slot);
+			return -1;
+		}
+	}
+
+	chipreset(ether);
+
+	ether->attach = attach;
+	ether->transmit = transmit;
+	ether->interrupt = interrupt;
+	ether->ifstat = ifstat;
+	ether->promiscuous = promiscuous;
+	ether->multicast = multicast;
+	ether->arg = ether;
+	iunlock(ctlr);
+	return 0;
+}
+
+void
+ethersmclink(void)
+{
+	addethercard("smc91cXX", reset);
+}
--- /dev/null
+++ b/os/pc/ethervt6102.c
@@ -1,0 +1,1025 @@
+/*
+ * VIA VT6102 Fast Ethernet Controller (Rhine II).
+ * To do:
+ *	cache-line size alignments - done
+ *	reduce tx interrupts
+ *	use 2 descriptors on tx for alignment - done
+ *	reorganise initialisation/shutdown/reset
+ *	adjust Tx FIFO threshold on underflow - untested
+ *	why does the link status never cause an interrupt?
+ *	use the lproc as a periodic timer for stalls, etc.
+ */
+#include "u.h"
+#include "../port/lib.h"
+#include "mem.h"
+#include "dat.h"
+#include "fns.h"
+#include "io.h"
+#include "../port/error.h"
+#include "../port/netif.h"
+
+#include "etherif.h"
+#include "ethermii.h"
+
+enum {
+	Par0		= 0x00,		/* Ethernet Address */
+	Rcr		= 0x06,		/* Receive Configuration */
+	Tcr		= 0x07,		/* Transmit Configuration */
+	Cr		= 0x08,		/* Control */
+	Isr		= 0x0C,		/* Interrupt Status */
+	Imr		= 0x0E,		/* Interrupt Mask */
+	Rxdaddr		= 0x18,		/* Current Rx Descriptor Address */
+	Txdaddr		= 0x1C,		/* Current Tx Descriptor Address */
+	Phyadr		= 0x6C,		/* Phy Address */
+	Miisr		= 0x6D,		/* MII Status */
+	Bcr0		= 0x6E,		/* Bus Control */
+	Bcr1		= 0x6F,
+	Miicr		= 0x70,		/* MII Control */
+	Miiadr		= 0x71,		/* MII Address */
+	Miidata		= 0x72,		/* MII Data */
+	Eecsr		= 0x74,		/* EEPROM Control and Status */
+};
+
+enum {					/* Rcr */
+	Sep		= 0x01,		/* Accept Error Packets */
+	Ar		= 0x02,		/* Accept Small Packets */
+	Am		= 0x04,		/* Accept Multicast */
+	Ab		= 0x08,		/* Accept Broadcast */
+	Prom		= 0x10,		/* Accept Physical Address Packets */
+	RrftMASK	= 0xE0,		/* Receive FIFO Threshold */
+	RrftSHIFT	= 5,
+	Rrft64		= 0<<RrftSHIFT,
+	Rrft32		= 1<<RrftSHIFT,
+	Rrft128		= 2<<RrftSHIFT,
+	Rrft256		= 3<<RrftSHIFT,
+	Rrft512		= 4<<RrftSHIFT,
+	Rrft768		= 5<<RrftSHIFT,
+	Rrft1024	= 6<<RrftSHIFT,
+	RrftSAF		= 7<<RrftSHIFT,
+};
+
+enum {					/* Tcr */
+	Lb0		= 0x02,		/* Loopback Mode */
+	Lb1		= 0x04,
+	Ofset		= 0x08,		/* Back-off Priority Selection */
+	RtsfMASK	= 0xE0,		/* Transmit FIFO Threshold */
+	RtsfSHIFT	= 5,
+	Rtsf128		= 0<<RtsfSHIFT,
+	Rtsf256		= 1<<RtsfSHIFT,
+	Rtsf512		= 2<<RtsfSHIFT,
+	Rtsf1024	= 3<<RtsfSHIFT,
+	RtsfSAF		= 7<<RtsfSHIFT,
+};
+
+enum {					/* Cr */
+	Init		= 0x0001,	/* INIT Process Begin */
+	Strt		= 0x0002,	/* Start NIC */
+	Stop		= 0x0004,	/* Stop NIC */
+	Rxon		= 0x0008,	/* Turn on Receive Process */
+	Txon		= 0x0010,	/* Turn on Transmit Process */
+	Tdmd		= 0x0020,	/* Transmit Poll Demand */
+	Rdmd		= 0x0040,	/* Receive Poll Demand */
+	Eren		= 0x0100,	/* Early Receive Enable */
+	Fdx		= 0x0400,	/* Set MAC to Full Duplex Mode */
+	Dpoll		= 0x0800,	/* Disable Td/Rd Auto Polling */
+	Tdmd1		= 0x2000,	/* Transmit Poll Demand 1 */
+	Rdmd1		= 0x4000,	/* Receive Poll Demand 1 */
+	Sfrst		= 0x8000,	/* Software Reset */
+};
+
+enum {					/* Isr/Imr */
+	Prx		= 0x0001,	/* Received Packet Successfully */
+	Ptx		= 0x0002,	/* Transmitted Packet Successfully */
+	Rxe		= 0x0004,	/* Receive Error */
+	Txe		= 0x0008,	/* Transmit Error */
+	Tu		= 0x0010,	/* Transmit Buffer Underflow */
+	Ru		= 0x0020,	/* Receive Buffer Link Error */
+	Be		= 0x0040,	/* PCI Bus Error */
+	Cnt		= 0x0080,	/* Counter Overflow */
+	Eri		= 0x0100,	/* Early Receive Interrupt */
+	Udfi		= 0x0200,	/* Tx FIFO Underflow */
+	Ovfi		= 0x0400,	/* Receive FIFO Overflow */
+	Pktrace		= 0x0800,	/* Hmmm... */
+	Norbf		= 0x1000,	/* No Receive Buffers */
+	Abti		= 0x2000,	/* Transmission Abort */
+	Srci		= 0x4000,	/* Port State Change */
+	Geni		= 0x8000,	/* General Purpose Interrupt */
+};
+
+enum {					/* Phyadr */
+	PhyadMASK	= 0x1F,		/* PHY Address */
+	PhyadSHIFT	= 0,
+	Mfdc		= 0x20,		/* Accelerate MDC Speed */
+	Mpo0		= 0x40,		/* MII Polling Timer Interval */
+	Mpo1		= 0x80,
+};
+
+enum {					/* Bcr0 */
+	DmaMASK		= 0x07,		/* DMA Length */
+	DmaSHIFT	= 0,
+	Dma32		= 0<<DmaSHIFT,
+	Dma64		= 1<<DmaSHIFT,
+	Dma128		= 2<<DmaSHIFT,
+	Dma256		= 3<<DmaSHIFT,
+	Dma512		= 4<<DmaSHIFT,
+	Dma1024		= 5<<DmaSHIFT,
+	DmaSAF		= 7<<DmaSHIFT,
+	CrftMASK	= 0x38,		/* Rx FIFO Threshold */
+	CrftSHIFT	= 3,
+	Crft64		= 1<<CrftSHIFT,
+	Crft128		= 2<<CrftSHIFT,
+	Crft256		= 3<<CrftSHIFT,
+	Crft512		= 4<<CrftSHIFT,
+	Crft1024	= 5<<CrftSHIFT,
+	CrftSAF		= 7<<CrftSHIFT,
+	Extled		= 0x40,		/* Extra LED Support Control */
+	Med2		= 0x80,		/* Medium Select Control */
+};
+
+enum {					/* Bcr1 */
+	PotMASK		= 0x07,		/* Polling Timer Interval */
+	PotSHIFT	= 0,
+	CtftMASK	= 0x38,		/* Tx FIFO Threshold */
+	CtftSHIFT	= 3,
+	Ctft64		= 1<<CtftSHIFT,
+	Ctft128		= 2<<CtftSHIFT,
+	Ctft256		= 3<<CtftSHIFT,
+	Ctft512		= 4<<CtftSHIFT,
+	Ctft1024	= 5<<CtftSHIFT,
+	CtftSAF		= 7<<CtftSHIFT,
+};
+
+enum {					/* Miicr */
+	Mdc		= 0x01,		/* Clock */
+	Mdi		= 0x02,		/* Data In */
+	Mdo		= 0x04,		/* Data Out */
+	Mout		= 0x08,		/* Output Enable */
+	Mdpm		= 0x10,		/* Direct Program Mode Enable */
+	Wcmd		= 0x20,		/* Write Enable */
+	Rcmd		= 0x40,		/* Read Enable */
+	Mauto		= 0x80,		/* Auto Polling Enable */
+};
+
+enum {					/* Miiadr */
+	MadMASK		= 0x1F,		/* MII Port Address */
+	MadSHIFT	= 0,
+	Mdone		= 0x20,		/* Accelerate MDC Speed */
+	Msrcen		= 0x40,		/* MII Polling Timer Interval */
+	Midle		= 0x80,
+};
+
+enum {					/* Eecsr */
+	Edo		= 0x01,		/* Data Out */
+	Edi		= 0x02,		/* Data In */
+	Eck		= 0x04,		/* Clock */
+	Ecs		= 0x08,		/* Chip Select */
+	Dpm		= 0x10,		/* Direct Program Mode Enable */
+	Autold		= 0x20,		/* Dynamic Reload */
+	Embp		= 0x40,		/* Embedded Program Enable */
+	Eepr		= 0x80,		/* Programmed */
+};
+
+/*
+ * Ring descriptor. The space allocated for each
+ * of these will be rounded up to a cache-line boundary.
+ * The first 4 elements are known to the hardware.
+ */
+typedef struct Ds Ds;
+typedef struct Ds {
+	uint	status;
+	uint	control;
+	uint	addr;
+	uint	branch;
+
+	Block*	bp;
+	void*	bounce;
+	Ds*	next;
+	Ds*	prev;
+} Ds;
+
+enum {					/* Rx Ds status */
+	Rerr		= 0x00000001,	/* Receiver Error */
+	Crc		= 0x00000002,	/* CRC Error */
+	Fae		= 0x00000004,	/* Frame Alignment Error */
+	Fov		= 0x00000008,	/* FIFO Overflow */
+	Long		= 0x00000010,	/* A Long Packet */
+	Runt		= 0x00000020,	/* A Runt Packet */
+	Rxserr		= 0x00000040,	/* System Error */
+	Buff		= 0x00000080,	/* Buffer Underflow Error */
+	Rxedp		= 0x00000100,	/* End of Packet Buffer */
+	Rxstp		= 0x00000200,	/* Packet Start */
+	Chn		= 0x00000400,	/* Chain Buffer */
+	Phy		= 0x00000800,	/* Physical Address Packet */
+	Bar		= 0x00001000,	/* Broadcast Packet */
+	Mar		= 0x00002000,	/* Multicast Packet */
+	Rxok		= 0x00008000,	/* Packet Received Successfully */
+	LengthMASK	= 0x07FF0000,	/* Received Packet Length */
+	LengthSHIFT	= 16,
+
+	Own		= 0x80000000,	/* Descriptor Owned by NIC */
+};
+
+enum {					/* Tx Ds status */
+	NcrMASK		= 0x0000000F,	/* Collision Retry Count */
+	NcrSHIFT	= 0,
+	Cols		= 0x00000010,	/* Experienced Collisions */
+	Cdh		= 0x00000080,	/* CD Heartbeat */
+	Abt		= 0x00000100,	/* Aborted after Excessive Collisions */
+	Owc		= 0x00000200,	/* Out of Window Collision Seen */
+	Crs		= 0x00000400,	/* Carrier Sense Lost */
+	Udf		= 0x00000800,	/* FIFO Underflow */
+	Tbuff		= 0x00001000,	/* Invalid Td */
+	Txserr		= 0x00002000,	/* System Error */
+	Terr		= 0x00008000,	/* Excessive Collisions */
+};
+
+enum {					/* Tx Ds control */
+	TbsMASK		= 0x000007FF,	/* Tx Buffer Size */
+	TbsSHIFT	= 0,
+	Chain		= 0x00008000,	/* Chain Buffer */
+	Crcdisable	= 0x00010000,	/* Disable CRC generation */
+	Stp		= 0x00200000,	/* Start of Packet */
+	Edp		= 0x00400000,	/* End of Packet */
+	Ic		= 0x00800000,	/* Assert Interrupt Immediately */
+};
+
+enum {
+	Nrd		= 64,
+	Ntd		= 64,
+	Rdbsz		= ROUNDUP(ETHERMAXTU+4, 4),
+
+	Nrxstats	= 8,
+	Ntxstats	= 9,
+
+	Txcopy		= 128,
+};
+
+typedef struct Ctlr Ctlr;
+typedef struct Ctlr {
+	int	port;
+	Pcidev*	pcidev;
+	Ctlr*	next;
+	int	active;
+	int	id;
+	uchar	par[Eaddrlen];
+
+	QLock	alock;			/* attach */
+	void*	alloc;			/* receive/transmit descriptors */
+	int	cls;			/* alignment */
+	int	nrd;
+	int	ntd;
+
+	Ds*	rd;
+	Ds*	rdh;
+
+	Lock	tlock;
+	Ds*	td;
+	Ds*	tdh;
+	Ds*	tdt;
+	int	tdused;
+
+	Lock	clock;			/*  */
+	int	cr;
+	int	imr;
+	int	tft;			/* Tx threshold */
+
+	Mii*	mii;
+	Rendez	lrendez;
+	int	lwakeup;
+
+	uint	rxstats[Nrxstats];	/* statistics */
+	uint	txstats[Ntxstats];
+	uint	intr;
+	uint	lintr;			
+	uint	lsleep;
+	uint	rintr;
+	uint	tintr;
+	uint	taligned;
+	uint	tsplit;
+	uint	tcopied;
+	uint	txdw;
+} Ctlr;
+
+static Ctlr* vt6102ctlrhead;
+static Ctlr* vt6102ctlrtail;
+
+#define csr8r(c, r)	(inb((c)->port+(r)))
+#define csr16r(c, r)	(ins((c)->port+(r)))
+#define csr32r(c, r)	(inl((c)->port+(r)))
+#define csr8w(c, r, b)	(outb((c)->port+(r), (int)(b)))
+#define csr16w(c, r, w)	(outs((c)->port+(r), (ushort)(w)))
+#define csr32w(c, r, w)	(outl((c)->port+(r), (ulong)(w)))
+
+static char* rxstats[Nrxstats] = {
+	"Receiver Error",
+	"CRC Error",
+	"Frame Alignment Error",
+	"FIFO Overflow",
+	"Long Packet",
+	"Runt Packet",
+	"System Error",
+	"Buffer Underflow Error",
+};
+static char* txstats[Ntxstats] = {
+	"Aborted after Excessive Collisions",
+	"Out of Window Collision Seen",
+	"Carrier Sense Lost",
+	"FIFO Underflow",
+	"Invalid Td",
+	"System Error",
+	nil,
+	"Excessive Collisions",
+};
+
+static long
+vt6102ifstat(Ether* edev, void* a, long n, ulong offset)
+{
+	char *p;
+	Ctlr *ctlr;
+	int i, l, r;
+
+	ctlr = edev->ctlr;
+
+	p = malloc(2*READSTR);
+	l = 0;
+	for(i = 0; i < Nrxstats; i++){
+		l += snprint(p+l, 2*READSTR-l, "%s: %ud\n",
+			rxstats[i], ctlr->rxstats[i]);
+	}
+	for(i = 0; i < Ntxstats; i++){
+		if(txstats[i] == nil)
+			continue;
+		l += snprint(p+l, 2*READSTR-l, "%s: %ud\n",
+			txstats[i], ctlr->txstats[i]);
+	}
+	l += snprint(p+l, 2*READSTR-l, "cls: %ud\n", ctlr->cls);
+	l += snprint(p+l, 2*READSTR-l, "intr: %ud\n", ctlr->intr);
+	l += snprint(p+l, 2*READSTR-l, "lintr: %ud\n", ctlr->lintr);
+	l += snprint(p+l, 2*READSTR-l, "lsleep: %ud\n", ctlr->lsleep);
+	l += snprint(p+l, 2*READSTR-l, "rintr: %ud\n", ctlr->rintr);
+	l += snprint(p+l, 2*READSTR-l, "tintr: %ud\n", ctlr->tintr);
+	l += snprint(p+l, 2*READSTR-l, "taligned: %ud\n", ctlr->taligned);
+	l += snprint(p+l, 2*READSTR-l, "tsplit: %ud\n", ctlr->tsplit);
+	l += snprint(p+l, 2*READSTR-l, "tcopied: %ud\n", ctlr->tcopied);
+	l += snprint(p+l, 2*READSTR-l, "txdw: %ud\n", ctlr->txdw);
+	l += snprint(p+l, 2*READSTR-l, "tft: %ud\n", ctlr->tft);
+
+	if(ctlr->mii != nil && ctlr->mii->curphy != nil){
+		l += snprint(p+l, 2*READSTR, "phy:   ");
+		for(i = 0; i < NMiiPhyr; i++){
+			if(i && ((i & 0x07) == 0))
+				l += snprint(p+l, 2*READSTR-l, "\n       ");
+			r = miimir(ctlr->mii, i);
+			l += snprint(p+l, 2*READSTR-l, " %4.4uX", r);
+		}
+		snprint(p+l, 2*READSTR-l, "\n");
+	}
+	snprint(p+l, 2*READSTR-l, "\n");
+
+	n = readstr(offset, a, n, p);
+	free(p);
+
+	return n;
+}
+
+static void
+vt6102promiscuous(void* arg, int on)
+{
+	int rcr;
+	Ctlr *ctlr;
+	Ether *edev;
+
+	edev = arg;
+	ctlr = edev->ctlr;
+	rcr = csr8r(ctlr, Rcr);
+	if(on)
+		rcr |= Prom;
+	else
+		rcr &= ~Prom;
+	csr8w(ctlr, Rcr, rcr);
+}
+
+static void
+vt6102multicast(void* arg, uchar* addr, int on)
+{
+	/*
+	 * For now Am is set in Rcr.
+	 * Will need to interlock with promiscuous
+	 * when this gets filled in.
+	 */
+	USED(arg, addr, on);
+}
+
+static int
+vt6102wakeup(void* v)
+{
+	return *((int*)v) != 0;
+}
+
+static void
+vt6102imr(Ctlr* ctlr, int imr)
+{
+	ilock(&ctlr->clock);
+	ctlr->imr |= imr;
+	csr16w(ctlr, Imr, ctlr->imr);
+	iunlock(&ctlr->clock);
+}
+
+static void
+vt6102lproc(void* arg)
+{
+	Ctlr *ctlr;
+	Ether *edev;
+	MiiPhy *phy;
+
+	edev = arg;
+	ctlr = edev->ctlr;
+	for(;;){
+		if(ctlr->mii == nil || ctlr->mii->curphy == nil)
+			break;
+		if(miistatus(ctlr->mii) < 0)
+			goto enable;
+
+		phy = ctlr->mii->curphy;
+		ilock(&ctlr->clock);
+		if(phy->fd)
+			ctlr->cr |= Fdx;
+		else
+			ctlr->cr &= ~Fdx;
+		csr16w(ctlr, Cr, ctlr->cr);
+		iunlock(&ctlr->clock);
+enable:
+		ctlr->lwakeup = 0;
+		vt6102imr(ctlr, Srci);
+
+		ctlr->lsleep++;
+		sleep(&ctlr->lrendez, vt6102wakeup, &ctlr->lwakeup);
+
+	}
+	pexit("vt6102lproc: done", 1);
+}
+
+static void
+vt6102attach(Ether* edev)
+{
+	int i;
+	Ctlr *ctlr;
+	Ds *ds, *prev;
+	uchar *alloc, *bounce;
+	char name[KNAMELEN];
+
+	ctlr = edev->ctlr;
+	qlock(&ctlr->alock);
+	if(ctlr->alloc != nil){
+		qunlock(&ctlr->alock);
+		return;
+	}
+
+	/*
+	 * Descriptor and bounce-buffer space.
+	 * Must all be aligned on a 4-byte boundary,
+	 * but try to align on cache-lines.
+	 */
+	ctlr->nrd = Nrd;
+	ctlr->ntd = Ntd;
+	alloc = malloc((ctlr->nrd+ctlr->ntd)*ctlr->cls + ctlr->ntd*Txcopy + ctlr->cls-1);
+	if(alloc == nil){
+		qunlock(&ctlr->alock);
+		return;
+	}
+	ctlr->alloc = alloc;
+	alloc = (uchar*)ROUNDUP((ulong)alloc, ctlr->cls);
+
+	ctlr->rd = (Ds*)alloc;
+
+	if(waserror()){
+		ds = ctlr->rd;
+		for(i = 0; i < ctlr->nrd; i++){
+			if(ds->bp != nil){
+				freeb(ds->bp);
+				ds->bp = nil;
+			}
+			if((ds = ds->next) == nil)
+				break;
+		}
+		free(ctlr->alloc);
+		ctlr->alloc = nil;
+		qunlock(&ctlr->alock);
+		nexterror();
+	}
+
+	prev = ctlr->rd + ctlr->nrd-1;
+	for(i = 0; i < ctlr->nrd; i++){
+		ds = (Ds*)alloc;
+		alloc += ctlr->cls;
+
+		ds->control = Rdbsz;
+		ds->branch = PCIWADDR(alloc);
+
+		ds->bp = iallocb(Rdbsz+3);
+		if(ds->bp == nil)
+			error("vt6102: can't allocate receive ring\n");
+		ds->bp->rp = (uchar*)ROUNDUP((ulong)ds->bp->rp, 4);
+		ds->addr = PCIWADDR(ds->bp->rp);
+
+		ds->next = (Ds*)alloc;
+		ds->prev = prev;
+		prev = ds;
+
+		ds->status = Own;
+	}
+	prev->branch = 0;
+	prev->next = ctlr->rd;
+	prev->status = 0;
+	ctlr->rdh = ctlr->rd;
+
+	ctlr->td = (Ds*)alloc;
+	prev = ctlr->td + ctlr->ntd-1;
+	bounce = alloc + ctlr->ntd*ctlr->cls;
+	for(i = 0; i < ctlr->ntd; i++){
+		ds = (Ds*)alloc;
+		alloc += ctlr->cls;
+
+		ds->bounce = bounce;
+		bounce += Txcopy;
+		ds->next = (Ds*)alloc;
+		ds->prev = prev;
+		prev = ds;
+	}
+	prev->next = ctlr->td;
+	ctlr->tdh = ctlr->tdt = ctlr->td;
+	ctlr->tdused = 0;
+
+	ctlr->cr = Dpoll|Rdmd|Txon|Rxon|Strt;
+	/*Srci|Abti|Norbf|Pktrace|Ovfi|Udfi|Be|Ru|Tu|Txe|Rxe|Ptx|Prx*/
+	ctlr->imr = Abti|Norbf|Pktrace|Ovfi|Udfi|Be|Ru|Tu|Txe|Rxe|Ptx|Prx;
+
+	ilock(&ctlr->clock);
+	csr32w(ctlr, Rxdaddr, PCIWADDR(ctlr->rd));
+	csr32w(ctlr, Txdaddr, PCIWADDR(ctlr->td));
+	csr16w(ctlr, Isr, ~0);
+	csr16w(ctlr, Imr, ctlr->imr);
+	csr16w(ctlr, Cr, ctlr->cr);
+	iunlock(&ctlr->clock);
+
+	snprint(name, KNAMELEN, "#l%dlproc", edev->ctlrno);
+	kproc(name, vt6102lproc, edev, 0);
+
+	qunlock(&ctlr->alock);
+	poperror();
+}
+
+static void
+vt6102transmit(Ether* edev)
+{
+	Block *bp;
+	Ctlr *ctlr;
+	Ds *ds, *next;
+	int control, i, o, prefix, size, tdused, timeo;
+
+	ctlr = edev->ctlr;
+
+	ilock(&ctlr->tlock);
+
+	/*
+	 * Free any completed packets
+	 */
+	ds = ctlr->tdh;
+	for(tdused = ctlr->tdused; tdused > 0; tdused--){
+		/*
+		 * For some errors the chip will turn the Tx engine
+		 * off. Wait for that to happen.
+		 * Could reset and re-init the chip here if it doesn't
+		 * play fair.
+		 * To do: adjust Tx FIFO threshold on underflow.
+		 */
+		if(ds->status & (Abt|Tbuff|Udf)){
+			for(timeo = 0; timeo < 1000; timeo++){
+				if(!(csr16r(ctlr, Cr) & Txon))
+					break;
+				microdelay(1);
+			}
+			ds->status = Own;
+			csr32w(ctlr, Txdaddr, PCIWADDR(ds));
+		}
+
+		if(ds->status & Own)
+			break;
+		ds->addr = 0;
+		ds->branch = 0;
+
+		if(ds->bp != nil){
+			freeb(ds->bp);
+			ds->bp = nil;
+		}
+		for(i = 0; i < Ntxstats-1; i++){
+			if(ds->status & (1<<i))
+				ctlr->txstats[i]++;
+		}
+		ctlr->txstats[i] += (ds->status & NcrMASK)>>NcrSHIFT;
+
+		ds = ds->next;
+	}
+	ctlr->tdh = ds;
+
+	/*
+	 * Try to fill the ring back up.
+	 */
+	ds = ctlr->tdt;
+	while(tdused < ctlr->ntd-2){
+		if((bp = qget(edev->oq)) == nil)
+			break;
+		tdused++;
+
+		size = BLEN(bp);
+		prefix = 0;
+
+		if(o = (((int)bp->rp) & 0x03)){
+			prefix = Txcopy-o;
+			if(prefix > size)
+				prefix = size;
+			memmove(ds->bounce, bp->rp, prefix);
+			ds->addr = PCIWADDR(ds->bounce);
+			bp->rp += prefix;
+			size -= prefix;
+		}
+
+		next = ds->next;
+		ds->branch = PCIWADDR(ds->next);
+
+		if(size){
+			if(prefix){
+				next->bp = bp;
+				next->addr = PCIWADDR(bp->rp);
+				next->branch = PCIWADDR(next->next);
+				next->control = Edp|Chain|((size<<TbsSHIFT) & TbsMASK);
+
+				control = Stp|Chain|((prefix<<TbsSHIFT) & TbsMASK);
+
+				next = next->next;
+				tdused++;
+				ctlr->tsplit++;
+			}
+			else{
+				ds->bp = bp;
+				ds->addr = PCIWADDR(bp->rp);
+				control = Edp|Stp|((size<<TbsSHIFT) & TbsMASK);
+				ctlr->taligned++;
+			}
+		}
+		else{
+			freeb(bp);
+			control = Edp|Stp|((prefix<<TbsSHIFT) & TbsMASK);
+			ctlr->tcopied++;
+		}
+
+		ds->control = control;
+		if(tdused >= ctlr->ntd-2){
+			ds->control |= Ic;
+			ctlr->txdw++;
+		}
+		coherence();
+		ds->status = Own;
+
+		ds = next;
+	}
+	ctlr->tdt = ds;
+	ctlr->tdused = tdused;
+	if(ctlr->tdused)
+		csr16w(ctlr, Cr, Tdmd|ctlr->cr);
+
+	iunlock(&ctlr->tlock);
+}
+
+static void
+vt6102receive(Ether* edev)
+{
+	Ds *ds;
+	Block *bp;
+	Ctlr *ctlr;
+	int i, len;
+
+	ctlr = edev->ctlr;
+
+	ds = ctlr->rdh;
+	while(!(ds->status & Own) && ds->status != 0){
+		if(ds->status & Rerr){
+			for(i = 0; i < Nrxstats; i++){
+				if(ds->status & (1<<i))
+					ctlr->rxstats[i]++;
+			}
+		}
+		else if(bp = iallocb(Rdbsz+3)){
+			len = ((ds->status & LengthMASK)>>LengthSHIFT)-4;
+			ds->bp->wp = ds->bp->rp+len;
+			etheriq(edev, ds->bp, 1);
+			bp->rp = (uchar*)ROUNDUP((ulong)bp->rp, 4);
+			ds->addr = PCIWADDR(bp->rp);
+			ds->bp = bp;
+		}
+		ds->control = Rdbsz;
+		ds->branch = 0;
+		ds->status = 0;
+
+		ds->prev->branch = PCIWADDR(ds);
+		coherence();
+		ds->prev->status = Own;
+
+		ds = ds->next;
+	}
+	ctlr->rdh = ds;
+
+	csr16w(ctlr, Cr, ctlr->cr);
+}
+
+static void
+vt6102interrupt(Ureg*, void* arg)
+{
+	Ctlr *ctlr;
+	Ether *edev;
+	int imr, isr, r, timeo;
+
+	edev = arg;
+	ctlr = edev->ctlr;
+
+	ilock(&ctlr->clock);
+	csr16w(ctlr, Imr, 0);
+	imr = ctlr->imr;
+	ctlr->intr++;
+	for(;;){
+		if((isr = csr16r(ctlr, Isr)) != 0)
+			csr16w(ctlr, Isr, isr);
+		if((isr & ctlr->imr) == 0)
+			break;
+			
+		if(isr & Srci){
+			imr &= ~Srci;
+			ctlr->lwakeup = isr & Srci;
+			wakeup(&ctlr->lrendez);
+			isr &= ~Srci;
+			ctlr->lintr++;
+		}
+		if(isr & (Norbf|Pktrace|Ovfi|Ru|Rxe|Prx)){
+			vt6102receive(edev);
+			isr &= ~(Norbf|Pktrace|Ovfi|Ru|Rxe|Prx);
+			ctlr->rintr++;
+		}
+		if(isr & (Abti|Udfi|Tu|Txe|Ptx)){
+			if(isr & (Abti|Udfi|Tu)){
+				for(timeo = 0; timeo < 1000; timeo++){
+					if(!(csr16r(ctlr, Cr) & Txon))
+						break;
+					microdelay(1);
+				}
+
+				if((isr & Udfi) && ctlr->tft < CtftSAF){
+					ctlr->tft += 1<<CtftSHIFT;
+					r = csr8r(ctlr, Bcr1) & ~CtftMASK;
+					csr8w(ctlr, Bcr1, r|ctlr->tft);
+				}
+			}
+			vt6102transmit(edev);
+			isr &= ~(Abti|Udfi|Tu|Txe|Ptx);
+			ctlr->tintr++;
+		}
+		if(isr)
+			panic("vt6102: isr %4.4uX\n", isr);
+	}
+	ctlr->imr = imr;
+	csr16w(ctlr, Imr, ctlr->imr);
+	iunlock(&ctlr->clock);
+}
+
+static int
+vt6102miimicmd(Mii* mii, int pa, int ra, int cmd, int data)
+{
+	Ctlr *ctlr;
+	int r, timeo;
+
+	ctlr = mii->ctlr;
+
+	csr8w(ctlr, Miicr, 0);
+	r = csr8r(ctlr, Phyadr);
+	csr8w(ctlr, Phyadr, (r & ~PhyadMASK)|pa);
+	csr8w(ctlr, Phyadr, pa);
+	csr8w(ctlr, Miiadr, ra);
+	if(cmd == Wcmd)
+		csr16w(ctlr, Miidata, data);
+	csr8w(ctlr, Miicr, cmd);
+
+	for(timeo = 0; timeo < 10000; timeo++){
+		if(!(csr8r(ctlr, Miicr) & cmd))
+			break;
+		microdelay(1);
+	}
+	if(timeo >= 10000)
+		return -1;
+
+	if(cmd == Wcmd)
+		return 0;
+	return csr16r(ctlr, Miidata);
+}
+
+static int
+vt6102miimir(Mii* mii, int pa, int ra)
+{
+	return vt6102miimicmd(mii, pa, ra, Rcmd, 0);
+}
+
+static int
+vt6102miimiw(Mii* mii, int pa, int ra, int data)
+{
+	return vt6102miimicmd(mii, pa, ra, Wcmd, data);
+}
+
+static int
+vt6102detach(Ctlr* ctlr)
+{
+	int timeo;
+
+	/*
+	 * Soft reset the controller.
+	 */
+	csr16w(ctlr, Cr, Sfrst);
+	for(timeo = 0; timeo < 10000; timeo++){
+		if(!(csr16r(ctlr, Cr) & Sfrst))
+			break;
+		microdelay(1);
+	}
+	if(timeo >= 1000)
+		return -1;
+
+	return 0;
+}
+
+static int
+vt6102reset(Ctlr* ctlr)
+{
+	MiiPhy *phy;
+	int i, r, timeo;
+
+	if(vt6102detach(ctlr) < 0)
+		return -1;
+
+	/*
+	 * Load the MAC address into the PAR[01]
+	 * registers.
+	 */
+	r = csr8r(ctlr, Eecsr);
+	csr8w(ctlr, Eecsr, Autold|r);
+	for(timeo = 0; timeo < 100; timeo++){
+		if(!(csr8r(ctlr, Cr) & Autold))
+			break;
+		microdelay(1);
+	}
+	if(timeo >= 100)
+		return -1;
+
+	for(i = 0; i < Eaddrlen; i++)
+		ctlr->par[i] = csr8r(ctlr, Par0+i);
+
+	/*
+	 * Configure DMA and Rx/Tx thresholds.
+	 * If the Rx/Tx threshold bits in Bcr[01] are 0 then
+	 * the thresholds are determined by Rcr/Tcr.
+	 */
+	r = csr8r(ctlr, Bcr0) & ~(CrftMASK|DmaMASK);
+	csr8w(ctlr, Bcr0, r|Crft64|Dma64);
+	r = csr8r(ctlr, Bcr1) & ~CtftMASK;
+	csr8w(ctlr, Bcr1, r|ctlr->tft);
+
+	r = csr8r(ctlr, Rcr) & ~(RrftMASK|Prom|Ar|Sep);
+	csr8w(ctlr, Rcr, r|Ab|Am);
+
+	r = csr8r(ctlr, Tcr) & ~(RtsfMASK|Ofset|Lb1|Lb0);
+	csr8w(ctlr, Tcr, r);
+
+	/*
+	 * Link management.
+	 */
+	if((ctlr->mii = malloc(sizeof(Mii))) == nil)
+		return -1;
+	ctlr->mii->mir = vt6102miimir;
+	ctlr->mii->miw = vt6102miimiw;
+	ctlr->mii->ctlr = ctlr;
+
+	if(mii(ctlr->mii, ~0) == 0 || (phy = ctlr->mii->curphy) == nil){
+		free(ctlr->mii);
+		ctlr->mii = nil;
+		return -1;
+	}
+	// print("oui %X phyno %d\n", phy->oui, phy->phyno);
+	USED(phy);
+
+	//miiane(ctlr->mii, ~0, ~0, ~0);
+
+	return 0;
+}
+
+static void
+vt6102pci(void)
+{
+	Pcidev *p;
+	Ctlr *ctlr;
+	int cls, port;
+
+	p = nil;
+	while(p = pcimatch(p, 0, 0)){
+		if(p->ccrb != Pcibcnet || p->ccru != Pciscether)
+			continue;
+
+		switch((p->did<<16)|p->vid){
+		default:
+			continue;
+		case (0x3065<<16)|0x1106:	/* Rhine II */
+		case (0x3106<<16)|0x1106:	/* Rhine III */
+			break;
+		}
+
+		port = p->mem[0].bar & ~0x01;
+		if(ioalloc(port, p->mem[0].size, 0, "vt6102") < 0){
+			print("vt6102: port 0x%uX in use\n", port);
+			continue;
+		}
+		ctlr = malloc(sizeof(Ctlr));
+		ctlr->port = port;
+		ctlr->pcidev = p;
+		ctlr->id = (p->did<<16)|p->vid;
+		if((cls = pcicfgr8(p, PciCLS)) == 0 || cls == 0xFF)
+			cls = 0x10;
+		ctlr->cls = cls*4;
+		if(ctlr->cls < sizeof(Ds)){
+			print("vt6102: cls %d < sizeof(Ds)\n", ctlr->cls);
+			iofree(port);
+			free(ctlr);
+			continue;
+		}
+		ctlr->tft = Ctft64;
+
+		if(vt6102reset(ctlr)){
+			iofree(port);
+			free(ctlr);
+			continue;
+		}
+		pcisetbme(p);
+
+		if(vt6102ctlrhead != nil)
+			vt6102ctlrtail->next = ctlr;
+		else
+			vt6102ctlrhead = ctlr;
+		vt6102ctlrtail = ctlr;
+	}
+}
+
+static int
+vt6102pnp(Ether* edev)
+{
+	Ctlr *ctlr;
+
+	if(vt6102ctlrhead == nil)
+		vt6102pci();
+
+	/*
+	 * Any adapter matches if no edev->port is supplied,
+	 * otherwise the ports must match.
+	 */
+	for(ctlr = vt6102ctlrhead; ctlr != nil; ctlr = ctlr->next){
+		if(ctlr->active)
+			continue;
+		if(edev->port == 0 || edev->port == ctlr->port){
+			ctlr->active = 1;
+			break;
+		}
+	}
+	if(ctlr == nil)
+		return -1;
+
+	edev->ctlr = ctlr;
+	edev->port = ctlr->port;
+	edev->irq = ctlr->pcidev->intl;
+	edev->tbdf = ctlr->pcidev->tbdf;
+	edev->mbps = 100;
+	memmove(edev->ea, ctlr->par, Eaddrlen);
+
+	/*
+	 * Linkage to the generic ethernet driver.
+	 */
+	edev->attach = vt6102attach;
+	edev->transmit = vt6102transmit;
+	edev->interrupt = vt6102interrupt;
+	edev->ifstat = vt6102ifstat;
+	edev->ctl = nil;
+
+	edev->arg = edev;
+	edev->promiscuous = vt6102promiscuous;
+	edev->multicast = vt6102multicast;
+
+	return 0;
+}
+
+void
+ethervt6102link(void)
+{
+	addethercard("vt6102", vt6102pnp);
+	addethercard("rhine", vt6102pnp);
+}
--- /dev/null
+++ b/os/pc/etherwavelan.c
@@ -1,0 +1,197 @@
+/* Pci/pcmcia code for wavelan.c */
+
+#include "u.h"
+#include "../port/lib.h"
+#include "mem.h"
+#include "dat.h"
+#include "fns.h"
+#include "io.h"
+#include "../port/error.h"
+#include "../port/netif.h"
+#include "etherif.h"
+
+#include "wavelan.h"
+
+static int
+wavelanpcmciareset(Ether *ether)
+{
+	int i;
+	char *p;
+	Ctlr *ctlr;
+
+	if((ctlr = malloc(sizeof(Ctlr))) == nil)
+		return -1;
+
+	ilock(ctlr);
+	ctlr->ctlrno = ether->ctlrno;
+
+	if (ether->port==0)
+		ether->port=WDfltIOB;
+	ctlr->iob = ether->port;
+
+	if (ether->irq==0)
+		ether->irq=WDfltIRQ;
+
+	if (ioalloc(ether->port,WIOLen,0,"wavelan")<0){
+	//	print("#l%d: port 0x%lx in use\n",
+	//			ether->ctlrno, ether->port);
+		goto abort1;
+	}
+
+	/*
+	 * If id= is specified, card must match.  Otherwise try generic.
+	 */
+	ctlr->slot = -1;
+	for(i=0; i<ether->nopt; i++){
+		if(cistrncmp(ether->opt[i], "id=", 3) == 0){
+			if((ctlr->slot = pcmspecial(&ether->opt[i][3], ether)) < 0)
+				goto abort;
+			break;
+		}
+	}
+	if(ctlr->slot == -1){
+		for (i=0; wavenames[i]; i++)
+			if((ctlr->slot = pcmspecial(wavenames[i], ether))>=0)
+				break;
+		if(!wavenames[i]){
+			DEBUG("no wavelan found\n");
+			goto abort;
+		}
+	}
+
+	// DEBUG("#l%d: port=0x%lx irq=%ld\n",
+	//		ether->ctlrno, ether->port, ether->irq);
+
+	if(wavelanreset(ether, ctlr) < 0){
+	abort:
+		iofree(ether->port);
+	abort1:
+		iunlock(ctlr);
+		free(ctlr);
+		ether->ctlr = nil;
+		return -1;
+	}
+
+	for(i = 0; i < ether->nopt; i++){
+		if(p = strchr(ether->opt[i], '='))
+			*p = ' ';
+		w_option(ctlr, ether->opt[i], strlen(ether->opt[i]));
+	}
+
+	iunlock(ctlr);
+	return 0;
+}
+
+static struct {
+	int vid;
+	int did;
+} wavelanpci[] = {
+	0x1260, 0x3873,	/* Intersil Prism2.5 */
+	0x1737,	0x0019,	/* Linksys WPC-11 untested */
+};
+
+static Ctlr *ctlrhead, *ctlrtail;
+
+static void
+wavelanpciscan(void)
+{
+	int i;
+	void *mem;
+	Pcidev *p;
+	Ctlr *ctlr;
+
+	p = nil;
+	while(p = pcimatch(p, 0, 0)){
+		for(i=0; i<nelem(wavelanpci); i++)
+			if(p->vid == wavelanpci[i].vid && p->did == wavelanpci[i].did)
+				break;
+		if(i==nelem(wavelanpci))
+			continue;
+
+		/*
+		 * On the Prism, bar[0] is the memory-mapped register address (4KB),
+		 */
+		if(p->mem[0].size != 4096){
+			print("wavelanpci: %.4ux %.4ux: unlikely mmio size\n", p->vid, p->did);
+			continue;
+		}
+
+		ctlr = malloc(sizeof(Ctlr));
+		ctlr->pcidev = p;
+		mem = vmap(p->mem[0].bar&~0xF, p->mem[0].size);
+		if(mem == nil){
+			print("wavelanpci: %.4ux %.4ux: vmap 0x%.8lux %d failed\n", p->vid, p->did, p->mem[0].bar&~0xF, p->mem[0].size);
+			free(ctlr);
+			continue;
+		}
+		ctlr->mmb = mem;
+		if(ctlrhead != nil)
+			ctlrtail->next = ctlr;
+		else
+			ctlrhead = ctlr;
+		ctlrtail = ctlr;
+		pcisetbme(p);
+	}
+}
+
+static int
+wavelanpcireset(Ether *ether)
+{
+	int i;
+	char *p;
+	Ctlr *ctlr;
+
+	if(ctlrhead == nil)
+		wavelanpciscan();
+
+	/*
+	 * Allow plan9.ini to set vid, did?
+	 */
+	for(ctlr=ctlrhead; ctlr!=nil; ctlr=ctlr->next)
+		if(ctlr->active == 0)
+			break;
+	if(ctlr == nil)
+		return -1;
+
+	ctlr->active = 1;
+	ilock(ctlr);
+	ether->irq = ctlr->pcidev->intl;
+	ether->tbdf = ctlr->pcidev->tbdf;
+
+	/*
+	 * Really hard reset.
+	 */
+	csr_outs(ctlr, WR_PciCor, 0x0080);
+	delay(250);
+	csr_outs(ctlr, WR_PciCor, 0x0000);
+	delay(500);
+	for(i=0; i<2*10; i++){
+		if(!(csr_ins(ctlr, WR_Cmd)&WCmdBusy))
+			break;
+		delay(100);
+	}
+	if(i >= 2*10)
+		print("wavelan pci %.4ux %.4ux: reset timeout %.4ux\n",
+			ctlr->pcidev->vid, ctlr->pcidev->did, csr_ins(ctlr, WR_Cmd));
+
+	if(wavelanreset(ether, ctlr) < 0){
+		iunlock(ctlr);
+		ether->ctlr = nil;
+		return -1;
+	}
+
+	for(i = 0; i < ether->nopt; i++){
+		if(p = strchr(ether->opt[i], '='))
+			*p = ' ';
+		w_option(ctlr, ether->opt[i], strlen(ether->opt[i]));
+	}
+	iunlock(ctlr);
+	return 0;
+}
+	
+void
+etherwavelanlink(void)
+{
+	addethercard("wavelan", wavelanpcmciareset);
+	addethercard("wavelanpci", wavelanpcireset);
+}
--- /dev/null
+++ b/os/pc/fix-pc-build.py
@@ -1,0 +1,29 @@
+#!/usr/bin/env python
+
+import sys
+
+if __name__ == "__main__":
+
+    if len(sys.argv) != 2:
+        sys.stderr.write("Usage: %s <configuration file>\n" % sys.argv[0])
+        sys.stderr.write("Patches the configuration file; e.g. os/pc/pc\n\n")
+        sys.exit(1)
+    
+    config_file = sys.argv[1]
+    
+    try:
+        t = open(config_file).read()
+        t = t.replace("\t/n\n", "\t/n\t/\n")
+        t = t.replace("\t/n/remote\n", "\t/n/remote\t/\n")
+        t = t.replace("wminit", "shell")
+        t = t.replace("consoleprint=0", "consoleprint=1")
+        t += "\t/dis/lib/arg.dis\n"
+        t += "\t/dis/lib/filepat.dis\n"
+        open(config_file, "w").write(t)
+    except IOError:
+        sys.stderr.write("Failed to fix configuration file '%s'.\n" % config_file)
+        sys.exit(1)
+    
+    sys.exit()
+
+
--- /dev/null
+++ b/os/pc/flashif.h
@@ -1,0 +1,82 @@
+typedef struct Flash Flash;
+typedef struct Flashpart Flashpart;
+typedef struct Flashregion Flashregion;
+
+/*
+ * logical partitions
+ */
+enum {
+	Maxflashpart = 8
+};
+
+struct Flashpart {
+	char*	name;
+	ulong	start;
+	ulong	end;
+};
+
+enum {
+	Maxflashregion = 8
+};
+
+/*
+ * physical erase block regions
+ */
+struct Flashregion {
+	int	n;	/* number of blocks in region */
+	ulong	start;	/* physical base address (allowing for banks) */
+	ulong	end;
+	ulong	erasesize;
+};
+
+/*
+ * structure defining a flash memory card
+ */
+struct Flash {
+	QLock;	/* interlock on flash operations */
+	Flash*	next;
+
+	/* the following are filled in by devflash before Flash.reset called */
+	char*	name;
+	void*	addr;
+	ulong	size;
+	int	(*reset)(Flash*);
+
+	/* the following are filled in by the reset routine */
+	int	(*eraseall)(Flash*);
+	int	(*erasezone)(Flash*, int);
+	int	(*write)(Flash*, ulong, void*, long);	/* writes of correct width and alignment */
+	int	(*suspend)(Flash*);
+	int	(*resume)(Flash*);
+
+	/* the following might be filled in by either archflashreset or the reset routine */
+	int	nr;
+	Flashregion	regions[Maxflashregion];
+
+	uchar	id;	/* flash manufacturer ID */
+	uchar	devid;	/* flash device ID */
+	int	width;	/* bytes per flash line */
+	int	erasesize;	/* size of erasable unit (accounting for width) */
+	void*	data;		/* flash type routines' private storage, or nil */
+	ulong	unusable;	/* bit mask of unusable sections */
+	Flashpart	part[Maxflashpart];	/* logical partitions */
+	int	protect;	/* software protection */
+};
+
+/*
+ * called by link routine of driver for specific flash type: arguments are
+ * conventional name for card type/model, and card driver's reset routine.
+ */
+void	addflashcard(char*, int (*)(Flash*));
+
+/*
+ * called by devflash.c:/^flashreset; if flash exists,
+ * sets type, address, and size in bytes of flash
+ * and returns 0; returns -1 if flash doesn't exist
+ */
+int	archflashreset(char*, void**, long*);
+
+/*
+ * enable/disable write protect
+ */
+void	archflashwp(int);
--- /dev/null
+++ b/os/pc/flashzpc.c
@@ -1,0 +1,371 @@
+#include	"u.h"
+#include	"../port/lib.h"
+#include	"mem.h"
+#include	"dat.h"
+#include	"fns.h"
+#include	"io.h"
+#include	"../port/error.h"
+
+#include	"flashif.h"
+
+#define FLASHMEM	0xfff80000
+#define FLASHPGSZ	0x40000
+#define FLASHBKSZ	(FLASHPGSZ>>2)
+#define LOG2FPGSZ	18
+#define FLASHEND	(FLASHMEM+FLASHPGSZ)
+#define SYSREG0	0x78
+#define SYSREG1	0x878
+
+/* Intel28F016SA flash memory family (8SA and (DD)32SA as well) in byte mode */
+
+/*
+  * word mode does not work - a 2 byte write to a location results in the lower address 
+  * byte being unchanged (4 byte writes are even stranger) and no indication of error.
+  * Perhaps the bridge is interfering with the address lines.
+  * Looks like the BIOS code doesn't use it either but that's not certain.
+  */
+
+/*
+  * When port 0x78 bit 2 is set to 1 (flash device 1)
+  * 	0xfff80000-0xfffbffff seems to be free but has dos block headers
+  *	 0xfffc0000-0xfffdffff seems to be the DOS P: drive 
+  *	 0xfffe0000-0xffffffff  is the BIOS
+  * When port 0x78 bit 2 is set to 0 (flash device 0)
+  *	0xfff80000-0xffffffff is a mixture of used and unused DOS blocks and apparently
+  *	many copies of the BIOS
+  *
+  *  In the absence of information from Ziatech and to preserve the BIOS and DOS sections,
+  *  this driver only uses the first range for a total of 8 x 0x40000 = 2Mb
+  */
+
+enum {
+	DQ7 = 0x80,
+	DQ6 = 0x40,
+	DQ5 = 0x20,
+	DQ4 = 0x10,
+	DQ3 = 0x08,
+	DQ2 = 0x04,
+	DQ1 = 0x02,
+	DQ0 = 0x01,
+};
+
+enum {
+	FLRDM = 0xFF,		/* read */
+	FLWTM = 0x10,	/* write/program */
+	FLCLR = 0x50,		/* clear SR */
+	FLBE1 = 0x20,		/* block erase */
+	FLBE2 = 0xD0,		/* block erase */
+	FLRSR = 0x70,		/* read SR */
+	FLDID = 0x90,		/* read id */
+};
+
+#define	DPRINT	if(0)print
+#define	EPRINT	if(1)print
+
+static int
+zpcwait(uchar *p, ulong ticks)
+{
+	uchar csr;
+
+	ticks += m->ticks+1;
+         while((*p & DQ7) != DQ7){
+		sched();
+		if(m->ticks >= ticks){
+			EPRINT("flash: timed out: %8.8lux\n", (ulong)*p);
+			return -1;
+		}
+	}
+	csr = *p;
+	if(csr & (DQ5|DQ4|DQ3)){
+		EPRINT("flash: DQ5 error: %8.8lux %8.8lux\n", p, (ulong)csr);
+		return 0;
+	}
+	return 1;
+}
+
+static int
+eraseall(Flash *f)
+{
+	uchar r;
+	uchar *p;
+	int i, j, s;
+
+	DPRINT("flash: erase all\n");
+	for (i = 0; i < 8; i++) {		/* page */
+		/* set page */
+		r = inb(SYSREG0);
+		r &= 0x8f;
+		r |= i<<4;
+		outb(SYSREG0, r);
+		p = (uchar *)f->addr;
+		for (j = 0; j < 4; j++) {	/* block within page */
+			DPRINT("erasing page %d block %d addr %lux\n", i, j, p);
+			s = splhi();
+			*p = FLBE1;
+			*p = FLBE2;
+			splx(s);
+			if(zpcwait(p, MS2TK(16*1000)) <= 0){
+				*p = FLCLR;	/* clr SR */
+				*p = FLRDM;	/* read mode */
+				f->unusable = ~0;
+				return -1;
+			}
+			*p = FLCLR;
+			*p = FLRDM;
+			p += FLASHPGSZ>>2;
+		}
+	}
+	return 0;
+}
+
+static int
+erasezone(Flash *f, int zone)
+{
+	uchar r;
+	uchar *p;
+	int s, pg, blk;
+
+	DPRINT("flash: erase zone %d\n", zone);
+	if(zone & ~31) {
+		EPRINT("flash: bad erasezone %d\n", zone);
+		return -1;	/* bad zone */
+	}
+	pg = zone>>2;
+	blk = zone&3;
+	/* set page */
+	r = inb(SYSREG0);
+	r &= 0x8f;
+	r |= pg<<4;
+	outb(SYSREG0, r);
+	p = (uchar *)f->addr + blk*(FLASHPGSZ>>2);
+	DPRINT("erasing zone %d pg %d blk %d addr %lux\n", zone, pg, blk, p);
+	s = splhi();
+	*p = FLBE1;
+	*p = FLBE2;
+	splx(s);
+	if(zpcwait(p, MS2TK(8*1000)) <= 0){
+		*p = FLCLR;
+		*p = FLRDM;	/* reset */
+		f->unusable |= 1<<zone;
+		return -1;
+	}
+	*p = FLCLR;
+	*p = FLRDM;
+	return 0;
+}
+
+static int
+readx(Flash *f, ulong offset, void *buf, long n)
+{
+	uchar r;
+	ulong pg, o;
+	long m;
+	uchar *p = buf;
+
+	pg = offset>>LOG2FPGSZ;
+	o = offset&(FLASHPGSZ-1);
+	while (n > 0) {
+		if (pg < 0 || pg > 7) {
+			EPRINT("flash: bad read %ld %ld\n", offset, n);
+			return -1;
+		}
+		/* set page */
+		r = inb(SYSREG0);
+		r &= 0x8f;
+		r |= pg<<4;
+		outb(SYSREG0, r);
+		if (o+n > FLASHPGSZ)
+			m = FLASHPGSZ-o;
+		else
+			m = n;
+		DPRINT("flash: read page %ld offset %lux buf %lux n %ld\n", pg, o, p-(uchar*)buf, m);
+		memmove(p, (uchar *)f->addr + o, m);
+		p += m;
+		n -= m;
+		pg++;
+		o = 0;
+	}
+	return 0;
+}
+
+static int
+writex(Flash *f, ulong offset, void *buf, long n)
+{
+	int i, s;
+	uchar r;
+	ulong pg, o;
+	long m;
+	uchar *a, *v = buf;
+
+	DPRINT("flash: writex\n");
+	pg = offset>>LOG2FPGSZ;
+	o = offset&(FLASHPGSZ-1);
+	while (n > 0) {
+		if (pg < 0 || pg > 7) {
+			EPRINT("flash: bad write %ld %ld\n", offset, n);
+			return -1;
+		}
+		/* set page */
+		r = inb(SYSREG0);
+		r &= 0x8f;
+		r |= pg<<4;
+		outb(SYSREG0, r);
+		if (o+n > FLASHPGSZ)
+			m = FLASHPGSZ-o;
+		else
+			m = n;
+		a = (uchar *)f->addr + o;
+		DPRINT("flash: write page %ld offset %lux buf %lux n %ld\n", pg, o, v-(uchar*)buf, m);
+		for (i = 0; i < m; i++, v++, a++) {
+			if (~*a & *v) {
+				EPRINT("flash: bad write: %lux %lux -> %lux\n", (ulong)a, (ulong)*a, (ulong)*v);
+				return -1;
+			}
+			if (*a == *v)
+				continue;
+			s = splhi();
+			*a = FLWTM;	/* program */
+			*a = *v;
+			splx(s);
+			microdelay(8);
+			if(zpcwait(a, 5) <= 0){
+				*a = FLCLR;	/* clr SR */
+				*a = FLRDM;	/* read mode */
+				f->unusable = ~0;
+				return -1;
+			}
+			*a = FLCLR;
+			*a = FLRDM;
+			if (*a != *v) {
+				EPRINT("flash: write %lux %lux -> %lux failed\n", (ulong)a, (ulong)*a, (ulong)*v);
+				return -1;
+			}
+		}
+		n -= m;
+		pg++;
+		o = 0;
+	}
+	return 0;
+}
+
+#ifdef ZERO
+/* search the whole of flash */
+static void
+flashsearch(Flash *f)
+{
+	int d, m, p, b, n, i;
+	uchar r, buf[64];
+
+	for (d = 0; d < 2; d++) {	/* flash device */
+		r = inb(SYSREG0);
+		r &= 0xfb;
+		r |= (d<<2);
+		outb(SYSREG0, r);
+		for (m = 0; m < 2; m++) {	/* lower/upper mem */
+			if (m == 0)
+				f->addr = (void *)FLASHMEM;
+			else
+				f->addr = (void *)FLASHEND;
+			for (p = 0; p < 8; p++) {	/* page */
+				for (b = 0; b < 4; b++) {	/* block */
+					n = readx(f, (4*p+b)*FLASHBKSZ, buf, 64);
+					if (n != 0) {
+						print("bad read in search %d\n", n);
+						goto end;
+					}
+					print("%d %d %d %d : ", d, m, p, b);
+					if (buf[0] == 0x5a && buf[1] == 0x54) {	/* DOS block */
+						n = 0;
+						for (i = 0; i < 64; i++) {
+							if (buf[i] == 0xff)
+								n++;
+						}
+						if (n == 64-28)
+							print("un");
+						print("used dos\n");
+					}
+					else if (buf[0] == 0x55 && buf[1] == 0xaa)
+						print("bios start\n");
+					else
+						print("bios ?\n");	
+				}
+			}
+		}
+	}
+end:
+	r = inb(SYSREG0);
+	r |= 4;
+	outb(SYSREG0, r);
+	f->addr = (void *)FLASHMEM;	
+}
+#endif
+
+static int
+reset(Flash *f)
+{
+	uchar r;
+	int s;
+	ulong pa;
+	Pcidev *bridge;
+
+	/*  get bridge device */
+	bridge = pcimatch(nil, 0x8086, 0x7000);	/* Intel PIIX3 ISA bridge device */
+	if (bridge == nil) {
+		EPRINT("flash : failed to find bridge device\n");
+		return 1;
+	}
+	/* enable extended BIOS and read/write */
+	s = splhi();
+	r = pcicfgr8(bridge, 0x4e);
+	r |= 0x84;
+	pcicfgw8(bridge, 0x4e, r);
+	splx(s);
+	/* set system register bits */
+	r = inb(SYSREG0);
+	r |= 0x86;	/* chip enable, non-BIOS part, set r/w */
+	outb(SYSREG0, r);
+	/*
+	  * might have to grab memory starting at PADDR(FLASHMEM) ie 0x7ff80000
+	  * because if this is mapped via virtual address FLASHMEM we would get a
+	  * a kernel panic in mmukmap().
+	  *	va = 0xfff80000 pa = 0xfff80000 for flash
+	  *     va = 0xfff80000 pa = 0x7ff80000 if lower memory grabbed by anything
+	  */
+	/* 
+	  * upafree(FLASHMEM, FLASHPGSZ);
+	  * pa = upamalloc(FLASHMEM, FLASHPGSZ, 0);
+	  * if (pa != FLASHMEM)
+	  *	error
+	  */
+	pa = mmukmap(FLASHMEM, FLASHMEM, FLASHPGSZ);
+	if (pa != FLASHEND) {
+		EPRINT("failed to map flash memory");
+		return 1;
+	}
+/*
+	pa = mmukmap(FLASHEND, FLASHEND, FLASHPGSZ);
+	if (pa != 0) {
+		EPRINT("failed to map flash memory");
+		return 1;
+	}
+*/
+	f->id = 0x0089;	/* can't use autoselect: might be running in flash */
+	f->devid = 0x66a0;
+	f->read = readx;
+	f->write = writex;
+	f->eraseall = eraseall;
+	f->erasezone = erasezone;
+	f->suspend = nil;
+	f->resume = nil;
+	f->width = 1;				/* must be 1 since devflash.c must not read directly */
+	f->erasesize = 64*1024;
+	*(uchar*)f->addr = FLCLR;	/* clear status registers */
+	*(uchar*)f->addr = FLRDM;	/* reset to read mode */
+	return 0;
+}
+
+void
+flashzpclink(void)
+{
+	addflashcard("DD28F032SA", reset);
+}
--- /dev/null
+++ b/os/pc/floppy.h
@@ -1,0 +1,183 @@
+typedef	struct FController FController;
+typedef	struct FDrive FDrive;
+typedef struct FType FType;
+
+static void floppyintr(Ureg*);
+static int floppyon(FDrive*);
+static void floppyoff(FDrive*);
+static void floppysetdef(FDrive*);
+
+/*
+ *  a floppy drive
+ */
+struct FDrive
+{
+	FType	*t;		/* floppy type */
+	int	dt;		/* drive type */
+	int	dev;
+
+	ulong	lasttouched;	/* time last touched */
+	int	cyl;		/* current arm position */
+	int	confused;	/* needs to be recalibrated */
+	int	vers;
+	int	maxtries;	/* max read attempts before Eio */
+
+	int	tcyl;		/* target cylinder */
+	int	thead;		/* target head */
+	int	tsec;		/* target sector */
+	long	len;		/* size of xfer */
+
+	uchar	*cache;		/* track cache */
+	int	ccyl;
+	int	chead;
+};
+
+/*
+ *  controller for 4 floppys
+ */
+struct FController
+{
+	QLock;			/* exclusive access to the contoller */
+
+	int	ndrive;
+	FDrive	*d;		/* the floppy drives */
+	FDrive	*selected;
+	int	rate;		/* current rate selected */
+	uchar	cmd[14];	/* command */
+	int	ncmd;		/* # command bytes */
+	uchar	stat[14];	/* command status */
+	int	nstat;		/* # status bytes */
+	int	confused;	/* controler needs to be reset */
+	Rendez	r;		/* wait here for command termination */
+	int	motor;		/* bit mask of spinning disks */
+};
+
+/*
+ *  floppy types (all MFM encoding)
+ */
+struct FType
+{
+	char	*name;
+	int	dt;		/* compatible drive type */
+	int	bytes;		/* bytes/sector */
+	int	sectors;	/* sectors/track */
+	int	heads;		/* number of heads */
+	int	steps;		/* steps per cylinder */
+	int	tracks;		/* tracks/disk */
+	int	gpl;		/* intersector gap length for read/write */	
+	int	fgpl;		/* intersector gap length for format */
+	int	rate;		/* rate code */
+
+	/*
+	 *  these depend on previous entries and are set filled in
+	 *  by floppyinit
+	 */
+	int	bcode;		/* coded version of bytes for the controller */
+	long	cap;		/* drive capacity in bytes */
+	long	tsize;		/* track size in bytes */
+};
+/* bits in the registers */
+enum
+{
+	/* status registers a & b */
+	Psra=		0x3f0,
+	Psrb=		0x3f1,
+
+	/* digital output register */
+	Pdor=		0x3f2,
+	Fintena=	0x8,	/* enable floppy interrupt */
+	Fena=		0x4,	/* 0 == reset controller */
+
+	/* main status register */
+	Pmsr=		0x3f4,
+	Fready=		0x80,	/* ready to be touched */
+	Ffrom=		0x40,	/* data from controller */
+	Ffloppybusy=	0x10,	/* operation not over */
+
+	/* data register */
+	Pfdata=		0x3f5,
+	Frecal=		0x07,	/* recalibrate cmd */
+	Fseek=		0x0f,	/* seek cmd */
+	Fsense=		0x08,	/* sense cmd */
+	Fread=		0x66,	/* read cmd */
+	Freadid=	0x4a,	/* read track id */
+	Fspec=		0x03,	/* set hold times */
+	Fwrite=		0x45,	/* write cmd */
+	Fformat=	0x4d,	/* format cmd */
+	Fmulti=		0x80,	/* or'd with Fread or Fwrite for multi-head */
+	Fdumpreg=	0x0e,	/* dump internal registers */
+
+	/* digital input register */
+	Pdir=		0x3F7,	/* disk changed port (read only) */
+	Pdsr=		0x3F7,	/* data rate select port (write only) */
+	Fchange=	0x80,	/* disk has changed */
+
+	/* status 0 byte */
+	Drivemask=	3<<0,
+	Seekend=	1<<5,
+	Codemask=	(3<<6)|(3<<3),
+	Cmdexec=	1<<6,
+
+	/* status 1 byte */
+	Overrun=	0x10,
+};
+
+
+static void
+pcfloppyintr(Ureg *ur, void *a)
+{
+	USED(a);
+
+	floppyintr(ur);
+}
+
+void
+floppysetup0(FController *fl)
+{
+	fl->ndrive = 0;
+	if(ioalloc(Psra, 6, 0, "floppy") < 0)
+		return;
+	if(ioalloc(Pdir, 1, 0, "floppy") < 0){
+		iofree(Psra);
+		return;
+	}
+	fl->ndrive = 2;
+}
+
+void
+floppysetup1(FController *fl)
+{
+	uchar equip;
+
+	/*
+	 *  read nvram for types of floppies 0 & 1
+	 */
+	equip = nvramread(0x10);
+	if(fl->ndrive > 0){
+		fl->d[0].dt = (equip >> 4) & 0xf;
+		floppysetdef(&fl->d[0]);
+	}
+	if(fl->ndrive > 1){
+		fl->d[1].dt = equip & 0xf;
+		floppysetdef(&fl->d[1]);
+	}
+	intrenable(IrqFLOPPY, pcfloppyintr, fl, BUSUNKNOWN, "floppy");
+}
+
+/*
+ *  eject disk ( unknown on safari )
+ */
+void
+floppyeject(FDrive *dp)
+{
+	floppyon(dp);
+	dp->vers++;
+	floppyoff(dp);
+}
+
+int 
+floppyexec(char *a, long b, int c)
+{
+	USED(a, b, c);
+	return b;
+}
--- /dev/null
+++ b/os/pc/fns.h
@@ -1,0 +1,165 @@
+#include "../port/portfns.h"
+void	aamloop(int);
+Dirtab*	addarchfile(char*, int, long(*)(Chan*,void*,long,vlong), long(*)(Chan*,void*,long,vlong));
+void	archinit(void);
+void	bootargs(ulong);
+int	cistrcmp(char*, char*);
+int	cistrncmp(char*, char*, int);
+#define	clearmmucache()				/* x86 doesn't have one */
+void	clockintr(Ureg*, void*);
+void	(*coherence)(void);
+void	cpuid(char*, int*, int*);
+int	cpuidentify(void);
+void	cpuidprint(void);
+void	(*cycles)(uvlong*);
+void	delay(int);
+int	dmacount(int);
+int	dmadone(int);
+void	dmaend(int);
+int	dmainit(int, int);
+long	dmasetup(int, void*, long, int);
+void	dumpregs(Ureg*);
+#define	evenaddr(x)				/* x86 doesn't care */
+void	fpinit(void);
+void	fpoff(void);
+void	fprestore(FPU*);
+void	fpsave(FPU*);
+ulong	fpstatus(void);
+ulong	getcr0(void);
+ulong	getcr2(void);
+ulong	getcr3(void);
+ulong	getcr4(void);
+char*	getconf(char*);
+void	guesscpuhz(int);
+int	i8042auxcmd(int);
+int	i8042auxcmdval(int);
+void	i8042auxenable(void (*)(int, int));
+int i8042auxdetect(void);
+void	i8042reset(void);
+void	i8250console(void);
+void	i8253enable(void);
+void	i8253init(void);
+void	i8253link(void);
+uvlong	i8253read(uvlong*);
+void	i8253timerset(uvlong);
+void	i8259init(void);
+int	i8259isr(int);
+int	i8259enable(Vctl*);
+int	i8259vecno(int);
+int	i8259disable(int);
+void	idle(void);
+void	idlehands(void);
+int	inb(int);
+void	insb(int, void*, int);
+ushort	ins(int);
+void	inss(int, void*, int);
+ulong	inl(int);
+void	insl(int, void*, int);
+int	intrdisable(int, void (*)(Ureg *, void *), void*, int, char*);
+void	intrenable(int, void (*)(Ureg*, void*), void*, int, char*);
+void	iofree(int);
+void	ioinit(void);
+int	iounused(int, int);
+int	ioalloc(int, int, int, char*);
+int	ioreserve(int, int, int, char*);
+int	iprint(char*, ...);
+int	isaconfig(char*, int, ISAConf*);
+int	isvalid_va(void*);
+void	kbdenable(void);
+void	kbdinit(void);
+void	kdbenable(void);
+#define	kmapinval()
+void	lapicclock(Ureg*, void*);
+void	lapictimerset(uvlong);
+void	lgdt(ushort[3]);
+void	lidt(ushort[3]);
+void	links(void);
+void	ltr(ulong);
+void	mach0init(void);
+void	machinit(void);
+void	mathinit(void);
+void	mb386(void);
+void	mb586(void);
+void	meminit(ulong);
+#define mmuflushtlb(pdb) putcr3(pdb)
+void	mmuinit(void);
+ulong	mmukmap(ulong, ulong, int);
+int	mmukmapsync(ulong);
+ulong*	mmuwalk(ulong*, ulong, int, int);
+uchar	nvramread(int);
+void	nvramwrite(int, uchar);
+void	outb(int, int);
+void	outsb(int, void*, int);
+void	outs(int, ushort);
+void	outss(int, void*, int);
+void	outl(int, ulong);
+void	outsl(int, void*, int);
+int	pciscan(int, Pcidev**);
+ulong	pcibarsize(Pcidev*, int);
+int	pcicfgr8(Pcidev*, int);
+int	pcicfgr16(Pcidev*, int);
+int	pcicfgr32(Pcidev*, int);
+void	pcicfgw8(Pcidev*, int, int);
+void	pcicfgw16(Pcidev*, int, int);
+void	pcicfgw32(Pcidev*, int, int);
+void	pciclrbme(Pcidev*);
+void	pciclrioe(Pcidev*);
+void	pciclrmwi(Pcidev*);
+int	pcigetpms(Pcidev*);
+void	pcihinv(Pcidev*);
+uchar	pciipin(Pcidev*, uchar);
+Pcidev* pcimatch(Pcidev*, int, int);
+Pcidev* pcimatchtbdf(int);
+void	pcireset(void);
+void	pcisetbme(Pcidev*);
+void	pcisetioe(Pcidev*);
+int	pcisetpms(Pcidev*, int);
+void	pcmcisread(PCMslot*);
+int	pcmcistuple(int, int, int, void*, int);
+PCMmap*	pcmmap(int, ulong, int, int);
+int	pcmspecial(char*, ISAConf*);
+int	(*_pcmspecial)(char *, ISAConf *);
+void	pcmspecialclose(int);
+void	(*_pcmspecialclose)(int);
+void	pcmunmap(int, PCMmap*);
+void	poolinit(void);
+void	poolsizeinit(void);
+void	procsave(Proc*);
+void	procsetup(Proc*);
+void	putcr3(ulong);
+void	putcr4(ulong);
+void	rdmsr(int, vlong*);
+ulong rdtsc32(void);
+void	screeninit(void);
+int	screenprint(char*, ...);			/* debugging */
+void	(*screenputs)(char*, int);
+int	segflush(void*, ulong);
+void	syncclock(void);
+uvlong	tscticks(uvlong*);
+void	trapenable(int, void (*)(Ureg*, void*), void*, char*);
+void	trapinit(void);
+ulong	umbmalloc(ulong, int, int);
+void	umbfree(ulong, int);
+ulong	umbrwmalloc(ulong, int, int);
+void	umbrwfree(ulong, int);
+ulong	upamalloc(ulong, int, int);
+void	upafree(ulong, int);
+void	upareserve(ulong, int);
+void	vectortable(void);
+void*	vmap(ulong, int);
+void	vunmap(void*, int);
+void	wrmsr(ulong, ulong);
+int	xchgw(ushort*, int);
+ulong	kzeromap(ulong, ulong, int);
+void	nmiscreen(void);
+int	kbdinready(void);
+
+#define	waserror()	(up->nerrlab++, setlabel(&up->errlab[up->nerrlab-1]))
+#define getcallerpc(x)	(((ulong*)(x))[-1])
+#define KADDR(a)	((void*)((ulong)(a)|KZERO))
+#define PADDR(a)	((ulong)(a)&~KZERO)
+
+#define	dcflush(a, b)
+#define	clockcheck();
+#define 	dumplongs(x, y, z)
+#define 	setpanic()
--- /dev/null
+++ b/os/pc/fpi.h
@@ -1,0 +1,61 @@
+typedef int Word;
+typedef unsigned long Single;
+typedef struct {
+	unsigned long l;
+	unsigned long h;
+} Double;
+
+enum {
+	FractBits	= 28,
+	CarryBit	= 0x10000000,
+	HiddenBit	= 0x08000000,
+	MsBit		= HiddenBit,
+	NGuardBits	= 3,
+	GuardMask	= 0x07,
+	LsBit		= (1<<NGuardBits),
+
+	SingleExpBias	= 127,
+	SingleExpMax	= 255,
+	DoubleExpBias	= 1023,
+	DoubleExpMax	= 2047,
+
+	ExpBias		= DoubleExpBias,
+	ExpInfinity	= DoubleExpMax,
+};
+
+typedef struct {
+	unsigned char s;
+	short e;
+	long l;				/* 0000FFFFFFFFFFFFFFFFFFFFFFFFFGGG */
+	long h;				/* 0000HFFFFFFFFFFFFFFFFFFFFFFFFFFF */
+} Internal;
+
+#define IsWeird(n)	((n)->e >= ExpInfinity)
+#define	IsInfinity(n)	(IsWeird(n) && (n)->h == HiddenBit && (n)->l == 0)
+#define	SetInfinity(n)	((n)->e = ExpInfinity, (n)->h = HiddenBit, (n)->l = 0)
+#define IsNaN(n)	(IsWeird(n) && (((n)->h & ~HiddenBit) || (n)->l))
+#define	SetQNaN(n)	((n)->s = 0, (n)->e = ExpInfinity, 		\
+			 (n)->h = HiddenBit|(LsBit<<1), (n)->l = 0)
+#define IsZero(n)	((n)->e == 1 && (n)->h == 0 && (n)->l == 0)
+#define SetZero(n)	((n)->e = 1, (n)->h = 0, (n)->l = 0)
+
+/*
+ * fpi.c
+ */
+extern void fpiround(Internal *);
+extern void fpiadd(Internal *, Internal *, Internal *);
+extern void fpisub(Internal *, Internal *, Internal *);
+extern void fpimul(Internal *, Internal *, Internal *);
+extern void fpidiv(Internal *, Internal *, Internal *);
+extern int fpicmp(Internal *, Internal *);
+extern void fpinormalise(Internal*);
+
+/*
+ * fpimem.c
+ */
+extern void fpis2i(Internal *, void *);
+extern void fpid2i(Internal *, void *);
+extern void fpiw2i(Internal *, void *);
+extern void fpii2s(void *, Internal *);
+extern void fpii2d(void *, Internal *);
+extern void fpii2w(Word *, Internal *);
--- /dev/null
+++ b/os/pc/fpi387.c
@@ -1,0 +1,742 @@
+/*
+ * Copyright © 1999 Vita Nuova Limited
+ *
+ * this doesn't attempt to implement 387 floating-point properties
+ * that aren't visible in the Inferno environment.  in particular,
+ * all arithmetic is done in double precision, not extended precision.
+ * furthermore, the FP trap status isn't updated.
+ */
+
+#ifdef TEST
+#include <u.h>
+#include <libc.h>
+#include <ureg.h>
+#include "fpi.h"
+#include "tst.h"
+#else
+#include <u.h>
+#include	"ureg.h"
+#include "fpi.h"
+#include	"../port/lib.h"
+#include	"mem.h"
+#include	"dat.h"
+#include	"fns.h"
+#include	"io.h"
+#endif
+
+#define	fabs	Fabs
+
+typedef struct FPI FPI;
+
+struct FPI {
+	char*	name;
+	void	(*f)(Ureg*, int, void*, Internal*, Internal*);
+	int	dstf;
+};
+
+enum {
+	RndNearest = 0,
+	RndDown,
+	RndUp,
+	Rnd0,
+
+	C0 = 1<<8,
+	C1 = 1<<9,
+	C2 = 1<<10,
+	C3 = 1<<14,
+};
+
+
+int	fpemudebug = 0;
+
+static Internal fpconst[7] = {	/* indexed by op&7 */
+	/* s, e, l, h */
+	{0, 0x3FF, 0x00000000, 0x08000000},	/* 1 */
+	{0, 0x400, 0x0BCD1B8A, 0x0D49A784}, /* l2t */
+	{0, 0x3FF, 0x095C17F0, 0x0B8AA3B2}, /* l2e */
+	{0, 0x400, 0x022168C2, 0x0C90FDAA},	/* pi */
+	{0, 0x3FD, 0x04FBCFF7, 0x09A209A8}, /* lg2 */
+	{0, 0x3FE, 0x07D1CF79, 0x0B17217F}, /* ln2 */
+	{0, 0x1, 0x00000000, 0x00000000}, /* z */
+};
+
+static Internal *fpstk(int i);
+#define ST(x) (*fpstk((x)))
+
+#define	I387	(up->env->fpu)
+
+/* BUG: check fetch (not worthwhile in Inferno) */
+#define	getubyte(a) (*(uchar*)(a))
+#define	getuword(a) (*(ushort*)(a))
+#define	getulong(a) (*(ulong*)(a))
+
+static void
+popfp(void)
+{
+	ushort *s;
+
+	s = &I387.status;
+	*s = (*s & ~0x3800) | ((*s + 0x0800) & 0x3800);
+}
+
+static void
+pushfp(void)
+{
+	ushort *s;
+
+	s = &I387.status;
+	*s = (*s & ~0x3800) | ((*s + 0x3800) & 0x3800);
+}
+
+static Internal *
+fpstk(int i)
+{
+	return (Internal*)I387.istack[(i+(I387.status>>11))&7];
+}
+
+static void
+fldc(Ureg*, int op, void*, Internal*, Internal *d)
+{
+	*d = fpconst[op&7];
+}
+
+static void
+fabs(Ureg*, int, void*, Internal*, Internal *d)
+{
+	d->s = 0;
+}
+
+static void
+fchs(Ureg*, int, void*, Internal*, Internal *d)
+{
+	d->s ^= 1;
+}
+
+static void
+fadd(Ureg*, int, void*, Internal *s, Internal *d)
+{
+	Internal l, r;
+
+	l = *s;
+	r = *d;
+	(l.s == r.s? fpiadd: fpisub)(&l, &r, d);
+}
+
+static void
+fsub(Ureg*, int, void*, Internal *s, Internal *d)
+{
+	Internal l, r;
+
+	l = *s;
+	r = *d;
+	l.s ^= 1;
+	(l.s == r.s? fpiadd: fpisub)(&l, &r, d);
+}
+
+static void
+fsubr(Ureg*, int, void*, Internal *s, Internal *d)
+{
+	Internal l, r;
+
+	l = *s;
+	r = *d;
+	r.s ^= 1;
+	(l.s == r.s? fpiadd: fpisub)(&r, &l, d);
+}
+
+static void
+fmul(Ureg*, int, void*, Internal *s, Internal *d)
+{
+	Internal l, r;
+
+	l = *s;
+	r = *d;
+	fpimul(&l, &r, d);
+}
+
+static void
+fdiv(Ureg*, int, void*, Internal *s, Internal *d)
+{
+	Internal l, r;
+
+	l = *s;
+	r = *d;
+	fpidiv(&l, &r, d);
+}
+
+static void
+fdivr(Ureg*, int, void*, Internal *s, Internal *d)
+{
+	Internal l, r;
+
+	l = *s;
+	r = *d;
+	fpidiv(&r, &l, d);
+}
+
+static void
+fcom(Ureg*, int, void*, Internal *s, Internal *d)
+{
+	int i;
+	ushort *p;
+
+	p = &I387.status;
+	if(IsWeird(s) || IsWeird(d)){
+		*p |= C0|C2|C3;
+		/* BUG: should trap if not masked */
+		return;
+	}
+	*p &= ~(C0|C2|C3);
+	i = fpicmp(d, s);
+	if(i < 0)
+		*p |= C0;
+	else if(i == 0)
+		*p |= C3;
+}
+
+static void
+fpush(Ureg*, int op, void*, Internal*, Internal*)
+{
+	Internal *p;
+
+	p = &ST(op & 7);
+	pushfp();
+	ST(0) = *p;
+}
+
+static void
+fmov(Ureg*, int, void*, Internal *s, Internal *d)
+{
+	*d = *s;
+}
+
+static void
+fmovr(Ureg*, int, void*, Internal *s, Internal *d)
+{
+	*s = *d;
+}
+
+static void
+fxch(Ureg*, int, void*, Internal *s, Internal *d)
+{
+	Internal t;
+
+	t = *s; *s = *d; *d = t;
+}
+
+static void
+frstor(Ureg*, int, void *s, Internal*, Internal*)
+{
+	validaddr(s, 108, 0);
+	memmove(&I387, s, 108);
+}
+
+static void
+fsave(Ureg*, int, void *d, Internal*, Internal*)
+{
+	validaddr(d, 108, 1);
+	memmove(d, &I387, 108);
+	I387.control = 0x037F;
+	I387.status = 0;
+	I387.tag = 0;
+}
+
+static void
+fstsw(Ureg*, int, void *d, Internal*, Internal*)
+{
+	validaddr(d, 2, 1);
+	*(short*)d = I387.status;
+}
+
+static void
+fldenv(Ureg*, int, void *s, Internal*, Internal*)
+{
+	validaddr(s, 28, 0);
+	memmove(&I387, s, 28);
+}
+
+static void
+fldcw(Ureg*, int, void *s, Internal*, Internal*)
+{
+	validaddr(s, 2, 0);
+	I387.control = *(short*)s;
+}
+
+static void
+fstenv(Ureg*, int, void *d, Internal*, Internal*)
+{
+	validaddr(d, 4*7, 1);
+	memmove(d, &I387, 4*7);
+}
+
+static void
+fstcw(Ureg*, int, void *d, Internal*, Internal*)
+{
+	validaddr(d, 2, 1);
+	*(short*)d = I387.control;
+}
+
+static void
+fincstp(Ureg*, int, void*, Internal*, Internal*)
+{
+	popfp();
+}
+
+static void
+fdecstp(Ureg*, int, void*, Internal*, Internal*)
+{
+	pushfp();
+}
+
+static void
+fscale(Ureg*, int, void*, Internal *s, Internal *d) 
+{
+	Word w;
+
+	fpii2w(&w, s);	/* should truncate towards zero ... */
+	d->e += w;
+}
+
+static void
+fstswax(Ureg *ur, int, void*, Internal*, Internal*)
+{
+	ur->ax = (ur->ax & ~0xFFFF) | (I387.status & 0xFFFF);
+}
+
+static void
+ftst(Ureg*, int, void*, Internal*, Internal *d)
+{
+	ushort *p;
+
+	p = &I387.status;
+	if(IsWeird(d)){
+		*p |= C0|C2|C3;
+		return;
+	}
+	*p &= ~(C0|C2|C3);
+	fpinormalise(d);
+	if(IsZero(d))
+		*p |= C3;
+	else if(d->s)
+		*p |=C0;
+}
+
+static void
+frndint(Ureg*, int, void*, Internal*, Internal *d)
+{
+	fpiround(d);	/* BUG: doesn't look at rounding mode */
+}
+
+static void
+fnop(Ureg*, int, void*, Internal*, Internal*)
+{
+}
+
+enum {
+	Fpop1= 1<<0,
+	Fpop2 = 1<<1,
+	Fload = 1<<2,
+};
+
+/*
+ * %e	-	effective address - Mod R/M value
+ * %f	-	floating point register F0-F7 - from Mod R/M register
+ */
+
+static void fload(Ureg*, int, void*, Internal*, Internal*);
+static void fstore(Ureg*, int, void*, Internal*, Internal*);
+
+#define	X(a,b) (((a)<<2)|(b))
+
+static	FPI	optab1[4][4] = {	/* normal mod r/m operand */
+[0]	{
+	[0]	{"FLDENV %e", fldenv, 0},
+	[1]	{"FLDCW %e", fldcw, 0},
+	[2]	{"FSTENV %e", fstenv, 0},
+	[3]	{"FSTCW %e", fstcw, 0},
+	},
+[1]	{
+	[1]	{"FMOVX %e,F0", nil, Fload},
+	[3]	{"FMOVXP F0,%e", nil, Fpop1},
+	},
+[2]	{
+	[0]	{"FRSTOR %e", frstor, 0},
+	[2]	{"FSAVE %e", fsave, 0},
+	[3]	{"FSTSW %e", fstsw, 0},
+	},
+[3]	{
+	[0]	{"FMOVB %e", nil, 0},
+	[1]	{"FMOVV %e,F0", nil, Fload},
+	[2]	{"FMOVBP %e", nil, Fpop1},
+	[3]	{"FMOVVP F0,%e", nil, Fpop1},
+	},
+};
+
+#undef X
+
+static	FPI	optab2a[1<<3] = {	/* A=0 */
+[0]	{"FADDx %e,F0", fadd, 0},
+[1]	{"FMULx %e,F0", fmul, 0},
+[2]	{"FCOMx %e,F0", fcom, 0},
+[3]	{"FCOMxP %e,F0", fcom, Fpop1},
+[4]	{"FSUBx %e,F0", fsub, 0},
+[5]	{"FSUBRx %e,F0", fsubr, 0},	/* ?? */
+[6]	{"FDIVx %e,F0", fdiv, 0},
+[7]	{"FDIVRx %e,F0", fdivr, 0},	/* ?? */
+};
+
+static	FPI	optab2b[1<<2] = {	/* A=1, B=0,2,3 */
+[0]	{"FMOVx %e,F0", fload, Fload},
+[2]	{"FMOVx F0,%e", fstore, 0},
+[3]	{"FMOVxP F0,%e", fstore, Fpop1},
+};
+
+#define	X(d,P,B) ((d<<4)|(P<<3)|B)
+
+static	FPI	optab3a[1<<5] = {	/* A=0 */
+[X(0,0,0)]	{"FADDD	%f,F0", fadd, 0},
+[X(1,0,0)]	{"FADDD	F0,%f", fadd, 0},
+[X(1,1,0)]	{"FADDDP	F0,%f", fadd, Fpop1},
+[X(0,0,1)]	{"FMULD	%f,F0", fmul, 0},
+[X(1,0,1)]	{"FMULD	F0,%f", fmul, 0},
+[X(1,1,1)]	{"FMULDP	F0,%f", fmul, Fpop1},
+[X(0,0,2)]	{"FCOMD	%f,F0", fcom, 0},
+[X(0,0,3)]	{"FCOMDP	%f,F0", fcom, Fpop1},
+[X(1,1,3)]	{"FCOMDPP", fcom, Fpop1|Fpop2},
+[X(0,0,4)]	{"FSUBD	%f,F0", fsub, 0},
+[X(1,0,4)]	{"FSUBRD	F0,%f", fsubr, 0},
+[X(1,1,4)]	{"FSUBRDP F0,%f", fsubr, Fpop1},
+[X(0,0,5)]	{"FSUBRD	%f,F0", fsubr, 0},
+[X(1,0,5)]	{"FSUBD	F0,%f", fsub, 0},
+[X(1,1,5)]	{"FSUBDP	F0,%f", fsub, Fpop1},
+[X(0,1,5)]	{"FUCOMPP", fcom, Fpop1|Fpop2},
+[X(0,0,6)]	{"FDIVD	%f,F0", fdiv, 0},
+[X(1,0,6)]	{"FDIVRD	F0,%f", fdivr, 0},
+[X(1,1,6)]	{"FDIVRDP F0,%f", fdivr, Fpop1},
+[X(0,0,7)]	{"FDIVRD	%f,F0", fdivr, 0},
+[X(1,0,7)]	{"FDIVD	F0,%f", fdiv, 0},
+[X(1,1,7)]	{"FDIVDP	F0,%f", fdiv, Fpop1},
+};
+
+static	FPI	optab3b[1<<5] = {	/* A=1 */
+[X(0,0,0)]	{"FMOVD	%f,F0", fmov, Fload},
+[X(0,0,1)]	{"FXCHD	%f,F0", fxch, 0},
+[X(0,0,2)]	{"FNOP", fnop, 0},	/* F0 only */
+[X(1,0,0)]	{"FFREED	%f", fnop, 0},
+[X(1,0,2)]	{"FMOVD	F0,%f", fmovr, 0},
+[X(1,0,3)]	{"FMOVDP	F0,%f", fmovr, Fpop1},
+[X(1,1,4)]	{"FSTSW	AX", fstswax, 0},
+[X(1,0,4)]	{"FUCOMD	%f,F0", fcom, 0},
+[X(1,0,5)]	{"FUCOMDP %f,F0", fcom, Fpop1},
+};
+
+#undef X
+
+static	FPI	optab4[1<<6] = {
+[0x00]	{"FCHS", fchs, 0},
+[0x01]	{"FABS", fabs, 0},
+[0x04]	{"FTST", ftst, 0},
+[0x05]	{"FXAM", nil, 0},
+[0x08]	{"FLD1", fldc, Fload},
+[0x09]	{"FLDL2T", fldc, Fload},
+[0x0a]	{"FLDL2E", fldc, Fload},
+[0x0b]	{"FLDPI", fldc, Fload},
+[0x0c]	{"FLDLG2", fldc, Fload},
+[0x0d]	{"FLDLN2", fldc, Fload},
+[0x0e]	{"FLDZ", fldc, Fload},
+[0x10]	{"F2XM1", nil, 0},
+[0x11]	{"FYL2X", nil, 0},
+[0x12]	{"FPTAN", nil, 0},
+[0x13]	{"FPATAN", nil, 0},
+[0x14]	{"FXTRACT", nil, 0},
+[0x15]	{"FPREM1", nil, 0},
+[0x16]	{"FDECSTP", fdecstp, 0},
+[0x17]	{"FINCSTP", fincstp, 0},
+[0x18]	{"FPREM", nil, 0},
+[0x19]	{"FYL2XP1", nil, 0},
+[0x1a]	{"FSQRT", nil, 0},
+[0x1b]	{"FSINCOS", nil, 0},
+[0x1c]	{"FRNDINT", frndint, 0},
+[0x1d]	{"FSCALE", fscale, 0},
+[0x1e]	{"FSIN", nil, 0},
+[0x1f]	{"FCOS", nil, 0},
+};
+
+static void
+loadr32(void *s, Internal *d)
+{
+	validaddr(s, 4, 0);
+	fpis2i(d, s);
+}
+
+static void
+loadi32(void *s, Internal *d)
+{
+	validaddr(s, 4, 0);
+	fpiw2i(d, s);
+}
+
+static void
+loadr64(void *s, Internal *d)
+{
+	validaddr(s, 8, 0);
+	fpid2i(d, s);
+}
+
+static void
+loadi16(void *s, Internal *d)
+{
+	Word w;
+
+	validaddr(s, 2, 0);
+	w = *(short*)s;
+	fpiw2i(d, &w);
+}
+
+static	void	(*loadf[4])(void*, Internal*) ={
+	loadr32, loadi32, loadr64, loadi16
+};
+
+static void
+storer32(Internal s, void *d)
+{
+	validaddr(d, 4, 1);
+	fpii2s(d, &s);
+}
+
+static void
+storei32(Internal s, void *d)
+{
+	validaddr(d, 4, 1);
+	fpii2w(d, &s);
+}
+
+static void
+storer64(Internal s, void *d)
+{
+	validaddr(d, 8, 1);
+	fpii2d(d, &s);
+}
+
+static void
+storei16(Internal s, void *d)
+{
+	Word w;
+
+	validaddr(d, 2, 1);
+	fpii2w(&w, &s);
+	if((short)w != w)
+		;	/* overflow */
+	*(short*)d = w;
+}
+
+static	void	(*storef[4])(Internal, void*) ={
+	storer32, storei32, storer64, storei16
+};
+
+static void
+fload(Ureg*, int op, void *mem, Internal*, Internal *d)
+{
+	(*loadf[(op>>9)&3])(mem, d);
+}
+
+static void
+fstore(Ureg*, int op, void *mem, Internal *s, Internal*)
+{
+	(*storef[(op>>9)&3])(*s, mem);
+}
+
+#define	REG(x) (*(ulong*)(((char*)ur)+roff[(x)]))
+
+static	int	roff[] = {
+	offsetof(Ureg, ax),
+	offsetof(Ureg, cx),
+	offsetof(Ureg, dx),
+	offsetof(Ureg, bx),
+	offsetof(Ureg, ecode),	/* ksp */
+	offsetof(Ureg, bp),
+	offsetof(Ureg, si),
+	offsetof(Ureg, di),
+};
+
+static long
+getdisp(Ureg *ur, int mod, int rm)
+{
+	uchar c;
+	long disp;
+
+	if(mod > 2)
+		return 0;
+	disp = 0;
+	if(mod == 1) {
+		c = getubyte(ur->pc++);
+		if(c&0x80)
+			disp = c|(~0<<8);
+		else
+			disp = c;
+	} else if(mod == 2 || rm == 5) {
+		disp = getulong(ur->pc);
+		ur->pc += 4;
+	}
+	if(mod || rm != 5)
+		disp += REG(rm);	/* base */
+	return disp;
+}
+
+static ulong
+modrm(Ureg *ur, uchar c)
+{
+	uchar rm, mod;
+	int reg;
+	ulong base;
+
+	mod = (c>>6)&3;
+	rm = c&7;
+	if(mod == 3)	/* register */
+		error("sys: fpemu: invalid addr mode");
+	/* no 16-bit mode */
+	if(rm == 4) {	/* scummy sib byte */
+		c = getubyte(ur->pc++);
+		reg = (c>>3)&0x07;	/* index */
+		base = getdisp(ur, mod, c&7);
+		if(reg != 4)
+			base += (REG(reg) << (c>>6));	/* index */
+		if(fpemudebug>1)
+			print("ur=#%lux sib=#%x reg=%d mod=%d base=%d basev=#%lux sp=%lux\n", ur, c, reg, mod, c&7, base, ur->usp);
+		return base;
+	}
+	if(rm == 5 && mod == 0){
+		ur->pc += 4;
+		return getulong(ur->pc-4);
+	}
+	return getdisp(ur, mod, rm);
+}
+
+static void *
+ea(Ureg *ur, uchar op)
+{
+	ulong addr;
+
+	addr = modrm(ur, op);
+	I387.operand = addr;
+	if(fpemudebug>1)
+		print("EA=#%lux\n", addr);
+	return (void*)addr;
+}
+
+void
+fpi387(Ureg *ur)
+{
+	int op, i;
+	ulong pc;
+	FPenv *ufp;
+	FPI *fp;
+	Internal tmp, *s, *d;
+	void *mem;
+	char buf[60];
+
+	ur->ecode = (ulong)&ur->sp;	/* BUG: TEMPORARY compensation for incorrect Ureg for kernel mode */
+	ufp = &up->env->fpu;	/* because all the state is in Osenv, it need not be saved/restored */
+	if(ufp->fpistate != FPACTIVE) {
+		ufp->fpistate = FPACTIVE;
+		ufp->control = 0x037f;
+		ufp->status = 0;
+		ufp->tag = 0;
+		ufp->oselector = 0x17;
+	}
+	while((op = getubyte(ur->pc)) >= 0xd8 && op <= 0xdf || op == 0x9B){
+		if(op == 0x9B){	/* WAIT */
+			ur->pc++;
+			continue;
+		}
+		if(ufp->control & ufp->status & 0x3F)
+			ufp->status |= 0x8000;
+		else
+			ufp->status &= 0x7FFF;
+		pc = ur->pc;
+		op = (op<<8) | getubyte(pc+1);
+		ufp->selector = ur->cs;
+		ufp->r4 = op-0xD800;
+		ur->pc += 2;
+		mem = nil;
+		s = nil;
+		d = nil;
+		/* decode op, following table 10.2.4 in i486 handbook */
+		i = op & 0xFFE0;
+		if(i == 0xD9E0){
+			fp = &optab4[op&0x1F];
+			s = &ST(0);
+			if(fp->dstf & Fload)
+				pushfp();
+			d = &ST(0);
+		} else if(i == 0xDBE0){
+			i = op & 0x1F;
+			if(i == 2){	/* FCLEX */
+				ufp->status &= 0x7f00;
+				continue;
+			} else if(i == 3){	/* FINIT */
+				ufp->control = 0x037f;
+				ufp->status = 0;
+				ufp->tag = 0;
+				continue;
+			}
+			fp = nil;
+		} else if((op & 0xF8C0) == 0xD8C0){
+			i = ((op>>6)&030)|((op>>3)&7);
+			if(op & (1<<8)){
+				fp = &optab3b[i];
+				s = &ST(op&7);
+				if(fp->dstf & Fload)
+					pushfp();
+				d = &ST(0);
+			} else {
+				fp = &optab3a[i];
+				i = op & 7;
+				if(op & (1<<10)){
+					s = &ST(0);
+					d = &ST(i);
+				}else{
+					s = &ST(i);
+					d = &ST(0);
+				}
+			}
+		} else if((op & 0xF920) == 0xD920){
+			mem = ea(ur, op&0xFF);
+			fp = &optab1[(op>>9)&3][(op>>3)&3];
+		} else {
+			mem = ea(ur, op&0xFF);
+			if(op & (1<<8)){
+				/* load/store */
+				fp = &optab2b[(op>>3)&7];
+				if(fp->dstf & Fload){
+					pushfp();
+					d = &ST(0);
+				} else
+					s = &ST(0);
+			} else {
+				/* mem OP reg */
+				fp = &optab2a[(op>>3)&7];
+				(*loadf[(op>>9)&3])(mem, &tmp);
+				s = &tmp;
+				d = &ST(0);
+			}
+		}
+		if(fp == nil || fp->f == nil){
+			if(fp == nil || fp->name == nil)
+				snprint(buf, sizeof(buf), "sys: fp: pc=%lux invalid fp 0x%.4x", pc, op);
+			else
+				snprint(buf, sizeof(buf), "sys: fp: pc=%lux unimp fp 0x%.4x (%s)", pc, op, fp->name);
+			error(buf);
+		}
+		if(fpemudebug)
+			print("%8.8lux %.4x %s\n", pc, op, fp->name);
+		(*fp->f)(ur, op, mem, s, d);
+		if(fp->dstf & Fpop1){
+			popfp();
+			if(fp->dstf & Fpop2)
+				popfp();
+		}
+		if(anyhigher())
+			sched();
+	}
+}
--- /dev/null
+++ b/os/pc/fpsave.s
@@ -1,0 +1,9 @@
+TEXT	FPsave(SB), 1, $0	/* save FPU environment without waiting */
+	MOVL	fpu+0(FP), AX
+	FSTENV	0(AX)
+	RET
+ 
+TEXT	FPrestore(SB), 1, $0	/* restore FPU environment without waiting */
+	MOVL	fpu+0(FP), AX
+	FLDENV	0(AX)
+	RET
--- /dev/null
+++ b/os/pc/i8250.c
@@ -1,0 +1,328 @@
+#include "u.h"
+#include "../port/lib.h"
+#include "mem.h"
+#include "dat.h"
+#include "fns.h"
+#include "io.h"
+#include "../port/error.h"
+
+/*
+ *  INS8250 uart
+ */
+enum
+{
+	/*
+	 *  register numbers
+	 */
+	Data=	0,		/* xmit/rcv buffer */
+	Iena=	1,		/* interrupt enable */
+	 Ircv=	(1<<0),		/*  for char rcv'd */
+	 Ixmt=	(1<<1),		/*  for xmit buffer empty */
+	 Irstat=(1<<2),		/*  for change in rcv'er status */
+	 Imstat=(1<<3),		/*  for change in modem status */
+	Istat=	2,		/* interrupt flag (read) */
+	 Fenabd=(3<<6),		/*  on if fifo's enabled */
+	Fifoctl=2,		/* fifo control (write) */
+	 Fena=	(1<<0),		/*  enable xmit/rcv fifos */
+	 Ftrig=	(1<<6),		/*  trigger after 4 input characters */
+	 Fclear=(3<<1),		/*  clear xmit & rcv fifos */
+	Format=	3,		/* byte format */
+	 Bits8=	(3<<0),		/*  8 bits/byte */
+	 Stop2=	(1<<2),		/*  2 stop bits */
+	 Pena=	(1<<3),		/*  generate parity */
+	 Peven=	(1<<4),		/*  even parity */
+	 Pforce=(1<<5),		/*  force parity */
+	 Break=	(1<<6),		/*  generate a break */
+	 Dra=	(1<<7),		/*  address the divisor */
+	Mctl=	4,		/* modem control */
+	 Dtr=	(1<<0),		/*  data terminal ready */
+	 Rts=	(1<<1),		/*  request to send */
+	 Ri=	(1<<2),		/*  ring */
+	 Inton=	(1<<3),		/*  turn on interrupts */
+	 Loop=	(1<<4),		/*  loop back */
+	Lstat=	5,		/* line status */
+	 Inready=(1<<0),	/*  receive buffer full */
+	 Oerror=(1<<1),		/*  receiver overrun */
+	 Perror=(1<<2),		/*  receiver parity error */
+	 Ferror=(1<<3),		/*  rcv framing error */
+	 Outready=(1<<5),	/*  output buffer empty */
+	Mstat=	6,		/* modem status */
+	 Ctsc=	(1<<0),		/*  clear to send changed */
+	 Dsrc=	(1<<1),		/*  data set ready changed */
+	 Rire=	(1<<2),		/*  rising edge of ring indicator */
+	 Dcdc=	(1<<3),		/*  data carrier detect changed */
+	 Cts=	(1<<4),		/*  complement of clear to send line */
+	 Dsr=	(1<<5),		/*  complement of data set ready line */
+	 Ring=	(1<<6),		/*  complement of ring indicator line */
+	 Dcd=	(1<<7),		/*  complement of data carrier detect line */
+	Scratch=7,		/* scratchpad */
+	Dlsb=	0,		/* divisor lsb */
+	Dmsb=	1,		/* divisor msb */
+
+	Serial=	0,
+	Modem=	1,
+};
+
+typedef struct Uart	Uart;
+struct Uart
+{
+	int	port;
+	uchar	sticky[8];	/* sticky write register values */
+	int	nofifo;
+
+	void	(*rx)(int);	/* routine to take a received character */
+	int	(*tx)(void);	/* routine to get a character to transmit */
+
+	ulong	frame;
+	ulong	overrun;
+};
+
+static Uart i8250uart[1];
+
+#define UartFREQ 1843200
+
+#define i8250regw(u, r, v)	outb((u)->port+(r), (u)->sticky[(r)]|(v))
+#define i8250regr(u, r)		inb((u)->port+(r))
+
+/*
+ *  set the baud rate by calculating and setting the baudrate
+ *  generator constant.  This will work with fairly non-standard
+ *  baud rates.
+ */
+static void
+i8250setbaud(Uart* uart, int rate)
+{
+	ulong brconst;
+
+	brconst = (UartFREQ+8*rate-1)/(16*rate);
+
+	i8250regw(uart, Format, Dra);
+	outb(uart->port+Dmsb, (brconst>>8) & 0xff);
+	outb(uart->port+Dlsb, brconst & 0xff);
+	i8250regw(uart, Format, 0);
+}
+
+/*
+ *  toggle DTR
+ */
+static void
+i8250dtr(Uart* uart, int n)
+{
+	if(n)
+		uart->sticky[Mctl] |= Dtr;
+	else
+		uart->sticky[Mctl] &= ~Dtr;
+	i8250regw(uart, Mctl, 0);
+}
+
+/*
+ *  toggle RTS
+ */
+static void
+i8250rts(Uart* uart, int n)
+{
+	if(n)
+		uart->sticky[Mctl] |= Rts;
+	else
+		uart->sticky[Mctl] &= ~Rts;
+	i8250regw(uart, Mctl, 0);
+}
+
+/*
+ * Enable/disable FIFOs (if possible).
+ */
+static void
+i8250fifo(Uart* uart, int n)
+{
+	int i, s;
+
+	if(uart->nofifo)
+		return;
+
+	s = splhi();
+
+	/* reset fifos */
+	i8250regw(uart, Fifoctl, Fclear);
+
+	/* empty buffer and interrupt conditions */
+	for(i = 0; i < 16; i++){
+		if(i8250regr(uart, Istat))
+			{}
+		if(i8250regr(uart, Data))
+			{}
+	}
+  
+	/* turn on fifo */
+	if(n){
+		i8250regw(uart, Fifoctl, Fena|Ftrig);
+
+		if((i8250regr(uart, Istat) & Fenabd) == 0){
+			/* didn't work, must be an earlier chip type */
+			uart->nofifo = 1;
+		}
+	}
+
+	splx(s);
+}
+
+#ifdef notdef
+static void
+i8250intr(Ureg*, void* arg)
+{
+	Uart *uart;
+	int ch;
+	int s, l, loops;
+
+	uart = arg;
+	for(loops = 0; loops < 1024; loops++){
+		s = i8250regr(uart, Istat);
+		switch(s & 0x3F){
+		case 6:	/* receiver line status */
+			l = i8250regr(uart, Lstat);
+			if(l & Ferror)
+				uart->frame++;
+			if(l & Oerror)
+				uart->overrun++;
+			break;
+	
+		case 4:	/* received data available */
+		case 12:
+			ch = inb(uart->port+Data);
+			if(uart->rx)
+				(*uart->rx)(ch & 0x7F);
+			break;
+	
+		case 2:	/* transmitter empty */
+			ch = -1;
+			if(uart->tx)
+				ch = (*uart->tx)();
+			if(ch != -1)
+				outb(uart->port+Data, ch);
+			break;
+	
+		case 0:	/* modem status */
+			i8250regr(uart, Mstat);
+			break;
+	
+		default:
+			if(s&1)
+				return;
+			print("weird modem interrupt #%2.2ux\n", s);
+			break;
+		}
+	}
+	panic("i8250intr: 0x%2.2ux\n", i8250regr(uart, Istat));
+}
+#endif /* notdef */
+
+/*
+ *  turn on a port's interrupts.  set DTR and RTS
+ */
+static void
+i8250enable(Uart* uart)
+{
+	/*
+ 	 *  turn on interrupts
+	 */
+	uart->sticky[Iena] = 0;
+#ifdef notdef
+	if(uart->tx)
+		uart->sticky[Iena] |= Ixmt;
+	if(uart->rx)
+		uart->sticky[Iena] |= Ircv|Irstat;
+#endif /* notdef */
+
+	/*
+	 *  turn on DTR and RTS
+	 */
+	i8250dtr(uart, 1);
+	i8250rts(uart, 1);
+	i8250fifo(uart, 1);
+
+	i8250regw(uart, Iena, 0);
+}
+
+void
+i8250special(int port, void (*rx)(int), int (*tx)(void), int baud)
+{
+	Uart *uart = &i8250uart[0];
+
+	if(uart->port)
+		return;
+
+	switch(port){
+
+	case 0:
+		uart->port = 0x3F8;
+#ifdef notdef
+		intrenable(VectorUART0, i8250intr, uart, BUSUNKNOWN);
+#endif /* notdef */
+		break;
+
+	case 1:
+		uart->port = 0x2F8;
+#ifdef notdef
+		intrenable(VectorUART1, i8250intr, uart, BUSUNKNOWN);
+#endif /* notdef */
+		break;
+
+	default:
+		return;
+	}
+
+	/*
+	 *  set rate to 9600 baud.
+	 *  8 bits/character.
+	 *  1 stop bit.
+	 *  interrupts enabled.
+	 */
+	i8250setbaud(uart, 9600);
+	uart->sticky[Format] = Bits8;
+	i8250regw(uart, Format, 0);
+	uart->sticky[Mctl] |= Inton;
+	i8250regw(uart, Mctl, 0x0);
+
+	uart->rx = rx;
+	uart->tx = tx;
+	i8250enable(uart);
+	if(baud)
+		i8250setbaud(uart, baud);
+}
+
+int
+i8250getc(void)
+{
+	Uart *uart = &i8250uart[0];
+
+	if(i8250regr(uart, Lstat) & Inready)
+		return inb(uart->port+Data);
+	return 0;
+}
+
+void
+i8250putc(int c)
+{
+	Uart *uart = &i8250uart[0];
+	int i;
+
+	for(i = 0; i < 100; i++){
+		if(i8250regr(uart, Lstat) & Outready)
+			break;
+		delay(1);
+	}
+	outb(uart->port+Data, c);
+}
+
+void
+i8250puts(char* s, int n)
+{
+	int x;
+
+	x = splhi();
+	while(n--){
+		if(*s == '\n')
+			i8250putc('\r');
+		i8250putc(*s++);
+	}
+	splx(x);
+}
--- /dev/null
+++ b/os/pc/i8253.c
@@ -1,0 +1,314 @@
+#include "u.h"
+#include "../port/lib.h"
+#include "mem.h"
+#include "dat.h"
+#include "fns.h"
+#include "io.h"
+
+/*
+ *  8253 timer
+ */
+enum
+{
+	T0cntr=	0x40,		/* counter ports */
+	T1cntr=	0x41,		/* ... */
+	T2cntr=	0x42,		/* ... */
+	Tmode=	0x43,		/* mode port (control word register) */
+	T2ctl=	0x61,		/* counter 2 control port */
+
+	/* commands */
+	Latch0=	0x00,		/* latch counter 0's value */
+	Load0l=	0x10,		/* load counter 0's lsb */
+	Load0m=	0x20,		/* load counter 0's msb */
+	Load0=	0x30,		/* load counter 0 with 2 bytes */
+
+	Latch1=	0x40,		/* latch counter 1's value */
+	Load1l=	0x50,		/* load counter 1's lsb */
+	Load1m=	0x60,		/* load counter 1's msb */
+	Load1=	0x70,		/* load counter 1 with 2 bytes */
+
+	Latch2=	0x80,		/* latch counter 2's value */
+	Load2l=	0x90,		/* load counter 2's lsb */
+	Load2m=	0xa0,		/* load counter 2's msb */
+	Load2=	0xb0,		/* load counter 2 with 2 bytes */
+
+	/* 8254 read-back command: everything > pc-at has an 8254 */
+	Rdback=	0xc0,		/* readback counters & status */
+	Rdnstat=0x10,		/* don't read status */
+	Rdncnt=	0x20,		/* don't read counter value */
+	Rd0cntr=0x02,		/* read back for which counter */
+	Rd1cntr=0x04,
+	Rd2cntr=0x08,
+
+	/* modes */
+	ModeMsk=0xe,
+	Square=	0x6,		/* periodic square wave */
+	Trigger=0x0,		/* interrupt on terminal count */
+	Sstrobe=0x8,		/* software triggered strobe */
+
+	/* T2ctl bits */
+	T2gate=	(1<<0),		/* enable T2 counting */
+	T2spkr=	(1<<1),		/* connect T2 out to speaker */
+	T2out=	(1<<5),		/* output of T2 */
+
+	Freq=	1193182,	/* Real clock frequency */
+	Tickshift=8,		/* extra accuracy */
+	MaxPeriod=Freq/HZ,
+	MinPeriod=Freq/(100*HZ),
+};
+
+typedef struct I8253 I8253;
+struct I8253
+{
+	Lock;
+	ulong	period;		/* current clock period */
+	int	enabled;
+	uvlong	hz;
+
+	ushort	last;		/* last value of clock 1 */
+	uvlong	ticks;		/* cumulative ticks of counter 1 */
+
+	ulong	periodset;
+};
+I8253 i8253;
+
+void
+i8253init(void)
+{
+	int loops, x;
+
+	ioalloc(T0cntr, 4, 0, "i8253");
+	ioalloc(T2ctl, 1, 0, "i8253.cntr2ctl");
+
+	i8253.period = Freq/HZ;
+
+	/*
+	 *  enable a 1/HZ interrupt for providing scheduling interrupts
+	 */
+	outb(Tmode, Load0|Square);
+	outb(T0cntr, (Freq/HZ));	/* low byte */
+	outb(T0cntr, (Freq/HZ)>>8);	/* high byte */
+
+	/*
+	 *  enable a longer period counter to use as a clock
+	 */
+	outb(Tmode, Load2|Square);
+	outb(T2cntr, 0);		/* low byte */
+	outb(T2cntr, 0);		/* high byte */
+	x = inb(T2ctl);
+	x |= T2gate;
+	outb(T2ctl, x);
+	
+	/*
+	 * Introduce a little delay to make sure the count is
+	 * latched and the timer is counting down; with a fast
+	 * enough processor this may not be the case.
+	 * The i8254 (which this probably is) has a read-back
+	 * command which can be used to make sure the counting
+	 * register has been written into the counting element.
+	 */
+	x = (Freq/HZ);
+	for(loops = 0; loops < 100000 && x >= (Freq/HZ); loops++){
+		outb(Tmode, Latch0);
+		x = inb(T0cntr);
+		x |= inb(T0cntr)<<8;
+	}
+}
+
+void
+guesscpuhz(int aalcycles)
+{
+	int loops, incr, x, y;
+	uvlong a, b, cpufreq;
+
+	/* find biggest loop that doesn't wrap */
+	incr = 16000000/(aalcycles*HZ*2);
+	x = 2000;
+	for(loops = incr; loops < 64*1024; loops += incr) {
+	
+		/*
+		 *  measure time for the loop
+		 *
+		 *			MOVL	loops,CX
+		 *	aaml1:	 	AAM
+		 *			LOOP	aaml1
+		 *
+		 *  the time for the loop should be independent of external
+		 *  cache and memory system since it fits in the execution
+		 *  prefetch buffer.
+		 *
+		 */
+		outb(Tmode, Latch0);
+		cycles(&a);
+		x = inb(T0cntr);
+		x |= inb(T0cntr)<<8;
+		aamloop(loops);
+		outb(Tmode, Latch0);
+		cycles(&b);
+		y = inb(T0cntr);
+		y |= inb(T0cntr)<<8;
+		x -= y;
+	
+		if(x < 0)
+			x += Freq/HZ;
+
+		if(x > Freq/(3*HZ))
+			break;
+	}
+
+	/*
+ 	 *  figure out clock frequency and a loop multiplier for delay().
+	 *  n.b. counter goes up by 2*Freq
+	 */
+	cpufreq = (vlong)loops*((aalcycles*2*Freq)/x);
+	m->loopconst = (cpufreq/1000)/aalcycles;	/* AAM+LOOP's for 1 ms */
+
+	if(m->havetsc){
+		/* counter goes up by 2*Freq */
+		b = (b-a)<<1;
+		b *= Freq;
+		b /= x;
+
+		/*
+		 *  round to the nearest megahz
+		 */
+		m->cpumhz = (b+500000)/1000000L;
+		m->cpuhz = b;
+		m->cyclefreq = b;
+	} else {
+		/*
+		 *  add in possible 0.5% error and convert to MHz
+		 */
+		m->cpumhz = (cpufreq + cpufreq/200)/1000000;
+		m->cpuhz = cpufreq;
+	}
+
+	i8253.hz = Freq<<Tickshift;
+}
+
+void
+i8253timerset(uvlong next)
+{
+	long period;
+	ulong want;
+	ulong now;
+
+	period = MaxPeriod;
+	if(next != 0){
+		want = next>>Tickshift;
+		now = i8253.ticks;	/* assuming whomever called us just did fastticks() */
+
+		period = want - now;
+		if(period < MinPeriod)
+			period = MinPeriod;
+		else if(period > MaxPeriod)
+			period = MaxPeriod;
+	}
+
+	/* hysteresis */
+	if(i8253.period != period){
+		ilock(&i8253);
+		/* load new value */
+		outb(Tmode, Load0|Square);
+		outb(T0cntr, period);		/* low byte */
+		outb(T0cntr, period >> 8);		/* high byte */
+
+		/* remember period */
+		i8253.period = period;
+		i8253.periodset++;
+		iunlock(&i8253);
+	}
+}
+
+static void
+i8253clock(Ureg* ureg, void*)
+{
+	timerintr(ureg, 0);
+}
+
+void
+i8253enable(void)
+{
+	i8253.enabled = 1;
+	i8253.period = Freq/HZ;
+	intrenable(IrqCLOCK, i8253clock, 0, BUSUNKNOWN, "clock");
+}
+
+void
+i8253link(void)
+{
+}
+
+/*
+ *  return the total ticks of counter 2.  We shift by
+ *  8 to give timesync more wriggle room for interpretation
+ *  of the frequency
+ */
+uvlong
+i8253read(uvlong *hz)
+{
+	ushort y, x;
+	uvlong ticks;
+
+	if(hz)
+		*hz = i8253.hz;
+
+	ilock(&i8253);
+	outb(Tmode, Latch2);
+	y = inb(T2cntr);
+	y |= inb(T2cntr)<<8;
+
+	if(y < i8253.last)
+		x = i8253.last - y;
+	else {
+		x = i8253.last + (0x10000 - y);
+		if (x > 3*MaxPeriod) {
+			outb(Tmode, Load2|Square);
+			outb(T2cntr, 0);		/* low byte */
+			outb(T2cntr, 0);		/* high byte */
+			y = 0xFFFF;
+			x = i8253.period;
+		}
+	}
+	i8253.last = y;
+	i8253.ticks += x>>1;
+	ticks = i8253.ticks;
+	iunlock(&i8253);
+
+	return ticks<<Tickshift;
+}
+
+void
+delay(int millisecs)
+{
+	millisecs *= m->loopconst;
+	if(millisecs <= 0)
+		millisecs = 1;
+	aamloop(millisecs);
+}
+
+void
+microdelay(int microsecs)
+{
+	microsecs *= m->loopconst;
+	microsecs /= 1000;
+	if(microsecs <= 0)
+		microsecs = 1;
+	aamloop(microsecs);
+}
+
+/*  
+ *  performance measurement ticks.  must be low overhead.
+ *  doesn't have to count over a second.
+ */
+ulong
+perfticks(void)
+{
+	uvlong x;
+
+	if(m->havetsc)
+		cycles(&x);
+	else
+		x = 0;
+	return x;
+}
--- /dev/null
+++ b/os/pc/i8259.c
@@ -1,0 +1,199 @@
+#include "u.h"
+#include "../port/lib.h"
+#include "mem.h"
+#include "dat.h"
+#include "fns.h"
+#include "io.h"
+
+/*
+ *  8259 interrupt controllers
+ */
+enum
+{
+	Int0ctl=	0x20,		/* control port (ICW1, OCW2, OCW3) */
+	Int0aux=	0x21,		/* everything else (ICW2, ICW3, ICW4, OCW1) */
+	Int1ctl=	0xA0,		/* control port */
+	Int1aux=	0xA1,		/* everything else (ICW2, ICW3, ICW4, OCW1) */
+
+	Icw1=		0x10,		/* select bit in ctl register */
+	Ocw2=		0x00,
+	Ocw3=		0x08,
+
+	EOI=		0x20,		/* non-specific end of interrupt */
+
+	Elcr1=		0x4D0,		/* Edge/Level Triggered Register */
+	Elcr2=		0x4D1,
+};
+
+static Lock i8259lock;
+static int i8259mask = 0xFFFF;		/* disabled interrupts */
+int i8259elcr;				/* mask of level-triggered interrupts */
+
+void
+i8259init(void)
+{
+	int x;
+
+	ioalloc(Int0ctl, 2, 0, "i8259.0");
+	ioalloc(Int1ctl, 2, 0, "i8259.1");
+	ilock(&i8259lock);
+
+	/*
+	 *  Set up the first 8259 interrupt processor.
+	 *  Make 8259 interrupts start at CPU vector VectorPIC.
+	 *  Set the 8259 as master with edge triggered
+	 *  input with fully nested interrupts.
+	 */
+	outb(Int0ctl, (1<<4)|(0<<3)|(1<<0));	/* ICW1 - master, edge triggered,
+					  	   ICW4 will be sent */
+	outb(Int0aux, VectorPIC);		/* ICW2 - interrupt vector offset */
+	outb(Int0aux, 0x04);			/* ICW3 - have slave on level 2 */
+	outb(Int0aux, 0x01);			/* ICW4 - 8086 mode, not buffered */
+
+	/*
+	 *  Set up the second 8259 interrupt processor.
+	 *  Make 8259 interrupts start at CPU vector VectorPIC+8.
+	 *  Set the 8259 as slave with edge triggered
+	 *  input with fully nested interrupts.
+	 */
+	outb(Int1ctl, (1<<4)|(0<<3)|(1<<0));	/* ICW1 - master, edge triggered,
+					  	   ICW4 will be sent */
+	outb(Int1aux, VectorPIC+8);		/* ICW2 - interrupt vector offset */
+	outb(Int1aux, 0x02);			/* ICW3 - I am a slave on level 2 */
+	outb(Int1aux, 0x01);			/* ICW4 - 8086 mode, not buffered */
+	outb(Int1aux, (i8259mask>>8) & 0xFF);
+
+	/*
+	 *  pass #2 8259 interrupts to #1
+	 */
+	i8259mask &= ~0x04;
+	outb(Int0aux, i8259mask & 0xFF);
+
+	/*
+	 * Set Ocw3 to return the ISR when ctl read.
+	 * After initialisation status read is set to IRR.
+	 * Read IRR first to possibly deassert an outstanding
+	 * interrupt.
+	 */
+	inb(Int0ctl);
+	outb(Int0ctl, Ocw3|0x03);
+	inb(Int1ctl);
+	outb(Int1ctl, Ocw3|0x03);
+
+	/*
+	 * Check for Edge/Level register.
+	 * This check may not work for all chipsets.
+	 * First try a non-intrusive test - the bits for
+	 * IRQs 13, 8, 2, 1 and 0 must be edge (0). If
+	 * that's OK try a R/W test.
+	 */
+	x = (inb(Elcr2)<<8)|inb(Elcr1);
+	if(!(x & 0x2107)){
+		outb(Elcr1, 0);
+		if(inb(Elcr1) == 0){
+			outb(Elcr1, 0x20);
+			if(inb(Elcr1) == 0x20)
+				i8259elcr = x;
+			outb(Elcr1, x & 0xFF);
+			print("ELCR: %4.4uX\n", i8259elcr);
+		}
+	}
+	iunlock(&i8259lock);
+}
+
+int
+i8259isr(int vno)
+{
+	int irq, isr;
+
+	if(vno < VectorPIC || vno > VectorPIC+MaxIrqPIC)
+		return 0;
+	irq = vno-VectorPIC;
+
+	/*
+	 *  tell the 8259 that we're done with the
+	 *  highest level interrupt (interrupts are still
+	 *  off at this point)
+	 */
+	ilock(&i8259lock);
+	isr = inb(Int0ctl);
+	outb(Int0ctl, EOI);
+	if(irq >= 8){
+		isr |= inb(Int1ctl)<<8;
+		outb(Int1ctl, EOI);
+	}
+	iunlock(&i8259lock);
+
+	return isr & (1<<irq);
+}
+
+int
+i8259enable(Vctl* v)
+{
+	int irq, irqbit;
+
+	/*
+	 * Given an IRQ, enable the corresponding interrupt in the i8259
+	 * and return the vector to be used. The i8259 is set to use a fixed
+	 * range of vectors starting at VectorPIC.
+	 */
+	irq = v->irq;
+	if(irq < 0 || irq > MaxIrqPIC){
+		print("i8259enable: irq %d out of range\n", irq);
+		return -1;
+	}
+	irqbit = 1<<irq;
+
+	ilock(&i8259lock);
+	if(!(i8259mask & irqbit) && !(i8259elcr & irqbit)){
+		print("i8259enable: irq %d shared but not level\n", irq);
+		iunlock(&i8259lock);
+		return -1;
+	}
+	i8259mask &= ~irqbit;
+	if(irq < 8)
+		outb(Int0aux, i8259mask & 0xFF);
+	else
+		outb(Int1aux, (i8259mask>>8) & 0xFF);
+
+	if(i8259elcr & irqbit)
+		v->eoi = i8259isr;
+	else
+		v->isr = i8259isr;
+	iunlock(&i8259lock);
+
+	return VectorPIC+irq;
+}
+
+int
+i8259vecno(int irq)
+{
+	return VectorPIC+irq;
+}
+
+int
+i8259disable(int irq)
+{
+	int irqbit;
+
+	/*
+	 * Given an IRQ, disable the corresponding interrupt
+	 * in the 8259.
+	 */
+	if(irq < 0 || irq > MaxIrqPIC){
+		print("i8259disable: irq %d out of range\n", irq);
+		return -1;
+	}
+	irqbit = 1<<irq;
+
+	ilock(&i8259lock);
+	if(!(i8259mask & irqbit)){
+		i8259mask |= irqbit;
+		if(irq < 8)
+			outb(Int0aux, i8259mask & 0xFF);
+		else
+			outb(Int1aux, (i8259mask>>8) & 0xFF);
+	}
+	iunlock(&i8259lock);
+	return 0;
+}
--- /dev/null
+++ b/os/pc/io.h
@@ -1,0 +1,371 @@
+#define X86STEPPING(x)	((x) & 0x0F)
+#define X86MODEL(x)	(((x)>>4) & 0x0F)
+#define X86FAMILY(x)	(((x)>>8) & 0x0F)
+
+enum {
+	VectorDBG	= 1,		/* debug exception */
+	VectorNMI	= 2,		/* non-maskable interrupt */
+	VectorBPT	= 3,		/* breakpoint */
+	VectorUD	= 6,		/* invalid opcode exception */
+	VectorCNA	= 7,		/* coprocessor not available */
+	Vector2F	= 8,		/* double fault */
+	VectorCSO	= 9,		/* coprocessor segment overrun */
+	VectorPF	= 14,		/* page fault */
+	Vector15	= 15,		/* reserved */
+	VectorCERR	= 16,		/* coprocessor error */
+
+	VectorPIC	= 32,		/* external i8259 interrupts */
+	IrqCLOCK	= 0,
+	IrqKBD		= 1,
+	IrqUART1	= 3,
+	IrqUART0	= 4,
+	IrqPCMCIA	= 5,
+	IrqFLOPPY	= 6,
+	IrqLPT		= 7,
+	IrqIRQ7		= 7,
+	IrqAUX		= 12,		/* PS/2 port */
+	IrqIRQ13	= 13,		/* coprocessor on 386 */
+	IrqATA0		= 14,
+	IrqATA1		= 15,
+	MaxIrqPIC	= 15,
+
+	VectorLAPIC	= VectorPIC+16,	/* local APIC interrupts */
+	IrqLINT0	= 16,		/* LINT[01] must be offsets 0 and 1 */
+	IrqLINT1	= 17,
+	IrqTIMER	= 18,
+	IrqERROR	= 19,
+	IrqPCINT	= 20,
+	IrqSPURIOUS	= 31,		/* must have bits [3-0] == 0x0F */
+	MaxIrqLAPIC	= 31,
+
+	VectorSYSCALL	= 64,
+
+	VectorAPIC	= 65,		/* external APIC interrupts */
+	MaxVectorAPIC	= 255,
+};
+
+typedef struct Vctl {
+	Vctl*	next;			/* handlers on this vector */
+
+	char	name[KNAMELEN];		/* of driver */
+	int	isintr;			/* interrupt or fault/trap */
+	int	irq;
+	int	tbdf;
+	int	(*isr)(int);		/* get isr bit for this irq */
+	int	(*eoi)(int);		/* eoi */
+
+	void	(*f)(Ureg*, void*);	/* handler to call */
+	void*	a;			/* argument to call it with */
+} Vctl;
+
+enum {
+	BusCBUS		= 0,		/* Corollary CBUS */
+	BusCBUSII,			/* Corollary CBUS II */
+	BusEISA,			/* Extended ISA */
+	BusFUTURE,			/* IEEE Futurebus */
+	BusINTERN,			/* Internal bus */
+	BusISA,				/* Industry Standard Architecture */
+	BusMBI,				/* Multibus I */
+	BusMBII,			/* Multibus II */
+	BusMCA,				/* Micro Channel Architecture */
+	BusMPI,				/* MPI */
+	BusMPSA,			/* MPSA */
+	BusNUBUS,			/* Apple Macintosh NuBus */
+	BusPCI,				/* Peripheral Component Interconnect */
+	BusPCMCIA,			/* PC Memory Card International Association */
+	BusTC,				/* DEC TurboChannel */
+	BusVL,				/* VESA Local bus */
+	BusVME,				/* VMEbus */
+	BusXPRESS,			/* Express System Bus */
+};
+
+#define MKBUS(t,b,d,f)	(((t)<<24)|(((b)&0xFF)<<16)|(((d)&0x1F)<<11)|(((f)&0x07)<<8))
+#define BUSFNO(tbdf)	(((tbdf)>>8)&0x07)
+#define BUSDNO(tbdf)	(((tbdf)>>11)&0x1F)
+#define BUSBNO(tbdf)	(((tbdf)>>16)&0xFF)
+#define BUSTYPE(tbdf)	((tbdf)>>24)
+#define BUSBDF(tbdf)	((tbdf)&0x00FFFF00)
+#define BUSUNKNOWN	(-1)
+
+enum {
+	MaxEISA		= 16,
+	CfgEISA		= 0xC80,
+};
+
+/*
+ * PCI support code.
+ */
+enum {					/* type 0 & type 1 pre-defined header */
+	PciVID		= 0x00,		/* vendor ID */
+	PciDID		= 0x02,		/* device ID */
+	PciPCR		= 0x04,		/* command */
+	PciPSR		= 0x06,		/* status */
+	PciRID		= 0x08,		/* revision ID */
+	PciCCRp		= 0x09,		/* programming interface class code */
+	PciCCRu		= 0x0A,		/* sub-class code */
+	PciCCRb		= 0x0B,		/* base class code */
+	PciCLS		= 0x0C,		/* cache line size */
+	PciLTR		= 0x0D,		/* latency timer */
+	PciHDT		= 0x0E,		/* header type */
+	PciBST		= 0x0F,		/* BIST */
+
+	PciBAR0		= 0x10,		/* base address */
+	PciBAR1		= 0x14,
+
+	PciINTL		= 0x3C,		/* interrupt line */
+	PciINTP		= 0x3D,		/* interrupt pin */
+};
+
+/* ccrb (base class code) values; controller types */
+enum {
+	Pcibcpci1	= 0,		/* pci 1.0; no class codes defined */
+	Pcibcstore	= 1,		/* mass storage */
+	Pcibcnet	= 2,		/* network */
+	Pcibcdisp	= 3,		/* display */
+	Pcibcmmedia	= 4,		/* multimedia */
+	Pcibcmem	= 5,		/* memory */
+	Pcibcbridge	= 6,		/* bridge */
+	Pcibccomm	= 7,		/* simple comms (e.g., serial) */
+	Pcibcbasesys	= 8,		/* base system */
+	Pcibcinput	= 9,		/* input */
+	Pcibcdock	= 0xa,		/* docking stations */
+	Pcibcproc	= 0xb,		/* processors */
+	Pcibcserial	= 0xc,		/* serial bus (e.g., USB) */
+	Pcibcwireless	= 0xd,		/* wireless */
+	Pcibcintell	= 0xe,		/* intelligent i/o */
+	Pcibcsatcom	= 0xf,		/* satellite comms */
+	Pcibccrypto	= 0x10,		/* encryption/decryption */
+	Pcibcdacq	= 0x11,		/* data acquisition & signal proc. */
+};
+
+/* ccru (sub-class code) values; common cases only */
+enum {
+	/* mass storage */
+	Pciscscsi	= 0,		/* SCSI */
+	Pciscide	= 1,		/* IDE (ATA) */
+
+	/* network */
+	Pciscether	= 0,		/* Ethernet */
+
+	/* display */
+	Pciscvga	= 0,		/* VGA */
+	Pciscxga	= 1,		/* XGA */
+	Pcisc3d		= 2,		/* 3D */
+
+	/* bridges */
+	Pcischostpci	= 0,		/* host/pci */
+	Pciscpcicpci	= 1,		/* pci/pci */
+
+	/* simple comms */
+	Pciscserial	= 0,		/* 16450, etc. */
+	Pciscmultiser	= 1,		/* multiport serial */
+
+	/* serial bus */
+	Pciscusb	= 3,		/* USB */
+};
+
+enum {					/* type 0 pre-defined header */
+	PciCIS		= 0x28,		/* cardbus CIS pointer */
+	PciSVID		= 0x2C,		/* subsystem vendor ID */
+	PciSID		= 0x2E,		/* cardbus CIS pointer */
+	PciEBAR0	= 0x30,		/* expansion ROM base address */
+	PciMGNT		= 0x3E,		/* burst period length */
+	PciMLT		= 0x3F,		/* maximum latency between bursts */
+};
+
+enum {					/* type 1 pre-defined header */
+	PciPBN		= 0x18,		/* primary bus number */
+	PciSBN		= 0x19,		/* secondary bus number */
+	PciUBN		= 0x1A,		/* subordinate bus number */
+	PciSLTR		= 0x1B,		/* secondary latency timer */
+	PciIBR		= 0x1C,		/* I/O base */
+	PciILR		= 0x1D,		/* I/O limit */
+	PciSPSR		= 0x1E,		/* secondary status */
+	PciMBR		= 0x20,		/* memory base */
+	PciMLR		= 0x22,		/* memory limit */
+	PciPMBR		= 0x24,		/* prefetchable memory base */
+	PciPMLR		= 0x26,		/* prefetchable memory limit */
+	PciPUBR		= 0x28,		/* prefetchable base upper 32 bits */
+	PciPULR		= 0x2C,		/* prefetchable limit upper 32 bits */
+	PciIUBR		= 0x30,		/* I/O base upper 16 bits */
+	PciIULR		= 0x32,		/* I/O limit upper 16 bits */
+	PciEBAR1	= 0x28,		/* expansion ROM base address */
+	PciBCR		= 0x3E,		/* bridge control register */
+};
+
+enum {					/* type 2 pre-defined header */
+	PciCBExCA	= 0x10,
+	PciCBSPSR	= 0x16,
+	PciCBPBN	= 0x18,		/* primary bus number */
+	PciCBSBN	= 0x19,		/* secondary bus number */
+	PciCBUBN	= 0x1A,		/* subordinate bus number */
+	PciCBSLTR	= 0x1B,		/* secondary latency timer */
+	PciCBMBR0	= 0x1C,
+	PciCBMLR0	= 0x20,
+	PciCBMBR1	= 0x24,
+	PciCBMLR1	= 0x28,
+	PciCBIBR0	= 0x2C,		/* I/O base */
+	PciCBILR0	= 0x30,		/* I/O limit */
+	PciCBIBR1	= 0x34,		/* I/O base */
+	PciCBILR1	= 0x38,		/* I/O limit */
+	PciCBSVID	= 0x40,		/* subsystem vendor ID */
+	PciCBSID	= 0x42,		/* subsystem ID */
+	PciCBLMBAR	= 0x44,		/* legacy mode base address */
+};
+
+typedef struct Pcisiz Pcisiz;
+struct Pcisiz
+{
+	Pcidev*	dev;
+	int	siz;
+	int	bar;
+};
+
+typedef struct Pcidev Pcidev;
+struct Pcidev
+{
+	int	tbdf;			/* type+bus+device+function */
+	ushort	vid;			/* vendor ID */
+	ushort	did;			/* device ID */
+
+	ushort	pcr;
+
+	uchar	rid;
+	uchar	ccrp;
+	uchar	ccru;
+	uchar	ccrb;
+	uchar	cls;
+	uchar	ltr;
+
+	struct {
+		ulong	bar;		/* base address */
+		int	size;
+	} mem[6];
+
+	struct {
+		ulong	bar;	
+		int	size;
+	} rom;
+	uchar	intl;			/* interrupt line */
+
+	Pcidev*	list;
+	Pcidev*	link;			/* next device on this bno */
+
+	Pcidev*	bridge;			/* down a bus */
+	struct {
+		ulong	bar;
+		int	size;
+	} ioa, mema;
+
+	int	pmrb;			/* power management register block */
+};
+
+#define PCIWINDOW	0
+#define PCIWADDR(va)	(PADDR(va)+PCIWINDOW)
+#define ISAWINDOW	0
+#define ISAWADDR(va)	(PADDR(va)+ISAWINDOW)
+
+/* SMBus transactions */
+enum
+{
+	SMBquick,		/* sends address only */
+
+	/* write */
+	SMBsend,		/* sends address and cmd */
+	SMBbytewrite,		/* sends address and cmd and 1 byte */
+	SMBwordwrite,		/* sends address and cmd and 2 bytes */
+
+	/* read */
+	SMBrecv,		/* sends address, recvs 1 byte */
+	SMBbyteread,		/* sends address and cmd, recv's byte */
+	SMBwordread,		/* sends address and cmd, recv's 2 bytes */
+};
+
+typedef struct SMBus SMBus;
+struct SMBus {
+	QLock;		/* mutex */
+	Rendez	r;	/* rendezvous point for completion interrupts */
+	void	*arg;	/* implementation dependent */
+	ulong	base;	/* port or memory base of smbus */
+	int	busy;
+	void	(*transact)(SMBus*, int, int, int, uchar*);
+};
+
+/*
+ * PCMCIA support code.
+ */
+
+typedef struct PCMslot		PCMslot;
+typedef struct PCMconftab	PCMconftab;
+
+/*
+ * Map between ISA memory space and PCMCIA card memory space.
+ */
+struct PCMmap {
+	ulong	ca;			/* card address */
+	ulong	cea;			/* card end address */
+	ulong	isa;			/* ISA address */
+	int	len;			/* length of the ISA area */
+	int	attr;			/* attribute memory */
+	int	ref;
+};
+
+/* configuration table entry */
+struct PCMconftab
+{
+	int	index;
+	ushort	irqs;		/* legal irqs */
+	uchar	irqtype;
+	uchar	bit16;		/* true for 16 bit access */
+	struct {
+		ulong	start;
+		ulong	len;
+	} io[16];
+	int	nio;
+	uchar	vpp1;
+	uchar	vpp2;
+	uchar	memwait;
+	ulong	maxwait;
+	ulong	readywait;
+	ulong	otherwait;
+};
+
+/* a card slot */
+struct PCMslot
+{
+	Lock;
+	int	ref;
+
+	void	*cp;		/* controller for this slot */
+	long	memlen;		/* memory length */
+	uchar	base;		/* index register base */
+	uchar	slotno;		/* slot number */
+
+	/* status */
+	uchar	special;	/* in use for a special device */
+	uchar	already;	/* already inited */
+	uchar	occupied;
+	uchar	battery;
+	uchar	wrprot;
+	uchar	powered;
+	uchar	configed;
+	uchar	enabled;
+	uchar	busy;
+
+	/* cis info */
+	ulong	msec;		/* time of last slotinfo call */
+	char	verstr[512];	/* version string */
+	int	ncfg;		/* number of configurations */
+	struct {
+		ushort	cpresent;	/* config registers present */
+		ulong	caddr;		/* relative address of config registers */
+	} cfg[8];
+	int	nctab;		/* number of config table entries */
+	PCMconftab	ctab[8];
+	PCMconftab	*def;	/* default conftab */
+
+	/* memory maps */
+	Lock	mlock;		/* lock down the maps */
+	int	time;
+	PCMmap	mmap[4];	/* maps, last is always for the kernel */
+};
--- /dev/null
+++ b/os/pc/kbd.c
@@ -1,0 +1,477 @@
+#include	"u.h"
+#include	"../port/lib.h"
+#include	"mem.h"
+#include	"dat.h"
+#include	"fns.h"
+#include	"io.h"
+#include	"../port/error.h"
+
+enum {
+	Data=		0x60,		/* data port */
+
+	Status=		0x64,		/* status port */
+	 Inready=	0x01,		/*  input character ready */
+	 Outbusy=	0x02,		/*  output busy */
+	 Sysflag=	0x04,		/*  system flag */
+	 Cmddata=	0x08,		/*  cmd==0, data==1 */
+	 Inhibit=	0x10,		/*  keyboard/mouse inhibited */
+	 Minready=	0x20,		/*  mouse character ready */
+	 Rtimeout=	0x40,		/*  general timeout */
+	 Parity=	0x80,
+
+	Cmd=		0x64,		/* command port (write only) */
+
+	Spec=		0x80,
+
+	PF=		Spec|0x20,	/* num pad function key */
+	View=		Spec|0x00,	/* view (shift window up) */
+	KF=		0xF000,	/* function key (begin Unicode private space) */
+	Shift=		Spec|0x60,
+	Break=		Spec|0x61,
+	Ctrl=		Spec|0x62,
+	Latin=		Spec|0x63,
+	Caps=		Spec|0x64,
+	Num=		Spec|0x65,
+	Middle=		Spec|0x66,
+	No=		0x00,		/* peter */
+
+	Home=		KF|13,
+	Up=		KF|14,
+	Pgup=		KF|15,
+	Print=		KF|16,
+	Left=		KF|17,
+	Right=		KF|18,
+	End=		'\r',
+	Down=		View,
+	Pgdown=		KF|19,
+	Ins=		KF|20,
+	Del=		0x7F,
+	Scroll=		KF|21,
+};
+
+/*
+ * The codes at 0x79 and 0x81 are produed by the PFU Happy Hacking keyboard.
+ * A 'standard' keyboard doesn't produce anything above 0x58.
+ */
+Rune kbtab[] = 
+{
+[0x00]	No,	0x1b,	'1',	'2',	'3',	'4',	'5',	'6',
+[0x08]	'7',	'8',	'9',	'0',	'-',	'=',	'\b',	'\t',
+[0x10]	'q',	'w',	'e',	'r',	't',	'y',	'u',	'i',
+[0x18]	'o',	'p',	'[',	']',	'\n',	Ctrl,	'a',	's',
+[0x20]	'd',	'f',	'g',	'h',	'j',	'k',	'l',	';',
+[0x28]	'\'',	'`',	Shift,	'\\',	'z',	'x',	'c',	'v',
+[0x30]	'b',	'n',	'm',	',',	'.',	'/',	Shift,	'*',
+[0x38]	Latin,	' ',	Ctrl,	KF|1,	KF|2,	KF|3,	KF|4,	KF|5,
+[0x40]	KF|6,	KF|7,	KF|8,	KF|9,	KF|10,	Num,	Scroll,	'7',
+[0x48]	'8',	'9',	'-',	'4',	'5',	'6',	'+',	'1',
+[0x50]	'2',	'3',	'0',	'.',	No,	No,	No,	KF|11,
+[0x58]	KF|12,	No,	No,	No,	No,	No,	No,	No,
+[0x60]	No,	No,	No,	No,	No,	No,	No,	No,
+[0x68]	No,	No,	No,	No,	No,	No,	No,	No,
+[0x70]	No,	No,	No,	No,	No,	No,	No,	No,
+[0x78]	No,	View,	No,	Up,	No,	No,	No,	No,
+};
+
+Rune kbtabshift[] =
+{
+[0x00]	No,	0x1b,	'!',	'@',	'#',	'$',	'%',	'^',
+[0x08]	'&',	'*',	'(',	')',	'_',	'+',	'\b',	'\t',
+[0x10]	'Q',	'W',	'E',	'R',	'T',	'Y',	'U',	'I',
+[0x18]	'O',	'P',	'{',	'}',	'\n',	Ctrl,	'A',	'S',
+[0x20]	'D',	'F',	'G',	'H',	'J',	'K',	'L',	':',
+[0x28]	'"',	'~',	Shift,	'|',	'Z',	'X',	'C',	'V',
+[0x30]	'B',	'N',	'M',	'<',	'>',	'?',	Shift,	'*',
+[0x38]	Latin,	' ',	Ctrl,	KF|1,	KF|2,	KF|3,	KF|4,	KF|5,
+[0x40]	KF|6,	KF|7,	KF|8,	KF|9,	KF|10,	Num,	Scroll,	'7',
+[0x48]	'8',	'9',	'-',	'4',	'5',	'6',	'+',	'1',
+[0x50]	'2',	'3',	'0',	'.',	No,	No,	No,	KF|11,
+[0x58]	KF|12,	No,	No,	No,	No,	No,	No,	No,
+[0x60]	No,	No,	No,	No,	No,	No,	No,	No,
+[0x68]	No,	No,	No,	No,	No,	No,	No,	No,
+[0x70]	No,	No,	No,	No,	No,	No,	No,	No,
+[0x78]	No,	Up,	No,	Up,	No,	No,	No,	No,
+};
+
+Rune kbtabesc1[] =
+{
+[0x00]	No,	No,	No,	No,	No,	No,	No,	No,
+[0x08]	No,	No,	No,	No,	No,	No,	No,	No,
+[0x10]	No,	No,	No,	No,	No,	No,	No,	No,
+[0x18]	No,	No,	No,	No,	'\n',	Ctrl,	No,	No,
+[0x20]	No,	No,	No,	No,	No,	No,	No,	No,
+[0x28]	No,	No,	Shift,	No,	No,	No,	No,	No,
+[0x30]	No,	No,	No,	No,	No,	'/',	No,	Print,
+[0x38]	Latin,	No,	No,	No,	No,	No,	No,	No,
+[0x40]	No,	No,	No,	No,	No,	No,	Break,	Home,
+[0x48]	Up,	Pgup,	No,	Left,	No,	Right,	No,	End,
+[0x50]	Down,	Pgdown,	Ins,	Del,	No,	No,	No,	No,
+[0x58]	No,	No,	No,	No,	No,	No,	No,	No,
+[0x60]	No,	No,	No,	No,	No,	No,	No,	No,
+[0x68]	No,	No,	No,	No,	No,	No,	No,	No,
+[0x70]	No,	No,	No,	No,	No,	No,	No,	No,
+[0x78]	No,	Up,	No,	No,	No,	No,	No,	No,
+};
+
+enum
+{
+	/* controller command byte */
+	Cscs1=		(1<<6),		/* scan code set 1 */
+	Cauxdis=	(1<<5),		/* mouse disable */
+	Ckbddis=	(1<<4),		/* kbd disable */
+	Csf=		(1<<2),		/* system flag */
+	Cauxint=	(1<<1),		/* mouse interrupt enable */
+	Ckbdint=	(1<<0),		/* kbd interrupt enable */
+};
+
+int mouseshifted;
+
+static Lock i8042lock;
+static uchar ccc;
+static void (*auxputc)(int, int);
+
+/*
+ *  wait for output no longer busy
+ */
+static int
+outready(void)
+{
+	int tries;
+
+	for(tries = 0; (inb(Status) & Outbusy); tries++){
+		if(tries > 500)
+			return -1;
+		delay(2);
+	}
+	return 0;
+}
+
+/*
+ *  wait for input
+ */
+static int
+inready(void)
+{
+	int tries;
+
+	for(tries = 0; !(inb(Status) & Inready); tries++){
+		if(tries > 500)
+			return -1;
+		delay(2);
+	}
+	return 0;
+}
+
+/*
+ *  ask 8042 to reset the machine
+ */
+void
+i8042reset(void)
+{
+	ushort *s = KADDR(0x472);
+	int i, x;
+
+	*s = 0x1234;		/* BIOS warm-boot flag */
+
+	/*
+	 *  newer reset the machine command
+	 */
+	outready();
+	outb(Cmd, 0xFE);
+	outready();
+
+	/*
+	 *  Pulse it by hand (old somewhat reliable)
+	 */
+	x = 0xDF;
+	for(i = 0; i < 5; i++){
+		x ^= 1;
+		outready();
+		outb(Cmd, 0xD1);
+		outready();
+		outb(Data, x);	/* toggle reset */
+		delay(100);
+	}
+}
+
+int
+i8042auxcmd(int cmd)
+{
+	unsigned int c;
+	int tries;
+
+	c = 0;
+	tries = 0;
+
+	ilock(&i8042lock);
+	do{
+		if(tries++ > 2)
+			break;
+		if(outready() < 0)
+			break;
+		outb(Cmd, 0xD4);
+		if(outready() < 0)
+			break;
+		outb(Data, cmd);
+		if(outready() < 0)
+			break;
+		if(inready() < 0)
+			break;
+		c = inb(Data);
+	} while(c == 0xFE || c == 0);
+	iunlock(&i8042lock);
+
+	if(c != 0xFA){
+		print("i8042: %2.2ux returned to the %2.2ux command\n", c, cmd);
+		return -1;
+	}
+	return 0;
+}
+
+int
+i8042auxcmds(uchar *cmd, int ncmd)
+{
+	int i;
+
+	ilock(&i8042lock);
+	for(i=0; i<ncmd; i++){
+		if(outready() < 0)
+			break;
+		outb(Cmd, 0xD4);
+		if(outready() < 0)
+			break;
+		outb(Data, cmd[i]);
+	}
+	iunlock(&i8042lock);
+	return i;
+}
+
+/*
+ *  keyboard interrupt
+ */
+static void
+i8042intr(Ureg*, void*)
+{
+	int s, c, i;
+	static int esc1, esc2;
+	static int alt, caps, ctl, num, shift;
+	static int collecting, nk;
+	static Rune kc[5];
+	int keyup;
+
+	/*
+	 *  get status
+	 */
+	lock(&i8042lock);
+	s = inb(Status);
+	if(!(s&Inready)){
+		unlock(&i8042lock);
+		return;
+	}
+
+	/*
+	 *  get the character
+	 */
+	c = inb(Data);
+	unlock(&i8042lock);
+
+	/*
+	 *  if it's the aux port...
+	 */
+	if(s & Minready){
+		if(auxputc != nil)
+			auxputc(c, shift);
+		return;
+	}
+
+	/*
+	 *  e0's is the first of a 2 character sequence
+	 */
+	if(c == 0xe0){
+		esc1 = 1;
+		return;
+	} else if(c == 0xe1){
+		esc2 = 2;
+		return;
+	}
+
+	keyup = c&0x80;
+	c &= 0x7f;
+	if(c > sizeof kbtab){
+		c |= keyup;
+		if(c != 0xFF)	/* these come fairly often: CAPSLOCK U Y */
+			print("unknown key %ux\n", c);
+		return;
+	}
+
+	if(esc1){
+		c = kbtabesc1[c];
+		esc1 = 0;
+	} else if(esc2){
+		esc2--;
+		return;
+	} else if(shift)
+		c = kbtabshift[c];
+	else
+		c = kbtab[c];
+
+	if(caps && c<='z' && c>='a')
+		c += 'A' - 'a';
+
+	/*
+	 *  keyup only important for shifts
+	 */
+	if(keyup){
+		switch(c){
+		case Latin:
+			alt = 0;
+			break;
+		case Shift:
+			shift = 0;
+			mouseshifted = 0;
+			break;
+		case Ctrl:
+			ctl = 0;
+			break;
+		}
+		return;
+	}
+
+	/*
+ 	 *  normal character
+	 */
+	if(!(c & (Spec|KF))){
+		if(ctl){
+			if(alt && c == Del)
+				exit(0);
+			c &= 0x1f;
+		}
+		if(!collecting){
+			kbdputc(kbdq, c);
+			return;
+		}
+		kc[nk++] = c;
+		c = latin1(kc, nk);
+		if(c < -1)	/* need more keystrokes */
+			return;
+		if(c != -1)	/* valid sequence */
+			kbdputc(kbdq, c);
+		else	/* dump characters */
+			for(i=0; i<nk; i++)
+				kbdputc(kbdq, kc[i]);
+		nk = 0;
+		collecting = 0;
+		return;
+	} else {
+		switch(c){
+		case Caps:
+			caps ^= 1;
+			return;
+		case Num:
+			num ^= 1;
+			return;
+		case Shift:
+			shift = 1;
+			mouseshifted = 1;
+			return;
+		case Latin:
+			alt = 1;
+			/*
+			 * VMware uses Ctl-Alt as the key combination
+			 * to make the VM give up keyboard and mouse focus.
+			 * This has the unfortunate side effect that when you
+			 * come back into focus, Plan 9 thinks you want to type
+			 * a compose sequence (you just typed alt). 
+			 *
+			 * As a clumsy hack around this, we look for ctl-alt
+			 * and don't treat it as the start of a compose sequence.
+			 */
+			if(!ctl){
+				collecting = 1;
+				nk = 0;
+			}
+			return;
+		case Ctrl:
+			collecting = 0;
+			nk = 0;
+			ctl = 1;
+			return;
+		}
+	}
+	kbdputc(kbdq, c);
+}
+
+void
+i8042auxenable(void (*putc)(int, int))
+{
+	char *err = "i8042: aux init failed\n";
+
+	/* enable kbd/aux xfers and interrupts */
+	ccc &= ~Cauxdis;
+	ccc |= Cauxint;
+
+	ilock(&i8042lock);
+	if(outready() < 0)
+		print(err);
+	outb(Cmd, 0x60);			/* write control register */
+	if(outready() < 0)
+		print(err);
+	outb(Data, ccc);
+	if(outready() < 0)
+		print(err);
+	outb(Cmd, 0xA8);			/* auxilliary device enable */
+	if(outready() < 0){
+		iunlock(&i8042lock);
+		return;
+	}
+	auxputc = putc;
+	intrenable(IrqAUX, i8042intr, 0, BUSUNKNOWN, "kbdaux");
+	iunlock(&i8042lock);
+}
+
+void
+kbdinit(void)
+{
+	int c;
+
+	/* wait for a quiescent controller */
+	while((c = inb(Status)) & (Outbusy | Inready))
+		if(c & Inready)
+			inb(Data);
+
+	/* get current controller command byte */
+	outb(Cmd, 0x20);
+	if(inready() < 0){
+		print("kbdinit: can't read ccc\n");
+		ccc = 0;
+	} else
+		ccc = inb(Data);
+
+	/* enable kbd xfers and interrupts */
+	/* disable mouse */
+	ccc &= ~Ckbddis;
+	ccc |= Csf | Ckbdint | Cscs1;
+	if(outready() < 0)
+		print("kbd init failed\n");
+	outb(Cmd, 0x60);
+	if(outready() < 0)
+		print("kbd init failed\n");
+	outb(Data, ccc);
+	outready();
+}
+
+void
+kbdenable(void)
+{
+	if(kbdq == nil){
+		kbdq = qopen(4*1024, 0, 0, 0);
+		if(kbdq == nil)
+			panic("kbdinit");
+		qnoblock(kbdq, 1);
+	}
+
+	ioalloc(Data, 1, 0, "kbd");
+	ioalloc(Cmd, 1, 0, "kbd");
+
+	intrenable(IrqKBD, i8042intr, 0, BUSUNKNOWN, "kbd");
+}
--- /dev/null
+++ b/os/pc/l.s
@@ -1,0 +1,953 @@
+#include "mem.h"
+
+#define PADDR(a)	((a) & ~KZERO)
+#define KADDR(a)	(KZERO|(a))
+
+/*
+ * Some machine instructions not handled by 8[al].
+ */
+#define OP16		BYTE $0x66
+#define DELAY		BYTE $0xEB; BYTE $0x00	/* JMP .+2 */
+#define CPUID		BYTE $0x0F; BYTE $0xA2	/* CPUID, argument in AX */
+#define WRMSR		BYTE $0x0F; BYTE $0x30	/* WRMSR, argument in AX/DX (lo/hi) */
+#define RDTSC 		BYTE $0x0F; BYTE $0x31	/* RDTSC, result in AX/DX (lo/hi) */
+#define RDMSR		BYTE $0x0F; BYTE $0x32	/* RDMSR, result in AX/DX (lo/hi) */
+#define WBINVD		BYTE $0x0F; BYTE $0x09
+#define HLT		BYTE $0xF4
+
+/*
+ * Macros for calculating offsets within the page directory base
+ * and page tables. Note that these are assembler-specific hence
+ * the '<<2'.
+ */
+#define PDO(a)		(((((a))>>22) & 0x03FF)<<2)
+#define PTO(a)		(((((a))>>12) & 0x03FF)<<2)
+
+/*
+ * For backwards compatiblity with 9load - should go away when 9load is changed
+ * 9load currently sets up the mmu, however the first 16MB of memory is identity
+ * mapped, so behave as if the mmu was not setup
+ */
+TEXT _start0x80100020(SB), $0
+	MOVL	$_start0x00100020(SB), AX
+	ANDL	$~KZERO, AX
+	JMP*	AX
+
+/*
+ * Must be 4-byte aligned.
+ */
+TEXT _multibootheader(SB), $0
+	LONG	$0x1BADB002			/* magic */
+	LONG	$0x00010003			/* flags */
+	LONG	$-(0x1BADB002 + 0x00010003)	/* checksum */
+	LONG	$_multibootheader-KZERO(SB)	/* header_addr */
+	LONG	$_start0x80100020-KZERO(SB)	/* load_addr */
+	LONG	$edata-KZERO(SB)		/* load_end_addr */
+	LONG	$end-KZERO(SB)			/* bss_end_addr */
+	LONG	$_start0x80100020-KZERO(SB)	/* entry_addr */
+	LONG	$0				/* mode_type */
+	LONG	$0				/* width */
+	LONG	$0				/* height */
+	LONG	$0				/* depth */
+
+/*
+ * In protected mode with paging turned off and segment registers setup to linear map all memory.
+ * Entered via a jump to 0x00100020, the physical address of the virtual kernel entry point of 0x80100020
+ * Make the basic page tables for processor 0. Four pages are needed for the basic set:
+ * a page directory, a page table for mapping the first 4MB of physical memory to KZERO,
+ * and virtual and physical pages for mapping the Mach structure.
+ * The remaining PTEs will be allocated later when memory is sized.
+ * An identity mmu map is also needed for the switch to virtual mode.  This
+ * identity mapping is removed once the MMU is going and the JMP has been made
+ * to virtual memory.
+ */
+TEXT _start0x00100020(SB), $0
+	CLI					/* make sure interrupts are off */
+
+	/* set up the gdt so we have sane plan 9 style gdts. */
+	MOVL	$tgdtptr(SB), AX
+	ANDL	$~KZERO, AX
+	MOVL	(AX), GDTR
+	MOVW	$1, AX
+	MOVW	AX, MSW
+
+	/* clear prefetch queue (weird code to avoid optimizations) */
+	DELAY
+
+	/* set segs to something sane (avoid traps later) */
+	MOVW	$(1<<3), AX
+	MOVW	AX, DS
+	MOVW	AX, SS
+	MOVW	AX, ES
+	MOVW	AX, FS
+	MOVW	AX, GS
+
+/*	JMP	$(2<<3):$mode32bit(SB) /**/
+	 BYTE	$0xEA
+	 LONG	$mode32bit-KZERO(SB)
+	 WORD	$(2<<3)
+
+/*
+ *  gdt to get us to 32-bit/segmented/unpaged mode
+ */
+TEXT tgdt(SB), $0
+
+	/* null descriptor */
+	LONG	$0
+	LONG	$0
+
+	/* data segment descriptor for 4 gigabytes (PL 0) */
+	LONG	$(0xFFFF)
+	LONG	$(SEGG|SEGB|(0xF<<16)|SEGP|SEGPL(0)|SEGDATA|SEGW)
+
+	/* exec segment descriptor for 4 gigabytes (PL 0) */
+	LONG	$(0xFFFF)
+	LONG	$(SEGG|SEGD|(0xF<<16)|SEGP|SEGPL(0)|SEGEXEC|SEGR)
+
+/*
+ *  pointer to initial gdt
+ *  Note the -KZERO which puts the physical address in the gdtptr. 
+ *  that's needed as we start executing in physical addresses. 
+ */
+TEXT tgdtptr(SB), $0
+
+	WORD	$(3*8)
+	LONG	$tgdt-KZERO(SB)
+
+TEXT mode32bit(SB), $0
+	/* At this point, the GDT setup is done. */
+
+	MOVL	$PADDR(CPU0PDB), DI		/* clear 4 pages for the tables etc. */
+	XORL	AX, AX
+	MOVL	$(4*BY2PG), CX
+	SHRL	$2, CX
+
+	CLD
+	REP;	STOSL
+
+	MOVL	$PADDR(CPU0PDB), AX
+	ADDL	$PDO(KZERO), AX			/* page directory offset for KZERO */
+	MOVL	$PADDR(CPU0PTE), (AX)		/* PTE's for 0x80000000 */
+	MOVL	$(PTEWRITE|PTEVALID), BX	/* page permissions */
+	ORL	BX, (AX)
+
+	MOVL	$PADDR(CPU0PTE), AX		/* first page of page table */
+	MOVL	$1024, CX			/* 1024 pages in 4MB */
+_setpte:
+	MOVL	BX, (AX)
+	ADDL	$(1<<PGSHIFT), BX
+	ADDL	$4, AX
+	LOOP	_setpte
+
+	MOVL	$PADDR(CPU0PTE), AX
+	ADDL	$PTO(MACHADDR), AX		/* page table entry offset for MACHADDR */
+	MOVL	$PADDR(CPU0MACH), (AX)		/* PTE for Mach */
+	MOVL	$(PTEWRITE|PTEVALID), BX	/* page permissions */
+	ORL	BX, (AX)
+
+/*
+ * Now ready to use the new map. Make sure the processor options are what is wanted.
+ * It is necessary on some processors to immediately follow mode switching with a JMP instruction
+ * to clear the prefetch queues.
+ */
+	MOVL	$PADDR(CPU0PDB), CX		/* load address of page directory */
+	MOVL	(PDO(KZERO))(CX), DX		/* double-map KZERO at 0 */
+	MOVL	DX, (PDO(0))(CX)
+	MOVL	CX, CR3
+	DELAY					/* JMP .+2 */
+
+	MOVL	CR0, DX
+	ORL	$0x80010000, DX			/* PG|WP */
+	ANDL	$~0x6000000A, DX		/* ~(CD|NW|TS|MP) */
+
+	MOVL	$_startpg(SB), AX		/* this is a virtual address */
+	MOVL	DX, CR0				/* turn on paging */
+	JMP*	AX				/* jump to the virtual nirvana */
+
+/*
+ * Basic machine environment set, can clear BSS and create a stack.
+ * The stack starts at the top of the page containing the Mach structure.
+ * The x86 architecture forces the use of the same virtual address for
+ * each processor's Mach structure, so the global Mach pointer 'm' can
+ * be initialised here.
+ */
+TEXT _startpg(SB), $0
+	MOVL	$0, (PDO(0))(CX)		/* undo double-map of KZERO at 0 */
+	MOVL	CX, CR3				/* load and flush the mmu */
+
+_clearbss:
+	MOVL	$edata(SB), DI
+	XORL	AX, AX
+	MOVL	$end(SB), CX
+	SUBL	DI, CX				/* end-edata bytes */
+	SHRL	$2, CX				/* end-edata doublewords */
+
+	CLD
+	REP;	STOSL				/* clear BSS */
+
+	MOVL	$MACHADDR, SP
+	MOVL	SP, m(SB)			/* initialise global Mach pointer */
+	MOVL	$0, 0(SP)			/* initialise m->machno */
+
+	ADDL	$(MACHSIZE-4), SP		/* initialise stack */
+
+/*
+ * Need to do one final thing to ensure a clean machine environment,
+ * clear the EFLAGS register, which can only be done once there is a stack.
+ */
+	MOVL	$0, AX
+	PUSHL	AX
+	POPFL
+
+	CALL	main(SB)
+
+/*
+ * Park a processor. Should never fall through a return from main to here,
+ * should only be called by application processors when shutting down.
+ */
+TEXT idle(SB), $0
+_idle:
+	STI
+	HLT
+	JMP	_idle
+
+/*
+ * Port I/O.
+ *	in[bsl]		input a byte|short|long
+ *	ins[bsl]	input a string of bytes|shorts|longs
+ *	out[bsl]	output a byte|short|long
+ *	outs[bsl]	output a string of bytes|shorts|longs
+ */
+TEXT inb(SB), $0
+	MOVL	port+0(FP), DX
+	XORL	AX, AX
+	INB
+	RET
+
+TEXT insb(SB), $0
+	MOVL	port+0(FP), DX
+	MOVL	address+4(FP), DI
+	MOVL	count+8(FP), CX
+	CLD
+	REP;	INSB
+	RET
+
+TEXT ins(SB), $0
+	MOVL	port+0(FP), DX
+	XORL	AX, AX
+	OP16;	INL
+	RET
+
+TEXT inss(SB), $0
+	MOVL	port+0(FP), DX
+	MOVL	address+4(FP), DI
+	MOVL	count+8(FP), CX
+	CLD
+	REP;	OP16; INSL
+	RET
+
+TEXT inl(SB), $0
+	MOVL	port+0(FP), DX
+	INL
+	RET
+
+TEXT insl(SB), $0
+	MOVL	port+0(FP), DX
+	MOVL	address+4(FP), DI
+	MOVL	count+8(FP), CX
+	CLD
+	REP;	INSL
+	RET
+
+TEXT outb(SB), $0
+	MOVL	port+0(FP), DX
+	MOVL	byte+4(FP), AX
+	OUTB
+	RET
+
+TEXT outsb(SB), $0
+	MOVL	port+0(FP), DX
+	MOVL	address+4(FP), SI
+	MOVL	count+8(FP), CX
+	CLD
+	REP;	OUTSB
+	RET
+
+TEXT outs(SB), $0
+	MOVL	port+0(FP), DX
+	MOVL	short+4(FP), AX
+	OP16;	OUTL
+	RET
+
+TEXT outss(SB), $0
+	MOVL	port+0(FP), DX
+	MOVL	address+4(FP), SI
+	MOVL	count+8(FP), CX
+	CLD
+	REP;	OP16; OUTSL
+	RET
+
+TEXT outl(SB), $0
+	MOVL	port+0(FP), DX
+	MOVL	long+4(FP), AX
+	OUTL
+	RET
+
+TEXT outsl(SB), $0
+	MOVL	port+0(FP), DX
+	MOVL	address+4(FP), SI
+	MOVL	count+8(FP), CX
+	CLD
+	REP;	OUTSL
+	RET
+
+/* there's a macro in fns.h but libinterp can't see it */
+TEXT getcallerpc(SB), $0
+	MOVL	a+0(FP), AX
+	RET
+
+/*
+ * Read/write various system registers.
+ * CR4 and the 'model specific registers' should only be read/written
+ * after it has been determined the processor supports them
+ */
+TEXT lgdt(SB), $0				/* GDTR - global descriptor table */
+	MOVL	gdtptr+0(FP), AX
+	MOVL	(AX), GDTR
+	RET
+
+TEXT lidt(SB), $0				/* IDTR - interrupt descriptor table */
+	MOVL	idtptr+0(FP), AX
+	MOVL	(AX), IDTR
+	RET
+
+TEXT ltr(SB), $0				/* TR - task register */
+	MOVL	tptr+0(FP), AX
+	MOVW	AX, TASK
+	RET
+
+TEXT getcr0(SB), $0				/* CR0 - processor control */
+	MOVL	CR0, AX
+	RET
+
+TEXT getcr2(SB), $0				/* CR2 - page fault linear address */
+	MOVL	CR2, AX
+	RET
+
+TEXT getcr3(SB), $0				/* CR3 - page directory base */
+	MOVL	CR3, AX
+	RET
+
+TEXT putcr3(SB), $0
+	MOVL	cr3+0(FP), AX
+	MOVL	AX, CR3
+	RET
+
+TEXT getcr4(SB), $0				/* CR4 - extensions */
+	MOVL	CR4, AX
+	RET
+
+TEXT putcr4(SB), $0
+	MOVL	cr4+0(FP), AX
+	MOVL	AX, CR4
+	RET
+
+TEXT _cycles(SB), $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
+
+TEXT rdmsr(SB), $0				/* model-specific register */
+	MOVL	index+0(FP), CX
+	RDMSR
+	MOVL	vlong+4(FP), CX			/* &vlong */
+	MOVL	AX, 0(CX)			/* lo */
+	MOVL	DX, 4(CX)			/* hi */
+	RET
+	
+TEXT wrmsr(SB), $0
+	MOVL	index+0(FP), CX
+	MOVL	lo+4(FP), AX
+	MOVL	hi+8(FP), DX
+	WRMSR
+	RET
+
+TEXT wbinvd(SB), $0
+	WBINVD
+	RET
+
+TEXT	rdtsc32(SB), $0
+	CPUID
+	RDTSC
+	RET
+
+/*
+ * Try to determine the CPU type which requires fiddling with EFLAGS.
+ * If the Id bit can be toggled then the CPUID instruction can be used
+ * to determine CPU identity and features. First have to check if it's
+ * a 386 (Ac bit can't be set). If it's not a 386 and the Id bit can't be
+ * toggled then it's an older 486 of some kind.
+ *
+ *	cpuid(id[], &ax, &dx);
+ */
+TEXT cpuid(SB), $0
+	MOVL	$0x240000, AX
+	PUSHL	AX
+	POPFL					/* set Id|Ac */
+
+	PUSHFL
+	POPL	BX				/* retrieve value */
+
+	MOVL	$0, AX
+	PUSHL	AX
+	POPFL					/* clear Id|Ac, EFLAGS initialised */
+
+	PUSHFL
+	POPL	AX				/* retrieve value */
+	XORL	BX, AX
+	TESTL	$0x040000, AX			/* Ac */
+	JZ	_cpu386				/* can't set this bit on 386 */
+	TESTL	$0x200000, AX			/* Id */
+	JZ	_cpu486				/* can't toggle this bit on some 486 */
+
+	MOVL	$0, AX
+	CPUID
+	MOVL	id+0(FP), BP
+	MOVL	BX, 0(BP)			/* "Genu" "Auth" "Cyri" */
+	MOVL	DX, 4(BP)			/* "ineI" "enti" "xIns" */
+	MOVL	CX, 8(BP)			/* "ntel" "cAMD" "tead" */
+
+	MOVL	$1, AX
+	CPUID
+	JMP	_cpuid
+
+_cpu486:
+	MOVL	$0x400, AX
+	MOVL	$0, DX
+	JMP	_cpuid
+
+_cpu386:
+	MOVL	$0x300, AX
+	MOVL	$0, DX
+
+_cpuid:
+	MOVL	ax+4(FP), BP
+	MOVL	AX, 0(BP)
+	MOVL	dx+8(FP), BP
+	MOVL	DX, 0(BP)
+	RET
+
+/*
+ * Basic timing loop to determine CPU frequency.
+ */
+TEXT aamloop(SB), $0
+	MOVL	count+0(FP), CX
+_aamloop:
+	AAM
+	LOOP	_aamloop
+	RET
+
+/*
+ * Floating point.
+ * Note: the encodings for the FCLEX, FINIT, FSAVE, FSTCW, FSENV and FSTSW
+ * instructions do NOT have the WAIT prefix byte (i.e. they act like their
+ * FNxxx variations) so WAIT instructions must be explicitly placed in the
+ * code as necessary.
+ */
+#define	FPOFF(l)							;\
+	MOVL	CR0, AX 					 	;\
+	ANDL	$0xC, AX			/* EM, TS */	 	;\
+	CMPL	AX, $0x8					 	;\
+	JEQ 	l						 	;\
+	WAIT							 	;\
+l:								 	;\
+	MOVL	CR0, AX							;\
+	ANDL	$~0x4, AX			/* EM=0 */		;\
+	ORL	$0x28, AX			/* NE=1, TS=1 */	;\
+	MOVL	AX, CR0
+
+#define	FPON								;\
+	MOVL	CR0, AX							;\
+	ANDL	$~0xC, AX			/* EM=0, TS=0 */	;\
+	MOVL	AX, CR0
+	
+TEXT fpoff(SB), $0				/* disable */
+	FPOFF(l1)
+	RET
+
+TEXT fpinit(SB), $0				/* enable and init */
+	FPON
+	FINIT
+	WAIT
+	/* setfcr(FPPDBL|FPRNR|FPINVAL|FPZDIV|FPOVFL) */
+	/* note that low 6 bits are masks, not enables, on this chip */
+	PUSHW	$0x0232
+	FLDCW	0(SP)
+	POPW	AX
+	WAIT
+	RET
+
+TEXT fpsave(SB), $0				/* save state and disable */
+	MOVL	p+0(FP), AX
+	FSAVE	0(AX)	/* no WAIT */
+	FPOFF(l2)
+	RET
+
+TEXT fprestore(SB), $0				/* enable and restore state */
+	FPON
+	MOVL	p+0(FP), AX
+	FRSTOR	0(AX)
+	WAIT
+	RET
+
+TEXT fpstatus(SB), $0				/* get floating point status */
+	FSTSW	AX
+	RET
+
+TEXT fpenv(SB), $0				/* save state without waiting */
+	MOVL	p+0(FP), AX
+	FSTENV	0(AX)
+	RET
+
+TEXT fpclear(SB), $0				/* clear pending exceptions */
+	FPON
+	FCLEX					/* no WAIT */
+	FPOFF(l3)
+	RET
+
+/*
+ */
+TEXT splhi(SB), $0
+	MOVL	$(MACHADDR+0x04), AX 		/* save PC in m->splpc */
+	MOVL	(SP), BX
+	MOVL	BX, (AX)
+
+	PUSHFL
+	POPL	AX
+	CLI
+	RET
+
+TEXT spllo(SB), $0
+	PUSHFL
+	POPL	AX
+	STI
+	RET
+
+TEXT splx(SB), $0
+	MOVL	$(MACHADDR+0x04), AX 		/* save PC in m->splpc */
+	MOVL	(SP), BX
+	MOVL	BX, (AX)
+	/*FALLTHROUGH*/
+
+TEXT splxpc(SB), $0				/* for iunlock */
+	MOVL	s+0(FP), AX
+	PUSHL	AX
+	POPFL
+	RET
+
+TEXT spldone(SB), $0
+	RET
+
+TEXT islo(SB), $0
+	PUSHFL
+	POPL	AX
+	ANDL	$0x200, AX			/* interrupt enable flag */
+	RET
+
+/*
+ * Test-And-Set
+ */
+TEXT _tas(SB), $0
+	MOVL	$0xDEADDEAD, AX
+	MOVL	lock+0(FP), BX
+	XCHGL	AX, (BX)			/* lock->key */
+	RET
+
+TEXT _xinc(SB), $0				/* void _xinc(long*); */
+	MOVL	l+0(FP), AX
+	LOCK;	INCL 0(AX)
+	RET
+
+TEXT _xdec(SB), $0				/* long _xdec(long*); */
+	MOVL	l+0(FP), BX
+	XORL	AX, AX
+	LOCK;	DECL 0(BX)
+	JLT	_xdeclt
+	JGT	_xdecgt
+	RET
+_xdecgt:
+	INCL	AX
+	RET
+_xdeclt:
+	DECL	AX
+	RET
+
+TEXT mb386(SB), $0
+	POPL	AX				/* return PC */
+	PUSHFL
+	PUSHL	CS
+	PUSHL	AX
+	IRETL
+
+TEXT mb586(SB), $0
+	XORL	AX, AX
+	CPUID
+	RET
+
+TEXT xchgw(SB), $0
+	MOVL	v+4(FP), AX
+	MOVL	p+0(FP), BX
+	XCHGW	AX, (BX)
+	RET
+
+TEXT mul64fract(SB), $0
+/*
+ * Multiply two 64-bit number s and keep the middle 64 bits from the 128-bit result
+ * See ../port/tod.c for motivation.
+ */
+	MOVL	r+0(FP), CX
+	XORL	BX, BX				/* BX = 0 */
+
+	MOVL	a+8(FP), AX
+	MULL	b+16(FP)			/* a1*b1 */
+	MOVL	AX, 4(CX)			/* r2 = lo(a1*b1) */
+
+	MOVL	a+8(FP), AX
+	MULL	b+12(FP)			/* a1*b0 */
+	MOVL	AX, 0(CX)			/* r1 = lo(a1*b0) */
+	ADDL	DX, 4(CX)			/* r2 += hi(a1*b0) */
+
+	MOVL	a+4(FP), AX
+	MULL	b+16(FP)			/* a0*b1 */
+	ADDL	AX, 0(CX)			/* r1 += lo(a0*b1) */
+	ADCL	DX, 4(CX)			/* r2 += hi(a0*b1) + carry */
+
+	MOVL	a+4(FP), AX
+	MULL	b+12(FP)			/* a0*b0 */
+	ADDL	DX, 0(CX)			/* r1 += hi(a0*b0) */
+	ADCL	BX, 4(CX)			/* r2 += carry */
+	RET
+
+/*
+ *  label consists of a stack pointer and a PC
+ */
+TEXT gotolabel(SB), $0
+	MOVL	label+0(FP), AX
+	MOVL	0(AX), SP			/* restore sp */
+	MOVL	4(AX), AX			/* put return pc on the stack */
+	MOVL	AX, 0(SP)
+	MOVL	$1, AX				/* return 1 */
+	RET
+
+TEXT setlabel(SB), $0
+	MOVL	label+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
+
+TEXT halt(SB), $0
+	STI
+	HLT
+	RET
+
+/*
+ * Interrupt/exception handling.
+ * Each entry in the vector table calls either _strayintr or _strayintrx depending
+ * on whether an error code has been automatically pushed onto the stack
+ * (_strayintrx) or not, in which case a dummy entry must be pushed before retrieving
+ * the trap type from the vector table entry and placing it on the stack as part
+ * of the Ureg structure.
+ * The size of each entry in the vector table (6 bytes) is known in trapinit().
+ */
+TEXT _strayintr(SB), $0
+	PUSHL	AX				/* save AX */
+	MOVL	4(SP), AX			/* return PC from vectortable(SB) */
+	JMP	intrcommon
+
+TEXT _strayintrx(SB), $0
+	XCHGL	AX, (SP)			/* exchange AX with pointer to trap type */
+intrcommon:
+	PUSHL	DS
+	MOVBLZX	(AX), AX			/* trap type -> AX */
+	XCHGL	AX, 4(SP)			/* exchange trap type with AX */
+	PUSHL	ES
+	PUSHL	FS
+	PUSHL	GS
+	PUSHAL
+	MOVL	$(KDSEL), AX
+	MOVW	AX, DS
+	MOVW	AX, ES
+	PUSHL	SP			/* Ureg* argument to trap */
+	CALL	trap(SB)
+
+TEXT forkret(SB), $0
+	POPL	AX
+	POPAL
+	POPL	GS
+	POPL	FS
+	POPL	ES
+	POPL	DS
+	ADDL	$8, SP				/* pop error code and trap type */
+	IRETL
+
+TEXT vectortable(SB), $0
+	CALL _strayintr(SB); BYTE $0x00		/* divide error */
+	CALL _strayintr(SB); BYTE $0x01		/* debug exception */
+	CALL _strayintr(SB); BYTE $0x02		/* NMI interrupt */
+	CALL _strayintr(SB); BYTE $0x03		/* breakpoint */
+	CALL _strayintr(SB); BYTE $0x04		/* overflow */
+	CALL _strayintr(SB); BYTE $0x05		/* bound */
+	CALL _strayintr(SB); BYTE $0x06		/* invalid opcode */
+	CALL _strayintr(SB); BYTE $0x07		/* no coprocessor available */
+	CALL _strayintrx(SB); BYTE $0x08	/* double fault */
+	CALL _strayintr(SB); BYTE $0x09		/* coprocessor segment overflow */
+	CALL _strayintrx(SB); BYTE $0x0A	/* invalid TSS */
+	CALL _strayintrx(SB); BYTE $0x0B	/* segment not available */
+	CALL _strayintrx(SB); BYTE $0x0C	/* stack exception */
+	CALL _strayintrx(SB); BYTE $0x0D	/* general protection error */
+	CALL _strayintrx(SB); BYTE $0x0E	/* page fault */
+	CALL _strayintr(SB); BYTE $0x0F		/*  */
+	CALL _strayintr(SB); BYTE $0x10		/* coprocessor error */
+	CALL _strayintrx(SB); BYTE $0x11	/* alignment check */
+	CALL _strayintr(SB); BYTE $0x12		/* machine check */
+	CALL _strayintr(SB); BYTE $0x13
+	CALL _strayintr(SB); BYTE $0x14
+	CALL _strayintr(SB); BYTE $0x15
+	CALL _strayintr(SB); BYTE $0x16
+	CALL _strayintr(SB); BYTE $0x17
+	CALL _strayintr(SB); BYTE $0x18
+	CALL _strayintr(SB); BYTE $0x19
+	CALL _strayintr(SB); BYTE $0x1A
+	CALL _strayintr(SB); BYTE $0x1B
+	CALL _strayintr(SB); BYTE $0x1C
+	CALL _strayintr(SB); BYTE $0x1D
+	CALL _strayintr(SB); BYTE $0x1E
+	CALL _strayintr(SB); BYTE $0x1F
+	CALL _strayintr(SB); BYTE $0x20		/* VectorLAPIC */
+	CALL _strayintr(SB); BYTE $0x21
+	CALL _strayintr(SB); BYTE $0x22
+	CALL _strayintr(SB); BYTE $0x23
+	CALL _strayintr(SB); BYTE $0x24
+	CALL _strayintr(SB); BYTE $0x25
+	CALL _strayintr(SB); BYTE $0x26
+	CALL _strayintr(SB); BYTE $0x27
+	CALL _strayintr(SB); BYTE $0x28
+	CALL _strayintr(SB); BYTE $0x29
+	CALL _strayintr(SB); BYTE $0x2A
+	CALL _strayintr(SB); BYTE $0x2B
+	CALL _strayintr(SB); BYTE $0x2C
+	CALL _strayintr(SB); BYTE $0x2D
+	CALL _strayintr(SB); BYTE $0x2E
+	CALL _strayintr(SB); BYTE $0x2F
+	CALL _strayintr(SB); BYTE $0x30
+	CALL _strayintr(SB); BYTE $0x31
+	CALL _strayintr(SB); BYTE $0x32
+	CALL _strayintr(SB); BYTE $0x33
+	CALL _strayintr(SB); BYTE $0x34
+	CALL _strayintr(SB); BYTE $0x35
+	CALL _strayintr(SB); BYTE $0x36
+	CALL _strayintr(SB); BYTE $0x37
+	CALL _strayintr(SB); BYTE $0x38
+	CALL _strayintr(SB); BYTE $0x39
+	CALL _strayintr(SB); BYTE $0x3A
+	CALL _strayintr(SB); BYTE $0x3B
+	CALL _strayintr(SB); BYTE $0x3C
+	CALL _strayintr(SB); BYTE $0x3D
+	CALL _strayintr(SB); BYTE $0x3E
+	CALL _strayintr(SB); BYTE $0x3F
+	CALL _strayintr(SB); BYTE $0x40		/* VectorSYSCALL */
+	CALL _strayintr(SB); BYTE $0x41
+	CALL _strayintr(SB); BYTE $0x42
+	CALL _strayintr(SB); BYTE $0x43
+	CALL _strayintr(SB); BYTE $0x44
+	CALL _strayintr(SB); BYTE $0x45
+	CALL _strayintr(SB); BYTE $0x46
+	CALL _strayintr(SB); BYTE $0x47
+	CALL _strayintr(SB); BYTE $0x48
+	CALL _strayintr(SB); BYTE $0x49
+	CALL _strayintr(SB); BYTE $0x4A
+	CALL _strayintr(SB); BYTE $0x4B
+	CALL _strayintr(SB); BYTE $0x4C
+	CALL _strayintr(SB); BYTE $0x4D
+	CALL _strayintr(SB); BYTE $0x4E
+	CALL _strayintr(SB); BYTE $0x4F
+	CALL _strayintr(SB); BYTE $0x50
+	CALL _strayintr(SB); BYTE $0x51
+	CALL _strayintr(SB); BYTE $0x52
+	CALL _strayintr(SB); BYTE $0x53
+	CALL _strayintr(SB); BYTE $0x54
+	CALL _strayintr(SB); BYTE $0x55
+	CALL _strayintr(SB); BYTE $0x56
+	CALL _strayintr(SB); BYTE $0x57
+	CALL _strayintr(SB); BYTE $0x58
+	CALL _strayintr(SB); BYTE $0x59
+	CALL _strayintr(SB); BYTE $0x5A
+	CALL _strayintr(SB); BYTE $0x5B
+	CALL _strayintr(SB); BYTE $0x5C
+	CALL _strayintr(SB); BYTE $0x5D
+	CALL _strayintr(SB); BYTE $0x5E
+	CALL _strayintr(SB); BYTE $0x5F
+	CALL _strayintr(SB); BYTE $0x60
+	CALL _strayintr(SB); BYTE $0x61
+	CALL _strayintr(SB); BYTE $0x62
+	CALL _strayintr(SB); BYTE $0x63
+	CALL _strayintr(SB); BYTE $0x64
+	CALL _strayintr(SB); BYTE $0x65
+	CALL _strayintr(SB); BYTE $0x66
+	CALL _strayintr(SB); BYTE $0x67
+	CALL _strayintr(SB); BYTE $0x68
+	CALL _strayintr(SB); BYTE $0x69
+	CALL _strayintr(SB); BYTE $0x6A
+	CALL _strayintr(SB); BYTE $0x6B
+	CALL _strayintr(SB); BYTE $0x6C
+	CALL _strayintr(SB); BYTE $0x6D
+	CALL _strayintr(SB); BYTE $0x6E
+	CALL _strayintr(SB); BYTE $0x6F
+	CALL _strayintr(SB); BYTE $0x70
+	CALL _strayintr(SB); BYTE $0x71
+	CALL _strayintr(SB); BYTE $0x72
+	CALL _strayintr(SB); BYTE $0x73
+	CALL _strayintr(SB); BYTE $0x74
+	CALL _strayintr(SB); BYTE $0x75
+	CALL _strayintr(SB); BYTE $0x76
+	CALL _strayintr(SB); BYTE $0x77
+	CALL _strayintr(SB); BYTE $0x78
+	CALL _strayintr(SB); BYTE $0x79
+	CALL _strayintr(SB); BYTE $0x7A
+	CALL _strayintr(SB); BYTE $0x7B
+	CALL _strayintr(SB); BYTE $0x7C
+	CALL _strayintr(SB); BYTE $0x7D
+	CALL _strayintr(SB); BYTE $0x7E
+	CALL _strayintr(SB); BYTE $0x7F
+	CALL _strayintr(SB); BYTE $0x80		/* Vector[A]PIC */
+	CALL _strayintr(SB); BYTE $0x81
+	CALL _strayintr(SB); BYTE $0x82
+	CALL _strayintr(SB); BYTE $0x83
+	CALL _strayintr(SB); BYTE $0x84
+	CALL _strayintr(SB); BYTE $0x85
+	CALL _strayintr(SB); BYTE $0x86
+	CALL _strayintr(SB); BYTE $0x87
+	CALL _strayintr(SB); BYTE $0x88
+	CALL _strayintr(SB); BYTE $0x89
+	CALL _strayintr(SB); BYTE $0x8A
+	CALL _strayintr(SB); BYTE $0x8B
+	CALL _strayintr(SB); BYTE $0x8C
+	CALL _strayintr(SB); BYTE $0x8D
+	CALL _strayintr(SB); BYTE $0x8E
+	CALL _strayintr(SB); BYTE $0x8F
+	CALL _strayintr(SB); BYTE $0x90
+	CALL _strayintr(SB); BYTE $0x91
+	CALL _strayintr(SB); BYTE $0x92
+	CALL _strayintr(SB); BYTE $0x93
+	CALL _strayintr(SB); BYTE $0x94
+	CALL _strayintr(SB); BYTE $0x95
+	CALL _strayintr(SB); BYTE $0x96
+	CALL _strayintr(SB); BYTE $0x97
+	CALL _strayintr(SB); BYTE $0x98
+	CALL _strayintr(SB); BYTE $0x99
+	CALL _strayintr(SB); BYTE $0x9A
+	CALL _strayintr(SB); BYTE $0x9B
+	CALL _strayintr(SB); BYTE $0x9C
+	CALL _strayintr(SB); BYTE $0x9D
+	CALL _strayintr(SB); BYTE $0x9E
+	CALL _strayintr(SB); BYTE $0x9F
+	CALL _strayintr(SB); BYTE $0xA0
+	CALL _strayintr(SB); BYTE $0xA1
+	CALL _strayintr(SB); BYTE $0xA2
+	CALL _strayintr(SB); BYTE $0xA3
+	CALL _strayintr(SB); BYTE $0xA4
+	CALL _strayintr(SB); BYTE $0xA5
+	CALL _strayintr(SB); BYTE $0xA6
+	CALL _strayintr(SB); BYTE $0xA7
+	CALL _strayintr(SB); BYTE $0xA8
+	CALL _strayintr(SB); BYTE $0xA9
+	CALL _strayintr(SB); BYTE $0xAA
+	CALL _strayintr(SB); BYTE $0xAB
+	CALL _strayintr(SB); BYTE $0xAC
+	CALL _strayintr(SB); BYTE $0xAD
+	CALL _strayintr(SB); BYTE $0xAE
+	CALL _strayintr(SB); BYTE $0xAF
+	CALL _strayintr(SB); BYTE $0xB0
+	CALL _strayintr(SB); BYTE $0xB1
+	CALL _strayintr(SB); BYTE $0xB2
+	CALL _strayintr(SB); BYTE $0xB3
+	CALL _strayintr(SB); BYTE $0xB4
+	CALL _strayintr(SB); BYTE $0xB5
+	CALL _strayintr(SB); BYTE $0xB6
+	CALL _strayintr(SB); BYTE $0xB7
+	CALL _strayintr(SB); BYTE $0xB8
+	CALL _strayintr(SB); BYTE $0xB9
+	CALL _strayintr(SB); BYTE $0xBA
+	CALL _strayintr(SB); BYTE $0xBB
+	CALL _strayintr(SB); BYTE $0xBC
+	CALL _strayintr(SB); BYTE $0xBD
+	CALL _strayintr(SB); BYTE $0xBE
+	CALL _strayintr(SB); BYTE $0xBF
+	CALL _strayintr(SB); BYTE $0xC0
+	CALL _strayintr(SB); BYTE $0xC1
+	CALL _strayintr(SB); BYTE $0xC2
+	CALL _strayintr(SB); BYTE $0xC3
+	CALL _strayintr(SB); BYTE $0xC4
+	CALL _strayintr(SB); BYTE $0xC5
+	CALL _strayintr(SB); BYTE $0xC6
+	CALL _strayintr(SB); BYTE $0xC7
+	CALL _strayintr(SB); BYTE $0xC8
+	CALL _strayintr(SB); BYTE $0xC9
+	CALL _strayintr(SB); BYTE $0xCA
+	CALL _strayintr(SB); BYTE $0xCB
+	CALL _strayintr(SB); BYTE $0xCC
+	CALL _strayintr(SB); BYTE $0xCD
+	CALL _strayintr(SB); BYTE $0xCE
+	CALL _strayintr(SB); BYTE $0xCF
+	CALL _strayintr(SB); BYTE $0xD0
+	CALL _strayintr(SB); BYTE $0xD1
+	CALL _strayintr(SB); BYTE $0xD2
+	CALL _strayintr(SB); BYTE $0xD3
+	CALL _strayintr(SB); BYTE $0xD4
+	CALL _strayintr(SB); BYTE $0xD5
+	CALL _strayintr(SB); BYTE $0xD6
+	CALL _strayintr(SB); BYTE $0xD7
+	CALL _strayintr(SB); BYTE $0xD8
+	CALL _strayintr(SB); BYTE $0xD9
+	CALL _strayintr(SB); BYTE $0xDA
+	CALL _strayintr(SB); BYTE $0xDB
+	CALL _strayintr(SB); BYTE $0xDC
+	CALL _strayintr(SB); BYTE $0xDD
+	CALL _strayintr(SB); BYTE $0xDE
+	CALL _strayintr(SB); BYTE $0xDF
+	CALL _strayintr(SB); BYTE $0xE0
+	CALL _strayintr(SB); BYTE $0xE1
+	CALL _strayintr(SB); BYTE $0xE2
+	CALL _strayintr(SB); BYTE $0xE3
+	CALL _strayintr(SB); BYTE $0xE4
+	CALL _strayintr(SB); BYTE $0xE5
+	CALL _strayintr(SB); BYTE $0xE6
+	CALL _strayintr(SB); BYTE $0xE7
+	CALL _strayintr(SB); BYTE $0xE8
+	CALL _strayintr(SB); BYTE $0xE9
+	CALL _strayintr(SB); BYTE $0xEA
+	CALL _strayintr(SB); BYTE $0xEB
+	CALL _strayintr(SB); BYTE $0xEC
+	CALL _strayintr(SB); BYTE $0xED
+	CALL _strayintr(SB); BYTE $0xEE
+	CALL _strayintr(SB); BYTE $0xEF
+	CALL _strayintr(SB); BYTE $0xF0
+	CALL _strayintr(SB); BYTE $0xF1
+	CALL _strayintr(SB); BYTE $0xF2
+	CALL _strayintr(SB); BYTE $0xF3
+	CALL _strayintr(SB); BYTE $0xF4
+	CALL _strayintr(SB); BYTE $0xF5
+	CALL _strayintr(SB); BYTE $0xF6
+	CALL _strayintr(SB); BYTE $0xF7
+	CALL _strayintr(SB); BYTE $0xF8
+	CALL _strayintr(SB); BYTE $0xF9
+	CALL _strayintr(SB); BYTE $0xFA
+	CALL _strayintr(SB); BYTE $0xFB
+	CALL _strayintr(SB); BYTE $0xFC
+	CALL _strayintr(SB); BYTE $0xFD
+	CALL _strayintr(SB); BYTE $0xFE
+	CALL _strayintr(SB); BYTE $0xFF
--- /dev/null
+++ b/os/pc/main.c
@@ -1,0 +1,468 @@
+#include	"u.h"
+#include	"../port/lib.h"
+#include	"mem.h"
+#include	"dat.h"
+#include	"fns.h"
+#include	"io.h"
+#include	"ureg.h"
+
+extern int main_pool_pcnt;
+extern int heap_pool_pcnt;
+extern int image_pool_pcnt;
+int	pckdebug;
+
+Mach *m;
+
+static  uchar *sp;	/* stack pointer for /boot */
+
+/*
+ * Where configuration info is left for the loaded programme.
+ * This will turn into a structure as more is done by the boot loader
+ * (e.g. why parse the .ini file twice?).
+ * There are 3584 bytes available at CONFADDR.
+ */
+#define BOOTLINE	((char*)CONFADDR)
+#define BOOTLINELEN	64
+#define BOOTARGS	((char*)(CONFADDR+BOOTLINELEN))
+#define	BOOTARGSLEN	(4096-0x200-BOOTLINELEN)
+#define	MAXCONF		64
+
+char bootdisk[KNAMELEN];
+char *confname[MAXCONF];
+char *confval[MAXCONF];
+int nconf;
+
+static void
+options(void)
+{
+	long i, n;
+	char *cp, *line[MAXCONF], *p, *q;
+
+	/*
+	 *  parse configuration args from dos file plan9.ini
+	 */
+	cp = BOOTARGS;	/* where b.com leaves its config */
+	cp[BOOTARGSLEN-1] = 0;
+
+	/*
+	 * Strip out '\r', change '\t' -> ' '.
+	 */
+	p = cp;
+	for(q = cp; *q; q++){
+		if(*q == '\r')
+			continue;
+		if(*q == '\t')
+			*q = ' ';
+		*p++ = *q;
+	}
+	*p = 0;
+
+	n = getfields(cp, line, MAXCONF, 1, "\n");
+	for(i = 0; i < n; i++){
+		if(*line[i] == '#')
+			continue;
+		cp = strchr(line[i], '=');
+		if(cp == nil)
+			continue;
+		*cp++ = '\0';
+		confname[nconf] = line[i];
+		confval[nconf] = cp;
+		nconf++;
+	}
+}
+
+static void
+doc(char *m)
+{
+	int i;
+	print("%s...\n", m);
+	for(i = 0; i < 100*1024*1024; i++)
+		i++;
+}
+
+void
+main(void)
+{
+	outb(0x3F2, 0x00);			/* botch: turn off the floppy motor */
+
+	mach0init();
+	options();
+	ioinit();
+	i8250console();
+	quotefmtinstall();
+	kbdinit();
+	i8253init();
+	cpuidentify();
+	confinit();
+	archinit();
+	xinit();
+	poolsizeinit();
+	trapinit();
+	printinit();
+	screeninit();
+	cpuidprint();
+	mmuinit();
+	eve = strdup("inferno");
+	if(arch->intrinit){	/* launches other processors on an mp */
+		doc("intrinit");
+		arch->intrinit();
+	}
+	doc("timersinit");
+	timersinit();
+	doc("mathinit");
+	mathinit();
+	doc("kbdenable");
+	kbdenable();
+	if(arch->clockenable){
+		doc("clockinit");
+		arch->clockenable();
+	}
+	doc("procinit");
+	procinit();
+	doc("links");
+	links();
+	doc("chandevreset");
+	chandevreset();
+	doc("userinit");
+	userinit();
+	doc("schedinit");
+	active.thunderbirdsarego = 1;
+	schedinit();
+	
+}
+
+void
+mach0init(void)
+{
+	conf.nmach = 1;
+	MACHP(0) = (Mach*)CPU0MACH;
+	m->pdb = (ulong*)CPU0PDB;
+	m->gdt = (Segdesc*)CPU0GDT;
+
+	machinit();
+
+	active.machs = 1;
+	active.exiting = 0;
+}
+
+void
+machinit(void)
+{
+	int machno;
+	ulong *pdb;
+	Segdesc *gdt;
+
+	machno = m->machno;
+	pdb = m->pdb;
+	gdt = m->gdt;
+	memset(m, 0, sizeof(Mach));
+	m->machno = machno;
+	m->pdb = pdb;
+	m->gdt = gdt;
+
+	/*
+	 * For polled uart output at boot, need
+	 * a default delay constant. 100000 should
+	 * be enough for a while. Cpuidentify will
+	 * calculate the real value later.
+	 */
+	m->loopconst = 100000;
+}
+
+void
+init0(void)
+{
+	Osenv *o;
+	int i;
+	char buf[2*KNAMELEN];
+
+	up->nerrlab = 0;
+
+	spllo();
+	if(waserror())
+		panic("init0: %r");
+	/*
+	 * These are o.k. because rootinit is null.
+	 * Then early kproc's will have a root and dot.
+	 */
+	o = up->env;
+	o->pgrp->slash = namec("#/", Atodir, 0, 0);
+	cnameclose(o->pgrp->slash->name);
+	o->pgrp->slash->name = newcname("/");
+	o->pgrp->dot = cclone(o->pgrp->slash);
+
+	chandevinit();
+
+	if(!waserror()){
+		ksetenv("cputype", "386", 0);
+		snprint(buf, sizeof(buf), "386 %s", conffile);
+		ksetenv("terminal", buf, 0);
+		for(i = 0; i < nconf; i++){
+			if(confname[i][0] != '*')
+				ksetenv(confname[i], confval[i], 0);
+			ksetenv(confname[i], confval[i], 1);
+		}
+		poperror();
+	}
+
+	poperror();
+
+	disinit("/osinit.dis");
+}
+
+void
+userinit(void)
+{
+	Proc *p;
+	Osenv *o;
+
+	p = newproc();
+	o = p->env;
+
+	o->fgrp = newfgrp(nil);
+
+	o->pgrp = newpgrp();
+	kstrdup(&o->user, eve);
+
+	strcpy(p->text, "interp");
+
+	p->fpstate = FPINIT;
+	fpoff();
+
+	/*
+	 * Kernel Stack
+	 *
+	 * N.B. make sure there's
+	 *	4 bytes for gotolabel's return PC
+	 */
+	p->sched.pc = (ulong)init0;
+	p->sched.sp = (ulong)p->kstack+KSTACK-BY2WD;
+
+	ready(p);
+}
+
+Conf	conf;
+
+char*
+getconf(char *name)
+{
+	int i;
+
+	for(i = 0; i < nconf; i++)
+		if(cistrcmp(confname[i], name) == 0)
+			return confval[i];
+	return 0;
+}
+
+void
+confinit(void)
+{
+	char *p;
+	int pcnt;
+	ulong maxmem;
+
+	if(p = getconf("*maxmem"))
+		maxmem = strtoul(p, 0, 0);
+	else
+		maxmem = 0;
+	if(p = getconf("*kernelpercent"))
+		pcnt = 100 - strtol(p, 0, 0);
+	else
+		pcnt = 0;
+
+	meminit(maxmem);
+
+	conf.npage = conf.npage0 + conf.npage1;
+	if(pcnt < 10)
+		pcnt = 70;
+	conf.ialloc = (((conf.npage*(100-pcnt))/100)/2)*BY2PG;
+
+	conf.nproc = 100 + ((conf.npage*BY2PG)/MB)*5;
+}
+
+void
+poolsizeinit(void)
+{
+	ulong nb;
+
+	nb = conf.npage*BY2PG;
+	poolsize(mainmem, (nb*main_pool_pcnt)/100, 0);
+	poolsize(heapmem, (nb*heap_pool_pcnt)/100, 0);
+	poolsize(imagmem, (nb*image_pool_pcnt)/100, 1);
+}
+
+static char *mathmsg[] =
+{
+	"invalid operation",
+	"denormalized operand",
+	"division by zero",
+	"numeric overflow",
+	"numeric underflow",
+	"precision loss",
+	"stack",
+	"error",
+};
+
+/*
+ *  math coprocessor error
+ */
+void
+matherror(Ureg* ureg, void* arg)
+{
+	ulong status;
+	int i;
+	char *msg;
+	char note[ERRMAX];
+
+	USED(arg);
+
+	/*
+	 *  a write cycle to port 0xF0 clears the interrupt latch attached
+	 *  to the error# line from the 387
+	 */
+	if(!(m->cpuiddx & 0x01))
+		outb(0xF0, 0xFF);
+
+	/*
+	 *  save floating point state to check out error
+	 */
+	FPsave(&up->fpsave.env);
+	status = up->fpsave.env.status;
+
+	msg = 0;
+	for(i = 0; i < 8; i++)
+		if((1<<i) & status){
+			msg = mathmsg[i];
+			sprint(note, "sys: fp: %s fppc=0x%lux", msg, up->fpsave.env.pc);
+			error(note);
+			break;
+		}
+	if(msg == 0){
+		sprint(note, "sys: fp: unknown fppc=0x%lux", up->fpsave.env.pc);
+		error(note);
+	}
+	if(ureg->pc & KZERO)
+		panic("fp: status %lux fppc=0x%lux pc=0x%lux", status,
+			up->fpsave.env.pc, ureg->pc);
+}
+
+/*
+ *  math coprocessor emulation fault
+ */
+void
+mathemu(Ureg* ureg, void* arg)
+{
+	USED(ureg, arg);
+	switch(up->fpstate){
+	case FPINIT:
+		fpinit();
+		up->fpstate = FPACTIVE;
+		break;
+	case FPINACTIVE:
+		fprestore(&up->fpsave);
+		up->fpstate = FPACTIVE;
+		break;
+	case FPACTIVE:
+		panic("math emu");
+		break;
+	}
+}
+
+/*
+ *  math coprocessor segment overrun
+ */
+void
+mathover(Ureg* ureg, void* arg)
+{
+	USED(arg);
+	print("sys: fp: math overrun pc 0x%lux pid %ld\n", ureg->pc, up->pid);
+	pexit("math overrun", 0);
+}
+
+void
+mathinit(void)
+{
+	trapenable(VectorCERR, matherror, 0, "matherror");
+	if(X86FAMILY(m->cpuidax) == 3)
+		intrenable(IrqIRQ13, matherror, 0, BUSUNKNOWN, "matherror");
+	trapenable(VectorCNA, mathemu, 0, "mathemu");
+	trapenable(VectorCSO, mathover, 0, "mathover");
+}
+
+/*
+ *  Save the mach dependent part of the process state.
+ */
+void
+procsave(Proc *p)
+{
+	if(p->fpstate == FPACTIVE){
+		if(p->state == Moribund)
+			fpoff();
+		else
+			fpsave(&up->fpsave);
+		p->fpstate = FPINACTIVE;
+	}
+}
+
+void
+exit(int ispanic)
+{
+	USED(ispanic);
+
+	up = 0;
+	print("exiting\n");
+
+	/* Shutdown running devices */
+	chandevshutdown();
+
+	arch->reset();
+}
+
+void
+reboot(void)
+{
+	exit(0);
+}
+
+int
+isaconfig(char *class, int ctlrno, ISAConf *isa)
+{
+	char cc[32], *p;
+	int i;
+
+	snprint(cc, sizeof cc, "%s%d", class, ctlrno);
+	p = getconf(cc);
+	if(p == nil)
+		return 0;
+
+	isa->nopt = tokenize(p, isa->opt, NISAOPT);
+	for(i = 0; i < isa->nopt; i++){
+		p = isa->opt[i];
+		if(cistrncmp(p, "type=", 5) == 0)
+			isa->type = p + 5;
+		else if(cistrncmp(p, "port=", 5) == 0)
+			isa->port = strtoul(p+5, &p, 0);
+		else if(cistrncmp(p, "irq=", 4) == 0)
+			isa->irq = strtoul(p+4, &p, 0);
+		else if(cistrncmp(p, "dma=", 4) == 0)
+			isa->dma = strtoul(p+4, &p, 0);
+		else if(cistrncmp(p, "mem=", 4) == 0)
+			isa->mem = strtoul(p+4, &p, 0);
+		else if(cistrncmp(p, "size=", 5) == 0)
+			isa->size = strtoul(p+5, &p, 0);
+		else if(cistrncmp(p, "freq=", 5) == 0)
+			isa->freq = strtoul(p+5, &p, 0);
+	}
+	return 1;
+}
+
+/*
+ *  put the processor in the halt state if we've no processes to run.
+ *  an interrupt will get us going again.
+ */
+void
+idlehands(void)
+{
+	if(conf.nmach == 1)
+		halt();
+}
--- /dev/null
+++ b/os/pc/mem.h
@@ -1,0 +1,144 @@
+/*
+ * Memory and machine-specific definitions.  Used in C and assembler.
+ */
+
+/*
+ * Sizes
+ */
+#define	BI2BY		8			/* bits per byte */
+#define	BI2WD		32			/* bits per word */
+#define	BY2WD		4			/* bytes per word */
+#define	BY2V		8			/* bytes per double word */
+#define	BY2PG		4096			/* bytes per page */
+#define	WD2PG		(BY2PG/BY2WD)		/* words per page */
+#define	PGSHIFT		12			/* log(BY2PG) */
+#define	ROUND(s, sz)	(((s)+((sz)-1))&~((sz)-1))
+#define	PGROUND(s)	ROUND(s, BY2PG)
+#define	BLOCKALIGN	8
+
+#define	MAXMACH		8			/* max # cpus system can run */
+#define	KSTACK		8192			/* Size of kernel stack */
+
+/*
+ * Time
+ */
+#define	HZ		(100)			/* clock frequency */
+#define	MS2HZ		(1000/HZ)		/* millisec per clock tick */
+#define	TK2SEC(t)	((t)/HZ)		/* ticks to seconds */
+#define	MS2TK(t)	((((ulong)(t))*HZ)/1000)	/* milliseconds to ticks */
+
+/*
+ * Fundamental addresses
+ */
+#define	IDTADDR		0x80000800		/* idt */
+#define	REBOOTADDR	0x00001000		/* reboot code - physical address */
+#define	APBOOTSTRAP	0x80001000		/* AP bootstrap code */
+#define	CONFADDR	0x80001200		/* info passed from boot loader */
+#define	CPU0PDB		0x80002000		/* bootstrap processor PDB */
+#define	CPU0PTE		0x80003000		/* bootstrap processor PTE's for 0-4MB */
+#define	CPU0GDT		0x80004000		/* bootstrap processor GDT */
+#define	MACHADDR	0x80005000		/* as seen by current processor */
+#define	CPU0MACH	0x80006000		/* Mach for bootstrap processor */
+#define	MACHSIZE	BY2PG
+/*
+ * N.B.  ramscan knows that CPU0MACH+BY2PG is the end of reserved data
+ * N.B.  _start0x00100020 knows that CPU0PDB is the first reserved page
+ * and that there are 5 of them.
+ */
+
+/*
+ *  Address spaces
+ *
+ *  User is at 0-2GB
+ *  Kernel is at 2GB-4GB
+ */
+#define	UZERO		0			/* base of user address space */
+#define	UTZERO		(UZERO+BY2PG)		/* first address in user text */
+#define	KZERO		0x80000000		/* base of kernel address space */
+#define	KTZERO		0x80100000		/* first address in kernel text */
+#define	USTKTOP		(KZERO-BY2PG)		/* byte just beyond user stack */
+#define	USTKSIZE	(16*1024*1024)		/* size of user stack */
+#define	TSTKTOP		(USTKTOP-USTKSIZE)	/* end of new stack in sysexec */
+#define	TSTKSIZ 	100
+
+/*
+ *  known x86 segments (in GDT) and their selectors
+ */
+#define	NULLSEG	0	/* null segment */
+#define	KDSEG	1	/* kernel data/stack */
+#define	KESEG	2	/* kernel executable */	
+#define	UDSEG	3	/* user data/stack */
+#define	UESEG	4	/* user executable */
+#define	TSSSEG	5	/* task segment */
+#define	APMCSEG		6	/* APM code segment */
+#define	APMCSEG16	7	/* APM 16-bit code segment */
+#define	APMDSEG		8	/* APM data segment */
+#define	NGDT		10	/* number of GDT entries required */
+/* #define	APM40SEG	8	/* APM segment 0x40 */
+
+#define	SELGDT	(0<<2)	/* selector is in gdt */
+#define	SELLDT	(1<<2)	/* selector is in ldt */
+
+#define	SELECTOR(i, t, p)	(((i)<<3) | (t) | (p))
+
+#define	NULLSEL	SELECTOR(NULLSEG, SELGDT, 0)
+#define	KDSEL	SELECTOR(KDSEG, SELGDT, 0)
+#define	KESEL	SELECTOR(KESEG, SELGDT, 0)
+#define	UESEL	SELECTOR(UESEG, SELGDT, 3)
+#define	UDSEL	SELECTOR(UDSEG, SELGDT, 3)
+#define	TSSSEL	SELECTOR(TSSSEG, SELGDT, 0)
+#define	APMCSEL 	SELECTOR(APMCSEG, SELGDT, 0)
+#define	APMCSEL16	SELECTOR(APMCSEG16, SELGDT, 0)
+#define	APMDSEL		SELECTOR(APMDSEG, SELGDT, 0)
+/* #define	APM40SEL	SELECTOR(APM40SEG, SELGDT, 0) */
+
+/*
+ *  fields in segment descriptors
+ */
+#define	SEGDATA	(0x10<<8)	/* data/stack segment */
+#define	SEGEXEC	(0x18<<8)	/* executable segment */
+#define	SEGTSS	(0x9<<8)	/* TSS segment */
+#define	SEGCG	(0x0C<<8)	/* call gate */
+#define	SEGIG	(0x0E<<8)	/* interrupt gate */
+#define	SEGTG	(0x0F<<8)	/* trap gate */
+#define	SEGTYPE	(0x1F<<8)
+
+#define	SEGP	(1<<15)		/* segment present */
+#define	SEGPL(x) ((x)<<13)	/* priority level */
+#define	SEGB	(1<<22)		/* granularity 1==4k (for expand-down) */
+#define	SEGG	(1<<23)		/* granularity 1==4k (for other) */
+#define	SEGE	(1<<10)		/* expand down */
+#define	SEGW	(1<<9)		/* writable (for data/stack) */
+#define	SEGR	(1<<9)		/* readable (for code) */
+#define	SEGD	(1<<22)		/* default 1==32bit (for code) */
+
+/*
+ *  virtual MMU
+ */
+#define	PTEMAPMEM	(1024*1024)	
+#define	PTEPERTAB	(PTEMAPMEM/BY2PG)
+#define	SEGMAPSIZE	1984
+#define	SSEGMAPSIZE	16
+#define	PPN(x)		((x)&~(BY2PG-1))
+
+/*
+ *  physical MMU
+ */
+#define	PTEVALID	(1<<0)
+#define	PTEWT		(1<<3)
+#define	PTEUNCACHED	(1<<4)
+#define	PTEWRITE	(1<<1)
+#define	PTERONLY	(0<<1)
+#define	PTEKERNEL	(0<<2)
+#define	PTEUSER		(1<<2)
+#define	PTESIZE		(1<<7)
+#define	PTEGLOBAL	(1<<8)
+
+/*
+ * Macros for calculating offsets within the page directory base
+ * and page tables. 
+ */
+#define	PDX(va)		((((ulong)(va))>>22) & 0x03FF)
+#define	PTX(va)		((((ulong)(va))>>12) & 0x03FF)
+
+#define	getpgcolor(a)	0
--- /dev/null
+++ b/os/pc/memory.c
@@ -1,0 +1,588 @@
+/*
+ * Size memory and create the kernel page-tables on the fly while doing so.
+ * Called from main(), this code should only be run by the bootstrap processor.
+ */
+#include "u.h"
+#include "../port/lib.h"
+#include "mem.h"
+#include "dat.h"
+#include "fns.h"
+#include "io.h"
+
+#define MEMDEBUG	0
+
+enum {
+	MemUPA		= 0,		/* unbacked physical address */
+	MemRAM		= 1,		/* physical memory */
+	MemUMB		= 2,		/* upper memory block (<16MB) */
+	NMemType	= 3,
+
+	KB		= 1024,
+
+	MemMinMB	= 4,		/* minimum physical memory (<=4MB) */
+	MemMaxMB	= 768,		/* maximum physical memory to check */
+
+	NMemBase	= 10,
+};
+
+typedef struct {
+	int	size;
+	ulong	addr;
+} Map;
+
+typedef struct {
+	char*	name;
+	Map*	map;
+	Map*	mapend;
+
+	Lock;
+} RMap;
+
+static Map mapupa[16];
+static RMap rmapupa = {
+	"unallocated unbacked physical memory",
+	mapupa,
+	&mapupa[nelem(mapupa)-1],
+};
+
+static Map xmapupa[16];
+static RMap xrmapupa = {
+	"unbacked physical memory",
+	xmapupa,
+	&xmapupa[nelem(xmapupa)-1],
+};
+
+static Map mapram[16];
+static RMap rmapram = {
+	"physical memory",
+	mapram,
+	&mapram[nelem(mapram)-1],
+};
+
+static Map mapumb[64];
+static RMap rmapumb = {
+	"upper memory block",
+	mapumb,
+	&mapumb[nelem(mapumb)-1],
+};
+
+static Map mapumbrw[16];
+static RMap rmapumbrw = {
+	"UMB device memory",
+	mapumbrw,
+	&mapumbrw[nelem(mapumbrw)-1],
+};
+
+void
+mapprint(RMap *rmap)
+{
+	Map *mp;
+
+	print("%s\n", rmap->name);	
+	for(mp = rmap->map; mp->size; mp++)
+		print("\t%8.8luX %8.8uX %8.8luX\n", mp->addr, mp->size, mp->addr+mp->size);
+}
+
+void
+memdebug(void)
+{
+	ulong maxpa, maxpa1, maxpa2;
+
+	if(MEMDEBUG == 0)
+		return;
+
+	maxpa = (nvramread(0x18)<<8)|nvramread(0x17);
+	maxpa1 = (nvramread(0x31)<<8)|nvramread(0x30);
+	maxpa2 = (nvramread(0x16)<<8)|nvramread(0x15);
+	print("maxpa = %luX -> %luX, maxpa1 = %luX maxpa2 = %luX\n",
+		maxpa, MB+maxpa*KB, maxpa1, maxpa2);
+
+	mapprint(&rmapram);
+	mapprint(&rmapumb);
+	mapprint(&rmapumbrw);
+	mapprint(&rmapupa);
+}
+
+void
+mapfree(RMap* rmap, ulong addr, ulong size)
+{
+	Map *mp;
+	ulong t;
+
+	if(size <= 0)
+		return;
+
+	lock(rmap);
+	for(mp = rmap->map; mp->addr <= addr && mp->size; mp++)
+		;
+
+	if(mp > rmap->map && (mp-1)->addr+(mp-1)->size == addr){
+		(mp-1)->size += size;
+		if(addr+size == mp->addr){
+			(mp-1)->size += mp->size;
+			while(mp->size){
+				mp++;
+				(mp-1)->addr = mp->addr;
+				(mp-1)->size = mp->size;
+			}
+		}
+	}
+	else{
+		if(addr+size == mp->addr && mp->size){
+			mp->addr -= size;
+			mp->size += size;
+		}
+		else do{
+			if(mp >= rmap->mapend){
+				print("mapfree: %s: losing 0x%luX, %ld\n",
+					rmap->name, addr, size);
+				break;
+			}
+			t = mp->addr;
+			mp->addr = addr;
+			addr = t;
+			t = mp->size;
+			mp->size = size;
+			mp++;
+		}while(size = t);
+	}
+	unlock(rmap);
+}
+
+ulong
+mapalloc(RMap* rmap, ulong addr, int size, int align)
+{
+	Map *mp;
+	ulong maddr, oaddr;
+
+	lock(rmap);
+	for(mp = rmap->map; mp->size; mp++){
+		maddr = mp->addr;
+
+		if(addr){
+			/*
+			 * A specific address range has been given:
+			 *   if the current map entry is greater then
+			 *   the address is not in the map;
+			 *   if the current map entry does not overlap
+			 *   the beginning of the requested range then
+			 *   continue on to the next map entry;
+			 *   if the current map entry does not entirely
+			 *   contain the requested range then the range
+			 *   is not in the map.
+			 */
+			if(maddr > addr)
+				break;
+			if(mp->size < addr - maddr)	/* maddr+mp->size < addr, but no overflow */
+				continue;
+			if(addr - maddr > mp->size - size)	/* addr+size > maddr+mp->size, but no overflow */
+				break;
+			maddr = addr;
+		}
+
+		if(align > 0)
+			maddr = ((maddr+align-1)/align)*align;
+		if(mp->addr+mp->size-maddr < size)
+			continue;
+
+		oaddr = mp->addr;
+		mp->addr = maddr+size;
+		mp->size -= maddr-oaddr+size;
+		if(mp->size == 0){
+			do{
+				mp++;
+				(mp-1)->addr = mp->addr;
+			}while((mp-1)->size = mp->size);
+		}
+
+		unlock(rmap);
+		if(oaddr != maddr)
+			mapfree(rmap, oaddr, maddr-oaddr);
+
+		return maddr;
+	}
+	unlock(rmap);
+
+	return 0;
+}
+
+static void
+umbscan(void)
+{
+	uchar *p;
+
+	/*
+	 * Scan the Upper Memory Blocks (0xA0000->0xF0000) for pieces
+	 * which aren't used; they can be used later for devices which
+	 * want to allocate some virtual address space.
+	 * Check for two things:
+	 * 1) device BIOS ROM. This should start with a two-byte header
+	 *    of 0x55 0xAA, followed by a byte giving the size of the ROM
+	 *    in 512-byte chunks. These ROM's must start on a 2KB boundary.
+	 * 2) device memory. This is read-write.
+	 * There are some assumptions: there's VGA memory at 0xA0000 and
+	 * the VGA BIOS ROM is at 0xC0000. Also, if there's no ROM signature
+	 * at 0xE0000 then the whole 64KB up to 0xF0000 is theoretically up
+	 * for grabs; check anyway.
+	 */
+	p = KADDR(0xD0000);
+	while(p < (uchar*)KADDR(0xE0000)){
+		/*
+		 * Test for 0x55 0xAA before poking obtrusively,
+		 * some machines (e.g. Thinkpad X20) seem to map
+		 * something dynamic here (cardbus?) causing weird
+		 * problems if it is changed.
+		 */
+		if(p[0] == 0x55 && p[1] == 0xAA){
+			p += p[2]*512;
+			continue;
+		}
+
+		p[0] = 0xCC;
+		p[2*KB-1] = 0xCC;
+		if(p[0] != 0xCC || p[2*KB-1] != 0xCC){
+			p[0] = 0x55;
+			p[1] = 0xAA;
+			p[2] = 4;
+			if(p[0] == 0x55 && p[1] == 0xAA){
+				p += p[2]*512;
+				continue;
+			}
+			if(p[0] == 0xFF && p[1] == 0xFF)
+				mapfree(&rmapumb, PADDR(p), 2*KB);
+		}
+		else
+			mapfree(&rmapumbrw, PADDR(p), 2*KB);
+		p += 2*KB;
+	}
+
+	p = KADDR(0xE0000);
+	if(p[0] != 0x55 || p[1] != 0xAA){
+		p[0] = 0xCC;
+		p[64*KB-1] = 0xCC;
+		if(p[0] != 0xCC && p[64*KB-1] != 0xCC)
+			mapfree(&rmapumb, PADDR(p), 64*KB);
+	}
+}
+
+
+static void
+ramscan(ulong maxmem)
+{
+	ulong *k0, kzero, map, maxpa, pa, *pte, *table, *va, x, n;
+	int nvalid[NMemType];
+	uchar *bda;
+
+	/*
+	 * The bootstrap code has has created a prototype page
+	 * table which maps the first MemMinMB of physical memory to KZERO.
+	 * The page directory is at m->pdb and the first page of
+	 * free memory is after the per-processor MMU information.
+	 */
+	/*
+	 * Initialise the memory bank information for conventional memory
+	 * (i.e. less than 640KB). The base is the first location after the
+	 * bootstrap processor MMU information and the limit is obtained from
+	 * the BIOS data area.
+	 */
+	x = PADDR(CPU0MACH+BY2PG);
+	bda = (uchar*)KADDR(0x400);
+	n = ((bda[0x14]<<8)|bda[0x13])*KB-x;
+	mapfree(&rmapram, x, n);
+//	memset(KADDR(x), 0, n);			/* keep us honest */
+
+	x = PADDR(PGROUND((ulong)end));
+	pa = MemMinMB*MB;
+	mapfree(&rmapram, x, pa-x);
+//	memset(KADDR(x), 0, pa-x);		/* keep us honest */
+
+	/*
+	 * Check if the extended memory size can be obtained from the CMOS.
+	 * If it's 0 then it's either not known or >= 64MB. Always check
+	 * at least 24MB in case there's a memory gap (up to 8MB) below 16MB;
+	 * in this case the memory from the gap is remapped to the top of
+	 * memory.
+	 * The value in CMOS is supposed to be the number of KB above 1MB.
+	 */
+	if(maxmem == 0){
+		x = (nvramread(0x18)<<8)|nvramread(0x17);
+		if(x == 0 || x >= (63*KB))
+			maxpa = MemMaxMB*MB;
+		else
+			maxpa = MB+x*KB;
+		if(maxpa < 24*MB)
+			maxpa = 24*MB;
+		maxmem = MemMaxMB*MB;
+	}
+	else
+		maxpa = maxmem;
+
+	/*
+	 * March up memory from MemMinMB to maxpa 1MB at a time,
+	 * mapping the first page and checking the page can
+	 * be written and read correctly. The page tables are created here
+	 * on the fly, allocating from low memory as necessary.
+	 */
+	k0 = (ulong*)KADDR(0);
+	kzero = *k0;
+	map = 0;
+	x = 0x12345678;
+	memset(nvalid, 0, sizeof(nvalid));
+	while(pa < maxpa){
+		/*
+		 * Map the page. Use mapalloc(&rmapram, ...) to make
+		 * the page table if necessary, it will be returned to the
+		 * pool later if it isn't needed.
+		 */
+		va = KADDR(pa);
+		table = &m->pdb[PDX(va)];
+		if(*table == 0){
+			if(map == 0 && (map = mapalloc(&rmapram, 0, BY2PG, BY2PG)) == 0)
+				break;
+			memset(KADDR(map), 0, BY2PG);
+			*table = map|PTEWRITE|PTEVALID;
+			memset(nvalid, 0, sizeof(nvalid));
+		}
+		table = KADDR(PPN(*table));
+		pte = &table[PTX(va)];
+
+		*pte = pa|PTEWRITE|PTEUNCACHED|PTEVALID;
+		mmuflushtlb(PADDR(m->pdb));
+
+		/*
+		 * Write a pattern to the page and write a different
+		 * pattern to a possible mirror at KZER0. If the data
+		 * reads back correctly the chunk is some type of RAM (possibly
+		 * a linearly-mapped VGA framebuffer, for instance...) and
+		 * can be cleared and added to the memory pool. If not, the
+		 * chunk is marked uncached and added to the UMB pool if <16MB
+		 * or is marked invalid and added to the UPA pool.
+		 */
+		*va = x;
+		*k0 = ~x;
+		if(*va == x){
+			nvalid[MemRAM] += MB/BY2PG;
+			mapfree(&rmapram, pa, MB);
+
+			do{
+				*pte++ = pa|PTEWRITE|PTEVALID;
+				pa += BY2PG;
+			}while(pa % MB);
+			mmuflushtlb(PADDR(m->pdb));
+			/* memset(va, 0, MB); so damn slow to memset all of memory */
+		}
+		else if(pa < 16*MB){
+			nvalid[MemUMB] += MB/BY2PG;
+			mapfree(&rmapumb, pa, MB);
+
+			do{
+				*pte++ = pa|PTEWRITE|PTEUNCACHED|PTEVALID;
+				pa += BY2PG;
+			}while(pa % MB);
+		}
+		else{
+			nvalid[MemUPA] += MB/BY2PG;
+			mapfree(&rmapupa, pa, MB);
+
+			*pte = 0;
+			pa += MB;
+		}
+
+		/*
+		 * Done with this 4MB chunk, review the options:
+		 * 1) not physical memory and >=16MB - invalidate the PDB entry;
+		 * 2) physical memory - use the 4MB page extension if possible;
+		 * 3) not physical memory and <16MB - use the 4MB page extension
+		 *    if possible;
+		 * 4) mixed or no 4MB page extension - commit the already
+		 *    initialised space for the page table.
+		 */
+		if((pa % (4*MB)) == 0){
+			table = &m->pdb[PDX(va)];
+			if(nvalid[MemUPA] == (4*MB)/BY2PG)
+				*table = 0;
+			else if(nvalid[MemRAM] == (4*MB)/BY2PG && (m->cpuiddx & 0x08))
+				*table = (pa - 4*MB)|PTESIZE|PTEWRITE|PTEVALID;
+			else if(nvalid[MemUMB] == (4*MB)/BY2PG && (m->cpuiddx & 0x08))
+				*table = (pa - 4*MB)|PTESIZE|PTEWRITE|PTEUNCACHED|PTEVALID;
+			else
+				map = 0;
+		}
+
+		mmuflushtlb(PADDR(m->pdb));
+		x += 0x3141526;
+	}
+
+	/*
+	 * If we didn't reach the end of the 4MB chunk, that part won't
+	 * be mapped.  Commit the already initialised space for the page table.
+	 */
+	if(pa % (4*MB))
+		map = 0;
+
+	if(map)
+		mapfree(&rmapram, map, BY2PG);
+	if(pa < maxmem)
+		mapfree(&rmapupa, pa, maxmem-pa);
+	if(maxmem < 0xFFE00000)
+		mapfree(&rmapupa, maxmem, 0x00000000-maxmem);
+	if(MEMDEBUG)
+		print("maxmem %luX %luX\n", maxmem, 0x00000000-maxmem);
+	*k0 = kzero;
+}
+
+void
+meminit(ulong maxmem)
+{
+	Map *mp, *xmp;
+	ulong pa, *pte;
+
+	/*
+	 * Set special attributes for memory between 640KB and 1MB:
+	 *   VGA memory is writethrough;
+	 *   BIOS ROM's/UMB's are uncached;
+	 * then scan for useful memory.
+	 */
+	for(pa = 0xA0000; pa < 0xC0000; pa += BY2PG){
+		pte = mmuwalk(m->pdb, (ulong)KADDR(pa), 2, 0);
+		*pte |= PTEWT;
+	}
+	for(pa = 0xC0000; pa < 0x100000; pa += BY2PG){
+		pte = mmuwalk(m->pdb, (ulong)KADDR(pa), 2, 0);
+		*pte |= PTEUNCACHED;
+	}
+	mmuflushtlb(PADDR(m->pdb));
+
+	umbscan();
+	ramscan(maxmem);
+
+	/*
+	 * Set the conf entries describing two banks of allocatable memory.
+	 * Grab the first and largest entries in rmapram as left by ramscan().
+	 *
+	 * It would be nice to have more than 2 memory banks describable in conf.
+	 */
+	mp = rmapram.map;
+	conf.base0 = mp->addr;
+	conf.npage0 = mp->size/BY2PG;
+	mp++;
+	for(xmp = 0; mp->size; mp++){
+		if(xmp == 0 || mp->size > xmp->size)
+			xmp = mp;
+	}
+
+	if(xmp){		
+		conf.base1 = xmp->addr;
+		conf.npage1 = xmp->size/BY2PG;
+	}
+	if(MEMDEBUG)
+		memdebug();
+}
+
+ulong
+umbmalloc(ulong addr, int size, int align)
+{
+	ulong a;
+
+	if(a = mapalloc(&rmapumb, addr, size, align))
+		return (ulong)KADDR(a);
+
+	return 0;
+}
+
+void
+umbfree(ulong addr, int size)
+{
+	mapfree(&rmapumb, PADDR(addr), size);
+}
+
+ulong
+umbrwmalloc(ulong addr, int size, int align)
+{
+	ulong a;
+	uchar *p;
+
+	if(a = mapalloc(&rmapumbrw, addr, size, align))
+		return(ulong)KADDR(a);
+
+	/*
+	 * Perhaps the memory wasn't visible before
+	 * the interface is initialised, so try again.
+	 */
+	if((a = umbmalloc(addr, size, align)) == 0)
+		return 0;
+	p = (uchar*)a;
+	p[0] = 0xCC;
+	p[size-1] = 0xCC;
+	if(p[0] == 0xCC && p[size-1] == 0xCC)
+		return a;
+	umbfree(a, size);
+
+	return 0;
+}
+
+void
+umbrwfree(ulong addr, int size)
+{
+	mapfree(&rmapumbrw, PADDR(addr), size);
+}
+
+ulong
+upamalloc(ulong pa, int size, int align)
+{
+	ulong a, ae;
+
+	if(a = mapalloc(&xrmapupa, pa, size, align))
+		return a;
+
+	if((a = mapalloc(&rmapupa, pa, size, align)) == 0){
+		memdebug();
+		return 0;
+	}
+
+	/*
+	 * Upamalloc is a request to map a range of physical addresses.
+	 * Therefore, if pa is 0 mapalloc will choose the base address.
+	 * Note, however, mmukmap is always asked to give a 1-to-1 mapping
+	 * of va to pa.
+	 */
+	ae = mmukmap(a, a, size);
+
+	/*
+	 * Should check here that it was all delivered
+	 * and put it back and barf if not.
+	 */
+	USED(ae);
+
+	/*
+	 * Be very careful this returns a PHYSICAL address
+	 * mapped 1-to-1 with the virtual address.
+	 * If a < KZERO it's probably not a good idea to
+	 * try KADDR(a)...
+	 */
+	return a;
+}
+
+void
+upafree(ulong pa, int size)
+{
+	mapfree(&xrmapupa, pa, size);
+}
+
+void
+upareserve(ulong pa, int size)
+{
+	ulong a;
+	
+	a = mapalloc(&rmapupa, pa, size, 0);
+	if(a != pa){
+		/*
+		 * This can happen when we're using the E820
+		 * map, which might have already reserved some
+		 * of the regions claimed by the pci devices.
+		 */
+	//	print("upareserve: cannot reserve pa=%#.8lux size=%d\n", pa, size);
+		if(a != 0)
+			mapfree(&rmapupa, a, size);
+	}
+}
--- /dev/null
+++ b/os/pc/mkfile
@@ -1,0 +1,83 @@
+<../../mkconfig
+
+#Configurable parameters
+
+CONF=pc			#default configuration
+CONFLIST=pc pcdisk
+CLEANCONFLIST=pc pcdisk zpc zpcrem pix pcsoe
+
+SYSTARG=$OSTARG
+OBJTYPE=386
+INSTALLDIR=$ROOT/Inferno/$OBJTYPE/bin	#path of directory where kernel is installed
+#INSTALLDIR=/$OBJTYPE
+
+#end configurable parameters
+
+<$ROOT/mkfiles/mkfile-$SYSTARG-$OBJTYPE	#set vars based on target system
+
+<| $SHELLNAME ../port/mkdevlist $CONF	#sets $IP, $DEVS, $ETHERS, $VGAS, $PORT, $MISC, $LIBS, $OTHERS
+
+OBJ=\
+	l.$O\
+	fpsave.$O\
+	portclock.$O\
+	tod.$O\
+	i8250.$O\
+	i8253.$O\
+	i8259.$O\
+	kbd.$O\
+	main.$O\
+	memory.$O\
+	mmu.$O\
+	trap.$O\
+	$CONF.root.$O\
+	$IP\
+	$DEVS\
+	$ETHERS\
+	$LINKS\
+	$PORT\
+	$MISC\
+	$OTHERS\
+
+LIBNAMES=${LIBS:%=lib%.a}
+
+HFILES=\
+	mem.h\
+	dat.h\
+	fns.h\
+	io.h\
+
+CFLAGS=-wFVT -I$ROOT/Inferno/$OBJTYPE/include -I$ROOT/include -I$ROOT/libinterp -I../port
+KERNDATE=`{$NDATE}
+
+default:V: i$CONF
+
+i$CONF: $OBJ $CONF.c $CONF.root.h $LIBNAMES
+	$CC $CFLAGS '-DKERNDATE='$KERNDATE $CONF.c
+	$LD -o $target -T0x80100020 -l $OBJ $CONF.$O $LIBFILES
+	$KSIZE $target
+
+install:V: i$CONF
+	cp i$CONF $INSTALLDIR/i$CONF
+
+<../port/portmkfile
+
+clock.$O:	$ROOT/Inferno/$OBJTYPE/include/ureg.h
+devether.$O:	$ROOT/Inferno/$OBJTYPE/include/ureg.h
+fault386.$O:	$ROOT/Inferno/$OBJTYPE/include/ureg.h
+main.$O:	$ROOT/Inferno/$OBJTYPE/include/ureg.h
+trap.$O:	$ROOT/Inferno/$OBJTYPE/include/ureg.h
+
+devether.$O $ETHERS:	etherif.h ../port/netif.h
+$IP devip.$O:		../ip/ip.h
+
+# to be moved to port/interp 
+bench.h:D: ../../module/bench.m
+	rm -f $target && limbo -a -I../../module ../../module/bench.m > $target
+benchmod.h:D:  ../../module/bench.m
+	rm -f $target && limbo -t Bench -I../../module ../../module/bench.m > $target
+devbench.$O: bench.h benchmod.h
+$VGA screen.$O:	screen.h vga.h
+
+devuart.$O:	../port/devuart.c ../port/uart.h
+	$CC $CFLAGS ../port/devuart.c
--- /dev/null
+++ b/os/pc/mmu.c
@@ -1,0 +1,343 @@
+#include	"u.h"
+#include	"../port/lib.h"
+#include	"mem.h"
+#include	"dat.h"
+#include	"fns.h"
+#include	"io.h"
+
+#define	DATASEGM(p) 	{ 0xFFFF, SEGG|SEGB|(0xF<<16)|SEGP|SEGPL(p)|SEGDATA|SEGW }
+#define	EXECSEGM(p) 	{ 0xFFFF, SEGG|SEGD|(0xF<<16)|SEGP|SEGPL(p)|SEGEXEC|SEGR }
+#define	TSSSEGM(b,p)	{ ((b)<<16)|sizeof(Tss),\
+			  ((b)&0xFF000000)|(((b)>>16)&0xFF)|SEGTSS|SEGPL(p)|SEGP }
+
+Segdesc gdt[NGDT] =
+{
+[NULLSEG]	{ 0, 0},		/* null descriptor */
+[KDSEG]		DATASEGM(0),		/* kernel data/stack */
+[KESEG]		EXECSEGM(0),		/* kernel code */
+[UDSEG]		DATASEGM(3),		/* user data/stack */
+[UESEG]		EXECSEGM(3),		/* user code */
+[TSSSEG]	TSSSEGM(0,0),		/* tss segment */
+};
+
+static void
+taskswitch(ulong pdb, ulong stack)
+{
+	Tss *tss;
+
+	tss = m->tss;
+	tss->ss0 = KDSEL;
+	tss->esp0 = stack;
+	tss->ss1 = KDSEL;
+	tss->esp1 = stack;
+	tss->ss2 = KDSEL;
+	tss->esp2 = stack;
+	tss->cr3 = pdb;
+	putcr3(pdb);
+}
+
+/* 
+ * On processors that support it, we set the PTEGLOBAL bit in
+ * page table and page directory entries that map kernel memory.
+ * Doing this tells the processor not to bother flushing them
+ * from the TLB when doing the TLB flush associated with a 
+ * context switch (write to CR3).  Since kernel memory mappings
+ * are never removed, this is safe.  (If we ever remove kernel memory
+ * mappings, we can do a full flush by turning off the PGE bit in CR4,
+ * writing to CR3, and then turning the PGE bit back on.) 
+ *
+ * See also mmukmap below.
+ * 
+ * Processor support for the PTEGLOBAL bit is enabled in devarch.c.
+ */
+static void
+memglobal(void)
+{
+	int i, j;
+	ulong *pde, *pte;
+
+	/* only need to do this once, on bootstrap processor */
+	if(m->machno != 0)
+		return;
+
+	if(!m->havepge)
+		return;
+
+	pde = m->pdb;
+	for(i=512; i<1024; i++){	/* 512: start at entry for virtual 0x80000000 */
+		if(pde[i] & PTEVALID){
+			pde[i] |= PTEGLOBAL;
+			if(!(pde[i] & PTESIZE)){
+				pte = KADDR(pde[i]&~(BY2PG-1));
+				for(j=0; j<1024; j++)
+					if(pte[j] & PTEVALID)
+						pte[j] |= PTEGLOBAL;
+			}
+		}
+	}			
+}
+
+void
+mmuinit(void)
+{
+	ulong x, *p;
+	ushort ptr[3];
+
+	memglobal();
+
+	m->tss = malloc(sizeof(Tss));
+	memset(m->tss, 0, sizeof(Tss));
+	m->tss->iomap = 0xDFFF<<16;
+
+	/*
+	 * We used to keep the GDT in the Mach structure, but it
+	 * turns out that that slows down access to the rest of the
+	 * page.  Since the Mach structure is accessed quite often,
+	 * it pays off anywhere from a factor of 1.25 to 2 on real
+	 * hardware to separate them (the AMDs are more sensitive
+	 * than Intels in this regard).  Under VMware it pays off
+	 * a factor of about 10 to 100.
+	 */
+
+	memmove(m->gdt, gdt, sizeof gdt);
+	x = (ulong)m->tss;
+	m->gdt[TSSSEG].d0 = (x<<16)|sizeof(Tss);
+	m->gdt[TSSSEG].d1 = (x&0xFF000000)|((x>>16)&0xFF)|SEGTSS|SEGPL(0)|SEGP;
+
+	ptr[0] = sizeof(gdt)-1;
+	x = (ulong)m->gdt;
+	ptr[1] = x & 0xFFFF;
+	ptr[2] = (x>>16) & 0xFFFF;
+	lgdt(ptr);
+
+	ptr[0] = sizeof(Segdesc)*256-1;
+	x = IDTADDR;
+	ptr[1] = x & 0xFFFF;
+	ptr[2] = (x>>16) & 0xFFFF;
+	lidt(ptr);
+
+	/* make kernel text unwritable */
+	for(x = KTZERO; x < (ulong)etext; x += BY2PG){
+		p = mmuwalk(m->pdb, x, 2, 0);
+		if(p == nil)
+			panic("mmuinit");
+		*p &= ~PTEWRITE;
+	}
+
+	taskswitch(PADDR(m->pdb),  (ulong)m + BY2PG);
+	ltr(TSSSEL);
+}
+
+
+
+
+ulong*
+mmuwalk(ulong* pdb, ulong va, int level, int create)
+{
+	ulong pa, *table;
+
+	/*
+	 * Walk the page-table pointed to by pdb and return a pointer
+	 * to the entry for virtual address va at the requested level.
+	 * If the entry is invalid and create isn't requested then bail
+	 * out early. Otherwise, for the 2nd level walk, allocate a new
+	 * page-table page and register it in the 1st level.
+	 */
+	table = &pdb[PDX(va)];
+	if(!(*table & PTEVALID) && create == 0)
+		return 0;
+
+	switch(level){
+
+	default:
+		return 0;
+
+	case 1:
+		return table;
+
+	case 2:
+		if(*table & PTESIZE)
+			panic("mmuwalk2: va %luX entry %luX\n", va, *table);
+		if(!(*table & PTEVALID)){
+			pa = PADDR(xspanalloc(BY2PG, BY2PG, 0));
+			*table = pa|PTEWRITE|PTEVALID;
+		}
+		table = KADDR(PPN(*table));
+
+		return &table[PTX(va)];
+	}
+}
+
+static Lock mmukmaplock;
+
+int
+mmukmapsync(ulong va)
+{
+	Mach *mach0;
+	ulong entry, *pte;
+
+	mach0 = MACHP(0);
+
+	ilock(&mmukmaplock);
+
+	if((pte = mmuwalk(mach0->pdb, va, 1, 0)) == nil){
+		iunlock(&mmukmaplock);
+		return 0;
+	}
+	if(!(*pte & PTESIZE) && mmuwalk(mach0->pdb, va, 2, 0) == nil){
+		iunlock(&mmukmaplock);
+		return 0;
+	}
+	entry = *pte;
+
+	if(!(m->pdb[PDX(va)] & PTEVALID))
+		m->pdb[PDX(va)] = entry;
+
+//	if(up && up->mmupdb){
+//		((ulong*)up->mmupdb->va)[PDX(va)] = entry;
+//		mmuflushtlb(up->mmupdb->pa);
+//	}
+//	else
+		mmuflushtlb(PADDR(m->pdb));
+
+	iunlock(&mmukmaplock);
+
+	return 1;
+}
+
+ulong
+mmukmap(ulong pa, ulong va, int size)
+{
+	Mach *mach0;
+	ulong ova, pae, *table, pgsz, *pte, x;
+	int pse, sync;
+
+	mach0 = MACHP(0);
+	if((mach0->cpuiddx & 0x08) && (getcr4() & 0x10))
+		pse = 1;
+	else
+		pse = 0;
+	sync = 0;
+
+	pa = PPN(pa);
+	if(va == 0)
+		va = (ulong)KADDR(pa);
+	else
+		va = PPN(va);
+	ova = va;
+
+	pae = pa + size;
+	ilock(&mmukmaplock);
+	while(pa < pae){
+		table = &mach0->pdb[PDX(va)];
+		/*
+		 * Possibly already mapped.
+		 */
+		if(*table & PTEVALID){
+			if(*table & PTESIZE){
+				/*
+				 * Big page. Does it fit within?
+				 * If it does, adjust pgsz so the correct end can be
+				 * returned and get out.
+				 * If not, adjust pgsz up to the next 4MB boundary
+				 * and continue.
+				 */
+				x = PPN(*table);
+				if(x != pa)
+					panic("mmukmap1: pa %luX  entry %luX\n",
+						pa, *table);
+				x += 4*MB;
+				if(pae <= x){
+					pa = pae;
+					break;
+				}
+				pgsz = x - pa;
+				pa += pgsz;
+				va += pgsz;
+
+				continue;
+			}
+			else{
+				/*
+				 * Little page. Walk to the entry.
+				 * If the entry is valid, set pgsz and continue.
+				 * If not, make it so, set pgsz, sync and continue.
+				 */
+				pte = mmuwalk(mach0->pdb, va, 2, 0);
+				if(pte && *pte & PTEVALID){
+					x = PPN(*pte);
+					if(x != pa)
+						panic("mmukmap2: pa %luX entry %luX\n",
+							pa, *pte);
+					pgsz = BY2PG;
+					pa += pgsz;
+					va += pgsz;
+					sync++;
+
+					continue;
+				}
+			}
+		}
+
+		/*
+		 * Not mapped. Check if it can be mapped using a big page -
+		 * starts on a 4MB boundary, size >= 4MB and processor can do it.
+		 * If not a big page, walk the walk, talk the talk.
+		 * Sync is set.
+		 *
+		 * If we're creating a kernel mapping, we know that it will never
+		 * expire and thus we can set the PTEGLOBAL bit to make the entry
+	 	 * persist in the TLB across flushes.  If we do add support later for
+		 * unmapping kernel addresses, see devarch.c for instructions on
+		 * how to do a full TLB flush.
+		 */
+		if(pse && (pa % (4*MB)) == 0 && (pae >= pa+4*MB)){
+			*table = pa|PTESIZE|PTEWRITE|PTEUNCACHED|PTEVALID;
+			if((va&KZERO) && m->havepge)
+				*table |= PTEGLOBAL;
+			pgsz = 4*MB;
+		}
+		else{
+			pte = mmuwalk(mach0->pdb, va, 2, 1);
+			*pte = pa|PTEWRITE|PTEUNCACHED|PTEVALID;
+			if((va&KZERO) && m->havepge)
+				*pte |= PTEGLOBAL;
+			pgsz = BY2PG;
+		}
+		pa += pgsz;
+		va += pgsz;
+		sync++;
+	}
+	iunlock(&mmukmaplock);
+
+	/*
+	 * If something was added
+	 * then need to sync up.
+	 */
+	if(sync)
+		mmukmapsync(ova);
+
+	return pa;
+}
+
+void*
+vmap(ulong pa, int size)
+{
+	pa = upamalloc(pa, size, 0);
+	if(pa == 0)
+		return nil;
+	return KADDR(pa);
+}
+
+void
+vunmap(void *va, int size)
+{
+	if(va != nil)
+		upafree(PADDR(va), size);
+}
+
+int
+segflush(void*, ulong)
+{
+	return 0;
+}
--- /dev/null
+++ b/os/pc/mouse.c
@@ -1,0 +1,84 @@
+#include "u.h"
+#include "../port/lib.h"
+#include "mem.h"
+#include "dat.h"
+#include "fns.h"
+#include "../port/error.h"
+#include "io.h"
+
+/*
+ *  mouse types
+ */
+enum
+{
+	Mouseother=	0,
+	Mouseserial=	1,
+	MousePS2=	2,
+};
+
+static int mousetype;
+
+/*
+ *  ps/2 mouse message is three bytes
+ *
+ *	byte 0 -	0 0 SDY SDX 1 M R L
+ *	byte 1 -	DX
+ *	byte 2 -	DY
+ *
+ *  shift & left button is the same as middle button
+ */
+static void
+ps2mouseputc(int c, int shift)
+{
+	static short msg[3];
+	static int nb;
+	static uchar b[] = {0, 1, 4, 5, 2, 3, 6, 7, 0, 1, 2, 5, 2, 3, 6, 7 };
+	int buttons, dx, dy;
+
+	/* 
+	 *  check byte 0 for consistency
+	 */
+	if(nb==0 && (c&0xc8)!=0x08)
+		return;
+
+	msg[nb] = c;
+	if(++nb == 3){
+		nb = 0;
+		if(msg[0] & 0x10)
+			msg[1] |= 0xFF00;
+		if(msg[0] & 0x20)
+			msg[2] |= 0xFF00;
+
+		buttons = b[(msg[0]&7) | (shift ? 8 : 0)];
+		dx = msg[1];
+		dy = -msg[2];
+		mousetrack(buttons, dx, dy, 1);
+	}
+	return;
+}
+
+/*
+ *  set up a ps2 mouse
+ */
+static void
+ps2mouse(void)
+{
+	if(mousetype == MousePS2)
+		return;
+
+	i8042auxenable(ps2mouseputc);
+	/* make mouse streaming, enabled */
+	i8042auxcmd(0xEA);
+	i8042auxcmd(0xF4);
+
+	mousetype = MousePS2;
+}
+
+void
+ps2mouselink(void)
+{
+	/*
+	 * hack
+	 */
+	ps2mouse();
+}
--- /dev/null
+++ b/os/pc/mp.c
@@ -1,0 +1,815 @@
+#include "u.h"
+#include "../port/lib.h"
+#include "mem.h"
+#include "dat.h"
+#include "fns.h"
+#include "io.h"
+#include "ureg.h"
+
+#include "mp.h"
+#include "apbootstrap.h"
+
+static Bus* mpbus;
+static Bus* mpbuslast;
+static int mpisabus = -1;
+static int mpeisabus = -1;
+extern int i8259elcr;			/* mask of level-triggered interrupts */
+static Apic mpapic[MaxAPICNO+1];
+static int machno2apicno[MaxAPICNO+1];	/* inverse map: machno -> APIC ID */
+static Lock mprdthilock;
+static int mprdthi;
+static Ref mpvnoref;			/* unique vector assignment */
+static int mpmachno = 1;
+
+static char* buses[] = {
+	"CBUSI ",
+	"CBUSII",
+	"EISA  ",
+	"FUTURE",
+	"INTERN",
+	"ISA   ",
+	"MBI   ",
+	"MBII  ",
+	"MCA   ",
+	"MPI   ",
+	"MPSA  ",
+	"NUBUS ",
+	"PCI   ",
+	"PCMCIA",
+	"TC    ",
+	"VL    ",
+	"VME   ",
+	"XPRESS",
+	0,
+};
+
+static Apic*
+mkprocessor(PCMPprocessor* p)
+{
+	Apic *apic;
+
+	if(!(p->flags & PcmpEN) || p->apicno > MaxAPICNO)
+		return 0;
+
+	apic = &mpapic[p->apicno];
+	apic->type = PcmpPROCESSOR;
+	apic->apicno = p->apicno;
+	apic->flags = p->flags;
+	apic->lintr[0] = ApicIMASK;
+	apic->lintr[1] = ApicIMASK;
+
+	if(p->flags & PcmpBP){
+		machno2apicno[0] = p->apicno;
+		apic->machno = 0;
+	}
+	else{
+		machno2apicno[mpmachno] = p->apicno;
+		apic->machno = mpmachno;
+		mpmachno++;
+	}
+
+	return apic;
+}
+
+static Bus*
+mkbus(PCMPbus* p)
+{
+	Bus *bus;
+	int i;
+
+	for(i = 0; buses[i]; i++){
+		if(strncmp(buses[i], p->string, sizeof(p->string)) == 0)
+			break;
+	}
+	if(buses[i] == 0)
+		return 0;
+
+	bus = xalloc(sizeof(Bus));
+	if(mpbus)
+		mpbuslast->next = bus;
+	else
+		mpbus = bus;
+	mpbuslast = bus;
+
+	bus->type = i;
+	bus->busno = p->busno;
+	if(bus->type == BusEISA){
+		bus->po = PcmpLOW;
+		bus->el = PcmpLEVEL;
+		if(mpeisabus != -1)
+			print("mkbus: more than one EISA bus\n");
+		mpeisabus = bus->busno;
+	}
+	else if(bus->type == BusPCI){
+		bus->po = PcmpLOW;
+		bus->el = PcmpLEVEL;
+	}
+	else if(bus->type == BusISA){
+		bus->po = PcmpHIGH;
+		bus->el = PcmpEDGE;
+		if(mpisabus != -1)
+			print("mkbus: more than one ISA bus\n");
+		mpisabus = bus->busno;
+	}
+	else{
+		bus->po = PcmpHIGH;
+		bus->el = PcmpEDGE;
+	}
+
+	return bus;
+}
+
+static Bus*
+mpgetbus(int busno)
+{
+	Bus *bus;
+
+	for(bus = mpbus; bus; bus = bus->next){
+		if(bus->busno == busno)
+			return bus;
+	}
+	print("mpgetbus: can't find bus %d\n", busno);
+
+	return 0;
+}
+
+static Apic*
+mkioapic(PCMPioapic* p)
+{
+	Apic *apic;
+
+	if(!(p->flags & PcmpEN) || p->apicno > MaxAPICNO)
+		return 0;
+
+	/*
+	 * Map the I/O APIC.
+	 */
+	if(mmukmap(p->addr, 0, 1024) == 0)
+		return 0;
+
+	apic = &mpapic[p->apicno];
+	apic->type = PcmpIOAPIC;
+	apic->apicno = p->apicno;
+	apic->addr = KADDR(p->addr);
+	apic->flags = p->flags;
+
+	return apic;
+}
+
+static Aintr*
+mkiointr(PCMPintr* p)
+{
+	Bus *bus;
+	Aintr *aintr;
+
+	/*
+	 * According to the MultiProcessor Specification, a destination
+	 * I/O APIC of 0xFF means the signal is routed to all I/O APICs.
+	 * It's unclear how that can possibly be correct so treat it as
+	 * an error for now.
+	 */
+	if(p->apicno == 0xFF)
+		return 0;
+	if((bus = mpgetbus(p->busno)) == 0)
+		return 0;
+
+	aintr = xalloc(sizeof(Aintr));
+	aintr->intr = p;
+	aintr->apic = &mpapic[p->apicno];
+	aintr->next = bus->aintr;
+	bus->aintr = aintr;
+
+	return aintr;
+}
+
+static int
+mpintrinit(Bus* bus, PCMPintr* intr, int vno, int /*irq*/)
+{
+	int el, po, v;
+
+	/*
+	 * Parse an I/O or Local APIC interrupt table entry and
+	 * return the encoded vector.
+	 */
+	v = vno;
+
+	po = intr->flags & PcmpPOMASK;
+	el = intr->flags & PcmpELMASK;
+
+	switch(intr->intr){
+
+	default:				/* PcmpINT */
+		v |= ApicLOWEST;
+		break;
+
+	case PcmpNMI:
+		v |= ApicNMI;
+		po = PcmpHIGH;
+		el = PcmpEDGE;
+		break;
+
+	case PcmpSMI:
+		v |= ApicSMI;
+		break;
+
+	case PcmpExtINT:
+		v |= ApicExtINT;
+		/*
+		 * The AMI Goliath doesn't boot successfully with it's LINTR0
+		 * entry which decodes to low+level. The PPro manual says ExtINT
+		 * should be level, whereas the Pentium is edge. Setting the
+		 * Goliath to edge+high seems to cure the problem. Other PPro
+		 * MP tables (e.g. ASUS P/I-P65UP5 have a entry which decodes
+		 * to edge+high, so who knows.
+		 * Perhaps it would be best just to not set an ExtINT entry at
+		 * all, it shouldn't be needed for SMP mode.
+		 */
+		po = PcmpHIGH;
+		el = PcmpEDGE;
+		break;
+	}
+
+	/*
+	 */
+	if(bus->type == BusEISA && !po && !el /*&& !(i8259elcr & (1<<irq))*/){
+		po = PcmpHIGH;
+		el = PcmpEDGE;
+	}
+	if(!po)
+		po = bus->po;
+	if(po == PcmpLOW)
+		v |= ApicLOW;
+	else if(po != PcmpHIGH){
+		print("mpintrinit: bad polarity 0x%uX\n", po);
+		return ApicIMASK;
+	}
+
+	if(!el)
+		el = bus->el;
+	if(el == PcmpLEVEL)
+		v |= ApicLEVEL;
+	else if(el != PcmpEDGE){
+		print("mpintrinit: bad trigger 0x%uX\n", el);
+		return ApicIMASK;
+	}
+
+	return v;
+}
+
+static int
+mklintr(PCMPintr* p)
+{
+	Apic *apic;
+	Bus *bus;
+	int intin, v;
+
+	/*
+	 * The offsets of vectors for LINT[01] are known to be
+	 * 0 and 1 from the local APIC vector space at VectorLAPIC.
+	 */
+	if((bus = mpgetbus(p->busno)) == 0)
+		return 0;
+	intin = p->intin;
+
+	/*
+	 * Pentium Pros have problems if LINT[01] are set to ExtINT
+	 * so just bag it, SMP mode shouldn't need ExtINT anyway.
+	 */
+	if(p->intr == PcmpExtINT || p->intr == PcmpNMI)
+		v = ApicIMASK;
+	else
+		v = mpintrinit(bus, p, VectorLAPIC+intin, p->irq);
+
+	if(p->apicno == 0xFF){
+		for(apic = mpapic; apic <= &mpapic[MaxAPICNO]; apic++){
+			if((apic->flags & PcmpEN)
+			&& apic->type == PcmpPROCESSOR)
+				apic->lintr[intin] = v;
+		}
+	}
+	else{
+		apic = &mpapic[p->apicno];
+		if((apic->flags & PcmpEN) && apic->type == PcmpPROCESSOR)
+			apic->lintr[intin] = v;
+	}
+
+	return v;
+}
+
+static void
+checkmtrr(void)
+{
+	int i, vcnt;
+	Mach *mach0;
+
+	/*
+	 * If there are MTRR registers, snarf them for validation.
+	 */
+	if(!(m->cpuiddx & 0x1000))
+		return;
+
+	rdmsr(0x0FE, &m->mtrrcap);
+	rdmsr(0x2FF, &m->mtrrdef);
+	if(m->mtrrcap & 0x0100){
+		rdmsr(0x250, &m->mtrrfix[0]);
+		rdmsr(0x258, &m->mtrrfix[1]);
+		rdmsr(0x259, &m->mtrrfix[2]);
+		for(i = 0; i < 8; i++)
+			rdmsr(0x268+i, &m->mtrrfix[(i+3)]);
+	}
+	vcnt = m->mtrrcap & 0x00FF;
+	if(vcnt > nelem(m->mtrrvar))
+		vcnt = nelem(m->mtrrvar);
+	for(i = 0; i < vcnt; i++)
+		rdmsr(0x200+i, &m->mtrrvar[i]);
+
+	/*
+	 * If not the bootstrap processor, compare.
+	 */
+	if(m->machno == 0)
+		return;
+
+	mach0 = MACHP(0);
+	if(mach0->mtrrcap != m->mtrrcap)
+		print("mtrrcap%d: %lluX %lluX\n",
+			m->machno, mach0->mtrrcap, m->mtrrcap);
+	if(mach0->mtrrdef != m->mtrrdef)
+		print("mtrrdef%d: %lluX %lluX\n",
+			m->machno, mach0->mtrrdef, m->mtrrdef);
+	for(i = 0; i < 11; i++){
+		if(mach0->mtrrfix[i] != m->mtrrfix[i])
+			print("mtrrfix%d: i%d: %lluX %lluX\n",
+				m->machno, i, mach0->mtrrfix[i], m->mtrrfix[i]);
+	}
+	for(i = 0; i < vcnt; i++){
+		if(mach0->mtrrvar[i] != m->mtrrvar[i])
+			print("mtrrvar%d: i%d: %lluX %lluX\n",
+				m->machno, i, mach0->mtrrvar[i], m->mtrrvar[i]);
+	}
+}
+
+static void
+squidboy(Apic* apic)
+{
+//	iprint("Hello Squidboy\n");
+
+	machinit();
+	mmuinit();
+
+	cpuidentify();
+	cpuidprint();
+	checkmtrr();
+
+	lock(&mprdthilock);
+	mprdthi |= (1<<apic->apicno)<<24;
+	unlock(&mprdthilock);
+
+	lapicinit(apic);
+	lapiconline();
+	syncclock();
+	timersinit();
+
+	fpoff();
+
+	lock(&active);
+	active.machs |= 1<<m->machno;
+	unlock(&active);
+
+	while(!active.thunderbirdsarego)
+		microdelay(100);
+
+	schedinit();
+}
+
+static void
+mpstartap(Apic* apic)
+{
+	ulong *apbootp, *pdb, *pte;
+	Mach *mach, *mach0;
+	int i, machno;
+	uchar *p;
+
+	mach0 = MACHP(0);
+
+	/*
+	 * Initialise the AP page-tables and Mach structure. The page-tables
+	 * are the same as for the bootstrap processor with the exception of
+	 * the PTE for the Mach structure.
+	 * Xspanalloc will panic if an allocation can't be made.
+	 */
+	p = xspanalloc(4*BY2PG, BY2PG, 0);
+	pdb = (ulong*)p;
+	memmove(pdb, mach0->pdb, BY2PG);
+	p += BY2PG;
+
+	if((pte = mmuwalk(pdb, MACHADDR, 1, 0)) == nil)
+		return;
+	memmove(p, KADDR(PPN(*pte)), BY2PG);
+	*pte = PADDR(p)|PTEWRITE|PTEVALID;
+	if(mach0->havepge)
+		*pte |= PTEGLOBAL;
+	p += BY2PG;
+
+	mach = (Mach*)p;
+	if((pte = mmuwalk(pdb, MACHADDR, 2, 0)) == nil)
+		return;
+	*pte = PADDR(mach)|PTEWRITE|PTEVALID;
+	if(mach0->havepge)
+		*pte |= PTEGLOBAL;
+	p += BY2PG;
+
+	machno = apic->machno;
+	MACHP(machno) = mach;
+	mach->machno = machno;
+	mach->pdb = pdb;
+	mach->gdt = (Segdesc*)p;	/* filled by mmuinit */
+
+	/*
+	 * Tell the AP where its kernel vector and pdb are.
+	 * The offsets are known in the AP bootstrap code.
+	 */
+	apbootp = (ulong*)(APBOOTSTRAP+0x08);
+	*apbootp++ = (ulong)squidboy;
+	*apbootp++ = PADDR(pdb);
+	*apbootp = (ulong)apic;
+
+	/*
+	 * Universal Startup Algorithm.
+	 */
+	p = KADDR(0x467);
+	*p++ = PADDR(APBOOTSTRAP);
+	*p++ = PADDR(APBOOTSTRAP)>>8;
+	i = (PADDR(APBOOTSTRAP) & ~0xFFFF)/16;
+	*p++ = i;
+	*p = i>>8;
+
+	nvramwrite(0x0F, 0x0A);
+	lapicstartap(apic, PADDR(APBOOTSTRAP));
+	for(i = 0; i < 1000; i++){
+		lock(&mprdthilock);
+		if(mprdthi & ((1<<apic->apicno)<<24)){
+			unlock(&mprdthilock);
+			break;
+		}
+		unlock(&mprdthilock);
+		delay(10);
+	}
+	nvramwrite(0x0F, 0x00);
+}
+
+void
+mpinit(void)
+{
+	int ncpu;
+	char *cp;
+	PCMP *pcmp;
+	uchar *e, *p;
+	Apic *apic, *bpapic;
+
+	i8259init();
+	syncclock();
+
+	if(_mp_ == 0)
+		return;
+	pcmp = KADDR(_mp_->physaddr);
+
+	/*
+	 * Map the local APIC.
+	 */
+	if(mmukmap(pcmp->lapicbase, 0, 1024) == 0)
+		return;
+
+	bpapic = nil;
+
+	/*
+	 * Run through the table saving information needed for starting
+	 * application processors and initialising any I/O APICs. The table
+	 * is guaranteed to be in order such that only one pass is necessary.
+	 */
+	p = ((uchar*)pcmp)+sizeof(PCMP);
+	e = ((uchar*)pcmp)+pcmp->length;
+	while(p < e) switch(*p){
+
+	default:
+		print("mpinit: unknown PCMP type 0x%uX (e-p 0x%luX)\n",
+			*p, e-p);
+		while(p < e){
+			print("%uX ", *p);
+			p++;
+		}
+		break;
+
+	case PcmpPROCESSOR:
+		if(apic = mkprocessor((PCMPprocessor*)p)){
+			/*
+			 * Must take a note of bootstrap processor APIC
+			 * now as it will be needed in order to start the
+			 * application processors later and there's no
+			 * guarantee that the bootstrap processor appears
+			 * first in the table before the others.
+			 */
+			apic->addr = KADDR(pcmp->lapicbase);
+			if(apic->flags & PcmpBP)
+				bpapic = apic;
+		}
+		p += sizeof(PCMPprocessor);
+		continue;
+
+	case PcmpBUS:
+		mkbus((PCMPbus*)p);
+		p += sizeof(PCMPbus);
+		continue;
+
+	case PcmpIOAPIC:
+		if(apic = mkioapic((PCMPioapic*)p))
+			ioapicinit(apic, ((PCMPioapic*)p)->apicno);
+		p += sizeof(PCMPioapic);
+		continue;
+
+	case PcmpIOINTR:
+		mkiointr((PCMPintr*)p);
+		p += sizeof(PCMPintr);
+		continue;
+
+	case PcmpLINTR:
+		mklintr((PCMPintr*)p);
+		p += sizeof(PCMPintr);
+		continue;
+	}
+
+	/*
+	 * No bootstrap processor, no need to go further.
+	 */
+	if(bpapic == 0)
+		return;
+
+	lapicinit(bpapic);
+	lock(&mprdthilock);
+	mprdthi |= (1<<bpapic->apicno)<<24;
+	unlock(&mprdthilock);
+
+	/*
+	 * These interrupts are local to the processor
+	 * and do not appear in the I/O APIC so it is OK
+	 * to set them now.
+	 */
+	intrenable(IrqTIMER, lapicclock, 0, BUSUNKNOWN, "clock");
+	intrenable(IrqERROR, lapicerror, 0, BUSUNKNOWN, "lapicerror");
+	intrenable(IrqSPURIOUS, lapicspurious, 0, BUSUNKNOWN, "lapicspurious");
+	lapiconline();
+
+	checkmtrr();
+
+	/*
+	 * Initialise the application processors.
+	 */
+	if(cp = getconf("*ncpu")){
+		ncpu = strtol(cp, 0, 0);
+		if(ncpu < 1)
+			ncpu = 1;
+	}
+	else
+		ncpu = MaxAPICNO;
+	memmove((void*)APBOOTSTRAP, apbootstrap, sizeof(apbootstrap));
+	for(apic = mpapic; apic <= &mpapic[MaxAPICNO]; apic++){
+		if(ncpu <= 1)
+			break;
+		if((apic->flags & (PcmpBP|PcmpEN)) == PcmpEN
+		&& apic->type == PcmpPROCESSOR){
+			mpstartap(apic);
+			conf.nmach++;
+			ncpu--;
+		}
+	}
+
+	/*
+	 *  we don't really know the number of processors till
+	 *  here.
+	 *
+	 *  set conf.copymode here if nmach > 1.
+	 *  Should look for an ExtINT line and enable it.
+	 */
+	if(X86FAMILY(m->cpuidax) == 3 || conf.nmach > 1)
+		conf.copymode = 1;
+}
+
+static int
+mpintrenablex(Vctl* v, int tbdf)
+{
+	Bus *bus;
+	Aintr *aintr;
+	Apic *apic;
+	Pcidev *pcidev;
+	int bno, dno, irq, lo, n, type, vno;
+
+	/*
+	 * Find the bus.
+	 */
+	type = BUSTYPE(tbdf);
+	bno = BUSBNO(tbdf);
+	dno = BUSDNO(tbdf);
+	n = 0;
+	for(bus = mpbus; bus != nil; bus = bus->next){
+		if(bus->type != type)
+			continue;
+		if(n == bno)
+			break;
+		n++;
+	}
+	if(bus == nil){
+		print("ioapicirq: can't find bus type %d\n", type);
+		return -1;
+	}
+
+	/*
+	 * For PCI devices the interrupt pin (INT[ABCD]) and device
+	 * number are encoded into the entry irq field, so create something
+	 * to match on. The interrupt pin used by the device has to be
+	 * obtained from the PCI config space.
+	 */
+	if(bus->type == BusPCI){
+		pcidev = pcimatchtbdf(tbdf);
+		if(pcidev != nil && (n = pcicfgr8(pcidev, PciINTP)) != 0)
+			irq = (dno<<2)|(n-1);
+		else
+			irq = -1;
+		//print("pcidev %uX: irq %uX v->irq %uX\n", tbdf, irq, v->irq);
+	}
+	else
+		irq = v->irq;
+
+	/*
+	 * Find a matching interrupt entry from the list of interrupts
+	 * attached to this bus.
+	 */
+	for(aintr = bus->aintr; aintr; aintr = aintr->next){
+		if(aintr->intr->irq != irq)
+			continue;
+
+		/*
+		 * Check if already enabled. Multifunction devices may share
+		 * INT[A-D]# so, if already enabled, check the polarity matches
+		 * and the trigger is level.
+		 *
+		 * Should check the devices differ only in the function number,
+		 * but that can wait for the planned enable/disable rewrite.
+		 * The RDT read here is safe for now as currently interrupts
+		 * are never disabled once enabled.
+		 */
+		apic = aintr->apic;
+		ioapicrdtr(apic, aintr->intr->intin, 0, &lo);
+		if(!(lo & ApicIMASK)){
+			vno = lo & 0xFF;
+			n = mpintrinit(bus, aintr->intr, vno, v->irq);
+			n |= ApicLOGICAL;
+			if(n != lo || !(n & ApicLEVEL)){
+				print("mpintrenable: multiple botch irq%d, tbdf %uX, lo %8.8uX, n %8.8uX\n",
+					v->irq, tbdf, lo, n);
+				return -1;
+			}
+
+			v->isr = lapicisr;
+			v->eoi = lapiceoi;
+
+			return vno;
+		}
+
+		/*
+		 * With the APIC a unique vector can be assigned to each
+		 * request to enable an interrupt. There are two reasons this
+		 * is a good idea:
+		 * 1) to prevent lost interrupts, no more than 2 interrupts
+		 *    should be assigned per block of 16 vectors (there is an
+		 *    in-service entry and a holding entry for each priority
+		 *    level and there is one priority level per block of 16
+		 *    interrupts).
+		 * 2) each input pin on the IOAPIC will receive a different
+		 *    vector regardless of whether the devices on that pin use
+		 *    the same IRQ as devices on another pin.
+		 */
+		vno = VectorAPIC + (incref(&mpvnoref)-1)*8;
+		if(vno > MaxVectorAPIC){
+			print("mpintrenable: vno %d, irq %d, tbdf %uX\n",
+				vno, v->irq, tbdf);
+			return -1;
+		}
+		lo = mpintrinit(bus, aintr->intr, vno, v->irq);
+		//print("lo 0x%uX: busno %d intr %d vno %d irq %d elcr 0x%uX\n",
+		//	lo, bus->busno, aintr->intr->irq, vno,
+		//	v->irq, i8259elcr);
+		if(lo & ApicIMASK)
+			return -1;
+		lo |= ApicLOGICAL;
+
+		if((apic->flags & PcmpEN) && apic->type == PcmpIOAPIC){
+			lock(&mprdthilock);
+ 			ioapicrdtw(apic, aintr->intr->intin, mprdthi, lo);
+			unlock(&mprdthilock);
+		}
+		//else
+		//	print("lo not enabled 0x%uX %d\n",
+		//		apic->flags, apic->type);
+
+		v->isr = lapicisr;
+		v->eoi = lapiceoi;
+
+		return vno;
+	}
+
+	return -1;
+}
+
+int
+mpintrenable(Vctl* v)
+{
+	int irq, tbdf, vno;
+
+	/*
+	 * If the bus is known, try it.
+	 * BUSUNKNOWN is given both by [E]ISA devices and by
+	 * interrupts local to the processor (local APIC, coprocessor
+	 * breakpoint and page-fault).
+	 */
+	tbdf = v->tbdf;
+	if(tbdf != BUSUNKNOWN && (vno = mpintrenablex(v, tbdf)) != -1)
+		return vno;
+
+	irq = v->irq;
+	if(irq >= IrqLINT0 && irq <= MaxIrqLAPIC){
+		if(irq != IrqSPURIOUS)
+			v->isr = lapiceoi;
+		return VectorPIC+irq;
+	}
+	if(irq < 0 || irq > MaxIrqPIC){
+		print("mpintrenable: irq %d out of range\n", irq);
+		return -1;
+	}
+
+	/*
+	 * Either didn't find it or have to try the default buses
+	 * (ISA and EISA). This hack is due to either over-zealousness 
+	 * or laziness on the part of some manufacturers.
+	 *
+	 * The MP configuration table on some older systems
+	 * (e.g. ASUS PCI/E-P54NP4) has an entry for the EISA bus
+	 * but none for ISA. It also has the interrupt type and
+	 * polarity set to 'default for this bus' which wouldn't
+	 * be compatible with ISA.
+	 */
+	if(mpeisabus != -1){
+		vno = mpintrenablex(v, MKBUS(BusEISA, 0, 0, 0));
+		if(vno != -1)
+			return vno;
+	}
+	if(mpisabus != -1){
+		vno = mpintrenablex(v, MKBUS(BusISA, 0, 0, 0));
+		if(vno != -1)
+			return vno;
+	}
+
+	return -1;
+}
+
+static Lock mpshutdownlock;
+
+void
+mpshutdown(void)
+{
+	/*
+	 * To be done...
+	 */
+	if(!canlock(&mpshutdownlock)){
+		/*
+		 * If this processor received the CTRL-ALT-DEL from
+		 * the keyboard, acknowledge it. Send an INIT to self.
+		 */
+#ifdef FIXTHIS
+		if(lapicisr(VectorKBD))
+			lapiceoi(VectorKBD);
+#endif /* FIX THIS */
+		idle();
+	}
+
+	print("apshutdown: active = 0x%2.2uX\n", active.machs);
+	delay(1000);
+	splhi();
+
+	/*
+	 * INIT all excluding self.
+	 */
+	lapicicrw(0, 0x000C0000|ApicINIT);
+
+#ifdef notdef
+	/*
+	 * Often the BIOS hangs during restart if a conventional 8042
+	 * warm-boot sequence is tried. The following is Intel specific and
+	 * seems to perform a cold-boot, but at least it comes back.
+	 */
+	*(ushort*)KADDR(0x472) = 0x1234;	/* BIOS warm-boot flag */
+	outb(0xCF9, 0x02);
+	outb(0xCF9, 0x06);
+#else
+	pcireset();
+	i8042reset();
+#endif /* notdef */
+}
--- /dev/null
+++ b/os/pc/mp.h
@@ -1,0 +1,225 @@
+/*
+ * MultiProcessor Specification Version 1.[14].
+ */
+typedef struct {			/* floating pointer */
+	uchar	signature[4];		/* "_MP_" */
+	long	physaddr;		/* physical address of MP configuration table */
+	uchar	length;			/* 1 */
+	uchar	specrev;		/* [14] */
+	uchar	checksum;		/* all bytes must add up to 0 */
+	uchar	type;			/* MP system configuration type */
+	uchar	imcrp;
+	uchar	reserved[3];
+} _MP_;
+
+typedef struct {			/* configuration table header */
+	uchar	signature[4];		/* "PCMP" */
+	ushort	length;			/* total table length */
+	uchar	version;		/* [14] */
+	uchar	checksum;		/* all bytes must add up to 0 */
+	uchar	product[20];		/* product id */
+	ulong	oemtable;		/* OEM table pointer */
+	ushort	oemlength;		/* OEM table length */
+	ushort	entry;			/* entry count */
+	ulong	lapicbase;		/* address of local APIC */
+	ushort	xlength;		/* extended table length */
+	uchar	xchecksum;		/* extended table checksum */
+	uchar	reserved;
+} PCMP;
+
+typedef struct {			/* processor table entry */
+	uchar	type;			/* entry type (0) */
+	uchar	apicno;			/* local APIC id */
+	uchar	version;		/* local APIC verison */
+	uchar	flags;			/* CPU flags */
+	uchar	signature[4];		/* CPU signature */
+	ulong	feature;		/* feature flags from CPUID instruction */
+	uchar	reserved[8];
+} PCMPprocessor;
+
+typedef struct {			/* bus table entry */
+	uchar	type;			/* entry type (1) */
+	uchar	busno;			/* bus id */
+	char	string[6];		/* bus type string */
+} PCMPbus;
+
+typedef struct {			/* I/O APIC table entry */
+	uchar	type;			/* entry type (2) */
+	uchar	apicno;			/* I/O APIC id */
+	uchar	version;		/* I/O APIC version */
+	uchar	flags;			/* I/O APIC flags */
+	ulong	addr;			/* I/O APIC address */
+} PCMPioapic;
+
+typedef struct {			/* interrupt table entry */
+	uchar	type;			/* entry type ([34]) */
+	uchar	intr;			/* interrupt type */
+	ushort	flags;			/* interrupt flag */
+	uchar	busno;			/* source bus id */
+	uchar	irq;			/* source bus irq */
+	uchar	apicno;			/* destination APIC id */
+	uchar	intin;			/* destination APIC [L]INTIN# */
+} PCMPintr;
+
+typedef struct {			/* system address space mapping entry */
+	uchar	type;			/* entry type (128) */
+	uchar	length;			/* of this entry (20) */
+	uchar	busno;			/* bus id */
+	uchar	addrtype;
+	ulong	addrbase[2];
+	ulong	addrlength[2];
+} PCMPsasm;
+
+typedef struct {			/* bus hierarchy descriptor entry */
+	uchar	type;			/* entry type (129) */
+	uchar	length;			/* of this entry (8) */
+	uchar	busno;			/* bus id */
+	uchar	info;			/* bus info */
+	uchar	parent;			/* parent bus */
+	uchar	reserved[3];
+} PCMPhierarchy;
+
+typedef struct {			/* compatibility bus address space modifier entry */
+	uchar	type;			/* entry type (130) */
+	uchar	length;			/* of this entry (8) */
+	uchar	busno;			/* bus id */
+	uchar	modifier;		/* address modifier */
+	ulong	range;			/* predefined range list */
+} PCMPcbasm;
+
+enum {					/* table entry types */
+	PcmpPROCESSOR	= 0x00,		/* one entry per processor */
+	PcmpBUS		= 0x01,		/* one entry per bus */
+	PcmpIOAPIC	= 0x02,		/* one entry per I/O APIC */
+	PcmpIOINTR	= 0x03,		/* one entry per bus interrupt source */
+	PcmpLINTR	= 0x04,		/* one entry per system interrupt source */
+
+	PcmpSASM	= 0x80,
+	PcmpHIERARCHY	= 0x81,
+	PcmpCBASM	= 0x82,
+
+					/* PCMPprocessor and PCMPioapic flags */
+	PcmpEN		= 0x01,		/* enabled */
+	PcmpBP		= 0x02,		/* bootstrap processor */
+
+					/* PCMPiointr and PCMPlintr flags */
+	PcmpPOMASK	= 0x03,		/* polarity conforms to specifications of bus */
+	PcmpHIGH	= 0x01,		/* active high */
+	PcmpLOW		= 0x03,		/* active low */
+	PcmpELMASK	= 0x0C,		/* trigger mode of APIC input signals */
+	PcmpEDGE	= 0x04,		/* edge-triggered */
+	PcmpLEVEL	= 0x0C,		/* level-triggered */
+
+					/* PCMPiointr and PCMPlintr interrupt type */
+	PcmpINT		= 0x00,		/* vectored interrupt from APIC Rdt */
+	PcmpNMI		= 0x01,		/* non-maskable interrupt */
+	PcmpSMI		= 0x02,		/* system management interrupt */
+	PcmpExtINT	= 0x03,		/* vectored interrupt from external PIC */
+
+					/* PCMPsasm addrtype */
+	PcmpIOADDR	= 0x00,		/* I/O address */
+	PcmpMADDR	= 0x01,		/* memory address */
+	PcmpPADDR	= 0x02,		/* prefetch address */
+
+					/* PCMPhierarchy info */
+	PcmpSD		= 0x01,		/* subtractive decode bus */
+
+					/* PCMPcbasm modifier */
+	PcmpPR		= 0x01,		/* predefined range list */
+};
+
+/*
+ * Condensed form of the MP Configuration Table.
+ * This is created during a single pass through the MP Configuration
+ * table.
+ */
+typedef struct Aintr Aintr;
+typedef struct Bus Bus;
+typedef struct Apic Apic;
+
+typedef struct Bus {
+	uchar	type;
+	uchar	busno;
+	uchar	po;
+	uchar	el;
+
+	Aintr*	aintr;			/* interrupts tied to this bus */
+	Bus*	next;
+} Bus;
+
+typedef struct Aintr {
+	PCMPintr* intr;
+	Apic*	apic;
+	Aintr*	next;
+};
+
+typedef struct Apic {
+	int	type;
+	int	apicno;
+	ulong*	addr;			/* register base address */
+	int	flags;			/* PcmpBP|PcmpEN */
+
+	Lock;				/* I/O APIC: register access */
+	int	mre;			/* I/O APIC: maximum redirection entry */
+
+	int	lintr[2];		/* Local APIC */
+	int	machno;
+} Apic;
+
+enum {
+	MaxAPICNO	= 31,
+};
+
+enum {					/* I/O APIC registers */
+	IoapicID	= 0x00,		/* ID */
+	IoapicVER	= 0x01,		/* version */
+	IoapicARB	= 0x02,		/* arbitration ID */
+	IoapicRDT	= 0x10,		/* redirection table */
+};
+
+/*
+ * Common bits for
+ *	I/O APIC Redirection Table Entry;
+ *	Local APIC Local Interrupt Vector Table;
+ *	Local APIC Inter-Processor Interrupt;
+ *	Local APIC Timer Vector Table.
+ */
+enum {
+	ApicFIXED	= 0x00000000,	/* [10:8] Delivery Mode */
+	ApicLOWEST	= 0x00000100,	/* Lowest priority */
+	ApicSMI		= 0x00000200,	/* System Management Interrupt */
+	ApicRR		= 0x00000300,	/* Remote Read */
+	ApicNMI		= 0x00000400,
+	ApicINIT	= 0x00000500,	/* INIT/RESET */
+	ApicSTARTUP	= 0x00000600,	/* Startup IPI */
+	ApicExtINT	= 0x00000700,
+
+	ApicPHYSICAL	= 0x00000000,	/* [11] Destination Mode (RW) */
+	ApicLOGICAL	= 0x00000800,
+
+	ApicDELIVS	= 0x00001000,	/* [12] Delivery Status (RO) */
+	ApicHIGH	= 0x00000000,	/* [13] Interrupt Input Pin Polarity (RW) */
+	ApicLOW		= 0x00002000,
+	ApicRemoteIRR	= 0x00004000,	/* [14] Remote IRR (RO) */
+	ApicEDGE	= 0x00000000,	/* [15] Trigger Mode (RW) */
+	ApicLEVEL	= 0x00008000,
+	ApicIMASK	= 0x00010000,	/* [16] Interrupt Mask */
+};
+
+extern void ioapicrdtr(Apic*, int, int*, int*);
+extern void ioapicrdtw(Apic*, int, int, int);
+extern void ioapicinit(Apic*, int);
+extern void lapiconline(void);
+extern void lapicinit(Apic*);
+extern void lapicstartap(Apic*, int);
+extern void lapicerror(Ureg*, void*);
+extern void lapicspurious(Ureg*, void*);
+extern int lapicisr(int);
+extern int lapiceoi(int);
+extern void lapicicrw(int, int);
+
+extern void mpinit(void);
+extern void mpshutdown(void);
+extern int mpintrenable(Vctl*);
+
+extern _MP_ *_mp_;
--- /dev/null
+++ b/os/pc/pc
@@ -1,0 +1,143 @@
+dev
+	root
+	cons
+	arch
+	env
+	mnt
+	pipe
+	prog
+	rtc
+	srv
+	dup
+	ssl
+	cap
+
+	draw	screen vga vgax cga
+	pointer
+	vga
+
+	ip	bootp ip ipv6 ipaux iproute arp netlog ptclbsum386 iprouter plan9 nullmedium pktmedium
+	ether		netif netaux ethermedium
+
+#	ata # Breaks on Linux/amd64
+	audio		dma
+	uart
+	floppy	dma
+	tinyfs
+#	mouse # Breaks on Linux/amd64
+	dbg	x86break
+ip
+	tcp
+	udp
+#	rudp # Breaks on Linux/amd64
+#	igmp # Breaks on Linux/amd64
+	ipifc
+	icmp
+	icmp6
+	ipmux
+lib
+	interp
+	keyring
+	draw 
+	memlayer
+	memdraw
+	tk
+	sec
+	mp
+	math
+	kern
+
+link
+	ether79c970	pci
+	ether2114x	pci
+	ether82557	pci
+	ether83815	pci
+	etherelnk3	pci
+	ps2mouse
+	ethermedium
+#	pppmedium ppp compress # Breaks on Linux/amd64
+
+misc
+	vgas3	+cur vgasavage
+	vgamach64xx
+	cga
+	uarti8250
+
+mod
+	sys
+	draw
+	tk
+	keyring
+	math
+
+init
+#	shell
+	wminit
+
+code
+	int kernel_pool_pcnt = 10;
+	int main_pool_pcnt = 40;
+	int heap_pool_pcnt = 20;
+	int image_pool_pcnt = 40;
+	int cflag=0;
+	int swcursor=0;
+	int consoleprint=1;
+
+port
+	alarm
+	alloc
+	allocb
+	chan
+	dev
+	dial
+	dis
+	discall
+	exception
+	exportfs
+	inferno
+	latin1
+	nocache
+	nodynld
+	parse
+	pgrp
+	print
+	proc
+	qio
+	qlock
+	random
+	sysfile
+	taslock
+	xalloc
+
+root
+	/chan	/
+	/dev	/
+	/dis
+	/env	/
+	/fd	/
+	/n	/
+	/n/remote	/
+	/net	/
+	/nvfs	/
+	/prog	/
+	/dis/lib
+	/dis/svc
+	/dis/wm
+	/osinit.dis
+	/dis/sh.dis
+	/dis/ls.dis
+	/dis/cat.dis
+	/dis/bind.dis
+	/dis/mount.dis
+	/dis/pwd.dis
+	/dis/echo.dis
+	/dis/cd.dis
+	/dis/lib/bufio.dis
+	/dis/lib/string.dis
+	/dis/lib/readdir.dis
+	/dis/lib/workdir.dis
+	/dis/lib/daytime.dis
+	/dis/lib/auth.dis
+	/dis/lib/ssl.dis
+	/dis/lib/arg.dis
+	/dis/lib/filepat.dis
--- /dev/null
+++ b/os/pc/pc4e
@@ -1,0 +1,148 @@
+dev
+	root
+	cons
+	arch
+	pnp	pci
+	env
+	mnt
+	pipe
+	prog
+	rtc
+	srv
+	dup
+	ssl
+	cap
+	draw	
+	ip	bootp ip ipv6 ipaux iproute arp netlog ptclbsum386 iprouter plan9 nullmedium pktmedium netaux
+	ether	netif netaux ethermedium
+
+#	ata
+#	audio
+	uart
+	floppy	dma
+#	tinyfs
+#	mouse
+#	dbg	x86break
+ip
+	tcp
+	udp
+#	rudp
+#	igmp
+	ipifc
+	icmp
+	icmp6
+	ipmux
+lib
+	interp
+	keyring
+	draw 
+	memlayer
+	memdraw
+	tk
+	sec
+	mp
+	math
+	kern
+
+link
+#	ether2114x	pci
+#	ether82557	pci
+	ether83815	pci
+	etherelnk3	pci
+#	ps2mouse
+#	pppmedium ppp compress
+	ethermedium
+
+misc
+	cgamemscr
+#	vgaclgd542x
+#	vgas3
+#	vgamach64ct
+	uarti8250
+
+mod
+	sys
+	draw
+	tk
+	keyring
+	crypt
+	ipints
+
+	math
+
+init
+	i4e
+
+code
+	int kernel_pool_pcnt = 10;
+	int main_pool_pcnt = 40;
+	int heap_pool_pcnt = 20;
+	int image_pool_pcnt = 40;
+	int cflag=0;
+	int swcursor=0;
+	int consoleprint=1;
+
+port
+	alarm
+	alloc
+	allocb
+	chan
+	dev
+	dial
+	dis
+	discall
+	exception
+	exportfs
+	inferno
+	latin1
+	nocache
+	nodynld
+	parse
+	pgrp
+	print
+	proc
+	qio
+	qlock
+	random
+	sysfile
+	taslock
+	xalloc
+
+root
+	/chan	/
+	/dev	/
+	/dis	/
+	/env	/
+	/fd	/
+	/n
+	/n/remote
+	/net	/
+	/net.alt	/
+	/nvfs	/
+	/prog	/
+	/dis/lib
+	/dis/svc
+	/dis/wm
+	/osinit.dis
+	/dis/sh.dis
+	/dis/ls.dis
+	/dis/cat.dis
+	/dis/bind.dis
+	/dis/mount.dis
+	/dis/pwd.dis
+	/dis/echo.dis
+	/dis/cd.dis
+	/dis/lib/arg.dis
+	/dis/lib/bufio.dis
+	/dis/lib/dhcpclient.dis
+	/dis/lib/filepat.dis
+	/dis/lib/ip.dis
+	/dis/lib/string.dis
+	/dis/lib/readdir.dis
+	/dis/lib/workdir.dis
+	/dis/lib/daytime.dis
+	/dis/lib/auth.dis
+	/dis/lib/ssl.dis
+	/keydb	/
+	/keydb/mutual	/usr/i4e/keyring/mutual
+	/keydb/spree /usr/i4e/keyring/spree
--- /dev/null
+++ b/os/pc/pcdisk
@@ -1,0 +1,157 @@
+dev
+	root
+	cons
+	arch
+	env
+	mnt
+	pipe
+	prog
+	rtc
+	srv
+	dup
+	ssl
+	cap
+
+	ip	bootp ip ipv6 ipaux iproute arp netlog ptclbsum386 iprouter plan9 nullmedium pktmedium
+	ether		netif netaux ethermedium
+
+	draw	screen vga vgax cga
+	pointer
+	vga
+
+	sd
+	ds
+	floppy	dma
+
+#	audio
+	uart
+	tinyfs
+
+#	dbg	x86break
+
+ip
+	tcp
+	udp
+#	rudp
+#	igmp
+	ipifc
+	icmp
+	icmp6
+	ipmux
+lib
+	interp
+	keyring
+	sec
+	mp
+	draw 
+	memlayer
+	memdraw
+	tk
+	math
+	kern
+
+link
+	ether2114x	pci
+#	ether82557	pci
+	ether83815	pci
+	etherelnk3	pci
+	ps2mouse
+	ethermedium
+#	pppmedium ppp compress
+
+misc
+	vgas3	+cur vgasavage
+	vgamach64xx
+	cga
+
+	sdata	pci sdscsi
+	sd53c8xx	pci sdscsi
+
+	uarti8250
+
+mod
+	sys
+	draw
+	tk
+	keyring
+	crypt
+	ipints
+
+	math
+
+init
+	wminit
+
+code
+	int kernel_pool_pcnt = 10;
+	int main_pool_pcnt = 40;
+	int heap_pool_pcnt = 20;
+	int image_pool_pcnt = 40;
+	int cflag=0;
+	int swcursor=0;
+	int consoleprint=0;
+	int novgascreen=1;
+
+port
+	alarm
+	alloc
+	allocb
+	chan
+	dev
+	dial
+	dis
+	discall
+	exception
+	exportfs
+	inferno
+	latin1
+	nocache
+	nodynld
+	parse
+	pgrp
+	print
+	proc
+	qio
+	qlock
+	random
+	sysfile
+	taslock
+	xalloc
+
+root
+	/chan
+	/dev
+	/dis
+	/env
+	/fd	/
+	/n
+	/n/remote
+	/net
+	/nvfs
+	/prog
+	/dis/lib
+	/dis/svc
+	/dis/wm
+	/osinit.dis
+	/dis/sh.dis
+	/dis/ls.dis
+	/dis/cat.dis
+	/dis/bind.dis
+	/dis/mount.dis
+	/dis/pwd.dis
+	/dis/echo.dis
+	/dis/cd.dis
+	/dis/lib/bufio.dis
+	/dis/lib/string.dis
+	/dis/lib/readdir.dis
+	/dis/lib/workdir.dis
+	/dis/lib/daytime.dis
+	/dis/lib/auth.dis
+	/dis/lib/ssl.dis
+
+# kfs
+	/dis/disk/kfs.dis
+	/dis/lib/arg.dis
+	/dis/lib/daytime.dis
+	/dis/lib/string.dis
+	/dis/lib/styx.dis
--- /dev/null
+++ b/os/pc/pci.acid
@@ -1,0 +1,252 @@
+// ACID PCI support
+
+pciclassdb = { 
+	{0x0000, "Unclassified device", {
+		{0x0000,"Non-VGA unclassified device"},
+		{0x0001,"VGA compatible unclassified device"},
+	}},
+	{0x0001, "Mass storage controller", {
+		{0x0000,"SCSI storage controller"},
+		{0x0001,"IDE interface"},
+		{0x0002,"Floppy disk controller"},
+		{0x0003,"IPI bus controller"},
+		{0x0004,"RAID bus controller"},
+		{0x0080,"Unknown mass storage controller"},
+	}},
+	{0x0002, "Network controller", {
+		{0x0000,"Ethernet controller"},
+		{0x0001,"Token ring network controller"},
+		{0x0002,"FDDI network controller"},
+		{0x0003,"ATM network controller"},
+		{0x0080,"Network controller"},
+	}},
+	{0x0003, "Display controller", {
+		{0x0000,"VGA compatible controller"},
+		{0x0001,"XGA compatible controller"},
+		{0x0080,"Display controller"},
+	}},
+	{0x0004, "Multimedia controller", {
+		{0x0000,"Multimedia video controller"},
+		{0x0001,"Multimedia audio controller"},
+		{0x0080,"Multimedia controller"},
+	}},
+	{0x0005, "Memory controller", {
+		{0x0000,"RAM memory"},
+		{0x0001,"FLASH memory"},
+		{0x0080,"Memory"},
+	}},
+	{0x0006, "Bridge", {
+		{0x0000,"Host bridge"},
+		{0x0001,"ISA bridge"},
+		{0x0002,"EISA bridge"},
+		{0x0003,"MicroChannel bridge"},
+		{0x0004,"PCI bridge"},
+		{0x0005,"PCMCIA bridge"},
+		{0x0006,"NuBus bridge"},
+		{0x0007,"CardBus bridge"},
+		{0x0080,"Bridge"},
+	}},
+	{0x0007, "Communication controller", {
+		{0x0000,"Serial controller"},
+		{0x0001,"Parallel controller"},
+		{0x0080,"Communication controller"},
+	}},
+	{0x0008, "Generic system peripheral", {
+		{0x0000,"PIC"},
+		{0x0001,"DMA controller"},
+		{0x0002,"Timer"},
+		{0x0003,"RTC"},
+		{0x0080,"System peripheral"},
+	}},
+	{0x0009, "Input device controller", {
+		{0x0000,"Keyboard controller"},
+		{0x0001,"Digitizer Pen"},
+		{0x0002,"Mouse controller"},
+		{0x0080,"Input device controller"},
+	}},
+	{0x000A, "Docking station", {
+		{0x0000,"Generic Docking Station"},
+		{0x0080,"Docking Station"},
+	}},
+	{0x000B, "Processor", {
+		{0x0000,"386"},
+		{0x0001,"486"},
+		{0x0002,"Pentium"},
+		{0x0010,"Alpha"},
+		{0x0020,"Power PC"},
+		{0x0040,"Co-processor"},
+	}},
+	{0x000C, "Serial bus controller", {
+		{0x0000,"FireWire (IEEE 1394)"},
+		{0x0001,"ACCESS Bus"},
+		{0x0002,"SSA"},
+		{0x0003,"USB Controller"},
+		{0x0004,"Fiber Channel"},
+	}},
+};
+
+//
+// Include the vendor and device id database
+//
+
+include( "pcidb.acid" );
+
+defn test(thingy)
+{
+	return thingy;
+}
+
+defn BUSFNO(tbdf)
+{
+	return (((tbdf\X)>>8)&0x07);
+}
+
+defn BUSDNO(tbdf)
+{
+	return (((tbdf\X)>>11)&0x1F);
+}
+
+defn BUSBNO(tbdf)
+{
+	return (((tbdf\X)>>16)&0xFF);
+}
+
+defn lookup( key, dictionary )
+{
+	local d, e;
+
+	d = dictionary;
+	while(d != {}) do {
+		e = head d;
+		if e[0] == key then 
+			return e;
+		d = tail d;
+	}
+	return {};
+}
+
+defn pciclass( ccru )
+{
+	local c, sc;
+	local class, subclass;
+	c = ccru >> 8;
+	sc = ccru & 0xff;
+
+	class = lookup( c, pciclassdb );
+	subclass = lookup( sc, class[2] );
+
+	if (subclass != {}) then {
+		print(" ",subclass[1]);
+		return 1;
+	}
+	if (class != {}) then {
+		print(" ",class[1]);
+		return 1;
+	}
+
+ 	print(" type=",ccru\x);
+	return 0;
+}
+
+defn pcivendor( vid )
+{
+	local v;
+	v = lookup( vid, pcivendordb );
+	if (v != {}) then {
+		print(" ",v[1]);
+	} else {
+		print(" VendorID=",vid\x,":");
+	}
+}
+
+defn pcidev( vid, did )
+{
+	local v;
+	local d;
+	v = lookup( vid, pcivendordb );
+	if (v != {}) then {
+		d = lookup( did, v[2] );
+		if (d != {}) then {
+			print(" ",d[1]);
+			return 1;
+		}
+	}
+	print(" DeviceID=",did\x);
+	return 0;
+}
+
+//
+// Dump PCI Info (short form)
+//
+
+defn pciinfo( r ) 
+{
+	local t;
+	local pcicount;
+	t = r;
+	pcicount = 0\d;
+	while t != 0 do {
+		print(pcicount\d,":","Bus:",BUSBNO(t.tbdf)\u," dev:",BUSDNO(t.tbdf)\u," fn:",BUSFNO(t.tbdf)\u,":");
+		pciclass(t.ccru);
+		print(":");
+		pcivendor(t.vid);
+		pcidev(t.vid, t.did);
+		print("\n");
+		pcicount = pcicount+1 ;
+		t = t.link;
+	}
+	t = r;
+	while t!= 0 do {
+		if (t.bridge !=0) then
+			pciinfo(t.bridge);
+		t = t.link;
+	}
+}
+
+defn lspci()
+{
+	pciinfo( *pciroot );
+}
+
+//
+// Dump PCI Info (long form - includes interrupt lines & memory segments)
+//
+
+defn pcidumper( r )
+{
+	local t;
+	local m;
+	t = r;
+	while t != 0 do {
+		print("Bus\t",BUSBNO(t.tbdf)\u);
+		print(", device\t",BUSDNO(t.tbdf)\u);
+		print(", function\t",BUSFNO(t.tbdf)\u,":\n");
+		print("  ");
+		pciclass(t.ccru);
+		print(":");
+		pcivendor(t.vid);
+		pcidev(t.vid, t.did);
+		print("\n");
+		print("   Interrupt Line: ",t.intl\u,"\n");
+		m =0\d;
+		loop 0,5 do {
+			if t.mem[m] != 0 then
+				print("\t",(m/2)\d,":",t.mem[m]\X," ",t.mem[m+1]\X,"\n");
+			m = m + 2;
+		}
+		t = t.link;
+	}
+	t = r;
+	while t!= 0 do {
+		if (t.bridge !=0) then
+			pcidumper(t.bridge);
+		t = t.link;
+	}
+}
+
+defn pcidump()
+{
+	pcidumper( *pciroot );
+}
+
+print("/os/pc/pci");
--- /dev/null
+++ b/os/pc/pci.c
@@ -1,0 +1,1340 @@
+/*
+ * PCI support code.
+ * Needs a massive rewrite.
+ */
+#include "u.h"
+#include "../port/lib.h"
+#include "mem.h"
+#include "dat.h"
+#include "fns.h"
+#include "io.h"
+#include "../port/error.h"
+
+#define DBG	if(0) pcilog
+
+struct
+{
+	char	output[16384];
+	int	ptr;
+}PCICONS;
+
+int
+pcilog(char *fmt, ...)
+{
+	int n;
+	va_list arg;
+	char buf[PRINTSIZE];
+
+	va_start(arg, fmt);
+	n = vseprint(buf, buf+sizeof(buf), fmt, arg) - buf;
+	va_end(arg);
+
+	memmove(PCICONS.output+PCICONS.ptr, buf, n);
+	PCICONS.ptr += n;
+	return n;
+}
+
+enum
+{					/* configuration mechanism #1 */
+	PciADDR		= 0xCF8,	/* CONFIG_ADDRESS */
+	PciDATA		= 0xCFC,	/* CONFIG_DATA */
+
+					/* configuration mechanism #2 */
+	PciCSE		= 0xCF8,	/* configuration space enable */
+	PciFORWARD	= 0xCFA,	/* which bus */
+
+	MaxFNO		= 7,
+	MaxUBN		= 255,
+};
+
+enum
+{					/* command register */
+	IOen		= (1<<0),
+	MEMen		= (1<<1),
+	MASen		= (1<<2),
+	MemWrInv	= (1<<4),
+	PErrEn		= (1<<6),
+	SErrEn		= (1<<8),
+};
+
+static Lock pcicfglock;
+static Lock pcicfginitlock;
+static int pcicfgmode = -1;
+static int pcimaxbno = 7;
+static int pcimaxdno;
+static Pcidev* pciroot;
+static Pcidev* pcilist;
+static Pcidev* pcitail;
+static int nobios, nopcirouting;
+
+static int pcicfgrw32(int, int, int, int);
+static int pcicfgrw16(int, int, int, int);
+static int pcicfgrw8(int, int, int, int);
+
+static char* bustypes[] = {
+	"CBUSI",
+	"CBUSII",
+	"EISA",
+	"FUTURE",
+	"INTERN",
+	"ISA",
+	"MBI",
+	"MBII",
+	"MCA",
+	"MPI",
+	"MPSA",
+	"NUBUS",
+	"PCI",
+	"PCMCIA",
+	"TC",
+	"VL",
+	"VME",
+	"XPRESS",
+};
+
+#pragma	varargck	type	"T"	int
+
+static int
+tbdffmt(Fmt* fmt)
+{
+	char *p;
+	int l, r, type, tbdf;
+
+	if((p = malloc(READSTR)) == nil)
+		return fmtstrcpy(fmt, "(tbdfconv)");
+		
+	switch(fmt->r){
+	case 'T':
+		tbdf = va_arg(fmt->args, int);
+		type = BUSTYPE(tbdf);
+		if(type < nelem(bustypes))
+			l = snprint(p, READSTR, bustypes[type]);
+		else
+			l = snprint(p, READSTR, "%d", type);
+		snprint(p+l, READSTR-l, ".%d.%d.%d",
+			BUSBNO(tbdf), BUSDNO(tbdf), BUSFNO(tbdf));
+		break;
+
+	default:
+		snprint(p, READSTR, "(tbdfconv)");
+		break;
+	}
+	r = fmtstrcpy(fmt, p);
+	free(p);
+
+	return r;
+}
+
+ulong
+pcibarsize(Pcidev *p, int rno)
+{
+	ulong v, size;
+
+	v = pcicfgrw32(p->tbdf, rno, 0, 1);
+	pcicfgrw32(p->tbdf, rno, 0xFFFFFFF0, 0);
+	size = pcicfgrw32(p->tbdf, rno, 0, 1);
+	if(v & 1)
+		size |= 0xFFFF0000;
+	pcicfgrw32(p->tbdf, rno, v, 0);
+
+	return -(size & ~0x0F);
+}
+
+static int
+pcisizcmp(void *a, void *b)
+{
+	Pcisiz *aa, *bb;
+
+	aa = a;
+	bb = b;
+	return aa->siz - bb->siz;
+}
+
+static ulong
+pcimask(ulong v)
+{
+	ulong m;
+
+	m = BI2BY*sizeof(v);
+	for(m = 1<<(m-1); m != 0; m >>= 1) {
+		if(m & v)
+			break;
+	}
+
+	m--;
+	if((v & m) == 0)
+		return v;
+
+	v |= m;
+	return v+1;
+}
+
+static void
+pcibusmap(Pcidev *root, ulong *pmema, ulong *pioa, int wrreg)
+{
+	Pcidev *p;
+	int ntb, i, size, rno, hole;
+	ulong v, mema, ioa, sioa, smema, base, limit;
+	Pcisiz *table, *tptr, *mtb, *itb;
+
+	if(!nobios)
+		return;
+
+	ioa = *pioa;
+	mema = *pmema;
+
+	DBG("pcibusmap wr=%d %T mem=%luX io=%luX\n", 
+		wrreg, root->tbdf, mema, ioa);
+
+	ntb = 0;
+	for(p = root; p != nil; p = p->link)
+		ntb++;
+
+	ntb *= (PciCIS-PciBAR0)/4;
+	table = malloc(2*ntb*sizeof(Pcisiz));
+	itb = table;
+	mtb = table+ntb;
+
+	/*
+	 * Build a table of sizes
+	 */
+	for(p = root; p != nil; p = p->link) {
+		if(p->ccrb == 0x06) {
+			if(p->ccru != 0x04 || p->bridge == nil) {
+//				DBG("pci: ignored bridge %T\n", p->tbdf);
+				continue;
+			}
+
+			sioa = ioa;
+			smema = mema;
+			pcibusmap(p->bridge, &smema, &sioa, 0);
+
+			hole = pcimask(smema-mema);
+			if(hole < (1<<20))
+				hole = 1<<20;
+			p->mema.size = hole;
+
+			hole = pcimask(sioa-ioa);
+			if(hole < (1<<12))
+				hole = 1<<12;
+
+			p->ioa.size = hole;
+
+			itb->dev = p;
+			itb->bar = -1;
+			itb->siz = p->ioa.size;
+			itb++;
+
+			mtb->dev = p;
+			mtb->bar = -1;
+			mtb->siz = p->mema.size;
+			mtb++;
+			continue;
+		}
+
+		for(i = 0; i <= 5; i++) {
+			rno = PciBAR0 + i*4;
+			v = pcicfgrw32(p->tbdf, rno, 0, 1);
+			size = pcibarsize(p, rno);
+			if(size == 0)
+				continue;
+
+			if(v & 1) {
+				itb->dev = p;
+				itb->bar = i;
+				itb->siz = size;
+				itb++;
+			}
+			else {
+				mtb->dev = p;
+				mtb->bar = i;
+				mtb->siz = size;
+				mtb++;
+			}
+
+			p->mem[i].size = size;
+		}
+	}
+
+	/*
+	 * Sort both tables IO smallest first, Memory largest
+	 */
+	qsort(table, itb-table, sizeof(Pcisiz), pcisizcmp);
+	tptr = table+ntb;
+	qsort(tptr, mtb-tptr, sizeof(Pcisiz), pcisizcmp);
+
+	/*
+	 * Allocate IO address space on this bus
+	 */
+	for(tptr = table; tptr < itb; tptr++) {
+		hole = tptr->siz;
+		if(tptr->bar == -1)
+			hole = 1<<12;
+		ioa = (ioa+hole-1) & ~(hole-1);
+
+		p = tptr->dev;
+		if(tptr->bar == -1)
+			p->ioa.bar = ioa;
+		else {
+			p->pcr |= IOen;
+			p->mem[tptr->bar].bar = ioa|1;
+			if(wrreg)
+				pcicfgrw32(p->tbdf, PciBAR0+(tptr->bar*4), ioa|1, 0);
+		}
+
+		ioa += tptr->siz;
+	}
+
+	/*
+	 * Allocate Memory address space on this bus
+	 */
+	for(tptr = table+ntb; tptr < mtb; tptr++) {
+		hole = tptr->siz;
+		if(tptr->bar == -1)
+			hole = 1<<20;
+		mema = (mema+hole-1) & ~(hole-1);
+
+		p = tptr->dev;
+		if(tptr->bar == -1)
+			p->mema.bar = mema;
+		else {
+			p->pcr |= MEMen;
+			p->mem[tptr->bar].bar = mema;
+			if(wrreg)
+				pcicfgrw32(p->tbdf, PciBAR0+(tptr->bar*4), mema, 0);
+		}
+		mema += tptr->siz;
+	}
+
+	*pmema = mema;
+	*pioa = ioa;
+	free(table);
+
+	if(wrreg == 0)
+		return;
+
+	/*
+	 * Finally set all the bridge addresses & registers
+	 */
+	for(p = root; p != nil; p = p->link) {
+		if(p->bridge == nil) {
+			pcicfgrw8(p->tbdf, PciLTR, 64, 0);
+
+			p->pcr |= MASen;
+			pcicfgrw16(p->tbdf, PciPCR, p->pcr, 0);
+			continue;
+		}
+
+		base = p->ioa.bar;
+		limit = base+p->ioa.size-1;
+		v = pcicfgrw32(p->tbdf, PciIBR, 0, 1);
+		v = (v&0xFFFF0000)|(limit & 0xF000)|((base & 0xF000)>>8);
+		pcicfgrw32(p->tbdf, PciIBR, v, 0);
+		v = (limit & 0xFFFF0000)|(base>>16);
+		pcicfgrw32(p->tbdf, PciIUBR, v, 0);
+
+		base = p->mema.bar;
+		limit = base+p->mema.size-1;
+		v = (limit & 0xFFF00000)|((base & 0xFFF00000)>>16);
+		pcicfgrw32(p->tbdf, PciMBR, v, 0);
+
+		/*
+		 * Disable memory prefetch
+		 */
+		pcicfgrw32(p->tbdf, PciPMBR, 0x0000FFFF, 0);
+		pcicfgrw8(p->tbdf, PciLTR, 64, 0);
+
+		/*
+		 * Enable the bridge
+		 */
+		p->pcr |= IOen|MEMen|MASen;
+		pcicfgrw32(p->tbdf, PciPCR, 0xFFFF0000|p->pcr , 0);
+
+		sioa = p->ioa.bar;
+		smema = p->mema.bar;
+		pcibusmap(p->bridge, &smema, &sioa, 1);
+	}
+}
+
+static int
+pcilscan(int bno, Pcidev** list)
+{
+	Pcidev *p, *head, *tail;
+	int dno, fno, i, hdt, l, maxfno, maxubn, rno, sbn, tbdf, ubn;
+
+	maxubn = bno;
+	head = nil;
+	tail = nil;
+	for(dno = 0; dno <= pcimaxdno; dno++){
+		maxfno = 0;
+		for(fno = 0; fno <= maxfno; fno++){
+			/*
+			 * For this possible device, form the
+			 * bus+device+function triplet needed to address it
+			 * and try to read the vendor and device ID.
+			 * If successful, allocate a device struct and
+			 * start to fill it in with some useful information
+			 * from the device's configuration space.
+			 */
+			tbdf = MKBUS(BusPCI, bno, dno, fno);
+			l = pcicfgrw32(tbdf, PciVID, 0, 1);
+			if(l == 0xFFFFFFFF || l == 0)
+				continue;
+			p = malloc(sizeof(*p));
+			p->tbdf = tbdf;
+			p->vid = l;
+			p->did = l>>16;
+
+			if(pcilist != nil)
+				pcitail->list = p;
+			else
+				pcilist = p;
+			pcitail = p;
+
+			p->pcr = pcicfgr16(p, PciPCR);
+			p->rid = pcicfgr8(p, PciRID);
+			p->ccrp = pcicfgr8(p, PciCCRp);
+			p->ccru = pcicfgr8(p, PciCCRu);
+			p->ccrb = pcicfgr8(p, PciCCRb);
+			p->cls = pcicfgr8(p, PciCLS);
+			p->ltr = pcicfgr8(p, PciLTR);
+
+			p->intl = pcicfgr8(p, PciINTL);
+
+			/*
+			 * If the device is a multi-function device adjust the
+			 * loop count so all possible functions are checked.
+			 */
+			hdt = pcicfgr8(p, PciHDT);
+			if(hdt & 0x80)
+				maxfno = MaxFNO;
+
+			/*
+			 * If appropriate, read the base address registers
+			 * and work out the sizes.
+			 */
+			switch(p->ccrb) {
+			case 0x01:		/* mass storage controller */
+			case 0x02:		/* network controller */
+			case 0x03:		/* display controller */
+			case 0x04:		/* multimedia device */
+			case 0x07:		/* simple comm. controllers */
+			case 0x08:		/* base system peripherals */
+			case 0x09:		/* input devices */
+			case 0x0A:		/* docking stations */
+			case 0x0B:		/* processors */
+			case 0x0C:		/* serial bus controllers */
+				if((hdt & 0x7F) != 0)
+					break;
+				rno = PciBAR0 - 4;
+				for(i = 0; i < nelem(p->mem); i++) {
+					rno += 4;
+					p->mem[i].bar = pcicfgr32(p, rno);
+					p->mem[i].size = pcibarsize(p, rno);
+				}
+				break;
+
+			case 0x00:
+			case 0x05:		/* memory controller */
+			case 0x06:		/* bridge device */
+			default:
+				break;
+			}
+
+			if(head != nil)
+				tail->link = p;
+			else
+				head = p;
+			tail = p;
+		}
+	}
+
+	*list = head;
+	for(p = head; p != nil; p = p->link){
+		/*
+		 * Find PCI-PCI bridges and recursively descend the tree.
+		 */
+		if(p->ccrb != 0x06 || p->ccru != 0x04)
+			continue;
+
+		/*
+		 * If the secondary or subordinate bus number is not
+		 * initialised try to do what the PCI BIOS should have
+		 * done and fill in the numbers as the tree is descended.
+		 * On the way down the subordinate bus number is set to
+		 * the maximum as it's not known how many buses are behind
+		 * this one; the final value is set on the way back up.
+		 */
+		sbn = pcicfgr8(p, PciSBN);
+		ubn = pcicfgr8(p, PciUBN);
+
+		if(sbn == 0 || ubn == 0 || nobios) {
+			sbn = maxubn+1;
+			/*
+			 * Make sure memory, I/O and master enables are
+			 * off, set the primary, secondary and subordinate
+			 * bus numbers and clear the secondary status before
+			 * attempting to scan the secondary bus.
+			 *
+			 * Initialisation of the bridge should be done here.
+			 */
+			pcicfgw32(p, PciPCR, 0xFFFF0000);
+			l = (MaxUBN<<16)|(sbn<<8)|bno;
+			pcicfgw32(p, PciPBN, l);
+			pcicfgw16(p, PciSPSR, 0xFFFF);
+			maxubn = pcilscan(sbn, &p->bridge);
+			l = (maxubn<<16)|(sbn<<8)|bno;
+
+			pcicfgw32(p, PciPBN, l);
+		}
+		else {
+			if(ubn > maxubn)
+				maxubn = ubn;
+			pcilscan(sbn, &p->bridge);
+		}
+	}
+
+	return maxubn;
+}
+
+int
+pciscan(int bno, Pcidev **list)
+{
+	int ubn;
+
+	lock(&pcicfginitlock);
+	ubn = pcilscan(bno, list);
+	unlock(&pcicfginitlock);
+	return ubn;
+}
+
+static uchar 
+pIIxget(Pcidev *router, uchar link)
+{
+	uchar pirq;
+
+	/* link should be 0x60, 0x61, 0x62, 0x63 */
+	pirq = pcicfgr8(router, link);
+	return (pirq < 16)? pirq: 0;
+}
+
+static void 
+pIIxset(Pcidev *router, uchar link, uchar irq)
+{
+	pcicfgw8(router, link, irq);
+}
+
+static uchar 
+viaget(Pcidev *router, uchar link)
+{
+	uchar pirq;
+
+	/* link should be 1, 2, 3, 5 */
+	pirq = (link < 6)? pcicfgr8(router, 0x55 + (link>>1)): 0;
+
+	return (link & 1)? (pirq >> 4): (pirq & 15);
+}
+
+static void 
+viaset(Pcidev *router, uchar link, uchar irq)
+{
+	uchar pirq;
+
+	pirq = pcicfgr8(router, 0x55 + (link >> 1));
+	pirq &= (link & 1)? 0x0f: 0xf0;
+	pirq |= (link & 1)? (irq << 4): (irq & 15);
+	pcicfgw8(router, 0x55 + (link>>1), pirq);
+}
+
+static uchar 
+optiget(Pcidev *router, uchar link)
+{
+	uchar pirq = 0;
+
+	/* link should be 0x02, 0x12, 0x22, 0x32 */
+	if ((link & 0xcf) == 0x02)
+		pirq = pcicfgr8(router, 0xb8 + (link >> 5));
+	return (link & 0x10)? (pirq >> 4): (pirq & 15);
+}
+
+static void 
+optiset(Pcidev *router, uchar link, uchar irq)
+{
+	uchar pirq;
+
+	pirq = pcicfgr8(router, 0xb8 + (link >> 5));
+    	pirq &= (link & 0x10)? 0x0f : 0xf0;
+    	pirq |= (link & 0x10)? (irq << 4): (irq & 15);
+	pcicfgw8(router, 0xb8 + (link >> 5), pirq);
+}
+
+static uchar 
+aliget(Pcidev *router, uchar link)
+{
+	/* No, you're not dreaming */
+	static const uchar map[] = { 0, 9, 3, 10, 4, 5, 7, 6, 1, 11, 0, 12, 0, 14, 0, 15 };
+	uchar pirq;
+
+	/* link should be 0x01..0x08 */
+	pirq = pcicfgr8(router, 0x48 + ((link-1)>>1));
+	return (link & 1)? map[pirq&15]: map[pirq>>4];
+}
+
+static void 
+aliset(Pcidev *router, uchar link, uchar irq)
+{
+	/* Inverse of map in aliget */
+	static const uchar map[] = { 0, 8, 0, 2, 4, 5, 7, 6, 0, 1, 3, 9, 11, 0, 13, 15 };
+	uchar pirq;
+
+	pirq = pcicfgr8(router, 0x48 + ((link-1)>>1));
+	pirq &= (link & 1)? 0x0f: 0xf0;
+	pirq |= (link & 1)? (map[irq] << 4): (map[irq] & 15);
+	pcicfgw8(router, 0x48 + ((link-1)>>1), pirq);
+}
+
+static uchar 
+cyrixget(Pcidev *router, uchar link)
+{
+	uchar pirq;
+
+	/* link should be 1, 2, 3, 4 */
+	pirq = pcicfgr8(router, 0x5c + ((link-1)>>1));
+	return ((link & 1)? pirq >> 4: pirq & 15);
+}
+
+static void 
+cyrixset(Pcidev *router, uchar link, uchar irq)
+{
+	uchar pirq;
+
+	pirq = pcicfgr8(router, 0x5c + (link>>1));
+	pirq &= (link & 1)? 0x0f: 0xf0;
+	pirq |= (link & 1)? (irq << 4): (irq & 15);
+	pcicfgw8(router, 0x5c + (link>>1), pirq);
+}
+
+typedef struct Bridge Bridge;
+struct Bridge
+{
+	ushort	vid;
+	ushort	did;
+	uchar	(*get)(Pcidev *, uchar);
+	void	(*set)(Pcidev *, uchar, uchar);	
+};
+
+static Bridge southbridges[] = {
+	{ 0x8086, 0x122e, pIIxget, pIIxset },	// Intel 82371FB
+	{ 0x8086, 0x1234, pIIxget, pIIxset },	// Intel 82371MX
+	{ 0x8086, 0x7000, pIIxget, pIIxset },	// Intel 82371SB
+	{ 0x8086, 0x7110, pIIxget, pIIxset },	// Intel 82371AB
+	{ 0x8086, 0x7198, pIIxget, pIIxset },	// Intel 82443MX (fn 1)
+	{ 0x8086, 0x2410, pIIxget, pIIxset },	// Intel 82801AA
+	{ 0x8086, 0x2420, pIIxget, pIIxset },	// Intel 82801AB
+	{ 0x8086, 0x2440, pIIxget, pIIxset },	// Intel 82801BA
+	{ 0x8086, 0x244c, pIIxget, pIIxset },	// Intel 82801BAM
+	{ 0x8086, 0x248c, pIIxget, pIIxset },	// Intel 82801CAM
+	{ 0x8086, 0x24cc, pIIxget, pIIxset },	// Intel 82801DBM
+	{ 0x8086, 0x24d0, pIIxget, pIIxset },	// Intel 82801EB
+	{ 0x8086, 0x2640, pIIxget, pIIxset },	// Intel 82801FB
+	{ 0x1106, 0x0586, viaget, viaset },	// Viatech 82C586
+	{ 0x1106, 0x0596, viaget, viaset },	// Viatech 82C596
+	{ 0x1106, 0x0686, viaget, viaset },	// Viatech 82C686
+	{ 0x1106, 0x3227, viaget, viaset },	// Viatech VT8237
+	{ 0x1045, 0xc700, optiget, optiset },	// Opti 82C700
+	{ 0x10b9, 0x1533, aliget, aliset },	// Al M1533
+	{ 0x1039, 0x0008, pIIxget, pIIxset },	// SI 503
+	{ 0x1039, 0x0496, pIIxget, pIIxset },	// SI 496
+	{ 0x1078, 0x0100, cyrixget, cyrixset },	// Cyrix 5530 Legacy
+
+	{ 0x1022, 0x746B, nil, nil },		// AMD 8111
+	{ 0x10DE, 0x00D1, nil, nil },		// NVIDIA nForce 3
+	{ 0x1166, 0x0200, nil, nil },		// ServerWorks ServerSet III LE
+};
+
+typedef struct Slot Slot;
+struct Slot {
+	uchar	bus;			// Pci bus number
+	uchar	dev;			// Pci device number
+	uchar	maps[12];		// Avoid structs!  Link and mask.
+	uchar	slot;			// Add-in/built-in slot
+	uchar	reserved;
+};
+
+typedef struct Router Router;
+struct Router {
+	uchar	signature[4];		// Routing table signature
+	uchar	version[2];		// Version number
+	uchar	size[2];		// Total table size
+	uchar	bus;			// Interrupt router bus number
+	uchar	devfn;			// Router's devfunc
+	uchar	pciirqs[2];		// Exclusive PCI irqs
+	uchar	compat[4];		// Compatible PCI interrupt router
+	uchar	miniport[4];		// Miniport data
+	uchar	reserved[11];
+	uchar	checksum;
+};
+
+static ushort pciirqs;			// Exclusive PCI irqs
+static Bridge *southbridge;		// Which southbridge to use.
+
+static void
+pcirouting(void)
+{
+	Slot *e;
+	Router *r;
+	int size, i, fn, tbdf;
+	Pcidev *sbpci, *pci;
+	uchar *p, pin, irq, link, *map;
+
+	// Search for PCI interrupt routing table in BIOS
+	for(p = (uchar *)KADDR(0xf0000); p < (uchar *)KADDR(0xfffff); p += 16)
+		if(p[0] == '$' && p[1] == 'P' && p[2] == 'I' && p[3] == 'R')
+			break;
+
+	if(p >= (uchar *)KADDR(0xfffff))
+		return;
+
+	r = (Router *)p;
+
+	// print("PCI interrupt routing table version %d.%d at %.6uX\n",
+	// 	r->version[0], r->version[1], (ulong)r & 0xfffff);
+
+	tbdf = (BusPCI << 24)|(r->bus << 16)|(r->devfn << 8);
+	sbpci = pcimatchtbdf(tbdf);
+	if(sbpci == nil) {
+		print("pcirouting: Cannot find south bridge %T\n", tbdf);
+		return;
+	}
+
+	for(i = 0; i != nelem(southbridges); i++)
+		if(sbpci->vid == southbridges[i].vid && sbpci->did == southbridges[i].did)
+			break;
+
+	if(i == nelem(southbridges)) {
+		print("pcirouting: ignoring south bridge %T %.4uX/%.4uX\n", tbdf, sbpci->vid, sbpci->did);
+		return;
+	}
+	southbridge = &southbridges[i];
+	if(southbridge->get == nil || southbridge->set == nil)
+		return;
+
+	pciirqs = (r->pciirqs[1] << 8)|r->pciirqs[0];
+
+	size = (r->size[1] << 8)|r->size[0];
+	for(e = (Slot *)&r[1]; (uchar *)e < p + size; e++) {
+		// print("%.2uX/%.2uX %.2uX: ", e->bus, e->dev, e->slot);
+		// for (i = 0; i != 4; i++) {
+		// 	uchar *m = &e->maps[i * 3];
+		// 	print("[%d] %.2uX %.4uX ",
+		// 		i, m[0], (m[2] << 8)|m[1]);
+		// }
+		// print("\n");
+
+		for(fn = 0; fn != 8; fn++) {
+			tbdf = (BusPCI << 24)|(e->bus << 16)|((e->dev | fn) << 8);
+			pci = pcimatchtbdf(tbdf);
+			if(pci == nil)
+				continue;
+			pin = pcicfgr8(pci, PciINTP);
+			if(pin == 0 || pin == 0xff) 
+				continue;
+
+			map = &e->maps[(pin - 1) * 3];
+			link = map[0];
+			irq = southbridge->get(sbpci, link);
+			if(irq == 0 || irq == pci->intl)
+				continue;
+			if(pci->intl != 0 && pci->intl != 0xFF) {
+				print("pcirouting: BIOS workaround: %T at pin %d link %d irq %d -> %d\n",
+					  tbdf, pin, link, irq, pci->intl);
+				southbridge->set(sbpci, link, pci->intl);
+				continue;
+			}
+			print("pcirouting: %T at pin %d link %d irq %d\n", tbdf, pin, link, irq);
+			pcicfgw8(pci, PciINTL, irq);
+			pci->intl = irq;
+		}
+	}
+}
+
+static void
+pcicfginit(void)
+{
+	char *p;
+	Pcidev **list;
+	ulong mema, ioa;
+	int bno, n, pcibios;
+
+	lock(&pcicfginitlock);
+	if(pcicfgmode != -1)
+		goto out;
+
+	pcibios = 0;
+	if(getconf("*nobios"))
+		nobios = 1;
+	else if(getconf("*pcibios"))
+		pcibios = 1;
+	if(getconf("*nopcirouting"))
+		nopcirouting = 1;
+
+	/*
+	 * Try to determine which PCI configuration mode is implemented.
+	 * Mode2 uses a byte at 0xCF8 and another at 0xCFA; Mode1 uses
+	 * a DWORD at 0xCF8 and another at 0xCFC and will pass through
+	 * any non-DWORD accesses as normal I/O cycles. There shouldn't be
+	 * a device behind these addresses so if Mode1 accesses fail try
+	 * for Mode2 (Mode2 is deprecated).
+	 */
+	if(!pcibios){
+		/*
+		 * Bits [30:24] of PciADDR must be 0,
+		 * according to the spec.
+		 */
+		n = inl(PciADDR);
+		if(!(n & 0x7FF00000)){
+			outl(PciADDR, 0x80000000);
+			outb(PciADDR+3, 0);
+			if(inl(PciADDR) & 0x80000000){
+				pcicfgmode = 1;
+				pcimaxdno = 31;
+			}
+		}
+		outl(PciADDR, n);
+
+		if(pcicfgmode < 0){
+			/*
+			 * The 'key' part of PciCSE should be 0.
+			 */
+			n = inb(PciCSE);
+			if(!(n & 0xF0)){
+				outb(PciCSE, 0x0E);
+				if(inb(PciCSE) == 0x0E){
+					pcicfgmode = 2;
+					pcimaxdno = 15;
+				}
+			}
+			outb(PciCSE, n);
+		}
+	}
+	
+	if(pcicfgmode < 0)
+		goto out;
+
+	fmtinstall('T', tbdffmt);
+
+	if(p = getconf("*pcimaxbno")){
+		n = strtoul(p, 0, 0);
+		if(n < pcimaxbno)
+			pcimaxbno = n;
+	}
+	if(p = getconf("*pcimaxdno")){
+		n = strtoul(p, 0, 0);
+		if(n < pcimaxdno)
+			pcimaxdno = n;
+	}
+
+	list = &pciroot;
+	for(bno = 0; bno <= pcimaxbno; bno++) {
+		int sbno = bno;
+		bno = pcilscan(bno, list);
+
+		while(*list)
+			list = &(*list)->link;
+
+		if (sbno == 0) {
+			Pcidev *pci;
+
+			/*
+			  * If we have found a PCI-to-Cardbus bridge, make sure
+			  * it has no valid mappings anymore.  
+			  */
+			pci = pciroot;
+			while (pci) {
+				if (pci->ccrb == 6 && pci->ccru == 7) {
+					ushort bcr;
+
+					/* reset the cardbus */
+					bcr = pcicfgr16(pci, PciBCR);
+					pcicfgw16(pci, PciBCR, 0x40 | bcr);
+					delay(50);
+				}
+				pci = pci->link;
+			}
+		}
+	}
+
+	if(pciroot == nil)
+		goto out;
+
+	if(nobios) {
+		/*
+		 * Work out how big the top bus is
+		 */
+		mema = 0;
+		ioa = 0;
+		pcibusmap(pciroot, &mema, &ioa, 0);
+
+		DBG("Sizes: mem=%8.8lux size=%8.8lux io=%8.8lux\n",
+			mema, pcimask(mema), ioa);
+	
+		/*
+		 * Align the windows and map it
+		 */
+		ioa = 0x1000;
+		mema = 0x90000000;
+
+		pcilog("Mask sizes: mem=%lux io=%lux\n", mema, ioa);
+
+		pcibusmap(pciroot, &mema, &ioa, 1);
+		DBG("Sizes2: mem=%lux io=%lux\n", mema, ioa);
+	
+		unlock(&pcicfginitlock);
+		return;
+	}
+
+	if (!nopcirouting)
+		pcirouting();
+
+out:
+	unlock(&pcicfginitlock);
+
+	if(getconf("*pcihinv"))
+		pcihinv(nil);
+}
+
+static int
+pcicfgrw8(int tbdf, int rno, int data, int read)
+{
+	int o, type, x;
+
+	if(pcicfgmode == -1)
+		pcicfginit();
+
+	if(BUSBNO(tbdf))
+		type = 0x01;
+	else
+		type = 0x00;
+	x = -1;
+	if(BUSDNO(tbdf) > pcimaxdno)
+		return x;
+
+	lock(&pcicfglock);
+	switch(pcicfgmode){
+
+	case 1:
+		o = rno & 0x03;
+		rno &= ~0x03;
+		outl(PciADDR, 0x80000000|BUSBDF(tbdf)|rno|type);
+		if(read)
+			x = inb(PciDATA+o);
+		else
+			outb(PciDATA+o, data);
+		outl(PciADDR, 0);
+		break;
+
+	case 2:
+		outb(PciCSE, 0x80|(BUSFNO(tbdf)<<1));
+		outb(PciFORWARD, BUSBNO(tbdf));
+		if(read)
+			x = inb((0xC000|(BUSDNO(tbdf)<<8)) + rno);
+		else
+			outb((0xC000|(BUSDNO(tbdf)<<8)) + rno, data);
+		outb(PciCSE, 0);
+		break;
+	}
+	unlock(&pcicfglock);
+
+	return x;
+}
+
+int
+pcicfgr8(Pcidev* pcidev, int rno)
+{
+	return pcicfgrw8(pcidev->tbdf, rno, 0, 1);
+}
+
+void
+pcicfgw8(Pcidev* pcidev, int rno, int data)
+{
+	pcicfgrw8(pcidev->tbdf, rno, data, 0);
+}
+
+static int
+pcicfgrw16(int tbdf, int rno, int data, int read)
+{
+	int o, type, x;
+
+	if(pcicfgmode == -1)
+		pcicfginit();
+
+	if(BUSBNO(tbdf))
+		type = 0x01;
+	else
+		type = 0x00;
+	x = -1;
+	if(BUSDNO(tbdf) > pcimaxdno)
+		return x;
+
+	lock(&pcicfglock);
+	switch(pcicfgmode){
+
+	case 1:
+		o = rno & 0x02;
+		rno &= ~0x03;
+		outl(PciADDR, 0x80000000|BUSBDF(tbdf)|rno|type);
+		if(read)
+			x = ins(PciDATA+o);
+		else
+			outs(PciDATA+o, data);
+		outl(PciADDR, 0);
+		break;
+
+	case 2:
+		outb(PciCSE, 0x80|(BUSFNO(tbdf)<<1));
+		outb(PciFORWARD, BUSBNO(tbdf));
+		if(read)
+			x = ins((0xC000|(BUSDNO(tbdf)<<8)) + rno);
+		else
+			outs((0xC000|(BUSDNO(tbdf)<<8)) + rno, data);
+		outb(PciCSE, 0);
+		break;
+	}
+	unlock(&pcicfglock);
+
+	return x;
+}
+
+int
+pcicfgr16(Pcidev* pcidev, int rno)
+{
+	return pcicfgrw16(pcidev->tbdf, rno, 0, 1);
+}
+
+void
+pcicfgw16(Pcidev* pcidev, int rno, int data)
+{
+	pcicfgrw16(pcidev->tbdf, rno, data, 0);
+}
+
+static int
+pcicfgrw32(int tbdf, int rno, int data, int read)
+{
+	int type, x;
+
+	if(pcicfgmode == -1)
+		pcicfginit();
+
+	if(BUSBNO(tbdf))
+		type = 0x01;
+	else
+		type = 0x00;
+	x = -1;
+	if(BUSDNO(tbdf) > pcimaxdno)
+		return x;
+
+	lock(&pcicfglock);
+	switch(pcicfgmode){
+
+	case 1:
+		rno &= ~0x03;
+		outl(PciADDR, 0x80000000|BUSBDF(tbdf)|rno|type);
+		if(read)
+			x = inl(PciDATA);
+		else
+			outl(PciDATA, data);
+		outl(PciADDR, 0);
+		break;
+
+	case 2:
+		outb(PciCSE, 0x80|(BUSFNO(tbdf)<<1));
+		outb(PciFORWARD, BUSBNO(tbdf));
+		if(read)
+			x = inl((0xC000|(BUSDNO(tbdf)<<8)) + rno);
+		else
+			outl((0xC000|(BUSDNO(tbdf)<<8)) + rno, data);
+		outb(PciCSE, 0);
+		break;
+	}
+	unlock(&pcicfglock);
+
+	return x;
+}
+
+int
+pcicfgr32(Pcidev* pcidev, int rno)
+{
+	return pcicfgrw32(pcidev->tbdf, rno, 0, 1);
+}
+
+void
+pcicfgw32(Pcidev* pcidev, int rno, int data)
+{
+	pcicfgrw32(pcidev->tbdf, rno, data, 0);
+}
+
+Pcidev*
+pcimatch(Pcidev* prev, int vid, int did)
+{
+	if(pcicfgmode == -1)
+		pcicfginit();
+
+	if(prev == nil)
+		prev = pcilist;
+	else
+		prev = prev->list;
+
+	while(prev != nil){
+		if((vid == 0 || prev->vid == vid)
+		&& (did == 0 || prev->did == did))
+			break;
+		prev = prev->list;
+	}
+	return prev;
+}
+
+Pcidev*
+pcimatchtbdf(int tbdf)
+{
+	Pcidev *pcidev;
+
+	if(pcicfgmode == -1)
+		pcicfginit();
+
+	for(pcidev = pcilist; pcidev != nil; pcidev = pcidev->list) {
+		if(pcidev->tbdf == tbdf)
+			break;
+	}
+	return pcidev;
+}
+
+uchar
+pciipin(Pcidev *pci, uchar pin)
+{
+	if (pci == nil)
+		pci = pcilist;
+
+	while (pci) {
+		uchar intl;
+
+		if (pcicfgr8(pci, PciINTP) == pin && pci->intl != 0 && pci->intl != 0xff)
+			return pci->intl;
+
+		if (pci->bridge && (intl = pciipin(pci->bridge, pin)) != 0)
+			return intl;
+
+		pci = pci->list;
+	}
+	return 0;
+}
+
+static void
+pcilhinv(Pcidev* p)
+{
+	int i;
+	Pcidev *t;
+
+	if(p == nil) {
+		putstrn(PCICONS.output, PCICONS.ptr);
+		p = pciroot;
+		print("bus dev type vid  did intl memory\n");
+	}
+	for(t = p; t != nil; t = t->link) {
+		print("%d  %2d/%d %.2ux %.2ux %.2ux %.4ux %.4ux %3d  ",
+			BUSBNO(t->tbdf), BUSDNO(t->tbdf), BUSFNO(t->tbdf),
+			t->ccrb, t->ccru, t->ccrp, t->vid, t->did, t->intl);
+
+		for(i = 0; i < nelem(p->mem); i++) {
+			if(t->mem[i].size == 0)
+				continue;
+			print("%d:%.8lux %d ", i,
+				t->mem[i].bar, t->mem[i].size);
+		}
+		if(t->ioa.bar || t->ioa.size)
+			print("ioa:%.8lux %d ", t->ioa.bar, t->ioa.size);
+		if(t->mema.bar || t->mema.size)
+			print("mema:%.8lux %d ", t->mema.bar, t->mema.size);
+		if(t->bridge)
+			print("->%d", BUSBNO(t->bridge->tbdf));
+		print("\n");
+	}
+	while(p != nil) {
+		if(p->bridge != nil)
+			pcilhinv(p->bridge);
+		p = p->link;
+	}	
+}
+
+void
+pcihinv(Pcidev* p)
+{
+	if(pcicfgmode == -1)
+		pcicfginit();
+	lock(&pcicfginitlock);
+	pcilhinv(p);
+	unlock(&pcicfginitlock);
+}
+
+void
+pcireset(void)
+{
+	Pcidev *p;
+
+	if(pcicfgmode == -1)
+		pcicfginit();
+
+	for(p = pcilist; p != nil; p = p->list) {
+		/* don't mess with the bridges */
+		if(p->ccrb == 0x06)
+			continue;
+		pciclrbme(p);
+	}
+}
+
+void
+pcisetioe(Pcidev* p)
+{
+	p->pcr |= IOen;
+	pcicfgw16(p, PciPCR, p->pcr);
+}
+
+void
+pciclrioe(Pcidev* p)
+{
+	p->pcr &= ~IOen;
+	pcicfgw16(p, PciPCR, p->pcr);
+}
+
+void
+pcisetbme(Pcidev* p)
+{
+	p->pcr |= MASen;
+	pcicfgw16(p, PciPCR, p->pcr);
+}
+
+void
+pciclrbme(Pcidev* p)
+{
+	p->pcr &= ~MASen;
+	pcicfgw16(p, PciPCR, p->pcr);
+}
+
+void
+pcisetmwi(Pcidev* p)
+{
+	p->pcr |= MemWrInv;
+	pcicfgw16(p, PciPCR, p->pcr);
+}
+
+void
+pciclrmwi(Pcidev* p)
+{
+	p->pcr &= ~MemWrInv;
+	pcicfgw16(p, PciPCR, p->pcr);
+}
+
+static int
+pcigetpmrb(Pcidev* p)
+{
+	int ptr;
+
+	if(p->pmrb != 0)
+		return p->pmrb;
+	p->pmrb = -1;
+
+	/*
+	 * If there are no extended capabilities implemented,
+	 * (bit 4 in the status register) assume there's no standard
+	 * power management method.
+	 * Find the capabilities pointer based on PCI header type.
+	 */
+	if(!(p->pcr & 0x0010))
+		return -1;
+	switch(pcicfgr8(p, PciHDT)){
+	default:
+		return -1;
+	case 0:					/* all other */
+	case 1:					/* PCI to PCI bridge */
+		ptr = 0x34;
+		break;
+	case 2:					/* CardBus bridge */
+		ptr = 0x14;
+		break;
+	}
+	ptr = pcicfgr32(p, ptr);
+
+	while(ptr != 0){
+		/*
+		 * Check for validity.
+		 * Can't be in standard header and must be double
+		 * word aligned.
+		 */
+		if(ptr < 0x40 || (ptr & ~0xFC))
+			return -1;
+		if(pcicfgr8(p, ptr) == 0x01){
+			p->pmrb = ptr;
+			return ptr;
+		}
+
+		ptr = pcicfgr8(p, ptr+1);
+	}
+
+	return -1;
+}
+
+int
+pcigetpms(Pcidev* p)
+{
+	int pmcsr, ptr;
+
+	if((ptr = pcigetpmrb(p)) == -1)
+		return -1;
+
+	/*
+	 * Power Management Register Block:
+	 *  offset 0:	Capability ID
+	 *	   1:	next item pointer
+	 *	   2:	capabilities
+	 *	   4:	control/status
+	 *	   6:	bridge support extensions
+	 *	   7:	data
+	 */
+	pmcsr = pcicfgr16(p, ptr+4);
+
+	return pmcsr & 0x0003;
+}
+
+int
+pcisetpms(Pcidev* p, int state)
+{
+	int ostate, pmc, pmcsr, ptr;
+
+	if((ptr = pcigetpmrb(p)) == -1)
+		return -1;
+
+	pmc = pcicfgr16(p, ptr+2);
+	pmcsr = pcicfgr16(p, ptr+4);
+	ostate = pmcsr & 0x0003;
+	pmcsr &= ~0x0003;
+
+	switch(state){
+	default:
+		return -1;
+	case 0:
+		break;
+	case 1:
+		if(!(pmc & 0x0200))
+			return -1;
+		break;
+	case 2:
+		if(!(pmc & 0x0400))
+			return -1;
+		break;
+	case 3:
+		break;
+	}
+	pmcsr |= state;
+	pcicfgw16(p, ptr+4, pmcsr);
+
+	return ostate;
+}
--- /dev/null
+++ b/os/pc/pcidb.acid
@@ -1,0 +1,2848 @@
+pcivendordb = { 
+	{0x0000, "Gammagraphx, Inc.", {
+	}},
+	{0x001a, "Ascend Communications, Inc.", {
+	}},
+	{0x003d, "Lockheed Martin-Marietta Corp", {
+	}},
+	{0x0e11, "Compaq Computer Corporation", {
+		{0x3032,"QVision 1280/p Rev 0"},
+		{0x3033,"QVision 1280/p Rev 1"},
+		{0x3034,"QVision 1280/p Rev 2"},
+		{0x4000,"4000 [Triflex]"},
+		{0xae10,"Smart-2/P RAID Controller"},
+		{0xae29,"MIS-L"},
+		{0xae2a,"MPC"},
+		{0xae2b,"MIS-E"},
+		{0xae32,"Netelligent 10/100"},
+		{0xae34,"Netelligent 10"},
+		{0xae35,"Integrated NetFlex-3/P"},
+		{0xae40,"Netelligent 10/100 Dual"},
+		{0xae43,"ProLiant Integrated Netelligent 10/100"},
+		{0xae69,"CETUS-L"},
+		{0xae6c,"Northstar"},
+		{0xb011,"Integrated Netelligent 10/100"},
+		{0xb012,"Netelligent 10 T/2"},
+		{0xf130,"NetFlex-3/P ThunderLAN 1.0"},
+		{0xf150,"NetFlex-3/P ThunderLAN 2.3"},
+	}},
+	{0x1000, "Symbios Logic Inc. (formerly NCR)", {
+		{0x0001,"53c810"},
+		{0x0002,"53c820"},
+		{0x0003,"53c825"},
+		{0x0004,"53c815"},
+		{0x0005,"53c810AP"},
+		{0x0006,"53c860"},
+		{0x000b,"53c896"},
+		{0x000c,"53c895"},
+		{0x000d,"53c885"},
+		{0x000f,"53c875"},
+		{0x008f,"53c875J"},
+		{0x0901,"61C102"},
+		{0x1000,"63C815"},
+	}},
+	{0x1002, "ATI Technologies Inc", {
+		{0x4158,"68800AX [Mach32]"},
+		{0x4354,"215CT [Mach64 CT]"},
+		{0x4358,"210888CX [Mach64 CX]"},
+		{0x4554,"210888ET [Mach64 ET]"},
+		{0x4742,"215GB [Mach64 GB]"},
+		{0x4744,"215GD [Mach64 GD]"},
+		{0x4749,"215GI [Mach64 GI]"},
+		{0x4750,"215GP [Mach64 GP]"},
+		{0x4751,"215GQ [Mach64 GQ]"},
+		{0x4754,"215GT [Mach64 GT]"},
+		{0x4755,"215GTB [Mach64 GTB]"},
+		{0x4756,"215IIC [Mach64 GT IIC]"},
+		{0x4758,"210888GX [Mach64 GX]"},
+		{0x4c47,"215LG [Mach64 LG]"},
+		{0x4c54,"264LT [Mach64 LT]"},
+		{0x5654,"264VT [Mach64 VT]"},
+		{0x5655,"264VTB [Mach64 VTB]"},
+		{0x5656,"264VT4 [Mach64 VT4]"},
+	}},
+	{0x1003, "ULSI Systems", {
+		{0x0201,"US201"},
+	}},
+	{0x1004, "VLSI Technology Inc", {
+		{0x0005,"82C592-FC1"},
+		{0x0006,"82C593-FC1"},
+		{0x0007,"82C594-AFC2"},
+		{0x0008,"82C596/7 [Wildcat]"},
+		{0x0009,"82C597-AFC2"},
+		{0x000c,"82C541 [Lynx]"},
+		{0x000d,"82C543 [Lynx]"},
+		{0x0101,"82C532"},
+		{0x0102,"82C534"},
+		{0x0103,"82C538"},
+		{0x0104,"82C535"},
+		{0x0105,"82C147"},
+		{0x0200,"82C975"},
+		{0x0280,"82C925"},
+		{0x0702,"VAS96011 [Golden Gate II]"},
+	}},
+	{0x1005, "Avance Logic, Inc.", {
+		{0x2301,"ALG2301"},
+		{0x2302,"ALG2302"},
+		{0x2364,"ALG2364"},
+	}},
+	{0x1006, "Reply Group", {
+	}},
+	{0x1007, "NetFrame Systems Inc", {
+	}},
+	{0x1008, "Epson", {
+	}},
+	{0x100a, "Phoenix Technologies", {
+	}},
+	{0x100b, "National Semiconductor Corporation", {
+		{0x0001,"DP83810"},
+		{0x0002,"87415"},
+		{0xd001,"87410"},
+	}},
+	{0x100c, "Tseng Labs Inc", {
+		{0x3202,"ET4000/W32p rev A"},
+		{0x3205,"ET4000/W32p rev B"},
+		{0x3206,"ET4000/W32p rev C"},
+		{0x3207,"ET4000/W32p rev D"},
+		{0x3208,"ET6000"},
+	}},
+	{0x100d, "AST Research Inc", {
+	}},
+	{0x100e, "Weitek", {
+		{0x9001,"P9000"},
+		{0x9100,"P9100"},
+	}},
+	{0x1010, "Video Logic, Ltd.", {
+	}},
+	{0x1011, "Digital Equipment Corporation", {
+		{0x0001,"DECchip 21050"},
+		{0x0002,"DECchip 21040 [Tulip]"},
+		{0x0004,"DECchip 21030 [TGA]"},
+		{0x0007,"NVRAM [Zephyr NVRAM]"},
+		{0x0008,"KZPSA [KZPSA]"},
+		{0x0009,"DECchip 21140 [FasterNet]"},
+		{0x000d,"PBXGB [TGA2]"},
+		{0x000f,"DEFPA"},
+		{0x0014,"DECchip 21041 [Tulip Pass 3]"},
+		{0x0016,"DGLPB [OPPO]"},
+		{0x0019,"DECchip 21142/43"},
+		{0x0021,"DECchip 21052"},
+		{0x0022,"DECchip 21150"},
+		{0x0024,"DECchip 21152"},
+	}},
+	{0x1012, "Micronics Computers Inc", {
+	}},
+	{0x1013, "Cirrus Logic", {
+		{0x0038,"GD 7548"},
+		{0x00a0,"GD 5430/40 [Alpine]"},
+		{0x00a4,"GD 5434-4 [Alpine]"},
+		{0x00a8,"GD 5434-8 [Alpine]"},
+		{0x00ac,"GD 5436 [Alpine]"},
+		{0x00b8,"GD 5446"},
+		{0x00bc,"GD 5480"},
+		{0x00d0,"GD 5462"},
+		{0x00d4,"GD 5464 [Laguna]"},
+		{0x00d6,"GD 5465 [Laguna]"},
+		{0x1100,"CL 6729"},
+		{0x1110,"PD 6832"},
+		{0x1200,"GD 7542 [Nordic]"},
+		{0x1202,"GD 7543 [Viking]"},
+		{0x1204,"GD 7541 [Nordic Light]"},
+	}},
+	{0x1014, "IBM", {
+		{0x000a,"Fire Coral"},
+		{0x0018,"TR"},
+		{0x001b,"GXT-150P"},
+		{0x001d,"82G2675"},
+		{0x0020,"MCA"},
+		{0x0022,"IBM27-82351"},
+		{0x002e,"ServeRAID controller"},
+		{0x0036,"Miami"},
+		{0x003e,"TR_Wake"},
+		{0x0046,"MPIC interrupt controller"},
+		{0x007d,"3780IDSP [MWave]"},
+		{0xffff,"MPIC-2 interrupt controller"},
+	}},
+	{0x1015, "LSI Logic Corp of Canada", {
+	}},
+	{0x1016, "ICL Personal Systems", {
+	}},
+	{0x1017, "SPEA Software AG", {
+	}},
+	{0x1018, "Unisys Systems", {
+	}},
+	{0x1019, "Elitegroup Computer Systems", {
+	}},
+	{0x101a, "AT&T GIS (NCR)", {
+	}},
+	{0x101b, "Vitesse Semiconductor", {
+	}},
+	{0x101c, "Western Digital", {
+		{0x0193,"33C193A"},
+		{0x0197,"33C197A"},
+		{0x0296,"33C296A"},
+		{0x3193,"7193"},
+		{0x3197,"WD 7197"},
+		{0x3296,"33C296A"},
+		{0x4296,"34C296"},
+		{0xc24a,"90C"},
+	}},
+	{0x101e, "American Megatrends Inc.", {
+		{0x9010,"MegaRAID"},
+	}},
+	{0x101f, "PictureTel", {
+	}},
+	{0x1020, "Hitachi Computer Products", {
+	}},
+	{0x1021, "OKI Electric Industry Co. Ltd.", {
+	}},
+	{0x1022, "Advanced Micro Devices", {
+		{0x2000,"79c970 [PCnet LANCE]"},
+		{0x2020,"53c974 [PCscsi]"},
+		{0x2040,"79c974"},
+	}},
+	{0x1023, "Trident Microsystems", {
+		{0x9320,"TGUI 9320"},
+		{0x9397,"Cyber9397"},
+		{0x9420,"TGUI 9420"},
+		{0x9430,"TGUI 9430"},
+		{0x9440,"TGUI 9440"},
+		{0x9660,"TGUI 9660/9680/9682"},
+		{0x9750,"3DIm`age 975"},
+	}},
+	{0x1024, "Zenith Data Systems", {
+	}},
+	{0x1025, "Acer Incorporated", {
+		{0x1435,"M1435"},
+		{0x1445,"M1445"},
+		{0x1449,"M1449"},
+		{0x1451,"M1451"},
+		{0x1461,"M1461"},
+		{0x3141,"M3141"},
+		{0x3143,"M3143"},
+		{0x3145,"M3145"},
+		{0x3147,"M3147"},
+		{0x3149,"M3149"},
+		{0x3151,"M3151"},
+	}},
+	{0x1028, "Dell Computer Corporation", {
+	}},
+	{0x1029, "Siemens Nixdorf IS", {
+	}},
+	{0x102a, "LSI Logic", {
+		{0x0000,"HYDRA"},
+		{0x0010,"ASPEN"},
+	}},
+	{0x102b, "Matrox Graphics, Inc.", {
+		{0x0518,"2085PX [Atlas MGA-2]"},
+		{0x0519,"MGA 2064W [Millennium]"},
+		{0x051a,"MGA 1064SG [Mystique]"},
+		{0x051b,"MGA 2164W [Millennium II]"},
+		{0x051f,"MGA 2164W AGP [Millennium II AGP]"},
+		{0x0d10,"MGA Ultima/Impression"},
+	}},
+	{0x102c, "Chips and Technologies", {
+		{0x00b8,"64310"},
+		{0x00d8,"65545"},
+		{0x00dc,"65548"},
+		{0x00e0,"65550"},
+		{0x00e4,"65554"},
+		{0x00e5,"65555"},
+	}},
+	{0x102d, "Wyse Technology Inc.", {
+	}},
+	{0x102e, "Olivetti Advanced Technology", {
+	}},
+	{0x102f, "Toshiba America", {
+	}},
+	{0x1030, "TMC Research", {
+	}},
+	{0x1031, "Miro Computer Products AG", {
+		{0x5601,"DC20 ASIC"},
+	}},
+	{0x1033, "NEC Corporation", {
+		{0x0035,"USB"},
+		{0x0046,"PowerVR PCX2 [midas]"},
+	}},
+	{0x1034, "Framatome Connectors USA Inc.", {
+	}},
+	{0x1035, "Comp. & Comm. Research Lab", {
+	}},
+	{0x1036, "Future Domain Corp.", {
+		{0x0000,"TMC-18C30 [36C70]"},
+	}},
+	{0x1037, "Hitachi Micro Systems", {
+	}},
+	{0x1038, "AMP, Inc", {
+	}},
+	{0x1039, "Silicon Integrated Systems", {
+		{0x0001,"5591/5592 AGP"},
+		{0x0002,"SG86C202"},
+		{0x0008,"85C503"},
+		{0x0009,"ACPI"},
+		{0x0200,"5597/5598 VGA"},
+		{0x0204,"82C204"},
+		{0x0205,"SG86C205"},
+		{0x0406,"85C501/2"},
+		{0x0496,"85C496"},
+		{0x0597,"5513C"},
+		{0x0601,"85C601"},
+		{0x5107,"5107"},
+		{0x5511,"5511/5512"},
+		{0x5513,"5513"},
+		{0x5571,"5571"},
+		{0x5591,"5591/5592 Host"},
+		{0x5597,"5597 [SiS5582]"},
+		{0x7001,"7001"},
+	}},
+	{0x103a, "Seiko Epson Corporation", {
+	}},
+	{0x103b, "Tatung Co. of America", {
+	}},
+	{0x103c, "Hewlett-Packard Company", {
+		{0x1030,"J2585A"},
+		{0x1031,"J2585B"},
+		{0x2910,"E2910A"},
+		{0x2925,"E2925A"},
+	}},
+	{0x103e, "Solliday Engineering", {
+	}},
+	{0x103f, "Synopsys/Logic Modeling Group", {
+	}},
+	{0x1040, "Accelgraphics Inc.", {
+	}},
+	{0x1041, "Computrend", {
+	}},
+	{0x1042, "Micron", {
+		{0x1000,"FDC 37C665"},
+		{0x1001,"37C922"},
+		{0x3000,"Samurai_0"},
+		{0x3010,"Samurai_1"},
+		{0x3020,"Samurai_IDE"},
+	}},
+	{0x1043, "Asustek Computer, Inc.", {
+	}},
+	{0x1044, "Distributed Processing Technology", {
+		{0xa400,"SmartCache/Raid III or IV"},
+	}},
+	{0x1045, "OPTi Inc.", {
+		{0xc178,"92C178"},
+		{0xc557,"82C557 [Viper-M]"},
+		{0xc558,"82C558 [Viper-M ISA+IDE]"},
+		{0xc621,"82C621"},
+		{0xc700,"82C700"},
+		{0xc701,"82C701 [FireStar Plus]"},
+		{0xc814,"82C814 [Firebridge 1]"},
+		{0xc822,"82C822"},
+		{0xc824,"82C824"},
+		{0xd568,"82C825 [Firebridge 2]"},
+	}},
+	{0x1046, "IPC Corporation, Ltd.", {
+	}},
+	{0x1047, "Genoa Systems Corp", {
+	}},
+	{0x1048, "Elsa AG", {
+	}},
+	{0x1049, "Fountain Technologies, Inc.", {
+	}},
+	{0x104a, "SGS Thomson Microelectronics", {
+		{0x0008,"STG 2000X"},
+		{0x0009,"STG 1764X"},
+	}},
+	{0x104b, "BusLogic", {
+		{0x0140,"BT-946C (old) [multimaster  01]"},
+		{0x1040,"BT-946C (BA80C30) [MultiMaster 10]"},
+		{0x8130,"Flashpoint LT"},
+	}},
+	{0x104c, "Texas Instruments", {
+		{0x3d04,"TVP4010 [Permedia]"},
+		{0x3d07,"TVP4020 [Permedia 2]"},
+		{0xa001,"TDC1570"},
+		{0xa100,"TDC1561"},
+		{0xac10,"PCI1050"},
+		{0xac11,"PCI1053"},
+		{0xac12,"PCI1130"},
+		{0xac13,"PCI1031"},
+		{0xac15,"PCI1131"},
+		{0xac16,"PCI-1250"},
+		{0xac17,"PCI-1220"},
+	}},
+	{0x104d, "Sony Corporation", {
+	}},
+	{0x104e, "Oak Technology, Inc", {
+		{0x0107,"OTI107"},
+	}},
+	{0x104f, "Co-time Computer Ltd", {
+	}},
+	{0x1050, "Winbond Electronics Corp", {
+		{0x0940,"89C940"},
+	}},
+	{0x1051, "Anigma, Inc.", {
+	}},
+	{0x1053, "Young Micro Systems", {
+	}},
+	{0x1054, "Hitachi, Ltd", {
+	}},
+	{0x1055, "EFAR Microsystems", {
+	}},
+	{0x1056, "ICL", {
+	}},
+	{0x1057, "Motorola Computer Group", {
+		{0x0001,"MPC105 [Eagle]"},
+		{0x0002,"MPC106 [Grackle]"},
+		{0x4801,"Raven"},
+	}},
+	{0x1058, "Electronics & Telecommunications RSH", {
+	}},
+	{0x1059, "Teknor Industrial Computers Inc", {
+	}},
+	{0x105a, "Promise Technology, Inc.", {
+		{0x4d33,"20246"},
+		{0x5300,"DC5300"},
+	}},
+	{0x105b, "Foxconn International, Inc.", {
+	}},
+	{0x105c, "Wipro Infotech Limited", {
+	}},
+	{0x105d, "Number 9 Computer Company", {
+		{0x2309,"Imagine 128"},
+		{0x2339,"Imagine 128-II"},
+		{0x493d,"Imagine 128 T2R [Ticket to Ride]"},
+	}},
+	{0x105e, "Vtech Computers Ltd", {
+	}},
+	{0x105f, "Infotronic America Inc", {
+	}},
+	{0x1060, "United Microelectronics", {
+		{0x0001,"UM82C881"},
+		{0x0002,"UM82C886"},
+		{0x0101,"UM8673F"},
+		{0x0881,"UM8881"},
+		{0x0886,"UM8886F"},
+		{0x0891,"UM8891A"},
+		{0x1001,"UM886A"},
+		{0x673a,"UM8886BF"},
+		{0x8710,"UM8710"},
+		{0x886a,"UM8886A"},
+		{0x8881,"UM8881F"},
+		{0x8886,"UM8886F"},
+		{0x888a,"UM8886A"},
+		{0x8891,"UM8891A"},
+		{0x9017,"UM9017F"},
+		{0xe881,"UM8881N"},
+		{0xe886,"UM8886N"},
+		{0xe891,"UM8891N"},
+	}},
+	{0x1061, "I.I.T.", {
+		{0x0001,"AGX016"},
+		{0x0002,"IIT3204/3501"},
+	}},
+	{0x1062, "Maspar Computer Corp", {
+	}},
+	{0x1063, "Ocean Office Automation", {
+	}},
+	{0x1064, "Alcatel CIT", {
+	}},
+	{0x1065, "Texas Microsystems", {
+	}},
+	{0x1066, "PicoPower Technology", {
+		{0x0000,"PT80C826"},
+		{0x0001,"PT86C52x [Vesuvius]"},
+		{0x0002,"PT80C524 [Nile]"},
+	}},
+	{0x1067, "Mitsubishi Electronics", {
+	}},
+	{0x1068, "Diversified Technology", {
+	}},
+	{0x1069, "Mylex Corporation", {
+		{0x0001,"DAC960P"},
+	}},
+	{0x106a, "Aten Research Inc", {
+	}},
+	{0x106b, "Apple Computer Inc.", {
+		{0x0001,"Bandit PowerPC host bridge"},
+		{0x0002,"Grand Central I/O"},
+		{0x000e,"Hydra Mac I/O"},
+	}},
+	{0x106c, "Hyundai Electronics America", {
+	}},
+	{0x106d, "Sequent Computer Systems", {
+	}},
+	{0x106e, "DFI, Inc", {
+	}},
+	{0x106f, "City Gate Development Ltd", {
+	}},
+	{0x1070, "Daewoo Telecom Ltd", {
+	}},
+	{0x1071, "Mitac", {
+	}},
+	{0x1072, "GIT Co Ltd", {
+	}},
+	{0x1073, "Yamaha Corporation", {
+		{0x0002,"YGV615 [RPA3 3D-Graphics Controller]"},
+	}},
+	{0x1074, "NexGen Microsystems", {
+		{0x4e78,"82c501"},
+	}},
+	{0x1075, "Advanced Integrations Research", {
+	}},
+	{0x1076, "Chaintech Computer Co. Ltd", {
+	}},
+	{0x1077, "Q Logic", {
+		{0x1020,"ISP1020"},
+		{0x1022,"ISP1022"},
+	}},
+	{0x1078, "Cyrix Corporation", {
+		{0x0000,"5510 [Grappa]"},
+		{0x0001,"PCI_Master"},
+		{0x0002,"5520 [Cognac]"},
+		{0x0100,"5530_Legacy [Kahlua]"},
+		{0x0101,"5530_SMI [Kahlua]"},
+		{0x0102,"5530_IDE [Kahlua]"},
+		{0x0103,"5530_Audio [Kahlua]"},
+		{0x0104,"5530_Video [Kahlua]"},
+	}},
+	{0x1079, "I-Bus", {
+	}},
+	{0x107a, "NetWorth", {
+	}},
+	{0x107b, "Gateway 2000", {
+	}},
+	{0x107c, "Goldstar", {
+	}},
+	{0x107d, "LeadTek Research Inc.", {
+		{0x0000,"P86C850"},
+	}},
+	{0x107e, "Interphase Corporation", {
+	}},
+	{0x107f, "Data Technology Corporation", {
+		{0x0802,"SL82C105"},
+	}},
+	{0x1080, "Contaq Microsystems", {
+		{0x0600,"82C599"},
+		{0xc693,"82c693"},
+	}},
+	{0x1081, "Supermac Technology", {
+	}},
+	{0x1082, "EFA Corporation of America", {
+	}},
+	{0x1083, "Forex Computer Corporation", {
+		{0x0001,"FR710"},
+	}},
+	{0x1084, "Parador", {
+	}},
+	{0x1085, "Tulip Computers Int.B.V.", {
+	}},
+	{0x1086, "J. Bond Computer Systems", {
+	}},
+	{0x1087, "Cache Computer", {
+	}},
+	{0x1088, "Microcomputer Systems (M) Son", {
+	}},
+	{0x1089, "Data General Corporation", {
+	}},
+	{0x108a, "Bit3 Computer Corp.", {
+	}},
+	{0x108c, "Oakleigh Systems Inc.", {
+	}},
+	{0x108d, "Olicom", {
+		{0x0001,"OC-3136/3137"},
+		{0x0011,"OC-2315"},
+		{0x0012,"OC-2325"},
+		{0x0013,"OC-2183/2185"},
+		{0x0014,"OC-2326"},
+		{0x0021,"OC-6151/6152 [RapidFire ATM PCI 155]"},
+	}},
+	{0x108e, "Sun Microsystems Computer Corp.", {
+		{0x1000,"EBUS"},
+		{0x1001,"Happy Meal"},
+		{0x5000,"Advanced PCI Bridge"},
+		{0x8000,"PCI Bus Module"},
+		{0xa000,"Ultra IIi PCI"},
+	}},
+	{0x108f, "Systemsoft", {
+	}},
+	{0x1090, "Encore Computer Corporation", {
+	}},
+	{0x1091, "Intergraph Corporation", {
+	}},
+	{0x1092, "Diamond Multimedia Systems", {
+	}},
+	{0x1093, "National Instruments", {
+		{0xc801,"PCI_GPIB"},
+	}},
+	{0x1094, "First International Computers", {
+	}},
+	{0x1095, "CMD Technology Inc", {
+		{0x0640,"PCI0640"},
+		{0x0643,"PCI0643"},
+		{0x0646,"PCI0646"},
+		{0x0650,"PBC0650A"},
+		{0x0670,"670"},
+	}},
+	{0x1096, "Alacron", {
+	}},
+	{0x1097, "Appian Technology", {
+	}},
+	{0x1098, "Quantum Designs (H.K.) Ltd", {
+		{0x0001,"QD-8500"},
+		{0x0002,"QD-8580"},
+	}},
+	{0x1099, "Samsung Electronics Co., Ltd", {
+	}},
+	{0x109a, "Packard Bell", {
+	}},
+	{0x109b, "Gemlight Computer Ltd.", {
+	}},
+	{0x109c, "Megachips Corporation", {
+	}},
+	{0x109d, "Zida Technologies Ltd.", {
+	}},
+	{0x109e, "Brooktree Corporation", {
+		{0x0350,"Bt848"},
+		{0x0351,"Bt849A"},
+		{0x8472,"Bt8472"},
+		{0x8474,"Bt8474"},
+	}},
+	{0x109f, "Trigem Computer Inc.", {
+	}},
+	{0x10a0, "Meidensha Corporation", {
+	}},
+	{0x10a1, "Juko Electronics Ind. Co. Ltd", {
+	}},
+	{0x10a2, "Quantum Corporation", {
+	}},
+	{0x10a3, "Everex Systems Inc", {
+	}},
+	{0x10a4, "Globe Manufacturing Sales", {
+	}},
+	{0x10a5, "Racal Interlan", {
+	}},
+	{0x10a6, "Informtech Industrial Ltd.", {
+	}},
+	{0x10a7, "Benchmarq Microelectronics", {
+	}},
+	{0x10a8, "Sierra Semiconductor", {
+		{0x0000,"STB Horizon 64"},
+	}},
+	{0x10a9, "Silicon Graphics", {
+	}},
+	{0x10aa, "ACC Microelectronics", {
+		{0x0000,"ACCM 2188"},
+	}},
+	{0x10ab, "Digicom", {
+	}},
+	{0x10ac, "Honeywell IAC", {
+	}},
+	{0x10ad, "Symphony Labs", {
+		{0x0001,"W83769F"},
+		{0x0103,"SL82c103"},
+		{0x0105,"SL82c105"},
+		{0x0565,"W83C553"},
+	}},
+	{0x10ae, "Cornerstone Technology", {
+	}},
+	{0x10af, "Micro Computer Systems Inc", {
+	}},
+	{0x10b0, "CardExpert Technology", {
+	}},
+	{0x10b1, "Cabletron Systems Inc", {
+	}},
+	{0x10b2, "Raytheon Company", {
+	}},
+	{0x10b3, "Databook Inc", {
+		{0x3106,"DB87144"},
+		{0xb106,"DB87144"},
+	}},
+	{0x10b4, "STB Systems Inc", {
+	}},
+	{0x10b5, "PLX Technology, Inc.", {
+		{0x9036,"9036"},
+		{0x9060,"9060"},
+		{0x906e,"9060ES"},
+		{0x9080,"9080"},
+	}},
+	{0x10b6, "Madge Networks", {
+		{0x0001,"Smart"},
+		{0x0002,"Smart 16/4 BM Mk2 PCI Ringnode"},
+		{0x1001,"Collage 155 Server"},
+	}},
+	{0x10b7, "3Com Corporation", {
+		{0x0001,"3c985 1000BaseSX"},
+		{0x3390,"Token Link Velocity"},
+		{0x5900,"3c590 10BaseT [Vortex]"},
+		{0x5950,"3c595 100BaseTX [Vortex]"},
+		{0x5951,"3c595 100BaseT4 [Vortex]"},
+		{0x5952,"3c595 100Base-MII [Vortex]"},
+		{0x9000,"3c900 10BaseT [Boomerang]"},
+		{0x9001,"3c900 Combo [Boomerang]"},
+		{0x9050,"3c905 100BaseTX [Boomerang]"},
+		{0x9051,"3c905 100BaseT4"},
+		{0x9055,"3c905B 100BaseTX [Cyclone]"},
+	}},
+	{0x10b8, "Standard Microsystems", {
+		{0x0005,"9432 TX"},
+		{0x1000,"37c665"},
+		{0x1001,"37C922"},
+	}},
+	{0x10b9, "Acer Laboratories Inc.", {
+		{0x1435,"M1435"},
+		{0x1445,"M1445"},
+		{0x1449,"M1449"},
+		{0x1451,"M1451"},
+		{0x1461,"M1461"},
+		{0x1489,"M1489"},
+		{0x1511,"M1511"},
+		{0x1513,"M1513"},
+		{0x1521,"M1521"},
+		{0x1523,"M1523"},
+		{0x1531,"M1531"},
+		{0x1533,"M1533"},
+		{0x1541,"M1541"},
+		{0x1543,"M1543"},
+		{0x3141,"M3141"},
+		{0x3143,"M3143"},
+		{0x3145,"M3145"},
+		{0x3147,"M3147"},
+		{0x3149,"M3149"},
+		{0x3151,"M3151"},
+		{0x3307,"M3307"},
+		{0x5215,"M4803"},
+		{0x5217,"m5217h"},
+		{0x5219,"M5219"},
+		{0x5229,"M5229"},
+		{0x5235,"m5225"},
+		{0x5237,"M5237"},
+		{0x7101,"M7101"},
+	}},
+	{0x10ba, "Mitsubishi Electric Corp.", {
+	}},
+	{0x10bb, "Dapha Electronics Corporation", {
+	}},
+	{0x10bc, "Advanced Logic Research", {
+	}},
+	{0x10bd, "Surecom Technology", {
+		{0x0e34,"NE-34PCI LAN"},
+	}},
+	{0x10be, "Tseng Labs International Co.", {
+	}},
+	{0x10bf, "Most Inc", {
+	}},
+	{0x10c0, "Boca Research Inc.", {
+	}},
+	{0x10c1, "ICM Co., Ltd.", {
+	}},
+	{0x10c2, "Auspex Systems Inc.", {
+	}},
+	{0x10c3, "Samsung Semiconductors, Inc.", {
+	}},
+	{0x10c4, "Award Software International Inc.", {
+	}},
+	{0x10c5, "Xerox Corporation", {
+	}},
+	{0x10c6, "Rambus Inc.", {
+	}},
+	{0x10c7, "Media Vision", {
+	}},
+	{0x10c8, "Neomagic Corporation", {
+		{0x0001,"NM2070 [MagicGraph NM2070]"},
+		{0x0002,"NM2090 [MagicGraph 128V]"},
+		{0x0003,"NM2093 [MagicGraph 128ZV]"},
+		{0x0004,"NM2160 [MagicGraph 128XD]"},
+	}},
+	{0x10c9, "Dataexpert Corporation", {
+	}},
+	{0x10ca, "Fujitsu Microelectr., Inc.", {
+	}},
+	{0x10cb, "Omron Corporation", {
+	}},
+	{0x10cc, "Mentor ARC Inc", {
+	}},
+	{0x10cd, "Advanced System Products, Inc", {
+		{0x1200,"ASC1200 [(abp940) Fast SCSI-II]"},
+		{0x1300,"ABP940-U"},
+		{0x2300,"ABP940-UW"},
+	}},
+	{0x10ce, "Radius", {
+	}},
+	{0x10cf, "Citicorp TTI", {
+		{0x2001,"mb86605"},
+	}},
+	{0x10d0, "Fujitsu Limited", {
+	}},
+	{0x10d1, "FuturePlus Systems Corp.", {
+	}},
+	{0x10d2, "Molex Incorporated", {
+	}},
+	{0x10d3, "Jabil Circuit Inc", {
+	}},
+	{0x10d4, "Hualon Microelectronics", {
+	}},
+	{0x10d5, "Autologic Inc.", {
+	}},
+	{0x10d6, "Cetia", {
+	}},
+	{0x10d7, "BCM Advanced Research", {
+	}},
+	{0x10d8, "Advanced Peripherals Labs", {
+	}},
+	{0x10d9, "Macronix, Inc.", {
+		{0x0512,"MX98713"},
+		{0x0531,"MX987x5"},
+	}},
+	{0x10da, "Compaq IPG-Austin", {
+	}},
+	{0x10db, "Rohm LSI Systems, Inc.", {
+	}},
+	{0x10dc, "CERN/ECP/EDU", {
+		{0x0001,"STAR/RD24 SCI-PCI (PMC)"},
+		{0x0002,"TAR/RD24 SCI-PCI (PMC) [ATT 2C15-3 (FPGA)  SCI bridge  on PCI 5 Volt card]"},
+		{0x0021,"HIPPI destination"},
+		{0x0022,"HIPPI source"},
+	}},
+	{0x10dd, "Evans & Sutherland", {
+	}},
+	{0x10de, "Nvidia Corporation", {
+		{0x0008,"NV1"},
+		{0x0009,"DAC64"},
+	}},
+	{0x10df, "Emulex Corporation", {
+	}},
+	{0x10e0, "Integrated Micro Solutions Inc.", {
+		{0x5026,"IMS5026/27/28"},
+		{0x8849,"8849"},
+		{0x9128,"IMS9129"},
+	}},
+	{0x10e1, "Tekram Technology Co.,Ltd.", {
+		{0x690c,"690c"},
+		{0xdc29,"DC290"},
+	}},
+	{0x10e2, "Aptix Corporation", {
+	}},
+	{0x10e3, "Tundra Semiconductor Corp.", {
+		{0x0000,"CA91C042 [Universe]"},
+		{0x0860,"CA91C860 [QSpan]"},
+	}},
+	{0x10e4, "Tandem Computers", {
+	}},
+	{0x10e5, "Micro Industries Corporation", {
+	}},
+	{0x10e6, "Gainbery Computer Products Inc.", {
+	}},
+	{0x10e7, "Vadem", {
+	}},
+	{0x10e8, "Applied Micro Circuits Corporation", {
+		{0x5920,"S5920"},
+		{0x8043,"LANai4.x [Myrinet LANai interface chip]"},
+		{0x8062,"S5933_PARASTATION"},
+		{0x807d,"S5933 [Matchmaker]"},
+		{0x809c,"S5933_HEPC3"},
+	}},
+	{0x10e9, "Alps Electric Co., Ltd.", {
+	}},
+	{0x10ea, "Intergraphics Systems", {
+		{0x1680,"IGA-1680"},
+		{0x1682,"IGA-1682"},
+	}},
+	{0x10eb, "Artists Graphics", {
+		{0x0101,"3GA"},
+	}},
+	{0x10ec, "Realtek Semiconductor Co., Ltd.", {
+		{0x8029,"8029"},
+		{0x8129,"8129"},
+		{0x8139,"8139"},
+	}},
+	{0x10ed, "Ascii Corporation", {
+		{0x7310,"V7310"},
+	}},
+	{0x10ee, "Xilinx, Inc.", {
+	}},
+	{0x10ef, "Racore Computer Products, Inc.", {
+	}},
+	{0x10f0, "Peritek Corporation", {
+	}},
+	{0x10f1, "Tyan Computer", {
+	}},
+	{0x10f2, "Achme Computer, Inc.", {
+	}},
+	{0x10f3, "Alaris, Inc.", {
+	}},
+	{0x10f4, "S-MOS Systems, Inc.", {
+	}},
+	{0x10f5, "NKK Corporation", {
+		{0xa001,"NDR4000 [NR4600 Bridge]"},
+	}},
+	{0x10f6, "Creative Electronic Systems SA", {
+	}},
+	{0x10f7, "Matsushita Electric Industrial Co., Ltd.", {
+	}},
+	{0x10f8, "Altos India Ltd", {
+	}},
+	{0x10f9, "PC Direct", {
+	}},
+	{0x10fa, "Truevision", {
+		{0x000c,"TARGA 1000"},
+	}},
+	{0x10fb, "Thesys Gesellschaft f€r Mikroelektronik mbH", {
+	}},
+	{0x10fc, "I-O Data Device, Inc.", {
+	}},
+	{0x10fd, "Soyo Computer, Inc", {
+	}},
+	{0x10fe, "Fast Multimedia AG", {
+	}},
+	{0x10ff, "NCube", {
+	}},
+	{0x1100, "Jazz Multimedia", {
+	}},
+	{0x1101, "Initio Corporation", {
+		{0x9100,"320 P"},
+		{0x9500,"360P"},
+	}},
+	{0x1102, "Creative Labs", {
+	}},
+	{0x1103, "Triones Technologies, Inc.", {
+	}},
+	{0x1104, "RasterOps Corp.", {
+	}},
+	{0x1105, "Sigma Designs, Inc.", {
+	}},
+	{0x1106, "VIA Technologies, Inc.", {
+		{0x0505,"VT 82C505"},
+		{0x0561,"VT 82C561"},
+		{0x0571,"VT82C586 IDE [Apollo]"},
+		{0x0576,"VT 82C576 3V [Apollo Master]"},
+		{0x0585,"VT82C585VP [Apollo VP1/VPX]"},
+		{0x0586,"VT82C586 ISA [Apollo VP]"},
+		{0x0595,"VT82C595 [Apollo VP2]"},
+		{0x0597,"VT82C597 [Apollo VP3]"},
+		{0x0926,"VT82C926 [Amazon]"},
+		{0x1000,"82C570MV"},
+		{0x1106,"82C570MV"},
+		{0x1571,"VT 82C416MV"},
+		{0x1595,"VT82C595/97 [Apollo VP2/97]"},
+		{0x3038,"VT82C586B USB"},
+		{0x3040,"VT82C586B ACPI"},
+		{0x6100,"VT85C100A [Rhine II]"},
+		{0x8597,"VT82C597 [Apollo VP3 AGP]"},
+	}},
+	{0x1107, "Stratus Computers", {
+	}},
+	{0x1108, "Proteon, Inc.", {
+		{0x0100,"p1690plus_AA"},
+		{0x0101,"p1690plus_AB"},
+	}},
+	{0x1109, "Cogent Data Technologies, Inc.", {
+		{0x1400,"EM110TX [EX110TX PCI Fast Ethernet Adapter]"},
+	}},
+	{0x110a, "Siemens Nixdorf AG", {
+		{0x6120,"SZB6120"},
+	}},
+	{0x110b, "Chromatic Research Inc.", {
+	}},
+	{0x110c, "Mini-Max Technology, Inc.", {
+	}},
+	{0x110d, "Znyx Advanced Systems", {
+	}},
+	{0x110e, "CPU Technology", {
+	}},
+	{0x110f, "Ross Technology", {
+	}},
+	{0x1110, "Powerhouse Systems", {
+	}},
+	{0x1111, "Santa Cruz Operation", {
+	}},
+	{0x1112, "RNS - Div. of Meret Communications Inc", {
+	}},
+	{0x1113, "Accton Technology Corporation", {
+	}},
+	{0x1114, "Atmel Corporation", {
+	}},
+	{0x1115, "3D Labs", {
+	}},
+	{0x1116, "Data Translation", {
+	}},
+	{0x1117, "Datacube, Inc", {
+	}},
+	{0x1118, "Berg Electronics", {
+	}},
+	{0x1119, "ICP Vortex Computersysteme GmbH", {
+		{0x0000,"GDT6000/6020/6050"},
+		{0x0001,"GDT6000b/6010"},
+		{0x0002,"GDT6110/6510"},
+		{0x0003,"GDT6120/6520"},
+		{0x0004,"GDT6530"},
+		{0x0005,"GDT6550"},
+		{0x0006,"GDT6x17"},
+		{0x0007,"GDT6x27"},
+		{0x0008,"GDT6537"},
+		{0x0009,"GDT5557"},
+		{0x000a,"GDT6x15"},
+		{0x000b,"GDT6x25"},
+		{0x000c,"GDT6535"},
+		{0x000d,"GDT6555"},
+		{0x0100,"GDT 6117RP/6517RP"},
+		{0x0101,"GDT 6127RP/6527RP"},
+		{0x0102,"GDT 6537RP"},
+		{0x0103,"GDT 6557RP"},
+		{0x0104,"GDT 6111RP/6511RP"},
+		{0x0105,"GDT 6121RP/6521RP"},
+		{0x0110,"GDT 6117RP1/6517RP1"},
+		{0x0111,"GDT 6127RP1/6527RP1"},
+		{0x0112,"GDT 6537RP1"},
+		{0x0113,"GDT 6557RP1"},
+		{0x0114,"GDT 6111RP1/6511RP1"},
+		{0x0115,"GDT 6121RP1/6521RP1"},
+		{0x0120,"GDT 6117RP2/6517RP2"},
+		{0x0121,"GDT 6127RP2/6527RP2"},
+		{0x0122,"GDT 6537RP2"},
+		{0x0123,"GDT 6557RP2"},
+		{0x0124,"GDT 6111RP2/6511RP2"},
+		{0x0125,"GDT 6121RP2/6521RP2"},
+	}},
+	{0x111a, "Efficient Networks, Inc", {
+		{0x0000,"155P-MF1 (FPGA)"},
+		{0x0002,"155P-MF1 (ASIC)"},
+	}},
+	{0x111b, "Teledyne Electronic Systems", {
+	}},
+	{0x111c, "Tricord Systems Inc.", {
+	}},
+	{0x111d, "Integrated Device Tech", {
+	}},
+	{0x111e, "Eldec", {
+	}},
+	{0x111f, "Precision Digital Images", {
+	}},
+	{0x1120, "EMC Corporation", {
+	}},
+	{0x1121, "Zilog", {
+	}},
+	{0x1122, "Multi-tech Systems, Inc.", {
+	}},
+	{0x1123, "Excellent Design, Inc.", {
+	}},
+	{0x1124, "Leutron Vision AG", {
+	}},
+	{0x1125, "Eurocore", {
+	}},
+	{0x1127, "FORE Systems Inc", {
+		{0x0210,"PCA-200PC"},
+		{0x0300,"PCA-200E"},
+	}},
+	{0x1129, "Firmworks", {
+	}},
+	{0x112a, "Hermes Electronics Company, Ltd.", {
+	}},
+	{0x112b, "Linotype - Hell AG", {
+	}},
+	{0x112c, "Zenith Data Systems", {
+	}},
+	{0x112d, "Ravicad", {
+	}},
+	{0x112e, "Infomedia Microelectronics Inc.", {
+	}},
+	{0x112f, "Imaging Technology Inc", {
+		{0x0000,"MVC IC-PCI"},
+	}},
+	{0x1130, "Computervision", {
+	}},
+	{0x1131, "Philips Semiconductors", {
+		{0x7145,"SAA7145"},
+		{0x7146,"SAA7146"},
+	}},
+	{0x1132, "Mitel Corp.", {
+	}},
+	{0x1133, "Eicon Technology Corporation", {
+		{0xe001,"DIVA20PRO"},
+		{0xe002,"DIVA20"},
+		{0xe003,"DIVA20PRO_U"},
+		{0xe004,"DIVA20_U"},
+	}},
+	{0x1134, "Mercury Computer Systems", {
+	}},
+	{0x1135, "Fuji Xerox Co Ltd", {
+	}},
+	{0x1136, "Momentum Data Systems", {
+	}},
+	{0x1137, "Cisco Systems Inc", {
+	}},
+	{0x1138, "Ziatech Corporation", {
+		{0x8905,"8905 [STD 32 Bridge]"},
+	}},
+	{0x1139, "Dynamic Pictures, Inc", {
+	}},
+	{0x113a, "FWB Inc", {
+	}},
+	{0x113b, "Network Computing Devices", {
+	}},
+	{0x113c, "Cyclone Microsystems, Inc.", {
+		{0x0001,"PCI-SDK [PCI i960 Evaluation Platform]"},
+		{0x0911,"PCI-911 [PCI-based i960Jx Intelligent I/O Controller]"},
+		{0x0912,"PCI-912 [i960CF-based Intelligent I/O Controller]"},
+		{0x0913,"PCI-913"},
+	}},
+	{0x113d, "Leading Edge Products Inc", {
+	}},
+	{0x113e, "Sanyo Electric Co - Computer Engineering Dept", {
+	}},
+	{0x113f, "Equinox Systems, Inc.", {
+	}},
+	{0x1140, "Intervoice Inc", {
+	}},
+	{0x1141, "Crest Microsystem Inc", {
+	}},
+	{0x1142, "Alliance Semiconductor Corporation", {
+		{0x3210,"AP6410"},
+		{0x6422,"AP6422"},
+		{0x6424,"AT24"},
+		{0x643d,"AT3D"},
+	}},
+	{0x1143, "NetPower, Inc", {
+	}},
+	{0x1144, "Cincinnati Milacron", {
+	}},
+	{0x1145, "Workbit Corporation", {
+	}},
+	{0x1146, "Force Computers", {
+	}},
+	{0x1147, "Interface Corp", {
+	}},
+	{0x1148, "Schneider & Koch", {
+	}},
+	{0x1149, "Win System Corporation", {
+	}},
+	{0x114a, "VMIC", {
+		{0x7587,"VMIVME-7587"},
+	}},
+	{0x114b, "Canopus Co., Ltd", {
+	}},
+	{0x114c, "Annabooks", {
+	}},
+	{0x114d, "IC Corporation", {
+	}},
+	{0x114e, "Nikon Systems Inc", {
+	}},
+	{0x114f, "Digi International", {
+		{0x0002,"AccelePort EPC"},
+		{0x0003,"RightSwitch SE-6"},
+		{0x0004,"AccelePort Xem"},
+		{0x0006,"AccelePort Xr,C/X"},
+		{0x0009,"AccelePort Xr/J"},
+		{0x000a,"AccelePort EPC/J"},
+		{0x0027,"AccelePort Xr 920"},
+	}},
+	{0x1150, "Thinking Machines Corp", {
+	}},
+	{0x1151, "JAE Electronics Inc.", {
+	}},
+	{0x1152, "Megatek", {
+	}},
+	{0x1153, "Land Win Electronic Corp", {
+	}},
+	{0x1154, "Melco Inc", {
+	}},
+	{0x1155, "Pine Technology Ltd", {
+	}},
+	{0x1156, "Periscope Engineering", {
+	}},
+	{0x1157, "Avsys Corporation", {
+	}},
+	{0x1158, "Voarx R & D Inc", {
+	}},
+	{0x1159, "Mutech Corp", {
+		{0x0001,"MV-1000"},
+	}},
+	{0x115a, "Harlequin Ltd", {
+	}},
+	{0x115b, "Parallax Graphics", {
+	}},
+	{0x115c, "Photron Ltd.", {
+	}},
+	{0x115d, "Xircom", {
+	}},
+	{0x115e, "Peer Protocols Inc", {
+	}},
+	{0x115f, "Maxtor Corporation", {
+	}},
+	{0x1160, "Megasoft Inc", {
+	}},
+	{0x1161, "PFU Limited", {
+	}},
+	{0x1162, "OA Laboratory Co Ltd", {
+	}},
+	{0x1163, "Rendition", {
+		{0x0001,"Verite 1000 PCI"},
+		{0x2000,"Verite V2100"},
+	}},
+	{0x1164, "Advanced Peripherals Technologies", {
+	}},
+	{0x1165, "Imagraph Corporation", {
+	}},
+	{0x1166, "Pequr Technology", {
+	}},
+	{0x1167, "Mutoh Industries Inc", {
+	}},
+	{0x1168, "Thine Electronics Inc", {
+	}},
+	{0x1169, "Centre for Development of Advanced Computing", {
+	}},
+	{0x116a, "Polaris Communications", {
+	}},
+	{0x116b, "Connectware Inc", {
+	}},
+	{0x116c, "Intelligent Resources Integrated Systems", {
+	}},
+	{0x116d, "Martin-Marietta", {
+	}},
+	{0x116e, "Electronics for Imaging", {
+	}},
+	{0x116f, "Workstation Technology", {
+	}},
+	{0x1170, "Inventec Corporation", {
+	}},
+	{0x1171, "Loughborough Sound Images Plc", {
+	}},
+	{0x1172, "Altera Corporation", {
+	}},
+	{0x1173, "Adobe Systems, Inc", {
+	}},
+	{0x1174, "Bridgeport Machines", {
+	}},
+	{0x1175, "Mitron Computer Inc.", {
+	}},
+	{0x1176, "SBE Incorporated", {
+	}},
+	{0x1177, "Silicon Engineering", {
+	}},
+	{0x1178, "Alfa, Inc.", {
+	}},
+	{0x1179, "Toshiba America Info Systems", {
+		{0x0601,"601"},
+		{0x060a,"ToPIC95"},
+		{0x060f,"ToPIC97"},
+		{0x0701,"Lucent DSP1645 [Mars]"},
+	}},
+	{0x117b, "L G Electronics, Inc.", {
+	}},
+	{0x117c, "Atto Technology", {
+	}},
+	{0x117d, "Becton & Dickinson", {
+	}},
+	{0x117e, "T/R Systems", {
+	}},
+	{0x117f, "Integrated Circuit Systems", {
+	}},
+	{0x1180, "Ricoh Co Ltd", {
+		{0x0466,"RL5C466"},
+	}},
+	{0x1181, "Telmatics International", {
+	}},
+	{0x1183, "Fujikura Ltd", {
+	}},
+	{0x1184, "Forks Inc", {
+	}},
+	{0x1185, "Dataworld International Ltd", {
+	}},
+	{0x1186, "D-Link System Inc", {
+	}},
+	{0x1187, "Advanced Technology Laboratories, Inc.", {
+	}},
+	{0x1188, "Shima Seiki Manufacturing Ltd.", {
+	}},
+	{0x1189, "Matsushita Electronics Co Ltd", {
+	}},
+	{0x118a, "Hilevel Technology", {
+	}},
+	{0x118b, "Hypertec Pty Limited", {
+	}},
+	{0x118c, "Corollary, Inc", {
+		{0x0014,"PCIB [C-bus II to PCI bus host bridge chip]"},
+	}},
+	{0x118d, "BitFlow Inc", {
+		{0x0001,"n/a [Raptor-PCI framegrabber]"},
+	}},
+	{0x118e, "Hermstedt GmbH", {
+	}},
+	{0x118f, "Green Logic", {
+	}},
+	{0x1191, "Artop Electronic Corp", {
+		{0x0004,"ATP8400"},
+		{0x0005,"ATP850UF"},
+	}},
+	{0x1192, "Densan Company Ltd", {
+	}},
+	{0x1193, "Zeitnet Inc.", {
+		{0x0001,"1221"},
+		{0x0002,"1225"},
+	}},
+	{0x1194, "Toucan Technology", {
+	}},
+	{0x1195, "Ratoc System Inc", {
+	}},
+	{0x1196, "Hytec Electronics Ltd", {
+	}},
+	{0x1197, "Gage Applied Sciences, Inc.", {
+	}},
+	{0x1198, "Lambda Systems Inc", {
+	}},
+	{0x1199, "Attachmate Corporation", {
+	}},
+	{0x119a, "Mind Share, Inc.", {
+	}},
+	{0x119b, "Omega Micro Inc.", {
+		{0x1221,"82C092G"},
+	}},
+	{0x119c, "Information Technology Inst.", {
+	}},
+	{0x119d, "Bug, Inc. Sapporo Japan", {
+	}},
+	{0x119e, "Fujitsu Microelectronics Ltd.", {
+	}},
+	{0x119f, "Bull HN Information Systems", {
+	}},
+	{0x11a0, "Convex Computer Corporation", {
+	}},
+	{0x11a1, "Hamamatsu Photonics K.K.", {
+	}},
+	{0x11a2, "Sierra Research and Technology", {
+	}},
+	{0x11a3, "Deuretzbacher GmbH & Co. Eng. KG", {
+	}},
+	{0x11a4, "Barco Graphics NV", {
+	}},
+	{0x11a5, "Microunity Systems Eng. Inc", {
+	}},
+	{0x11a6, "Pure Data Ltd.", {
+	}},
+	{0x11a7, "Power Computing Corp.", {
+	}},
+	{0x11a8, "Systech Corp.", {
+	}},
+	{0x11a9, "InnoSys Inc.", {
+	}},
+	{0x11aa, "Actel", {
+	}},
+	{0x11ab, "Galileo Technology Ltd.", {
+		{0x0146,"GT-64010"},
+		{0x4801,"GT-48001"},
+	}},
+	{0x11ac, "Canon Information Systems Research Aust.", {
+	}},
+	{0x11ad, "Lite-On Communications Inc", {
+		{0x0002,"LNE100TX"},
+	}},
+	{0x11ae, "Aztech System Ltd", {
+	}},
+	{0x11af, "Avid Technology Inc.", {
+	}},
+	{0x11b0, "V3 Semiconductor Inc.", {
+		{0x0292,"V292PBC [Am29030/40 Bridge]"},
+		{0x0960,"V96xPBC"},
+		{0xc960,"V96DPC"},
+	}},
+	{0x11b1, "Apricot Computers", {
+	}},
+	{0x11b2, "Eastman Kodak", {
+	}},
+	{0x11b3, "Barr Systems Inc.", {
+	}},
+	{0x11b4, "Leitch Technology International", {
+	}},
+	{0x11b5, "Radstone Technology Plc", {
+	}},
+	{0x11b6, "United Video Corp", {
+	}},
+	{0x11b8, "XPoint Technologies, Inc", {
+	}},
+	{0x11b9, "Pathlight Technology Inc.", {
+	}},
+	{0x11ba, "Videotron Corp", {
+	}},
+	{0x11bb, "Pyramid Technology", {
+	}},
+	{0x11bc, "Network Peripherals Inc", {
+		{0x0001,"NP-PCI"},
+	}},
+	{0x11bd, "Pinnacle Systems Inc.", {
+	}},
+	{0x11be, "International Microcircuits Inc", {
+	}},
+	{0x11bf, "Astrodesign, Inc.", {
+	}},
+	{0x11c0, "Hewlett Packard", {
+	}},
+	{0x11c1, "Lucent Microelectronics", {
+		{0x0440,"L56xMF"},
+	}},
+	{0x11c2, "Sand Microelectronics", {
+	}},
+	{0x11c4, "Document Technologies, Inc", {
+	}},
+	{0x11c5, "Shiva Corporation", {
+	}},
+	{0x11c6, "Dainippon Screen Mfg. Co. Ltd", {
+	}},
+	{0x11c7, "D.C.M. Data Systems", {
+	}},
+	{0x11c8, "Dolphin Interconnect Solutions AS", {
+		{0x0658,"PSB"},
+	}},
+	{0x11c9, "Magma", {
+	}},
+	{0x11ca, "LSI Systems, Inc", {
+	}},
+	{0x11cb, "Specialix Research Ltd.", {
+		{0x2000,"PCI_9050"},
+		{0x4000,"SUPI_1"},
+		{0x8000,"T225"},
+	}},
+	{0x11cc, "Michels & Kleberhoff Computer GmbH", {
+	}},
+	{0x11cd, "HAL Computer Systems, Inc.", {
+	}},
+	{0x11ce, "Netaccess", {
+	}},
+	{0x11cf, "Pioneer Electronic Corporation", {
+	}},
+	{0x11d0, "Lockheed Martin Federal Systems-Manassas", {
+	}},
+	{0x11d1, "Auravision", {
+		{0x01f7,"VxP524"},
+	}},
+	{0x11d2, "Intercom Inc.", {
+	}},
+	{0x11d3, "Trancell Systems Inc", {
+	}},
+	{0x11d4, "Analog Devices", {
+	}},
+	{0x11d5, "Ikon Corporation", {
+		{0x0115,"10115"},
+		{0x0117,"10117"},
+	}},
+	{0x11d6, "Tekelec Telecom", {
+	}},
+	{0x11d7, "Trenton Technology, Inc.", {
+	}},
+	{0x11d8, "Image Technologies Development", {
+	}},
+	{0x11d9, "TEC Corporation", {
+	}},
+	{0x11da, "Novell", {
+	}},
+	{0x11db, "Sega Enterprises Ltd", {
+	}},
+	{0x11dc, "Questra Corporation", {
+	}},
+	{0x11dd, "Crosfield Electronics Limited", {
+	}},
+	{0x11de, "Zoran Corporation", {
+		{0x6057,"ZR36057"},
+		{0x6120,"ZR36120"},
+	}},
+	{0x11df, "New Wave PDG", {
+	}},
+	{0x11e0, "Cray Communications A/S", {
+	}},
+	{0x11e1, "GEC Plessey Semi Inc.", {
+	}},
+	{0x11e2, "Samsung Information Systems America", {
+	}},
+	{0x11e3, "Quicklogic Corporation", {
+	}},
+	{0x11e4, "Second Wave Inc", {
+	}},
+	{0x11e5, "IIX Consulting", {
+	}},
+	{0x11e6, "Mitsui-Zosen System Research", {
+	}},
+	{0x11e7, "Toshiba America, Elec. Company", {
+	}},
+	{0x11e8, "Digital Processing Systems Inc.", {
+	}},
+	{0x11e9, "Highwater Designs Ltd.", {
+	}},
+	{0x11ea, "Elsag Bailey", {
+	}},
+	{0x11eb, "Formation Inc.", {
+	}},
+	{0x11ec, "Coreco Inc", {
+	}},
+	{0x11ed, "Mediamatics", {
+	}},
+	{0x11ee, "Dome Imaging Systems Inc", {
+	}},
+	{0x11ef, "Nicolet Technologies B.V.", {
+	}},
+	{0x11f0, "Compu-Shack GmbH", {
+	}},
+	{0x11f1, "Symbios Logic Inc", {
+	}},
+	{0x11f2, "Picture Tel Japan K.K.", {
+	}},
+	{0x11f3, "Keithley Metrabyte", {
+	}},
+	{0x11f4, "Kinetic Systems Corporation", {
+		{0x2915,"CAMAC controller"},
+	}},
+	{0x11f5, "Computing Devices International", {
+	}},
+	{0x11f6, "Compex", {
+		{0x0112,"ENet100VG4"},
+		{0x1401,"ReadyLink 2000"},
+	}},
+	{0x11f7, "Scientific Atlanta", {
+	}},
+	{0x11f8, "PMC-Sierra Inc.", {
+		{0x7375,"PM7375 [LASAR-155 ATM SAR]"},
+	}},
+	{0x11f9, "I-Cube Inc", {
+	}},
+	{0x11fa, "Kasan Electronics Company, Ltd.", {
+	}},
+	{0x11fb, "Datel Inc", {
+	}},
+	{0x11fc, "Silicon Magic", {
+	}},
+	{0x11fd, "High Street Consultants", {
+	}},
+	{0x11fe, "Comtrol Corporation", {
+		{0x0001,"RocketPort 8 Oct"},
+		{0x0002,"RocketPort 8 Intf"},
+		{0x0003,"RocketPort 16 Intf"},
+		{0x0004,"RocketPort 32 Intf"},
+	}},
+	{0x11ff, "Scion Corporation", {
+	}},
+	{0x1200, "CSS Corporation", {
+	}},
+	{0x1201, "Vista Controls Corp", {
+	}},
+	{0x1202, "Network General Corp.", {
+	}},
+	{0x1203, "Bayer Corporation, Agfa Division", {
+	}},
+	{0x1204, "Lattice Semiconductor Corporation", {
+	}},
+	{0x1205, "Array Corporation", {
+	}},
+	{0x1206, "Amdahl Corporation", {
+	}},
+	{0x1208, "Parsytec GmbH", {
+	}},
+	{0x1209, "SCI Systems Inc", {
+	}},
+	{0x120a, "Synaptel", {
+	}},
+	{0x120b, "Adaptive Solutions", {
+	}},
+	{0x120c, "Technical Corp.", {
+	}},
+	{0x120d, "Compression Labs, Inc.", {
+	}},
+	{0x120e, "Cyclades Corporation", {
+		{0x0100,"Cyclom_Y"},
+		{0x0200,"Cyclom_Z"},
+	}},
+	{0x120f, "Essential Communications", {
+		{0x0001,"Roadrunner serial HIPPI"},
+	}},
+	{0x1210, "Hyperparallel Technologies", {
+	}},
+	{0x1211, "Braintech Inc", {
+	}},
+	{0x1212, "Kingston Technology Corp.", {
+	}},
+	{0x1213, "Applied Intelligent Systems, Inc.", {
+	}},
+	{0x1214, "Performance Technologies, Inc.", {
+	}},
+	{0x1215, "Interware Co., Ltd", {
+	}},
+	{0x1216, "Purup Prepress A/S", {
+	}},
+	{0x1217, "O2 Micro, Inc.", {
+		{0x6729,"6729"},
+		{0x673a,"6730"},
+		{0x6832,"6832"},
+	}},
+	{0x1218, "Hybricon Corp.", {
+	}},
+	{0x1219, "First Virtual Corporation", {
+	}},
+	{0x121a, "3Dfx Interactive, Inc.", {
+		{0x0001,"Voodoo"},
+		{0x0002,"Voodoo2"},
+	}},
+	{0x121b, "Advanced Telecommunications Modules", {
+	}},
+	{0x121c, "Nippon Texaco., Ltd", {
+	}},
+	{0x121d, "Lippert Automationstechnik GmbH", {
+	}},
+	{0x121e, "CSPI", {
+	}},
+	{0x121f, "Arcus Technology, Inc.", {
+	}},
+	{0x1220, "Ariel Corporation", {
+	}},
+	{0x1221, "Contec Co., Ltd", {
+	}},
+	{0x1222, "Ancor Communications, Inc.", {
+	}},
+	{0x1223, "Heurikon/Computer Products", {
+	}},
+	{0x1224, "Interactive Images", {
+	}},
+	{0x1225, "Power I/O, Inc.", {
+	}},
+	{0x1227, "Tech-Source", {
+	}},
+	{0x1228, "Norsk Elektro Optikk A/S", {
+	}},
+	{0x1229, "Data Kinesis Inc.", {
+	}},
+	{0x122a, "Integrated Telecom", {
+	}},
+	{0x122b, "LG Industrial Systems Co., Ltd", {
+	}},
+	{0x122c, "Sican GmbH", {
+	}},
+	{0x122d, "Aztech System Ltd", {
+	}},
+	{0x122e, "Xyratex", {
+	}},
+	{0x122f, "Andrew Corporation", {
+	}},
+	{0x1230, "Fishcamp Engineering", {
+	}},
+	{0x1231, "Woodward McCoach, Inc.", {
+	}},
+	{0x1232, "GPT Limited", {
+	}},
+	{0x1233, "Bus-Tech, Inc.", {
+	}},
+	{0x1234, "Technical Corp.", {
+	}},
+	{0x1235, "Risq Modular Systems, Inc.", {
+	}},
+	{0x1236, "Sigma Designs Corporation", {
+		{0x6401,"REALmagic 64/GX (SD 6425)"},
+	}},
+	{0x1237, "Alta Technology Corporation", {
+	}},
+	{0x1238, "Adtran", {
+	}},
+	{0x1239, "3DO Company", {
+	}},
+	{0x123a, "Visicom Laboratories, Inc.", {
+	}},
+	{0x123b, "Seeq Technology, Inc.", {
+	}},
+	{0x123c, "Century Systems, Inc.", {
+	}},
+	{0x123d, "Engineering Design Team, Inc.", {
+	}},
+	{0x123e, "Simutech, Inc.", {
+	}},
+	{0x123f, "C-Cube Microsystems", {
+		{0x00e4,"MPEG"},
+	}},
+	{0x1240, "Marathon Technologies Corp.", {
+	}},
+	{0x1241, "DSC Communications", {
+	}},
+	{0x1243, "Delphax", {
+	}},
+	{0x1244, "AVM Audiovisuelles MKTG & Computer System GmbH", {
+	}},
+	{0x1245, "A.P.D., S.A.", {
+	}},
+	{0x1246, "Dipix Technologies, Inc.", {
+	}},
+	{0x1247, "Xylon Research, Inc.", {
+	}},
+	{0x1248, "Central Data Corporation", {
+	}},
+	{0x1249, "Samsung Electronics Co., Ltd.", {
+	}},
+	{0x124a, "AEG Electrocom GmbH", {
+	}},
+	{0x124b, "SBS/Greenspring Modular I/O", {
+	}},
+	{0x124c, "Solitron Technologies, Inc.", {
+	}},
+	{0x124d, "Stallion Technologies, Inc.", {
+		{0x0000,"EasyConnection 8/32 - PCI"},
+		{0x0002,"EasyConnection 8/64 - PCI"},
+		{0x0003,"EasyIO - PCI"},
+	}},
+	{0x124e, "Cylink", {
+	}},
+	{0x124f, "Infotrend Technology, Inc.", {
+	}},
+	{0x1250, "Hitachi Microcomputer System Ltd", {
+	}},
+	{0x1251, "VLSI Solutions Oy", {
+	}},
+	{0x1253, "Guzik Technical Enterprises", {
+	}},
+	{0x1254, "Linear Systems Ltd.", {
+	}},
+	{0x1255, "Optibase Ltd", {
+		{0x1110,"MPEG Forge"},
+		{0x1210,"MPEG Fusion"},
+		{0x2110,"VideoPlex"},
+		{0x2120,"VideoPlex CC"},
+		{0x2130,"VideoQuest"},
+	}},
+	{0x1256, "Perceptive Solutions, Inc.", {
+	}},
+	{0x1257, "Vertex Networks, Inc.", {
+	}},
+	{0x1258, "Gilbarco, Inc.", {
+	}},
+	{0x1259, "Allied Telesyn International", {
+	}},
+	{0x125a, "ABB Power Systems", {
+	}},
+	{0x125b, "Asix Electronics Corporation", {
+	}},
+	{0x125c, "Aurora Technologies, Inc.", {
+	}},
+	{0x125d, "ESS Technology", {
+	}},
+	{0x125e, "Specialvideo Engineering SRL", {
+	}},
+	{0x125f, "Concurrent Technologies, Inc.", {
+	}},
+	{0x1260, "Harris Semiconductor", {
+	}},
+	{0x1261, "Matsushita-Kotobuki Electronics Industries, Ltd.", {
+	}},
+	{0x1262, "ES Computer Company, Ltd.", {
+	}},
+	{0x1263, "Sonic Solutions", {
+	}},
+	{0x1264, "Aval Nagasaki Corporation", {
+	}},
+	{0x1265, "Casio Computer Co., Ltd.", {
+	}},
+	{0x1266, "Microdyne Corporation", {
+	}},
+	{0x1267, "S. A. Telecommunications", {
+		{0x5352,"PCR2101"},
+		{0x5a4b,"Telsat Turbo"},
+	}},
+	{0x1268, "Tektronix", {
+	}},
+	{0x1269, "Thomson-CSF/TTM", {
+	}},
+	{0x126a, "Lexmark International, Inc.", {
+	}},
+	{0x126b, "Adax, Inc.", {
+	}},
+	{0x126c, "Northern Telecom", {
+	}},
+	{0x126d, "Splash Technology, Inc.", {
+	}},
+	{0x126e, "Sumitomo Metal Industries, Ltd.", {
+	}},
+	{0x126f, "Silicon Motion, Inc.", {
+	}},
+	{0x1270, "Olympus Optical Co., Ltd.", {
+	}},
+	{0x1271, "GW Instruments", {
+	}},
+	{0x1272, "Telematics International", {
+	}},
+	{0x1273, "Hughes Network Systems", {
+		{0x0002,"DirecPC"},
+	}},
+	{0x1274, "Ensoniq", {
+		{0x5000,"AudioPCI"},
+	}},
+	{0x1275, "Network Appliance Corporation", {
+	}},
+	{0x1276, "Switched Network Technologies, Inc.", {
+	}},
+	{0x1277, "Comstream", {
+	}},
+	{0x1278, "Transtech Parallel Systems Ltd.", {
+	}},
+	{0x1279, "Transmeta Corporation", {
+	}},
+	{0x127a, "Rockwell International", {
+	}},
+	{0x127b, "Pixera Corporation", {
+	}},
+	{0x127c, "Crosspoint Solutions, Inc.", {
+	}},
+	{0x127d, "Vela Research", {
+	}},
+	{0x127e, "Winnov, L.P.", {
+	}},
+	{0x127f, "Fujifilm", {
+	}},
+	{0x1280, "Photoscript Group Ltd.", {
+	}},
+	{0x1281, "Yokogawa Electric Corporation", {
+	}},
+	{0x1282, "Davicom Semiconductor, Inc.", {
+	}},
+	{0x1283, "Integrated Technology Express, Inc.", {
+	}},
+	{0x1284, "Sahara Networks, Inc.", {
+	}},
+	{0x1285, "Platform Technologies, Inc.", {
+	}},
+	{0x1286, "Mazet GmbH", {
+	}},
+	{0x1287, "M-Pact, Inc.", {
+	}},
+	{0x1288, "Timestep Corporation", {
+	}},
+	{0x1289, "AVC Technology, Inc.", {
+	}},
+	{0x128a, "Asante Technologies, Inc.", {
+	}},
+	{0x128b, "Transwitch Corporation", {
+	}},
+	{0x128c, "Retix Corporation", {
+	}},
+	{0x128d, "G2 Networks, Inc.", {
+	}},
+	{0x128e, "Samho Multi Tech Ltd.", {
+	}},
+	{0x128f, "Tateno Dennou, Inc.", {
+	}},
+	{0x1290, "Sord Computer Corporation", {
+	}},
+	{0x1291, "NCS Computer Italia", {
+	}},
+	{0x1292, "Tritech Microelectronics Inc", {
+	}},
+	{0x1293, "Media Reality Technology", {
+	}},
+	{0x1294, "Rhetorex, Inc.", {
+	}},
+	{0x1295, "Imagenation Corporation", {
+	}},
+	{0x1296, "Kofax Image Products", {
+	}},
+	{0x1297, "Holco Enterprise Co, Ltd/Shuttle Computer", {
+	}},
+	{0x1298, "Spellcaster Telecommunications Inc.", {
+	}},
+	{0x1299, "Knowledge Technology Lab.", {
+	}},
+	{0x129b, "Image Access", {
+	}},
+	{0x129c, "Jaycor", {
+	}},
+	{0x129d, "Compcore Multimedia, Inc.", {
+	}},
+	{0x129e, "Victor Company of Japan, Ltd.", {
+	}},
+	{0x129f, "OEC Medical Systems, Inc.", {
+	}},
+	{0x12a0, "Allen-Bradley Company", {
+	}},
+	{0x12a1, "Simpact Associates, Inc.", {
+	}},
+	{0x12a2, "Newgen Systems Corporation", {
+	}},
+	{0x12a3, "Lucent Technologies", {
+	}},
+	{0x12a4, "NTT Electronics Technology Company", {
+	}},
+	{0x12a5, "Vision Dynamics Ltd.", {
+	}},
+	{0x12a6, "Scalable Networks, Inc.", {
+	}},
+	{0x12a7, "AMO GmbH", {
+	}},
+	{0x12a8, "News Datacom", {
+	}},
+	{0x12a9, "Xiotech Corporation", {
+	}},
+	{0x12aa, "SDL Communications, Inc.", {
+	}},
+	{0x12ab, "Yuan Yuan Enterprise Co., Ltd.", {
+	}},
+	{0x12ac, "Measurex Corporation", {
+	}},
+	{0x12ad, "Multidata GmbH", {
+	}},
+	{0x12ae, "Alteon Networks Inc.", {
+		{0x0001,"AceNIC Gigabit Ethernet"},
+	}},
+	{0x12af, "TDK USA Corp", {
+	}},
+	{0x12b0, "Jorge Scientific Corp", {
+	}},
+	{0x12b1, "GammaLink", {
+	}},
+	{0x12b2, "General Signal Networks", {
+	}},
+	{0x12b3, "Inter-Face Co Ltd", {
+	}},
+	{0x12b4, "FutureTel Inc", {
+	}},
+	{0x12b5, "Granite Systems Inc.", {
+	}},
+	{0x12b6, "Natural Microsystems", {
+	}},
+	{0x12b7, "Cognex Modular Vision Systems Div. - Acumen Inc.", {
+	}},
+	{0x12b8, "Korg", {
+	}},
+	{0x12b9, "US Robotics", {
+	}},
+	{0x12ba, "PMC Sierra", {
+	}},
+	{0x12bb, "Nippon Unisoft Corporation", {
+	}},
+	{0x12bc, "Array Microsystems", {
+	}},
+	{0x12bd, "Computerm Corp.", {
+	}},
+	{0x12be, "Anchor Chips Inc.", {
+	}},
+	{0x12bf, "Fujifilm Microdevices", {
+	}},
+	{0x12c0, "Infimed", {
+	}},
+	{0x12c1, "GMM Research Corp", {
+	}},
+	{0x12c2, "Mentec Limited", {
+	}},
+	{0x12c3, "Holtek Microelectronics Inc", {
+	}},
+	{0x12c4, "Connect Tech Inc", {
+	}},
+	{0x12c5, "Picture Elements Incorporated", {
+		{0x0081,"PCIVST [PCI Grayscale Thresholding Engine]"},
+	}},
+	{0x12c6, "Mitani Corporation", {
+	}},
+	{0x12c7, "Dialogic Corp", {
+	}},
+	{0x12c8, "G Force Co, Ltd", {
+	}},
+	{0x12c9, "Gigi Operations", {
+	}},
+	{0x12ca, "Integrated Computing Engines", {
+	}},
+	{0x12cb, "Antex Electronics Corporation", {
+	}},
+	{0x12cc, "Pluto Technologies International", {
+	}},
+	{0x12cd, "Aims Lab", {
+	}},
+	{0x12ce, "Netspeed Inc.", {
+	}},
+	{0x12cf, "Prophet Systems, Inc.", {
+	}},
+	{0x12d0, "GDE Systems, Inc.", {
+	}},
+	{0x12d1, "PSITech", {
+	}},
+	{0x12d2, "NVidia / SGS Thomson (Joint Venture)", {
+		{0x0018,"Riva128"},
+	}},
+	{0x12d3, "Vingmed Sound A/S", {
+	}},
+	{0x12d4, "DGM&S", {
+	}},
+	{0x12d5, "Equator Technologies", {
+	}},
+	{0x12d6, "Analogic Corp", {
+	}},
+	{0x12d7, "Biotronic SRL", {
+	}},
+	{0x12d8, "Pericom Semiconductor", {
+	}},
+	{0x12d9, "Aculab PLC", {
+	}},
+	{0x12da, "True Time Inc.", {
+	}},
+	{0x12db, "Annapolis Micro Systems, Inc", {
+	}},
+	{0x12dc, "Symicron Computer Communication Ltd.", {
+	}},
+	{0x12dd, "Management Graphics", {
+	}},
+	{0x12de, "Rainbow Technologies", {
+	}},
+	{0x12df, "SBS Technologies Inc", {
+	}},
+	{0x12e0, "Chase Research", {
+	}},
+	{0x12e1, "Nintendo Co, Ltd", {
+	}},
+	{0x12e2, "Datum Inc. Bancomm-Timing Division", {
+	}},
+	{0x12e3, "Imation Corp - Medical Imaging Systems", {
+	}},
+	{0x12e4, "Brooktrout Technology Inc", {
+	}},
+	{0x12e5, "Apex Semiconductor Inc", {
+	}},
+	{0x12e6, "Cirel Systems", {
+	}},
+	{0x12e7, "Sunsgroup Corporation", {
+	}},
+	{0x12e8, "Crisc Corp", {
+	}},
+	{0x12e9, "GE Spacenet", {
+	}},
+	{0x12ea, "Zuken", {
+	}},
+	{0x12eb, "Aureal Semiconductor", {
+	}},
+	{0x12ec, "3A International, Inc.", {
+	}},
+	{0x12ed, "Optivision Inc.", {
+	}},
+	{0x12ee, "Orange Micro", {
+	}},
+	{0x12ef, "Vienna Systems", {
+	}},
+	{0x12f0, "Pentek", {
+	}},
+	{0x12f1, "Sorenson Vision Inc", {
+	}},
+	{0x12f2, "Gammagraphx, Inc.", {
+	}},
+	{0x12f3, "Radstone Technology", {
+	}},
+	{0x12f4, "Megatel", {
+	}},
+	{0x12f5, "Forks", {
+	}},
+	{0x12f6, "Dawson France", {
+	}},
+	{0x12f7, "Cognex", {
+	}},
+	{0x12f8, "Electronic Design GmbH", {
+	}},
+	{0x12f9, "Four Fold Ltd", {
+	}},
+	{0x12fb, "Spectrum Signal Processing", {
+	}},
+	{0x12fc, "Capital Equipment Corp", {
+	}},
+	{0x12fd, "I2S", {
+	}},
+	{0x12fe, "ESD Electronic System Design GmbH", {
+	}},
+	{0x12ff, "Lexicon", {
+	}},
+	{0x1300, "Harman International Industries Inc", {
+	}},
+	{0x1302, "Computer Sciences Corp", {
+	}},
+	{0x1303, "Innovative Integration", {
+	}},
+	{0x1304, "Juniper Networks", {
+	}},
+	{0x1305, "Netphone, Inc", {
+	}},
+	{0x1306, "Duet Technologies", {
+	}},
+	{0x1307, "Computer Boards", {
+		{0x0001,"DAS1602/16"},
+	}},
+	{0x1308, "Jato Technologies Inc.", {
+	}},
+	{0x1309, "AB Semiconductor Ltd", {
+	}},
+	{0x130a, "Mitsubishi Electric Microcomputer", {
+	}},
+	{0x130b, "Colorgraphic Communications Corp", {
+	}},
+	{0x130c, "Ambex Technologies, Inc", {
+	}},
+	{0x130d, "Accelerix Inc", {
+	}},
+	{0x130e, "Yamatake-Honeywell Co. Ltd", {
+	}},
+	{0x130f, "Advanet Inc", {
+	}},
+	{0x1310, "Gespac", {
+	}},
+	{0x1311, "Videoserver, Inc", {
+	}},
+	{0x1312, "Acuity Imaging, Inc", {
+	}},
+	{0x1313, "Yaskawa Electric Co.", {
+	}},
+	{0x1316, "Teradyne Inc", {
+	}},
+	{0x1317, "Bridgecom, Inc", {
+	}},
+	{0x1318, "Packet Engines Inc.", {
+	}},
+	{0x1319, "Fortemedia, Inc", {
+	}},
+	{0x131a, "Finisar Corp.", {
+	}},
+	{0x131c, "Nippon Electro-Sensory Devices Corp", {
+	}},
+	{0x131d, "Sysmic, Inc.", {
+	}},
+	{0x131e, "Xinex Networks Inc", {
+	}},
+	{0x131f, "Siig Inc", {
+	}},
+	{0x1320, "Crypto AG", {
+	}},
+	{0x1321, "Arcobel Graphics BV", {
+	}},
+	{0x1322, "MTT Co., Ltd", {
+	}},
+	{0x1323, "Dome Inc", {
+	}},
+	{0x1324, "Sphere Communications", {
+	}},
+	{0x1325, "Salix Technologies, Inc", {
+	}},
+	{0x1326, "Seachange international", {
+	}},
+	{0x1327, "Voss scientific", {
+	}},
+	{0x1328, "quadrant international", {
+	}},
+	{0x1329, "Productivity Enhancement", {
+	}},
+	{0x132a, "Microcom Inc.", {
+	}},
+	{0x132b, "Broadband Technologies", {
+	}},
+	{0x132c, "Micrel Inc", {
+	}},
+	{0x132d, "Integrated Silicon Solution, Inc.", {
+	}},
+	{0x1330, "MMC Networks", {
+	}},
+	{0x1331, "Radisys Corp.", {
+	}},
+	{0x1332, "Micro Memory", {
+	}},
+	{0x1334, "Redcreek Communications, Inc", {
+	}},
+	{0x1335, "Videomail, Inc", {
+	}},
+	{0x1337, "Third Planet Publishing", {
+	}},
+	{0x1338, "BT Electronics", {
+	}},
+	{0x133a, "Vtel Corp", {
+	}},
+	{0x133b, "Softcom Microsystems", {
+	}},
+	{0x133c, "Holontech Corp", {
+	}},
+	{0x133d, "SS Technologies", {
+	}},
+	{0x133e, "Virtual Computer Corp", {
+	}},
+	{0x133f, "SCM Microsystems", {
+	}},
+	{0x1340, "Atalla Corp", {
+	}},
+	{0x1341, "Kyoto Microcomputer Co", {
+	}},
+	{0x1342, "Promax Systems Inc", {
+	}},
+	{0x1343, "Phylon Communications Inc", {
+	}},
+	{0x1344, "Crucial Technology", {
+	}},
+	{0x1345, "Arescom Inc", {
+	}},
+	{0x1347, "Odetics", {
+	}},
+	{0x1349, "Sumitomo Electric Industries, Ltd.", {
+	}},
+	{0x134a, "DTC Technology Corp.", {
+	}},
+	{0x134b, "ARK Research Corp.", {
+	}},
+	{0x134c, "Chori Joho System Co. Ltd", {
+	}},
+	{0x134d, "PCTel Inc", {
+	}},
+	{0x134e, "CSTI", {
+	}},
+	{0x134f, "Algo System Co Ltd", {
+	}},
+	{0x1350, "Systec Co. Ltd", {
+	}},
+	{0x1351, "Sonix Inc", {
+	}},
+	{0x1353, "Dassault A.T.", {
+	}},
+	{0x1354, "Dwave System Inc", {
+	}},
+	{0x1355, "Kratos Analytical Ltd", {
+	}},
+	{0x1356, "The Logical Co", {
+	}},
+	{0x1359, "Prisa Networks", {
+	}},
+	{0x135a, "Brain Boxes", {
+	}},
+	{0x135b, "Giganet Inc", {
+	}},
+	{0x135c, "Quatech Inc", {
+	}},
+	{0x135d, "ABB Network Partner AB", {
+	}},
+	{0x135e, "Sealevel Systems Inc", {
+	}},
+	{0x135f, "I-Data International A-S", {
+	}},
+	{0x1360, "Meinberg Funkuhren", {
+	}},
+	{0x1361, "Soliton Systems K.K.", {
+	}},
+	{0x1362, "Fujifacom Corporation", {
+	}},
+	{0x1363, "Phoenix Technology Ltd", {
+	}},
+	{0x1364, "ATM Communications Inc", {
+	}},
+	{0x1365, "Hypercope GmbH", {
+	}},
+	{0x1366, "Teijin Seiki Co. Ltd", {
+	}},
+	{0x1367, "Hitachi Zosen Corporation", {
+	}},
+	{0x1368, "Skyware Corporation", {
+	}},
+	{0x1369, "Digigram", {
+	}},
+	{0x136a, "High Soft Tech", {
+	}},
+	{0x136b, "Kawasaki Steel Corporation", {
+	}},
+	{0x136c, "Adtek System Science Co Ltd", {
+	}},
+	{0x136d, "Gigalabs Inc", {
+	}},
+	{0x136f, "Applied Magic Inc", {
+	}},
+	{0x1370, "ATL Products", {
+	}},
+	{0x1371, "CNet Technology Inc", {
+	}},
+	{0x1373, "Silicon Vision Inc", {
+	}},
+	{0x1374, "Silicom Ltd", {
+	}},
+	{0x1375, "Argosystems Inc", {
+	}},
+	{0x1376, "LMC", {
+	}},
+	{0x1377, "Electronic Equipment Production & Distribution GmbH", {
+	}},
+	{0x1378, "Telemann Co. Ltd", {
+	}},
+	{0x1379, "Asahi Kasei Microsystems Co Ltd", {
+	}},
+	{0x137a, "Mark of the Unicorn Inc", {
+	}},
+	{0x137b, "PPT Vision", {
+	}},
+	{0x137c, "Iwatsu Electric Co Ltd", {
+	}},
+	{0x137d, "Dynachip Corporation", {
+	}},
+	{0x137e, "Patriot Scientific Corporation", {
+	}},
+	{0x137f, "Japan Satellite Systems Inc", {
+	}},
+	{0x1380, "Sanritz Automation Co Ltd", {
+	}},
+	{0x1381, "Brains Co. Ltd", {
+	}},
+	{0x1382, "Marian - Electronic & Software", {
+	}},
+	{0x1383, "Controlnet Inc", {
+	}},
+	{0x1384, "Reality Simulation Systems Inc", {
+	}},
+	{0x1385, "Netgear", {
+	}},
+	{0x1386, "Video Domain Technologies", {
+	}},
+	{0x1387, "Systran Corp", {
+	}},
+	{0x1388, "Hitachi Information Technology Co Ltd", {
+	}},
+	{0x1389, "Applicom International", {
+	}},
+	{0x138a, "Fusion Micromedia Corp", {
+	}},
+	{0x138b, "Tokimec Inc", {
+	}},
+	{0x138c, "Silicon Reality", {
+	}},
+	{0x138d, "Future Techno Designs pte Ltd", {
+	}},
+	{0x138e, "Basler GmbH", {
+	}},
+	{0x138f, "Patapsco Designs Inc", {
+	}},
+	{0x1390, "Concept Development Inc", {
+	}},
+	{0x1391, "Development Concepts Inc", {
+	}},
+	{0x1392, "Medialight Inc", {
+	}},
+	{0x1393, "Moxa Technologies Co Ltd", {
+	}},
+	{0x1394, "Level One Communications", {
+	}},
+	{0x1395, "Ambicom Inc", {
+	}},
+	{0x1396, "Cipher Systems Inc", {
+	}},
+	{0x1397, "Cologne Chip Designs GmbH", {
+	}},
+	{0x1398, "Clarion co. Ltd", {
+	}},
+	{0x1399, "Rios systems Co Ltd", {
+	}},
+	{0x139a, "Alacritech Inc", {
+	}},
+	{0x139b, "Mediasonic Multimedia Systems Ltd", {
+	}},
+	{0x139c, "Quantum 3d Inc", {
+	}},
+	{0x139d, "EPL limited", {
+	}},
+	{0x139e, "Media4", {
+	}},
+	{0x139f, "Aethra s.r.l.", {
+	}},
+	{0x13a0, "Crystal Group Inc", {
+	}},
+	{0x13a1, "Kawasaki Heavy Industries Ltd", {
+	}},
+	{0x13a2, "Ositech Communications Inc", {
+	}},
+	{0x13a3, "Hi-Fn", {
+	}},
+	{0x13a4, "Rascom Inc", {
+	}},
+	{0x13a5, "Audio Digital Imaging Inc", {
+	}},
+	{0x13a6, "Videonics Inc", {
+	}},
+	{0x13a7, "Teles AG", {
+	}},
+	{0x13a8, "Exar Corp.", {
+	}},
+	{0x13a9, "Siemens Medical Systems, Ultrasound Group", {
+	}},
+	{0x13aa, "Broadband Networks Inc", {
+	}},
+	{0x13ab, "Arcom Control Systems Ltd", {
+	}},
+	{0x13ac, "Motion Media Technology Ltd", {
+	}},
+	{0x13ad, "Nexus Inc", {
+	}},
+	{0x13ae, "ALD Technology Ltd", {
+	}},
+	{0x13af, "T.Sqware", {
+	}},
+	{0x13b0, "Maxspeed Corp", {
+	}},
+	{0x13b1, "Tamura corporation", {
+	}},
+	{0x13b2, "Techno Chips Co. Ltd", {
+	}},
+	{0x13b3, "Lanart Corporation", {
+	}},
+	{0x13b4, "Wellbean Co Inc", {
+	}},
+	{0x13b5, "ARM", {
+	}},
+	{0x13b6, "Dlog GmbH", {
+	}},
+	{0x13b7, "Logic Devices Inc", {
+	}},
+	{0x13b8, "Nokia Telecommunications oy", {
+	}},
+	{0x13b9, "Elecom Co Ltd", {
+	}},
+	{0x13ba, "Oxford Instruments", {
+	}},
+	{0x13bb, "Sanyo Technosound Co Ltd", {
+	}},
+	{0x13bc, "Bitran Corporation", {
+	}},
+	{0x13bd, "Sharp corporation", {
+	}},
+	{0x13be, "Miroku Jyoho Service Co. Ltd", {
+	}},
+	{0x13bf, "Sharewave Inc", {
+	}},
+	{0x13c0, "Microgate Corporation", {
+	}},
+	{0x13c1, "3ware Inc", {
+	}},
+	{0x13c2, "Technotrend Systemtechnik GmbH", {
+	}},
+	{0x13c3, "Janz Computer AG", {
+	}},
+	{0x13c4, "Phase Metrics", {
+	}},
+	{0x13c5, "Alphi Technology Corp", {
+	}},
+	{0x13c6, "Condor Engineering Inc", {
+	}},
+	{0x13c7, "Blue Chip Technology Ltd", {
+	}},
+	{0x13c8, "Apptech Inc", {
+	}},
+	{0x13c9, "Eaton Corporation", {
+	}},
+	{0x13ca, "Iomega Corporation", {
+	}},
+	{0x13cb, "Yano Electric Co Ltd", {
+	}},
+	{0x13cc, "Metheus Corporation", {
+	}},
+	{0x13cd, "Compatible Systems Corporation", {
+	}},
+	{0x13ce, "Cocom A/S", {
+	}},
+	{0x13cf, "Studio Audio & Video Ltd", {
+	}},
+	{0x13d0, "Techsan Electronics Co Ltd", {
+	}},
+	{0x13d1, "Abocom Systems Inc", {
+	}},
+	{0x13d2, "Shark Multimedia Inc", {
+	}},
+	{0x13d3, "IMC Networks", {
+	}},
+	{0x13d4, "Graphics Microsystems Inc", {
+	}},
+	{0x13d5, "Media 100 Inc", {
+	}},
+	{0x13d6, "K.I. Technology Co Ltd", {
+	}},
+	{0x13d7, "Toshiba Engineering Corporation", {
+	}},
+	{0x13d8, "Phobos corporation", {
+	}},
+	{0x13d9, "Apex PC Solutions Inc", {
+	}},
+	{0x13da, "Intresource Systems pte Ltd", {
+	}},
+	{0x13db, "Janich & Klass Computertechnik GmbH", {
+	}},
+	{0x13dc, "Netboost Corporation", {
+	}},
+	{0x13dd, "Multimedia Bundle Inc", {
+	}},
+	{0x13de, "ABB Robotics Products AB", {
+	}},
+	{0x13df, "E-Tech Inc", {
+	}},
+	{0x13e0, "GVC Corporation", {
+	}},
+	{0x13e1, "Silicom Multimedia Systems Inc", {
+	}},
+	{0x13e2, "Dynamics Research Corporation", {
+	}},
+	{0x13e3, "Nest Inc", {
+	}},
+	{0x13e4, "Calculex Inc", {
+	}},
+	{0x13e5, "Telesoft Design Ltd", {
+	}},
+	{0x13e6, "Argosy research Inc", {
+	}},
+	{0x13e7, "NAC Incorporated", {
+	}},
+	{0x13e8, "Chip Express Corporation", {
+	}},
+	{0x13e9, "Chip Express Corporation", {
+	}},
+	{0x13ea, "Dallas Semiconductor", {
+	}},
+	{0x13eb, "Hauppauge Computer Works Inc", {
+	}},
+	{0x13ec, "Zydacron Inc", {
+	}},
+	{0x13ed, "Raytheion E-Systems", {
+	}},
+	{0x13ee, "Hayes Microcomputer Products Inc", {
+	}},
+	{0x13ef, "Coppercom Inc", {
+	}},
+	{0x13f0, "Sundance technology Inc", {
+	}},
+	{0x13f1, "Oce' - Technologies B.V.", {
+	}},
+	{0x13f2, "Ford Microelectronics Inc", {
+	}},
+	{0x13f3, "Mcdata Corporation", {
+	}},
+	{0x13f4, "Troika Design Inc", {
+	}},
+	{0x13f5, "Kansai Electric Co. Ltd", {
+	}},
+	{0x13f6, "C-Media Electronics Inc", {
+	}},
+	{0x13f7, "Wildfire Communications", {
+	}},
+	{0x13f8, "Ad Lib Multimedia Inc", {
+	}},
+	{0x13f9, "NTT Advanced Technology Corp.", {
+	}},
+	{0x13fa, "Pentland Systems Ltd", {
+	}},
+	{0x13fb, "Aydin Corp", {
+	}},
+	{0x13fc, "Computer Peripherals International", {
+	}},
+	{0x13fd, "Micro Science Inc", {
+	}},
+	{0x13fe, "Advantech Co. Ltd", {
+	}},
+	{0x13ff, "Silicon Spice Inc", {
+	}},
+	{0x1400, "Artx Inc", {
+	}},
+	{0x1401, "CR-Systems A/S", {
+	}},
+	{0x1402, "Meilhaus Electronic GmbH", {
+	}},
+	{0x1403, "Ascor Inc", {
+	}},
+	{0x1404, "Fundamental Software Inc", {
+	}},
+	{0x1405, "Excalibur Systems Inc", {
+	}},
+	{0x1406, "Oce' Printing Systems GmbH", {
+	}},
+	{0x1407, "Lava Computer mfg Inc", {
+	}},
+	{0x1408, "Aloka Co. Ltd", {
+	}},
+	{0x1409, "Timedia Technology Co Ltd", {
+	}},
+	{0x140a, "DSP Research Inc", {
+	}},
+	{0x140b, "Ramix Inc", {
+	}},
+	{0x140c, "Elmic Systems Inc", {
+	}},
+	{0x140d, "Matsushita Electric Works Ltd", {
+	}},
+	{0x140e, "Goepel Electronic GmbH", {
+	}},
+	{0x140f, "Salient Systems Corp", {
+	}},
+	{0x1410, "Midas lab Inc", {
+	}},
+	{0x1411, "Ikos Systems Inc", {
+	}},
+	{0x1412, "IC Ensemble Inc", {
+	}},
+	{0x1413, "Addonics", {
+	}},
+	{0x1414, "Microsoft Corporation", {
+	}},
+	{0x1415, "Oxford Semiconductor Ltd", {
+	}},
+	{0x1416, "Multiwave Innovation pte Ltd", {
+	}},
+	{0x1417, "Convergenet Technologies Inc", {
+	}},
+	{0x1418, "Kyushu electronics systems Inc", {
+	}},
+	{0x1419, "Excel Switching Corp", {
+	}},
+	{0x141a, "Apache Micro Peripherals Inc", {
+	}},
+	{0x141b, "Zoom Telephonics Inc", {
+	}},
+	{0x141d, "Digitan Systems Inc", {
+	}},
+	{0x141e, "Fanuc Ltd", {
+	}},
+	{0x141f, "Visiontech Ltd", {
+	}},
+	{0x1420, "Psion Dacom plc", {
+	}},
+	{0x1421, "Ads Technologies Inc", {
+	}},
+	{0x1422, "Ygrec Systems Co Ltd", {
+	}},
+	{0x1423, "Custom Technology Corp.", {
+	}},
+	{0x1424, "Videoserver Connections", {
+	}},
+	{0x1425, "ASIC Designers Inc", {
+	}},
+	{0x1426, "Storage Technology Corp.", {
+	}},
+	{0x1427, "Better On-Line Solutions", {
+	}},
+	{0x1428, "Edec Co Ltd", {
+	}},
+	{0x1429, "Unex Technology Corp.", {
+	}},
+	{0x142a, "Kingmax Technology Inc", {
+	}},
+	{0x142b, "Radiolan", {
+	}},
+	{0x142c, "Minton Optic Industry Co Ltd", {
+	}},
+	{0x142d, "Pix stream Inc", {
+	}},
+	{0x142e, "Vitec Multimedia", {
+	}},
+	{0x142f, "Radicom Research Inc", {
+	}},
+	{0x1430, "ITT Aerospace/Communications Division", {
+	}},
+	{0x1431, "Gilat Satellite Networks", {
+	}},
+	{0x1432, "Edimax Computer Co.", {
+	}},
+	{0x1433, "Eltec Elektronik GmbH", {
+	}},
+	{0x1435, "Real Time Devices US Inc.", {
+	}},
+	{0x1436, "CIS Technology Inc", {
+	}},
+	{0x1668, "Action Tec Electronics Inc", {
+	}},
+	{0x1b13, "Jaton Corp", {
+	}},
+	{0x1c1c, "Symphony", {
+		{0x0001,"82C101"},
+	}},
+	{0x21c3, "21st Century Computer Corp.", {
+	}},
+	{0x270b, "Xantel Corporation", {
+	}},
+	{0x270f, "Chaintech Computer Co. Ltd", {
+	}},
+	{0x3388, "Hint Corp", {
+	}},
+	{0x3d3d, "3DLabs", {
+		{0x0001,"GLINT 300SX"},
+		{0x0002,"GLINT 500TX"},
+		{0x0003,"GLINT Delta"},
+		{0x0004,"Permedia"},
+		{0x0006,"GLINT MX"},
+	}},
+	{0x4005, "Avance Logic Inc.", {
+		{0x2064,"ALG2064i"},
+		{0x2301,"ALG2301"},
+		{0x2302,"ALG2302"},
+	}},
+	{0x4444, "Internext Compression Inc", {
+	}},
+	{0x4680, "Umax Computer Corp", {
+	}},
+	{0x4843, "Hercules Computer Technology Inc", {
+	}},
+	{0x4978, "Axil Computer Inc", {
+	}},
+	{0x4a14, "NetVin", {
+		{0x5000,"PCI NV5000SC"},
+	}},
+	{0x4ddc, "ILC Data Device Corp", {
+	}},
+	{0x5053, "Voyetra Technologies", {
+	}},
+	{0x5143, "Qualcomm Inc", {
+	}},
+	{0x5333, "S3 Inc.", {
+		{0x0551,"Plato/PX (system)"},
+		{0x5631,"86C325 [ViRGE]"},
+		{0x8810,"86C764_0 [Trio 32 vers 0]"},
+		{0x8811,"86C764_1 [Trio 32/64 vers 1]"},
+		{0x8812,"Aurora64V+"},
+		{0x8813,"86C764_3 [Trio 32/64 vers 3]"},
+		{0x8814,"Trio64UV+"},
+		{0x8815,"Aurora128"},
+		{0x883d,"ViRGE/VX"},
+		{0x8880,"Vision 868 vers 0"},
+		{0x8881,"Vision 868 vers 1"},
+		{0x8882,"Vision 868 vers 2"},
+		{0x8883,"Vision 868 vers 3"},
+		{0x88b0,"Vision 928 vers 0"},
+		{0x88b1,"Vision 928 vers 1"},
+		{0x88b2,"Vision 928 vers 2"},
+		{0x88b3,"Vision 928 vers 3"},
+		{0x88c0,"Vision 864 vers 0"},
+		{0x88c1,"Vision 864 vers 1"},
+		{0x88c2,"86C864"},
+		{0x88c3,"86C864"},
+		{0x88d0,"Vision 964 vers 0"},
+		{0x88d1,"Vision 964 vers 1"},
+		{0x88d2,"86C964"},
+		{0x88d3,"86C964"},
+		{0x88f0,"Vision 968"},
+		{0x88f1,"86C968"},
+		{0x88f2,"86C968"},
+		{0x88f3,"86C968"},
+		{0x8901,"Trio64V2/DX or /GX"},
+		{0x8902,"Plato/PX (graphics)"},
+		{0x8a01,"ViRGE/DX or /GX"},
+		{0x8a10,"ViRGE/GX2"},
+		{0x8c01,"ViRGE/MX"},
+		{0x8c02,"ViRGE/MX+"},
+		{0x8c03,"ViRGE/MX+MV"},
+		{0xca00,"SonicVibes"},
+	}},
+	{0x5555, "Genroco, Inc", {
+		{0x0003,"TURBOstor HFP-832 [HiPPI NIC]"},
+	}},
+	{0x6374, "c't Magazin f€r Computertechnik", {
+		{0x6773,"GPPCI"},
+	}},
+	{0x6666, "Decision Computer International Co.", {
+	}},
+	{0x8008, "Quancm Electronic GmbH", {
+		{0x0010,"WDOG1 [PCI-Watchdog 1]"},
+		{0x0011,"PWDOG2 [Watchdog2/PCI]"},
+	}},
+	{0x8086, "Intel Corporation", {
+		{0x0482,"82375EB"},
+		{0x0483,"82424ZX [Saturn]"},
+		{0x0484,"82378IB [SIO ISA Bridge]"},
+		{0x0486,"82430ZX [Aries]"},
+		{0x04a3,"82434LX [Mercury/Neptune]"},
+		{0x0960,"80960RP [i960 RP Microprocessor/Bridge]"},
+		{0x1221,"82092AA_0"},
+		{0x1222,"82092AA_1"},
+		{0x1223,"SAA7116"},
+		{0x1226,"82596"},
+		{0x1227,"82865"},
+		{0x1228,"82556"},
+		{0x1229,"82557"},
+		{0x122d,"430FX - 82437FX TSC [Triton I]"},
+		{0x122e,"82371FB PIIX ISA [Triton I]"},
+		{0x1230,"82371FB PIIX IDE [Triton I]"},
+		{0x1234,"430MX - 82371MX MPIIX [430MX PCIset - 82371MX Mobile PCI I/O IDE Xcelerator (MPIIX)]"},
+		{0x1235,"430MX - 82437MX MTSC [430MX PCIset - 82437MX Mobile System Controller (MTSC) and 82438MX Mobile Data Path (MTDP)]"},
+		{0x1237,"440FX - 82441FX PMC [Natoma]"},
+		{0x124b,"82380FB"},
+		{0x1250,"430HX - 82439HX TXC [Triton II]"},
+		{0x1960,"80960RP [i960RP Microprocessor]"},
+		{0x7000,"82371SB PIIX3 ISA [Natoma/Triton II]"},
+		{0x7010,"82371SB PIIX3 IDE [Natoma/Triton II]"},
+		{0x7020,"82371SB PIIX3 USB [Natoma/Triton II]"},
+		{0x7030,"430VX - 82437VX TVX [Triton VX]"},
+		{0x7100,"430TX - 82439TX MTXC"},
+		{0x7110,"82371AB PIIX4 ISA"},
+		{0x7111,"82371AB PIIX4 IDE"},
+		{0x7112,"82371AB PIIX4 USB"},
+		{0x7113,"82371AB PIIX4 ACPI"},
+		{0x7180,"440LX - 82443LX PAC Host"},
+		{0x7181,"440LX - 82443LX PAC AGP"},
+		{0x7190,"440BX - 82443BX Host"},
+		{0x7191,"440BX - 82443BX AGP"},
+		{0x7192,"440BX - 82443BX (AGP disabled)"},
+		{0x7800,"i740"},
+		{0x84c4,"82450KX [Orion]"},
+		{0x84c5,"82450GX [Orion]"},
+	}},
+	{0x8800, "Trigem Computer Inc.", {
+	}},
+	{0x8888, "Silicon Magic", {
+	}},
+	{0x8e0e, "Computone Corporation", {
+	}},
+	{0x8e2e, "KTI", {
+		{0x3000,"ET32P2"},
+	}},
+	{0x9004, "Adaptec", {
+		{0x1078,"AIC-7810"},
+		{0x5078,"AIC-7850"},
+		{0x5178,"7851"},
+		{0x5278,"7852"},
+		{0x5575,"2930"},
+		{0x5578,"AIC-7855"},
+		{0x5800,"AIC-5800"},
+		{0x6075,"AIC-1480"},
+		{0x6078,"AIC-7860"},
+		{0x6178,"AIC-7861"},
+		{0x6278,"AIC-7860"},
+		{0x6378,"AIC-7860"},
+		{0x7078,"AIC-7870"},
+		{0x7178,"AIC-7871"},
+		{0x7278,"AIC-7872"},
+		{0x7378,"AIC-7873"},
+		{0x7478,"AIC-7874 [AHA-2944]"},
+		{0x7578,"7875"},
+		{0x7678,"7876"},
+		{0x7895,"AIC-7895"},
+		{0x8078,"AIC-7880U"},
+		{0x8178,"AIC-7881U"},
+		{0x8278,"AIC-7882U"},
+		{0x8378,"AIC-7883U"},
+		{0x8478,"AIC-7884U"},
+		{0x8578,"7885"},
+		{0x8678,"7886"},
+		{0x8b78,"ABA-1030"},
+	}},
+	{0x907f, "Atronics", {
+		{0x2015,"IDE-2015PL"},
+	}},
+	{0x9412, "Holtek", {
+		{0x6565,"6565"},
+	}},
+	{0xa200, "NEC Corporation", {
+	}},
+	{0xa259, "Hewlett Packard", {
+	}},
+	{0xa25b, "Hewlett Packard GmbH PL24-MKT", {
+	}},
+	{0xa304, "Sony", {
+	}},
+	{0xa727, "3Com Corporation", {
+	}},
+	{0xaa42, "Scitex Digital Video", {
+	}},
+	{0xb1b3, "Shiva Europe Limited", {
+	}},
+	{0xc001, "TSI Telsys", {
+	}},
+	{0xc0a9, "Micron/Crucial Technology", {
+	}},
+	{0xc0de, "Motorola", {
+	}},
+	{0xc0fe, "Motion Engineering, Inc.", {
+	}},
+	{0xcafe, "Chrysalis-ITS", {
+	}},
+	{0xd4d4, "Dy4 Systems Inc", {
+	}},
+	{0xe159, "Tiger Jet Network Inc.", {
+		{0x0001,"300"},
+	}},
+	{0xecc0, "Echo Corporation", {
+	}},
+	{0xedd8, "ARK Logic Inc", {
+		{0xa091,"1000PV [Stingray]"},
+		{0xa099,"2000PV [Stingray]"},
+		{0xa0a1,"2000MT"},
+		{0xa0a9,"2000MI"},
+	}},
+};
+
--- /dev/null
+++ b/os/pc/pcmciamodem.c
@@ -1,0 +1,75 @@
+#include "u.h"
+#include "../port/lib.h"
+#include "mem.h"
+#include "dat.h"
+#include "fns.h"
+#include "io.h"
+#include "ureg.h"
+#include "../port/error.h"
+
+/*
+ *  PCMCIA modem.
+ *  By default, this will set it up with the port and irq of
+ *  COM2 unless a serialx=type=com line is found in plan9.ini.
+ *  The assumption is that a laptop with a pcmcia will have only
+ *  one com port.
+ */
+
+enum {
+	Maxcard=	8,
+};
+
+static char* modems[] = {
+	"IBM 33.6 Data/Fax/Voice Modem",
+	"CM-56G",			/* Xircom CreditCard Modem 56 - GlobalACCESS */
+	"KeepInTouch",
+	"CEM56",
+	"MONTANA V.34 FAX/MODEM",	/* Motorola */
+	"REM10",
+	"GSM/GPRS",
+	"AirCard 555",
+	"Gold Card Global",		/* Psion V90 Gold card */
+	"Merlin UMTS Modem",		/* Novatel card */
+	0,
+};
+
+void
+pcmciamodemlink(void)
+{
+	ISAConf isa;
+	int i, j, slot, com2used, usingcom2;
+
+	i = 0;
+	com2used = 0;
+	for(j = 0; modems[j]; j++){
+		memset(&isa, 0, sizeof(isa));
+
+		/* look for a configuration line */
+		for(; i < Maxcard; i++){
+			if(isaconfig("serial", i, &isa))
+			if(cistrcmp(isa.type, "com") == 0)
+				break;
+			memset(&isa, 0, sizeof(isa));
+		}
+
+		usingcom2 = 0;
+		if (isa.irq == 0 && isa.port == 0) {
+			if (com2used == 0) {
+				/* default is COM2 */
+				isa.irq = 3;
+				isa.port = 0x2F8;
+				usingcom2 = 1;
+			} else
+				break;
+		}
+		slot = pcmspecial(modems[j], &isa);
+		if(slot >= 0){
+			if(usingcom2)
+				com2used = 1;
+			if(ioalloc(isa.port, 8, 0, modems[j]) < 0)
+				print("%s port %lux already in use\n", modems[j], isa.port);
+			print("%s in pcmcia slot %d port 0x%lux irq %d\n",
+				modems[j], slot, isa.port, isa.irq);
+		}
+	}
+}	
--- /dev/null
+++ b/os/pc/piix4smbus.c
@@ -1,0 +1,213 @@
+#include	"u.h"
+#include	"../port/lib.h"
+#include	"mem.h"
+#include	"dat.h"
+#include	"fns.h"
+#include	"io.h"
+
+//
+//	SMBus support for the PIIX4
+//
+enum
+{
+	IntelVendID=	0x8086,
+	Piix4PMID=	0x7113,		/* PIIX4 power management function */
+
+	// SMBus configuration registers (function 3)
+	SMBbase=	0x90,		// 4 byte base address (bit 0 == 1, bit 3:1 == 0)
+	SMBconfig=	0xd2,
+	SMBintrselect=	(7<<1),
+	SMIenable=	(0<<1),		//  interrupts sent to SMI#
+	IRQ9enable=	(4<<1),		//  intettupts sent to IRQ9
+	SMBenable=	(1<<0),		//  1 enables
+
+	// SMBus IO space registers
+	Hoststatus=	0x0,		//  (writing 1 bits reset the interrupt bits)
+	Failed=		(1<<4),	 	//  transaction terminated by KILL
+	Bus_error=	(1<<3),		//  transactio collision
+	Dev_error=	(1<<2),		//  device error interrupt
+	Host_complete=	(1<<1),		//  host command completion interrupt 
+	Host_busy=	(1<<0),		//
+	Slavestatus=	0x1,		//  (writing 1 bits reset)
+	Alert_sts=	(1<<5),		//  someone asserted SMBALERT#
+	Shdw2_sts=	(1<<4),		//  slave accessed shadow 2 port
+	Shdw1_sts=	(1<<3),		//  slave accessed shadow 1 port
+	Slv_sts=	(1<<2),		//  slave accessed shadow 1 port
+	Slv_bsy=	(1<<0),
+	Hostcontrol=	0x2,
+	Start=		(1<<6),		//  start execution
+	Cmd_prot=	(7<<2),		//  command protocol mask
+	Quick=		(0<<2),		//   address only
+	Byte=		(1<<2),		//   address + cmd
+	ByteData=	(2<<2),		//   address + cmd + data
+	WordData=	(3<<2),		//   address + cmd + data + data
+	Kill=		(1<<1),		//  abort in progress command
+	Ienable=	(1<<0),		//  enable completion interrupts
+	Hostcommand=	0x3,
+	Hostaddress=	0x4,
+	AddressMask=	(0x7f<<1),	//  target address
+	Read=		(1<<0),		//  1 == read, 0 == write
+	Hostdata0=	0x5,
+	Hostdata1=	0x6,
+	Blockdata=	0x7,
+	Slavecontrol=	0x8,
+	Alert_en=	(1<<3),		//  enable inter on SMBALERT#
+	Shdw2_en=	(1<<2),		//  enable inter on external shadow 2 access
+	Shdw1_en=	(1<<1),		//  enable inter on external shadow 1 access
+	Slv_en=		(1<<0),		//  enable inter on access of host ctlr slave port
+	Shadowcommand=	0x9,
+	Slaveevent=	0xa,
+	Slavedata=	0xc,
+};
+
+static struct
+{
+	int	rw;
+	int	cmd;
+	int	len;
+	int	proto;
+} proto[] =
+{
+	[SMBquick]	{ 0,	0,	0,	Quick },
+	[SMBsend]	{ 0,	1,	0,	Byte },
+	[SMBbytewrite]	{ 0,	1,	1,	ByteData },
+	[SMBwordwrite]	{ 0,	1,	2,	WordData },
+	[SMBrecv]	{ Read,	0,	1, 	Byte },
+	[SMBbyteread]	{ Read,	1,	1,	ByteData },
+	[SMBwordread]	{ Read,	1,	2,	WordData },
+};
+
+static void
+transact(SMBus *s, int type, int addr, int cmd, uchar *data)
+{
+	int tries, status;
+	char err[256];
+
+	if(type < 0 || type > nelem(proto))
+		panic("piix4smbus: illegal transaction type %d", type);
+
+	if(waserror()){
+		qunlock(s);
+		nexterror();
+	}
+	qlock(s);
+
+	// wait a while for the host interface to be available
+	for(tries = 0; tries < 1000000; tries++){
+		if((inb(s->base+Hoststatus) & Host_busy) == 0)
+			break;
+		sched();
+	}
+	if(tries >= 1000000){
+		// try aborting current transaction
+		outb(s->base+Hostcontrol, Kill);
+		for(tries = 0; tries < 1000000; tries++){
+			if((inb(s->base+Hoststatus) & Host_busy) == 0)
+				break;
+			sched();
+		}
+		if(tries >= 1000000){
+			snprint(err, sizeof(err), "SMBus jammed: %2.2ux", inb(s->base+Hoststatus));
+			error(err);
+		}
+	}
+
+	// set up for transaction
+	outb(s->base+Hostaddress, (addr<<1)|proto[type].rw);
+	if(proto[type].cmd)
+		outb(s->base+Hostcommand, cmd);
+	if(proto[type].rw != Read){
+		switch(proto[type].len){
+		case 2:
+			outb(s->base+Hostdata1, data[1]);
+			// fall through
+		case 1:
+			outb(s->base+Hostdata0, data[0]);
+			break;
+		}
+	}
+	 
+
+	// reset the completion/error bits and start transaction
+	outb(s->base+Hoststatus, Failed|Bus_error|Dev_error|Host_complete);
+	outb(s->base+Hostcontrol, Start|proto[type].proto);
+
+	// wait for completion
+	status = 0;
+	for(tries = 0; tries < 1000000; tries++){
+		status = inb(s->base+Hoststatus);
+		if(status & (Failed|Bus_error|Dev_error|Host_complete))
+			break;
+		sched();
+	}
+	if((status & Host_complete) == 0){
+		snprint(err, sizeof(err), "SMBus request failed: %2.2ux", status);
+		error(err);
+	}
+
+	// get results
+	if(proto[type].rw == Read){
+		switch(proto[type].len){
+		case 2:
+			data[1] = inb(s->base+Hostdata1);
+			// fall through
+		case 1:
+			data[0] = inb(s->base+Hostdata0);
+			break;
+		}
+	}
+	qunlock(s);
+	poperror();
+}
+
+static SMBus smbusproto =
+{
+	.transact = transact,
+};
+
+//
+//  return 0 if this is a piix4 with an smbus interface
+//
+SMBus*
+piix4smbus(void)
+{
+	Pcidev *p;
+	static SMBus *s;
+
+	if(s != nil)
+		return s;
+
+	p = pcimatch(nil, IntelVendID, Piix4PMID);
+	if(p == nil)
+		return nil;
+
+	s = smalloc(sizeof(*s));	
+	memmove(s, &smbusproto, sizeof(*s));
+	s->arg = p;
+
+	// disable the smbus
+	pcicfgw8(p, SMBconfig, IRQ9enable|0);
+
+	// see if bios gave us a viable port space
+	s->base = pcicfgr32(p, SMBbase) & ~1;
+print("SMB base from bios is 0x%lux\n", s->base);
+	if(ioalloc(s->base, 0xd, 0, "piix4smbus") < 0){
+		s->base = ioalloc(-1, 0xd, 2, "piix4smbus");
+		if(s->base < 0){
+			free(s);
+			print("piix4smbus: can't allocate io port\n");
+			return nil;
+		}
+print("SMB base ialloc is 0x%lux\n", s->base);
+		pcicfgw32(p, SMBbase, s->base|1);
+	}
+
+	// disable SMBus interrupts, abort any transaction in progress
+	outb(s->base+Hostcontrol, Kill);
+	outb(s->base+Slavecontrol, 0);
+
+	// enable the smbus
+	pcicfgw8(p, SMBconfig, IRQ9enable|SMBenable);
+
+	return s;
+}
--- /dev/null
+++ b/os/pc/pix
@@ -1,0 +1,156 @@
+dev
+	root
+	cons
+	arch
+	env
+	mnt
+	pipe
+	prog
+	rtc
+	srv
+	dup
+	ssl
+	cap
+
+#	draw	screen vga vgax cga
+#	pointer
+#	vga
+
+	ip	bootp ip ipv6 ipaux iproute arp netlog ptclbsum386 iprouter plan9 nullmedium pktmedium
+	ether		netif netaux
+
+	sd
+	ds
+	uart
+	floppy	dma
+	tinyfs
+#	mouse
+#	dbg	x86break
+
+ip
+	il
+	tcp
+	udp
+	rudp
+	gre
+	ipifc
+	icmp
+	icmp6
+	ipmux
+lib
+	interp
+	keyring
+	sec
+	mp
+#	draw 
+#	memlayer
+#	memdraw
+#	tk
+	math
+	kern
+
+link
+#	ps2mouse
+	ether82557	pci
+	ethermedium
+	loopbackmedium
+	netdevmedium
+
+misc
+#	vgaclgd542x
+#	vgas3	+cur vgasavage
+#	cga
+	sdata	pci sdscsi
+
+	uarti8250
+
+mod
+	sys
+#	draw
+#	tk
+	keyring
+	crypt
+	ipints
+
+	math
+
+init
+	soeinit	# it will do
+
+code
+	int kernel_pool_pcnt = 10;
+	int main_pool_pcnt = 40;
+	int heap_pool_pcnt = 20;
+	int image_pool_pcnt = 0;
+	int cflag=0;
+	int swcursor=0;
+	int consoleprint=0;
+	void screeninit(void){}
+
+port
+	alarm
+	alloc
+	allocb
+	chan
+	dev
+	dial
+	dis
+	discall
+	exception
+	exportfs
+	inferno
+	latin1
+	nocache
+	nodynld
+	parse
+	pgrp
+	print
+	proc
+	qio
+	qlock
+	random
+	sysfile
+	taslock
+	xalloc
+
+root
+	/chan
+	/dev
+	/dis
+	/env
+	/fd	/
+	/n
+	/n/remote
+	/net
+	/nvfs
+	/prog
+	/dis/lib
+	/dis/svc
+	/dis/wm
+	/osinit.dis
+	/dis/sh.dis
+	/dis/ls.dis
+	/dis/cat.dis
+	/dis/bind.dis
+	/dis/mount.dis
+	/dis/pwd.dis
+	/dis/echo.dis
+	/dis/cd.dis
+	/dis/lib/bufio.dis
+	/dis/lib/string.dis
+	/dis/lib/readdir.dis
+	/dis/lib/workdir.dis
+	/dis/lib/daytime.dis
+	/dis/lib/auth.dis
+	/dis/lib/ssl.dis
+	/dis/lib/filepat.dis
+
+# kfs
+	/dis/disk/kfs.dis
+	/dis/lib/arg.dis
+	/dis/lib/daytime.dis
+	/dis/lib/string.dis
+	/dis/lib/styx.dis
+
+# auth
+	/nvfs/default	/usr/inferno/keyring/default
--- /dev/null
+++ b/os/pc/plan9.ini
@@ -1,0 +1,3 @@
+bootfile=fd0!ipc.gz
+bootfile=sdD0!cdboot!ipc.gz
+
--- /dev/null
+++ b/os/pc/ps2mouse.c
@@ -1,0 +1,84 @@
+#include "u.h"
+#include "../port/lib.h"
+#include "mem.h"
+#include "dat.h"
+#include "fns.h"
+#include "../port/error.h"
+#include "io.h"
+
+/*
+ *  mouse types
+ */
+enum
+{
+	Mouseother=	0,
+	Mouseserial=	1,
+	MousePS2=	2,
+};
+
+static int mousetype;
+
+/*
+ *  ps/2 mouse message is three bytes
+ *
+ *	byte 0 -	0 0 SDY SDX 1 M R L
+ *	byte 1 -	DX
+ *	byte 2 -	DY
+ *
+ *  shift & left button is the same as middle button
+ */
+static void
+ps2mouseputc(int c, int shift)
+{
+	static short msg[3];
+	static int nb;
+	static uchar b[] = {0, 1, 4, 5, 2, 3, 6, 7, 0, 1, 2, 5, 2, 3, 6, 7 };
+	int buttons, dx, dy;
+
+	/* 
+	 *  check byte 0 for consistency
+	 */
+	if(nb==0 && (c&0xc8)!=0x08)
+		return;
+
+	msg[nb] = c;
+	if(++nb == 3){
+		nb = 0;
+		if(msg[0] & 0x10)
+			msg[1] |= 0xFF00;
+		if(msg[0] & 0x20)
+			msg[2] |= 0xFF00;
+
+		buttons = b[(msg[0]&7) | (shift ? 8 : 0)];
+		dx = msg[1];
+		dy = -msg[2];
+		mousetrack(buttons, dx, dy, 1);
+	}
+	return;
+}
+
+/*
+ *  set up a ps2 mouse
+ */
+static void
+ps2mouse(void)
+{
+	if(mousetype == MousePS2)
+		return;
+
+	i8042auxenable(ps2mouseputc);
+	/* make mouse streaming, enabled */
+	i8042auxcmd(0xEA);
+	i8042auxcmd(0xF4);
+
+	mousetype = MousePS2;
+}
+
+void
+ps2mouselink(void)
+{
+	/*
+	 * hack
+	 */
+	ps2mouse();
+}
--- /dev/null
+++ b/os/pc/ptclbsum386.s
@@ -1,0 +1,126 @@
+TEXT ptclbsum(SB), $0
+	MOVL	addr+0(FP), SI
+	MOVL	len+4(FP), CX
+
+	XORL	AX, AX			/* sum */
+
+	TESTL	$1, SI			/* byte aligned? */
+	MOVL	SI, DI
+	JEQ	_2align
+
+	DECL	CX
+	JLT	_return
+
+	MOVB	0x00(SI), AH
+	INCL	SI
+
+_2align:
+	TESTL	$2, SI			/* word aligned? */
+	JEQ	_32loop
+
+	CMPL	CX, $2			/* less than 2 bytes? */
+	JLT	_1dreg
+	SUBL	$2, CX
+
+	XORL	BX, BX
+	MOVW	0x00(SI), BX
+	ADDL	BX, AX
+	ADCL	$0, AX
+	LEAL	2(SI), SI
+
+_32loop:
+	CMPL	CX, $0x20
+	JLT	_8loop
+
+	MOVL	CX, BP
+	SHRL	$5, BP
+	ANDL	$0x1F, CX
+
+_32loopx:
+	MOVL	0x00(SI), BX
+	MOVL	0x1C(SI), DX
+	ADCL	BX, AX
+	MOVL	0x04(SI), BX
+	ADCL	DX, AX
+	MOVL	0x10(SI), DX
+	ADCL	BX, AX
+	MOVL	0x08(SI), BX
+	ADCL	DX, AX
+	MOVL	0x14(SI), DX
+	ADCL	BX, AX
+	MOVL	0x0C(SI), BX
+	ADCL	DX, AX
+	MOVL	0x18(SI), DX
+	ADCL	BX, AX
+	LEAL	0x20(SI), SI
+	ADCL	DX, AX
+
+	DECL	BP
+	JNE	_32loopx
+
+	ADCL	$0, AX
+
+_8loop:
+	CMPL	CX, $0x08
+	JLT	_2loop
+
+	MOVL	CX, BP
+	SHRL	$3, BP
+	ANDL	$0x07, CX
+
+_8loopx:
+	MOVL	0x00(SI), BX
+	ADCL	BX, AX
+	MOVL	0x04(SI), DX
+	ADCL	DX, AX
+
+	LEAL	0x08(SI), SI
+	DECL	BP
+	JNE	_8loopx
+
+	ADCL	$0, AX
+
+_2loop:
+	CMPL	CX, $0x02
+	JLT	_1dreg
+
+	MOVL	CX, BP
+	SHRL	$1, BP
+	ANDL	$0x01, CX
+
+_2loopx:
+	MOVWLZX	0x00(SI), BX
+	ADCL	BX, AX
+
+	LEAL	0x02(SI), SI
+	DECL	BP
+	JNE	_2loopx
+
+	ADCL	$0, AX
+
+_1dreg:
+	TESTL	$1, CX			/* 1 byte left? */
+	JEQ	_fold
+
+	XORL	BX, BX
+	MOVB	0x00(SI), BX
+	ADDL	BX, AX
+	ADCL	$0, AX
+
+_fold:
+	MOVL	AX, BX
+	SHRL	$16, BX
+	JEQ	_swab
+
+	ANDL	$0xFFFF, AX
+	ADDL	BX, AX
+	JMP	_fold
+
+_swab:
+	TESTL	$1, addr+0(FP)
+	/*TESTL	$1, DI*/
+	JNE	_return
+	XCHGB	AH, AL
+
+_return:
+	RET
--- /dev/null
+++ b/os/pc/screen.c
@@ -1,0 +1,410 @@
+#include "u.h"
+#include "../port/lib.h"
+#include "mem.h"
+#include "dat.h"
+#include "fns.h"
+#include "io.h"
+#include "ureg.h"
+#include "../port/error.h"
+
+#include <draw.h>
+#include <memdraw.h>
+#include <cursor.h>
+#include "screen.h"
+
+#define RGB2K(r,g,b)	((156763*(r)+307758*(g)+59769*(b))>>19)
+
+Point ZP = {0, 0};
+
+Rectangle physgscreenr;
+Cursorinfo cursor;	/* TO DO */
+
+Memdata gscreendata;
+Memimage *gscreen;
+
+VGAscr vgascreen[1];
+
+Cursor	arrow = {
+	{ -1, -1 },
+	{ 0xFF, 0xFF, 0x80, 0x01, 0x80, 0x02, 0x80, 0x0C, 
+	  0x80, 0x10, 0x80, 0x10, 0x80, 0x08, 0x80, 0x04, 
+	  0x80, 0x02, 0x80, 0x01, 0x80, 0x02, 0x8C, 0x04, 
+	  0x92, 0x08, 0x91, 0x10, 0xA0, 0xA0, 0xC0, 0x40, 
+	},
+	{ 0x00, 0x00, 0x7F, 0xFE, 0x7F, 0xFC, 0x7F, 0xF0, 
+	  0x7F, 0xE0, 0x7F, 0xE0, 0x7F, 0xF0, 0x7F, 0xF8, 
+	  0x7F, 0xFC, 0x7F, 0xFE, 0x7F, 0xFC, 0x73, 0xF8, 
+	  0x61, 0xF0, 0x60, 0xE0, 0x40, 0x40, 0x00, 0x00, 
+	},
+};
+
+int
+screensize(int x, int y, int z, ulong chan)
+{
+	VGAscr *scr;
+
+	memimageinit();
+	scr = &vgascreen[0];
+
+	/*
+	 * BUG: need to check if any xalloc'ed memory needs to
+	 * be given back if aperture is set.
+	 */
+	if(scr->aperture == 0){
+		int width = (x*z)/BI2WD;
+
+		gscreendata.bdata = xalloc(width*BY2WD*y);
+		if(gscreendata.bdata == 0)
+			error("screensize: vga soft memory");
+/*		memset(gscreendata.bdata, 0x72, width*BY2WD*y);	/* not really black */
+		scr->useflush = 1;
+		scr->aperture = VGAMEM();
+		scr->apsize = 1<<16;
+	}
+	else
+		gscreendata.bdata = KADDR(scr->aperture);
+
+	if(gscreen)
+		freememimage(gscreen);
+
+	gscreen = allocmemimaged(Rect(0,0,x,y), chan, &gscreendata);
+	vgaimageinit(chan);
+	if(gscreen == nil)
+		return -1;
+
+	if(scr->dev && scr->dev->flush)
+		scr->useflush = 1;
+
+	scr->palettedepth = 6;	/* default */
+	scr->gscreendata = &gscreendata;
+	scr->memdefont = getmemdefont();
+	scr->gscreen = gscreen;
+
+	physgscreenr = gscreen->r;
+
+	drawcmap();
+	return 0;
+}
+
+int
+screenaperture(int size, int align)
+{
+	VGAscr *scr;
+	ulong aperture;
+
+	scr = &vgascreen[0];
+
+	if(size == 0){
+		if(scr->aperture && scr->isupamem)
+			upafree(scr->aperture, scr->apsize);
+		scr->aperture = 0;
+		scr->isupamem = 0;
+		return 0;
+	}
+	if(scr->dev && scr->dev->linear){
+		aperture = scr->dev->linear(scr, &size, &align);
+		if(aperture == 0)
+			return 1;
+	}else{
+		aperture = upamalloc(0, size, align);
+		if(aperture == 0)
+			return 1;
+
+		if(scr->aperture && scr->isupamem)
+			upafree(scr->aperture, scr->apsize);
+		scr->isupamem = 1;
+	}
+
+	scr->aperture = aperture;
+	scr->apsize = size;
+
+	return 0;
+}
+
+uchar*
+attachscreen(Rectangle* r, ulong* chan, int* d, int* width, int *softscreen)
+{
+	VGAscr *scr;
+
+	scr = &vgascreen[0];
+	if(scr->gscreen == nil || scr->gscreendata == nil)
+		return nil;
+
+	*r = scr->gscreen->clipr;
+	*chan = scr->gscreen->chan;
+	*d = scr->gscreen->depth;
+	*width = scr->gscreen->width;
+	*softscreen = scr->useflush;
+
+	return scr->gscreendata->bdata;
+}
+
+/*
+ * It would be fair to say that this doesn't work for >8-bit screens.
+ */
+void
+flushmemscreen(Rectangle r)
+{
+	VGAscr *scr;
+	uchar *sp, *disp, *sdisp, *edisp;
+	int y, len, incs, off, page;
+
+	scr = &vgascreen[0];
+	if(scr->dev && scr->dev->flush){
+		scr->dev->flush(scr, r);
+		return;
+	}
+	if(scr->gscreen == nil || scr->useflush == 0)
+		return;
+	if(scr->dev == nil || scr->dev->page == nil)
+		return;
+
+	if(rectclip(&r, scr->gscreen->r) == 0)
+		return;
+
+	incs = scr->gscreen->width * BY2WD;
+
+	switch(scr->gscreen->depth){
+	default:
+		len = 0;
+		panic("flushmemscreen: depth\n");
+		break;
+	case 8:
+		len = Dx(r);
+		break;
+	}
+	if(len < 1)
+		return;
+
+	off = r.min.y*scr->gscreen->width*BY2WD+(r.min.x*scr->gscreen->depth)/8;
+	page = off/scr->apsize;
+	off %= scr->apsize;
+	disp = KADDR(scr->aperture);
+	sdisp = disp+off;
+	edisp = disp+scr->apsize;
+
+	off = r.min.y*scr->gscreen->width*BY2WD+(r.min.x*scr->gscreen->depth)/8;
+
+	sp = scr->gscreendata->bdata + off;
+
+	scr->dev->page(scr, page);
+	for(y = r.min.y; y < r.max.y; y++) {
+		if(sdisp + incs < edisp) {
+			memmove(sdisp, sp, len);
+			sp += incs;
+			sdisp += incs;
+		}
+		else {
+			off = edisp - sdisp;
+			page++;
+			if(off <= len){
+				if(off > 0)
+					memmove(sdisp, sp, off);
+				scr->dev->page(scr, page);
+				if(len - off > 0)
+					memmove(disp, sp+off, len - off);
+			}
+			else {
+				memmove(sdisp, sp, len);
+				scr->dev->page(scr, page);
+			}
+			sp += incs;
+			sdisp += incs - scr->apsize;
+		}
+	}
+}
+
+void
+getcolor(ulong p, ulong* pr, ulong* pg, ulong* pb)
+{
+	VGAscr *scr;
+	ulong x;
+
+	scr = &vgascreen[0];
+	if(scr->gscreen == nil)
+		return;
+
+	switch(scr->gscreen->depth){
+	default:
+		x = 0x0F;
+		break;
+	case 8:
+		x = 0xFF;
+		break;
+	}
+	p &= x;
+
+	lock(&cursor);
+	*pr = scr->colormap[p][0];
+	*pg = scr->colormap[p][1];
+	*pb = scr->colormap[p][2];
+	unlock(&cursor);
+}
+
+int
+setpalette(ulong p, ulong r, ulong g, ulong b)
+{
+	VGAscr *scr;
+	int d;
+
+	scr = &vgascreen[0];
+	d = scr->palettedepth;
+
+	lock(&cursor);
+	scr->colormap[p][0] = r;
+	scr->colormap[p][1] = g;
+	scr->colormap[p][2] = b;
+	vgao(PaddrW, p);
+	vgao(Pdata, r>>(32-d));
+	vgao(Pdata, g>>(32-d));
+	vgao(Pdata, b>>(32-d));
+	unlock(&cursor);
+
+	return ~0;
+}
+
+/*
+ * On some video cards (e.g. Mach64), the palette is used as the 
+ * DAC registers for >8-bit modes.  We don't want to set them when the user
+ * is trying to set a colormap and the card is in one of these modes.
+ */
+int
+setcolor(ulong p, ulong r, ulong g, ulong b)
+{
+	VGAscr *scr;
+	int x;
+
+	scr = &vgascreen[0];
+	if(scr->gscreen == nil)
+		return 0;
+
+	switch(scr->gscreen->depth){
+	case 1:
+	case 2:
+	case 4:
+		x = 0x0F;
+		break;
+	case 8:
+		x = 0xFF;
+		break;
+	default:
+		return 0;
+	}
+	p &= x;
+
+	return setpalette(p, r, g, b);
+}
+
+int
+cursoron(int dolock)
+{
+	VGAscr *scr;
+	int v;
+
+	scr = &vgascreen[0];
+	if(scr->cur == nil || scr->cur->move == nil)
+		return 0;
+
+	if(dolock)
+		lock(&cursor);
+	v = scr->cur->move(scr, mousexy());
+	if(dolock)
+		unlock(&cursor);
+
+	return v;
+}
+
+void
+cursoroff(int)
+{
+}
+
+void
+setcursor(Cursor* curs)
+{
+	VGAscr *scr;
+
+	scr = &vgascreen[0];
+	if(scr->cur == nil || scr->cur->load == nil)
+		return;
+
+	scr->cur->load(scr, curs);
+}
+
+int hwaccel = 1;
+int hwblank = 0;	/* turned on by drivers that are known good */
+int panning = 0;
+
+int
+hwdraw(Memdrawparam *par)
+{
+	VGAscr *scr;
+	Memimage *dst, *src;
+	int m;
+
+	if(hwaccel == 0)
+		return 0;
+
+	dst = par->dst;
+	scr = &vgascreen[0];
+	if(dst == nil || dst->data == nil)
+		return 0;
+
+	if(dst->data->bdata != gscreendata.bdata)
+		return 0;
+
+	if(scr->fill==nil && scr->scroll==nil)
+		return 0;
+
+	/*
+	 * If we have an opaque mask and source is one opaque
+	 * pixel we can convert to the destination format and just
+	 * replicate with memset.
+	 */
+	m = Simplesrc|Simplemask|Fullmask;
+	if(scr->fill
+	&& (par->state&m)==m
+	&& ((par->srgba&0xFF) == 0xFF)
+	&& (par->op&S) == S)
+		return scr->fill(scr, par->r, par->sdval);
+
+	/*
+	 * If no source alpha, an opaque mask, we can just copy the
+	 * source onto the destination.  If the channels are the same and
+	 * the source is not replicated, memmove suffices.
+	 */
+	m = Simplemask|Fullmask;
+	src = par->src;
+	if(scr->scroll
+	&& src->data->bdata==dst->data->bdata
+	&& !(src->flags&Falpha)
+	&& (par->state&m)==m
+	&& (par->op&S) == S)
+		return scr->scroll(scr, par->r, par->sr);
+
+	return 0;	
+}
+
+void
+blankscreen(int blank)
+{
+	VGAscr *scr;
+
+	scr = &vgascreen[0];
+	if(hwblank){
+		if(scr->blank)
+			scr->blank(scr, blank);
+		else
+			vgablank(scr, blank);
+	}
+}
+
+void
+cursorenable(void)
+{
+}
+
+void
+cursordisable(void)
+{
+}
--- /dev/null
+++ b/os/pc/screen.h
@@ -1,0 +1,173 @@
+typedef struct Cursor Cursor;
+
+enum {
+	CURSWID = 16,
+	CURSHGT = 16,
+};
+
+struct	Cursor
+{
+	Point	offset;
+	uchar	clr[CURSWID/BI2BY*CURSHGT];
+	uchar	set[CURSWID/BI2BY*CURSHGT];
+};
+typedef struct Cursorinfo Cursorinfo;
+struct Cursorinfo {
+	Cursor;
+	Lock;
+};
+
+/* devmouse.c */
+extern void mousetrack(int, int, int, int);
+extern Point mousexy(void);
+
+extern void mouseaccelerate(int);
+extern int m3mouseputc(Queue*, int);
+extern int m5mouseputc(Queue*, int);
+extern int mouseputc(Queue*, int);
+
+extern Cursorinfo cursor;
+extern Cursor arrow;
+
+/*
+ * Generic VGA registers.
+ */
+enum {
+	MiscW		= 0x03C2,	/* Miscellaneous Output (W) */
+	MiscR		= 0x03CC,	/* Miscellaneous Output (R) */
+	Status0		= 0x03C2,	/* Input status 0 (R) */
+	Status1		= 0x03DA,	/* Input Status 1 (R) */
+	FeatureR	= 0x03CA,	/* Feature Control (R) */
+	FeatureW	= 0x03DA,	/* Feature Control (W) */
+
+	Seqx		= 0x03C4,	/* Sequencer Index, Data at Seqx+1 */
+	Crtx		= 0x03D4,	/* CRT Controller Index, Data at Crtx+1 */
+	Grx		= 0x03CE,	/* Graphics Controller Index, Data at Grx+1 */
+	Attrx		= 0x03C0,	/* Attribute Controller Index and Data */
+
+	PaddrW		= 0x03C8,	/* Palette Address Register, write */
+	Pdata		= 0x03C9,	/* Palette Data Register */
+	Pixmask		= 0x03C6,	/* Pixel Mask Register */
+	PaddrR		= 0x03C7,	/* Palette Address Register, read */
+	Pstatus		= 0x03C7,	/* DAC Status (RO) */
+
+	Pcolours	= 256,		/* Palette */
+	Pred		= 0,
+	Pgreen		= 1,
+	Pblue		= 2,
+
+	Pblack		= 0x00,
+	Pwhite		= 0xFF,
+};
+
+#define VGAMEM()	0xA0000
+#define vgai(port)		inb(port)
+#define vgao(port, data)	outb(port, data)
+
+extern int vgaxi(long, uchar);
+extern int vgaxo(long, uchar, uchar);
+
+/*
+ */
+typedef struct VGAdev VGAdev;
+typedef struct VGAcur VGAcur;
+typedef struct VGAscr VGAscr;
+
+struct VGAdev {
+	char*	name;
+
+	void	(*enable)(VGAscr*);
+	void	(*disable)(VGAscr*);
+	void	(*page)(VGAscr*, int);
+	ulong	(*linear)(VGAscr*, int*, int*);
+	void	(*drawinit)(VGAscr*);
+	int	(*fill)(VGAscr*, Rectangle, ulong);
+	void	(*ovlctl)(VGAscr*, Chan*, void*, int);
+	int	(*ovlwrite)(VGAscr*, void*, int, vlong);
+	void (*flush)(VGAscr*, Rectangle);
+};
+
+struct VGAcur {
+	char*	name;
+
+	void	(*enable)(VGAscr*);
+	void	(*disable)(VGAscr*);
+	void	(*load)(VGAscr*, Cursor*);
+	int	(*move)(VGAscr*, Point);
+
+	int	doespanning;
+};
+
+/*
+ */
+struct VGAscr {
+	Lock	devlock;
+	VGAdev*	dev;
+
+	VGAcur*	cur;
+	ulong	storage;
+	Cursor;
+
+	int	useflush;
+
+	ulong	aperture;			/* physical address */
+	int	isupamem;
+	int	apsize;
+
+	ulong	io;				/* device specific registers */
+
+	ulong	colormap[Pcolours][3];
+	int	palettedepth;
+
+	ulong	*mmio;
+	Memimage* gscreen;
+	Memdata* gscreendata;
+	Memsubfont* memdefont;
+
+	int	(*fill)(VGAscr*, Rectangle, ulong);
+	int	(*scroll)(VGAscr*, Rectangle, Rectangle);
+	void	(*blank)(VGAscr*, int);
+	ulong	id;	/* internal identifier for driver use */
+	int isblank;
+	int overlayinit;
+};
+
+extern VGAscr vgascreen[];
+
+enum {
+	Backgnd		= 0,	/* black */
+};
+
+/* mouse.c */
+extern void mousectl(Cmdbuf*);
+
+/* screen.c */
+extern int		hwaccel;	/* use hw acceleration; default on */
+extern int		hwblank;	/* use hw blanking; default on */
+extern int		panning;	/* use virtual screen panning; default off */
+extern void addvgaseg(char*, ulong, ulong);
+extern uchar* attachscreen(Rectangle*, ulong*, int*, int*, int*);
+extern void	flushmemscreen(Rectangle);
+extern int	cursoron(int);
+extern void	cursoroff(int);
+extern void	setcursor(Cursor*);
+extern int	screensize(int, int, int, ulong);
+extern int	screenaperture(int, int);
+extern Rectangle physgscreenr;	/* actual monitor size */
+extern void	blankscreen(int);
+
+/* devdraw.c */
+extern void	deletescreenimage(void);
+extern int		drawhasclients(void);
+extern ulong	blanktime;
+extern void	setscreenimageclipr(Rectangle);
+extern void	drawflush(void);
+extern int drawidletime(void);
+
+/* vga.c */
+extern void	vgascreenwin(VGAscr*);
+extern void	vgaimageinit(ulong);
+extern ulong	vgapcilinear(VGAscr*, int*, int*, int, int);
+
+extern void	drawblankscreen(int);
+extern void	vgablank(VGAscr*, int);
--- /dev/null
+++ b/os/pc/sd53c8xx.c
@@ -1,0 +1,2135 @@
+/*
+ * NCR/Symbios/LSI Logic 53c8xx driver for Plan 9
+ * Nigel Roles (nigel@9fs.org)
+ *
+ * 27/5/02	Fixed problems with transfers >= 256 * 512
+ *
+ * 13/3/01	Fixed microcode to support targets > 7
+ *
+ * 01/12/00	Removed previous comments. Fixed a small problem in
+ *			mismatch recovery for targets with synchronous offsets of >=16
+ *			connected to >=875s. Thanks, Jean.
+ *
+ * Known problems
+ *
+ * Read/write mismatch recovery may fail on 53c1010s. Really need to get a manual.
+ */
+
+#define MAXTARGET	16		/* can be 8 or 16 */
+
+#include "u.h"
+#include "../port/lib.h"
+#include "mem.h"
+#include "dat.h"
+#include "fns.h"
+#include "io.h"
+
+#include "../port/sd.h"
+extern SDifc sd53c8xxifc;
+
+/**********************************/
+/* Portable configuration macros  */
+/**********************************/
+
+//#define BOOTDEBUG
+//#define ASYNC_ONLY
+//#define	INTERNAL_SCLK
+//#define ALWAYS_DO_WDTR
+#define WMR_DEBUG
+
+/**********************************/
+/* CPU specific macros            */
+/**********************************/
+
+#define PRINTPREFIX "sd53c8xx: "
+
+#ifdef BOOTDEBUG
+
+#define KPRINT oprint
+#define IPRINT intrprint
+#define DEBUG(n) 1
+#define IFLUSH() iflush()
+
+#else
+
+#define KPRINT	if(0) print
+#define IPRINT	if(0) print
+#define DEBUG(n)	(0)
+#define IFLUSH()
+
+#endif /* BOOTDEBUG */
+
+/*******************************/
+/* General                     */
+/*******************************/
+
+#ifndef DMASEG
+#define DMASEG(x) PCIWADDR(x)
+#define legetl(x) (*(ulong*)(x))
+#define lesetl(x,v) (*(ulong*)(x) = (v))
+#define swabl(a,b,c)
+#else
+#endif /*DMASEG */
+#define DMASEG_TO_KADDR(x) KADDR((x)-PCIWINDOW)
+#define KPTR(x) ((x) == 0 ? 0 : DMASEG_TO_KADDR(x))
+
+#define MEGA 1000000L
+#ifdef INTERNAL_SCLK
+#define	SCLK (33 * MEGA)
+#else
+#define SCLK (40 * MEGA)
+#endif /* INTERNAL_SCLK */
+#define ULTRA_NOCLOCKDOUBLE_SCLK (80 * MEGA)
+
+#define MAXSYNCSCSIRATE (5 * MEGA)
+#define MAXFASTSYNCSCSIRATE (10 * MEGA)
+#define MAXULTRASYNCSCSIRATE (20 * MEGA)
+#define MAXULTRA2SYNCSCSIRATE (40 * MEGA)
+#define MAXASYNCCORERATE (25 * MEGA)
+#define MAXSYNCCORERATE (25 * MEGA)
+#define MAXFASTSYNCCORERATE (50 * MEGA)
+#define MAXULTRASYNCCORERATE (80 * MEGA)
+#define MAXULTRA2SYNCCORERATE (160 * MEGA)
+
+
+#define X_MSG	1
+#define X_MSG_SDTR 1
+#define X_MSG_WDTR 3
+
+struct na_patch {
+	unsigned lwoff;
+	unsigned char type;
+};
+
+typedef struct Ncr {
+	uchar scntl0;	/* 00 */
+	uchar scntl1;
+	uchar scntl2;
+	uchar scntl3;
+
+	uchar scid;	/* 04 */
+	uchar sxfer;
+	uchar sdid;
+	uchar gpreg;
+
+	uchar sfbr;	/* 08 */
+	uchar socl;
+	uchar ssid;
+	uchar sbcl;
+
+	uchar dstat;	/* 0c */
+	uchar sstat0;
+	uchar sstat1;
+	uchar sstat2;
+
+	uchar dsa[4];	/* 10 */
+
+	uchar istat;	/* 14 */
+	uchar istatpad[3];
+
+	uchar ctest0;	/* 18 */
+	uchar ctest1;
+	uchar ctest2;
+	uchar ctest3;
+
+	uchar temp[4];	/* 1c */
+
+	uchar dfifo;	/* 20 */
+	uchar ctest4;
+	uchar ctest5;
+	uchar ctest6;
+
+	uchar dbc[3];	/* 24 */
+	uchar dcmd;	/* 27 */
+
+	uchar dnad[4];	/* 28 */
+	uchar dsp[4];	/* 2c */
+	uchar dsps[4];	/* 30 */
+
+	uchar scratcha[4];	/* 34 */
+
+	uchar dmode;	/* 38 */
+	uchar dien;
+	uchar dwt;
+	uchar dcntl;
+
+	uchar adder[4];	/* 3c */
+
+	uchar sien0;	/* 40 */
+	uchar sien1;
+	uchar sist0;
+	uchar sist1;
+
+	uchar slpar;	/* 44 */
+	uchar slparpad0;
+	uchar macntl;
+	uchar gpcntl;
+
+	uchar stime0;	/* 48 */
+	uchar stime1;
+	uchar respid;
+	uchar respidpad0;
+
+	uchar stest0;	/* 4c */
+	uchar stest1;
+	uchar stest2;
+	uchar stest3;
+
+	uchar sidl;	/* 50 */
+	uchar sidlpad[3];
+
+	uchar sodl;	/* 54 */
+	uchar sodlpad[3];
+
+	uchar sbdl;	/* 58 */
+	uchar sbdlpad[3];
+
+	uchar scratchb[4];	/* 5c */
+} Ncr;
+
+typedef struct Movedata {
+	uchar dbc[4];
+	uchar pa[4];
+} Movedata;
+
+typedef enum NegoState {
+	NeitherDone, WideInit, WideResponse, WideDone,
+	SyncInit, SyncResponse, BothDone
+} NegoState;
+
+typedef enum State {
+	Allocated, Queued, Active, Done
+} State;
+
+typedef struct Dsa {
+	uchar stateb;
+	uchar result;
+	uchar dmablks;
+	uchar flag;	/* setbyte(state,3,...) */
+
+	union {
+		ulong dmancr;		/* For block transfer: NCR order (little-endian) */
+		uchar dmaaddr[4];
+	};
+
+	uchar target;			/* Target */
+	uchar pad0[3];
+
+	uchar lun;			/* Logical Unit Number */
+	uchar pad1[3];
+
+	uchar scntl3;
+	uchar sxfer;
+	uchar pad2[2];
+
+	uchar next[4];			/* chaining for SCRIPT (NCR byte order) */
+	struct Dsa *freechain;		/* chaining for freelist */
+	Rendez;
+	uchar scsi_id_buf[4];
+	Movedata msg_out_buf;
+	Movedata cmd_buf;
+	Movedata data_buf;
+	Movedata status_buf;
+	uchar msg_out[10];		/* enough to include SDTR */
+	uchar status;
+	int p9status;
+	uchar parityerror;
+} Dsa;
+
+typedef enum Feature {
+	BigFifo = 1,			/* 536 byte fifo */
+	BurstOpCodeFetch = 2,		/* burst fetch opcodes */
+	Prefetch = 4,			/* prefetch 8 longwords */
+	LocalRAM = 8,			/* 4K longwords of local RAM */
+	Differential = 16,		/* Differential support */
+	Wide = 32,			/* Wide capable */
+	Ultra = 64,			/* Ultra capable */
+	ClockDouble = 128,		/* Has clock doubler */
+	ClockQuad = 256,		/* Has clock quadrupler (same as Ultra2) */
+	Ultra2 = 256,
+} Feature;
+
+typedef enum Burst {
+	Burst2 = 0,
+	Burst4 = 1,
+	Burst8 = 2,
+	Burst16 = 3,
+	Burst32 = 4,
+	Burst64 = 5,
+	Burst128 = 6
+} Burst;
+
+typedef struct Variant {
+	ushort did;
+	uchar maxrid;			/* maximum allowed revision ID */
+	char *name;
+	Burst burst;			/* codings for max burst */
+	uchar maxsyncoff;		/* max synchronous offset */
+	uchar registers;		/* number of 32 bit registers */
+	unsigned feature;
+} Variant;
+
+static unsigned char cf2[] = { 6, 2, 3, 4, 6, 8, 12, 16 };
+#define NULTRA2SCF (sizeof(cf2)/sizeof(cf2[0]))
+#define NULTRASCF (NULTRA2SCF - 2)
+#define NSCF (NULTRASCF - 1)
+
+typedef struct Controller {
+	Lock;
+	struct {
+		uchar scntl3;
+		uchar stest2;
+	} bios;
+	uchar synctab[NULTRA2SCF - 1][8];/* table of legal tpfs */
+	NegoState s[MAXTARGET];
+	uchar scntl3[MAXTARGET];
+	uchar sxfer[MAXTARGET];
+	uchar cap[MAXTARGET];		/* capabilities byte from Identify */
+	ushort capvalid;		/* bit per target for validity of cap[] */
+	ushort wide;			/* bit per target set if wide negotiated */
+	ulong sclk;			/* clock speed of controller */
+	uchar clockmult;		/* set by synctabinit */
+	uchar ccf;			/* CCF bits */
+	uchar tpf;			/* best tpf value for this controller */
+	uchar feature;			/* requested features */
+	int running;			/* is the script processor running? */
+	int ssm;			/* single step mode */
+	Ncr *n;				/* pointer to registers */
+	Variant *v;			/* pointer to variant type */
+	ulong *script;			/* where the real script is */
+	ulong scriptpa;			/* where the real script is */
+	Pcidev* pcidev;
+	SDev*	sdev;
+
+	struct {
+		Lock;
+		uchar head[4];		/* head of free list (NCR byte order) */
+		Dsa	*tail;
+		Dsa	*freechain;
+	} dsalist;
+
+	QLock q[MAXTARGET];		/* queues for each target */
+} Controller;
+
+#define SYNCOFFMASK(c)		(((c)->v->maxsyncoff * 2) - 1)
+#define SSIDMASK(c)		(((c)->v->feature & Wide) ? 15 : 7)
+
+/* ISTAT */
+enum { Abrt = 0x80, Srst = 0x40, Sigp = 0x20, Sem = 0x10, Con = 0x08, Intf = 0x04, Sip = 0x02, Dip = 0x01 };
+
+/* DSTAT */
+enum { Dfe = 0x80, Mdpe = 0x40, Bf = 0x20, Abrted = 0x10, Ssi = 0x08, Sir = 0x04, Iid = 0x01 };
+
+/* SSTAT */
+enum { DataOut, DataIn, Cmd, Status, ReservedOut, ReservedIn, MessageOut, MessageIn };
+
+static void setmovedata(Movedata*, ulong, ulong);
+static void advancedata(Movedata*, long);
+static int bios_set_differential(Controller *c);
+
+static char *phase[] = {
+	"data out", "data in", "command", "status",
+	"reserved out", "reserved in", "message out", "message in"
+};
+
+#ifdef BOOTDEBUG
+#define DEBUGSIZE 10240
+char debugbuf[DEBUGSIZE];
+char *debuglast;
+
+static void
+intrprint(char *format, ...)
+{
+	if (debuglast == 0)
+		debuglast = debugbuf;
+	debuglast = vseprint(debuglast, debugbuf + (DEBUGSIZE - 1), format, (&format + 1));
+}
+
+static void
+iflush()
+{
+	int s;
+	char *endp;
+	s = splhi();
+	if (debuglast == 0)
+		debuglast = debugbuf;
+	if (debuglast == debugbuf) {
+		splx(s);
+		return;
+	}
+	endp = debuglast;
+	splx(s);
+	screenputs(debugbuf, endp - debugbuf);
+	s = splhi();
+	memmove(debugbuf, endp, debuglast - endp);
+	debuglast -= endp - debugbuf;
+	splx(s);
+}
+
+static void
+oprint(char *format, ...)
+{
+	int s;
+
+	iflush();
+	s = splhi();
+	if (debuglast == 0)
+		debuglast = debugbuf;
+	debuglast = vseprint(debuglast, debugbuf + (DEBUGSIZE - 1), format, (&format + 1));
+	splx(s);
+	iflush();	
+}
+#endif
+
+#include "sd53c8xx.i"
+
+static Dsa *
+dsaalloc(Controller *c, int target, int lun)
+{
+	Dsa *d;
+
+	ilock(&c->dsalist);
+	if ((d = c->dsalist.freechain) == 0) {
+		d = xalloc(sizeof(*d));
+		if (DEBUG(1)) {
+			KPRINT(PRINTPREFIX "%d/%d: allocated new dsa %lux\n", target, lun, (ulong)d);
+		}
+		lesetl(d->next, 0);
+		lesetl(&d->stateb, A_STATE_ALLOCATED);
+		if (legetl(c->dsalist.head) == 0)
+			lesetl(c->dsalist.head, DMASEG(d));	/* ATOMIC?!? */
+		else
+			lesetl(c->dsalist.tail->next, DMASEG(d));	/* ATOMIC?!? */
+		c->dsalist.tail = d;
+	}
+	else {
+		if (DEBUG(1)) {
+			KPRINT(PRINTPREFIX "%d/%d: reused dsa %lux\n", target, lun, (ulong)d);
+		}
+		c->dsalist.freechain = d->freechain;
+		lesetl(&d->stateb, A_STATE_ALLOCATED);
+	}
+	iunlock(&c->dsalist);
+	d->target = target;
+	d->lun = lun;
+	return d;
+}
+
+static void
+dsafree(Controller *c, Dsa *d)
+{
+	ilock(&c->dsalist);
+	d->freechain = c->dsalist.freechain;
+	c->dsalist.freechain = d;
+	lesetl(&d->stateb, A_STATE_FREE);
+	iunlock(&c->dsalist);
+}
+
+static Dsa *
+dsafind(Controller *c, uchar target, uchar lun, uchar state)
+{
+	Dsa *d;
+	for (d = KPTR(legetl(c->dsalist.head)); d; d = KPTR(legetl(d->next))) {
+		if (d->target != 0xff && d->target != target)
+			continue;
+		if (lun != 0xff && d->lun != lun)
+			continue;
+		if (state != 0xff && d->stateb != state)
+			continue;
+		break;
+	}
+	return d;
+}
+
+static void
+dumpncrregs(Controller *c, int intr)
+{
+	int i;
+	Ncr *n = c->n;
+	int depth = c->v->registers / 4;
+
+	if (intr) {
+		IPRINT("sa = %.8lux\n", c->scriptpa);
+	}
+	else {
+		KPRINT("sa = %.8lux\n", c->scriptpa);
+	}
+	for (i = 0; i < depth; i++) {
+		int j;
+		for (j = 0; j < 4; j++) {
+			int k = j * depth + i;
+			uchar *p;
+
+			/* display little-endian to make 32-bit values readable */
+			p = (uchar*)n+k*4;
+			if (intr) {
+				IPRINT(" %.2x%.2x%.2x%.2x %.2x %.2x", p[3], p[2], p[1], p[0], k * 4, (k * 4) + 0x80);
+			}
+			else {
+				KPRINT(" %.2x%.2x%.2x%.2x %.2x %.2x", p[3], p[2], p[1], p[0], k * 4, (k * 4) + 0x80);
+			}
+			USED(p);
+		}
+		if (intr) {
+			IPRINT("\n");
+		}
+		else {
+			KPRINT("\n");
+		}
+	}
+}	
+
+static int
+chooserate(Controller *c, int tpf, int *scfp, int *xferpp)
+{
+	/* find lowest entry >= tpf */
+	int besttpf = 1000;
+	int bestscfi = 0;
+	int bestxferp = 0;
+	int scf, xferp;
+	int maxscf;
+
+	if (c->v->feature & Ultra2)
+		maxscf = NULTRA2SCF;
+	else if (c->v->feature & Ultra)
+		maxscf = NULTRASCF;
+	else
+		maxscf = NSCF;
+
+	/*
+	 * search large clock factors first since this should
+	 * result in more reliable transfers
+	 */
+	for (scf = maxscf; scf >= 1; scf--) {
+		for (xferp = 0; xferp < 8; xferp++) {
+			unsigned char v = c->synctab[scf - 1][xferp];
+			if (v == 0)
+				continue;
+			if (v >= tpf && v < besttpf) {
+				besttpf = v;
+				bestscfi = scf;
+				bestxferp = xferp;
+			}
+		}
+	}
+	if (besttpf == 1000)
+		return 0;
+	if (scfp)
+		*scfp = bestscfi;
+	if (xferpp)
+		*xferpp = bestxferp;
+	return besttpf;
+}
+
+static void
+synctabinit(Controller *c)
+{
+	int scf;
+	unsigned long scsilimit;
+	int xferp;
+	unsigned long cr, sr;
+	int tpf;
+	int fast;
+	int maxscf;
+
+	if (c->v->feature & Ultra2)
+		maxscf = NULTRA2SCF;
+	else if (c->v->feature & Ultra)
+		maxscf = NULTRASCF;
+	else
+		maxscf = NSCF;
+
+	/*
+	 * for chips with no clock doubler, but Ultra capable (e.g. 860, or interestingly the
+	 * first spin of the 875), assume 80MHz
+	 * otherwise use the internal (33 Mhz) or external (40MHz) default
+	 */
+
+	if ((c->v->feature & Ultra) != 0 && (c->v->feature & (ClockDouble | ClockQuad)) == 0)
+		c->sclk = ULTRA_NOCLOCKDOUBLE_SCLK;
+	else
+		c->sclk = SCLK;
+
+	/*
+	 * otherwise, if the chip is Ultra capable, but has a slow(ish) clock,
+	 * invoke the doubler
+	 */
+
+	if (SCLK <= 40000000) {
+		if (c->v->feature & ClockDouble) {
+			c->sclk *= 2;
+			c->clockmult = 1;
+		}
+		else if (c->v->feature & ClockQuad) {
+			c->sclk *= 4;
+			c->clockmult = 1;
+		}
+		else
+			c->clockmult = 0;
+	}
+	else
+		c->clockmult = 0;
+
+	/* derive CCF from sclk */
+	/* woebetide anyone with SCLK < 16.7 or > 80MHz */
+	if (c->sclk <= 25 * MEGA)
+		c->ccf = 1;
+	else if (c->sclk <= 3750000)
+		c->ccf = 2;
+	else if (c->sclk <= 50 * MEGA)
+		c->ccf = 3;
+	else if (c->sclk <= 75 * MEGA)
+		c->ccf = 4;
+	else if ((c->v->feature & ClockDouble) && c->sclk <= 80 * MEGA)
+		c->ccf = 5;
+	else if ((c->v->feature & ClockQuad) && c->sclk <= 120 * MEGA)
+		c->ccf = 6;
+	else if ((c->v->feature & ClockQuad) && c->sclk <= 160 * MEGA)
+		c->ccf = 7;
+
+	for (scf = 1; scf < maxscf; scf++) {
+		/* check for legal core rate */
+		/* round up so we run slower for safety */
+	   	cr = (c->sclk * 2 + cf2[scf] - 1) / cf2[scf];
+		if (cr <= MAXSYNCCORERATE) {
+			scsilimit = MAXSYNCSCSIRATE;
+			fast = 0;
+		}
+		else if (cr <= MAXFASTSYNCCORERATE) {
+			scsilimit = MAXFASTSYNCSCSIRATE;
+			fast = 1;
+		}
+		else if ((c->v->feature & Ultra) && cr <= MAXULTRASYNCCORERATE) {
+			scsilimit = MAXULTRASYNCSCSIRATE;
+			fast = 2;
+		}
+		else if ((c->v->feature & Ultra2) && cr <= MAXULTRA2SYNCCORERATE) {
+			scsilimit = MAXULTRA2SYNCSCSIRATE;
+			fast = 3;
+		}
+		else
+			continue;
+		for (xferp = 11; xferp >= 4; xferp--) {
+			int ok;
+			int tp;
+			/* calculate scsi rate - round up again */
+			/* start from sclk for accuracy */
+			int totaldivide = xferp * cf2[scf];
+			sr = (c->sclk * 2 + totaldivide - 1) / totaldivide;
+			if (sr > scsilimit)
+				break;
+			/*
+			 * now work out transfer period
+			 * round down now so that period is pessimistic
+			 */
+			tp = (MEGA * 1000) / sr;
+			/*
+			 * bounds check it
+			 */
+			if (tp < 25 || tp > 255 * 4)
+				continue;
+			/*
+			 * spot stupid special case for Ultra or Ultra2
+			 * while working out factor
+			 */
+			if (tp == 25)
+				tpf = 10;
+			else if (tp == 50)
+				tpf = 12;
+			else if (tp < 52)
+				continue;
+			else
+				tpf = tp / 4;
+			/*
+			 * now check tpf looks sensible
+			 * given core rate
+			 */
+			switch (fast) {
+			case 0:
+				/* scf must be ccf for SCSI 1 */
+				ok = tpf >= 50 && scf == c->ccf;
+				break;
+			case 1:
+				ok = tpf >= 25 && tpf < 50;
+				break;
+			case 2:
+				/*
+				 * must use xferp of 4, or 5 at a pinch
+				 * for an Ultra transfer
+				 */
+				ok = xferp <= 5 && tpf >= 12 && tpf < 25;
+				break;
+			case 3:
+				ok = xferp == 4 && (tpf == 10 || tpf == 11);
+				break;
+			default:
+				ok = 0;
+			}
+			if (!ok)
+				continue;
+			c->synctab[scf - 1][xferp - 4] = tpf;
+		}
+	}
+
+#ifndef NO_ULTRA2
+	if (c->v->feature & Ultra2)
+		tpf = 10;
+	else
+#endif
+	if (c->v->feature & Ultra)
+		tpf = 12;
+	else
+		tpf = 25;
+	for (; tpf < 256; tpf++) {
+		if (chooserate(c, tpf, &scf, &xferp) == tpf) {
+			unsigned tp = tpf == 10 ? 25 : (tpf == 12 ? 50 : tpf * 4);
+			unsigned long khz = (MEGA + tp - 1) / (tp);
+			KPRINT(PRINTPREFIX "tpf=%d scf=%d.%.1d xferp=%d mhz=%ld.%.3ld\n",
+			    tpf, cf2[scf] / 2, (cf2[scf] & 1) ? 5 : 0,
+			    xferp + 4, khz / 1000, khz % 1000);
+			USED(khz);
+			if (c->tpf == 0)
+				c->tpf = tpf;	/* note lowest value for controller */
+		}
+	}
+}
+
+static void
+synctodsa(Dsa *dsa, Controller *c)
+{
+/*
+	KPRINT("synctodsa(dsa=%lux, target=%d, scntl3=%.2lx sxfer=%.2x)\n",
+	    dsa, dsa->target, c->scntl3[dsa->target], c->sxfer[dsa->target]);
+*/
+	dsa->scntl3 = c->scntl3[dsa->target];
+	dsa->sxfer = c->sxfer[dsa->target];
+}
+
+static void
+setsync(Dsa *dsa, Controller *c, int target, uchar ultra, uchar scf, uchar xferp, uchar reqack)
+{
+	c->scntl3[target] =
+	    (c->scntl3[target] & 0x08) | (((scf << 4) | c->ccf | (ultra << 7)) & ~0x08);
+	c->sxfer[target] = (xferp << 5) | reqack;
+	c->s[target] = BothDone;
+	if (dsa) {
+		synctodsa(dsa, c);
+		c->n->scntl3 = c->scntl3[target];
+		c->n->sxfer = c->sxfer[target];
+	}
+}
+
+static void
+setasync(Dsa *dsa, Controller *c, int target)
+{
+	setsync(dsa, c, target, 0, c->ccf, 0, 0);
+}
+
+static void
+setwide(Dsa *dsa, Controller *c, int target, uchar wide)
+{
+	c->scntl3[target] = wide ? (1 << 3) : 0;
+	setasync(dsa, c, target);
+	c->s[target] = WideDone;
+}
+
+static int
+buildsdtrmsg(uchar *buf, uchar tpf, uchar offset)
+{
+	*buf++ = X_MSG;
+	*buf++ = 3;
+	*buf++ = X_MSG_SDTR;
+	*buf++ = tpf;
+	*buf = offset;
+	return 5;
+}
+
+static int
+buildwdtrmsg(uchar *buf, uchar expo)
+{
+	*buf++ = X_MSG;
+	*buf++ = 2;
+	*buf++ = X_MSG_WDTR;
+	*buf = expo;
+	return 4;
+}
+
+static void
+start(Controller *c, long entry)
+{
+	ulong p;
+
+	if (c->running)
+		panic(PRINTPREFIX "start called while running");
+	c->running = 1;
+	p = c->scriptpa + entry;
+	lesetl(c->n->dsp, p);
+	if (c->ssm)
+		c->n->dcntl |= 0x4;		/* start DMA in SSI mode */
+}
+
+static void
+ncrcontinue(Controller *c)
+{
+	if (c->running)
+		panic(PRINTPREFIX "ncrcontinue called while running");
+	/* set the start DMA bit to continue execution */
+	c->running = 1;
+	c->n->dcntl |= 0x4;
+}
+
+static void
+softreset(Controller *c)
+{
+	Ncr *n = c->n;
+
+	n->istat = Srst;		/* software reset */
+	n->istat = 0;
+	/* general initialisation */
+	n->scid = (1 << 6) | 7;		/* respond to reselect, ID 7 */
+	n->respid = 1 << 7;		/* response ID = 7 */
+
+#ifdef INTERNAL_SCLK
+	n->stest1 = 0x80;		/* disable external scsi clock */
+#else
+	n->stest1 = 0x00;
+#endif
+
+	n->stime0 = 0xdd;		/* about 0.5 second timeout on each device */
+	n->scntl0 |= 0x8;		/* Enable parity checking */
+
+	/* continued setup */
+	n->sien0 = 0x8f;
+	n->sien1 = 0x04;
+	n->dien = 0x7d;
+	n->stest3 = 0x80;		/* TolerANT enable */
+	c->running = 0;
+
+	if (c->v->feature & BigFifo)
+		n->ctest5 = (1 << 5);
+	n->dmode = c->v->burst << 6;	/* set burst length bits */
+	if (c->v->burst & 4)
+		n->ctest5 |= (1 << 2);	/* including overflow into ctest5 bit 2 */
+	if (c->v->feature & Prefetch)
+		n->dcntl |= (1 << 5);	/* prefetch enable */
+	else if (c->v->feature & BurstOpCodeFetch)
+		n->dmode |= (1 << 1);	/* burst opcode fetch */
+	if (c->v->feature & Differential) {
+		/* chip capable */
+		if ((c->feature & Differential) || bios_set_differential(c)) {
+			/* user enabled, or some evidence bios set differential */
+			if (n->sstat2 & (1 << 2))
+				print(PRINTPREFIX "can't go differential; wrong cable\n");
+			else {
+				n->stest2 = (1 << 5);
+				print(PRINTPREFIX "differential mode set\n");
+			}
+		}
+	}
+	if (c->clockmult) {
+		n->stest1 |= (1 << 3);	/* power up doubler */
+		delay(2);
+		n->stest3 |= (1 << 5);	/* stop clock */
+		n->stest1 |= (1 << 2);	/* enable doubler */
+		n->stest3 &= ~(1 << 5);	/* start clock */
+		/* pray */
+	}
+}
+
+static void
+msgsm(Dsa *dsa, Controller *c, int msg, int *cont, int *wakeme)
+{
+	uchar histpf, hisreqack;
+	int tpf;
+	int scf, xferp;
+	int len;
+
+	Ncr *n = c->n;
+
+	switch (c->s[dsa->target]) {
+	case SyncInit:
+		switch (msg) {
+		case A_SIR_MSG_SDTR:
+			/* reply to my SDTR */
+			histpf = n->scratcha[2];
+			hisreqack = n->scratcha[3];
+			KPRINT(PRINTPREFIX "%d: SDTN response %d %d\n",
+			    dsa->target, histpf, hisreqack);
+
+			if (hisreqack == 0)
+				setasync(dsa, c, dsa->target);
+			else {
+				/* hisreqack should be <= c->v->maxsyncoff */
+				tpf = chooserate(c, histpf, &scf, &xferp);
+				KPRINT(PRINTPREFIX "%d: SDTN: using %d %d\n",
+				    dsa->target, tpf, hisreqack);
+				setsync(dsa, c, dsa->target, tpf < 25, scf, xferp, hisreqack);
+			}
+			*cont = -2;
+			return;
+		case A_SIR_EV_PHASE_SWITCH_AFTER_ID:
+			/* target ignored ATN for message after IDENTIFY - not SCSI-II */
+			KPRINT(PRINTPREFIX "%d: illegal phase switch after ID message - SCSI-1 device?\n", dsa->target);
+			KPRINT(PRINTPREFIX "%d: SDTN: async\n", dsa->target);
+			setasync(dsa, c, dsa->target);
+			*cont = E_to_decisions;
+			return;
+		case A_SIR_MSG_REJECT:
+			/* rejection of my SDTR */
+			KPRINT(PRINTPREFIX "%d: SDTN: rejected SDTR\n", dsa->target);
+		//async:
+			KPRINT(PRINTPREFIX "%d: SDTN: async\n", dsa->target);
+			setasync(dsa, c, dsa->target);
+			*cont = -2;
+			return;
+		}
+		break;
+	case WideInit:
+		switch (msg) {
+		case A_SIR_MSG_WDTR:
+			/* reply to my WDTR */
+			KPRINT(PRINTPREFIX "%d: WDTN: response %d\n",
+			    dsa->target, n->scratcha[2]);
+			setwide(dsa, c, dsa->target, n->scratcha[2]);
+			*cont = -2;
+			return;
+		case A_SIR_EV_PHASE_SWITCH_AFTER_ID:
+			/* target ignored ATN for message after IDENTIFY - not SCSI-II */
+			KPRINT(PRINTPREFIX "%d: illegal phase switch after ID message - SCSI-1 device?\n", dsa->target);
+			setwide(dsa, c, dsa->target, 0);
+			*cont = E_to_decisions;
+			return;
+		case A_SIR_MSG_REJECT:
+			/* rejection of my SDTR */
+			KPRINT(PRINTPREFIX "%d: WDTN: rejected WDTR\n", dsa->target);
+			setwide(dsa, c, dsa->target, 0);
+			*cont = -2;
+			return;
+		}
+		break;
+
+	case NeitherDone:
+	case WideDone:
+	case BothDone:
+		switch (msg) {
+		case A_SIR_MSG_WDTR: {
+			uchar hiswide, mywide;
+			hiswide = n->scratcha[2];
+			mywide = (c->v->feature & Wide) != 0;
+			KPRINT(PRINTPREFIX "%d: WDTN: target init %d\n",
+			    dsa->target, hiswide);
+			if (hiswide < mywide)
+				mywide = hiswide;
+			KPRINT(PRINTPREFIX "%d: WDTN: responding %d\n",
+			    dsa->target, mywide);
+			setwide(dsa, c, dsa->target, mywide);
+			len = buildwdtrmsg(dsa->msg_out, mywide);
+			setmovedata(&dsa->msg_out_buf, DMASEG(dsa->msg_out), len);
+			*cont = E_response;
+			c->s[dsa->target] = WideResponse;
+			return;
+		}
+		case A_SIR_MSG_SDTR:
+#ifdef ASYNC_ONLY
+			*cont = E_reject;
+			return;
+#else
+			/* target decides to renegotiate */
+			histpf = n->scratcha[2];
+			hisreqack = n->scratcha[3];
+			KPRINT(PRINTPREFIX "%d: SDTN: target init %d %d\n",
+			    dsa->target, histpf, hisreqack);
+			if (hisreqack == 0) {
+				/* he wants asynchronous */
+				setasync(dsa, c, dsa->target);
+				tpf = 0;
+			}
+			else {
+				/* he wants synchronous */
+				tpf = chooserate(c, histpf, &scf, &xferp);
+				if (hisreqack > c->v->maxsyncoff)
+					hisreqack = c->v->maxsyncoff;
+				KPRINT(PRINTPREFIX "%d: using %d %d\n",
+				    dsa->target, tpf, hisreqack);
+				setsync(dsa, c, dsa->target, tpf < 25, scf, xferp, hisreqack);
+			}
+			/* build my SDTR message */
+			len = buildsdtrmsg(dsa->msg_out, tpf, hisreqack);
+			setmovedata(&dsa->msg_out_buf, DMASEG(dsa->msg_out), len);
+			*cont = E_response;
+			c->s[dsa->target] = SyncResponse;
+			return;
+#endif
+		}
+		break;
+	case WideResponse:
+		switch (msg) {
+		case A_SIR_EV_RESPONSE_OK:
+			c->s[dsa->target] = WideDone;
+			KPRINT(PRINTPREFIX "%d: WDTN: response accepted\n", dsa->target);
+			*cont = -2;
+			return;
+		case A_SIR_MSG_REJECT:
+			setwide(dsa, c, dsa->target, 0);
+			KPRINT(PRINTPREFIX "%d: WDTN: response REJECTed\n", dsa->target);
+			*cont = -2;
+			return;
+		}
+		break;
+	case SyncResponse:
+		switch (msg) {
+		case A_SIR_EV_RESPONSE_OK:
+			c->s[dsa->target] = BothDone;
+			KPRINT(PRINTPREFIX "%d: SDTN: response accepted (%s)\n",
+			    dsa->target, phase[n->sstat1 & 7]);
+			*cont = -2;
+			return;	/* chf */
+		case A_SIR_MSG_REJECT:
+			setasync(dsa, c, dsa->target);
+			KPRINT(PRINTPREFIX "%d: SDTN: response REJECTed\n", dsa->target);
+			*cont = -2;
+			return;
+		}
+		break;
+	}
+	KPRINT(PRINTPREFIX "%d: msgsm: state %d msg %d\n",
+	    dsa->target, c->s[dsa->target], msg);
+	*wakeme = 1;
+	return;
+}
+
+static void
+calcblockdma(Dsa *d, ulong base, ulong count)
+{
+	ulong blocks;
+	if (DEBUG(3))
+		blocks = 0;
+	else {
+		blocks = count / A_BSIZE;
+		if (blocks > 255)
+			blocks = 255;
+	}
+	d->dmablks = blocks;
+	d->dmaaddr[0] = base;
+	d->dmaaddr[1] = base >> 8;
+	d->dmaaddr[2] = base >> 16;
+	d->dmaaddr[3] = base >> 24;
+	setmovedata(&d->data_buf, base + blocks * A_BSIZE, count - blocks * A_BSIZE);
+	d->flag = legetl(d->data_buf.dbc) == 0;
+}
+
+static ulong
+read_mismatch_recover(Controller *c, Ncr *n, Dsa *dsa)
+{
+	ulong dbc;
+	uchar dfifo = n->dfifo;
+	int inchip;
+
+	dbc = (n->dbc[2]<<16)|(n->dbc[1]<<8)|n->dbc[0];
+	if (n->ctest5 & (1 << 5))
+		inchip = ((dfifo | ((n->ctest5 & 3) << 8)) - (dbc & 0x3ff)) & 0x3ff;
+	else
+		inchip = ((dfifo & 0x7f) - (dbc & 0x7f)) & 0x7f;
+	if (inchip) {
+		IPRINT(PRINTPREFIX "%d/%d: read_mismatch_recover: DMA FIFO = %d\n",
+		    dsa->target, dsa->lun, inchip);
+	}
+	if (n->sxfer & SYNCOFFMASK(c)) {
+		/* SCSI FIFO */
+		uchar fifo = n->sstat1 >> 4;
+		if (c->v->maxsyncoff > 8)
+			fifo |= (n->sstat2 & (1 << 4));
+		if (fifo) {
+			inchip += fifo;
+			IPRINT(PRINTPREFIX "%d/%d: read_mismatch_recover: SCSI FIFO = %d\n",
+			    dsa->target, dsa->lun, fifo);
+		}
+	}
+	else {
+		if (n->sstat0 & (1 << 7)) {
+			inchip++;
+			IPRINT(PRINTPREFIX "%d/%d: read_mismatch_recover: SIDL full\n",
+			    dsa->target, dsa->lun);
+		}
+		if (n->sstat2 & (1 << 7)) {
+			inchip++;
+			IPRINT(PRINTPREFIX "%d/%d: read_mismatch_recover: SIDL msb full\n",
+			    dsa->target, dsa->lun);
+		}
+	}
+	USED(inchip);
+	return dbc;
+}
+
+static ulong
+write_mismatch_recover(Controller *c, Ncr *n, Dsa *dsa)
+{
+	ulong dbc;
+	uchar dfifo = n->dfifo;
+	int inchip;
+
+	dbc = (n->dbc[2]<<16)|(n->dbc[1]<<8)|n->dbc[0];
+	USED(dsa);
+	if (n->ctest5 & (1 << 5))
+		inchip = ((dfifo | ((n->ctest5 & 3) << 8)) - (dbc & 0x3ff)) & 0x3ff;
+	else
+		inchip = ((dfifo & 0x7f) - (dbc & 0x7f)) & 0x7f;
+#ifdef WMR_DEBUG
+	if (inchip) {
+		IPRINT(PRINTPREFIX "%d/%d: write_mismatch_recover: DMA FIFO = %d\n",
+		    dsa->target, dsa->lun, inchip);
+	}
+#endif
+	if (n->sstat0 & (1 << 5)) {
+		inchip++;
+#ifdef WMR_DEBUG
+		IPRINT(PRINTPREFIX "%d/%d: write_mismatch_recover: SODL full\n", dsa->target, dsa->lun);
+#endif
+	}
+	if (n->sstat2 & (1 << 5)) {
+		inchip++;
+#ifdef WMR_DEBUG
+		IPRINT(PRINTPREFIX "%d/%d: write_mismatch_recover: SODL msb full\n", dsa->target, dsa->lun);
+#endif
+	}
+	if (n->sxfer & SYNCOFFMASK(c)) {
+		/* synchronous SODR */
+		if (n->sstat0 & (1 << 6)) {
+			inchip++;
+#ifdef WMR_DEBUG
+			IPRINT(PRINTPREFIX "%d/%d: write_mismatch_recover: SODR full\n",
+			    dsa->target, dsa->lun);
+#endif
+		}
+		if (n->sstat2 & (1 << 6)) {
+			inchip++;
+#ifdef WMR_DEBUG
+			IPRINT(PRINTPREFIX "%d/%d: write_mismatch_recover: SODR msb full\n",
+			    dsa->target, dsa->lun);
+#endif
+		}
+	}
+	/* clear the dma fifo */
+	n->ctest3 |= (1 << 2);
+	/* wait till done */
+	while ((n->dstat & Dfe) == 0)
+		;
+	return dbc + inchip;
+}
+
+static void
+sd53c8xxinterrupt(Ureg *ur, void *a)
+{
+	uchar istat;
+	ushort sist;
+	uchar dstat;
+	int wakeme = 0;
+	int cont = -1;
+	Dsa *dsa;
+	Controller *c = a;
+	Ncr *n = c->n;
+
+	USED(ur);
+	if (DEBUG(1)) {
+		IPRINT(PRINTPREFIX "int\n");
+	}
+	ilock(c);
+	istat = n->istat;
+	if (istat & Intf) {
+		Dsa *d;
+		int wokesomething = 0;
+		if (DEBUG(1)) {
+			IPRINT(PRINTPREFIX "Intfly\n");
+		}
+		n->istat = Intf;
+		/* search for structures in A_STATE_DONE */
+		for (d = KPTR(legetl(c->dsalist.head)); d; d = KPTR(legetl(d->next))) {
+			if (d->stateb == A_STATE_DONE) {
+				d->p9status = d->status;
+				if (DEBUG(1)) {
+					IPRINT(PRINTPREFIX "waking up dsa %lux\n", (ulong)d);
+				}
+				wakeup(d);
+				wokesomething = 1;
+			}
+		}
+		if (!wokesomething) {
+			IPRINT(PRINTPREFIX "nothing to wake up\n");
+		}
+	}
+
+	if ((istat & (Sip | Dip)) == 0) {
+		if (DEBUG(1)) {
+			IPRINT(PRINTPREFIX "int end %x\n", istat);
+		}
+		iunlock(c);
+		return;
+	}
+
+	sist = (n->sist1<<8)|n->sist0;	/* BUG? can two-byte read be inconsistent? */
+	dstat = n->dstat;
+	dsa = (Dsa *)DMASEG_TO_KADDR(legetl(n->dsa));
+	c->running = 0;
+	if (istat & Sip) {
+		if (DEBUG(1)) {
+			IPRINT("sist = %.4x\n", sist);
+		}
+		if (sist & 0x80) {
+			ulong addr;
+			ulong sa;
+			ulong dbc;
+			ulong tbc;
+			int dmablks;
+			ulong dmaaddr;
+
+			addr = legetl(n->dsp);
+			sa = addr - c->scriptpa;
+			if (DEBUG(1) || DEBUG(2)) {
+				IPRINT(PRINTPREFIX "%d/%d: Phase Mismatch sa=%.8lux\n",
+				    dsa->target, dsa->lun, sa);
+			}
+			/*
+			 * now recover
+			 */
+			if (sa == E_data_in_mismatch) {
+				/*
+				 * though this is a failure in the residue, there may have been blocks
+				 * as well. if so, dmablks will not have been zeroed, since the state
+				 * was not saved by the microcode. 
+				 */
+				dbc = read_mismatch_recover(c, n, dsa);
+				tbc = legetl(dsa->data_buf.dbc) - dbc;
+				dsa->dmablks = 0;
+				n->scratcha[2] = 0;
+				advancedata(&dsa->data_buf, tbc);
+				if (DEBUG(1) || DEBUG(2)) {
+					IPRINT(PRINTPREFIX "%d/%d: transferred = %ld residue = %ld\n",
+					    dsa->target, dsa->lun, tbc, legetl(dsa->data_buf.dbc));
+				}
+				cont = E_data_mismatch_recover;
+			}
+			else if (sa == E_data_in_block_mismatch) {
+				dbc = read_mismatch_recover(c, n, dsa);
+				tbc = A_BSIZE - dbc;
+				/* recover current state from registers */
+				dmablks = n->scratcha[2];
+				dmaaddr = legetl(n->scratchb);
+				/* we have got to dmaaddr + tbc */
+				/* we have dmablks * A_BSIZE - tbc + residue left to do */
+				/* so remaining transfer is */
+				IPRINT("in_block_mismatch: dmaaddr = 0x%lux tbc=%lud dmablks=%d\n",
+				    dmaaddr, tbc, dmablks);
+				calcblockdma(dsa, dmaaddr + tbc,
+				    dmablks * A_BSIZE - tbc + legetl(dsa->data_buf.dbc));
+				/* copy changes into scratch registers */
+				IPRINT("recalc: dmablks %d dmaaddr 0x%lx pa 0x%lx dbc %ld\n",
+				    dsa->dmablks, legetl(dsa->dmaaddr),
+				    legetl(dsa->data_buf.pa), legetl(dsa->data_buf.dbc));
+				n->scratcha[2] = dsa->dmablks;
+				lesetl(n->scratchb, dsa->dmancr);
+				cont = E_data_block_mismatch_recover;
+			}
+			else if (sa == E_data_out_mismatch) {
+				dbc = write_mismatch_recover(c, n, dsa);
+				tbc = legetl(dsa->data_buf.dbc) - dbc;
+				dsa->dmablks = 0;
+				n->scratcha[2] = 0;
+				advancedata(&dsa->data_buf, tbc);
+				if (DEBUG(1) || DEBUG(2)) {
+					IPRINT(PRINTPREFIX "%d/%d: transferred = %ld residue = %ld\n",
+					    dsa->target, dsa->lun, tbc, legetl(dsa->data_buf.dbc));
+				}
+				cont = E_data_mismatch_recover;
+			}
+			else if (sa == E_data_out_block_mismatch) {
+				dbc = write_mismatch_recover(c, n, dsa);
+				tbc = legetl(dsa->data_buf.dbc) - dbc;
+				/* recover current state from registers */
+				dmablks = n->scratcha[2];
+				dmaaddr = legetl(n->scratchb);
+				/* we have got to dmaaddr + tbc */
+				/* we have dmablks blocks - tbc + residue left to do */
+				/* so remaining transfer is */
+				IPRINT("out_block_mismatch: dmaaddr = %lux tbc=%lud dmablks=%d\n",
+				    dmaaddr, tbc, dmablks);
+				calcblockdma(dsa, dmaaddr + tbc,
+				    dmablks * A_BSIZE - tbc + legetl(dsa->data_buf.dbc));
+				/* copy changes into scratch registers */
+				n->scratcha[2] = dsa->dmablks;
+				lesetl(n->scratchb, dsa->dmancr);
+				cont = E_data_block_mismatch_recover;
+			}
+			else if (sa == E_id_out_mismatch) {
+				/*
+				 * target switched phases while attention held during
+				 * message out. The possibilities are:
+				 * 1. It didn't like the last message. This is indicated
+				 *    by the new phase being message_in. Use script to recover
+				 *
+				 * 2. It's not SCSI-II compliant. The new phase will be other
+				 *    than message_in. We should also indicate that the device
+				 *    is asynchronous, if it's the SDTR that got ignored
+				 * 
+				 * For now, if the phase switch is not to message_in, and
+				 * and it happens after IDENTIFY and before SDTR, we
+				 * notify the negotiation state machine.
+				 */
+				ulong lim = legetl(dsa->msg_out_buf.dbc);
+				uchar p = n->sstat1 & 7;
+				dbc = write_mismatch_recover(c, n, dsa);
+				tbc = lim - dbc;
+				IPRINT(PRINTPREFIX "%d/%d: msg_out_mismatch: %lud/%lud sent, phase %s\n",
+				    dsa->target, dsa->lun, tbc, lim, phase[p]);
+				if (p != MessageIn && tbc == 1) {
+					msgsm(dsa, c, A_SIR_EV_PHASE_SWITCH_AFTER_ID, &cont, &wakeme);
+				}
+				else
+					cont = E_id_out_mismatch_recover;
+			}
+			else if (sa == E_cmd_out_mismatch) {
+				/*
+				 * probably the command count is longer than the device wants ...
+				 */
+				ulong lim = legetl(dsa->cmd_buf.dbc);
+				uchar p = n->sstat1 & 7;
+				dbc = write_mismatch_recover(c, n, dsa);
+				tbc = lim - dbc;
+				IPRINT(PRINTPREFIX "%d/%d: cmd_out_mismatch: %lud/%lud sent, phase %s\n",
+				    dsa->target, dsa->lun, tbc, lim, phase[p]);
+				USED(p, tbc);
+				cont = E_to_decisions;
+			}
+			else {
+				IPRINT(PRINTPREFIX "%d/%d: ma sa=%.8lux wanted=%s got=%s\n",
+				    dsa->target, dsa->lun, sa,
+				    phase[n->dcmd & 7],
+				    phase[n->sstat1 & 7]);
+				dumpncrregs(c, 1);
+				dsa->p9status = SDeio;	/* chf */
+				wakeme = 1;
+			}
+		}
+		/*else*/ if (sist & 0x400) {
+			if (DEBUG(0)) {
+				IPRINT(PRINTPREFIX "%d/%d Sto\n", dsa->target, dsa->lun);
+			}
+			dsa->p9status = SDtimeout;
+			dsa->stateb = A_STATE_DONE;
+			softreset(c);
+			cont = E_issue_check;
+			wakeme = 1;
+		}
+		if (sist & 0x1) {
+			IPRINT(PRINTPREFIX "%d/%d: parity error\n", dsa->target, dsa->lun);
+			dsa->parityerror = 1;
+		}
+		if (sist & 0x4) {
+			IPRINT(PRINTPREFIX "%d/%d: unexpected disconnect\n",
+			    dsa->target, dsa->lun);
+			dumpncrregs(c, 1);
+			//wakeme = 1;
+			dsa->p9status = SDeio;
+		}
+	}
+	if (istat & Dip) {
+		if (DEBUG(1)) {
+			IPRINT("dstat = %.2x\n", dstat);
+		}
+		/*else*/ if (dstat & Ssi) {
+			ulong *p = DMASEG_TO_KADDR(legetl(n->dsp));
+			ulong w = (uchar *)p - (uchar *)c->script;
+			IPRINT("[%lux]", w);
+			USED(w);
+			cont = -2;	/* restart */
+		}
+		if (dstat & Sir) {
+			switch (legetl(n->dsps)) {
+			case A_SIR_MSG_IO_COMPLETE:
+				dsa->p9status = dsa->status;
+				wakeme = 1;
+				break;
+			case A_SIR_MSG_SDTR:
+			case A_SIR_MSG_WDTR:
+			case A_SIR_MSG_REJECT:
+			case A_SIR_EV_RESPONSE_OK:
+				msgsm(dsa, c, legetl(n->dsps), &cont, &wakeme);
+				break;
+			case A_SIR_MSG_IGNORE_WIDE_RESIDUE:
+				/* back up one in the data transfer */
+				IPRINT(PRINTPREFIX "%d/%d: ignore wide residue %d, WSR = %d\n",
+				    dsa->target, dsa->lun, n->scratcha[1], n->scntl2 & 1);
+				if (dsa->flag == 2) {
+					IPRINT(PRINTPREFIX "%d/%d: transfer over; residue ignored\n",
+					    dsa->target, dsa->lun);
+				}
+				else {
+					calcblockdma(dsa, legetl(dsa->dmaaddr) - 1,
+					    dsa->dmablks * A_BSIZE + legetl(dsa->data_buf.dbc) + 1);
+				}
+				cont = -2;
+				break;
+			case A_SIR_ERROR_NOT_MSG_IN_AFTER_RESELECT:
+				IPRINT(PRINTPREFIX "%d: not msg_in after reselect (%s)",
+				    n->ssid & SSIDMASK(c), phase[n->sstat1 & 7]);
+				dsa = dsafind(c, n->ssid & SSIDMASK(c), -1, A_STATE_DISCONNECTED);
+				dumpncrregs(c, 1);
+				wakeme = 1;
+				break;
+			case A_SIR_NOTIFY_MSG_IN:
+				IPRINT(PRINTPREFIX "%d/%d: msg_in %d\n",
+				    dsa->target, dsa->lun, n->sfbr);
+				cont = -2;
+				break;
+			case A_SIR_NOTIFY_DISC:
+				IPRINT(PRINTPREFIX "%d/%d: disconnect:", dsa->target, dsa->lun);
+				goto dsadump;
+			case A_SIR_NOTIFY_STATUS:
+				IPRINT(PRINTPREFIX "%d/%d: status\n", dsa->target, dsa->lun);
+				cont = -2;
+				break;
+			case A_SIR_NOTIFY_COMMAND:
+				IPRINT(PRINTPREFIX "%d/%d: commands\n", dsa->target, dsa->lun);
+				cont = -2;
+				break;
+			case A_SIR_NOTIFY_DATA_IN:
+				IPRINT(PRINTPREFIX "%d/%d: data in a %lx b %lx\n",
+				    dsa->target, dsa->lun, legetl(n->scratcha), legetl(n->scratchb));
+				cont = -2;
+				break;
+			case A_SIR_NOTIFY_BLOCK_DATA_IN:
+				IPRINT(PRINTPREFIX "%d/%d: block data in: a2 %x b %lx\n",
+				    dsa->target, dsa->lun, n->scratcha[2], legetl(n->scratchb));
+				cont = -2;
+				break;
+			case A_SIR_NOTIFY_DATA_OUT:
+				IPRINT(PRINTPREFIX "%d/%d: data out\n", dsa->target, dsa->lun);
+				cont = -2;
+				break;
+			case A_SIR_NOTIFY_DUMP:
+				IPRINT(PRINTPREFIX "%d/%d: dump\n", dsa->target, dsa->lun);
+				dumpncrregs(c, 1);
+				cont = -2;
+				break;
+			case A_SIR_NOTIFY_DUMP2:
+				IPRINT(PRINTPREFIX "%d/%d: dump2:", dsa->target, dsa->lun);
+				IPRINT(" sa %lux", legetl(n->dsp) - c->scriptpa);
+				IPRINT(" dsa %lux", legetl(n->dsa));
+				IPRINT(" sfbr %ux", n->sfbr);
+				IPRINT(" a %lux", legetl(n->scratcha));
+				IPRINT(" b %lux", legetl(n->scratchb));
+				IPRINT(" ssid %ux", n->ssid);
+				IPRINT("\n");
+				cont = -2;
+				break;
+			case A_SIR_NOTIFY_WAIT_RESELECT:
+				IPRINT(PRINTPREFIX "wait reselect\n");
+				cont = -2;
+				break;
+			case A_SIR_NOTIFY_RESELECT:
+				IPRINT(PRINTPREFIX "reselect: ssid %.2x sfbr %.2x at %ld\n",
+				    n->ssid, n->sfbr, TK2MS(m->ticks));
+				cont = -2;
+				break;
+			case A_SIR_NOTIFY_ISSUE:
+				IPRINT(PRINTPREFIX "%d/%d: issue:", dsa->target, dsa->lun);
+			dsadump:
+				IPRINT(" tgt=%d", dsa->target);
+				IPRINT(" time=%ld", TK2MS(m->ticks));
+				IPRINT("\n");
+				cont = -2;
+				break;
+			case A_SIR_NOTIFY_ISSUE_CHECK:
+				IPRINT(PRINTPREFIX "issue check\n");
+				cont = -2;
+				break;
+			case A_SIR_NOTIFY_SIGP:
+				IPRINT(PRINTPREFIX "responded to SIGP\n");
+				cont = -2;
+				break;
+			case A_SIR_NOTIFY_DUMP_NEXT_CODE: {
+				ulong *dsp = DMASEG_TO_KADDR(legetl(n->dsp));
+				int x;
+				IPRINT(PRINTPREFIX "code at %lux", dsp - c->script);
+				for (x = 0; x < 6; x++) {
+					IPRINT(" %.8lux", dsp[x]);
+				}
+				IPRINT("\n");
+				USED(dsp);
+				cont = -2;
+				break;
+			}
+			case A_SIR_NOTIFY_WSR:
+				IPRINT(PRINTPREFIX "%d/%d: WSR set\n", dsa->target, dsa->lun);
+				cont = -2;
+				break;
+			case A_SIR_NOTIFY_LOAD_SYNC:
+				IPRINT(PRINTPREFIX "%d/%d: scntl=%.2x sxfer=%.2x\n",
+				    dsa->target, dsa->lun, n->scntl3, n->sxfer);
+				cont = -2;
+				break;
+			case A_SIR_NOTIFY_RESELECTED_ON_SELECT:
+				if (DEBUG(2)) {
+					IPRINT(PRINTPREFIX "%d/%d: reselected during select\n",
+ 					    dsa->target, dsa->lun);
+				}
+				cont = -2;
+				break;
+			case A_error_reselected:		/* dsa isn't valid here */
+				print(PRINTPREFIX "reselection error\n");
+				dumpncrregs(c, 1);
+				for (dsa = KPTR(legetl(c->dsalist.head)); dsa; dsa = KPTR(legetl(dsa->next))) {
+					IPRINT(PRINTPREFIX "dsa target %d lun %d state %d\n", dsa->target, dsa->lun, dsa->stateb);
+				}
+				break;
+			default:
+				IPRINT(PRINTPREFIX "%d/%d: script error %ld\n",
+					dsa->target, dsa->lun, legetl(n->dsps));
+				dumpncrregs(c, 1);
+				wakeme = 1;
+			}
+		}
+		/*else*/ if (dstat & Iid) {
+			ulong addr = legetl(n->dsp);
+			ulong dbc = (n->dbc[2]<<16)|(n->dbc[1]<<8)|n->dbc[0];
+			IPRINT(PRINTPREFIX "%d/%d: Iid pa=%.8lux sa=%.8lux dbc=%lux\n",
+			    dsa->target, dsa->lun,
+			    addr, addr - c->scriptpa, dbc);
+			addr = (ulong)DMASEG_TO_KADDR(addr);
+			IPRINT("%.8lux %.8lux %.8lux\n",
+			    *(ulong *)(addr - 12), *(ulong *)(addr - 8), *(ulong *)(addr - 4));
+			USED(addr, dbc);
+			dsa->p9status = SDeio;
+			wakeme = 1;
+		}
+		/*else*/ if (dstat & Bf) {
+			IPRINT(PRINTPREFIX "%d/%d: Bus Fault\n", dsa->target, dsa->lun);
+			dumpncrregs(c, 1);
+			dsa->p9status = SDeio;
+			wakeme = 1;
+		}
+	}
+	if (cont == -2)
+		ncrcontinue(c);
+	else if (cont >= 0)
+		start(c, cont);
+	if (wakeme){
+		if(dsa->p9status == SDnostatus)
+			dsa->p9status = SDeio;
+		wakeup(dsa);
+	}
+	iunlock(c);
+	if (DEBUG(1)) {
+		IPRINT(PRINTPREFIX "int end 1\n");
+	}
+}
+
+static int
+done(void *arg)
+{
+	return ((Dsa *)arg)->p9status != SDnostatus;
+}
+
+static void
+setmovedata(Movedata *d, ulong pa, ulong bc)
+{
+	d->pa[0] = pa;
+	d->pa[1] = pa>>8;
+	d->pa[2] = pa>>16;
+	d->pa[3] = pa>>24;
+	d->dbc[0] = bc;
+	d->dbc[1] = bc>>8;
+	d->dbc[2] = bc>>16;
+	d->dbc[3] = bc>>24;
+}
+
+static void
+advancedata(Movedata *d, long v)
+{
+	lesetl(d->pa, legetl(d->pa) + v);
+	lesetl(d->dbc, legetl(d->dbc) - v);
+}
+
+static void
+dumpwritedata(uchar *data, int datalen)
+{
+	int i;
+	uchar *bp;
+	if (!DEBUG(0)){
+		USED(data, datalen);
+		return;
+	}
+
+	if (datalen) {
+		KPRINT(PRINTPREFIX "write:");
+		for (i = 0, bp = data; i < 50 && i < datalen; i++, bp++) {
+			KPRINT("%.2ux", *bp);
+		}
+		if (i < datalen) {
+			KPRINT("...");
+		}
+		KPRINT("\n");
+	}
+}
+
+static void
+dumpreaddata(uchar *data, int datalen)
+{
+	int i;
+	uchar *bp;
+	if (!DEBUG(0)){
+		USED(data, datalen);
+		return;
+	}
+
+	if (datalen) {
+		KPRINT(PRINTPREFIX "read:");
+		for (i = 0, bp = data; i < 50 && i < datalen; i++, bp++) {
+			KPRINT("%.2ux", *bp);
+		}
+		if (i < datalen) {
+			KPRINT("...");
+		}
+		KPRINT("\n");
+	}
+}
+
+static void
+busreset(Controller *c)
+{
+	int x, ntarget;
+
+	/* bus reset */
+	c->n->scntl1 |= (1 << 3);
+	delay(500);
+	c->n->scntl1 &= ~(1 << 3);
+	if(!(c->v->feature & Wide))
+		ntarget = 8;
+	else
+		ntarget = MAXTARGET;
+	for (x = 0; x < ntarget; x++) {
+		setwide(0, c, x, 0);
+#ifndef ASYNC_ONLY
+		c->s[x] = NeitherDone;
+#endif
+	}
+	c->capvalid = 0;
+}
+
+static void
+reset(Controller *c)
+{
+	/* should wakeup all pending tasks */
+	softreset(c);
+	busreset(c);
+}
+
+static int
+sd53c8xxrio(SDreq* r)
+{
+	Dsa *d;
+	uchar *bp;
+	Controller *c;
+	uchar target_expo, my_expo;
+	int bc, check, i, status, target;
+
+	if((target = r->unit->subno) == 0x07)
+		return r->status = SDtimeout;	/* assign */
+	c = r->unit->dev->ctlr;
+
+	check = 0;
+	d = dsaalloc(c, target, r->lun);
+
+	qlock(&c->q[target]);			/* obtain access to target */
+docheck:
+	/* load the transfer control stuff */
+	d->scsi_id_buf[0] = 0;
+	d->scsi_id_buf[1] = c->sxfer[target];
+	d->scsi_id_buf[2] = target;
+	d->scsi_id_buf[3] = c->scntl3[target];
+	synctodsa(d, c);
+
+	bc = 0;
+
+	d->msg_out[bc] = 0x80 | r->lun;
+
+#ifndef NO_DISCONNECT
+	d->msg_out[bc] |= (1 << 6);
+#endif
+	bc++;
+
+	/* work out what to do about negotiation */
+	switch (c->s[target]) {
+	default:
+		KPRINT(PRINTPREFIX "%d: strange nego state %d\n", target, c->s[target]);
+		c->s[target] = NeitherDone;
+		/* fall through */
+	case NeitherDone:
+		if ((c->capvalid & (1 << target)) == 0)
+			break;
+		target_expo = (c->cap[target] >> 5) & 3;
+		my_expo = (c->v->feature & Wide) != 0;
+		if (target_expo < my_expo)
+			my_expo = target_expo;
+#ifdef ALWAYS_DO_WDTR
+		bc += buildwdtrmsg(d->msg_out + bc, my_expo);
+		KPRINT(PRINTPREFIX "%d: WDTN: initiating expo %d\n", target, my_expo);
+		c->s[target] = WideInit;
+		break;
+#else
+		if (my_expo) {
+			bc += buildwdtrmsg(d->msg_out + bc, (c->v->feature & Wide) ? 1 : 0);
+			KPRINT(PRINTPREFIX "%d: WDTN: initiating expo %d\n", target, my_expo);
+			c->s[target] = WideInit;
+			break;
+		}
+		KPRINT(PRINTPREFIX "%d: WDTN: narrow\n", target);
+		/* fall through */
+#endif
+	case WideDone:
+		if (c->cap[target] & (1 << 4)) {
+			KPRINT(PRINTPREFIX "%d: SDTN: initiating %d %d\n", target, c->tpf, c->v->maxsyncoff);
+			bc += buildsdtrmsg(d->msg_out + bc, c->tpf, c->v->maxsyncoff);
+			c->s[target] = SyncInit;
+			break;
+		}
+		KPRINT(PRINTPREFIX "%d: SDTN: async only\n", target);
+		c->s[target] = BothDone;
+		break;
+
+	case BothDone:
+		break;
+	}
+
+	setmovedata(&d->msg_out_buf, DMASEG(d->msg_out), bc);
+	setmovedata(&d->cmd_buf, DMASEG(r->cmd), r->clen);
+	calcblockdma(d, DMASEG(r->data), r->dlen);
+
+	if (DEBUG(0)) {
+		KPRINT(PRINTPREFIX "%d/%d: exec: ", target, r->lun);
+		for (bp = r->cmd; bp < &r->cmd[r->clen]; bp++) {
+			KPRINT("%.2ux", *bp);
+		}
+		KPRINT("\n");
+		if (!r->write) {
+			KPRINT(PRINTPREFIX "%d/%d: exec: limit=(%d)%ld\n",
+			  target, r->lun, d->dmablks, legetl(d->data_buf.dbc));
+		}
+		else
+			dumpwritedata(r->data, r->dlen);
+	}
+
+	setmovedata(&d->status_buf, DMASEG(&d->status), 1);	
+
+	d->p9status = SDnostatus;
+	d->parityerror = 0;
+
+	d->stateb = A_STATE_ISSUE;		/* start operation */
+
+	ilock(c);
+	if (c->ssm)
+		c->n->dcntl |= 0x10;		/* SSI */
+	if (c->running) {
+		c->n->istat |= Sigp;
+	}
+	else {
+		start(c, E_issue_check);
+	}
+	iunlock(c);
+
+	while(waserror())
+		;
+	tsleep(d, done, d, 600 * 1000);
+	poperror();
+
+	if (!done(d)) {
+		KPRINT(PRINTPREFIX "%d/%d: exec: Timed out\n", target, r->lun);
+		dumpncrregs(c, 0);
+		dsafree(c, d);
+		reset(c);
+		qunlock(&c->q[target]);
+		r->status = SDtimeout;
+		return r->status = SDtimeout;	/* assign */
+	}
+
+	if((status = d->p9status) == SDeio)
+		c->s[target] = NeitherDone;
+	if (d->parityerror) {
+		status = SDeio;
+	}
+
+	/*
+	 * adjust datalen
+	 */
+	r->rlen = r->dlen;
+	if (DEBUG(0)) {
+		KPRINT(PRINTPREFIX "%d/%d: exec: before rlen adjust: dmablks %d flag %d dbc %lud\n",
+		    target, r->lun, d->dmablks, d->flag, legetl(d->data_buf.dbc));
+	}
+	r->rlen = r->dlen;
+	if (d->flag != 2) {
+		r->rlen -= d->dmablks * A_BSIZE;
+		r->rlen -= legetl(d->data_buf.dbc);
+	}
+	if(!r->write)
+		dumpreaddata(r->data, r->rlen);
+	if (DEBUG(0)) {
+		KPRINT(PRINTPREFIX "%d/%d: exec: p9status=%d status %d rlen %ld\n",
+		    target, r->lun, d->p9status, status, r->rlen);
+	}
+	/*
+	 * spot the identify
+	 */
+	if ((c->capvalid & (1 << target)) == 0
+	 && (status == SDok || status == SDcheck)
+	 && r->cmd[0] == 0x12 && r->dlen >= 8) {
+		c->capvalid |= 1 << target;
+		bp = r->data;
+		c->cap[target] = bp[7];
+		KPRINT(PRINTPREFIX "%d: capabilities %.2x\n", target, bp[7]);
+	}
+	if(!check && status == SDcheck && !(r->flags & SDnosense)){
+		check = 1;
+		r->write = 0;
+		memset(r->cmd, 0, sizeof(r->cmd));
+		r->cmd[0] = 0x03;
+		r->cmd[1] = r->lun<<5;
+		r->cmd[4] = sizeof(r->sense)-1;
+		r->clen = 6;
+		r->data = r->sense;
+		r->dlen = sizeof(r->sense)-1;
+		/*
+		 * Clear out the microcode state
+		 * so the Dsa can be re-used.
+		 */
+		lesetl(&d->stateb, A_STATE_ALLOCATED);
+		goto docheck;
+	}
+	qunlock(&c->q[target]);
+	dsafree(c, d);
+
+	if(status == SDok && check){
+		status = SDcheck;
+		r->flags |= SDvalidsense;
+	}
+	if(DEBUG(0))
+		KPRINT(PRINTPREFIX "%d: r flags %8.8uX status %d rlen %ld\n",
+			target, r->flags, status, r->rlen);
+	if(r->flags & SDvalidsense){
+		if(!DEBUG(0))
+			KPRINT(PRINTPREFIX "%d: r flags %8.8uX status %d rlen %ld\n",
+				target, r->flags, status, r->rlen);
+		for(i = 0; i < r->rlen; i++)
+			KPRINT(" %2.2uX", r->sense[i]);
+		KPRINT("\n");
+	}
+	return r->status = status;
+}
+
+static void
+cribbios(Controller *c)
+{
+	c->bios.scntl3 = c->n->scntl3;
+	c->bios.stest2 = c->n->stest2;
+	KPRINT(PRINTPREFIX "bios scntl3(%.2x) stest2(%.2x)\n", c->bios.scntl3, c->bios.stest2);
+}
+
+static int
+bios_set_differential(Controller *c)
+{
+	/* Concept lifted from FreeBSD - thanks Gerard */
+	/* basically, if clock conversion factors are set, then there is
+ 	 * evidence the bios had a go at the chip, and if so, it would
+	 * have set the differential enable bit in stest2
+	 */
+	return (c->bios.scntl3 & 7) != 0 && (c->bios.stest2 & 0x20) != 0;
+}
+
+#define NCR_VID 	0x1000
+#define NCR_810_DID 	0x0001
+#define NCR_820_DID	0x0002	/* don't know enough about this one to support it */
+#define NCR_825_DID	0x0003
+#define NCR_815_DID	0x0004
+#define SYM_810AP_DID	0x0005
+#define SYM_860_DID	0x0006
+#define SYM_896_DID	0x000b
+#define SYM_895_DID	0x000c
+#define SYM_885_DID	0x000d	/* ditto */
+#define SYM_875_DID	0x000f	/* ditto */
+#define SYM_1010_DID	0x0020
+#define SYM_1011_DID	0x0021
+#define SYM_875J_DID	0x008f
+
+static Variant variant[] = {
+{ NCR_810_DID,   0x0f, "NCR53C810",	Burst16,   8, 24, 0 },
+{ NCR_810_DID,   0x1f, "SYM53C810ALV",	Burst16,   8, 24, Prefetch },
+{ NCR_810_DID,   0xff, "SYM53C810A",	Burst16,   8, 24, Prefetch },
+{ SYM_810AP_DID, 0xff, "SYM53C810AP",	Burst16,   8, 24, Prefetch },
+{ NCR_815_DID,   0xff, "NCR53C815",	Burst16,   8, 24, BurstOpCodeFetch },
+{ NCR_825_DID,   0x0f, "NCR53C825",	Burst16,   8, 24, Wide|BurstOpCodeFetch|Differential },
+{ NCR_825_DID,   0xff, "SYM53C825A",	Burst128, 16, 24, Prefetch|LocalRAM|BigFifo|Differential|Wide },
+{ SYM_860_DID,   0x0f, "SYM53C860",	Burst16,   8, 24, Prefetch|Ultra },
+{ SYM_860_DID,   0xff, "SYM53C860LV",	Burst16,   8, 24, Prefetch|Ultra },
+{ SYM_875_DID,   0x01, "SYM53C875r1",	Burst128, 16, 24, Prefetch|LocalRAM|BigFifo|Differential|Wide|Ultra },
+{ SYM_875_DID,   0xff, "SYM53C875",	Burst128, 16, 24, Prefetch|LocalRAM|BigFifo|Differential|Wide|Ultra|ClockDouble },
+{ SYM_875J_DID,   0xff, "SYM53C875j",	Burst128, 16, 24, Prefetch|LocalRAM|BigFifo|Differential|Wide|Ultra|ClockDouble },
+{ SYM_885_DID,   0xff, "SYM53C885",	Burst128, 16, 24, Prefetch|LocalRAM|BigFifo|Wide|Ultra|ClockDouble },
+{ SYM_895_DID,   0xff, "SYM53C895",	Burst128, 16, 24, Prefetch|LocalRAM|BigFifo|Wide|Ultra|Ultra2 },
+{ SYM_896_DID,   0xff, "SYM53C896",	Burst128, 16, 64, Prefetch|LocalRAM|BigFifo|Wide|Ultra|Ultra2 },
+{ SYM_1010_DID,  0xff, "SYM53C1010",	Burst128, 16, 64, Prefetch|LocalRAM|BigFifo|Wide|Ultra|Ultra2 },
+{ SYM_1011_DID,   0xff, "SYM53C1010",	Burst128, 16, 64, Prefetch|LocalRAM|BigFifo|Wide|Ultra|Ultra2 },
+};
+
+static int
+xfunc(Controller *c, enum na_external x, unsigned long *v)
+{
+	switch (x)
+	{
+	case X_scsi_id_buf:
+		*v = offsetof(Dsa, scsi_id_buf[0]); return 1;
+	case X_msg_out_buf:
+		*v = offsetof(Dsa, msg_out_buf); return 1;
+	case X_cmd_buf:
+		*v = offsetof(Dsa, cmd_buf); return 1;
+	case X_data_buf:
+		*v = offsetof(Dsa, data_buf); return 1;
+	case X_status_buf:
+		*v = offsetof(Dsa, status_buf); return 1;
+	case X_dsa_head:
+		*v = DMASEG(&c->dsalist.head[0]); return 1;
+	case X_ssid_mask:
+		*v = SSIDMASK(c); return 1;
+	default:
+		print("xfunc: can't find external %d\n", x);
+		return 0;
+	}
+}
+
+static int
+na_fixup(Controller *c, ulong pa_reg,
+    struct na_patch *patch, int patches,
+    int (*externval)(Controller*, int, ulong*))
+{
+	int p;
+	int v;
+	ulong *script, pa_script;
+	unsigned long lw, lv;
+
+	script = c->script;
+	pa_script = c->scriptpa;
+	for (p = 0; p < patches; p++) {
+		switch (patch[p].type) {
+		case 1:
+			/* script relative */
+			script[patch[p].lwoff] += pa_script;
+			break;
+		case 2:
+			/* register i/o relative */
+			script[patch[p].lwoff] += pa_reg;
+			break;
+		case 3:
+			/* data external */
+			lw = script[patch[p].lwoff];
+			v = (lw >> 8) & 0xff;
+			if (!(*externval)(c, v, &lv))
+				return 0;
+			v = lv & 0xff;
+			script[patch[p].lwoff] = (lw & 0xffff00ffL) | (v << 8);
+			break;
+		case 4:
+			/* 32 bit external */
+			lw = script[patch[p].lwoff];
+			if (!(*externval)(c, lw, &lv))
+				return 0;
+			script[patch[p].lwoff] = lv;
+			break;
+		case 5:
+			/* 24 bit external */
+			lw = script[patch[p].lwoff];
+			if (!(*externval)(c, lw & 0xffffff, &lv))
+				return 0;
+			script[patch[p].lwoff] = (lw & 0xff000000L) | (lv & 0xffffffL);
+			break;
+		}
+	}
+	return 1;
+}
+
+static SDev*
+sd53c8xxpnp(void)
+{
+	char *cp;
+	Pcidev *p;
+	Variant *v;
+	int ba, nctlr;
+	void *scriptma;
+	Controller *ctlr;
+	SDev *sdev, *head, *tail;
+	ulong regpa, *script, scriptpa;
+
+	if(cp = getconf("*maxsd53c8xx"))
+		nctlr = strtoul(cp, 0, 0);
+	else
+		nctlr = 32;
+
+	p = nil;
+	head = tail = nil;
+	while((p = pcimatch(p, NCR_VID, 0)) != nil && nctlr > 0){
+		for(v = variant; v < &variant[nelem(variant)]; v++){
+			if(p->did == v->did && p->rid <= v->maxrid)
+				break;
+		}
+		if(v >= &variant[nelem(variant)]) {
+			print("no match\n");
+			continue;
+		}
+		print(PRINTPREFIX "%s rev. 0x%2.2x intr=%d command=%4.4uX\n",
+			v->name, p->rid, p->intl, p->pcr);
+
+		regpa = p->mem[1].bar;
+		ba = 2;
+		if(regpa & 0x04){
+			if(p->mem[2].bar)
+				continue;
+			ba++;
+		}
+		regpa = upamalloc(regpa & ~0x0F, p->mem[1].size, 0);
+		if(regpa == 0)
+			continue;
+
+		script = nil;
+		scriptpa = 0;
+		scriptma = nil;
+		if((v->feature & LocalRAM) && sizeof(na_script) <= 4096){
+			scriptpa = p->mem[ba].bar;
+			if((scriptpa & 0x04) && p->mem[ba+1].bar){
+				upafree(regpa, p->mem[1].size);
+				continue;
+			}
+			scriptpa = upamalloc(scriptpa & ~0x0F,
+					p->mem[ba].size, 0);
+			if(scriptpa)
+				script = KADDR(scriptpa);
+		}
+		if(scriptpa == 0){
+			/*
+			 * Either the map failed, or this chip does not have
+			 * local RAM. It will need a copy of the microcode.
+			 */
+			scriptma = malloc(sizeof(na_script));
+			if(scriptma == nil){
+				upafree(regpa, p->mem[1].size);
+				continue;
+			}
+			scriptpa = DMASEG(scriptma);
+			script = scriptma;
+		}
+
+		ctlr = malloc(sizeof(Controller));
+		sdev = malloc(sizeof(SDev));
+		if(ctlr == nil || sdev == nil){
+buggery:
+			if(ctlr)
+				free(ctlr);
+			if(sdev)
+				free(sdev);
+			if(scriptma)
+				free(scriptma);
+			else
+				upafree(scriptpa, p->mem[ba].size);
+			upafree(regpa, p->mem[1].size);
+			continue;
+		}
+
+		ctlr->n = KADDR(regpa);
+		ctlr->v = v;
+		ctlr->script = script;
+		memmove(ctlr->script, na_script, sizeof(na_script));
+
+		/*
+		 * Because we don't yet have an abstraction for the
+		 * addresses as seen from the controller side (and on
+		 * the 386 it doesn't matter), the follwong two lines
+		 * are different between the 386 and alpha copies of
+		 * this driver.
+		 */
+		ctlr->scriptpa = scriptpa;
+		if(!na_fixup(ctlr, regpa, na_patches, NA_PATCHES, xfunc)){
+			print("script fixup failed\n");
+			goto buggery;
+		}
+		swabl(ctlr->script, ctlr->script, sizeof(na_script));
+
+		ctlr->dsalist.freechain = 0;
+		lesetl(ctlr->dsalist.head, 0);
+
+		ctlr->pcidev = p;
+
+		sdev->ifc = &sd53c8xxifc;
+		sdev->ctlr = ctlr;
+		if(!(v->feature & Wide))
+			sdev->nunit = 8;
+		else
+			sdev->nunit = MAXTARGET;
+		ctlr->sdev = sdev;
+
+		if(head != nil)
+			tail->next = sdev;
+		else
+			head = sdev;
+		tail = sdev;
+
+		nctlr--;
+	}
+
+	return head;
+}
+
+static SDev*
+sd53c8xxid(SDev* sdev)
+{
+	return scsiid(sdev, &sd53c8xxifc);
+}
+
+static int
+sd53c8xxenable(SDev* sdev)
+{
+	Pcidev *pcidev;
+	Controller *ctlr;
+	char name[32];
+
+	ctlr = sdev->ctlr;
+	pcidev = ctlr->pcidev;
+
+	pcisetbme(pcidev);
+	snprint(name, sizeof(name), "%s (%s)", sdev->name, sdev->ifc->name);
+	intrenable(pcidev->intl, sd53c8xxinterrupt, ctlr, pcidev->tbdf, name);
+
+	ilock(ctlr);
+	synctabinit(ctlr);
+	cribbios(ctlr);
+	reset(ctlr);
+	iunlock(ctlr);
+
+	return 1;
+}
+
+SDifc sd53c8xxifc = {
+	"53c8xx",			/* name */
+
+	sd53c8xxpnp,			/* pnp */
+	nil,				/* legacy */
+	sd53c8xxid,			/* id */
+	sd53c8xxenable,			/* enable */
+	nil,				/* disable */
+
+	scsiverify,			/* verify */
+	scsionline,			/* online */
+	sd53c8xxrio,			/* rio */
+	nil,				/* rctl */
+	nil,				/* wctl */
+
+	scsibio,			/* bio */
+	nil,				/* probe */
+	nil,				/* clear */
+	nil,				/* stat */
+};
--- /dev/null
+++ b/os/pc/sd53c8xx.i
@@ -1,0 +1,773 @@
+unsigned long na_script[] = {
+			/*	extern	scsi_id_buf */
+			/*	extern	msg_out_buf */
+			/*	extern	cmd_buf */
+			/*	extern	data_buf */
+			/*	extern	status_buf */
+			/*	extern	msgin_buf */
+			/*	extern	dsa_0 */
+			/*	extern  dsa_1 */
+			/*	extern	dsa_head */
+			/*	extern	ssid_mask */
+			/*	SIR_MSG_IO_COMPLETE = 0 */
+			/*	error_not_cmd_complete = 1 */
+			/*	error_disconnected = 2 */
+			/*	error_reselected = 3 */
+			/*	error_unexpected_phase = 4 */
+			/*	error_weird_message = 5 */
+			/*	SIR_ERROR_NOT_MSG_IN_AFTER_RESELECT = 6 */
+			/*	error_not_identify_after_reselect = 7 */
+			/*	error_too_much_data = 8 */
+			/*	error_too_little_data = 9 */
+			/*	SIR_MSG_REJECT = 10 */
+			/*	SIR_MSG_SDTR = 11 */
+			/*	SIR_EV_RESPONSE_OK = 12 */
+			/*	error_sigp_set = 13 */
+			/*	SIR_EV_PHASE_SWITCH_AFTER_ID = 14 */
+			/*	SIR_MSG_WDTR = 15 */
+			/*	SIR_MSG_IGNORE_WIDE_RESIDUE = 16 */
+			/*	SIR_NOTIFY_DISC = 100 */
+			/*	SIR_NOTIFY_RESELECT = 101 */
+			/*	SIR_NOTIFY_MSG_IN = 102 */
+			/*	SIR_NOTIFY_STATUS = 103 */
+			/*	SIR_NOTIFY_DUMP = 104 */
+			/*	SIR_NOTIFY_DUMP2 = 105 */
+			/*	SIR_NOTIFY_SIGP = 106 */
+			/*	SIR_NOTIFY_ISSUE = 107 */
+			/*	SIR_NOTIFY_WAIT_RESELECT = 108 */
+			/*	SIR_NOTIFY_ISSUE_CHECK = 109 */
+			/*	SIR_NOTIFY_DUMP_NEXT_CODE = 110 */
+			/*	SIR_NOTIFY_COMMAND = 111 */
+			/*	SIR_NOTIFY_DATA_IN = 112 */
+			/*	SIR_NOTIFY_DATA_OUT = 113 */
+			/*	SIR_NOTIFY_BLOCK_DATA_IN = 114 */
+			/*	SIR_NOTIFY_WSR = 115 */
+			/*	SIR_NOTIFY_LOAD_SYNC = 116 */
+			/*	SIR_NOTIFY_RESELECTED_ON_SELECT = 117 */
+			/*	STATE_FREE = 0 */
+			/*	STATE_ALLOCATED = 1 */
+			/*	STATE_ISSUE = 2 */
+			/*	STATE_DISCONNECTED = 3 */
+			/*	STATE_DONE = 4 */
+			/*	RESULT_OK = 0 */
+			/*	MSG_IDENTIFY = 0x80 */
+			/*	MSG_DISCONNECT = 0x04 */
+			/*	MSG_SAVE_DATA_POINTER = 0x02 */
+			/*	MSG_RESTORE_POINTERS = 0x03 */
+			/*	MSG_IGNORE_WIDE_RESIDUE = 0x23 */
+			/*	X_MSG = 0x01 */
+			/*	X_MSG_SDTR = 0x01 */
+			/*	X_MSG_WDTR = 0x03 */
+			/*	MSG_REJECT = 0x07 */
+			/*	BSIZE = 512 */
+/* 0000 */ 0x80880000L, /*		jump	wait_for_reselection */
+/* 0004 */ 0x00000514L,
+/* 0008 */ 0x88880000L, /*		call	load_sync */
+/* 000c */ 0x0000074cL,
+/* 0010 */ 0x60000200L, /*		clear	target */
+/* 0014 */ 0x00000000L,
+/* 0018 */ 0x47000000L, /*		select	atn from scsi_id_buf, reselected_on_select */
+/* 001c */ 0x000004ecL,
+/* 0020 */ 0x878b0000L, /*		jump	start1, when msg_in */
+/* 0024 */ 0x00000000L,
+/* 0028 */ 0x1e000000L, /*		move	from msg_out_buf, when msg_out */
+/* 002c */ 0x00000001L,
+/* 0030 */ 0x868b0000L, /*		jump	start1, when msg_out */
+/* 0034 */ 0x00fffff0L,
+/* 0038 */ 0x82830000L, /*		jump	to_decisions, when not cmd */
+/* 003c */ 0x000005f0L,
+/* 0040 */ 0x60000008L, /*		clear	atn */
+/* 0044 */ 0x00000000L,
+/* 0048 */ 0x1a000000L, /*		move	from cmd_buf, when cmd */
+/* 004c */ 0x00000002L,
+/* 0050 */ 0x81830000L, /*		jump	to_decisions, when not data_in */
+/* 0054 */ 0x000005d8L,
+/* 0058 */ 0xc0000004L, /*		move	memory 4, state, scratcha */
+/* 005c */ 0x00000678L,
+/* 0060 */ 0x00000034L,
+/* 0064 */ 0xc0000004L, /*		move	memory 4, dmaaddr, scratchb */
+/* 0068 */ 0x0000067cL,
+/* 006c */ 0x0000005cL,
+/* 0070 */ 0x72360000L, /*		move	scratcha2 to sfbr */
+/* 0074 */ 0x00000000L,
+/* 0078 */ 0x808c0000L, /*		jump	data_in_normal, if 0 */
+/* 007c */ 0x00000078L,
+/* 0080 */ 0x29000200L, /*		move	BSIZE, ptr dmaaddr, when data_in */
+/* 0084 */ 0x0000067cL,
+/* 0088 */ 0x7e5d0200L, /*		move	scratchb1 + BSIZE / 256 to scratchb1 */
+/* 008c */ 0x00000000L,
+/* 0090 */ 0x7f5e0000L, /*		move	scratchb2 + 0 to scratchb2 with carry */
+/* 0094 */ 0x00000000L,
+/* 0098 */ 0x7f5f0000L, /*		move	scratchb3 + 0 to scratchb3 with carry */
+/* 009c */ 0x00000000L,
+/* 00a0 */ 0x7e36ff00L, /*		move	scratcha2 + 255 to scratcha2 */
+/* 00a4 */ 0x00000000L,
+/* 00a8 */ 0xc0000004L, /*		move	memory 4, scratchb, dmaaddr */
+/* 00ac */ 0x0000005cL,
+/* 00b0 */ 0x0000067cL,
+/* 00b4 */ 0x818b0000L, /*		jump	data_in_block_loop, when data_in */
+/* 00b8 */ 0x00ffffb4L,
+/* 00bc */ 0xc0000004L, /*		move	memory 4, scratcha, state */
+/* 00c0 */ 0x00000034L,
+/* 00c4 */ 0x00000678L,
+/* 00c8 */ 0x88880000L, /*		call	save_state */
+/* 00cc */ 0x000005e0L,
+/* 00d0 */ 0x80880000L, /*		jump	to_decisions */
+/* 00d4 */ 0x00000558L,
+/* 00d8 */ 0xc0000004L, /*		move	memory 4, scratchb, dmaaddr */
+/* 00dc */ 0x0000005cL,
+/* 00e0 */ 0x0000067cL,
+/* 00e4 */ 0xc0000004L, /*		move	memory 4, scratcha, state */
+/* 00e8 */ 0x00000034L,
+/* 00ec */ 0x00000678L,
+/* 00f0 */ 0x80880000L, /*		jump	to_decisions */
+/* 00f4 */ 0x00000538L,
+/* 00f8 */ 0x72370000L, /*		move	scratcha3 to sfbr */
+/* 00fc */ 0x00000000L,
+/* 0100 */ 0x98040000L, /*		int	error_too_much_data, if not 0 */
+/* 0104 */ 0x00000008L,
+/* 0108 */ 0x19000000L, /*		move	from data_buf, when data_in */
+/* 010c */ 0x00000003L,
+/* 0110 */ 0x78370200L, /*		move	2 to scratcha3 */
+/* 0114 */ 0x00000000L,
+/* 0118 */ 0xc0000004L, /*		move	memory 4, scratcha, state */
+/* 011c */ 0x00000034L,
+/* 0120 */ 0x00000678L,
+/* 0124 */ 0x88880000L, /*		call	save_state */
+/* 0128 */ 0x00000584L,
+/* 012c */ 0x80880000L, /*		jump	post_data_to_decisions */
+/* 0130 */ 0x0000052cL,
+/* 0134 */ 0xc0000004L, /*		move	memory 4, state, scratcha */
+/* 0138 */ 0x00000678L,
+/* 013c */ 0x00000034L,
+/* 0140 */ 0xc0000004L, /*		move	memory 4, dmaaddr, scratchb */
+/* 0144 */ 0x0000067cL,
+/* 0148 */ 0x0000005cL,
+/* 014c */ 0x72360000L, /*		move	scratcha2 to sfbr */
+/* 0150 */ 0x00000000L,
+/* 0154 */ 0x808c0000L, /*		jump	data_out_normal, if 0 */
+/* 0158 */ 0x0000005cL,
+/* 015c */ 0xc0000004L, /*		move	memory 4, dmaaddr, scratchb */
+/* 0160 */ 0x0000067cL,
+/* 0164 */ 0x0000005cL,
+/* 0168 */ 0x28000200L, /*		move	BSIZE, ptr dmaaddr, when data_out */
+/* 016c */ 0x0000067cL,
+/* 0170 */ 0x7e5d0200L, /*		move	scratchb1 + BSIZE / 256 to scratchb1 */
+/* 0174 */ 0x00000000L,
+/* 0178 */ 0x7f5e0000L, /*		move	scratchb2 + 0 to scratchb2 with carry */
+/* 017c */ 0x00000000L,
+/* 0180 */ 0x7f5f0000L, /*		move	scratchb3 + 0 to scratchb3 with carry */
+/* 0184 */ 0x00000000L,
+/* 0188 */ 0x7e36ff00L, /*		move	scratcha2 + 255 to scratcha2 */
+/* 018c */ 0x00000000L,
+/* 0190 */ 0xc0000004L, /*		move	memory 4, scratchb, dmaaddr */
+/* 0194 */ 0x0000005cL,
+/* 0198 */ 0x0000067cL,
+/* 019c */ 0x808b0000L, /*		jump	data_out_block_loop, when data_out */
+/* 01a0 */ 0x00ffffa8L,
+/* 01a4 */ 0xc0000004L, /*		move	memory 4, scratcha, state */
+/* 01a8 */ 0x00000034L,
+/* 01ac */ 0x00000678L,
+/* 01b0 */ 0x80880000L, /*		jump	to_decisions */
+/* 01b4 */ 0x00000478L,
+/* 01b8 */ 0x72370000L, /*		move	scratcha3 to sfbr */
+/* 01bc */ 0x00000000L,
+/* 01c0 */ 0x98040000L, /*		int	error_too_little_data, if not 0 */
+/* 01c4 */ 0x00000009L,
+/* 01c8 */ 0x18000000L, /*		move	from data_buf, when data_out */
+/* 01cc */ 0x00000003L,
+/* 01d0 */ 0x78370200L, /*		move	2 to scratcha3 */
+/* 01d4 */ 0x00000000L,
+/* 01d8 */ 0xc0000004L, /*		move	memory 4, scratcha, state */
+/* 01dc */ 0x00000034L,
+/* 01e0 */ 0x00000678L,
+/* 01e4 */ 0x88880000L, /*		call	save_state */
+/* 01e8 */ 0x000004c4L,
+/* 01ec */ 0x80880000L, /*		jump	post_data_to_decisions */
+/* 01f0 */ 0x0000046cL,
+/* 01f4 */ 0x1b000000L, /*		move	from status_buf, when status */
+/* 01f8 */ 0x00000004L,
+/* 01fc */ 0x9f030000L, /*		int	error_unexpected_phase, when not msg_in */
+/* 0200 */ 0x00000004L,
+/* 0204 */ 0x0f000001L, /*		move	1, scratcha, when msg_in */
+/* 0208 */ 0x00000034L,
+/* 020c */ 0x808c0007L, /*		jump	rejected, if MSG_REJECT */
+/* 0210 */ 0x00000088L,
+/* 0214 */ 0x808c0004L, /*		jump	disconnected, if MSG_DISCONNECT */
+/* 0218 */ 0x00000298L,
+/* 021c */ 0x808c0002L, /*		jump	msg_in_skip, if MSG_SAVE_DATA_POINTER */
+/* 0220 */ 0x00000090L,
+/* 0224 */ 0x808c0003L, /*		jump	msg_in_skip, if MSG_RESTORE_POINTERS */
+/* 0228 */ 0x00000088L,
+/* 022c */ 0x808c0023L, /*		jump	ignore_wide, if MSG_IGNORE_WIDE_RESIDUE */
+/* 0230 */ 0x000001f0L,
+/* 0234 */ 0x808c0001L, /*		jump	extended, if X_MSG */
+/* 0238 */ 0x00000088L,
+/* 023c */ 0x98040000L, /*		int	error_not_cmd_complete, if not 0 */
+/* 0240 */ 0x00000001L,
+/* 0244 */ 0x7c027e00L, /*		move	scntl2&0x7e to scntl2 */
+/* 0248 */ 0x00000000L,
+/* 024c */ 0x60000040L, /*		clear	ack */
+/* 0250 */ 0x00000000L,
+/* 0254 */ 0x48000000L, /*		wait	disconnect */
+/* 0258 */ 0x00000000L,
+/* 025c */ 0xc0000004L, /*		move	memory 4, state, scratcha */
+/* 0260 */ 0x00000678L,
+/* 0264 */ 0x00000034L,
+/* 0268 */ 0x78340400L, /*		move	STATE_DONE to scratcha0 */
+/* 026c */ 0x00000000L,
+/* 0270 */ 0x78350000L, /*		move	RESULT_OK to scratcha1 */
+/* 0274 */ 0x00000000L,
+/* 0278 */ 0xc0000004L, /*		move	memory 4, scratcha, state */
+/* 027c */ 0x00000034L,
+/* 0280 */ 0x00000678L,
+/* 0284 */ 0x88880000L, /*		call	save_state */
+/* 0288 */ 0x00000424L,
+/* 028c */ 0x98180000L, /*		intfly	0 */
+/* 0290 */ 0x00000000L,
+/* 0294 */ 0x80880000L, /*		jump	issue_check */
+/* 0298 */ 0x0000043cL,
+/* 029c */ 0x98080000L, /*		int	SIR_MSG_REJECT */
+/* 02a0 */ 0x0000000aL,
+/* 02a4 */ 0x60000040L, /*		clear	ack */
+/* 02a8 */ 0x00000000L,
+/* 02ac */ 0x80880000L, /*		jump	to_decisions */
+/* 02b0 */ 0x0000037cL,
+/* 02b4 */ 0x60000040L, /*		clear	ack */
+/* 02b8 */ 0x00000000L,
+/* 02bc */ 0x80880000L, /*		jump	to_decisions */
+/* 02c0 */ 0x0000036cL,
+/* 02c4 */ 0x60000040L, /*		clear	ack */
+/* 02c8 */ 0x00000000L,
+/* 02cc */ 0x9f030000L, /*		int	error_unexpected_phase, when not msg_in */
+/* 02d0 */ 0x00000004L,
+/* 02d4 */ 0x0f000001L, /*		move	1, scratcha1, when msg_in */
+/* 02d8 */ 0x00000035L,
+/* 02dc */ 0x808c0003L, /*		jump	ext_3, if 3 */
+/* 02e0 */ 0x00000030L,
+/* 02e4 */ 0x808c0002L, /*		jump	ext_2, if 2 */
+/* 02e8 */ 0x00000098L,
+/* 02ec */ 0x98040001L, /*		int	error_weird_message, if not 1 */
+/* 02f0 */ 0x00000005L,
+/* 02f4 */ 0x60000040L, /*		clear	ack */
+/* 02f8 */ 0x00000000L,
+/* 02fc */ 0x9f030000L, /*		int	error_unexpected_phase, when not msg_in */
+/* 0300 */ 0x00000004L,
+/* 0304 */ 0x0f000001L, /*		move	1, scratcha1, when msg_in */
+/* 0308 */ 0x00000035L,
+/* 030c */ 0x80880000L, /*		jump	ext_done */
+/* 0310 */ 0x000000c8L,
+/* 0314 */ 0x60000040L, /*	ext_3:	clear	ack */
+/* 0318 */ 0x00000000L,
+/* 031c */ 0x9f030000L, /*		int	error_unexpected_phase, when not msg_in */
+/* 0320 */ 0x00000004L,
+/* 0324 */ 0x0f000001L, /*		move	1, scratcha1, when msg_in */
+/* 0328 */ 0x00000035L,
+/* 032c */ 0x60000040L, /*		clear	ack */
+/* 0330 */ 0x00000000L,
+/* 0334 */ 0x9f030000L, /*		int	error_unexpected_phase, when not msg_in */
+/* 0338 */ 0x00000004L,
+/* 033c */ 0x0f000001L, /*		move	1, scratcha2, when msg_in */
+/* 0340 */ 0x00000036L,
+/* 0344 */ 0x60000040L, /*		clear	ack */
+/* 0348 */ 0x00000000L,
+/* 034c */ 0x9f030000L, /*		int	error_unexpected_phase, when not msg_in */
+/* 0350 */ 0x00000004L,
+/* 0354 */ 0x0f000001L, /*		move	1, scratcha3, when msg_in */
+/* 0358 */ 0x00000037L,
+/* 035c */ 0x72350000L, /*		move	scratcha1 to sfbr */
+/* 0360 */ 0x00000000L,
+/* 0364 */ 0x80840001L, /*		jump	ext_done, if not X_MSG_SDTR */
+/* 0368 */ 0x00000070L,
+/* 036c */ 0x98080000L, /*	sdtr:	int	SIR_MSG_SDTR */
+/* 0370 */ 0x0000000bL,
+/* 0374 */ 0x60000040L, /*		clear	ack */
+/* 0378 */ 0x00000000L,
+/* 037c */ 0x80880000L, /*		jump	to_decisions */
+/* 0380 */ 0x000002acL,
+/* 0384 */ 0x60000040L, /*	ext_2:	clear	ack */
+/* 0388 */ 0x00000000L,
+/* 038c */ 0x9f030000L, /*		int	error_unexpected_phase, when not msg_in */
+/* 0390 */ 0x00000004L,
+/* 0394 */ 0x0f000001L, /*		move	1, scratcha1, when msg_in */
+/* 0398 */ 0x00000035L,
+/* 039c */ 0x60000040L, /*		clear	ack */
+/* 03a0 */ 0x00000000L,
+/* 03a4 */ 0x9f030000L, /*		int	error_unexpected_phase, when not msg_in */
+/* 03a8 */ 0x00000004L,
+/* 03ac */ 0x0f000001L, /*		move	1, scratcha2, when msg_in */
+/* 03b0 */ 0x00000036L,
+/* 03b4 */ 0x72350000L, /*		move	scratcha1 to sfbr */
+/* 03b8 */ 0x00000000L,
+/* 03bc */ 0x80840003L, /*		jump	ext_done, if not X_MSG_WDTR */
+/* 03c0 */ 0x00000018L,
+/* 03c4 */ 0x98080000L, /*	wdtr:	int	SIR_MSG_WDTR */
+/* 03c8 */ 0x0000000fL,
+/* 03cc */ 0x60000040L, /*		clear	ack */
+/* 03d0 */ 0x00000000L,
+/* 03d4 */ 0x80880000L, /*		jump	to_decisions */
+/* 03d8 */ 0x00000254L,
+/* 03dc */ 0x58000008L, /*		set	atn */
+/* 03e0 */ 0x00000000L,
+/* 03e4 */ 0x60000040L, /*		clear	ack */
+/* 03e8 */ 0x00000000L,
+/* 03ec */ 0x78340700L, /*		move	MSG_REJECT to scratcha */
+/* 03f0 */ 0x00000000L,
+/* 03f4 */ 0x9e030000L, /*		int	error_unexpected_phase, when not msg_out */
+/* 03f8 */ 0x00000004L,
+/* 03fc */ 0x60000008L, /*		clear	atn */
+/* 0400 */ 0x00000000L,
+/* 0404 */ 0x0e000001L, /*		move	1, scratcha, when msg_out */
+/* 0408 */ 0x00000034L,
+/* 040c */ 0x60000040L, /*		clear	ack */
+/* 0410 */ 0x00000000L,
+/* 0414 */ 0x868b0000L, /*		jump	reject, when msg_out */
+/* 0418 */ 0x00ffffc0L,
+/* 041c */ 0x80880000L, /*		jump	to_decisions */
+/* 0420 */ 0x0000020cL,
+/* 0424 */ 0x60000040L, /*		clear	ack */
+/* 0428 */ 0x00000000L,
+/* 042c */ 0x9f030000L, /*		int	error_unexpected_phase, when not msg_in */
+/* 0430 */ 0x00000004L,
+/* 0434 */ 0x0f000001L, /*		move	1, scratcha1, when msg_in */
+/* 0438 */ 0x00000035L,
+/* 043c */ 0x98080000L, /*		int	SIR_MSG_IGNORE_WIDE_RESIDUE */
+/* 0440 */ 0x00000010L,
+/* 0444 */ 0x60000040L, /*		clear	ack */
+/* 0448 */ 0x00000000L,
+/* 044c */ 0x80880000L, /*		jump	to_decisions */
+/* 0450 */ 0x000001dcL,
+/* 0454 */ 0x58000008L, /*		set	atn */
+/* 0458 */ 0x00000000L,
+/* 045c */ 0x60000040L, /*		clear	ack */
+/* 0460 */ 0x00000000L,
+/* 0464 */ 0x9e030000L, /*		int	error_unexpected_phase, when not msg_out */
+/* 0468 */ 0x00000004L,
+/* 046c */ 0x1e000000L, /*		move	from msg_out_buf, when msg_out */
+/* 0470 */ 0x00000001L,
+/* 0474 */ 0x868b0000L, /*		jump	response_repeat, when msg_out */
+/* 0478 */ 0x00fffff0L,
+/* 047c */ 0x878b0000L, /*		jump	response_msg_in, when msg_in */
+/* 0480 */ 0x00000010L,
+/* 0484 */ 0x98080000L, /*		int	SIR_EV_RESPONSE_OK */
+/* 0488 */ 0x0000000cL,
+/* 048c */ 0x80880000L, /*		jump	to_decisions */
+/* 0490 */ 0x0000019cL,
+/* 0494 */ 0x0f000001L, /*		move	1, scratcha, when msg_in */
+/* 0498 */ 0x00000034L,
+/* 049c */ 0x808c0007L, /*		jump	rejected, if MSG_REJECT */
+/* 04a0 */ 0x00fffdf8L,
+/* 04a4 */ 0x98080000L, /*		int	SIR_EV_RESPONSE_OK */
+/* 04a8 */ 0x0000000cL,
+/* 04ac */ 0x80880000L, /*		jump	msg_in_not_reject */
+/* 04b0 */ 0x00fffd60L,
+/* 04b4 */ 0x7c027e00L, /*		move	scntl2&0x7e to scntl2 */
+/* 04b8 */ 0x00000000L,
+/* 04bc */ 0x60000040L, /*		clear 	ack */
+/* 04c0 */ 0x00000000L,
+/* 04c4 */ 0x48000000L, /*		wait	disconnect */
+/* 04c8 */ 0x00000000L,
+/* 04cc */ 0xc0000004L, /*		move	memory 4, state, scratcha */
+/* 04d0 */ 0x00000678L,
+/* 04d4 */ 0x00000034L,
+/* 04d8 */ 0x78340300L, /*		move	STATE_DISCONNECTED to scratcha0 */
+/* 04dc */ 0x00000000L,
+/* 04e0 */ 0xc0000004L, /*		move	memory 4, scratcha, state */
+/* 04e4 */ 0x00000034L,
+/* 04e8 */ 0x00000678L,
+/* 04ec */ 0x88880000L, /*		call	save_state */
+/* 04f0 */ 0x000001bcL,
+/* 04f4 */ 0x74020100L, /*		move	scntl2&0x01 to sfbr */
+/* 04f8 */ 0x00000000L,
+/* 04fc */ 0x98040000L, /*		int	SIR_NOTIFY_WSR, if not 0 */
+/* 0500 */ 0x00000073L,
+/* 0504 */ 0x80880000L, /*		jump	issue_check */
+/* 0508 */ 0x000001ccL,
+/* 050c */ 0x98080000L, /*		int	SIR_NOTIFY_RESELECTED_ON_SELECT */
+/* 0510 */ 0x00000075L,
+/* 0514 */ 0x80880000L, /*		jump	reselected */
+/* 0518 */ 0x00000008L,
+/* 051c */ 0x54000000L, /*		wait reselect sigp_set */
+/* 0520 */ 0x000001acL,
+/* 0524 */ 0x60000200L, /*		clear	target */
+/* 0528 */ 0x00000000L,
+/* 052c */ 0x9f030000L, /*		int	SIR_ERROR_NOT_MSG_IN_AFTER_RESELECT, when not msg_in */
+/* 0530 */ 0x00000006L,
+/* 0534 */ 0x0f000001L, /*		move	1, scratchb, when msg_in */
+/* 0538 */ 0x0000005cL,
+/* 053c */ 0x98041f80L, /*		int	error_not_identify_after_reselect, if not MSG_IDENTIFY and mask 0x1f */
+/* 0540 */ 0x00000007L,
+/* 0544 */ 0xc0000004L, /*	 	move	memory 4, dsa_head, dsa */
+/* 0548 */ 0x00000008L,
+/* 054c */ 0x00000010L,
+/* 0550 */ 0x72100000L, /*		move	dsa0 to sfbr */
+/* 0554 */ 0x00000000L,
+/* 0558 */ 0x80840000L, /*		jump	find_dsa_1, if not 0 */
+/* 055c */ 0x00000030L,
+/* 0560 */ 0x72110000L, /*		move	dsa1 to sfbr */
+/* 0564 */ 0x00000000L,
+/* 0568 */ 0x80840000L, /*		jump	find_dsa_1, if not 0 */
+/* 056c */ 0x00000020L,
+/* 0570 */ 0x72120000L, /*		move	dsa2 to sfbr */
+/* 0574 */ 0x00000000L,
+/* 0578 */ 0x80840000L, /*		jump	find_dsa_1, if not 0 */
+/* 057c */ 0x00000010L,
+/* 0580 */ 0x72130000L, /*		move	dsa3 to sfbr */
+/* 0584 */ 0x00000000L,
+/* 0588 */ 0x980c0000L, /*		int	error_reselected, if 0 */
+/* 058c */ 0x00000003L,
+/* 0590 */ 0x88880000L, /*		call	load_state */
+/* 0594 */ 0x000000f8L,
+/* 0598 */ 0xc0000004L, /*		move	memory 4, state, scratcha */
+/* 059c */ 0x00000678L,
+/* 05a0 */ 0x00000034L,
+/* 05a4 */ 0x72340000L, /*		move	scratcha0 to sfbr */
+/* 05a8 */ 0x00000000L,
+/* 05ac */ 0x80840003L, /*		jump	find_dsa_next, if not STATE_DISCONNECTED */
+/* 05b0 */ 0x00000038L,
+/* 05b4 */ 0x740a0900L, /*		move	ssid & ssid_mask to sfbr */
+/* 05b8 */ 0x00000000L,
+/* 05bc */ 0xc0000001L, /*		move	memory 1, targ, find_dsa_smc1 */
+/* 05c0 */ 0x00000680L,
+/* 05c4 */ 0x000005c8L,
+/* 05c8 */ 0x808400ffL, /*		jump	find_dsa_next, if not 255 */
+/* 05cc */ 0x0000001cL,
+/* 05d0 */ 0xc0000001L, /*		move	memory 1, lun, find_dsa_smc2 */
+/* 05d4 */ 0x00000684L,
+/* 05d8 */ 0x000005e4L,
+/* 05dc */ 0x725c0000L, /*		move	scratchb0 to sfbr */
+/* 05e0 */ 0x00000000L,
+/* 05e4 */ 0x808cf8ffL, /*		jump	reload_sync, if 255 and mask ~7 */
+/* 05e8 */ 0x00000034L,
+/* 05ec */ 0xc0000004L, /*		move	memory 4, next, dsa */
+/* 05f0 */ 0x0000068cL,
+/* 05f4 */ 0x00000010L,
+/* 05f8 */ 0x80880000L, /*		jump	find_dsa_loop */
+/* 05fc */ 0x00ffff50L,
+/* 0600 */ 0x60000008L, /*		clear	atn */
+/* 0604 */ 0x00000000L,
+/* 0608 */ 0x878b0000L, /*	        jump    msg_in_phase, when msg_in */
+/* 060c */ 0x00fffbf4L,
+/* 0610 */ 0x98080000L, /*	        int     SIR_MSG_REJECT */
+/* 0614 */ 0x0000000aL,
+/* 0618 */ 0x80880000L, /*	        jump    to_decisions */
+/* 061c */ 0x00000010L,
+/* 0620 */ 0x88880000L, /*		call	load_sync */
+/* 0624 */ 0x00000134L,
+/* 0628 */ 0x60000040L, /*		clear	ack */
+/* 062c */ 0x00000000L,
+/* 0630 */ 0x818b0000L, /*		jump	data_in_phase, when data_in */
+/* 0634 */ 0x00fffa20L,
+/* 0638 */ 0x828a0000L, /*		jump	cmd_phase, if cmd */
+/* 063c */ 0x00fffa00L,
+/* 0640 */ 0x808a0000L, /*		jump	data_out_phase, if data_out */
+/* 0644 */ 0x00fffaecL,
+/* 0648 */ 0x838a0000L, /*		jump	status_phase, if status */
+/* 064c */ 0x00fffba4L,
+/* 0650 */ 0x878a0000L, /*		jump	msg_in_phase, if msg_in */
+/* 0654 */ 0x00fffbacL,
+/* 0658 */ 0x98080000L, /*		int	error_unexpected_phase */
+/* 065c */ 0x00000004L,
+/* 0660 */ 0x838b0000L, /*		jump	status_phase, when status */
+/* 0664 */ 0x00fffb8cL,
+/* 0668 */ 0x878a0000L, /*		jump	msg_in_phase, if msg_in */
+/* 066c */ 0x00fffb94L,
+/* 0670 */ 0x98080000L, /*		int	error_unexpected_phase */
+/* 0674 */ 0x00000004L,
+/* 0678 */ 0x00000000L, /*	state:	defw	0 */
+/* 067c */ 0x00000000L, /*	dmaaddr: defw	0 */
+/* 0680 */ 0x00000000L, /*	targ:	defw	0 */
+/* 0684 */ 0x00000000L, /*	lun:	defw	0 */
+/* 0688 */ 0x00000000L, /*	sync:	defw	0 */
+/* 068c */ 0x00000000L, /*	next:	defw	0 */
+			/*	dsa_load_len = dsa_load_end - dsa_copy */
+			/*	dsa_save_len = dsa_save_end - dsa_copy */
+/* 0690 */ 0xc0000004L, /*		move	memory 4, dsa, load_state_smc0 + 4 */
+/* 0694 */ 0x00000010L,
+/* 0698 */ 0x000006a0L,
+/* 069c */ 0xc0000018L, /*		move	memory dsa_load_len, 0, dsa_copy */
+/* 06a0 */ 0x00000000L,
+/* 06a4 */ 0x00000678L,
+/* 06a8 */ 0x90080000L, /*		return */
+/* 06ac */ 0x00000000L,
+/* 06b0 */ 0xc0000004L, /*		move	memory 4, dsa, save_state_smc0 + 8 */
+/* 06b4 */ 0x00000010L,
+/* 06b8 */ 0x000006c4L,
+/* 06bc */ 0xc0000008L, /*		move	memory dsa_save_len, dsa_copy, 0 */
+/* 06c0 */ 0x00000678L,
+/* 06c4 */ 0x00000000L,
+/* 06c8 */ 0x90080000L, /*		return */
+/* 06cc */ 0x00000000L,
+/* 06d0 */ 0x721a0000L, /*		move	ctest2 to sfbr */
+/* 06d4 */ 0x00000000L,
+/* 06d8 */ 0xc0000004L, /*		move	memory 4, dsa_head, dsa */
+/* 06dc */ 0x00000008L,
+/* 06e0 */ 0x00000010L,
+/* 06e4 */ 0x72100000L, /*		move	dsa0 to sfbr */
+/* 06e8 */ 0x00000000L,
+/* 06ec */ 0x80840000L, /*		jump	issue_check_1, if not 0 */
+/* 06f0 */ 0x00000030L,
+/* 06f4 */ 0x72110000L, /*		move	dsa1 to sfbr */
+/* 06f8 */ 0x00000000L,
+/* 06fc */ 0x80840000L, /*		jump	issue_check_1, if not 0 */
+/* 0700 */ 0x00000020L,
+/* 0704 */ 0x72120000L, /*		move	dsa2 to sfbr */
+/* 0708 */ 0x00000000L,
+/* 070c */ 0x80840000L, /*		jump	issue_check_1, if not 0 */
+/* 0710 */ 0x00000010L,
+/* 0714 */ 0x72130000L, /*		move	dsa3 to sfbr */
+/* 0718 */ 0x00000000L,
+/* 071c */ 0x808c0000L, /*		jump	wait_for_reselection, if 0 */
+/* 0720 */ 0x00fffdf8L,
+/* 0724 */ 0x88880000L, /*	 	call	load_state */
+/* 0728 */ 0x00ffff64L,
+/* 072c */ 0xc0000004L, /*		move	memory 4, state, scratcha */
+/* 0730 */ 0x00000678L,
+/* 0734 */ 0x00000034L,
+/* 0738 */ 0x72340000L, /*		move	scratcha0 to sfbr */
+/* 073c */ 0x00000000L,
+/* 0740 */ 0x808c0002L, /*		jump	start, if STATE_ISSUE */
+/* 0744 */ 0x00fff8c0L,
+/* 0748 */ 0xc0000004L, /*		move	memory 4, next, dsa */
+/* 074c */ 0x0000068cL,
+/* 0750 */ 0x00000010L,
+/* 0754 */ 0x80880000L, /*		jump	issue_check_loop */
+/* 0758 */ 0x00ffff88L,
+/* 075c */ 0xc0000004L, /*		move	memory 4, sync, scratcha */
+/* 0760 */ 0x00000688L,
+/* 0764 */ 0x00000034L,
+/* 0768 */ 0x72340000L, /*		move	scratcha0 to sfbr */
+/* 076c */ 0x00000000L,
+/* 0770 */ 0x6a030000L, /*		move	sfbr to scntl3 */
+/* 0774 */ 0x00000000L,
+/* 0778 */ 0x72350000L, /*		move	scratcha1 to sfbr */
+/* 077c */ 0x00000000L,
+/* 0780 */ 0x6a050000L, /*		move	sfbr to sxfer */
+/* 0784 */ 0x00000000L,
+/* 0788 */ 0x90080000L, /*		return */
+/* 078c */ 0x00000000L,
+};
+
+#define NA_SCRIPT_SIZE 484
+
+struct na_patch na_patches[] = {
+	{ 0x0006, 5 }, /* 00000018 */
+	{ 0x000b, 4 }, /* 0000002c */
+	{ 0x0013, 4 }, /* 0000004c */
+	{ 0x0017, 1 }, /* 0000005c */
+	{ 0x0018, 2 }, /* 00000060 */
+	{ 0x001a, 1 }, /* 00000068 */
+	{ 0x001b, 2 }, /* 0000006c */
+	{ 0x0021, 1 }, /* 00000084 */
+	{ 0x002b, 2 }, /* 000000ac */
+	{ 0x002c, 1 }, /* 000000b0 */
+	{ 0x0030, 2 }, /* 000000c0 */
+	{ 0x0031, 1 }, /* 000000c4 */
+	{ 0x0037, 2 }, /* 000000dc */
+	{ 0x0038, 1 }, /* 000000e0 */
+	{ 0x003a, 2 }, /* 000000e8 */
+	{ 0x003b, 1 }, /* 000000ec */
+	{ 0x0043, 4 }, /* 0000010c */
+	{ 0x0047, 2 }, /* 0000011c */
+	{ 0x0048, 1 }, /* 00000120 */
+	{ 0x004e, 1 }, /* 00000138 */
+	{ 0x004f, 2 }, /* 0000013c */
+	{ 0x0051, 1 }, /* 00000144 */
+	{ 0x0052, 2 }, /* 00000148 */
+	{ 0x0058, 1 }, /* 00000160 */
+	{ 0x0059, 2 }, /* 00000164 */
+	{ 0x005b, 1 }, /* 0000016c */
+	{ 0x0065, 2 }, /* 00000194 */
+	{ 0x0066, 1 }, /* 00000198 */
+	{ 0x006a, 2 }, /* 000001a8 */
+	{ 0x006b, 1 }, /* 000001ac */
+	{ 0x0073, 4 }, /* 000001cc */
+	{ 0x0077, 2 }, /* 000001dc */
+	{ 0x0078, 1 }, /* 000001e0 */
+	{ 0x007e, 4 }, /* 000001f8 */
+	{ 0x0082, 2 }, /* 00000208 */
+	{ 0x0098, 1 }, /* 00000260 */
+	{ 0x0099, 2 }, /* 00000264 */
+	{ 0x009f, 2 }, /* 0000027c */
+	{ 0x00a0, 1 }, /* 00000280 */
+	{ 0x00b6, 2 }, /* 000002d8 */
+	{ 0x00c2, 2 }, /* 00000308 */
+	{ 0x00ca, 2 }, /* 00000328 */
+	{ 0x00d0, 2 }, /* 00000340 */
+	{ 0x00d6, 2 }, /* 00000358 */
+	{ 0x00e6, 2 }, /* 00000398 */
+	{ 0x00ec, 2 }, /* 000003b0 */
+	{ 0x0102, 2 }, /* 00000408 */
+	{ 0x010e, 2 }, /* 00000438 */
+	{ 0x011c, 4 }, /* 00000470 */
+	{ 0x0126, 2 }, /* 00000498 */
+	{ 0x0134, 1 }, /* 000004d0 */
+	{ 0x0135, 2 }, /* 000004d4 */
+	{ 0x0139, 2 }, /* 000004e4 */
+	{ 0x013a, 1 }, /* 000004e8 */
+	{ 0x014e, 2 }, /* 00000538 */
+	{ 0x0152, 4 }, /* 00000548 */
+	{ 0x0153, 2 }, /* 0000054c */
+	{ 0x0167, 1 }, /* 0000059c */
+	{ 0x0168, 2 }, /* 000005a0 */
+	{ 0x016d, 3 }, /* 000005b4 */
+	{ 0x0170, 1 }, /* 000005c0 */
+	{ 0x0171, 1 }, /* 000005c4 */
+	{ 0x0175, 1 }, /* 000005d4 */
+	{ 0x0176, 1 }, /* 000005d8 */
+	{ 0x017c, 1 }, /* 000005f0 */
+	{ 0x017d, 2 }, /* 000005f4 */
+	{ 0x01a5, 2 }, /* 00000694 */
+	{ 0x01a6, 1 }, /* 00000698 */
+	{ 0x01a9, 1 }, /* 000006a4 */
+	{ 0x01ad, 2 }, /* 000006b4 */
+	{ 0x01ae, 1 }, /* 000006b8 */
+	{ 0x01b0, 1 }, /* 000006c0 */
+	{ 0x01b7, 4 }, /* 000006dc */
+	{ 0x01b8, 2 }, /* 000006e0 */
+	{ 0x01cc, 1 }, /* 00000730 */
+	{ 0x01cd, 2 }, /* 00000734 */
+	{ 0x01d3, 1 }, /* 0000074c */
+	{ 0x01d4, 2 }, /* 00000750 */
+	{ 0x01d8, 1 }, /* 00000760 */
+	{ 0x01d9, 2 }, /* 00000764 */
+};
+#define NA_PATCHES 80
+
+enum na_external {
+	X_scsi_id_buf,
+	X_msg_out_buf,
+	X_cmd_buf,
+	X_data_buf,
+	X_status_buf,
+	X_msgin_buf,
+	X_dsa_0,
+	X_dsa_1,
+	X_dsa_head,
+	X_ssid_mask,
+};
+
+enum {
+	E_issue_check_next = 1864,
+	E_issue_check_1 = 1828,
+	E_issue_check_loop = 1764,
+	E_save_state_smc0 = 1724,
+	E_load_state_smc0 = 1692,
+	E_dsa_load_end = 1680,
+	E_sync = 1672,
+	E_dsa_save_end = 1664,
+	E_dsa_copy = 1656,
+	E_id_out_mismatch_recover = 1536,
+	E_next = 1676,
+	E_reload_sync = 1568,
+	E_find_dsa_smc2 = 1508,
+	E_lun = 1668,
+	E_find_dsa_smc1 = 1480,
+	E_targ = 1664,
+	E_find_dsa_next = 1516,
+	E_load_state = 1680,
+	E_find_dsa_1 = 1424,
+	E_find_dsa_loop = 1360,
+	E_find_dsa = 1348,
+	E_sigp_set = 1744,
+	E_reselected = 1316,
+	E_wsr_check = 1268,
+	E_response_msg_in = 1172,
+	E_response_repeat = 1132,
+	E_response = 1108,
+	E_reject = 988,
+	E_wdtr = 964,
+	E_sdtr = 876,
+	E_ext_done = 988,
+	E_ext_1 = 756,
+	E_ext_2 = 900,
+	E_ext_3 = 788,
+	E_issue_check = 1752,
+	E_extended = 708,
+	E_ignore_wide = 1060,
+	E_msg_in_skip = 692,
+	E_disconnected = 1204,
+	E_msg_in_not_reject = 532,
+	E_rejected = 668,
+	E_msg_in_phase = 516,
+	E_status_phase = 500,
+	E_data_out_mismatch = 464,
+	E_data_out_block_mismatch = 368,
+	E_data_out_normal = 440,
+	E_data_out_block_loop = 332,
+	E_data_out_phase = 308,
+	E_post_data_to_decisions = 1632,
+	E_data_in_mismatch = 272,
+	E_data_mismatch_recover = 228,
+	E_data_block_mismatch_recover = 216,
+	E_save_state = 1712,
+	E_data_in_block_mismatch = 136,
+	E_data_in_normal = 248,
+	E_data_in_block_loop = 112,
+	E_dmaaddr = 1660,
+	E_state = 1656,
+	E_data_in_phase = 88,
+	E_cmd_out_mismatch = 80,
+	E_cmd_phase = 64,
+	E_to_decisions = 1584,
+	E_id_out_mismatch = 48,
+	E_start1 = 40,
+	E_reselected_on_select = 1292,
+	E_load_sync = 1884,
+	E_start = 8,
+	E_wait_for_reselection = 1308,
+	E_idle = 0,
+};
+#define A_dsa_save_len 8
+#define A_dsa_load_len 24
+#define A_BSIZE 512
+#define A_MSG_REJECT 7
+#define A_X_MSG_WDTR 3
+#define A_X_MSG_SDTR 1
+#define A_X_MSG 1
+#define A_MSG_IGNORE_WIDE_RESIDUE 35
+#define A_MSG_RESTORE_POINTERS 3
+#define A_MSG_SAVE_DATA_POINTER 2
+#define A_MSG_DISCONNECT 4
+#define A_MSG_IDENTIFY 128
+#define A_RESULT_OK 0
+#define A_STATE_DONE 4
+#define A_STATE_DISCONNECTED 3
+#define A_STATE_ISSUE 2
+#define A_STATE_ALLOCATED 1
+#define A_STATE_FREE 0
+#define A_SIR_NOTIFY_RESELECTED_ON_SELECT 117
+#define A_SIR_NOTIFY_LOAD_SYNC 116
+#define A_SIR_NOTIFY_WSR 115
+#define A_SIR_NOTIFY_BLOCK_DATA_IN 114
+#define A_SIR_NOTIFY_DATA_OUT 113
+#define A_SIR_NOTIFY_DATA_IN 112
+#define A_SIR_NOTIFY_COMMAND 111
+#define A_SIR_NOTIFY_DUMP_NEXT_CODE 110
+#define A_SIR_NOTIFY_ISSUE_CHECK 109
+#define A_SIR_NOTIFY_WAIT_RESELECT 108
+#define A_SIR_NOTIFY_ISSUE 107
+#define A_SIR_NOTIFY_SIGP 106
+#define A_SIR_NOTIFY_DUMP2 105
+#define A_SIR_NOTIFY_DUMP 104
+#define A_SIR_NOTIFY_STATUS 103
+#define A_SIR_NOTIFY_MSG_IN 102
+#define A_SIR_NOTIFY_RESELECT 101
+#define A_SIR_NOTIFY_DISC 100
+#define A_SIR_MSG_IGNORE_WIDE_RESIDUE 16
+#define A_SIR_MSG_WDTR 15
+#define A_SIR_EV_PHASE_SWITCH_AFTER_ID 14
+#define A_error_sigp_set 13
+#define A_SIR_EV_RESPONSE_OK 12
+#define A_SIR_MSG_SDTR 11
+#define A_SIR_MSG_REJECT 10
+#define A_error_too_little_data 9
+#define A_error_too_much_data 8
+#define A_error_not_identify_after_reselect 7
+#define A_SIR_ERROR_NOT_MSG_IN_AFTER_RESELECT 6
+#define A_error_weird_message 5
+#define A_error_unexpected_phase 4
+#define A_error_reselected 3
+#define A_error_disconnected 2
+#define A_error_not_cmd_complete 1
+#define A_SIR_MSG_IO_COMPLETE 0
--- /dev/null
+++ b/os/pc/sd53c8xx.n
@@ -1,0 +1,448 @@
+// NCR 53c8xx driver for Plan 9
+// Nigel Roles (nigel@9fs.org)
+//
+// Microcode
+//
+// 27/5/02	Fixed problems with transfers >= 256 * 512
+//
+// 13/3/01	Fixed microcode to support targets > 7
+//
+
+extern	scsi_id_buf
+extern	msg_out_buf
+extern	cmd_buf
+extern	data_buf
+extern	status_buf
+extern	msgin_buf
+extern	dsa_0
+extern  dsa_1
+extern	dsa_head
+extern	ssid_mask
+
+SIR_MSG_IO_COMPLETE = 0
+error_not_cmd_complete = 1
+error_disconnected = 2
+error_reselected = 3
+error_unexpected_phase = 4
+error_weird_message = 5
+SIR_ERROR_NOT_MSG_IN_AFTER_RESELECT = 6
+error_not_identify_after_reselect = 7
+error_too_much_data = 8
+error_too_little_data = 9
+SIR_MSG_REJECT = 10
+SIR_MSG_SDTR = 11
+SIR_EV_RESPONSE_OK = 12
+error_sigp_set = 13
+SIR_EV_PHASE_SWITCH_AFTER_ID = 14
+SIR_MSG_WDTR = 15
+SIR_MSG_IGNORE_WIDE_RESIDUE = 16
+SIR_NOTIFY_DISC = 100
+SIR_NOTIFY_RESELECT = 101
+SIR_NOTIFY_MSG_IN = 102
+SIR_NOTIFY_STATUS = 103
+SIR_NOTIFY_DUMP = 104
+SIR_NOTIFY_DUMP2 = 105
+SIR_NOTIFY_SIGP = 106
+SIR_NOTIFY_ISSUE = 107
+SIR_NOTIFY_WAIT_RESELECT = 108
+SIR_NOTIFY_ISSUE_CHECK = 109
+SIR_NOTIFY_DUMP_NEXT_CODE = 110
+SIR_NOTIFY_COMMAND = 111
+SIR_NOTIFY_DATA_IN = 112
+SIR_NOTIFY_DATA_OUT = 113
+SIR_NOTIFY_BLOCK_DATA_IN = 114
+SIR_NOTIFY_WSR = 115
+SIR_NOTIFY_LOAD_SYNC = 116
+SIR_NOTIFY_RESELECTED_ON_SELECT = 117
+
+STATE_FREE = 0
+STATE_ALLOCATED = 1
+STATE_ISSUE = 2
+STATE_DISCONNECTED = 3
+STATE_DONE = 4
+
+RESULT_OK = 0
+	
+MSG_IDENTIFY = 0x80
+MSG_DISCONNECT = 0x04
+MSG_SAVE_DATA_POINTER = 0x02
+MSG_RESTORE_POINTERS = 0x03
+MSG_IGNORE_WIDE_RESIDUE = 0x23
+X_MSG = 0x01
+X_MSG_SDTR = 0x01
+X_MSG_WDTR = 0x03
+MSG_REJECT = 0x07
+
+BSIZE = 512
+//BSIZE=4096
+
+idle:
+	jump	wait_for_reselection	
+start:
+	call	load_sync
+//	move	13 to ctest0
+//	int	SIR_NOTIFY_ISSUE
+	clear	target
+	select	atn from scsi_id_buf, reselected_on_select // do I need to clear ATN here?
+	jump	start1, when msg_in
+start1:
+//	move	14 to ctest0
+	move	from msg_out_buf, when msg_out
+id_out_mismatch:
+	jump	start1, when msg_out		// repeat on parity grounds
+	jump	to_decisions, when not cmd
+cmd_phase:
+//	int	SIR_NOTIFY_COMMAND
+	clear	atn
+	move	from cmd_buf, when cmd
+cmd_out_mismatch:
+	jump	to_decisions, when not data_in
+data_in_phase:
+	move	memory 4, state, scratcha
+	move	memory 4, dmaaddr, scratchb
+//	int	SIR_NOTIFY_DATA_IN
+data_in_block_loop:
+	move	scratcha2 to sfbr
+	jump	data_in_normal, if 0
+//	int	SIR_NOTIFY_BLOCK_DATA_IN
+	move	BSIZE, ptr dmaaddr, when data_in		// transfer BSIZE bytes
+data_in_block_mismatch:
+	move	scratchb1 + BSIZE / 256 to scratchb1		// add BSIZE to scratchb
+	move	scratchb2 + 0 to scratchb2 with carry
+	move	scratchb3 + 0 to scratchb3 with carry
+	move	scratcha2 + 255 to scratcha2			// sub one from block count
+	move	memory 4, scratchb, dmaaddr			// save latest dmaddr
+	jump	data_in_block_loop, when data_in
+	move	memory 4, scratcha, state			// save latest state
+	call	save_state
+	jump	to_decisions
+data_block_mismatch_recover:
+	move	memory 4, scratchb, dmaaddr			// save latest dmaddr
+data_mismatch_recover:
+	move	memory 4, scratcha, state			// save latest state
+	jump	to_decisions					// no need to save
+								// as interrupt routine
+								// did this
+data_in_normal:
+	move	scratcha3 to sfbr
+	int	error_too_much_data, if not 0
+	move	from data_buf, when data_in
+data_in_mismatch:
+	move	2 to scratcha3
+	move	memory 4, scratcha, state
+	call	save_state
+	jump	post_data_to_decisions
+data_out_phase:
+//	int	SIR_NOTIFY_DATA_OUT
+	move	memory 4, state, scratcha
+	move	memory 4, dmaaddr, scratchb
+data_out_block_loop:
+	move	scratcha2 to sfbr
+	jump	data_out_normal, if 0
+	move	memory 4, dmaaddr, scratchb
+	move	BSIZE, ptr dmaaddr, when data_out		// transfer BSIZE bytes
+data_out_block_mismatch:
+	move	scratchb1 + BSIZE / 256 to scratchb1		// add BSIZE to scratchb
+	move	scratchb2 + 0 to scratchb2 with carry
+	move	scratchb3 + 0 to scratchb3 with carry
+	move	scratcha2 + 255 to scratcha2			// sub one from block count
+	move	memory 4, scratchb, dmaaddr			// save latest dmaddr
+	jump	data_out_block_loop, when data_out
+	move	memory 4, scratcha, state			// save latest state
+	jump	to_decisions
+data_out_normal:
+	move	scratcha3 to sfbr
+	int	error_too_little_data, if not 0
+	move	from data_buf, when data_out
+data_out_mismatch:
+	move	2 to scratcha3
+	move	memory 4, scratcha, state
+	call	save_state
+	jump	post_data_to_decisions
+status_phase:
+	move	from status_buf, when status
+//	int	SIR_NOTIFY_STATUS
+	int	error_unexpected_phase, when not msg_in
+msg_in_phase:
+	move	1, scratcha, when msg_in
+//	int	SIR_NOTIFY_MSG_IN
+	jump	rejected, if MSG_REJECT
+msg_in_not_reject:
+	jump	disconnected, if MSG_DISCONNECT
+	jump	msg_in_skip, if MSG_SAVE_DATA_POINTER
+	jump	msg_in_skip, if MSG_RESTORE_POINTERS
+	jump	ignore_wide, if MSG_IGNORE_WIDE_RESIDUE
+	jump	extended, if X_MSG
+	int	error_not_cmd_complete, if not 0
+	move	scntl2&0x7e to scntl2			// take care not to clear WSR
+	clear	ack
+	wait	disconnect
+	// update state
+	move	memory 4, state, scratcha
+	move	STATE_DONE to scratcha0
+	move	RESULT_OK to scratcha1
+	move	memory 4, scratcha, state
+	call	save_state
+//	int	SIR_MSG_IO_COMPLETE
+	intfly	0
+	jump	issue_check
+
+rejected:
+	int	SIR_MSG_REJECT
+	clear	ack
+	jump	to_decisions
+msg_in_skip:
+	clear	ack
+	jump	to_decisions
+	
+extended:
+	clear	ack
+	int	error_unexpected_phase, when not msg_in
+	move	1, scratcha1, when msg_in
+	jump	ext_3, if 3
+	jump	ext_2, if 2
+	int	error_weird_message, if not 1
+ext_1:
+	clear	ack
+	int	error_unexpected_phase, when not msg_in
+	move	1, scratcha1, when msg_in
+	jump	ext_done
+
+ext_3:	clear	ack
+	int	error_unexpected_phase, when not msg_in
+	move	1, scratcha1, when msg_in
+	clear	ack
+	int	error_unexpected_phase, when not msg_in
+	move	1, scratcha2, when msg_in
+	clear	ack
+	int	error_unexpected_phase, when not msg_in
+	move	1, scratcha3, when msg_in
+	move	scratcha1 to sfbr
+	jump	ext_done, if not X_MSG_SDTR
+
+// the target sent SDTR - leave ACK asserted and signal kernel
+// kernel will either restart at reject, or continue
+sdtr:	int	SIR_MSG_SDTR
+	clear	ack
+	jump	to_decisions
+
+ext_2:	clear	ack
+	int	error_unexpected_phase, when not msg_in
+	move	1, scratcha1, when msg_in
+	clear	ack
+	int	error_unexpected_phase, when not msg_in
+	move	1, scratcha2, when msg_in
+	move	scratcha1 to sfbr
+	jump	ext_done, if not X_MSG_WDTR
+
+wdtr:	int	SIR_MSG_WDTR
+	clear	ack
+	jump	to_decisions
+
+ext_done:
+//	ought to check message here, but instead reject all
+//	NB ATN set
+reject:
+	set	atn					// get target's ATN
+	clear	ack					// finish ACK
+	move	MSG_REJECT to scratcha			// prepare message
+	int	error_unexpected_phase, when not msg_out// didn't get ATN
+	clear	atn					// last byte coming
+	move	1, scratcha, when msg_out		// send byte
+	clear	ack					// finish ACK
+	jump	reject, when msg_out			// parity error
+	jump	to_decisions
+	
+ignore_wide:
+	clear	ack
+	int	error_unexpected_phase, when not msg_in
+	move	1, scratcha1, when msg_in
+	int	SIR_MSG_IGNORE_WIDE_RESIDUE
+	clear	ack
+	jump	to_decisions
+
+//	sends a response to a message
+response:
+	set	atn
+	clear	ack
+	int	error_unexpected_phase, when not msg_out
+response_repeat:
+	move	from msg_out_buf, when msg_out
+	jump	response_repeat, when msg_out		// repeat on parity grounds
+// now look for response
+// msg_in could be a REJECT
+// anything other message is something else so signal kernel first
+	jump	response_msg_in, when msg_in
+	int	SIR_EV_RESPONSE_OK			// not a MSG_IN so OK
+	jump	to_decisions
+
+response_msg_in:
+	move	1, scratcha, when msg_in
+	jump	rejected, if MSG_REJECT		// go and generate rej interrupt
+	int	SIR_EV_RESPONSE_OK		// not a REJECT so OK
+	jump	msg_in_not_reject		// try others
+
+disconnected:
+//	move	5 to ctest0
+	move	scntl2&0x7e to scntl2			// don't clear WSR
+	clear 	ack
+	wait	disconnect
+	// UPDATE state to disconnected
+	move	memory 4, state, scratcha
+	move	STATE_DISCONNECTED to scratcha0
+	move	memory 4, scratcha, state
+	call	save_state
+wsr_check:
+	move	scntl2&0x01 to sfbr
+	int	SIR_NOTIFY_WSR, if not 0
+//	int	SIR_NOTIFY_DISC
+	jump	issue_check
+
+reselected_on_select:
+	int	SIR_NOTIFY_RESELECTED_ON_SELECT
+	jump	reselected
+
+wait_for_reselection:
+//	move	11 to ctest0
+//	int	SIR_NOTIFY_WAIT_RESELECT
+	wait reselect sigp_set
+reselected:
+//	move	12 to ctest0
+	clear	target
+	int	SIR_ERROR_NOT_MSG_IN_AFTER_RESELECT, when not msg_in
+	move	1, scratchb, when msg_in
+	int	error_not_identify_after_reselect, if not MSG_IDENTIFY and mask 0x1f
+//	int	SIR_NOTIFY_RESELECT
+	// now locate the right DSA - note do not clear ACK, so target doesn't start
+	// synchronous transfer until we are ready
+find_dsa:
+//	move	6 to ctest0
+ 	move	memory 4, dsa_head, dsa
+find_dsa_loop:
+//	move	7 to ctest0
+	move	dsa0 to sfbr
+	jump	find_dsa_1, if not 0
+	move	dsa1 to sfbr
+	jump	find_dsa_1, if not 0
+	move	dsa2 to sfbr
+	jump	find_dsa_1, if not 0
+	move	dsa3 to sfbr
+	int	error_reselected, if 0			// couldn't match dsa (panic)
+find_dsa_1:
+//	move	8 to ctest0
+	// load state from DSA into dsa_copy
+	call	load_state
+	move	memory 4, state, scratcha		// get dsastate in scratcha
+	move	scratcha0 to sfbr			// and state variable in sfbr
+	jump	find_dsa_next, if not STATE_DISCONNECTED // wrong state
+	move	ssid & ssid_mask to sfbr			// get target ID
+	move	memory 1, targ, find_dsa_smc1		// forge target comparison instruction
+find_dsa_smc1:
+	jump	find_dsa_next, if not 255		// jump if not matched
+	move	memory 1, lun, find_dsa_smc2		// forge lun comparison instruction
+	move	scratchb0 to sfbr			// recover IDENTIFY message
+find_dsa_smc2:
+	jump	reload_sync, if 255 and mask ~7		// off we jolly well go
+find_dsa_next:
+	move	memory 4, next, dsa			// find next
+	jump	find_dsa_loop
+
+// id_out terminated early
+// most likely the message wasn't recognised
+// clear ATN and accept the message in
+id_out_mismatch_recover:
+	clear	atn
+        jump    msg_in_phase, when msg_in
+        int     SIR_MSG_REJECT
+        jump    to_decisions
+
+// Reload synchronous registers after a reconnect. If the transfer is a synchronous read, then
+// as soon as we clear ACK, the target will switch to data_in and start blasting data into the
+// fifo. We need to be executing the 'jump when data_in' instruction before the target stops REQing
+// since it is the REQ which latches the 'when'. The target will do 16 REQs before stopping, so
+// we have 16 bytes (160uS) plus delays to do this after clearing ACK. Once the jump is executing,
+// the target will wait, so as much debugging as you like can happen in data_in_phase, just don't
+// stick any delays between 'clear ack' and 'jump data_in_phase, when data_in'.
+
+reload_sync:
+	call	load_sync
+	clear	ack
+to_decisions:
+	jump	data_in_phase, when data_in
+	jump	cmd_phase, if cmd
+	jump	data_out_phase, if data_out
+	jump	status_phase, if status
+	jump	msg_in_phase, if msg_in
+	int	error_unexpected_phase
+post_data_to_decisions:
+	jump	status_phase, when status
+	jump	msg_in_phase, if msg_in
+	int	error_unexpected_phase
+	
+//
+// MULTI_TARGET
+//
+// following must mirror top of dsa structure
+// the first section is loaded and saved, the
+// second section loaded only
+dsa_copy:
+state:	defw	0			// a0 is state, a1 result, a2 dma block count
+dmaaddr: defw	0			// dma address for block moves
+dsa_save_end:
+targ:	defw	0			// lsb is target
+lun:	defw	0			// lsb is lun
+sync:	defw	0			// lsb is scntl3, sxfer
+next:	defw	0
+dsa_load_end:
+dsa_load_len = dsa_load_end - dsa_copy
+dsa_save_len = dsa_save_end - dsa_copy
+
+load_state:
+	// load state from DSA into dsa_copy
+//	move	9 to ctest0
+	move	memory 4, dsa, load_state_smc0 + 4
+load_state_smc0:
+	move	memory dsa_load_len, 0, dsa_copy
+//	move	20 to ctest0
+	return
+save_state:
+	move	memory 4, dsa, save_state_smc0 + 8
+save_state_smc0:
+	move	memory dsa_save_len, dsa_copy, 0
+	return
+
+sigp_set:
+//	int	SIR_NOTIFY_SIGP
+	move	ctest2 to sfbr				// clear SIGP
+issue_check:
+//	int	SIR_NOTIFY_ISSUE_CHECK
+//	move	1 to ctest0
+	move	memory 4, dsa_head, dsa
+issue_check_loop:
+//	move	2 to ctest0
+	move	dsa0 to sfbr
+	jump	issue_check_1, if not 0
+	move	dsa1 to sfbr
+	jump	issue_check_1, if not 0
+	move	dsa2 to sfbr
+	jump	issue_check_1, if not 0
+	move	dsa3 to sfbr
+	jump	wait_for_reselection, if 0		// nothing to do
+issue_check_1:
+//	move	3 to ctest0
+ 	call	load_state
+	move	memory 4, state, scratcha		// get dsastate in scratcha
+	move	scratcha0 to sfbr			// and state variable in sfbr
+	jump	start, if STATE_ISSUE			// right state
+issue_check_next:
+//	move	4 to ctest0
+	move	memory 4, next, dsa			// find next
+	jump	issue_check_loop
+load_sync:
+	move	memory 4, sync, scratcha		// load the sync stuff
+	move	scratcha0 to sfbr			// assuming load_state has been called
+	move	sfbr to scntl3
+	move	scratcha1 to sfbr
+	move	sfbr to sxfer
+//	int	SIR_NOTIFY_LOAD_SYNC
+	return
--- /dev/null
+++ b/os/pc/sdata.c
@@ -1,0 +1,2206 @@
+#include "u.h"
+#include "../port/lib.h"
+#include "mem.h"
+#include "dat.h"
+#include "fns.h"
+#include "io.h"
+#include "ureg.h"
+#include "../port/error.h"
+
+#include "../port/sd.h"
+
+extern SDifc sdataifc;
+
+enum {
+	DbgCONFIG	= 0x0001,	/* detected drive config info */
+	DbgIDENTIFY	= 0x0002,	/* detected drive identify info */
+	DbgSTATE	= 0x0004,	/* dump state on panic */
+	DbgPROBE	= 0x0008,	/* trace device probing */
+	DbgDEBUG	= 0x0080,	/* the current problem... */
+	DbgINL		= 0x0100,	/* That Inil20+ message we hate */
+	Dbg48BIT	= 0x0200,	/* 48-bit LBA */
+	DbgBsy		= 0x0400,	/* interrupt but Bsy (shared IRQ) */
+};
+#define DEBUG		(DbgDEBUG|DbgSTATE)
+
+enum {					/* I/O ports */
+	Data		= 0,
+	Error		= 1,		/* (read) */
+	Features	= 1,		/* (write) */
+	Count		= 2,		/* sector count<7-0>, sector count<15-8> */
+	Ir		= 2,		/* interrupt reason (PACKET) */
+	Sector		= 3,		/* sector number */
+	Lbalo		= 3,		/* LBA<7-0>, LBA<31-24> */
+	Cyllo		= 4,		/* cylinder low */
+	Bytelo		= 4,		/* byte count low (PACKET) */
+	Lbamid		= 4,		/* LBA<15-8>, LBA<39-32> */
+	Cylhi		= 5,		/* cylinder high */
+	Bytehi		= 5,		/* byte count hi (PACKET) */
+	Lbahi		= 5,		/* LBA<23-16>, LBA<47-40> */
+	Dh		= 6,		/* Device/Head, LBA<32-14> */
+	Status		= 7,		/* (read) */
+	Command		= 7,		/* (write) */
+
+	As		= 2,		/* Alternate Status (read) */
+	Dc		= 2,		/* Device Control (write) */
+};
+
+enum {					/* Error */
+	Med		= 0x01,		/* Media error */
+	Ili		= 0x01,		/* command set specific (PACKET) */
+	Nm		= 0x02,		/* No Media */
+	Eom		= 0x02,		/* command set specific (PACKET) */
+	Abrt		= 0x04,		/* Aborted command */
+	Mcr		= 0x08,		/* Media Change Request */
+	Idnf		= 0x10,		/* no user-accessible address */
+	Mc		= 0x20,		/* Media Change */
+	Unc		= 0x40,		/* Uncorrectable data error */
+	Wp		= 0x40,		/* Write Protect */
+	Icrc		= 0x80,		/* Interface CRC error */
+};
+
+enum {					/* Features */
+	Dma		= 0x01,		/* data transfer via DMA (PACKET) */
+	Ovl		= 0x02,		/* command overlapped (PACKET) */
+};
+
+enum {					/* Interrupt Reason */
+	Cd		= 0x01,		/* Command/Data */
+	Io		= 0x02,		/* I/O direction */
+	Rel		= 0x04,		/* Bus Release */
+};
+
+enum {					/* Device/Head */
+	Dev0		= 0xA0,		/* Master */
+	Dev1		= 0xB0,		/* Slave */
+	Lba		= 0x40,		/* LBA mode */
+};
+
+enum {					/* internal flags */
+	Lba48		= 0x1,		/* LBA48 mode */
+	Lba48always	= 0x2,		/* ... */
+};
+
+enum {					/* Status, Alternate Status */
+	Err		= 0x01,		/* Error */
+	Chk		= 0x01,		/* Check error (PACKET) */
+	Drq		= 0x08,		/* Data Request */
+	Dsc		= 0x10,		/* Device Seek Complete */
+	Serv		= 0x10,		/* Service */
+	Df		= 0x20,		/* Device Fault */
+	Dmrd		= 0x20,		/* DMA ready (PACKET) */
+	Drdy		= 0x40,		/* Device Ready */
+	Bsy		= 0x80,		/* Busy */
+};
+
+enum {					/* Command */
+	Cnop		= 0x00,		/* NOP */
+	Cdr		= 0x08,		/* Device Reset */
+	Crs		= 0x20,		/* Read Sectors */
+	Crs48		= 0x24,		/* Read Sectors Ext */
+	Crd48		= 0x25,		/* Read w/ DMA Ext */
+	Crdq48		= 0x26,		/* Read w/ DMA Queued Ext */
+	Crsm48		= 0x29,		/* Read Multiple Ext */
+	Cws		= 0x30,		/* Write Sectors */
+	Cws48		= 0x34,		/* Write Sectors Ext */
+	Cwd48		= 0x35,		/* Write w/ DMA Ext */
+	Cwdq48		= 0x36,		/* Write w/ DMA Queued Ext */
+	Cwsm48		= 0x39,		/* Write Multiple Ext */
+	Cedd		= 0x90,		/* Execute Device Diagnostics */
+	Cpkt		= 0xA0,		/* Packet */
+	Cidpkt		= 0xA1,		/* Identify Packet Device */
+	Crsm		= 0xC4,		/* Read Multiple */
+	Cwsm		= 0xC5,		/* Write Multiple */
+	Csm		= 0xC6,		/* Set Multiple */
+	Crdq		= 0xC7,		/* Read DMA queued */
+	Crd		= 0xC8,		/* Read DMA */
+	Cwd		= 0xCA,		/* Write DMA */
+	Cwdq		= 0xCC,		/* Write DMA queued */
+	Cstandby	= 0xE2,		/* Standby */
+	Cid		= 0xEC,		/* Identify Device */
+	Csf		= 0xEF,		/* Set Features */
+};
+
+enum {					/* Device Control */
+	Nien		= 0x02,		/* (not) Interrupt Enable */
+	Srst		= 0x04,		/* Software Reset */
+	Hob		= 0x80,		/* High Order Bit [sic] */
+};
+
+enum {					/* PCI Configuration Registers */
+	Bmiba		= 0x20,		/* Bus Master Interface Base Address */
+	Idetim		= 0x40,		/* IE Timing */
+	Sidetim		= 0x44,		/* Slave IE Timing */
+	Udmactl		= 0x48,		/* Ultra DMA/33 Control */
+	Udmatim		= 0x4A,		/* Ultra DMA/33 Timing */
+};
+
+enum {					/* Bus Master IDE I/O Ports */
+	Bmicx		= 0,		/* Command */
+	Bmisx		= 2,		/* Status */
+	Bmidtpx		= 4,		/* Descriptor Table Pointer */
+};
+
+enum {					/* Bmicx */
+	Ssbm		= 0x01,		/* Start/Stop Bus Master */
+	Rwcon		= 0x08,		/* Read/Write Control */
+};
+
+enum {					/* Bmisx */
+	Bmidea		= 0x01,		/* Bus Master IDE Active */
+	Idedmae		= 0x02,		/* IDE DMA Error  (R/WC) */
+	Ideints		= 0x04,		/* IDE Interrupt Status (R/WC) */
+	Dma0cap		= 0x20,		/* Drive 0 DMA Capable */
+	Dma1cap		= 0x40,		/* Drive 0 DMA Capable */
+};
+enum {					/* Physical Region Descriptor */
+	PrdEOT		= 0x80000000,	/* Bus Master IDE Active */
+};
+
+enum {					/* offsets into the identify info. */
+	Iconfig		= 0,		/* general configuration */
+	Ilcyl		= 1,		/* logical cylinders */
+	Ilhead		= 3,		/* logical heads */
+	Ilsec		= 6,		/* logical sectors per logical track */
+	Iserial		= 10,		/* serial number */
+	Ifirmware	= 23,		/* firmware revision */
+	Imodel		= 27,		/* model number */
+	Imaxrwm		= 47,		/* max. read/write multiple sectors */
+	Icapabilities	= 49,		/* capabilities */
+	Istandby	= 50,		/* device specific standby timer */
+	Ipiomode	= 51,		/* PIO data transfer mode number */
+	Ivalid		= 53,
+	Iccyl		= 54,		/* cylinders if (valid&0x01) */
+	Ichead		= 55,		/* heads if (valid&0x01) */
+	Icsec		= 56,		/* sectors if (valid&0x01) */
+	Iccap		= 57,		/* capacity if (valid&0x01) */
+	Irwm		= 59,		/* read/write multiple */
+	Ilba		= 60,		/* LBA size */
+	Imwdma		= 63,		/* multiword DMA mode */
+	Iapiomode	= 64,		/* advanced PIO modes supported */
+	Iminmwdma	= 65,		/* min. multiword DMA cycle time */
+	Irecmwdma	= 66,		/* rec. multiword DMA cycle time */
+	Iminpio		= 67,		/* min. PIO cycle w/o flow control */
+	Iminiordy	= 68,		/* min. PIO cycle with IORDY */
+	Ipcktbr		= 71,		/* time from PACKET to bus release */
+	Iserbsy		= 72,		/* time from SERVICE to !Bsy */
+	Iqdepth		= 75,		/* max. queue depth */
+	Imajor		= 80,		/* major version number */
+	Iminor		= 81,		/* minor version number */
+	Icsfs		= 82,		/* command set/feature supported */
+	Icsfe		= 85,		/* command set/feature enabled */
+	Iudma		= 88,		/* ultra DMA mode */
+	Ierase		= 89,		/* time for security erase */
+	Ieerase		= 90,		/* time for enhanced security erase */
+	Ipower		= 91,		/* current advanced power management */
+	Ilba48		= 100,		/* 48-bit LBA size (64 bits in 100-103) */
+	Irmsn		= 127,		/* removable status notification */
+	Isecstat	= 128,		/* security status */
+	Icfapwr		= 160,		/* CFA power mode */
+	Imediaserial	= 176,		/* current media serial number */
+	Icksum		= 255,		/* checksum */
+};
+
+enum {					/* bit masks for config identify info */
+	Mpktsz		= 0x0003,	/* packet command size */
+	Mincomplete	= 0x0004,	/* incomplete information */
+	Mdrq		= 0x0060,	/* DRQ type */
+	Mrmdev		= 0x0080,	/* device is removable */
+	Mtype		= 0x1F00,	/* device type */
+	Mproto		= 0x8000,	/* command protocol */
+};
+
+enum {					/* bit masks for capabilities identify info */
+	Mdma		= 0x0100,	/* DMA supported */
+	Mlba		= 0x0200,	/* LBA supported */
+	Mnoiordy	= 0x0400,	/* IORDY may be disabled */
+	Miordy		= 0x0800,	/* IORDY supported */
+	Msoftrst	= 0x1000,	/* needs soft reset when Bsy */
+	Mstdby		= 0x2000,	/* standby supported */
+	Mqueueing	= 0x4000,	/* queueing overlap supported */
+	Midma		= 0x8000,	/* interleaved DMA supported */
+};
+
+enum {					/* bit masks for supported/enabled features */
+	Msmart		= 0x0001,
+	Msecurity	= 0x0002,
+	Mrmmedia	= 0x0004,
+	Mpwrmgmt	= 0x0008,
+	Mpkt		= 0x0010,
+	Mwcache		= 0x0020,
+	Mlookahead	= 0x0040,
+	Mrelirq		= 0x0080,
+	Msvcirq		= 0x0100,
+	Mreset		= 0x0200,
+	Mprotected	= 0x0400,
+	Mwbuf		= 0x1000,
+	Mrbuf		= 0x2000,
+	Mnop		= 0x4000,
+	Mmicrocode	= 0x0001,
+	Mqueued		= 0x0002,
+	Mcfa		= 0x0004,
+	Mapm		= 0x0008,
+	Mnotify		= 0x0010,
+	Mstandby	= 0x0020,
+	Mspinup		= 0x0040,
+	Mmaxsec		= 0x0100,
+	Mautoacoustic	= 0x0200,
+	Maddr48		= 0x0400,
+	Mdevconfov	= 0x0800,
+	Mflush		= 0x1000,
+	Mflush48	= 0x2000,
+	Msmarterror	= 0x0001,
+	Msmartselftest	= 0x0002,
+	Mmserial	= 0x0004,
+	Mmpassthru	= 0x0008,
+	Mlogging	= 0x0020,
+};
+
+typedef struct Ctlr Ctlr;
+typedef struct Drive Drive;
+
+typedef struct Prd {
+	ulong	pa;			/* Physical Base Address */
+	int	count;
+} Prd;
+
+enum {
+	Nprd		= SDmaxio/(64*1024)+2,
+};
+
+typedef struct Ctlr {
+	int	cmdport;
+	int	ctlport;
+	int	irq;
+	int	tbdf;
+	int	bmiba;			/* bus master interface base address */
+
+	Pcidev*	pcidev;
+	void	(*ienable)(Ctlr*);
+	void	(*idisable)(Ctlr*);
+	SDev*	sdev;
+
+	Drive*	drive[2];
+
+	Prd*	prdt;			/* physical region descriptor table */
+	void*	prdtbase;
+
+	QLock;				/* current command */
+	Drive*	curdrive;
+	int	command;		/* last command issued (debugging) */
+	Rendez;
+	int	done;
+
+	Lock;				/* register access */
+} Ctlr;
+
+typedef struct Drive {
+	Ctlr*	ctlr;
+
+	int	dev;
+	ushort	info[256];
+	int	c;			/* cylinder */
+	int	h;			/* head */
+	int	s;			/* sector */
+	vlong	sectors;		/* total */
+	int	secsize;		/* sector size */
+
+	int	dma;			/* DMA R/W possible */
+	int	dmactl;
+	int	rwm;			/* read/write multiple possible */
+	int	rwmctl;
+
+	int	pkt;			/* PACKET device, length of pktcmd */
+	uchar	pktcmd[16];
+	int	pktdma;			/* this PACKET command using dma */
+
+	uchar	sense[18];
+	uchar	inquiry[48];
+
+	QLock;				/* drive access */
+	int	command;		/* current command */
+	int	write;
+	uchar*	data;
+	int	dlen;
+	uchar*	limit;
+	int	count;			/* sectors */
+	int	block;			/* R/W bytes per block */
+	int	status;
+	int	error;
+	int	flags;			/* internal flags */
+} Drive;
+
+static void
+pc87415ienable(Ctlr* ctlr)
+{
+	Pcidev *p;
+	int x;
+
+	p = ctlr->pcidev;
+	if(p == nil)
+		return;
+
+	x = pcicfgr32(p, 0x40);
+	if(ctlr->cmdport == p->mem[0].bar)
+		x &= ~0x00000100;
+	else
+		x &= ~0x00000200;
+	pcicfgw32(p, 0x40, x);
+}
+
+static void
+atadumpstate(Drive* drive, uchar* cmd, vlong lba, int count)
+{
+	Prd *prd;
+	Pcidev *p;
+	Ctlr *ctlr;
+	int i, bmiba;
+
+	if(!(DEBUG & DbgSTATE)){
+		USED(drive, cmd, lba, count);
+		return;
+	}
+
+	ctlr = drive->ctlr;
+	print("command %2.2uX\n", ctlr->command);
+	print("data %8.8p limit %8.8p dlen %d status %uX error %uX\n",
+		drive->data, drive->limit, drive->dlen,
+		drive->status, drive->error);
+	if(cmd != nil){
+		print("lba %d -> %lld, count %d -> %d (%d)\n",
+			(cmd[2]<<24)|(cmd[3]<<16)|(cmd[4]<<8)|cmd[5], lba,
+			(cmd[7]<<8)|cmd[8], count, drive->count);
+	}
+	if(!(inb(ctlr->ctlport+As) & Bsy)){
+		for(i = 1; i < 7; i++)
+			print(" 0x%2.2uX", inb(ctlr->cmdport+i));
+		print(" 0x%2.2uX\n", inb(ctlr->ctlport+As));
+	}
+	if(drive->command == Cwd || drive->command == Crd){
+		bmiba = ctlr->bmiba;
+		prd = ctlr->prdt;
+		print("bmicx %2.2uX bmisx %2.2uX prdt %8.8p\n",
+			inb(bmiba+Bmicx), inb(bmiba+Bmisx), prd);
+		for(;;){
+			print("pa 0x%8.8luX count %8.8uX\n",
+				prd->pa, prd->count);
+			if(prd->count & PrdEOT)
+				break;
+			prd++;
+		}
+	}
+	if(ctlr->pcidev && ctlr->pcidev->vid == 0x8086){
+		p = ctlr->pcidev;
+		print("0x40: %4.4uX 0x42: %4.4uX",
+			pcicfgr16(p, 0x40), pcicfgr16(p, 0x42));
+		print("0x48: %2.2uX\n", pcicfgr8(p, 0x48));
+		print("0x4A: %4.4uX\n", pcicfgr16(p, 0x4A));
+	}
+}
+
+static int
+atadebug(int cmdport, int ctlport, char* fmt, ...)
+{
+	int i, n;
+	va_list arg;
+	char buf[PRINTSIZE];
+
+	if(!(DEBUG & DbgPROBE)){
+		USED(cmdport, ctlport, fmt);
+		return 0;
+	}
+
+	va_start(arg, fmt);
+	n = vseprint(buf, buf+sizeof(buf), fmt, arg) - buf;
+	va_end(arg);
+
+	if(cmdport){
+		if(buf[n-1] == '\n')
+			n--;
+		n += snprint(buf+n, PRINTSIZE-n, " ataregs 0x%uX:",
+			cmdport);
+		for(i = Features; i < Command; i++)
+			n += snprint(buf+n, PRINTSIZE-n, " 0x%2.2uX",
+				inb(cmdport+i));
+		if(ctlport)
+			n += snprint(buf+n, PRINTSIZE-n, " 0x%2.2uX",
+				inb(ctlport+As));
+		n += snprint(buf+n, PRINTSIZE-n, "\n");
+	}
+	putstrn(buf, n);
+
+	return n;
+}
+
+static int
+ataready(int cmdport, int ctlport, int dev, int reset, int ready, int micro)
+{
+	int as;
+
+	atadebug(cmdport, ctlport, "ataready: dev %uX reset %uX ready %uX",
+		dev, reset, ready);
+
+	for(;;){
+		/*
+		 * Wait for the controller to become not busy and
+		 * possibly for a status bit to become true (usually
+		 * Drdy). Must change to the appropriate device
+		 * register set if necessary before testing for ready.
+		 * Always run through the loop at least once so it
+		 * can be used as a test for !Bsy.
+		 */
+		as = inb(ctlport+As);
+		if(as & reset){
+			/* nothing to do */
+		}
+		else if(dev){
+			outb(cmdport+Dh, dev);
+			dev = 0;
+		}
+		else if(ready == 0 || (as & ready)){
+			atadebug(0, 0, "ataready: %d 0x%2.2uX\n", micro, as);
+			return as;
+		}
+
+		if(micro-- <= 0){
+			atadebug(0, 0, "ataready: %d 0x%2.2uX\n", micro, as);
+			break;
+		}
+		microdelay(1);
+	}
+	atadebug(cmdport, ctlport, "ataready: timeout");
+
+	return -1;
+}
+
+/*
+static int
+atacsf(Drive* drive, vlong csf, int supported)
+{
+	ushort *info;
+	int cmdset, i, x;
+
+	if(supported)
+		info = &drive->info[Icsfs];
+	else
+		info = &drive->info[Icsfe];
+
+	for(i = 0; i < 3; i++){
+		x = (csf>>(16*i)) & 0xFFFF;
+		if(x == 0)
+			continue;
+		cmdset = info[i];
+		if(cmdset == 0 || cmdset == 0xFFFF)
+			return 0;
+		return cmdset & x;
+	}
+
+	return 0;
+}
+*/
+
+static int
+atadone(void* arg)
+{
+	return ((Ctlr*)arg)->done;
+}
+
+static int
+atarwmmode(Drive* drive, int cmdport, int ctlport, int dev)
+{
+	int as, maxrwm, rwm;
+
+	maxrwm = (drive->info[Imaxrwm] & 0xFF);
+	if(maxrwm == 0)
+		return 0;
+
+	/*
+	 * Sometimes drives come up with the current count set
+	 * to 0; if so, set a suitable value, otherwise believe
+	 * the value in Irwm if the 0x100 bit is set.
+	 */
+	if(drive->info[Irwm] & 0x100)
+		rwm = (drive->info[Irwm] & 0xFF);
+	else
+		rwm = 0;
+	if(rwm == 0)
+		rwm = maxrwm;
+	if(rwm > 16)
+		rwm = 16;
+	if(ataready(cmdport, ctlport, dev, Bsy|Drq, Drdy, 102*1000) < 0)
+		return 0;
+	outb(cmdport+Count, rwm);
+	outb(cmdport+Command, Csm);
+	microdelay(1);
+	as = ataready(cmdport, ctlport, 0, Bsy, Drdy|Df|Err, 1000);
+	inb(cmdport+Status);
+	if(as < 0 || (as & (Df|Err)))
+		return 0;
+
+	drive->rwm = rwm;
+
+	return rwm;
+}
+
+static int
+atadmamode(Drive* drive)
+{
+	int dma;
+
+	/*
+	 * Check if any DMA mode enabled.
+	 * Assumes the BIOS has picked and enabled the best.
+	 * This is completely passive at the moment, no attempt is
+	 * made to ensure the hardware is correctly set up.
+	 */
+	dma = drive->info[Imwdma] & 0x0707;
+	drive->dma = (dma>>8) & dma;
+	if(drive->dma == 0 && (drive->info[Ivalid] & 0x04)){
+		dma = drive->info[Iudma] & 0x7F7F;
+		drive->dma = (dma>>8) & dma;
+		if(drive->dma)
+			drive->dma |= 'U'<<16;
+	}
+
+	return dma;
+}
+
+static int
+ataidentify(int cmdport, int ctlport, int dev, int pkt, void* info)
+{
+	int as, command, drdy;
+
+	if(pkt){
+		command = Cidpkt;
+		drdy = 0;
+	}
+	else{
+		command = Cid;
+		drdy = Drdy;
+	}
+	as = ataready(cmdport, ctlport, dev, Bsy|Drq, drdy, 103*1000);
+	if(as < 0)
+		return as;
+	outb(cmdport+Command, command);
+	microdelay(1);
+
+	as = ataready(cmdport, ctlport, 0, Bsy, Drq|Err, 400*1000);
+	if(as < 0)
+		return -1;
+	if(as & Err)
+		return as;
+
+	memset(info, 0, 512);
+	inss(cmdport+Data, info, 256);
+	inb(cmdport+Status);
+
+	if(DEBUG & DbgIDENTIFY){
+		int i;
+		ushort *sp;
+
+		sp = (ushort*)info;
+		for(i = 0; i < 256; i++){
+			if(i && (i%16) == 0)
+				print("\n");
+			print(" %4.4uX", *sp);
+			sp++;
+		}
+		print("\n");
+	}
+
+	return 0;
+}
+
+static Drive*
+atadrive(int cmdport, int ctlport, int dev)
+{
+	Drive *drive;
+	int as, i, pkt;
+	uchar buf[512], *p;
+	ushort iconfig, *sp;
+
+	atadebug(0, 0, "identify: port 0x%uX dev 0x%2.2uX\n", cmdport, dev);
+	pkt = 1;
+retry:
+	as = ataidentify(cmdport, ctlport, dev, pkt, buf);
+	if(as < 0)
+		return nil;
+	if(as & Err){
+		if(pkt == 0)
+			return nil;
+		pkt = 0;
+		goto retry;
+	}
+
+	if((drive = malloc(sizeof(Drive))) == nil)
+		return nil;
+	drive->dev = dev;
+	memmove(drive->info, buf, sizeof(drive->info));
+	drive->sense[0] = 0x70;
+	drive->sense[7] = sizeof(drive->sense)-7;
+
+	drive->inquiry[2] = 2;
+	drive->inquiry[3] = 2;
+	drive->inquiry[4] = sizeof(drive->inquiry)-4;
+	p = &drive->inquiry[8];
+	sp = &drive->info[Imodel];
+	for(i = 0; i < 20; i++){
+		*p++ = *sp>>8;
+		*p++ = *sp++;
+	}
+
+	drive->secsize = 512;
+
+	/*
+	 * Beware the CompactFlash Association feature set.
+	 * Now, why this value in Iconfig just walks all over the bit
+	 * definitions used in the other parts of the ATA/ATAPI standards
+	 * is a mystery and a sign of true stupidity on someone's part.
+	 * Anyway, the standard says if this value is 0x848A then it's
+	 * CompactFlash and it's NOT a packet device.
+	 */
+	iconfig = drive->info[Iconfig];
+	if(iconfig != 0x848A && (iconfig & 0xC000) == 0x8000){
+		if(iconfig & 0x01)
+			drive->pkt = 16;
+		else
+			drive->pkt = 12;
+	}
+	else{
+		if(drive->info[Ivalid] & 0x0001){
+			drive->c = drive->info[Iccyl];
+			drive->h = drive->info[Ichead];
+			drive->s = drive->info[Icsec];
+		}else{
+			drive->c = drive->info[Ilcyl];
+			drive->h = drive->info[Ilhead];
+			drive->s = drive->info[Ilsec];
+		}
+		if(drive->info[Icapabilities] & Mlba){
+			if(drive->info[Icsfs+1] & Maddr48){
+				drive->sectors = drive->info[Ilba48]
+					| (drive->info[Ilba48+1]<<16)
+					| ((vlong)drive->info[Ilba48+2]<<32);
+				drive->flags |= Lba48;
+			}else{
+				drive->sectors = (drive->info[Ilba+1]<<16)
+					 |drive->info[Ilba];
+			}
+			drive->dev |= Lba;
+		}else
+			drive->sectors = drive->c*drive->h*drive->s;
+		atarwmmode(drive, cmdport, ctlport, dev);
+	}
+	atadmamode(drive);	
+
+	if(DEBUG & DbgCONFIG){
+		print("dev %2.2uX port %uX config %4.4uX capabilities %4.4uX",
+			dev, cmdport, iconfig, drive->info[Icapabilities]);
+		print(" mwdma %4.4uX", drive->info[Imwdma]);
+		if(drive->info[Ivalid] & 0x04)
+			print(" udma %4.4uX", drive->info[Iudma]);
+		print(" dma %8.8uX rwm %ud\n", drive->dma, drive->rwm);
+		if(drive->flags&Lba48)
+			print("\tLLBA sectors %lld\n", drive->sectors);
+	}
+
+	return drive;
+}
+
+static void
+atasrst(int ctlport)
+{
+	/*
+	 * Srst is a big stick and may cause problems if further
+	 * commands are tried before the drives become ready again.
+	 * Also, there will be problems here if overlapped commands
+	 * are ever supported.
+	 */
+	microdelay(5);
+	outb(ctlport+Dc, Srst);
+	microdelay(5);
+	outb(ctlport+Dc, 0);
+	microdelay(2*1000);
+}
+
+static SDev*
+ataprobe(int cmdport, int ctlport, int irq)
+{
+	Ctlr* ctlr;
+	SDev *sdev;
+	Drive *drive;
+	int dev, error, rhi, rlo;
+
+	if(ioalloc(cmdport, 8, 0, "atacmd") < 0) {
+		print("ataprobe: Cannot allocate %X\n", cmdport);
+		return nil;
+	}
+	if(ioalloc(ctlport+As, 1, 0, "atactl") < 0){
+		print("ataprobe: Cannot allocate %X\n", ctlport + As);
+		iofree(cmdport);
+		return nil;
+	}
+
+	/*
+	 * Try to detect a floating bus.
+	 * Bsy should be cleared. If not, see if the cylinder registers
+	 * are read/write capable.
+	 * If the master fails, try the slave to catch slave-only
+	 * configurations.
+	 * There's no need to restore the tested registers as they will
+	 * be reset on any detected drives by the Cedd command.
+	 * All this indicates is that there is at least one drive on the
+	 * controller; when the non-existent drive is selected in a
+	 * single-drive configuration the registers of the existing drive
+	 * are often seen, only command execution fails.
+	 */
+	dev = Dev0;
+	if(inb(ctlport+As) & Bsy){
+		outb(cmdport+Dh, dev);
+		microdelay(1);
+trydev1:
+		atadebug(cmdport, ctlport, "ataprobe bsy");
+		outb(cmdport+Cyllo, 0xAA);
+		outb(cmdport+Cylhi, 0x55);
+		outb(cmdport+Sector, 0xFF);
+		rlo = inb(cmdport+Cyllo);
+		rhi = inb(cmdport+Cylhi);
+		if(rlo != 0xAA && (rlo == 0xFF || rhi != 0x55)){
+			if(dev == Dev1){
+release:
+				iofree(cmdport);
+				iofree(ctlport+As);
+				return nil;
+			}
+			dev = Dev1;
+			if(ataready(cmdport, ctlport, dev, Bsy, 0, 20*1000) < 0)
+				goto trydev1;
+		}
+	}
+
+	/*
+	 * Disable interrupts on any detected controllers.
+	 */
+	outb(ctlport+Dc, Nien);
+tryedd1:
+	if(ataready(cmdport, ctlport, dev, Bsy|Drq, 0, 105*1000) < 0){
+		/*
+		 * There's something there, but it didn't come up clean,
+		 * so try hitting it with a big stick. The timing here is
+		 * wrong but this is a last-ditch effort and it sometimes
+		 * gets some marginal hardware back online.
+		 */
+		atasrst(ctlport);
+		if(ataready(cmdport, ctlport, dev, Bsy|Drq, 0, 106*1000) < 0)
+			goto release;
+	}
+
+	/*
+	 * Can only get here if controller is not busy.
+	 * If there are drives Bsy will be set within 400nS,
+	 * must wait 2mS before testing Status.
+	 * Wait for the command to complete (6 seconds max).
+	 */
+	outb(cmdport+Command, Cedd);
+	delay(2);
+	if(ataready(cmdport, ctlport, dev, Bsy|Drq, 0, 6*1000*1000) < 0)
+		goto release;
+
+	/*
+	 * If bit 0 of the error register is set then the selected drive
+	 * exists. This is enough to detect single-drive configurations.
+	 * However, if the master exists there is no way short of executing
+	 * a command to determine if a slave is present.
+	 * It appears possible to get here testing Dev0 although it doesn't
+	 * exist and the EDD won't take, so try again with Dev1.
+	 */
+	error = inb(cmdport+Error);
+	atadebug(cmdport, ctlport, "ataprobe: dev %uX", dev);
+	if((error & ~0x80) != 0x01){
+		if(dev == Dev1)
+			goto release;
+		dev = Dev1;
+		goto tryedd1;
+	}
+
+	/*
+	 * At least one drive is known to exist, try to
+	 * identify it. If that fails, don't bother checking
+	 * any further.
+	 * If the one drive found is Dev0 and the EDD command
+	 * didn't indicate Dev1 doesn't exist, check for it.
+	 */
+	if((drive = atadrive(cmdport, ctlport, dev)) == nil)
+		goto release;
+	if((ctlr = malloc(sizeof(Ctlr))) == nil){
+		free(drive);
+		goto release;
+	}
+	memset(ctlr, 0, sizeof(Ctlr));
+	if((sdev = malloc(sizeof(SDev))) == nil){
+		free(ctlr);
+		free(drive);
+		goto release;
+	}
+	memset(sdev, 0, sizeof(SDev));
+	drive->ctlr = ctlr;
+	if(dev == Dev0){
+		ctlr->drive[0] = drive;
+		if(!(error & 0x80)){
+			/*
+			 * Always leave Dh pointing to a valid drive,
+			 * otherwise a subsequent call to ataready on
+			 * this controller may try to test a bogus Status.
+			 * Ataprobe is the only place possibly invalid
+			 * drives should be selected.
+			 */
+			drive = atadrive(cmdport, ctlport, Dev1);
+			if(drive != nil){
+				drive->ctlr = ctlr;
+				ctlr->drive[1] = drive;
+			}
+			else{
+				outb(cmdport+Dh, Dev0);
+				microdelay(1);
+			}
+		}
+	}
+	else
+		ctlr->drive[1] = drive;
+
+	ctlr->cmdport = cmdport;
+	ctlr->ctlport = ctlport;
+	ctlr->irq = irq;
+	ctlr->tbdf = BUSUNKNOWN;
+	ctlr->command = Cedd;		/* debugging */
+
+	sdev->ifc = &sdataifc;
+	sdev->ctlr = ctlr;
+	sdev->nunit = 2;
+	ctlr->sdev = sdev;
+
+	return sdev;
+}
+
+static void
+ataclear(SDev *sdev)
+{
+	Ctlr* ctlr;
+
+	ctlr = sdev->ctlr;
+	iofree(ctlr->cmdport);
+	iofree(ctlr->ctlport + As);
+
+	if (ctlr->drive[0])
+		free(ctlr->drive[0]);
+	if (ctlr->drive[1])
+		free(ctlr->drive[1]);
+	if (sdev->name)
+		free(sdev->name);
+	if (sdev->unitflg)
+		free(sdev->unitflg);
+	if (sdev->unit)
+		free(sdev->unit);
+	free(ctlr);
+	free(sdev);
+}
+
+static char *
+atastat(SDev *sdev, char *p, char *e)
+{
+	Ctlr *ctlr = sdev->ctlr;
+
+	return seprint(p, e, "%s ata port %X ctl %X irq %d\n", 
+		    	       sdev->name, ctlr->cmdport, ctlr->ctlport, ctlr->irq);
+}
+
+static SDev*
+ataprobew(DevConf *cf)
+{
+	if (cf->nports != 2)
+		error(Ebadarg);
+
+	return ataprobe(cf->ports[0].port, cf->ports[1].port, cf->intnum);
+}
+
+static int
+atasetsense(Drive* drive, int status, int key, int asc, int ascq)
+{
+	drive->sense[2] = key;
+	drive->sense[12] = asc;
+	drive->sense[13] = ascq;
+
+	return status;
+}
+
+static int
+atastandby(Drive* drive, int period)
+{
+	Ctlr* ctlr;
+	int cmdport, done;
+
+	ctlr = drive->ctlr;
+	drive->command = Cstandby;
+	qlock(ctlr);
+
+	cmdport = ctlr->cmdport;
+	ilock(ctlr);
+	outb(cmdport+Count, period);
+	outb(cmdport+Dh, drive->dev);
+	ctlr->done = 0;
+	ctlr->curdrive = drive;
+	ctlr->command = Cstandby;	/* debugging */
+	outb(cmdport+Command, Cstandby);
+	iunlock(ctlr);
+
+	while(waserror())
+		;
+	tsleep(ctlr, atadone, ctlr, 30*1000);
+	poperror();
+
+	done = ctlr->done;
+	qunlock(ctlr);
+
+	if(!done || (drive->status & Err))
+		return atasetsense(drive, SDcheck, 4, 8, drive->error);
+	return SDok;
+}
+
+static int
+atamodesense(Drive* drive, uchar* cmd)
+{
+	int len;
+
+	/*
+	 * Fake a vendor-specific request with page code 0,
+	 * return the drive info.
+	 */
+	if((cmd[2] & 0x3F) != 0 && (cmd[2] & 0x3F) != 0x3F)
+		return atasetsense(drive, SDcheck, 0x05, 0x24, 0);
+	len = (cmd[7]<<8)|cmd[8];
+	if(len == 0)
+		return SDok;
+	if(len < 8+sizeof(drive->info))
+		return atasetsense(drive, SDcheck, 0x05, 0x1A, 0);
+	if(drive->data == nil || drive->dlen < len)
+		return atasetsense(drive, SDcheck, 0x05, 0x20, 1);
+	memset(drive->data, 0, 8);
+	drive->data[0] = sizeof(drive->info)>>8;
+	drive->data[1] = sizeof(drive->info);
+	memmove(drive->data+8, drive->info, sizeof(drive->info));
+	drive->data += 8+sizeof(drive->info);
+
+	return SDok;
+}
+
+static void
+atanop(Drive* drive, int subcommand)
+{
+	Ctlr* ctlr;
+	int as, cmdport, ctlport, timeo;
+
+	/*
+	 * Attempt to abort a command by using NOP.
+	 * In response, the drive is supposed to set Abrt
+	 * in the Error register, set (Drdy|Err) in Status
+	 * and clear Bsy when done. However, some drives
+	 * (e.g. ATAPI Zip) just go Bsy then clear Status
+	 * when done, hence the timeout loop only on Bsy
+	 * and the forced setting of drive->error.
+	 */
+	ctlr = drive->ctlr;
+	cmdport = ctlr->cmdport;
+	outb(cmdport+Features, subcommand);
+	outb(cmdport+Dh, drive->dev);
+	ctlr->command = Cnop;		/* debugging */
+	outb(cmdport+Command, Cnop);
+
+	microdelay(1);
+	ctlport = ctlr->ctlport;
+	for(timeo = 0; timeo < 1000; timeo++){
+		as = inb(ctlport+As);
+		if(!(as & Bsy))
+			break;
+		microdelay(1);
+	}
+	drive->error |= Abrt;
+}
+
+static void
+ataabort(Drive* drive, int dolock)
+{
+	/*
+	 * If NOP is available (packet commands) use it otherwise
+	 * must try a software reset.
+	 */
+	if(dolock)
+		ilock(drive->ctlr);
+	if(drive->info[Icsfs] & Mnop)
+		atanop(drive, 0);
+	else{
+		atasrst(drive->ctlr->ctlport);
+		drive->error |= Abrt;
+	}
+	if(dolock)
+		iunlock(drive->ctlr);
+}
+
+static int
+atadmasetup(Drive* drive, int len)
+{
+	Prd *prd;
+	ulong pa;
+	Ctlr *ctlr;
+	int bmiba, bmisx, count;
+
+	pa = PCIWADDR(drive->data);
+	if(pa & 0x03)
+		return -1;
+	ctlr = drive->ctlr;
+	prd = ctlr->prdt;
+
+	/*
+	 * Sometimes drives identify themselves as being DMA capable
+	 * although they are not on a busmastering controller.
+	 */
+	if(prd == nil){
+		drive->dmactl = 0;
+		print("disabling dma: not on a busmastering controller\n");
+		return -1;
+	}
+
+	for(;;){
+		prd->pa = pa;
+		count = 64*1024 - (pa & 0xFFFF);
+		if(count >= len){
+			prd->count = PrdEOT|(len & 0xFFFF);
+			break;
+		}
+		prd->count = count;
+		len -= count;
+		pa += count;
+		prd++;
+	}
+
+	bmiba = ctlr->bmiba;
+	outl(bmiba+Bmidtpx, PCIWADDR(ctlr->prdt));
+	if(drive->write)
+		outb(ctlr->bmiba+Bmicx, 0);
+	else
+		outb(ctlr->bmiba+Bmicx, Rwcon);
+	bmisx = inb(bmiba+Bmisx);
+	outb(bmiba+Bmisx, bmisx|Ideints|Idedmae);
+
+	return 0;
+}
+
+static void
+atadmastart(Ctlr* ctlr, int write)
+{
+	if(write)
+		outb(ctlr->bmiba+Bmicx, Ssbm);
+	else
+		outb(ctlr->bmiba+Bmicx, Rwcon|Ssbm);
+}
+
+static int
+atadmastop(Ctlr* ctlr)
+{
+	int bmiba;
+
+	bmiba = ctlr->bmiba;
+	outb(bmiba+Bmicx, inb(bmiba+Bmicx) & ~Ssbm);
+
+	return inb(bmiba+Bmisx);
+}
+
+static void
+atadmainterrupt(Drive* drive, int count)
+{
+	Ctlr* ctlr;
+	int bmiba, bmisx;
+
+	ctlr = drive->ctlr;
+	bmiba = ctlr->bmiba;
+	bmisx = inb(bmiba+Bmisx);
+	switch(bmisx & (Ideints|Idedmae|Bmidea)){
+	case Bmidea:
+		/*
+		 * Data transfer still in progress, nothing to do
+		 * (this should never happen).
+		 */
+		return;
+
+	case Ideints:
+	case Ideints|Bmidea:
+		/*
+		 * Normal termination, tidy up.
+		 */
+		drive->data += count;
+		break;
+
+	default:
+		/*
+		 * What's left are error conditions (memory transfer
+		 * problem) and the device is not done but the PRD is
+		 * exhausted. For both cases must somehow tell the
+		 * drive to abort.
+		 */
+		ataabort(drive, 0);
+		break;
+	}
+	atadmastop(ctlr);
+	ctlr->done = 1;
+}
+
+static void
+atapktinterrupt(Drive* drive)
+{
+	Ctlr* ctlr;
+	int cmdport, len;
+
+	ctlr = drive->ctlr;
+	cmdport = ctlr->cmdport;
+	switch(inb(cmdport+Ir) & (/*Rel|*/Io|Cd)){
+	case Cd:
+		outss(cmdport+Data, drive->pktcmd, drive->pkt/2);
+		break;
+
+	case 0:
+		len = (inb(cmdport+Bytehi)<<8)|inb(cmdport+Bytelo);
+		if(drive->data+len > drive->limit){
+			atanop(drive, 0);
+			break;
+		}
+		outss(cmdport+Data, drive->data, len/2);
+		drive->data += len;
+		break;
+
+	case Io:
+		len = (inb(cmdport+Bytehi)<<8)|inb(cmdport+Bytelo);
+		if(drive->data+len > drive->limit){
+			atanop(drive, 0);
+			break;
+		}
+		inss(cmdport+Data, drive->data, len/2);
+		drive->data += len;
+		break;
+
+	case Io|Cd:
+		if(drive->pktdma)
+			atadmainterrupt(drive, drive->dlen);
+		else
+			ctlr->done = 1;
+		break;
+	}
+}
+
+static int
+atapktio(Drive* drive, uchar* cmd, int clen)
+{
+	Ctlr *ctlr;
+	int as, cmdport, ctlport, len, r, timeo;
+
+	if(cmd[0] == 0x5A && (cmd[2] & 0x3F) == 0)
+		return atamodesense(drive, cmd);
+
+	r = SDok;
+
+	drive->command = Cpkt;
+	memmove(drive->pktcmd, cmd, clen);
+	memset(drive->pktcmd+clen, 0, drive->pkt-clen);
+	drive->limit = drive->data+drive->dlen;
+
+	ctlr = drive->ctlr;
+	cmdport = ctlr->cmdport;
+	ctlport = ctlr->ctlport;
+
+	qlock(ctlr);
+
+	if(ataready(cmdport, ctlport, drive->dev, Bsy|Drq, 0, 107*1000) < 0){
+		qunlock(ctlr);
+		return -1;
+	}
+
+	ilock(ctlr);
+	if(drive->dlen && drive->dmactl && !atadmasetup(drive, drive->dlen))
+		drive->pktdma = Dma;
+	else
+		drive->pktdma = 0;
+
+	outb(cmdport+Features, drive->pktdma);
+	outb(cmdport+Count, 0);
+	outb(cmdport+Sector, 0);
+	len = 16*drive->secsize;
+	outb(cmdport+Bytelo, len);
+	outb(cmdport+Bytehi, len>>8);
+	outb(cmdport+Dh, drive->dev);
+	ctlr->done = 0;
+	ctlr->curdrive = drive;
+	ctlr->command = Cpkt;		/* debugging */
+	if(drive->pktdma)
+		atadmastart(ctlr, drive->write);
+	outb(cmdport+Command, Cpkt);
+
+	if((drive->info[Iconfig] & Mdrq) != 0x0020){
+		microdelay(1);
+		as = ataready(cmdport, ctlport, 0, Bsy, Drq|Chk, 4*1000);
+		if(as < 0)
+			r = SDtimeout;
+		else if(as & Chk)
+			r = SDcheck;
+		else
+			atapktinterrupt(drive);
+	}
+	iunlock(ctlr);
+
+	while(waserror())
+		;
+	if(!drive->pktdma)
+		sleep(ctlr, atadone, ctlr);
+	else for(timeo = 0; !ctlr->done; timeo++){
+		tsleep(ctlr, atadone, ctlr, 1000);
+		if(ctlr->done)
+			break;
+		ilock(ctlr);
+		atadmainterrupt(drive, 0);
+		if(!drive->error && timeo > 10){
+			ataabort(drive, 0);
+			atadmastop(ctlr);
+			drive->dmactl = 0;
+			drive->error |= Abrt;
+		}
+		if(drive->error){
+			drive->status |= Chk;
+			ctlr->curdrive = nil;
+		}
+		iunlock(ctlr);
+	}
+	poperror();
+
+	qunlock(ctlr);
+
+	if(drive->status & Chk)
+		r = SDcheck;
+
+	return r;
+}
+
+static uchar cmd48[256] = {
+	[Crs]	Crs48,
+	[Crd]	Crd48,
+	[Crdq]	Crdq48,
+	[Crsm]	Crsm48,
+	[Cws]	Cws48,
+	[Cwd]	Cwd48,
+	[Cwdq]	Cwdq48,
+	[Cwsm]	Cwsm48,
+};
+
+static int
+atageniostart(Drive* drive, vlong lba)
+{
+	Ctlr *ctlr;
+	uchar cmd;
+	int as, c, cmdport, ctlport, h, len, s, use48;
+
+	use48 = 0;
+	if((drive->flags&Lba48always) || (lba>>28) || drive->count > 256){
+		if(!(drive->flags & Lba48))
+			return -1;
+		use48 = 1;
+		c = h = s = 0;
+	}else if(drive->dev & Lba){
+		c = (lba>>8) & 0xFFFF;
+		h = (lba>>24) & 0x0F;
+		s = lba & 0xFF;
+	}else{
+		c = lba/(drive->s*drive->h);
+		h = ((lba/drive->s) % drive->h);
+		s = (lba % drive->s) + 1;
+	}
+
+	ctlr = drive->ctlr;
+	cmdport = ctlr->cmdport;
+	ctlport = ctlr->ctlport;
+	if(ataready(cmdport, ctlport, drive->dev, Bsy|Drq, 0, 101*1000) < 0)
+		return -1;
+
+	ilock(ctlr);
+	if(drive->dmactl && !atadmasetup(drive, drive->count*drive->secsize)){
+		if(drive->write)
+			drive->command = Cwd;
+		else
+			drive->command = Crd;
+	}
+	else if(drive->rwmctl){
+		drive->block = drive->rwm*drive->secsize;
+		if(drive->write)
+			drive->command = Cwsm;
+		else
+			drive->command = Crsm;
+	}
+	else{
+		drive->block = drive->secsize;
+		if(drive->write)
+			drive->command = Cws;
+		else
+			drive->command = Crs;
+	}
+	drive->limit = drive->data + drive->count*drive->secsize;
+	cmd = drive->command;
+	if(use48){
+		outb(cmdport+Count, (drive->count>>8) & 0xFF);
+		outb(cmdport+Count, drive->count & 0XFF);
+		outb(cmdport+Lbalo, (lba>>24) & 0xFF);
+		outb(cmdport+Lbalo, lba & 0xFF);
+		outb(cmdport+Lbamid, (lba>>32) & 0xFF);
+		outb(cmdport+Lbamid, (lba>>8) & 0xFF);
+		outb(cmdport+Lbahi, (lba>>40) & 0xFF);
+		outb(cmdport+Lbahi, (lba>>16) & 0xFF);
+		outb(cmdport+Dh, drive->dev|Lba);
+		cmd = cmd48[cmd];
+
+		if(DEBUG & Dbg48BIT)
+			print("using 48-bit commands\n");
+	}else{
+		outb(cmdport+Count, drive->count);
+		outb(cmdport+Sector, s);
+		outb(cmdport+Cyllo, c);
+		outb(cmdport+Cylhi, c>>8);
+		outb(cmdport+Dh, drive->dev|h);
+	}
+	ctlr->done = 0;
+	ctlr->curdrive = drive;
+	ctlr->command = drive->command;	/* debugging */
+	outb(cmdport+Command, cmd);
+
+	switch(drive->command){
+	case Cws:
+	case Cwsm:
+		microdelay(1);
+		as = ataready(cmdport, ctlport, 0, Bsy, Drq|Err, 1000);
+		if(as < 0 || (as & Err)){
+			iunlock(ctlr);
+			return -1;
+		}
+		len = drive->block;
+		if(drive->data+len > drive->limit)
+			len = drive->limit-drive->data;
+		outss(cmdport+Data, drive->data, len/2);
+		break;
+
+	case Crd:
+	case Cwd:
+		atadmastart(ctlr, drive->write);
+		break;
+	}
+	iunlock(ctlr);
+
+	return 0;
+}
+
+static int
+atagenioretry(Drive* drive)
+{
+	if(drive->dmactl){
+		drive->dmactl = 0;
+		print("atagenioretry: disabling dma\n");
+	}
+	else if(drive->rwmctl)
+		drive->rwmctl = 0;
+	else
+		return atasetsense(drive, SDcheck, 4, 8, drive->error);
+
+	return SDretry;
+}
+
+static int
+atagenio(Drive* drive, uchar* cmd, int)
+{
+	uchar *p;
+	Ctlr *ctlr;
+	int count, max;
+	vlong lba, len;
+
+	/*
+	 * Map SCSI commands into ATA commands for discs.
+	 * Fail any command with a LUN except INQUIRY which
+	 * will return 'logical unit not supported'.
+	 */
+	if((cmd[1]>>5) && cmd[0] != 0x12)
+		return atasetsense(drive, SDcheck, 0x05, 0x25, 0);
+
+	switch(cmd[0]){
+	default:
+		return atasetsense(drive, SDcheck, 0x05, 0x20, 0);
+
+	case 0x00:			/* test unit ready */
+		return SDok;
+
+	case 0x03:			/* request sense */
+		if(cmd[4] < sizeof(drive->sense))
+			len = cmd[4];
+		else
+			len = sizeof(drive->sense);
+		if(drive->data && drive->dlen >= len){
+			memmove(drive->data, drive->sense, len);
+			drive->data += len;
+		}
+		return SDok;
+
+	case 0x12:			/* inquiry */
+		if(cmd[4] < sizeof(drive->inquiry))
+			len = cmd[4];
+		else
+			len = sizeof(drive->inquiry);
+		if(drive->data && drive->dlen >= len){
+			memmove(drive->data, drive->inquiry, len);
+			drive->data += len;
+		}
+		return SDok;
+
+	case 0x1B:			/* start/stop unit */
+		/*
+		 * NOP for now, can use the power management feature
+		 * set later.
+		 */
+		return SDok;
+
+	case 0x25:			/* read capacity */
+		if((cmd[1] & 0x01) || cmd[2] || cmd[3])
+			return atasetsense(drive, SDcheck, 0x05, 0x24, 0);
+		if(drive->data == nil || drive->dlen < 8)
+			return atasetsense(drive, SDcheck, 0x05, 0x20, 1);
+		/*
+		 * Read capacity returns the LBA of the last sector.
+		 */
+		len = drive->sectors-1;
+		p = drive->data;
+		*p++ = len>>24;
+		*p++ = len>>16;
+		*p++ = len>>8;
+		*p++ = len;
+		len = drive->secsize;
+		*p++ = len>>24;
+		*p++ = len>>16;
+		*p++ = len>>8;
+		*p = len;
+		drive->data += 8;
+		return SDok;
+
+	case 0x9E:			/* long read capacity */
+		if((cmd[1] & 0x01) || cmd[2] || cmd[3])
+			return atasetsense(drive, SDcheck, 0x05, 0x24, 0);
+		if(drive->data == nil || drive->dlen < 8)
+			return atasetsense(drive, SDcheck, 0x05, 0x20, 1);
+		/*
+		 * Read capacity returns the LBA of the last sector.
+		 */
+		len = drive->sectors-1;
+		p = drive->data;
+		*p++ = len>>56;
+		*p++ = len>>48;
+		*p++ = len>>40;
+		*p++ = len>>32;
+		*p++ = len>>24;
+		*p++ = len>>16;
+		*p++ = len>>8;
+		*p++ = len;
+		len = drive->secsize;
+		*p++ = len>>24;
+		*p++ = len>>16;
+		*p++ = len>>8;
+		*p = len;
+		drive->data += 8;
+		return SDok;
+
+	case 0x28:			/* read */
+	case 0x2A:			/* write */
+		break;
+
+	case 0x5A:
+		return atamodesense(drive, cmd);
+	}
+
+	ctlr = drive->ctlr;
+	lba = (cmd[2]<<24)|(cmd[3]<<16)|(cmd[4]<<8)|cmd[5];
+	count = (cmd[7]<<8)|cmd[8];
+	if(drive->data == nil)
+		return SDok;
+	if(drive->dlen < count*drive->secsize)
+		count = drive->dlen/drive->secsize;
+	qlock(ctlr);
+	while(count){
+		max = (drive->flags&Lba48) ? 65536 : 256;
+		if(count > max)
+			drive->count = max;
+		else
+			drive->count = count;
+		if(atageniostart(drive, lba)){
+			ilock(ctlr);
+			atanop(drive, 0);
+			iunlock(ctlr);
+			qunlock(ctlr);
+			return atagenioretry(drive);
+		}
+
+		while(waserror())
+			;
+		tsleep(ctlr, atadone, ctlr, 30*1000);
+		poperror();
+		if(!ctlr->done){
+			/*
+			 * What should the above timeout be? In
+			 * standby and sleep modes it could take as
+			 * long as 30 seconds for a drive to respond.
+			 * Very hard to get out of this cleanly.
+			 */
+			atadumpstate(drive, cmd, lba, count);
+			ataabort(drive, 1);
+			qunlock(ctlr);
+			return atagenioretry(drive);
+		}
+
+		if(drive->status & Err){
+			qunlock(ctlr);
+			return atasetsense(drive, SDcheck, 4, 8, drive->error);
+		}
+		count -= drive->count;
+		lba += drive->count;
+	}
+	qunlock(ctlr);
+
+	return SDok;
+}
+
+static int
+atario(SDreq* r)
+{
+	Ctlr *ctlr;
+	Drive *drive;
+	SDunit *unit;
+	uchar cmd10[10], *cmdp, *p;
+	int clen, reqstatus, status;
+
+	unit = r->unit;
+	if((ctlr = unit->dev->ctlr) == nil || ctlr->drive[unit->subno] == nil){
+		r->status = SDtimeout;
+		return SDtimeout;
+	}
+	drive = ctlr->drive[unit->subno];
+
+	/*
+	 * Most SCSI commands can be passed unchanged except for
+	 * the padding on the end. The few which require munging
+	 * are not used internally. Mode select/sense(6) could be
+	 * converted to the 10-byte form but it's not worth the
+	 * effort. Read/write(6) are easy.
+	 */
+	switch(r->cmd[0]){
+	case 0x08:			/* read */
+	case 0x0A:			/* write */
+		cmdp = cmd10;
+		memset(cmdp, 0, sizeof(cmd10));
+		cmdp[0] = r->cmd[0]|0x20;
+		cmdp[1] = r->cmd[1] & 0xE0;
+		cmdp[5] = r->cmd[3];
+		cmdp[4] = r->cmd[2];
+		cmdp[3] = r->cmd[1] & 0x0F;
+		cmdp[8] = r->cmd[4];
+		clen = sizeof(cmd10);
+		break;
+
+	default:
+		cmdp = r->cmd;
+		clen = r->clen;
+		break;
+	}
+
+	qlock(drive);
+retry:
+	drive->write = r->write;
+	drive->data = r->data;
+	drive->dlen = r->dlen;
+
+	drive->status = 0;
+	drive->error = 0;
+	if(drive->pkt)
+		status = atapktio(drive, cmdp, clen);
+	else
+		status = atagenio(drive, cmdp, clen);
+	if(status == SDretry){
+		if(DbgDEBUG)
+			print("%s: retry: dma %8.8uX rwm %4.4uX\n",
+				unit->name, drive->dmactl, drive->rwmctl);
+		goto retry;
+	}
+	if(status == SDok){
+		atasetsense(drive, SDok, 0, 0, 0);
+		if(drive->data){
+			p = r->data;
+			r->rlen = drive->data - p;
+		}
+		else
+			r->rlen = 0;
+	}
+	else if(status == SDcheck && !(r->flags & SDnosense)){
+		drive->write = 0;
+		memset(cmd10, 0, sizeof(cmd10));
+		cmd10[0] = 0x03;
+		cmd10[1] = r->lun<<5;
+		cmd10[4] = sizeof(r->sense)-1;
+		drive->data = r->sense;
+		drive->dlen = sizeof(r->sense)-1;
+		drive->status = 0;
+		drive->error = 0;
+		if(drive->pkt)
+			reqstatus = atapktio(drive, cmd10, 6);
+		else
+			reqstatus = atagenio(drive, cmd10, 6);
+		if(reqstatus == SDok){
+			r->flags |= SDvalidsense;
+			atasetsense(drive, SDok, 0, 0, 0);
+		}
+	}
+	qunlock(drive);
+	r->status = status;
+	if(status != SDok)
+		return status;
+
+	/*
+	 * Fix up any results.
+	 * Many ATAPI CD-ROMs ignore the LUN field completely and
+	 * return valid INQUIRY data. Patch the response to indicate
+	 * 'logical unit not supported' if the LUN is non-zero.
+	 */
+	switch(cmdp[0]){
+	case 0x12:			/* inquiry */
+		if((p = r->data) == nil)
+			break;
+		if((cmdp[1]>>5) && (!drive->pkt || (p[0] & 0x1F) == 0x05))
+			p[0] = 0x7F;
+		/*FALLTHROUGH*/
+	default:
+		break;
+	}
+
+	return SDok;
+}
+
+static void
+atainterrupt(Ureg*, void* arg)
+{
+	Ctlr *ctlr;
+	Drive *drive;
+	int cmdport, len, status;
+
+	ctlr = arg;
+
+	ilock(ctlr);
+	if(inb(ctlr->ctlport+As) & Bsy){
+		iunlock(ctlr);
+		if(DEBUG & DbgBsy)
+			print("IBsy+");
+		return;
+	}
+	cmdport = ctlr->cmdport;
+	status = inb(cmdport+Status);
+	if((drive = ctlr->curdrive) == nil){
+		iunlock(ctlr);
+		if((DEBUG & DbgINL) && ctlr->command != Cedd)
+			print("Inil%2.2uX+", ctlr->command);
+		return;
+	}
+
+	if(status & Err)
+		drive->error = inb(cmdport+Error);
+	else switch(drive->command){
+	default:
+		drive->error = Abrt;
+		break;
+
+	case Crs:
+	case Crsm:
+		if(!(status & Drq)){
+			drive->error = Abrt;
+			break;
+		}
+		len = drive->block;
+		if(drive->data+len > drive->limit)
+			len = drive->limit-drive->data;
+		inss(cmdport+Data, drive->data, len/2);
+		drive->data += len;
+		if(drive->data >= drive->limit)
+			ctlr->done = 1;
+		break;
+
+	case Cws:
+	case Cwsm:
+		len = drive->block;
+		if(drive->data+len > drive->limit)
+			len = drive->limit-drive->data;
+		drive->data += len;
+		if(drive->data >= drive->limit){
+			ctlr->done = 1;
+			break;
+		}
+		if(!(status & Drq)){
+			drive->error = Abrt;
+			break;
+		}
+		len = drive->block;
+		if(drive->data+len > drive->limit)
+			len = drive->limit-drive->data;
+		outss(cmdport+Data, drive->data, len/2);
+		break;
+
+	case Cpkt:
+		atapktinterrupt(drive);
+		break;
+
+	case Crd:
+	case Cwd:
+		atadmainterrupt(drive, drive->count*drive->secsize);
+		break;
+
+	case Cstandby:
+		ctlr->done = 1;
+		break;
+	}
+	iunlock(ctlr);
+
+	if(drive->error){
+		status |= Err;
+		ctlr->done = 1;
+	}
+
+	if(ctlr->done){
+		ctlr->curdrive = nil;
+		drive->status = status;
+		wakeup(ctlr);
+	}
+}
+
+static SDev*
+atapnp(void)
+{
+	Ctlr *ctlr;
+	Pcidev *p;
+	int channel, ispc87415, pi, r;
+	SDev *legacy[2], *sdev, *head, *tail;
+
+	legacy[0] = legacy[1] = head = tail = nil;
+	if(sdev = ataprobe(0x1F0, 0x3F4, IrqATA0)){
+		head = tail = sdev;
+		legacy[0] = sdev;
+	}
+	if(sdev = ataprobe(0x170, 0x374, IrqATA1)){
+		if(head != nil)
+			tail->next = sdev;
+		else
+			head = sdev;
+		tail = sdev;
+		legacy[1] = sdev;
+	}
+
+	p = nil;
+	while(p = pcimatch(p, 0, 0)){
+		/*
+		 * Look for devices with the correct class and sub-class
+		 * code and known device and vendor ID; add native-mode
+		 * channels to the list to be probed, save info for the
+		 * compatibility mode channels.
+		 * Note that the legacy devices should not be considered
+		 * PCI devices by the interrupt controller.
+		 * For both native and legacy, save info for busmastering
+		 * if capable.
+		 * Promise Ultra ATA/66 (PDC20262) appears to
+		 * 1) give a sub-class of 'other mass storage controller'
+		 *    instead of 'IDE controller', regardless of whether it's
+		 *    the only controller or not;
+		 * 2) put 0 in the programming interface byte (probably
+		 *    as a consequence of 1) above).
+		 * Sub-class code 0x04 is 'RAID controller', e.g. VIA VT8237.
+		 */
+		if(p->ccrb != 0x01)
+			continue;
+		if(p->ccru != 0x01 && p->ccru != 0x04 && p->ccru != 0x80)
+			continue;
+		pi = p->ccrp;
+		ispc87415 = 0;
+
+		switch((p->did<<16)|p->vid){
+		default:
+			continue;
+
+		case (0x0002<<16)|0x100B:	/* NS PC87415 */
+			/*
+			 * Disable interrupts on both channels until
+			 * after they are probed for drives.
+			 * This must be called before interrupts are
+			 * enabled because the IRQ may be shared.
+			 */
+			ispc87415 = 1;
+			pcicfgw32(p, 0x40, 0x00000300);
+			break;
+		case (0x1000<<16)|0x1042:	/* PC-Tech RZ1000 */
+			/*
+			 * Turn off prefetch. Overkill, but cheap.
+			 */
+			r = pcicfgr32(p, 0x40);
+			r &= ~0x2000;
+			pcicfgw32(p, 0x40, r);
+			break;
+		case (0x4D38<<16)|0x105A:	/* Promise PDC20262 */
+		case (0x4D30<<16)|0x105A:	/* Promise PDC202xx */
+		case (0x4D68<<16)|0x105A:	/* Promise PDC20268 */
+		case (0x3373<<16)|0x105A:	/* Promise 20378 RAID */
+		case (0x3149<<16)|0x1106:	/* VIA VT8237 SATA/RAID */
+			pi = 0x85;
+			break;
+		case (0x0004<<16)|0x1103:	/* HighPoint HPT-370 */
+			pi = 0x85;
+			/*
+			 * Turn off fast interrupt prediction.
+			 */
+			if((r = pcicfgr8(p, 0x51)) & 0x80)
+				pcicfgw8(p, 0x51, r & ~0x80);
+			if((r = pcicfgr8(p, 0x55)) & 0x80)
+				pcicfgw8(p, 0x55, r & ~0x80);
+			break;
+		case (0x0640<<16)|0x1095:	/* CMD 640B */
+			/*
+			 * Bugfix code here...
+			 */
+			break;
+		case (0x7441<<16)|0x1022:	/* AMD 768 */
+			/*
+			 * Set:
+			 *	0x41	prefetch, postwrite;
+			 *	0x43	FIFO configuration 1/2 and 1/2;
+			 *	0x44	status register read retry;
+			 *	0x46	DMA read and end of sector flush.
+			 */
+			r = pcicfgr8(p, 0x41);
+			pcicfgw8(p, 0x41, r|0xF0);
+			r = pcicfgr8(p, 0x43);
+			pcicfgw8(p, 0x43, (r & 0x90)|0x2A);
+			r = pcicfgr8(p, 0x44);
+			pcicfgw8(p, 0x44, r|0x08);
+			r = pcicfgr8(p, 0x46);
+			pcicfgw8(p, 0x46, (r & 0x0C)|0xF0);
+			break;
+		case (0x0646<<16)|0x1095:	/* CMD 646 */
+		case (0x0571<<16)|0x1106:	/* VIA 82C686 */
+		case (0x0211<<16)|0x1166:	/* ServerWorks IB6566 */
+		case (0x1230<<16)|0x8086:	/* 82371FB (PIIX) */
+		case (0x7010<<16)|0x8086:	/* 82371SB (PIIX3) */
+		case (0x7111<<16)|0x8086:	/* 82371[AE]B (PIIX4[E]) */
+		case (0x2411<<16)|0x8086:	/* 82801AA (ICH) */
+		case (0x2421<<16)|0x8086:	/* 82801AB (ICH0) */
+		case (0x244A<<16)|0x8086:	/* 82801BA (ICH2, Mobile) */
+		case (0x244B<<16)|0x8086:	/* 82801BA (ICH2, High-End) */
+		case (0x248A<<16)|0x8086:	/* 82801CA (ICH3, Mobile) */
+		case (0x248B<<16)|0x8086:	/* 82801CA (ICH3, High-End) */
+		case (0x24CA<<16)|0x8086:	/* 82801DBM (ICH4, Mobile) */
+		case (0x24CB<<16)|0x8086:	/* 82801DB (ICH4, High-End) */
+		case (0x24DB<<16)|0x8086:	/* 82801EB (ICH5) */
+			break;
+		}
+
+		for(channel = 0; channel < 2; channel++){
+			if(pi & (1<<(2*channel))){
+				sdev = ataprobe(p->mem[0+2*channel].bar & ~0x01,
+						p->mem[1+2*channel].bar & ~0x01,
+						p->intl);
+				if(sdev == nil)
+					continue;
+
+				ctlr = sdev->ctlr;
+				if(ispc87415) {
+					ctlr->ienable = pc87415ienable;
+					print("pc87415disable: not yet implemented\n");
+				}
+
+				if(head != nil)
+					tail->next = sdev;
+				else
+					head = sdev;
+				tail = sdev;
+				ctlr->tbdf = p->tbdf;
+			}
+			else if((sdev = legacy[channel]) == nil)
+				continue;
+			else
+				ctlr = sdev->ctlr;
+
+			ctlr->pcidev = p;
+			if(!(pi & 0x80))
+				continue;
+			ctlr->bmiba = (p->mem[4].bar & ~0x01) + channel*8;
+		}
+	}
+
+if(0){
+	int port;
+	ISAConf isa;
+
+	/*
+	 * Hack for PCMCIA drives.
+	 * This will be tidied once we figure out how the whole
+	 * removeable device thing is going to work.
+	 */
+	memset(&isa, 0, sizeof(isa));
+	isa.port = 0x180;		/* change this for your machine */
+	isa.irq = 11;			/* change this for your machine */
+
+	port = isa.port+0x0C;
+	channel = pcmspecial("MK2001MPL", &isa);
+	if(channel == -1)
+		channel = pcmspecial("SunDisk", &isa);
+	if(channel == -1){
+		isa.irq = 10;
+		channel = pcmspecial("CF", &isa);
+	}
+	if(channel == -1){
+		isa.irq = 10;
+		channel = pcmspecial("OLYMPUS", &isa);
+	}
+	if(channel == -1){
+		port = isa.port+0x204;
+		channel = pcmspecial("ATA/ATAPI", &isa);
+	}
+	if(channel >= 0 && (sdev = ataprobe(isa.port, port, isa.irq)) != nil){
+		if(head != nil)
+			tail->next = sdev;
+		else
+			head = sdev;
+	}
+}
+	return head;
+}
+
+static SDev*
+atalegacy(int port, int irq)
+{
+	return ataprobe(port, port+0x204, irq);
+}
+
+static SDev*
+ataid(SDev* sdev)
+{
+	int i;
+	Ctlr *ctlr;
+	char name[32];
+
+	/*
+	 * Legacy controllers are always 'C' and 'D' and if
+	 * they exist and have drives will be first in the list.
+	 * If there are no active legacy controllers, native
+	 * controllers start at 'C'.
+	 */
+	if(sdev == nil)
+		return nil;
+	ctlr = sdev->ctlr;
+	if(ctlr->cmdport == 0x1F0 || ctlr->cmdport == 0x170)
+		i = 2;
+	else
+		i = 0;
+	while(sdev){
+		if(sdev->ifc == &sdataifc){
+			ctlr = sdev->ctlr;
+			if(ctlr->cmdport == 0x1F0)
+				sdev->idno = 'C';
+			else if(ctlr->cmdport == 0x170)
+				sdev->idno = 'D';
+			else{
+				sdev->idno = 'C'+i;
+				i++;
+			}
+			snprint(name, sizeof(name), "sd%c", sdev->idno);
+			kstrdup(&sdev->name, name);
+		}
+		sdev = sdev->next;
+	}
+
+	return nil;
+}
+
+static int
+ataenable(SDev* sdev)
+{
+	Ctlr *ctlr;
+	char name[32];
+
+	ctlr = sdev->ctlr;
+
+	if(ctlr->bmiba){
+#define ALIGN	(4 * 1024)
+		if(ctlr->pcidev != nil)
+			pcisetbme(ctlr->pcidev);
+		// ctlr->prdt = xspanalloc(Nprd*sizeof(Prd), 4, 4*1024);
+		ctlr->prdtbase = xalloc(Nprd * sizeof(Prd) + ALIGN);
+		ctlr->prdt = (Prd *)(((ulong)ctlr->prdtbase + ALIGN) & ~(ALIGN - 1));
+	}
+	snprint(name, sizeof(name), "%s (%s)", sdev->name, sdev->ifc->name);
+	intrenable(ctlr->irq, atainterrupt, ctlr, ctlr->tbdf, name);
+	outb(ctlr->ctlport+Dc, 0);
+	if(ctlr->ienable)
+		ctlr->ienable(ctlr);
+
+	return 1;
+}
+
+static int
+atadisable(SDev *sdev)
+{
+	Ctlr *ctlr;
+	char name[32];
+
+	ctlr = sdev->ctlr;
+	outb(ctlr->ctlport+Dc, Nien);		/* disable interrupts */
+	if (ctlr->idisable)
+		ctlr->idisable(ctlr);
+	snprint(name, sizeof(name), "%s (%s)", sdev->name, sdev->ifc->name);
+	intrdisable(ctlr->irq, atainterrupt, ctlr, ctlr->tbdf, name);
+	if (ctlr->bmiba) {
+		if (ctlr->pcidev)
+			pciclrbme(ctlr->pcidev);
+		xfree(ctlr->prdtbase);
+	}
+	return 0;
+}
+
+static int
+atarctl(SDunit* unit, char* p, int l)
+{
+	int n;
+	Ctlr *ctlr;
+	Drive *drive;
+
+	if((ctlr = unit->dev->ctlr) == nil || ctlr->drive[unit->subno] == nil)
+		return 0;
+	drive = ctlr->drive[unit->subno];
+
+	qlock(drive);
+	n = snprint(p, l, "config %4.4uX capabilities %4.4uX",
+		drive->info[Iconfig], drive->info[Icapabilities]);
+	if(drive->dma)
+		n += snprint(p+n, l-n, " dma %8.8uX dmactl %8.8uX",
+			drive->dma, drive->dmactl);
+	if(drive->rwm)
+		n += snprint(p+n, l-n, " rwm %ud rwmctl %ud",
+			drive->rwm, drive->rwmctl);
+	if(drive->flags&Lba48)
+		n += snprint(p+n, l-n, " lba48always %s",
+			(drive->flags&Lba48always) ? "on" : "off");
+	n += snprint(p+n, l-n, "\n");
+	if(drive->sectors){
+		n += snprint(p+n, l-n, "geometry %lld %d",
+			drive->sectors, drive->secsize);
+		if(drive->pkt == 0)
+			n += snprint(p+n, l-n, " %d %d %d",
+				drive->c, drive->h, drive->s);
+		n += snprint(p+n, l-n, "\n");
+	}
+	qunlock(drive);
+
+	return n;
+}
+
+static int
+atawctl(SDunit* unit, Cmdbuf* cb)
+{
+	int period;
+	Ctlr *ctlr;
+	Drive *drive;
+
+	if((ctlr = unit->dev->ctlr) == nil || ctlr->drive[unit->subno] == nil)
+		return 0;
+	drive = ctlr->drive[unit->subno];
+
+	qlock(drive);
+	if(waserror()){
+		qunlock(drive);
+		nexterror();
+	}
+
+	/*
+	 * Dma and rwm control is passive at the moment,
+	 * i.e. it is assumed that the hardware is set up
+	 * correctly already either by the BIOS or when
+	 * the drive was initially identified.
+	 */
+	if(strcmp(cb->f[0], "dma") == 0){
+		if(cb->nf != 2 || drive->dma == 0)
+			error(Ebadctl);
+		if(strcmp(cb->f[1], "on") == 0)
+			drive->dmactl = drive->dma;
+		else if(strcmp(cb->f[1], "off") == 0)
+			drive->dmactl = 0;
+		else
+			error(Ebadctl);
+	}
+	else if(strcmp(cb->f[0], "rwm") == 0){
+		if(cb->nf != 2 || drive->rwm == 0)
+			error(Ebadctl);
+		if(strcmp(cb->f[1], "on") == 0)
+			drive->rwmctl = drive->rwm;
+		else if(strcmp(cb->f[1], "off") == 0)
+			drive->rwmctl = 0;
+		else
+			error(Ebadctl);
+	}
+	else if(strcmp(cb->f[0], "standby") == 0){
+		switch(cb->nf){
+		default:
+			error(Ebadctl);
+		case 2:
+			period = strtol(cb->f[1], 0, 0);
+			if(period && (period < 30 || period > 240*5))
+				error(Ebadctl);
+			period /= 5;
+			break;
+		}
+		if(atastandby(drive, period) != SDok)
+			error(Ebadctl);
+	}
+	else if(strcmp(cb->f[0], "lba48always") == 0){
+		if(cb->nf != 2 || !(drive->flags&Lba48))
+			error(Ebadctl);
+		if(strcmp(cb->f[1], "on") == 0)
+			drive->flags |= Lba48always;
+		else if(strcmp(cb->f[1], "off") == 0)
+			drive->flags &= ~Lba48always;
+		else
+			error(Ebadctl);
+	}
+	else
+		error(Ebadctl);
+	qunlock(drive);
+	poperror();
+
+	return 0;
+}
+
+SDifc sdataifc = {
+	"ata",				/* name */
+
+	atapnp,				/* pnp */
+	atalegacy,			/* legacy */
+	ataid,				/* id */
+	ataenable,			/* enable */
+	atadisable,		/* disable */
+
+	scsiverify,			/* verify */
+	scsionline,			/* online */
+	atario,				/* rio */
+	atarctl,			/* rctl */
+	atawctl,			/* wctl */
+
+	scsibio,			/* bio */
+	ataprobew,		/* probe */
+	ataclear,			/* clear */
+	atastat,			/* stat */
+};
--- /dev/null
+++ b/os/pc/sdmylex.c
@@ -1,0 +1,1249 @@
+/*
+ * Mylex MultiMaster (Buslogic BT-*) SCSI Host Adapter
+ * in both 24-bit and 32-bit mode.
+ * 24-bit mode works for Adaptec AHA-154xx series too.
+ *
+ * To do:
+ *	allocate more Ccb's as needed, up to NMbox-1;
+ *	add nmbox and nccb to Ctlr struct for the above;
+ *	64-bit LUN/explicit wide support necessary?
+ *
+ */
+#include "u.h"
+#include "../port/lib.h"
+#include "mem.h"
+#include "dat.h"
+#include "fns.h"
+#include "io.h"
+#include "ureg.h"
+#include "../port/error.h"
+
+#include "../port/sd.h"
+
+#define K2BPA(va, tbdf)	PADDR(va)
+#define BPA2K(pa, tbdf)	KADDR(pa)
+
+extern SDifc sdmylexifc;
+
+enum {					/* registers */
+	Rcontrol	= 0x00,		/* WO: control register */
+	Rstatus		= 0x00,		/* RO: status register */
+	Rcpr		= 0x01,		/* WO: command/parameter register */
+	Rdatain		= 0x01,		/* RO: data-in register */
+	Rinterrupt	= 0x02,		/* RO: interrupt register */
+};
+
+enum {					/* Rcontrol */
+	Rsbus		= 0x10,		/* SCSI Bus Reset */
+	Rint		= 0x20,		/* Interrupt Reset */
+	Rsoft		= 0x40,		/* Soft Reset */
+	Rhard		= 0x80,		/* Hard Reset */
+};
+
+enum {					/* Rstatus */
+	Cmdinv		= 0x01,		/* Command Invalid */
+	Dirrdy		= 0x04,		/* Data In Register Ready */
+	Cprbsy		= 0x08,		/* Command/Parameter Register Busy */
+	Hardy		= 0x10,		/* Host Adapter Ready */
+	Inreq		= 0x20,		/* Initialisation Required */
+	Dfail		= 0x40,		/* Diagnostic Failure */
+	Dact		= 0x80,		/* Diagnostic Active */
+};
+
+enum {					/* Rcpr */
+	Cinitialise	= 0x01,		/* Initialise Mailbox */
+	Cstart		= 0x02,		/* Start Mailbox Command */
+	Cinquiry	= 0x04,		/* Adapter Inquiry */
+	Ceombri		= 0x05,		/* Enable OMBR Interrupt */
+	Cinquire	= 0x0B,		/* Inquire Configuration */
+	Cextbios	= 0x28,		/* AHA-1542: extended BIOS info. */
+	Cmbienable	= 0x29,		/* AHA-1542: Mailbox interface enable */
+	Ciem		= 0x81,		/* Initialise Extended Mailbox */
+	Ciesi		= 0x8D,		/* Inquire Extended Setup Information */
+	Cerrm		= 0x8F,		/* Enable strict round-robin mode */
+	Cwide		= 0x96,		/* Wide CCB */
+};
+
+enum {					/* Rinterrupt */
+	Imbl		= 0x01,		/* Incoming Mailbox Loaded */
+	Mbor		= 0x02,		/* Mailbox Out Ready */
+	Cmdc		= 0x04,		/* Command Complete */
+	Rsts		= 0x08,		/* SCSI Reset State */
+	Intv		= 0x80,		/* Interrupt Valid */
+};
+
+typedef struct Mbox24 Mbox24;
+struct Mbox24 {
+	uchar	code;			/* action/completion code */
+	uchar	ccb[3];			/* CCB pointer (MSB, ..., LSB) */
+};
+
+typedef struct Mbox32 Mbox32;
+struct Mbox32 {
+	uchar	ccb[4];			/* CCB pointer (LSB, ..., MSB) */
+	uchar	btstat;			/* BT-7[45]7[SD] status */
+	uchar	sdstat;			/* SCSI device status */
+	uchar	pad;
+	uchar	code;			/* action/completion code */
+};
+
+enum {					/* mailbox commands */
+	Mbfree		= 0x00,		/* Mailbox not in use */
+
+	Mbostart	= 0x01,		/* Start a mailbox command */
+	Mboabort	= 0x02,		/* Abort a mailbox command */
+
+	Mbiok		= 0x01,		/* CCB completed without error */
+	Mbiabort	= 0x02,		/* CCB aborted at request of host */
+	Mbinx		= 0x03,		/* Aborted CCB not found */
+	Mbierror	= 0x04,		/* CCB completed with error */
+};
+
+typedef struct Ccb24 Ccb24;
+typedef struct Ccb32 Ccb32;
+typedef union Ccb Ccb;
+
+typedef struct Ccb24 {
+	uchar	opcode;			/* Operation code */
+	uchar	datadir;		/* Data direction control */
+	uchar	cdblen;			/* Length of CDB */
+	uchar	senselen;		/* Length of sense area */
+	uchar	datalen[3];		/* Data length (MSB, ..., LSB) */
+	uchar	dataptr[3];		/* Data pointer (MSB, ..., LSB) */
+	uchar	linkptr[3];		/* Link pointer (MSB, ..., LSB) */
+	uchar	linkid;			/* command linking identifier */
+	uchar	btstat;			/* BT-* adapter status */
+	uchar	sdstat;			/* SCSI device status */
+	uchar	reserved[2];		/* */
+	uchar	cs[12+0xFF];		/* Command descriptor block + Sense */
+
+	void*	data;			/* buffer if address > 24-bits */
+
+	Rendez;
+	int	done;			/* command completed */
+
+	Ccb*	ccb;			/* link on free list */
+} Ccb24;
+
+
+typedef struct Ccb32 {
+	uchar	opcode;			/* Operation code */
+	uchar	datadir;		/* Data direction control */
+	uchar	cdblen;			/* Length of CDB */
+	uchar	senselen;		/* Length of sense area */
+	uchar	datalen[4];		/* Data length (LSB, ..., MSB) */
+	uchar	dataptr[4];		/* Data pointer (LSB, ..., MSB) */
+	uchar	reserved[2];
+	uchar	btstat;			/* BT-* adapter status */
+	uchar	sdstat;			/* SCSI device status */
+	uchar	targetid;		/* Target ID */
+	uchar	luntag;			/* LUN & tag */
+	uchar	cdb[12];		/* Command descriptor block */
+	uchar	ccbctl;			/* CCB control */
+	uchar	linkid;			/* command linking identifier */
+	uchar	linkptr[4];		/* Link pointer (LSB, ..., MSB) */
+	uchar	senseptr[4];		/* Sense pointer (LSB, ..., MSB) */
+	uchar	sense[0xFF];		/* Sense bytes */
+
+	Rendez;
+	int	done;			/* command completed */
+
+	Ccb*	ccb;			/* link on free list */
+} Ccb32;
+
+typedef union Ccb {
+	Ccb24;
+	Ccb32;
+} Ccb;
+
+enum {					/* opcode */
+	OInitiator	= 0x00,		/* initiator CCB */
+	Ordl		= 0x03,		/* initiator CCB with
+					 * residual data length returned
+					 */
+};
+
+enum {					/* datadir */
+	CCBdatain	= 0x08,		/* inbound, length is checked */
+	CCBdataout	= 0x10,		/* outbound, length is checked */
+};
+
+enum {					/* btstat */
+	Eok		= 0x00,		/* normal completion with no errors */
+};
+
+enum {					/* luntag */
+	TagEnable	= 0x20,		/* Tag enable */
+	SQTag		= 0x00,		/* Simple Queue Tag */
+	HQTag		= 0x40,		/* Head of Queue Tag */
+	OQTag		= 0x80,		/* Ordered Queue Tag */
+};
+
+enum {					/* CCB control */
+	NoDisc		= 0x08,		/* No disconnect */
+	NoUnd		= 0x10,		/* No underrrun error report */
+	NoData		= 0x20,		/* No data transfer */
+	NoStat		= 0x40,		/* No CCB status if zero */
+	NoIntr		= 0x80,		/* No Interrupts */
+};
+
+typedef struct Ctlr Ctlr;
+struct Ctlr {
+	int	port;			/* I/O port */
+	int	id;			/* adapter SCSI id */
+	int	bus;			/* 24 or 32 -bit */
+	int	irq;
+	int	wide;
+	Pcidev*	pcidev;
+	SDev*	sdev;
+	int	spurious;
+
+	Lock	issuelock;
+
+	Lock	ccblock;
+	QLock	ccbq;
+	Rendez	ccbr;
+
+	Lock	mboxlock;
+	void*	mb;			/* mailbox out + mailbox in */
+	int	mbox;			/* current mailbox out index into mb */
+	int	mbix;			/* current mailbox in index into mb */
+
+	Lock	cachelock;
+	Ccb*	ccb;			/* list of free Ccb's */
+	Ccb**	cache;			/* last completed Ccb */
+};
+
+/*
+ * The number of mailboxes should be a multiple of 8 (4 for Mbox32)
+ * to ensure the boundary between the out and in mailboxes doesn't
+ * straddle a cache-line boundary.
+ * The number of Ccb's should be less than the number of mailboxes to
+ * ensure no queueing is necessary on mailbox allocation.
+ */
+enum {
+	NMbox		= 8*8,		/* number of Mbox's */
+	NCcb		= NMbox-1,	/* number of Ccb's */
+};
+
+#define PADDR24(a, n)	((PADDR(a)+(n)) <= (1<<24))
+
+static void
+ccbfree(Ctlr* ctlr, Ccb* ccb)
+{
+	lock(&ctlr->ccblock);
+	if(ctlr->bus == 24)
+		((Ccb24*)ccb)->ccb = ctlr->ccb;
+	else
+		((Ccb32*)ccb)->ccb = ctlr->ccb;
+	if(ctlr->ccb == nil)
+		wakeup(&ctlr->ccbr);
+	ctlr->ccb = ccb;
+	unlock(&ctlr->ccblock);
+}
+
+static int
+ccbavailable(void* a)
+{
+	return ((Ctlr*)a)->ccb != nil;
+}
+
+static Ccb*
+ccballoc(Ctlr* ctlr)
+{
+	Ccb *ccb;
+
+	for(;;){
+		lock(&ctlr->ccblock);
+		if((ccb = ctlr->ccb) != nil){
+			if(ctlr->bus == 24)
+				 ctlr->ccb = ((Ccb24*)ccb)->ccb;
+			else
+				 ctlr->ccb = ((Ccb32*)ccb)->ccb;
+			unlock(&ctlr->ccblock);
+			break;
+		}
+
+		unlock(&ctlr->ccblock);
+		qlock(&ctlr->ccbq);
+		if(waserror()){
+			qunlock(&ctlr->ccbq);
+			continue;
+		}
+		sleep(&ctlr->ccbr, ccbavailable, ctlr);
+		qunlock(&ctlr->ccbq);
+		poperror();
+	}
+
+	return ccb;
+}
+
+static int
+done24(void* arg)
+{
+	return ((Ccb24*)arg)->done;
+}
+
+static int
+mylex24rio(SDreq* r)
+{
+	ulong p;
+	Ctlr *ctlr;
+	Ccb24 *ccb;
+	Mbox24 *mb;
+	uchar *data, lun, *sense;
+	int d, n, btstat, sdstat, target;
+
+	ctlr = r->unit->dev->ctlr;
+	target = r->unit->subno;
+	lun = (r->cmd[1]>>5) & 0x07;
+
+	/*
+	 * Ctlr->cache holds the last completed Ccb for this target if it
+	 * returned 'check condition'.
+	 * If this command is a request-sense and there is valid sense data
+	 * from the last completed Ccb, return it immediately.
+	 */
+	lock(&ctlr->cachelock);
+	if((ccb = ctlr->cache[target]) != nil){
+		ctlr->cache[target] = nil;
+		if(r->cmd[0] == 0x03
+		&& ccb->sdstat == SDcheck && lun == ((ccb->cs[1]>>5) & 0x07)){
+			unlock(&ctlr->cachelock);
+			if(r->dlen){
+				sense = &ccb->cs[ccb->cdblen];
+				n = 8+sense[7];
+				if(n > r->dlen)
+					n = r->dlen;
+				memmove(r->data, sense, n);
+				r->rlen = n;
+			}
+			ccbfree(ctlr, (Ccb*)ccb);
+			return SDok;
+		}
+	}
+	unlock(&ctlr->cachelock);
+	if(ccb == nil)
+		ccb = ccballoc(ctlr);
+
+	/*
+	 * Check if the transfer is to memory above the 24-bit limit the
+	 * controller can address. If it is, try to allocate a temporary
+	 * buffer as a staging area.
+	 */
+	n = r->dlen;
+	if(n && !PADDR24(r->data, n)){
+		data = mallocz(n, 0);
+		if(data == nil || !PADDR24(data, n)){
+			if(data != nil){
+				free(data);
+				ccb->data = nil;
+			}
+			ccbfree(ctlr, (Ccb*)ccb);
+			return SDmalloc;
+		}
+		if(r->write)
+			memmove(data, r->data, n);
+		ccb->data = r->data;
+	}
+	else
+		data = r->data;
+
+	/*
+	 * Fill in the ccb.
+	 */
+	ccb->opcode = Ordl;
+
+	ccb->datadir = (target<<5)|lun;
+	if(n == 0)
+		ccb->datadir |= CCBdataout|CCBdatain;
+	else if(!r->write)
+		ccb->datadir |= CCBdatain;
+	else
+		ccb->datadir |= CCBdataout;
+
+	ccb->cdblen = r->clen;
+	ccb->senselen = 0xFF;
+
+	ccb->datalen[0] = n>>16;
+	ccb->datalen[1] = n>>8;
+	ccb->datalen[2] = n;
+	p = PADDR(data);
+	ccb->dataptr[0] = p>>16;
+	ccb->dataptr[1] = p>>8;
+	ccb->dataptr[2] = p;
+
+	ccb->linkptr[0] = ccb->linkptr[1] = ccb->linkptr[2] = 0;
+	ccb->linkid = 0;
+	ccb->btstat = ccb->sdstat = 0;
+	ccb->reserved[0] = ccb->reserved[1] = 0;
+
+	memmove(ccb->cs, r->cmd, r->clen);
+
+	/*
+	 * There's one more mbox than there there is
+	 * ccb so there is always one free.
+	 */
+	lock(&ctlr->mboxlock);
+	mb = ctlr->mb;
+	mb += ctlr->mbox;
+	p = PADDR(ccb);
+	mb->ccb[0] = p>>16;
+	mb->ccb[1] = p>>8;
+	mb->ccb[2] = p;
+	mb->code = Mbostart;
+	ctlr->mbox++;
+	if(ctlr->mbox >= NMbox)
+		ctlr->mbox = 0;
+
+	/*
+	 * This command does not require Hardy
+	 * and doesn't generate a Cmdc interrupt.
+	 */
+	ccb->done = 0;
+	outb(ctlr->port+Rcpr, Cstart);
+	unlock(&ctlr->mboxlock);
+
+	/*
+	 * Wait for the request to complete and return the status.
+	 * Since the buffer is not reference counted cannot return
+	 * until the DMA is done writing into the buffer so the caller
+	 * cannot free the buffer prematurely.
+	 */
+	while(waserror())
+		;
+	sleep(ccb, done24, ccb);
+	poperror();
+
+	/*
+	 * Save the status and patch up the number of
+	 * bytes actually transferred.
+	 * There's a firmware bug on some 956C controllers
+	 * which causes the return count from a successful
+	 * READ CAPACITY not be updated, so fix it here.
+	 */
+	sdstat = ccb->sdstat;
+	btstat = ccb->btstat;
+
+	d = ccb->datalen[0]<<16;
+	d |= ccb->datalen[1]<<8;
+	d |= ccb->datalen[2];
+	if(ccb->cs[0] == 0x25 && sdstat == SDok)
+		d = 0;
+	n -= d;
+	r->rlen = n;
+
+	/*
+	 * Tidy things up if a staging area was used for the data,
+	 */
+	if(ccb->data != nil){
+		if(sdstat == SDok && btstat == 0 && !r->write)
+			memmove(ccb->data, data, n);
+		free(data);
+		ccb->data = nil;
+	}
+
+	/*
+	 * If there was a check-condition, save the
+	 * ccb for a possible request-sense command.
+	 */
+	if(sdstat == SDcheck){
+		if(r->flags & SDnosense){
+			lock(&ctlr->cachelock);
+			if(ctlr->cache[target])
+				ccbfree(ctlr, ctlr->cache[target]);
+			ctlr->cache[target] = (Ccb*)ccb;
+			unlock(&ctlr->cachelock);
+			return SDcheck;
+		}
+		sense = &ccb->cs[ccb->cdblen];
+		n = 8+sense[7];
+		if(n > sizeof(r->sense)-1)
+			n = sizeof(r->sense)-1;
+		memmove(r->sense, sense, n);
+		r->flags |= SDvalidsense;
+	}
+	ccbfree(ctlr, (Ccb*)ccb);
+
+	if(btstat){
+		if(btstat == 0x11)
+			return SDtimeout;
+		return SDeio;
+	}
+	return sdstat;
+}
+
+static void
+mylex24interrupt(Ureg*, void* arg)
+{
+	ulong pa;
+	Ctlr *ctlr;
+	Ccb24 *ccb;
+	Mbox24 *mb, *mbox;
+	int port, rinterrupt, rstatus;
+
+	ctlr = arg;
+	port = ctlr->port;
+
+	/*
+	 * Save and clear the interrupt(s). The only
+	 * interrupts expected are Cmdc, which is ignored,
+	 * and Imbl which means something completed.
+	 * There's one spurious interrupt left over from
+	 * initialisation, ignore it.
+	 */
+	rinterrupt = inb(port+Rinterrupt);
+	rstatus = inb(port+Rstatus);
+	outb(port+Rcontrol, Rint);
+	if((rinterrupt & ~(Cmdc|Imbl)) != Intv && ctlr->spurious++)
+		print("%s: interrupt 0x%2.2ux\n",
+			ctlr->sdev->name, rinterrupt);
+	if((rinterrupt & Cmdc) && (rstatus & Cmdinv))
+		print("%s: command invalid\n", ctlr->sdev->name);
+
+	/*
+	 * Look for something in the mail.
+	 * If there is, save the status, free the mailbox
+	 * and wakeup whoever.
+	 */
+	mb = ctlr->mb;
+	for(mbox = &mb[ctlr->mbix]; mbox->code; mbox = &mb[ctlr->mbix]){
+		pa = (mbox->ccb[0]<<16)|(mbox->ccb[1]<<8)|mbox->ccb[2];
+		ccb = BPA2K(pa, BUSUNKNOWN);
+		mbox->code = 0;
+		ccb->done = 1;
+		wakeup(ccb);
+
+		ctlr->mbix++;
+		if(ctlr->mbix >= NMbox+NMbox)
+			ctlr->mbix = NMbox;
+	}
+}
+
+static int
+done32(void* arg)
+{
+	return ((Ccb32*)arg)->done;
+}
+
+static int
+mylex32rio(SDreq* r)
+{
+	ulong p;
+	uchar lun;
+	Ctlr *ctlr;
+	Ccb32 *ccb;
+	Mbox32 *mb;
+	int d, n, btstat, sdstat, target;
+
+	ctlr = r->unit->dev->ctlr;
+	target = r->unit->subno;
+	lun = (r->cmd[1]>>5) & 0x07;
+
+	/*
+	 * Ctlr->cache holds the last completed Ccb for this target if it
+	 * returned 'check condition'.
+	 * If this command is a request-sense and there is valid sense data
+	 * from the last completed Ccb, return it immediately.
+	 */
+	lock(&ctlr->cachelock);
+	if((ccb = ctlr->cache[target]) != nil){
+		ctlr->cache[target] = nil;
+		if(r->cmd[0] == 0x03
+		&& ccb->sdstat == SDcheck && lun == (ccb->luntag & 0x07)){
+			unlock(&ctlr->cachelock);
+			if(r->dlen){
+				n = 8+ccb->sense[7];
+				if(n > r->dlen)
+					n = r->dlen;
+				memmove(r->data, ccb->sense, n);
+				r->rlen = n;
+			}
+			ccbfree(ctlr, (Ccb*)ccb);
+			return SDok;
+		}
+	}
+	unlock(&ctlr->cachelock);
+	if(ccb == nil)
+		ccb = ccballoc(ctlr);
+
+	/*
+	 * Fill in the ccb.
+	 */
+	ccb->opcode = Ordl;
+
+	n = r->dlen;
+	if(n == 0)
+		ccb->datadir = CCBdataout|CCBdatain;
+	else if(!r->write)
+		ccb->datadir = CCBdatain;
+	else
+		ccb->datadir = CCBdataout;
+
+	ccb->cdblen = r->clen;
+
+	ccb->datalen[0] = n;
+	ccb->datalen[1] = n>>8;
+	ccb->datalen[2] = n>>16;
+	ccb->datalen[3] = n>>24;
+	p = PADDR(r->data);
+	ccb->dataptr[0] = p;
+	ccb->dataptr[1] = p>>8;
+	ccb->dataptr[2] = p>>16;
+	ccb->dataptr[3] = p>>24;
+
+	ccb->targetid = target;
+	ccb->luntag = lun;
+	if(r->unit->inquiry[7] & 0x02)
+		ccb->luntag |= SQTag|TagEnable;
+	memmove(ccb->cdb, r->cmd, r->clen);
+	ccb->btstat = ccb->sdstat = 0;
+	ccb->ccbctl = 0;
+
+	/*
+	 * There's one more mbox than there there is
+	 * ccb so there is always one free.
+	 */
+	lock(&ctlr->mboxlock);
+	mb = ctlr->mb;
+	mb += ctlr->mbox;
+	p = PADDR(ccb);
+	mb->ccb[0] = p;
+	mb->ccb[1] = p>>8;
+	mb->ccb[2] = p>>16;
+	mb->ccb[3] = p>>24;
+	mb->code = Mbostart;
+	ctlr->mbox++;
+	if(ctlr->mbox >= NMbox)
+		ctlr->mbox = 0;
+
+	/*
+	 * This command does not require Hardy
+	 * and doesn't generate a Cmdc interrupt.
+	 */
+	ccb->done = 0;
+	outb(ctlr->port+Rcpr, Cstart);
+	unlock(&ctlr->mboxlock);
+
+	/*
+	 * Wait for the request to complete and return the status.
+	 * Since the buffer is not reference counted cannot return
+	 * until the DMA is done writing into the buffer so the caller
+	 * cannot free the buffer prematurely.
+	 */
+	while(waserror())
+		;
+	sleep(ccb, done32, ccb);
+	poperror();
+
+	/*
+	 * Save the status and patch up the number of
+	 * bytes actually transferred.
+	 * There's a firmware bug on some 956C controllers
+	 * which causes the return count from a successful
+	 * READ CAPACITY not to be updated, so fix it here.
+	 */
+	sdstat = ccb->sdstat;
+	btstat = ccb->btstat;
+
+	d = ccb->datalen[0];
+	d |= (ccb->datalen[1]<<8);
+	d |= (ccb->datalen[2]<<16);
+	d |= (ccb->datalen[3]<<24);
+	if(ccb->cdb[0] == 0x25 && sdstat == SDok)
+		d = 0;
+	n -= d;
+	r->rlen = n;
+
+	/*
+	 * If there was a check-condition, save the
+	 * ccb for a possible request-sense command.
+	 */
+	if(sdstat == SDcheck){
+		if(r->flags & SDnosense){
+			lock(&ctlr->cachelock);
+			if(ctlr->cache[target])
+				ccbfree(ctlr, ctlr->cache[target]);
+			ctlr->cache[target] = (Ccb*)ccb;
+			unlock(&ctlr->cachelock);
+			return SDcheck;
+		}
+		n = 8+ccb->sense[7];
+		if(n > sizeof(r->sense)-1)
+			n = sizeof(r->sense)-1;
+		memmove(r->sense, ccb->sense, n);
+		r->flags |= SDvalidsense;
+	}
+	ccbfree(ctlr, (Ccb*)ccb);
+
+	if(btstat){
+		if(btstat == 0x11)
+			return SDtimeout;
+		return SDeio;
+	}
+	return sdstat;
+}
+
+static void
+mylex32interrupt(Ureg*, void* arg)
+{
+	ulong pa;
+	Ctlr *ctlr;
+	Ccb32 *ccb;
+	Mbox32 *mb, *mbox;
+	int port, rinterrupt, rstatus;
+
+	ctlr = arg;
+	port = ctlr->port;
+
+	/*
+	 * Save and clear the interrupt(s). The only
+	 * interrupts expected are Cmdc, which is ignored,
+	 * and Imbl which means something completed.
+	 * There's one spurious interrupt left over from
+	 * initialisation, ignore it.
+	 */
+	rinterrupt = inb(port+Rinterrupt);
+	rstatus = inb(port+Rstatus);
+	outb(port+Rcontrol, Rint);
+	if((rinterrupt & ~(Cmdc|Imbl)) != Intv && ctlr->spurious++)
+		print("%s: interrupt 0x%2.2ux\n",
+			ctlr->sdev->name, rinterrupt);
+	if((rinterrupt & Cmdc) && (rstatus & Cmdinv))
+		print("%s: command invalid\n", ctlr->sdev->name);
+
+	/*
+	 * Look for something in the mail.
+	 * If there is, free the mailbox and wakeup whoever.
+	 */
+	mb = ctlr->mb;
+	for(mbox = &mb[ctlr->mbix]; mbox->code; mbox = &mb[ctlr->mbix]){
+		pa = (mbox->ccb[3]<<24)
+		    |(mbox->ccb[2]<<16)
+		    |(mbox->ccb[1]<<8)
+		    |mbox->ccb[0];
+		if(ctlr->pcidev)
+			ccb = BPA2K(pa, ctlr->pcidev->tbdf);
+		else
+			ccb = BPA2K(pa, BUSUNKNOWN);
+		mbox->code = 0;
+		ccb->done = 1;
+		wakeup(ccb);
+
+		ctlr->mbix++;
+		if(ctlr->mbix >= NMbox+NMbox)
+			ctlr->mbix = NMbox;
+	}
+}
+
+static int
+mylexrio(SDreq* r)
+{
+	int subno;
+	Ctlr *ctlr;
+
+	subno = r->unit->subno;
+	ctlr = r->unit->dev->ctlr;
+	if(subno == ctlr->id || (!ctlr->wide && subno >= 8))
+		r->status = SDtimeout;
+	else if(ctlr->bus == 24)
+		r->status = mylex24rio(r);
+	else
+		r->status = mylex32rio(r);
+	return r->status;
+}
+
+/*
+ * Issue a command to a controller. The command and its length is
+ * contained in cmd and cmdlen. If any data is to be
+ * returned, datalen should be non-zero, and the returned data
+ * will be placed in data.
+ * If Cmdc is set, bail out, the invalid command will be handled
+ * when the interrupt is processed.
+ */
+static void
+issueio(int port, uchar* cmd, int cmdlen, uchar* data, int datalen)
+{
+	int len;
+
+	if(cmd[0] != Cstart && cmd[0] != Ceombri){
+		while(!(inb(port+Rstatus) & Hardy))
+			;
+	}
+	outb(port+Rcpr, cmd[0]);
+
+	len = 1;
+	while(len < cmdlen){
+		if(!(inb(port+Rstatus) & Cprbsy)){
+			outb(port+Rcpr, cmd[len]);
+			len++;
+		}
+		if(inb(port+Rinterrupt) & Cmdc)
+			return;
+	}
+
+	if(datalen){
+		len = 0;
+		while(len < datalen){
+			if(inb(port+Rstatus) & Dirrdy){
+				data[len] = inb(port+Rdatain);
+				len++;
+			}
+			if(inb(port+Rinterrupt) & Cmdc)
+				return;
+		}
+	}
+}
+
+/*
+ * Issue a command to a controller, wait for it to complete then
+ * try to reset the interrupt. Should only be called at initialisation.
+ */
+static int
+issue(Ctlr* ctlr, uchar* cmd, int cmdlen, uchar* data, int datalen)
+{
+	int port;
+	uchar rinterrupt, rstatus;
+	static Lock mylexissuelock;
+
+	port = ctlr->port;
+
+	ilock(&ctlr->issuelock);
+	issueio(port, cmd, cmdlen, data, datalen);
+
+	while(!((rinterrupt = inb(port+Rinterrupt)) & Cmdc))
+		;
+
+	rstatus = inb(port+Rstatus);
+	outb(port+Rcontrol, Rint);
+	iunlock(&ctlr->issuelock);
+
+	if((rinterrupt & Cmdc) && (rstatus & Cmdinv))
+		return 0;
+	return 1;
+}
+
+static SDev*
+mylexprobe(int port, int irq)
+{
+	SDev *sdev;
+	Ctlr *ctlr;
+	uchar cmd[6], data[256];
+	int clen, dlen, timeo;
+
+	if(ioalloc(port, 0x3, 0, "mylex") < 0)
+		return nil;
+	ctlr = nil;
+	sdev = nil;
+	/*
+	 * Attempt to hard-reset the board and reset
+	 * the SCSI bus. If the board state doesn't settle to
+	 * idle with mailbox initialisation required, either
+	 * it isn't a compatible board or it's broken.
+	 * If the controller has SCAM set this can take a while.
+	 */
+	if(getconf("*noscsireset") != nil)
+		outb(port+Rcontrol, Rhard);
+	else
+		outb(port+Rcontrol, Rhard|Rsbus);
+	for(timeo = 0; timeo < 100; timeo++){
+		if(inb(port+Rstatus) == (Inreq|Hardy))
+			break;
+		delay(100);
+	}
+	if(inb(port+Rstatus) != (Inreq|Hardy)){
+buggery:
+		if(ctlr != nil)
+			free(ctlr);
+		if (sdev != nil)
+			free(sdev);
+		iofree(port);
+		return nil;
+	}
+
+	if((ctlr = malloc(sizeof(Ctlr))) == nil)
+		goto buggery;
+	ctlr->port = port;
+	ctlr->irq = irq;
+	ctlr->bus = 24;
+	ctlr->wide = 0;
+
+	/*
+	 * Try to determine if this is a 32-bit MultiMaster controller
+	 * by attempting to obtain the extended inquiry information;
+	 * this command is not implemented on Adaptec 154xx
+	 * controllers. If successful, the first byte of the returned
+	 * data is the host adapter bus type, 'E' for 32-bit EISA,
+	 * PCI and VLB buses.
+	 */
+	cmd[0] = Ciesi;
+	cmd[1] = 4;
+	clen = 2;
+	dlen = 256;
+	if(issue(ctlr, cmd, clen, data, dlen)){
+		if(data[0] == 'E')
+			ctlr->bus = 32;
+		ctlr->wide = data[0x0D] & 0x01;
+	}
+	else{
+		/*
+		 * Inconceivable though it may seem, a hard controller reset
+		 * is necessary here to clear out the command queue. Every
+		 * board seems to lock-up in a different way if you give an
+		 * invalid command and then try to clear out the
+		 * command/parameter and/or data-in register.
+		 * Soft reset doesn't do the job either. Fortunately no
+		 * serious initialisation has been done yet so there's nothing
+		 * to tidy up.
+		 */
+		outb(port+Rcontrol, Rhard);
+		for(timeo = 0; timeo < 100; timeo++){
+			if(inb(port+Rstatus) == (Inreq|Hardy))
+				break;
+			delay(100);
+		}
+		if(inb(port+Rstatus) != (Inreq|Hardy))
+			goto buggery;
+	}
+
+	/*
+	 * If the BIOS is enabled on the AHA-1542C/CF and BIOS options for
+	 * support of drives > 1Gb, dynamic scanning of the SCSI bus or more
+	 * than 2 drives under DOS 5.0 are enabled, the BIOS disables
+	 * accepting Cmbinit to protect against running with drivers which
+	 * don't support those options. In order to unlock the interface it
+	 * is necessary to read a lock-code using Cextbios and write it back
+	 * using Cmbienable; the lock-code is non-zero.
+	 */
+	cmd[0] = Cinquiry;
+	clen = 1;
+	dlen = 4;
+	if(issue(ctlr, cmd, clen, data, dlen) == 0)
+		goto buggery;
+	if(data[0] >= 0x43){
+		cmd[0] = Cextbios;
+		clen = 1;
+		dlen = 2;
+		if(issue(ctlr, cmd, clen, data, dlen) == 0)
+			goto buggery;
+
+		/*
+		 * Lock-code returned in data[1]. If it's non-zero write
+		 * it back along with bit 0 of byte 0 cleared to enable
+		 * mailbox initialisation.
+		 */
+		if(data[1]){
+			cmd[0] = Cmbienable;
+			cmd[1] = 0;
+			cmd[2] = data[1];
+			clen = 3;
+			if(issue(ctlr, cmd, clen, 0, 0) == 0)
+				goto buggery;
+		}
+	}
+
+	/*
+	 * Get the id, DMA and IRQ info from the board. This will
+	 * cause an interrupt which will hopefully not cause any
+	 * trouble because the interrupt number isn't known yet.
+	 * This is necessary as the DMA won't be set up if the
+	 * board has the BIOS disabled.
+	 *
+	 * If the IRQ is already known, this must be a 32-bit PCI
+	 * or EISA card, in which case the returned DMA and IRQ can
+	 * be ignored.
+	 */
+	cmd[0] = Cinquire;
+	clen = 1;
+	dlen = 3;
+	if(issue(ctlr, cmd, clen, data, dlen) == 0)
+		goto buggery;
+
+	ctlr->id = data[2] & 0x07;
+	if(ctlr->irq < 0){
+		switch(data[0]){		/* DMA Arbitration Priority */
+		case 0x80:			/* Channel 7 */
+			outb(0xD6, 0xC3);
+			outb(0xD4, 0x03);
+			break;
+		case 0x40:			/* Channel 6 */
+			outb(0xD6, 0xC2);
+			outb(0xD4, 0x02);
+			break;
+		case 0x20:			/* Channel 5 */
+			outb(0xD6, 0xC1);
+			outb(0xD4, 0x01);
+			break;
+		case 0x01:			/* Channel 0 */
+			outb(0x0B, 0xC0);
+			outb(0x0A, 0x00);
+			break;
+		default:
+			if(ctlr->bus == 24)
+				goto buggery;
+			break;
+		}
+	
+		switch(data[1]){		/* Interrupt Channel */
+		case 0x40:
+			ctlr->irq = 15;
+			break;
+		case 0x20:
+			ctlr->irq = 14;
+			break;
+		case 0x08:
+			ctlr->irq = 12;
+			break;
+		case 0x04:
+			ctlr->irq = 11;
+			break;
+		case 0x02:
+			ctlr->irq = 10;
+			break;
+		case 0x01:
+			ctlr->irq = 9;
+			break;
+		default:
+			goto buggery;
+		}
+	}
+
+	if((sdev = malloc(sizeof(SDev))) == nil)
+		goto buggery;
+	sdev->ifc = &sdmylexifc;
+	sdev->ctlr = ctlr;
+	ctlr->sdev = sdev;
+	if(!ctlr->wide)
+		sdev->nunit = 8;
+	else
+		sdev->nunit = 16;
+
+	return sdev;
+}
+
+static int mylexport[8] = {
+	0x330, 0x334, 0x230, 0x234, 0x130, 0x134, 0x000, 0x000,
+};
+
+static SDev*
+mylexpnp(void)
+{
+	Pcidev *p;
+	Ctlr *ctlr;
+	ISAConf isa;
+	int cfg, ctlrno, i, x;
+	SDev *sdev, *head, *tail;
+
+	p = nil;
+	head = tail = nil;
+	while(p = pcimatch(p, 0x104B, 0)){
+		if((sdev = mylexprobe(p->mem[0].bar & ~0x01, p->intl)) == nil)
+			continue;
+
+		ctlr = sdev->ctlr;
+		ctlr->pcidev = p;
+
+		if(head != nil)
+			tail->next = sdev;
+		else
+			head = sdev;
+		tail = sdev;
+	}
+
+	if(strncmp(KADDR(0xFFFD9), "EISA", 4) == 0){
+		for(cfg = 0x1000; cfg < MaxEISA*0x1000; cfg += 0x1000){
+			x = 0;
+			for(i = 0; i < 4; i++)
+				x |= inb(cfg+CfgEISA+i)<<(i*8);
+			if(x != 0x0142B30A && x != 0x0242B30A)
+				continue;
+	
+			x = inb(cfg+0xC8C);
+			if((sdev = mylexprobe(mylexport[x & 0x07], -1)) == nil)
+				continue;
+	
+			if(head != nil)
+				tail->next = sdev;
+			else
+				head = sdev;
+			tail = sdev;
+		}
+	}
+
+	for(ctlrno = 0; ctlrno < 4; ctlrno++){
+		memset(&isa, 0, sizeof(isa));
+		if(!isaconfig("scsi", ctlrno, &isa))
+			continue;
+		if(strcmp(isa.type, "aha1542"))
+			continue;
+		if((sdev = mylexprobe(isa.port, -1)) == nil)
+			continue;
+
+		if(head != nil)
+			tail->next = sdev;
+		else
+			head = sdev;
+		tail = sdev;
+	}
+
+	return head;
+}
+
+static SDev*
+mylexid(SDev* sdev)
+{
+	return scsiid(sdev, &sdmylexifc);
+}
+
+static int
+mylex24enable(Ctlr* ctlr)
+{
+	ulong p;
+	Ccb24 *ccb, *ccbp;
+	uchar cmd[6], *v;
+	int len;
+
+	len = (sizeof(Mbox24)*NMbox*2)+(sizeof(Ccb24)*NCcb);
+	v = xspanalloc(len, 32, 0);
+
+	if(!PADDR24(ctlr, sizeof(Ctlr)) || !PADDR24(v, len))
+		return 0;
+
+	ctlr->mb = v;
+	v += sizeof(Mbox24)*NMbox*2;
+
+	ccb = (Ccb24*)v;
+	for(ccbp = ccb; ccbp < &ccb[NCcb]; ccbp++){
+		ccbp->ccb = ctlr->ccb;
+		ctlr->ccb = (Ccb*)ccbp;
+	}
+
+	/*
+	 * Initialise the software controller and
+	 * set the board scanning the mailboxes.
+	 */
+	ctlr->mbix = NMbox;
+
+	cmd[0] = Cinitialise;
+	cmd[1] = NMbox;
+	p = K2BPA(ctlr->mb, BUSUNKNOWN);
+	cmd[2] = p>>16;
+	cmd[3] = p>>8;
+	cmd[4] = p;
+
+	return issue(ctlr, cmd, 5, 0, 0);
+}
+
+static int
+mylex32enable(Ctlr* ctlr)
+{
+	ulong p;
+	Ccb32 *ccb, *ccbp;
+	uchar cmd[6], *v;
+
+	v = xspanalloc((sizeof(Mbox32)*NMbox*2)+(sizeof(Ccb32)*NCcb), 32, 0);
+
+	ctlr->mb = v;
+	v += sizeof(Mbox32)*NMbox*2;
+
+	ccb = (Ccb32*)v;
+	for(ccbp = ccb; ccbp < &ccb[NCcb]; ccbp++){
+		/*
+		 * Fill in some stuff that doesn't change.
+		 */
+		ccbp->senselen = sizeof(ccbp->sense);
+		p = PADDR(ccbp->sense);
+		ccbp->senseptr[0] = p;
+		ccbp->senseptr[1] = p>>8;
+		ccbp->senseptr[2] = p>>16;
+		ccbp->senseptr[3] = p>>24;
+
+		ccbp->ccb = ctlr->ccb;
+		ctlr->ccb = (Ccb*)ccbp;
+	}
+
+	/*
+	 * Attempt wide mode setup.
+	 */
+	if(ctlr->wide){
+		cmd[0] = Cwide;
+		cmd[1] = 1;
+		if(!issue(ctlr, cmd, 2, 0, 0))
+			ctlr->wide = 0;
+	}
+
+	/*
+	 * Initialise the software controller and
+	 * set the board scanning the mailboxes.
+	 */
+	ctlr->mbix = NMbox;
+
+	cmd[0] = Ciem;
+	cmd[1] = NMbox;
+	if(ctlr->pcidev)
+		p = K2BPA(ctlr->mb, ctlr->tbdf);
+	else
+		p = K2BPA(ctlr->mb, BUSUNKNOWN);
+	cmd[2] = p;
+	cmd[3] = p>>8;
+	cmd[4] = p>>16;
+	cmd[5] = p>>24;
+
+	return issue(ctlr, cmd, 6, 0, 0);
+}
+
+static int
+mylexenable(SDev* sdev)
+{
+	int tbdf;
+	Ctlr *ctlr;
+	void (*interrupt)(Ureg*, void*);
+	char name[32];
+
+	ctlr = sdev->ctlr;
+	if(ctlr->cache == nil){
+		if((ctlr->cache = malloc(sdev->nunit*sizeof(Ccb*))) == nil)
+			return 0;
+	}
+
+	tbdf = BUSUNKNOWN;
+	if(ctlr->bus == 32){
+		if(ctlr->pcidev){
+			tbdf = ctlr->pcidev->tbdf;
+			pcisetbme(ctlr->pcidev);
+		}
+		if(!mylex32enable(ctlr))
+			return 0;
+		interrupt = mylex32interrupt;
+	}
+	else if(mylex24enable(ctlr))
+		interrupt = mylex24interrupt;
+	else
+		return 0;
+
+	snprint(name, sizeof(name), "sd%c (%s)", sdev->idno, sdev->ifc->name);
+	intrenable(ctlr->irq, interrupt, ctlr, tbdf, name);
+
+	return 1;
+}
+
+SDifc sdmylexifc = {
+	"mylex",			/* name */
+
+	mylexpnp,			/* pnp */
+	nil,				/* legacy */
+	mylexid,			/* id */
+	mylexenable,			/* enable */
+	nil,				/* disable */
+
+	scsiverify,			/* verify */
+	scsionline,			/* online */
+	mylexrio,			/* rio */
+	nil,				/* rctl */
+	nil,				/* wctl */
+
+	scsibio,			/* bio */
+	nil,				/* probe */
+	nil,				/* clear */
+	nil,				/* stat */
+};
--- /dev/null
+++ b/os/pc/sdscsi.c
@@ -1,0 +1,394 @@
+#include "u.h"
+#include "../port/lib.h"
+#include "mem.h"
+#include "dat.h"
+#include "fns.h"
+#include "io.h"
+#include "ureg.h"
+#include "../port/error.h"
+
+#include "../port/sd.h"
+
+static int
+scsitest(SDreq* r)
+{
+	r->write = 0;
+	memset(r->cmd, 0, sizeof(r->cmd));
+	r->cmd[1] = r->lun<<5;
+	r->clen = 6;
+	r->data = nil;
+	r->dlen = 0;
+	r->flags = 0;
+
+	r->status = ~0;
+
+	return r->unit->dev->ifc->rio(r);
+}
+
+int
+scsiverify(SDunit* unit)
+{
+	SDreq *r;
+	int i, status;
+	uchar *inquiry;
+
+	if((r = malloc(sizeof(SDreq))) == nil)
+		return 0;
+	if((inquiry = sdmalloc(sizeof(unit->inquiry))) == nil){
+		free(r);
+		return 0;
+	}
+	r->unit = unit;
+	r->lun = 0;		/* ??? */
+
+	memset(unit->inquiry, 0, sizeof(unit->inquiry));
+	r->write = 0;
+	r->cmd[0] = 0x12;
+	r->cmd[1] = r->lun<<5;
+	r->cmd[4] = sizeof(unit->inquiry)-1;
+	r->clen = 6;
+	r->data = inquiry;
+	r->dlen = sizeof(unit->inquiry)-1;
+	r->flags = 0;
+
+	r->status = ~0;
+	if(unit->dev->ifc->rio(r) != SDok){
+		free(r);
+		return 0;
+	}
+	memmove(unit->inquiry, inquiry, r->dlen);
+	free(inquiry); 
+
+	SET(status);
+	for(i = 0; i < 3; i++){
+		while((status = scsitest(r)) == SDbusy)
+			;
+		if(status == SDok || status != SDcheck)
+			break;
+		if(!(r->flags & SDvalidsense))
+			break;
+		if((r->sense[2] & 0x0F) != 0x02)
+			continue;
+
+		/*
+		 * Unit is 'not ready'.
+		 * If it is in the process of becoming ready or needs
+		 * an initialising command, set status so it will be spun-up
+		 * below.
+		 * If there's no medium, that's OK too, but don't
+		 * try to spin it up.
+		 */
+		if(r->sense[12] == 0x04){
+			if(r->sense[13] == 0x02 || r->sense[13] == 0x01){
+				status = SDok;
+				break;
+			}
+		}
+		if(r->sense[12] == 0x3A)
+			break;
+	}
+
+	if(status == SDok){
+		/*
+		 * Try to ensure a direct-access device is spinning.
+		 * Don't wait for completion, ignore the result.
+		 */
+		if((unit->inquiry[0] & 0x1F) == 0){
+			memset(r->cmd, 0, sizeof(r->cmd));
+			r->write = 0;
+			r->cmd[0] = 0x1B;
+			r->cmd[1] = (r->lun<<5)|0x01;
+			r->cmd[4] = 1;
+			r->clen = 6;
+			r->data = nil;
+			r->dlen = 0;
+			r->flags = 0;
+
+			r->status = ~0;
+			unit->dev->ifc->rio(r);
+		}
+	}
+	free(r);
+
+	if(status == SDok || status == SDcheck)
+		return 1;
+	return 0;
+}
+
+static int
+scsirio(SDreq* r)
+{
+	/*
+	 * Perform an I/O request, returning
+	 *	-1	failure
+	 *	 0	ok
+	 *	 1	no medium present
+	 *	 2	retry
+	 * The contents of r may be altered so the
+	 * caller should re-initialise if necesary.
+	 */
+	r->status = ~0;
+	switch(r->unit->dev->ifc->rio(r)){
+	default:
+		return -1;
+	case SDcheck:
+		if(!(r->flags & SDvalidsense))
+			return -1;
+		switch(r->sense[2] & 0x0F){
+		case 0x00:		/* no sense */
+		case 0x01:		/* recovered error */
+			return 2;
+		case 0x06:		/* check condition */
+			/*
+			 * 0x28 - not ready to ready transition,
+			 *	  medium may have changed.
+			 * 0x29 - power on or some type of reset.
+			 */
+			if(r->sense[12] == 0x28 && r->sense[13] == 0)
+				return 2;
+			if(r->sense[12] == 0x29)
+				return 2;
+			return -1;
+		case 0x02:		/* not ready */
+			/*
+			 * If no medium present, bail out.
+			 * If unit is becoming ready, rather than not
+			 * not ready, wait a little then poke it again. 				 */
+			if(r->sense[12] == 0x3A)
+				return 1;
+			if(r->sense[12] != 0x04 || r->sense[13] != 0x01)
+				return -1;
+
+			while(waserror())
+				;
+			tsleep(&up->sleep, return0, 0, 500);
+			poperror();
+			scsitest(r);
+			return 2;
+		default:
+			return -1;
+		}
+		return -1;
+	case SDok:
+		return 0;
+	}
+	return -1;
+}
+
+int
+scsionline(SDunit* unit)
+{
+	SDreq *r;
+	uchar *p;
+	int ok, retries;
+
+	if((r = malloc(sizeof(SDreq))) == nil)
+		return 0;
+	if((p = sdmalloc(8)) == nil){
+		free(r);
+		return 0;
+	}
+
+	ok = 0;
+
+	r->unit = unit;
+	r->lun = 0;				/* ??? */
+	for(retries = 0; retries < 10; retries++){
+		/*
+		 * Read-capacity is mandatory for DA, WORM, CD-ROM and
+		 * MO. It may return 'not ready' if type DA is not
+		 * spun up, type MO or type CD-ROM are not loaded or just
+		 * plain slow getting their act together after a reset.
+		 */
+		r->write = 0;
+		memset(r->cmd, 0, sizeof(r->cmd));
+		r->cmd[0] = 0x25;
+		r->cmd[1] = r->lun<<5;
+		r->clen = 10;
+		r->data = p;
+		r->dlen = 8;
+		r->flags = 0;
+	
+		r->status = ~0;
+		switch(scsirio(r)){
+		default:
+			break;
+		case 0:
+			unit->sectors = (p[0]<<24)|(p[1]<<16)|(p[2]<<8)|p[3];
+			if(unit->sectors == 0)
+				continue;
+			/*
+			 * Read-capacity returns the LBA of the last sector,
+			 * therefore the number of sectors must be incremented.
+			 */
+			unit->sectors++;
+			unit->secsize = (p[4]<<24)|(p[5]<<16)|(p[6]<<8)|p[7];
+
+			/*
+			 * Some ATAPI CD readers lie about the block size.
+			 * Since we don't read audio via this interface
+			 * it's okay to always fudge this.
+			 */
+			if(unit->secsize == 2352)
+				unit->secsize = 2048;
+			ok = 1;
+			break;
+		case 1:
+			ok = 1;
+			break;
+		case 2:
+			continue;
+		}
+		break;
+	}
+	free(p);
+	free(r);
+
+	if(ok)
+		return ok+retries;
+	else
+		return 0;
+}
+
+int
+scsiexec(SDunit* unit, int write, uchar* cmd, int clen, void* data, int* dlen)
+{
+	SDreq *r;
+	int status;
+
+	if((r = malloc(sizeof(SDreq))) == nil)
+		return SDmalloc;
+	r->unit = unit;
+	r->lun = cmd[1]>>5;		/* ??? */
+	r->write = write;
+	memmove(r->cmd, cmd, clen);
+	r->clen = clen;
+	r->data = data;
+	if(dlen)
+		r->dlen = *dlen;
+	r->flags = 0;
+
+	r->status = ~0;
+
+	/*
+	 * Call the device-specific I/O routine.
+	 * There should be no calls to 'error()' below this
+	 * which percolate back up.
+	 */
+	switch(status = unit->dev->ifc->rio(r)){
+	case SDok:
+		if(dlen)
+			*dlen = r->rlen;
+		/*FALLTHROUGH*/
+	case SDcheck:
+		/*FALLTHROUGH*/
+	default:
+		/*
+		 * It's more complicated than this. There are conditions
+		 * which are 'ok' but for which the returned status code
+		 * is not 'SDok'.
+		 * Also, not all conditions require a reqsense, might
+		 * need to do a reqsense here and make it available to the
+		 * caller somehow.
+		 *
+		 * Mañana.
+		 */
+		break;
+	}
+	sdfree(r);
+
+	return status;
+}
+
+long
+scsibio(SDunit* unit, int lun, int write, void* data, long nb, long bno)
+{
+	SDreq *r;
+	long rlen;
+
+	if((r = malloc(sizeof(SDreq))) == nil)
+		error(Enomem);
+	r->unit = unit;
+	r->lun = lun;
+again:
+	r->write = write;
+	if(write == 0)
+		r->cmd[0] = 0x28;
+	else
+		r->cmd[0] = 0x2A;
+	r->cmd[1] = (lun<<5);
+	r->cmd[2] = bno>>24;
+	r->cmd[3] = bno>>16;
+	r->cmd[4] = bno>>8;
+	r->cmd[5] = bno;
+	r->cmd[6] = 0;
+	r->cmd[7] = nb>>8;
+	r->cmd[8] = nb;
+	r->cmd[9] = 0;
+	r->clen = 10;
+	r->data = data;
+	r->dlen = nb*unit->secsize;
+	r->flags = 0;
+
+	r->status = ~0;
+	switch(scsirio(r)){
+	default:
+		rlen = -1;
+		break;
+	case 0:
+		rlen = r->rlen;
+		break;
+	case 2:
+		rlen = -1;
+		if(!(r->flags & SDvalidsense))
+			break;
+		switch(r->sense[2] & 0x0F){
+		default:
+			break;
+		case 0x06:		/* check condition */
+			/*
+			 * Check for a removeable media change.
+			 * If so, mark it by zapping the geometry info
+			 * to force an online request.
+			 */
+			if(r->sense[12] != 0x28 || r->sense[13] != 0)
+				break;
+			if(unit->inquiry[1] & 0x80)
+				unit->sectors = 0;
+			break;
+		case 0x02:		/* not ready */
+			/*
+			 * If unit is becoming ready,
+			 * rather than not not ready, try again.
+			 */
+			if(r->sense[12] == 0x04 && r->sense[13] == 0x01)
+				goto again;
+			break;
+		}
+		break;
+	}
+	free(r);
+
+	return rlen;
+}
+
+SDev*
+scsiid(SDev* sdev, SDifc* ifc)
+{
+	char name[32];
+	static char idno[16] = "0123456789abcdef";
+	static char *p = idno;
+
+	while(sdev){
+		if(sdev->ifc == ifc){
+			sdev->idno = *p++;
+			snprint(name, sizeof(name), "sd%c", sdev->idno);
+			kstrdup(&sdev->name, name);
+			if(p >= &idno[sizeof(idno)])
+				break;
+		}
+		sdev = sdev->next;
+	}
+
+	return nil;
+}
--- /dev/null
+++ b/os/pc/trap.c
@@ -1,0 +1,571 @@
+#include	"u.h"
+#include	"../port/lib.h"
+#include	"mem.h"
+#include	"dat.h"
+#include	"fns.h"
+#include	"io.h"
+#include	"ureg.h"
+#include	"../port/error.h"
+
+int (*breakhandler)(Ureg *ur, Proc*);
+
+static void debugbpt(Ureg*, void*);
+static void fault386(Ureg*, void*);
+static void doublefault(Ureg*, void*);
+static void unexpected(Ureg*, void*);
+static void _dumpstack(Ureg*);
+
+static Lock vctllock;
+static Vctl *vctl[256];
+
+enum
+{
+	Ntimevec = 20		/* number of time buckets for each intr */
+};
+ulong intrtimes[256][Ntimevec];
+
+void
+intrenable(int irq, void (*f)(Ureg*, void*), void* a, int tbdf, char *name)
+{
+	int vno;
+	Vctl *v;
+
+	if(f == nil){
+		print("intrenable: nil handler for %d, tbdf 0x%uX for %s\n",
+			irq, tbdf, name);
+		return;
+	}
+
+	v = xalloc(sizeof(Vctl));
+	v->isintr = 1;
+	v->irq = irq;
+	v->tbdf = tbdf;
+	v->f = f;
+	v->a = a;
+	strncpy(v->name, name, KNAMELEN-1);
+	v->name[KNAMELEN-1] = 0;
+
+	ilock(&vctllock);
+	vno = arch->intrenable(v);
+	if(vno == -1){
+		iunlock(&vctllock);
+		print("intrenable: couldn't enable irq %d, tbdf 0x%uX for %s\n",
+			irq, tbdf, v->name);
+		xfree(v);
+		return;
+	}
+	if(vctl[vno]){
+		if(vctl[vno]->isr != v->isr || vctl[vno]->eoi != v->eoi)
+			panic("intrenable: handler: %s %s %luX %luX %luX %luX\n",
+				vctl[vno]->name, v->name,
+				vctl[vno]->isr, v->isr, vctl[vno]->eoi, v->eoi);
+		v->next = vctl[vno];
+	}
+	vctl[vno] = v;
+	iunlock(&vctllock);
+}
+
+int
+intrdisable(int irq, void (*f)(Ureg *, void *), void *a, int tbdf, char *name)
+{
+	Vctl **pv, *v;
+	int vno;
+
+	/*
+	 * For now, none of this will work with the APIC code,
+	 * there is no mapping between irq and vector as the IRQ
+	 * is pretty meaningless.
+	 */
+	if(arch->intrvecno == nil)
+		return -1;
+	vno = arch->intrvecno(irq);
+	ilock(&vctllock);
+	pv = &vctl[vno];
+	while (*pv && 
+		  ((*pv)->irq != irq || (*pv)->tbdf != tbdf || (*pv)->f != f || (*pv)->a != a ||
+		   strcmp((*pv)->name, name)))
+		pv = &((*pv)->next);
+	assert(*pv);
+
+	v = *pv;
+	*pv = (*pv)->next;	/* Link out the entry */
+	
+	if(vctl[vno] == nil && arch->intrdisable != nil)
+		arch->intrdisable(irq);
+	iunlock(&vctllock);
+	xfree(v);
+	return 0;
+}
+
+static long
+irqallocread(Chan*, void *vbuf, long n, vlong offset)
+{
+	char *buf, *p, str[2*(11+1)+KNAMELEN+1+1];
+	int m, vno;
+	long oldn;
+	Vctl *v;
+
+	if(n < 0 || offset < 0)
+		error(Ebadarg);
+
+	oldn = n;
+	buf = vbuf;
+	for(vno=0; vno<nelem(vctl); vno++){
+		for(v=vctl[vno]; v; v=v->next){
+			m = snprint(str, sizeof str, "%11d %11d %.*s\n", vno, v->irq, KNAMELEN, v->name);
+			if(m <= offset)	/* if do not want this, skip entry */
+				offset -= m;
+			else{
+				/* skip offset bytes */
+				m -= offset;
+				p = str+offset;
+				offset = 0;
+
+				/* write at most max(n,m) bytes */
+				if(m > n)
+					m = n;
+				memmove(buf, p, m);
+				n -= m;
+				buf += m;
+
+				if(n == 0)
+					return oldn;
+			}	
+		}
+	}
+	return oldn - n;
+}
+
+void
+trapenable(int vno, void (*f)(Ureg*, void*), void* a, char *name)
+{
+	Vctl *v;
+
+	if(vno < 0 || vno >= VectorPIC)
+		panic("trapenable: vno %d\n", vno);
+	v = xalloc(sizeof(Vctl));
+	v->tbdf = BUSUNKNOWN;
+	v->f = f;
+	v->a = a;
+	strncpy(v->name, name, KNAMELEN);
+	v->name[KNAMELEN-1] = 0;
+
+	lock(&vctllock);
+	if(vctl[vno])
+		v->next = vctl[vno]->next;
+	vctl[vno] = v;
+	unlock(&vctllock);
+}
+
+static void
+nmienable(void)
+{
+	int x;
+
+	/*
+	 * Hack: should be locked with NVRAM access.
+	 */
+	outb(0x70, 0x80);		/* NMI latch clear */
+	outb(0x70, 0);
+
+	x = inb(0x61) & 0x07;		/* Enable NMI */
+	outb(0x61, 0x08|x);
+	outb(0x61, x);
+}
+
+void
+trapinit(void)
+{
+	int d1, v;
+	ulong vaddr;
+	Segdesc *idt;
+
+	idt = (Segdesc*)IDTADDR;
+	vaddr = (ulong)vectortable;
+	for(v = 0; v < 256; v++){
+		d1 = (vaddr & 0xFFFF0000)|SEGP;
+		switch(v){
+
+		case VectorBPT:
+			d1 |= SEGPL(3)|SEGIG;
+			break;
+
+		case VectorSYSCALL:
+			d1 |= SEGPL(3)|SEGIG;
+			break;
+
+		default:
+			d1 |= SEGPL(0)|SEGIG;
+			break;
+		}
+		idt[v].d0 = (vaddr & 0xFFFF)|(KESEL<<16);
+		idt[v].d1 = d1;
+		vaddr += 6;
+	}
+
+	/*
+	 * Special traps.
+	 * Syscall() is called directly without going through trap().
+	 */
+	trapenable(VectorBPT, debugbpt, 0, "debugpt");
+	trapenable(VectorPF, fault386, 0, "fault386");
+	trapenable(Vector2F, doublefault, 0, "doublefault");
+	trapenable(Vector15, unexpected, 0, "unexpected");
+
+	nmienable();
+
+	addarchfile("irqalloc", 0444, irqallocread, nil);
+}
+
+static char* excname[32] = {
+	"divide error",
+	"debug exception",
+	"nonmaskable interrupt",
+	"breakpoint",
+	"overflow",
+	"bounds check",
+	"invalid opcode",
+	"coprocessor not available",
+	"double fault",
+	"coprocessor segment overrun",
+	"invalid TSS",
+	"segment not present",
+	"stack exception",
+	"general protection violation",
+	"page fault",
+	"15 (reserved)",
+	"coprocessor error",
+	"alignment check",
+	"machine check",
+	"19 (reserved)",
+	"20 (reserved)",
+	"21 (reserved)",
+	"22 (reserved)",
+	"23 (reserved)",
+	"24 (reserved)",
+	"25 (reserved)",
+	"26 (reserved)",
+	"27 (reserved)",
+	"28 (reserved)",
+	"29 (reserved)",
+	"30 (reserved)",
+	"31 (reserved)",
+};
+
+/*
+ *  keep histogram of interrupt service times
+ */
+void
+intrtime(Mach*, int vno)
+{
+	USED(vno);
+}
+
+/*
+ *  All traps come here.  It is slower to have all traps call trap()
+ *  rather than directly vectoring the handler.  However, this avoids a
+ *  lot of code duplication and possible bugs.  The only exception is
+ *  VectorSYSCALL.
+ *  Trap is called with interrupts disabled via interrupt-gates.
+ */
+void
+trap(Ureg* ureg)
+{
+	int i, vno;
+	char buf[ERRMAX];
+	Vctl *ctl, *v;
+	Mach *mach;
+
+	vno = ureg->trap;
+	if(ctl = vctl[vno]){
+		if(ctl->isintr){
+			m->intr++;
+			if(vno >= VectorPIC && vno != VectorSYSCALL)
+				m->lastintr = ctl->irq;
+		}
+
+		if(ctl->isr)
+			ctl->isr(vno);
+		for(v = ctl; v != nil; v = v->next){
+			if(v->f)
+				v->f(ureg, v->a);
+		}
+		if(ctl->eoi)
+			ctl->eoi(vno);
+
+		if(ctl->isintr){
+			if(up && ctl->irq != IrqTIMER && ctl->irq != IrqCLOCK)
+				preemption(0);
+		}
+	}
+	else if(vno <= nelem(excname) && up->type == Interp){
+		spllo();
+		sprint(buf, "sys: trap: %s", excname[vno]);
+		error(buf);
+	}
+	else if(vno >= VectorPIC && vno != VectorSYSCALL){
+		/*
+		 * An unknown interrupt.
+		 * Check for a default IRQ7. This can happen when
+		 * the IRQ input goes away before the acknowledge.
+		 * In this case, a 'default IRQ7' is generated, but
+		 * the corresponding bit in the ISR isn't set.
+		 * In fact, just ignore all such interrupts.
+		 */
+
+		/* call all interrupt routines, just in case */
+		for(i = VectorPIC; i <= MaxIrqLAPIC; i++){
+			ctl = vctl[i];
+			if(ctl == nil)
+				continue;
+			if(!ctl->isintr)
+				continue;
+			for(v = ctl; v != nil; v = v->next){
+				if(v->f)
+					v->f(ureg, v->a);
+			}
+			/* should we do this? */
+			if(ctl->eoi)
+				ctl->eoi(i);
+		}
+
+		/* clear the interrupt */
+		i8259isr(vno);
+			
+		if(0)print("cpu%d: spurious interrupt %d, last %d",
+			m->machno, vno, m->lastintr);
+		if(0)if(conf.nmach > 1){
+			for(i = 0; i < 32; i++){
+				if(!(active.machs & (1<<i)))
+					continue;
+				mach = MACHP(i);
+				if(m->machno == mach->machno)
+					continue;
+				print(" cpu%d: last %d",
+					mach->machno, mach->lastintr);
+			}
+			print("\n");
+		}
+		m->spuriousintr++;
+		return;
+	}
+	else{
+		if(vno == VectorNMI){
+			nmienable();
+			if(m->machno != 0){
+				print("cpu%d: PC %8.8luX\n",
+					m->machno, ureg->pc);
+				for(;;);
+			}
+		}
+		dumpregs(ureg);
+		if(vno < nelem(excname))
+			panic("%s", excname[vno]);
+		panic("unknown trap/intr: %d\n", vno);
+	}
+
+	/* delaysched set because we held a lock or because our quantum ended */
+	if(up && up->delaysched){
+		sched();
+		splhi();
+	}
+}
+
+/*
+ *  dump registers
+ */
+void
+dumpregs2(Ureg* ureg)
+{
+	if(up)
+		print("cpu%d: registers for %s %lud\n",
+			m->machno, up->text, up->pid);
+	else
+		print("cpu%d: registers for kernel\n", m->machno);
+	print("FLAGS=%luX TRAP=%luX ECODE=%luX PC=%luX",
+		ureg->flags, ureg->trap, ureg->ecode, ureg->pc);
+	print(" SS=%4.4luX USP=%luX\n", ureg->ss & 0xFFFF, ureg->usp);
+	print("  AX %8.8luX  BX %8.8luX  CX %8.8luX  DX %8.8luX\n",
+		ureg->ax, ureg->bx, ureg->cx, ureg->dx);
+	print("  SI %8.8luX  DI %8.8luX  BP %8.8luX\n",
+		ureg->si, ureg->di, ureg->bp);
+	print("  CS %4.4luX  DS %4.4luX  ES %4.4luX  FS %4.4luX  GS %4.4luX\n",
+		ureg->cs & 0xFFFF, ureg->ds & 0xFFFF, ureg->es & 0xFFFF,
+		ureg->fs & 0xFFFF, ureg->gs & 0xFFFF);
+}
+
+void
+dumpregs(Ureg* ureg)
+{
+	extern ulong etext;
+	vlong mca, mct;
+
+	dumpregs2(ureg);
+
+	/*
+	 * Processor control registers.
+	 * If machine check exception, time stamp counter, page size extensions
+	 * or enhanced virtual 8086 mode extensions are supported, there is a
+	 * CR4. If there is a CR4 and machine check extensions, read the machine
+	 * check address and machine check type registers if RDMSR supported.
+	 */
+	print("  CR0 %8.8lux CR2 %8.8lux CR3 %8.8lux",
+		getcr0(), getcr2(), getcr3());
+	if(m->cpuiddx & 0x9A){
+		print(" CR4 %8.8lux", getcr4());
+		if((m->cpuiddx & 0xA0) == 0xA0){
+			rdmsr(0x00, &mca);
+			rdmsr(0x01, &mct);
+			print("\n  MCA %8.8llux MCT %8.8llux", mca, mct);
+		}
+	}
+	print("\n  ur %lux up %lux\n", ureg, up);
+}
+
+/*
+ * Fill in enough of Ureg to get a stack trace, and call a function.
+ * Used by debugging interface rdb.
+ */
+void
+callwithureg(void (*fn)(Ureg*))
+{
+	Ureg ureg;
+	ureg.pc = getcallerpc(&fn);
+	ureg.sp = (ulong)&fn;
+	fn(&ureg);
+}
+
+static void
+_dumpstack(Ureg *ureg)
+{
+	ulong l, v, i, estack;
+	extern ulong etext;
+
+	print("ktrace /kernel/path %.8lux %.8lux\n", ureg->pc, ureg->sp);
+	i = 0;
+	if(up
+	&& (ulong)&l >= (ulong)up->kstack
+	&& (ulong)&l <= (ulong)up->kstack+KSTACK)
+		estack = (ulong)up->kstack+KSTACK;
+	else if((ulong)&l >= (ulong)m->stack
+	&& (ulong)&l <= (ulong)m+BY2PG)
+		estack = (ulong)m+MACHSIZE;
+	else
+		return;
+
+	for(l=(ulong)&l; l<estack; l+=4){
+		v = *(ulong*)l;
+		if(KTZERO < v && v < (ulong)&etext){
+			/*
+			 * we could Pick off general CALL (((uchar*)v)[-5] == 0xE8)
+			 * and CALL indirect through AX (((uchar*)v)[-2] == 0xFF && ((uchar*)v)[-2] == 0xD0),
+			 * but this is too clever and misses faulting address.
+			 */
+			print("%.8lux=%.8lux ", l, v);
+			i++;
+		}
+		if(i == 4){
+			i = 0;
+			print("\n");
+		}
+	}
+	if(i)
+		print("\n");
+}
+
+void
+dumpstack(void)
+{
+	callwithureg(_dumpstack);
+}
+
+static void
+debugbpt(Ureg* ureg, void*)
+{
+	char buf[ERRMAX];
+
+	if(breakhandler != nil){
+		breakhandler(ureg, up);
+		return;
+	}
+	if(up == 0)
+		panic("kernel bpt");
+	/* restore pc to instruction that caused the trap */
+	ureg->pc--;
+	sprint(buf, "sys: breakpoint");
+	error(buf);
+}
+
+static void
+doublefault(Ureg*, void*)
+{
+	panic("double fault");
+}
+
+static void
+unexpected(Ureg* ureg, void*)
+{
+	print("unexpected trap %lud; ignoring\n", ureg->trap);
+}
+
+static void
+fault386(Ureg* ureg, void*)
+{
+	ulong addr;
+	int read, user;
+	char buf[ERRMAX];
+
+	addr = getcr2();
+	user = (ureg->cs & 0xFFFF) == UESEL;
+	if(!user && mmukmapsync(addr))
+		return;
+	read = !(ureg->ecode & 2);
+	spllo();
+	snprint(buf, sizeof(buf), "trap: fault %s pc=0x%lux addr=0x%lux",
+			read ? "read" : "write", ureg->pc, addr);
+	if(up->type == Interp)
+		disfault(ureg, buf);
+	dumpregs(ureg);
+	panic("fault: %s\n", buf);
+}
+
+
+
+
+
+static void
+linkproc(void)
+{
+	spllo();
+	up->kpfun(up->arg);
+	pexit("kproc dying", 0);
+}
+
+void
+kprocchild(Proc* p, void (*func)(void*), void* arg)
+{
+	/*
+	 * gotolabel() needs a word on the stack in
+	 * which to place the return PC used to jump
+	 * to linkproc().
+	 */
+	p->sched.pc = (ulong)linkproc;
+	p->sched.sp = (ulong)p->kstack+KSTACK-BY2WD;
+
+	p->kpfun = func;
+	p->arg = arg;
+}
+
+
+
+ulong
+dbgpc(Proc *p)
+{
+	Ureg *ureg;
+
+	ureg = p->dbgreg;
+	if(ureg == 0)
+		return 0;
+
+	return ureg->pc;
+}
--- /dev/null
+++ b/os/pc/tv.h
@@ -1,0 +1,15 @@
+static ushort
+swab16(ushort u) {
+	return u;
+}
+
+#define	ScreenWidth	640		/* screen width */
+#define	ScreenHeight	480		/* screen height */
+#define	XCorrection	9		/* correction for x axes (trial and error) */
+#define	YCorrection	32		/* correction for y axes (trial and error) */
+#define	HSync		1
+#define	VSync		1
+
+#define	AudioChip	TEA6320T	/* new board has TEA6320T */
+
+#define	EISA(a)		(a)
--- /dev/null
+++ b/os/pc/uarti8250.c
@@ -1,0 +1,740 @@
+#include "u.h"
+#include "../port/lib.h"
+#include "mem.h"
+#include "dat.h"
+#include "fns.h"
+#include "io.h"
+#include "../port/error.h"
+#include "../port/uart.h"
+
+/*
+ * 8250 UART and compatibles.
+ */
+enum {
+	Uart0		= 0x3F8,	/* COM1 */
+	Uart0IRQ	= 4,
+	Uart1		= 0x2F8,	/* COM2 */
+	Uart1IRQ	= 3,
+
+	UartFREQ	= 1843200,
+};
+
+enum {					/* I/O ports */
+	Rbr		= 0,		/* Receiver Buffer (RO) */
+	Thr		= 0,		/* Transmitter Holding (WO) */
+	Ier		= 1,		/* Interrupt Enable */
+	Iir		= 2,		/* Interrupt Identification (RO) */
+	Fcr		= 2,		/* FIFO Control (WO) */
+	Lcr		= 3,		/* Line Control */
+	Mcr		= 4,		/* Modem Control */
+	Lsr		= 5,		/* Line Status */
+	Msr		= 6,		/* Modem Status */
+	Scr		= 7,		/* Scratch Pad */
+	Dll		= 0,		/* Divisor Latch LSB */
+	Dlm		= 1,		/* Divisor Latch MSB */
+};
+
+enum {					/* Ier */
+	Erda		= 0x01,		/* Enable Received Data Available */
+	Ethre		= 0x02,		/* Enable Thr Empty */
+	Erls		= 0x04,		/* Enable Receiver Line Status */
+	Ems		= 0x08,		/* Enable Modem Status */
+};
+
+enum {					/* Iir */
+	Ims		= 0x00,		/* Ms interrupt */
+	Ip		= 0x01,		/* Interrupt Pending (not) */
+	Ithre		= 0x02,		/* Thr Empty */
+	Irda		= 0x04,		/* Received Data Available */
+	Irls		= 0x06,		/* Receiver Line Status */
+	Ictoi		= 0x0C,		/* Character Time-out Indication */
+	IirMASK		= 0x3F,
+	Ifena		= 0xC0,		/* FIFOs enabled */
+};
+
+enum {					/* Fcr */
+	FIFOena		= 0x01,		/* FIFO enable */
+	FIFOrclr	= 0x02,		/* clear Rx FIFO */
+	FIFOtclr	= 0x04,		/* clear Tx FIFO */
+	FIFO1		= 0x00,		/* Rx FIFO trigger level 1 byte */
+	FIFO4		= 0x40,		/*	4 bytes */
+	FIFO8		= 0x80,		/*	8 bytes */
+	FIFO14		= 0xC0,		/*	14 bytes */
+};
+
+enum {					/* Lcr */
+	Wls5		= 0x00,		/* Word Length Select 5 bits/byte */
+	Wls6		= 0x01,		/*	6 bits/byte */
+	Wls7		= 0x02,		/*	7 bits/byte */
+	Wls8		= 0x03,		/*	8 bits/byte */
+	WlsMASK		= 0x03,
+	Stb		= 0x04,		/* 2 stop bits */
+	Pen		= 0x08,		/* Parity Enable */
+	Eps		= 0x10,		/* Even Parity Select */
+	Stp		= 0x20,		/* Stick Parity */
+	Brk		= 0x40,		/* Break */
+	Dlab		= 0x80,		/* Divisor Latch Access Bit */
+};
+
+enum {					/* Mcr */
+	Dtr		= 0x01,		/* Data Terminal Ready */
+	Rts		= 0x02,		/* Ready To Send */
+	Out1		= 0x04,		/* no longer in use */
+	Ie		= 0x08,		/* IRQ Enable */
+	Dm		= 0x10,		/* Diagnostic Mode loopback */
+};
+
+enum {					/* Lsr */
+	Dr		= 0x01,		/* Data Ready */
+	Oe		= 0x02,		/* Overrun Error */
+	Pe		= 0x04,		/* Parity Error */
+	Fe		= 0x08,		/* Framing Error */
+	Bi		= 0x10,		/* Break Interrupt */
+	Thre		= 0x20,		/* Thr Empty */
+	Temt		= 0x40,		/* Tramsmitter Empty */
+	FIFOerr		= 0x80,		/* error in receiver FIFO */
+};
+
+enum {					/* Msr */
+	Dcts		= 0x01,		/* Delta Cts */
+	Ddsr		= 0x02,		/* Delta Dsr */
+	Teri		= 0x04,		/* Trailing Edge of Ri */
+	Ddcd		= 0x08,		/* Delta Dcd */
+	Cts		= 0x10,		/* Clear To Send */
+	Dsr		= 0x20,		/* Data Set Ready */
+	Ri		= 0x40,		/* Ring Indicator */
+	Dcd		= 0x80,		/* Data Set Ready */
+};
+
+typedef struct Ctlr {
+	int	io;
+	int	irq;
+	int	tbdf;
+	int	iena;
+
+	uchar	sticky[8];
+
+	Lock;
+	int	hasfifo;
+	int	checkfifo;
+	int	fena;
+} Ctlr;
+
+extern PhysUart i8250physuart;
+
+static Ctlr i8250ctlr[2] = {
+{	.io	= Uart0,
+	.irq	= Uart0IRQ,
+	.tbdf	= BUSUNKNOWN, },
+
+{	.io	= Uart1,
+	.irq	= Uart1IRQ,
+	.tbdf	= BUSUNKNOWN, },
+};
+
+static Uart i8250uart[2] = {
+{	.regs	= &i8250ctlr[0],
+	.name	= "COM1",
+	.freq	= UartFREQ,
+	.phys	= &i8250physuart,
+	.special= 0,
+	.next	= &i8250uart[1], },
+
+{	.regs	= &i8250ctlr[1],
+	.name	= "COM2",
+	.freq	= UartFREQ,
+	.phys	= &i8250physuart,
+	.special= 0,
+	.next	= nil, },
+};
+
+#define csr8r(c, r)	inb((c)->io+(r))
+#define csr8w(c, r, v)	outb((c)->io+(r), (c)->sticky[(r)]|(v))
+
+static long
+i8250status(Uart* uart, void* buf, long n, long offset)
+{
+	char *p;
+	Ctlr *ctlr;
+	uchar ier, lcr, mcr, msr;
+
+	ctlr = uart->regs;
+	p = malloc(READSTR);
+	mcr = ctlr->sticky[Mcr];
+	msr = csr8r(ctlr, Msr);
+	ier = ctlr->sticky[Ier];
+	lcr = ctlr->sticky[Lcr];
+	snprint(p, READSTR,
+		"b%d c%d d%d e%d l%d m%d p%c r%d s%d i%d\n"
+		"dev(%d) type(%d) framing(%d) overruns(%d) "
+		"berr(%d) serr(%d)%s%s%s%s\n",
+
+		uart->baud,
+		uart->hup_dcd, 
+		(msr & Dsr) != 0,
+		uart->hup_dsr,
+		(lcr & WlsMASK) + 5,
+		(ier & Ems) != 0, 
+		(lcr & Pen) ? ((lcr & Eps) ? 'e': 'o'): 'n',
+		(mcr & Rts) != 0,
+		(lcr & Stb) ? 2: 1,
+		ctlr->fena,
+
+		uart->dev,
+		uart->type,
+		uart->ferr,
+		uart->oerr,
+		uart->berr,
+		uart->serr,
+		(msr & Cts) ? " cts": "",
+		(msr & Dsr) ? " dsr": "",
+		(msr & Dcd) ? " dcd": "",
+		(msr & Ri) ? " ring": ""
+	);
+	n = readstr(offset, buf, n, p);
+	free(p);
+
+	return n;
+}
+
+static void
+i8250fifo(Uart* uart, int level)
+{
+	Ctlr *ctlr;
+
+	ctlr = uart->regs;
+	if(ctlr->hasfifo == 0)
+		return;
+
+	/*
+	 * Changing the FIFOena bit in Fcr flushes data
+	 * from both receive and transmit FIFOs; there's
+	 * no easy way to guarantee not losing data on
+	 * the receive side, but it's possible to wait until
+	 * the transmitter is really empty.
+	 */
+	ilock(ctlr);
+	while(!(csr8r(ctlr, Lsr) & Temt))
+		;
+
+	/*
+	 * Set the trigger level, default is the max.
+	 * value.
+	 * Some UARTs require FIFOena to be set before
+	 * other bits can take effect, so set it twice.
+	 */
+	ctlr->fena = level;
+	switch(level){
+	case 0:
+		break;
+	case 1:
+		level = FIFO1|FIFOena;
+		break;
+	case 4:
+		level = FIFO4|FIFOena;
+		break;
+	case 8:
+		level = FIFO8|FIFOena;
+		break;
+	default:
+		level = FIFO14|FIFOena;
+		break;
+	}
+	csr8w(ctlr, Fcr, level);
+	csr8w(ctlr, Fcr, level);
+	iunlock(ctlr);
+}
+
+static void
+i8250dtr(Uart* uart, int on)
+{
+	Ctlr *ctlr;
+
+	/*
+	 * Toggle DTR.
+	 */
+	ctlr = uart->regs;
+	if(on)
+		ctlr->sticky[Mcr] |= Dtr;
+	else
+		ctlr->sticky[Mcr] &= ~Dtr;
+	csr8w(ctlr, Mcr, 0);
+}
+
+static void
+i8250rts(Uart* uart, int on)
+{
+	Ctlr *ctlr;
+
+	/*
+	 * Toggle RTS.
+	 */
+	ctlr = uart->regs;
+	if(on)
+		ctlr->sticky[Mcr] |= Rts;
+	else
+		ctlr->sticky[Mcr] &= ~Rts;
+	csr8w(ctlr, Mcr, 0);
+}
+
+static void
+i8250modemctl(Uart* uart, int on)
+{
+	Ctlr *ctlr;
+
+	ctlr = uart->regs;
+	ilock(&uart->tlock);
+	if(on){
+		ctlr->sticky[Ier] |= Ems;
+		csr8w(ctlr, Ier, ctlr->sticky[Ier]);
+		uart->modem = 1;
+		uart->cts = csr8r(ctlr, Msr) & Cts;
+	}
+	else{
+		ctlr->sticky[Ier] &= ~Ems;
+		csr8w(ctlr, Ier, ctlr->sticky[Ier]);
+		uart->modem = 0;
+		uart->cts = 1;
+	}
+	iunlock(&uart->tlock);
+
+	/* modem needs fifo */
+	(*uart->phys->fifo)(uart, on);
+}
+
+static int
+i8250parity(Uart* uart, int parity)
+{
+	int lcr;
+	Ctlr *ctlr;
+
+	ctlr = uart->regs;
+	lcr = ctlr->sticky[Lcr] & ~(Eps|Pen);
+
+	switch(parity){
+	case 'e':
+		lcr |= Eps|Pen;
+		break;
+	case 'o':
+		lcr |= Pen;
+		break;
+	case 'n':
+		break;
+	default:
+		return -1;
+	}
+	ctlr->sticky[Lcr] = lcr;
+	csr8w(ctlr, Lcr, 0);
+
+	uart->parity = parity;
+
+	return 0;
+}
+
+static int
+i8250stop(Uart* uart, int stop)
+{
+	int lcr;
+	Ctlr *ctlr;
+
+	ctlr = uart->regs;
+	lcr = ctlr->sticky[Lcr] & ~Stb;
+
+	switch(stop){
+	case 1:
+		break;
+	case 2:
+		lcr |= Stb;
+		break;
+	default:
+		return -1;
+	}
+	ctlr->sticky[Lcr] = lcr;
+	csr8w(ctlr, Lcr, 0);
+
+	uart->stop = stop;
+
+	return 0;
+}
+
+static int
+i8250bits(Uart* uart, int bits)
+{
+	int lcr;
+	Ctlr *ctlr;
+
+	ctlr = uart->regs;
+	lcr = ctlr->sticky[Lcr] & ~WlsMASK;
+
+	switch(bits){
+	case 5:
+		lcr |= Wls5;
+		break;
+	case 6:
+		lcr |= Wls6;
+		break;
+	case 7:
+		lcr |= Wls7;
+		break;
+	case 8:
+		lcr |= Wls8;
+		break;
+	default:
+		return -1;
+	}
+	ctlr->sticky[Lcr] = lcr;
+	csr8w(ctlr, Lcr, 0);
+
+	uart->bits = bits;
+
+	return 0;
+}
+
+static int
+i8250baud(Uart* uart, int baud)
+{
+	ulong bgc;
+	Ctlr *ctlr;
+
+	/*
+	 * Set the Baud rate by calculating and setting the Baud rate
+	 * Generator Constant. This will work with fairly non-standard
+	 * Baud rates.
+	 */
+	if(uart->freq == 0 || baud <= 0)
+		return -1;
+	bgc = (uart->freq+8*baud-1)/(16*baud);
+
+	ctlr = uart->regs;
+	csr8w(ctlr, Lcr, Dlab);
+	outb(ctlr->io+Dlm, bgc>>8);
+	outb(ctlr->io+Dll, bgc);
+	csr8w(ctlr, Lcr, 0);
+
+	uart->baud = baud;
+
+	return 0;
+}
+
+static void
+i8250break(Uart* uart, int ms)
+{
+	Ctlr *ctlr;
+
+	/*
+	 * Send a break.
+	 */
+	if(ms <= 0)
+		ms = 200;
+
+	ctlr = uart->regs;
+	csr8w(ctlr, Lcr, Brk);
+	tsleep(&up->sleep, return0, 0, ms);
+	csr8w(ctlr, Lcr, 0);
+}
+
+static void
+i8250kick(Uart* uart)
+{
+	int i;
+	Ctlr *ctlr;
+
+	if(uart->cts == 0 || uart->blocked)
+		return;
+
+	/*
+	 *  128 here is an arbitrary limit to make sure
+	 *  we don't stay in this loop too long.  If the
+	 *  chip's output queue is longer than 128, too
+	 *  bad -- presotto
+	 */
+	ctlr = uart->regs;
+	for(i = 0; i < 128; i++){
+		if(!(csr8r(ctlr, Lsr) & Thre))
+			break;
+		if(uart->op >= uart->oe && uartstageoutput(uart) == 0)
+			break;
+		outb(ctlr->io+Thr, *(uart->op++));
+	}
+}
+
+static void
+i8250interrupt(Ureg*, void* arg)
+{
+	Ctlr *ctlr;
+	Uart *uart;
+	int iir, lsr, old, r;
+
+	uart = arg;
+
+	ctlr = uart->regs;
+	for(iir = csr8r(ctlr, Iir); !(iir & Ip); iir = csr8r(ctlr, Iir)){
+		switch(iir & IirMASK){
+		case Ims:		/* Ms interrupt */
+			r = csr8r(ctlr, Msr);
+			if(r & Dcts){
+				ilock(&uart->tlock);
+				old = uart->cts;
+				uart->cts = r & Cts;
+				if(old == 0 && uart->cts)
+					uart->ctsbackoff = 2;
+				iunlock(&uart->tlock);
+			}
+		 	if(r & Ddsr){
+				old = r & Dsr;
+				if(uart->hup_dsr && uart->dsr && !old)
+					uart->dohup = 1;
+				uart->dsr = old;
+			}
+		 	if(r & Ddcd){
+				old = r & Dcd;
+				if(uart->hup_dcd && uart->dcd && !old)
+					uart->dohup = 1;
+				uart->dcd = old;
+			}
+			break;
+		case Ithre:		/* Thr Empty */
+			uartkick(uart);
+			break;
+		case Irda:		/* Received Data Available */
+		case Irls:		/* Receiver Line Status */
+		case Ictoi:		/* Character Time-out Indication */
+			/*
+			 * Consume any received data.
+			 * If the received byte came in with a break,
+			 * parity or framing error, throw it away;
+			 * overrun is an indication that something has
+			 * already been tossed.
+			 */
+			while((lsr = csr8r(ctlr, Lsr)) & Dr){
+				if(lsr & (FIFOerr|Oe))
+					uart->oerr++;
+				if(lsr & Pe)
+					uart->perr++;
+				if(lsr & Fe)
+					uart->ferr++;
+				r = csr8r(ctlr, Rbr);
+				if(!(lsr & (Bi|Fe|Pe)))
+					uartrecv(uart, r);
+			}
+			break;
+
+		default:
+			iprint("weird uart interrupt 0x%2.2uX\n", iir);
+			break;
+		}
+	}
+}
+
+static void
+i8250disable(Uart* uart)
+{
+	Ctlr *ctlr;
+
+	/*
+ 	 * Turn off DTR and RTS, disable interrupts and fifos.
+	 */
+	(*uart->phys->dtr)(uart, 0);
+	(*uart->phys->rts)(uart, 0);
+	(*uart->phys->fifo)(uart, 0);
+
+	ctlr = uart->regs;
+	ctlr->sticky[Ier] = 0;
+	csr8w(ctlr, Ier, ctlr->sticky[Ier]);
+
+	if(ctlr->iena != 0){
+		if(intrdisable(ctlr->irq, i8250interrupt, uart, ctlr->tbdf, uart->name) == 0)
+			ctlr->iena = 0;
+	}
+}
+
+static void
+i8250enable(Uart* uart, int ie)
+{
+	Ctlr *ctlr;
+
+	ctlr = uart->regs;
+
+	/*
+	 * Check if there is a FIFO.
+	 * Changing the FIFOena bit in Fcr flushes data
+	 * from both receive and transmit FIFOs; there's
+	 * no easy way to guarantee not losing data on
+	 * the receive side, but it's possible to wait until
+	 * the transmitter is really empty.
+	 * Also, reading the Iir outwith i8250interrupt()
+	 * can be dangerous, but this should only happen
+	 * once, before interrupts are enabled.
+	 */
+	ilock(ctlr);
+	if(!ctlr->checkfifo){
+		/*
+		 * Wait until the transmitter is really empty.
+		 */
+		while(!(csr8r(ctlr, Lsr) & Temt))
+			;
+		csr8w(ctlr, Fcr, FIFOena);
+		if(csr8r(ctlr, Iir) & Ifena)
+			ctlr->hasfifo = 1;
+		csr8w(ctlr, Fcr, 0);
+		ctlr->checkfifo = 1;
+	}
+	iunlock(ctlr);
+
+	/*
+ 	 * Enable interrupts and turn on DTR and RTS.
+	 * Be careful if this is called to set up a polled serial line
+	 * early on not to try to enable interrupts as interrupt-
+	 * -enabling mechanisms might not be set up yet.
+	 */
+	if(ie){
+		if(ctlr->iena == 0){
+			intrenable(ctlr->irq, i8250interrupt, uart, ctlr->tbdf, uart->name);
+			ctlr->iena = 1;
+		}
+		ctlr->sticky[Ier] = Ethre|Erda;
+		ctlr->sticky[Mcr] |= Ie;
+	}
+	else{
+		ctlr->sticky[Ier] = 0;
+		ctlr->sticky[Mcr] = 0;
+	}
+	csr8w(ctlr, Ier, ctlr->sticky[Ier]);
+	csr8w(ctlr, Mcr, ctlr->sticky[Mcr]);
+
+	(*uart->phys->dtr)(uart, 1);
+	(*uart->phys->rts)(uart, 1);
+
+	/*
+	 * During startup, the i8259 interrupt controller is reset.
+	 * This may result in a lost interrupt from the i8250 uart.
+	 * The i8250 thinks the interrupt is still outstanding and does not
+	 * generate any further interrupts. The workaround is to call the
+	 * interrupt handler to clear any pending interrupt events.
+	 * Note: this must be done after setting Ier.
+	 */
+	if(ie)
+		i8250interrupt(nil, uart);
+}
+
+void*
+i8250alloc(int io, int irq, int tbdf)
+{
+	Ctlr *ctlr;
+
+	if((ctlr = malloc(sizeof(Ctlr))) != nil){
+		ctlr->io = io;
+		ctlr->irq = irq;
+		ctlr->tbdf = tbdf;
+	}
+
+	return ctlr;
+}
+
+static Uart*
+i8250pnp(void)
+{
+	return i8250uart;
+}
+
+static int
+i8250getc(Uart *uart)
+{
+	Ctlr *ctlr;
+
+	ctlr = uart->regs;
+	while(!(csr8r(ctlr, Lsr)&Dr))
+		delay(1);
+	return csr8r(ctlr, Rbr);
+}
+
+static void
+i8250putc(Uart *uart, int c)
+{
+	int i;
+	Ctlr *ctlr;
+
+	ctlr = uart->regs;
+	for(i = 0; !(csr8r(ctlr, Lsr)&Thre) && i < 128; i++)
+		delay(1);
+	outb(ctlr->io+Thr, c);
+	for(i = 0; !(csr8r(ctlr, Lsr)&Thre) && i < 128; i++)
+		delay(1);
+}
+
+PhysUart i8250physuart = {
+	.name		= "i8250",
+	.pnp		= i8250pnp,
+	.enable		= i8250enable,
+	.disable	= i8250disable,
+	.kick		= i8250kick,
+	.dobreak	= i8250break,
+	.baud		= i8250baud,
+	.bits		= i8250bits,
+	.stop		= i8250stop,
+	.parity		= i8250parity,
+	.modemctl	= i8250modemctl,
+	.rts		= i8250rts,
+	.dtr		= i8250dtr,
+	.status		= i8250status,
+	.fifo		= i8250fifo,
+	.getc		= i8250getc,
+	.putc		= i8250putc,
+};
+
+void
+i8250console(void)
+{
+	Uart *uart;
+	int n;
+	char *cmd, *p;
+
+	if((p = getconf("console")) == nil)
+		return;
+	n = strtoul(p, &cmd, 0);
+	if(p == cmd)
+		return;
+	switch(n){
+	default:
+		return;
+	case 0:
+		uart = &i8250uart[0];
+		break;
+	case 1:
+		uart = &i8250uart[1];
+		break;	
+	}
+
+	(*uart->phys->enable)(uart, 0);
+	uartctl(uart, "b9600 l8 pn s1");
+	if(*cmd != '\0')
+		uartctl(uart, cmd);
+
+	consuart = uart;
+	uart->console = 1;
+}
+
+void
+i8250mouse(char* which, int (*putc)(Queue*, int), int setb1200)
+{
+	char *p;
+	int port;
+
+	port = strtol(which, &p, 0);
+	if(p == which || port < 0 || port > 1)
+		error(Ebadarg);
+	uartmouse(&i8250uart[port], putc, setb1200);
+}
+
+void
+i8250setmouseputc(char* which, int (*putc)(Queue*, int))
+{
+	char *p;
+	int port;
+
+	port = strtol(which, &p, 0);
+	if(p == which || port < 0 || port > 1)
+		error(Ebadarg);
+	uartsetmouseputc(&i8250uart[port], putc);
+
+}
--- /dev/null
+++ b/os/pc/uartisa.c
@@ -1,0 +1,98 @@
+#include "u.h"
+#include "../port/lib.h"
+#include "mem.h"
+#include "dat.h"
+#include "fns.h"
+#include "io.h"
+#include "../port/error.h"
+#include "../port/uart.h"
+
+extern PhysUart i8250physuart;
+extern PhysUart isaphysuart;
+extern void* i8250alloc(int, int, int);
+
+static Uart*
+uartisa(int ctlrno, ISAConf* isa)
+{
+	int io;
+	void *ctlr;
+	Uart *uart;
+	char buf[64];
+
+	io = isa->port;
+	snprint(buf, sizeof(buf), "%s%d", isaphysuart.name, ctlrno);
+	if(ioalloc(io, 8, 0, buf) < 0){
+		print("uartisa: I/O 0x%uX in use\n", io);
+		return nil;
+	}
+
+	uart = malloc(sizeof(Uart));
+	ctlr = i8250alloc(io, isa->irq, BUSUNKNOWN);
+	if(ctlr == nil){
+		iofree(io);
+		free(uart);
+		return nil;
+	}
+
+	uart->regs = ctlr;
+	snprint(buf, sizeof(buf), "COM%d", ctlrno+1);
+	kstrdup(&uart->name, buf);
+	uart->freq = isa->freq;
+	uart->phys = &i8250physuart;
+
+	return uart;
+}
+
+static Uart*
+uartisapnp(void)
+{
+	int ctlrno;
+	ISAConf isa;
+	Uart *head, *tail, *uart;
+
+	/*
+	 * Look for up to 4 discrete UARTs on the ISA bus.
+	 * All suitable devices are configured to simply point
+	 * to the generic i8250 driver.
+	 */
+	head = tail = nil;
+	for(ctlrno = 2; ctlrno < 6; ctlrno++){
+		memset(&isa, 0, sizeof(isa));
+		if(!isaconfig("uart", ctlrno, &isa))
+			continue;
+		if(strcmp(isa.type, "isa") != 0)
+			continue;
+		if(isa.port == 0 || isa.irq == 0)
+			continue;
+		if(isa.freq == 0)
+			isa.freq = 1843200;
+		uart = uartisa(ctlrno, &isa);
+		if(uart == nil)
+			continue;
+		if(head != nil)
+			tail->next = uart;
+		else
+			head = uart;
+		tail = uart;
+	}
+
+	return head;
+}
+
+PhysUart isaphysuart = {
+	.name		= "UartISA",
+	.pnp		= uartisapnp,
+	.enable		= nil,
+	.disable	= nil,
+	.kick		= nil,
+	.dobreak	= nil,
+	.baud		= nil,
+	.bits		= nil,
+	.stop		= nil,
+	.parity		= nil,
+	.modemctl	= nil,
+	.rts		= nil,
+	.dtr		= nil,
+	.status		= nil,
+	.fifo		= nil,
+};
--- /dev/null
+++ b/os/pc/uartpci.c
@@ -1,0 +1,137 @@
+#include "u.h"
+#include "../port/lib.h"
+#include "mem.h"
+#include "dat.h"
+#include "fns.h"
+#include "io.h"
+#include "../port/error.h"
+#include "../port/uart.h"
+
+extern PhysUart i8250physuart;
+extern PhysUart pciphysuart;
+extern void* i8250alloc(int, int, int);
+
+static Uart*
+uartpci(int ctlrno, Pcidev* p, int barno, int n, int freq, char* name)
+{
+	int i, io;
+	void *ctlr;
+	char buf[64];
+	Uart *head, *uart;
+
+	io = p->mem[barno].bar & ~0x01;
+	snprint(buf, sizeof(buf), "%s%d", pciphysuart.name, ctlrno);
+	if(ioalloc(io, p->mem[barno].size, 0, buf) < 0){
+		print("uartpci: I/O 0x%uX in use\n", io);
+		return nil;
+	}
+
+	head = uart = malloc(sizeof(Uart)*n);
+
+	for(i = 0; i < n; i++){
+		ctlr = i8250alloc(io, p->intl, p->tbdf);
+		io += 8;
+		if(ctlr == nil)
+			continue;
+
+		uart->regs = ctlr;
+		snprint(buf, sizeof(buf), "%s.%8.8uX", name, p->tbdf);
+		kstrdup(&uart->name, buf);
+		uart->freq = freq;
+		uart->phys = &i8250physuart;
+		if(uart != head)
+			(uart-1)->next = uart;
+		uart++;
+	}
+
+	return head;
+}
+
+static Uart*
+uartpcipnp(void)
+{
+	Pcidev *p;
+	char *name;
+	int ctlrno, n, subid;
+	Uart *head, *tail, *uart;
+
+	/*
+	 * Loop through all PCI devices looking for simple serial
+	 * controllers (ccrb == 0x07) and configure the ones which
+	 * are familiar. All suitable devices are configured to
+	 * simply point to the generic i8250 driver.
+	 */
+	head = tail = nil;
+	ctlrno = 0;
+	for(p = pcimatch(nil, 0, 0); p != nil; p = pcimatch(p, 0, 0)){
+		if(p->ccrb != 0x07 || p->ccru != 0)
+			continue;
+
+		switch((p->did<<16)|p->vid){
+		default:
+			continue;
+		case (0x9050<<16)|0x10B5:	/* Perle PCI-Fast4 series */
+		case (0x9030<<16)|0x10B5:	/* Perle Ultraport series */
+			/*
+			 * These devices consists of a PLX bridge (the above
+			 * PCI VID+DID) behind which are some 16C654 UARTs.
+			 * Must check the subsystem VID and DID for correct
+			 * match.
+			 */
+			subid = pcicfgr16(p, PciSVID);
+			subid |= pcicfgr16(p, PciSID)<<16;
+			switch(subid){
+			default:
+				continue;
+			case (0x0011<<16)|0x12E0:	/* Perle PCI-Fast16 */
+				n = 16;
+				name = "PCI-Fast16";
+				break;
+			case (0x0021<<16)|0x12E0:	/* Perle PCI-Fast8 */
+				n = 8;
+				name = "PCI-Fast8";
+				break;
+			case (0x0031<<16)|0x12E0:	/* Perle PCI-Fast4 */
+				n = 4;
+				name = "PCI-Fast4";
+				break;
+			case (0x0021<<16)|0x155F:	/* Perle Ultraport8 */
+				n = 8;
+				name = "Ultraport8";	/* 16C754 UARTs */
+				break;
+			}
+			uart = uartpci(ctlrno, p, 2, n, 7372800, name);
+			if(uart == nil)
+				continue;
+			break;
+		}
+
+		if(head != nil)
+			tail->next = uart;
+		else
+			head = uart;
+		for(tail = uart; tail->next != nil; tail = tail->next)
+			;
+		ctlrno++;
+	}
+
+	return head;
+}
+
+PhysUart pciphysuart = {
+	.name		= "UartPCI",
+	.pnp		= uartpcipnp,
+	.enable		= nil,
+	.disable	= nil,
+	.kick		= nil,
+	.dobreak	= nil,
+	.baud		= nil,
+	.bits		= nil,
+	.stop		= nil,
+	.parity		= nil,
+	.modemctl	= nil,
+	.rts		= nil,
+	.dtr		= nil,
+	.status		= nil,
+	.fifo		= nil,
+};
--- /dev/null
+++ b/os/pc/usb.h
@@ -1,0 +1,160 @@
+typedef struct Ctlr Ctlr;
+typedef struct Endpt Endpt;
+typedef struct Udev Udev;
+typedef struct Usbhost Usbhost;
+
+enum
+{
+	MaxUsb = 10,		/* max number of USB Host Controller Interfaces (Usbhost*) */
+	MaxUsbDev = 32,	/* max number of attached USB devices, including root hub (Udev*) */
+
+	/*
+	 * USB packet definitions...
+ 	*/
+	TokIN = 0x69,
+	TokOUT = 0xE1,
+	TokSETUP = 0x2D,
+
+	/* request type */
+	RH2D = 0<<7,
+	RD2H = 1<<7,
+	Rstandard = 0<<5,
+	Rclass = 1<<5,
+	Rvendor = 2<<5,
+	Rdevice = 0,
+	Rinterface = 1,
+	Rendpt = 2,
+	Rother = 3,
+};
+
+#define Class(csp)		((csp)&0xff)
+#define Subclass(csp)	(((csp)>>8)&0xff)
+#define Proto(csp)		(((csp)>>16)&0xff)
+#define CSP(c, s, p)	((c) | ((s)<<8) | ((p)<<16))
+
+/*
+ * device endpoint
+ */
+struct Endpt
+{
+	Ref;
+	Lock;
+	int		x;		/* index in Udev.ep */
+	int		id;		/* hardware endpoint address */
+	int		maxpkt;	/* maximum packet size (from endpoint descriptor) */
+	int		data01;	/* 0=DATA0, 1=DATA1 */
+	uchar	eof;
+	ulong	csp;
+	uchar	mode;	/* OREAD, OWRITE, ORDWR */
+	uchar	nbuf;	/* number of buffers allowed */
+	uchar	iso;
+	uchar	debug;
+	uchar	active;	/* listed for examination by interrupts */
+	int		setin;
+	/* ISO related: */
+	int		hz;
+	int		remain;	/* for packet size calculations */
+	int		samplesz;
+	int		sched;	/* schedule index; -1 if undefined or aperiodic */
+	int		pollms;	/* polling interval in msec */
+	int		psize;	/* (remaining) size of this packet */
+	int		off;		/* offset into packet */
+	/* Real-time iso stuff */
+	ulong	foffset;	/* file offset (to detect seeks) */
+	ulong	poffset;	/* offset of next packet to be queued */
+	ulong	toffset;	/* offset associated with time */
+	vlong	time;		/* timeassociated with offset */
+	int		buffered;	/* bytes captured but unread, or written but unsent */
+	/* end ISO stuff */
+
+	Udev*	dev;	/* owning device */
+
+	ulong	nbytes;
+	ulong	nblocks;
+
+	void	*private;
+
+	// all the rest could (should?) move to the driver private structure; except perhaps err
+	QLock	rlock;
+	Rendez	rr;
+	Queue*	rq;
+	QLock	wlock;
+	Rendez	wr;
+	Queue*	wq;
+
+	int		ntd;
+	char*	err;		// needs to be global for unstall; fix?
+
+	Endpt*	activef;	/* active endpoint list */
+};
+
+/* device parameters */
+enum
+{
+	/* Udev.state */
+	Disabled = 0,
+	Attached,
+	Enabled,
+	Assigned,
+	Configured,
+
+	/* Udev.class */
+	Noclass = 0,
+	Hubclass = 9,
+};
+
+/*
+ * active USB device
+ */
+struct Udev
+{
+	Ref;
+	Lock;
+	Usbhost	*uh;
+	int		x;		/* index in usbdev[] */
+	int		busy;
+	int		state;
+	int		id;
+	uchar	port;		/* port number on connecting hub */
+	ulong	csp;
+	ushort	vid;		/* vendor id */
+	ushort	did;		/* product id */
+	int		ls;
+	int		npt;
+	Endpt*	ep[16];	/* active end points */
+	Udev*	ports;	/* active ports, if hub */
+	Udev*	next;		/* next device on this hub */
+};
+
+/*
+ * One of these per active Host Controller Interface (HCI)
+ */
+struct Usbhost
+{
+	ISAConf;					/* hardware info */
+	int	tbdf;					/* type+busno+devno+funcno */
+
+	QLock;					/* protects namespace state */
+	int		idgen;			/* version number to distinguish new connections */
+	Udev*	dev[MaxUsbDev];	/* device endpoints managed by this HCI */
+
+	void	(*init)(Usbhost*);
+	void	(*interrupt)(Ureg*, void*);
+
+	void	(*portinfo)(Usbhost*, char*, char*);
+	void	(*portreset)(Usbhost*, int);
+	void	(*portenable)(Usbhost*, int, int);
+
+	void	(*epalloc)(Usbhost*, Endpt*);
+	void	(*epfree)(Usbhost*, Endpt*);
+	void	(*epopen)(Usbhost*, Endpt*);
+	void	(*epclose)(Usbhost*, Endpt*);
+	void	(*epmode)(Usbhost*, Endpt*);
+
+	long	(*read)(Usbhost*, Endpt*, void*, long, vlong);
+	long	(*write)(Usbhost*, Endpt*, void*, long, vlong, int);
+
+	void	*ctlr;
+};
+
+extern void addusbtype(char*, int(*)(Usbhost*));
--- /dev/null
+++ b/os/pc/usbuhci.c
@@ -1,0 +1,1538 @@
+#include	"u.h"
+#include	"../port/lib.h"
+#include	"mem.h"
+#include	"dat.h"
+#include	"fns.h"
+#include	"io.h"
+#include	"../port/error.h"
+
+#include	"usb.h"
+
+#define XPRINT if(debug)print
+
+static int Chatty = 0;
+static int debug = 0;
+
+static	char	Estalled[] = "usb endpoint stalled";
+
+/*
+ * UHCI interface registers and bits
+ */
+enum
+{
+	/* i/o space */
+	Cmd = 0,
+	Status = 2,
+	Usbintr = 4,
+	Frnum = 6,
+	Flbaseadd = 8,
+	SOFMod = 0xC,
+	Portsc0 = 0x10,
+	Portsc1 = 0x12,
+
+	/* port status */
+	Suspend =		1<<12,
+	PortReset =		1<<9,
+	SlowDevice =		1<<8,
+	ResumeDetect =	1<<6,
+	PortChange =		1<<3,	/* write 1 to clear */
+	PortEnable =		1<<2,
+	StatusChange =	1<<1,	/* write 1 to clear */
+	DevicePresent =	1<<0,
+
+	NFRAME = 	1024,
+	FRAMESIZE=	NFRAME*sizeof(ulong),	/* fixed by hardware; aligned to same */
+
+	Vf =			1<<2,	/* TD only */
+	IsQH =		1<<1,
+	Terminate =	1<<0,
+
+	/* TD.status */
+	SPD =		1<<29,
+	ErrLimit0 =	0<<27,
+	ErrLimit1 =	1<<27,
+	ErrLimit2 =	2<<27,
+	ErrLimit3 =	3<<27,
+	LowSpeed =	1<<26,
+	IsoSelect =	1<<25,
+	IOC =		1<<24,
+	Active =		1<<23,
+	Stalled =		1<<22,
+	DataBufferErr =	1<<21,
+	Babbling =	1<<20,
+	NAKed =		1<<19,
+	CRCorTimeout = 1<<18,
+	BitstuffErr =	1<<17,
+	AnyError = (Stalled | DataBufferErr | Babbling | NAKed | CRCorTimeout | BitstuffErr),
+
+	/* TD.dev */
+	IsDATA1 =	1<<19,
+
+	/* TD.flags (software) */
+	CancelTD=	1<<0,
+	IsoClean=		1<<2,
+};
+
+static struct
+{
+	int	bit;
+	char	*name;
+}
+portstatus[] =
+{
+	{ Suspend,		"suspend", },
+	{ PortReset,		"reset", },
+	{ SlowDevice,		"lowspeed", },
+	{ ResumeDetect,	"resume", },
+	{ PortChange,		"portchange", },
+	{ PortEnable,		"enable", },
+	{ StatusChange,	"statuschange", },
+	{ DevicePresent,	"present", },
+};
+
+typedef struct Ctlr Ctlr;
+typedef struct Endptx Endptx;
+typedef struct QH QH;
+typedef struct TD TD;
+
+/*
+ * software structures
+ */
+struct Ctlr
+{
+	Lock;	/* protects state shared with interrupt (eg, free list) */
+	Ctlr*	next;
+	Pcidev*	pcidev;
+	int	active;
+
+	int	io;
+	ulong*	frames;	/* frame list */
+	ulong*	frameld;	/* real time load on each of the frame list entries */
+	QLock	resetl;	/* lock controller during USB reset */
+
+	TD*	tdpool;
+	TD*	freetd;
+	QH*	qhpool;
+	QH*	freeqh;
+
+	QH*	ctlq;	/* queue for control i/o */
+	QH*	bwsop;	/* empty bandwidth sop (to PIIX4 errata specifications) */
+	QH*	bulkq;	/* queue for bulk i/o (points back to bandwidth sop) */
+	QH*	recvq;	/* receive queues for bulk i/o */
+
+	Udev*	ports[2];
+
+	struct {
+		Lock;
+		Endpt*	f;
+	} activends;
+
+	long		usbints;	/* debugging */
+	long		framenumber;
+	long		frameptr;
+	long		usbbogus;
+};
+
+#define	IN(x)	ins(ctlr->io+(x))
+#define	OUT(x, v)	outs(ctlr->io+(x), (v))
+
+static Ctlr* ctlrhead;
+static Ctlr* ctlrtail;
+
+struct Endptx
+{
+	QH*		epq;	/* queue of TDs for this endpoint */
+
+	/* ISO related: */
+	void*	tdalloc;
+	void*	bpalloc;
+	uchar*	bp0;		/* first block in array */
+	TD	*	td0;		/* first td in array */
+	TD	*	etd;		/* pointer into circular list of TDs for isochronous ept */
+	TD	*	xtd;		/* next td to be cleaned */
+};
+
+/*
+ * UHCI hardware structures, aligned on 16-byte boundary
+ */
+struct TD
+{
+	ulong	link;
+	ulong	status;	/* controller r/w */
+	ulong	dev;
+	ulong	buffer;
+
+	/* software */
+	ulong	flags;
+	union{
+		Block*	bp;		/* non-iso */
+		ulong	offset;	/* iso */
+	};
+	Endpt*	ep;
+	TD*		next;
+};
+#define	TFOL(p)	((TD*)KADDR((ulong)(p) & ~(0xF|PCIWINDOW)))
+
+struct QH
+{
+	ulong	head;
+	ulong	entries;	/* address of next TD or QH to process (updated by controller) */
+
+	/* software */
+	QH*		hlink;
+	TD*		first;
+	QH*		next;		/* free list */
+	TD*		last;
+	ulong	_d1;		/* fillers */
+	ulong	_d2;
+};
+#define	QFOL(p)	((QH*)KADDR((ulong)(p) & ~(0xF|PCIWINDOW)))
+
+static TD *
+alloctd(Ctlr *ctlr)
+{
+	TD *t;
+
+	ilock(ctlr);
+	t = ctlr->freetd;
+	if(t == nil)
+		panic("alloctd");	/* TO DO */
+	ctlr->freetd = t->next;
+	t->next = nil;
+	iunlock(ctlr);
+	t->ep = nil;
+	t->bp = nil;
+	t->status = 0;
+	t->link = Terminate;
+	t->buffer = 0;
+	t->flags = 0;
+	return t;
+}
+
+static void
+freetd(Ctlr *ctlr, TD *t)
+{
+	t->ep = nil;
+	if(t->bp)
+		freeb(t->bp);
+	t->bp = nil;
+	ilock(ctlr);
+	t->buffer = 0xdeadbeef;
+	t->next = ctlr->freetd;
+	ctlr->freetd = t;
+	iunlock(ctlr);
+}
+
+static void
+dumpdata(Block *b, int n)
+{
+	int i;
+
+	XPRINT("\tb %8.8lux[%d]: ", (ulong)b->rp, n);
+	if(n > 16)
+		n = 16;
+	for(i=0; i<n; i++)
+		XPRINT(" %2.2ux", b->rp[i]);
+	XPRINT("\n");
+}
+
+static void
+dumptd(TD *t, int follow)
+{
+	int i, n;
+	char buf[20], *s;
+	TD *t0;
+
+	t0 = t;
+	while(t){
+		i = t->dev & 0xFF;
+		if(i == TokOUT || i == TokSETUP)
+			n = ((t->dev>>21) + 1) & 0x7FF;
+		else if((t->status & Active) == 0)
+			n = (t->status + 1) & 0x7FF;
+		else
+			n = 0;
+		s = buf;
+		if(t->status & Active)
+			*s++ = 'A';
+		if(t->status & Stalled)
+			*s++ = 'S';
+		if(t->status & DataBufferErr)
+			*s++ = 'D';
+		if(t->status & Babbling)
+			*s++ = 'B';
+		if(t->status & NAKed)
+			*s++ = 'N';
+		if(t->status & CRCorTimeout)
+			*s++ = 'T';
+		if(t->status & BitstuffErr)
+			*s++ = 'b';
+		if(t->status & LowSpeed)
+			*s++ = 'L';
+		*s = 0;
+		XPRINT("td %8.8lux: ", t);
+		XPRINT("l=%8.8lux s=%8.8lux d=%8.8lux b=%8.8lux %8.8lux f=%8.8lux\n",
+			t->link, t->status, t->dev, t->buffer, t->bp?(ulong)t->bp->rp:0, t->flags);
+		XPRINT("\ts=%s,ep=%ld,d=%ld,D=%ld\n",
+			buf, (t->dev>>15)&0xF, (t->dev>>8)&0xFF, (t->dev>>19)&1);
+		if(debug && t->bp && (t->flags & CancelTD) == 0)
+			dumpdata(t->bp, n);
+		if(!follow || t->link & Terminate || t->link & IsQH)
+			break;
+		t = TFOL(t->link);
+		if(t == t0)
+			break;	/* looped */
+	}
+}
+
+static TD *
+alloctde(Ctlr *ctlr, Endpt *e, int pid, int n)
+{
+	TD *t;
+	int tog, id;
+
+	t = alloctd(ctlr);
+	id = (e->x<<7)|(e->dev->x&0x7F);
+	tog = 0;
+	if(e->data01 && pid != TokSETUP)
+		tog = IsDATA1;
+	t->ep = e;
+	t->status = ErrLimit3 | Active | IOC;	/* or put IOC only on last? */
+	if(e->dev->ls)
+		t->status |= LowSpeed;
+	t->dev = ((n-1)<<21) | ((id&0x7FF)<<8) | pid | tog;
+	return t;
+}
+
+static QH *
+allocqh(Ctlr *ctlr)
+{
+	QH *qh;
+
+	ilock(ctlr);
+	qh = ctlr->freeqh;
+	if(qh == nil)
+		panic("allocqh");	/* TO DO */
+	ctlr->freeqh = qh->next;
+	qh->next = nil;
+	iunlock(ctlr);
+	qh->head = Terminate;
+	qh->entries = Terminate;
+	qh->hlink = nil;
+	qh->first = nil;
+	qh->last = nil;
+	return qh;
+}
+
+static void
+freeqh(Ctlr *ctlr, QH *qh)
+{
+	ilock(ctlr);
+	qh->next = ctlr->freeqh;
+	ctlr->freeqh = qh;
+	iunlock(ctlr);
+}
+
+static void
+dumpqh(QH *q)
+{
+	int i;
+	QH *q0;
+
+	q0 = q;
+	for(i = 0; q != nil && i < 10; i++){
+		XPRINT("qh %8.8lux: %8.8lux %8.8lux\n", q, q->head, q->entries);
+		if((q->entries & Terminate) == 0)
+			dumptd(TFOL(q->entries), 1);
+		if(q->head & Terminate)
+			break;
+		if((q->head & IsQH) == 0){
+			XPRINT("head:");
+			dumptd(TFOL(q->head), 1);
+			break;
+		}
+		q = QFOL(q->head);
+		if(q == q0)
+			break;	/* looped */
+	}
+}
+
+static void
+queuetd(Ctlr *ctlr, QH *q, TD *t, int vf, char *why)
+{
+	TD *lt;
+
+	for(lt = t; lt->next != nil; lt = lt->next)
+		lt->link = PCIWADDR(lt->next) | vf;
+	lt->link = Terminate;
+	ilock(ctlr);
+	XPRINT("queuetd %s: t=%p lt=%p q=%p first=%p last=%p entries=%.8lux\n",
+		why, t, lt, q, q->first, q->last, q->entries);
+	if(q->first != nil){
+		q->last->link = PCIWADDR(t) | vf;
+		q->last->next = t;
+	}else{
+		q->first = t;
+		q->entries = PCIWADDR(t);
+	}
+	q->last = lt;
+	XPRINT("	t=%p q=%p first=%p last=%p entries=%.8lux\n",
+		t, q, q->first, q->last, q->entries);
+	dumpqh(q);
+	iunlock(ctlr);
+}
+
+static void
+cleantd(Ctlr *ctlr, TD *t, int discard)
+{
+	Block *b;
+	int n, err;
+
+	XPRINT("cleanTD: %8.8lux %8.8lux %8.8lux %8.8lux\n", t->link, t->status, t->dev, t->buffer);
+	if(t->ep != nil && t->ep->debug)
+		dumptd(t, 0);
+	if(t->status & Active)
+		panic("cleantd Active");
+	err = t->status & (AnyError&~NAKed);
+	/* TO DO: on t->status&AnyError, q->entries will not have advanced */
+	if (err) {
+		XPRINT("cleanTD: Error %8.8lux %8.8lux %8.8lux %8.8lux\n", t->link, t->status, t->dev, t->buffer);
+//		print("cleanTD: Error %8.8lux %8.8lux %8.8lux %8.8lux\n", t->link, t->status, t->dev, t->buffer);
+	}
+	switch(t->dev&0xFF){
+	case TokIN:
+		if(discard || (t->flags & CancelTD) || t->ep == nil || t->ep->x!=0&&err){
+			if(t->ep != nil){
+				if(err != 0)
+					t->ep->err = err==Stalled? Estalled: Eio;
+				wakeup(&t->ep->rr);	/* in case anyone cares */
+			}
+			break;
+		}
+		b = t->bp;
+		n = (t->status + 1) & 0x7FF;
+		if(n > b->lim - b->wp)
+			n = 0;
+		b->wp += n;
+		if(Chatty)
+			dumpdata(b, n);
+		t->bp = nil;
+		t->ep->nbytes += n;
+		t->ep->nblocks++;
+		qpass(t->ep->rq, b);	/* TO DO: flow control */
+		wakeup(&t->ep->rr);	/* TO DO */
+		break;
+	case TokSETUP:
+		XPRINT("cleanTD: TokSETUP %lux\n", &t->ep);
+		/* don't really need to wakeup: subsequent IN or OUT gives status */
+		if(t->ep != nil) {
+			wakeup(&t->ep->wr);	/* TO DO */
+			XPRINT("cleanTD: wakeup %lux\n", &t->ep->wr);
+		}
+		break;
+	case TokOUT:
+		/* TO DO: mark it done somewhere */
+		XPRINT("cleanTD: TokOut %lux\n", &t->ep);
+		if(t->ep != nil){
+			if(t->bp){
+				n = BLEN(t->bp);
+				t->ep->nbytes += n;
+				t->ep->nblocks++;
+			}
+			if(t->ep->x!=0 && err != 0)
+				t->ep->err = err==Stalled? Estalled: Eio;
+			if(--t->ep->ntd < 0)
+				panic("cleantd ntd");
+			wakeup(&t->ep->wr);	/* TO DO */
+			XPRINT("cleanTD: wakeup %lux\n", &t->ep->wr);
+		}
+		break;
+	}
+	freetd(ctlr, t);
+}
+
+static void
+cleanq(Ctlr *ctlr, QH *q, int discard, int vf)
+{
+	TD *t, *tp;
+
+	ilock(ctlr);
+	tp = nil;
+	for(t = q->first; t != nil;){
+		XPRINT("cleanq: %8.8lux %8.8lux %8.8lux %8.8lux %8.8lux %8.8lux\n", t->link, t->status, t->dev, t->buffer, t->flags, t->next);
+		if(t->status & Active){
+			if(t->status & NAKed){
+				t->status = (t->status & ~NAKed) | IOC;	/* ensure interrupt next frame */
+				tp = t;
+				t = t->next;
+				continue;
+			}
+			if(t->flags & CancelTD){
+				XPRINT("cancelTD: %8.8lux\n", (ulong)t);
+				t->status = (t->status & ~Active) | IOC;	/* ensure interrupt next frame */
+				tp = t;
+				t = t->next;
+				continue;
+			}
+			tp = t;
+			t = t->next;
+			continue;
+		}
+		t->status &= ~IOC;
+		if (tp == nil) {
+			q->first = t->next;
+			if(q->first != nil)
+				q->entries = PCIWADDR(q->first);
+			else
+				q->entries = Terminate;
+		} else {
+			tp->next = t->next;
+			if (t->next != nil)
+				tp->link = PCIWADDR(t->next) | vf;
+			else
+				tp->link = Terminate;
+		}
+		if (q->last == t)
+			q->last = tp;
+		iunlock(ctlr);
+		cleantd(ctlr, t, discard);
+		ilock(ctlr);
+		if (tp)
+			t = tp->next;
+		else
+			t = q->first;
+		XPRINT("t = %8.8lux\n", t);
+		dumpqh(q);
+	}
+	if(q->first && q->entries != PCIWADDR(q->first)){
+		ctlr->usbbogus++;
+		q->entries = PCIWADDR(q->first);
+	}
+	iunlock(ctlr);
+}
+
+static void
+canceltds(Ctlr *ctlr, QH *q, Endpt *e)
+{
+	TD *t;
+
+	if(q != nil){
+		ilock(ctlr);
+		for(t = q->first; t != nil; t = t->next)
+			if(t->ep == e)
+				t->flags |= CancelTD;
+		iunlock(ctlr);
+		XPRINT("cancel:\n");
+		dumpqh(q);
+	}
+}
+
+static void
+eptcancel(Ctlr *ctlr, Endpt *e)
+{
+	Endptx *x;
+
+	if(e == nil)
+		return;
+	x = e->private;
+	canceltds(ctlr, x->epq, e);
+	canceltds(ctlr, ctlr->ctlq, e);
+	canceltds(ctlr, ctlr->bulkq, e);
+}
+
+static void
+eptactivate(Ctlr *ctlr, Endpt *e)
+{
+	ilock(&ctlr->activends);
+	if(e->active == 0){
+		XPRINT("activate 0x%p\n", e);
+		e->active = 1;
+		e->activef = ctlr->activends.f;
+		ctlr->activends.f = e;
+	}
+	iunlock(&ctlr->activends);
+}
+
+static void
+eptdeactivate(Ctlr *ctlr, Endpt *e)
+{
+	Endpt **l;
+
+	/* could be O(1) but not worth it yet */
+	ilock(&ctlr->activends);
+	if(e->active){
+		e->active = 0;
+		XPRINT("deactivate 0x%p\n", e);
+		for(l = &ctlr->activends.f; *l != e; l = &(*l)->activef)
+			if(*l == nil){
+				iunlock(&ctlr->activends);
+				panic("usb eptdeactivate");
+			}
+		*l = e->activef;
+	}
+	iunlock(&ctlr->activends);
+}
+
+static void
+queueqh(Ctlr *ctlr, QH *qh)
+{
+	QH *q;
+
+	// See if it's already queued
+	for (q = ctlr->recvq->next; q; q = q->hlink)
+		if (q == qh)
+			return;
+	if ((qh->hlink = ctlr->recvq->next) == nil)
+		qh->head = Terminate;
+	else
+		qh->head = PCIWADDR(ctlr->recvq->next) | IsQH;
+	ctlr->recvq->next = qh;
+	ctlr->recvq->entries = PCIWADDR(qh) | IsQH;
+}
+
+static QH*
+qxmit(Ctlr *ctlr, Endpt *e, Block *b, int pid)
+{
+	TD *t;
+	int n, vf;
+	QH *qh;
+	Endptx *x;
+
+	x = e->private;
+	if(b != nil){
+		n = BLEN(b);
+		t = alloctde(ctlr, e, pid, n);
+		t->bp = b;
+		t->buffer = PCIWADDR(b->rp);
+	}else
+		t = alloctde(ctlr, e, pid, 0);
+	ilock(ctlr);
+	e->ntd++;
+	iunlock(ctlr);
+	if(e->debug) pprint("QTD: %8.8lux n=%ld\n", t, b?BLEN(b): 0);
+	vf = 0;
+	if(e->x == 0){
+		qh = ctlr->ctlq;
+		vf = 0;
+	}else if((qh = x->epq) == nil || e->mode != OWRITE){
+		qh = ctlr->bulkq;
+		vf = Vf;
+	}
+	queuetd(ctlr, qh, t, vf, "qxmit");
+	return qh;
+}
+
+static QH*
+qrcv(Ctlr *ctlr, Endpt *e)
+{
+	TD *t;
+	Block *b;
+	QH *qh;
+	int vf;
+	Endptx *x;
+
+	x = e->private;
+	t = alloctde(ctlr, e, TokIN, e->maxpkt);
+	b = allocb(e->maxpkt);
+	t->bp = b;
+	t->buffer = PCIWADDR(b->wp);
+	vf = 0;
+	if(e->x == 0){
+		qh = ctlr->ctlq;
+	}else if((qh = x->epq) == nil || e->mode != OREAD){
+		qh = ctlr->bulkq;
+		vf = Vf;
+	}
+	queuetd(ctlr, qh, t, vf, "qrcv");
+	return qh;
+}
+
+static int
+usbsched(Ctlr *ctlr, int pollms, ulong load)
+{
+	int i, d, q;
+	ulong best, worst;
+
+	best = 1000000;
+	q = -1;
+	for (d = 0; d < pollms; d++){
+		worst = 0;
+		for (i = d; i < NFRAME; i++){
+			if (ctlr->frameld[i] + load > worst)
+				worst = ctlr->frameld[i] + load;
+		}
+		if (worst < best){
+			best = worst;
+			q = d;
+		}
+	}
+	return q;
+}
+
+static int
+schedendpt(Ctlr *ctlr, Endpt *e)
+{
+	TD *td;
+	Endptx *x;
+	uchar *bp;
+	int i, id, ix, size, frnum;
+
+	if(!e->iso || e->sched >= 0)
+		return 0;
+
+	if (e->active){
+		return -1;
+	}
+	e->off = 0;
+	e->sched = usbsched(ctlr, e->pollms, e->maxpkt);
+	if(e->sched < 0)
+		return -1;
+
+	x = e->private;
+	if (x->tdalloc || x->bpalloc)
+		panic("usb: tdalloc/bpalloc");
+	x->tdalloc = mallocz(0x10 + NFRAME*sizeof(TD), 1);
+	x->bpalloc = mallocz(0x10 + e->maxpkt*NFRAME/e->pollms, 1);
+	x->td0 = (TD*)(((ulong)x->tdalloc + 0xf) & ~0xf);
+	x->bp0 = (uchar *)(((ulong)x->bpalloc + 0xf) & ~0xf);
+	frnum = (IN(Frnum) + 1) & 0x3ff;
+	frnum = (frnum & ~(e->pollms - 1)) + e->sched;
+	x->xtd = &x->td0[(frnum+8)&0x3ff];	/* Next td to finish */
+	x->etd = nil;
+	e->remain = 0;
+	e->nbytes = 0;
+	td = x->td0;
+	for(i = e->sched; i < NFRAME; i += e->pollms){
+		bp = x->bp0 + e->maxpkt*i/e->pollms;
+		td->buffer = PCIWADDR(bp);
+		td->ep = e;
+		td->next = &td[1];
+		ctlr->frameld[i] += e->maxpkt;
+		td++;
+	}
+	td[-1].next = x->td0;
+	for(i = e->sched; i < NFRAME; i += e->pollms){
+		ix = (frnum+i) & 0x3ff;
+		td = &x->td0[ix];
+
+		id = (e->x<<7)|(e->dev->x&0x7F);
+		if (e->mode == OREAD)
+			/* enable receive on this entry */
+			td->dev = ((e->maxpkt-1)<<21) | ((id&0x7FF)<<8) | TokIN;
+		else{
+			size = (e->hz + e->remain)*e->pollms/1000;
+			e->remain = (e->hz + e->remain)*e->pollms%1000;
+			size *= e->samplesz;
+			td->dev = ((size-1)<<21) | ((id&0x7FF)<<8) | TokOUT;
+		}
+		td->status = ErrLimit1 | Active | IsoSelect | IOC;
+		td->link = ctlr->frames[ix];
+		td->flags |= IsoClean;
+		ctlr->frames[ix] = PCIWADDR(td);
+	}
+	return 0;
+}
+
+static void
+unschedendpt(Ctlr *ctlr, Endpt *e)
+{
+	int q;
+	TD *td;
+	Endptx *x;
+	ulong *addr;
+
+	if(!e->iso || e->sched < 0)
+		return;
+
+	x = e->private;
+	if (x->tdalloc == nil)
+		panic("tdalloc");
+	for (q = e->sched; q < NFRAME; q += e->pollms){
+		td = x->td0++;
+		addr = &ctlr->frames[q];
+		while(*addr != PADDR(td)) {
+			if(*addr & IsQH)
+				panic("usb: TD expected");
+			addr = &TFOL(*addr)->link;
+		}
+		*addr = td->link;
+		ctlr->frameld[q] -= e->maxpkt;
+	}
+	free(x->tdalloc);
+	free(x->bpalloc);
+	x->tdalloc = nil;
+	x->bpalloc = nil;
+	x->etd = nil;
+	x->td0 = nil;
+	e->sched = -1;
+}
+
+static void
+epalloc(Usbhost *uh, Endpt *e)
+{
+	Endptx *x;
+
+	x = malloc(sizeof(Endptx));
+	e->private = x;
+	x->epq = allocqh(uh->ctlr);
+	if(x->epq == nil)
+		panic("devendptx");
+}
+
+static void
+epfree(Usbhost *uh, Endpt *e)
+{
+	Ctlr *ctlr;
+	Endptx *x;
+
+	ctlr = uh->ctlr;
+	x = e->private;
+	if(x->epq != nil)
+		freeqh(ctlr, x->epq);
+}
+
+static void
+epopen(Usbhost *uh, Endpt *e)
+{
+	Ctlr *ctlr;
+
+	ctlr = uh->ctlr;
+	if(e->iso && e->active)
+		error("already open");
+	if(schedendpt(ctlr, e) < 0){
+		if(e->active)
+			error("cannot schedule USB endpoint, active");
+		else
+			error("cannot schedule USB endpoint");
+	}
+	eptactivate(ctlr, e);
+}
+
+static void
+epclose(Usbhost *uh, Endpt *e)
+{
+	Ctlr *ctlr;
+
+	ctlr = uh->ctlr;
+	eptdeactivate(ctlr, e);
+	unschedendpt(ctlr, e);
+}
+
+static void
+epmode(Usbhost *uh, Endpt *e)
+{
+	Ctlr *ctlr;
+	Endptx *x;
+
+	ctlr = uh->ctlr;
+	x = e->private;
+	if(e->iso) {
+		if(x->epq != nil) {
+			freeqh(ctlr, x->epq);
+			x->epq = nil;
+		}
+	}
+	else {
+		/* Each bulk device gets a queue head hanging off the
+		 * bulk queue head
+		 */
+		if(x->epq == nil) {
+			x->epq = allocqh(ctlr);
+			if(x->epq == nil)
+				panic("epbulk: allocqh");
+		}
+		queueqh(ctlr, x->epq);
+	}
+}
+
+static	int	ioport[] = {-1, Portsc0, Portsc1};
+
+static void
+portreset(Usbhost *uh, int port)
+{
+	int i, p;
+	Ctlr *ctlr;
+
+	ctlr = uh->ctlr;
+	if(port != 1 && port != 2)
+		error(Ebadarg);
+
+	/* should check that device not being configured on other port? */
+	p = ioport[port];
+	qlock(&ctlr->resetl);
+	if(waserror()){
+		qunlock(&ctlr->resetl);
+		nexterror();
+	}
+	XPRINT("r: %x\n", IN(p));
+	ilock(ctlr);
+	OUT(p, PortReset);
+	delay(12);	/* BUG */
+	XPRINT("r2: %x\n", IN(p));
+	OUT(p, IN(p) & ~PortReset);
+	XPRINT("r3: %x\n", IN(p));
+	OUT(p, IN(p) | PortEnable);
+	microdelay(64);
+	for(i=0; i<1000 && (IN(p) & PortEnable) == 0; i++)
+		;
+	XPRINT("r': %x %d\n", IN(p), i);
+	OUT(p, (IN(p) & ~PortReset)|PortEnable);
+	iunlock(ctlr);
+	poperror();
+	qunlock(&ctlr->resetl);
+}
+
+static void
+portenable(Usbhost *uh, int port, int on)
+{
+	int w, p;
+	Ctlr *ctlr;
+
+	ctlr = uh->ctlr;
+	if(port != 1 && port != 2)
+		error(Ebadarg);
+
+	/* should check that device not being configured on other port? */
+	p = ioport[port];
+	qlock(&ctlr->resetl);
+	if(waserror()){
+		qunlock(&ctlr->resetl);
+		nexterror();
+	}
+	ilock(ctlr);
+	w = IN(p);
+	if(on)
+		w |= PortEnable;
+	else
+		w &= ~PortEnable;
+	OUT(p, w);
+	microdelay(64);
+	iunlock(ctlr);
+	XPRINT("e: %x\n", IN(p));
+	poperror();
+	qunlock(&ctlr->resetl);
+}
+
+static void
+portinfo(Usbhost *uh, char *s, char *se)
+{
+	int x, i, j;
+	Ctlr *ctlr;
+
+	ctlr = uh->ctlr;
+	for(i = 1; i <= 2; i++) {
+		ilock(ctlr);
+		x = IN(ioport[i]);
+		if((x & (PortChange|StatusChange)) != 0)
+			OUT(ioport[i], x);
+		iunlock(ctlr);
+		s = seprint(s, se, "%d %ux", i, x);
+		for(j = 0; j < nelem(portstatus); j++) {
+			if((x & portstatus[j].bit) != 0)
+				s = seprint(s, se, " %s", portstatus[j].name);
+		}
+		s = seprint(s, se, "\n");
+	}
+}
+
+static void
+cleaniso(Endpt *e, int frnum)
+{
+	TD *td;
+	int id, n, i;
+	Endptx *x;
+	uchar *bp;
+
+	x = e->private;
+	td = x->xtd;
+	if (td->status & Active)
+		return;
+	id = (e->x<<7)|(e->dev->x&0x7F);
+	do {
+		if (td->status & AnyError)
+			XPRINT("usbisoerror 0x%lux\n", td->status);
+		n = (td->status + 1) & 0x3ff;
+		e->nbytes += n;
+		if ((td->flags & IsoClean) == 0)
+			e->nblocks++;
+		if (e->mode == OREAD){
+			e->buffered += n;
+			e->poffset += (td->status + 1) & 0x3ff;
+			td->offset = e->poffset;
+			td->dev = ((e->maxpkt -1)<<21) | ((id&0x7FF)<<8) | TokIN;
+			e->toffset = td->offset;
+		}else{
+			if ((td->flags & IsoClean) == 0){
+				e->buffered -= n;
+				if (e->buffered < 0){
+//					print("e->buffered %d?\n", e->buffered);
+					e->buffered = 0;
+				}
+			}
+			e->toffset = td->offset;
+			n = (e->hz + e->remain)*e->pollms/1000;
+			e->remain = (e->hz + e->remain)*e->pollms%1000;
+			n *= e->samplesz;
+			td->dev = ((n -1)<<21) | ((id&0x7FF)<<8) | TokOUT;
+			td->offset = e->poffset;
+			e->poffset += n;
+		}
+		td = td->next;
+		if (x->xtd == td){
+			XPRINT("@");
+			break;
+		}
+	} while ((td->status & Active) == 0);
+	e->time = todget(nil);
+	x->xtd = td;
+	for (n = 2; n < 4; n++){
+		i = ((frnum + n)&0x3ff);
+		td = x->td0 + i;
+		bp = x->bp0 + e->maxpkt*i/e->pollms;
+		if (td->status & Active)
+			continue;
+
+		if (e->mode == OWRITE){
+			if (td == x->etd) {
+				XPRINT("*");
+				memset(bp+e->off, 0, e->maxpkt-e->off);
+				if (e->off == 0)
+					td->flags |= IsoClean;
+				else
+					e->buffered += (((td->dev>>21) +1) & 0x3ff) - e->off;
+				x->etd = nil;
+			}else if ((td->flags & IsoClean) == 0){
+				XPRINT("-");
+				memset(bp, 0, e->maxpkt);
+				td->flags |= IsoClean;
+			}
+		} else {
+			/* Unread bytes are now lost */
+			e->buffered -= (td->status + 1) & 0x3ff;
+		}
+		td->status = ErrLimit1 | Active | IsoSelect | IOC;
+	}
+	wakeup(&e->wr);
+}
+
+static void
+interrupt(Ureg*, void *a)
+{
+	QH *q;
+	Ctlr *ctlr;
+	Endpt *e;
+	Endptx *x;
+	int s, frnum;
+	Usbhost *uh;
+
+	uh = a;
+	ctlr = uh->ctlr;
+	s = IN(Status);
+	ctlr->frameptr = inl(ctlr->io+Flbaseadd);
+	ctlr->framenumber = IN(Frnum) & 0x3ff;
+	OUT(Status, s);
+	if ((s & 0x1f) == 0)
+		return;
+	ctlr->usbints++;
+	frnum = IN(Frnum) & 0x3ff;
+	if (s & 0x1a) {
+		XPRINT("cmd #%x sofmod #%x\n", IN(Cmd), inb(ctlr->io+SOFMod));
+		XPRINT("sc0 #%x sc1 #%x\n", IN(Portsc0), IN(Portsc1));
+	}
+
+	ilock(&ctlr->activends);
+	for(e = ctlr->activends.f; e != nil; e = e->activef) {
+		x = e->private;
+		if(!e->iso && x->epq != nil) {
+			XPRINT("cleanq(ctlr, x->epq, 0, 0)\n");
+			cleanq(ctlr, x->epq, 0, 0);
+		}
+		if(e->iso) {
+			XPRINT("cleaniso(e)\n");
+			cleaniso(e, frnum);
+		}
+	}
+	iunlock(&ctlr->activends);
+	XPRINT("cleanq(ctlr, ctlr->ctlq, 0, 0)\n");
+	cleanq(ctlr, ctlr->ctlq, 0, 0);
+	XPRINT("cleanq(ctlr, ctlr->bulkq, 0, Vf)\n");
+	cleanq(ctlr, ctlr->bulkq, 0, Vf);
+	XPRINT("clean recvq\n");
+	for (q = ctlr->recvq->next; q; q = q->hlink) {
+		XPRINT("cleanq(ctlr, q, 0, Vf)\n");
+		cleanq(ctlr, q, 0, Vf);
+	}
+}
+
+static int
+eptinput(void *arg)
+{
+	Endpt *e;
+
+	e = arg;
+	return e->eof || e->err || qcanread(e->rq);
+}
+
+static int
+isoreadyx(Endptx *x)
+{
+	return x->etd == nil || (x->etd != x->xtd && (x->etd->status & Active) == 0);
+}
+
+static int
+isoready(void *arg)
+{
+	int ret;
+	Ctlr *ctlr;
+	Endpt *e;
+	Endptx *x;
+
+	e = arg;
+	ctlr = e->dev->uh->ctlr;
+	x = e->private;
+	ilock(&ctlr->activends);
+	ret = isoreadyx(x);
+	iunlock(&ctlr->activends);
+	return ret;
+}
+
+static long
+isoio(Ctlr *ctlr, Endpt *e, void *a, long n, ulong offset, int w)
+{
+	TD *td;
+	Endptx *x;
+	int i, frnum;
+	uchar *p, *q, *bp;
+	volatile int isolock;
+
+	x = e->private;
+	qlock(&e->rlock);
+	isolock = 0;
+	if(waserror()){
+		if (isolock){
+			isolock = 0;
+			iunlock(&ctlr->activends);
+		}
+		qunlock(&e->rlock);
+		eptcancel(ctlr, e);
+		nexterror();
+	}
+	p = a;
+	if (offset != 0 && offset != e->foffset){
+		iprint("offset %lud, foffset %lud\n", offset, e->foffset);
+		/* Seek to a specific position */
+		frnum = (IN(Frnum) + 8) & 0x3ff;
+		td = x->td0 +frnum;
+		if (offset < td->offset)
+			error("ancient history");
+		while (offset > e->toffset){
+			tsleep(&e->wr, return0, 0, 500);
+		}
+		while (offset >= td->offset + ((w?(td->dev >> 21):td->status) + 1) & 0x7ff){
+			td = td->next;
+			if (td == x->xtd)
+				iprint("trouble\n");
+		}
+		ilock(&ctlr->activends);
+		isolock = 1;
+		e->off = td->offset - offset;
+		if (e->off >= e->maxpkt){
+			iprint("I can't program: %d\n", e->off);
+			e->off = 0;
+		}
+		x->etd = td;
+		e->foffset = offset;
+	}
+	do {
+		if (isolock == 0){
+			ilock(&ctlr->activends);
+			isolock = 1;
+		}
+		td = x->etd;
+		if (td == nil || e->off == 0){
+			if (td == nil){
+				XPRINT("0");
+				if (w){
+					frnum = (IN(Frnum) + 1) & 0x3ff;
+					td = x->td0 + frnum;
+					while(td->status & Active)
+						td = td->next;
+				}else{
+					frnum = (IN(Frnum) - 4) & 0x3ff;
+					td = x->td0 + frnum;
+					while(td->next != x->xtd)
+						td = td->next;
+				}
+				x->etd = td;
+				e->off = 0;
+			}else{
+				/* New td, make sure it's ready */
+				while (isoreadyx(x) == 0){
+					isolock = 0;
+					iunlock(&ctlr->activends);
+					sleep(&e->wr, isoready, e);
+					ilock(&ctlr->activends);
+					isolock = 1;
+				}
+				if (x->etd == nil){
+					XPRINT("!");
+					continue;
+				}
+			}
+			if (w)
+				e->psize = ((td->dev >> 21) + 1) & 0x7ff;
+			else
+				e->psize = (x->etd->status + 1) & 0x7ff;
+			if(e->psize > e->maxpkt)
+				panic("packet size > maximum");
+		}
+		if((i = n) >= e->psize)
+			i = e->psize;
+		if (w)
+			e->buffered += i;
+		else{
+			e->buffered -= i;
+			if (e->buffered < 0)
+				e->buffered = 0;
+		}
+		isolock = 0;
+		iunlock(&ctlr->activends);
+		td->flags &= ~IsoClean;
+		bp = x->bp0 + (td - x->td0) * e->maxpkt / e->pollms;
+		q = bp + e->off;
+		if (w){
+			memmove(q, p, i);
+		}else{
+			memmove(p, q, i);
+		}
+		p += i;
+		n -= i;
+		e->off += i;
+		e->psize -= i;
+		if (e->psize){
+			if (n != 0)
+				panic("usb iso: can't happen");
+			break;
+		}
+		if(w)
+			td->offset = offset + (p-(uchar*)a) - (((td->dev >> 21) + 1) & 0x7ff);
+		td->status = ErrLimit3 | Active | IsoSelect | IOC;
+		x->etd = td->next;
+		e->off = 0;
+	} while(n > 0);
+	n = p-(uchar*)a;
+	e->foffset += n;
+	poperror();
+	if (isolock)
+		iunlock(&ctlr->activends);
+	qunlock(&e->rlock);
+	return n;
+}
+
+static long
+read(Usbhost *uh, Endpt *e, void *a, long n, vlong offset)
+{
+	long l, i;
+	Block *b;
+	Ctlr *ctlr;
+	uchar *p;
+
+	ctlr = uh->ctlr;
+	if(e->iso)
+		return isoio(ctlr, e, a, n, (ulong)offset, 0);
+
+	XPRINT("qlock(%p)\n", &e->rlock);
+	qlock(&e->rlock);
+	XPRINT("got qlock(%p)\n", &e->rlock);
+	if(waserror()){
+		qunlock(&e->rlock);
+		eptcancel(ctlr, e);
+		nexterror();
+	}
+	p = a;
+	do {
+		if(e->eof) {
+			XPRINT("e->eof\n");
+			break;
+		}
+		if(e->err)
+			error(e->err);
+		qrcv(ctlr, e);
+		if(!e->iso)
+			e->data01 ^= 1;
+		sleep(&e->rr, eptinput, e);
+		if(e->err)
+			error(e->err);
+		b = qget(e->rq);	/* TO DO */
+		if(b == nil) {
+			XPRINT("b == nil\n");
+			break;
+		}
+		if(waserror()){
+			freeb(b);
+			nexterror();
+		}
+		l = BLEN(b);
+		if((i = l) > n)
+			i = n;
+		if(i > 0){
+			memmove(p, b->rp, i);
+			p += i;
+		}
+		poperror();
+		freeb(b);
+		n -= i;
+		if (l != e->maxpkt)
+			break;
+	} while (n > 0);
+	poperror();
+	qunlock(&e->rlock);
+	return p-(uchar*)a;
+}
+
+static int
+qisempty(void *arg)
+{
+	return ((QH*)arg)->entries & Terminate;
+}
+
+static long
+write(Usbhost *uh, Endpt *e, void *a, long n, vlong offset, int tok)
+{
+	int i, j;
+	QH *qh;
+	Block *b;
+	Ctlr *ctlr;
+	uchar *p;
+
+	ctlr = uh->ctlr;
+	if(e->iso)
+		return isoio(ctlr, e, a, n, (ulong)offset, 1);
+
+	p = a;
+	qlock(&e->wlock);
+	if(waserror()){
+		qunlock(&e->wlock);
+		eptcancel(ctlr, e);
+		nexterror();
+	}
+	do {
+		if(e->err)
+			error(e->err);
+		if((i = n) >= e->maxpkt)
+			i = e->maxpkt;
+		b = allocb(i);
+		if(waserror()){
+			freeb(b);
+			nexterror();
+		}
+		XPRINT("out [%d]", i);
+		for (j = 0; j < i; j++) XPRINT(" %.2x", p[j]);
+		XPRINT("\n");
+		memmove(b->wp, p, i);
+		b->wp += i;
+		p += i;
+		n -= i;
+		poperror();
+		qh = qxmit(ctlr, e, b, tok);
+		tok = TokOUT;
+		e->data01 ^= 1;
+		if(e->ntd >= e->nbuf) {
+XPRINT("qh %s: q=%p first=%p last=%p entries=%.8lux\n",
+ "writeusb sleep", qh, qh->first, qh->last, qh->entries);
+			XPRINT("write: sleep %lux\n", &e->wr);
+			sleep(&e->wr, qisempty, qh);
+			XPRINT("write: awake\n");
+		}
+	} while(n > 0);
+	poperror();
+	qunlock(&e->wlock);
+	return p-(uchar*)a;
+}
+
+static void
+init(Usbhost* uh)
+{
+	Ctlr *ctlr;
+
+	ctlr = uh->ctlr;
+	ilock(ctlr);
+	outl(ctlr->io+Flbaseadd, PCIWADDR(ctlr->frames));
+	OUT(Frnum, 0);
+	OUT(Usbintr, 0xF);	/* enable all interrupts */
+	XPRINT("cmd 0x%x sofmod 0x%x\n", IN(Cmd), inb(ctlr->io+SOFMod));
+	XPRINT("sc0 0x%x sc1 0x%x\n", IN(Portsc0), IN(Portsc1));
+	if((IN(Cmd)&1)==0)
+		OUT(Cmd, 1);	/* run */
+//	pprint("at: c=%x s=%x c0=%x\n", IN(Cmd), IN(Status), IN(Portsc0));
+	iunlock(ctlr);
+}
+
+static void
+scanpci(void)
+{
+	int io;
+	Ctlr *ctlr;
+	Pcidev *p;
+	static int already = 0;
+
+	if(already)
+		return;
+	already = 1;
+	p = nil;
+	while(p = pcimatch(p, 0, 0)) {
+		/*
+		 * Find UHCI controllers.  Class = 12 (serial controller),
+		 * Sub-class = 3 (USB) and Programming Interface = 0.
+		 */
+		if(p->ccrb != 0x0C || p->ccru != 0x03 || p->ccrp != 0x00)
+			continue;
+		io = p->mem[4].bar & ~0x0F;
+		if(io == 0) {
+			print("usbuhci: failed to map registers\n");
+			continue;
+		}
+		if(ioalloc(io, p->mem[4].size, 0, "usbuhci") < 0){
+			print("usbuhci: port %d in use\n", io);
+			continue;
+		}
+		if(p->intl == 0xFF || p->intl == 0) {
+			print("usbuhci: no irq assigned for port %d\n", io);
+			continue;
+		}
+
+		XPRINT("usbuhci: %x/%x port 0x%ux size 0x%x irq %d\n",
+			p->vid, p->did, io, p->mem[4].size, p->intl);
+
+		ctlr = malloc(sizeof(Ctlr));
+		ctlr->pcidev = p;
+		ctlr->io = io;
+		if(ctlrhead != nil)
+			ctlrtail->next = ctlr;
+		else
+			ctlrhead = ctlr;
+		ctlrtail = ctlr;
+	}
+}
+
+static int
+reset(Usbhost *uh)
+{
+	int i;
+	TD *t;
+	ulong io;
+	Ctlr *ctlr;
+	Pcidev *p;
+
+	scanpci();
+
+	/*
+	 * Any adapter matches if no uh->port is supplied,
+	 * otherwise the ports must match.
+	 */
+	for(ctlr = ctlrhead; ctlr != nil; ctlr = ctlr->next){
+		if(ctlr->active)
+			continue;
+		if(uh->port == 0 || uh->port == ctlr->io){
+			ctlr->active = 1;
+			break;
+		}
+	}
+	if(ctlr == nil)
+		return -1;
+
+	io = ctlr->io;
+	p = ctlr->pcidev;
+
+	uh->ctlr = ctlr;
+	uh->port = io;
+	uh->irq = p->intl;
+	uh->tbdf = p->tbdf;
+
+	XPRINT("usbcmd\t0x%.4x\nusbsts\t0x%.4x\nusbintr\t0x%.4x\nfrnum\t0x%.2x\n",
+		IN(Cmd), IN(Status), IN(Usbintr), inb(io+Frnum));
+	XPRINT("frbaseadd\t0x%.4x\nsofmod\t0x%x\nportsc1\t0x%.4x\nportsc2\t0x%.4x\n",
+		IN(Flbaseadd), inb(io+SOFMod), IN(Portsc0), IN(Portsc1));
+
+	OUT(Cmd, 0);					/* stop */
+	while((IN(Status) & (1<<5)) == 0)	/* wait for halt */
+		;
+	OUT(Status, 0xFF);				/* clear pending interrupts */
+	pcicfgw16(p, 0xc0, 0x2000);		/* legacy support register: turn off lunacy mode */
+
+	if(0){
+		i = inb(io+SOFMod);
+		OUT(Cmd, 4);	/* global reset */
+		delay(15);
+		OUT(Cmd, 0);	/* end reset */
+		delay(4);
+		outb(io+SOFMod, i);
+	}
+
+	ctlr->tdpool = xspanalloc(128*sizeof(TD), 16, 0);
+	for(i=128; --i>=0;){
+		ctlr->tdpool[i].next = ctlr->freetd;
+		ctlr->freetd = &ctlr->tdpool[i];
+	}
+	ctlr->qhpool = xspanalloc(64*sizeof(QH), 16, 0);
+	for(i=64; --i>=0;){
+		ctlr->qhpool[i].next = ctlr->freeqh;
+		ctlr->freeqh = &ctlr->qhpool[i];
+	}
+
+	/*
+	 * the last entries of the periodic (interrupt & isochronous) scheduling TD entries
+	 * points to the control queue and the bandwidth sop for bulk traffic.
+	 * this is looped following the instructions in PIIX4 errata 29773804.pdf:
+	 * a QH links to a looped but inactive TD as its sole entry,
+	 * with its head entry leading on to the bulk traffic, the last QH of which
+	 * links back to the empty QH.
+	 */
+	ctlr->ctlq = allocqh(ctlr);
+	ctlr->bwsop = allocqh(ctlr);
+	ctlr->bulkq = allocqh(ctlr);
+	ctlr->recvq = allocqh(ctlr);
+	t = alloctd(ctlr);	/* inactive TD, looped */
+	t->link = PCIWADDR(t);
+	ctlr->bwsop->entries = PCIWADDR(t);
+
+	ctlr->ctlq->head = PCIWADDR(ctlr->bulkq) | IsQH;
+	ctlr->bulkq->head = PCIWADDR(ctlr->recvq) | IsQH;
+	ctlr->recvq->head = PCIWADDR(ctlr->bwsop) | IsQH;
+	if (1)	/* don't use loop back */
+ 		ctlr->bwsop->head = Terminate;
+	else	/* set up loop back */
+		ctlr->bwsop->head = PCIWADDR(ctlr->bwsop) | IsQH;
+
+	ctlr->frames = xspanalloc(FRAMESIZE, FRAMESIZE, 0);
+	ctlr->frameld = xallocz(FRAMESIZE, 1);
+	for (i = 0; i < NFRAME; i++)
+		ctlr->frames[i] = PCIWADDR(ctlr->ctlq) | IsQH;
+
+	/*
+	 * Linkage to the generic USB driver.
+	 */
+	uh->init = init;
+	uh->interrupt = interrupt;
+
+	uh->portinfo = portinfo;
+	uh->portreset = portreset;
+	uh->portenable = portenable;
+
+	uh->epalloc = epalloc;
+	uh->epfree = epfree;
+	uh->epopen = epopen;
+	uh->epclose = epclose;
+	uh->epmode = epmode;
+
+	uh->read = read;
+	uh->write = write;
+
+	return 0;
+}
+
+void
+usbuhcilink(void)
+{
+	addusbtype("uhci", reset);
+}
--- /dev/null
+++ b/os/pc/vga.c
@@ -1,0 +1,241 @@
+#include "u.h"
+#include "../port/lib.h"
+#include "mem.h"
+#include "dat.h"
+#include "fns.h"
+#include "../port/error.h"
+
+#define	Image	IMAGE
+#include <draw.h>
+#include <memdraw.h>
+#include <cursor.h>
+#include "screen.h"
+
+static Memimage* back;
+static Memimage *conscol;
+
+static Point curpos;
+static Rectangle window;
+static int *xp;
+static int xbuf[256];
+static Lock vgascreenlock;
+int drawdebug;
+
+void
+vgaimageinit(ulong chan)
+{
+	if(back == nil){
+		back = allocmemimage(Rect(0,0,1,1), chan);	/* RSC BUG */
+		if(back == nil)
+			panic("back alloc");		/* RSC BUG */
+		back->flags |= Frepl;
+		back->clipr = Rect(-0x3FFFFFF, -0x3FFFFFF, 0x3FFFFFF, 0x3FFFFFF);
+		memfillcolor(back, DBlack);
+	}
+
+	if(conscol == nil){
+		conscol = allocmemimage(Rect(0,0,1,1), chan);	/* RSC BUG */
+		if(conscol == nil)
+			panic("conscol alloc");	/* RSC BUG */
+		conscol->flags |= Frepl;
+		conscol->clipr = Rect(-0x3FFFFFF, -0x3FFFFFF, 0x3FFFFFF, 0x3FFFFFF);
+		memfillcolor(conscol, DWhite);
+	}
+}
+
+static void
+vgascroll(VGAscr* scr)
+{
+	int h, o;
+	Point p;
+	Rectangle r;
+
+	h = scr->memdefont->height;
+	o = 8*h;
+	r = Rpt(window.min, Pt(window.max.x, window.max.y-o));
+	p = Pt(window.min.x, window.min.y+o);
+	memimagedraw(scr->gscreen, r, scr->gscreen, p, nil, p, S);
+	r = Rpt(Pt(window.min.x, window.max.y-o), window.max);
+	memimagedraw(scr->gscreen, r, back, ZP, nil, ZP, S);
+
+	curpos.y -= o;
+}
+
+static void
+vgascreenputc(VGAscr* scr, char* buf, Rectangle *flushr)
+{
+	Point p;
+	int h, w, pos;
+	Rectangle r;
+
+//	drawdebug = 1;
+	if(xp < xbuf || xp >= &xbuf[sizeof(xbuf)])
+		xp = xbuf;
+
+	h = scr->memdefont->height;
+	switch(buf[0]){
+
+	case '\n':
+		if(curpos.y+h >= window.max.y){
+			vgascroll(scr);
+			*flushr = window;
+		}
+		curpos.y += h;
+		vgascreenputc(scr, "\r", flushr);
+		break;
+
+	case '\r':
+		xp = xbuf;
+		curpos.x = window.min.x;
+		break;
+
+	case '\t':
+		p = memsubfontwidth(scr->memdefont, " ");
+		w = p.x;
+		if(curpos.x >= window.max.x-4*w)
+			vgascreenputc(scr, "\n", flushr);
+
+		pos = (curpos.x-window.min.x)/w;
+		pos = 4-(pos%4);
+		*xp++ = curpos.x;
+		r = Rect(curpos.x, curpos.y, curpos.x+pos*w, curpos.y + h);
+		memimagedraw(scr->gscreen, r, back, back->r.min, nil, back->r.min, S);
+		curpos.x += pos*w;
+		break;
+
+	case '\b':
+		if(xp <= xbuf)
+			break;
+		xp--;
+		r = Rect(*xp, curpos.y, curpos.x, curpos.y+h);
+		memimagedraw(scr->gscreen, r, back, back->r.min, nil, ZP, S);
+		combinerect(flushr, r);
+		curpos.x = *xp;
+		break;
+
+	case '\0':
+		break;
+
+	default:
+		p = memsubfontwidth(scr->memdefont, buf);
+		w = p.x;
+
+		if(curpos.x >= window.max.x-w)
+			vgascreenputc(scr, "\n", flushr);
+
+		*xp++ = curpos.x;
+		r = Rect(curpos.x, curpos.y, curpos.x+w, curpos.y+h);
+		memimagedraw(scr->gscreen, r, back, back->r.min, nil, back->r.min, S);
+		memimagestring(scr->gscreen, curpos, conscol, ZP, scr->memdefont, buf);
+		combinerect(flushr, r);
+		curpos.x += w;
+	}
+//	drawdebug = 0;
+}
+
+static void
+vgascreenputs(char* s, int n)
+{
+	int i;
+	Rune r;
+	char buf[4];
+	VGAscr *scr;
+	Rectangle flushr;
+
+	scr = &vgascreen[0];
+
+	if(!islo()){
+		/*
+		 * Don't deadlock trying to
+		 * print in an interrupt.
+		 */
+		if(!canlock(&vgascreenlock))
+			return;
+	}
+	else
+		lock(&vgascreenlock);
+
+	flushr = Rect(10000, 10000, -10000, -10000);
+
+	while(n > 0){
+		i = chartorune(&r, s);
+		if(i == 0){
+			s++;
+			--n;
+			continue;
+		}
+		memmove(buf, s, i);
+		buf[i] = 0;
+		n -= i;
+		s += i;
+		vgascreenputc(scr, buf, &flushr);
+	}
+	flushmemscreen(flushr);
+
+	unlock(&vgascreenlock);
+}
+
+void
+vgascreenwin(VGAscr* scr)
+{
+	int h, w;
+
+	h = scr->memdefont->height;
+	w = scr->memdefont->info[' '].width;
+
+	window = insetrect(scr->gscreen->r, 48);
+	window.max.x = window.min.x+((window.max.x-window.min.x)/w)*w;
+	window.max.y = window.min.y+((window.max.y-window.min.y)/h)*h;
+	curpos = window.min;
+
+	screenputs = vgascreenputs;
+}
+
+/*
+ * Supposedly this is the way to turn DPMS
+ * monitors off using just the VGA registers.
+ * Unfortunately, it seems to mess up the video mode
+ * on the cards I've tried.
+ */
+void
+vgablank(VGAscr*, int blank)
+{
+	uchar seq1, crtc17;
+
+	if(blank) {
+		seq1 = 0x00;
+		crtc17 = 0x80;
+	} else {
+		seq1 = 0x20;
+		crtc17 = 0x00;
+	}
+
+	outs(Seqx, 0x0100);			/* synchronous reset */
+	seq1 |= vgaxi(Seqx, 1) & ~0x20;
+	vgaxo(Seqx, 1, seq1);
+	crtc17 |= vgaxi(Crtx, 0x17) & ~0x80;
+	delay(10);
+	vgaxo(Crtx, 0x17, crtc17);
+	outs(Crtx, 0x0300);				/* end synchronous reset */
+}
+
+void
+cornerstring(char *s)
+{
+	int h, w;
+	VGAscr *scr;
+	Rectangle r;
+	Point p;
+
+	scr = &vgascreen[0];
+	if(scr->aperture == 0 || screenputs != vgascreenputs)
+		return;
+	p = memsubfontwidth(scr->memdefont, s);
+	w = p.x;
+	h = scr->memdefont->height;
+
+	r = Rect(0, 0, w, h);
+	memimagedraw(scr->gscreen, r, back, back->r.min, nil, back->r.min, S);
+	memimagestring(scr->gscreen, r.min, conscol, ZP, scr->memdefont, s);
+//	flushmemscreen(r);
+}
--- /dev/null
+++ b/os/pc/vga.h
@@ -1,0 +1,75 @@
+/*
+ * Generic VGA registers.
+ */
+enum {
+	MiscW		= 0x03C2,	/* Miscellaneous Output (W) */
+	MiscR		= 0x03CC,	/* Miscellaneous Output (R) */
+	Status0		= 0x03C2,	/* Input status 0 (R) */
+	Status1		= 0x03DA,	/* Input Status 1 (R) */
+	FeatureR	= 0x03CA,	/* Feature Control (R) */
+	FeatureW	= 0x03DA,	/* Feature Control (W) */
+
+	Seqx		= 0x03C4,	/* Sequencer Index, Data at Seqx+1 */
+	Crtx		= 0x03D4,	/* CRT Controller Index, Data at Crtx+1 */
+	Grx		= 0x03CE,	/* Graphics Controller Index, Data at Grx+1 */
+	Attrx		= 0x03C0,	/* Attribute Controller Index and Data */
+
+	PaddrW		= 0x03C8,	/* Palette Address Register, write */
+	Pdata		= 0x03C9,	/* Palette Data Register */
+	Pixmask		= 0x03C6,	/* Pixel Mask Register */
+	PaddrR		= 0x03C7,	/* Palette Address Register, read */
+	Pstatus		= 0x03C7,	/* DAC Status (RO) */
+
+	Pcolours	= 256,		/* Palette */
+	Pred		= 0,
+	Pgreen		= 1,
+	Pblue		= 2,
+
+	Pblack		= 0x00,
+	Pwhite		= 0xFF,
+};
+
+#define vgai(port)		inb(port)
+#define vgao(port, data)	outb(port, data)
+
+extern int vgaxi(long, uchar);
+extern int vgaxo(long, uchar, uchar);
+
+typedef struct Cursor Cursor;
+struct	Cursor
+{
+	Point	offset;
+	uchar	clr[2*16];
+	uchar	set[2*16];
+};
+
+/*
+ * First pass at tidying this up...
+ */
+typedef struct Mode {
+	int	x;
+	int	y;
+	int	d;
+
+	ulong	aperture;		/* this is a physical address */
+	int	apsize;
+	int	apshift;
+} Mode;
+
+/*
+ * Definitions of known VGA controllers.
+ */
+typedef struct Vgac Vgac;
+struct Vgac {
+	char*	name;
+	void	(*page)(int);
+	void	(*init)(Mode*);
+	int	(*ident)(void);
+	void	(*enable)(void);
+	void	(*disable)(void);
+	void	(*move)(int, int);
+	void	(*load)(Cursor*);
+	Vgac*	link;
+};
+
+extern void addvgaclink(Vgac*);
--- /dev/null
+++ b/os/pc/vga3dfx.c
@@ -1,0 +1,258 @@
+#include "u.h"
+#include "../port/lib.h"
+#include "mem.h"
+#include "dat.h"
+#include "fns.h"
+#include "io.h"
+#include "../port/error.h"
+
+#define	Image	IMAGE
+#include <draw.h>
+#include <memdraw.h>
+#include <cursor.h>
+#include "screen.h"
+
+typedef struct {
+	int	vidProcCfg;
+	int	hwCurPatAddr;
+	int	hwCurLoc;
+	int	hwCurC0;
+	int	hwCurC1;
+} Cursor3dfx;
+
+enum {
+	dramInit0	= 0x18,
+	dramInit1	= 0x1C,
+
+	hwCur		= 0x5C,
+};
+
+static ulong
+tdfxlinear(VGAscr* scr, int* size, int* align)
+{
+	Pcidev *p;
+	int oapsize, wasupamem;
+	ulong aperture, oaperture;
+
+	oaperture = scr->aperture;
+	oapsize = scr->apsize;
+	wasupamem = scr->isupamem;
+
+	aperture = 0;
+	if(p = pcimatch(nil, 0x121A, 0)){
+		switch(p->did){
+		case 0x0003:		/* Banshee */
+		case 0x0005:		/* Avenger (a.k.a. Voodoo3) */
+		case 0x0009:		/* Voodoo5 */
+			aperture = p->mem[1].bar & ~0x0F;
+			*size = p->mem[1].size;
+			break;
+		default:
+			break;
+		}
+	}
+
+	if(wasupamem){
+		if(oaperture == aperture)
+			return oaperture;
+		upafree(oaperture, oapsize);
+	}
+	scr->isupamem = 0;
+
+	aperture = upamalloc(aperture, *size, *align);
+	if(aperture == 0){
+		if(wasupamem && upamalloc(oaperture, oapsize, 0)){
+			aperture = oaperture;
+			scr->isupamem = 1;
+		}
+		else
+			scr->isupamem = 0;
+	}
+	else
+		scr->isupamem = 1;
+
+	return aperture;
+}
+
+static void
+tdfxenable(VGAscr* scr)
+{
+	Pcidev *p;
+	ulong aperture;
+	int align, i, *mmio, size;
+
+	/*
+	 * Only once, can't be disabled for now.
+	 * scr->io holds the physical address of
+	 * the MMIO registers.
+	 */
+	if(scr->io)
+		return;
+	if(p = pcimatch(nil, 0x121A, 0)){
+		switch(p->did){
+		case 0x0003:		/* Banshee */
+		case 0x0005:		/* Avenger (a.k.a. Voodoo3) */
+			break;
+		default:
+			return;
+		}
+	}
+	else
+		return;
+	scr->io = upamalloc(p->mem[0].bar & ~0x0F, p->mem[0].size, 0);
+	if(scr->io == 0)
+		return;
+
+	addvgaseg("3dfxmmio", (ulong)scr->io, p->mem[0].size);
+
+	size = p->mem[1].size;
+	align = 0;
+	aperture = tdfxlinear(scr, &size, &align);
+	if(aperture){
+		scr->aperture = aperture;
+		scr->apsize = size;
+		addvgaseg("3dfxscreen", aperture, size);
+	}
+
+	/*
+	 * Find a place for the cursor data in display memory.
+	 * If SDRAM then there's 16MB memory else it's SGRAM
+	 * and can count it based on the power-on straps -
+	 * chip size can be 8Mb or 16Mb, and there can be 4 or
+	 * 8 of them.
+	 * Use the last 1KB of the framebuffer.
+	 */
+	mmio = KADDR(scr->io + dramInit0);
+	if(*(mmio+1) & 0x40000000)
+		i = 16*1024*1024;
+	else{
+		if(*mmio & 0x08000000)
+			i = 16*1024*1024/8;
+		else
+			i = 8*1024*1024/8;
+		if(*mmio & 0x04000000)
+			i *= 8;
+		else
+			i *= 4;
+	}
+	scr->storage = i - 1024;
+}
+
+static void
+tdfxcurdisable(VGAscr* scr)
+{
+	Cursor3dfx *cursor3dfx;
+
+	if(scr->io == 0)
+		return;
+	cursor3dfx = KADDR(scr->io+hwCur);
+	cursor3dfx->vidProcCfg &= ~0x08000000;
+}
+
+static void
+tdfxcurload(VGAscr* scr, Cursor* curs)
+{
+	int y;
+	uchar *p;
+	Cursor3dfx *cursor3dfx;
+
+	if(scr->io == 0)
+		return;
+	cursor3dfx = KADDR(scr->io+hwCur);
+
+	/*
+	 * Disable the cursor then load the new image in
+	 * the top-left of the 64x64 array.
+	 * The cursor data is stored in memory as 128-bit
+	 * words consisting of plane 0 in the least significant 64-bits
+	 * and plane 1 in the most significant.
+	 * The X11 cursor truth table is:
+	 *	p0 p1	colour
+	 *	 0  0	transparent
+	 *	 0  1	transparent
+	 *	 1  0	hwCurC0
+	 *	 1  1	hwCurC1
+	 * Unused portions of the image have been initialised to be
+	 * transparent.
+	 */
+	cursor3dfx->vidProcCfg &= ~0x08000000;
+	p = KADDR(scr->aperture + scr->storage);
+	for(y = 0; y < 16; y++){
+		*p++ = curs->clr[2*y]|curs->set[2*y];
+		*p++ = curs->clr[2*y+1]|curs->set[2*y+1];
+		p += 6;
+		*p++ = curs->set[2*y];
+		*p++ = curs->set[2*y+1];
+		p += 6;
+	}
+
+	/*
+	 * Save the cursor hotpoint and enable the cursor.
+	 * The 0,0 cursor point is bottom-right.
+	 */
+	scr->offset.x = 63+curs->offset.x;
+	scr->offset.y = 63+curs->offset.y;
+	cursor3dfx->vidProcCfg |= 0x08000000;
+}
+
+static int
+tdfxcurmove(VGAscr* scr, Point p)
+{
+	Cursor3dfx *cursor3dfx;
+
+	if(scr->io == 0)
+		return 1;
+	cursor3dfx = KADDR(scr->io+hwCur);
+
+	cursor3dfx->hwCurLoc = ((p.y+scr->offset.y)<<16)|(p.x+scr->offset.x);
+
+	return 0;
+}
+
+static void
+tdfxcurenable(VGAscr* scr)
+{
+	Cursor3dfx *cursor3dfx;
+
+	tdfxenable(scr);
+	if(scr->io == 0)
+		return;
+	cursor3dfx = KADDR(scr->io+hwCur);
+
+	/*
+	 * Cursor colours.
+	 */
+	cursor3dfx->hwCurC0 = 0xFFFFFFFF;
+	cursor3dfx->hwCurC1 = 0x00000000;
+
+	/*
+	 * Initialise the 64x64 cursor to be transparent (X11 mode).
+	 */
+	cursor3dfx->hwCurPatAddr = scr->storage;
+	memset(KADDR(scr->aperture + scr->storage), 0, 64*16);
+
+	/*
+	 * Load, locate and enable the 64x64 cursor in X11 mode.
+	 */
+	tdfxcurload(scr, &arrow);
+	tdfxcurmove(scr, ZP);
+	cursor3dfx->vidProcCfg |= 0x08000002;
+}
+
+VGAdev vga3dfxdev = {
+	"3dfx",
+
+	tdfxenable,
+	nil,
+	nil,
+	tdfxlinear,
+};
+
+VGAcur vga3dfxcur = {
+	"3dfxhwgc",
+
+	tdfxcurenable,
+	tdfxcurdisable,
+	tdfxcurload,
+	tdfxcurmove,
+};
--- /dev/null
+++ b/os/pc/vgaark2000pv.c
@@ -1,0 +1,190 @@
+#include "u.h"
+#include "../port/lib.h"
+#include "mem.h"
+#include "dat.h"
+#include "fns.h"
+#include "../port/error.h"
+
+#define	Image	IMAGE
+#include <draw.h>
+#include <memdraw.h>
+#include <cursor.h>
+#include "screen.h"
+
+static int
+ark2000pvpageset(VGAscr*, int page)
+{
+	uchar seq15;
+
+	seq15 = vgaxi(Seqx, 0x15);
+	vgaxo(Seqx, 0x15, page);
+	vgaxo(Seqx, 0x16, page);
+
+	return seq15;
+}
+
+static void
+ark2000pvpage(VGAscr* scr, int page)
+{
+	lock(&scr->devlock);
+	ark2000pvpageset(scr, page);
+	unlock(&scr->devlock);
+}
+
+static void
+ark2000pvdisable(VGAscr*)
+{
+	uchar seq20;
+
+	seq20 = vgaxi(Seqx, 0x20) & ~0x08;
+	vgaxo(Seqx, 0x20, seq20);
+}
+
+static void
+ark2000pvenable(VGAscr* scr)
+{
+	uchar seq20;
+	ulong storage;
+
+	/*
+	 * Disable the cursor then configure for X-Windows style,
+	 * 32x32 and 4/8-bit colour depth.
+	 * Set cursor colours for 4/8-bit.
+	 */
+	seq20 = vgaxi(Seqx, 0x20) & ~0x1F;
+	vgaxo(Seqx, 0x20, seq20);
+	seq20 |= 0x18;
+
+	vgaxo(Seqx, 0x26, 0x00);
+	vgaxo(Seqx, 0x27, 0x00);
+	vgaxo(Seqx, 0x28, 0x00);
+	vgaxo(Seqx, 0x29, 0xFF);
+	vgaxo(Seqx, 0x2A, 0xFF);
+	vgaxo(Seqx, 0x2B, 0xFF);
+
+	/*
+	 * Cursor storage is a 256 byte or 1Kb block located in the last
+	 * 16Kb of video memory. Crt25 is the index of which block.
+	 */
+	storage = (vgaxi(Seqx, 0x10)>>6) & 0x03;
+	storage = (1024*1024)<<storage;
+	storage -= 256;
+	scr->storage = storage;
+	vgaxo(Seqx, 0x25, 0x3F);
+
+	/*
+	 * Enable the cursor.
+	 */
+	vgaxo(Seqx, 0x20, seq20);
+}
+
+static void
+ark2000pvload(VGAscr* scr, Cursor* curs)
+{
+	uchar *p, seq10;
+	int opage, x, y;
+
+	/*
+	 * Is linear addressing turned on? This will determine
+	 * how we access the cursor storage.
+	 */
+	seq10 = vgaxi(Seqx, 0x10);
+	opage = 0;
+	p = KADDR(scr->aperture);
+	if(!(seq10 & 0x10)){
+		lock(&scr->devlock);
+		opage = ark2000pvpageset(scr, scr->storage>>16);
+		p += (scr->storage & 0xFFFF);
+	}
+	else
+		p += scr->storage;
+
+	/*
+	 * The cursor is set in X11 mode which gives the following
+	 * truth table:
+	 *	and xor	colour
+	 *	 0   0	underlying pixel colour
+	 *	 0   1	underlying pixel colour
+	 *	 1   0	background colour
+	 *	 1   1	foreground colour
+	 * Put the cursor into the top-left of the 32x32 array.
+	 * The manual doesn't say what the data layout in memory is -
+	 * this worked out by trial and error.
+	 */
+	for(y = 0; y < 32; y++){
+		for(x = 0; x < 32/8; x++){
+			if(x < 16/8 && y < 16){
+				*p++ = curs->clr[2*y + x]|curs->set[2*y + x];
+				*p++ = curs->set[2*y + x];
+			}
+			else {
+				*p++ = 0x00;
+				*p++ = 0x00;
+			}
+		}
+	}
+
+	if(!(seq10 & 0x10)){
+		ark2000pvpageset(scr, opage);
+		unlock(&scr->devlock);
+	}
+
+	/*
+	 * Save the cursor hotpoint.
+	 */
+	scr->offset = curs->offset;
+}
+
+static int
+ark2000pvmove(VGAscr* scr, Point p)
+{
+	int x, xo, y, yo;
+
+	/*
+	 * Mustn't position the cursor offscreen even partially,
+	 * or it might disappear. Therefore, if x or y is -ve, adjust the
+	 * cursor origins instead.
+	 */
+	if((x = p.x+scr->offset.x) < 0){
+		xo = -x;
+		x = 0;
+	}
+	else
+		xo = 0;
+	if((y = p.y+scr->offset.y) < 0){
+		yo = -y;
+		y = 0;
+	}
+	else
+		yo = 0;
+
+	/*
+	 * Load the new values.
+	 */
+	vgaxo(Seqx, 0x2C, xo);
+	vgaxo(Seqx, 0x2D, yo);
+	vgaxo(Seqx, 0x21, (x>>8) & 0x0F);
+	vgaxo(Seqx, 0x22, x & 0xFF);
+	vgaxo(Seqx, 0x23, (y>>8) & 0x0F);
+	vgaxo(Seqx, 0x24, y & 0xFF);
+
+	return 0;
+}
+
+VGAdev vgaark2000pvdev = {
+	"ark2000pv",
+
+	0,
+	0,
+	ark2000pvpage,
+	0,
+};
+
+VGAcur vgaark2000pvcur = {
+	"ark2000pvhwgc",
+
+	ark2000pvenable,
+	ark2000pvdisable,
+	ark2000pvload,
+	ark2000pvmove,
+};
--- /dev/null
+++ b/os/pc/vgabt485.c
@@ -1,0 +1,245 @@
+#include "u.h"
+#include "../port/lib.h"
+#include "mem.h"
+#include "dat.h"
+#include "fns.h"
+#include "../port/error.h"
+
+#define	Image	IMAGE
+#include <draw.h>
+#include <memdraw.h>
+#include <cursor.h>
+#include "screen.h"
+
+/*
+ * Hardware graphics cursor support for
+ * Brooktree Bt485 Monolithic True-Color RAMDAC.
+ * Assumes hooked up to an S3 86C928.
+ *
+ * BUGS:
+ *	64x64x2 cursor always used;
+ *	no support for interlaced mode.
+ */
+enum {
+	AddrW		= 0x00,		/* Address register; palette/cursor RAM write */
+	Palette		= 0x01,		/* 6/8-bit color palette data */
+	Pmask		= 0x02,		/* Pixel mask register */
+	AddrR		= 0x03,		/* Address register; palette/cursor RAM read */
+	ColorW		= 0x04,		/* Address register; cursor/overscan color write */
+	Color		= 0x05,		/* Cursor/overscan color data */
+	Cmd0		= 0x06,		/* Command register 0 */
+	ColorR		= 0x07,		/* Address register; cursor/overscan color read */
+	Cmd1		= 0x08,		/* Command register 1 */
+	Cmd2		= 0x09,		/* Command register 2 */
+	Status		= 0x0A,		/* Status */
+	Cmd3		= 0x1A,		/* Command register 3 */
+	Cram		= 0x0B,		/* Cursor RAM array data */
+	Cxlr		= 0x0C,		/* Cursor x-low register */
+	Cxhr		= 0x0D,		/* Cursor x-high register */
+	Cylr		= 0x0E,		/* Cursor y-low register */
+	Cyhr		= 0x0F,		/* Cursor y-high register */
+
+	Nreg		= 0x10,
+};
+
+/*
+ * Lower 2-bits of indirect DAC register
+ * addressing.
+ */
+static ushort dacxreg[4] = {
+	PaddrW, Pdata, Pixmask, PaddrR
+};
+
+static uchar
+bt485io(uchar reg)
+{
+	uchar crt55, cr0;
+
+	crt55 = vgaxi(Crtx, 0x55) & 0xFC;
+	if((reg & 0x0F) == Status){
+		/*
+		 * 1,2: Set indirect addressing for Status or
+		 *      Cmd3 - set bit7 of Cr0.
+		 */
+		vgaxo(Crtx, 0x55, crt55|((Cmd0>>2) & 0x03));
+		cr0 = vgai(dacxreg[Cmd0 & 0x03])|0x80;
+		vgao(dacxreg[Cmd0 & 0x03], cr0);
+
+		/*
+		 * 3,4: Set the index into the Write register,
+		 *      index == 0x00 for Status, 0x01 for Cmd3.
+		 */
+		vgaxo(Crtx, 0x55, crt55|((AddrW>>2) & 0x03));
+		vgao(dacxreg[AddrW & 0x03], (reg == Status) ? 0x00: 0x01);
+
+		/*
+		 * 5,6: Get the contents of the appropriate
+		 *      register at 0x0A.
+		 */
+	}
+
+	return crt55;
+}
+
+static uchar
+bt485i(uchar reg)
+{
+	uchar crt55, r;
+
+	crt55 = bt485io(reg);
+	vgaxo(Crtx, 0x55, crt55|((reg>>2) & 0x03));
+	r = vgai(dacxreg[reg & 0x03]);
+	vgaxo(Crtx, 0x55, crt55);
+
+	return r;
+}
+
+static void
+bt485o(uchar reg, uchar data)
+{
+	uchar crt55;
+
+	crt55 = bt485io(reg);
+	vgaxo(Crtx, 0x55, crt55|((reg>>2) & 0x03));
+	vgao(dacxreg[reg & 0x03], data);
+	vgaxo(Crtx, 0x55, crt55);
+}
+
+static void
+bt485disable(VGAscr*)
+{
+	uchar r;
+
+	/*
+	 * Disable 
+	 *	cursor mode 3;
+	 *	cursor control enable for Bt485 DAC;
+	 *	the hardware cursor external operation mode.
+	 */
+	r = bt485i(Cmd2) & ~0x03;
+	bt485o(Cmd2, r);
+
+	r = vgaxi(Crtx, 0x45) & ~0x20;
+	vgaxo(Crtx, 0x45, r);
+
+	r = vgaxi(Crtx, 0x55) & ~0x20;
+	vgaxo(Crtx, 0x55, r);
+}
+
+static void
+bt485enable(VGAscr*)
+{
+	uchar r;
+
+	/*
+	 * Turn cursor off.
+	 */
+	r = bt485i(Cmd2) & 0xFC;
+	bt485o(Cmd2, r);
+
+	/*
+	 * Overscan colour,
+	 * cursor colour 1 (white),
+	 * cursor colour 2, 3 (black).
+	 */
+	bt485o(ColorW, 0x00);
+	bt485o(Color, Pwhite); bt485o(Color, Pwhite); bt485o(Color, Pwhite);
+
+	bt485o(Color, Pwhite); bt485o(Color, Pwhite); bt485o(Color, Pwhite);
+
+	bt485o(Color, Pblack); bt485o(Color, Pblack); bt485o(Color, Pblack);
+	bt485o(Color, Pblack); bt485o(Color, Pblack); bt485o(Color, Pblack);
+
+	/*
+	 * Finally, enable
+	 *	the hardware cursor external operation mode;
+	 *	cursor control enable for Bt485 DAC.
+	 * The #9GXE cards seem to need the 86C928 Bt485 support
+	 * enabled in order to work at all in enhanced mode.
+	 */
+
+	r = vgaxi(Crtx, 0x55)|0x20;
+	vgaxo(Crtx, 0x55, r);
+
+	r = vgaxi(Crtx, 0x45)|0x20;
+	vgaxo(Crtx, 0x45, r);
+}
+
+static void
+bt485load(VGAscr* scr, Cursor* curs)
+{
+	uchar r;
+	int x, y;
+
+	/*
+	 * Turn cursor off;
+	 * put cursor into 64x64x2 mode and clear MSBs of address;
+	 * clear LSBs of address;
+	 */
+	r = bt485i(Cmd2) & 0xFC;
+	bt485o(Cmd2, r);
+
+	r = (bt485i(Cmd3) & 0xFC)|0x04;
+	bt485o(Cmd3, r);
+
+	bt485o(AddrW, 0x00);
+
+	/*
+	 * Now load the cursor RAM array, both planes.
+	 * The cursor is 16x16, the array 64x64; put
+	 * the cursor in the top left. The 0,0 cursor
+	 * point is bottom-right, so positioning will
+	 * have to take that into account.
+	 */
+	for(y = 0; y < 64; y++){
+		for(x = 0; x < 64/8; x++){
+			if(x < 16/8 && y < 16)
+				bt485o(Cram, curs->clr[x+y*2]);
+			else
+				bt485o(Cram, 0x00);
+		}
+	}
+	for(y = 0; y < 64; y++){
+		for(x = 0; x < 64/8; x++){
+			if(x < 16/8 && y < 16)
+				bt485o(Cram, curs->set[x+y*2]);
+			else
+				bt485o(Cram, 0x00);
+		}
+	}
+
+	/*
+	 * Initialise the cursor hot-point
+	 * and enable the cursor.
+	 */
+	scr->offset.x = 64+curs->offset.x;
+	scr->offset.y = 64+curs->offset.y;
+
+	r = (bt485i(Cmd2) & 0xFC)|0x01;
+	bt485o(Cmd2, r);
+}
+
+static int
+bt485move(VGAscr* scr, Point p)
+{
+	int x, y;
+
+	x = p.x+scr->offset.x;
+	y = p.y+scr->offset.y;
+
+	bt485o(Cxlr, x & 0xFF);
+	bt485o(Cxhr, (x>>8) & 0x0F);
+	bt485o(Cylr, y & 0xFF);
+	bt485o(Cyhr, (y>>8) & 0x0F);
+
+	return 0;
+}
+
+VGAcur vgabt485cur = {
+	"bt485hwgc",
+
+	bt485enable,
+	bt485disable,
+	bt485load,
+	bt485move,
+};
--- /dev/null
+++ b/os/pc/vgaclgd542x.c
@@ -1,0 +1,291 @@
+#include "u.h"
+#include "../port/lib.h"
+#include "mem.h"
+#include "dat.h"
+#include "fns.h"
+#include "io.h"
+#include "../port/error.h"
+
+#define	Image	IMAGE
+#include <draw.h>
+#include <memdraw.h>
+#include <cursor.h>
+#include "screen.h"
+
+static int
+clgd542xpageset(VGAscr*, int page)
+{
+	uchar gr09;
+	int opage;
+	
+	if(vgaxi(Seqx, 0x07) & 0xF0)
+		page = 0;
+	gr09 = vgaxi(Grx, 0x09);
+	if(vgaxi(Grx, 0x0B) & 0x20){
+		vgaxo(Grx, 0x09, page<<2);
+		opage = gr09>>2;
+	}
+	else{
+		vgaxo(Grx, 0x09, page<<4);
+		opage = gr09>>4;
+	}
+
+	return opage;
+}
+
+static void
+clgd542xpage(VGAscr* scr, int page)
+{
+	lock(&scr->devlock);
+	clgd542xpageset(scr, page);
+	unlock(&scr->devlock);
+}
+
+static ulong
+clgd542xlinear(VGAscr* scr, int* size, int* align)
+{
+	ulong aperture, oaperture;
+	int oapsize, wasupamem;
+	Pcidev *p;
+
+	oaperture = scr->aperture;
+	oapsize = scr->apsize;
+	wasupamem = scr->isupamem;
+	if(wasupamem)
+		upafree(oaperture, oapsize);
+	scr->isupamem = 0;
+
+	if(p = pcimatch(nil, 0x1013, 0)){
+		aperture = p->mem[0].bar & ~0x0F;
+		*size = p->mem[0].size;
+	}
+	else
+		aperture = 0;
+
+	aperture = upamalloc(aperture, *size, *align);
+	if(aperture == 0){
+		if(wasupamem && upamalloc(oaperture, oapsize, 0))
+			scr->isupamem = 1;
+	}
+	else
+		scr->isupamem = 1;
+
+	return aperture;
+}
+
+static void
+clgd542xdisable(VGAscr*)
+{
+	uchar sr12;
+
+	sr12 = vgaxi(Seqx, 0x12);
+	vgaxo(Seqx, 0x12, sr12 & ~0x01);
+}
+
+static void
+clgd542xenable(VGAscr* scr)
+{
+	uchar sr12;
+	int mem, x;
+ 
+	/*
+	 * Disable the cursor.
+	 */
+	sr12 = vgaxi(Seqx, 0x12);
+	vgaxo(Seqx, 0x12, sr12 & ~0x01);
+
+	/*
+	 * Cursor colours.
+	 * Can't call setcolor here as cursor is already locked.
+	 */
+	vgaxo(Seqx, 0x12, sr12|0x02);
+	vgao(PaddrW, 0x00);
+	vgao(Pdata, Pwhite);
+	vgao(Pdata, Pwhite);
+	vgao(Pdata, Pwhite);
+	vgao(PaddrW, 0x0F);
+	vgao(Pdata, Pblack);
+	vgao(Pdata, Pblack);
+	vgao(Pdata, Pblack);
+	vgaxo(Seqx, 0x12, sr12);
+
+	mem = 0;
+	switch(vgaxi(Crtx, 0x27) & ~0x03){
+
+	case 0x88:				/* CL-GD5420 */
+	case 0x8C:				/* CL-GD5422 */
+	case 0x94:				/* CL-GD5424 */
+	case 0x80:				/* CL-GD5425 */
+	case 0x90:				/* CL-GD5426 */
+	case 0x98:				/* CL-GD5427 */
+	case 0x9C:				/* CL-GD5429 */
+		/*
+		 * The BIOS leaves the memory size in Seq0A, bits 4 and 3.
+		 * See Technical Reference Manual Appendix E1, Section 1.3.2.
+		 *
+		 * The storage area for the 64x64 cursors is the last 16Kb of
+		 * display memory.
+		 */
+		mem = (vgaxi(Seqx, 0x0A)>>3) & 0x03;
+		break;
+
+	case 0xA0:				/* CL-GD5430 */
+	case 0xA8:				/* CL-GD5434 */
+	case 0xAC:				/* CL-GD5436 */
+	case 0xB8:				/* CL-GD5446 */
+	case 0x30:				/* CL-GD7543 */
+		/*
+		 * Attempt to intuit the memory size from the DRAM control
+		 * register. Minimum is 512KB.
+		 * If DRAM bank switching is on then there's double.
+		 */
+		x = vgaxi(Seqx, 0x0F);
+		mem = (x>>3) & 0x03;
+		if(x & 0x80)
+			mem++;
+		break;
+
+	default:				/* uh, ah dunno */
+		break;
+	}
+	scr->storage = ((256<<mem)-16)*1024;
+
+	/*
+	 * Set the current cursor to index 0
+	 * and turn the 64x64 cursor on.
+	 */
+	vgaxo(Seqx, 0x13, 0);
+	vgaxo(Seqx, 0x12, sr12|0x05);
+}
+
+static void
+clgd542xinitcursor(VGAscr* scr, int xo, int yo, int index)
+{
+	uchar *p, seq07;
+	uint p0, p1;
+	int opage, x, y;
+
+	/*
+	 * Is linear addressing turned on? This will determine
+	 * how we access the cursor storage.
+	 */
+	seq07 = vgaxi(Seqx, 0x07);
+	opage = 0;
+	p = KADDR(scr->aperture);
+	if(!(seq07 & 0xF0)){
+		lock(&scr->devlock);
+		opage = clgd542xpageset(scr, scr->storage>>16);
+		p += (scr->storage & 0xFFFF);
+	}
+	else
+		p += scr->storage;
+	p += index*1024;
+
+	for(y = yo; y < 16; y++){
+		p0 = scr->set[2*y];
+		p1 = scr->set[2*y+1];
+		if(xo){
+			p0 = (p0<<xo)|(p1>>(8-xo));
+			p1 <<= xo;
+		}
+		*p++ = p0;
+		*p++ = p1;
+
+		for(x = 16; x < 64; x += 8)
+			*p++ = 0x00;
+
+		p0 = scr->clr[2*y]|scr->set[2*y];
+		p1 = scr->clr[2*y+1]|scr->set[2*y+1];
+		if(xo){
+			p0 = (p0<<xo)|(p1>>(8-xo));
+			p1 <<= xo;
+		}
+		*p++ = p0;
+		*p++ = p1;
+
+		for(x = 16; x < 64; x += 8)
+			*p++ = 0x00;
+	}
+	while(y < 64+yo){
+		for(x = 0; x < 64; x += 8){
+			*p++ = 0x00;
+			*p++ = 0x00;
+		}
+		y++;
+	}
+
+	if(!(seq07 & 0xF0)){
+		clgd542xpageset(scr, opage);
+		unlock(&scr->devlock);
+	}
+}
+
+static void
+clgd542xload(VGAscr* scr, Cursor* curs)
+{
+	uchar sr12;
+
+	/*
+	 * Disable the cursor.
+	 */
+	sr12 = vgaxi(Seqx, 0x12);
+	vgaxo(Seqx, 0x12, sr12 & ~0x01);
+
+	memmove(&scr->Cursor, curs, sizeof(Cursor));
+	clgd542xinitcursor(scr, 0, 0, 0);
+
+	/*
+	 * Enable the cursor.
+	 */
+	vgaxo(Seqx, 0x13, 0);
+	vgaxo(Seqx, 0x12, sr12|0x05);
+}
+
+static int
+clgd542xmove(VGAscr* scr, Point p)
+{
+	int index, x, xo, y, yo;
+
+	index = 0;
+	if((x = p.x+scr->offset.x) < 0){
+		xo = -x;
+		x = 0;
+	}
+	else
+		xo = 0;
+	if((y = p.y+scr->offset.y) < 0){
+		yo = -y;
+		y = 0;
+	}
+	else
+		yo = 0;
+
+	if(xo || yo){
+		clgd542xinitcursor(scr, xo, yo, 1);
+		index = 1;
+	}
+	vgaxo(Seqx, 0x13, index<<2);
+	
+	vgaxo(Seqx, 0x10|((x & 0x07)<<5), (x>>3) & 0xFF);
+	vgaxo(Seqx, 0x11|((y & 0x07)<<5), (y>>3) & 0xFF);
+
+	return 0;
+}
+
+VGAdev vgaclgd542xdev = {
+	"clgd542x",
+
+	0,
+	0,
+	clgd542xpage,
+	clgd542xlinear,
+};
+
+VGAcur vgaclgd542xcur = {
+	"clgd542xhwgc",
+
+	clgd542xenable,
+	clgd542xdisable,
+	clgd542xload,
+	clgd542xmove,
+};
--- /dev/null
+++ b/os/pc/vgaclgd546x.c
@@ -1,0 +1,277 @@
+#include "u.h"
+#include "../port/lib.h"
+#include "mem.h"
+#include "dat.h"
+#include "fns.h"
+#include "io.h"
+#include "../port/error.h"
+
+#define	Image	IMAGE
+#include <draw.h>
+#include <memdraw.h>
+#include <cursor.h>
+#include "screen.h"
+
+typedef struct Cursor546x Cursor546x;
+struct Cursor546x {
+	ushort	x;
+	ushort	y;
+	ushort	preset;
+	ushort	enable;
+	ushort	addr;
+};
+
+enum {
+	PaletteState	= 0xB0,
+	CursorMMIO	= 0xE0,
+};
+
+static ulong
+clgd546xlinear(VGAscr* scr, int* size, int* align)
+{
+	ulong aperture, oaperture;
+	int oapsize, wasupamem;
+	Pcidev *p;
+
+	oaperture = scr->aperture;
+	oapsize = scr->apsize;
+	wasupamem = scr->isupamem;
+
+	aperture = 0;
+	if(p = pcimatch(nil, 0x1013, 0)){
+		switch(p->did){
+		case 0xD0:
+		case 0xD4:
+		case 0xD6:
+			aperture = p->mem[0].bar & ~0x0F;
+			*size = p->mem[0].size;
+			break;
+		default:
+			break;
+		}
+	}
+
+	if(wasupamem){
+		if(oaperture == aperture)
+			return oaperture;
+		upafree(oaperture, oapsize);
+	}
+	scr->isupamem = 0;
+
+	aperture = upamalloc(aperture, *size, *align);
+	if(aperture == 0){
+		if(wasupamem && upamalloc(oaperture, oapsize, 0)){
+			aperture = oaperture;
+			scr->isupamem = 1;
+		}
+		else
+			scr->isupamem = 0;
+	}
+	else
+		scr->isupamem = 1;
+
+	return aperture;
+}
+static void
+clgd546xenable(VGAscr* scr)
+{
+	Pcidev *p;
+	int size, align;
+	ulong aperture;
+
+	/*
+	 * Only once, can't be disabled for now.
+	 * scr->io holds the virtual address of
+	 * the MMIO registers.
+	 */
+	if(scr->io)
+		return;
+	if(p = pcimatch(nil, 0x1013, 0)){
+		switch(p->did){
+		case 0xD0:
+		case 0xD4:
+		case 0xD6:
+			break;
+		default:
+			return;
+		}
+	}
+	else
+		return;
+	scr->io = upamalloc(p->mem[1].bar & ~0x0F, p->mem[1].size, 0);
+	if(scr->io == 0)
+		return;
+	addvgaseg("clgd546xmmio", scr->io, p->mem[1].size);
+
+	scr->io = (ulong)KADDR(scr->io);
+
+	size = p->mem[0].size;
+	align = 0;
+	aperture = clgd546xlinear(scr, &size, &align);
+	if(aperture) {
+		scr->aperture = aperture;
+		scr->apsize = size;
+		addvgaseg("clgd546xscreen", aperture, size);
+	}
+}
+
+static void
+clgd546xcurdisable(VGAscr* scr)
+{
+	Cursor546x *cursor546x;
+
+	if(scr->io == 0)
+		return;
+	cursor546x = (Cursor546x*)(scr->io+CursorMMIO);
+	cursor546x->enable = 0;
+}
+
+static void
+clgd546xcurload(VGAscr* scr, Cursor* curs)
+{
+	int c, i, m, y;
+	uchar *p;
+	Cursor546x *cursor546x;
+
+	if(scr->io == 0)
+		return;
+	cursor546x = (Cursor546x*)(scr->io+CursorMMIO);
+
+	/*
+	 * Disable the cursor then change only the bits
+	 * that need it.
+	 */
+	cursor546x->enable = 0;
+	p = (uchar*)(scr->aperture + scr->storage);
+	for(y = 0; y < 16; y++){
+		c = curs->set[2*y];
+		m = 0;
+		for(i = 0; i < 8; i++){
+			if(c & (1<<(7-i)))
+				m |= 1<<i;
+		}
+		*p++ = m;
+		c = curs->set[2*y + 1];
+		m = 0;
+		for(i = 0; i < 8; i++){
+			if(c & (1<<(7-i)))
+				m |= 1<<i;
+		}
+		*p++ = m;
+		p += 6;
+		c = curs->set[2*y]|curs->clr[2*y];
+		m = 0;
+		for(i = 0; i < 8; i++){
+			if(c & (1<<(7-i)))
+				m |= 1<<i;
+		}
+		*p++ = m;
+		c = curs->set[2*y + 1]|curs->clr[2*y + 1];
+		m = 0;
+		for(i = 0; i < 8; i++){
+			if(c & (1<<(7-i)))
+				m |= 1<<i;
+		}
+		*p++ = m;
+		p += 6;
+	}
+
+	/*
+	 * Save the cursor hotpoint and enable the cursor.
+	 */
+	scr->offset = curs->offset;
+	cursor546x->enable = 1;
+}
+
+static int
+clgd546xcurmove(VGAscr* scr, Point p)
+{
+	int x, xo, y, yo;
+	Cursor546x *cursor546x;
+
+	if(scr->io == 0)
+		return 1;
+	cursor546x = (Cursor546x*)(scr->io+CursorMMIO);
+
+	if((x = p.x+scr->offset.x) < 0){
+		xo = -x;
+		x = 0;
+	}
+	else
+		xo = 0;
+	if((y = p.y+scr->offset.y) < 0){
+		yo = -y;
+		y = 0;
+	}
+	else
+		yo = 0;
+
+	cursor546x->preset = (xo<<8)|yo;
+	cursor546x->x = x;
+	cursor546x->y = y;
+
+	return 0;
+}
+
+static void
+clgd546xcurenable(VGAscr* scr)
+{
+	uchar *p;
+	Cursor546x *cursor546x;
+
+	clgd546xenable(scr);
+	if(scr->io == 0)
+		return;
+	cursor546x = (Cursor546x*)(scr->io+CursorMMIO);
+
+	/*
+	 * Cursor colours.
+	 * Can't call setcolor here as cursor is already locked.
+	 */
+	p = (uchar*)(scr->io+PaletteState);
+	*p |= 0x08;
+	vgao(PaddrW, 0x00);
+	vgao(Pdata, Pwhite);
+	vgao(Pdata, Pwhite);
+	vgao(Pdata, Pwhite);
+	vgao(PaddrW, 0x0F);
+	vgao(Pdata, Pblack);
+	vgao(Pdata, Pblack);
+	vgao(Pdata, Pblack);
+	*p &= ~0x08;
+
+	/*
+	 * Find a place for the cursor data in display memory.
+	 * 2 cursor images might be needed, 1KB each so use the last
+	 * 2KB of the framebuffer and initialise them to be
+	 * transparent.
+	 */
+	scr->storage = ((vgaxi(Seqx, 0x14) & 0x07)+1)*1024*1022;
+	cursor546x->addr = (scr->storage>>10)<<2;
+	memset((uchar*)(scr->aperture + scr->storage), 0, 2*64*16);
+
+	/*
+	 * Load, locate and enable the 64x64 cursor.
+	 */
+	clgd546xcurload(scr, &arrow);
+	clgd546xcurmove(scr, ZP);
+	cursor546x->enable = 1;
+}
+
+VGAdev vgaclgd546xdev = {
+	"clgd546x",
+
+	clgd546xenable,
+	nil,
+	nil,
+	clgd546xlinear,
+};
+
+VGAcur vgaclgd546xcur = {
+	"clgd546xhwgc",
+
+	clgd546xcurenable,
+	clgd546xcurdisable,
+	clgd546xcurload,
+	clgd546xcurmove,
+};
--- /dev/null
+++ b/os/pc/vgact65545.c
@@ -1,0 +1,149 @@
+#include "u.h"
+#include "../port/lib.h"
+#include "mem.h"
+#include "dat.h"
+#include "fns.h"
+#include "../port/error.h"
+
+#define	Image	IMAGE
+#include <draw.h>
+#include <memdraw.h>
+#include <cursor.h>
+#include "screen.h"
+
+static void
+ct65545page(VGAscr*, int page)
+{
+	outb(0x3D6, 0x10);
+	outb(0x3D7, page<<6);
+}
+
+static void
+ct65545disable(VGAscr*)
+{
+	outl(0xA3D0, 0);
+}
+
+static void
+ct65545enable(VGAscr* scr)
+{
+	ulong storage;
+
+	/*
+	 * Find a place for the cursor data in display memory.
+	 * Must be on a 1024-byte boundary.
+	 */
+	storage = ROUND(scr->gscreen->width*BY2WD*scr->gscreen->r.max.y, 1024);
+	outl(0xB3D0, storage);
+	scr->storage = storage;
+
+	/*
+	 * Set the colours.
+	 * Enable the cursor.
+	 */
+	outl(0xA7D0, 0xFFFF0000);
+	outl(0xA3D0, 2);
+}
+
+static void
+ct65545initcursor(VGAscr* scr, int xo, int yo, int index)
+{
+	uchar *mem;
+	uint and, clr, set, xor;
+	int i, x, y;
+
+	mem = KADDR(scr->aperture);
+	mem += scr->storage + index*1024;
+
+	for(y = yo; y < 16; y++){
+		clr = (scr->clr[2*y]<<8)|scr->clr[2*y+1];
+		set = (scr->set[2*y]<<8)|scr->set[2*y+1];
+		if(xo){
+			clr <<= xo;
+			set <<= xo;
+		}
+
+		and = 0;
+		xor = 0;
+		for(i = 0; i < 16; i++){
+			if(set & (1<<i)){
+				/* nothing to do */
+			}
+			else if(clr & (1<<i))
+				xor |= 1<<i;
+			else
+				and |= 1<<i;
+		}
+		*mem++ = and>>8;
+		*mem++ = xor>>8;
+		*mem++ = and;
+		*mem++ = xor;
+
+		for(x = 16; x < 64; x += 8){
+			*mem++ = 0xFF;
+			*mem++ = 0x00;
+		}
+	}
+	while(y < 64+yo){
+		for(x = 0; x < 64; x += 8){
+			*mem++ = 0xFF;
+			*mem++ = 0x00;
+		}
+		y++;
+	}
+}
+
+static void
+ct65545load(VGAscr* scr, Cursor* curs)
+{
+	memmove(&scr->Cursor, curs, sizeof(Cursor));
+	ct65545initcursor(scr, 0, 0, 0);
+}
+
+static int
+ct65545move(VGAscr* scr, Point p)
+{
+	int index, x, xo, y, yo;
+
+	index = 0;
+	if((x = p.x+scr->offset.x) < 0){
+		xo = -x;
+		x = 0;
+	}
+	else
+		xo = 0;
+	if((y = p.y+scr->offset.y) < 0){
+		yo = -y;
+		y = 0;
+	}
+	else
+		yo = 0;
+
+	if(xo || yo){
+		ct65545initcursor(scr, xo, yo, 1);
+		index = 1;
+	}
+	outl(0xB3D0, scr->storage + index*1024);
+
+	outl(0xAFD0, (y<<16)|x);
+
+	return 0;
+}
+
+VGAdev vgact65545dev = {
+	"ct65540",				/* BUG: really 65545 */
+
+	0,
+	0,
+	ct65545page,
+	0,
+};
+
+VGAcur vgact65545cur = {
+	"ct65545hwgc",
+
+	ct65545enable,
+	ct65545disable,
+	ct65545load,
+	ct65545move,
+};
--- /dev/null
+++ b/os/pc/vgacyber938x.c
@@ -1,0 +1,225 @@
+#include "u.h"
+#include "../port/lib.h"
+#include "mem.h"
+#include "dat.h"
+#include "fns.h"
+#include "io.h"
+#include "../port/error.h"
+
+#define	Image	IMAGE
+#include <draw.h>
+#include <memdraw.h>
+#include <cursor.h>
+#include "screen.h"
+
+enum {
+	CursorON	= 0xC8,
+	CursorOFF	= 0x00,
+};
+
+static int
+cyber938xpageset(VGAscr*, int page)
+{
+	int opage;
+
+	opage = inb(0x3D8);
+
+	outb(0x3D8, page);
+	outb(0x3D9, page);
+
+	return opage;
+}
+
+static void
+cyber938xpage(VGAscr* scr, int page)
+{
+	lock(&scr->devlock);
+	cyber938xpageset(scr, page);
+	unlock(&scr->devlock);
+}
+
+static ulong
+cyber938xlinear(VGAscr* scr, int* size, int* align)
+{
+	ulong aperture, oaperture;
+	int oapsize, wasupamem;
+	int osize;
+	Pcidev *p;
+
+	osize = *size;
+	oaperture = scr->aperture;
+	oapsize = scr->apsize;
+	wasupamem = scr->isupamem;
+	if(wasupamem)
+		upafree(oaperture, oapsize);
+	scr->isupamem = 0;
+	scr->mmio = 0;
+
+	if(p = pcimatch(nil, 0x1023, 0)){
+		aperture = p->mem[0].bar & ~0x0F;
+		*size = p->mem[0].size;
+		/*
+		 * Heuristic to detect the MMIO space.  We're flying blind
+		 * here, with only the XFree86 source to guide us.
+		 */
+		if(p->mem[1].size == 0x20000)
+			scr->mmio = (ulong*)(p->mem[1].bar & ~0x0F);
+	}
+	else
+		aperture = 0;
+
+	aperture = upamalloc(aperture, *size, *align);
+	if(aperture == 0){
+		if(wasupamem && upamalloc(oaperture, oapsize, 0))
+			scr->isupamem = 1;
+	}
+	else
+		scr->isupamem = 1;
+
+	if(aperture)
+		addvgaseg("cyber938xscreen", aperture, osize);
+	if(scr->mmio)
+		addvgaseg("cyber938xmmio", (ulong)scr->mmio, 0x20000);
+
+	return aperture;
+}
+
+static void
+cyber938xcurdisable(VGAscr*)
+{
+	vgaxo(Crtx, 0x50, CursorOFF);
+}
+
+static void
+cyber938xcurload(VGAscr* scr, Cursor* curs)
+{
+	uchar *p;
+	int islinear, opage, y;
+
+	cyber938xcurdisable(scr);
+
+	opage = 0;
+	p = KADDR(scr->aperture);
+	islinear = vgaxi(Crtx, 0x21) & 0x20;
+	if(!islinear){
+		lock(&scr->devlock);
+		opage = cyber938xpageset(scr, scr->storage>>16);
+		p += (scr->storage & 0xFFFF);
+	}
+	else
+		p += scr->storage;
+
+	for(y = 0; y < 16; y++){
+		*p++ = curs->set[2*y]|curs->clr[2*y];
+		*p++ = curs->set[2*y + 1]|curs->clr[2*y + 1];
+		*p++ = 0x00;
+		*p++ = 0x00;
+		*p++ = curs->set[2*y];
+		*p++ = curs->set[2*y + 1];
+		*p++ = 0x00;
+		*p++ = 0x00;
+	}
+	memset(p, 0, (32-y)*8);
+
+	if(!islinear){
+		cyber938xpageset(scr, opage);
+		unlock(&scr->devlock);
+	}
+
+	/*
+	 * Save the cursor hotpoint and enable the cursor.
+	 */
+	scr->offset = curs->offset;
+	vgaxo(Crtx, 0x50, CursorON);
+}
+
+static int
+cyber938xcurmove(VGAscr* scr, Point p)
+{
+	int x, xo, y, yo;
+
+	/*
+	 * Mustn't position the cursor offscreen even partially,
+	 * or it might disappear. Therefore, if x or y is -ve, adjust the
+	 * cursor origins instead.
+	 */
+	if((x = p.x+scr->offset.x) < 0){
+		xo = -x;
+		x = 0;
+	}
+	else
+		xo = 0;
+	if((y = p.y+scr->offset.y) < 0){
+		yo = -y;
+		y = 0;
+	}
+	else
+		yo = 0;
+
+	/*
+	 * Load the new values.
+	 */
+	vgaxo(Crtx, 0x46, xo);
+	vgaxo(Crtx, 0x47, yo);
+	vgaxo(Crtx, 0x40, x & 0xFF);
+	vgaxo(Crtx, 0x41, (x>>8) & 0xFF);
+	vgaxo(Crtx, 0x42, y & 0xFF);
+	vgaxo(Crtx, 0x43, (y>>8) & 0xFF);
+
+	return 0;
+}
+
+static void
+cyber938xcurenable(VGAscr* scr)
+{
+	int i;
+	ulong storage;
+
+	cyber938xcurdisable(scr);
+
+	/*
+	 * Cursor colours.
+	 */
+	for(i = 0x48; i < 0x4C; i++)
+		vgaxo(Crtx, i, 0x00);
+	for(i = 0x4C; i < 0x50; i++)
+		vgaxo(Crtx, i, 0xFF);
+
+	/*
+	 * Find a place for the cursor data in display memory.
+	 */
+	storage = ((scr->gscreen->width*BY2WD*scr->gscreen->r.max.y+1023)/1024);
+	vgaxo(Crtx, 0x44, storage & 0xFF);
+	vgaxo(Crtx, 0x45, (storage>>8) & 0xFF);
+	storage *= 1024;
+	scr->storage = storage;
+
+	/*
+	 * Load, locate and enable the 32x32 cursor.
+	 * (64x64 is bit 0, X11 format is bit 6 and cursor
+	 * enable is bit 7). Bit 3 needs to be set on 9382
+	 * chips otherwise even the white bits are black.
+	 */
+	cyber938xcurload(scr, &arrow);
+	cyber938xcurmove(scr, ZP);
+	vgaxo(Crtx, 0x50, CursorON);
+}
+
+VGAdev vgacyber938xdev = {
+	"cyber938x",
+
+	nil,				/* enable */
+	nil,				/* disable */
+	cyber938xpage,			/* page */
+	cyber938xlinear,		/* linear */
+	nil,				/* drawinit */
+};
+
+VGAcur vgacyber938xcur = {
+	"cyber938xhwgc",
+
+	cyber938xcurenable,		/* enable */
+	cyber938xcurdisable,		/* disable */
+	cyber938xcurload,		/* load */
+	cyber938xcurmove,		/* move */
+};
--- /dev/null
+++ b/os/pc/vgaet4000.c
@@ -1,0 +1,270 @@
+#include "u.h"
+#include "../port/lib.h"
+#include "mem.h"
+#include "dat.h"
+#include "fns.h"
+#include "../port/error.h"
+
+#define	Image	IMAGE
+#include <draw.h>
+#include <memdraw.h>
+#include <cursor.h>
+#include "screen.h"
+
+static void
+setet4000page(int page)
+{
+	uchar p;
+
+	p = page & 0x0F;
+	p |= p<<4;
+	outb(0x3CD, p);
+
+	p = (page & 0x30);
+	p |= p>>4;
+	outb(0x3CB, p);
+}
+
+static void
+et4000page(VGAscr *scr, int page)
+{
+	lock(&scr->devlock);
+	setet4000page(page);
+	unlock(&scr->devlock);
+}
+
+static void
+et4000disable(VGAscr*)
+{
+	uchar imaF7;
+
+	outb(0x217A, 0xF7);
+	imaF7 = inb(0x217B) & ~0x80;
+	outb(0x217B, imaF7);
+}
+
+static void
+et4000enable(VGAscr *scr)
+{
+	uchar imaF7;
+
+	et4000disable(scr);
+
+	/*
+	 * Configure CRTCB for Sprite, 64x64,
+	 * CRTC pixel overlay.
+	 */
+	outb(0x217A, 0xEF);
+	outb(0x217B, 0x02);
+
+	/*
+	 * Cursor goes in the top left corner
+	 * of the Sprite area, so the horizontal and
+	 * vertical presets are 0.
+	 */
+	outb(0x217A, 0xE2);
+	outb(0x217B, 0x00);
+	outb(0x217A, 0xE3);
+	outb(0x217B, 0x00);
+
+	outb(0x217A, 0xE6);
+	outb(0x217B, 0x00);
+	outb(0x217A, 0xE7);
+	outb(0x217B, 0x00);
+
+	/*
+	 * Find a place for the cursor data in display memory.
+	 * Must be on a "doubleword" boundary, but put it on a
+	 * 1024-byte boundary so that there's no danger of it
+	 * crossing a page.
+	 */
+	scr->storage = (scr->gscreen->width*BY2WD*scr->gscreen->r.max.y+1023)/1024;
+	scr->storage *= 1024/4;
+	outb(0x217A, 0xE8);
+	outb(0x217B, scr->storage & 0xFF);
+	outb(0x217A, 0xE9);
+	outb(0x217B, (scr->storage>>8) & 0xFF);
+	outb(0x217A, 0xEA);
+	outb(0x217B, (scr->storage>>16) & 0x0F);
+	scr->storage *= 4;
+
+	/*
+	 * Row offset in "quadwords". Must be 2 for Sprite.
+	 * Bag the pixel-panning.
+	 * Colour depth, must be 2 for Sprite.
+	 */
+	outb(0x217A, 0xEB);
+	outb(0x217B, 0x02);
+	outb(0x217A, 0xEC);
+	outb(0x217B, 0x00);
+
+	outb(0x217A, 0xED);
+	outb(0x217B, 0x00);
+
+	outb(0x217A, 0xEE);
+//	if(vgascreen.ldepth == 3)
+		outb(0x217B, 0x01);
+//	else
+//		outb(0x217B, 0x00);
+
+	/*
+	 * Enable the CRTCB/Sprite.
+	 */
+	outb(0x217A, 0xF7);
+	imaF7 = inb(0x217B);
+	outb(0x217B, 0x80|imaF7);
+}
+
+static void
+et4000load(VGAscr *scr, Cursor *c)
+{
+	uchar p0, p1, *mem;
+	int i, x, y;
+	ushort p;
+	uchar clr[2*16], set[2*16];
+
+	/*
+	 * Lock the display memory so we can update the
+	 * cursor bitmap if necessary.
+	 */
+	lock(&scr->devlock);
+
+	/*
+	 * Disable the cursor.
+	 * Set the display page (do we need to restore
+	 * the current contents when done?) and the
+	 * pointer to the two planes. What if this crosses
+	 * into a new page?
+	 */
+	et4000disable(scr);
+
+	setet4000page(scr->storage>>16);
+	mem = (uchar*)KADDR(scr->aperture) + (scr->storage & 0xFFFF);
+
+	/*
+	 * Initialise the 64x64 cursor RAM array. There are 2 planes,
+	 * p0 and p1. Data is written 4 pixels per byte, with p1 the
+	 * MS bit of each pixel.
+	 * The cursor mode gives the following truth table:
+	 *	p1 p0	colour
+	 *	 0  0	Sprite Colour 0 (defined as 0x00)
+	 *	 0  1	Sprite Colour 1 (defined as 0xFF)
+	 *	 1  0	Transparent (allow CRTC pixel pass through)
+	 *	 1  1	Invert (allow CRTC pixel invert through)
+	 * Put the cursor into the top-left of the 64x64 array.
+	 *
+	 * This is almost certainly wrong, since it has not
+	 * been updated for the 3rd edition color values.
+	 */
+	memmove(clr, c->clr, sizeof(clr));
+//	pixreverse(clr, sizeof(clr), 0);
+	memmove(set, c->set, sizeof(set));
+//	pixreverse(set, sizeof(set), 0);
+	for(y = 0; y < 64; y++){
+		for(x = 0; x < 64/8; x++){
+			if(x < 16/8 && y < 16){
+				p0 = clr[x+y*2];
+				p1 = set[x+y*2];
+
+				p = 0x0000;
+				for(i = 0; i < 8; i++){
+					if(p1 & (1<<(7-i))){
+						/* nothing to do */
+					}
+					else if(p0 & (1<<(7-i)))
+						p |= 0x01<<(2*i);
+					else
+						p |= 0x02<<(2*i);
+				}
+				*mem++ = p & 0xFF;
+				*mem++ = (p>>8) & 0xFF;
+			}
+			else {
+				*mem++ = 0xAA;
+				*mem++ = 0xAA;
+			}
+		}
+	}
+
+	/*
+	 * enable the cursor.
+	 */
+	outb(0x217A, 0xF7);
+	p = inb(0x217B)|0x80;
+	outb(0x217B, p);
+
+	unlock(&scr->devlock);
+}
+
+static int
+et4000move(VGAscr *scr, Point p)
+{
+	int x, xo, y, yo;
+
+	if(canlock(&scr->devlock) == 0)
+		return 1;
+
+	/*
+	 * Mustn't position the cursor offscreen even partially,
+	 * or it disappears. Therefore, if x or y is -ve, adjust the
+	 * cursor presets instead.
+	 */
+	if((x = p.x+scr->offset.x) < 0){
+		xo = -x;
+		x = 0;
+	}
+	else
+		xo = 0;
+	if((y = p.y+scr->offset.y) < 0){
+		yo = -y;
+		y = 0;
+	}
+	else
+		yo = 0;
+
+	/*
+	 * The cursor image is jerky if we don't do this.
+	 * The cursor information is probably fetched from
+	 * display memory during the horizontal blank active
+	 * time and it doesn't like it if the coordinates
+	 * are changed underneath.
+	 */
+	while((vgai(Status1) & 0x08) == 0)
+		;
+
+	outb(0x217A, 0xE2);
+	outb(0x217B, xo);
+
+	outb(0x217A, 0xE6);
+	outb(0x217B, yo);
+
+	outb(0x217A, 0xE1);
+	outb(0x217B, (x>>8) & 0xFF);
+	outb(0x217A, 0xE0);
+	outb(0x217B, x & 0xFF);
+	outb(0x217A, 0xE5);
+	outb(0x217B, (y>>8) & 0xFF);
+	outb(0x217A, 0xE4);
+	outb(0x217B, y & 0xFF);
+
+	unlock(&scr->devlock);
+	return 0;
+}
+
+VGAcur vgaet4000cur = {
+	"et4000hwgc",
+
+	et4000enable,
+	et4000disable,
+	et4000load,
+	et4000move,
+};
+
+VGAdev vgaet4000dev = {
+	"et4000",
+
+	0,
+	0,
+	et4000page,
+	0
+};
--- /dev/null
+++ b/os/pc/vgahiqvideo.c
@@ -1,0 +1,274 @@
+#include "u.h"
+#include "../port/lib.h"
+#include "mem.h"
+#include "dat.h"
+#include "fns.h"
+#include "io.h"
+#include "../port/error.h"
+
+#define	Image	IMAGE
+#include <draw.h>
+#include <memdraw.h>
+#include <cursor.h>
+#include "screen.h"
+
+enum {
+	Xrx		= 0x3D6,	/* Configuration Extensions Index */
+};
+
+static uchar
+hiqvideoxi(long port, uchar index)
+{
+	uchar data;
+
+	outb(port, index);
+	data = inb(port+1);
+
+	return data;
+}
+
+static void
+hiqvideoxo(long port, uchar index, uchar data)
+{
+	outb(port, index);
+	outb(port+1, data);
+}
+
+static ulong
+hiqvideolinear(VGAscr* scr, int* size, int* align)
+{
+	ulong aperture, oaperture;
+	int oapsize, wasupamem;
+	Pcidev *p;
+
+	oaperture = scr->aperture;
+	oapsize = scr->apsize;
+	wasupamem = scr->isupamem;
+
+	aperture = 0;
+	if(p = pcimatch(nil, 0x102C, 0)){
+		switch(p->did){
+		case 0x00C0:		/* 69000 HiQVideo */
+		case 0x00E0:		/* 65550 HiQV32 */
+		case 0x00E4:		/* 65554 HiQV32 */
+		case 0x00E5:		/* 65555 HiQV32 */
+			aperture = p->mem[0].bar & ~0x0F;
+			*size = p->mem[0].size;
+			break;
+		default:
+			break;
+		}
+	}
+
+	if(wasupamem){
+		if(oaperture == aperture)
+			return oaperture;
+		upafree(oaperture, oapsize);
+	}
+	scr->isupamem = 0;
+
+	aperture = upamalloc(aperture, *size, *align);
+	if(aperture == 0){
+		if(wasupamem && upamalloc(oaperture, oapsize, 0)){
+			aperture = oaperture;
+			scr->isupamem = 1;
+		}
+		else
+			scr->isupamem = 0;
+	}
+	else
+		scr->isupamem = 1;
+
+	return aperture;
+}
+
+static void
+hiqvideoenable(VGAscr* scr)
+{
+	Pcidev *p;
+	int align, size, vmsize;
+	ulong aperture;
+
+	/*
+	 * Only once, can't be disabled for now.
+	 */
+	if(scr->io)
+		return;
+	if(p = pcimatch(nil, 0x102C, 0)){
+		switch(p->did){
+		case 0x00C0:		/* 69000 HiQVideo */
+			vmsize = 2*1024*1024;
+			break;
+		case 0x00E0:		/* 65550 HiQV32 */
+		case 0x00E4:		/* 65554 HiQV32 */
+		case 0x00E5:		/* 65555 HiQV32 */
+			switch((hiqvideoxi(Xrx, 0x43)>>1) & 0x03){
+			default:
+			case 0:
+				vmsize = 1*1024*1024;
+				break;
+			case 1:
+				vmsize = 2*1024*1024;
+				break;
+			}
+			break;
+		default:
+			return;
+		}
+	}
+	else
+		return;
+
+	size = p->mem[0].size;
+	align = 0;
+	aperture = hiqvideolinear(scr, &size, &align);
+	if(aperture) {
+		scr->aperture = aperture;
+		scr->apsize = size;
+		addvgaseg("hiqvideoscreen", aperture, size);
+	}
+
+	/*
+	 * Find a place for the cursor data in display memory.
+	 * Must be on a 4096-byte boundary.
+	 * scr->io holds the physical address of the cursor
+	 * storage area in the framebuffer region.
+	 */
+	scr->storage = vmsize-4096;
+	scr->io = scr->aperture+scr->storage;
+}
+
+static void
+hiqvideocurdisable(VGAscr*)
+{
+	hiqvideoxo(Xrx, 0xA0, 0x10);
+}
+
+static void
+hiqvideocurload(VGAscr* scr, Cursor* curs)
+{
+	uchar *p;
+	int x, y;
+
+	/*
+	 * Disable the cursor.
+	 */
+	hiqvideocurdisable(scr);
+
+	if(scr->io == 0)
+		return;
+	p = KADDR(scr->io);
+
+	for(y = 0; y < 16; y += 2){
+		*p++ = ~(curs->clr[2*y]|curs->set[2*y]);
+		*p++ = ~(curs->clr[2*y+1]|curs->set[2*y+1]);
+		*p++ = 0xFF;
+		*p++ = 0xFF;
+		*p++ = ~(curs->clr[2*y+2]|curs->set[2*y+2]);
+		*p++ = ~(curs->clr[2*y+3]|curs->set[2*y+3]);
+		*p++ = 0xFF;
+		*p++ = 0xFF;
+		*p++ = curs->set[2*y];
+		*p++ = curs->set[2*y+1];
+		*p++ = 0x00;
+		*p++ = 0x00;
+		*p++ = curs->set[2*y+2];
+		*p++ = curs->set[2*y+3];
+		*p++ = 0x00;
+		*p++ = 0x00;
+	}
+	while(y < 32){
+		for(x = 0; x < 64; x += 8)
+			*p++ = 0xFF;
+		for(x = 0; x < 64; x += 8)
+			*p++ = 0x00;
+		y += 2;
+	}
+
+	/*
+	 * Save the cursor hotpoint and enable the cursor.
+	 */
+	scr->offset = curs->offset;
+	hiqvideoxo(Xrx, 0xA0, 0x11);
+}
+
+static int
+hiqvideocurmove(VGAscr* scr, Point p)
+{
+	int x, y;
+
+	if(scr->io == 0)
+		return 1;
+
+	if((x = p.x+scr->offset.x) < 0)
+		x = 0x8000|(-x & 0x07FF);
+	if((y = p.y+scr->offset.y) < 0)
+		y = 0x8000|(-y & 0x07FF);
+
+	hiqvideoxo(Xrx, 0xA4, x & 0xFF);
+	hiqvideoxo(Xrx, 0xA5, (x>>8) & 0xFF);
+	hiqvideoxo(Xrx, 0xA6, y & 0xFF);
+	hiqvideoxo(Xrx, 0xA7, (y>>8) & 0xFF);
+
+	return 0;
+}
+
+static void
+hiqvideocurenable(VGAscr* scr)
+{
+	uchar xr80;
+
+	hiqvideoenable(scr);
+	if(scr->io == 0)
+		return;
+
+	/*
+	 * Disable the cursor.
+	 */
+	hiqvideocurdisable(scr);
+
+	/*
+	 * Cursor colours.
+	 * Can't call setcolor here as cursor is already locked.
+	 * When done make sure the cursor enable in Xr80 is set.
+	 */
+	xr80 = hiqvideoxi(Xrx, 0x80);
+	hiqvideoxo(Xrx, 0x80, xr80|0x01);
+	vgao(PaddrW, 0x04);
+	vgao(Pdata, Pwhite);
+	vgao(Pdata, Pwhite);
+	vgao(Pdata, Pwhite);
+	vgao(Pdata, Pblack);
+	vgao(Pdata, Pblack);
+	vgao(Pdata, Pblack);
+	hiqvideoxo(Xrx, 0x80, xr80|0x10);
+
+	hiqvideoxo(Xrx, 0xA2, (scr->storage>>12)<<4);
+	hiqvideoxo(Xrx, 0xA3, (scr->storage>>16) & 0x3F);
+
+	/*
+	 * Load, locate and enable the 32x32 cursor.
+	 * Cursor enable in Xr80 better be set already.
+	 */
+	hiqvideocurload(scr, &arrow);
+	hiqvideocurmove(scr, ZP);
+	hiqvideoxo(Xrx, 0xA0, 0x11);
+}
+
+VGAdev vgahiqvideodev = {
+	"hiqvideo",
+
+	hiqvideoenable,			/* enable */
+	nil,				/* disable */
+	nil,				/* page */
+	hiqvideolinear,			/* linear */
+};
+
+VGAcur vgahiqvideocur = {
+	"hiqvideohwgc",
+
+	hiqvideocurenable,		/* enable */
+	hiqvideocurdisable,		/* disable */
+	hiqvideocurload,		/* load */
+	hiqvideocurmove,		/* move */
+};
--- /dev/null
+++ b/os/pc/vgai81x.c
@@ -1,0 +1,282 @@
+#include "u.h"
+#include "../port/lib.h"
+#include "mem.h"
+#include "dat.h"
+#include "fns.h"
+#include "io.h"
+#include "../port/error.h"
+
+#define	Image	IMAGE
+#include <draw.h>
+#include <memdraw.h>
+#include <cursor.h>
+#include "screen.h"
+
+typedef struct
+{
+	ushort	ctl;
+	ushort	pad;
+	ulong	base;
+	ulong	pos;
+} CursorI81x;
+
+enum {
+	Fbsize		= 8*MB,
+
+	hwCur		= 0x70080,
+};
+
+static Pcidev *
+i81xpcimatch(void)
+{
+	Pcidev *p;
+
+	p = nil;
+	while((p = pcimatch(p, 0x8086, 0)) != nil){
+		switch(p->did){
+		default:
+			continue;
+		case 0x7121:
+		case 0x7123:
+		case 0x7125:
+		case 0x1102:
+		case 0x1112:
+		case 0x1132:
+		case 0x3577:	/* IBM R31 uses intel 830M chipset */
+			return p;
+		}
+	}
+	return nil;
+}
+
+static ulong
+i81xlinear(VGAscr* scr, int* size, int* align)
+{
+	Pcidev *p;
+	int oapsize, wasupamem;
+	ulong aperture, oaperture, fbuf, fbend, *rp;
+
+	oaperture = scr->aperture;
+	oapsize = scr->apsize;
+	wasupamem = scr->isupamem;
+
+	aperture = 0;
+	p = i81xpcimatch();
+	if(p != nil) {
+		aperture = p->mem[0].bar & ~0x0F;
+		*size = p->mem[0].size;
+		if(*size > Fbsize)
+			*size = Fbsize;
+	}
+
+	if(wasupamem){
+		if(oaperture == aperture)
+			return oaperture;
+		upafree(oaperture, oapsize);
+	}
+	scr->isupamem = 0;
+
+	aperture = upamalloc(aperture, *size, *align);
+	if(aperture == 0){
+		if(wasupamem && upamalloc(oaperture, oapsize, 0)){
+			aperture = oaperture;
+			scr->isupamem = 1;
+		}
+		else
+			scr->isupamem = 0;
+	}
+	else
+		scr->isupamem = 1;
+
+	/* allocate space for frame buffer, populate page table */
+	if(oapsize == 0) {
+		fbuf = PADDR(xspanalloc(*size, BY2PG, 0));
+		fbend = PGROUND(fbuf+*size);
+		rp = KADDR(scr->io+0x10000);
+		while(fbuf < fbend) {
+			*rp++ = fbuf | (1<<0);
+			fbuf += BY2PG;
+		}
+	}
+	return aperture;
+}
+
+static void
+i81xenable(VGAscr* scr)
+{
+	Pcidev *p;
+	int align, size;
+	Mach *mach0;
+	ulong aperture, pgtbl, *rp, cursor, *pte;
+
+	/*
+	 * Only once, can't be disabled for now.
+	 * scr->io holds the physical address of
+	 * the MMIO registers.
+	 */
+	if(scr->io)
+		return;
+	p = i81xpcimatch();
+	if(p == nil)
+		return;
+	scr->io = upamalloc(p->mem[1].bar & ~0x0F, p->mem[1].size, 0);
+	if(scr->io == 0)
+		return;
+
+	/* allocate page table */
+	pgtbl = PADDR(xspanalloc(64*1024, BY2PG, 0));
+	rp = KADDR(scr->io+0x2020);
+	*rp = pgtbl | 1;
+
+	addvgaseg("i81xmmio", (ulong)scr->io, p->mem[0].size);
+
+	size = p->mem[0].size;
+	align = 0;
+	aperture = i81xlinear(scr, &size, &align);
+	if(aperture){
+		scr->aperture = aperture;
+		scr->apsize = size;
+		addvgaseg("i81xscreen", aperture, size);
+	}
+
+	/*
+	 * allocate space for the cursor data in system memory.
+	 * must be uncached.
+	 */
+	cursor = (ulong)xspanalloc(BY2PG, BY2PG, 0);
+	mach0 = MACHP(0);
+	pte = mmuwalk(mach0->pdb, cursor, 2, 0);
+	if(pte == nil)
+		panic("i81x cursor");
+	*pte |= PTEUNCACHED;
+	scr->storage = PADDR(cursor);
+}
+
+static void
+i81xcurdisable(VGAscr* scr)
+{
+	CursorI81x *hwcurs;
+
+	if(scr->io == 0)
+		return;
+	hwcurs = KADDR(scr->io+hwCur);
+	hwcurs->ctl = (1<<4);
+}
+
+static void
+i81xcurload(VGAscr* scr, Cursor* curs)
+{
+	int y;
+	uchar *p;
+	CursorI81x *hwcurs;
+
+	if(scr->io == 0)
+		return;
+	hwcurs = KADDR(scr->io+hwCur);
+
+	/*
+	 * Disable the cursor then load the new image in
+	 * the top-left of the 32x32 array.
+	 * Unused portions of the image have been initialised to be
+	 * transparent.
+	 */
+	hwcurs->ctl = (1<<4);
+	p = KADDR(scr->storage);
+	for(y = 0; y < 16; y += 2) {
+		*p++ = ~(curs->clr[2*y]|curs->set[2*y]);
+		*p++ = ~(curs->clr[2*y+1]|curs->set[2*y+1]);
+		p += 2;
+		*p++ = ~(curs->clr[2*y+2]|curs->set[2*y+2]);
+		*p++ = ~(curs->clr[2*y+3]|curs->set[2*y+3]);
+		p += 2;
+		*p++ = curs->set[2*y];
+		*p++ = curs->set[2*y+1];
+		p += 2;
+		*p++ = curs->set[2*y+2];
+		*p++ = curs->set[2*y+3];
+		p += 2;
+	}
+
+	/*
+	 * Save the cursor hotpoint and enable the cursor.
+	 * The 0,0 cursor point is top-left.
+	 */
+	scr->offset.x = curs->offset.x;
+	scr->offset.y = curs->offset.y;
+	hwcurs->ctl = (1<<4)|1;
+}
+
+static int
+i81xcurmove(VGAscr* scr, Point p)
+{
+	int x, y;
+	ulong pos;
+	CursorI81x *hwcurs;
+
+	if(scr->io == 0)
+		return 1;
+	hwcurs = KADDR(scr->io+hwCur);
+
+	x = p.x+scr->offset.x;
+	y = p.y+scr->offset.y;
+	pos = 0;
+	if(x < 0) {
+		pos |= (1<<15);
+		x = -x;
+	}
+	if(y < 0) {
+		pos |= (1<<31);
+		y = -y;
+	}
+	pos |= ((y&0x7ff)<<16)|(x&0x7ff);
+	hwcurs->pos = pos;
+
+	return 0;
+}
+
+static void
+i81xcurenable(VGAscr* scr)
+{
+	int i;
+	uchar *p;
+	CursorI81x *hwcurs;
+
+	i81xenable(scr);
+	if(scr->io == 0)
+		return;
+	hwcurs = KADDR(scr->io+hwCur);
+
+	/*
+	 * Initialise the 32x32 cursor to be transparent in 2bpp mode.
+	 */
+	hwcurs->base = scr->storage;
+	p = KADDR(scr->storage);
+	for(i = 0; i < 32/2; i++) {
+		memset(p, 0xff, 8);
+		memset(p+8, 0, 8);
+		p += 16;
+	}
+	/*
+	 * Load, locate and enable the 32x32 cursor in 2bpp mode.
+	 */
+	i81xcurload(scr, &arrow);
+	i81xcurmove(scr, ZP);
+}
+
+VGAdev vgai81xdev = {
+	"i81x",
+
+	i81xenable,
+	nil,
+	nil,
+	i81xlinear,
+};
+
+VGAcur vgai81xcur = {
+	"i81xhwgc",
+
+	i81xcurenable,
+	i81xcurdisable,
+	i81xcurload,
+	i81xcurmove,
+};
--- /dev/null
+++ b/os/pc/vgamach64xx.c
@@ -1,0 +1,1250 @@
+#include "u.h"
+#include "../port/lib.h"
+#include "mem.h"
+#include "dat.h"
+#include "fns.h"
+#include "io.h"
+#include "../port/error.h"
+
+#define	Image	IMAGE
+#include <draw.h>
+#include <memdraw.h>
+#include <cursor.h>
+#include "screen.h"
+
+char Eunsupportedformat[] = "unsupported video format";
+char Enotconfigured[] = "device not configured";
+
+#define SCALE_ZERO_EXTEND           	0x0
+#define SCALE_DYNAMIC               		0x1
+#define SCALE_RED_TEMP_6500K        	0x0
+#define SCALE_RED_TEMP_9800K        	0x2
+#define SCALE_HORZ_BLEND            	0x0
+#define SCALE_HORZ_REP              		0x4
+#define SCALE_VERT_BLEND            		0x0
+#define SCALE_VERT_REP              		0x8
+#define SCALE_BANDWIDTH_NORMAL     0x0
+#define SCALE_BANDWIDTH_EXCEEDED  0x4000000
+#define SCALE_BANDWIDTH_RESET       	0x4000000
+#define SCALE_CLK_ACTIVITY          	0x0
+#define SCALE_CLK_CONTINUOUS        	0x20000000
+#define OVERLAY_DISABLE             		0x0
+#define OVERLAY_ENABLE              		0x40000000
+#define SCALE_DISABLE               		0x0
+#define SCALE_ENABLE                		0x80000000
+
+#define SCALER_FRAME_READ_MODE_FULL 	0x0
+#define SCALER_BUF_MODE_SINGLE      		0x0
+#define SCALER_BUF_MODE_DOUBLE      		0x40000
+#define SCALER_BUF_NEXT_0           		0x0
+#define SCALER_BUF_NEXT_1           		0x80000
+#define SCALER_BUF_STATUS_0         		0x0
+#define SCALER_BUF_STATUS_1         		0x100000
+
+#define OVERLAY_MIX_G_CMP           		0x0
+#define OVERLAY_MIX_ALWAYS_G        		0x100
+#define OVERLAY_MIX_ALWAYS_V        		0x200
+#define OVERLAY_MIX_NOT_G           		0x300
+#define OVERLAY_MIX_NOT_V           		0x400
+#define OVERLAY_MIX_G_XOR_V         		0x500
+#define OVERLAY_MIX_NOT_G_XOR_V     	0x600
+#define OVERLAY_MIX_V_CMP           		0x700
+#define OVERLAY_MIX_NOT_G_OR_NOT_V	0x800
+#define OVERLAY_MIX_G_OR_NOT_V      	0x900
+#define OVERLAY_MIX_NOT_G_OR_V      	0xA00
+#define OVERLAY_MIX_G_OR_V          		0xB00
+#define OVERLAY_MIX_G_AND_V         		0xC00
+#define OVERLAY_MIX_NOT_G_AND_V     	0xD00
+#define OVERLAY_MIX_G_AND_NOT_V     	0xE00
+#define OVERLAY_MIX_NOT_G_AND_NOT_V 	0xF00
+#define OVERLAY_EXCLUSIVE_NORMAL    	0x0
+#define OVERLAY_EXCLUSIVE_V_ONLY    	0x80000000
+
+#define VIDEO_IN_8BPP               			0x2
+#define VIDEO_IN_16BPP              			0x4
+#define VIDEO_IN_32BPP              			0x6
+#define VIDEO_IN_VYUY422            			0xB         		/*16 bpp */
+#define VIDEO_IN_YVYU422            			0xC         		/* 16 bpp */
+#define SCALE_IN_15BPP              			0x30000     	/* aRGB 1555 */
+#define SCALE_IN_16BPP              			0x40000     	/* RGB 565 */
+#define SCALE_IN_32BPP              			0x60000     	/* aRGB 8888 */
+#define SCALE_IN_YUV9               			0x90000     	/* planar */
+#define SCALE_IN_YUV12              			0xA0000     	/* planar */
+#define SCALE_IN_VYUY422            			0xB0000     	/* 16 bpp */
+#define SCALE_IN_YVYU422            			0xC0000     	/* 16 bpp */
+#define HOST_YUV_APERTURE_UPPER     		0x0
+#define HOST_YUV_APERTURE_LOWER     	0x20000000
+#define HOST_MEM_MODE_Y             		0x40000000
+#define HOST_MEM_MODE_U             		0x80000000
+#define HOST_MEM_MODE_V             		0xC0000000
+#define HOST_MEM_MODE_NORMAL     		HOST_YUV_APERTURE_UPPER 
+
+static Chan *ovl_chan;	/* Channel of controlling process */
+static int ovl_width;		/* Width of input overlay buffer */
+static int ovl_height;		/* Height of input overlay buffer */
+static int ovl_format;	/* Overlay format */
+static ulong ovl_fib;		/* Frame in bytes */
+
+enum {
+	 VTGTB1S1        = 0x01,            //  Asic description for VTB1S1 and GTB1S1.
+	 VT4GTIIC        	= 0x3A,            // asic descr for VT4 and RAGE IIC
+	 GTB1U1          	= 0x19,            //  Asic description for GTB1U1.
+	 GTB1S2          	= 0x41,            //  Asic description for GTB1S2.
+	 GTB2U1          	= 0x1A,
+	 GTB2U2          	= 0x5A,
+	 GTB2U3          	= 0x9A,
+	 GTIIIC1U1       	= 0x1B,            // 3D RAGE PRO asic descrp.
+	 GTIIIC1U2       	= 0x5B,            // 3D RAGE PRO asic descrp.
+	 GTIIIC2U1       	= 0x1C,            // 3D RAGE PRO asic descrp.
+	 GTIIIC2U2       	= 0x5C,           // 3D RAGE PRO asic descrp.
+	 GTIIIC2U3       	= 0x7C,            // 3D RAGE PRO asic descrp.
+	 GTBC            	= 0x3A,            // 3D RAGE IIC asic descrp.
+	 LTPRO           	= 0x9C,            // 3D RAGE LT PRO
+};
+
+/*
+ * ATI Mach64(CT|ET|G*|V*|L*).
+ */
+typedef struct Mach64types Mach64types;
+struct Mach64types {
+	ushort 	m64_id;			/* Chip ID */
+	int 		m64_vtgt;		/* Is this a VT or GT chipset? */
+	ulong	m64_ovlclock;		/* Max. overlay clock frequency */
+	int		m64_pro;			/* Is this a PRO? */
+};
+
+static ulong mach64refclock;
+static Mach64types *mach64type;
+static int mach64revb;			/* Revision B or greater? */
+static ulong mach64overlay;		/* Overlay buffer */
+
+static Mach64types mach64s[] = {
+	('C'<<8)|'T',	0,	1350000, /*?*/	0,	/* 4354: CT */
+	('E'<<8)|'T',	0,	1350000, /*?*/	0,	/* 4554: ET */
+	('G'<<8)|'B',	1,	1250000,		1, 	/* 4742: 264GT PRO */
+	('G'<<8)|'D',	1,	1250000,		1, 	/* 4744: 264GT PRO */
+	('G'<<8)|'I',	1,	1250000,		1, 	/* 4749: 264GT PRO */
+	('G'<<8)|'M',	0,	1350000,		0,	/* 474D: Rage XL */
+	('G'<<8)|'P',	1,	1250000,		1, 	/* 4750: 264GT PRO */
+	('G'<<8)|'Q',	1,	1250000,		1,	/* 4751: 264GT PRO */
+	('G'<<8)|'R',	1,	1250000,		1,	/* 4752: */
+	('G'<<8)|'T',	1,	800000,		0,	/* 4754: 264GT[B] */
+	('G'<<8)|'U',	1,	1000000,		0,	/* 4755: 264GT DVD */
+	('G'<<8)|'V',	1,	1000000,		0,	/* 4756: Rage2C */
+	('G'<<8)|'Z',	1,	1000000,		0,	/* 475A: Rage2C */
+	('V'<<8)|'T',	1,	800000,		0,	/* 5654: 264VT/GT/VTB */
+	('V'<<8)|'U',	1,	800000,		0,	/* 5655: 264VT3 */
+	('V'<<8)|'V',	1,	1000000,		0,	/* 5656: 264VT4 */
+	('L'<<8)|'B',	0,	1350000,		1,	/* 4C42: Rage LTPro AGP */
+	('L'<<8)|'I',		0,	1350000,		0,	/* 4C49: Rage LTPro AGP */
+	('L'<<8)|'M',	0,	1350000,		0,	/* 4C4D: Rage Mobility */
+	('L'<<8)|'P',	0,	1350000,		1,	/* 4C50: 264LT PRO */
+};
+
+
+static int hwfill(VGAscr*, Rectangle, ulong);
+static int hwscroll(VGAscr*, Rectangle, Rectangle);
+static void initengine(VGAscr*);
+
+static Pcidev*
+mach64xxpci(void)
+{
+	Pcidev *p;
+	int i;
+
+	if((p = pcimatch(nil, 0x1002, 0)) == nil)
+		return nil;
+
+	for (i = 0; i != nelem(mach64s); i++)
+		if (mach64s[i].m64_id == p->did) {
+			mach64type = &mach64s[i];
+			return p;
+		}
+	return nil;
+}
+
+static void
+mach64xxenable(VGAscr* scr)
+{
+	Pcidev *p;
+
+	/*
+	 * Only once, can't be disabled for now.
+	 */
+	if(scr->io)
+		return;
+	if(p = mach64xxpci()){
+		scr->id = p->did;
+
+		/*
+		 * The CT doesn't always have the I/O base address
+		 * in the PCI base registers. There is a way to find
+		 * it via the vendor-specific PCI config space but
+		 * this will do for now.
+		 */
+		scr->io = p->mem[1].bar & ~0x03;
+
+		if(scr->io == 0)
+			scr->io = 0x2EC;
+	}
+}
+
+static ulong
+mach64xxlinear(VGAscr* scr, int* size, int* align)
+{
+	ulong aperture, osize, oaperture;
+	int i, oapsize, wasupamem;
+	Pcidev *p;
+
+	osize = *size;
+	oaperture = scr->aperture;
+	oapsize = scr->apsize;
+	wasupamem = scr->isupamem;
+
+	if(p = mach64xxpci()){
+		for(i=0; i<nelem(p->mem); i++){
+			if(p->mem[i].size >= *size
+			&& ((p->mem[i].bar & ~0x0F) & (*align-1)) == 0)
+				break;
+		}
+		if(i >= nelem(p->mem)){
+			print("vgamach64xx: aperture not found\n");
+			return 0;
+		}
+		aperture = p->mem[i].bar & ~0x0F;
+		*size = p->mem[i].size;
+	}
+	else
+		aperture = 0;
+
+	if(wasupamem)
+		upafree(oaperture, oapsize);
+	scr->isupamem = 0;
+
+	aperture = upamalloc(aperture, *size, *align);
+	if(aperture == 0){
+		if(wasupamem && upamalloc(oaperture, oapsize, 0))
+			scr->isupamem = 1;
+	}
+	else
+		scr->isupamem = 1;
+
+	scr->mmio = KADDR(aperture+osize-0x400);
+	if(oaperture && oaperture != aperture)
+		print("warning (BUG): redefinition of aperture does not change mach64mmio segment\n");
+	addvgaseg("mach64mmio", aperture+osize-BY2PG, BY2PG);
+	addvgaseg("mach64screen", aperture, osize);
+
+	return aperture;
+}
+
+enum {
+	CrtcOffPitch	= 0x05,
+	CrtcGenCtl	= 0x07,
+	CurClr0		= 0x0B,		/* I/O Select */
+	CurClr1		= 0x0C,
+	CurOffset	= 0x0D,
+	CurHVposn	= 0x0E,
+	CurHVoff	= 0x0F,
+	BusCntl	= 0x13,
+	GenTestCntl	= 0x19,
+
+	CrtcHsyncDis	= 0x04,
+	CrtcVsyncDis	= 0x08,
+
+	ContextMask	= 0x100,	/* not accessible via I/O */
+	FifoStat,
+	GuiStat,
+	DpFrgdClr,
+	DpBkgdClr,
+	DpWriteMask,
+	DpMix,
+	DpPixWidth,
+	DpSrc,
+	ClrCmpCntl,
+	GuiTrajCntl,
+	ScLeftRight,
+	ScTopBottom,
+	DstOffPitch,
+	DstYX,
+	DstHeightWidth,
+	DstCntl,
+	DstHeight,
+	DstBresErr,
+	DstBresInc,
+	DstBresDec,
+	SrcCntl,
+	SrcHeight1Width1,
+	SrcHeight2Width2,
+	SrcYX,
+	SrcWidth1,
+	SrcYXstart,
+	HostCntl,
+	PatReg0,
+	PatReg1,
+	PatCntl,
+	ScBottom,
+	ScLeft,
+	ScRight,
+	ScTop,
+	ClrCmpClr,
+	ClrCmpMask,
+	DpChainMask,
+	SrcOffPitch,	
+	LcdIndex,
+	LcdData,
+	ClockCntl,
+	OverlayScaleCntl,
+	ConfigChipId,
+	Buf0Pitch,
+	ScalerBuf0Pitch,
+	CaptureConfig,
+	OverlayKeyCntl,
+	ScalerColourCntl,
+	ScalerHCoef0,
+	ScalerHCoef1,
+	ScalerHCoef2,
+	ScalerHCoef3,
+	ScalerHCoef4,
+	VideoFormat,
+	Buf0Offset,
+	ScalerBuf0Offset,
+	CrtcGenCntl,
+	OverlayScaleInc,
+	OverlayYX,
+	OverlayYXEnd,
+	ScalerHeightWidth,
+	HTotalDisp,
+	VTotalDisp,
+};
+
+enum {
+	LCD_ConfigPanel = 0,
+	LCD_GenCtrl,
+	LCD_DstnCntl,
+	LCD_HfbPitchAddr,
+	LCD_HorzStretch,
+	LCD_VertStretch,
+	LCD_ExtVertStretch,
+	LCD_LtGio,
+	LCD_PowerMngmnt,
+	LCD_ZvgPio,
+	Nlcd,
+};
+
+#define Bank1			(-0x100)		/* 1KB */
+
+static int mmoffset[] = {
+	[HTotalDisp]		0x00,
+	[VTotalDisp]		0x02,
+	[CrtcOffPitch]		0x05,
+	[CrtcGenCntl]		0x07,
+	[CurClr0]			0x18,
+	[CurClr1]			0x19,
+	[CurOffset]		0x1A,
+	[CurHVposn]		0x1B,
+	[CurHVoff]		0x1C,
+	[ClockCntl]		0x24,
+	[BusCntl]			0x28,
+	[LcdIndex]		0x29,
+	[LcdData]			0x2A,
+	[GenTestCntl]		0x34,
+	[ConfigChipId]		0x38,
+	[DstOffPitch]		0x40,
+	[DstYX]			0x43,
+	[DstHeight]		0x45,
+	[DstHeightWidth]	0x46,
+	[DstBresErr]		0x49,
+	[DstBresInc]		0x4A,
+	[DstBresDec]		0x4B,
+	[DstCntl]			0x4C,
+	[SrcOffPitch]		0x60,
+	[SrcYX]			0x63,
+	[SrcWidth1]		0x64,
+	[SrcYXstart]		0x69,
+	[SrcHeight1Width1]	0x66,
+	[SrcHeight2Width2]	0x6C,
+	[SrcCntl]			0x6D,
+	[HostCntl]			0x90,
+	[PatReg0]			0xA0,
+	[PatReg1]			0xA1,
+	[PatCntl]			0xA2,
+	[ScLeft]			0xA8,
+	[ScRight]			0xA9,
+	[ScLeftRight]		0xAA,
+	[ScTop]			0xAB,
+	[ScBottom] 		0xAC,
+	[ScTopBottom]		0xAD,
+	[DpBkgdClr]		0xB0,
+	[DpFrgdClr]		0xB1,
+	[DpWriteMask]		0xB2,
+	[DpChainMask]		0xB3,
+	[DpPixWidth]		0xB4,
+	[DpMix]			0xB5,
+	[DpSrc]			0xB6,
+	[ClrCmpClr]		0xC0,
+	[ClrCmpMask]		0xC1,
+	[ClrCmpCntl]		0xC2,
+	[FifoStat]			0xC4,
+	[ContextMask]		0xC8,
+	[GuiTrajCntl]		0xCC,
+	[GuiStat]			0xCE,
+
+	/* Bank1 */
+	[OverlayYX]		Bank1 + 0x00,
+	[OverlayYXEnd]		Bank1 + 0x01,
+	[OverlayKeyCntl]	Bank1 + 0x06,
+	[OverlayScaleInc]	Bank1 + 0x08,
+	[OverlayScaleCntl]	Bank1 + 0x09,
+	[ScalerHeightWidth]	Bank1 + 0x0A,
+	[ScalerBuf0Offset]	Bank1 + 0x0D,
+	[ScalerBuf0Pitch]	Bank1 + 0x0F,
+	[VideoFormat]		Bank1 + 0x12,
+	[CaptureConfig]	Bank1 + 0x14,
+	[Buf0Offset]		Bank1 + 0x20,
+	[Buf0Pitch]		Bank1 + 0x23,
+	[ScalerColourCntl]	Bank1 + 0x54,
+	[ScalerHCoef0]		Bank1 + 0x55,
+	[ScalerHCoef1]		Bank1 + 0x56,
+	[ScalerHCoef2]		Bank1 + 0x57,
+	[ScalerHCoef3]		Bank1 + 0x58,
+	[ScalerHCoef4]		Bank1 + 0x59,
+};
+
+static ulong
+ior32(VGAscr* scr, int r)
+{
+	if(scr->io == 0x2EC || scr->io == 0x1C8)
+		return inl((r<<10)+scr->io);
+	if(r >= 0x100 && scr->mmio != nil)
+		return scr->mmio[mmoffset[r]];
+	return inl((mmoffset[r]<<2)+scr->io);
+}
+
+static void
+iow32(VGAscr* scr, int r, ulong l)
+{
+	if(scr->io == 0x2EC || scr->io == 0x1C8)
+		outl(((r)<<10)+scr->io, l);
+	else if(r >= 0x100 && scr->mmio != nil)
+		scr->mmio[mmoffset[r]] = l;
+	else
+		outl((mmoffset[r]<<2)+scr->io, l);
+}
+
+static ulong
+lcdr32(VGAscr *scr, ulong r)
+{
+	ulong or;
+
+	or = ior32(scr, LcdIndex);
+	iow32(scr, LcdIndex, (or&~0x0F) | (r&0x0F));
+	return ior32(scr, LcdData);
+}
+
+static void
+lcdw32(VGAscr *scr, ulong r, ulong v)
+{
+	ulong or;
+
+	or = ior32(scr, LcdIndex);
+	iow32(scr, LcdIndex, (or&~0x0F) | (r&0x0F));
+	iow32(scr, LcdData, v);
+}
+
+static void
+mach64xxcurdisable(VGAscr* scr)
+{
+	ulong r;
+
+	r = ior32(scr, GenTestCntl);
+	iow32(scr, GenTestCntl, r & ~0x80);
+}
+
+static void
+mach64xxcurload(VGAscr* scr, Cursor* curs)
+{
+	uchar *p;
+	int i, y;
+	ulong c, s, m, r;
+
+	/*
+	 * Disable the cursor.
+	 */
+	r = ior32(scr, GenTestCntl);
+	iow32(scr, GenTestCntl, r & ~0x80);
+
+	p = KADDR(scr->aperture);
+	p += scr->storage;
+
+	/*
+	 * Initialise the 64x64 cursor RAM array.
+	 * The cursor mode gives the following truth table:
+	 *	p1 p0	colour
+	 *	 0  0	Cursor Colour 0
+	 *	 0  1	Cursor Colour 1
+	 *	 1  0	Transparent
+	 *	 1  1	Complement
+	 * Put the cursor into the top-right of the 64x64 array.
+	 */
+	for(y = 0; y < 16; y++){
+		for(i = 0; i < (64-16)/8; i++){
+			*p++ = 0xAA;
+			*p++ = 0xAA;
+		}
+
+		c = (curs->clr[2*y]<<8)|curs->clr[y*2 + 1];
+		s = (curs->set[2*y]<<8)|curs->set[y*2 + 1];
+
+		m = 0x00000000;
+		for(i = 0; i < 16; i++){
+			if(s & (1<<(15-i)))
+				m |= 0x01<<(2*i);
+			else if(c & (1<<(15-i))){
+				/* nothing to do */
+			}
+			else
+				m |= 0x02<<(2*i);
+		}
+		*p++ = m;
+		*p++ = m>>8;
+		*p++ = m>>16;
+		*p++ = m>>24;
+	}
+	memset(p, 0xAA, (64-16)*16);
+
+	/*
+	 * Set the cursor hotpoint and enable the cursor.
+	 */
+	scr->offset = curs->offset;
+	iow32(scr, GenTestCntl, 0x80|r);
+}
+
+static int
+ptalmostinrect(Point p, Rectangle r)
+{
+	return p.x>=r.min.x && p.x<=r.max.x &&
+	       p.y>=r.min.y && p.y<=r.max.y;
+}
+
+/*
+ * If necessary, translate the rectangle physr
+ * some multiple of [dx dy] so that it includes p.
+ * Return 1 if the rectangle changed.
+ */
+static int
+screenpan(Point p, Rectangle *physr, int dx, int dy)
+{
+	int d;
+
+	if(ptalmostinrect(p, *physr))
+		return 0;
+
+	if(p.y < physr->min.y){
+		d = physr->min.y - (p.y&~(dy-1));
+		physr->min.y -= d;
+		physr->max.y -= d;
+	}
+	if(p.y > physr->max.y){
+		d = ((p.y+dy-1)&~(dy-1)) - physr->max.y;
+		physr->min.y += d;
+		physr->max.y += d;
+	}
+
+	if(p.x < physr->min.x){
+		d = physr->min.x - (p.x&~(dx-1));
+		physr->min.x -= d;
+		physr->max.x -= d;
+	}
+	if(p.x > physr->max.x){
+		d = ((p.x+dx-1)&~(dx-1)) - physr->max.x;
+		physr->min.x += d;
+		physr->max.x += d;
+	}
+	return 1;
+}
+
+static int
+mach64xxcurmove(VGAscr* scr, Point p)
+{
+	int x, xo, y, yo;
+	int dx;
+	ulong off, pitch;
+
+	/*
+	 * If the point we want to display is outside the current
+	 * screen rectangle, pan the screen to display it.
+	 *
+	 * We have to move in 64-bit chunks.
+	 */
+	if(scr->gscreen->depth == 24)
+		dx = (64*3)/24;
+	else
+		dx = 64 / scr->gscreen->depth;
+
+	if(panning && screenpan(p, &physgscreenr, dx, 1)){
+		off = (physgscreenr.min.y*Dx(scr->gscreen->r)+physgscreenr.min.x)/dx;
+		pitch = Dx(scr->gscreen->r)/8;
+		iow32(scr, CrtcOffPitch, (pitch<<22)|off);
+	}
+
+	p.x -= physgscreenr.min.x;
+	p.y -= physgscreenr.min.y;
+
+	/*
+	 * Mustn't position the cursor offscreen even partially,
+	 * or it disappears. Therefore, if x or y is -ve, adjust the
+	 * cursor presets instead. If y is negative also have to
+	 * adjust the starting offset.
+	 */
+	if((x = p.x+scr->offset.x) < 0){
+		xo = x;
+		x = 0;
+	}
+	else
+		xo = 0;
+	if((y = p.y+scr->offset.y) < 0){
+		yo = y;
+		y = 0;
+	}
+	else
+		yo = 0;
+
+	iow32(scr, CurHVoff, ((64-16-yo)<<16)|(64-16-xo));
+	iow32(scr, CurOffset, scr->storage/8 + (-yo*2));
+	iow32(scr, CurHVposn, (y<<16)|x);
+
+	return 0;
+}
+
+static void
+mach64xxcurenable(VGAscr* scr)
+{
+	ulong r, storage;
+
+	mach64xxenable(scr);
+	if(scr->io == 0)
+		return;
+
+	r = ior32(scr, GenTestCntl);
+	iow32(scr, GenTestCntl, r & ~0x80);
+
+	iow32(scr, CurClr0, (Pwhite<<24)|(Pwhite<<16)|(Pwhite<<8)|Pwhite);
+	iow32(scr, CurClr1, (Pblack<<24)|(Pblack<<16)|(Pblack<<8)|Pblack);
+
+	/*
+	 * Find a place for the cursor data in display memory.
+	 * Must be 64-bit aligned.
+	 */
+	storage = (scr->gscreen->width*BY2WD*scr->gscreen->r.max.y+7)/8;
+	iow32(scr, CurOffset, storage);
+	scr->storage = storage*8;
+
+	/*
+	 * Cursor goes in the top right corner of the 64x64 array
+	 * so the horizontal and vertical presets are 64-16.
+	 */
+	iow32(scr, CurHVposn, (0<<16)|0);
+	iow32(scr, CurHVoff, ((64-16)<<16)|(64-16));
+
+	/*
+	 * Load, locate and enable the 64x64 cursor.
+	 */
+	mach64xxcurload(scr, &arrow);
+	mach64xxcurmove(scr, ZP);
+	iow32(scr, GenTestCntl, 0x80|r);
+}
+
+static void
+waitforfifo(VGAscr *scr, int entries)
+{
+	int x;
+
+	x = 0;
+	while((ior32(scr, FifoStat)&0xFF) > (0x8000>>entries) && x++ < 1000000)
+		;
+	if(x >= 1000000)
+		iprint("fifo %d stat %.8lux %.8lux scrio %.8lux mmio %p scr %p pc %luX\n", entries, ior32(scr, FifoStat), scr->mmio[mmoffset[FifoStat]], scr->io, scr->mmio, scr, getcallerpc(&scr));
+}
+
+static void
+waitforidle(VGAscr *scr)
+{
+	int x;
+
+	waitforfifo(scr, 16);
+	x = 0;
+	while((ior32(scr, GuiStat)&1) && x++ < 1000000)
+		;
+	if(x >= 1000000)
+		iprint("idle stat %.8lux %.8lux scrio %.8lux mmio %p scr %p pc %luX\n", ior32(scr, GuiStat), scr->mmio[mmoffset[GuiStat]], scr->io, scr->mmio, scr, getcallerpc(&scr));
+}
+
+static void
+resetengine(VGAscr *scr)
+{
+	ulong x;
+	x = ior32(scr, GenTestCntl);
+	iow32(scr, GenTestCntl, x&~0x100);
+	iow32(scr, GenTestCntl, x|0x100);
+	iow32(scr, BusCntl, ior32(scr, BusCntl)|0x00A00000);
+}
+
+static void
+init_overlayclock(VGAscr *scr)
+{
+	uchar *cc, save, pll_ref_div, pll_vclk_cntl, vclk_post_div, 
+			vclk_fb_div, ecp_div;
+	int i;
+	ulong dotclock;
+
+	/* Taken from GLX */
+	/* Get monitor dotclock, check for Overlay Scaler clock limit */
+ 	cc = (uchar *)&scr->mmio[mmoffset[ClockCntl]];
+  	save = cc[1]; i = cc[0] & 3;
+  	cc[1] = 2<<2; pll_ref_div = cc[2];
+  	cc[1] = 5<<2; pll_vclk_cntl = cc[2];
+  	cc[1] = 6<<2; vclk_post_div = (cc[2]>>(i+i)) & 3;
+  	cc[1] = (7+i)<<2; vclk_fb_div = cc[2];
+
+	dotclock = 2 * mach64refclock * vclk_fb_div / 
+			(pll_ref_div * (1 << vclk_post_div));
+	/* ecp_div: 0=dotclock, 1=dotclock/2, 2=dotclock/4 */
+  	ecp_div = dotclock / mach64type->m64_ovlclock;
+  	if (ecp_div>2) ecp_div = 2;
+
+  	/* Force a scaler clock factor of 1 if refclock *
+   	  * is unknown (VCLK_SRC not PLLVCLK)  */
+  	if ((pll_vclk_cntl & 0x03) != 0x03) 
+		ecp_div = 0;
+  	if ((pll_vclk_cntl & 0x30) != ecp_div<<4) {
+    		cc[1] = (5<<2)|2;
+    		cc[2] = (pll_vclk_cntl&0xCF) | (ecp_div<<4);
+	}
+
+  	/* Restore PLL Register Index */
+  	cc[1] = save;
+}
+
+static void
+initengine(VGAscr *scr)
+{
+	ulong pitch;
+	uchar *bios;
+	ushort table;
+
+	pitch = Dx(scr->gscreen->r)/8;
+	if(scr->gscreen->depth == 24)
+		pitch *= 3;
+
+	resetengine(scr);
+	waitforfifo(scr, 14);
+	iow32(scr, ContextMask, ~0);
+	iow32(scr, DstOffPitch, pitch<<22);
+	iow32(scr, DstYX, 0);
+	iow32(scr, DstHeight, 0);
+	iow32(scr, DstBresErr, 0);
+	iow32(scr, DstBresInc, 0);
+	iow32(scr, DstBresDec, 0);
+	iow32(scr, DstCntl, 0x23);
+	iow32(scr, SrcOffPitch, pitch<<22);
+	iow32(scr, SrcYX, 0);
+	iow32(scr, SrcHeight1Width1, 1);
+	iow32(scr, SrcYXstart, 0);
+	iow32(scr, SrcHeight2Width2, 1);
+	iow32(scr, SrcCntl, 0x01);
+
+	waitforfifo(scr, 13);
+	iow32(scr, HostCntl, 0);
+	iow32(scr, PatReg0, 0);
+	iow32(scr, PatReg1, 0);
+	iow32(scr, PatCntl, 0);
+	iow32(scr, ScLeft, 0);
+	iow32(scr, ScTop, 0);
+	iow32(scr, ScBottom, 0xFFFF);
+	iow32(scr, ScRight, 0xFFFF);
+	iow32(scr, DpBkgdClr, 0);
+	iow32(scr, DpFrgdClr, ~0);
+	iow32(scr, DpWriteMask, ~0);
+	iow32(scr, DpMix, 0x70003);
+	iow32(scr, DpSrc, 0x00010100);
+
+	waitforfifo(scr, 3);
+	iow32(scr, ClrCmpClr, 0);
+	iow32(scr, ClrCmpMask, ~0);
+	iow32(scr, ClrCmpCntl, 0);
+
+	waitforfifo(scr, 2);
+	switch(scr->gscreen->depth){
+	case 8:
+	case 24:	/* [sic] */
+		iow32(scr, DpPixWidth, 0x00020202);
+		iow32(scr, DpChainMask, 0x8080);
+		break;
+	case 16:
+		iow32(scr, DpPixWidth, 0x00040404);
+		iow32(scr, DpChainMask, 0x8410);
+		break;
+	case 32:
+		iow32(scr, DpPixWidth, 0x00060606);
+		iow32(scr, DpChainMask, 0x8080);
+		break;
+	}
+
+	/* Get the base freq from the BIOS */
+	bios  = KADDR(0xC000);
+	table = *(ushort *)(bios + 0x48);
+	table = *(ushort *)(bios + table + 0x10);
+	switch (*(ushort *)(bios + table + 0x08)) {
+      	case 2700: 
+		mach64refclock = 270000; 
+		break;
+      	case 2863: 
+      	case 2864: 
+		mach64refclock = 286363; 
+		break;
+      	case 2950: 
+		mach64refclock = 294989; 
+		break;
+    	case 1432: 
+	default:
+		mach64refclock = 143181; 
+		break ;	
+	}
+	
+	/* Figure out which revision this chip is */
+	switch ((scr->mmio[mmoffset[ConfigChipId]] >> 24) & 0xFF) {
+	case VTGTB1S1:
+	case GTB1U1:
+	case GTB1S2:
+	case GTB2U1:
+	case GTB2U2:
+	case GTB2U3:
+	case GTBC:
+	case GTIIIC1U1:
+	case GTIIIC1U2:
+	case GTIIIC2U1:
+	case GTIIIC2U2: 
+	case GTIIIC2U3: 
+	case LTPRO:
+			mach64revb = 1;
+			break;
+	default: 
+			mach64revb = 0;
+			break;
+	}
+
+	waitforidle(scr);
+}
+
+static int
+mach64hwfill(VGAscr *scr, Rectangle r, ulong sval)
+{
+	ulong pitch;
+	ulong ctl;
+
+if(drawdebug)
+	iprint("hwfill %R val %lux...\n", r, sval);
+
+	/* shouldn't happen */
+	if(scr->io == 0x2EC || scr->io == 0x1C8 || scr->io == 0)
+		return 0;
+
+	pitch = Dx(scr->gscreen->r)/8;
+	ctl = 1|2;	/* left-to-right, top-to-bottom */
+	if(scr->gscreen->depth == 24){
+		r.min.x *= 3;
+		r.max.x *= 3;
+		pitch *= 3;
+		ctl |= (1<<7)|(((r.min.x/4)%6)<<8);
+	}
+
+	waitforfifo(scr, 11);
+	iow32(scr, DpFrgdClr, sval);
+	iow32(scr, DpWriteMask, 0xFFFFFFFF);
+	iow32(scr, DpMix, 0x00070003);
+	iow32(scr, DpSrc, 0x00000111);
+	iow32(scr, ClrCmpCntl, 0x00000000);
+	iow32(scr, ScLeftRight, 0x1FFF0000);
+	iow32(scr, ScTopBottom, 0x1FFF0000);
+	iow32(scr, DstOffPitch, pitch<<22);
+	iow32(scr, DstCntl, ctl);
+	iow32(scr, DstYX, (r.min.x<<16)|r.min.y);
+	iow32(scr, DstHeightWidth, (Dx(r)<<16)|Dy(r));
+
+	waitforidle(scr);
+	return 1;
+}
+
+static int
+mach64hwscroll(VGAscr *scr, Rectangle r, Rectangle sr)
+{
+	ulong pitch;
+	Point dp, sp;
+	ulong ctl;
+	int dx, dy;
+
+	dx = Dx(r);
+	dy = Dy(r);
+	pitch = Dx(scr->gscreen->r)/8;
+	if(scr->gscreen->depth == 24){
+		dx *= 3;
+		pitch *= 3;
+		r.min.x *= 3;
+		sr.min.x *= 3;
+	}
+
+	ctl = 0;
+	if(r.min.x <= sr.min.x){
+		ctl |= 1;
+		dp.x = r.min.x;
+		sp.x = sr.min.x;
+	}else{
+		dp.x = r.min.x+dx-1;
+		sp.x = sr.min.x+dx-1;
+	}
+
+	if(r.min.y <= sr.min.y){
+		ctl |= 2;
+		dp.y = r.min.y;
+		sp.y = sr.min.y;
+	}else{
+		dp.y = r.min.y+dy-1;
+		sp.y = sr.min.y+dy-1;
+	}
+
+	if(scr->gscreen->depth == 24)
+		ctl |= (1<<7)|(((dp.x/4)%6)<<8);
+
+	waitforfifo(scr, 6);
+	iow32(scr, ScLeftRight, 0x1FFF0000);
+	iow32(scr, ScTopBottom, 0x1FFF0000);
+	iow32(scr, DpWriteMask, 0xFFFFFFFF);
+	iow32(scr, DpMix, 0x00070003);
+	iow32(scr, DpSrc, 0x00000300);
+	iow32(scr, ClrCmpCntl, 0x00000000);
+
+	waitforfifo(scr, 8);
+	iow32(scr, SrcOffPitch, pitch<<22);
+	iow32(scr, SrcCntl, 0x00000000);
+	iow32(scr, SrcYX, (sp.x<<16)|sp.y);
+	iow32(scr, SrcWidth1, dx);
+	iow32(scr, DstOffPitch, pitch<<22);
+	iow32(scr, DstCntl, ctl);
+
+	iow32(scr, DstYX, (dp.x<<16)|dp.y);
+	iow32(scr, DstHeightWidth, (dx<<16)|dy);
+
+	waitforidle(scr);
+
+	return 1;
+}
+
+/*
+ * This should work, but doesn't.
+ * It messes up the screen timings for some reason.
+ */
+static void
+mach64blank(VGAscr *scr, int blank)
+{
+	ulong ctl;
+
+	ctl = ior32(scr, CrtcGenCtl) & ~(CrtcHsyncDis|CrtcVsyncDis);
+	if(blank)
+		ctl |= CrtcHsyncDis|CrtcVsyncDis;
+	iow32(scr, CrtcGenCtl, ctl);
+}
+
+/*
+ * We squirrel away whether the LCD and/or CRT were
+ * on when we were called to blank the screen, and
+ * restore the old state.  If we are called to blank the
+ * screen when it is already blank, we don't update the state.
+ * Such a call sequence should not happen, though.
+ *
+ * We could try forcing the chip into power management
+ * mode instead, but I'm not sure how that would interact
+ * with screen updates going on while the screen is blanked.
+ */
+static void
+mach64lcdblank(VGAscr *scr, int blank)
+{
+	static int crtlcd;
+	ulong x;
+
+	if(blank) {
+		x = lcdr32(scr, LCD_GenCtrl);
+		if(x & 3) {
+			crtlcd = x & 3;
+			lcdw32(scr, LCD_GenCtrl,  x&~3);
+		}
+	} else {
+		if(crtlcd == 0)
+			crtlcd = 2;	/* lcd only */
+		x = lcdr32(scr, LCD_GenCtrl);
+		lcdw32(scr, LCD_GenCtrl, x | crtlcd);
+	}
+}
+
+static void
+mach64xxdrawinit(VGAscr *scr)
+{
+	if(scr->io > 0x2FF){
+		initengine(scr);
+		scr->fill = mach64hwfill;
+		scr->scroll = mach64hwscroll;
+	}
+/*	scr->blank = mach64blank; */
+	switch(scr->id){
+	default:
+		break;
+	case ('L'<<8)|'B':		/* 4C42: Rage 3D LTPro */
+	case ('L'<<8)|'I':		/* 4C49: Rage 3D LTPro */
+	case ('L'<<8)|'M':		/* 4C4D: Rage Mobility */
+	case ('L'<<8)|'P':		/* 4C50: Rage 3D LTPro */
+		scr->blank = mach64lcdblank;
+		hwblank = 1;
+		break;
+	}
+}
+
+static void
+ovl_configure(VGAscr *scr, Chan *c, char **field)
+{
+	int w, h;
+	char *format;
+
+	w = (int)strtol(field[1], nil, 0);
+	h = (int)strtol(field[2], nil, 0);
+	format = field[3];
+
+	if (c != ovl_chan) 
+		error(Einuse);
+	if (strcmp(format, "YUYV"))
+		error(Eunsupportedformat);
+	
+	ovl_width  = w;
+	ovl_height = h;
+	ovl_fib       = w * h * sizeof(ushort);
+
+	waitforidle(scr);
+	scr->mmio[mmoffset[BusCntl]] |= 0x08000000;	/* Enable regblock 1 */
+	scr->mmio[mmoffset[OverlayScaleCntl]] = 
+		SCALE_ZERO_EXTEND|SCALE_RED_TEMP_6500K|
+		SCALE_HORZ_BLEND|SCALE_VERT_BLEND;
+	scr->mmio[mmoffset[!mach64revb? Buf0Pitch: ScalerBuf0Pitch]] = w;
+	scr->mmio[mmoffset[CaptureConfig]] = 
+		SCALER_FRAME_READ_MODE_FULL|
+		SCALER_BUF_MODE_SINGLE|
+		SCALER_BUF_NEXT_0;
+	scr->mmio[mmoffset[OverlayKeyCntl]] = !mach64revb?
+		OVERLAY_MIX_ALWAYS_V|(OVERLAY_EXCLUSIVE_NORMAL << 28): 
+		0x011;
+
+	if (mach64type->m64_pro) {
+		waitforfifo(scr, 6);
+
+		/* set the scaler co-efficient registers */
+		scr->mmio[mmoffset[ScalerColourCntl]] = 
+			(0x00) | (0x10 << 8) | (0x10 << 16);
+		scr->mmio[mmoffset[ScalerHCoef0]] = 
+			(0x00) | (0x20 << 8);
+		scr->mmio[mmoffset[ScalerHCoef1]] = 
+			(0x0D) | (0x20 << 8) | (0x06 << 16) | (0x0D << 24);
+		scr->mmio[mmoffset[ScalerHCoef2]] = 
+			(0x0D) | (0x1C << 8) | (0x0A << 16) | (0x0D << 24);
+		scr->mmio[mmoffset[ScalerHCoef3]] = 
+			(0x0C) | (0x1A << 8) | (0x0E << 16) | (0x0C << 24);
+		scr->mmio[mmoffset[ScalerHCoef4]] = 
+			(0x0C) | (0x14 << 8) | (0x14 << 16) | (0x0C << 24);
+	}
+	
+	waitforfifo(scr, 3);
+	scr->mmio[mmoffset[VideoFormat]] = SCALE_IN_YVYU422 |
+		(!mach64revb? 0xC: 0);
+
+	if (mach64overlay == 0)
+		mach64overlay = scr->storage + 64 * 64 * sizeof(uchar);
+	scr->mmio[mmoffset[!mach64revb? Buf0Offset: ScalerBuf0Offset]] = 
+		mach64overlay;
+}
+
+static void
+ovl_enable(VGAscr *scr, Chan *c, char **field)
+{
+	int x, y, w, h;
+	long h_inc, v_inc;
+
+	x = (int)strtol(field[1], nil, 0);
+	y = (int)strtol(field[2], nil, 0);
+	w = (int)strtol(field[3], nil, 0);
+	h = (int)strtol(field[4], nil, 0);
+
+	if (x < 0 || x + w > physgscreenr.max.x ||
+	     y < 0 || y + h > physgscreenr.max.y)
+		error(Ebadarg);
+
+	if (c != ovl_chan) 
+		error(Einuse);
+	if (scr->mmio[mmoffset[CrtcGenCntl]] & 1) {	/* double scan enable */
+		y *= 2;
+		h *= 2;
+	}
+
+	waitforfifo(scr, 2);
+	scr->mmio[mmoffset[OverlayYX]] = 
+			((x & 0xFFFF) << 16) | (y & 0xFFFF);
+	scr->mmio[mmoffset[OverlayYXEnd]] = 
+			(((x + w) & 0xFFFF) << 16) | ((y + h) & 0xFFFF);
+
+	h_inc = (ovl_width << 12) / (w >> 1);  /* ??? */
+	v_inc = (ovl_height << 12) / h;
+	waitforfifo(scr, 2);
+	scr->mmio[mmoffset[OverlayScaleInc]] = 
+			((h_inc & 0xFFFF) << 16) | (v_inc & 0xFFFF);
+	scr->mmio[mmoffset[ScalerHeightWidth]] = 
+			((ovl_width & 0xFFFF) << 16) | (ovl_height & 0xFFFF);
+	waitforidle(scr);
+	scr->mmio[mmoffset[OverlayScaleCntl]] |= 
+			(SCALE_ENABLE|OVERLAY_ENABLE);
+}
+
+static void
+ovl_status(VGAscr *scr, Chan *, char **field)
+{
+	pprint("%s: %s %.4uX, VT/GT %s, PRO %s, ovlclock %d, rev B %s, refclock %ld\n",
+		   scr->dev->name, field[0], mach64type->m64_id,
+		   mach64type->m64_vtgt? "yes": "no",
+		   mach64type->m64_pro? "yes": "no",
+		   mach64type->m64_ovlclock,
+		   mach64revb? "yes": "no",
+		   mach64refclock);
+	pprint("%s: storage @%.8luX, aperture @%8.ulX, ovl buf @%.8ulX\n",
+		   scr->dev->name, scr->storage, scr->aperture,
+		   mach64overlay);
+}
+	
+static void
+ovl_openctl(VGAscr *, Chan *c, char **)
+{
+	if (ovl_chan) 
+		error(Einuse);
+	ovl_chan = c;
+}
+
+static void
+ovl_closectl(VGAscr *scr, Chan *c, char **)
+{
+	if (c != ovl_chan) return;
+
+	waitforidle(scr);
+	scr->mmio[mmoffset[OverlayScaleCntl]] &=
+			~(SCALE_ENABLE|OVERLAY_ENABLE);
+	ovl_chan = nil;
+	ovl_width = ovl_height = ovl_fib = 0;
+}
+
+enum
+{
+	CMclosectl,
+	CMconfigure,
+	CMenable,
+	CMopenctl,
+	CMstatus,
+};
+
+static void (*ovl_cmds[])(VGAscr *, Chan *, char **) =
+{
+	[CMclosectl]	ovl_closectl,
+	[CMconfigure]	ovl_configure,
+	[CMenable]	ovl_enable,
+	[CMopenctl]	ovl_openctl,
+	[CMstatus]	ovl_status,
+};
+
+static Cmdtab mach64xxcmd[] =
+{
+	CMclosectl,	"closectl",	1,
+	CMconfigure,	"configure",	4,
+	CMenable,	"enable",	5,
+	CMopenctl,	"openctl",	1,
+	CMstatus,	"status",	1,
+};
+
+static void
+mach64xxovlctl(VGAscr *scr, Chan *c, void *a, int n)
+{
+	Cmdbuf *cb;
+	Cmdtab *ct;
+
+	if(!mach64type->m64_vtgt) 
+		error(Enodev);
+
+	if(!scr->overlayinit){
+		scr->overlayinit = 1;
+		init_overlayclock(scr);
+	}
+	cb = parsecmd(a, n);
+	if(waserror()){
+		free(cb);
+		nexterror();
+	}
+
+	ct = lookupcmd(cb, mach64xxcmd, nelem(mach64xxcmd));
+
+	ovl_cmds[ct->index](scr, c, cb->f);
+
+	poperror();
+	free(cb);
+}
+
+static int
+mach64xxovlwrite(VGAscr *scr, void *a, int len, vlong offs)
+{
+	uchar *src;
+	int _len;
+
+	if (ovl_chan == nil) return len;	/* Acts as a /dev/null */
+	
+	/* Calculate the destination address */
+	_len = len;
+	src   = (uchar *)a;
+	while (len > 0) {
+		ulong _offs;
+		int nb;
+
+		_offs = (ulong)(offs % ovl_fib);
+		nb     = (_offs + len > ovl_fib)? ovl_fib - _offs: len;
+		memmove((uchar *)KADDR(scr->aperture + mach64overlay + _offs), 
+				  src, nb);
+		offs += nb;
+		src  += nb;
+		len  -= nb;
+	}
+	return _len;
+}
+
+VGAdev vgamach64xxdev = {
+	"mach64xx",
+
+	mach64xxenable,			/* enable */
+	0,				/* disable */
+	0,				/* page */
+	mach64xxlinear,			/* linear */
+	mach64xxdrawinit,	/* drawinit */
+	0,
+	mach64xxovlctl,	/* overlay control */
+	mach64xxovlwrite,	/* write the overlay */
+};
+
+VGAcur vgamach64xxcur = {
+	"mach64xxhwgc",
+
+	mach64xxcurenable,		/* enable */
+	mach64xxcurdisable,		/* disable */
+	mach64xxcurload,		/* load */
+	mach64xxcurmove,		/* move */
+
+	1					/* doespanning */
+};
+
--- /dev/null
+++ b/os/pc/vgamga2164w.c
@@ -1,0 +1,289 @@
+#include "u.h"
+#include "../port/lib.h"
+#include "mem.h"
+#include "dat.h"
+#include "fns.h"
+#include "io.h"
+#include "../port/error.h"
+
+#define	Image	IMAGE
+#include <draw.h>
+#include <memdraw.h>
+#include <cursor.h>
+#include "screen.h"
+
+/*
+ * Matrox Millennium and Matrox Millennium II.
+ * Matrox MGA-2064W, MGA-2164W 3D graphics accelerators.
+ * Texas Instruments Tvp3026 RAMDAC.
+ */
+
+enum {
+	/* pci chip manufacturer */
+	MATROX		= 0x102B,
+
+	/* pci chip device ids */
+	MGA2064		= 0x0519,
+	MGA2164		= 0x051B,
+	MGA2164AGP	= 0x051F
+};
+
+static Pcidev*
+mgapcimatch(void)
+{
+	Pcidev *p;
+
+	p = pcimatch(nil, MATROX, MGA2164AGP);
+	if(p == nil) {
+		p = pcimatch(nil, MATROX, MGA2164);
+		if(p == nil)
+			p = pcimatch(nil, MATROX, MGA2064);
+	}
+	return p;
+}
+
+static ulong
+mga2164wlinear(VGAscr* scr, int* size, int* align)
+{
+	ulong aperture, oaperture;
+	int oapsize, wasupamem;
+	Pcidev *p;
+
+	oaperture = scr->aperture;
+	oapsize = scr->apsize;
+	wasupamem = scr->isupamem;
+
+	if(p = mgapcimatch()){
+		aperture = p->mem[p->did==MGA2064? 1 : 0].bar & ~0x0F;
+		*size = (p->did==MGA2064? 8 :16)*1024*1024;
+	}
+	else
+		aperture = 0;
+
+	if(wasupamem) {
+		if(oaperture == aperture)
+			return oaperture;
+		upafree(oaperture, oapsize);
+	}
+	scr->isupamem = 0;
+
+	aperture = upamalloc(aperture, *size, *align);
+	if(aperture == 0){
+		if(wasupamem && upamalloc(oaperture, oapsize, 0)) {
+			aperture = oaperture;
+			scr->isupamem = 1;
+		}
+		else
+			scr->isupamem = 0;
+	}
+	else
+		scr->isupamem = 1;
+
+	return aperture;
+}
+
+static void
+mga2164wenable(VGAscr* scr)
+{
+	Pcidev *p;
+	int size, align, immio;
+	ulong aperture;
+
+	/*
+	 * Only once, can't be disabled for now.
+	 * scr->io holds the virtual address of
+	 * the MMIO registers.
+	 */
+	if(scr->io)
+		return;
+
+	p = mgapcimatch();
+	if(p == nil)
+		return;
+
+	immio = p->did==MGA2064? 0 : 1;
+	scr->io = upamalloc(p->mem[immio].bar & ~0x0F, p->mem[immio].size, 0);
+	if(scr->io == 0)
+		return;
+	addvgaseg("mga2164wmmio", scr->io, p->mem[immio].size);
+
+	scr->io = (ulong)KADDR(scr->io);
+
+	/* need to map frame buffer here too, so vga can find memory size */
+	size = (p->did==MGA2064? 8 :16)*1024*1024;
+	align = 0;
+	aperture = mga2164wlinear(scr, &size, &align);
+	if(aperture) {
+		scr->aperture = aperture;
+		scr->apsize = size;
+		addvgaseg("mga2164wscreen", aperture, size);
+	}
+}
+
+enum {
+	Index		= 0x00,		/* Index */
+	Data		= 0x0A,		/* Data */
+
+	CaddrW		= 0x04,		/* Colour Write Address */
+	Cdata		= 0x05,		/* Colour Data */
+
+	Cctl		= 0x09,		/* Direct Cursor Control */
+	Cram		= 0x0B,		/* Cursor Ram Data */
+	Cxlsb		= 0x0C,		/* Cursor X LSB */
+	Cxmsb		= 0x0D,		/* Cursor X MSB */
+	Cylsb		= 0x0E,		/* Cursor Y LSB */
+	Cymsb		= 0x0F,		/* Cursor Y MSB */
+
+	Icctl		= 0x06,		/* Indirect Cursor Control */
+};
+
+static void
+tvp3026disable(VGAscr* scr)
+{
+	uchar *tvp3026;
+
+	if(scr->io == 0)
+		return;
+	tvp3026 = KADDR(scr->io+0x3C00);
+
+	/*
+	 * Make sure cursor is off
+	 * and direct control enabled.
+	 */
+	*(tvp3026+Index) = Icctl;
+	*(tvp3026+Data) = 0x90;
+	*(tvp3026+Cctl) = 0x00;
+}
+
+static void
+tvp3026load(VGAscr* scr, Cursor* curs)
+{
+	int x, y;
+	uchar *tvp3026;
+
+	if(scr->io == 0)
+		return;
+	tvp3026 = KADDR(scr->io+0x3C00);
+
+	/*
+	 * Make sure cursor is off by initialising the cursor
+	 * control to defaults.
+	 * Write to the indirect control register to make sure
+	 * direct register is enabled and upper 2 bits of cursor
+	 * RAM address are 0.
+	 * Put 0 in index register for lower 8 bits of cursor RAM address.
+	 */
+	tvp3026disable(scr);
+	*(tvp3026+Index) = 0;
+
+	/*
+	 * Initialise the 64x64 cursor RAM array. There are 2 planes,
+	 * p0 and p1. Data is written 8 pixels per byte, with p0 in the
+	 * first 512 bytes of the array and p1 in the second.
+	 * The cursor is set in 3-colour mode which gives the following
+	 * truth table:
+	 *	p1 p0	colour
+	 *	 0  0	transparent
+	 *	 0  1	cursor colour 0
+	 *	 1  0	cursor colour 1
+	 *	 1  1	cursor colour 2
+	 * Put the cursor into the top-left of the 64x64 array.
+	 * The 0,0 cursor point is bottom-right, so positioning will
+	 * have to take that into account.
+	 */
+	for(y = 0; y < 64; y++){
+		for(x = 0; x < 64/8; x++){
+			if(x < 16/8 && y < 16)
+				*(tvp3026+Cram) = curs->clr[x+y*2];
+			else
+				*(tvp3026+Cram) = 0x00;
+		}
+	}
+	for(y = 0; y < 64; y++){
+		for(x = 0; x < 64/8; x++){
+			if(x < 16/8 && y < 16)
+				*(tvp3026+Cram) = curs->set[x+y*2];
+			else
+				*(tvp3026+Cram) = 0x00;
+		}
+	}
+
+	/*
+	 * Initialise the cursor hotpoint
+	 * and enable the cursor in 3-colour mode.
+	 */
+	scr->offset.x = 64+curs->offset.x;
+	scr->offset.y = 64+curs->offset.y;
+	*(tvp3026+Cctl) = 0x01;
+}
+
+static int
+tvp3026move(VGAscr* scr, Point p)
+{
+	int x, y;
+	uchar *tvp3026;
+
+	if(scr->io == 0)
+		return 1;
+	tvp3026 = KADDR(scr->io+0x3C00);
+
+	x = p.x+scr->offset.x;
+	y = p.y+scr->offset.y;
+
+	*(tvp3026+Cxlsb) = x & 0xFF;
+	*(tvp3026+Cxmsb) = (x>>8) & 0x0F;
+	*(tvp3026+Cylsb) = y & 0xFF;
+	*(tvp3026+Cymsb) = (y>>8) & 0x0F;
+
+	return 0;
+}
+
+static void
+tvp3026enable(VGAscr* scr)
+{
+	int i;
+	uchar *tvp3026;
+
+	if(scr->io == 0)
+		return;
+	tvp3026 = KADDR(scr->io+0x3C00);
+
+	tvp3026disable(scr);
+
+	/*
+	 * Overscan colour,
+	 * cursor colour 1 (white),
+	 * cursor colour 2, 3 (black).
+	 */
+	*(tvp3026+CaddrW) = 0x00;
+	for(i = 0; i < 6; i++)
+		*(tvp3026+Cdata) = Pwhite; 
+	for(i = 0; i < 6; i++)
+		*(tvp3026+Cdata) = Pblack; 
+
+	/*
+	 * Load, locate and enable the
+	 * 64x64 cursor in 3-colour mode.
+	 */
+	tvp3026load(scr, &arrow);
+	tvp3026move(scr, ZP);
+	*(tvp3026+Cctl) = 0x01;
+}
+
+VGAdev vgamga2164wdev = {
+	"mga2164w",
+
+	mga2164wenable,			/* enable */
+	0,				/* disable */
+	0,				/* page */
+	mga2164wlinear,			/* linear */
+};
+
+VGAcur vgamga2164wcur = {
+	"mga2164whwgc",
+
+	tvp3026enable,
+	tvp3026disable,
+	tvp3026load,
+	tvp3026move,
+};
--- /dev/null
+++ b/os/pc/vgamga4xx.c
@@ -1,0 +1,603 @@
+
+/*
+ * Matrox G200, G400 and G450.
+ * see /sys/src/cmd/aux/vga/mga4xx.c
+ */
+
+#include 	"u.h"
+#include 	"../port/lib.h"
+#include 	"mem.h"
+#include 	"dat.h"
+#include 	"fns.h"
+#include 	"io.h"
+#include 	"../port/error.h"
+
+#define	Image	IMAGE
+#include 	<draw.h>
+#include 	<memdraw.h>
+#include 	<cursor.h>
+#include	"screen.h"
+
+enum {
+	MATROX			= 0x102B,
+	MGA4xx			= 0x0525,
+	MGA200			= 0x0521,
+
+	Kilo				= 1024,
+	Meg				= 1024*1024,
+
+	FCOL			= 0x1c24,
+	FXRIGHT			= 0x1cac,	
+	FXLEFT			= 0x1ca8,
+	YDST			= 0x1c90,
+	YLEN			= 0x1c5c,
+ 	DWGCTL			= 0x1c00,
+ 		DWG_TRAP		= 0x04,
+ 		DWG_BITBLT		= 0x08,
+ 		DWG_ILOAD		= 0x09,
+ 		DWG_LINEAR		= 0x0080,
+ 		DWG_SOLID		= 0x0800,
+ 		DWG_ARZERO		= 0x1000,
+ 		DWG_SGNZERO	= 0x2000,
+ 		DWG_SHIFTZERO	= 0x4000,
+		DWG_REPLACE		= 0x000C0000,
+ 		DWG_REPLACE2	= (DWG_REPLACE | 0x40),
+ 		DWG_XOR		= 0x00060010,
+ 		DWG_BFCOL		= 0x04000000,
+ 		DWG_BMONOWF	= 0x08000000,
+		DWG_TRANSC		= 0x40000000,
+ 	SRCORG			= 0x2cb4,
+	PITCH			= 0x1c8c,
+	DSTORG			= 0x2cb8,
+	PLNWRT			= 0x1c1c,
+	ZORG			= 0x1c0c,
+	MACCESS			= 0x1c04,
+	STATUS			= 0x1e14,
+	FXBNDRY			= 0x1C84,
+ 	CXBNDRY			= 0x1C80,
+	YTOP			= 0x1C98,
+	YBOT			= 0x1C9C,
+	YDSTLEN			= 0x1C88,
+	AR0				= 0x1C60,
+	AR1				= 0x1C64,
+	AR2				= 0x1C68,
+	AR3				= 0x1C6C,
+	AR4				= 0x1C70,
+	AR5				= 0x1C74,
+	SGN				= 0x1C58,
+		SGN_SCANLEFT = 		1,
+		SGN_SCANRIGHT = 		0,
+		SGN_SDY_POSITIVE = 	0,
+		SGN_SDY_NEGATIVE = 	4,
+
+	GO				= 0x0100,
+ 	FIFOSTATUS		= 0x1E10,
+	CACHEFLUSH		= 0x1FFF,
+
+	CRTCEXTIDX		= 0x1FDE,		/* CRTC Extension Index */
+	CRTCEXTDATA		= 0x1FDF,		/* CRTC Extension Data */
+
+	FILL_OPERAND		= 0x800c7804,
+};
+
+static Pcidev*
+mgapcimatch(void)
+{
+	Pcidev*	p;
+	
+	p = pcimatch(nil, MATROX, MGA4xx);
+	if (p == nil)
+		p = pcimatch(nil, MATROX, MGA200);
+	return p;
+}
+
+static ulong
+mga4xxlinear(VGAscr* scr, int* size, int* align)
+{
+	ulong 	aperture, oaperture;
+	int 		oapsize, wasupamem;
+	Pcidev *	p;
+
+	oaperture = scr->aperture;
+	oapsize = scr->apsize;
+	wasupamem = scr->isupamem;
+
+	if(p = mgapcimatch()){
+		aperture = p->mem[0].bar & ~0x0F;
+		if(p->did == MGA4xx)
+			*size = 32*Meg;
+		else
+			*size = 8*Meg;
+	}
+	else
+		aperture = 0;
+
+	if(wasupamem) {
+		if(oaperture == aperture)
+			return oaperture;
+		upafree(oaperture, oapsize);
+	}
+	scr->isupamem = 0;
+
+	aperture = upamalloc(aperture, *size, *align);
+	if(aperture == 0){
+		if(wasupamem && upamalloc(oaperture, oapsize, 0)) {
+			aperture = oaperture;
+			scr->isupamem = 1;
+		}
+		else
+			scr->isupamem = 0;
+	}
+	else
+		scr->isupamem = 1;
+
+	return aperture;
+}
+
+static void
+mgawrite8(VGAscr* scr, int index, uchar val)
+{
+	((uchar*)scr->io)[index] = val;
+}
+
+static uchar
+mgaread8(VGAscr* scr, int index)
+{
+	return ((uchar*)scr->io)[index];
+}
+
+static uchar
+crtcextset(VGAscr* scr, int index, uchar set, uchar clr)
+{
+	uchar	tmp;
+
+	mgawrite8(scr, CRTCEXTIDX, index);
+	tmp = mgaread8(scr, CRTCEXTDATA);
+	mgawrite8(scr, CRTCEXTIDX, index);
+	mgawrite8(scr, CRTCEXTDATA, (tmp & ~clr) | set);
+
+	return tmp;
+}
+
+static void
+mga4xxenable(VGAscr* scr)
+{
+	Pcidev *	pci;
+	int 		size, align;
+	ulong 	aperture;
+	int 		i, n, k;
+	uchar *	p;
+	uchar	x[16];
+	uchar	crtcext3;
+
+	/*
+	 * Only once, can't be disabled for now.
+	 * scr->io holds the virtual address of
+	 * the MMIO registers.
+	 */
+	if(scr->io)
+		return;
+
+	pci = mgapcimatch();
+	if(pci == nil)
+		return;
+
+	scr->io = upamalloc(pci->mem[1].bar & ~0x0F, 16*1024, 0);
+	if(scr->io == 0)
+		return;
+
+	addvgaseg("mga4xxmmio", scr->io, pci->mem[1].size);
+
+	scr->io = (ulong)KADDR(scr->io);
+
+	/* need to map frame buffer here too, so vga can find memory size */
+	size = 8*Meg;
+	align = 0;
+	aperture = mga4xxlinear(scr, &size, &align);
+	if(aperture) {
+		scr->aperture = aperture;
+		addvgaseg("mga4xxscreen", aperture, size);
+
+		/* Find out how much memory is here, some multiple of 2 Meg */
+
+		/* First Set MGA Mode ... */
+		crtcext3 = crtcextset(scr, 3, 0x80, 0x00);
+
+		p = (uchar*)aperture;
+		n = (size / Meg) / 2;
+		for (i = 0; i < n; i++) {
+			k = (2*i+1)*Meg;
+			p[k] = 0;
+			p[k] = i+1;
+			*((uchar*)(scr->io + CACHEFLUSH)) = 0;
+			x[i] = p[k];
+ 		}
+		for(i = 1; i < n; i++)
+			if(x[i] != i+1)
+				break;
+  		scr->apsize = 2*i*Meg;
+
+		crtcextset(scr, 3, crtcext3, 0xff);
+	}
+}
+
+enum {
+	Index		= 0x00,		/* Index */
+	Data			= 0x0A,		/* Data */
+
+	Cxlsb		= 0x0C,		/* Cursor X LSB */
+	Cxmsb		= 0x0D,		/* Cursor X MSB */
+	Cylsb		= 0x0E,		/* Cursor Y LSB */
+	Cymsb		= 0x0F,		/* Cursor Y MSB */
+
+	Icuradrl		= 0x04,		/* Cursor Base Address Low */	
+	Icuradrh		= 0x05,		/* Cursor Base Address High */
+	Icctl			= 0x06,		/* Indirect Cursor Control */
+};
+
+static void
+dac4xxdisable(VGAscr* scr)
+{
+	uchar * 	dac4xx;
+	
+	if(scr->io == 0)
+		return;
+
+	dac4xx = KADDR(scr->io+0x3C00);
+	
+	*(dac4xx+Index) = Icctl;
+	*(dac4xx+Data) = 0x00;
+}
+
+static void
+dac4xxload(VGAscr* scr, Cursor* curs)
+{
+	int 		y;
+	uchar *	p;
+	uchar * 	dac4xx;
+
+	if(scr->io == 0)
+		return;
+
+	dac4xx = KADDR(scr->io+0x3C00);
+	
+	dac4xxdisable(scr);
+
+	p = KADDR(scr->storage);
+	for(y = 0; y < 64; y++){
+		*p++ = 0; *p++ = 0; *p++ = 0;
+		*p++ = 0; *p++ = 0; *p++ = 0;
+		if(y <16){
+			*p++ = curs->set[1+y*2]|curs->clr[1+2*y];
+			*p++ = curs->set[y*2]|curs->clr[2*y];
+		} else{
+			*p++ = 0; *p++ = 0;
+		}
+
+		*p++ = 0; *p++ = 0; *p++ = 0;
+		*p++ = 0; *p++ = 0; *p++ = 0;
+		if(y <16){
+			*p++ = curs->set[1+y*2];
+			*p++ = curs->set[y*2];
+		} else{
+			*p++ = 0; *p++ = 0;
+		}
+	}
+	scr->offset.x = 64 + curs->offset.x;
+	scr->offset.y = 64 + curs->offset.y;
+
+	*(dac4xx+Index) = Icctl;
+	*(dac4xx+Data) = 0x03;
+}
+
+static int
+dac4xxmove(VGAscr* scr, Point p)
+{
+	int 		x, y;
+	uchar *	dac4xx;
+
+	if(scr->io == 0)
+		return 1;
+
+	dac4xx = KADDR(scr->io + 0x3C00);
+
+	x = p.x + scr->offset.x;
+	y = p.y + scr->offset.y;
+
+	*(dac4xx+Cxlsb) = x & 0xFF;
+	*(dac4xx+Cxmsb) = (x>>8) & 0x0F;
+
+	*(dac4xx+Cylsb) = y & 0xFF;
+	*(dac4xx+Cymsb) = (y>>8) & 0x0F;
+
+	return 0;
+}
+
+static void
+dac4xxenable(VGAscr* scr)
+{
+	uchar *	dac4xx;
+	ulong	storage;
+	
+	if(scr->io == 0)
+		return;
+	dac4xx = KADDR(scr->io+0x3C00);
+
+	dac4xxdisable(scr);
+
+	storage = (scr->apsize - 4096) & ~0x3ff;
+
+	*(dac4xx+Index) = Icuradrl;
+	*(dac4xx+Data) = 0xff & (storage >> 10);
+	*(dac4xx+Index) = Icuradrh;
+	*(dac4xx+Data) = 0xff & (storage >> 18);		
+
+	scr->storage = (ulong) KADDR((ulong)scr->aperture + (ulong)storage);
+
+	/* Show X11-Like Cursor */
+	*(dac4xx+Index) = Icctl;
+	*(dac4xx+Data) = 0x03;
+
+	/* Cursor Color 0 : White */
+	*(dac4xx+Index) = 0x08;
+	*(dac4xx+Data)  = 0xff;
+	*(dac4xx+Index) = 0x09;
+	*(dac4xx+Data)  = 0xff;
+	*(dac4xx+Index) = 0x0a;
+	*(dac4xx+Data)  = 0xff;
+
+	/* Cursor Color 1 : Black */
+	*(dac4xx+Index) = 0x0c;
+	*(dac4xx+Data)  = 0x00;
+	*(dac4xx+Index) = 0x0d;
+	*(dac4xx+Data)  = 0x00;
+	*(dac4xx+Index) = 0x0e;
+	*(dac4xx+Data)  = 0x00;
+
+	/* Cursor Color 2 : Red */
+	*(dac4xx+Index) = 0x10;
+	*(dac4xx+Data)  = 0xff;
+	*(dac4xx+Index) = 0x11;
+	*(dac4xx+Data)  = 0x00;
+	*(dac4xx+Index) = 0x12;
+	*(dac4xx+Data)  = 0x00;
+
+	/*
+	 * Load, locate and enable the
+	 * 64x64 cursor in X11 mode.
+	 */
+	dac4xxload(scr, &arrow);
+	dac4xxmove(scr, ZP);
+}
+
+static void
+mga4xxblank(VGAscr* scr, int blank)
+{
+	char * 	cp;
+	uchar * 	mga;
+	uchar 	seq1, crtcext1;
+	
+	/* blank = 0 -> turn screen on */
+	/* blank = 1 -> turn screen off */
+
+	if(scr->io == 0)
+		return;
+	mga = KADDR(scr->io);	
+
+	if (blank == 0) {
+		seq1 = 0x00;
+		crtcext1 = 0x00;
+	} else {
+		seq1 = 0x20;
+		crtcext1 = 0x10;			/* Default value ... : standby */
+		cp = getconf("*dpms");
+		if (cp) {
+			if (cistrcmp(cp, "standby") == 0) {
+				crtcext1 = 0x10;
+			} else if (cistrcmp(cp, "suspend") == 0) {
+				crtcext1 = 0x20;
+			} else if (cistrcmp(cp, "off") == 0) {
+				crtcext1 = 0x30;
+			}
+		}
+	}
+
+	*(mga + 0x1fc4) = 1;
+	seq1 |= *(mga + 0x1fc5) & ~0x20;
+	*(mga + 0x1fc5) = seq1;
+
+	*(mga + 0x1fde) = 1;
+	crtcext1 |= *(mga + 0x1fdf) & ~0x30;
+	*(mga + 0x1fdf) = crtcext1;
+}
+
+static void
+mgawrite32(uchar * mga, ulong reg, ulong val)
+{
+	ulong *	l;
+
+	l = (ulong *)(&mga[reg]);
+	l[0] = val;
+}
+
+static ulong
+mgaread32(uchar * mga, ulong reg)
+{
+	return *((ulong *)(&mga[reg]));
+}
+
+static int
+mga4xxfill(VGAscr* scr, Rectangle r, ulong color)
+{
+	uchar * 		mga;
+ 
+	/* Constant Shaded Trapezoids / Rectangle Fills */
+	if(scr->io == 0)
+		return 0;
+	mga = KADDR(scr->io);
+
+	mgawrite32(mga, DWGCTL, 0);
+	mgawrite32(mga, FCOL, color);
+	mgawrite32(mga, FXRIGHT, r.max.x);
+	mgawrite32(mga, FXLEFT, r.min.x);
+	mgawrite32(mga, YDST, r.min.y);
+	mgawrite32(mga, YLEN, Dy(r));
+	mgawrite32(mga, DWGCTL + GO, FILL_OPERAND);
+
+	while (mgaread32(mga, STATUS) & 0x00010000)
+		;
+	return 1;
+}
+
+#define mga_fifo(n)	do {} while ((mgaread32(mga, FIFOSTATUS) & 0xFF) < (n))
+
+static int
+mga4xxscroll(VGAscr* scr, Rectangle r_dst, Rectangle r_src)
+{
+	uchar * 	mga;
+	ulong	pitch, y;
+ 	ulong 	width, height, start, end, scandir;
+	int 		ydir;
+ 
+	/* Two-operand Bitblts */
+	if(scr->io == 0)
+		return 0;
+
+	mga = KADDR(scr->io);
+
+	pitch = Dx(scr->gscreen->r);
+
+	mgawrite32(mga, DWGCTL, 0);
+
+ 	scandir 	= 0;
+
+	height 	= abs(Dy(r_src));
+	width 	= abs(Dx(r_src));
+
+	assert(height == abs(Dy(r_dst)));
+	assert(width == abs(Dx(r_dst)));
+
+	if ((r_src.min.y == r_dst.min.y) && (r_src.min.x == r_dst.min.x))
+	{
+		if (0) 
+			print("move x,y to x,y !\n");
+		return 1;
+	}
+
+	ydir = 1;
+	if (r_dst.min.y > r_src.min.y)
+	{
+		if (0) 
+			print("ydir = -1\n");
+		ydir = -1;
+		scandir |= 4;	// Blit UP
+	}
+
+	if (r_dst.min.x > r_src.min.x)
+	{
+		if (0) 
+			print("xdir = -1\n");
+		scandir |= 1;	// Blit Left
+	}
+
+	mga_fifo(4);
+	if (scandir)
+	{
+ 		mgawrite32(mga, DWGCTL, DWG_BITBLT | DWG_SHIFTZERO | 
+			DWG_SGNZERO | DWG_BFCOL | DWG_REPLACE);
+		mgawrite32(mga, SGN, scandir);
+	} else
+	{
+		mgawrite32(mga, DWGCTL, DWG_BITBLT | DWG_SHIFTZERO | 
+			DWG_BFCOL | DWG_REPLACE);
+ 	}
+	mgawrite32(mga, AR5, ydir * pitch);
+
+	width--;
+	start = end = r_src.min.x + (r_src.min.y * pitch);
+	if ((scandir & 1) == 1)
+	{
+		start += width;
+	} else
+	{
+		end += width;
+	}
+
+	y = r_dst.min.y;
+	if ((scandir & 4) == 4)
+	{
+		start += (height - 1) * pitch;
+		end += (height - 1) * pitch;
+		y += (height - 1);
+	}
+
+	mga_fifo(4);
+	mgawrite32(mga, AR0, end);
+	mgawrite32(mga, AR3, start);
+	mgawrite32(mga, FXBNDRY, ((r_dst.min.x+width)<<16) | r_dst.min.x);
+	mgawrite32(mga, YDSTLEN + GO, (y << 16) | height);
+ 
+	if (1)
+	{
+		while (mgaread32(mga, STATUS) & 0x00010000)
+			;
+	}
+
+	return 1;
+}
+
+static void
+mga4xxdrawinit(VGAscr* scr)
+{
+	uchar * 	mga;
+ 	Pcidev*	p;
+
+	p = pcimatch(nil, MATROX, MGA4xx);
+	if (p == nil)
+		return ;
+
+	if(scr->io == 0)
+		return;
+	mga = KADDR(scr->io);
+
+	mgawrite32(mga, SRCORG, 0);
+	mgawrite32(mga, DSTORG, 0);
+	mgawrite32(mga, ZORG, 0);
+	mgawrite32(mga, PLNWRT, ~0);
+	mgawrite32(mga, FCOL, 0xffff0000);
+	mgawrite32(mga, CXBNDRY, 0xFFFF0000);
+	mgawrite32(mga, YTOP, 0);
+	mgawrite32(mga, YBOT, 0x01FFFFFF);
+	mgawrite32(mga, PITCH, Dx(scr->gscreen->r) & ((1 << 13) - 1));
+	switch(scr->gscreen->depth) {
+	case 8:
+		mgawrite32(mga, MACCESS, 0);
+		break;
+	case 32:
+		mgawrite32(mga, MACCESS, 2);
+		break;
+	default:
+		return;		/* depth not supported ! */
+	}
+	scr->fill = mga4xxfill;
+	scr->scroll = mga4xxscroll;
+	scr->blank = mga4xxblank;
+}
+
+VGAdev vgamga4xxdev = {
+	"mga4xx",
+
+	mga4xxenable,		/* enable */
+	0,					/* disable */
+	0,					/* page */
+	mga4xxlinear,			/* linear */
+	mga4xxdrawinit,
+};
+
+VGAcur vgamga4xxcur = {
+	"mga4xxhwgc",
+	dac4xxenable,
+	dac4xxdisable,
+	dac4xxload,
+	dac4xxmove,
+};
--- /dev/null
+++ b/os/pc/vganeomagic.c
@@ -1,0 +1,541 @@
+#include "u.h"
+#include "../port/lib.h"
+#include "mem.h"
+#include "dat.h"
+#include "fns.h"
+#include "io.h"
+#include "../port/error.h"
+
+#define	Image	IMAGE
+#include <draw.h>
+#include <memdraw.h>
+#include <cursor.h>
+#include "screen.h"
+
+typedef struct CursorNM CursorNM;
+struct CursorNM {
+	int	enable;
+	int	x;
+	int	y;
+	int	colour1;
+	int	colour2;
+	int	addr;
+};
+
+static ulong
+neomagiclinear(VGAscr* scr, int* size, int* align)
+{
+	ulong aperture, oaperture;
+	int oapsize, wasupamem;
+	Pcidev *p;
+
+	oaperture = scr->aperture;
+	oapsize = scr->apsize;
+	wasupamem = scr->isupamem;
+
+	aperture = 0;
+	if(p = pcimatch(nil, 0x10C8, 0)){
+		switch(p->did){
+		case 0x0004:		/* MagicGraph 128XD */
+		case 0x0005:		/* MagicMedia 256AV */
+		case 0x0006:		/* MagicMedia 256ZX */
+			aperture = p->mem[0].bar & ~0x0F;
+			*size = p->mem[0].size;
+			break;
+		default:
+			break;
+		}
+	}
+
+	if(wasupamem){
+		if(oaperture == aperture)
+			return oaperture;
+		upafree(oaperture, oapsize);
+	}
+	scr->isupamem = 0;
+
+	aperture = upamalloc(aperture, *size, *align);
+	if(aperture == 0){
+		if(wasupamem && upamalloc(oaperture, oapsize, 0)){
+			aperture = oaperture;
+			scr->isupamem = 1;
+		}
+		else
+			scr->isupamem = 0;
+	}
+	else
+		scr->isupamem = 1;
+
+	return aperture;
+}
+
+static void
+neomagicenable(VGAscr* scr)
+{
+	Pcidev *p;
+	int align, curoff, size, vmsize;
+	ulong aperture;
+
+	/*
+	 * Only once, can't be disabled for now.
+	 * scr->io holds the physical address of the cursor registers
+	 * in the MMIO space. This may need to change for older chips
+	 * which have the MMIO space offset in the framebuffer region.
+	 */
+	if(scr->io)
+		return;
+	if(p = pcimatch(nil, 0x10C8, 0)){
+		switch(p->did){
+		case 0x0004:		/* MagicGraph 128XD */
+			curoff = 0x100;
+			vmsize = 2048*1024;
+			break;
+		case 0x0005:		/* MagicMedia 256AV */
+			curoff = 0x1000;
+			vmsize = 2560*1024;
+			break;
+		case 0x0006:		/* MagicMedia 256ZX */
+			curoff = 0x1000;
+			vmsize = 4096*1024;
+			break;
+		default:
+			return;
+		}
+	}
+	else
+		return;
+	scr->io = upamalloc(p->mem[1].bar & ~0x0F, p->mem[1].size, 0);
+	if(scr->io == 0)
+		return;
+	addvgaseg("neomagicmmio", scr->io, p->mem[1].size);
+	scr->mmio = KADDR(scr->io);
+
+	/*
+	 * Find a place for the cursor data in display memory.
+	 * 2 cursor images might be needed, 1KB each so use the
+	 * last 2KB of the framebuffer.
+	 */
+	scr->storage = vmsize-2*1024;
+	scr->io += curoff;
+
+	size = p->mem[0].size;
+	align = 0;
+	aperture = neomagiclinear(scr, &size, &align);
+	if(aperture) {
+		scr->aperture = aperture;
+		scr->apsize = size;
+		addvgaseg("neomagicscreen", aperture, size);
+	}
+}
+
+static void
+neomagiccurdisable(VGAscr* scr)
+{
+	CursorNM *cursornm;
+
+	if(scr->io == 0)
+		return;
+	cursornm = KADDR(scr->io);
+	cursornm->enable = 0;
+}
+
+static void
+neomagicinitcursor(VGAscr* scr, int xo, int yo, int index)
+{
+	uchar *p;
+	uint p0, p1;
+	int x, y;
+
+	p = KADDR(scr->aperture);
+	p += scr->storage + index*1024;
+
+	for(y = yo; y < 16; y++){
+		p0 = scr->set[2*y];
+		p1 = scr->set[2*y+1];
+		if(xo){
+			p0 = (p0<<xo)|(p1>>(8-xo));
+			p1 <<= xo;
+		}
+		*p++ = p0;
+		*p++ = p1;
+
+		for(x = 16; x < 64; x += 8)
+			*p++ = 0x00;
+
+		p0 = scr->clr[2*y]|scr->set[2*y];
+		p1 = scr->clr[2*y+1]|scr->set[2*y+1];
+		if(xo){
+			p0 = (p0<<xo)|(p1>>(8-xo));
+			p1 <<= xo;
+		}
+		*p++ = p0;
+		*p++ = p1;
+
+		for(x = 16; x < 64; x += 8)
+			*p++ = 0x00;
+	}
+	while(y < 64+yo){
+		for(x = 0; x < 64; x += 8){
+			*p++ = 0x00;
+			*p++ = 0x00;
+		}
+		y++;
+	}
+}
+
+static void
+neomagiccurload(VGAscr* scr, Cursor* curs)
+{
+	CursorNM *cursornm;
+
+	if(scr->io == 0)
+		return;
+	cursornm = KADDR(scr->io);
+
+	cursornm->enable = 0;
+	memmove(&scr->Cursor, curs, sizeof(Cursor));
+	neomagicinitcursor(scr, 0, 0, 0);
+	cursornm->enable = 1;
+}
+
+static int
+neomagiccurmove(VGAscr* scr, Point p)
+{
+	CursorNM *cursornm;
+	int addr, index, x, xo, y, yo;
+
+	if(scr->io == 0)
+		return 1;
+	cursornm = KADDR(scr->io);
+
+	index = 0;
+	if((x = p.x+scr->offset.x) < 0){
+		xo = -x;
+		x = 0;
+	}
+	else
+		xo = 0;
+	if((y = p.y+scr->offset.y) < 0){
+		yo = -y;
+		y = 0;
+	}
+	else
+		yo = 0;
+
+	if(xo || yo){
+		index = 1;
+		neomagicinitcursor(scr, xo, yo, index);
+	}
+	addr = ((scr->storage+(1024*index))>>10) & 0xFFF;
+	addr = ((addr & 0x00F)<<8)|((addr>>4) & 0xFF);
+	if(cursornm->addr != addr)
+		cursornm->addr = addr;
+
+	cursornm->x = x;
+	cursornm->y = y;
+
+	return 0;
+}
+
+static void
+neomagiccurenable(VGAscr* scr)
+{
+	CursorNM *cursornm;
+
+	neomagicenable(scr);
+	if(scr->io == 0)
+		return;
+	cursornm = KADDR(scr->io);
+	cursornm->enable = 0;
+
+	/*
+	 * Cursor colours.
+	 */
+	cursornm->colour1 = (Pblack<<16)|(Pblack<<8)|Pblack;
+	cursornm->colour2 = (Pwhite<<16)|(Pwhite<<8)|Pwhite;
+
+	/*
+	 * Load, locate and enable the 64x64 cursor.
+	 */
+	neomagiccurload(scr, &arrow);
+	neomagiccurmove(scr, ZP);
+	cursornm->enable = 1;
+}
+
+static int neomagicbltflags;
+
+/* registers */
+enum {
+	BltStat = 0,
+	BltCntl = 1,
+	XPColor = 2,
+	FGColor = 3,
+	BGColor = 4,
+	Pitch = 5,
+	ClipLT = 6,
+	ClipRB = 7,
+	SrcBitOff = 8,
+	SrcStartOff = 9,
+
+	DstStartOff = 11,
+	XYExt = 12,
+
+	PageCntl = 20,
+	PageBase,
+	PostBase,
+	PostPtr,
+	DataPtr,
+};
+
+/* flags */
+enum {
+	NEO_BS0_BLT_BUSY =	0x00000001,
+	NEO_BS0_FIFO_AVAIL =	0x00000002,
+	NEO_BS0_FIFO_PEND =	0x00000004,
+
+	NEO_BC0_DST_Y_DEC =	0x00000001,
+	NEO_BC0_X_DEC =		0x00000002,
+	NEO_BC0_SRC_TRANS =	0x00000004,
+	NEO_BC0_SRC_IS_FG =	0x00000008,
+	NEO_BC0_SRC_Y_DEC =	0x00000010,
+	NEO_BC0_FILL_PAT =	0x00000020,
+	NEO_BC0_SRC_MONO =	0x00000040,
+	NEO_BC0_SYS_TO_VID =	0x00000080,
+
+	NEO_BC1_DEPTH8 =	0x00000100,
+	NEO_BC1_DEPTH16 =	0x00000200,
+	NEO_BC1_DEPTH24 =	0x00000300,
+	NEO_BC1_X_320 =		0x00000400,
+	NEO_BC1_X_640 =		0x00000800,
+	NEO_BC1_X_800 =		0x00000c00,
+	NEO_BC1_X_1024 =		0x00001000,
+	NEO_BC1_X_1152 =		0x00001400,
+	NEO_BC1_X_1280 =		0x00001800,
+	NEO_BC1_X_1600 =		0x00001c00,
+	NEO_BC1_DST_TRANS =	0x00002000,
+	NEO_BC1_MSTR_BLT =	0x00004000,
+	NEO_BC1_FILTER_Z =	0x00008000,
+
+	NEO_BC2_WR_TR_DST =	0x00800000,
+
+	NEO_BC3_SRC_XY_ADDR =	0x01000000,
+	NEO_BC3_DST_XY_ADDR =	0x02000000,
+	NEO_BC3_CLIP_ON =		0x04000000,
+	NEO_BC3_FIFO_EN =		0x08000000,
+	NEO_BC3_BLT_ON_ADDR =	0x10000000,
+	NEO_BC3_SKIP_MAPPING =	0x80000000,
+
+	NEO_MODE1_DEPTH8 =			0x0100,
+	NEO_MODE1_DEPTH16 =			0x0200,
+	NEO_MODE1_DEPTH24 =			0x0300,
+	NEO_MODE1_X_320 =			0x0400,
+	NEO_MODE1_X_640 =			0x0800,
+	NEO_MODE1_X_800 =			0x0c00,
+	NEO_MODE1_X_1024 =			0x1000,
+	NEO_MODE1_X_1152 =			0x1400,
+	NEO_MODE1_X_1280 =			0x1800,
+	NEO_MODE1_X_1600 =			0x1c00,
+	NEO_MODE1_BLT_ON_ADDR =	0x2000,
+};
+
+/* Raster Operations */
+enum {
+	GXclear =			0x000000,	/* 0x0000 */
+	GXand =			0x080000,	/* 0x1000 */
+	GXandReverse =	0x040000,	/* 0x0100 */
+	GXcopy =			0x0c0000,	/* 0x1100 */
+	GXandInvert =		0x020000,	/* 0x0010 */
+	GXnoop =			0x0a0000,	/* 0x1010 */
+	GXxor =			0x060000,	/* 0x0110 */
+	GXor =			0x0e0000,	/* 0x1110 */
+	GXnor =			0x010000,	/* 0x0001 */
+	GXequiv =			0x090000,	/* 0x1001 */
+	GXinvert =		0x050000,	/* 0x0101 */
+	GXorReverse =		0x0d0000,	/* 0x1101 */
+	GXcopyInvert =		0x030000,	/* 0x0011 */
+	GXorInverted =		0x0b0000,	/* 0x1011 */
+	GXnand =			0x070000,	/* 0x0111 */
+	GXset =			0x0f0000,	/* 0x1111 */
+};
+
+static void
+waitforidle(VGAscr *scr)
+{
+	ulong *mmio;
+	long x;
+
+	mmio = scr->mmio;
+	x = 0;
+	while((mmio[BltStat] & NEO_BS0_BLT_BUSY) && x++ < 1000000)
+		;
+	//if(x >= 1000000)
+	//	iprint("idle stat %lud scrmmio %.8lux scr %p pc %luX\n", mmio[BltStat], scr->mmio, scr, getcallerpc(&scr));
+}
+
+static void
+waitforfifo(VGAscr *scr, int entries)
+{
+	ulong *mmio;
+	long x;
+
+	mmio = scr->mmio;
+	x = 0;
+	while(((mmio[BltStat]>>8) < entries) && x++ < 1000000)
+		;
+	//if(x >= 1000000)
+	//	iprint("fifo stat %d scrmmio %.8lux scr %p pc %luX\n", mmio[BltStat]>>8, scr->mmio, scr, getcallerpc(&scr));
+	/* DirectFB says the above doesn't work.  if so... */
+	/* waitforidle(scr); */
+}
+
+static int
+neomagichwfill(VGAscr *scr, Rectangle r, ulong sval)
+{
+	ulong *mmio;
+
+	mmio = scr->mmio;
+
+	waitforfifo(scr, 1);
+	mmio[FGColor] = sval;
+	waitforfifo(scr, 3);
+	mmio[BltCntl] = neomagicbltflags
+		| NEO_BC3_FIFO_EN
+		| NEO_BC0_SRC_IS_FG
+		| NEO_BC3_SKIP_MAPPING
+		| GXcopy;
+	mmio[DstStartOff] = scr->aperture
+		+ r.min.y*scr->gscreen->width*BY2WD
+		+ r.min.x*scr->gscreen->depth/BI2BY;
+	mmio[XYExt] = (Dy(r) << 16) | (Dx(r) & 0xffff);
+	waitforidle(scr);
+	return 1;
+}
+
+static int
+neomagichwscroll(VGAscr *scr, Rectangle r, Rectangle sr)
+{
+	ulong *mmio;
+	int pitch, pixel;
+
+	mmio = scr->mmio;
+
+	pitch = scr->gscreen->width*BY2WD;
+	pixel = scr->gscreen->depth/BI2BY;
+
+	waitforfifo(scr, 4);
+	if (r.min.y < sr.min.y || (r.min.y == sr.min.y && r.min.x < sr.min.x)) {
+		/* start from upper-left */
+		mmio[BltCntl] = neomagicbltflags
+			| NEO_BC3_FIFO_EN
+			| NEO_BC3_SKIP_MAPPING
+			| GXcopy;
+		mmio[SrcStartOff] = scr->aperture
+			+ sr.min.y*pitch + sr.min.x*pixel;
+		mmio[DstStartOff] = scr->aperture
+			+ r.min.y*pitch + r.min.x*pixel;
+	} else {
+		/* start from lower-right */
+		mmio[BltCntl] = neomagicbltflags
+			| NEO_BC0_X_DEC
+			| NEO_BC0_DST_Y_DEC
+			| NEO_BC0_SRC_Y_DEC
+			| NEO_BC3_FIFO_EN
+			| NEO_BC3_SKIP_MAPPING
+			| GXcopy;
+		mmio[SrcStartOff] = scr->aperture
+			+ (sr.max.y-1)*pitch + (sr.max.x-1)*pixel;
+		mmio[DstStartOff] = scr->aperture
+			+ (r.max.y-1)*pitch + (r.max.x-1)*pixel;
+	}
+	mmio[XYExt] = (Dy(r) << 16) | (Dx(r) & 0xffff);
+	waitforidle(scr);
+	return 1;
+}
+
+static void
+neomagicdrawinit(VGAscr *scr)
+{
+	ulong *mmio;
+	uint bltmode, pitch;
+
+	mmio = scr->mmio;
+
+	pitch = scr->gscreen->width*BY2WD;
+
+	neomagicbltflags = bltmode = 0;
+
+	switch(scr->gscreen->depth) {
+	case 8:
+		bltmode |= NEO_MODE1_DEPTH8;
+		neomagicbltflags |= NEO_BC1_DEPTH8;
+		break;
+	case 16:
+		bltmode |= NEO_MODE1_DEPTH16;
+		neomagicbltflags |= NEO_BC1_DEPTH16;
+		break;
+	case 24:	/* I can't get it to work, and XFree86 doesn't either. */
+	default:	/* give up */
+		return;
+	}
+
+	switch(Dx(scr->gscreen->r)) {
+	case 320:
+		bltmode |= NEO_MODE1_X_320;
+		neomagicbltflags |= NEO_BC1_X_320;
+		break;
+	case 640:
+		bltmode |= NEO_MODE1_X_640;
+		neomagicbltflags |= NEO_BC1_X_640;
+		break;
+	case 800:
+		bltmode |= NEO_MODE1_X_800;
+		neomagicbltflags |= NEO_BC1_X_800;
+		break;
+	case 1024:
+		bltmode |= NEO_MODE1_X_1024;
+		neomagicbltflags |= NEO_BC1_X_1024;
+		break;
+	case 1152:
+		bltmode |= NEO_MODE1_X_1152;
+		neomagicbltflags |= NEO_BC1_X_1152;
+		break;
+	case 1280:
+		bltmode |= NEO_MODE1_X_1280;
+		neomagicbltflags |= NEO_BC1_X_1280;
+		break;
+	case 1600:
+		bltmode |= NEO_MODE1_X_1600;
+		neomagicbltflags |= NEO_BC1_X_1600;
+		break;
+	default:
+		/* don't worry about it */
+		break;
+	}
+
+	waitforidle(scr);
+	mmio[BltStat] = bltmode << 16;
+	mmio[Pitch] = (pitch << 16) | (pitch & 0xffff);
+
+	scr->fill = neomagichwfill;
+	scr->scroll = neomagichwscroll;
+}
+
+VGAdev vganeomagicdev = {
+	"neomagic",
+
+	neomagicenable,
+	nil,
+	nil,
+	neomagiclinear,
+	neomagicdrawinit,
+};
+
+VGAcur vganeomagiccur = {
+	"neomagichwgc",
+
+	neomagiccurenable,
+	neomagiccurdisable,
+	neomagiccurload,
+	neomagiccurmove,
+};
+
--- /dev/null
+++ b/os/pc/vganvidia.c
@@ -1,0 +1,373 @@
+#include "u.h"
+#include "../port/lib.h"
+#include "mem.h"
+#include "dat.h"
+#include "fns.h"
+#include "io.h"
+#include "../port/error.h"
+
+#define	Image	IMAGE
+#include <draw.h>
+#include <memdraw.h>
+#include <cursor.h>
+#include "screen.h"
+
+enum {
+	Pramin = 0x00710000,
+	Pramdac = 0x00680000,
+	Fifo = 0x00800000,
+	Pgraph = 0x00400000
+};
+
+enum {
+	hwCurPos = Pramdac + 0x0300,
+	hwCurImage = Pramin + (0x00010000 - 0x0800),
+};
+
+/* Nvidia is good about backwards compatibility -- any did >= 0x20 is fine */
+static Pcidev*
+nvidiapci(void)
+{
+	Pcidev *p;
+
+	p = nil;
+	while((p = pcimatch(p, 0x10DE, 0)) != nil){
+		if(p->did >= 0x20 && p->ccrb == 3)	/* video card */
+			return p;
+	}
+	return nil;
+}
+
+static ulong
+nvidialinear(VGAscr* scr, int* size, int* align)
+{
+	Pcidev *p;
+	int oapsize, wasupamem;
+	ulong aperture, oaperture;
+
+	oaperture = scr->aperture;
+	oapsize = scr->apsize;
+	wasupamem = scr->isupamem;
+
+	aperture = 0;
+	if(p = nvidiapci()){
+		aperture = p->mem[1].bar & ~0x0F;
+		*size = p->mem[1].size;
+	}
+
+	if(wasupamem){
+		if(oaperture == aperture)
+			return oaperture;
+		upafree(oaperture, oapsize);
+	}
+	scr->isupamem = 0;
+
+	aperture = upamalloc(aperture, *size, *align);
+	if(aperture == 0){
+		if(wasupamem && upamalloc(oaperture, oapsize, 0)){
+			aperture = oaperture;
+			scr->isupamem = 1;
+		}
+		else
+			scr->isupamem = 0;
+	}
+	else
+		scr->isupamem = 1;
+
+	return aperture;
+}
+
+static void
+nvidiaenable(VGAscr* scr)
+{
+	Pcidev *p;
+	ulong aperture;
+	int align, size;
+
+	/*
+	 * Only once, can't be disabled for now.
+	 * scr->io holds the physical address of
+	 * the MMIO registers.
+	 */
+	if(scr->io)
+		return;
+	p = nvidiapci();
+	if(p == nil)
+		return;
+	scr->id = p->did;
+
+	scr->io = upamalloc(p->mem[0].bar & ~0x0F, p->mem[0].size, 0);
+	if(scr->io == 0)
+		return;
+	addvgaseg("nvidiammio", scr->io, p->mem[0].size);
+
+	size = p->mem[1].size;
+	align = 0;
+	aperture = nvidialinear(scr, &size, &align);
+	if(aperture){
+		scr->aperture = aperture;
+		scr->apsize = size;
+		addvgaseg("nvidiascreen", aperture, size);
+	}
+}
+
+static void
+nvidiacurdisable(VGAscr* scr)
+{
+	if(scr->io == 0)
+		return;
+
+	vgaxo(Crtx, 0x31, vgaxi(Crtx, 0x31) & ~0x01);
+}
+
+static void
+nvidiacurload(VGAscr* scr, Cursor* curs)
+{
+	ulong*	p;
+	int	i,j;
+	ushort	c,s;
+	ulong	tmp;
+
+	if(scr->io == 0)
+		return;
+
+	vgaxo(Crtx, 0x31, vgaxi(Crtx, 0x31) & ~0x01);
+
+	p = KADDR(scr->io + hwCurImage);
+
+	for(i=0; i<16; i++) {
+		switch(scr->id){
+		default:
+			c = (curs->clr[2 * i] << 8) | curs->clr[2 * i+1];
+			s = (curs->set[2 * i] << 8) | curs->set[2 * i+1];
+			break;
+		case 0x171:		/* for Geforece4 MX bug, K.Okamoto */
+		case 0x181:
+			c = (curs->clr[2 * i+1] << 8) | curs->clr[2 * i];
+			s = (curs->set[2 * i+1] << 8) | curs->set[2 * i];
+			break;
+		}
+		tmp = 0;
+		for (j=0; j<16; j++){
+			if(s&0x8000)
+				tmp |= 0x80000000;
+			else if(c&0x8000)
+				tmp |= 0xFFFF0000;
+			if (j&0x1){
+				*p++ = tmp;
+				tmp = 0;
+			} else {
+				tmp>>=16;
+			}
+			c<<=1;
+			s<<=1;
+		}
+		for (j=0; j<8; j++)
+			*p++ = 0;
+	}
+	for (i=0; i<256; i++)
+		*p++ = 0;
+
+	scr->offset = curs->offset;
+	vgaxo(Crtx, 0x31, vgaxi(Crtx, 0x31) | 0x01);
+
+	return;
+}
+
+static int
+nvidiacurmove(VGAscr* scr, Point p)
+{
+	ulong*	cursorpos;
+
+	if(scr->io == 0)
+		return 1;
+
+	cursorpos = KADDR(scr->io + hwCurPos);
+	*cursorpos = ((p.y+scr->offset.y)<<16)|((p.x+scr->offset.x) & 0xFFFF);
+
+	return 0;
+}
+
+static void
+nvidiacurenable(VGAscr* scr)
+{
+	nvidiaenable(scr);
+	if(scr->io == 0)
+		return;
+
+	vgaxo(Crtx, 0x1F, 0x57);
+
+	nvidiacurload(scr, &arrow);
+	nvidiacurmove(scr, ZP);
+
+	vgaxo(Crtx, 0x31, vgaxi(Crtx, 0x31) | 0x01);
+}
+
+enum {
+	RopFifo 		= 0x00000000, 
+	ClipFifo 		= 0x00002000,
+	PattFifo 		= 0x00004000,
+	BltFifo 		= 0x00008000,
+	BitmapFifo 	= 0x0000A000,
+};
+
+enum {
+	RopRop3 = RopFifo + 0x300,
+
+	ClipTopLeft = ClipFifo + 0x300,
+	ClipWidthHeight = ClipFifo + 0x304,
+
+	PattShape = PattFifo + 0x0308,
+	PattColor0 = PattFifo + 0x0310,
+	PattColor1 = PattFifo + 0x0314,
+	PattMonochrome0 = PattFifo + 0x0318,
+	PattMonochrome1 = PattFifo + 0x031C,
+
+	BltTopLeftSrc = BltFifo + 0x0300,
+	BltTopLeftDst = BltFifo + 0x0304,
+	BltWidthHeight = BltFifo + 0x0308,
+
+	BitmapColor1A = BitmapFifo + 0x03FC,
+	BitmapURect0TopLeft = BitmapFifo + 0x0400,
+	BitmapURect0WidthHeight = BitmapFifo + 0x0404,
+};
+
+static void
+waitforidle(VGAscr *scr)
+{
+	ulong*	pgraph;
+	int x;
+
+	pgraph = KADDR(scr->io + Pgraph);
+
+	x = 0;
+	while(pgraph[0x00000700/4] & 0x01 && x++ < 1000000)
+		;
+
+	if(x >= 1000000)
+		iprint("idle stat %lud scrio %.8lux scr %p pc %luX\n", *pgraph, scr->io, scr, getcallerpc(&scr));
+}
+
+static void
+waitforfifo(VGAscr *scr, int fifo, int entries)
+{
+	ushort* fifofree;
+	int x;
+
+	x = 0;
+	fifofree = KADDR(scr->io + Fifo + fifo + 0x10);
+
+	while(((*fifofree >> 2) < entries) && x++ < 1000000)
+		;
+
+	if(x >= 1000000)
+		iprint("fifo stat %d scrio %.8lux scr %p pc %luX\n", *fifofree, scr->io, scr, getcallerpc(&scr));
+}
+
+static int
+nvidiahwfill(VGAscr *scr, Rectangle r, ulong sval)
+{
+	ulong*	fifo;
+
+	fifo = KADDR(scr->io + Fifo);
+
+	waitforfifo(scr, BitmapFifo, 1);
+
+	fifo[BitmapColor1A/4] = sval;
+
+	waitforfifo(scr, BitmapFifo, 2);
+
+	fifo[BitmapURect0TopLeft/4] = (r.min.x << 16) | r.min.y;
+	fifo[BitmapURect0WidthHeight/4] = (Dx(r) << 16) | Dy(r);
+
+	waitforidle(scr);
+
+	return 1;
+}
+
+static int
+nvidiahwscroll(VGAscr *scr, Rectangle r, Rectangle sr)
+{
+	ulong*	fifo;
+
+	fifo = KADDR(scr->io + Fifo);
+
+	waitforfifo(scr, BltFifo, 3);
+
+	fifo[BltTopLeftSrc/4] = (sr.min.y << 16) | sr.min.x;
+	fifo[BltTopLeftDst/4] = (r.min.y << 16) | r.min.x;
+	fifo[BltWidthHeight/4] = (Dy(r) << 16) | Dx(r);
+
+	waitforidle(scr);
+
+	return 1;
+}
+
+void
+nvidiablank(VGAscr*, int blank)
+{
+	uchar seq1, crtc1A;
+
+	seq1 = vgaxi(Seqx, 1) & ~0x20;
+	crtc1A = vgaxi(Crtx, 0x1A) & ~0xC0;
+
+	if(blank){
+		seq1 |= 0x20;
+//		crtc1A |= 0xC0;
+		crtc1A |= 0x80;
+	}
+
+	vgaxo(Seqx, 1, seq1);
+	vgaxo(Crtx, 0x1A, crtc1A);
+}
+
+static void
+nvidiadrawinit(VGAscr *scr)
+{
+	ulong*	fifo;
+
+	fifo = KADDR(scr->io + Fifo);
+
+	waitforfifo(scr, ClipFifo, 2);
+
+	fifo[ClipTopLeft/4] = 0x0;
+	fifo[ClipWidthHeight/4] = 0x80008000;
+
+	waitforfifo(scr, PattFifo, 5);
+
+	fifo[PattShape/4] = 0;
+	fifo[PattColor0/4] = 0xffffffff;
+	fifo[PattColor1/4] = 0xffffffff;
+	fifo[PattMonochrome0/4] = 0xffffffff;
+	fifo[PattMonochrome1/4] = 0xffffffff;
+
+	waitforfifo(scr, RopFifo, 1);
+
+	fifo[RopRop3/4] = 0xCC;
+
+	waitforidle(scr);
+
+	scr->blank = nvidiablank;
+	hwblank = 1;
+	scr->fill = nvidiahwfill;
+	scr->scroll = nvidiahwscroll;
+}
+
+VGAdev vganvidiadev = {
+	"nvidia",
+
+	nvidiaenable,
+	nil,
+	nil,
+	nvidialinear,
+	nvidiadrawinit,
+};
+
+VGAcur vganvidiacur = {
+	"nvidiahwgc",
+
+	nvidiacurenable,
+	nvidiacurdisable,
+	nvidiacurload,
+	nvidiacurmove,
+};
--- /dev/null
+++ b/os/pc/vgargb524.c
@@ -1,0 +1,236 @@
+#include "u.h"
+#include "../port/lib.h"
+#include "mem.h"
+#include "dat.h"
+#include "fns.h"
+#include "../port/error.h"
+
+#define	Image	IMAGE
+#include <draw.h>
+#include <memdraw.h>
+#include <cursor.h>
+#include "screen.h"
+
+/*
+ * IBM RGB524.
+ * 170/220MHz High Performance Palette DAC.
+ *
+ * Assumes hooked up to an S3 Vision96[48].
+ */
+enum {
+	IndexLo		= 0x00,
+	IndexHi		= 0x01,
+	Data		= 0x02,
+	IndexCtl	= 0x03,
+};
+
+enum {						/* index registers */
+	CursorCtl	= 0x30,
+	CursorXLo	= 0x31,
+	CursorXHi	= 0x32,
+	CursorYLo	= 0x33,
+	CursorYHi	= 0x34,
+	CursorHotX	= 0x35,
+	CursorHotY	= 0x36,
+
+	CursorR1	= 0x40,
+	CursorG1	= 0x41,
+	CursorB1	= 0x42,
+	CursorR2	= 0x43,
+	CursorG2	= 0x44,
+	CursorB2	= 0x45,
+	CursorR3	= 0x46,
+	CursorG3	= 0x47,
+	CursorB3	= 0x48,
+
+	CursorArray	= 0x100,
+};
+
+/*
+ * Lower 2-bits of indirect DAC register
+ * addressing.
+ */
+static ushort dacxreg[4] = {
+	PaddrW, Pdata, Pixmask, PaddrR
+};
+
+static uchar
+rgb524setrs2(void)
+{
+	uchar rs2;
+
+	rs2 = vgaxi(Crtx, 0x55);
+	vgaxo(Crtx, 0x55, (rs2 & 0xFC)|0x01);
+
+	return rs2;
+}
+
+static void
+rgb524xo(int index, uchar data)
+{
+	vgao(dacxreg[IndexLo], index & 0xFF);
+	vgao(dacxreg[IndexHi], (index>>8) & 0xFF);
+	vgao(dacxreg[Data], data);
+}
+
+static void
+rgb524disable(VGAscr*)
+{
+	uchar rs2;
+
+	rs2 = rgb524setrs2();
+	rgb524xo(CursorCtl, 0x00);
+	vgaxo(Crtx, 0x55, rs2);
+}
+
+static void
+rgb524enable(VGAscr*)
+{
+	uchar rs2;
+
+	rs2 = rgb524setrs2();
+
+	/*
+	 * Make sure cursor is off by initialising the cursor
+	 * control to defaults.
+	 */
+	rgb524xo(CursorCtl, 0x00);
+
+	/*
+	 * Cursor colour 1 (white),
+	 * cursor colour 2 (black).
+	 */
+	rgb524xo(CursorR1, Pwhite); rgb524xo(CursorG1, Pwhite); rgb524xo(CursorB1, Pwhite);
+	rgb524xo(CursorR2, Pblack); rgb524xo(CursorG2, Pblack); rgb524xo(CursorB2, Pblack);
+
+	/*
+	 * Enable the cursor, 32x32, mode 2.
+	 */
+	rgb524xo(CursorCtl, 0x23);
+
+	vgaxo(Crtx, 0x55, rs2);
+}
+
+static void
+rgb524load(VGAscr*, Cursor* curs)
+{
+	uchar p, p0, p1, rs2;
+	int x, y;
+
+	rs2 = rgb524setrs2();
+
+	/*
+	 * Make sure cursor is off by initialising the cursor
+	 * control to defaults.
+	 */
+	rgb524xo(CursorCtl, 0x00);
+
+	/*
+	 * Set auto-increment mode for index-register addressing
+	 * and initialise the cursor array index.
+	 */
+	vgao(dacxreg[IndexCtl], 0x01);
+	vgao(dacxreg[IndexLo], CursorArray & 0xFF);
+	vgao(dacxreg[IndexHi], (CursorArray>>8) & 0xFF);
+
+	/*
+	 * Initialise the 32x32 cursor RAM array. There are 2 planes,
+	 * p0 and p1. Data is written 4 pixels per byte, with p1 the
+	 * MS bit of each pixel.
+	 * The cursor is set in X-Windows mode which gives the following
+	 * truth table:
+	 *	p1 p0	colour
+	 *	 0  0	underlying pixel colour
+	 *	 0  1	underlying pixel colour
+	 *	 1  0	cursor colour 1
+	 *	 1  1	cursor colour 2
+	 * Put the cursor into the top-left of the 32x32 array.
+	 */
+	for(y = 0; y < 32; y++){
+		for(x = 0; x < 32/8; x++){
+			if(x < 16/8 && y < 16){
+				p0 = curs->clr[x+y*2];
+				p1 = curs->set[x+y*2];
+
+				p = 0x00;
+				if(p1 & 0x80)
+					p |= 0xC0;
+				else if(p0 & 0x80)
+					p |= 0x80;
+				if(p1 & 0x40)
+					p |= 0x30;
+				else if(p0 & 0x40)
+					p |= 0x20;
+				if(p1 & 0x20)
+					p |= 0x0C;
+				else if(p0 & 0x20)
+					p |= 0x08;
+				if(p1 & 0x10)
+					p |= 0x03;
+				else if(p0 & 0x10)
+					p |= 0x02;
+				vgao(dacxreg[Data], p);
+
+				p = 0x00;
+				if(p1 & 0x08)
+					p |= 0xC0;
+				else if(p0 & 0x08)
+					p |= 0x80;
+				if(p1 & 0x04)
+					p |= 0x30;
+				else if(p0 & 0x04)
+					p |= 0x20;
+				if(p1 & 0x02)
+					p |= 0x0C;
+				else if(p0 & 0x02)
+					p |= 0x08;
+				if(p1 & 0x01)
+					p |= 0x03;
+				else if(p0 & 0x01)
+					p |= 0x02;
+				vgao(dacxreg[Data], p);
+			}
+			else{
+				vgao(dacxreg[Data], 0x00);
+				vgao(dacxreg[Data], 0x00);
+			}
+		}
+	}
+
+	/*
+	 * Initialise the cursor hotpoint,
+	 * enable the cursor and restore state.
+	 */
+	rgb524xo(CursorHotX, -curs->offset.x);
+	rgb524xo(CursorHotY, -curs->offset.y);
+
+	rgb524xo(CursorCtl, 0x23);
+
+	vgaxo(Crtx, 0x55, rs2);
+}
+
+static int
+rgb524move(VGAscr*, Point p)
+{
+	uchar rs2;
+
+	rs2 = rgb524setrs2();
+
+	rgb524xo(CursorXLo, p.x & 0xFF);
+	rgb524xo(CursorXHi, (p.x>>8) & 0x0F);
+	rgb524xo(CursorYLo, p.y & 0xFF);
+	rgb524xo(CursorYHi, (p.y>>8) & 0x0F);
+
+	vgaxo(Crtx, 0x55, rs2);
+
+	return 0;
+}
+
+VGAcur vgargb524cur = {
+	"rgb524hwgc",
+
+	rgb524enable,
+	rgb524disable,
+	rgb524load,
+	rgb524move,
+};
--- /dev/null
+++ b/os/pc/vgas3.c
@@ -1,0 +1,620 @@
+#include "u.h"
+#include "../port/lib.h"
+#include "mem.h"
+#include "dat.h"
+#include "fns.h"
+#include "io.h"
+#include "../port/error.h"
+
+#define	Image	IMAGE
+#include <draw.h>
+#include <memdraw.h>
+#include <cursor.h>
+#include "screen.h"
+
+enum {
+	PCIS3		= 0x5333,		/* PCI VID */
+
+	SAVAGE3D	= 0x8A20,		/* PCI DID */
+	SAVAGE3DMV	= 0x8A21,
+	SAVAGE4		= 0x8A22,
+	PROSAVAGEP	= 0x8A25,
+	PROSAVAGEK	= 0x8A26,
+	PROSAVAGE8	= 0x8D04,
+	SAVAGEMXMV	= 0x8C10,
+	SAVAGEMX	= 0x8C11,
+	SAVAGEIXMV	= 0x8C12,
+	SAVAGEIX	= 0x8C13,
+	SUPERSAVAGEIXC16 = 0x8C2E,
+	SAVAGE2000	= 0x9102,
+
+	VIRGE		= 0x5631,
+	VIRGEGX2	= 0x8A10,
+	VIRGEDXGX	= 0x8A01,
+	VIRGEVX		= 0x883D,
+	VIRGEMX		= 0x8C01,
+	VIRGEMXP	= 0x8C03,
+
+	VIRTUALPC2004	= 0x8810,
+	AURORA64VPLUS	= 0x8812,
+};
+
+static int
+s3pageset(VGAscr* scr, int page)
+{
+	uchar crt35, crt51;
+	int opage;
+
+	crt35 = vgaxi(Crtx, 0x35);
+	if(scr->gscreen->depth >= 8){
+		/*
+		 * The S3 registers need to be unlocked for this.
+		 * Let's hope they are already:
+		 *	vgaxo(Crtx, 0x38, 0x48);
+		 *	vgaxo(Crtx, 0x39, 0xA0);
+		 *
+		 * The page is 6 bits, the lower 4 bits in Crt35<3:0>,
+		 * the upper 2 in Crt51<3:2>.
+		 */
+		vgaxo(Crtx, 0x35, page & 0x0F);
+		crt51 = vgaxi(Crtx, 0x51);
+		vgaxo(Crtx, 0x51, (crt51 & ~0x0C)|((page & 0x30)>>2));
+		opage = ((crt51 & 0x0C)<<2)|(crt35 & 0x0F);
+	}
+	else{
+		vgaxo(Crtx, 0x35, (page<<2) & 0x0C);
+		opage = (crt35>>2) & 0x03;
+	}
+
+	return opage;
+}
+
+static void
+s3page(VGAscr* scr, int page)
+{
+	int id;
+
+	id = (vgaxi(Crtx, 0x2D)<<8)|vgaxi(Crtx, 0x2E);
+	switch(id){
+
+	case VIRGEGX2:
+		break;
+
+	default:
+		lock(&scr->devlock);
+		s3pageset(scr, page);
+		unlock(&scr->devlock);
+		break;
+	}
+}
+
+static ulong
+s3linear(VGAscr* scr, int* size, int* align)
+{
+	char *mmioname;
+	ulong aperture, oaperture, mmiobase, mmiosize;
+	int i, id, j, osize, oapsize, wasupamem;
+	Pcidev *p;
+
+	osize = *size;
+	oaperture = scr->aperture;
+	oapsize = scr->apsize;
+	wasupamem = scr->isupamem;
+
+	mmiosize = 0;
+	mmiobase = 0;
+	mmioname = nil;
+
+	/*
+	 * S3 makes cards other than display controllers, so
+	 * look for the first S3 display controller (device class 3)
+	 * and not one of their sound cards.
+	 */
+	p = nil;
+	while(p = pcimatch(p, PCIS3, 0)){
+		if(p->ccrb == 0x03)
+			break;
+	}
+	if(p != nil){
+		for(i=0; i<nelem(p->mem); i++){
+			if(p->mem[i].size >= *size
+			&& ((p->mem[i].bar & ~0x0F) & (*align-1)) == 0)
+				break;
+		}
+		if(i >= nelem(p->mem)){
+			print("vgas3: aperture not found\n");
+			return 0;
+		}
+		aperture = p->mem[i].bar & ~0x0F;
+		*size = p->mem[i].size;
+
+		id = (vgaxi(Crtx, 0x2D)<<8)|vgaxi(Crtx, 0x2E);
+		switch(id){			/* find mmio */
+		case SAVAGE4:
+		case PROSAVAGEP:
+		case PROSAVAGEK:
+		case PROSAVAGE8:
+		case SUPERSAVAGEIXC16:
+			/*
+			 * We could assume that the MMIO registers
+			 * will be in the screen segment and just use
+			 * that, but PCI software is allowed to move them
+			 * if it feels like it, so we look for an aperture of
+			 * the right size; only the first 512k actually means
+			 * anything.  The S3 engineers overestimated how
+			 * much space they would need in the first design.
+			 */
+			for(j=0; j<nelem(p->mem); j++){
+				if(i == j)
+					continue;
+				if(p->mem[j].size==512*1024 || p->mem[j].size==16*1024*1024){
+					mmiobase = p->mem[j].bar & ~0x0F;
+					mmiosize = 512*1024;
+					scr->mmio = (ulong*)upamalloc(mmiobase, mmiosize, 0);
+					mmioname = "savagemmio";
+					break;
+				}
+			}
+			if(mmiosize == 0){
+				print("savage4: mmio not found\n");
+				return 0;
+			}
+		}
+	}else
+		aperture = 0;
+
+	if(wasupamem)
+		upafree(oaperture, oapsize);
+	scr->isupamem = 0;
+
+	aperture = upamalloc(aperture, *size, *align);
+	if(aperture == 0){
+		if(wasupamem && upamalloc(oaperture, oapsize, 0))
+			scr->isupamem = 1;
+	}
+	else
+		scr->isupamem = 1;
+
+	if(oaperture && oaperture != aperture)
+		print("warning (BUG): redefinition of aperture does not change s3screen segment\n");
+	addvgaseg("s3screen", aperture, osize);
+
+	if(mmiosize)
+		addvgaseg(mmioname, mmiobase, mmiosize);
+
+	return aperture;
+}
+
+static void
+s3vsyncactive(void)
+{
+	/*
+	 * Hardware cursor information is fetched from display memory
+	 * during the horizontal blank active time. The 80x chips may hang
+	 * if the cursor is turned on or off during this period.
+	 */
+	while((vgai(Status1) & 0x08) == 0)
+		;
+}
+
+static void
+s3disable(VGAscr*)
+{
+	uchar crt45;
+
+	/*
+	 * Turn cursor off.
+	 */
+	crt45 = vgaxi(Crtx, 0x45) & 0xFE;
+	s3vsyncactive();
+	vgaxo(Crtx, 0x45, crt45);
+}
+
+static void
+s3load(VGAscr* scr, Cursor* curs)
+{
+	uchar *p;
+	int id, dolock, opage, x, y;
+
+	/*
+	 * Disable the cursor and
+	 * set the pointer to the two planes.
+	 */
+	s3disable(scr);
+
+	opage = 0;
+	p = KADDR(scr->aperture);
+	id = (vgaxi(Crtx, 0x2D)<<8)|vgaxi(Crtx, 0x2E);
+	switch(id){
+
+	case VIRTUALPC2004:
+	case VIRGE:
+	case VIRGEDXGX:
+	case VIRGEGX2:
+	case VIRGEVX:	
+	case SAVAGEMXMV:
+	case SAVAGEIXMV:
+	case SAVAGE4:
+	case PROSAVAGEP:
+	case PROSAVAGEK:
+	case PROSAVAGE8:
+	case SUPERSAVAGEIXC16:
+		dolock = 0;
+		p += scr->storage;
+		break;
+
+	default:
+		dolock = 1;
+		lock(&scr->devlock);
+		opage = s3pageset(scr, scr->storage>>16);
+		p += (scr->storage & 0xFFFF);
+		break;
+	}
+
+	/*
+	 * The cursor is set in Microsoft Windows format (the ViRGE/GX2 doesn't
+	 * support the X11 format) which gives the following truth table:
+	 *	and xor	colour
+	 *	 0   0	background colour
+	 *	 0   1	foreground colour
+	 *	 1   0	current screen pixel
+	 *	 1   1	NOT current screen pixel
+	 * Put the cursor into the top-left of the 64x64 array.
+	 *
+	 * The cursor pattern in memory is interleaved words of
+	 * AND and XOR patterns.
+	 */
+	for(y = 0; y < 64; y++){
+		for(x = 0; x < 64/8; x += 2){
+			if(x < 16/8 && y < 16){
+				*p++ = ~(curs->clr[2*y + x]|curs->set[2*y + x]);
+				*p++ = ~(curs->clr[2*y + x+1]|curs->set[2*y + x+1]);
+				*p++ = curs->set[2*y + x];
+				*p++ = curs->set[2*y + x+1];
+			}
+			else {
+				*p++ = 0xFF;
+				*p++ = 0xFF;
+				*p++ = 0x00;
+				*p++ = 0x00;
+			}
+		}
+	}
+
+	if(dolock){
+		s3pageset(scr, opage);
+		unlock(&scr->devlock);
+	}
+
+	/*
+	 * Save the cursor hotpoint and enable the cursor.
+	 */
+	scr->offset = curs->offset;
+	s3vsyncactive();
+	vgaxo(Crtx, 0x45, 0x01);
+}
+
+static int
+s3move(VGAscr* scr, Point p)
+{
+	int x, xo, y, yo;
+
+	/*
+	 * Mustn't position the cursor offscreen even partially,
+	 * or it disappears. Therefore, if x or y is -ve, adjust the
+	 * cursor offset instead.
+	 * There seems to be a bug in that if the offset is 1, the
+	 * cursor doesn't disappear off the left edge properly, so
+	 * round it up to be even.
+	 */
+	if((x = p.x+scr->offset.x) < 0){
+		xo = -x;
+		xo = ((xo+1)/2)*2;
+		x = 0;
+	}
+	else
+		xo = 0;
+	if((y = p.y+scr->offset.y) < 0){
+		yo = -y;
+		y = 0;
+	}
+	else
+		yo = 0;
+
+	vgaxo(Crtx, 0x46, (x>>8) & 0x07);
+	vgaxo(Crtx, 0x47, x & 0xFF);
+	vgaxo(Crtx, 0x49, y & 0xFF);
+	vgaxo(Crtx, 0x4E, xo);
+	vgaxo(Crtx, 0x4F, yo);
+	vgaxo(Crtx, 0x48, (y>>8) & 0x07);
+
+	return 0;
+}
+
+static void
+s3enable(VGAscr* scr)
+{
+	int i;
+	ulong storage;
+
+	s3disable(scr);
+
+	/*
+	 * Cursor colours. Set both the CR0[EF] and the colour
+	 * stack in case we are using a 16-bit RAMDAC.
+	 */
+	vgaxo(Crtx, 0x0E, Pwhite);
+	vgaxo(Crtx, 0x0F, Pblack);
+	vgaxi(Crtx, 0x45);
+
+	for(i = 0; i < 3; i++)
+		vgaxo(Crtx, 0x4A, Pblack);
+	vgaxi(Crtx, 0x45);
+	for(i = 0; i < 3; i++)
+		vgaxo(Crtx, 0x4B, Pwhite);
+
+	/*
+	 * Find a place for the cursor data in display memory.
+	 * Must be on a 1024-byte boundary.
+	 */
+	storage = (scr->gscreen->width*BY2WD*scr->gscreen->r.max.y+1023)/1024;
+	vgaxo(Crtx, 0x4C, storage>>8);
+	vgaxo(Crtx, 0x4D, storage & 0xFF);
+	storage *= 1024;
+	scr->storage = storage;
+
+	/*
+	 * Load, locate and enable the cursor
+	 * in Microsoft Windows format.
+	 */
+	s3load(scr, &arrow);
+	s3move(scr, ZP);
+	vgaxo(Crtx, 0x55, vgaxi(Crtx, 0x55) & ~0x10);
+	s3vsyncactive();
+	vgaxo(Crtx, 0x45, 0x01);
+}
+
+/*
+ * The manual gives byte offsets, but we want ulong offsets, hence /4.
+ */
+enum {
+	SrcBase = 0xA4D4/4,
+	DstBase = 0xA4D8/4,
+	Stride = 0xA4E4/4,
+	FgrdData = 0xA4F4/4,
+	WidthHeight = 0xA504/4,
+	SrcXY = 0xA508/4,
+	DestXY = 0xA50C/4,
+	Command = 0xA500/4,
+	SubStat = 0x8504/4,
+	FifoStat = 0x850C/4,
+};
+
+/*
+ * Wait for writes to VGA memory via linear aperture to flush.
+ */
+enum {Maxloop = 1<<24};
+struct {
+	ulong linear;
+	ulong fifo;
+	ulong idle;
+	ulong lineartimeout;
+	ulong fifotimeout;
+	ulong idletimeout;
+} waitcount;
+
+static void
+waitforlinearfifo(VGAscr *scr)
+{
+	ulong *mmio;
+	long x;
+	static ulong nwaitforlinearfifo;
+	ulong mask, val;
+
+	switch(scr->id){
+	default:
+		panic("unknown scr->id in s3 waitforlinearfifo");
+	case 0x8A01:	/* ViRGE/[DG]X.  XFree86 says no waiting necessary */
+		return;
+	case 0x5631:	/* ViRGE */
+	case 0x883D:	/* ViRGE/VX */
+		mask = 0x0F<<6;
+		val = 0x08<<6;
+		break;
+	case 0x8A10:	/* ViRGE/GX2 */
+		mask = 0x1F<<6;
+		val = 0x10<<6;
+		break;
+	}
+	mmio = scr->mmio;
+	x = 0;
+	while((mmio[FifoStat]&mask) != val && x++ < Maxloop)
+		waitcount.linear++;
+	if(x >= Maxloop)
+		waitcount.lineartimeout++;
+}
+
+static void
+waitforfifo(VGAscr *scr, int entries)
+{
+	ulong *mmio;
+	long x;
+	static ulong nwaitforfifo;
+
+	mmio = scr->mmio;
+	x = 0;
+	while((mmio[SubStat]&0x1F00) < ((entries+2)<<8) && x++ < Maxloop)
+		waitcount.fifo++;
+	if(x >= Maxloop)
+		waitcount.fifotimeout++;
+}
+
+static void
+waitforidle(VGAscr *scr)
+{
+	ulong *mmio;
+	long x;
+
+	mmio = scr->mmio;
+	x = 0;
+	while((mmio[SubStat]&0x3F00) != 0x3000 && x++ < Maxloop)
+		waitcount.idle++;
+	if(x >= Maxloop)
+		waitcount.idletimeout++;
+}
+
+static int
+hwscroll(VGAscr *scr, Rectangle r, Rectangle sr)
+{
+	enum { Bitbltop = 0xCC };	/* copy source */
+	ulong *mmio;
+	ulong cmd, stride;
+	Point dp, sp;
+	int did, d;
+
+	d = scr->gscreen->depth;
+	did = (d-8)/8;
+	cmd = 0x00000020|(Bitbltop<<17)|(did<<2);
+	stride = Dx(scr->gscreen->r)*d/8;
+
+	if(r.min.x <= sr.min.x){
+		cmd |= 1<<25;
+		dp.x = r.min.x;
+		sp.x = sr.min.x;
+	}else{
+		dp.x = r.max.x-1;
+		sp.x = sr.max.x-1;
+	}
+
+	if(r.min.y <= sr.min.y){
+		cmd |= 1<<26;
+		dp.y = r.min.y;
+		sp.y = sr.min.y;
+	}else{
+		dp.y = r.max.y-1;
+		sp.y = sr.max.y-1;
+	}
+
+	mmio = scr->mmio;
+	waitforlinearfifo(scr);
+	waitforfifo(scr, 7);
+	mmio[SrcBase] = scr->aperture;
+	mmio[DstBase] = scr->aperture;
+	mmio[Stride] = (stride<<16)|stride;
+	mmio[WidthHeight] = ((Dx(r)-1)<<16)|Dy(r);
+	mmio[SrcXY] = (sp.x<<16)|sp.y;
+	mmio[DestXY] = (dp.x<<16)|dp.y;
+	mmio[Command] = cmd;
+	waitforidle(scr);
+	return 1;
+}
+
+static int
+hwfill(VGAscr *scr, Rectangle r, ulong sval)
+{
+	enum { Bitbltop = 0xCC };	/* copy source */
+	ulong *mmio;
+	ulong cmd, stride;
+	int did, d;
+
+	d = scr->gscreen->depth;
+	did = (d-8)/8;
+	cmd = 0x16000120|(Bitbltop<<17)|(did<<2);
+	stride = Dx(scr->gscreen->r)*d/8;
+	mmio = scr->mmio;
+	waitforlinearfifo(scr);
+	waitforfifo(scr, 8);
+	mmio[SrcBase] = scr->aperture;
+	mmio[DstBase] = scr->aperture;
+	mmio[DstBase] = scr->aperture;
+	mmio[Stride] = (stride<<16)|stride;
+	mmio[FgrdData] = sval;
+	mmio[WidthHeight] = ((Dx(r)-1)<<16)|Dy(r);
+	mmio[DestXY] = (r.min.x<<16)|r.min.y;
+	mmio[Command] = cmd;
+	waitforidle(scr);
+	return 1;
+}
+
+enum {
+	CursorSyncCtl = 0x0D,	/* in Seqx */
+	VsyncHi = 0x80,
+	VsyncLo = 0x40,
+	HsyncHi = 0x20,
+	HsyncLo = 0x10,
+};
+
+static void
+s3blank(VGAscr*, int blank)
+{
+	uchar x;
+
+	x = vgaxi(Seqx, CursorSyncCtl);
+	x &= ~0xF0;
+	if(blank)
+		x |= VsyncLo | HsyncLo;
+	vgaxo(Seqx, CursorSyncCtl, x);
+}
+
+static void
+s3drawinit(VGAscr *scr)
+{
+	extern void savageinit(VGAscr*);	/* vgasavage.c */
+	ulong id;
+
+	id = (vgaxi(Crtx, 0x2D)<<8)|vgaxi(Crtx, 0x2E);
+	scr->id = id;
+
+	/*
+	 * It's highly likely that other ViRGEs will work without
+	 * change to the driver, with the exception of the size of
+	 * the linear aperture memory write FIFO.  Since we don't
+	 * know that size, I'm not turning them on.  See waitforlinearfifo
+	 * above.
+	 */
+	scr->blank = s3blank;
+	/* hwblank = 1;		not known to work well */
+
+	switch(id){
+	case VIRGE:
+	case VIRGEVX:
+	case VIRGEGX2:
+		scr->mmio = (ulong*)(scr->aperture+0x1000000);
+		scr->fill = hwfill;
+		scr->scroll = hwscroll;
+		break;
+	case SAVAGEMXMV:
+	case SAVAGEIXMV:
+		scr->mmio = (ulong*)(scr->aperture+0x1000000);
+		savageinit(scr);	
+		break;
+	case SUPERSAVAGEIXC16:
+	case SAVAGE4:
+	case PROSAVAGEP:
+	case PROSAVAGE8:
+	case PROSAVAGEK:
+		/* scr->mmio is set by s3linear */
+		savageinit(scr);
+		break;
+	}
+}
+
+VGAdev vgas3dev = {
+	"s3",
+
+	0,
+	0,
+	s3page,
+	s3linear,
+	s3drawinit,
+};
+
+VGAcur vgas3cur = {
+	"s3hwgc",
+
+	s3enable,
+	s3disable,
+	s3load,
+	s3move,
+};
+
--- /dev/null
+++ b/os/pc/vgasavage.c
@@ -1,0 +1,571 @@
+#include "u.h"
+#include "../port/lib.h"
+#include "mem.h"
+#include "dat.h"
+#include "fns.h"
+#include "io.h"
+#include "../port/error.h"
+
+#define	Image	IMAGE
+#include <draw.h>
+#include <memdraw.h>
+#include <cursor.h>
+#include "screen.h"
+
+enum {
+	PCIS3		= 0x5333,		/* PCI VID */
+
+	SAVAGE3D	= 0x8A20,	/* PCI DID */
+	SAVAGE3DMV	= 0x8A21,
+	SAVAGE4		= 0x8A22,
+	PROSAVAGEP	= 0x8A25,
+	PROSAVAGEK	= 0x8A26,
+	PROSAVAGE8	= 0x8D04,
+	SAVAGEMXMV	= 0x8C10,
+	SAVAGEMX	= 0x8C11,
+	SAVAGEIXMV	= 0x8C12,
+	SAVAGEIX	= 0x8C13,
+	SUPERSAVAGEIXC16 = 0x8C2E,
+	SAVAGE2000	= 0x9102,
+
+	VIRGE		= 0x5631,
+	VIRGEGX2	= 0x8A10,
+	VIRGEDXGX	= 0x8A01,
+	VIRGEVX		= 0x883D,
+	VIRGEMX		= 0x8C01,
+	VIRGEMXP	= 0x8C03,
+
+	AURORA64VPLUS	= 0x8812,
+};
+
+/*
+ * Savage4 et al. acceleration.
+ *
+ * This is based only on the Savage4 documentation.
+ * It is expected to work on other Savage cards as well,
+ * but has not been tried.
+ * 
+ * There are five ways to access the 2D graphics engine registers:
+ * 	- Old MMIO non-packed format
+ *	- Old MMIO packed format
+ *	- New MMIO non-packed format
+ *	- New MMIO packed format
+ *	- Burst Command Interface (BCI)
+ *
+ * Of these, the manual hints that the first three are deprecated,
+ * and it does not document any of those three well enough to use.
+ * 
+ * I have tried for many hours with no success to understand the BCI
+ * interface well enough to use it.  It is not well documented, and the
+ * XFree86 driver seems to completely contradict what little documentation
+ * there is.
+ *
+ * This leaves the packed new MMIO.
+ * The manual contradicts itself here, claming that the registers
+ * start at 0x2008100 as well as at 0x0008100 from the base of the 
+ * mmio segment.  Since the segment is only 512k, we assume that
+ * the latter is the correct offset.
+ *
+ * According to the manual, only 16-bit reads of the 2D registers
+ * are supported: 32-bit reads will return garbage in the upper word.
+ * 32-bit writes must be enabled explicitly.
+ * 
+ * 32-bit reads of the status registers seem just fine.
+ */
+
+/* 2D graphics engine registers for Savage4; others appear to be mostly the same */
+enum {
+	SubsystemStatus = 0x8504,	/* Subsystem Status: read only */
+	  /* read only: whether we get interrupts on various events */
+	  VsyncInt		= 1<<0,		/* vertical sync */
+	  GeBusyInt		= 1<<1,		/* 2D graphics engine busy */
+	  BfifoFullInt	= 1<<2,		/* BIU FIFO full */
+	  BfifoEmptyInt	= 1<<3,		/* BIU FIFO empty */
+	  CfifoFullInt	= 1<<4,		/* command FIFO full */
+	  CfifoEmptyInt	= 1<<5,		/* command FIFO empty */
+	  BciInt		= 1<<6, 	/* BCI */
+	  LpbInt		= 1<<7, 	/* LPB */
+	  CbHiInt		= 1<<16,	/* COB upper threshold */
+	  CbLoInt		= 1<<17,	/* COB lower threshold */
+
+	SubsystemCtl 	= 0x8504,	/* Subsystem Control: write only */
+	  /* clear interrupts for various events */
+	  VsyncClr		= 1<<0,
+	  GeBusyClr		= 1<<1,
+	  BfifoFullClr	= 1<<2,
+	  BfifoEmptyClr	= 1<<3,
+	  CfifoFullClr	= 1<<4,
+	  CfifoEmptyClr	= 1<<5,
+	  BciClr		= 1<<6,
+	  LpbClr		= 1<<7,
+	  CbHiClr		= 1<<16,
+	  CbLoClr		= 1<<17,
+
+	  /* enable interrupts for various events */
+	  VsyncEna		= 1<<8,
+	  Busy2DEna		= 1<<9,
+	  BfifoFullEna	= 1<<10,
+	  BfifoEmptyEna	= 1<<11,
+	  CfifoFullEna	= 1<<12,
+	  CfifoEmptyEna	= 1<<13,
+	  SubsysBciEna	= 1<<14,
+	  CbHiEna		= 1<<24,
+	  CbLoEna		= 1<<25,
+
+	  /* 2D graphics engine software reset */
+	  GeSoftReset	= 1<<15,
+
+	FifoStatus		= 0x8508,	/* FIFO status: read only */
+	  CwbEmpty		= 1<<0,		/* command write buffer empty */
+	  CrbEmpty		= 1<<1,		/* command read buffer empty */
+	  CobEmpty		= 1<<2,		/* command overflow buffer empty */
+	  CfifoEmpty	= 1<<3,		/* command FIFO empty */
+	  CwbFull		= 1<<8,		/* command write buffer full */
+	  CrbFull		= 1<<9,		/* command read buffer full */
+	  CobFull		= 1<<10,	/* command overflow buffer full */
+	  CfifoFull		= 1<<11,	/* command FIFO full */
+
+	AdvFunCtl		= 0x850C,	/* Advanced Function Control: read/write */
+	  GeEna			= 1<<0,	/* enable 2D/3D engine */
+	  /*
+	   * according to the manual, BigPixel should be
+	   * set when bpp >= 8 (bpp != 4), and then CR50_5-4 are
+	   * used to figure out bpp example.  however, it does bad things
+	   * to the screen in 8bpp mode.
+	   */
+	  BigPixel		= 1<<2,		/* 8 or more bpp enhanced mode */
+	  LaEna			= 1<<3,		/* linear addressing ena: or'ed with CR58_4 */
+	  Mclk_2		= 0<<8,		/* 2D engine clock divide: MCLK/2 */
+	  Mclk_4		= 1<<8,		/* " MCLK/4 */
+	  Mclk			= 2<<8,		/* " MCLK */
+	/* Mclk			= 3<<8,		/* " MCLK */
+	  Ic33mhz		= 1<<16,	/* Internal clock 33 MHz (instead of 66) */
+
+	WakeupReg		= 0x8510,	/* Wakeup: read/write */
+	  WakeupBit		= 1<<0,	/* wake up: or'ed with 3C3_0 */
+
+	SourceY			= 0x8100,	/* UL corner of bitblt source */
+	SourceX			= 0x8102,	/* " */
+	RectY			= 0x8100,	/* UL corner of rectangle fill */
+	RectX			= 0x8102,	/* " */
+	DestY			= 0x8108,	/* UL corner of bitblt dest */
+	DestX			= 0x810A,	/* " */
+	Height			= 0x8148,	/* bitblt, image xfer rectangle height */
+	Width			= 0x814A, 	/* bitblt, image xfer rectangle width */
+
+	StartY			= 0x8100,	/* Line draw: first point*/
+	StartX			= 0x8102,	/* " */
+	/*
+	 * For line draws, the following must be programmed:
+	 * axial step constant = 2*min(|dx|,|dy|)
+	 * diagonal step constant = 2*[min(|dx|,|dy|) - max(|dx|,|dy|)]
+	 * error term = 2*min(|dx|,|dy|) - max(|dx|,|dy| - 1
+	 *	[sic] when start X < end X
+	 * error term = 2*min(|dx|,|dy|) - max(|dx|,|dy|
+	 *  [sic] when start X >= end X
+	 */
+	AxialStep		= 0x8108,
+	DiagonalStep	= 0x810A,
+	LineError		= 0x8110,
+	MinorLength		= 0x8148,	/* pixel count along minor axis */
+	MajorLength		= 0x814A,	/* pixel count along major axis */
+
+	DrawCmd			= 0x8118,	/* Drawing Command: write only */
+	  CmdMagic		= 0<<1,
+	  AcrossPlane	= 1<<1,		/* across the plane mode */
+	  LastPixelOff	= 1<<2,		/* last pixel of line or vector draw not drawn */
+	  Radial		= 1<<3,		/* enable radial direction (else axial) */
+	  DoDraw		= 1<<4,		/* draw pixels (else only move current pos) */
+
+	  DrawRight		= 1<<5,		/* axial drawing direction: left to right */
+	  /* DrawLeft		= 0<<5, */
+	  MajorY		= 1<<6,
+	  /* MajorX		= 0<<6, */
+	  DrawDown		= 1<<7,
+	  /* DrawUp		= 0<<7, */
+	  Degree0		= 0<<5,		/* drawing direction when Radial */
+	  Degree45		= 1<<5,
+		/* ... */
+	  Degree315		= 7<<5,
+
+	  UseCPUData	= 1<<8,
+
+	  /* image write bus transfer width */
+	  Bus8			= 0<<9,
+	  Bus16			= 1<<9,
+	  /*
+	   * in Bus32 mode, doubleword bits beyond the image rect width are
+	   * discarded.  each line starts on a new doubleword.
+	   * Bus32AP is intended for across-the-plane mode and
+	   * rounds to byte boundaries instead.
+	   */
+	  Bus32			= 2<<9,
+	  Bus32AP		= 3<<9,
+
+	  CmdNop		= 0<<13,	/* nop */
+	  CmdLine		= 1<<13,	/* draw line */
+	  CmdFill		= 2<<13,	/* fill rectangle */
+	  CmdBitblt		= 6<<13,	/* bitblt */
+	  CmdPatblt		= 7<<13,	/* 8x8 pattern blt */
+
+	  SrcGBD		= 0<<16,
+	  SrcPBD		= 1<<16,
+	  SrcSBD		= 2<<16,
+
+	  DstGBD		= 0<<18,
+	  DstPBD		= 1<<18,
+	  DstSBD		= 2<<18,
+
+	/* color sources, controls */
+	BgColor			= 0x8120,	/* Background Color: read/write */
+	FgColor			= 0x8124,	/* Foreground Color: read/write */
+	BitplaneWmask	= 0x8128,	/* Bitplane Write Mask: read/write */
+	BitplaneRmask	= 0x812C,	/* Bitplane Read Mask: read/write */
+	CmpColor		= 0x8130,	/* Color Compare: read/write */
+	BgMix			= 0x8134,
+	FgMix			= 0x8136,
+	  MixNew		= 7,
+	  SrcBg			= 0<<5,
+	  SrcFg			= 1<<5,
+	  SrcCPU		= 2<<5,
+	  SrcDisp		= 3<<5,
+
+	/* clipping rectangle */
+	TopScissors		= 0x8138,	/* Top Scissors: write only */
+	LeftScissors	= 0x813A,	/* Left Scissors: write only */
+	BottomScissors	= 0x813C,	/* Bottom Scissors: write only */
+	RightScissors	= 0x813E,	/* Right Scissors: write only */
+
+	/*
+	 * Registers with Magic were indirectly accessed in older modes.
+	 * It is not clear whether the Magic is necessary.
+	 * In the older modes, writes to these registers were pipelined,
+	 * so that you had to issue an engine command and wait for engine
+	 * idle before reading a write back.  It is not clear if this is
+	 * still the case either.
+	 */
+	PixCtl			= 0x8140,	/* Pixel Control: write only */
+	  PixMagic		= 0xA<<12,
+	  PixMixFg		= 0<<6,		/* foreground mix register always */
+	  PixMixCPU		= 2<<6,		/* CPU data determines mix register */
+	  PixMixDisp	= 3<<6,		/* display data determines mix register */
+
+	MfMisc2Ctl		= 0x8142,	/* Multifunction Control Misc. 2: write only */
+	  MfMisc2Magic	= 0xD<<12,
+	  DstShift		= 0,		/* 3 bits: destination base address in MB */
+	  SrcShift		= 4,		/* 3 bits: source base address in MB */
+	  WaitFifoEmpty	= 2<<8,		/* wait for write FIFO empty between draws */
+
+	MfMiscCtl		= 0x8144,	/* Multifunction Control Misc: write only */
+	  MfMiscMagic	= 0xE<<12,
+	  UseHighBits	= 1<<4,		/* select upper 16 bits for 32-bit reg access */
+	  ClipInvert	= 1<<5,		/* only touch pixels outside clip rectangle */
+	  SkipSame		= 0<<6,		/* ignore pixels with color CmpColor */
+	  SkipDifferent	= 1<<7,		/* ignore pixels not color CmpColor */
+	  CmpEna		= 1<<8,		/* enable color compare */
+	  W32Ena		= 1<<9,		/* enable 32-bit register write */
+	  ClipDis		= 1<<11,	/* disable clipping */
+
+	/*
+	 * The bitmap descriptor 1 registers contain the starting
+	 * address of the bitmap (in bytes).
+	 * The bitmap descriptor 2 registesr contain stride (in pixels)
+	 * in the lower 16 bits, depth (in bits) in the next 8 bits,
+	 * and whether block write is disabled.
+	 */
+	GBD1			= 0x8168,	/* Global Bitmap Descriptor 1: read/write */
+	GBD2			= 0x816C,	/* Global Bitmap Descriptor 2: read/write */
+	  /* GBD2-only bits */
+	  BDS64			= 1<<0,		/* bitmap descriptor size 64 bits */
+	  GBDBciEna		= 1<<3,		/* BCI enable */
+	  /* generic BD2 bits */
+	  BlockWriteDis	= 1<<28,
+	  StrideShift	= 0,
+	  DepthShift	= 16,
+
+	PBD1			= 0x8170,	/* Primary Bitmap Descriptor: read/write */
+	PBD2			= 0x8174,
+	SBD1			= 0x8178,	/* Secondary Bitmap Descriptor: read/write */
+	SBD2			= 0x817C,
+};
+
+/* mastered data transfer registers */
+
+/* configuration/status registers */
+enum {
+	XStatus0			= 0x48C00,	/* Status Word 0: read only */
+	  /* rev. A silicon differs from rev. B; use AltStatus0 */
+	  CBEMaskA		= 0x1FFFF,	/* filled command buffer entries */
+	  CBEShiftA		= 0,
+	  BciIdleA		= 1<<17,	/* BCI idle */
+	  Ge3IdleA		= 1<<18,	/* 3D engine idle */
+	  Ge2IdleA		= 1<<19,	/* 2D engine idle */
+	  McpIdleA		= 1<<20,	/* motion compensation processor idle */
+	  MeIdleA		= 1<<22,	/* master engine idle */
+	  PfPendA		= 1<<23,	/* page flip pending */
+
+	  CBEMaskB		= 0x1FFFFF,
+	  CBEShiftB		= 0,
+	  BciIdleB		= 1<<25,
+	  Ge3IdleB		= 1<<26,
+	  Ge2IdleB		= 1<<27,
+	  McpIdleB		= 1<<28,
+	  MeIdleB		= 1<<30,
+	  PfPendB		= 1<<31,
+
+	AltStatus0		= 0x48C60,	/* Alternate Status Word 0: read only */
+	  CBEMask		= 0x1FFFF,
+	  CBEShift		= 0,
+	  /* the Savage4 manual says bits 17..23 for these, like Status0 */
+	  /* empirically, they are bits 21..26 */
+	  BciIdle		= 1<<21,
+	  Ge3Idle		= 1<<22,
+	  Ge2Idle		= 1<<23,
+	  McpIdle		= 1<<24,
+	  MeIdle		= 1<<25,
+	  PfPend		= 1<<26,
+
+	XStatus1			= 0x48C04,	/* Status Word 1: read only */
+	  /* contains event tag 1, event tag 0, both 16 bits */
+
+	XStatus2			= 0x48C08,	/* Status Word 2: read only */
+	  ScanMask		= 0x3FF,	/* current scan line */
+	  ScanShift		= 0,
+	  VRTMask		= 0x7F100,	/* vert retrace count */
+	  VRTShift		= 11,
+
+	CbThresh		= 0x48C10,	/* Command Buffer Thresholds: read/write */
+	CobOff			= 0x48C14,	/* Command Overflow Buffer: read/write */
+
+	CobPtr			= 0x48C18,	/* Command Overflow Buffer Pointers: read/write */
+	  CobEna		= 1<<2,		/* command overflow buffer enable */
+	  CobBciEna		= 1<<3,		/* BCI function enable */
+	  CbeMask		= 0xFFFF8000,	/* no. of entries in command buffer */
+	  CbeShift		= 15,
+
+	AltStatus1		= 0x48C64,	/* Alternate Status Word 1: read onnly */
+	  /* contains current texture surface tag, vertex buffer tag */
+
+};
+
+struct {
+	ulong idletimeout;
+	ulong tostatw[16];
+} savagestats;
+
+enum {
+	Maxloop = 1<<20
+};
+
+static void
+savagewaitidle(VGAscr *scr)
+{
+	long x;
+	ulong *statw, mask, goal;
+
+	switch(scr->id){
+	case SAVAGE4:
+	case PROSAVAGEP:
+	case PROSAVAGEK:
+	case PROSAVAGE8:
+		/* wait for engine idle and FIFO empty */
+		statw = (ulong*)((uchar*)scr->mmio+AltStatus0);
+		mask = CBEMask | Ge2Idle;
+		goal = Ge2Idle;
+		break;
+	/* case SAVAGEMXMV: ? */
+	/* case SAVAGEMX: ? */
+	/* case SAVAGEIX: ? */
+	case SUPERSAVAGEIXC16:
+	case SAVAGEIXMV:
+	case SAVAGEMXMV:
+		/* wait for engine idle and FIFO empty */
+		statw = (ulong*)((uchar*)scr->mmio+XStatus0);
+		mask = CBEMaskA | Ge2IdleA;
+		goal = Ge2IdleA;
+		break;
+	default:
+		/* 
+		 * best we can do: can't print or we'll call ourselves.
+		 * savageinit is supposed to not let this happen.
+		 */	
+		return;
+	}
+
+	for(x=0; x<Maxloop; x++)
+		if((*statw & mask) == goal)
+			return;
+
+	savagestats.tostatw[savagestats.idletimeout++&15] = *statw;
+	savagestats.tostatw[savagestats.idletimeout++&15] = (ulong)statw;
+}
+
+static int
+savagefill(VGAscr *scr, Rectangle r, ulong sval)
+{
+	uchar *mmio;
+
+	mmio = (uchar*)scr->mmio;
+
+	*(ulong*)(mmio+FgColor) = sval;
+	*(ulong*)(mmio+BgColor) = sval;
+	*(ulong*)(mmio+BgMix) = SrcFg|MixNew;
+	*(ulong*)(mmio+FgMix) = SrcFg|MixNew;
+	*(ushort*)(mmio+RectY) = r.min.y;
+	*(ushort*)(mmio+RectX) = r.min.x;
+	*(ushort*)(mmio+Width) = Dx(r)-1;
+	*(ushort*)(mmio+Height) = Dy(r)-1;
+	*(ulong*)(mmio+DrawCmd) = CmdMagic | DoDraw | CmdFill | DrawRight | DrawDown;
+	savagewaitidle(scr);
+	return 1;
+}
+
+static int
+savagescroll(VGAscr *scr, Rectangle r, Rectangle sr)
+{
+	uchar *mmio;
+	ulong cmd;
+	Point dp, sp;
+
+	cmd = CmdMagic | DoDraw | CmdBitblt | SrcPBD | DstGBD;
+
+	if(r.min.x <= sr.min.x){
+		cmd |= DrawRight;
+		dp.x = r.min.x;
+		sp.x = sr.min.x;
+	}else{
+		dp.x = r.max.x-1;
+		sp.x = sr.max.x-1;
+	}
+
+	if(r.min.y <= sr.min.y){
+		cmd |= DrawDown;
+		dp.y = r.min.y;
+		sp.y = sr.min.y;
+	}else{
+		dp.y = r.max.y-1;
+		sp.y = sr.max.y-1;
+	}
+
+	mmio = (uchar*)scr->mmio;
+
+	*(ushort*)(mmio+SourceX) = sp.x;
+	*(ushort*)(mmio+SourceY) = sp.y;
+	*(ushort*)(mmio+DestX) = dp.x;
+	*(ushort*)(mmio+DestY) = dp.y;
+	*(ushort*)(mmio+Width) = Dx(r)-1;
+	*(ushort*)(mmio+Height) = Dy(r)-1;
+	*(ulong*)(mmio+BgMix) = SrcDisp|MixNew;
+	*(ulong*)(mmio+FgMix) = SrcDisp|MixNew;
+	*(ulong*)(mmio+DrawCmd) = cmd;
+	savagewaitidle(scr);
+	return 1;
+}
+
+static void
+savageblank(VGAscr*, int blank)
+{
+	uchar seqD;
+
+	/*
+	 * Will handle DPMS to monitor
+	 */
+	vgaxo(Seqx, 8, vgaxi(Seqx,8)|0x06);
+	seqD = vgaxi(Seqx, 0xD);
+	seqD &= 0x03;
+	if(blank)
+		seqD |= 0x50;
+	vgaxo(Seqx, 0xD, seqD);
+
+	/*
+	 * Will handle LCD
+	 */
+	if(blank)
+		vgaxo(Seqx, 0x31, vgaxi(Seqx, 0x31) & ~0x10);
+	else
+		vgaxo(Seqx, 0x31, vgaxi(Seqx, 0x31) | 0x10);
+}
+
+
+void
+savageinit(VGAscr *scr)
+{
+	uchar *mmio;
+	ulong bd;
+
+	/* if you add chip IDs here be sure to update savagewaitidle */
+	switch(scr->id){
+	case SAVAGE4:
+	case PROSAVAGEP:
+	case PROSAVAGEK:
+	case PROSAVAGE8:
+	case SAVAGEIXMV:
+	case SUPERSAVAGEIXC16:
+	case SAVAGEMXMV:
+		break;
+	default:
+		print("unknown savage %.4lux\n", scr->id);
+		return;
+	}
+
+	mmio = (uchar*)scr->mmio;
+	if(mmio == nil) {
+		print("savageinit: no mmio\n");
+		return;
+	}
+
+	/* 2D graphics engine software reset */
+	*(ushort*)(mmio+SubsystemCtl) = GeSoftReset;
+	delay(2);
+	*(ushort*)(mmio+SubsystemCtl) = 0;
+	savagewaitidle(scr);
+
+	/* disable BCI as much as possible */
+	*(ushort*)(mmio+CobPtr) &= ~CobBciEna;
+	*(ushort*)(mmio+GBD2) &= ~GBDBciEna;
+	savagewaitidle(scr);
+
+	/* enable 32-bit writes, disable clipping */
+	*(ushort*)(mmio+MfMiscCtl) = MfMiscMagic|W32Ena|ClipDis;
+	savagewaitidle(scr);
+
+	/* enable all read, write planes */
+	*(ulong*)(mmio+BitplaneRmask) = ~0;
+	*(ulong*)(mmio+BitplaneWmask) = ~0;
+	savagewaitidle(scr);
+
+	/* turn on linear access, 2D engine */
+	*(ulong*)(mmio+AdvFunCtl) |= GeEna|LaEna;
+	savagewaitidle(scr);
+
+	/* set bitmap descriptors */
+	bd = (scr->gscreen->depth<<DepthShift) |
+		(Dx(scr->gscreen->r)<<StrideShift) | BlockWriteDis
+		| BDS64;
+
+	*(ulong*)(mmio+GBD1) = 0;
+	*(ulong*)(mmio+GBD2) = bd;
+
+	*(ulong*)(mmio+PBD1) = 0;
+	*(ulong*)(mmio+PBD2) = bd;
+
+	*(ulong*)(mmio+SBD1) = 0;
+	*(ulong*)(mmio+SBD2) = bd;
+
+	/*
+	 * For some reason, the GBD needs to get programmed twice,
+	 * once before the PBD, SBD, and once after.
+	 * This empirically makes it get set right.
+	 * I would like to better understand the ugliness
+	 * going on here.
+	 */
+	*(ulong*)(mmio+GBD1) = 0;
+	*(ulong*)(mmio+GBD2) = bd;
+	*(ushort*)(mmio+GBD2+2) = bd>>16;
+	savagewaitidle(scr);
+
+	scr->fill = savagefill;
+	scr->scroll = savagescroll;
+	scr->blank = savageblank;
+	hwblank = 0;
+}
--- /dev/null
+++ b/os/pc/vgat2r4.c
@@ -1,0 +1,586 @@
+#include "u.h"
+#include "../port/lib.h"
+#include "mem.h"
+#include "dat.h"
+#include "fns.h"
+#include "io.h"
+#include "../port/error.h"
+
+#define	Image	IMAGE
+#include <draw.h>
+#include <memdraw.h>
+#include <cursor.h>
+#include "screen.h"
+
+/*
+ * #9 Ticket to Ride IV.
+ */
+enum {
+	IndexLo		= 0x10/4,
+	IndexHi		= 0x14/4,
+	Data		= 0x18/4,
+	IndexCtl	= 0x1C/4,
+
+	Zoom		= 0x54/4,
+};
+
+enum {						/* index registers */
+	CursorSyncCtl	= 0x03,
+	  HsyncHi = 0x01,
+	  HsyncLo = 0x02,
+	  VsyncHi = 0x04,
+	  VsyncLo = 0x08,
+
+	CursorCtl	= 0x30,
+	CursorXLo	= 0x31,
+	CursorXHi	= 0x32,
+	CursorYLo	= 0x33,
+	CursorYHi	= 0x34,
+	CursorHotX	= 0x35,
+	CursorHotY	= 0x36,
+
+	CursorR1	= 0x40,
+	CursorG1	= 0x41,
+	CursorB1	= 0x42,
+	CursorR2	= 0x43,
+	CursorG2	= 0x44,
+	CursorB2	= 0x45,
+	CursorR3	= 0x46,
+	CursorG3	= 0x47,
+	CursorB3	= 0x48,
+
+	CursorArray	= 0x100,
+
+	CursorMode32x32	= 0x23,
+	CursorMode64x64	= 0x27,
+	CursorMode	= CursorMode32x32,
+};
+
+static ulong
+t2r4linear(VGAscr* scr, int* size, int* align)
+{
+	ulong aperture, oaperture;
+	int oapsize, wasupamem;
+	Pcidev *p;
+
+	oaperture = scr->aperture;
+	oapsize = scr->apsize;
+	wasupamem = scr->isupamem;
+
+	aperture = 0;
+	if(p = pcimatch(nil, 0x105D, 0)){
+		switch(p->did){
+		case 0x5348:
+			aperture = p->mem[0].bar & ~0x0F;
+			*size = p->mem[0].size;
+			break;
+		default:
+			break;
+		}
+	}
+
+	if(wasupamem){
+		if(oaperture == aperture)
+			return oaperture;
+		upafree(oaperture, oapsize);
+	}
+	scr->isupamem = 0;
+
+	aperture = upamalloc(aperture, *size, *align);
+	if(aperture == 0){
+		if(wasupamem && upamalloc(oaperture, oapsize, 0)){
+			aperture = oaperture;
+			scr->isupamem = 1;
+		}
+		else
+			scr->isupamem = 0;
+	}
+	else
+		scr->isupamem = 1;
+
+	return aperture;
+}
+
+static void
+t2r4enable(VGAscr* scr)
+{
+	Pcidev *p;
+	int size, align;
+	ulong aperture, mmio;
+
+	/*
+	 * Only once, can't be disabled for now.
+	 * scr->mmio holds the virtual address of
+	 * the MMIO registers.
+	 */
+	if(scr->mmio)
+		return;
+	if(p = pcimatch(nil, 0x105D, 0)){
+		switch(p->did){
+		case 0x5348:
+			break;
+		default:
+			return;
+		}
+	}
+	else
+		return;
+	mmio = upamalloc(p->mem[4].bar & ~0x0F, p->mem[4].size, 0);
+	if(mmio == 0)
+		return;
+	addvgaseg("t2r4mmio", mmio, p->mem[4].size);
+
+	scr->mmio = KADDR(mmio);
+
+	size = p->mem[0].size;
+	align = 0;
+	aperture = t2r4linear(scr, &size, &align);
+	if(aperture){
+		scr->aperture = aperture;
+		scr->apsize = size;
+		addvgaseg("t2r4screen", aperture, size);
+	}
+}
+
+static uchar
+t2r4xi(VGAscr* scr, int index)
+{
+	ulong *mmio;
+
+	mmio = scr->mmio;
+	mmio[IndexLo] = index & 0xFF;
+	mmio[IndexHi] = (index>>8) & 0xFF;
+
+	return mmio[Data];
+}
+
+static void
+t2r4xo(VGAscr* scr, int index, uchar data)
+{
+	ulong *mmio;
+
+	mmio = scr->mmio;
+	mmio[IndexLo] = index & 0xFF;
+	mmio[IndexHi] = (index>>8) & 0xFF;
+
+	mmio[Data] = data;
+}
+
+static void
+t2r4curdisable(VGAscr* scr)
+{
+	if(scr->mmio == 0)
+		return;
+	t2r4xo(scr, CursorCtl, 0x00);
+}
+
+static void
+t2r4curload(VGAscr* scr, Cursor* curs)
+{
+	uchar *data;
+	int size, x, y, zoom;
+	ulong clr, *mmio, pixels, set;
+
+	mmio = scr->mmio;
+	if(mmio == 0)
+		return;
+
+	/*
+	 * Make sure cursor is off by initialising the cursor
+	 * control to defaults.
+	 */
+	t2r4xo(scr, CursorCtl, 0x00);
+
+	/*
+	 * Set auto-increment mode for index-register addressing
+	 * and initialise the cursor array index.
+	 */
+	mmio[IndexCtl] = 0x01;
+	mmio[IndexLo] = CursorArray & 0xFF;
+	mmio[IndexHi] = (CursorArray>>8) & 0xFF;
+
+	/*
+	 * Initialise the cursor RAM array. There are 2 planes,
+	 * p0 and p1. Data is written 4 pixels per byte, with p1 the
+	 * MS bit of each pixel.
+	 * The cursor is set in X-Windows mode which gives the following
+	 * truth table:
+	 *	p1 p0	colour
+	 *	 0  0	underlying pixel colour
+	 *	 0  1	underlying pixel colour
+	 *	 1  0	cursor colour 1
+	 *	 1  1	cursor colour 2
+	 * Put the cursor into the top-left of the array.
+	 *
+	 * Although this looks a lot like the IBM RGB524 cursor, the
+	 * scanlines appear to be twice as long as they should be and
+	 * some of the other features are missing.
+	 */
+	if(mmio[Zoom] & 0x0F)
+		zoom = 32;
+	else
+		zoom = 16;
+	data = (uchar*)&mmio[Data];
+	for(y = 0; y < zoom; y++){
+		clr = (curs->clr[2*y]<<8)|curs->clr[y*2 + 1];
+		set = (curs->set[2*y]<<8)|curs->set[y*2 + 1];
+		pixels = 0;
+		for(x = 0; x < 16; x++){
+			if(set & (1<<x))
+				pixels |= 0x03<<(x*2);
+			else if(clr & (1<<x))
+				pixels |= 0x02<<(x*2);
+		}
+
+		*data = pixels>>24;
+		*data = pixels>>16;
+		*data = pixels>>8;
+		*data = pixels;
+
+		*data = 0x00;
+		*data = 0x00;
+		*data = 0x00;
+		*data = 0x00;
+
+		if(CursorMode == CursorMode32x32 && zoom == 16)
+			continue;
+		*data = pixels>>24;
+		*data = pixels>>16;
+		*data = pixels>>8;
+		*data = pixels;
+
+		*data = 0x00;
+		*data = 0x00;
+		*data = 0x00;
+		*data = 0x00;
+	}
+	if(CursorMode == CursorMode32x32)
+		size = 32;
+	else
+		size = 64;
+	while(y < size){
+		for(x = 0; x < size/8; x++){
+			*data = 0x00;
+			*data = 0x00;
+		}
+		y++;
+	}
+	mmio[IndexCtl] = 0x00;
+
+	/*
+	 * Initialise the hotpoint and enable the cursor.
+	 */
+	t2r4xo(scr, CursorHotX, -curs->offset.x);
+	zoom = (scr->mmio[Zoom] & 0x0F)+1;
+	t2r4xo(scr, CursorHotY, -curs->offset.y*zoom);
+
+	t2r4xo(scr, CursorCtl, CursorMode);
+}
+
+static int
+t2r4curmove(VGAscr* scr, Point p)
+{
+	int y, zoom;
+
+	if(scr->mmio == 0)
+		return 1;
+
+	t2r4xo(scr, CursorXLo, p.x & 0xFF);
+	t2r4xo(scr, CursorXHi, (p.x>>8) & 0x0F);
+
+	zoom = (scr->mmio[Zoom] & 0x0F)+1;
+	y = p.y*zoom;
+	t2r4xo(scr, CursorYLo, y & 0xFF);
+	t2r4xo(scr, CursorYHi, (y>>8) & 0x0F);
+
+	return 0;
+}
+
+static void
+t2r4curenable(VGAscr* scr)
+{
+	t2r4enable(scr);
+	if(scr->mmio == 0)
+		return;
+
+	/*
+	 * Make sure cursor is off by initialising the cursor
+	 * control to defaults.
+	 */
+	t2r4xo(scr, CursorCtl, 0x00);
+
+	/*
+	 * Cursor colour 1 (white),
+	 * cursor colour 2 (black).
+	 */
+	t2r4xo(scr, CursorR1, Pwhite);
+	t2r4xo(scr, CursorG1, Pwhite);
+	t2r4xo(scr, CursorB1, Pwhite);
+
+	t2r4xo(scr, CursorR2, Pblack);
+	t2r4xo(scr, CursorG2, Pblack);
+	t2r4xo(scr, CursorB2, Pblack);
+
+	/*
+	 * Load, locate and enable the cursor, 64x64, mode 2.
+	 */
+	t2r4curload(scr, &arrow);
+	t2r4curmove(scr, ZP);
+	t2r4xo(scr, CursorCtl, CursorMode);
+}
+
+enum {
+	Flow		= 0x08/4,
+	Busy		= 0x0C/4,
+	BufCtl		= 0x20/4,
+	DeSorg		= 0x28/4,
+	DeDorg		= 0x2C/4,
+	DeSptch		= 0x40/4,
+	DeDptch		= 0x44/4,
+	CmdOpc		= 0x50/4,
+	CmdRop		= 0x54/4,
+	CmdStyle 	= 0x58/4,
+	CmdPatrn 	= 0x5C/4,
+	CmdClp		= 0x60/4,
+	CmdPf		= 0x64/4,
+	Fore		= 0x68/4,
+	Back		= 0x6C/4,
+	Mask		= 0x70/4,
+	DeKey		= 0x74/4,
+	Lpat		= 0x78/4,
+	Pctrl 		= 0x7C/4,
+	Clptl		= 0x80/4,
+	Clpbr		= 0x84/4,
+	XY0		= 0x88/4,
+	XY1		= 0x8C/4,
+	XY2		= 0x90/4,
+	XY3		= 0x94/4,
+	XY4		= 0x98/4,
+	Alpha 		= 0x128/4,
+	ACtl 		= 0x16C/4,
+
+	RBaseD 		= 0x4000/4,
+};
+
+/* wait until pipeline ready for new command */
+static void
+waitforfifo(VGAscr *scr)
+{
+	int x;
+	ulong *d;
+	x = 0;
+
+	d = scr->mmio + RBaseD;
+	while((d[Busy]&1) && x++ < 1000000)
+		;
+	if(x >= 1000000)	/* shouldn't happen */
+		iprint("busy %8lux\n", d[Busy]);
+}
+
+/* wait until command has finished executing */
+static void
+waitforcmd(VGAscr *scr)
+{
+	int x;
+	ulong *d;
+	x = 0;
+
+	d = scr->mmio + RBaseD;
+	while((d[Flow]&0x1B) && x++ < 1000000)
+		;
+	if(x >= 1000000)	/* shouldn't happen */
+		iprint("flow %8lux\n", d[Flow]);
+}
+
+/* wait until memory controller not busy (i.e. wait for writes to flush) */
+static void
+waitformem(VGAscr *scr)
+{
+	int x;
+	ulong *d;
+	x = 0;
+
+	d = scr->mmio + RBaseD;
+	while((d[Flow]&2)&& x++ < 1000000)
+		;
+	if(x >= 1000000)	/* shouldn't happen */
+		iprint("mem %8lux\n", d[Busy]);
+}
+
+static int
+t2r4hwscroll(VGAscr *scr, Rectangle r, Rectangle sr)
+{
+	int ctl;
+	Point dp, sp;
+	ulong *d;
+	int depth;
+
+	if(r.min.y == sr.min.y){	/* a purely horizontal scroll */
+		depth = scr->gscreen->depth;
+		switch(depth){
+		case 32:
+			/*
+			 * Using the SGI flat panels with the Ticket-to-Ride IV, horizontal
+			 * 32-bit scrolls don't work perfectly on rectangles of width <= 24.
+			 * we bail on a bigger bound for padding.
+			 */
+			if(Dx(r) < 32)
+				return 0;
+			break;
+		case 16:
+			/*
+			 * Using the SGI flat panels with the Ticket-to-Ride IV, horizontal
+			 * 16-bit scrolls don't work perfectly on rectangles of width <= 96.
+			 * we bail on a bigger bound for padding.
+			 */
+			if(Dx(r) < 104)
+				return 0;
+			break;
+		}
+	}
+	waitformem(scr);
+	waitforfifo(scr);
+	d = scr->mmio + RBaseD;
+	ctl = 0;
+	if(r.min.x <= sr.min.x){
+		dp.x = r.min.x;
+		sp.x = sr.min.x;
+	}else{
+		ctl |= 2;
+		dp.x = r.max.x-1;
+		sp.x = sr.max.x-1;
+	}
+
+	if(r.min.y < sr.min.y){
+		dp.y = r.min.y;
+		sp.y = sr.min.y;
+	}else{
+		ctl |= 1;
+		dp.y = r.max.y-1;
+		sp.y = sr.max.y-1;
+	}
+
+	d[CmdOpc] = 0x1;	/* bitblt */
+	d[CmdRop] = 0xC;	/* copy source */
+	d[CmdStyle] = 0;
+	d[CmdPatrn] = 0;
+	d[Fore] = 0;
+	d[Back] = 0;
+
+	/* writing XY1 executes cmd */
+	d[XY3] = ctl;
+	d[XY0] = (sp.x<<16)|sp.y;
+	d[XY2] = (Dx(r)<<16)|Dy(r);
+	d[XY4] = 0;
+	d[XY1] = (dp.x<<16)|dp.y;
+	waitforcmd(scr);
+
+	return 1;
+}
+
+static int
+t2r4hwfill(VGAscr *scr, Rectangle r, ulong sval)
+{
+	ulong *d;
+
+	d = scr->mmio + RBaseD;
+
+	waitformem(scr);
+	waitforfifo(scr);
+	d[CmdOpc] = 0x1;	/* bitblt */
+	d[CmdRop] = 0xC;	/* copy source */
+	d[CmdStyle] = 1;	/* use source from Fore register */
+	d[CmdPatrn] = 0;	/* no stipple */
+	d[Fore] = sval;
+	d[Back] = sval;
+
+	/* writing XY1 executes cmd */
+	d[XY3] = 0;
+	d[XY0] = (r.min.x<<16)|r.min.y;
+	d[XY2] = (Dx(r)<<16)|Dy(r);
+	d[XY4] = 0;
+	d[XY1] = (r.min.x<<16)|r.min.y;
+	waitforcmd(scr);
+
+	return 1;
+}
+
+static void
+t2r4blank(VGAscr *scr, int blank)
+{
+	uchar x;
+
+	x = t2r4xi(scr, CursorSyncCtl);
+	x &= ~0x0F;
+	if(blank)
+		x |= HsyncLo | VsyncLo;
+	t2r4xo(scr, CursorSyncCtl, x);
+}
+
+static void
+t2r4drawinit(VGAscr *scr)
+{
+	ulong pitch;
+	int depth;
+	int fmt;
+	ulong *d;
+
+	pitch = Dx(scr->gscreen->r);
+	depth = scr->gscreen->depth;
+
+	switch(scr->gscreen->chan){
+	case RGB16:
+		fmt = 3;
+		break;
+	case XRGB32:
+		fmt = 2;
+		break;
+	case RGB15:
+		fmt = 1;
+		break;
+	default:
+		scr->fill = nil;
+		scr->scroll = nil;
+		return;
+	}
+
+	d = scr->mmio + RBaseD;
+
+	d[BufCtl] = fmt<<24;
+	d[DeSorg] = 0;
+	d[DeDorg] = 0;
+	d[DeSptch] = (pitch*depth)/8;
+	d[DeDptch] = (pitch*depth)/8;
+	d[CmdClp] = 0;	/* 2 = inside rectangle */
+	d[Mask] = ~0;
+	d[DeKey] = 0;
+	d[Clptl] = 0; 
+	d[Clpbr] = 0xFFF0FFF0;
+	d[Alpha] = 0;
+	d[ACtl] = 0;
+
+	scr->fill = t2r4hwfill;
+	scr->scroll = t2r4hwscroll;
+	scr->blank = t2r4blank;
+	hwblank = 1;
+}
+
+VGAdev vgat2r4dev = {
+	"t2r4",
+
+	t2r4enable,
+	nil,
+	nil,
+	t2r4linear,
+	t2r4drawinit,
+};
+
+VGAcur vgat2r4cur = {
+	"t2r4hwgc",
+
+	t2r4curenable,
+	t2r4curdisable,
+	t2r4curload,
+	t2r4curmove,
+};
+
--- /dev/null
+++ b/os/pc/vgatvp3020.c
@@ -1,0 +1,216 @@
+#include "u.h"
+#include "../port/lib.h"
+#include "mem.h"
+#include "dat.h"
+#include "fns.h"
+#include "../port/error.h"
+
+#define	Image	IMAGE
+#include <draw.h>
+#include <memdraw.h>
+#include <cursor.h>
+#include "screen.h"
+
+/*
+ * TVP3020 Viewpoint Video Interface Pallette.
+ * Assumes hooked up to an S3 86C928.
+ */
+enum {
+	Index		= 0x06,				/* Index register */
+	Data		= 0x07,				/* Data register */
+};
+
+/*
+ * Lower 2-bits of indirect DAC register
+ * addressing.
+ */
+static ushort dacxreg[4] = {
+	PaddrW, Pdata, Pixmask, PaddrR
+};
+
+static uchar
+tvp3020io(uchar reg, uchar data)
+{
+	uchar crt55;
+
+	crt55 = vgaxi(Crtx, 0x55) & 0xFC;
+	vgaxo(Crtx, 0x55, crt55|((reg>>2) & 0x03));
+	vgao(dacxreg[reg & 0x03], data);
+
+	return crt55;
+}
+
+static void
+tvp3020xo(uchar index, uchar data)
+{
+	uchar crt55;
+
+	crt55 = tvp3020io(Index, index);
+	vgao(dacxreg[Data & 0x03], data);
+	vgaxo(Crtx, 0x55, crt55);
+}
+
+static void
+tvp3020disable(VGAscr*)
+{
+	uchar r;
+
+	/*
+	 * Disable 
+	 *	cursor;
+	 *	cursor control enable for Bt485 DAC (!);
+	 *	the hardware cursor external operation mode.
+	 */
+	tvp3020xo(0x06, 0x10);				/* Cursor Control Register */
+
+	r = vgaxi(Crtx, 0x45) & ~0x20;
+	vgaxo(Crtx, 0x45, r);
+
+	r = vgaxi(Crtx, 0x55) & ~0x20;
+	vgaxo(Crtx, 0x55, r);
+}
+
+static void
+tvp3020enable(VGAscr*)
+{
+	uchar r;
+
+	/*
+	 * Make sure cursor is off by initialising the cursor
+	 * control to defaults + X-Windows cursor mode.
+	 */
+	tvp3020xo(0x06, 0x10);				/* Cursor Control Register */
+
+	/*
+	 * Overscan colour,
+	 * cursor colour 1 (white),
+	 * cursor colour 2 (black).
+	 */
+	tvp3020xo(0x20, Pwhite); tvp3020xo(0x21, Pwhite); tvp3020xo(0x22, Pwhite);
+	tvp3020xo(0x23, Pwhite); tvp3020xo(0x24, Pwhite); tvp3020xo(0x25, Pwhite);
+	tvp3020xo(0x26, Pblack); tvp3020xo(0x27, Pblack); tvp3020xo(0x28, Pblack);
+
+	/*
+	 * Finally, enable
+	 *	the hardware cursor external operation mode;
+	 *	cursor control enable for Bt485 DAC (!).
+	 */
+	r = vgaxi(Crtx, 0x55)|0x20;
+	vgaxo(Crtx, 0x55, r);
+
+	r = vgaxi(Crtx, 0x45)|0x20;
+	vgaxo(Crtx, 0x45, r);
+}
+
+static void
+tvp3020load(VGAscr*, Cursor* curs)
+{
+	uchar p, p0, p1;
+	int x, y;
+
+	/*
+	 * Make sure cursor is off by initialising the cursor
+	 * control to defaults + X-Windows cursor mode.
+	 */
+	tvp3020xo(0x06, 0x10);				/* Cursor Control Register */
+
+	/*
+	 * Initialise the cursor RAM LS and MS address
+	 * (LS must be first).
+	 */
+	tvp3020xo(0x08, 0x00);				/* Cursor RAM LS Address */
+	tvp3020xo(0x09, 0x00);				/* Cursor RAM MS Address */
+
+	/*
+	 * Initialise the 64x64 cursor RAM array. There are 2 planes,
+	 * p0 and p1. Data is written 4 pixels per byte, with p1 the
+	 * MS bit of each pixel.
+	 * The cursor is set in X-Windows mode which gives the following
+	 * truth table:
+	 *	p1 p0	colour
+	 *	 0  0	underlying pixel colour
+	 *	 0  1	underlying pixel colour
+	 *	 1  0	cursor colour 1
+	 *	 1  1	cursor colour 2
+	 * Put the cursor into the top-left of the 64x64 array.
+	 */
+	for(y = 0; y < 64; y++){
+		for(x = 0; x < 64/8; x++){
+			if(x < 16/8 && y < 16){
+				p0 = curs->clr[x+y*2];
+				p1 = curs->set[x+y*2];
+
+				p = 0x00;
+				if(p1 & 0x10)
+					p |= 0x03;
+				else if(p0 & 0x10)
+					p |= 0x02;
+				if(p1 & 0x20)
+					p |= 0x0C;
+				else if(p0 & 0x20)
+					p |= 0x08;
+				if(p1 & 0x40)
+					p |= 0x30;
+				else if(p0 & 0x40)
+					p |= 0x20;
+				if(p1 & 0x80)
+					p |= 0xC0;
+				else if(p0 & 0x80)
+					p |= 0x80;
+				tvp3020xo(0x0A, p);	/* Cursor RAM Data */
+
+				p = 0x00;
+				if(p1 & 0x01)
+					p |= 0x03;
+				else if(p0 & 0x01)
+					p |= 0x02;
+				if(p1 & 0x02)
+					p |= 0x0C;
+				else if(p0 & 0x02)
+					p |= 0x08;
+				if(p1 & 0x04)
+					p |= 0x30;
+				else if(p0 & 0x04)
+					p |= 0x20;
+				if(p1 & 0x08)
+					p |= 0xC0;
+				else if(p0 & 0x08)
+					p |= 0x80;
+				tvp3020xo(0x0A, p);	/* Cursor RAM Data */
+			}
+			else{
+				tvp3020xo(0x0A, 0x00);	/* Cursor RAM Data */
+				tvp3020xo(0x0A, 0x00);
+			}
+		}
+	}
+
+	/*
+	 * Initialise the cursor hotpoint
+	 * and enable the cursor.
+	 */
+	tvp3020xo(0x04, -curs->offset.x);		/* Sprite Origin X */
+	tvp3020xo(0x05, -curs->offset.y);		/* Sprite Origin Y */
+
+	tvp3020xo(0x06, 0x40|0x10);			/* Cursor Control Register */
+}
+
+static int
+tvp3020move(VGAscr*, Point p)
+{
+	tvp3020xo(0x00, p.x & 0xFF);			/* Cursor Position X LSB */
+	tvp3020xo(0x01, (p.x>>8) & 0x0F);		/* Cursor Position X MSB */
+	tvp3020xo(0x02, p.y & 0xFF);			/* Cursor Position Y LSB */
+	tvp3020xo(0x03, (p.y>>8) & 0x0F);		/* Cursor Position Y MSB */
+
+	return 0;
+}
+
+VGAcur vgatvp3020cur = {
+	"tvp3020hwgc",
+
+	tvp3020enable,
+	tvp3020disable,
+	tvp3020load,
+	tvp3020move,
+};
--- /dev/null
+++ b/os/pc/vgatvp3026.c
@@ -1,0 +1,189 @@
+#include "u.h"
+#include "../port/lib.h"
+#include "mem.h"
+#include "dat.h"
+#include "fns.h"
+#include "../port/error.h"
+
+#define	Image	IMAGE
+#include <draw.h>
+#include <memdraw.h>
+#include <cursor.h>
+#include "screen.h"
+
+/*
+ * TVP3026 Viewpoint Video Interface Pallette.
+ * Assumes hooked up to an S3 Vision968.
+ */
+enum {
+	Index		= 0x00,		/* Index */
+	Data		= 0x0A,		/* Data */
+
+	CaddrW		= 0x04,		/* Colour Write Address */
+	Cdata		= 0x05,		/* Colour Data */
+
+	Cctl		= 0x09,		/* Direct Cursor Control */
+	Cram		= 0x0B,		/* Cursor Ram Data */
+	Cxlsb		= 0x0C,		/* Cursor X LSB */
+	Cxmsb		= 0x0D,		/* Cursor X MSB */
+	Cylsb		= 0x0E,		/* Cursor Y LSB */
+	Cymsb		= 0x0F,		/* Cursor Y MSB */
+
+	Icctl		= 0x06,		/* Indirect Cursor Control */
+};
+
+/*
+ * Lower 2-bits of indirect DAC register
+ * addressing.
+ */
+static ushort dacxreg[4] = {
+	PaddrW, Pdata, Pixmask, PaddrR
+};
+
+static uchar
+tvp3026io(uchar reg, uchar data)
+{
+	uchar crt55;
+
+	crt55 = vgaxi(Crtx, 0x55) & 0xFC;
+	vgaxo(Crtx, 0x55, crt55|((reg>>2) & 0x03));
+	vgao(dacxreg[reg & 0x03], data);
+
+	return crt55;
+}
+
+static void
+tvp3026o(uchar reg, uchar data)
+{
+	uchar crt55;
+
+	crt55 = tvp3026io(reg, data);
+	vgaxo(Crtx, 0x55, crt55);
+}
+
+void
+tvp3026xo(uchar index, uchar data)
+{
+	uchar crt55;
+
+	crt55 = tvp3026io(Index, index);
+	vgaxo(Crtx, 0x55, crt55|((Data>>2) & 0x03));
+	vgao(dacxreg[Data & 0x03], data);
+	vgaxo(Crtx, 0x55, crt55);
+}
+
+static void
+tvp3026disable(VGAscr*)
+{
+	tvp3026xo(Icctl, 0x90);
+	tvp3026o(Cctl, 0x00);
+}
+
+static void
+tvp3026enable(VGAscr*)
+{
+	/*
+	 * Make sure cursor is off and direct control enabled.
+	 */
+	tvp3026xo(Icctl, 0x90);
+	tvp3026o(Cctl, 0x00);
+
+	/*
+	 * Overscan colour,
+	 * cursor colour 1 (white),
+	 * cursor colour 2, 3 (black).
+	 */
+	tvp3026o(CaddrW, 0x00);
+	tvp3026o(Cdata, Pwhite); tvp3026o(Cdata, Pwhite); tvp3026o(Cdata, Pwhite);
+	tvp3026o(Cdata, Pwhite); tvp3026o(Cdata, Pwhite); tvp3026o(Cdata, Pwhite);
+	tvp3026o(Cdata, Pblack); tvp3026o(Cdata, Pblack); tvp3026o(Cdata, Pblack);
+	tvp3026o(Cdata, Pblack); tvp3026o(Cdata, Pblack); tvp3026o(Cdata, Pblack);
+
+	/*
+	 * Enable the cursor in 3-colour mode.
+	 */
+	tvp3026o(Cctl, 0x01);
+}
+
+static void
+tvp3026load(VGAscr* scr, Cursor* curs)
+{
+	int x, y;
+
+	/*
+	 * Make sure cursor is off by initialising the cursor
+	 * control to defaults.
+	 * Write to the indirect control register to make sure
+	 * direct register is enabled and upper 2 bits of cursor
+	 * RAM address are 0.
+	 * The LSBs of the cursor RAM address are in PaddrW.
+	 */
+	tvp3026xo(Icctl, 0x90);
+	tvp3026o(Cctl, 0x00);
+	vgao(PaddrW, 0x00);
+
+	/*
+	 * Initialise the 64x64 cursor RAM array. There are 2 planes,
+	 * p0 and p1. Data is written 8 pixels per byte, with p0 in the
+	 * first 512 bytes of the array and p1 in the second.
+	 * The cursor is set in 3-colour mode which gives the following
+	 * truth table:
+	 *	p1 p0	colour
+	 *	 0  0	transparent
+	 *	 0  1	cursor colour 0
+	 *	 1  0	cursor colour 1
+	 *	 1  1	cursor colour 2
+	 * Put the cursor into the top-left of the 64x64 array.
+	 * The 0,0 cursor point is bottom-right, so positioning will
+	 * have to take that into account.
+	 */
+	for(y = 0; y < 64; y++){
+		for(x = 0; x < 64/8; x++){
+			if(x < 16/8 && y < 16)
+				tvp3026o(Cram, curs->clr[x+y*2]);
+			else
+				tvp3026o(Cram, 0x00);
+		}
+	}
+	for(y = 0; y < 64; y++){
+		for(x = 0; x < 64/8; x++){
+			if(x < 16/8 && y < 16)
+				tvp3026o(Cram, curs->set[x+y*2]);
+			else
+				tvp3026o(Cram, 0x00);
+		}
+	}
+
+	/*
+	 * Initialise the cursor hotpoint
+	 * and enable the cursor in 3-colour mode.
+	 */
+	scr->offset.x = 64+curs->offset.x;
+	scr->offset.y = 64+curs->offset.y;
+	tvp3026o(Cctl, 0x01);
+}
+
+static int
+tvp3026move(VGAscr* scr, Point p)
+{
+	int x, y;
+
+	x = p.x+scr->offset.x;
+	y = p.y+scr->offset.y;
+
+	tvp3026o(Cxlsb, x & 0xFF);
+	tvp3026o(Cxmsb, (x>>8) & 0x0F);
+	tvp3026o(Cylsb, y & 0xFF);
+	tvp3026o(Cymsb, (y>>8) & 0x0F);
+
+	return 0;
+}
+
+VGAcur vgatvp3026cur = {
+	"tvp3026hwgc",
+
+	tvp3026enable,
+	tvp3026disable,
+	tvp3026load,
+	tvp3026move,
+};
--- /dev/null
+++ b/os/pc/vgavmware.c
@@ -1,0 +1,386 @@
+#include "u.h"
+#include "../port/lib.h"
+#include "mem.h"
+#include "dat.h"
+#include "fns.h"
+#include "io.h"
+#include "../port/error.h"
+
+#define	Image	IMAGE
+#include <draw.h>
+#include <memdraw.h>
+#include <cursor.h>
+#include "screen.h"
+
+enum {
+	PCIVMWARE	= 0x15AD,	/* PCI VID */
+
+	VMWARE1		= 0x0710,	/* PCI DID */
+	VMWARE2		= 0x0405,
+};
+
+enum {
+	Rid = 0,
+	Renable,
+	Rwidth,
+	Rheight,
+	Rmaxwidth,
+
+	Rmaxheight,
+	Rdepth,
+	Rbpp,
+	Rpseudocolor,
+	Rrmask,
+
+	Rgmask,
+	Rbmask,
+	Rbpl,
+	Rfbstart,
+	Rfboffset,
+
+	Rfbmaxsize,
+	Rfbsize,
+	Rcap,
+	Rmemstart,
+	Rmemsize,
+
+	Rconfigdone,
+	Rsync,
+	Rbusy,
+	Rguestid,
+	Rcursorid,
+
+	Rcursorx,
+	Rcursory,
+	Rcursoron,
+	Nreg,
+
+	Crectfill = 1<<0,
+	Crectcopy = 1<<1,
+	Crectpatfill = 1<<2,
+	Coffscreen = 1<<3,
+	Crasterop = 1<<4,
+	Ccursor = 1<<5,
+	Ccursorbypass = 1<<6,
+	Ccursorbypass2 = 1<<7,
+	C8bitemulation = 1<<8,
+	Calphacursor = 1<<9,
+
+	FifoMin = 0,
+	FifoMax = 1,
+	FifoNextCmd = 2,
+	FifoStop = 3,
+	FifoUser = 4,
+
+	Xupdate = 1,
+	Xrectfill = 2,
+	Xrectcopy = 3,
+	Xdefinebitmap = 4,
+	Xdefinebitmapscanline = 5,
+	Xdefinepixmap = 6,
+	Xdefinepixmapscanline = 7,
+	Xrectbitmapfill = 8,
+	Xrectpixmapfill = 9,
+	Xrectbitmapcopy = 10,
+	Xrectpixmapcopy = 11,
+	Xfreeobject = 12,
+	Xrectropfill = 13,
+	Xrectropcopy = 14,
+	Xrectropbitmapfill = 15,
+	Xrectroppixmapfill = 16,
+	Xrectropbitmapcopy = 17,
+	Xrectroppixmapcopy = 18,
+	Xdefinecursor = 19,
+	Xdisplaycursor = 20,
+	Xmovecursor = 21,
+	Xdefinealphacursor = 22,
+	Xcmdmax = 23,
+
+	CursorOnHide = 0,
+	CursorOnShow = 1,
+	CursorOnRemoveFromFb = 2,
+	CursorOnRestoreToFb = 3,
+
+	Rpalette = 1024,
+};
+
+typedef struct Vmware	Vmware;
+struct Vmware {
+	ulong	fb;
+
+	ulong	ra;
+	ulong	rd;
+
+	ulong	r[Nreg];
+	ulong	*mmio;
+	ulong	mmiosize;
+
+	char	chan[32];
+	int	depth;
+};
+
+Vmware xvm;
+Vmware *vm=&xvm;
+
+static ulong
+vmrd(Vmware *vm, int i)
+{
+	outl(vm->ra, i);
+	return inl(vm->rd);
+}
+
+static void
+vmwr(Vmware *vm, int i, ulong v)
+{
+	outl(vm->ra, i);
+	outl(vm->rd, v);
+}
+
+static void
+vmwait(Vmware *vm)
+{
+	vmwr(vm, Rsync, 1);
+	while(vmrd(vm, Rbusy))
+		;
+}
+
+static ulong
+vmwarelinear(VGAscr* scr, int* size, int* align)
+{
+	char err[64];
+	ulong aperture, oaperture;
+	int osize, oapsize, wasupamem;
+	Pcidev *p;
+
+	osize = *size;
+	oaperture = scr->aperture;
+	oapsize = scr->apsize;
+	wasupamem = scr->isupamem;
+
+	p = pcimatch(nil, PCIVMWARE, 0);
+	if(p == nil)
+		error("no vmware card found");
+
+	switch(p->did){
+	default:
+		snprint(err, sizeof err, "unknown vmware id %.4ux", p->did);
+		error(err);
+		
+	case VMWARE1:
+		vm->ra = 0x4560;
+		vm->rd = 0x4560+4;
+		break;
+
+	case VMWARE2:
+		vm->ra = p->mem[0].bar&~3;
+		vm->rd = vm->ra + 1;
+	}
+
+	aperture = (ulong)(vmrd(vm, Rfbstart));
+	*size = vmrd(vm, Rfbsize);
+
+	if(wasupamem)
+		upafree(oaperture, oapsize);
+	scr->isupamem = 0;
+
+	aperture = upamalloc(aperture, *size, *align);
+	if(aperture == 0){
+		if(wasupamem && upamalloc(oaperture, oapsize, 0))
+			scr->isupamem = 1;
+	}else
+		scr->isupamem = 1;
+
+	if(oaperture && aperture != oaperture)
+		print("warning (BUG): redefinition of aperture does not change vmwarescreen segment\n");
+	addvgaseg("vmwarescreen", aperture, osize);
+
+	return aperture;
+}
+
+static void
+vmfifowr(Vmware *vm, ulong v)
+{
+	ulong *mm;
+
+	mm = vm->mmio;
+	if(mm == nil){
+		iprint("!");
+		return;
+	}
+
+	if(mm[FifoNextCmd]+sizeof(ulong) == mm[FifoStop]
+	|| (mm[FifoNextCmd]+sizeof(ulong) == mm[FifoMax]
+	    && mm[FifoStop] == mm[FifoMin]))
+		vmwait(vm);
+
+	mm[mm[FifoNextCmd]/sizeof(ulong)] = v;
+
+	/* must do this way so mm[FifoNextCmd] is never mm[FifoMax] */
+	v = mm[FifoNextCmd] + sizeof(ulong);
+	if(v == mm[FifoMax])
+		v = mm[FifoMin];
+	mm[FifoNextCmd] = v;
+}
+
+static void
+vmwareflush(VGAscr*, Rectangle r)
+{
+	if(vm->mmio == nil)
+		return;
+
+	vmfifowr(vm, Xupdate);
+	vmfifowr(vm, r.min.x);
+	vmfifowr(vm, r.min.y);
+	vmfifowr(vm, r.max.x-r.min.x);
+	vmfifowr(vm, r.max.y-r.min.y);
+	vmwait(vm);
+}
+
+static void
+vmwareload(VGAscr*, Cursor *c)
+{
+	int i;
+	ulong clr, set;
+	ulong and[16];
+	ulong xor[16];
+
+	if(vm->mmio == nil)
+		return;
+	vmfifowr(vm, Xdefinecursor);
+	vmfifowr(vm, 1);	/* cursor id */
+	vmfifowr(vm, -c->offset.x);
+	vmfifowr(vm, -c->offset.y);
+
+	vmfifowr(vm, 16);	/* width */
+	vmfifowr(vm, 16);	/* height */
+	vmfifowr(vm, 1);	/* depth for and mask */
+	vmfifowr(vm, 1);	/* depth for xor mask */
+
+	for(i=0; i<16; i++){
+		clr = (c->clr[i*2+1]<<8) | c->clr[i*2];
+		set = (c->set[i*2+1]<<8) | c->set[i*2];
+		and[i] = ~(clr|set);	/* clr and set pixels => black */
+		xor[i] = clr&~set;		/* clr pixels => white */
+	}
+	for(i=0; i<16; i++)
+		vmfifowr(vm, and[i]);
+	for(i=0; i<16; i++)
+		vmfifowr(vm, xor[i]);
+
+	vmwait(vm);
+}
+
+static int
+vmwaremove(VGAscr*, Point p)
+{
+	vmwr(vm, Rcursorid, 1);
+	vmwr(vm, Rcursorx, p.x);
+	vmwr(vm, Rcursory, p.y);
+	vmwr(vm, Rcursoron, CursorOnShow);
+	return 0;
+}
+
+static void
+vmwaredisable(VGAscr*)
+{
+	vmwr(vm, Rcursorid, 1);
+	vmwr(vm, Rcursoron, CursorOnHide);
+}
+
+static void
+vmwareenable(VGAscr*)
+{
+	vmwr(vm, Rcursorid, 1);
+	vmwr(vm, Rcursoron, CursorOnShow);
+}
+
+static void
+vmwareblank(int)
+{
+}
+
+static int
+vmwarescroll(VGAscr*, Rectangle r, Rectangle sr)
+{
+	if(vm->mmio == nil)
+		return 0;
+	vmfifowr(vm, Xrectcopy);
+	vmfifowr(vm, sr.min.x);
+	vmfifowr(vm, sr.min.y);
+	vmfifowr(vm, r.min.x);
+	vmfifowr(vm, r.min.y);
+	vmfifowr(vm, Dx(r));
+	vmfifowr(vm, Dy(r));
+	vmwait(vm);
+	return 1;
+}
+
+static int
+vmwarefill(VGAscr*, Rectangle r, ulong sval)
+{
+	if(vm->mmio == nil)
+		return 0;
+	vmfifowr(vm, Xrectfill);
+	vmfifowr(vm, sval);
+	vmfifowr(vm, r.min.x);
+	vmfifowr(vm, r.min.y);
+	vmfifowr(vm, r.max.x-r.min.x);
+	vmfifowr(vm, r.max.y-r.min.y);
+	vmwait(vm);
+	return 1;
+}
+
+static void
+vmwaredrawinit(VGAscr *scr)
+{
+	ulong offset;
+	ulong mmiobase, mmiosize;
+
+	if(scr->mmio==nil){
+		mmiobase = vmrd(vm, Rmemstart);
+		if(mmiobase == 0)
+			return;
+		mmiosize = vmrd(vm, Rmemsize);
+		scr->mmio = KADDR(upamalloc(mmiobase, mmiosize, 0));
+		vm->mmio = scr->mmio;
+		vm->mmiosize = mmiosize;
+		if(scr->mmio == nil)
+			return;
+		addvgaseg("vmwaremmio", mmiobase, mmiosize);
+	}
+
+	scr->mmio[FifoMin] = 4*sizeof(ulong);
+	scr->mmio[FifoMax] = vm->mmiosize;
+	scr->mmio[FifoNextCmd] = 4*sizeof(ulong);
+	scr->mmio[FifoStop] = 4*sizeof(ulong);
+	vmwr(vm, Rconfigdone, 1);
+
+	scr->scroll = vmwarescroll;
+	scr->fill = vmwarefill;
+
+	offset = vmrd(vm, Rfboffset);
+	scr->gscreendata->bdata += offset;
+}
+
+VGAdev vgavmwaredev = {
+	"vmware",
+
+	0,
+	0,
+	0,
+	vmwarelinear,
+	vmwaredrawinit,
+	0,
+	0,
+	0,
+	vmwareflush,
+};
+
+VGAcur vgavmwarecur = {
+	"vmwarehwgc",
+
+	vmwareenable,
+	vmwaredisable,
+	vmwareload,
+	vmwaremove,
+};
--- /dev/null
+++ b/os/pc/vgax.c
@@ -1,0 +1,102 @@
+#include "u.h"
+#include "../port/lib.h"
+#include "mem.h"
+#include "dat.h"
+#include "fns.h"
+#include "../port/error.h"
+
+#define	Image	IMAGE
+#include <draw.h>
+#include <memdraw.h>
+#include <cursor.h>
+#include "screen.h"
+
+static Lock vgaxlock;			/* access to index registers */
+
+int
+vgaxi(long port, uchar index)
+{
+	uchar data;
+
+	ilock(&vgaxlock);
+	switch(port){
+
+	case Seqx:
+	case Crtx:
+	case Grx:
+		outb(port, index);
+		data = inb(port+1);
+		break;
+
+	case Attrx:
+		/*
+		 * Allow processor access to the colour
+		 * palette registers. Writes to Attrx must
+		 * be preceded by a read from Status1 to
+		 * initialise the register to point to the
+		 * index register and not the data register.
+		 * Processor access is allowed by turning
+		 * off bit 0x20.
+		 */
+		inb(Status1);
+		if(index < 0x10){
+			outb(Attrx, index);
+			data = inb(Attrx+1);
+			inb(Status1);
+			outb(Attrx, 0x20|index);
+		}
+		else{
+			outb(Attrx, 0x20|index);
+			data = inb(Attrx+1);
+		}
+		break;
+
+	default:
+		iunlock(&vgaxlock);
+		return -1;
+	}
+	iunlock(&vgaxlock);
+
+	return data & 0xFF;
+}
+
+int
+vgaxo(long port, uchar index, uchar data)
+{
+	ilock(&vgaxlock);
+	switch(port){
+
+	case Seqx:
+	case Crtx:
+	case Grx:
+		/*
+		 * We could use an outport here, but some chips
+		 * (e.g. 86C928) have trouble with that for some
+		 * registers.
+		 */
+		outb(port, index);
+		outb(port+1, data);
+		break;
+
+	case Attrx:
+		inb(Status1);
+		if(index < 0x10){
+			outb(Attrx, index);
+			outb(Attrx, data);
+			inb(Status1);
+			outb(Attrx, 0x20|index);
+		}
+		else{
+			outb(Attrx, 0x20|index);
+			outb(Attrx, data);
+		}
+		break;
+
+	default:
+		iunlock(&vgaxlock);
+		return -1;
+	}
+	iunlock(&vgaxlock);
+
+	return 0;
+}
--- /dev/null
+++ b/os/pc/wavelan.c
@@ -1,0 +1,1268 @@
+/*
+	Lucent Wavelan IEEE 802.11 pcmcia.
+	There is almost no documentation for the card.
+	the driver is done using both the FreeBSD, Linux and
+	original Plan 9 drivers as `documentation'.
+
+	Has been used with the card plugged in during all up time.
+	no cards removals/insertions yet.
+
+	For known BUGS see the comments below. Besides,
+	the driver keeps interrupts disabled for just too
+	long. When it gets robust, locks should be revisited.
+
+	BUGS: check endian, alignment and mem/io issues;
+	      multicast;
+	      receive watchdog interrupts.
+	TODO: automatic power management;
+	      improve locking.
+ */
+#include "u.h"
+#include "../port/lib.h"
+#include "mem.h"
+#include "dat.h"
+#include "fns.h"
+#include "io.h"
+#include "../port/error.h"
+#include "../port/netif.h"
+#include "etherif.h"
+#include "wavelan.h"
+
+enum
+{
+	MSperTick=	50,	/* ms between ticks of kproc */
+};
+
+/*
+ * When we're using a PCI device and memory-mapped I/O, 
+ * the registers are spaced out as though each takes 32 bits,
+ * even though they are only 16-bit registers.  Thus, 
+ * ctlr->mmb[reg] is the right way to access register reg,
+ * even though a priori you'd expect to use ctlr->mmb[reg/2].
+ */
+void
+csr_outs(Ctlr *ctlr, int reg, ushort arg)
+{
+	if(ctlr->mmb)
+		ctlr->mmb[reg] = arg;
+	else
+		outs(ctlr->iob+reg, arg);
+}
+
+ushort
+csr_ins(Ctlr *ctlr, int reg)
+{
+	if(ctlr->mmb)
+		return ctlr->mmb[reg];
+	else
+		return ins(ctlr->iob+reg);
+}
+
+static void
+csr_ack(Ctlr *ctlr, int ev)
+{
+	csr_outs(ctlr, WR_EvAck, ev);
+}
+
+static void
+csr_inss(Ctlr *ctlr, int reg, void *dat, int ndat)
+{
+	ushort *rp, *wp;
+
+	if(ctlr->mmb){
+		rp = &ctlr->mmb[reg];
+		wp = dat;
+		while(ndat-- > 0)
+			*wp++ = *rp;
+	}else
+		inss(ctlr->iob+reg, dat, ndat);
+}
+
+static void
+csr_outss(Ctlr *ctlr, int reg, void *dat, int ndat)
+{
+	ushort *rp, *wp;
+
+	if(ctlr->mmb){
+		rp = dat;
+		wp = &ctlr->mmb[reg];
+		while(ndat-- > 0)
+			*wp = *rp++;
+	}else
+		outss(ctlr->iob+reg, dat, ndat);
+}
+
+// w_... routines do not ilock the Ctlr and should
+// be called locked.
+
+void
+w_intdis(Ctlr* ctlr)
+{
+	csr_outs(ctlr, WR_IntEna, 0);
+	csr_ack(ctlr, 0xffff);
+}
+
+static void
+w_intena(Ctlr* ctlr)
+{
+	csr_outs(ctlr, WR_IntEna, WEvs);
+}
+
+int
+w_cmd(Ctlr *ctlr, ushort cmd, ushort arg)
+{
+	int i, rc;
+
+	for(i=0; i<WTmOut; i++)
+		if((csr_ins(ctlr, WR_Cmd)&WCmdBusy) == 0)
+			break;
+	if(i==WTmOut){
+		print("#l%d: issuing cmd %.4ux: %.4ux\n", ctlr->ctlrno, cmd, csr_ins(ctlr, WR_Cmd));
+		return -1;
+	}
+
+	csr_outs(ctlr, WR_Parm0, arg);
+	csr_outs(ctlr, WR_Cmd, cmd);
+
+	for(i=0; i<WTmOut; i++)
+		if(csr_ins(ctlr, WR_EvSts)&WCmdEv)
+			break;
+	if(i==WTmOut){
+		/*
+		 * WCmdIni can take a really long time.
+		 */
+		enum { IniTmOut = 2000 };
+		for(i=0; i<IniTmOut; i++){
+			if(csr_ins(ctlr, WR_EvSts)&WCmdEv)
+				break;
+			microdelay(100);
+		}
+		if(i < IniTmOut)
+			if(0) print("#l%d: long cmd %.4ux %d\n", ctlr->ctlrno, cmd, i);
+		if(i == IniTmOut){
+			print("#l%d: execing cmd %.4ux: %.4ux\n", ctlr->ctlrno, cmd, csr_ins(ctlr, WR_EvSts));
+			return -1;
+		}
+	}
+	rc = csr_ins(ctlr, WR_Sts);
+	csr_ack(ctlr, WCmdEv);
+
+	if((rc&WCmdMsk) != (cmd&WCmdMsk)){
+		print("#l%d: cmd %.4ux: status %.4ux\n", ctlr->ctlrno, cmd, rc);
+		return -1;
+	}
+	if(rc&WResSts){
+		/*
+		 * Don't print; this happens on every WCmdAccWr for some reason.
+		 */
+		if(0) print("#l%d: cmd %.4ux: status %.4ux\n", ctlr->ctlrno, cmd, rc);
+		return -1;
+	}
+	return 0;
+}
+
+static int
+w_seek(Ctlr* ctlr, ushort id, ushort offset, int chan)
+{
+	int i, rc;
+	static ushort sel[] = { WR_Sel0, WR_Sel1 };
+	static ushort off[] = { WR_Off0, WR_Off1 };
+
+	if(chan != 0 && chan != 1)
+		panic("wavelan: bad chan\n");
+	csr_outs(ctlr, sel[chan], id);
+	csr_outs(ctlr, off[chan], offset);
+	for (i=0; i<WTmOut; i++){
+		rc = csr_ins(ctlr, off[chan]);
+		if((rc & (WBusyOff|WErrOff)) == 0)
+			return 0;
+	}
+	return -1;
+}
+
+int
+w_inltv(Ctlr* ctlr, Wltv* ltv)
+{
+	int len;
+	ushort code;
+
+	if(w_cmd(ctlr, WCmdAccRd, ltv->type)){
+		DEBUG("wavelan: access read failed\n");
+		return -1;
+	}
+	if(w_seek(ctlr,ltv->type,0,1)){
+		DEBUG("wavelan: seek failed\n");
+		return -1;
+	}
+	len = csr_ins(ctlr, WR_Data1);
+	if(len > ltv->len)
+		return -1;
+	ltv->len = len;
+	if((code=csr_ins(ctlr, WR_Data1)) != ltv->type){
+		USED(code);
+		DEBUG("wavelan: type %x != code %x\n",ltv->type,code);
+		return -1;
+	}
+	if(ltv->len > 0)
+		csr_inss(ctlr, WR_Data1, &ltv->val, ltv->len-1);
+
+	return 0;
+}
+
+static void
+w_outltv(Ctlr* ctlr, Wltv* ltv)
+{
+	if(w_seek(ctlr,ltv->type, 0, 1))
+		return;
+	csr_outss(ctlr, WR_Data1, ltv, ltv->len+1);
+	w_cmd(ctlr, WCmdAccWr, ltv->type);
+}
+
+void
+ltv_outs(Ctlr* ctlr, int type, ushort val)
+{
+	Wltv ltv;
+
+	ltv.len = 2;
+	ltv.type = type;
+	ltv.val = val;
+	w_outltv(ctlr, &ltv);
+}
+
+int
+ltv_ins(Ctlr* ctlr, int type)
+{
+	Wltv ltv;
+
+	ltv.len = 2;
+	ltv.type = type;
+	ltv.val = 0;
+	if(w_inltv(ctlr, &ltv))
+		return -1;
+	return ltv.val;
+}
+
+static void
+ltv_outstr(Ctlr* ctlr, int type, char* val)
+{
+	Wltv ltv;
+	int len;
+
+	len = strlen(val);
+	if(len > sizeof(ltv.s))
+		len = sizeof(ltv.s);
+	memset(&ltv, 0, sizeof(ltv));
+	ltv.len = (sizeof(ltv.type)+sizeof(ltv.slen)+sizeof(ltv.s))/2;
+	ltv.type = type;
+
+//	This should be ltv.slen = len; according to Axel Belinfante
+	ltv.slen = len;	
+
+	strncpy(ltv.s, val, len);
+	w_outltv(ctlr, &ltv);
+}
+
+static char Unkname[] = "who knows";
+static char Nilname[] = "card does not tell";
+
+static char*
+ltv_inname(Ctlr* ctlr, int type)
+{
+	static Wltv ltv;
+	int len;
+
+	memset(&ltv,0,sizeof(ltv));
+	ltv.len = WNameLen/2+2;
+	ltv.type = type;
+	if(w_inltv(ctlr, &ltv))
+		return Unkname;
+	len = ltv.slen;
+	if(len == 0 || ltv.s[0] == 0)
+		return Nilname;
+	if(len >= sizeof ltv.s)
+		len = sizeof ltv.s - 1;
+	ltv.s[len] = '\0';
+	return ltv.s;
+}
+
+static int
+w_read(Ctlr* ctlr, int type, int off, void* buf, ulong len)
+{
+	if(w_seek(ctlr, type, off, 1)){
+		DEBUG("wavelan: w_read: seek failed");
+		return 0;
+	}
+	csr_inss(ctlr, WR_Data1, buf, len/2);
+
+	return len;
+}
+
+static int
+w_write(Ctlr* ctlr, int type, int off, void* buf, ulong len)
+{
+	int tries;
+
+	for (tries=0; tries < WTmOut; tries++){
+		if(w_seek(ctlr, type, off, 0)){
+			DEBUG("wavelan: w_write: seek failed\n");
+			return 0;
+		}
+
+		csr_outss(ctlr, WR_Data0, buf, len/2);
+
+		csr_outs(ctlr, WR_Data0, 0xdead);
+		csr_outs(ctlr, WR_Data0, 0xbeef);
+		if(w_seek(ctlr, type, off + len, 0)){
+			DEBUG("wavelan: write seek failed\n");
+			return 0;
+		}
+		if(csr_ins(ctlr, WR_Data0) == 0xdead)
+		if(csr_ins(ctlr, WR_Data0) == 0xbeef)
+			return len;
+		DEBUG("wavelan: Hermes bug byte.\n");
+		return 0;
+	}
+	DEBUG("wavelan: tx timeout\n");
+	return 0;
+}
+
+static int
+w_alloc(Ctlr* ctlr, int len)
+{
+	int rc;
+	int i,j;
+
+	if(w_cmd(ctlr, WCmdMalloc, len)==0)
+		for (i = 0; i<WTmOut; i++)
+			if(csr_ins(ctlr, WR_EvSts) & WAllocEv){
+				csr_ack(ctlr, WAllocEv);
+				rc=csr_ins(ctlr, WR_Alloc);
+				if(w_seek(ctlr, rc, 0, 0))
+					return -1;
+				len = len/2;
+				for (j=0; j<len; j++)
+					csr_outs(ctlr, WR_Data0, 0);
+				return rc;
+			}
+	return -1;
+}
+
+static int
+w_enable(Ether* ether)
+{
+	Wltv ltv;
+	Ctlr* ctlr = (Ctlr*) ether->ctlr;
+
+	if(!ctlr)
+		return -1;
+
+	w_intdis(ctlr);
+	w_cmd(ctlr, WCmdDis, 0);
+	w_intdis(ctlr);
+	if(w_cmd(ctlr, WCmdIni, 0))
+		return -1;
+	w_intdis(ctlr);
+
+	ltv_outs(ctlr, WTyp_Tick, 8);
+	ltv_outs(ctlr, WTyp_MaxLen, ctlr->maxlen);
+	ltv_outs(ctlr, WTyp_Ptype, ctlr->ptype);
+ 	ltv_outs(ctlr, WTyp_CreateIBSS, ctlr->createibss);
+	ltv_outs(ctlr, WTyp_RtsThres, ctlr->rtsthres);
+	ltv_outs(ctlr, WTyp_TxRate, ctlr->txrate);
+	ltv_outs(ctlr, WTyp_ApDens, ctlr->apdensity);
+	ltv_outs(ctlr, WTyp_PMWait, ctlr->pmwait);
+	ltv_outs(ctlr, WTyp_PM, ctlr->pmena);
+	if(*ctlr->netname)
+		ltv_outstr(ctlr, WTyp_NetName, ctlr->netname);
+	if(*ctlr->wantname)
+		ltv_outstr(ctlr, WTyp_WantName, ctlr->wantname);
+	ltv_outs(ctlr, WTyp_Chan, ctlr->chan);
+	if(*ctlr->nodename)
+		ltv_outstr(ctlr, WTyp_NodeName, ctlr->nodename);
+	ltv.len = 4;
+	ltv.type = WTyp_Mac;
+	memmove(ltv.addr, ether->ea, Eaddrlen);
+	w_outltv(ctlr, &ltv);
+
+	ltv_outs(ctlr, WTyp_Prom, (ether->prom?1:0));
+
+	if(ctlr->hascrypt && ctlr->crypt){
+		ltv_outs(ctlr, WTyp_Crypt, ctlr->crypt);
+		ltv_outs(ctlr, WTyp_TxKey, ctlr->txkey);
+		w_outltv(ctlr, &ctlr->keys);
+		ltv_outs(ctlr, WTyp_XClear, ctlr->xclear);
+	}
+
+	// BUG: set multicast addresses
+
+	if(w_cmd(ctlr, WCmdEna, 0)){
+		DEBUG("wavelan: Enable failed");
+		return -1;
+	}
+	ctlr->txdid = w_alloc(ctlr, 1518 + sizeof(WFrame) + 8);
+	ctlr->txmid = w_alloc(ctlr, 1518 + sizeof(WFrame) + 8);
+	if(ctlr->txdid == -1 || ctlr->txmid == -1)
+		DEBUG("wavelan: alloc failed");
+	ctlr->txbusy = 0;
+	w_intena(ctlr);
+	return 0;
+}
+
+static void
+w_rxdone(Ether* ether)
+{
+	Ctlr* ctlr = (Ctlr*) ether->ctlr;
+	int len, sp;
+	WFrame f;
+	Block* bp=0;
+	Etherpkt* ep;
+
+	sp = csr_ins(ctlr, WR_RXId);
+	len = w_read(ctlr, sp, 0, &f, sizeof(f));
+	if(len == 0){
+		DEBUG("wavelan: read frame error\n");
+		goto rxerror;
+	}
+	if(f.sts&WF_Err){
+		goto rxerror;
+	}
+	switch(f.sts){
+	case WF_1042:
+	case WF_Tunnel:
+	case WF_WMP:
+		len = f.dlen + WSnapHdrLen;
+		bp = iallocb(ETHERHDRSIZE + len + 2);
+		if(!bp)
+			goto rxerror;
+		ep = (Etherpkt*) bp->wp;
+		memmove(ep->d, f.addr1, Eaddrlen);
+		memmove(ep->s, f.addr2, Eaddrlen);
+		memmove(ep->type,&f.type,2);
+		bp->wp += ETHERHDRSIZE;
+		if(w_read(ctlr, sp, WF_802_11_Off, bp->wp, len+2) == 0){
+			DEBUG("wavelan: read 802.11 error\n");
+			goto rxerror;
+		}
+		bp->wp = bp->rp+(ETHERHDRSIZE+f.dlen);
+		break;
+	default:
+		len = ETHERHDRSIZE + f.dlen + 2;
+		bp = iallocb(len);
+		if(!bp)
+			goto rxerror;
+		if(w_read(ctlr, sp, WF_802_3_Off, bp->wp, len) == 0){
+			DEBUG("wavelan: read 800.3 error\n");
+			goto rxerror;
+		}
+		bp->wp += len;
+	}
+
+	ctlr->nrx++;
+	etheriq(ether,bp,1);
+	ctlr->signal = ((ctlr->signal*15)+((f.qinfo>>8) & 0xFF))/16;
+	ctlr->noise = ((ctlr->noise*15)+(f.qinfo & 0xFF))/16;
+	return;
+
+rxerror:
+	freeb(bp);
+	ctlr->nrxerr++;
+}
+
+static void
+w_txstart(Ether* ether)
+{
+	Etherpkt *pkt;
+	Ctlr *ctlr;
+	Block *bp;
+	int len, off;
+
+	if((ctlr = ether->ctlr) == nil || (ctlr->state & (Attached|Power)) != (Attached|Power) || ctlr->txbusy)
+		return;
+
+	if((bp = qget(ether->oq)) == nil)
+		return;
+	pkt = (Etherpkt*)bp->rp;
+
+	//
+	// If the packet header type field is > 1500 it is an IP or
+	// ARP datagram, otherwise it is an 802.3 packet. See RFC1042.
+	//
+	memset(&ctlr->txf, 0, sizeof(ctlr->txf));
+	if(((pkt->type[0]<<8)|pkt->type[1]) > 1500){
+		ctlr->txf.framectl = WF_Data;
+		memmove(ctlr->txf.addr1, pkt->d, Eaddrlen);
+		memmove(ctlr->txf.addr2, pkt->s, Eaddrlen);
+		memmove(ctlr->txf.dstaddr, pkt->d, Eaddrlen);
+		memmove(ctlr->txf.srcaddr, pkt->s, Eaddrlen);
+		memmove(&ctlr->txf.type, pkt->type, 2);
+		bp->rp += ETHERHDRSIZE;
+		len = BLEN(bp);
+		off = WF_802_11_Off;
+		ctlr->txf.dlen = len+ETHERHDRSIZE-WSnapHdrLen;
+		hnputs((uchar*)&ctlr->txf.dat[0], WSnap0);
+		hnputs((uchar*)&ctlr->txf.dat[1], WSnap1);
+		hnputs((uchar*)&ctlr->txf.len, len+ETHERHDRSIZE-WSnapHdrLen);
+	}
+	else{
+		len = BLEN(bp);
+		off = WF_802_3_Off;
+		ctlr->txf.dlen = len;
+	}
+	w_write(ctlr, ctlr->txdid, 0, &ctlr->txf, sizeof(ctlr->txf));
+	w_write(ctlr, ctlr->txdid, off, bp->rp, len+2);
+
+	if(w_cmd(ctlr, WCmdReclaim|WCmdTx, ctlr->txdid)){
+		DEBUG("wavelan: transmit failed\n");
+		ctlr->ntxerr++;
+	}
+	else{
+		ctlr->txbusy = 1;
+		ctlr->txtmout = 2;
+	}
+	freeb(bp);
+}
+
+static void
+w_txdone(Ctlr* ctlr, int sts)
+{
+	ctlr->txbusy = 0;
+	ctlr->txtmout = 0;
+	if(sts & WTxErrEv)
+		ctlr->ntxerr++;
+	else
+		ctlr->ntx++;
+}
+
+/* save the stats info in the ctlr struct */
+static void
+w_stats(Ctlr* ctlr, int len)
+{
+	int i, rc;
+	ulong* p = (ulong*)&ctlr->WStats;
+	ulong* pend = (ulong*)&ctlr->end;
+
+	for (i = 0; i < len && p < pend; i++){
+		rc = csr_ins(ctlr, WR_Data1);
+		if(rc > 0xf000)
+			rc = ~rc & 0xffff;
+		p[i] += rc;
+	}
+}
+
+/* send the base station scan info to any readers */
+static void
+w_scaninfo(Ether* ether, Ctlr *ctlr, int len)
+{
+	int i, j;
+	Netfile **ep, *f, **fp;
+	Block *bp;
+	WScan *wsp;
+	ushort *scanbuf;
+
+	scanbuf = malloc(len*2);
+	if(scanbuf == nil)
+		return;
+	
+	for (i = 0; i < len ; i++)
+		scanbuf[i] = csr_ins(ctlr, WR_Data1);
+
+	/* calculate number of samples */
+	len /= 25;
+	if(len == 0)
+		goto out;
+
+	i = ether->scan;
+	ep = &ether->f[Ntypes];
+	for(fp = ether->f; fp < ep && i > 0; fp++){
+		f = *fp;
+		if(f == nil || f->scan == 0)
+			continue;
+
+		bp = iallocb(100*len);
+		if(bp == nil)
+			break;
+		for(j = 0; j < len; j++){
+			wsp = (WScan*)(&scanbuf[j*25]);
+			if(wsp->ssid_len > 32)
+				wsp->ssid_len = 32;
+			bp->wp = (uchar*)seprint((char*)bp->wp, (char*)bp->lim,
+				"ssid=%.*s;bssid=%E;signal=%d;noise=%d;chan=%d%s\n",
+				wsp->ssid_len, wsp->ssid, wsp->bssid, wsp->signal,
+				wsp->noise, wsp->chan, (wsp->capinfo&(1<<4))?";wep":"");
+		}
+		qpass(f->in, bp);
+		i--;
+	}
+out:
+	free(scanbuf);
+}
+
+static int
+w_info(Ether *ether, Ctlr* ctlr)
+{
+	int sp;
+	Wltv ltv;
+
+	sp = csr_ins(ctlr, WR_InfoId);
+	ltv.len = ltv.type = 0;
+	w_read(ctlr, sp, 0, &ltv, 4);
+	ltv.len--;
+	switch(ltv.type){
+	case WTyp_Stats:
+		w_stats(ctlr, ltv.len);
+		return 0;
+	case WTyp_Scan:
+		w_scaninfo(ether, ctlr, ltv.len);
+		return 0;
+	}
+	return -1;
+}
+
+/* set scanning interval */
+static void
+w_scanbs(void *a, uint secs)
+{
+	Ether *ether = a;
+	Ctlr* ctlr = (Ctlr*) ether->ctlr;
+
+	ctlr->scanticks = secs*(1000/MSperTick);
+}
+
+static void
+w_intr(Ether *ether)
+{
+	int rc, txid;
+	Ctlr* ctlr = (Ctlr*) ether->ctlr;
+
+	if((ctlr->state & Power) == 0)
+		return;
+
+	if((ctlr->state & Attached) == 0){
+		csr_ack(ctlr, 0xffff);
+		csr_outs(ctlr, WR_IntEna, 0);
+		return;
+	}
+
+	rc = csr_ins(ctlr, WR_EvSts);
+	csr_ack(ctlr, ~WEvs);	// Not interested in them
+	if(rc & WRXEv){
+		w_rxdone(ether);
+		csr_ack(ctlr, WRXEv);
+	}
+	if(rc & WTXEv){
+		w_txdone(ctlr, rc);
+		csr_ack(ctlr, WTXEv);
+	}
+	if(rc & WAllocEv){
+		ctlr->nalloc++;
+		txid = csr_ins(ctlr, WR_Alloc);
+		csr_ack(ctlr, WAllocEv);
+		if(txid == ctlr->txdid){
+			if((rc & WTXEv) == 0)
+				w_txdone(ctlr, rc);
+		}
+	}
+	if(rc & WInfoEv){
+		ctlr->ninfo++;
+		w_info(ether, ctlr);
+		csr_ack(ctlr, WInfoEv);
+	}
+	if(rc & WTxErrEv){
+		w_txdone(ctlr, rc);
+		csr_ack(ctlr, WTxErrEv);
+	}
+	if(rc & WIDropEv){
+		ctlr->nidrop++;
+		csr_ack(ctlr, WIDropEv);
+	}
+	w_txstart(ether);
+}
+
+// Watcher to ensure that the card still works properly and
+// to request WStats updates once a minute.
+// BUG: it runs much more often, see the comment below.
+
+static void
+w_timer(void* arg)
+{
+	Ether* ether = (Ether*) arg;
+	Ctlr* ctlr = (Ctlr*)ether->ctlr;
+
+	ctlr->timerproc = up;
+	for(;;){
+		tsleep(&up->sleep, return0, 0, MSperTick);
+		ctlr = (Ctlr*)ether->ctlr;
+		if(ctlr == 0)
+			break;
+		if((ctlr->state & (Attached|Power)) != (Attached|Power))
+			continue;
+		ctlr->ticks++;
+
+		ilock(ctlr);
+
+		// Seems that the card gets frames BUT does
+		// not send the interrupt; this is a problem because
+		// I suspect it runs out of receive buffers and
+		// stops receiving until a transmit watchdog
+		// reenables the card.
+		// The problem is serious because it leads to
+		// poor rtts.
+		// This can be seen clearly by commenting out
+		// the next if and doing a ping: it will stop
+		// receiving (although the icmp replies are being
+		// issued from the remote) after a few seconds.
+		// Of course this `bug' could be because I'm reading
+		// the card frames in the wrong way; due to the
+		// lack of documentation I cannot know.
+
+		if(csr_ins(ctlr, WR_EvSts)&WEvs){
+			ctlr->tickintr++;
+			w_intr(ether);
+		}
+
+		if((ctlr->ticks % 10) == 0) {
+			if(ctlr->txtmout && --ctlr->txtmout == 0){
+				ctlr->nwatchdogs++;
+				w_txdone(ctlr, WTxErrEv);
+				if(w_enable(ether)){
+					DEBUG("wavelan: wdog enable failed\n");
+				}
+				w_txstart(ether);
+			}
+			if((ctlr->ticks % 120) == 0)
+			if(ctlr->txbusy == 0)
+				w_cmd(ctlr, WCmdEnquire, WTyp_Stats);
+			if(ctlr->scanticks > 0)
+			if((ctlr->ticks % ctlr->scanticks) == 0)
+			if(ctlr->txbusy == 0)
+				w_cmd(ctlr, WCmdEnquire, WTyp_Scan);
+		}
+		iunlock(ctlr);
+	}
+	pexit("terminated", 0);
+}
+
+void
+w_multicast(void*, uchar*, int)
+{
+	// BUG: to be added.
+}
+
+void
+w_attach(Ether* ether)
+{
+	Ctlr* ctlr;
+	char name[64];
+	int rc;
+
+	if(ether->ctlr == 0)
+		return;
+
+	snprint(name, sizeof(name), "#l%dtimer", ether->ctlrno);
+	ctlr = (Ctlr*) ether->ctlr;
+	if((ctlr->state & Attached) == 0){
+		ilock(ctlr);
+		rc = w_enable(ether);
+		iunlock(ctlr);
+		if(rc == 0){
+			ctlr->state |= Attached;
+			kproc(name, w_timer, ether, 0);
+		} else
+			print("#l%d: enable failed\n",ether->ctlrno);
+	}
+}
+
+void
+w_detach(Ether* ether)
+{
+	Ctlr* ctlr;
+	char name[64];
+
+	if(ether->ctlr == nil)
+		return;
+
+	snprint(name, sizeof(name), "#l%dtimer", ether->ctlrno);
+	ctlr = (Ctlr*) ether->ctlr;
+	if(ctlr->state & Attached){
+		ilock(ctlr);
+		w_intdis(ctlr);
+		if(ctlr->timerproc){
+			if(!postnote(ctlr->timerproc, 1, "kill", 0))
+				print("timerproc note not posted\n");
+			print("w_detach, killing 0x%p\n", ctlr->timerproc);
+		}
+		ctlr->state &= ~Attached;
+		iunlock(ctlr);
+	}
+	ether->ctlr = nil;
+}
+
+void
+w_power(Ether* ether, int on)
+{
+	Ctlr *ctlr;
+
+	ctlr = (Ctlr*) ether->ctlr;
+	ilock(ctlr);
+iprint("w_power %d\n", on);
+	if(on){
+		if((ctlr->state & Power) == 0){
+			if (wavelanreset(ether, ctlr) < 0){
+				iprint("w_power: reset failed\n");
+				iunlock(ctlr);
+				w_detach(ether);
+				free(ctlr);
+				return;
+			}
+			if(ctlr->state & Attached)
+				w_enable(ether);
+			ctlr->state |= Power;
+		}
+	}else{
+		if(ctlr->state & Power){
+			if(ctlr->state & Attached)
+				w_intdis(ctlr);
+			ctlr->state &= ~Power;
+		}
+	}
+	iunlock(ctlr);
+}
+
+#define PRINTSTAT(fmt,val)	l += snprint(p+l, READSTR-l, (fmt), (val))
+#define PRINTSTR(fmt)		l += snprint(p+l, READSTR-l, (fmt))
+
+long
+w_ifstat(Ether* ether, void* a, long n, ulong offset)
+{
+	Ctlr *ctlr = (Ctlr*) ether->ctlr;
+	char *k, *p;
+	int i, l, txid;
+
+	ether->oerrs = ctlr->ntxerr;
+	ether->crcs = ctlr->nrxfcserr;
+	ether->frames = 0;
+	ether->buffs = ctlr->nrxdropnobuf;
+	ether->overflows = 0;
+
+	//
+	// Offset must be zero or there's a possibility the
+	// new data won't match the previous read.
+	//
+	if(n == 0 || offset != 0)
+		return 0;
+
+	p = malloc(READSTR);
+	l = 0;
+
+	PRINTSTAT("Signal: %d\n", ctlr->signal-149);
+	PRINTSTAT("Noise: %d\n", ctlr->noise-149);
+	PRINTSTAT("SNR: %ud\n", ctlr->signal-ctlr->noise);
+	PRINTSTAT("Interrupts: %lud\n", ctlr->nints);
+	PRINTSTAT("Double Interrupts: %lud\n", ctlr->ndoubleint);
+	PRINTSTAT("TxPackets: %lud\n", ctlr->ntx);
+	PRINTSTAT("RxPackets: %lud\n", ctlr->nrx);
+	PRINTSTAT("TxErrors: %lud\n", ctlr->ntxerr);
+	PRINTSTAT("RxErrors: %lud\n", ctlr->nrxerr);
+	PRINTSTAT("TxRequests: %lud\n", ctlr->ntxrq);
+	PRINTSTAT("AllocEvs: %lud\n", ctlr->nalloc);
+	PRINTSTAT("InfoEvs: %lud\n", ctlr->ninfo);
+	PRINTSTAT("InfoDrop: %lud\n", ctlr->nidrop);
+	PRINTSTAT("WatchDogs: %lud\n", ctlr->nwatchdogs);
+	PRINTSTAT("Ticks: %ud\n", ctlr->ticks);
+	PRINTSTAT("TickIntr: %ud\n", ctlr->tickintr);
+	k = ((ctlr->state & Attached) ? "attached" : "not attached");
+	PRINTSTAT("Card %s", k);
+	k = ((ctlr->state & Power) ? "on" : "off");
+	PRINTSTAT(", power %s", k);
+	k = ((ctlr->txbusy)? ", txbusy" : "");
+	PRINTSTAT("%s\n", k);
+
+	if(ctlr->hascrypt){
+		PRINTSTR("Keys: ");
+		for (i = 0; i < WNKeys; i++){
+			if(ctlr->keys.keys[i].len == 0)
+				PRINTSTR("none ");
+			else if(SEEKEYS == 0)
+				PRINTSTR("set ");
+			else
+				PRINTSTAT("%s ", ctlr->keys.keys[i].dat);
+		}
+		PRINTSTR("\n");
+	}
+
+	// real card stats
+	ilock(ctlr);
+	PRINTSTR("\nCard stats: \n");
+	PRINTSTAT("Status: %ux\n", csr_ins(ctlr, WR_Sts));
+	PRINTSTAT("Event status: %ux\n", csr_ins(ctlr, WR_EvSts));
+	i = ltv_ins(ctlr, WTyp_Ptype);
+	PRINTSTAT("Port type: %d\n", i);
+	PRINTSTAT("Transmit rate: %d\n", ltv_ins(ctlr, WTyp_TxRate));
+	PRINTSTAT("Current Transmit rate: %d\n",
+		ltv_ins(ctlr, WTyp_CurTxRate));
+	PRINTSTAT("Channel: %d\n", ltv_ins(ctlr, WTyp_Chan));
+	PRINTSTAT("AP density: %d\n", ltv_ins(ctlr, WTyp_ApDens));
+	PRINTSTAT("Promiscuous mode: %d\n", ltv_ins(ctlr, WTyp_Prom));
+	if(i == WPTypeAdHoc)
+		PRINTSTAT("SSID name: %s\n", ltv_inname(ctlr, WTyp_NetName));
+	else {
+		Wltv ltv;
+		PRINTSTAT("Current name: %s\n", ltv_inname(ctlr, WTyp_CurName));
+		ltv.type = WTyp_BaseID;
+		ltv.len = 4;
+		if(w_inltv(ctlr, &ltv))
+			print("#l%d: unable to read base station mac addr\n", ether->ctlrno);
+		l += snprint(p+l, READSTR-l, "Base station: %2.2x%2.2x%2.2x%2.2x%2.2x%2.2x\n",
+			ltv.addr[0], ltv.addr[1], ltv.addr[2], ltv.addr[3], ltv.addr[4], ltv.addr[5]);
+	}
+	PRINTSTAT("Net name: %s\n", ltv_inname(ctlr, WTyp_WantName));
+	PRINTSTAT("Node name: %s\n", ltv_inname(ctlr, WTyp_NodeName));
+	if(ltv_ins(ctlr, WTyp_HasCrypt) == 0)
+		PRINTSTR("WEP: not supported\n");
+	else {
+		if(ltv_ins(ctlr, WTyp_Crypt) == 0)
+			PRINTSTR("WEP: disabled\n");
+		else{
+			PRINTSTR("WEP: enabled\n");
+			k = ((ctlr->xclear)? "excluded": "included");
+			PRINTSTAT("Clear packets: %s\n", k);
+			txid = ltv_ins(ctlr, WTyp_TxKey);
+			PRINTSTAT("Transmit key id: %d\n", txid);
+		}
+	}
+	iunlock(ctlr);
+
+	PRINTSTAT("ntxuframes: %lud\n", ctlr->ntxuframes);
+	PRINTSTAT("ntxmframes: %lud\n", ctlr->ntxmframes);
+	PRINTSTAT("ntxfrags: %lud\n", ctlr->ntxfrags);
+	PRINTSTAT("ntxubytes: %lud\n", ctlr->ntxubytes);
+	PRINTSTAT("ntxmbytes: %lud\n", ctlr->ntxmbytes);
+	PRINTSTAT("ntxdeferred: %lud\n", ctlr->ntxdeferred);
+	PRINTSTAT("ntxsretries: %lud\n", ctlr->ntxsretries);
+	PRINTSTAT("ntxmultiretries: %lud\n", ctlr->ntxmultiretries);
+	PRINTSTAT("ntxretrylimit: %lud\n", ctlr->ntxretrylimit);
+	PRINTSTAT("ntxdiscards: %lud\n", ctlr->ntxdiscards);
+	PRINTSTAT("nrxuframes: %lud\n", ctlr->nrxuframes);
+	PRINTSTAT("nrxmframes: %lud\n", ctlr->nrxmframes);
+	PRINTSTAT("nrxfrags: %lud\n", ctlr->nrxfrags);
+	PRINTSTAT("nrxubytes: %lud\n", ctlr->nrxubytes);
+	PRINTSTAT("nrxmbytes: %lud\n", ctlr->nrxmbytes);
+	PRINTSTAT("nrxfcserr: %lud\n", ctlr->nrxfcserr);
+	PRINTSTAT("nrxdropnobuf: %lud\n", ctlr->nrxdropnobuf);
+	PRINTSTAT("nrxdropnosa: %lud\n", ctlr->nrxdropnosa);
+	PRINTSTAT("nrxcantdecrypt: %lud\n", ctlr->nrxcantdecrypt);
+	PRINTSTAT("nrxmsgfrag: %lud\n", ctlr->nrxmsgfrag);
+	PRINTSTAT("nrxmsgbadfrag: %lud\n", ctlr->nrxmsgbadfrag);
+	USED(l);
+	n = readstr(offset, a, n, p);
+	free(p);
+	return n;
+}
+#undef PRINTSTR
+#undef PRINTSTAT
+
+static int
+parsekey(WKey* key, char* a) 
+{
+	int i, k, len, n;
+	char buf[WMaxKeyLen];
+
+	len = strlen(a);
+	if(len == WMinKeyLen || len == WMaxKeyLen){
+		memset(key->dat, 0, sizeof(key->dat));
+		memmove(key->dat, a, len);
+		key->len = len;
+
+		return 0;
+	}
+	else if(len == WMinKeyLen*2 || len == WMaxKeyLen*2){
+		k = 0;
+		for(i = 0; i < len; i++){
+			if(*a >= '0' && *a <= '9')
+				n = *a++ - '0';
+			else if(*a >= 'a' && *a <= 'f')
+				n = *a++ - 'a' + 10;
+			else if(*a >= 'A' && *a <= 'F')
+				n = *a++ - 'A' + 10;
+			else
+				return -1;
+	
+			if(i & 1){
+				buf[k] |= n;
+				k++;
+			}
+			else
+				buf[k] = n<<4;
+		}
+
+		memset(key->dat, 0, sizeof(key->dat));
+		memmove(key->dat, buf, k);
+		key->len = k;
+
+		return 0;
+	}
+
+	return -1;
+}
+
+int
+w_option(Ctlr* ctlr, char* buf, long n)
+{
+	char *p;
+	int i, r;
+	Cmdbuf *cb;
+
+	r = 0;
+
+	cb = parsecmd(buf, n);
+	if(cb->nf < 2)
+		r = -1;
+	else if(cistrcmp(cb->f[0], "essid") == 0){
+		if(cistrcmp(cb->f[1],"default") == 0)
+			p = "";
+		else
+			p = cb->f[1];
+		if(ctlr->ptype == WPTypeAdHoc){
+			memset(ctlr->netname, 0, sizeof(ctlr->netname));
+			strncpy(ctlr->netname, p, WNameLen);
+		}
+		else{
+			memset(ctlr->wantname, 0, sizeof(ctlr->wantname));
+			strncpy(ctlr->wantname, p, WNameLen);
+		}
+	}
+	else if(cistrcmp(cb->f[0], "station") == 0){
+		memset(ctlr->nodename, 0, sizeof(ctlr->nodename));
+		strncpy(ctlr->nodename, cb->f[1], WNameLen);
+	}
+	else if(cistrcmp(cb->f[0], "channel") == 0){
+		if((i = atoi(cb->f[1])) >= 1 && i <= 16)
+			ctlr->chan = i;
+		else
+			r = -1;
+	}
+	else if(cistrcmp(cb->f[0], "mode") == 0){
+		if(cistrcmp(cb->f[1], "managed") == 0)
+			ctlr->ptype = WPTypeManaged;
+		else if(cistrcmp(cb->f[1], "wds") == 0)
+			ctlr->ptype = WPTypeWDS;
+		else if(cistrcmp(cb->f[1], "adhoc") == 0)
+			ctlr->ptype = WPTypeAdHoc;
+		else if((i = atoi(cb->f[1])) >= 0 && i <= 3)
+			ctlr->ptype = i;
+		else
+			r = -1;
+	}
+	else if(cistrcmp(cb->f[0], "ibss") == 0){
+		if(cistrcmp(cb->f[1], "on") == 0)
+			ctlr->createibss = 1;
+		else
+			ctlr->createibss = 0;
+	}
+	else if(cistrcmp(cb->f[0], "crypt") == 0){
+		if(cistrcmp(cb->f[1], "off") == 0)
+			ctlr->crypt = 0;
+		else if(cistrcmp(cb->f[1], "on") == 0 && ctlr->hascrypt)
+			ctlr->crypt = 1;
+		else
+			r = -1;
+	}
+	else if(cistrcmp(cb->f[0], "clear") == 0){
+		if(cistrcmp(cb->f[1], "on") == 0)
+			ctlr->xclear = 0;
+		else if(cistrcmp(cb->f[1], "off") == 0 && ctlr->hascrypt)
+			ctlr->xclear = 1;
+		else
+			r = -1;
+	}
+	else if(cistrncmp(cb->f[0], "key", 3) == 0){
+		if((i = atoi(cb->f[0]+3)) >= 1 && i <= WNKeys){
+			ctlr->txkey = i-1;
+			if(parsekey(&ctlr->keys.keys[ctlr->txkey], cb->f[1]))
+				r = -1;
+		}
+		else
+			r = -1;
+	}
+	else if(cistrcmp(cb->f[0], "txkey") == 0){
+		if((i = atoi(cb->f[1])) >= 1 && i <= WNKeys)
+			ctlr->txkey = i-1;
+		else
+			r = -1;
+	}
+	else if(cistrcmp(cb->f[0], "pm") == 0){
+		if(cistrcmp(cb->f[1], "off") == 0)
+			ctlr->pmena = 0;
+		else if(cistrcmp(cb->f[1], "on") == 0){
+			ctlr->pmena = 1;
+			if(cb->nf == 3){
+				i = atoi(cb->f[2]);
+				// check range here? what are the units?
+				ctlr->pmwait = i;
+			}
+		}
+		else
+			r = -1;
+	}
+	else
+		r = -2;
+	free(cb);
+
+	return r;
+}
+
+long
+w_ctl(Ether* ether, void* buf, long n)
+{
+	Ctlr *ctlr;
+
+	if((ctlr = ether->ctlr) == nil)
+		error(Enonexist);
+	if((ctlr->state & Attached) == 0)
+		error(Eshutdown);
+
+	ilock(ctlr);
+	if(w_option(ctlr, buf, n)){
+		iunlock(ctlr);
+		error(Ebadctl);
+	}
+	if(ctlr->txbusy)
+		w_txdone(ctlr, WTxErrEv);
+	w_enable(ether);
+	w_txstart(ether);
+	iunlock(ctlr);
+
+	return n;
+}
+
+void
+w_transmit(Ether* ether)
+{
+	Ctlr* ctlr = ether->ctlr;
+
+	if(ctlr == 0)
+		return;
+
+	ilock(ctlr);
+	ctlr->ntxrq++;
+	w_txstart(ether);
+	iunlock(ctlr);
+}
+
+void
+w_promiscuous(void* arg, int on)
+{
+	Ether* ether = (Ether*)arg;
+	Ctlr* ctlr = ether->ctlr;
+
+	if(ctlr == nil)
+		error("card not found");
+	if((ctlr->state & Attached) == 0)
+		error("card not attached");
+	ilock(ctlr);
+	ltv_outs(ctlr, WTyp_Prom, (on?1:0));
+	iunlock(ctlr);
+}
+
+void
+w_interrupt(Ureg* ,void* arg)
+{
+	Ether* ether = (Ether*) arg;
+	Ctlr* ctlr = (Ctlr*) ether->ctlr;
+
+	if(ctlr == 0)
+		return;
+	ilock(ctlr);
+	ctlr->nints++;
+	w_intr(ether);
+	iunlock(ctlr);
+}
+
+int
+wavelanreset(Ether* ether, Ctlr *ctlr)
+{
+	Wltv ltv;
+
+	iprint("wavelanreset, iob 0x%ux\n", ctlr->iob);
+	w_intdis(ctlr);
+	if(w_cmd(ctlr,WCmdIni,0)){
+		iprint("#l%d: init failed\n", ether->ctlrno);
+		return -1;
+	}
+	w_intdis(ctlr);
+	ltv_outs(ctlr, WTyp_Tick, 8);
+
+	ctlr->chan = 0;
+	ctlr->ptype = WDfltPType;
+	ctlr->txkey = 0;
+	ctlr->createibss = 0;
+	ctlr->keys.len = sizeof(WKey)*WNKeys/2 + 1;
+	ctlr->keys.type = WTyp_Keys;
+	if(ctlr->hascrypt = ltv_ins(ctlr, WTyp_HasCrypt))
+		ctlr->crypt = 1;
+	*ctlr->netname = *ctlr->wantname = 0;
+	strcpy(ctlr->nodename, "Plan 9 STA");
+
+	ctlr->netname[WNameLen-1] = 0;
+	ctlr->wantname[WNameLen-1] = 0;
+	ctlr->nodename[WNameLen-1] =0;
+
+	ltv.type = WTyp_Mac;
+	ltv.len	= 4;
+	if(w_inltv(ctlr, &ltv)){
+		iprint("#l%d: unable to read mac addr\n",
+			ether->ctlrno);
+		return -1;
+	}
+	memmove(ether->ea, ltv.addr, Eaddrlen);
+
+	if(ctlr->chan == 0)
+		ctlr->chan = ltv_ins(ctlr, WTyp_Chan);
+	ctlr->apdensity = WDfltApDens;
+	ctlr->rtsthres = WDfltRtsThres;
+	ctlr->txrate = WDfltTxRate;
+	ctlr->maxlen = WMaxLen;
+	ctlr->pmena = 0;
+	ctlr->pmwait = 100;
+	ctlr->signal = 1;
+	ctlr->noise = 1;
+	ctlr->state |= Power;
+
+	// free old Ctlr struct if resetting after suspend
+	if(ether->ctlr && ether->ctlr != ctlr)
+		free(ether->ctlr);
+
+	// link to ether
+	ether->ctlr = ctlr;
+	ether->mbps = 10;
+	ether->attach = w_attach;
+	ether->detach = w_detach;
+	ether->interrupt = w_interrupt;
+	ether->transmit = w_transmit;
+	ether->ifstat = w_ifstat;
+	ether->ctl = w_ctl;
+	ether->power = w_power;
+	ether->promiscuous = w_promiscuous;
+	ether->multicast = w_multicast;
+	ether->scanbs = w_scanbs;
+	ether->arg = ether;
+
+	DEBUG("#l%d: irq %d port %lx type %s",
+		ether->ctlrno, ether->irq, ether->port,	ether->type);
+	DEBUG(" %2.2ux%2.2ux%2.2ux%2.2ux%2.2ux%2.2ux\n",
+		ether->ea[0], ether->ea[1], ether->ea[2],
+		ether->ea[3], ether->ea[4], ether->ea[5]);
+
+	return 0;
+}
+
+char* wavenames[] = {
+	"WaveLAN/IEEE",
+	"TrueMobile 1150",
+	"Instant Wireless ; Network PC CARD",
+	"Instant Wireless Network PC Card",
+	"Avaya Wireless PC Card",
+	"AirLancer MC-11",
+	"INTERSIL;HFA384x/IEEE;Version 01.02;",
+	nil,
+};
--- /dev/null
+++ b/os/pc/wavelan.h
@@ -1,0 +1,327 @@
+#define DEBUG	if(1){}else print
+
+#define SEEKEYS 0
+
+// Lucent's Length-Type-Value records to talk to the wavelan.
+// most operational parameters are read/set using this.
+enum
+{
+	WTyp_Stats	= 0xf100,
+	WTyp_Scan	= 0xf101,
+	WTyp_Link	= 0xf200,
+	WTyp_Ptype	= 0xfc00,
+	WTyp_Mac	= 0xfc01,
+	WTyp_WantName	= 0xfc02,
+	WTyp_Chan	= 0xfc03,
+	WTyp_NetName	= 0xfc04,
+	WTyp_ApDens	= 0xfc06,
+	WTyp_MaxLen	= 0xfc07,
+	WTyp_PM		= 0xfc09,
+	WTyp_PMWait	= 0xfc0c,
+	WTyp_NodeName	= 0xfc0e,
+	WTyp_Crypt	= 0xfc20,
+	WTyp_XClear	= 0xfc22,
+ 	WTyp_CreateIBSS	= 0xfc81,
+	WTyp_RtsThres	= 0xfc83,
+	WTyp_TxRate	= 0xfc84,
+		WTx1Mbps	= 0x0,
+		WTx2Mbps	= 0x1,
+		WTxAuto		= 0x3,
+	WTyp_Prom	= 0xfc85,
+	WTyp_Keys	= 0xfcb0,
+	WTyp_TxKey	= 0xfcb1,
+	WTyp_StationID	= 0xfd20,
+	WTyp_CurName	= 0xfd41,
+	WTyp_BaseID	= 0xfd42,	// ID of the currently connected-to base station
+	WTyp_CurTxRate	= 0xfd44,	// Current TX rate
+	WTyp_HasCrypt	= 0xfd4f,
+	WTyp_Tick	= 0xfce0,
+};
+
+// Controller
+enum
+{
+	WDfltIRQ	= 3,		// default irq
+	WDfltIOB	= 0x180,	// default IO base
+
+	WIOLen		= 0x40,		// Hermes IO length
+
+	WTmOut		= 65536,	// Cmd time out
+
+	WPTypeManaged	= 1,
+	WPTypeWDS	= 2,
+	WPTypeAdHoc	= 3,
+	WDfltPType	= WPTypeManaged,
+
+	WDfltApDens	= 1,
+	WDfltRtsThres	= 2347,		// == disabled
+	WDfltTxRate	= WTxAuto,	// 2Mbps
+
+	WMaxLen		= 2304,
+	WNameLen	= 32,
+
+	WNKeys		= 4,
+	WKeyLen		= 14,
+	WMinKeyLen	= 5,
+	WMaxKeyLen	= 13,
+
+	// Wavelan hermes registers
+	WR_Cmd		= 0x00,
+		WCmdIni		= 0x0000,
+		WCmdEna		= 0x0001,
+		WCmdDis		= 0x0002,
+		WCmdTx		= 0x000b,
+		WCmdMalloc	= 0x000a,
+		WCmdEnquire	= 0x0011,
+		WCmdMsk		= 0x003f,
+		WCmdAccRd	= 0x0021,
+		WCmdReclaim	= 0x0100,
+		WCmdAccWr	= 0x0121,
+		WCmdBusy	= 0x8000,
+	WR_Parm0	= 0x02,
+	WR_Parm1	= 0x04,
+	WR_Parm2	= 0x06,
+	WR_Sts		= 0x08,
+	WR_InfoId	= 0x10,
+	WR_Sel0		= 0x18,
+	WR_Sel1		= 0x1a,
+	WR_Off0		= 0x1c,
+	WR_Off1		= 0x1e,
+		WBusyOff	= 0x8000,
+		WErrOff		= 0x4000,
+		WResSts		= 0x7f00,
+	WR_RXId		= 0x20,
+	WR_Alloc	= 0x22,
+	WR_EvSts	= 0x30,
+	WR_IntEna	= 0x32,
+		WCmdEv		= 0x0010,
+		WRXEv		= 0x0001,
+		WTXEv		= 0x0002,
+		WTxErrEv	= 0x0004,
+		WAllocEv	= 0x0008,
+		WInfoEv		= 0x0080,
+		WIDropEv	= 0x2000,
+		WTickEv		= 0x8000,
+		WEvs		= WRXEv|WTXEv|WAllocEv|WInfoEv|WIDropEv,
+
+	WR_EvAck	= 0x34,
+	WR_Data0	= 0x36,
+	WR_Data1	= 0x38,
+
+	WR_PciCor	= 0x26,
+	WR_PciHcr	= 0x2E,
+
+	// Frame stuff
+
+	WF_Err		= 0x0003,
+	WF_1042		= 0x2000,
+	WF_Tunnel	= 0x4000,
+	WF_WMP		= 0x6000,
+
+	WF_Data		= 0x0008,
+
+	WSnapK1		= 0xaa,
+	WSnapK2		= 0x00,
+	WSnapCtlr	= 0x03,
+	WSnap0		= (WSnapK1|(WSnapK1<<8)),
+	WSnap1		= (WSnapK2|(WSnapCtlr<<8)),
+	WSnapHdrLen	= 6,
+
+	WF_802_11_Off	= 0x44,
+	WF_802_3_Off	= 0x2e,
+
+};
+
+typedef struct Ctlr	Ctlr;
+typedef struct Wltv	Wltv;
+typedef struct WFrame	WFrame;
+typedef struct Stats	Stats;
+typedef struct WStats	WStats;
+typedef struct WScan	WScan;
+typedef struct WKey	WKey;
+
+struct WStats
+{
+	ulong	ntxuframes;		// unicast frames
+	ulong	ntxmframes;		// multicast frames
+	ulong	ntxfrags;		// fragments
+	ulong	ntxubytes;		// unicast bytes
+	ulong	ntxmbytes;		// multicast bytes
+	ulong	ntxdeferred;		// deferred transmits
+	ulong	ntxsretries;		// single retries
+	ulong	ntxmultiretries;	// multiple retries
+	ulong	ntxretrylimit;
+	ulong	ntxdiscards;
+	ulong	nrxuframes;		// unicast frames
+	ulong	nrxmframes;		// multicast frames
+	ulong	nrxfrags;		// fragments
+	ulong	nrxubytes;		// unicast bytes
+	ulong	nrxmbytes;		// multicast bytes
+	ulong	nrxfcserr;
+	ulong	nrxdropnobuf;
+	ulong	nrxdropnosa;
+	ulong	nrxcantdecrypt;
+	ulong	nrxmsgfrag;
+	ulong	nrxmsgbadfrag;
+	ulong	end;
+};
+
+struct WScan
+{
+	ushort	chan;			/* dss channel */
+	ushort	noise;			/* average noise in the air */
+	ushort	signal;			/* signal strength */
+	uchar	bssid[Eaddrlen];	/* MAC address of the ap */
+	ushort	interval;		/* beacon transmit interval */
+	ushort	capinfo;		/* capability bits (0-ess, 1-ibss, 4-privacy [wep]) */
+	ushort	ssid_len;		/* ssid length */
+	char	ssid[WNameLen];		/* ssid (ap name) */
+};
+
+struct WFrame
+{
+	ushort	sts;
+	ushort	rsvd0;
+	ushort	rsvd1;
+	ushort	qinfo;
+	ushort	rsvd2;
+	ushort	rsvd3;
+	ushort	txctl;
+	ushort	framectl;
+	ushort	id;
+	uchar	addr1[Eaddrlen];
+	uchar	addr2[Eaddrlen];
+	uchar	addr3[Eaddrlen];
+	ushort	seqctl;
+	uchar	addr4[Eaddrlen];
+	ushort	dlen;
+	uchar	dstaddr[Eaddrlen];
+	uchar	srcaddr[Eaddrlen];
+	ushort	len;
+	ushort	dat[3];
+	ushort	type;
+};
+
+struct WKey
+{
+	ushort	len;
+	char	dat[WKeyLen];
+};
+
+struct Wltv
+{
+	ushort	len;
+	ushort	type;
+	union
+	{
+		struct {
+			ushort	val;
+			ushort	pad;
+		};
+		struct {
+			uchar	addr[8];
+		};
+		struct {
+			ushort	slen;
+			char	s[WNameLen];
+		};
+		struct {
+			char	name[WNameLen];
+		};
+		struct {
+			WKey	keys[WNKeys];
+		};
+	};
+};
+
+// What the driver thinks. Not what the card thinks.
+struct Stats
+{
+	ulong	nints;
+	ulong	ndoubleint;
+	ulong	nrx;
+	ulong	ntx;
+	ulong	ntxrq;
+	ulong	nrxerr;
+	ulong	ntxerr;
+	ulong	nalloc;			// allocation (reclaim) events
+	ulong	ninfo;
+	ulong	nidrop;
+	ulong	nwatchdogs;		// transmit time outs, actually
+	int	ticks;
+	int	tickintr;
+	int	signal;
+	int	noise;
+};
+
+enum {
+	Attached = 0x01,
+	Power = 0x02,
+};
+
+struct Ctlr
+{
+	Lock;
+
+	int	state;	// Attached | Power
+	int	slot;
+	int	iob;
+ 	int	createibss;
+	int	ptype;
+	int	apdensity;
+	int	rtsthres;
+	int	txbusy;
+	int	txrate;
+	int	txdid;
+	int	txmid;
+	int	txtmout;
+	int	maxlen;
+	int	chan;
+	int	pmena;
+	int	pmwait;
+
+	Proc	*timerproc;
+	int	scanticks;
+
+	char	netname[WNameLen];
+	char	wantname[WNameLen];
+	char	nodename[WNameLen];
+	WFrame	txf;
+	uchar	txbuf[1536];
+
+	int	hascrypt;		// card has encryption
+	int	crypt;			// encryption off/on
+	int	txkey;			// transmit key
+	Wltv	keys;			// default keys
+	int	xclear;			// exclude clear packets off/on
+
+	int	ctlrno;
+
+	ushort	*mmb;
+	/* for PCI-based devices */
+	Ctlr	*next;
+	int	active;
+	Pcidev	*pcidev;
+
+	Stats;
+	WStats;
+};
+
+extern char* wavenames[];
+
+void	csr_outs(Ctlr*, int, ushort);
+ushort	csr_ins(Ctlr*, int);
+void	w_intdis(Ctlr*);
+int	w_cmd(Ctlr *, ushort, ushort);
+void	ltv_outs(Ctlr*, int, ushort);
+int	ltv_ins(Ctlr*, int);
+int	w_option(Ctlr*, char*, long);
+int	w_inltv(Ctlr*, Wltv*);
+void	w_attach(Ether*);
+void	w_interrupt(Ureg*,void*);
+void	w_transmit(Ether*);
+long	w_ifstat(Ether*, void*, long, ulong);
+long	w_ctl(Ether*, void*, long);
+void	w_promiscuous(void*, int);
+void	w_multicast(void*, uchar*, int);
+int	wavelanreset(Ether*, Ctlr*);
--- /dev/null
+++ b/os/pc/x86break.c
@@ -1,0 +1,138 @@
+#include "u.h"
+#include "../port/lib.h"
+#include "mem.h"
+#include "dat.h"
+#include "fns.h"
+#include "io.h"
+#include "ureg.h"
+#include "../port/error.h"
+
+//
+// from trap.c
+//
+
+uchar BREAK = 0xcc;
+static ulong skipflags;
+extern int (*breakhandler)(Ureg *ur, Proc*);
+static Bkpt *skip;
+int breakmatch(BkptCond *cond, Ureg *ur, Proc *p);
+void breaknotify(Bkpt *b, Proc *p);
+void breakrestore(Bkpt *b);
+Bkpt* breakclear(int id);
+
+void
+skiphandler(Ureg *ur, void*)
+{
+	if (skip == 0)
+		panic("single step outside of skip");
+
+	breakrestore( skip );
+	skip = 0;
+	ur->flags = skipflags;
+	if (up != 0)
+		up->state = Running;
+}
+
+void
+machbreakinit(void)
+{
+	breakhandler = breakhit;
+	trapenable(VectorDBG, skiphandler, nil, "bkpt.skip");
+}
+
+Instr
+machinstr(ulong addr)
+{
+	if (addr < KTZERO)
+		error(Ebadarg);
+	return *(uchar*)addr;
+}
+
+void
+machbreakset(ulong addr)
+{
+	if (addr < KTZERO)
+		error(Ebadarg);
+	*(uchar*)addr = BREAK;
+}
+
+void
+machbreakclear(ulong addr, Instr i)
+{
+	if (addr < KTZERO)
+		error(Ebadarg);
+	*(uchar*)addr = i;
+}
+
+//
+// Called from the exception handler when a breakpoint instruction has been
+// hit.  This cannot not be called unless at least one breakpoint with this
+// address is in the list of breakpoints.  (All breakpoint notifications must
+// previously have been set via setbreak())
+//
+//	foreach breakpoint in list
+//		if breakpoint matches conditions
+//			notify the break handler
+//	if no breakpoints matched the conditions
+//		pick a random breakpoint set to this address
+//
+//		set a breakpoint at the next instruction to be executed,
+//		and pass the current breakpoint to the "skiphandler"
+//
+//		clear the current breakpoint
+//
+//		Tell the scheduler to stop scheduling, so the caller is
+//		guaranteed to execute the instruction, followed by the
+//		added breakpoint.
+//
+//
+
+extern Bkpt *breakpoints;
+
+
+int
+breakhit(Ureg *ur, Proc *p)
+{
+	Bkpt *b;
+	int nmatched;
+
+	ur->pc--;
+
+	nmatched = 0;
+	for(b = breakpoints; b != nil; b = b->next) {
+		if(breakmatch(b->conditions, ur, p)) {
+			breaknotify(b, p);
+			++nmatched;
+		}
+	}
+
+	if (nmatched)
+		return 1;
+
+	if (skip != nil)
+		panic("x86break: non-nil skip in breakhit\n");
+
+	for(b = breakpoints; b != (Bkpt*) nil;  b = b->next) {
+		if(b->addr == ur->pc) {
+			if(breakclear(b->id) == 0)
+				panic("breakhit: breakclear() failed");
+			
+			skip = b;
+			skipflags = ur->flags;
+			if (p != 0)
+				p->state = Stopped;			/* this should disable scheduling */
+
+			if (ur->flags & (1 << 9)) {		/* mask all interrupts */
+				ur->flags &= ~(1<<9);
+			}
+			ur->flags |= (1 << 8);
+		}
+	}
+	return 1;
+}
+
+int
+isvalid_va(void*)
+{
+	return 1;
+}
--- /dev/null
+++ b/os/pc/zoran.h
@@ -1,0 +1,907 @@
+static uchar
+zrmpeg1[] = {
+0x00, 0x08, 0x80, 0x00, 0x00, 0x08, 0x66, 0x05, 
+0x00, 0x08, 0x00, 0x80, 0x00, 0x08, 0x5a, 0x00, 
+0x00, 0x08, 0x58, 0x01, 0x00, 0x08, 0x6e, 0x00, 
+0x00, 0x08, 0x6c, 0x00, 0x00, 0x03, 0x81, 0x03, 
+0x00, 0x08, 0x9a, 0x00, 0x00, 0x08, 0xfc, 0x3f, 
+0x00, 0x0d, 0x02, 0x97, 0x00, 0x06, 0x01, 0xfa, 
+0x00, 0x0c, 0xa2, 0xd1, 0x00, 0x08, 0x6a, 0x00, 
+0x00, 0x06, 0x01, 0x00, 0x00, 0x0c, 0xa0, 0x6b, 
+0x00, 0x06, 0x01, 0xb3, 0x00, 0x0c, 0xa8, 0x09, 
+0x00, 0x0c, 0xd8, 0x12, 0x00, 0x08, 0x5c, 0x01, 
+0x00, 0x08, 0x02, 0xf0, 0x00, 0x08, 0xe4, 0x33, 
+0x00, 0x08, 0x0c, 0x0c, 0x00, 0x08, 0x04, 0x08, 
+0x00, 0x00, 0xe3, 0x80, 0x00, 0x04, 0x85, 0x90, 
+0x00, 0x08, 0x0c, 0x0c, 0x00, 0x00, 0xe3, 0x80, 
+0x00, 0x04, 0x85, 0xb1, 0x00, 0x08, 0x0c, 0x08, 
+0x00, 0x04, 0x8d, 0x08, 0x00, 0x06, 0x81, 0x08, 
+0x00, 0x0c, 0xa8, 0x22, 0x00, 0x08, 0x64, 0xff, 
+0x00, 0x01, 0x01, 0x07, 0x00, 0x08, 0x02, 0x00, 
+0x00, 0x06, 0x01, 0x02, 0x00, 0x0c, 0x90, 0x27, 
+0x00, 0x08, 0x02, 0x01, 0x00, 0x06, 0x6b, 0x00, 
+0x00, 0x08, 0xde, 0x01, 0x00, 0x0c, 0xa8, 0x09, 
+0x00, 0x08, 0x0c, 0x0f, 0x00, 0x08, 0x80, 0x00, 
+0x00, 0x08, 0x0c, 0x10, 0x00, 0x06, 0x8d, 0x01, 
+0x00, 0x0c, 0xa0, 0x3a, 0x00, 0x08, 0x0a, 0x00, 
+0x00, 0x08, 0x60, 0x00, 0x00, 0x08, 0x0c, 0x08, 
+0x00, 0x04, 0x9d, 0x08, 0x00, 0x08, 0x8b, 0x41, 
+0x00, 0x02, 0x50, 0x05, 0x00, 0x06, 0x0b, 0x40, 
+0x00, 0x0c, 0xa8, 0x31, 0x00, 0x06, 0x6b, 0x00, 
+0x00, 0x0c, 0xa8, 0x3f, 0x00, 0x0d, 0x00, 0x3b, 
+0x00, 0x08, 0x60, 0x01, 0x00, 0x08, 0x0a, 0x40, 
+0x00, 0x08, 0x0c, 0x01, 0x00, 0x06, 0x0d, 0x00, 
+0x00, 0x0c, 0x98, 0x49, 0x00, 0x08, 0x0a, 0x40, 
+0x00, 0x08, 0x0c, 0x08, 0x00, 0x04, 0x9d, 0x08, 
+0x00, 0x08, 0x8b, 0x41, 0x00, 0x02, 0x50, 0x05, 
+0x00, 0x06, 0x0b, 0x80, 0x00, 0x0c, 0xa8, 0x40, 
+0x00, 0x06, 0x6b, 0x00, 0x00, 0x0c, 0xa8, 0x16, 
+0x00, 0x0d, 0x00, 0x4e, 0x00, 0x08, 0x02, 0x10, 
+0x00, 0x08, 0x8a, 0x41, 0x00, 0x02, 0x50, 0x05, 
+0x00, 0x06, 0x0b, 0x80, 0x00, 0x0c, 0xa8, 0x4a, 
+0x00, 0x08, 0xfc, 0x3f, 0x00, 0x0d, 0x02, 0x97, 
+0x00, 0x08, 0xfc, 0x3f, 0x00, 0x0d, 0x02, 0x95, 
+0x00, 0x06, 0x01, 0xb8, 0x00, 0x0c, 0xa9, 0xff, 
+0x00, 0x06, 0x6f, 0x00, 0x00, 0x0c, 0xa0, 0x57, 
+0x00, 0x08, 0x58, 0x00, 0x00, 0x08, 0x6e, 0x01, 
+0x00, 0x08, 0x0c, 0x10, 0x00, 0x08, 0x0c, 0x09, 
+0x00, 0x08, 0x0c, 0x01, 0x00, 0x08, 0x80, 0x06, 
+0x00, 0x08, 0x0c, 0x01, 0x00, 0x04, 0x9d, 0x0f, 
+0x00, 0x03, 0x12, 0x6c, 0x00, 0x08, 0xd8, 0x01, 
+0x00, 0x08, 0x6c, 0x00, 0x00, 0x06, 0x01, 0x00, 
+0x00, 0x0c, 0x98, 0x64, 0x00, 0x08, 0x58, 0x00, 
+0x00, 0x08, 0xfc, 0x3f, 0x00, 0x0d, 0x02, 0x97, 
+0x00, 0x08, 0xfc, 0x3f, 0x00, 0x0d, 0x02, 0x95, 
+0x00, 0x0d, 0x01, 0xf7, 0x00, 0x06, 0x5d, 0x01, 
+0x00, 0x0c, 0xa0, 0x6c, 0x00, 0x0c, 0xd8, 0x6b, 
+0x00, 0x09, 0x20, 0xe5, 0x00, 0x09, 0x62, 0xe6, 
+0x00, 0x09, 0x5e, 0xe3, 0x00, 0x09, 0x60, 0xc2, 
+0x00, 0x08, 0x5c, 0x00, 0x00, 0x08, 0x80, 0x00, 
+0x00, 0x02, 0x00, 0x2d, 0x00, 0x0c, 0xa0, 0x75, 
+0x00, 0x08, 0xda, 0x00, 0x00, 0x0c, 0xd0, 0x76, 
+0x00, 0x08, 0x50, 0x00, 0x00, 0x08, 0x23, 0xff, 
+0x00, 0x08, 0x24, 0x00, 0x00, 0x08, 0x0c, 0x0a, 
+0x00, 0x08, 0xe6, 0x32, 0x00, 0x04, 0x8d, 0x06, 
+0x00, 0x08, 0xe8, 0x00, 0x00, 0x08, 0x0c, 0x03, 
+0x00, 0x04, 0x8d, 0x0d, 0x00, 0x08, 0xaa, 0x00, 
+0x00, 0x08, 0x0c, 0x10, 0x00, 0x06, 0x2b, 0x01, 
+0x00, 0x0c, 0xa8, 0x8c, 0x00, 0x06, 0x6d, 0x00, 
+0x00, 0x0c, 0xa8, 0x8a, 0x00, 0x08, 0x80, 0x2c, 
+0x00, 0x01, 0x01, 0x01, 0x00, 0x08, 0xd8, 0x00, 
+0x00, 0x08, 0x6c, 0x01, 0x00, 0x0d, 0x00, 0xab, 
+0x00, 0x08, 0x58, 0x00, 0x00, 0x0d, 0x00, 0xab, 
+0x00, 0x06, 0x2b, 0x02, 0x00, 0x0c, 0xa8, 0x97, 
+0x00, 0x06, 0x6d, 0x00, 0x00, 0x0c, 0xa0, 0x99, 
+0x00, 0x08, 0x58, 0x00, 0x00, 0x0d, 0x00, 0x9a, 
+0x00, 0x08, 0x02, 0x03, 0x00, 0x06, 0x5b, 0x01, 
+0x00, 0x0c, 0xa8, 0xb0, 0x00, 0x08, 0x02, 0x01, 
+0x00, 0x0d, 0x00, 0xb0, 0x00, 0x06, 0xd9, 0x01, 
+0x00, 0x0c, 0xa0, 0x9a, 0x00, 0x08, 0x58, 0x03, 
+0x00, 0x08, 0xfc, 0x3f, 0x00, 0x0d, 0x02, 0xab, 
+0x00, 0x08, 0xd4, 0x02, 0x00, 0x08, 0xae, 0x00, 
+0x00, 0x08, 0xac, 0x03, 0x00, 0x01, 0xc1, 0x9e, 
+0x00, 0x02, 0xc1, 0x9f, 0x00, 0x08, 0x80, 0x00, 
+0x00, 0x06, 0x2b, 0x03, 0x00, 0x0c, 0xa8, 0xab, 
+0x00, 0x08, 0xfc, 0x3f, 0x00, 0x0d, 0x02, 0xab, 
+0x00, 0x08, 0xd6, 0x02, 0x00, 0x08, 0xb2, 0x00, 
+0x00, 0x08, 0xb0, 0x03, 0x00, 0x01, 0xc1, 0xa0, 
+0x00, 0x02, 0xc1, 0xa1, 0x00, 0x08, 0xfc, 0x3f, 
+0x00, 0x0d, 0x02, 0x97, 0x00, 0x08, 0x82, 0x15, 
+0x00, 0x06, 0xd9, 0x02, 0x00, 0x0c, 0xa8, 0x92, 
+0x00, 0x09, 0x02, 0xe7, 0x00, 0x08, 0xfc, 0x3f, 
+0x00, 0x0d, 0x02, 0x95, 0x00, 0x08, 0x88, 0x0d, 
+0x00, 0x08, 0x90, 0x04, 0x00, 0x08, 0x92, 0x04, 
+0x00, 0x08, 0x94, 0x04, 0x00, 0x08, 0x45, 0xfe, 
+0x00, 0x08, 0x34, 0x00, 0x00, 0x08, 0x36, 0x00, 
+0x00, 0x08, 0x38, 0x00, 0x00, 0x08, 0x3a, 0x00, 
+0x00, 0x08, 0x0c, 0x05, 0x00, 0x04, 0x8d, 0x0b, 
+0x00, 0x09, 0x00, 0xc0, 0x00, 0x08, 0x46, 0x01, 
+0x00, 0x08, 0x0c, 0x01, 0x00, 0x08, 0x80, 0x28, 
+0x00, 0x08, 0x0c, 0x1c, 0x00, 0x08, 0x0e, 0x01, 
+0x00, 0x08, 0xce, 0x00, 0x00, 0x06, 0x0f, 0x22, 
+0x00, 0x0c, 0xa0, 0xc2, 0x00, 0x0c, 0x82, 0x7c, 
+0x00, 0x00, 0x80, 0xa2, 0x00, 0x06, 0x47, 0x00, 
+0x00, 0x08, 0xd0, 0x00, 0x00, 0x0c, 0xaa, 0x77, 
+0x00, 0x08, 0x4c, 0x01, 0x00, 0x0d, 0x02, 0x85, 
+0x00, 0x06, 0x5b, 0x00, 0x00, 0x0c, 0xa2, 0xbe, 
+0x00, 0x08, 0x52, 0x00, 0x00, 0x08, 0x4c, 0x00, 
+0x00, 0x08, 0xfc, 0x3f, 0x00, 0x0d, 0x02, 0xd5, 
+0x00, 0x09, 0x52, 0xe0, 0x00, 0x06, 0x2b, 0x01, 
+0x00, 0x0c, 0xa0, 0xf0, 0x00, 0x08, 0x0c, 0x1c, 
+0x00, 0x06, 0x2b, 0x03, 0x00, 0x0c, 0xa1, 0x01, 
+0x00, 0x0c, 0x91, 0xff, 0x00, 0x08, 0x0e, 0x02, 
+0x00, 0x08, 0x80, 0x00, 0x00, 0x06, 0x8f, 0x01, 
+0x00, 0x0c, 0xa8, 0xf8, 0x00, 0x08, 0xa6, 0x07, 
+0x00, 0x08, 0x52, 0x20, 0x00, 0x08, 0x80, 0x00, 
+0x00, 0x06, 0xa7, 0x08, 0x00, 0x0c, 0xa9, 0x0c, 
+0x00, 0x08, 0x34, 0x00, 0x00, 0x08, 0x36, 0x00, 
+0x00, 0x08, 0x00, 0x00, 0x00, 0x08, 0x02, 0x00, 
+0x00, 0x08, 0x8a, 0x2a, 0x00, 0x08, 0x48, 0x03, 
+0x00, 0x08, 0x46, 0x06, 0x00, 0x08, 0xfc, 0x3f, 
+0x00, 0x0d, 0x02, 0x23, 0x00, 0x06, 0x4d, 0x00, 
+0x00, 0x0c, 0xa9, 0x66, 0x00, 0x0d, 0x01, 0x0c, 
+0x00, 0x08, 0x0c, 0x01, 0x00, 0x06, 0x0d, 0x00, 
+0x00, 0x0c, 0x80, 0xf6, 0x00, 0x08, 0x0c, 0x01, 
+0x00, 0x08, 0x26, 0x11, 0x00, 0x0d, 0x01, 0x0e, 
+0x00, 0x08, 0x26, 0x01, 0x00, 0x0d, 0x01, 0x0e, 
+0x00, 0x06, 0xd9, 0x02, 0x00, 0x0c, 0xa1, 0x0c, 
+0x00, 0x08, 0xa6, 0x07, 0x00, 0x08, 0x52, 0x00, 
+0x00, 0x08, 0x46, 0x06, 0x00, 0x08, 0x48, 0x03, 
+0x00, 0x08, 0xfc, 0x3f, 0x00, 0x0d, 0x02, 0x23, 
+0x00, 0x0d, 0x01, 0x0c, 0x00, 0x08, 0x0e, 0x03, 
+0x00, 0x08, 0x00, 0x00, 0x00, 0x06, 0x8f, 0x01, 
+0x00, 0x0c, 0xa8, 0xf8, 0x00, 0x06, 0x8f, 0x04, 
+0x00, 0x0c, 0xa1, 0x08, 0x00, 0x03, 0x01, 0x04, 
+0x00, 0x06, 0x8f, 0x08, 0x00, 0x0c, 0xa1, 0x0b, 
+0x00, 0x03, 0x01, 0x20, 0x00, 0x08, 0xd2, 0x00, 
+0x00, 0x08, 0xa6, 0x07, 0x00, 0x08, 0x80, 0x00, 
+0x00, 0x06, 0xa7, 0x10, 0x00, 0x0c, 0xa1, 0x13, 
+0x00, 0x08, 0x0c, 0x05, 0x00, 0x04, 0x8d, 0x0b, 
+0x00, 0x09, 0x00, 0xc0, 0x00, 0x06, 0xa7, 0x08, 
+0x00, 0x0c, 0xa1, 0x2f, 0x00, 0x08, 0x88, 0x17, 
+0x00, 0x08, 0x8a, 0x1e, 0x00, 0x08, 0x84, 0x1f, 
+0x00, 0x08, 0x80, 0x16, 0x00, 0x08, 0x86, 0x1a, 
+0x00, 0x08, 0xc8, 0x02, 0x00, 0x08, 0xc6, 0x03, 
+0x00, 0x08, 0xfc, 0x3f, 0x00, 0x0d, 0x02, 0x00, 
+0x00, 0x08, 0x84, 0x1b, 0x00, 0x08, 0x80, 0x16, 
+0x00, 0x08, 0xb4, 0x01, 0x00, 0x08, 0xc6, 0x02, 
+0x00, 0x08, 0xfc, 0x3f, 0x00, 0x0d, 0x02, 0x00, 
+0x00, 0x08, 0xb6, 0x01, 0x00, 0x08, 0x80, 0x00, 
+0x00, 0x06, 0xa7, 0x08, 0x00, 0x0c, 0xa1, 0x2f, 
+0x00, 0x08, 0x82, 0x1b, 0x00, 0x08, 0x80, 0x1a, 
+0x00, 0x08, 0x8a, 0x2a, 0x00, 0x08, 0x46, 0x06, 
+0x00, 0x08, 0x48, 0x03, 0x00, 0x08, 0xfc, 0x3f, 
+0x00, 0x0d, 0x02, 0x23, 0x00, 0x06, 0xa7, 0x04, 
+0x00, 0x0c, 0xa1, 0x4e, 0x00, 0x06, 0x4d, 0x00, 
+0x00, 0x0c, 0xa9, 0x44, 0x00, 0x08, 0x80, 0x18, 
+0x00, 0x08, 0x88, 0x19, 0x00, 0x08, 0x8a, 0x20, 
+0x00, 0x08, 0x84, 0x21, 0x00, 0x08, 0x86, 0x1c, 
+0x00, 0x08, 0xc8, 0x02, 0x00, 0x08, 0xc6, 0x03, 
+0x00, 0x08, 0xfc, 0x3f, 0x00, 0x0d, 0x02, 0x00, 
+0x00, 0x08, 0xb8, 0x01, 0x00, 0x08, 0x80, 0x00, 
+0x00, 0x08, 0x84, 0x1d, 0x00, 0x08, 0x80, 0x18, 
+0x00, 0x08, 0xc6, 0x02, 0x00, 0x08, 0xfc, 0x3f, 
+0x00, 0x0d, 0x02, 0x00, 0x00, 0x08, 0xba, 0x01, 
+0x00, 0x0c, 0xc9, 0x44, 0x00, 0x08, 0x82, 0x1d, 
+0x00, 0x08, 0x80, 0x1c, 0x00, 0x08, 0x8a, 0x2b, 
+0x00, 0x08, 0x46, 0x06, 0x00, 0x08, 0x48, 0x00, 
+0x00, 0x08, 0xfc, 0x3f, 0x00, 0x0d, 0x02, 0x23, 
+0x00, 0x06, 0x4d, 0x00, 0x00, 0x0c, 0xa9, 0x66, 
+0x00, 0x06, 0x4d, 0x00, 0x00, 0x0c, 0xa9, 0x58, 
+0x00, 0x06, 0xa7, 0x02, 0x00, 0x08, 0x28, 0x00, 
+0x00, 0x0c, 0xa1, 0x58, 0x00, 0x08, 0x0c, 0x1c, 
+0x00, 0x08, 0x0e, 0x04, 0x00, 0x08, 0x80, 0x00, 
+0x00, 0x08, 0xa8, 0x07, 0x00, 0x08, 0x80, 0x00, 
+0x00, 0x06, 0xd9, 0x02, 0x00, 0x0c, 0xa1, 0x5c, 
+0x00, 0x08, 0x00, 0x00, 0x00, 0x0d, 0x01, 0x5e, 
+0x00, 0x08, 0x80, 0x13, 0x00, 0x01, 0x01, 0x01, 
+0x00, 0x09, 0x00, 0xc1, 0x00, 0x06, 0xa7, 0x01, 
+0x00, 0x0c, 0xa1, 0x66, 0x00, 0x08, 0x28, 0x3f, 
+0x00, 0x08, 0x34, 0x00, 0x00, 0x08, 0x36, 0x00, 
+0x00, 0x08, 0x38, 0x00, 0x00, 0x08, 0x3a, 0x00, 
+0x00, 0x08, 0x46, 0x06, 0x00, 0x0c, 0xd9, 0x69, 
+0x00, 0x0d, 0x01, 0xff, 0x00, 0x0c, 0xd1, 0x67, 
+0x00, 0x0c, 0xc9, 0x6b, 0x00, 0x08, 0x00, 0x01, 
+0x00, 0x01, 0x80, 0x23, 0x00, 0x06, 0xa7, 0x01, 
+0x00, 0x08, 0xc6, 0x00, 0x00, 0x0c, 0xa1, 0xa5, 
+0x00, 0x08, 0x0c, 0x1c, 0x00, 0x06, 0x47, 0x02, 
+0x00, 0x0c, 0x81, 0x75, 0x00, 0x08, 0x0e, 0x06, 
+0x00, 0x0d, 0x01, 0x76, 0x00, 0x08, 0x0e, 0x07, 
+0x00, 0x08, 0x08, 0x00, 0x00, 0x06, 0x0f, 0x00, 
+0x00, 0x0c, 0xa1, 0x85, 0x00, 0x08, 0xfa, 0x07, 
+0x00, 0x08, 0x8c, 0x3d, 0x00, 0x05, 0x4f, 0x10, 
+0x00, 0x04, 0xe9, 0x84, 0x00, 0x01, 0x90, 0x07, 
+0x00, 0x03, 0xa2, 0x81, 0x00, 0x06, 0x88, 0x02, 
+0x00, 0x0c, 0xa9, 0x85, 0x00, 0x08, 0x05, 0xff, 
+0x00, 0x03, 0xaf, 0x82, 0x00, 0x02, 0x10, 0x04, 
+0x00, 0x03, 0x15, 0x84, 0x00, 0x03, 0xc9, 0x03, 
+0x00, 0x06, 0x47, 0x04, 0x00, 0x0c, 0x91, 0x96, 
+0x00, 0x06, 0x47, 0x02, 0x00, 0x0c, 0x81, 0x96, 
+0x00, 0x00, 0xc8, 0x48, 0x00, 0x0d, 0x01, 0x9f, 
+0x00, 0x06, 0x47, 0x04, 0x00, 0x0c, 0x91, 0x8a, 
+0x00, 0x06, 0x47, 0x01, 0x00, 0x0c, 0xa1, 0x93, 
+0x00, 0x00, 0xc8, 0x4a, 0x00, 0x08, 0x94, 0x04, 
+0x00, 0x0d, 0x01, 0xa0, 0x00, 0x00, 0xc8, 0x49, 
+0x00, 0x08, 0x92, 0x04, 0x00, 0x0d, 0x01, 0xa0, 
+0x00, 0x08, 0x84, 0x28, 0x00, 0x05, 0x14, 0x22, 
+0x00, 0x06, 0x03, 0x01, 0x00, 0x0c, 0xa1, 0x8c, 
+0x00, 0x00, 0xc8, 0x4d, 0x00, 0x06, 0x47, 0x01, 
+0x00, 0x0c, 0xa1, 0x94, 0x00, 0x06, 0x47, 0x00, 
+0x00, 0x0c, 0xa1, 0x91, 0x00, 0x08, 0x90, 0x04, 
+0x00, 0x08, 0x0a, 0x80, 0x00, 0x06, 0xd9, 0x02, 
+0x00, 0x0c, 0xa9, 0xcc, 0x00, 0x08, 0x8b, 0x44, 
+0x00, 0x0d, 0x01, 0xbc, 0x00, 0x06, 0x47, 0x00, 
+0x00, 0x0c, 0xa1, 0xaf, 0x00, 0x06, 0xd3, 0x20, 
+0x00, 0x0c, 0xa1, 0xaf, 0x00, 0x08, 0x80, 0x1a, 
+0x00, 0x08, 0x82, 0x1b, 0x00, 0x08, 0x8a, 0x2a, 
+0x00, 0x08, 0x48, 0x03, 0x00, 0x08, 0xfc, 0x3f, 
+0x00, 0x0d, 0x02, 0x23, 0x00, 0x06, 0x4d, 0x00, 
+0x00, 0x0c, 0xa9, 0xd5, 0x00, 0x08, 0x80, 0x23, 
+0x00, 0x03, 0x80, 0x81, 0x00, 0x06, 0x80, 0x14, 
+0x00, 0x0c, 0xa1, 0xd5, 0x00, 0x08, 0x0c, 0x1c, 
+0x00, 0x08, 0x0e, 0x08, 0x00, 0x08, 0x0a, 0x80, 
+0x00, 0x00, 0xda, 0x3c, 0x00, 0x06, 0xd9, 0x02, 
+0x00, 0x0c, 0xa9, 0xd1, 0x00, 0x08, 0x8b, 0x47, 
+0x00, 0x08, 0x7a, 0x1c, 0x00, 0x08, 0x8c, 0x3d, 
+0x00, 0x08, 0x0e, 0x00, 0x00, 0x08, 0x80, 0x00, 
+0x00, 0x0c, 0xb1, 0xd5, 0x00, 0x00, 0xda, 0x3c, 
+0x00, 0x02, 0x50, 0x05, 0x00, 0x08, 0x8b, 0xc7, 
+0x00, 0x0c, 0xc1, 0xc0, 0x00, 0x08, 0x52, 0x00, 
+0x00, 0x08, 0x80, 0x00, 0x00, 0x09, 0x52, 0xe0, 
+0x00, 0x0c, 0xb1, 0xd5, 0x00, 0x08, 0x8c, 0x3d, 
+0x00, 0x0c, 0xc1, 0xc8, 0x00, 0x0d, 0x01, 0xc8, 
+0x00, 0x06, 0x47, 0x00, 0x00, 0x0c, 0xa1, 0xd1, 
+0x00, 0x08, 0x48, 0x03, 0x00, 0x08, 0xfc, 0x3f, 
+0x00, 0x0d, 0x02, 0x23, 0x00, 0x08, 0x7a, 0x1c, 
+0x00, 0x08, 0x8c, 0x3d, 0x00, 0x08, 0x0e, 0x00, 
+0x00, 0x0d, 0x01, 0xc8, 0x00, 0x06, 0x47, 0x00, 
+0x00, 0x0c, 0xa1, 0xdf, 0x00, 0x06, 0xd3, 0x04, 
+0x00, 0x0c, 0xa1, 0xdf, 0x00, 0x08, 0x80, 0x1c, 
+0x00, 0x08, 0x82, 0x1d, 0x00, 0x08, 0x8a, 0x2b, 
+0x00, 0x08, 0x48, 0x00, 0x00, 0x08, 0xfc, 0x3f, 
+0x00, 0x0d, 0x02, 0x23, 0x00, 0x08, 0x82, 0x29, 
+0x00, 0x01, 0x13, 0x24, 0x00, 0x06, 0x47, 0x00, 
+0x00, 0x08, 0xd2, 0x01, 0x00, 0x0c, 0xa9, 0x67, 
+0x00, 0x06, 0x4d, 0x00, 0x00, 0x0c, 0xaa, 0x8d, 
+0x00, 0x06, 0xa7, 0x01, 0x00, 0x0c, 0xa1, 0xea, 
+0x00, 0x08, 0x80, 0x28, 0x00, 0x08, 0xc4, 0x00, 
+0x00, 0x08, 0x0c, 0x1c, 0x00, 0x08, 0x0e, 0x01, 
+0x00, 0x08, 0x80, 0x28, 0x00, 0x06, 0x0f, 0x24, 
+0x00, 0x0c, 0xa1, 0xf1, 0x00, 0x08, 0xce, 0x00, 
+0x00, 0x0d, 0x00, 0xc5, 0x00, 0x08, 0xfc, 0x3f, 
+0x00, 0x0d, 0x02, 0x97, 0x00, 0x06, 0x01, 0x01, 
+0x00, 0x0c, 0x81, 0xf7, 0x00, 0x06, 0x01, 0xaf, 
+0x00, 0x0c, 0x88, 0xb3, 0x00, 0x06, 0x01, 0x00, 
+0x00, 0x0c, 0xa0, 0x69, 0x00, 0x06, 0x01, 0xb8, 
+0x00, 0x0c, 0xa0, 0x54, 0x00, 0x06, 0x01, 0xb3, 
+0x00, 0x0c, 0xa0, 0x12, 0x00, 0x06, 0x01, 0xb7, 
+0x00, 0x0c, 0xa2, 0xbe, 0x00, 0x0d, 0x00, 0x09, 
+0x00, 0x08, 0x0c, 0x1c, 0x00, 0x08, 0x0e, 0x05, 
+0x00, 0x08, 0x02, 0x00, 0x00, 0x08, 0x86, 0x07, 
+0x00, 0x03, 0x88, 0x81, 0x00, 0x06, 0x01, 0x01, 
+0x00, 0x0c, 0xa2, 0x0f, 0x00, 0x06, 0x07, 0x00, 
+0x00, 0x0c, 0xa2, 0x0f, 0x00, 0x08, 0xfa, 0x04, 
+0x00, 0x08, 0x8c, 0x3d, 0x00, 0x05, 0x29, 0x10, 
+0x00, 0x04, 0xe5, 0x82, 0x00, 0x05, 0x10, 0x81, 
+0x00, 0x05, 0x23, 0x81, 0x00, 0x08, 0x04, 0x00, 
+0x00, 0x03, 0xb9, 0x83, 0x00, 0x06, 0x07, 0x00, 
+0x00, 0x0c, 0xa2, 0x1b, 0x00, 0x03, 0xa1, 0x05, 
+0x00, 0x06, 0x07, 0x00, 0x00, 0x0c, 0x92, 0x19, 
+0x00, 0x00, 0x97, 0x83, 0x00, 0x00, 0xa7, 0x82, 
+0x00, 0x0d, 0x02, 0x1b, 0x00, 0x05, 0x17, 0x83, 
+0x00, 0x05, 0x27, 0x82, 0x00, 0x00, 0x96, 0x23, 
+0x00, 0x06, 0x02, 0x05, 0x00, 0x0c, 0x92, 0x21, 
+0x00, 0x06, 0x48, 0x01, 0x00, 0x0c, 0x92, 0x21, 
+0x00, 0x08, 0xfe, 0x3e, 0x00, 0x00, 0x94, 0x23, 
+0x00, 0x08, 0xfe, 0x3e, 0x00, 0x06, 0xd9, 0x02, 
+0x00, 0x0c, 0xa2, 0x27, 0x00, 0x08, 0x00, 0x00, 
+0x00, 0x08, 0x02, 0x00, 0x00, 0x06, 0x0b, 0x01, 
+0x00, 0x0c, 0xaa, 0x2b, 0x00, 0x03, 0x81, 0x01, 
+0x00, 0x03, 0x93, 0x01, 0x00, 0x06, 0x47, 0x02, 
+0x00, 0x0c, 0x92, 0x41, 0x00, 0x06, 0x01, 0x00, 
+0x00, 0x0c, 0x9a, 0x30, 0x00, 0x00, 0x81, 0x01, 
+0x00, 0x04, 0x21, 0x01, 0x00, 0x04, 0x05, 0x01, 
+0x00, 0x03, 0xc1, 0x01, 0x00, 0x05, 0x45, 0x82, 
+0x00, 0x06, 0x03, 0x00, 0x00, 0x0c, 0x9a, 0x37, 
+0x00, 0x00, 0x93, 0x01, 0x00, 0x04, 0x33, 0x01, 
+0x00, 0x04, 0x17, 0x01, 0x00, 0x03, 0xc3, 0x01, 
+0x00, 0x05, 0x47, 0x83, 0x00, 0x08, 0x0a, 0x03, 
+0x00, 0x03, 0xca, 0x11, 0x00, 0x00, 0xc1, 0x80, 
+0x00, 0x03, 0xca, 0x12, 0x00, 0x00, 0xc3, 0x81, 
+0x00, 0x0d, 0x02, 0x4a, 0x00, 0x01, 0x21, 0x01, 
+0x00, 0x04, 0x01, 0x01, 0x00, 0x01, 0x33, 0x01, 
+0x00, 0x04, 0x13, 0x01, 0x00, 0x08, 0x0a, 0x04, 
+0x00, 0x03, 0xca, 0x11, 0x00, 0x00, 0xc1, 0x80, 
+0x00, 0x03, 0xca, 0x12, 0x00, 0x00, 0xc3, 0x81, 
+0x00, 0x08, 0x88, 0x29, 0x00, 0x03, 0xa4, 0x64, 
+0x00, 0x03, 0x45, 0x84, 0x00, 0x02, 0x50, 0x24, 
+0x00, 0x03, 0xbb, 0x83, 0x00, 0x03, 0x47, 0x84, 
+0x00, 0x01, 0x28, 0xa4, 0x00, 0x06, 0x05, 0x24, 
+0x00, 0x0c, 0xaa, 0x58, 0x00, 0x06, 0x49, 0x00, 
+0x00, 0x0c, 0xaa, 0x58, 0x00, 0x0c, 0xda, 0x57, 
+0x00, 0x0d, 0x01, 0xff, 0x00, 0x0c, 0xca, 0x55, 
+0x00, 0x06, 0xd9, 0x02, 0x00, 0x0c, 0xa2, 0x5f, 
+0x00, 0x06, 0x6d, 0x00, 0x00, 0x0c, 0xa2, 0x5e, 
+0x00, 0x08, 0x08, 0x04, 0x00, 0x0d, 0x02, 0x5f, 
+0x00, 0x08, 0x08, 0x20, 0x00, 0x08, 0xd2, 0x04, 
+0x00, 0x09, 0x08, 0xe0, 0x00, 0x01, 0xc0, 0x23, 
+0x00, 0x03, 0xc9, 0x01, 0x00, 0x08, 0x0a, 0x26, 
+0x00, 0x03, 0xdb, 0x04, 0x00, 0x03, 0x5b, 0x08, 
+0x00, 0x00, 0xd9, 0x84, 0x00, 0x08, 0xfe, 0x04, 
+0x00, 0x0d, 0x02, 0x72, 0x00, 0x08, 0x80, 0x00, 
+0x00, 0x0d, 0x02, 0x72, 0x00, 0x08, 0x80, 0x00, 
+0x00, 0x00, 0x81, 0x08, 0x00, 0x0d, 0x02, 0x75, 
+0x00, 0x00, 0x93, 0x08, 0x00, 0x0d, 0x02, 0x72, 
+0x00, 0x00, 0x81, 0x08, 0x00, 0x08, 0x80, 0x00, 
+0x00, 0x09, 0x00, 0xe1, 0x00, 0x09, 0x02, 0xe2, 
+0x00, 0x08, 0xfe, 0x3e, 0x00, 0x00, 0x93, 0x08, 
+0x00, 0x0d, 0x02, 0x72, 0x00, 0x08, 0x0c, 0x1c, 
+0x00, 0x08, 0x0e, 0x01, 0x00, 0x08, 0x80, 0x00, 
+0x00, 0x06, 0x0f, 0x23, 0x00, 0x0c, 0xa0, 0xc8, 
+0x00, 0x08, 0x82, 0x28, 0x00, 0x08, 0xce, 0x01, 
+0x00, 0x00, 0xf3, 0x80, 0x00, 0x08, 0xd0, 0x00, 
+0x00, 0x06, 0x0f, 0x01, 0x00, 0x0c, 0xa0, 0xce, 
+0x00, 0x06, 0x47, 0x00, 0x00, 0x0c, 0xa8, 0xce, 
+0x00, 0x08, 0x4c, 0x02, 0x00, 0x08, 0x00, 0x00, 
+0x00, 0x09, 0x00, 0xc1, 0x00, 0x08, 0xfc, 0x3f, 
+0x00, 0x0d, 0x02, 0xd5, 0x00, 0x06, 0x2b, 0x02, 
+0x00, 0x0c, 0xa9, 0x26, 0x00, 0x08, 0x26, 0x00, 
+0x00, 0x0d, 0x00, 0xe0, 0x00, 0x02, 0x00, 0x27, 
+0x00, 0x02, 0x10, 0x00, 0x00, 0x06, 0x50, 0x01, 
+0x00, 0x08, 0xce, 0x00, 0x00, 0x0c, 0x92, 0x85, 
+0x00, 0x06, 0x4d, 0x01, 0x00, 0x0c, 0xa2, 0x77, 
+0x00, 0x0d, 0x00, 0xce, 0x00, 0x06, 0x01, 0xb2, 
+0x00, 0x0c, 0xaa, 0xa7, 0x00, 0x08, 0x0d, 0x08, 
+0x00, 0x06, 0x0d, 0x00, 0x00, 0x0c, 0xaa, 0x97, 
+0x00, 0x08, 0x0c, 0x08, 0x00, 0x04, 0x8d, 0x08, 
+0x00, 0x0c, 0xaa, 0x97, 0x00, 0x08, 0x0c, 0x10, 
+0x00, 0x06, 0x0d, 0x00, 0x00, 0x0c, 0xa2, 0x9d, 
+0x00, 0x06, 0x0d, 0x01, 0x00, 0x0c, 0xa2, 0xa8, 
+0x00, 0x04, 0x8d, 0x08, 0x00, 0x06, 0x01, 0x01, 
+0x00, 0x0c, 0xaa, 0x97, 0x00, 0x03, 0x8d, 0x08, 
+0x00, 0x04, 0x81, 0x08, 0x00, 0x08, 0xfe, 0x3e, 
+0x00, 0x08, 0x0c, 0x08, 0x00, 0x04, 0x8d, 0x08, 
+0x00, 0x08, 0xfe, 0x3e, 0x00, 0x08, 0x0c, 0x01, 
+0x00, 0x04, 0xad, 0x0f, 0x00, 0x08, 0x0c, 0x03, 
+0x00, 0x04, 0x8d, 0x0d, 0x00, 0x06, 0x67, 0xff, 
+0x00, 0x0c, 0xa2, 0xba, 0x00, 0x08, 0x82, 0x33, 
+0x00, 0x05, 0x92, 0x34, 0x00, 0x01, 0x13, 0x1f, 
+0x00, 0x02, 0x10, 0x01, 0x00, 0x00, 0x83, 0x80, 
+0x00, 0x06, 0x01, 0x07, 0x00, 0x0c, 0x8a, 0xba, 
+0x00, 0x05, 0x00, 0x87, 0x00, 0x0d, 0x02, 0xb6, 
+0x00, 0x01, 0x80, 0x00, 0x00, 0x03, 0xb0, 0x81, 
+0x00, 0x03, 0xc7, 0x04, 0x00, 0x08, 0xfe, 0x3e, 
+0x00, 0x06, 0x5b, 0x01, 0x00, 0x0c, 0xa2, 0xc2, 
+0x00, 0x09, 0x00, 0xe8, 0x00, 0x0d, 0x00, 0x01, 
+0x00, 0x0c, 0xda, 0xc2, 0x00, 0x08, 0x5a, 0x00, 
+0x00, 0x08, 0x23, 0xff, 0x00, 0x08, 0x24, 0x00, 
+0x00, 0x08, 0x4e, 0x00, 0x00, 0x08, 0x2a, 0x02, 
+0x00, 0x08, 0x00, 0x00, 0x00, 0x09, 0x2a, 0xe7, 
+0x00, 0x08, 0x82, 0x10, 0x00, 0x00, 0x80, 0x71, 
+0x00, 0x01, 0x90, 0x01, 0x00, 0x0c, 0xaa, 0xcb, 
+0x00, 0x02, 0x01, 0xa8, 0x00, 0x08, 0x4c, 0x02, 
+0x00, 0x0d, 0x02, 0x85, 0x00, 0x08, 0xe4, 0x33, 
+0x00, 0x08, 0x60, 0x00, 0x00, 0x08, 0x6a, 0x01, 
+0x00, 0x0d, 0x00, 0x2f, 0x00, 0x02, 0x00, 0x11, 
+0x00, 0x06, 0x20, 0x00, 0x00, 0x08, 0xa2, 0x00, 
+0x00, 0x0c, 0x92, 0xdd, 0x00, 0x02, 0x00, 0x12, 
+0x00, 0x08, 0x22, 0x00, 0x00, 0x08, 0xa4, 0x00, 
+0x00, 0x08, 0x80, 0x00, 0x00, 0x08, 0xfe, 0x3e, 
+0x00, 0x08, 0x80, 0x00, 0x00, 0x08, 0x80, 0x00, 
+0x00, 0x08, 0x80, 0x00, 0x00, 0x08, 0x80, 0x00, 
+0x00, 0x08, 0x80, 0x00, 0x00, 0x08, 0x80, 0x00, 
+0x00, 0x08, 0x80, 0x00, 0x00, 0x08, 0x80, 0x00, 
+0x00, 0x08, 0x80, 0x00, 0x00, 0x08, 0x80, 0x00, 
+0x00, 0x08, 0x80, 0x00, 0x00, 0x08, 0x80, 0x00, 
+0x00, 0x08, 0x80, 0x00, 0x00, 0x08, 0x80, 0x00, 
+0x00, 0x08, 0x80, 0x00, 0x00, 0x08, 0x80, 0x00, 
+0x00, 0x08, 0x80, 0x00, 0x00, 0x08, 0x80, 0x00, 
+0x00, 0x08, 0x80, 0x00, 0x00, 0x08, 0x80, 0x00, 
+0x00, 0x08, 0x80, 0x00, 0x00, 0x08, 0x80, 0x00, 
+0x00, 0x08, 0x80, 0x00, 0x00, 0x08, 0x80, 0x00, 
+0x00, 0x08, 0x80, 0x00, 0x00, 0x08, 0x80, 0x00, 
+0x00, 0x08, 0x80, 0x00, 0x00, 0x08, 0x80, 0x00, 
+0x00, 0x08, 0x80, 0x00, 0x00, 0x08, 0x80, 0x00, 
+0x00, 0x08, 0x80, 0x00, 0x00, 0x08, 0x80, 0x00, 
+0x0a, 0x0d, 
+};
+static uchar
+zrmpeg2[] = {
+0x00, 0x02, 0xef, 0x00, 0x00, 0x03, 0xc1, 0x81, 
+0x00, 0x02, 0xee, 0x00, 0x00, 0x03, 0x2e, 0x01, 
+0x00, 0x02, 0xee, 0x00, 0x00, 0x03, 0x47, 0x17, 
+0x00, 0x03, 0x2e, 0x02, 0x00, 0x02, 0xee, 0x00, 
+0x00, 0x03, 0x74, 0x17, 0x00, 0x02, 0x9c, 0x02, 
+0x00, 0x03, 0x88, 0x0c, 0x00, 0x03, 0x46, 0x81, 
+0x00, 0x03, 0x50, 0x07, 0x00, 0x02, 0x80, 0x80, 
+0x00, 0x03, 0x88, 0x13, 0x00, 0x03, 0x43, 0x81, 
+0x00, 0x02, 0x9c, 0x01, 0x00, 0x03, 0x88, 0x14, 
+0x00, 0x03, 0x48, 0x80, 0x00, 0x03, 0x40, 0x80, 
+0x00, 0x02, 0x9c, 0x80, 0x00, 0x03, 0x88, 0x17, 
+0x00, 0x03, 0x44, 0x81, 0x00, 0x02, 0x9c, 0x04, 
+0x00, 0x03, 0x88, 0x1a, 0x00, 0x03, 0x45, 0x81, 
+0x00, 0x02, 0xee, 0x00, 0x00, 0x03, 0x50, 0x17, 
+0x00, 0x00, 0x5c, 0x8f, 0x00, 0x02, 0x84, 0x01, 
+0x00, 0x03, 0x88, 0x20, 0x00, 0x03, 0x49, 0x81, 
+0x00, 0x02, 0xee, 0x00, 0x00, 0x00, 0x5d, 0x0f, 
+0x00, 0x00, 0xc9, 0x04, 0x00, 0x03, 0x4d, 0x12, 
+0x00, 0x00, 0xa4, 0x2d, 0x00, 0x02, 0xe8, 0x00, 
+0x00, 0x02, 0x80, 0x01, 0x00, 0x03, 0x8a, 0x29, 
+0x00, 0x02, 0xe8, 0x01, 0x00, 0x03, 0x4d, 0x14, 
+0x00, 0x00, 0x41, 0x10, 0x00, 0x01, 0x49, 0x02, 
+0x00, 0x00, 0xab, 0x6d, 0x00, 0x00, 0x41, 0x20, 
+0x00, 0x01, 0x49, 0x02, 0x00, 0x00, 0xaa, 0x0d, 
+0x00, 0x02, 0x9c, 0x01, 0x00, 0x03, 0x8a, 0x33, 
+0x00, 0x00, 0x92, 0x10, 0x00, 0x03, 0x4d, 0x14, 
+0x00, 0x00, 0x5d, 0x10, 0x00, 0x00, 0xc9, 0x02, 
+0x00, 0x00, 0xab, 0x6d, 0x00, 0x00, 0x5d, 0x20, 
+0x00, 0x00, 0xc9, 0x02, 0x00, 0x00, 0xaa, 0x0d, 
+0x00, 0x02, 0xee, 0x00, 0x00, 0x03, 0x28, 0x5f, 
+0x00, 0x02, 0xef, 0x00, 0x00, 0x03, 0x42, 0x17, 
+0x00, 0x03, 0x2e, 0x20, 0x00, 0x01, 0xdc, 0x01, 
+0x00, 0x03, 0xa6, 0x43, 0x00, 0x03, 0x4d, 0x17, 
+0x00, 0x00, 0x20, 0x0d, 0x00, 0x03, 0x20, 0x17, 
+0x00, 0x02, 0xef, 0x00, 0x00, 0x03, 0x2e, 0x21, 
+0x00, 0x00, 0x3c, 0x02, 0x00, 0x03, 0x20, 0x24, 
+0x00, 0x02, 0xef, 0x00, 0x00, 0x03, 0x75, 0x17, 
+0x00, 0x03, 0x2e, 0x68, 0x00, 0x02, 0xef, 0x00, 
+0x00, 0x03, 0x43, 0x17, 0x00, 0x02, 0xef, 0x00, 
+0x00, 0x03, 0x44, 0x17, 0x00, 0x02, 0xef, 0x00, 
+0x00, 0x03, 0x45, 0x17, 0x00, 0x00, 0x1c, 0x01, 
+0x00, 0x03, 0x46, 0x10, 0x00, 0x00, 0x20, 0x02, 
+0x00, 0x03, 0x47, 0x10, 0x00, 0x00, 0x22, 0x23, 
+0x00, 0x00, 0x20, 0x02, 0x00, 0x03, 0x49, 0x10, 
+0x00, 0x00, 0x23, 0xa3, 0x00, 0x03, 0x50, 0x35, 
+0x00, 0x00, 0x00, 0x01, 0x00, 0x01, 0xe0, 0x04, 
+0x00, 0x03, 0xa6, 0x5e, 0x00, 0x01, 0xe0, 0x02, 
+0x00, 0x03, 0x4f, 0x10, 0x00, 0x00, 0x20, 0x02, 
+0x00, 0x03, 0x58, 0x10, 0x00, 0x03, 0x50, 0x35, 
+0x00, 0x01, 0x40, 0x01, 0x00, 0x00, 0x00, 0x01, 
+0x00, 0x00, 0x20, 0x83, 0x00, 0x03, 0x59, 0x11, 
+0x00, 0x01, 0xe0, 0x84, 0x00, 0x03, 0x5a, 0x11, 
+0x00, 0x02, 0xe2, 0x01, 0x00, 0x00, 0x26, 0x83, 
+0x00, 0x00, 0x36, 0xa5, 0x00, 0x03, 0x14, 0x69, 
+0x00, 0x03, 0x10, 0x6a, 0x00, 0x03, 0xa6, 0x6f, 
+0x00, 0x03, 0x1c, 0x6a, 0x00, 0x03, 0x1e, 0x6b, 
+0x00, 0x03, 0xa6, 0x72, 0x00, 0x03, 0x30, 0x6b, 
+0x00, 0x03, 0x2a, 0x6c, 0x00, 0x03, 0xa8, 0x77, 
+0x00, 0x03, 0x0e, 0x6c, 0x00, 0x03, 0xa6, 0x77, 
+0x00, 0x03, 0x12, 0x6c, 0x00, 0x03, 0x32, 0x6d, 
+0x00, 0x03, 0x34, 0x6e, 0x00, 0x03, 0xa8, 0x7c, 
+0x00, 0x03, 0x0c, 0x6d, 0x00, 0x03, 0x2a, 0x6e, 
+0x00, 0x02, 0xef, 0x00, 0x00, 0x03, 0x42, 0x17, 
+0x00, 0x03, 0x2e, 0x22, 0x00, 0x00, 0x1c, 0x01, 
+0x00, 0x01, 0x40, 0x01, 0x00, 0x00, 0xc0, 0x01, 
+0x00, 0x03, 0x20, 0x18, 0x00, 0x02, 0xef, 0x00, 
+0x00, 0x03, 0x73, 0x17, 0x00, 0x03, 0x2e, 0x23, 
+0x00, 0x02, 0xef, 0x00, 0x00, 0x03, 0x76, 0x17, 
+0x00, 0x03, 0x2e, 0x60, 0x00, 0x01, 0x5f, 0x01, 
+0x00, 0x02, 0x9c, 0x01, 0x00, 0x03, 0x88, 0x8d, 
+0x00, 0x03, 0x41, 0x81, 0x00, 0x02, 0xef, 0x00, 
+0x00, 0x03, 0x43, 0x17, 0x00, 0x03, 0x2e, 0x61, 
+0x00, 0x02, 0x9c, 0x01, 0x00, 0x03, 0x88, 0x94, 
+0x00, 0x03, 0x42, 0x81, 0x00, 0x03, 0x47, 0x80, 
+0x00, 0x02, 0xef, 0x00, 0x00, 0x03, 0x44, 0x17, 
+0x00, 0x02, 0xef, 0x00, 0x00, 0x03, 0x45, 0x17, 
+0x00, 0x03, 0x4d, 0x33, 0x00, 0x01, 0xff, 0x2d, 
+0x00, 0x03, 0x73, 0x0c, 0x00, 0x03, 0x5b, 0x1f, 
+0x00, 0x03, 0x8f, 0x94, 0x00, 0x03, 0x4d, 0x36, 
+0x00, 0x03, 0x50, 0x05, 0x00, 0x00, 0x20, 0x02, 
+0x00, 0x03, 0x51, 0x03, 0x00, 0x00, 0x24, 0x84, 
+0x00, 0x03, 0xa6, 0xa7, 0x00, 0x00, 0xc0, 0x01, 
+0x00, 0x01, 0xa3, 0x6d, 0x00, 0x01, 0xa7, 0x6d, 
+0x00, 0x03, 0x8e, 0xab, 0x00, 0x01, 0xa0, 0x0d, 
+0x00, 0x00, 0xc0, 0x01, 0x00, 0x03, 0x4d, 0x11, 
+0x00, 0x01, 0xe3, 0x6d, 0x00, 0x03, 0x72, 0x0d, 
+0x00, 0x00, 0x1c, 0x81, 0x00, 0x03, 0x41, 0x11, 
+0x00, 0x00, 0x3d, 0xa2, 0x00, 0x02, 0xee, 0x00, 
+0x00, 0x00, 0x1f, 0x81, 0x00, 0x03, 0x2e, 0x6f, 
+0x00, 0x02, 0xee, 0x00, 0x00, 0x03, 0x4c, 0x17, 
+0x00, 0x03, 0x51, 0x03, 0x00, 0x01, 0x45, 0x01, 
+0x00, 0x00, 0x28, 0x86, 0x00, 0x03, 0x47, 0x11, 
+0x00, 0x00, 0x06, 0x01, 0x00, 0x03, 0x51, 0x06, 
+0x00, 0x00, 0xc4, 0x81, 0x00, 0x03, 0x48, 0x11, 
+0x00, 0x00, 0x26, 0x63, 0x00, 0x00, 0x08, 0x81, 
+0x00, 0x03, 0x4e, 0x11, 0x00, 0x00, 0x28, 0x05, 
+0x00, 0x03, 0x4f, 0x10, 0x00, 0x00, 0x00, 0x01, 
+0x00, 0x03, 0x71, 0x10, 0x00, 0x03, 0x50, 0x05, 
+0x00, 0x00, 0xc1, 0x81, 0x00, 0x03, 0x4b, 0x13, 
+0x00, 0x00, 0x2c, 0x03, 0x00, 0x03, 0x58, 0x10, 
+0x00, 0x03, 0x50, 0x36, 0x00, 0x01, 0x41, 0x81, 
+0x00, 0x03, 0x4d, 0x13, 0x00, 0x03, 0x52, 0x04, 
+0x00, 0x01, 0x48, 0x81, 0x00, 0x01, 0xa5, 0x0d, 
+0x00, 0x01, 0xca, 0x81, 0x00, 0x03, 0x53, 0x32, 
+0x00, 0x00, 0x2d, 0x08, 0x00, 0x00, 0x29, 0x03, 
+0x00, 0x01, 0xc9, 0x01, 0x00, 0x03, 0x56, 0x12, 
+0x00, 0x03, 0x52, 0x31, 0x00, 0x00, 0x2b, 0x62, 
+0x00, 0x01, 0x4d, 0x81, 0x00, 0x00, 0x2d, 0x8d, 
+0x00, 0x03, 0x5a, 0x13, 0x00, 0x01, 0xcd, 0x81, 
+0x00, 0x03, 0x59, 0x13, 0x00, 0x01, 0xc0, 0x01, 
+0x00, 0x03, 0x4d, 0x11, 0x00, 0x01, 0xe2, 0xad, 
+0x00, 0x03, 0x18, 0x62, 0x00, 0x03, 0x1c, 0x63, 
+0x00, 0x03, 0x1e, 0x64, 0x00, 0x03, 0x0e, 0x65, 
+0x00, 0x03, 0x32, 0x66, 0x00, 0x03, 0xa6, 0xed, 
+0x00, 0x03, 0xa8, 0xf2, 0x00, 0x03, 0xa3, 0x03, 
+0x00, 0x03, 0xab, 0x09, 0x00, 0x03, 0x0a, 0x62, 
+0x00, 0x03, 0x50, 0x31, 0x00, 0x03, 0x20, 0x64, 
+0x00, 0x03, 0x28, 0x65, 0x00, 0x03, 0x34, 0x66, 
+0x00, 0x03, 0x8f, 0x0c, 0x00, 0x03, 0xa9, 0x0c, 
+0x00, 0x03, 0xaa, 0xfb, 0x00, 0x03, 0xa2, 0xfe, 
+0x00, 0x03, 0x02, 0x62, 0x00, 0x03, 0x8f, 0x0c, 
+0x00, 0x03, 0xa2, 0xf6, 0x00, 0x03, 0xa5, 0x0c, 
+0x00, 0x03, 0x2a, 0x66, 0x00, 0x03, 0x8f, 0x0c, 
+0x00, 0x03, 0x30, 0x63, 0x00, 0x03, 0x12, 0x64, 
+0x00, 0x03, 0x2c, 0x65, 0x00, 0x03, 0x2a, 0x66, 
+0x00, 0x03, 0x8f, 0x0c, 0x00, 0x03, 0x02, 0x62, 
+0x00, 0x03, 0x0c, 0x65, 0x00, 0x03, 0x8f, 0x0c, 
+0x00, 0x03, 0x0a, 0x62, 0x00, 0x03, 0x30, 0x64, 
+0x00, 0x03, 0x12, 0x65, 0x00, 0x03, 0x2c, 0x66, 
+0x00, 0x03, 0x8f, 0x0c, 0x00, 0x03, 0x0a, 0x62, 
+0x00, 0x03, 0x30, 0x63, 0x00, 0x03, 0x12, 0x64, 
+0x00, 0x03, 0x2c, 0x65, 0x00, 0x03, 0x16, 0x66, 
+0x00, 0x03, 0x8f, 0x0c, 0x00, 0x03, 0x02, 0x62, 
+0x00, 0x03, 0x0c, 0x65, 0x00, 0x03, 0x34, 0x66, 
+0x00, 0x03, 0x14, 0x67, 0x00, 0x03, 0xa9, 0x0f, 
+0x00, 0x03, 0x10, 0x67, 0x00, 0x03, 0x4c, 0x1f, 
+0x00, 0x03, 0x8f, 0x86, 0x00, 0x02, 0xda, 0x44, 
+0x00, 0x02, 0xe2, 0x01, 0x00, 0x02, 0xe0, 0x04, 
+0x00, 0x02, 0xee, 0x00, 0x00, 0x03, 0x4d, 0x57, 
+0x00, 0x00, 0x27, 0x6d, 0x00, 0x01, 0xc0, 0x01, 
+0x00, 0x03, 0x8b, 0x14, 0x00, 0x02, 0xe0, 0x04, 
+0x00, 0x02, 0xef, 0x00, 0x00, 0x01, 0x5d, 0x0a, 
+0x00, 0x03, 0x4d, 0x52, 0x00, 0x00, 0x27, 0x6d, 
+0x00, 0x03, 0x4d, 0x57, 0x00, 0x00, 0x27, 0x6d, 
+0x00, 0x01, 0xc0, 0x01, 0x00, 0x03, 0x8b, 0x1a, 
+0x00, 0x02, 0xe0, 0x06, 0x00, 0x02, 0xee, 0x00, 
+0x00, 0x03, 0x4d, 0x57, 0x00, 0x00, 0x27, 0x6d, 
+0x00, 0x01, 0xc0, 0x01, 0x00, 0x03, 0x8b, 0x23, 
+0x00, 0x02, 0xef, 0x00, 0x00, 0x03, 0x2e, 0x56, 
+0x00, 0x02, 0xef, 0x00, 0x00, 0x03, 0x2e, 0x57, 
+0x00, 0x02, 0xef, 0x00, 0x00, 0x03, 0x71, 0x17, 
+0x00, 0x02, 0xef, 0x00, 0x00, 0x03, 0x72, 0x17, 
+0x00, 0x02, 0xef, 0x00, 0x00, 0x03, 0x77, 0x17, 
+0x00, 0x02, 0xef, 0x00, 0x00, 0x03, 0x41, 0x17, 
+0x00, 0x03, 0xa1, 0x36, 0x00, 0x01, 0x5f, 0x82, 
+0x00, 0x02, 0xf1, 0x75, 0x00, 0x03, 0x59, 0x17, 
+0x00, 0x03, 0x5b, 0x1f, 0x00, 0x03, 0x8f, 0xc5, 
+0x00, 0x00, 0xc4, 0x89, 0x00, 0x01, 0x40, 0x07, 
+0x00, 0x03, 0x4d, 0x10, 0x00, 0x00, 0xa4, 0x0d, 
+0x00, 0x03, 0x73, 0x10, 0x00, 0x02, 0xee, 0x00, 
+0x00, 0x03, 0x50, 0x34, 0x00, 0x03, 0x20, 0x03, 
+0x00, 0x02, 0xe4, 0x00, 0x00, 0x03, 0x53, 0x00, 
+0x00, 0x03, 0xb3, 0x48, 0x00, 0x01, 0x4d, 0x84, 
+0x00, 0x02, 0x8c, 0x01, 0x00, 0x03, 0x89, 0x50, 
+0x00, 0x00, 0x4c, 0x0e, 0x00, 0x01, 0x40, 0x01, 
+0x00, 0x02, 0x40, 0x05, 0x00, 0x03, 0x81, 0x50, 
+0x00, 0x02, 0xe4, 0x01, 0x00, 0x02, 0x40, 0x03, 
+0x00, 0x03, 0x81, 0x50, 0x00, 0x02, 0xe4, 0x02, 
+0x00, 0x03, 0x75, 0x12, 0x00, 0x02, 0xe0, 0x00, 
+0x00, 0x03, 0x76, 0x10, 0x00, 0x02, 0x48, 0x02, 
+0x00, 0x03, 0x85, 0x70, 0x00, 0x03, 0x59, 0x37, 
+0x00, 0x03, 0x50, 0x33, 0x00, 0x01, 0x40, 0x02, 
+0x00, 0x03, 0x58, 0x10, 0x00, 0x03, 0x5b, 0x1f, 
+0x00, 0x03, 0x8f, 0xc5, 0x00, 0x00, 0xc4, 0x81, 
+0x00, 0x01, 0x40, 0x0f, 0x00, 0x03, 0x4d, 0x10, 
+0x00, 0x00, 0xa4, 0x0d, 0x00, 0x00, 0x23, 0xaf, 
+0x00, 0x03, 0x59, 0x31, 0x00, 0x03, 0xb3, 0x63, 
+0x00, 0x03, 0x59, 0x32, 0x00, 0x03, 0x4c, 0x1f, 
+0x00, 0x03, 0x8f, 0x7c, 0x00, 0x03, 0x72, 0x10, 
+0x00, 0x03, 0x4b, 0x0e, 0x00, 0x02, 0x60, 0x0e, 
+0x00, 0x03, 0x85, 0x6a, 0x00, 0x03, 0x4b, 0x10, 
+0x00, 0x03, 0x50, 0x0e, 0x00, 0x01, 0xa0, 0x0b, 
+0x00, 0x03, 0x76, 0x10, 0x00, 0x03, 0x51, 0x32, 
+0x00, 0x01, 0xa4, 0x8b, 0x00, 0x03, 0x72, 0x11, 
+0x00, 0x02, 0xef, 0x00, 0x00, 0x03, 0x50, 0x10, 
+0x00, 0x02, 0xef, 0x00, 0x00, 0x03, 0x4a, 0x17, 
+0x00, 0x02, 0xef, 0x00, 0x00, 0x03, 0x4b, 0x17, 
+0x00, 0x02, 0xef, 0x00, 0x00, 0x03, 0x4b, 0x17, 
+0x00, 0x02, 0xee, 0x00, 0x00, 0x03, 0x45, 0x17, 
+0x00, 0x02, 0xee, 0x00, 0x00, 0x03, 0x8f, 0x7a, 
+0x00, 0x03, 0x50, 0x33, 0x00, 0x01, 0x40, 0x02, 
+0x00, 0x03, 0x58, 0x10, 0x00, 0x03, 0x5b, 0x1f, 
+0x00, 0x03, 0x8f, 0xc5, 0x00, 0x00, 0xc4, 0x81, 
+0x00, 0x01, 0x40, 0x0f, 0x00, 0x03, 0x4d, 0x10, 
+0x00, 0x03, 0x5f, 0x0c, 0x00, 0x00, 0xa4, 0x0d, 
+0x00, 0x03, 0x58, 0x35, 0x00, 0x03, 0x59, 0x36, 
+0x00, 0x03, 0x5b, 0x1f, 0x00, 0x03, 0x8f, 0xc5, 
+0x00, 0x00, 0xc4, 0x89, 0x00, 0x01, 0x40, 0x07, 
+0x00, 0x03, 0x4d, 0x10, 0x00, 0x00, 0xa4, 0x0d, 
+0x00, 0x03, 0xa1, 0x90, 0x00, 0x01, 0x40, 0x02, 
+0x00, 0x03, 0xb1, 0x92, 0x00, 0x00, 0xc0, 0x02, 
+0x00, 0x03, 0x5f, 0x0c, 0x00, 0x03, 0x4f, 0x10, 
+0x00, 0x03, 0xa7, 0x96, 0x00, 0x02, 0xec, 0x0f, 
+0x00, 0x03, 0x4c, 0x33, 0x00, 0x00, 0x3b, 0x0c, 
+0x00, 0x03, 0x87, 0x9a, 0x00, 0x02, 0xec, 0x00, 
+0x00, 0x03, 0x4d, 0x16, 0x00, 0x00, 0xd8, 0x01, 
+0x00, 0x00, 0x22, 0x03, 0x00, 0x03, 0x52, 0x03, 
+0x00, 0x01, 0x49, 0x01, 0x00, 0x00, 0x2a, 0x8d, 
+0x00, 0x03, 0x50, 0x15, 0x00, 0x03, 0xa9, 0xbf, 
+0x00, 0x03, 0xa7, 0xb5, 0x00, 0x03, 0xab, 0xb0, 
+0x00, 0x03, 0xad, 0xab, 0x00, 0x03, 0xa3, 0xa8, 
+0x00, 0x03, 0xa5, 0xc2, 0x00, 0x03, 0x8f, 0xc3, 
+0x00, 0x03, 0x50, 0x14, 0x00, 0x03, 0xa5, 0xc3, 
+0x00, 0x03, 0x8f, 0xc2, 0x00, 0x03, 0xa3, 0xae, 
+0x00, 0x03, 0xaf, 0xc3, 0x00, 0x03, 0x8f, 0xc2, 
+0x00, 0x03, 0x50, 0x14, 0x00, 0x03, 0x8f, 0xc2, 
+0x00, 0x03, 0x50, 0x16, 0x00, 0x03, 0xa3, 0xb3, 
+0x00, 0x03, 0x8f, 0xc3, 0x00, 0x00, 0xc0, 0x01, 
+0x00, 0x03, 0x8f, 0xc2, 0x00, 0x03, 0xab, 0xbc, 
+0x00, 0x03, 0xa3, 0xb9, 0x00, 0x03, 0xaf, 0xc3, 
+0x00, 0x03, 0x8f, 0xc2, 0x00, 0x03, 0x50, 0x14, 
+0x00, 0x03, 0xaf, 0xc3, 0x00, 0x03, 0x8f, 0xc2, 
+0x00, 0x03, 0x50, 0x16, 0x00, 0x01, 0xc0, 0x01, 
+0x00, 0x03, 0x8f, 0xc3, 0x00, 0x03, 0xa7, 0xc1, 
+0x00, 0x03, 0x8f, 0xa4, 0x00, 0x03, 0xaf, 0xc3, 
+0x00, 0x00, 0x00, 0x01, 0x00, 0x03, 0x5f, 0x1b, 
+0x00, 0x03, 0x20, 0x70, 0x00, 0x02, 0xe4, 0x01, 
+0x00, 0x02, 0xe0, 0x01, 0x00, 0x00, 0xc0, 0x0f, 
+0x00, 0x03, 0x5a, 0x10, 0x00, 0x02, 0xe0, 0x00, 
+0x00, 0x02, 0xe2, 0x00, 0x00, 0x02, 0xa8, 0x19, 
+0x00, 0x03, 0x89, 0xce, 0x00, 0x00, 0x24, 0x98, 
+0x00, 0x01, 0x40, 0x01, 0x00, 0x02, 0x84, 0x01, 
+0x00, 0x03, 0x89, 0xd2, 0x00, 0x00, 0xa0, 0x1a, 
+0x00, 0x01, 0x44, 0x81, 0x00, 0x00, 0xc9, 0x01, 
+0x00, 0x03, 0x8b, 0xcb, 0x00, 0x03, 0x5f, 0x1b, 
+0x00, 0x03, 0x50, 0x10, 0x00, 0x03, 0x50, 0x10, 
+0x00, 0x03, 0x50, 0x10, 0x00, 0x03, 0x50, 0x10, 
+0x00, 0x03, 0x50, 0x10, 0x00, 0x03, 0x50, 0x10, 
+0x00, 0x03, 0x50, 0x10, 0x00, 0x03, 0x50, 0x10, 
+0x00, 0x03, 0x50, 0x10, 0x00, 0x03, 0x50, 0x10, 
+0x00, 0x03, 0x50, 0x10, 0x00, 0x03, 0x50, 0x10, 
+0x00, 0x03, 0x50, 0x10, 0x00, 0x03, 0x50, 0x10, 
+0x00, 0x03, 0x50, 0x10, 0x00, 0x03, 0x50, 0x10, 
+0x00, 0x03, 0x50, 0x10, 0x00, 0x03, 0x50, 0x10, 
+0x00, 0x03, 0x50, 0x10, 0x00, 0x03, 0x50, 0x10, 
+0x00, 0x03, 0x50, 0x10, 0x00, 0x03, 0x50, 0x10, 
+0x00, 0x03, 0x50, 0x10, 0x00, 0x03, 0x50, 0x10, 
+0x00, 0x03, 0x50, 0x10, 0x00, 0x03, 0x50, 0x10, 
+0x00, 0x03, 0x50, 0x10, 0x00, 0x03, 0x50, 0x10, 
+0x00, 0x03, 0x50, 0x10, 0x00, 0x03, 0x50, 0x10, 
+0x00, 0x03, 0x50, 0x10, 0x00, 0x03, 0x50, 0x10, 
+0x00, 0x03, 0x50, 0x10, 0x00, 0x03, 0x50, 0x10, 
+0x00, 0x03, 0x50, 0x10, 0x00, 0x03, 0x50, 0x10, 
+0x00, 0x03, 0x50, 0x10, 0x00, 0x03, 0x50, 0x10, 
+0x00, 0x03, 0x50, 0x10, 0x00, 0x03, 0x50, 0x10, 
+0x00, 0x03, 0x50, 0x10, 0x00, 0x03, 0x50, 0x10, 
+0x0a, 0x0d, 
+};
+static uchar
+zrmpeg3s[] = {
+0x00, 0x03, 0x8e, 0x00, 0x00, 0x03, 0xc0, 0x0a, 
+0x00, 0x03, 0x50, 0x0b, 0x00, 0x01, 0x40, 0x08, 
+0x00, 0x02, 0x80, 0x02, 0x00, 0x03, 0x8a, 0x07, 
+0x00, 0x02, 0xca, 0x00, 0x00, 0x02, 0x80, 0x01, 
+0x00, 0x03, 0x8a, 0x0a, 0x00, 0x02, 0xd6, 0xe0, 
+0x00, 0x02, 0xce, 0x00, 0x00, 0x03, 0x4c, 0x80, 
+0x00, 0x02, 0xde, 0x04, 0x00, 0x02, 0xc2, 0x00, 
+0x00, 0x02, 0xd2, 0x00, 0x00, 0x02, 0xf8, 0x00, 
+0x00, 0x03, 0x5d, 0x10, 0x00, 0x03, 0x5b, 0x1f, 
+0x00, 0x03, 0x8f, 0x8c, 0x00, 0x03, 0x88, 0x1d, 
+0x00, 0x02, 0x5c, 0xfa, 0x00, 0x03, 0x8a, 0x11, 
+0x00, 0x02, 0xe8, 0x46, 0x00, 0x02, 0xf8, 0x00, 
+0x00, 0x02, 0xef, 0x00, 0x00, 0x03, 0x5d, 0x17, 
+0x00, 0x01, 0xd2, 0x01, 0x00, 0x03, 0x8a, 0x18, 
+0x00, 0x03, 0x8e, 0x11, 0x00, 0x02, 0xee, 0x00, 
+0x00, 0x03, 0x5b, 0x1f, 0x00, 0x03, 0x8f, 0xd2, 
+0x00, 0x03, 0xa0, 0x22, 0x00, 0x03, 0x46, 0x17, 
+0x00, 0x03, 0x5b, 0x1f, 0x00, 0x03, 0x8f, 0x8c, 
+0x00, 0x02, 0x5c, 0xbb, 0x00, 0x03, 0x8a, 0x29, 
+0x00, 0x03, 0x5b, 0x1f, 0x00, 0x03, 0x8f, 0x8c, 
+0x00, 0x03, 0x88, 0x1d, 0x00, 0x02, 0x5c, 0xb9, 
+0x00, 0x03, 0x89, 0x00, 0x00, 0x02, 0x5c, 0xbc, 
+0x00, 0x03, 0x84, 0x26, 0x00, 0x03, 0x50, 0x17, 
+0x00, 0x02, 0xef, 0x00, 0x00, 0x03, 0x54, 0x17, 
+0x00, 0x02, 0xee, 0x00, 0x00, 0x01, 0xd2, 0x01, 
+0x00, 0x02, 0x5c, 0xff, 0x00, 0x03, 0x88, 0x30, 
+0x00, 0x01, 0x5d, 0x06, 0x00, 0x02, 0x48, 0x01, 
+0x00, 0x03, 0x8a, 0x3a, 0x00, 0x02, 0xee, 0x00, 
+0x00, 0x01, 0xd2, 0x02, 0x00, 0x02, 0xee, 0x00, 
+0x00, 0x01, 0x5d, 0x04, 0x00, 0x03, 0x88, 0x46, 
+0x00, 0x03, 0x5b, 0x1f, 0x00, 0x03, 0x8f, 0xd2, 
+0x00, 0x03, 0x44, 0x17, 0x00, 0x01, 0xd2, 0x04, 
+0x00, 0x02, 0x48, 0x03, 0x00, 0x03, 0x8a, 0x46, 
+0x00, 0x02, 0xee, 0x00, 0x00, 0x03, 0x5b, 0x1f, 
+0x00, 0x03, 0x8f, 0xd2, 0x00, 0x01, 0xd2, 0x05, 
+0x00, 0x02, 0x50, 0x05, 0x00, 0x03, 0x86, 0x26, 
+0x00, 0x02, 0xe2, 0xff, 0x00, 0x00, 0x67, 0x6b, 
+0x00, 0x02, 0x60, 0x0d, 0x00, 0x03, 0x85, 0xfa, 
+0x00, 0x03, 0xb2, 0xab, 0x00, 0x02, 0x88, 0x02, 
+0x00, 0x03, 0x89, 0x16, 0x00, 0x03, 0x42, 0x17, 
+0x00, 0x03, 0x8e, 0xa0, 0x00, 0x02, 0x88, 0x02, 
+0x00, 0x03, 0x89, 0x16, 0x00, 0x03, 0x55, 0x00, 
+0x00, 0x02, 0x94, 0x01, 0x00, 0x03, 0x88, 0x5a, 
+0x00, 0x01, 0x57, 0x01, 0x00, 0x00, 0x5b, 0x07, 
+0x00, 0x03, 0xb5, 0x16, 0x00, 0x03, 0x8e, 0x5f, 
+0x00, 0x02, 0x94, 0x10, 0x00, 0x03, 0x89, 0x16, 
+0x00, 0x01, 0x57, 0x05, 0x00, 0x00, 0x5b, 0x07, 
+0x00, 0x03, 0xb7, 0x16, 0x00, 0x01, 0x41, 0x05, 
+0x00, 0x02, 0x48, 0x06, 0x00, 0x03, 0x8b, 0x16, 
+0x00, 0x02, 0x58, 0x00, 0x00, 0x03, 0x8b, 0x16, 
+0x00, 0x03, 0x55, 0x00, 0x00, 0x02, 0x94, 0x01, 
+0x00, 0x03, 0x8a, 0x69, 0x00, 0x03, 0x4b, 0x81, 
+0x00, 0x03, 0x8e, 0x6a, 0x00, 0x03, 0x4a, 0x81, 
+0x00, 0x03, 0x45, 0x10, 0x00, 0x03, 0xa0, 0x6e, 
+0x00, 0x03, 0x40, 0x81, 0x00, 0x03, 0x22, 0x16, 
+0x00, 0x03, 0x50, 0x35, 0x00, 0x02, 0x40, 0x01, 
+0x00, 0x03, 0x80, 0x75, 0x00, 0x03, 0x84, 0x7d, 
+0x00, 0x02, 0xe0, 0x00, 0x00, 0x02, 0xe2, 0x00, 
+0x00, 0x03, 0x8e, 0x79, 0x00, 0x03, 0x4d, 0x32, 
+0x00, 0x03, 0x50, 0x04, 0x00, 0x03, 0x4c, 0x1f, 
+0x00, 0x03, 0x8f, 0x83, 0x00, 0x03, 0x20, 0x10, 
+0x00, 0x03, 0x22, 0x11, 0x00, 0x03, 0x20, 0x12, 
+0x00, 0x03, 0x22, 0x13, 0x00, 0x03, 0xb4, 0x80, 
+0x00, 0x03, 0xb6, 0x80, 0x00, 0x03, 0x8e, 0x51, 
+0x00, 0x03, 0x55, 0x1c, 0x00, 0x02, 0xf8, 0x01, 
+0x00, 0x03, 0xb4, 0x84, 0x00, 0x02, 0xf8, 0x02, 
+0x00, 0x03, 0x51, 0x07, 0x00, 0x03, 0x5d, 0x11, 
+0x00, 0x01, 0xd2, 0x02, 0x00, 0x03, 0xa6, 0x90, 
+0x00, 0x03, 0x43, 0x81, 0x00, 0x02, 0xe2, 0xff, 
+0x00, 0x00, 0xc4, 0x88, 0x00, 0x00, 0x67, 0x65, 
+0x00, 0x02, 0xee, 0x00, 0x00, 0x00, 0xbc, 0x8d, 
+0x00, 0x03, 0x5d, 0x11, 0x00, 0x01, 0xd2, 0x01, 
+0x00, 0x02, 0xef, 0x00, 0x00, 0x03, 0x5d, 0x17, 
+0x00, 0x01, 0xd2, 0x02, 0x00, 0x02, 0x50, 0x01, 
+0x00, 0x03, 0x80, 0x90, 0x00, 0x02, 0xef, 0x00, 
+0x00, 0x03, 0x47, 0x17, 0x00, 0x02, 0x90, 0x01, 
+0x00, 0x03, 0x88, 0x26, 0x00, 0x02, 0xee, 0x00, 
+0x00, 0x00, 0xdd, 0x08, 0x00, 0x02, 0xe0, 0xff, 
+0x00, 0x00, 0x61, 0x65, 0x00, 0x00, 0xa9, 0x65, 
+0x00, 0x03, 0x43, 0x80, 0x00, 0x03, 0x8e, 0x26, 
+0x00, 0x03, 0x48, 0x81, 0x00, 0x03, 0x49, 0x81, 
+0x00, 0x03, 0xa0, 0xa5, 0x00, 0x03, 0x40, 0x81, 
+0x00, 0x03, 0x22, 0x16, 0x00, 0x03, 0x4d, 0x36, 
+0x00, 0x03, 0x50, 0x02, 0x00, 0x03, 0x4c, 0x1f, 
+0x00, 0x03, 0x8f, 0x83, 0x00, 0x03, 0x20, 0x14, 
+0x00, 0x03, 0x22, 0x15, 0x00, 0x03, 0x50, 0x0e, 
+0x00, 0x02, 0xf8, 0x00, 0x00, 0x03, 0x51, 0x07, 
+0x00, 0x03, 0x5d, 0x11, 0x00, 0x03, 0x45, 0x80, 
+0x00, 0x03, 0xa5, 0x1d, 0x00, 0x01, 0xd2, 0x02, 
+0x00, 0x03, 0xa2, 0xbc, 0x00, 0x03, 0x41, 0x81, 
+0x00, 0x02, 0xe2, 0xff, 0x00, 0x00, 0xc4, 0x88, 
+0x00, 0x00, 0x67, 0x60, 0x00, 0x02, 0xee, 0x00, 
+0x00, 0x00, 0xbf, 0x8d, 0x00, 0x00, 0x12, 0x01, 
+0x00, 0x03, 0x8e, 0xbd, 0x00, 0x03, 0x50, 0x17, 
+0x00, 0x02, 0xef, 0x00, 0x00, 0x03, 0xa8, 0xc7, 
+0x00, 0x02, 0x5d, 0xe0, 0x00, 0x03, 0x89, 0x98, 
+0x00, 0x02, 0x5c, 0x00, 0x00, 0x03, 0x8a, 0xc7, 
+0x00, 0x02, 0xec, 0x01, 0x00, 0x01, 0xbb, 0xef, 
+0x00, 0x03, 0x8a, 0xcd, 0x00, 0x02, 0xde, 0x01, 
+0x00, 0x03, 0x8e, 0xcf, 0x00, 0x02, 0xde, 0x04, 
+0x00, 0x02, 0x5c, 0x01, 0x00, 0x03, 0x89, 0x1d, 
+0x00, 0x01, 0x5f, 0x08, 0x00, 0x02, 0x58, 0x01, 
+0x00, 0x03, 0x89, 0x70, 0x00, 0x03, 0xa8, 0xcf, 
+0x00, 0x03, 0x5d, 0x17, 0x00, 0x01, 0xd2, 0x02, 
+0x00, 0x02, 0x50, 0x01, 0x00, 0x03, 0x80, 0xbb, 
+0x00, 0x03, 0x51, 0x17, 0x00, 0x02, 0xef, 0x00, 
+0x00, 0x03, 0x45, 0x81, 0x00, 0x03, 0x42, 0x80, 
+0x00, 0x02, 0xce, 0x00, 0x00, 0x03, 0x4e, 0x17, 
+0x00, 0x03, 0xa8, 0xda, 0x00, 0x03, 0x47, 0x17, 
+0x00, 0x02, 0xd8, 0x26, 0x00, 0x02, 0x90, 0x01, 
+0x00, 0x03, 0x88, 0xde, 0x00, 0x02, 0xd8, 0xf9, 
+0x00, 0x03, 0x4f, 0x81, 0x00, 0x02, 0x5c, 0x01, 
+0x00, 0x03, 0x88, 0xea, 0x00, 0x01, 0x5e, 0x88, 
+0x00, 0x02, 0x54, 0x01, 0x00, 0x03, 0x88, 0xe5, 
+0x00, 0x03, 0x5f, 0x0c, 0x00, 0x02, 0xda, 0xff, 
+0x00, 0x00, 0x7f, 0x0d, 0x00, 0x02, 0x44, 0x00, 
+0x00, 0x03, 0x89, 0x2d, 0x00, 0x03, 0x5f, 0x0c, 
+0x00, 0x02, 0x84, 0xff, 0x00, 0x03, 0x8a, 0xf1, 
+0x00, 0x02, 0x90, 0x01, 0x00, 0x03, 0x8a, 0xf4, 
+0x00, 0x03, 0x42, 0x81, 0x00, 0x02, 0xce, 0x00, 
+0x00, 0x02, 0xdc, 0x00, 0x00, 0x03, 0x50, 0x10, 
+0x00, 0x03, 0x5f, 0x0c, 0x00, 0x03, 0x50, 0x10, 
+0x00, 0x02, 0xee, 0x00, 0x00, 0x00, 0xdd, 0x08, 
+0x00, 0x03, 0x56, 0x17, 0x00, 0x02, 0xd8, 0xfb, 
+0x00, 0x03, 0x8f, 0x2d, 0x00, 0x02, 0xee, 0x00, 
+0x00, 0x00, 0xdd, 0x08, 0x00, 0x02, 0xe0, 0xff, 
+0x00, 0x00, 0x60, 0x20, 0x00, 0x00, 0xa8, 0x20, 
+0x00, 0x03, 0x41, 0x80, 0x00, 0x03, 0x8e, 0x26, 
+0x00, 0x03, 0xb9, 0x0a, 0x00, 0x03, 0x50, 0x1e, 
+0x00, 0x02, 0x40, 0x00, 0x00, 0x03, 0x89, 0x06, 
+0x00, 0x02, 0x40, 0x90, 0x00, 0x03, 0x8b, 0x08, 
+0x00, 0x02, 0xe0, 0x10, 0x00, 0x03, 0x20, 0xc0, 
+0x00, 0x02, 0xe0, 0x80, 0x00, 0x03, 0x20, 0xc0, 
+0x00, 0x03, 0x50, 0x07, 0x00, 0x03, 0xa7, 0x0e, 
+0x00, 0x03, 0x50, 0x05, 0x00, 0x03, 0x8f, 0x0f, 
+0x00, 0x03, 0x50, 0x05, 0x00, 0x02, 0xf8, 0x00, 
+0x00, 0x03, 0x5d, 0x10, 0x00, 0x03, 0x4c, 0x1f, 
+0x00, 0x03, 0x8f, 0x7c, 0x00, 0x02, 0xe0, 0xf0, 
+0x00, 0x03, 0x20, 0xc0, 0x00, 0x03, 0x8e, 0x00, 
+0x00, 0x02, 0x50, 0x00, 0x00, 0x03, 0x88, 0x26, 
+0x00, 0x01, 0xd2, 0x02, 0x00, 0x02, 0xef, 0x00, 
+0x00, 0x01, 0xd2, 0x02, 0x00, 0x03, 0x85, 0x19, 
+0x00, 0x03, 0x8e, 0x26, 0x00, 0x03, 0x4f, 0x80, 
+0x00, 0x03, 0x56, 0x10, 0x00, 0x03, 0xbf, 0x21, 
+0x00, 0x00, 0xdb, 0x08, 0x00, 0x02, 0x58, 0x00, 
+0x00, 0x03, 0x8a, 0xcd, 0x00, 0x02, 0xd8, 0xcd, 
+0x00, 0x03, 0xbf, 0x2b, 0x00, 0x01, 0xd2, 0x02, 
+0x00, 0x02, 0x50, 0x01, 0x00, 0x03, 0x87, 0x72, 
+0x00, 0x02, 0xef, 0x00, 0x00, 0x01, 0x5f, 0x08, 
+0x00, 0x03, 0x8f, 0x2d, 0x00, 0x00, 0xdf, 0x08, 
+0x00, 0x01, 0x5b, 0x08, 0x00, 0x02, 0x58, 0xb7, 
+0x00, 0x03, 0x89, 0x6b, 0x00, 0x02, 0x58, 0xb8, 
+0x00, 0x03, 0x89, 0x5e, 0x00, 0x02, 0xea, 0x00, 
+0x00, 0x02, 0x74, 0x0a, 0x00, 0x03, 0x89, 0x63, 
+0x00, 0x02, 0x58, 0xb3, 0x00, 0x03, 0x89, 0x5f, 
+0x00, 0x02, 0x58, 0xb8, 0x00, 0x03, 0x89, 0x5f, 
+0x00, 0x02, 0x58, 0x00, 0x00, 0x03, 0x8b, 0x63, 
+0x00, 0x02, 0xec, 0x01, 0x00, 0x00, 0x3a, 0x69, 
+0x00, 0x03, 0xa9, 0x5f, 0x00, 0x02, 0x50, 0x01, 
+0x00, 0x03, 0x87, 0x63, 0x00, 0x03, 0x56, 0x0a, 
+0x00, 0x02, 0x78, 0x09, 0x00, 0x03, 0x83, 0x63, 
+0x00, 0x03, 0xbf, 0x49, 0x00, 0x00, 0xdf, 0x88, 
+0x00, 0x03, 0x4d, 0x17, 0x00, 0x02, 0xee, 0x00, 
+0x00, 0x00, 0xbf, 0x8d, 0x00, 0x01, 0xd2, 0x01, 
+0x00, 0x03, 0x8f, 0x4b, 0x00, 0x02, 0xef, 0x00, 
+0x00, 0x01, 0xd2, 0x02, 0x00, 0x00, 0x5f, 0x38, 
+0x00, 0x02, 0x58, 0x18, 0x00, 0x03, 0x8b, 0x59, 
+0x00, 0x03, 0x44, 0x81, 0x00, 0x03, 0x56, 0x0a, 
+0x00, 0x01, 0xba, 0x69, 0x00, 0x00, 0x38, 0x61, 
+0x00, 0x03, 0x56, 0x01, 0x00, 0x01, 0xbb, 0x0b, 
+0x00, 0x02, 0x78, 0x0a, 0x00, 0x03, 0x81, 0x58, 
+0x00, 0x02, 0xc2, 0x00, 0x00, 0x01, 0xba, 0x69, 
+0x00, 0x03, 0x8f, 0x68, 0x00, 0x02, 0xec, 0x00, 
+0x00, 0x03, 0x5d, 0x16, 0x00, 0x02, 0xed, 0x00, 
+0x00, 0x03, 0x5d, 0x16, 0x00, 0x03, 0x8f, 0x68, 
+0x00, 0x03, 0xaf, 0x75, 0x00, 0x03, 0x44, 0x80, 
+0x00, 0x02, 0xec, 0x00, 0x00, 0x03, 0x5d, 0x16, 
+0x00, 0x03, 0x47, 0x0e, 0x00, 0x03, 0xbf, 0x68, 
+0x00, 0x03, 0xa9, 0x68, 0x00, 0x03, 0xab, 0x68, 
+0x00, 0x02, 0xec, 0x01, 0x00, 0x03, 0x5d, 0x16, 
+0x00, 0x03, 0x50, 0x10, 0x00, 0x03, 0x5f, 0x0c, 
+0x00, 0x03, 0x50, 0x10, 0x00, 0x02, 0xec, 0xcd, 
+0x00, 0x02, 0x78, 0x0c, 0x00, 0x03, 0x8b, 0x76, 
+0x00, 0x02, 0xd8, 0xcf, 0x00, 0x03, 0x8f, 0x76, 
+0x00, 0x03, 0x4f, 0x81, 0x00, 0x03, 0x8f, 0x1e, 
+0x00, 0x02, 0xe2, 0x01, 0x00, 0x02, 0xd8, 0xd4, 
+0x00, 0x03, 0x8f, 0x28, 0x00, 0x02, 0xd9, 0x00, 
+0x00, 0x03, 0x4c, 0x81, 0x00, 0x02, 0xce, 0x00, 
+0x00, 0x02, 0xe6, 0x00, 0x00, 0x03, 0x5d, 0x13, 
+0x00, 0x02, 0xe7, 0xb7, 0x00, 0x03, 0x5d, 0x13, 
+0x00, 0x02, 0xec, 0x0a, 0x00, 0x02, 0xe6, 0x00, 
+0x00, 0x03, 0x5d, 0x13, 0x00, 0x01, 0xdb, 0x01, 
+0x00, 0x03, 0x8b, 0x7e, 0x00, 0x03, 0x5f, 0x0c, 
+0x00, 0x03, 0x50, 0x10, 0x00, 0x01, 0xe0, 0x06, 
+0x00, 0x01, 0x40, 0x01, 0x00, 0x03, 0x58, 0x10, 
+0x00, 0x03, 0x59, 0x33, 0x00, 0x03, 0x5b, 0x1f, 
+0x00, 0x03, 0x8f, 0xda, 0x00, 0x00, 0x24, 0x0d, 
+0x00, 0x03, 0x5f, 0x0c, 0x00, 0x01, 0x40, 0x88, 
+0x00, 0x02, 0xe0, 0x00, 0x00, 0x02, 0xee, 0x00, 
+0x00, 0x02, 0x5c, 0x01, 0x00, 0x03, 0x89, 0x93, 
+0x00, 0x03, 0x81, 0x8c, 0x00, 0x00, 0x00, 0x01, 
+0x00, 0x03, 0x8f, 0x8d, 0x00, 0x02, 0x40, 0x02, 
+0x00, 0x03, 0x85, 0x8c, 0x00, 0x02, 0xee, 0x00, 
+0x00, 0x03, 0x5f, 0x1b, 0x00, 0x02, 0x5c, 0xba, 
+0x00, 0x02, 0x50, 0x0d, 0x00, 0x03, 0x84, 0xc0, 
+0x00, 0x02, 0xea, 0x02, 0x00, 0x02, 0xe2, 0x3c, 
+0x00, 0x02, 0x50, 0x0d, 0x00, 0x03, 0x83, 0xa0, 
+0x00, 0x02, 0xef, 0x00, 0x00, 0x03, 0x8e, 0xc0, 
+0x00, 0x00, 0x16, 0x81, 0x00, 0x02, 0xee, 0x00, 
+0x00, 0x02, 0xc4, 0xff, 0x00, 0x00, 0x64, 0xa2, 
+0x00, 0x02, 0x7c, 0x02, 0x00, 0x03, 0x8b, 0xb0, 
+0x00, 0x00, 0x45, 0x07, 0x00, 0x01, 0x44, 0x83, 
+0x00, 0x00, 0xc9, 0x08, 0x00, 0x03, 0x42, 0x12, 
+0x00, 0x00, 0xa4, 0x82, 0x00, 0x02, 0x54, 0x0b, 
+0x00, 0x03, 0x85, 0xa0, 0x00, 0x02, 0xea, 0x00, 
+0x00, 0x01, 0xd2, 0x0b, 0x00, 0x03, 0x8f, 0x9c, 
+0x00, 0x03, 0x43, 0x15, 0x00, 0x01, 0xf2, 0x03, 
+0x00, 0x02, 0xe2, 0x07, 0x00, 0x00, 0xc4, 0x88, 
+0x00, 0x00, 0x04, 0x81, 0x00, 0x00, 0xc5, 0x08, 
+0x00, 0x02, 0x54, 0x02, 0x00, 0x03, 0x81, 0xc3, 
+0x00, 0x03, 0x85, 0xbd, 0x00, 0x03, 0x42, 0x12, 
+0x00, 0x00, 0xbf, 0x82, 0x00, 0x00, 0x12, 0x02, 
+0x00, 0x03, 0x8e, 0xc0, 0x00, 0x00, 0xdd, 0x08, 
+0x00, 0x03, 0x43, 0x12, 0x00, 0x02, 0xee, 0x00, 
+0x00, 0x00, 0xbf, 0x83, 0x00, 0x00, 0x12, 0x01, 
+0x00, 0x03, 0x8e, 0xc0, 0x00, 0x03, 0x43, 0x12, 
+0x00, 0x03, 0x4f, 0x80, 0x00, 0x00, 0x45, 0x07, 
+0x00, 0x01, 0x44, 0x83, 0x00, 0x00, 0xc9, 0x08, 
+0x00, 0x03, 0x42, 0x12, 0x00, 0x00, 0xa4, 0x82, 
+0x00, 0x03, 0xbf, 0xb5, 0x00, 0x00, 0xc5, 0x08, 
+0x00, 0x01, 0x49, 0x08, 0x00, 0x00, 0xa9, 0x03, 
+0x00, 0x03, 0x5d, 0x12, 0x00, 0x01, 0xd6, 0x82, 
+0x00, 0x03, 0x4f, 0x81, 0x00, 0x03, 0x8f, 0xc5, 
+0x00, 0x02, 0xef, 0x00, 0x00, 0x00, 0x5f, 0x82, 
+0x00, 0x00, 0xdf, 0x8e, 0x00, 0x03, 0x5a, 0x17, 
+0x00, 0x02, 0xef, 0x00, 0x00, 0x01, 0x5f, 0x81, 
+0x00, 0x03, 0x5f, 0x1b, 0x00, 0x00, 0xbf, 0x9a, 
+0x00, 0x02, 0xe4, 0x01, 0x00, 0x02, 0xe0, 0x01, 
+0x00, 0x00, 0xc0, 0x0f, 0x00, 0x03, 0x5a, 0x10, 
+0x00, 0x02, 0xe0, 0x00, 0x00, 0x02, 0xe2, 0x00, 
+0x00, 0x02, 0xa8, 0x19, 0x00, 0x03, 0x89, 0xe3, 
+0x00, 0x00, 0x24, 0x98, 0x00, 0x01, 0x40, 0x01, 
+0x00, 0x02, 0x84, 0x01, 0x00, 0x03, 0x89, 0xe7, 
+0x00, 0x00, 0xa0, 0x1a, 0x00, 0x01, 0x44, 0x81, 
+0x00, 0x00, 0xc9, 0x01, 0x00, 0x03, 0x8b, 0xe0, 
+0x00, 0x03, 0x5f, 0x1b, 0x00, 0x03, 0x50, 0x10, 
+0x00, 0x03, 0x50, 0x10, 0x00, 0x03, 0x50, 0x10, 
+0x00, 0x03, 0x50, 0x10, 0x00, 0x03, 0x50, 0x10, 
+0x00, 0x03, 0x3c, 0xc0, 0x00, 0x03, 0x70, 0x10, 
+0x00, 0x03, 0x50, 0x1e, 0x00, 0x02, 0x40, 0xe0, 
+0x00, 0x03, 0x8b, 0xf6, 0x00, 0x03, 0x47, 0x81, 
+0x00, 0x01, 0x40, 0x04, 0x00, 0x03, 0x88, 0x01, 
+0x00, 0x03, 0x60, 0x80, 0x00, 0x03, 0x50, 0x30, 
+0x00, 0x02, 0xea, 0xff, 0x00, 0x00, 0x77, 0x65, 
+0x00, 0x03, 0x88, 0x51, 0x00, 0x02, 0x60, 0x0d, 
+0x00, 0x03, 0x88, 0x7d, 0x00, 0x03, 0x8f, 0x16, 
+0x0a, 0x0d, 
+};
--- /dev/null
+++ b/os/port/NOTICE
@@ -1,0 +1,18 @@
+The following files are subject to the Lucent Public License 1.02:
+
+devbridge.c
+devds.c
+devdup.c
+devloopback.c
+devpci.c	(portions)
+devpnp.c
+devsd.c
+devuart.c
+edf.c
+edf.h
+ethermii.c
+ethermii.h
+log.c
+mul64fract.c
+rdb.c
+sd.h
--- /dev/null
+++ b/os/port/alarm.c
@@ -1,0 +1,41 @@
+#include	"u.h"
+#include	"../port/lib.h"
+#include	"mem.h"
+#include	"dat.h"
+#include	"fns.h"
+
+Talarm	talarm;
+
+/*
+ *  called every clock tick
+ */
+void
+checkalarms(void)
+{
+	Proc *p;
+	ulong now;
+
+	now = MACHP(0)->ticks;
+
+	if(talarm.list == 0 || canlock(&talarm) == 0)
+		return;
+
+	for(;;) {
+		p = talarm.list;
+		if(p == 0)
+			break;
+
+		if(p->twhen == 0) {
+			talarm.list = p->tlink;
+			p->trend = 0;
+			continue;
+		}
+		if(now < p->twhen)
+			break;
+		wakeup(p->trend);
+		talarm.list = p->tlink;
+		p->trend = 0;
+	}
+
+	unlock(&talarm);
+}
--- /dev/null
+++ b/os/port/alloc.c
@@ -1,0 +1,963 @@
+#include "u.h"
+#include "../port/lib.h"
+#include "mem.h"
+#include "dat.h"
+#include "fns.h"
+#include "interp.h"
+
+#define left	u.s.bhl
+#define right	u.s.bhr
+#define fwd	u.s.bhf
+#define prev	u.s.bhv
+#define parent	u.s.bhp
+
+#define RESERVED	256*1024
+
+struct Pool
+{
+	char*	name;
+	int	pnum;
+	ulong	maxsize;
+	int	quanta;
+	int	chunk;
+	ulong	ressize;
+	ulong	cursize;
+	ulong	arenasize;
+	ulong	hw;
+	Lock	l;
+	Bhdr*	root;
+	Bhdr*	chain;
+	ulong	nalloc;
+	ulong	nfree;
+	int	nbrk;
+	int	lastfree;
+	int	warn;
+	void	(*move)(void*, void*);
+};
+
+static
+struct
+{
+	int	n;
+	Pool	pool[MAXPOOL];
+	// Lock	l;
+} table = {
+	3,
+	{
+		{ "main",  0,	 4*1024*1024, 31,  128*1024, 15*256*1024 },
+		{ "heap",  1,	16*1024*1024, 31,  128*1024, 15*1024*1024 },
+		{ "image", 2,	 8*1024*1024, 31, 300*1024, 15*512*1024 },
+	}
+};
+
+Pool*	mainmem = &table.pool[0];
+Pool*	heapmem = &table.pool[1];
+Pool*	imagmem = &table.pool[2];
+
+static void _auditmemloc(char *, void *);
+void (*auditmemloc)(char *, void *) = _auditmemloc;
+static void _poolfault(void *, char *, ulong);
+void (*poolfault)(void *, char *, ulong) = _poolfault;
+
+/*	non tracing
+ *
+enum {
+	Npadlong	= 0,
+	MallocOffset = 0,
+	ReallocOffset = 0
+};
+ *
+ */
+
+/* tracing */
+enum {
+	Npadlong	= 2,
+	MallocOffset = 0,
+	ReallocOffset = 1
+};
+
+int
+memusehigh(void)
+{
+	return 	mainmem->cursize > mainmem->ressize ||
+			heapmem->cursize > heapmem->ressize ||
+			imagmem->cursize > imagmem->ressize;
+}
+
+void
+poolimmutable(void *v)
+{
+	Bhdr *b;
+
+	D2B(b, v);
+	b->magic = MAGIC_I;
+}
+
+void
+poolmutable(void *v)
+{
+	Bhdr *b;
+
+	D2B(b, v);
+	b->magic = MAGIC_A;
+	((Heap*)v)->color = mutator;
+}
+
+char*
+poolname(Pool *p)
+{
+	return p->name;
+}
+
+Bhdr*
+poolchain(Pool *p)
+{
+	return p->chain;
+}
+
+void
+pooldel(Pool *p, Bhdr *t)
+{
+	Bhdr *s, *f, *rp, *q;
+
+	if(t->parent == nil && p->root != t) {
+		t->prev->fwd = t->fwd;
+		t->fwd->prev = t->prev;
+		return;
+	}
+
+	if(t->fwd != t) {
+		f = t->fwd;
+		s = t->parent;
+		f->parent = s;
+		if(s == nil)
+			p->root = f;
+		else {
+			if(s->left == t)
+				s->left = f;
+			else
+				s->right = f;
+		}
+
+		rp = t->left;
+		f->left = rp;
+		if(rp != nil)
+			rp->parent = f;
+		rp = t->right;
+		f->right = rp;
+		if(rp != nil)
+			rp->parent = f;
+
+		t->prev->fwd = t->fwd;
+		t->fwd->prev = t->prev;
+		return;
+	}
+
+	if(t->left == nil)
+		rp = t->right;
+	else {
+		if(t->right == nil)
+			rp = t->left;
+		else {
+			f = t;
+			rp = t->right;
+			s = rp->left;
+			while(s != nil) {
+				f = rp;
+				rp = s;
+				s = rp->left;
+			}
+			if(f != t) {
+				s = rp->right;
+				f->left = s;
+				if(s != nil)
+					s->parent = f;
+				s = t->right;
+				rp->right = s;
+				if(s != nil)
+					s->parent = rp;
+			}
+			s = t->left;
+			rp->left = s;
+			s->parent = rp;
+		}
+	}
+	q = t->parent;
+	if(q == nil)
+		p->root = rp;
+	else {
+		if(t == q->left)
+			q->left = rp;
+		else
+			q->right = rp;
+	}
+	if(rp != nil)
+		rp->parent = q;
+}
+
+void
+pooladd(Pool *p, Bhdr *q)
+{
+	int size;
+	Bhdr *tp, *t;
+
+	q->magic = MAGIC_F;
+
+	q->left = nil;
+	q->right = nil;
+	q->parent = nil;
+	q->fwd = q;
+	q->prev = q;
+
+	t = p->root;
+	if(t == nil) {
+		p->root = q;
+		return;
+	}
+
+	size = q->size;
+
+	tp = nil;
+	while(t != nil) {
+		if(size == t->size) {
+			q->prev = t->prev;
+			q->prev->fwd = q;
+			q->fwd = t;
+			t->prev = q;
+			return;
+		}
+		tp = t;
+		if(size < t->size)
+			t = t->left;
+		else
+			t = t->right;
+	}
+
+	q->parent = tp;
+	if(size < tp->size)
+		tp->left = q;
+	else
+		tp->right = q;
+}
+
+void
+poolsummary(void)
+{
+	int x = 0;
+	char buf[400];
+
+	print("\n");
+	print("    cursize     maxsize       hw         nalloc       nfree      nbrk       max      name\n");
+
+	x=poolread( buf, sizeof buf - 1, x );
+	buf[x] = 0;
+	putstrn(buf, x);
+	print("\n");
+}
+
+void*
+poolalloc(Pool *p, ulong asize)
+{
+	Bhdr *q, *t;
+	int alloc, ldr, ns, frag;
+	int osize, size;
+	Prog *prog;
+
+	if(asize >= 1024*1024*1024)	/* for sanity and to avoid overflow */
+		return nil;
+	if(p->cursize > p->ressize && (prog = currun()) != nil && prog->flags&Prestricted)
+		return nil;
+	size = asize;
+	osize = size;
+	size = (size + BHDRSIZE + p->quanta) & ~(p->quanta);
+
+	ilock(&p->l);
+	p->nalloc++;
+
+	t = p->root;
+	q = nil;
+	while(t) {
+		if(t->size == size) {
+			t = t->fwd;
+			pooldel(p, t);
+			t->magic = MAGIC_A;
+			p->cursize += t->size;
+			if(p->cursize > p->hw)
+				p->hw = p->cursize;
+			iunlock(&p->l);
+			return B2D(t);
+		}
+		if(size < t->size) {
+			q = t;
+			t = t->left;
+		}
+		else
+			t = t->right;
+	}
+	if(q != nil) {
+		pooldel(p, q);
+		q->magic = MAGIC_A;
+		frag = q->size - size;
+		if(frag < (size>>2) && frag < 0x8000) {
+			p->cursize += q->size;
+			if(p->cursize > p->hw)
+				p->hw = p->cursize;
+			iunlock(&p->l);
+			return B2D(q);
+		}
+		/* Split */
+		ns = q->size - size;
+		q->size = size;
+		B2T(q)->hdr = q;
+		t = B2NB(q);
+		t->size = ns;
+		B2T(t)->hdr = t;
+		pooladd(p, t);
+		p->cursize += q->size;
+		if(p->cursize > p->hw)
+			p->hw = p->cursize;
+		iunlock(&p->l);
+		return B2D(q);
+	}
+
+	ns = p->chunk;
+	if(size > ns)
+		ns = size;
+	ldr = p->quanta+1;
+
+	alloc = ns+ldr+ldr;
+	p->arenasize += alloc;
+	if(p->arenasize > p->maxsize) {
+		p->arenasize -= alloc;
+		ns = p->maxsize-p->arenasize-ldr-ldr;
+		ns &= ~p->quanta;
+		if (ns < size) {
+			if(poolcompact(p)) {
+				iunlock(&p->l);
+				return poolalloc(p, osize);
+			}
+
+			iunlock(&p->l);
+			if(p->warn)
+				return nil;
+			p->warn = 1;
+			if (p != mainmem || ns > 512)
+				print("arena too large: %s size %d cursize %lud arenasize %lud maxsize %lud, alloc = %d\n", p->name, osize, p->cursize, p->arenasize, p->maxsize, alloc);
+			return nil;
+		}
+		alloc = ns+ldr+ldr;
+		p->arenasize += alloc;
+	}
+
+	p->nbrk++;
+	t = xalloc(alloc);
+	if(t == nil) {
+		p->nbrk--;
+		iunlock(&p->l);
+		return nil;
+	}
+	/* Double alignment */
+	t = (Bhdr *)(((ulong)t + 7) & ~7);
+
+	/* TBS xmerge */
+	if(0 && p->chain != nil && (char*)t-(char*)B2LIMIT(p->chain)-ldr == 0){
+		/* can merge chains */
+		if(0)print("merging chains %p and %p in %s\n", p->chain, t, p->name);
+		q = B2LIMIT(p->chain);
+		q->magic = MAGIC_A;
+		q->size = alloc;
+		B2T(q)->hdr = q;
+		t = B2NB(q);
+		t->magic = MAGIC_E;
+		p->chain->csize += alloc;
+		p->cursize += alloc;
+		iunlock(&p->l);
+		poolfree(p, B2D(q));		/* for backward merge */
+		return poolalloc(p, osize);
+	}
+
+	t->magic = MAGIC_E;		/* Make a leader */
+	t->size = ldr;
+	t->csize = ns+ldr;
+	t->clink = p->chain;
+	p->chain = t;
+	B2T(t)->hdr = t;
+	t = B2NB(t);
+
+	t->magic = MAGIC_A;		/* Make the block we are going to return */
+	t->size = size;
+	B2T(t)->hdr = t;
+	q = t;
+
+	ns -= size;			/* Free the rest */
+	if(ns > 0) {
+		q = B2NB(t);
+		q->size = ns;
+		B2T(q)->hdr = q;
+		pooladd(p, q);
+	}
+	B2NB(q)->magic = MAGIC_E;	/* Mark the end of the chunk */
+
+	p->cursize += t->size;
+	if(p->cursize > p->hw)
+		p->hw = p->cursize;
+	iunlock(&p->l);
+	return B2D(t);
+}
+
+void
+poolfree(Pool *p, void *v)
+{
+	Bhdr *b, *c;
+	extern Bhdr *ptr;
+
+	D2B(b, v);
+
+	ilock(&p->l);
+	p->nfree++;
+	p->cursize -= b->size;
+
+	c = B2NB(b);
+	if(c->magic == MAGIC_F) {	/* Join forward */
+		if(c == ptr)
+			ptr = b;
+		pooldel(p, c);
+		c->magic = 0;
+		b->size += c->size;
+		B2T(b)->hdr = b;
+	}
+
+	c = B2PT(b)->hdr;
+	if(c->magic == MAGIC_F) {	/* Join backward */
+		if(b == ptr)
+			ptr = c;
+		pooldel(p, c);
+		b->magic = 0;
+		c->size += b->size;
+		b = c;
+		B2T(b)->hdr = b;
+	}
+
+	pooladd(p, b);
+	iunlock(&p->l);
+}
+
+void *
+poolrealloc(Pool *p, void *v, ulong size)
+{
+	Bhdr *b;
+	void *nv;
+	int osize;
+
+	if(size >= 1024*1024*1024)	/* for sanity and to avoid overflow */
+		return nil;
+	if(size == 0){
+		poolfree(p, v);
+		return nil;
+	}
+	SET(osize);
+	if(v != nil){
+		ilock(&p->l);
+		D2B(b, v);
+		osize = b->size - BHDRSIZE;
+		iunlock(&p->l);
+		if(osize >= size)
+			return v;
+	}
+	nv = poolalloc(p, size);
+	if(nv != nil && v != nil){
+		memmove(nv, v, osize);
+		poolfree(p, v);
+	}
+	return nv;
+}
+
+ulong
+poolmsize(Pool *p, void *v)
+{
+	Bhdr *b;
+	ulong size;
+
+	if(v == nil)
+		return 0;
+	ilock(&p->l);
+	D2B(b, v);
+	size = b->size - BHDRSIZE;
+	iunlock(&p->l);
+	return size;
+}
+
+static ulong
+poolmax(Pool *p)
+{
+	Bhdr *t;
+	ulong size;
+
+	ilock(&p->l);
+	size = p->maxsize - p->cursize;
+	t = p->root;
+	if(t != nil) {
+		while(t->right != nil)
+			t = t->right;
+		if(size < t->size)
+			size = t->size;
+	}
+	if(size >= BHDRSIZE)
+		size -= BHDRSIZE;
+	iunlock(&p->l);
+	return size;
+}
+
+int
+poolread(char *va, int count, ulong offset)
+{
+	Pool *p;
+	int n, i, signed_off;
+
+	n = 0;
+	signed_off = offset;
+	for(i = 0; i < table.n; i++) {
+		p = &table.pool[i];
+		n += snprint(va+n, count-n, "%11lud %11lud %11lud %11lud %11lud %11d %11lud %s\n",
+			p->cursize,
+			p->maxsize,
+			p->hw,
+			p->nalloc,
+			p->nfree,
+			p->nbrk,
+			poolmax(p),
+			p->name);
+
+		if(signed_off > 0) {
+			signed_off -= n;
+			if(signed_off < 0) {
+				memmove(va, va+n+signed_off, -signed_off);
+				n = -signed_off;
+			}
+			else
+				n = 0;
+		}
+
+	}
+	return n;
+}
+
+void*
+malloc(ulong size)
+{
+	void *v;
+
+	v = poolalloc(mainmem, size+Npadlong*sizeof(ulong));
+	if(v != nil){
+		if(Npadlong){
+			v = (ulong*)v+Npadlong;
+			setmalloctag(v, getcallerpc(&size));
+			setrealloctag(v, 0);
+		}
+		memset(v, 0, size);
+	}
+	return v;
+}
+
+void*
+smalloc(ulong size)
+{
+	void *v;
+
+	for(;;) {
+		v = poolalloc(mainmem, size+Npadlong*sizeof(ulong));
+		if(v != nil)
+			break;
+		tsleep(&up->sleep, return0, 0, 100);
+	}
+	if(Npadlong){
+		v = (ulong*)v+Npadlong;
+		setmalloctag(v, getcallerpc(&size));
+		setrealloctag(v, 0);
+	}
+	memset(v, 0, size);
+	return v;
+}
+
+void*
+mallocz(ulong size, int clr)
+{
+	void *v;
+
+	v = poolalloc(mainmem, size+Npadlong*sizeof(ulong));
+	if(v != nil){
+		if(Npadlong){
+			v = (ulong*)v+Npadlong;
+			setmalloctag(v, getcallerpc(&size));
+			setrealloctag(v, 0);
+		}
+		if(clr)
+			memset(v, 0, size);
+	}
+	return v;
+}
+
+void
+free(void *v)
+{
+	Bhdr *b;
+
+	if(v != nil) {
+		if(Npadlong)
+			v = (ulong*)v-Npadlong;
+		D2B(b, v);
+		poolfree(mainmem, v);
+	}
+}
+
+void*
+realloc(void *v, ulong size)
+{
+	void *nv;
+
+	if(size == 0)
+		return malloc(size);	/* temporary change until realloc calls can be checked */
+	if(v != nil)
+		v = (ulong*)v-Npadlong;
+	if(Npadlong!=0 && size!=0)
+		size += Npadlong*sizeof(ulong);
+	nv = poolrealloc(mainmem, v, size);
+	if(nv != nil) {
+		nv = (ulong*)nv+Npadlong;
+		setrealloctag(nv, getcallerpc(&v));
+		if(v == nil)
+			setmalloctag(v, getcallerpc(&v));
+	}
+	return nv;
+}
+
+void
+setmalloctag(void *v, ulong pc)
+{
+	ulong *u;
+
+	USED(v);
+	USED(pc);
+	if(Npadlong <= MallocOffset || v == nil)
+		return;
+	u = v;
+	u[-Npadlong+MallocOffset] = pc;
+}
+
+ulong
+getmalloctag(void *v)
+{
+	USED(v);
+	if(Npadlong <= MallocOffset)
+		return ~0;
+	return ((ulong*)v)[-Npadlong+MallocOffset];
+}
+
+void
+setrealloctag(void *v, ulong pc)
+{
+	ulong *u;
+
+	USED(v);
+	USED(pc);
+	if(Npadlong <= ReallocOffset || v == nil)
+		return;
+	u = v;
+	u[-Npadlong+ReallocOffset] = pc;
+}
+
+ulong
+getrealloctag(void *v)
+{
+	USED(v);
+	if(Npadlong <= ReallocOffset)
+		return ((ulong*)v)[-Npadlong+ReallocOffset];
+	return ~0;
+}
+
+ulong
+msize(void *v)
+{
+	if(v == nil)
+		return 0;
+	return poolmsize(mainmem, (ulong*)v-Npadlong)-Npadlong*sizeof(ulong);
+}
+
+void*
+calloc(ulong n, ulong szelem)
+{
+	return malloc(n*szelem);
+}
+
+void
+pooldump(Bhdr *b, int d, int c)
+{
+	Bhdr *t;
+
+	if(b == nil)
+		return;
+
+	print("%.8lux %.8lux %.8lux %c %4d %lud (f %.8lux p %.8lux)\n",
+		b, b->left, b->right, c, d, b->size, b->fwd, b->prev);
+	d++;
+	for(t = b->fwd; t != b; t = t->fwd)
+		print("\t%.8lux %.8lux %.8lux\n", t, t->prev, t->fwd);
+	pooldump(b->left, d, 'l');
+	pooldump(b->right, d, 'r');
+}
+
+void
+poolshow(void)
+{
+	int i;
+
+	for(i = 0; i < table.n; i++) {
+		print("Arena: %s root=%.8lux\n", table.pool[i].name, table.pool[i].root);
+		pooldump(table.pool[i].root, 0, 'R');
+	}
+}
+
+void
+poolsetcompact(Pool *p, void (*move)(void*, void*))
+{
+	p->move = move;
+}
+
+int
+poolcompact(Pool *pool)
+{
+	Bhdr *base, *limit, *ptr, *end, *next;
+	int compacted, nb;
+
+	if(pool->move == nil || pool->lastfree == pool->nfree)
+		return 0;
+
+	pool->lastfree = pool->nfree;
+
+	base = pool->chain;
+	ptr = B2NB(base);	/* First Block in arena has clink */
+	limit = B2LIMIT(base);
+	compacted = 0;
+
+	pool->root = nil;
+	end = ptr;
+	while(base != nil) {
+		next = B2NB(ptr);
+		if(ptr->magic == MAGIC_A || ptr->magic == MAGIC_I) {
+			if(ptr != end) {
+				memmove(end, ptr, ptr->size);
+				pool->move(B2D(ptr), B2D(end));
+				compacted = 1;
+			}
+			end = B2NB(end);
+		}
+		if(next >= limit) {
+			nb = (uchar*)limit - (uchar*)end;
+			if(nb > 0){
+				if(nb < pool->quanta+1)
+					panic("poolcompact: leftover too small\n");
+				end->size = nb;
+				B2T(end)->hdr = end;
+				pooladd(pool, end);
+			}
+			base = base->clink;
+			if(base == nil)
+				break;
+			ptr = B2NB(base);
+			end = ptr;	/* could do better by copying between chains */
+			limit = B2LIMIT(base);
+		} else
+			ptr = next;
+	}
+
+	return compacted;
+}
+
+void
+poolsize(Pool *p, int max, int contig)
+{
+	void *x;
+
+	p->maxsize = max;
+	if(max == 0)
+		p->ressize = max;
+	else if(max < RESERVED)
+		p->ressize = max;
+	else
+		p->ressize = max-RESERVED;
+	if (contig && max > 0) {
+		p->chunk = max-1024;
+		x = poolalloc(p, p->chunk);
+		if(x == nil)
+			panic("poolsize: don't have %d bytes\n", p->chunk);
+		poolfree(p, x);
+		p->hw = 0;
+	}
+}
+
+static void
+_poolfault(void *v, char *msg, ulong c)
+{
+	setpanic();
+	auditmemloc(msg, v);
+	panic("%s %lux (from %lux/%lux)", msg, v, getcallerpc(&v), c);
+}
+
+static void
+dumpvl(char *msg, ulong *v, int n)
+{
+	int i, l;
+
+	l = print("%s at %p: ", msg, v);
+	for(i = 0; i < n; i++) {
+		if(l >= 60) {
+			print("\n");
+			l = print("    %p: ", v);
+		}
+		l += print(" %lux", *v++);
+	}
+	print("\n");
+	USED(l);
+}
+
+static void
+corrupted(char *str, char *msg, Pool *p, Bhdr *b, void *v)
+{
+	print("%s(%p): pool %s CORRUPT: %s at %p'%lud(magic=%lux)\n",
+		str, v, p->name, msg, b, b->size, b->magic);
+	dumpvl("bad Bhdr", (ulong *)((ulong)b & ~3)-4, 10);
+}
+
+static void
+_auditmemloc(char *str, void *v)
+{
+	Pool *p;
+	Bhdr *bc, *ec, *b, *nb, *fb = nil;
+	char *fmsg, *msg;
+	ulong fsz;
+
+	SET(fsz, fmsg);
+	for (p = &table.pool[0]; p < &table.pool[nelem(table.pool)]; p++) {
+		ilock(&p->l);
+		for (bc = p->chain; bc != nil; bc = bc->clink) {
+			if (bc->magic != MAGIC_E) {
+				iunlock(&p->l);
+				corrupted(str, "chain hdr!=MAGIC_E", p, bc, v);
+				goto nextpool;
+			}
+			ec = B2LIMIT(bc);
+			if (((Bhdr*)v >= bc) && ((Bhdr*)v < ec))
+				goto found;
+		}
+		iunlock(&p->l);
+nextpool:	;
+	}
+	print("%s: %lux not in pools\n", str, v);
+	return;
+
+found:
+	for (b = bc; b < ec; b = nb) {
+		switch(b->magic) {
+		case MAGIC_F:
+			msg = "free blk";
+			break;
+		case MAGIC_I:
+			msg = "immutable block";
+			break;
+		case MAGIC_A:
+			msg = "block";
+			break;
+		default:
+			if (b == bc && b->magic == MAGIC_E) {
+				msg = "pool hdr";
+				break;
+			}
+			iunlock(&p->l);
+			corrupted(str, "bad magic", p, b, v);
+			goto badchunk;
+		}
+		if (b->size <= 0 || (b->size & p->quanta)) {
+			iunlock(&p->l);
+			corrupted(str, "bad size", p, b, v);
+			goto badchunk;
+		}
+		if (fb != nil)
+			break;
+		nb = B2NB(b);
+		if ((Bhdr*)v < nb) {
+			fb = b;
+			fsz = b->size;
+			fmsg = msg;
+		}
+	}
+	iunlock(&p->l);
+	if (b >= ec) {
+		if (b > ec)
+			corrupted(str, "chain size mismatch", p, b, v);
+		else if (b->magic != MAGIC_E)
+			corrupted(str, "chain end!=MAGIC_E", p, b, v);
+	}
+badchunk:
+	if (fb != nil) {
+		print("%s: %lux in %s:", str, v, p->name);
+		if (fb == v)
+			print(" is %s '%lux\n", fmsg, fsz);
+		else
+			print(" in %s at %lux'%lux\n", fmsg, fb, fsz);
+		dumpvl("area", (ulong *)((ulong)v & ~3)-4, 20);
+	}
+}
+
+char *
+poolaudit(char*(*audit)(int, Bhdr *))
+{
+	Pool *p;
+	Bhdr *bc, *ec, *b;
+	char *r = nil;
+
+	for (p = &table.pool[0]; p < &table.pool[nelem(table.pool)]; p++) {
+		ilock(&p->l);
+		for (bc = p->chain; bc != nil; bc = bc->clink) {
+			if (bc->magic != MAGIC_E) {
+				iunlock(&p->l);
+				return "bad chain hdr";
+			}
+			ec = B2LIMIT(bc);
+			for (b = bc; b < ec; b = B2NB(b)) {
+				if (b->size <= 0 || (b->size & p->quanta))
+					r = "bad size in bhdr";
+				else
+					switch(b->magic) {
+					case MAGIC_E:
+						if (b != bc) {
+							r = "unexpected MAGIC_E";
+							break;
+						}
+					case MAGIC_F:
+					case MAGIC_A:
+					case MAGIC_I:
+						r = audit(p->pnum, b);
+						break;
+					default:
+						r = "bad magic";
+					}
+				if (r != nil) {
+					iunlock(&p->l);
+					return r;
+				}
+			}
+			if (b != ec || b->magic != MAGIC_E) {
+				iunlock(&p->l);
+				return "bad chain ending";
+			}
+		}
+		iunlock(&p->l);
+	}
+	return r;
+}
+
+void
+poolinit(void)
+{
+	debugkey('m', "memory pools", poolsummary, 0);
+}
--- /dev/null
+++ b/os/port/allocb.c
@@ -1,0 +1,159 @@
+#include	"u.h"
+#include	"../port/lib.h"
+#include	"mem.h"
+#include	"dat.h"
+#include	"fns.h"
+#include	"../port/error.h"
+
+enum
+{
+	Hdrspc		= 64,		/* leave room for high-level headers */
+	Bdead		= 0x51494F42,	/* "QIOB" */
+};
+
+struct
+{
+	Lock;
+	ulong	bytes;
+} ialloc;
+
+/*
+ *  allocate blocks (round data base address to 64 bit boundary).
+ *  if mallocz gives us more than we asked for, leave room at the front
+ *  for header.
+ */
+Block*
+_allocb(int size)
+{
+	Block *b;
+	ulong addr;
+	int n;
+
+	b = mallocz(sizeof(Block)+size+Hdrspc+(BY2V-1), 0);
+	if(b == nil)
+		return nil;
+
+	b->next = nil;
+	b->list = nil;
+	b->free = nil;
+	b->flag = 0;
+
+	addr = (ulong)b;
+	addr = ROUND(addr + sizeof(Block), BY2V);
+	b->base = (uchar*)addr;
+	b->lim = ((uchar*)b) + msize(b);
+	b->rp = b->base;
+	n = b->lim - b->base - size;
+	b->rp += n & ~(BY2V-1);
+	b->wp = b->rp;
+
+	return b;
+}
+
+Block*
+allocb(int size)
+{
+	Block *b;
+
+	if(0 && up == nil)
+		panic("allocb outside process: %8.8lux", getcallerpc(&size));
+	b = _allocb(size);
+	if(b == 0)
+		exhausted("Blocks");
+	setmalloctag(b, getcallerpc(&size));
+	return b;
+}
+
+/*
+ *  interrupt time allocation
+ */
+Block*
+iallocb(int size)
+{
+	Block *b;
+
+	if(ialloc.bytes > conf.ialloc){
+		//print("iallocb: limited %lud/%lud\n", ialloc.bytes, conf.ialloc);
+		return nil;
+	}
+
+	b = _allocb(size);
+	if(b == nil){
+		//print("iallocb: no memory %lud/%lud\n", ialloc.bytes, conf.ialloc);
+		return nil;
+	}
+	setmalloctag(b, getcallerpc(&size));
+	b->flag = BINTR;
+
+	ilock(&ialloc);
+	ialloc.bytes += b->lim - b->base;
+	iunlock(&ialloc);
+
+	return b;
+}
+
+void
+freeb(Block *b)
+{
+	void *dead = (void*)Bdead;
+
+	if(b == nil)
+		return;
+
+	/*
+	 * drivers which perform non cache coherent DMA manage their own buffer
+	 * pool of uncached buffers and provide their own free routine.
+	 */
+	if(b->free) {
+		b->free(b);
+		return;
+	}
+	if(b->flag & BINTR) {
+		ilock(&ialloc);
+		ialloc.bytes -= b->lim - b->base;
+		iunlock(&ialloc);
+	}
+
+	/* poison the block in case someone is still holding onto it */
+	b->next = dead;
+	b->rp = dead;
+	b->wp = dead;
+	b->lim = dead;
+	b->base = dead;
+
+	free(b);
+}
+
+void
+checkb(Block *b, char *msg)
+{
+	void *dead = (void*)Bdead;
+
+	if(b == dead)
+		panic("checkb b %s %lux", msg, b);
+	if(b->base == dead || b->lim == dead || b->next == dead
+	  || b->rp == dead || b->wp == dead){
+		print("checkb: base 0x%8.8luX lim 0x%8.8luX next 0x%8.8luX\n",
+			b->base, b->lim, b->next);
+		print("checkb: rp 0x%8.8luX wp 0x%8.8luX\n", b->rp, b->wp);
+		panic("checkb dead: %s\n", msg);
+	}
+
+	if(b->base > b->lim)
+		panic("checkb 0 %s %lux %lux", msg, b->base, b->lim);
+	if(b->rp < b->base)
+		panic("checkb 1 %s %lux %lux", msg, b->base, b->rp);
+	if(b->wp < b->base)
+		panic("checkb 2 %s %lux %lux", msg, b->base, b->wp);
+	if(b->rp > b->lim)
+		panic("checkb 3 %s %lux %lux", msg, b->rp, b->lim);
+	if(b->wp > b->lim)
+		panic("checkb 4 %s %lux %lux", msg, b->wp, b->lim);
+
+}
+
+void
+iallocsummary(void)
+{
+	print("ialloc %lud/%lud\n", ialloc.bytes, conf.ialloc);
+}
--- /dev/null
+++ b/os/port/chan.c
@@ -1,0 +1,1431 @@
+#include	"u.h"
+#include	"../port/lib.h"
+#include	"mem.h"
+#include	"dat.h"
+#include	"fns.h"
+#include	"../port/error.h"
+
+char*
+channame(Chan *c)		/* DEBUGGING */
+{
+	if(c == nil)
+		return "<nil chan>";
+	if(c->name == nil)
+		return "<nil name>";
+	if(c->name->s == nil)
+		return "<nil name.s>";
+	return c->name->s;
+}
+
+enum
+{
+	CNAMESLOP	= 20
+};
+
+struct
+{
+	Lock;
+	int	fid;
+	Chan	*free;
+	Chan	*list;
+}chanalloc;
+
+typedef struct Elemlist Elemlist;
+
+struct Elemlist
+{
+	char	*name;	/* copy of name, so '/' can be overwritten */
+	int	nelems;
+	char	**elems;
+	int	*off;
+	int	mustbedir;
+};
+
+#define SEP(c) ((c) == 0 || (c) == '/')
+void cleancname(Cname*);
+
+int
+isdotdot(char *p)
+{
+	return p[0]=='.' && p[1]=='.' && p[2]=='\0';
+}
+
+int
+incref(Ref *r)
+{
+	int x;
+
+	lock(&r->l);
+	x = ++r->ref;
+	unlock(&r->l);
+	return x;
+}
+
+int
+decref(Ref *r)
+{
+	int x;
+
+	lock(&r->l);
+	x = --r->ref;
+	unlock(&r->l);
+	if(x < 0)
+		panic("decref, pc=0x%lux", getcallerpc(&r));
+
+	return x;
+}
+
+/*
+ * Rather than strncpy, which zeros the rest of the buffer, kstrcpy
+ * truncates if necessary, always zero terminates, does not zero fill,
+ * and puts ... at the end of the string if it's too long.  Usually used to
+ * save a string in up->genbuf;
+ */
+void
+kstrcpy(char *s, char *t, int ns)
+{
+	int nt;
+
+	nt = strlen(t);
+	if(nt+1 <= ns){
+		memmove(s, t, nt+1);
+		return;
+	}
+	/* too long */
+	if(ns < 4){
+		/* but very short! */
+		strncpy(s, t, ns);
+		return;
+	}
+	/* truncate with ... at character boundary (very rare case) */
+	memmove(s, t, ns-4);
+	ns -= 4;
+	s[ns] = '\0';
+	/* look for first byte of UTF-8 sequence by skipping continuation bytes */
+	while(ns>0 && (s[--ns]&0xC0)==0x80)
+		;
+	strcpy(s+ns, "...");
+}
+
+int
+emptystr(char *s)
+{
+	if(s == nil)
+		return 1;
+	if(s[0] == '\0')
+		return 1;
+	return 0;
+}
+
+/*
+ * Atomically replace *p with copy of s
+ */
+void
+kstrdup(char **p, char *s)
+{
+	int n;
+	char *t, *prev;
+
+	n = strlen(s)+1;
+	/* if it's a user, we can wait for memory; if not, something's very wrong */
+	if(up){
+		t = smalloc(n);
+		setmalloctag(t, getcallerpc(&p));
+	}else{
+		t = malloc(n);
+		if(t == nil)
+			panic("kstrdup: no memory");
+	}
+	memmove(t, s, n);
+	prev = *p;
+	*p = t;
+	free(prev);
+}
+
+void
+chandevreset(void)
+{
+	int i;
+
+	for(i=0; devtab[i] != nil; i++)
+		devtab[i]->reset();
+}
+
+void
+chandevinit(void)
+{
+	int i;
+
+	for(i=0; devtab[i] != nil; i++)
+		devtab[i]->init();
+}
+
+void
+chandevshutdown(void)
+{
+	int i;
+	
+	/* shutdown in reverse order */
+	for(i=0; devtab[i] != nil; i++)
+		;
+	for(i--; i >= 0; i--)
+		devtab[i]->shutdown();
+}
+
+Chan*
+newchan(void)
+{
+	Chan *c;
+
+	lock(&chanalloc);
+	c = chanalloc.free;
+	if(c != 0)
+		chanalloc.free = c->next;
+	unlock(&chanalloc);
+
+	if(c == nil) {
+		c = smalloc(sizeof(Chan));
+		lock(&chanalloc);
+		c->fid = ++chanalloc.fid;
+		c->link = chanalloc.list;
+		chanalloc.list = c;
+		unlock(&chanalloc);
+	}
+
+	/* if you get an error before associating with a dev,
+	   close calls rootclose, a nop */
+	c->type = 0;
+	c->flag = 0;
+	c->ref = 1;
+	c->dev = 0;
+	c->offset = 0;
+	c->iounit = 0;
+	c->umh = 0;
+	c->uri = 0;
+	c->dri = 0;
+	c->aux = 0;
+	c->mchan = 0;
+	c->mcp = 0;
+	c->mux = 0;
+	c->mqid.path = 0;
+	c->mqid.vers = 0;
+	c->mqid.type = 0;
+	c->name = 0;
+	return c;
+}
+
+static Ref ncname;
+
+Cname*
+newcname(char *s)
+{
+	Cname *n;
+	int i;
+
+	n = smalloc(sizeof(Cname));
+	i = strlen(s);
+	n->len = i;
+	n->alen = i+CNAMESLOP;
+	n->s = smalloc(n->alen);
+	memmove(n->s, s, i+1);
+	n->ref = 1;
+	incref(&ncname);
+	return n;
+}
+
+void
+cnameclose(Cname *n)
+{
+	if(n == nil)
+		return;
+	if(decref(n))
+		return;
+	decref(&ncname);
+	free(n->s);
+	free(n);
+}
+
+Cname*
+addelem(Cname *n, char *s)
+{
+	int i, a;
+	char *t;
+	Cname *new;
+
+	if(s[0]=='.' && s[1]=='\0')
+		return n;
+
+	if(n->ref > 1){
+		/* copy on write */
+		new = newcname(n->s);
+		cnameclose(n);
+		n = new;
+	}
+
+	i = strlen(s);
+	if(n->len+1+i+1 > n->alen){
+		a = n->len+1+i+1 + CNAMESLOP;
+		t = smalloc(a);
+		memmove(t, n->s, n->len+1);
+		free(n->s);
+		n->s = t;
+		n->alen = a;
+	}
+	if(n->len>0 && n->s[n->len-1]!='/' && s[0]!='/')	/* don't insert extra slash if one is present */
+		n->s[n->len++] = '/';
+	memmove(n->s+n->len, s, i+1);
+	n->len += i;
+	if(isdotdot(s))
+		cleancname(n);
+	return n;
+}
+
+void
+chanfree(Chan *c)
+{
+	c->flag = CFREE;
+
+	if(c->umh != nil){
+		putmhead(c->umh);
+		c->umh = nil;
+	}
+	if(c->umc != nil){
+		cclose(c->umc);
+		c->umc = nil;
+	}
+	if(c->mux != nil){
+		muxclose(c->mux);
+		c->mux = nil;
+	}
+	if(c->mchan != nil){
+		cclose(c->mchan);
+		c->mchan = nil;
+	}
+
+	cnameclose(c->name);
+
+	lock(&chanalloc);
+	c->next = chanalloc.free;
+	chanalloc.free = c;
+	unlock(&chanalloc);
+}
+
+void
+cclose(Chan *c)
+{
+	if(c == 0)
+		return;
+
+	if(c->flag&CFREE)
+		panic("cclose %lux", getcallerpc(&c));
+
+	if(decref(c))
+		return;
+
+	if(!waserror()){
+		devtab[c->type]->close(c);
+		poperror();
+	}
+	chanfree(c);
+}
+
+/*
+ * Make sure we have the only copy of c.  (Copy on write.)
+ */
+Chan*
+cunique(Chan *c)
+{
+	Chan *nc;
+
+	if(c->ref != 1) {
+		nc = cclone(c);
+		cclose(c);
+		c = nc;
+	}
+
+	return c;
+}
+
+int
+eqqid(Qid a, Qid b)
+{
+	return a.path==b.path && a.vers==b.vers;
+}
+
+int
+eqchan(Chan *a, Chan *b, int pathonly)
+{
+	if(a->qid.path != b->qid.path)
+		return 0;
+	if(!pathonly && a->qid.vers!=b->qid.vers)
+		return 0;
+	if(a->type != b->type)
+		return 0;
+	if(a->dev != b->dev)
+		return 0;
+	return 1;
+}
+
+int
+eqchantdqid(Chan *a, int type, int dev, Qid qid, int pathonly)
+{
+	if(a->qid.path != qid.path)
+		return 0;
+	if(!pathonly && a->qid.vers!=qid.vers)
+		return 0;
+	if(a->type != type)
+		return 0;
+	if(a->dev != dev)
+		return 0;
+	return 1;
+}
+
+Mhead*
+newmhead(Chan *from)
+{
+	Mhead *mh;
+
+	mh = smalloc(sizeof(Mhead));
+	mh->ref = 1;
+	mh->from = from;
+	incref(from);
+
+/*
+	n = from->name->len;
+	if(n >= sizeof(mh->fromname))
+		n = sizeof(mh->fromname)-1;
+	memmove(mh->fromname, from->name->s, n);
+	mh->fromname[n] = 0;
+*/
+	return mh;
+}
+
+int
+cmount(Chan *new, Chan *old, int flag, char *spec)
+{
+	Pgrp *pg;
+	int order, flg;
+	Mhead *m, **l, *mh;
+	Mount *nm, *f, *um, **h;
+
+	if(QTDIR & (old->qid.type^new->qid.type))
+		error(Emount);
+
+if(old->umh)
+	print("cmount old extra umh\n");
+
+	order = flag&MORDER;
+
+	if((old->qid.type&QTDIR)==0 && order != MREPL)
+		error(Emount);
+
+	mh = new->umh;
+
+	/*
+	 * Not allowed to bind when the old directory
+	 * is itself a union.  (Maybe it should be allowed, but I don't see
+	 * what the semantics would be.)
+	 *
+	 * We need to check mh->mount->next to tell unions apart from
+	 * simple mount points, so that things like
+	 *	mount -c fd /root
+	 *	bind -c /root /
+	 * work.  The check of mount->mflag catches things like
+	 *	mount fd /root
+	 *	bind -c /root /
+	 * 
+	 * This is far more complicated than it should be, but I don't
+	 * see an easier way at the moment.		-rsc
+	 */
+	if((flag&MCREATE) && mh && mh->mount
+	&& (mh->mount->next || !(mh->mount->mflag&MCREATE)))
+		error(Emount);
+
+	pg = up->env->pgrp;
+	wlock(&pg->ns);
+
+	l = &MOUNTH(pg, old->qid);
+	for(m = *l; m; m = m->hash) {
+		if(eqchan(m->from, old, 1))
+			break;
+		l = &m->hash;
+	}
+
+	if(m == nil) {
+		/*
+		 *  nothing mounted here yet.  create a mount
+		 *  head and add to the hash table.
+		 */
+		m = newmhead(old);
+		*l = m;
+
+		/*
+		 *  if this is a union mount, add the old
+		 *  node to the mount chain.
+		 */
+		if(order != MREPL)
+			m->mount = newmount(m, old, 0, 0);
+	}
+	wlock(&m->lock);
+	if(waserror()){
+		wunlock(&m->lock);
+		nexterror();
+	}
+	wunlock(&pg->ns);
+
+	nm = newmount(m, new, flag, spec);
+	if(mh != nil && mh->mount != nil) {
+		/*
+		 *  copy a union when binding it onto a directory
+		 */
+		flg = order;
+		if(order == MREPL)
+			flg = MAFTER;
+		h = &nm->next;
+		um = mh->mount;
+		for(um = um->next; um; um = um->next) {
+			f = newmount(m, um->to, flg, um->spec);
+			*h = f;
+			h = &f->next;
+		}
+	}
+
+	if(m->mount && order == MREPL) {
+		mountfree(m->mount);
+		m->mount = 0;
+	}
+
+	if(flag & MCREATE)
+		nm->mflag |= MCREATE;
+
+	if(m->mount && order == MAFTER) {
+		for(f = m->mount; f->next; f = f->next)
+			;
+		f->next = nm;
+	}
+	else {
+		for(f = nm; f->next; f = f->next)
+			;
+		f->next = m->mount;
+		m->mount = nm;
+	}
+
+	wunlock(&m->lock);
+	poperror();
+	return nm->mountid;
+}
+
+void
+cunmount(Chan *mnt, Chan *mounted)
+{
+	Pgrp *pg;
+	Mhead *m, **l;
+	Mount *f, **p;
+
+	if(mnt->umh)	/* should not happen */
+		print("cunmount newp extra umh %p has %p\n", mnt, mnt->umh);
+
+	/*
+	 * It _can_ happen that mounted->umh is non-nil, 
+	 * because mounted is the result of namec(Aopen)
+	 * (see sysfile.c:/^sysunmount).
+	 * If we open a union directory, it will have a umh.
+	 * Although surprising, this is okay, since the
+	 * cclose will take care of freeing the umh.
+	 */
+
+	pg = up->env->pgrp;
+	wlock(&pg->ns);
+
+	l = &MOUNTH(pg, mnt->qid);
+	for(m = *l; m; m = m->hash) {
+		if(eqchan(m->from, mnt, 1))
+			break;
+		l = &m->hash;
+	}
+
+	if(m == 0) {
+		wunlock(&pg->ns);
+		error(Eunmount);
+	}
+
+	wlock(&m->lock);
+	if(mounted == 0) {
+		*l = m->hash;
+		wunlock(&pg->ns);
+		mountfree(m->mount);
+		m->mount = nil;
+		cclose(m->from);
+		wunlock(&m->lock);
+		putmhead(m);
+		return;
+	}
+
+	p = &m->mount;
+	for(f = *p; f; f = f->next) {
+		/* BUG: Needs to be 2 pass */
+		if(eqchan(f->to, mounted, 1) ||
+		  (f->to->mchan && eqchan(f->to->mchan, mounted, 1))) {
+			*p = f->next;
+			f->next = 0;
+			mountfree(f);
+			if(m->mount == nil) {
+				*l = m->hash;
+				cclose(m->from);
+				wunlock(&m->lock);
+				wunlock(&pg->ns);
+				putmhead(m);
+				return;
+			}
+			wunlock(&m->lock);
+			wunlock(&pg->ns);
+			return;
+		}
+		p = &f->next;
+	}
+	wunlock(&m->lock);
+	wunlock(&pg->ns);
+	error(Eunion);
+}
+
+Chan*
+cclone(Chan *c)
+{
+	Chan *nc;
+	Walkqid *wq;
+
+	wq = devtab[c->type]->walk(c, nil, nil, 0);
+	if(wq == nil)
+		error("clone failed");
+	nc = wq->clone;
+	free(wq);
+	nc->name = c->name;
+	if(c->name)
+		incref(c->name);
+	return nc;
+}
+
+int
+findmount(Chan **cp, Mhead **mp, int type, int dev, Qid qid)
+{
+	Pgrp *pg;
+	Mhead *m;
+
+	pg = up->env->pgrp;
+	rlock(&pg->ns);
+	for(m = MOUNTH(pg, qid); m; m = m->hash){
+		rlock(&m->lock);
+if(m->from == nil){
+	print("m %p m->from 0\n", m);
+	runlock(&m->lock);
+	continue;
+}
+		if(eqchantdqid(m->from, type, dev, qid, 1)) {
+			runlock(&pg->ns);
+			if(mp != nil){
+				incref(m);
+				if(*mp != nil)
+					putmhead(*mp);
+				*mp = m;
+			}
+			if(*cp != nil)
+				cclose(*cp);
+			incref(m->mount->to);
+			*cp = m->mount->to;
+			runlock(&m->lock);
+			return 1;
+		}
+		runlock(&m->lock);
+	}
+
+	runlock(&pg->ns);
+	return 0;
+}
+
+int
+domount(Chan **cp, Mhead **mp)
+{
+	return findmount(cp, mp, (*cp)->type, (*cp)->dev, (*cp)->qid);
+}
+
+Chan*
+undomount(Chan *c, Cname *name)
+{
+	Chan *nc;
+	Pgrp *pg;
+	Mount *t;
+	Mhead **h, **he, *f;
+
+	pg = up->env->pgrp;
+	rlock(&pg->ns);
+	if(waserror()) {
+		runlock(&pg->ns);
+		nexterror();
+	}
+
+	he = &pg->mnthash[MNTHASH];
+	for(h = pg->mnthash; h < he; h++) {
+		for(f = *h; f; f = f->hash) {
+			if(strcmp(f->from->name->s, name->s) != 0)
+				continue;
+			for(t = f->mount; t; t = t->next) {
+				if(eqchan(c, t->to, 1)) {
+					/*
+					 * We want to come out on the left hand side of the mount
+					 * point using the element of the union that we entered on.
+					 * To do this, find the element that has a from name of
+					 * c->name->s.
+					 */
+					if(strcmp(t->head->from->name->s, name->s) != 0)
+						continue;
+					nc = t->head->from;
+					incref(nc);
+					cclose(c);
+					c = nc;
+					break;
+				}
+			}
+		}
+	}
+	poperror();
+	runlock(&pg->ns);
+	return c;
+}
+
+/*
+ * Either walks all the way or not at all.  No partial results in *cp.
+ * *nerror is the number of names to display in an error message.
+ */
+static char Edoesnotexist[] = "does not exist";
+int
+walk(Chan **cp, char **names, int nnames, int nomount, int *nerror)
+{
+	int dev, dotdot, i, n, nhave, ntry, type;
+	Chan *c, *nc;
+	Cname *cname;
+	Mount *f;
+	Mhead *mh, *nmh;
+	Walkqid *wq;
+
+	c = *cp;
+	incref(c);
+	cname = c->name;
+	incref(cname);
+	mh = nil;
+
+	/*
+	 * While we haven't gotten all the way down the path:
+	 *    1. step through a mount point, if any
+	 *    2. send a walk request for initial dotdot or initial prefix without dotdot
+	 *    3. move to the first mountpoint along the way.
+	 *    4. repeat.
+	 *
+	 * An invariant is that each time through the loop, c is on the undomount
+	 * side of the mount point, and c's name is cname.
+	 */
+	for(nhave=0; nhave<nnames; nhave+=n){
+		if((c->qid.type&QTDIR)==0){
+			if(nerror)
+				*nerror = nhave;
+			cnameclose(cname);
+			cclose(c);
+			strcpy(up->env->errstr, Enotdir);
+			if(mh != nil)
+				putmhead(mh);
+			return -1;
+		}
+		ntry = nnames - nhave;
+		if(ntry > MAXWELEM)
+			ntry = MAXWELEM;
+		dotdot = 0;
+		for(i=0; i<ntry; i++){
+			if(isdotdot(names[nhave+i])){
+				if(i==0) {
+					dotdot = 1;
+					ntry = 1;
+				} else
+					ntry = i;
+				break;
+			}
+		}
+
+		if(!dotdot && !nomount)
+			domount(&c, &mh);
+
+		type = c->type;
+		dev = c->dev;
+
+		if((wq = devtab[type]->walk(c, nil, names+nhave, ntry)) == nil){
+			/* try a union mount, if any */
+			if(mh && !nomount){
+				/*
+				 * mh->mount == c, so start at mh->mount->next
+				 */
+				rlock(&mh->lock);
+				for(f = mh->mount->next; f; f = f->next)
+					if((wq = devtab[f->to->type]->walk(f->to, nil, names+nhave, ntry)) != nil)
+						break;
+				runlock(&mh->lock);
+				if(f != nil){
+					type = f->to->type;
+					dev = f->to->dev;
+				}
+			}
+			if(wq == nil){
+				cclose(c);
+				cnameclose(cname);
+				if(nerror)
+					*nerror = nhave+1;
+				if(mh != nil)
+					putmhead(mh);
+				return -1;
+			}
+		}
+
+		nmh = nil;
+		if(dotdot) {
+			assert(wq->nqid == 1);
+			assert(wq->clone != nil);
+
+			cname = addelem(cname, "..");
+			nc = undomount(wq->clone, cname);
+			n = 1;
+		} else {
+			nc = nil;
+			if(!nomount)
+				for(i=0; i<wq->nqid && i<ntry-1; i++)
+					if(findmount(&nc, &nmh, type, dev, wq->qid[i]))
+						break;
+			if(nc == nil){	/* no mount points along path */
+				if(wq->clone == nil){
+					cclose(c);
+					cnameclose(cname);
+					if(wq->nqid==0 || (wq->qid[wq->nqid-1].type&QTDIR)){
+						if(nerror)
+							*nerror = nhave+wq->nqid+1;
+						strcpy(up->env->errstr, Edoesnotexist);
+					}else{
+						if(nerror)
+							*nerror = nhave+wq->nqid;
+						strcpy(up->env->errstr, Enotdir);
+					}
+					free(wq);
+					if(mh != nil)
+						putmhead(mh);
+					return -1;
+				}
+				n = wq->nqid;
+				nc = wq->clone;
+			}else{		/* stopped early, at a mount point */
+				if(wq->clone != nil){
+					cclose(wq->clone);
+					wq->clone = nil;
+				}
+				n = i+1;
+			}
+			for(i=0; i<n; i++)
+				cname = addelem(cname, names[nhave+i]);
+		}
+		cclose(c);
+		c = nc;
+		putmhead(mh);
+		mh = nmh;
+		free(wq);
+	}
+
+	putmhead(mh);
+
+	c = cunique(c);
+
+	if(c->umh != nil){	//BUG
+		print("walk umh\n");
+		putmhead(c->umh);
+		c->umh = nil;
+	}
+
+	cnameclose(c->name);
+	c->name = cname;
+
+	cclose(*cp);
+	*cp = c;
+	if(nerror)
+		*nerror = 0;
+	return 0;
+}
+
+/*
+ * c is a mounted non-creatable directory.  find a creatable one.
+ */
+Chan*
+createdir(Chan *c, Mhead *m)
+{
+	Chan *nc;
+	Mount *f;
+
+	rlock(&m->lock);
+	if(waserror()) {
+		runlock(&m->lock);
+		nexterror();
+	}
+	for(f = m->mount; f; f = f->next) {
+		if(f->mflag&MCREATE) {
+			nc = cclone(f->to);
+			runlock(&m->lock);
+			poperror();
+			cclose(c);
+			return nc;
+		}
+	}
+	error(Enocreate);
+	return 0;
+}
+
+void
+saveregisters(void)
+{
+}
+
+/*
+ * In place, rewrite name to compress multiple /, eliminate ., and process ..
+ */
+void
+cleancname(Cname *n)
+{
+	char *p;
+
+	if(n->s[0] == '#'){
+		p = strchr(n->s, '/');
+		if(p == nil)
+			return;
+		cleanname(p);
+
+		/*
+		 * The correct name is #i rather than #i/,
+		 * but the correct name of #/ is #/.
+		 */
+		if(strcmp(p, "/")==0 && n->s[1] != '/')
+			*p = '\0';
+	}else
+		cleanname(n->s);
+	n->len = strlen(n->s);
+}
+
+static void
+growparse(Elemlist *e)
+{
+	char **new;
+	int *inew;
+	enum { Delta = 8 };
+
+	if(e->nelems % Delta == 0){
+		new = smalloc((e->nelems+Delta) * sizeof(char*));
+		memmove(new, e->elems, e->nelems*sizeof(char*));
+		free(e->elems);
+		e->elems = new;
+		inew = smalloc((e->nelems+Delta+1) * sizeof(int));
+		memmove(inew, e->off, e->nelems*sizeof(int));
+		free(e->off);
+		e->off = inew;
+	}
+}
+
+/*
+ * The name is known to be valid.
+ * Copy the name so slashes can be overwritten.
+ * An empty string will set nelem=0.
+ * A path ending in / or /. or /.//./ etc. will have
+ * e.mustbedir = 1, so that we correctly
+ * reject, e.g., "/adm/users/." when /adm/users is a file
+ * rather than a directory.
+ */
+static void
+parsename(char *name, Elemlist *e)
+{
+	char *slash;
+
+	kstrdup(&e->name, name);
+	name = e->name;
+	e->nelems = 0;
+	e->elems = nil;
+	e->off = smalloc(sizeof(int));
+	e->off[0] = skipslash(name) - name;
+	for(;;){
+		name = skipslash(name);
+		if(*name=='\0'){
+			e->mustbedir = 1;
+			break;
+		}
+		growparse(e);
+		
+		e->elems[e->nelems++] = name;
+		slash = utfrune(name, '/');
+		if(slash == nil){
+			e->off[e->nelems] = name+strlen(name) - e->name;
+			e->mustbedir = 0;
+			break;
+		}
+		e->off[e->nelems] = slash - e->name;
+		*slash++ = '\0';
+		name = slash;
+	}
+}
+
+void*
+memrchr(void *va, int c, long n)
+{
+	uchar *a, *e;
+
+	a = va;
+	for(e=a+n-1; e>a; e--)
+		if(*e == c)
+			return e;
+	return nil;
+}
+
+/*
+ * Turn a name into a channel.
+ * &name[0] is known to be a valid address.  It may be a kernel address.
+ *
+ * Opening with amode Aopen, Acreate, or Aremove guarantees
+ * that the result will be the only reference to that particular fid.
+ * This is necessary since we might pass the result to
+ * devtab[]->remove().
+ *
+ * Opening Atodir, Amount, or Aaccess does not guarantee this.
+ *
+ * Opening Aaccess can, under certain conditions, return a
+ * correct Chan* but with an incorrect Cname attached.
+ * Since the functions that open Aaccess (sysstat, syswstat, sys_stat)
+ * do not use the Cname*, this avoids an unnecessary clone.
+ */
+Chan*
+namec(char *aname, int amode, int omode, ulong perm)
+{
+	int n, prefix, len, t, nomount, npath;
+	Chan *c, *cnew;
+	Cname *cname;
+	Elemlist e;
+	Rune r;
+	Mhead *m;
+	char *createerr, tmperrbuf[ERRMAX];
+	char *name;
+
+	name = aname;
+	if(name[0] == '\0')
+		error("empty file name");
+	validname(name, 1);
+
+	/*
+	 * Find the starting off point (the current slash, the root of
+	 * a device tree, or the current dot) as well as the name to
+	 * evaluate starting there.
+	 */
+	nomount = 0;
+	switch(name[0]){
+	case '/':
+		c = up->env->pgrp->slash;
+		incref(c);
+		break;
+	
+	case '#':
+		nomount = 1;
+		up->genbuf[0] = '\0';
+		n = 0;
+		while(*name!='\0' && (*name != '/' || n < 2)){
+			if(n >= sizeof(up->genbuf)-1)
+				error(Efilename);
+			up->genbuf[n++] = *name++;
+		}
+		up->genbuf[n] = '\0';
+		n = chartorune(&r, up->genbuf+1)+1;
+		if(r == 'M')
+			error(Enoattach);
+		/*
+		 *  the nodevs exceptions are
+		 *	|  it only gives access to pipes you create
+		 *	e  this process's environment
+		 *	s  private file2chan creation space
+		 *	D private secure sockets name space
+		 *	a private TLS name space
+		 */
+		if(up->env->pgrp->nodevs &&
+		   (utfrune("|esDa", r) == nil || r == 's' && up->genbuf[n]!='\0'))
+			error(Enoattach);
+		t = devno(r, 1);
+		if(t == -1)
+			error(Ebadsharp);
+		c = devtab[t]->attach(up->genbuf+n);
+		break;
+
+	default:
+		c = up->env->pgrp->dot;
+		incref(c);
+		break;
+	}
+	prefix = name - aname;
+
+	e.name = nil;
+	e.elems = nil;
+	e.off = nil;
+	e.nelems = 0;
+	if(waserror()){
+		cclose(c);
+		free(e.name);
+		free(e.elems);
+		free(e.off);
+//dumpmount();
+		nexterror();
+	}
+
+	/*
+	 * Build a list of elements in the path.
+	 */
+	parsename(name, &e);
+
+	/*
+	 * On create, ....
+	 */
+	if(amode == Acreate){
+		/* perm must have DMDIR if last element is / or /. */
+		if(e.mustbedir && !(perm&DMDIR)){
+			npath = e.nelems;
+			strcpy(tmperrbuf, "create without DMDIR");
+			goto NameError;
+		}
+
+		/* don't try to walk the last path element just yet. */
+		if(e.nelems == 0)
+			error(Eexist);
+		e.nelems--;
+	}
+
+	if(walk(&c, e.elems, e.nelems, nomount, &npath) < 0){
+		if(npath < 0 || npath > e.nelems){
+			print("namec %s walk error npath=%d\n", aname, npath);
+			nexterror();
+		}
+		strcpy(tmperrbuf, up->env->errstr);
+	NameError:
+		len = prefix+e.off[npath];
+		if(len < ERRMAX/3 || (name=memrchr(aname, '/', len))==nil || name==aname)
+			snprint(up->genbuf, sizeof up->genbuf, "%.*s", len, aname);
+		else
+			snprint(up->genbuf, sizeof up->genbuf, "...%.*s", (int)(len-(name-aname)), name);
+		snprint(up->env->errstr, ERRMAX, "%#q %s", up->genbuf, tmperrbuf);
+		nexterror();
+	}
+
+	if(e.mustbedir && !(c->qid.type&QTDIR)){
+		npath = e.nelems;
+		strcpy(tmperrbuf, "not a directory");
+		goto NameError;
+	}
+
+	if(amode == Aopen && (omode&3) == OEXEC && (c->qid.type&QTDIR)){
+		npath = e.nelems;
+		error("cannot exec directory");
+	}
+
+	switch(amode){
+	case Aaccess:
+		if(!nomount)
+			domount(&c, nil);
+		break;
+
+	case Abind:
+		m = nil;
+		if(!nomount)
+			domount(&c, &m);
+		if(c->umh != nil)
+			putmhead(c->umh);
+		c->umh = m;
+		break;
+
+	case Aremove:
+	case Aopen:
+	Open:
+		/* save the name; domount might change c */
+		cname = c->name;
+		incref(cname);
+		m = nil;
+		if(!nomount)
+			domount(&c, &m);
+
+		/* our own copy to open or remove */
+		c = cunique(c);
+
+		/* now it's our copy anyway, we can put the name back */
+		cnameclose(c->name);
+		c->name = cname;
+
+		switch(amode){
+		case Aremove:
+			putmhead(m);
+			break;
+
+		case Aopen:
+		case Acreate:
+if(c->umh != nil){
+	print("cunique umh\n");
+	putmhead(c->umh);
+	c->umh = nil;
+}
+
+			/* only save the mount head if it's a multiple element union */
+			if(m && m->mount && m->mount->next)
+				c->umh = m;
+			else
+				putmhead(m);
+
+			/* save registers else error() in open has wrong value of c saved */
+			saveregisters();
+
+			if(omode == OEXEC)
+				c->flag &= ~CCACHE;
+
+			c = devtab[c->type]->open(c, omode&~OCEXEC);
+
+			if(omode & OCEXEC)
+				c->flag |= CCEXEC;
+			if(omode & ORCLOSE)
+				c->flag |= CRCLOSE;
+			break;
+		}
+		break;
+
+	case Atodir:
+		/*
+		 * Directories (e.g. for cd) are left before the mount point,
+		 * so one may mount on / or . and see the effect.
+		 */
+		if(!(c->qid.type & QTDIR))
+			error(Enotdir);
+		break;
+
+	case Amount:
+		/*
+		 * When mounting on an already mounted upon directory,
+		 * one wants subsequent mounts to be attached to the
+		 * original directory, not the replacement.  Don't domount.
+		 */
+		break;
+
+	case Acreate:
+		/*
+		 * We've already walked all but the last element.
+		 * If the last exists, try to open it OTRUNC.
+		 * If omode&OEXCL is set, just give up.
+		 */
+		e.nelems++;
+		if(walk(&c, e.elems+e.nelems-1, 1, nomount, nil) == 0){
+			if(omode&OEXCL)
+				error(Eexist);
+			omode |= OTRUNC;
+			goto Open;
+		}
+
+		/*
+		 * The semantics of the create(2) system call are that if the
+		 * file exists and can be written, it is to be opened with truncation.
+		 * On the other hand, the create(5) message fails if the file exists.
+		 * If we get two create(2) calls happening simultaneously, 
+		 * they might both get here and send create(5) messages, but only 
+		 * one of the messages will succeed.  To provide the expected create(2)
+		 * semantics, the call with the failed message needs to try the above
+		 * walk again, opening for truncation.  This correctly solves the 
+		 * create/create race, in the sense that any observable outcome can
+		 * be explained as one happening before the other.
+		 * The create/create race is quite common.  For example, it happens
+		 * when two rc subshells simultaneously update the same
+		 * environment variable.
+		 *
+		 * The implementation still admits a create/create/remove race:
+		 * (A) walk to file, fails
+		 * (B) walk to file, fails
+		 * (A) create file, succeeds, returns 
+		 * (B) create file, fails
+		 * (A) remove file, succeeds, returns
+		 * (B) walk to file, return failure.
+		 *
+		 * This is hardly as common as the create/create race, and is really
+		 * not too much worse than what might happen if (B) got a hold of a
+		 * file descriptor and then the file was removed -- either way (B) can't do
+		 * anything with the result of the create call.  So we don't care about this race.
+		 *
+		 * Applications that care about more fine-grained decision of the races
+		 * can use the OEXCL flag to get at the underlying create(5) semantics;
+		 * by default we provide the common case.
+		 *
+		 * We need to stay behind the mount point in case we
+		 * need to do the first walk again (should the create fail).
+		 *
+		 * We also need to cross the mount point and find the directory
+		 * in the union in which we should be creating.
+		 *
+		 * The channel staying behind is c, the one moving forward is cnew.
+		 */
+		m = nil;
+		cnew = nil;	/* is this assignment necessary? */
+		if(!waserror()){	/* try create */
+			if(!nomount && findmount(&cnew, &m, c->type, c->dev, c->qid))
+				cnew = createdir(cnew, m);
+			else{
+				cnew = c;
+				incref(cnew);
+			}
+
+			/*
+			 * We need our own copy of the Chan because we're
+			 * about to send a create, which will move it.  Once we have
+			 * our own copy, we can fix the name, which might be wrong
+			 * if findmount gave us a new Chan.
+			 */
+			cnew = cunique(cnew);
+			cnameclose(cnew->name);
+			cnew->name = c->name;
+			incref(cnew->name);
+
+			devtab[cnew->type]->create(cnew, e.elems[e.nelems-1], omode&~(OEXCL|OCEXEC), perm);
+			poperror();
+			if(omode & OCEXEC)
+				cnew->flag |= CCEXEC;
+			if(omode & ORCLOSE)
+				cnew->flag |= CRCLOSE;
+			if(m)
+				putmhead(m);
+			cclose(c);
+			c = cnew;
+			c->name = addelem(c->name, e.elems[e.nelems-1]);
+			break;
+		}
+
+		/* create failed */
+		cclose(cnew);
+		if(m)
+			putmhead(m);
+		if(omode & OEXCL)
+			nexterror();
+		/* save error */
+		createerr = up->env->errstr;
+		up->env->errstr = tmperrbuf;
+		/* note: we depend that walk does not error */
+		if(walk(&c, e.elems+e.nelems-1, 1, nomount, nil) < 0){
+			up->env->errstr = createerr;
+			error(createerr);	/* report true error */
+		}
+		up->env->errstr = createerr;
+		omode |= OTRUNC;
+		goto Open;
+
+	default:
+		panic("unknown namec access %d\n", amode);
+	}
+
+	poperror();
+
+	/* place final element in genbuf for e.g. exec */
+	if(e.nelems > 0)
+		kstrcpy(up->genbuf, e.elems[e.nelems-1], sizeof up->genbuf);
+	else
+		kstrcpy(up->genbuf, ".", sizeof up->genbuf);
+	free(e.name);
+	free(e.elems);
+	free(e.off);
+
+	return c;
+}
+
+/*
+ * name is valid. skip leading / and ./ as much as possible
+ */
+char*
+skipslash(char *name)
+{
+	while(name[0]=='/' || (name[0]=='.' && (name[1]==0 || name[1]=='/')))
+		name++;
+	return name;
+}
+
+char isfrog[256]={
+	/*NUL*/	1, 1, 1, 1, 1, 1, 1, 1,
+	/*BKS*/	1, 1, 1, 1, 1, 1, 1, 1,
+	/*DLE*/	1, 1, 1, 1, 1, 1, 1, 1,
+	/*CAN*/	1, 1, 1, 1, 1, 1, 1, 1,
+	['/']	1,
+	[0x7f]	1,
+};
+
+/*
+ * Check that the name
+ *  a) is in valid memory.
+ *  b) is shorter than 2^16 bytes, so it can fit in a 9P string field.
+ *  c) contains no frogs.
+ * The first byte is known to be addressible by the requester, so the
+ * routine works for kernel and user memory both.
+ * The parameter slashok flags whether a slash character is an error
+ * or a valid character.
+ */
+void
+validname(char *aname, int slashok)
+{
+	char *ename, *name;
+	int c;
+	Rune r;
+
+	name = aname;
+	ename = memchr(name, 0, (1<<16));
+
+	if(ename==nil || ename-name>=(1<<16))
+		error("name too long");
+
+	while(*name){
+		/* all characters above '~' are ok */
+		c = *(uchar*)name;
+		if(c >= Runeself)
+			name += chartorune(&r, name);
+		else{
+			if(isfrog[c])
+				if(!slashok || c!='/'){
+					snprint(up->genbuf, sizeof(up->genbuf), "%s: %q", Ebadchar, aname);
+					error(up->genbuf);
+			}
+			name++;
+		}
+	}
+}
+
+void
+isdir(Chan *c)
+{
+	if(c->qid.type & QTDIR)
+		return;
+	error(Enotdir);
+}
+
+/*
+ * This is necessary because there are many
+ * pointers to the top of a given mount list:
+ *
+ *	- the mhead in the namespace hash table
+ *	- the mhead in chans returned from findmount:
+ *	  used in namec and then by unionread.
+ *	- the mhead in chans returned from createdir:
+ *	  used in the open/create race protect, which is gone.
+ *
+ * The RWlock in the Mhead protects the mount list it contains.
+ * The mount list is deleted when we cunmount.
+ * The RWlock ensures that nothing is using the mount list at that time.
+ *
+ * It is okay to replace c->mh with whatever you want as 
+ * long as you are sure you have a unique reference to it.
+ *
+ * This comment might belong somewhere else.
+ */
+void
+putmhead(Mhead *m)
+{
+	if(m && decref(m) == 0){
+		m->mount = (Mount*)0xCafeBeef;
+		free(m);
+	}
+}
--- /dev/null
+++ b/os/port/cis.c
@@ -1,0 +1,539 @@
+#include "u.h"
+#include "../port/lib.h"
+#include "mem.h"
+#include "dat.h"
+#include "fns.h"
+#include "../port/error.h"
+#include "io.h"
+
+enum{
+	Linktarget = 0x13,
+};
+	
+/*
+ *  read and crack the card information structure enough to set
+ *  important parameters like power
+ */
+/* cis memory walking */
+typedef struct Cisdat {
+	uchar	*cisbase;
+	int	cispos;
+	int	cisskip;
+	int	cislen;
+} Cisdat;
+
+static void	tcfig(PCMslot*, Cisdat*, int);
+static void	tentry(PCMslot*, Cisdat*, int);
+static void	tvers1(PCMslot*, Cisdat*, int);
+static void	tlonglnkmfc(PCMslot*, Cisdat*, int);
+
+static int
+readc(Cisdat *cis, uchar *x)
+{
+	if(cis->cispos >= cis->cislen)
+		return 0;
+	*x = cis->cisbase[cis->cisskip*cis->cispos];
+	cis->cispos++;
+	return 1;
+}
+
+static int
+xcistuple(int slotno, int tuple, int subtuple, void *v, int nv, int attr)
+{
+	PCMmap *m;
+	Cisdat cis;
+	int i, l;
+	uchar *p;
+	uchar type, link, n, c;
+	int this, subtype;
+
+	m = pcmmap(slotno, 0, 0, attr);
+	if(m == 0)
+		return -1;
+
+	cis.cisbase = KADDR(m->isa);
+	cis.cispos = 0;
+	cis.cisskip = attr ? 2 : 1;
+	cis.cislen = m->len;
+
+	/* loop through all the tuples */
+	for(i = 0; i < 1000; i++){
+		this = cis.cispos;
+		if(readc(&cis, &type) != 1)
+			break;
+		if(type == 0xFF)
+			break;
+		if(readc(&cis, &link) != 1)
+			break;
+		if(link == 0xFF)
+			break;
+
+		n = link;
+		if(link > 1 && subtuple != -1){
+			if(readc(&cis, &c) != 1)
+				break;
+			subtype = c;
+			n--;
+		}else
+			subtype = -1;
+
+		if(type == tuple && subtype == subtuple){
+			p = v;
+			for(l=0; l<nv && l<n; l++)
+				if(readc(&cis, p++) != 1)
+					break;
+			pcmunmap(slotno, m);
+			return nv;
+		}
+		cis.cispos = this + (2+link);
+	}
+	pcmunmap(slotno, m);
+	return -1;
+}
+
+int
+pcmcistuple(int slotno, int tuple, int subtuple, void *v, int nv)
+{
+	int n;
+
+	/* try attribute space, then memory */
+	if((n = xcistuple(slotno, tuple, subtuple, v, nv, 1)) >= 0)
+		return n;
+	return xcistuple(slotno, tuple, subtuple, v, nv, 0);
+}
+
+void
+pcmcisread(PCMslot *pp)
+{
+	int this;
+	Cisdat cis;
+	PCMmap *m;
+	uchar type, link;
+
+	memset(pp->ctab, 0, sizeof(pp->ctab));
+	pp->ncfg = 0;
+	memset(pp->cfg, 0, sizeof(pp->cfg));
+	pp->configed = 0;
+	pp->nctab = 0;
+	pp->verstr[0] = 0;
+
+	/*
+	 * Read all tuples in attribute space.
+	 */
+	m = pcmmap(pp->slotno, 0, 0, 1);
+	if(m == 0)
+		return;
+
+	cis.cisbase = KADDR(m->isa);
+	cis.cispos = 0;
+	cis.cisskip = 2;
+	cis.cislen = m->len;
+
+	/* loop through all the tuples */
+	for(;;){
+		this = cis.cispos;
+		if(readc(&cis, &type) != 1)
+			break;
+		if(type == 0xFF)
+			break;
+		if(readc(&cis, &link) != 1)
+			break;
+
+		switch(type){
+		default:
+			break;
+		case 6:
+			tlonglnkmfc(pp, &cis, type);
+			break;
+		case 0x15:
+			tvers1(pp, &cis, type);
+			break;
+		case 0x1A:
+			tcfig(pp, &cis, type);
+			break;
+		case 0x1B:
+			tentry(pp, &cis, type);
+			break;
+		}
+
+		if(link == 0xFF)
+			break;
+		cis.cispos = this + (2+link);
+	}
+	pcmunmap(pp->slotno, m);
+}
+
+static ulong
+getlong(Cisdat *cis, int size)
+{
+	uchar c;
+	int i;
+	ulong x;
+
+	x = 0;
+	for(i = 0; i < size; i++){
+		if(readc(cis, &c) != 1)
+			break;
+		x |= c<<(i*8);
+	}
+	return x;
+}
+
+static void
+tcfig(PCMslot *pp, Cisdat *cis, int )
+{
+	uchar size, rasize, rmsize;
+	uchar last;
+
+	if(readc(cis, &size) != 1)
+		return;
+	rasize = (size&0x3) + 1;
+	rmsize = ((size>>2)&0xf) + 1;
+	if(readc(cis, &last) != 1)
+		return;
+
+	if(pp->ncfg >= 8){
+		print("tcfig: too many configuration registers\n");
+		return;
+	}
+	
+	pp->cfg[pp->ncfg].caddr = getlong(cis, rasize);
+	pp->cfg[pp->ncfg].cpresent = getlong(cis, rmsize);
+	pp->ncfg++;
+}
+
+static ulong vexp[8] =
+{
+	1, 10, 100, 1000, 10000, 100000, 1000000, 10000000
+};
+static ulong vmant[16] =
+{
+	10, 12, 13, 15, 20, 25, 30, 35, 40, 45, 50, 55, 60, 70, 80, 90,
+};
+
+static ulong
+microvolt(Cisdat *cis)
+{
+	uchar c;
+	ulong microvolts;
+	ulong exp;
+
+	if(readc(cis, &c) != 1)
+		return 0;
+	exp = vexp[c&0x7];
+	microvolts = vmant[(c>>3)&0xf]*exp;
+	while(c & 0x80){
+		if(readc(cis, &c) != 1)
+			return 0;
+		switch(c){
+		case 0x7d:
+			break;		/* high impedence when sleeping */
+		case 0x7e:
+		case 0x7f:
+			microvolts = 0;	/* no connection */
+			break;
+		default:
+			exp /= 10;
+			microvolts += exp*(c&0x7f);
+		}
+	}
+	return microvolts;
+}
+
+static ulong
+nanoamps(Cisdat *cis)
+{
+	uchar c;
+	ulong nanoamps;
+
+	if(readc(cis, &c) != 1)
+		return 0;
+	nanoamps = vexp[c&0x7]*vmant[(c>>3)&0xf];
+	while(c & 0x80){
+		if(readc(cis, &c) != 1)
+			return 0;
+		if(c == 0x7d || c == 0x7e || c == 0x7f)
+			nanoamps = 0;
+	}
+	return nanoamps;
+}
+
+/*
+ * only nominal voltage (feature 1) is important for config,
+ * other features must read card to stay in sync.
+ */
+static ulong
+power(Cisdat *cis)
+{
+	uchar feature;
+	ulong mv;
+
+	mv = 0;
+	if(readc(cis, &feature) != 1)
+		return 0;
+	if(feature & 1)
+		mv = microvolt(cis);
+	if(feature & 2)
+		microvolt(cis);
+	if(feature & 4)
+		microvolt(cis);
+	if(feature & 8)
+		nanoamps(cis);
+	if(feature & 0x10)
+		nanoamps(cis);
+	if(feature & 0x20)
+		nanoamps(cis);
+	if(feature & 0x40)
+		nanoamps(cis);
+	return mv/1000000;
+}
+
+static ulong mantissa[16] =
+{ 0, 10, 12, 13, 15, 20, 25, 30, 35, 40, 45, 50, 55, 60, 70, 80, };
+
+static ulong exponent[8] =
+{ 1, 10, 100, 1000, 10000, 100000, 1000000, 10000000, };
+
+static ulong
+ttiming(Cisdat *cis, int scale)
+{
+	uchar unscaled;
+	ulong nanosecs;
+
+	if(readc(cis, &unscaled) != 1)
+		return 0;
+	nanosecs = (mantissa[(unscaled>>3)&0xf]*exponent[unscaled&7])/10;
+	nanosecs = nanosecs * vexp[scale];
+	return nanosecs;
+}
+
+static void
+timing(Cisdat *cis, PCMconftab *ct)
+{
+	uchar c, i;
+
+	if(readc(cis, &c) != 1)
+		return;
+	i = c&0x3;
+	if(i != 3)
+		ct->maxwait = ttiming(cis, i);		/* max wait */
+	i = (c>>2)&0x7;
+	if(i != 7)
+		ct->readywait = ttiming(cis, i);		/* max ready/busy wait */
+	i = (c>>5)&0x7;
+	if(i != 7)
+		ct->otherwait = ttiming(cis, i);		/* reserved wait */
+}
+
+static void
+iospaces(Cisdat *cis, PCMconftab *ct)
+{
+	uchar c;
+	int i, nio;
+
+	ct->nio = 0;
+	if(readc(cis, &c) != 1)
+		return;
+
+	ct->bit16 = ((c>>5)&3) >= 2;
+	if(!(c & 0x80)){
+		ct->io[0].start = 0;
+		ct->io[0].len = 1<<(c&0x1f);
+		ct->nio = 1;
+		return;
+	}
+
+	if(readc(cis, &c) != 1)
+		return;
+
+	/*
+	 * For each of the range descriptions read the
+	 * start address and the length (value is length-1).
+	 */
+	nio = (c&0xf)+1;
+	for(i = 0; i < nio; i++){
+		ct->io[i].start = getlong(cis, (c>>4)&0x3);
+		ct->io[i].len = getlong(cis, (c>>6)&0x3)+1;
+	}
+	ct->nio = nio;
+}
+
+static void
+irq(Cisdat *cis, PCMconftab *ct)
+{
+	uchar c;
+
+	if(readc(cis, &c) != 1)
+		return;
+	ct->irqtype = c & 0xe0;
+	if(c & 0x10)
+		ct->irqs = getlong(cis, 2);
+	else
+		ct->irqs = 1<<(c&0xf);
+	ct->irqs &= 0xDEB8;		/* levels available to card */
+}
+
+static void
+memspace(Cisdat *cis, int asize, int lsize, int host)
+{
+	ulong haddress, address, len;
+
+	len = getlong(cis, lsize)*256;
+	address = getlong(cis, asize)*256;
+	USED(len, address);
+	if(host){
+		haddress = getlong(cis, asize)*256;
+		USED(haddress);
+	}
+}
+
+static void
+tentry(PCMslot *pp, Cisdat *cis, int )
+{
+	uchar c, i, feature;
+	PCMconftab *ct;
+
+	if(pp->nctab >= nelem(pp->ctab))
+		return;
+	if(readc(cis, &c) != 1)
+		return;
+	ct = &pp->ctab[pp->nctab++];
+
+	/* copy from last default config */
+	if(pp->def)
+		*ct = *pp->def;
+
+	ct->index = c & 0x3f;
+
+	/* is this the new default? */
+	if(c & 0x40)
+		pp->def = ct;
+
+	/* memory wait specified? */
+	if(c & 0x80){
+		if(readc(cis, &i) != 1)
+			return;
+		if(i&0x80)
+			ct->memwait = 1;
+	}
+
+	if(readc(cis, &feature) != 1)
+		return;
+	switch(feature&0x3){
+	case 1:
+		ct->vpp1 = ct->vpp2 = power(cis);
+		break;
+	case 2:
+		power(cis);
+		ct->vpp1 = ct->vpp2 = power(cis);
+		break;
+	case 3:
+		power(cis);
+		ct->vpp1 = power(cis);
+		ct->vpp2 = power(cis);
+		break;
+	default:
+		break;
+	}
+	if(feature&0x4)
+		timing(cis, ct);
+	if(feature&0x8)
+		iospaces(cis, ct);
+	if(feature&0x10)
+		irq(cis, ct);
+	switch((feature>>5)&0x3){
+	case 1:
+		memspace(cis, 0, 2, 0);
+		break;
+	case 2:
+		memspace(cis, 2, 2, 0);
+		break;
+	case 3:
+		if(readc(cis, &c) != 1)
+			return;
+		for(i = 0; i <= (c&0x7); i++)
+			memspace(cis, (c>>5)&0x3, (c>>3)&0x3, c&0x80);
+		break;
+	}
+	pp->configed++;
+}
+
+static void
+tvers1(PCMslot *pp, Cisdat *cis, int )
+{
+	uchar c, major, minor, last;
+	int  i;
+
+	if(readc(cis, &major) != 1)
+		return;
+	if(readc(cis, &minor) != 1)
+		return;
+	last = 0;
+	for(i = 0; i < sizeof(pp->verstr)-1; i++){
+		if(readc(cis, &c) != 1)
+			return;
+		if(c == 0)
+			c = ';';
+		if(c == '\n')
+			c = ';';
+		if(c == 0xff)
+			break;
+		if(c == ';' && last == ';')
+			continue;
+		pp->verstr[i] = c;
+		last = c;
+	}
+	pp->verstr[i] = 0;
+}
+
+static void
+tlonglnkmfc(PCMslot *pp, Cisdat *cis, int)
+{
+	int i, npos, opos;
+	uchar nfn, space, expect, type, this, link;
+
+	readc(cis, &nfn);
+	for(i = 0; i < nfn; i++){
+		readc(cis, &space);
+		npos        = getlong(cis, 4);
+		opos        = cis->cispos;
+		cis->cispos = npos;
+		expect      = Linktarget;
+
+		while(1){
+			this = cis->cispos;
+			if(readc(cis, &type) != 1)
+				break;
+			if(type == 0xFF)
+				break;
+			if(readc(cis, &link) != 1)
+				break;
+
+			if(expect && expect != type){
+				print("tlonglnkmfc: expected %X found %X\n",
+					expect, type);
+				break;
+			}
+			expect = 0;
+
+			switch(type){
+			default:
+				break;
+			case 0x15:
+				tvers1(pp, cis, type);
+				break;
+			case 0x1A:
+				tcfig(pp, cis, type);
+				break;
+			case 0x1B:
+				tentry(pp, cis, type);
+				break;
+			}
+
+			if(link == 0xFF)
+				break;
+			cis->cispos = this + (2+link);
+		}
+		cis->cispos = opos;
+	}
+}
--- /dev/null
+++ b/os/port/dev.c
@@ -1,0 +1,432 @@
+#include	"u.h"
+#include	"../port/lib.h"
+#include	"mem.h"
+#include	"dat.h"
+#include	"fns.h"
+#include	"../port/error.h"
+
+extern ulong	kerndate;
+
+void
+mkqid(Qid *q, vlong path, ulong vers, int type)
+{
+	q->type = type;
+	q->vers = vers;
+	q->path = path;
+}
+
+int
+devno(int c, int user)
+{
+	int i;
+
+	for(i = 0; devtab[i] != nil; i++) {
+		if(devtab[i]->dc == c)
+			return i;
+	}
+	if(user == 0)
+		panic("devno %C 0x%ux", c, c);
+
+	return -1;
+}
+
+void
+devdir(Chan *c, Qid qid, char *n, vlong length, char *user, long perm, Dir *db)
+{
+	db->name = n;
+	if(c->flag&CMSG)
+		qid.type |= QTMOUNT;
+	db->qid = qid;
+	db->type = devtab[c->type]->dc;
+	db->dev = c->dev;
+	db->mode = perm;
+	db->mode |= qid.type << 24;
+	db->atime = seconds();
+	db->mtime = kerndate;
+	db->length = length;
+	db->uid = user;
+	db->gid = eve;
+	db->muid = user;
+}
+
+/*
+ * the zeroth element of the table MUST be the directory itself for ..
+*/
+int
+devgen(Chan *c, char*, Dirtab *tab, int ntab, int i, Dir *dp)
+{
+	if(tab == 0)
+		return -1;
+	if(i != DEVDOTDOT){
+		/* skip over the first element, that for . itself */
+		i++;
+		if(i >= ntab)
+			return -1;
+		tab += i;
+	}
+	devdir(c, tab->qid, tab->name, tab->length, eve, tab->perm, dp);
+	return 1;
+}
+
+void
+devreset(void)
+{
+}
+
+void
+devinit(void)
+{
+}
+
+void
+devshutdown(void)
+{
+}
+
+Chan*
+devattach(int tc, char *spec)
+{
+	Chan *c;
+	char *buf;
+
+	c = newchan();
+	mkqid(&c->qid, 0, 0, QTDIR);
+	c->type = devno(tc, 0);
+	if(spec == nil)
+		spec = "";
+	buf = smalloc(4+strlen(spec)+1);
+	sprint(buf, "#%C%s", tc, spec);
+	c->name = newcname(buf);
+	free(buf);
+	return c;
+}
+
+
+Chan*
+devclone(Chan *c)
+{
+	Chan *nc;
+
+	if(c->flag & COPEN)
+		panic("clone of open file type %C\n", devtab[c->type]->dc);
+
+	nc = newchan();
+
+	nc->type = c->type;
+	nc->dev = c->dev;
+	nc->mode = c->mode;
+	nc->qid = c->qid;
+	nc->offset = c->offset;
+	nc->umh = nil;
+	nc->mountid = c->mountid;
+	nc->aux = c->aux;
+	nc->mqid = c->mqid;
+	nc->mcp = c->mcp;
+	return nc;
+}
+
+Walkqid*
+devwalk(Chan *c, Chan *nc, char **name, int nname, Dirtab *tab, int ntab, Devgen *gen)
+{
+	int i, j, alloc;
+	Walkqid *wq;
+	char *n;
+	Dir dir;
+
+	if(nname > 0)
+		isdir(c);
+
+	alloc = 0;
+	wq = smalloc(sizeof(Walkqid)+(nname-1)*sizeof(Qid));
+	if(waserror()){
+		if(alloc && wq->clone!=nil)
+			cclose(wq->clone);
+		free(wq);
+		return nil;
+	}
+	if(nc == nil){
+		nc = devclone(c);
+		nc->type = 0;	/* device doesn't know about this channel yet */
+		alloc = 1;
+	}
+	wq->clone = nc;
+
+	for(j=0; j<nname; j++){
+		if(!(nc->qid.type&QTDIR)){
+			if(j==0)
+				error(Enotdir);
+			goto Done;
+		}
+		n = name[j];
+		if(strcmp(n, ".") == 0){
+    Accept:
+			wq->qid[wq->nqid++] = nc->qid;
+			continue;
+		}
+		if(strcmp(n, "..") == 0){
+			(*gen)(nc, nil, tab, ntab, DEVDOTDOT, &dir);
+			nc->qid = dir.qid;
+			goto Accept;
+		}
+		/*
+		 * Ugly problem: If we're using devgen, make sure we're
+		 * walking the directory itself, represented by the first
+		 * entry in the table, and not trying to step into a sub-
+		 * directory of the table, e.g. /net/net. Devgen itself
+		 * should take care of the problem, but it doesn't have
+		 * the necessary information (that we're doing a walk).
+		 */
+		if(gen==devgen && nc->qid.path!=tab[0].qid.path)
+			goto Notfound;
+		for(i=0;; i++) {
+			switch((*gen)(nc, n, tab, ntab, i, &dir)){
+			case -1:
+			Notfound:
+				if(j == 0)
+					error(Enonexist);
+				kstrcpy(up->env->errstr, Enonexist, ERRMAX);
+				goto Done;
+			case 0:
+				continue;
+			case 1:
+				if(strcmp(n, dir.name) == 0){
+					nc->qid = dir.qid;
+					goto Accept;
+				}
+				continue;
+			}
+		}
+	}
+	/*
+	 * We processed at least one name, so will return some data.
+	 * If we didn't process all nname entries succesfully, we drop
+	 * the cloned channel and return just the Qids of the walks.
+	 */
+Done:
+	poperror();
+	if(wq->nqid < nname){
+		if(alloc)
+			cclose(wq->clone);
+		wq->clone = nil;
+	}else if(wq->clone){
+		/* attach cloned channel to same device */
+		wq->clone->type = c->type;
+	}
+	return wq;
+}
+
+int
+devstat(Chan *c, uchar *db, int n, Dirtab *tab, int ntab, Devgen *gen)
+{
+	int i;
+	Dir dir;
+	char *p, *elem;
+
+	for(i=0;; i++)
+		switch((*gen)(c, nil, tab, ntab, i, &dir)){
+		case -1:
+			if(c->qid.type & QTDIR){
+				if(c->name == nil)
+					elem = "???";
+				else if(strcmp(c->name->s, "/") == 0)
+					elem = "/";
+				else
+					for(elem=p=c->name->s; *p; p++)
+						if(*p == '/')
+							elem = p+1;
+				devdir(c, c->qid, elem, 0, eve, DMDIR|0555, &dir);
+				n = convD2M(&dir, db, n);
+				if(n == 0)
+					error(Ebadarg);
+				return n;
+			}
+			print("%s %s: devstat %C %llux\n",
+				up->text, up->env->user,
+				devtab[c->type]->dc, c->qid.path);
+
+			error(Enonexist);
+		case 0:
+			break;
+		case 1:
+			if(c->qid.path == dir.qid.path) {
+				if(c->flag&CMSG)
+					dir.mode |= DMMOUNT;
+				n = convD2M(&dir, db, n);
+				if(n == 0)
+					error(Ebadarg);
+				return n;
+			}
+			break;
+		}
+}
+
+long
+devdirread(Chan *c, char *d, long n, Dirtab *tab, int ntab, Devgen *gen)
+{
+	long m, dsz;
+	struct{
+		Dir;
+		char slop[100];
+	}dir;
+
+	for(m=0; m<n; c->dri++) {
+		switch((*gen)(c, nil, tab, ntab, c->dri, &dir)){
+		case -1:
+			return m;
+
+		case 0:
+			break;
+
+		case 1:
+			dsz = convD2M(&dir, (uchar*)d, n-m);
+			if(dsz <= BIT16SZ){	/* <= not < because this isn't stat; read is stuck */
+				if(m == 0)
+					error(Eshort);
+				return m;
+			}
+			m += dsz;
+			d += dsz;
+			break;
+		}
+	}
+
+	return m;
+}
+
+/*
+ * error(Eperm) if open permission not granted for up->env->user.
+ */
+void
+devpermcheck(char *fileuid, ulong perm, int omode)
+{
+	ulong t;
+	static int access[] = { 0400, 0200, 0600, 0100 };
+
+	if(strcmp(up->env->user, fileuid) == 0)
+		perm <<= 0;
+	else
+	if(strcmp(up->env->user, eve) == 0)
+		perm <<= 3;
+	else
+		perm <<= 6;
+
+	t = access[omode&3];
+	if((t&perm) != t)
+		error(Eperm);
+}
+
+Chan*
+devopen(Chan *c, int omode, Dirtab *tab, int ntab, Devgen *gen)
+{
+	int i;
+	Dir dir;
+
+	for(i=0;; i++) {
+		switch((*gen)(c, nil, tab, ntab, i, &dir)){
+		case -1:
+			goto Return;
+		case 0:
+			break;
+		case 1:
+			if(c->qid.path == dir.qid.path) {
+				devpermcheck(dir.uid, dir.mode, omode);
+				goto Return;
+			}
+			break;
+		}
+	}
+Return:
+	c->offset = 0;
+	if((c->qid.type&QTDIR) && omode!=OREAD)
+		error(Eperm);
+	c->mode = openmode(omode);
+	c->flag |= COPEN;
+	return c;
+}
+
+void
+devcreate(Chan*, char*, int, ulong)
+{
+	error(Eperm);
+}
+
+Block*
+devbread(Chan *c, long n, ulong offset)
+{
+	Block *bp;
+
+	bp = allocb(n);
+	if(bp == 0)
+		error(Enomem);
+	if(waserror()) {
+		freeb(bp);
+		nexterror();
+	}
+	bp->wp += devtab[c->type]->read(c, bp->wp, n, offset);
+	poperror();
+	return bp;
+}
+
+long
+devbwrite(Chan *c, Block *bp, ulong offset)
+{
+	long n;
+
+	if(waserror()) {
+		freeb(bp);
+		nexterror();
+	}
+	n = devtab[c->type]->write(c, bp->rp, BLEN(bp), offset);
+	poperror();
+	freeb(bp);
+
+	return n;
+}
+
+void
+devremove(Chan*)
+{
+	error(Eperm);
+}
+
+int
+devwstat(Chan*, uchar*, int)
+{
+	error(Eperm);
+	return 0;
+}
+
+void
+devpower(int)
+{
+	error(Eperm);
+}
+
+int
+devconfig(int, char *, DevConf *)
+{
+	error(Eperm);
+	return 0;
+}
+
+/*
+ * check that the name in a wstat is plausible
+ */
+void
+validwstatname(char *name)
+{
+	validname(name, 0);
+	if(strcmp(name, ".") == 0 || strcmp(name, "..") == 0)
+		error(Efilename);
+}
+
+Dev*
+devbyname(char *name)
+{
+	int i;
+
+	for(i = 0; devtab[i] != nil; i++)
+		if(strcmp(devtab[i]->name, name) == 0)
+			return devtab[i];
+	return nil;
+}
--- /dev/null
+++ b/os/port/devXXX.c
@@ -1,0 +1,147 @@
+/*
+ *  template for making a new device
+ */
+#include	"u.h"
+#include	"../port/lib.h"
+#include	"mem.h"
+#include	"dat.h"
+#include	"fns.h"
+#include	"../port/error.h"
+
+
+enum{
+	Qdir,
+	Qdata,
+};
+
+static
+Dirtab XXXtab[]={
+	".",			{Qdir, 0, QTDIR},	0,	0555,	/* entry for "." must be first if devgen used */
+	"data",		{Qdata, 0},	0,	0666,
+};
+
+static void
+XXXreset(void)						/* default in dev.c */
+{
+}
+
+static void
+XXXinit(void)						/* default in dev.c */
+{
+}
+
+static Chan*
+XXXattach(char* spec)
+{
+	return devattach('X', spec);
+}
+
+static Walkqid*
+XXXwalk(Chan *c, Chan *nc, char **name, int nname)
+{
+	return devwalk(c, nc, name, nname, XXXtab, nelem(XXXtab), devgen);
+}
+
+static int
+XXXstat(Chan* c, uchar *db, int n)
+{
+	return devstat(c, db, n, XXXtab, nelem(XXXtab), devgen);
+}
+
+static Chan*
+XXXopen(Chan* c, int omode)
+{
+	return devopen(c, omode, XXXtab, nelem(XXXtab), devgen);
+}
+
+static void
+XXXcreate(Chan* c, char* name, int omode, ulong perm)	/* default in dev.c */
+{
+	USED(c, name, omode, perm);
+	error(Eperm);
+}
+
+static void
+XXXremove(Chan* c)					/* default in dev.c */
+{
+	USED(c);
+	error(Eperm);
+}
+
+static int
+XXXwstat(Chan* c, uchar *dp, int n)				/* default in dev.c */
+{
+	USED(c, dp);
+	error(Eperm);
+	return n;
+}
+
+static void
+XXXclose(Chan* c)
+{
+	USED(c);
+}
+
+static long
+XXXread(Chan* c, void* a, long n, vlong offset)
+{
+	USED(offset);
+
+	switch((ulong)c->qid.path){
+	case Qdir:
+		return devdirread(c, a, n, XXXtab, nelem(XXXtab), devgen);
+	case Qdata:
+		break;
+	default:
+		n=0;
+		break;
+	}
+	return n;
+}
+
+static Block*
+XXXbread(Chan* c, long n, ulong offset)			/* default in dev.c */
+{
+	return devbread(c, n, offset);
+}
+
+static long
+XXXwrite(Chan* c, void* a, long n, vlong offset)
+{
+	USED(a, offset);
+
+	switch((ulong)c->qid.path){
+	case Qdata:
+		break;
+	default:
+		error(Ebadusefd);
+	}
+	return n;
+}
+
+static long
+XXXbwrite(Chan* c, Block* bp, ulong offset)		/* default in dev.c */
+{
+	return devbwrite(c, bp, offset);
+}
+
+Dev XXXdevtab = {					/* defaults in dev.c */
+	'X',
+	"XXX",
+
+	XXXreset,					/* devreset */
+	XXXinit,					/* devinit */
+	devshutdown,
+	XXXattach,
+	XXXwalk,
+	XXXstat,
+	XXXopen,
+	XXXcreate,					/* devcreate */
+	XXXclose,
+	XXXread,
+	XXXbread,					/* devbread */
+	XXXwrite,
+	XXXbwrite,					/* devbwrite */
+	XXXremove,					/* devremove */
+	XXXwstat,					/* devwstat */
+};
--- /dev/null
+++ b/os/port/devaudio.c
@@ -1,0 +1,947 @@
+/*
+ *	SB 16 driver
+ */
+#include	"u.h"
+#include	"../port/lib.h"
+#include	"mem.h"
+#include	"dat.h"
+#include	"fns.h"
+#include	"../port/error.h"
+#include	"io.h"
+#include	"audio.h"
+
+typedef struct	AQueue	AQueue;
+typedef struct	Buf	Buf;
+
+enum
+{
+	Qdir		= 0,
+	Qaudio,
+	Qvolume,
+
+	Fmono		= 1,
+	Fin		= 2,
+	Fout		= 4,
+
+	Aclosed		= 0,
+	Aread,
+	Awrite,
+
+	Vaudio		= 0,
+	Vsynth,
+	Vcd,
+	Vline,
+	Vmic,
+	Vspeaker,
+	Vtreb,
+	Vbass,
+	Vspeed,
+	Nvol,
+
+	Speed		= 44100,
+	Ncmd		= 50,		/* max volume command words */
+};
+
+static
+Dirtab audiodir[] =
+{
+	".",		{Qdir, 0, QTDIR},	0,	0555,
+	"audio",	{Qaudio},		0,	0666,
+	"volume",	{Qvolume},		0,	0666,
+};
+
+struct	Buf
+{
+	uchar*	virt;
+	ulong	phys;
+	Buf*	next;
+};
+struct	AQueue
+{
+	Lock;
+	Buf*	first;
+	Buf*	last;
+};
+static	struct
+{
+	QLock;
+	Rendez	vous;
+	int	bufinit;	/* boolean if buffers allocated */
+	int	curcount;	/* how much data in current buffer */
+	int	active;		/* boolean dma running */
+	int	intr;		/* boolean an interrupt has happened */
+	int	amode;		/* Aclosed/Aread/Awrite for /audio */
+	int	rivol[Nvol];		/* right/left input/output volumes */
+	int	livol[Nvol];
+	int	rovol[Nvol];
+	int	lovol[Nvol];
+	int	major;		/* SB16 major version number (sb 4) */
+	int	minor;		/* SB16 minor version number */
+
+	Buf	buf[Nbuf];	/* buffers and queues */
+	AQueue	empty;
+	AQueue	full;
+	Buf*	current;
+	Buf*	filling;
+} audio;
+
+static	struct
+{
+	char*	name;
+	int	flag;
+	int	ilval;		/* initial values */
+	int	irval;
+} volumes[] =
+{
+[Vaudio]	"audio",	Fout, 		50,	50,
+[Vsynth]	"synth",	Fin|Fout,	0,	0,
+[Vcd]		"cd",		Fin|Fout,	0,	0,
+[Vline]		"line",		Fin|Fout,	0,	0,
+[Vmic]		"mic",		Fin|Fout|Fmono,	0,	0,
+[Vspeaker]	"speaker",	Fout|Fmono,	0,	0,
+
+[Vtreb]		"treb",		Fout, 		50,	50,
+[Vbass]		"bass",		Fout, 		50,	50,
+
+[Vspeed]	"speed",	Fin|Fout|Fmono,	Speed,	Speed,
+		0
+};
+
+static struct
+{
+	Lock;
+	int	reset;		/* io ports to the sound blaster */
+	int	read;
+	int	write;
+	int	wstatus;
+	int	rstatus;
+	int	mixaddr;
+	int	mixdata;
+	int	clri8;
+	int	clri16;
+	int	clri401;
+	int	dma;
+} blaster;
+
+static	void	swab(uchar*);
+
+static	char	Emajor[]	= "soundblaster not responding/wrong version";
+static	char	Emode[]		= "illegal open mode";
+static	char	Evolume[]	= "illegal volume specifier";
+
+static	int
+sbcmd(int val)
+{
+	int i, s;
+
+	for(i=1<<16; i!=0; i--) {
+		s = inb(blaster.wstatus);
+		if((s & 0x80) == 0) {
+			outb(blaster.write, val);
+			return 0;
+		}
+	}
+/*	print("#A: sbcmd (#%.2x) timeout\n", val);	/**/
+	return 1;
+}
+
+static	int
+sbread(void)
+{
+	int i, s;
+
+	for(i=1<<16; i!=0; i--) {
+		s = inb(blaster.rstatus);
+		if((s & 0x80) != 0) {
+			return inb(blaster.read);
+		}
+	}
+/*	print("#A: sbread did not respond\n");	/**/
+	return 0xbb;
+}
+
+static	int
+mxcmd(int addr, int val)
+{
+
+	outb(blaster.mixaddr, addr);
+	outb(blaster.mixdata, val);
+	return 1;
+}
+
+static	int
+mxread(int addr)
+{
+	int s;
+
+	outb(blaster.mixaddr, addr);
+	s = inb(blaster.mixdata);
+	return s;
+}
+
+static	void
+mxcmds(int s, int v)
+{
+
+	if(v > 100)
+		v = 100;
+	if(v < 0)
+		v = 0;
+	mxcmd(s, (v*255)/100);
+}
+
+static	void
+mxcmdt(int s, int v)
+{
+
+	if(v > 100)
+		v = 100;
+	if(v <= 0)
+		mxcmd(s, 0);
+	else
+		mxcmd(s, 255-100+v);
+}
+
+static	void
+mxcmdu(int s, int v)
+{
+
+	if(v > 100)
+		v = 100;
+	if(v <= 0)
+		v = 0;
+	mxcmd(s, 128-50+v);
+}
+
+static	void
+mxvolume(void)
+{
+	int *left, *right;
+	int source;
+
+	if(audio.amode == Aread){
+		left = audio.livol;
+		right = audio.rivol;
+	}else{
+		left = audio.lovol;
+		right = audio.rovol;
+	}
+
+	ilock(&blaster);
+
+	mxcmd(0x30, 255);		/* left master */
+	mxcmd(0x31, 255);		/* right master */
+	mxcmd(0x3f, 0);		/* left igain */
+	mxcmd(0x40, 0);		/* right igain */
+	mxcmd(0x41, 0);		/* left ogain */
+	mxcmd(0x42, 0);		/* right ogain */
+
+	mxcmds(0x32, left[Vaudio]);
+	mxcmds(0x33, right[Vaudio]);
+
+	mxcmds(0x34, left[Vsynth]);
+	mxcmds(0x35, right[Vsynth]);
+
+	mxcmds(0x36, left[Vcd]);
+	mxcmds(0x37, right[Vcd]);
+
+	mxcmds(0x38, left[Vline]);
+	mxcmds(0x39, right[Vline]);
+
+	mxcmds(0x3a, left[Vmic]);
+	mxcmds(0x3b, left[Vspeaker]);
+
+	mxcmdu(0x44, left[Vtreb]);
+	mxcmdu(0x45, right[Vtreb]);
+
+	mxcmdu(0x46, left[Vbass]);
+	mxcmdu(0x47, right[Vbass]);
+
+	source = 0;
+	if(left[Vsynth])
+		source |= 1<<6;
+	if(right[Vsynth])
+		source |= 1<<5;
+	if(left[Vaudio])
+		source |= 1<<4;
+	if(right[Vaudio])
+		source |= 1<<3;
+	if(left[Vcd])
+		source |= 1<<2;
+	if(right[Vcd])
+		source |= 1<<1;
+	if(left[Vmic])
+		source |= 1<<0;
+	if(audio.amode == Aread)
+		mxcmd(0x3c, 0);		/* output switch */
+	else
+		mxcmd(0x3c, source);
+	mxcmd(0x3d, source);		/* input left switch */
+	mxcmd(0x3e, source);		/* input right switch */
+	iunlock(&blaster);
+}
+
+static	Buf*
+getbuf(AQueue *q)
+{
+	Buf *b;
+
+	ilock(q);
+	b = q->first;
+	if(b)
+		q->first = b->next;
+	iunlock(q);
+
+	return b;
+}
+
+static	void
+putbuf(AQueue *q, Buf *b)
+{
+
+	ilock(q);
+	b->next = 0;
+	if(q->first)
+		q->last->next = b;
+	else
+		q->first = b;
+	q->last = b;
+	iunlock(q);
+}
+
+/*
+ * move the dma to the next buffer
+ */
+static	void
+contindma(void)
+{
+	Buf *b;
+
+	if(!audio.active)
+		goto shutdown;
+
+	b = audio.current;
+	if(audio.amode == Aread) {
+		if(b)	/* shouldnt happen */
+			putbuf(&audio.full, b);
+		b = getbuf(&audio.empty);
+	} else {
+		if(b)	/* shouldnt happen */
+			putbuf(&audio.empty, b);
+		b = getbuf(&audio.full);
+	}
+	audio.current = b;
+	if(b == 0)
+		goto shutdown;
+
+	dmasetup(blaster.dma, b->virt, Bufsize, audio.amode == Aread);
+	return;
+
+shutdown:
+	dmaend(blaster.dma);
+	sbcmd(0xd9);				/* exit at end of count */
+	sbcmd(0xd5);				/* pause */
+	audio.curcount = 0;
+	audio.active = 0;
+}
+
+/*
+ * cause sb to get an interrupt per buffer.
+ * start first dma
+ */
+static	void
+startdma(void)
+{
+	ulong count;
+	int speed;
+
+	ilock(&blaster);
+	dmaend(blaster.dma);
+	if(audio.amode == Aread) {
+		sbcmd(0x42);			/* input sampling rate */
+		speed = audio.livol[Vspeed];
+	} else {
+		sbcmd(0x41);			/* output sampling rate */
+		speed = audio.lovol[Vspeed];
+	}
+	sbcmd(speed>>8);
+	sbcmd(speed);
+
+	count = (Bufsize >> 1) - 1;
+	if(audio.amode == Aread)
+		sbcmd(0xbe);			/* A/D, autoinit */
+	else
+		sbcmd(0xb6);			/* D/A, autoinit */
+	sbcmd(0x30);				/* stereo, 16 bit */
+	sbcmd(count);
+	sbcmd(count>>8);
+
+	audio.active = 1;
+	contindma();
+	iunlock(&blaster);
+}
+
+/*
+ * if audio is stopped,
+ * start it up again.
+ */
+static	void
+pokeaudio(void)
+{
+	if(!audio.active)
+		startdma();
+}
+
+static void
+audiosbintr(void)
+{
+	int stat, dummy;
+
+	stat = mxread(0x82) & 7;		/* get irq status */
+	if(stat) {
+		dummy = 0;
+		if(stat & 2) {
+			ilock(&blaster);
+			dummy = inb(blaster.clri16);
+			contindma();
+			iunlock(&blaster);
+			audio.intr = 1;
+			wakeup(&audio.vous);
+		}
+		if(stat & 1) {
+			dummy = inb(blaster.clri8);
+		}
+		if(stat & 4) {
+			dummy = inb(blaster.clri401);
+		}
+		USED(dummy);
+	}
+}
+
+static void
+pcaudiosbintr(Ureg*, void*)
+{
+/*	print("#A: audio interrupt\n");	/**/
+	audiosbintr();
+}
+
+static void
+audiodmaintr(void)
+{
+/*	print("#A: dma interrupt\n");	/**/
+}
+
+static int
+anybuf(void*)
+{
+	return audio.intr;
+}
+
+/*
+ * wait for some output to get
+ * empty buffers back.
+ */
+static void
+waitaudio(void)
+{
+
+	audio.intr = 0;
+	pokeaudio();
+	tsleep(&audio.vous, anybuf, 0, 10*1000);
+	if(audio.intr == 0) {
+/*		print("#A: audio timeout\n");	/**/
+		audio.active = 0;
+		pokeaudio();
+	}
+}
+
+static void
+sbbufinit(void)
+{
+	int i;
+	void *p;
+
+	for(i=0; i<Nbuf; i++) {
+		p = xspanalloc(Bufsize, CACHELINESZ, 64*1024);
+		dcflush(p, Bufsize);
+		audio.buf[i].virt = UNCACHED(uchar, p);
+		audio.buf[i].phys = (ulong)PADDR(p);
+	}
+}
+
+static	void
+setempty(void)
+{
+	int i;
+
+	ilock(&blaster);
+	audio.empty.first = 0;
+	audio.empty.last = 0;
+	audio.full.first = 0;
+	audio.full.last = 0;
+	audio.current = 0;
+	audio.filling = 0;
+	for(i=0; i<Nbuf; i++)
+		putbuf(&audio.empty, &audio.buf[i]);
+	iunlock(&blaster);
+}
+
+static	void
+resetlevel(void)
+{
+	int i;
+
+	for(i=0; volumes[i].name; i++) {
+		audio.lovol[i] = volumes[i].ilval;
+		audio.rovol[i] = volumes[i].irval;
+		audio.livol[i] = volumes[i].ilval;
+		audio.rivol[i] = volumes[i].irval;
+	}
+}
+
+static void
+audioinit(void)
+{
+	ISAConf sbconf;
+	int i;
+
+	sbconf.port = 0x220;
+	sbconf.dma = Dma;
+	sbconf.irq = 7;
+	if(isaconfig("audio", 0, &sbconf) == 0)
+		return;
+	if(strcmp(sbconf.type, "sb16") != 0)
+		return;
+	switch(sbconf.port){
+	case 0x220:
+	case 0x240:
+	case 0x260:
+	case 0x280:
+		break;
+	default:
+		print("#A: bad port 0x%lx\n", sbconf.port);
+		return;
+	}
+	switch(sbconf.irq){
+	case 2:
+	case 5:
+	case 7:
+	case 10:
+		break;
+	default:
+		print("#A: bad irq %d\n", sbconf.irq);
+		return;
+	}
+
+	blaster.reset = sbconf.port + 0x6;
+	blaster.read = sbconf.port + 0xa;
+	blaster.write = sbconf.port + 0xc;
+	blaster.wstatus = sbconf.port + 0xc;
+	blaster.rstatus = sbconf.port + 0xe;
+	blaster.mixaddr = sbconf.port + 0x4;
+	blaster.mixdata = sbconf.port + 0x5;
+	blaster.clri8 = sbconf.port + 0xe;
+	blaster.clri16 = sbconf.port + 0xf;
+	blaster.clri401 = sbconf.port + 0x100;
+	blaster.dma = sbconf.dma;
+
+	seteisadma(blaster.dma, audiodmaintr);
+	setvec(Int0vec+sbconf.irq, pcaudiosbintr, 0);
+
+	audio.amode = Aclosed;
+	resetlevel();
+
+	outb(blaster.reset, 1);
+	delay(1);			/* >3 υs */
+	outb(blaster.reset, 0);
+	delay(1);
+
+	i = sbread();
+	if(i != 0xaa) {
+		print("#A: no response #%.2x\n", i);
+		return;
+	}
+
+	sbcmd(0xe1);			/* get version */
+	audio.major = sbread();
+	audio.minor = sbread();
+
+	if(audio.major != 4) {
+		print("#A: model #%.2x #%.2x; not SB 16\n", audio.major, audio.minor);
+		return;
+	}
+	/*
+	 * initialize the mixer
+	 */
+	mxcmd(0x00, 0);			/* Reset mixer */
+	mxvolume();
+
+	/*
+	 * set up irq/dma chans
+	 */
+	mxcmd(0x80,			/* irq */
+		(sbconf.irq==2)? 1:
+		(sbconf.irq==5)? 2:
+		(sbconf.irq==7)? 4:
+		(sbconf.irq==10)? 8:
+		0);
+	mxcmd(0x81, 1<<blaster.dma);	/* dma */
+}
+
+static Chan*
+audioattach(char *param)
+{
+	return devattach('A', param);
+}
+
+static Walkqid*
+audiowalk(Chan *c, Chan *nc, char **name, int nname)
+{
+	return devwalk(c, nc, name, nname, audiodir, nelem(audiodir), devgen);
+}
+
+static int
+audiostat(Chan *c, uchar *db, int n)
+{
+	return devstat(c, db, n, audiodir, nelem(audiodir), devgen);
+}
+
+static Chan*
+audioopen(Chan *c, int omode)
+{
+	int amode;
+
+	if(audio.major != 4)
+		error(Emajor);
+
+	switch((ulong)c->qid.path) {
+	default:
+		error(Eperm);
+		break;
+
+	case Qvolume:
+	case Qdir:
+		break;
+
+	case Qaudio:
+		amode = Awrite;
+		if((omode&7) == OREAD)
+			amode = Aread;
+		qlock(&audio);
+		if(audio.amode != Aclosed){
+			qunlock(&audio);
+			error(Einuse);
+		}
+		if(audio.bufinit == 0) {
+			audio.bufinit = 1;
+			sbbufinit();
+		}
+		audio.amode = amode;
+		setempty();
+		audio.curcount = 0;
+		qunlock(&audio);
+		mxvolume();
+		break;
+	}
+	c = devopen(c, omode, audiodir, nelem(audiodir), devgen);
+	c->mode = openmode(omode);
+	c->flag |= COPEN;
+	c->offset = 0;
+
+	return c;
+}
+
+static void
+audioclose(Chan *c)
+{
+
+	switch((ulong)c->qid.path) {
+	default:
+		error(Eperm);
+		break;
+
+	case Qdir:
+	case Qvolume:
+		break;
+
+	case Qaudio:
+		if(c->flag & COPEN) {
+			qlock(&audio);
+			audio.amode = Aclosed;
+			if(waserror()){
+				qunlock(&audio);
+				nexterror();
+			}
+			while(audio.active)
+				waitaudio();
+			setempty();
+			poperror();
+			qunlock(&audio);
+		}
+		break;
+	}
+}
+
+static long
+audioread(Chan *c, void *vp, long n, vlong offset)
+{
+	int liv, riv, lov, rov;
+	long m, n0;
+	char buf[300];
+	Buf *b;
+	int j;
+	char *a;
+
+	a = vp;
+	n0 = n;
+	switch((ulong)c->qid.path) {
+	default:
+		error(Eperm);
+		break;
+
+	case Qdir:
+		return devdirread(c, a, n, audiodir, nelem(audiodir), devgen);
+
+	case Qaudio:
+		if(audio.amode != Aread)
+			error(Emode);
+		qlock(&audio);
+		if(waserror()){
+			qunlock(&audio);
+			nexterror();
+		}
+		while(n > 0) {
+			b = audio.filling;
+			if(b == 0) {
+				b = getbuf(&audio.full);
+				if(b == 0) {
+					waitaudio();
+					continue;
+				}
+				audio.filling = b;
+				swab(b->virt);
+				audio.curcount = 0;
+			}
+			m = Bufsize-audio.curcount;
+			if(m > n)
+				m = n;
+			memmove(a, b->virt+audio.curcount, m);
+
+			audio.curcount += m;
+			n -= m;
+			a += m;
+			if(audio.curcount >= Bufsize) {
+				audio.filling = 0;
+				putbuf(&audio.empty, b);
+			}
+		}
+		poperror();
+		qunlock(&audio);
+		break;
+
+	case Qvolume:
+		j = 0;
+		buf[0] = 0;
+		for(m=0; volumes[m].name; m++){
+			liv = audio.livol[m];
+			riv = audio.rivol[m];
+			lov = audio.lovol[m];
+			rov = audio.rovol[m];
+			j += snprint(buf+j, sizeof(buf)-j, "%s", volumes[m].name);
+			if((volumes[m].flag & Fmono) || liv==riv && lov==rov){
+				if((volumes[m].flag&(Fin|Fout))==(Fin|Fout) && liv==lov)
+					j += snprint(buf+j, sizeof(buf)-j, " %d", liv);
+				else{
+					if(volumes[m].flag & Fin)
+						j += snprint(buf+j, sizeof(buf)-j, " in %d", liv);
+					if(volumes[m].flag & Fout)
+						j += snprint(buf+j, sizeof(buf)-j, " out %d", lov);
+				}
+			}else{
+				if((volumes[m].flag&(Fin|Fout))==(Fin|Fout) && liv==lov && riv==rov)
+					j += snprint(buf+j, sizeof(buf)-j, " left %d right %d",
+						liv, riv);
+				else{
+					if(volumes[m].flag & Fin)
+						j += snprint(buf+j, sizeof(buf)-j, " in left %d right %d",
+							liv, riv);
+					if(volumes[m].flag & Fout)
+						j += snprint(buf+j, sizeof(buf)-j, " out left %d right %d",
+							lov, rov);
+				}
+			}
+			j += snprint(buf+j, sizeof(buf)-j, "\n");
+		}
+
+		return readstr(offset, a, n, buf);
+	}
+	return n0-n;
+}
+
+static long
+audiowrite(Chan *c, void *vp, long n, vlong)
+{
+	long m, n0;
+	int i, nf, v, left, right, in, out;
+	char buf[255], *field[Ncmd];
+	Buf *b;
+	char *a;
+
+	a = vp;
+	n0 = n;
+	switch((ulong)c->qid.path) {
+	default:
+		error(Eperm);
+		break;
+
+	case Qvolume:
+		v = Vaudio;
+		left = 1;
+		right = 1;
+		in = 1;
+		out = 1;
+		if(n > sizeof(buf)-1)
+			n = sizeof(buf)-1;
+		memmove(buf, a, n);
+		buf[n] = '\0';
+
+		nf = getfields(buf, field, Ncmd, 1, " \t\n");
+		for(i = 0; i < nf; i++){
+			/*
+			 * a number is volume
+			 */
+			if(field[i][0] >= '0' && field[i][0] <= '9') {
+				m = strtoul(field[i], 0, 10);
+				if(left && out)
+					audio.lovol[v] = m;
+				if(left && in)
+					audio.livol[v] = m;
+				if(right && out)
+					audio.rovol[v] = m;
+				if(right && in)
+					audio.rivol[v] = m;
+				mxvolume();
+				goto cont0;
+			}
+
+			for(m=0; volumes[m].name; m++) {
+				if(strcmp(field[i], volumes[m].name) == 0) {
+					v = m;
+					in = 1;
+					out = 1;
+					left = 1;
+					right = 1;
+					goto cont0;
+				}
+			}
+
+			if(strcmp(field[i], "reset") == 0) {
+				resetlevel();
+				mxvolume();
+				goto cont0;
+			}
+			if(strcmp(field[i], "in") == 0) {
+				in = 1;
+				out = 0;
+				goto cont0;
+			}
+			if(strcmp(field[i], "out") == 0) {
+				in = 0;
+				out = 1;
+				goto cont0;
+			}
+			if(strcmp(field[i], "left") == 0) {
+				left = 1;
+				right = 0;
+				goto cont0;
+			}
+			if(strcmp(field[i], "right") == 0) {
+				left = 0;
+				right = 1;
+				goto cont0;
+			}
+			error(Evolume);
+			break;
+		cont0:;
+		}
+		break;
+
+	case Qaudio:
+		if(audio.amode != Awrite)
+			error(Emode);
+		qlock(&audio);
+		if(waserror()){
+			qunlock(&audio);
+			nexterror();
+		}
+		while(n > 0) {
+			b = audio.filling;
+			if(b == 0) {
+				b = getbuf(&audio.empty);
+				if(b == 0) {
+					waitaudio();
+					continue;
+				}
+				audio.filling = b;
+				audio.curcount = 0;
+			}
+
+			m = Bufsize-audio.curcount;
+			if(m > n)
+				m = n;
+			memmove(b->virt+audio.curcount, a, m);
+
+			audio.curcount += m;
+			n -= m;
+			a += m;
+			if(audio.curcount >= Bufsize) {
+				audio.filling = 0;
+				swab(b->virt);
+				putbuf(&audio.full, b);
+			}
+		}
+		poperror();
+		qunlock(&audio);
+		break;
+	}
+	return n0 - n;
+}
+
+static	void
+swab(uchar *a)
+{
+	ulong *p, *ep, b;
+
+	if(!SBswab)
+		return;
+	p = (ulong*)a;
+	ep = p + (Bufsize>>2);
+	while(p < ep) {
+		b = *p;
+		b = (b>>24) | (b<<24) |
+			((b&0xff0000) >> 8) |
+			((b&0x00ff00) << 8);
+		*p++ = b;
+	}
+}
+
+Dev audiodevtab = {
+	'A',
+	"audio",
+
+	devreset,
+	audioinit,
+	devshutdown,
+	audioattach,
+	audiowalk,
+	audiostat,
+	audioopen,
+	devcreate,
+	audioclose,
+	audioread,
+	devbread,
+	audiowrite,
+	devbwrite,
+	devremove,
+	devwstat,
+};
--- /dev/null
+++ b/os/port/devbench.c
@@ -1,0 +1,1165 @@
+#include "u.h"
+#include "../port/lib.h"
+#include "mem.h"
+#include "dat.h"
+#include "fns.h"
+#include <interp.h>
+#include "io.h"
+#include "../port/error.h"
+#include <isa.h>
+#include "kernel.h"
+
+/* Builtin module support */
+#include "bench.h"
+#include "benchmod.h"
+
+typedef enum { None, Calibrate, Base, Op, Intr, Dis, Gc, MS2T, xTest};
+static struct {
+	int		inuse;	/* reference count */
+	int		test;
+	void*	scratch;
+	char*	buf;
+	int		bufsz;
+	char*	wpos;
+	void		(*op)(void);
+	vlong	tickstart;
+} bench;
+
+static void
+log(char *msg, ...)
+{
+	va_list ap;
+
+	va_start(ap, msg);
+	bench.wpos = vseprint(bench.wpos, bench.buf+bench.bufsz, msg, ap);
+	va_end(ap);
+}
+
+void
+elog(char *msg, ...)
+{
+	va_list ap;
+
+	if(bench.buf == 0)
+		return;
+	va_start(ap, msg);
+	bench.wpos = vseprint(bench.wpos, bench.buf+bench.bufsz, msg, ap);
+	va_end(ap);
+}
+
+static void
+clear(void)
+{
+	bench.wpos = bench.buf;
+}
+
+static long
+rep(void *to, long n, ulong offset)
+{
+	long left = bench.wpos - bench.buf - offset;
+	if(left < 0)
+		left = 0;
+	if(n > left)
+		n = left;
+	memmove(to, bench.buf+offset, n);
+	return n;
+}
+
+static long
+notest(int report, void *va, long n, ulong offset)
+{
+	USED(report, va, n, offset);
+	if(report)
+		return rep(va, n, offset);
+	return 0;
+}
+
+// Calibration
+static long MS2TS = 0;	// time stamps per millisec
+static long US2TS = 0;	// time stamps per microsecond
+
+static long
+cal(int report, void *va, long n, ulong offset)
+{
+	int tot, i, lim, low, max, pl, mdelay;
+	ulong t;
+	if(report)
+		return rep(va, n, offset);
+	clear();
+	setpri(PriRealtime);
+	lim = 1000;
+	low = 64000000;
+	max = 0;
+	tot = 0;
+	mdelay = 1000;
+	for(i=0; i<lim; i++){
+		do{
+			pl = splhi();
+			t = archrdtsc32();
+			microdelay(mdelay);
+			t = archrdtsc32() - t;
+			splx(pl);
+		} while(t < 0);
+		if(t < low)
+			low = t;
+		if(t > max)
+			max = t;
+		tot += t;
+	}
+	MS2TS = tot/lim;
+	US2TS = MS2TS/1000;
+	if(va)
+		log("mdelay=%lud lim=%lud tot=%lud low=%lud max=%lud\n", mdelay, lim, tot, low, max);
+	setpri(PriNormal);
+	return n;
+}
+
+/*
+ * ticks to format string
+ */
+/*static*/ char *
+ts2str(vlong ticks)
+{
+#define Nbuf 5
+	static char b[Nbuf][40];
+	static int n=Nbuf-1;
+	char *fmt, *unit;
+	double d;
+
+	if(0){
+		print("ticks=%lld MS2TS=%ld\n", ticks, MS2TS);
+		d = (double)ticks;
+		print("1:%f\n", d);
+		d = (double)ticks*1000;
+		//print("2:%f\n", d);
+		d = ((double)ticks)/MS2TS;
+		//print("3:%f\n", d);
+	}
+	n = (n+1)%Nbuf;
+	if(ticks > MS2TS*1000) {
+		fmt = "%.2f %s";
+		unit = "s";
+		d = ((double)ticks/MS2TS) * 1000.0;
+	} else if(ticks > MS2TS) {
+		fmt = "%.2f %s";
+		unit = "ms";
+		d = (double)ticks/MS2TS;
+	} else if(ticks > MS2TS/1000) {
+		fmt = "%.2f %s";
+		unit = "us";
+		d = ((double)ticks*1000)/MS2TS;
+	} else {
+		fmt = "%.2f %s";
+		unit = "ns";
+		d = ((double)ticks*1000*1000)/MS2TS;
+	}
+	sprint(b[n], fmt, d, unit);
+	return b[n];
+}
+
+/*
+ * ticks to microseconds
+ */
+static double
+ts2us(vlong ticks)
+{
+	return ((double)ticks*1000)/MS2TS;
+}
+
+/*
+ * microseconds timestamp
+ */
+static vlong
+bus(int reset)
+{
+	vlong now;
+	if(US2TS == 0)
+		return 0;
+	if(reset) {
+		bench.tickstart = archrdtsc();
+		return 0;
+	}
+	now = archrdtsc();
+	return ((now-bench.tickstart))/US2TS;
+}
+
+// Base
+static long
+base(int report, void *va, long n, ulong offset)
+{
+	int tot, i, lim, low, max, pl;
+	ulong t;
+	char *bm;
+
+	if(report)
+		return rep(va, n, offset);
+	clear();
+	setpri(PriRealtime);
+	lim = 1000;
+	low = 64000000;
+	max = 0;
+	tot = 0;
+	for(i=0; i<lim; i++){
+		do {
+			pl = splhi();
+			t = archrdtsc32();
+			// do nothing
+			t = archrdtsc32() - t;
+			splx(pl);
+		} while(t < 0);
+		if(t < low)
+			low = t;
+		if(t > max)
+			max = t;
+		tot += t;
+	}
+	bm = ts2str(tot/lim);
+	log("%d %lud %lud %lud %lud (%s)\n", up->pid, lim, tot, low, max, bm);
+	setpri(PriNormal);
+	return n;
+}
+
+// Timeop
+
+typedef struct Psync Psync;
+
+enum {
+	Maxprocs=3,
+};
+
+struct Psync {
+	Rendez	r;
+	int	flag;
+	int	id;
+	int	awaken;
+};
+static Psync timesync[Maxprocs];
+static Ref nactive;
+static Ref nbusy;
+static RWlock sync;
+
+static void
+nilop(void)
+{
+}
+
+static int
+timev(void *a)
+{
+	return *(int*)a;
+}
+
+static void
+timeop0(void *ap)
+{
+	int tot, i, lim, low, max;
+	ulong t;
+	Psync *ps;
+	char *bm;
+
+	ps = ap;
+	setpri(PriRealtime);
+	incref(&nactive);
+	sleep(&ps->r, timev, &ps->flag);
+	rlock(&sync);
+	lim = 1000;
+	low = 64000000;
+	max = 0;
+	tot = 0;
+	for(i=0; i<lim; i++){
+		do{
+			t = archrdtsc32();
+			(*bench.op)();
+			t = archrdtsc32() - t;
+		}while(t < 0);
+		if(t < low)
+			low = t;
+		if(t > max)
+			max = t;
+		tot += t;
+	}
+	bm = ts2str(tot/lim);
+	log("%d %lud %lud %lud %lud (%s)\n", up->pid, lim, tot, low, max, bm);
+	runlock(&sync);
+	pexit("", 0);
+}
+
+static long
+timeop(int report, void *va, long n, ulong offset)
+{
+	int i, np, pl;
+
+	if(report)
+		return rep(va, n, offset);
+	clear();
+	bench.op = 0;
+	if(strncmp(va, "nil", 3) == 0)
+		bench.op = nilop;
+	else if(strncmp(va, "sched", 5) == 0)
+		bench.op = sched;
+	else
+		return 0;
+	for(np=1; np<=Maxprocs; np++) {
+		nactive.ref = 0;
+		wlock(&sync);
+		log("%d procs\n", np);
+		setpri(PriRealtime);
+		for(i=0; i<np; i++) {
+			timesync[i].id = i;
+			kproc("timeop", timeop0, &timesync[i], 0);
+		}
+		while(nactive.ref < np)
+			tsleep(&up->sleep, return0, 0, 20);
+		for(i=0; i<np; i++){
+			timesync[i].flag = 1;
+			wakeup(&timesync[i].r);
+		}
+		sched();
+		pl = splhi();
+		setpri(PriNormal);
+		wunlock(&sync);
+		// now they run
+		wlock(&sync);		// wait for last reader
+		wunlock(&sync);
+		splx(pl);
+	}
+	return n;
+}
+
+typedef struct Ictr Ictr;
+struct Ictr {
+	ulong	base;
+	ulong	sleep;
+	ulong	spllo;
+	ulong	intr;
+	ulong	isave;
+	ulong	arrive;
+	ulong	wakeup;
+	ulong	awake;
+};
+static Ictr counters[5/*100*/], *curct;
+static int intrwant;
+static Rendez vous;
+int	spltbl;	/* set by spllo */
+int	intrtbl;	/* set by intrvec() */
+int	isavetbl;	/* set by intrvec() */
+
+static int ienable;
+
+
+static void
+intrwake(void)
+{
+	if(ienable == 0)
+		return;
+	ienable = 0;
+	if(spltbl == 0)		// not used here
+		curct->spllo = curct->intr = curct->isave = archrdtsc32();
+	else {
+		curct->spllo = spltbl;
+		curct->intr = intrtbl;
+		curct->isave = isavetbl;
+	}
+	curct->arrive = archrdtsc32();
+	intrwant = 0;
+	wakeup(&vous);
+	curct->wakeup = archrdtsc32();
+}
+
+/*
+ * sleep calls intrtest with splhi (under lock):
+ * provoke the interrupt now, so that it is guaranteed
+ * not to happen until sleep has queued the process,
+ * forcing wakeup to do something.
+ */
+static int
+intrtest(void*)
+{
+	ienable = 1;		/* enable recording on interrupt */
+	curct->sleep = archrdtsc32();
+	return intrwant==0;
+}
+
+static long
+intrtime(int report, void *va, long n, ulong offset)
+{
+	Ictr *ic;
+	long t;
+	int i;
+	char *bm;
+	if(report)
+		return rep(va, n, offset);
+	clear();
+
+	setpri(PriRealtime);
+	sched();
+	curct = counters;
+	ienable = 0;
+	addclock0link(intrwake, MS2HZ);
+	for(i=0; i<nelem(counters); i++){
+		curct = &counters[i];
+		intrwant = 1;
+		curct->base = archrdtsc32();
+		sleep(&vous, intrtest, nil);
+		curct->awake = archrdtsc32();
+		sched();	/* just to slow it down between trials */
+	}
+	log("interrupt\n");
+	for(i=0; i<nelem(counters); i++){
+		ic = &counters[i];
+		t = ic->awake - ic->base;
+		bm = ts2str(ic->awake - ic->arrive);
+		ic->awake -= ic->wakeup;
+		ic->wakeup -= ic->arrive;
+		ic->arrive -= ic->isave;
+		ic->isave -= ic->intr;
+		ic->intr -= ic->spllo;
+		ic->spllo -= ic->sleep;
+		ic->sleep -= ic->base;
+		log("%ld\t%ld\t%ld\t%ld\t%ld\t%ld\t%ld\t%ld (%s)\n", ic->sleep, ic->spllo, ic->intr, ic->isave, ic->arrive, ic->wakeup, ic->awake, t, bm);
+	}
+	setpri(PriNormal);
+	return n;
+}
+
+
+/* DIS operation timing */
+
+typedef struct  {
+	vlong	n;		/* count */
+	vlong	min;
+	vlong 	max;
+	vlong	sum;
+	vlong	sumsq;	/* sum of squares */
+} Stat;
+
+static void
+stat(enum { Reset, Inc } op, Stat *c, vlong val)
+{
+	switch(op) {
+	case	Reset:
+		c->n = 0;
+		c->sum = 0;
+		c->sumsq = 0;
+		c->min = 0;
+		c->max = 0;
+		break;
+	case Inc:
+		c->n++;
+		c->sum += val;
+		c->sumsq += val*val;
+		break;
+	}
+	if(val < c->min || c->n == 1)
+		c->min = val;
+	if(val > c->max || c->n == 1)
+		c->max = val;
+}
+
+static void
+statinc(Stat *d, Stat *s)
+{
+	d->n += s->n;
+	d->sum += s->sum;
+	d->sumsq += s->sumsq;
+	if(s->min < d->min || d->n == s->n)
+		d->min = s->min;
+	if(s->max > d->max || d->n == s->n)
+		d->max = s->max;
+}
+
+enum
+{
+	HSIZE	= 31,
+	MAXCOUNT	= 100000000L,
+};
+
+typedef struct {
+	int		op;
+	int		pc;
+	long		count;
+	Stat	t;		/* combined dec and execution time */
+} Istat;
+
+typedef struct Mstat Mstat;
+struct Mstat {
+	char*	name;
+	char*	path;
+	int		ninst;
+	Istat*	inst;
+	Inst*		base;
+	Mstat*	hash;
+	Mstat*	link;
+};
+
+struct
+{
+	Mstat*	hash[HSIZE];
+	Mstat*	list;
+} vmstat;
+
+extern struct			/* see ../../interp/tab.h:/keywds/ */
+{
+	char*	name;
+	int	op;
+	int	terminal;
+}keywds[];
+
+static char *
+opname(int op)
+{
+	char *name;
+
+	if(op < 0 || op >= MAXDIS)
+		return "Unknown";
+	return keywds[op].name;
+	if(name == 0)
+		name = "<Noname>";
+	return name;
+}
+
+static void
+mreset(void)
+{
+	Mstat *mp, *link;
+
+	for(mp=vmstat.list; mp; mp=link) {
+		link = mp->link;
+		free(mp->inst);
+		free(mp);
+	}
+	vmstat.list = 0;
+	memset(vmstat.hash, 0, HSIZE*sizeof(Mstat*));
+}
+
+static ulong
+hash(void *s)
+{
+	ulong sum = 0;
+	uchar *a = s;
+
+	while(*a)
+		sum = (sum << 1) + *a++;
+	return sum%HSIZE;
+}
+
+static Mstat *
+mlookup(Module *mod)
+{
+	Mstat *m;
+	ulong h;
+
+	for(m=vmstat.hash[hash(mod->name)]; m; m=m->hash)
+		if(strcmp(m->name, mod->name) == 0
+		&& strcmp(m->path, mod->path) == 0) {
+			return m;
+		}
+
+
+	m = malloc(sizeof(Mstat));
+	if(m == 0)
+		return 0;
+	kstrdup(&m->name, mod->name);
+	kstrdup(&m->path, mod->path);
+	m->ninst = mod->nprog;
+	m->inst = malloc(m->ninst*sizeof(Istat));
+	if(m->path == 0 || m->inst == 0)
+		return 0;
+	m->base = mod->prog;
+	m->link = vmstat.list;
+	vmstat.list = m;
+	h = hash(m->name);
+	m->hash = vmstat.hash[h];
+	vmstat.hash[h] = m;
+	return m;
+}
+
+/* interpreted code Dis timing */
+void
+bxec(Prog *p)
+{
+	int op, pc;
+	vlong t0, t;
+	Mstat*	ms;
+	Istat*	is;
+	Module *om;
+
+	R = p->R;
+	R.MP = R.M->MP;
+	R.IC = p->quanta;
+
+	if(p->kill != nil) {
+		char *m;
+		m = p->kill;
+		p->kill = nil;
+		error(m);
+	}
+
+	if(R.M->compiled)
+		comvec();
+	else {
+		om = 0;
+		ms = mlookup(R.M->m);
+		do {
+			op = R.PC->op;
+			pc = R.PC-R.M->prog;
+			if(om != R.M->m) {
+				om = R.M->m;
+				ms = mlookup(R.M->m);
+			}
+
+			t0 = archrdtsc();
+			dec[R.PC->add]();
+			R.PC++;
+			optab[op]();
+			t = archrdtsc();
+			if(ms) {
+				is = &ms->inst[pc];
+				if(is->count < MAXCOUNT) {
+					if(is->count++ == 0) {
+						is->op = op;
+						is->pc = pc;
+					}
+					stat(Inc, &is->t, t-t0);
+				}
+			}
+			if(op==ISPAWN || op==IMSPAWN) {
+				Prog *new = delruntail(Pdebug);
+				new->xec = bxec;
+				addrun(new);
+			}
+		} while(--R.IC != 0);
+	}
+
+	p->R = R;
+}
+
+/* compiled code Dis timing */
+
+static struct {			/* compiled code timing */
+	int		set;
+	int 		op, pc;		/* Dis opcode and program counter */
+	vlong	t0, t;			/* time-in and time-out */
+	vlong	base;		/* cost of doing the timing */
+	Mstat	*ms;
+	Module	*om;
+	int		timing;		/* between "dis timer start" and stop */
+} C;
+
+enum { Nop = 0 };	/* opcode value for Dis NOP instruction */
+void
+dopostcomp(vlong t)
+{
+	Istat*	is;
+
+	C.t = t;
+	C.set = 0;
+	if(C.ms != 0) {
+		is = &C.ms->inst[C.pc];
+		if(C.op == Nop) {			/* NOP  calibration */
+			vlong newbase = C.t - C.t0;
+			if(C.base == 0 || newbase < C.base)
+				C.base = newbase;
+		}
+		if(is->count < MAXCOUNT) {
+			if(is->count++ == 0) {
+				is->op = C.op;
+				is->pc = C.pc;
+			}
+			stat(Inc, &is->t, C.t-C.t0/*-C.base*/);
+		}
+	}
+}
+
+void
+postcomp(void)
+{
+	vlong t;
+
+	t = archrdtsc();
+	if(C.timing == 0 || C.set == 0)
+		return;
+	dopostcomp(t);
+}
+
+void
+precomp(void)
+{
+	vlong t;
+
+	t = archrdtsc();
+	if(C.timing == 0)
+		return;
+	if(C.set)
+		dopostcomp(t);
+	C.pc = *(ulong *)R.m;
+	C.op = *(ulong *)R.s;
+	if(C.om != R.M->m) {
+		C.om = R.M->m;
+		C.ms = mlookup(R.M->m);
+	}
+	C.set = 1;
+	C.t0 = archrdtsc();
+}
+
+/* standard deviation */
+static vlong
+sdev(Stat *s)
+{
+	extern double sqrt(double);
+	vlong var;
+	var = s->sum;
+	var *= var/s->n;
+	var = (s->sumsq - var)/s->n;
+	return (vlong)sqrt(var);
+}
+
+/*
+ * Use the sequence:
+ * 1. "timer startclr" or "timer start", then,
+ * 2. Any DIS operations, and,
+ * 3. "timer stop", to stop timing.
+ * 4. Read the results from the data file after:
+ *	a) "timer report" to get module/pc level results, or
+ *	b) "timer summary" to get opcode level results
+ */
+static long
+distime(int report, void *va, long n, ulong offset)
+{
+	Prog *p;
+	Mstat *mp;
+	Istat *ip, *ep;
+
+	if(report)
+		return rep(va, n, offset);
+	clear();
+	acquire();
+	p = currun();
+	if(strncmp(va, "timer startclr", 14) == 0) {
+		mreset();
+		memset(&C, 0, sizeof(C));
+		C.timing = 1;
+		p->xec = bxec;
+	} else if(strncmp(va, "timer start", 11) == 0) {
+		p->xec = bxec;
+		C.timing = 1;
+	} else if(strncmp(va, "timer stop", 10) == 0) {
+		p->xec = xec;				/* bug: stop all xec threads */
+		C.timing = 0;
+	} else if(strncmp(va, "timer nilop", 11) == 0) {
+	} else if(strncmp(va, "timer report", 12) == 0)	/* by address */
+		for(mp=vmstat.list; mp; mp=mp->link) {
+			ep = mp->inst + mp->ninst;
+			for(ip=mp->inst; ip<ep; ip++)
+				if(ip->count > 0) {
+					char *mean = ts2str(ip->t.sum/ip->count);
+					char *min = ts2str(ip->t.min);
+					char *max = ts2str(ip->t.max);
+					char *std = ts2str(sdev(&ip->t));
+					log("%s %d %s %ld %s %s %s %s\n", mp->path, ip->pc, opname(ip->op), ip->count, mean, min, max, std);
+				}
+		}
+	else if(strncmp(va, "timer summary", 13) == 0) {	/* by opcode */
+		static Stat T[MAXDIS];
+		int i;
+
+		for(i=0; i<MAXDIS; i++)
+			stat(Reset, &T[i], 0);
+		for(mp=vmstat.list; mp; mp=mp->link) {
+			ep = mp->inst + mp->ninst;
+			for(ip=mp->inst; ip<ep; ip++)
+				if(ip->count > 0)
+					statinc(&T[ip->op], &ip->t);
+		}
+		for(i=0; i<MAXDIS; i++) {
+			Stat *t = &T[i];
+			char *mean = "0.00 ms";
+			char *min = "0.00 ms";
+			char *max = "0.00 ms";
+			char *std = "0.00 ms";
+			if(t->n > 0) {
+				mean = ts2str(t->sum/t->n);
+				min = ts2str(t->min);
+				max = ts2str(t->max);
+				std = ts2str(sdev(t));
+			}
+			log("%d %s %lld %s %s %s %s\n", i, opname(i), t->n, mean, min, max, std);
+		}
+	} else
+		n = 0;
+	R.IC = 1;
+	release();
+
+	return n;
+}
+ 
+/*
+ * Garbage collection
+ */
+static int nidle;
+
+int
+idlegc(void *p)
+{
+	int done;
+	Prog *head;
+	vlong t0, t1, tot;
+	USED(p);
+
+	head = progn(0);	/* isched.head */
+	done = gccolor + 3;
+	tot = 0;
+	while(gccolor < done && gcruns()) {
+		if(tready(nil))
+			break;
+		t0 = archrdtsc();
+		rungc(head);
+		t1 = archrdtsc();
+		t1 -= t0;
+		tot += t1;
+//		log(" %.2f",  ts2us(t1));
+	}
+	log(" %.2f",  ts2us(tot));
+	nidle--;
+	if(nidle == 0) {
+		log("\n");
+		return 1;
+	}
+	return 0;
+}
+
+static long
+gctime(int report, void *va, long n, ulong offset)
+{
+	int i;
+	vlong t0, t1;
+	Prog *head;
+
+	if(report)
+		return rep(va, n, offset);
+	clear();
+	acquire();
+	head = progn(0);	/* isched.head */
+/*
+	if(strncmp(va, "idle", 4) == 0) {
+		nidle = 100;
+		log("GCIDLE:1l:Observation:n:Time:us");
+		atidle(idlegc, 0);
+	} else if(strncmp(va, "stop", 4) == 0) {
+		atidledont(idlegc, 0);
+	} else 
+*/
+	if(strncmp(va, "sched", 5) == 0) {
+		log("GCSCHED:1l:Observation:n:Time:us");
+		for(i=0; i<1000; i++) {
+			t0 = archrdtsc();
+			rungc(head);
+			t1 = archrdtsc();
+			log(" %.2f",  ts2us(t1-t0));
+			release();
+			acquire();
+		}
+		log("\n");
+	} else if(strncmp(va, "acquire", 7) == 0) {
+		log("GCACQUIRE:1l:Observation:n:Time:us");
+		for(i=0; i<1000; i++) {
+			t0 = archrdtsc();
+			release();
+			acquire();
+			head = progn(0);	/* isched.head */
+			rungc(head);
+			release();
+			acquire();
+			t1 = archrdtsc();
+			log(" %.2f",  ts2us(t1-t0));
+		}
+		log("\n");
+	}
+
+	release();
+
+	return n;
+}
+
+
+/* 
+ * Request the number of time stamp ticks per millisecond
+ */
+static long
+ms2ts(int report, void *va, long n, ulong offset)
+{
+	if(report)
+		return rep(va, n, offset);
+	log("%.ld\n", MS2TS);
+	return n;
+}
+
+/*
+ * test
+ */
+
+static long
+test(int report, void *va, long n, ulong offset)
+{
+//	vlong v;
+	double d;
+	if(report)
+		return rep(va, n, offset);
+//	v = 5;
+//	print("vlong %lld\n", v);
+//	print("before cast\n");
+//	d = (double)v;
+//	print("after cast\n");
+//	print("before assign\n");
+	d=100.0;
+	print("after assign\n");
+	print("double %f\n", d);
+//	log("%lld %f\n", v, d);
+	return n;
+}
+
+/*
+ * $Bench builtin support
+ */
+void
+Bench_reset(void *)
+{
+	bus(1);
+}
+
+void
+Bench_microsec(void *fp)
+{
+	F_Bench_microsec *f;
+
+	f = fp;
+	*f->ret = bus(0);
+}
+
+void
+Bench_disablegc(void *)
+{
+	gclock();
+}
+
+void
+Bench_enablegc(void *)
+{
+	gcunlock();
+}
+
+
+#define fdchk(x)	((x) == (Bench_FD*)H ? -1 : (x)->fd)
+void
+Bench_read(void *fp)
+{
+	int n;
+	F_Bench_read *f;
+	vlong usrelease, uskread, usacquire, ussched;
+
+	f = fp;
+	n = f->n;
+	if(f->buf == (Array*)H) {
+		*f->ret = 0;
+		return;		
+	}
+	if(n > f->buf->len)
+		n = f->buf->len;
+
+	bus(1);
+	release();
+	usrelease = bus(0);
+	*f->ret = kread(fdchk(f->fd), f->buf->data, n);
+	uskread = bus(0);
+	acquire();
+	usacquire = bus(0);
+	sched();
+	ussched = bus(0);
+	log("%lld %lld %lld %lud %lld\n", usrelease, uskread, usacquire, m->ticks, ussched);
+}
+
+
+/*
+ * driver support
+ */
+long (*Test[])(int report, void *va, long n, ulong offset) = {
+	[None]		notest,
+	[Calibrate]	cal,
+	[Base]		base,
+	[Op]			timeop,
+	[Intr]			intrtime,
+	[Dis]			distime,
+	[Gc]			gctime,
+	[MS2T]		ms2ts,
+	[xTest]		test,
+};
+
+enum {
+	Benchdirqid,
+	Benchdataqid,
+	Benchctlqid,
+	Benchusqid,
+};
+#define Data 0
+static Dirtab benchtab[]={
+	".",		{Benchdirqid,0,QTDIR},	0,	0555,
+	"bdata",	{Benchdataqid},		0,	0444,
+	"bctl",	{Benchctlqid},		0,	0660,
+	"busec",	{Benchusqid},		0,	0660,
+};
+
+static void
+benchreset(void)
+{
+	builtinmod("$Bench", Benchmodtab);
+}
+
+static Chan*
+benchattach(char *spec)
+{
+	bench.inuse++;
+	if(bench.inuse == 1) {
+		bench.bufsz = 100*READSTR;
+		bench.buf = xalloc(bench.bufsz);
+		bench.wpos = bench.buf;
+		if(bench.buf == 0)
+			error(Enomem);
+		bench.test = None;
+		cal(0, 0, 0, 0);
+	}	
+	return devattach('x', spec);
+}
+
+void
+benchshutdown(void)
+{
+	bench.inuse--;
+	if(bench.inuse == 0)
+		xfree(bench.buf);
+}
+
+static Walkqid*
+benchwalk(Chan *c, Chan *nc, char **name, int nname)
+{
+	return devwalk(c, nc, name, nname, benchtab, nelem(benchtab), devgen);
+}
+
+static Chan*
+benchopen(Chan *c, int omode)
+{
+	if(c->qid.path == Benchdirqid){
+		if(omode != OREAD)
+			error(Eperm);
+	}
+	c->mode = openmode(omode);
+	c->flag |= COPEN;
+	c->offset = 0;
+	return c;
+}
+
+static int
+benchstat(Chan *c, uchar *dp, int n)
+{
+	switch((ulong)c->qid.path){
+	case Benchdataqid:
+		benchtab[Data].length = bench.wpos - bench.buf;
+	}
+	return devstat(c, dp, n, benchtab, nelem(benchtab), devgen);
+}
+
+static void
+benchclose(Chan*)
+{
+}
+
+static long	 
+benchread(Chan *c, void *buf, long n, vlong offset)
+{
+	vlong us;
+	char tmp[64];
+
+	switch((ulong)c->qid.path){
+	case Benchdirqid:
+		return devdirread(c, buf, n, benchtab, nelem(benchtab), devgen);
+
+	case Benchdataqid:
+		return Test[bench.test](1, buf, n, offset);
+
+	case Benchusqid:
+		us = archrdtsc();
+		us /= US2TS;
+		snprint(tmp, sizeof(tmp), "%.lld", us);
+		return readstr(0, buf, n, tmp);
+	default:
+		n = 0;
+		break;
+	}
+	return n;
+}
+
+static long	 
+benchwrite(Chan *c, void *buf, long n, vlong offset)
+{
+	int argn = n;
+
+	switch((ulong)c->qid.path){
+	case Benchctlqid:
+		bench.test = None;
+		memset((char *)bench.buf, 0, bench.bufsz);
+		bench.wpos = bench.buf;
+		if(strncmp(buf, "test", 4) == 0)
+			bench.test = xTest;
+		else if(strncmp(buf, "calibrate", 9) == 0)
+			bench.test = Calibrate;
+		else if(strncmp(buf, "base", 4) == 0)
+			bench.test = Base;
+		else if(strncmp(buf, "intr", 4) == 0)
+			bench.test = Intr;
+		else if(strncmp(buf, "op ", 3) == 0) {
+			bench.test = Op;
+			buf = (char *)buf + 3;
+			argn -= 3;
+		} else if(strncmp(buf, "dis ", 4) == 0) {
+			bench.test = Dis;
+			buf = (char *)buf + 4;
+			argn -= 4;
+		} else if(strncmp(buf, "gc ", 3) == 0) {
+			bench.test = Gc;
+			buf = (char *)buf + 3;
+			argn -= 3;
+		} else if(strncmp(buf, "ms2ts", 5) == 0)
+			bench.test = MS2T;
+		else
+			error(Ebadctl);
+		Test[bench.test](0, buf, argn, offset);
+		break;
+	case Benchusqid:
+		bench.tickstart = archrdtsc();
+		break;
+	default:
+		error(Ebadusefd);
+	}
+	return n;
+}
+
+Dev	benchdevtab = {
+	'x',
+	"bench",
+
+	benchreset,
+	devinit,
+	benchshutdown,
+	benchattach,
+	benchwalk,
+	benchstat,
+	benchopen,
+	devcreate,
+	benchclose,
+	benchread,
+	devbread,
+	benchwrite,
+	devbwrite,
+	devremove,
+	devwstat,
+
+};
--- /dev/null
+++ b/os/port/devboot.c
@@ -1,0 +1,150 @@
+#include	"u.h"
+#include	"../port/lib.h"
+#include	"mem.h"
+#include	"dat.h"
+#include	"fns.h"
+#include	"../port/error.h"
+
+enum{
+	Qdir,
+	Qboot,
+	Qmem,
+	Qkexec,
+
+	Maxkexec = 1536*1024,
+};
+
+static 
+Dirtab bootdir[]={
+	".",			{Qdir, 0, QTDIR},	0,	0555,
+	"boot",		{Qboot},	0,	0220,
+	"mem",		{Qmem},		0,	0660,
+	"kexec",		{Qkexec},		0,	0220,
+};
+
+static Chan*
+bootattach(char *spec)
+{
+	return devattach('B', spec);
+}
+
+static Walkqid*
+bootwalk(Chan *c, Chan *nc, char **name, int nname)
+{
+	return devwalk(c, nc, name, nname, bootdir, nelem(bootdir), devgen);
+}
+
+static int
+bootstat(Chan *c, uchar *dp, int n)
+{
+	return devstat(c, dp, n, bootdir, nelem(bootdir), devgen);
+}
+
+static Chan*
+bootopen(Chan *c, int omode)
+{
+	if (c->qid.path == Qkexec) {
+		c->aux = malloc(Maxkexec);
+		print("kexec buffer: %lux\n", c->aux);
+	}
+	return devopen(c, omode, bootdir, nelem(bootdir), devgen);
+}
+
+static void	 
+bootclose(Chan *c)
+{
+	if(c->qid.path == Qkexec && c->aux != nil){
+		print("exec new kernel @%lux\n", (ulong)c->aux);
+		splhi();
+		segflush(c->aux, 64*1024);
+		gotopc((ulong)c->aux);
+	}
+}
+
+static long	 
+bootread(Chan *c, void *buf, long n, vlong offset)
+{
+	switch((ulong)c->qid.path){
+
+	case Qdir:
+		return devdirread(c, buf, n, bootdir, nelem(bootdir), devgen);
+
+	case Qmem:
+		/* kernel memory */
+		if(offset>=KZERO && offset<KZERO+conf.npage*BY2PG){
+			if(offset+n > KZERO+conf.npage*BY2PG)
+				n = KZERO+conf.npage*BY2PG - offset;
+			memmove(buf, (char*)offset, n);
+			return n;
+		}
+		error(Ebadarg);
+	}
+
+	error(Egreg);
+	return 0;	/* not reached */
+}
+
+static long	 
+bootwrite(Chan *c, void *buf, long n, vlong offset)
+{
+	ulong pc;
+	uchar *p;
+
+	switch((ulong)c->qid.path){
+	case Qmem:
+		/* kernel memory */
+		if(offset>=KZERO && offset<KZERO+conf.npage*BY2PG){
+			if(offset+n > KZERO+conf.npage*BY2PG)
+				n = KZERO+conf.npage*BY2PG - offset;
+			memmove((char*)offset, buf, n);
+			segflush((void*)offset, n);
+			return n;
+		}
+		error(Ebadarg);
+
+	case Qboot:
+		p = (uchar*)buf;
+		pc = (((((p[0]<<8)|p[1])<<8)|p[2])<<8)|p[3];
+		if(pc < KZERO || pc >= KZERO+conf.npage*BY2PG)
+			error(Ebadarg);
+		splhi();
+		segflush((void*)pc, 64*1024);
+		gotopc(pc);
+
+	case Qkexec:
+		print(".");
+		if(c->aux != nil && offset <= Maxkexec){
+			if(offset+n > Maxkexec)
+				n = Maxkexec - offset;
+			memmove((char*)c->aux+offset, buf, n);
+			segflush((char*)c->aux+offset, n);
+			return n;
+		}
+		free(c->aux);
+		c->aux = nil;
+		error(Ebadarg);
+	}
+	error(Ebadarg);
+	return 0;	/* not reached */
+}
+
+Dev bootdevtab = {
+	'B',
+	"boot",
+
+	devreset,
+	devinit,
+	devshutdown,
+	bootattach,
+	bootwalk,
+	bootstat,
+	bootopen,
+	devcreate,
+	bootclose,
+	bootread,
+	devbread,
+	bootwrite,
+	devbwrite,
+	devremove,
+	devwstat,
+};
--- /dev/null
+++ b/os/port/devbridge.c
@@ -1,0 +1,1206 @@
+#include "u.h"
+#include "../port/lib.h"
+#include "mem.h"
+#include "dat.h"
+#include "fns.h"
+#include "../port/netif.h"
+#include "../port/error.h"
+
+typedef struct Bridge 	Bridge;
+typedef struct Port 	Port;
+typedef struct Centry	Centry;
+typedef struct Iphdr	Iphdr;
+typedef struct Tcphdr	Tcphdr;
+
+enum
+{
+	Qtopdir=	1,		/* top level directory */
+
+	Qbridgedir,			/* bridge* directory */
+	Qbctl,
+	Qstats,
+	Qcache,
+	Qlog,
+
+	Qportdir,			/* directory for a protocol */
+	Qpctl,
+	Qlocal,
+	Qstatus,
+
+	MaxQ,
+
+	Maxbridge=	4,
+	Maxport=	128,		// power of 2
+	CacheHash=	257,		// prime
+	CacheLook=	5,		// how many cache entries to examine
+	CacheSize=	(CacheHash+CacheLook-1),
+	CacheTimeout=	5*60,		// timeout for cache entry in seconds
+
+	TcpMssMax = 1300,			// max desirable Tcp MSS value
+	TunnelMtu = 1400,
+};
+
+static Dirtab bridgedirtab[]={
+	"ctl",		{Qbctl},	0,	0666,
+	"stats",	{Qstats},	0,	0444,
+	"cache",	{Qcache},	0,	0444,
+	"log",		{Qlog},		0,	0666,
+};
+
+static Dirtab portdirtab[]={
+	"ctl",		{Qpctl},	0,	0666,
+	"local",	{Qlocal},	0,	0444,
+	"status",	{Qstatus},	0,	0444,
+};
+
+enum {
+	Logcache=	(1<<0),
+	Logmcast=	(1<<1),
+};
+
+// types of interfaces
+enum
+{
+	Tether,
+	Ttun,
+};
+
+static Logflag logflags[] =
+{
+	{ "cache",	Logcache, },
+	{ "multicast",	Logmcast, },
+	{ nil,		0, },
+};
+
+static Dirtab	*dirtab[MaxQ];
+
+#define TYPE(x) 	(((ulong)(x).path) & 0xff)
+#define PORT(x) 	((((ulong)(x).path) >> 8)&(Maxport-1))
+#define QID(x, y) 	(((x)<<8) | (y))
+
+struct Centry
+{
+	uchar	d[Eaddrlen];
+	int	port;
+	long	expire;		// entry expires this number of seconds after bootime
+	long	src;
+	long	dst;
+};
+
+struct Bridge
+{
+	QLock;
+	int	nport;
+	Port	*port[Maxport];
+	Centry	cache[CacheSize];
+	ulong	hit;
+	ulong	miss;
+	ulong	copy;
+	long	delay0;		// constant microsecond delay per packet
+	long	delayn;		// microsecond delay per byte
+	int tcpmss;		// modify tcpmss value
+
+	Log;
+};
+
+struct Port
+{
+	int	id;
+	Bridge	*bridge;
+	int	ref;
+	int	closed;
+
+	Chan	*data[2];	// channel to data
+
+	int	mcast;		// send multi cast packets
+
+	Proc	*readp;		// read proc
+	
+	// the following uniquely identifies the port
+	int	type;
+	char	name[KNAMELEN];
+	
+	// owner hash - avoids bind/unbind races
+	ulong	ownhash;
+
+	// various stats
+	int	in;		// number of packets read
+	int	inmulti;	// multicast or broadcast
+	int	inunknown;	// unknown address
+	int	out;		// number of packets read
+	int	outmulti;	// multicast or broadcast
+	int	outunknown;	// unknown address
+	int outfrag;	// fragmented the packet
+	int	nentry;		// number of cache entries for this port
+};
+
+enum {
+	IP_VER		= 0x40,		/* Using IP version 4 */
+	IP_HLEN		= 0x05,		/* Header length in characters */
+	IP_DF		= 0x4000,	/* Don't fragment */
+	IP_MF		= 0x2000,	/* More fragments */
+	IP_MAX		= (32*1024),	/* Maximum Internet packet size */
+	IP_TCPPROTO = 6,
+	EOLOPT		= 0,
+	NOOPOPT		= 1,
+	MSSOPT		= 2,
+	MSS_LENGTH	= 4,		/* Mean segment size */
+	SYN		= 0x02,		/* Pkt. is synchronise */
+	IPHDR		= 20,		/* sizeof(Iphdr) */
+};
+
+struct Iphdr
+{
+	uchar	vihl;		/* Version and header length */
+	uchar	tos;		/* Type of service */
+	uchar	length[2];	/* packet length */
+	uchar	id[2];		/* ip->identification */
+	uchar	frag[2];	/* Fragment information */
+	uchar	ttl;		/* Time to live */
+	uchar	proto;		/* Protocol */
+	uchar	cksum[2];	/* Header checksum */
+	uchar	src[4];		/* IP source */
+	uchar	dst[4];		/* IP destination */
+};
+
+struct Tcphdr
+{
+	uchar	sport[2];
+	uchar	dport[2];
+	uchar	seq[4];
+	uchar	ack[4];
+	uchar	flag[2];
+	uchar	win[2];
+	uchar	cksum[2];
+	uchar	urg[2];
+};
+
+static Bridge bridgetab[Maxbridge];
+
+static int m2p[] = {
+	[OREAD]		4,
+	[OWRITE]	2,
+	[ORDWR]		6
+};
+
+static int	bridgegen(Chan *c, char*, Dirtab*, int, int s, Dir *dp);
+static void	portbind(Bridge *b, int argc, char *argv[]);
+static void	portunbind(Bridge *b, int argc, char *argv[]);
+static void	etherread(void *a);
+static char	*cachedump(Bridge *b);
+static void	portfree(Port *port);
+static void	cacheflushport(Bridge *b, int port);
+static void	etherwrite(Port *port, Block *bp);
+
+extern ulong	parseip(uchar*, char*);
+extern ushort	ipcsum(uchar *addr);
+
+static void
+bridgeinit(void)
+{
+	int i;
+	Dirtab *dt;
+	// setup dirtab with non directory entries
+	for(i=0; i<nelem(bridgedirtab); i++) {
+		dt = bridgedirtab + i;
+		dirtab[TYPE(dt->qid)] = dt;
+	}
+	for(i=0; i<nelem(portdirtab); i++) {
+		dt = portdirtab + i;
+		dirtab[TYPE(dt->qid)] = dt;
+	}
+}
+
+static Chan*
+bridgeattach(char* spec)
+{
+	Chan *c;
+	int dev;
+
+	dev = atoi(spec);
+	if(dev<0 || dev >= Maxbridge)
+		error("bad specification");
+
+	c = devattach('B', spec);
+	mkqid(&c->qid, QID(0, Qtopdir), 0, QTDIR);
+	c->dev = dev;
+
+	return c;
+}
+
+static Walkqid*
+bridgewalk(Chan *c, Chan *nc, char **name, int nname)
+{
+	return devwalk(c, nc, name, nname, (Dirtab*)0, 0, bridgegen);
+}
+
+static int
+bridgestat(Chan* c, uchar* db, int n)
+{
+	return devstat(c, db, n, (Dirtab *)0, 0L, bridgegen);
+}
+
+static Chan*
+bridgeopen(Chan* c, int omode)
+{
+	int perm;
+	Bridge *b;
+
+	omode &= 3;
+	perm = m2p[omode];
+	USED(perm);
+
+	b = bridgetab + c->dev;
+	USED(b);
+
+	switch(TYPE(c->qid)) {
+	default:
+		break;
+	case Qlog:
+		logopen(b);
+		break;
+	case Qcache:
+		c->aux = cachedump(b);
+		break;
+	}
+	c->mode = openmode(omode);
+	c->flag |= COPEN;
+	c->offset = 0;
+	return c;
+}
+
+static void
+bridgeclose(Chan* c)
+{
+	Bridge *b  = bridgetab + c->dev;
+
+	switch(TYPE(c->qid)) {
+	case Qcache:
+		if(c->flag & COPEN)
+			free(c->aux);
+		break;
+	case Qlog:
+		if(c->flag & COPEN)
+			logclose(b);
+		break;
+	}
+}
+
+static long
+bridgeread(Chan *c, void *a, long n, vlong off)
+{
+	char buf[256];
+	Bridge *b = bridgetab + c->dev;
+	Port *port;
+	int i, ingood, outgood;
+
+	USED(off);
+	switch(TYPE(c->qid)) {
+	default:
+		error(Eperm);
+	case Qtopdir:
+	case Qbridgedir:
+	case Qportdir:
+		return devdirread(c, a, n, 0, 0, bridgegen);
+	case Qlog:
+		return logread(b, a, off, n);
+	case Qstatus:
+		qlock(b);
+		port = b->port[PORT(c->qid)];
+		if(port == 0)
+			strcpy(buf, "unbound\n");
+		else {
+			i = 0;
+			switch(port->type) {
+			default: panic("bridgeread: unknown port type: %d", port->type);
+			case Tether:
+				i += snprint(buf+i, sizeof(buf)-i, "ether %s: ", port->name);
+				break;
+			case Ttun:
+				i += snprint(buf+i, sizeof(buf)-i, "tunnel %s: ", port->name);
+				break;
+			}
+			ingood = port->in-port->inmulti-port->inunknown;
+			outgood = port->out-port->outmulti-port->outunknown;
+			i += snprint(buf+i, sizeof(buf)-i, "in=%d(%d:%d:%d) out=%d(%d:%d:%d:%d)\n",
+				port->in, ingood, port->inmulti, port->inunknown,
+				port->out, outgood, port->outmulti, port->outunknown, port->outfrag);
+			USED(i);
+		}
+		n = readstr(off, a, n, buf);
+		qunlock(b);
+		return n;
+	case Qbctl:
+		snprint(buf, sizeof(buf), "%s tcpmss\ndelay %ld %ld\n", b->tcpmss ? "set" : "clear",
+			b->delay0, b->delayn);
+		n = readstr(off, a, n, buf);
+		return n;
+	case Qcache:
+		n = readstr(off, a, n, c->aux);
+		return n;
+	case Qstats:
+		snprint(buf, sizeof(buf), "hit=%uld miss=%uld copy=%uld\n",
+			b->hit, b->miss, b->copy);
+		n = readstr(off, a, n, buf);
+		return n;
+	}
+}
+
+static void
+bridgeoption(Bridge *b, char *option, int value)
+{
+	if(strcmp(option, "tcpmss") == 0)
+		b->tcpmss = value;
+	else
+		error("unknown bridge option");
+}
+
+
+static long
+bridgewrite(Chan *c, void *a, long n, vlong off)
+{
+	Bridge *b = bridgetab + c->dev;
+	Cmdbuf *cb;
+	char *arg0;
+	char *p;
+	
+	USED(off);
+	switch(TYPE(c->qid)) {
+	default:
+		error(Eperm);
+	case Qbctl:
+		cb = parsecmd(a, n);
+		qlock(b);
+		if(waserror()) {
+			qunlock(b);
+			free(cb);
+			nexterror();
+		}
+		if(cb->nf == 0)
+			error("short write");
+		arg0 = cb->f[0];
+		if(strcmp(arg0, "bind") == 0) {
+			portbind(b, cb->nf-1, cb->f+1);
+		} else if(strcmp(arg0, "unbind") == 0) {
+			portunbind(b, cb->nf-1, cb->f+1);
+		} else if(strcmp(arg0, "cacheflush") == 0) {
+			logb(b, Logcache, "cache flush\n");
+			memset(b->cache, 0, CacheSize*sizeof(Centry));
+		} else if(strcmp(arg0, "set") == 0) {
+			if(cb->nf != 2)
+				error("usage: set option");
+			bridgeoption(b, cb->f[1], 1);
+		} else if(strcmp(arg0, "clear") == 0) {
+			if(cb->nf != 2)
+				error("usage: clear option");
+			bridgeoption(b, cb->f[1], 0);
+		} else if(strcmp(arg0, "delay") == 0) {
+			if(cb->nf != 3)
+				error("usage: delay delay0 delayn");
+			b->delay0 = strtol(cb->f[1], nil, 10);
+			b->delayn = strtol(cb->f[2], nil, 10);
+		} else
+			error("unknown control request");
+		poperror();
+		qunlock(b);
+		free(cb);
+		return n;
+	case Qlog:
+		cb = parsecmd(a, n);
+		p = logctl(b, cb->nf, cb->f, logflags);
+		free(cb);
+		if(p != nil)
+			error(p);
+		return n;
+	}
+}
+
+static int
+bridgegen(Chan *c, char *, Dirtab*, int, int s, Dir *dp)
+{
+	Bridge *b = bridgetab + c->dev;
+	int type = TYPE(c->qid);
+	Dirtab *dt;
+	Qid qid;
+
+	if(s  == DEVDOTDOT){
+		switch(TYPE(c->qid)){
+		case Qtopdir:
+		case Qbridgedir:
+			snprint(up->genbuf, sizeof(up->genbuf), "#B%ld", c->dev);
+			mkqid(&qid, Qtopdir, 0, QTDIR);
+			devdir(c, qid, up->genbuf, 0, eve, 0555, dp);
+			break;
+		case Qportdir:
+			snprint(up->genbuf, sizeof(up->genbuf), "bridge%ld", c->dev);
+			mkqid(&qid, Qbridgedir, 0, QTDIR);
+			devdir(c, qid, up->genbuf, 0, eve, 0555, dp);
+			break;
+		default:
+			panic("bridgewalk %llux", c->qid.path);
+		}
+		return 1;
+	}
+
+	switch(type) {
+	default:
+		// non directory entries end up here
+		if(c->qid.type & QTDIR)
+			panic("bridgegen: unexpected directory");	
+		if(s != 0)
+			return -1;
+		dt = dirtab[TYPE(c->qid)];
+		if(dt == nil)
+			panic("bridgegen: unknown type: %lud", TYPE(c->qid));
+		devdir(c, c->qid, dt->name, dt->length, eve, dt->perm, dp);
+		return 1;
+	case Qtopdir:
+		if(s != 0)
+			return -1;
+		snprint(up->genbuf, sizeof(up->genbuf), "bridge%ld", c->dev);
+		mkqid(&qid, QID(0, Qbridgedir), 0, QTDIR);
+		devdir(c, qid, up->genbuf, 0, eve, 0555, dp);
+		return 1;
+	case Qbridgedir:
+		if(s<nelem(bridgedirtab)) {
+			dt = bridgedirtab+s;
+			devdir(c, dt->qid, dt->name, dt->length, eve, dt->perm, dp);
+			return 1;
+		}
+		s -= nelem(bridgedirtab);
+		if(s >= b->nport)
+			return -1;
+		mkqid(&qid, QID(s, Qportdir), 0, QTDIR);
+		snprint(up->genbuf, sizeof(up->genbuf), "%d", s);
+		devdir(c, qid, up->genbuf, 0, eve, 0555, dp);
+		return 1;
+	case Qportdir:
+		if(s>=nelem(portdirtab))
+			return -1;
+		dt = portdirtab+s;
+		mkqid(&qid, QID(PORT(c->qid),TYPE(dt->qid)), 0, QTFILE);
+		devdir(c, qid, dt->name, dt->length, eve, dt->perm, dp);
+		return 1;
+	}
+}
+
+// also in netif.c
+static int
+parseaddr(uchar *to, char *from, int alen)
+{
+	char nip[4];
+	char *p;
+	int i;
+
+	p = from;
+	for(i = 0; i < alen; i++){
+		if(*p == 0)
+			return -1;
+		nip[0] = *p++;
+		if(*p == 0)
+			return -1;
+		nip[1] = *p++;
+		nip[2] = 0;
+		to[i] = strtoul(nip, 0, 16);
+		if(*p == ':')
+			p++;
+	}
+	return 0;
+}
+
+// assumes b is locked
+static void
+portbind(Bridge *b, int argc, char *argv[])
+{
+	Port *port;
+	char path[8*KNAMELEN];
+	char buf[100];
+	char *dev, *dev2=nil, *p;
+	Chan *ctl;
+	int type=0, i, n;
+	char *usage = "usage: bind ether|tunnel name ownhash dev [dev2]";
+	char name[KNAMELEN];
+	ulong ownhash;
+
+	memset(name, 0, KNAMELEN);
+	if(argc < 4)
+		error(usage);
+	if(strcmp(argv[0], "ether") == 0) {
+		if(argc != 4)
+			error(usage);
+		type = Tether;
+		strncpy(name, argv[1], KNAMELEN);
+		name[KNAMELEN-1] = 0;
+//		parseaddr(addr, argv[1], Eaddrlen);
+	} else if(strcmp(argv[0], "tunnel") == 0) {
+		if(argc != 5)
+			error(usage);
+		type = Ttun;
+		strncpy(name, argv[1], KNAMELEN);
+		name[KNAMELEN-1] = 0;
+//		parseip(addr, argv[1]);
+		dev2 = argv[4];
+	} else
+		error(usage);
+	ownhash = atoi(argv[2]);
+	dev = argv[3];
+	for(i=0; i<b->nport; i++) {
+		port = b->port[i];
+		if(port != nil)
+		if(port->type == type)
+		if(memcmp(port->name, name, KNAMELEN) == 0)
+			error("port in use");
+	}
+	for(i=0; i<Maxport; i++)
+		if(b->port[i] == nil)
+			break;
+	if(i == Maxport)
+		error("no more ports");
+	port = smalloc(sizeof(Port));
+	port->ref = 1;
+	port->id = i;
+	port->ownhash = ownhash;
+
+	if(waserror()) {
+		portfree(port);
+		nexterror();
+	}
+	port->type = type;
+	memmove(port->name, name, KNAMELEN);
+	switch(port->type) {
+	default: panic("portbind: unknown port type: %d", type);
+	case Tether:
+		snprint(path, sizeof(path), "%s/clone", dev);
+		ctl = namec(path, Aopen, ORDWR, 0);
+		if(waserror()) {
+			cclose(ctl);
+			nexterror();
+		}
+		// check addr?
+
+		// get directory name
+		n = devtab[ctl->type]->read(ctl, buf, sizeof(buf), 0);
+		buf[n] = 0;
+		for(p = buf; *p == ' '; p++)
+			;
+		snprint(path, sizeof(path), "%s/%lud/data", dev, strtoul(p, 0, 0));
+
+		// setup connection to be promiscuous
+		snprint(buf, sizeof(buf), "connect -1");
+		devtab[ctl->type]->write(ctl, buf, strlen(buf), 0);
+		snprint(buf, sizeof(buf), "promiscuous");
+		devtab[ctl->type]->write(ctl, buf, strlen(buf), 0);
+		snprint(buf, sizeof(buf), "bridge");
+		devtab[ctl->type]->write(ctl, buf, strlen(buf), 0);
+
+		// open data port
+		port->data[0] = namec(path, Aopen, ORDWR, 0);
+		// dup it
+		incref(port->data[0]);
+		port->data[1] = port->data[0];
+
+		poperror();
+		cclose(ctl);		
+
+		break;
+	case Ttun:
+		port->data[0] = namec(dev, Aopen, OREAD, 0);
+		port->data[1] = namec(dev2, Aopen, OWRITE, 0);
+		break;
+	}
+
+	poperror();
+
+	// commited to binding port
+	b->port[port->id] = port;
+	port->bridge = b;
+	if(b->nport <= port->id)
+		b->nport = port->id+1;
+
+	// assumes kproc always succeeds
+	kproc("etherread", etherread, port, 0);	// poperror must be next
+	port->ref++;
+}
+
+// assumes b is locked
+static void
+portunbind(Bridge *b, int argc, char *argv[])
+{
+	Port *port=nil;
+	int type=0, i;
+	char *usage = "usage: unbind ether|tunnel addr [ownhash]";
+	char name[KNAMELEN];
+	ulong ownhash;
+
+	memset(name, 0, KNAMELEN);
+	if(argc < 2 || argc > 3)
+		error(usage);
+	if(strcmp(argv[0], "ether") == 0) {
+		type = Tether;
+		strncpy(name, argv[1], KNAMELEN);
+		name[KNAMELEN-1] = 0;
+//		parseaddr(addr, argv[1], Eaddrlen);
+	} else if(strcmp(argv[0], "tunnel") == 0) {
+		type = Ttun;
+		strncpy(name, argv[1], KNAMELEN);
+		name[KNAMELEN-1] = 0;
+//		parseip(addr, argv[1]);
+	} else
+		error(usage);
+	if(argc == 3)
+		ownhash = atoi(argv[2]);
+	else
+		ownhash = 0;
+	for(i=0; i<b->nport; i++) {
+		port = b->port[i];
+		if(port != nil)
+		if(port->type == type)
+		if(memcmp(port->name, name, KNAMELEN) == 0)
+			break;
+	}
+	if(i == b->nport)
+		error("port not found");
+	if(ownhash != 0 && port->ownhash != 0 && ownhash != port->ownhash)
+		error("bad owner hash");
+
+	port->closed = 1;
+	b->port[i] = nil;	// port is now unbound
+	cacheflushport(b, i);
+
+	// try and stop reader
+	if(port->readp)
+		postnote(port->readp, 1, "unbind", 0);
+	portfree(port);
+}
+
+// assumes b is locked
+static Centry *
+cachelookup(Bridge *b, uchar d[Eaddrlen])
+{
+	int i;
+	uint h;
+	Centry *p;
+	long sec;
+
+	// dont cache multicast or broadcast
+	if(d[0] & 1)
+		return 0;
+
+	h = 0;
+	for(i=0; i<Eaddrlen; i++) {
+		h *= 7;
+		h += d[i];
+	}
+	h %= CacheHash;
+	p = b->cache + h;
+	sec = TK2SEC(m->ticks);
+	for(i=0; i<CacheLook; i++,p++) {
+		if(memcmp(d, p->d, Eaddrlen) == 0) {
+			p->dst++;
+			if(sec >= p->expire) {
+				logb(b, Logcache, "expired cache entry: %E %d\n",
+					d, p->port);
+				return nil;
+			}
+			p->expire = sec + CacheTimeout;
+			return p;
+		}
+	}
+	logb(b, Logcache, "cache miss: %E\n", d);
+	return nil;
+}
+
+// assumes b is locked
+static void
+cacheupdate(Bridge *b, uchar d[Eaddrlen], int port)
+{
+	int i;
+	uint h;
+	Centry *p, *pp;
+	long sec;
+
+	// dont cache multicast or broadcast
+	if(d[0] & 1) {
+		logb(b, Logcache, "bad source address: %E\n", d);
+		return;
+	}
+	
+	h = 0;
+	for(i=0; i<Eaddrlen; i++) {
+		h *= 7;
+		h += d[i];
+	}
+	h %= CacheHash;
+	p = b->cache + h;
+	pp = p;
+	sec = p->expire;
+
+	// look for oldest entry
+	for(i=0; i<CacheLook; i++,p++) {
+		if(memcmp(p->d, d, Eaddrlen) == 0) {
+			p->expire = TK2SEC(m->ticks) + CacheTimeout;
+			if(p->port != port) {
+				logb(b, Logcache, "NIC changed port %d->%d: %E\n",
+					p->port, port, d);
+				p->port = port;
+			}
+			p->src++;
+			return;
+		}
+		if(p->expire < sec) {
+			sec = p->expire;
+			pp = p;
+		}
+	}
+	if(pp->expire != 0)
+		logb(b, Logcache, "bumping from cache: %E %d\n", pp->d, pp->port);
+	pp->expire = TK2SEC(m->ticks) + CacheTimeout;
+	memmove(pp->d, d, Eaddrlen);
+	pp->port = port;
+	pp->src = 1;
+	pp->dst = 0;
+	logb(b, Logcache, "adding to cache: %E %d\n", pp->d, pp->port);
+}
+
+// assumes b is locked
+static void
+cacheflushport(Bridge *b, int port)
+{
+	Centry *ce;
+	int i;
+
+	ce = b->cache;
+	for(i=0; i<CacheSize; i++,ce++) {
+		if(ce->port != port)
+			continue;
+		memset(ce, 0, sizeof(Centry));
+	}
+}
+
+static char *
+cachedump(Bridge *b)
+{
+	int i, n;
+	long sec, off;
+	char *buf, *p, *ep;
+	Centry *ce;
+	char c;
+
+	qlock(b);
+	if(waserror()) {
+		qunlock(b);
+		nexterror();
+	}
+	sec = TK2SEC(m->ticks);
+	n = 0;
+	for(i=0; i<CacheSize; i++)
+		if(b->cache[i].expire != 0)
+			n++;
+	
+	n *= 51;	// change if print format is changed
+	n += 10;	// some slop at the end
+	buf = malloc(n);
+	p = buf;
+	ep = buf + n;
+	ce = b->cache;
+	off = seconds() - sec;
+	for(i=0; i<CacheSize; i++,ce++) {
+		if(ce->expire == 0)
+			continue;	
+		c = (sec < ce->expire)?'v':'e';
+		p += snprint(p, ep-p, "%E %2d %10ld %10ld %10ld %c\n", ce->d,
+			ce->port, ce->src, ce->dst, ce->expire+off, c);
+	}
+	*p = 0;
+	poperror();
+	qunlock(b);
+
+	return buf;
+}
+
+
+
+// assumes b is locked
+static void
+ethermultiwrite(Bridge *b, Block *bp, Port *port)
+{
+	Port *oport;
+	Block *bp2;
+	Etherpkt *ep;
+	int i, mcast, bcast;
+	static uchar bcastaddr[Eaddrlen] = {0xff, 0xff, 0xff, 0xff, 0xff, 0xff};
+
+	if(waserror()) {
+		if(bp)
+			freeb(bp);
+		nexterror();
+	}
+	
+	ep = (Etherpkt*)bp->rp;
+	mcast = ep->d[0] & 1;
+	if(mcast)
+		bcast = memcmp(ep->d, bcastaddr, Eaddrlen) == 0;
+	else
+		bcast = 0;
+
+	oport = nil;
+	for(i=0; i<b->nport; i++) {
+		if(i == port->id || b->port[i] == nil)
+			continue;
+		if(mcast && !bcast && !b->port[i]->mcast)
+			continue;
+		if(mcast)
+			b->port[i]->outmulti++;
+		else
+			b->port[i]->outunknown++;
+
+		// delay one so that the last write does not copy
+		if(oport != nil) {
+			b->copy++;
+			bp2 = copyblock(bp, blocklen(bp));
+			if(!waserror()) {
+				etherwrite(oport, bp2);
+				poperror();
+			}
+		}
+		oport = b->port[i];
+	}
+
+	// last write free block
+	if(oport) {
+		bp2 = bp; bp = nil; USED(bp);
+		if(!waserror()) {
+			etherwrite(oport, bp2);
+			poperror();
+		}
+	} else
+		freeb(bp);
+
+	poperror();
+}
+
+static void
+tcpmsshack(Etherpkt *epkt, int n)
+{
+	int hl;
+	Iphdr *iphdr;
+	Tcphdr *tcphdr;
+	ulong mss;
+	ulong cksum;
+	int optlen;
+	uchar *optr;
+
+	// check it is an ip packet
+	if(nhgets(epkt->type) != 0x800)
+		return;
+	iphdr = (Iphdr*)(epkt->data);
+	n -= ETHERHDRSIZE;
+	if(n < IPHDR)
+		return;
+
+	// check it is ok IP packet
+	if(iphdr->vihl != (IP_VER|IP_HLEN)) {
+		hl = (iphdr->vihl&0xF)<<2;
+		if((iphdr->vihl&0xF0) != IP_VER || hl < (IP_HLEN<<2))
+			return;
+	} else
+		hl = IP_HLEN<<2;
+
+	// check TCP
+	if(iphdr->proto != IP_TCPPROTO)
+		return;
+	n -= hl;
+	if(n < sizeof(Tcphdr))
+		return;
+	tcphdr = (Tcphdr*)((uchar*)(iphdr) + hl);
+	// MSS can only appear in SYN packet
+	if(!(tcphdr->flag[1] & SYN))
+		return;
+	hl = (tcphdr->flag[0] & 0xf0)>>2;
+	if(n < hl)
+		return;
+
+	// check for MSS option
+	optr = (uchar*)(tcphdr) + sizeof(Tcphdr);
+	n = hl - sizeof(Tcphdr);
+	for(;;) {
+		if(n <= 0 || *optr == EOLOPT)
+			return;
+		if(*optr == NOOPOPT) {
+			n--;
+			optr++;
+			continue;
+		}
+		optlen = optr[1];
+		if(optlen < 2 || optlen > n)
+			return;
+		if(*optr == MSSOPT && optlen == MSS_LENGTH)
+			break;
+		n -= optlen;
+		optr += optlen;
+	}
+
+	mss = nhgets(optr+2);
+	if(mss <= TcpMssMax)
+		return;
+	// fit checksum
+	cksum = nhgets(tcphdr->cksum);
+	if(optr-(uchar*)tcphdr & 1) {
+print("tcpmsshack: odd alignment!\n");
+		// odd alignments are a pain
+		cksum += nhgets(optr+1);
+		cksum -= (optr[1]<<8)|(TcpMssMax>>8);
+		cksum += (cksum>>16);
+		cksum &= 0xffff;
+		cksum += nhgets(optr+3);
+		cksum -= ((TcpMssMax&0xff)<<8)|optr[4];
+		cksum += (cksum>>16);
+	} else {
+		cksum += mss;
+		cksum -= TcpMssMax;
+		cksum += (cksum>>16);
+	}
+	hnputs(tcphdr->cksum, cksum);
+	hnputs(optr+2, TcpMssMax);
+}
+
+/*
+ *  process to read from the ethernet
+ */
+static void
+etherread(void *a)
+{
+	Port *port = a;
+	Bridge *b = port->bridge;
+	Block *bp, *bp2;
+	Etherpkt *ep;
+	Centry *ce;
+	long md;
+	
+	qlock(b);
+	port->readp = up;	/* hide identity under a rock for unbind */
+
+	while(!port->closed){
+		// release lock to read - error means it is time to quit
+		qunlock(b);
+		if(waserror()) {
+print("etherread read error: %s\n", up->env->errstr);
+			qlock(b);
+			break;
+		}
+if(0)print("devbridge: etherread: reading\n");
+		bp = devtab[port->data[0]->type]->bread(port->data[0], ETHERMAXTU, 0);
+if(0)print("devbridge: etherread: blocklen = %d\n", blocklen(bp));
+		poperror();
+		qlock(b);
+		if(bp == nil || port->closed)
+			break;
+		if(waserror()) {
+//print("etherread bridge error\n");
+			if(bp)
+				freeb(bp);
+			continue;
+		}
+		if(blocklen(bp) < ETHERMINTU)
+			error("short packet");
+		port->in++;
+
+		ep = (Etherpkt*)bp->rp;
+		cacheupdate(b, ep->s, port->id);
+		if(b->tcpmss)
+			tcpmsshack(ep, BLEN(bp));
+
+		/*
+		 * delay packets to simulate a slow link
+		 */
+		if(b->delay0 || b->delayn){
+			md = b->delay0 + b->delayn * BLEN(bp);
+			if(md > 0)
+				microdelay(md);
+		}
+
+		if(ep->d[0] & 1) {
+			logb(b, Logmcast, "multicast: port=%d src=%E dst=%E type=%#.4ux\n",
+				port->id, ep->s, ep->d, (ep->type[0]<<8)|ep->type[1] );
+			port->inmulti++;
+			bp2 = bp; bp = nil;
+			ethermultiwrite(b, bp2, port);
+		} else {
+			ce = cachelookup(b, ep->d);
+			if(ce == nil) {
+				b->miss++;
+				port->inunknown++;
+				bp2 = bp; bp = nil;
+				ethermultiwrite(b, bp2, port);
+			}else if(ce->port != port->id){
+				b->hit++;
+				bp2 = bp; bp = nil;
+				etherwrite(b->port[ce->port], bp2);
+			}
+		}
+
+		poperror();
+		if(bp)
+			freeb(bp);
+	}
+//print("etherread: trying to exit\n");
+	port->readp = nil;
+	portfree(port);
+	qunlock(b);
+	pexit("hangup", 1);
+}
+
+static int
+fragment(Etherpkt *epkt, int n)
+{
+	Iphdr *iphdr;
+
+	if(n <= TunnelMtu)
+		return 0;
+
+	// check it is an ip packet
+	if(nhgets(epkt->type) != 0x800)
+		return 0;
+	iphdr = (Iphdr*)(epkt->data);
+	n -= ETHERHDRSIZE;
+	if(n < IPHDR)
+		return 0;
+
+	// check it is ok IP packet - I don't handle IP options for the momment
+	if(iphdr->vihl != (IP_VER|IP_HLEN))
+		return 0;
+
+	// check for don't fragment
+	if(iphdr->frag[0] & (IP_DF>>8))
+		return 0;
+
+	// check for short block
+	if(nhgets(iphdr->length) > n)
+		return 0;
+
+	return 1;
+}
+
+
+static void
+etherwrite(Port *port, Block *bp)
+{
+	Iphdr *eh, *feh;
+	Etherpkt *epkt;
+	int n, lid, len, seglen, chunk, dlen, blklen, offset, mf;
+	Block *xp, *nb;
+	ushort fragoff, frag;
+
+	port->out++;
+	epkt = (Etherpkt*)bp->rp;
+	n = blocklen(bp);
+	if(port->type != Ttun || !fragment(epkt, n)) {
+		devtab[port->data[1]->type]->bwrite(port->data[1], bp, 0);
+		return;
+	}
+	port->outfrag++;
+	if(waserror()){
+		freeblist(bp);	
+		nexterror();
+	}
+
+	seglen = (TunnelMtu - ETHERHDRSIZE - IPHDR) & ~7;
+	eh = (Iphdr*)(epkt->data);
+	len = nhgets(eh->length);
+	frag = nhgets(eh->frag);
+	mf = frag & IP_MF;
+	frag <<= 3;
+	dlen = len - IPHDR;
+	xp = bp;
+	lid = nhgets(eh->id);
+	offset = ETHERHDRSIZE+IPHDR;
+	while(xp != nil && offset && offset >= BLEN(xp)) {
+		offset -= BLEN(xp);
+		xp = xp->next;
+	}
+	xp->rp += offset;
+	
+if(0) print("seglen=%d, dlen=%d, mf=%x, frag=%d\n", seglen, dlen, mf, frag);
+	for(fragoff = 0; fragoff < dlen; fragoff += seglen) {
+		nb = allocb(ETHERHDRSIZE+IPHDR+seglen);
+		
+		feh = (Iphdr*)(nb->wp+ETHERHDRSIZE);
+
+		memmove(nb->wp, epkt, ETHERHDRSIZE+IPHDR);
+		nb->wp += ETHERHDRSIZE+IPHDR;
+
+		if((fragoff + seglen) >= dlen) {
+			seglen = dlen - fragoff;
+			hnputs(feh->frag, (frag+fragoff)>>3 | mf);
+		}
+		else	
+			hnputs(feh->frag, (frag+fragoff>>3) | IP_MF);
+
+		hnputs(feh->length, seglen + IPHDR);
+		hnputs(feh->id, lid);
+
+		/* Copy up the data area */
+		chunk = seglen;
+		while(chunk) {
+			blklen = chunk;
+			if(BLEN(xp) < chunk)
+				blklen = BLEN(xp);
+			memmove(nb->wp, xp->rp, blklen);
+			nb->wp += blklen;
+			xp->rp += blklen;
+			chunk -= blklen;
+			if(xp->rp == xp->wp)
+				xp = xp->next;
+		} 
+
+		feh->cksum[0] = 0;
+		feh->cksum[1] = 0;
+		hnputs(feh->cksum, ipcsum(&feh->vihl));
+	
+		// don't generate small packets
+		if(BLEN(nb) < ETHERMINTU)
+			nb->wp = nb->rp + ETHERMINTU;
+		devtab[port->data[1]->type]->bwrite(port->data[1], nb, 0);
+	}
+	poperror();
+	freeblist(bp);	
+}
+
+// hold b lock
+static void
+portfree(Port *port)
+{
+	port->ref--;
+	if(port->ref < 0)
+		panic("portfree: bad ref");
+	if(port->ref > 0)
+		return;
+
+	if(port->data[0])
+		cclose(port->data[0]);
+	if(port->data[1])
+		cclose(port->data[1]);
+	memset(port, 0, sizeof(Port));
+	free(port);
+}
+
+Dev bridgedevtab = {
+	'B',
+	"bridge",
+
+	devreset,
+	bridgeinit,
+	devshutdown,
+	bridgeattach,
+	bridgewalk,
+	bridgestat,
+	bridgeopen,
+	devcreate,
+	bridgeclose,
+	bridgeread,
+	devbread,
+	bridgewrite,
+	devbwrite,
+	devremove,
+	devwstat,
+};
--- /dev/null
+++ b/os/port/devcap.c
@@ -1,0 +1,248 @@
+#include	"u.h"
+#include	"../port/lib.h"
+#include	"mem.h"
+#include	"dat.h"
+#include	"fns.h"
+#include	"error.h"
+#include	"mp.h"
+#include	"libsec.h"
+
+/*
+ * Copyright © 2003 Vita Nuova Holdings Limited.  All rights reserved.
+ */
+
+enum {
+	Captimeout = 15,	/* seconds until expiry */
+	Capidletime = 60	/* idle seconds before capwatch exits */
+};
+
+typedef struct Caps Caps;
+struct Caps
+{
+	uchar	hash[SHA1dlen];
+	ulong	time;
+	Caps*	next;
+};
+
+struct {
+	QLock	l;
+	Caps*	caps;
+	int	kpstarted;
+} allcaps;
+
+enum {
+	Qdir,
+	Qhash,
+	Quse
+};
+
+static Dirtab capdir[] =
+{
+	".",			{Qdir, 0, QTDIR},	0,	DMDIR|0555,
+	"capuse",		{Quse, 0},			0,	0222,
+	"caphash",	{Qhash, 0},		0,	0200,
+};
+
+static int ncapdir = nelem(capdir);
+
+static void
+capwatch(void *a)
+{
+	Caps *c, **l;
+	int idletime;
+
+	USED(a);
+	idletime = 0;
+	for(;;){
+		tsleep(&up->sleep, return0, nil, 30*1000);
+		qlock(&allcaps.l);
+		for(l = &allcaps.caps; (c = *l) != nil;)
+			if(++c->time > Captimeout){
+				*l = c->next;
+				free(c);
+			}else
+				l = &c->next;
+		if(allcaps.caps == nil){
+			if(++idletime > Capidletime){
+				allcaps.kpstarted = 0;
+				qunlock(&allcaps.l);
+				pexit("", 0);
+			}
+		}else
+			idletime = 0;
+		qunlock(&allcaps.l);
+	}
+}
+
+static Chan *
+capattach(char *spec)
+{
+	return devattach(L'¤', spec);
+}
+
+static Walkqid*
+capwalk(Chan *c, Chan *nc, char **name, int nname)
+{
+	return devwalk(c, nc, name, nname, capdir, nelem(capdir), devgen);
+}
+
+static int
+capstat(Chan *c, uchar *db, int n)
+{
+	return devstat(c, db, n, capdir, nelem(capdir), devgen);
+}
+
+static Chan*
+capopen(Chan *c, int omode)
+{
+	if(c->qid.type & QTDIR) {
+		if(omode != OREAD)
+			error(Eisdir);
+		c->mode = omode;
+		c->flag |= COPEN;
+		c->offset = 0;
+		return c;
+	}
+
+	if(c->qid.path == Qhash && !iseve())
+		error(Eperm);
+
+	c->mode = openmode(omode);
+	c->flag |= COPEN;
+	c->offset = 0;
+	return c;
+}
+
+static void
+capclose(Chan *c)
+{
+	USED(c);
+}
+
+static long
+capread(Chan *c, void *va, long n, vlong vl)
+{
+	USED(vl);
+	switch((ulong)c->qid.path){
+	case Qdir:
+		return devdirread(c, va, n, capdir, ncapdir, devgen);
+
+	default:
+		error(Eperm);
+		break;
+	}
+	return n;
+}
+
+static int
+capwritehash(uchar *a, int l)
+{
+	Caps *c;
+
+	if(l != SHA1dlen)
+		return -1;
+	c = malloc(sizeof(*c));
+	if(c == nil)
+		return -1;
+	memmove(c->hash, a, l);
+	c->time = 0;
+	qlock(&allcaps.l);
+	c->next = allcaps.caps;
+	allcaps.caps = c;
+	if(!allcaps.kpstarted){
+		allcaps.kpstarted = 1;
+		kproc("capwatch", capwatch, 0, 0);
+	}
+	qunlock(&allcaps.l);
+	return 0;
+}
+
+static int
+capwriteuse(uchar *a, int len)
+{
+	int n;
+	uchar digest[SHA1dlen];
+	char buf[128], *p, *users[3];
+	Caps *c, **l;
+
+	if(len >= sizeof(buf)-1)
+		return -1;
+	memmove(buf, a, len);
+	buf[len] = 0;
+	p = strrchr(buf, '@');
+	if(p == nil)
+		return -1;
+	*p++ = 0;
+	len = strlen(p);
+	n = strlen(buf);
+	if(len == 0 || n == 0)
+		return -1;
+	hmac_sha1((uchar*)buf, n, (uchar*)p, len, digest, nil);
+	n = getfields(buf, users, nelem(users), 0, "@");
+	if(n == 1)
+		users[1] = users[0];
+	else if(n != 2)
+		return -1;
+	if(*users[0] == 0 || *users[1] == 0)
+		return -1;
+	qlock(&allcaps.l);
+	for(l = &allcaps.caps; (c = *l) != nil; l = &c->next)
+		if(memcmp(c->hash, digest, sizeof(c->hash)) == 0){
+			*l = c->next;
+			qunlock(&allcaps.l);
+			free(c);
+			if(n == 2 && strcmp(up->env->user, users[0]) != 0)
+				return -1;
+			kstrdup(&up->env->user, users[1]);
+			return 0;
+		}
+	qunlock(&allcaps.l);
+	return -1;
+}
+
+static long	 
+capwrite(Chan* c, void* buf, long n, vlong offset)
+{
+	USED(offset);
+	switch((ulong)c->qid.path){
+	case Qhash:
+		if(capwritehash(buf, n) < 0)
+			error(Ebadarg);
+		return n;
+	case Quse:
+		if(capwriteuse(buf, n) < 0)
+			error("invalid capability");
+		return n;
+	}
+	error(Ebadarg);
+	return 0;
+}
+
+static void
+capremove(Chan *c)
+{
+	if(c->qid.path != Qhash || !iseve())
+		error(Eperm);
+	ncapdir = nelem(capdir)-1;
+}
+
+Dev capdevtab = {
+	L'¤',
+	"cap",
+
+	devreset,
+	devinit,
+	devshutdown,
+	capattach,
+	capwalk,
+	capstat,
+	capopen,
+	devcreate,
+	capclose,
+	capread,
+	devbread,
+	capwrite,
+	devbwrite,
+	capremove,
+	devwstat
+};
--- /dev/null
+++ b/os/port/devcons.c
@@ -1,0 +1,1259 @@
+#include	"u.h"
+#include	"../port/lib.h"
+#include	"mem.h"
+#include	"dat.h"
+#include	"fns.h"
+#include	"../port/error.h"
+#include	<version.h>
+#include	"mp.h"
+#include	"libsec.h"
+#include	"keyboard.h"
+
+extern int cflag;
+extern int keepbroken;
+
+void	(*serwrite)(char *, int);
+
+Queue*	kscanq;			/* keyboard raw scancodes (when needed) */
+char*	kscanid;		/* name of raw scan format (if defined) */
+Queue*	kbdq;			/* unprocessed console input */
+Queue*	lineq;			/* processed console input */
+Queue*	printq;			/* console output */
+Queue*	klogq;			/* kernel print (log) output */
+int	iprintscreenputs;
+
+static struct
+{
+	RWlock;
+	Queue*	q;
+} kprintq;
+
+static struct
+{
+	QLock;
+
+	int	raw;		/* true if we shouldn't process input */
+	int	ctl;		/* number of opens to the control file */
+	int	kbdr;		/* number of open reads to the keyboard */
+	int	scan;		/* true if reading raw scancodes */
+	int	x;		/* index into line */
+	char	line[1024];	/* current input line */
+
+	char	c;
+	int	count;
+	int	repeat;
+} kbd;
+
+char*	sysname;
+char*	eve;
+
+enum
+{
+	CMreboot,
+	CMhalt,
+	CMpanic,
+	CMbroken,
+	CMnobroken,
+	CMconsole,
+};
+
+static Cmdtab sysctlcmd[] =
+{
+	CMreboot,	"reboot",	0,
+	CMhalt,	"halt", 0,
+	CMpanic,	"panic", 0,
+	CMconsole,	"console", 1,
+	CMbroken,	"broken", 0,
+	CMnobroken,	"nobroken", 0,
+};
+
+void
+printinit(void)
+{
+	lineq = qopen(2*1024, 0, nil, nil);
+	if(lineq == nil)
+		panic("printinit");
+	qnoblock(lineq, 1);
+}
+
+/*
+ *  return true if current user is eve
+ */
+int
+iseve(void)
+{
+	Osenv *o;
+
+	o = up->env;
+	return strcmp(eve, o->user) == 0;
+}
+
+static int
+consactive(void)
+{
+	if(printq)
+		return qlen(printq) > 0;
+	return 0;
+}
+
+static void
+prflush(void)
+{
+	ulong now;
+
+	now = m->ticks;
+	while(serwrite==nil && consactive())
+		if(m->ticks - now >= HZ)
+			break;
+}
+
+/*
+ *   Print a string on the console.  Convert \n to \r\n for serial
+ *   line consoles.  Locking of the queues is left up to the screen
+ *   or uart code.  Multi-line messages to serial consoles may get
+ *   interspersed with other messages.
+ */
+static void
+putstrn0(char *str, int n, int usewrite)
+{
+	int m;
+	char *t;
+	char buf[PRINTSIZE+2];
+
+	/*
+	 *  if kprint is open, put the message there, otherwise
+	 *  if there's an attached bit mapped display,
+	 *  put the message there.
+	 */
+	m = consoleprint;
+	if(canrlock(&kprintq)){
+		if(kprintq.q != nil){
+			if(waserror()){
+				runlock(&kprintq);
+				nexterror();
+			}
+			if(usewrite)
+				qwrite(kprintq.q, str, n);
+			else
+				qiwrite(kprintq.q, str, n);
+			poperror();
+			m = 0;
+		}
+		runlock(&kprintq);
+	}
+	if(m && screenputs != nil)
+		screenputs(str, n);
+
+	/*
+	 *  if there's a serial line being used as a console,
+	 *  put the message there.
+	 */
+	if(serwrite != nil) {
+		serwrite(str, n);
+		return;
+	}
+
+	if(printq == 0)
+		return;
+
+	while(n > 0) {
+		t = memchr(str, '\n', n);
+		if(t && !kbd.raw) {
+			m = t - str;
+			if(m > sizeof(buf)-2)
+				m = sizeof(buf)-2;
+			memmove(buf, str, m);
+			buf[m] = '\r';
+			buf[m+1] = '\n';
+			if(usewrite)
+				qwrite(printq, buf, m+2);
+			else
+				qiwrite(printq, buf, m+2);
+			str = t + 1;
+			n -= m + 1;
+		} else {
+			if(usewrite)
+				qwrite(printq, str, n);
+			else 
+				qiwrite(printq, str, n);
+			break;
+		}
+	}
+}
+
+void
+putstrn(char *str, int n)
+{
+	putstrn0(str, n, 0);
+}
+
+int
+snprint(char *s, int n, char *fmt, ...)
+{
+	va_list arg;
+
+	va_start(arg, fmt);
+	n = vseprint(s, s+n, fmt, arg) - s;
+	va_end(arg);
+
+	return n;
+}
+
+int
+sprint(char *s, char *fmt, ...)
+{
+	int n;
+	va_list arg;
+
+	va_start(arg, fmt);
+	n = vseprint(s, s+PRINTSIZE, fmt, arg) - s;
+	va_end(arg);
+
+	return n;
+}
+
+int
+print(char *fmt, ...)
+{
+	int n;
+	va_list arg;
+	char buf[PRINTSIZE];
+
+	va_start(arg, fmt);
+	n = vseprint(buf, buf+sizeof(buf), fmt, arg) - buf;
+	va_end(arg);
+	putstrn(buf, n);
+
+	return n;
+}
+
+int
+fprint(int fd, char *fmt, ...)
+{
+	int n;
+	va_list arg;
+	char buf[PRINTSIZE];
+
+	USED(fd);
+	va_start(arg, fmt);
+	n = vseprint(buf, buf+sizeof(buf), fmt, arg) - buf;
+	va_end(arg);
+	putstrn(buf, n);
+
+	return n;
+}
+
+int
+kprint(char *fmt, ...)
+{
+	va_list arg;
+	char buf[PRINTSIZE];
+	int n;
+
+	va_start(arg, fmt);
+	n = vseprint(buf, buf+sizeof(buf), fmt, arg) - buf;
+	va_end(arg);
+	if(qfull(klogq))
+		qflush(klogq);
+	return qproduce(klogq, buf, n);
+}
+
+int
+iprint(char *fmt, ...)
+{
+	int n, s;
+	va_list arg;
+	char buf[PRINTSIZE];
+
+	s = splhi();
+	va_start(arg, fmt);
+	n = vseprint(buf, buf+sizeof(buf), fmt, arg) - buf;
+	va_end(arg);
+	if(screenputs != nil && iprintscreenputs)
+		screenputs(buf, n);
+	uartputs(buf, n);
+	splx(s);
+
+	return n;
+}
+
+void
+panic(char *fmt, ...)
+{
+	int n;
+	va_list arg;
+	char buf[PRINTSIZE];
+
+	setpanic();
+	kprintq.q = nil;
+	strcpy(buf, "panic: ");
+	va_start(arg, fmt);
+	n = vseprint(buf+strlen(buf), buf+sizeof(buf)-1, fmt, arg) - buf;
+	va_end(arg);
+	buf[n] = '\n';
+	putstrn(buf, n+1);
+	spllo();
+	dumpstack();
+
+	exit(1);
+}
+
+void
+_assert(char *fmt)
+{
+	panic("assert failed: %s", fmt);
+}
+
+/*
+ * mainly for libmp
+ */
+void
+sysfatal(char *fmt, ...)
+{
+	va_list arg;
+	char buf[64];
+
+	va_start(arg, fmt);
+	vsnprint(buf, sizeof(buf), fmt, arg);
+	va_end(arg);
+	error(buf);
+}
+
+int
+pprint(char *fmt, ...)
+{
+	int n;
+	Chan *c;
+	Osenv *o;
+	va_list arg;
+	char buf[2*PRINTSIZE];
+
+	n = sprint(buf, "%s %ld: ", up->text, up->pid);
+	va_start(arg, fmt);
+	n = vseprint(buf+n, buf+sizeof(buf), fmt, arg) - buf;
+	va_end(arg);
+
+	o = up->env;
+	if(o->fgrp == 0) {
+		print("%s", buf);
+		return 0;
+	}
+	c = o->fgrp->fd[2];
+	if(c==0 || (c->mode!=OWRITE && c->mode!=ORDWR)) {
+		print("%s", buf);
+		return 0;
+	}
+
+	if(waserror()) {
+		print("%s", buf);
+		return 0;
+	}
+	devtab[c->type]->write(c, buf, n, c->offset);
+	poperror();
+
+	lock(c);
+	c->offset += n;
+	unlock(c);
+
+	return n;
+}
+
+void
+echo(Rune r, char *buf, int n)
+{
+	if(kbd.raw)
+		return;
+
+	if(r == '\n'){
+		if(printq)
+			qiwrite(printq, "\r", 1);
+	} else if(r == 0x15){
+		buf = "^U\n";
+		n = 3;
+	}
+	if(consoleprint && screenputs != nil)
+		screenputs(buf, n);
+	if(printq)
+		qiwrite(printq, buf, n);
+}
+
+/*
+ *	Debug key support.  Allows other parts of the kernel to register debug
+ *	key handlers, instead of devcons.c having to know whatever's out there.
+ *	A kproc is used to invoke most handlers, rather than tying up the CPU at
+ *	splhi, which can choke some device drivers (eg softmodem).
+ */
+typedef struct {
+	Rune	r;
+	char	*m;
+	void	(*f)(Rune);
+	int	i;	/* function called at interrupt time */
+} Dbgkey;
+
+static struct {
+	Rendez;
+	Dbgkey	*work;
+	Dbgkey	keys[50];
+	int	nkeys;
+	int	on;
+} dbg;
+
+static Dbgkey *
+finddbgkey(Rune r)
+{
+	int i;
+	Dbgkey *dp;
+
+	for(dp = dbg.keys, i = 0; i < dbg.nkeys; i++, dp++)
+		if(dp->r == r)
+			return dp;
+	return nil;
+}
+
+static int
+dbgwork(void *)
+{
+	return dbg.work != 0;
+}
+
+static void
+dbgproc(void *)
+{
+	Dbgkey *dp;
+
+	setpri(PriRealtime);
+	for(;;) {
+		do {
+			sleep(&dbg, dbgwork, 0);
+			dp = dbg.work;
+		} while(dp == nil);
+		dp->f(dp->r);
+		dbg.work = nil;
+	}
+}
+
+void
+debugkey(Rune r, char *msg, void (*fcn)(), int iflag)
+{
+	Dbgkey *dp;
+
+	if(dbg.nkeys >= nelem(dbg.keys))
+		return;
+	if(finddbgkey(r) != nil)
+		return;
+	for(dp = &dbg.keys[dbg.nkeys++] - 1; dp >= dbg.keys; dp--) {
+		if(strcmp(dp->m, msg) < 0)
+			break;
+		dp[1] = dp[0];
+	}
+	dp++;
+	dp->r = r;
+	dp->m = msg;
+	dp->f = fcn;
+	dp->i = iflag;
+}
+
+static int
+isdbgkey(Rune r)
+{
+	static int ctrlt;
+	Dbgkey *dp;
+	int echoctrlt = ctrlt;
+
+	/*
+	 * ^t hack BUG
+	 */
+	if(dbg.on || (ctrlt >= 2)) {
+		if(r == 0x14 || r == 0x05) {
+			ctrlt++;
+			return 0;
+		}
+		if(dp = finddbgkey(r)) {
+			if(dp->i || ctrlt > 2)
+				dp->f(r);
+			else {
+				dbg.work = dp;
+				wakeup(&dbg);
+			}
+			ctrlt = 0;
+			return 1;
+		}
+		ctrlt = 0;
+	}
+	else if(r == 0x14){
+		ctrlt++;
+		return 1;
+	}
+	else
+		ctrlt = 0;
+	if(echoctrlt){
+		char buf[UTFmax];
+
+		buf[0] = 0x14;
+		while(--echoctrlt >= 0){
+			echo(buf[0], buf, 1);
+			qproduce(kbdq, buf, 1);
+		}
+	}
+	return 0;
+}
+
+static void
+dbgtoggle(Rune)
+{
+	dbg.on = !dbg.on;
+	print("Debug keys %s\n", dbg.on ? "HOT" : "COLD");
+}
+
+static void
+dbghelp(void)
+{
+	int i;
+	Dbgkey *dp;
+	Dbgkey *dp2;
+	static char fmt[] = "%c: %-22s";
+
+	dp = dbg.keys;
+	dp2 = dp + (dbg.nkeys + 1)/2;
+	for(i = dbg.nkeys; i > 1; i -= 2, dp++, dp2++) {
+		print(fmt, dp->r, dp->m);
+		print(fmt, dp2->r, dp2->m);
+		print("\n");
+	}
+	if(i)
+		print(fmt, dp->r, dp->m);
+	print("\n");
+}
+
+static void
+debuginit(void)
+{
+	kproc("consdbg", dbgproc, nil, 0);
+	debugkey('|', "HOT|COLD keys", dbgtoggle, 0);
+	debugkey('?', "help", dbghelp, 0);
+}
+
+/*
+ *  Called by a uart interrupt for console input.
+ *
+ *  turn '\r' into '\n' before putting it into the queue.
+ */
+int
+kbdcr2nl(Queue *q, int ch)
+{
+	if(ch == '\r')
+		ch = '\n';
+	return kbdputc(q, ch);
+}
+
+/*
+ *  Put character, possibly a rune, into read queue at interrupt time.
+ *  Performs translation for compose sequences
+ *  Called at interrupt time to process a character.
+ */
+int
+kbdputc(Queue *q, int ch)
+{
+	int n;
+	char buf[UTFmax];
+	Rune r;
+	static Rune kc[15];
+	static int nk, collecting = 0;
+
+	r = ch;
+	if(r == Latin) {
+		collecting = 1;
+		nk = 0;
+		return 0;
+	}
+	if(collecting) {
+		int c;
+		nk += runetochar((char*)&kc[nk], &r);
+		c = latin1(kc, nk);
+		if(c < -1)	/* need more keystrokes */
+			return 0;
+		collecting = 0;
+		if(c == -1) {	/* invalid sequence */
+			echo(kc[0], (char*)kc, nk);
+			qproduce(q, kc, nk);
+			return 0;
+		}
+		r = (Rune)c;
+	}
+	kbd.c = r;
+	n = runetochar(buf, &r);
+	if(n == 0)
+		return 0;
+	if(!isdbgkey(r)) {
+		echo(r, buf, n);
+		qproduce(q, buf, n);
+	}
+	return 0;
+}
+
+void
+kbdrepeat(int rep)
+{
+	kbd.repeat = rep;
+	kbd.count = 0;
+}
+
+void
+kbdclock(void)
+{
+	if(kbd.repeat == 0)
+		return;
+	if(kbd.repeat==1 && ++kbd.count>HZ){
+		kbd.repeat = 2;
+		kbd.count = 0;
+		return;
+	}
+	if(++kbd.count&1)
+		kbdputc(kbdq, kbd.c);
+}
+
+enum{
+	Qdir,
+	Qcons,
+	Qsysctl,
+	Qconsctl,
+	Qdrivers,
+	Qhostowner,
+	Qkeyboard,
+	Qklog,
+	Qkprint,
+	Qscancode,
+	Qmemory,
+	Qmsec,
+	Qnull,
+	Qrandom,
+	Qnotquiterandom,
+	Qsysname,
+	Qtime,
+	Quser,
+	Qjit,
+};
+
+static Dirtab consdir[]=
+{
+	".",	{Qdir, 0, QTDIR},	0,		DMDIR|0555,
+	"cons",		{Qcons},	0,		0660,
+	"consctl",	{Qconsctl},	0,		0220,
+	"sysctl",	{Qsysctl},	0,		0644,
+	"drivers",	{Qdrivers},	0,		0444,
+	"hostowner",	{Qhostowner},	0,	0644,
+	"keyboard",	{Qkeyboard},	0,		0666,
+	"klog",		{Qklog},	0,		0444,
+	"kprint",		{Qkprint},	0,		0444,
+	"scancode",	{Qscancode},	0,		0444,
+	"memory",	{Qmemory},	0,		0444,
+	"msec",		{Qmsec},	NUMSIZE,	0444,
+	"null",		{Qnull},	0,		0666,
+	"random",	{Qrandom},	0,		0444,
+	"notquiterandom", {Qnotquiterandom}, 0,	0444,
+	"sysname",	{Qsysname},	0,		0664,
+	"time",		{Qtime},	0,		0664,
+	"user",		{Quser},	0,	0644,
+	"jit",		{Qjit},	0,	0666,
+};
+
+ulong	boottime;		/* seconds since epoch at boot */
+
+long
+seconds(void)
+{
+	return boottime + TK2SEC(MACHP(0)->ticks);
+}
+
+vlong
+mseconds(void)
+{
+	return ((vlong)boottime*1000)+((vlong)(TK2MS(MACHP(0)->ticks)));
+}
+
+vlong
+osusectime(void)
+{
+	return (((vlong)boottime*1000)+((vlong)(TK2MS(MACHP(0)->ticks)))*1000);
+}
+
+vlong
+nsec(void)
+{
+	return osusectime()*1000;	/* TO DO */
+}
+
+int
+readnum(ulong off, char *buf, ulong n, ulong val, int size)
+{
+	char tmp[64];
+
+	if(size > 64) size = 64;
+
+	snprint(tmp, sizeof(tmp), "%*.0lud ", size, val);
+	if(off >= size)
+		return 0;
+	if(off+n > size)
+		n = size-off;
+	memmove(buf, tmp+off, n);
+	return n;
+}
+
+int
+readstr(ulong off, char *buf, ulong n, char *str)
+{
+	int size;
+
+	size = strlen(str);
+	if(off >= size)
+		return 0;
+	if(off+n > size)
+		n = size-off;
+	memmove(buf, str+off, n);
+	return n;
+}
+
+void
+fddump()
+{
+	Proc *p;
+	Osenv *o;
+	int i;
+	Chan *c;
+
+	p = proctab(6);
+	o = p->env;
+	for(i = 0; i <= o->fgrp->maxfd; i++) {
+		if((c = o->fgrp->fd[i]) == nil)
+			continue;
+		print("%d: %s\n", i, c->name == nil? "???": c->name->s);
+	}
+}
+
+static void
+qpanic(Rune)
+{
+	panic("User requested panic.");
+}
+
+static void
+rexit(Rune)
+{
+	exit(0);
+}
+
+static void
+consinit(void)
+{
+	randominit();
+	debuginit();
+	debugkey('f', "files/6", fddump, 0);
+	debugkey('q', "panic", qpanic, 1);
+	debugkey('r', "exit", rexit, 1);
+	klogq = qopen(128*1024, 0, 0, 0);
+}
+
+static Chan*
+consattach(char *spec)
+{
+	return devattach('c', spec);
+}
+
+static Walkqid*
+conswalk(Chan *c, Chan *nc, char **name, int nname)
+{
+	return devwalk(c, nc, name, nname, consdir, nelem(consdir), devgen);
+}
+
+static int
+consstat(Chan *c, uchar *dp, int n)
+{
+	return devstat(c, dp, n, consdir, nelem(consdir), devgen);
+}
+
+static void
+flushkbdline(Queue *q)
+{
+	if(kbd.x){
+		qwrite(q, kbd.line, kbd.x);
+		kbd.x = 0;
+	}
+}
+
+static Chan*
+consopen(Chan *c, int omode)
+{
+	c->aux = 0;
+	switch((ulong)c->qid.path){
+	case Qconsctl:
+		if(!iseve())
+			error(Eperm);
+		qlock(&kbd);
+		kbd.ctl++;
+		qunlock(&kbd);
+		break;
+
+	case Qkeyboard:
+		if((omode & 3) != OWRITE) {
+			qlock(&kbd);
+			kbd.kbdr++;
+			flushkbdline(kbdq);
+			kbd.raw = 1;
+			qunlock(&kbd);
+		}
+		break;
+
+	case Qscancode:
+		qlock(&kbd);
+		if(kscanq || !kscanid) {
+			qunlock(&kbd);
+			c->flag &= ~COPEN;
+			if(kscanq)
+				error(Einuse);
+			else
+				error(Ebadarg);
+		}
+		kscanq = qopen(256, 0, nil, nil);
+		qunlock(&kbd);
+		break;
+
+	case Qkprint:
+		if((omode & 3) != OWRITE) {
+			wlock(&kprintq);
+			if(kprintq.q != nil){
+				wunlock(&kprintq);
+				error(Einuse);
+			}
+			kprintq.q = qopen(32*1024, Qcoalesce, nil, nil);
+			if(kprintq.q == nil){
+				wunlock(&kprintq);
+				error(Enomem);
+			}
+			qnoblock(kprintq.q, 1);
+			wunlock(&kprintq);
+			c->iounit = qiomaxatomic;
+		}
+		break;
+	}
+	return devopen(c, omode, consdir, nelem(consdir), devgen);
+}
+
+static void
+consclose(Chan *c)
+{
+	if((c->flag&COPEN) == 0)
+		return;
+
+	switch((ulong)c->qid.path){
+	case Qconsctl:
+		/* last close of control file turns off raw */
+		qlock(&kbd);
+		if(--kbd.ctl == 0)
+			kbd.raw = 0;
+		qunlock(&kbd);
+		break;
+
+	case Qkeyboard:
+		if(c->mode != OWRITE) {
+			qlock(&kbd);
+			--kbd.kbdr;
+			qunlock(&kbd);
+		}
+		break;
+
+	case Qscancode:
+		qlock(&kbd);
+		if(kscanq) {
+			qfree(kscanq);
+			kscanq = 0;
+		}
+		qunlock(&kbd);
+		break;
+
+	case Qkprint:
+		wlock(&kprintq);
+		qfree(kprintq.q);
+		kprintq.q = nil;
+		wunlock(&kprintq);
+		break;
+	}
+}
+
+static long
+consread(Chan *c, void *buf, long n, vlong offset)
+{
+	int l;
+	Osenv *o;
+	int ch, eol, i;
+	char *p, tmp[128];
+	char *cbuf = buf;
+
+	if(n <= 0)
+		return n;
+	o = up->env;
+	switch((ulong)c->qid.path){
+	case Qdir:
+		return devdirread(c, buf, n, consdir, nelem(consdir), devgen);
+	case Qsysctl:
+		return readstr(offset, buf, n, VERSION);
+	case Qcons:
+	case Qkeyboard:
+		qlock(&kbd);
+		if(waserror()) {
+			qunlock(&kbd);
+			nexterror();
+		}
+		if(kbd.raw || kbd.kbdr) {
+			if(qcanread(lineq))
+				n = qread(lineq, buf, n);
+			else {
+				/* read as much as possible */
+				do {
+					i = qread(kbdq, cbuf, n);
+					cbuf += i;
+					n -= i;
+				} while(n>0 && qcanread(kbdq));
+				n = cbuf - (char*)buf;
+			}
+		} else {
+			while(!qcanread(lineq)) {
+				qread(kbdq, &kbd.line[kbd.x], 1);
+				ch = kbd.line[kbd.x];
+				eol = 0;
+				switch(ch){
+				case '\b':
+					if(kbd.x)
+						kbd.x--;
+					break;
+				case 0x15:
+					kbd.x = 0;
+					break;
+				case '\n':
+				case 0x04:
+					eol = 1;
+				default:
+					kbd.line[kbd.x++] = ch;
+					break;
+				}
+				if(kbd.x == sizeof(kbd.line) || eol) {
+					if(ch == 0x04)
+						kbd.x--;
+					qwrite(lineq, kbd.line, kbd.x);
+					kbd.x = 0;
+				}
+			}
+			n = qread(lineq, buf, n);
+		}
+		qunlock(&kbd);
+		poperror();
+		return n;
+
+	case Qscancode:
+		if(offset == 0)
+			return readstr(0, buf, n, kscanid);
+		else
+			return qread(kscanq, buf, n);
+
+	case Qtime:
+		snprint(tmp, sizeof(tmp), "%.lld", (vlong)mseconds()*1000);
+		return readstr(offset, buf, n, tmp);
+
+	case Qhostowner:
+		return readstr(offset, buf, n, eve);
+
+	case Quser:
+		return readstr(offset, buf, n, o->user);
+
+	case Qjit:
+		snprint(tmp, sizeof(tmp), "%d", cflag);
+		return readstr(offset, buf, n, tmp);
+
+	case Qnull:
+		return 0;
+
+	case Qmsec:
+		return readnum(offset, buf, n, TK2MS(MACHP(0)->ticks), NUMSIZE);
+
+	case Qsysname:
+		if(sysname == nil)
+			return 0;
+		return readstr(offset, buf, n, sysname);
+
+	case Qnotquiterandom:
+		genrandom(buf, n);
+		return n;
+
+	case Qrandom:
+		return randomread(buf, n);
+
+	case Qmemory:
+		return poolread(buf, n, offset);
+
+	case Qdrivers:
+		p = malloc(READSTR);
+		if(p == nil)
+			error(Enomem);
+		l = 0;
+		for(i = 0; devtab[i] != nil; i++)
+			l += snprint(p+l, READSTR-l, "#%C %s\n", devtab[i]->dc,  devtab[i]->name);
+		if(waserror()){
+			free(p);
+			nexterror();
+		}
+		n = readstr(offset, buf, n, p);
+		free(p);
+		poperror();
+		return n;
+
+	case Qklog:
+		return qread(klogq, buf, n);
+
+	case Qkprint:
+		rlock(&kprintq);
+		if(waserror()){
+			runlock(&kprintq);
+			nexterror();
+		}
+		n = qread(kprintq.q, buf, n);
+		poperror();
+		runlock(&kprintq);
+		return n;
+
+	default:
+		print("consread %llud\n", c->qid.path);
+		error(Egreg);
+	}
+	return -1;		/* never reached */
+}
+
+static long
+conswrite(Chan *c, void *va, long n, vlong offset)
+{
+	vlong t;
+	long l, bp;
+	char *a = va;
+	Cmdbuf *cb;
+	Cmdtab *ct;
+	char buf[256];
+	int x;
+
+	switch((ulong)c->qid.path){
+	case Qcons:
+		/*
+		 * Can't page fault in putstrn, so copy the data locally.
+		 */
+		l = n;
+		while(l > 0){
+			bp = l;
+			if(bp > sizeof buf)
+				bp = sizeof buf;
+			memmove(buf, a, bp);
+			putstrn0(a, bp, 1);
+			a += bp;
+			l -= bp;
+		}
+		break;
+
+	case Qconsctl:
+		if(n >= sizeof(buf))
+			n = sizeof(buf)-1;
+		strncpy(buf, a, n);
+		buf[n] = 0;
+		for(a = buf; a;){
+			if(strncmp(a, "rawon", 5) == 0){
+				qlock(&kbd);
+				flushkbdline(kbdq);
+				kbd.raw = 1;
+				qunlock(&kbd);
+			} else if(strncmp(a, "rawoff", 6) == 0){
+				qlock(&kbd);
+				kbd.raw = 0;
+				kbd.x = 0;
+				qunlock(&kbd);
+			}
+			if(a = strchr(a, ' '))
+				a++;
+		}
+		break;
+
+	case Qkeyboard:
+		for(x=0; x<n; ) {
+			Rune r;
+			x += chartorune(&r, &a[x]);
+			kbdputc(kbdq, r);
+		}
+		break;
+	
+	case Qtime:
+		if(n >= sizeof(buf))
+			n = sizeof(buf)-1;
+		strncpy(buf, a, n);
+		buf[n] = 0;
+		t = strtoll(buf, 0, 0)/1000000;
+		boottime = t - TK2SEC(MACHP(0)->ticks);
+		break;
+
+	case Qhostowner:
+		if(!iseve())
+			error(Eperm);
+		if(offset != 0 || n >= sizeof(buf))
+			error(Ebadarg);
+		memmove(buf, a, n);
+		buf[n] = '\0';
+		if(n > 0 && buf[n-1] == '\n')
+			buf[--n] = 0;
+		if(n <= 0)
+			error(Ebadarg);
+		renameuser(eve, buf);
+		renameproguser(eve, buf);
+		kstrdup(&eve, buf);
+		kstrdup(&up->env->user, buf);
+		break;
+
+	case Quser:
+		if(!iseve())
+			error(Eperm);
+		if(offset != 0)
+			error(Ebadarg);
+		if(n <= 0 || n >= sizeof(buf))
+			error(Ebadarg);
+		strncpy(buf, a, n);
+		buf[n] = 0;
+		if(buf[n-1] == '\n')
+			buf[n-1] = 0;
+		kstrdup(&up->env->user, buf);
+		break;
+
+	case Qjit:
+		if(n >= sizeof(buf))
+			n = sizeof(buf)-1;
+		strncpy(buf, va, n);
+		buf[n] = '\0';
+		x = atoi(buf);
+		if(x < 0 || x > 9)
+			error(Ebadarg);
+		cflag = x;
+		return n;
+
+	case Qnull:
+		break;
+
+	case Qsysname:
+		if(offset != 0)
+			error(Ebadarg);
+		if(n <= 0 || n >= sizeof(buf))
+			error(Ebadarg);
+		strncpy(buf, a, n);
+		buf[n] = 0;
+		if(buf[n-1] == '\n')
+			buf[n-1] = 0;
+		kstrdup(&sysname, buf);
+		break;
+
+	case Qsysctl:
+		if(!iseve())
+			error(Eperm);
+		cb = parsecmd(a, n);
+		if(waserror()){
+			free(cb);
+			nexterror();
+		}
+		ct = lookupcmd(cb, sysctlcmd, nelem(sysctlcmd));
+		switch(ct->index){
+		case CMreboot:
+			reboot();
+			break;
+		case CMhalt:
+			halt();
+			break;
+		case CMpanic:
+			panic("sysctl");
+		case CMconsole:
+			consoleprint = strcmp(cb->f[1], "off") != 0;
+			break;
+		case CMbroken:
+			keepbroken = 1;
+			break;
+		case CMnobroken:
+			keepbroken = 0;
+			break;
+		}
+		poperror();
+		free(cb);
+		break;
+
+	default:
+		print("conswrite: %llud\n", c->qid.path);
+		error(Egreg);
+	}
+	return n;
+}
+
+Dev consdevtab = {
+	'c',
+	"cons",
+
+	devreset,
+	consinit,
+	devshutdown,
+	consattach,
+	conswalk,
+	consstat,
+	consopen,
+	devcreate,
+	consclose,
+	consread,
+	devbread,
+	conswrite,
+	devbwrite,
+	devremove,
+	devwstat,
+};
+
+static	ulong	randn;
+
+static void
+seedrand(void)
+{
+	randomread((void*)&randn, sizeof(randn));
+}
+
+int
+nrand(int n)
+{
+	if(randn == 0)
+		seedrand();
+	randn = randn*1103515245 + 12345 + MACHP(0)->ticks;
+	return (randn>>16) % n;
+}
+
+int
+rand(void)
+{
+	nrand(1);
+	return randn;
+}
+
+ulong
+truerand(void)
+{
+	ulong x;
+
+	randomread(&x, sizeof(x));
+	return x;
+}
+
+QLock grandomlk;
+
+void
+_genrandomqlock(void)
+{
+	qlock(&grandomlk);
+}
+
+
+void
+_genrandomqunlock(void)
+{
+	qunlock(&grandomlk);
+}
--- /dev/null
+++ b/os/port/devdbg.c
@@ -1,0 +1,1000 @@
+#include "u.h"
+#include "../port/lib.h"
+#include "mem.h"
+#include "dat.h"
+#include "fns.h"
+#include "ureg.h"
+#include "../port/error.h"
+#include	"rdbg.h"
+
+#include	<kernel.h>
+#include	<interp.h>
+
+/*
+ *	The following should be set in the config file to override
+ *	the defaults.
+ */
+int	dbgstart;
+char	*dbgdata;
+char	*dbgctl;
+char	*dbgctlstart;
+char	*dbgctlstop;
+char	*dbgctlflush;
+
+//
+// Error messages sent to the remote debugger
+//
+static	uchar	Ereset[9] = { 'r', 'e', 's', 'e', 't' };
+static	uchar	Ecount[9] = { 'c', 'o', 'u', 'n', 't' };
+static	uchar	Eunk[9] = { 'u', 'n', 'k' };
+static	uchar	Einval[9] = { 'i', 'n', 'v', 'a', 'l' };
+static	uchar	Ebadpid[9] = {'p', 'i', 'd'};
+static	uchar	Eunsup[9] = { 'u', 'n', 's', 'u', 'p' };
+static	uchar	Enotstop[9] = { 'n', 'o', 't', 's', 't', 'o', 'p' };
+ 
+//
+// Error messages raised via call to error()
+//
+static	char	Erunning[] = "Not allowed while debugger is running";
+static	char	Enumarg[] = "Not enough args";
+static	char	Ebadcmd[] = "Unknown command";
+
+static	int	PROCREG;
+static	struct {
+	Rendez;
+	Bkpt *b;
+} brk;
+
+static	Queue	*logq;
+
+int	dbgchat = 0;
+
+typedef struct Debugger Debugger;
+struct Debugger {
+	RWlock;
+	int	running;
+	char	data[PRINTSIZE];
+	char	ctl[PRINTSIZE];
+	char	ctlstart[PRINTSIZE];
+	char	ctlstop[PRINTSIZE];
+	char	ctlflush[PRINTSIZE];
+};
+
+static Debugger debugger = {
+	.data=		"#t/eia0",
+	.ctl=		"#t/eia0ctl",
+	.ctlstart=	"b19200",
+	.ctlstop=	"h",
+	.ctlflush=	"f",
+};
+
+enum {
+	BkptStackSize=	256,
+};
+
+typedef struct SkipArg SkipArg;
+struct SkipArg
+{
+	Bkpt *b;
+	Proc *p;
+};
+
+Bkpt	*breakpoints;
+void	freecondlist(BkptCond *l);
+
+static int
+getbreaks(ulong addr, Bkpt **a, int nb)
+{
+	Bkpt *b;
+	int n;
+
+	n = 0;
+	for(b = breakpoints; b != nil; b = b->next){
+		if(b->addr == addr){
+			a[n++] = b;
+			if(n == nb)
+				break;
+		}
+	}
+	return n;
+}
+
+Bkpt*
+newbreak(int id, ulong addr, BkptCond *conds, void(*handler)(Bkpt*), void *aux)
+{
+	Bkpt *b;
+
+	b = malloc(sizeof(*b));
+	if(b == nil)
+		error(Enomem);
+
+	b->id = id;
+	b->conditions = conds;
+	b->addr = addr;
+	b->handler = handler;
+	b->aux = aux;
+	b->next = nil;
+
+	return b;
+}
+
+void
+freebreak(Bkpt *b)
+{
+	freecondlist(b->conditions);
+	free(b);
+}
+
+BkptCond*
+newcondition(uchar cmd, ulong val)
+{
+	BkptCond *c;
+
+	c = mallocz(sizeof(*c), 0);
+	if(c == nil)
+		error(Enomem);
+
+	c->op = cmd;
+	c->val = val;
+	c->next = nil;
+
+	return c;
+}
+
+void
+freecondlist(BkptCond *l)
+{
+	BkptCond *next;
+
+	while(l != nil){
+		next = l->next;
+		free(l);
+		l = next;
+	}
+}
+
+
+void
+breakset(Bkpt *b)
+{
+	Bkpt *e[1];
+
+	if(getbreaks(b->addr, e, 1) != 0){
+		b->instr = e[0]->instr;
+	} else {
+		b->instr = machinstr(b->addr);
+		machbreakset(b->addr);
+	}
+
+	b->next = breakpoints;
+	breakpoints = b;
+}
+
+void
+breakrestore(Bkpt *b)
+{
+	b->next = breakpoints;
+	breakpoints = b;
+	machbreakset(b->addr);
+}
+
+Bkpt*
+breakclear(int id)
+{
+	Bkpt *b, *e, *p;
+
+	for(b=breakpoints, p=nil; b != nil && b->id != id; p = b, b = b->next)
+		;
+
+	if(b != nil){
+		if(p == nil)
+			breakpoints = b->next;
+		else
+			p->next = b->next;
+
+		if(getbreaks(b->addr, &e, 1) == 0)
+			machbreakclear(b->addr, b->instr);
+	}
+
+	return b;
+}
+
+void
+breaknotify(Bkpt *b, Proc *p)
+{
+	p->dbgstop = 1;		// stop running this process.
+	b->handler(b);
+}
+
+int
+breakmatch(BkptCond *cond, Ureg *ur, Proc *p)
+{
+	ulong a, b;
+	int pos;
+	ulong s[BkptStackSize];
+
+	memset(s, 0, sizeof(s));
+	pos = 0;
+
+	for(;cond != nil; cond = cond->next){
+		switch(cond->op){
+		default:
+			panic("breakmatch: unknown operator %c", cond->op);
+			break;
+		case 'k':
+			if(p == nil || p->pid != cond->val)
+				return 0;
+			s[pos++] = 1;
+			break;
+		case 'b':
+			if(ur->pc != cond->val)
+				return 0;
+			s[pos++] = 1;
+			break;
+		case 'p': s[pos++] = cond->val; break;
+		case '*': a = *(ulong*)s[--pos]; s[pos++] = a; break;
+		case '&': a = s[--pos]; b = s[--pos]; s[pos++] = a & b; break;
+		case '=': a = s[--pos]; b = s[--pos]; s[pos++] = a == b; break;
+		case '!': a = s[--pos]; b = s[--pos]; s[pos++] = a != b; break;
+		case 'a': a = s[--pos]; b = s[--pos]; s[pos++] = a && b; break;
+		case 'o': a = s[--pos]; b = s[--pos]; s[pos++] = a || b; break;
+		}
+	}
+
+	if(pos && s[pos-1])
+		return 1;
+	return 0;
+}
+
+void
+breakinit(void)
+{
+	machbreakinit();
+}
+
+static void
+dbglog(char *fmt, ...)
+{
+	int n;
+	va_list arg;
+	char buf[PRINTSIZE];
+
+	va_start(arg, fmt);
+	n = vseprint(buf, buf+sizeof(buf), fmt, arg) - buf;
+	va_end(arg);
+	qwrite(logq, buf, n);
+}
+
+static int
+get(int dbgfd, uchar *b)
+{
+	int i;
+	uchar c;
+
+	if(kread(dbgfd, &c, 1) < 0)
+		error(Eio);
+	for(i=0; i<9; i++){
+		if(kread(dbgfd, b++, 1) < 0)
+			error(Eio);
+	}
+	return c;
+}
+
+static void
+mesg(int dbgfd, int m, uchar *buf)
+{
+	int i;
+	uchar c;
+
+	c = m;
+	if(kwrite(dbgfd, &c, 1) < 0)
+		error(Eio);
+	for(i=0; i<9; i++){
+		if(kwrite(dbgfd, buf+i, 1) < 0)
+			error(Eio);
+	}
+}
+
+static ulong
+dbglong(uchar *s)
+{
+	return (s[0]<<24)|(s[1]<<16)|(s[2]<<8)|(s[3]<<0);
+}
+
+static Proc *
+dbgproc(ulong pid, int dbgok)
+{
+	int i;
+	Proc *p;
+
+	if(!dbgok && pid == up->pid)
+		return 0;
+	p = proctab(0);
+	for(i = 0; i < conf.nproc; i++){
+		if(p->pid == pid)
+			return p;
+		p++;
+	}
+	return 0;
+}
+
+static void*
+addr(uchar *s)
+{
+	ulong a;
+	Proc *p;
+	static Ureg ureg;
+
+	a = ((s[0]<<24)|(s[1]<<16)|(s[2]<<8)|(s[3]<<0));
+	if(a < sizeof(Ureg)){
+		p = dbgproc(PROCREG, 0);
+		if(p == 0){
+			dbglog("dbg: invalid pid\n");
+			return 0;
+		}
+		if(p->dbgreg){
+			/* in trap(), registers are all on stack */
+			memmove(&ureg, p->dbgreg, sizeof(ureg));
+		}
+		else {
+			/* not in trap, only pc and sp are available */
+			memset(&ureg, 0, sizeof(ureg));
+			ureg.sp = p->sched.sp;
+			ureg.pc = p->sched.pc;
+		}
+		return (uchar*)&ureg+a;
+	}
+	return (void*)a;
+}
+
+
+static void
+dumpcmd(uchar cmd, uchar *min)
+{
+	char *s;
+	int n;
+
+	switch(cmd){
+	case Terr:		s = "Terr"; break;
+	case Tmget:		s = "Tmget"; break;
+	case Tmput:		s = "Tmput"; break;
+	case Tspid:		s = "Tspid"; break;
+	case Tproc:		s = "Tproc"; break;
+	case Tstatus:		s = "Tstatus"; break;
+	case Trnote:		s = "Trnote"; break;
+	case Tstartstop:	s = "Tstartstop"; break;
+	case Twaitstop:		s = "Twaitstop"; break;
+	case Tstart:		s = "Tstart"; break;
+	case Tstop:		s = "Tstop"; break;
+	case Tkill:		s = "Tkill"; break;
+	case Tcondbreak:	s = "Tcondbreak"; break;
+	default:		s = "<Unknown>"; break;
+	}
+	dbglog("%s: [%2.2ux]: ", s, cmd);
+	for(n = 0; n < 9; n++)
+		dbglog("%2.2ux", min[n]);
+	dbglog("\n");
+}
+
+static int
+brkpending(void *a)
+{
+	Proc *p;
+
+	p = a;
+	if(brk.b != nil) return 1;
+
+	p->dbgstop = 0;			/* atomic */
+	if(p->state == Stopped)
+		ready(p);
+
+	return 0;
+}
+
+static void
+gotbreak(Bkpt *b)
+{
+	Bkpt *cur, *prev;
+
+	b->link = nil;
+
+	for(prev = nil, cur = brk.b; cur != nil; prev = cur, cur = cur->link)
+		;
+	if(prev == nil)
+		brk.b = b;
+	else
+		prev->link = b;
+
+	wakeup(&brk);
+}
+
+static int
+startstop(Proc *p)
+{
+	int id;
+	int s;
+	Bkpt *b;
+
+	sleep(&brk, brkpending, p);
+
+	s = splhi();
+	b = brk.b;
+	brk.b = b->link;
+	splx(s);
+
+	id = b->id;
+
+	return id;
+}
+
+static int
+condbreak(char cmd, ulong val)
+{
+	BkptCond *c;
+	static BkptCond *head = nil;
+	static BkptCond *tail = nil;
+	static Proc *p = nil;
+	static int id = -1;
+	int s;
+
+	if(waserror()){
+		dbglog(up->env->errstr);
+		freecondlist(head);
+		head = tail = nil;
+		p = nil;
+		id = -1;
+		return 0;
+	}
+
+	switch(cmd){
+	case 'b': case 'p':
+	case '*': case '&': case '=':
+	case '!': case 'a': case 'o':
+		break;
+	case 'n':
+		id = val;
+		poperror();
+		return 1;
+	case 'k':
+		p = dbgproc(val, 0);
+		if(p == nil)
+			error("k: unknown pid");
+		break;
+	case 'd': {
+		Bkpt *b;
+
+		s = splhi();
+		b = breakclear(val);
+		if(b != nil){
+			Bkpt *cur, *prev;
+
+			prev = nil;
+			cur = brk.b;
+			while(cur != nil){
+				if(cur->id == b->id){
+					if(prev == nil)
+						brk.b = cur->link;
+					else
+						prev->link = cur->link;
+					break;
+				}
+				cur = cur->link;
+			}
+			freebreak(b);
+		}
+		splx(s);
+		poperror();
+		return 1;
+		}
+	default:
+		dbglog("condbreak(): unknown op %c %lux\n", cmd, val);
+		error("unknown op");
+	}
+
+	c = newcondition(cmd, val);
+
+	 //
+	 // the 'b' command comes last, (so we know we have reached the end
+	 // of the condition list), but it should be the first thing
+	 // checked, so put it at the head.
+	 //
+	if(cmd == 'b'){
+		if(p == nil) error("no pid");
+		if(id == -1) error("no id");
+
+		c->next = head;
+		s = splhi();
+		breakset(newbreak(id, val, c, gotbreak, p));
+		splx(s);
+		head = tail = nil;
+		p = nil;
+		id = -1;
+	} else if(tail != nil){
+		tail->next = c;
+		tail = c;
+	} else
+		head = tail = c;
+
+	poperror();
+
+	return 1;
+}
+
+static void
+dbg(void*)
+{
+	Proc *p;
+	ulong val;
+	int n, cfd, dfd;
+	uchar cmd, *a, min[RDBMSGLEN-1], mout[RDBMSGLEN-1];
+
+	rlock(&debugger);
+
+	setpri(PriRealtime);
+
+	closefgrp(up->env->fgrp);
+	up->env->fgrp = newfgrp(nil);
+
+	if(waserror()){
+		dbglog("dbg: quits: %s\n", up->env->errstr);
+		runlock(&debugger);
+		wlock(&debugger);
+		debugger.running = 0;
+		wunlock(&debugger);
+		pexit("", 0);
+	}
+
+	dfd = kopen(debugger.data, ORDWR);
+	if(dfd < 0){
+		dbglog("dbg: can't open %s: %s\n",debugger.data, up->env->errstr);
+		error(Eio);
+	}
+	if(waserror()){
+		kclose(dfd);
+		nexterror();
+	}
+
+	if(debugger.ctl[0] != 0){
+		cfd = kopen(debugger.ctl, ORDWR);
+		if(cfd < 0){
+			dbglog("dbg: can't open %s: %s\n", debugger.ctl, up->env->errstr);
+			error(Eio);
+		}
+		if(kwrite(cfd, debugger.ctlstart, strlen(debugger.ctlstart)) < 0){
+			dbglog("dbg: write %s: %s\n", debugger.ctl, up->env->errstr);
+			error(Eio);
+		}
+	}else
+		cfd = -1;
+	if(waserror()){
+		if(cfd != -1){
+			kwrite(cfd, debugger.ctlflush, strlen(debugger.ctlflush));
+			kclose(cfd);
+		}
+		nexterror();
+	}
+
+	mesg(dfd, Rerr, Ereset);
+
+	for(;;){
+		memset(mout, 0, sizeof(mout));
+		cmd = get(dfd, min);
+		if(dbgchat)
+			dumpcmd(cmd, min);
+		switch(cmd){
+		case Tmget:
+			n = min[4];
+			if(n > 9){
+				mesg(dfd, Rerr, Ecount);
+				break;
+			}
+			a = addr(min+0);
+			if(!isvalid_va(a)){
+				mesg(dfd, Rerr, Einval);
+				break;
+			}
+			memmove(mout, a, n);
+			mesg(dfd, Rmget, mout);
+			break;
+		case Tmput:
+			n = min[4];
+			if(n > 4){
+				mesg(dfd, Rerr, Ecount);
+				break;
+			}
+			a = addr(min+0);
+			if(!isvalid_va(a)){
+				mesg(dfd, Rerr, Einval);
+				break;
+			}
+			memmove(a, min+5, n);
+			segflush(a, n);
+			mesg(dfd, Rmput, mout);
+			break;
+		case Tproc:
+			p = dbgproc(dbglong(min+0), 0);
+			if(p == 0){
+				mesg(dfd, Rerr, Ebadpid);
+				break;
+			}
+			PROCREG = p->pid;	/* try this instead of Tspid */
+			sprint((char*)mout, "%8.8lux", p);
+			mesg(dfd, Rproc, mout);
+			break;
+		case Tstatus:
+			p = dbgproc(dbglong(min+0), 1);
+			if(p == 0){
+				mesg(dfd, Rerr, Ebadpid);
+				break;
+			}
+			if(p->state > Rendezvous || p->state < Dead)
+				sprint((char*)mout, "%8.8ux", p->state);
+			else if(p->dbgstop == 1)
+				strncpy((char*)mout, statename[Stopped], sizeof(mout));
+			else
+				strncpy((char*)mout, statename[p->state], sizeof(mout));
+			mesg(dfd, Rstatus, mout);
+			break;
+		case Trnote:
+			p = dbgproc(dbglong(min+0), 0);
+			if(p == 0){
+				mesg(dfd, Rerr, Ebadpid);
+				break;
+			}
+			mout[0] = 0;	/* should be trap status, if any */
+			mesg(dfd, Rrnote, mout);
+			break;
+		case Tstop:
+			p = dbgproc(dbglong(min+0), 0);
+			if(p == 0){
+				mesg(dfd, Rerr, Ebadpid);
+				break;
+			}
+			p->dbgstop = 1;			/* atomic */
+			mout[0] = 0;
+			mesg(dfd, Rstop, mout);
+			break;
+		case Tstart:
+			p = dbgproc(dbglong(min+0), 0);
+			if(p == 0){
+				mesg(dfd, Rerr, Ebadpid);
+				break;
+			}
+			p->dbgstop = 0;			/* atomic */
+			if(p->state == Stopped)
+				ready(p);
+			mout[0] = 0;
+			mesg(dfd, Rstart, mout);
+			break;
+		case Tstartstop:
+			p = dbgproc(dbglong(min+0), 0);
+			if(p == 0){
+				mesg(dfd, Rerr, Ebadpid);
+				break;
+			}
+			if(!p->dbgstop){
+				mesg(dfd, Rerr, Enotstop);
+				break;
+			}
+			mout[0] = startstop(p);
+			mesg(dfd, Rstartstop, mout);
+			break;
+		case Tcondbreak:
+			val = dbglong(min+0);
+			if(!condbreak(min[4], val)){
+				mesg(dfd, Rerr, Eunk);
+				break;
+			}
+			mout[0] = 0;
+			mesg(dfd, Rcondbreak, mout);
+			break;
+		default:
+			dumpcmd(cmd, min);
+			mesg(dfd, Rerr, Eunk);
+			break;
+		}
+	}
+}
+
+static void
+dbgnote(Proc *p, Ureg *ur)
+{
+	if(p){
+		p->dbgreg = ur;
+		PROCREG = p->pid;	/* acid can get the trap info from regs */
+	}
+}
+
+enum {
+	Qdir,
+	Qdbgctl,
+	Qdbglog,
+
+	DBGrun = 1,
+	DBGstop = 2,
+
+	Loglimit = 4096,
+};
+
+static Dirtab dbgdir[]=
+{
+	".",		{Qdir, 0, QTDIR},	0,	0555,
+	"dbgctl",	{Qdbgctl},	0,		0660,
+	"dbglog",	{Qdbglog},	0,		0440,
+};
+
+static void
+start_debugger(void)
+{
+	breakinit();
+	dbglog("starting debugger\n");
+	debugger.running++;
+	kproc("dbg", dbg, 0, KPDUPPG);
+}
+
+static void
+dbginit(void)
+{
+
+	logq = qopen(Loglimit, 0, 0, 0);
+	if(logq == nil)
+		error(Enomem);
+	qnoblock(logq, 1);
+
+	wlock(&debugger);
+	if(waserror()){
+		wunlock(&debugger);
+		qfree(logq);
+		logq = nil;
+		nexterror();
+	}
+
+	if(dbgdata != nil){
+		strncpy(debugger.data, dbgdata, sizeof(debugger.data));
+		debugger.data[sizeof(debugger.data)-1] = 0;
+	}
+	if(dbgctl != nil){
+		strncpy(debugger.ctl, dbgctl, sizeof(debugger.ctl));
+		debugger.ctl[sizeof(debugger.ctl)-1] = 0;
+	}
+	if(dbgctlstart != nil){
+		strncpy(debugger.ctlstart, dbgctlstart, sizeof(debugger.ctlstart));
+		debugger.ctlstart[sizeof(debugger.ctlstart)-1] = 0;
+	}
+	if(dbgctlstop != nil){
+		strncpy(debugger.ctlstop, dbgctlstop, sizeof(debugger.ctlstop));
+		debugger.ctlstop[sizeof(debugger.ctlstop)-1] = 0;
+	}
+	if(dbgctlflush != nil){
+		strncpy(debugger.ctlflush, dbgctlflush, sizeof(debugger.ctlflush));
+		debugger.ctlflush[sizeof(debugger.ctlflush)-1] = 0;
+	}
+	if(dbgstart)
+		start_debugger();
+
+	poperror();
+	wunlock(&debugger);
+}
+
+static Chan*
+dbgattach(char *spec)
+{
+	return devattach('b', spec);
+}
+
+static Walkqid*
+dbgwalk(Chan *c, Chan *nc, char **name, int nname)
+{
+	return devwalk(c, nc, name, nname, dbgdir, nelem(dbgdir), devgen);
+}
+
+static int
+dbgstat(Chan *c, uchar *dp, int n)
+{
+	return devstat(c, dp, n, dbgdir, nelem(dbgdir), devgen);
+}
+
+static Chan*
+dbgopen(Chan *c, int omode)
+{
+	return devopen(c, omode, dbgdir, nelem(dbgdir), devgen);
+}
+
+static long
+dbgread(Chan *c, void *buf, long n, vlong offset)
+{
+	char *ctlstate;
+
+	switch((ulong)c->qid.path){
+	case Qdir:
+		return devdirread(c, buf, n, dbgdir, nelem(dbgdir), devgen);
+	case Qdbgctl:
+		rlock(&debugger);
+		ctlstate = smprint("%s data %s ctl %s ctlstart %s ctlstop %s ctlflush %s\n",
+			debugger.running ? "running" : "stopped",
+			debugger.data, debugger.ctl, 
+			debugger.ctlstart, debugger.ctlstop, debugger.ctlflush);
+		runlock(&debugger);
+		if(ctlstate == nil)
+			error(Enomem);
+		if(waserror()){
+			free(ctlstate);
+			nexterror();
+		}
+		n = readstr(offset, buf, n, ctlstate);
+		poperror();
+		free(ctlstate);
+		return n;
+	case Qdbglog:
+		return qread(logq, buf, n);
+	default:
+		error(Egreg);
+	}
+	return -1;		/* never reached */
+}
+
+static void
+ctl(Cmdbuf *cb)
+{
+	Debugger d;
+	int dbgstate = 0;
+	int i;
+	char *df;
+	int dfsize;
+	int setval;
+
+	memset(&d, 0, sizeof(d));
+	for(i=0; i < cb->nf; i++){
+		setval = 0;
+		df = nil;
+		dfsize = 0;
+		switch(cb->f[i][0]){
+		case 'd':
+			df = d.data;
+			dfsize = sizeof(d.data);
+			setval=1;
+			break;
+		case 'c':
+			df = d.ctl;
+			dfsize = sizeof(d.ctl);
+			setval=1;
+			break;
+		case 'i':
+			df = d.ctlstart;
+			dfsize = sizeof(d.ctlstart);
+			setval=1;
+			break;
+		case 'h':
+			df = d.ctlstop;
+			dfsize = sizeof(d.ctlstop);
+			setval=1;
+			break;
+		case 'f':
+			df = d.ctlflush;
+			dfsize = sizeof(d.ctlflush);
+			setval=1;
+			break;
+		case 'r':
+			dbgstate = DBGrun;
+			break;
+		case 's':
+			dbgstate = DBGstop;
+			break;
+		default:
+			error(Ebadcmd);
+		}
+		if(setval){
+			if(i+1 >= cb->nf)
+				cmderror(cb, Enumarg);
+			strncpy(df, cb->f[i+1], dfsize-1);
+			df[dfsize-1] = 0;
+			++d.running;
+			++i;
+		}
+	}
+
+	if(d.running){
+		wlock(&debugger);
+		if(debugger.running){
+			wunlock(&debugger);
+			error(Erunning);
+		}
+		if(d.data[0] != 0){
+			strcpy(debugger.data, d.data);
+			dbglog("data %s\n",debugger.data);
+		}
+		if(d.ctl[0] != 0){
+			strcpy(debugger.ctl, d.ctl);
+			dbglog("ctl %s\n",debugger.ctl);
+		}
+		if(d.ctlstart[0] != 0){
+			strcpy(debugger.ctlstart, d.ctlstart);
+			dbglog("ctlstart %s\n",debugger.ctlstart);
+		}
+		if(d.ctlstop[0] != 0){
+			strcpy(debugger.ctlstop, d.ctlstop);
+			dbglog("ctlstop %s\n",debugger.ctlstop);
+		}
+		wunlock(&debugger);
+	}
+
+	if(dbgstate == DBGrun){
+		if(!debugger.running){
+			wlock(&debugger);
+			if(waserror()){
+				wunlock(&debugger);
+				nexterror();
+			}
+			if(!debugger.running)
+				start_debugger();
+			else
+				dbglog("debugger already running\n");
+			poperror();
+			wunlock(&debugger);
+		} else
+			dbglog("debugger already running\n");
+	} else if(dbgstate == DBGstop){
+		if(debugger.running){
+			/* force hangup to stop the dbg process */
+			int cfd;
+			if(debugger.ctl[0] == 0)
+				return;
+			cfd = kopen(debugger.ctl, OWRITE);
+			if(cfd == -1)
+				error(up->env->errstr);
+			dbglog("stopping debugger\n");
+			if(kwrite(cfd, debugger.ctlstop, strlen(debugger.ctlstop)) == -1)
+				error(up->env->errstr);
+			kclose(cfd);
+		} else
+			dbglog("debugger not running\n");
+	}
+}
+
+static long
+dbgwrite(Chan *c, void *va, long n, vlong)
+{
+	Cmdbuf *cb;
+
+	switch((ulong)c->qid.path){
+	default:
+		error(Egreg);
+		break;
+	case Qdbgctl:
+		cb = parsecmd(va, n);
+		if(waserror()){
+			free(cb);
+			nexterror();
+		}
+		ctl(cb);
+		poperror();
+		break;
+	}
+	return n;
+}
+
+static void
+dbgclose(Chan*)
+{
+}
+
+Dev dbgdevtab = {
+	'b',
+	"dbg",
+
+	devreset,
+	dbginit,
+	devshutdown,
+	dbgattach,
+	dbgwalk,
+	dbgstat,
+	dbgopen,
+	devcreate,
+	dbgclose,
+	dbgread,
+	devbread,
+	dbgwrite,
+	devbwrite,
+	devremove,
+	devwstat,
+};
--- /dev/null
+++ b/os/port/devdraw.c
@@ -1,0 +1,2088 @@
+#include	"u.h"
+#include	"../port/lib.h"
+#include	"mem.h"
+#include	"dat.h"
+#include	"fns.h"
+#include	"../port/error.h"
+
+#define	Image	IMAGE
+#include	<draw.h>
+#include	<memdraw.h>
+#include	<memlayer.h>
+#include	<cursor.h>
+#include	"screen.h"
+
+enum
+{
+	Qtopdir		= 0,
+	Qnew,
+	Q3rd,
+	Q2nd,
+	Qcolormap,
+	Qctl,
+	Qdata,
+	Qrefresh,
+};
+
+/*
+ * Qid path is:
+ *	 4 bits of file type (qids above)
+ *	24 bits of mux slot number +1; 0 means not attached to client
+ */
+#define	QSHIFT	4	/* location in qid of client # */
+
+#define	QID(q)		((((ulong)(q).path)&0x0000000F)>>0)
+#define	CLIENTPATH(q)	((((ulong)q)&0x7FFFFFF0)>>QSHIFT)
+#define	CLIENT(q)	CLIENTPATH((q).path)
+
+#define	NHASH		(1<<5)
+#define	HASHMASK	(NHASH-1)
+#define	IOUNIT	(64*1024)
+
+typedef struct Client Client;
+typedef struct Draw Draw;
+typedef struct DImage DImage;
+typedef struct DScreen DScreen;
+typedef struct CScreen CScreen;
+typedef struct FChar FChar;
+typedef struct Refresh Refresh;
+typedef struct Refx Refx;
+typedef struct DName DName;
+
+ulong blanktime = 30;	/* in minutes; a half hour */
+
+struct Draw
+{
+	QLock;
+	int		clientid;
+	int		nclient;
+	Client**	client;
+	int		nname;
+	DName*	name;
+	int		vers;
+	int		softscreen;
+	int		blanked;	/* screen turned off */
+	ulong		blanktime;	/* time of last operation */
+	ulong		savemap[3*256];
+};
+
+struct Client
+{
+	Ref		r;
+	DImage*		dimage[NHASH];
+	CScreen*	cscreen;
+	Refresh*	refresh;
+	Rendez		refrend;
+	uchar*		readdata;
+	int		nreaddata;
+	int		busy;
+	int		clientid;
+	int		slot;
+	int		refreshme;
+	int		infoid;
+	int		op;
+};
+
+struct Refresh
+{
+	DImage*		dimage;
+	Rectangle	r;
+	Refresh*	next;
+};
+
+struct Refx
+{
+	Client*		client;
+	DImage*		dimage;
+};
+
+struct DName
+{
+	char			*name;
+	Client	*client;
+	DImage*		dimage;
+	int			vers;
+};
+
+struct FChar
+{
+	int		minx;	/* left edge of bits */
+	int		maxx;	/* right edge of bits */
+	uchar		miny;	/* first non-zero scan-line */
+	uchar		maxy;	/* last non-zero scan-line + 1 */
+	schar		left;	/* offset of baseline */
+	uchar		width;	/* width of baseline */
+};
+
+/*
+ * Reference counts in DImages:
+ *	one per open by original client
+ *	one per screen image or fill
+ * 	one per image derived from this one by name
+ */
+struct DImage
+{
+	int		id;
+	int		ref;
+	char		*name;
+	int		vers;
+	Memimage*	image;
+	int		ascent;
+	int		nfchar;
+	FChar*		fchar;
+	DScreen*	dscreen;	/* 0 if not a window */
+	DImage*	fromname;	/* image this one is derived from, by name */
+	DImage*		next;
+};
+
+struct CScreen
+{
+	DScreen*	dscreen;
+	CScreen*	next;
+};
+
+struct DScreen
+{
+	int		id;
+	int		public;
+	int		ref;
+	DImage	*dimage;
+	DImage	*dfill;
+	Memscreen*	screen;
+	Client*		owner;
+	DScreen*	next;
+};
+
+static	Draw		sdraw;
+static	Memimage	*screenimage;
+static	Memdata	screendata;
+static	Rectangle	flushrect;
+static	int		waste;
+static	DScreen*	dscreen;
+extern	void		flushmemscreen(Rectangle);
+	void		drawmesg(Client*, void*, int);
+	void		drawuninstall(Client*, int);
+	void		drawfreedimage(DImage*);
+	Client*		drawclientofpath(ulong);
+
+static	char Enodrawimage[] =	"unknown id for draw image";
+static	char Enodrawscreen[] =	"unknown id for draw screen";
+static	char Eshortdraw[] =	"short draw message";
+static	char Eshortread[] =	"draw read too short";
+static	char Eimageexists[] =	"image id in use";
+static	char Escreenexists[] =	"screen id in use";
+static	char Edrawmem[] =	"out of memory: image";
+static	char Ereadoutside[] =	"readimage outside image";
+static	char Ewriteoutside[] =	"writeimage outside image";
+static	char Enotfont[] =	"image not a font";
+static	char Eindex[] =		"character index out of range";
+static	char Enoclient[] =	"no such draw client";
+static	char Enameused[] =	"image name in use";
+static	char Enoname[] =	"no image with that name";
+static	char Eoldname[] =	"named image no longer valid";
+static	char Enamed[] = 	"image already has name";
+static	char Ewrongname[] = 	"wrong name for image";
+
+static int
+drawgen(Chan *c, char*, Dirtab*, int, int s, Dir *dp)
+{
+	int t;
+	Qid q;
+	ulong path;
+	Client *cl;
+
+	q.vers = 0;
+
+	if(s == DEVDOTDOT){
+		switch(QID(c->qid)){
+		case Qtopdir:
+		case Q2nd:
+			mkqid(&q, Qtopdir, 0, QTDIR);
+			devdir(c, q, "#i", 0, eve, 0500, dp);
+			break;
+		case Q3rd:
+			cl = drawclientofpath(c->qid.path);
+			if(cl == nil)
+				strcpy(up->genbuf, "??");
+			else
+				sprint(up->genbuf, "%d", cl->clientid);
+			mkqid(&q, Q2nd, 0, QTDIR);
+			devdir(c, q, up->genbuf, 0, eve, 0500, dp);
+			break;
+		default:
+			panic("drawwalk %llux", c->qid.path);
+		}
+		return 1;
+	}
+
+	/*
+	 * Top level directory contains the name of the device.
+	 */
+	t = QID(c->qid);
+	if(t == Qtopdir){
+		switch(s){
+		case 0:
+			mkqid(&q, Q2nd, 0, QTDIR);
+			devdir(c, q, "draw", 0, eve, 0555, dp);
+			break;
+		default:
+			return -1;
+		}
+		return 1;
+	}
+
+	/*
+	 * Second level contains "new" plus all the clients.
+	 */
+	if(t == Q2nd || t == Qnew){
+		if(s == 0){
+			mkqid(&q, Qnew, 0, QTFILE);
+			devdir(c, q, "new", 0, eve, 0666, dp);
+		}
+		else if(s <= sdraw.nclient){
+			cl = sdraw.client[s-1];
+			if(cl == 0)
+				return 0;
+			sprint(up->genbuf, "%d", cl->clientid);
+			mkqid(&q, (s<<QSHIFT)|Q3rd, 0, QTDIR);
+			devdir(c, q, up->genbuf, 0, eve, 0555, dp);
+			return 1;
+		}
+		else
+			return -1;
+		return 1;
+	}
+
+	/*
+	 * Third level.
+	 */
+	path = c->qid.path&~((1<<QSHIFT)-1);	/* slot component */
+	q.vers = c->qid.vers;
+	q.type = QTFILE;
+	switch(s){
+	case 0:
+		q.path = path|Qcolormap;
+		devdir(c, q, "colormap", 0, eve, 0600, dp);
+		break;
+	case 1:
+		q.path = path|Qctl;
+		devdir(c, q, "ctl", 0, eve, 0600, dp);
+		break;
+	case 2:
+		q.path = path|Qdata;
+		devdir(c, q, "data", 0, eve, 0600, dp);
+		break;
+	case 3:
+		q.path = path|Qrefresh;
+		devdir(c, q, "refresh", 0, eve, 0400, dp);
+		break;
+	default:
+		return -1;
+	}
+	return 1;
+}
+
+static
+int
+drawrefactive(void *a)
+{
+	Client *c;
+
+	c = a;
+	return c->refreshme || c->refresh!=0;
+}
+
+static
+void
+drawrefreshscreen(DImage *l, Client *client)
+{
+	while(l != nil && l->dscreen == nil)
+		l = l->fromname;
+	if(l != nil && l->dscreen->owner != client)
+		l->dscreen->owner->refreshme = 1;
+}
+
+static
+void
+drawrefresh(Memimage*, Rectangle r, void *v)
+{
+	Refx *x;
+	DImage *d;
+	Client *c;
+	Refresh *ref;
+
+	if(v == 0)
+		return;
+	x = v;
+	c = x->client;
+	d = x->dimage;
+	for(ref=c->refresh; ref; ref=ref->next)
+		if(ref->dimage == d){
+			combinerect(&ref->r, r);
+			return;
+		}
+	ref = malloc(sizeof(Refresh));
+	if(ref){
+		ref->dimage = d;
+		ref->r = r;
+		ref->next = c->refresh;
+		c->refresh = ref;
+	}
+}
+
+static void
+addflush(Rectangle r)
+{
+	int abb, ar, anbb;
+	Rectangle nbb;
+
+	if(sdraw.softscreen==0 || !rectclip(&r, screenimage->r))
+		return;
+
+	if(flushrect.min.x >= flushrect.max.x){
+		flushrect = r;
+		waste = 0;
+		return;
+	}
+	nbb = flushrect;
+	combinerect(&nbb, r);
+	ar = Dx(r)*Dy(r);
+	abb = Dx(flushrect)*Dy(flushrect);
+	anbb = Dx(nbb)*Dy(nbb);
+	/*
+	 * Area of new waste is area of new bb minus area of old bb,
+	 * less the area of the new segment, which we assume is not waste.
+	 * This could be negative, but that's OK.
+	 */
+	waste += anbb-abb - ar;
+	if(waste < 0)
+		waste = 0;
+	/*
+	 * absorb if:
+	 *	total area is small
+	 *	waste is less than half total area
+	 * 	rectangles touch
+	 */
+	if(anbb<=1024 || waste*2<anbb || rectXrect(flushrect, r)){
+		flushrect = nbb;
+		return;
+	}
+	/* emit current state */
+	if(flushrect.min.x < flushrect.max.x)
+		flushmemscreen(flushrect);
+	flushrect = r;
+	waste = 0;
+}
+
+static
+void
+dstflush(Memimage *dst, Rectangle r)
+{
+	Memlayer *l;
+
+	if(dst == screenimage){
+		combinerect(&flushrect, r);
+		return;
+	}
+	l = dst->layer;
+	if(l == nil)
+		return;
+	do{
+		if(l->screen->image->data != screenimage->data)
+			return;
+		r = rectaddpt(r, l->delta);
+		l = l->screen->image->layer;
+	}while(l);
+	addflush(r);
+}
+
+void
+drawflush(void)
+{
+	if(flushrect.min.x < flushrect.max.x)
+		flushmemscreen(flushrect);
+	flushrect = Rect(10000, 10000, -10000, -10000);
+}
+
+static
+int
+drawcmp(char *a, char *b, int n)
+{
+	if(strlen(a) != n)
+		return 1;
+	return memcmp(a, b, n);
+}
+
+DName*
+drawlookupname(int n, char *str)
+{
+	DName *name, *ename;
+
+	name = sdraw.name;
+	ename = &name[sdraw.nname];
+	for(; name<ename; name++)
+		if(drawcmp(name->name, str, n) == 0)
+			return name;
+	return 0;
+}
+
+int
+drawgoodname(DImage *d)
+{
+	DName *n;
+
+	/* if window, validate the screen's own images */
+	if(d->dscreen)
+		if(drawgoodname(d->dscreen->dimage) == 0
+		|| drawgoodname(d->dscreen->dfill) == 0)
+			return 0;
+	if(d->name == nil)
+		return 1;
+	n = drawlookupname(strlen(d->name), d->name);
+	if(n==nil || n->vers!=d->vers)
+		return 0;
+	return 1;
+}
+
+DImage*
+drawlookup(Client *client, int id, int checkname)
+{
+	DImage *d;
+
+	d = client->dimage[id&HASHMASK];
+	while(d){
+		if(d->id == id){
+			if(checkname && !drawgoodname(d))
+				error(Eoldname);
+			return d;
+		}
+		d = d->next;
+	}
+	return 0;
+}
+
+DScreen*
+drawlookupdscreen(int id)
+{
+	DScreen *s;
+
+	s = dscreen;
+	while(s){
+		if(s->id == id)
+			return s;
+		s = s->next;
+	}
+	return 0;
+}
+
+DScreen*
+drawlookupscreen(Client *client, int id, CScreen **cs)
+{
+	CScreen *s;
+
+	s = client->cscreen;
+	while(s){
+		if(s->dscreen->id == id){
+			*cs = s;
+			return s->dscreen;
+		}
+		s = s->next;
+	}
+	error(Enodrawscreen);
+	return 0;
+}
+
+Memimage*
+drawinstall(Client *client, int id, Memimage *i, DScreen *dscreen)
+{
+	DImage *d;
+
+	d = malloc(sizeof(DImage));
+	if(d == 0)
+		return 0;
+	d->id = id;
+	d->ref = 1;
+	d->name = 0;
+	d->vers = 0;
+	d->image = i;
+	d->nfchar = 0;
+	d->fchar = 0;
+	d->fromname = 0;
+	d->dscreen = dscreen;
+	d->next = client->dimage[id&HASHMASK];
+	client->dimage[id&HASHMASK] = d;
+	return i;
+}
+
+Memscreen*
+drawinstallscreen(Client *client, DScreen *d, int id, DImage *dimage, DImage *dfill, int public)
+{
+	Memscreen *s;
+	CScreen *c;
+
+	c = malloc(sizeof(CScreen));
+	if(dimage && dimage->image && dimage->image->chan == 0)
+		panic("bad image %p in drawinstallscreen", dimage->image);
+
+	if(c == 0)
+		return 0;
+	if(d == 0){
+		d = malloc(sizeof(DScreen));
+		if(d == 0){
+			free(c);
+			return 0;
+		}
+		s = malloc(sizeof(Memscreen));
+		if(s == 0){
+			free(c);
+			free(d);
+			return 0;
+		}
+		s->frontmost = 0;
+		s->rearmost = 0;
+		d->dimage = dimage;
+		if(dimage){
+			s->image = dimage->image;
+			dimage->ref++;
+		}
+		d->dfill = dfill;
+		if(dfill){
+			s->fill = dfill->image;
+			dfill->ref++;
+		}
+		d->ref = 0;
+		d->id = id;
+		d->screen = s;
+		d->public = public;
+		d->next = dscreen;
+		d->owner = client;
+		dscreen = d;
+	}
+	c->dscreen = d;
+	d->ref++;
+	c->next = client->cscreen;
+	client->cscreen = c;
+	return d->screen;
+}
+
+void
+drawdelname(DName *name)
+{
+	int i;
+
+	i = name-sdraw.name;
+	memmove(name, name+1, (sdraw.nname-(i+1))*sizeof(DName));
+	sdraw.nname--;
+}
+
+void
+drawfreedscreen(DScreen *this)
+{
+	DScreen *ds, *next;
+
+	this->ref--;
+	if(this->ref < 0)
+		print("negative ref in drawfreedscreen\n");
+	if(this->ref > 0)
+		return;
+	ds = dscreen;
+	if(ds == this){
+		dscreen = this->next;
+		goto Found;
+	}
+	while(next = ds->next){	/* assign = */
+		if(next == this){
+			ds->next = this->next;
+			goto Found;
+		}
+		ds = next;
+	}
+	error(Enodrawimage);
+
+    Found:
+	if(this->dimage)
+		drawfreedimage(this->dimage);
+	if(this->dfill)
+		drawfreedimage(this->dfill);
+	free(this->screen);
+	free(this);
+}
+
+void
+drawfreedimage(DImage *dimage)
+{
+	int i;
+	Memimage *l;
+	DScreen *ds;
+
+	dimage->ref--;
+	if(dimage->ref < 0)
+		print("negative ref in drawfreedimage\n");
+	if(dimage->ref > 0)
+		return;
+
+	/* any names? */
+	for(i=0; i<sdraw.nname; )
+		if(sdraw.name[i].dimage == dimage)
+			drawdelname(sdraw.name+i);
+		else
+			i++;
+	if(dimage->fromname){	/* acquired by name; owned by someone else*/
+		drawfreedimage(dimage->fromname);
+		goto Return;
+	}
+	if(dimage->image == screenimage)	/* don't free the display */
+		goto Return;
+	ds = dimage->dscreen;
+	if(ds){
+		l = dimage->image;
+		if(l->data == screenimage->data)
+			dstflush(l->layer->screen->image, l->layer->screenr);
+		if(l->layer->refreshfn == drawrefresh)	/* else true owner will clean up */
+			free(l->layer->refreshptr);
+		l->layer->refreshptr = nil;
+		if(drawgoodname(dimage))
+			memldelete(l);
+		else
+			memlfree(l);
+		drawfreedscreen(ds);
+	}else
+		freememimage(dimage->image);
+    Return:
+	free(dimage->fchar);
+	free(dimage);
+}
+
+void
+drawuninstallscreen(Client *client, CScreen *this)
+{
+	CScreen *cs, *next;
+
+	cs = client->cscreen;
+	if(cs == this){
+		client->cscreen = this->next;
+		drawfreedscreen(this->dscreen);
+		free(this);
+		return;
+	}
+	while(next = cs->next){	/* assign = */
+		if(next == this){
+			cs->next = this->next;
+			drawfreedscreen(this->dscreen);
+			free(this);
+			return;
+		}
+		cs = next;
+	}
+}
+
+void
+drawuninstall(Client *client, int id)
+{
+	DImage *d, *next;
+
+	d = client->dimage[id&HASHMASK];
+	if(d == 0)
+		error(Enodrawimage);
+	if(d->id == id){
+		client->dimage[id&HASHMASK] = d->next;
+		drawfreedimage(d);
+		return;
+	}
+	while(next = d->next){	/* assign = */
+		if(next->id == id){
+			d->next = next->next;
+			drawfreedimage(next);
+			return;
+		}
+		d = next;
+	}
+	error(Enodrawimage);
+}
+
+void
+drawaddname(Client *client, DImage *di, int n, char *str)
+{
+	DName *name, *ename, *new, *t;
+
+	name = sdraw.name;
+	ename = &name[sdraw.nname];
+	for(; name<ename; name++)
+		if(drawcmp(name->name, str, n) == 0)
+			error(Enameused);
+	t = smalloc((sdraw.nname+1)*sizeof(DName));
+	memmove(t, sdraw.name, sdraw.nname*sizeof(DName));
+	free(sdraw.name);
+	sdraw.name = t;
+	new = &sdraw.name[sdraw.nname++];
+	new->name = smalloc(n+1);
+	memmove(new->name, str, n);
+	new->name[n] = 0;
+	new->dimage = di;
+	new->client = client;
+	new->vers = ++sdraw.vers;
+}
+
+Client*
+drawnewclient(void)
+{
+	Client *cl, **cp;
+	int i;
+
+	for(i=0; i<sdraw.nclient; i++){
+		cl = sdraw.client[i];
+		if(cl == 0)
+			break;
+	}
+	if(i == sdraw.nclient){
+		cp = malloc((sdraw.nclient+1)*sizeof(Client*));
+		if(cp == 0)
+			return 0;
+		memmove(cp, sdraw.client, sdraw.nclient*sizeof(Client*));
+		free(sdraw.client);
+		sdraw.client = cp;
+		sdraw.nclient++;
+		cp[i] = 0;
+	}
+	cl = malloc(sizeof(Client));
+	if(cl == 0)
+		return 0;
+	memset(cl, 0, sizeof(Client));
+	cl->slot = i;
+	cl->clientid = ++sdraw.clientid;
+	cl->op = SoverD;
+	sdraw.client[i] = cl;
+	return cl;
+}
+
+static int
+drawclientop(Client *cl)
+{
+	int op;
+
+	op = cl->op;
+	cl->op = SoverD;
+	return op;
+}
+	
+int
+drawhasclients(void)
+{
+	/*
+	 * if draw has ever been used, we can't resize the frame buffer,
+	 * even if all clients have exited (nclients is cumulative); it's too
+	 * hard to make work.
+	 */
+	return sdraw.nclient != 0;
+}
+
+Client*
+drawclientofpath(ulong path)
+{
+	Client *cl;
+	int slot;
+
+	slot = CLIENTPATH(path);
+	if(slot == 0)
+		return nil;
+	cl = sdraw.client[slot-1];
+	if(cl==0 || cl->clientid==0)
+		return nil;
+	return cl;
+}
+
+
+Client*
+drawclient(Chan *c)
+{
+	Client *client;
+
+	client = drawclientofpath(c->qid.path);
+	if(client == nil)
+		error(Enoclient);
+	return client;
+}
+
+Memimage*
+drawimage(Client *client, uchar *a)
+{
+	DImage *d;
+
+	d = drawlookup(client, BGLONG(a), 1);
+	if(d == nil)
+		error(Enodrawimage);
+	return d->image;
+}
+
+void
+drawrectangle(Rectangle *r, uchar *a)
+{
+	r->min.x = BGLONG(a+0*4);
+	r->min.y = BGLONG(a+1*4);
+	r->max.x = BGLONG(a+2*4);
+	r->max.y = BGLONG(a+3*4);
+}
+
+void
+drawpoint(Point *p, uchar *a)
+{
+	p->x = BGLONG(a+0*4);
+	p->y = BGLONG(a+1*4);
+}
+
+Point
+drawchar(Memimage *dst, Point p, Memimage *src, Point *sp, DImage *font, int index, int op)
+{
+	FChar *fc;
+	Rectangle r;
+	Point sp1;
+
+	fc = &font->fchar[index];
+	r.min.x = p.x+fc->left;
+	r.min.y = p.y-(font->ascent-fc->miny);
+	r.max.x = r.min.x+(fc->maxx-fc->minx);
+	r.max.y = r.min.y+(fc->maxy-fc->miny);
+	sp1.x = sp->x+fc->left;
+	sp1.y = sp->y+fc->miny;
+	memdraw(dst, r, src, sp1, font->image, Pt(fc->minx, fc->miny), op);
+	p.x += fc->width;
+	sp->x += fc->width;
+	return p;
+}
+
+static int
+initscreenimage(void)
+{
+	int width, depth;
+	ulong chan;
+	Rectangle r;
+
+	if(screenimage != nil)
+		return 1;
+
+	screendata.base = nil;
+	screendata.bdata = attachscreen(&r, &chan, &depth, &width, &sdraw.softscreen);
+	if(screendata.bdata == nil)
+		return 0;
+	screendata.ref = 1;
+
+	screenimage = allocmemimaged(r, chan, &screendata);
+	if(screenimage == nil){
+		/* RSC: BUG: detach screen */
+		return 0;
+	}
+
+	screenimage->width = width;
+	screenimage->clipr = r;
+	return 1;
+}
+
+void
+deletescreenimage(void)
+{
+	qlock(&sdraw);
+	/* RSC: BUG: detach screen */
+	if(screenimage)
+		freememimage(screenimage);
+	screenimage = nil;
+	qunlock(&sdraw);
+}
+
+static Chan*
+drawattach(char *spec)
+{
+	qlock(&sdraw);
+	if(!initscreenimage()){
+		qunlock(&sdraw);
+		error("no frame buffer");
+	}
+	qunlock(&sdraw);
+	return devattach('i', spec);
+}
+
+static Walkqid*
+drawwalk(Chan *c, Chan *nc, char **name, int nname)
+{
+	if(screendata.bdata == nil)
+		error("no frame buffer");
+	return devwalk(c, nc, name, nname, 0, 0, drawgen);
+}
+
+static int
+drawstat(Chan *c, uchar *db, int n)
+{
+	return devstat(c, db, n, 0, 0, drawgen);
+}
+
+static Chan*
+drawopen(Chan *c, int omode)
+{
+	Client *cl;
+
+	if(c->qid.type & QTDIR)
+		return devopen(c, omode, 0, 0, drawgen);
+
+	qlock(&sdraw);
+	if(waserror()){
+		qunlock(&sdraw);
+		nexterror();
+	}
+
+	if(QID(c->qid) == Qnew){
+		cl = drawnewclient();
+		if(cl == 0)
+			error(Enodev);
+		c->qid.path = Qctl|((cl->slot+1)<<QSHIFT);
+	}
+
+	switch(QID(c->qid)){
+	case Qnew:
+		break;
+
+	case Qctl:
+		cl = drawclient(c);
+		if(cl->busy)
+			error(Einuse);
+		cl->busy = 1;
+		flushrect = Rect(10000, 10000, -10000, -10000);
+		drawinstall(cl, 0, screenimage, 0);
+		incref(&cl->r);
+		break;
+	case Qcolormap:
+	case Qdata:
+	case Qrefresh:
+		cl = drawclient(c);
+		incref(&cl->r);
+		break;
+	}
+	qunlock(&sdraw);
+	poperror();
+	c->mode = openmode(omode);
+	c->flag |= COPEN;
+	c->offset = 0;
+	c->iounit = IOUNIT;
+	return c;
+}
+
+static void
+drawclose(Chan *c)
+{
+	int i;
+	DImage *d, **dp;
+	Client *cl;
+	Refresh *r;
+
+	if(QID(c->qid) < Qcolormap)	/* Qtopdir, Qnew, Q3rd, Q2nd have no client */
+		return;
+	qlock(&sdraw);
+	if(waserror()){
+		qunlock(&sdraw);
+		nexterror();
+	}
+
+	cl = drawclient(c);
+	if(QID(c->qid) == Qctl)
+		cl->busy = 0;
+	if((c->flag&COPEN) && (decref(&cl->r)==0)){
+		while(r = cl->refresh){	/* assign = */
+			cl->refresh = r->next;
+			free(r);
+		}
+		/* free names */
+		for(i=0; i<sdraw.nname; )
+			if(sdraw.name[i].client == cl)
+				drawdelname(sdraw.name+i);
+			else
+				i++;
+		while(cl->cscreen)
+			drawuninstallscreen(cl, cl->cscreen);
+		/* all screens are freed, so now we can free images */
+		dp = cl->dimage;
+		for(i=0; i<NHASH; i++){
+			while((d = *dp) != nil){
+				*dp = d->next;
+				drawfreedimage(d);
+			}
+			dp++;
+		}
+		sdraw.client[cl->slot] = 0;
+		drawflush();	/* to erase visible, now dead windows */
+		free(cl);
+	}
+	qunlock(&sdraw);
+	poperror();
+}
+
+long
+drawread(Chan *c, void *a, long n, vlong off)
+{
+	int index, m;
+	ulong red, green, blue;
+	Client *cl;
+	uchar *p;
+	Refresh *r;
+	DImage *di;
+	Memimage *i;
+	ulong offset = off;
+	char buf[16];
+
+	if(c->qid.type & QTDIR)
+		return devdirread(c, a, n, 0, 0, drawgen);
+	cl = drawclient(c);
+	qlock(&sdraw);
+	if(waserror()){
+		qunlock(&sdraw);
+		nexterror();
+	}
+	switch(QID(c->qid)){
+	case Qctl:
+		if(n < 12*12)
+			error(Eshortread);
+		if(cl->infoid < 0)
+			error(Enodrawimage);
+		if(cl->infoid == 0){
+			i = screenimage;
+			if(i == nil)
+				error(Enodrawimage);
+		}else{
+			di = drawlookup(cl, cl->infoid, 1);
+			if(di == nil)
+				error(Enodrawimage);
+			i = di->image;
+		}
+		n = sprint(a, "%11d %11d %11s %11d %11d %11d %11d %11d %11d %11d %11d %11d ",
+			cl->clientid, cl->infoid, chantostr(buf, i->chan), (i->flags&Frepl)==Frepl,
+			i->r.min.x, i->r.min.y, i->r.max.x, i->r.max.y,
+			i->clipr.min.x, i->clipr.min.y, i->clipr.max.x, i->clipr.max.y);
+		cl->infoid = -1;
+		break;
+
+	case Qcolormap:
+		drawactive(1);	/* to restore map from backup */
+		p = malloc(4*12*256+1);
+		if(p == 0)
+			error(Enomem);
+		m = 0;
+		for(index = 0; index < 256; index++){
+			getcolor(index, &red, &green, &blue);
+			m += sprint((char*)p+m, "%11d %11lud %11lud %11lud\n", index, red>>24, green>>24, blue>>24);
+		}
+		n = readstr(offset, a, n, (char*)p);
+		free(p);
+		break;
+
+	case Qdata:
+		if(cl->readdata == nil)
+			error("no draw data");
+		if(n < cl->nreaddata)
+			error(Eshortread);
+		n = cl->nreaddata;
+		memmove(a, cl->readdata, cl->nreaddata);
+		free(cl->readdata);
+		cl->readdata = nil;
+		break;
+
+	case Qrefresh:
+		if(n < 5*4)
+			error(Ebadarg);
+		for(;;){
+			if(cl->refreshme || cl->refresh)
+				break;
+			qunlock(&sdraw);
+			if(waserror()){
+				qlock(&sdraw);	/* restore lock for waserror() above */
+				nexterror();
+			}
+			sleep(&cl->refrend, drawrefactive, cl);
+			poperror();
+			qlock(&sdraw);
+		}
+		p = a;
+		while(cl->refresh && n>=5*4){
+			r = cl->refresh;
+			BPLONG(p+0*4, r->dimage->id);
+			BPLONG(p+1*4, r->r.min.x);
+			BPLONG(p+2*4, r->r.min.y);
+			BPLONG(p+3*4, r->r.max.x);
+			BPLONG(p+4*4, r->r.max.y);
+			cl->refresh = r->next;
+			free(r);
+			p += 5*4;
+			n -= 5*4;
+		}
+		cl->refreshme = 0;
+		n = p-(uchar*)a;
+	}
+	qunlock(&sdraw);
+	poperror();
+	return n;
+}
+
+void
+drawwakeall(void)
+{
+	Client *cl;
+	int i;
+
+	for(i=0; i<sdraw.nclient; i++){
+		cl = sdraw.client[i];
+		if(cl && (cl->refreshme || cl->refresh))
+			wakeup(&cl->refrend);
+	}
+}
+
+static long
+drawwrite(Chan *c, void *a, long n, vlong)
+{
+	char buf[128], *fields[4], *q;
+	Client *cl;
+	int i, m, red, green, blue, x;
+
+	if(c->qid.type & QTDIR)
+		error(Eisdir);
+	cl = drawclient(c);
+	qlock(&sdraw);
+	if(waserror()){
+		drawwakeall();
+		qunlock(&sdraw);
+		nexterror();
+	}
+	switch(QID(c->qid)){
+	case Qctl:
+		if(n != 4)
+			error("unknown draw control request");
+		cl->infoid = BGLONG((uchar*)a);
+		break;
+
+	case Qcolormap:
+		drawactive(1);	/* to restore map from backup */
+		m = n;
+		n = 0;
+		while(m > 0){
+			x = m;
+			if(x > sizeof(buf)-1)
+				x = sizeof(buf)-1;
+			q = memccpy(buf, a, '\n', x);
+			if(q == 0)
+				break;
+			i = q-buf;
+			n += i;
+			a = (char*)a + i;
+			m -= i;
+			*q = 0;
+			if(tokenize(buf, fields, nelem(fields)) != 4)
+				error(Ebadarg);
+			i = strtoul(fields[0], 0, 0);
+			red = strtoul(fields[1], 0, 0);
+			green = strtoul(fields[2], 0, 0);
+			blue = strtoul(fields[3], &q, 0);
+			if(fields[3] == q)
+				error(Ebadarg);
+			if(red>255 || green>255 || blue>255 || i<0 || i>255)
+				error(Ebadarg);
+			red |= red<<8;
+			red |= red<<16;
+			green |= green<<8;
+			green |= green<<16;
+			blue |= blue<<8;
+			blue |= blue<<16;
+			setcolor(i, red, green, blue);
+		}
+		break;
+
+	case Qdata:
+		drawmesg(cl, a, n);
+		drawwakeall();
+		break;
+
+	default:
+		error(Ebadusefd);
+	}
+	qunlock(&sdraw);
+	poperror();
+	return n;
+}
+
+uchar*
+drawcoord(uchar *p, uchar *maxp, int oldx, int *newx)
+{
+	int b, x;
+
+	if(p >= maxp)
+		error(Eshortdraw);
+	b = *p++;
+	x = b & 0x7F;
+	if(b & 0x80){
+		if(p+1 >= maxp)
+			error(Eshortdraw);
+		x |= *p++ << 7;
+		x |= *p++ << 15;
+		if(x & (1<<22))
+			x |= ~0<<23;
+	}else{
+		if(b & 0x40)
+			x |= ~0<<7;
+		x += oldx;
+	}
+	*newx = x;
+	return p;
+}
+
+static void
+printmesg(char *fmt, uchar *a, int plsprnt)
+{
+	char buf[256];
+	char *p, *q;
+	int s;
+
+	if(1|| plsprnt==0){
+		SET(s,q,p);
+		USED(fmt, a, buf, p, q, s);
+		return;
+	}
+	q = buf;
+	*q++ = *a++;
+	for(p=fmt; *p; p++){
+		switch(*p){
+		case 'l':
+			q += sprint(q, " %ld", (long)BGLONG(a));
+			a += 4;
+			break;
+		case 'L':
+			q += sprint(q, " %.8lux", (ulong)BGLONG(a));
+			a += 4;
+			break;
+		case 'R':
+			q += sprint(q, " [%d %d %d %d]", BGLONG(a), BGLONG(a+4), BGLONG(a+8), BGLONG(a+12));
+			a += 16;
+			break;
+		case 'P':
+			q += sprint(q, " [%d %d]", BGLONG(a), BGLONG(a+4));
+			a += 8;
+			break;
+		case 'b':
+			q += sprint(q, " %d", *a++);
+			break;
+		case 's':
+			q += sprint(q, " %d", BGSHORT(a));
+			a += 2;
+			break;
+		case 'S':
+			q += sprint(q, " %.4ux", BGSHORT(a));
+			a += 2;
+			break;
+		}
+	}
+	*q++ = '\n';
+	*q = 0;
+	iprint("%.*s", (int)(q-buf), buf);
+}
+
+void
+drawmesg(Client *client, void *av, int n)
+{
+	int c, op, repl, m, y, dstid, scrnid, ni, ci, j, nw, e0, e1, ox, oy, esize, oesize, doflush;
+	uchar *u, *a, refresh;
+	char *fmt;
+	ulong value, chan;
+	Rectangle r, clipr;
+	Point p, q, *pp, sp;
+	Memimage *i, *dst, *src, *mask;
+	Memimage *l, **lp;
+	Memscreen *scrn;
+	DImage *font, *ll, *di, *ddst, *dsrc;
+	DName *dn;
+	DScreen *dscrn;
+	FChar *fc;
+	Refx *refx;
+	CScreen *cs;
+	Refreshfn reffn;
+
+	a = av;
+	m = 0;
+	fmt = nil;
+	if(waserror()){
+		if(fmt) printmesg(fmt, a, 1);
+	/*	iprint("error: %s\n", up->env->errstr);	*/
+		nexterror();
+	}
+	while((n-=m) > 0){
+		USED(fmt);
+		a += m;
+		switch(*a){
+		default:
+			error("bad draw command");
+		/* new allocate: 'b' id[4] screenid[4] refresh[1] chan[4] repl[1] R[4*4] clipR[4*4] rrggbbaa[4] */
+		case 'b':
+			printmesg(fmt="LLbLbRRL", a, 0);
+			m = 1+4+4+1+4+1+4*4+4*4+4;
+			if(n < m)
+				error(Eshortdraw);
+			dstid = BGLONG(a+1);
+			scrnid = BGSHORT(a+5);
+			refresh = a[9];
+			chan = BGLONG(a+10);
+			repl = a[14];
+			drawrectangle(&r, a+15);
+			drawrectangle(&clipr, a+31);
+			value = BGLONG(a+47);
+			if(drawlookup(client, dstid, 0))
+				error(Eimageexists);
+			if(scrnid){
+				dscrn = drawlookupscreen(client, scrnid, &cs);
+				scrn = dscrn->screen;
+				if(repl || chan!=scrn->image->chan)
+					error("image parameters incompatible with screen");
+				reffn = nil;
+				switch(refresh){
+				case Refbackup:
+					break;
+				case Refnone:
+					reffn = memlnorefresh;
+					break;
+				case Refmesg:
+					reffn = drawrefresh;
+					break;
+				default:
+					error("unknown refresh method");
+				}
+				l = memlalloc(scrn, r, reffn, 0, value);
+				if(l == 0)
+					error(Edrawmem);
+				dstflush(l->layer->screen->image, l->layer->screenr);
+				l->clipr = clipr;
+				rectclip(&l->clipr, r);
+				if(drawinstall(client, dstid, l, dscrn) == 0){
+					memldelete(l);
+					error(Edrawmem);
+				}
+				dscrn->ref++;
+				if(reffn){
+					refx = nil;
+					if(reffn == drawrefresh){
+						refx = malloc(sizeof(Refx));
+						if(refx == 0){
+							drawuninstall(client, dstid);
+							error(Edrawmem);
+						}
+						refx->client = client;
+						refx->dimage = drawlookup(client, dstid, 1);
+					}
+					memlsetrefresh(l, reffn, refx);
+				}
+				continue;
+			}
+			i = allocmemimage(r, chan);
+			if(i == 0)
+				error(Edrawmem);
+			if(repl)
+				i->flags |= Frepl;
+			i->clipr = clipr;
+			if(!repl)
+				rectclip(&i->clipr, r);
+			if(drawinstall(client, dstid, i, 0) == 0){
+				freememimage(i);
+				error(Edrawmem);
+			}
+			memfillcolor(i, value);
+			continue;
+
+		/* allocate screen: 'A' id[4] imageid[4] fillid[4] public[1] */
+		case 'A':
+			printmesg(fmt="LLLb", a, 1);
+			m = 1+4+4+4+1;
+			if(n < m)
+				error(Eshortdraw);
+			dstid = BGLONG(a+1);
+			if(dstid == 0)
+				error(Ebadarg);
+			if(drawlookupdscreen(dstid))
+				error(Escreenexists);
+			ddst = drawlookup(client, BGLONG(a+5), 1);
+			dsrc = drawlookup(client, BGLONG(a+9), 1);
+			if(ddst==0 || dsrc==0)
+				error(Enodrawimage);
+			if(drawinstallscreen(client, 0, dstid, ddst, dsrc, a[13]) == 0)
+				error(Edrawmem);
+			continue;
+
+		/* set repl and clip: 'c' dstid[4] repl[1] clipR[4*4] */
+		case 'c':
+			printmesg(fmt="LbR", a, 0);
+			m = 1+4+1+4*4;
+			if(n < m)
+				error(Eshortdraw);
+			ddst = drawlookup(client, BGLONG(a+1), 1);
+			if(ddst == nil)
+				error(Enodrawimage);
+			if(ddst->name)
+				error("cannot change repl/clipr of shared image");
+			dst = ddst->image;
+			if(a[5])
+				dst->flags |= Frepl;
+			drawrectangle(&dst->clipr, a+6);
+			continue;
+
+		/* draw: 'd' dstid[4] srcid[4] maskid[4] R[4*4] P[2*4] P[2*4] */
+		case 'd':
+			printmesg(fmt="LLLRPP", a, 0);
+			m = 1+4+4+4+4*4+2*4+2*4;
+			if(n < m)
+				error(Eshortdraw);
+			dst = drawimage(client, a+1);
+			src = drawimage(client, a+5);
+			mask = drawimage(client, a+9);
+			drawrectangle(&r, a+13);
+			drawpoint(&p, a+29);
+			drawpoint(&q, a+37);
+			op = drawclientop(client);
+			memdraw(dst, r, src, p, mask, q, op);
+			dstflush(dst, r);
+			continue;
+
+		/* toggle debugging: 'D' val[1] */
+		case 'D':
+			printmesg(fmt="b", a, 0);
+			m = 1+1;
+			if(n < m)
+				error(Eshortdraw);
+			drawdebug = a[1];
+			continue;
+
+		/* ellipse: 'e' dstid[4] srcid[4] center[2*4] a[4] b[4] thick[4] sp[2*4] alpha[4] phi[4]*/
+		case 'e':
+		case 'E':
+			printmesg(fmt="LLPlllPll", a, 0);
+			m = 1+4+4+2*4+4+4+4+2*4+2*4;
+			if(n < m)
+				error(Eshortdraw);
+			dst = drawimage(client, a+1);
+			src = drawimage(client, a+5);
+			drawpoint(&p, a+9);
+			e0 = BGLONG(a+17);
+			e1 = BGLONG(a+21);
+			if(e0<0 || e1<0)
+				error("invalid ellipse semidiameter");
+			j = BGLONG(a+25);
+			if(j < 0)
+				error("negative ellipse thickness");
+			drawpoint(&sp, a+29);
+			c = j;
+			if(*a == 'E')
+				c = -1;
+			ox = BGLONG(a+37);
+			oy = BGLONG(a+41);
+			op = drawclientop(client);
+			/* high bit indicates arc angles are present */
+			if(ox & (1<<31)){
+				if((ox & (1<<30)) == 0)
+					ox &= ~(1<<31);
+				memarc(dst, p, e0, e1, c, src, sp, ox, oy, op);
+			}else
+				memellipse(dst, p, e0, e1, c, src, sp, op);
+			dstflush(dst, Rect(p.x-e0-j, p.y-e1-j, p.x+e0+j+1, p.y+e1+j+1));
+			continue;
+
+		/* free: 'f' id[4] */
+		case 'f':
+			printmesg(fmt="L", a, 1);
+			m = 1+4;
+			if(n < m)
+				error(Eshortdraw);
+			ll = drawlookup(client, BGLONG(a+1), 0);
+			if(ll && ll->dscreen && ll->dscreen->owner != client)
+				ll->dscreen->owner->refreshme = 1;
+			drawuninstall(client, BGLONG(a+1));
+			continue;
+
+		/* free screen: 'F' id[4] */
+		case 'F':
+			printmesg(fmt="L", a, 1);
+			m = 1+4;
+			if(n < m)
+				error(Eshortdraw);
+			drawlookupscreen(client, BGLONG(a+1), &cs);
+			drawuninstallscreen(client, cs);
+			continue;
+
+		/* initialize font: 'i' fontid[4] nchars[4] ascent[1] */
+		case 'i':
+			printmesg(fmt="Llb", a, 1);
+			m = 1+4+4+1;
+			if(n < m)
+				error(Eshortdraw);
+			dstid = BGLONG(a+1);
+			dst = drawimage(client, a+1);
+			if(dstid == 0 || dst == screenimage)
+				error("cannot use display as font");
+			font = drawlookup(client, dstid, 1);
+			if(font == 0)
+				error(Enodrawimage);
+			if(font->image->layer)
+				error("cannot use window as font");
+			ni = BGLONG(a+5);
+			if(ni<=0 || ni>4096)
+				error("bad font size (4096 chars max)");
+			free(font->fchar);	/* should we complain if non-zero? */
+			font->fchar = malloc(ni*sizeof(FChar));
+			if(font->fchar == 0)
+				error(Enomem);
+			memset(font->fchar, 0, ni*sizeof(FChar));
+			font->nfchar = ni;
+			font->ascent = a[9];
+			continue;
+
+		/* load character: 'l' fontid[4] srcid[4] index[2] R[4*4] P[2*4] left[1] width[1] */
+		case 'l':
+			printmesg(fmt="LLSRPbb", a, 0);
+			m = 1+4+4+2+4*4+2*4+1+1;
+			if(n < m)
+				error(Eshortdraw);
+			font = drawlookup(client, BGLONG(a+1), 1);
+			if(font == 0)
+				error(Enodrawimage);
+			if(font->nfchar == 0)
+				error(Enotfont);
+			src = drawimage(client, a+5);
+			ci = BGSHORT(a+9);
+			if(ci >= font->nfchar)
+				error(Eindex);
+			drawrectangle(&r, a+11);
+			drawpoint(&p, a+27);
+			memdraw(font->image, r, src, p, memopaque, p, S);
+			fc = &font->fchar[ci];
+			fc->minx = r.min.x;
+			fc->maxx = r.max.x;
+			fc->miny = r.min.y;
+			fc->maxy = r.max.y;
+			fc->left = a[35];
+			fc->width = a[36];
+			continue;
+
+		/* draw line: 'L' dstid[4] p0[2*4] p1[2*4] end0[4] end1[4] radius[4] srcid[4] sp[2*4] */
+		case 'L':
+			printmesg(fmt="LPPlllLP", a, 0);
+			m = 1+4+2*4+2*4+4+4+4+4+2*4;
+			if(n < m)
+				error(Eshortdraw);
+			dst = drawimage(client, a+1);
+			drawpoint(&p, a+5);
+			drawpoint(&q, a+13);
+			e0 = BGLONG(a+21);
+			e1 = BGLONG(a+25);
+			j = BGLONG(a+29);
+			if(j < 0)
+				error("negative line width");
+			src = drawimage(client, a+33);
+			drawpoint(&sp, a+37);
+			op = drawclientop(client);
+			memline(dst, p, q, e0, e1, j, src, sp, op);
+			/* avoid memlinebbox if possible */
+			if(dst == screenimage || dst->layer!=nil){
+				/* BUG: this is terribly inefficient: update maximal containing rect*/
+				r = memlinebbox(p, q, e0, e1, j);
+				dstflush(dst, insetrect(r, -(1+1+j)));
+			}
+			continue;
+
+		/* create image mask: 'm' newid[4] id[4] */
+/*
+ *
+		case 'm':
+			printmesg("LL", a, 0);
+			m = 4+4;
+			if(n < m)
+				error(Eshortdraw);
+			break;
+ *
+ */
+
+		/* attach to a named image: 'n' dstid[4] j[1] name[j] */
+		case 'n':
+			printmesg(fmt="Lz", a, 0);
+			m = 1+4+1;
+			if(n < m)
+				error(Eshortdraw);
+			j = a[5];
+			if(j == 0)	/* give me a non-empty name please */
+				error(Eshortdraw);
+			m += j;
+			if(n < m)
+				error(Eshortdraw);
+			dstid = BGLONG(a+1);
+			if(drawlookup(client, dstid, 0))
+				error(Eimageexists);
+			dn = drawlookupname(j, (char*)a+6);
+			if(dn == nil)
+				error(Enoname);
+			if(drawinstall(client, dstid, dn->dimage->image, 0) == 0)
+				error(Edrawmem);
+			di = drawlookup(client, dstid, 0);
+			if(di == 0)
+				error("draw: cannot happen");
+			di->vers = dn->vers;
+			di->name = smalloc(j+1);
+			di->fromname = dn->dimage;
+			di->fromname->ref++;
+			memmove(di->name, a+6, j);
+			di->name[j] = 0;
+			client->infoid = dstid;
+			continue;
+
+		/* name an image: 'N' dstid[4] in[1] j[1] name[j] */
+		case 'N':
+			printmesg(fmt="Lbz", a, 0);
+			m = 1+4+1+1;
+			if(n < m)
+				error(Eshortdraw);
+			c = a[5];
+			j = a[6];
+			if(j == 0)	/* give me a non-empty name please */
+				error(Eshortdraw);
+			m += j;
+			if(n < m)
+				error(Eshortdraw);
+			di = drawlookup(client, BGLONG(a+1), 0);
+			if(di == 0)
+				error(Enodrawimage);
+			if(di->name)
+				error(Enamed);
+			if(c)
+				drawaddname(client, di, j, (char*)a+7);
+			else{
+				dn = drawlookupname(j, (char*)a+7);
+				if(dn == nil)
+					error(Enoname);
+				if(dn->dimage != di)
+					error(Ewrongname);
+				drawdelname(dn);
+			}
+			continue;
+
+		/* position window: 'o' id[4] r.min [2*4] screenr.min [2*4] */
+		case 'o':
+			printmesg(fmt="LPP", a, 0);
+			m = 1+4+2*4+2*4;
+			if(n < m)
+				error(Eshortdraw);
+			dst = drawimage(client, a+1);
+			if(dst->layer){
+				drawpoint(&p, a+5);
+				drawpoint(&q, a+13);
+				r = dst->layer->screenr;
+				ni = memlorigin(dst, p, q);
+				if(ni < 0)
+					error("image origin failed");
+				if(ni > 0){
+					dstflush(dst->layer->screen->image, r);
+					dstflush(dst->layer->screen->image, dst->layer->screenr);
+					ll = drawlookup(client, BGLONG(a+1), 1);
+					drawrefreshscreen(ll, client);
+				}
+			}
+			continue;
+
+		/* set compositing operator for next draw operation: 'O' op */
+		case 'O':
+			printmesg(fmt="b", a, 0);
+			m = 1+1;
+			if(n < m)
+				error(Eshortdraw);
+			client->op = a[1];
+			continue;
+
+		/* filled polygon: 'P' dstid[4] n[2] wind[4] ignore[2*4] srcid[4] sp[2*4] p0[2*4] dp[2*2*n] */
+		/* polygon: 'p' dstid[4] n[2] end0[4] end1[4] radius[4] srcid[4] sp[2*4] p0[2*4] dp[2*2*n] */
+		case 'p':
+		case 'P':
+			printmesg(fmt="LslllLPP", a, 0);
+			m = 1+4+2+4+4+4+4+2*4;
+			if(n < m)
+				error(Eshortdraw);
+			dst = drawimage(client, a+1);
+			ni = BGSHORT(a+5);
+			if(ni < 0)
+				error("negative count in polygon");
+			e0 = BGLONG(a+7);
+			e1 = BGLONG(a+11);
+			j = 0;
+			if(*a == 'p'){
+				j = BGLONG(a+15);
+				if(j < 0)
+					error("negative polygon line width");
+			}
+			src = drawimage(client, a+19);
+			drawpoint(&sp, a+23);
+			drawpoint(&p, a+31);
+			ni++;
+			pp = malloc(ni*sizeof(Point));
+			if(pp == nil)
+				error(Enomem);
+			doflush = 0;
+			if(dst == screenimage || (dst->layer && dst->layer->screen->image->data == screenimage->data))
+				doflush = 1;	/* simplify test in loop */
+			ox = oy = 0;
+			esize = 0;
+			u = a+m;
+			for(y=0; y<ni; y++){
+				q = p;
+				oesize = esize;
+				u = drawcoord(u, a+n, ox, &p.x);
+				u = drawcoord(u, a+n, oy, &p.y);
+				ox = p.x;
+				oy = p.y;
+				if(doflush){
+					esize = j;
+					if(*a == 'p'){
+						if(y == 0){
+							c = memlineendsize(e0);
+							if(c > esize)
+								esize = c;
+						}
+						if(y == ni-1){
+							c = memlineendsize(e1);
+							if(c > esize)
+								esize = c;
+						}
+					}
+					if(*a=='P' && e0!=1 && e0 !=~0)
+						r = dst->clipr;
+					else if(y > 0){
+						r = Rect(q.x-oesize, q.y-oesize, q.x+oesize+1, q.y+oesize+1);
+						combinerect(&r, Rect(p.x-esize, p.y-esize, p.x+esize+1, p.y+esize+1));
+					}
+					if(rectclip(&r, dst->clipr))		/* should perhaps be an arg to dstflush */
+						dstflush(dst, r);
+				}
+				pp[y] = p;
+			}
+			if(y == 1)
+				dstflush(dst, Rect(p.x-esize, p.y-esize, p.x+esize+1, p.y+esize+1));
+			op = drawclientop(client);
+			if(*a == 'p')
+				mempoly(dst, pp, ni, e0, e1, j, src, sp, op);
+			else
+				memfillpoly(dst, pp, ni, e0, src, sp, op);
+			free(pp);
+			m = u-a;
+			continue;
+
+		/* read: 'r' id[4] R[4*4] */
+		case 'r':
+			printmesg(fmt="LR", a, 0);
+			m = 1+4+4*4;
+			if(n < m)
+				error(Eshortdraw);
+			i = drawimage(client, a+1);
+			drawrectangle(&r, a+5);
+			if(!rectinrect(r, i->r))
+				error(Ereadoutside);
+			c = bytesperline(r, i->depth);
+			c *= Dy(r);
+			free(client->readdata);
+			client->readdata = mallocz(c, 0);
+			if(client->readdata == nil)
+				error("readimage malloc failed");
+			client->nreaddata = memunload(i, r, client->readdata, c);
+			if(client->nreaddata < 0){
+				free(client->readdata);
+				client->readdata = nil;
+				error("bad readimage call");
+			}
+			continue;
+
+		/* string: 's' dstid[4] srcid[4] fontid[4] P[2*4] clipr[4*4] sp[2*4] ni[2] ni*(index[2]) */
+		/* stringbg: 'x' dstid[4] srcid[4] fontid[4] P[2*4] clipr[4*4] sp[2*4] ni[2] bgid[4] bgpt[2*4] ni*(index[2]) */
+		case 's':
+		case 'x':
+			printmesg(fmt="LLLPRPs", a, 0);
+			m = 1+4+4+4+2*4+4*4+2*4+2;
+			if(*a == 'x')
+				m += 4+2*4;
+			if(n < m)
+				error(Eshortdraw);
+
+			dst = drawimage(client, a+1);
+			src = drawimage(client, a+5);
+			font = drawlookup(client, BGLONG(a+9), 1);
+			if(font == 0)
+				error(Enodrawimage);
+			if(font->nfchar == 0)
+				error(Enotfont);
+			drawpoint(&p, a+13);
+			drawrectangle(&r, a+21);
+			drawpoint(&sp, a+37);
+			ni = BGSHORT(a+45);
+			u = a+m;
+			m += ni*2;
+			if(n < m)
+				error(Eshortdraw);
+			clipr = dst->clipr;
+			dst->clipr = r;
+			op = drawclientop(client);
+			if(*a == 'x'){
+				/* paint background */
+				l = drawimage(client, a+47);
+				drawpoint(&q, a+51);
+				r.min.x = p.x;
+				r.min.y = p.y-font->ascent;
+				r.max.x = p.x;
+				r.max.y = r.min.y+Dy(font->image->r);
+				j = ni;
+				while(--j >= 0){
+					ci = BGSHORT(u);
+					if(ci<0 || ci>=font->nfchar){
+						dst->clipr = clipr;
+						error(Eindex);
+					}
+					r.max.x += font->fchar[ci].width;
+					u += 2;
+				}
+				memdraw(dst, r, l, q, memopaque, ZP, op);
+				u -= 2*ni;
+			}
+			q = p;
+			while(--ni >= 0){
+				ci = BGSHORT(u);
+				if(ci<0 || ci>=font->nfchar){
+					dst->clipr = clipr;
+					error(Eindex);
+				}
+				q = drawchar(dst, q, src, &sp, font, ci, op);
+				u += 2;
+			}
+			dst->clipr = clipr;
+			p.y -= font->ascent;
+			dstflush(dst, Rect(p.x, p.y, q.x, p.y+Dy(font->image->r)));
+			continue;
+
+		/* use public screen: 'S' id[4] chan[4] */
+		case 'S':
+			printmesg(fmt="Ll", a, 0);
+			m = 1+4+4;
+			if(n < m)
+				error(Eshortdraw);
+			dstid = BGLONG(a+1);
+			if(dstid == 0)
+				error(Ebadarg);
+			dscrn = drawlookupdscreen(dstid);
+			if(dscrn==0 || (dscrn->public==0 && dscrn->owner!=client))
+				error(Enodrawscreen);
+			if(dscrn->screen->image->chan != BGLONG(a+5))
+				error("inconsistent chan");
+			if(drawinstallscreen(client, dscrn, 0, 0, 0, 0) == 0)
+				error(Edrawmem);
+			continue;
+
+		/* top or bottom windows: 't' top[1] nw[2] n*id[4] */
+		case 't':
+			printmesg(fmt="bsL", a, 0);
+			m = 1+1+2;
+			if(n < m)
+				error(Eshortdraw);
+			nw = BGSHORT(a+2);
+			if(nw < 0)
+				error(Ebadarg);
+			if(nw == 0)
+				continue;
+			m += nw*4;
+			if(n < m)
+				error(Eshortdraw);
+			lp = malloc(nw*sizeof(Memimage*));
+			if(lp == 0)
+				error(Enomem);
+			if(waserror()){
+				free(lp);
+				nexterror();
+			}
+			for(j=0; j<nw; j++)
+				lp[j] = drawimage(client, a+1+1+2+j*4);
+			if(lp[0]->layer == 0)
+				error("images are not windows");
+			for(j=1; j<nw; j++)
+				if(lp[j]->layer->screen != lp[0]->layer->screen)
+					error("images not on same screen");
+			if(a[1])
+				memltofrontn(lp, nw);
+			else
+				memltorearn(lp, nw);
+			if(lp[0]->layer->screen->image->data == screenimage->data)
+				for(j=0; j<nw; j++)
+					dstflush(lp[j]->layer->screen->image, lp[j]->layer->screenr);
+			ll = drawlookup(client, BGLONG(a+1+1+2), 1);
+			drawrefreshscreen(ll, client);
+			poperror();
+			free(lp);
+			continue;
+
+		/* visible: 'v' */
+		case 'v':
+			printmesg(fmt="", a, 0);
+			m = 1;
+			drawflush();
+			continue;
+
+		/* write: 'y' id[4] R[4*4] data[x*1] */
+		/* write from compressed data: 'Y' id[4] R[4*4] data[x*1] */
+		case 'y':
+		case 'Y':
+			printmesg(fmt="LR", a, 0);
+		//	iprint("load %c\n", *a);
+			m = 1+4+4*4;
+			if(n < m)
+				error(Eshortdraw);
+			dst = drawimage(client, a+1);
+			drawrectangle(&r, a+5);
+			if(!rectinrect(r, dst->r))
+				error(Ewriteoutside);
+			y = memload(dst, r, a+m, n-m, *a=='Y');
+			if(y < 0)
+				error("bad writeimage call");
+			dstflush(dst, r);
+			m += y;
+			continue;
+		}
+	}
+	poperror();
+}
+
+int
+drawlsetrefresh(ulong qidpath, int id, void *reffn, void *refx)
+{
+	DImage *d;
+	Memimage *i;
+	Client *client;
+
+	client = drawclientofpath(qidpath);
+	if(client == 0)
+		return 0;
+	d = drawlookup(client, id, 0);
+	if(d == nil)
+		return 0;
+	i = d->image;
+	if(i->layer == nil)
+		return 0;
+	return memlsetrefresh(i, reffn, refx);
+}
+
+Dev drawdevtab = {
+	'i',
+	"draw",
+
+	devreset,
+	devinit,
+	devshutdown,
+	drawattach,
+	drawwalk,
+	drawstat,
+	drawopen,
+	devcreate,
+	drawclose,
+	drawread,
+	devbread,
+	drawwrite,
+	devbwrite,
+	devremove,
+	devwstat,
+};
+
+/*
+ * On 8 bit displays, load the default color map
+ */
+void
+drawcmap(void)
+{
+	int r, g, b, cr, cg, cb, v;
+	int num, den;
+	int i, j;
+
+	drawactive(1);	/* to restore map from backup */
+	for(r=0,i=0; r!=4; r++)
+	    for(v=0; v!=4; v++,i+=16){
+		for(g=0,j=v-r; g!=4; g++)
+		    for(b=0;b!=4;b++,j++){
+			den = r;
+			if(g > den)
+				den = g;
+			if(b > den)
+				den = b;
+			if(den == 0)	/* divide check -- pick grey shades */
+				cr = cg = cb = v*17;
+			else{
+				num = 17*(4*den+v);
+				cr = r*num/den;
+				cg = g*num/den;
+				cb = b*num/den;
+			}
+			setcolor(i+(j&15),
+				cr*0x01010101, cg*0x01010101, cb*0x01010101);
+		    }
+	}
+}
+
+void
+drawblankscreen(int blank)
+{
+	int i, nc;
+	ulong *p;
+
+	if(blank == sdraw.blanked)
+		return;
+	if(!canqlock(&sdraw))
+		return;
+	if(!initscreenimage()){
+		qunlock(&sdraw);
+		return;
+	}
+	p = sdraw.savemap;
+	nc = screenimage->depth > 8 ? 256 : 1<<screenimage->depth;
+
+	/*
+	 * blankscreen uses the hardware to blank the screen
+	 * when possible.  to help in cases when it is not possible,
+	 * we set the color map to be all black.
+	 */
+	if(blank == 0){	/* turn screen on */
+		for(i=0; i<nc; i++, p+=3)
+			setcolor(i, p[0], p[1], p[2]);
+		blankscreen(0);
+	}else{	/* turn screen off */
+		blankscreen(1);
+		for(i=0; i<nc; i++, p+=3){
+			getcolor(i, &p[0], &p[1], &p[2]);
+			setcolor(i, 0, 0, 0);
+		}
+	}
+	sdraw.blanked = blank;
+	qunlock(&sdraw);
+}
+
+/*
+ * record activity on screen, changing blanking as appropriate
+ */
+void
+drawactive(int active)
+{
+	if(active){
+		drawblankscreen(0);
+		sdraw.blanktime = 0;
+	}else{
+		if(blanktime && TK2SEC(sdraw.blanktime)/60 >= blanktime)
+			drawblankscreen(1);
+		else
+			sdraw.blanktime++;
+	}
+}
+
+void
+interf(void)
+{
+	/* force it to load */
+	drawreplxy(0, 0, 0);
+}
+
+int
+drawidletime(void)
+{
+	return TK2SEC(MACHP(0)->ticks - sdraw.blanktime)/60;
+}
--- /dev/null
+++ b/os/port/devds.c
@@ -1,0 +1,605 @@
+/*
+ * (file system) device subsystems
+ * '#k'. 
+ * Follows device config in Ken's file server.
+ * Builds mirrors, device cats, interleaving, and partition of devices out of
+ * other (inner) devices.
+ *
+ * This code is from Plan 9, and subject to the Lucent Public License 1.02.
+ * Only the name changed for Inferno (name clash).
+ */
+
+#include "u.h"
+#include "../port/lib.h"
+#include "mem.h"
+#include "dat.h"
+#include "fns.h"
+#include "io.h"
+#include "ureg.h"
+#include "../port/error.h"
+
+enum {
+	Fmirror,	// mirror of others
+	Fcat,		// catenation of others
+	Finter,		// interleaving of others
+	Fpart,		// part of others
+
+	Blksize	= 8*1024,	// for Finter only
+	Maxconf	= 1024,		// max length for config
+
+	Nfsdevs = 64,
+	Ndevs	= 8,
+
+	Qtop	= 0,	// top dir (contains "ds")
+	Qdir	= 1,	// actual dir
+	Qctl	= 2,	// ctl file
+	Qfirst	= 3,	// first fs file
+};
+
+#define	Cfgstr	"fsdev:\n"
+
+typedef struct Fsdev Fsdev;
+
+struct Fsdev
+{
+	int	type;
+	char	*name;		// name for this fsdev
+	vlong	start;		// start address (for Fpart)
+	vlong	size;		// min(idev sizes)
+	int	ndevs;		// number of inner devices
+	char	*iname[Ndevs];	// inner device names
+	Chan	*idev[Ndevs];	// inner devices
+	vlong	isize[Ndevs];	// sizes for inneer devices
+};
+
+/*
+ * Once configured, a fsdev is never removed. The name of those
+ * configured is never nil. We have no locks here.
+ */
+static Fsdev	fsdev[Nfsdevs];
+
+static Qid	tqid = {Qtop, 0, QTDIR};
+static Qid	dqid = {Qdir, 0, QTDIR};
+static Qid	cqid = {Qctl, 0, 0};
+
+static Cmdtab configs[] = {
+	Fmirror,"mirror",	0,
+	Fcat,	"cat",		0,
+	Finter,	"inter",	0,
+	Fpart,	"part",		5,
+};
+
+static char	confstr[Maxconf];
+static int	configed;
+
+
+static Fsdev*
+path2dev(int i, int mustexist)
+{
+	if (i < 0 || i >= nelem(fsdev))
+		error("bug: bad index in devfsdev");
+	if (mustexist && fsdev[i].name == nil)
+		error(Enonexist);
+
+	if (fsdev[i].name == nil)
+		return nil;
+	else
+		return &fsdev[i];
+}
+
+static Fsdev*
+devalloc(void)
+{
+	int	i;
+
+	for (i = 0; i < nelem(fsdev); i++)
+		if (fsdev[i].name == nil)
+			break;
+	if (i == nelem(fsdev))
+		error(Enodev);
+
+	return &fsdev[i];
+}
+
+static void
+setdsize(Fsdev* mp)
+{
+	uchar	buf[128];	/* old DIRLEN plus a little should be plenty */
+	int	i;
+	Chan	*mc;
+	Dir	d;
+	long	l;
+
+	if (mp->type != Fpart){
+		mp->start= 0;
+		mp->size = 0LL;
+	}
+	for (i = 0; i < mp->ndevs; i++){
+		mc = mp->idev[i];
+		l = devtab[mc->type]->stat(mc, buf, sizeof(buf));
+		convM2D(buf, l, &d, nil);
+		mp->isize[i] = d.length;
+		switch(mp->type){
+		case Fmirror:
+			if (mp->size == 0LL || mp->size > d.length)
+				mp->size = d.length;
+			break;
+		case Fcat:
+			mp->size += d.length;
+			break;
+		case Finter:
+			// truncate to multiple of Blksize
+			d.length = (d.length & ~(Blksize-1));
+			mp->isize[i] = d.length;
+			mp->size += d.length;
+			break;
+		case Fpart:
+			// should raise errors here?
+			if (mp->start > d.length)
+				mp->start = d.length;
+			if (d.length < mp->start + mp->size)
+				mp->size = d.length - mp->start;
+			break;
+		}
+	}
+}
+
+static void
+mpshut(Fsdev *mp)
+{
+	int	i;
+	char	*nm;
+
+	nm = mp->name;
+	mp->name = nil;		// prevent others from using this.
+	if (nm)
+		free(nm);
+	for (i = 0; i < mp->ndevs; i++){
+		if (mp->idev[i] != nil)
+			cclose(mp->idev[i]);
+		if (mp->iname[i])
+			free(mp->iname[i]);
+	}
+	memset(mp, 0, sizeof(*mp));
+}
+
+
+static void
+mconfig(char* a, long n)	// "name idev0 idev1"
+{
+	static	QLock	lck;
+	Cmdbuf	*cb;
+	Cmdtab	*ct;
+	Fsdev	*mp;
+	int	i;
+	char	*oldc;
+	char	*c;
+	vlong	size, start;
+
+	size = 0;
+	start = 0;
+	if (confstr[0] == 0)
+		seprint(confstr, confstr+sizeof(confstr), Cfgstr);
+	oldc = confstr + strlen(confstr);
+	qlock(&lck);
+	if (waserror()){
+		*oldc = 0;
+		qunlock(&lck);
+		nexterror();
+	}
+	cb = parsecmd(a, n);
+	if(waserror()){
+		free(cb);
+		nexterror();
+	}
+	c = oldc;
+	for (i = 0; i < cb->nf; i++)
+		c = seprint(c, confstr+sizeof(confstr), "%s ", cb->f[i]);
+	*(c-1) = '\n';
+	ct = lookupcmd(cb, configs, nelem(configs));
+	cb->f++;	// skip command
+	cb->nf--;
+	if (ct->index == Fpart){
+		size = strtoll(cb->f[3], nil, 10);
+		cb->nf--;
+		start = strtoll(cb->f[2], nil, 10);
+		cb->nf--;
+	}
+	for (i = 0; i < nelem(fsdev); i++)
+		if (fsdev[i].name != nil && strcmp(fsdev[i].name, cb->f[0])==0)
+			error(Eexist);
+	if (cb->nf - 1 > Ndevs)
+		error("too many devices; fix me");
+	for (i = 0; i < cb->nf; i++)
+		validname(cb->f[i], (i != 0));
+	mp = devalloc();
+	if(waserror()){
+		mpshut(mp);
+		nexterror();
+	}
+	mp->type = ct->index;
+	if (mp->type == Fpart){
+		mp->size = size;
+		mp->start = start;
+	}
+	kstrdup(&mp->name, cb->f[0]);
+	for (i = 1; i < cb->nf; i++){
+		kstrdup(&mp->iname[i-1], cb->f[i]);
+		mp->idev[i-1] = namec(mp->iname[i-1], Aopen, ORDWR, 0);
+		if (mp->idev[i-1] == nil)
+			error(Egreg);
+		mp->ndevs++;
+	}
+	setdsize(mp);
+	poperror();
+	free(cb);
+	poperror();
+	poperror();
+	configed = 1;
+	qunlock(&lck);
+	
+}
+
+static void
+rdconf(void)
+{
+	int	mustrd;
+	char	*s;
+	char	*c;
+	char	*p;
+	char	*e;
+	Chan	*cc;
+
+	s = getconf("fsconfig");
+	if (s == nil){
+		mustrd = 0;
+		s = "/dev/sdC0/fscfg";
+	} else
+		mustrd = 1;
+	if (waserror()){
+		configed = 1;
+		if (!mustrd)
+			return;
+		nexterror();
+	}
+	cc = namec(s, Aopen, OREAD, 0);
+	if(waserror()){
+		cclose(cc);
+		nexterror();
+	}
+	devtab[cc->type]->read(cc, confstr, sizeof(confstr), 0);
+	poperror();
+	cclose(cc);
+	if (strncmp(confstr, Cfgstr, strlen(Cfgstr)) != 0)
+		error("config string must start with `fsdev:'");
+	kstrdup(&c, confstr + strlen(Cfgstr));
+	if(waserror()){
+		free(c);
+		nexterror();
+	}
+	memset(confstr, 0, sizeof(confstr));
+	for (p = c; p != nil && *p != 0; p = e){
+		e = strchr(p, '\n');
+		if (e == p){
+			e++;
+			continue;
+		}
+		if (e == nil)
+			e = p + strlen(p);
+		mconfig(p, e - p);
+	}
+	poperror();
+	poperror();
+}
+
+
+static int
+mgen(Chan *c, char*, Dirtab*, int, int i, Dir *dp)
+{
+	Qid	qid;
+	Fsdev	*mp;
+
+	if (c->qid.path == Qtop){
+		switch(i){
+		case DEVDOTDOT:
+			devdir(c, tqid, "#k", 0, eve, DMDIR|0775, dp);
+			return 1;
+		case 0:
+			devdir(c, dqid, "ds", 0, eve, DMDIR|0775, dp);
+			return 1;
+		default:
+			return -1;
+		}
+	}
+	if (c->qid.path != Qdir){
+		switch(i){
+		case DEVDOTDOT:
+			devdir(c, dqid, "ds", 0, eve, DMDIR|0775, dp);
+			return 1;
+		default:
+			return -1;
+		}
+	}
+	switch(i){
+	case DEVDOTDOT:
+		devdir(c, tqid, "#k", 0, eve, DMDIR|0775, dp);
+		return 1;
+	case 0:
+		devdir(c, cqid, "ctl", 0, eve, 0664, dp);
+		return 1;
+	}
+	i--;	// for ctl
+	qid.path = Qfirst + i;
+	qid.vers = 0;
+	qid.type = 0;
+	mp = path2dev(i, 0);
+	if (mp == nil)
+		return -1;
+	kstrcpy(up->genbuf, mp->name, sizeof(up->genbuf));
+	devdir(c, qid, up->genbuf, mp->size, eve, 0664, dp);
+	return 1;
+}
+
+static Chan*
+mattach(char *spec)
+{
+	*confstr = 0;
+	return devattach(L'k', spec);
+}
+
+static Walkqid*
+mwalk(Chan *c, Chan *nc, char **name, int nname)
+{
+	if (!configed)
+		rdconf();
+	return devwalk(c, nc, name, nname, 0, 0, mgen);
+}
+
+static int
+mstat(Chan *c, uchar *db, int n)
+{
+	Dir	d;
+	Fsdev	*mp;
+	int	p;
+
+	p = c->qid.path;
+	memset(&d, 0, sizeof(d));
+	switch(p){
+	case Qtop:
+		devdir(c, tqid, "#k", 0, eve, DMDIR|0775, &d);
+		break;
+	case Qdir:
+		devdir(c, dqid, "ds", 0, eve, DMDIR|0775, &d);
+		break;
+	case Qctl:
+		devdir(c, cqid, "ctl", 0, eve, 0664, &d);
+		break;
+	default:
+		mp = path2dev(p - Qfirst, 1);
+		devdir(c, c->qid, mp->name, mp->size, eve, 0664, &d);
+	}
+	n = convD2M(&d, db, n);
+	if (n == 0)
+		error(Ebadarg);
+	return n;
+}
+
+static Chan*
+mopen(Chan *c, int omode)
+{
+	if((c->qid.type & QTDIR) && omode != OREAD)
+		error(Eperm);
+	if (omode & OTRUNC)
+		omode &= ~OTRUNC;
+	c->mode = openmode(omode);
+	c->flag |= COPEN;
+	c->offset = 0;
+	return c;
+}
+
+static void
+mclose(Chan*)
+{
+	// that's easy
+}
+
+static long
+catio(Fsdev *mp, int isread, void *a, long n, vlong off)
+{
+	int	i;
+	Chan*	mc;
+	long	l, wl, res;
+	//print("catio %d %p %ld %lld\n", isread, a, n, off);
+	res = n;
+	for (i = 0; n >= 0 && i < mp->ndevs ; i++){
+		mc = mp->idev[i];
+		if (off > mp->isize[i]){
+			off -= mp->isize[i];
+			continue;
+		}
+		if (off + n > mp->isize[i])
+			l = mp->isize[i] - off;
+		else
+			l = n;
+		//print("\tdev %d %p %ld %lld\n", i, a, l, off);
+
+		if (isread)
+			wl = devtab[mc->type]->read(mc, a, l, off);
+		else
+			wl = devtab[mc->type]->write(mc, a, l, off);
+		if (wl != l)
+			error("#k: write failed");
+		a = (char*)a + l;
+		off = 0;
+		n -= l;
+	}
+	//print("\tres %ld\n", res - n);
+	return res - n;
+}
+
+static long
+interio(Fsdev *mp, int isread, void *a, long n, vlong off)
+{
+	int	i;
+	Chan*	mc;
+	long	l, wl, wsz;
+	vlong	woff, blk, mblk;
+	long	boff, res;
+
+	blk  = off / Blksize;
+	boff = off % Blksize;
+	wsz  = Blksize - boff;
+	res = n;
+	while(n > 0){
+		i    = blk % mp->ndevs;
+		mc   = mp->idev[i];
+		mblk = blk / mp->ndevs;
+		woff = mblk * Blksize + boff;
+		if (n > wsz)
+			l = wsz;
+		else
+			l = n;
+		if (isread)
+			wl = devtab[mc->type]->read(mc, a, l, woff);
+		else
+			wl = devtab[mc->type]->write(mc, a, l, woff);
+		if (wl != l || l == 0)
+			error(Eio);
+		a = (char*)a + l;
+		n -= l;
+		blk++;
+		boff = 0;
+		wsz = Blksize;
+	}
+	return res;
+}
+
+static long
+mread(Chan *c, void *a, long n, vlong off)
+{
+	int	i;
+	Fsdev	*mp;
+	Chan	*mc;
+	long	l;
+	long	res;
+
+	if (c->qid.type & QTDIR)
+		return devdirread(c, a, n, 0, 0, mgen);
+	if (c->qid.path == Qctl)
+		return readstr((long)off, a, n, confstr + strlen(Cfgstr));
+	i = c->qid.path - Qfirst;
+	mp = path2dev(i, 1);
+
+	if (off >= mp->size)
+		return 0;
+	if (off + n > mp->size)
+		n = mp->size - off;
+	if (n == 0)
+		return 0;
+
+	res = -1;
+	switch(mp->type){
+	case Fmirror:
+		for (i = 0; i < mp->ndevs; i++){
+			mc = mp->idev[i];
+			if (waserror()){
+				// if a read fails we let the user know and try
+				// another device.
+				print("#k: mread: (%llx %d): %s\n",
+					c->qid.path, i, up->env->errstr);
+				continue;
+			}
+			l = devtab[mc->type]->read(mc, a, n, off);
+			poperror();
+			if (l >=0){
+				res = l;
+				break;
+			}
+		}
+		if (i == mp->ndevs)
+			error(Eio);
+		break;
+	case Fcat:
+		res = catio(mp, 1, a, n, off);
+		break;
+	case Finter:
+		res = interio(mp, 1, a, n, off);
+		break;
+	case Fpart:
+		off += mp->start;
+		mc = mp->idev[0];
+		res = devtab[mc->type]->read(mc, a, n, off);
+		break;
+	}
+	return res;
+}
+
+static long
+mwrite(Chan *c, void *a, long n, vlong off)
+{
+	Fsdev	*mp;
+	long	l, res;
+	int	i;
+	Chan	*mc;
+
+	if (c->qid.type & QTDIR)
+		error(Eperm);
+	if (c->qid.path == Qctl){
+		mconfig(a, n);
+		return n;
+	}
+	mp = path2dev(c->qid.path - Qfirst, 1);
+
+	if (off >= mp->size)
+		return 0;
+	if (off + n > mp->size)
+		n = mp->size - off;
+	if (n == 0)
+		return 0;
+	res = n;
+	switch(mp->type){
+	case Fmirror:
+		for (i = mp->ndevs-1; i >=0; i--){
+			mc = mp->idev[i];
+			l = devtab[mc->type]->write(mc, a, n, off);
+			if (l < res)
+				res = l;
+		}
+		break;
+	case Fcat:
+		res = catio(mp, 0, a, n, off);
+		break;
+	case Finter:
+		res = interio(mp, 0, a, n, off);
+		break;
+	case Fpart:
+		mc = mp->idev[0];
+		off += mp->start;
+		l = devtab[mc->type]->write(mc, a, n, off);
+		if (l < res)
+			res = l;
+		break;
+	}
+	return res;
+}
+
+Dev dsdevtab = {
+	'k',
+	"ds",
+
+	devreset,
+	devinit,
+	devshutdown,
+	mattach,
+	mwalk,
+	mstat,
+	mopen,
+	devcreate,
+	mclose,
+	mread,
+	devbread,
+	mwrite,
+	devbwrite,
+	devremove,
+	devwstat,
+};
--- /dev/null
+++ b/os/port/devdup.c
@@ -1,0 +1,151 @@
+#include	"u.h"
+#include	"../port/lib.h"
+#include	"mem.h"
+#include	"dat.h"
+#include	"fns.h"
+#include	"../port/error.h"
+
+/* Qid is (2*fd + (file is ctl))+1 */
+
+static int
+dupgen(Chan *c, char *, Dirtab*, int, int s, Dir *dp)
+{
+	Fgrp *fgrp = up->env->fgrp;
+	Chan *f;
+	static int perm[] = { 0400, 0200, 0600, 0 };
+	int p;
+	Qid q;
+
+	if(s == DEVDOTDOT){
+		devdir(c, c->qid, ".", 0, eve, DMDIR|0555, dp);
+		return 1;
+	}
+	if(s == 0)
+		return 0;
+	s--;
+	if(s/2 > fgrp->maxfd)
+		return -1;
+	if((f=fgrp->fd[s/2]) == nil)
+		return 0;
+	if(s & 1){
+		p = 0400;
+		sprint(up->genbuf, "%dctl", s/2);
+	}else{
+		p = perm[f->mode&3];
+		sprint(up->genbuf, "%d", s/2);
+	}
+	mkqid(&q, s+1, 0, QTFILE);
+	devdir(c, q, up->genbuf, 0, eve, p, dp);
+	return 1;
+}
+
+static Chan*
+dupattach(char *spec)
+{
+	return devattach('d', spec);
+}
+
+static Walkqid*
+dupwalk(Chan *c, Chan *nc, char **name, int nname)
+{
+	return devwalk(c, nc, name, nname, nil, 0, dupgen);
+}
+
+static int
+dupstat(Chan *c, uchar *db, int n)
+{
+	return devstat(c, db, n, nil, 0, dupgen);
+}
+
+static Chan*
+dupopen(Chan *c, int omode)
+{
+	Chan *f;
+	int fd, twicefd;
+
+	if(c->qid.type & QTDIR){
+		if(omode != 0)
+			error(Eisdir);
+		c->mode = 0;
+		c->flag |= COPEN;
+		c->offset = 0;
+		return c;
+	}
+	if(c->qid.type & QTAUTH)
+		error(Eperm);
+	twicefd = c->qid.path - 1;
+	fd = twicefd/2;
+	if((twicefd & 1)){
+		/* ctl file */
+		f = c;
+		f->mode = openmode(omode);
+		f->flag |= COPEN;
+		f->offset = 0;
+	}else{
+		/* fd file */
+		f = fdtochan(up->env->fgrp, fd, openmode(omode), 0, 1);
+		cclose(c);
+	}
+	if(omode & OCEXEC)
+		f->flag |= CCEXEC;
+	return f;
+}
+
+static void
+dupclose(Chan*)
+{
+}
+
+static long
+dupread(Chan *c, void *va, long n, vlong offset)
+{
+	char *a = va;
+	char buf[256];
+	int fd, twicefd;
+
+	if(c->qid.type == QTDIR)
+		return devdirread(c, a, n, nil, 0, dupgen);
+	twicefd = c->qid.path - 1;
+	fd = twicefd/2;
+	if(twicefd & 1){
+		c = fdtochan(up->env->fgrp, fd, -1, 0, 1);
+		if(waserror()){
+			cclose(c);
+			nexterror();
+		}
+		progfdprint(c, fd, 0, buf, sizeof buf);
+		poperror();
+		cclose(c);
+		return readstr((ulong)offset, va, n, buf);
+	}
+	panic("dupread");
+	return 0;
+}
+
+static long
+dupwrite(Chan*, void*, long, vlong)
+{
+	panic("dupwrite");
+	return 0;		/* not reached */
+}
+
+Dev dupdevtab = {
+	'd',
+	"dup",
+
+	devreset,
+	devinit,
+	devshutdown,
+	dupattach,
+	dupwalk,
+	dupstat,
+	dupopen,
+	devcreate,
+	dupclose,
+	dupread,
+	devbread,
+	dupwrite,
+	devbwrite,
+	devremove,
+	devwstat,
+};
--- /dev/null
+++ b/os/port/devdynld.c
@@ -1,0 +1,365 @@
+#include	"u.h"
+#include	"../port/lib.h"
+#include	"mem.h"
+#include	"dat.h"
+#include	"fns.h"
+#include	"../port/error.h"
+#include	<a.out.h>
+#include	<dynld.h>
+#include	<kernel.h>
+
+/*
+ * TO DO
+ *	- dynamic allocation of Dev.dc
+ *	- inter-module dependencies
+ *	- reference count on Dev to allow error("inuse") [or how else to do it]
+ *	- is Dev.shutdown the right function to call?  Dev.config perhaps?
+ */
+
+#define	DBG	if(1) print
+#define	NATIVE
+
+
+extern ulong ndevs;
+
+enum
+{
+	Qdir,
+	Qdynld,
+	Qdynsyms,
+
+	DEVCHAR	= 'L',
+};
+
+static Dirtab	dltab[] =
+{
+	".",			{Qdir, 0, QTDIR},	0,	DMDIR|0555,
+	"dynld",		{Qdynld},	0,	0644,
+	"dynsyms",	{Qdynsyms},	0,	0444,
+};
+
+typedef struct Dyndev Dyndev;
+
+struct Dyndev
+{
+	char*	name;	/* device name (eg, "dynld") */
+	char*	tag;	/* version tag (eg, MD5 or SHA1 hash of content) */
+	char*	path;	/* file from whence it came */
+	Dynobj*	o;
+	Dev*	dev;
+	Dyndev*	next;
+};
+
+static	Dyndev	*loaded;
+static	QLock	dllock;
+
+static	Dyndev**	finddyndev(char*);
+static	int	matched(Dyndev*, char*, char*);
+
+extern	Dynobj*	kdynloadfd(int, Dynsym*, int, ulong);
+
+static void
+dlfree(Dyndev *l)
+{
+	if(l != nil){
+		free(l->tag);
+		free(l->name);
+		free(l->path);
+		dynobjfree(l->o);
+		free(l);
+	}
+}
+
+static Dyndev*
+dlload(char *path, Dynsym *tab, int ntab)
+{
+	Dyndev *l;
+	int fd;
+
+	/* in Plan 9, would probably use Chan* interface here */
+	fd = kopen(path, OREAD);
+	if(fd < 0)
+		error("cannot open");
+	if(waserror()){
+		kclose(fd);
+		nexterror();
+	}
+	l = mallocz(sizeof(Dyndev), 1);
+	if(l == nil)
+		error(Enomem);
+	if(waserror()){
+		dlfree(l);
+		nexterror();
+	}
+	l->path = strdup(path);
+	if(l->path == nil)
+		error(Enomem);
+	l->o = kdynloadfd(fd, tab, ntab, 0);
+	if(l->o == nil)
+		error(up->env->errstr);
+	poperror();
+	poperror();
+	kclose(fd);
+	return l;
+}
+
+static void
+devload(char *name, char *path, char *tag)
+{
+	int i;
+	Dyndev *l, **lp;
+	Dev *dev;
+	char tabname[32];
+
+	lp = finddyndev(name);
+	if(*lp != nil)
+		error("already loaded");	/* i'm assuming the name (eg, "cons") is to be unique */
+	l = dlload(path, _exporttab, dyntabsize(_exporttab));
+	if(waserror()){
+		dlfree(l);
+		nexterror();
+	}
+	snprint(tabname, sizeof(tabname), "%sdevtab", name);
+	dev = dynimport(l->o, tabname, signof(*dev));
+	if(dev == nil)
+		errorf("can't find %sdevtab in module", name);
+	kstrdup(&l->name, name);
+	kstrdup(&l->tag, tag != nil? tag: "");
+	if(dev->name == nil)
+		dev->name = l->name;
+	else if(strcmp(dev->name, l->name) != 0)
+		errorf("module file has device %s", dev->name);
+	/* TO DO: allocate dev->dc dynamically (cf. brucee's driver) */
+	if(devno(dev->dc, 1) >= 0)
+		errorf("devchar %C already used", dev->dc);
+	for(i = 0; devtab[i] != nil; i++)
+		;
+	if(i >= ndevs || devtab[i+1] != nil)
+		error("device table full");
+#ifdef NATIVE
+	i = splhi();
+	dev->reset();
+	splx(i);
+#endif
+	dev->init();
+	l->dev = devtab[i] = dev;
+	l->next = loaded;
+	loaded = l;			/* recently loaded ones first: good unload order? */
+	poperror();
+}
+
+static Dyndev**
+finddyndev(char *name)
+{
+	Dyndev *l, **lp;
+
+	for(lp = &loaded; (l = *lp) != nil; lp = &l->next)
+		if(strcmp(l->name, name) == 0)
+			break;
+	return lp;
+}
+
+static int
+matched(Dyndev *l, char *path, char *tag)
+{
+	if(path != nil && strcmp(l->path, path) != 0)
+		return 0;
+	if(tag != nil && strcmp(l->tag, tag) != 0)
+		return 0;
+	return 1;
+}
+
+static void
+devunload(char *name, char *path, char *tag)
+{
+	int i;
+	Dyndev *l, **lp;
+
+	lp = finddyndev(name);
+	l = *lp;
+	if(l == nil)
+		error("not loaded");
+	if(!matched(l, path, tag))
+		error("path/tag mismatch");
+	for(i = 0; i < ndevs; i++)
+		if(l->dev == devtab[i]){
+			devtab[i] = nil;		/* TO DO: ensure driver is not currently in use */
+			break;
+		}
+#ifdef NATIVE
+	l->dev->shutdown();
+#endif
+	*lp = l->next;
+	dlfree(l);
+}
+
+static long
+readdynld(void *a, ulong n, ulong offset)
+{
+	char *p;
+	Dyndev *l;
+	int m, len;
+
+	m = 0;
+	for(l = loaded; l != nil; l = l->next)
+		m += 48 + strlen(l->name) + strlen(l->path) + strlen(l->tag);
+	p = malloc(m);
+	if(p == nil)
+		error(Enomem);
+	if(waserror()){
+		free(p);
+		nexterror();
+	}
+	*p = 0;
+	len = 0;
+	for(l = loaded; l != nil; l = l->next)
+		if(l->dev)
+			len += snprint(p+len, m-len, "#%C\t%.8p\t%.8lud\t%q\t%q\t%q\n",
+					l->dev->dc, l->o->base, l->o->size, l->name, l->path, l->tag);
+	n = readstr(offset, a, n, p);
+	poperror();
+	free(p);
+	return n;
+}
+
+static long
+readsyms(char *a, ulong n, ulong offset)
+{
+	char *p;
+	Dynsym *t;
+	long l, nr;
+
+	p = malloc(READSTR);
+	if(p == nil)
+		error(Enomem);
+	if(waserror()){
+		free(p);
+		nexterror();
+	}
+	nr = 0;
+	for(t = _exporttab; n > 0 && t->name != nil; t++){
+		l = snprint(p, READSTR, "%.8lux %.8lux %s\n", t->addr, t->sig, t->name);
+		if(offset >= l){
+			offset -= l;
+			continue;
+		}
+		l = readstr(offset, a, n, p);
+		offset = 0;
+		n -= l;
+		a += l;
+		nr += l;
+	}
+	poperror();
+	free(p);
+	return nr;
+}
+
+static Chan*
+dlattach(char *spec)
+{
+	return devattach(DEVCHAR, spec);
+}
+
+static Walkqid*
+dlwalk(Chan *c, Chan *nc, char **name, int nname)
+{
+	return devwalk(c, nc, name, nname, dltab, nelem(dltab), devgen);
+}
+
+static int
+dlstat(Chan *c, uchar *db, int n)
+{
+	return devstat(c, db, n, dltab, nelem(dltab), devgen);
+}
+
+static Chan*
+dlopen(Chan *c, int omode)
+{
+	return devopen(c, omode, dltab, nelem(dltab), devgen);
+}
+
+static void
+dlclose(Chan *c)
+{
+	USED(c);
+}
+
+static long
+dlread(Chan *c, void *a, long n, vlong voffset)
+{
+	switch((ulong)c->qid.path){
+	case Qdir:
+		return devdirread(c, a, n, dltab, nelem(dltab), devgen);
+	case Qdynld:
+		return readdynld(a, n, voffset);
+	case Qdynsyms:
+		return readsyms(a, n, voffset);
+	default:
+		error(Egreg);
+	}
+	return n;
+}
+
+static long
+dlwrite(Chan *c, void *a, long n, vlong voffset)
+{
+	Cmdbuf *cb;
+	char *name, *tag, *path;
+
+	USED(voffset);
+	switch((ulong)c->qid.path){
+	case Qdynld:
+		cb = parsecmd(a, n);
+		qlock(&dllock);
+		if(waserror()){
+			qunlock(&dllock);
+			free(cb);
+			nexterror();
+		}
+		if(cb->nf < 3 || strcmp(cb->f[1], "dev") != 0)	/* only do devices */
+			cmderror(cb, Ebadctl);
+		name = cb->f[2];
+		path = nil;
+		if(cb->nf > 3)
+			path = cb->f[3];
+		tag = nil;
+		if(cb->nf > 4)
+			tag = cb->f[4];
+		if(strcmp(cb->f[0], "load") == 0){
+			if(path == nil)
+				cmderror(cb, "missing load path");
+			devload(name, path, tag);	/* TO DO: remaining parameters might be dependencies? */
+		}else if(strcmp(cb->f[0], "unload") == 0)
+			devunload(name, path, tag);
+		else
+			cmderror(cb, Ebadctl);
+		poperror();
+		qunlock(&dllock);
+		free(cb);
+		break;
+	default:
+		error(Egreg);
+	}
+	return n;
+}
+
+Dev dynlddevtab = {
+	DEVCHAR,
+	"dynld",
+
+	devreset,
+	devinit,
+	devshutdown,	/* TO DO */
+	dlattach,
+	dlwalk,
+	dlstat,
+	dlopen,
+	devcreate,
+	dlclose,
+	dlread,
+	devbread,
+	dlwrite,
+	devbwrite,
+	devremove,
+	devwstat,
+};
--- /dev/null
+++ b/os/port/devenv.c
@@ -1,0 +1,338 @@
+/*
+ *	devenv - environment
+ */
+#include "u.h"
+#include "../port/lib.h"
+#include "../port/error.h"
+#include "mem.h"
+#include	"dat.h"
+#include	"fns.h"
+
+static void envremove(Chan*);
+
+enum
+{
+	Maxenvsize = 16300,
+};
+
+static int
+envgen(Chan *c, char*, Dirtab*, int, int s, Dir *dp)
+{
+	Egrp *eg;
+	Evalue *e;
+
+	if(s == DEVDOTDOT){
+		devdir(c, c->qid, "#e", 0, eve, DMDIR|0775, dp);
+		return 1;
+	}
+	eg = up->env->egrp;
+	qlock(eg);
+	for(e = eg->entries; e != nil && s != 0; e = e->next)
+		s--;
+	if(e == nil) {
+		qunlock(eg);
+		return -1;
+	}
+	/* make sure name string continues to exist after we release lock */
+	kstrcpy(up->genbuf, e->var, sizeof up->genbuf);
+	devdir(c, e->qid, up->genbuf, e->len, eve, 0666, dp);
+	qunlock(eg);
+	return 1;
+}
+
+static Chan*
+envattach(char *spec)
+{
+	if(up->env == nil || up->env->egrp == nil)
+		error(Enodev);
+	return devattach('e', spec);
+}
+
+static Walkqid*
+envwalk(Chan *c, Chan *nc, char **name, int nname)
+{
+	return devwalk(c, nc, name, nname, 0, 0, envgen);
+}
+
+static int
+envstat(Chan *c, uchar *db, int n)
+{
+	if(c->qid.type & QTDIR)
+		c->qid.vers = up->env->egrp->vers;
+	return devstat(c, db, n, 0, 0, envgen);
+}
+
+static Chan *
+envopen(Chan *c, int mode)
+{
+	Egrp *eg;
+	Evalue *e;
+	
+	if(c->qid.type & QTDIR) {
+		if(mode != OREAD)
+			error(Eperm);
+		c->mode = mode;
+		c->flag |= COPEN;
+		c->offset = 0;
+		return c;
+	}
+	eg = up->env->egrp;
+	qlock(eg);
+	for(e = eg->entries; e != nil; e = e->next)
+		if(e->qid.path == c->qid.path)
+			break;
+	if(e == nil) {
+		qunlock(eg);
+		error(Enonexist);
+	}
+	if((mode & OTRUNC) && e->val) {
+		free(e->val);
+		e->val = 0;
+		e->len = 0;
+		e->qid.vers++;
+	}
+	qunlock(eg);
+	c->mode = openmode(mode);
+	c->flag |= COPEN;
+	c->offset = 0;
+	return c;
+}
+
+static void
+envcreate(Chan *c, char *name, int mode, ulong)
+{
+	Egrp *eg;
+	Evalue *e, **le;
+
+	if(c->qid.type != QTDIR)
+		error(Eperm);
+	if(strlen(name) >= sizeof(up->genbuf))
+		error("name too long");	/* needs to fit for stat */
+	mode = openmode(mode);
+	eg = up->env->egrp;
+	qlock(eg);
+	if(waserror()){
+		qunlock(eg);
+		nexterror();
+	}
+	for(le = &eg->entries; (e = *le) != nil; le = &e->next)
+		if(strcmp(e->var, name) == 0)
+			error(Eexist);
+	e = smalloc(sizeof(Evalue));
+	e->var = smalloc(strlen(name)+1);
+	strcpy(e->var, name);
+	e->val = 0;
+	e->len = 0;
+	e->qid.path = ++eg->path;
+	e->next = nil;
+	e->qid.vers = 0;
+	*le = e;
+	c->qid = e->qid;
+	eg->vers++;
+	poperror();
+	qunlock(eg);
+	c->offset = 0;
+	c->flag |= COPEN;
+	c->mode = mode;
+}
+
+static void
+envclose(Chan *c)
+{
+	if(c->flag & CRCLOSE)
+		envremove(c);
+}
+
+static long
+envread(Chan *c, void *a, long n, vlong offset)
+{
+	Egrp *eg;
+	Evalue *e;
+
+	if(c->qid.type & QTDIR)
+		return devdirread(c, a, n, 0, 0, envgen);
+	eg = up->env->egrp;
+	qlock(eg);
+	if(waserror()){
+		qunlock(eg);
+		nexterror();
+	}
+	for(e = eg->entries; e != nil; e = e->next)
+		if(e->qid.path == c->qid.path)
+			break;
+	if(e == nil)
+		error(Enonexist);
+	if(offset > e->len)	/* protects against overflow converting vlong to ulong */
+		n = 0;
+	else if(offset + n > e->len)
+		n = e->len - offset;
+	if(n <= 0)
+		n = 0;
+	else
+		memmove(a, e->val+offset, n);
+	poperror();
+	qunlock(eg);
+	return n;
+}
+
+static long
+envwrite(Chan *c, void *a, long n, vlong offset)
+{
+	char *s;
+	ulong ve;
+	Egrp *eg;
+	Evalue *e;
+
+	if(n <= 0)
+		return 0;
+	ve = offset+n;
+	if(ve > Maxenvsize)
+		error(Etoobig);
+	eg = up->env->egrp;
+	qlock(eg);
+	if(waserror()){
+		qunlock(eg);
+		nexterror();
+	}
+	for(e = eg->entries; e != nil; e = e->next)
+		if(e->qid.path == c->qid.path)
+			break;
+	if(e == nil)
+		error(Enonexist);
+	if(ve > e->len) {
+		s = smalloc(ve);
+		memmove(s, e->val, e->len);
+		if(e->val != nil)
+			free(e->val);
+		e->val = s;
+		e->len = ve;
+	}
+	memmove(e->val+offset, a, n);
+	e->qid.vers++;
+	poperror();
+	qunlock(eg);
+	return n;
+}
+
+static void
+envremove(Chan *c)
+{
+	Egrp *eg;
+	Evalue *e, **l;
+
+	if(c->qid.type & QTDIR)
+		error(Eperm);
+	eg = up->env->egrp;
+	qlock(eg);
+	for(l = &eg->entries; (e = *l) != nil; l = &e->next)
+		if(e->qid.path == c->qid.path)
+			break;
+	if(e == nil) {
+		qunlock(eg);
+		error(Enonexist);
+	}
+	*l = e->next;
+	eg->vers++;
+	qunlock(eg);
+	free(e->var);
+	if(e->val != nil)
+		free(e->val);
+	free(e);
+}
+
+Dev envdevtab = {
+	'e',
+	"env",
+
+	devreset,
+	devinit,
+	devshutdown,
+	envattach,
+	envwalk,
+	envstat,
+	envopen,
+	envcreate,
+	envclose,
+	envread,
+	devbread,
+	envwrite,
+	devbwrite,
+	envremove,
+	devwstat
+};
+
+/*
+ * kernel interface to environment variables
+ */
+Egrp*
+newegrp(void)
+{
+	Egrp	*e;
+
+	e = smalloc(sizeof(Egrp));
+	e->ref = 1;
+	return e;
+}
+
+void
+closeegrp(Egrp *e)
+{
+	Evalue *el, *nl;
+
+	if(e == nil || decref(e) != 0)
+		return;
+	for (el = e->entries; el != nil; el = nl) {
+		free(el->var);
+		if (el->val)
+			free(el->val);
+		nl = el->next;
+		free(el);
+	}
+	free(e);
+}
+
+void
+egrpcpy(Egrp *to, Egrp *from)
+{
+	Evalue *e, *ne, **last;
+
+	if(from == nil)
+		return;
+	last = &to->entries;
+	qlock(from);
+	for (e = from->entries; e != nil; e = e->next) {
+		ne = smalloc(sizeof(Evalue));
+		ne->var = smalloc(strlen(e->var)+1);
+		strcpy(ne->var, e->var);
+		if (e->val) {
+			ne->val = smalloc(e->len);
+			memmove(ne->val, e->val, e->len);
+			ne->len = e->len;
+		}
+		ne->qid.path = ++to->path;
+		*last = ne;
+		last = &ne->next;
+	}
+	qunlock(from);
+}
+
+void
+ksetenv(char *var, char *val, int)
+{
+	Chan *c;
+	char buf[2*KNAMELEN];
+
+	snprint(buf, sizeof(buf), "#e/%s", var);
+	if(waserror())
+		return;
+	c = namec(buf, Acreate, OWRITE, 0600);
+	poperror();
+	if(!waserror()){
+		if(!waserror()){
+			devtab[c->type]->write(c, val, strlen(val), 0);
+			poperror();
+		}
+		poperror();
+	}
+	cclose(c);
+}
--- /dev/null
+++ b/os/port/devflash.c
@@ -1,0 +1,641 @@
+#include	"u.h"
+#include	"../port/lib.h"
+#include	"mem.h"
+#include	"dat.h"
+#include	"fns.h"
+#include	"../port/error.h"
+
+/*
+ * flash memory
+ */
+
+#include "../port/flashif.h"
+
+typedef struct Flashtype Flashtype;
+struct Flashtype {
+	char*	name;
+	int	(*reset)(Flash*);
+	Flashtype*	next;
+};
+
+enum {
+	Nbanks = 2,
+};
+
+static struct
+{
+	Flash*	card[Nbanks];	/* actual card type, reset for access */
+	Flashtype*	types;	/* possible card types */
+}flash;
+
+enum{
+	Qtopdir,
+	Qflashdir,
+	Qdata,
+	Qctl,
+};
+
+#define	TYPE(q)	((ulong)(q) & 0xFF)
+#define	PART(q)	((ulong)(q)>>8)
+#define	QID(p,t)	(((p)<<8) | (t))
+
+static	Flashregion*	flashregion(Flash*, ulong);
+static	char*	flashnewpart(Flash*, char*, ulong, ulong);
+static	ulong	flashaddr(Flash*, Flashpart*, char*);
+static	void	protect(Flash*, ulong);
+static	void	eraseflash(Flash*, Flashregion*, ulong);
+static	long	readflash(Flash*, void*, long, int);
+static	long	writeflash(Flash*, long, void*,int);
+
+static char Eprotect[] = "flash region protected";
+
+static int
+flash2gen(Chan *c, ulong p, Dir *dp)
+{
+	Flashpart *fp;
+	Flash *f;
+	Qid q;
+	int mode;
+
+	f = flash.card[c->dev];
+	fp = &f->part[PART(p)];
+	if(fp->name == nil)
+		return 0;
+	mkqid(&q, p, 0, QTFILE);
+	switch(TYPE(p)){
+	case Qdata:
+		mode = 0660;
+		if(f->write == nil)
+			mode = 0440;
+		devdir(c, q, fp->name, fp->end-fp->start, eve, mode, dp);
+		return 1;
+	case Qctl:
+		snprint(up->genbuf, sizeof(up->genbuf), "%sctl", fp->name);
+		devdir(c, q, up->genbuf, 0, eve, 0660, dp);
+		return 1;
+	default:
+		return -1;
+	}
+}
+
+static int
+flashgen(Chan *c, char*, Dirtab*, int, int s, Dir *dp)
+{
+	Qid q;
+	char *n;
+
+	if(s == DEVDOTDOT){
+		mkqid(&q, QID(0, Qtopdir), 0, QTDIR);
+		n = "#F";
+		if(c->dev != 0){
+			sprint(up->genbuf, "#F%ld", c->dev);
+			n = up->genbuf;
+		}
+		devdir(c, q, n, 0, eve, 0555, dp);
+		return 1;
+	}
+	switch(TYPE(c->qid.path)){
+	case Qtopdir:
+		if(s != 0)
+			break;
+		mkqid(&q, QID(0, Qflashdir), 0, QTDIR);
+		n = "flash";
+		if(c->dev != 0){
+			sprint(up->genbuf, "flash%ld", c->dev);
+			n = up->genbuf;
+		}
+		devdir(c, q, n, 0, eve, 0555, dp);
+		return 1;
+	case Qflashdir:
+		if(s >= 2*nelem(flash.card[c->dev]->part))
+			return -1;
+		return flash2gen(c, QID(s>>1, s&1?Qctl:Qdata), dp);
+	case Qctl:
+	case Qdata:
+		return flash2gen(c, (ulong)c->qid.path, dp);
+	}
+	return -1;
+}
+		
+static void
+flashreset(void)
+{
+	Flash *f;
+	Flashtype *t;
+	char *e;
+	int bank;
+
+	for(bank = 0; bank < Nbanks; bank++){
+		f = malloc(sizeof(*f));
+		if(f == nil){
+			print("#F%d: can't allocate Flash data\n", bank);
+			return;
+		}
+		f->cmask = ~(ulong)0;
+		if(archflashreset(bank, f) < 0 || f->type == nil || f->addr == nil){
+			free(f);
+			return;
+		}
+		for(t = flash.types; t != nil; t = t->next)
+			if(strcmp(f->type, t->name) == 0)
+				break;
+		if(t == nil){
+			iprint("#F%d: no flash driver for type %s (addr %p)\n", bank, f->type, f->addr);
+			free(f);
+			return;
+		}
+		f->reset = t->reset;
+		f->protect = 1;
+		if(f->reset(f) == 0){
+			flash.card[bank] = f;
+			iprint("#F%d: %s addr 0x%lux len %lud width %d interleave %d\n", bank, f->type, PADDR(f->addr), f->size, f->width, f->interleave);
+			e = flashnewpart(f, "flash", 0, f->size);
+			if(e != nil)
+				panic("#F%d: couldn't init table: %s\n", bank, e);	/* shouldn't happen */
+		}else
+			iprint("#F%d: reset failed (%s)\n", bank, f->type);
+	}
+}
+
+static Chan*
+flashattach(char *spec)
+{
+	Flash *f;
+	int bank;
+	Chan *c;
+
+	bank = strtol(spec, nil, 0);
+	if(bank < 0 || bank >= Nbanks ||
+	   (f = flash.card[bank]) == nil ||
+	   f->attach != nil && f->attach(f) < 0)
+		error(Enodev);
+	c = devattach('F', spec);
+	c->dev = bank;
+	return c;
+}
+
+static Walkqid*
+flashwalk(Chan *c, Chan *nc, char **name, int nname)
+{
+	return devwalk(c, nc, name, nname, nil, 0, flashgen);
+}
+
+static int
+flashstat(Chan *c, uchar *dp, int n)
+{
+	return devstat(c, dp, n, nil, 0, flashgen);
+}
+
+static Chan*
+flashopen(Chan *c, int omode)
+{
+	omode = openmode(omode);
+	switch(TYPE(c->qid.path)){
+	case Qdata:
+	case Qctl:
+		if(flash.card[c->dev] == nil)
+			error(Enodev);
+		break;
+	}
+	return devopen(c, omode, nil, 0, flashgen);
+}
+
+static void	 
+flashclose(Chan*)
+{
+}
+
+static long	 
+flashread(Chan *c, void *buf, long n, vlong offset)
+{
+	Flash *f;
+	char *s, *o;
+	Flashpart *fp;
+	Flashregion *r;
+	int i;
+	ulong start, end;
+
+	if(c->qid.type & QTDIR)
+		return devdirread(c, buf, n, nil, 0, flashgen);
+
+	f = flash.card[c->dev];
+	fp = &f->part[PART(c->qid.path)];
+	if(fp->name == nil)
+		error(Egreg);
+	switch(TYPE(c->qid.path)){
+	case Qdata:
+		offset += fp->start;
+		if(offset >= fp->end)
+			return 0;
+		if(offset+n > fp->end)
+			n = fp->end - offset;
+		n = readflash(f, buf, offset, n);
+		if(n < 0)
+			error(Eio);
+		return n;
+	case Qctl:
+		s = malloc(READSTR);
+		if(waserror()){
+			free(s);
+			nexterror();
+		}
+		o = seprint(s, s+READSTR, "%#2.2ux %#4.4ux %d %q\n",
+			f->id, f->devid, f->width, f->sort!=nil? f->sort: "nor");
+		for(i=0; i<f->nr; i++){
+			r = &f->regions[i];
+			if(r->start < fp->end && fp->start < r->end){
+				start = r->start;
+				if(fp->start > start)
+					start = fp->start;
+				end = r->end;
+				if(fp->end < end)
+					end = fp->end;
+				o = seprint(o, s+READSTR, "%#8.8lux %#8.8lux %#8.8lux", start, end, r->erasesize);
+				if(r->pagesize)
+					o = seprint(o, s+READSTR, " %#8.8lux", r->pagesize);
+				o = seprint(o, s+READSTR, "\n");
+			}
+		}
+		n = readstr(offset, buf, n, s);
+		poperror();
+		free(s);
+		return n;
+	}
+	error(Egreg);
+	return 0;		/* not reached */
+}
+
+enum {
+	CMerase,
+	CMadd,
+	CMremove,
+	CMsync,
+	CMprotectboot,
+};
+
+static Cmdtab flashcmds[] = {
+	{CMerase, "erase", 2},
+	{CMadd, "add", 0},
+	{CMremove, "remove", 2},
+	{CMsync, "sync", 0},
+	{CMprotectboot, "protectboot", 0},
+};
+
+static long	 
+flashwrite(Chan *c, void *buf, long n, vlong offset)
+{
+	Cmdbuf *cb;
+	Cmdtab *ct;
+	ulong addr, start, end;
+	char *e;
+	Flashpart *fp;
+	Flashregion *r;
+	Flash *f;
+
+	f = flash.card[c->dev];
+	fp = &f->part[PART(c->qid.path)];
+	if(fp->name == nil)
+		error(Egreg);
+	switch(TYPE(c->qid.path)){
+	case Qdata:
+		if(f->write == nil)
+			error(Eperm);
+		offset += fp->start;
+		if(offset >= fp->end)
+			return 0;
+		if(offset+n > fp->end)
+			n = fp->end - offset;
+		n = writeflash(f, offset, buf, n);
+		if(n < 0)
+			error(Eio);
+		return n;
+	case Qctl:
+		cb = parsecmd(buf, n);
+		if(waserror()){
+			free(cb);
+			nexterror();
+		}
+		ct = lookupcmd(cb, flashcmds, nelem(flashcmds));
+		switch(ct->index){
+		case CMerase:
+			if(strcmp(cb->f[1], "all") != 0){
+				addr = flashaddr(f, fp, cb->f[1]);
+				r = flashregion(f, addr);
+				if(r == nil)
+					error("nonexistent flash region");
+				if(addr%r->erasesize != 0)
+					error("invalid erase block address");
+				eraseflash(f, r, addr);
+			}else if(fp->start == 0 && fp->end == f->size && f->eraseall != nil){
+				eraseflash(f, nil, 0);
+			}else{
+				for(addr = fp->start; addr < fp->end; addr += r->erasesize){
+					r = flashregion(f, addr);
+					if(r == nil)
+						error("nonexistent flash region");
+					if(addr%r->erasesize != 0)
+						error("invalid erase block address");
+					eraseflash(f, r, addr);
+				}
+			}
+			break;
+		case CMadd:
+			if(cb->nf < 3)
+				error(Ebadarg);
+			start = flashaddr(f, fp, cb->f[2]);
+			if(cb->nf > 3 && strcmp(cb->f[3], "end") != 0)
+				end = flashaddr(f, fp, cb->f[3]);
+			else
+				end = fp->end;
+			if(start > end || start >= fp->end || end > fp->end)
+				error(Ebadarg);
+			e = flashnewpart(f, cb->f[1], start, end);
+			if(e != nil)
+				error(e);
+			break;
+		case CMremove:
+			/* TO DO */
+			break;
+		case CMprotectboot:
+			if(cb->nf > 1 && strcmp(cb->f[1], "off") == 0)
+				f->protect = 0;
+			else
+				f->protect = 1;
+			break;
+		case CMsync:
+			/* TO DO? */
+			break;
+		default:
+			error(Ebadarg);
+		}
+		poperror();
+		free(cb);
+		return n;
+	}
+	error(Egreg);
+	return 0;		/* not reached */
+}
+
+static char*
+flashnewpart(Flash *f, char *name, ulong start, ulong end)
+{
+	Flashpart *fp, *empty;
+	int i;
+
+	empty = nil;
+	for(i = 0; i < nelem(f->part); i++){
+		fp = &f->part[i];
+		if(fp->name == nil){
+			if(empty == nil)
+				empty = fp;
+		}else if(strcmp(fp->name, name) == 0)
+			return Eexist;
+	}
+	if((fp = empty) == nil)
+		return "partition table full";
+	fp->name = strdup(name);
+	if(fp->name == nil)
+		return Enomem;
+	fp->start = start;
+	fp->end = end;
+	return nil;
+}
+
+static ulong
+flashaddr(Flash *f, Flashpart *fp, char *s)
+{
+	Flashregion *r;
+	ulong addr;
+
+	addr = strtoul(s, &s, 0);
+	if(*s)
+		error(Ebadarg);
+	if(fp->name == nil)
+		error("partition removed");
+	addr += fp->start;
+	r = flashregion(f, addr);
+	if(r != nil && addr%r->erasesize != 0)
+		error("invalid erase unit address");
+	if(addr < fp->start || addr > fp->end || addr > f->size)
+		error(Ebadarg);
+	return addr;
+}
+
+static Flashregion*
+flashregion(Flash *f, ulong a)
+{
+	int i;
+	Flashregion *r;
+
+	for(i=0; i<f->nr; i++){
+		r = &f->regions[i];
+		if(r->start <= a && a < r->end)
+			return r;
+	}
+	return nil;
+}
+
+Dev flashdevtab = {
+	'F',
+	"flash",
+
+	flashreset,
+	devinit,
+	devshutdown,
+	flashattach,
+	flashwalk,
+	flashstat,
+	flashopen,
+	devcreate,
+	flashclose,
+	flashread,
+	devbread,
+	flashwrite,
+	devbwrite,
+	devremove,
+	devwstat,
+};
+
+/*
+ * called by flash card types named in link section (eg, flashamd.c)
+ */
+void
+addflashcard(char *name, int (*reset)(Flash*))
+{
+	Flashtype *f, **l;
+
+	f = (Flashtype*)malloc(sizeof(*f));
+	f->name = name;
+	f->reset = reset;
+	f->next = nil;
+	for(l = &flash.types; *l != nil; l = &(*l)->next)
+		;
+	*l = f;
+}
+
+static long
+readflash(Flash *f, void *buf, long offset, int n)
+{
+	int r, width, wmask;
+	uchar tmp[16];
+	uchar *p;
+	ulong o;
+
+	if(offset < 0 || offset+n > f->size)
+		error(Ebadarg);
+	qlock(f);
+	if(waserror()){
+		qunlock(f);
+		nexterror();
+	}
+	if(f->read != nil){
+		width = f->width;
+		wmask = width-1;
+		p = buf;
+		if(offset&wmask) {
+			o = offset & ~wmask;
+			if(f->read(f, o, (ulong*)tmp, width) < 0)
+				error(Eio);
+			memmove(tmp, (uchar*)f->addr+o, width);
+			for(; n > 0 && offset&wmask; n--)
+				*p++ = tmp[offset++&wmask];
+		}
+		r = n&wmask;
+		n &= ~wmask;
+		if(n){
+			if(f->read(f, offset, (ulong*)p, n) < 0)
+				error(Eio);
+			offset += n;
+			p += n;
+		}
+		if(r){
+			if(f->read(f, offset, (ulong*)tmp, width))
+				error(Eio);
+			memmove(p, tmp, r);
+		}
+	}else
+		memmove(buf, (uchar*)f->addr+offset, n);	/* assumes hardware supports byte access */
+	poperror();
+	qunlock(f);
+	return n;
+}
+
+static long
+writeflash(Flash *f, long offset, void *buf, int n)
+{
+	uchar tmp[16];
+	uchar *p;
+	ulong o;
+	int r, width, wmask;
+	Flashregion *rg;
+
+	if(f->write == nil || offset < 0 || offset+n > f->size)
+		error(Ebadarg);
+	rg = flashregion(f, offset);
+	if(f->protect && rg != nil && rg->start == 0 && offset < rg->erasesize)
+		error(Eprotect);
+	width = f->width;
+	wmask = width-1;
+	qlock(f);
+	archflashwp(f, 0);
+	if(waserror()){
+		archflashwp(f, 1);
+		qunlock(f);
+		nexterror();
+	}
+	p = buf;
+	if(offset&wmask){
+		o = offset & ~wmask;
+		if(f->read != nil){
+			if(f->read(f, o, tmp, width) < 0)
+				error(Eio);
+		}else
+			memmove(tmp, (uchar*)f->addr+o, width);
+		for(; n > 0 && offset&wmask; n--)
+			tmp[offset++&wmask] = *p++;
+		if(f->write(f, o, tmp, width) < 0)
+			error(Eio);
+	}
+	r = n&wmask;
+	n &= ~wmask;
+	if(n){
+		if(f->write(f, offset, p, n) < 0)
+			error(Eio);
+		offset += n;
+		p += n;
+	}
+	if(r){
+		if(f->read != nil){
+			if(f->read(f, offset, tmp, width) < 0)
+				error(Eio);
+		}else
+			memmove(tmp, (uchar*)f->addr+offset, width);
+		memmove(tmp, p, r);
+		if(f->write(f, offset, tmp, width) < 0)
+			error(Eio);
+	}
+	poperror();
+	archflashwp(f, 1);
+	qunlock(f);
+	return n;
+}
+
+static void
+eraseflash(Flash *f, Flashregion *r, ulong addr)
+{
+	int rv;
+
+	if(f->protect && r != nil && r->start == 0 && addr < r->erasesize)
+		error(Eprotect);
+	qlock(f);
+	archflashwp(f, 0);
+	if(waserror()){
+		archflashwp(f, 1);
+		qunlock(f);
+		nexterror();
+	}
+	if(r == nil){
+		if(f->eraseall != nil)
+			rv = f->eraseall(f);
+		else
+			rv = -1;
+	}else
+		rv = f->erasezone(f, r, addr);
+	if(rv < 0)
+		error(Eio);
+	poperror();
+	archflashwp(f, 1);
+	qunlock(f);
+}
+
+/*
+ * flash access taking width and interleave into account
+ */
+int
+flashget(Flash *f, ulong a)
+{
+	switch(f->width){
+	default:
+		return ((uchar*)f->addr)[a<<f->bshift];
+	case 2:
+		return ((ushort*)f->addr)[a];
+	case 4:
+		return ((ulong*)f->addr)[a];
+	}
+}
+
+void
+flashput(Flash *f, ulong a, int v)
+{
+	switch(f->width){
+	default:
+		((uchar*)f->addr)[a<<f->bshift] = v;
+		break;
+	case 2:
+		((ushort*)f->addr)[a] = v;
+		break;
+	case 4:
+		((ulong*)f->addr)[a] = v;
+		break;
+	}
+}
--- /dev/null
+++ b/os/port/devftl.c
@@ -1,0 +1,1361 @@
+/*
+ * basic Flash Translation Layer driver
+ *	see for instance the Intel technical paper
+ *	``Understanding the Flash Translation Layer (FTL) Specification''
+ *	Order number 297816-001 (online at www.intel.com)
+ *
+ * a public driver by David Hinds, dhinds@allegro.stanford.edu
+ * further helps with some details.
+ *
+ * this driver uses the common simplification of never storing
+ * the VBM on the medium (a waste of precious flash!) but
+ * rather building it on the fly as the block maps are read.
+ *
+ * Plan 9 driver (c) 1997 by C H Forsyth (forsyth@terzarima.net)
+ *	This driver may be used or adapted by anyone for any non-commercial purpose.
+ *
+ * adapted for Inferno 1998 by C H Forsyth, Vita Nuova Limited, York, England (forsyth@vitanuova.com)
+ *
+ * TO DO:
+ *	check error handling details for get/put flash
+ *	bad block handling
+ *	reserved space in formatted size
+ *	possibly block size as parameter
+ */
+
+#include "u.h"
+#include "../port/lib.h"
+#include "../port/error.h"
+#include "mem.h"
+#include "dat.h"
+#include "fns.h"
+
+#include "kernel.h"
+
+typedef struct Ftl Ftl;
+typedef struct Merase Merase;
+typedef struct Terase Terase;
+
+enum {
+	Eshift = 18,	/* 2^18=256k; log2(eraseunit) */
+	Flashseg = 1<<Eshift,
+	Bshift = 9,		/* 2^9=512 */
+	Bsize = 1<<Bshift,
+	BAMoffset = 0x100,
+	Nolimit = ~0,
+	USABLEPCT = 95,	/* release only this % to client */
+
+	FTLDEBUG = 0,
+
+	NPART = 4,
+};
+
+/* erase unit header (defined by FTL specification) */
+struct Merase {
+	uchar	linktuple[5];
+	uchar	orgtuple[10];
+	uchar	nxfer;
+	uchar	nerase[4];
+	uchar	id[2];
+	uchar	bshift;
+	uchar	eshift;
+	uchar	pstart[2];
+	uchar	nunits[2];
+	uchar	psize[4];
+	uchar	vbmbase[4];
+	uchar	nvbm[2];
+	uchar	flags;
+	uchar	code;
+	uchar	serial[4];
+	uchar	altoffset[4];
+	uchar	bamoffset[4];
+	uchar	rsv2[12];
+};
+#define	ERASEHDRLEN	64
+
+enum {
+	/* special unit IDs */
+	XferID = 0xffff,
+	XferBusy = 0x7fff,
+
+	/* special BAM addresses */
+	Bfree = 0xffffffff,
+	Bwriting = 0xfffffffe,
+	Bdeleted = 0,
+
+	/* block types */
+	TypeShift = 7,
+	BlockType = (1<<TypeShift)-1,
+	ControlBlock = 0x30,
+	DataBlock = 0x40,
+	ReplacePage = 0x60,
+	BadBlock = 0x70,
+};
+
+#define	BTYPE(b)	((b) & BlockType)
+#define	BADDR(b)	((b) & ~BlockType)
+#define	BNO(va)	(((ulong)(va))>>Bshift)
+#define	MKBAM(b,t)	(((b)<<Bshift)|(t))
+
+struct Terase {
+	int	x;
+	int	id;
+	ulong	offset;
+	ulong	bamoffset;
+	ulong	nbam;
+	ulong*	bam;
+	ulong	bamx;
+	ulong	nfree;
+	ulong	nused;
+	ulong	ndead;
+	ulong	nbad;
+	ulong	nerase;
+};
+
+struct Ftl {
+	QLock;
+	Ref;
+
+	Chan*	flash;
+	Chan*	flashctl;
+	ulong	base;	/* base of flash region */
+	ulong	size;	/* size of flash region */
+	ulong	segsize;	/* size of flash segment (erase unit) */
+	int	eshift;	/* log2(erase-unit-size) */
+	int	bshift;	/* log2(bsize) */
+	int	bsize;
+	int	nunit;	/* number of segments (erase units) */
+	Terase**	unit;
+	int	lastx;	/* index in unit of last allocation */
+	int	xfer;		/* index in unit of current transfer unit (-1 if none) */
+	ulong	nfree;	/* total free space in blocks */
+	ulong	nblock;	/* total space in blocks */
+	ulong	rwlimit;	/* user-visible block limit (`formatted size') */
+	ulong*	vbm;		/* virtual block map */
+	ulong	fstart;	/* address of first block of data in a segment */
+	int	trace;	/* (debugging) trace of read/write actions */
+	int	detach;	/* free Ftl on last close */
+
+	/* scavenging variables */
+	QLock	wantq;
+	Rendez	wantr;
+	Rendez	workr;
+	int	needspace;
+	int	hasproc;
+	int	npart;		/* over and above ftldata */
+	struct {
+		ulong start, size;
+		ulong rwlimit;
+		char *name;	/* nil if slot unused */
+	} part[NPART];
+};
+
+enum {
+	/* Ftl.detach */
+	Detached = 1,	/* detach on close */
+	Deferred	/* scavenger must free it */
+};
+
+/* little endian */
+#define	GET2(p)	(((p)[1]<<8)|(p)[0])
+#define	GET4(p)	(((((((p)[3]<<8)|(p)[2])<<8)|(p)[1])<<8)|(p)[0])
+#define	PUT2(p,v)	(((p)[1]=(v)>>8),((p)[0]=(v)))
+#define	PUT4(p,v)	(((p)[3]=(v)>>24),((p)[2]=(v)>>16),((p)[1]=(v)>>8),((p)[0]=(v)))
+
+static	Lock	ftllock;
+static	Ftl	*ftls;
+static	int	ftlpct = USABLEPCT;
+
+static	ulong	allocblk(Ftl*);
+static	int	erasedetect(Ftl *ftl, ulong base, ulong size, ushort *pstart, ushort *nunits);
+static	void	eraseflash(Ftl*, ulong);
+static	void	erasefree(Terase*);
+static	void	eraseinit(Ftl*, ulong, int, int);
+static	Terase*	eraseload(Ftl*, int, ulong);
+static	void	ftlfree(Ftl*);
+static	void	getflash(Ftl*, void*, ulong, long);
+static	int	mapblk(Ftl*, ulong, Terase**, ulong*);
+static	Ftl*	mkftl(char*, ulong, ulong, int, char*);
+static	void	putbam(Ftl*, Terase*, int, ulong);
+static	void	putflash(Ftl*, ulong, void*, long);
+static	int	scavenge(Ftl*);
+
+enum {
+	Qdir,
+	Qctl,
+	Qdata,
+};
+
+#define DATAQID(q) ((q) >= Qdata && (q) <= Qdata + NPART)
+
+static void
+ftlpartcmd(Ftl *ftl, char **fields, int nfields)
+{
+	ulong start, end;
+	char *p;
+	int n, newn;
+
+	/* name start [end] */
+	if(nfields < 2 || nfields > 3)
+		error(Ebadarg);
+	if(ftl->npart >= NPART)
+		error("table full");
+	if(strcmp(fields[0], "ctl") == 0 || strcmp(fields[0], "data") == 0)
+		error(Ebadarg);
+	newn = -1;
+	for(n = 0; n < NPART; n++){
+		if(ftl->part[n].name == nil){
+			if(newn < 0)
+				newn = n;
+			continue;
+		}
+		if(strcmp(fields[0], ftl->part[n].name + 3) == 0)
+			error(Ebadarg);
+	}
+	start = strtoul(fields[1], 0, 0);
+	if(nfields > 2)
+		end = strtoul(fields[2], 0, 0);
+	else
+		end = ftl->rwlimit * Bsize;
+	if(start >= end || start % Bsize || end % Bsize)
+		error(Ebadarg);
+	ftl->part[newn].start = start;
+	ftl->part[newn].size = end - start;
+	ftl->part[newn].rwlimit = end / Bsize;
+	free(ftl->part[newn].name);
+	p = malloc(strlen(fields[0]) + 3 + 1);
+	strcpy(p, "ftl");
+	strcat(p, fields[0]);
+	ftl->part[newn].name = p;
+	ftl->npart++;
+}
+		
+static void
+ftldelpartcmd(Ftl *ftl, char **fields, int nfields)
+{
+	int n;
+	// name
+	if(nfields != 1)
+		error(Ebadarg);
+	for(n = 0; n < NPART; n++)
+		if(strcmp(fields[0], ftl->part[n].name + 3) == 0){
+			free(ftl->part[n].name);
+			ftl->part[n].name = nil;
+			ftl->npart--;
+			return;
+		}
+	error(Ebadarg);
+}
+
+static int
+ftlgen(Chan *c, char*, Dirtab*, int, int i, Dir *dp)
+{
+	int n;
+	switch(i){
+	case DEVDOTDOT:
+		devdir(c, (Qid){Qdir, 0, QTDIR}, "#X", 0, eve, 0555, dp);
+		break;
+	case 0:
+		devdir(c, (Qid){Qctl, 0, QTFILE}, "ftlctl", 0, eve, 0660, dp);
+		break;
+	case 1:
+		devdir(c, (Qid){Qdata, 0, QTFILE}, "ftldata", ftls ? ftls->rwlimit * Bsize : 0, eve, 0660, dp);
+		break;
+	default:
+		if(ftls == nil)
+			return -1;
+		i -= 2;
+		if(i >= ftls->npart)
+			return -1;
+		for(n = 0; n < NPART; n++)
+			if(ftls->part[n].name != nil){
+				if(i == 0)
+					break;
+				i--;
+			}
+		if(i != 0){
+			print("wierd\n");
+			return -1;
+		}
+		devdir(c, (Qid){Qdata + 1 + n, 0, QTFILE}, ftls->part[n].name, ftls->part[n].size, eve, 0660, dp);
+	}
+	return 1;
+}
+
+static Ftl *
+ftlget(void)
+{
+	Ftl *ftl;
+
+	lock(&ftllock);
+	ftl = ftls;
+	if(ftl != nil)
+		incref(ftl);
+	unlock(&ftllock);
+	return ftl;
+}
+
+static void
+ftlput(Ftl *ftl)
+{
+	if(ftl != nil){
+		lock(&ftllock);
+		if(decref(ftl) == 0 && ftl->detach == Detached){
+			ftls = nil;
+			if(ftl->hasproc){	/* no lock needed: can't change if ftl->ref==0 */
+				ftl->detach = Deferred;
+				wakeup(&ftl->workr);
+			}else
+				ftlfree(ftl);
+		}
+		unlock(&ftllock);
+	}
+}
+
+static Chan *
+ftlattach(char *spec)
+{
+	return devattach('X', spec);
+}
+
+static Walkqid*
+ftlwalk(Chan *c, Chan *nc, char **name, int nname)
+{
+	Walkqid *wq;
+
+	wq = devwalk(c, nc, name, nname, 0, 0, ftlgen);
+	if(wq != nil && wq->clone != nil && wq->clone != c)
+		if(DATAQID(wq->clone->qid.path))
+			wq->clone->aux = ftlget();
+	return wq;
+}
+
+static int
+ftlstat(Chan *c, uchar *dp, int n)
+{
+	return devstat(c, dp, n, 0, 0, ftlgen);
+}
+
+static Chan*
+ftlopen(Chan *c, int omode)
+{
+	Ftl *ftl;
+	omode = openmode(omode);
+	if(DATAQID(c->qid.path)){
+		ftl = ftls;
+		if(ftl == nil)
+			error(Enodev);
+		if(strcmp(up->env->user, eve)!=0)
+			error(Eperm);
+	}
+	else if(c->qid.path == Qctl){
+		if(strcmp(up->env->user, eve)!=0)
+			error(Eperm);
+	}
+	c = devopen(c, omode, 0, 0, ftlgen);
+	if(DATAQID(c->qid.path)){
+		c->aux = ftlget();
+		if(c->aux == nil)
+			error(Enodev);
+	}
+	return c;
+}
+
+static void	 
+ftlclose(Chan *c)
+{
+	if(DATAQID(c->qid.path) && (c->flag&COPEN) != 0)
+		ftlput((Ftl*)c->aux);
+}
+
+static long	 
+ftlread(Chan *c, void *buf, long n, vlong offset)
+{
+	Ftl *ftl;
+	Terase *e;
+	int nb;
+	uchar *a;
+	ulong pb;
+	if(c->qid.type & QTDIR)
+		return devdirread(c, buf, n, 0, 0, ftlgen);
+
+	if(DATAQID(c->qid.path)){
+		ulong rwlimit;
+
+		if(n <= 0 || n%Bsize || offset%Bsize)
+			error(Eio);
+		ftl = c->aux;
+		if(c->qid.path > Qdata){
+			int p = c->qid.path - Qdata - 1;
+			offset += ftl->part[p].start;
+			rwlimit = ftl->part[p].rwlimit;
+		}
+		else
+			rwlimit = ftl->rwlimit;
+		nb = n/Bsize;
+		offset /= Bsize;
+		if(offset >= rwlimit)
+			return 0;
+		if(offset+nb > rwlimit)
+			nb = rwlimit - offset;
+		a = buf;
+		for(n = 0; n < nb; n++){
+			qlock(ftl);
+			if(waserror()){
+				qunlock(ftl);
+				nexterror();
+			}
+			if(mapblk(ftl, offset+n, &e, &pb))
+				getflash(ftl, a, e->offset + pb*Bsize, Bsize);
+			else
+				memset(a, 0, Bsize);
+			poperror();
+			qunlock(ftl);
+			a += Bsize;
+		}
+		return a-(uchar*)buf;
+	}
+
+	if(c->qid.path != Qctl)
+		error(Egreg);
+
+	return 0;
+}
+
+static long	 
+ftlwrite(Chan *c, void *buf, long n, vlong offset)
+{
+	char cmd[64], *fields[6];
+	int ns, i, k, nb;
+	uchar *a;
+	Terase *e, *oe;
+	ulong ob, v, base, size, segsize;
+	Ftl *ftl;
+
+	if(n <= 0)
+		return 0;
+
+	if(DATAQID(c->qid.path)){
+		ulong rwlimit;
+		ftl = c->aux;
+		if(n <= 0 || n%Bsize || offset%Bsize)
+			error(Eio);
+		if(c->qid.path > Qdata){
+			int p = c->qid.path - Qdata - 1;
+			offset += ftl->part[p].start;
+			rwlimit = ftl->part[p].rwlimit;
+		}
+		else
+			rwlimit = ftl->rwlimit;
+		nb = n/Bsize;
+		offset /= Bsize;
+		if(offset >= rwlimit)
+			return 0;
+		if(offset+nb > rwlimit)
+			nb = rwlimit - offset;
+		a = buf;
+		for(n = 0; n < nb; n++){
+			ns = 0;
+			while((v = allocblk(ftl)) == 0)
+				if(!scavenge(ftl) || ++ns > 3){
+					static int stop;
+
+					if(stop < 3){
+						stop++;
+						print("ftl: flash memory full\n");
+					}
+					error("flash memory full");
+				}
+			qlock(ftl);
+			if(waserror()){
+				qunlock(ftl);
+				nexterror();
+			}
+			if(!mapblk(ftl, offset+n, &oe, &ob))
+				oe = nil;
+			e = ftl->unit[v>>16];
+			v &= 0xffff;
+			putflash(ftl, e->offset + v*Bsize, a, Bsize);
+			putbam(ftl, e, v, MKBAM(offset+n, DataBlock));
+			/* both old and new block references exist in this window (can't be closed?) */
+			ftl->vbm[offset+n] = (e->x<<16) | v;
+			if(oe != nil){
+				putbam(ftl, oe, ob, Bdeleted);
+				oe->ndead++;
+			}
+			poperror();
+			qunlock(ftl);
+			a += Bsize;
+		}
+		return a-(uchar*)buf;
+	}
+	else if(c->qid.path == Qctl){
+		if(n > sizeof(cmd)-1)
+			n = sizeof(cmd)-1;
+		memmove(cmd, buf, n);
+		cmd[n] = 0;
+		i = getfields(cmd, fields, 6, 1, " \t\n");
+		if(i <= 0)
+			error(Ebadarg);
+		if(i >= 2 && (strcmp(fields[0], "init") == 0 || strcmp(fields[0], "format") == 0)){
+			if(i > 2)
+				base = strtoul(fields[2], nil, 0);
+			else
+				base = 0;	/* TO DO: hunt for signature */
+			if(i > 3)
+				size = strtoul(fields[3], nil, 0);
+			else
+				size = Nolimit;
+			if(i > 4)
+				segsize = strtoul(fields[4], nil, 0);
+			else
+				segsize = 0;
+			/* segsize must be power of two and size and base must be multiples of it
+			 * if segsize is zero, then use whatever the device returns
+			 */
+			if(segsize != 0 && (segsize > size
+				|| segsize&(segsize-1)
+				|| (base != Nolimit && base&(segsize-1))
+				|| size == 0
+				|| (size != Nolimit && size&(segsize-1))))
+				error(Ebadarg);
+			if(segsize == 0)
+				k = 0;
+			else {
+				for(k=0; k<32 && (1<<k) != segsize; k++)
+					;
+			}
+			if(ftls != nil)
+				error(Einuse);
+			ftls = mkftl(fields[1], base, size, k, fields[0]);
+		}else if(strcmp(fields[0], "scavenge") == 0){
+			if(ftls != nil)
+				print("scavenge %d\n", scavenge(ftls));
+		}else if(strcmp(fields[0], "trace") == 0){
+			if(ftls != nil)
+				ftls->trace = i>1? strtol(fields[1], nil, 0): 1;
+		}else if(strcmp(fields[0], "detach") == 0){
+			if((ftl = ftlget()) != nil){
+				if(ftl->ref > 1){
+					ftlput(ftl);
+					error(Einuse);
+				}
+				ftl->detach = Detached;
+				ftlput(ftl);
+			}else
+				error(Enodev);
+		}else if(strcmp(fields[0], "part")==0){
+			if((ftl = ftlget()) != nil){
+				if(ftl->ref > 1){
+					ftlput(ftl);
+					error(Einuse);
+				}
+				if(waserror()){
+					ftlput(ftl);
+					nexterror();
+				}
+				ftlpartcmd(ftl, fields + 1, i - 1);
+				poperror();
+				ftlput(ftl);
+			}else
+				error(Enodev);
+		}else if(strcmp(fields[0], "delpart")==0){
+			if((ftl = ftlget()) != nil){
+				if(ftl->ref > 1){
+					ftlput(ftl);
+					error(Einuse);
+				}
+				if(waserror()){
+					ftlput(ftl);
+					nexterror();
+				}
+				ftldelpartcmd(ftls, fields + 1, i - 1);
+				poperror();
+				ftlput(ftl);
+			}else
+				error(Enodev);
+		}else if(i >= 2 && strcmp(fields[0], "pct")==0){
+			v = strtoul(fields[1], nil, 0);
+			if(v >= 50)
+				ftlpct = v;
+		}else
+			error(Ebadarg);
+		return n;
+	}
+	error(Egreg);
+	return 0;		/* not reached */
+}
+
+static Chan *
+ftlkopen(char *name, char *suffix, int mode)
+{
+	Chan *c;
+	char *fn;
+	int fd;
+
+	if(suffix != nil && *suffix){
+		fn = smalloc(strlen(name)+strlen(suffix)+1);
+		if(fn == nil)
+			return nil;
+		strcpy(fn, name);
+		strcat(fn, suffix);
+		fd = kopen(fn, mode);
+		free(fn);
+	}else
+		fd = kopen(name, mode);
+	if(fd < 0)
+		return nil;
+	c = fdtochan(up->env->fgrp, fd, mode, 0, 1);
+	kclose(fd);
+	return c;
+}
+
+static ulong
+ftlfsize(Chan *c)
+{
+	uchar dbuf[STATFIXLEN+32*4];
+	Dir d;
+	int n;
+
+	n = devtab[c->type]->stat(c, dbuf, sizeof(dbuf));
+	if(convM2D(dbuf, n, &d, nil) == 0)
+		return 0;
+	return d.length;
+}
+
+static Ftl *
+mkftl(char *fname, ulong base, ulong size, int eshift, char *op)
+{
+	int i, j, nov, segblocks, n, badseg, old, valid;
+	ulong limit;
+	Terase *e;
+	Ftl *ftl;
+	char buf[64], *fields[8];
+	ulong ea;
+	Chan *statfd;
+
+	ftl = malloc(sizeof(*ftl));
+	if(ftl == nil)
+		error(Enomem);
+	e = nil;
+	if(waserror()){
+		ftlfree(ftl);
+		if(e)
+			free(e);
+		nexterror();
+	}
+	ftl->lastx = 0;
+	ftl->trace = 0;
+	ftl->flash = ftlkopen(fname, "", ORDWR);
+	if(ftl->flash == nil)
+		error(up->env->errstr);
+	ftl->flashctl = ftlkopen(fname, "ctl", ORDWR);
+	if(ftl->flashctl == nil)
+		error(up->env->errstr);
+	old = 1;
+	statfd = ftlkopen(fname, "stat", OREAD);	/* old scheme */
+	if(statfd == nil){
+		statfd = ftl->flashctl;	/* new just uses ctl */
+		old = 0;
+	}
+	statfd->offset = 0;
+	if((n = kchanio(statfd, buf, sizeof(buf), OREAD)) <= 0){
+		print("ftl: read stat/ctl failed: %s\n", up->env->errstr);
+		error(up->env->errstr);
+	}
+	if(n >= sizeof(buf))
+		n = sizeof(buf)-1;
+	buf[n] = 0;
+	if(statfd != ftl->flashctl)
+		cclose(statfd);
+
+	n = getfields(buf, fields, nelem(fields), 1, " \t\n");
+	ea = 0;
+	if(old){
+		if(n >= 4)
+			if((ea = strtoul(fields[3], nil, 16)) < 8*1024)
+				ea = 0;	/* assume the format is wrong */
+	}else{
+		if(n >= 7)
+			if((ea = strtoul(fields[6], nil, 0)) < 2*1024)
+				ea = 0;	/* assume the format is wrong */
+	}
+	if(ea != 0){
+		for(i=0; i < 32 && (1<<i) != ea; i++)
+			;
+		if(eshift && i != eshift)
+			print("ftl: overriding erasesize %d with %d\n", 1 << eshift, 1 << i);
+		eshift = i;
+		if(FTLDEBUG)
+			print("ftl: e=%lud eshift=%d\n", ea, i);
+	}
+	if(eshift == 0)
+		error("no erasesize");
+
+	limit = ftlfsize(ftl->flash);
+	if(limit == 0)
+		error("no space for flash translation");
+	ftl->segsize = 1 << eshift;
+	if(base == Nolimit){
+		ushort pstart, nunits;
+		erasedetect(ftl, 0, limit, &pstart, &nunits);
+		base = pstart * ftl->segsize;
+		size = nunits * ftl->segsize;
+		print("ftl: partition in %s at 0x%.8lux, length 0x%.8lux\n", fname, base, size);
+	} else if(size == Nolimit)
+		size = limit-base;
+	if(base >= limit || size > limit || base+size > limit || eshift < 8 || (1<<eshift) > size){
+		print("ftl: bad: base=%#lux limit=%#lux size=%ld eshift=%d\n", base, limit, size, eshift);
+		error("bad flash space parameters");
+	}
+	if(FTLDEBUG)
+		print("%s flash %s #%lux:#%lux limit #%lux\n", op, fname, base, size, limit);
+	ftl->base = base;
+	ftl->size = size;
+	ftl->bshift = Bshift;
+	ftl->bsize = Bsize;
+	ftl->eshift = eshift;
+	ftl->nunit = size>>eshift;
+	nov = ((ftl->segsize/Bsize)*4 + BAMoffset + Bsize - 1)/Bsize;	/* number of overhead blocks per segment (header, and BAM itself) */
+	ftl->fstart = nov;
+	segblocks = ftl->segsize/Bsize - nov;
+	ftl->nblock = ftl->nunit*segblocks;
+	if(ftl->nblock > 0x10000){
+		/* oops - too many blocks */
+		ftl->nunit = 0x10000 / segblocks;
+		ftl->nblock = ftl->nunit * segblocks;
+		size = ftl->nunit * ftl->segsize;
+		ftl->size = size;
+		print("ftl: too many blocks - limiting to %ld bytes %d units %lud blocks\n",
+		    size, ftl->nunit, ftl->nblock);
+	}
+	ftl->vbm = malloc(ftl->nblock*sizeof(*ftl->vbm));
+	ftl->unit = malloc(ftl->nunit*sizeof(*ftl->unit));
+	if(ftl->vbm == nil || ftl->unit == nil)
+		error(Enomem);
+	if(strcmp(op, "format") == 0){
+		for(i=0; i<ftl->nunit-1; i++)
+			eraseinit(ftl, i*ftl->segsize, i, 1);
+		eraseinit(ftl, i*ftl->segsize, XferID, 1);
+	}
+	badseg = -1;
+	ftl->xfer = -1;
+	valid = 0;
+	for(i=0; i<ftl->nunit; i++){
+		e = eraseload(ftl, i, i*ftl->segsize);
+		if(e == nil){
+			print("ftl: logical segment %d: bad format\n", i);
+			if(badseg == -1)
+				badseg = i;
+			else
+				badseg = -2;
+			continue;
+		}
+		if(e->id == XferBusy){
+			e->nerase++;
+			eraseinit(ftl, e->offset, XferID, e->nerase);
+			e->id = XferID;
+		}
+		for(j=0; j<ftl->nunit; j++)
+			if(ftl->unit[j] != nil && ftl->unit[j]->id == e->id){
+				print("ftl: duplicate erase unit #%x\n", e->id);
+				erasefree(e);
+				e = nil;
+				break;
+			}
+		if(e){
+			valid++;
+			ftl->unit[e->x] = e;
+			if(e->id == XferID)
+				ftl->xfer = e->x;
+			if(FTLDEBUG)
+				print("ftl: unit %d:#%x used %lud free %lud dead %lud bad %lud nerase %lud\n",
+					e->x, e->id, e->nused, e->nfree, e->ndead, e->nbad, e->nerase);
+			e = nil;
+			USED(e);
+		}
+	}
+	if(badseg >= 0){
+		if(ftl->xfer >= 0)
+			error("invalid ftl format");
+		i = badseg;
+		eraseinit(ftl, i*ftl->segsize, XferID, 1);
+		e = eraseload(ftl, i, i*ftl->segsize);
+		if(e == nil)
+			error("bad ftl format");
+		ftl->unit[e->x] = e;
+		ftl->xfer = e->x;
+		print("ftl: recovered transfer unit %d\n", e->x);
+		valid++;
+		e = nil;
+		USED(e);
+	}
+	if(ftl->xfer < 0 && valid <= 0 || ftl->xfer >= 0 && valid <= 1)
+		error("no valid flash data units");
+	if(ftl->xfer < 0)
+		error("ftl: no transfer unit: device is WORM\n");
+	else
+		ftl->nblock -= segblocks;	/* discount transfer segment */
+	if(ftl->nblock >= 1000)
+		ftl->rwlimit = ftl->nblock-100;	/* TO DO: variable reserve */
+	else
+		ftl->rwlimit = ftl->nblock*ftlpct/100;
+	poperror();
+	return ftl;
+}
+
+static void
+ftlfree(Ftl *ftl)
+{
+	int i, n;
+
+	if(ftl != nil){
+		if(ftl->flashctl != nil)
+			cclose(ftl->flashctl);
+		if(ftl->flash != nil)
+			cclose(ftl->flash);
+
+		if(ftl->unit){
+			for(i = 0; i < ftl->nunit; i++)
+				erasefree(ftl->unit[i]);
+			free(ftl->unit);
+		}
+		free(ftl->vbm);
+		for(n = 0; n < NPART; n++)
+			free(ftl->part[n].name);
+		free(ftl);
+	}
+}
+
+/*
+ * this simple greedy algorithm weighted by nerase does seem to lead
+ * to even wear of erase units (cf. the eNVy file system)
+ */
+static Terase *
+bestcopy(Ftl *ftl)
+{
+	Terase *e, *be;
+	int i;
+
+	be = nil;
+	for(i=0; i<ftl->nunit; i++)
+		if((e = ftl->unit[i]) != nil && e->id != XferID && e->id != XferBusy && e->ndead+e->nbad &&
+		    (be == nil || e->nerase <= be->nerase && e->ndead >= be->ndead))
+			be = e;
+	return be;
+}
+
+static int
+copyunit(Ftl *ftl, Terase *from, Terase *to)
+{
+	int i, nb;
+	uchar id[2];
+	ulong *bam;
+	uchar *buf;
+	ulong v, bno;
+
+	if(FTLDEBUG || ftl->trace)
+		print("ftl: copying %d (#%lux) to #%lux\n", from->id, from->offset, to->offset);
+	to->nbam = 0;
+	free(to->bam);
+	to->bam = nil;
+	bam = nil;
+	buf = malloc(Bsize);
+	if(buf == nil)
+		return 0;
+	if(waserror()){
+		free(buf);
+		free(bam);
+		return 0;
+	}
+	PUT2(id, XferBusy);
+	putflash(ftl, to->offset+offsetof(Merase,id[0]), id, 2);
+	/* make new BAM */
+	nb = from->nbam*sizeof(*to->bam);
+	bam = malloc(nb);
+	if(bam == nil)
+		error(Enomem);
+	memmove(bam, from->bam, nb);
+	to->nused = 0;
+	to->nbad = 0;
+	to->nfree = 0;
+	to->ndead = 0;
+	for(i = 0; i < from->nbam; i++)
+		switch(bam[i]){
+		case Bwriting:
+		case Bdeleted:
+		case Bfree:
+			bam[i] = Bfree;
+			to->nfree++;
+			break;
+		default:
+			switch(bam[i]&BlockType){
+			default:
+			case BadBlock:	/* it isn't necessarily bad in this unit */
+				to->nfree++;
+				bam[i] = Bfree;
+				break;
+			case DataBlock:
+			case ReplacePage:
+				v = bam[i];
+				bno = BNO(v & ~BlockType);
+				if(i < ftl->fstart || bno >= ftl->nblock){
+					print("ftl: unit %d:#%x bad bam[%d]=#%lux\n", from->x, from->id, i, v);
+					to->nfree++;
+					bam[i] = Bfree;
+					break;
+				}
+				getflash(ftl, buf, from->offset+i*Bsize, Bsize);
+				putflash(ftl, to->offset+i*Bsize, buf, Bsize);
+				to->nused++;
+				break;
+			case ControlBlock:
+				to->nused++;
+				break;
+			}
+		}
+	for(i=0; i<from->nbam; i++){
+		uchar *p = (uchar*)&bam[i];
+		v = bam[i];
+		if(v != Bfree && ftl->trace > 1)
+			print("to[%d]=#%lux\n", i, v);
+		PUT4(p, v);
+	}
+	putflash(ftl, to->bamoffset, bam, nb);	/* BUG: PUT4 */
+	for(i=0; i<from->nbam; i++){
+		uchar *p = (uchar*)&bam[i];
+		v = bam[i];
+		PUT4(p, v);
+	}
+	to->id = from->id;
+	PUT2(id, to->id);
+	putflash(ftl, to->offset+offsetof(Merase,id[0]), id, 2);
+	to->nbam = from->nbam;
+	to->bam = bam;
+	ftl->nfree += to->nfree - from->nfree;
+	poperror();
+	free(buf);
+	return 1;
+}
+
+static int
+mustscavenge(void *a)
+{
+	return ((Ftl*)a)->needspace || ((Ftl*)a)->detach == Deferred;
+}
+
+static int
+donescavenge(void *a)
+{
+	return ((Ftl*)a)->needspace == 0;
+}
+
+static void
+scavengeproc(void *arg)
+{
+	Ftl *ftl;
+	int i;
+	Terase *e, *ne;
+
+	ftl = arg;
+	if(waserror()){
+		print("ftl: kproc noted\n");
+		pexit("ftldeath", 0);
+	}
+	for(;;){
+		sleep(&ftl->workr, mustscavenge, ftl);
+		if(ftl->detach == Deferred){
+			ftlfree(ftl);
+			pexit("", 0);
+		}
+		if(FTLDEBUG || ftl->trace)
+			print("ftl: scavenge %ld\n", ftl->nfree);
+		qlock(ftl);
+		if(waserror()){
+			qunlock(ftl);
+			nexterror();
+		}
+		e = bestcopy(ftl);
+		if(e == nil || ftl->xfer < 0 || (ne = ftl->unit[ftl->xfer]) == nil || ne->id != XferID || e == ne)
+			goto Fail;
+		if(copyunit(ftl, e, ne)){
+			i = ne->x; ne->x = e->x; e->x = i;
+			ftl->unit[ne->x] = ne;
+			ftl->unit[e->x] = e;
+			ftl->xfer = e->x;
+			e->id = XferID;
+			e->nbam = 0;
+			free(e->bam);
+			e->bam = nil;
+			e->bamx = 0;
+			e->nerase++;
+			eraseinit(ftl, e->offset, XferID, e->nerase);
+		}
+	Fail:
+		if(FTLDEBUG || ftl->trace)
+			print("ftl: end scavenge %ld\n", ftl->nfree);
+		ftl->needspace = 0;
+		wakeup(&ftl->wantr);
+		poperror();
+		qunlock(ftl);
+	}
+}
+
+static int
+scavenge(Ftl *ftl)
+{
+	if(ftl->xfer < 0 || bestcopy(ftl) == nil)
+		return 0;	/* you worm! */
+
+	qlock(ftl);
+	if(waserror()){
+		qunlock(ftl);
+		return 0;
+	}
+	if(!ftl->hasproc){
+		ftl->hasproc = 1;
+		kproc("ftl.scavenge", scavengeproc, ftl, 0);
+	}
+	ftl->needspace = 1;
+	wakeup(&ftl->workr);
+	poperror();
+	qunlock(ftl);
+
+	qlock(&ftl->wantq);
+	if(waserror()){
+		qunlock(&ftl->wantq);
+		nexterror();
+	}
+	while(ftl->needspace)
+		sleep(&ftl->wantr, donescavenge, ftl);
+	poperror();
+	qunlock(&ftl->wantq);
+
+	return ftl->nfree;
+}
+
+static void
+putbam(Ftl *ftl, Terase *e, int n, ulong entry)
+{
+	uchar b[4];
+
+	e->bam[n] = entry;
+	PUT4(b, entry);
+	putflash(ftl, e->bamoffset + n*4, b, 4);
+}
+
+static ulong
+allocblk(Ftl *ftl)
+{
+	Terase *e;
+	int i, j;
+
+	qlock(ftl);
+	i = ftl->lastx;
+	do{
+		e = ftl->unit[i];
+		if(e != nil && e->id != XferID && e->nfree){
+			ftl->lastx = i;
+			for(j=e->bamx; j<e->nbam; j++)
+				if(e->bam[j] == Bfree){
+					putbam(ftl, e, j, Bwriting);
+					ftl->nfree--;
+					e->nfree--;
+					e->bamx = j+1;
+					qunlock(ftl);
+					return (e->x<<16) | j;
+				}
+			e->nfree = 0;
+			qunlock(ftl);
+			print("ftl: unit %d:#%x nfree %ld but not free in BAM\n", e->x, e->id, e->nfree);
+			qlock(ftl);
+		}
+		if(++i >= ftl->nunit)
+			i = 0;
+	}while(i != ftl->lastx);
+	qunlock(ftl);
+	return 0;
+}
+
+static int
+mapblk(Ftl *ftl, ulong bno, Terase **ep, ulong *bp)
+{
+	ulong v;
+	int x;
+
+	if(bno < ftl->nblock){
+		v = ftl->vbm[bno];
+		if(v == 0 || v == ~0)
+			return 0;
+		x = v>>16;
+		if(x >= ftl->nunit || x == ftl->xfer || ftl->unit[x] == nil){
+			print("ftl: corrupt format: bad block mapping %lud -> unit #%x\n", bno, x);
+			return 0;
+		}
+		*ep = ftl->unit[x];
+		*bp = v & 0xFFFF;
+		return 1;
+	}
+	return 0;
+}
+
+static void
+eraseinit(Ftl *ftl, ulong offset, int id, int nerase)
+{
+	union {
+		Merase;
+		uchar	block[ERASEHDRLEN];
+	} *m;
+	uchar *bam, *p;
+	int i, nov;
+
+	nov = ((ftl->segsize/Bsize)*4 + BAMoffset + Bsize - 1)/Bsize;	/* number of overhead blocks (header, and BAM itself) */
+	if(nov*Bsize >= ftl->segsize)
+		error("ftl -- too small for files");
+	eraseflash(ftl, offset);
+	m = malloc(sizeof(*m));
+	if(m == nil)
+		error(Enomem);
+	memset(m, 0xFF, sizeof(*m));
+	m->linktuple[0] = 0x13;
+	m->linktuple[1] = 0x3;
+	memmove(m->linktuple+2, "CIS", 3);
+	m->orgtuple[0] = 0x46;
+	m->orgtuple[1] = 0x57;
+	m->orgtuple[2] = 0x00;
+	memmove(m->orgtuple+3, "FTL100", 7);
+	m->nxfer = 1;
+	PUT4(m->nerase, nerase);
+	PUT2(m->id, id);
+	m->bshift = ftl->bshift;
+	m->eshift = ftl->eshift;
+	PUT2(m->pstart, ftl->base >> ftl->eshift);
+	PUT2(m->nunits, ftl->nunit);
+	PUT4(m->psize, ftl->size - nov*Bsize*ftl->nunit);
+	PUT4(m->vbmbase, 0xffffffff);	/* we always calculate the VBM */
+	PUT2(m->nvbm, 0);
+	m->flags = 0;
+	m->code = 0xFF;
+	memmove(m->serial, "Inf1", 4);
+	PUT4(m->altoffset, 0);
+	PUT4(m->bamoffset, BAMoffset);
+	putflash(ftl, offset, m, ERASEHDRLEN);
+	free(m);
+	if(id == XferID)
+		return;
+	nov *= 4;	/* now bytes of BAM */
+	bam = malloc(nov);
+	if(bam == nil)
+		error(Enomem);
+	for(i=0; i<nov; i += 4){
+		p = bam+i;
+		PUT4(p, ControlBlock);	/* reserve them */
+	}
+	putflash(ftl, offset+BAMoffset, bam, nov);
+	free(bam);
+}
+
+static int
+erasedetect(Ftl *ftl, ulong base, ulong size, ushort *pstart, ushort *nunits)
+{
+	ulong o;
+	int rv;
+
+	union {
+		Merase;
+		uchar	block[ERASEHDRLEN];
+	} *m;
+	m = malloc(sizeof(*m));
+	if(m == nil)
+		error(Enomem);
+	rv  = 0;
+	for(o = base; o < base + size; o += ftl->segsize){
+		if(waserror())
+			continue;
+		getflash(ftl, m, o, ERASEHDRLEN);
+		poperror();
+		if(memcmp(m->orgtuple + 3, "FTL100", 7) == 0
+		    && memcmp(m->serial, "Inf1", 4) == 0){
+			*pstart = GET2(m->pstart);
+			*nunits = GET2(m->nunits);
+			rv = 1;
+			break;
+		}
+	}
+	free(m);
+	return rv;
+}
+
+static Terase *
+eraseload(Ftl *ftl, int x, ulong offset)
+{
+	union {
+		Merase;
+		uchar	block[ERASEHDRLEN];
+	} *m;
+	Terase *e;
+	uchar *p;
+	int i, nbam;
+	ulong bno, v;
+
+	m = malloc(sizeof(*m));
+	if(m == nil)
+		error(Enomem);
+	if(waserror()){
+		free(m);
+		return nil;
+	}
+	getflash(ftl, m, offset, ERASEHDRLEN);
+	poperror();
+	if(memcmp(m->orgtuple+3, "FTL100", 7) != 0 ||
+	   memcmp(m->serial, "Inf1", 4) != 0){
+		free(m);
+print("%8.8lux: bad sig\n", offset);
+		return nil;
+	}
+	e = malloc(sizeof(*e));
+	if(e == nil){
+		free(m);
+		error(Enomem);
+	}
+	e->x = x;
+	e->id = GET2(m->id);
+	e->offset = offset;
+	e->bamoffset = GET4(m->bamoffset);
+	e->nerase = GET4(m->nerase);
+	free(m);
+	m = nil;
+	USED(m);
+	if(e->bamoffset != BAMoffset){
+		free(e);
+print("%8.8lux: bad bamoffset %8.8lux\n", offset, e->bamoffset);
+		return nil;
+	}
+	e->bamoffset += offset;
+	if(e->id == XferID || e->id == XferBusy){
+		e->bam = nil;
+		e->nbam = 0;
+		return e;
+	}
+	nbam = ftl->segsize/Bsize;
+	e->bam = malloc(nbam*sizeof(*e->bam));
+	e->nbam = nbam;
+	if(waserror()){
+		free(e);
+		nexterror();
+	}
+	getflash(ftl, e->bam, e->bamoffset, nbam*4);
+	poperror();
+	/* scan BAM to build VBM */
+	e->bamx = 0;
+	for(i=0; i<nbam; i++){
+		p = (uchar*)&e->bam[i];
+		e->bam[i] = v = GET4(p);
+		if(v == Bwriting || v == Bdeleted)
+			e->ndead++;
+		else if(v == Bfree){
+			if(e->bamx == 0)
+				e->bamx = i;
+			e->nfree++;
+			ftl->nfree++;
+		}else{
+			switch(v & BlockType){
+			case ControlBlock:
+				break;
+			case DataBlock:
+				/* add to VBM */
+				if(v & (1<<31))
+					break;	/* negative => VBM page, ignored */
+				bno = BNO(v & ~BlockType);
+				if(i < ftl->fstart || bno >= ftl->nblock){
+					print("ftl: unit %d:#%x bad bam[%d]=#%lux\n", e->x, e->id, i, v);
+					e->nbad++;
+					break;
+				}
+				ftl->vbm[bno] = (e->x<<16) | i;
+				e->nused++;
+				break;
+			case ReplacePage:
+				/* replacement VBM page; ignored */
+				break;
+			default:
+				print("ftl: unit %d:#%x bad bam[%d]=%lux\n", e->x, e->id, i, v);
+			case BadBlock:
+				e->nbad++;
+				break;
+			}
+		}
+	}
+	return e;
+}
+
+static void
+erasefree(Terase *e)
+{
+	if(e){
+		free(e->bam);
+		free(e);
+	}
+}
+
+static void
+eraseflash(Ftl *ftl, ulong offset)
+{
+	char cmd[40];
+
+	offset += ftl->base;
+	if(FTLDEBUG || ftl->trace)
+		print("ftl: erase seg @#%lux\n", offset);
+	snprint(cmd, sizeof(cmd), "erase 0x%8.8lux", offset);
+	if(kchanio(ftl->flashctl, cmd, strlen(cmd), OWRITE) <= 0){
+		print("ftl: erase failed: %s\n", up->env->errstr);
+		error(up->env->errstr);
+	}
+}
+
+static void
+putflash(Ftl *ftl, ulong offset, void *buf, long n)
+{
+	offset += ftl->base;
+	if(ftl->trace)
+		print("ftl: write(#%lux, %ld)\n", offset, n);
+	ftl->flash->offset = offset;
+	if(kchanio(ftl->flash, buf, n, OWRITE) != n){
+		print("ftl: flash write error: %s\n", up->env->errstr);
+		error(up->env->errstr);
+	}
+}
+
+static void
+getflash(Ftl *ftl, void *buf, ulong offset, long n)
+{
+	offset += ftl->base;
+	if(ftl->trace)
+		print("ftl: read(#%lux, %ld)\n", offset, n);
+	ftl->flash->offset = offset;
+	if(kchanio(ftl->flash, buf, n, OREAD) != n){
+		print("ftl: flash read error %s\n", up->env->errstr);
+		error(up->env->errstr);
+	}
+}
+
+Dev ftldevtab = {
+	'X',	/* TO DO */
+	"ftl",
+
+	devreset,
+	devinit,
+	devshutdown,
+	ftlattach,
+	ftlwalk,
+	ftlstat,
+	ftlopen,
+	devcreate,
+	ftlclose,
+	ftlread,
+	devbread,
+	ftlwrite,
+	devbwrite,
+	devremove,
+	devwstat,
+};
--- /dev/null
+++ b/os/port/devi2c.c
@@ -1,0 +1,227 @@
+/*
+ * i2c
+ *
+ * Copyright © 1998, 2003 Vita Nuova Limited.
+ */
+
+#include "u.h"
+#include "../port/lib.h"
+#include "mem.h"
+#include "dat.h"
+#include "fns.h"
+#include "io.h"
+#include "../port/error.h"
+
+typedef struct I2Cdir I2Cdir;
+
+enum{
+	Qdir,
+	Qdata,
+	Qctl,
+};
+
+static
+Dirtab i2ctab[]={
+	".",	{Qdir, 0, QTDIR},	0,	0555,
+	"i2cdata",		{Qdata, 0},	256,	0660,
+	"i2cctl",		{Qctl, 0},		0,	0660,
+};
+
+struct I2Cdir {
+	Ref;
+	I2Cdev;
+	Dirtab	tab[nelem(i2ctab)];
+};
+
+static void
+i2creset(void)
+{
+	i2csetup(0);
+}
+
+static Chan*
+i2cattach(char* spec)
+{
+	char *s;
+	ulong addr;
+	I2Cdir *d;
+	Chan *c;
+
+	addr = strtoul(spec, &s, 16);
+	if(*spec == 0 || *s || addr >= (1<<10))
+		error("invalid i2c address");
+	d = malloc(sizeof(I2Cdir));
+	if(d == nil)
+		error(Enomem);
+	d->ref = 1;
+	d->addr = addr;
+	d->salen = 0;
+	d->tenbit = addr >= 128;
+	memmove(d->tab, i2ctab, sizeof(d->tab));
+	sprint(d->tab[1].name, "i2c.%lux.data", addr);
+	sprint(d->tab[2].name, "i2c.%lux.ctl", addr);
+
+	c = devattach('J', spec);
+	c->aux = d;
+	return c;
+}
+
+static Walkqid*
+i2cwalk(Chan* c, Chan *nc, char **name, int nname)
+{
+	Walkqid *wq;
+	I2Cdir *d;
+
+	d = c->aux;
+	wq = devwalk(c, nc, name, nname, d->tab, nelem(d->tab), devgen);
+	if(wq != nil && wq->clone != nil && wq->clone != c)
+		incref(d);
+	return wq;
+}
+
+static int
+i2cstat(Chan* c, uchar *dp, int n)
+{
+	I2Cdir *d;
+
+	d = c->aux;
+	return devstat(c, dp, n, d->tab, nelem(d->tab), devgen);
+}
+
+static Chan*
+i2copen(Chan* c, int omode)
+{
+	I2Cdir *d;
+
+	d = c->aux;
+	return devopen(c, omode, d->tab, nelem(d->tab), devgen);
+}
+
+static void
+i2cclose(Chan *c)
+{
+	I2Cdir *d;
+
+	d = c->aux;
+	if(decref(d) == 0)
+		free(d);
+}
+
+static long
+i2cread(Chan *c, void *a, long n, vlong offset)
+{
+	I2Cdir *d;
+	char *s, *e;
+	ulong len;
+
+	d = c->aux;
+	switch((ulong)c->qid.path){
+	case Qdir:
+		return devdirread(c, a, n, d->tab, nelem(d->tab), devgen);
+	case Qdata:
+		len = d->tab[1].length;
+		if(offset+n >= len){
+			n = len - offset;
+			if(n <= 0)
+				return 0;
+		}
+		n = i2crecv(d, a, n, offset);
+		break;
+	case Qctl:
+		s = smalloc(READSTR);
+		if(waserror()){
+			free(s);
+			nexterror();
+		}
+		e = seprint(s, s+READSTR, "size %lud\n", (ulong)d->tab[1].length);
+		if(d->salen)
+			e = seprint(e, s+READSTR, "subaddress %d\n", d->salen);
+		if(d->tenbit)
+			seprint(e, s+READSTR, "a10\n");
+		n = readstr(offset, a, n, s);
+		poperror();
+		free(s);
+		return n;
+	default:
+		n=0;
+		break;
+	}
+	return n;
+}
+
+static long
+i2cwrite(Chan *c, void *a, long n, vlong offset)
+{
+	I2Cdir *d;
+	long len;
+	Cmdbuf *cb;
+
+	USED(offset);
+	switch((ulong)c->qid.path){
+	case Qdata:
+		d = c->aux;
+		len = d->tab[1].length;
+		if(offset+n >= len){
+			n = len - offset;
+			if(n <= 0)
+				return 0;
+		}
+		n = i2csend(d, a, n, offset);
+		break;
+	case Qctl:
+		cb = parsecmd(a, n);
+		if(waserror()){
+			free(cb);
+			nexterror();
+		}
+		if(cb->nf < 1)
+			error(Ebadctl);
+		d = c->aux;
+		if(strcmp(cb->f[0], "subaddress") == 0){
+			if(cb->nf > 1){
+				len = strtol(cb->f[1], nil, 0);
+				if(len <= 0)
+					len = 0;
+				if(len > 4)
+					cmderror(cb, "subaddress too long");
+			}else
+				len = 1;
+			d->salen = len;
+		}else if(cb->nf > 1 && strcmp(cb->f[0], "size") == 0){
+			len = strtol(cb->f[1], nil, 0);
+			if(len < 0)
+				cmderror(cb, "size is negative");
+			d->tab[1].length = len;
+		}else if(strcmp(cb->f[0], "a10") == 0)
+			d->tenbit = 1;
+		else
+			cmderror(cb, "unknown control request");
+		poperror();
+		free(cb);
+		break;
+	default:
+		error(Ebadusefd);
+	}
+	return n;
+}
+
+Dev i2cdevtab = {
+	'J',
+	"i2c",
+
+	i2creset,
+	devinit,
+	devshutdown,
+	i2cattach,
+	i2cwalk,
+	i2cstat,
+	i2copen,
+	devcreate,
+	i2cclose,
+	i2cread,
+	devbread,
+	i2cwrite,
+	devbwrite,
+	devremove,
+	devwstat,
+};
--- /dev/null
+++ b/os/port/devindir.c
@@ -1,0 +1,40 @@
+#include	"u.h"
+#include	"../port/lib.h"
+#include	"mem.h"
+#include	"dat.h"
+#include	"fns.h"
+#include	"../port/error.h"
+
+static Chan *
+indirattach(char *spec)
+{
+	char *p;
+	Dev *d;
+
+	if(*spec == 0)
+		error(Ebadspec);
+	p = strrchr(spec, '!');
+	if(p == nil)
+		p = "";
+	else
+		*p++ = 0;
+	d = devbyname(spec);
+	if(d == nil || d->dc == '*'){
+		snprint(up->env->errstr, ERRMAX, "unknown device: %s", spec);
+		error(up->env->errstr);
+	}
+	if(up->env->pgrp->nodevs &&
+	   (utfrune("|esDa", d->dc) == nil || d->dc == 's' && *p!='\0'))
+		error(Enoattach);
+	return d->attach(p);
+}
+
+Dev indirdevtab = {
+	'*',
+	"indir",
+
+	devreset,
+	devinit,
+	devshutdown,
+	indirattach,
+};
--- /dev/null
+++ b/os/port/devkprof.c
@@ -1,0 +1,230 @@
+#include	"u.h"
+#include	"../port/lib.h"
+#include	"mem.h"
+#include	"dat.h"
+#include	"fns.h"
+#include	"../port/error.h"
+
+#ifndef LRES
+#define	LRES	3		/* log of PC resolution */
+#endif
+
+#define	SZ	4		/* sizeof of count cell; well known as 4 */
+
+enum {
+	SpecialTotalTicks,
+	SpecialOutsideTicks,
+	SpecialMicroSecondsPerTick,
+	SpecialSamples,
+	SpecialSampleSize,
+	SpecialSampleLogBucketSize,
+	SpecialMax
+};
+
+struct
+{
+	int	minpc;
+	int	maxpc;
+	int	nbuf;
+	int	time;
+	ulong	*buf;
+}kprof;
+
+enum{
+	Qdir,
+	Qdata,
+	Qctl,
+	Kprofmaxqid,
+};
+
+Dirtab kproftab[]={
+	".",		{Qdir, 0, QTDIR},	0,	0500,
+	"kpdata",	{Qdata},		0,	0600,
+	"kpctl",	{Qctl},		0,	0600,
+};
+
+void kproftimer(ulong);
+void	(*kproftick)(ulong);
+
+static void
+kprofinit(void)
+{
+	if(SZ != sizeof kprof.buf[0])
+		panic("kprof size");
+}
+
+static void
+kprofbufinit(void)
+{
+	kprof.buf[SpecialMicroSecondsPerTick] = archkprofmicrosecondspertick();
+	kprof.buf[SpecialSamples] = kprof.nbuf;
+	kprof.buf[SpecialSampleSize] = SZ;
+	kprof.buf[SpecialSampleLogBucketSize] = LRES;
+}
+
+static Chan *
+kprofattach(char *spec)
+{
+	ulong n;
+
+	/* allocate when first used */
+	kproftick = kproftimer;
+	kprof.minpc = KTZERO;
+	kprof.maxpc = (ulong)etext;
+	kprof.nbuf = (kprof.maxpc-kprof.minpc) >> LRES;
+	n = kprof.nbuf*SZ;
+	if(kprof.buf == 0) {
+		kprof.buf = xalloc(n);
+		if(kprof.buf == 0)
+			error(Enomem);
+	}
+	kproftab[0].length = n;
+	kprofbufinit();
+	return devattach('K', spec);
+}
+
+static Walkqid*
+kprofwalk(Chan *c, Chan *nc, char **name, int nname)
+{
+	return devwalk(c, nc, name, nname, kproftab, nelem(kproftab), devgen);
+}
+
+static int
+kprofstat(Chan *c, uchar *db, int n)
+{
+	return devstat(c, db, n, kproftab, nelem(kproftab), devgen);
+}
+
+static Chan *
+kprofopen(Chan *c, int omode)
+{
+	if(c->qid.type & QTDIR){
+		if(omode != OREAD)
+			error(Eperm);
+	}
+	c->mode = openmode(omode);
+	c->flag |= COPEN;
+	c->offset = 0;
+	return c;
+}
+
+void
+kprofclose(Chan*)
+{
+}
+
+static long
+kprofread(Chan *c, void *va, long n, vlong offset)
+{
+	ulong tabend;
+	ulong w, *bp;
+	uchar *a, *ea;
+
+	switch((ulong)c->qid.path){
+	case Qdir:
+		return devdirread(c, va, n, kproftab, nelem(kproftab), devgen);
+
+	case Qdata:
+		tabend = kprof.nbuf*SZ;
+		if(offset & (SZ-1))
+			error(Ebadarg);
+		if(offset >= tabend){
+			n = 0;
+			break;
+		}
+		if(offset+n > tabend)
+			n = tabend-offset;
+		n &= ~(SZ-1);
+		a = va;
+		ea = a + n;
+		bp = kprof.buf + offset/SZ;
+		while(a < ea){
+			w = *bp++;
+			*a++ = w>>24;
+			*a++ = w>>16;
+			*a++ = w>>8;
+			*a++ = w>>0;
+		}
+		break;
+
+	default:
+		n = 0;
+		break;
+	}
+	return n;
+}
+
+static long
+kprofwrite(Chan *c, void *vp, long n, vlong offset)
+{
+	char *a;
+	USED(offset);
+
+	a = vp;
+	switch((ulong)c->qid.path){
+	case Qctl:
+		if(strncmp(a, "startclr", 8) == 0){
+			memset((char *)kprof.buf, 0, kprof.nbuf*SZ);
+			kprofbufinit();
+			archkprofenable(1);
+			kprof.time = 1;
+		}else if(strncmp(a, "start", 5) == 0) {
+			archkprofenable(1);
+			kprof.time = 1;
+		}
+		else if(strncmp(a, "stop", 4) == 0) {
+			archkprofenable(0);
+			kprof.time = 0;
+		}
+		else
+			error(Ebadctl);
+		break;
+	default:
+		error(Ebadusefd);
+	}
+	return n;
+}
+
+void
+kproftimer(ulong pc)
+{
+	extern void spldone(void);
+
+	if(kprof.time == 0)
+		return;
+	/*
+	 *  if the pc is coming out of spllo or splx,
+	 *  use the pc saved when we went splhi.
+	 */
+//	if(pc>=(ulong)splx && pc<=(ulong)spldone)
+//		pc = m->splpc;
+
+	kprof.buf[SpecialTotalTicks]++;
+	if(kprof.minpc + (SpecialMax << LRES) <= pc && pc < kprof.maxpc){
+		pc -= kprof.minpc;
+		pc >>= LRES;
+		kprof.buf[pc]++;
+	}else
+		kprof.buf[SpecialOutsideTicks]++;
+}
+
+Dev kprofdevtab = {
+	'K',
+	"kprof",
+
+	devreset,
+	kprofinit,
+	devshutdown,
+	kprofattach,
+	kprofwalk,
+	kprofstat,
+	kprofopen,
+	devcreate,
+	kprofclose,
+	kprofread,
+	devbread,
+	kprofwrite,
+	devbwrite,
+	devremove,
+	devwstat,
+};
--- /dev/null
+++ b/os/port/devlogfs.c
@@ -1,0 +1,1528 @@
+#ifndef EMU
+#include "u.h"
+#include "../port/lib.h"
+#include "../port/error.h"
+#include "mem.h"
+#else
+#include	"error.h"
+#endif
+#include	"dat.h"
+#include	"fns.h"
+#include	"kernel.h"
+#include	"logfs.h"
+#include	"nandfs.h"
+
+#ifndef EMU
+#define Sleep sleep
+#define Wakeup wakeup
+#endif
+
+typedef struct Devlogfs Devlogfs;
+typedef struct DevlogfsSession DevlogfsSession;
+
+//#define CALLTRACE
+
+enum {
+	DEVLOGFSDEBUG = 0,
+	DEVLOGFSIODEBUG = 0,
+	DEVLOGFSBAD = 1,
+};
+
+enum {
+	Qdir,
+	Qctl,
+	Qusers,
+	Qdump,
+	Qfs,
+	Qfsboot,
+	Qend,
+};
+
+typedef enum DevlogfsServerState { Closed, BootOpen, NeedVersion, NeedAttach, Attached, Hungup } DevlogfsServerState;
+
+struct Devlogfs {
+	QLock qlock;
+	QLock	rlock;
+	QLock	wlock;
+	Ref ref;
+	int instance;
+	int trace;	/* (debugging) trace of read/write actions */
+	int nand;
+	char *name;
+	char *device;
+	char *filename[Qend - Qfs];
+	LogfsLowLevel *ll;
+	Chan *flash, *flashctl;
+	QLock bootqlock;
+	int logfstrace;
+	LogfsBoot *lb;
+	/* stuff for server */
+	ulong openflags;
+	Fcall in;
+	Fcall out;
+	int reading;
+	DevlogfsServerState state;
+	Rendez readrendez;
+	Rendez writerendez;
+	uint readcount;
+	ulong readbufsize;
+	uchar *readbuf;
+	uchar *readp;
+	LogfsServer *server;
+	Devlogfs *next;
+};
+
+#define MAXMSIZE 8192
+
+static struct {
+	RWlock rwlock;		/* rlock when walking, wlock when changing */
+	QLock configqlock;		/* serialises addition of new configurations */
+	Devlogfs *head;
+	char *defname;
+} devlogfslist;
+
+static LogfsIdentityStore *is;
+
+#ifndef EMU
+char Eunknown[] = "unknown user or group id";
+#endif
+
+static	void	devlogfsfree(Devlogfs*);
+
+#define SPLITPATH(path, qtype, instance, qid, qt) { instance = path >> 4; qid = path & 0xf; qt = qtype & QTDIR; }
+#define DATAQID(q, qt) (!(qt) && (q) >= Qfs && (q) < Qend)
+#define MKPATH(instance, qid) ((instance << 4) | qid)
+
+#define PREFIX "logfs"
+
+static char *devlogfsprefix = PREFIX;
+static char *devlogfsctlname = PREFIX "ctl";
+static char *devlogfsusersname = PREFIX "users";
+static char *devlogfsdumpname = PREFIX "dump";
+static char *devlogfsbootsuffix = "boot";
+static char *devlogfs9pversion = "9P2000";
+
+static void
+errorany(char *errmsg)
+{
+	if (errmsg)
+		error(errmsg);
+}
+
+static void *
+emalloc(ulong size)
+{
+	void *p;
+	p = logfsrealloc(nil, size);
+	if (p == nil)
+		error(Enomem);
+	return p;
+}
+
+static char *
+estrdup(char *q)
+{
+	void *p;
+	if (q == nil)
+		return nil;
+	p = logfsrealloc(nil, strlen(q) + 1);
+	if (p == nil)
+		error(Enomem);
+	return strcpy(p, q);
+}
+
+static char *
+estrconcat(char *a, ...)
+{
+	va_list l;
+	char *p, *r;
+	int t;
+
+	t = strlen(a);
+	va_start(l, a);
+	while ((p = va_arg(l, char *)) != nil)
+		t += strlen(p);
+
+	r = logfsrealloc(nil, t + 1);
+	if (r == nil)
+		error(Enomem);
+
+	strcpy(r, a);
+	va_start(l, a);
+	while ((p = va_arg(l, char *)) != nil)
+		strcat(r, p);
+
+	va_end(l);
+
+	return r;
+}
+
+static int
+gen(Chan *c, int i, Dir *dp, int lockit)
+{
+	Devlogfs *l;
+	long size;
+	Qid qid;
+	qid.vers = 0;
+	qid.type = 0;
+
+	if (i + Qctl < Qfs) {
+		switch (i + Qctl) {
+		case Qctl:
+			qid.path = Qctl;
+			devdir(c, qid, devlogfsctlname, 0, eve, 0666, dp);
+			return 1;
+		case Qusers:
+			qid.path = Qusers;
+			devdir(c, qid, devlogfsusersname, 0, eve, 0444, dp);
+			return 1;
+		case Qdump:
+			qid.path = Qdump;
+			devdir(c, qid, devlogfsdumpname, 0, eve, 0444, dp);
+			return 1;
+		}
+	}
+
+	i -= Qfs - Qctl;
+
+	if (lockit)
+		rlock(&devlogfslist.rwlock);
+
+	if (waserror()) {
+		if (lockit)
+			runlock(&devlogfslist.rwlock);
+		nexterror();
+	}
+
+	for (l = devlogfslist.head; l; l = l->next) {
+		if (i < Qend - Qfs)
+			break;
+		i -= Qend - Qfs;
+	}
+
+	if (l == nil) {
+		poperror();
+		if (lockit)
+			runlock(&devlogfslist.rwlock);
+		return -1;
+	}
+
+	switch (Qfs + i) {
+	case Qfsboot:
+		size = l->lb ? logfsbootgetsize(l->lb) : 0;
+		break;
+	default:
+		size = 0;
+		break;
+	}
+	/* perhaps the user id should come from the underlying file */
+	qid.path = MKPATH(l->instance, Qfs + i);
+	devdir(c, qid, l->filename[i], size, eve, 0666, dp);
+
+	poperror();
+	if (lockit)
+		runlock(&devlogfslist.rwlock);
+
+	return 1;
+}
+
+static int
+devlogfsgen(Chan *c, char *n, Dirtab *tab, int ntab, int i, Dir *dp)
+{
+	USED(n);
+	USED(tab);
+	USED(ntab);
+	return gen(c, i, dp, 1);
+}
+
+static int
+devlogfsgennolock(Chan *c, char *n, Dirtab *tab, int ntab, int i, Dir *dp)
+{
+	USED(n);
+	USED(tab);
+	USED(ntab);
+	return gen(c, i, dp, 0);
+}
+
+/* called under lock */
+static Devlogfs *
+devlogfsfind(int instance)
+{
+	Devlogfs *l;
+
+	for (l = devlogfslist.head; l; l = l->next)
+		if (l->instance == instance)
+			break;
+	return l;
+}
+
+static Devlogfs *
+devlogfsget(int instance)
+{
+	Devlogfs *l;
+	rlock(&devlogfslist.rwlock);
+	for (l = devlogfslist.head; l; l = l->next)
+		if (l->instance == instance)
+			break;
+	if (l)
+		incref(&l->ref);
+	runlock(&devlogfslist.rwlock);
+	return l;
+}
+
+static Devlogfs *
+devlogfsfindbyname(char *name)
+{
+	Devlogfs *l;
+
+	rlock(&devlogfslist.rwlock);
+	for (l = devlogfslist.head; l; l = l->next)
+		if (strcmp(l->name, name) == 0)
+			break;
+	runlock(&devlogfslist.rwlock);
+	return l;
+}
+
+static Devlogfs *
+devlogfssetdefname(char *name)
+{
+	Devlogfs *l;
+	char *searchname;
+	wlock(&devlogfslist.rwlock);
+	if (waserror()) {
+		wunlock(&devlogfslist.rwlock);
+		nexterror();
+	}
+	if (name == nil)
+		searchname = devlogfslist.defname;
+	else
+		searchname = name;
+	for (l = devlogfslist.head; l; l = l->next)
+		if (strcmp(l->name, searchname) == 0)
+			break;
+	if (l == nil) {
+		logfsfreemem(devlogfslist.defname);
+		devlogfslist.defname = nil;
+	}
+	else if (name) {
+		if (devlogfslist.defname) {
+			logfsfreemem(devlogfslist.defname);
+			devlogfslist.defname = nil;
+		}
+		devlogfslist.defname = estrdup(name);
+	}
+	poperror();
+	wunlock(&devlogfslist.rwlock);
+	return l;
+}
+
+static Chan *
+devlogfskopen(char *name, char *suffix, int mode)
+{
+	Chan *c;
+	char *fn;
+	int fd;
+
+	fn = estrconcat(name, suffix, 0);
+	fd = kopen(fn, mode);
+	logfsfreemem(fn);
+	if (fd < 0)
+		error(up->env->errstr);
+	c = fdtochan(up->env->fgrp, fd, mode, 0, 1);
+	kclose(fd);
+	return c;
+}
+
+static char *
+xread(void *a, void *buf, long nbytes, ulong offset)
+{
+	Devlogfs *l = a;
+	long rv;
+
+	if (DEVLOGFSIODEBUG || l->trace)
+		print("devlogfs: %s: read(0x%lux, %ld)\n", l->device, offset, nbytes);
+	l->flash->offset = offset;
+	rv = kchanio(l->flash, buf, nbytes, OREAD);
+	if (rv < 0) {
+		print("devlogfs: %s: flash read error: %s\n", l->device, up->env->errstr);
+		return up->env->errstr;
+	}
+	if (rv != nbytes) {
+		print("devlogfs: %s: short flash read: offset %lud, %ld not %ld\n", l->device, offset, rv, nbytes);
+		return "short read";
+	}
+	return nil;
+}
+
+static char *
+xwrite(void *a, void *buf, long nbytes, ulong offset)
+{
+	Devlogfs *l = a;
+	long rv;
+
+	if (DEVLOGFSIODEBUG || l->trace)
+		print("devlogfs: %s: write(0x%lux, %ld)\n", l->device, offset, nbytes);
+	l->flash->offset = offset;
+	rv = kchanio(l->flash, buf, nbytes, OWRITE);
+	if (rv < 0) {
+		print("devlogfs: %s: flash write error: %s\n", l->device, up->env->errstr);
+		return up->env->errstr;
+	}
+	if (rv != nbytes) {
+		print("devlogfs: %s: short flash write: offset %lud, %ld not %ld\n", l->device, offset, rv, nbytes);
+		return "short write";
+	}
+	return nil;
+}
+
+static char *
+xerase(void *a, long address)
+{
+	Devlogfs *l = a;
+	char cmd[40];
+
+	if (DEVLOGFSIODEBUG || l->trace)
+		print("devlogfs: %s: erase(0x%lux)\n", l->device, address);
+	snprint(cmd, sizeof(cmd), "erase 0x%8.8lux", address);
+	if (kchanio(l->flashctl, cmd, strlen(cmd), OWRITE) <= 0) {
+		print("devlogfs: %s: flash erase error: %s\n", l->device, up->env->errstr);
+		return up->env->errstr;
+	}
+	return nil;
+}
+
+static char *
+xsync(void *a)
+{
+	Devlogfs *l = a;
+
+	if (DEVLOGFSIODEBUG || l->trace)
+		print("devlogfs: %s: sync()\n", l->device);
+	if (kchanio(l->flashctl, "sync", 4, OWRITE) <= 0){
+		print("devlogfs: %s: flash sync error: %s\n", l->device, up->env->errstr);
+		return up->env->errstr;
+	}
+	return nil;
+}
+
+//#define LEAKHUNT
+#ifdef LEAKHUNT
+#define MAXLIVE 2000
+typedef struct Live {
+	void *p;
+	int freed;
+	ulong callerpc;
+} Live;
+
+static Live livemem[MAXLIVE];
+
+static void
+leakalloc(void *p, ulong callerpc)
+{
+	int x;
+	int use = -1;
+	for (x = 0; x < MAXLIVE; x++) {
+		if (livemem[x].p == p) {
+			if (!livemem[x].freed)
+				print("leakalloc: unexpected realloc of 0x%.8lux from 0x%.8lux\n", p, callerpc);
+//			else
+//				print("leakalloc: reusing address 0x%.8lux from 0x%.8lux\n", p, callerpc);
+			livemem[x].freed = 0;
+			livemem[x].callerpc = callerpc;
+			return;
+		}
+		else if (use < 0 && livemem[x].p == 0)
+			use = x;
+	}
+	if (use < 0)
+		panic("leakalloc: too many live entries");
+	livemem[use].p = p;
+	livemem[use].freed = 0;
+	livemem[use].callerpc = callerpc;
+}
+
+static void
+leakaudit(void)
+{
+	int x;
+	for (x = 0; x < MAXLIVE; x++) {
+		if (livemem[x].p && !livemem[x].freed)
+			print("leakaudit: 0x%.8lux from 0x%.8lux\n", livemem[x].p, livemem[x].callerpc);
+	}
+}
+
+static void
+leakfree(void *p, ulong callerpc)
+{
+	int x;
+	if (p == nil)
+		return;
+	for (x = 0; x < MAXLIVE; x++) {
+		if (livemem[x].p == p) {
+			if (livemem[x].freed)
+				print("leakfree: double free of 0x%.8lux from 0x%.8lux, originally by 0x%.8lux\n",
+					p, callerpc, livemem[x].callerpc);
+			livemem[x].freed = 1;
+			livemem[x].callerpc = callerpc;
+			return;
+		}
+	}
+	print("leakfree: free of unalloced address 0x%.8lux from 0x%.8lux\n", p, callerpc);
+	leakaudit();
+}
+
+static void
+leakrealloc(void *newp, void *oldp, ulong callerpc)
+{
+	leakfree(oldp, callerpc);
+	leakalloc(newp, callerpc);
+}
+#endif
+
+
+#ifdef LEAKHUNT
+static void *_realloc(void *p, ulong size, ulong callerpc)
+#else
+void *
+logfsrealloc(void *p, ulong size)
+#endif
+{
+	void *q;
+	ulong osize;
+	if (waserror()) {
+		print("wobbly thrown in memory allocator: %s\n", up->env->errstr);
+		nexterror();
+	}
+	if (p == nil) {
+		q = smalloc(size);
+		poperror();
+#ifdef LEAKHUNT
+		leakrealloc(q, nil, callerpc);
+#endif
+		return q;
+	}
+	q = realloc(p, size);
+	if (q) {
+		poperror();
+#ifdef LEAKHUNT
+		leakrealloc(q, p, callerpc);
+#endif
+		return q;
+	}
+	q = smalloc(size);
+	osize = msize(p);
+	if (osize > size)
+		osize = size;
+	memmove(q, p, osize);
+	free(p);
+	poperror();
+#ifdef LEAKHUNT
+	leakrealloc(q, p, callerpc);
+#endif
+	return q;
+}
+
+#ifdef LEAKHUNT
+void *
+logfsrealloc(void *p, ulong size)
+{
+	return _realloc(p, size, getcallerpc(&p));
+}
+
+void *
+nandfsrealloc(void *p, ulong size)
+{
+	return _realloc(p, size, getcallerpc(&p));
+}
+#else
+void *
+nandfsrealloc(void *p, ulong size)
+{
+	return logfsrealloc(p, size);
+}
+#endif
+
+void
+logfsfreemem(void *p)
+{
+#ifdef LEAKHUNT
+	leakfree(p, getcallerpc(&p));
+#endif
+	free(p);
+}
+
+void
+nandfsfreemem(void *p)
+{
+#ifdef LEAKHUNT
+	leakfree(p, getcallerpc(&p));
+#endif
+	free(p);
+}
+
+static Devlogfs *
+devlogfsconfig(char *name, char *device)
+{
+	Devlogfs *newl, *l;
+	int i;
+	int n;
+	char buf[100], *fields[12];
+	long rawblocksize, rawsize;
+
+	newl = nil;
+
+	qlock(&devlogfslist.configqlock);
+
+	if (waserror()) {
+		qunlock(&devlogfslist.configqlock);
+		devlogfsfree(newl);
+		nexterror();
+	}
+
+	rlock(&devlogfslist.rwlock);
+	for (l = devlogfslist.head; l; l = l->next)
+		if (strcmp(l->name, name) == 0) {
+			runlock(&devlogfslist.rwlock);
+			error(Einuse);
+		}
+
+	/* horrid n^2 solution to finding a unique instance number */
+
+	for (i = 0;; i++) {
+		for (l = devlogfslist.head; l; l = l->next)
+			if (l->instance == i)
+				break;
+		if (l == nil)
+			break;
+	}
+	runlock(&devlogfslist.rwlock);
+
+	newl = emalloc(sizeof(Devlogfs));
+	newl->instance = i;
+	newl->name = estrdup(name);
+	newl->device = estrdup(device);
+	newl->filename[Qfs - Qfs] = estrconcat(devlogfsprefix, name, nil);
+	newl->filename[Qfsboot - Qfs] = estrconcat(devlogfsprefix, name, devlogfsbootsuffix, nil);
+	newl->flash = devlogfskopen(device, nil, ORDWR);
+	newl->flashctl = devlogfskopen(device, "ctl", ORDWR);
+	newl->flashctl->offset = 0;
+	if ((n = kchanio(newl->flashctl, buf, sizeof(buf), OREAD)) <= 0) {
+		print("devlogfsconfig: read ctl failed: %s\n", up->env->errstr);
+		error(up->env->errstr);
+	}
+
+	if (n >= sizeof(buf))
+		n = sizeof(buf) - 1;
+	buf[n] = 0;
+	n = tokenize(buf, fields, nelem(fields));
+	if(n < 7)
+		error("unexpected flashctl format");
+	newl->nand = strcmp(fields[3], "nand") == 0;
+	rawblocksize = strtol(fields[6], nil, 0);
+	rawsize = strtol(fields[5], nil, 0)-strtol(fields[4], nil, 0);
+	if(newl->nand == 0)
+		error("only NAND supported at the moment");
+	errorany(nandfsinit(newl, rawsize, rawblocksize, xread, xwrite, xerase, xsync, &newl->ll));
+	wlock(&devlogfslist.rwlock);
+	newl->next = devlogfslist.head;
+	devlogfslist.head = newl;
+	logfsfreemem(devlogfslist.defname);
+	devlogfslist.defname = nil;
+	if (!waserror()){
+		devlogfslist.defname = estrdup(name);
+		poperror();
+	}
+	wunlock(&devlogfslist.rwlock);
+	poperror();
+	qunlock(&devlogfslist.configqlock);
+	return newl;
+}
+
+static void
+devlogfsunconfig(Devlogfs *devlogfs)
+{
+	Devlogfs **lp;
+
+	qlock(&devlogfslist.configqlock);
+
+	if (waserror()) {
+		qunlock(&devlogfslist.configqlock);
+		nexterror();
+	}
+
+	wlock(&devlogfslist.rwlock);
+
+	if (waserror()) {
+		wunlock(&devlogfslist.rwlock);
+		nexterror();
+	}
+
+	for (lp = &devlogfslist.head; *lp && (*lp) != devlogfs; lp = &(*lp)->next)
+		;
+	if (*lp == nil) {
+		if (DEVLOGFSBAD)
+			print("devlogfsunconfig: not in list\n");
+	}
+	else
+		*lp = devlogfs->next;
+
+	poperror();
+	wunlock(&devlogfslist.rwlock);
+
+	/* now invisible to the naked eye */
+	devlogfsfree(devlogfs);
+	poperror();
+	qunlock(&devlogfslist.configqlock);
+}
+
+static void
+devlogfsllopen(Devlogfs *l)
+{
+	qlock(&l->qlock);
+	if (waserror()) {
+		qunlock(&l->qlock);
+		nexterror();
+	}
+	if (l->lb == nil)
+		errorany(logfsbootopen(l->ll, 0, 0, l->logfstrace, 1, &l->lb));
+	l->state = BootOpen;
+	poperror();
+	qunlock(&l->qlock);
+}
+
+static void
+devlogfsllformat(Devlogfs *l, long bootsize)
+{
+	qlock(&l->qlock);
+	if (waserror()) {
+		qunlock(&l->qlock);
+		nexterror();
+	}
+	if (l->lb == nil)
+		errorany(logfsformat(l->ll, 0, 0, bootsize, l->logfstrace));
+	poperror();
+	qunlock(&l->qlock);
+}
+
+static Chan *
+devlogfsattach(char *spec)
+{
+	Chan *c;
+#ifdef CALLTRACE
+	print("devlogfsattach(spec = %s) - start\n", spec);
+#endif
+	/* create the identity store on first attach */
+	if (is == nil)
+		errorany(logfsisnew(&is));
+	c =  devattach(0x29f, spec);
+//	c =  devattach(L'ʟ', spec);
+#ifdef CALLTRACE
+	print("devlogfsattach(spec = %s) - return %.8lux\n", spec, (ulong)c);
+#endif
+	return c;
+}
+
+static Walkqid*
+devlogfswalk(Chan *c, Chan *nc, char **name, int nname)
+{
+	int instance, qid, qt, clone;
+	Walkqid *wq;
+
+#ifdef CALLTRACE
+	print("devlogfswalk(c = 0x%.8lux, nc = 0x%.8lux, name = 0x%.8lux, nname = %d) - start\n",
+		(ulong)c, (ulong)nc, (ulong)name, nname);
+#endif
+	clone = 0;
+	if(nc == nil){
+		nc = devclone(c);
+		nc->type = 0;
+		SPLITPATH(c->qid.path, c->qid.type, instance, qid, qt);
+		if(DATAQID(qid, qt))
+			nc->aux = devlogfsget(instance);
+		clone = 1;
+	}
+	wq = devwalk(c, nc, name, nname, 0, 0, devlogfsgen);
+	if (wq == nil || wq->nqid < nname) {
+		if(clone)
+			cclose(nc);
+	}
+	else if (clone) {
+		wq->clone = nc;
+		nc->type = c->type;
+	}
+#ifdef CALLTRACE
+	print("devlogfswalk(c = 0x%.8lux, nc = 0x%.8lux, name = 0x%.8lux, nname = %d) - return\n",
+		(ulong)c, (ulong)nc, (ulong)name, nname);
+#endif
+	return wq;
+}
+
+static int
+devlogfsstat(Chan *c, uchar *dp, int n)
+{
+#ifdef CALLTRACE
+	print("devlogfsstat(c = 0x%.8lux, dp = 0x%.8lux n= %d)\n",
+		(ulong)c, (ulong)dp, n);
+#endif
+	return devstat(c, dp, n, 0, 0, devlogfsgen);
+}
+
+static Chan*
+devlogfsopen(Chan *c, int omode)
+{
+	int instance, qid, qt;
+
+	omode = openmode(omode);
+	SPLITPATH(c->qid.path, c->qid.type, instance, qid, qt);
+#ifdef CALLTRACE
+	print("devlogfsopen(c = 0x%.8lux, omode = %o, instance = %d, qid = %d, qt = %d)\n",
+		(ulong)c, omode, instance, qid, qt);
+#endif
+
+
+	rlock(&devlogfslist.rwlock);
+	if (waserror()) {
+		runlock(&devlogfslist.rwlock);
+#ifdef CALLTRACE
+		print("devlogfsopen(c = 0x%.8lux, omode = %o) - error %s\n", (ulong)c, omode, up->env->errstr);
+#endif
+		nexterror();
+	}
+
+	if (DATAQID(qid, qt)) {
+		Devlogfs *d;
+		d = devlogfsfind(instance);
+		if (d == nil)
+			error(Enodev);
+		if (strcmp(up->env->user, eve) != 0)
+			error(Eperm);
+		if (qid == Qfs && d->state != BootOpen)
+			error(Eperm);
+		if (d->server == nil) {
+			errorany(logfsservernew(d->lb, d->ll, is, d->openflags, d->logfstrace, &d->server));
+			d->state = NeedVersion;
+		}
+		c = devopen(c, omode, 0, 0, devlogfsgennolock);
+		incref(&d->ref);
+		c->aux = d;
+	}
+	else if (qid == Qctl || qid == Qusers) {
+		if (strcmp(up->env->user, eve) != 0)
+			error(Eperm);
+		c = devopen(c, omode, 0, 0, devlogfsgennolock);
+	}
+	else
+		c = devopen(c, omode, 0, 0, devlogfsgennolock);
+	poperror();
+	runlock(&devlogfslist.rwlock);
+#ifdef CALLTRACE
+	print("devlogfsopen(c = 0x%.8lux, omode = %o) - return\n", (ulong)c, omode);
+#endif
+	return c;
+}
+
+static void
+devlogfsclose(Chan *c)
+{
+	int instance, qid, qt;
+#ifdef CALLTRACE
+	print("devlogfsclose(c = 0x%.8lux)\n", (ulong)c);
+#endif
+	SPLITPATH(c->qid.path, c->qid.type, instance, qid, qt);
+	USED(instance);
+	if(DATAQID(qid, qt) && (c->flag & COPEN) != 0) {
+		Devlogfs *d;
+		d = c->aux;
+		qlock(&d->qlock);
+		if (qid == Qfs && d->state == Attached) {
+			logfsserverflush(d->server);
+			logfsserverfree(&d->server);
+			d->state = BootOpen;
+		}
+		qunlock(&d->qlock);
+		decref(&d->ref);
+	}
+#ifdef CALLTRACE
+	print("devlogfsclose(c = 0x%.8lux) - return\n", (ulong)c);
+#endif
+}
+
+typedef char *(SMARTIOFN)(void *magic, void *buf, long n, ulong offset, int write);
+
+static void
+smartio(SMARTIOFN *io, void *magic, void *buf, long n, ulong offset, long blocksize, int write)
+{
+	void *tmp = nil;
+	ulong blocks, toread;
+
+	if (waserror()) {
+		logfsfreemem(tmp);
+		nexterror();
+	}
+	if (offset % blocksize) {
+		ulong aoffset;
+		int tmpoffset;
+		int tocopy;
+
+		if (tmp == nil)
+			tmp = emalloc(blocksize);
+		aoffset = offset / blocksize;
+		aoffset *= blocksize;
+		errorany((*io)(magic, tmp, blocksize, aoffset, 0));
+		tmpoffset = offset - aoffset;
+		tocopy = blocksize - tmpoffset;
+		if (tocopy > n)
+			tocopy = n;
+		if (write) {
+			memmove((uchar *)tmp + tmpoffset, buf, tocopy);
+			errorany((*io)(magic, tmp, blocksize, aoffset, 1));
+		}
+		else
+			memmove(buf, (uchar *)tmp + tmpoffset, tocopy);
+		buf = (uchar *)buf + tocopy;
+		n -= tocopy;
+		offset = aoffset + blocksize;
+	}
+	blocks = n / blocksize;
+	toread = blocks * blocksize;
+	errorany((*io)(magic, buf, toread, offset, write));
+	buf = (uchar *)buf + toread;
+	n -= toread;
+	offset += toread;
+	if (n) {
+		if (tmp == nil)
+			tmp = emalloc(blocksize);
+		errorany((*io)(magic, tmp, blocksize, offset, 0));
+		if (write) {
+			memmove(tmp, buf, n);
+			errorany((*io)(magic, tmp, blocksize, offset, 1));
+		}
+		memmove(buf, tmp, n);
+	}
+	poperror();
+	logfsfreemem(tmp);
+}
+
+static int
+readok(void *a)
+{
+	Devlogfs *d = a;
+	return d->reading;
+}
+
+static int
+writeok(void *a)
+{
+	Devlogfs *d = a;
+	return !d->reading;
+}
+
+static long
+lfsrvread(Devlogfs *d, void *buf, long n)
+{
+	qlock(&d->rlock);
+	if(waserror()){
+		qunlock(&d->rlock);
+		nexterror();
+	}
+	if (d->state == Hungup)
+		error(Ehungup);
+	Sleep(&d->readrendez, readok, d);
+	if (n > d->readcount)
+		n = d->readcount;
+	memmove(buf, d->readp, n);
+	d->readp += n;
+	d->readcount -= n;
+	if (d->readcount == 0) {
+		d->reading = 0;
+		Wakeup(&d->writerendez);
+	}
+	poperror();
+	qunlock(&d->rlock);
+	return n;
+}
+
+static void
+reply(Devlogfs *d)
+{
+	d->readp = d->readbuf;
+	d->readcount = convS2M(&d->out, d->readp, d->readbufsize);
+//print("reply is %d bytes\n", d->readcount);
+	if (d->readcount == 0)
+		panic("logfs: reply: did not fit\n");
+	d->reading = 1;
+	Wakeup(&d->readrendez);
+}
+
+static void
+rerror(Devlogfs *d, char *ename)
+{
+	d->out.type = Rerror;
+	d->out.ename = ename;
+	reply(d);
+}
+
+static struct {
+	QLock qlock;
+	int (*read)(void *magic, Devlogfs *d, int line, char *buf, int buflen);
+	void *magic;
+	Devlogfs *d;
+	int line;
+} dump;
+
+static void *
+extentdumpinit(Devlogfs *d, int argc, char **argv)
+{
+	int *p;
+	ulong path;
+	u32int flashaddr, length;
+	long block;
+	int page, offset;
+
+	if (argc != 1)
+		error(Ebadarg);
+	path = strtoul(argv[0], 0, 0);
+	errorany(logfsserverreadpathextent(d->server, path, 0, &flashaddr, &length, &block, &page, &offset));
+	p = emalloc(sizeof(ulong));
+	*p = path;
+	return p;
+}
+
+static int
+extentdumpread(void *magic, Devlogfs *d, int line, char *buf, int buflen)
+{
+	ulong *p = magic;
+	u32int flashaddr, length;
+	long block;
+	int page, offset;
+	USED(d);
+	errorany(logfsserverreadpathextent(d->server, *p, line, &flashaddr, &length, &block, &page, &offset));
+	if (length == 0)
+		return 0;
+	return snprint(buf, buflen, "%.8ux %ud %ld %d %d\n", flashaddr, length, block, page, offset);
+}
+
+static void
+devlogfsdumpinit(Devlogfs *d,
+	void *(*init)(Devlogfs *d, int argc, char **argv),
+	int (*read)(void *magic, Devlogfs *d, int line, char *buf, int buflen), int argc, char **argv)
+{
+	qlock(&dump.qlock);
+	if (waserror()) {
+		qunlock(&dump.qlock);
+		nexterror();
+	}
+	if (d) {
+		if (d->state < NeedVersion)
+			error("not mounted");
+		qlock(&d->qlock);
+		if (waserror()) {
+			qunlock(&d->qlock);
+			nexterror();
+		}
+	}
+	if (dump.magic) {
+		logfsfreemem(dump.magic);
+		dump.magic = nil;
+	}
+	dump.d = d;
+	dump.magic = (*init)(d, argc, argv);
+	dump.read = read;
+	dump.line = 0;
+	if (d) {
+		poperror();
+		qunlock(&d->qlock);
+	}
+	poperror();
+	qunlock(&dump.qlock);
+}
+
+static long
+devlogfsdumpread(char *buf, int buflen)
+{
+	char *tmp = nil;
+	long n;
+	qlock(&dump.qlock);
+	if (waserror()) {
+		logfsfreemem(tmp);
+		qunlock(&dump.qlock);
+		nexterror();
+	}
+	if (dump.magic == nil)
+		error(Eio);
+	tmp = emalloc(READSTR);
+	if (dump.d) {
+		if (dump.d->state < NeedVersion)
+			error("not mounted");
+		qlock(&dump.d->qlock);
+		if (waserror()) {
+			qunlock(&dump.d->qlock);
+			nexterror();
+		}
+	}
+	n = (*dump.read)(dump.magic, dump.d, dump.line, tmp, READSTR);
+	if (n) {
+		dump.line++;
+		n = readstr(0, buf, buflen, tmp);
+	}
+	if (dump.d) {
+		poperror();
+		qunlock(&dump.d->qlock);
+	}
+	logfsfreemem(tmp);
+	poperror();
+	qunlock(&dump.qlock);
+	return n;
+}
+
+static void
+devlogfsserverlogsweep(Devlogfs *d, int justone)
+{
+	int didsomething;
+	if (d->state < NeedVersion)
+		error("not mounted");
+	qlock(&d->qlock);
+	if (waserror()) {
+		qunlock(&d->qlock);
+		nexterror();
+	}
+	errorany(logfsserverlogsweep(d->server, justone, &didsomething));
+	poperror();
+	qunlock(&d->qlock);
+}
+
+static void
+devlogfsserversync(Devlogfs *d)
+{
+	if (d->state < NeedVersion)
+		return;
+	qlock(&d->qlock);
+	if (waserror()) {
+		qunlock(&d->qlock);
+		nexterror();
+	}
+	errorany(logfsserverflush(d->server));
+	poperror();
+	qunlock(&d->qlock);
+}
+
+static void
+lfssrvwrite(Devlogfs *d, void *buf, long n)
+{
+	volatile int locked = 0;
+
+	qlock(&d->wlock);
+	if(waserror()){
+		qunlock(&d->wlock);
+		nexterror();
+	}
+	if (d->state == Hungup)
+		error(Ehungup);
+	Sleep(&d->writerendez, writeok, d);
+	if (convM2S(buf, n, &d->in) != n) {
+		/*
+		 * someone is writing drivel; have nothing to do with them anymore
+		 * most common cause; trying to mount authenticated
+		 */
+		d->state = Hungup;
+		error(Ehungup);
+	}
+	d->out.tag = d->in.tag;
+	d->out.fid = d->in.fid;
+	d->out.type = d->in.type + 1;
+	if (waserror()) {
+		if (locked)
+			qunlock(&d->qlock);
+		rerror(d, up->env->errstr);
+		goto Replied;
+	}
+	if (d->in.type != Tversion && d->in.type != Tattach) {
+		if (d->state != Attached)
+			error("must be attached");
+		qlock(&d->qlock);
+		locked = 1;
+	}
+	switch (d->in.type) {
+	case Tauth:
+		error("no authentication needed");
+	case Tversion: {
+		char *rversion;
+		if (d->state != NeedVersion)
+			error("unexpected Tversion");
+		 if (d->in.tag != NOTAG)
+			error("protocol botch");
+		/*
+		 * check the version string
+		 */
+		if (strcmp(d->in.version, devlogfs9pversion) != 0)
+			rversion = "unknown";
+		else
+			rversion = devlogfs9pversion;
+		/*
+		 * allocate the reply buffer
+		 */
+		d->readbufsize = d->in.msize;
+		if (d->readbufsize > MAXMSIZE)
+			d->readbufsize = MAXMSIZE;
+		d->readbuf = emalloc(d->readbufsize);
+		/*
+		 * compose the Rversion
+		 */
+		d->out.msize = d->readbufsize;
+		d->out.version = rversion;
+		d->state = NeedAttach;
+		break;
+	}
+	case Tattach:
+		if (d->state != NeedAttach)
+			error("unexpected attach");
+		if (d->in.afid != NOFID)
+			error("unexpected afid");
+		errorany(logfsserverattach(d->server, d->in.fid, d->in.uname, &d->out.qid));
+		d->state = Attached;
+		break;
+	case Tclunk:
+		errorany(logfsserverclunk(d->server, d->in.fid));
+		break;
+	case Tcreate:
+		errorany(logfsservercreate(d->server, d->in.fid, d->in.name, d->in.perm, d->in.mode, &d->out.qid));
+		d->out.iounit = d->readbufsize - 11;
+		break;
+	case Tflush:
+		break;
+	case Topen:
+		errorany(logfsserveropen(d->server, d->in.fid, d->in.mode, &d->out.qid));
+		d->out.iounit = d->readbufsize - 11;
+		break;
+	case Tread:
+		d->out.data = (char *)d->readbuf + 11;
+		/* TODO - avoid memmove */
+		errorany(logfsserverread(d->server, d->in.fid, d->in.offset, d->in.count, (uchar *)d->out.data,
+			d->readbufsize - 11, &d->out.count));
+		break;
+	case Tremove:
+		errorany(logfsserverremove(d->server, d->in.fid));
+		break;
+	case Tstat:
+		d->out.stat = d->readbuf + 9;
+		/* TODO - avoid memmove */
+		errorany(logfsserverstat(d->server, d->in.fid, d->out.stat, d->readbufsize - 9, &d->out.nstat));
+//		print("nstat %d\n", d->out.nstat);
+		break;
+	case Twalk:
+		errorany(logfsserverwalk(d->server, d->in.fid, d->in.newfid,
+			d->in.nwname, d->in.wname, &d->out.nwqid, d->out.wqid));
+		break;
+	case Twrite:
+		errorany(logfsserverwrite(d->server, d->in.fid, d->in.offset, d->in.count, (uchar *)d->in.data,
+			&d->out.count));
+		break;
+	case Twstat:
+		errorany(logfsserverwstat(d->server, d->in.fid, d->in.stat, d->in.nstat));
+		break;
+	default:
+		print("lfssrvwrite: msg %d unimplemented\n", d->in.type);
+		error("unimplemented");
+	}
+	poperror();
+	if (locked)
+		qunlock(&d->qlock);
+	reply(d);
+Replied:
+	poperror();
+	qunlock(&d->wlock);
+}
+
+static long
+devlogfsread(Chan *c, void *buf, long n, vlong off)
+{
+	int instance, qid, qt;
+
+	SPLITPATH(c->qid.path, c->qid.type, instance, qid, qt);
+	USED(instance);
+#ifdef CALLTRACE
+	print("devlogfsread(c = 0x%.8lux, buf = 0x%.8lux, n = %ld, instance = %d, qid = %d, qt = %d) - start\n",
+		(ulong)c, (ulong)buf, n, instance, qid, qt);
+#endif
+	if(qt & QTDIR) {
+#ifdef CALLTRACE
+		print("devlogfsread(c = 0x%.8lux, buf = 0x%.8lux, n = %ld, instance = %d, qid = %d, qt = %d) - calling devdirread\n",
+			(ulong)c, (ulong)buf, n, instance, qid, qt);
+#endif
+		return devdirread(c, buf, n, 0, 0, devlogfsgen);
+	}
+
+	if(DATAQID(qid, qt)) {
+		if (qid == Qfsboot) {
+			Devlogfs *l = c->aux;
+			qlock(&l->bootqlock);
+			if (waserror()) {
+				qunlock(&l->bootqlock);
+				nexterror();
+			}
+			smartio((SMARTIOFN *)logfsbootio, l->lb, buf, n, off, logfsbootgetiosize(l->lb), 0);
+			poperror();
+			qunlock(&l->bootqlock);
+			return n;
+		}
+		else if (qid == Qfs) {
+			Devlogfs *d = c->aux;
+			return lfsrvread(d, buf, n);
+		}
+		error(Eio);
+	}
+
+	if (qid == Qusers) {
+		long nr;
+		errorany(logfsisusersread(is, buf, n, (ulong)off, &nr));
+		return nr;
+	}
+	else if (qid == Qdump)
+		return devlogfsdumpread(buf, n);
+
+	if (qid != Qctl)
+		error(Egreg);
+
+	return 0;
+}
+
+enum {
+	CMconfig,
+	CMformat,
+	CMopen,
+	CMsweep,
+	CMtrace,
+	CMunconfig,
+	CMextent,
+	CMsweepone,
+	CMtest,
+	CMleakaudit,
+	CMsync
+};
+
+static Cmdtab fscmds[] = {
+	{CMconfig, "config", 2},
+	{CMformat, "format", 2},
+	{CMopen, "open", 0},
+	{CMsweep, "sweep", 1},
+	{CMsweepone, "sweepone", 1},
+	{CMtrace, "trace", 0},
+	{CMunconfig, "unconfig", 1},
+	{CMextent, "extent", 0},
+	{CMtest, "test", 0},
+	{CMleakaudit, "leakaudit", 1},
+	{CMsync, "sync", 1},
+};
+
+static long
+devlogfswrite(Chan *c, void *buf, long n, vlong off)
+{
+	int instance, qid, qt, i;
+	Cmdbuf *cmd;
+	Cmdtab *ct;
+
+	if(n <= 0)
+		return 0;
+	SPLITPATH(c->qid.path, c->qid.type, instance, qid, qt);
+#ifdef CALLTRACE
+	print("devlogfswrite(c = 0x%.8lux, buf = 0x%.8lux, n = %ld, instance = %d, qid = %d, qt = %d) - start\n",
+		(ulong)c, (ulong)buf, n, instance, qid, qt);
+#endif
+	USED(instance);
+	if(DATAQID(qid, qt)){
+		if (qid == Qfsboot) {
+			Devlogfs *l = c->aux;
+			qlock(&l->bootqlock);
+			if (waserror()) {
+				qunlock(&l->bootqlock);
+				nexterror();
+			}
+			smartio((SMARTIOFN *)logfsbootio, l->lb, buf, n, off, logfsbootgetiosize(l->lb), 1);
+			poperror();
+			qunlock(&l->bootqlock);
+			return n;
+		}
+		else if (qid == Qfs) {
+			Devlogfs *d = c->aux;
+			lfssrvwrite(d, buf, n);
+			return n;
+		}
+		error(Eio);
+	}
+	else if (qid == Qctl) {
+		Devlogfs *l = nil;
+		int a;
+
+		cmd = parsecmd(buf, n);
+		if(waserror()){
+			free(cmd);
+			nexterror();
+		}
+		i = cmd->nf;
+		if(0){print("i=%d", i); for(i=0; i<cmd->nf; i++)print(" %q", cmd->f[i]); print("\n");}
+		if (i <= 0)
+			error(Ebadarg);
+		if (i == 3 && strcmp(cmd->f[0], "uname") == 0) {
+			switch (cmd->f[2][0]) {
+			default:
+				errorany(logfsisgroupcreate(is, cmd->f[1], cmd->f[2]));
+				break;
+			case ':':
+				errorany(logfsisgroupcreate(is, cmd->f[1], cmd->f[2] + 1));
+				break;
+			case '%':
+				errorany(logfsisgrouprename(is, cmd->f[1], cmd->f[2] + 1));
+				break;
+			case '=':
+				errorany(logfsisgroupsetleader(is, cmd->f[1], cmd->f[2] + 1));
+				break;
+			case '+':
+				errorany(logfsisgroupaddmember(is, cmd->f[1], cmd->f[2] + 1));
+				break;
+			case '-':
+				errorany(logfsisgroupremovemember(is, cmd->f[1], cmd->f[2] + 1));
+				break;
+			}
+			i = 0;
+		}
+		if (i == 4 && strcmp(cmd->f[0], "fsys") == 0 && strcmp(cmd->f[2], "config") == 0) {
+			l = devlogfsconfig(cmd->f[1], cmd->f[3]);
+			i = 0;
+		}
+		else if (i >= 2 && strcmp(cmd->f[0], "fsys") == 0) {
+			l = devlogfssetdefname(cmd->f[1]);
+			if (l == nil)
+				errorf("file system %q not configured", cmd->f[1]);
+			i -= 2;
+			cmd->f += 2;
+			cmd->nf = i;
+		}
+		if (i != 0) {
+			ct = lookupcmd(cmd, fscmds, nelem(fscmds));
+			if (l == nil)
+				l = devlogfssetdefname(nil);
+			if(l == nil && ct->index != CMleakaudit)
+				error("file system not configured");
+			switch(ct->index){
+			case CMopen:
+				for (a = 1; a < i; a++)
+					if (cmd->f[a][0] == '-')
+						switch (cmd->f[a][1]) {
+						case 'P':
+							l->openflags |= LogfsOpenFlagNoPerm;
+							break;
+						case 'W':
+							l->openflags |= LogfsOpenFlagWstatAllow;
+							break;
+						default:
+							error(Ebadarg);
+						}
+				devlogfsllopen(l);
+				break;
+			case CMformat:
+				devlogfsllformat(l, strtol(cmd->f[1], nil, 0));
+				break;
+			case CMsweep:
+				devlogfsserverlogsweep(l, 0);
+				break;
+			case CMsweepone:
+				devlogfsserverlogsweep(l, 1);
+				break;
+			case CMtrace:
+				l->logfstrace = i > 1 ? strtol(cmd->f[1], nil, 0) : 0;
+				if (l->server)
+					logfsservertrace(l->server, l->logfstrace);
+				if (l->lb)
+					logfsboottrace(l->lb, l->logfstrace);
+				break;
+			case CMunconfig:
+				if (l->ref.ref > 0)
+					error(Einuse);
+				devlogfsunconfig(l);
+				break;
+			case CMextent:
+				if(i < 2)
+					error(Ebadarg);
+				devlogfsdumpinit(l, extentdumpinit, extentdumpread, i - 1, cmd->f + 1);
+				break;
+			case CMtest:
+				if(i < 2)
+					error(Ebadarg);
+				errorany(logfsservertestcmd(l->server, i - 1, cmd->f + 1));
+				break;
+			case CMleakaudit:
+#ifdef LEAKHUNT
+				leakaudit();
+#endif
+				break;
+			case CMsync:
+				devlogfsserversync(l);
+				break;
+			default:
+				error(Ebadarg);
+			}
+		}
+		poperror();
+		free(cmd);
+		return n;
+	}
+	error(Egreg);
+	return 0;		/* not reached */
+}
+
+static void
+devlogfsfree(Devlogfs *devlogfs)
+{
+	if (devlogfs != nil) {
+		int i;
+		logfsfreemem(devlogfs->device);
+		logfsfreemem(devlogfs->name);
+		for (i = 0; i < Qend - Qfs; i++)
+			logfsfreemem(devlogfs->filename[i]);
+		cclose(devlogfs->flash);
+		cclose(devlogfs->flashctl);
+		qlock(&devlogfs->qlock);
+		logfsserverfree(&devlogfs->server);
+		logfsbootfree(devlogfs->lb);
+		if (devlogfs->ll)
+			(*devlogfs->ll->free)(devlogfs->ll);
+		logfsfreemem(devlogfs->readbuf);
+		qunlock(&devlogfs->qlock);
+		logfsfreemem(devlogfs);
+	}
+}
+
+#ifdef EMU
+ulong
+logfsnow(void)
+{
+	extern vlong timeoffset;
+	return (timeoffset + osusectime()) / 1000000;
+}
+#endif
+
+Dev logfsdevtab = {
+	0x29f,
+//	L'ʟ',
+	"logfs",
+
+#ifndef EMU
+	devreset,
+#endif
+	devinit,
+#ifndef EMU
+	devshutdown,
+#endif
+	devlogfsattach,
+	devlogfswalk,
+	devlogfsstat,
+	devlogfsopen,
+	devcreate,
+	devlogfsclose,
+	devlogfsread,
+	devbread,
+	devlogfswrite,
+	devbwrite,
+	devremove,
+	devwstat,
+};
--- /dev/null
+++ b/os/port/devloopback.c
@@ -1,0 +1,743 @@
+#include	"u.h"
+#include	"../port/lib.h"
+#include	"mem.h"
+#include	"dat.h"
+#include	"fns.h"
+#include	"../port/error.h"
+
+typedef struct Link	Link;
+typedef struct Loop	Loop;
+
+struct Link
+{
+	Lock;
+
+	int	ref;
+
+	long	packets;	/* total number of packets sent */
+	long	bytes;		/* total number of bytes sent */
+	int	indrop;		/* enable dropping on iq overflow */
+	long	soverflows;	/* packets dropped because iq overflowed */
+	long	droprate;	/* drop 1/droprate packets in tq */
+	long	drops;		/* packets deliberately dropped */
+
+	vlong	delay0ns;	/* nanosec of delay in the link */
+	long	delaynns;	/* nanosec of delay per byte */
+
+	Block	*tq;		/* transmission queue */
+	Block	*tqtail;
+	vlong	tout;		/* time the last packet in tq is really out */
+	vlong	tin;		/* time the head packet in tq enters the remote side  */
+
+	long	limit;		/* queue buffering limit */
+	Queue	*oq;		/* output queue from other side & packets in the link */
+	Queue	*iq;
+
+	Timer	ci;		/* time to move packets from  next packet from oq */
+};
+
+struct Loop
+{
+	QLock;
+	int	ref;
+	int	minmtu;		/* smallest block transmittable */
+	Loop	*next;
+	ulong	path;
+	Link	link[2];
+};
+
+static struct
+{
+	Lock;
+	ulong	path;
+} loopbackalloc;
+
+enum
+{
+	Qtopdir=	1,		/* top level directory */
+
+	Qloopdir,			/* loopback* directory */
+
+	Qportdir,			/* directory each end of the loop */
+	Qctl,
+	Qstatus,
+	Qstats,
+	Qdata,
+
+	MaxQ,
+
+	Nloopbacks	= 5,
+
+	Statelen	= 23*1024,	/* status buffer size */
+
+	Tmsize		= 8,
+	Delayn 		= 10000,	/* default delays in ns */
+	Delay0 		= 2500000,
+
+	Loopqlim	= 32*1024,	/* default size of queues */
+};
+
+static Dirtab loopportdir[] =
+{
+	"ctl",		{Qctl},		0,			0222,
+	"status",	{Qstatus},	0,			0444,
+	"stats",	{Qstats},	0,			0444,
+	"data",		{Qdata},	0,			0666,
+};
+static Dirtab loopdirs[MaxQ];
+
+static Loop	loopbacks[Nloopbacks];
+
+#define TYPE(x) 	(((ulong)(x))&0xff)
+#define ID(x) 		(((ulong)(x))>>8)
+#define QID(x,y) 	((((ulong)(x))<<8)|((ulong)(y)))
+
+static void	looper(Loop *lb);
+static long	loopoput(Loop *lb, Link *link, Block *bp);
+static void	ptime(uchar *p, vlong t);
+static vlong	gtime(uchar *p);
+static void	closelink(Link *link, int dofree);
+static void	pushlink(Link *link, vlong now);
+static void	freelb(Loop *lb);
+static void	linkintr(Ureg*, Timer *ci);
+
+static void
+loopbackinit(void)
+{
+	int i;
+
+	for(i = 0; i < Nloopbacks; i++)
+		loopbacks[i].path = i;
+
+	/* invert directory tables for non-directory entries */
+	for(i=0; i<nelem(loopportdir); i++)
+		loopdirs[loopportdir[i].qid.path] = loopportdir[i];
+}
+
+static Chan*
+loopbackattach(char *spec)
+{
+	Loop *volatile lb;
+	Queue *q;
+	Chan *c;
+	int chan;
+	int dev;
+
+	dev = 0;
+	if(spec != nil){
+		dev = atoi(spec);
+		if(dev >= Nloopbacks)
+			error(Ebadspec);
+	}
+
+	c = devattach('X', spec);
+	lb = &loopbacks[dev];
+
+	qlock(lb);
+	if(waserror()){
+		lb->ref--;
+		qunlock(lb);
+		nexterror();
+	}
+
+	lb->ref++;
+	if(lb->ref == 1){
+		for(chan = 0; chan < 2; chan++){
+			lb->link[chan].ci.tmode = Tabsolute;
+			lb->link[chan].ci.ta = &lb->link[chan];
+			lb->link[chan].ci.tf = linkintr;
+			lb->link[chan].limit = Loopqlim;
+			q = qopen(lb->link[chan].limit, 0, 0, 0);
+			lb->link[chan].iq = q;
+			if(q == nil){
+				freelb(lb);
+				exhausted("memory");
+			}
+			q = qopen(lb->link[chan].limit, 0, 0, 0);
+			lb->link[chan].oq = q;
+			if(q == nil){
+				freelb(lb);
+				exhausted("memory");
+			}
+			lb->link[chan].indrop = 1;
+
+			lb->link[chan].delaynns = Delayn;
+			lb->link[chan].delay0ns = Delay0;
+		}
+	}
+	poperror();
+	qunlock(lb);
+
+	mkqid(&c->qid, QID(0, Qtopdir), 0, QTDIR);
+	c->aux = lb;
+	c->dev = dev;
+	return c;
+}
+
+static int
+loopbackgen(Chan *c, char*, Dirtab*, int, int i, Dir *dp)
+{
+	Dirtab *tab;
+	int len, type;
+	Qid qid;
+
+	type = TYPE(c->qid.path);
+	if(i == DEVDOTDOT){
+		switch(type){
+		case Qtopdir:
+		case Qloopdir:
+			snprint(up->genbuf, sizeof(up->genbuf), "#X%ld", c->dev);
+			mkqid(&qid, QID(0, Qtopdir), 0, QTDIR);
+			devdir(c, qid, up->genbuf, 0, eve, 0555, dp);
+			break;
+		case Qportdir:
+			snprint(up->genbuf, sizeof(up->genbuf), "loopback%ld", c->dev);
+			mkqid(&qid, QID(0, Qloopdir), 0, QTDIR);
+			devdir(c, qid, up->genbuf, 0, eve, 0555, dp);
+			break;
+		default:
+			panic("loopbackgen %llux", c->qid.path);
+		}
+		return 1;
+	}
+
+	switch(type){
+	case Qtopdir:
+		if(i != 0)
+			return -1;
+		snprint(up->genbuf, sizeof(up->genbuf), "loopback%ld", c->dev);
+		mkqid(&qid, QID(0, Qloopdir), 0, QTDIR);
+		devdir(c, qid, up->genbuf, 0, eve, 0555, dp);
+		return 1;
+	case Qloopdir:
+		if(i >= 2)
+			return -1;
+		snprint(up->genbuf, sizeof(up->genbuf), "%d", i);
+		mkqid(&qid, QID(i, QID(0, Qportdir)), 0, QTDIR);
+		devdir(c, qid, up->genbuf, 0, eve, 0555, dp);
+		return 1;
+	case Qportdir:
+		if(i >= nelem(loopportdir))
+			return -1;
+		tab = &loopportdir[i];
+		mkqid(&qid, QID(ID(c->qid.path), tab->qid.path), 0, QTFILE);
+		devdir(c, qid, tab->name, tab->length, eve, tab->perm, dp);
+		return 1;
+	default:
+		/* non directory entries end up here; must be in lowest level */
+		if(c->qid.type & QTDIR)
+			panic("loopbackgen: unexpected directory");	
+		if(i != 0)
+			return -1;
+		tab = &loopdirs[type];
+		if(tab == nil)
+			panic("loopbackgen: unknown type: %d", type);
+		len = tab->length;
+		devdir(c, c->qid, tab->name, len, eve, tab->perm, dp);
+		return 1;
+	}
+}
+
+
+static Walkqid*
+loopbackwalk(Chan *c, Chan *nc, char **name, int nname)
+{
+	Walkqid *wq;
+	Loop *lb;
+
+	wq = devwalk(c, nc, name, nname, nil, 0, loopbackgen);
+	if(wq != nil && wq->clone != nil && wq->clone != c){
+		lb = c->aux;
+		qlock(lb);
+		lb->ref++;
+		if((c->flag & COPEN) && TYPE(c->qid.path) == Qdata)
+			lb->link[ID(c->qid.path)].ref++;
+		qunlock(lb);
+	}
+	return wq;
+}
+
+static int
+loopbackstat(Chan *c, uchar *db, int n)
+{
+	return devstat(c, db, n, nil, 0, loopbackgen);
+}
+
+/*
+ *  if the stream doesn't exist, create it
+ */
+static Chan*
+loopbackopen(Chan *c, int omode)
+{
+	Loop *lb;
+
+	if(c->qid.type & QTDIR){
+		if(omode != OREAD)
+			error(Ebadarg);
+		c->mode = omode;
+		c->flag |= COPEN;
+		c->offset = 0;
+		return c;
+	}
+
+	lb = c->aux;
+	qlock(lb);
+	if(TYPE(c->qid.path) == Qdata){
+		if(lb->link[ID(c->qid.path)].ref){
+			qunlock(lb);
+			error(Einuse);
+		}
+		lb->link[ID(c->qid.path)].ref++;
+	}
+	qunlock(lb);
+
+	c->mode = openmode(omode);
+	c->flag |= COPEN;
+	c->offset = 0;
+	c->iounit = qiomaxatomic;
+	return c;
+}
+
+static void
+loopbackclose(Chan *c)
+{
+	Loop *lb;
+	int ref, chan;
+
+	lb = c->aux;
+
+	qlock(lb);
+
+	/*
+	 * closing either side hangs up the stream
+	 */
+	if((c->flag & COPEN) && TYPE(c->qid.path) == Qdata){
+		chan = ID(c->qid.path);
+		if(--lb->link[chan].ref == 0){
+			qhangup(lb->link[chan ^ 1].oq, nil);
+			looper(lb);
+		}
+	}
+
+
+	/*
+	 *  if both sides are closed, they are reusable
+	 */
+	if(lb->link[0].ref == 0 && lb->link[1].ref == 0){
+		for(chan = 0; chan < 2; chan++){
+			closelink(&lb->link[chan], 0);
+			qreopen(lb->link[chan].iq);
+			qreopen(lb->link[chan].oq);
+			qsetlimit(lb->link[chan].oq, lb->link[chan].limit);
+			qsetlimit(lb->link[chan].iq, lb->link[chan].limit);
+		}
+	}
+	ref = --lb->ref;
+	if(ref == 0)
+		freelb(lb);
+	qunlock(lb);
+}
+
+static void
+freelb(Loop *lb)
+{
+	int chan;
+
+	for(chan = 0; chan < 2; chan++)
+		closelink(&lb->link[chan], 1);
+}
+
+/*
+ * called with the Loop qlocked,
+ * so only pushlink can mess with the queues
+ */
+static void
+closelink(Link *link, int dofree)
+{
+	Queue *iq, *oq;
+	Block *bp;
+
+	ilock(link);
+	iq = link->iq;
+	oq = link->oq;
+	bp = link->tq;
+	link->tq = nil;
+	link->tqtail = nil;
+	link->tout = 0;
+	link->tin = 0;
+	timerdel(&link->ci);
+	iunlock(link);
+	if(iq != nil){
+		qclose(iq);
+		if(dofree){
+			ilock(link);
+			free(iq);
+			link->iq = nil;
+			iunlock(link);
+		}
+	}
+	if(oq != nil){
+		qclose(oq);
+		if(dofree){
+			ilock(link);
+			free(oq);
+			link->oq = nil;
+			iunlock(link);
+		}
+	}
+	freeblist(bp);
+}
+
+static long
+loopbackread(Chan *c, void *va, long n, vlong offset)
+{
+	Loop *lb;
+	Link *link;
+	char *buf;
+	long rv;
+
+	lb = c->aux;
+	switch(TYPE(c->qid.path)){
+	default:
+		error(Eperm);
+		return -1;	/* not reached */
+	case Qtopdir:
+	case Qloopdir:
+	case Qportdir:
+		return devdirread(c, va, n, nil, 0, loopbackgen);
+	case Qdata:
+		return qread(lb->link[ID(c->qid.path)].iq, va, n);
+	case Qstatus:
+		link = &lb->link[ID(c->qid.path)];
+		buf = smalloc(Statelen);
+		rv = snprint(buf, Statelen, "delay %lld %ld\n", link->delay0ns, link->delaynns);
+		rv += snprint(buf+rv, Statelen-rv, "limit %ld\n", link->limit);
+		rv += snprint(buf+rv, Statelen-rv, "indrop %d\n", link->indrop);
+		snprint(buf+rv, Statelen-rv, "droprate %ld\n", link->droprate);
+		rv = readstr(offset, va, n, buf);
+		free(buf);
+		break;
+	case Qstats:
+		link = &lb->link[ID(c->qid.path)];
+		buf = smalloc(Statelen);
+		rv = snprint(buf, Statelen, "packets: %ld\n", link->packets);
+		rv += snprint(buf+rv, Statelen-rv, "bytes: %ld\n", link->bytes);
+		rv += snprint(buf+rv, Statelen-rv, "dropped: %ld\n", link->drops);
+		snprint(buf+rv, Statelen-rv, "soft overflows: %ld\n", link->soverflows);
+		rv = readstr(offset, va, n, buf);
+		free(buf);
+		break;
+	}
+	return rv;
+}
+
+static Block*
+loopbackbread(Chan *c, long n, ulong offset)
+{
+	Loop *lb;
+
+	lb = c->aux;
+	if(TYPE(c->qid.path) == Qdata)
+		return qbread(lb->link[ID(c->qid.path)].iq, n);
+
+	return devbread(c, n, offset);
+}
+
+static long
+loopbackbwrite(Chan *c, Block *bp, ulong off)
+{
+	Loop *lb;
+
+	lb = c->aux;
+	if(TYPE(c->qid.path) == Qdata)
+		return loopoput(lb, &lb->link[ID(c->qid.path) ^ 1], bp);
+	return devbwrite(c, bp, off);
+}
+
+static long
+loopbackwrite(Chan *c, void *va, long n, vlong off)
+{
+	Loop *lb;
+	Link *link;
+	Cmdbuf *volatile cb;
+	Block *volatile bp;
+	vlong d0ns;
+	long dnns;
+
+	switch(TYPE(c->qid.path)){
+	case Qdata:
+		bp = allocb(n);
+		if(waserror()){
+			freeb(bp);
+			nexterror();
+		}
+		memmove(bp->wp, va, n);
+		poperror();
+		bp->wp += n;
+		return loopbackbwrite(c, bp, off);
+	case Qctl:
+		lb = c->aux;
+		link = &lb->link[ID(c->qid.path)];
+		cb = parsecmd(va, n);
+		if(waserror()){
+			free(cb);
+			nexterror();
+		}
+		if(cb->nf < 1)
+			error("short control request");
+		if(strcmp(cb->f[0], "delay") == 0){
+			if(cb->nf != 3)
+				error("usage: delay latency bytedelay");
+			d0ns = strtoll(cb->f[1], nil, 10);
+			dnns = strtol(cb->f[2], nil, 10);
+
+			/*
+			 * it takes about 20000 cycles on a pentium ii
+			 * to run pushlink; perhaps this should be accounted.
+			 */
+
+			ilock(link);
+			link->delay0ns = d0ns;
+			link->delaynns = dnns;
+			iunlock(link);
+		}else if(strcmp(cb->f[0], "indrop") == 0){
+			if(cb->nf != 2)
+				error("usage: indrop [01]");
+			ilock(link);
+			link->indrop = strtol(cb->f[1], nil, 0) != 0;
+			iunlock(link);
+		}else if(strcmp(cb->f[0], "droprate") == 0){
+			if(cb->nf != 2)
+				error("usage: droprate ofn");
+			ilock(link);
+			link->droprate = strtol(cb->f[1], nil, 0);
+			iunlock(link);
+		}else if(strcmp(cb->f[0], "limit") == 0){
+			if(cb->nf != 2)
+				error("usage: limit maxqsize");
+			ilock(link);
+			link->limit = strtol(cb->f[1], nil, 0);
+			qsetlimit(link->oq, link->limit);
+			qsetlimit(link->iq, link->limit);
+			iunlock(link);
+		}else if(strcmp(cb->f[0], "reset") == 0){
+			if(cb->nf != 1)
+				error("usage: reset");
+			ilock(link);
+			link->packets = 0;
+			link->bytes = 0;
+			link->indrop = 0;
+			link->soverflows = 0;
+			link->drops = 0;
+			iunlock(link);
+		}else
+			error("unknown control request");
+		poperror();
+		free(cb);
+		break;
+	default:
+		error(Eperm);
+	}
+
+	return n;
+}
+
+static long
+loopoput(Loop *lb, Link *link, Block *volatile bp)
+{
+	long n;
+
+	n = BLEN(bp);
+
+	/* make it a single block with space for the loopback timing header */
+	if(waserror()){
+		freeb(bp);
+		nexterror();
+	}
+	bp = padblock(bp, Tmsize);
+	if(bp->next)
+		bp = concatblock(bp);
+	if(BLEN(bp) < lb->minmtu)
+		bp = adjustblock(bp, lb->minmtu);
+	poperror();
+	ptime(bp->rp, todget(nil));
+
+	link->packets++;
+	link->bytes += n;
+
+	qbwrite(link->oq, bp);
+
+	looper(lb);
+	return n;
+}
+
+static void
+looper(Loop *lb)
+{
+	vlong t;
+	int chan;
+
+	t = todget(nil);
+	for(chan = 0; chan < 2; chan++)
+		pushlink(&lb->link[chan], t);
+}
+
+static void
+linkintr(Ureg*, Timer *ci)
+{
+	Link *link;
+
+	link = ci->ta;
+	pushlink(link, ci->tns);
+}
+
+/*
+ * move blocks between queues if they are ready.
+ * schedule an interrupt for the next interesting time.
+ *
+ * must be called with the link ilocked.
+ */
+static void
+pushlink(Link *link, vlong now)
+{
+	Block *bp;
+	vlong tout, tin;
+
+	/*
+	 * put another block in the link queue
+	 */
+	ilock(link);
+	if(link->iq == nil || link->oq == nil){
+		iunlock(link);
+		return;
+
+	}
+	timerdel(&link->ci);
+
+	/*
+	 * put more blocks into the xmit queue
+	 * use the time the last packet was supposed to go out
+	 * as the start time for the next packet, rather than
+	 * the current time.  this more closely models a network
+	 * device which can queue multiple output packets.
+	 */
+	tout = link->tout;
+	if(!tout)
+		tout = now;
+	while(tout <= now){
+		bp = qget(link->oq);
+		if(bp == nil){
+			tout = 0;
+			break;
+		}
+
+		/*
+		 * can't send the packet before it gets queued
+		 */
+		tin = gtime(bp->rp);
+		if(tin > tout)
+			tout = tin;
+		tout = tout + (BLEN(bp) - Tmsize) * link->delaynns;
+
+		/*
+		 * drop packets
+		 */
+		if(link->droprate && nrand(link->droprate) == 0)
+			link->drops++;
+		else{
+			ptime(bp->rp, tout + link->delay0ns);
+			if(link->tq == nil)
+				link->tq = bp;
+			else
+				link->tqtail->next = bp;
+			link->tqtail = bp;
+		}
+	}
+
+	/*
+	 * record the next time a packet can be sent,
+	 * but don't schedule an interrupt if none is waiting
+	 */
+	link->tout = tout;
+	if(!qcanread(link->oq))
+		tout = 0;
+
+	/*
+	 * put more blocks into the receive queue
+	 */
+	tin = 0;
+	while(bp = link->tq){
+		tin = gtime(bp->rp);
+		if(tin > now)
+			break;
+		bp->rp += Tmsize;
+		link->tq = bp->next;
+		bp->next = nil;
+		if(!link->indrop)
+			qpassnolim(link->iq, bp);
+		else if(qpass(link->iq, bp) < 0)
+			link->soverflows++;
+		tin = 0;
+	}
+	if(bp == nil && qisclosed(link->oq) && !qcanread(link->oq) && !qisclosed(link->iq))
+		qhangup(link->iq, nil);
+	link->tin = tin;
+	if(!tin || tin > tout && tout)
+		tin = tout;
+
+	link->ci.tns = tin;
+	if(tin){
+		if(tin < now)
+			panic("loopback unfinished business");
+		timeradd(&link->ci);
+	}
+	iunlock(link);
+}
+
+static void
+ptime(uchar *p, vlong t)
+{
+	ulong tt;
+
+	tt = t >> 32;
+	p[0] = tt >> 24;
+	p[1] = tt >> 16;
+	p[2] = tt >> 8;
+	p[3] = tt;
+	tt = t;
+	p[4] = tt >> 24;
+	p[5] = tt >> 16;
+	p[6] = tt >> 8;
+	p[7] = tt;
+}
+
+static vlong
+gtime(uchar *p)
+{
+	ulong t1, t2;
+
+	t1 = (p[0] << 24) | (p[1] << 16) | (p[2] << 8) | p[3];
+	t2 = (p[4] << 24) | (p[5] << 16) | (p[6] << 8) | p[7];
+	return ((vlong)t1 << 32) | t2;
+}
+
+Dev loopbackdevtab = {
+	'X',
+	"loopback",
+
+	devreset,
+	loopbackinit,
+	devshutdown,
+	loopbackattach,
+	loopbackwalk,
+	loopbackstat,
+	loopbackopen,
+	devcreate,
+	loopbackclose,
+	loopbackread,
+	loopbackbread,
+	loopbackwrite,
+	loopbackbwrite,
+	devremove,
+	devwstat,
+};
--- /dev/null
+++ b/os/port/devmnt.c
@@ -1,0 +1,1204 @@
+#include	"u.h"
+#include	"../port/lib.h"
+#include	"mem.h"
+#include	"dat.h"
+#include	"fns.h"
+#include	"../port/error.h"
+
+/*
+ * References are managed as follows:
+ * The channel to the server - a network connection or pipe - has one
+ * reference for every Chan open on the server.  The server channel has
+ * c->mux set to the Mnt used for muxing control to that server.  Mnts
+ * have no reference count; they go away when c goes away.
+ * Each channel derived from the mount point has mchan set to c,
+ * and increfs/decrefs mchan to manage references on the server
+ * connection.
+ */
+
+#define MAXRPC (IOHDRSZ+8192)
+
+struct Mntrpc
+{
+	Chan*	c;		/* Channel for whom we are working */
+	Mntrpc*	list;		/* Free/pending list */
+	Fcall	request;	/* Outgoing file system protocol message */
+	Fcall 	reply;		/* Incoming reply */
+	Mnt*	m;		/* Mount device during rpc */
+	Rendez	r;		/* Place to hang out */
+	uchar*	rpc;		/* I/O Data buffer */
+	uint		rpclen;	/* len of buffer */
+	Block	*b;		/* reply blocks */
+	char	done;		/* Rpc completed */
+	uvlong	stime;		/* start time for mnt statistics */
+	ulong	reqlen;		/* request length for mnt statistics */
+	ulong	replen;		/* reply length for mnt statistics */
+	Mntrpc*	flushed;	/* message this one flushes */
+};
+
+enum
+{
+	TAGSHIFT = 5,			/* ulong has to be 32 bits */
+	TAGMASK = (1<<TAGSHIFT)-1,
+	NMASK = (64*1024)>>TAGSHIFT,
+};
+
+struct Mntalloc
+{
+	Lock;
+	Mnt*	list;		/* Mount devices in use */
+	Mnt*	mntfree;	/* Free list */
+	Mntrpc*	rpcfree;
+	int	nrpcfree;
+	int	nrpcused;
+	ulong	id;
+	ulong	tagmask[NMASK];
+}mntalloc;
+
+void	mattach(Mnt*, Chan*, char*);
+Mnt*	mntchk(Chan*);
+void	mntdirfix(uchar*, Chan*);
+Mntrpc*	mntflushalloc(Mntrpc*, ulong);
+void	mntflushfree(Mnt*, Mntrpc*);
+void	mntfree(Mntrpc*);
+void	mntgate(Mnt*);
+void	mntpntfree(Mnt*);
+void	mntqrm(Mnt*, Mntrpc*);
+Mntrpc*	mntralloc(Chan*, ulong);
+long	mntrdwr(int, Chan*, void*, long, vlong);
+int	mntrpcread(Mnt*, Mntrpc*);
+void	mountio(Mnt*, Mntrpc*);
+void	mountmux(Mnt*, Mntrpc*);
+void	mountrpc(Mnt*, Mntrpc*);
+int	rpcattn(void*);
+Chan*	mntchan(void);
+
+char	Esbadstat[] = "invalid directory entry received from server";
+char Enoversion[] = "version not established for mount channel";
+
+
+void (*mntstats)(int, Chan*, uvlong, ulong);
+
+static void
+mntreset(void)
+{
+	mntalloc.id = 1;
+	mntalloc.tagmask[0] = 1;			/* don't allow 0 as a tag */
+	mntalloc.tagmask[NMASK-1] = 0x80000000UL;	/* don't allow NOTAG */
+	fmtinstall('F', fcallfmt);
+/*	fmtinstall('D', dirfmt); */
+/*	fmtinstall('M', dirmodefmt);  */
+
+	cinit();
+}
+
+/*
+ * Version is not multiplexed: message sent only once per connection.
+ */
+long
+mntversion(Chan *c, char *version, int msize, int returnlen)
+{
+	Fcall f;
+	uchar *msg;
+	Mnt *m;
+	char *v;
+	long k, l;
+	uvlong oo;
+	char buf[128];
+
+	qlock(&c->umqlock);	/* make sure no one else does this until we've established ourselves */
+	if(waserror()){
+		qunlock(&c->umqlock);
+		nexterror();
+	}
+
+	/* defaults */
+	if(msize == 0)
+		msize = MAXRPC;
+	if(msize > c->iounit && c->iounit != 0)
+		msize = c->iounit;
+	v = version;
+	if(v == nil || v[0] == '\0')
+		v = VERSION9P;
+
+	/* validity */
+	if(msize < 0)
+		error("bad iounit in version call");
+	if(strncmp(v, VERSION9P, strlen(VERSION9P)) != 0)
+		error("bad 9P version specification");
+
+	m = c->mux;
+
+	if(m != nil){
+		qunlock(&c->umqlock);
+		poperror();
+
+		strecpy(buf, buf+sizeof buf, m->version);
+		k = strlen(buf);
+		if(strncmp(buf, v, k) != 0){
+			snprint(buf, sizeof buf, "incompatible 9P versions %s %s", m->version, v);
+			error(buf);
+		}
+		if(returnlen > 0){
+			if(returnlen < k)
+				error(Eshort);
+			memmove(version, buf, k);
+		}
+		return k;
+	}
+
+	f.type = Tversion;
+	f.tag = NOTAG;
+	f.msize = msize;
+	f.version = v;
+	msg = malloc(8192+IOHDRSZ);
+	if(msg == nil)
+		exhausted("version memory");
+	if(waserror()){
+		free(msg);
+		nexterror();
+	}
+	k = convS2M(&f, msg, 8192+IOHDRSZ);
+	if(k == 0)
+		error("bad fversion conversion on send");
+
+	lock(c);
+	oo = c->offset;
+	c->offset += k;
+	unlock(c);
+
+	l = devtab[c->type]->write(c, msg, k, oo);
+
+	if(l < k){
+		lock(c);
+		c->offset -= k - l;
+		unlock(c);
+		error("short write in fversion");
+	}
+
+	/* message sent; receive and decode reply */
+	k = devtab[c->type]->read(c, msg, 8192+IOHDRSZ, c->offset);
+	if(k <= 0)
+		error("EOF receiving fversion reply");
+
+	lock(c);
+	c->offset += k;
+	unlock(c);
+
+	l = convM2S(msg, k, &f);
+	if(l != k)
+		error("bad fversion conversion on reply");
+	if(f.type != Rversion){
+		if(f.type == Rerror)
+			error(f.ename);
+		error("unexpected reply type in fversion");
+	}
+	if(f.msize > msize)
+		error("server tries to increase msize in fversion");
+	if(f.msize<256 || f.msize>1024*1024)
+		error("nonsense value of msize in fversion");
+	if(strncmp(f.version, v, strlen(f.version)) != 0)
+		error("bad 9P version returned from server");
+
+	/* now build Mnt associated with this connection */
+	lock(&mntalloc);
+	m = mntalloc.mntfree;
+	if(m != 0)
+		mntalloc.mntfree = m->list;
+	else {
+		m = malloc(sizeof(Mnt));
+		if(m == 0) {
+			unlock(&mntalloc);
+			exhausted("mount devices");
+		}
+	}
+	m->list = mntalloc.list;
+	mntalloc.list = m;
+	m->version = nil;
+	kstrdup(&m->version, f.version);
+	m->id = mntalloc.id++;
+	m->q = qopen(10*MAXRPC, 0, nil, nil);
+	m->msize = f.msize;
+	unlock(&mntalloc);
+
+	poperror();	/* msg */
+	free(msg);
+
+	lock(m);
+	m->queue = 0;
+	m->rip = 0;
+
+	c->flag |= CMSG;
+	c->mux = m;
+	m->c = c;
+	unlock(m);
+
+	poperror();	/* c */
+	qunlock(&c->umqlock);
+
+	k = strlen(f.version);
+	if(returnlen > 0){
+		if(returnlen < k)
+			error(Eshort);
+		memmove(version, f.version, k);
+	}
+
+	return k;
+}
+
+Chan*
+mntauth(Chan *c, char *spec)
+{
+	Mnt *m;
+	Mntrpc *r;
+
+	m = c->mux;
+
+	if(m == nil){
+		mntversion(c, VERSION9P, MAXRPC, 0);
+		m = c->mux;
+		if(m == nil)
+			error(Enoversion);
+	}
+
+	c = mntchan();
+	if(waserror()) {
+		/* Close must not be called since it will
+		 * call mnt recursively
+		 */
+		chanfree(c);
+		nexterror();
+	}
+
+	r = mntralloc(0, m->msize);
+
+	if(waserror()) {
+		mntfree(r);
+		nexterror();
+	}
+
+	r->request.type = Tauth;
+	r->request.afid = c->fid;
+	r->request.uname = up->env->user;
+	r->request.aname = spec;
+	mountrpc(m, r);
+
+	c->qid = r->reply.aqid;
+	c->mchan = m->c;
+	incref(m->c);
+	c->mqid = c->qid;
+	c->mode = ORDWR;
+
+	poperror();	/* r */
+	mntfree(r);
+
+	poperror();	/* c */
+
+	return c;
+
+}
+
+static Chan*
+mntattach(char *muxattach)
+{
+	Mnt *m;
+	Chan *c;
+	Mntrpc *r;
+	struct bogus{
+		Chan	*chan;
+		Chan	*authchan;
+		char	*spec;
+		int	flags;
+	}bogus;
+
+	bogus = *((struct bogus *)muxattach);
+	c = bogus.chan;
+
+	m = c->mux;
+
+	if(m == nil){
+		mntversion(c, nil, 0, 0);
+		m = c->mux;
+		if(m == nil)
+			error(Enoversion);
+	}
+
+	c = mntchan();
+	if(waserror()) {
+		/* Close must not be called since it will
+		 * call mnt recursively
+		 */
+		chanfree(c);
+		nexterror();
+	}
+
+	r = mntralloc(0, m->msize);
+
+	if(waserror()) {
+		mntfree(r);
+		nexterror();
+	}
+
+	r->request.type = Tattach;
+	r->request.fid = c->fid;
+	if(bogus.authchan == nil)
+		r->request.afid = NOFID;
+	else
+		r->request.afid = bogus.authchan->fid;
+	r->request.uname = up->env->user;
+	r->request.aname = bogus.spec;
+	mountrpc(m, r);
+
+	c->qid = r->reply.qid;
+	c->mchan = m->c;
+	incref(m->c);
+	c->mqid = c->qid;
+
+	poperror();	/* r */
+	mntfree(r);
+
+	poperror();	/* c */
+
+	if(bogus.flags&MCACHE)
+		c->flag |= CCACHE;
+	return c;
+}
+
+Chan*
+mntchan(void)
+{
+	Chan *c;
+
+	c = devattach('M', 0);
+	lock(&mntalloc);
+	c->dev = mntalloc.id++;
+	unlock(&mntalloc);
+
+	if(c->mchan)
+		panic("mntchan non-zero %p", c->mchan);
+	return c;
+}
+
+static Walkqid*
+mntwalk(Chan *c, Chan *nc, char **name, int nname)
+{
+	int i, alloc;
+	Mnt *m;
+	Mntrpc *r;
+	Walkqid *wq;
+
+	if(nc != nil)
+		print("mntwalk: nc != nil\n");
+	if(nname > MAXWELEM)
+		error("devmnt: too many name elements");
+	alloc = 0;
+	wq = smalloc(sizeof(Walkqid)+(nname-1)*sizeof(Qid));
+	if(waserror()){
+		if(alloc && wq->clone!=nil)
+			cclose(wq->clone);
+		free(wq);
+		return nil;
+	}
+
+	alloc = 0;
+	m = mntchk(c);
+	r = mntralloc(c, m->msize);
+	if(nc == nil){
+		nc = devclone(c);
+		/*
+		 * Until the other side accepts this fid, we can't mntclose it.
+		 * Therefore set type to 0 for now; rootclose is known to be safe.
+		 */
+		nc->type = 0;
+		alloc = 1;
+	}
+	wq->clone = nc;
+
+	if(waserror()) {
+		mntfree(r);
+		nexterror();
+	}
+	r->request.type = Twalk;
+	r->request.fid = c->fid;
+	r->request.newfid = nc->fid;
+	r->request.nwname = nname;
+	memmove(r->request.wname, name, nname*sizeof(char*));
+
+	mountrpc(m, r);
+
+	if(r->reply.nwqid > nname)
+		error("too many QIDs returned by walk");
+	if(r->reply.nwqid < nname){
+		if(alloc)
+			cclose(nc);
+		wq->clone = nil;
+		if(r->reply.nwqid == 0){
+			free(wq);
+			wq = nil;
+			goto Return;
+		}
+	}
+
+	/* move new fid onto mnt device and update its qid */
+	if(wq->clone != nil){
+		if(wq->clone != c){
+			wq->clone->type = c->type;
+			wq->clone->mchan = c->mchan;
+			incref(c->mchan);
+		}
+		if(r->reply.nwqid > 0)
+			wq->clone->qid = r->reply.wqid[r->reply.nwqid-1];
+	}
+	wq->nqid = r->reply.nwqid;
+	for(i=0; i<wq->nqid; i++)
+		wq->qid[i] = r->reply.wqid[i];
+
+    Return:
+	poperror();
+	mntfree(r);
+	poperror();
+	return wq;
+}
+
+static int
+mntstat(Chan *c, uchar *dp, int n)
+{
+	Mnt *m;
+	Mntrpc *r;
+
+	if(n < BIT16SZ)
+		error(Eshortstat);
+	m = mntchk(c);
+	r = mntralloc(c, m->msize);
+	if(waserror()) {
+		mntfree(r);
+		nexterror();
+	}
+	r->request.type = Tstat;
+	r->request.fid = c->fid;
+	mountrpc(m, r);
+
+	if(r->reply.nstat > n){
+		/* doesn't fit; just patch the count and return */
+		PBIT16((uchar*)dp, r->reply.nstat);
+		n = BIT16SZ;
+	}else{
+		n = r->reply.nstat;
+		memmove(dp, r->reply.stat, n);
+		validstat(dp, n);
+		mntdirfix(dp, c);
+	}
+	poperror();
+	mntfree(r);
+	return n;
+}
+
+static Chan*
+mntopencreate(int type, Chan *c, char *name, int omode, ulong perm)
+{
+	Mnt *m;
+	Mntrpc *r;
+
+	m = mntchk(c);
+	r = mntralloc(c, m->msize);
+	if(waserror()) {
+		mntfree(r);
+		nexterror();
+	}
+	r->request.type = type;
+	r->request.fid = c->fid;
+	r->request.mode = omode;
+	if(type == Tcreate){
+		r->request.perm = perm;
+		r->request.name = name;
+	}
+	mountrpc(m, r);
+
+	c->qid = r->reply.qid;
+	c->offset = 0;
+	c->mode = openmode(omode);
+	c->iounit = r->reply.iounit;
+	if(c->iounit == 0 || c->iounit > m->msize-IOHDRSZ)
+		c->iounit = m->msize-IOHDRSZ;
+	c->flag |= COPEN;
+	poperror();
+	mntfree(r);
+
+	if(c->flag & CCACHE)
+		copen(c);
+
+	return c;
+}
+
+static Chan*
+mntopen(Chan *c, int omode)
+{
+	return mntopencreate(Topen, c, nil, omode, 0);
+}
+
+static void
+mntcreate(Chan *c, char *name, int omode, ulong perm)
+{
+	mntopencreate(Tcreate, c, name, omode, perm);
+}
+
+static void
+mntclunk(Chan *c, int t)
+{
+	Mnt *m;
+	Mntrpc *r;
+
+	m = mntchk(c);
+	r = mntralloc(c, m->msize);
+	if(waserror()){
+		mntfree(r);
+		nexterror();
+	}
+
+	r->request.type = t;
+	r->request.fid = c->fid;
+	mountrpc(m, r);
+	mntfree(r);
+	poperror();
+}
+
+void
+muxclose(Mnt *m)
+{
+	Mntrpc *q, *r;
+
+	for(q = m->queue; q; q = r) {
+		r = q->list;
+		mntfree(q);
+	}
+	m->id = 0;
+	free(m->version);
+	m->version = nil;
+	mntpntfree(m);
+}
+
+void
+mntpntfree(Mnt *m)
+{
+	Mnt *f, **l;
+	Queue *q;
+
+	lock(&mntalloc);
+	l = &mntalloc.list;
+	for(f = *l; f; f = f->list) {
+		if(f == m) {
+			*l = m->list;
+			break;
+		}
+		l = &f->list;
+	}
+	m->list = mntalloc.mntfree;
+	mntalloc.mntfree = m;
+	q = m->q;
+	unlock(&mntalloc);
+
+	qfree(q);
+}
+
+static void
+mntclose(Chan *c)
+{
+	mntclunk(c, Tclunk);
+}
+
+static void
+mntremove(Chan *c)
+{
+	mntclunk(c, Tremove);
+}
+
+static int
+mntwstat(Chan *c, uchar *dp, int n)
+{
+	Mnt *m;
+	Mntrpc *r;
+
+	m = mntchk(c);
+	r = mntralloc(c, m->msize);
+	if(waserror()) {
+		mntfree(r);
+		nexterror();
+	}
+	r->request.type = Twstat;
+	r->request.fid = c->fid;
+	r->request.nstat = n;
+	r->request.stat = dp;
+	mountrpc(m, r);
+	poperror();
+	mntfree(r);
+	return n;
+}
+
+static long
+mntread(Chan *c, void *buf, long n, vlong off)
+{
+	uchar *p, *e;
+	int nc, cache, isdir, dirlen;
+
+	isdir = 0;
+	cache = c->flag & CCACHE;
+	if(c->qid.type & QTDIR) {
+		cache = 0;
+		isdir = 1;
+	}
+
+	p = buf;
+	if(cache) {
+		nc = cread(c, buf, n, off);
+		if(nc > 0) {
+			n -= nc;
+			if(n == 0)
+				return nc;
+			p += nc;
+			off += nc;
+		}
+		n = mntrdwr(Tread, c, p, n, off);
+		cupdate(c, p, n, off);
+		return n + nc;
+	}
+
+	n = mntrdwr(Tread, c, buf, n, off);
+	if(isdir) {
+		for(e = &p[n]; p+BIT16SZ < e; p += dirlen){
+			dirlen = BIT16SZ+GBIT16(p);
+			if(p+dirlen > e)
+				break;
+			validstat(p, dirlen);
+			mntdirfix(p, c);
+		}
+		if(p != e)
+			error(Esbadstat);
+	}
+	return n;
+}
+
+static long
+mntwrite(Chan *c, void *buf, long n, vlong off)
+{
+	return mntrdwr(Twrite, c, buf, n, off);
+}
+
+long
+mntrdwr(int type, Chan *c, void *buf, long n, vlong off)
+{
+	Mnt *m;
+ 	Mntrpc *r;
+	char *uba;
+	int cache;
+	ulong cnt, nr, nreq;
+
+	m = mntchk(c);
+	uba = buf;
+	cnt = 0;
+	cache = c->flag & CCACHE;
+	if(c->qid.type & QTDIR)
+		cache = 0;
+	for(;;) {
+		r = mntralloc(c, m->msize);
+		if(waserror()) {
+			mntfree(r);
+			nexterror();
+		}
+		r->request.type = type;
+		r->request.fid = c->fid;
+		r->request.offset = off;
+		r->request.data = uba;
+		nr = n;
+		if(nr > m->msize-IOHDRSZ)
+			nr = m->msize-IOHDRSZ;
+		r->request.count = nr;
+		mountrpc(m, r);
+		nreq = r->request.count;
+		nr = r->reply.count;
+		if(nr > nreq)
+			nr = nreq;
+
+		if(type == Tread)
+			r->b = bl2mem((uchar*)uba, r->b, nr);
+		else if(cache)
+			cwrite(c, (uchar*)uba, nr, off);
+
+		poperror();
+		mntfree(r);
+		off += nr;
+		uba += nr;
+		cnt += nr;
+		n -= nr;
+		if(nr != nreq || n == 0 || up->killed)
+			break;
+	}
+	return cnt;
+}
+
+void
+mountrpc(Mnt *m, Mntrpc *r)
+{
+	char *sn, *cn;
+	int t;
+
+	r->reply.tag = 0;
+	r->reply.type = Tmax;	/* can't ever be a valid message type */
+
+	mountio(m, r);
+
+	t = r->reply.type;
+	switch(t) {
+	case Rerror:
+		error(r->reply.ename);
+	case Rflush:
+		error(Eintr);
+	default:
+		if(t == r->request.type+1)
+			break;
+		sn = "?";
+		if(m->c->name != nil)
+			sn = m->c->name->s;
+		cn = "?";
+		if(r->c != nil && r->c->name != nil)
+			cn = r->c->name->s;
+		print("mnt: proc %s %lud: mismatch from %s %s rep 0x%lux tag %d fid %d T%d R%d rp %d\n",
+			up->text, up->pid, sn, cn,
+			r, r->request.tag, r->request.fid, r->request.type,
+			r->reply.type, r->reply.tag);
+		error(Emountrpc);
+	}
+}
+
+void
+mountio(Mnt *m, Mntrpc *r)
+{
+	int n;
+
+	while(waserror()) {
+		if(m->rip == up)
+			mntgate(m);
+		if(strcmp(up->env->errstr, Eintr) != 0){
+			mntflushfree(m, r);
+			nexterror();
+		}
+		r = mntflushalloc(r, m->msize);
+	}
+
+	lock(m);
+	r->m = m;
+	r->list = m->queue;
+	m->queue = r;
+	unlock(m);
+
+	/* Transmit a file system rpc */
+	if(m->msize == 0)
+		panic("msize");
+	n = convS2M(&r->request, r->rpc, m->msize);
+	if(n < 0)
+		panic("bad message type in mountio");
+	if(devtab[m->c->type]->write(m->c, r->rpc, n, 0) != n)
+		error(Emountrpc);
+/*	r->stime = fastticks(nil); */
+	r->reqlen = n;
+
+	/* Gate readers onto the mount point one at a time */
+	for(;;) {
+		lock(m);
+		if(m->rip == 0)
+			break;
+		unlock(m);
+		sleep(&r->r, rpcattn, r);
+		if(r->done){
+			poperror();
+			mntflushfree(m, r);
+			return;
+		}
+	}
+	m->rip = up;
+	unlock(m);
+	while(r->done == 0) {
+		if(mntrpcread(m, r) < 0)
+			error(Emountrpc);
+		mountmux(m, r);
+	}
+	mntgate(m);
+	poperror();
+	mntflushfree(m, r);
+}
+
+static int
+doread(Mnt *m, int len)
+{
+	Block *b;
+
+	while(qlen(m->q) < len){
+		b = devtab[m->c->type]->bread(m->c, m->msize, 0);
+		if(b == nil)
+			return -1;
+		if(blocklen(b) == 0){
+			freeblist(b);
+			return -1;
+		}
+		qaddlist(m->q, b);
+	}
+	return 0;
+}
+
+int
+mntrpcread(Mnt *m, Mntrpc *r)
+{
+	int i, t, len, hlen;
+	Block *b, **l, *nb;
+
+	r->reply.type = 0;
+	r->reply.tag = 0;
+
+	/* read at least length, type, and tag and pullup to a single block */
+	if(doread(m, BIT32SZ+BIT8SZ+BIT16SZ) < 0)
+		return -1;
+	nb = pullupqueue(m->q, BIT32SZ+BIT8SZ+BIT16SZ);
+
+	/* read in the rest of the message, avoid ridiculous (for now) message sizes */
+	len = GBIT32(nb->rp);
+	if(len > m->msize){
+		qdiscard(m->q, qlen(m->q));
+		return -1;
+	}
+	if(doread(m, len) < 0)
+		return -1;
+
+	/* pullup the header (i.e. everything except data) */
+	t = nb->rp[BIT32SZ];
+	switch(t){
+	case Rread:
+		hlen = BIT32SZ+BIT8SZ+BIT16SZ+BIT32SZ;
+		break;
+	default:
+		hlen = len;
+		break;
+	}
+	nb = pullupqueue(m->q, hlen);
+
+	if(convM2S(nb->rp, len, &r->reply) <= 0){
+		/* bad message, dump it */
+		print("mntrpcread: convM2S failed\n");
+		qdiscard(m->q, len);
+		return -1;
+	}
+
+	/* hang the data off of the fcall struct */
+	l = &r->b;
+	*l = nil;
+	do {
+		b = qremove(m->q);
+		if(hlen > 0){
+			b->rp += hlen;
+			len -= hlen;
+			hlen = 0;
+		}
+		i = BLEN(b);
+		if(i <= len){
+			len -= i;
+			*l = b;
+			l = &(b->next);
+		} else {
+			/* split block and put unused bit back */
+			nb = allocb(i-len);
+			memmove(nb->wp, b->rp+len, i-len);
+			b->wp = b->rp+len;
+			nb->wp += i-len;
+			qputback(m->q, nb);
+			*l = b;
+			return 0;
+		}
+	}while(len > 0);
+
+	return 0;
+}
+
+void
+mntgate(Mnt *m)
+{
+	Mntrpc *q;
+
+	lock(m);
+	m->rip = 0;
+	for(q = m->queue; q; q = q->list) {
+		if(q->done == 0)
+		if(wakeup(&q->r))
+			break;
+	}
+	unlock(m);
+}
+
+void
+mountmux(Mnt *m, Mntrpc *r)
+{
+	Mntrpc **l, *q;
+
+	lock(m);
+	l = &m->queue;
+	for(q = *l; q; q = q->list) {
+		/* look for a reply to a message */
+		if(q->request.tag == r->reply.tag) {
+			*l = q->list;
+			if(q != r) {
+				/*
+				 * Completed someone else.
+				 * Trade pointers to receive buffer.
+				 */
+				q->reply = r->reply;
+				q->b = r->b;
+				r->b = nil;
+			}
+			q->done = 1;
+			unlock(m);
+			if(mntstats != nil)
+				(*mntstats)(q->request.type,
+					m->c, q->stime,
+					q->reqlen + r->replen);
+			if(q != r)
+				wakeup(&q->r);
+			return;
+		}
+		l = &q->list;
+	}
+	unlock(m);
+	print("unexpected reply tag %ud; type %d\n", r->reply.tag, r->reply.type);
+}
+
+/*
+ * Create a new flush request and chain the previous
+ * requests from it
+ */
+Mntrpc*
+mntflushalloc(Mntrpc *r, ulong iounit)
+{
+	Mntrpc *fr;
+
+	fr = mntralloc(0, iounit);
+
+	fr->request.type = Tflush;
+	if(r->request.type == Tflush)
+		fr->request.oldtag = r->request.oldtag;
+	else
+		fr->request.oldtag = r->request.tag;
+	fr->flushed = r;
+
+	return fr;
+}
+
+/*
+ *  Free a chain of flushes.  Remove each unanswered
+ *  flush and the original message from the unanswered
+ *  request queue.  Mark the original message as done
+ *  and if it hasn't been answered set the reply to to
+ *  Rflush.
+ */
+void
+mntflushfree(Mnt *m, Mntrpc *r)
+{
+	Mntrpc *fr;
+
+	while(r){
+		fr = r->flushed;
+		if(!r->done){
+			r->reply.type = Rflush;
+			mntqrm(m, r);
+		}
+		if(fr)
+			mntfree(r);
+		r = fr;
+	}
+}
+
+static int
+alloctag(void)
+{
+	int i, j;
+	ulong v;
+
+	for(i = 0; i < NMASK; i++){
+		v = mntalloc.tagmask[i];
+		if(v == ~0UL)
+			continue;
+		for(j = 0; j < 1<<TAGSHIFT; j++)
+			if((v & (1<<j)) == 0){
+				mntalloc.tagmask[i] |= 1<<j;
+				return (i<<TAGSHIFT) + j;
+			}
+	}
+	/* panic("no devmnt tags left"); */
+	return NOTAG;
+}
+
+static void
+freetag(int t)
+{
+	mntalloc.tagmask[t>>TAGSHIFT] &= ~(1<<(t&TAGMASK));
+}
+
+Mntrpc*
+mntralloc(Chan *c, ulong msize)
+{
+	Mntrpc *new;
+
+	lock(&mntalloc);
+	new = mntalloc.rpcfree;
+	if(new == nil){
+		new = malloc(sizeof(Mntrpc));
+		if(new == nil) {
+			unlock(&mntalloc);
+			exhausted("mount rpc header");
+		}
+		/*
+		 * The header is split from the data buffer as
+		 * mountmux may swap the buffer with another header.
+		 */
+		new->rpc = mallocz(msize, 0);
+		if(new->rpc == nil){
+			free(new);
+			unlock(&mntalloc);
+			exhausted("mount rpc buffer");
+		}
+		new->rpclen = msize;
+		new->request.tag = alloctag();
+		if(new->request.tag == NOTAG){
+			free(new);
+			unlock(&mntalloc);
+			exhausted("rpc tags");
+		}
+	}
+	else {
+		mntalloc.rpcfree = new->list;
+		mntalloc.nrpcfree--;
+		if(new->rpclen < msize){
+			free(new->rpc);
+			new->rpc = mallocz(msize, 0);
+			if(new->rpc == nil){
+				free(new);
+				mntalloc.nrpcused--;
+				unlock(&mntalloc);
+				exhausted("mount rpc buffer");
+			}
+			new->rpclen = msize;
+		}
+	}
+	mntalloc.nrpcused++;
+	unlock(&mntalloc);
+	new->c = c;
+	new->done = 0;
+	new->flushed = nil;
+	new->b = nil;
+	return new;
+}
+
+void
+mntfree(Mntrpc *r)
+{
+	if(r->b != nil)
+		freeblist(r->b);
+	lock(&mntalloc);
+	if(mntalloc.nrpcfree >= 10){
+		free(r->rpc);
+		freetag(r->request.tag);
+		free(r);
+	}
+	else{
+		r->list = mntalloc.rpcfree;
+		mntalloc.rpcfree = r;
+		mntalloc.nrpcfree++;
+	}
+	mntalloc.nrpcused--;
+	unlock(&mntalloc);
+}
+
+void
+mntqrm(Mnt *m, Mntrpc *r)
+{
+	Mntrpc **l, *f;
+
+	lock(m);
+	r->done = 1;
+
+	l = &m->queue;
+	for(f = *l; f; f = f->list) {
+		if(f == r) {
+			*l = r->list;
+			break;
+		}
+		l = &f->list;
+	}
+	unlock(m);
+}
+
+Mnt*
+mntchk(Chan *c)
+{
+	Mnt *m;
+
+	/* This routine is mostly vestiges of prior lives; now it's just sanity checking */
+
+	if(c->mchan == nil)
+		panic("mntchk 1: nil mchan c %s\n", channame(c));
+
+	m = c->mchan->mux;
+
+	if(m == nil)
+		print("mntchk 2: nil mux c %s c->mchan %s \n", channame(c), channame(c->mchan));
+
+	/*
+	 * Was it closed and reused (was error(Eshutdown); now, it can't happen)
+	 */
+	if(m->id == 0 || m->id >= c->dev)
+		panic("mntchk 3: can't happen");
+
+	return m;
+}
+
+/*
+ * Rewrite channel type and dev for in-flight data to
+ * reflect local values.  These entries are known to be
+ * the first two in the Dir encoding after the count.
+ */
+void
+mntdirfix(uchar *dirbuf, Chan *c)
+{
+	uint r;
+
+	r = devtab[c->type]->dc;
+	dirbuf += BIT16SZ;	/* skip count */
+	PBIT16(dirbuf, r);
+	dirbuf += BIT16SZ;
+	PBIT32(dirbuf, c->dev);
+}
+
+int
+rpcattn(void *v)
+{
+	Mntrpc *r;
+
+	r = v;
+	return r->done || r->m->rip == 0;
+}
+
+Dev mntdevtab = {
+	'M',
+	"mnt",
+
+	mntreset,
+	devinit,
+	devshutdown,
+	mntattach,
+	mntwalk,
+	mntstat,
+	mntopen,
+	mntcreate,
+	mntclose,
+	mntread,
+	devbread,
+	mntwrite,
+	devbwrite,
+	mntremove,
+	mntwstat,
+};
--- /dev/null
+++ b/os/port/devns16552.c
@@ -1,0 +1,1090 @@
+#include	"u.h"
+#include	"../port/lib.h"
+#include	"mem.h"
+#include	"dat.h"
+#include	"fns.h"
+#include	"io.h"
+#include	"../port/error.h"
+
+#include	"../port/netif.h"
+
+/*
+ *  Driver for the ns16552.
+ */
+enum
+{
+	/*
+	 *  register numbers
+	 */
+	Data=	0,		/* xmit/rcv buffer */
+	Iena=	1,		/* interrupt enable */
+	 Ircv=	(1<<0),		/*  for char rcv'd */
+	 Ixmt=	(1<<1),		/*  for xmit buffer empty */
+	 Irstat=(1<<2),		/*  for change in rcv'er status */
+	 Imstat=(1<<3),		/*  for change in modem status */
+	Istat=	2,		/* interrupt flag (read) */
+	 Ipend=	1,		/* interrupt pending (not) */
+	 Fenabd=(3<<6),   		/*  on if fifo's enabled */
+	Fifoctl=2,		/* fifo control (write) */
+	 Fena=	(1<<0),		/*  enable xmit/rcv fifos */
+	 Ftrig=	(1<<6),		/*  trigger after 4 input characters */
+	 Fclear=(3<<1),		/*  clear xmit & rcv fifos */
+	Format=	3,		/* byte format */
+	 Bits8=	(3<<0),		/*  8 bits/byte */
+	 Stop2=	(1<<2),		/*  2 stop bits */
+	 Pena=	(1<<3),		/*  generate parity */
+	 Peven=	(1<<4),		/*  even parity */
+	 Pforce=(1<<5),		/*  force parity */
+	 Break=	(1<<6),		/*  generate a break */
+	 Dra=	(1<<7),		/*  address the divisor */
+	Mctl=	4,		/* modem control */
+	 Dtr=	(1<<0),		/*  data terminal ready */
+	 Rts=	(1<<1),		/*  request to send */
+	 Ri=	(1<<2),		/*  ring */
+	 Inton=	(1<<3),		/*  turn on interrupts */
+	 Loop=	(1<<4),		/*  loop back */
+	Lstat=	5,		/* line status */
+	 Inready=(1<<0),	/*  receive buffer full */
+	 Oerror=(1<<1),		/*  receiver overrun */
+	 Perror=(1<<2),		/*  receiver parity error */
+	 Ferror=(1<<3),		/*  rcv framing error */
+	 Berror=(1<<4),		/* break alarm */
+	 Outready=(1<<5),	/*  output buffer full */
+	Mstat=	6,		/* modem status */
+	 Ctsc=	(1<<0),		/*  clear to send changed */
+	 Dsrc=	(1<<1),		/*  data set ready changed */
+	 Rire=	(1<<2),		/*  rising edge of ring indicator */
+	 Dcdc=	(1<<3),		/*  data carrier detect changed */
+	 Cts=	(1<<4),		/*  complement of clear to send line */
+	 Dsr=	(1<<5),		/*  complement of data set ready line */
+	 Ring=	(1<<6),		/*  complement of ring indicator line */
+	 Dcd=	(1<<7),		/*  complement of data carrier detect line */
+	Scratch=7,		/* scratchpad */
+	Dlsb=	0,		/* divisor lsb */
+	Dmsb=	1,		/* divisor msb */
+
+	CTLS= 023,
+	CTLQ= 021,
+
+	Stagesize= 1024,
+	Nuart=	32,		/* max per machine */
+};
+
+typedef struct Uart Uart;
+struct Uart
+{
+	QLock;
+	int	opens;
+
+	int	enabled;
+	Uart	*elist;			/* next enabled interface */
+	char	name[KNAMELEN];
+
+	uchar	sticky[8];		/* sticky write register values */
+	ulong	port;
+	ulong	freq;			/* clock frequency */
+	uchar	mask;			/* bits/char */
+	int	dev;
+	int	baud;			/* baud rate */
+
+	uchar	istat;			/* last istat read */
+	int	frame;			/* framing errors */
+	int	overrun;		/* rcvr overruns */
+
+	/* buffers */
+	int	(*putc)(Queue*, int);
+	Queue	*iq;
+	Queue	*oq;
+
+	Lock	flock;			/* fifo */
+	uchar	fifoon;			/* fifo's enabled */
+	uchar	nofifo;			/* earlier chip version with nofifo */
+
+	Lock	rlock;			/* receive */
+	uchar	istage[Stagesize];
+	uchar	*ip;
+	uchar	*ie;
+
+	int	haveinput;
+
+	Lock	tlock;			/* transmit */
+	uchar	ostage[Stagesize];
+	uchar	*op;
+	uchar	*oe;
+
+	int	modem;			/* hardware flow control on */
+	int	xonoff;			/* software flow control on */
+	int	blocked;
+	int	cts, dsr, dcd;		/* keep track of modem status */ 
+	int	ctsbackoff;
+	int	hup_dsr, hup_dcd;	/* send hangup upstream? */
+	int	dohup;
+
+	Rendez	r;
+};
+
+static Uart *uart[Nuart];
+static int nuart;
+static Uart *consuart;
+
+struct Uartalloc {
+	Lock;
+	Uart *elist;	/* list of enabled interfaces */
+} uartalloc;
+
+void ns16552intr(int);
+
+/*
+ *  pick up architecture specific routines and definitions
+ */
+#include "ns16552.h"
+
+/*
+ *  set the baud rate by calculating and setting the baudrate
+ *  generator constant.  This will work with fairly non-standard
+ *  baud rates.
+ */
+static void
+ns16552setbaud(Uart *p, int rate)
+{
+	ulong brconst;
+
+	if(rate <= 0)
+		return;
+
+	brconst = (p->freq+8*rate-1)/(16*rate);
+
+	uartwrreg(p, Format, Dra);
+	outb(p->port + Dmsb, (brconst>>8) & 0xff);
+	outb(p->port + Dlsb, brconst & 0xff);
+	uartwrreg(p, Format, 0);
+
+	p->baud = rate;
+}
+
+/*
+ * decide if we should hangup when dsr or dcd drops.
+ */
+static void
+ns16552dsrhup(Uart *p, int n)
+{
+	p->hup_dsr = n;
+}
+
+static void
+ns16552dcdhup(Uart *p, int n)
+{
+	p->hup_dcd = n;
+}
+
+static void
+ns16552parity(Uart *p, char type)
+{
+	switch(type){
+	case 'e':
+		p->sticky[Format] |= Pena|Peven;
+		break;
+	case 'o':
+		p->sticky[Format] &= ~Peven;
+		p->sticky[Format] |= Pena;
+		break;
+	default:
+		p->sticky[Format] &= ~(Pena|Peven);
+		break;
+	}
+	uartwrreg(p, Format, 0);
+}
+
+/*
+ *  set bits/character, default 8
+ */
+void
+ns16552bits(Uart *p, int bits)
+{
+	if(bits < 5 || bits > 8)
+		error(Ebadarg);
+
+	p->sticky[Format] &= ~3;
+	p->sticky[Format] |= bits-5;
+
+	uartwrreg(p, Format, 0);
+}
+
+
+/*
+ *  toggle DTR
+ */
+void
+ns16552dtr(Uart *p, int n)
+{
+	if(n)
+		p->sticky[Mctl] |= Dtr;
+	else
+		p->sticky[Mctl] &= ~Dtr;
+
+	uartwrreg(p, Mctl, 0);
+}
+
+/*
+ *  toggle RTS
+ */
+void
+ns16552rts(Uart *p, int n)
+{
+	if(n)
+		p->sticky[Mctl] |= Rts;
+	else
+		p->sticky[Mctl] &= ~Rts;
+
+	uartwrreg(p, Mctl, 0);
+}
+
+/*
+ *  send break
+ */
+static void
+ns16552break(Uart *p, int ms)
+{
+	if(ms == 0)
+		ms = 200;
+
+	uartwrreg(p, Format, Break);
+	tsleep(&up->sleep, return0, 0, ms);
+	uartwrreg(p, Format, 0);
+}
+
+static void
+ns16552fifoon(Uart *p)
+{
+	ulong i, x;
+
+	if(p->nofifo || uartrdreg(p, Istat) & Fenabd)
+		return;
+
+	x = splhi();
+
+	/* reset fifos */
+	p->sticky[Fifoctl] = 0;
+	uartwrreg(p, Fifoctl, Fclear);
+
+	/* empty buffer and interrupt conditions */
+	for(i = 0; i < 16; i++){
+		if(uartrdreg(p, Istat)){
+			/* nothing to do */
+		}
+		if(uartrdreg(p, Data)){
+			/* nothing to do */
+		}
+	}
+
+	/* turn on fifo */
+	p->fifoon = 1;
+	p->sticky[Fifoctl] = Fena|Ftrig;
+	uartwrreg(p, Fifoctl, 0);
+	p->istat = uartrdreg(p, Istat);
+	if((p->istat & Fenabd) == 0) {
+		/* didn't work, must be an earlier chip type */
+		p->nofifo = 1;
+	}
+
+	splx(x);
+}
+
+/*
+ *  modem flow control on/off (rts/cts)
+ */
+static void
+ns16552mflow(Uart *p, int n)
+{
+	ilock(&p->tlock);
+	if(n){
+		p->sticky[Iena] |= Imstat;
+		uartwrreg(p, Iena, 0);
+		p->modem = 1;
+		p->cts = uartrdreg(p, Mstat) & Cts;
+	} else {
+		p->sticky[Iena] &= ~Imstat;
+		uartwrreg(p, Iena, 0);
+		p->modem = 0;
+		p->cts = 1;
+	}
+	iunlock(&p->tlock);
+
+//	ilock(&p->flock);
+//	if(1)
+//		/* turn on fifo's */
+//		ns16552fifoon(p);
+//	else {
+//		/* turn off fifo's */
+//		p->fifoon = 0;
+//		p->sticky[Fifoctl] = 0;
+//		uartwrreg(p, Fifoctl, Fclear);
+//	}
+//	iunlock(&p->flock);
+}
+
+/*
+ *  turn on a port's interrupts.  set DTR and RTS
+ */
+static void
+ns16552enable(Uart *p)
+{
+	Uart **l;
+
+	if(p->enabled)
+		return;
+
+	uartpower(p->dev, 1);
+
+	p->hup_dsr = p->hup_dcd = 0;
+	p->cts = p->dsr = p->dcd = 0;
+
+	/*
+ 	 *  turn on interrupts
+	 */
+	p->sticky[Iena] = Ircv | Ixmt | Irstat;
+	uartwrreg(p, Iena, 0);
+
+	/*
+	 *  turn on DTR and RTS
+	 */
+	ns16552dtr(p, 1);
+	ns16552rts(p, 1);
+
+	ns16552fifoon(p);
+
+	/*
+	 *  assume we can send
+	 */
+	ilock(&p->tlock);
+	p->cts = 1;
+	p->blocked = 0;
+	iunlock(&p->tlock);
+
+	/*
+	 *  set baud rate to the last used
+	 */
+	ns16552setbaud(p, p->baud);
+
+	lock(&uartalloc);
+	for(l = &uartalloc.elist; *l; l = &(*l)->elist){
+		if(*l == p)
+			break;
+	}
+	if(*l == 0){
+		p->elist = uartalloc.elist;
+		uartalloc.elist = p;
+	}
+	p->enabled = 1;
+	unlock(&uartalloc);
+}
+
+/*
+ *  turn off a port's interrupts.  reset DTR and RTS
+ */
+static void
+ns16552disable(Uart *p)
+{
+	Uart **l;
+
+	/*
+ 	 *  turn off interrupts
+	 */
+	p->sticky[Iena] = 0;
+	uartwrreg(p, Iena, 0);
+
+	/*
+	 *  revert to default settings
+	 */
+	p->sticky[Format] = Bits8;
+	uartwrreg(p, Format, 0);
+
+	/*
+	 *  turn off DTR, RTS, hardware flow control & fifo's
+	 */
+	ns16552dtr(p, 0);
+	ns16552rts(p, 0);
+	ns16552mflow(p, 0);
+	ilock(&p->tlock);
+	p->xonoff = p->blocked = 0;
+	iunlock(&p->tlock);
+
+	uartpower(p->dev, 0);
+
+	lock(&uartalloc);
+	for(l = &uartalloc.elist; *l; l = &(*l)->elist){
+		if(*l == p){
+			*l = p->elist;
+			break;
+		}
+	}
+	p->enabled = 0;
+	unlock(&uartalloc);
+}
+
+/*
+ *  put some bytes into the local queue to avoid calling
+ *  qconsume for every character
+ */
+static int
+stageoutput(Uart *p)
+{
+	int n;
+
+	n = qconsume(p->oq, p->ostage, Stagesize);
+	if(n <= 0)
+		return 0;
+	p->op = p->ostage;
+	p->oe = p->ostage + n;
+	return n;
+}
+
+/*
+ *  (re)start output
+ */
+static void
+ns16552kick0(Uart *p)
+{
+	int i;
+	if((p->modem && (p->cts == 0)) || p->blocked)
+		return;
+
+	/*
+	 *  128 here is an arbitrary limit to make sure
+	 *  we don't stay in this loop too long.  If the
+	 *  chips output queue is longer than 128, too
+	 *  bad -- presotto
+	 */
+	for(i = 0; i < 128; i++){
+		if(!(uartrdreg(p, Lstat) & Outready))
+			break;
+		if(p->op >= p->oe && stageoutput(p) == 0)
+			break;
+		outb(p->port + Data, *(p->op++));
+	}
+}
+
+static void
+ns16552kick(void *v)
+{
+	Uart *p;
+
+	p = v;
+	ilock(&p->tlock);
+	ns16552kick0(p);
+	iunlock(&p->tlock);
+}
+
+/*
+ *  restart input if it's off
+ */
+static void
+ns16552flow(void *v)
+{
+	Uart *p;
+
+	p = v;
+	if(p->modem)
+		ns16552rts(p, 1);
+	ilock(&p->rlock);
+	p->haveinput = 1;
+	iunlock(&p->rlock);
+}
+
+/*
+ *  default is 9600 baud, 1 stop bit, 8 bit chars, no interrupts,
+ *  transmit and receive enabled, interrupts disabled.
+ */
+static void
+ns16552setup0(Uart *p)
+{
+	memset(p->sticky, 0, sizeof(p->sticky));
+	/*
+	 *  set rate to 9600 baud.
+	 *  8 bits/character.
+	 *  1 stop bit.
+	 *  interrupts enabled.
+	 */
+	p->sticky[Format] = Bits8;
+	uartwrreg(p, Format, 0);
+	p->sticky[Mctl] |= Inton;
+	uartwrreg(p, Mctl, 0x0);
+
+	ns16552setbaud(p, 9600);
+
+	p->iq = qopen(4*1024, 0, ns16552flow, p);
+	p->oq = qopen(4*1024, 0, ns16552kick, p);
+	if(p->iq == nil || p->oq == nil)
+		panic("ns16552setup0");
+
+	p->ip = p->istage;
+	p->ie = &p->istage[Stagesize];
+	p->op = p->ostage;
+	p->oe = p->ostage;
+}
+
+/*
+ *  called by main() to create a new duart
+ */
+void
+ns16552setup(ulong port, ulong freq, char *name)
+{
+	Uart *p;
+
+	if(nuart >= Nuart)
+		return;
+
+	p = xalloc(sizeof(Uart));
+	uart[nuart] = p;
+	strcpy(p->name, name);
+	p->dev = nuart;
+	nuart++;
+	p->port = port;
+	p->freq = freq;
+	ns16552setup0(p);
+}
+
+/*
+ *  called by main() to configure a duart port as a console or a mouse
+ */
+void
+ns16552special(int port, int baud, Queue **in, Queue **out, int (*putc)(Queue*, int))
+{
+	Uart *p = uart[port];
+	ns16552enable(p);
+	if(baud)
+		ns16552setbaud(p, baud);
+	p->putc = putc;
+	if(in)
+		*in = p->iq;
+	if(out)
+		*out = p->oq;
+	p->opens++;
+}
+
+/*
+ *  handle an interrupt to a single uart
+ */
+void
+ns16552intr(int dev)
+{
+	uchar ch;
+	int s, l;
+	Uart *p = uart[dev];
+
+	for (s = uartrdreg(p, Istat); !(s&Ipend); s = uartrdreg(p, Istat)) {
+		switch(s&0x3f){
+		case 4:	/* received data available */
+		case 6:	/* receiver line status (alarm or error) */
+		case 12:	/* character timeout indication */
+			while ((l = uartrdreg(p, Lstat)) & Inready) {
+				if(l & Ferror)
+					p->frame++;
+				if(l & Oerror)
+					p->overrun++;
+				ch = uartrdreg(p, Data) & 0xff;
+				if (l & (Berror|Perror|Ferror)) {
+					/* ch came with break, parity or framing error - consume */
+					continue;
+				}
+				if (ch == CTLS || ch == CTLQ) {
+					ilock(&p->tlock);
+					if(p->xonoff){
+						if(ch == CTLS)
+							p->blocked = 1;
+						else
+							p->blocked = 0;	/* clock gets output going again */
+					}
+					iunlock(&p->tlock);
+				}
+				if(p->putc)
+					p->putc(p->iq, ch);
+				else {
+					ilock(&p->rlock);
+					if(p->ip < p->ie)
+						*p->ip++ = ch;
+					else
+						p->overrun++;
+					p->haveinput = 1;
+					iunlock(&p->rlock);
+				}
+			}
+			break;
+
+		case 2:	/* transmitter not full */
+			ns16552kick(p);
+			break;
+
+		case 0:	/* modem status */
+			ch = uartrdreg(p, Mstat);
+			if(ch & Ctsc){
+				ilock(&p->tlock);
+				l = p->cts;
+				p->cts = ch & Cts;
+				if(l == 0 && p->cts)
+					p->ctsbackoff = 2; /* clock gets output going again */
+				iunlock(&p->tlock);
+			}
+	 		if (ch & Dsrc) {
+				l = ch & Dsr;
+				if(p->hup_dsr && p->dsr && !l){
+					ilock(&p->rlock);
+					p->dohup = 1;
+					iunlock(&p->rlock);
+				}
+				p->dsr = l;
+			}
+	 		if (ch & Dcdc) {
+				l = ch & Dcd;
+				if(p->hup_dcd && p->dcd && !l){
+					ilock(&p->rlock);
+					p->dohup = 1;
+					iunlock(&p->rlock);
+				}
+				p->dcd = l;
+			}
+			break;
+
+		default:
+			print("weird uart interrupt #%2.2ux\n", s);
+			break;
+		}
+	}
+	p->istat = s;
+}
+
+/*
+ *  we save up input characters till clock time
+ *
+ *  There's also a bit of code to get a stalled print going.
+ *  It shouldn't happen, but it does.  Obviously I don't
+ *  understand something.  Since it was there, I bundled a
+ *  restart after flow control with it to give some hysteresis
+ *  to the hardware flow control.  This makes compressing
+ *  modems happier but will probably bother something else.
+ *	 -- presotto
+ */
+void
+uartclock(void)
+{
+	int n;
+	Uart *p;
+
+	for(p = uartalloc.elist; p; p = p->elist){
+
+		/* this amortizes cost of qproduce to many chars */
+		if(p->haveinput){
+			ilock(&p->rlock);
+			if(p->haveinput){
+				n = p->ip - p->istage;
+				if(n > 0 && p->iq){
+					if(n > Stagesize)
+						panic("uartclock");
+					if(qproduce(p->iq, p->istage, n) < 0)
+						ns16552rts(p, 0);
+					else
+						p->ip = p->istage;
+				}
+				p->haveinput = 0;
+			}
+			iunlock(&p->rlock);
+		}
+		if(p->dohup){
+			ilock(&p->rlock);
+			if(p->dohup){
+				qhangup(p->iq, 0);
+				qhangup(p->oq, 0);
+			}
+			p->dohup = 0;
+			iunlock(&p->rlock);
+		}
+
+		/* this adds hysteresis to hardware flow control */
+		if(p->ctsbackoff){
+			ilock(&p->tlock);
+			if(p->ctsbackoff){
+				if(--(p->ctsbackoff) == 0)
+					ns16552kick0(p);
+			}
+			iunlock(&p->tlock);
+		}
+	}
+}
+
+Dirtab *ns16552dir;
+int ndir;
+
+static void
+setlength(int i)
+{
+	Uart *p;
+
+	if(i > 0){
+		p = uart[i];
+		if(p && p->opens && p->iq)
+			ns16552dir[1+3*i].length = qlen(p->iq);
+	} else for(i = 0; i < nuart; i++){
+		p = uart[i];
+		if(p && p->opens && p->iq)
+			ns16552dir[1+3*i].length = qlen(p->iq);
+	}
+}
+
+/*
+ *  all uarts must be ns16552setup() by this point or inside of ns16552install()
+ */
+static void
+ns16552reset(void)
+{
+	int i;
+	Dirtab *dp;
+	ns16552install();	/* architecture specific */
+
+	ndir = 1+3*nuart;
+	ns16552dir = xalloc(ndir * sizeof(Dirtab));
+	dp = ns16552dir;
+	strcpy(dp->name, ".");
+	mkqid(&dp->qid, 0, 0, QTDIR);
+	dp->length = 0;
+	dp->perm = DMDIR|0555;
+	dp++;
+	for(i = 0; i < nuart; i++){
+		/* 3 directory entries per port */
+		strcpy(dp->name, uart[i]->name);
+		dp->qid.path = NETQID(i, Ndataqid);
+		dp->perm = 0666;
+		dp++;
+		sprint(dp->name, "%sctl", uart[i]->name);
+		dp->qid.path = NETQID(i, Nctlqid);
+		dp->perm = 0666;
+		dp++;
+		sprint(dp->name, "%sstatus", uart[i]->name);
+		dp->qid.path = NETQID(i, Nstatqid);
+		dp->perm = 0444;
+		dp++;
+	}
+}
+
+static Chan*
+ns16552attach(char *spec)
+{
+	return devattach('t', spec);
+}
+
+static Walkqid*
+ns16552walk(Chan *c, Chan *nc, char **name, int nname)
+{
+	return devwalk(c, nc, name, nname, ns16552dir, ndir, devgen);
+}
+
+static int
+ns16552stat(Chan *c, uchar *dp, int n)
+{
+	if(NETTYPE(c->qid.path) == Ndataqid)
+		setlength(NETID(c->qid.path));
+	return devstat(c, dp, n, ns16552dir, ndir, devgen);
+}
+
+static Chan*
+ns16552open(Chan *c, int omode)
+{
+	Uart *p;
+
+	c = devopen(c, omode, ns16552dir, ndir, devgen);
+
+	switch(NETTYPE(c->qid.path)){
+	case Nctlqid:
+	case Ndataqid:
+		p = uart[NETID(c->qid.path)];
+		qlock(p);
+		if(p->opens++ == 0){
+			ns16552enable(p);
+			qreopen(p->iq);
+			qreopen(p->oq);
+		}
+		qunlock(p);
+		break;
+	}
+
+	return c;
+}
+
+static void
+ns16552close(Chan *c)
+{
+	Uart *p;
+
+	if(c->qid.type & QTDIR)
+		return;
+	if((c->flag & COPEN) == 0)
+		return;
+	switch(NETTYPE(c->qid.path)){
+	case Ndataqid:
+	case Nctlqid:
+		p = uart[NETID(c->qid.path)];
+		qlock(p);
+		if(--(p->opens) == 0){
+			ns16552disable(p);
+			qclose(p->iq);
+			qclose(p->oq);
+			p->ip = p->istage;
+			p->dcd = p->dsr = p->dohup = 0;
+		}
+		qunlock(p);
+		break;
+	}
+}
+
+static long
+uartstatus(Chan*, Uart *p, void *buf, long n, long offset)
+{
+	uchar mstat, fstat, istat, tstat;
+	char str[256];
+
+	str[0] = 0;
+	tstat = p->sticky[Mctl];
+	mstat = uartrdreg(p, Mstat);
+	istat = p->sticky[Iena];
+	fstat = p->sticky[Format];
+	snprint(str, sizeof str,
+		"b%d c%d d%d e%d l%d m%d p%c r%d s%d\n"
+		"%d %d %d%s%s%s%s%s\n",
+
+		p->baud,
+		p->hup_dcd, 
+		(tstat & Dtr) != 0,
+		p->hup_dsr,
+		(fstat & Bits8) + 5,
+		(istat & Imstat) != 0, 
+		(fstat & Pena) ? ((fstat & Peven) ? 'e' : 'o') : 'n',
+		(tstat & Rts) != 0,
+		(fstat & Stop2) ? 2 : 1,
+
+		p->dev,
+		p->frame,
+		p->overrun, 
+		uartrdreg(p, Istat) & Fenabd       ? " fifo" : "",
+		(mstat & Cts)    ? " cts"  : "",
+		(mstat & Dsr)    ? " dsr"  : "",
+		(mstat & Dcd)    ? " dcd"  : "",
+		(mstat & Ring)   ? " ring" : ""
+	);
+	return readstr(offset, buf, n, str);
+}
+
+static long
+ns16552read(Chan *c, void *buf, long n, vlong off)
+{
+	Uart *p;
+	ulong offset = off;
+
+	if(c->qid.type & QTDIR){
+		setlength(-1);
+		return devdirread(c, buf, n, ns16552dir, ndir, devgen);
+	}
+
+	p = uart[NETID(c->qid.path)];
+	switch(NETTYPE(c->qid.path)){
+	case Ndataqid:
+		return qread(p->iq, buf, n);
+	case Nctlqid:
+		return readnum(offset, buf, n, NETID(c->qid.path), NUMSIZE);
+	case Nstatqid:
+		return uartstatus(c, p, buf, n, offset);
+	}
+
+	return 0;
+}
+
+static void
+ns16552ctl(Uart *p, char *cmd)
+{
+	int i, n;
+
+	/* let output drain for a while */
+	for(i = 0; i < 16 && qlen(p->oq); i++)
+		tsleep(&p->r, (int(*)(void*))qlen, p->oq, 125);
+
+	if(strncmp(cmd, "break", 5) == 0){
+		ns16552break(p, 0);
+		return;
+	}
+
+
+	n = atoi(cmd+1);
+	switch(*cmd){
+	case 'B':
+	case 'b':
+		ns16552setbaud(p, n);
+		break;
+	case 'C':
+	case 'c':
+		ns16552dcdhup(p, n);
+		break;
+	case 'D':
+	case 'd':
+		ns16552dtr(p, n);
+		break;
+	case 'E':
+	case 'e':
+		ns16552dsrhup(p, n);
+		break;
+	case 'f':
+	case 'F':
+		qflush(p->oq);
+		break;
+	case 'H':
+	case 'h':
+		qhangup(p->iq, 0);
+		qhangup(p->oq, 0);
+		break;
+	case 'L':
+	case 'l':
+		ns16552bits(p, n);
+		break;
+	case 'm':
+	case 'M':
+		ns16552mflow(p, n);
+		break;
+	case 'n':
+	case 'N':
+		qnoblock(p->oq, n);
+		break;
+	case 'P':
+	case 'p':
+		ns16552parity(p, *(cmd+1));
+		break;
+	case 'K':
+	case 'k':
+		ns16552break(p, n);
+		break;
+	case 'R':
+	case 'r':
+		ns16552rts(p, n);
+		break;
+	case 'Q':
+	case 'q':
+		qsetlimit(p->iq, n);
+		qsetlimit(p->oq, n);
+		break;
+	case 'W':
+	case 'w':
+		/* obsolete */
+		break;
+	case 'X':
+	case 'x':
+		ilock(&p->tlock);
+		p->xonoff = n;
+		iunlock(&p->tlock);
+		break;
+	}
+}
+
+static long
+ns16552write(Chan *c, void *buf, long n, vlong)
+{
+	Uart *p;
+	char cmd[32];
+
+	if(c->qid.type & QTDIR)
+		error(Eperm);
+
+	p = uart[NETID(c->qid.path)];
+
+	/*
+	 *  The fifo's turn themselves off sometimes.
+	 *  It must be something I don't understand. -- presotto
+	 */
+	lock(&p->flock);
+	if((p->istat & Fenabd) == 0 && p->fifoon && p->nofifo == 0)
+		ns16552fifoon(p);
+	unlock(&p->flock);
+
+	switch(NETTYPE(c->qid.path)){
+	case Ndataqid:
+		return qwrite(p->oq, buf, n);
+	case Nctlqid:
+		if(n >= sizeof(cmd))
+			n = sizeof(cmd)-1;
+		memmove(cmd, buf, n);
+		cmd[n] = 0;
+		ns16552ctl(p, cmd);
+		return n;
+	}
+}
+
+static int
+ns16552wstat(Chan *c, uchar *dp, int n)
+{
+	Dir d;
+	Dirtab *dt;
+
+	if(!iseve())
+		error(Eperm);
+	if(c->qid.type & QTDIR)
+		error(Eperm);
+	if(NETTYPE(c->qid.path) == Nstatqid)
+		error(Eperm);
+
+	dt = &ns16552dir[1+3 * NETID(c->qid.path)];
+	n = convM2D(dp, n, &d, nil);
+	if(n == 0)
+		error(Eshortstat);
+	if(d.mode != ~0UL){
+		d.mode &= 0666;
+		dt[0].perm = dt[1].perm = d.mode;
+	}
+	return n;
+}
+
+Dev ns16552devtab = {
+	't',
+	"ns16552",
+
+	ns16552reset,
+	devinit,
+	devshutdown,
+	ns16552attach,
+	ns16552walk,
+	ns16552stat,
+	ns16552open,
+	devcreate,
+	ns16552close,
+	ns16552read,
+	devbread,
+	ns16552write,
+	devbwrite,
+	devremove,
+	ns16552wstat,
+};
+
+void
+uartputc(int c)
+{
+	Uart *p;
+	int i;
+
+	p = consuart;
+	if(p == nil)
+		return;
+	for(i = 0; !(uartrdreg(p, Lstat)&Outready) && i < 128; i++)
+		delay(1);
+	outb(p->port+Data, c);
+	for(i = 0; !(uartrdreg(p, Lstat)&Outready) && i < 128; i++)
+		delay(1);
+}
+
+void
+uartputs(char *s, int n)
+{
+	char *e;
+
+	if(consuart == nil)
+		return;
+	e = s+n;
+	for(; s < e; s++){
+		if(*s == '\n')
+			uartputc('\r');
+		uartputc(*s);
+	}
+}
--- /dev/null
+++ b/os/port/devpci.c
@@ -1,0 +1,227 @@
+/*
+ *	access to PCI configuration space (devpnp.c without PNP)
+ *
+ *	TODO
+ *		- extend PCI raw access to configuration space (writes, byte/short access?)
+ *		- implement PCI access to memory/io space/BIOS ROM
+ *		- use c->aux instead of performing lookup on each read/write?
+ */
+#include	"u.h"
+#include	"../port/lib.h"
+#include	"mem.h"
+#include	"dat.h"
+#include	"fns.h"
+#include	"io.h"
+#include	"../port/error.h"
+
+#define	DPRINT	if(0) print
+#define	XPRINT	if(1) print
+
+enum {
+	Qtopdir = 0,
+
+	Qpcidir,
+	Qpcictl,
+	Qpciraw,
+};
+
+#define TYPE(q)		((ulong)(q).path & 0x0F)
+#define QID(c, t)	(((c)<<4)|(t))
+
+static Dirtab topdir[] = {
+	".",	{ Qtopdir, 0, QTDIR },	0,	0555,
+	"pci",	{ Qpcidir, 0, QTDIR },	0,	0555,
+};
+
+extern Dev pcidevtab;
+
+static int
+pcigen2(Chan *c, int t, int tbdf, Dir *dp)
+{
+	Qid q;
+
+	q = (Qid){BUSBDF(tbdf)|t, 0, 0};
+	switch(t) {
+	case Qpcictl:
+		sprint(up->genbuf, "%d.%d.%dctl", BUSBNO(tbdf), BUSDNO(tbdf), BUSFNO(tbdf));
+		devdir(c, q, up->genbuf, 0, eve, 0444, dp);
+		return 1;
+	case Qpciraw:
+		sprint(up->genbuf, "%d.%d.%draw", BUSBNO(tbdf), BUSDNO(tbdf), BUSFNO(tbdf));
+		devdir(c, q, up->genbuf, 128, eve, 0444, dp);
+		return 1;
+	}
+	return -1;
+}
+
+static int
+pcigen(Chan *c, char *, Dirtab*, int, int s, Dir *dp)
+{
+	Qid q;
+	Pcidev *p;
+	int tbdf;
+
+	switch(TYPE(c->qid)){
+	case Qtopdir:
+		if(s == DEVDOTDOT){
+			q = (Qid){QID(0, Qtopdir), 0, QTDIR};
+			sprint(up->genbuf, "#%C", pcidevtab.dc);
+			devdir(c, q, up->genbuf, 0, eve, 0555, dp);
+			return 1;
+		}
+		return devgen(c, nil, topdir, nelem(topdir), s, dp);
+	case Qpcidir:
+		if(s == DEVDOTDOT){
+			q = (Qid){QID(0, Qtopdir), 0, QTDIR};
+			sprint(up->genbuf, "#%C", pcidevtab.dc);
+			devdir(c, q, up->genbuf, 0, eve, 0555, dp);
+			return 1;
+		}
+		p = pcimatch(nil, 0, 0);
+		while(s >= 2 && p != nil) {
+			p = pcimatch(p, 0, 0);
+			s -= 2;
+		}
+		if(p == nil)
+			return -1;
+		return pcigen2(c, s+Qpcictl, p->tbdf, dp);
+	case Qpcictl:
+	case Qpciraw:
+		tbdf = MKBUS(BusPCI, 0, 0, 0)|BUSBDF((ulong)c->qid.path);
+		p = pcimatchtbdf(tbdf);
+		if(p == nil)
+			return -1;
+		return pcigen2(c, TYPE(c->qid), tbdf, dp);
+	default:
+		break;
+	}
+	return -1;
+}
+
+static Chan*
+pciattach(char *spec)
+{
+	return devattach(pcidevtab.dc, spec);
+}
+
+Walkqid*
+pciwalk(Chan* c, Chan *nc, char** name, int nname)
+{
+	return devwalk(c, nc, name, nname, nil, 0, pcigen);
+}
+
+static int
+pcistat(Chan* c, uchar* dp, int n)
+{
+	return devstat(c, dp, n, nil, 0, pcigen);
+}
+
+static Chan*
+pciopen(Chan *c, int omode)
+{
+	return devopen(c, omode, nil, 0, pcigen);
+}
+
+static void
+pciclose(Chan*)
+{
+}
+
+static long
+pciread(Chan *c, void *va, long n, vlong offset)
+{
+	ulong x;
+	Pcidev *p;
+	char buf[256], *ebuf, *w;
+	char *a = va;
+	int i, tbdf, r;
+
+	switch(TYPE(c->qid)){
+	case Qtopdir:
+	case Qpcidir:
+		return devdirread(c, a, n, nil, 0, pcigen);
+	case Qpcictl:
+		tbdf = MKBUS(BusPCI, 0, 0, 0)|BUSBDF((ulong)c->qid.path);
+		p = pcimatchtbdf(tbdf);
+		if(p == nil)
+			error(Egreg);
+		ebuf = buf+sizeof buf-1;	/* -1 for newline */
+		w = seprint(buf, ebuf, "%.2x.%.2x.%.2x %.4x/%.4x %3d",
+			p->ccrb, p->ccru, p->ccrp, p->vid, p->did, p->intl);
+		for(i=0; i<nelem(p->mem); i++){
+			if(p->mem[i].size == 0)
+				continue;
+			w = seprint(w, ebuf, " %d:%.8lux %d", i, p->mem[i].bar, p->mem[i].size);
+		}
+		*w++ = '\n';
+		*w = '\0';
+		return readstr(offset, a, n, buf);
+	case Qpciraw:
+		tbdf = MKBUS(BusPCI, 0, 0, 0)|BUSBDF((ulong)c->qid.path);
+		p = pcimatchtbdf(tbdf);
+		if(p == nil)
+			error(Egreg);
+		if(offset > 256)
+			return 0;
+		if(n+offset > 256)
+			n = 256-offset;
+		if(offset%4)
+			error(Ebadarg);
+		r = offset;
+		for(i = 0; i+4 <= n; i+=4) {
+			x = pcicfgr32(p, r);
+			a[0] = x;
+			a[1] = (x>>8);
+			a[2] = (x>>16);
+			a[3] = (x>>24);
+			a += 4;
+			r += 4;
+		}
+		return i;
+	default:
+		error(Egreg);
+	}
+	return n;
+}
+
+static long
+pciwrite(Chan *c, void *a, long n, vlong)
+{
+	char buf[256];
+
+	if(n >= sizeof(buf))
+		n = sizeof(buf)-1;
+	strncpy(buf, a, n);
+	buf[n] = 0;
+
+	switch(TYPE(c->qid)){
+	case Qpcictl:
+	case Qpciraw:
+		error(Eperm);
+	default:
+		error(Egreg);
+	}
+	return n;
+}
+
+
+Dev pcidevtab = {
+	'$',
+	"pci",
+
+	devreset,
+	devinit,
+	devshutdown,
+	pciattach,
+	pciwalk,
+	pcistat,
+	pciopen,
+	devcreate,
+	pciclose,
+	pciread,
+	devbread,
+	pciwrite,
+	devbwrite,
+	devremove,
+	devwstat,
+};
--- /dev/null
+++ b/os/port/devpipe.c
@@ -1,0 +1,464 @@
+#include	"u.h"
+#include	"../port/lib.h"
+#include	"mem.h"
+#include	"dat.h"
+#include	"fns.h"
+#include	"../port/error.h"
+#include "interp.h"
+
+#define NETTYPE(x)	((ulong)(x)&0x1f)
+#define NETID(x)	(((ulong)(x))>>5)
+#define NETQID(i,t)	(((i)<<5)|(t))
+
+typedef struct Pipe	Pipe;
+struct Pipe
+{
+	QLock;
+	Pipe*	next;
+	int	ref;
+	ulong	path;
+	Queue*	q[2];
+	int	qref[2];
+	Dirtab	*pipedir;
+	char*	user;
+};
+
+static struct
+{
+	Lock;
+	ulong	path;
+	int	pipeqsize;	
+} pipealloc;
+
+enum
+{
+	Qdir,
+	Qdata0,
+	Qdata1,
+};
+
+static 
+Dirtab pipedir[] =
+{
+	".",		{Qdir,0,QTDIR},	0,		DMDIR|0500,
+	"data",		{Qdata0},	0,			0660,
+	"data1",	{Qdata1},	0,			0660,
+};
+
+static void
+freepipe(Pipe *p)
+{
+	if(p != nil){
+		free(p->user);
+		free(p->q[0]);
+		free(p->q[1]);
+		free(p->pipedir);
+		free(p);
+	}
+}
+
+static void
+pipeinit(void)
+{
+	pipealloc.pipeqsize = 32*1024;
+}
+
+/*
+ *  create a pipe, no streams are created until an open
+ */
+static Chan*
+pipeattach(char *spec)
+{
+	Pipe *p;
+	Chan *c;
+
+	c = devattach('|', spec);
+	p = malloc(sizeof(Pipe));
+	if(p == 0)
+		error(Enomem);
+	if(waserror()){
+		freepipe(p);
+		nexterror();
+	}
+	p->pipedir = malloc(sizeof(pipedir));
+	if (p->pipedir == 0)
+		error(Enomem);
+	memmove(p->pipedir, pipedir, sizeof(pipedir));
+	kstrdup(&p->user, up->env->user);
+	p->ref = 1;
+
+	p->q[0] = qopen(pipealloc.pipeqsize, 0, 0, 0);
+	if(p->q[0] == 0)
+		error(Enomem);
+	p->q[1] = qopen(pipealloc.pipeqsize, 0, 0, 0);
+	if(p->q[1] == 0)
+		error(Enomem);
+	poperror();
+
+	lock(&pipealloc);
+	p->path = ++pipealloc.path;
+	unlock(&pipealloc);
+
+	c->qid.path = NETQID(2*p->path, Qdir);
+	c->qid.vers = 0;
+	c->qid.type = QTDIR;
+	c->aux = p;
+	c->dev = 0;
+	return c;
+}
+
+static int
+pipegen(Chan *c, char *, Dirtab *tab, int ntab, int i, Dir *dp)
+{
+	int id, len;
+	Qid qid;
+	Pipe *p;
+
+	if(i == DEVDOTDOT){
+		devdir(c, c->qid, "#|", 0, eve, 0555, dp);
+		return 1;
+	}
+	i++;	/* skip . */
+	if(tab==0 || i>=ntab)
+		return -1;
+	tab += i;
+	p = c->aux;
+	switch(NETTYPE(tab->qid.path)){
+	case Qdata0:
+		len = qlen(p->q[0]);
+		break;
+	case Qdata1:
+		len = qlen(p->q[1]);
+		break;
+	default:
+		len = tab->length;
+		break;
+	}
+	id = NETID(c->qid.path);
+	qid.path = NETQID(id, tab->qid.path);
+	qid.vers = 0;
+	qid.type = QTFILE;
+	devdir(c, qid, tab->name, len, eve, tab->perm, dp);
+	return 1;
+}
+
+
+static Walkqid*
+pipewalk(Chan *c, Chan *nc, char **name, int nname)
+{
+	Walkqid *wq;
+	Pipe *p;
+
+	p = c->aux;
+	wq = devwalk(c, nc, name, nname, p->pipedir, nelem(pipedir), pipegen);
+	if(wq != nil && wq->clone != nil && wq->clone != c){
+		qlock(p);
+		p->ref++;
+		if(c->flag & COPEN){
+			switch(NETTYPE(c->qid.path)){
+			case Qdata0:
+				p->qref[0]++;
+				break;
+			case Qdata1:
+				p->qref[1]++;
+				break;
+			}
+		}
+		qunlock(p);
+	}
+	return wq;
+}
+
+static int
+pipestat(Chan *c, uchar *db, int n)
+{
+	Pipe *p;
+	Dir dir;
+	Dirtab *tab;
+
+	p = c->aux;
+	tab = p->pipedir;
+
+	switch(NETTYPE(c->qid.path)){
+	case Qdir:
+		devdir(c, c->qid, ".", 0, eve, DMDIR|0555, &dir);
+		break;
+	case Qdata0:
+		devdir(c, c->qid, tab[1].name, qlen(p->q[0]), eve, tab[1].perm, &dir);
+		break;
+	case Qdata1:
+		devdir(c, c->qid, tab[2].name, qlen(p->q[1]), eve, tab[2].perm, &dir);
+		break;
+	default:
+		panic("pipestat");
+	}
+	n = convD2M(&dir, db, n);
+	if(n < BIT16SZ)
+		error(Eshortstat);
+	return n;
+}
+
+/*
+ *  if the stream doesn't exist, create it
+ */
+static Chan*
+pipeopen(Chan *c, int omode)
+{
+	Pipe *p;
+
+	if(c->qid.type & QTDIR){
+		if(omode != OREAD)
+			error(Ebadarg);
+		c->mode = omode;
+		c->flag |= COPEN;
+		c->offset = 0;
+		return c;
+	}
+
+	openmode(omode);	/* check it */
+
+	p = c->aux;
+	qlock(p);
+	if(waserror()){
+		qunlock(p);
+		nexterror();
+	}
+	switch(NETTYPE(c->qid.path)){
+	case Qdata0:
+		devpermcheck(p->user, p->pipedir[1].perm, omode);
+		p->qref[0]++;
+		break;
+	case Qdata1:
+		devpermcheck(p->user, p->pipedir[2].perm, omode);
+		p->qref[1]++;
+		break;
+	}
+	poperror();
+	qunlock(p);
+
+	c->mode = openmode(omode);
+	c->flag |= COPEN;
+	c->offset = 0;
+	c->iounit = qiomaxatomic;
+	return c;
+}
+
+static void
+pipeclose(Chan *c)
+{
+	Pipe *p;
+
+	p = c->aux;
+	qlock(p);
+
+	if(c->flag & COPEN){
+		/*
+		 *  closing either side hangs up the stream
+		 */
+		switch(NETTYPE(c->qid.path)){
+		case Qdata0:
+			p->qref[0]--;
+			if(p->qref[0] == 0){
+				qhangup(p->q[1], 0);
+				qclose(p->q[0]);
+			}
+			break;
+		case Qdata1:
+			p->qref[1]--;
+			if(p->qref[1] == 0){
+				qhangup(p->q[0], 0);
+				qclose(p->q[1]);
+			}
+			break;
+		}
+	}
+
+	
+	/*
+	 *  if both sides are closed, they are reusable
+	 */
+	if(p->qref[0] == 0 && p->qref[1] == 0){
+		qreopen(p->q[0]);
+		qreopen(p->q[1]);
+	}
+
+	/*
+	 *  free the structure on last close
+	 */
+	p->ref--;
+	if(p->ref == 0){
+		qunlock(p);
+		freepipe(p);
+	} else
+		qunlock(p);
+}
+
+static long
+piperead(Chan *c, void *va, long n, vlong)
+{
+	Pipe *p;
+
+	p = c->aux;
+
+	switch(NETTYPE(c->qid.path)){
+	case Qdir:
+		return devdirread(c, va, n, p->pipedir, nelem(pipedir), pipegen);
+	case Qdata0:
+		return qread(p->q[0], va, n);
+	case Qdata1:
+		return qread(p->q[1], va, n);
+	default:
+		panic("piperead");
+	}
+	return -1;	/* not reached */
+}
+
+static Block*
+pipebread(Chan *c, long n, ulong offset)
+{
+	Pipe *p;
+
+	p = c->aux;
+
+	switch(NETTYPE(c->qid.path)){
+	case Qdata0:
+		return qbread(p->q[0], n);
+	case Qdata1:
+		return qbread(p->q[1], n);
+	}
+
+	return devbread(c, n, offset);
+}
+
+/*
+ *  a write to a closed pipe causes an exception to be sent to
+ *  the prog.
+ */
+static long
+pipewrite(Chan *c, void *va, long n, vlong)
+{
+	Pipe *p;
+	Prog *r;
+
+	if(waserror()) {
+		/* avoid exceptions when pipe is a mounted queue */
+		if((c->flag & CMSG) == 0) {
+			r = up->iprog;
+			if(r != nil && r->kill == nil)
+				r->kill = "write on closed pipe";
+		}
+		nexterror();
+	}
+
+	p = c->aux;
+
+	switch(NETTYPE(c->qid.path)){
+	case Qdata0:
+		n = qwrite(p->q[1], va, n);
+		break;
+
+	case Qdata1:
+		n = qwrite(p->q[0], va, n);
+		break;
+
+	default:
+		panic("pipewrite");
+	}
+
+	poperror();
+	return n;
+}
+
+static long
+pipebwrite(Chan *c, Block *bp, ulong junk)
+{
+	long n;
+	Pipe *p;
+	Prog *r;
+
+	USED(junk);
+	if(waserror()) {
+		/* avoid exceptions when pipe is a mounted queue */
+		if((c->flag & CMSG) == 0) {
+			r = up->iprog;
+			if(r != nil && r->kill == nil)
+				r->kill = "write on closed pipe";
+		}
+		nexterror();
+	}
+
+	p = c->aux;
+	switch(NETTYPE(c->qid.path)){
+	case Qdata0:
+		n = qbwrite(p->q[1], bp);
+		break;
+
+	case Qdata1:
+		n = qbwrite(p->q[0], bp);
+		break;
+
+	default:
+		n = 0;
+		panic("pipebwrite");
+	}
+
+	poperror();
+	return n;
+}
+
+static int
+pipewstat(Chan *c, uchar *dp, int n)
+{
+	Dir *d;
+	Pipe *p;
+	int d1;
+
+	if (c->qid.type&QTDIR)
+		error(Eperm);
+	p = c->aux;
+	if(strcmp(up->env->user, p->user) != 0)
+		error(Eperm);
+	d = smalloc(sizeof(*d)+n);
+	if(waserror()){
+		free(d);
+		nexterror();
+	}
+	n = convM2D(dp, n, d, (char*)&d[1]);
+	if(n == 0)
+		error(Eshortstat);
+	d1 = NETTYPE(c->qid.path) == Qdata1;
+	if(!emptystr(d->name)){
+		validwstatname(d->name);
+		if(strlen(d->name) >= KNAMELEN)
+			error(Efilename);
+		if(strcmp(p->pipedir[1+!d1].name, d->name) == 0)
+			error(Eexist);
+		kstrcpy(p->pipedir[1+d1].name, d->name, KNAMELEN);
+	}
+	if(d->mode != ~0UL)
+		p->pipedir[d1 + 1].perm = d->mode & 0777;
+	poperror();
+	free(d);
+	return n;
+}
+
+Dev pipedevtab = {
+	'|',
+	"pipe",
+
+	devreset,
+	pipeinit,
+	devshutdown,
+	pipeattach,
+	pipewalk,
+	pipestat,
+	pipeopen,
+	devcreate,
+	pipeclose,
+	piperead,
+	pipebread,
+	pipewrite,
+	pipebwrite,
+	devremove,
+	pipewstat,
+};
--- /dev/null
+++ b/os/port/devpointer.c
@@ -1,0 +1,279 @@
+/*
+ * mouse or stylus
+ */
+
+#include	"u.h"
+#include	"../port/lib.h"
+#include	"mem.h"
+#include	"dat.h"
+#include	"fns.h"
+#include	"../port/error.h"
+
+#include <draw.h>
+#include <memdraw.h>
+#include <cursor.h>
+#include "screen.h"
+
+enum{
+	Qdir,
+	Qpointer,
+	Qcursor,
+};
+
+typedef struct Pointer Pointer;
+
+struct Pointer {
+	int	x;
+	int	y;
+	int	b;
+	ulong	msec;
+};
+
+static struct
+{
+	Pointer;
+	int	modify;
+	int	lastb;
+	Rendez	r;
+	Ref	ref;
+	QLock	q;
+} mouse;
+
+static
+Dirtab pointertab[]={
+	".",			{Qdir, 0, QTDIR},	0,	0555,
+	"pointer",		{Qpointer},	0,	0666,
+	"cursor",		{Qcursor},		0,	0222,
+};
+
+enum {
+	Nevent = 16	/* enough for some */
+};
+
+static struct {
+	int	rd;
+	int	wr;
+	Pointer	clicks[Nevent];
+	Rendez r;
+	int	full;
+	int	put;
+	int	get;
+} ptrq;
+
+/*
+ * called by any source of pointer data
+ */
+void
+mousetrack(int b, int x, int y, int isdelta)
+{
+	int lastb;
+	ulong msec;
+	Pointer e;
+
+	if(isdelta){
+		x += mouse.x;
+		y += mouse.y;
+	}
+	msec = TK2MS(MACHP(0)->ticks);
+	if(b && (mouse.b ^ b)&0x1f){
+		if(msec - mouse.msec < 300 && mouse.lastb == b
+		   && abs(mouse.x - x) < 12 && abs(mouse.y - y) < 12)
+			b |= 1<<8;
+		mouse.lastb = b & 0x1f;
+		mouse.msec = msec;
+	}
+	if(x == mouse.x && y == mouse.y && mouse.b == b)
+		return;
+	lastb = mouse.b;
+	mouse.x = x;
+	mouse.y = y;
+	mouse.b = b;
+	mouse.msec = msec;
+	if(!ptrq.full && lastb != b){
+		e = mouse.Pointer;
+		ptrq.clicks[ptrq.wr] = e;
+		if(++ptrq.wr >= Nevent)
+			ptrq.wr = 0;
+		if(ptrq.wr == ptrq.rd)
+			ptrq.full = 1;
+	}
+	mouse.modify = 1;
+	ptrq.put++;
+	wakeup(&ptrq.r);
+	drawactive(1);
+	/* TO DO: cursor update */
+}
+
+static int
+ptrqnotempty(void*)
+{
+	return ptrq.full || ptrq.put != ptrq.get;
+}
+
+static Pointer
+mouseconsume(void)
+{
+	Pointer e;
+
+	sleep(&ptrq.r, ptrqnotempty, 0);
+	ptrq.full = 0;
+	ptrq.get = ptrq.put;
+	if(ptrq.rd != ptrq.wr){
+		e = ptrq.clicks[ptrq.rd];
+		if(++ptrq.rd >= Nevent)
+			ptrq.rd = 0;
+	}else
+		e = mouse.Pointer;
+	return e;
+}
+
+Point
+mousexy(void)
+{
+	return Pt(mouse.x, mouse.y);
+}
+
+
+static Chan*
+pointerattach(char* spec)
+{
+	return devattach('m', spec);
+}
+
+static Walkqid*
+pointerwalk(Chan *c, Chan *nc, char **name, int nname)
+{
+	Walkqid *wq;
+
+	wq = devwalk(c, nc, name, nname, pointertab, nelem(pointertab), devgen);
+	if(wq != nil && wq->clone != c && wq->clone != nil && (ulong)c->qid.path == Qpointer)
+		incref(&mouse.ref);	/* can this happen? */
+	return wq;
+}
+
+static int
+pointerstat(Chan* c, uchar *db, int n)
+{
+	return devstat(c, db, n, pointertab, nelem(pointertab), devgen);
+}
+
+static Chan*
+pointeropen(Chan* c, int omode)
+{
+	c = devopen(c, omode, pointertab, nelem(pointertab), devgen);
+	if((ulong)c->qid.path == Qpointer){
+		if(waserror()){
+			c->flag &= ~COPEN;
+			nexterror();
+		}
+		if(!canqlock(&mouse.q))
+			error(Einuse);
+		if(incref(&mouse.ref) != 1){
+			qunlock(&mouse.q);
+			error(Einuse);
+		}
+		cursorenable();
+		qunlock(&mouse.q);
+		poperror();
+	}
+	return c;
+}
+
+static void
+pointerclose(Chan* c)
+{
+	if((c->flag & COPEN) == 0)
+		return;
+	switch((ulong)c->qid.path){
+	case Qpointer:
+		qlock(&mouse.q);
+		if(decref(&mouse.ref) == 0)
+			cursordisable();
+		qunlock(&mouse.q);
+		break;
+	}
+}
+
+static long
+pointerread(Chan* c, void* a, long n, vlong)
+{
+	Pointer mt;
+	char tmp[128];
+	int l;
+
+	switch((ulong)c->qid.path){
+	case Qdir:
+		return devdirread(c, a, n, pointertab, nelem(pointertab), devgen);
+	case Qpointer:
+		qlock(&mouse.q);
+		if(waserror()) {
+			qunlock(&mouse.q);
+			nexterror();
+		}
+		mt = mouseconsume();
+		poperror();
+		qunlock(&mouse.q);
+		l = sprint(tmp, "m%11d %11d %11d %11lud ", mt.x, mt.y, mt.b, mt.msec);
+		if(l < n)
+			n = l;
+		memmove(a, tmp, n);
+		break;
+	case Qcursor:
+		/* TO DO: interpret data written as Image; give to drawcursor() */
+		break;
+	default:
+		n=0;
+		break;
+	}
+	return n;
+}
+
+static long
+pointerwrite(Chan* c, void* va, long n, vlong)
+{
+	char *a = va;
+	char buf[128];
+	int b, x, y;
+
+	switch((ulong)c->qid.path){
+	case Qpointer:
+		if(n > sizeof buf-1)
+			n = sizeof buf -1;
+		memmove(buf, va, n);
+		buf[n] = 0;
+		x = strtoul(buf+1, &a, 0);
+		if(*a == 0)
+			error(Eshort);
+		y = strtoul(a, &a, 0);
+		if(*a != 0)
+			b = strtoul(a, 0, 0);
+		else
+			b = mouse.b;
+		mousetrack(b, x, y, 0);
+		break;
+	default:
+		error(Ebadusefd);
+	}
+	return n;
+}
+
+Dev pointerdevtab = {
+	'm',
+	"pointer",
+
+	devreset,
+	devinit,
+	devshutdown,
+	pointerattach,
+	pointerwalk,
+	pointerstat,
+	pointeropen,
+	devcreate,
+	pointerclose,
+	pointerread,
+	devbread,
+	pointerwrite,
+	devbwrite,
+	devremove,
+	devwstat,
+};
--- /dev/null
+++ b/os/port/devprof.c
@@ -1,0 +1,722 @@
+#include	"u.h"
+#include	"../port/lib.h"
+#include "mem.h"
+#include "dat.h"
+#include "fns.h"
+#include	"../port/error.h"
+#include	<interp.h>
+#include	<isa.h>
+#include	"runt.h"
+
+static void	cpxec(Prog *);
+static void memprof(int, Heap*, ulong);
+
+extern	Inst*	pc2dispc(Inst*, Module*);
+
+static	int	interval = 100;	/* Sampling interval in milliseconds */
+
+enum
+{
+	HSIZE	= 32,
+};
+
+#define HASH(m)	((m)%HSIZE)
+
+/* cope with  multiple profilers some day */
+
+typedef struct Record Record;
+struct Record
+{
+	int	id;
+	char*	name;
+	char*	path;
+	Inst*	base;
+	int	size;
+	/*Module*	m;	*/
+	ulong	mtime;
+	Qid	qid;
+	Record*	hash;
+	Record*	link;
+	ulong	bucket[1];
+};
+
+struct
+{
+	Lock	l;
+	vlong	time;
+	Record*	hash[HSIZE];
+	Record*	list;
+} profile;
+
+typedef struct Pmod Pmod;
+struct Pmod
+{
+	char*	name;
+	Pmod*	link;
+} *pmods;
+	
+#define QSHIFT	4
+#define QID(q)		((ulong)(q).path&0xf)
+#define QPID(pid)	((pid)<<QSHIFT)
+#define PID(q)		((q).vers)
+#define PATH(q)	((ulong)(q).path&~((1<<QSHIFT)-1))
+
+enum
+{
+	Qdir,
+	Qname,
+	Qpath,
+	Qhist,
+	Qpctl,
+	Qctl,
+};
+
+Dirtab profdir[] =
+{
+	".",			{Qdir, 0, QTDIR},	0,	DMDIR|0555,
+	"name",		{Qname},	0,			0444,
+	"path",		{Qpath},	0,			0444,
+	"histogram",	{Qhist},	0,			0444,
+	"pctl",		{Qpctl},	0,			0222,
+	"ctl",			{Qctl},	0,			0222,
+};
+
+enum{
+	Pnil,	/* null profiler */
+	Psam,	/* sampling profiler */
+	Pcov,	/* coverage profiler */
+	Pmem,	/* memory profiler */
+};
+
+static int profiler = Pnil;
+
+static int ids;
+static int samplefn;
+
+static void sampler(void*);
+
+static Record*
+getrec(int id)
+{
+	Record *r;
+
+	for(r = profile.list; r != nil; r = r->link)
+		if(r->id == id)
+			break;
+	return r;
+}
+
+static void
+addpmod(char *m)
+{
+	Pmod *p = malloc(sizeof(Pmod));
+
+	if(p == nil)
+		return;
+	p->name = malloc(strlen(m)+1);
+	if(p->name == nil){
+		free(p);
+		return;
+	}
+	strcpy(p->name, m);
+	p->link = pmods;
+	pmods = p;
+}
+
+static void
+freepmods(void)
+{
+	Pmod *p, *np;
+
+	for(p = pmods; p != nil; p = np){
+		free(p->name);
+		np = p->link;
+		free(p);
+	}
+	pmods = nil;
+}
+
+static int
+inpmods(char *m)
+{
+	Pmod *p;
+
+	for(p = pmods; p != nil; p = p->link)
+		if(strcmp(p->name, m) == 0)
+			return 1;
+	return 0;
+}
+
+static void
+freeprof(void)
+{
+	int i;
+	Record *r, *nr;
+
+	ids = 0;
+	profiler = Pnil;
+	freepmods();
+	for(r = profile.list; r != nil; r = nr){
+		free(r->path);
+		nr = r->link;
+		free(r);
+	}
+	profile.list = nil;
+	profile.time = 0;
+	for(i = 0; i < HSIZE; i++)
+		profile.hash[i] = nil;
+}
+
+static int
+profgen(Chan *c, char *name, Dirtab *d, int nd, int s, Dir *dp)
+{
+	Qid qid;
+	Record *r;
+	ulong path, perm, len;
+	Dirtab *tab;
+
+	USED(name);
+	USED(d);
+	USED(nd);
+
+	if(s == DEVDOTDOT) {
+		mkqid(&qid, Qdir, 0, QTDIR);
+		devdir(c, qid, "#P", 0, eve, 0555, dp);
+		return 1;
+	}
+
+	if(c->qid.path == Qdir && c->qid.type & QTDIR) {
+		acquire();
+		if(s-- == 0){
+			tab = &profdir[Qctl];
+			mkqid(&qid, PATH(c->qid)|tab->qid.path, c->qid.vers, QTFILE);
+			devdir(c, qid, tab->name, tab->length, eve, tab->perm, dp);
+			release();
+			return 1;
+		}
+		r = profile.list;
+		while(s-- && r != nil)
+			r = r->link;
+		if(r == nil) {
+			release();
+			return -1;
+		}
+		sprint(up->genbuf, "%.8lux", (ulong)r->id);
+		mkqid(&qid, (r->id<<QSHIFT), r->id, QTDIR);
+		devdir(c, qid, up->genbuf, 0, eve, DMDIR|0555, dp);
+		release();
+		return 1;
+	}
+	if(s >= nelem(profdir)-1)
+		error(Enonexist);	/* was return -1; */
+	tab = &profdir[s];
+	path = PATH(c->qid);
+
+	acquire();
+	r = getrec(PID(c->qid));
+	if(r == nil) {
+		release();
+		error(Enonexist);	/* was return -1; */
+	}
+
+	perm = tab->perm;
+	len = tab->length;
+	mkqid(&qid, path|tab->qid.path, c->qid.vers, QTFILE);
+	devdir(c, qid, tab->name, len, eve, perm, dp);
+	release();
+	return 1;
+}
+
+static Chan*
+profattach(char *spec)
+{
+	return devattach('P', spec);
+}
+
+static Walkqid*
+profwalk(Chan *c, Chan *nc, char **name, int nname)
+{
+	return devwalk(c, nc, name, nname, 0, 0, profgen);
+}
+
+static int
+profstat(Chan *c, uchar *db, int n)
+{
+	return devstat(c, db, n, 0, 0, profgen);
+}
+
+static Chan*
+profopen(Chan *c, int omode)
+{
+	int qid;
+	Record *r;
+
+	if(c->qid.type & QTDIR) {
+		if(omode != OREAD)
+			error(Eisdir);
+		c->mode = openmode(omode);
+		c->flag |= COPEN;
+		c->offset = 0;
+		return c;
+	}
+
+	if(omode&OTRUNC)
+		error(Eperm);
+
+	qid = QID(c->qid);
+	if(qid == Qctl || qid == Qpctl){
+		if (omode != OWRITE)
+			error(Eperm);
+	}
+	else{
+		if(omode != OREAD)
+			error(Eperm);
+	}
+
+	if(qid != Qctl){
+		acquire();
+		r = getrec(PID(c->qid));
+		release();
+		if(r == nil)
+			error(Ethread);
+	}
+
+	c->offset = 0;
+	c->flag |= COPEN;
+	c->mode = openmode(omode);
+	if(QID(c->qid) == Qhist)
+		c->aux = nil;
+	return c;
+}
+
+static int
+profwstat(Chan *c, uchar *dp, int n)
+{
+	Dir d;
+	Record *r;
+
+	if(strcmp(up->env->user, eve))
+		error(Eperm);
+	if(c->qid.type & QTDIR)
+		error(Eperm);
+	acquire();
+	r = getrec(PID(c->qid));
+	release();
+	if(r == nil)
+		error(Ethread);
+	n = convM2D(dp, n, &d, nil);
+	if(n == 0)
+		error(Eshortstat);
+	d.mode &= 0777;
+	/* TO DO: copy to c->aux->perm, once that exists */
+	return n;
+}
+
+static void
+profclose(Chan *c)
+{
+	USED(c);
+}
+
+static long
+profread(Chan *c, void *va, long n, vlong offset)
+{
+	int i;
+	Record *r;
+	char *a = va;
+
+	if(c->qid.type & QTDIR)
+		return devdirread(c, a, n, 0, 0, profgen);
+	acquire();
+	r = getrec(PID(c->qid));
+	release();
+	if(r == nil)
+		error(Ethread);
+	switch(QID(c->qid)){
+	case Qname:
+		return readstr(offset, va, n, r->name);
+	case Qpath:
+		return readstr(offset, va, n, r->path);
+	case Qhist:
+		i = (int)c->aux;
+		while(i < r->size && r->bucket[i] == 0)
+			i++;
+		if(i >= r->size)
+			return 0;
+		c->aux = (void*)(i+1);
+		if(n < 20)
+			error(Etoosmall);
+		return sprint(a, "%d %lud", i, r->bucket[i]);
+	case Qctl:
+		error(Eperm);
+	}
+	return 0;
+}
+
+static long
+profwrite(Chan *c, void *va, long n, vlong offset)
+{
+	int i;
+	char *a = va;
+	char buf[128], *fields[128];
+
+	USED(va);
+	USED(n);
+	USED(offset);
+
+	if(c->qid.type & QTDIR)
+		error(Eisdir);
+	switch(QID(c->qid)){
+	case Qctl:
+		if(n > sizeof(buf)-1)
+			n = sizeof(buf)-1;
+		memmove(buf, a, n);
+		buf[n] = 0;
+		i = getfields(buf, fields, nelem(fields), 1, " \t\n");
+		if(i > 0 && strcmp(fields[0], "module") == 0){
+			freepmods();
+			while(--i > 0)
+				addpmod(fields[i]);
+			return n;
+		}
+		if(i == 1){
+			if(strcmp(fields[0], "start") == 0){
+				if(profiler == Pnil) {
+					profiler = Psam;
+					if(!samplefn){
+						samplefn = 1;
+						kproc("prof", sampler, 0, 0);
+					}
+				}
+			}
+			else if(strcmp(fields[0], "startmp") == 0){
+				if(profiler == Pnil){
+					profiler = Pmem;
+					heapmonitor = memprof;
+				}
+			}
+			else if(strcmp(fields[0], "stop") == 0)
+				profiler = Pnil;
+			else if(strcmp(fields[0], "end") == 0){
+				profiler = Pnil;
+				freeprof();
+				interval = 100;
+			}
+			else
+				error(Ebadarg);
+		}
+		else if (i == 2){
+			if(strcmp(fields[0], "interval") == 0)
+				interval = strtoul(fields[1], nil, 0);
+			else if(strcmp(fields[0], "startcp") == 0){
+				Prog *p;
+
+				acquire();
+				p = progpid(strtoul(fields[1], nil, 0));
+				if(p == nil){
+					release();
+					return -1;
+				}
+				if(profiler == Pnil){
+					profiler = Pcov;
+					p->xec = cpxec;
+				}
+				release();
+			}
+			else
+				error(Ebadarg);
+		}
+		else
+			error(Ebadarg);
+		return n;
+	default:
+		error(Eperm);
+	}
+	return 0;
+}
+
+static Record*
+newmodule(Module *m, int vm, int scale, int origin)
+{
+	int dsize;
+	Record *r, **l;
+
+	if(!vm)
+		acquire();
+	if((m->compiled && m->pctab == nil) || m->prog == nil) {
+		if(!vm)
+			release();
+		return nil;
+	}
+/* print("newmodule %x %s %s %d %d %d\n", m, m->name, m->path, m->mtime, m->qid.path, m->qid.vers); */
+	if(m->compiled)
+		dsize = m->nprog * sizeof(r->bucket[0]);
+	else
+		dsize = (msize(m->prog)/sizeof(Inst)) * sizeof(r->bucket[0]);
+	dsize *= scale;
+	dsize += origin;
+	r = malloc(sizeof(Record)+dsize);
+	if(r == nil) {
+		if(!vm)
+			release();
+		return nil;
+	}
+
+	r->id = ++ids;
+	if(ids == (1<<8)-1)
+		ids = 0;
+	kstrdup(&r->name, m->name);
+	kstrdup(&r->path, m->path);
+	r->base = m->prog;
+	r->size = dsize/sizeof(r->bucket[0]);
+	// r->m = m;
+	r->mtime = m->mtime;
+	r->qid.path = m->qid.path;
+	r->qid.vers = m->qid.vers;
+	memset(r->bucket, 0, dsize);
+	r->link = profile.list;
+	profile.list = r;
+
+	l = &profile.hash[HASH(m->mtime)];
+	r->hash = *l;
+	*l = r;
+
+	if(!vm)
+		release();
+	return r;
+}
+
+static Record*
+mlook(Module *m, int vm, int scale, int origin)
+{
+	Record *r;
+
+	for(r = profile.hash[HASH(m->mtime)]; r; r = r->hash){
+		/* if(r->m == m){	/* bug - r->m could be old exited module */
+		if(r->mtime == m->mtime && r->qid.path == m->qid.path && r->qid.vers == m->qid.vers && strcmp(r->name, m->name) == 0 && strcmp(r->path, m->path) == 0){
+			r->base = m->prog;
+			return r;
+		}
+	}
+	if(pmods == nil || inpmods(m->name) || inpmods(m->path)){
+		if(0 && profiler == Pmem)
+			heapmonitor = nil;
+		r = newmodule(m, vm, scale, origin);
+		if(0 && profiler == Pmem)
+			heapmonitor = memprof;
+		return r;
+	}
+	return nil;
+}
+
+static void
+sampler(void* a)
+{
+	int i;
+	Module *m;
+	Record *r;
+	Inst *p;
+
+	USED(a);
+	for(;;) {
+		tsleep(&up->sleep, return0, nil, interval);
+		if(profiler != Psam)
+			break;
+		lock(&profile.l);
+		profile.time += interval;
+		if(R.M == H || (m = R.M->m) == nil){
+			unlock(&profile.l);
+			continue;
+		}
+		p = R.PC;
+		r = mlook(m, 0, 1, 0);
+		if(r == nil){
+			unlock(&profile.l);
+			continue;
+		}
+		if(m->compiled && m->pctab != nil)
+			p = pc2dispc(p, m);
+		if((i = p-r->base) >= 0 && i < r->size)
+			r->bucket[i]++;
+		unlock(&profile.l);
+	}
+	samplefn = 0;
+	pexit("", 0);
+}
+
+/*
+ *	coverage profiling
+ */
+
+static void
+cpxec(Prog *p)
+{
+	int op, i;
+	Module *m;
+	Record *r;
+	Prog *n;
+
+	R = p->R;
+	R.MP = R.M->MP;
+	R.IC = p->quanta;
+
+	if(p->kill != nil){
+		char *m;
+		m = p->kill;
+		p->kill = nil;
+		error(m);
+	}
+
+	if(R.M->compiled)
+		comvec();
+	else{
+		m = R.M->m;
+		r = profiler == Pcov ? mlook(m, 1, 1, 0) : nil;
+		do{
+			dec[R.PC->add]();
+			op = R.PC->op;
+			if(r != nil){
+				i = R.PC-r->base;
+				if(i >= 0 && i < r->size)
+					r->bucket[i]++;
+			}
+			R.PC++;
+			optab[op]();
+			if(op == ISPAWN || op == IMSPAWN){
+				n = delruntail(Pdebug);	// any state will do
+				n->xec = cpxec;
+				addrun(n);
+			}
+			if(m != R.M->m){
+				m = R.M->m;
+				r = profiler == Pcov ? mlook(m, 1, 1, 0) : nil;
+			}
+		}while(--R.IC != 0);
+	}
+
+	p->R = R;
+}
+
+/* memory profiling */
+
+enum{
+	Halloc,
+	Hfree,
+	Hgcfree,
+};
+
+#define MPAD	sizeof(double)
+
+static void
+memprof(int c, Heap *h, ulong n)
+{
+	int i, j, k;
+	ulong kk, *b;
+	Module *m;
+	Record *r;
+	Inst *p;
+
+/* print("%d %x %uld\n", c, h, n); */
+	USED(h);
+	if(profiler != Pmem){
+		heapmonitor = nil;
+		return;
+	}
+	lock(&profile.l);
+	m = nil;
+	if(c != Hgcfree && (R.M == H || (m = R.M->m) == nil)){
+		unlock(&profile.l);
+		return;
+	}
+	j = n;
+	if(c == 0 || c == 4){		/* heap or main allocation */
+		p = R.PC;
+		if(m->compiled && m->pctab != nil)
+			p = pc2dispc(p, m);
+		if((r = mlook(m, 1, 2, 2)) == nil){
+			unlock(&profile.l);
+			return;
+		}
+		i = p-r->base;
+		k = (r->id<<24) | i;
+		if(c == 0){
+			h->hprof = k;
+			k = sizeof(Heap);
+		}
+		else{
+			*(ulong*)h = k;
+			k = MPAD;
+		}
+		/* 31 is pool quanta - dependency on alloc.c */
+		j = ((j+k+BHDRSIZE+31)&~31) - (k+BHDRSIZE);
+	}
+	else{
+		/* c == 1 is ref count free */
+		/* c == 2 is gc free */
+		/* c == 3 is main free */
+		if(c == 3)
+			k = *(ulong*)h;
+		else
+			k = h->hprof;
+		if((r = getrec(k>>24)) == nil){
+			unlock(&profile.l);
+			return;
+		}
+		i = k&0xffffff;
+		if(c == 3)
+			j = msize(h)-MPAD;
+		else
+			j = hmsize(h)-sizeof(Heap);
+		j = -j;
+	}
+	i = 2*(i+1);
+	b = r->bucket;
+	if(i >= 0 && i < r->size){
+		if(0){
+			if(c == 1){
+				b[0] -= j;
+				b[i] -= j;
+			}
+			else if(c == 2){
+				b[1] -= j;
+				b[i+1] -= j;
+			}
+		}
+		else{
+			b[0] += j;
+			if((int)b[0] < 0)
+				b[0] = 0;
+			b[i] += j;
+			if((int)b[i] < 0)
+				b[i] = 0;
+			if(j > 0){
+				if((kk = b[0]) > b[1])
+					b[1] = kk;
+				if((kk = b[i]) > b[i+1])
+					b[i+1] = kk;
+			}
+		}
+	}
+	unlock(&profile.l);
+}
+
+Dev profdevtab = {
+	'P',
+	"prof",
+
+	devreset,
+	devinit,
+	devshutdown,
+	profattach,
+	profwalk,
+	profstat,
+	profopen,
+	devcreate,
+	profclose,
+	profread,
+	devbread,
+	profwrite,
+	devbwrite,
+	devremove,
+	profwstat
+};
--- /dev/null
+++ b/os/port/devprog.c
@@ -1,0 +1,1508 @@
+#include	"u.h"
+#include	"../port/lib.h"
+#include	"mem.h"
+#include	"dat.h"
+#include	"fns.h"
+#include	"error.h"
+#include	<interp.h>
+#include	<isa.h>
+#include	"runt.h"
+
+/*
+ * Enable the heap device for environments that allow debugging =>
+ * Must be 1 for a production environment.
+ */
+int	SECURE = 0;
+
+enum
+{
+	Qdir,
+	Qctl,
+	Qdbgctl,
+	Qheap,
+	Qns,
+	Qnsgrp,
+	Qpgrp,
+	Qstack,
+	Qstatus,
+	Qtext,
+	Qwait,
+	Qfd,
+	Qexception,
+};
+
+/*
+ * must be in same order as enum
+ */
+Dirtab progdir[] =
+{
+	"ctl",		{Qctl},		0,			0200,
+	"dbgctl",	{Qdbgctl},	0,			0600,
+	"heap",		{Qheap},	0,			0600,
+	"ns",		{Qns},		0,			0400,
+	"nsgrp",	{Qnsgrp},	0,			0444,
+	"pgrp",		{Qpgrp},	0,			0444,
+	"stack",	{Qstack},	0,			0400,
+	"status",	{Qstatus},	0,			0444,
+	"text",		{Qtext},	0,			0000,
+	"wait",		{Qwait},	0,			0400,
+	"fd",		{Qfd},		0,			0400,
+	"exception",	{Qexception},	0,	0400,
+};
+
+enum
+{
+	CMkill,
+	CMkillgrp,
+	CMrestricted,
+	CMexceptions,
+	CMprivate
+};
+
+static
+Cmdtab progcmd[] = {
+	CMkill,	"kill",	1,
+	CMkillgrp,	"killgrp",	1,
+	CMrestricted, "restricted", 1,
+	CMexceptions, "exceptions", 2,
+	CMprivate, "private",	1,
+};
+
+enum
+{
+	CDstep,
+	CDtoret,
+	CDcont,
+	CDstart,
+	CDstop,
+	CDunstop,
+	CDmaim,
+	CDbpt
+};
+
+static
+Cmdtab progdbgcmd[] = {
+	CDstep,	"step",	0,	/* known below to be first, to cope with stepN */
+	CDtoret,	"toret",	1,
+	CDcont,	"cont",	1,
+	CDstart,	"start",	1,
+	CDstop,	"stop",	1,
+	CDunstop,	"unstop",	1,
+	CDmaim,	"maim",	1,
+	CDbpt,	"bpt",	4,
+};
+
+typedef struct Heapqry Heapqry;
+struct Heapqry
+{
+	char	fmt;
+	ulong	addr;
+	ulong	module;
+	int	count;
+};
+
+typedef struct Bpt	Bpt;
+
+struct Bpt
+{
+	Bpt	*next;
+	int	pc;
+	char	*file;
+	char	path[1];
+};
+
+typedef struct Progctl Progctl;
+struct Progctl
+{
+	Rendez	r;
+	int	ref;
+	Proc	*debugger;	/* waiting for dbgxec */
+	char	*msg;		/* reply from dbgxec */
+	int	step;		/* instructions to try */
+	int	stop;		/* stop running the program */
+	Bpt*	bpts;		/* active breakpoints */
+	Queue*	q;		/* status queue */
+};
+
+#define	QSHIFT		4		/* location in qid of pid */
+#define	QID(q)		(((ulong)(q).path&0x0000000F)>>0)
+#define QPID(pid)	(((pid)<<QSHIFT))
+#define	PID(q)		((q).vers)
+#define PATH(q)		((ulong)(q).path&~((1<<QSHIFT)-1))
+
+static char *progstate[] =			/* must correspond to include/interp.h */
+{
+	"alt",				/* blocked in alt instruction */
+	"send",				/* waiting to send */
+	"recv",				/* waiting to recv */
+	"debug",			/* debugged */
+	"ready",			/* ready to be scheduled */
+	"release",			/* interpreter released */
+	"exiting",			/* exit because of kill or error */
+	"broken",			/* thread crashed */
+};
+
+static	void	dbgstep(Progctl*, Prog*, int);
+static	void	dbgstart(Prog*);
+static	void	freebpts(Bpt*);
+static	Bpt*	delbpt(Bpt*, char*, int);
+static	Bpt*	setbpt(Bpt*, char*, int);
+static	void	mntscan(Mntwalk*, Pgrp*);
+extern	Type	*Trdchan;
+extern	Type	*Twrchan;
+extern	Module*	modules;
+static  char 	Emisalign[] = "misaligned address";
+
+static int
+proggen(Chan *c, char *name, Dirtab *tab, int, int s, Dir *dp)
+{
+	Qid qid;
+	Prog *p;
+	char *e;
+	Osenv *o;
+	ulong pid, path, perm, len;
+
+	if(s == DEVDOTDOT){
+		mkqid(&qid, Qdir, 0, QTDIR);
+		devdir(c, qid, "#p", 0, eve, DMDIR|0555, dp);
+		return 1;
+	}
+
+	if((ulong)c->qid.path == Qdir) {
+		if(name != nil){
+			/* ignore s and use name to find pid */
+			pid = strtoul(name, &e, 0);
+			if(pid == 0 || *e != '\0')
+				return -1;
+			acquire();
+			p = progpid(pid);
+			if(p == nil){
+				release();
+				return -1;
+			}
+		}else{
+			acquire();
+			p = progn(s);
+			if(p == nil) {
+				release();
+				return -1;
+			}
+			pid = p->pid;
+		}
+		o = p->osenv;
+		sprint(up->genbuf, "%lud", pid);
+		if(name != nil && strcmp(name, up->genbuf) != 0){
+			release();
+			return -1;
+		}
+		mkqid(&qid, pid<<QSHIFT, pid, QTDIR);
+		devdir(c, qid, up->genbuf, 0, o->user, DMDIR|0555, dp);
+		release();
+		return 1;
+	}
+
+	if(s >= nelem(progdir))
+		return -1;
+	tab = &progdir[s];
+	path = PATH(c->qid);
+
+	acquire();
+	p = progpid(PID(c->qid));
+	if(p == nil) {
+		release();
+		return -1;
+	}
+
+	o = p->osenv;
+
+	perm = tab->perm;
+	if((perm & 7) == 0)
+		perm = (perm|(perm>>3)|(perm>>6)) & o->pgrp->progmode;
+
+	len = tab->length;
+	mkqid(&qid, path|tab->qid.path, c->qid.vers, QTFILE);
+	devdir(c, qid, tab->name, len, o->user, perm, dp);
+	release();
+	return 1;
+}
+
+static Chan*
+progattach(char *spec)
+{
+	return devattach('p', spec);
+}
+
+static Walkqid*
+progwalk(Chan *c, Chan *nc, char **name, int nname)
+{
+	return devwalk(c, nc, name, nname, 0, 0, proggen);
+}
+
+static int
+progstat(Chan *c, uchar *db, int n)
+{
+	return devstat(c, db, n, 0, 0, proggen);
+}
+
+static Chan*
+progopen(Chan *c, int omode)
+{
+	Prog *p;
+	Osenv *o;
+	Progctl *ctl;
+	int perm;
+
+	if(c->qid.type & QTDIR)
+		return devopen(c, omode, 0, 0, proggen);
+
+	acquire();
+	if (waserror()) {
+		release();
+		nexterror();
+	}
+	p = progpid(PID(c->qid));
+	if(p == nil)
+		error(Ethread);
+
+	o = p->osenv;
+	perm = progdir[QID(c->qid)-1].perm;
+	if((perm & 7) == 0)
+		perm = (perm|(perm>>3)|(perm>>6)) & o->pgrp->progmode;
+	devpermcheck(o->user, perm, omode);
+	omode = openmode(omode);
+
+	switch(QID(c->qid)){
+	default:
+		error(Egreg);
+	case Qnsgrp:
+	case Qpgrp:
+	case Qtext:
+	case Qstatus:
+	case Qstack:
+	case Qctl:
+	case Qfd:
+	case Qexception:
+		break;
+	case Qwait:
+		c->aux = qopen(1024, Qmsg, nil, nil);
+		if(c->aux == nil)
+			error(Enomem);
+		o->childq = c->aux;
+		break;
+	case Qns:
+		c->aux = malloc(sizeof(Mntwalk));
+		if(c->aux == nil)
+			error(Enomem);
+		break;
+	case Qheap:
+		if(SECURE || p->group->flags&Pprivatemem || omode != ORDWR)
+			error(Eperm);
+		c->aux = malloc(sizeof(Heapqry));
+		if(c->aux == nil)
+			error(Enomem);
+		break;
+	case Qdbgctl:
+		if(SECURE || p->group->flags&Pprivatemem || omode != ORDWR)
+			error(Eperm);
+		ctl = malloc(sizeof(Progctl));
+		if(ctl == nil)
+			error(Enomem);
+		ctl->q = qopen(1024, Qmsg, nil, nil);
+		if(ctl->q == nil) {
+			free(ctl);
+			error(Enomem);
+		}
+		ctl->bpts = nil;
+		ctl->ref = 1;
+		c->aux = ctl;
+		break;
+	}
+	if(p->state != Pexiting)
+		c->qid.vers = p->pid;
+
+	poperror();
+	release();
+	c->offset = 0;
+	c->mode = omode;
+	c->flag |= COPEN;
+	return c;
+}
+
+static int
+progwstat(Chan *c, uchar *db, int n)
+{
+	Dir d;
+	Prog *p;
+	char *u;
+	Osenv *o;
+
+	if(c->qid.type&QTDIR)
+		error(Eperm);
+	acquire();
+	p = progpid(PID(c->qid));
+	if(p == nil) {
+		release();
+		error(Ethread);
+	}
+
+	u = up->env->user;
+	o = p->osenv;
+	if(strcmp(u, o->user) != 0 && strcmp(u, eve) != 0) {
+		release();
+		error(Eperm);
+	}
+
+	n = convM2D(db, n, &d, nil);
+	if(n == 0){
+		release();
+		error(Eshortstat);
+	}
+	if(d.mode != ~0UL)
+		o->pgrp->progmode = d.mode&0777;
+	release();
+	return n;
+}
+
+static void
+closedbgctl(Progctl *ctl, Prog *p)
+{
+	Osenv *o;
+
+	if(ctl->ref-- > 1)
+		return;
+	freebpts(ctl->bpts);
+	if(p != nil){
+		o = p->osenv;
+		if(o->debug == ctl){
+			o->debug = nil;
+			p->xec = xec;
+		}
+	}
+	qfree(ctl->q);
+	free(ctl);
+}
+
+static void
+progclose(Chan *c)
+{
+	int i;
+	Prog *f;
+	Osenv *o;
+	Progctl *ctl;
+
+	switch(QID(c->qid)) {
+	case Qns:
+	case Qheap:
+		free(c->aux);
+		break;
+	case Qdbgctl:
+		if((c->flag & COPEN) == 0)
+			return;
+		ctl = c->aux;
+		acquire();
+		closedbgctl(ctl, progpid(PID(c->qid)));
+		release();
+		break;
+	case Qwait:
+		acquire();
+		i = 0;
+		for(;;) {
+			f = progn(i++);
+			if(f == nil)
+				break;
+			o = f->osenv;
+			if(o->waitq == c->aux)
+				o->waitq = nil;
+			if(o->childq == c->aux)
+				o->childq = nil;
+		}
+		release();
+		qfree(c->aux);
+	}
+}
+
+static int
+progsize(Prog *p)
+{
+	int size;
+	Frame *f;
+	uchar *fp;
+	Modlink *m;
+
+	m = p->R.M;
+	size = 0;
+	if(m->MP != H)
+		size += hmsize(D2H(m->MP));
+	if(m->prog != nil)
+		size += msize(m->prog);
+
+	fp = p->R.FP;
+	while(fp != nil) {
+		f = (Frame*)fp;
+		fp = f->fp;
+		if(f->mr != nil) {
+			if(f->mr->MP != H)
+				size += hmsize(D2H(f->mr->MP));
+			if(f->mr->prog != nil)
+				size += msize(f->mr->prog);
+		}
+		if(f->t == nil)
+			size += msize(SEXTYPE(f));
+	}
+	return size/1024;
+}
+
+static long
+progoffset(long offset, char *va, int *np)
+{
+	if(offset > 0) {
+		offset -= *np;
+		if(offset < 0) {
+			memmove(va, va+*np+offset, -offset);
+			*np = -offset;
+		}
+		else
+			*np = 0;
+	}
+	return offset;
+}
+
+static int
+progqidwidth(Chan *c)
+{
+	char buf[32];
+
+	return sprint(buf, "%lud", c->qid.vers);
+}
+
+int
+progfdprint(Chan *c, int fd, int w, char *s, int ns)
+{
+	int n;
+
+	if(w == 0)
+		w = progqidwidth(c);
+	n = snprint(s, ns, "%3d %.2s %C %4ld (%.16llux %*lud %.2ux) %5ld %8lld %s\n",
+		fd,
+		&"r w rw"[(c->mode&3)<<1],
+		devtab[c->type]->dc, c->dev,
+		c->qid.path, w, c->qid.vers, c->qid.type,
+		c->iounit, c->offset, c->name->s);
+	return n;
+}
+
+static int
+progfds(Osenv *o, char *va, int count, long offset)
+{
+	Fgrp *f;
+	Chan *c;
+	int n, i, w, ww;
+
+	f = o->fgrp;	/* f is not locked because we've acquired */
+	n = readstr(0, va, count, o->pgrp->dot->name->s);
+	n += snprint(va+n, count-n, "\n");
+	offset = progoffset(offset, va, &n);
+	/* compute width of qid.path */
+	w = 0;
+	for(i = 0; i <= f->maxfd; i++) {
+		c = f->fd[i];
+		if(c == nil)
+			continue;
+		ww = progqidwidth(c);
+		if(ww > w)
+			w = ww;
+	}
+	for(i = 0; i <= f->maxfd; i++) {
+		c = f->fd[i];
+		if(c == nil)
+			continue;
+		n += progfdprint(c, i, w, va+n, count-n);
+		offset = progoffset(offset, va, &n);
+	}
+	return n;
+}
+
+Inst *
+pc2dispc(Inst *pc, Module *mod)
+{
+	ulong l, u, m, v;
+	ulong *tab = mod->pctab;
+
+	v = (ulong)pc - (ulong)mod->prog;
+	l = 0;
+	u = mod->nprog-1;
+	while(l < u){
+		m = (l+u+1)/2;
+		if(tab[m] < v)
+			l = m;
+		else if(tab[m] > v)
+			u = m-1;
+		else
+			l = u = m;
+	}
+	if(l == u && tab[u] <= v && u != mod->nprog-1 && tab[u+1] > v)
+		return &mod->prog[u];
+	return 0;
+}
+
+static int
+progstack(REG *reg, int state, char *va, int count, long offset)
+{
+	int n;
+	Frame *f;
+	Inst *pc;
+	uchar *fp;
+	Modlink *m;
+
+	n = 0;
+	m = reg->M;
+	fp = reg->FP;
+	pc = reg->PC;
+
+	/*
+	 * all states other than debug and ready block,
+	 * but interp has already advanced the PC
+	 */
+	if(!m->compiled && state != Pready && state != Pdebug && pc > m->prog)
+		pc--;
+	if(m->compiled && m->m->pctab != nil)
+		pc = pc2dispc(pc, m->m);
+
+	while(fp != nil) {
+		f = (Frame*)fp;
+		n += snprint(va+n, count-n, "%.8lux %.8lux %.8lux %.8lux %d %s\n",
+				(ulong)f,		/* FP */
+				(ulong)(pc - m->prog),	/* PC in dis instructions */
+				(ulong)m->MP,		/* MP */
+				(ulong)m->prog,	/* Code for module */
+				m->compiled && m->m->pctab == nil,	/* True if native assembler: fool stack utility for now */
+				m->m->path);	/* File system path */
+
+		if(offset > 0) {
+			offset -= n;
+			if(offset < 0) {
+				memmove(va, va+n+offset, -offset);
+				n = -offset;
+			}
+			else
+				n = 0;
+		}
+
+		pc = f->lr;
+		fp = f->fp;
+		if(f->mr != nil)
+			m = f->mr;
+		if(!m->compiled)
+			pc--;
+		else if(m->m->pctab != nil)
+			pc = pc2dispc(pc, m->m)-1;
+	}
+	return n;
+}
+
+static int
+calldepth(REG *reg)
+{
+	int n;
+	uchar *fp;
+
+	n = 0;
+	for(fp = reg->FP; fp != nil; fp = ((Frame*)fp)->fp)
+		n++;
+	return n;
+}
+
+static int
+progheap(Heapqry *hq, char *va, int count, ulong offset)
+{
+	WORD *w;
+	void *p;
+	List *hd;
+	Array *a;
+	char *fmt, *str;
+	Module *m;
+	Modlink *ml;
+	Channel *c;
+	ulong addr;
+	String *ss;
+	union { REAL r; LONG l; WORD w[2]; } rock;
+	int i, s, n, len, signed_off;
+	Type *t;
+
+	n = 0;
+	s = 0;
+	signed_off = offset;
+	addr = hq->addr;
+	for(i = 0; i < hq->count; i++) {
+		switch(hq->fmt) {
+		case 'W':
+			if(addr & 3)
+				return -1;
+			n += snprint(va+n, count-n, "%d\n", *(WORD*)addr);
+			s = sizeof(WORD);
+			break;
+		case 'B':
+			n += snprint(va+n, count-n, "%d\n", *(BYTE*)addr);
+			s = sizeof(BYTE);
+			break;
+		case 'V':
+			if(addr & 3)
+				return -1;
+			w = (WORD*)addr;
+			rock.w[0] = w[0];
+			rock.w[1] = w[1];
+			n += snprint(va+n, count-n, "%lld\n", rock.l);
+			s = sizeof(LONG);
+			break;
+		case 'R':
+			if(addr & 3)
+				return -1;
+			w = (WORD*)addr;
+			rock.w[0] = w[0];
+			rock.w[1] = w[1];
+			n += snprint(va+n, count-n, "%g\n", rock.r);
+			s = sizeof(REAL);
+			break;
+		case 'I':
+			if(addr & 3)
+				return -1;
+			for(m = modules; m != nil; m = m->link)
+				if(m == (Module*)hq->module)
+					break;
+			if(m == nil)
+				error(Ebadctl);
+			addr = (ulong)(m->prog+addr);
+			n += snprint(va+n, count-n, "%D\n", (Inst*)addr);
+			s = sizeof(Inst);
+			break;
+		case 'P':
+			if(addr & 3)
+				return -1;
+			p = *(void**)addr;
+			fmt = "nil\n";
+			if(p != H)
+				fmt = "%lux\n";
+			n += snprint(va+n, count-n, fmt, p);
+			s = sizeof(WORD);
+			break;
+		case 'L':
+			if(addr & 3)
+				return -1;
+			hd = *(List**)addr;
+			if(hd == H || D2H(hd)->t != &Tlist)
+				return -1;
+			n += snprint(va+n, count-n, "%lux.%lux\n", (ulong)&hd->tail, (ulong)hd->data);
+			s = sizeof(WORD);
+			break;
+		case 'A':
+			if(addr & 3)
+				return -1;
+			a = *(Array**)addr;
+			if(a == H)
+				n += snprint(va+n, count-n, "nil\n");
+			else {
+				if(D2H(a)->t != &Tarray)
+					return -1;
+				n += snprint(va+n, count-n, "%d.%lux\n", a->len, (ulong)a->data);
+			}
+			s = sizeof(WORD);
+			break;
+		case 'C':
+			if(addr & 3)
+				return -1;
+			ss = *(String**)addr;
+			if(ss == H)
+				ss = &snil;
+			else
+			if(D2H(ss)->t != &Tstring)
+				return -1;
+			n += snprint(va+n, count-n, "%d.", abs(ss->len));
+			str = string2c(ss);
+			len = strlen(str);
+			if(count-n < len)
+				len = count-n;
+			if(len > 0) {
+				memmove(va+n, str, len);
+				n += len;
+			}
+			break;
+		case 'M':
+			if(addr & 3)
+				return -1;
+			ml = *(Modlink**)addr;
+			fmt = ml == H ? "nil\n" : "%lux\n";
+			n += snprint(va+n, count-n, fmt, ml->MP);
+			s = sizeof(WORD);
+			break;
+		case 'c':
+			if(addr & 3)
+				return -1;
+			c = *(Channel**)addr;
+			if(c == H)
+				n += snprint(va+n, count-n, "nil\n");
+			else{
+				t = D2H(c)->t;
+				if(t != &Tchannel && t != Trdchan && t != Twrchan)
+					return -1;
+				if(c->buf == H)
+					n += snprint(va+n, count-n, "0.%lux\n", (ulong)c);
+				else
+					n += snprint(va+n, count-n, "%d.%lux.%d.%d\n", c->buf->len, (ulong)c->buf->data, c->front, c->size);
+			}
+			break;
+			
+		}
+		addr += s;
+		if(signed_off > 0) {
+			signed_off -= n;
+			if(signed_off < 0) {
+				memmove(va, va+n+signed_off, -signed_off);
+				n = -signed_off;
+			}
+			else
+				n = 0;
+		}
+	}
+	return n;
+}
+
+WORD
+modstatus(REG *r, char *ptr, int len)
+{
+	Inst *PC;
+	Frame *f;
+
+	if(r->M->m->name[0] == '$') {
+		f = (Frame*)r->FP;
+		snprint(ptr, len, "%s[%s]", f->mr->m->name, r->M->m->name);
+		if(f->mr->compiled)
+			return (WORD)f->lr;
+		return f->lr - f->mr->prog;
+	}
+	memmove(ptr, r->M->m->name, len);
+	if(r->M->compiled)
+		return (WORD)r->PC;
+	PC = r->PC;
+	/* should really check for blocked states */
+	if(PC > r->M->prog)
+		PC--;
+	return PC - r->M->prog;
+}
+
+static void
+int2flag(int flag, char *s)
+{
+	if(flag == 0){
+		*s = '\0';
+		return;
+	}
+	*s++ = '-';
+	if(flag & MAFTER)
+		*s++ = 'a';
+	if(flag & MBEFORE)
+		*s++ = 'b';
+	if(flag & MCREATE)
+		*s++ = 'c';
+	if(flag & MCACHE)
+		*s++ = 'C';
+	*s = '\0';
+}
+
+static char*
+progtime(ulong msec, char *buf, char *ebuf)
+{
+	int tenths, sec;
+
+	tenths = msec/100;
+	sec = tenths/10;
+	seprint(buf, ebuf, "%4d:%2.2d.%d", sec/60, sec%60, tenths%10);
+	return buf;
+}
+
+static long
+progread(Chan *c, void *va, long n, vlong offset)
+{
+	int i;
+	Prog *p;
+	Osenv *o;
+	Mntwalk *mw;
+	ulong grpid;
+	char *a = va;
+	Progctl *ctl;
+	char mbuf[64], timebuf[12];
+	char flag[10];
+
+	if(c->qid.type & QTDIR)
+		return devdirread(c, a, n, 0, 0, proggen);
+
+	switch(QID(c->qid)){
+	case Qdbgctl:
+		ctl = c->aux;
+		return qread(ctl->q, va, n);
+	case Qstatus:
+		acquire();
+		p = progpid(PID(c->qid));
+		if(p == nil || p->state == Pexiting || p->R.M == H) {
+			release();
+			snprint(up->genbuf, sizeof(up->genbuf), "%8lud %8d %10s %s %10s %5dK %s",
+				PID(c->qid),
+				0,
+				eve,
+				progtime(0, timebuf, timebuf+sizeof(timebuf)),
+				progstate[Pexiting],
+				0,
+				"[$Sys]");
+			return readstr(offset, va, n, up->genbuf);
+		}
+		modstatus(&p->R, mbuf, sizeof(mbuf));
+		o = p->osenv;
+		snprint(up->genbuf, sizeof(up->genbuf), "%8d %8d %10s %s %10s %5dK %s",
+			p->pid,
+			p->group!=nil? p->group->id: 0,
+			o->user,
+			progtime(TK2MS(p->ticks), timebuf, timebuf+sizeof(timebuf)),
+			progstate[p->state],
+			progsize(p),
+			mbuf);
+		release();
+		return readstr(offset, va, n, up->genbuf);
+	case Qwait:
+		return qread(c->aux, va, n);
+	case Qns:
+		acquire();
+		if(waserror()){
+			release();
+			nexterror();
+		}
+		p = progpid(PID(c->qid));
+		if(p == nil)
+			error(Ethread);
+		mw = c->aux;
+		if(mw->cddone){
+			poperror();
+			release();
+			return 0;
+		}
+		o = p->osenv;
+		mntscan(mw, o->pgrp);
+		if(mw->mh == 0) {
+			mw->cddone = 1;
+			i = snprint(a, n, "cd %s\n", o->pgrp->dot->name->s);
+			poperror();
+			release();
+			return i;
+		}
+		int2flag(mw->cm->mflag, flag);
+		if(strcmp(mw->cm->to->name->s, "#M") == 0){
+			i = snprint(a, n, "mount %s %s %s %s\n", flag,
+				mw->cm->to->mchan->name->s,
+				mw->mh->from->name->s, mw->cm->spec? mw->cm->spec : "");
+		}else
+			i = snprint(a, n, "bind %s %s %s\n", flag,
+				mw->cm->to->name->s, mw->mh->from->name->s);
+		poperror();
+		release();
+		return i;
+	case Qnsgrp:
+		acquire();
+		p = progpid(PID(c->qid));
+		if(p == nil) {
+			release();
+			error(Ethread);
+		}
+		grpid = ((Osenv *)p->osenv)->pgrp->pgrpid;
+		release();
+		return readnum(offset, va, n, grpid, NUMSIZE);
+	case Qpgrp:
+		acquire();
+		p = progpid(PID(c->qid));
+		if(p == nil) {
+			release();
+			error(Ethread);
+		}
+		grpid = p->group!=nil? p->group->id: 0;
+		release();
+		return readnum(offset, va, n, grpid, NUMSIZE);
+	case Qstack:
+		acquire();
+		p = progpid(PID(c->qid));
+		if(p == nil || p->state == Pexiting) {
+			release();
+			error(Ethread);
+		}
+		if(p->state == Pready) {
+			release();
+			error(Estopped);
+		}
+		n = progstack(&p->R, p->state, va, n, offset);
+		release();
+		return n;
+	case Qheap:
+		acquire();
+		if(waserror()){
+			release();
+			nexterror();
+		}
+		n = progheap(c->aux, va, n, offset);
+		if(n == -1)
+			error(Emisalign);
+		poperror();
+		release();
+		return n;
+	case Qfd:
+		acquire();
+		if(waserror()) {
+			release();
+			nexterror();
+		}
+		p = progpid(PID(c->qid));
+		if(p == nil)
+			error(Ethread);
+		o = p->osenv;
+		n = progfds(o, va, n, offset);
+		poperror();
+		release();
+		return n;
+	case Qexception:
+		acquire();
+		p = progpid(PID(c->qid));
+		if(p == nil) {
+			release();
+			error(Ethread);
+		}
+		if(p->exstr == nil)
+			up->genbuf[0] = 0;
+		else
+			snprint(up->genbuf, sizeof(up->genbuf), p->exstr);
+		release();
+		return readstr(offset, va, n, up->genbuf);
+	}
+	error(Egreg);
+	return 0;
+}
+
+static void
+mntscan(Mntwalk *mw, Pgrp *pg)
+{
+	Mount *t;
+	Mhead *f;
+	int nxt, i;
+	ulong last, bestmid;
+
+	rlock(&pg->ns);
+
+	nxt = 0;
+	bestmid = ~0;
+
+	last = 0;
+	if(mw->mh)
+		last = mw->cm->mountid;
+
+	for(i = 0; i < MNTHASH; i++) {
+		for(f = pg->mnthash[i]; f; f = f->hash) {
+			for(t = f->mount; t; t = t->next) {
+				if(mw->mh == 0 ||
+				  (t->mountid > last && t->mountid < bestmid)) {
+					mw->cm = t;
+					mw->mh = f;
+					bestmid = mw->cm->mountid;
+					nxt = 1;
+				}
+			}
+		}
+	}
+	if(nxt == 0)
+		mw->mh = 0;
+
+	runlock(&pg->ns);
+}
+
+static long
+progwrite(Chan *c, void *va, long n, vlong offset)
+{
+	Prog *p, *f;
+	Heapqry *hq;
+	char buf[128];
+	Progctl *ctl;
+	char *b;
+	int i, pc;
+	Cmdbuf *cb;
+	Cmdtab *ct;
+
+	USED(offset);
+	USED(va);
+
+	if(c->qid.type & QTDIR)
+		error(Eisdir);
+
+	acquire();
+	if(waserror()) {
+		release();
+		nexterror();
+	}
+	p = progpid(PID(c->qid));
+	if(p == nil)
+		error(Ethread);
+
+	switch(QID(c->qid)){
+	case Qctl:
+		cb = parsecmd(va, n);
+		if(waserror()){
+			free(cb);
+			nexterror();
+		}
+		ct = lookupcmd(cb, progcmd, nelem(progcmd));
+		switch(ct->index){
+		case CMkillgrp:
+			killgrp(p, "killed");
+			break;
+		case CMkill:
+			killprog(p, "killed");
+			break;
+		case CMrestricted:
+			p->flags |= Prestrict;
+			break;
+		case CMexceptions:
+			if(p->group->id != p->pid)
+				error(Eperm);
+			if(strcmp(cb->f[1], "propagate") == 0)
+				p->flags |= Ppropagate;
+			else if(strcmp(cb->f[1], "notifyleader") == 0)
+				p->flags |= Pnotifyleader;
+			else
+				error(Ebadctl);
+			break;
+		case CMprivate:
+			p->group->flags |= Pprivatemem;
+			break;
+		}
+		poperror();
+		free(cb);
+		break;
+	case Qdbgctl:
+		cb = parsecmd(va, n);
+		if(waserror()){
+			free(cb);
+			nexterror();
+		}
+		if(cb->nf == 1 && strncmp(cb->f[0], "step", 4) == 0)
+			ct = progdbgcmd;
+		else
+			ct = lookupcmd(cb, progdbgcmd, nelem(progdbgcmd));
+		switch(ct->index){
+		case CDstep:
+			if(cb->nf == 1)
+				i = strtoul(cb->f[0]+4, nil, 0);
+			else
+				i = strtoul(cb->f[1], nil, 0);
+			dbgstep(c->aux, p, i);
+			break;
+		case CDtoret:
+			f = currun();
+			i = calldepth(&p->R);
+			while(f->kill == nil) {
+				dbgstep(c->aux, p, 1024);
+				if(i > calldepth(&p->R))
+					break;
+			}
+			break;
+		case CDcont:
+			f = currun();
+			while(f->kill == nil)
+				dbgstep(c->aux, p, 1024);
+			break;
+		case CDstart:
+			dbgstart(p);
+			break;
+		case CDstop:
+			ctl = c->aux;
+			ctl->stop = 1;
+			break;
+		case CDunstop:
+			ctl = c->aux;
+			ctl->stop = 0;
+			break;
+		case CDbpt:
+			pc = strtoul(cb->f[3], nil, 10);
+			ctl = c->aux;
+			if(strcmp(cb->f[1], "set") == 0)
+				ctl->bpts = setbpt(ctl->bpts, cb->f[2], pc);
+			else if(strcmp(cb->f[1], "del") == 0)
+				ctl->bpts = delbpt(ctl->bpts, cb->f[2], pc);
+			else
+				error(Ebadctl);
+			break;
+		case CDmaim:
+			p->kill = "maim";
+			break;
+		}
+		poperror();
+		free(cb);
+		break;
+	case Qheap:
+		/*
+		 * Heap query:
+		 *	addr.Fn
+		 *	pc+module.In
+		 */
+		i = n;
+		if(i > sizeof(buf)-1)
+			i = sizeof(buf)-1;
+		memmove(buf, va, i);
+		buf[i] = '\0';
+		hq = c->aux;
+		hq->addr = strtoul(buf, &b, 0);
+		if(*b == '+')
+			hq->module = strtoul(b, &b, 0);
+		if(*b++ != '.')
+			error(Ebadctl);
+		hq->fmt = *b++;
+		hq->count = strtoul(b, nil, 0);
+		break;
+	default:
+		print("unknown qid in procwrite\n");
+		error(Egreg);
+	}
+	poperror();
+	release();
+	return n;
+}
+
+static Bpt*
+setbpt(Bpt *bpts, char *path, int pc)
+{
+	int n;
+	Bpt *b;
+
+	n = strlen(path);
+	b = mallocz(sizeof *b + n, 0);
+	if(b == nil)
+		return bpts;
+	b->pc = pc;
+	memmove(b->path, path, n+1);
+	b->file = b->path;
+	path = strrchr(b->path, '/');
+	if(path != nil)
+		b->file = path + 1;
+	b->next = bpts;
+	return b;
+}
+
+static Bpt*
+delbpt(Bpt *bpts, char *path, int pc)
+{
+	Bpt *b, **last;
+
+	last = &bpts;
+	for(b = bpts; b != nil; b = b->next){
+		if(b->pc == pc && strcmp(b->path, path) == 0) {
+			*last = b->next;
+			free(b);
+			break;
+		}
+		last = &b->next;
+	}
+	return bpts;
+}
+
+static void
+freebpts(Bpt *b)
+{
+	Bpt *next;
+
+	for(; b != nil; b = next){
+		next = b->next;
+		free(b);
+	}
+}
+
+static void
+telldbg(Progctl *ctl, char *msg)
+{
+	kstrcpy(ctl->msg, msg, ERRMAX);
+	ctl->debugger = nil;
+	wakeup(&ctl->r);
+}
+
+static void
+dbgstart(Prog *p)
+{
+	Osenv *o;
+	Progctl *ctl;
+
+	o = p->osenv;
+	ctl = o->debug;
+	if(ctl != nil && ctl->debugger != nil)
+		error("prog debugged");
+	if(p->state == Pdebug)
+		addrun(p);
+	o->debug = nil;
+	p->xec = xec;
+}
+
+static int
+xecdone(void *vc)
+{
+	Progctl *ctl = vc;
+
+	return ctl->debugger == nil;
+}
+
+static void
+dbgstep(Progctl *vctl, Prog *p, int n)
+{
+	Osenv *o;
+	Progctl *ctl;
+	char buf[ERRMAX+20], *msg;
+
+	if(p == currun())
+		error("cannot step yourself");
+
+	if(p->state == Pbroken)
+		error("prog broken");
+
+	ctl = vctl;
+	if(ctl->debugger != nil)
+		error("prog already debugged");
+
+	o = p->osenv;
+	if(o->debug == nil) {
+		o->debug = ctl;
+		p->xec = dbgxec;
+	}else if(o->debug != ctl)
+		error("prog already debugged");
+
+	ctl->step = n;
+	if(p->state == Pdebug)
+		addrun(p);
+	ctl->debugger = up;
+	strcpy(buf, "child: ");
+	msg = buf+7;
+	ctl->msg = msg;
+
+	/*
+	 * wait for reply from dbgxec; release is okay because prog is now
+	 * debugged, cannot exit.
+	 */
+	if(waserror()){
+		acquire();
+		ctl->debugger = nil;
+		ctl->msg = nil;
+		o->debug = nil;
+		p->xec = xec;
+		nexterror();
+	}
+	release();
+	sleep(&ctl->r, xecdone, ctl);
+	poperror();
+	acquire();
+	if(msg[0] != '\0')
+		error(buf);
+}
+
+void
+dbgexit(Prog *kid, int broken, char *estr)
+{
+	int n;
+	Osenv *e;
+	Progctl *ctl;
+	char buf[ERRMAX+20];
+
+	e = kid->osenv;
+	ctl = e->debug;
+	e->debug = nil;
+	kid->xec = xec;
+
+	if(broken)
+		n = snprint(buf, sizeof(buf), "broken: %s", estr);
+	else
+		n = snprint(buf, sizeof(buf), "exited");
+	if(ctl->debugger)
+		telldbg(ctl, buf);
+	qproduce(ctl->q, buf, n);
+}
+
+static void
+dbgaddrun(Prog *p)
+{
+	Osenv *o;
+
+	p->state = Pdebug;
+	p->addrun = nil;
+	o = p->osenv;
+	if(o->rend != nil)
+		wakeup(o->rend);
+	o->rend = nil;
+}
+
+static int
+bdone(void *vp)
+{
+	Prog *p = vp;
+
+	return p->addrun == nil;
+}
+
+static void
+dbgblock(Prog *p)
+{
+	Osenv *o;
+	Progctl *ctl;
+
+	o = p->osenv;
+	ctl = o->debug;
+	qproduce(ctl->q, progstate[p->state], strlen(progstate[p->state]));
+	pushrun(p);
+	p->addrun = dbgaddrun;
+	o->rend = &up->sleep;
+
+	/*
+	 * bdone(p) is safe after release because p is being debugged,
+	 * so cannot exit.
+	 */
+	if(waserror()){
+		acquire();
+		nexterror();
+	}
+	release();
+	if(o->rend != nil)
+		sleep(o->rend, bdone, p);
+	poperror();
+	acquire();
+	if(p->kill != nil)
+		error(p->kill);
+	ctl = o->debug;
+	if(ctl != nil)
+		qproduce(ctl->q, "run", 3);
+}
+
+void
+dbgxec(Prog *p)
+{
+	Bpt *b;
+	Prog *kid;
+	Osenv *env;
+	Progctl *ctl;
+	int op, pc, n;
+	char buf[ERRMAX+10];
+	extern void (*dec[])(void);
+
+	env = p->osenv;
+	ctl = env->debug;
+	ctl->ref++;
+	if(waserror()){
+		closedbgctl(ctl, p);
+		nexterror();
+	}
+
+	R = p->R;
+	R.MP = R.M->MP;
+
+	R.IC = p->quanta;
+	if((ulong)R.IC > ctl->step)
+		R.IC = ctl->step;
+	if(ctl->stop)
+		R.IC = 0;
+
+
+	buf[0] = '\0';
+
+	if(R.IC != 0 && R.M->compiled) {
+		comvec();
+		if(p != currun())
+			dbgblock(p);
+		goto save;
+	}
+
+	while(R.IC != 0) {
+		if(0)
+			print("step: %lux: %s %4ld %D\n",
+				(ulong)p, R.M->m->name, R.PC-R.M->prog, R.PC);
+
+		dec[R.PC->add]();
+		op = R.PC->op;
+		R.PC++;
+		optab[op]();
+
+		/*
+		 * check notification about new progs
+		 */
+		if(op == ISPAWN || op == IMSPAWN) {
+			/* pick up the kid from the end of the run queue */
+			kid = delruntail(Pdebug);
+			n = snprint(buf, sizeof buf, "new %d", kid->pid);
+			qproduce(ctl->q, buf, n);
+			buf[0] = '\0';
+		}
+		if(op == ILOAD) {
+			n = snprint(buf, sizeof buf, "load %s", string2c(*(String**)R.s));
+			qproduce(ctl->q, buf, n);
+			buf[0] = '\0';
+		}
+
+		/*
+		 * check for returns at big steps
+		 */
+		if(op == IRET)
+			R.IC = 1;
+
+		/*
+		 * check for blocked progs
+		 */
+		if(R.IC == 1 && p != currun())
+			dbgblock(p);
+		if(ctl->stop)
+			R.IC = 1;
+		R.IC--;
+		if(ctl->bpts == nil)
+			continue;
+		pc = R.PC - R.M->prog;
+		for(b = ctl->bpts; b != nil; b = b->next) {
+			if(pc == b->pc &&
+			  (strcmp(R.M->m->path, b->path) == 0 ||
+			   strcmp(R.M->m->path, b->file) == 0)) {
+				strcpy(buf, "breakpoint");
+				goto save;
+			}
+		}
+	}
+
+save:
+	if(ctl->stop)
+		strcpy(buf, "stopped");
+
+	p->R = R;
+
+	if(env->debug == nil) {
+		poperror();
+		return;
+	}
+
+	if(p == currun())
+		delrun(Pdebug);
+
+	telldbg(env->debug, buf);
+	poperror();
+	closedbgctl(env->debug, p);
+}
+
+Dev progdevtab = {
+	'p',
+	"prog",
+
+	devreset,
+	devinit,
+	devshutdown,
+	progattach,
+	progwalk,
+	progstat,
+	progopen,
+	devcreate,
+	progclose,
+	progread,
+	devbread,
+	progwrite,
+	devbwrite,
+	devremove,
+	progwstat,
+};
--- /dev/null
+++ b/os/port/devroot.c
@@ -1,0 +1,153 @@
+#include	"u.h"
+#include	"../port/lib.h"
+#include	"mem.h"
+#include	"dat.h"
+#include	"fns.h"
+#include	"../port/error.h"
+
+extern Rootdata rootdata[];
+extern Dirtab roottab[];
+extern int	rootmaxq;
+
+static Chan*
+rootattach(char *spec)
+{
+	int i;
+	ulong len;
+	Rootdata *r;
+
+	if(*spec)
+		error(Ebadspec);
+	for (i = 0; i < rootmaxq; i++){
+		r = &rootdata[i];
+		if (r->sizep){
+			len = *r->sizep;
+			r->size = len;
+			roottab[i].length = len;
+		}
+	}
+	return devattach('/', spec);
+}
+
+static int
+rootgen(Chan *c, char *name, Dirtab *tab, int nd, int s, Dir *dp)
+{
+	int p, i;
+	Rootdata *r;
+
+	if(s == DEVDOTDOT){
+		p = rootdata[c->qid.path].dotdot;
+		c->qid.path = p;
+		c->qid.type = QTDIR;
+		name = "#/";
+		if(p != 0){
+			for(i = 0; i < rootmaxq; i++)
+				if(roottab[i].qid.path == c->qid.path){
+					name = roottab[i].name;
+					break;
+				}
+		}
+		devdir(c, c->qid, name, 0, eve, 0555, dp);
+		return 1;
+	}
+	if(name != nil){
+		isdir(c);
+		r = &rootdata[(int)c->qid.path];
+		tab = r->ptr;
+		for(i=0; i<r->size; i++, tab++)
+			if(strcmp(tab->name, name) == 0){
+				devdir(c, tab->qid, tab->name, tab->length, eve, tab->perm, dp);
+				return 1;
+			}
+		return -1;
+	}
+	if(s >= nd)
+		return -1;
+	tab += s;
+	devdir(c, tab->qid, tab->name, tab->length, eve, tab->perm, dp);
+	return 1;
+}
+
+static Walkqid*
+rootwalk(Chan *c, Chan *nc, char **name, int nname)
+{
+	ulong p;
+
+	p = c->qid.path;
+	if(nname == 0)
+		p = rootdata[p].dotdot;
+	return devwalk(c, nc, name, nname, rootdata[p].ptr, rootdata[p].size, rootgen);
+}
+
+static int
+rootstat(Chan *c, uchar *dp, int n)
+{
+	int p;
+
+	p = rootdata[c->qid.path].dotdot;
+	return devstat(c, dp, n, rootdata[p].ptr, rootdata[p].size, rootgen);
+}
+
+static Chan*
+rootopen(Chan *c, int omode)
+{
+	int p;
+
+	p = rootdata[c->qid.path].dotdot;
+	return devopen(c, omode, rootdata[p].ptr, rootdata[p].size, rootgen);
+}
+
+/*
+ * sysremove() knows this is a nop
+ */
+static void	 
+rootclose(Chan*)
+{
+}
+
+static long	 
+rootread(Chan *c, void *buf, long n, vlong offset)
+{
+	ulong p, len;
+	uchar *data;
+
+	p = c->qid.path;
+	if(c->qid.type & QTDIR)
+		return devdirread(c, buf, n, rootdata[p].ptr, rootdata[p].size, rootgen);
+	len = rootdata[p].size;
+	if(offset < 0 || offset >= len)
+		return 0;
+	if(offset+n > len)
+		n = len - offset;
+	data = rootdata[p].ptr;
+	memmove(buf, data+offset, n);
+	return n;
+}
+
+static long	 
+rootwrite(Chan*, void*, long, vlong)
+{
+	error(Eperm);
+	return 0;
+}
+
+Dev rootdevtab = {
+	'/',
+	"root",
+
+	devreset,
+	devinit,
+	devshutdown,
+	rootattach,
+	rootwalk,
+	rootstat,
+	rootopen,
+	devcreate,
+	rootclose,
+	rootread,
+	devbread,
+	rootwrite,
+	devbwrite,
+	devremove,
+	devwstat,
+};
--- /dev/null
+++ b/os/port/devsd.c
@@ -1,0 +1,1474 @@
+/*
+ * Storage Device.
+ */
+#include "u.h"
+#include "../port/lib.h"
+#include "mem.h"
+#include "dat.h"
+#include "fns.h"
+#include "io.h"
+#include "ureg.h"
+#include "../port/error.h"
+
+#include "../port/sd.h"
+
+extern Dev sddevtab;
+extern SDifc* sdifc[];
+
+typedef struct SDevgrp {
+	SDev*	dev;
+	int	nunits;		/* num units in dev */
+} SDevgrp;
+
+static SDevgrp* devs;			/* all devices */
+static QLock devslock;			/* insertion and removal of devices */
+static int ndevs;			/* total number of devices in the system */
+
+enum {
+	Rawcmd,
+	Rawdata,
+	Rawstatus,
+};
+
+enum {
+	Qtopdir		= 1,		/* top level directory */
+	Qtopbase,
+	Qtopctl = Qtopbase,
+	Qtopstat,
+
+	Qunitdir,			/* directory per unit */
+	Qunitbase,
+	Qctl		= Qunitbase,
+	Qraw,
+	Qpart,
+
+	TypeLOG		= 4,
+	NType		= (1<<TypeLOG),
+	TypeMASK	= (NType-1),
+	TypeSHIFT	= 0,
+
+	PartLOG		= 8,
+	NPart		= (1<<PartLOG),
+	PartMASK	= (NPart-1),
+	PartSHIFT	= TypeLOG,
+
+	UnitLOG		= 8,
+	NUnit		= (1<<UnitLOG),
+	UnitMASK	= (NUnit-1),
+	UnitSHIFT	= (PartLOG+TypeLOG),
+
+	DevLOG		= 8,
+	NDev		= (1 << DevLOG),
+	DevMASK	= (NDev-1),
+	DevSHIFT = (UnitLOG+PartLOG+TypeLOG),
+
+	Ncmd = 20,
+};
+
+#define TYPE(q)		((((ulong)(q).path)>>TypeSHIFT) & TypeMASK)
+#define PART(q)		((((ulong)(q).path)>>PartSHIFT) & PartMASK)
+#define UNIT(q)		((((ulong)(q).path)>>UnitSHIFT) & UnitMASK)
+#define DEV(q)		((((ulong)(q).path)>>DevSHIFT) & DevMASK)
+#define QID(d,u, p, t)	(((d)<<DevSHIFT)|((u)<<UnitSHIFT)|\
+					 ((p)<<PartSHIFT)|((t)<<TypeSHIFT))
+
+
+static void
+sdaddpart(SDunit* unit, char* name, ulong start, ulong end)
+{
+	SDpart *pp;
+	int i, partno;
+
+	/*
+	 * Check name not already used
+	 * and look for a free slot.
+	 */
+	if(unit->part != nil){
+		partno = -1;
+		for(i = 0; i < unit->npart; i++){
+			pp = &unit->part[i];
+			if(!pp->valid){
+				if(partno == -1)
+					partno = i;
+				break;
+			}
+			if(strcmp(name, pp->name) == 0){
+				if(pp->start == start && pp->end == end)
+					return;
+				error(Ebadctl);
+			}
+		}
+	}
+	else{
+		if((unit->part = malloc(sizeof(SDpart)*SDnpart)) == nil)
+			error(Enomem);
+		unit->npart = SDnpart;
+		partno = 0;
+	}
+
+	/*
+	 * If no free slot found then increase the
+	 * array size (can't get here with unit->part == nil).
+	 */
+	if(partno == -1){
+		if(unit->npart >= NPart)
+			error(Enomem);
+		if((pp = malloc(sizeof(SDpart)*(unit->npart+SDnpart))) == nil)
+			error(Enomem);
+		memmove(pp, unit->part, sizeof(SDpart)*unit->npart);
+		free(unit->part);
+		unit->part = pp;
+		partno = unit->npart;
+		unit->npart += SDnpart;
+	}
+
+	/*
+	 * Check size and extent are valid.
+	 */
+	if(start > end || end > unit->sectors)
+		error(Eio);
+	pp = &unit->part[partno];
+	pp->start = start;
+	pp->end = end;
+	kstrdup(&pp->name, name);
+	kstrdup(&pp->user, eve);
+	pp->perm = 0640;
+	pp->valid = 1;
+}
+
+static void
+sddelpart(SDunit* unit,  char* name)
+{
+	int i;
+	SDpart *pp;
+
+	/*
+	 * Look for the partition to delete.
+	 * Can't delete if someone still has it open.
+	 */
+	pp = unit->part;
+	for(i = 0; i < unit->npart; i++){
+		if(strcmp(name, pp->name) == 0)
+			break;
+		pp++;
+	}
+	if(i >= unit->npart)
+		error(Ebadctl);
+	if(strcmp(up->env->user, pp->user) && !iseve())
+		error(Eperm);
+	pp->valid = 0;
+	pp->vers++;
+}
+
+static int
+sdinitpart(SDunit* unit)
+{
+	int i, nf;
+	ulong start, end;
+	char *f[4], *p, *q, buf[10];
+
+	unit->vers++;
+	unit->sectors = unit->secsize = 0;
+	if(unit->part){
+		for(i = 0; i < unit->npart; i++){
+			unit->part[i].valid = 0;
+			unit->part[i].vers++;
+		}
+	}
+
+	if(unit->inquiry[0] & 0xC0)
+		return 0;
+	switch(unit->inquiry[0] & 0x1F){
+	case 0x00:			/* DA */
+	case 0x04:			/* WORM */
+	case 0x05:			/* CD-ROM */
+	case 0x07:			/* MO */
+		break;
+	default:
+		return 0;
+	}
+
+	if(unit->dev->ifc->online)
+		unit->dev->ifc->online(unit);
+	if(unit->sectors){
+		sdaddpart(unit, "data", 0, unit->sectors);
+	
+		/*
+		 * Use partitions passed from boot program,
+		 * e.g.
+		 *	sdC0part=dos 63 123123/plan9 123123 456456
+		 * This happens before /boot sets hostname so the
+		 * partitions will have the null-string for user.
+		 * The gen functions patch it up.
+		 */
+		snprint(buf, sizeof buf, "%spart", unit->name);
+		for(p = getconf(buf); p != nil; p = q){
+			if(q = strchr(p, '/'))
+				*q++ = '\0';
+			nf = tokenize(p, f, nelem(f));
+			if(nf < 3)
+				continue;
+		
+			start = strtoul(f[1], 0, 0);
+			end = strtoul(f[2], 0, 0);
+			if(!waserror()){
+				sdaddpart(unit, f[0], start, end);
+				poperror();
+			}
+		}			
+	}
+
+	return 1;
+}
+
+static SDev*
+sdgetdev(int idno)
+{
+	SDev *sdev;
+	int i;
+
+	qlock(&devslock);
+	for(i = 0; i != ndevs; i++)
+		if(devs[i].dev->idno == idno)
+			break;
+	
+	if(i == ndevs)
+		sdev = nil;
+	else{
+		sdev = devs[i].dev;
+		incref(&sdev->r);
+	}
+	qunlock(&devslock);
+	return sdev;
+}
+
+static SDunit*
+sdgetunit(SDev* sdev, int subno)
+{
+	SDunit *unit;
+	char buf[32];
+
+	/*
+	 * Associate a unit with a given device and sub-unit
+	 * number on that device.
+	 * The device will be probed if it has not already been
+	 * successfully accessed.
+	 */
+	qlock(&sdev->unitlock);
+	if(subno > sdev->nunit){
+		qunlock(&sdev->unitlock);
+		return nil;
+	}
+
+	unit = sdev->unit[subno];
+	if(unit == nil){
+		/*
+		 * Probe the unit only once. This decision
+		 * may be a little severe and reviewed later.
+		 */
+		if(sdev->unitflg[subno]){
+			qunlock(&sdev->unitlock);
+			return nil;
+		}
+		if((unit = malloc(sizeof(SDunit))) == nil){
+			qunlock(&sdev->unitlock);
+			return nil;
+		}
+		sdev->unitflg[subno] = 1;
+
+		snprint(buf, sizeof(buf), "%s%d", sdev->name, subno);
+		kstrdup(&unit->name, buf);
+		kstrdup(&unit->user, eve);
+		unit->perm = 0555;
+		unit->subno = subno;
+		unit->dev = sdev;
+
+		if(sdev->enabled == 0 && sdev->ifc->enable)
+			sdev->ifc->enable(sdev);
+		sdev->enabled = 1;
+
+		/*
+		 * No need to lock anything here as this is only
+		 * called before the unit is made available in the
+		 * sdunit[] array.
+		 */
+		if(unit->dev->ifc->verify(unit) == 0){
+			qunlock(&sdev->unitlock);
+			free(unit);
+			return nil;
+		}
+		sdev->unit[subno] = unit;
+	}
+	qunlock(&sdev->unitlock);
+	return unit;
+}
+
+static void
+sdreset(void)
+{
+	int i;
+	SDev *sdev, *tail, *sdlist;
+
+	/*
+	 * Probe all configured controllers and make a list
+	 * of devices found, accumulating a possible maximum number
+	 * of units attached and marking each device with an index
+	 * into the linear top-level directory array of units.
+	 */
+	tail = sdlist = nil;
+	for(i = 0; sdifc[i] != nil; i++){
+		if(sdifc[i]->pnp == nil || (sdev = sdifc[i]->pnp()) == nil)
+			continue;
+		if(sdlist != nil)
+			tail->next = sdev;
+		else
+			sdlist = sdev;
+		for(tail = sdev; tail->next != nil; tail = tail->next){
+			tail->unit = (SDunit**)malloc(tail->nunit * sizeof(SDunit*));
+			tail->unitflg = (int*)malloc(tail->nunit * sizeof(int));
+			assert(tail->unit && tail->unitflg);
+			ndevs++;
+		}
+		tail->unit = (SDunit**)malloc(tail->nunit * sizeof(SDunit*));
+		tail->unitflg = (int*)malloc(tail->nunit * sizeof(int));
+		ndevs++;
+	}
+	
+	/*
+	 * Legacy and option code goes here. This will be hard...
+	 */
+
+	/*
+	 * The maximum number of possible units is known, allocate
+	 * placeholders for their datastructures; the units will be
+	 * probed and structures allocated when attached.
+	 * Allocate controller names for the different types.
+	 */
+	if(ndevs == 0)
+		return;
+	for(i = 0; sdifc[i] != nil; i++){
+		/*
+		 * BUG: no check is made here or later when a
+		 * unit is attached that the id and name are set.
+		 */
+		if(sdifc[i]->id)
+			sdifc[i]->id(sdlist);
+	}
+
+	/* 
+	  * The IDs have been set, unlink the sdlist and copy the spec to
+	  * the devtab.
+	  */
+	devs = (SDevgrp*)malloc(ndevs * sizeof(SDevgrp));
+	memset(devs, 0, ndevs * sizeof(SDevgrp));
+	i = 0;
+	while(sdlist != nil){
+		devs[i].dev = sdlist;
+		devs[i].nunits = sdlist->nunit;
+		sdlist = sdlist->next;
+		devs[i].dev->next = nil;
+		i++;
+	}
+}
+
+static int
+sd2gen(Chan* c, int i, Dir* dp)
+{
+	Qid q;
+	vlong l;
+	SDpart *pp;
+	SDperm *perm;
+	SDunit *unit;
+	SDev *sdev;
+	int rv;
+
+	sdev = sdgetdev(DEV(c->qid));
+	assert(sdev);
+	unit = sdev->unit[UNIT(c->qid)];
+
+	rv = -1;
+	switch(i){
+	case Qctl:
+		mkqid(&q, QID(DEV(c->qid), UNIT(c->qid), PART(c->qid), Qctl), 
+			   unit->vers, QTFILE);
+		perm = &unit->ctlperm;
+		if(emptystr(perm->user)){
+			kstrdup(&perm->user, eve);
+			perm->perm = 0640;
+		}
+		devdir(c, q, "ctl", 0, perm->user, perm->perm, dp);
+		rv = 1;
+		break;
+
+	case Qraw:
+		mkqid(&q, QID(DEV(c->qid), UNIT(c->qid), PART(c->qid), Qraw), 
+			   unit->vers, QTFILE);
+		perm = &unit->rawperm;
+		if(emptystr(perm->user)){
+			kstrdup(&perm->user, eve);
+			perm->perm = DMEXCL|0600;
+		}
+		devdir(c, q, "raw", 0, perm->user, perm->perm, dp);
+		rv = 1;
+		break;
+
+	case Qpart:
+		pp = &unit->part[PART(c->qid)];
+		l = (pp->end - pp->start) * (vlong)unit->secsize;
+		mkqid(&q, QID(DEV(c->qid), UNIT(c->qid), PART(c->qid), Qpart), 
+			   unit->vers+pp->vers, QTFILE);
+		if(emptystr(pp->user))
+			kstrdup(&pp->user, eve);
+		devdir(c, q, pp->name, l, pp->user, pp->perm, dp);
+		rv = 1;
+		break;
+	}
+	
+	decref(&sdev->r);
+	return rv;
+}
+
+static int
+sd1gen(Chan* c, int i, Dir* dp)
+{
+	Qid q;
+
+	switch(i){
+	case Qtopctl:
+		mkqid(&q, QID(0, 0, 0, Qtopctl), 0, QTFILE);
+		devdir(c, q, "sdctl", 0, eve, 0640, dp);
+		return 1;
+	case Qtopstat:
+		mkqid(&q, QID(0, 0, 0, Qtopstat), 0, QTFILE);
+		devdir(c, q, "sdstat", 0, eve, 0640, dp);
+		return 1;
+	}
+	return -1;
+}
+
+static int
+sdgen(Chan* c, char*, Dirtab*, int, int s, Dir* dp)
+{
+	Qid q;
+	vlong l;
+	int i, r;
+	SDpart *pp;
+	SDunit *unit;
+	SDev *sdev;
+
+	switch(TYPE(c->qid)){
+	case Qtopdir:
+		if(s == DEVDOTDOT){
+			mkqid(&q, QID(0, 0, 0, Qtopdir), 0, QTDIR);
+			sprint(up->genbuf, "#%C", sddevtab.dc);
+			devdir(c, q, up->genbuf, 0, eve, 0555, dp);
+			return 1;
+		}
+
+		if(s == 0 || s == 1)
+			return sd1gen(c, s + Qtopbase, dp);
+		s -= 2;
+
+		qlock(&devslock);
+		for(i = 0; i != ndevs; i++){
+			if(s < devs[i].nunits)
+				break;
+			s -= devs[i].nunits;
+		}
+		
+		if(i == ndevs){
+			/* Run of the end of the list */
+			qunlock(&devslock);
+			return -1;
+		}
+
+		if((sdev = devs[i].dev) == nil){
+			qunlock(&devslock);
+			return 0;
+		}
+
+		incref(&sdev->r);
+		qunlock(&devslock);
+
+		if((unit = sdev->unit[s]) == nil)
+			if((unit = sdgetunit(sdev, s)) == nil){
+				decref(&sdev->r);
+				return 0;
+			}
+
+		mkqid(&q, QID(sdev->idno, s, 0, Qunitdir), 0, QTDIR);
+		if(emptystr(unit->user))
+			kstrdup(&unit->user, eve);
+		devdir(c, q, unit->name, 0, unit->user, unit->perm, dp);
+		decref(&sdev->r);
+		return 1;
+
+	case Qunitdir:
+		if(s == DEVDOTDOT){
+			mkqid(&q, QID(0, 0, 0, Qtopdir), 0, QTDIR);
+			sprint(up->genbuf, "#%C", sddevtab.dc);
+			devdir(c, q, up->genbuf, 0, eve, 0555, dp);
+			return 1;
+		}
+		
+		if((sdev = sdgetdev(DEV(c->qid))) == nil){
+			devdir(c, q, "unavailable", 0, eve, 0, dp);
+			return 1;
+		}
+
+		unit = sdev->unit[UNIT(c->qid)];
+		qlock(&unit->ctl);
+
+		/*
+		 * Check for media change.
+		 * If one has already been detected, sectors will be zero.
+		 * If there is one waiting to be detected, online
+		 * will return > 1.
+		 * Online is a bit of a large hammer but does the job.
+		 */
+		if(unit->sectors == 0
+		|| (unit->dev->ifc->online && unit->dev->ifc->online(unit) > 1))
+			sdinitpart(unit);
+
+		i = s+Qunitbase;
+		if(i < Qpart){
+			r = sd2gen(c, i, dp);
+			qunlock(&unit->ctl);
+			decref(&sdev->r);
+			return r;
+		}
+		i -= Qpart;
+		if(unit->part == nil || i >= unit->npart){
+			qunlock(&unit->ctl);
+			decref(&sdev->r);
+			break;
+		}
+		pp = &unit->part[i];
+		if(!pp->valid){
+			qunlock(&unit->ctl);
+			decref(&sdev->r);
+			return 0;
+		}
+		l = (pp->end - pp->start) * (vlong)unit->secsize;
+		mkqid(&q, QID(DEV(c->qid), UNIT(c->qid), i, Qpart), 
+			    unit->vers+pp->vers, QTFILE);
+		if(emptystr(pp->user))
+			kstrdup(&pp->user, eve);
+		devdir(c, q, pp->name, l, pp->user, pp->perm, dp);
+		qunlock(&unit->ctl);
+		decref(&sdev->r);
+		return 1;
+	case Qraw:
+	case Qctl:
+	case Qpart:
+		if((sdev = sdgetdev(DEV(c->qid))) == nil){
+			devdir(c, q, "unavailable", 0, eve, 0, dp);
+			return 1;
+		}
+		unit = sdev->unit[UNIT(c->qid)];
+		qlock(&unit->ctl);
+		r = sd2gen(c, TYPE(c->qid), dp);
+		qunlock(&unit->ctl);
+		decref(&sdev->r);
+		return r;
+	case Qtopctl:
+	case Qtopstat:
+		return sd1gen(c, TYPE(c->qid), dp);
+	default:
+		break;
+	}
+
+	return -1;
+}
+
+static Chan*
+sdattach(char* spec)
+{
+	Chan *c;
+	char *p;
+	SDev *sdev;
+	int idno, subno, i;
+
+	if(ndevs == 0 || *spec == '\0'){
+		c = devattach(sddevtab.dc, spec);
+		mkqid(&c->qid, QID(0, 0, 0, Qtopdir), 0, QTDIR);
+		return c;
+	}
+
+	if(spec[0] != 's' || spec[1] != 'd')
+		error(Ebadspec);
+	idno = spec[2];
+	subno = strtol(&spec[3], &p, 0);
+	if(p == &spec[3])
+		error(Ebadspec);
+
+	qlock(&devslock);
+	for (sdev = nil, i = 0; i != ndevs; i++)
+		if((sdev = devs[i].dev) != nil && sdev->idno == idno)
+			break;
+
+	if(i == ndevs || subno >= sdev->nunit || sdgetunit(sdev, subno) == nil){
+		qunlock(&devslock);
+		error(Enonexist);
+	}
+	incref(&sdev->r);
+	qunlock(&devslock);
+
+	c = devattach(sddevtab.dc, spec);
+	mkqid(&c->qid, QID(sdev->idno, subno, 0, Qunitdir), 0, QTDIR);
+	c->dev = (sdev->idno << UnitLOG) + subno;
+	decref(&sdev->r);
+	return c;
+}
+
+static Walkqid*
+sdwalk(Chan* c, Chan* nc, char** name, int nname)
+{
+	return devwalk(c, nc, name, nname, nil, 0, sdgen);
+}
+
+static int
+sdstat(Chan* c, uchar* db, int n)
+{
+	return devstat(c, db, n, nil, 0, sdgen);
+}
+
+static Chan*
+sdopen(Chan* c, int omode)
+{
+	SDpart *pp;
+	SDunit *unit;
+	SDev *sdev;
+	uchar tp;
+
+	c = devopen(c, omode, 0, 0, sdgen);
+	if((tp = TYPE(c->qid)) != Qctl && tp != Qraw && tp != Qpart)
+		return c;
+
+	sdev = sdgetdev(DEV(c->qid));
+	if(sdev == nil)
+		error(Enonexist);
+	unit = sdev->unit[UNIT(c->qid)];
+
+	switch(TYPE(c->qid)){
+	case Qctl:
+		c->qid.vers = unit->vers;
+		break;
+	case Qraw:
+		c->qid.vers = unit->vers;
+		if(_tas(&unit->rawinuse) != 0){
+			c->flag &= ~COPEN;
+			error(Einuse);
+		}
+		unit->state = Rawcmd;
+		break;
+	case Qpart:
+		qlock(&unit->ctl);
+		if(waserror()){
+			qunlock(&unit->ctl);
+			c->flag &= ~COPEN;
+			nexterror();
+		}
+		pp = &unit->part[PART(c->qid)];
+		c->qid.vers = unit->vers+pp->vers;
+		qunlock(&unit->ctl);
+		poperror();
+		break;
+	}
+	decref(&sdev->r);
+	return c;
+}
+
+static void
+sdclose(Chan* c)
+{
+	SDunit *unit;
+	SDev *sdev;
+
+	if(c->qid.type & QTDIR)
+		return;
+	if(!(c->flag & COPEN))
+		return;
+
+	switch(TYPE(c->qid)){
+	default:
+		break;
+	case Qraw:
+		sdev = sdgetdev(DEV(c->qid));
+		if(sdev){
+			unit = sdev->unit[UNIT(c->qid)];
+			unit->rawinuse = 0;
+			decref(&sdev->r);
+		}
+		break;
+	}
+}
+
+static long
+sdbio(Chan* c, int write, char* a, long len, vlong off)
+{
+	int nchange;
+	long l;
+	uchar *b;
+	SDpart *pp;
+	SDunit *unit;
+	SDev *sdev;
+	ulong bno, max, nb, offset;
+
+	sdev = sdgetdev(DEV(c->qid));
+	if(sdev == nil)
+		error(Enonexist);
+	unit = sdev->unit[UNIT(c->qid)];
+	if(unit == nil)
+		error(Enonexist);
+
+	nchange = 0;
+	qlock(&unit->ctl);
+	while(waserror()){
+		/* notification of media change; go around again */
+		if(strcmp(up->env->errstr, Eio) == 0 && unit->sectors == 0 && nchange++ == 0){
+			sdinitpart(unit);
+			continue;
+		}
+
+		/* other errors; give up */
+		qunlock(&unit->ctl);
+		decref(&sdev->r);
+		nexterror();
+	}
+	pp = &unit->part[PART(c->qid)];
+	if(unit->vers+pp->vers != c->qid.vers)
+		error(Eio);
+
+	/*
+	 * Check the request is within bounds.
+	 * Removeable drives are locked throughout the I/O
+	 * in case the media changes unexpectedly.
+	 * Non-removeable drives are not locked during the I/O
+	 * to allow the hardware to optimise if it can; this is
+	 * a little fast and loose.
+	 * It's assumed that non-removeable media parameters
+	 * (sectors, secsize) can't change once the drive has
+	 * been brought online.
+	 */
+	bno = (off/unit->secsize) + pp->start;
+	nb = ((off+len+unit->secsize-1)/unit->secsize) + pp->start - bno;
+	max = SDmaxio/unit->secsize;
+	if(nb > max)
+		nb = max;
+	if(bno+nb > pp->end)
+		nb = pp->end - bno;
+	if(bno >= pp->end || nb == 0){
+		if(write)
+			error(Eio);
+		qunlock(&unit->ctl);
+		decref(&sdev->r);
+		poperror();
+		return 0;
+	}
+	if(!(unit->inquiry[1] & 0x80)){
+		qunlock(&unit->ctl);
+		poperror();
+	}
+
+	b = sdmalloc(nb*unit->secsize);
+	if(b == nil)
+		error(Enomem);
+	if(waserror()){
+		sdfree(b);
+		if(!(unit->inquiry[1] & 0x80))
+			decref(&sdev->r);		/* gadverdamme! */
+		nexterror();
+	}
+
+	offset = off%unit->secsize;
+	if(offset+len > nb*unit->secsize)
+		len = nb*unit->secsize - offset;
+	if(write){
+		if(offset || (len%unit->secsize)){
+			l = unit->dev->ifc->bio(unit, 0, 0, b, nb, bno);
+			if(l < 0)
+				error(Eio);
+			if(l < (nb*unit->secsize)){
+				nb = l/unit->secsize;
+				l = nb*unit->secsize - offset;
+				if(len > l)
+					len = l;
+			}
+		}
+		memmove(b+offset, a, len);
+		l = unit->dev->ifc->bio(unit, 0, 1, b, nb, bno);
+		if(l < 0)
+			error(Eio);
+		if(l < offset)
+			len = 0;
+		else if(len > l - offset)
+			len = l - offset;
+	}
+	else{
+		l = unit->dev->ifc->bio(unit, 0, 0, b, nb, bno);
+		if(l < 0)
+			error(Eio);
+		if(l < offset)
+			len = 0;
+		else if(len > l - offset)
+			len = l - offset;
+		memmove(a, b+offset, len);
+	}
+	sdfree(b);
+	poperror();
+
+	if(unit->inquiry[1] & 0x80){
+		qunlock(&unit->ctl);
+		poperror();
+	}
+
+	decref(&sdev->r);
+	return len;
+}
+
+static long
+sdrio(SDreq* r, void* a, long n)
+{
+	void *data;
+
+	if(n >= SDmaxio || n < 0)
+		error(Etoobig);
+
+	data = nil;
+	if(n){
+		if((data = sdmalloc(n)) == nil)
+			error(Enomem);
+		if(r->write)
+			memmove(data, a, n);
+	}
+	r->data = data;
+	r->dlen = n;
+
+	if(waserror()){
+		if(data != nil){
+			sdfree(data);
+			r->data = nil;
+		}
+		nexterror();
+	}
+
+	if(r->unit->dev->ifc->rio(r) != SDok)
+		error(Eio);
+
+	if(!r->write && r->rlen > 0)
+		memmove(a, data, r->rlen);
+	if(data != nil){
+		sdfree(data);
+		r->data = nil;
+	}
+	poperror();
+
+	return r->rlen;
+}
+
+static long
+sdread(Chan *c, void *a, long n, vlong off)
+{
+	char *p, *e, *buf;
+	SDpart *pp;
+	SDunit *unit;
+	SDev *sdev;
+	ulong offset;
+	int i, l, status;
+
+	offset = off;
+	switch(TYPE(c->qid)){
+	default:
+		error(Eperm);
+	case Qtopstat:
+		p = buf = malloc(READSTR);
+		assert(p);
+		e = p + READSTR;
+		qlock(&devslock);
+		for(i = 0; i != ndevs; i++){
+			SDev *sdev = devs[i].dev;
+
+			if(sdev->ifc->stat)
+				p = sdev->ifc->stat(sdev, p, e);
+			else
+				p = seprint(e, "%s; no statistics available\n", sdev->name);
+		}
+		qunlock(&devslock);
+		n = readstr(off, a, n, buf);
+		free(buf);
+		return n;
+
+	case Qtopdir:
+	case Qunitdir:
+		return devdirread(c, a, n, 0, 0, sdgen);
+
+	case Qctl:
+		sdev = sdgetdev(DEV(c->qid));
+		if(sdev == nil)
+			error(Enonexist);
+
+		unit = sdev->unit[UNIT(c->qid)];
+		p = malloc(READSTR);
+		l = snprint(p, READSTR, "inquiry %.48s\n",
+			(char*)unit->inquiry+8);
+		qlock(&unit->ctl);
+		/*
+		 * If there's a device specific routine it must
+		 * provide all information pertaining to night geometry
+		 * and the garscadden trains.
+		 */
+		if(unit->dev->ifc->rctl)
+			l += unit->dev->ifc->rctl(unit, p+l, READSTR-l);
+		if(unit->sectors == 0)
+			sdinitpart(unit);
+		if(unit->sectors){
+			if(unit->dev->ifc->rctl == nil)
+				l += snprint(p+l, READSTR-l,
+					"geometry %ld %ld\n",
+					unit->sectors, unit->secsize);
+			pp = unit->part;
+			for(i = 0; i < unit->npart; i++){
+				if(pp->valid)
+					l += snprint(p+l, READSTR-l,
+						"part %s %lud %lud\n",
+						pp->name, pp->start, pp->end);
+				pp++;
+			}
+		}
+		qunlock(&unit->ctl);
+		decref(&sdev->r);
+		l = readstr(offset, a, n, p);
+		free(p);
+		return l;
+
+	case Qraw:
+		sdev = sdgetdev(DEV(c->qid));
+		if(sdev == nil)
+			error(Enonexist);
+
+		unit = sdev->unit[UNIT(c->qid)];
+		qlock(&unit->raw);
+		if(waserror()){
+			qunlock(&unit->raw);
+			decref(&sdev->r);
+			nexterror();
+		}
+		if(unit->state == Rawdata){
+			unit->state = Rawstatus;
+			i = sdrio(unit->req, a, n);
+		}
+		else if(unit->state == Rawstatus){
+			status = unit->req->status;
+			unit->state = Rawcmd;
+			free(unit->req);
+			unit->req = nil;
+			i = readnum(0, a, n, status, NUMSIZE);
+		} else
+			i = 0;
+		qunlock(&unit->raw);
+		decref(&sdev->r);
+		poperror();
+		return i;
+
+	case Qpart:
+		return sdbio(c, 0, a, n, off);
+	}
+
+	return 0;
+}
+
+typedef struct Confdata Confdata;
+struct Confdata {
+	int	on;
+	char*	spec;
+	DevConf	cf;
+};
+
+static void
+parseswitch(Confdata* cd, char* option)
+{
+	if(!strcmp("on", option))
+		cd->on = 1;
+	else if(!strcmp("off", option))
+		cd->on = 0;
+	else
+		error(Ebadarg);
+}
+
+static void
+parsespec(Confdata* cd, char* option)
+{
+	if(strlen(option) > 1) 
+		error(Ebadarg);
+	cd->spec = option;
+}
+
+static Devport*
+getnewport(DevConf* dc)
+{
+	Devport *p;
+
+	p = (Devport *)malloc((dc->nports + 1) * sizeof(Devport));
+	if(dc->nports > 0){
+		memmove(p, dc->ports, dc->nports * sizeof(Devport));
+		free(dc->ports);
+	}
+	dc->ports = p;
+	p = &dc->ports[dc->nports++];
+	p->size = -1;
+	p->port = (ulong)-1;
+	return p;
+}
+
+static void
+parseport(Confdata* cd, char* option)
+{
+	char *e;
+	Devport *p;
+
+	if(cd->cf.nports == 0 || cd->cf.ports[cd->cf.nports-1].port != (ulong)-1)
+		p = getnewport(&cd->cf);
+	else
+		p = &cd->cf.ports[cd->cf.nports-1];
+	p->port = strtol(option, &e, 0);
+	if(e == nil || *e != '\0')
+		error(Ebadarg);
+}
+
+static void
+parsesize(Confdata* cd, char* option)
+{
+	char *e;
+	Devport *p;
+
+	if(cd->cf.nports == 0 || cd->cf.ports[cd->cf.nports-1].size != -1)
+		p = getnewport(&cd->cf);
+	else
+		p = &cd->cf.ports[cd->cf.nports-1];
+	p->size = (int)strtol(option, &e, 0);
+	if(e == nil || *e != '\0')
+		error(Ebadarg);
+}
+
+static void
+parseirq(Confdata* cd, char* option)
+{
+	char *e;
+
+	cd->cf.intnum = strtoul(option, &e, 0);
+	if(e == nil || *e != '\0')
+		error(Ebadarg);
+}
+
+static void
+parsetype(Confdata* cd, char* option)
+{
+	cd->cf.type = option;
+}
+
+static struct {
+	char	*option;
+	void	(*parse)(Confdata*, char*);
+} options[] = {
+	{ 	"switch",	parseswitch,	},
+	{	"spec",		parsespec,	},
+	{	"port",		parseport,	},
+	{	"size",		parsesize,	},
+	{	"irq",		parseirq,	},
+	{	"type",		parsetype,	},
+};
+
+static long
+sdwrite(Chan* c, void* a, long n, vlong off)
+{
+	Cmdbuf *cb;
+	SDreq *req;
+	SDunit *unit;
+	SDev *sdev;
+	ulong end, start;
+
+	switch(TYPE(c->qid)){
+	default:
+		error(Eperm);
+	case Qtopctl: {
+		Confdata cd;
+		char buf[256], *field[Ncmd];
+		int nf, i, j;
+
+		memset(&cd, 0, sizeof(Confdata));
+		if(n > sizeof(buf)-1) n = sizeof(buf)-1;
+		memmove(buf, a, n);
+		buf[n] = '\0';
+
+		cd.on = -1;
+		cd.spec = '\0';
+		memset(&cd.cf, 0, sizeof(DevConf));
+
+		nf = tokenize(buf, field, Ncmd);
+		for(i = 0; i < nf; i++){
+			char *opt = field[i++];
+			if(i >= nf)
+				error(Ebadarg);
+			for(j = 0; j != nelem(options); j++)
+				if(!strcmp(opt, options[j].option))
+					break;
+					
+			if(j == nelem(options))
+				error(Ebadarg);
+			options[j].parse(&cd, field[i]);
+		}
+
+		if(cd.on < 0) 
+			error(Ebadarg);
+
+		if(cd.on){
+			if(cd.spec == '\0' || cd.cf.nports == 0 || 
+			     cd.cf.intnum == 0 || cd.cf.type == nil)
+				error(Ebadarg);
+		}
+		else{
+			if(cd.spec == '\0')
+				error(Ebadarg);
+		}
+
+		if(sddevtab.config == nil)
+			error("No configuration function");
+		sddevtab.config(cd.on, cd.spec, &cd.cf);
+		break;
+	}
+	case Qctl:
+		cb = parsecmd(a, n);
+		sdev = sdgetdev(DEV(c->qid));
+		if(sdev == nil)
+			error(Enonexist);
+		unit = sdev->unit[UNIT(c->qid)];
+
+		qlock(&unit->ctl);
+		if(waserror()){
+			qunlock(&unit->ctl);
+			decref(&sdev->r);
+			free(cb);
+			nexterror();
+		}
+		if(unit->vers != c->qid.vers)
+			error(Eio);
+
+		if(cb->nf < 1)
+			error(Ebadctl);
+		if(strcmp(cb->f[0], "part") == 0){
+			if(cb->nf != 4)
+				error(Ebadctl);
+			if(unit->sectors == 0 && !sdinitpart(unit))
+				error(Eio);
+			start = strtoul(cb->f[2], 0, 0);
+			end = strtoul(cb->f[3], 0, 0);
+			sdaddpart(unit, cb->f[1], start, end);
+		}
+		else if(strcmp(cb->f[0], "delpart") == 0){
+			if(cb->nf != 2 || unit->part == nil)
+				error(Ebadctl);
+			sddelpart(unit, cb->f[1]);
+		}
+		else if(unit->dev->ifc->wctl)
+			unit->dev->ifc->wctl(unit, cb);
+		else
+			error(Ebadctl);
+		qunlock(&unit->ctl);
+		decref(&sdev->r);
+		poperror();
+		free(cb);
+		break;
+
+	case Qraw:
+		sdev = sdgetdev(DEV(c->qid));
+		if(sdev == nil)
+			error(Enonexist);
+		unit = sdev->unit[UNIT(c->qid)];
+		qlock(&unit->raw);
+		if(waserror()){
+			qunlock(&unit->raw);
+			decref(&sdev->r);
+			nexterror();
+		}
+		switch(unit->state){
+		case Rawcmd:
+			if(n < 6 || n > sizeof(req->cmd))
+				error(Ebadarg);
+			if((req = malloc(sizeof(SDreq))) == nil)
+				error(Enomem);
+			req->unit = unit;
+			memmove(req->cmd, a, n);
+			req->clen = n;
+			req->flags = SDnosense;
+			req->status = ~0;
+
+			unit->req = req;
+			unit->state = Rawdata;
+			break;
+
+		case Rawstatus:
+			unit->state = Rawcmd;
+			free(unit->req);
+			unit->req = nil;
+			error(Ebadusefd);
+
+		case Rawdata:
+			if(unit->state != Rawdata)
+				error(Ebadusefd);
+			unit->state = Rawstatus;
+
+			unit->req->write = 1;
+			n = sdrio(unit->req, a, n);
+		}
+		qunlock(&unit->raw);
+		decref(&sdev->r);
+		poperror();
+		break;
+	case Qpart:
+		return sdbio(c, 1, a, n, off);
+	}
+
+	return n;
+}
+
+static int
+sdwstat(Chan* c, uchar* dp, int n)
+{
+	Dir *d;
+	SDpart *pp;
+	SDperm *perm;
+	SDunit *unit;
+	SDev *sdev;
+
+	if(c->qid.type & QTDIR)
+		error(Eperm); 
+
+	sdev = sdgetdev(DEV(c->qid));
+	if(sdev == nil)
+		error(Enonexist);
+	unit = sdev->unit[UNIT(c->qid)];
+	qlock(&unit->ctl);
+	d = nil;
+	if(waserror()){
+		free(d);
+		qunlock(&unit->ctl);
+		decref(&sdev->r);
+		nexterror();
+	}
+
+	switch(TYPE(c->qid)){
+	default:
+		error(Eperm);
+	case Qctl:
+		perm = &unit->ctlperm;
+		break;
+	case Qraw:
+		perm = &unit->rawperm;
+		break;
+	case Qpart:
+		pp = &unit->part[PART(c->qid)];
+		if(unit->vers+pp->vers != c->qid.vers)
+			error(Enonexist);
+		perm = &pp->SDperm;
+		break;
+	}
+
+	if(strcmp(up->env->user, perm->user) && !iseve())
+		error(Eperm);
+
+	d = smalloc(sizeof(Dir)+n);
+	n = convM2D(dp, n, &d[0], (char*)&d[1]);
+	if(n == 0)
+		error(Eshortstat);
+	if(!emptystr(d[0].uid))
+		kstrdup(&perm->user, d[0].uid);
+	if(d[0].mode != ~0UL)
+		perm->perm = (perm->perm & ~0777) | (d[0].mode & 0777);
+
+	free(d);
+	qunlock(&unit->ctl);
+	decref(&sdev->r);
+	poperror();
+	return n;
+}
+
+static char
+getspec(char base)
+{
+	while(1){
+		int i;
+		SDev *sdev;
+
+		for(i = 0; i != ndevs; i++)
+			if((sdev = devs[i].dev) != nil && (char)sdev->idno == base)
+				break;
+
+		if(i == ndevs)
+			return base;
+		base++;
+	}
+	return '\0';
+}
+
+static int
+configure(char* spec, DevConf* cf)
+{
+	ISAConf isa;
+	SDevgrp *tmpdevs;
+	SDev *tail, *sdev, *(*probe)(DevConf*);
+	char *p, name[32];
+	int i, nnew;
+
+	if((p = strchr(cf->type, '/')) != nil)
+		*p++ = '\0';
+
+	for(i = 0; sdifc[i] != nil; i++)
+		if(!strcmp(sdifc[i]->name, cf->type))
+			break;
+
+	if(sdifc[i] == nil)
+		error("type not found");
+	
+	if((probe = sdifc[i]->probe) == nil)
+		error("No probe function");
+
+	if(p){
+		/* Try to find the card on the ISA bus.  This code really belongs
+		     in sdata and I'll move it later.  Really! */
+		memset(&isa, 0, sizeof(isa));
+		isa.port = cf->ports[0].port;
+		isa.irq = cf->intnum;
+
+		if(pcmspecial(p, &isa) < 0)
+			error("Cannot find controller");
+	}
+
+	qlock(&devslock);
+	if(waserror()){
+		qunlock(&devslock);
+		nexterror();
+	}
+	
+	for(i = 0; i != ndevs; i++)
+		if((sdev = devs[i].dev) != nil && sdev->idno == *spec)
+			break;
+	if(i != ndevs)
+		error(Eexist);
+
+	if((sdev = (*probe)(cf)) == nil)
+		error("Cannot probe controller");
+	poperror();
+
+	nnew = 0;
+	tail = sdev;
+	while(tail){
+		nnew++;
+		tail = tail->next;
+	}
+	
+	tmpdevs = (SDevgrp*)malloc((ndevs + nnew) * sizeof(SDevgrp));
+	memmove(tmpdevs, devs, ndevs * sizeof(SDevgrp));
+	free(devs);
+	devs = tmpdevs;
+
+	while(sdev){
+		/* Assign `spec' to the device */
+		*spec = getspec(*spec);
+		snprint(name, sizeof(name), "sd%c", *spec);
+		kstrdup(&sdev->name, name);
+		sdev->idno = *spec;
+		sdev->unit = (SDunit **)malloc(sdev->nunit * sizeof(SDunit*));
+		sdev->unitflg = (int *)malloc(sdev->nunit * sizeof(int));
+		assert(sdev->unit && sdev->unitflg);
+
+		devs[ndevs].dev = sdev;
+		devs[ndevs].nunits = sdev->nunit;
+		sdev = sdev->next;
+		devs[ndevs].dev->next = nil;
+		ndevs++;
+	}
+
+	qunlock(&devslock);
+	return 0;
+}
+
+static int
+unconfigure(char* spec)
+{
+	int i;	
+	SDev *sdev;
+
+	qlock(&devslock);
+	if(waserror()){
+		qunlock(&devslock);
+		nexterror();
+	}
+
+	sdev = nil;
+	for(i = 0; i != ndevs; i++)
+		if((sdev = devs[i].dev) != nil && sdev->idno == *spec)
+			break;
+
+	if(i == ndevs)
+		error(Enonexist);
+
+	if(sdev->r.ref)
+		error(Einuse);
+
+	/* make sure no interrupts arrive anymore before removing resources */
+	if(sdev->enabled && sdev->ifc->disable)
+		sdev->ifc->disable(sdev);
+
+	/* we're alone and the device tab is locked; make the device unavailable */
+	memmove(&devs[i], &devs[ndevs - 1], sizeof(SDevgrp));
+	memset(&devs[ndevs - 1], 0, sizeof(SDevgrp));
+	ndevs--;
+
+	qunlock(&devslock);
+	poperror();
+
+	for(i = 0; i != sdev->nunit; i++)
+		if(sdev->unit[i]){
+			SDunit *unit = sdev->unit[i];
+
+			free(unit->name);
+			free(unit->user);
+			free(unit);
+		}
+
+	if(sdev->ifc->clear)
+		sdev->ifc->clear(sdev);
+	return 0;
+}
+
+static int
+sdconfig(int on, char* spec, DevConf* cf)
+{
+	if(on)
+		return configure(spec, cf);
+	return unconfigure(spec);
+}
+
+Dev sddevtab = {
+	'S',
+	"sd",
+
+	sdreset,
+	devinit,
+	devshutdown,
+	sdattach,
+	sdwalk,
+	sdstat,
+	sdopen,
+	devcreate,
+	sdclose,
+	sdread,
+	devbread,
+	sdwrite,
+	devbwrite,
+	devremove,
+	sdwstat,
+	devpower,
+	sdconfig,
+};
--- /dev/null
+++ b/os/port/devsign.c
@@ -1,0 +1,446 @@
+#include	"u.h"
+#include	"../port/lib.h"
+#include	"mem.h"
+#include	"dat.h"
+#include	"fns.h"
+#include	"../port/error.h"
+
+#include	"interp.h"
+#include	<isa.h>
+#include	"runt.h"
+#include	"mp.h"
+#include	"libsec.h"
+#include "../../libkeyring/keys.h"
+
+/*
+ * experimental version of signed modules
+ */
+
+enum
+{
+	Qdir,
+	Qkey,
+	Qctl,
+
+	Maxkey = 2048
+};
+
+static Dirtab signdir[] =
+{
+	".",		{Qdir, 0, QTDIR},	0,	DMDIR|0555,
+	"signerkey",	{Qkey},	0,			0644,
+	"signerctl",	{Qctl},	0,			0600,
+};
+
+typedef struct Get Get;
+struct Get {
+	uchar*	p;
+	uchar*	ep;
+};
+
+#define	G32(b)	((b[0]<<24)|(b[1]<<16)|(b[2]<<8)|b[3])
+
+static	int	vc(Get*);
+static	int	vs(void*, int, Get*, int);
+static Signerkey* findsignerkey(Skeyset*, char*, int, char*);
+extern vlong		osusectime(void);
+
+int
+verifysigner(uchar *sign, int len, uchar *data, ulong ndata)
+{
+	Get sig;
+	int alg;
+	ulong issued, expires, now;
+	int footprint, r, n;
+	uchar buf[128], digest[SHA1dlen];
+	DigestState *ds;
+	volatile struct {BigInt b;} b;
+	volatile struct {BigInt s;} s;
+	SigAlgVec *sa;
+	Signerkey *key;
+	Skeyset *sigs;
+
+	/* alg[1] issued[4] expires[4] footprint[2] signer[n] sig[m] */
+	sigs = up->env->sigs;
+	if(sigs == nil)
+		return 1;	/* not enforcing signed modules */
+	sig.p = sign;
+	sig.ep = sign+len;
+	alg = vc(&sig);
+	if(alg != 2)
+		return 0;	/* we do only SHA1/RSA */
+	sa = findsigalg("rsa");
+	if(sa == nil)
+		return 0;
+	if(vs(buf, sizeof(buf), &sig, 4) < 0)
+		return 0;
+	now = osusectime()/1000000;
+	issued = G32(buf);
+	if(vs(buf, sizeof(buf), &sig, 4) < 0)
+		return 0;
+	if(issued != 0 && now < issued)
+		return 0;
+	expires = G32(buf);
+	if(expires != 0 && now >= expires)
+		return 0;
+	footprint = vc(&sig) << 8;
+	footprint |= vc(&sig);
+	if(footprint < 0)
+		return 0;
+	r = 0;
+	b.b = nil;
+	s.s = nil;
+	qlock(sigs);
+	if(waserror())
+		goto out;
+	if((n = vs(buf, sizeof(buf)-NUMSIZE-1, &sig, -1)) < 0)	/* owner */
+		goto out;
+	buf[n] = 0;
+	key = findsignerkey(sigs, sa->name, footprint, (char*)buf);
+	if(key == nil)
+		goto out;
+	n += snprint((char*)buf+n, NUMSIZE, " %lud", expires);
+	ds = sha1(buf, n, nil, nil);
+	sha1(data, ndata, digest, ds);
+	b.b = betomp(digest, SHA1dlen, nil);
+	if(b.b == nil)
+		goto out;
+	s.s = betomp(sig.p, sig.ep-sig.p, nil);
+	if(s.s == nil)
+		goto out;
+	r = (*sa->verify)(b.b, s.s, key->pk);
+out:
+	qunlock(sigs);
+	if(b.b != nil)
+		mpfree(b.b);
+	if(s.s != nil)
+		mpfree(s.s);
+	return r;
+}
+
+int
+mustbesigned(char *path, uchar*, ulong, Dir *dir)
+{
+	USED(path);
+if(0)print("load %s: %d %C\n", path, up->env->sigs!=nil, dir==nil?'?':dir->type);
+	/* allow only signed modules and those in #/; already loaded modules are reloaded from cache */
+	return up->env->sigs != nil && (dir == nil || dir->type != '/');
+}
+
+static int
+vc(Get *g)
+{
+	return g->p < g->ep? *g->p++: -1;
+}
+
+static int
+vs(void *s, int lim, Get *g, int n)
+{
+	int nr;
+
+	if(n < 0){
+		if(g->p >= g->ep)
+			return -1;
+		n = *g->p++;
+		lim--;
+	}
+	if(n > lim)
+		return -1;
+	nr = g->ep - g->p;
+	if(n > nr)
+		return -1;
+	if(s != nil)
+		memmove(s, g->p, n);
+	g->p += n;
+	return n;
+}
+
+static char*
+cstring(char *str, char **strp)
+{
+	char *p, *s;
+	int n;
+
+	p = strchr(str, '\n');
+	if(p == 0)
+		p = str + strlen(str);
+	n = p - str;
+	s = malloc(n+1);
+	if(s == nil)
+		return nil;
+	memmove(s, str, n);
+	s[n] = 0;
+
+	if(strp){
+		if(*p)
+			p++;
+		*strp = p;
+	}
+
+	return s;
+}
+
+static SigAlgVec*
+cstrtoalg(char *str, char **strp)
+{
+	int n;
+	char *p, name[KNAMELEN];
+
+	p = strchr(str, '\n');
+	if(p == 0){
+		p = str + strlen(str);
+		if(strp)
+			*strp = p;
+	} else {
+		if(strp)
+			*strp = p+1;
+	}
+
+	n = p - str;
+	if(n >= sizeof(name))
+		return nil;
+	strncpy(name, str, n);
+	name[n] = 0;
+	return findsigalg(name);
+}
+
+static Signerkey*
+strtopk(char *buf)
+{
+	SigAlgVec *sa;
+	char *p;
+	Signerkey *key;
+
+	key = malloc(sizeof(*key));
+	if(key == nil)
+		return nil;
+	key->ref = 1;
+	sa = cstrtoalg(buf, &p);
+	if(sa == nil){
+		free(key);
+		return nil;
+	}
+	key->alg = sa;
+	key->pkfree = sa->pkfree;
+	key->owner = cstring(p, &p);
+	if(key->owner == nil){
+		free(key);
+		return nil;
+	}
+	key->pk = (*sa->str2pk)(p, &p);
+	if(key->pk == nil){
+		free(key->owner);
+		free(key);
+		return nil;
+	}
+	return key;
+}
+
+static Signerkey*
+findsignerkey(Skeyset *sigs, char *alg, int footprint, char *owner)
+{
+	int i;
+	Signerkey *key;
+
+	for(i=0; i<sigs->nkey; i++){
+		key = sigs->keys[i];
+		if(key->footprint == footprint &&
+		   strcmp(alg, ((SigAlgVec*)key->alg)->name) == 0 &&
+		   strcmp(key->owner, owner) == 0)
+			return key;
+	}
+	return nil;
+}
+
+static Chan*
+signattach(char *spec)
+{
+	return devattach(L'Σ', spec);
+}
+
+static Walkqid*
+signwalk(Chan *c, Chan *nc, char **name, int nname)
+{
+	return devwalk(c, nc, name, nname, signdir, nelem(signdir), devgen);
+}
+
+static int
+signstat(Chan *c, uchar *db, int n)
+{
+	return devstat(c, db, n, signdir, nelem(signdir), devgen);
+}
+
+static Chan*
+signopen(Chan *c, int omode)
+{
+	if(c->qid.type & QTDIR) {
+		if(omode != OREAD)
+			error(Eisdir);
+		c->mode = openmode(omode);
+		c->flag |= COPEN;
+		c->offset = 0;
+		return c;
+	}
+
+	switch((ulong)c->qid.path){
+	case Qctl:
+		if(!iseve())
+			error(Eperm);
+		break;
+
+	case Qkey:
+		if(omode != OREAD && !iseve())
+			error(Eperm);
+		break;
+	}
+
+	c->mode = openmode(omode);
+	c->flag |= COPEN;
+	c->offset = 0;
+	return c;
+}
+
+static void
+signclose(Chan *c)
+{
+	USED(c);
+}
+
+static long
+signread(Chan *c, void *va, long n, vlong offset)
+{
+	char *buf, *p;
+	SigAlgVec *sa;
+	Skeyset *sigs;
+	Signerkey *key;
+	int i;
+
+	if(c->qid.type & QTDIR)
+		return devdirread(c, va, n, signdir, nelem(signdir), devgen);
+	sigs = up->env->sigs;
+	if(sigs == nil)
+		return 0;
+	switch((ulong)c->qid.path){
+	case Qkey:
+		buf = smalloc(Maxkey);
+		if(waserror()){
+			free(buf);
+			nexterror();
+		}
+		qlock(sigs);
+		if(waserror()){
+			qunlock(sigs);
+			nexterror();
+		}
+		p = buf;
+		for(i=0; i<sigs->nkey; i++){
+			key = sigs->keys[i];
+			sa = key->alg;
+			p = seprint(p, buf+Maxkey, "owner=%s alg=%s footprint=%ud expires=%lud\n",
+				key->owner, sa->name, key->footprint, key->expires);
+		}
+		poperror();
+		qunlock(sigs);
+		n = readstr(offset, va, n, buf);
+		poperror();
+		free(buf);
+		return n;
+
+	case Qctl:
+		return readnum(offset, va, n, sigs->nkey, NUMSIZE);
+	}
+	return 0;
+}
+
+static long
+signwrite(Chan *c, void *va, long n, vlong offset)
+{
+	char *buf;
+	Skeyset *sigs;
+	Signerkey *okey, *key;
+	int i;
+
+	if(c->qid.type & QTDIR)
+		error(Eisdir);
+	USED(offset);
+	switch((ulong)c->qid.path){
+	case Qkey:
+		if(n >= Maxkey)
+			error(Etoobig);
+		buf = smalloc(Maxkey);
+		if(waserror()){
+			free(buf);
+			nexterror();
+		}
+		memmove(buf, va, n);
+		buf[n] = 0;
+
+		key = strtopk(buf);
+		if(key == nil)
+			error("bad key syntax");
+		poperror();
+		free(buf);
+
+		if(waserror()){
+			freeskey(key);
+			nexterror();
+		}
+		sigs = up->env->sigs;
+		if(sigs == nil){
+			sigs = malloc(sizeof(*sigs));
+			if(sigs == nil)
+				error(Enomem);
+			sigs->ref = 1;
+			up->env->sigs = sigs;
+		}
+		qlock(sigs);
+		if(waserror()){
+			qunlock(sigs);
+			nexterror();
+		}
+		for(i=0; i<sigs->nkey; i++){
+			okey = sigs->keys[i];
+			if(strcmp(okey->owner, key->owner) == 0){
+				/* replace existing key */
+				sigs->keys[i] = key;
+				freeskey(okey);
+				break;
+			}
+		}
+		if(i >= sigs->nkey){
+			if(sigs->nkey >= nelem(sigs->keys))
+				error("too many keys");
+			sigs->keys[sigs->nkey++] = key;
+		}
+		poperror();
+		qunlock(sigs);
+		poperror();	/* key */
+
+		return n;
+	case Qctl:
+		error(Ebadctl);
+		break;
+	}
+	return 0;
+}
+
+Dev signdevtab = {
+	L'Σ',
+	"sign",
+
+	devreset,
+	devinit,
+	devshutdown,
+	signattach,
+	signwalk,
+	signstat,
+	signopen,
+	devcreate,
+	signclose,
+	signread,
+	devbread,
+	signwrite,
+	devbwrite,
+	devremove,
+	devwstat
+};
--- /dev/null
+++ b/os/port/devsrv.c
@@ -1,0 +1,834 @@
+#include "u.h"
+#include "../port/lib.h"
+#include "mem.h"
+#include "dat.h"
+#include "fns.h"
+#include "../port/error.h"
+#include "interp.h"
+#include "isa.h"
+#include "runt.h"
+
+typedef struct SrvFile SrvFile;
+typedef struct Pending Pending;
+
+/* request pending to a server, in case a server vanishes */
+struct Pending
+{
+	Pending*	next;
+	Pending*	prev;
+	int fid;
+	Channel*	rc;
+	Channel*	wc;
+};
+
+struct SrvFile
+{
+	char*	name;
+	char*	user;
+	ulong		perm;
+	Qid		qid;
+	int		ref;
+
+	/* root directory */
+	char*	spec;
+	SrvFile*	devlist;
+	SrvFile*	entry;
+
+	/* file */
+	int		opens;
+	int		flags;
+	vlong	length;
+	Channel*	read;
+	Channel*	write;
+	SrvFile*	dir;		/* parent directory */
+	Pending	waitlist;	/* pending requests from client opens */
+};
+
+enum
+{
+	SORCLOSE	= (1<<0),
+	SRDCLOSE	= (1<<1),
+	SWRCLOSE	= (1<<2),
+	SREMOVED	= (1<<3),
+};
+
+typedef struct SrvDev SrvDev;
+struct SrvDev
+{
+	Type*	Rread;
+	Type*	Rwrite;
+	QLock	l;
+	ulong	pathgen;
+	SrvFile*	devices;
+};
+
+static SrvDev dev;
+
+void	freechan(Heap*, int);
+static void	freerdchan(Heap*, int);
+static void	freewrchan(Heap*, int);
+static void delwaiting(Pending*);
+
+Type	*Trdchan;
+Type	*Twrchan;
+
+static int
+srvgen(Chan *c, char *name, Dirtab *tab, int ntab, int s, Dir *dp)
+{
+	SrvFile *f;
+
+	USED(name);
+	USED(tab);
+	USED(ntab);
+
+	if(s == DEVDOTDOT){
+		devdir(c, c->qid, "#s", 0, eve, 0555, dp);
+		return 1;
+	}
+	f = c->aux;
+	if((c->qid.type & QTDIR) == 0){
+		if(s > 0)
+			return -1;
+		devdir(c, f->qid, f->name, f->length, f->user, f->perm, dp);
+		return 1;
+	}
+
+	for(f = f->entry; f != nil; f = f->entry){
+		if(s-- == 0)
+			break;
+	}
+	if(f == nil)
+		return -1;
+
+	devdir(c, f->qid, f->name, f->length, f->user, f->perm, dp);
+	return 1;
+}
+
+static void
+srvinit(void)
+{
+	static uchar rmap[] = Sys_Rread_map;
+	static uchar wmap[] = Sys_Rwrite_map;
+
+	Trdchan = dtype(freerdchan, sizeof(Channel), Tchannel.map, Tchannel.np);
+	Twrchan = dtype(freewrchan, sizeof(Channel), Tchannel.map, Tchannel.np);
+
+	dev.pathgen = 1;
+	dev.Rread = dtype(freeheap, Sys_Rread_size, rmap, sizeof(rmap));
+	dev.Rwrite = dtype(freeheap, Sys_Rwrite_size, wmap, sizeof(wmap));
+}
+
+static int
+srvcanattach(SrvFile *d)
+{
+	if(strcmp(d->user, up->env->user) == 0)
+		return 1;
+
+	/*
+	 * Need write permission in other to allow attaches if
+	 * we are not the owner
+	 */
+	if(d->perm & 2)
+		return 1;
+
+	return 0;
+}
+
+static Chan*
+srvattach(char *spec)
+{
+	Chan *c;
+	SrvFile *d;
+	char srvname[16];
+
+	qlock(&dev.l);
+	if(waserror()){
+		qunlock(&dev.l);
+		nexterror();
+	}
+
+	if(spec[0] != '\0'){
+		for(d = dev.devices; d != nil; d = d->devlist){
+			if(strcmp(spec, d->spec) == 0){
+				if(!srvcanattach(d))
+					error(Eperm);
+				c = devattach('s', spec);
+				c->aux = d;
+				c->qid = d->qid;
+				d->ref++;
+				poperror();
+				qunlock(&dev.l);
+				return c;
+			}
+		}
+	}
+
+	d = malloc(sizeof(SrvFile));
+	if(d == nil)
+		error(Enomem);
+
+	d->ref = 1;
+	kstrdup(&d->spec, spec);
+	kstrdup(&d->user, up->env->user);
+	snprint(srvname, sizeof(srvname), "srv%ld", up->env->pgrp->pgrpid);
+	kstrdup(&d->name, srvname);
+	d->perm = DMDIR|0770;
+	mkqid(&d->qid, dev.pathgen++, 0, QTDIR);
+
+	d->devlist = dev.devices;
+	dev.devices = d;
+
+	poperror();
+	qunlock(&dev.l);
+
+	c = devattach('s', spec);
+	c->aux = d;
+	c->qid = d->qid;
+
+	return c;
+}
+
+static Walkqid*
+srvwalk(Chan *c, Chan *nc, char **name, int nname)
+{
+	SrvFile *d, *pd;
+	Walkqid *w;
+
+	pd = c->aux;
+	qlock(&dev.l);
+	if(waserror()){
+		qunlock(&dev.l);
+		nexterror();
+	}
+
+	w = devwalk(c, nc, name, nname, nil, 0, srvgen);
+	if(w != nil && w->clone != nil){
+		if(nname != 0){
+			for(d = pd->entry; d != nil; d = d->entry)
+				if(d->qid.path == w->clone->qid.path)
+					break;
+			if(d == nil)
+				panic("srvwalk");
+			if(w->clone == c)
+				pd->ref--;
+		}else
+			d = pd;
+		w->clone->aux = d;
+		d->ref++;
+	}
+
+	poperror();
+	qunlock(&dev.l);
+	return w;
+}
+
+static int
+srvstat(Chan *c, uchar *db, int n)
+{
+	qlock(&dev.l);
+	if(waserror()){
+		qunlock(&dev.l);
+		nexterror();
+	}
+	n = devstat(c, db, n, 0, 0, srvgen);
+	poperror();
+	qunlock(&dev.l);
+	return n;
+}
+
+static Chan*
+srvopen(Chan *c, int omode)
+{
+	SrvFile *sf;
+
+	openmode(omode);	/* check it */
+	if(c->qid.type & QTDIR){
+		if(omode != OREAD)
+			error(Eisdir);
+		c->mode = omode;
+		c->flag |= COPEN;
+		c->offset = 0;
+		return c;
+	}
+
+	sf = c->aux;
+
+	qlock(&dev.l);
+	if(waserror()){
+		qunlock(&dev.l);
+		nexterror();
+	}
+	devpermcheck(sf->user, sf->perm, omode);
+	if(omode&ORCLOSE && strcmp(sf->user, up->env->user) != 0)
+		error(Eperm);
+	if(sf->perm & DMEXCL && sf->opens != 0)
+		error(Einuse);
+	sf->opens++;
+	if(omode&ORCLOSE)
+		sf->flags |= SORCLOSE;
+	poperror();
+	qunlock(&dev.l);
+
+	c->offset = 0;
+	c->flag |= COPEN;
+	c->mode = openmode(omode);
+
+	return c;
+}
+
+static int
+srvwstat(Chan *c, uchar *dp, int n)
+{
+	Dir *d;
+	SrvFile *sf, *f;
+
+	sf = c->aux;
+	if(strcmp(up->env->user, sf->user) != 0)
+		error(Eperm);
+
+	d = smalloc(sizeof(*d)+n);
+	if(waserror()){
+		free(d);
+		nexterror();
+	}
+	n = convM2D(dp, n, d, (char*)&d[1]);
+	if(n == 0)
+		error(Eshortstat);
+	if(!emptystr(d->name)){
+		if(sf->dir == nil)
+			error(Eperm);
+		validwstatname(d->name);
+		qlock(&dev.l);
+		for(f = sf->dir; f != nil; f = f->entry)
+			if(strcmp(f->name, d->name) == 0){
+				qunlock(&dev.l);
+				error(Eexist);
+			}
+		kstrdup(&sf->name, d->name);
+		qunlock(&dev.l);
+	}
+	if(d->mode != ~0UL)
+		sf->perm = d->mode & (DMEXCL|DMAPPEND|0777);
+	if(d->length != (vlong)-1)
+		sf->length = d->length;
+	poperror();
+	free(d);
+	return n;
+}
+
+static void
+srvputdir(SrvFile *dir)
+{
+	SrvFile **l, *d;
+
+	dir->ref--;
+	if(dir->ref != 0)
+		return;
+
+	for(l = &dev.devices; (d = *l) != nil; l = &d->devlist)
+		if(d == dir){
+			*l = d->devlist;
+			break;
+		}
+	free(dir->spec);
+	free(dir->user);
+	free(dir->name);
+	free(dir);
+}
+
+static void
+srvunblock(SrvFile *sf, int fid)
+{
+	Channel *d;
+	Sys_FileIO_read rreq;
+	Sys_FileIO_write wreq;
+
+	acquire();
+	if(waserror()){
+		release();
+		nexterror();
+	}
+	d = sf->read;
+	if(d != H){
+		rreq.t0 = 0;
+		rreq.t1 = 0;
+		rreq.t2 = fid;
+		rreq.t3 = H;
+		csendalt(d, &rreq, d->mid.t, -1);
+	}
+
+	d = sf->write;
+	if(d != H){
+		wreq.t0 = 0;
+		wreq.t1 = H;
+		wreq.t2 = fid;
+		wreq.t3 = H;
+		csendalt(d, &wreq, d->mid.t, -1);
+	}
+	poperror();
+	release();
+}
+
+static void
+srvcancelreqs(SrvFile *sf)
+{
+	Pending *w, *ws;
+	Sys_Rread rreply;
+	Sys_Rwrite wreply;
+
+	acquire();
+	ws = &sf->waitlist;
+	while((w = ws->next) != ws){
+		delwaiting(w);
+		if(waserror() == 0){
+			if(w->rc != nil){
+				rreply.t0 = H;
+				rreply.t1 = c2string(Ehungup, strlen(Ehungup));
+				csend(w->rc, &rreply);
+			}
+			if(w->wc != nil){
+				wreply.t0 = 0;
+				wreply.t1 = c2string(Ehungup, strlen(Ehungup));
+				csend(w->wc, &wreply);
+			}
+			poperror();
+		}
+	}
+	release();
+}
+
+static void
+srvdelete(SrvFile *sf)
+{
+	SrvFile *f, **l;
+
+	if((sf->flags & SREMOVED) == 0){
+		for(l = &sf->dir->entry; (f = *l) != nil; l = &f->entry){
+			if(sf == f){
+				*l = f->entry;
+				break;
+			}
+		}
+		sf->ref--;
+		sf->flags |= SREMOVED;
+	}
+}
+
+static void
+srvchkref(SrvFile *sf)
+{
+	if(sf->ref != 0)
+		return;
+
+	if(sf->dir != nil)
+		srvputdir(sf->dir);
+
+	free(sf->user);
+	free(sf->name);
+	free(sf);
+}
+
+static void
+srvfree(SrvFile *sf, int flag)
+{
+	sf->flags |= flag;
+	if((sf->flags & (SRDCLOSE | SWRCLOSE)) == (SRDCLOSE | SWRCLOSE)){
+		sf->ref--;
+		srvdelete(sf);
+		/* no further requests can arrive; return error to pending requests */
+		srvcancelreqs(sf);
+		srvchkref(sf);
+	}
+}
+
+static void
+freerdchan(Heap *h, int swept)
+{
+	SrvFile *sf;
+
+	release();
+	qlock(&dev.l);
+	sf = H2D(Channel*, h)->aux;
+	sf->read = H;
+	srvfree(sf, SRDCLOSE);
+	qunlock(&dev.l);
+	acquire();
+
+	freechan(h, swept);
+}
+
+static void
+freewrchan(Heap *h, int swept)
+{
+	SrvFile *sf;
+
+	release();
+	qlock(&dev.l);
+	sf = H2D(Channel*, h)->aux;
+	sf->write = H;
+	srvfree(sf, SWRCLOSE);
+	qunlock(&dev.l);
+	acquire();
+
+	freechan(h, swept);
+}
+
+static void
+srvclunk(Chan *c, int remove)
+{
+	int opens, noperm;
+	SrvFile *sf;
+
+	sf = c->aux;
+	qlock(&dev.l);
+	if(c->qid.type & QTDIR){
+		srvputdir(sf);
+		qunlock(&dev.l);
+		if(remove)
+			error(Eperm);
+		return;
+	}
+	opens = 0;
+	if(c->flag & COPEN){
+		opens = sf->opens--;
+		if(sf->read != H || sf->write != H)
+			srvunblock(sf, c->fid);
+	}
+
+	sf->ref--;
+	if(opens == 1){
+		if(sf->flags & SORCLOSE)
+			remove = 1;
+	}
+
+	noperm = 0;
+	if(remove && strcmp(sf->dir->user, up->env->user) != 0){
+		noperm = 1;
+		remove = 0;
+	}
+	if(remove)
+		srvdelete(sf);
+	srvchkref(sf);
+	qunlock(&dev.l);
+
+	if(noperm)
+		error(Eperm);
+}
+
+static void
+srvclose(Chan *c)
+{
+	srvclunk(c, 0);
+}
+
+static void
+srvremove(Chan *c)
+{
+	srvclunk(c, 1);
+}
+
+static void
+addwaiting(SrvFile *sp, Pending *w)
+{
+	Pending *sw;
+
+	sw = &sp->waitlist;
+	w->next = sw;
+	w->prev = sw->prev;
+	sw->prev->next = w;
+	sw->prev = w;
+}
+
+static void
+delwaiting(Pending *w)
+{
+	w->next->prev = w->prev;
+	w->prev->next = w->next;
+}
+
+static long
+srvread(Chan *c, void *va, long count, vlong offset)
+{
+	int l;
+	Heap * volatile h;
+	Array *a;
+	SrvFile *sp;
+	Channel *rc;
+	Channel *rd;
+	Pending wait;
+	Sys_Rread * volatile r;
+	Sys_FileIO_read req;
+
+	if(c->qid.type & QTDIR){
+		qlock(&dev.l);
+		if(waserror()){
+			qunlock(&dev.l);
+			nexterror();
+		}
+		l = devdirread(c, va, count, 0, 0, srvgen);
+		poperror();
+		qunlock(&dev.l);
+		return l;
+	}
+
+	sp = c->aux;
+
+	acquire();
+	if(waserror()){
+		release();
+		nexterror();
+	}
+
+	rd = sp->read;
+	if(rd == H)
+		error(Ehungup);
+
+	rc = cnewc(dev.Rread, movtmp, 1);
+	ptradd(D2H(rc));
+	if(waserror()){
+		ptrdel(D2H(rc));
+		destroy(rc);
+		nexterror();
+	}
+
+	req.t0 = offset;
+	req.t1 = count;
+	req.t2 = c->fid;
+	req.t3 = rc;
+	csend(rd, &req);
+
+	h = heap(dev.Rread);
+	r = H2D(Sys_Rread *, h);
+	ptradd(h);
+	if(waserror()){
+		ptrdel(h);
+		destroy(r);
+		nexterror();
+	}
+
+	wait.fid = c->fid;
+	wait.rc = rc;
+	wait.wc = nil;
+	addwaiting(sp, &wait);
+	if(waserror()){
+		delwaiting(&wait);
+		nexterror();
+	}
+	crecv(rc, r);
+	poperror();
+	delwaiting(&wait);
+
+	if(r->t1 != H)
+		error(string2c(r->t1));
+
+	a = r->t0;
+	l = 0;
+	if(a != H){
+		l = a->len;
+		if(l > count)
+			l = count;
+		memmove(va, a->data, l);
+	}
+
+	poperror();
+	ptrdel(h);
+	destroy(r);
+
+	poperror();
+	ptrdel(D2H(rc));
+	destroy(rc);
+
+	poperror();
+	release();
+
+	return l;
+}
+
+static long
+srvwrite(Chan *c, void *va, long count, vlong offset)
+{
+	long l;
+	Heap * volatile h;
+	SrvFile *sp;
+	Channel *wc;
+	Channel *wr;
+	Pending wait;
+	Sys_Rwrite * volatile w;
+	Sys_FileIO_write req;
+
+	if(c->qid.type & QTDIR)
+		error(Eperm);
+
+	acquire();
+	if(waserror()){
+		release();
+		nexterror();
+	}
+
+	sp = c->aux;
+	wr = sp->write;
+	if(wr == H)
+		error(Ehungup);
+
+	wc = cnewc(dev.Rwrite, movtmp, 1);
+	ptradd(D2H(wc));
+	if(waserror()){
+		ptrdel(D2H(wc));
+		destroy(wc);
+		nexterror();
+	}
+
+	req.t0 = offset;
+	req.t1 = mem2array(va, count);
+	req.t2 = c->fid;
+	req.t3 = wc;
+
+	ptradd(D2H(req.t1));
+
+	if(waserror()){
+		ptrdel(D2H(req.t1));
+		destroy(req.t1);
+		nexterror();
+	}
+
+	csend(wr, &req);
+
+	poperror();
+	ptrdel(D2H(req.t1));
+	destroy(req.t1);
+
+	h = heap(dev.Rwrite);
+	w = H2D(Sys_Rwrite *, h);
+	ptradd(h);
+
+	if(waserror()){
+		ptrdel(h);
+		destroy(w);
+		nexterror();
+	}
+
+	wait.fid = c->fid;
+	wait.rc = nil;
+	wait.wc = wc;
+	addwaiting(sp, &wait);
+	if(waserror()){
+		delwaiting(&wait);
+		nexterror();
+	}
+	crecv(wc, w);
+	poperror();
+	delwaiting(&wait);
+
+	if(w->t1 != H)
+		error(string2c(w->t1));
+	poperror();
+	ptrdel(h);
+	l = w->t0;
+	destroy(w);
+
+	poperror();
+	ptrdel(D2H(wc));
+	destroy(wc);
+
+	poperror();
+	release();
+	if(l < 0)
+		l = 0;
+	return l;
+}
+
+static void
+srvretype(Channel *c, SrvFile *f, Type *t)
+{
+	Heap *h;
+
+	h = D2H(c);
+	freetype(h->t);
+	h->t = t;
+	t->ref++;
+	c->aux = f;
+}
+
+int
+srvf2c(char *dir, char *file, Sys_FileIO *io)
+{
+	SrvFile *s, *f;
+	volatile struct { Chan *c; } c;
+
+	c.c = nil;
+	if(waserror()){
+		cclose(c.c);
+		return -1;
+	}
+
+	if(strchr(file, '/') != nil || strlen(file) >= 64 || strcmp(file, ".") == 0 || strcmp(file, "..") == 0)
+		error(Efilename);
+
+	c.c = namec(dir, Aaccess, 0, 0);
+	if((c.c->qid.type&QTDIR) == 0 || devtab[c.c->type]->dc != 's')
+		error("directory not a srv device");
+
+	s = c.c->aux;
+
+	qlock(&dev.l);
+	if(waserror()){
+		qunlock(&dev.l);
+		nexterror();
+	}
+	for(f = s->entry; f != nil; f = f->entry){
+		if(strcmp(f->name, file) == 0)
+			error(Eexist);
+	}
+
+	f = malloc(sizeof(SrvFile));
+	if(f == nil)
+		error(Enomem);
+
+	srvretype(io->read, f, Trdchan);
+	srvretype(io->write, f, Twrchan);
+	f->read = io->read;
+	f->write = io->write;
+	
+	f->waitlist.next = &f->waitlist;
+	f->waitlist.prev = &f->waitlist;
+
+	kstrdup(&f->name, file);
+	kstrdup(&f->user, up->env->user);
+	f->perm = 0666 & (~0666 | (s->perm & 0666));
+	f->length = 0;
+	f->ref = 2;
+	mkqid(&f->qid, dev.pathgen++, 0, QTFILE);
+
+	f->entry = s->entry;
+	s->entry = f;
+	s->ref++;
+	f->dir = s;
+	poperror();
+	qunlock(&dev.l);
+
+	cclose(c.c);
+	poperror();
+
+	return 0;
+}
+
+Dev srvdevtab = {
+	's',
+	"srv",
+
+	devreset,
+	srvinit,
+	devshutdown,
+	srvattach,
+	srvwalk,
+	srvstat,
+	srvopen,
+	devcreate,
+	srvclose,
+	srvread,
+	devbread,
+	srvwrite,
+	devbwrite,
+	srvremove,
+	srvwstat
+};
--- /dev/null
+++ b/os/port/devssl.c
@@ -1,0 +1,1432 @@
+/*
+ *  devssl - secure sockets layer
+ */
+#include "u.h"
+#include "../port/lib.h"
+#include "../port/error.h"
+#include "mem.h"
+#include	"dat.h"
+#include	"fns.h"
+#include	"kernel.h"
+
+#include	"mp.h"
+#include	"libsec.h"
+
+typedef struct OneWay OneWay;
+struct OneWay
+{
+	QLock	q;
+	QLock	ctlq;
+
+	void	*state;		/* encryption state */
+	int	slen;			/* secret data length */
+	uchar	*secret;		/* secret */
+	ulong	mid;		/* message id */
+};
+
+enum
+{
+	/* connection states */
+	Sincomplete=	0,
+	Sclear=		1,
+	Sencrypting=	2,
+	Sdigesting=	4,
+	Sdigenc=	Sencrypting|Sdigesting,
+
+	/* encryption algorithms */
+	Noencryption=	0,
+	DESCBC=		1,
+	DESECB=		2,
+	RC4=		3,
+	IDEACBC=	4,
+	IDEAECB=		5
+};
+
+typedef struct Dstate Dstate;
+struct Dstate
+{
+	Chan	*c;			/* io channel */
+	uchar	state;		/* state of connection */
+	int	ref;			/* serialized by dslock for atomic destroy */
+
+	uchar	encryptalg;	/* encryption algorithm */
+	ushort	blocklen;	/* blocking length */
+
+	ushort	diglen;		/* length of digest */
+	DigestState *(*hf)(uchar*, ulong, uchar*, DigestState*);	/* hash func */
+
+	/* for SSL format */
+	int	max;			/* maximum unpadded data per msg */
+	int	maxpad;			/* maximum padded data per msg */
+
+	/* input side */
+	OneWay	in;
+	Block	*processed;
+	Block	*unprocessed;
+
+	/* output side */
+	OneWay	out;
+
+	/* protections */
+	char*	user;
+	int	perm;
+};
+
+Lock	dslock;
+int	dshiwat;
+int	maxdstate = 20;
+Dstate** dstate;
+
+enum
+{
+	Maxdmsg=	1<<16,
+	Maxdstate=	1<<10,
+};
+
+enum{
+	Qtopdir		= 1,	/* top level directory */
+	Qclonus,
+	Qconvdir,			/* directory for a conversation */
+	Qdata,
+	Qctl,
+	Qsecretin,
+	Qsecretout,
+	Qencalgs,
+	Qhashalgs
+};
+
+#define TYPE(x) 	((ulong)(x).path & 0xf)
+#define CONV(x) 	(((ulong)(x).path >> 4)&(Maxdstate-1))
+#define QID(c, y) 	(((c)<<4) | (y))
+
+/* for generating random fill */
+ulong badlong;
+uchar *badarray = (uchar*)&badlong;
+static char*	encalgs;
+static char*	hashalgs;
+
+void producerand(void);
+
+static void alglistinit(void);
+static void	ensure(Dstate*, Block**, int);
+static void	consume(Block**, uchar*, int);
+static void	setsecret(OneWay*, uchar*, int);
+static Block*	encryptb(Dstate*, Block*, int);
+static Block*	decryptb(Dstate*, Block*);
+static Block*	digestb(Dstate*, Block*, int);
+static void	checkdigestb(Dstate*, Block*);
+static Chan*	buftochan(char*);
+static void	sslhangup(Dstate*);
+static void	dsclone(Chan *c);
+static void	dsnew(Chan *c, Dstate **);
+
+static int
+sslgen(Chan *c, char*, Dirtab *d, int nd, int s, Dir *dp)
+{
+	Qid q;
+	Dstate *ds;
+	char *p, *nm;
+
+	USED(nd);
+	USED(d);
+	q.type = QTFILE;
+	q.vers = 0;
+	if(s == DEVDOTDOT){
+		q.path = QID(0, Qtopdir);
+		q.type = QTDIR;
+		devdir(c, q, "#D", 0, eve, 0555, dp);
+		return 1;
+	}
+	switch(TYPE(c->qid)) {
+	case Qtopdir:
+		if(s < dshiwat) {
+			q.path = QID(s, Qconvdir);
+			q.type = QTDIR;
+			ds = dstate[s];
+			if(ds != 0)
+ 				nm = ds->user;
+ 			else
+ 				nm = eve;
+			snprint(up->genbuf, sizeof(up->genbuf), "%d", s);
+			devdir(c, q, up->genbuf, 0, nm, DMDIR|0555, dp);
+			return 1;
+		}
+		if(s > dshiwat)
+			return -1;
+		/* fall through */
+	case Qclonus:
+		q.path = QID(0, Qclonus);
+		devdir(c, q, "clone", 0, eve, 0666, dp);
+		return 1;
+	case Qconvdir:
+		ds = dstate[CONV(c->qid)];
+		if(ds != 0)
+			nm = ds->user;
+		else
+			nm = eve;
+		switch(s) {
+		default:
+			return -1;
+		case 0:
+			q.path = QID(CONV(c->qid), Qctl);
+			p = "ctl";
+			break;
+		case 1:
+			q.path = QID(CONV(c->qid), Qdata);
+			p = "data";
+			break;
+		case 2:
+			q.path = QID(CONV(c->qid), Qsecretin);
+			p = "secretin";
+			break;
+		case 3:
+			q.path = QID(CONV(c->qid), Qsecretout);
+			p = "secretout";
+			break;
+		case 4:
+			q.path = QID(CONV(c->qid), Qencalgs);
+			p = "encalgs";
+			break;
+		case 5:
+			q.path = QID(CONV(c->qid), Qhashalgs);
+			p = "hashalgs";
+			break;
+		}
+		devdir(c, q, p, 0, nm, 0660, dp);
+		return 1;
+	}
+	return -1;
+}
+
+static void
+sslinit(void)
+{
+	if((dstate = malloc(sizeof(Dstate*) * maxdstate)) == 0)
+		panic("sslinit");
+	alglistinit();
+}
+
+static Chan *
+sslattach(char *spec)
+{
+	Chan *c;
+
+	c = devattach('D', spec);
+	c->qid.path = QID(0, Qtopdir);
+	c->qid.vers = 0;
+	c->qid.type = QTDIR;
+	return c;
+}
+
+static Walkqid*
+sslwalk(Chan *c, Chan *nc, char **name, int nname)
+{
+	return devwalk(c, nc, name, nname, 0, 0, sslgen);
+}
+
+static int
+sslstat(Chan *c, uchar *db, int n)
+{
+	return devstat(c, db, n, 0, 0, sslgen);
+}
+
+static Chan*
+sslopen(Chan *c, int omode)
+{
+	Dstate *s, **pp;
+	int perm;
+
+	perm = 0;
+	omode &= 3;
+	switch(omode) {
+	case OREAD:
+		perm = 4;
+		break;
+	case OWRITE:
+		perm = 2;
+		break;
+	case ORDWR:
+		perm = 6;
+		break;
+	}
+
+	switch(TYPE(c->qid)) {
+	default:
+		panic("sslopen");
+	case Qtopdir:
+	case Qconvdir:
+		if(omode != OREAD)
+			error(Eperm);
+		break;
+	case Qclonus:
+		dsclone(c);
+		break;
+	case Qctl:
+	case Qdata:
+	case Qsecretin:
+	case Qsecretout:
+		if(waserror()) {
+			unlock(&dslock);
+			nexterror();
+		}
+		lock(&dslock);
+		pp = &dstate[CONV(c->qid)];
+		s = *pp;
+		if(s == 0)
+			dsnew(c, pp);
+		else {
+			if((perm & (s->perm>>6)) != perm
+			   && (strcmp(up->env->user, s->user) != 0
+			     || (perm & s->perm) != perm))
+				error(Eperm);
+
+			s->ref++;
+		}
+		unlock(&dslock);
+		poperror();
+		break;
+	case Qencalgs:
+	case Qhashalgs:
+		if(omode != OREAD)
+			error(Eperm);
+		break;
+	}
+	c->mode = openmode(omode);
+	c->flag |= COPEN;
+	c->offset = 0;
+	return c;
+}
+
+static int
+sslwstat(Chan *c, uchar *db, int n)
+{
+	Dir *dir;
+	Dstate *s;
+	int m;
+
+	s = dstate[CONV(c->qid)];
+	if(s == 0)
+		error(Ebadusefd);
+	if(strcmp(s->user, up->env->user) != 0)
+		error(Eperm);
+
+	dir = smalloc(sizeof(Dir)+n);
+	m = convM2D(db, n, &dir[0], (char*)&dir[1]);
+	if(m == 0){
+		free(dir);
+		error(Eshortstat);
+	}
+
+	if(!emptystr(dir->uid))
+		kstrdup(&s->user, dir->uid);
+	if(dir->mode != ~0UL)
+		s->perm = dir->mode;
+
+	free(dir);
+	return m;
+}
+
+static void
+sslclose(Chan *c)
+{
+	Dstate *s;
+
+	switch(TYPE(c->qid)) {
+	case Qctl:
+	case Qdata:
+	case Qsecretin:
+	case Qsecretout:
+		if((c->flag & COPEN) == 0)
+			break;
+
+		s = dstate[CONV(c->qid)];
+		if(s == 0)
+			break;
+
+		lock(&dslock);
+		if(--s->ref > 0) {
+			unlock(&dslock);
+			break;
+		}
+		dstate[CONV(c->qid)] = 0;
+		unlock(&dslock);
+
+		sslhangup(s);
+		if(s->c)
+			cclose(s->c);
+		free(s->user);
+		free(s->in.secret);
+		free(s->out.secret);
+		free(s->in.state);
+		free(s->out.state);
+		free(s);
+	}
+}
+
+/*
+ *  make sure we have at least 'n' bytes in list 'l'
+ */
+static void
+ensure(Dstate *s, Block **l, int n)
+{
+	int sofar, i;
+	Block *b, *bl;
+
+	sofar = 0;
+	for(b = *l; b; b = b->next){
+		sofar += BLEN(b);
+		if(sofar >= n)
+			return;
+		l = &b->next;
+	}
+
+	while(sofar < n){
+		bl = devtab[s->c->type]->bread(s->c, Maxdmsg, 0);
+		if(bl == 0)
+			error(Ehungup);
+		*l = bl;
+		i = 0;
+		for(b = bl; b; b = b->next){
+			i += BLEN(b);
+			l = &b->next;
+		}
+		if(i == 0)
+			error(Ehungup);
+
+		sofar += i;
+	}
+}
+
+/*
+ *  copy 'n' bytes from 'l' into 'p' and free
+ *  the bytes in 'l'
+ */
+static void
+consume(Block **l, uchar *p, int n)
+{
+	Block *b;
+	int i;
+
+	for(; *l && n > 0; n -= i){
+		b = *l;
+		i = BLEN(b);
+		if(i > n)
+			i = n;
+		memmove(p, b->rp, i);
+		b->rp += i;
+		p += i;
+		if(BLEN(b) < 0)
+			panic("consume");
+		if(BLEN(b))
+			break;
+		*l = b->next;
+		freeb(b);
+	}
+}
+
+/*
+ *  remove at most n bytes from the queue, if discard is set
+ *  dump the remainder
+ */
+static Block*
+qtake(Block **l, int n, int discard)
+{
+	Block *nb, *b, *first;
+	int i;
+
+	first = *l;
+	for(b = first; b; b = b->next){
+		i = BLEN(b);
+		if(i == n){
+			if(discard){
+				freeblist(b->next);
+				*l = 0;
+			} else
+				*l = b->next;
+			b->next = 0;
+			return first;
+		} else if(i > n){
+			i -= n;
+			if(discard){
+				freeblist(b->next);
+				*l = 0;
+			} else {
+				nb = allocb(i);
+				memmove(nb->wp, b->rp+n, i);
+				nb->wp += i;
+				nb->next = b->next;
+				*l = nb;
+			}
+			b->wp -= i;
+			b->next = 0;
+			if(BLEN(b) < 0)
+				panic("qtake");
+			return first;
+		} else
+			n -= i;
+		if(BLEN(b) < 0)
+			panic("qtake");
+	}
+	*l = 0;
+	return first;
+}
+
+static Block*
+sslbread(Chan *c, long n, ulong offset)
+{
+	volatile struct { Dstate *s; } s;
+	Block *b;
+	uchar count[2];
+	int len, pad;
+
+	USED(offset);
+
+	s.s = dstate[CONV(c->qid)];
+	if(s.s == 0)
+		panic("sslbread");
+	if(s.s->state == Sincomplete)
+		error(Ebadusefd);
+
+	if(waserror()){
+		qunlock(&s.s->in.q);
+		sslhangup(s.s);
+		nexterror();
+	}
+	qlock(&s.s->in.q);
+
+	if(s.s->processed == 0){
+		/* read in the whole message */
+		ensure(s.s, &s.s->unprocessed, 2);
+		consume(&s.s->unprocessed, count, 2);
+		if(count[0] & 0x80){
+			len = ((count[0] & 0x7f)<<8) | count[1];
+			ensure(s.s, &s.s->unprocessed, len);
+			pad = 0;
+		} else {
+			len = ((count[0] & 0x3f)<<8) | count[1];
+			ensure(s.s, &s.s->unprocessed, len+1);
+			consume(&s.s->unprocessed, count, 1);
+			pad = count[0];
+			if(pad > len){
+				print("pad %d buf len %d\n", pad, len);
+				error("bad pad in ssl message");
+			}
+		}
+
+		/* put extra on unprocessed queue */
+		s.s->processed = qtake(&s.s->unprocessed, len, 0);
+
+		if(waserror()){
+			qunlock(&s.s->in.ctlq);
+			nexterror();
+		}
+		qlock(&s.s->in.ctlq);
+		switch(s.s->state){
+		case Sencrypting:
+			s.s->processed = decryptb(s.s, s.s->processed);
+			break;
+		case Sdigesting:
+			s.s->processed = pullupblock(s.s->processed, s.s->diglen);
+			if(s.s->processed == 0)
+				error("ssl message too short");
+			checkdigestb(s.s, s.s->processed);
+			s.s->processed->rp += s.s->diglen;
+			break;
+		case Sdigenc:
+			s.s->processed = decryptb(s.s, s.s->processed);
+			s.s->processed = pullupblock(s.s->processed, s.s->diglen);
+			if(s.s->processed == 0)
+				error("ssl message too short");
+			checkdigestb(s.s, s.s->processed);
+			s.s->processed->rp += s.s->diglen;
+			len -= s.s->diglen;
+			break;
+		}
+		s.s->in.mid++;
+		qunlock(&s.s->in.ctlq);
+		poperror();
+
+		/* remove pad */
+		if(pad)
+			s.s->processed = qtake(&s.s->processed, len - pad, 1);
+	}
+
+	/* return at most what was asked for */
+	b = qtake(&s.s->processed, n, 0);
+
+	qunlock(&s.s->in.q);
+	poperror();
+
+	return b;
+}
+
+static long
+sslread(Chan *c, void *a, long n, vlong offset)
+{
+	volatile struct { Block *b; } b;
+	Block *nb;
+	uchar *va;
+	int i;
+	char buf[128];
+
+	if(c->qid.type & QTDIR)
+		return devdirread(c, a, n, 0, 0, sslgen);
+
+	switch(TYPE(c->qid)) {
+	default:
+		error(Ebadusefd);
+	case Qctl:
+		sprint(buf, "%ld", CONV(c->qid));
+		return readstr(offset, a, n, buf);
+	case Qdata:
+		b.b = sslbread(c, n, offset);
+		break;
+	case Qencalgs:
+		return readstr(offset, a, n, encalgs);
+	case Qhashalgs:
+		return readstr(offset, a, n, hashalgs);
+	}
+
+	n = 0;
+	va = a;
+	for(nb = b.b; nb; nb = nb->next){
+		i = BLEN(nb);
+		memmove(va+n, nb->rp, i);
+		n += i;
+	}
+
+	freeblist(b.b);
+
+	return n;
+}
+
+/*
+ *  this algorithm doesn't have to be great since we're just
+ *  trying to obscure the block fill
+ */
+static void
+randfill(uchar *buf, int len)
+{
+	while(len-- > 0)
+		*buf++ = nrand(256);
+}
+
+/*
+ *  use SSL record format, add in count and digest or encrypt
+ */
+static long
+sslbwrite(Chan *c, Block *b, ulong offset)
+{
+	volatile struct { Dstate *s; } s;
+	volatile struct { Block *b; } bb;
+	Block *nb;
+	int h, n, m, pad, rv;
+	uchar *p;
+
+	bb.b = b;
+
+	s.s = dstate[CONV(c->qid)];
+	if(s.s == 0)
+		panic("sslbwrite");
+	if(s.s->state == Sincomplete){
+		freeb(b);
+		error(Ebadusefd);
+	}
+
+	if(waserror()){
+		qunlock(&s.s->out.q);
+		if(bb.b)
+			freeb(bb.b);
+		sslhangup(s.s);
+		nexterror();
+	}
+	qlock(&s.s->out.q);
+
+	rv = 0;
+	while(bb.b){
+		m = n = BLEN(bb.b);
+		h = s.s->diglen + 2;
+
+		/* trim to maximum block size */
+		pad = 0;
+		if(m > s.s->max){
+			m = s.s->max;
+		} else if(s.s->blocklen != 1){
+			pad = (m + s.s->diglen)%s.s->blocklen;
+			if(pad){
+				if(m > s.s->maxpad){
+					pad = 0;
+					m = s.s->maxpad;
+				} else {
+					pad = s.s->blocklen - pad;
+					h++;
+				}
+			}
+		}
+
+		rv += m;
+		if(m != n){
+			nb = allocb(m + h + pad);
+			memmove(nb->wp + h, bb.b->rp, m);
+			nb->wp += m + h;
+			bb.b->rp += m;
+		} else {
+			/* add header space */
+			nb = padblock(bb.b, h);
+			bb.b = 0;
+		}
+		m += s.s->diglen;
+
+		/* SSLv2 style count */
+		if(pad){
+			nb = padblock(nb, -pad);
+			randfill(nb->wp, pad);
+			nb->wp += pad;
+			m += pad;
+
+			p = nb->rp;
+			p[0] = (m>>8);
+			p[1] = m;
+			p[2] = pad;
+			offset = 3;
+		} else {
+			p = nb->rp;
+			p[0] = (m>>8) | 0x80;
+			p[1] = m;
+			offset = 2;
+		}
+
+		switch(s.s->state){
+		case Sencrypting:
+			nb = encryptb(s.s, nb, offset);
+			break;
+		case Sdigesting:
+			nb = digestb(s.s, nb, offset);
+			break;
+		case Sdigenc:
+			nb = digestb(s.s, nb, offset);
+			nb = encryptb(s.s, nb, offset);
+			break;
+		}
+
+		s.s->out.mid++;
+
+		m = BLEN(nb);
+		devtab[s.s->c->type]->bwrite(s.s->c, nb, s.s->c->offset);
+		s.s->c->offset += m;
+	}
+	qunlock(&s.s->out.q);
+	poperror();
+
+	return rv;
+}
+
+static void
+setsecret(OneWay *w, uchar *secret, int n)
+{
+	free(w->secret);
+	w->secret = mallocz(n, 0);
+	if(w->secret == nil)
+		error(Enomem);
+	memmove(w->secret, secret, n);
+	w->slen = n;
+}
+
+static void
+initIDEAkey(OneWay *w)
+{
+	free(w->state);
+	w->state = malloc(sizeof(IDEAstate));
+	if(w->state == nil)
+		error(Enomem);
+	if(w->slen >= 24)
+		setupIDEAstate(w->state, w->secret, w->secret+16);
+	else if(w->slen >= 16)
+		setupIDEAstate(w->state, w->secret, 0);
+	else
+		error("secret too short");
+}
+
+static void
+initDESkey(OneWay *w)
+{
+	free(w->state);
+	w->state = malloc(sizeof(DESstate));
+	if (!w->state)
+		error(Enomem);
+	if(w->slen >= 16)
+		setupDESstate(w->state, w->secret, w->secret+8);
+	else if(w->slen >= 8)
+		setupDESstate(w->state, w->secret, 0);
+	else
+		error("secret too short");
+}
+
+/*
+ *  40 bit DES is the same as 56 bit DES.  However,
+ *  16 bits of the key are masked to zero.
+ */
+static void
+initDESkey_40(OneWay *w)
+{
+	uchar key[8];
+
+	if(w->slen >= 8) {
+		memmove(key, w->secret, 8);
+		key[0] &= 0x0f;
+		key[2] &= 0x0f;
+		key[4] &= 0x0f;
+		key[6] &= 0x0f;
+	}
+
+	free(w->state);
+	w->state = malloc(sizeof(DESstate));
+	if (!w->state)
+		error(Enomem);
+	if(w->slen >= 16)
+		setupDESstate(w->state, key, w->secret+8);
+	else if(w->slen >= 8)
+		setupDESstate(w->state, key, 0);
+	else
+		error("secret too short");
+}
+
+static void
+initRC4key(OneWay *w)
+{
+	free(w->state);
+	w->state = malloc(sizeof(RC4state));
+	if (!w->state)
+		error(Enomem);
+	setupRC4state(w->state, w->secret, w->slen);
+}
+
+/*
+ *  40 bit RC4 is the same as n-bit RC4.  However,
+ *  we ignore all but the first 40 bits of the key.
+ */
+static void
+initRC4key_40(OneWay *w)
+{
+	int slen = w->slen;
+
+	if(slen > 5)
+		slen = 5;
+
+	free(w->state);
+	w->state = malloc(sizeof(RC4state));
+	if (!w->state)
+		error(Enomem);
+	setupRC4state(w->state, w->secret, slen);
+}
+
+/*
+ *  128 bit RC4 is the same as n-bit RC4.  However,
+ *  we ignore all but the first 128 bits of the key.
+ */
+static void
+initRC4key_128(OneWay *w)
+{
+	int slen = w->slen;
+
+	if(slen > 16)
+		slen = 16;
+
+	free(w->state);
+	w->state = malloc(sizeof(RC4state));
+	if (!w->state)
+		error(Enomem);
+	setupRC4state(w->state, w->secret, slen);
+}
+
+typedef struct Hashalg Hashalg;
+struct Hashalg
+{
+	char	*name;
+	int	diglen;
+	DigestState *(*hf)(uchar*, ulong, uchar*, DigestState*);
+};
+
+Hashalg hashtab[] =
+{
+	{ "md4", MD4dlen, md4, },
+	{ "md5", MD5dlen, md5, },
+	{ "sha1", SHA1dlen, sha1, },
+	{ "sha", SHA1dlen, sha1, },
+	{ 0 }
+};
+
+static int
+parsehashalg(char *p, Dstate *s)
+{
+	Hashalg *ha;
+
+	for(ha = hashtab; ha->name; ha++){
+		if(strcmp(p, ha->name) == 0){
+			s->hf = ha->hf;
+			s->diglen = ha->diglen;
+			s->state &= ~Sclear;
+			s->state |= Sdigesting;
+			return 0;
+		}
+	}
+	return -1;
+}
+
+typedef struct Encalg Encalg;
+struct Encalg
+{
+	char	*name;
+	int	blocklen;
+	int	alg;
+	void	(*keyinit)(OneWay*);
+};
+
+Encalg encrypttab[] =
+{
+	{ "descbc", 8, DESCBC, initDESkey, },           /* DEPRECATED -- use des_56_cbc */
+	{ "desecb", 8, DESECB, initDESkey, },           /* DEPRECATED -- use des_56_ecb */
+	{ "des_56_cbc", 8, DESCBC, initDESkey, },
+	{ "des_56_ecb", 8, DESECB, initDESkey, },
+	{ "des_40_cbc", 8, DESCBC, initDESkey_40, },
+	{ "des_40_ecb", 8, DESECB, initDESkey_40, },
+	{ "rc4", 1, RC4, initRC4key_40, },              /* DEPRECATED -- use rc4_X      */
+	{ "rc4_256", 1, RC4, initRC4key, },
+	{ "rc4_128", 1, RC4, initRC4key_128, },
+	{ "rc4_40", 1, RC4, initRC4key_40, },
+	{ "ideacbc", 8, IDEACBC, initIDEAkey, },
+	{ "ideaecb", 8, IDEAECB, initIDEAkey, },
+	{ 0 }
+};
+
+static int
+parseencryptalg(char *p, Dstate *s)
+{
+	Encalg *ea;
+
+	for(ea = encrypttab; ea->name; ea++){
+		if(strcmp(p, ea->name) == 0){
+			s->encryptalg = ea->alg;
+			s->blocklen = ea->blocklen;
+			(*ea->keyinit)(&s->in);
+			(*ea->keyinit)(&s->out);
+			s->state &= ~Sclear;
+			s->state |= Sencrypting;
+			return 0;
+		}
+	}
+	return -1;
+}
+
+static void
+alglistinit(void)
+{
+	Hashalg *h;
+	Encalg *e;
+	int n;
+
+	n = 1;
+	for(e = encrypttab; e->name != nil; e++)
+		n += strlen(e->name) + 1;
+	encalgs = malloc(n);
+	if(encalgs == nil)
+		panic("sslinit");
+	n = 0;
+	for(e = encrypttab; e->name != nil; e++){
+		strcpy(encalgs+n, e->name);
+		n += strlen(e->name);
+		if(e[1].name == nil)
+			break;
+		encalgs[n++] = ' ';
+	}
+	encalgs[n] = 0;
+
+	n = 1;
+	for(h = hashtab; h->name != nil; h++)
+		n += strlen(h->name) + 1;
+	hashalgs = malloc(n);
+	if(hashalgs == nil)
+		panic("sslinit");
+	n = 0;
+	for(h = hashtab; h->name != nil; h++){
+		strcpy(hashalgs+n, h->name);
+		n += strlen(h->name);
+		if(h[1].name == nil)
+			break;
+		hashalgs[n++] = ' ';
+	}
+	hashalgs[n] = 0;
+}
+
+static long
+sslwrite(Chan *c, void *a, long n, vlong offset)
+{
+	volatile struct { Dstate *s; } s;
+	volatile struct { Block *b; } b;
+	int m, t;
+	char *p, *np, *e, buf[32];
+	uchar *x;
+
+	s.s = dstate[CONV(c->qid)];
+	if(s.s == 0)
+		panic("sslwrite");
+
+	t = TYPE(c->qid);
+	if(t == Qdata){
+		if(s.s->state == Sincomplete)
+			error(Ebadusefd);
+
+		p = a;
+		e = p + n;
+		do {
+			m = e - p;
+			if(m > s.s->max)
+				m = s.s->max;
+
+			b.b = allocb(m);
+			memmove(b.b->wp, p, m);
+			b.b->wp += m;
+
+			sslbwrite(c, b.b, offset);
+
+			p += m;
+		} while(p < e);
+		return n;
+	}
+
+	/* mutex with operations using what we're about to change */
+	if(waserror()){
+		qunlock(&s.s->in.ctlq);
+		qunlock(&s.s->out.q);
+		nexterror();
+	}
+	qlock(&s.s->in.ctlq);
+	qlock(&s.s->out.q);
+
+	switch(t){
+	default:
+		panic("sslwrite");
+	case Qsecretin:
+		setsecret(&s.s->in, a, n);
+		goto out;
+	case Qsecretout:
+		setsecret(&s.s->out, a, n);
+		goto out;
+	case Qctl:
+		break;
+	}
+
+	if(n >= sizeof(buf))
+		error(Ebadarg);
+	strncpy(buf, a, n);
+	buf[n] = 0;
+	p = strchr(buf, '\n');
+	if(p)
+		*p = 0;
+	p = strchr(buf, ' ');
+	if(p)
+		*p++ = 0;
+
+	if(strcmp(buf, "fd") == 0){
+		s.s->c = buftochan(p);
+
+		/* default is clear (msg delimiters only) */
+		s.s->state = Sclear;
+		s.s->blocklen = 1;
+		s.s->diglen = 0;
+		s.s->maxpad = s.s->max = (1<<15) - s.s->diglen - 1;
+		s.s->in.mid = 0;
+		s.s->out.mid = 0;
+	} else if(strcmp(buf, "alg") == 0 && p != 0){
+		s.s->blocklen = 1;
+		s.s->diglen = 0;
+
+		if(s.s->c == 0)
+			error("must set fd before algorithm");
+
+		if(strcmp(p, "clear") == 0){
+			s.s->state = Sclear;
+			s.s->maxpad = s.s->max = (1<<15) - s.s->diglen - 1;
+			goto out;
+		}
+
+		if(s.s->in.secret && s.s->out.secret == 0)
+			setsecret(&s.s->out, s.s->in.secret, s.s->in.slen);
+		if(s.s->out.secret && s.s->in.secret == 0)
+			setsecret(&s.s->in, s.s->out.secret, s.s->out.slen);
+		if(s.s->in.secret == 0 || s.s->out.secret == 0)
+			error("algorithm but no secret");
+
+		s.s->hf = 0;
+		s.s->encryptalg = Noencryption;
+		s.s->blocklen = 1;
+
+		for(;;){
+			np = strchr(p, ' ');
+			if(np)
+				*np++ = 0;
+			else{
+				np = strchr(p, '/');
+				if(np)
+					*np++ = 0;
+			}
+			if(parsehashalg(p, s.s) < 0)
+			if(parseencryptalg(p, s.s) < 0)
+				error(Ebadarg);
+
+			if(np == 0)
+				break;
+			p = np;
+		}
+
+		if(s.s->hf == 0 && s.s->encryptalg == Noencryption)
+			error(Ebadarg);
+
+		if(s.s->blocklen != 1){
+			/* make multiple of blocklen */
+			s.s->max = (1<<15) - s.s->diglen - 1;
+			s.s->max -= s.s->max % s.s->blocklen;
+			s.s->maxpad = (1<<14) - s.s->diglen - 1;
+			s.s->maxpad -= s.s->maxpad % s.s->blocklen;
+		} else
+			s.s->maxpad = s.s->max = (1<<15) - s.s->diglen - 1;
+	} else if(strcmp(buf, "secretin") == 0 && p != 0) {
+		m = (strlen(p)*3)/2;
+		x = smalloc(m);
+		if(waserror()){
+			free(x);
+			nexterror();
+		}
+		t = dec64(x, m, p, strlen(p));
+		setsecret(&s.s->in, x, t);
+		poperror();
+		free(x);
+	} else if(strcmp(buf, "secretout") == 0 && p != 0) {
+		m = (strlen(p)*3)/2;
+		x = smalloc(m);
+		if(waserror()){
+			free(x);
+			nexterror();
+		}
+		t = dec64(x, m, p, strlen(p));
+		setsecret(&s.s->out, x, t);
+		poperror();
+		free(x);
+	} else
+		error(Ebadarg);
+
+out:
+	qunlock(&s.s->in.ctlq);
+	qunlock(&s.s->out.q);
+	poperror();
+	return n;
+}
+
+Dev ssldevtab = {
+	'D',
+	"ssl",
+
+	devreset,
+	sslinit,
+	devshutdown,
+	sslattach,
+	sslwalk,
+	sslstat,
+	sslopen,
+	devcreate,
+	sslclose,
+	sslread,
+	sslbread,
+	sslwrite,
+	sslbwrite,
+	devremove,
+	sslwstat,
+};
+
+static Block*
+encryptb(Dstate *s, Block *b, int offset)
+{
+	uchar *p, *ep, *p2, *ip, *eip;
+	DESstate *ds;
+	IDEAstate *is;
+
+	switch(s->encryptalg){
+	case DESECB:
+		ds = s->out.state;
+		ep = b->rp + BLEN(b);
+		for(p = b->rp + offset; p < ep; p += 8)
+			block_cipher(ds->expanded, p, 0);
+		break;
+	case DESCBC:
+		ds = s->out.state;
+		ep = b->rp + BLEN(b);
+		for(p = b->rp + offset; p < ep; p += 8){
+			p2 = p;
+			ip = ds->ivec;
+			for(eip = ip+8; ip < eip; )
+				*p2++ ^= *ip++;
+			block_cipher(ds->expanded, p, 0);
+			memmove(ds->ivec, p, 8);
+		}
+		break;
+	case IDEAECB:
+		is = s->out.state;
+		ep = b->rp + BLEN(b);
+		for(p = b->rp + offset; p < ep; p += 8)
+			idea_cipher(is->edkey, p, 0);
+		break;
+	case IDEACBC:
+		is = s->out.state;
+		ep = b->rp + BLEN(b);
+		for(p = b->rp + offset; p < ep; p += 8){
+			p2 = p;
+			ip = is->ivec;
+			for(eip = ip+8; ip < eip; )
+				*p2++ ^= *ip++;
+			idea_cipher(is->edkey, p, 0);
+			memmove(is->ivec, p, 8);
+		}
+		break;
+	case RC4:
+		rc4(s->out.state, b->rp + offset, BLEN(b) - offset);
+		break;
+	}
+	return b;
+}
+
+static Block*
+decryptb(Dstate *s, Block *inb)
+{
+	Block *b, **l;
+	uchar *p, *ep, *tp, *ip, *eip;
+	DESstate *ds;
+	IDEAstate *is;
+	uchar tmp[8];
+	int i;
+
+	l = &inb;
+	for(b = inb; b; b = b->next){
+		/* make sure we have a multiple of s->blocklen */
+		if(s->blocklen > 1){
+			i = BLEN(b);
+			if(i % s->blocklen){
+				*l = b = pullupblock(b, i + s->blocklen - (i%s->blocklen));
+				if(b == 0)
+					error("ssl encrypted message too short");
+			}
+		}
+		l = &b->next;
+
+		/* decrypt */
+		switch(s->encryptalg){
+		case DESECB:
+			ds = s->in.state;
+			ep = b->rp + BLEN(b);
+			for(p = b->rp; p < ep; p += 8)
+				block_cipher(ds->expanded, p, 1);
+			break;
+		case DESCBC:
+			ds = s->in.state;
+			ep = b->rp + BLEN(b);
+			for(p = b->rp; p < ep;){
+				memmove(tmp, p, 8);
+				block_cipher(ds->expanded, p, 1);
+				tp = tmp;
+				ip = ds->ivec;
+				for(eip = ip+8; ip < eip; ){
+					*p++ ^= *ip;
+					*ip++ = *tp++;
+				}
+			}
+			break;
+		case IDEAECB:
+			is = s->in.state;
+			ep = b->rp + BLEN(b);
+			for(p = b->rp; p < ep; p += 8)
+				idea_cipher(is->edkey, p, 1);
+			break;
+		case IDEACBC:
+			is = s->in.state;
+			ep = b->rp + BLEN(b);
+			for(p = b->rp; p < ep;){
+				memmove(tmp, p, 8);
+				idea_cipher(is->edkey, p, 1);
+				tp = tmp;
+				ip = is->ivec;
+				for(eip = ip+8; ip < eip; ){
+					*p++ ^= *ip;
+					*ip++ = *tp++;
+				}
+			}
+			break;
+		case RC4:
+			rc4(s->in.state, b->rp, BLEN(b));
+			break;
+		}
+	}
+	return inb;
+}
+
+static Block*
+digestb(Dstate *s, Block *b, int offset)
+{
+	uchar *p;
+	DigestState ss;
+	uchar msgid[4];
+	ulong n, h;
+	OneWay *w;
+
+	w = &s->out;
+
+	memset(&ss, 0, sizeof(ss));
+	h = s->diglen + offset;
+	n = BLEN(b) - h;
+
+	/* hash secret + message */
+	(*s->hf)(w->secret, w->slen, 0, &ss);
+	(*s->hf)(b->rp + h, n, 0, &ss);
+
+	/* hash message id */
+	p = msgid;
+	n = w->mid;
+	*p++ = n>>24;
+	*p++ = n>>16;
+	*p++ = n>>8;
+	*p = n;
+	(*s->hf)(msgid, 4, b->rp + offset, &ss);
+
+	return b;
+}
+
+static void
+checkdigestb(Dstate *s, Block *inb)
+{
+	uchar *p;
+	DigestState ss;
+	uchar msgid[4];
+	int n, h;
+	OneWay *w;
+	uchar digest[128];
+	Block *b;
+
+	w = &s->in;
+
+	memset(&ss, 0, sizeof(ss));
+
+	/* hash secret */
+	(*s->hf)(w->secret, w->slen, 0, &ss);
+
+	/* hash message */
+	h = s->diglen;
+	for(b = inb; b; b = b->next){
+		n = BLEN(b) - h;
+		if(n < 0)
+			panic("checkdigestb");
+		(*s->hf)(b->rp + h, n, 0, &ss);
+		h = 0;
+	}
+
+	/* hash message id */
+	p = msgid;
+	n = w->mid;
+	*p++ = n>>24;
+	*p++ = n>>16;
+	*p++ = n>>8;
+	*p = n;
+	(*s->hf)(msgid, 4, digest, &ss);
+
+	/* requires pullupblock */
+	if(memcmp(digest, inb->rp, s->diglen) != 0)
+		error("bad digest");
+}
+
+/* get channel associated with an fd */
+static Chan*
+buftochan(char *p)
+{
+	Chan *c;
+	int fd;
+
+	if(p == 0)
+		error(Ebadarg);
+	fd = strtoul(p, 0, 0);
+	if(fd < 0)
+		error(Ebadarg);
+	c = fdtochan(up->env->fgrp, fd, -1, 0, 1);	/* error check and inc ref */
+	return c;
+}
+
+/* hang up a digest connection */
+static void
+sslhangup(Dstate *s)
+{
+	qlock(&s->in.q);
+	freeblist(s->processed);
+	s->processed = 0;
+	freeblist(s->unprocessed);
+	s->unprocessed = 0;
+	s->state = Sincomplete;
+	qunlock(&s->in.q);
+}
+
+extern void rbcheck(char*);
+
+static void
+dsclone(Chan *ch)
+{
+	Dstate **pp, **ep, **np;
+	int newmax;
+
+	lock(&dslock);
+	if(waserror()) {
+		unlock(&dslock);
+		nexterror();
+	}
+	ep = &dstate[maxdstate];
+	for(pp = dstate; pp < ep; pp++) {
+		if(*pp == 0) {
+			dsnew(ch, pp);
+			break;
+		}
+	}
+	if(pp >= ep) {
+		if(maxdstate >= Maxdstate)
+			error(Enodev);
+		newmax = 2 * maxdstate;
+		if(newmax > Maxdstate)
+			newmax = Maxdstate;
+		np = realloc(dstate, sizeof(Dstate*) * newmax);
+		if(np == 0)
+			error(Enomem);
+		dstate = np;
+		pp = &dstate[maxdstate];
+		memset(pp, 0, sizeof(Dstate*)*(newmax - maxdstate));
+		maxdstate = newmax;
+		dsnew(ch, pp);
+	}
+	poperror();
+	unlock(&dslock);
+}
+
+static void
+dsnew(Chan *ch, Dstate **pp)
+{
+	Dstate *s;
+	int t;
+
+	*pp = s = mallocz(sizeof(*s), 1);
+	if(s == nil)
+		error(Enomem);
+	if(pp - dstate >= dshiwat)
+		dshiwat++;
+	s->state = Sincomplete;
+	s->ref = 1;
+	kstrdup(&s->user, up->env->user);
+	s->perm = 0660;
+	t = TYPE(ch->qid);
+	if(t == Qclonus)
+		t = Qctl;
+	ch->qid.path = QID(pp - dstate, t);
+	ch->qid.vers = 0;
+	ch->qid.type = QTFILE;
+}
--- /dev/null
+++ b/os/port/devtest.c
@@ -1,0 +1,125 @@
+/*
+ *  Test device
+ */
+#include	"u.h"
+#include	"../port/lib.h"
+#include	"mem.h"
+#include	"dat.h"
+#include	"fns.h"
+#include	"io.h"
+#include	"../port/error.h"
+#include	"libcrypt.h"
+
+#include <kernel.h>
+
+#define	DEBUG	0
+
+extern void _startup(void);
+
+enum{
+	Qdir,
+	Qkt5sum,
+	Qkerndate,
+};
+
+static
+Dirtab testtab[]={
+	".",			{ Qdir, 0, QTDIR},	0,	0555,
+	"kt5sum",		{ Qkt5sum },		0,	0444,
+	"kerndate",	{ Qkerndate },		0,	0444,
+};
+
+
+void ktsum(char *digest)
+{
+	uchar rawdigest[MD5dlen+1];
+	int i;
+	void *start =  _startup;
+	ulong size = (ulong)etext - (ulong) start;
+	md5(start, size, rawdigest, nil);
+	for (i=0; i<MD5dlen; i++)
+		sprint(&digest[2*i], "%2.2x", rawdigest[i]);
+	digest[MD5dlen*2] = 0;
+	strcat(digest, "\n");
+}
+
+static Chan*
+testattach(char *spec)
+{
+	return devattach('Z', spec);
+}
+
+static Walkqid*
+testwalk(Chan *c, Chan *nc, char **name, int nname)
+{
+	return devwalk(c, nc, name, nname, testtab, nelem(testtab), devgen);
+}
+
+static int
+teststat(Chan *c, uchar *db, int n)
+{
+	return devstat(c, db, n, testtab, nelem(testtab), devgen);
+}
+
+static Chan*
+testopen(Chan *c, int omode)
+{
+	return devopen(c, omode, testtab, nelem(testtab), devgen);
+}
+
+static void
+testclose(Chan *)
+{
+}
+
+extern ulong kerndate;
+
+static long
+testread(Chan* c, void* a, long n, vlong offset)
+{
+	char digest[MD5dlen*2+1];
+	switch ((ulong)c->qid.path) {
+	case Qdir:
+		return devdirread(c, a, n, testtab, nelem(testtab), devgen);
+	case Qkt5sum:
+		ktsum(digest);
+		return readstr(offset, a, n, digest);
+	case Qkerndate:
+		sprint(digest, "%ld\n", kerndate);
+		return readstr(offset, a, n, digest);
+	default:
+		n = 0;
+		break;
+	}
+	return n;
+}
+	
+
+static long
+testwrite(Chan*, void*, long, vlong)
+{
+	error(Ebadusefd);
+	return 0;
+}
+
+Dev testdevtab = {
+	'Z',
+	"test",
+
+	devreset,
+	devinit,
+	devshutdown,
+	testattach,
+	testwalk,
+	teststat,
+	testopen,
+	devcreate,
+	testclose,
+	testread,
+	devbread,
+	testwrite,
+	devbwrite,
+	devremove,
+	devwstat,
+};
+
--- /dev/null
+++ b/os/port/devtinyfs.c
@@ -1,0 +1,915 @@
+/*
+ *  a pity the code isn't also tiny...
+ */
+#include "u.h"
+#include "../port/lib.h"
+#include "../port/error.h"
+#include "mem.h"
+#include "dat.h"
+#include "fns.h"
+
+enum{
+	Qdir,
+	Qmedium,
+
+	Maxfs=		10,	/* max file systems */
+
+	Blen=		48,	/* block length */
+	Nlen=		28,	/* name length */
+	Dlen=		Blen - 4,
+
+	Tagdir=		'd',
+	Tagdata=	'D',
+	Tagend=		'e',
+	Tagfree=	'f',
+
+	Notapin=		0xffff,
+	Notabno=		0xffff,
+
+	Fcreating=	1,
+	Frmonclose=	2
+};
+
+/* representation of a Tdir on medium */
+typedef struct Mdir Mdir;
+struct Mdir {
+	uchar	type;
+	uchar	bno[2];
+	uchar	pin[2];
+	char	name[Nlen];
+	char	pad[Blen - Nlen - 6];
+	uchar	sum;
+};
+
+/* representation of a Tdata/Tend on medium */
+typedef struct Mdata Mdata;
+struct Mdata {
+	uchar	type;
+	uchar	bno[2];
+	uchar	data[Dlen];
+	uchar	sum;
+};
+
+typedef struct Tfile Tfile;
+struct Tfile {
+	int	r;
+	char	name[Nlen];
+	ushort	bno;
+	ushort	dbno;
+	ushort	pin;
+	uchar	flag;
+	ulong	length;
+
+	/* hint to avoid egregious reading */
+	ushort	fbno;
+	ulong	finger;
+};
+
+typedef struct Tfs Tfs;
+struct Tfs {
+	QLock	ql;
+	int	r;
+	Chan	*c;
+	uchar	*map;
+	int	nblocks;
+	Tfile	*f;
+	int	nf;
+	int	fsize;
+};
+
+static struct {
+	Tfs	fs[Maxfs];
+} tinyfs;
+
+#define GETS(x) ((x)[0]|((x)[1]<<8))
+#define PUTS(x, v) {(x)[0] = (v);(x)[1] = ((v)>>8);}
+
+#define GETL(x) (GETS(x)|(GETS(x+2)<<16))
+#define PUTL(x, v) {PUTS(x, v);PUTS(x+2, (v)>>16)};
+
+static uchar
+checksum(uchar *p)
+{
+	uchar *e;
+	uchar s;
+
+	s = 0;
+	for(e = p + Blen; p < e; p++)
+		s += *p;
+	return s;
+}
+
+static void
+mapclr(Tfs *fs, ulong bno)
+{
+	fs->map[bno>>3] &= ~(1<<(bno&7));
+}
+
+static void
+mapset(Tfs *fs, ulong bno)
+{
+	fs->map[bno>>3] |= 1<<(bno&7);
+}
+
+static int
+isalloced(Tfs *fs, ulong bno)
+{
+	return fs->map[bno>>3] & (1<<(bno&7));
+}
+
+static int
+mapalloc(Tfs *fs)
+{
+	int i, j, lim;
+	uchar x;
+
+	lim = (fs->nblocks + 8 - 1)/8;
+	for(i = 0; i < lim; i++){
+		x = fs->map[i];
+		if(x == 0xff)
+			continue;
+		for(j = 0; j < 8; j++)
+			if((x & (1<<j)) == 0){
+				fs->map[i] = x|(1<<j);
+				return i*8 + j;
+			}
+	}
+
+	return Notabno;
+}
+
+static Mdir*
+validdir(Tfs *fs, uchar *p)
+{
+	Mdir *md;
+	ulong x;
+
+	if(checksum(p) != 0)
+		return 0;
+	if(p[0] != Tagdir)
+		return 0;
+	md = (Mdir*)p;
+	x = GETS(md->bno);
+	if(x >= fs->nblocks)
+		return 0;
+	return md;
+}
+
+static Mdata*
+validdata(Tfs *fs, uchar *p, int *lenp)
+{
+	Mdata *md;
+	ulong x;
+
+	if(checksum(p) != 0)
+		return 0;
+	md = (Mdata*)p;
+	switch(md->type){
+	case Tagdata:
+		x = GETS(md->bno);
+		if(x >= fs->nblocks)
+			return 0;
+		if(lenp)
+			*lenp = Dlen;
+		break;
+	case Tagend:
+		x = GETS(md->bno);
+		if(x > Dlen)
+			return 0;
+		if(lenp)
+			*lenp = x;
+		break;
+	default:
+		return 0;
+	}
+	return md;
+}
+
+static Mdata*
+readdata(Tfs *fs, ulong bno, uchar *buf, int *lenp)
+{
+	if(bno >= fs->nblocks)
+		return 0;
+	if(devtab[fs->c->type]->read(fs->c, buf, Blen, Blen*bno) != Blen)
+		error(Eio);
+	return validdata(fs, buf, lenp);
+}
+
+static void
+writedata(Tfs *fs, ulong bno, ulong next, uchar *buf, int len, int last)
+{
+	Mdata md;
+
+	if(bno >= fs->nblocks)
+		error(Eio);
+	if(len > Dlen)
+		len = Dlen;
+	if(len < 0)
+		error(Eio);
+	memset(&md, 0, sizeof(md));
+	if(last){
+		md.type = Tagend;
+		PUTS(md.bno, len);
+	} else {
+		md.type = Tagdata;
+		PUTS(md.bno, next);
+	}
+	memmove(md.data, buf, len);
+	md.sum = 0 - checksum((uchar*)&md);
+	
+	if(devtab[fs->c->type]->write(fs->c, &md, Blen, Blen*bno) != Blen)
+		error(Eio);
+}
+
+static void
+writedir(Tfs *fs, Tfile *f)
+{
+	Mdir *md;
+	uchar buf[Blen];
+
+	if(f->bno == Notabno)
+		return;
+
+	md = (Mdir*)buf;
+	memset(buf, 0, Blen);
+	md->type = Tagdir;
+	strncpy(md->name, f->name, sizeof(md->name)-1);
+	PUTS(md->bno, f->dbno);
+	PUTS(md->pin, f->pin);
+	md->sum = 0 - checksum(buf);
+	
+	if(devtab[fs->c->type]->write(fs->c, buf, Blen, Blen*f->bno) != Blen)
+		error(Eio);
+}
+
+static void
+freeblocks(Tfs *fs, ulong bno, ulong bend)
+{
+	uchar buf[Blen];
+	Mdata *md;
+
+	if(waserror())
+		return;
+
+	while(bno != bend && bno != Notabno){
+		mapclr(fs, bno);
+		if(devtab[fs->c->type]->read(fs->c, buf, Blen, Blen*bno) != Blen)
+			break;
+		md = validdata(fs, buf, 0);
+		if(md == 0)
+			break;
+		if(md->type == Tagend)
+			break;
+		bno = GETS(md->bno);
+	}
+
+	poperror();
+}
+
+static void
+freefile(Tfs *fs, Tfile *f, ulong bend)
+{
+	uchar buf[Blen];
+
+	/* remove blocks from map */
+	freeblocks(fs, f->dbno, bend);
+
+	/* change file type to free on medium */
+	if(f->bno != Notabno){
+		memset(buf, 0x55, Blen);
+		devtab[fs->c->type]->write(fs->c, buf, Blen, Blen*f->bno);
+		mapclr(fs, f->bno);
+	}
+
+	/* forget we ever knew about it */
+	memset(f, 0, sizeof(*f));
+}
+
+static void
+expand(Tfs *fs)
+{
+	Tfile *f;
+
+	fs->fsize += 8;
+	f = malloc(fs->fsize*sizeof(*f));
+	if(f == nil)
+		error(Enomem);
+
+	if(fs->f){
+		memmove(f, fs->f, fs->nf*sizeof(*f));
+		free(fs->f);
+	}
+	fs->f = f;
+}
+
+static Tfile*
+newfile(Tfs *fs, char *name)
+{
+	int i;
+	volatile struct {
+		Tfile *f;
+		Tfs *fs;
+	} rock;
+
+	/* find free entry in file table */
+	rock.f = 0;
+	rock.fs = fs;
+	for(;;) {
+		for(i = 0; i < rock.fs->fsize; i++){
+			rock.f = &rock.fs->f[i];
+			if(rock.f->name[0] == 0){
+				strncpy(rock.f->name, name, sizeof(rock.f->name)-1);
+				break;
+			}
+		}
+
+		if(i < rock.fs->fsize){
+			if(i >= rock.fs->nf)
+				rock.fs->nf = i+1;
+			break;
+		}
+
+		expand(rock.fs);
+	}
+
+	rock.f->flag = Fcreating;
+	rock.f->dbno = Notabno;
+	rock.f->bno = mapalloc(rock.fs);
+	rock.f->fbno = Notabno;
+	rock.f->r = 1;
+	rock.f->pin = Notapin;
+
+	/* write directory block */
+	if(waserror()){
+		freefile(rock.fs, rock.f, Notabno);
+		nexterror();
+	}
+	if(rock.f->bno == Notabno)
+		error("out of space");
+	writedir(rock.fs, rock.f);
+	poperror();
+	
+	return rock.f;
+}
+
+/*
+ *  Read the whole medium and build a file table and used
+ *  block bitmap.  Inconsistent files are purged.  The medium
+ *  had better be small or this could take a while.
+ */
+static void
+tfsinit(Tfs *fs)
+{
+	uchar dbuf[STATFIXLEN+4*KNAMELEN];
+	Dir d;
+	uchar buf[Blen];
+	ulong x, bno;
+	int n, done;
+	Tfile *f;
+	Mdir *mdir;
+	Mdata *mdata;
+
+	n = devtab[fs->c->type]->stat(fs->c, dbuf, sizeof(dbuf));
+	if(n == 0)
+		error(Eshortstat);
+	n = convM2D(dbuf, n, &d, nil);
+	if(n == 0)
+		error(Eshortstat);
+	fs->nblocks = d.length/Blen;
+	if(fs->nblocks < 3)
+		error("tinyfs medium too small");
+
+	/* bitmap for block usage */
+	x = (fs->nblocks + 8 - 1)/8;
+	fs->map = malloc(x);
+	if(fs->map == nil)
+		error(Enomem);
+	memset(fs->map, 0x0, x);
+	for(bno = fs->nblocks; bno < x*8; bno++)
+		mapset(fs, bno);
+
+	/* find files */
+	for(bno = 0; bno < fs->nblocks; bno++){
+		n = devtab[fs->c->type]->read(fs->c, buf, Blen, Blen*bno);
+		if(n != Blen)
+			break;
+
+		mdir = validdir(fs, buf);
+		if(mdir == 0)
+			continue;
+
+		if(fs->nf >= fs->fsize)
+			expand(fs);
+
+		f = &fs->f[fs->nf++];
+
+		x = GETS(mdir->bno);
+		mapset(fs, bno);
+		strncpy(f->name, mdir->name, sizeof(f->name));
+		f->pin = GETS(mdir->pin);
+		f->bno = bno;
+		f->dbno = x;
+		f->fbno = Notabno;
+	}
+
+	/* follow files */
+	for(f = fs->f; f < &(fs->f[fs->nf]); f++){
+		bno = f->dbno;
+		for(done = 0; !done;) {
+			if(isalloced(fs, bno)){
+				freefile(fs, f, bno);
+				break;
+			}
+			n = devtab[fs->c->type]->read(fs->c, buf, Blen, Blen*bno);
+			if(n != Blen){
+				freefile(fs, f, bno);
+				break;
+			}
+			mdata = validdata(fs, buf, 0);
+			if(mdata == 0){
+				freefile(fs, f, bno);
+				break;
+			}
+			mapset(fs, bno);
+			switch(mdata->type){
+			case Tagdata:
+				bno = GETS(mdata->bno);
+				f->length += Dlen;
+				break;
+			case Tagend:
+				f->length += GETS(mdata->bno);
+				done = 1;
+				break;
+			}
+			if(done)
+				f->flag &= ~Fcreating;
+		}
+	}
+}
+
+/*
+ *  single directory
+ */
+static int
+tinyfsgen(Chan *c, char*, Dirtab *tab, int ntab, int i, Dir *dp)
+{
+	Tfs *fs;
+	Tfile *f;
+	Qid qid;
+
+	USED(ntab);
+	USED(tab);
+
+	fs = &tinyfs.fs[c->dev];
+	if(i >= fs->nf)
+		return -1;
+	if(i == DEVDOTDOT){
+		mkqid(&qid, Qdir, 0, QTDIR);
+		devdir(c, qid, ".", 0, eve, 0555, dp);
+		return 1;
+	}
+	f = &fs->f[i];
+	if(f->name[0] == 0)
+		return 0;
+	mkqid(&qid, i, 0, QTFILE);
+	devdir(c, qid, f->name, f->length, eve, 0664, dp);
+	return 1;
+}
+
+static void
+tinyfsinit(void)
+{
+	if(Nlen > KNAMELEN)
+		panic("tinyfsinit");
+}
+
+/*
+ *  specifier is the name of a device in /dev
+ */
+static Chan*
+tinyfsattach(char *spec)
+{
+	Tfs *fs;
+	Chan *c;
+	volatile struct { Chan *cc; } rock;
+	int i;
+	char buf[KNAMELEN*3];
+
+	if(*spec == 0)
+		error("bad specifier");
+
+	snprint(buf, sizeof(buf), "/dev/%s", spec);
+	rock.cc = namec(buf, Aopen, ORDWR, 0);
+	if(waserror()){
+		cclose(rock.cc);
+		nexterror();
+	}
+
+	fs = 0;
+	for(i = 0; i < Maxfs; i++){
+		fs = &tinyfs.fs[i];
+		qlock(&fs->ql);
+		if(fs->r && eqchan(rock.cc, fs->c, 1))
+			break;
+		qunlock(&fs->ql);
+	}
+	if(i < Maxfs){
+		fs->r++;
+		qunlock(&fs->ql);
+		cclose(rock.cc);
+	} else {
+		for(fs = tinyfs.fs; fs < &tinyfs.fs[Maxfs]; fs++){
+			qlock(&fs->ql);
+			if(fs->r == 0)
+				break;
+			qunlock(&fs->ql);
+		}
+		if(fs == &tinyfs.fs[Maxfs])
+			error("too many tinyfs's");
+		fs->c = rock.cc;
+		fs->r = 1;
+		fs->f = 0;
+		fs->nf = 0;
+		fs->fsize = 0;
+		tfsinit(fs);
+		qunlock(&fs->ql);
+	}
+	poperror();
+
+	c = devattach('F', spec);
+	c->dev = fs - tinyfs.fs;
+	c->qid.path = Qdir;
+	c->qid.type = QTDIR;
+	c->qid.vers = 0;
+
+	return c;
+}
+
+static Walkqid*
+tinyfswalk(Chan *c, Chan *nc, char **name, int nname)
+{
+	Tfs *fs;
+	Walkqid *w;
+
+	fs = &tinyfs.fs[c->dev];
+
+	qlock(&fs->ql);
+	if(waserror()){
+		qunlock(&fs->ql);
+		nexterror();
+	}
+	w = devwalk(c, nc, name, nname, 0, 0, tinyfsgen);
+	if(w != nil && w->clone!=nil && w->clone->qid.type != QTDIR){
+		fs = &tinyfs.fs[w->clone->dev];
+		fs->r++;
+		fs->f[(ulong)w->clone->qid.path].r++;
+	}
+	poperror();
+	qunlock(&fs->ql);
+	return w;
+}
+
+static int
+tinyfsstat(Chan *c, uchar *db, int n)
+{
+	return devstat(c, db, n, 0, 0, tinyfsgen);
+}
+
+static Chan*
+tinyfsopen(Chan *c, int omode)
+{
+	Tfile *f;
+	volatile struct { Tfs *fs; } rock;
+
+	rock.fs = &tinyfs.fs[c->dev];
+
+	if(c->qid.type & QTDIR){
+		if(omode != OREAD)
+			error(Eperm);
+	} else {
+		qlock(&rock.fs->ql);
+		if(waserror()){
+			qunlock(&rock.fs->ql);
+			nexterror();
+		}
+		switch(omode){
+		case OTRUNC|ORDWR:
+		case OTRUNC|OWRITE:
+			f = newfile(rock.fs, rock.fs->f[c->qid.path].name);
+			rock.fs->f[c->qid.path].r--;
+			c->qid.path = f - rock.fs->f;
+			break;
+		case OREAD:
+			break;
+		default:
+			error(Eperm);
+		}
+		qunlock(&rock.fs->ql);
+		poperror();
+	}
+
+	return devopen(c, omode, 0, 0, tinyfsgen);
+}
+
+static void
+tinyfscreate(Chan *c, char *name, int omode, ulong perm)
+{
+	volatile struct { Tfs *fs; } rock;
+	Tfile *f;
+
+	USED(perm);
+
+	rock.fs = &tinyfs.fs[c->dev];
+
+	qlock(&rock.fs->ql);
+	if(waserror()){
+		qunlock(&rock.fs->ql);
+		nexterror();
+	}
+	f = newfile(rock.fs, name);
+	qunlock(&rock.fs->ql);
+	poperror();
+
+	c->qid.path = f - rock.fs->f;
+	c->qid.vers = 0;
+	c->mode = openmode(omode);
+}
+
+static void
+tinyfsremove(Chan *c)
+{
+	Tfs *fs;
+	Tfile *f;
+
+	if(c->qid.type & QTDIR)
+		error(Eperm);
+	fs = &tinyfs.fs[c->dev];
+	f = &fs->f[c->qid.path];
+	qlock(&fs->ql);
+	freefile(fs, f, Notabno);
+	qunlock(&fs->ql);
+}
+
+static void
+tinyfsclose(Chan *c)
+{
+	volatile struct { Tfs *fs; } rock;
+	Tfile *f, *nf;
+	int i;
+
+	rock.fs = &tinyfs.fs[c->dev];
+
+	qlock(&rock.fs->ql);
+
+	/* dereference file and remove old versions */
+	if(!waserror()){
+		if(c->qid.path != Qdir){
+			f = &rock.fs->f[c->qid.path];
+			f->r--;
+			if(f->r == 0){
+				if(f->flag & Frmonclose)
+					freefile(rock.fs, f, Notabno);
+				else if(f->flag & Fcreating){
+					/* remove all other files with this name */
+					for(i = 0; i < rock.fs->fsize; i++){
+						nf = &rock.fs->f[i];
+						if(f == nf)
+							continue;
+						if(strcmp(nf->name, f->name) == 0){
+							if(nf->r)
+								nf->flag |= Frmonclose;
+							else
+								freefile(rock.fs, nf, Notabno);
+						}
+					}
+					f->flag &= ~Fcreating;
+				}
+			}
+		}
+		poperror();
+	}
+
+	/* dereference rock.fs and remove on zero refs */
+	rock.fs->r--;
+	if(rock.fs->r == 0){
+		if(rock.fs->f)
+			free(rock.fs->f);
+		rock.fs->f = 0;
+		rock.fs->nf = 0;
+		rock.fs->fsize = 0;
+		if(rock.fs->map)
+			free(rock.fs->map);
+		rock.fs->map = 0;
+		cclose(rock.fs->c);
+		rock.fs->c = 0;
+	}
+	qunlock(&rock.fs->ql);
+}
+
+static long
+tinyfsread(Chan *c, void *a, long n, vlong offset)
+{
+	volatile struct { Tfs *fs; } rock;
+	Tfile *f;
+	int sofar, i, off;
+	ulong bno;
+	Mdata *md;
+	uchar buf[Blen];
+	uchar *p;
+
+	if(c->qid.type & QTDIR)
+		return devdirread(c, a, n, 0, 0, tinyfsgen);
+
+	p = a;
+	rock.fs = &tinyfs.fs[c->dev];
+	f = &rock.fs->f[c->qid.path];
+	if(offset >= f->length)
+		return 0;
+
+	qlock(&rock.fs->ql);
+	if(waserror()){
+		qunlock(&rock.fs->ql);
+		nexterror();
+	}
+	if(n + offset >= f->length)
+		n = f->length - offset;
+
+	/* walk to starting data block */
+	if(0 && f->finger <= offset && f->fbno != Notabno){
+		sofar = f->finger;
+		bno = f->fbno;
+	} else {
+		sofar = 0;
+		bno = f->dbno;
+	}
+	for(; sofar + Dlen <= offset; sofar += Dlen){
+		md = readdata(rock.fs, bno, buf, 0);
+		if(md == 0)
+			error(Eio);
+		bno = GETS(md->bno);
+	}
+
+	/* read data */
+	off = offset%Dlen;
+	offset -= off;
+	for(sofar = 0; sofar < n; sofar += i){
+		md = readdata(rock.fs, bno, buf, &i);
+		if(md == 0)
+			error(Eio);
+
+		/* update finger for successful read */
+		f->finger = offset;
+		f->fbno = bno;
+		offset += Dlen;
+
+		i -= off;
+		if(i > n - sofar)
+			i = n - sofar;
+		memmove(p, md->data+off, i);
+		p += i;
+		bno = GETS(md->bno);
+		off = 0;
+	}
+	qunlock(&rock.fs->ql);
+	poperror();
+
+	return sofar;
+}
+
+/*
+ *  if we get a write error in this routine, blocks will
+ *  be lost.  They should be recovered next fsinit.
+ */
+static long
+tinyfswrite(Chan *c, void *a, long n, vlong offset)
+{
+	Tfile *f;
+	int last, next, i, finger, off, used;
+	ulong bno, fbno;
+	Mdata *md;
+	uchar buf[Blen];
+	uchar *p;
+	volatile struct {
+		Tfs *fs;
+		ulong dbno;
+	} rock;
+
+	if(c->qid.type & QTDIR)
+		error(Eperm);
+
+	if(n == 0)
+		return 0;
+
+	p = a;
+	rock.fs = &tinyfs.fs[c->dev];
+	f = &rock.fs->f[c->qid.path];
+
+	qlock(&rock.fs->ql);
+	rock.dbno = Notabno;
+	if(waserror()){
+		freeblocks(rock.fs, rock.dbno, Notabno);
+		qunlock(&rock.fs->ql);
+		nexterror();
+	}
+
+	/* files are append only, anything else is illegal */
+	if(offset != f->length)
+		error("append only");
+
+	/* write blocks backwards */
+	p += n;
+	last = offset + n;
+	fbno = Notabno;
+	finger = 0;
+	off = offset; /* so we have something signed to compare against */
+	for(next = ((last-1)/Dlen)*Dlen; next >= off; next -= Dlen){
+		bno = mapalloc(rock.fs);
+		if(bno == Notabno)
+			error("out of space");
+		i = last - next;
+		p -= i;
+		if(last == n+offset){
+			writedata(rock.fs, bno, rock.dbno, p, i, 1);
+			finger = next;	/* remember for later */
+			fbno = bno;
+		} else {
+			writedata(rock.fs, bno, rock.dbno, p, i, 0);
+		}
+		rock.dbno = bno;
+		last = next;
+	}
+
+	/* walk to last data block */
+	md = (Mdata*)buf;
+	if(0 && f->finger < offset && f->fbno != Notabno){
+		next = f->finger;
+		bno = f->fbno;
+	} else {
+		next = 0;
+		bno = f->dbno;
+	}
+
+	used = 0;
+	while(bno != Notabno){
+		md = readdata(rock.fs, bno, buf, &used);
+		if(md == 0)
+			error(Eio);
+		if(md->type == Tagend){
+			if(next + Dlen < offset)
+				panic("devtinyfs1");
+			break;
+		}
+		next += Dlen;
+		if(next > offset)
+			panic("devtinyfs1");
+		bno = GETS(md->bno);
+	}
+
+	/* point to new blocks */
+	if(offset == 0){
+		/* first block in a file */
+		f->dbno = rock.dbno;
+		writedir(rock.fs, f);
+	} else {
+		/* updating a current block */
+		i = last - offset;
+		if(i > 0){
+			p -= i;
+			memmove(md->data + used, p, i);
+			used += i;
+		}
+		writedata(rock.fs, bno, rock.dbno, md->data, used, last == n+offset);
+	}
+	f->length += n;
+
+	/* update finger */
+	if(fbno != Notabno){
+		f->finger = finger;
+		f->fbno =  fbno;
+	}
+	poperror();
+	qunlock(&rock.fs->ql);
+
+	return n;
+}
+
+Dev tinyfsdevtab = {
+	'F',
+	"tinyfs",
+
+	devreset,
+	tinyfsinit,
+	devshutdown,
+	tinyfsattach,
+	tinyfswalk,
+	tinyfsstat,
+	tinyfsopen,
+	tinyfscreate,
+	tinyfsclose,
+	tinyfsread,
+	devbread,
+	tinyfswrite,
+	devbwrite,
+	tinyfsremove,
+	devwstat,
+};
--- /dev/null
+++ b/os/port/devtk.c
@@ -1,0 +1,180 @@
+#include	"u.h"
+#include	"../port/lib.h"
+#include	"mem.h"
+#include	"dat.h"
+#include	"fns.h"
+#include	"../port/error.h"
+
+#include <interp.h>
+
+#include "draw.h"
+
+enum{
+	Qdir,
+	Qtkevents
+};
+
+static
+Dirtab tkdirtab[]={
+	{".",	{Qdir,0,QTDIR},	0,	0500},
+	{"tkevents",		{Qtkevents, 0},		0,	0600},
+};
+
+static struct {
+	QLock	l;
+	Queue*	eq;
+	Ref	inuse;
+} tkevents;
+
+static void
+tkwiretapper(void *top, char *cmd, char *result, void *image, Rectangle *rp)
+{
+	Block *b;
+	int n;
+	char *s, *e;
+
+	n = 12;
+	if(cmd != nil)
+		n += strlen(cmd)+2+1;
+	if(result != nil)
+		n += strlen(result)+2+1;
+	if(image != nil)
+		n += 12;
+	if(rp != nil)
+		n += 4*20;
+	n++;
+	b = allocb(n);
+	if(b != nil){
+		s = (char*)b->wp;
+		e = s+n;
+		s += snprint(s, e-s, "%p", top);
+		if(cmd != nil){
+			*s++ = ' ';
+			*s++ = '[';
+			n = strlen(cmd);
+			memmove(s, cmd, n);
+			s += n;
+			*s++ = ']';
+		}
+		/* ignore result for now */
+		if(image != nil)
+			s += snprint(s, e-s, " %p", image);
+		if(rp != nil)
+			s += snprint(s, e-s, " %d %d %d %d", rp->min.x, rp->min.y, rp->max.x, rp->max.y);
+		*s++ = '\n';
+		b->wp = (uchar*)s;
+		release();
+		qlock(&tkevents.l);
+		if(waserror()){
+			qunlock(&tkevents.l);
+			acquire();
+			return;
+		}
+		if(tkevents.eq != nil)
+			qbwrite(tkevents.eq, b);
+		poperror();
+		qunlock(&tkevents.l);
+		acquire();
+	}
+}
+
+void	(*tkwiretap)(void*, char*, char*, void*, Rectangle*);
+
+static Chan*
+tkattach(char* spec)
+{
+	return devattach(L'τ', spec);
+}
+
+static Walkqid*
+tkwalk(Chan *c, Chan *nc, char **name, int nname)
+{
+	return devwalk(c, nc, name, nname, tkdirtab, nelem(tkdirtab), devgen);
+}
+
+static int
+tkstat(Chan *c, uchar *db, int n)
+{
+	return devstat(c, db, n, tkdirtab, nelem(tkdirtab), devgen);
+}
+
+static Chan*
+tkopen(Chan* c, int omode)
+{
+	if(c->qid.type & QTDIR)
+		return devopen(c, omode, tkdirtab, nelem(tkdirtab), devgen);
+	switch((ulong)c->qid.path){
+	case Qtkevents:
+		c = devopen(c, omode, tkdirtab, nelem(tkdirtab), devgen);
+		qlock(&tkevents.l);
+		if(incref(&tkevents.inuse) != 1){
+			qunlock(&tkevents.l);
+			error(Einuse);
+		}
+		if(tkevents.eq == nil)
+			tkevents.eq = qopen(256*1024, 0, nil, nil);
+		else
+			qreopen(tkevents.eq);
+		tkwiretap = tkwiretapper;
+		qunlock(&tkevents.l);
+		break;
+	}
+	return c;
+}
+
+static void
+tkclose(Chan* c)
+{
+	if(c->qid.type & QTDIR || (c->flag & COPEN) == 0)
+		return;
+	qlock(&tkevents.l);
+	if(decref(&tkevents.inuse) == 0){
+		tkwiretap = nil;
+		qclose(tkevents.eq);
+	}
+	qunlock(&tkevents.l);
+}
+
+static long
+tkread(Chan* c, void* a, long n, vlong offset)
+{
+	USED(offset);
+	switch((ulong)c->qid.path){
+	case Qdir:
+		return devdirread(c, a, n, tkdirtab, nelem(tkdirtab), devgen);
+	case Qtkevents:
+		return qread(tkevents.eq, a, n);
+	default:
+		n=0;
+		break;
+	}
+	return n;
+}
+
+static long
+tkwrite(Chan*, void*, long, vlong)
+{
+	error(Ebadusefd);
+	return 0;
+}
+
+Dev tkdevtab = {
+	L'τ',
+	"tk",
+
+	devreset,
+	devinit,
+	devshutdown,
+	tkattach,
+	tkwalk,
+	tkstat,
+	tkopen,
+	devcreate,
+	tkclose,
+	tkread,
+	devbread,
+	tkwrite,
+	devbwrite,
+	devremove,
+	devwstat,
+};
--- /dev/null
+++ b/os/port/devuart.c
@@ -1,0 +1,748 @@
+#include	"u.h"
+#include	"../port/lib.h"
+#include	"mem.h"
+#include	"dat.h"
+#include	"fns.h"
+#include	"io.h"
+#include	"../port/error.h"
+
+#include	"../port/netif.h"
+
+#include	"../port/uart.h"
+
+enum
+{
+	/* soft flow control chars */
+	CTLS= 023,
+	CTLQ= 021,
+};
+
+extern Dev uartdevtab;
+extern PhysUart* physuart[];
+
+static Uart* uartlist;
+static Uart** uart;
+static int uartnuart;
+static Dirtab *uartdir;
+static int uartndir;
+
+struct Uartalloc {
+	Lock;
+	Uart *elist;	/* list of enabled interfaces */
+} uartalloc;
+
+static void	uartclock(void);
+static void	uartflow(void*);
+
+/*
+ *  enable/disable uart and add/remove to list of enabled uarts
+ */
+static Uart*
+uartenable(Uart *p)
+{
+	Uart **l;
+
+	if(p->iq == nil){
+		if((p->iq = qopen(4*1024, 0, uartflow, p)) == nil)
+			return nil;
+	}
+	else
+		qreopen(p->iq);
+	if(p->oq == nil){
+		if((p->oq = qopen(4*1024, 0, uartkick, p)) == nil){
+			qfree(p->iq);
+			p->iq = nil;
+			return nil;
+		}
+	}
+	else
+		qreopen(p->oq);
+
+	p->ir = p->istage;
+	p->iw = p->istage;
+	p->ie = &p->istage[Stagesize];
+	p->op = p->ostage;
+	p->oe = p->ostage;
+
+	p->hup_dsr = p->hup_dcd = 0;
+	p->dsr = p->dcd = 0;
+
+	/* assume we can send */
+	p->cts = 1;
+	p->ctsbackoff = 0;
+
+	if(p->bits == 0)
+		uartctl(p, "l8");
+	if(p->stop == 0)
+		uartctl(p, "s1");
+	if(p->parity == 0)
+		uartctl(p, "pn");
+	if(p->baud == 0)
+		uartctl(p, "b9600");
+	(*p->phys->enable)(p, 1);
+
+	lock(&uartalloc);
+	for(l = &uartalloc.elist; *l; l = &(*l)->elist){
+		if(*l == p)
+			break;
+	}
+	if(*l == 0){
+		p->elist = uartalloc.elist;
+		uartalloc.elist = p;
+	}
+	p->enabled = 1;
+	unlock(&uartalloc);
+
+	return p;
+}
+
+static void
+uartdisable(Uart *p)
+{
+	Uart **l;
+
+	(*p->phys->disable)(p);
+
+	lock(&uartalloc);
+	for(l = &uartalloc.elist; *l; l = &(*l)->elist){
+		if(*l == p){
+			*l = p->elist;
+			break;
+		}
+	}
+	p->enabled = 0;
+	unlock(&uartalloc);
+}
+
+void
+uartmouse(Uart* p, int (*putc)(Queue*, int), int setb1200)
+{
+	qlock(p);
+	if(p->opens++ == 0 && uartenable(p) == nil){
+		qunlock(p);
+		error(Enodev);
+	}
+	if(setb1200)
+		uartctl(p, "b1200");
+	p->putc = putc;
+	p->special = 1;
+	qunlock(p);
+}
+
+void
+uartsetmouseputc(Uart* p, int (*putc)(Queue*, int))
+{
+	qlock(p);
+	if(p->opens == 0 || p->special == 0){
+		qunlock(p);
+		error(Enodev);
+	}
+	p->putc = putc;
+	qunlock(p);
+}
+
+static void
+setlength(int i)
+{
+	Uart *p;
+
+	if(i > 0){
+		p = uart[i];
+		if(p && p->opens && p->iq)
+			uartdir[1+3*i].length = qlen(p->iq);
+	} else for(i = 0; i < uartnuart; i++){
+		p = uart[i];
+		if(p && p->opens && p->iq)
+			uartdir[1+3*i].length = qlen(p->iq);
+	}
+}
+
+/*
+ *  set up the '#t' directory
+ */
+static void
+uartreset(void)
+{
+	int i;
+	Dirtab *dp;
+	Uart *p, *tail;
+
+	tail = nil;
+	for(i = 0; physuart[i] != nil; i++){
+		if(physuart[i]->pnp == nil)
+			continue;
+		if((p = physuart[i]->pnp()) == nil)
+			continue;
+		if(uartlist != nil)
+			tail->next = p;
+		else
+			uartlist = p;
+		for(tail = p; tail->next != nil; tail = tail->next)
+			uartnuart++;
+		uartnuart++;
+	}
+
+	if(uartnuart)
+		uart = xalloc(uartnuart*sizeof(Uart*));
+
+	uartndir = 1 + 3*uartnuart;
+	uartdir = xalloc(uartndir * sizeof(Dirtab));
+	dp = uartdir;
+	strcpy(dp->name, ".");
+	mkqid(&dp->qid, 0, 0, QTDIR);
+	dp->length = 0;
+	dp->perm = DMDIR|0555;
+	dp++;
+	p = uartlist;
+	for(i = 0; i < uartnuart; i++){
+		/* 3 directory entries per port */
+		sprint(dp->name, "eia%d", i);
+		dp->qid.path = NETQID(i, Ndataqid);
+		dp->perm = 0660;
+		dp++;
+		sprint(dp->name, "eia%dctl", i);
+		dp->qid.path = NETQID(i, Nctlqid);
+		dp->perm = 0660;
+		dp++;
+		sprint(dp->name, "eia%dstatus", i);
+		dp->qid.path = NETQID(i, Nstatqid);
+		dp->perm = 0444;
+		dp++;
+
+		uart[i] = p;
+		p->dev = i;
+		if(p->console || p->special){
+			if(uartenable(p) != nil){
+				if(p->console){
+					kbdq = p->iq;
+					printq = p->oq;
+					p->putc = kbdcr2nl;
+				}
+				p->opens++;
+			}
+		}
+		p = p->next;
+	}
+
+	if(uartnuart){
+		/*
+		 * at 115200 baud, the 1024 char buffer takes 56 ms to process,
+		 * processing it every 22 ms should be fine
+		 */
+		addclock0link(uartclock, 22);
+	}
+}
+
+
+static Chan*
+uartattach(char *spec)
+{
+	return devattach('t', spec);
+}
+
+static Walkqid*
+uartwalk(Chan *c, Chan *nc, char **name, int nname)
+{
+	return devwalk(c, nc, name, nname, uartdir, uartndir, devgen);
+}
+
+static int
+uartstat(Chan *c, uchar *dp, int n)
+{
+	if(NETTYPE(c->qid.path) == Ndataqid)
+		setlength(NETID(c->qid.path));
+	return devstat(c, dp, n, uartdir, uartndir, devgen);
+}
+
+static Chan*
+uartopen(Chan *c, int omode)
+{
+	Uart *p;
+
+	c = devopen(c, omode, uartdir, uartndir, devgen);
+
+	switch(NETTYPE(c->qid.path)){
+	case Nctlqid:
+	case Ndataqid:
+		p = uart[NETID(c->qid.path)];
+		qlock(p);
+		if(p->opens++ == 0 && uartenable(p) == nil){
+			qunlock(p);
+			c->flag &= ~COPEN;
+			error(Enodev);
+		}
+		qunlock(p);
+		break;
+	}
+
+	c->iounit = qiomaxatomic;
+	return c;
+}
+
+static int
+uartdrained(void* arg)
+{
+	Uart *p;
+
+	p = arg;
+	return qlen(p->oq) == 0 && p->op == p->oe;
+}
+
+static void
+uartdrainoutput(Uart *p)
+{
+	if(!p->enabled)
+		return;
+
+	p->drain = 1;
+	if(waserror()){
+		p->drain = 0;
+		nexterror();
+	}
+	sleep(&p->r, uartdrained, p);
+	poperror();
+}
+
+static void
+uartclose(Chan *c)
+{
+	Uart *p;
+
+	if(c->qid.type & QTDIR)
+		return;
+	if((c->flag & COPEN) == 0)
+		return;
+	switch(NETTYPE(c->qid.path)){
+	case Ndataqid:
+	case Nctlqid:
+		p = uart[NETID(c->qid.path)];
+		qlock(p);
+		if(--(p->opens) == 0){
+			qclose(p->iq);
+			p->ir = p->iw = p->istage;
+
+			/*
+			 */
+			qhangup(p->oq, nil);
+			if(!waserror()){
+				uartdrainoutput(p);
+				poperror();
+			}
+			qclose(p->oq);
+			uartdisable(p);
+			p->dcd = p->dsr = p->dohup = 0;
+		}
+		qunlock(p);
+		break;
+	}
+}
+
+static long
+uartread(Chan *c, void *buf, long n, vlong off)
+{
+	Uart *p;
+	ulong offset = off;
+
+	if(c->qid.type & QTDIR){
+		setlength(-1);
+		return devdirread(c, buf, n, uartdir, uartndir, devgen);
+	}
+
+	p = uart[NETID(c->qid.path)];
+	switch(NETTYPE(c->qid.path)){
+	case Ndataqid:
+		return qread(p->iq, buf, n);
+	case Nctlqid:
+		return readnum(offset, buf, n, NETID(c->qid.path), NUMSIZE);
+	case Nstatqid:
+		return (*p->phys->status)(p, buf, n, offset);
+	}
+
+	return 0;
+}
+
+int
+uartctl(Uart *p, char *cmd)
+{
+	char *f[16];
+	int i, n, nf;
+
+	nf = tokenize(cmd, f, nelem(f));
+	for(i = 0; i < nf; i++){
+		if(strncmp(f[i], "break", 5) == 0){
+			(*p->phys->dobreak)(p, 0);
+			continue;
+		}
+
+		n = atoi(f[i]+1);
+		switch(*f[i]){
+		case 'B':
+		case 'b':
+			uartdrainoutput(p);
+			if((*p->phys->baud)(p, n) < 0)
+				return -1;
+			break;
+		case 'C':
+		case 'c':
+			p->hup_dcd = n;
+			break;
+		case 'D':
+		case 'd':
+			uartdrainoutput(p);
+			(*p->phys->dtr)(p, n);
+			break;
+		case 'E':
+		case 'e':
+			p->hup_dsr = n;
+			break;
+		case 'f':
+		case 'F':
+			if(p->oq != nil)
+				qflush(p->oq);
+			break;
+		case 'H':
+		case 'h':
+			if(p->iq != nil)
+				qhangup(p->iq, 0);
+			if(p->oq != nil)
+				qhangup(p->oq, 0);
+			break;
+		case 'i':
+		case 'I':
+			uartdrainoutput(p);
+			(*p->phys->fifo)(p, n);
+			break;
+		case 'K':
+		case 'k':
+			uartdrainoutput(p);
+			(*p->phys->dobreak)(p, n);
+			break;
+		case 'L':
+		case 'l':
+			uartdrainoutput(p);
+			if((*p->phys->bits)(p, n) < 0)
+				return -1;
+			break;
+		case 'm':
+		case 'M':
+			uartdrainoutput(p);
+			(*p->phys->modemctl)(p, n);
+			break;
+		case 'n':
+		case 'N':
+			if(p->oq != nil)
+				qnoblock(p->oq, n);
+			break;
+		case 'P':
+		case 'p':
+			uartdrainoutput(p);
+			if((*p->phys->parity)(p, *(f[i]+1)) < 0)
+				return -1;
+			break;
+		case 'Q':
+		case 'q':
+			if(p->iq != nil)
+				qsetlimit(p->iq, n);
+			if(p->oq != nil)
+				qsetlimit(p->oq, n);
+			break;
+		case 'R':
+		case 'r':
+			uartdrainoutput(p);
+			(*p->phys->rts)(p, n);
+			break;
+		case 'S':
+		case 's':
+			uartdrainoutput(p);
+			if((*p->phys->stop)(p, n) < 0)
+				return -1;
+			break;
+		case 'T':
+		case 't':
+			p->dcdts = n;
+			break;
+		case 'W':
+		case 'w':
+			/* obsolete */
+			break;
+		case 'X':
+		case 'x':
+			if(p->enabled){
+				ilock(&p->tlock);
+				p->xonoff = n;
+				iunlock(&p->tlock);
+			}
+			break;
+		}
+	}
+	return 0;
+}
+
+static long
+uartwrite(Chan *c, void *buf, long n, vlong)
+{
+	Uart *p;
+	char *cmd;
+
+	if(c->qid.type & QTDIR)
+		error(Eperm);
+
+	p = uart[NETID(c->qid.path)];
+
+	switch(NETTYPE(c->qid.path)){
+	case Ndataqid:
+		qlock(p);
+		if(waserror()){
+			qunlock(p);
+			nexterror();
+		}
+
+		n = qwrite(p->oq, buf, n);
+
+		qunlock(p);
+		poperror();
+		break;
+	case Nctlqid:
+		cmd = malloc(n+1);
+		memmove(cmd, buf, n);
+		cmd[n] = 0;
+		qlock(p);
+		if(waserror()){
+			qunlock(p);
+			free(cmd);
+			nexterror();
+		}
+
+		/* let output drain */
+		if(uartctl(p, cmd) < 0)
+			error(Ebadarg);
+
+		qunlock(p);
+		poperror();
+		free(cmd);
+		break;
+	}
+
+	return n;
+}
+
+static int
+uartwstat(Chan *c, uchar *dp, int n)
+{
+	Dir d;
+	Dirtab *dt;
+
+	if(!iseve())
+		error(Eperm);
+	if(QTDIR & c->qid.type)
+		error(Eperm);
+	if(NETTYPE(c->qid.path) == Nstatqid)
+		error(Eperm);
+
+	dt = &uartdir[1 + 3 * NETID(c->qid.path)];
+	n = convM2D(dp, n, &d, nil);
+	if(n == 0)
+		error(Eshortstat);
+	if(d.mode != ~0UL)
+		dt[0].perm = dt[1].perm = d.mode;
+	return n;
+}
+
+void
+uartpower(int on)
+{
+	Uart *p;
+
+	for(p = uartlist; p != nil; p = p->next) {
+		if(p->phys->power)
+			(*p->phys->power)(p, on);
+	}
+}
+
+Dev uartdevtab = {
+	't',
+	"uart",
+
+	uartreset,
+	devinit,
+	devshutdown,
+	uartattach,
+	uartwalk,
+	uartstat,
+	uartopen,
+	devcreate,
+	uartclose,
+	uartread,
+	devbread,
+	uartwrite,
+	devbwrite,
+	devremove,
+	uartwstat,
+	uartpower,
+};
+
+/*
+ *  restart input if it's off
+ */
+static void
+uartflow(void *v)
+{
+	Uart *p;
+
+	p = v;
+	if(p->modem)
+		(*p->phys->rts)(p, 1);
+}
+
+/*
+ *  put some bytes into the local queue to avoid calling
+ *  qconsume for every character
+ */
+int
+uartstageoutput(Uart *p)
+{
+	int n;
+
+	n = qconsume(p->oq, p->ostage, Stagesize);
+	if(n <= 0)
+		return 0;
+	p->op = p->ostage;
+	p->oe = p->ostage + n;
+	return n;
+}
+
+/*
+ *  restart output
+ */
+void
+uartkick(void *v)
+{
+	Uart *p = v;
+
+	if(p->blocked)
+		return;
+
+	ilock(&p->tlock);
+	(*p->phys->kick)(p);
+	iunlock(&p->tlock);
+
+	if(p->drain && uartdrained(p)){
+		p->drain = 0;
+		wakeup(&p->r);
+	}
+}
+
+/*
+ *  receive a character at interrupt time
+ */
+void
+uartrecv(Uart *p,  char ch)
+{
+	uchar *next;
+
+	/* software flow control */
+	if(p->xonoff){
+		if(ch == CTLS){
+			p->blocked = 1;
+		}else if(ch == CTLQ){
+			p->blocked = 0;
+			p->ctsbackoff = 2; /* clock gets output going again */
+		}
+	}
+
+	/* receive the character */
+	if(p->putc)
+		p->putc(p->iq, ch);
+	else{
+		next = p->iw + 1;
+		if(next == p->ie)
+			next = p->istage;
+		if(next != p->ir){
+			*p->iw = ch;
+			p->iw = next;
+		}
+	}
+}
+
+/*
+ *  we save up input characters till clock time to reduce
+ *  per character interrupt overhead.
+ */
+static void
+uartclock(void)
+{
+	Uart *p;
+	uchar *iw;
+
+	for(p = uartalloc.elist; p; p = p->elist){
+
+		/* this amortizes cost of qproduce to many chars */
+		if(p->iw != p->ir){
+			iw = p->iw;
+			if(iw < p->ir){
+				if(qproduce(p->iq, p->ir, p->ie-p->ir) < 0)
+					(*p->phys->rts)(p, 0);
+				p->ir = p->istage;
+			}
+			if(iw > p->ir)
+				if(qproduce(p->iq, p->ir, iw-p->ir) < 0)
+					(*p->phys->rts)(p, 0);
+			p->ir = iw;
+		}
+
+		/* hang up if requested */
+		if(p->dohup){
+			qhangup(p->iq, 0);
+			qhangup(p->oq, 0);
+			p->dohup = 0;
+		}
+
+		/* this adds hysteresis to hardware/software flow control */
+		if(p->ctsbackoff){
+			ilock(&p->tlock);
+			if(p->ctsbackoff){
+				if(--(p->ctsbackoff) == 0)
+					(*p->phys->kick)(p);
+			}
+			iunlock(&p->tlock);
+		}
+	}
+}
+
+/*
+ * polling console input, output
+ */
+
+Uart* consuart;
+
+int
+uartgetc(void)
+{
+	if(consuart == nil || consuart->phys->getc == nil)
+		return -1;
+	return consuart->phys->getc(consuart);
+}
+
+void
+uartputc(int c)
+{
+	if(consuart == nil || consuart->phys->putc == nil)
+		return;
+	consuart->phys->putc(consuart, c);
+}
+
+void
+uartputs(char *s, int n)
+{
+	char *e;
+
+	if(consuart == nil || consuart->phys->putc == nil)
+		return;
+
+	e = s+n;
+	for(; s<e; s++){
+		if(*s == '\n')
+			consuart->phys->putc(consuart, '\r');
+		consuart->phys->putc(consuart, *s);
+	}
+}
--- /dev/null
+++ b/os/port/dial.c
@@ -1,0 +1,424 @@
+#include	"u.h"
+#include	"../port/lib.h"
+#include	"mem.h"
+#include	"dat.h"
+#include	"fns.h"
+#include	"../port/error.h"
+#include	"kernel.h"
+
+typedef struct DS DS;
+
+static int	call(char*, char*, DS*);
+static int	csdial(DS*);
+static void	_dial_string_parse(char*, DS*);
+static int	nettrans(char*, char*, int na, char*, int);
+
+enum
+{
+	Maxstring=	128,
+	Maxpath=	100
+};
+
+struct DS
+{
+	char	buf[Maxstring];			/* dist string */
+	char	*netdir;
+	char	*proto;
+	char	*rem;
+	char	*local;				/* other args */
+	char	*dir;
+	int	*cfdp;
+};
+
+/*
+ *  the dialstring is of the form '[/net/]proto!dest'
+ */
+int
+kdial(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;
+	kerrstr(err, sizeof err);
+	if(strstr(err, "refused") != 0){
+		kerrstr(err, sizeof err);
+		return rv;
+	}
+
+	ds.netdir = "/net.alt";
+	rv = csdial(&ds);
+	if(rv >= 0)
+		return rv;
+
+	alterr[0] = 0;
+	kerrstr(alterr, sizeof err);
+
+	if(strstr(alterr, "translate") || strstr(alterr, "does not exist"))
+		kerrstr(err, sizeof err);
+	else
+		kerrstr(alterr, sizeof alterr);
+	return rv;
+}
+
+static int
+csdial(DS *ds)
+{
+	int n, fd, rv;
+	char *p, buf[Maxstring], clone[Maxpath], err[ERRMAX], besterr[ERRMAX];
+
+	/*
+	 *  open connection server
+	 */
+	snprint(buf, sizeof(buf), "%s/cs", ds->netdir);
+	fd = kopen(buf, ORDWR);
+	if(fd < 0){
+		/* no connection server, don't translate */
+		snprint(clone, sizeof(clone), "%s/%s/clone", ds->netdir, ds->proto);
+		return call(clone, ds->rem, ds);
+	}
+
+	/*
+	 *  ask connection server to translate
+	 */
+	sprint(buf, "%s!%s", ds->proto, ds->rem);
+	if(kwrite(fd, buf, strlen(buf)) < 0){
+		kerrstr(err, sizeof err);
+		kclose(fd);
+		kwerrstr("%s (%s)", err, buf);
+		return -1;
+	}
+
+	/*
+	 *  loop through each address from the connection server till
+	 *  we get one that works.
+	 */
+	*besterr = 0;
+	strcpy(err, Egreg);
+	rv = -1;
+	kseek(fd, 0, 0);
+	while((n = kread(fd, buf, sizeof(buf) - 1)) > 0){
+		buf[n] = 0;
+		p = strchr(buf, ' ');
+		if(p == 0)
+			continue;
+		*p++ = 0;
+		rv = call(buf, p, ds);
+		if(rv >= 0)
+			break;
+		err[0] = 0;
+		kerrstr(err, sizeof err);
+		if(strstr(err, "does not exist") == 0)
+			memmove(besterr, err, sizeof besterr);
+	}
+	kclose(fd);
+
+	if(rv < 0 && *besterr)
+		kerrstr(besterr, sizeof besterr);
+	else
+		kerrstr(err, sizeof err);
+	return rv;
+}
+
+static int
+call(char *clone, char *dest, DS *ds)
+{
+	int fd, cfd, n;
+	char name[Maxpath], data[Maxpath], err[ERRMAX], *p;
+
+	cfd = kopen(clone, ORDWR);
+	if(cfd < 0){
+		kerrstr(err, sizeof err);
+		kwerrstr("%s (%s)", err, clone);
+		return -1;
+	}
+
+	/* get directory name */
+	n = kread(cfd, name, sizeof(name)-1);
+	if(n < 0){
+		kerrstr(err, sizeof err);
+		kclose(cfd);
+		kwerrstr("read %s: %s", clone, err);
+		return -1;
+	}
+	name[n] = 0;
+	for(p = name; *p == ' '; p++)
+		;
+	sprint(name, "%ld", strtoul(p, 0, 0));
+	p = strrchr(clone, '/');
+	*p = 0;
+	if(ds->dir)
+		snprint(ds->dir, NETPATHLEN, "%s/%s", clone, name);
+	snprint(data, sizeof(data), "%s/%s/data", clone, name);
+
+	/* connect */
+	if(ds->local)
+		snprint(name, sizeof(name), "connect %s %s", dest, ds->local);
+	else
+		snprint(name, sizeof(name), "connect %s", dest);
+	if(kwrite(cfd, name, strlen(name)) < 0){
+		err[0] = 0;
+		kerrstr(err, sizeof err);
+		kclose(cfd);
+		kwerrstr("%s (%s)", err, name);
+		return -1;
+	}
+
+	/* open data connection */
+	fd = kopen(data, ORDWR);
+	if(fd < 0){
+		err[0] = 0;
+		kerrstr(err, sizeof err);
+		kwerrstr("%s (%s)", err, data);
+		kclose(cfd);
+		return -1;
+	}
+	if(ds->cfdp)
+		*ds->cfdp = cfd;
+	else
+		kclose(cfd);
+
+	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;
+	}
+}
+
+/*
+ *  announce a network service.
+ */
+int
+kannounce(char *addr, char *dir)
+{
+	int ctl, n, m;
+	char buf[NETPATHLEN];
+	char buf2[Maxpath];
+	char netdir[NETPATHLEN];
+	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 = kopen(netdir, ORDWR);
+	if(ctl<0)
+		return -1;
+	cp = strrchr(netdir, '/');
+	*cp = 0;
+
+	/*
+	 *  find out which line we have
+	 */
+	n = sprint(buf, "%.*s/", sizeof buf, netdir);
+	m = kread(ctl, &buf[n], sizeof(buf)-n-1);
+	if(m <= 0){
+		kclose(ctl);
+		return -1;
+	}
+	buf[n+m] = 0;
+
+	/*
+	 *  make the call
+	 */
+	n = snprint(buf2, sizeof buf2, "announce %s", naddr);
+	if(kwrite(ctl, buf2, n)!=n){
+		kclose(ctl);
+		return -1;
+	}
+
+	/*
+	 *  return directory etc.
+	 */
+	if(dir)
+		strcpy(dir, buf);
+	return ctl;
+}
+
+/*
+ *  listen for an incoming call
+ */
+int
+klisten(char *dir, char *newdir)
+{
+	int ctl, n, m;
+	char buf[NETPATHLEN];
+	char *cp;
+
+	/*
+	 *  open listen, wait for a call
+	 */
+	snprint(buf, sizeof buf, "%s/listen", dir);
+	ctl = kopen(buf, ORDWR);
+	if(ctl < 0)
+		return -1;
+
+	/*
+	 *  find out which line we have
+	 */
+	strcpy(buf, dir);
+	cp = strrchr(buf, '/');
+	*++cp = 0;
+	n = cp-buf;
+	m = kread(ctl, cp, sizeof(buf) - n - 1);
+	if(m <= 0){
+		kclose(ctl);
+		return -1;
+	}
+	buf[n+m] = 0;
+
+	/*
+	 *  return directory etc.
+	 */
+	if(newdir)
+		strcpy(newdir, buf);
+	return ctl;
+
+}
+
+/*
+ *  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[NETPATHLEN];
+	char *p, *p2;
+	long n;
+
+	/*
+	 *  parse, get network directory
+	 */
+	p = strchr(addr, '!');
+	if(p == 0){
+		kwerrstr("bad dial string: %s", addr);
+		return -1;
+	}
+	if(*addr != '/'){
+		strcpy(netdir, "/net");
+	} else {
+		for(p2 = p; *p2 != '/'; p2--)
+			;
+		i = p2 - addr;
+		if(i == 0 || i >= sizeof(netdir)){
+			kwerrstr("bad dial string: %s", addr);
+			return -1;
+		}
+		strncpy(netdir, addr, i);
+		netdir[i] = 0;
+		addr = p2 + 1;
+	}
+
+	/*
+	 *  ask the connection server
+	 */
+	sprint(buf, "%s/cs", netdir);
+	fd = kopen(buf, ORDWR);
+	if(fd < 0)
+		return identtrans(netdir, addr, naddr, na, file, nf);
+	if(kwrite(fd, addr, strlen(addr)) < 0){
+		kclose(fd);
+		return -1;
+	}
+	kseek(fd, 0, 0);
+	n = kread(fd, buf, sizeof(buf)-1);
+	kclose(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;
+}
--- /dev/null
+++ b/os/port/dis.c
@@ -1,0 +1,1141 @@
+#include "u.h"
+#include "../port/lib.h"
+#include "mem.h"
+#include "dat.h"
+#include "fns.h"
+#include "../port/error.h"
+#include <isa.h>
+#include <interp.h>
+#include <kernel.h>
+#include "raise.h"
+
+	void	vmachine(void*);
+
+struct
+{
+	Lock	l;
+	Prog*	runhd;
+	Prog*	runtl;
+	Prog*	head;
+	Prog*	tail;
+	Rendez	irend;
+	int	idle;
+	int	nyield;
+	int	creating;
+	Proc*	vmq;		/* queue of procs wanting vm */
+	Proc*	vmqt;
+	Proc*	idlevmq;	/* queue of procs wanting work */
+	Atidle*	idletasks;
+} isched;
+
+int	bflag;
+int	cflag;
+uvlong	gcbusy;
+uvlong	gcidle;
+uvlong	gcidlepass;
+uvlong	gcpartial;
+int keepbroken = 1;
+static Prog*	proghash[64];
+
+static Progs*	delgrp(Prog*);
+static void	addgrp(Prog*, Prog*);
+void	printgrp(Prog*, char*);
+
+static Prog**
+pidlook(int pid)
+{
+	ulong h;
+	Prog **l;
+
+	h = (ulong)pid % nelem(proghash);
+	for(l = &proghash[h]; *l != nil && (*l)->pid != pid; l = &(*l)->pidlink)
+		;
+	return l;
+}
+
+int
+tready(void *a)
+{
+	USED(a);
+	return isched.runhd != nil || isched.vmq != nil;
+}
+
+Prog*
+progpid(int pid)
+{
+	return *pidlook(pid);
+}
+
+Prog*
+progn(int n)
+{
+	Prog *p;
+
+	for(p = isched.head; p && n--; p = p->next)
+		;
+	return p;
+}
+
+int
+nprog(void)
+{
+	int n;
+	Prog *p;
+
+	n = 0;
+	for(p = isched.head; p; p = p->next)
+		n++;
+	return n;
+}
+
+static void
+accountant(void)
+{
+	Prog *p;
+
+	p = isched.runhd;
+	if(p != nil)
+		p->ticks++;
+}
+
+static void
+execatidle(void)
+{
+	int done;
+
+	if(tready(nil))
+		return;
+
+	gcidle++;
+	up->type = IdleGC;
+	up->iprog = nil;
+	addrun(up->prog);
+
+	done = gccolor+3;
+	while(gccolor < done && gcruns()) {
+		if(isched.vmq != nil || isched.runhd != isched.runtl) {
+			gcpartial++;
+			break;
+		}
+		rungc(isched.head);
+		gcidlepass++;
+		if(((ulong)gcidlepass&0xFF) == 0)
+			sched();
+	}
+
+	up->type = Interp;
+	delrunq(up->prog);
+}
+
+Prog*
+newprog(Prog *p, Modlink *m)
+{
+	Heap *h;
+	Prog *n, **ph;
+	Osenv *on, *op;
+	static int pidnum;
+
+	if(p != nil){
+		if(p->group != nil)
+			p->flags |= p->group->flags & Pkilled;
+		if(p->kill != nil)
+			error(p->kill);
+		if(p->flags & Pkilled)
+			error("");
+	}
+	n = malloc(sizeof(Prog)+sizeof(Osenv));
+	if(n == 0){
+		if(p == nil)
+			panic("no memory");
+		else
+			error(exNomem);
+	}
+
+	n->pid = ++pidnum;
+	if(n->pid <= 0)
+		panic("no pids");
+	n->group = nil;
+
+	if(isched.tail != nil) {
+		n->prev = isched.tail;
+		isched.tail->next = n;
+	}
+	else {
+		isched.head = n;
+		n->prev = nil;
+	}
+	isched.tail = n;
+
+	ph = pidlook(n->pid);
+	if(*ph != nil)
+		panic("dup pid");
+	n->pidlink = nil;
+	*ph = n;
+
+	n->osenv = (Osenv*)((uchar*)n + sizeof(Prog));
+	n->xec = xec;
+	n->quanta = PQUANTA;
+	n->flags = 0;
+	n->exval = H;
+
+	h = D2H(m);
+	h->ref++;
+	Setmark(h);
+	n->R.M = m;
+	n->R.MP = m->MP;
+	if(m->MP != H)
+		Setmark(D2H(m->MP));
+	addrun(n);
+
+	if(p == nil){
+		newgrp(n);
+		return n;
+	}
+
+	addgrp(n, p);
+	n->flags = p->flags;
+	if(p->flags & Prestrict)
+		n->flags |= Prestricted;
+	memmove(n->osenv, p->osenv, sizeof(Osenv));
+	op = p->osenv;
+	on = n->osenv;
+	on->waitq = op->childq;
+	on->childq = nil;
+	on->debug = nil;
+	incref(on->pgrp);
+	incref(on->fgrp);
+	if(on->egrp != nil)
+		incref(on->egrp);
+	if(on->sigs != nil)
+		incref(on->sigs);
+	on->user = nil;
+	kstrdup(&on->user, op->user);
+	on->errstr = on->errbuf0;
+	on->syserrstr = on->errbuf1;
+
+	return n;
+}
+
+void
+delprog(Prog *p, char *msg)
+{
+	Osenv *o;
+	Prog **ph;
+
+	tellsomeone(p, msg);	/* call before being removed from prog list */
+
+	o = p->osenv;
+	release();
+	closepgrp(o->pgrp);
+	closefgrp(o->fgrp);
+	closeegrp(o->egrp);
+	closesigs(o->sigs);
+	acquire();
+
+	delgrp(p);
+
+	if(p->prev)
+		p->prev->next = p->next;
+	else
+		isched.head = p->next;
+
+	if(p->next)
+		p->next->prev = p->prev;
+	else
+		isched.tail = p->prev;
+
+	ph = pidlook(p->pid);
+	if(*ph == nil)
+		panic("lost pid");
+	*ph = p->pidlink;
+
+	if(p == isched.runhd) {
+		isched.runhd = p->link;
+		if(p->link == nil)
+			isched.runtl = nil;
+	}
+	p->state = 0xdeadbeef;
+	free(o->user);
+	free(p->killstr);
+	free(p->exstr);
+	free(p);
+}
+
+void
+renameproguser(char *old, char *new)
+{
+	Prog *p;
+	Osenv *o;
+
+	acquire();
+	for(p = isched.head; p; p = p->next){
+		o = p->osenv;
+		if(o->user != nil && strcmp(o->user, old) == 0)
+			kstrdup(&o->user, new);
+	}
+	release();
+}
+
+void
+tellsomeone(Prog *p, char *buf)
+{
+	Osenv *o;
+
+	if(waserror())
+		return;
+	o = p->osenv;
+	if(o->childq != nil)
+		qproduce(o->childq, buf, strlen(buf));
+	if(o->waitq != nil)
+		qproduce(o->waitq, buf, strlen(buf));
+	poperror();
+}
+
+static void
+swiprog(Prog *p)
+{
+	Proc *q, *eq;
+
+	q = proctab(0);
+	for(eq = q+conf.nproc; q < eq; q++) {
+		if(q->iprog == p) {
+			swiproc(q, 1);
+			return;
+		}
+	}
+	/*print("didn't find\n");*/
+}
+
+static Prog*
+grpleader(Prog *p)
+{
+	Progs *g;
+	Prog *l;
+
+	g = p->group;
+	if(g != nil && (l = g->head) != nil && l->pid == g->id)
+		return l;
+	return nil;
+}
+
+int
+exprog(Prog *p, char *exc)
+{
+	switch(p->state) {
+	case Palt:
+		altdone(p->R.s, p, nil, -1);
+		break;
+	case Psend:
+		cqdelp(&p->chan->send, p);
+		break;
+	case Precv:
+		cqdelp(&p->chan->recv, p);
+		break;
+	case Pready:
+		break;
+	case Prelease:
+		swiprog(p);
+		break;
+	case Pexiting:
+	case Pbroken:
+	case Pdebug:
+		return 0;
+	default:
+		panic("exprog - bad state 0x%x\n", p->state);
+	}
+	if(p->state != Pready && p->state != Prelease)
+		addrun(p);
+	if(p->kill == nil){
+		if(p->killstr == nil){
+			p->killstr = malloc(ERRMAX);
+			if(p->killstr == nil){
+				p->kill = Enomem;
+				return 1;
+			}
+		}
+		kstrcpy(p->killstr, exc, ERRMAX);
+		p->kill = p->killstr;
+	}
+	return 1;
+}
+
+static void
+propex(Prog *p, char *estr)
+{
+	Prog *f, *nf, *pgl;
+
+	if(!(p->flags & (Ppropagate|Pnotifyleader)) || p->group == nil)
+		return;
+	if(*estr == 0){
+		if((p->flags & Pkilled) == 0)
+			return;
+		estr = "killed";
+	}
+	pgl = grpleader(p);
+	if(pgl == nil)
+		pgl = p;
+	if(!(pgl->flags & (Ppropagate|Pnotifyleader)))
+		return;	/* exceptions are local; don't propagate */
+	for(f = p->group->head; f != nil; f = nf){
+		nf = f->grpnext;
+		if(f != p && f != pgl){
+			if(pgl->flags & Ppropagate)
+				exprog(f, estr);
+			else{
+				f->flags &= ~(Ppropagate|Pnotifyleader);	/* prevent recursion */
+				killprog(f, "killed");
+			}
+		}
+	}
+	if(p != pgl)
+		exprog(pgl, estr);
+}
+
+int
+killprog(Prog *p, char *cause)
+{
+	Osenv *env;
+	char msg[ERRMAX+2*KNAMELEN];
+
+	if(p == isched.runhd) {
+		p->kill = "";
+		p->flags |= Pkilled;
+		p->state = Pexiting;
+		return 0;
+	}
+
+	switch(p->state) {
+	case Palt:
+		altdone(p->R.s, p, nil, -1);
+		break;
+	case Psend:
+		cqdelp(&p->chan->send, p);
+		break;
+	case Precv:
+		cqdelp(&p->chan->recv, p);
+		break;
+	case Pready:
+		delrunq(p);
+		break;
+	case Prelease:
+		p->kill = "";
+		p->flags |= Pkilled;
+		p->state = Pexiting;
+		swiprog(p);
+		/* No break */
+	case Pexiting:
+		return 0;
+	case Pbroken:
+	case Pdebug:
+		break;
+	default:
+		panic("killprog - bad state 0x%x\n", p->state);
+	}
+
+	if(p->addrun != nil) {
+		p->kill = "";
+		p->flags |= Pkilled;
+		p->addrun(p);
+		p->addrun = nil;
+		return 0;
+	}
+
+	env = p->osenv;
+	if(env->debug != nil) {
+		p->state = Pbroken;
+		dbgexit(p, 0, cause);
+		return 0;
+	}
+
+	propex(p, "killed");
+
+	snprint(msg, sizeof(msg), "%d \"%s\":%s", p->pid, p->R.M->m->name, cause);
+
+	p->state = Pexiting;
+	gclock();
+	destroystack(&p->R);
+	delprog(p, msg);
+	gcunlock();
+
+	return 1;
+}
+
+void
+newgrp(Prog *p)
+{
+	Progs *pg, *g;
+
+	if(p->group != nil && p->group->id == p->pid)
+		return;
+	g = malloc(sizeof(*g));
+	if(g == nil)
+		error(Enomem);
+	p->flags &= ~(Ppropagate|Pnotifyleader);
+	g->id = p->pid;
+	g->flags = 0;
+	g->child = nil;
+	pg = delgrp(p);
+	g->head = g->tail = p;
+	p->group = g;
+	if(pg != nil){
+		g->sib = pg->child;
+		pg->child = g;
+	}
+	g->parent = pg;
+}
+
+static void
+addgrp(Prog *n, Prog *p)
+{
+	Progs *g;
+
+	n->group = p->group;
+	if((g = n->group) != nil){
+		n->grpnext = nil;
+		if(g->head != nil){
+			n->grpprev = g->tail;
+			g->tail->grpnext = n;
+		}else{
+			n->grpprev = nil;
+			g->head = n;
+		}
+		g->tail = n;
+	}
+}
+
+static Progs*
+delgrp(Prog *p)
+{
+	Progs *g, *pg, *cg, **l;
+
+	g = p->group;
+	if(g == nil)
+		return nil;
+	if(p->grpprev)
+		p->grpprev->grpnext = p->grpnext;
+	else
+		g->head = p->grpnext;
+	if(p->grpnext)
+		p->grpnext->grpprev = p->grpprev;
+	else
+		g->tail = p->grpprev;
+	p->grpprev = p->grpnext = nil;
+	p->group = nil;
+
+	if(g->head == nil){
+		/* move up, giving subgroups of groups with no Progs to their parents */
+		do{
+			if((pg = g->parent) != nil){
+				pg = g->parent;
+				for(l = &pg->child; *l != nil && *l != g; l = &(*l)->sib)
+					;
+				*l = g->sib;
+			}
+			/* put subgroups in new parent group */
+			while((cg = g->child) != nil){
+				g->child = cg->sib;
+				cg->parent = pg;
+				if(pg != nil){
+					cg->sib = pg->child;
+					pg->child = cg;
+				}
+			}
+			free(g);
+		}while((g = pg) != nil && g->head == nil);
+	}
+	return g;
+}
+
+void
+printgrp(Prog *p, char *v)
+{
+	Progs *g;
+	Prog *q;
+
+	g = p->group;
+	print("%s pid %d grp %d pgrp %d: [pid", v, p->pid, g->id, g->parent!=nil?g->parent->id:0);
+	for(q = g->head; q != nil; q = q->grpnext)
+		print(" %d", q->pid);
+	print(" subgrp");
+	for(g = g->child; g != nil; g = g->sib)
+		print(" %d", g->id);
+	print("]\n");
+}
+
+int
+killgrp(Prog *p, char *msg)
+{
+	int i, npid, *pids;
+	Prog *f;
+	Progs *g;
+
+	/* interpreter has been acquired */
+	g = p->group;
+	if(g == nil || g->head == nil)
+		return 0;
+	while(g->flags & Pkilled){
+		release();
+		acquire();
+	}
+	npid = 0;
+	for(f = g->head; f != nil; f = f->grpnext)
+		if(f->group != g)
+			panic("killgrp");
+		else
+			npid++;
+	/* use pids not Prog* because state can change during killprog (eg, in delprog) */
+	pids = malloc(npid*sizeof(int));
+	if(pids == nil)
+		error(Enomem);
+	npid = 0;
+	for(f = g->head; f != nil; f = f->grpnext)
+		pids[npid++] = f->pid;
+	g->flags |= Pkilled;
+	if(waserror()) {
+		g->flags &= ~Pkilled;
+		free(pids);
+		nexterror();
+	}
+	for(i = 0; i < npid; i++) {
+		f = progpid(pids[i]);
+		if(f != nil && f != currun())
+			killprog(f, msg);
+	}
+	poperror();
+	g->flags &= ~Pkilled;
+	free(pids);
+	return 1;
+}
+
+char	changup[] = "channel hangup";
+
+void
+killcomm(Progq **q)
+{
+	Prog *p;
+	Progq *f;
+
+	for (f = *q; f != nil; f = *q) {
+		*q = f->next;
+		p = f->prog;
+		free(f);
+		if(p == nil)
+			return;
+		p->ptr = nil;
+		switch(p->state) {
+		case Prelease:
+			swiprog(p);
+			break;
+		case Psend:
+		case Precv:
+			p->kill = changup;
+			addrun(p);
+			break;
+		case Palt:
+			altgone(p);
+			break;
+		}
+	}
+}
+
+void
+addprog(Proc *p)
+{
+	Prog *n;
+
+	if((n = p->prog) == nil) {
+		n = malloc(sizeof(Prog));
+		if(n == nil)
+			panic("no memory");
+		p->prog = n;
+	} else
+		memset(n, 0, sizeof(Prog));
+	n->osenv = p->env;
+}
+
+static void
+cwakeme(Prog *p)
+{
+	Osenv *o;
+
+	p->addrun = nil;
+	o = p->osenv;
+	wakeup(o->rend);
+}
+
+static int
+cdone(void *vp)
+{
+	Prog *p = vp;
+
+	return p->addrun == nil || p->kill != nil;
+}
+
+void
+cblock(Prog *p)
+{
+	Osenv *o;
+
+	p->addrun = cwakeme;
+	o = p->osenv;
+	o->rend = &up->sleep;
+	release();
+
+	/*
+	 * To allow cdone(p) safely after release,
+	 * p must be currun before the release.
+	 * Exits in the error case with the vm acquired.
+	 */
+	if(waserror()) {
+		acquire();
+		p->addrun = nil;
+		nexterror();
+	}
+	sleep(o->rend, cdone, p);
+	if (p->kill != nil)
+		error(Eintr);
+	poperror();
+	acquire();
+}
+
+void
+addrun(Prog *p)
+{
+	if(p->addrun != 0) {
+		p->addrun(p);
+		return;
+	}
+	if(p->state == Pready && p != (Prog *)up->prog)
+		panic("addrun of ready prog %8.8p by %8.8lux\n", p, getcallerpc(&p));
+	p->state = Pready;
+	p->link = nil;
+	if(isched.runhd == nil)
+		isched.runhd = p;
+	else
+		isched.runtl->link = p;
+
+	isched.runtl = p;
+}
+
+Prog*
+delrun(int state)
+{
+	Prog *p;
+
+	p = isched.runhd;
+	p->state = state;
+	isched.runhd = p->link;
+	if(p->link == nil)
+		isched.runtl = nil;
+
+	return p;
+}
+
+void
+delrunq(Prog *p)
+{
+	Prog *prev, *f;
+
+	prev = nil;
+	for(f = isched.runhd; f; f = f->link) {
+		if(f == p)
+			break;
+		prev = f;
+	}
+	if(f == nil)
+		return;
+	if(prev == nil)
+		isched.runhd = p->link;
+	else
+		prev->link = p->link;
+	if(p == isched.runtl)
+		isched.runtl = prev;
+}
+
+Prog*
+delruntail(int state)
+{
+	Prog *p;
+
+	p = isched.runtl;
+	delrunq(isched.runtl);
+	p->state = state;
+	return p;
+}
+
+Prog*
+currun(void)
+{
+	return isched.runhd;
+}
+
+Prog*
+schedmod(Module *m)
+{
+	Heap *h;
+	Type *t;
+	Prog *p;
+	Modlink *ml;
+	Frame f, *fp;
+
+	ml = mklinkmod(m, 0);
+
+	if(m->origmp != H && m->ntype > 0) {
+		t = m->type[0];
+		h = nheap(t->size);
+		h->t = t;
+		t->ref++;
+		ml->MP = H2D(uchar*, h);
+		newmp(ml->MP, m->origmp, t);
+	}
+
+	p = newprog(nil, ml);
+	h = D2H(ml);
+	h->ref--;
+	p->R.PC = m->entry;
+	fp = &f;
+	R.s = &fp;
+	f.t = m->entryt;
+	newstack(p);
+	initmem(m->entryt, p->R.FP);
+
+	return p;
+}
+
+void
+acquire(void)
+{
+	int empty;
+	Prog *p;
+
+	lock(&isched.l);
+	if(isched.idle) {
+		isched.idle = 0;
+		unlock(&isched.l);
+	}
+	else {
+		up->qnext = nil;
+		if(isched.vmq != nil){
+			empty = 0;
+			isched.vmqt->qnext = up;
+		}else{
+			isched.vmq = up;
+			empty = 1;
+		}
+		isched.vmqt = up;
+
+		up->state = Queueing;
+		up->pc = getcallerpc(&empty);
+		unlock(&isched.l);
+		if(empty)
+			wakeup(&isched.irend);
+		sched();
+	}
+
+	if(up->type == Interp) {
+		p = up->iprog;
+		up->iprog = nil;
+		irestore(p);
+	}
+	else
+		p = up->prog;
+
+	p->state = Pready;
+	p->link = isched.runhd;
+	isched.runhd = p;
+	if(p->link == nil)
+		isched.runtl = p;
+}
+
+void
+release(void)
+{
+	Proc *p, **pq;
+	int f;
+
+	if(up->type == Interp){
+		if(up->iprog != nil)
+			panic("Double release (Interp)?");
+		up->iprog = isave();
+	}else{
+		if(((Prog *)up->prog)->state != Pready) panic("double release (GC)?");
+		delrun(Prelease);
+	}
+
+	lock(&isched.l);
+	if(*(pq = &isched.vmq) == nil && *(pq = &isched.idlevmq) == nil) {
+		isched.idle = 1;
+		f = isched.creating;
+		isched.creating = 1;
+		unlock(&isched.l);
+		if(f == 0)
+			kproc("dis", vmachine, nil, 0);
+		return;
+	}
+	p = *pq;
+	*pq = p->qnext;
+	unlock(&isched.l);
+
+	ready(p);
+}
+
+void
+iyield(void)
+{
+	Proc *p;
+
+	lock(&isched.l);
+	p = isched.vmq;
+	if(p == nil) {
+		unlock(&isched.l);
+		return;
+	}
+	isched.nyield++;
+	isched.vmq = p->qnext;
+
+	if(up->iprog != nil)
+		panic("iyield but iprog, type %d", up->type);
+	if(up->type != Interp){
+		static int once;
+		if(!once++)
+			print("tell charles: #%p->type==%d\n", up, up->type);
+	}
+	up->qnext = isched.idlevmq;
+	isched.idlevmq = up;
+
+	up->state = Queueing;
+	up->pc = getcallerpc(&p);
+	unlock(&isched.l);
+	ready(p);
+	sched();
+}
+
+void
+startup(void)
+{
+	int x;
+
+	up->type = Interp;
+	up->iprog = nil;
+
+	lock(&isched.l);
+	isched.creating = 0;
+	if(isched.idle) {
+		isched.idle = 0;
+		unlock(&isched.l);
+		return;
+	}
+	up->qnext = isched.idlevmq;
+	isched.idlevmq = up;
+	up->state = Queueing;
+	up->pc = getcallerpc(&x);
+	unlock(&isched.l);
+	sched();
+}
+
+void
+progexit(void)
+{
+	Prog *r;
+	Module *m;
+	int broken;
+	char *estr, msg[ERRMAX+2*KNAMELEN];
+
+	estr = up->env->errstr;
+	broken = 0;
+	if(estr[0] != '\0' && strcmp(estr, Eintr) != 0 && strncmp(estr, "fail:", 5) != 0)
+		broken = 1;
+
+	r = up->iprog;
+	if(r != nil)
+		acquire();
+	else
+		r = currun();
+
+	if(*estr == '\0' && r->flags & Pkilled)
+		estr = "killed";
+
+	m = R.M->m;
+	if(broken)
+		print("[%s] Broken: \"%s\"\n", m->name, estr);
+
+	snprint(msg, sizeof(msg), "%d \"%s\":%s", r->pid, m->name, estr);
+
+	if(up->env->debug != nil) {
+		dbgexit(r, broken, estr);
+		broken = 1;
+		/* must force it to break if in debug */
+	}else if(broken && (!keepbroken || strncmp(estr, "out of memory", 13)==0 || memusehigh()))
+		broken = 0;	/* don't want them or short of memory */
+
+	if(broken){
+		tellsomeone(r, msg);
+		r = isave();
+		r->state = Pbroken;
+		return;
+	}
+
+	gclock();
+	destroystack(&R);
+	delprog(r, msg);
+	gcunlock();
+}
+
+void
+disfault(void *reg, char *msg)
+{
+	Prog *p;
+
+	USED(reg);
+
+	if(strncmp(msg, Eintr, 6) == 0 || up == nil) {
+		print("EMU: faults: %s\n", msg);
+		panic("disfault");
+	}
+	if(up->type != Interp) {
+		print("SYS: process %s faults: %s\n", up->text, msg);
+		panic("disfault");
+	}
+
+	if(up->iprog != nil)
+		acquire();
+
+	p = currun();
+	if(p == nil)
+		panic("Interp faults with no dis prog");
+
+	/* cause an exception in the dis prog. */
+	error(msg);
+}
+
+void
+vmachine(void*)
+{
+	Prog *r;
+	Osenv *o;
+	int cycles;
+
+	startup();
+
+	while(waserror()) {
+		if(up->type != Interp)
+			panic("vmachine: non-interp kproc");
+		if(up->iprog != nil)
+			acquire();
+		if(handler(up->env->errstr) == 0) {
+			propex(currun(), up->env->errstr);
+			progexit();
+		}
+		up->env = &up->defenv;
+	}
+
+	cycles = 0;
+	for(;;) {
+		if(tready(nil) == 0) {
+			execatidle();
+			sleep(&isched.irend, tready, 0);
+		}
+
+		if(isched.vmq != nil && (isched.runhd == nil || ++cycles > 2)){
+			iyield();
+			cycles = 0;
+		}
+
+		r = isched.runhd;
+		if(r != nil) {
+			o = r->osenv;
+			up->env = o;
+
+			FPrestore(&o->fpu);
+			r->xec(r);
+			FPsave(&o->fpu);
+
+			if(isched.runhd != nil)
+			if(r == isched.runhd)
+			if(isched.runhd != isched.runtl) {
+				isched.runhd = r->link;
+				r->link = nil;
+				isched.runtl->link = r;
+				isched.runtl = r;
+			}
+			up->env = &up->defenv;
+			if(isched.runhd != nil)
+			if (up->iprog == nil) {
+				up->type = BusyGC;
+				pushrun(up->prog);
+				rungc(isched.head);
+				up->type = Interp;
+				delrunq(up->prog);
+			} else
+				print("up->iprog not nil (%lux)\n", up->iprog);
+		}
+	}
+}
+
+void
+disinit(void *a)
+{
+	Prog *p;
+	Osenv *o;
+	Module *root;
+	char *initmod = a;
+
+	if(waserror())
+		panic("disinit error: %r");
+
+	print("Initial Dis: \"%s\"\n", initmod);
+
+	fmtinstall('D', Dconv);
+
+	addclock0link(accountant, MS2HZ);
+
+	FPinit();
+	FPsave(&up->env->fpu);
+
+	opinit();
+	modinit();
+	excinit();
+
+	root = load(initmod);
+	if(root == 0) {
+		kgerrstr(up->genbuf, sizeof up->genbuf);
+		panic("loading \"%s\": %s", initmod, up->genbuf);
+	}
+
+	p = schedmod(root);
+
+	memmove(p->osenv, up->env, sizeof(Osenv));
+	o = p->osenv;
+	incref(o->pgrp);
+	incref(o->fgrp);
+	if(o->egrp != nil)
+		incref(o->egrp);
+	if(o->sigs != nil)
+		incref(o->sigs);
+	o->user = nil;
+	kstrdup(&o->user, up->env->user);
+	o->errstr = o->errbuf0;
+	o->syserrstr = o->errbuf1;
+
+	isched.idle = 1;
+
+	if(kopen("#c/cons", OREAD) != 0)
+		panic("failed to make fd0 from #c/cons");
+	kopen("#c/cons", OWRITE);
+	kopen("#c/cons", OWRITE);
+
+	poperror();
+	vmachine(nil);
+}
+
+void
+pushrun(Prog *p)
+{
+	if(p->addrun != nil)
+		panic("pushrun addrun");
+	p->state = Pready;
+	p->link = isched.runhd;
+	isched.runhd = p;
+	if(p->link == nil)
+		isched.runtl = p;
+}
--- /dev/null
+++ b/os/port/discall.c
@@ -1,0 +1,185 @@
+#include "u.h"
+#include "../port/lib.h"
+#include "mem.h"
+#include "dat.h"
+#include "fns.h"
+#include "../port/error.h"
+#include <isa.h>
+#include <interp.h>
+#include "kernel.h"
+
+#define	QP(l)	(Prog**)((char*)(l)+sizeof(QLock))
+
+void*
+libqlowner(void *l)
+{
+	return *QP(l);
+}
+
+void
+libqlock(void *l)
+{
+	Prog *p;
+	QLock *q;
+
+	q = l;
+	p = currun();
+	if(p == nil)
+		panic("libqlock");
+
+	if(!canqlock(q)) {
+		release();
+		qlock(q);
+		acquire();
+	}
+	*QP(l) = p;
+}
+
+void
+libqunlock(void *l)
+{
+	Prog *p;
+	QLock *q;
+
+	q = l;
+	p = currun();
+	if(p == nil)
+		panic("libqunlock 1");
+	if(*QP(l) != p)
+		panic("libqunlock 2");
+
+	*QP(l) = nil;
+	qunlock(q);
+}
+
+void*
+libqlalloc(void)
+{
+	QLock *q;
+
+	q = mallocz(sizeof(QLock)+sizeof(Prog*), 1);
+	return q;
+}
+
+void
+libqlfree(void *l)
+{
+	free(l);
+}
+
+vlong
+libseek(int fd, vlong off, int whence)
+{
+	release();
+	off = kseek(fd, off, whence);
+	acquire();
+	return off;
+}
+
+int
+libread(int fd, void *buf, int n)
+{
+	release();
+	n = kread(fd, buf, n);
+	acquire();
+	return n;
+}
+
+int
+libreadn(int fd, void *av, long n)
+{
+	char *a;
+	long m, t;
+
+	a = av;
+	t = 0;
+	release();
+	while(t < n){
+		m = kread(fd, a+t, n-t);
+		if(m <= 0){
+			if(t == 0){
+				acquire();
+				return m;
+			}
+			break;
+		}
+		t += m;
+	}
+	acquire();
+	return t;
+}
+
+int
+libwrite(int fd, void *buf, int n)
+{
+	release();
+	n = kwrite(fd, buf, n);
+	acquire();
+	return n;
+}
+
+int
+libopen(char *name, int omode)
+{
+	int fd;
+
+	release();
+	fd = kopen(name, omode);
+	acquire();
+	return fd;
+}
+
+int
+libclose(int fd)
+{
+	release();
+	fd = kclose(fd);
+	acquire();
+	return fd;
+}
+
+Dir*
+libdirfstat(int fd)
+{
+	Dir *d;
+
+	release();
+	d = kdirfstat(fd);
+	acquire();
+	return d;
+}
+
+int
+libbind(char *s, char *t, int f)
+{
+	int n;
+
+	release();
+	n = kbind(s, t, f);
+	acquire();
+	return n;
+}
+
+void
+libchanclose(void *chan)
+{
+	release();
+	cclose(chan);
+	acquire();
+}
+
+void*
+libfdtochan(int fd, int mode)
+{
+	Chan *c;
+
+	release();
+	if(waserror()) {
+		acquire();
+		return nil;
+	}
+	c = fdtochan(up->env->fgrp, fd, mode, 0, 1);
+	poperror();
+	acquire();
+	return c;
+}
--- /dev/null
+++ b/os/port/dynld.c
@@ -1,0 +1,75 @@
+#include "u.h"
+#include "../port/lib.h"
+#include "../port/error.h"
+#include "mem.h"
+#include	"dat.h"
+#include	"fns.h"
+#include	<a.out.h>
+#include	<dynld.h>
+#include	<kernel.h>
+
+/*
+ * kernel interface to dynld, for use by devdynld.c,
+ * libinterp/dlm.c, and possibly others
+ */
+
+typedef struct Fd Fd;
+struct Fd {
+	int	fd;
+};
+
+static long
+readfd(void *a, void *buf, long nbytes)
+{
+	return kread(((Fd*)a)->fd, buf, nbytes);
+}
+
+static vlong
+seekfd(void *a, vlong off, int t)
+{
+	return kseek(((Fd*)a)->fd, off, t);
+}
+
+static void
+errfd(char *s)
+{
+	kstrcpy(up->env->errstr, s, ERRMAX);
+}
+
+Dynobj*
+kdynloadfd(int fd, Dynsym *tab, int ntab)
+{
+	Fd f;
+
+	f.fd = fd;
+	return dynloadgen(&f, readfd, seekfd, errfd, tab, ntab, 0);
+}
+
+int
+kdynloadable(int fd)
+{
+	Fd f;
+
+	f.fd = fd;
+	return dynloadable(&f, readfd, seekfd);
+}
+
+/* auxiliary routines for dynamic loading of C modules */
+
+Dynobj*
+dynld(int fd)
+{
+	Fd f;
+	
+	f.fd = fd;
+	return dynloadgen(&f, readfd, seekfd, errfd, _exporttab, dyntabsize(_exporttab), 0);
+}
+
+int
+dynldable(int fd)
+{
+	Fd f;
+
+	f.fd = fd;
+	return dynloadable(&f, readfd, seekfd);
+}
--- /dev/null
+++ b/os/port/edf.c
@@ -1,0 +1,626 @@
+/* EDF scheduling */
+#include	"u.h"
+#include	"../port/lib.h"
+#include	"mem.h"
+#include	"dat.h"
+#include	"fns.h"
+#include	"../port/error.h"
+#include	"../port/edf.h"
+#include	<trace.h>
+
+/* debugging */
+int	edfprint = 0;
+#define DPRINT	if(edfprint)print
+
+static vlong	now;
+extern ulong	delayedscheds;
+extern Schedq	runq[Nrq];
+extern int	nrdy;
+extern ulong	runvec;
+
+/* Statistics stuff */
+ulong		nilcount;
+ulong		scheds;
+vlong		edfruntime;
+ulong		edfnrun;
+int		misseddeadlines;
+
+/* Edfschedlock protects modification of admission params */
+int		edfinited;
+QLock		edfschedlock;
+static Lock	thelock;
+
+enum{
+	Dl,	/* Invariant for schedulability test: Dl < Rl */
+	Rl,
+};
+
+static char *testschedulability(Proc*);
+static Proc *qschedulability;
+
+enum {
+	Onemicrosecond =	1000ULL,
+	Onemillisecond =	1000000ULL,
+	Onesecond =		1000000000ULL,
+	OneRound = 		Onemillisecond/2LL,
+	MilliRound = 		Onemicrosecond/2LL,
+};
+
+static int
+timeconv(Fmt *f)
+{
+	char buf[128], *sign;
+	vlong t;
+
+	buf[0] = 0;
+	switch(f->r) {
+	case 'U':
+		t = va_arg(f->args, uvlong);
+		break;
+	case 't':	// vlong in nanoseconds
+		t = va_arg(f->args, vlong);
+		break;
+	default:
+		return fmtstrcpy(f, "(timeconv)");
+	}
+	if (t < 0) {
+		sign = "-";
+		t = -t;
+	}
+	else
+		sign = "";
+	if (t > Onesecond){
+		t += OneRound;
+		sprint(buf, "%s%d.%.3ds", sign, (int)(t / Onesecond), (int)(t % Onesecond)/1000000);
+	}else if (t > Onemillisecond){
+		t += MilliRound;
+		sprint(buf, "%s%d.%.3dms", sign, (int)(t / Onemillisecond), (int)(t % Onemillisecond)/1000);
+	}else if (t > Onemicrosecond)
+		sprint(buf, "%s%d.%.3dµs", sign, (int)(t / Onemicrosecond), (int)(t % Onemicrosecond));
+	else
+		sprint(buf, "%s%dns", sign, (int)t);
+	return fmtstrcpy(f, buf);
+}
+
+Edf*
+edflock(Proc *p)
+{
+	Edf *e;
+
+	if (p->edf == nil)
+		return nil;
+	ilock(&thelock);
+	if ((e = p->edf) && (e->flags & Admitted)){
+		now = todget(nil);
+		return e;
+	}
+	iunlock(&thelock);
+	return nil;
+}
+
+void
+edfunlock(void)
+{
+	edfruntime += todget(nil) - now;
+	edfnrun++;
+	iunlock(&thelock);
+}
+
+void
+edfinit(Proc*p)
+{
+	if(!edfinited){
+		fmtinstall('t', timeconv);
+		edfinited++;
+	}
+	now = todget(nil);
+	DPRINT("%t edfinit %lud[%s]\n", now, p->pid, statename[p->state]);
+	p->edf = malloc(sizeof(Edf));
+	if(p->edf == nil)
+		error(Enomem);
+	return;
+}
+
+static void
+deadlineintr(Ureg*, Timer *t)
+{
+	/* Proc reached deadline */
+	extern int panicking;
+	Proc *p;
+	void (*pt)(Proc*, int, vlong);
+
+	if(panicking || active.exiting)
+		return;
+
+	p = t->ta;
+
+	DPRINT("%t deadlineintr %lud[%s]\n", todget(nil), p->pid, statename[p->state]);
+	/* If we're interrupting something other than the proc pointed to by t->a,
+	 * we've already achieved recheduling, so we need not do anything
+	 * Otherwise, we must cause a reschedule, but if we call sched()
+	 * here directly, the timer interrupt routine will not finish its business
+	 * Instead, we cause the resched to happen when the interrupted proc
+	 * returns to user space
+	 */
+	if (p == up){
+		pt = proctrace;
+		if(up->trace && pt)
+			pt(up, SInts, 0);
+		up->delaysched++;
+ 		delayedscheds++;
+	}
+}
+
+static void
+release(Proc *p)
+{
+	/* Called with edflock held */
+	Edf *e;
+	void (*pt)(Proc*, int, vlong);
+
+	e = p->edf;
+	e->flags &= ~Yield;
+	if (e->d < now){
+		e->periods++;
+		e->r = now;
+		if ((e->flags & Sporadic) == 0){
+			/* Non sporadic processes stay true to their period;
+			 * calculate next release time
+			 */
+			while(e->t <= now)
+				e->t += e->T;
+		}else{
+			/* Sporadic processes may not be released earlier than
+			 * one period after this release
+			 */
+			e->t = e->r + e->T;
+		}
+		e->d = e->r + e->D;
+		e->S = e->C;
+		DPRINT("%t release %lud[%s], r=%t, d=%t, t=%t, S=%t\n",
+			now, p->pid, statename[p->state], e->r, e->d, e->t, e->S);
+		if (pt = proctrace){
+			pt(p, SRelease, e->r);
+			pt(p, SDeadline, e->d);
+		}
+	}else{
+		DPRINT("%t release %lud[%s], too late t=%t, called from 0x%lux\n",
+			now, p->pid, statename[p->state], e->t, getcallerpc(&p));
+	}
+}
+
+static void
+releaseintr(Ureg*, Timer *t)
+{
+	Proc *p;
+	extern int panicking;
+	Schedq *rq;
+
+	if(panicking || active.exiting)
+		return;
+
+	p = t->ta;
+	if((edflock(p)) == nil)
+		return;
+	DPRINT("%t releaseintr %lud[%s]\n", now, p->pid, statename[p->state]);
+	switch(p->state){
+	default:
+		edfunlock();
+		return;
+	case Ready:
+		/* remove proc from current runq */
+		rq = &runq[p->pri];
+		if (dequeueproc(rq, p) != p){
+			DPRINT("releaseintr: can't find proc or lock race\n");
+			release(p);	/* It'll start best effort */
+			edfunlock();
+			return;
+		}
+		p->state = Waitrelease;
+		/* fall through */
+	case Waitrelease:
+		release(p);
+		edfunlock();
+		ready(p);
+		if (up){
+			up->delaysched++;
+			delayedscheds++;
+		}
+		return;
+	case Running:
+		release(p);
+		edfrun(p, 1);
+		break;
+	case Wakeme:
+		release(p);
+		edfunlock();
+		if (p->trend)
+			wakeup(p->trend);
+		p->trend = nil;
+		if (up){
+			up->delaysched++;
+			delayedscheds++;
+		}
+		return;
+	}
+	edfunlock();
+}
+
+void
+edfrecord(Proc *p)
+{
+	vlong used;
+	Edf *e;
+	void (*pt)(Proc*, int, vlong);
+
+	if((e = edflock(p)) == nil)
+		return;
+	used = now - e->s;
+	if (e->d <= now)
+		e->edfused += used;
+	else
+		e->extraused += used;
+	if (e->S > 0){
+		if (e->S <= used){
+			if(pt = proctrace)
+				pt(p, SSlice, now);
+			DPRINT("%t edfrecord slice used up\n", now);
+			e->d = now;
+			e->S = 0;
+		}else
+			e->S -= used;
+	}
+	e->s = now;
+	edfunlock();
+}
+
+void
+edfrun(Proc *p, int edfpri)
+{
+	Edf *e;
+	void (*pt)(Proc*, int, vlong);
+
+	e = p->edf;
+	/* Called with edflock held */
+	if(edfpri){
+		if (e->d <= now || e->S == 0){
+			/* Deadline reached or resources exhausted,
+			 * deschedule forthwith
+			 */
+			p->delaysched++;
+ 			delayedscheds++;
+			e->s = now;
+			return;
+		}
+		e->tns = now + e->S;
+		if (e->d < e->tns)
+			e->tns = e->d;
+		if(e->tt == nil || e->tf != deadlineintr){
+			DPRINT("%t edfrun, deadline=%t\n", now, e->tns);
+		}else{
+			DPRINT("v");
+		}
+		if(p->trace && (pt = proctrace))
+			pt(p, SInte, e->tns);
+		e->tmode = Tabsolute;
+		e->tf = deadlineintr;
+		e->ta = p;
+		timeradd(e);
+	}else{
+		DPRINT("<");
+	}
+	e->s = now;
+}
+
+char *
+edfadmit(Proc *p)
+{
+	char *err;
+	Edf *e;
+	int i;
+	Proc *r;
+	void (*pt)(Proc*, int, vlong);
+
+	e = p->edf;
+	if (e->flags & Admitted)
+		return "task state";	/* should never happen */
+
+	/* simple sanity checks */
+	if (e->T == 0)
+		return "T not set";
+	if (e->C == 0)
+		return "C not set";
+	if (e->D > e->T)
+		return "D > T";
+	if (e->D == 0)	/* if D is not set, set it to T */
+		e->D = e->T;
+	if (e->C > e->D)
+		return "C > D";
+
+	qlock(&edfschedlock);
+	if (err = testschedulability(p)){
+		qunlock(&edfschedlock);
+		return err;
+	}
+	e->flags |= Admitted;
+
+	edflock(p);
+
+	if(pt = proctrace)
+		pt(p, SAdmit, now);
+
+	/* Look for another proc with the same period to synchronize to */
+	SET(r);
+	for(i=0; i<conf.nproc; i++) {
+		r = proctab(i);
+		if(r->state == Dead || r == p)
+			continue;
+		if (r->edf == nil || (r->edf->flags & Admitted) == 0)
+			continue;
+		if (r->edf->T == e->T)
+				break;
+	}
+	if (i == conf.nproc){
+		/* Can't synchronize to another proc, release now */
+		e->t = now;
+		e->d = 0;
+		release(p);
+		if (p == up){
+			DPRINT("%t edfadmit self %lud[%s], release now: r=%t d=%t t=%t\n",
+				now, p->pid, statename[p->state], e->r, e->d, e->t);
+			/* We're already running */
+			edfrun(p, 1);
+		}else{
+			/* We're releasing another proc */
+			DPRINT("%t edfadmit other %lud[%s], release now: r=%t d=%t t=%t\n",
+				now, p->pid, statename[p->state], e->r, e->d, e->t);
+			p->ta = p;
+			edfunlock();
+			qunlock(&edfschedlock);
+			releaseintr(nil, p);
+			return nil;
+		}
+	}else{
+		/* Release in synch to something else */
+		e->t = r->edf->t;
+		if (p == up){
+			DPRINT("%t edfadmit self %lud[%s], release at %t\n",
+				now, p->pid, statename[p->state], e->t);
+			edfunlock();
+			qunlock(&edfschedlock);
+			return nil;
+		}else{
+			DPRINT("%t edfadmit other %lud[%s], release at %t\n",
+				now, p->pid, statename[p->state], e->t);
+			if(e->tt == nil){
+				e->tf = releaseintr;
+				e->ta = p;
+				e->tns = e->t;
+				e->tmode = Tabsolute;
+				timeradd(e);
+			}
+		}
+	}
+	edfunlock();
+	qunlock(&edfschedlock);
+	return nil;
+}
+
+void
+edfstop(Proc *p)
+{
+	Edf *e;
+	void (*pt)(Proc*, int, vlong);
+
+	if (e = edflock(p)){
+		DPRINT("%t edfstop %lud[%s]\n", now, p->pid, statename[p->state]);
+		if(pt = proctrace)
+			pt(p, SExpel, now);
+		e->flags &= ~Admitted;
+		if (e->tt)
+			timerdel(e);
+		edfunlock();
+	}
+}
+
+static int
+yfn(void *)
+{
+	return up->trend == nil || todget(nil) >= up->edf->r;
+}
+
+void
+edfyield(void)
+{
+	/* sleep until next release */
+	Edf *e;
+	void (*pt)(Proc*, int, vlong);
+
+	if((e = edflock(up)) == nil)
+		return;
+	if(pt = proctrace)
+		pt(up, SYield, now);
+	while(e->t < now)
+		e->t += e->T;
+	e->r = e->t;
+	e->flags |= Yield;
+	e->d = now;
+	if (up->tt == nil){
+		up->tns = e->t;
+		up->tf = releaseintr;
+		up->tmode = Tabsolute;
+		up->ta = up;
+		up->trend = &up->sleep;
+		timeradd(up);
+	}else if(up->tf != releaseintr)
+		print("edfyield: surprise! 0x%lux\n", up->tf);
+	edfunlock();
+	sleep(&up->sleep, yfn, nil);
+}
+
+int
+edfready(Proc *p)
+{
+	Edf *e;
+	Schedq *rq;
+	Proc *l, *pp;
+	void (*pt)(Proc*, int, vlong);
+
+	if((e = edflock(p)) == nil)
+		return 0;
+	if (e->d <= now){
+		/* past deadline, arrange for next release */
+		if ((e->flags & Sporadic) == 0){
+			/* Non sporadic processes stay true to their period, calculate next release time */
+			while(e->t < now)
+				e->t += e->T;
+		}	
+		if (now < e->t){
+			/* Next release is in the future, schedule it */
+			if (e->tt == nil || e->tf != releaseintr){
+				e->tns = e->t;
+				e->tmode = Tabsolute;
+				e->tf = releaseintr;
+				e->ta = p;
+				timeradd(e);
+				DPRINT("%t edfready %lud[%s], release=%t\n",
+					now, p->pid, statename[p->state], e->t);
+			}
+			if(p->state == Running && (e->flags & (Yield|Yieldonblock)) == 0 && (e->flags & Extratime)){
+				/* If we were running, we've overrun our CPU allocation
+				 * or missed the deadline, continue running best-effort at low priority
+				 * Otherwise we were blocked.  If we don't yield on block, we continue
+				 * best effort
+				 */
+				DPRINT(">");
+				p->pri = PriExtra;
+				edfunlock();
+				return 0;	/* Stick on runq[PriExtra] */
+			}
+			DPRINT("%t edfready %lud[%s] wait release at %t\n",
+				now, p->pid, statename[p->state], e->t);
+			p->state = Waitrelease;
+			edfunlock();
+			return 1;	/* Make runnable later */
+		}
+		DPRINT("%t edfready %lud %s release now\n", now, p->pid, statename[p->state]);
+		/* release now */
+		release(p);
+	}
+	edfunlock();
+	DPRINT("^");
+	rq = &runq[PriEdf];
+	/* insert in queue in earliest deadline order */
+	lock(runq);
+	l = nil;
+	for(pp = rq->head; pp; pp = pp->rnext){
+		if(pp->edf->d > e->d)
+			break;
+		l = pp;
+	}
+	p->rnext = pp;
+	if (l == nil)
+		rq->head = p;
+	else
+		l->rnext = p;
+	if(pp == nil)
+		rq->tail = p;
+	rq->n++;
+	nrdy++;
+	runvec |= 1 << PriEdf;
+	p->pri = PriEdf;
+	p->readytime = m->ticks;
+	p->state = Ready;
+	unlock(runq);
+	if(pt = proctrace)
+		pt(p, SReady, now);
+	return 1;
+}
+
+
+static void
+testenq(Proc *p)
+{
+	Proc *xp, **xpp;
+	Edf *e;
+
+	e = p->edf;
+	e->testnext = nil;
+	if (qschedulability == nil) {
+		qschedulability = p;
+		return;
+	}
+	SET(xp);
+	for (xpp = &qschedulability; *xpp; xpp = &xp->edf->testnext) {
+		xp = *xpp;
+		if (e->testtime < xp->edf->testtime
+		|| (e->testtime == xp->edf->testtime && e->testtype < xp->edf->testtype)){
+			e->testnext = xp;
+			*xpp = p;
+			return;
+		}
+	}
+	assert(xp->edf->testnext == nil);
+	xp->edf->testnext = p;
+}
+
+static char *
+testschedulability(Proc *theproc)
+{
+	Proc *p;
+	vlong H, G, Cb, ticks;
+	int steps, i;
+
+	/* initialize */
+	DPRINT("schedulability test %lud\n", theproc->pid);
+	qschedulability = nil;
+	for(i=0; i<conf.nproc; i++) {
+		p = proctab(i);
+		if(p->state == Dead)
+			continue;
+		if ((p->edf == nil || (p->edf->flags & Admitted) == 0) && p != theproc)
+			continue;
+		p->edf->testtype = Rl;
+		p->edf->testtime = 0;
+		DPRINT("\tInit: edfenqueue %lud\n", p->pid);
+		testenq(p);
+	}
+	H=0;
+	G=0;
+	for(steps = 0; steps < Maxsteps; steps++){
+		p = qschedulability;
+		qschedulability = p->edf->testnext;
+		ticks = p->edf->testtime;
+		switch (p->edf->testtype){
+		case Dl:
+			H += p->edf->C;
+			Cb = 0;
+			DPRINT("\tStep %3d, Ticks %t, pid %lud, deadline, H += %t → %t, Cb = %t\n",
+				steps, ticks, p->pid, p->edf->C, H, Cb);
+			if (H+Cb>ticks){
+				DPRINT("not schedulable\n");
+				return "not schedulable";
+			}
+			p->edf->testtime += p->edf->T - p->edf->D;
+			p->edf->testtype = Rl;
+			testenq(p);
+			break;
+		case Rl:
+			DPRINT("\tStep %3d, Ticks %t, pid %lud, release, G  %t, C%t\n",
+				steps, ticks, p->pid, p->edf->C, G);
+			if(ticks && G <= ticks){
+				DPRINT("schedulable\n");
+				return nil;
+			}
+			G += p->edf->C;
+			p->edf->testtime += p->edf->D;
+			p->edf->testtype = Dl;
+			testenq(p);
+			break;
+		default:
+			assert(0);
+		}
+	}
+	DPRINT("probably not schedulable\n");
+	return "probably not schedulable";
+}
--- /dev/null
+++ b/os/port/edf.h
@@ -1,0 +1,53 @@
+enum {
+	Maxsteps = 200 * 100 * 2,	/* 100 periods of 200 procs */
+
+	/* Edf.flags field */
+	Admitted		= 0x01,
+	Sporadic		= 0x02,
+	Yieldonblock		= 0x04,
+	Sendnotes		= 0x08,
+	Deadline		= 0x10,
+	Yield			= 0x20,
+	Extratime		= 0x40,
+
+	Infinity = ~0ULL,
+};
+
+typedef struct Edf		Edf;
+
+struct Edf {
+	/* time intervals */
+	vlong		D;			/* Deadline */
+	vlong		Delta;		/* Inherited deadline */
+	vlong		T;			/* period */
+	vlong		C;			/* Cost */
+	vlong		S;			/* Slice: time remaining in this period */
+	/* times */
+	vlong		r;			/* (this) release time */
+	vlong		d;			/* (this) deadline */
+	vlong		t;			/* Start of next period, t += T at release */
+	vlong		s;			/* Time at which this proc was last scheduled */
+	/* for schedulability testing */
+	vlong		testDelta;
+	int			testtype;	/* Release or Deadline */
+	vlong		testtime;
+	Proc		*testnext;
+	/* other */
+	ushort		flags;
+	Timer;
+	/* Stats */
+	vlong		edfused;
+	vlong		extraused;
+	vlong		aged;
+	ulong		periods;
+	ulong		missed;
+};
+
+extern Lock	edftestlock;	/* for atomic admitting/expelling */
+
+#pragma	varargck	type	"t"		vlong
+#pragma	varargck	type	"U"		uvlong
+
+/* Interface: */
+Edf*		edflock(Proc*);
+void		edfunlock(void);
--- /dev/null
+++ b/os/port/error.h
@@ -1,0 +1,61 @@
+extern char Enoerror[];		/* no error */
+extern char Emount[];		/* inconsistent mount */
+extern char Eunmount[];		/* not mounted */
+extern char Eunion[];		/* not in union */
+extern char Emountrpc[];	/* mount rpc error */
+extern char Eshutdown[];	/* mounted device shut down */
+extern char Enocreate[];	/* mounted directory forbids creation */
+extern char Enonexist[];	/* file does not exist */
+extern char Eexist[];		/* file already exists */
+extern char Ebadsharp[];	/* unknown device in # filename */
+extern char Enotdir[];		/* not a directory */
+extern char Eisdir[];		/* file is a directory */
+extern char Ebadchar[];		/* bad character in file name */
+extern char Efilename[];	/* file name syntax */
+extern char Eperm[];		/* permission denied */
+extern char Ebadusefd[];	/* inappropriate use of fd */
+extern char Ebadarg[];		/* bad arg in system call */
+extern char Einuse[];		/* device or object already in use */
+extern char Eio[];		/* i/o error */
+extern char Etoobig[];		/* read or write too large */
+extern char Etoosmall[];	/* read or write too small */
+extern char Enetaddr[];		/* bad network address */
+extern char Emsgsize[];		/* message is too big for protocol */
+extern char Enetbusy[];		/* network device is busy or allocated */
+extern char Enoproto[];		/* network protocol not supported */
+extern char Enoport[];		/* network port not available */
+extern char Enoifc[];		/* bad interface or no free interface slots */
+extern char Enolisten[];	/* not announced */
+extern char Ehungup[];		/* i/o on hungup channel */
+extern char Ebadctl[];		/* bad process or channel control request */
+extern char Enodev[];		/* no free devices */
+extern char Enoenv[];		/* no free environment resources */
+extern char Ethread[];		/* thread exited */
+extern char Estopped[];		/* thread must be stopped */
+extern char Enochild[];		/* no living children */
+extern char Eioload[];		/* i/o error in demand load */
+extern char Enovmem[];		/* out of memory: virtual memory */
+extern char Ebadld[];		/* illegal line discipline */
+extern char Ebadfd[];		/* fd out of range or not open */
+extern char Eisstream[];	/* seek on a stream */
+extern char Ebadexec[];		/* exec header invalid */
+extern char Etimedout[];	/* connection timed out */
+extern char Econrefused[];	/* connection refused */
+extern char Econinuse[];	/* connection in use */
+extern char Eintr[];		/* interrupted */
+extern char Eneedservice[];	/* service required for tcp/udp/il calls */
+extern char Enomem[];		/* out of memory: kernel */
+extern char Esfnotcached[];	/* subfont not cached */
+extern char Esoverlap[];	/* segments overlap */
+extern char Emouseset[];	/* mouse type already set */
+extern char Erecover[];		/* failed to recover fd */
+extern char Eshort[];		/* i/o count too small */
+extern char Enobitstore[];	/* out of screen memory */
+extern char Egreg[];		/* jim'll fix it */
+extern char Ebadspec[];		/* bad attach specifier */
+extern char Enoattach[];	/* mount/attach disallowed */
+extern char Eshortstat[];	/* stat buffer too small */
+extern char Enegoff[];	/* negative i/o offset */
+extern char Ecmdargs[];		/* wrong #args in control message */
+extern char Ebadstat[];		/* malformed stat buffer */
+extern char	Enofd[];	/* no free file descriptors */
--- /dev/null
+++ b/os/port/ethermii.c
@@ -1,0 +1,238 @@
+#include "u.h"
+#include "../port/lib.h"
+#include "mem.h"
+#include "dat.h"
+#include "fns.h"
+#include "io.h"
+#include "../port/error.h"
+#include "../port/netif.h"
+
+#include "etherif.h"
+#include "ethermii.h"
+
+int
+mii(Mii* mii, int mask)
+{
+	MiiPhy *miiphy;
+	int bit, oui, phyno, r, rmask;
+
+	/*
+	 * Probe through mii for PHYs in mask;
+	 * return the mask of those found in the current probe.
+	 * If the PHY has not already been probed, update
+	 * the Mii information.
+	 */
+	rmask = 0;
+	for(phyno = 0; phyno < NMiiPhy; phyno++){
+		bit = 1<<phyno;
+		if(!(mask & bit))
+			continue;
+		if(mii->mask & bit){
+			rmask |= bit;
+			continue;
+		}
+		if(mii->mir(mii, phyno, Bmsr) == -1)
+			continue;
+		r = mii->mir(mii, phyno, Phyidr1);
+		oui = (r & 0x3FFF)<<6;
+		r = mii->mir(mii, phyno, Phyidr2);
+		oui |= r>>10;
+		if(oui == 0xFFFFF || oui == 0)
+			continue;
+
+		if((miiphy = malloc(sizeof(MiiPhy))) == nil)
+			continue;
+
+		miiphy->mii = mii;
+		miiphy->oui = oui;
+		miiphy->phyno = phyno;
+
+		miiphy->anar = ~0;
+		miiphy->fc = ~0;
+		miiphy->mscr = ~0;
+
+		mii->phy[phyno] = miiphy;
+		if(mii->curphy == nil)
+			mii->curphy = miiphy;
+		mii->mask |= bit;
+		mii->nphy++;
+
+		rmask |= bit;
+	}
+	return rmask;
+}
+
+int
+miimir(Mii* mii, int r)
+{
+	if(mii == nil || mii->ctlr == nil || mii->curphy == nil)
+		return -1;
+	return mii->mir(mii, mii->curphy->phyno, r);
+}
+
+int
+miimiw(Mii* mii, int r, int data)
+{
+	if(mii == nil || mii->ctlr == nil || mii->curphy == nil)
+		return -1;
+	return mii->miw(mii, mii->curphy->phyno, r, data);
+}
+
+int
+miireset(Mii* mii)
+{
+	int bmcr;
+
+	if(mii == nil || mii->ctlr == nil || mii->curphy == nil)
+		return -1;
+	bmcr = mii->mir(mii, mii->curphy->phyno, Bmcr);
+	bmcr |= BmcrR;
+	mii->miw(mii, mii->curphy->phyno, Bmcr, bmcr);
+/*	microdelay(1);*/
+	microdelay(500);	/* DP83847, at least */
+
+	return 0;
+}
+
+int
+miiane(Mii* mii, int a, int p, int e)
+{
+	int anar, bmsr, mscr, r, phyno;
+
+	if(mii == nil || mii->ctlr == nil || mii->curphy == nil)
+		return -1;
+	phyno = mii->curphy->phyno;
+
+	bmsr = mii->mir(mii, phyno, Bmsr);
+	if(!(bmsr & BmsrAna))
+		return -1;
+
+	if(a != ~0)
+		anar = (AnaTXFD|AnaTXHD|Ana10FD|Ana10HD) & a;
+	else if(mii->curphy->anar != ~0)
+		anar = mii->curphy->anar;
+	else{
+		anar = mii->mir(mii, phyno, Anar);
+		anar &= ~(AnaAP|AnaP|AnaT4|AnaTXFD|AnaTXHD|Ana10FD|Ana10HD);
+		if(bmsr & Bmsr10THD)
+			anar |= Ana10HD;
+		if(bmsr & Bmsr10TFD)
+			anar |= Ana10FD;
+		if(bmsr & Bmsr100TXHD)
+			anar |= AnaTXHD;
+		if(bmsr & Bmsr100TXFD)
+			anar |= AnaTXFD;
+	}
+	mii->curphy->anar = anar;
+
+	if(p != ~0)
+		anar |= (AnaAP|AnaP) & p;
+	else if(mii->curphy->fc != ~0)
+		anar |= mii->curphy->fc;
+	mii->curphy->fc = (AnaAP|AnaP) & anar;
+
+	if(bmsr & BmsrEs){
+		mscr = mii->mir(mii, phyno, Mscr);
+		mscr &= ~(Mscr1000TFD|Mscr1000THD);
+		if(e != ~0)
+			mscr |= (Mscr1000TFD|Mscr1000THD) & e;
+		else if(mii->curphy->mscr != ~0)
+			mscr = mii->curphy->mscr;
+		else{
+			r = mii->mir(mii, phyno, Esr);
+			if(r & Esr1000THD)
+				mscr |= Mscr1000THD;
+			if(r & Esr1000TFD)
+				mscr |= Mscr1000TFD;
+		}
+		mii->curphy->mscr = mscr;
+		mii->miw(mii, phyno, Mscr, mscr);
+	}
+	mii->miw(mii, phyno, Anar, anar);
+
+	r = mii->mir(mii, phyno, Bmcr);
+	if(!(r & BmcrR)){
+		r |= BmcrAne|BmcrRan;
+		mii->miw(mii, phyno, Bmcr, r);
+	}
+
+	return 0;
+}
+
+int
+miistatus(Mii* mii)
+{
+	MiiPhy *phy;
+	int anlpar, bmsr, p, r, phyno;
+
+	if(mii == nil || mii->ctlr == nil || mii->curphy == nil)
+		return -1;
+	phy = mii->curphy;
+	phyno = phy->phyno;
+
+	/*
+	 * Check Auto-Negotiation is complete and link is up.
+	 * (Read status twice as the Ls bit is sticky).
+	 */
+	bmsr = mii->mir(mii, phyno, Bmsr);
+	if(!(bmsr & (BmsrAnc|BmsrAna)))
+{
+print("miistatus: auto-neg incomplete\n");
+		return -1;
+}
+
+	bmsr = mii->mir(mii, phyno, Bmsr);
+	if(!(bmsr & BmsrLs)){
+print("miistatus: link down\n");
+		phy->link = 0;
+		return -1;
+	}
+
+	phy->speed = phy->fd = phy->rfc = phy->tfc = 0;
+	if(phy->mscr){
+		r = mii->mir(mii, phyno, Mssr);
+		if((phy->mscr & Mscr1000TFD) && (r & Mssr1000TFD)){
+			phy->speed = 1000;
+			phy->fd = 1;
+		}
+		else if((phy->mscr & Mscr1000THD) && (r & Mssr1000THD))
+			phy->speed = 1000;
+	}
+
+	anlpar = mii->mir(mii, phyno, Anlpar);
+	if(phy->speed == 0){
+		r = phy->anar & anlpar;
+		if(r & AnaTXFD){
+			phy->speed = 100;
+			phy->fd = 1;
+		}
+		else if(r & AnaTXHD)
+			phy->speed = 100;
+		else if(r & Ana10FD){
+			phy->speed = 10;
+			phy->fd = 1;
+		}
+		else if(r & Ana10HD)
+			phy->speed = 10;
+	}
+	if(phy->speed == 0)
+{
+print("miistatus: phy speed 0\n");
+		return -1;
+}
+
+	if(phy->fd){
+		p = phy->fc;
+		r = anlpar & (AnaAP|AnaP);
+		if(p == AnaAP && r == (AnaAP|AnaP))
+			phy->tfc = 1;
+		else if(p == (AnaAP|AnaP) && r == AnaAP)
+			phy->rfc = 1;
+		else if((p & AnaP) && (r & AnaP))
+			phy->rfc = phy->tfc = 1;
+	}
+
+	phy->link = 1;
+
+	return 0;
+}
--- /dev/null
+++ b/os/port/ethermii.h
@@ -1,0 +1,116 @@
+typedef struct Mii Mii;
+typedef struct MiiPhy MiiPhy;
+
+enum {					/* registers */
+	Bmcr		= 0x00,		/* Basic Mode Control */
+	Bmsr		= 0x01,		/* Basic Mode Status */
+	Phyidr1		= 0x02,		/* PHY Identifier #1 */
+	Phyidr2		= 0x03,		/* PHY Identifier #2 */
+	Anar		= 0x04,		/* Auto-Negotiation Advertisement */
+	Anlpar		= 0x05,		/* AN Link Partner Ability */
+	Aner		= 0x06,		/* AN Expansion */
+	Annptr		= 0x07,		/* AN Next Page TX */
+	Annprr		= 0x08,		/* AN Next Page RX */
+	Mscr		= 0x09,		/* MASTER-SLAVE Control */
+	Mssr		= 0x0A,		/* MASTER-SLAVE Status */
+	Esr		= 0x0F,		/* Extended Status */
+
+	NMiiPhyr	= 32,
+	NMiiPhy		= 32,
+};
+
+enum {					/* Bmcr */
+	BmcrSs1		= 0x0040,	/* Speed Select[1] */
+	BmcrCte		= 0x0080,	/* Collision Test Enable */
+	BmcrDm		= 0x0100,	/* Duplex Mode */
+	BmcrRan		= 0x0200,	/* Restart Auto-Negotiation */
+	BmcrI		= 0x0400,	/* Isolate */
+	BmcrPd		= 0x0800,	/* Power Down */
+	BmcrAne		= 0x1000,	/* Auto-Negotiation Enable */
+	BmcrSs0		= 0x2000,	/* Speed Select[0] */
+	BmcrLe		= 0x4000,	/* Loopback Enable */
+	BmcrR		= 0x8000,	/* Reset */
+};
+
+enum {					/* Bmsr */
+	BmsrEc		= 0x0001,	/* Extended Capability */
+	BmsrJd		= 0x0002,	/* Jabber Detect */
+	BmsrLs		= 0x0004,	/* Link Status */
+	BmsrAna		= 0x0008,	/* Auto-Negotiation Ability */
+	BmsrRf		= 0x0010,	/* Remote Fault */
+	BmsrAnc		= 0x0020,	/* Auto-Negotiation Complete */
+	BmsrPs		= 0x0040,	/* Preamble Suppression Capable */
+	BmsrEs		= 0x0100,	/* Extended Status */
+	Bmsr100T2HD	= 0x0200,	/* 100BASE-T2 HD Capable */
+	Bmsr100T2FD	= 0x0400,	/* 100BASE-T2 FD Capable */
+	Bmsr10THD	= 0x0800,	/* 10BASE-T HD Capable */
+	Bmsr10TFD	= 0x1000,	/* 10BASE-T FD Capable */
+	Bmsr100TXHD	= 0x2000,	/* 100BASE-TX HD Capable */
+	Bmsr100TXFD	= 0x4000,	/* 100BASE-TX FD Capable */
+	Bmsr100T4	= 0x8000,	/* 100BASE-T4 Capable */
+};
+
+enum {					/* Anar/Anlpar */
+	Ana10HD		= 0x0020,	/* Advertise 10BASE-T */
+	Ana10FD		= 0x0040,	/* Advertise 10BASE-T FD */
+	AnaTXHD		= 0x0080,	/* Advertise 100BASE-TX */
+	AnaTXFD		= 0x0100,	/* Advertise 100BASE-TX FD */
+	AnaT4		= 0x0200,	/* Advertise 100BASE-T4 */
+	AnaP		= 0x0400,	/* Pause */
+	AnaAP		= 0x0800,	/* Asymmetrical Pause */
+	AnaRf		= 0x2000,	/* Remote Fault */
+	AnaAck		= 0x4000,	/* Acknowledge */
+	AnaNp		= 0x8000,	/* Next Page Indication */
+};
+
+enum {					/* Mscr */
+	Mscr1000THD	= 0x0100,	/* Advertise 1000BASE-T HD */
+	Mscr1000TFD	= 0x0200,	/* Advertise 1000BASE-T FD */
+};
+
+enum {					/* Mssr */
+	Mssr1000THD	= 0x0400,	/* Link Partner 1000BASE-T HD able */
+	Mssr1000TFD	= 0x0800,	/* Link Partner 1000BASE-T FD able */
+};
+
+enum {					/* Esr */
+	Esr1000THD	= 0x1000,	/* 1000BASE-T HD Capable */
+	Esr1000TFD	= 0x2000,	/* 1000BASE-T FD Capable */
+	Esr1000XHD	= 0x4000,	/* 1000BASE-X HD Capable */
+	Esr1000XFD	= 0x8000,	/* 1000BASE-X FD Capable */
+};
+
+typedef struct Mii {
+	Lock;
+	int	nphy;
+	int	mask;
+	MiiPhy*	phy[NMiiPhy];
+	MiiPhy*	curphy;
+
+	void*	ctlr;
+	int	(*mir)(Mii*, int, int);
+	int	(*miw)(Mii*, int, int, int);
+} Mii;
+
+typedef struct MiiPhy {
+	Mii*	mii;
+	int	oui;
+	int	phyno;
+
+	int	anar;
+	int	fc;
+	int	mscr;
+
+	int	link;
+	int	speed;
+	int	fd;
+	int	rfc;
+	int	tfc;
+};
+
+extern int mii(Mii*, int);
+extern int miiane(Mii*, int, int, int);
+extern int miimir(Mii*, int);
+extern int miimiw(Mii*, int, int);
+extern int miireset(Mii*);
+extern int miistatus(Mii*);
--- /dev/null
+++ b/os/port/ethersink.c
@@ -1,0 +1,65 @@
+/*
+ * An ethernet /dev/null.
+ * Useful as a bridging target with ethernet-based VPN.
+ */
+#include "u.h"
+#include "../port/lib.h"
+#include "mem.h"
+#include "dat.h"
+#include "fns.h"
+#include "io.h"
+#include "../port/error.h"
+#include "../port/netif.h"
+#include "etherif.h"
+
+static long
+ctl(Ether *ether, void *buf, long n)
+{
+	uchar ea[Eaddrlen];
+	Cmdbuf *cb;
+
+	cb = parsecmd(buf, n);
+	if(cb->nf >= 2
+	&& strcmp(cb->f[0], "ea")==0
+	&& parseether(ea, cb->f[1]) == 0){
+		free(cb);
+		memmove(ether->ea, ea, Eaddrlen);
+		memmove(ether->addr, ether->ea, Eaddrlen);
+		return 0;
+	}
+	free(cb);
+	error(Ebadctl);
+	return -1;	/* not reached */
+}
+
+static void
+nop(Ether*)
+{
+}
+
+static int
+reset(Ether* ether)
+{
+	uchar ea[Eaddrlen];
+
+	if(ether->type==nil)
+		return -1;
+	memset(ea, 0, sizeof ea);
+	ether->mbps = 1000;
+	ether->attach = nop;
+	ether->transmit = nop;
+	ether->irq = -1;
+	ether->interrupt = nil;
+	ether->ifstat = nil;
+	ether->ctl = ctl;
+	ether->promiscuous = nil;
+	ether->multicast = nil;
+	ether->arg = ether;
+	return 0;
+}
+
+void
+ethersinklink(void)
+{
+	addethercard("sink", reset);
+}
--- /dev/null
+++ b/os/port/exception.c
@@ -1,0 +1,216 @@
+#include "u.h"
+#include "../port/lib.h"
+#include "mem.h"
+#include "dat.h"
+#include "fns.h"
+#include "../port/error.h"
+#include "interp.h"
+#include "isa.h"
+#include "runt.h"
+#include "kernel.h"
+#include "raise.h"
+
+static int
+ematch(char *pat, char *exp)
+{
+	int l;
+
+	if(strcmp(pat, exp) == 0)
+		return 1;
+
+	l = strlen(pat);
+	if(l == 0)
+		return 0;
+	if(pat[l-1] == '*') {
+		if(l == 1)
+			return 1;
+		if(strncmp(pat, exp, l-1) == 0)
+			return 1;
+	}
+	return 0;
+}
+
+static void
+setstr(String *s, char *p)
+{
+	if(s == H)
+		return;
+	if(s->len < 0 || s->max < 4)
+		return;
+	kstrcpy(s->Sascii, p, s->max);	/* TO DO: we are assuming they aren't runes */
+	s->len = strlen(s->Sascii);
+}
+
+static String *exstr;
+
+void
+excinit(void)
+{
+	exstr = newstring(ERRMAX);
+	poolimmutable(D2H(exstr));
+}
+
+static String*
+newestring(char *estr)
+{
+	String *s;
+
+	if(waserror()){
+		setstr(exstr, estr);
+		D2H(exstr)->ref++;
+		return exstr;
+	}
+	s = c2string(estr, strlen(estr));
+	poperror();
+	return s;
+}
+
+#define NOPC	0xffffffff
+
+#define FRTYPE(f)	((f)->t == nil ? SEXTYPE(f)->reg.TR : (f)->t)
+
+/*
+ * clear up an uncalled frame
+ */
+static void
+freeframe(uchar *fp, int setsp)
+{
+	Frame *f;
+
+	f = (Frame*)fp;
+	if(f->t == nil)
+		unextend(f);
+	else if(f->t->np)
+		freeptrs(f, f->t);
+	if(setsp)
+		R.SP = fp;
+}
+
+int
+handler(char *estr)
+{
+	Prog *p;
+	Modlink *m, *mr;
+	int str, ne;
+	ulong pc, newpc;
+	long eoff;
+	uchar *fp, **eadr;
+	Frame *f;
+	Type *t, *zt;
+	Handler *h;
+	Except *e;
+	void *v;
+
+	p = currun();
+	if(*estr == 0 || p == nil)
+		return 0;
+	str = p->exval == H || D2H(p->exval)->t == &Tstring;
+	m = R.M;
+	if(m->compiled)
+		pc = (ulong)R.PC-(ulong)m->prog;
+	else
+		pc = R.PC-m->prog;
+	pc--;
+	fp = R.FP;
+	while(fp != nil){		/* look for a handler */
+		if((h = m->m->htab) != nil){
+			for( ; h->etab != nil; h++){
+				if(pc < h->pc1 || pc >= h->pc2)
+					continue;
+				eoff = h->eoff;
+				zt = h->t;
+				for(e = h->etab, ne = h->ne; e->s != nil; e++, ne--){
+					if(ematch(e->s, estr) && (str && ne <= 0 || !str && ne > 0)){
+						newpc = e->pc;
+						goto found;
+					}
+				}
+				newpc = e->pc;
+				if(newpc != NOPC)
+					goto found;
+			}
+		}
+		if(!str && fp != R.FP){		/* becomes a string exception in immediate caller */
+			v = p->exval;
+			p->exval = *(String**)v;
+			D2H(p->exval)->ref++;
+			destroy(v);
+			str = 1;
+			continue;
+		}
+		f = (Frame*)fp;
+		if(f->mr != nil)
+			m = f->mr;
+		if(m->compiled)
+			pc = (ulong)f->lr-(ulong)m->prog;
+		else
+			pc = f->lr-m->prog;
+		pc--;
+		fp = f->fp;
+	}
+	destroy(p->exval);
+	p->exval = H;
+	return 0;
+found:
+	{
+		int n;
+		char name[3*KNAMELEN];
+
+		pc = modstatus(&R, name, sizeof(name));
+		n = 10+1+strlen(name)+1+strlen(estr)+1;
+		p->exstr = realloc(p->exstr, n);
+		if(p->exstr != nil)
+			snprint(p->exstr, n, "%lud %s %s", pc, name, estr);
+	}
+
+	/*
+	 * there may be an uncalled frame at the top of the stack
+	 */
+	f = (Frame*)R.FP;
+	t = FRTYPE(f);
+	if(R.FP < R.EX || R.FP >= R.TS)
+		freeframe(R.EX+OA(Stkext, reg.tos.fr), 0);
+	else if(R.FP+t->size < R.SP)
+		freeframe(R.FP+t->size, 1);
+
+	m = R.M;
+	while(R.FP != fp){
+		f = (Frame*)R.FP;
+		R.PC = f->lr;
+		R.FP = f->fp;
+		R.SP = (uchar*)f;
+		mr = f->mr;
+		if(f->t == nil)
+			unextend(f);
+		else if(f->t->np)
+			freeptrs(f, f->t);
+		if(mr != nil){
+			m = mr;
+			destroy(R.M);
+			R.M = m;
+			R.MP = m->MP;
+		}
+	}
+	if(zt != nil){
+		freeptrs(fp, zt);
+		initmem(zt, fp);
+	}
+	eadr = (uchar**)(fp+eoff);
+	destroy(*eadr);
+	*eadr = H;
+	if(p->exval == H)
+		*eadr = (uchar*)newestring(estr);	/* might fail */
+	else{
+		D2H(p->exval)->ref++;
+		*eadr = p->exval;
+	}
+	if(m->compiled)
+		R.PC = (Inst*)((ulong)m->prog+newpc);
+	else
+		R.PC = m->prog+newpc;
+	memmove(&p->R, &R, sizeof(R));
+	p->kill = nil;
+	destroy(p->exval);
+	p->exval = H;
+	return 1;
+}
--- /dev/null
+++ b/os/port/exportfs.c
@@ -1,0 +1,1328 @@
+#include	"u.h"
+#include	"../port/lib.h"
+#include	"mem.h"
+#include	"dat.h"
+#include	"fns.h"
+#include	"../port/error.h"
+#include	"kernel.h"
+
+typedef	struct Fid	Fid;
+typedef	struct Export	Export;
+typedef	struct Exq	Exq;
+typedef	struct Uqid Uqid;
+
+enum
+{
+	Nfidhash	= 32,
+	Nqidhash = 32,
+	QIDMASK = ((vlong)1<<48)-1,
+	MAXFDATA	= 8192,
+	MAXRPCDEF		= IOHDRSZ+MAXFDATA,	/* initial/default */
+	MAXRPCMAX	= IOHDRSZ+64*1024,	/* most every allowed */
+	MSGHDRSZ	= BIT32SZ+BIT8SZ+BIT16SZ
+};
+
+struct Export
+{
+	Lock;
+	Ref	r;
+	Exq*	work;
+	Lock	fidlock;
+	Fid*	fid[Nfidhash];
+	QLock	qidlock;
+	Uqid*	qids[Nqidhash];
+	ulong	pathgen;
+	Chan*	io;
+	Chan*	root;
+	Pgrp*	pgrp;
+	Egrp*	egrp;
+	Fgrp*	fgrp;
+	int	async;
+	int	readonly;
+	int	msize;
+	char*	user;
+};
+
+struct Fid
+{
+	Fid*	next;
+	Fid**	last;
+	Chan*	chan;
+	int	fid;
+	int	ref;		/* fcalls using the fid; locked by Export.Lock */
+	vlong	offset;	/* last offset used (within directory) */
+	int	attached;	/* fid attached or cloned but not clunked */
+	Uqid*	qid;	/* generated qid */
+};
+
+struct Uqid
+{
+	Ref;
+	int	type;
+	int	dev;
+	vlong	oldpath;
+	vlong	newpath;
+	Uqid*	next;
+};
+
+struct Exq
+{
+	Lock;
+	int	busy;	/* fcall in progress */
+	int	finished;	/* will do no more work on this request or flushes */
+	Exq*	next;
+	int	shut;		/* has been noted for shutdown */
+	Exq*	flush;	/* queued flush requests */
+	Exq*	flusht;	/* tail of flush queue */
+	Export*	export;
+	Proc*	slave;
+	Fcall	in, out;
+	uchar*	buf;
+	int	bsize;
+};
+
+struct
+{
+	Lock	l;
+	QLock	qwait;
+	Rendez	rwait;
+	Exq	*head;		/* work waiting for a slave */
+	Exq	*tail;
+}exq;
+
+static void	exshutdown(Export*);
+static int	exflushed(Export*, Exq*);
+static void	exslave(void*);
+static void	exfree(Export*);
+static void	exfreeq(Exq*);
+static void	exportproc(void*);
+static void	exreply(Exq*, char*);
+static int	exisroot(Export*, Chan*);
+static Uqid*	uqidalloc(Export*, Chan*);
+static void		freeuqid(Export*, Uqid*);
+
+static char*	Exversion(Export*, Fcall*, Fcall*);
+static char*	Exauth(Export*, Fcall*, Fcall*);
+static char*	Exattach(Export*, Fcall*, Fcall*);
+static char*	Exclunk(Export*, Fcall*, Fcall*);
+static char*	Excreate(Export*, Fcall*, Fcall*);
+static char*	Exopen(Export*, Fcall*, Fcall*);
+static char*	Exread(Export*, Fcall*, Fcall*);
+static char*	Exremove(Export*, Fcall*, Fcall*);
+static char*	Exstat(Export*, Fcall*, Fcall*);
+static char*	Exwalk(Export*, Fcall*, Fcall*);
+static char*	Exwrite(Export*, Fcall*, Fcall*);
+static char*	Exwstat(Export*, Fcall*, Fcall*);
+
+static char	*(*fcalls[Tmax])(Export*, Fcall*, Fcall*);
+
+static char	Enofid[]   = "no such fid";
+static char	Eseekdir[] = "can't seek on a directory";
+static char	Eopen[]	= "walk of open fid";
+static char	Emode[] = "open/create -- unknown mode";
+static char	Edupfid[]	= "fid in use";
+static char	Eaccess[] = "read/write -- not open in suitable mode";
+static char	Ecount[] = "read/write -- count too big";
+int	exdebug = 0;
+
+int
+export(int fd, char *dir, int async)
+{
+	Chan *c, *dc;
+	Pgrp *pg;
+	Egrp *eg;
+	Export *fs;
+
+	if(waserror())
+		return -1;
+	c = fdtochan(up->env->fgrp, fd, ORDWR, 1, 1);
+	poperror();
+
+	if(waserror()){
+		cclose(c);
+		return -1;
+	}
+	dc = namec(dir, Atodir, 0, 0);
+	poperror();
+
+	fs = malloc(sizeof(Export));
+	if(fs == nil){
+		cclose(c);
+		cclose(dc);
+		error(Enomem);
+	}
+
+	fs->r.ref = 1;
+	pg = up->env->pgrp;
+	fs->pgrp = pg;
+	incref(pg);
+	eg = up->env->egrp;
+	fs->egrp = eg;
+	if(eg != nil)
+		incref(eg);
+	fs->fgrp = newfgrp(nil);
+	kstrdup(&fs->user, up->env->user);
+	fs->root = dc;
+	fs->io = c;
+	fs->pathgen = 0;
+	fs->msize = 0;
+	c->flag |= CMSG;
+	fs->async = async;
+
+	if(async){
+		if(waserror())
+			return -1;
+		kproc("exportfs", exportproc, fs, 0);
+		poperror();
+	}else
+		exportproc(fs);
+
+	return 0;
+}
+
+static void
+exportinit(void)
+{
+	lock(&exq.l);
+	if(fcalls[Tversion] != nil) {
+		unlock(&exq.l);
+		return;
+	}
+	fcalls[Tversion] = Exversion;
+	fcalls[Tauth] = Exauth;
+	fcalls[Tattach] = Exattach;
+	fcalls[Twalk] = Exwalk;
+	fcalls[Topen] = Exopen;
+	fcalls[Tcreate] = Excreate;
+	fcalls[Tread] = Exread;
+	fcalls[Twrite] = Exwrite;
+	fcalls[Tclunk] = Exclunk;
+	fcalls[Tremove] = Exremove;
+	fcalls[Tstat] = Exstat;
+	fcalls[Twstat] = Exwstat;
+	unlock(&exq.l);
+}
+
+static int
+exisroot(Export *fs, Chan *c)
+{
+	return eqchan(fs->root, c, 1);
+}
+
+static int
+exreadn(Chan *c, void *buf, int n)
+{
+	int nr, t;
+
+	if(waserror())
+		return -1;
+	for(nr = 0; nr < n;){
+		t = devtab[c->type]->read(c, (char*)buf+nr, n-nr, 0);
+		if(t <= 0)
+			break;
+		nr += t;
+	}
+	poperror();
+	return nr;
+}
+
+static int
+exreadmsg(Chan *c, void *a, uint n)
+{
+	int m, len;
+	uchar *buf;
+
+	buf = a;
+	m = exreadn(c, buf, BIT32SZ);
+	if(m < BIT32SZ){
+		if(m < 0)
+			return -1;
+		return 0;
+	}
+	len = GBIT32(buf);
+	if(len <= BIT32SZ || len > n){
+		kwerrstr("bad length in Styx message header");
+		return -1;
+	}
+	len -= BIT32SZ;
+	m = exreadn(c, buf+BIT32SZ, len);
+	if(m < len){
+		if(m < 0)
+			return -1;
+		return 0;
+	}
+	return BIT32SZ+m;
+}
+
+static void
+exportproc(void *a)
+{
+	Exq *q;
+	int async, msize;
+	int n, type;
+	Export *fs = a;
+
+	exportinit();
+
+	for(;;){
+
+		msize = fs->msize;
+		if(msize == 0)
+			msize = MAXRPCDEF;
+		for(n=0;; n++){	/* we don't use smalloc, to avoid memset */
+			q = mallocz(sizeof(*q)+msize, 0);
+			if(q != nil || n > 6000)
+				break;
+			if(n%600 == 0)
+				print("exportproc %ld: waiting for memory (%d) for request\n", up->pid, msize);
+			tsleep(&up->sleep, return0, nil, 100);
+		}
+		if(q == nil){
+			kwerrstr("out of memory: read request");
+			n = -1;
+			break;
+		}
+		memset(q, 0, sizeof(*q));
+		q->buf = (uchar*)q + sizeof(*q);
+		q->bsize = msize;
+
+		n = exreadmsg(fs->io, q->buf, msize);	/* TO DO: avoid copy */
+		if(n <= 0)
+			break;
+		if(convM2S(q->buf, n, &q->in) != n){
+			kwerrstr("bad T-message");
+			n = -1;
+			break;
+		}
+		type = q->in.type;
+		if(type < Tversion || type >= Tmax || type&1 || type == Terror){
+			kwerrstr("invalid T-message type %d", type);
+			n = -1;
+			break;
+		}
+
+		if(exdebug)
+			print("export %ld <- %F\n", up->pid, &q->in);
+
+		q->out.type = type+1;
+		q->out.tag = q->in.tag;
+
+		q->export = fs;
+		incref(&fs->r);
+
+		if(fs->readonly){
+			switch(type){
+			case Topen:
+				if((q->in.mode & (ORCLOSE|OTRUNC|3)) == OREAD)
+					break;
+				/* FALL THROUGH */
+			case Tcreate:
+			case Twrite:
+			case Tremove:
+			case Twstat:
+				q->out.type = Rerror;
+				q->out.ename = "file system read only";
+				exreply(q, "exportproc");
+				exfreeq(q);
+				continue;
+			}
+		}
+
+		if(q->in.type == Tflush){
+			if(exflushed(fs, q)){
+				/* not yet started or not found (flush arrived after reply); we reply */
+				if(exdebug)
+					print("export: flush %d\n", q->in.oldtag);
+				exreply(q, "exportproc");
+				exfreeq(q);
+			}
+			continue;
+		}
+
+		lock(&exq.l);
+		if(exq.head == nil)
+			exq.head = q;
+		else
+			exq.tail->next = q;
+		q->next = nil;
+		exq.tail = q;
+		unlock(&exq.l);
+		if(exq.qwait.head == nil)
+			kproc("exslave", exslave, nil, 0);
+		wakeup(&exq.rwait);
+	}
+
+	if(exdebug){
+		if(n < 0)
+			print("exportproc %ld shut down: %s\n", up->pid, up->env->errstr);
+		else
+			print("exportproc %ld shut down\n", up->pid);
+	}
+
+	free(q);
+	exshutdown(fs);
+	async = fs->async;
+	exfree(fs);
+
+	if(async)
+		pexit("mount shut down", 0);
+}
+
+static int
+exflushed(Export *fs, Exq *fq)
+{
+	Exq *q, **last;
+	ulong pid;
+
+	/* not yet started? */
+	lock(&exq.l);
+	for(last = &exq.head; (q = *last) != nil; last = &q->next)
+		if(q->export == fs && q->in.tag == fq->in.oldtag){
+			*last = q->next;
+			unlock(&exq.l);
+			/* not yet started: discard, and Rflush */
+			exfreeq(q);
+			return 1;
+		}
+	unlock(&exq.l);
+
+	/* tricky case: in progress */
+	lock(fs);
+	for(q = fs->work; q != nil; q = q->next)
+		if(q->in.tag == fq->in.oldtag){
+			pid = 0;
+			lock(q);
+			if(q->finished){
+				/* slave replied and emptied its flush queue; we can Rflush now */
+				unlock(q);
+				return 1;
+			}
+			/* append to slave's flush queue */
+			fq->next = nil;
+			if(q->flush != nil)
+				q->flusht->next = fq;
+			else
+				q->flush = fq;
+			q->flusht = fq;
+			if(q->busy){
+				pid = q->slave->pid;
+				swiproc(q->slave, 0);
+			}
+			unlock(q);
+			unlock(fs);
+			if(exdebug && pid)
+				print("export: swiproc %ld to flush %d\n", pid, fq->in.oldtag);
+			return 0;
+		}
+	unlock(fs);
+
+	/* not found */
+	return 1;
+}
+
+static void
+exfreeq(Exq *q)
+{
+	Exq *fq;
+
+	while((fq = q->flush) != nil){
+		q->flush = fq->next;
+		exfree(fq->export);
+		free(fq);
+	}
+	exfree(q->export);
+	free(q);
+}
+
+static void
+exshutdown(Export *fs)
+{
+	Exq *q, **last;
+
+	/* work not started */
+	lock(&exq.l);
+	for(last = &exq.head; (q = *last) != nil;)
+		if(q->export == fs){
+			*last = q->next;
+			exfreeq(q);
+		}else
+			last = &q->next;
+	unlock(&exq.l);
+
+	/* tell slaves to abandon work in progress */
+	lock(fs);
+	while((q = fs->work) != nil){
+		fs->work = q->next;
+		lock(q);
+		q->shut = 1;
+		swiproc(q->slave, 0);	/* whether busy or not */
+		unlock(q);
+	}
+	unlock(fs);
+}
+
+static void
+exfreefids(Export *fs)
+{
+	Fid *f, *n;
+	int i;
+
+	for(i = 0; i < Nfidhash; i++){
+		for(f = fs->fid[i]; f != nil; f = n){
+			n = f->next;
+			f->attached = 0;
+			if(f->ref == 0) {
+				if(f->chan != nil)
+					cclose(f->chan);
+				freeuqid(fs, f->qid);
+				free(f);
+			} else
+				print("exfreefids: busy fid\n");
+		}
+	}
+}
+
+static void
+exfree(Export *fs)
+{
+	if(exdebug)
+		print("export p/s %ld free %p ref %ld\n", up->pid, fs, fs->r.ref);
+	if(decref(&fs->r) != 0)
+		return;
+	closepgrp(fs->pgrp);
+	closeegrp(fs->egrp);
+	closefgrp(fs->fgrp);
+	cclose(fs->root);
+	cclose(fs->io);
+	exfreefids(fs);
+	free(fs->user);
+	free(fs);
+}
+
+static int
+exwork(void*)
+{
+	return exq.head != nil;
+}
+
+static void
+exslave(void*)
+{
+	Export *fs;
+	Exq *q, *t, *fq, **last;
+	char *err;
+	int nstat;
+
+	for(;;){
+		qlock(&exq.qwait);
+		if(waserror()){
+			qunlock(&exq.qwait);
+			continue;
+		}
+		sleep(&exq.rwait, exwork, nil);
+		poperror();
+
+		lock(&exq.l);
+		q = exq.head;
+		if(q == nil) {
+			unlock(&exq.l);
+			qunlock(&exq.qwait);
+			continue;
+		}
+		exq.head = q->next;
+
+		qunlock(&exq.qwait);
+
+		/*
+		 * put the job on the work queue before it's
+		 * visible as off of the head queue, so it's always
+		 * findable for flushes and shutdown
+		 */
+		notkilled();
+		q->slave = up;
+		q->busy = 1;	/* fcall in progress: interruptible */
+		fs = q->export;
+		lock(fs);
+		q->next = fs->work;
+		fs->work = q;
+		unlock(fs);
+		unlock(&exq.l);
+
+		up->env->pgrp = q->export->pgrp;
+		up->env->egrp = q->export->egrp;
+		up->env->fgrp = q->export->fgrp;
+		kstrdup(&up->env->user, q->export->user);
+
+		if(exdebug > 1)
+			print("exslave %ld dispatch %F\n", up->pid, &q->in);
+
+		if(waserror()){
+			print("exslave %ld err %s\n", up->pid, up->env->errstr);	/* shouldn't happen */
+			err = up->env->errstr;
+		}else{
+			if(q->in.type >= Tmax || !fcalls[q->in.type]){
+				snprint(up->genbuf, sizeof(up->genbuf), "unknown message: %F", &q->in);
+				err = up->genbuf;
+			}else{
+				switch(q->in.type){
+				case Tread:
+					q->out.data = (char*)q->buf + IOHDRSZ;
+					break;
+				case Tstat:
+					q->out.stat = q->buf + MSGHDRSZ + BIT16SZ;	/* leaves it just where we want it */
+					nstat = q->bsize;
+					if(nstat > STATMAX)
+						nstat = STATMAX;
+					nstat -= MSGHDRSZ+BIT16SZ;
+					q->out.nstat = nstat;
+					break;
+				}
+				err = (*fcalls[q->in.type])(fs, &q->in, &q->out);
+			}
+			poperror();
+		}
+
+		/*
+		 * if the fcall completed without error we must reply,
+		 * even if a flush is pending (because the underlying server
+		 * might have changed state), unless the export has shut down completely.
+		 * must also reply to each flush in order, and only after the original reply (if sent).
+		 */
+		lock(q);
+		notkilled();
+		q->busy = 0;	/* operation complete */
+		if(!q->shut){
+			if(q->flush == nil || err == nil){
+				unlock(q);
+				q->out.type = q->in.type+1;
+				q->out.tag = q->in.tag;
+				if(err){
+					q->out.type = Rerror;
+					q->out.ename = err;
+				}
+				exreply(q, "exslave");
+				lock(q);
+			}
+			while((fq = q->flush) != nil && !q->shut){
+				q->flush = fq->next;
+				unlock(q);
+				exreply(fq, "exslave");
+				exfreeq(fq);
+				lock(q);
+			}
+		}
+		q->finished = 1;	/* promise not to send any more */
+		unlock(q);
+
+		lock(fs);
+		for(last = &fs->work; (t = *last) != nil; last = &t->next)
+			if(t == q){
+				*last = q->next;
+				break;
+			}
+		unlock(fs);
+
+		notkilled();
+		exfreeq(q);
+	}
+}
+
+static void
+exreply(Exq *q, char *who)
+{
+	Export *fs;
+	Fcall *r;
+	int n;
+
+	fs = q->export;
+	r = &q->out;
+
+	n = convS2M(r, q->buf, q->bsize);
+	if(n == 0){
+		r->type = Rerror;
+		if(fs->msize == 0)
+			r->ename = "Tversion not seen";
+		else
+			r->ename = "failed to convert R-message";
+		n = convS2M(r, q->buf, q->bsize);
+	}
+
+	if(exdebug)
+		print("%s %ld -> %F\n", who, up->pid, r);
+
+	if(!waserror()){
+		devtab[fs->io->type]->write(fs->io, q->buf, n, 0);
+		poperror();
+	}
+}
+
+static int
+exiounit(Export *fs, Chan *c)
+{
+	int iounit;
+
+	iounit = fs->msize-IOHDRSZ;
+	if(c->iounit != 0 && c->iounit < fs->msize)
+		iounit = c->iounit;
+	return iounit;
+}
+
+static Qid
+Exrmtqid(Chan *c, Uqid *qid)
+{
+	Qid q;
+
+	q.path = qid->newpath;
+	q.vers = c->qid.vers;
+	q.type = c->qid.type;
+	return q;
+}
+
+static Fid*
+Exmkfid(Export *fs, ulong fid)
+{
+	ulong h;
+	Fid *f, *nf;
+
+	nf = malloc(sizeof(Fid));
+	if(nf == nil)
+		return nil;
+	lock(&fs->fidlock);
+	h = fid % Nfidhash;
+	for(f = fs->fid[h]; f != nil; f = f->next){
+		if(f->fid == fid){
+			unlock(&fs->fidlock);
+			free(nf);
+			return nil;
+		}
+	}
+
+	nf->next = fs->fid[h];
+	if(nf->next != nil)
+		nf->next->last = &nf->next;
+	nf->last = &fs->fid[h];
+	fs->fid[h] = nf;
+
+	nf->fid = fid;
+	nf->ref = 1;
+	nf->attached = 1;
+	nf->offset = 0;
+	nf->chan = nil;
+	nf->qid = nil;
+	unlock(&fs->fidlock);
+	return nf;
+}
+
+static Fid*
+Exgetfid(Export *fs, ulong fid)
+{
+	Fid *f;
+	ulong h;
+
+	lock(&fs->fidlock);
+	h = fid % Nfidhash;
+	for(f = fs->fid[h]; f; f = f->next) {
+		if(f->fid == fid){
+			if(f->attached == 0)
+				break;
+			f->ref++;
+			unlock(&fs->fidlock);
+			return f;
+		}
+	}
+	unlock(&fs->fidlock);
+	return nil;
+}
+
+static void
+Exputfid(Export *fs, Fid *f)
+{
+	Chan *c;
+
+	lock(&fs->fidlock);
+	f->ref--;
+	if(f->ref == 0 && f->attached == 0){
+		c = f->chan;
+		f->chan = nil;
+		*f->last = f->next;
+		if(f->next != nil)
+			f->next->last = f->last;
+		unlock(&fs->fidlock);
+		if(c != nil)
+			cclose(c);
+		freeuqid(fs, f->qid);
+		free(f);
+		return;
+	}
+	unlock(&fs->fidlock);
+}
+
+static Chan*
+exmount(Chan *c, Mhead **mp, int doname)
+{
+	Chan *nc;
+	Cname *oname;
+
+	nc = nil;
+	if((c->flag & COPEN) == 0 && findmount(&nc, mp, c->type, c->dev, c->qid)){
+		if(waserror()){
+			cclose(nc);
+			nexterror();
+		}
+		nc = cunique(nc);
+		poperror();
+		if(doname){
+			oname = c->name;
+			incref(oname);
+			cnameclose(nc->name);
+			nc->name = oname;
+		}
+		return nc;
+	}
+	incref(c);
+	return c;
+}
+
+static char*
+Exversion(Export *fs, Fcall *t, Fcall *r)
+{
+	char *p;
+	static char version[] = VERSION9P;
+	int iounit;
+
+	r->msize = t->msize;
+	if(r->msize > MAXRPCMAX)
+		r->msize = MAXRPCMAX;
+	iounit = fs->io->iounit;
+	if(iounit != 0 && iounit > 64 && iounit < r->msize)
+		r->msize = iounit;
+	if(r->msize < 64)
+		return "message size too small";
+	if((p = strchr(t->version, '.')) != nil)
+		*p = 0;
+	if(strncmp(t->version, "9P", 2) ==0 && strcmp(version, t->version) <= 0){
+		r->version = version;
+		fs->msize = r->msize;
+	}else
+		r->version = "unknown";
+	return nil;
+}
+
+static char*
+Exauth(Export *fs, Fcall *t, Fcall *r)
+{
+	USED(fs);
+	USED(t);
+	USED(r);
+	return "authentication not required";
+}
+
+static char*
+Exattach(Export *fs, Fcall *t, Fcall *r)
+{
+	Fid *f;
+
+	f = Exmkfid(fs, t->fid);
+	if(f == nil)
+		return Edupfid;
+	if(waserror()){
+		f->attached = 0;
+		Exputfid(fs, f);
+		return up->env->errstr;
+	}
+	f->chan = cclone(fs->root);
+	f->qid = uqidalloc(fs, f->chan);
+	poperror();
+	r->qid = Exrmtqid(f->chan, f->qid);
+	Exputfid(fs, f);
+	return nil;
+}
+
+static char*
+Exclunk(Export *fs, Fcall *t, Fcall *r)
+{
+	Fid *f;
+
+	USED(r);
+	f = Exgetfid(fs, t->fid);
+	if(f == nil)
+		return Enofid;
+	f->attached = 0;
+	Exputfid(fs, f);
+	return nil;
+}
+
+static int
+safewalk(Chan **cp, char **names, int nnames, int nomount, int *nerror)
+{
+	int r;
+
+	/* walk can raise error */
+	if(waserror())
+		return -1;
+	r = walk(cp, names, nnames, nomount, nerror);
+	poperror();
+	return r;
+}
+
+static char*
+Exwalk(Export *fs, Fcall *t, Fcall *r)
+{
+	Fid *f, *nf;
+	Chan *c;
+	char *name;
+	Uqid *qid;
+	int i;
+
+	f = Exgetfid(fs, t->fid);
+	if(f == nil)
+		return Enofid;
+	if(f->chan->flag & COPEN){
+		Exputfid(fs, f);
+		return Eopen;
+	}
+
+	if(waserror())
+		return up->env->errstr;
+	c = cclone(f->chan);
+	poperror();
+	qid = f->qid;
+	incref(qid);
+	r->nwqid = 0;
+	if(t->nwname > 0){
+		for(i=0; i<t->nwname; i++){
+			name = t->wname[i];
+			if(!exisroot(fs, c) || *name != '\0' && strcmp(name, "..") != 0){
+				if(safewalk(&c, &name, 1, 0, nil) < 0){
+					/* leave the original state on error */
+					cclose(c);
+					freeuqid(fs, qid);
+					Exputfid(fs, f);
+					if(i == 0)
+						return up->env->errstr;
+					return nil;
+				}
+				freeuqid(fs, qid);
+				qid = uqidalloc(fs, c);
+			}
+			r->wqid[r->nwqid++] = Exrmtqid(c, qid);
+		}
+	}
+
+	if(t->newfid != t->fid){
+		nf = Exmkfid(fs, t->newfid);
+		if(nf == nil){
+			cclose(c);
+			freeuqid(fs, qid);
+			Exputfid(fs, f);
+			return Edupfid;
+		}
+		nf->chan = c;
+		nf->qid = qid;
+		Exputfid(fs, nf);
+	}else{
+		cclose(f->chan);
+		f->chan = c;
+		freeuqid(fs, f->qid);
+		f->qid = qid;
+	}
+	Exputfid(fs, f);
+	return nil;
+}
+
+static char*
+Exopen(Export *fs, Fcall *t, Fcall *r)
+{
+	Fid *f;
+	Chan *c;
+	Uqid *qid;
+	Mhead *m;
+
+	f = Exgetfid(fs, t->fid);
+	if(f == nil)
+		return Enofid;
+	if(f->chan->flag & COPEN){
+		Exputfid(fs, f);
+		return Emode;
+	}
+	m = nil;
+	c = exmount(f->chan, &m, 1);
+	if(waserror()){
+		cclose(c);
+		Exputfid(fs, f);
+		return up->env->errstr;
+	}
+
+	/* only save the mount head if it's a multiple element union */
+	if(m && m->mount && m->mount->next)
+		c->umh = m;
+	else
+		putmhead(m);
+
+	c = devtab[c->type]->open(c, t->mode);
+	if(t->mode & ORCLOSE)
+		c->flag |= CRCLOSE;
+
+	qid = uqidalloc(fs, c);
+	poperror();
+	freeuqid(fs, f->qid);
+	cclose(f->chan);
+	f->chan = c;
+	f->qid = qid;
+	f->offset = 0;
+	r->qid = Exrmtqid(c, f->qid);
+	r->iounit = exiounit(fs, c);
+	Exputfid(fs, f);
+	return nil;
+}
+
+static char*
+Excreate(Export *fs, Fcall *t, Fcall *r)
+{
+	Fid *f;
+	volatile struct {Chan *c;} c, dc;
+	Cname *oname;
+	Uqid *qid;
+	Mhead *m;
+
+	f = Exgetfid(fs, t->fid);
+	if(f == nil)
+		return Enofid;
+	if(f->chan->flag & COPEN){
+		Exputfid(fs, f);
+		return Emode;
+	}
+	if(waserror()){
+		Exputfid(fs, f);
+		return up->env->errstr;
+	}
+	validname(t->name, 0);
+	if(t->name[0] == '.' && (t->name[1] == '\0' || t->name[1] == '.' && t->name[2] == '\0'))
+		error(Efilename);	/* underlying server should check, but stop it here */
+
+	m = nil;
+	c.c = exmount(f->chan, &m, 1);
+	if(waserror()){
+		cclose(c.c);
+		if(m != nil)
+			putmhead(m);
+		nexterror();
+	}
+	if(m != nil){
+		oname = c.c->name;
+		incref(oname);
+		if(waserror()){
+			cnameclose(oname);
+			nexterror();
+		}
+		dc.c = createdir(c.c, m);
+		if(waserror()){
+			cclose(dc.c);
+			nexterror();
+		}
+		c.c = cunique(dc.c);
+		poperror();
+		cnameclose(c.c->name);
+		poperror();
+		c.c->name = oname;
+	}
+	devtab[c.c->type]->create(c.c, t->name, t->mode, t->perm);
+	c.c->name = addelem(c.c->name, t->name);
+	if(t->mode & ORCLOSE)
+		c.c->flag |= CRCLOSE;
+	qid = uqidalloc(fs, c.c);
+	poperror();
+	if(m != nil)
+		putmhead(m);
+
+	poperror();
+	cclose(f->chan);
+	f->chan = c.c;
+	freeuqid(fs, f->qid);
+	f->qid = qid;
+	r->qid = Exrmtqid(c.c, f->qid);
+	r->iounit = exiounit(fs, c.c);
+	Exputfid(fs, f);
+	return nil;
+}
+
+static char*
+Exread(Export *fs, Fcall *t, Fcall *r)
+{
+	Fid *f;
+	Chan *c;
+	vlong off;
+	int dir, n, seek;
+
+	f = Exgetfid(fs, t->fid);
+	if(f == nil)
+		return Enofid;
+
+	if(waserror()) {
+		Exputfid(fs, f);
+		return up->env->errstr;
+	}
+	c = f->chan;
+	if((c->flag & COPEN) == 0)
+		error(Emode);
+	if(c->mode != OREAD && c->mode != ORDWR)
+		error(Eaccess);
+	if(t->count < 0 || t->count > fs->msize-IOHDRSZ)
+		error(Ecount);
+	if(t->offset < 0)
+		error(Enegoff);
+	dir = c->qid.type & QTDIR;
+	if(dir && t->offset != f->offset){
+		if(t->offset != 0)
+			error(Eseekdir);
+		f->offset = 0;
+		c->uri = 0;
+		c->dri = 0;
+	}
+
+	for(;;){
+		n = t->count;
+		seek = 0;
+		off = t->offset;
+		if(dir && f->offset != off){
+			off = f->offset;
+			n = t->offset - off;
+			if(n > MAXFDATA)
+				n = MAXFDATA;
+			seek = 1;
+		}
+		if(dir && c->umh != nil){
+			if(0)
+				print("union read %d uri %d dri %d\n", seek, c->uri, c->dri);
+			n = unionread(c, r->data, n);
+		}
+		else{
+			c->offset = off;
+			n = devtab[c->type]->read(c, r->data, n, off);
+			lock(c);
+			c->offset += n;
+			unlock(c);
+		}
+		f->offset = off + n;
+		if(n == 0 || !seek)
+			break;
+	}
+	r->count = n;
+
+	poperror();
+	Exputfid(fs, f);
+	return nil;
+}
+
+static char*
+Exwrite(Export *fs, Fcall *t, Fcall *r)
+{
+	Fid *f;
+	Chan *c;
+
+	f = Exgetfid(fs, t->fid);
+	if(f == nil)
+		return Enofid;
+	if(waserror()){
+		Exputfid(fs, f);
+		return up->env->errstr;
+	}
+	c = f->chan;
+	if((c->flag & COPEN) == 0)
+		error(Emode);
+	if(c->mode != OWRITE && c->mode != ORDWR)
+		error(Eaccess);
+	if(c->qid.type & QTDIR)
+		error(Eisdir);
+	if(t->count < 0 || t->count > fs->msize-IOHDRSZ)
+		error(Ecount);
+	if(t->offset < 0)
+		error(Enegoff);
+	r->count = devtab[c->type]->write(c, t->data, t->count, t->offset);
+	poperror();
+	Exputfid(fs, f);
+	return nil;
+}
+
+static char*
+Exstat(Export *fs, Fcall *t, Fcall *r)
+{
+	Fid *f;
+	Chan *c;
+	int n;
+
+	f = Exgetfid(fs, t->fid);
+	if(f == nil)
+		return Enofid;
+	c = exmount(f->chan, nil, 1);
+	if(waserror()){
+		cclose(c);
+		Exputfid(fs, f);
+		return up->env->errstr;
+	}
+	n = devtab[c->type]->stat(c, r->stat, r->nstat);
+	if(n <= BIT16SZ)
+		error(Eshortstat);
+	r->nstat = n;
+	poperror();
+	cclose(c);
+	Exputfid(fs, f);
+	return nil;
+}
+
+static char*
+Exwstat(Export *fs, Fcall *t, Fcall *r)
+{
+	Fid *f;
+	Chan *c;
+
+	USED(r);
+	f = Exgetfid(fs, t->fid);
+	if(f == nil)
+		return Enofid;
+	if(waserror()){
+		Exputfid(fs, f);
+		return up->env->errstr;
+	}
+	validstat(t->stat, t->nstat);	/* check name */
+
+	c = exmount(f->chan, nil, 0);
+	if(waserror()){
+		cclose(c);
+		nexterror();
+	}
+	devtab[c->type]->wstat(c, t->stat, t->nstat);
+	poperror();
+
+	cclose(c);
+	poperror();
+	Exputfid(fs, f);
+	return nil;
+}
+
+static char*
+Exremove(Export *fs, Fcall *t, Fcall *r)
+{
+	Fid *f;
+	Chan *c;
+
+	USED(r);
+	f = Exgetfid(fs, t->fid);
+	if(f == nil)
+		return Enofid;
+	if(waserror()){
+		f->attached = 0;
+		Exputfid(fs, f);
+		return up->env->errstr;
+	}
+	c = exmount(f->chan, nil, 0);
+	if(waserror()){
+		c->type = 0;	/* see below */
+		cclose(c);
+		nexterror();
+	}
+	devtab[c->type]->remove(c);
+	poperror();
+	poperror();
+
+	/*
+	 * chan is already clunked by remove.
+	 * however, we need to recover the chan,
+	 * and follow sysremove's lead in making it point to root.
+	 */
+	c->type = 0;
+
+	cclose(c);
+	f->attached = 0;
+	Exputfid(fs, f);
+	return nil;
+}
+
+/*
+ * unique path generation
+ */
+
+static int
+uqidhash(vlong path)
+{
+	ulong p;
+	p = (ulong)path;
+	return ((p>>16) ^ (p>>8) ^ p) & (Nqidhash-1);
+}
+
+static Uqid **
+uqidlook(Uqid **tab, Chan *c, vlong path)
+{
+	Uqid **hp, *q;
+
+	for(hp = &tab[uqidhash(path)]; (q = *hp) != nil; hp = &q->next)
+		if(q->type == c->type && q->dev == c->dev && q->oldpath == path)
+			break;
+	return hp;
+}
+
+static int
+uqidexists(Uqid **tab, vlong path)
+{
+	int i;
+	Uqid *q;
+
+	for(i=0; i<Nqidhash; i++)
+		for(q = tab[i]; q != nil; q = q->next)
+			if(q->newpath == path)
+				return 1;
+	return 0;
+}
+
+static Uqid *
+uqidalloc(Export *fs, Chan *c)
+{
+	Uqid **hp, *q;
+
+	qlock(&fs->qidlock);
+	hp = uqidlook(fs->qids, c, c->qid.path);
+	if((q = *hp) != nil){
+		incref(q);
+		qunlock(&fs->qidlock);
+		return q;
+	}
+	q = mallocz(sizeof(*q), 1);
+	if(q == nil){
+		qunlock(&fs->qidlock);
+		error(Enomem);
+	}
+	q->ref = 1;
+	q->type = c->type;
+	q->dev = c->dev;
+	q->oldpath = c->qid.path;
+	q->newpath = c->qid.path;
+	while(uqidexists(fs->qids, q->newpath)){
+		if(++fs->pathgen >= (1<<16))
+			fs->pathgen = 1;
+		q->newpath = ((vlong)fs->pathgen<<48) | (q->newpath & QIDMASK);
+	}
+	q->next = nil;
+	*hp = q;
+	qunlock(&fs->qidlock);
+	return q;
+}
+
+static void
+freeuqid(Export *fs, Uqid *q)
+{
+	Uqid **hp;
+
+	if(q == nil)
+		return;
+	qlock(&fs->qidlock);
+	if(decref(q) == 0){
+		hp = &fs->qids[uqidhash(q->oldpath)];
+		for(; *hp != nil; hp = &(*hp)->next)
+			if(*hp == q){
+				*hp = q->next;
+				free(q);
+				break;
+			}
+	}
+	qunlock(&fs->qidlock);
+}
--- /dev/null
+++ b/os/port/flashamd29f0x0.c
@@ -1,0 +1,167 @@
+#include	"u.h"
+#include	"../port/lib.h"
+#include	"mem.h"
+#include	"dat.h"
+#include	"fns.h"
+#include	"io.h"
+#include	"../port/error.h"
+
+#include	"../port/flashif.h"
+
+/*
+ * AMD29F0x0 with 4 interleaved to give 32 bits
+ */
+
+enum {
+	DQ7 = 0x80808080,
+	DQ6 = 0x40404040,
+	DQ5 = 0x20202020,
+	DQ3 = 0x08080808,
+	DQ2 = 0x04040404,
+};
+
+#define	DPRINT	if(0)print
+#define	EPRINT	if(1)print
+
+static char*
+amdwait(ulong *p, ulong ticks)
+{
+	ulong v0, v1;
+
+	ticks += m->ticks+1;
+	v0 = *p;
+	for(;;){
+		sched();
+		v1 = *p;
+		if((v1 & DQ6) == (v0 & DQ6))
+			break;
+		if((v1 & DQ5) == DQ5){
+			v0 = *p;
+			v1 = *p;
+			if((v1 & DQ6) == (v0 & DQ6))
+				break;
+			EPRINT("flash: DQ5 error: %8.8lux %8.8lux\n", v0, v1);
+			return "flash write error";
+		}
+		if(m->ticks >= ticks){
+			EPRINT("flash: timed out: %8.8lux\n", *p);
+			return "flash write timed out";
+		}
+		v0 = v1;
+	}
+	return nil;
+}
+
+static int
+eraseall(Flash *f)
+{
+	ulong *p;
+	int s;
+	char *e;
+
+	DPRINT("flash: erase all\n");
+	p = (ulong*)f->addr;
+	s = splhi();
+	*(p+0x555) = 0xAAAAAAAA;
+	*(p+0x2AA) = 0x55555555;
+	*(p+0x555) = 0x80808080;
+	*(p+0x555) = 0xAAAAAAAA;
+	*(p+0x2AA) = 0x55555555;
+	*(p+0x555) = 0x10101010;	/* chip erase */
+	splx(s);
+	e = amdwait(p, MS2TK(64*1000));
+	*p = 0xF0F0F0F0;	/* reset */
+	if(e != nil)
+		error(e);
+	return 0;
+}
+
+static int
+erasezone(Flash *f, Flashregion *r, ulong addr)
+{
+	ulong *p;
+	int s;
+	char *e;
+
+	DPRINT("flash: erase %8.8lux\n", addr);
+	if(addr & (r->erasesize-1))
+		return -1;	/* bad zone */
+	p = (ulong*)f->addr;
+	s = splhi();
+	*(p+0x555) = 0xAAAAAAAA;
+	*(p+0x2AA) = 0x55555555;
+	*(p+0x555) = 0x80808080;
+	*(p+0x555) = 0xAAAAAAAA;
+	*(p+0x2AA) = 0x55555555;
+	p += addr>>2;
+	*p = 0x30303030;	/* sector erase */
+	splx(s);
+	e = amdwait(p, MS2TK(8*1000));
+	*p = 0xF0F0F0F0;	/* reset */
+	if(e != nil)
+		error(e);
+	return 0;
+}
+
+static int
+write4(Flash *f, ulong offset, void *buf, long n)
+{
+	ulong *p, *a, *v, w;
+	int s;
+	char *e;
+
+	p = (ulong*)f->addr;
+	if(((ulong)p|offset|n)&3)
+		return -1;
+	n >>= 2;
+	a = p + (offset>>2);
+	v = buf;
+	for(; --n >= 0; v++, a++){
+		w = *a;
+		DPRINT("flash: write %lux %lux -> %lux\n", (ulong)a, w, *v);
+		if(w == *v)
+			continue;	/* already set */
+		if(~w & *v)
+			error("flash not erased");
+		s = splhi();
+		*(p+0x555) = 0xAAAAAAAA;
+		*(p+0x2AA) = 0x55555555;
+		*(p+0x555) = 0xA0A0A0A0;	/* program */
+		*a = *v;
+		splx(s);
+		microdelay(8);
+		if(*a != *v){
+			microdelay(8);
+			while(*a != *v){
+				e = amdwait(a, 1);
+				if(e != nil)
+					error(e);
+			}
+		}
+	}
+	return 0;
+}
+
+static int
+reset(Flash *f)
+{
+	f->id = 0x01;	/* can't use autoselect: might be running in flash */
+	f->devid = 0;
+	f->write = write4;
+	f->eraseall = eraseall;
+	f->erasezone = erasezone;
+	f->suspend = nil;
+	f->resume = nil;
+	f->width = 4;
+	f->interleave = 0;	/* TO DO */
+	f->nr = 1;
+	f->regions[0] = (Flashregion){f->size/(4*64*1024), 0, f->size, 4*64*1024, 0};
+	*(ulong*)f->addr = 0xF0F0F0F0;	/* reset (just in case) */
+	return 0;
+}
+
+void
+flashamd29f0x0link(void)
+{
+	addflashcard("AMD29F0x0", reset);
+}
--- /dev/null
+++ b/os/port/flashcfi16.c
@@ -1,0 +1,134 @@
+#include	"u.h"
+#include	"../port/lib.h"
+#include	"mem.h"
+#include	"dat.h"
+#include	"fns.h"
+#include	"io.h"
+#include	"../port/error.h"
+
+#include	"../port/flashif.h"
+
+/*
+ * Common Flash Interface (1x16 and 2x16)
+ */
+
+/* interleaved flash has chips at even and odd word addresses */
+#define	I(x)	(((x)<<24)|((x)<<16)|((x)<<8)|(x))
+
+enum {
+	ReadArray = I(0xFF),
+	ReadQuery = I(0x98),
+};
+
+#include "flashintel"
+
+static int
+cfiget1(Flash *f, ulong a)
+{
+	ulong v;
+
+	v = flashget(f, a);
+//iprint("%.8lux->%.4ux\n", a, v);
+	if(f->width == 2 && v == 0xFFFF)
+		return 0;	/* get this on old, partially-conforming CFI */
+	return v & 0xFF;
+}
+
+static int
+cfiget2(Flash *f, ulong i)
+{
+	return (cfiget1(f, i+1)<<8) | cfiget1(f, i);
+}
+
+static int
+cfiquery(Flash *f)
+{
+	Flashregion *r;
+	ulong addr;
+	int i;
+
+	flashput(f, 0x55, ReadQuery);
+	if(!(cfiget1(f, 0x10) == 'Q' && cfiget1(f, 0x11) == 'R' && cfiget1(f, 0x12) == 'Y'))	/* TO DO: detect interleave */
+		return 0;
+	f->alg = cfiget2(f, 0x13);
+	i = cfiget1(f, 0x27);
+	if(i > 0 && i < 32)
+		i = 1<<i;
+	else
+		i = 0;
+	f->devsize = i;
+	f->size = f->devsize;
+	if(f->interleave)
+		f->size *= 2;
+	i = cfiget2(f, 0x2A);
+	if(i > 0 && i < 32)
+		i = 1<<i;
+	else
+		i = 0;
+	f->maxwb = i;
+	f->nr = cfiget1(f, 0x2C);
+	if(f->nr != 0){
+		addr = 0;
+		for(i=0; i<f->nr; i++){
+			r = &f->regions[i];
+			r->n = cfiget2(f, 0x2D+4*i)+1;
+			r->erasesize = cfiget2(f, 0x2D+2+4*i)*256;
+			if(r->erasesize == 0)
+				r->erasesize = 128;
+			if(f->interleave)
+				r->erasesize *= 2;	/* TO DO */
+			r->start = addr;
+			r->end = r->start + r->n*r->erasesize;
+		}
+		if(1){
+			iprint("cfi: devsize=%lud maxwb=%d\n", f->devsize, f->maxwb);
+			for(i=0; i<f->nr; i++){
+				r = &f->regions[i];
+				iprint("flash %d: %d %lud %8.8lux %8.8lux\n", i, r->n, r->erasesize, r->start, r->end);
+			}
+		}
+	}else{
+		f->nr = 1;
+		f->regions[0] = (Flashregion){1, 0, f->devsize, f->devsize, 0};
+	}
+	return 1;
+}
+
+static int
+reset(Flash *f)
+{
+	if(f->xip)
+		return -1;	/* can't use this interface if executing from flash */
+	if(f->width == 0)
+		f->width = 2;
+	if(!cfiquery(f) || f->alg != 1 && f->alg != 3){
+		/* apparently not CFI: try to reset to read mode before return */
+		flashput(f, 0x55, ClearStatus);
+		flashput(f, 0x55, ReadArray);
+		return -1;
+	}
+	f->cmask = 0x00FF00FF;
+	flashput(f, 0x55, ClearStatus);
+	flashput(f, 0x55, ReadID);
+	f->id = cfiget1(f, 0x00);
+	f->devid = cfiget1(f, 0x01);
+	flashput(f, 0x55, ClearStatus);
+	flashput(f, 0x55, ReadArray);
+	if(f->width == 2){
+		f->cmask = 0x00FF;
+		f->write = intelwrite2;
+	}else{
+		f->cmask = 0x00FF00FF;
+		f->write = intelwrite4;
+	}
+	f->erasezone = intelerase;
+	f->suspend = nil;
+	f->resume = nil;
+	return 0;
+}
+
+void
+flashcfi16link(void)
+{
+	addflashcard("cfi16", reset);
+}
--- /dev/null
+++ b/os/port/flashcfi8.c
@@ -1,0 +1,143 @@
+#include	"u.h"
+#include	"../port/lib.h"
+#include	"mem.h"
+#include	"dat.h"
+#include	"fns.h"
+#include	"io.h"
+#include	"../port/error.h"
+
+#include	"../port/flashif.h"
+
+/*
+ * Common Flash Interface (1x8 and 2x8)
+ */
+
+/* interleaved flash has chips at even and odd word addresses */
+#define	I(x)	(((x)<<24)|((x)<<16)|((x)<<8)|(x))
+
+enum {
+	ReadArray = I(0xFF),
+	ReadQuery = I(0x98),
+};
+
+/* TO DO: flash amd */
+#include "flashintel"
+
+static int
+cfiget1(Flash *f, ulong a)
+{
+	ulong v;
+
+	v = flashget(f, a);
+//iprint("%.8lux->%.4ux\n", a, v);
+	return v & 0xFF;
+}
+
+static int
+cfiget2(Flash *f, ulong i)
+{
+	return (cfiget1(f, i+1)<<8) | cfiget1(f, i);
+}
+
+static int
+cfiquery(Flash *f)
+{
+	Flashregion *r;
+	ulong addr;
+	int i;
+
+	flashput(f, 0x55, ReadQuery);
+	if(!(cfiget1(f, 0x10) == 'Q' && cfiget1(f, 0x11) == 'R' && cfiget1(f, 0x12) == 'Y'))	/* TO DO: detect interleave */
+		return 0;
+	f->alg = cfiget2(f, 0x13);
+	i = cfiget1(f, 0x27);
+	if(i > 0 && i < 32)
+		i = 1<<i;
+	else
+		i = 0;
+	f->devsize = i;
+	f->size = f->devsize;
+	if(f->interleave)
+		f->size *= 2;
+	i = cfiget2(f, 0x2A);
+	if(i > 0 && i < 32)
+		i = 1<<i;
+	else
+		i = 0;
+	f->maxwb = i;
+	f->nr = cfiget1(f, 0x2C);
+	if(f->nr != 0){
+		addr = 0;
+		for(i=0; i<f->nr; i++){
+			r = &f->regions[i];
+			r->n = cfiget2(f, 0x2D+4*i)+1;
+			r->erasesize = cfiget2(f, 0x2D+2+4*i)*256;
+			if(r->erasesize == 0)
+				r->erasesize = 128;
+			if(f->interleave)
+				r->erasesize *= 2;	/* TO DO */
+			r->start = addr;
+			r->end = r->start + r->n*r->erasesize;
+		}
+		if(1){
+			iprint("cfi: devsize=%lud maxwb=%d\n", f->devsize, f->maxwb);
+			for(i=0; i<f->nr; i++){
+				r = &f->regions[i];
+				iprint("flash %d: %d %lud %8.8lux %8.8lux\n", i, r->n, r->erasesize, r->start, r->end);
+			}
+		}
+	}else{
+		f->nr = 1;
+		f->regions[0] = (Flashregion){1, 0, f->devsize, f->devsize, 0};
+	}
+	return 1;
+}
+
+static int
+reset(Flash *f)
+{
+	if(f->xip)
+		return -1;	/* can't use this interface if executing from flash */
+	if(f->width == 0)
+		f->width = 1;
+	if(!cfiquery(f) && f->bshift==0)
+		f->bshift = 1;	/* try this */
+	if(!cfiquery(f) || f->alg != 1 && f->alg != 3){
+		if(f->alg == 2){
+			print("amd algorithm\n");
+			goto ok;
+		}
+		/* apparently not CFI: try to reset to read mode before return */
+		flashput(f, 0x55, ClearStatus);
+		flashput(f, 0x55, ReadArray);
+		return -1;
+	}
+ok:
+	switch(f->width){
+	case 1:
+		f->cmask = 0x00FF;
+		break;
+	case 2:
+		f->cmask = 0xFFFF;
+		break;
+	case 4:
+		f->cmask = 0xFFFFFFFF;
+		break;
+	}
+	flashput(f, 0x55, ClearStatus);
+	flashput(f, 0x55, ReadID);
+	f->id = cfiget1(f, 0x00);
+	f->devid = cfiget1(f, 0x01);
+	flashput(f, 0x55, ClearStatus);
+	flashput(f, 0x55, ReadArray);
+	f->erasezone = nil;
+	f->suspend = nil;
+	f->resume = nil;
+	return 0;
+}
+
+void
+flashcfi8link(void)
+{
+	addflashcard("cfi8", reset);
+}
--- /dev/null
+++ b/os/port/flashif.h
@@ -1,0 +1,147 @@
+typedef struct Flash Flash;
+typedef struct Flashchip Flashchip;
+typedef struct Flashpart Flashpart;
+typedef struct Flashregion Flashregion;
+
+/*
+ * logical partitions
+ */
+enum {
+	Maxflashpart = 8
+};
+
+struct Flashpart {
+	char*	name;
+	ulong	start;
+	ulong	end;
+};
+
+enum {
+	Maxflashregion = 4
+};
+
+/*
+ * physical erase block regions
+ */
+struct Flashregion {
+	int	n;	/* number of blocks in region */
+	ulong	start;	/* physical base address (allowing for banks) */
+	ulong	end;
+	ulong	erasesize;
+	ulong	pagesize;	/* if non-zero, the size of pages within the erase block */
+};
+
+/*
+ * one of a set of chips in a given region
+ */
+struct Flashchip {
+	int	nr;
+	Flashregion	regions[Maxflashregion];
+
+	uchar	id;	/* flash manufacturer ID */
+	ushort	devid;	/* flash device ID */
+	int	width;	/* bytes per flash line */
+	int	maxwb;	/* max write buffer size */
+	ulong	devsize;	/* physical device size */
+	int	alg;	/* programming algorithm (if CFI) */
+	int	protect;	/* software protection */
+};
+
+/*
+ * structure defining a contiguous region of flash memory
+ */
+struct Flash {
+	QLock;	/* interlock on flash operations */
+	Flash*	next;
+
+	/* the following are filled in before calling Flash.reset */
+	char*	type;
+	void*	addr;
+	ulong	size;
+	int	xip;	/* executing in place: don't query */
+	int	(*reset)(Flash*);
+
+	/* the following are filled in by the reset routine */
+	int	(*eraseall)(Flash*);
+	int	(*erasezone)(Flash*, Flashregion*, ulong);
+	int	(*read)(Flash*, ulong, void*, long);	/* (optional) reads of correct width and alignment */
+	int	(*write)(Flash*, ulong, void*, long);	/* writes of correct width and alignment */
+	int	(*suspend)(Flash*);
+	int	(*resume)(Flash*);
+	int	(*attach)(Flash*);
+
+	/* the following might be filled in by either archflashreset or the reset routine */
+	int	nr;
+	Flashregion	regions[Maxflashregion];
+
+	uchar	id;	/* flash manufacturer ID */
+	ushort	devid;	/* flash device ID */
+	int	width;	/* bytes per flash line */
+	int	interleave;	/* addresses are interleaved across set of chips */
+	int	bshift;	/* byte addresses are shifted */
+	ulong	cmask;	/* command mask for interleaving */
+	int	maxwb;	/* max write buffer size */
+	ulong	devsize;	/* physical device size */
+	int	alg;	/* programming algorithm (if CFI) */
+	void*	data;		/* flash type routines' private storage, or nil */
+	Flashpart	part[Maxflashpart];	/* logical partitions */
+	int	protect;	/* software protection */
+	char*	sort;	/* "nand", "nor", "serial", nil (unspecified) */
+};
+
+/*
+ * called by link routine of driver for specific flash type: arguments are
+ * conventional name for card type/model, and card driver's reset routine.
+ */
+void	addflashcard(char*, int (*)(Flash*));
+
+/*
+ * called by devflash.c:/^flashreset; if flash exists,
+ * sets type, address, and size in bytes of flash
+ * and returns 0; returns -1 if flash doesn't exist
+ */
+int	archflashreset(int, Flash*);
+
+/*
+ * enable/disable write protect
+ */
+void	archflashwp(Flash*, int);
+
+/*
+ * flash access taking width and interleave into account
+ */
+int	flashget(Flash*, ulong);
+void	flashput(Flash*, ulong, int);
+
+/*
+ * Architecture specific routines for managing nand devices
+ */
+
+/*
+ * do any device spcific initialisation
+ */
+void archnand_init(Flash*);
+
+/*
+ * if claim is 1, claim device exclusively, and enable it (power it up)
+ * if claim is 0, release, and disable it (power it down)
+ * claiming may be as simple as a qlock per device
+ */
+void archnand_claim(Flash*, int claim);
+
+/*
+ * set command latch enable (CLE) and address latch enable (ALE)
+ * appropriately
+ */
+void archnand_setCLEandALE(Flash*, int cle, int ale);
+
+/*
+ * write a sequence of bytes to the device
+ */
+void archnand_write(Flash*, void *buf, int len);
+
+/*
+ * read a sequence of bytes from the device
+ * if buf is 0, throw away the data
+ */
+void archnand_read(Flash*, void *buf, int len);
--- /dev/null
+++ b/os/port/flashintel
@@ -1,0 +1,179 @@
+
+enum {
+	DQ7 = I(0x80),
+	DQ6 = I(0x40),
+	DQ5 = I(0x20),
+	DQ4 = I(0x10),
+	DQ3 = I(0x08),
+	DQ2 = I(0x04),
+	DQ1 = I(0x02),
+	DQ0 = I(0x01),
+};
+
+/*
+ * intel algorithm
+ */
+
+enum {
+	ReadID = I(0x90),
+	ClearStatus = I(0x50),
+	ReadStatus = I(0x70),
+	Program = I(0x40),
+	BlockErase = I(0x20),
+	Confirm = I(0xD0),
+};
+
+#define	DPRINT	if(0)print
+#define	EPRINT	if(1)print
+
+static char*
+intelwait(Flash *f, void *p, ulong ticks)
+{
+	ulong csr, mask;
+
+	ticks += m->ticks+1;
+	mask = f->cmask;
+	for(;;){
+		if(f->width == 2)
+			csr = *(ushort*)p;
+		else
+			csr = *(ulong*)p;
+		if((csr & mask) == (DQ7 & mask))
+			break;
+		sched();
+		if(m->ticks >= ticks)
+			return "flash write timed out";
+	}
+	if(csr & (DQ5|DQ4|DQ3|DQ1)){
+		EPRINT("flash: error: %8.8lux %8.8lux\n", p, csr);
+		if(csr & DQ1)
+			return "flash block locked";
+		if(csr & DQ3)
+			return "low flash programming voltage";
+		return Eio;
+	}
+	return nil;
+}
+
+static int
+intelerase(Flash *f, Flashregion *r, ulong addr)
+{
+	int s;
+	char *e;
+
+	DPRINT("flash: erase zone %8.8lux\n", addr);
+	if(addr & (r->erasesize-1))
+		return -1;	/* bad zone */
+	if(f->width == 2){
+		ushort *p = (ushort*)((ulong)f->addr + addr);
+		s = splhi();
+		*p = BlockErase & f->cmask;
+		*p = Confirm & f->cmask;
+		splx(s);
+		e = intelwait(f, p, MS2TK(8*1000));
+		*p = ClearStatus & f->cmask;
+		*p = ReadArray & f->cmask;
+	}else{
+		ulong *p = (ulong*)((ulong)f->addr + addr);
+		s = splhi();
+		*p = BlockErase & f->cmask;
+		*p = Confirm & f->cmask;
+		splx(s);
+		e = intelwait(f, p, MS2TK(8*1000));
+		*p = ClearStatus & f->cmask;
+		*p = ReadArray & f->cmask;
+	}
+	if(e != nil)
+		error(e);
+	return 0;
+}
+
+static int
+intelwrite2(Flash *f, ulong offset, void *buf, long n)
+{
+	ushort *a, *v;
+	ulong w;
+	int s;
+	char *e;
+
+	if(((ulong)f->addr|offset|n)&(f->width-1))
+		return -1;
+	a = (ushort*)((ulong)f->addr + offset);
+	n /= f->width;
+	v = buf;
+	if(waserror()){
+		*a = ClearStatus & f->cmask;
+		*a = ReadArray & f->cmask;
+		nexterror();
+	}
+	for(; --n >= 0; v++, a++){
+		w = *a;
+		DPRINT("flash: write %p %#ulx -> %#lux\n", a, w, (ulong)*v);
+		if(w == *v)
+			continue;	/* already set */
+		if(~w & *v)
+			error("flash not erased");
+		s = splhi();
+		*a = Program & f->cmask;	/* program */
+		*a = *v;
+		splx(s);
+		microdelay(8);
+		e = intelwait(f, a, 5);
+		*a = ClearStatus & f->cmask;
+		*a = ReadArray & f->cmask;
+		if(e != nil)
+			error(e);
+		w = *a;
+		if(w != *v){
+			EPRINT("flash: write %p %#8.8lux -> %#8.8lux failed\n", a, w, (ulong)*v);
+			error(Eio);
+		}
+	}
+	poperror();
+	return 0;
+}
+
+static int
+intelwrite4(Flash *f, ulong offset, void *buf, long n)
+{
+	ulong *a, *v;
+	ulong w;
+	int s;
+	char *e;
+
+	if(((ulong)f->addr|offset|n)&(f->width-1))
+		return -1;
+	a = (ulong*)((ulong)f->addr + offset);
+	n /= f->width;
+	v = buf;
+	if(waserror()){
+		*a = ClearStatus & f->cmask;
+		*a = ReadArray & f->cmask;
+		nexterror();
+	}
+	for(; --n >= 0; v++, a++){
+		w = *a;
+		DPRINT("flash: write %p %#ulx -> %#lux\n", a, w, (ulong)*v);
+		if(w == *v)
+			continue;	/* already set */
+		if(~w & *v)
+			error("flash not erased");
+		s = splhi();
+		*a = Program;	/* program */
+		*a = *v;
+		splx(s);
+		microdelay(8);
+		e = intelwait(f, a, 5);
+		*a = ClearStatus & f->cmask;
+		*a = ReadArray & f->cmask;
+		if(e != nil)
+			error(e);
+		w = *a;
+		if(w != *v){
+			EPRINT("flash: write %p %#8.8lux -> %#8.8lux failed\n", a, w, *v);
+			error(Eio);
+		}
+	}
+	poperror();
+	return 0;
+}
--- /dev/null
+++ b/os/port/flashnand.c
@@ -1,0 +1,337 @@
+#include	"u.h"
+#include	"../port/lib.h"
+#include	"mem.h"
+#include	"dat.h"
+#include	"fns.h"
+#include	"io.h"
+#include	"../port/error.h"
+
+#include	"flashif.h"
+
+typedef struct Nandtab Nandtab;
+
+struct Nandtab {
+	short manufacturer;
+	uchar id;
+	uchar l2bytesperpage;
+	ushort pagesperblock;
+	ushort blocks;
+	uchar tPROGms;
+	ushort tBERASEms;
+	uchar tRus;
+};
+
+static Nandtab nandtab[] = {
+	{ 0xec, 	0xe6,	9,		16,		1024,	1,		4,		7 },	/* Samsung KM29U64000T */
+
+	{ 0x98,	0xe6,	9,		16,		1024,	1,		4,		25 }, /* Toshiba TC58V64AFT */
+	{ 0x98,	0x73,	9,		32,		1024,	1,		10,		25},	/* Toshiba TC56V128AFT */
+	/* Generic entries which take timings from Toshiba SMIL example code */
+	{ -1,		0xea,	8,		16,		512,		20,		400,		100	 },
+	{ -1,		0xe3,	9,		16,		512,		20,		400,		100	 },
+	{ -1,		0xe5,	9,		16,		512,		20,		400,		100	 },
+	{ -1,		0x73,	9,		32,		1024,	20,		400,		100	 },
+	{ -1,		0x75,	9,		32,		2048,	20,		400,		100	 },
+	{ -1,		0x76,	9,		32,		4096,	20,		400,		100	 },
+};
+
+enum {
+	ReadMode1 = 0x00,
+	ReadMode2 = 0x01,
+	Program = 0x10,
+	ReadMode3 = 0x50,
+	Erase1 = 0x60,
+	ReadStatus = 0x70,
+	Write = 0x80,
+	Identify = 0x90,
+	Erase2 = 0xd0,
+
+	StatusReady = 0x40,
+	StatusFail = 0x01,
+};
+
+/*
+ * NAND flash driver
+ */
+
+#define	DPRINT	if(0)print
+#define	EPRINT	if(1)print
+
+static int idchip(Flash *f);
+
+static void
+nand_writebyte(Flash *f, uchar b)
+{
+	archnand_write(f, &b, 1);
+}
+
+static uchar
+nand_readbyte(Flash *f)
+{
+	uchar b;
+	archnand_read(f, &b, 1);
+	return b;
+}
+
+static int
+idchip(Flash *f)
+{
+	int x;
+	uchar maker, device;
+
+	f->id = 0;
+	f->devid = 0;
+	f->width = 1;
+	archnand_claim(f, 1);
+	archnand_setCLEandALE(f, 1, 0);
+	nand_writebyte(f, Identify);
+	archnand_setCLEandALE(f, 0, 1);
+	nand_writebyte(f, 0);
+	archnand_setCLEandALE(f, 0, 0);
+	maker = nand_readbyte(f);
+	device = nand_readbyte(f);
+	archnand_claim(f, 0);
+	iprint("man=%#ux device=%#ux\n", maker, device);
+	for(x = 0; x < sizeof(nandtab) / sizeof(nandtab[0]); x++){
+		if(nandtab[x].id == (device & 0xff)
+			&& (nandtab[x].manufacturer == maker || nandtab[x].manufacturer == -1)){
+			ulong bpp;
+			f->id = maker;
+			f->devid = device;
+			f->nr = 1;
+			bpp = 1 << nandtab[x].l2bytesperpage;
+			bpp |= bpp >> 5;
+			f->regions[0].erasesize = bpp * nandtab[x].pagesperblock;
+			f->size = f->regions[0].erasesize * nandtab[x].blocks;
+			f->regions[0].n = nandtab[x].blocks;
+			f->regions[0].start = 0;
+			f->regions[0].end = f->size;
+			f->regions[0].pagesize = bpp;
+			f->data = &nandtab[x];
+			return 0;
+		}
+	}
+	print("nand: device %#.2ux/%#.2ux not recognised\n", maker, device);
+	return -1;
+}
+
+static int
+erasezone(Flash *f, Flashregion *r, ulong byteaddr)
+{
+	Nandtab *nt = f->data;
+	int paddress;
+	uchar val;
+	int rv;
+	uchar addr[2];
+
+	if(byteaddr%r->erasesize || byteaddr >= f->size)
+		return -1;	/* bad zone */
+	paddress = byteaddr/r->erasesize * nt->pagesperblock;	/* can simplify ... */
+//print("erasezone(%.8lux) page %d %.8lux\n", byteaddr, paddress, r->erasesize);
+	archnand_claim(f, 1);
+	archnand_setCLEandALE(f, 1, 0);		// command mode
+	nand_writebyte(f, Erase1);
+	archnand_setCLEandALE(f, 0, 1);		// address mode
+	addr[0] = paddress;
+	addr[1] = paddress >> 8;
+	archnand_write(f, addr, 2);
+	archnand_setCLEandALE(f, 1, 0);		// command mode
+	nand_writebyte(f, Erase2);
+	nand_writebyte(f, ReadStatus);
+	archnand_setCLEandALE(f, 0, 0);		// data mode
+
+	do {
+		val = nand_readbyte(f);
+	} while((val & StatusReady) != StatusReady);
+
+	if((val & StatusFail) != 0){
+		print("erasezone failed: %.2ux\n", val);
+		rv = -1;
+	}
+	else
+		rv = 0;
+	archnand_claim(f, 0);				// turn off chip
+	return rv;
+}
+
+static int
+writepage(Flash *f, ulong page, ushort addr, void *buf, long n)
+{
+	uchar cmd;
+	uchar val;
+	int rv;
+	uchar cmdbuf[3];
+
+//print("writepage(%ld, %d, %ld)\n", page, addr, n);
+	// Fake a read to set the pointer
+	if(addr < 256)
+		cmd = ReadMode1;
+	else if(addr < 512){
+		cmd = ReadMode2;
+		addr -= 256;
+	}else{
+		cmd = ReadMode3;
+		addr -= 512;
+	}
+	archnand_claim(f, 1);
+	archnand_setCLEandALE(f, 1, 0);		// command mode
+	nand_writebyte(f, cmd);
+	nand_writebyte(f, Write);
+	archnand_setCLEandALE(f, 0, 1);		// address mode
+	cmdbuf[0] = addr;
+	cmdbuf[1] = page;
+	cmdbuf[2] = page >> 8;
+	archnand_write(f, cmdbuf, 3);
+	archnand_setCLEandALE(f, 0, 0);		// data mode
+	archnand_write(f, buf, n);
+	archnand_setCLEandALE(f, 1, 0);		// command mode
+	nand_writebyte(f, Program);
+	nand_writebyte(f, ReadStatus);
+	archnand_setCLEandALE(f, 0, 0);		// data mode
+
+	do {
+		val = nand_readbyte(f);
+	}while((val & StatusReady) != StatusReady);
+
+	if((val & StatusFail) != 0){
+		print("writepage failed: %.2ux\n", val);
+		rv = -1;
+	}else
+		rv = 0;
+
+	archnand_claim(f, 0);
+	return rv;
+}
+
+static int
+write(Flash *f, ulong offset, void *buf, long n)
+{
+	Nandtab *nt = f->data;
+	ulong page;
+	ulong addr;
+	ulong xbpp;
+
+//print("write(%ld, %ld)\n", offset, n);
+
+	xbpp = (1 << nt->l2bytesperpage);
+	xbpp |= (xbpp >> 5);
+	page = offset / xbpp;
+	addr = offset % xbpp;
+
+	while(n > 0){
+		int count;
+		count = xbpp - addr;
+		if(count > n)
+			count = n;
+		if(writepage(f, page, addr, buf, count) < 0)
+			return -1;
+		offset += count;
+		n -= count;
+		buf = (uchar *)buf + count;
+		addr = 0;
+	}
+//print("write done\n");
+	return 0;
+}
+
+static int
+read(Flash *f, ulong offset, void *buf, long n)
+{
+	Nandtab *nt = f->data;
+	uchar cmd;
+	ulong page;
+	ulong addr;
+	ushort bytesperpage, xbytesperpage, skip, partialaddr;
+	uchar cmdbuf[3];
+	int toread;
+
+//print("read(%ld, %.8lux, %ld)\n", offset, buf, n);
+
+	bytesperpage = (1 << nt->l2bytesperpage);
+	xbytesperpage = bytesperpage;
+	xbytesperpage += bytesperpage >> 5;	// 512 => 16, 256 => 8
+	page = offset / xbytesperpage;
+	partialaddr = offset % xbytesperpage;
+	skip = 0;
+	if(partialaddr >= bytesperpage && xbytesperpage - partialaddr < n){
+		// cannot start read in extended area, and then chain into main area,
+		// so start on last byte of main area, and skip the extra bytes
+		// stupid chip design this one
+		skip = partialaddr - bytesperpage + 1;
+		n += skip;
+		partialaddr = bytesperpage - 1;
+	}
+	addr = partialaddr;
+	if(addr >= bytesperpage){
+		cmd = ReadMode3;
+		addr -= bytesperpage;
+	}else if(addr >= 256){
+		cmd = ReadMode2;
+		addr -= 256;
+	}else
+		cmd = ReadMode1;
+
+//print("cmd %.2x page %.4lux addr %.8lux partialaddr %d skip %d\n", cmd, page, addr, partialaddr, skip);
+	// Read first page
+	archnand_claim(f, 1);
+	archnand_setCLEandALE(f, 1, 0);
+	nand_writebyte(f, cmd);
+	archnand_setCLEandALE(f, 0, 1);
+	cmdbuf[0] = addr;
+	cmdbuf[1] = page;
+	cmdbuf[2] = page >> 8;
+	archnand_write(f, cmdbuf, 3);
+	archnand_setCLEandALE(f, 0, 0);
+	if(partialaddr){
+		// partial first page
+		microdelay(nt->tRus);
+		toread = partialaddr < xbytesperpage ? xbytesperpage - partialaddr : 0;
+		if(toread > n)
+			toread = n;
+		if(skip){
+			archnand_read(f, 0, skip);
+			toread -= skip;
+			n -= skip;
+//			partialaddr += skip;
+		}
+		archnand_read(f, buf, toread);
+		n -= toread;
+//		partialaddr += toread;
+		buf = (uchar *)buf + toread;
+	}
+	while(n){
+		microdelay(nt->tRus);
+		toread = xbytesperpage;
+		if(n < toread)
+			toread = n;
+		archnand_read(f, buf, toread);
+		n -= toread;
+		buf = (uchar *)buf + toread;
+	}
+	archnand_claim(f, 0);
+//print("readn done\n");
+	return 0;
+}
+
+static int
+reset(Flash *f)
+{
+//iprint("nandreset\n");
+	if(f->data != nil)
+		return 1;
+	f->write = write;
+ 	f->read = read;
+	f->eraseall = nil;
+	f->erasezone = erasezone;
+	f->suspend = nil;
+	f->resume = nil;
+	f->sort = "nand";
+	archnand_init(f);
+	return idchip(f);
+}
+
+void
+flashnandlink(void)
+{
+	addflashcard("nand", reset);
+}
--- /dev/null
+++ b/os/port/fpi.c
@@ -1,0 +1,304 @@
+/*
+ * Floating Point Interpreter.
+ * shamelessly stolen from an original by ark.
+ */
+#include "fpi.h"
+
+void
+fpiround(Internal *i)
+{
+	unsigned long guard;
+
+	guard = i->l & GuardMask;
+	i->l &= ~GuardMask;
+	if(guard > (LsBit>>1) || (guard == (LsBit>>1) && (i->l & LsBit))){
+		i->l += LsBit;
+		if(i->l & CarryBit){
+			i->l &= ~CarryBit;
+			i->h++;
+			if(i->h & CarryBit){
+				if (i->h & 0x01)
+					i->l |= CarryBit;
+				i->l >>= 1;
+				i->h >>= 1;
+				i->e++;
+			}
+		}
+	}
+}
+
+static void
+matchexponents(Internal *x, Internal *y)
+{
+	int count;
+
+	count = y->e - x->e;
+	x->e = y->e;
+	if(count >= 2*FractBits){
+		x->l = x->l || x->h;
+		x->h = 0;
+		return;
+	}
+	if(count >= FractBits){
+		count -= FractBits;
+		x->l = x->h|(x->l != 0);
+		x->h = 0;
+	}
+	while(count > 0){
+		count--;
+		if(x->h & 0x01)
+			x->l |= CarryBit;
+		if(x->l & 0x01)
+			x->l |= 2;
+		x->l >>= 1;
+		x->h >>= 1;
+	}
+}
+
+static void
+shift(Internal *i)
+{
+	i->e--;
+	i->h <<= 1;
+	i->l <<= 1;
+	if(i->l & CarryBit){
+		i->l &= ~CarryBit;
+		i->h |= 0x01;
+	}
+}
+
+static void
+normalise(Internal *i)
+{
+	while((i->h & HiddenBit) == 0)
+		shift(i);
+}
+
+static void
+renormalise(Internal *i)
+{
+	if(i->e < -2 * FractBits)
+		i->e = -2 * FractBits;
+	while(i->e < 1){
+		i->e++;
+		if(i->h & 0x01)
+			i->l |= CarryBit;
+		i->h >>= 1;
+		i->l = (i->l>>1)|(i->l & 0x01);
+	}
+	if(i->e >= ExpInfinity)
+		SetInfinity(i);
+}
+
+void
+fpinormalise(Internal *x)
+{
+	if(!IsWeird(x) && !IsZero(x))
+		normalise(x);
+}
+
+void
+fpiadd(Internal *x, Internal *y, Internal *i)
+{
+	Internal *t;
+
+	i->s = x->s;
+	if(IsWeird(x) || IsWeird(y)){
+		if(IsNaN(x) || IsNaN(y))
+			SetQNaN(i);
+		else
+			SetInfinity(i);
+		return;
+	}
+	if(x->e > y->e){
+		t = x;
+		x = y;
+		y = t;
+	}
+	matchexponents(x, y);
+	i->e = x->e;
+	i->h = x->h + y->h;
+	i->l = x->l + y->l;
+	if(i->l & CarryBit){
+		i->h++;
+		i->l &= ~CarryBit;
+	}
+	if(i->h & (HiddenBit<<1)){
+		if(i->h & 0x01)
+			i->l |= CarryBit;
+		i->l = (i->l>>1)|(i->l & 0x01);
+		i->h >>= 1;
+		i->e++;
+	}
+	if(IsWeird(i))
+		SetInfinity(i);
+}
+
+void
+fpisub(Internal *x, Internal *y, Internal *i)
+{
+	Internal *t;
+
+	if(y->e < x->e
+	   || (y->e == x->e && (y->h < x->h || (y->h == x->h && y->l < x->l)))){
+		t = x;
+		x = y;
+		y = t;
+	}
+	i->s = y->s;
+	if(IsNaN(y)){
+		SetQNaN(i);
+		return;
+	}
+	if(IsInfinity(y)){
+		if(IsInfinity(x))
+			SetQNaN(i);
+		else
+			SetInfinity(i);
+		return;
+	}
+	matchexponents(x, y);
+	i->e = y->e;
+	i->h = y->h - x->h;
+	i->l = y->l - x->l;
+	if(i->l < 0){
+		i->l += CarryBit;
+		i->h--;
+	}
+	if(i->h == 0 && i->l == 0)
+		SetZero(i);
+	else while(i->e > 1 && (i->h & HiddenBit) == 0)
+		shift(i);
+}
+
+#define	CHUNK		(FractBits/2)
+#define	CMASK		((1<<CHUNK)-1)
+#define	HI(x)		((short)((x)>>CHUNK) & CMASK)
+#define	LO(x)		((short)(x) & CMASK)
+#define	SPILL(x)	((x)>>CHUNK)
+#define	M(x, y)		((long)a[x]*(long)b[y])
+#define	C(h, l)		(((long)((h) & CMASK)<<CHUNK)|((l) & CMASK))
+
+void
+fpimul(Internal *x, Internal *y, Internal *i)
+{
+	long a[4], b[4], c[7], f[4];
+
+	i->s = x->s^y->s;
+	if(IsWeird(x) || IsWeird(y)){
+		if(IsNaN(x) || IsNaN(y) || IsZero(x) || IsZero(y))
+			SetQNaN(i);
+		else
+			SetInfinity(i);
+		return;
+	}
+	else if(IsZero(x) || IsZero(y)){
+		SetZero(i);
+		return;
+	}
+	normalise(x);
+	normalise(y);
+	i->e = x->e + y->e - (ExpBias - 1);
+
+	a[0] = HI(x->h); b[0] = HI(y->h);
+	a[1] = LO(x->h); b[1] = LO(y->h);
+	a[2] = HI(x->l); b[2] = HI(y->l);
+	a[3] = LO(x->l); b[3] = LO(y->l);
+
+	c[6] =                               M(3, 3);
+	c[5] =                     M(2, 3) + M(3, 2) + SPILL(c[6]);
+	c[4] =           M(1, 3) + M(2, 2) + M(3, 1) + SPILL(c[5]);
+	c[3] = M(0, 3) + M(1, 2) + M(2, 1) + M(3, 0) + SPILL(c[4]);
+	c[2] = M(0, 2) + M(1, 1) + M(2, 0)           + SPILL(c[3]);
+	c[1] = M(0, 1) + M(1, 0)                     + SPILL(c[2]);
+	c[0] = M(0, 0)                               + SPILL(c[1]);
+
+	f[0] = c[0];
+	f[1] = C(c[1], c[2]);
+	f[2] = C(c[3], c[4]);
+	f[3] = C(c[5], c[6]);
+
+	if((f[0] & HiddenBit) == 0){
+		f[0] <<= 1;
+		f[1] <<= 1;
+		f[2] <<= 1;
+		f[3] <<= 1;
+		if(f[1] & CarryBit){
+			f[0] |= 1;
+			f[1] &= ~CarryBit;
+		}
+		if(f[2] & CarryBit){
+			f[1] |= 1;
+			f[2] &= ~CarryBit;
+		}
+		if(f[3] & CarryBit){
+			f[2] |= 1;
+			f[3] &= ~CarryBit;
+		}
+		i->e--;
+	}
+	i->h = f[0];
+	i->l = f[1];
+	if(f[2] || f[3])
+		i->l |= 1;
+	renormalise(i);
+}
+
+void
+fpidiv(Internal *x, Internal *y, Internal *i)
+{
+	i->s = x->s^y->s;
+	if(IsNaN(x) || IsNaN(y)
+	   || (IsInfinity(x) && IsInfinity(y)) || (IsZero(x) && IsZero(y))){
+		SetQNaN(i);
+		return;
+	}
+	else if(IsZero(x) || IsInfinity(y)){
+		SetInfinity(i);
+		return;
+	}
+	else if(IsInfinity(x) || IsZero(y)){
+		SetZero(i);
+		return;
+	}
+	normalise(x);
+	normalise(y);
+	i->h = 0;
+	i->l = 0;
+	i->e = y->e - x->e + (ExpBias + 2*FractBits - 1);
+	do{
+		if(y->h > x->h || (y->h == x->h && y->l >= x->l)){
+			i->l |= 0x01;
+			y->h -= x->h;
+			y->l -= x->l;
+			if(y->l < 0){
+				y->l += CarryBit;
+				y->h--;
+			}
+		}
+		shift(y);
+		shift(i);
+	}while ((i->h & HiddenBit) == 0);
+/*
+	if(y->h > x->h || (y->h == x->h && y->l >= x->l))
+		i->l |= 0x01;
+*/
+	if(y->h || y->l)
+		i->l |= 0x01;
+	renormalise(i);
+}
+
+int
+fpicmp(Internal *x, Internal *y)
+{
+	if(IsNaN(x) && IsNaN(y))
+		return 0;
+	if(IsInfinity(x) && IsInfinity(y))
+		return y->s - x->s;
+	if(x->e == y->e && x->h == y->h && x->l == y->l)
+		return y->s - x->s;
+	if(x->e < y->e
+	   || (x->e == y->e && (x->h < y->h || (x->h == y->h && x->l < y->l))))
+		return y->s ? 1: -1;
+	return x->s ? -1: 1;
+}
--- /dev/null
+++ b/os/port/fpimem.c
@@ -1,0 +1,136 @@
+#include "fpi.h"
+
+/*
+ * the following routines depend on memory format, not the machine
+ */
+
+void
+fpis2i(Internal *i, void *v)
+{
+	Single *s = v;
+
+	i->s = (*s & 0x80000000) ? 1: 0;
+	if((*s & ~0x80000000) == 0){
+		SetZero(i);
+		return;
+	}
+	i->e = ((*s>>23) & 0x00FF) - SingleExpBias + ExpBias;
+	i->h = (*s & 0x007FFFFF)<<(1+NGuardBits);
+	i->l = 0;
+	if(i->e)
+		i->h |= HiddenBit;
+	else
+		i->e++;
+}
+
+void
+fpid2i(Internal *i, void *v)
+{
+	Double *d = v;
+
+	i->s = (d->h & 0x80000000) ? 1: 0;
+	i->e = (d->h>>20) & 0x07FF;
+	i->h = ((d->h & 0x000FFFFF)<<(4+NGuardBits))|((d->l>>25) & 0x7F);
+	i->l = (d->l & 0x01FFFFFF)<<NGuardBits;
+	if(i->e)
+		i->h |= HiddenBit;
+	else
+		i->e++;
+}
+
+void
+fpiw2i(Internal *i, void *v)
+{
+	Word w, word = *(Word*)v;
+	int e;
+
+	if(word < 0){
+		i->s = 1;
+		word = -word;
+	}
+	else
+		i->s = 0;
+	if(word == 0){
+		SetZero(i);
+		return;
+	}
+	if(word > 0){
+		for (e = 0, w = word; w; w >>= 1, e++)
+			;
+	} else
+		e = 32;
+	if(e > FractBits){
+		i->h = word>>(e - FractBits);
+		i->l = (word & ((1<<(e - FractBits)) - 1))<<(2*FractBits - e);
+	}
+	else {
+		i->h = word<<(FractBits - e);
+		i->l = 0;
+	}
+	i->e = (e - 1) + ExpBias;
+}
+
+void
+fpii2s(void *v, Internal *i)
+{
+	int e;
+	Single *s = (Single*)v;
+
+	fpiround(i);
+	if(i->h & HiddenBit)
+		i->h &= ~HiddenBit;
+	else
+		i->e--;
+	*s = i->s ? 0x80000000: 0;
+	e = i->e;
+	if(e < ExpBias){
+		if(e <= (ExpBias - SingleExpBias))
+			return;
+		e = SingleExpBias - (ExpBias - e);
+	}
+	else  if(e >= (ExpBias + (SingleExpMax-SingleExpBias))){
+		*s |= SingleExpMax<<23;
+		return;
+	}
+	else
+		e = SingleExpBias + (e - ExpBias);
+	*s |= (e<<23)|(i->h>>(1+NGuardBits));
+}
+
+void
+fpii2d(void *v, Internal *i)
+{
+	Double *d = (Double*)v;
+
+	fpiround(i);
+	if(i->h & HiddenBit)
+		i->h &= ~HiddenBit;
+	else
+		i->e--;
+	i->l = ((i->h & GuardMask)<<25)|(i->l>>NGuardBits);
+	i->h >>= NGuardBits;
+	d->h = i->s ? 0x80000000: 0;
+	d->h |= (i->e<<20)|((i->h & 0x00FFFFFF)>>4);
+	d->l = (i->h<<28)|i->l;
+}
+
+void
+fpii2w(Word *word, Internal *i)
+{
+	Word w;
+	int e;
+
+	fpiround(i);
+	e = (i->e - ExpBias) + 1;
+	if(e <= 0)
+		w = 0;
+	else if(e > 31)
+		w = 0x7FFFFFFF;
+	else if(e > FractBits)
+		w = (i->h<<(e - FractBits))|(i->l>>(2*FractBits - e));
+	else
+		w = i->h>>(FractBits-e);
+	if(i->s)
+		w = -w;
+	*word = w;
+}
--- /dev/null
+++ b/os/port/inferno.c
@@ -1,0 +1,1063 @@
+#include "u.h"
+#include "../port/lib.h"
+#include "mem.h"
+#include "dat.h"
+#include "fns.h"
+#include "../port/error.h"
+#include "isa.h"
+#include "interp.h"
+#include "runt.h"
+#include "kernel.h"
+
+/*
+ * here because Sys_FileIO is not public
+ */
+extern	int	srvf2c(char*, char*, Sys_FileIO*);
+
+/*
+ * System types connected to gc
+ */
+uchar	FDmap[] = Sys_FD_map;
+uchar	FileIOmap[] = Sys_FileIO_map;
+void	freeFD(Heap*, int);
+void	freeFileIO(Heap*, int);
+Type*	TFD;
+Type*	TFileIO;
+
+static	uchar	rmap[] = Sys_FileIO_read_map;
+static	uchar	wmap[] = Sys_FileIO_write_map;
+static	Type*	FioTread;
+static	Type*	FioTwrite;
+static	uchar	dmap[] = Sys_Dir_map;
+static	Type*	Tdir;
+
+typedef struct FD FD;
+struct FD
+{
+	Sys_FD	fd;
+	Fgrp*	grp;
+};
+
+void
+sysinit(void)
+{
+	TFD = dtype(freeFD, sizeof(FD), FDmap, sizeof(FDmap));
+	TFileIO = dtype(freeFileIO, Sys_FileIO_size, FileIOmap, sizeof(FileIOmap));
+
+	/* Support for devsrv.c */
+	FioTread = dtype(freeheap, Sys_FileIO_read_size, rmap, sizeof(rmap));
+	FioTwrite = dtype(freeheap, Sys_FileIO_write_size, wmap, sizeof(wmap));
+
+	/* Support for dirread */
+	Tdir = dtype(freeheap, Sys_Dir_size, dmap, sizeof(dmap));
+}
+
+void
+freeFD(Heap *h, int swept)
+{
+	FD *handle;
+
+	USED(swept);
+
+	handle = H2D(FD*, h);
+
+	release();
+	if(handle->fd.fd >= 0)
+		kfgrpclose(handle->grp, handle->fd.fd);
+	closefgrp(handle->grp);
+	acquire();
+}
+
+void
+freeFileIO(Heap *h, int swept)
+{
+	Sys_FileIO *fio;
+
+	if(swept)
+		return;
+
+	fio = H2D(Sys_FileIO*, h);
+	destroy(fio->read);
+	destroy(fio->write);
+}
+
+Sys_FD*
+mkfd(int fd)
+{
+	Heap *h;
+	Fgrp *fg;
+	FD *handle;
+
+	h = heap(TFD);
+	handle = H2D(FD*, h);
+	handle->fd.fd = fd;
+	fg = up->env->fgrp;
+	handle->grp = fg;
+	incref(fg);
+	return (Sys_FD*)handle;
+}
+#define fdchk(x)	((x) == (Sys_FD*)H ? -1 : (x)->fd)
+
+void
+seterror(char *err, ...)
+{
+	char *estr;
+	va_list arg;
+
+	estr = up->env->errstr;
+	va_start(arg, err);
+	vseprint(estr, estr+ERRMAX, err, arg);
+	va_end(arg);
+}
+
+char*
+syserr(char *s, char *es, Prog *p)
+{
+	Osenv *o;
+
+	o = p->osenv;
+	kstrcpy(s, o->errstr, es - s);
+	return s + strlen(s);
+}
+
+void
+Sys_millisec(void *fp)
+{
+	F_Sys_millisec *f;
+
+	f = fp;
+	*f->ret = TK2MS(MACHP(0)->ticks);
+}
+
+void
+Sys_open(void *fp)
+{
+	int fd;
+	F_Sys_open *f;
+
+	f = fp;
+	destroy(*f->ret);
+	*f->ret = H;
+	release();
+	fd = kopen(string2c(f->s), f->mode);
+	acquire();
+	if(fd == -1)
+		return;
+
+	*f->ret = mkfd(fd);
+}
+
+void
+Sys_pipe(void *fp)
+{
+	Array *a;
+	int fd[2];
+	Sys_FD **sfd;
+	F_Sys_pipe *f;
+
+	f = fp;
+	*f->ret = -1;
+
+	a = f->fds;
+	if(a->len < 2)
+		return;
+	if(kpipe(fd) < 0)
+		return;
+
+	sfd = (Sys_FD**)a->data;
+	destroy(sfd[0]);
+	destroy(sfd[1]);
+	sfd[0] = H;
+	sfd[1] = H;
+	sfd[0] = mkfd(fd[0]);
+	sfd[1] = mkfd(fd[1]);
+	*f->ret = 0;
+}
+
+void
+Sys_fildes(void *fp)
+{
+	F_Sys_fildes *f;
+	int fd;
+
+	f = fp;
+	destroy(*f->ret);
+	*f->ret = H;
+	release();
+	fd = kdup(f->fd, -1);
+	acquire();
+	if(fd == -1)
+		return;
+	*f->ret = mkfd(fd);
+}
+
+void
+Sys_dup(void *fp)
+{
+	F_Sys_dup *f;
+
+	f = fp;
+	release();
+	*f->ret = kdup(f->old, f->new);	
+	acquire();
+}
+
+void
+Sys_create(void *fp)
+{
+	int fd;
+	F_Sys_create *f;
+
+	f = fp;
+	destroy(*f->ret);
+	*f->ret = H;
+	release();
+	fd = kcreate(string2c(f->s), f->mode, f->perm);
+	acquire();
+	if(fd == -1)
+		return;
+
+	*f->ret = mkfd(fd);
+}
+
+void
+Sys_remove(void *fp)
+{
+	F_Sys_remove *f;
+
+	f = fp;
+	release();
+	*f->ret = kremove(string2c(f->s));
+	acquire();
+}
+
+void
+Sys_seek(void *fp)
+{
+	F_Sys_seek *f;
+
+	f = fp;
+	release();
+	*f->ret = kseek(fdchk(f->fd), f->off, f->start);
+	acquire();
+}
+
+void
+Sys_unmount(void *fp)
+{
+	F_Sys_unmount *f;
+
+	f = fp;
+	release();
+	*f->ret = kunmount(string2c(f->s1), string2c(f->s2));
+	acquire();
+}
+
+void
+Sys_read(void *fp)
+{
+	int n;
+	F_Sys_read *f;
+
+	f = fp;
+	n = f->n;
+	if(f->buf == (Array*)H || n < 0) {
+		*f->ret = 0;
+		return;		
+	}
+	if(n > f->buf->len)
+		n = f->buf->len;
+
+	release();
+	*f->ret = kread(fdchk(f->fd), f->buf->data, n);
+	acquire();
+}
+
+void
+Sys_readn(void *fp)
+{
+	int fd, m, n, t;
+	F_Sys_readn *f;
+
+	f = fp;
+	n = f->n;
+	if(f->buf == (Array*)H || n < 0) {
+		*f->ret = 0;
+		return;		
+	}
+	if(n > f->buf->len)
+		n = f->buf->len;
+	fd = fdchk(f->fd);
+
+	release();
+	for(t = 0; t < n; t += m){
+		m = kread(fd, (char*)f->buf->data+t, n-t);
+		if(m <= 0){
+			if(t == 0)
+				t = m;
+			break;
+		}
+	}
+	*f->ret = t;
+	acquire();
+}
+
+void
+Sys_pread(void *fp)
+{
+	int n;
+	F_Sys_pread *f;
+
+	f = fp;
+	n = f->n;
+	if(f->buf == (Array*)H || n < 0) {
+		*f->ret = 0;
+		return;		
+	}
+	if(n > f->buf->len)
+		n = f->buf->len;
+
+	release();
+	*f->ret = kpread(fdchk(f->fd), f->buf->data, n, f->off);
+	acquire();
+}
+
+void
+Sys_chdir(void *fp)
+{
+	F_Sys_chdir *f;
+
+	f = fp;
+	release();
+	*f->ret = kchdir(string2c(f->path));
+	acquire();
+}
+
+void
+Sys_write(void *fp)
+{
+	int n;
+	F_Sys_write *f;
+
+	f = fp;
+	n = f->n;
+	if(f->buf == (Array*)H || n < 0) {
+		*f->ret = 0;
+		return;		
+	}
+	if(n > f->buf->len)
+		n = f->buf->len;
+
+	release();
+	*f->ret = kwrite(fdchk(f->fd), f->buf->data, n);
+	acquire();
+}
+
+void
+Sys_pwrite(void *fp)
+{
+	int n;
+	F_Sys_pwrite *f;
+
+	f = fp;
+	n = f->n;
+	if(f->buf == (Array*)H || n < 0) {
+		*f->ret = 0;
+		return;		
+	}
+	if(n > f->buf->len)
+		n = f->buf->len;
+
+	release();
+	*f->ret = kpwrite(fdchk(f->fd), f->buf->data, n, f->off);
+	acquire();
+}
+
+static void
+unpackdir(Dir *d, Sys_Dir *sd)
+{
+	retstr(d->name, &sd->name);
+	retstr(d->uid, &sd->uid);
+	retstr(d->gid, &sd->gid);
+	retstr(d->muid, &sd->muid);
+	sd->qid.path = d->qid.path;
+	sd->qid.vers = d->qid.vers;
+	sd->qid.qtype = d->qid.type;
+	sd->mode = d->mode;
+	sd->atime = d->atime;
+	sd->mtime = d->mtime;
+	sd->length = d->length;
+	sd->dtype = d->type;
+	sd->dev = d->dev;
+}
+
+static Dir*
+packdir(Sys_Dir *sd)
+{
+	char *nm[4], *p;
+	int i, n;
+	Dir *d;
+
+	nm[0] = string2c(sd->name);
+	nm[1] = string2c(sd->uid);
+	nm[2] = string2c(sd->gid);
+	nm[3] = string2c(sd->muid);
+	n = 0;
+	for(i=0; i<4; i++)
+		n += strlen(nm[i])+1;
+	d = smalloc(sizeof(*d)+n);
+	p = (char*)d+sizeof(*d);
+	for(i=0; i<4; i++){
+		n = strlen(nm[i])+1;
+		memmove(p, nm[i], n);
+		nm[i] = p;
+		p += n;
+	}
+	d->name = nm[0];
+	d->uid = nm[1];
+	d->gid = nm[2];
+	d->muid = nm[3];
+	d->qid.path = sd->qid.path;
+	d->qid.vers = sd->qid.vers;
+	d->qid.type = sd->qid.qtype;
+	d->mode = sd->mode;
+	d->atime = sd->atime;
+	d->mtime = sd->mtime;
+	d->length = sd->length;
+	d->type = sd->dtype;
+	d->dev = sd->dev;
+	return d;
+}
+
+void
+Sys_fstat(void *fp)
+{
+	Dir *d;
+	F_Sys_fstat *f;
+
+	f = fp;
+	f->ret->t0 = -1;
+	release();
+	d = kdirfstat(fdchk(f->fd));
+	acquire();
+	if(d == nil)
+		return;
+	if(waserror() == 0){
+		unpackdir(d, &f->ret->t1);
+		f->ret->t0 = 0;
+		poperror();
+	}
+	free(d);
+}
+
+void
+Sys_stat(void *fp)
+{
+	Dir *d;
+	F_Sys_stat *f;
+
+	f = fp;
+	f->ret->t0 = -1;
+	release();
+	d = kdirstat(string2c(f->s));
+	acquire();
+	if(d == nil)
+		return;
+	if(waserror() == 0){
+		unpackdir(d, &f->ret->t1);
+		f->ret->t0 = 0;
+		poperror();
+	}
+	free(d);
+}
+
+void
+Sys_fd2path(void *fp)
+{
+	F_Sys_fd2path *f;
+	char *s;
+	void *r;
+
+	f = fp;
+	r = *f->ret;
+	*f->ret = H;
+	destroy(r);
+	release();
+	s = kfd2path(fdchk(f->fd));
+	acquire();
+	if(waserror() == 0){
+		retstr(s, f->ret);
+		poperror();
+	}
+	free(s);
+}
+
+void
+Sys_mount(void *fp)
+{
+	F_Sys_mount *f;
+
+	f = fp;
+	release();
+	*f->ret = kmount(fdchk(f->fd), fdchk(f->afd), string2c(f->on), f->flags, string2c(f->spec));
+	acquire();
+}
+
+void
+Sys_bind(void *fp)
+{
+	F_Sys_bind *f;
+
+	f = fp;
+	release();
+	*f->ret = kbind(string2c(f->s), string2c(f->on), f->flags);
+	acquire();
+}
+
+void
+Sys_wstat(void *fp)
+{
+	Dir *d;
+	F_Sys_wstat *f;
+
+	f = fp;
+	d = packdir(&f->d);
+	release();
+	*f->ret = kdirwstat(string2c(f->s), d);
+	acquire();
+	free(d);
+}
+
+void
+Sys_fwstat(void *fp)
+{
+	Dir *d;
+	F_Sys_fwstat *f;
+
+	f = fp;
+	d = packdir(&f->d);
+	release();
+	*f->ret = kdirfwstat(fdchk(f->fd), d);
+	acquire();
+	free(d);
+}
+
+void
+Sys_print(void *fp)
+{
+	int n;
+	Prog *p;
+	Chan *c;
+	char buf[1024], *b = buf;
+	F_Sys_print *f;
+	f = fp;
+	c = up->env->fgrp->fd[1];
+	if(c == nil)
+		return;
+	p = currun();
+
+	release();
+	n = xprint(p, f, &f->vargs, f->s, buf, sizeof(buf));
+	if (n >= sizeof(buf)-UTFmax-2)
+		n = bigxprint(p, f, &f->vargs, f->s, &b, sizeof(buf));
+	*f->ret = kwrite(1, b, n);
+	if (b != buf)
+		free(b);
+	acquire();
+}
+
+void
+Sys_fprint(void *fp)
+{
+	int n;
+	Prog *p;
+	char buf[1024], *b = buf;
+	F_Sys_fprint *f;
+
+	f = fp;
+	p = currun();
+	release();
+	n = xprint(p, f, &f->vargs, f->s, buf, sizeof(buf));
+	if (n >= sizeof(buf)-UTFmax-2)
+		n = bigxprint(p, f, &f->vargs, f->s, &b, sizeof(buf));
+	*f->ret = kwrite(fdchk(f->fd), b, n);
+	if (b != buf)
+		free(b);
+	acquire();
+}
+
+void
+Sys_werrstr(void *fp)
+{
+	F_Sys_werrstr *f;
+
+	f = fp;
+	*f->ret = 0;
+	kstrcpy(up->env->errstr, string2c(f->s), ERRMAX);
+}
+
+void
+Sys_dial(void *fp)
+{
+	int cfd;
+	char dir[NETPATHLEN], *a, *l;
+	F_Sys_dial *f;
+
+	f = fp;
+	a = string2c(f->addr);
+	l = string2c(f->local);
+	release();
+	f->ret->t0 = kdial(a, l, dir, &cfd);
+	acquire();
+	destroy(f->ret->t1.dfd);
+	f->ret->t1.dfd = H;
+	destroy(f->ret->t1.cfd);
+	f->ret->t1.cfd = H;
+	if(f->ret->t0 == -1)
+		return;
+
+	f->ret->t1.dfd = mkfd(f->ret->t0);
+	f->ret->t0 = 0;
+	f->ret->t1.cfd = mkfd(cfd);
+	retstr(dir, &f->ret->t1.dir);
+}
+
+void
+Sys_announce(void *fp)
+{
+	char dir[NETPATHLEN], *a;
+	F_Sys_announce *f;
+
+	f = fp;
+	a = string2c(f->addr);
+	release();
+	f->ret->t0 = kannounce(a, dir);
+	acquire();
+	destroy(f->ret->t1.dfd);
+	f->ret->t1.dfd = H;
+	destroy(f->ret->t1.cfd);
+	f->ret->t1.cfd = H;
+	if(f->ret->t0 == -1)
+		return;
+
+	f->ret->t1.cfd = mkfd(f->ret->t0);
+	f->ret->t0 = 0;
+	retstr(dir, &f->ret->t1.dir);
+}
+
+void
+Sys_listen(void *fp)
+{
+	F_Sys_listen *f;
+	char dir[NETPATHLEN], *d;
+
+	f = fp;
+	d = string2c(f->c.dir);
+	release();
+	f->ret->t0 = klisten(d, dir);
+	acquire();
+
+	destroy(f->ret->t1.dfd);
+	f->ret->t1.dfd = H;
+	destroy(f->ret->t1.cfd);
+	f->ret->t1.cfd = H;
+	if(f->ret->t0 == -1)
+		return;
+
+	f->ret->t1.cfd = mkfd(f->ret->t0);
+	f->ret->t0 = 0;
+	retstr(dir, &f->ret->t1.dir);
+}
+
+void
+Sys_sleep(void *fp)
+{
+	F_Sys_sleep *f;
+
+	f = fp;
+	release();
+	if(f->period > 0){
+		if(waserror()){
+			acquire();
+			error("");
+		}
+		tsleep(&up->sleep, return0, 0, f->period);
+		poperror();
+	}
+	acquire();
+	*f->ret = 0;
+}
+
+void
+Sys_stream(void *fp)
+{
+	Prog *p;
+	uchar *buf;
+	int src, dst;
+	F_Sys_stream *f;
+	int nbytes, t, n;
+
+	f = fp;
+	buf = malloc(f->bufsiz);
+	if(buf == nil) {
+		kwerrstr(Enomem);
+		*f->ret = -1;
+		return;
+	}
+
+	src = fdchk(f->src);
+	dst = fdchk(f->dst);
+
+	p = currun();
+
+	release();
+	t = 0;
+	nbytes = 0;
+	while(p->kill == nil) {
+		n = kread(src, buf+t, f->bufsiz-t);
+		if(n <= 0)
+			break;
+		t += n;
+		if(t >= f->bufsiz) {
+			if(kwrite(dst, buf, t) != t) {
+				t = 0;
+				break;
+			}
+
+			nbytes += t;
+			t = 0;
+		}
+	}
+	if(t != 0) {
+		kwrite(dst, buf, t);
+		nbytes += t;
+	}
+	acquire();
+	free(buf);
+	*f->ret = nbytes;
+}
+
+void
+Sys_export(void *fp)
+{
+	F_Sys_export *f;
+
+	f = fp;
+	release();
+	*f->ret = export(fdchk(f->c), string2c(f->dir), f->flag&Sys_EXPASYNC);
+	acquire();
+}
+
+void
+Sys_file2chan(void *fp)
+{
+	int r;
+	Heap *h;
+	Channel *c;
+	Sys_FileIO *fio;
+	F_Sys_file2chan *f;
+	void *sv;
+
+	h = heap(TFileIO);
+
+	fio = H2D(Sys_FileIO*, h);
+
+	c = cnewc(FioTread, movtmp, 16);
+	fio->read = c;
+
+	c = cnewc(FioTwrite, movtmp, 16);
+	fio->write = c;
+
+	f = fp;
+	sv = *f->ret;
+	*f->ret = fio;
+	destroy(sv);
+
+	release();
+	r = srvf2c(string2c(f->dir), string2c(f->file), fio);
+	acquire();
+	if(r == -1) {
+		*f->ret = H;
+		destroy(fio);
+	}
+}
+
+enum
+{
+	/* the following pctl calls can block and must release the virtual machine */
+	BlockingPctl=	Sys_NEWFD|Sys_FORKFD|Sys_NEWNS|Sys_FORKNS|Sys_NEWENV|Sys_FORKENV
+};
+
+void
+Sys_pctl(void *fp)
+{
+	int fd;
+	Prog *p;
+	List *l;
+	Chan *c;
+	volatile struct {Pgrp *np;} np;
+	Pgrp *opg;
+	Chan *dot;
+	Osenv *o;
+	F_Sys_pctl *f;
+	Fgrp *fg, *ofg, *nfg;
+	volatile struct {Egrp *ne;} ne;
+	Egrp *oe;
+
+	f = fp;
+
+	p = currun();
+	if(f->flags & BlockingPctl)
+		release();
+
+	np.np = nil;
+	ne.ne = nil;
+	if(waserror()) {
+		closepgrp(np.np);
+		closeegrp(ne.ne);
+		if(f->flags & BlockingPctl)
+			acquire();
+		*f->ret = -1;
+		return;
+	}
+
+	o = p->osenv;
+	if(f->flags & Sys_NEWFD) {
+		ofg = o->fgrp;
+		nfg = newfgrp(ofg);
+		lock(ofg);
+		/* file descriptors to preserve */
+		for(l = f->movefd; l != H; l = l->tail) {
+			fd = *(int*)l->data;
+			if(fd >= 0 && fd <= ofg->maxfd) {
+				c = ofg->fd[fd];
+				if(c != nil && fd < nfg->nfd && nfg->fd[fd] == nil) {
+					incref(c);
+					nfg->fd[fd] = c;
+					if(nfg->maxfd < fd)
+						nfg->maxfd = fd;
+				}
+			}
+		}
+		unlock(ofg);
+		o->fgrp = nfg;
+		closefgrp(ofg);
+	}
+	else
+	if(f->flags & Sys_FORKFD) {
+		ofg = o->fgrp;
+		fg = dupfgrp(ofg);
+		/* file descriptors to close */
+		for(l = f->movefd; l != H; l = l->tail)
+			kclose(*(int*)l->data);
+		o->fgrp = fg;
+		closefgrp(ofg);
+	}
+
+	if(f->flags & Sys_NEWNS) {
+		np.np = newpgrp();
+		dot = o->pgrp->dot;
+		np.np->dot = cclone(dot);
+		np.np->slash = cclone(dot);
+		cnameclose(np.np->slash->name);
+		np.np->slash->name = newcname("/");
+		np.np->nodevs = o->pgrp->nodevs;
+		opg = o->pgrp;
+		o->pgrp = np.np;
+		np.np = nil;
+		closepgrp(opg);
+	}
+	else
+	if(f->flags & Sys_FORKNS) {
+		np.np = newpgrp();
+		pgrpcpy(np.np, o->pgrp);
+		opg = o->pgrp;
+		o->pgrp = np.np;
+		np.np = nil;
+		closepgrp(opg);
+	}
+
+	if(f->flags & Sys_NEWENV) {
+		oe = o->egrp;
+		o->egrp = newegrp();
+		closeegrp(oe);
+	}
+	else
+	if(f->flags & Sys_FORKENV) {
+		ne.ne = newegrp();
+		egrpcpy(ne.ne, o->egrp);
+		oe = o->egrp;
+		o->egrp = ne.ne;
+		ne.ne = nil;
+		closeegrp(oe);
+	}
+
+	if(f->flags & Sys_NEWPGRP)
+		newgrp(p);
+
+	if(f->flags & Sys_NODEVS)
+		o->pgrp->nodevs = 1;
+
+	poperror();
+
+	if(f->flags & BlockingPctl)
+		acquire();
+
+	*f->ret = p->pid;
+}
+
+void
+Sys_dirread(void *fp)
+{
+
+	Dir *b;
+	int i, n;
+	Heap *h;
+	uchar *d;
+	void *r;
+	F_Sys_dirread *f;
+
+	f = fp;
+	f->ret->t0 = -1;
+	r = f->ret->t1;
+	f->ret->t1 = H;
+	destroy(r);
+	release();
+	n = kdirread(fdchk(f->fd), &b);
+	acquire();
+	if(n <= 0) {
+		f->ret->t0 = n;
+		free(b);
+		return;
+	}
+	if(waserror()){
+		free(b);
+		return;
+	}
+	h = heaparray(Tdir, n);
+	poperror();
+	d = H2D(Array*, h)->data;
+	for(i = 0; i < n; i++) {
+		unpackdir(b+i, (Sys_Dir*)d);
+		d += Sys_Dir_size;
+	}
+	f->ret->t0 = n;
+	f->ret->t1 = H2D(Array*, h);
+	free(b);
+}
+
+void
+Sys_fauth(void *fp)
+{
+
+	int fd;
+	F_Sys_fauth *f;
+	void *r;
+
+	f = fp;
+	r = *f->ret;
+	*f->ret = H;
+	destroy(r);
+	release();
+	fd = kfauth(fdchk(f->fd), string2c(f->aname));
+	acquire();
+	if(fd >= 0)
+		*f->ret = mkfd(fd);
+}
+
+void
+Sys_fversion(void *fp)
+{
+	void *r;
+	F_Sys_fversion *f;
+	int n;
+	char buf[20], *s;
+
+	f = fp;
+	f->ret->t0 = -1;
+	r = f->ret->t1;
+	f->ret->t1 = H;
+	destroy(r);
+	s = string2c(f->version);
+	n = strlen(s);
+	if(n >= sizeof(buf)-1)
+		n = sizeof(buf)-1;
+	memmove(buf, s, n);
+	buf[n] = 0;
+	release();
+	n = kfversion(fdchk(f->fd), f->msize, buf, sizeof(buf));
+	acquire();
+	if(n >= 0){
+		f->ret->t0 = f->msize;
+		retnstr(buf, n, &f->ret->t1);
+	}
+}
+
+void
+Sys_iounit(void *fp)
+{
+	F_Sys_iounit *f;
+
+	f = fp;
+	release();
+	*f->ret = kiounit(fdchk(f->fd));
+	acquire();
+}
+
+void
+ccom(Progq **cl, Prog *p)
+{
+	volatile struct {Progq **cl;} vcl;
+
+	cqadd(cl, p);
+	vcl.cl = cl;
+	if(waserror()) {
+		if(p->ptr != nil) {	/* no killcomm */
+			cqdelp(vcl.cl, p);
+			p->ptr = nil;
+		}
+		nexterror();
+	}
+	cblock(p);
+	poperror();
+}
+
+void
+crecv(Channel *c, void *ip)
+{
+	Prog *p;
+	REG rsav;
+
+	if(c->send->prog == nil && c->size == 0) {
+		p = currun();
+		p->ptr = ip;
+		ccom(&c->recv, p);
+		return;
+	}
+
+	rsav = R;
+	R.s = &c;
+	R.d = ip;
+	irecv();
+	R = rsav;
+}
+
+void
+csend(Channel *c, void *ip)
+{
+ 	Prog *p;
+	REG rsav;
+
+	if(c->recv->prog == nil && (c->buf == H || c->size == c->buf->len)) {
+		p = currun();
+		p->ptr = ip;
+		ccom(&c->send, p);
+		return;
+	}
+
+	rsav = R;
+	R.s = ip;
+	R.d = &c;
+	isend();
+	R = rsav;
+}
--- /dev/null
+++ b/os/port/latin1.c
@@ -1,0 +1,76 @@
+#include <u.h>
+
+/*
+ * The code makes two assumptions: strlen(ld) is 1 or 2; latintab[i].ld can be a
+ * prefix of latintab[j].ld only when j<i.
+ */
+struct cvlist
+{
+	char	*ld;		/* must be seen before using this conversion */
+	char	*si;		/* options for last input characters */
+	void	*so;		/* the corresponding Rune for each si entry */
+} latintab[] = {
+#include "../port/latin1.h"
+	0,	0,		0
+};
+
+/*
+ * Given 5 characters k[0]..k[4], find the rune or return -1 for failure.
+ */
+long
+unicode(Rune *k)
+{
+	long i, c;
+
+	k++;	/* skip 'X' */
+	c = 0;
+	for(i=0; i<4; i++,k++){
+		c <<= 4;
+		if('0'<=*k && *k<='9')
+			c += *k-'0';
+		else if('a'<=*k && *k<='f')
+			c += 10 + *k-'a';
+		else if('A'<=*k && *k<='F')
+			c += 10 + *k-'A';
+		else
+			return -1;
+	}
+	return c;
+}
+
+/*
+ * Given n characters k[0]..k[n-1], find the corresponding rune or return -1 for
+ * failure, or something < -1 if n is too small.  In the latter case, the result
+ * is minus the required n.
+ */
+long
+latin1(Rune *k, int n)
+{
+	struct cvlist *l;
+	int c;
+	char* p;
+
+	if(k[0] == 'X')
+		if(n>=5)
+			return unicode(k);
+		else
+			return -5;
+	for(l=latintab; l->ld!=0; l++)
+		if(k[0] == l->ld[0]){
+			if(n == 1)
+				return -2;
+			if(l->ld[1] == 0)
+				c = k[1];
+			else if(l->ld[1] != k[1])
+				continue;
+			else if(n == 2)
+				return -3;
+			else
+				c = k[2];
+			for(p=l->si; *p!=0; p++)
+				if(*p == c)
+					return ((Rune*)l->so)[p - l->si];
+			return -1;
+		}
+	return -1;
+}
--- /dev/null
+++ b/os/port/latin1.h
@@ -1,0 +1,100 @@
+	" ", " i",	L"␣ı",
+	"!~", "-=~",	L"≄≇≉",
+	"!", "!<=>?bmp",	L"¡≮≠≯‽⊄∉⊅",
+	"\"*", "IUiu",	L"ΪΫϊϋ",
+	"\"", "\"AEIOUYaeiouy",	L"¨ÄËÏÖÜŸäëïöüÿ",
+	"$*", "fhk",	L"ϕϑϰ",
+	"$", "BEFHILMRVaefglopv",	L"ℬℰℱℋℐℒℳℛƲɑℯƒℊℓℴ℘ʋ",
+	"\'\"", "Uu",	L"Ǘǘ",
+	"\'", "\'ACEILNORSUYZacegilnorsuyz",	L"´ÁĆÉÍĹŃÓŔŚÚÝŹáćéģíĺńóŕśúýź",
+	"*", "*ABCDEFGHIKLMNOPQRSTUWXYZabcdefghiklmnopqrstuwxyz",	L"∗ΑΒΞΔΕΦΓΘΙΚΛΜΝΟΠΨΡΣΤΥΩΧΗΖαβξδεφγθικλμνοπψρστυωχηζ",
+	"+", "-O",	L"±⊕",
+	",", ",ACEGIKLNORSTUacegiklnorstu",	L"¸ĄÇĘĢĮĶĻŅǪŖŞŢŲąçęģįķļņǫŗşţų",
+	"-*", "l",	L"ƛ",
+	"-", "+-2:>DGHILOTZbdghiltuz~",	L"∓­ƻ÷→ÐǤĦƗŁ⊖ŦƵƀðǥℏɨłŧʉƶ≂",
+	".", ".CEGILOZceglz",	L"·ĊĖĠİĿ⊙Żċėġŀż",
+	"/", "Oo",	L"Øø",
+	"1", "234568",	L"½⅓¼⅕⅙⅛",
+	"2", "-35",	L"ƻ⅔⅖",
+	"3", "458",	L"¾⅗⅜",
+	"4", "5",	L"⅘",
+	"5", "68",	L"⅚⅝",
+	"7", "8",	L"⅞",
+	":", "()-=",	L"☹☺÷≔",
+	"<!", "=~",	L"≨⋦",
+	"<", "-<=>~",	L"←«≤≶≲",
+	"=", ":<=>OV",	L"≕⋜≡⋝⊜⇒",
+	">!", "=~",	L"≩⋧",
+	">", "<=>~",	L"≷≥»≳",
+	"?", "!?",	L"‽¿",
+	"@\'", "\'",	L"ъ",
+	"@@", "\'EKSTYZekstyz",	L"ьЕКСТЫЗекстыз",
+	"@C", "Hh",	L"ЧЧ",
+	"@E", "Hh",	L"ЭЭ",
+	"@K", "Hh",	L"ХХ",
+	"@S", "CHch",	L"ЩШЩШ",
+	"@T", "Ss",	L"ЦЦ",
+	"@Y", "AEOUaeou",	L"ЯЕЁЮЯЕЁЮ",
+	"@Z", "Hh",	L"ЖЖ",
+	"@c", "h",	L"ч",
+	"@e", "h",	L"э",
+	"@k", "h",	L"х",
+	"@s", "ch",	L"щш",
+	"@t", "s",	L"ц",
+	"@y", "aeou",	L"яеёю",
+	"@z", "h",	L"ж",
+	"@", "ABDFGIJLMNOPRUVXabdfgijlmnopruvx",	L"АБДФГИЙЛМНОПРУВХабдфгийлмнопрувх",
+	"A", "E",	L"Æ",
+	"C", "ACU",	L"⋂ℂ⋃",
+	"Dv", "Zz",	L"DŽDž",
+	"D", "-e",	L"Ð∆",
+	"G", "-",	L"Ǥ",
+	"H", "-H",	L"Ħℍ",
+	"I", "-J",	L"ƗIJ",
+	"L", "&-Jj|",	L"⋀ŁLJLj⋁",
+	"M", "#48bs",	L"♮♩♪♭♯",
+	"N", "JNj",	L"NJℕNj",
+	"O", "*+-./=EIcoprx",	L"⊛⊕⊖⊙⊘⊜ŒƢ©⊚℗®⊗",
+	"P", "P",	L"ℙ",
+	"Q", "Q",	L"ℚ",
+	"R", "R",	L"ℝ",
+	"S", "123S",	L"¹²³§",
+	"T", "-u",	L"Ŧ⊨",
+	"V", "=",	L"⇐",
+	"Y", "R",	L"Ʀ",
+	"Z", "-ACSZ",	L"Ƶℤ",
+	"^", "ACEGHIJOSUWYaceghijosuwy",	L"ÂĈÊĜĤÎĴÔŜÛŴŶâĉêĝĥîĵôŝûŵŷ",
+	"_\"", "AUau",	L"ǞǕǟǖ",
+	"_,", "Oo",	L"Ǭǭ",
+	"_.", "Aa",	L"Ǡǡ",
+	"_", "AEIOU_aeiou",	L"ĀĒĪŌŪ¯āēīōū",
+	"`\"", "Uu",	L"Ǜǜ",
+	"`", "AEIOUaeiou",	L"ÀÈÌÒÙàèìòù",
+	"a", "ben",	L"↔æ∠",
+	"b", "()+-0123456789=bknpqru",	L"₍₎₊₋₀₁₂₃₄₅₆₇₈₉₌♝♚♞♟♛♜•",
+	"c", "$Oagu",	L"¢©∩≅∪",
+	"dv", "z",	L"dž",
+	"d", "-adegz",	L"ð↓‡°†ʣ",
+	"e", "$lmns",	L"€⋯—–∅",
+	"f", "a",	L"∀",
+	"g", "$-r",	L"¤ǥ∇",
+	"h", "-v",	L"ℏƕ",
+	"i", "-bfjps",	L"ɨ⊆∞ij⊇∫",
+	"l", "\"$&\'-jz|",	L"“£∧‘łlj⋄∨",
+	"m", "iou",	L"µ∈×",
+	"n", "jo",	L"nj¬",
+	"o", "AOUaeiu",	L"Å⊚Ůåœƣů",
+	"p", "Odgrt",	L"℗∂¶∏∝",
+	"r", "\"\'O",	L"”’®",
+	"s", "()+-0123456789=abnoprstu",	L"⁽⁾⁺⁻⁰ⁱ⁲⁳⁴⁵⁶⁷⁸⁹⁼ª⊂ⁿº⊃√ß∍∑",
+	"t", "-efmsu",	L"ŧ∃∴™ς⊢",
+	"u", "-AEGIOUaegiou",	L"ʉĂĔĞĬŎŬ↑ĕğĭŏŭ",
+	"v\"", "Uu",	L"Ǚǚ",
+	"v", "ACDEGIKLNORSTUZacdegijklnorstuz",	L"ǍČĎĚǦǏǨĽŇǑŘŠŤǓŽǎčďěǧǐǰǩľňǒřšťǔž",
+	"w", "bknpqr",	L"♗♔♘♙♕♖",
+	"x", "O",	L"⊗",
+	"y", "$",	L"¥",
+	"z", "-",	L"ƶ",
+	"|", "Pp|",	L"Þþ¦",
+	"~!", "=",	L"≆",
+	"~", "-=AINOUainou~",	L"≃≅ÃĨÑÕŨãĩñõũ≈",
--- /dev/null
+++ b/os/port/lib.h
@@ -1,0 +1,218 @@
+
+/*
+ * functions (possibly) linked in, complete, from libc.
+ */
+#define nelem(x)	(sizeof(x)/sizeof((x)[0]))
+#define offsetof(s, m)	(ulong)(&(((s*)0)->m))
+#define assert(x)	if(x){}else _assert("x")
+
+/*
+ * mem routines
+ */
+extern	void*	memccpy(void*, void*, int, ulong);
+extern	void*	memset(void*, int, ulong);
+extern	int	memcmp(void*, void*, ulong);
+extern	void*	memmove(void*, void*, ulong);
+extern	void*	memchr(void*, int, ulong);
+
+/*
+ * string routines
+ */
+extern	char*	strcat(char*, char*);
+extern	char*	strchr(char*, int);
+extern	char*	strrchr(char*, int);
+extern	int	strcmp(char*, char*);
+extern	char*	strcpy(char*, char*);
+extern	char*	strecpy(char*, char*, char*);
+extern	char*	strncat(char*, char*, long);
+extern	char*	strncpy(char*, char*, long);
+extern	int	strncmp(char*, char*, long);
+extern	long	strlen(char*);
+extern	char*	strdup(char*);
+extern	char*	strstr(char*, char*);
+extern	int	cistrncmp(char*, char*, int);
+extern	int	cistrcmp(char*, char*);
+extern	char*	cistrstr(char*, char*);
+extern	int	atoi(char*);
+extern	int	fullrune(char*, int);
+
+enum
+{
+	UTFmax		= 4,		/* maximum bytes per rune */
+	Runesync	= 0x80,		/* cannot represent part of a UTF sequence (<) */
+	Runeself	= 0x80,		/* rune and UTF sequences are the same (<) */
+	Runeerror	= 0xFFFD,	/* decoding error in UTF */
+	Runemax		= 0x10FFFF,	/* 21-bit rune */
+	Runemask	= 0x1FFFFF,	/* bits used by runes (see grep) */
+};
+
+/*
+ * rune routines
+ */
+extern	int	runetochar(char*, Rune*);
+extern	int	chartorune(Rune*, char*);
+extern	char*	utfrune(char*, long);
+extern	int	utflen(char*);
+extern	int	runelen(long);
+
+extern	int	abs(int);
+
+/*
+ * print routines
+ */
+typedef struct Fmt	Fmt;
+typedef int (*Fmts)(Fmt*);
+struct Fmt{
+	uchar	runes;			/* output buffer is runes or chars? */
+	void	*start;			/* of buffer */
+	void	*to;			/* current place in the buffer */
+	void	*stop;			/* end of the buffer; overwritten if flush fails */
+	int	(*flush)(Fmt *);	/* called when to == stop */
+	void	*farg;			/* to make flush a closure */
+	int	nfmt;			/* num chars formatted so far */
+	va_list	args;			/* args passed to dofmt */
+	int	r;			/* % format Rune */
+	int	width;
+	int	prec;
+	ulong	flags;
+};
+extern	int	print(char*, ...);
+extern	char*	seprint(char*, char*, char*, ...);
+extern	char*	vseprint(char*, char*, char*, va_list);
+extern	char*	smprint(char*, ...);
+extern	char*	vsmprint(char*, va_list);
+extern	int	snprint(char*, int, char*, ...);
+extern	int	vsnprint(char*, int, char*, va_list);
+extern	int	sprint(char*, char*, ...);
+
+extern	int	fmtinstall(int, int (*)(Fmt*));
+extern	void	quotefmtinstall(void);
+extern	int	fmtprint(Fmt*, char*, ...);
+extern	int	fmtstrcpy(Fmt*, char*);
+
+#pragma	varargck	argpos	fmtprint	2
+#pragma	varargck	argpos	print		1
+#pragma	varargck	argpos	seprint		3
+#pragma	varargck	argpos	snprint		3
+#pragma	varargck	argpos	sprint		2
+#pragma	varargck	argpos	vseprint	3
+#pragma	varargck	argpos	vsnprint	3
+
+extern	int	fltconv(Fmt*);
+
+/*
+ * math
+ */
+extern int	isNaN(double);
+extern int	isInf(double, int);
+extern double	floor(double);
+extern double	frexp(double, int*);
+extern double	pow10(int);
+
+/*
+ * one-of-a-kind
+ */
+extern	char*	cleanname(char*);
+extern	ulong	getcallerpc(void*);
+
+extern	long	strtol(char*, char**, int);
+extern	ulong	strtoul(char*, char**, int);
+extern	vlong	strtoll(char*, char**, int);
+extern	uvlong	strtoull(char*, char**, int);
+extern	char	etext[];
+extern	char	edata[];
+extern	char	end[];
+extern	int	getfields(char*, char**, int, int, char*);
+extern	int	tokenize(char*, char**, int);
+extern	int	dec64(uchar*, int, char*, int);
+extern	void	qsort(void*, long, long, int (*)(void*, void*));
+
+extern	int	toupper(int);
+extern	char*	netmkaddr(char*, char*, char*);
+extern	int	myetheraddr(uchar*, char*);
+extern	int	parseether(uchar*, char*);
+
+/*
+ * network dialling
+ */
+#define	NETPATHLEN	40
+
+/*
+ * Syscall data structures
+ */
+#define	MORDER	0x0003	/* mask for bits defining order of mounting */
+#define	MREPL	0x0000	/* mount replaces object */
+#define	MBEFORE	0x0001	/* mount goes before others in union directory */
+#define	MAFTER	0x0002	/* mount goes after others in union directory */
+#define	MCREATE	0x0004	/* permit creation in mounted directory */
+#define	MCACHE	0x0010	/* cache some data */
+#define	MMASK	0x0017	/* all bits on */
+
+#define	OREAD	0	/* open for read */
+#define	OWRITE	1	/* write */
+#define	ORDWR	2	/* read and write */
+#define	OEXEC	3	/* execute, == read but check execute permission */
+#define	OTRUNC	16	/* or'ed in (except for exec), truncate file first */
+#define	OCEXEC	32	/* or'ed in, close on exec */
+#define	ORCLOSE	64	/* or'ed in, remove on close */
+#define OEXCL   0x1000	/* or'ed in, exclusive create */
+
+#define	NCONT	0	/* continue after note */
+#define	NDFLT	1	/* terminate after note */
+#define	NSAVE	2	/* clear note but hold state */
+#define	NRSTR	3	/* restore saved state */
+
+typedef struct Qid	Qid;
+typedef struct Dir	Dir;
+typedef struct Waitmsg	Waitmsg;
+
+#define	STATMAX	65535U	/* max length of machine-independent stat structure */
+#define	ERRMAX			128	/* max length of error string */
+#define	KNAMELEN		28	/* max length of name held in kernel */
+
+/* bits in Qid.type */
+#define QTDIR		0x80		/* type bit for directories */
+#define QTAPPEND	0x40		/* type bit for append only files */
+#define QTEXCL		0x20		/* type bit for exclusive use files */
+#define QTMOUNT		0x10		/* type bit for mounted channel */
+#define QTAUTH		0x08		/* type bit for authentication file */
+#define QTFILE		0x00		/* plain file */
+
+/* bits in Dir.mode */
+#define DMDIR		0x80000000	/* mode bit for directories */
+#define DMAPPEND	0x40000000	/* mode bit for append only files */
+#define DMEXCL		0x20000000	/* mode bit for exclusive use files */
+#define DMMOUNT		0x10000000	/* mode bit for mounted channel */
+#define DMREAD		0x4		/* mode bit for read permission */
+#define DMWRITE		0x2		/* mode bit for write permission */
+#define DMEXEC		0x1		/* mode bit for execute permission */
+
+struct Qid
+{
+	uvlong	path;
+	ulong	vers;
+	uchar	type;
+};
+
+struct Dir {
+	/* system-modified data */
+	ushort	type;	/* server type */
+	uint	dev;	/* server subtype */
+	/* file data */
+	Qid	qid;	/* unique id from server */
+	ulong	mode;	/* permissions */
+	ulong	atime;	/* last read time */
+	ulong	mtime;	/* last write time */
+	vlong	length;	/* file length: see <u.h> */
+	char	*name;	/* last element of path */
+	char	*uid;	/* owner name */
+	char	*gid;	/* group name */
+	char	*muid;	/* last modifier name */
+};
+
+struct Waitmsg
+{
+	int	pid;	/* of loved one */
+	ulong	time[3];	/* of loved one and descendants */
+	char	msg[ERRMAX];	/* actually variable-size in user mode */
+};
--- /dev/null
+++ b/os/port/log.c
@@ -1,0 +1,186 @@
+#include "u.h"
+#include "../port/lib.h"
+#include "mem.h"
+#include "dat.h"
+#include "fns.h"
+#include "../port/error.h"
+
+static char Ebadlogctl[] = "unknown log ctl message";
+
+void
+logopen(Log *alog)
+{
+	lock(alog);
+	if(waserror()){
+		unlock(alog);
+		nexterror();
+	}
+	if(alog->opens == 0){
+		if(alog->nlog == 0)
+			alog->nlog = 4*1024;
+		if(alog->minread == 0)
+			alog->minread = 1;
+		if(alog->buf == nil)
+			alog->buf = malloc(alog->nlog);
+		alog->rptr = alog->buf;
+		alog->end = alog->buf + alog->nlog;
+		alog->len = 0;
+	}
+	alog->opens++;
+	unlock(alog);
+	poperror();
+}
+
+void
+logclose(Log *alog)
+{
+	lock(alog);
+	alog->opens--;
+	if(alog->opens == 0){
+		free(alog->buf);
+		alog->buf = nil;
+	}
+	unlock(alog);
+}
+
+static int
+logready(void *a)
+{
+	Log *alog = a;
+
+	return alog->len >= alog->minread;
+}
+
+long
+logread(Log *alog, void *a, ulong, long n)
+{
+	int i, d;
+	char *p, *rptr;
+
+	qlock(&alog->readq);
+	if(waserror()){
+		qunlock(&alog->readq);
+		nexterror();
+	}
+
+	for(;;){
+		lock(alog);
+		if(alog->len >= alog->minread || alog->len >= n){
+			if(n > alog->len)
+				n = alog->len;
+			d = 0;
+			rptr = alog->rptr;
+			alog->rptr += n;
+			if(alog->rptr >= alog->end){
+				d = alog->rptr - alog->end;
+				alog->rptr = alog->buf + d;
+			}
+			alog->len -= n;
+			unlock(alog);
+
+			i = n-d;
+			p = a;
+			memmove(p, rptr, i);
+			memmove(p+i, alog->buf, d);
+			break;
+		}
+		else
+			unlock(alog);
+
+		sleep(&alog->readr, logready, alog);
+	}
+
+	qunlock(&alog->readq);
+	poperror();
+
+	return n;
+}
+
+char*
+logctl(Log *alog, int argc, char *argv[], Logflag *flags)
+{
+	int i, set;
+	Logflag *fp;
+
+	if(argc < 2)
+		return Ebadlogctl;
+
+	if(strcmp("set", argv[0]) == 0)
+		set = 1;
+	else if(strcmp("clear", argv[0]) == 0)
+		set = 0;
+	else
+		return Ebadlogctl;
+
+	for(i = 1; i < argc; i++){
+		for(fp = flags; fp->name; fp++)
+			if(strcmp(fp->name, argv[i]) == 0)
+				break;
+		if(fp->name == nil)
+			continue;
+		if(set)
+			alog->logmask |= fp->mask;
+		else
+			alog->logmask &= ~fp->mask;
+	}
+
+	return nil;
+}
+
+void
+logn(Log *alog, int mask, void *buf, int n)
+{
+	char *fp, *t;
+	int dowake, i;
+
+	if(!(alog->logmask & mask))
+		return;
+
+	if(alog->opens == 0)
+		return;
+
+	if(n > alog->nlog)
+		return;
+
+	lock(alog);
+	i = alog->len + n - alog->nlog;
+	if(i > 0){
+		alog->len -= i;
+		alog->rptr += i;
+		if(alog->rptr >= alog->end)
+			alog->rptr = alog->buf + (alog->rptr - alog->end);
+	}
+	t = alog->rptr + alog->len;
+	fp = buf;
+	alog->len += n;
+	while(n-- > 0){
+		if(t >= alog->end)
+			t = alog->buf + (t - alog->end);
+		*t++ = *fp++;
+	}
+	dowake = alog->len >= alog->minread;
+	unlock(alog);
+
+	if(dowake)
+		wakeup(&alog->readr);
+}
+
+void
+logb(Log *alog, int mask, char *fmt, ...)
+{
+	int n;
+	va_list arg;
+	char buf[128];
+
+	if(!(alog->logmask & mask))
+		return;
+
+	if(alog->opens == 0)
+		return;
+
+	va_start(arg, fmt);
+	n = vseprint(buf, buf+sizeof(buf), fmt, arg) - buf;
+	va_end(arg);
+
+	logn(alog, mask, buf, n);
+}
--- /dev/null
+++ b/os/port/master
@@ -1,0 +1,95 @@
+# do not edit; automatically generated
+
+$	pci
+$	pnp
+*	indir
+*	pet
+/	root
+A	audio
+B	boot
+B	bridge
+B	tiboot
+D	ssl
+E	mpeg
+F	flash
+F	tinyfs
+G	fpga
+G	gpio
+H	ata
+I	ip
+J	i2c
+J	touch
+K	kprof
+L	ds1620
+L	lpt
+M	mnt
+O	omap
+P	arch
+P	prof
+Q	ssfdc
+S	sd
+T	cerf
+T	ep7
+T	ipaq
+T	lm78
+T	ti925
+T	touch
+U	usb
+U	usbc
+U	usbh
+V	tv
+X	XXX
+X	ftl
+X	irlap
+X	loopback
+X	plap
+Y	pbus
+Y	pccard
+Z	test
+Z	zt5512
+a	beep
+b	dbg
+c	cons
+d	dup
+e	env
+f	floppy
+g	gray
+h	dsp
+i	draw
+k	ds
+l	ether
+l	lance
+m	modem
+m	mouse
+m	pointer
+p	prog
+r	pcf8563
+r	rtc
+s	srv
+t	ns16552
+t	uart
+v	vga
+v	vid
+v	vsc
+x	esw
+y	i82365
+y	pcmcia
+y	sapcm
+|	pipe
+¤	cap
+Û	isp1161
+Û	usbh
+Σ	sign
+τ	tk
+↓	power
+
+α	local use
+β	local use
+γ	local use
+δ	local use
+ε	local use
+ζ	local use
+η	local use
+θ	local use
+ι	local use
+κ	local use
--- /dev/null
+++ b/os/port/master.local
@@ -1,0 +1,10 @@
+α	local use
+β	local use
+γ	local use
+δ	local use
+ε	local use
+ζ	local use
+η	local use
+θ	local use
+ι	local use
+κ	local use
--- /dev/null
+++ b/os/port/mkdevc
@@ -1,0 +1,216 @@
+$AWK -v 'objtype='$OBJTYPE '
+BEGIN{
+		if(ARGC < 2)
+			exit
+}
+
+/^$/{
+		next;
+}
+/^#/{
+		next;
+}
+collect && /^[^	\t]/{
+		collect = 0;
+}
+collect && section ~ "dev"{
+		dev[ndev++] = $1;
+		if($1 ~ "vga")
+			devvga = 1;
+}
+collect && section ~ "ip"{
+		ip[nip++] = $1;
+}
+collect && (section ~ "ether" || section ~ "link") {
+		link[nlink++] = $1;
+}
+collect && section ~ "mod"{
+		mod[nmod++] = $1;
+}
+collect && section ~ "vga"{
+		option = 0;
+		for(i = 2; i < NF; i++){
+			if($i ~ "[+]hwgc"){
+				hwgc[nhwgc++] = $1;
+				option = 1;
+			} else if($i ~ "[+=]hwgc"){
+				hwgc[nhwgc++] = $1;
+				if(option == 0)
+					option = 2;
+			}
+		}
+		if(option < 2)
+			vga[nvga++] = $1;
+}
+collect && section ~ "misc"{
+		misc[nmisc++] = $1;
+		if($1 ~ "^arch.*")
+			arch[narch++] = $1;
+		else if($1 ~ "^sd.*")
+			sdifc[nsdifc++] = $1;
+		else if($1 ~ "^uart.*")
+			physuart[nphysuart++] = substr($1, 5, length($1)-4) "physuart";
+		else if($1 ~ "^vga.*"){
+			if(NF == 1)
+				vgadev[nvgadev++] = $1;
+			else for(i = 2; i <= NF; i++){
+				if($i ~ "[+]cur")
+					vgadev[nvgadev++] = $1;
+				if($i ~ "[+=]cur")
+					vgacur[nvgacur++] = $1;
+			}
+		}
+		else if($1 ~ ".*\.root"){
+			x = substr($1, 1, index($1, ".")-1);
+			if(x ~ "(dossrv|kfs)")
+				x = "fs";
+			fs[nfs++] = x;
+		}
+}
+collect && section ~ "port"{
+		port[nport++] = $0;
+}
+collect && section ~ "code"{
+		code[ncode++] = $0;
+}
+$0 ~ /^[^ \t]/{
+		if($0 ~ "(code|dev|ether|ip|lib|link|mod|misc|port|root|vga)"){
+			section = $0;
+			collect = 1;
+		}
+		next;
+}
+
+END{
+		if(ARGC < 2)
+			exit "usage"
+
+		printf "#include \"u.h\"\n"
+		printf "#include \"../port/lib.h\"\n"
+		printf "#include \"mem.h\"\n"
+		printf "#include \"dat.h\"\n"
+		printf "#include \"fns.h\"\n"
+		printf "#include \"io.h\"\n"
+		if(nphysuart)
+			printf "#include \"../port/uart.h\"\n"
+		printf "#include \"../port/error.h\"\n"
+		printf "#include \"interp.h\"\n\n"
+		printf "#include \"%s.root.h\"\n\n", ARGV[1];
+
+		printf "ulong ndevs = %d;\n", ndev+8
+		for(i = 0; i < ndev; i++)
+			printf "extern Dev %sdevtab;\n", dev[i];
+		printf "Dev* devtab[%d]={\n", ndev+8
+		for(i = 0; i < ndev; i++)
+			printf "\t&%sdevtab,\n", dev[i];
+		printf "\tnil,\n};\n\n";
+
+
+		for(i = 0; i < nfs; i++){
+			printf "extern uchar %scode[];\n", fs[i];
+			printf "extern ulong %slen;\n", fs[i];
+		}
+		for(i = 0; i < nlink; i++)
+			printf "extern void %slink(void);\n", link[i];
+
+		printf "void links(void){\n";
+		for(i = 0; i < nfs; i++)
+			printf "\taddrootfile(\"%s\", %scode, %slen);\n", fs[i], fs[i], fs[i];
+		for(i = 0; i < nlink; i++)
+			printf "\t%slink();\n", link[i];
+		printf "}\n\n";
+
+		for(i = 0; i < nmod; i++)
+			printf "extern void %smodinit(void);\n", mod[i];
+		printf "void modinit(void){\n";
+		for(i = 0; i < nmod; i++)
+			printf("\t%smodinit();\n",mod[i]);
+		printf("}\n\n");
+
+		if(narch || objtype ~ "386"){
+			for(i = 0; i < narch; i++)
+				printf "extern PCArch %s;\n", arch[i];
+			printf "PCArch* knownarch[] = {\n";
+			for(i = 0; i < narch; i++)
+				printf "\t&%s,\n", arch[i];
+			printf "\tnil,\n};\n\n";
+		}
+
+		if(nsdifc){
+			printf "#include \"../port/sd.h\"\n";
+			for(i = 0; i < nsdifc; i++)
+				printf "extern SDifc %sifc;\n", sdifc[i];
+			printf "SDifc* sdifc[] = {\n";
+		  	for(i = 0; i < nsdifc; i++)
+				printf "\t&%sifc,\n", sdifc[i];
+		  	printf "\tnil,\n};\n\n";
+		}
+
+		if(nphysuart){
+			for(i = 0; i < nphysuart; i++)
+				printf "extern PhysUart %s;\n", physuart[i];
+			printf "PhysUart* physuart[] = {\n";
+		  	for(i = 0; i < nphysuart; i++)
+				printf "\t&%s,\n", physuart[i];
+		  	printf "\tnil,\n};\n\n";
+		}
+
+		if(devvga || nvga || nvgadev){
+			printf "#include <draw.h>\n"
+			printf "#include <memdraw.h>\n"
+
+			if(nvga){
+				printf "#include \"vga.h\"\n"
+				for(i = 0; i < nvga; i++)
+					printf "extern Vgac %s;\n", vga[i];
+			  	printf "Vgac* knownvga[] = {\n";
+			  	for(i = 0; i < nvga; i++)
+					printf "\t&%s,\n", vga[i];
+			  	printf "\tnil,\n};\n\n";
+
+				if(nhwgc){
+					for(i = 0; i < nhwgc; i++)
+						printf "extern Hwgc %shwgc;\n", hwgc[i];
+				  	printf "Hwgc* knownhwgc[] = {\n";
+				  	for(i = 0; i < nhwgc; i++)
+						printf "\t&%shwgc,\n", hwgc[i];
+				  	printf "\tnil,\n};\n\n";
+				}
+			}
+
+			if(nvgadev){
+				printf "#include \"screen.h\"\n";
+				for(i = 0; i < nvgadev; i++)
+					printf "extern VGAdev %sdev;\n", vgadev[i];
+			  	printf "VGAdev* vgadev[] = {\n";
+			  	for(i = 0; i < nvgadev; i++)
+					printf "\t&%sdev,\n", vgadev[i];
+			  	printf "\tnil,\n};\n\n";
+
+				for(i = 0; i < nvgacur; i++)
+					printf "extern VGAcur %scur;\n", vgacur[i];
+			  	printf "VGAcur* vgacur[] = {\n";
+			  	for(i = 0; i < nvgacur; i++)
+					printf "\t&%scur,\n", vgacur[i];
+			  	printf "\tnil,\n};\n\n";
+			}
+		}
+	
+		if(nip){
+			printf "#include \"../ip/ip.h\"\n";
+			for(i = 0; i < nip; i++)
+				printf "extern void %sinit(Fs*);\n", ip[i];
+			printf "void (*ipprotoinit[])(Fs*) = {\n";
+			for(i = 0; i < nip; i++)
+				printf "\t%sinit,\n", ip[i];
+			printf "\tnil,\n};\n\n";
+		}
+
+		for(i = 0; i < ncode; i++)
+			printf "%s\n", code[i];
+
+		printf "char* conffile = \"%s\";\n", ARGV[1];
+		printf "ulong kerndate = KERNDATE;\n";
+
+		exit
+}' $*
--- /dev/null
+++ b/os/port/mkdevlist
@@ -1,0 +1,86 @@
+$AWK '
+BEGIN{
+		var["dev"] = "DEVS=";
+		var["vga"] = "VGAS=";
+		var["ether"] = "ETHERS=";
+		var["init"] = "INIT=";
+		var["ip"] = "IP=";
+		var["port"] = "PORT=";
+		var["misc"] = "MISC=";
+		var["lib"] = "LIBS=";
+		var["link"] = "LINKS=";
+		var["root"] = "ROOTFILES=";
+		infernoroot = ENVIRON["ROOT"];
+}
+/^$/{		next;
+}
+/^#/{		next;
+}
+/^env/{
+		inenv = 1;
+		next;
+}
+inenv != 0 && /^[ 	]/{
+		sub("^[ 	]*", "", $0)
+		printf "%s\n", $0
+		next
+}
+/^(code|dev|ether|init|ip|lib|link|mod|misc|port|root|vga)/{
+		inenv = 0;
+		if(current != "")
+			print current;
+		current = var[$1];
+		type = $1;
+		next;
+}
+/^[^ 	]/	{
+		inenv = 0;
+		if(current != "")
+			print current;
+		current = "";
+}
+current && /^[ 	]/{
+		if(type == "dev")
+			file = "dev" $1;
+		else if(type == "root"){
+			if (NF > 1)
+				file = $2;
+			else if ($1 == "/osinit.dis")
+				next;	# handled via explicit dependency
+			else
+				file = $1;
+			if(have[file] == 0){
+				current = current " " infernoroot file;
+				have[file]++;
+			}
+			next;
+		}
+		else
+			file = $1;
+		if(type == "init" || type == "lib")
+			current = current " " file;
+		else if(have[file] == 0){
+			if(type == "lib")
+				current = current " " file;
+			else
+				current = current " " file "'.$O'";
+			have[file]++;
+		}
+		for(i = 2; i <= NF; i++){
+			if($i !~ "^[+=-].*"){
+				if(have[$i] == 0){
+					others[++nothers] = $i;
+					have[$i]++;
+				}
+			}
+		}
+		next;
+}
+END{
+		if(current != "")
+			print current;
+		for(i = 1; i <= nothers; i++)
+			x = x " " others[i] "'.$O' ";
+		if(x)
+			printf("OTHERS=%s\n", x);
+}' $*
--- /dev/null
+++ b/os/port/mkfile
@@ -1,0 +1,15 @@
+# If the existence of this mkfile screws something up, rename it.	-rsc
+
+master:DV:
+	{
+	echo '# do not edit; automatically generated'
+	echo
+	echo '
+		X , s/Dev (.*)devtab.*{.*\n	L?''(.*)''/DEV \1 \2\n/
+		X ,x g/^DEV/ p
+	' | sam -d ../*/dev*.c >[2]/dev/null |
+	awk '/^DEV/ { printf("%s\t%s\n", $3, $2); }' |
+	sort -u
+	echo
+	cat master.local
+	} >master
--- /dev/null
+++ b/os/port/mkroot
@@ -1,0 +1,119 @@
+$AWK '
+BEGIN{
+		if (ARGC < 2)
+			exit "usage";
+
+		conf = ARGV[1];
+		infernoroot = ENVIRON["ROOT"];
+		init = ENVIRON["INIT"];
+		data2s = ENVIRON["DATA2S"];
+		nroot = 0;
+}
+/^$/{
+		next;
+}
+/^#/{
+		next;
+}
+collect && /^[^	\t]/{
+		collect = 0;
+}
+collect && section ~ "root"{
+		dst[nroot] = $1;
+		if (NF > 1)
+			src[nroot] = infernoroot $2;
+		else if (dst[nroot] == "/osinit.dis")
+			src[nroot] = infernoroot "/os/init/" init ".dis";
+		else
+			src[nroot] = infernoroot $1;
+		for(i=0; i<nroot; i++)
+			if(dst[i] == dst[nroot])
+				break;
+		if(i == nroot)
+			nroot++;
+}
+$0 ~ /^[^ \t]/{
+		if($0 ~ "(code|dev|ether|ip|lib|link|mod|misc|port|root|vga)"){
+			section = $0;
+			collect = 1;
+		}
+		next;
+}
+END{
+		rootdata = conf ".root.s";
+		system("rm -f " rootdata);
+		print("/* Generated by /os/port/mkroot */") >rootdata;
+		close(rootdata);
+		isdir[0] = 1;
+		dotdot[0] = 0;
+		qid = 1;
+		for (i = 0; i < nroot; i++) {
+			ncomp = split(dst[i], comp, "/");
+			if (comp[1] != "" || ncomp < 2)
+				continue;
+			q = 0;
+			for (j = 2; j <= ncomp; j++) {
+				key = q "/" comp[j];
+				if (walk[key] == 0) {
+					walk[key] = qid;
+					dotdot[qid] = q;
+					q = qid++;
+					name[q] = comp[j];
+					if (j < ncomp)
+						isdir[q] = 1;
+				}
+				else
+					q = walk[key];
+			}
+			if (system("test -d " src[i]) == 0)
+				isdir[q] = 1;
+			else {
+				if (system(data2s " root" q " <" src[i] " >>" rootdata) != 0)
+					exit 1;
+				print("extern unsigned char root" q "code[];");
+				print("extern int root" q "len;");
+			}
+		}
+
+		x = 1;
+		sort[0] = 0;
+		unsort[0] = 0;
+		for (q = 0; q < qid; q++) {
+			if (isdir[q]) {
+				nchild[q] = 0;
+				for (q2 = 1; q2 < qid; q2++) {
+					if (dotdot[q2] == q) {
+						if (nchild[q]++ == 0)
+							child0[q] = x;
+						sort[q2] = x++;
+						unsort[sort[q2]] = q2;
+					}
+				}
+			}
+		}
+
+		print("int rootmaxq = " qid ";");
+
+		print("Dirtab roottab[" qid "] = {");
+		for (oq = 0; oq < qid; oq++) {
+			q = unsort[oq];
+			if (!isdir[q])
+				print("\t\"" name[q] "\",\t{" oq ", 0, QTFILE},\t", "0,\t0444,");
+			else
+				print("\t\"" name[q] "\",\t{" oq ", 0, QTDIR},\t", "0,\t0555,");
+		}
+		print("};");
+
+		print("Rootdata rootdata[" qid "] = {");
+		for (oq = 0; oq < qid; oq++) {
+			q = unsort[oq];
+			if (!isdir[q])
+				print("\t" sort[dotdot[q]] ",\t", "root" q "code,\t", "0,\t", "&root" q "len,");
+			else if (nchild[q])
+				print("\t" sort[dotdot[q]] ",\t", "&roottab[" child0[q] "],\t", nchild[q] ",\tnil,");
+			else
+				print("\t" sort[dotdot[q]] ",\t", "nil,\t", "0,\t", "nil,");
+		}
+		print("};");
+}
+' $1 >$1.root.h
--- /dev/null
+++ b/os/port/mul64fract.c
@@ -1,0 +1,39 @@
+#include <u.h>
+
+/* mul64fract(uvlong*r, uvlong a, uvlong b)
+ *
+ * Multiply two 64 numbers and return the middle 64 bits of the 128 bit result.
+ *
+ * The assumption is that one of the numbers is a 
+ * fixed point number with the integer portion in the
+ * high word and the fraction in the low word.
+ *
+ * There should be an assembler version of this routine
+ * for each architecture.  This one is intended to
+ * make ports easier.
+ *
+ *	ignored		r0 = lo(a0*b0)
+ *	lsw of result	r1 = hi(a0*b0) +lo(a0*b1) +lo(a1*b0)
+ *	msw of result	r2 = 		hi(a0*b1) +hi(a1*b0) +lo(a1*b1)
+ *	ignored		r3 = hi(a1*b1)
+ */
+
+void
+mul64fract(uvlong *r, uvlong a, uvlong b)
+{
+	uvlong bh, bl;
+	uvlong ah, al;
+	uvlong res;
+
+	bl = b & 0xffffffffULL;
+	bh = b >> 32;
+	al = a & 0xffffffffULL;
+	ah = a >> 32;
+
+	res = (al*bl)>>32;
+	res += (al*bh);
+	res += (ah*bl);
+	res += (ah*bh)<<32;
+
+	*r = res;
+}
--- /dev/null
+++ b/os/port/netaux.c
@@ -1,0 +1,67 @@
+#include	"u.h"
+#include	"../port/lib.h"
+#include	"mem.h"
+#include	"dat.h"
+#include	"fns.h"
+#include	"../port/error.h"
+#include	"../port/netif.h"
+
+
+void
+hnputv(void *p, vlong v)
+{
+	uchar *a;
+
+	a = p;
+	hnputl(a, v>>32);
+	hnputl(a+4, v);
+}
+
+void
+hnputl(void *p, ulong v)
+{
+	uchar *a;
+
+	a = p;
+	a[0] = v>>24;
+	a[1] = v>>16;
+	a[2] = v>>8;
+	a[3] = v;
+}
+
+void
+hnputs(void *p, ushort v)
+{
+	uchar *a;
+
+	a = p;
+	a[0] = v>>8;
+	a[1] = v;
+}
+
+vlong
+nhgetv(void *p)
+{
+	uchar *a;
+
+	a = p;
+	return ((vlong)nhgetl(a) << 32) | nhgetl(a+4);
+}
+
+ulong
+nhgetl(void *p)
+{
+	uchar *a;
+
+	a = p;
+	return (a[0]<<24)|(a[1]<<16)|(a[2]<<8)|(a[3]<<0);
+}
+
+ushort
+nhgets(void *p)
+{
+	uchar *a;
+
+	a = p;
+	return (a[0]<<8)|(a[1]<<0);
+}
--- /dev/null
+++ b/os/port/netif.c
@@ -1,0 +1,671 @@
+#include	"u.h"
+#include	"../port/lib.h"
+#include	"mem.h"
+#include	"dat.h"
+#include	"fns.h"
+#include	"../port/error.h"
+#include	"../port/netif.h"
+
+static int netown(Netfile*, char*, int);
+static int openfile(Netif*, int);
+static char* matchtoken(char*, char*);
+static char* netmulti(Netif*, Netfile*, uchar*, int);
+static int parseaddr(uchar*, char*, int);
+
+/*
+ *  set up a new network interface
+ */
+void
+netifinit(Netif *nif, char *name, int nfile, ulong limit)
+{
+	strncpy(nif->name, name, KNAMELEN-1);
+	nif->name[KNAMELEN-1] = 0;
+	nif->nfile = nfile;
+	nif->f = malloc(nfile*sizeof(Netfile*));
+	if(nif->f)
+		memset(nif->f, 0, nfile*sizeof(Netfile*));
+	else
+		nif->nfile = 0;
+	nif->limit = limit;
+}
+
+/*
+ *  generate a 3 level directory
+ */
+static int
+netifgen(Chan *c, char*, Dirtab *vp, int, int i, Dir *dp)
+{
+	Qid q;
+	Netif *nif = (Netif*)vp;
+	Netfile *f;
+	int t;
+	int perm;
+	char *o;
+
+	q.type = QTFILE;
+	q.vers = 0;
+
+	/* top level directory contains the name of the network */
+	if(c->qid.path == 0){
+		switch(i){
+		case DEVDOTDOT:
+			q.path = 0;
+			q.type = QTDIR;
+			devdir(c, q, ".", 0, eve, 0555, dp);
+			break;
+		case 0:
+			q.path = N2ndqid;
+			q.type = QTDIR;
+			strcpy(up->genbuf, nif->name);
+			devdir(c, q, up->genbuf, 0, eve, 0555, dp);
+			break;
+		default:
+			return -1;
+		}
+		return 1;
+	}
+
+	/* second level contains clone plus all the conversations */
+	t = NETTYPE(c->qid.path);
+	if(t == N2ndqid || t == Ncloneqid || t == Naddrqid){
+		switch(i) {
+		case DEVDOTDOT:
+			q.type = QTDIR;
+			q.path = 0;
+			devdir(c, q, ".", 0, eve, DMDIR|0555, dp);
+			break;
+		case 0:
+			q.path = Ncloneqid;
+			devdir(c, q, "clone", 0, eve, 0666, dp);
+			break;
+		case 1:
+			q.path = Naddrqid;
+			devdir(c, q, "addr", 0, eve, 0666, dp);
+			break;
+		case 2:
+			q.path = Nstatqid;
+			devdir(c, q, "stats", 0, eve, 0444, dp);
+			break;
+		case 3:
+			q.path = Nifstatqid;
+			devdir(c, q, "ifstats", 0, eve, 0444, dp);
+			break;
+		default:
+			i -= 4;
+			if(i >= nif->nfile)
+				return -1;
+			if(nif->f[i] == 0)
+				return 0;
+			q.type = QTDIR;
+			q.path = NETQID(i, N3rdqid);
+			sprint(up->genbuf, "%d", i);
+			devdir(c, q, up->genbuf, 0, eve, DMDIR|0555, dp);
+			break;
+		}
+		return 1;
+	}
+
+	/* third level */
+	f = nif->f[NETID(c->qid.path)];
+	if(f == 0)
+		return 0;
+	if(*f->owner){
+		o = f->owner;
+		perm = f->mode;
+	} else {
+		o = eve;
+		perm = 0666;
+	}
+	switch(i){
+	case DEVDOTDOT:
+		q.type = QTDIR;
+		q.path = N2ndqid;
+		strcpy(up->genbuf, nif->name);
+		devdir(c, q, up->genbuf, 0, eve, DMDIR|0555, dp);
+		break;
+	case 0:
+		q.path = NETQID(NETID(c->qid.path), Ndataqid);
+		devdir(c, q, "data", 0, o, perm, dp);
+		break;
+	case 1:
+		q.path = NETQID(NETID(c->qid.path), Nctlqid);
+		devdir(c, q, "ctl", 0, o, perm, dp);
+		break;
+	case 2:
+		q.path = NETQID(NETID(c->qid.path), Nstatqid);
+		devdir(c, q, "stats", 0, eve, 0444, dp);
+		break;
+	case 3:
+		q.path = NETQID(NETID(c->qid.path), Ntypeqid);
+		devdir(c, q, "type", 0, eve, 0444, dp);
+		break;
+	case 4:
+		q.path = NETQID(NETID(c->qid.path), Nifstatqid);
+		devdir(c, q, "ifstats", 0, eve, 0444, dp);
+		break;
+	default:
+		return -1;
+	}
+	return 1;
+}
+
+Walkqid*
+netifwalk(Netif *nif, Chan *c, Chan *nc, char **name, int nname)
+{
+	return devwalk(c, nc, name, nname, (Dirtab *)nif, 0, netifgen);
+}
+
+Chan*
+netifopen(Netif *nif, Chan *c, int omode)
+{
+	int id;
+	Netfile *f;
+
+	id = 0;
+	if(c->qid.type & QTDIR){
+		if(omode != OREAD)
+			error(Eperm);
+	} else {
+		switch(NETTYPE(c->qid.path)){
+		case Ndataqid:
+		case Nctlqid:
+			id = NETID(c->qid.path);
+			openfile(nif, id);
+			break;
+		case Ncloneqid:
+			id = openfile(nif, -1);
+			c->qid.path = NETQID(id, Nctlqid);
+			break;
+		default:
+			if(omode != OREAD)
+				error(Ebadarg);
+		}
+		switch(NETTYPE(c->qid.path)){
+		case Ndataqid:
+		case Nctlqid:
+			f = nif->f[id];
+			if(netown(f, up->env->user, omode&7) < 0)
+				error(Eperm);
+			break;
+		}
+	}
+	c->mode = openmode(omode);
+	c->flag |= COPEN;
+	c->offset = 0;
+	c->iounit = qiomaxatomic;
+	return c;
+}
+
+long
+netifread(Netif *nif, Chan *c, void *a, long n, ulong offset)
+{
+	int i, j;
+	Netfile *f;
+	char *p;
+
+	if(c->qid.type&QTDIR)
+		return devdirread(c, a, n, (Dirtab*)nif, 0, netifgen);
+
+	switch(NETTYPE(c->qid.path)){
+	case Ndataqid:
+		f = nif->f[NETID(c->qid.path)];
+		return qread(f->in, a, n);
+	case Nctlqid:
+		return readnum(offset, a, n, NETID(c->qid.path), NUMSIZE);
+	case Nstatqid:
+		p = malloc(READSTR);
+		if(p == nil)
+			return 0;
+		j = snprint(p, READSTR, "in: %d\n", nif->inpackets);
+		j += snprint(p+j, READSTR-j, "link: %d\n", nif->link);
+		j += snprint(p+j, READSTR-j, "out: %d\n", nif->outpackets);
+		j += snprint(p+j, READSTR-j, "crc errs: %d\n", nif->crcs);
+		j += snprint(p+j, READSTR-j, "overflows: %d\n", nif->overflows);
+		j += snprint(p+j, READSTR-j, "soft overflows: %d\n", nif->soverflows);
+		j += snprint(p+j, READSTR-j, "framing errs: %d\n", nif->frames);
+		j += snprint(p+j, READSTR-j, "buffer errs: %d\n", nif->buffs);
+		j += snprint(p+j, READSTR-j, "output errs: %d\n", nif->oerrs);
+		j += snprint(p+j, READSTR-j, "prom: %d\n", nif->prom);
+		j += snprint(p+j, READSTR-j, "mbps: %d\n", nif->mbps);
+		j += snprint(p+j, READSTR-j, "addr: ");
+		for(i = 0; i < nif->alen; i++)
+			j += snprint(p+j, READSTR-j, "%2.2ux", nif->addr[i]);
+		snprint(p+j, READSTR-j, "\n");
+		n = readstr(offset, a, n, p);
+		free(p);
+		return n;
+	case Naddrqid:
+		p = malloc(READSTR);
+		if(p == nil)
+			return 0;
+		j = 0;
+		for(i = 0; i < nif->alen; i++)
+			j += snprint(p+j, READSTR-j, "%2.2ux", nif->addr[i]);
+		n = readstr(offset, a, n, p);
+		free(p);
+		return n;
+	case Ntypeqid:
+		f = nif->f[NETID(c->qid.path)];
+		return readnum(offset, a, n, f->type, NUMSIZE);
+	case Nifstatqid:
+		return 0;
+	}
+	error(Ebadarg);
+	return -1;	/* not reached */
+}
+
+Block*
+netifbread(Netif *nif, Chan *c, long n, ulong offset)
+{
+	if((c->qid.type & QTDIR) || NETTYPE(c->qid.path) != Ndataqid)
+		return devbread(c, n, offset);
+
+	return qbread(nif->f[NETID(c->qid.path)]->in, n);
+}
+
+/*
+ *  make sure this type isn't already in use on this device
+ */
+static int
+typeinuse(Netif *nif, int type)
+{
+	Netfile *f, **fp, **efp;
+
+	if(type <= 0)
+		return 0;
+
+	efp = &nif->f[nif->nfile];
+	for(fp = nif->f; fp < efp; fp++){
+		f = *fp;
+		if(f == 0)
+			continue;
+		if(f->type == type)
+			return 1;
+	}
+	return 0;
+}
+
+/*
+ *  the devxxx.c that calls us handles writing data, it knows best
+ */
+long
+netifwrite(Netif *nif, Chan *c, void *a, long n)
+{
+	Netfile *f;
+	int type;
+	char *p, buf[64];
+	uchar binaddr[Nmaxaddr];
+
+	if(NETTYPE(c->qid.path) != Nctlqid)
+		error(Eperm);
+
+	if(n >= sizeof(buf))
+		n = sizeof(buf)-1;
+	memmove(buf, a, n);
+	buf[n] = 0;
+
+	if(waserror()){
+		qunlock(nif);
+		nexterror();
+	}
+
+	qlock(nif);
+	f = nif->f[NETID(c->qid.path)];
+	if((p = matchtoken(buf, "connect")) != 0){
+		type = atoi(p);
+		if(typeinuse(nif, type))
+			error(Einuse);
+		f->type = type;
+		if(f->type < 0)
+			nif->all++;
+	} else if(matchtoken(buf, "promiscuous")){
+		if(f->prom == 0){
+			if(nif->prom == 0 && nif->promiscuous != nil)
+				nif->promiscuous(nif->arg, 1);
+			f->prom = 1;
+			nif->prom++;
+		}
+	} else if((p = matchtoken(buf, "scanbs")) != 0){
+		/* scan for base stations */
+		if(f->scan == 0){
+			type = atoi(p);
+			if(type < 5)
+				type = 5;
+			if(nif->scanbs != nil)
+				nif->scanbs(nif->arg, type);
+			f->scan = type;
+			nif->scan++;
+		}
+	} else if(matchtoken(buf, "bridge")){
+		f->bridge = 1;
+	} else if(matchtoken(buf, "headersonly")){
+		f->headersonly = 1;
+	} else if((p = matchtoken(buf, "addmulti")) != 0){
+		if(parseaddr(binaddr, p, nif->alen) < 0)
+			error("bad address");
+		p = netmulti(nif, f, binaddr, 1);
+		if(p)
+			error(p);
+	} else if((p = matchtoken(buf, "remmulti")) != 0){
+		if(parseaddr(binaddr, p, nif->alen) < 0)
+			error("bad address");
+		p = netmulti(nif, f, binaddr, 0);
+		if(p)
+			error(p);
+	} else
+		n = -1;
+	qunlock(nif);
+	poperror();
+	return n;
+}
+
+int
+netifwstat(Netif *nif, Chan *c, uchar *db, int n)
+{
+	Dir *dir;
+	Netfile *f;
+	int m;
+
+	f = nif->f[NETID(c->qid.path)];
+	if(f == 0)
+		error(Enonexist);
+
+	if(netown(f, up->env->user, OWRITE) < 0)
+		error(Eperm);
+
+	dir = smalloc(sizeof(Dir)+n);
+	m = convM2D(db, n, &dir[0], (char*)&dir[1]);
+	if(m == 0){
+		free(dir);
+		error(Eshortstat);
+	}
+	if(!emptystr(dir[0].uid))
+		strncpy(f->owner, dir[0].uid, KNAMELEN);
+	if(dir[0].mode != ~0UL)
+		f->mode = dir[0].mode;
+	free(dir);
+	return m;
+}
+
+int
+netifstat(Netif *nif, Chan *c, uchar *db, int n)
+{
+	return devstat(c, db, n, (Dirtab *)nif, 0, netifgen);
+}
+
+void
+netifclose(Netif *nif, Chan *c)
+{
+	Netfile *f;
+	int t;
+	Netaddr *ap;
+
+	if((c->flag & COPEN) == 0)
+		return;
+
+	t = NETTYPE(c->qid.path);
+	if(t != Ndataqid && t != Nctlqid)
+		return;
+
+	f = nif->f[NETID(c->qid.path)];
+	qlock(f);
+	if(--(f->inuse) == 0){
+		if(f->prom){
+			qlock(nif);
+			if(--(nif->prom) == 0 && nif->promiscuous != nil)
+				nif->promiscuous(nif->arg, 0);
+			qunlock(nif);
+			f->prom = 0;
+		}
+		if(f->scan){
+			qlock(nif);
+			if(--(nif->scan) == 0 && nif->scanbs != nil)
+				nif->scanbs(nif->arg, 0);
+			qunlock(nif);
+			f->prom = 0;
+			f->scan = 0;
+		}
+		if(f->nmaddr){
+			qlock(nif);
+			t = 0;
+			for(ap = nif->maddr; ap; ap = ap->next){
+				if(f->maddr[t/8] & (1<<(t%8)))
+					netmulti(nif, f, ap->addr, 0);
+			}
+			qunlock(nif);
+			f->nmaddr = 0;
+		}
+		if(f->type < 0){
+			qlock(nif);
+			--(nif->all);
+			qunlock(nif);
+		}
+		f->owner[0] = 0;
+		f->type = 0;
+		f->bridge = 0;
+		f->headersonly = 0;
+		qclose(f->in);
+	}
+	qunlock(f);
+}
+
+Lock netlock;
+
+static int
+netown(Netfile *p, char *o, int omode)
+{
+	static int access[] = { 0400, 0200, 0600, 0100 };
+	int mode;
+	int t;
+
+	lock(&netlock);
+	if(*p->owner){
+		if(strncmp(o, p->owner, KNAMELEN) == 0)	/* User */
+			mode = p->mode;
+		else if(strncmp(o, eve, KNAMELEN) == 0)	/* Bootes is group */
+			mode = p->mode<<3;
+		else
+			mode = p->mode<<6;		/* Other */
+
+		t = access[omode&3];
+		if((t & mode) == t){
+			unlock(&netlock);
+			return 0;
+		} else {
+			unlock(&netlock);
+			return -1;
+		}
+	}
+	strncpy(p->owner, o, KNAMELEN);
+	p->mode = 0660;
+	unlock(&netlock);
+	return 0;
+}
+
+/*
+ *  Increment the reference count of a network device.
+ *  If id < 0, return an unused ether device.
+ */
+static int
+openfile(Netif *nif, int id)
+{
+	Netfile *f, **fp, **efp;
+
+	if(id >= 0){
+		f = nif->f[id];
+		if(f == 0)
+			error(Enodev);
+		qlock(f);
+		qreopen(f->in);
+		f->inuse++;
+		qunlock(f);
+		return id;
+	}
+
+	qlock(nif);
+	if(waserror()){
+		qunlock(nif);
+		nexterror();
+	}
+	efp = &nif->f[nif->nfile];
+	for(fp = nif->f; fp < efp; fp++){
+		f = *fp;
+		if(f == 0){
+			f = malloc(sizeof(Netfile));
+			if(f == 0)
+				exhausted("memory");
+			f->in = qopen(nif->limit, Qmsg, 0, 0);
+			if(f->in == nil){
+				free(f);
+				exhausted("memory");
+			}
+			*fp = f;
+			qlock(f);
+		} else {
+			qlock(f);
+			if(f->inuse){
+				qunlock(f);
+				continue;
+			}
+		}
+		f->inuse = 1;
+		qreopen(f->in);
+		netown(f, up->env->user, 0);
+		qunlock(f);
+		qunlock(nif);
+		poperror();
+		return fp - nif->f;
+	}
+	error(Enodev);
+	return -1;	/* not reached */
+}
+
+/*
+ *  look for a token starting a string,
+ *  return a pointer to first non-space char after it
+ */
+static char*
+matchtoken(char *p, char *token)
+{
+	int n;
+
+	n = strlen(token);
+	if(strncmp(p, token, n))
+		return 0;
+	p += n;
+	if(*p == 0)
+		return p;
+	if(*p != ' ' && *p != '\t' && *p != '\n')
+		return 0;
+	while(*p == ' ' || *p == '\t' || *p == '\n')
+		p++;
+	return p;
+}
+
+static ulong
+hash(uchar *a, int len)
+{
+	ulong sum = 0;
+
+	while(len-- > 0)
+		sum = (sum << 1) + *a++;
+	return sum%Nmhash;
+}
+
+int
+activemulti(Netif *nif, uchar *addr, int alen)
+{
+	Netaddr *hp;
+
+	for(hp = nif->mhash[hash(addr, alen)]; hp; hp = hp->hnext)
+		if(memcmp(addr, hp->addr, alen) == 0){
+			if(hp->ref)
+				return 1;
+			else
+				break;
+		}
+	return 0;
+}
+
+static int
+parseaddr(uchar *to, char *from, int alen)
+{
+	char nip[4];
+	char *p;
+	int i;
+
+	p = from;
+	for(i = 0; i < alen; i++){
+		if(*p == 0)
+			return -1;
+		nip[0] = *p++;
+		if(*p == 0)
+			return -1;
+		nip[1] = *p++;
+		nip[2] = 0;
+		to[i] = strtoul(nip, 0, 16);
+		if(*p == ':')
+			p++;
+	}
+	return 0;
+}
+
+/*
+ *  keep track of multicast addresses
+ */
+static char*
+netmulti(Netif *nif, Netfile *f, uchar *addr, int add)
+{
+	Netaddr **l, *ap;
+	int i;
+	ulong h;
+
+	if(nif->multicast == nil)
+		return "interface does not support multicast";
+
+	l = &nif->maddr;
+	i = 0;
+	for(ap = *l; ap; ap = *l){
+		if(memcmp(addr, ap->addr, nif->alen) == 0)
+			break;
+		i++;
+		l = &ap->next;
+	}
+
+	if(add){
+		if(ap == 0){
+			*l = ap = smalloc(sizeof(*ap));
+			memmove(ap->addr, addr, nif->alen);
+			ap->next = 0;
+			ap->ref = 1;
+			h = hash(addr, nif->alen);
+			ap->hnext = nif->mhash[h];
+			nif->mhash[h] = ap;
+		} else {
+			ap->ref++;
+		}
+		if(ap->ref == 1){
+			nif->nmaddr++;
+			nif->multicast(nif->arg, addr, 1);
+		}
+		if(i < 8*sizeof(f->maddr)){
+			if((f->maddr[i/8] & (1<<(i%8))) == 0)
+				f->nmaddr++;
+			f->maddr[i/8] |= 1<<(i%8);
+		}
+	} else {
+		if(ap == 0 || ap->ref == 0)
+			return 0;
+		ap->ref--;
+		if(ap->ref == 0){
+			nif->nmaddr--;
+			nif->multicast(nif->arg, addr, 0);
+		}
+		if(i < 8*sizeof(f->maddr)){
+			if((f->maddr[i/8] & (1<<(i%8))) != 0)
+				f->nmaddr--;
+			f->maddr[i/8] &= ~(1<<(i%8));
+		}
+	}
+	return 0;
+}
--- /dev/null
+++ b/os/port/netif.h
@@ -1,0 +1,134 @@
+typedef struct Etherpkt	Etherpkt;
+typedef struct Netaddr	Netaddr;
+typedef struct Netfile	Netfile;
+typedef struct Netif	Netif;
+
+enum
+{
+	Nmaxaddr=	64,
+	Nmhash=		31,
+
+	Ncloneqid=	1,
+	Naddrqid,
+	N2ndqid,
+	N3rdqid,
+	Ndataqid,
+	Nctlqid,
+	Nstatqid,
+	Ntypeqid,
+	Nifstatqid,
+};
+
+/*
+ *  Macros to manage Qid's used for multiplexed devices
+ */
+#define NETTYPE(x)	(((ulong)x)&0x1f)
+#define NETID(x)	((((ulong)x))>>5)
+#define NETQID(i,t)	((((ulong)i)<<5)|(t))
+
+/*
+ *  one per multiplexed connection
+ */
+struct Netfile
+{
+	QLock;
+
+	int	inuse;
+	ulong	mode;
+	char	owner[KNAMELEN];
+
+	int	type;			/* multiplexor type */
+	int	prom;			/* promiscuous mode */
+	int	scan;			/* base station scanning interval */
+	int	bridge;			/* bridge mode */
+	int	headersonly;		/* headers only - no data */
+	uchar	maddr[8];		/* bitmask of multicast addresses requested */
+	int	nmaddr;			/* number of multicast addresses */
+
+	Queue	*in;			/* input buffer */
+};
+
+/*
+ *  a network address
+ */
+struct Netaddr
+{
+	Netaddr	*next;		/* allocation chain */
+	Netaddr	*hnext;
+	uchar	addr[Nmaxaddr];
+	int	ref;
+};
+
+/*
+ *  a network interface
+ */
+struct Netif
+{
+	QLock;
+
+	/* multiplexing */
+	char	name[KNAMELEN];		/* for top level directory */
+	int	nfile;			/* max number of Netfiles */
+	Netfile	**f;
+
+	/* about net */
+	int	limit;			/* flow control */
+	int	alen;			/* address length */
+	int	mbps;			/* megabits per sec */
+	int	link;			/* link status */
+	uchar	addr[Nmaxaddr];
+	uchar	bcast[Nmaxaddr];
+	Netaddr	*maddr;			/* known multicast addresses */
+	int	nmaddr;			/* number of known multicast addresses */
+	Netaddr *mhash[Nmhash];		/* hash table of multicast addresses */
+	int	prom;			/* number of promiscuous opens */
+	int	scan;			/* number of base station scanners */
+	int	all;			/* number of -1 multiplexors */
+
+	/* statistics */
+	int	misses;
+	int	inpackets;
+	int	outpackets;
+	int	crcs;		/* input crc errors */
+	int	oerrs;		/* output errors */
+	int	frames;		/* framing errors */
+	int	overflows;	/* packet overflows */
+	int	buffs;		/* buffering errors */
+	int	soverflows;	/* software overflow */
+
+	/* routines for touching the hardware */
+	void	*arg;
+	void	(*promiscuous)(void*, int);
+	void	(*multicast)(void*, uchar*, int);
+	void	(*scanbs)(void*, uint);	/* scan for base stations */
+};
+
+void	netifinit(Netif*, char*, int, ulong);
+Walkqid*	netifwalk(Netif*, Chan*, Chan*, char **, int);
+Chan*	netifopen(Netif*, Chan*, int);
+void	netifclose(Netif*, Chan*);
+long	netifread(Netif*, Chan*, void*, long, ulong);
+Block*	netifbread(Netif*, Chan*, long, ulong);
+long	netifwrite(Netif*, Chan*, void*, long);
+int	netifwstat(Netif*, Chan*, uchar*, int);
+int	netifstat(Netif*, Chan*, uchar*, int);
+int	activemulti(Netif*, uchar*, int);
+
+/*
+ *  Ethernet specific
+ */
+enum
+{
+	Eaddrlen=	6,
+	ETHERMINTU =	60,		/* minimum transmit size */
+	ETHERMAXTU =	1514,		/* maximum transmit size */
+	ETHERHDRSIZE =	14,		/* size of an ethernet header */
+};
+
+struct Etherpkt
+{
+	uchar	d[Eaddrlen];
+	uchar	s[Eaddrlen];
+	uchar	type[2];
+	uchar	data[1500];
+};
--- /dev/null
+++ b/os/port/nocache.c
@@ -1,0 +1,49 @@
+#include	"u.h"
+#include	"../port/lib.h"
+#include	"mem.h"
+#include	"dat.h"
+#include	"fns.h"
+#include	"../port/error.h"
+
+/*
+ * stubs when no devmnt cache
+ */
+void
+cinit(void)
+{
+}
+
+void
+copen(Chan *c)
+{
+	c->flag &= ~CCACHE;
+}
+
+int
+cread(Chan *c, uchar *b, int n, vlong off)
+{
+	USED(c);
+	USED(b);
+	USED(n);
+	USED(off);
+	return 0;
+}
+
+void
+cwrite(Chan *c, uchar *buf, int n, vlong off)
+{
+	USED(c);
+	USED(buf);
+	USED(n);
+	USED(off);
+}
+
+void
+cupdate(Chan *c, uchar *buf,  int n, vlong off)
+{
+	USED(c);
+	USED(buf);
+	USED(n);
+	USED(off);
+}
+
--- /dev/null
+++ b/os/port/nodynld.c
@@ -1,0 +1,48 @@
+#include "u.h"
+#include "../port/lib.h"
+#include "../port/error.h"
+#include "mem.h"
+#include	"dat.h"
+#include	"fns.h"
+#include	<a.out.h>
+#include	<dynld.h>
+
+/*
+ * null kernel interface to dynld, to stop libinterp moaning
+ */
+
+void*
+dynimport(Dynobj*, char*, ulong)
+{
+	return nil;
+}
+
+void
+dynobjfree(Dynobj*)
+{
+}
+
+Dynobj*
+kdynloadfd(int fd, Dynsym *tab, int ntab)
+{
+	USED(fd, tab, ntab);
+	return nil;
+}
+
+int
+kdynloadable(int)
+{
+	return 0;
+}
+
+Dynobj*
+dynld(int)
+{
+	return nil;
+}
+
+int
+dynldable(int)
+{
+	return 0;
+}
--- /dev/null
+++ b/os/port/noenv.c
@@ -1,0 +1,33 @@
+/*
+ * use this when devenv.c not used
+ */
+#include "u.h"
+#include "../port/lib.h"
+#include "../port/error.h"
+#include "mem.h"
+#include	"dat.h"
+#include	"fns.h"
+
+/*
+ * null kernel interface
+ */
+Egrp*
+newegrp(void)
+{
+	return nil;
+}
+
+void
+closeegrp(Egrp*)
+{
+}
+
+void
+egrpcpy(Egrp*, Egrp*)
+{
+}
+
+void
+ksetenv(char*, char*, int)
+{
+}
--- /dev/null
+++ b/os/port/noscreen.c
@@ -1,0 +1,9 @@
+void
+screeninit(void)
+{
+}
+
+void
+screenrotate(int)
+{
+}
--- /dev/null
+++ b/os/port/parse.c
@@ -1,0 +1,114 @@
+#include	"u.h"
+#include	"../port/lib.h"
+#include	"mem.h"
+#include	"dat.h"
+#include	"fns.h"
+#include	"../port/error.h"
+
+/*
+ * Generous estimate of number of fields, including terminal nil pointer
+ */
+static int
+ncmdfield(char *p, int n)
+{
+	int white, nwhite;
+	char *ep;
+	int nf;
+
+	if(p == nil)
+		return 1;
+
+	nf = 0;
+	ep = p+n;
+	white = 1;	/* first text will start field */
+	while(p < ep){
+		nwhite = (strchr(" \t\r\n", *p++ & 0xFF) != 0);	/* UTF is irrelevant */
+		if(white && !nwhite)	/* beginning of field */
+			nf++;
+		white = nwhite;
+	}
+	return nf+1;	/* +1 for nil */
+}
+
+/*
+ *  parse a command written to a device
+ */
+Cmdbuf*
+parsecmd(char *p, int n)
+{
+	Cmdbuf *volatile cb;
+	int nf;
+	char *sp;
+
+	nf = ncmdfield(p, n);
+
+	/* allocate Cmdbuf plus string pointers plus copy of string including \0 */
+	sp = smalloc(sizeof(*cb) + nf * sizeof(char*) + n + 1);
+	cb = (Cmdbuf*)sp;
+	cb->f = (char**)(&cb[1]);
+	cb->buf = (char*)(&cb->f[nf]);
+
+	if(up!=nil && waserror()){
+		free(cb);
+		nexterror();
+	}
+	memmove(cb->buf, p, n);
+	if(up != nil)
+		poperror();
+
+	/* dump new line and null terminate */
+	if(n > 0 && cb->buf[n-1] == '\n')
+		n--;
+	cb->buf[n] = '\0';
+
+	cb->nf = tokenize(cb->buf, cb->f, nf-1);
+	cb->f[cb->nf] = nil;
+
+	return cb;
+}
+
+/*
+ * Reconstruct original message, for error diagnostic
+ */
+void
+cmderror(Cmdbuf *cb, char *s)
+{
+	int i;
+	char *p, *e;
+
+	p = up->genbuf;
+	e = p+ERRMAX-10;
+	p = seprint(p, e, "%s \"", s);
+	for(i=0; i<cb->nf; i++){
+		if(i > 0)
+			p = seprint(p, e, " ");
+		p = seprint(p, e, "%q", cb->f[i]);
+	}
+	strcpy(p, "\"");
+	error(up->genbuf);
+}
+
+/*
+ * Look up entry in table
+ */
+Cmdtab*
+lookupcmd(Cmdbuf *cb, Cmdtab *ctab, int nctab)
+{
+	int i;
+	Cmdtab *ct;
+
+	if(cb->nf == 0)
+		error("empty control message");
+
+	for(ct = ctab, i=0; i<nctab; i++, ct++){
+		if(strcmp(ct->cmd, "*") !=0)	/* wildcard always matches */
+		if(strcmp(ct->cmd, cb->f[0]) != 0)
+			continue;
+		if(ct->narg != 0 && ct->narg != cb->nf)
+			cmderror(cb, Ecmdargs);
+		return ct;
+	}
+
+	cmderror(cb, "unknown control message");
+	return nil;
+}
--- /dev/null
+++ b/os/port/pgrp.c
@@ -1,0 +1,277 @@
+#include	"u.h"
+#include	"../port/lib.h"
+#include	"mem.h"
+#include	"dat.h"
+#include	"fns.h"
+#include	"../port/error.h"
+
+static Ref pgrpid;
+static Ref mountid;
+
+Pgrp*
+newpgrp(void)
+{
+	Pgrp *p;
+
+	p = smalloc(sizeof(Pgrp));
+	p->ref = 1;
+	p->pgrpid = incref(&pgrpid);
+	p->progmode = 0644;
+	return p;
+}
+
+void
+closepgrp(Pgrp *p)
+{
+	Mhead **h, **e, *f, *next;
+	
+	if(p == nil || decref(p) != 0)
+		return;
+
+	wlock(&p->ns);
+	p->pgrpid = -1;
+
+	e = &p->mnthash[MNTHASH];
+	for(h = p->mnthash; h < e; h++) {
+		for(f = *h; f; f = next) {
+			wlock(&f->lock);
+			cclose(f->from);
+			mountfree(f->mount);
+			f->mount = nil;
+			next = f->hash;
+			wunlock(&f->lock);
+			putmhead(f);
+		}
+	}
+	wunlock(&p->ns);
+	cclose(p->dot);
+	cclose(p->slash);
+	free(p);
+}
+
+void
+pgrpinsert(Mount **order, Mount *m)
+{
+	Mount *f;
+
+	m->order = 0;
+	if(*order == 0) {
+		*order = m;
+		return;
+	}
+	for(f = *order; f; f = f->order) {
+		if(m->mountid < f->mountid) {
+			m->order = f;
+			*order = m;
+			return;
+		}
+		order = &f->order;
+	}
+	*order = m;
+}
+
+/*
+ * pgrpcpy MUST preserve the mountid allocation order of the parent group
+ */
+void
+pgrpcpy(Pgrp *to, Pgrp *from)
+{
+	int i;
+	Mount *n, *m, **link, *order;
+	Mhead *f, **tom, **l, *mh;
+
+	wlock(&from->ns);
+	if(waserror()){
+		wunlock(&from->ns);
+		nexterror();
+	}
+	order = 0;
+	tom = to->mnthash;
+	for(i = 0; i < MNTHASH; i++) {
+		l = tom++;
+		for(f = from->mnthash[i]; f; f = f->hash) {
+			rlock(&f->lock);
+			if(waserror()){
+				runlock(&f->lock);
+				nexterror();
+			}
+			mh = malloc(sizeof(Mhead));
+			if(mh == nil)
+				error(Enomem);
+			mh->from = f->from;
+			mh->ref = 1;
+			incref(mh->from);
+			*l = mh;
+			l = &mh->hash;
+			link = &mh->mount;
+			for(m = f->mount; m; m = m->next) {
+				n = newmount(mh, m->to, m->mflag, m->spec);
+				m->copy = n;
+				pgrpinsert(&order, m);
+				*link = n;
+				link = &n->next;
+			}
+			poperror();
+			runlock(&f->lock);
+		}
+	}
+	/*
+	 * Allocate mount ids in the same sequence as the parent group
+	 */
+	lock(&mountid.l);
+	for(m = order; m; m = m->order)
+		m->copy->mountid = mountid.ref++;
+	unlock(&mountid.l);
+
+	to->progmode = from->progmode;
+	to->slash = cclone(from->slash);
+	to->dot = cclone(from->dot);
+	to->nodevs = from->nodevs;
+
+	poperror();
+	wunlock(&from->ns);
+}
+
+Fgrp*
+newfgrp(Fgrp *old)
+{
+	Fgrp *new;
+	int n;
+
+	new = smalloc(sizeof(Fgrp));
+	new->ref = 1;
+	n = DELTAFD;
+	if(old != nil){
+		lock(old);
+		if(old->maxfd >= n)
+			n = (old->maxfd+1 + DELTAFD-1)/DELTAFD * DELTAFD;
+		new->maxfd = old->maxfd;
+		unlock(old);
+	}
+	new->nfd = n;
+	new->fd = smalloc(n*sizeof(Chan*));
+	return new;
+}
+
+Fgrp*
+dupfgrp(Fgrp *f)
+{
+	int i;
+	Chan *c;
+	Fgrp *new;
+	int n;
+
+	new = smalloc(sizeof(Fgrp));
+	new->ref = 1;
+	lock(f);
+	n = DELTAFD;
+	if(f->maxfd >= n)
+		n = (f->maxfd+1 + DELTAFD-1)/DELTAFD * DELTAFD;
+	new->nfd = n;
+	new->fd = malloc(n*sizeof(Chan*));
+	if(new->fd == nil){
+		unlock(f);
+		free(new);
+		error(Enomem);
+	}
+	new->maxfd = f->maxfd;
+	new->minfd = f->minfd;
+	for(i = 0; i <= f->maxfd; i++) {
+		if(c = f->fd[i]){
+			incref(c);
+			new->fd[i] = c;
+		}
+	}
+	unlock(f);
+
+	return new;
+}
+
+void
+closefgrp(Fgrp *f)
+{
+	int i;
+	Chan *c;
+
+	if(f == nil || decref(f) != 0)
+		return;
+
+	for(i = 0; i <= f->maxfd; i++)
+		if(c = f->fd[i])
+			cclose(c);
+
+	free(f->fd);
+	free(f);
+}
+
+Mount*
+newmount(Mhead *mh, Chan *to, int flag, char *spec)
+{
+	Mount *m;
+
+	m = smalloc(sizeof(Mount));
+	m->to = to;
+	m->head = mh;
+	incref(to);
+	m->mountid = incref(&mountid);
+	m->mflag = flag;
+	if(spec != 0)
+		kstrdup(&m->spec, spec);
+
+	return m;
+}
+
+void
+mountfree(Mount *m)
+{
+	Mount *f;
+
+	while(m) {
+		f = m->next;
+		cclose(m->to);
+		m->mountid = 0;
+		free(m->spec);
+		free(m);
+		m = f;
+	}
+}
+
+void
+resrcwait(char *reason)
+{
+	char *p;
+
+	if(up == 0)
+		panic("resrcwait");
+
+	p = up->psstate;
+	if(reason) {
+		up->psstate = reason;
+		print("%s\n", reason);
+	}
+
+	tsleep(&up->sleep, return0, 0, 300);
+	up->psstate = p;
+}
+
+void
+closesigs(Skeyset *s)
+{
+	int i;
+
+	if(s == nil || decref(s) != 0)
+		return;
+	for(i=0; i<s->nkey; i++)
+		freeskey(s->keys[i]);
+	free(s);
+}
+
+void
+freeskey(Signerkey *key)
+{
+	if(key == nil || decref(key) != 0)
+		return;
+	free(key->owner);
+	(*key->pkfree)(key->pk);
+	free(key);
+}
--- /dev/null
+++ b/os/port/portbreak.c
@@ -1,0 +1,161 @@
+#include "u.h"
+#include "../port/lib.h"
+#include "mem.h"
+#include "dat.h"
+#include "portfns.h"
+#include "ureg.h"
+#include "../port/error.h"
+
+//
+// These bits used to be in port/devdbg but were removed in
+// order to allow for using hardware debug features on certain
+// architectures
+//
+
+extern void breakset(Bkpt *b);
+extern void breakrestore(Bkpt *b);
+extern Bkpt* breakclear(int id);
+extern void breaknotify(Bkpt *b, Proc *p);
+extern int breakmatch(BkptCond *cond, Ureg *ur, Proc *p);
+
+void	skipfree(Bkpt *b);
+Bkpt*newskip(ulong addr, Bkpt *skipb, Proc *skipp);
+Bkpt *skipalloc;
+extern Bkpt	*breakpoints;
+typedef struct SkipArg SkipArg;
+struct SkipArg
+{
+	Bkpt *b;
+	Proc *p;
+};
+
+void
+skiphandler(Bkpt *b)
+{
+	SkipArg *a = b->aux;
+	Bkpt *l;
+
+	if(breakclear(b->id) == nil)
+		panic("skiphandler: breakclear() failed");
+	breakrestore(a->b);
+	l = a->b->link;
+	while(l != nil) {
+		breakrestore(l);
+		l = l->link;
+	}
+	skipfree(b);
+	a->p->dbgstop = 0;		// Whoo!
+	if(a->p->state == Stopped)
+		ready(a->p);
+}
+
+Bkpt*
+newskip(ulong addr, Bkpt *skipb, Proc *skipp)
+{
+	Bkpt *b;
+	SkipArg *a;
+
+	b = skipalloc;
+	if(b == nil)
+		panic("newskip(): no free skips\n");
+	skipalloc = b->next;
+
+	b->addr = addr;
+	b->conditions->val = addr;
+	b->link = nil;
+	a = b->aux;
+	a->b = skipb;
+	a->p = skipp;
+
+	return b;
+}
+
+void
+skipfree(Bkpt *b)
+{
+	b->next = skipalloc;
+	skipalloc = b;
+}
+
+//
+// Called from the exception handler when a breakpoint instruction has been
+// hit.  This cannot not be called unless at least one breakpoint with this
+// address is in the list of breakpoints.  (All breakpoint notifications must
+// previously have been set via setbreak())
+//
+//	foreach breakpoint in list
+//		if breakpoint matches conditions
+//			notify the break handler
+//	if no breakpoints matched the conditions
+//		pick a random breakpoint set to this address
+//
+//		set a breakpoint at the next instruction to be executed,
+//		and pass the current breakpoint to the "skiphandler"
+//
+//		clear the current breakpoint
+//
+//		Tell the scheduler to stop scheduling, so the caller is
+//		guaranteed to execute the instruction, followed by the
+//		added breakpoint.
+//
+//
+int
+breakhit(Ureg *ur, Proc *p)
+{
+	Bkpt *b;
+	int nmatched;
+	Bkpt *skip;
+
+	nmatched = 0;
+	for(b = breakpoints; b != nil; b = b->next) {
+		if(breakmatch(b->conditions, ur, p)) {
+			breaknotify(b, p);
+			++nmatched;
+		}
+	}
+
+	if(nmatched)
+		return BrkSched;
+
+	skip = nil;
+	for(b = breakpoints; b != nil;  b = b->next) {
+		if(b->addr == ur->pc) {
+			if(breakclear(b->id) == nil)
+				panic("breakhit: breakclear() failed");
+
+			if(skip == nil)
+				skip = newskip(machnextaddr(ur), b, p);
+			else {
+				b->link = skip->link;
+				skip->link = b;
+			}
+		}
+	}
+	if(skip == nil)
+		return BrkSched;
+	breakset(skip);
+	return BrkNoSched;
+}
+
+void
+portbreakinit(void)
+{
+	Bkpt *b;
+	int i;
+
+	skipalloc = mallocz(conf.nproc*(sizeof(Bkpt)+sizeof(BkptCond)+sizeof(SkipArg)), 1);
+	if(skipalloc == nil)
+		error(Enomem);
+
+	b = skipalloc;
+	for(i=0; i < conf.nproc-1; i++) {
+		b->id = -(i+1);
+		b->conditions = (BkptCond*)((uchar*)b + sizeof(Bkpt));
+		b->conditions->op = 'b';
+		b->handler = skiphandler;
+		b->aux = (SkipArg*)((uchar*)b+sizeof(Bkpt)+sizeof(BkptCond));
+		b->next = (Bkpt*)((uchar*)b+sizeof(Bkpt)+sizeof(BkptCond)+sizeof(SkipArg));
+		b = b->next;
+	}
+	b->next = nil;
+}
--- /dev/null
+++ b/os/port/portclock.c
@@ -1,0 +1,277 @@
+#include "u.h"
+#include "../port/lib.h"
+#include "mem.h"
+#include "dat.h"
+#include "fns.h"
+#include "io.h"
+#include "ureg.h"
+
+struct Timers
+{
+	Lock;
+	Timer	*head;
+};
+
+static Timers timers[MAXMACH];
+
+ulong intrcount[MAXMACH];
+ulong fcallcount[MAXMACH];
+
+static uvlong
+tadd(Timers *tt, Timer *nt)
+{
+	Timer *t, **last;
+
+	/* Called with tt locked */
+	assert(nt->tt == nil);
+	switch(nt->tmode){
+	default:
+		panic("timer");
+		break;
+	case Trelative:
+		assert(nt->tns > 0);
+		nt->twhen = fastticks(nil) + ns2fastticks(nt->tns);
+		break;
+	case Tabsolute:
+		nt->twhen = tod2fastticks(nt->tns);
+		break;
+	case Tperiodic:
+		assert(nt->tns >= 100000);	/* At least 100 µs period */
+		if(nt->twhen == 0){
+			/* look for another timer at same frequency for combining */
+			for(t = tt->head; t; t = t->tnext){
+				if(t->tmode == Tperiodic && t->tns == nt->tns)
+					break;
+			}
+			if (t)
+				nt->twhen = t->twhen;
+			else
+				nt->twhen = fastticks(nil);
+		}
+		nt->twhen += ns2fastticks(nt->tns);
+		break;
+	}
+
+	for(last = &tt->head; t = *last; last = &t->tnext){
+		if(t->twhen > nt->twhen)
+			break;
+	}
+	nt->tnext = *last;
+	*last = nt;
+	nt->tt = tt;
+	if(last == &tt->head)
+		return nt->twhen;
+	return 0;
+}
+
+static uvlong
+tdel(Timer *dt)
+{
+
+	Timer *t, **last;
+	Timers *tt;
+
+	tt = dt->tt;
+	if (tt == nil)
+		return 0;
+	for(last = &tt->head; t = *last; last = &t->tnext){
+		if(t == dt){
+			assert(dt->tt);
+			dt->tt = nil;
+			*last = t->tnext;
+			break;
+		}
+	}
+	if(last == &tt->head && tt->head)
+		return tt->head->twhen;
+	return 0;
+}
+
+/* add or modify a timer */
+void
+timeradd(Timer *nt)
+{
+	Timers *tt;
+	vlong when;
+
+	if (nt->tmode == Tabsolute){
+		when = todget(nil);
+		if (nt->tns <= when){
+	//		if (nt->tns + MS2NS(10) <= when)	/* small deviations will happen */
+	//			print("timeradd (%lld %lld) %lld too early 0x%lux\n",
+	//				when, nt->tns, when - nt->tns, getcallerpc(&nt));
+			nt->tns = when;
+		}
+	}
+	/* Must lock Timer struct before Timers struct */
+	ilock(nt);
+	if(tt = nt->tt){
+		ilock(tt);
+		tdel(nt);
+		iunlock(tt);
+	}
+	tt = &timers[m->machno];
+	ilock(tt);
+	when = tadd(tt, nt);
+	if(when)
+		timerset(when);
+	iunlock(tt);
+	iunlock(nt);
+}
+
+
+void
+timerdel(Timer *dt)
+{
+	Timers *tt;
+	uvlong when;
+
+	ilock(dt);
+	if(tt = dt->tt){
+		ilock(tt);
+		when = tdel(dt);
+		if(when && tt == &timers[m->machno])
+			timerset(tt->head->twhen);
+		iunlock(tt);
+	}
+	iunlock(dt);
+}
+
+void
+hzclock(Ureg *ur)
+{
+	m->ticks++;
+	if(m->proc)
+		m->proc->pc = ur->pc;
+
+	kmapinval();
+
+	if(kproftick != nil)
+		kproftick(ur->pc);
+
+	if((active.machs&(1<<m->machno)) == 0)
+		return;
+
+	if(active.exiting) {
+		print("someone's exiting\n");
+		exit(0);
+	}
+
+	checkalarms();
+
+	if(up && up->state == Running){
+		if(anyready()){
+			sched();
+			splhi();
+		}
+	}
+}
+
+void
+timerintr(Ureg *u, uvlong)
+{
+	Timer *t;
+	Timers *tt;
+	uvlong when, now;
+	int callhzclock;
+	static int sofar;
+
+	intrcount[m->machno]++;
+	callhzclock = 0;
+	tt = &timers[m->machno];
+	now = fastticks(nil);
+	ilock(tt);
+	while(t = tt->head){
+		/*
+		 * No need to ilock t here: any manipulation of t
+		 * requires tdel(t) and this must be done with a
+		 * lock to tt held.  We have tt, so the tdel will
+		 * wait until we're done
+		 */
+		when = t->twhen;
+		if(when > now){
+			timerset(when);
+			iunlock(tt);
+			if(callhzclock)
+				hzclock(u);
+			return;
+		}
+		tt->head = t->tnext;
+		assert(t->tt == tt);
+		t->tt = nil;
+		fcallcount[m->machno]++;
+		iunlock(tt);
+		if(t->tf)
+			(*t->tf)(u, t);
+		else
+			callhzclock++;
+		ilock(tt);
+		if(t->tmode == Tperiodic)
+			tadd(tt, t);
+	}
+	iunlock(tt);
+}
+
+void
+timersinit(void)
+{
+	Timer *t;
+
+	todinit();
+	t = malloc(sizeof(*t));
+	t->tmode = Tperiodic;
+	t->tt = nil;
+	t->tns = 1000000000/HZ;
+	t->tf = nil;
+	timeradd(t);
+}
+
+Timer*
+addclock0link(void (*f)(void), int ms)
+{
+	Timer *nt;
+	uvlong when;
+
+	/* Synchronize to hztimer if ms is 0 */
+	nt = malloc(sizeof(Timer));
+	if(ms == 0)
+		ms = 1000/HZ;
+	nt->tns = (vlong)ms*1000000LL;
+	nt->tmode = Tperiodic;
+	nt->tt = nil;
+	nt->tf = (void (*)(Ureg*, Timer*))f;
+
+	ilock(&timers[0]);
+	when = tadd(&timers[0], nt);
+	if(when)
+		timerset(when);
+	iunlock(&timers[0]);
+	return nt;
+}
+
+/*
+ *  This tk2ms avoids overflows that the macro version is prone to.
+ *  It is a LOT slower so shouldn't be used if you're just converting
+ *  a delta.
+ */
+ulong
+tk2ms(ulong ticks)
+{
+	uvlong t, hz;
+
+	t = ticks;
+	hz = HZ;
+	t *= 1000L;
+	t = t/hz;
+	ticks = t;
+	return ticks;
+}
+
+ulong
+ms2tk(ulong ms)
+{
+	/* avoid overflows at the cost of precision */
+	if(ms >= 1000000000/HZ)
+		return (ms/1000)*HZ;
+	return (ms*HZ+500)/1000;
+}
--- /dev/null
+++ b/os/port/portdat.h
@@ -1,0 +1,670 @@
+typedef struct Alarms	Alarms;
+typedef struct Block	Block;
+typedef struct Bkpt Bkpt;
+typedef struct BkptCond BkptCond;
+typedef struct Chan	Chan;
+typedef struct Cmdbuf	Cmdbuf;
+typedef struct Cmdtab	Cmdtab;
+typedef struct Cname	Cname;
+typedef struct Crypt	Crypt;
+typedef struct Dev	Dev;
+typedef struct DevConf	DevConf;
+typedef struct Dirtab	Dirtab;
+typedef struct Edf	Edf;
+typedef struct Egrp	Egrp;
+typedef struct Evalue	Evalue;
+typedef struct Fgrp	Fgrp;
+typedef struct List	List;
+typedef struct Log	Log;
+typedef struct Logflag	Logflag;
+typedef struct Mntcache Mntcache;
+typedef struct Mntparam Mntparam;
+typedef struct Mount	Mount;
+typedef struct Mntrpc	Mntrpc;
+typedef struct Mntwalk	Mntwalk;
+typedef struct Mnt	Mnt;
+typedef struct Mhead	Mhead;
+typedef struct Osenv	Osenv;
+typedef struct Pgrp	Pgrp;
+typedef struct Proc	Proc;
+typedef struct QLock	QLock;
+typedef struct Queue	Queue;
+typedef struct Ref	Ref;
+typedef struct Rendez	Rendez;
+typedef struct Rept	Rept;
+typedef struct Rootdata	Rootdata;
+typedef struct RWlock	RWlock;
+typedef struct Signerkey Signerkey;
+typedef struct Skeyset	Skeyset;
+typedef struct Talarm	Talarm;
+typedef struct Timer	Timer;
+typedef struct Timers	Timers;
+typedef struct Uart	Uart;
+typedef struct Walkqid	Walkqid;
+typedef int    Devgen(Chan*, char*, Dirtab*, int, int, Dir*);
+
+#pragma incomplete DevConf
+#pragma incomplete Edf
+#pragma incomplete Mntcache
+#pragma incomplete Mntrpc
+#pragma incomplete Queue
+#pragma incomplete Timers
+
+#include "fcall.h"
+#include <pool.h>
+
+struct Ref
+{
+	Lock	l;
+	long	ref;
+};
+
+struct Rendez
+{
+	Lock;
+	Proc	*p;
+};
+
+struct Rept
+{
+	Lock	l;
+	Rendez	r;
+	void	*o;
+	int	t;
+	int	(*active)(void*);
+	int	(*ck)(void*, int);
+	void	(*f)(void*);	/* called with VM acquire()'d */
+};
+
+struct Osenv
+{
+	char	*syserrstr;	/* last error from a system call, errbuf0 or 1 */
+	char	*errstr;	/* reason we're unwinding the error stack, errbuf1 or 0 */
+	char	errbuf0[ERRMAX];
+	char	errbuf1[ERRMAX];
+	Pgrp*	pgrp;		/* Ref to namespace, working dir and root */
+	Fgrp*	fgrp;		/* Ref to file descriptors */
+	Egrp*	egrp;	/* Environment vars */
+	Skeyset*	sigs;		/* Signed module keys */
+	Rendez*	rend;		/* Synchro point */
+	Queue*	waitq;		/* Info about dead children */
+	Queue*	childq;		/* Info about children for debuggers */
+	void*	debug;		/* Debugging master */
+	int	uid;		/* Numeric user id for system */
+	int	gid;		/* Numeric group id for system */
+	char*	user;		/* Inferno user name */
+	FPenv	fpu;		/* Floating point thread state */
+};
+
+enum
+{
+	Nopin =	-1
+};
+
+struct QLock
+{
+	Lock	use;			/* to access Qlock structure */
+	Proc	*head;			/* next process waiting for object */
+	Proc	*tail;			/* last process waiting for object */
+	int	locked;			/* flag */
+};
+
+struct RWlock
+{
+	Lock;				/* Lock modify lock */
+	QLock	x;			/* Mutual exclusion lock */
+	QLock	k;			/* Lock for waiting writers */
+	int	readers;		/* Count of readers in lock */
+};
+
+struct Talarm
+{
+	Lock;
+	Proc*	list;
+};
+
+struct Alarms
+{
+	QLock;
+	Proc*	head;
+};
+
+struct Rootdata
+{
+	int	dotdot;
+	void	*ptr;
+	int	size;
+	int	*sizep;
+};
+
+/*
+ * Access types in namec & channel flags
+ */
+enum
+{
+	Aaccess,			/* as in stat, wstat */
+	Abind,			/* for left-hand-side of bind */
+	Atodir,				/* as in chdir */
+	Aopen,				/* for i/o */
+	Amount,				/* to be mounted or mounted upon */
+	Acreate,			/* is to be created */
+	Aremove,			/* will be removed by caller */
+
+	COPEN	= 0x0001,		/* for i/o */
+	CMSG	= 0x0002,		/* the message channel for a mount */
+	CCEXEC	= 0x0008,		/* close on exec */
+	CFREE	= 0x0010,		/* not in use */
+	CRCLOSE	= 0x0020,		/* remove on close */
+	CCACHE	= 0x0080,		/* client cache */
+};
+
+enum
+{
+	BINTR		=	(1<<0),
+	BFREE		=	(1<<1),
+	Bipck	=	(1<<2),		/* ip checksum */
+	Budpck	=	(1<<3),		/* udp checksum */
+	Btcpck	=	(1<<4),		/* tcp checksum */
+	Bpktck	=	(1<<5),		/* packet checksum */
+};
+
+struct Block
+{
+	Block*	next;
+	Block*	list;
+	uchar*	rp;			/* first unconsumed byte */
+	uchar*	wp;			/* first empty byte */
+	uchar*	lim;			/* 1 past the end of the buffer */
+	uchar*	base;			/* start of the buffer */
+	void	(*free)(Block*);
+	ushort	flag;
+	ushort	checksum;		/* IP checksum of complete packet (minus media header) */
+};
+#define BLEN(s)	((s)->wp - (s)->rp)
+#define BALLOC(s) ((s)->lim - (s)->base)
+
+struct Chan
+{
+	Lock;
+	Ref;
+	Chan*	next;			/* allocation */
+	Chan*	link;
+	vlong	offset;			/* in file */
+	ushort	type;
+	ulong	dev;
+	ushort	mode;			/* read/write */
+	ushort	flag;
+	Qid	qid;
+	int	fid;			/* for devmnt */
+	ulong	iounit;	/* chunk size for i/o; 0==default */
+	Mhead*	umh;			/* mount point that derived Chan; used in unionread */
+	Chan*	umc;			/* channel in union; held for union read */
+	QLock	umqlock;		/* serialize unionreads */
+	int	uri;			/* union read index */
+	int	dri;			/* devdirread index */
+	ulong	mountid;
+	Mntcache *mcp;			/* Mount cache pointer */
+	Mnt		*mux;		/* Mnt for clients using me for messages */
+	union {
+		void*	aux;
+		char	tag[4];		/* for iproute */
+	};
+	Chan*	mchan;			/* channel to mounted server */
+	Qid	mqid;			/* qid of root of mount point */
+	Cname	*name;
+};
+
+struct Cname
+{
+	Ref;
+	int	alen;			/* allocated length */
+	int	len;			/* strlen(s) */
+	char	*s;
+};
+
+struct Dev
+{
+	int	dc;
+	char*	name;
+
+	void	(*reset)(void);
+	void	(*init)(void);
+	void	(*shutdown)(void);
+	Chan*	(*attach)(char*);
+	Walkqid*	(*walk)(Chan*, Chan*, char**, int);
+	int	(*stat)(Chan*, uchar*, int);
+	Chan*	(*open)(Chan*, int);
+	void	(*create)(Chan*, char*, int, ulong);
+	void	(*close)(Chan*);
+	long	(*read)(Chan*, void*, long, vlong);
+	Block*	(*bread)(Chan*, long, ulong);
+	long	(*write)(Chan*, void*, long, vlong);
+	long	(*bwrite)(Chan*, Block*, ulong);
+	void	(*remove)(Chan*);
+	int	(*wstat)(Chan*, uchar*, int);
+	void	(*power)(int);	/* power mgt: power(1) → on, power (0) → off */
+	int	(*config)(int, char*, DevConf*);
+};
+
+struct Dirtab
+{
+	char	name[KNAMELEN];
+	Qid	qid;
+	vlong	length;
+	long	perm;
+};
+
+struct Walkqid
+{
+	Chan	*clone;
+	int	nqid;
+	Qid	qid[1];
+};
+
+enum
+{
+	NSMAX	=	1000,
+	NSLOG	=	7,
+	NSCACHE	=	(1<<NSLOG),
+};
+
+struct Mntwalk				/* state for /proc/#/ns */
+{
+	int		cddone;
+	ulong	id;
+	Mhead*	mh;
+	Mount*	cm;
+};
+
+struct Mount
+{
+	ulong	mountid;
+	Mount*	next;
+	Mhead*	head;
+	Mount*	copy;
+	Mount*	order;
+	Chan*	to;			/* channel replacing channel */
+	int	mflag;
+	char	*spec;
+};
+
+struct Mhead
+{
+	Ref;
+	RWlock	lock;
+	Chan*	from;			/* channel mounted upon */
+	Mount*	mount;			/* what's mounted upon it */
+	Mhead*	hash;			/* Hash chain */
+};
+
+struct Mnt
+{
+	Lock;
+	/* references are counted using c->ref; channels on this mount point incref(c->mchan) == Mnt.c */
+	Chan	*c;		/* Channel to file service */
+	Proc	*rip;		/* Reader in progress */
+	Mntrpc	*queue;		/* Queue of pending requests on this channel */
+	ulong	id;		/* Multiplexer id for channel check */
+	Mnt	*list;		/* Free list */
+	int	flags;		/* cache */
+	int	msize;		/* data + IOHDRSZ */
+	char	*version;			/* 9P version */
+	Queue	*q;		/* input queue */
+};
+
+enum
+{
+	RENDLOG	=	5,
+	RENDHASH =	1<<RENDLOG,		/* Hash to lookup rendezvous tags */
+	MNTLOG	=	5,
+	MNTHASH =	1<<MNTLOG,		/* Hash to walk mount table */
+	DELTAFD=		20,		/* allocation quantum for process file descriptors */
+	MAXNFD =		4000,		/* max per process file descriptors */
+	MAXKEY =		8,	/* keys for signed modules */
+};
+#define MOUNTH(p,qid)	((p)->mnthash[(qid).path&((1<<MNTLOG)-1)])
+
+struct Mntparam {
+	Chan*	chan;
+	Chan*	authchan;
+	char*	spec;
+	int	flags;
+};
+
+struct Pgrp
+{
+	Ref;				/* also used as a lock when mounting */
+	ulong	pgrpid;
+	QLock	debug;			/* single access via devproc.c */
+	RWlock	ns;			/* Namespace n read/one write lock */
+	QLock	nsh;
+	Mhead*	mnthash[MNTHASH];
+	int	progmode;
+	Chan*	dot;
+	Chan*	slash;
+	int	nodevs;
+	int	pin;
+};
+
+struct Fgrp
+{
+	Lock;
+	Ref;
+	Chan**	fd;
+	int	nfd;			/* number of fd slots */
+	int	maxfd;			/* highest fd in use */
+	int	minfd;			/* lower bound on free fd */
+};
+
+struct Evalue
+{
+	char	*var;
+	char	*val;
+	int	len;
+	Qid	qid;
+	Evalue	*next;
+};
+
+struct Egrp
+{
+	Ref;
+	QLock;
+	Evalue	*entries;
+	ulong	path;	/* qid.path of next Evalue to be allocated */
+	ulong	vers;	/* of Egrp */
+};
+
+struct Signerkey
+{
+	Ref;
+	char*	owner;
+	ushort	footprint;
+	ulong	expires;
+	void*	alg;
+	void*	pk;
+	void	(*pkfree)(void*);
+};
+
+struct Skeyset
+{
+	Ref;
+	QLock;
+	ulong	flags;
+	char*	devs;
+	int	nkey;
+	Signerkey	*keys[MAXKEY];
+};
+
+/*
+ * fasttick timer interrupts
+ */
+enum {
+	/* Mode */
+	Trelative,	/* timer programmed in ns from now */
+	Tabsolute,	/* timer programmed in ns since epoch */
+	Tperiodic,	/* periodic timer, period in ns */
+};
+
+struct Timer
+{
+	/* Public interface */
+	int	tmode;		/* See above */
+	vlong	tns;		/* meaning defined by mode */
+	void	(*tf)(Ureg*, Timer*);
+	void	*ta;
+	/* Internal */
+	Lock;
+	Timers	*tt;		/* Timers queue this timer runs on */
+	vlong	twhen;		/* ns represented in fastticks */
+	Timer	*tnext;
+};
+
+enum
+{
+	Dead = 0,		/* Process states */
+	Moribund,
+	Ready,
+	Scheding,
+	Running,
+	Queueing,
+	Wakeme,
+	Broken,
+	Stopped,
+	Rendezvous,
+	Waitrelease,
+
+	Proc_stopme = 1, 	/* devproc requests */
+	Proc_exitme,
+	Proc_traceme,
+	Proc_exitbig,
+
+	NERR		= 30,
+
+	Unknown		= 0,
+	IdleGC,
+	Interp,
+	BusyGC,
+
+	PriLock		= 0,	/* Holding Spin lock */
+	PriEdf,	/* active edf processes */
+	PriRelease,	/* released edf processes */
+	PriRealtime,		/* Video telephony */
+	PriHicodec,		/* MPEG codec */
+	PriLocodec,		/* Audio codec */
+	PriHi,			/* Important task */
+	PriNormal,
+	PriLo,
+	PriBackground,
+	PriExtra,	/* edf processes we don't care about */
+	Nrq
+};
+
+struct Proc
+{
+	Label		sched;		/* known to l.s */
+	char*		kstack;		/* known to l.s */
+	Mach*		mach;		/* machine running this proc */
+	char		text[KNAMELEN];
+	Proc*		rnext;		/* next process in run queue */
+	Proc*		qnext;		/* next process on queue for a QLock */
+	QLock*		qlock;		/* addrof qlock being queued for DEBUG */
+	int		state;
+	int		type;
+	void*		prog;		/* Dummy Prog for interp release */
+	void*		iprog;
+	Osenv*		env;
+	Osenv		defenv;
+	int		swipend;	/* software interrupt pending for Prog */
+	Lock		sysio;		/* note handler lock */
+	char*		psstate;	/* What /proc/#/status reports */
+	ulong		pid;
+	int		fpstate;
+	int		procctl;	/* Control for /proc debugging */
+	ulong		pc;		/* DEBUG only */
+	Lock	rlock;	/* sync between sleep/swiproc for r */
+	Rendez*		r;		/* rendezvous point slept on */
+	Rendez		sleep;		/* place for syssleep/debug */
+	int		killed;		/* by swiproc */
+	int		kp;		/* true if a kernel process */
+	ulong		alarm;		/* Time of call */
+	int		pri;		/* scheduler priority */
+	ulong		twhen;
+	Rendez*		trend;
+	Proc*		tlink;
+	int		(*tfn)(void*);
+	void		(*kpfun)(void*);
+	void*		arg;
+	FPU		fpsave;
+	int		scallnr;
+	int		nerrlab;
+	Label		errlab[NERR];
+	char	genbuf[128];	/* buffer used e.g. for last name element from namec */
+	Mach*		mp;		/* machine this process last ran on */
+	Mach*		wired;
+	ulong		movetime;	/* next time process should switch processors */
+	ulong		delaysched;
+	int			preempted;	/* process yielding in interrupt */
+	ulong		qpc;		/* last call that blocked in qlock */
+	void*		dbgreg;		/* User registers for devproc */
+ 	int		dbgstop;		/* don't run this kproc */
+	Edf*	edf;	/* if non-null, real-time proc, edf contains scheduling params */
+};
+
+enum
+{
+	/* kproc flags */
+	KPDUPPG		= (1<<0),
+	KPDUPFDG	= (1<<1),
+	KPDUPENVG	= (1<<2),
+	KPDUP = KPDUPPG | KPDUPFDG | KPDUPENVG
+};
+
+enum {
+	BrkSched,
+	BrkNoSched,
+};
+
+struct BkptCond
+{
+	uchar op;
+	ulong val;
+	BkptCond *next;
+};
+
+struct Bkpt
+{
+	int id;
+	ulong addr;
+	BkptCond *conditions;
+	Instr instr;
+	void (*handler)(Bkpt*);
+	void *aux;
+	Bkpt *next;
+	Bkpt *link;
+};
+
+enum
+{
+	PRINTSIZE =	256,
+	NUMSIZE	=	12,		/* size of formatted number */
+	MB =		(1024*1024),
+	READSTR =	1000,		/* temporary buffer size for device reads */
+};
+
+extern	Conf	conf;
+extern	char*	conffile;
+extern	int	consoleprint;
+extern	Dev*	devtab[];
+extern	char*	eve;
+extern	int	hwcurs;
+extern	FPU	initfp;
+extern  Queue	*kbdq;
+extern  Queue	*kscanq;
+extern  Ref	noteidalloc;
+extern  Queue	*printq;
+extern	uint	qiomaxatomic;
+extern	char*	statename[];
+extern	char*	sysname;
+extern	Talarm	talarm;
+
+/*
+ *  action log
+ */
+struct Log {
+	Lock;
+	int	opens;
+	char*	buf;
+	char	*end;
+	char	*rptr;
+	int	len;
+	int	nlog;
+	int	minread;
+
+	int	logmask;	/* mask of things to debug */
+
+	QLock	readq;
+	Rendez	readr;
+};
+
+struct Logflag {
+	char*	name;
+	int	mask;
+};
+
+struct Cmdbuf
+{
+	char	*buf;
+	char	**f;
+	int	nf;
+};
+
+struct Cmdtab
+{
+	int	index;	/* used by client to switch on result */
+	char	*cmd;	/* command name */
+	int	narg;	/* expected #args; 0 ==> variadic */
+};
+
+enum
+{
+	MAXPOOL		= 8,
+};
+
+extern Pool*	mainmem;
+extern Pool*	heapmem;
+extern Pool*	imagmem;
+
+/* queue state bits,  Qmsg, Qcoalesce, and Qkick can be set in qopen */
+enum
+{
+	/* Queue.state */
+	Qstarve		= (1<<0),	/* consumer starved */
+	Qmsg		= (1<<1),	/* message stream */
+	Qclosed		= (1<<2),	/* queue has been closed/hungup */
+	Qflow		= (1<<3),	/* producer flow controlled */
+	Qcoalesce	= (1<<4),	/* coallesce packets on read */
+	Qkick		= (1<<5),	/* always call the kick routine after qwrite */
+};
+
+#define DEVDOTDOT -1
+
+#pragma	varargck	argpos	print	1
+#pragma	varargck	argpos	snprint	3
+#pragma	varargck	argpos	seprint	3
+#pragma	varargck	argpos	sprint	2
+#pragma	varargck	argpos	fprint	2
+#pragma	varargck	argpos	iprint	1
+#pragma	varargck	argpos	panic	1
+#pragma	varargck	argpos	kwerrstr	1
+#pragma	varargck	argpos	kprint	1
+
+#pragma	varargck	type	"lld"	vlong
+#pragma	varargck	type	"llx"	vlong
+#pragma	varargck	type	"lld"	uvlong
+#pragma	varargck	type	"llx"	uvlong
+#pragma	varargck	type	"lx"	void*
+#pragma	varargck	type	"ld"	long
+#pragma	varargck	type	"lx"	long
+#pragma	varargck	type	"ld"	ulong
+#pragma	varargck	type	"lx"	ulong
+#pragma	varargck	type	"d"	int
+#pragma	varargck	type	"x"	int
+#pragma	varargck	type	"c"	int
+#pragma	varargck	type	"C"	int
+#pragma	varargck	type	"d"	uint
+#pragma	varargck	type	"x"	uint
+#pragma	varargck	type	"c"	uint
+#pragma	varargck	type	"C"	uint
+#pragma	varargck	type	"f"	double
+#pragma	varargck	type	"e"	double
+#pragma	varargck	type	"g"	double
+#pragma	varargck	type	"s"	char*
+#pragma	varargck	type	"S"	Rune*
+#pragma	varargck	type	"r"	void
+#pragma	varargck	type	"%"	void
+#pragma	varargck	type	"I"	uchar*
+#pragma	varargck	type	"V"	uchar*
+#pragma	varargck	type	"E"	uchar*
+#pragma	varargck	type	"M"	uchar*
+#pragma	varargck	type	"p"	void*
+#pragma	varargck	type	"q"	char*
--- /dev/null
+++ b/os/port/portfns.h
@@ -1,0 +1,318 @@
+#define		FPinit() fpinit() /* remove this if math lib is linked */
+void		FPrestore(void*);
+void		FPsave(void*);
+Timer*		addclock0link(void (*)(void), int);
+Cname*		addelem(Cname*, char*);
+void		addprog(Proc*);
+void		addrootfile(char*, uchar*, ulong);
+Block*		adjustblock(Block*, int);
+Block*		allocb(int);
+int	anyhigher(void);
+int	anyready(void);
+void	_assert(char*);
+Block*		bl2mem(uchar*, Block*, int);
+int		blocklen(Block*);
+int	breakhit(Ureg *ur, Proc*);
+void		callwithureg(void(*)(Ureg*));
+char*		channame(Chan*);
+int		canlock(Lock*);
+int		canqlock(QLock*);
+void		cclose(Chan*);
+int		canrlock(RWlock*);
+void		chandevinit(void);
+void		chandevreset(void);
+void		chandevshutdown(void);
+Dir*		chandirstat(Chan*);
+void		chanfree(Chan*);
+void		chanrec(Mnt*);
+void		checkalarms(void);
+void		checkb(Block*, char*);
+void		cinit(void);
+Chan*		cclone(Chan*);
+void		cclose(Chan*);
+void		closeegrp(Egrp*);
+void		closefgrp(Fgrp*);
+void		closemount(Mount*);
+void		closepgrp(Pgrp*);
+void		closesigs(Skeyset*);
+void		cmderror(Cmdbuf*, char*);
+int		cmount(Chan*, Chan*, int, char*);
+void		cnameclose(Cname*);
+Block*		concatblock(Block*);
+void		confinit(void);
+void		copen(Chan*);
+Block*		copyblock(Block*, int);
+int		cread(Chan*, uchar*, int, vlong);
+Chan*	cunique(Chan*);
+Chan*		createdir(Chan*, Mhead*);
+void		cunmount(Chan*, Chan*);
+void		cupdate(Chan*, uchar*, int, vlong);
+void		cursorenable(void);
+void		cursordisable(void);
+int		cursoron(int);
+void		cursoroff(int);
+void		cwrite(Chan*, uchar*, int, vlong);
+void		debugkey(Rune, char *, void(*)(), int);
+int		decref(Ref*);
+Chan*		devattach(int, char*);
+Block*		devbread(Chan*, long, ulong);
+long		devbwrite(Chan*, Block*, ulong);
+Chan*		devclone(Chan*);
+void		devcreate(Chan*, char*, int, ulong);
+void		devdir(Chan*, Qid, char*, vlong, char*, long, Dir*);
+long		devdirread(Chan*, char*, long, Dirtab*, int, Devgen*);
+Devgen		devgen;
+void		devinit(void);
+int		devno(int, int);
+void	devpower(int);
+Dev*	devbyname(char*);
+Chan*		devopen(Chan*, int, Dirtab*, int, Devgen*);
+void		devpermcheck(char*, ulong, int);
+void		devremove(Chan*);
+void		devreset(void);
+void		devshutdown(void);
+int		devstat(Chan*, uchar*, int, Dirtab*, int, Devgen*);
+Walkqid*	devwalk(Chan*, Chan*, char**, int, Dirtab*, int, Devgen*);
+int		devwstat(Chan*, uchar*, int);
+void		disinit(void*);
+void		disfault(void*, char*);
+int		domount(Chan**, Mhead**);
+void		drawactive(int);
+void		drawcmap(void);
+void		dumpstack(void);
+Fgrp*		dupfgrp(Fgrp*);
+void		egrpcpy(Egrp*, Egrp*);
+int		emptystr(char*);
+int		eqchan(Chan*, Chan*, int);
+int		eqqid(Qid, Qid);
+void		error(char*);
+void		errorf(char*, ...);
+#pragma varargck argpos errorf 1
+void		errstr(char*, int);
+void		excinit(void);
+void		exhausted(char*);
+void		exit(int);
+void		reboot(void);
+void		halt(void);
+int		export(int, char*, int);
+uvlong		fastticks(uvlong*);
+uvlong		fastticks2ns(uvlong);
+void		fdclose(Fgrp*, int);
+Chan*		fdtochan(Fgrp*, int, int, int, int);
+int		findmount(Chan**, Mhead**, int, int, Qid);
+void		free(void*);
+void		freeb(Block*);
+void		freeblist(Block*);
+void		freeskey(Signerkey*);
+void		getcolor(ulong, ulong*, ulong*, ulong*);
+ulong	getmalloctag(void*);
+ulong	getrealloctag(void*);
+void		gotolabel(Label*);
+void		hnputl(void*, ulong);
+void		hnputs(void*, ushort);
+Block*		iallocb(int);
+void		iallocsummary(void);
+void		ilock(Lock*);
+int		incref(Ref*);
+int		iprint(char*, ...);
+#pragma varargck argpos iprint 1
+void		isdir(Chan*);
+int		iseve(void);
+int		islo(void);
+void		iunlock(Lock*);
+void		ixsummary(void);
+void		kbdclock(void);
+int		kbdcr2nl(Queue*, int);
+int		kbdputc(Queue*, int);
+void		kbdrepeat(int);
+void		kproc(char*, void(*)(void*), void*, int);
+int		kfgrpclose(Fgrp*, int);
+void		kprocchild(Proc*, void (*)(void*), void*);
+int		kprint(char*, ...);
+void	(*kproftick)(ulong);
+void		ksetenv(char*, char*, int);
+void		kstrcpy(char*, char*, int);
+void		kstrdup(char**, char*);
+long		latin1(Rune*, int);
+void		lock(Lock*);
+void		logopen(Log*);
+void		logclose(Log*);
+char*		logctl(Log*, int, char**, Logflag*);
+void		logn(Log*, int, void*, int);
+long		logread(Log*, void*, ulong, long);
+void		logb(Log*, int, char*, ...);
+#define	pragma varargck argpos logb 3
+Cmdtab*		lookupcmd(Cmdbuf*, Cmdtab*, int);
+void		machinit(void);
+extern void	machbreakinit(void);
+extern Instr	machinstr(ulong addr);
+extern void	machbreakset(ulong addr);
+extern void	machbreakclear(ulong addr, Instr i);
+extern ulong	machnextaddr(Ureg *ur);
+void*		malloc(ulong);
+void*		mallocz(ulong, int);
+Block*		mem2bl(uchar*, int);
+int			memusehigh(void);
+void		microdelay(int);
+uvlong		mk64fract(uvlong, uvlong);
+void		mkqid(Qid*, vlong, ulong, int);
+void		modinit(void);
+Chan*		mntauth(Chan*, char*);
+long		mntversion(Chan*, char*, int, int);
+void		mountfree(Mount*);
+void		mousetrack(int, int, int, int);
+uvlong		ms2fastticks(ulong);
+ulong		msize(void*);
+void		mul64fract(uvlong*, uvlong, uvlong);
+void		muxclose(Mnt*);
+Chan*		namec(char*, int, int, ulong);
+Chan*		newchan(void);
+Egrp*		newegrp(void);
+Fgrp*		newfgrp(Fgrp*);
+Mount*		newmount(Mhead*, Chan*, int, char*);
+Pgrp*		newpgrp(void);
+Proc*		newproc(void);
+char*		nextelem(char*, char*);
+void		nexterror(void);
+Cname*		newcname(char*);
+int		notify(Ureg*);
+void	notkilled(void);
+int		nrand(int);
+uvlong		ns2fastticks(uvlong);
+int		okaddr(ulong, ulong, int);
+int		openmode(ulong);
+Block*		packblock(Block*);
+Block*		padblock(Block*, int);
+void		panic(char*, ...);
+Cmdbuf*		parsecmd(char*, int);
+void		pexit(char*, int);
+void		pgrpcpy(Pgrp*, Pgrp*);
+#define		poperror()		up->nerrlab--
+int		poolread(char*, int, ulong);
+void		poolsize(Pool *, int, int);
+int		postnote(Proc *, int, char *, int);
+int		pprint(char*, ...);
+int		preemption(int);
+void		printinit(void);
+void		procctl(Proc*);
+void		procdump(void);
+void		procinit(void);
+Proc*		proctab(int);
+void	(*proctrace)(Proc*, int, vlong); 
+int		progfdprint(Chan*, int, int, char*, int);
+int		pullblock(Block**, int);
+Block*		pullupblock(Block*, int);
+Block*		pullupqueue(Queue*, int);
+void		putmhead(Mhead*);
+void		putstrn(char*, int);
+void		qaddlist(Queue*, Block*);
+Block*		qbread(Queue*, int);
+long		qbwrite(Queue*, Block*);
+Queue*	qbypass(void (*)(void*, Block*), void*);
+int		qcanread(Queue*);
+void		qclose(Queue*);
+int		qconsume(Queue*, void*, int);
+Block*		qcopy(Queue*, int, ulong);
+int		qdiscard(Queue*, int);
+void		qflush(Queue*);
+void		qfree(Queue*);
+int		qfull(Queue*);
+Block*		qget(Queue*);
+void		qhangup(Queue*, char*);
+int		qisclosed(Queue*);
+int		qiwrite(Queue*, void*, int);
+int		qlen(Queue*);
+void		qlock(QLock*);
+void		qnoblock(Queue*, int);
+Queue*		qopen(int, int, void (*)(void*), void*);
+int		qpass(Queue*, Block*);
+int		qpassnolim(Queue*, Block*);
+int		qproduce(Queue*, void*, int);
+void		qputback(Queue*, Block*);
+long		qread(Queue*, void*, int);
+Block*		qremove(Queue*);
+void		qreopen(Queue*);
+void		qsetlimit(Queue*, int);
+void		qunlock(QLock*);
+int		qwindow(Queue*);
+int		qwrite(Queue*, void*, int);
+void		randominit(void);
+ulong	randomread(void*, ulong);
+void*	realloc(void*, ulong);
+int		readnum(ulong, char*, ulong, ulong, int);
+int		readnum_vlong(ulong, char*, ulong, vlong, int);
+int		readstr(ulong, char*, ulong, char*);
+void		ready(Proc*);
+void		renameproguser(char*, char*);
+void		renameuser(char*, char*);
+void		resrcwait(char*);
+int		return0(void*);
+void		rlock(RWlock*);
+void		runlock(RWlock*);
+Proc*		runproc(void);
+void		sched(void);
+void		schedinit(void);
+long		seconds(void);
+void		(*serwrite)(char*, int);
+int		setcolor(ulong, ulong, ulong, ulong);
+int		setlabel(Label*);
+void		setmalloctag(void*, ulong);
+int		setpri(int);
+void		setrealloctag(void*, ulong);
+char*		skipslash(char*);
+void		sleep(Rendez*, int(*)(void*), void*);
+void*		smalloc(ulong);
+int		splhi(void);
+int		spllo(void);
+void		splx(int);
+void	splxpc(int);
+void		swiproc(Proc*, int);
+ulong		_tas(ulong*);
+void		timeradd(Timer*);
+void		timerdel(Timer*);
+void		timersinit(void);
+void		timerintr(Ureg*, uvlong);
+void		timerset(uvlong);
+ulong	tk2ms(ulong);
+#define		TK2MS(x) ((x)*(1000/HZ))
+uvlong		tod2fastticks(vlong);
+vlong		todget(vlong*);
+void		todfix(void);
+void		todsetfreq(vlong);
+void		todinit(void);
+void		todset(vlong, vlong, int);
+int		tready(void*);
+Block*		trimblock(Block*, int, int);
+void		tsleep(Rendez*, int (*)(void*), void*, int);
+int		uartgetc(void);
+void		uartputc(int);
+void		uartputs(char*, int);
+long		unionread(Chan*, void*, long);
+void		unlock(Lock*);
+void		userinit(void);
+ulong		userpc(void);
+void		validname(char*, int);
+void		validstat(uchar*, int);
+void		validwstatname(char*);
+int		wakeup(Rendez*);
+int		walk(Chan**, char**, int, int, int*);
+void		werrstr(char*, ...);
+void		wlock(RWlock*);
+void		wunlock(RWlock*);
+void*		xalloc(ulong);
+void*		xallocz(ulong, int);
+void		xfree(void*);
+void		xhole(ulong, ulong);
+void		xinit(void);
+int		xmerge(void*, void*);
+void*		xspanalloc(ulong, int, ulong);
+void		xsummary(void);
+ 
+void		validaddr(void*, ulong, int);
+void*	vmemchr(void*, int, int);
+void		hnputv(void*, vlong);
+void		hnputl(void*, ulong);
+void		hnputs(void*, ushort);
+vlong		nhgetv(void*);
+ulong		nhgetl(void*);
+ushort		nhgets(void*);
--- /dev/null
+++ b/os/port/portmkfile
@@ -1,0 +1,149 @@
+PORTHFILES=\
+	../port/error.h\
+	../port/lib.h\
+	../port/portdat.h\
+	../port/portfns.h\
+
+LIBFILES=${LIBS:%=$ROOT/Inferno/$OBJTYPE/lib/lib%.a}
+
+CLEANEXTRA=
+
+%.$O:	%.s
+	$AS $ASFLAGS $stem.s
+
+%.$O:	%.c
+	$CC $CFLAGS $stem.c
+
+%.$O:		../port/%.c
+		$CC $CFLAGS -I. ../port/$stem.c
+
+%.$O:		../ip/%.c
+		$CC $CFLAGS -I. ../ip/$stem.c
+
+&.$O:		$HFILES $PORTHFILES
+
+$INSTALLDIR/%: %
+	cp $stem $INSTALLDIR/$stem
+
+installall:V:	install-$SHELLTYPE
+all:V:		default-$SHELLTYPE
+
+acid:V: i$CONF.acid
+i$CONF.acid:V: $SHELLTYPE-i$CONF.acid
+
+LIBHDIRS= -I$ROOT/libmp/port -I$ROOT/libsec/port
+
+
+rc-i$CONF.acid nt-i$CONF.acid:V: i$CONF
+	{
+		x=i$CONF; test -e i$CONF.p9 && x=i$CONF.p9
+		for (i in `{srclist -ec -r $ROOT/ $x}) {
+			echo '//FILE: ' $i
+			$CC -I. $CFLAGS $LIBHDIRS '-DKERNDATE='$KERNDATE -a $i
+		}
+		echo 'include ("inferno");'
+	} >i$CONF.acid
+
+sh-i$CONF.acid:V: i$CONF
+	x=i$CONF; test -e i$CONF.p9 && x=i$CONF.p9
+	for i in `srclist -ec -r $ROOT/ $x`
+	do
+		echo '//FILE: ' $i
+		$CC -I. $CFLAGS $LIBHDIRS '-DKERNDATE='$KERNDATE -a $i
+	done >i$CONF.acid
+	echo 'include ("inferno");' >> i$CONF.acid
+
+lib%.a:V:	$SHELLTYPE-lib%.a
+
+rc-lib%.a nt-lib%.a:VQ:
+		echo '@{builtin cd' $ROOT/lib$stem ';' mk 'SHELLTYPE='$SHELLTYPE 'SYSTARG='$SYSTARG 'OBJTYPE='$OBJTYPE install'}'
+		@{builtin cd $ROOT/lib$stem; mk 'SHELLTYPE='$SHELLTYPE 'SYSTARG='$SYSTARG 'OBJTYPE='$OBJTYPE install}
+
+sh-lib%.a:VQ:
+		echo "(cd $ROOT/lib$stem ; mk SHELLTYPE=$SHELLTYPE SYSTARG=$SYSTARG OBJTYPE=$OBJTYPE install)"
+		(cd $ROOT/lib$stem; mk 'SHELLTYPE='$SHELLTYPE 'SYSTARG='$SYSTARG 'OBJTYPE='$OBJTYPE install)
+
+%-rc %-nt:V:
+		for(i in $CONFLIST)
+			mk 'CONF='$i $stem
+
+%-sh:V:
+		for i in $CONFLIST
+		do
+			mk 'CONF='$i $stem
+		done
+
+clean:V:	cleanconf-$SHELLTYPE
+		rm -f *.[$OS] *.root.[sh] errstr.h *.out $CLEANEXTRA
+
+cleanconf-sh:V:
+		for i in $CONFLIST $CLEANCONFLIST
+		do
+			rm -f $i.c i$i i$i.* $i.ver
+		done
+
+cleanconf-rc cleanconf-nt:V:
+		for(i in $CONFLIST $CLEANCONFLIST)
+			rm -f $i.c i$i i$i.* $i.ver
+
+nuke-sh:QV:
+		for i in $LIBDIRS
+		do
+			echo "(cd $ROOT/lib$i ; mk SHELLTYPE=$SHELLTYPE SYSTARG=$SYSTARG OBJTYPE=$OBJTYPE nuke)"
+			(cd $ROOT/lib$i; mk 'SHELLTYPE='$SHELLTYPE 'SYSTARG='$SYSTARG 'OBJTYPE='$OBJTYPE nuke)
+		done
+
+nuke-rc nuke-nt:QV:
+		for (i in $LIBDIRS)
+		{
+			echo '@{cd $ROOT/lib$i ; mk SHELLTYPE=$SHELLTYPE SYSTARG=$SYSTARG OBJTYPE=$OBJTYPE nuke}'
+			@{cd $ROOT/lib$i; mk 'SHELLTYPE='$SHELLTYPE 'SYSTARG='$SYSTARG 'OBJTYPE='$OBJTYPE nuke}
+		}
+
+nuke:V:		clean nuke-$SHELLTYPE
+
+$CONF.c:	../port/mkdevc $CONF
+		$SHELLNAME ../port/mkdevc $CONF > $CONF.c
+
+errstr.h:	../port/error.h
+		sed 's/extern //;s,;.*/\* , = ",;s, \*/,";,' < ../port/error.h > errstr.h
+
+../init/%.dis:	../init/%.b
+		cd ../init; mk 'SHELLTYPE='$SHELLTYPE 'SYSTARG='$SYSTARG 'OBJTYPE='$OBJTYPE $stem.dis
+
+$ROOT/libinterp/runt.h:
+		cd $ROOT/libinterp
+		mk 'SHELLTYPE='$SHELLTYPE 'SYSTARG='$SYSTARG 'OBJTYPE='$OBJTYPE runt.h
+
+RUNT=$ROOT/libinterp/runt.h		# for culling dependencies
+INTERP=$ROOT/include/interp.h
+
+alloc.$O:	$INTERP
+devdbg.$O:	$INTERP
+
+devmnt.$O:	$ROOT/include/fcall.h
+devns16552.$O:	../port/netif.h
+devns16552.$O:	ns16552.h
+devpipe.$O:	$INTERP
+devprof.$O:	$RUNT $INTERP
+devprog.$O:	$RUNT $INTERP
+devroot.$O:	errstr.h
+devsign.$O:	$RUNT $INTERP
+devsrv.$O:	$RUNT	$INTERP
+dis.$O:	$INTERP
+discall.$O:	$INTERP
+exception.$O:	$RUNT	$INTERP
+inferno.$O:	$RUNT	$INTERP
+latin1.$O:	../port/latin1.h
+main.$O:	../port/error.h
+netif.$O:	../port/netif.h
+proc.$O:	errstr.h	$INTERP
+screen.$O:	screen.h
+trap.$O:	$ROOT/Inferno/$OBJTYPE/include/ureg.h
+
+devroot.$O:	$CONF.root.h
+$CONF.$O:	$CONF.root.h
+$CONF.root.s $CONF.root.h: $CONF ../init/$INIT.dis ../port/mkroot $ROOTFILES
+	$SHELLNAME ../port/mkroot $CONF
+
+%.$O:	$ROOT/Inferno/$OBJTYPE/include/u.h ../port/lib.h mem.h dat.h fns.h io.h ../port/error.h ../port/portdat.h ../port/portfns.h
--- /dev/null
+++ b/os/port/print.c
@@ -1,0 +1,31 @@
+#include "u.h"
+#include "../port/lib.h"
+#include "mem.h"
+#include "dat.h"
+#include "fns.h"
+
+static Lock fmtl;
+
+void
+_fmtlock(void)
+{
+	lock(&fmtl);
+}
+
+void
+_fmtunlock(void)
+{
+	unlock(&fmtl);
+}
+
+int
+_efgfmt(Fmt*)
+{
+	return -1;
+}
+
+int
+errfmt(Fmt*)
+{
+	return -1;
+}
--- /dev/null
+++ b/os/port/proc.c
@@ -1,0 +1,788 @@
+#include	"u.h"
+#include	"../port/lib.h"
+#include	"mem.h"
+#include	"dat.h"
+#include	"fns.h"
+#include	"../port/error.h"
+#include	<interp.h>
+
+Ref	pidalloc;
+
+struct
+{
+	Lock;
+	Proc*	arena;
+	Proc*	free;
+}procalloc;
+
+typedef struct
+{
+	Lock;
+	Proc*	head;
+	Proc*	tail;
+}Schedq;
+
+static Schedq	runq[Nrq];
+static ulong	occupied;
+int	nrdy;
+
+char *statename[] =
+{			/* BUG: generate automatically */
+	"Dead",
+	"Moribund",
+	"Ready",
+	"Scheding",
+	"Running",
+	"Queueing",
+	"Wakeme",
+	"Broken",
+	"Stopped",
+	"Rendez",
+};
+
+/*
+ * Always splhi()'ed.
+ */
+void
+schedinit(void)		/* never returns */
+{
+	setlabel(&m->sched);
+	if(up) {
+/*
+		if((e = up->edf) && (e->flags & Admitted))
+			edfrecord(up);
+*/
+		m->proc = nil;
+		switch(up->state) {
+		case Running:
+			ready(up);
+			break;
+		case Moribund:
+			up->state = Dead;
+/*
+			edfstop(up);
+			if(up->edf){
+				free(up->edf);
+				up->edf = nil;
+			}
+*/
+			/*
+			 * Holding locks from pexit:
+			 * 	procalloc
+			 */
+			up->qnext = procalloc.free;
+			procalloc.free = up;
+			unlock(&procalloc);
+			break;
+		}
+		up->mach = nil;
+		up = nil;
+	}
+	sched();
+}
+
+void
+sched(void)
+{
+	if(up) {
+		splhi();
+		procsave(up);
+		if(setlabel(&up->sched)) {
+			/* procrestore(up); */
+			spllo();
+			return;
+		}
+		gotolabel(&m->sched);
+	}
+	up = runproc();
+	up->state = Running;
+	up->mach = MACHP(m->machno);	/* m might be a fixed address; use MACHP */
+	m->proc = up;
+	gotolabel(&up->sched);
+}
+
+void
+ready(Proc *p)
+{
+	int s;
+	Schedq *rq;
+
+	s = splhi();
+/*
+	if(edfready(p)){
+		splx(s);
+		return;
+	}
+*/
+	rq = &runq[p->pri];
+	lock(runq);
+	p->rnext = 0;
+	if(rq->tail)
+		rq->tail->rnext = p;
+	else
+		rq->head = p;
+	rq->tail = p;
+
+	nrdy++;
+	occupied |= 1<<p->pri;
+	p->state = Ready;
+	unlock(runq);
+	splx(s);
+}
+
+int
+anyready(void)
+{
+	/* same priority only */
+	return occupied & (1<<up->pri);
+}
+
+int
+anyhigher(void)
+{
+	return occupied & ((1<<up->pri)-1);
+}
+
+int
+preemption(int tick)
+{
+	if(up != nil && up->state == Running && !up->preempted &&
+	   (anyhigher() || tick && anyready())){
+		up->preempted = 1;
+		sched();
+		splhi();
+		up->preempted = 0;
+		return 1;
+	}
+	return 0;
+}
+		
+Proc*
+runproc(void)
+{
+	Proc *p, *l;
+	Schedq *rq, *erq;
+
+	erq = runq + Nrq - 1;
+loop:
+	splhi();
+	for(rq = runq; rq->head == 0; rq++)
+		if(rq >= erq) {
+			idlehands();
+			spllo();
+			goto loop;
+		}
+
+	if(!canlock(runq))
+		goto loop;
+	/* choose first one we last ran on this processor at this level or hasn't moved recently */
+	l = nil;
+	for(p = rq->head; p != nil; p = p->rnext)
+		if(p->mp == nil || p->mp == MACHP(m->machno) || p->movetime < MACHP(0)->ticks)
+			break;
+	if(p == nil)
+		p = rq->head;
+	/* p->mach==0 only when process state is saved */
+	if(p == 0 || p->mach) {
+		unlock(runq);
+		goto loop;
+	}
+	if(p->rnext == nil)
+		rq->tail = l;
+	if(l)
+		l->rnext = p->rnext;
+	else
+		rq->head = p->rnext;
+	if(rq->head == nil){
+		rq->tail = nil;
+		occupied &= ~(1<<p->pri);
+	}
+	nrdy--;
+	if(p->dbgstop){
+		p->state = Stopped;
+		unlock(runq);
+		goto loop;
+	}
+	if(p->state != Ready)
+		print("runproc %s %lud %s\n", p->text, p->pid, statename[p->state]);
+	unlock(runq);
+	p->state = Scheding;
+	if(p->mp != MACHP(m->machno))
+		p->movetime = MACHP(0)->ticks + HZ/10;
+	p->mp = MACHP(m->machno);
+
+/*
+	if(edflock(p)){
+		edfrun(p, rq == &runq[PriEdf]);	// start deadline timer and do admin
+		edfunlock();
+	}
+*/
+	return p;
+}
+
+int
+setpri(int pri)
+{
+	int p;
+
+	/* called by up so not on run queue */
+	p = up->pri;
+	up->pri = pri;
+	if(up->state == Running && anyhigher())
+		sched();
+	return p;
+}
+
+Proc*
+newproc(void)
+{
+	Proc *p;
+
+	lock(&procalloc);
+	for(;;) {
+		if(p = procalloc.free)
+			break;
+
+		unlock(&procalloc);
+		resrcwait("no procs");
+		lock(&procalloc);
+	}
+	procalloc.free = p->qnext;
+	unlock(&procalloc);
+
+	p->type = Unknown;
+	p->state = Scheding;
+	p->pri = PriNormal;
+	p->psstate = "New";
+	p->mach = 0;
+	p->qnext = 0;
+	p->fpstate = FPINIT;
+	p->kp = 0;
+	p->killed = 0;
+	p->swipend = 0;
+	p->mp = 0;
+	p->movetime = 0;
+	p->delaysched = 0;
+	p->edf = nil;
+	memset(&p->defenv, 0, sizeof(p->defenv));
+	p->env = &p->defenv;
+	p->dbgreg = 0;
+	kstrdup(&p->env->user, "*nouser");
+	p->env->errstr = p->env->errbuf0;
+	p->env->syserrstr = p->env->errbuf1;
+
+	p->pid = incref(&pidalloc);
+	if(p->pid == 0)
+		panic("pidalloc");
+	if(p->kstack == 0)
+		p->kstack = smalloc(KSTACK);
+	addprog(p);
+
+	return p;
+}
+
+void
+procinit(void)
+{
+	Proc *p;
+	int i;
+
+	procalloc.free = xalloc(conf.nproc*sizeof(Proc));
+	procalloc.arena = procalloc.free;
+
+	p = procalloc.free;
+	for(i=0; i<conf.nproc-1; i++,p++)
+		p->qnext = p+1;
+	p->qnext = 0;
+
+	debugkey('p', "processes", procdump, 0);
+}
+
+void
+sleep(Rendez *r, int (*f)(void*), void *arg)
+{
+	int s;
+
+	if(up == nil)
+		panic("sleep() not in process (%lux)", getcallerpc(&r));
+	/*
+	 * spl is to allow lock to be called
+	 * at interrupt time. lock is mutual exclusion
+	 */
+	s = splhi();
+
+	lock(&up->rlock);
+	lock(r);
+
+	/*
+	 * if killed or condition happened, never mind
+	 */
+	if(up->killed || f(arg)){
+		unlock(r);
+	}else{
+
+		/*
+		 * now we are committed to
+		 * change state and call scheduler
+		 */
+		if(r->p != nil) {
+			print("double sleep pc=0x%lux %lud %lud r=0x%lux\n", getcallerpc(&r), r->p->pid, up->pid, r);
+			dumpstack();
+			panic("sleep");
+		}
+		up->state = Wakeme;
+		r->p = up;
+		unlock(r);
+		up->swipend = 0;
+		up->r = r;	/* for swiproc */
+		unlock(&up->rlock);
+
+		sched();
+		splhi();	/* sched does spllo */
+
+		lock(&up->rlock);
+		up->r = nil;
+	}
+
+	if(up->killed || up->swipend) {
+		up->killed = 0;
+		up->swipend = 0;
+		unlock(&up->rlock);
+		splx(s);
+		error(Eintr);
+	}
+	unlock(&up->rlock);
+	splx(s);
+}
+
+int
+tfn(void *arg)
+{
+	return MACHP(0)->ticks >= up->twhen || (*up->tfn)(arg);
+}
+
+void
+tsleep(Rendez *r, int (*fn)(void*), void *arg, int ms)
+{
+	ulong when;
+	Proc *f, **l;
+
+	if(up == nil)
+		panic("tsleep() not in process (0x%lux)", getcallerpc(&r));
+
+	when = MS2TK(ms)+MACHP(0)->ticks;
+	lock(&talarm);
+	/* take out of list if checkalarm didn't */
+	if(up->trend) {
+		l = &talarm.list;
+		for(f = *l; f; f = f->tlink) {
+			if(f == up) {
+				*l = up->tlink;
+				break;
+			}
+			l = &f->tlink;
+		}
+	}
+	/* insert in increasing time order */
+	l = &talarm.list;
+	for(f = *l; f; f = f->tlink) {
+		if(f->twhen >= when)
+			break;
+		l = &f->tlink;
+	}
+	up->trend = r;
+	up->twhen = when;
+	up->tfn = fn;
+	up->tlink = *l;
+	*l = up;
+	unlock(&talarm);
+
+	if(waserror()){
+		up->twhen = 0;
+		nexterror();
+	}
+	sleep(r, tfn, arg);
+	up->twhen = 0;
+	poperror();
+}
+
+int
+wakeup(Rendez *r)
+{
+	Proc *p;
+	int s;
+
+	s = splhi();
+	lock(r);
+	p = r->p;
+	if(p){
+		r->p = nil;
+		if(p->state != Wakeme)
+			panic("wakeup: state");
+		ready(p);
+	}
+	unlock(r);
+	splx(s);
+	return p != nil;
+}
+
+void
+swiproc(Proc *p, int interp)
+{
+	ulong s;
+	Rendez *r;
+
+	if(p == nil)
+		return;
+
+	s = splhi();
+	lock(&p->rlock);
+	if(!interp)
+		p->killed = 1;
+	r = p->r;
+	if(r != nil) {
+		lock(r);
+		if(r->p == p){
+			p->swipend = 1;
+			r->p = nil;
+			ready(p);
+		}
+		unlock(r);
+	}
+	unlock(&p->rlock);
+	splx(s);
+}
+
+void
+notkilled(void)
+{
+	lock(&up->rlock);
+	up->killed = 0;
+	unlock(&up->rlock);
+}
+
+void
+pexit(char*, int)
+{
+	Osenv *o;
+
+	up->alarm = 0;
+
+	o = up->env;
+	if(o != nil){
+		closefgrp(o->fgrp);
+		closepgrp(o->pgrp);
+		closeegrp(o->egrp);
+		closesigs(o->sigs);
+	}
+
+	/* Sched must not loop for this lock */
+	lock(&procalloc);
+
+/*
+	edfstop(up);
+*/
+	up->state = Moribund;
+	sched();
+	panic("pexit");
+}
+
+Proc*
+proctab(int i)
+{
+	return &procalloc.arena[i];
+}
+
+void
+procdump(void)
+{
+	int i;
+	char *s;
+	Proc *p;
+	char tmp[14];
+
+	for(i=0; i<conf.nproc; i++) {
+		p = &procalloc.arena[i];
+		if(p->state == Dead)
+			continue;
+
+		s = p->psstate;
+		if(s == nil)
+			s = "kproc";
+		if(p->state == Wakeme)
+			snprint(tmp, sizeof(tmp), " /%.8lux", p->r);
+		else
+			*tmp = '\0';
+		print("%lux:%3lud:%14s pc %.8lux %s/%s qpc %.8lux pri %d%s\n",
+			p, p->pid, p->text, p->pc, s, statename[p->state], p->qpc, p->pri, tmp);
+	}
+}
+
+void
+kproc(char *name, void (*func)(void *), void *arg, int flags)
+{
+	Proc *p;
+	Pgrp *pg;
+	Fgrp *fg;
+	Egrp *eg;
+
+	p = newproc();
+	p->psstate = 0;
+	p->kp = 1;
+
+	p->fpsave = up->fpsave;
+	p->scallnr = up->scallnr;
+	p->nerrlab = 0;
+
+	kstrdup(&p->env->user, up->env->user);
+	if(flags & KPDUPPG) {
+		pg = up->env->pgrp;
+		incref(pg);
+		p->env->pgrp = pg;
+	}
+	if(flags & KPDUPFDG) {
+		fg = up->env->fgrp;
+		incref(fg);
+		p->env->fgrp = fg;
+	}
+	if(flags & KPDUPENVG) {
+		eg = up->env->egrp;
+		if(eg != nil)
+			incref(eg);
+		p->env->egrp = eg;
+	}
+
+	kprocchild(p, func, arg);
+
+	strcpy(p->text, name);
+
+	ready(p);
+}
+
+void
+errorf(char *fmt, ...)
+{
+	va_list arg;
+	char buf[PRINTSIZE];
+
+	va_start(arg, fmt);
+	vseprint(buf, buf+sizeof(buf), fmt, arg);
+	va_end(arg);
+	error(buf);
+}
+
+void
+error(char *err)
+{
+	if(up == nil)
+		panic("error(%s) not in a process", err);
+	spllo();
+	if(up->nerrlab > NERR)
+		panic("error stack too deep");
+	if(err != up->env->errstr)
+		kstrcpy(up->env->errstr, err, ERRMAX);
+	setlabel(&up->errlab[NERR-1]);
+	nexterror();
+}
+
+#include "errstr.h"
+
+/* Set kernel error string */
+void
+kerrstr(char *err, uint size)
+{
+
+	char tmp[ERRMAX];
+
+	kstrcpy(tmp, up->env->errstr, sizeof(tmp));
+	kstrcpy(up->env->errstr, err, ERRMAX);
+	kstrcpy(err, tmp, size);
+}
+
+/* Get kernel error string */
+void
+kgerrstr(char *err, uint size)
+{
+	char tmp[ERRMAX];
+
+	kstrcpy(tmp, up->env->errstr, sizeof(tmp));
+	kstrcpy(up->env->errstr, err, ERRMAX);
+	kstrcpy(err, tmp, size);
+}
+
+/* Set kernel error string, using formatted print */
+void
+kwerrstr(char *fmt, ...)
+{
+	va_list arg;
+	char buf[ERRMAX];
+
+	va_start(arg, fmt);
+	vseprint(buf, buf+sizeof(buf), fmt, arg);
+	va_end(arg);
+	kstrcpy(up->env->errstr, buf, ERRMAX);
+}
+
+void
+werrstr(char *fmt, ...)
+{
+	va_list arg;
+	char buf[ERRMAX];
+
+	va_start(arg, fmt);
+	vseprint(buf, buf+sizeof(buf), fmt, arg);
+	va_end(arg);
+	kstrcpy(up->env->errstr, buf, ERRMAX);
+}
+
+void
+nexterror(void)
+{
+	gotolabel(&up->errlab[--up->nerrlab]);
+}
+
+/* for dynamic modules - functions not macros */
+	
+void*
+waserr(void)
+{
+	up->nerrlab++;
+	return &up->errlab[up->nerrlab-1];
+}
+
+void
+poperr(void)
+{
+	up->nerrlab--;
+}
+
+char*
+enverror(void)
+{
+	return up->env->errstr;
+}
+
+void
+exhausted(char *resource)
+{
+	char buf[64];
+
+	snprint(buf, sizeof(buf), "no free %s", resource);
+	iprint("%s\n", buf);
+	error(buf);
+}
+
+/*
+ *  change ownership to 'new' of all processes owned by 'old'.  Used when
+ *  eve changes.
+ */
+void
+renameuser(char *old, char *new)
+{
+	Proc *p, *ep;
+	Osenv *o;
+
+	ep = procalloc.arena+conf.nproc;
+	for(p = procalloc.arena; p < ep; p++) {
+		o = &p->defenv;
+		if(o->user != nil && strcmp(o->user, old) == 0)
+			kstrdup(&o->user, new);
+	}
+}
+
+int
+return0(void*)
+{
+	return 0;
+}
+
+void
+setid(char *name, int owner)
+{
+	if(!owner || iseve())
+		kstrdup(&up->env->user, name);
+}
+
+void
+rptwakeup(void *o, void *ar)
+{
+	Rept *r;
+
+	r = ar;
+	if(r == nil)
+		return;
+	lock(&r->l);
+	r->o = o;
+	unlock(&r->l);
+	wakeup(&r->r);
+}
+
+static int
+rptactive(void *a)
+{
+	Rept *r = a;
+	int i;
+	lock(&r->l);
+	i = r->active(r->o);
+	unlock(&r->l);
+	return i;
+}
+
+static void
+rproc(void *a)
+{
+	long now, then;
+	ulong t;
+	int i;
+	void *o;
+	Rept *r;
+
+	r = a;
+	t = r->t;
+
+Wait:
+	sleep(&r->r, rptactive, r);
+	lock(&r->l);
+	o = r->o;
+	unlock(&r->l);
+	then = TK2MS(MACHP(0)->ticks);
+	for(;;){
+		tsleep(&up->sleep, return0, nil, t);
+		now = TK2MS(MACHP(0)->ticks);
+		if(waserror())
+			break;
+		i = r->ck(o, now-then);
+		poperror();
+		if(i == -1)
+			goto Wait;
+		if(i == 0)
+			continue;
+		then = now;
+		acquire();
+		if(waserror()) {
+			release();
+			break;
+		}
+		r->f(o);
+		poperror();
+		release();
+	}
+	pexit("", 0);
+}
+
+void*
+rptproc(char *s, int t, void *o, int (*active)(void*), int (*ck)(void*, int), void (*f)(void*))
+{
+	Rept *r;
+
+	r = mallocz(sizeof(Rept), 1);
+	if(r == nil)
+		return nil;
+	r->t = t;
+	r->active = active;
+	r->ck = ck;
+	r->f = f;
+	r->o = o;
+	kproc(s, rproc, r, KPDUP);
+	return r;
+}
--- /dev/null
+++ b/os/port/qio.c
@@ -1,0 +1,1529 @@
+#include	"u.h"
+#include	"../port/lib.h"
+#include	"mem.h"
+#include	"dat.h"
+#include	"fns.h"
+#include	"../port/error.h"
+
+static ulong padblockcnt;
+static ulong concatblockcnt;
+static ulong pullupblockcnt;
+static ulong copyblockcnt;
+static ulong consumecnt;
+static ulong producecnt;
+static ulong qcopycnt;
+
+static int debugging;
+
+#define QDEBUG	if(0)
+
+/*
+ *  IO queues
+ */
+typedef struct Queue	Queue;
+
+struct Queue
+{
+	Lock;
+
+	Block*	bfirst;		/* buffer */
+	Block*	blast;
+
+	int	len;		/* bytes allocated to queue */
+	int	dlen;		/* data bytes in queue */
+	int	limit;		/* max bytes in queue */
+	int	inilim;		/* initial limit */
+	int	state;
+	int	noblock;	/* true if writes return immediately when q full */
+	int	eof;		/* number of eofs read by user */
+
+	void	(*kick)(void*);	/* restart output */
+	void	(*bypass)(void*, Block*);	/* bypass queue altogether */
+	void*	arg;		/* argument to kick */
+
+	QLock	rlock;		/* mutex for reading processes */
+	Rendez	rr;		/* process waiting to read */
+	QLock	wlock;		/* mutex for writing processes */
+	Rendez	wr;		/* process waiting to write */
+
+	char	err[ERRMAX];
+};
+
+enum
+{
+	Maxatomic	= 64*1024,
+};
+
+uint	qiomaxatomic = Maxatomic;
+
+void
+ixsummary(void)
+{
+	debugging ^= 1;
+	iallocsummary();
+	print("pad %lud, concat %lud, pullup %lud, copy %lud\n",
+		padblockcnt, concatblockcnt, pullupblockcnt, copyblockcnt);
+	print("consume %lud, produce %lud, qcopy %lud\n",
+		consumecnt, producecnt, qcopycnt);
+}
+
+/*
+ *  free a list of blocks
+ */
+void
+freeblist(Block *b)
+{
+	Block *next;
+
+	for(; b != 0; b = next){
+		next = b->next;
+		b->next = 0;
+		freeb(b);
+	}
+}
+
+/*
+ *  pad a block to the front (or the back if size is negative)
+ */
+Block*
+padblock(Block *bp, int size)
+{
+	int n;
+	Block *nbp;
+
+	QDEBUG checkb(bp, "padblock 1");
+	if(size >= 0){
+		if(bp->rp - bp->base >= size){
+			bp->rp -= size;
+			return bp;
+		}
+
+		if(bp->next)
+			panic("padblock 0x%luX", getcallerpc(&bp));
+		n = BLEN(bp);
+		padblockcnt++;
+		nbp = allocb(size+n);
+		nbp->rp += size;
+		nbp->wp = nbp->rp;
+		memmove(nbp->wp, bp->rp, n);
+		nbp->wp += n;
+		freeb(bp);
+		nbp->rp -= size;
+	} else {
+		size = -size;
+
+		if(bp->next)
+			panic("padblock 0x%luX", getcallerpc(&bp));
+
+		if(bp->lim - bp->wp >= size)
+			return bp;
+
+		n = BLEN(bp);
+		padblockcnt++;
+		nbp = allocb(size+n);
+		memmove(nbp->wp, bp->rp, n);
+		nbp->wp += n;
+		freeb(bp);
+	}
+	QDEBUG checkb(nbp, "padblock 1");
+	return nbp;
+}
+
+/*
+ *  return count of bytes in a string of blocks
+ */
+int
+blocklen(Block *bp)
+{
+	int len;
+
+	len = 0;
+	while(bp) {
+		len += BLEN(bp);
+		bp = bp->next;
+	}
+	return len;
+}
+
+/*
+ * return count of space in blocks
+ */
+int
+blockalloclen(Block *bp)
+{
+	int len;
+
+	len = 0;
+	while(bp) {
+		len += BALLOC(bp);
+		bp = bp->next;
+	}
+	return len;
+}
+
+/*
+ *  copy the  string of blocks into
+ *  a single block and free the string
+ */
+Block*
+concatblock(Block *bp)
+{
+	int len;
+	Block *nb, *f;
+
+	if(bp->next == 0)
+		return bp;
+
+	nb = allocb(blocklen(bp));
+	for(f = bp; f; f = f->next) {
+		len = BLEN(f);
+		memmove(nb->wp, f->rp, len);
+		nb->wp += len;
+	}
+	concatblockcnt += BLEN(nb);
+	freeblist(bp);
+	QDEBUG checkb(nb, "concatblock 1");
+	return nb;
+}
+
+/*
+ *  make sure the first block has at least n bytes
+ */
+Block*
+pullupblock(Block *bp, int n)
+{
+	int i;
+	Block *nbp;
+
+	/*
+	 *  this should almost always be true, it's
+	 *  just to avoid every caller checking.
+	 */
+	if(BLEN(bp) >= n)
+		return bp;
+
+	/*
+	 *  if not enough room in the first block,
+	 *  add another to the front of the list.
+	 */
+	if(bp->lim - bp->rp < n){
+		nbp = allocb(n);
+		nbp->next = bp;
+		bp = nbp;
+	}
+
+	/*
+	 *  copy bytes from the trailing blocks into the first
+	 */
+	n -= BLEN(bp);
+	while(nbp = bp->next){
+		i = BLEN(nbp);
+		if(i > n) {
+			memmove(bp->wp, nbp->rp, n);
+			pullupblockcnt++;
+			bp->wp += n;
+			nbp->rp += n;
+			QDEBUG checkb(bp, "pullupblock 1");
+			return bp;
+		}
+		else {
+			memmove(bp->wp, nbp->rp, i);
+			pullupblockcnt++;
+			bp->wp += i;
+			bp->next = nbp->next;
+			nbp->next = 0;
+			freeb(nbp);
+			n -= i;
+			if(n == 0){
+				QDEBUG checkb(bp, "pullupblock 2");
+				return bp;
+			}
+		}
+	}
+	freeb(bp);
+	return 0;
+}
+
+/*
+ *  make sure the first block has at least n bytes
+ */
+Block*
+pullupqueue(Queue *q, int n)
+{
+	Block *b;
+
+	if(BLEN(q->bfirst) >= n)
+		return q->bfirst;
+	q->bfirst = pullupblock(q->bfirst, n);
+	for(b = q->bfirst; b != nil && b->next != nil; b = b->next)
+		;
+	q->blast = b;
+	return q->bfirst;
+}
+
+/*
+ *  trim to len bytes starting at offset
+ */
+Block *
+trimblock(Block *bp, int offset, int len)
+{
+	ulong l;
+	Block *nb, *startb;
+
+	QDEBUG checkb(bp, "trimblock 1");
+	if(blocklen(bp) < offset+len) {
+		freeblist(bp);
+		return nil;
+	}
+
+	while((l = BLEN(bp)) < offset) {
+		offset -= l;
+		nb = bp->next;
+		bp->next = nil;
+		freeb(bp);
+		bp = nb;
+	}
+
+	startb = bp;
+	bp->rp += offset;
+
+	while((l = BLEN(bp)) < len) {
+		len -= l;
+		bp = bp->next;
+	}
+
+	bp->wp -= (BLEN(bp) - len);
+
+	if(bp->next) {
+		freeblist(bp->next);
+		bp->next = nil;
+	}
+
+	return startb;
+}
+
+/*
+ *  copy 'count' bytes into a new block
+ */
+Block*
+copyblock(Block *bp, int count)
+{
+	int l;
+	Block *nbp;
+
+	QDEBUG checkb(bp, "copyblock 0");
+	nbp = allocb(count);
+	for(; count > 0 && bp != 0; bp = bp->next){
+		l = BLEN(bp);
+		if(l > count)
+			l = count;
+		memmove(nbp->wp, bp->rp, l);
+		nbp->wp += l;
+		count -= l;
+	}
+	if(count > 0){
+		memset(nbp->wp, 0, count);
+		nbp->wp += count;
+	}
+	copyblockcnt++;
+	QDEBUG checkb(nbp, "copyblock 1");
+
+	return nbp;
+}
+
+Block*
+adjustblock(Block* bp, int len)
+{
+	int n;
+	Block *nbp;
+
+	if(len < 0){
+		freeb(bp);
+		return nil;
+	}
+
+	if(bp->rp+len > bp->lim){
+		nbp = copyblock(bp, len);
+		freeblist(bp);
+		QDEBUG checkb(nbp, "adjustblock 1");
+
+		return nbp;
+	}
+
+	n = BLEN(bp);
+	if(len > n)
+		memset(bp->wp, 0, len-n);
+	bp->wp = bp->rp+len;
+	QDEBUG checkb(bp, "adjustblock 2");
+
+	return bp;
+}
+
+
+/*
+ *  throw away up to count bytes from a
+ *  list of blocks.  Return count of bytes
+ *  thrown away.
+ */
+int
+pullblock(Block **bph, int count)
+{
+	Block *bp;
+	int n, bytes;
+
+	bytes = 0;
+	if(bph == nil)
+		return 0;
+
+	while(*bph != nil && count != 0) {
+		bp = *bph;
+		n = BLEN(bp);
+		if(count < n)
+			n = count;
+		bytes += n;
+		count -= n;
+		bp->rp += n;
+		QDEBUG checkb(bp, "pullblock ");
+		if(BLEN(bp) == 0) {
+			*bph = bp->next;
+			bp->next = nil;
+			freeb(bp);
+		}
+	}
+	return bytes;
+}
+
+/*
+ *  get next block from a queue, return null if nothing there
+ */
+Block*
+qget(Queue *q)
+{
+	int dowakeup;
+	Block *b;
+
+	/* sync with qwrite */
+	ilock(q);
+
+	b = q->bfirst;
+	if(b == nil){
+		q->state |= Qstarve;
+		iunlock(q);
+		return nil;
+	}
+	q->bfirst = b->next;
+	b->next = 0;
+	q->len -= BALLOC(b);
+	q->dlen -= BLEN(b);
+	QDEBUG checkb(b, "qget");
+
+	/* if writer flow controlled, restart */
+	if((q->state & Qflow) && q->len < q->limit/2){
+		q->state &= ~Qflow;
+		dowakeup = 1;
+	} else
+		dowakeup = 0;
+
+	iunlock(q);
+
+	if(dowakeup)
+		wakeup(&q->wr);
+
+	return b;
+}
+
+/*
+ *  throw away the next 'len' bytes in the queue
+ * returning the number actually discarded
+ */
+int
+qdiscard(Queue *q, int len)
+{
+	Block *b;
+	int dowakeup, n, sofar;
+
+	ilock(q);
+	for(sofar = 0; sofar < len; sofar += n){
+		b = q->bfirst;
+		if(b == nil)
+			break;
+		QDEBUG checkb(b, "qdiscard");
+		n = BLEN(b);
+		if(n <= len - sofar){
+			q->bfirst = b->next;
+			b->next = 0;
+			q->len -= BALLOC(b);
+			q->dlen -= BLEN(b);
+			freeb(b);
+		} else {
+			n = len - sofar;
+			b->rp += n;
+			q->dlen -= n;
+		}
+	}
+
+	/*
+	 *  if writer flow controlled, restart
+	 *
+	 *  This used to be
+	 *	q->len < q->limit/2
+	 *  but it slows down tcp too much for certain write sizes.
+	 *  I really don't understand it completely.  It may be
+	 *  due to the queue draining so fast that the transmission
+	 *  stalls waiting for the app to produce more data.  - presotto
+	 */
+	if((q->state & Qflow) && q->len < q->limit){
+		q->state &= ~Qflow;
+		dowakeup = 1;
+	} else
+		dowakeup = 0;
+
+	iunlock(q);
+
+	if(dowakeup)
+		wakeup(&q->wr);
+
+	return sofar;
+}
+
+/*
+ *  Interrupt level copy out of a queue, return # bytes copied.
+ */
+int
+qconsume(Queue *q, void *vp, int len)
+{
+	Block *b;
+	int n, dowakeup;
+	uchar *p = vp;
+	Block *tofree = nil;
+
+	/* sync with qwrite */
+	ilock(q);
+
+	for(;;) {
+		b = q->bfirst;
+		if(b == 0){
+			q->state |= Qstarve;
+			iunlock(q);
+			return -1;
+		}
+		QDEBUG checkb(b, "qconsume 1");
+
+		n = BLEN(b);
+		if(n > 0)
+			break;
+		q->bfirst = b->next;
+		q->len -= BALLOC(b);
+
+		/* remember to free this */
+		b->next = tofree;
+		tofree = b;
+	};
+
+	if(n < len)
+		len = n;
+	memmove(p, b->rp, len);
+	consumecnt += n;
+	b->rp += len;
+	q->dlen -= len;
+
+	/* discard the block if we're done with it */
+	if((q->state & Qmsg) || len == n){
+		q->bfirst = b->next;
+		b->next = 0;
+		q->len -= BALLOC(b);
+		q->dlen -= BLEN(b);
+
+		/* remember to free this */
+		b->next = tofree;
+		tofree = b;
+	}
+
+	/* if writer flow controlled, restart */
+	if((q->state & Qflow) && q->len < q->limit/2){
+		q->state &= ~Qflow;
+		dowakeup = 1;
+	} else
+		dowakeup = 0;
+
+	iunlock(q);
+
+	if(dowakeup)
+		wakeup(&q->wr);
+
+	if(tofree != nil)
+		freeblist(tofree);
+
+	return len;
+}
+
+int
+qpass(Queue *q, Block *b)
+{
+	int dlen, len, dowakeup;
+
+	/* sync with qread */
+	dowakeup = 0;
+	ilock(q);
+	if(q->len >= q->limit){
+		freeblist(b);
+		iunlock(q);
+		return -1;
+	}
+	if(q->state & Qclosed){
+		len = blocklen(b);
+		freeblist(b);
+		iunlock(q);
+		return len;
+	}
+
+	/* add buffer to queue */
+	if(q->bfirst)
+		q->blast->next = b;
+	else
+		q->bfirst = b;
+	len = BALLOC(b);
+	dlen = BLEN(b);
+	QDEBUG checkb(b, "qpass");
+	while(b->next){
+		b = b->next;
+		QDEBUG checkb(b, "qpass");
+		len += BALLOC(b);
+		dlen += BLEN(b);
+	}
+	q->blast = b;
+	q->len += len;
+	q->dlen += dlen;
+
+	if(q->len >= q->limit/2)
+		q->state |= Qflow;
+
+	if(q->state & Qstarve){
+		q->state &= ~Qstarve;
+		dowakeup = 1;
+	}
+	iunlock(q);
+
+	if(dowakeup)
+		wakeup(&q->rr);
+
+	return len;
+}
+
+int
+qpassnolim(Queue *q, Block *b)
+{
+	int dlen, len, dowakeup;
+
+	/* sync with qread */
+	dowakeup = 0;
+	ilock(q);
+
+	if(q->state & Qclosed){
+		freeblist(b);
+		iunlock(q);
+		return BALLOC(b);
+	}
+
+	/* add buffer to queue */
+	if(q->bfirst)
+		q->blast->next = b;
+	else
+		q->bfirst = b;
+	len = BALLOC(b);
+	dlen = BLEN(b);
+	QDEBUG checkb(b, "qpass");
+	while(b->next){
+		b = b->next;
+		QDEBUG checkb(b, "qpass");
+		len += BALLOC(b);
+		dlen += BLEN(b);
+	}
+	q->blast = b;
+	q->len += len;
+	q->dlen += dlen;
+
+	if(q->len >= q->limit/2)
+		q->state |= Qflow;
+
+	if(q->state & Qstarve){
+		q->state &= ~Qstarve;
+		dowakeup = 1;
+	}
+	iunlock(q);
+
+	if(dowakeup)
+		wakeup(&q->rr);
+
+	return len;
+}
+
+/*
+ *  if the allocated space is way out of line with the used
+ *  space, reallocate to a smaller block
+ */
+Block*
+packblock(Block *bp)
+{
+	Block **l, *nbp;
+	int n;
+
+	for(l = &bp; *l; l = &(*l)->next){
+		nbp = *l;
+		n = BLEN(nbp);
+		if((n<<2) < BALLOC(nbp)){
+			*l = allocb(n);
+			memmove((*l)->wp, nbp->rp, n);
+			(*l)->wp += n;
+			(*l)->next = nbp->next;
+			freeb(nbp);
+		}
+	}
+
+	return bp;
+}
+
+int
+qproduce(Queue *q, void *vp, int len)
+{
+	Block *b;
+	int dowakeup;
+	uchar *p = vp;
+
+	/* sync with qread */
+	dowakeup = 0;
+	ilock(q);
+
+	/* no waiting receivers, room in buffer? */
+	if(q->len >= q->limit){
+		q->state |= Qflow;
+		iunlock(q);
+		return -1;
+	}
+
+	/* save in buffer */
+	/* use Qcoalesce here to save storage */
+	b = q->blast;
+	if((q->state & Qcoalesce)==0 || q->bfirst==nil || b->lim-b->wp < len){
+		/* need a new block */
+		b = iallocb(len);
+		if(b == 0){
+			iunlock(q);
+			return 0;
+		}
+		if(q->bfirst)
+			q->blast->next = b;
+		else
+			q->bfirst = b;
+		q->blast = b;
+		/* b->next = 0; done by iallocb() */
+		q->len += BALLOC(b);
+	}
+	memmove(b->wp, p, len);
+	producecnt += len;
+	b->wp += len;
+	q->dlen += len;
+	QDEBUG checkb(b, "qproduce");
+
+	if(q->state & Qstarve){
+		q->state &= ~Qstarve;
+		dowakeup = 1;
+	}
+
+	if(q->len >= q->limit)
+		q->state |= Qflow;
+	iunlock(q);
+
+	if(dowakeup)
+		wakeup(&q->rr);
+
+	return len;
+}
+
+/*
+ *  copy from offset in the queue
+ */
+Block*
+qcopy(Queue *q, int len, ulong offset)
+{
+	int sofar;
+	int n;
+	Block *b, *nb;
+	uchar *p;
+
+	nb = allocb(len);
+
+	ilock(q);
+
+	/* go to offset */
+	b = q->bfirst;
+	for(sofar = 0; ; sofar += n){
+		if(b == nil){
+			iunlock(q);
+			return nb;
+		}
+		n = BLEN(b);
+		if(sofar + n > offset){
+			p = b->rp + offset - sofar;
+			n -= offset - sofar;
+			break;
+		}
+		QDEBUG checkb(b, "qcopy");
+		b = b->next;
+	}
+
+	/* copy bytes from there */
+	for(sofar = 0; sofar < len;){
+		if(n > len - sofar)
+			n = len - sofar;
+		memmove(nb->wp, p, n);
+		qcopycnt += n;
+		sofar += n;
+		nb->wp += n;
+		b = b->next;
+		if(b == nil)
+			break;
+		n = BLEN(b);
+		p = b->rp;
+	}
+	iunlock(q);
+
+	return nb;
+}
+
+/*
+ *  called by non-interrupt code
+ */
+Queue*
+qopen(int limit, int msg, void (*kick)(void*), void *arg)
+{
+	Queue *q;
+
+	q = malloc(sizeof(Queue));
+	if(q == 0)
+		return 0;
+
+	q->limit = q->inilim = limit;
+	q->kick = kick;
+	q->arg = arg;
+	q->state = msg;
+	q->state |= Qstarve;
+	q->eof = 0;
+	q->noblock = 0;
+
+	return q;
+}
+
+/* open a queue to be bypassed */
+Queue*
+qbypass(void (*bypass)(void*, Block*), void *arg)
+{
+	Queue *q;
+
+	q = malloc(sizeof(Queue));
+	if(q == 0)
+		return 0;
+
+	q->limit = 0;
+	q->arg = arg;
+	q->bypass = bypass;
+	q->state = 0;
+
+	return q;
+}
+
+static int
+notempty(void *a)
+{
+	Queue *q = a;
+
+	return (q->state & Qclosed) || q->bfirst != 0;
+}
+
+/*
+ *  wait for the queue to be non-empty or closed.
+ *  called with q ilocked.
+ */
+static int
+qwait(Queue *q)
+{
+	/* wait for data */
+	for(;;){
+		if(q->bfirst != nil)
+			break;
+
+		if(q->state & Qclosed){
+			if(++q->eof > 3)
+				return -1;
+			if(*q->err && strcmp(q->err, Ehungup) != 0)
+				return -1;
+			return 0;
+		}
+
+		q->state |= Qstarve;	/* flag requesting producer to wake me */
+		iunlock(q);
+		sleep(&q->rr, notempty, q);
+		ilock(q);
+	}
+	return 1;
+}
+
+/*
+ * add a block list to a queue
+ */
+void
+qaddlist(Queue *q, Block *b)
+{
+	/* queue the block */
+	if(q->bfirst)
+		q->blast->next = b;
+	else
+		q->bfirst = b;
+	q->len += blockalloclen(b);
+	q->dlen += blocklen(b);
+	while(b->next)
+		b = b->next;
+	q->blast = b;
+}
+
+/*
+ *  called with q ilocked
+ */
+Block*
+qremove(Queue *q)
+{
+	Block *b;
+
+	b = q->bfirst;
+	if(b == nil)
+		return nil;
+	q->bfirst = b->next;
+	b->next = nil;
+	q->dlen -= BLEN(b);
+	q->len -= BALLOC(b);
+	QDEBUG checkb(b, "qremove");
+	return b;
+}
+
+/*
+ *  copy the contents of a string of blocks into
+ *  memory.  emptied blocks are freed.  return
+ *  pointer to first unconsumed block.
+ */
+Block*
+bl2mem(uchar *p, Block *b, int n)
+{
+	int i;
+	Block *next;
+
+	for(; b != nil; b = next){
+		i = BLEN(b);
+		if(i > n){
+			memmove(p, b->rp, n);
+			b->rp += n;
+			return b;
+		}
+		memmove(p, b->rp, i);
+		n -= i;
+		p += i;
+		b->rp += i;
+		next = b->next;
+		freeb(b);
+	}
+	return nil;
+}
+
+/*
+ *  copy the contents of memory into a string of blocks.
+ *  return nil on error.
+ */
+Block*
+mem2bl(uchar *p, int len)
+{
+	int n;
+	Block *b, *first, **l;
+
+	first = nil;
+	l = &first;
+	if(waserror()){
+		freeblist(first);
+		nexterror();
+	}
+	do {
+		n = len;
+		if(n > Maxatomic)
+			n = Maxatomic;
+
+		*l = b = allocb(n);
+		setmalloctag(b, getcallerpc(&p));
+		memmove(b->wp, p, n);
+		b->wp += n;
+		p += n;
+		len -= n;
+		l = &b->next;
+	} while(len > 0);
+	poperror();
+
+	return first;
+}
+
+/*
+ *  put a block back to the front of the queue
+ *  called with q ilocked
+ */
+void
+qputback(Queue *q, Block *b)
+{
+	b->next = q->bfirst;
+	if(q->bfirst == nil)
+		q->blast = b;
+	q->bfirst = b;
+	q->len += BALLOC(b);
+	q->dlen += BLEN(b);
+}
+
+/*
+ *  flow control, get producer going again
+ *  called with q ilocked
+ */
+static void
+qwakeup_iunlock(Queue *q)
+{
+	int dowakeup = 0;
+
+	/* if writer flow controlled, restart */
+	if((q->state & Qflow) && q->len < q->limit/2){
+		q->state &= ~Qflow;
+		dowakeup = 1;
+	}
+
+	iunlock(q);
+
+	/* wakeup flow controlled writers */
+	if(dowakeup){
+		if(q->kick)
+			q->kick(q->arg);
+		wakeup(&q->wr);
+	}
+}
+
+/*
+ *  get next block from a queue (up to a limit)
+ */
+Block*
+qbread(Queue *q, int len)
+{
+	Block *b, *nb;
+	int n;
+
+	qlock(&q->rlock);
+	if(waserror()){
+		qunlock(&q->rlock);
+		nexterror();
+	}
+
+	ilock(q);
+	switch(qwait(q)){
+	case 0:
+		/* queue closed */
+		iunlock(q);
+		qunlock(&q->rlock);
+		poperror();
+		return nil;
+	case -1:
+		/* multiple reads on a closed queue */
+		iunlock(q);
+		error(q->err);
+	}
+
+	/* if we get here, there's at least one block in the queue */
+	b = qremove(q);
+	n = BLEN(b);
+
+	/* split block if it's too big and this is not a message queue */
+	nb = b;
+	if(n > len){
+		if((q->state&Qmsg) == 0){
+			n -= len;
+			b = allocb(n);
+			memmove(b->wp, nb->rp+len, n);
+			b->wp += n;
+			qputback(q, b);
+		}
+		nb->wp = nb->rp + len;
+	}
+
+	/* restart producer */
+	qwakeup_iunlock(q);
+
+	poperror();
+	qunlock(&q->rlock);
+	return nb;
+}
+
+/*
+ *  read a queue.  if no data is queued, post a Block
+ *  and wait on its Rendez.
+ */
+long
+qread(Queue *q, void *vp, int len)
+{
+	Block *b, *first, **l;
+	int m, n;
+
+	qlock(&q->rlock);
+	if(waserror()){
+		qunlock(&q->rlock);
+		nexterror();
+	}
+
+	ilock(q);
+again:
+	switch(qwait(q)){
+	case 0:
+		/* queue closed */
+		iunlock(q);
+		qunlock(&q->rlock);
+		poperror();
+		return 0;
+	case -1:
+		/* multiple reads on a closed queue */
+		iunlock(q);
+		error(q->err);
+	}
+
+	/* if we get here, there's at least one block in the queue */
+	if(q->state & Qcoalesce){
+		/* when coalescing, 0 length blocks just go away */
+		b = q->bfirst;
+		if(BLEN(b) <= 0){
+			freeb(qremove(q));
+			goto again;
+		}
+
+		/*  grab the first block plus as many
+		 *  following blocks as will completely
+		 *  fit in the read.
+		 */
+		n = 0;
+		l = &first;
+		m = BLEN(b);
+		for(;;) {
+			*l = qremove(q);
+			l = &b->next;
+			n += m;
+
+			b = q->bfirst;
+			if(b == nil)
+				break;
+			m = BLEN(b);
+			if(n+m > len)
+				break;
+		}
+	} else {
+		first = qremove(q);
+		n = BLEN(first);
+	}
+
+	/* copy to user space outside of the ilock */
+	iunlock(q);
+	b = bl2mem(vp, first, len);
+	ilock(q);
+
+	/* take care of any left over partial block */
+	if(b != nil){
+		n -= BLEN(b);
+		if(q->state & Qmsg)
+			freeb(b);
+		else
+			qputback(q, b);
+	}
+
+	/* restart producer */
+	qwakeup_iunlock(q);
+
+	poperror();
+	qunlock(&q->rlock);
+	return n;
+}
+
+static int
+qnotfull(void *a)
+{
+	Queue *q = a;
+
+	return q->len < q->limit || (q->state & Qclosed);
+}
+
+ulong noblockcnt;
+
+/*
+ *  add a block to a queue obeying flow control
+ */
+long
+qbwrite(Queue *q, Block *b)
+{
+	int n, dowakeup;
+
+	n = BLEN(b);
+
+	if(q->bypass){
+		(*q->bypass)(q->arg, b);
+		return n;
+	}
+
+	dowakeup = 0;
+	qlock(&q->wlock);
+	if(waserror()){
+		if(b != nil)
+			freeb(b);
+		qunlock(&q->wlock);
+		nexterror();
+	}
+
+	ilock(q);
+
+	/* give up if the queue is closed */
+	if(q->state & Qclosed){
+		iunlock(q);
+		error(q->err);
+	}
+
+	/* if nonblocking, don't queue over the limit */
+	if(q->len >= q->limit){
+		if(q->noblock){
+			iunlock(q);
+			freeb(b);
+			noblockcnt += n;
+			qunlock(&q->wlock);
+			poperror();
+			return n;
+		}
+	}
+
+	/* queue the block */
+	if(q->bfirst)
+		q->blast->next = b;
+	else
+		q->bfirst = b;
+	q->blast = b;
+	b->next = 0;
+	q->len += BALLOC(b);
+	q->dlen += n;
+	QDEBUG checkb(b, "qbwrite");
+	b = nil;
+
+	/* make sure other end gets awakened */
+	if(q->state & Qstarve){
+		q->state &= ~Qstarve;
+		dowakeup = 1;
+	}
+	iunlock(q);
+
+	/*  get output going again */
+	if(q->kick && (dowakeup || (q->state&Qkick)))
+		q->kick(q->arg);
+
+	/* wakeup anyone consuming at the other end */
+	if(dowakeup)
+		wakeup(&q->rr);
+
+	/*
+	 *  flow control, wait for queue to get below the limit
+	 *  before allowing the process to continue and queue
+	 *  more.  We do this here so that postnote can only
+	 *  interrupt us after the data has been queued.  This
+	 *  means that things like 9p flushes and ssl messages
+	 *  will not be disrupted by software interrupts.
+	 *
+	 *  Note - this is moderately dangerous since a process
+	 *  that keeps getting interrupted and rewriting will
+	 *  queue infinite crud.
+	 */
+	for(;;){
+		if(q->noblock || qnotfull(q))
+			break;
+
+		ilock(q);
+		q->state |= Qflow;
+		iunlock(q);
+		sleep(&q->wr, qnotfull, q);
+	}
+	USED(b);
+
+	qunlock(&q->wlock);
+	poperror();
+	return n;
+}
+
+/*
+ *  write to a queue.  only Maxatomic bytes at a time is atomic.
+ */
+int
+qwrite(Queue *q, void *vp, int len)
+{
+	int n, sofar;
+	Block *b;
+	uchar *p = vp;
+
+	QDEBUG if(!islo())
+		print("qwrite hi %lux\n", getcallerpc(&q));
+
+	sofar = 0;
+	do {
+		n = len-sofar;
+		if(n > Maxatomic)
+			n = Maxatomic;
+
+		b = allocb(n);
+		setmalloctag(b, getcallerpc(&q));
+		if(waserror()){
+			freeb(b);
+			nexterror();
+		}
+		memmove(b->wp, p+sofar, n);
+		poperror();
+		b->wp += n;
+
+		qbwrite(q, b);
+
+		sofar += n;
+	} while(sofar < len && (q->state & Qmsg) == 0);
+
+	return len;
+}
+
+/*
+ *  used by print() to write to a queue.  Since we may be splhi or not in
+ *  a process, don't qlock.
+ */
+int
+qiwrite(Queue *q, void *vp, int len)
+{
+	int n, sofar, dowakeup;
+	Block *b;
+	uchar *p = vp;
+
+	dowakeup = 0;
+
+	sofar = 0;
+	do {
+		n = len-sofar;
+		if(n > Maxatomic)
+			n = Maxatomic;
+
+		b = iallocb(n);
+		if(b == nil)
+			break;
+		memmove(b->wp, p+sofar, n);
+		b->wp += n;
+
+		ilock(q);
+
+		QDEBUG checkb(b, "qiwrite");
+		if(q->bfirst)
+			q->blast->next = b;
+		else
+			q->bfirst = b;
+		q->blast = b;
+		q->len += BALLOC(b);
+		q->dlen += n;
+
+		if(q->state & Qstarve){
+			q->state &= ~Qstarve;
+			dowakeup = 1;
+		}
+
+		iunlock(q);
+
+		if(dowakeup){
+			if(q->kick)
+				q->kick(q->arg);
+			wakeup(&q->rr);
+		}
+
+		sofar += n;
+	} while(sofar < len && (q->state & Qmsg) == 0);
+
+	return sofar;
+}
+
+/*
+ *  be extremely careful when calling this,
+ *  as there is no reference accounting
+ */
+void
+qfree(Queue *q)
+{
+	qclose(q);
+	free(q);
+}
+
+/*
+ *  Mark a queue as closed.  No further IO is permitted.
+ *  All blocks are released.
+ */
+void
+qclose(Queue *q)
+{
+	Block *bfirst;
+
+	if(q == nil)
+		return;
+
+	/* mark it */
+	ilock(q);
+	q->state |= Qclosed;
+	q->state &= ~(Qflow|Qstarve);
+	strcpy(q->err, Ehungup);
+	bfirst = q->bfirst;
+	q->bfirst = 0;
+	q->len = 0;
+	q->dlen = 0;
+	q->noblock = 0;
+	iunlock(q);
+
+	/* free queued blocks */
+	freeblist(bfirst);
+
+	/* wake up readers/writers */
+	wakeup(&q->rr);
+	wakeup(&q->wr);
+}
+
+/*
+ *  Mark a queue as closed.  Wakeup any readers.  Don't remove queued
+ *  blocks.
+ */
+void
+qhangup(Queue *q, char *msg)
+{
+	/* mark it */
+	ilock(q);
+	q->state |= Qclosed;
+	if(msg == 0 || *msg == 0)
+		strcpy(q->err, Ehungup);
+	else
+		strncpy(q->err, msg, ERRMAX-1);
+	iunlock(q);
+
+	/* wake up readers/writers */
+	wakeup(&q->rr);
+	wakeup(&q->wr);
+}
+
+/*
+ *  return non-zero if the q is hungup
+ */
+int
+qisclosed(Queue *q)
+{
+	return q->state & Qclosed;
+}
+
+/*
+ *  mark a queue as no longer hung up
+ */
+void
+qreopen(Queue *q)
+{
+	ilock(q);
+	q->state &= ~Qclosed;
+	q->state |= Qstarve;
+	q->eof = 0;
+	q->limit = q->inilim;
+	iunlock(q);
+}
+
+/*
+ *  return bytes queued
+ */
+int
+qlen(Queue *q)
+{
+	return q->dlen;
+}
+
+/*
+ * return space remaining before flow control
+ */
+int
+qwindow(Queue *q)
+{
+	int l;
+
+	l = q->limit - q->len;
+	if(l < 0)
+		l = 0;
+	return l;
+}
+
+/*
+ *  return true if we can read without blocking
+ */
+int
+qcanread(Queue *q)
+{
+	return q->bfirst!=0;
+}
+
+/*
+ *  change queue limit
+ */
+void
+qsetlimit(Queue *q, int limit)
+{
+	q->limit = limit;
+}
+
+/*
+ *  set blocking/nonblocking
+ */
+void
+qnoblock(Queue *q, int onoff)
+{
+	q->noblock = onoff;
+}
+
+/*
+ *  flush the output queue
+ */
+void
+qflush(Queue *q)
+{
+	Block *bfirst;
+
+	/* mark it */
+	ilock(q);
+	bfirst = q->bfirst;
+	q->bfirst = 0;
+	q->len = 0;
+	q->dlen = 0;
+	iunlock(q);
+
+	/* free queued blocks */
+	freeblist(bfirst);
+
+	/* wake up readers/writers */
+	wakeup(&q->wr);
+}
+
+int
+qfull(Queue *q)
+{
+	return q->state & Qflow;
+}
+
+int
+qstate(Queue *q)
+{
+	return q->state;
+}
+
+void
+qdump(Queue *q)
+{
+	if(q)
+	kprint("q=%p bfirst=%p blast=%p len=%d dlen=%d limit=%d state=#%x\n",
+		q, q->bfirst, q->blast, q->len, q->dlen, q->limit, q->state);
+}
--- /dev/null
+++ b/os/port/qlock.c
@@ -1,0 +1,111 @@
+#include "u.h"
+#include "../port/lib.h"
+#include "mem.h"
+#include "dat.h"
+#include "fns.h"
+
+void
+qlock(QLock *q)
+{
+	Proc *p, *mp;
+
+	lock(&q->use);
+	if(!q->locked) {
+		q->locked = 1;
+		unlock(&q->use);
+		return;
+	}
+	p = q->tail;
+	mp = up;
+	if(p == 0)
+		q->head = mp;
+	else
+		p->qnext = mp;
+	q->tail = mp;
+	mp->qnext = 0;
+	mp->state = Queueing;
+	up->qpc = getcallerpc(&q);
+	unlock(&q->use);
+	sched();
+}
+
+int
+canqlock(QLock *q)
+{
+	if(!canlock(&q->use))
+		return 0;
+	if(q->locked){
+		unlock(&q->use);
+		return 0;
+	}
+	q->locked = 1;
+	unlock(&q->use);
+	return 1;
+}
+
+void
+qunlock(QLock *q)
+{
+	Proc *p;
+
+	lock(&q->use);
+	p = q->head;
+	if(p) {
+		q->head = p->qnext;
+		if(q->head == 0)
+			q->tail = 0;
+		unlock(&q->use);
+		ready(p);
+		return;
+	}
+	q->locked = 0;
+	unlock(&q->use);
+}
+
+void
+rlock(RWlock *l)
+{
+	qlock(&l->x);		/* wait here for writers and exclusion */
+	lock(l);
+	l->readers++;
+	canqlock(&l->k);	/* block writers if we are the first reader */
+	unlock(l);
+	qunlock(&l->x);
+}
+
+/* same as rlock but punts if there are any writers waiting */
+int
+canrlock(RWlock *l)
+{
+	if (!canqlock(&l->x))
+		return 0;
+	lock(l);
+	l->readers++;
+	canqlock(&l->k);	/* block writers if we are the first reader */
+	unlock(l);
+	qunlock(&l->x);
+	return 1;
+}
+
+void
+runlock(RWlock *l)
+{
+	lock(l);
+	if(--l->readers == 0)	/* last reader out allows writers */
+		qunlock(&l->k);
+	unlock(l);
+}
+
+void
+wlock(RWlock *l)
+{
+	qlock(&l->x);		/* wait here for writers and exclusion */
+	qlock(&l->k);		/* wait here for last reader */
+}
+
+void
+wunlock(RWlock *l)
+{
+	qunlock(&l->k);
+	qunlock(&l->x);
+}
--- /dev/null
+++ b/os/port/random.c
@@ -1,0 +1,156 @@
+#include	"u.h"
+#include	"../port/lib.h"
+#include	"mem.h"
+#include	"dat.h"
+#include	"fns.h"
+
+static struct
+{
+	QLock;
+	Rendez	producer;
+	Rendez	consumer;
+	ulong	randomcount;
+	uchar	buf[1024];
+	uchar	*ep;
+	uchar	*rp;
+	uchar	*wp;
+	uchar	next;
+	uchar	bits;
+	uchar	wakeme;
+	uchar	filled;
+	int	target;
+	int	kprocstarted;
+	ulong	randn;
+} rb;
+
+static int
+rbnotfull(void*)
+{
+	int i;
+
+	i = rb.wp - rb.rp;
+	if(i < 0)
+		i += sizeof(rb.buf);
+	return i < rb.target;
+}
+
+static int
+rbnotempty(void*)
+{
+	return rb.wp != rb.rp;
+}
+
+static void
+genrandom(void*)
+{
+	setpri(PriBackground);
+
+	for(;;) {
+		for(;;)
+			if(++rb.randomcount > 100000)
+				break;
+		if(anyhigher())
+			sched();
+		if(rb.filled || !rbnotfull(0))
+			sleep(&rb.producer, rbnotfull, 0);
+	}
+}
+
+/*
+ *  produce random bits in a circular buffer
+ */
+static void
+randomclock(void)
+{
+	uchar *p;
+
+	if(rb.randomcount == 0)
+		return;
+
+	if(!rbnotfull(0)) {
+		rb.filled = 1;
+		return;
+	}
+
+	rb.bits = (rb.bits<<2) ^ (rb.randomcount&3);
+	rb.randomcount = 0;
+
+	rb.next += 2;
+	if(rb.next != 8)
+		return;
+
+	rb.next = 0;
+	*rb.wp ^= rb.bits ^ *rb.rp;
+	p = rb.wp+1;
+	if(p == rb.ep)
+		p = rb.buf;
+	rb.wp = p;
+
+	if(rb.wakeme)
+		wakeup(&rb.consumer);
+}
+
+void
+randominit(void)
+{
+	/* Frequency close but not equal to HZ */
+	addclock0link(randomclock, 13);
+	rb.target = 16;
+	rb.ep = rb.buf + sizeof(rb.buf);
+	rb.rp = rb.wp = rb.buf;
+}
+
+/*
+ *  consume random bytes from a circular buffer
+ */
+ulong
+randomread(void *xp, ulong n)
+{
+	int i, sofar;
+	uchar *e, *p;
+
+	p = xp;
+
+	qlock(&rb);
+	if(waserror()){
+		qunlock(&rb);
+		nexterror();
+	}
+	if(!rb.kprocstarted){
+		rb.kprocstarted = 1;
+		kproc("genrand", genrandom, nil, 0);
+	}
+
+	for(sofar = 0; sofar < n; sofar += i){
+		i = rb.wp - rb.rp;
+		if(i == 0){
+			rb.wakeme = 1;
+			wakeup(&rb.producer);
+			sleep(&rb.consumer, rbnotempty, 0);
+			rb.wakeme = 0;
+			continue;
+		}
+		if(i < 0)
+			i = rb.ep - rb.rp;
+		if((i+sofar) > n)
+			i = n - sofar;
+		memmove(p + sofar, rb.rp, i);
+		e = rb.rp + i;
+		if(e == rb.ep)
+			e = rb.buf;
+		rb.rp = e;
+	}
+	if(rb.filled && rb.wp == rb.rp){
+		i = 2*rb.target;
+		if(i > sizeof(rb.buf) - 1)
+			i = sizeof(rb.buf) - 1;
+		rb.target = i;
+		rb.filled = 0;
+	}
+	poperror();
+	qunlock(&rb);
+
+	wakeup(&rb.producer);
+
+	return n;
+}
--- /dev/null
+++ b/os/port/rdb.c
@@ -1,0 +1,112 @@
+#include "u.h"
+#include "../port/lib.h"
+#include "mem.h"
+#include "dat.h"
+#include "fns.h"
+#include "io.h"
+#include "ureg.h"
+
+/*
+ * alternative debug protocol for plan 9's rdbfs(4) for plan 9's own acid
+ */
+#define DBG	if(0)scrprint
+#pragma varargck argpos scrprint 1
+static Ureg ureg;
+extern Queue *klogq;
+
+static void
+scrprint(char *fmt, ...)
+{
+	char buf[128];
+	va_list va;
+	int n;
+
+	va_start(va, fmt);
+	n = vseprint(buf, buf+sizeof buf, fmt, va)-buf;
+	va_end(va);
+	putstrn(buf, n);
+}
+
+static char*
+getline(void)
+{
+	static char buf[128];
+	int i, c;
+
+	for(;;){
+		for(i=0; i<nelem(buf) && (c=uartgetc()) != '\n'; i++){
+			DBG("%c...", c);
+			buf[i] = c;
+		}
+
+		if(i < nelem(buf)){
+			buf[i] = 0;
+			return buf;
+		}
+	}
+}
+
+static void*
+addr(char *s, Ureg *ureg, char **p)
+{
+	ulong a;
+
+	a = strtoul(s, p, 16);
+	if(a < sizeof(Ureg))
+		return ((uchar*)ureg)+a;
+	return (void*)a;
+}
+
+static void
+talkrdb(Ureg *ureg)
+{
+	uchar *a;
+	char *p;
+	char *req;
+
+	printq = nil;	// turn off serial console
+	klogq = nil;	// turn off /dev/kprint if active
+	iprint("Edebugger reset\n");
+	for(;;){
+		req = getline();
+		switch(*req){
+		case 'r':
+			a = addr(req+1, ureg, nil);
+			DBG("read %p\n", a);
+			iprint("R%.8lux %.2ux %.2ux %.2ux %.2ux\n", strtoul(req+1, 0, 16), a[0], a[1], a[2], a[3]);
+			break;
+
+		case 'w':
+			a = addr(req+1, ureg, &p);
+			*(ulong*)a = strtoul(p, nil, 16);
+			iprint("W\n");
+			break;
+/*
+ *		case Tmput:
+			n = min[4];
+			if(n > 4){
+				mesg(Rerr, Ecount);
+				break;
+			}
+			a = addr(min+0);
+			scrprint("mput %.8lux\n", a);
+			memmove(a, min+5, n);
+			mesg(Rmput, mout);
+			break;
+ *
+ */
+		default:
+			DBG("unknown %c\n", *req);
+			iprint("Eunknown message\n");
+			break;
+		}
+	}
+}
+
+void
+rdb(void)
+{
+	splhi();
+	iprint("rdb...");
+	callwithureg(talkrdb);
+}
--- /dev/null
+++ b/os/port/sd.h
@@ -1,0 +1,132 @@
+/*
+ * Storage Device.
+ */
+typedef struct SDev SDev;
+typedef struct SDifc SDifc;
+typedef struct SDpart SDpart;
+typedef struct SDperm SDperm;
+typedef struct SDreq SDreq;
+typedef struct SDunit SDunit;
+
+struct SDperm {
+	char*	name;
+	char*	user;
+	ulong	perm;
+};
+
+struct SDpart {
+	ulong	start;
+	ulong	end;
+	SDperm;
+	int	valid;
+	ulong	vers;
+};
+
+struct SDunit {
+	SDev*	dev;
+	int	subno;
+	uchar	inquiry[256];		/* format follows SCSI spec */
+	SDperm;
+
+	QLock	ctl;
+	ulong	sectors;
+	ulong	secsize;
+	SDpart*	part;			/* nil or array of size npart */
+	int	npart;
+	ulong	vers;
+	SDperm	ctlperm;
+
+	QLock	raw;			/* raw read or write in progress */
+	ulong	rawinuse;		/* really just a test-and-set */
+	int	state;
+	SDreq*	req;
+	SDperm	rawperm;
+};
+
+/* 
+ * Each controller is represented by a SDev.
+ * Each controller is responsible for allocating its unit structures.
+ * Each controller has at least one unit.
+ */ 
+struct SDev {
+	Ref	r;			/* Number of callers using device */
+	SDifc*	ifc;			/* pnp/legacy */
+	void*	ctlr;
+	int	idno;
+	char*	name;
+	SDev*	next;
+
+	QLock;				/* enable/disable */
+	int	enabled;
+	int	nunit;			/* Number of units */
+	QLock	unitlock;		/* `Loading' of units */
+	int*	unitflg;		/* Unit flags */
+	SDunit**unit;
+};
+
+struct SDifc {
+	char*	name;
+
+	SDev*	(*pnp)(void);
+	SDev*	(*legacy)(int, int);
+	SDev*	(*id)(SDev*);
+	int	(*enable)(SDev*);
+	int	(*disable)(SDev*);
+
+	int	(*verify)(SDunit*);
+	int	(*online)(SDunit*);
+	int	(*rio)(SDreq*);
+	int	(*rctl)(SDunit*, char*, int);
+	int	(*wctl)(SDunit*, Cmdbuf*);
+
+	long	(*bio)(SDunit*, int, int, void*, long, long);
+	SDev*	(*probe)(DevConf*);
+	void	(*clear)(SDev*);
+	char*	(*stat)(SDev*, char*, char*);
+};
+
+struct SDreq {
+	SDunit*	unit;
+	int	lun;
+	int	write;
+	uchar	cmd[16];
+	int	clen;
+	void*	data;
+	int	dlen;
+
+	int	flags;
+
+	int	status;
+	long	rlen;
+	uchar	sense[256];
+};
+
+enum {
+	SDnosense	= 0x00000001,
+	SDvalidsense	= 0x00010000,
+};
+
+enum {
+	SDretry		= -5,		/* internal to controllers */
+	SDmalloc	= -4,
+	SDeio		= -3,
+	SDtimeout	= -2,
+	SDnostatus	= -1,
+
+	SDok		= 0,
+
+	SDcheck		= 0x02,		/* check condition */
+	SDbusy		= 0x08,		/* busy */
+
+	SDmaxio		= 2048*1024,
+	SDnpart		= 16,
+};
+
+#define sdmalloc(n)	malloc(n)
+#define sdfree(p)	free(p)
+
+/* sdscsi.c */
+extern int scsiverify(SDunit*);
+extern int scsionline(SDunit*);
+extern long scsibio(SDunit*, int, int, void*, long, long);
+extern SDev* scsiid(SDev*, SDifc*);
--- /dev/null
+++ b/os/port/swcursor.c
@@ -1,0 +1,358 @@
+#include "u.h"
+#include "../port/lib.h"
+#include "mem.h"
+#include "dat.h"
+#include "fns.h"
+#include "../port/error.h"
+#include "io.h"
+
+#include <draw.h>
+#include <memdraw.h>
+#include <cursor.h>
+
+#include "screen.h"
+
+typedef struct SWcursor SWcursor;
+
+/*
+ *	Software cursor code: done by hand, might be better to use memdraw
+ */
+ 
+struct SWcursor {
+	ulong	*fb;	/* screen frame buffer */
+	Rectangle r;
+	int	d;	/* ldepth of screen */
+	int 	width;	/* width of screen in ulongs */
+	int	x;
+	int	y;
+	int	hotx;
+	int	hoty;
+	uchar	cbwid;	/* cursor byte width */
+	uchar	f;	/* flags */
+	uchar	cwid;
+	uchar	chgt;
+	int	hidecount;
+	uchar	data[CURSWID*CURSHGT];
+	uchar	mask[CURSWID*CURSHGT];
+	uchar	save[CURSWID*CURSHGT];
+};
+
+enum {
+	CUR_ENA = 0x01,		/* cursor is enabled */
+	CUR_DRW = 0x02,		/* cursor is currently drawn */
+	CUR_SWP = 0x10,		/* bit swap */
+};
+
+static Rectangle cursoroffrect;
+static int	cursorisoff;
+
+static void swcursorflush(int, int);
+static void	swcurs_draw_or_undraw(SWcursor *);
+
+static void
+cursorupdate0(void)
+{
+	int inrect, x, y;
+	Point m;
+
+	m = mousexy();
+	x = m.x - swc->hotx;
+	y = m.y - swc->hoty;
+	inrect = (x >= cursoroffrect.min.x && x < cursoroffrect.max.x
+		&& y >= cursoroffrect.min.y && y < cursoroffrect.max.y);
+	if (cursorisoff == inrect)
+		return;
+	cursorisoff = inrect;
+	if (inrect)
+		swcurs_hide(swc);
+	else {
+		swc->hidecount = 0;
+		swcurs_draw_or_undraw(swc);
+	}
+	swcursorflush(m.x, m.y);
+}
+
+void
+cursorupdate(Rectangle r)
+{
+	lock(vd);
+	r.min.x -= 16;
+	r.min.y -= 16;
+	cursoroffrect = r;
+	if (swc != nil)
+		cursorupdate0();
+	unlock(vd);
+}
+
+void
+cursorenable(void)
+{
+	Point m;
+
+	lock(vd);
+	if(swc != nil) {
+		swcurs_enable(swc);
+		m = mousexy();
+		swcursorflush(m.x, m.y);
+	}
+	unlock(vd);
+}
+
+void
+cursordisable(void)
+{
+	Point m;
+
+	lock(vd);
+	if(swc != nil) {
+		swcurs_disable(swc);
+		m = mousexy();
+		swcursorflush(m.x, m.y);
+	}
+	unlock(vd);
+}
+
+void
+swcursupdate(int oldx, int oldy, int x, int y)
+{
+
+	if(!canlock(vd))
+		return;		/* if can't lock, don't wake up stuff */
+
+	if(x < gscreen->r.min.x)
+		x = gscreen->r.min.x;
+	if(x >= gscreen->r.max.x)
+		x = gscreen->r.max.x;
+	if(y < gscreen->r.min.y)
+		y = gscreen->r.min.y;
+	if(y >= gscreen->r.max.y)
+		y = gscreen->r.max.y;
+	if(swc != nil) {
+		swcurs_hide(swc);
+		swc->x = x;
+		swc->y = y;
+		cursorupdate0();
+		swcurs_unhide(swc);
+		swcursorflush(oldx, oldy);
+		swcursorflush(x, y);
+	}
+
+	unlock(vd);
+}
+
+void
+drawcursor(Drawcursor* c)
+{
+	Point p, m;
+	Cursor curs, *cp;
+	int j, i, h, bpl;
+	uchar *bc, *bs, *cclr, *cset;
+
+	if(swc == nil)
+		return;
+
+	/* Set the default system cursor */
+	if(c == nil || c->data == nil){
+		swcurs_disable(swc);
+		return;
+	}
+	else {
+		cp = &curs;
+		p.x = c->hotx;
+		p.y = c->hoty;
+		cp->offset = p;
+		bpl = bytesperline(Rect(c->minx, c->miny, c->maxx, c->maxy), 1);
+
+		h = (c->maxy-c->miny)/2;
+		if(h > 16)
+			h = 16;
+
+		bc = c->data;
+		bs = c->data + h*bpl;
+
+		cclr = cp->clr;
+		cset = cp->set;
+		for(i = 0; i < h; i++) {
+			for(j = 0; j < 2; j++) {
+				cclr[j] = bc[j];
+				cset[j] = bs[j];
+			}
+			bc += bpl;
+			bs += bpl;
+			cclr += 2;
+			cset += 2;
+		}
+	}
+	swcurs_load(swc, cp);
+	m = mousexy();
+	swcursorflush(m.x, m.y);
+	swcurs_enable(swc);
+}
+
+SWcursor*
+swcurs_create(ulong *fb, int width, int ldepth, Rectangle r, int bitswap)
+{
+	SWcursor *swc;
+
+	swc = (SWcursor*)malloc(sizeof(SWcursor));
+	swc->fb = fb;
+	swc->r = r;
+	swc->d = ldepth;
+	swc->width = width;
+	swc->f = bitswap ? CUR_SWP : 0;
+	swc->x = swc->y = 0;
+	swc->hotx = swc->hoty = 0;
+	swc->hidecount = 0;
+	return swc;
+}
+
+void
+swcurs_destroy(SWcursor *swc)
+{
+	swcurs_disable(swc);
+	free(swc);
+}
+
+static void
+swcursorflush(int x, int y)
+{
+	Rectangle r;
+
+	/* XXX a little too paranoid here */
+	r.min.x = x-16;
+	r.min.y = y-16;
+	r.max.x = x+17;
+	r.max.y = y+17;
+	flushmemscreen(r);
+}
+
+static void
+swcurs_draw_or_undraw(SWcursor *swc)
+{
+	uchar *p;
+	uchar *cs;
+	int w, vw;
+	int x1 = swc->r.min.x;
+	int y1 = swc->r.min.y;
+	int x2 = swc->r.max.x;
+	int y2 = swc->r.max.y; 
+	int xp = swc->x - swc->hotx;
+	int yp = swc->y - swc->hoty;
+	int ofs;
+
+	if(((swc->f & CUR_ENA) && (swc->hidecount <= 0))
+			 == ((swc->f & CUR_DRW) != 0))
+		return;
+	w = swc->cbwid*BI2BY/(1 << swc->d);
+	x1 = xp < x1 ? x1 : xp;
+	y1 = yp < y1 ? y1 : yp;
+	x2 = xp+w >= x2 ? x2 : xp+w;
+	y2 = yp+swc->chgt >= y2 ? y2 : yp+swc->chgt;
+	if(x2 <= x1 || y2 <= y1)
+		return;
+	p = (uchar*)(swc->fb + swc->width*y1)
+		+ x1*(1 << swc->d)/BI2BY;
+	y2 -= y1;
+	x2 = (x2-x1)*(1 << swc->d)/BI2BY;
+	vw = swc->width*BY2WD - x2;
+	w = swc->cbwid - x2;
+	ofs = swc->cbwid*(y1-yp)+(x1-xp);
+	cs = swc->save + ofs;
+	if((swc->f ^= CUR_DRW) & CUR_DRW) {
+		uchar *cm = swc->mask + ofs; 
+		uchar *cd = swc->data + ofs;
+		while(y2--) {
+			x1 = x2;
+			while(x1--) {
+				*p = ((*cs++ = *p) & *cm++) ^ *cd++;
+				p++;
+			}
+			cs += w;
+			cm += w;
+			cd += w;
+			p += vw;
+		}
+	} else {
+		while(y2--) {
+			x1 = x2;
+			while(x1--) 
+				*p++ = *cs++;
+			cs += w;
+			p += vw;
+		}
+	}
+}
+
+void
+swcurs_hide(SWcursor *swc)
+{
+	++swc->hidecount;
+	swcurs_draw_or_undraw(swc);
+}
+
+void
+swcurs_unhide(SWcursor *swc)
+{
+	if (--swc->hidecount < 0)
+		swc->hidecount = 0;
+	swcurs_draw_or_undraw(swc);
+}
+
+void
+swcurs_enable(SWcursor *swc)
+{
+	swc->f |= CUR_ENA;
+	swcurs_draw_or_undraw(swc);
+}
+
+void
+swcurs_disable(SWcursor *swc)
+{
+	swc->f &= ~CUR_ENA;
+	swcurs_draw_or_undraw(swc);
+}
+
+void
+swcurs_load(SWcursor *swc, Cursor *c)
+{
+	int i, k;
+	uchar *bc, *bs, *cd, *cm;
+	static uchar bdv[4] = {0,Backgnd,Foregnd,0xff};
+	static uchar bmv[4] = {0xff,0,0,0xff};
+	int bits = 1<<swc->d;
+	uchar mask = (1<<bits)-1;
+	int bswp = (swc->f&CUR_SWP) ? 8-bits : 0;
+
+	bc = c->clr;
+	bs = c->set;
+
+	swcurs_hide(swc);
+	cd = swc->data;
+	cm = swc->mask;
+	swc->hotx = c->offset.x;
+	swc->hoty = c->offset.y;
+	swc->chgt = CURSHGT;
+	swc->cwid = CURSWID;
+	swc->cbwid = CURSWID*(1<<swc->d)/BI2BY;
+	for(i = 0; i < CURSWID/BI2BY*CURSHGT; i++) {
+		uchar bcb = *bc++;
+		uchar bsb = *bs++;
+		for(k=0; k<BI2BY;) {
+			uchar cdv = 0;
+			uchar cmv = 0;
+			int z;
+			for(z=0; z<BI2BY; z += bits) {
+				int n = ((bsb&(0x80))|((bcb&(0x80))<<1))>>7;
+				int s = z^bswp;
+				cdv |= (bdv[n]&mask) << s;
+				cmv |= (bmv[n]&mask) << s;
+				bcb <<= 1;
+				bsb <<= 1;
+				k++;
+			}
+			*cd++ = cdv;
+			*cm++ = cmv;
+		}
+	}
+	swcurs_unhide(swc);
+}
--- /dev/null
+++ b/os/port/sysfile.c
@@ -1,0 +1,1125 @@
+#include	"u.h"
+#include	"../port/lib.h"
+#include	"mem.h"
+#include	"dat.h"
+#include	"fns.h"
+#include	"../port/error.h"
+
+static int
+growfd(Fgrp *f, int fd)
+{
+	int n;
+	Chan **nfd, **ofd;
+
+	if(fd < f->nfd)
+		return 0;
+	n = f->nfd+DELTAFD;
+	if(n > MAXNFD)
+		n = MAXNFD;
+	if(fd >= n)
+		return -1;
+	nfd = malloc(n*sizeof(Chan*));
+	if(nfd == nil)
+		return -1;
+	ofd = f->fd;
+	memmove(nfd, ofd, f->nfd*sizeof(Chan *));
+	f->fd = nfd;
+	f->nfd = n;
+	free(ofd);
+	return 0;
+}
+
+int
+newfd(Chan *c)
+{
+	int i;
+	Fgrp *f = up->env->fgrp;
+
+	lock(f);
+	for(i=f->minfd; i<f->nfd; i++)
+		if(f->fd[i] == 0)
+			break;
+	if(i >= f->nfd && growfd(f, i) < 0){
+		unlock(f);
+		exhausted("file descriptors");
+		return -1;
+	}
+	f->minfd = i + 1;
+	if(i > f->maxfd)
+		f->maxfd = i;
+	f->fd[i] = c;
+	unlock(f);
+	return i;
+}
+
+Chan*
+fdtochan(Fgrp *f, int fd, int mode, int chkmnt, int iref)
+{
+	Chan *c;
+
+	c = 0;
+
+	lock(f);
+	if(fd<0 || f->maxfd<fd || (c = f->fd[fd])==0) {
+		unlock(f);
+		error(Ebadfd);
+	}
+	if(iref)
+		incref(c);
+	unlock(f);
+
+	if(chkmnt && (c->flag&CMSG)) {
+		if(iref)
+			cclose(c);
+		error(Ebadusefd);
+	}
+
+	if(mode<0 || c->mode==ORDWR)
+		return c;
+
+	if((mode&OTRUNC) && c->mode==OREAD) {
+		if(iref)
+			cclose(c);
+		error(Ebadusefd);
+	}
+
+	if((mode&~OTRUNC) != c->mode) {
+		if(iref)
+			cclose(c);
+		error(Ebadusefd);
+	}
+
+	return c;
+}
+
+long
+kchanio(void *vc, void *buf, int n, int mode)
+{
+	int r;
+	Chan *c;
+
+	c = vc;
+	if(waserror())
+		return -1;
+
+	if(mode == OREAD)
+		r = devtab[c->type]->read(c, buf, n, c->offset);
+	else
+		r = devtab[c->type]->write(c, buf, n, c->offset);
+
+	lock(c);
+	c->offset += r;
+	unlock(c);
+	poperror();
+	return r;
+}
+
+int
+openmode(ulong o)
+{
+	if(o >= (OTRUNC|OCEXEC|ORCLOSE|OEXEC))
+		error(Ebadarg);
+	o &= ~(OTRUNC|OCEXEC|ORCLOSE);
+	if(o > OEXEC)
+		error(Ebadarg);
+	if(o == OEXEC)
+		return OREAD;
+	return o;
+}
+
+void
+fdclose(Fgrp *f, int fd)
+{
+	int i;
+	Chan *c;
+
+	lock(f);
+	c = f->fd[fd];
+	if(c == 0){
+		/* can happen for users with shared fd tables */
+		unlock(f);
+		return;
+	}
+	f->fd[fd] = 0;
+	if(fd == f->maxfd)
+		for(i=fd; --i>=0 && f->fd[i]==0; )
+			f->maxfd = i;
+	if(fd < f->minfd)
+		f->minfd = fd;
+	unlock(f);
+	cclose(c);
+}
+
+int
+kchdir(char *path)
+{
+	Chan *c;
+	Pgrp *pg;
+
+	if(waserror())
+		return -1;
+
+	c = namec(path, Atodir, 0, 0);
+	pg = up->env->pgrp;
+	cclose(pg->dot);
+	pg->dot = c;
+	poperror();
+	return 0;
+}
+
+int
+kfgrpclose(Fgrp *f, int fd)
+{
+	if(waserror())
+		return -1;
+
+	/*
+	 * Take no reference on the chan because we don't really need the
+	 * data structure, and are calling fdtochan only for error checks.
+	 * fdclose takes care of processes racing through here.
+	 */
+	fdtochan(f, fd, -1, 0, 0);
+	fdclose(f, fd);
+	poperror();
+	return 0;
+}
+
+int
+kclose(int fd)
+{
+	return kfgrpclose(up->env->fgrp, fd);
+}
+
+int
+kcreate(char *path, int mode, ulong perm)
+{
+	int fd;
+	Chan *c;
+
+	if(waserror())
+		return -1;
+
+	openmode(mode&~OEXCL);	/* error check only; OEXCL okay here */
+	c = namec(path, Acreate, mode, perm);
+	if(waserror()) {
+		cclose(c);
+		nexterror();
+	}
+	fd = newfd(c);
+	if(fd < 0)
+		error(Enofd);
+	poperror();
+
+	poperror();
+	return fd;
+}
+
+int
+kdup(int old, int new)
+{
+	int fd;
+	Chan *c, *oc;
+	Fgrp *f = up->env->fgrp;
+
+	if(waserror())
+		return -1;
+
+	c = fdtochan(up->env->fgrp, old, -1, 0, 1);
+	if(c->qid.type & QTAUTH)
+		error(Eperm);
+	fd = new;
+	if(fd != -1){
+		lock(f);
+		if(fd<0 || growfd(f, fd) < 0) {
+			unlock(f);
+			cclose(c);
+			error(Ebadfd);
+		}
+		if(fd > f->maxfd)
+			f->maxfd = fd;
+		oc = f->fd[fd];
+		f->fd[fd] = c;
+		unlock(f);
+		if(oc)
+			cclose(oc);
+	}else{
+		if(waserror()) {
+			cclose(c);
+			nexterror();
+		}
+		fd = newfd(c);
+		if(fd < 0)
+			error(Enofd);
+		poperror();
+	}
+	poperror();
+	return fd;
+}
+
+int
+kfstat(int fd, uchar *buf, int n)
+{
+	Chan *c;
+
+	if(waserror())
+		return -1;
+
+	c = fdtochan(up->env->fgrp, fd, -1, 0, 1);
+	if(waserror()) {
+		cclose(c);
+		nexterror();
+	}
+	devtab[c->type]->stat(c, buf, n);
+
+	poperror();
+	cclose(c);
+
+	poperror();
+	return n;
+}
+
+char*
+kfd2path(int fd)
+{
+	Chan *c;
+	char *s;
+
+	if(waserror())
+		return nil;
+	c = fdtochan(up->env->fgrp, fd, -1, 0, 1);
+	s = nil;
+	if(c->name != nil){
+		s = malloc(c->name->len+1);
+		if(s == nil){
+			cclose(c);
+			error(Enomem);
+		}
+		memmove(s, c->name->s, c->name->len+1);
+		cclose(c);
+	}
+	poperror();
+	return s;
+}
+
+int
+kfauth(int fd, char *aname)
+{
+	Chan *c, *ac;
+
+	if(waserror())
+		return -1;
+
+	validname(aname, 1);
+	c = fdtochan(up->env->fgrp, fd, ORDWR, 0, 1);
+	if(waserror()){
+		cclose(c);
+		nexterror();
+	}
+
+	ac = mntauth(c, aname);
+
+	/* at this point ac is responsible for keeping c alive */
+	poperror();	/* c */
+	cclose(c);
+
+	if(waserror()){
+		cclose(ac);
+		nexterror();
+	}
+
+	fd = newfd(ac);
+	if(fd < 0)
+		error(Enofd);
+	poperror();	/* ac */
+
+	poperror();
+
+	return fd;
+}
+
+int
+kfversion(int fd, uint msize, char *vers, uint arglen)
+{
+	int m;
+	Chan *c;
+
+	if(waserror())
+		return -1;
+
+	/* check there's a NUL in the version string */
+	if(arglen==0 || memchr(vers, 0, arglen)==0)
+		error(Ebadarg);
+
+	c = fdtochan(up->env->fgrp, fd, ORDWR, 0, 1);
+	if(waserror()){
+		cclose(c);
+		nexterror();
+	}
+
+	m = mntversion(c, vers, msize, arglen);
+
+	poperror();
+	cclose(c);
+
+	poperror();
+	return m;
+}
+
+int
+kpipe(int fd[2])
+{
+	Dev *d;
+	Fgrp *f;
+	Chan *c[2];
+	static char *names[] = {"data", "data1"};
+
+	f = up->env->fgrp;
+
+	d = devtab[devno('|', 0)];
+	c[0] = namec("#|", Atodir, 0, 0);
+	c[1] = 0;
+	fd[0] = -1;
+	fd[1] = -1;
+	if(waserror()) {
+		if(c[0] != 0)
+			cclose(c[0]);
+		if(c[1] != 0)
+			cclose(c[1]);
+		if(fd[0] >= 0)
+			f->fd[fd[0]]=0;
+		if(fd[1] >= 0)
+			f->fd[fd[1]]=0;
+		return -1;
+	}
+	c[1] = cclone(c[0]);
+	if(walk(&c[0], &names[0], 1, 1, nil) < 0)
+		error(Egreg);
+	if(walk(&c[1], &names[1], 1, 1, nil) < 0)
+		error(Egreg);
+	c[0] = d->open(c[0], ORDWR);
+	c[1] = d->open(c[1], ORDWR);
+	fd[0] = newfd(c[0]);
+	if(fd[0] < 0)
+		error(Enofd);
+	fd[1] = newfd(c[1]);
+	if(fd[1] < 0)
+		error(Enofd);
+	poperror();
+	return 0;
+}
+
+int
+kfwstat(int fd, uchar *buf, int n)
+{
+	Chan *c;
+
+	if(waserror())
+		return -1;
+
+	validstat(buf, n);
+	c = fdtochan(up->env->fgrp, fd, -1, 1, 1);
+	if(waserror()) {
+		cclose(c);
+		nexterror();
+	}
+	n = devtab[c->type]->wstat(c, buf, n);
+	poperror();
+	cclose(c);
+
+	poperror();
+	return n;
+}
+
+long
+bindmount(Chan *c, char *old, int flag, char *spec)
+{
+	int ret;
+	Chan *c1;
+
+	if(flag>MMASK || (flag&MORDER) == (MBEFORE|MAFTER))
+		error(Ebadarg);
+
+	c1 = namec(old, Amount, 0, 0);
+	if(waserror()){
+		cclose(c1);
+		nexterror();
+	}
+	ret = cmount(c, c1, flag, spec);
+
+	poperror();
+	cclose(c1);
+	return ret;
+}
+
+int
+kbind(char *new, char *old, int flags)
+{
+	long r;
+	Chan *c0;
+
+	if(waserror())
+		return -1;
+
+	c0 = namec(new, Abind, 0, 0);
+	if(waserror()) {
+		cclose(c0);
+		nexterror();
+	}
+	r = bindmount(c0, old, flags, "");
+	poperror();
+	cclose(c0);
+
+	poperror();
+	return r;
+}
+
+int
+kmount(int fd, int afd, char *old, int flags, char *spec)
+{
+	long r;
+	volatile struct { Chan *c; } c0;
+	volatile struct { Chan *c; } bc;
+	volatile struct { Chan *c; } ac;
+	Mntparam mntparam;
+
+	ac.c = nil;
+	bc.c = nil;
+	c0.c = nil;
+	if(waserror()) {
+		cclose(ac.c);
+		cclose(bc.c);
+		cclose(c0.c);
+		return -1;
+	}
+	bc.c = fdtochan(up->env->fgrp, fd, ORDWR, 0, 1);
+	if(afd >= 0)
+		ac.c = fdtochan(up->env->fgrp, afd, ORDWR, 0, 1);
+	mntparam.chan = bc.c;
+	mntparam.authchan = ac.c;
+	mntparam.spec = spec;
+	mntparam.flags = flags;
+	c0.c = devtab[devno('M', 0)]->attach((char*)&mntparam);
+
+	r = bindmount(c0.c, old, flags, spec);
+	poperror();
+	cclose(ac.c);
+	cclose(bc.c);
+	cclose(c0.c);
+
+	return r;
+}
+
+int
+kunmount(char *old, char *new)
+{
+	volatile struct { Chan *c; } cmount;
+	volatile struct { Chan *c; } cmounted;
+
+	cmount.c = nil;
+	cmounted.c = nil;
+	if(waserror()) {
+		cclose(cmount.c);
+		cclose(cmounted.c);
+		return -1;
+	}
+
+	cmount.c = namec(new, Amount, 0, 0);
+	if(old != nil && old[0] != '\0') {
+		/*
+		 * This has to be namec(..., Aopen, ...) because
+		 * if arg[0] is something like /srv/cs or /fd/0,
+		 * opening it is the only way to get at the real
+		 * Chan underneath.
+		 */
+		cmounted.c = namec(old, Aopen, OREAD, 0);
+	}
+
+	cunmount(cmount.c, cmounted.c);
+	poperror();
+	cclose(cmount.c);
+	cclose(cmounted.c);
+	return 0;
+}
+
+int
+kopen(char *path, int mode)
+{
+	int fd;
+	Chan *c;
+
+	if(waserror())
+		return -1;
+
+	openmode(mode);                         /* error check only */
+	c = namec(path, Aopen, mode, 0);
+	if(waserror()){
+		cclose(c);
+		nexterror();
+	}
+	fd = newfd(c);
+	if(fd < 0)
+		error(Enofd);
+	poperror();
+
+	poperror();
+	return fd;
+}
+
+long
+unionread(Chan *c, void *va, long n)
+{
+	int i;
+	long nr;
+	Mhead *m;
+	Mount *mount;
+
+	qlock(&c->umqlock);
+	m = c->umh;
+	rlock(&m->lock);
+	mount = m->mount;
+	/* bring mount in sync with c->uri and c->umc */
+	for(i = 0; mount != nil && i < c->uri; i++)
+		mount = mount->next;
+
+	nr = 0;
+	while(mount != nil) {
+		/* Error causes component of union to be skipped */
+		if(mount->to && !waserror()) {
+			if(c->umc == nil){
+				c->umc = cclone(mount->to);
+				c->umc = devtab[c->umc->type]->open(c->umc, OREAD);
+			}
+	
+			nr = devtab[c->umc->type]->read(c->umc, va, n, c->umc->offset);
+			if(nr < 0)
+				nr = 0;	/* dev.c can return -1 */
+			c->umc->offset += nr;
+			poperror();
+		}
+		if(nr > 0)
+			break;
+
+		/* Advance to next element */
+		c->uri++;
+		if(c->umc) {
+			cclose(c->umc);
+			c->umc = nil;
+		}
+		mount = mount->next;
+	}
+	runlock(&m->lock);
+	qunlock(&c->umqlock);
+	return nr;
+}
+
+static void
+unionrewind(Chan *c)
+{
+	qlock(&c->umqlock);
+	c->uri = 0;
+	if(c->umc){
+		cclose(c->umc);
+		c->umc = nil;
+	}
+	qunlock(&c->umqlock);
+}
+
+static long
+rread(int fd, void *va, long n, vlong *offp)
+{
+	int dir;
+	Chan *c;
+	vlong off;
+
+	if(waserror())
+		return -1;
+
+	c = fdtochan(up->env->fgrp, fd, OREAD, 1, 1);
+	if(waserror()) {
+		cclose(c);
+		nexterror();
+	}
+
+	if(n < 0)
+		error(Etoosmall);
+
+	dir = c->qid.type & QTDIR;
+	if(dir && c->umh)
+		n = unionread(c, va, n);
+	else{
+		if(offp == nil){
+			lock(c);	/* lock for vlong assignment */
+			off = c->offset;
+			unlock(c);
+		}else
+			off = *offp;
+		if(off < 0)
+			error(Enegoff);
+		if(off == 0){
+			if(offp == nil){
+				lock(c);
+				c->offset = 0;
+				c->dri = 0;
+				unlock(c);
+			}
+			unionrewind(c);
+		}
+		n = devtab[c->type]->read(c, va, n, off);
+		lock(c);
+		c->offset += n;
+		unlock(c);
+	}
+
+	poperror();
+	cclose(c);
+
+	poperror();
+	return n;
+}
+
+long
+kread(int fd, void *va, long n)
+{
+	return rread(fd, va, n, nil);
+}
+
+long
+kpread(int fd, void *va, long n, vlong off)
+{
+	return rread(fd, va, n, &off);
+}
+
+int
+kremove(char *path)
+{
+	Chan *c;
+
+	if(waserror())
+		return -1;
+
+	c = namec(path, Aremove, 0, 0);
+	if(waserror()) {
+		c->type = 0;	/* see below */
+		cclose(c);
+		nexterror();
+	}
+	devtab[c->type]->remove(c);
+	/*
+	 * Remove clunks the fid, but we need to recover the Chan
+	 * so fake it up.  rootclose() is known to be a nop.
+	 */
+	c->type = 0;
+	poperror();
+	cclose(c);
+
+	poperror();
+	return 0;
+}
+
+vlong
+kseek(int fd, vlong off, int whence)
+{
+	Dir *dir;
+	Chan *c;
+
+	if(waserror())
+		return -1;
+
+	c = fdtochan(up->env->fgrp, fd, -1, 1, 1);
+	if(waserror()) {
+		cclose(c);
+		nexterror();
+	}
+
+	if(devtab[c->type]->dc == '|')
+		error(Eisstream);
+
+	switch(whence) {
+	case 0:
+		if(c->qid.type & QTDIR){
+			if(off != 0)
+				error(Eisdir);
+			unionrewind(c);
+		}else if(off < 0)
+			error(Enegoff);
+		lock(c);	/* lock for vlong assignment */
+		c->offset = off;
+		unlock(c);
+		break;
+
+	case 1:
+		if(c->qid.type & QTDIR)
+			error(Eisdir);
+		lock(c);	/* lock for read/write update */
+		off += c->offset;
+		if(off < 0){
+			unlock(c);
+			error(Enegoff);
+		}
+		c->offset = off;
+		unlock(c);
+		break;
+
+	case 2:
+		if(c->qid.type & QTDIR)
+			error(Eisdir);
+		dir = chandirstat(c);
+		if(dir == nil)
+			error("internal error: stat error in seek");
+		off += dir->length;
+		free(dir);
+		if(off < 0)
+			error(Enegoff);
+		lock(c);	/* lock for read/write update */
+		c->offset = off;
+		unlock(c);
+		break;
+
+	default:
+		error(Ebadarg);
+		break;
+	}
+	poperror();
+	c->dri = 0;
+	cclose(c);
+	poperror();
+	return off;
+}
+
+void
+validstat(uchar *s, int n)
+{
+	int m;
+	char buf[64];
+
+	if(statcheck(s, n) < 0)
+		error(Ebadstat);
+	/* verify that name entry is acceptable */
+	s += STATFIXLEN - 4*BIT16SZ;	/* location of first string */
+	/*
+	 * s now points at count for first string.
+	 * if it's too long, let the server decide; this is
+	 * only for his protection anyway. otherwise
+	 * we'd have to allocate and waserror.
+	 */
+	m = GBIT16(s);
+	s += BIT16SZ;
+	if(m+1 > sizeof buf)
+		return;
+	memmove(buf, s, m);
+	buf[m] = '\0';
+	/* name could be '/' */
+	if(strcmp(buf, "/") != 0)
+		validname(buf, 0);
+}
+
+int
+kstat(char *path, uchar *buf, int n)
+{
+	Chan *c;
+
+	if(waserror())
+		return -1;
+
+	c = namec(path, Aaccess, 0, 0);
+	if(waserror()){
+		cclose(c);
+		nexterror();
+	}
+	devtab[c->type]->stat(c, buf, n);
+	poperror();
+	cclose(c);
+
+	poperror();
+	return 0;
+}
+
+static long
+rwrite(int fd, void *va, long n, vlong *offp)
+{
+	Chan *c;
+	vlong off;
+	long m;
+
+	if(waserror())
+		return -1;
+	c = fdtochan(up->env->fgrp, fd, OWRITE, 1, 1);
+	if(waserror()) {
+		cclose(c);
+		nexterror();
+	}
+	if(c->qid.type & QTDIR)
+		error(Eisdir);
+
+	if(n < 0)
+		error(Etoosmall);
+
+	if(offp == nil){
+		lock(c);
+		off = c->offset;
+		c->offset += n;
+		unlock(c);
+	}else
+		off = *offp;
+
+	if(waserror()){
+		if(offp == nil){
+			lock(c);
+			c->offset -= n;
+			unlock(c);
+		}
+		nexterror();
+	}
+	if(off < 0)
+		error(Enegoff);
+	m = devtab[c->type]->write(c, va, n, off);
+	poperror();
+
+	if(offp == nil && m < n){
+		lock(c);
+		c->offset -= n - m;
+		unlock(c);
+	}
+
+	poperror();
+	cclose(c);
+
+	poperror();
+	return n;
+}
+
+long
+kwrite(int fd, void *va, long n)
+{
+	return rwrite(fd, va, n, nil);
+}
+
+long
+kpwrite(int fd, void *va, long n, vlong off)
+{
+	return rwrite(fd, va, n, &off);
+}
+
+int
+kwstat(char *path, uchar *buf, int n)
+{
+	Chan *c;
+
+	if(waserror())
+		return -1;
+
+	validstat(buf, n);
+	c = namec(path, Aaccess, 0, 0);
+	if(waserror()){
+		cclose(c);
+		nexterror();
+	}
+	n = devtab[c->type]->wstat(c, buf, n);
+	poperror();
+	cclose(c);
+
+	poperror();
+	return n;
+}
+
+enum
+{
+	DIRSIZE = STATFIXLEN + 32 * 4,
+	DIRREADLIM = 2048,	/* should handle the largest reasonable directory entry */
+};
+
+Dir*
+chandirstat(Chan *c)
+{
+	Dir *d;
+	uchar *buf;
+	int n, nd, i;
+
+	nd = DIRSIZE;
+	for(i=0; i<2; i++){	/* should work by the second try */
+		d = smalloc(sizeof(Dir) + nd);
+		buf = (uchar*)&d[1];
+		if(waserror()){
+			free(d);
+			return nil;
+		}
+		n = devtab[c->type]->stat(c, buf, nd);
+		poperror();
+		if(n < BIT16SZ){
+			free(d);
+			return nil;
+		}
+		nd = GBIT16((uchar*)buf) + BIT16SZ;	/* size needed to store whole stat buffer including count */
+		if(nd <= n){
+			convM2D(buf, n, d, (char*)&d[1]);
+			return d;
+		}
+		/* else sizeof(Dir)+nd is plenty */
+		free(d);
+	}
+	return nil;
+
+}
+
+Dir*
+kdirstat(char *name)
+{
+	Chan *c;
+	Dir *d;
+
+	if(waserror())
+		return nil;
+
+	c = namec(name, Aaccess, 0, 0);
+	if(waserror()){
+		cclose(c);
+		nexterror();
+	}
+	d = chandirstat(c);
+	poperror();
+	cclose(c);
+
+	poperror();
+	return d;
+}
+
+Dir*
+kdirfstat(int fd)
+{
+	Chan *c;
+	Dir *d;
+
+	if(waserror())
+		return nil;
+
+	c = fdtochan(up->env->fgrp, fd, -1, 0, 1);
+	if(waserror()) {
+		cclose(c);
+		nexterror();
+	}
+	d = chandirstat(c);
+	poperror();
+	cclose(c);
+
+	poperror();
+	return d;
+}
+
+int
+kdirwstat(char *name, Dir *dir)
+{
+	uchar *buf;
+	int r;
+
+	r = sizeD2M(dir);
+	buf = smalloc(r);
+	convD2M(dir, buf, r);
+	r = kwstat(name, buf, r);
+	free(buf);
+	return r < 0? r: 0;
+}
+
+int
+kdirfwstat(int fd, Dir *dir)
+{
+	uchar *buf;
+	int r;
+
+	r = sizeD2M(dir);
+	buf = smalloc(r);
+	convD2M(dir, buf, r);
+	r = kfwstat(fd, buf, r);
+	free(buf);
+	return r < 0? r: 0;
+}
+
+static long
+dirpackage(uchar *buf, long ts, Dir **d)
+{
+	char *s;
+	long ss, i, n, nn, m;
+
+	*d = nil;
+	if(ts <= 0)
+		return ts;
+
+	/*
+	 * 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)
+		error("bad directory format");
+
+	*d = malloc(n * sizeof(Dir) + ss);
+	if(*d == nil)
+		error(Enomem);
+
+	/*
+	 * 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;
+			error("bad directory entry");
+		}
+		nn++;
+		s += m;
+	}
+
+	return nn;
+}
+
+long
+kdirread(int fd, Dir **d)
+{
+	uchar *buf;
+	long ts;
+
+	*d = nil;
+	if(waserror())
+		return -1;
+	buf = malloc(DIRREADLIM);
+	if(buf == nil)
+		error(Enomem);
+	if(waserror()){
+		free(buf);
+		nexterror();
+	}
+	ts = kread(fd, buf, DIRREADLIM);
+	if(ts >= 0)
+		ts = dirpackage(buf, ts, d);
+	poperror();
+	free(buf);
+	poperror();
+	return ts;
+}
+
+int
+kiounit(int fd)
+{
+	Chan *c;
+	int n;
+
+	c = fdtochan(up->env->fgrp, fd, -1, 0, 1);
+	if(waserror()){
+		cclose(c);
+		return 0;	/* n.b. */
+	}
+	n = c->iounit;
+	poperror();
+	cclose(c);
+	return n;
+}
--- /dev/null
+++ b/os/port/taslock.c
@@ -1,0 +1,126 @@
+#include "u.h"
+#include "../port/lib.h"
+#include "mem.h"
+#include "dat.h"
+#include "fns.h"
+#include "../port/error.h"
+
+static void
+lockloop(Lock *l, ulong pc)
+{
+	setpanic();
+	print("lock loop 0x%lux key 0x%lux pc 0x%lux held by pc 0x%lux\n", l, l->key, pc, l->pc);
+	panic("lockloop");
+}
+
+void
+lock(Lock *l)
+{
+	int i;
+	ulong pc;
+
+	pc = getcallerpc(&l);
+	if(up == 0) {
+		if (_tas(&l->key) != 0) {
+			for(i=0; ; i++) {
+				if(_tas(&l->key) == 0)
+					break;
+				if (i >= 1000000) {
+					lockloop(l, pc);
+					break;
+				}
+			}
+		}
+		l->pc = pc;
+		return;
+	}
+
+	for(i=0; ; i++) {
+		if(_tas(&l->key) == 0)
+			break;
+		if (i >= 1000) {
+			lockloop(l, pc);
+			break;
+		}
+		if(conf.nmach == 1 && up->state == Running && islo()) {
+			up->pc = pc;
+			sched();
+		}
+	}
+	l->pri = up->pri;
+	up->pri = PriLock;
+	l->pc = pc;
+}
+
+void
+ilock(Lock *l)
+{
+	ulong x, pc;
+	int i;
+
+	pc = getcallerpc(&l);
+	x = splhi();
+	for(;;) {
+		if(_tas(&l->key) == 0) {
+			l->sr = x;
+			l->pc = pc;
+			return;
+		}
+		if(conf.nmach < 2)
+			panic("ilock: no way out: pc 0x%lux: lock 0x%lux held by pc 0x%lux", pc, l, l->pc);
+		for(i=0; ; i++) {
+			if(l->key == 0)
+				break;
+			clockcheck();
+			if (i > 100000) {
+				lockloop(l, pc);
+				break;
+			}
+		}
+	}
+}
+
+int
+canlock(Lock *l)
+{
+	if(_tas(&l->key))
+		return 0;
+	if(up){
+		l->pri = up->pri;
+		up->pri = PriLock;
+	}
+	l->pc = getcallerpc(&l);
+	return 1;
+}
+
+void
+unlock(Lock *l)
+{
+	int p;
+
+	if(l->key == 0)
+		print("unlock: not locked: pc %lux\n", getcallerpc(&l));
+	p = l->pri;
+	l->pc = 0;
+	l->key = 0;
+	coherence();
+	if(up){
+		up->pri = p;
+		if(up->state == Running && anyhigher())
+			sched();
+	}
+}
+
+void
+iunlock(Lock *l)
+{
+	ulong sr;
+
+	if(l->key == 0)
+		print("iunlock: not locked: pc %lux\n", getcallerpc(&l));
+	sr = l->sr;
+	l->pc = 0;
+	l->key = 0;
+	coherence();
+	splxpc(sr);
+}
--- /dev/null
+++ b/os/port/tod.c
@@ -1,0 +1,283 @@
+#include	"u.h"
+#include	"../port/lib.h"
+#include	"mem.h"
+#include	"dat.h"
+#include	"fns.h"
+#include	"../port/error.h"
+
+/* compute nanosecond epoch time from the fastest ticking clock
+ * on the system.  converting the time to nanoseconds requires
+ * the following formula
+ *
+ *	t = (((1000000000<<31)/f)*ticks)>>31
+ *
+ *  where
+ *
+ *	'f'		is the clock frequency
+ *	'ticks'		are clock ticks
+ *
+ *  to avoid too much calculation in todget(), we calculate
+ *
+ *	mult = (1000000000<<32)/f
+ *
+ *  each time f is set.  f is normally set by a user level
+ *  program writing to /dev/fastclock.  mul64fract will then
+ *  take that fractional multiplier and a 64 bit integer and
+ *  return the resulting integer product.
+ *
+ *  We assume that the cpu's of a multiprocessor are synchronized.
+ *  This assumption needs to be questioned with each new architecture.
+ */
+
+/* frequency of the tod clock */
+#define TODFREQ	1000000000ULL
+
+struct {
+	int		init;		// true if initialized
+	ulong	cnt;
+	Lock;
+	uvlong	multiplier;	// t = off + (multiplier*ticks)>>31
+	uvlong	divider;	// ticks = (divider*(ticks-off))>>31
+	vlong	hz;		// frequency of fast clock
+	vlong	last;		// last reading of fast clock
+	vlong	off;		// offset from epoch to last
+	vlong	lasttime;	// last return value from todget
+	vlong	delta;		// add 'delta' each slow clock tick from sstart to send
+	ulong	sstart;		// ...
+	ulong	send;		// ...
+} tod;
+
+void
+todinit(void)
+{
+	if(tod.init)
+		return;
+	ilock(&tod);
+	tod.last = fastticks((uvlong*)&tod.hz);
+	iunlock(&tod);
+	todsetfreq(tod.hz);
+	tod.init = 1;
+	addclock0link(todfix, 100);
+}
+
+/*
+ *  calculate multiplier
+ */
+void
+todsetfreq(vlong f)
+{
+	ilock(&tod);
+	tod.hz = f;
+
+	/* calculate multiplier for time conversion */
+	tod.multiplier = mk64fract(TODFREQ, f);
+	tod.divider = mk64fract(f, TODFREQ);
+
+	iunlock(&tod);
+}
+
+/*
+ *  Set the time of day struct
+ */
+void
+todset(vlong t, vlong delta, int n)
+{
+	if(!tod.init)
+		todinit();
+
+	ilock(&tod);
+	if(t >= 0){
+		tod.off = t;
+		tod.last = fastticks(nil);
+		tod.lasttime = 0;
+		tod.delta = 0;
+		tod.sstart = tod.send;
+	} else {
+		if(n <= 0)
+			n = 1;
+		n *= HZ;
+		if(delta < 0 && n > -delta)
+			n = -delta;
+		if(delta > 0 && n > delta)
+			n = delta;
+		delta = delta/n;
+		tod.sstart = MACHP(0)->ticks;
+		tod.send = tod.sstart + n;
+		tod.delta = delta;
+	}
+	iunlock(&tod);
+}
+
+/*
+ *  get time of day
+ */
+vlong
+todget(vlong *ticksp)
+{
+	uvlong x;
+	vlong ticks, diff;
+	ulong t;
+
+	if(!tod.init)
+		todinit();
+
+	// we don't want time to pass twixt the measuring of fastticks
+	// and grabbing tod.last.  Also none of the vlongs are atomic so
+	// we have to look at them inside the lock.
+	ilock(&tod);
+	tod.cnt++;
+	ticks = fastticks(nil);
+
+	// add in correction
+	if(tod.sstart != tod.send){
+		t = MACHP(0)->ticks;
+		if(t >= tod.send)
+			t = tod.send;
+		tod.off = tod.off + tod.delta*(t - tod.sstart);
+		tod.sstart = t;
+	}
+
+	// convert to epoch
+	diff = ticks - tod.last;
+	if(diff < 0)
+		diff = 0;
+	mul64fract(&x, diff, tod.multiplier);
+	x += tod.off;
+
+	// time can't go backwards
+	if(x < tod.lasttime)
+		x = tod.lasttime;
+	else
+		tod.lasttime = x;
+
+	iunlock(&tod);
+
+	if(ticksp != nil)
+		*ticksp = ticks;
+
+	return x;
+}
+
+/*
+ *  convert time of day to ticks
+ */
+uvlong
+tod2fastticks(vlong ns)
+{
+	uvlong x;
+
+	ilock(&tod);
+	mul64fract(&x, ns-tod.off, tod.divider);
+	x += tod.last;
+	iunlock(&tod);
+	return x;
+}
+
+/*
+ *  called regularly to avoid calculation overflows
+ */
+void
+todfix(void)
+{
+	vlong ticks, diff;
+	uvlong x;
+
+	ticks = fastticks(nil);
+
+	diff = ticks - tod.last;
+	if(diff > tod.hz){
+		ilock(&tod);
+	
+		// convert to epoch
+		mul64fract(&x, diff, tod.multiplier);
+if(x > 30000000000ULL) print("todfix %llud\n", x);
+		x += tod.off;
+	
+		// protect against overflows
+		tod.last = ticks;
+		tod.off = x;
+	
+		iunlock(&tod);
+	}
+}
+
+long
+tseconds(void)
+{
+	vlong x;
+	int i;
+
+	x = todget(nil);
+	x = x/TODFREQ;
+	i = x;
+	return i;
+}
+
+//  convert milliseconds to fast ticks
+//
+uvlong
+ms2fastticks(ulong ms)
+{
+	if(!tod.init)
+		todinit();
+	return (tod.hz*ms)/1000ULL;
+}
+
+/*
+ *  convert nanoseconds to fast ticks
+ */
+uvlong
+ns2fastticks(uvlong ns)
+{
+	uvlong res;
+
+	if(!tod.init)
+		todinit();
+	mul64fract(&res, ns, tod.divider);
+	return res;
+}
+
+/*
+ *  convert fast ticks to ns
+ */
+uvlong
+fastticks2ns(uvlong ticks)
+{
+	uvlong res;
+
+	if(!tod.init)
+		todinit();
+	mul64fract(&res, ticks, tod.multiplier);
+	return res;
+}
+
+/*
+ * Make a 64 bit fixed point number that has a decimal point
+ * to the left of the low order 32 bits.  This is used with
+ * mul64fract for converting twixt nanoseconds and fastticks.
+ *
+ *	multiplier = (to<<32)/from
+ */
+uvlong
+mk64fract(uvlong to, uvlong from)
+{
+/*
+	int shift;
+
+	if(to == 0ULL)
+		return 0ULL;
+
+	shift = 0;
+	while(shift < 32 && to < (1ULL<<(32+24))){
+		to <<= 8;
+		shift += 8;
+	}
+	while(shift < 32 && to < (1ULL<<(32+31))){
+		to <<= 1;
+		shift += 1;
+	}
+
+	return (to/from)<<(32-shift);
+*/
+	return (to<<32)/from;
+}
--- /dev/null
+++ b/os/port/uart.h
@@ -1,0 +1,104 @@
+typedef struct PhysUart PhysUart;
+typedef struct Uart Uart;
+
+/*
+ *  routines to access UART hardware
+ */
+struct PhysUart
+{
+	char*	name;
+	Uart*	(*pnp)(void);
+	void	(*enable)(Uart*, int);
+	void	(*disable)(Uart*);
+	void	(*kick)(Uart*);
+	void	(*dobreak)(Uart*, int);
+	int	(*baud)(Uart*, int);
+	int	(*bits)(Uart*, int);
+	int	(*stop)(Uart*, int);
+	int	(*parity)(Uart*, int);
+	void	(*modemctl)(Uart*, int);
+	void	(*rts)(Uart*, int);
+	void	(*dtr)(Uart*, int);
+	long	(*status)(Uart*, void*, long, long);
+	void	(*fifo)(Uart*, int);
+	void	(*power)(Uart*, int);
+	int	(*getc)(Uart*);	/* polling versions, for iprint, rdb */
+	void	(*putc)(Uart*, int);
+};
+
+enum {
+	Stagesize=	1024
+};
+
+/*
+ *  software UART
+ */
+struct Uart
+{
+	void*	regs;			/* hardware stuff */
+	void*	saveregs;		/* place to put registers on power down */
+	char*	name;			/* internal name */
+	ulong	freq;			/* clock frequency */
+	int	bits;			/* bits per character */
+	int	stop;			/* stop bits */
+	int	parity;			/* even, odd or no parity */
+	int	baud;			/* baud rate */
+	PhysUart*phys;
+	int	console;		/* used as a serial console */
+	int	special;		/* internal kernel device */
+	Uart*	next;			/* list of allocated uarts */
+
+	QLock;
+	int	type;			/* ?? */
+	int	dev;
+	int	opens;
+
+	int	enabled;
+	Uart	*elist;			/* next enabled interface */
+
+	int	perr;			/* parity errors */
+	int	ferr;			/* framing errors */
+	int	oerr;			/* rcvr overruns */
+	int	berr;			/* no input buffers */
+	int	serr;			/* input queue overflow */
+
+	/* buffers */
+	int	(*putc)(Queue*, int);
+	Queue	*iq;
+	Queue	*oq;
+
+	Lock	rlock;
+	uchar	istage[Stagesize];
+	uchar	*iw;
+	uchar	*ir;
+	uchar	*ie;
+
+	Lock	tlock;			/* transmit */
+	uchar	ostage[Stagesize];
+	uchar	*op;
+	uchar	*oe;
+	int	drain;
+
+	int	modem;			/* hardware flow control on */
+	int	xonoff;			/* software flow control on */
+	int	blocked;
+	int	cts, dsr, dcd, dcdts;	/* keep track of modem status */ 
+	int	ctsbackoff;
+	int	hup_dsr, hup_dcd;	/* send hangup upstream? */
+	int	dohup;
+
+	Rendez	r;
+};
+
+extern Uart*	consuart;
+
+extern int	uartctl(Uart*, char*);
+extern int	uartgetc(void);
+extern void	uartkick(void*);
+extern void	uartmouse(Uart*, int (*)(Queue*, int), int);
+extern void	uartsetmouseputc(Uart*, int (*)(Queue*, int));
+extern void	uartputc(int);
+extern void	uartputs(char*, int);
+extern void	uartrecv(Uart*, char);
+extern Uart*	uartsetup(Uart*);
+extern int		uartstageoutput(Uart*);
--- /dev/null
+++ b/os/port/xalloc.c
@@ -1,0 +1,251 @@
+#include "u.h"
+#include "../port/lib.h"
+#include "mem.h"
+#include "dat.h"
+#include "fns.h"
+
+#define datoff		((ulong)((Xhdr*)0)->data)
+
+enum
+{
+	Chunk		= 64*1024,
+	Nhole		= 128,
+	Magichole	= 0xDeadBabe,
+};
+
+typedef struct Hole Hole;
+typedef struct Xalloc Xalloc;
+typedef struct Xhdr Xhdr;
+
+struct Hole
+{
+	ulong	addr;
+	ulong	size;
+	ulong	top;
+	Hole*	link;
+};
+
+struct Xhdr
+{
+	ulong	size;
+	ulong	magix;
+	char	data[1];
+};
+
+struct Xalloc
+{
+	Lock;
+	Hole	hole[Nhole];
+	Hole*	flist;
+	Hole*	table;
+};
+
+static Xalloc	xlists;
+
+static void
+ixprt()
+{
+	xsummary();
+	ixsummary();
+}
+
+void
+xinit(void)
+{
+	Hole *h, *eh;
+
+	eh = &xlists.hole[Nhole-1];
+	for(h = xlists.hole; h < eh; h++)
+		h->link = h+1;
+
+	xlists.flist = xlists.hole;
+
+	if(conf.npage1)
+		xhole(conf.base1, conf.npage1*BY2PG);
+	conf.npage1 = conf.base1+(conf.npage1*BY2PG);
+
+	if(conf.npage0)
+		xhole(conf.base0, conf.npage0*BY2PG);
+	conf.npage0 = conf.base0+(conf.npage0*BY2PG);
+
+	/* Save the bounds of kernel alloc memory for kernel mmu mapping */
+	conf.base0 = (ulong)KADDR(conf.base0);
+	conf.base1 = (ulong)KADDR(conf.base1);
+	conf.npage0 = (ulong)KADDR(conf.npage0);
+	conf.npage1 = (ulong)KADDR(conf.npage1);
+
+	debugkey('x', "xalloc/ialloc", ixprt, 0);
+}
+
+void*
+xspanalloc(ulong size, int align, ulong span)
+{
+	ulong a, v, t;
+
+	a = (ulong)xalloc(size+align+span);
+	if(a == 0)
+		panic("xspanalloc: %lud %d %lux\n", size, align, span);
+
+	if(span > 2) {
+		v = (a + span) & ~(span-1);
+		t = v - a;
+		if(t > 0)
+			xhole(PADDR(a), t);
+		t = a + span - v;
+		if(t > 0)
+			xhole(PADDR(v+size+align), t);
+	}
+	else
+		v = a;
+
+	if(align > 1)
+		v = (v + align) & ~(align-1);
+
+	return (void*)v;
+}
+
+void*
+xallocz(ulong size, int zero)
+{
+	Xhdr *p;
+	Hole *h, **l;
+
+	size += BY2V + sizeof(Xhdr);
+	size &= ~(BY2V-1);
+
+	ilock(&xlists);
+	l = &xlists.table;
+	for(h = *l; h; h = h->link) {
+		if(h->size >= size) {
+			p = (Xhdr*)h->addr;
+			h->addr += size;
+			h->size -= size;
+			if(h->size == 0) {
+				*l = h->link;
+				h->link = xlists.flist;
+				xlists.flist = h;
+			}
+			iunlock(&xlists);
+			p = KADDR(p);
+			p->magix = Magichole;
+			p->size = size;
+			if(zero)
+				memset(p->data, 0, size);
+			return p->data;
+		}
+		l = &h->link;
+	}
+	iunlock(&xlists);
+	return nil;
+}
+
+void*
+xalloc(ulong size)
+{
+	return xallocz(size, 1);
+}
+
+void
+xfree(void *p)
+{
+	Xhdr *x;
+
+	x = (Xhdr*)((ulong)p - datoff);
+	if(x->magix != Magichole) {
+		xsummary();
+		panic("xfree(0x%lux) 0x%lux!=0x%lux", p, (ulong)Magichole, x->magix);
+	}
+	xhole(PADDR(x), x->size);
+}
+
+int
+xmerge(void *vp, void *vq)
+{
+	Xhdr *p, *q;
+
+	p = (Xhdr*)(((ulong)vp - offsetof(Xhdr, data[0])));
+	q = (Xhdr*)(((ulong)vq - offsetof(Xhdr, data[0])));
+	if(p->magix != Magichole || q->magix != Magichole) {
+		xsummary();
+		panic("xmerge(%#p, %#p) bad magic %#lux, %#lux\n",
+			vp, vq, p->magix, q->magix);
+	}
+	if((uchar*)p+p->size == (uchar*)q) {
+		p->size += q->size;
+		return 1;
+	}
+	return 0;
+}
+
+void
+xhole(ulong addr, ulong size)
+{
+	ulong top;
+	Hole *h, *c, **l;
+
+	if(size == 0)
+		return;
+
+	top = addr + size;
+	ilock(&xlists);
+	l = &xlists.table;
+	for(h = *l; h; h = h->link) {
+		if(h->top == addr) {
+			h->size += size;
+			h->top = h->addr+h->size;
+			c = h->link;
+			if(c && h->top == c->addr) {
+				h->top += c->size;
+				h->size += c->size;
+				h->link = c->link;
+				c->link = xlists.flist;
+				xlists.flist = c;
+			}
+			iunlock(&xlists);
+			return;
+		}
+		if(h->addr > addr)
+			break;
+		l = &h->link;
+	}
+	if(h && top == h->addr) {
+		h->addr -= size;
+		h->size += size;
+		iunlock(&xlists);
+		return;
+	}
+
+	if(xlists.flist == nil) {
+		iunlock(&xlists);
+		print("xfree: no free holes, leaked %lud bytes\n", size);
+		return;
+	}
+
+	h = xlists.flist;
+	xlists.flist = h->link;
+	h->addr = addr;
+	h->top = top;
+	h->size = size;
+	h->link = *l;
+	*l = h;
+	iunlock(&xlists);
+}
+
+void
+xsummary(void)
+{
+	int i;
+	Hole *h;
+
+	i = 0;
+	for(h = xlists.flist; h; h = h->link)
+		i++;
+
+	print("%d holes free\n", i);
+	i = 0;
+	for(h = xlists.table; h; h = h->link) {
+		print("%.8lux %.8lux %lud\n", h->addr, h->top, h->size);
+		i += h->size;
+	}
+	print("%d bytes free\n", i);
+}
--- /dev/null
+++ b/os/pxa/NOTICE
@@ -1,0 +1,2 @@
+Inferno® Copyright © 1996-1999 Lucent Technologies Inc.  All rights reserved.
+PXA 25x Inferno port Copyright © 2000,2003,2004 Vita Nuova Holdings Limited.  All rights reserved.
--- /dev/null
+++ b/os/pxa/clock.c
@@ -1,0 +1,318 @@
+#include "u.h"
+#include "../port/lib.h"
+#include "mem.h"
+#include "dat.h"
+#include "fns.h"
+#include "io.h"
+
+#include "ureg.h"
+
+static ulong timer_incr[4] = { 0, 0, 0, -1 };
+
+typedef struct Clock0link Clock0link;
+typedef struct Clock0link {
+	void		(*clock)(void);
+	Clock0link*	link;
+} Clock0link;
+
+static Clock0link *clock0link;
+static Lock clock0lock;
+static void (*prof_fcn)(Ureg *, int);
+
+Timer*
+addclock0link(void (*clock)(void), int)
+{
+	Clock0link *lp;
+
+	if((lp = malloc(sizeof(Clock0link))) == 0){
+		print("addclock0link: too many links\n");
+		return nil;
+	}
+	ilock(&clock0lock);
+	lp->clock = clock;
+	lp->link = clock0link;
+	clock0link = lp;
+	iunlock(&clock0lock);
+	return nil;
+}
+
+static void
+profintr(Ureg *ur, void*)
+{
+	OstmrReg *ost = OSTMRREG;
+	int t;
+
+	if((ost->osmr[3] - ost->oscr) < 2*CLOCKFREQ) {
+		/* less than 2 seconds before reset, say something */
+		setpanic();
+		clockpoll();
+		dumpregs(ur);
+		panic("Watchdog timer will expire");
+	}
+
+	/* advance the profile clock tick */
+	ost->osmr[2] += timer_incr[2];
+	ost->ossr = (1 << 2); 			/* Clear the SR */
+	t = 1;
+	while((ost->osmr[2] - ost->oscr) > 0x80000000) {
+		ost->osmr[2] += timer_incr[2];
+		t++;
+	}
+	if(prof_fcn)
+		prof_fcn(ur, t);
+}
+
+static void
+clockintr(Ureg*, void*)
+{
+	Clock0link *lp;
+	int losttick = 0;
+	OstmrReg *ost = OSTMRREG;
+
+{static int tag, led; if(++tag >= HZ){ledset(led ^= 1);tag=0;}}
+	m->ticks++;
+//	ost->osmr[3] = ost->oscr + timer_incr[3]; /* restart the watchdog */
+	ost->osmr[0] += timer_incr[0];		/* advance the clock tick */
+	ost->ossr = (1<<0); 			/* Clear the SR */
+
+	while((ost->osmr[0] - ost->oscr) >= 0x80000000) {
+		ost->osmr[0] += timer_incr[0]; 
+		losttick++;
+		m->ticks++;
+	}
+
+	checkalarms();
+
+	if(canlock(&clock0lock)){
+		for(lp = clock0link; lp; lp = lp->link)
+			if(lp->clock)
+				lp->clock();
+		unlock(&clock0lock);
+	}
+
+	/* round robin time slice is done by trap.c and proc.c */
+}
+
+void
+timerenable( int timer, int Hz, void (*f)(Ureg *, void*), void* a)
+{
+	OstmrReg *ost = OSTMRREG;
+	char name[KNAMELEN];
+
+	if(timer < 0 || timer > 3)
+		return;
+	timer_incr[timer] = CLOCKFREQ/Hz;		/* set up freq */
+	ost->osmr[timer] = ost->oscr+timer_incr[timer];
+	snprint(name, sizeof(name), "timer%d", timer);
+	intrenable(IRQ, IRQtimer0+timer, f, a, name);
+	ost->ossr = (1 << timer);	/* clear any pending interrupt */
+	ost->oier |= (1 << timer);		/* enable interrupt */
+}
+
+void
+timerdisable( int timer )
+{
+	OstmrReg *ost = OSTMRREG;
+
+	if(timer < 0 || timer > 3)
+		return;
+	ost->osmr[timer] = 0;				/* clear freq */
+	ost->oier &= ~(1 << timer);			/* disable interrupt */
+}
+
+void
+installprof(void (*pf)(Ureg *, int))
+{
+	int s;
+
+	s = splfhi();
+	prof_fcn = pf;
+	timerenable( 2, HZ+1, profintr, 0);
+	timer_incr[2] = timer_incr[0]+63;	/* fine tuning */
+	splx(s);
+}
+
+void
+clockinit(void)
+{
+	OstmrReg *ost = OSTMRREG;
+	m->ticks = 0;
+	/* Set up timer registers */
+	ost->ossr = 0xf;	/* clear all four OSSR trigger bits */
+	ost->oier = 0;
+	ost->osmr[0] = 0;
+	ost->osmr[1] = 0;
+	ost->osmr[2] = 0;
+	//	ost->osmr[3] = 0;
+	timerenable( 0, HZ, clockintr, 0);
+//	timer_incr[3] = CLOCKFREQ*10;	/* 10 second watchdog */
+//	timer_setwatchdog(timer_incr[3]);
+//	timerenable( 2, 1, profintr, 0);	/* watch the watchdog */
+}
+
+void
+clockpoll(void)
+{
+	OstmrReg *ost = OSTMRREG;
+//	ost->osmr[3] = ost->oscr + timer_incr[3];	/* restart the watchdog */
+}
+
+void
+clockcheck(void)
+{
+	OstmrReg *ost = OSTMRREG;
+
+	if((ost->osmr[3] - ost->oscr) < CLOCKFREQ) {
+		setpanic();
+		clockpoll();
+		dumpstack();
+		panic("Watchdog timer will expire");
+	}
+}
+
+// macros for fixed-point math
+
+ulong _mularsv(ulong m0, ulong m1, ulong a, ulong s);
+
+/* truncated: */
+#define FXDPTDIV(a,b,n) ((ulong)(((uvlong)(a) << (n)) / (b)))
+#define MAXMUL(a,n)     ((ulong)((((uvlong)1<<(n))-1)/(a)))
+#define MULDIV(x,a,b,n) (((x)*FXDPTDIV(a,b,n)) >> (n)) 
+#define MULDIV64(x,a,b,n) ((ulong)_mularsv(x, FXDPTDIV(a,b,n), 0, (n)))
+
+/* rounded: */
+#define FXDPTDIVR(a,b,n) ((ulong)((((uvlong)(a) << (n))+((b)/2)) / (b)))
+#define MAXMULR(a,n)     ((ulong)((((uvlong)1<<(n))-1)/(a)))
+#define MULDIVR(x,a,b,n) (((x)*FXDPTDIVR(a,b,n)+(1<<((n)-1))) >> (n)) 
+#define MULDIVR64(x,a,b,n) ((ulong)_mularsv(x, FXDPTDIVR(a,b,n), 1<<((n)-1), (n)))
+
+
+// these routines are all limited to a maximum of 1165 seconds,
+// due to the wrap-around of the OSTIMER
+
+ulong
+timer_start(void)
+{
+	return OSTMRREG->oscr;
+}
+
+ulong
+timer_ticks(ulong t0)
+{
+	return OSTMRREG->oscr - t0;
+}
+
+int
+timer_devwait(ulong *adr, ulong mask, ulong val, int ost)
+{
+	int i;
+	ulong t0 = timer_start();
+	while((*adr & mask) != val) 
+		if(timer_ticks(t0) > ost)
+			return ((*adr & mask) == val) ? 0 : -1;
+		else
+			for(i = 0; i < 10; i++);	/* don't pound OSCR too hard! (why not?) */
+	return 0;
+}
+
+void
+timer_setwatchdog(int t)
+{
+	OstmrReg *ost = OSTMRREG;
+	ost->osmr[3] = ost->oscr + t;
+	if(t) {
+		ost->ossr = (1<<3);
+		ost->oier |= (1<<3);
+		ost->ower = 1;
+	} else 
+		ost->oier &= ~(1<<3);
+}
+
+void
+timer_delay(int t)
+{	
+	ulong t0 = timer_start();
+	while(timer_ticks(t0) < t)
+		;
+}
+
+
+ulong
+us2tmr(int us)
+{
+	return MULDIV64(us, CLOCKFREQ, 1000000, 24);
+}
+
+int
+tmr2us(ulong t)
+{
+	return MULDIV64(t, 1000000, CLOCKFREQ, 24);
+}
+
+void
+microdelay(int us)
+{
+	ulong t0 = timer_start();
+	ulong t = us2tmr(us);
+	while(timer_ticks(t0) <= t)
+		;
+}
+
+ulong
+ms2tmr(int ms)
+{
+	return MULDIV64(ms, CLOCKFREQ, 1000, 20);
+}
+
+int
+tmr2ms(ulong t)
+{
+	return MULDIV64(t, 1000, CLOCKFREQ, 32);
+}
+
+void
+delay(int ms)
+{
+	ulong t0 = timer_start();
+	ulong t = ms2tmr(ms);
+	while(timer_ticks(t0) <= t)
+		clockpoll();
+}
+
+/*
+ * for devbench.c
+ */
+vlong
+archrdtsc(void)
+{
+	return OSTMRREG->oscr;
+}
+
+ulong
+archrdtsc32(void)
+{
+	return OSTMRREG->oscr;
+}
+
+/*
+ * for devkprof.c
+ */
+long
+archkprofmicrosecondspertick(void)
+{
+	return MS2HZ*1000;
+}
+
+void
+archkprofenable(int)
+{
+	/* TO DO */
+}
+
+uvlong
+fastticks(uvlong *hz)
+{
+	if(hz)
+		*hz = HZ;
+	return m->ticks;
+}
--- /dev/null
+++ b/os/pxa/devether.c
@@ -1,0 +1,617 @@
+#include "u.h"
+#include "../port/lib.h"
+#include "mem.h"
+#include "dat.h"
+#include "fns.h"
+#include "io.h"
+#include "ureg.h"
+#include "../port/error.h"
+#include "../port/netif.h"
+
+#include "etherif.h"
+
+static Ether *etherxx[MaxEther];
+
+Chan*
+etherattach(char* spec)
+{
+	ulong ctlrno;
+	char *p;
+	Chan *chan;
+	Ether *ether;
+
+	ctlrno = 0;
+	if(spec && *spec){
+		ctlrno = strtoul(spec, &p, 0);
+		if((ctlrno == 0 && p == spec) || *p || (ctlrno >= MaxEther))
+			error(Ebadarg);
+	}
+	if((ether = etherxx[ctlrno]) == 0)
+		error(Enodev);
+	rlock(ether);
+	if(waserror()){
+		runlock(ether);
+		nexterror();
+	}
+	chan = devattach('l', spec);
+	chan->dev = ctlrno;
+	if(ether->attach)
+		ether->attach(etherxx[ctlrno]);
+	poperror();
+	runlock(ether);
+	return chan;
+}
+
+static void
+ethershutdown(void)
+{
+	Ether *ether;
+	int i;
+
+	for(i=0; i<MaxEther; i++){
+		ether = etherxx[i];
+		if(ether != nil && ether->detach != nil)
+			ether->detach(ether);
+	}
+}
+
+static Walkqid*
+etherwalk(Chan* chan, Chan *nchan, char **name, int nname)
+{
+	Walkqid *wq;
+	Ether *ether;
+
+	ether = etherxx[chan->dev];
+	rlock(ether);
+	if(waserror()) {
+		runlock(ether);
+		nexterror();
+	}
+	wq = netifwalk(etherxx[chan->dev], chan, nchan, name, nname);
+	poperror();
+	runlock(ether);
+	return wq;
+}
+
+static int
+etherstat(Chan* chan, uchar* dp, int n)
+{
+	int s;
+	Ether *ether;
+
+	ether = etherxx[chan->dev];
+	rlock(ether);
+	if(waserror()) {
+		runlock(ether);
+		nexterror();
+	}
+	s = netifstat(ether, chan, dp, n);
+	poperror();
+	runlock(ether);
+	return s;
+}
+
+static Chan*
+etheropen(Chan* chan, int omode)
+{
+	Chan *c;
+	Ether *ether;
+
+	ether = etherxx[chan->dev];
+	rlock(ether);
+	if(waserror()) {
+		runlock(ether);
+		nexterror();
+	}
+	c = netifopen(ether, chan, omode);
+	poperror();
+	runlock(ether);
+	return c;
+}
+
+static void
+ethercreate(Chan*, char*, int, ulong)
+{
+}
+
+static void
+etherclose(Chan* chan)
+{
+	Ether *ether;
+
+	ether = etherxx[chan->dev];
+	rlock(ether);
+	if(waserror()) {
+		runlock(ether);
+		nexterror();
+	}
+	netifclose(ether, chan);
+	poperror();
+	runlock(ether);
+}
+
+static long
+etherread(Chan* chan, void* buf, long n, vlong off)
+{
+	Ether *ether;
+	ulong offset = off;
+	long r;
+
+	ether = etherxx[chan->dev];
+	rlock(ether);
+	if(waserror()) {
+		runlock(ether);
+		nexterror();
+	}
+	if((chan->qid.type & QTDIR) == 0 && ether->ifstat){
+		/*
+		 * With some controllers it is necessary to reach
+		 * into the chip to extract statistics.
+		 */
+		if(NETTYPE(chan->qid.path) == Nifstatqid){
+			r = ether->ifstat(ether, buf, n, offset);
+			goto out;
+		}
+		if(NETTYPE(chan->qid.path) == Nstatqid)
+			ether->ifstat(ether, buf, 0, offset);
+	}
+	r = netifread(ether, chan, buf, n, offset);
+out:
+	poperror();
+	runlock(ether);
+	return r;
+}
+
+static Block*
+etherbread(Chan* chan, long n, ulong offset)
+{
+	Block *b;
+	Ether *ether;
+
+	ether = etherxx[chan->dev];
+	rlock(ether);
+	if(waserror()) {
+		runlock(ether);
+		nexterror();
+	}
+	b = netifbread(ether, chan, n, offset);
+	poperror();
+	runlock(ether);
+	return b;
+}
+
+static int
+etherwstat(Chan* chan, uchar* dp, int n)
+{
+	Ether *ether;
+	int r;
+
+	ether = etherxx[chan->dev];
+	rlock(ether);
+	if(waserror()) {
+		runlock(ether);
+		nexterror();
+	}
+	r = netifwstat(ether, chan, dp, n);
+	poperror();
+	runlock(ether);
+	return r;
+}
+
+static void
+etherrtrace(Netfile* f, Etherpkt* pkt, int len)
+{
+	int i, n;
+	Block *bp;
+
+	if(qwindow(f->in) <= 0)
+		return;
+	if(len > 58)
+		n = 58;
+	else
+		n = len;
+	bp = iallocb(64);
+	if(bp == nil)
+		return;
+	memmove(bp->wp, pkt->d, n);
+	i = TK2MS(MACHP(0)->ticks);
+	bp->wp[58] = len>>8;
+	bp->wp[59] = len;
+	bp->wp[60] = i>>24;
+	bp->wp[61] = i>>16;
+	bp->wp[62] = i>>8;
+	bp->wp[63] = i;
+	bp->wp += 64;
+	qpass(f->in, bp);
+}
+
+Block*
+etheriq(Ether* ether, Block* bp, int fromwire)
+{
+	Etherpkt *pkt;
+	ushort type;
+	int len, multi, tome, fromme;
+	Netfile **ep, *f, **fp, *fx;
+	Block *xbp;
+
+	ether->inpackets++;
+
+	pkt = (Etherpkt*)bp->rp;
+	len = BLEN(bp);
+	type = (pkt->type[0]<<8)|pkt->type[1];
+	fx = 0;
+	ep = &ether->f[Ntypes];
+
+	multi = pkt->d[0] & 1;
+	/* check for valid multcast addresses */
+	if(multi && memcmp(pkt->d, ether->bcast, sizeof(pkt->d)) != 0 && ether->prom == 0){
+		if(!activemulti(ether, pkt->d, sizeof(pkt->d))){
+			if(fromwire){
+				freeb(bp);
+				bp = 0;
+			}
+			return bp;
+		}
+	}
+
+	/* is it for me? */
+	tome = memcmp(pkt->d, ether->ea, sizeof(pkt->d)) == 0;
+	fromme = memcmp(pkt->s, ether->ea, sizeof(pkt->s)) == 0;
+
+	/*
+	 * Multiplex the packet to all the connections which want it.
+	 * If the packet is not to be used subsequently (fromwire != 0),
+	 * attempt to simply pass it into one of the connections, thereby
+	 * saving a copy of the data (usual case hopefully).
+	 */
+	for(fp = ether->f; fp < ep; fp++){
+		if((f = *fp) && (f->type == type || f->type < 0))
+		if(tome || multi || f->prom){
+			/* Don't want to hear bridged packets */
+			if(f->bridge && !fromwire && !fromme)
+				continue;
+			if(!f->headersonly){
+				if(fromwire && fx == 0)
+					fx = f;
+				else if(xbp = iallocb(len)){
+					memmove(xbp->wp, pkt, len);
+					xbp->wp += len;
+					if(qpass(f->in, xbp) < 0)
+						ether->soverflows++;
+				}
+				else
+					ether->soverflows++;
+			}
+			else
+				etherrtrace(f, pkt, len);
+		}
+	}
+
+	if(fx){
+		if(qpass(fx->in, bp) < 0)
+			ether->soverflows++;
+		return 0;
+	}
+	if(fromwire){
+		freeb(bp);
+		return 0;
+	}
+
+	return bp;
+}
+
+static int
+etheroq(Ether* ether, Block* bp)
+{
+	int len, loopback, s;
+	Etherpkt *pkt;
+
+	ether->outpackets++;
+
+	/*
+	 * Check if the packet has to be placed back onto the input queue,
+	 * i.e. if it's a loopback or broadcast packet or the interface is
+	 * in promiscuous mode.
+	 * If it's a loopback packet indicate to etheriq that the data isn't
+	 * needed and return, etheriq will pass-on or free the block.
+	 * To enable bridging to work, only packets that were originated
+	 * by this interface are fed back.
+	 */
+	pkt = (Etherpkt*)bp->rp;
+	len = BLEN(bp);
+	loopback = memcmp(pkt->d, ether->ea, sizeof(pkt->d)) == 0;
+	if(loopback || memcmp(pkt->d, ether->bcast, sizeof(pkt->d)) == 0 || ether->prom){
+		s = splhi();
+		etheriq(ether, bp, 0);
+		splx(s);
+	}
+
+	if(!loopback){
+		qbwrite(ether->oq, bp);
+		if(ether->transmit != nil)
+			ether->transmit(ether);
+	}else
+		freeb(bp);
+
+	return len;
+}
+
+static long
+etherwrite(Chan* chan, void* buf, long n, vlong)
+{
+	Ether *ether;
+	Block *bp;
+	int onoff;
+	Cmdbuf *cb;
+	long l;
+
+	ether = etherxx[chan->dev];
+	rlock(ether);
+	if(waserror()) {
+		runlock(ether);
+		nexterror();
+	}
+	if(NETTYPE(chan->qid.path) != Ndataqid) {
+		l = netifwrite(ether, chan, buf, n);
+		if(l >= 0)
+			goto out;
+		cb = parsecmd(buf, n);
+		if(strcmp(cb->f[0], "nonblocking") == 0){
+			if(cb->nf <= 1)
+				onoff = 1;
+			else
+				onoff = atoi(cb->f[1]);
+			qnoblock(ether->oq, onoff);
+			free(cb);
+			goto out;
+		}
+		free(cb);
+		if(ether->ctl!=nil){
+			l = ether->ctl(ether,buf,n);
+			goto out;
+		}
+		error(Ebadctl);
+	}
+
+	if(n > ether->maxmtu)
+		error(Etoobig);
+	if(n < ether->minmtu)
+		error(Etoosmall);
+	bp = allocb(n);
+	if(waserror()){
+		freeb(bp);
+		nexterror();
+	}
+	memmove(bp->rp, buf, n);
+	memmove(bp->rp+Eaddrlen, ether->ea, Eaddrlen);
+	bp->wp += n;
+	poperror();
+
+	l = etheroq(ether, bp);
+out:
+	poperror();
+	runlock(ether);
+	return l;
+}
+
+static long
+etherbwrite(Chan* chan, Block* bp, ulong)
+{
+	Ether *ether;
+	long n;
+
+	n = BLEN(bp);
+	if(NETTYPE(chan->qid.path) != Ndataqid){
+		if(waserror()) {
+			freeb(bp);
+			nexterror();
+		}
+		n = etherwrite(chan, bp->rp, n, 0);
+		poperror();
+		freeb(bp);
+		return n;
+	}
+	ether = etherxx[chan->dev];
+	rlock(ether);
+	if(waserror()) {
+		runlock(ether);
+		nexterror();
+	}
+	if(n > ether->maxmtu){
+		freeb(bp);
+		error(Etoobig);
+	}
+	if(n < ether->minmtu){
+		freeb(bp);
+		error(Etoosmall);
+	}
+	n = etheroq(ether, bp);
+	poperror();
+	runlock(ether);
+	return n;
+}
+
+static struct {
+	char*	type;
+	int	(*reset)(Ether*);
+} cards[MaxEther+1];
+
+void
+addethercard(char* t, int (*r)(Ether*))
+{
+	static int ncard;
+
+	if(ncard == MaxEther)
+		panic("too many ether cards");
+	cards[ncard].type = t;
+	cards[ncard].reset = r;
+	ncard++;
+}
+
+int
+parseether(uchar *to, char *from)
+{
+	char nip[4];
+	char *p;
+	int i;
+
+	p = from;
+	for(i = 0; i < Eaddrlen; i++){
+		if(*p == 0)
+			return -1;
+		nip[0] = *p++;
+		if(*p == 0)
+			return -1;
+		nip[1] = *p++;
+		nip[2] = 0;
+		to[i] = strtoul(nip, 0, 16);
+		if(*p == ':')
+			p++;
+	}
+	return 0;
+}
+
+static void
+etherreset(void)
+{
+	Ether *ether;
+	int i, n, ctlrno;
+	char name[KNAMELEN], buf[128];
+
+	for(ether = 0, ctlrno = 0; ctlrno < MaxEther; ctlrno++){
+		if(ether == 0)
+			ether = malloc(sizeof(Ether));
+		memset(ether, 0, sizeof(Ether));
+		ether->ctlrno = ctlrno;
+		ether->mbps = 10;
+		ether->minmtu = ETHERMINTU;
+		ether->maxmtu = ETHERMAXTU;
+		ether->itype = -1;
+
+		if(archether(ctlrno, ether) <= 0)
+			continue;
+
+		for(n = 0; cards[n].type; n++){
+			if(cistrcmp(cards[n].type, ether->type))
+				continue;
+			for(i = 0; i < ether->nopt; i++){
+				if(cistrncmp(ether->opt[i], "ea=", 3) == 0){
+					if(parseether(ether->ea, &ether->opt[i][3]) == -1)
+						memset(ether->ea, 0, Eaddrlen);
+				}else if(cistrcmp(ether->opt[i], "fullduplex") == 0 ||
+					cistrcmp(ether->opt[i], "10BASE-TFD") == 0)
+					ether->fullduplex = 1;
+				else if(cistrcmp(ether->opt[i], "100BASE-TXFD") == 0)
+					ether->mbps = 100;
+			}
+			if(cards[n].reset(ether))
+				break;
+			snprint(name, sizeof(name), "ether%d", ctlrno);
+
+			if(ether->interrupt != nil)
+				intrenable(ether->itype, ether->irq, ether->interrupt, ether, name);
+
+			i = sprint(buf, "#l%d: %s: %dMbps port 0x%luX irq %lud",
+				ctlrno, ether->type, ether->mbps, ether->port, ether->irq);
+			if(ether->mem)
+				i += sprint(buf+i, " addr 0x%luX", PADDR(ether->mem));
+			if(ether->size)
+				i += sprint(buf+i, " size 0x%luX", ether->size);
+			i += sprint(buf+i, ": %2.2uX%2.2uX%2.2uX%2.2uX%2.2uX%2.2uX",
+				ether->ea[0], ether->ea[1], ether->ea[2],
+				ether->ea[3], ether->ea[4], ether->ea[5]);
+			sprint(buf+i, "\n");
+			iprint(buf);
+
+			if(ether->mbps == 100){
+				netifinit(ether, name, Ntypes, 256*1024);
+				if(ether->oq == 0)
+					ether->oq = qopen(256*1024, Qmsg, 0, 0);
+			}
+			else{
+				netifinit(ether, name, Ntypes, 64*1024);
+				if(ether->oq == 0)
+					ether->oq = qopen(64*1024, Qmsg, 0, 0);
+			}
+			if(ether->oq == 0)
+				panic("etherreset %s", name);
+			ether->alen = Eaddrlen;
+			memmove(ether->addr, ether->ea, Eaddrlen);
+			memset(ether->bcast, 0xFF, Eaddrlen);
+
+			etherxx[ctlrno] = ether;
+			ether = 0;
+			break;
+		}
+	}
+	if(ether)
+		free(ether);
+}
+
+static void
+etherpower(int on)
+{
+	int i;
+	Ether *ether;
+
+	for(i = 0; i < MaxEther; i++){
+		if((ether = etherxx[i]) == nil || ether->power == nil)
+			continue;
+		if(on){
+			if(canrlock(ether))
+				continue;
+			if(ether->power != nil)
+				ether->power(ether, on);
+			wunlock(ether);
+		}else{
+			if(!canrlock(ether))
+				continue;
+			wlock(ether);
+			if(ether->power != nil)
+				ether->power(ether, on);
+			/* Keep locked until power goes back on */
+		}
+	}
+}
+
+#define POLY 0xedb88320
+
+/* really slow 32 bit crc for ethers */
+ulong
+ethercrc(uchar *p, int len)
+{
+	int i, j;
+	ulong crc, b;
+
+	crc = 0xffffffff;
+	for(i = 0; i < len; i++){
+		b = *p++;
+		for(j = 0; j < 8; j++){
+			crc = (crc>>1) ^ (((crc^b) & 1) ? POLY : 0);
+			b >>= 1;
+		}
+	}
+	return crc;
+}
+
+Dev etherdevtab = {
+	'l',
+	"ether",
+
+	etherreset,
+	devinit,
+	ethershutdown,
+	etherattach,
+	etherwalk,
+	etherstat,
+	etheropen,
+	ethercreate,
+	etherclose,
+	etherread,
+	etherbread,
+	etherwrite,
+	etherbwrite,
+	devremove,
+	etherwstat,
+	etherpower,
+};
--- /dev/null
+++ b/os/pxa/devrtc.c
@@ -1,0 +1,169 @@
+#include	"u.h"
+#include	"../port/lib.h"
+#include	"mem.h"
+#include	"dat.h"
+#include	"fns.h"
+#include	"../port/error.h"
+
+#include	"io.h"
+
+/*
+ * SA11x0 real time clock
+ *	TO DO: alarms, wakeup, allow trim setting(?)
+ */
+
+enum{
+	Qdir,
+	Qrtc,
+	Qrtctrim,
+};
+
+static Dirtab rtcdir[]={
+	".",		{Qdir,0,QTDIR},	0,	0555,
+	"rtc",		{Qrtc},	NUMSIZE,	0664,
+	"rtctrim",	{Qrtctrim},	0,	0664,
+};
+#define	NRTC	(sizeof(rtcdir)/sizeof(rtcdir[0]))
+
+extern ulong boottime;
+
+enum {
+	RTSR_al=	1<<0,	/* RTC alarm detected */
+	RTSR_hz=	1<<1,	/* 1-Hz rising-edge detected */
+	RTSR_ale=	1<<2,	/* RTC alarm interrupt enabled */
+	RTSR_hze=	1<<3,	/* 1-Hz interrupt enable */
+};
+
+static void
+rtcreset(void)
+{
+	RTCreg *r;
+
+	r = RTCREG;
+	if((r->rttr & 0xFFFF) == 0){	/* reset state */
+		r->rttr = 32768-1;
+		r->rcnr = boottime;	/* typically zero */
+	}
+	r->rtar = ~0;
+	r->rtsr = RTSR_al | RTSR_hz;
+}
+
+static Chan*
+rtcattach(char *spec)
+{
+	return devattach('r', spec);
+}
+
+static Walkqid*
+rtcwalk(Chan *c, Chan *nc, char **name, int nname)
+{
+	return devwalk(c, nc, name, nname, rtcdir, NRTC, devgen);
+}
+
+static int
+rtcstat(Chan *c, uchar *dp, int n)
+{
+	return devstat(c, dp, n, rtcdir, NRTC, devgen);
+}
+
+static Chan*
+rtcopen(Chan *c, int omode)
+{
+	return devopen(c, omode, rtcdir, NRTC, devgen);
+}
+
+static void	 
+rtcclose(Chan*)
+{
+}
+
+static long	 
+rtcread(Chan *c, void *buf, long n, vlong off)
+{
+	if(c->qid.type & QTDIR)
+		return devdirread(c, buf, n, rtcdir, NRTC, devgen);
+
+	switch((ulong)c->qid.path){
+	case Qrtc:
+		return readnum(off, buf, n, RTCREG->rcnr, NUMSIZE);
+	case Qrtctrim:
+		return readnum(off, buf, n, RTCREG->rttr, NUMSIZE);
+	}
+	error(Egreg);
+	return 0;		/* not reached */
+}
+
+static long	 
+rtcwrite(Chan *c, void *buf, long n, vlong off)
+{
+	ulong offset = off;
+	ulong secs;
+	char *cp, sbuf[32];
+
+	switch((ulong)c->qid.path){
+	case Qrtc:
+		/*
+		 *  write the time
+		 */
+		if(offset != 0 || n >= sizeof(sbuf)-1)
+			error(Ebadarg);
+		memmove(sbuf, buf, n);
+		sbuf[n] = '\0';
+		cp = sbuf;
+		while(*cp){
+			if(*cp>='0' && *cp<='9')
+				break;
+			cp++;
+		}
+		secs = strtoul(cp, 0, 0);
+		RTCREG->rcnr = secs;
+		return n;
+
+	case Qrtctrim:
+		if(offset != 0 || n >= sizeof(sbuf)-1)
+			error(Ebadarg);
+		memmove(sbuf, buf, n);
+		sbuf[n] = '\0';
+		RTCREG->rttr = strtoul(sbuf, 0, 0);
+		return n;
+	}
+	error(Egreg);
+	return 0;		/* not reached */
+}
+
+static void
+rtcpower(int on)
+{
+	if(on)
+		boottime = RTCREG->rcnr - TK2SEC(MACHP(0)->ticks);
+	else
+		RTCREG->rcnr = seconds();
+}
+
+long
+rtctime(void)
+{
+	return RTCREG->rcnr;
+}
+
+Dev rtcdevtab = {
+	'r',
+	"rtc",
+
+	rtcreset,
+	devinit,
+	devshutdown,
+	rtcattach,
+	rtcwalk,
+	rtcstat,
+	rtcopen,
+	devcreate,
+	rtcclose,
+	rtcread,
+	devbread,
+	rtcwrite,
+	devbwrite,
+	devremove,
+	devwstat,
+	rtcpower,
+};
--- /dev/null
+++ b/os/pxa/devuart.c
@@ -1,0 +1,1073 @@
+#include	"u.h"
+#include	"../port/lib.h"
+#include	"mem.h"
+#include	"dat.h"
+#include	"fns.h"
+#include	"io.h"
+#include	"../port/error.h"
+
+#include	"../port/netif.h"
+
+/*
+ *  Driver for the uart.
+ *	TO DO: replace by uartpxa.c
+ */
+enum
+{
+	/*
+	 *  register numbers
+	 */
+	Data=	0,		/* xmit/rcv buffer */
+	Iena=	1,		/* interrupt enable */
+	 Ircv=	(1<<0),		/*  for char rcv'd */
+	 Ixmt=	(1<<1),		/*  for xmit buffer empty */
+	 Irstat=(1<<2),		/*  for change in rcv'er status */
+	 Imstat=(1<<3),		/*  for change in modem status */
+	 Rtoie=	1<<4,	/* character timeout indication */
+	 Nrze=	1<<5,	/* NRZ encoding enabled */
+	 Uue=	1<<6,	/* Uart unit enabled */
+	 Dmae=	1<<7,	/* DMA requests enabled */
+	Istat=	2,		/* interrupt flag (read) */
+	 Ipend=	1,		/* interrupt pending (not) */
+	 Fenabd=(3<<6),   		/*  on if fifo's enabled */
+	Fifoctl=2,		/* fifo control (write) */
+	 Fena=	(1<<0),		/*  enable xmit/rcv fifos */
+	 Fdma=	(1<<3),		/* dma on */
+	 Ftrig=	(1<<6),		/*  trigger after 4 input characters */
+	 Fclear=(3<<1),		/*  clear xmit & rcv fifos */
+	Format=	3,		/* byte format */
+	 Bits8=	(3<<0),		/*  8 bits/byte */
+	 Stop2=	(1<<2),		/*  2 stop bits */
+	 Pena=	(1<<3),		/*  generate parity */
+	 Peven=	(1<<4),		/*  even parity */
+	 Pforce=(1<<5),		/*  force parity */
+	 Break=	(1<<6),		/*  generate a break */
+	 Dra=	(1<<7),		/*  address the divisor */
+	Mctl=	4,		/* modem control */
+	 Dtr=	(1<<0),		/*  data terminal ready */
+	 Rts=	(1<<1),		/*  request to send */
+	 Ri=	(1<<2),		/*  ring */
+	 Inton=	(1<<3),		/*  turn on interrupts */
+	 Loop=	(1<<4),		/*  loop back */
+	Lstat=	5,		/* line status */
+	 Inready=(1<<0),	/*  receive buffer full */
+	 Oerror=(1<<1),		/*  receiver overrun */
+	 Perror=(1<<2),		/*  receiver parity error */
+	 Ferror=(1<<3),		/*  rcv framing error */
+	 Berror=(1<<4),		/* break alarm */
+	 Outready=(1<<5),	/*  output buffer full */
+	Mstat=	6,		/* modem status */
+	 Ctsc=	(1<<0),		/*  clear to send changed */
+	 Dsrc=	(1<<1),		/*  data set ready changed */
+	 Rire=	(1<<2),		/*  rising edge of ring indicator */
+	 Dcdc=	(1<<3),		/*  data carrier detect changed */
+	 Cts=	(1<<4),		/*  complement of clear to send line */
+	 Dsr=	(1<<5),		/*  complement of data set ready line */
+	 Ringl=	(1<<6),		/*  complement of ring indicator line */
+	 Dcd=	(1<<7),		/*  complement of data carrier detect line */
+	Scratch=7,		/* scratchpad */
+	Dlsb=	0,		/* divisor lsb */
+	Dmsb=	1,		/* divisor msb */
+
+	CTLS= 023,
+	CTLQ= 021,
+
+	Stagesize= 1024,
+	Nuart=	4,		/* max per machine */
+};
+
+typedef struct Uart Uart;
+struct Uart
+{
+	QLock;
+	int	opens;
+
+	int	enabled;
+	Uart	*elist;			/* next enabled interface */
+	char	name[KNAMELEN];
+
+	ulong	sticky[8];		/* sticky write register values */
+	void*	regs;
+	ulong	port;
+	ulong	freq;			/* clock frequency */
+	uchar	mask;			/* bits/char */
+	int	dev;
+	int	baud;			/* baud rate */
+
+	uchar	istat;			/* last istat read */
+	int	frame;			/* framing errors */
+	int	overrun;		/* rcvr overruns */
+
+	/* buffers */
+	int	(*putc)(Queue*, int);
+	Queue	*iq;
+	Queue	*oq;
+
+	Lock	flock;			/* fifo */
+	uchar	fifoon;			/* fifo's enabled */
+	uchar	nofifo;			/* earlier chip version with nofifo */
+
+	Lock	rlock;			/* receive */
+	uchar	istage[Stagesize];
+	uchar	*ip;
+	uchar	*ie;
+
+	int	haveinput;
+
+	Lock	tlock;			/* transmit */
+	uchar	ostage[Stagesize];
+	uchar	*op;
+	uchar	*oe;
+
+	int	modem;			/* hardware flow control on */
+	int	xonoff;			/* software flow control on */
+	int	blocked;
+	int	cts, dsr, dcd;		/* keep track of modem status */ 
+	int	ctsbackoff;
+	int	hup_dsr, hup_dcd;	/* send hangup upstream? */
+	int	dohup;
+
+	Rendez	r;
+};
+
+static Uart *uart[Nuart];
+static int nuart;
+
+struct Uartalloc {
+	Lock;
+	Uart *elist;	/* list of enabled interfaces */
+} uartalloc;
+
+static void uartintr(Uart*);
+
+/*
+ *  pick up architecture specific routines and definitions
+ */
+#include "uart.h"
+
+/*
+ *  set the baud rate by calculating and setting the baudrate
+ *  generator constant.  This will work with fairly non-standard
+ *  baud rates.
+ */
+static void
+uartsetbaud(Uart *p, int rate)
+{
+	ulong brconst;
+
+	if(rate <= 0)
+		return;
+
+	p->freq = archuartclock(p->port, rate);
+	if(p->freq == 0)
+		return;
+
+	brconst = (p->freq+8*rate-1)/(16*rate);
+
+	uartwrreg(p, Format, Dra);
+	uartwr(p, Dmsb, (brconst>>8) & 0xff);
+	uartwr(p, Dlsb, brconst & 0xff);
+	uartwrreg(p, Format, 0);
+
+	p->baud = rate;
+}
+
+/*
+ * decide if we should hangup when dsr or dcd drops.
+ */
+static void
+uartdsrhup(Uart *p, int n)
+{
+	p->hup_dsr = n;
+}
+
+static void
+uartdcdhup(Uart *p, int n)
+{
+	p->hup_dcd = n;
+}
+
+static void
+uartparity(Uart *p, char type)
+{
+	switch(type){
+	case 'e':
+		p->sticky[Format] |= Pena|Peven;
+		break;
+	case 'o':
+		p->sticky[Format] &= ~Peven;
+		p->sticky[Format] |= Pena;
+		break;
+	default:
+		p->sticky[Format] &= ~(Pena|Peven);
+		break;
+	}
+	uartwrreg(p, Format, 0);
+}
+
+/*
+ *  set bits/character, default 8
+ */
+void
+uartbits(Uart *p, int bits)
+{
+	if(bits < 5 || bits > 8)
+		error(Ebadarg);
+
+	p->sticky[Format] &= ~3;
+	p->sticky[Format] |= bits-5;
+
+	uartwrreg(p, Format, 0);
+}
+
+
+/*
+ *  toggle DTR
+ */
+void
+uartdtr(Uart *p, int n)
+{
+	if(n)
+		p->sticky[Mctl] |= Dtr;
+	else
+		p->sticky[Mctl] &= ~Dtr;
+
+	uartwrreg(p, Mctl, 0);
+}
+
+/*
+ *  toggle RTS
+ */
+void
+uartrts(Uart *p, int n)
+{
+	if(n)
+		p->sticky[Mctl] |= Rts;
+	else
+		p->sticky[Mctl] &= ~Rts;
+
+	uartwrreg(p, Mctl, 0);
+}
+
+/*
+ *  send break
+ */
+static void
+uartbreak(Uart *p, int ms)
+{
+	if(ms == 0)
+		ms = 200;
+
+	uartwrreg(p, Format, Break);
+	tsleep(&up->sleep, return0, 0, ms);
+	uartwrreg(p, Format, 0);
+}
+
+static void
+uartfifoon(Uart *p)
+{
+	ulong i, x;
+
+	if(p->nofifo || uartrdreg(p, Istat) & Fenabd)
+		return;
+
+	x = splhi();
+
+	/* reset fifos */
+	p->sticky[Fifoctl] = 0;
+	uartwrreg(p, Fifoctl, Fclear);
+
+	/* empty buffer and interrupt conditions */
+	for(i = 0; i < 16; i++){
+		if(uartrdreg(p, Istat)){
+			/* nothing to do */
+		}
+		if(uartrdreg(p, Data)){
+			/* nothing to do */
+		}
+	}
+
+	/* turn on fifo */
+	p->fifoon = 1;
+	p->sticky[Fifoctl] = Fena|Ftrig;
+	uartwrreg(p, Fifoctl, 0);
+	p->istat = uartrdreg(p, Istat);
+	if((p->istat & Fenabd) == 0) {
+		/* didn't work, must be an earlier chip type */
+		p->nofifo = 1;
+	}
+
+	splx(x);
+}
+
+/*
+ *  modem flow control on/off (rts/cts)
+ */
+static void
+uartmflow(Uart *p, int n)
+{
+	ilock(&p->tlock);
+	if(n){
+		p->sticky[Iena] |= Imstat;
+		uartwrreg(p, Iena, 0);
+		p->modem = 1;
+		p->cts = uartrdreg(p, Mstat) & Cts;
+	} else {
+		p->sticky[Iena] &= ~Imstat;
+		uartwrreg(p, Iena, 0);
+		p->modem = 0;
+		p->cts = 1;
+	}
+	iunlock(&p->tlock);
+
+//	ilock(&p->flock);
+//	if(1)
+//		/* turn on fifo's */
+//		uartfifoon(p);
+//	else {
+//		/* turn off fifo's */
+//		p->fifoon = 0;
+//		p->sticky[Fifoctl] = 0;
+//		uartwrreg(p, Fifoctl, Fclear);
+//	}
+//	iunlock(&p->flock);
+}
+
+/*
+ *  turn on a port's interrupts.  set DTR and RTS
+ */
+static void
+uartenable(Uart *p)
+{
+	Uart **l;
+
+	if(p->enabled)
+		return;
+
+	uartportpower(p, 1);
+
+	p->hup_dsr = p->hup_dcd = 0;
+	p->cts = p->dsr = p->dcd = 0;
+
+	/*
+ 	 *  turn on interrupts
+	 */
+	p->sticky[Iena] = Ircv | Ixmt | Irstat | Uue;
+	uartwrreg(p, Iena, 0);
+
+	/*
+	 *  turn on DTR and RTS
+	 */
+	uartdtr(p, 1);
+	uartrts(p, 1);
+
+	uartfifoon(p);
+
+	/*
+	 *  assume we can send
+	 */
+	ilock(&p->tlock);
+	p->cts = 1;
+	p->blocked = 0;
+	iunlock(&p->tlock);
+
+	/*
+	 *  set baud rate to the last used
+	 */
+	uartsetbaud(p, p->baud);
+
+	lock(&uartalloc);
+	for(l = &uartalloc.elist; *l; l = &(*l)->elist){
+		if(*l == p)
+			break;
+	}
+	if(*l == 0){
+		p->elist = uartalloc.elist;
+		uartalloc.elist = p;
+	}
+	p->enabled = 1;
+	unlock(&uartalloc);
+}
+
+/*
+ *  turn off a port's interrupts.  reset DTR and RTS
+ */
+static void
+uartdisable(Uart *p)
+{
+	Uart **l;
+
+	/*
+ 	 *  turn off interrupts
+	 */
+	p->sticky[Iena] = Uue;
+	uartwrreg(p, Iena, 0);
+
+	/*
+	 *  revert to default settings
+	 */
+	p->sticky[Format] = Bits8;
+	uartwrreg(p, Format, 0);
+
+	/*
+	 *  turn off DTR, RTS, hardware flow control & fifo's
+	 */
+	uartdtr(p, 0);
+	uartrts(p, 0);
+	uartmflow(p, 0);
+	ilock(&p->tlock);
+	p->xonoff = p->blocked = 0;
+	iunlock(&p->tlock);
+
+	uartportpower(p, 0);
+
+	lock(&uartalloc);
+	for(l = &uartalloc.elist; *l; l = &(*l)->elist){
+		if(*l == p){
+			*l = p->elist;
+			break;
+		}
+	}
+	p->enabled = 0;
+	unlock(&uartalloc);
+}
+
+/*
+ *  put some bytes into the local queue to avoid calling
+ *  qconsume for every character
+ */
+static int
+stageoutput(Uart *p)
+{
+	int n;
+
+	n = qconsume(p->oq, p->ostage, Stagesize);
+	if(n <= 0)
+		return 0;
+	p->op = p->ostage;
+	p->oe = p->ostage + n;
+	return n;
+}
+
+/*
+ *  (re)start output
+ */
+static void
+uartkick0(Uart *p)
+{
+	int i;
+	if((p->modem && (p->cts == 0)) || p->blocked)
+		return;
+
+	/*
+	 *  128 here is an arbitrary limit to make sure
+	 *  we don't stay in this loop too long.  If the
+	 *  chips output queue is longer than 128, too
+	 *  bad -- presotto
+	 */
+	for(i = 0; i < 128; i++){
+		if(!(uartrdreg(p, Lstat) & Outready))
+			break;
+		if(p->op >= p->oe && stageoutput(p) == 0)
+			break;
+		uartwr(p, Data, *p->op++);
+	}
+}
+
+static void
+uartkick(void *v)
+{
+	Uart *p;
+
+	p = v;
+	ilock(&p->tlock);
+	uartkick0(p);
+	iunlock(&p->tlock);
+}
+
+/*
+ *  restart input if it's off
+ */
+static void
+uartflow(void *v)
+{
+	Uart *p;
+
+	p = v;
+	if(p->modem)
+		uartrts(p, 1);
+	ilock(&p->rlock);
+	p->haveinput = 1;
+	iunlock(&p->rlock);
+}
+
+/*
+ *  default is 9600 baud, 1 stop bit, 8 bit chars, no interrupts,
+ *  transmit and receive enabled, interrupts disabled.
+ */
+static void
+uartsetup0(Uart *p)
+{
+	memset(p->sticky, 0, sizeof(p->sticky));
+	/*
+	 *  set rate to 9600 baud.
+	 *  8 bits/character.
+	 *  1 stop bit.
+	 *  interrupts enabled.
+	 */
+	p->sticky[Format] = Bits8;
+	uartwrreg(p, Format, 0);
+	p->sticky[Mctl] |= Inton;
+	uartwrreg(p, Mctl, 0x0);
+
+//	uartsetbaud(p, 9600);
+	uartsetbaud(p, 38400);
+
+	p->iq = qopen(4*1024, 0, uartflow, p);
+	p->oq = qopen(4*1024, 0, uartkick, p);
+	if(p->iq == nil || p->oq == nil)
+		panic("uartsetup0");
+
+	p->ip = p->istage;
+	p->ie = &p->istage[Stagesize];
+	p->op = p->ostage;
+	p->oe = p->ostage;
+}
+
+/*
+ *  called by uartinstall() to create a new duart
+ */
+void
+uartsetup(ulong port, void *regs, ulong freq, char *name)
+{
+	Uart *p;
+
+	if(nuart >= Nuart)
+		return;
+
+	p = xalloc(sizeof(Uart));
+	uart[nuart] = p;
+	strcpy(p->name, name);
+	p->dev = nuart;
+	nuart++;
+	p->port = port;
+	p->regs = regs;
+	p->freq = freq;
+	uartsetup0(p);
+}
+
+/*
+ *  called by main() to configure a duart port as a console or a mouse
+ */
+void
+uartspecial(int port, int baud, Queue **in, Queue **out, int (*putc)(Queue*, int))
+{
+	Uart *p = uart[port];
+	uartenable(p);
+	if(baud)
+		uartsetbaud(p, baud);
+	p->putc = putc;
+	if(in)
+		*in = p->iq;
+	if(out)
+		*out = p->oq;
+	p->opens++;
+}
+
+/*
+ *  handle an interrupt to a single uart
+ */
+static void
+uartintr(Uart *p)
+{
+	uchar ch;
+	int s, l;
+
+	for (s = uartrdreg(p, Istat); !(s&Ipend); s = uartrdreg(p, Istat)) {
+		switch(s&0x3f){
+		case 4:	/* received data available */
+		case 6:	/* receiver line status (alarm or error) */
+		case 12:	/* character timeout indication */
+			while ((l = uartrdreg(p, Lstat)) & Inready) {
+				if(l & Ferror)
+					p->frame++;
+				if(l & Oerror)
+					p->overrun++;
+				ch = uartrdreg(p, Data) & 0xff;
+				if (l & (Berror|Perror|Ferror)) {
+					/* ch came with break, parity or framing error - consume */
+					continue;
+				}
+				if (ch == CTLS || ch == CTLQ) {
+					ilock(&p->tlock);
+					if(p->xonoff){
+						if(ch == CTLS)
+							p->blocked = 1;
+						else
+							p->blocked = 0;	/* clock gets output going again */
+					}
+					iunlock(&p->tlock);
+				}
+				if(p->putc)
+					p->putc(p->iq, ch);
+				else {
+					ilock(&p->rlock);
+					if(p->ip < p->ie)
+						*p->ip++ = ch;
+					else
+						p->overrun++;
+					p->haveinput = 1;
+					iunlock(&p->rlock);
+				}
+			}
+			break;
+
+		case 2:	/* transmitter not full */
+			uartkick(p);
+			break;
+
+		case 0:	/* modem status */
+			ch = uartrdreg(p, Mstat);
+			if(ch & Ctsc){
+				ilock(&p->tlock);
+				l = p->cts;
+				p->cts = ch & Cts;
+				if(l == 0 && p->cts)
+					p->ctsbackoff = 2; /* clock gets output going again */
+				iunlock(&p->tlock);
+			}
+	 		if (ch & Dsrc) {
+				l = ch & Dsr;
+				if(p->hup_dsr && p->dsr && !l){
+					ilock(&p->rlock);
+					p->dohup = 1;
+					iunlock(&p->rlock);
+				}
+				p->dsr = l;
+			}
+	 		if (ch & Dcdc) {
+				l = ch & Dcd;
+				if(p->hup_dcd && p->dcd && !l){
+					ilock(&p->rlock);
+					p->dohup = 1;
+					iunlock(&p->rlock);
+				}
+				p->dcd = l;
+			}
+			break;
+
+		default:
+			iprint("weird uart interrupt #%2.2ux\n", s);
+			break;
+		}
+	}
+	p->istat = s;
+}
+
+/*
+ *  we save up input characters till clock time
+ *
+ *  There's also a bit of code to get a stalled print going.
+ *  It shouldn't happen, but it does.  Obviously I don't
+ *  understand something.  Since it was there, I bundled a
+ *  restart after flow control with it to give some hysteresis
+ *  to the hardware flow control.  This makes compressing
+ *  modems happier but will probably bother something else.
+ *	 -- presotto
+ */
+void
+uartclock(void)
+{
+	int n;
+	Uart *p;
+
+	for(p = uartalloc.elist; p; p = p->elist){
+
+		/* this amortizes cost of qproduce to many chars */
+		if(p->haveinput){
+			ilock(&p->rlock);
+			if(p->haveinput){
+				n = p->ip - p->istage;
+				if(n > 0 && p->iq){
+					if(n > Stagesize)
+						panic("uartclock");
+					if(qproduce(p->iq, p->istage, n) < 0)
+						uartrts(p, 0);
+					else
+						p->ip = p->istage;
+				}
+				p->haveinput = 0;
+			}
+			iunlock(&p->rlock);
+		}
+		if(p->dohup){
+			ilock(&p->rlock);
+			if(p->dohup){
+				qhangup(p->iq, 0);
+				qhangup(p->oq, 0);
+			}
+			p->dohup = 0;
+			iunlock(&p->rlock);
+		}
+
+		/* this adds hysteresis to hardware flow control */
+		if(p->ctsbackoff){
+			ilock(&p->tlock);
+			if(p->ctsbackoff){
+				if(--(p->ctsbackoff) == 0)
+					uartkick0(p);
+			}
+			iunlock(&p->tlock);
+		}
+	}
+}
+
+Dirtab *uartdir;
+int ndir;
+
+static void
+setlength(int i)
+{
+	Uart *p;
+
+	if(i >= 0){
+		p = uart[i];
+		if(p && p->opens && p->iq)
+			uartdir[1+3*i].length = qlen(p->iq);
+	} else for(i = 0; i < nuart; i++){
+		p = uart[i];
+		if(p && p->opens && p->iq)
+			uartdir[1+3*i].length = qlen(p->iq);
+	}
+}
+
+/*
+ *  all uarts must be uartsetup() by this point or inside of uartinstall()
+ */
+static void
+uartreset(void)
+{
+	int i;
+	Dirtab *dp;
+	uartinstall();	/* architecture specific */
+
+	ndir = 1+3*nuart;
+	uartdir = xalloc(ndir * sizeof(Dirtab));
+	dp = uartdir;
+	strcpy(dp->name, ".");
+	mkqid(&dp->qid, 0, 0, QTDIR);
+	dp->length = 0;
+	dp->perm = DMDIR|0555;
+	dp++;
+	for(i = 0; i < nuart; i++){
+		/* 3 directory entries per port */
+		strcpy(dp->name, uart[i]->name);
+		dp->qid.path = NETQID(i, Ndataqid);
+		dp->perm = 0666;
+		dp++;
+		sprint(dp->name, "%sctl", uart[i]->name);
+		dp->qid.path = NETQID(i, Nctlqid);
+		dp->perm = 0666;
+		dp++;
+		sprint(dp->name, "%sstatus", uart[i]->name);
+		dp->qid.path = NETQID(i, Nstatqid);
+		dp->perm = 0444;
+		dp++;
+	}
+}
+
+static Chan*
+uartattach(char *spec)
+{
+	return devattach('t', spec);
+}
+
+static Walkqid*
+uartwalk(Chan *c, Chan *nc, char **name, int nname)
+{
+	return devwalk(c, nc, name, nname, uartdir, ndir, devgen);
+}
+
+static int
+uartstat(Chan *c, uchar *dp, int n)
+{
+	if(NETTYPE(c->qid.path) == Ndataqid)
+		setlength(NETID(c->qid.path));
+	return devstat(c, dp, n, uartdir, ndir, devgen);
+}
+
+static Chan*
+uartopen(Chan *c, int omode)
+{
+	Uart *p;
+
+	c = devopen(c, omode, uartdir, ndir, devgen);
+
+	switch(NETTYPE(c->qid.path)){
+	case Nctlqid:
+	case Ndataqid:
+		p = uart[NETID(c->qid.path)];
+		qlock(p);
+		if(p->opens++ == 0){
+			uartenable(p);
+			qreopen(p->iq);
+			qreopen(p->oq);
+		}
+		qunlock(p);
+		break;
+	}
+
+	return c;
+}
+
+static void
+uartclose(Chan *c)
+{
+	Uart *p;
+
+	if(c->qid.type & QTDIR)
+		return;
+	if((c->flag & COPEN) == 0)
+		return;
+	switch(NETTYPE(c->qid.path)){
+	case Ndataqid:
+	case Nctlqid:
+		p = uart[NETID(c->qid.path)];
+		qlock(p);
+		if(--(p->opens) == 0){
+			uartdisable(p);
+			qclose(p->iq);
+			qclose(p->oq);
+			p->ip = p->istage;
+			p->dcd = p->dsr = p->dohup = 0;
+		}
+		qunlock(p);
+		break;
+	}
+}
+
+static long
+uartstatus(Chan*, Uart *p, void *buf, long n, long offset)
+{
+	uchar mstat, fstat, istat, tstat;
+	char str[256];
+
+	str[0] = 0;
+	tstat = p->sticky[Mctl];
+	mstat = uartrdreg(p, Mstat);
+	istat = p->sticky[Iena];
+	fstat = p->sticky[Format];
+	snprint(str, sizeof str,
+		"b%d c%d d%d e%d l%d m%d p%c r%d s%d\n"
+		"%d %d %d%s%s%s%s%s\n",
+
+		p->baud,
+		p->hup_dcd, 
+		(tstat & Dtr) != 0,
+		p->hup_dsr,
+		(fstat & Bits8) + 5,
+		(istat & Imstat) != 0, 
+		(fstat & Pena) ? ((fstat & Peven) ? 'e' : 'o') : 'n',
+		(tstat & Rts) != 0,
+		(fstat & Stop2) ? 2 : 1,
+
+		p->dev,
+		p->frame,
+		p->overrun, 
+		uartrdreg(p, Istat) & Fenabd       ? " fifo" : "",
+		(mstat & Cts)    ? " cts"  : "",
+		(mstat & Dsr)    ? " dsr"  : "",
+		(mstat & Dcd)    ? " dcd"  : "",
+		(mstat & Ringl)   ? " ring" : ""
+	);
+	return readstr(offset, buf, n, str);
+}
+
+static long
+uartread(Chan *c, void *buf, long n, vlong off)
+{
+	Uart *p;
+	ulong offset = off;
+
+	if(c->qid.type & QTDIR){
+		setlength(-1);
+		return devdirread(c, buf, n, uartdir, ndir, devgen);
+	}
+
+	p = uart[NETID(c->qid.path)];
+	switch(NETTYPE(c->qid.path)){
+	case Ndataqid:
+		return qread(p->iq, buf, n);
+	case Nctlqid:
+		return readnum(offset, buf, n, NETID(c->qid.path), NUMSIZE);
+	case Nstatqid:
+		return uartstatus(c, p, buf, n, offset);
+	}
+
+	return 0;
+}
+
+static void
+uartctl(Uart *p, char *cmd)
+{
+	int i, n;
+
+	/* let output drain for a while */
+	for(i = 0; i < 16 && qlen(p->oq); i++)
+		tsleep(&p->r, (int(*)(void*))qlen, p->oq, 125);
+
+	if(strncmp(cmd, "break", 5) == 0){
+		uartbreak(p, 0);
+		return;
+	}
+
+
+	n = atoi(cmd+1);
+	switch(*cmd){
+	case 'B':
+	case 'b':
+		uartsetbaud(p, n);
+		break;
+	case 'C':
+	case 'c':
+		uartdcdhup(p, n);
+		break;
+	case 'D':
+	case 'd':
+		uartdtr(p, n);
+		break;
+	case 'E':
+	case 'e':
+		uartdsrhup(p, n);
+		break;
+	case 'f':
+	case 'F':
+		qflush(p->oq);
+		break;
+	case 'H':
+	case 'h':
+		qhangup(p->iq, 0);
+		qhangup(p->oq, 0);
+		break;
+	case 'L':
+	case 'l':
+		uartbits(p, n);
+		break;
+	case 'm':
+	case 'M':
+		uartmflow(p, n);
+		break;
+	case 'n':
+	case 'N':
+		qnoblock(p->oq, n);
+		break;
+	case 'P':
+	case 'p':
+		uartparity(p, *(cmd+1));
+		break;
+	case 'K':
+	case 'k':
+		uartbreak(p, n);
+		break;
+	case 'R':
+	case 'r':
+		uartrts(p, n);
+		break;
+	case 'Q':
+	case 'q':
+		qsetlimit(p->iq, n);
+		qsetlimit(p->oq, n);
+		break;
+	case 'W':
+	case 'w':
+		/* obsolete */
+		break;
+	case 'X':
+	case 'x':
+		ilock(&p->tlock);
+		p->xonoff = n;
+		iunlock(&p->tlock);
+		break;
+	}
+}
+
+static long
+uartwrite(Chan *c, void *buf, long n, vlong)
+{
+	Uart *p;
+	char cmd[32];
+
+	if(c->qid.type & QTDIR)
+		error(Eperm);
+
+	p = uart[NETID(c->qid.path)];
+
+	/*
+	 *  The fifo's turn themselves off sometimes.
+	 *  It must be something I don't understand. -- presotto
+	 */
+	lock(&p->flock);
+	if((p->istat & Fenabd) == 0 && p->fifoon && p->nofifo == 0)
+		uartfifoon(p);
+	unlock(&p->flock);
+
+	switch(NETTYPE(c->qid.path)){
+	case Ndataqid:
+		return qwrite(p->oq, buf, n);
+	case Nctlqid:
+		if(n >= sizeof(cmd))
+			n = sizeof(cmd)-1;
+		memmove(cmd, buf, n);
+		cmd[n] = 0;
+		uartctl(p, cmd);
+		return n;
+	default:
+		error(Egreg);
+		return n;
+	}
+}
+
+static int
+uartwstat(Chan *c, uchar *dp, int n)
+{
+	Dir d;
+	Dirtab *dt;
+
+	if(!iseve())
+		error(Eperm);
+	if(c->qid.type & QTDIR)
+		error(Eperm);
+	if(NETTYPE(c->qid.path) == Nstatqid)
+		error(Eperm);
+
+	dt = &uartdir[1+3 * NETID(c->qid.path)];
+	n = convM2D(dp, n, &d, nil);
+	if(n == 0)
+		error(Eshortstat);
+	if(d.mode != ~0UL){
+		d.mode &= 0666;
+		dt[0].perm = dt[1].perm = d.mode;
+	}
+	return n;
+}
+
+Dev uartdevtab = {
+	't',
+	"uart",
+
+	uartreset,
+	devinit,
+	devshutdown,
+	uartattach,
+	uartwalk,
+	uartstat,
+	uartopen,
+	devcreate,
+	uartclose,
+	uartread,
+	devbread,
+	uartwrite,
+	devbwrite,
+	devremove,
+	uartwstat,
+};
--- /dev/null
+++ b/os/pxa/dma.c
@@ -1,0 +1,244 @@
+#include	"u.h"
+#include	"../port/lib.h"
+#include	"mem.h"
+#include	"dat.h"
+#include	"fns.h"
+#include	"../port/error.h"
+#include	"io.h"
+
+#define	DMAREGS	((Dmaregs*)PHYSDMA)
+typedef struct Dmadesc Dmadesc;
+typedef struct Dmaregs Dmaregs;
+
+struct Dmadesc {
+	ulong	ddadr;	/* next descriptor address (0 mod 16) */
+	ulong	dsadr;	/* source address (0 mod 8 if external, 0 mod 4 internal) */
+	ulong	dtadr;	/* target address (same) */
+	ulong	dcmd;	/* command */
+};
+
+struct Dmaregs {
+	ulong	dcsr[16];	/* control and status */
+	uchar	pad0[0xF0-0x40];
+	ulong	dint;	/* mask of interrupting channels: 0 is bit 0 */
+	uchar	pad1[0x100-0xF4];
+	ulong	drcmr[40];
+	Dmadesc	chan[16];	/* offset 0x200 */
+};
+
+enum {
+	/* dcsr */
+	DcsRun=	1<<31,	/* start the channel */
+	DcsNodesc=	1<<30,	/* set if channel is in no-descriptor fetch mode */
+	DcsStopirq=	1<<29,	/* enable interrupt if channel is uninitialised or stopped */
+	DcsReqpend=	1<<8,	/* channel has pending request */
+	DcsStopstate=	1<<3,	/* channel is uninitialised or stopped */
+	DcsEndintr=	1<<2,	/* transaction complete, length now 0 */
+	DcsStartintr=	1<<1,	/* successful descriptor fetch */
+	DcsBuserr=	1<<0,	/* bus error */
+
+	/* drcmr */
+	DmrValid=	1<<7,	/* mapped to channel given by bits 0-3 */
+	DmrChan=	0xF,		/* channel number mask */
+
+	/* ddadr */
+	DdaStop=	1<<1,	/* =0, run channel; =1, stop channel after this descriptor */
+
+	/* dcmd */
+	DcmIncsrc=	1<<31,	/* increment source address after use */
+	DcmIncdest=	1<<30,	/* increment destination address after use */
+	DcmFlowsrc=	1<<29,	/* enable flow control on source */
+	DcmFlowdest=	1<<28,	/* enable flow control on target */
+	DcmStartirq=	1<<22,	/* interrupt when descriptor loaded (fetch mode) */
+	DcmEndirq=	1<<21,	/* interrupt when transfer complete */
+	DcmEndian=	1<<18,	/* must be zero (little endian) */
+	DcmBurst8=	1<<16,	/* burst size in bytes */
+	DcmBurst16=	2<<16,
+	DcmBurst32=	3<<16,
+	DcmWidth0=	0<<14,	/* width for external memory */
+	DcmWidth1=	1<<14,	/* width of on-chip peripheral */
+	DcmWidth2=	2<<14,
+	DcmWidth4=	3<<14,
+	DcmLength=	(1<<13)-1,
+
+	Ndma=	16,		/* number of dma channels */
+	MaxDMAbytes=	8192-1,	/* annoyingly small limit */
+};
+
+struct Dma {
+	int	chan;
+	Dmadesc*	desc;
+	Dmadesc	stop;
+	ulong	*csr;
+	void	(*interrupt)(void*, ulong);
+	void*	arg;
+	Rendez	r;
+	ulong	attrs;	/* transfer attributes: flow control, burst size, width */
+};
+
+static struct {
+	Lock;
+	ulong	avail;
+	Dma	dma[Ndma];
+} dmachans;
+
+static	void	dmaintr(Ureg*, void*);
+
+void
+dmareset(void)
+{
+	int i;
+	Dma *d;
+
+	for(i=0; i<Ndma; i++){
+		dmachans.avail |= 1<<i;
+		d = &dmachans.dma[i];
+		d->chan = i;
+		d->csr = &DMAREGS->dcsr[i];
+		d->desc = &DMAREGS->chan[i];
+		d->stop.ddadr = (ulong)&d->stop | DdaStop;
+		d->stop.dcmd = 0;
+	}
+	intrenable(IRQ, IRQdma, dmaintr, nil, "dma");
+}
+
+/*
+ * allocate a DMA channel, reset it, and configure it for the given device
+ */
+Dma*
+dmasetup(int owner, void (*interrupt)(void*, ulong), void *arg, ulong attrs)
+{
+	Dma *d;
+	Dmadesc *dc;
+	int i;
+
+	ilock(&dmachans);
+	for(i=0; (dmachans.avail & (1<<i)) == 0; i++)
+		if(i >= Ndma){
+			iunlock(&dmachans);
+			return nil;
+		}
+	dmachans.avail &= ~(1<<i);
+	iunlock(&dmachans);
+
+	d = &dmachans.dma[i];
+	d->owner = owner;
+	d->interrupt = interrupt;
+	d->arg = arg;
+	d->attrs = attrs;
+	dc = d->desc;
+	dc->ddadr = (ulong)&d->stop | DdaStop;	/* empty list */
+	dc->dcmd = 0;
+	*d->csr = DcsEndintr | DcsStartintr | DcsBuserr;	/* clear status, stopped */
+	DMAREGS->drcmr[owner] = DmrValid | i;
+	return d;
+}
+
+void
+dmafree(Dma *dma)
+{
+	dmastop(dma);
+	DMAREGS->drcmr[d->owner] = 0;
+	ilock(&dmachans);
+	dmachans.avail |= 1<<dma->chan;
+	dma->interrupt = nil;
+	iunlock(&dmachans);
+}
+
+/*
+ * simple dma transfer on a channel, using `no fetch descriptor' mode.
+ * virtual buffer addresses are assumed to refer to contiguous physical addresses.
+ */
+int
+dmastart(Dma *dma, void *from, void *to, int nbytes)
+{
+	Dmadesc *dc;
+
+	if((ulong)nbytes > MaxDMAbytes)
+		panic("dmastart");
+	if((*dma->csr & DcsStopstate) == 0)
+		return 0;	/* busy */
+	dc = dma->desc;
+	dc->ddadr = DdaStop;
+	dc->dsadr = PADDR(from);
+	dc->dtadr = PADDR(to);
+	dc->dcmd = dma->attrs | DcmEndirq | nbytes;
+	*dma->csr = DcsRun | DcsNodesc | DcsEndintr | DcsStartintr | DcsBuserr;
+	return 1;
+}
+
+/*
+ * stop dma on a channel
+ */
+void
+dmastop(Dma *dma)
+{
+	*dma->csr = 0;
+	while((*dma->csr & DcsStopstate) == 0)
+		;
+	*dma->csr = DcsStopstate;
+}
+
+/*
+ * return nonzero if there was a memory error during DMA,
+ * and clear the error state
+ */
+int
+dmaerror(Dma *dma)
+{
+	ulong e;
+
+	e = *dma->csr & DcsBuserr;
+	*dma->csr |= e;
+	return e;
+}
+
+/*
+ * return nonzero if the DMA channel is not busy
+ */
+int
+dmaidle(Dma *d)
+{
+	return (*d->csr & DcsStopstate) == 0;
+}
+
+static int
+dmaidlep(void *a)
+{
+	return dmaidle((Dma*)a);
+}
+
+void
+dmawait(Dma *d)
+{
+	while(!dmaidle(d))
+		sleep(&d->r, dmaidlep, d);
+}
+
+/*
+ * this interface really only copes with one buffer at once
+ */
+static void
+dmaintr(Ureg*, void*)
+{
+	Dma *d;
+	Dmaregs *dr;
+	int i;
+	ulong s, csr;
+
+	dr = DMAREGS;
+	s = dr->dint;
+	dr->dint = s;
+	for(i=0; i<Ndma && s != 0; i++)
+		if(s & (1<<i)){
+			d = &dmachans.dma[i];
+			csr = *d->csr;
+			if(csr & DcsBuserr)
+				iprint("DMA error, chan %d status #%8.8lux\n", d->chan, csr);
+			*d->csr = csr & (DcsRun | DcsNodesc | DcsEndintr | DcsStartintr | DcsBuserr);
+			if(d->interrupt != nil)
+				d->interrupt(d->arg, csr);
+			else
+				wakeup(&d->r);
+		}
+}
--- /dev/null
+++ b/os/pxa/etherif.h
@@ -1,0 +1,41 @@
+enum {
+	MaxEther	= 3,
+	Ntypes		= 8,
+};
+
+typedef struct Ether Ether;
+struct Ether {
+RWlock;	/* TO DO */
+	ISAConf;			/* hardware info */
+	int	ctlrno;
+	int	minmtu;
+	int	maxmtu;
+	uchar	ea[Eaddrlen];
+	int	encry;
+
+	void	(*attach)(Ether*);	/* filled in by reset routine */
+	void	(*closed)(Ether*);
+	void	(*detach)(Ether*);
+	void	(*transmit)(Ether*);
+	void	(*interrupt)(Ureg*, void*);
+	long	(*ifstat)(Ether*, void*, long, ulong);
+	long	(*ctl)(Ether*, void*, long); /* custom ctl messages */
+	void	(*power)(Ether*, int);	/* power on/off */
+	void	(*shutdown)(Ether*);	/* shutdown hardware before reboot */
+	void	*ctlr;
+	int	pcmslot;		/* PCMCIA */
+	int	fullduplex;	/* non-zero if full duplex */
+
+	Queue*	oq;
+
+	Netif;
+};
+
+extern Block* etheriq(Ether*, Block*, int);
+extern void addethercard(char*, int(*)(Ether*));
+extern int archether(int, Ether*);
+
+#define NEXT(x, l)	(((x)+1)%(l))
+#define PREV(x, l)	(((x) == 0) ? (l)-1: (x)-1)
+#define	HOWMANY(x, y)	(((x)+((y)-1))/(y))
+#define ROUNDUP(x, y)	(HOWMANY((x), (y))*(y))
--- /dev/null
+++ b/os/pxa/fpi.h
@@ -1,0 +1,61 @@
+typedef long Word;
+typedef unsigned long Single;
+typedef struct {
+	unsigned long l;
+	unsigned long h;
+} Double;
+
+enum {
+	FractBits	= 28,
+	CarryBit	= 0x10000000,
+	HiddenBit	= 0x08000000,
+	MsBit		= HiddenBit,
+	NGuardBits	= 3,
+	GuardMask	= 0x07,
+	LsBit		= (1<<NGuardBits),
+
+	SingleExpBias	= 127,
+	SingleExpMax	= 255,
+	DoubleExpBias	= 1023,
+	DoubleExpMax	= 2047,
+
+	ExpBias		= DoubleExpBias,
+	ExpInfinity	= DoubleExpMax,
+};
+
+typedef struct {
+	unsigned char s;
+	short e;
+	long l;				/* 0000FFFFFFFFFFFFFFFFFFFFFFFFFGGG */
+	long h;				/* 0000HFFFFFFFFFFFFFFFFFFFFFFFFFFF */
+} Internal;
+
+#define IsWeird(n)	((n)->e >= ExpInfinity)
+#define	IsInfinity(n)	(IsWeird(n) && (n)->h == HiddenBit && (n)->l == 0)
+#define	SetInfinity(n)	((n)->e = ExpInfinity, (n)->h = HiddenBit, (n)->l = 0)
+#define IsNaN(n)	(IsWeird(n) && (((n)->h & ~HiddenBit) || (n)->l))
+#define	SetQNaN(n)	((n)->s = 0, (n)->e = ExpInfinity, 		\
+			 (n)->h = HiddenBit|(LsBit<<1), (n)->l = 0)
+#define IsZero(n)	((n)->e == 1 && (n)->h == 0 && (n)->l == 0)
+#define SetZero(n)	((n)->e = 1, (n)->h = 0, (n)->l = 0)
+
+/*
+ * fpi.c
+ */
+extern void fpiround(Internal *);
+extern void fpiadd(Internal *, Internal *, Internal *);
+extern void fpisub(Internal *, Internal *, Internal *);
+extern void fpimul(Internal *, Internal *, Internal *);
+extern void fpidiv(Internal *, Internal *, Internal *);
+extern int fpicmp(Internal *, Internal *);
+extern void fpinormalise(Internal*);
+
+/*
+ * fpimem.c
+ */
+extern void fpis2i(Internal *, void *);
+extern void fpid2i(Internal *, void *);
+extern void fpiw2i(Internal *, void *);
+extern void fpii2s(void *, Internal *);
+extern void fpii2d(void *, Internal *);
+extern void fpii2w(Word *, Internal *);
--- /dev/null
+++ b/os/pxa/fpiarm.c
@@ -1,0 +1,483 @@
+/*
+ * this doesn't attempt to implement ARM floating-point properties
+ * that aren't visible in the Inferno environment.
+ * all arithmetic is done in double precision.
+ * the FP trap status isn't updated.
+ */
+#include "u.h"
+#include	"../port/lib.h"
+#include	"mem.h"
+#include	"dat.h"
+#include	"fns.h"
+#include	"io.h"
+
+#include	"ureg.h"
+
+#include "fpi.h"
+
+// #define	R13OK	undef this if correct kernel r13 isn't in Ureg; check calculation in fpiarm below
+
+#define	REG(x) (*(long*)(((char*)(ur))+roff[(x)]))
+#define	FPENV	(*(ufp))
+#define	FR(x) (*(Internal*)(ufp)->regs[(x)&7])
+
+/* BUG: check fetch (not worthwhile in Inferno) */
+#define	getubyte(a) (*(uchar*)(a))
+#define	getuword(a) (*(ushort*)(a))
+#define	getulong(a) (*(ulong*)(a))
+
+typedef struct FP2 FP2;
+typedef struct FP1 FP1;
+
+struct FP2 {
+	char*	name;
+	void	(*f)(Internal, Internal, Internal*);
+};
+
+struct FP1 {
+	char*	name;
+	void	(*f)(Internal*, Internal*);
+};
+
+enum {
+	N = 1<<31,
+	Z = 1<<30,
+	C = 1<<29,
+	V = 1<<28,
+	REGPC = 15,
+};
+
+int	fpemudebug = 0;
+
+#undef OFR
+#define	OFR(X)	((ulong)&((Ureg*)0)->X)
+
+static	int	roff[] = {
+	OFR(r0), OFR(r1), OFR(r2), OFR(r3),
+	OFR(r4), OFR(r5), OFR(r6), OFR(r7),
+	OFR(r8), OFR(r9), OFR(r10), OFR(r11),
+#ifdef R13OK
+	OFR(r12), OFR(r13), OFR(r14), OFR(pc),
+#else
+	OFR(r12), OFR(type), OFR(r14), OFR(pc),
+#endif
+};
+
+static Internal fpconst[8] = {	/* indexed by op&7 */
+	/* s, e, l, h */
+	{0, 0x1, 0x00000000, 0x00000000}, /* 0.0 */
+	{0, 0x3FF, 0x00000000, 0x08000000},	/* 1.0 */
+	{0, 0x400, 0x00000000, 0x08000000},	/* 2.0 */
+	{0, 0x400, 0x00000000, 0x0C000000},	/* 3.0 */
+	{0, 0x401, 0x00000000, 0x08000000},	/* 4.0 */
+	{0, 0x401, 0x00000000, 0x0A000000},	/* 5.0 */
+	{0, 0x3FE, 0x00000000, 0x08000000},	/* 0.5 */
+	{0, 0x402, 0x00000000, 0x0A000000},	/* 10.0 */
+};
+
+/*
+ * arm binary operations
+ */
+
+static void
+fadd(Internal m, Internal n, Internal *d)
+{
+	(m.s == n.s? fpiadd: fpisub)(&m, &n, d);
+}
+
+static void
+fsub(Internal m, Internal n, Internal *d)
+{
+	m.s ^= 1;
+	(m.s == n.s? fpiadd: fpisub)(&m, &n, d);
+}
+
+static void
+fsubr(Internal m, Internal n, Internal *d)
+{
+	n.s ^= 1;
+	(n.s == m.s? fpiadd: fpisub)(&n, &m, d);
+}
+
+static void
+fmul(Internal m, Internal n, Internal *d)
+{
+	fpimul(&m, &n, d);
+}
+
+static void
+fdiv(Internal m, Internal n, Internal *d)
+{
+	fpidiv(&m, &n, d);
+}
+
+static void
+fdivr(Internal m, Internal n, Internal *d)
+{
+	fpidiv(&n, &m, d);
+}
+
+/*
+ * arm unary operations
+ */
+
+static void
+fmov(Internal *m, Internal *d)
+{
+	*d = *m;
+}
+
+static void
+fmovn(Internal *m, Internal *d)
+{
+	*d = *m;
+	d->s ^= 1;
+}
+
+static void
+fabsf(Internal *m, Internal *d)
+{
+	*d = *m;
+	d->s = 0;
+}
+
+static void
+frnd(Internal *m, Internal *d)
+{
+	short e;
+
+	(m->s? fsub: fadd)(fpconst[6], *m, d);
+	if(IsWeird(d))
+		return;
+	fpiround(d);
+	e = (d->e - ExpBias) + 1;
+	if(e <= 0)
+		SetZero(d);
+	else if(e > FractBits){
+		if(e < 2*FractBits)
+			d->l &= ~((1<<(2*FractBits - e))-1);
+	}else{
+		d->l = 0;
+		if(e < FractBits)
+			d->h &= ~((1<<(FractBits-e))-1);
+	}
+}
+
+static	FP1	optab1[16] = {	/* Fd := OP Fm */
+[0]	{"MOVF",	fmov},
+[1]	{"NEGF",	fmovn},
+[2]	{"ABSF",	fabsf},
+[3]	{"RNDF",	frnd},
+[4]	{"SQTF",	/*fsqt*/0},
+/* LOG, LGN, EXP, SIN, COS, TAN, ASN, ACS, ATN all `deprecated' */
+/* URD and NRM aren't implemented */
+};
+
+static	FP2	optab2[16] = {	/* Fd := Fn OP Fm */
+[0]	{"ADDF",	fadd},
+[1]	{"MULF",	fmul},
+[2]	{"SUBF",	fsub},
+[3]	{"RSUBF",	fsubr},
+[4]	{"DIVF",	fdiv},
+[5]	{"RDIVF",	fdivr},
+/* POW, RPW deprecated */
+[8]	{"REMF",	/*frem*/0},
+[9]	{"FMF",	fmul},	/* fast multiply */
+[10]	{"FDV",	fdiv},	/* fast divide */
+[11]	{"FRD",	fdivr},	/* fast reverse divide */
+/* POL deprecated */
+};
+
+static ulong
+fcmp(Internal *n, Internal *m)
+{
+	int i;
+
+	if(IsWeird(m) || IsWeird(n)){
+		/* BUG: should trap if not masked */
+		return V|C;
+	}
+	i = fpicmp(n, m);
+	if(i > 0)
+		return C;
+	else if(i == 0)
+		return C|Z;
+	else
+		return N;
+}
+
+static void
+fld(void (*f)(Internal*, void*), int d, ulong ea, int n, FPenv *ufp)
+{
+	void *mem;
+
+	mem = (void*)ea;
+	(*f)(&FR(d), mem);
+	if(fpemudebug)
+		print("MOV%c #%lux, F%d\n", n==8? 'D': 'F', ea, d);
+}
+
+static void
+fst(void (*f)(void*, Internal*), ulong ea, int s, int n, FPenv *ufp)
+{
+	Internal tmp;
+	void *mem;
+
+	mem = (void*)ea;
+	tmp = FR(s);
+	if(fpemudebug)
+		print("MOV%c	F%d,#%lux\n", n==8? 'D': 'F', s, ea);
+	(*f)(mem, &tmp);
+}
+
+static int
+condok(int cc, int c)
+{
+	switch(c){
+	case 0:	/* Z set */
+		return cc&Z;
+	case 1:	/* Z clear */
+		return (cc&Z) == 0;
+	case 2:	/* C set */
+		return cc&C;
+	case 3:	/* C clear */
+		return (cc&C) == 0;
+	case 4:	/* N set */
+		return cc&N;
+	case 5:	/* N clear */
+		return (cc&N) == 0;
+	case 6:	/* V set */
+		return cc&V;
+	case 7:	/* V clear */
+		return (cc&V) == 0;
+	case 8:	/* C set and Z clear */
+		return cc&C && (cc&Z) == 0;
+	case 9:	/* C clear or Z set */
+		return (cc&C) == 0 || cc&Z;
+	case 10:	/* N set and V set, or N clear and V clear */
+		return (~cc&(N|V))==0 || (cc&(N|V)) == 0;
+	case 11:	/* N set and V clear, or N clear and V set */
+		return (cc&(N|V))==N || (cc&(N|V))==V;
+	case 12:	/* Z clear, and either N set and V set or N clear and V clear */
+		return (cc&Z) == 0 && ((~cc&(N|V))==0 || (cc&(N|V))==0);
+	case 13:	/* Z set, or N set and V clear or N clear and V set */
+		return (cc&Z) || (cc&(N|V))==N || (cc&(N|V))==V;
+	case 14:	/* always */
+		return 1;
+	case 15:	/* never (reserved) */
+		return 0;
+	}
+	return 0;	/* not reached */
+}
+
+static void
+unimp(ulong pc, ulong op)
+{
+	char buf[60];
+
+	snprint(buf, sizeof(buf), "sys: fp: pc=%lux unimp fp 0x%.8lux", pc, op);
+	if(fpemudebug)
+		print("FPE: %s\n", buf);
+	error(buf);
+	/* no return */
+}
+
+static void
+fpemu(ulong pc, ulong op, Ureg *ur, FPenv *ufp)
+{
+	int rn, rd, tag, o;
+	long off;
+	ulong ea;
+	Internal tmp, *fm, *fn;
+
+	/* note: would update fault status here if we noted numeric exceptions */
+
+	/*
+	 * LDF, STF; 10.1.1
+	 */
+	if(((op>>25)&7) == 6){
+		if(op & (1<<22))
+			unimp(pc, op);	/* packed or extended */
+		rn = (op>>16)&0xF;
+		off = (op&0xFF)<<2;
+		if((op & (1<<23)) == 0)
+			off = -off;
+		ea = REG(rn);
+		if(rn == REGPC)
+			ea += 8;
+		if(op & (1<<24))
+			ea += off;
+		rd = (op>>12)&7;
+		if(op & (1<<20)){
+			if(op & (1<<15))
+				fld(fpid2i, rd, ea, 8, ufp);
+			else
+				fld(fpis2i, rd, ea, 4, ufp);
+		}else{
+			if(op & (1<<15))
+				fst(fpii2d, ea, rd, 8, ufp);
+			else
+				fst(fpii2s, ea, rd, 4, ufp);
+		}
+		if((op & (1<<24)) == 0)
+			ea += off;
+		if(op & (1<<21))
+			REG(rn) = ea;
+		return;
+	}
+
+	/*
+	 * CPRT/transfer, 10.3
+	 */
+	if(op & (1<<4)){
+		rd = (op>>12) & 0xF;
+
+		/*
+		 * compare, 10.3.1
+		 */
+		if(rd == 15 && op & (1<<20)){
+			rn = (op>>16)&7;
+			fn = &FR(rn);
+			if(op & (1<<3)){
+				fm = &fpconst[op&7];
+				tag = 'C';
+			}else{
+				fm = &FR(op&7);
+				tag = 'F';
+			}
+			switch((op>>21)&7){
+			default:
+				unimp(pc, op);
+			case 4:	/* CMF: Fn :: Fm */
+			case 6:	/* CMFE: Fn :: Fm (with exception) */
+				ur->psr &= ~(N|C|Z|V);
+				ur->psr |= fcmp(fn, fm);
+				break;
+			case 5:	/* CNF: Fn :: -Fm */
+			case 7:	/* CNFE: Fn :: -Fm (with exception) */
+				tmp = *fm;
+				tmp.s ^= 1;
+				ur->psr &= ~(N|C|Z|V);
+				ur->psr |= fcmp(fn, &tmp);
+				break;
+			}
+			if(fpemudebug)
+				print("CMPF	%c%d,F%ld =%x\n", tag, rn, op&7, ur->psr>>28);
+			return;
+		}
+
+		/*
+		 * other transfer, 10.3
+		 */
+		switch((op>>20)&0xF){
+		default:
+			unimp(pc, op);
+		case 0:	/* FLT */
+			rn = (op>>16) & 7;
+			fpiw2i(&FR(rn), &REG(rd));
+			if(fpemudebug)
+				print("MOVW[FD]	R%d, F%d\n", rd, rn);
+			break;
+		case 1:	/* FIX */
+			if(op & (1<<3))
+				unimp(pc, op);
+			rn = op & 7;
+			tmp = FR(rn);
+			fpii2w(&REG(rd), &tmp);
+			if(fpemudebug)
+				print("MOV[FD]W	F%d, R%d =%ld\n", rn, rd, REG(rd));
+			break;
+		case 2:	/* FPSR := Rd */
+			FPENV.status = REG(rd);
+			if(fpemudebug)
+				print("MOVW	R%d, FPSR\n", rd);
+			break;
+		case 3:	/* Rd := FPSR */
+			REG(rd) = FPENV.status;
+			if(fpemudebug)
+				print("MOVW	FPSR, R%d\n", rd);
+			break;
+		case 4:	/* FPCR := Rd */
+			FPENV.control = REG(rd);
+			if(fpemudebug)
+				print("MOVW	R%d, FPCR\n", rd);
+			break;
+		case 5:	/* Rd := FPCR */
+			REG(rd) = FPENV.control;
+			if(fpemudebug)
+				print("MOVW	FPCR, R%d\n", rd);
+			break;
+		}
+		return;
+	}
+
+	/*
+	 * arithmetic
+	 */
+
+	if(op & (1<<3)){	/* constant */
+		fm = &fpconst[op&7];
+		tag = 'C';
+	}else{
+		fm = &FR(op&7);
+		tag = 'F';
+	}
+	rd = (op>>12)&7;
+	o = (op>>20)&0xF;
+	if(op & (1<<15)){	/* monadic */
+		FP1 *fp;
+		fp = &optab1[o];
+		if(fp->f == nil)
+			unimp(pc, op);
+		if(fpemudebug)
+			print("%s	%c%ld,F%d\n", fp->name, tag, op&7, rd);
+		(*fp->f)(fm, &FR(rd));
+	} else {
+		FP2 *fp;
+		fp = &optab2[o];
+		if(fp->f == nil)
+			unimp(pc, op);
+		rn = (op>>16)&7;
+		if(fpemudebug)
+			print("%s	%c%ld,F%d,F%d\n", fp->name, tag, op&7, rn, rd);
+		(*fp->f)(*fm, FR(rn), &FR(rd));
+	}
+}
+
+/*
+ * returns the number of FP instructions emulated
+ */
+int
+fpiarm(Ureg *ur)
+{
+	ulong op, o;
+	FPenv *ufp;
+	int n;
+
+#ifndef R13OK
+/*	ur->type = &ur->pc+1;	/* calculate kernel sp/R13 and put it here for roff[13] */
+	ur->type = (ulong)(ur + 1);
+#endif
+	if (up == nil)
+		panic("fpiarm not in a process");
+	ufp = &up->env->fpu;	/* because all the state is in Osenv, it need not be saved/restored */
+	if(ufp->fpistate != FPACTIVE) {
+		ufp->fpistate = FPACTIVE;
+		ufp->control = 0;
+		ufp->status = (0x01<<28)|(1<<12);	/* software emulation, alternative C flag */
+		for(n = 0; n < 8; n++)
+			FR(n) = fpconst[0];
+	}
+	for(n=0;;n++){
+		op = getulong(ur->pc);
+		o = (op>>24) & 0xF;
+		if(((op>>8) & 0xF) != 1 || o != 0xE && (o&~1) != 0xC)
+			break;
+		if(condok(ur->psr, op>>28))
+			fpemu(ur->pc, op, ur, ufp);
+		ur->pc += 4;
+		if(anyhigher())
+			sched();
+	}
+	return n;
+}
--- /dev/null
+++ b/os/pxa/gpio.c
@@ -1,0 +1,88 @@
+#include	"u.h"
+#include 	"mem.h"
+#include	"../port/lib.h"
+#include 	"dat.h"
+#include	"fns.h"
+#include	"io.h"
+
+static ulong gpioreserved[3];
+static Lock gpiolock;
+
+void
+gpioreserve(int n)
+{
+	ulong mask, *r;
+
+	r = &gpioreserved[GPR(n)];
+	mask = GPB(n);
+	ilock(&gpiolock);
+	if(*r & mask)
+		panic("gpioreserve: duplicate use of GPIO %d", n);
+	*r |= mask;
+	iunlock(&gpiolock);
+}
+
+/*
+ * set direction and alternative function bits in the GPIO control register,
+ * following the configuration bits in cfg.
+ */
+void
+gpioconfig(int n, ulong cfg)
+{
+	GpioReg *g;
+	ulong o, m, *r;
+
+	m = GPB(n);
+	o = n>>5;
+	ilock(&gpiolock);
+	g = GPIOREG;
+	r = &g->gpdr[o];
+	if(cfg & Gpio_out)
+		*r |= m;
+	else
+		*r &= ~m;
+	r = &g->gafr[o*2];
+	*r = (*r & ~GPAF(n, 3)) | GPAF(n, cfg&3);
+	iunlock(&gpiolock);
+}
+
+ulong
+gpioget(int n)
+{
+	ulong mask, o;
+
+	mask = GPB(n);
+	o = GPR(n);
+	return GPIOREG->gplr[o] & mask;
+}
+
+void
+gpioset(int n, int v)
+{
+	GpioReg *g;
+	ulong mask, o;
+
+	g = GPIOREG;
+	mask = GPB(n);
+	o = GPR(n);
+	ilock(&gpiolock);
+	if(v)
+		g->gpsr[o] = mask;
+	else
+		g->gpcr[o] = mask;
+	iunlock(&gpiolock);
+}
+
+void
+gpiorelease(int n)
+{
+	ulong mask, *r;
+
+	mask = GPB(n);
+	r = &gpioreserved[GPR(n)];
+	ilock(&gpiolock);
+	if((*r & mask) != mask)
+		panic("gpiorelease: unexpected release of GPIO %d", n);
+	*r &= ~mask;
+	iunlock(&gpiolock);
+}
--- /dev/null
+++ b/os/pxa/i2c.c
@@ -1,0 +1,561 @@
+/*
+ *	basic read/write interface to PXA25x I⁲C bus (master mode)
+ *	7 bit addressing only.
+ * TO DO:
+ *	- enable unit clock
+ */
+#include	"u.h"
+#include	"../port/lib.h"
+#include	"mem.h"
+#include	"dat.h"
+#include	"fns.h"
+#include	"../port/error.h"
+#include	"io.h"
+
+typedef struct Ctlr Ctlr;
+typedef struct I2Cregs I2Cregs;
+struct I2Cregs {
+	ulong	ibmr;	/* bus monitor */
+	ulong	pad0;
+	ulong	idbr;	/* data buffer */
+	ulong	pad1;
+	ulong	icr;	/* control */
+	ulong	pad2;
+	ulong	isr;	/* status */
+	ulong	pad3;
+	ulong	isar;	/* slave address */
+};
+
+enum {
+	/* ibmr */
+	Scls=	1<<1,	/* SCL pin status */
+	Sdas=	1<<0,	/* SDA pin status */
+
+	/* icr */
+	Fm=		1<<15,	/* =0, 100 kb/sec; =1, 400 kb/sec */
+	Ur=		1<<14,	/* reset the i2c unit only */
+	Sadie=	1<<13,	/* slave address detected interrupt enable */
+	Aldie=	1<<12,	/* arbitration loss detected interrupt enable (master mode) */
+	Ssdie=	1<<11,	/* stop detected interrupt enable (slave mode) */
+	Beie=	1<<10,	/* bus error interrupt enable */
+	Irfie=	1<<9,	/* idbr receive full, interrupt enable */
+	Iteie=	1<<8,	/* idbr transmit empty interrupt enable */
+	Gcd=	1<<7,	/* disable response to general call message (slave); must be set if master uses g.c. */
+	Scle=	1<<6,	/* SCL enable: enable clock output for master mode */
+	Iue=		1<<5,	/* enable i2c (default: slave) */
+	Ma=		1<<4,	/* master abort (send STOP without data) */
+	Tb=		1<<3,	/* transfer byte on i2c bus */
+	Ack=		0<<2,
+	Nak=	1<<2,
+	Stop=	1<<1,	/* send a stop */
+	Start=	1<<0,	/* send a stop */
+
+	/* isr */
+	Bed=		1<<10,	/* bus error detected */
+	Sad=		1<<9,	/* slave address detected */
+	Gcad=	1<<8,	/* general call address detected */
+	Irf=		1<<7,	/* idbr receive full */
+	Ite=		1<<6,	/* idbr transmit empty */
+	Ald=		1<<5,	/* arbitration loss detected (multi-master) */
+	Ssd=		1<<4,	/* slave stop detected */
+	Ibb=		1<<3,	/* i2c bus is busy */
+	Ub=		1<<2,	/* unit is busy (between start and stop) */
+	Nakrcv=	1<<1,	/* nak received or sent a NAK */
+	Rwm=	1<<0,	/* =0, master transmit (or slave receive); =1, master receive (or slave transmit) */
+	Err=		Bed | Ssd,
+
+	/* isar address (0x7F bits) */
+
+	/* others */
+	Rbit =	1<<0,	/* bit in address byte denoting read */
+	Wbit=	0<<0,
+
+	MaxIO =	8192,	/* largest transfer done at once (can change) */
+	MaxSA=	2,		/* largest subaddress; could be FIFOsize */
+	Bufsize =	MaxIO,	/* subaddress bytes don't go in buffer */
+	Freq =	0,	/* set to Fm for high-speed */
+//	I2Ctimeout = 125,	/* msec (can change) */
+	I2Ctimeout = 10000,	/* msec when Chatty */
+
+	Chatty = 0,
+};
+
+#define	DPRINT	if(Chatty)print
+
+/*
+ * I2C software structures
+ */
+
+struct Ctlr {
+	Lock;
+	QLock	io;
+	int	init;
+	int	polling;	/* eg, when running before system set up */
+	I2Cregs*	regs;	/* hardware registers */
+
+	/* controller state (see below) */
+	int	status;
+	int	phase;
+	Rendez	r;
+
+	/* transfer parameters */
+	int	addr;
+	int	salen;	/* bytes remaining of subaddress */
+	int	offset;	/* sub-addressed offset */
+	int	cntl;		/* everything but transfer length */
+	int	rdcount;	/* requested read transfer size */
+	Block*	b;
+};
+
+enum {
+	/* Ctlr.state */
+	Idle,
+	Done,
+	Failed,
+	Busy,
+	Address,
+	Subaddress,
+	Read,
+	Write,
+	Halting,
+};
+
+static	Ctlr	i2cctlr[1];
+
+static void	interrupt(Ureg*, void*);
+static int readyxfer(Ctlr*, int);
+static void	rxstart(Ctlr*);
+static void	txstart(Ctlr*);
+static void	stopxfer(Ctlr*);
+static void	txoffset(Ctlr*, ulong, int);
+static int idlectlr(Ctlr*);
+
+static void
+i2cdump(char *t, I2Cregs *i2c)
+{
+	iprint("i2c %s: ibmr=%.4lux icr=%.4lux isr=%.4lux\n", t, i2c->ibmr, i2c->icr, i2c->isr);
+}
+
+static void
+initialise(I2Cregs *i2c, int eintr)
+{
+	int ctl;
+
+	/* initialisation (see p. 9-11 on) */
+	i2c->isar = 0;
+	ctl = Freq | Gcd | Scle | Iue;
+	if(eintr)
+		ctl |= Beie | Irfie;	/* Iteie set by txstart */
+	i2c->icr = ctl;
+	if(Chatty)
+		iprint("ctl=%4.4ux icr=%4.4lux\n", ctl, i2c->icr);
+}
+
+/*
+ * called by the reset routine of any driver using the IIC
+ */
+void
+i2csetup(int polling)
+{
+	I2Cregs *i2c;
+	Ctlr *ctlr;
+
+	ctlr = i2cctlr;
+	ctlr->polling = polling;
+	i2c = KADDR(PHYSI2C);
+	ctlr->regs = i2c;
+	if(!polling){
+		if(ctlr->init == 0){
+			initialise(i2c, 1);
+			ctlr->init = 1;
+			intrenable(IRQ, IRQi2c, interrupt, i2cctlr, "i2c");
+			if(Chatty)
+				i2cdump("init", i2c);
+		}
+	}else
+		initialise(i2c, 0);
+}
+
+static void
+done(Ctlr *ctlr)
+{
+	ctlr->phase = Done;
+	wakeup(&ctlr->r);
+}
+
+static void
+failed(Ctlr *ctlr)
+{
+	ctlr->phase = Failed;
+	wakeup(&ctlr->r);
+}
+
+static void
+interrupt(Ureg*, void *arg)
+{
+	int sts, idl;
+	Ctlr *ctlr;
+	Block *b;
+	I2Cregs *i2c;
+	char xx[12];
+
+	ctlr = arg;
+	i2c = ctlr->regs;
+	idl = (i2c->ibmr & 3) == 3;
+	if(Chatty && ctlr->phase != Read && ctlr->phase != Write){
+		snprint(xx, sizeof(xx), "intr %d", ctlr->phase);
+		i2cdump(xx, i2c);
+	}
+	sts = i2c->isr;
+	if(sts & (Bed | Sad | Gcad | Ald))
+		iprint("i2c: unexpected status: %.4ux", sts);
+	i2c->isr = sts;
+	ctlr->status = sts;
+	i2c->icr &= ~(Start | Stop | Nak | Ma | Iteie);
+	if(sts & Err){
+		failed(ctlr);
+		return;
+	}
+	switch(ctlr->phase){
+	default:
+		iprint("i2c: unexpected interrupt: p-%d s=%.4ux\n", ctlr->phase, sts);
+		break;
+
+	case Halting:
+		ctlr->phase = Idle;
+		break;
+
+	case Subaddress:
+		if(ctlr->salen){
+			/* push out next byte of subaddress */
+			ctlr->salen -= 8;
+			i2c->idbr = ctlr->offset >> ctlr->salen;
+			i2c->icr |= Aldie | Tb | Iteie;
+			break;
+		}
+		/* subaddress finished */
+		if(ctlr->cntl & Rbit){
+			/* must readdress if reading to change mode */
+			i2c->idbr = (ctlr->addr << 1) | Rbit;
+			i2c->icr |= Start | Tb | Iteie;
+			ctlr->phase = Address;	/* readdress */
+			break;
+		}
+		/* FALL THROUGH if writing */
+	case Address:
+		/* if not sub-addressed, rxstart/txstart */
+		if(ctlr->cntl & Rbit)
+			rxstart(ctlr);
+		else
+			txstart(ctlr);
+		break;
+
+	case Read:
+		b = ctlr->b;
+		if(b == nil)
+			panic("i2c: no buffer");
+		/* master receive: next byte */
+		if(sts & Irf){
+			ctlr->rdcount--;
+			if(b->wp < b->lim)
+				*b->wp++ = i2c->idbr;
+		}
+		if(ctlr->rdcount <= 0 || sts & Nakrcv || idl){
+			if(Chatty)
+				iprint("done: %.4ux\n", sts);
+			done(ctlr);
+			break;
+		}
+		rxstart(ctlr);
+		break;
+
+	case Write:
+		b = ctlr->b;
+		if(b == nil)
+			panic("i2c: no buffer");
+		/* account for data transmitted */
+		if(BLEN(b) <= 0 || sts & Nakrcv){
+			done(ctlr);
+			break;
+		}
+		txstart(ctlr);
+		break;
+	}
+}
+
+static int
+isdone(void *a)
+{
+	return ((Ctlr*)a)->phase < Busy;
+}
+
+static int
+i2cerror(char *s)
+{
+	DPRINT("i2c error: %s\n", s);
+	if(up)
+		error(s);
+	/* no current process, don't call error */
+	return -1;
+}
+
+static char*
+startxfer(I2Cdev *d, int op, Block *b, int n, ulong offset)
+{
+	I2Cregs *i2c;
+	Ctlr *ctlr;
+	int i, p, s;
+
+	ctlr = i2cctlr;
+	if(up){
+		qlock(&ctlr->io);
+		if(waserror()){
+			qunlock(&ctlr->io);
+			nexterror();
+		}
+	}
+	ilock(ctlr);
+	if(!idlectlr(ctlr)){
+		iunlock(ctlr);
+		if(up)
+			error("bus confused");
+		return "bus confused";
+	}
+	if(ctlr->phase >= Busy)
+		panic("i2c: ctlr busy");
+	ctlr->cntl = op;
+	ctlr->b = b;
+	ctlr->rdcount = n;
+	ctlr->addr = d->addr;
+	i2c = ctlr->regs;
+	ctlr->salen = d->salen*8;
+	ctlr->offset = offset;
+	if(ctlr->salen){
+		ctlr->phase = Subaddress;
+		op = Wbit;
+	}else
+		ctlr->phase = Address;
+	i2c->idbr = (d->addr<<1) | op;	/* 7-bit address + R/nW */
+	i2c->icr |= Start | Tb | Iteie;
+	if(Chatty)
+		i2cdump("start", i2c);
+	iunlock(ctlr);
+
+	/* wait for it */
+	if(ctlr->polling){
+		for(i=0; !isdone(ctlr); i++){
+			delay(2);
+			interrupt(nil, ctlr);
+		}
+	}else
+		tsleep(&ctlr->r, isdone, ctlr, I2Ctimeout);
+
+	ilock(ctlr);
+	p = ctlr->phase;
+	s = ctlr->status;
+	ctlr->b = nil;
+	if(ctlr->phase != Done && ctlr->phase != Idle)
+		stopxfer(ctlr);
+	iunlock(ctlr);
+
+	if(up){
+		poperror();
+		qunlock(&ctlr->io);
+	}
+	if(p != Done || s & (Bed|Ald)){	/* CHECK; time out */
+		if(s & Ald)
+			return "i2c lost arbitration";
+		if(s & Bed)
+			return "i2c bus error";
+		if(s & Ssd)
+			return "i2c transfer aborted";	/* ?? */
+		if(0 && p != Done)
+			return "i2c timed out";
+		sprint(up->genbuf, "i2c error: phase=%d status=%.4ux", p, s);
+		return up->genbuf;
+	}
+	return nil;
+}
+
+long
+i2csend(I2Cdev *d, void *buf, long n, ulong offset)
+{
+	Block *b;
+	char *e;
+
+	if(n <= 0)
+		return 0;
+	if(n > MaxIO)
+		n = MaxIO;
+
+	if(up){
+		b = allocb(n);
+		if(b == nil)
+			error(Enomem);
+		if(waserror()){
+			freeb(b);
+			nexterror();
+		}
+	}else{
+		b = iallocb(n);
+		if(b == nil)
+			return -1;
+	}
+	memmove(b->wp, buf, n);
+	b->wp += n;
+	e = startxfer(d, 0, b, 0, offset);
+	if(up)
+		poperror();
+	n -= BLEN(b);	/* residue */
+	freeb(b);
+	if(e)
+		return i2cerror(e);
+	return n;
+}
+
+long
+i2crecv(I2Cdev *d, void *buf, long n, ulong offset)
+{
+	Block *b;
+	long nr;
+	char *e;
+
+	if(n <= 0)
+		return 0;
+	if(n > MaxIO)
+		n = MaxIO;
+
+	if(up){
+		b = allocb(n);
+		if(b == nil)
+			error(Enomem);
+		if(waserror()){
+			freeb(b);
+			nexterror();
+		}
+	}else{
+		b = iallocb(n);
+		if(b == nil)
+			return -1;
+	}
+	e = startxfer(d, Rbit, b, n, offset);
+	nr = BLEN(b);
+	if(nr > 0)
+		memmove(buf, b->rp, nr);
+	if(up)
+		poperror();
+	freeb(b);
+	if(e)
+		return i2cerror(e);
+	return nr;
+}
+
+/*
+ * the controller must be locked for the following functions
+ */
+
+static int
+readyxfer(Ctlr *ctlr, int phase)
+{
+	I2Cregs *i2c;
+
+	i2c = ctlr->regs;
+	if((i2c->isr & Bed) != 0){
+		failed(ctlr);
+		return 0;
+	}
+	ctlr->phase = phase;
+	return 1;
+}
+
+/*
+ * start a master  transfer to receive the next byte of data
+ */
+static void
+rxstart(Ctlr *ctlr)
+{
+	Block *b;
+	int cntl;
+
+	b = ctlr->b;
+	if(b == nil || ctlr->rdcount<= 0){
+		done(ctlr);
+		return;
+	}
+	if(!readyxfer(ctlr, Read))
+		return;
+	cntl = Aldie | Tb;
+	if(ctlr->rdcount == 1)
+		cntl |= Stop | Nak | Iteie;	/* last byte of transfer */
+	ctlr->regs->icr |= cntl;
+}
+
+/*
+ * start a master transfer to send the next chunk of data
+ */
+static void
+txstart(Ctlr *ctlr)
+{
+	Block *b;
+	int cntl;
+	long nb;
+	I2Cregs *i2c;
+
+	b = ctlr->b;
+	if(b == nil || (nb = BLEN(b)) <= 0){
+		done(ctlr);
+		return;
+	}
+	if(!readyxfer(ctlr, Write))
+		return;
+	i2c = ctlr->regs;
+	i2c->idbr = *b->rp++;
+	cntl = Aldie | Tb | Iteie;
+	if(nb == 1)
+		cntl |= Stop;
+	i2c->icr |= cntl;
+}
+
+/*
+ * stop a transfer if one is in progress
+ */
+static void
+stopxfer(Ctlr *ctlr)
+{
+	I2Cregs *i2c;
+
+	i2c = ctlr->regs;
+	if((i2c->isr & Ub) == 0){
+		ctlr->phase = Idle;
+		return;
+	}
+	if((i2c->isr & Ibb) == 0 && ctlr->phase != Halting){
+		ctlr->phase = Halting;	/* interrupt will clear the state */
+		i2c->icr |= Ma;
+	}
+	/* if that doesn't clear it by the next operation, idlectlr will do so below */
+}
+
+static int
+idlectlr(Ctlr *ctlr)
+{
+	I2Cregs *i2c;
+
+	i2c = ctlr->regs;
+	if((i2c->isr & Ibb) == 0){
+		if((i2c->isr & Ub) == 0){
+			ctlr->phase = Idle;
+			return 1;
+		}
+		iprint("i2c: bus free, ctlr busy: isr=%.4lux icr=%.4lux\n", i2c->isr, i2c->icr);
+	}
+	/* hit it with the hammer, soft reset */
+	iprint("i2c: soft reset\n");
+	i2c->icr = Ur;
+	iunlock(ctlr);
+	delay(1);
+	ilock(ctlr);
+	initialise(i2c, !ctlr->polling);
+	ctlr->phase = Idle;
+	return (i2c->isr & (Ibb | Ub)) == 0;
+}
--- /dev/null
+++ b/os/pxa/l.s
@@ -1,0 +1,548 @@
+#include "mem.h"
+
+#define	CPWAIT	MRC	CpMMU, 0, R2, C(2), C(0), 0; MOVW R2, R2; SUB $4, R15
+
+/*
+ * Entered here from the boot loader with
+ *	supervisor mode, interrupts disabled;
+ *	MMU, IDC and WB disabled.
+ */
+
+TEXT _startup(SB), $-4
+	MOVW		$setR12(SB), R12 	/* static base (SB) */
+	MOVW		$(PsrDirq|PsrDfiq|PsrMsvc), R1	/* ensure SVC mode with interrupts disabled */
+	MOVW		R1, CPSR
+	MOVW		$(MACHADDR+BY2PG-4), R13	/* stack; 4 bytes for link */
+
+	BL		main(SB)
+dead:
+	B	dead
+	BL	_div(SB)			/* hack to get _div etc loaded */
+
+TEXT	getcpuid(SB), $-4
+	MRC		CpMMU, 0, R0, C(CpCPUID), C(0)
+	RET
+
+TEXT mmugetctl(SB), $-4
+	MRC		CpMMU, 0, R0, C(CpControl), C(0)
+	RET	
+
+TEXT	mmugetdac(SB), $-4
+	MRC		CpMMU, 0, R0, C(CpDAC), C(0)
+	RET
+
+TEXT	mmugetfar(SB), $-4
+	MRC		CpMMU, 0, R0, C(CpFAR), C(0)
+	RET
+
+TEXT	mmugetfsr(SB), $-4
+	MRC		CpMMU, 0, R0, C(CpFSR), C(0)
+	RET
+
+TEXT	mmuputdac(SB), $-4
+	MCR		CpMMU, 0, R0, C(CpDAC), C(0)
+	CPWAIT
+	RET
+
+TEXT	mmuputfsr(SB), $-4
+	MCR		CpMMU, 0, R0, C(CpFSR), C(0)
+	CPWAIT
+	RET
+
+TEXT	mmuputttb(SB), $-4
+	MCR		CpMMU, 0, R0, C(CpTTB), C(0)
+	CPWAIT
+	RET
+
+TEXT mmuputctl(SB), $-4
+	MCR		CpMMU, 0, R0, C(CpControl), C(0)
+	CPWAIT
+	RET	
+
+TEXT tlbinvalidateall(SB), $-4
+	MCR		CpMMU, 0, R0, C(CpTLBops), C(7)
+	CPWAIT
+	RET
+
+TEXT itlbinvalidate(SB), $-4
+	MCR		CpMMU, 0, R0, C(CpTLBops), C(5), 1
+	CPWAIT
+	RET
+
+TEXT dtlbinvalidate(SB), $-4
+	MCR		CpMMU, 0, R0, C(CpTLBops), C(6), 1
+	CPWAIT
+	RET
+
+TEXT mmuenable(SB), $-4
+
+	MOVW	$1, R1
+	MCR	CpMMU, 0, R1, C(CpDAC), C(3)	/* set domain 0 to client */
+
+	/* disable and invalidate all caches and TLB's before (re-)enabling MMU */
+	MOVW	$(CpCwpd | CpCsystem), R1
+	MRC		CpMMU, 0, R1, C(CpControl), C(0)
+	CPWAIT
+
+	MOVW	$0, R1				/* disable everything */
+	MCR	CpMMU, 0, R1, C(CpCacheCtl), C(7), 0	/* invalidate I&D Caches and BTB */
+	MCR	CpMMU, 0, R1, C(CpCacheCtl), C(10), 4	/* drain write and fill buffer */
+	MCR	CpMMU, 0, R1, C(CpTLBops), C(7), 0	/* invalidate I&D TLB */
+
+	/* enable desired mmu mode (R0) */
+	MCR	CpMMU, 0, R0, C(1), C(0)
+	CPWAIT
+	RET				/* start running in remapped area */
+
+TEXT setr13(SB), $-4
+	MOVW		4(FP), R1
+
+	MOVW		CPSR, R2
+	BIC		$PsrMask, R2, R3
+	ORR		R0, R3
+	MOVW		R3, CPSR
+
+	MOVW		R13, R0
+	MOVW		R1, R13
+
+	MOVW		R2, CPSR
+	RET
+
+TEXT vectors(SB), $-4
+	MOVW	0x18(R15), R15			/* reset */
+	MOVW	0x18(R15), R15			/* undefined */
+	MOVW	0x18(R15), R15			/* SWI */
+	MOVW	0x18(R15), R15			/* prefetch abort */
+	MOVW	0x18(R15), R15			/* data abort */
+	MOVW	0x18(R15), R15			/* reserved */
+	MOVW	0x18(R15), R15			/* IRQ */
+	MOVW	0x18(R15), R15			/* FIQ */
+
+TEXT vtable(SB), $-4
+	WORD	$_vsvc(SB)			/* reset, in svc mode already */
+	WORD	$_vund(SB)			/* undefined, switch to svc mode */
+	WORD	$_vsvc(SB)			/* swi, in svc mode already */
+	WORD	$_vpab(SB)			/* prefetch abort, switch to svc mode */
+	WORD	$_vdab(SB)			/* data abort, switch to svc mode */
+	WORD	$_vsvc(SB)			/* reserved */
+	WORD	$_virq(SB)			/* IRQ, switch to svc mode */
+	WORD	$_vfiq(SB)			/* FIQ, switch to svc mode */
+
+TEXT _vund(SB), $-4			
+	MOVM.DB		[R0-R3], (R13)
+	MOVW		$PsrMund, R0
+	B		_vswitch
+
+TEXT _vsvc(SB), $-4				
+	MOVW.W		R14, -4(R13)
+	MOVW		CPSR, R14
+	MOVW.W		R14, -4(R13)
+	BIC		$PsrMask, R14
+	ORR		$(PsrDirq|PsrDfiq|PsrMsvc), R14
+	MOVW		R14, CPSR
+	MOVW		$PsrMsvc, R14
+	MOVW.W		R14, -4(R13)
+	B		_vsaveu
+
+TEXT _vpab(SB), $-4			
+	MOVM.DB		[R0-R3], (R13)
+	MOVW		$PsrMabt, R0
+	B		_vswitch
+
+TEXT _vdab(SB), $-4	
+	MOVM.DB		[R0-R3], (R13)
+	MOVW		$(PsrMabt+1), R0
+	B		_vswitch
+
+TEXT _vfiq(SB), $-4				/* FIQ */
+	MOVM.DB		[R0-R3], (R13)
+	MOVW		$PsrMfiq, R0
+	B		_vswitch
+
+TEXT _virq(SB), $-4				/* IRQ */
+	MOVM.DB		[R0-R3], (R13)
+	MOVW		$PsrMirq, R0
+
+_vswitch:					/* switch to svc mode */
+	MOVW		SPSR, R1
+	MOVW		R14, R2
+	MOVW		R13, R3
+
+	MOVW		CPSR, R14
+	BIC		$PsrMask, R14
+	ORR		$(PsrDirq|PsrDfiq|PsrMsvc), R14
+	MOVW		R14, CPSR
+
+	MOVM.DB.W 	[R0-R2], (R13)
+	MOVM.DB	  	(R3), [R0-R3]
+
+_vsaveu:						/* Save Registers */
+	MOVW.W		R14, -4(R13)			/* save link */
+	MCR		CpMMU, 0, R0, C(0), C(0), 0	
+
+	SUB		$8, R13
+	MOVM.DB.W 	[R0-R12], (R13)
+
+	MOVW		R0, R0				/* gratuitous noop */
+
+	MOVW		$setR12(SB), R12		/* static base (SB) */
+	MOVW		R13, R0				/* argument is ureg */
+	SUB		$8, R13				/* space for arg+lnk*/
+	BL		trap(SB)
+
+_vrfe:							/* Restore Regs */
+	MOVW		CPSR, R0			/* splhi on return */
+	ORR		$(PsrDirq|PsrDfiq), R0, R1
+	MOVW		R1, CPSR
+	ADD		$(8+4*15), R13		/* [r0-R14]+argument+link */
+	MOVW		(R13), R14			/* restore link */
+	MOVW		8(R13), R0
+	MOVW		R0, SPSR
+	MOVM.DB.S 	(R13), [R0-R14]		/* restore user registers */
+	MOVW		R0, R0				/* gratuitous nop */
+	ADD		$12, R13		/* skip saved link+type+SPSR*/
+	RFE					/* MOVM.IA.S.W (R13), [R15] */
+	
+TEXT splhi(SB), $-4					
+	MOVW		CPSR, R0
+	ORR		$(PsrDirq), R0, R1
+	MOVW		R1, CPSR
+	MOVW	$(MACHADDR), R6
+	MOVW	R14, (R6)	/* m->splpc */
+	RET
+
+TEXT spllo(SB), $-4
+	MOVW		CPSR, R0
+	BIC		$(PsrDirq|PsrDfiq), R0, R1
+	MOVW		R1, CPSR
+	RET
+
+TEXT splx(SB), $-4
+	MOVW	$(MACHADDR), R6
+	MOVW	R14, (R6)	/* m->splpc */
+
+TEXT splxpc(SB), $-4
+	MOVW		R0, R1
+	MOVW		CPSR, R0
+	MOVW		R1, CPSR
+	RET
+
+TEXT spldone(SB), $-4
+	RET
+
+TEXT islo(SB), $-4
+	MOVW		CPSR, R0
+	AND		$(PsrDirq), R0
+	EOR		$(PsrDirq), R0
+	RET
+
+TEXT splfhi(SB), $-4					
+	MOVW		CPSR, R0
+	ORR		$(PsrDfiq|PsrDirq), R0, R1
+	MOVW		R1, CPSR
+	RET
+
+TEXT splflo(SB), $-4
+	MOVW		CPSR, R0
+	BIC		$(PsrDfiq), R0, R1
+	MOVW		R1, CPSR
+	RET
+
+TEXT getcpsr(SB), $-4
+	MOVW		CPSR, R0
+	RET
+
+TEXT getspsr(SB), $-4
+	MOVW		SPSR, R0
+	RET
+
+TEXT getcallerpc(SB), $-4
+	MOVW		0(R13), R0
+	RET
+
+TEXT _tas(SB), $-4
+	MOVW		R0, R1
+	MOVW		$0xDEADDEAD, R2
+	SWPW		R2, (R1), R0
+	RET
+
+TEXT setlabel(SB), $-4
+	MOVW		R13, 0(R0)		/* sp */
+	MOVW		R14, 4(R0)		/* pc */
+	MOVW		$0, R0
+	RET
+
+TEXT gotolabel(SB), $-4
+	MOVW		0(R0), R13		/* sp */
+	MOVW		4(R0), R14		/* pc */
+	MOVW		$1, R0
+	RET
+
+/*
+ * flush (invalidate) the whole icache
+ */
+TEXT icflushall(SB), $-4
+_icflushall:
+	MCR	 	CpMMU, 0, R0, C(CpCacheCtl), C(5), 0	/* clean i-cache and branch buffer */
+	CPWAIT
+	RET
+
+/*
+ * invalidate part of i-cache and  invalidate branch target buffer
+ */
+TEXT	icflush(SB), $-4
+	MOVW	4(FP), R1
+	CMP		$(CACHESIZE/2), R1
+	BGE		_icflushall
+	ADD		R0, R1
+	BIC		$(CACHELINESZ-1), R0
+icflush1:
+	MCR		CpMMU, 0, R0, C(CpCacheCtl), C(5), 1	/* clean entry */
+	ADD		$CACHELINESZ, R0
+	CMP		R1, R0
+	BLO	icflush1
+	MCR		CpMMU, 0, R0, C(CpCacheCtl), C(5), 6	/* invalidate branch target buffer */
+	CPWAIT							
+	RET
+
+/*
+ * write back whole data cache and drain write buffer
+ */
+TEXT dcflushall(SB), $-4
+_dcflushall:
+	MOVW		$(DCFADDR), R0
+	ADD		$CACHESIZE, R0, R1
+dcflushall1:
+	MCR	CpMMU, 0, R0, C(CpCacheCtl), C(2), 5	/* allocate line */
+	ADD		$CACHELINESZ, R0
+	CMP		R1,R0
+	BNE		dcflushall1
+	MCR		CpMMU, 0, R0, C(CpCacheCtl), C(10), 4	/* drain write buffer */
+	CPWAIT
+	RET
+
+/*
+ * write back a given region and drain write buffer
+ */
+TEXT	dcflush(SB), $-4
+	MOVW	4(FP), R1
+	CMP		$(CACHESIZE/2), R1
+	BGE		_dcflushall
+	ADD		R0, R1
+	BIC		$(CACHELINESZ-1), R0
+dcflush1:
+	MCR		CpMMU, 0, R0, C(CpCacheCtl), C(10), 1	/* clean entry */
+	ADD		$CACHELINESZ, R0
+	CMP		R1, R0
+	BLO	dcflush1
+	MCR		CpMMU, 0, R0, C(CpCacheCtl), C(10), 4	/* drain write buffer */
+	CPWAIT							
+	RET
+
+#ifdef NOTYET
+/*
+ * write back mini data cache
+ * TO DO: need to allocate pair of unused 2k blocks and read them alternately
+ */
+TEXT minidcflush(SB), $-4		
+	MOVW		$(MCFADDR), R0
+	ADD		$(16*CACHELINESZ), R0, R1
+
+wbbflush:
+	MOVW.P	CACHELINESZ(R0), R2
+	CMP		R1,R0
+	BNE		wbbflush
+	MCR		CpMMU, 0, R0, C(CpCacheCtl), C(10), 4	/* drain write buffer */
+	CPWAIT							
+	RET
+#endif
+
+/*
+ * invalidate data caches (main and mini)
+ */
+TEXT dcinval(SB), $-4
+	MCR		CpMMU, 0, R0, C(CpCacheCtl), C(6), 0
+	CPWAIT
+	RET
+
+/* for devboot */
+TEXT	gotopc(SB), $-4
+	MOVW	R0, R1
+	MOVW	$0, R0
+	MOVW	R1, PC
+	RET
+
+TEXT	idle(SB), $-4
+	MOVW	$1, R0	/* idle mode */
+	MCR		CpPWR, 0, R0, C(7), C(0), 0
+	RET
+
+TEXT	getcclkcfg(SB), $-4
+	MRC	CpPWR, 0, R0, C(6), C(0), 0
+	RET
+
+TEXT	putcclkcfg(SB), $-4
+	MCR	CpPWR, 0, R0, C(6), C(0), 0
+	RET
+
+#ifdef NOTYET
+/*
+ * the following code is considerably modified from the
+ * sleep code by nemo@gsyc.escet.urjc.es for Plan 9, but that's
+ * where it started.  in particular, there's no need to save regs in all modes,
+ * since here we're called from kernel main level (a kproc) so nothing is live;
+ * the only regs needed are the various R13s, but we have trap restore them on resume.
+ * similarly there's no need to save SPSR, CPSR, etc. (even CpPID isn't really needed here).
+ */
+
+#define MDREFR_k1db2	(1<<22)
+#define MDREFR_slfrsh	(1<<31)	/* self refresh */
+#define MDREFR_e1pin	(1<<20)
+#define MSC_rt		((3<<16)|(3<<0))
+#define MDCFNG_de	((3<<16)|(3<<0))	/* dram enable, banks (3 2), (1 0) */
+
+TEXT suspenditall(SB), $-4
+	MOVW.W	R14, -4(R13)
+	/* push mmu state on stack */
+	MRC		CpMMU, 0, R1, C(CpDAC), C(0)
+	MRC		CpMMU, 0, R2, C(CpTTB), C(0)
+	MRC		CpMMU, 0, R3, C(CpPID), C(0)
+	MRC		CpMMU, 0, R4, C(CpControl), C(0)
+	MOVM.DB.W	[R1-R4], (R13)
+	/* if pspr by convention held a stack pointer pointing to a pc we wouldn't need power_state */
+	MOVW	R13, power_state+0(SB)
+
+	BL	dcflushall(SB)
+	/* don't write DRAM after this */
+
+	MOVW	$PHYSPOWER, R3
+
+	/* put resume address in scratchpad for boot loader */
+	MOVW	$power_resume+0(SB), R2
+	MOVW	R2, 0x8(R3)	/* pspr */
+
+	/* disable clock switching */
+	MCR   	CpPWR, 0, R1, C(CpTest), C(0x2), 2
+
+	/* adjust mem timing first to avoid processor bug causing hang */
+	MOVW	$MDCNFG, R5
+	MOVW	0x1c(R5), R2
+	ORR	$(MDREFR_k1db2), R2
+	MOVW	R2, 0x1c(R5)
+
+	/* set PLL to lower speed w/ delay */
+	MOVW	$(120*206),R0
+l11:	SUB	$1,R0
+	BGT	l11
+	MOVW	$0, R2
+	MOVW	R2, 0x14(R3)	/* ppcr */
+	MOVW	$(120*206),R0
+l12:	SUB	$1,R0
+	BGT	l12
+
+	/*
+	 *  SA1110 fix for various suspend bugs in pre-B4 chips (err. 14-16, 18):
+	 * 	set up register values here for use in code below that is at most
+	 *	one cache line (32 bytes) long, to run without DRAM.
+	 */
+	/* 1. clear RT in MSCx (R1, R7, R8) without changing other bits */
+	MOVW	0x10(R5), R1	/* MSC0 */
+	BIC	$(MSC_rt), R1
+	MOVW	0x14(R5), R7	/* MSC1 */
+	BIC	$(MSC_rt), R7
+	MOVW	0x2c(R5), R8	/* MSC2 */
+	BIC	$(MSC_rt), R8
+	/* 2. clear DRI0-11 in MDREFR (R4) without changing other bits */
+	MOVW	0x1c(R5), R4
+	BIC	$(0xfff0), R4
+	/* 3. set SLFRSH in MDREFR (R6) without changing other bits */
+	ORR	$(MDREFR_slfrsh), R4, R6
+	/* 4. clear DE in MDCNFG (R9), and any other bits desired */
+	MOVW	0x0(R5), R9
+	BIC	$(MDCFNG_de), R9
+	/* 5. clear SLFRSH and E1PIN (R10), without changing other bits */
+	BIC	$(MDREFR_slfrsh), R4, R10
+	BIC	$(MDREFR_e1pin), R10
+	/* 6. force sleep mode in PMCR (R2) */
+	MOVW	$1,R2
+	MOVW	suspendcode+0(SB), R0
+	B	(R0)	/* off to do it */
+
+/*
+ * the following is copied by trap.c to a cache-aligned area (suspendcode),
+ * so that it can all run during disabling of DRAM
+ */
+TEXT _suspendcode(SB), $-4
+	/* 1: clear RT field of all MSCx registers */
+	MOVW	R1, 0x10(R5)
+	MOVW	R7, 0x14(R5)
+	MOVW	R8, 0x2c(R5)
+	/* 2: clear DRI field in MDREFR */
+	MOVW	R4, 0x1c(R5)
+	/* 3: set SLFRSH bit in MDREFR */
+	MOVW	R6, 0x1c(R5)
+	/* 4: clear DE bits in MDCFNG */
+	MOVW	R9, 0x0(R5)
+	/* 5: clear SLFRSH and E1PIN in MDREFR */
+	MOVW	R10, 0x1c(R5)
+	/* 6: suspend request */
+	MOVW	R2, 0x0(R3)	 /* pmcr */
+	B		0(PC)		/* wait for it */
+
+/*
+ * The boot loader comes here after the resume.
+ */
+TEXT power_resume(SB), $-4
+	MOVW	$(PsrDirq|PsrDfiq|PsrMsvc), R0
+	MOVW	R0, CPSR		/* svc mode, interrupts off */
+	MOVW	$setR12(SB), R12
+
+	/* flush caches */
+	MCR		CpMMU, 0, R0, C(CpCacheCtl), C(7), 0
+	/* drain prefetch */
+	MOVW	R0,R0						
+	MOVW	R0,R0
+	MOVW	R0,R0
+	MOVW	R0,R0
+	/* drain write buffer */
+	MCR		CpMMU, 0, R0, C(CpCacheCtl), C(10), 4
+	/* flush tlb */
+	MCR		CpMMU, 0, R0, C(CpTLBops), C(7)
+
+	/* restore state */
+	MOVW	power_state+0(SB), R13
+	MOVM.IA.W	(R13), [R1-R4]
+	MOVW.P	4(R13), R14
+
+	MCR		CpMMU, 0, R1, C(CpDAC), C(0x0)
+	MCR		CpMMU, 0, R2, C(CpTTB), C(0x0)
+	MCR		CpMMU, 0, R3, C(CpPID), C(0x0)
+	MCR		CpMMU, 0, R4, C(CpControl), C(0x0)	/* enable cache and mmu */
+	MOVW	R0,R0						
+	MOVW	R0,R0
+	MOVW	R0,R0
+	MOVW	R0,R0
+	/* flush i&d caches */
+	MCR		CpMMU, 0, R0, C(CpCacheCtl), C(7)
+	/* flush tlb */
+	MCR		CpMMU, 0, R0, C(CpTLBops), C(7)
+	/* drain prefetch */
+	MOVW	R0,R0						
+	MOVW	R0,R0
+	MOVW	R0,R0
+	MOVW	R0,R0
+	/* enable clock switching */
+	MCR   	CpPWR, 0, R1, C(CpTest), C(1), 2
+	RET
+#endif
+
+	GLOBL	power_state+0(SB), $4
+
+#ifdef YYY
+/* for debugging sleep code: */
+TEXT fastreset(SB), $-4
+	MOVW	$PHYSRESET, R7
+	MOVW	$1, R1
+	MOVW	R1, (R7)
+	RET
+#endif
--- /dev/null
+++ b/os/pxa/mmu.c
@@ -1,0 +1,252 @@
+#include "u.h"
+#include "../port/lib.h"
+#include "mem.h"
+#include "dat.h"
+#include "fns.h"
+
+/*
+ * Small pages:
+ *	L1: 12-bit index -> 4096 descriptors -> 16Kb
+ *	L2:  8-bit index ->  256 descriptors ->  1Kb
+ * Each L2 descriptor has access permissions for 4 1Kb sub-pages.
+ *
+ *	TTB + L1Tx gives address of L1 descriptor
+ *	L1 descriptor gives PTBA
+ *	PTBA + L2Tx gives address of L2 descriptor
+ *	L2 descriptor gives PBA
+ *
+ * on the Xscale, X, C & B are interpreted as follows:
+ * X = 0:
+ *	C=0 B=0	uncached, unbuffered, stall until data access complete
+ *	C=0 B=1	uncached, buffered (different from Strongarm)
+ *	C=1 B=0	cached, buffered, write through, read allocate (different from Strongarm)
+ *	C=1 B=1	cached, buffered, write back, read allocate
+ * X = 1:
+ *	C=0 B=0	undefined
+ *	C=0 B=1	uncached, buffered, writes will not coalesce
+ *	C=1 B=0	mini data cache (policy set by auxiliary control reg)
+ *	C=1 B=1	cached, buffered, write back, read/write allocate
+ * and the i-cache uses only the C bit (cached if non-zero).
+ */
+#define TTB(pa)	((pa) & ~0x3FFF)	/* translation table base */
+#define L1x(pa)	(((pa)>>20) & 0xFFF)	/* L1 table index */
+#define PTBA(pa)	((pa) & ~0x3FF)		/* page table base address */
+#define L2x(pa)	(((pa)>>12) & 0xFF)	/* L2 table index */
+#define PBA(pa)	((pa) & ~0xFFF)		/* page base address */
+#define SBA(pa)	((pa) & ~0xFFFFF)	/* section base address */
+
+enum {
+	/* sizes */
+	Section=	1<<20,
+	LargePage=	1<<16,
+	SmallPage=	1<<12,
+	EsmallPage=	1<<10,
+	SectionPages = Section/SmallPage,
+	PtAlign = 1<<10,
+
+	/* L1 descriptor format */
+	L1type= 	3<<0,	/* mask for type */
+	L1page= 	1<<0,		/* descriptor is for L2 pages */
+	L1section= 2<<0,			/* descriptor is for section */
+	L1fpage=	3<<0,	/* descriptor is for fine (1k) L2 pages */
+	L1buffered=	1<<2,
+	L1cached=	1<<3,
+	L1P=	1<<9,	/* application-processor specific */
+	L1sectionX=	1<<12,	/* X bit in section descriptor */
+	L1minicache=	(L1sectionX | L1cached),
+
+	/* L2 descriptor format for coarse page table */
+	L2type=	3<<0,	/* mask for type */
+	L2invalid=	0<<0,
+	L2large=	1<<0,			/* large page */
+	L2small=	2<<0,			/* small page */
+	L2esmall=	3<<0,	/* extended small page */
+	L2buffered=	1<<2,
+	L2cached=	1<<3,
+	/* then access permissions */
+	L2smallX=	1<<6,
+	L2largeX=	1<<12,
+
+	/* domains */
+	Dnone=	0,
+	Dclient=	1,
+	Dmanager=	3,
+
+	/* access permissions */
+	APsro=	0,	/* supervisor ro if S|R */
+	APsrw=	1,	/* supervisor rw */
+	APuro=	2,	/* supervisor rw + user ro */
+	APurw=	3,	/* supervisor rw + user rw */
+
+	MINICACHED = 0x10000000,
+};
+
+#define L1dom(d)	(((d) & 0xF)<<5)	/* L1 domain */
+#define AP(i, v)	((v)<<(((i)*2)+4))	/* access permissions */
+#define L1AP(v)	AP(3, (v))
+#define L2AP(v)	AP(3, (v))|AP(2, (v))|AP(1, (v))|AP(0, (v))
+
+#define L1krw	(L1AP(APsrw) | L1dom(0))
+
+/*
+ * return physical address corresponding to a given virtual address,
+ * or 0 if there is no such address
+ */
+ulong
+va2pa(void *v)
+{
+	int idx;
+	ulong pte, ste, *ttb;
+
+	idx = L1x((ulong)v);
+	ttb = (ulong*)KTTB;
+	ste = ttb[idx];
+	switch(ste & L1type) {
+	case L1section:
+		return SBA(ste)|((ulong)v & 0x000fffff);
+	case L1page:
+		pte = ((ulong *)PTBA(ste))[L2x((ulong)v)]; 
+		switch(pte & 3) {
+		case L2large:
+			return (pte & 0xffff0000)|((ulong)v & 0x0000ffff);
+		case L2small:
+			return (pte & 0xfffff000)|((ulong)v & 0x00000fff);
+		}
+	}
+	return 0;
+}
+
+/* for debugging */
+void
+prs(char *s)
+{
+	for(; *s; s++)
+		uartputc(*s);
+}
+
+void
+pr16(ulong n)
+{
+	int i, c;
+
+	for(i=28; i>=0; i-=4){
+		c = (n>>i) & 0xF;
+		if(c >= 0 && c <= 9)
+			c += '0';
+		else
+			c += 'A'-10;
+		uartputc(c);
+	}
+}
+
+void
+xdelay(int n)
+{
+	int j;
+
+	for(j=0; j<1000000/4; j++)
+		n++;
+	USED(n);
+}
+
+void*
+mmuphysmap(ulong phys, ulong)
+{
+	ulong *ttb;
+	void *va;
+
+	ttb = (ulong*)KTTB;
+	va = KADDR(phys);
+	ttb[L1x((ulong)va)] = phys | L1krw | L1section;
+	return va;
+}
+
+/*
+ * Set a 1-1 map of virtual to physical memory, except:
+ *	doubly-map page0 at the alternative interrupt vector address,
+ * 	doubly-map physical memory at KZERO+256*MB as uncached but buffered,
+ *	map flash to virtual space away from 0,
+ *	disable access to 0 (nil pointers).
+ *
+ * Other section maps are added later as required by mmuphysmap.
+ */
+void
+mmuinit(void)
+{
+	int i;
+	ulong *ttb, *ptable, va;
+
+	ttb = (ulong*)KTTB;
+	for(i=0; i<L1x(0x10000000); i++)
+		ttb[i] = 0;
+	for(; i < 0x1000; i++)
+		ttb[i] = (i<<20) | L1krw | L1section;
+
+	/* cached dram at normal kernel addresses */
+	for(va = KZERO; va < KZERO+64*MB; va += MB)
+		ttb[L1x(va)] = va | L1krw | L1section | L1cached | L1buffered;
+
+	/* aliases for uncached dram */
+	for(i = 0; i < 64*MB; i += MB)
+		ttb[L1x(UCDRAMZERO+i)] = (PHYSMEM0+i) | L1krw | L1section;
+
+	/* TO DO: make the text read only */
+
+	/* minicached region; used for frame buffer (if present) */
+	if(0)
+	for(va = KZERO; va < KZERO+64*MB; va += MB)
+		ttb[L1x(va|MINICACHED)] = va | L1krw  | L1minicache | L1section;
+
+	ttb[L1x(DCFADDR)] |= L1cached | L1buffered;	/* cached and buffered for cache writeback */
+
+#ifdef NOTYET
+	/* TO DO: mini cache writeback */
+	ttb[L1x(MCFADDR)] |= L1minicache;	/* cached and unbuffered for minicache writeback */
+#endif
+
+	/* remap flash */
+	for(i=0; i<32*MB; i+=MB)
+		ttb[L1x(FLASHMEM+i)] = (PHYSFLASH0+i) | L1krw | L1section;	/* we'll make flash uncached for now */
+
+	/*
+	 * build page table for alternative vector page, mapping trap vectors in *page0
+	 */
+	ptable = xspanalloc(SectionPages*sizeof(*ptable), PtAlign, 0);
+	ptable[L2x(AIVECADDR)] = PADDR(page0) | L2AP(APsrw) | L2cached | L2buffered | L2small;
+	ttb[L1x(AIVECADDR)] = PADDR(ptable) | L1page;
+	mmuputttb(KTTB);
+	mmuputdac(Dclient);
+	mmuenable(CpCaltivec | CpCIcache | CpCsystem | CpCwpd | CpCDcache | CpCmmu);
+}
+
+/*
+ * flush data in a given address range to memory
+ * and invalidate the region in the instruction cache.
+ */
+int
+segflush(void *a, ulong n)
+{
+	dcflush(a, n);
+	icflush(a, n);
+	return 0;
+}
+
+#ifdef NOTYET
+/*
+ * map an address to cached but unbuffered memory
+ * forcing load allocations to the mini data cache.
+ * the address a must be in a region that is cache line aligned
+ * with a length that is a multiple of the cache line size
+ */
+void *
+minicached(void *a)
+{
+	if(conf.useminicache == 0)
+		return a;
+	/* must flush and invalidate any data lingering in main cache */
+	dcflushall();
+	minidcflush();
+	dcinval();
+	return (void*)((ulong)a | MINICACHED);
+}
+#endif
--- /dev/null
+++ b/os/pxa/pxaio.h
@@ -1,0 +1,383 @@
+typedef struct I2Cdev I2Cdev;
+typedef struct PCMconftab PCMconftab;
+typedef struct PCMmap	PCMmap;
+typedef struct PCMslot	PCMslot;
+
+#define INTRREG 	((IntrReg*)PHYSINTR)
+typedef struct IntrReg IntrReg;
+struct IntrReg {
+	ulong	icip;	/*  IRQ pending */
+	ulong	icmr;	/*  mask */
+	ulong	iclr;	/*  level */
+	ulong	icfp;	/*  FIQ pending */
+	ulong	icpr;	/*  pending */
+	ulong	iccr;	/*  control */
+};
+
+/*
+ *  types of interrupts
+ */
+enum
+{
+	GPIOrising,
+	GPIOfalling,
+	GPIOboth,
+	IRQ,
+};
+
+enum {
+	/* first-level interrupts (table 4-36) */
+	IRQrtc=	31,
+	IRQhz=	30,
+	IRQtimer3=	29,
+	IRQtimer2=	28,
+	IRQtimer1=	27,
+	IRQtimer0=	26,
+	IRQdma=	25,
+	IRQssp=	24,
+	IRQmmc=	23,
+	IRQffuart=	22,
+	IRQbtuart=	21,
+	IRQstuart=	20,
+	IRQicp=	19,
+	IRQi2c=	18,
+	IRQlcd=	17,
+	IRQnssp=	16,
+	IRQac97=	14,
+	IRQi2s=	13,
+	IRQpmu=	12,
+	IRQusb=	11,
+	IRQgpio=	10,
+	IRQgpio1=	9,
+	IRQgpio0=	8,
+	IRQhwuart=	7,
+};
+
+#define GPIOREG		((GpioReg*)PHYSGPIO)
+typedef struct GpioReg GpioReg;
+struct GpioReg {
+	ulong gplr[3];
+	ulong gpdr[3];
+	ulong gpsr[3];
+	ulong gpcr[3];
+	ulong grer[3];
+	ulong gfer[3];
+	ulong gedr[3];
+	ulong gafr[6];
+};
+
+enum {
+	/* GPIO alternative functions if gafr bits set (see table 4-1, pp. 4-3 to 4-6) */
+	GPIO_GP_RST_1_i=	1,	/* active low GP_reset */
+	GPIO_FFRXD_1_i=	34,	/* FFUART receive */
+	GPIO_FFTXD_2_o=	39,	/* FFUART transmit */
+
+	MaxGPIObit=	84,
+	MaxGPIOIRQ=	1,
+};
+#define	GPB(n)	(1<<((n)&31))
+#define	GPR(n)	((n)>>5)
+#define	GPAF(n,v)	((v)<<(((n)&15)*2))
+
+void	gpioreserve(int);
+void	gpioconfig(int, ulong);
+ulong	gpioget(int);
+void	gpioset(int, int);
+void	gpiorelease(int);
+
+enum {
+	/* software configuration bits for gpioconfig */
+	Gpio_gpio=	0<<0,
+	Gpio_Alt1=	1<<0,
+	Gpio_Alt2=	2<<0,
+	Gpio_Alt3=	3<<0,
+	Gpio_in=		1<<2,
+	Gpio_out=	1<<3,
+};
+
+/*
+ * software structures used by ../port/devi2c.c and iic.c
+ */
+struct I2Cdev {
+	int	addr;
+	int	salen;	/* length in bytes of subaddress, if used; 0 otherwise */
+	int	tenbit;	/* 10-bit addresses */
+};
+
+long	i2crecv(I2Cdev*, void*, long, ulong);
+long	i2csend(I2Cdev*, void*, long, ulong);
+void	i2csetup(int);
+
+#define COREREG	((Coreregs*)PHYSCORE)
+typedef struct Coreregs Coreregs;
+struct Coreregs {
+	ulong	cccr;	/* core clock config */
+	ulong	cken;	/* clock enable */
+	ulong	oscc;	/* oscillator configuration */
+};
+
+#define RTCREG		((RTCreg*)PHYSRTC)
+typedef struct RTCreg RTCreg;
+struct RTCreg {
+	ulong	rcnr;	/*  count */
+	ulong	rtar;	/*  alarm */
+	ulong	rtsr;	/*  status */
+	ulong	rttr;	/*  trim */
+};
+
+#define OSTMRREG	((OstmrReg*)PHYSOSTMR)
+typedef struct OstmrReg OstmrReg;
+struct OstmrReg {
+	ulong	osmr[4];	/*  match */
+	ulong	oscr;		/*  counter */
+	ulong	ossr;		/*  status */
+	ulong	ower;		/*  watchdog */
+	ulong	oier;		/*  interrupt enable */
+};
+
+#define PMGRREG		((PmgrReg*)PHYSPOWER)
+typedef struct PmgrReg PmgrReg;
+struct PmgrReg {
+	ulong	pmcr;	/*  ctl register */
+	ulong	pssr;		/*  sleep status */
+	ulong	pspr;		/*  scratch pad */
+	ulong	pwer;	/*  wakeup enable */
+	ulong	prer;		/* rising-edge detect enable */
+	ulong	pfer;		/* falling-edge detect enable */
+	ulong	pedr;	/* GPIO edge detect status */
+	ulong	pcfr;		/*  general configuration */
+	ulong	pgsr[3];		/*  GPIO sleep state */
+	ulong	rsvd;
+	ulong	rcsr;		/* reset controller status register */
+};
+
+enum {
+	/* pp. 3-25 to 3-31 */
+	PWER_rtc		= 1<<31,	/* wakeup by RTC alarm */
+	PWER_we0	= 1<<0,	/* wake-up on GP0 edge detect */
+
+	PSSR_sss		= 1<<0,	/* software sleep status */
+	PSSR_bfs		= 1<<1,	/* battery fault status */
+	PSSR_vfs		= 1<<2,	/* VDD fault status */
+	PSSR_ph		= 1<<4,	/* peripheral control hold */
+	PSSR_rdh		= 1<<5,	/* read disable hold */
+
+	PMFW_fwake=	1<<1,	/* fast wakeup enable (no power stabilisation delay) */
+
+	RSCR_gpr=	1<<3,	/* gpio reset has occurred */
+	RSCR_smr=	1<<2,	/* sleep mode has occurred */
+	RSCR_wdr=	1<<1,	/* watchdog reset has occurred */
+	RSCR_hwr=	1<<0,	/* hardware reset has occurred */
+};
+
+#define MEMCFGREG	((MemcfgReg*)PHYSMEMCFG)
+typedef struct MemcfgReg MemcfgReg;
+struct MemcfgReg {
+	ulong	mdcnfg;		/*  SDRAM config */
+	ulong	mdrefr;		/* dram refresh */
+	ulong	msc0;		/* static memory or devices */
+	ulong	msc1;
+	ulong	msc2;		/* static memory or devices */
+	ulong	mecr;		/* expansion bus (pcmcia, CF) */
+	ulong	sxcnfg;	/* synchronous static memory control */
+	ulong	sxmrs;	/* MRS value to write to SMROM */
+	ulong	mcmem0;	/* card interface socket 0 memory timing */
+	ulong	mcmem1;	/* card interface socket 1 memory timing */
+	ulong	mcatt0;	/* socket 0 attribute memory timing */
+	ulong	mcatt1;	/* socket 1 attribute memory timing */
+	ulong	mcio0;	/* socket 0 i/o timing */
+	ulong	mcio1;	/* socket 1 i/o timing */
+	ulong	mdmrs;	/* MRS value to write to SDRAM */
+	ulong	boot_def;	/* read-only boot-time register */
+	ulong	mdmrslp;	/* low-power SDRAM mode register set config */
+	ulong	sa1111cr;	/* SA1111 compatibility */
+};
+
+#define LCDREG		((LcdReg*)PHYSLCD)
+typedef struct LcdReg LcdReg;
+struct LcdReg {
+	ulong	lccr0;	/*  control 0 */
+	ulong	lccr1;	/*  control 1 */
+	ulong	lccr2;	/*  control 2 */
+	ulong	lccr3;	/*  control 3 */
+	struct {
+		ulong	fdadr;	/* dma frame descriptor address register */
+		ulong	fsadr;	/* dma frame source address register */
+		ulong	fidr;	/* dma frame ID register */
+		ulong	ldcmd;	/* dma command */
+	} frame[2];
+	ulong	fbr[2];	/* frame branch register */
+	ulong	lcsr;		/*  status  */
+	ulong	liidr;	/* interrupt ID register */
+	ulong	trgbr;	/* TMED RGB seed register */
+	ulong	tcr;	/* TMED control register */
+};
+
+#define USBREG	((UsbReg*)PHYSUSB)
+typedef struct UsbReg UsbReg;
+struct UsbReg {
+	ulong	udccr;	/*  control */
+	ulong	udccs[16];	/* endpoint control/status */
+	ulong	ufnrh;	/* frame number high */
+	ulong	ufnrl;	/* frame number low */
+	ulong	udbcr2;
+	ulong	udbcr4;
+	ulong	udbcr7;
+	ulong	udbcr9;
+	ulong	udbcr12;
+	ulong	udbcr14;
+	ulong	uddr[16];	/* endpoint data */
+	ulong	uicr0;
+	ulong	uicr1;
+	ulong	usir0;
+	ulong	usir1;
+};
+
+enum {
+	/*  DMA configuration parameters */
+
+	 /*  DMA Direction */
+	DmaOut=		0,
+	DmaIn=		1,
+
+	 /*  dma devices */
+	DmaDREQ0=		0,
+	DmaDREQ1,
+	DmaI2S_i,
+	DmaI2S_o,
+	DmaBTUART_i,
+	DmaBTUART_o,
+	DmaFFUART_i,
+	DmaFFUART_o,
+	DmaAC97mic,
+	DmaAC97modem_i,
+	DmaAC97modem_o,
+	DmaAC97audio_i,
+	DmaAC97audio_o,
+	DmaSSP_i,
+	DmaSSP_o,
+	DmaNSSP_i,
+	DmaNSSP_o,
+	DmaICP_i,
+	DmaICP_o,
+	DmaSTUART_i,
+	DmaSTUART_o,
+	DmaMMC_i,
+	DmaMMC_o,
+	DmaRsvd0,
+	DmaRsvd1,
+	DmaUSB1,
+	DmaUSB2,
+	DmaUSB3,
+	DmaUSB4,
+	DmaHWUART_i,
+	DmaUSB6,
+	DmaUSB7,
+	DmaUSB8,
+	DmaUSB9,
+	DmaHWUART_o,
+	DmaUSB11,
+	DmaUSB12,
+	DmaUSB13,
+	DmaUSB14,
+	DmaRsvd2,
+};
+
+/*
+ *	Interface to platform-specific PCMCIA signals, in arch*.c
+ */
+enum {
+	/* argument to pcmpin() */
+	PCMready,
+	PCMeject,
+	PCMstschng,
+};
+
+/*
+ * physical device addresses are mapped to the same virtual ones,
+ * allowing the same addresses to be used with or without mmu.
+ */
+
+#define PCMCIAcard(n)	(PHYSPCMCIA0+((n)*PCMCIASIZE))
+#define PCMCIAIO(n)	(PCMCIAcard(n)+0x0)		/* I/O space */
+#define PCMCIAAttr(n)	(PCMCIAcard(n)+0x8000000) /* Attribute space*/
+#define PCMCIAMem(n)	(PCMCIAcard(n)+0xC000000) /* Memory space */
+
+/*
+ * PCMCIA structures known by both port/cis.c and the pcmcia driver
+ */
+
+/*
+ * Map between ISA memory space and PCMCIA card memory space.
+ */
+struct PCMmap {
+	ulong	ca;			/* card address */
+	ulong	cea;			/* card end address */
+	ulong	isa;			/* local virtual address */
+	int	len;			/* length of the ISA area */
+	int	attr;			/* attribute memory */
+};
+
+/*
+ *  a PCMCIA configuration entry
+ */
+struct PCMconftab
+{
+	int	index;
+	ushort	irqs;		/* legal irqs */
+	uchar	irqtype;
+	uchar	bit16;		/* true for 16 bit access */
+	uchar	nlines;
+	struct {
+		ulong	start;
+		ulong	len;
+	} io[16];
+	int	nio;
+	uchar	vcc;
+	uchar	vpp1;
+	uchar	vpp2;
+	uchar	memwait;
+	ulong	maxwait;
+	ulong	readywait;
+	ulong	otherwait;
+};
+
+/*
+ *  PCMCIA card slot
+ */
+struct PCMslot
+{
+	RWlock;
+
+	Ref	ref;
+
+	long	memlen;		/* memory length */
+	uchar	slotno;		/* slot number */
+	void	*regs;		/* i/o registers */
+	void	*mem;		/* memory */
+	void	*attr;		/* attribute memory */
+
+	/* status */
+	uchar	occupied;	/* card in the slot */
+	uchar	configed;	/* card configured */
+	uchar	busy;
+	uchar	powered;
+	uchar	battery;
+	uchar	wrprot;
+	uchar	enabled;
+	uchar	special;
+	uchar	dsize;
+
+	/* cis info */
+	int	cisread;	/* set when the cis has been read */
+	char	verstr[512];	/* version string */
+	uchar	cpresent;	/* config registers present */
+	ulong	caddr;		/* relative address of config registers */
+	int	nctab;		/* number of config table entries */
+	PCMconftab	ctab[8];
+	PCMconftab	*def;		/* default conftab */
+
+	/* maps are fixed */
+	PCMmap memmap;
+	PCMmap attrmap;
+};
--- /dev/null
+++ b/os/pxa/sa1110break.c
@@ -1,0 +1,360 @@
+#include "u.h"
+#include "../port/lib.h"
+#include "mem.h"
+#include "dat.h"
+#include "fns.h"
+#include "ureg.h"
+#include "../port/error.h"
+
+//
+// from trap.c
+//
+extern int (*breakhandler)(Ureg *ur, Proc*);
+extern Instr BREAK;
+extern void portbreakinit(void);
+
+//
+// Instructions that can have the PC as a destination register
+//
+enum {
+	IADD = 1,
+	IBRANCH,
+	ILDM,
+	ILDR,
+	IMOV,
+
+	//
+	// These should eventually be implemented
+	//
+	IADC,
+	IAND,
+	IBIC,
+	IEOR,
+	ILDRT,
+	IMRS,
+	IMVN,
+	IORR,
+	IRSB,
+	IRSC,
+	ISBC,
+	ISUB,
+};
+
+static int	instrtype(Instr i);
+static ulong	iadd(Ureg *ur, Instr i);
+static ulong	ibranch(Ureg *ur, Instr i);
+static ulong	ildm(Ureg *ur, Instr i);
+static ulong	ildr(Ureg *ur, Instr i);
+static ulong	imov(Ureg *ur, Instr i);
+static ulong	shifterval(Ureg *ur, Instr i);
+static int	condpass(Instr i, ulong psr);
+static ulong	*address(Ureg *ur, Instr i);
+static ulong*	multiaddr(Ureg *ur, Instr i);
+static int	nbits(ulong v);
+
+#define COND_N(psr)	(((psr) >> 31) & 1)
+#define COND_Z(psr)	(((psr) >> 30) & 1)
+#define COND_C(psr)	(((psr) >> 29) & 1)
+#define COND_V(psr)	(((psr) >> 28) & 1)
+#define REG(i, a, b)	(((i) & BITS((a), (b))) >> (a))
+#define REGVAL(ur, r)	(*((ulong*)(ur) + (r)))
+#define LSR(v, s)	((ulong)(v) >> (s))
+#define ASR(v, s)	((long)(v) >> (s))
+#define ROR(v, s)	(LSR((v), (s)) | (((v) & ((1 << (s))-1)) << (32 - (s))))
+
+void
+machbreakinit(void)
+{
+	portbreakinit();
+	breakhandler = breakhit;
+}
+
+Instr
+machinstr(ulong addr)
+{
+	if (addr < KTZERO)
+		error(Ebadarg);
+	return *(Instr*)addr;
+}
+
+void
+machbreakset(ulong addr)
+{
+	if (addr < KTZERO)
+		error(Ebadarg);
+	*(Instr*)addr = BREAK;
+	segflush((void*)addr, sizeof(Instr));
+}
+
+void
+machbreakclear(ulong addr, Instr i)
+{
+	if (addr < KTZERO)
+		error(Ebadarg);
+	*(Instr*)addr = i;
+	segflush((void*)addr, sizeof(Instr));
+}
+
+//
+// Return the address of the instruction that will be executed after the
+// instruction at address ur->pc.
+//
+// This means decoding the instruction at ur->pc.
+//
+// In the simple case, the PC will simply be the address of the next
+// sequential instruction following ur->pc.
+//
+// In the complex case, the instruction is a branch of some sort, so the
+// value of the PC after the instruction must be computed by decoding
+// and simulating the instruction enough to determine the PC.
+//
+
+ulong
+machnextaddr(Ureg *ur)
+{
+	Instr i;
+	i = machinstr(ur->pc);
+	switch(instrtype(i)) {
+		case IADD:	return iadd(ur,i);
+		case IBRANCH:	return ibranch(ur,i);
+		case ILDM:	return ildm(ur,i);
+		case ILDR:	return ildr(ur,i);
+		case IMOV:	return imov(ur,i);
+
+		case IADC:
+		case IAND:
+		case IBIC:
+		case IEOR:
+		case ILDRT:
+		case IMRS:
+		case IMVN:
+		case IORR:
+		case IRSB:
+		case IRSC:
+		case ISBC:
+		case ISUB:
+			// XXX - Tad: unimplemented
+			//
+			// any of these instructions could possibly have the
+			// PC as Rd.  Eventually, these should all be
+			// checked just like the others.
+		default:
+			return ur->pc+4;
+	}
+
+	return 0;
+}
+
+static int
+instrtype(Instr i)
+{
+	if(i & BITS(26,27) == 0) {
+		switch((i >> 21) & 0xF) {
+			case 0:		return IAND;
+			case 1:		return IEOR;
+			case 2:		return ISUB;
+			case 3:		return IRSB;
+			case 4:		return IADD;
+			case 5:		return IADC;
+			case 6:		return ISBC;
+			case 7:		return IRSC;
+			case 0xD:	return IMOV;
+			case 0xC:	return IORR;
+			case 0xE:	return IBIC;
+			case 0xF:	return IMVN;
+		}
+		if(((i & BIT(25)|BITS(23,24)|BITS(20,21))) >> 20 == 0x10)
+			return IMRS;
+		return 0;
+	}
+
+	if(((i & BITS(27,25)|BIT(20)) >> 20) == 0x81) return ILDM;
+	if(((i & BITS(26,27)|BIT(22)|BIT(20)) >> 20) == 0x41) return ILDR;
+	if(((i & BITS(25,27)) >> 25) == 5) return IBRANCH;
+
+	return 0;
+}
+
+static ulong
+iadd(Ureg *ur, Instr i)
+{
+	ulong Rd = REG(i, 12, 15);
+	ulong Rn = REG(i, 16, 19);
+
+	if(Rd != 15 || !condpass(i, ur->psr))
+		return ur->pc+4;
+
+	return REGVAL(ur, Rn) + shifterval(ur, i);
+}
+
+static ulong
+ibranch(Ureg *ur, Instr i)
+{
+	if(!condpass(i, ur->psr))
+		return ur->pc+4;
+	return ur->pc + ((signed long)(i << 8) >> 6) + 8;
+}
+
+static ulong
+ildm(Ureg *ur, Instr i)
+{
+	if((i & BIT(15)) == 0)
+		return ur->pc+4;
+
+	return *(multiaddr(ur, i) + nbits(i & BITS(15, 0)));
+}
+
+static ulong
+ildr(Ureg *ur, Instr i)
+{
+	if(REG(i, 12, 19) != 15 || !condpass(i, ur->psr))
+		return ur->pc+4;
+
+	return *address(ur, i);
+}
+
+static ulong
+imov(Ureg *ur, Instr i)
+{
+	if(REG(i, 12, 15) != 15 || !condpass(i, ur->psr))
+		return ur->pc+4;
+
+	return shifterval(ur, i);
+}
+
+static int
+condpass(Instr i, ulong psr)
+{
+	uchar n = COND_N(psr);
+	uchar z = COND_Z(psr);
+	uchar c = COND_C(psr);
+	uchar v = COND_V(psr);
+
+	switch(LSR(i,28)) {
+		case 0:		return z;
+		case 1:		return !z;
+		case 2:		return c;
+		case 3:		return !c;
+		case 4:		return n;
+		case 5:		return !n;
+		case 6:		return v;
+		case 7:		return !v;
+		case 8:		return c && !z;
+		case 9:		return !c || z;
+		case 10:	return n == v;
+		case 11:	return n != v;
+		case 12:	return !z && (n == v);
+		case 13:	return z && (n != v);
+		case 14:	return 1;
+		case 15:	return 0;
+	}
+}
+
+static ulong
+shifterval(Ureg *ur, Instr i)
+{
+	if(i & BIT(25)) {					// IMMEDIATE
+		ulong imm = i & BITS(0,7);
+		ulong s = (i & BITS(8,11)) >> 7;  // this contains the * 2
+		return ROR(imm, s);
+	} else {
+		ulong Rm = REGVAL(ur, REG(i, 0, 3));
+		ulong s = (i & BITS(7,11)) >> 7;
+
+		switch((i & BITS(6,4)) >> 4) {
+		case 0: 					// LSL
+			return Rm << s;
+		case 1:						// LSLREG
+			s = REGVAL(ur, s >> 1) & 0xFF;
+			if(s >= 32) return 0;
+			return Rm << s;
+		case 2: 					// LSRIMM
+			return LSR(Rm, s);
+		case 3: 					// LSRREG
+			s = REGVAL(ur, s >> 1) & 0xFF;
+			if(s >= 32) return 0;
+			return LSR(Rm, s);
+		case 4:						// ASRIMM
+			if(s == 0) {
+				if(Rm & BIT(31) == 0)
+					return 0;
+				return 0xFFFFFFFF;
+			}
+			return ASR(Rm, s);
+		case 5:						// ASRREG
+			s = REGVAL(ur, s >> 1) & 0xFF;
+			if(s >= 32) {
+				if(Rm & BIT(31) == 0)
+					return 0;
+				return 0xFFFFFFFF;
+			}
+			return ASR(Rm, s);
+		case 6: 					// RORIMM
+			if(s == 0)
+				return (COND_C(ur->psr) << 31) | LSR(Rm, 1);
+			return ROR(Rm, s);
+		case 7: 					// RORREG
+			s = REGVAL(ur, s >> 1) & 0xFF;
+			if(s == 0 || (s & 0xF) == 0)
+				return Rm;
+			return ROR(Rm, s & 0xF);
+		}
+	}
+}
+
+static ulong*
+address(Ureg *ur, Instr i)
+{
+	ulong Rn = REGVAL(ur, REG(i, 16, 19));
+
+	if(i & BIT(24) == 0) 					// POSTIDX
+		return (ulong*)REGVAL(ur, Rn);
+	if(i & BIT(25) == 0) {					// OFFSET
+		if(i & BIT(23))
+			return (ulong*)(REGVAL(ur, Rn) + (i & BITS(0, 11)));
+		return (ulong*)(REGVAL(ur, Rn) - (i & BITS(0, 11)));
+	} else {						// REGOFF
+		ulong Rm = REGVAL(ur, REG(i, 0, 3));
+		ulong index = 0;
+		switch(i & BITS(5,6) >> 5) {
+		case 0:	index = Rm << ((i & BITS(7, 11)) >> 7);		break;
+		case 1:	index = LSR(Rm, ((i & BITS(7, 11)) >> 7));	break;
+		case 2:	index = ASR(Rm, ((i & BITS(7, 11)) >> 7));	break;
+		case 3:
+			if(i & BITS(7, 11) == 0)
+				index = (COND_C(ur->psr) << 31) | LSR(Rm, 1);
+			else
+				index = ROR(Rm, (i & BITS(7, 11)) >> 7);
+			break;
+		}
+		if(i & BIT(23))
+			return (ulong*)(Rn + index);
+		return (ulong*)(Rn - index);
+	}
+}
+
+static ulong*
+multiaddr(Ureg *ur, Instr i)
+{
+	ulong Rn = REGVAL(ur, REG(i, 16, 19));
+
+	switch((i >> 23) & 3) {
+		case 0: return (ulong*)(Rn - (nbits(i & BITS(0,15))*4)+4);
+		case 1:	return (ulong*)Rn;
+		case 2: return (ulong*)(Rn - (nbits(i & BITS(0,15))*4));
+		case 3: return (ulong*)(Rn + 4);
+	}
+}
+
+static int
+nbits(ulong v)
+{
+	int n = 0;
+	int i;
+	for(i = 0; i < 32; i++) {
+		if(v & 1)
+			++n;
+		v = LSR(v, 1);
+	}
+	return n;
+}
--- /dev/null
+++ b/os/pxa/trap.c
@@ -1,0 +1,587 @@
+#include	"u.h"
+#include	"../port/lib.h"
+#include	"mem.h"
+#include	"dat.h"
+#include	"fns.h"
+#include	"io.h"
+#include	"ureg.h"
+#include	"../port/error.h"
+
+#define waslo(sr) (!((sr) & (PsrDirq|PsrDfiq)))
+
+typedef struct Handler Handler;
+struct Handler {
+	void	(*r)(Ureg*, void*);
+	void	*a;
+	char	name[KNAMELEN];
+	Handler	*next;
+};
+
+static Handler irqvec[32];
+static Handler gpiovec[MaxGPIObit+1];
+static Lock veclock;
+
+Instr BREAK = 0xE6BAD010;
+
+int (*breakhandler)(Ureg*, Proc*);
+int (*catchdbg)(Ureg *, uint);
+void (*suspendcode)(void);
+
+extern void (*serwrite)(char *, int);
+
+/*
+ * Interrupt sources not masked by splhi(): special
+ *  interrupt handlers (eg, profiler or watchdog), not allowed
+ *  to share regular kernel data structures.  All interrupts are
+ *  masked by splfhi(), which should only be used sparingly.
+ * splflo enables FIQ but no others.
+ */
+enum {
+	IRQ_NONMASK = (1 << IRQtimer3) | (1 << IRQtimer2),
+};
+
+void
+intrenable(int sort, int v, void (*f)(Ureg*, void*), void* a, char *name)
+{
+	int x, o;
+	ulong m;
+	GpioReg *g;
+	Handler *ie;
+
+	ilock(&veclock);
+	switch(sort) {
+	case GPIOfalling:
+	case GPIOrising:
+	case GPIOboth:
+		if(v < 0 || v > MaxGPIObit)
+			panic("intrenable: gpio %d out of range", v);
+		g = GPIOREG;
+		o = GPR(v);
+		m = GPB(v);
+		switch(sort){
+		case GPIOfalling:
+			g->gfer[o] |= m;
+			g->grer[o] &= ~m;
+			break;
+		case GPIOrising:
+			g->grer[o] |= m;
+			g->gfer[o] &= ~m;
+			break;
+		case GPIOboth:
+			g->grer[o] |= m;
+			g->gfer[o] |= m;
+			break;
+		}
+		g->gpdr[o] &= ~m;	/* must be input */
+		if(v > MaxGPIOIRQ) {
+			ie = &gpiovec[v];
+			if(ie->r != nil)
+				iprint("duplicate gpio irq: %d (%s)\n", v, ie->name);
+			ie->r = f;
+			ie->a = a;
+			strncpy(ie->name, name, KNAMELEN-1);
+			ie->name[KNAMELEN-1] = 0;
+			iunlock(&veclock);
+			return;
+		}
+		v += IRQgpio0;
+		/*FALLTHROUGH for GPIO sources 0-MaxGPIOIRQ */
+	case IRQ:
+		if(v < 0 || v > 31)
+			panic("intrenable: irq source %d out of range", v);
+		ie = &irqvec[v];
+		if(ie->r != nil)
+			iprint("duplicate irq: %d (%s)\n", v, ie->name);
+		ie->r = f;
+		ie->a = a;
+		strncpy(ie->name, name, KNAMELEN-1);
+		ie->name[KNAMELEN-1] = 0;
+
+		x = splfhi();
+		/* Enable the interrupt by setting the mask bit */
+		INTRREG->icmr |= 1 << v;
+		splx(x);
+		break;
+	default:
+		panic("intrenable: unknown irq bus %d", sort);
+	}
+	iunlock(&veclock);
+}
+
+void
+intrdisable(int sort, int v, void (*f)(Ureg*, void*), void* a, char *name)
+{
+	int x, o;
+	GpioReg *g;
+	Handler *ie;
+	ulong m;
+
+	ilock(&veclock);
+	switch(sort) {
+	case GPIOfalling:
+	case GPIOrising:
+	case GPIOboth:
+		if(v < 0 || v > MaxGPIObit)
+			panic("intrdisable: gpio source %d out of range", v);
+		if(v > MaxGPIOIRQ)
+			ie = &gpiovec[v];
+		else
+			ie = &irqvec[v];
+		if(ie->r != f || ie->a != a || strcmp(ie->name, name) != 0)
+			break;
+		ie->r = nil;
+		if(v <= MaxGPIOIRQ){
+			v += IRQgpio0;
+			x = splfhi();
+			INTRREG->icmr &= ~(1<<v);
+			splx(x);
+		}
+		g = GPIOREG;
+		o = GPR(v);
+		m = GPB(v);
+		switch(sort){
+		case GPIOfalling:
+			g->gfer[o] &= ~m;
+			break;
+		case GPIOrising:
+			g->grer[o] &= ~m;
+			break;
+		case GPIOboth:
+			g->grer[o] &= ~m;
+			g->gfer[o] &= ~m;
+			break;
+		}
+		break;
+	case IRQ:
+		if(v < 0 || v > 31)
+			panic("intrdisable: irq source %d out of range", v);
+		ie = &irqvec[v];
+		if(ie->r != f || ie->a != a || strcmp(ie->name, name) != 0)
+			break;
+		ie->r = nil;
+		x = splfhi();
+		INTRREG->icmr &= ~(1<<v);
+		splx(x);
+		break;
+	default:
+		panic("intrdisable: unknown irq bus %d", sort);
+	}
+	iunlock(&veclock);
+}
+
+static void
+gpiointr(Ureg *ur, void*)
+{
+	Handler *cur;
+	ulong e;
+	int i, o;
+
+	for(o=0; o<3; o++){
+		e = GPIOREG->gedr[o];
+		GPIOREG->gedr[o] = e;
+		for(i = 0; i < 32 && e != 0; i++){
+			if(e & (1<<i)){
+				cur = &gpiovec[(o<<5)+i];
+				if(cur->r != nil){
+					cur->r(ur, cur->a);
+					e &= ~(1<<i);
+				}
+			}
+		}
+		if(e != 0){
+			GPIOREG->gfer[o] &= ~e;
+			GPIOREG->grer[o] &= ~e;
+			iprint("spurious GPIO[%d] %8.8lux interrupt\n", o,e);
+		}
+	}
+}
+
+static void
+intrs(Ureg *ur, ulong ibits)
+{
+	Handler *cur;
+	int i, s;
+
+	for(i=0; i<nelem(irqvec) && ibits; i++)
+		if(ibits & (1<<i)){
+			cur = &irqvec[i];
+			if(cur->r != nil){
+				cur->r(ur, cur->a);
+				ibits &= ~(1<<i);
+			}
+		}
+	if(ibits != 0){
+		iprint("spurious irq interrupt: %8.8lux\n", ibits);
+		s = splfhi();
+		INTRREG->icmr &= ~ibits;
+		splx(s);
+	}
+}
+
+/*
+ * initialise R13 in each trap mode, at the start and after suspend reset.
+ */
+void
+trapstacks(void)
+{
+	setr13(PsrMfiq, m->fiqstack+nelem(m->fiqstack));
+	setr13(PsrMirq, m->irqstack+nelem(m->irqstack));
+	setr13(PsrMabt, m->abtstack+nelem(m->abtstack));
+	setr13(PsrMund, m->undstack+nelem(m->undstack));
+}
+
+void
+trapinit(void)
+{
+	IntrReg *intr = INTRREG;
+
+	intr->icmr = 0;	/* mask everything */
+	intr->iccr = 1;	/* only enabled and unmasked interrupts wake us */
+	intr->iclr = IRQ_NONMASK;
+
+	trapstacks();
+
+	memmove(page0->vectors, vectors, sizeof(page0->vectors));
+	memmove(page0->vtable, vtable, sizeof(page0->vtable));
+	dcflush(page0, sizeof(*page0));
+
+#ifdef NOTYET
+	suspendcode = xspanalloc(16*sizeof(ulong), CACHELINESZ, 0);
+	memmove(suspendcode, _suspendcode, 16*sizeof(ulong));
+	dcflush(suspendcode, 8*sizeof(ulong));
+#endif
+
+	icflushall();
+
+	intrenable(IRQ, IRQgpio, gpiointr, nil, "gpio");
+}
+
+static char *trapnames[PsrMask+1] = {
+	[ PsrMfiq ] "Fiq interrupt",
+	[ PsrMirq ] "Mirq interrupt",
+	[ PsrMsvc ] "SVC/SWI Exception",
+	[ PsrMabt ] "Prefetch Abort/Data Abort",
+	[ PsrMabt+1 ] "Data Abort",
+	[ PsrMund ] "Undefined instruction",
+	[ PsrMsys ] "Sys trap"
+};
+
+static char *
+trapname(int psr)
+{
+	char *s;
+
+	s = trapnames[psr & PsrMask];
+	if(s == nil)
+		s = "Undefined trap";
+	return s;
+}
+
+static void
+sys_trap_error(int type)
+{
+	char errbuf[ERRMAX];
+	sprint(errbuf, "sys: trap: %s\n", trapname(type));
+	error(errbuf);
+}
+
+static void
+faultarm(Ureg *ureg, ulong far)
+{
+	char buf[ERRMAX];
+
+	sprint(buf, "sys: trap: fault pc=%8.8lux addr=0x%lux", (ulong)ureg->pc, far);
+	if(1){
+		iprint("%s\n", buf);
+		dumpregs(ureg);
+	}
+	if(far == ~0)
+		disfault(ureg, "dereference of nil");
+	disfault(ureg, buf);
+}
+
+/*
+ *  All traps come here.  It might be slightly slower to have all traps call trap
+ *  rather than directly vectoring the handler.
+ *  However, this avoids a lot of code duplication and possible bugs.
+ *  trap is called splfhi().
+ */
+void
+trap(Ureg* ureg)
+{
+	ulong far, fsr;
+	int rem, t, itype;
+	Proc *oup;
+
+	if(up != nil)
+		rem = ((char*)ureg)-up->kstack;
+	else
+		rem = ((char*)ureg)-(char*)m->stack;
+	if(ureg->type != PsrMfiq && rem < 256)
+		panic("trap %d bytes remaining (%s), up=#%8.8lux ureg=#%8.8lux pc=#%8.8ux",
+			rem, up?up->text:"", up, ureg, ureg->pc);
+
+	/*
+	 * All interrupts/exceptions should be resumed at ureg->pc-4,
+	 * except for Data Abort which resumes at ureg->pc-8.
+	 */
+	itype = ureg->type;
+	if(itype == PsrMabt+1)
+		ureg->pc -= 8;
+	else
+		ureg->pc -= 4;
+	ureg->sp = (ulong)(ureg+1);
+	if(itype == PsrMfiq){	/* fast interrupt (eg, profiler) */
+		oup = up;
+		up = nil;
+		intrs(ureg, INTRREG->icfp);
+		up = oup;
+		return;
+	}
+
+	/* All other traps */
+
+	if(0 && ureg->psr & PsrDfiq)
+		panic("FIQ disabled");
+
+	if(up){
+		up->pc = ureg->pc;
+		up->dbgreg = ureg;
+	}
+	switch(itype) {
+	case PsrMirq:
+		t = m->ticks;	/* CPU time per proc */
+		up = nil;		/* no process at interrupt level */
+		splflo();	/* allow fast interrupts */
+		intrs(ureg, INTRREG->icip);
+		up = m->proc;
+		preemption(m->ticks - t);
+		break;
+
+	case PsrMund:				/* Undefined instruction */
+		if(*(ulong*)ureg->pc == BREAK && breakhandler) {
+			int s;
+			Proc *p;
+
+			p = up;
+			/* if(!waslo(ureg->psr) || ureg->pc >= (ulong)splhi && ureg->pc < (ulong)islo)
+				p = 0; */
+			s = breakhandler(ureg, p);
+			if(s == BrkSched) {
+				p->preempted = 0;
+				sched();
+			} else if(s == BrkNoSched) {
+				p->preempted = 1;	/* stop it being preempted until next instruction */
+				if(up)
+					up->dbgreg = 0;
+				return;
+			}
+			break;
+		}
+		if(up == nil)
+			goto faultpanic;
+		spllo();
+		if(waserror()) {
+			if(waslo(ureg->psr) && up->type == Interp)
+				disfault(ureg, up->env->errstr);
+			setpanic();
+			dumpregs(ureg);
+			panic("%s", up->env->errstr);
+		}
+		if(!fpiarm(ureg)) {
+			dumpregs(ureg);
+			sys_trap_error(ureg->type);
+		}
+		poperror();
+		break;
+
+	case PsrMsvc:				/* Jump through 0 or SWI */
+		if(waslo(ureg->psr) && up && up->type == Interp) {
+			spllo();
+			dumpregs(ureg);
+			sys_trap_error(ureg->type);
+		}
+		setpanic();
+		dumpregs(ureg);
+		panic("SVC/SWI exception");
+		break;
+
+	case PsrMabt:				/* Prefetch abort */
+		if(catchdbg && catchdbg(ureg, 0))
+			break;
+		/* FALL THROUGH */
+	case PsrMabt+1:			/* Data abort */
+		fsr = mmugetfsr();
+		far = mmugetfar();
+		if(fsr & (1<<9)) {
+			mmuputfsr(fsr & ~(1<<9));
+			if(catchdbg && catchdbg(ureg, fsr))
+				break;
+			print("Debug/");
+		}
+		if(waslo(ureg->psr) && up && up->type == Interp) {
+			spllo();
+			faultarm(ureg, far);
+		}
+		iprint("Data Abort: FSR %8.8luX FAR %8.8luX\n", fsr, far); xdelay(1);
+		/* FALL THROUGH */
+
+	default:				/* ??? */
+faultpanic:
+		setpanic();
+		dumpregs(ureg);
+		panic("exception %uX %s\n", ureg->type, trapname(ureg->type));
+		break;
+	}
+
+	splhi();
+	if(up)
+		up->dbgreg = 0;		/* becomes invalid after return from trap */
+}
+
+void
+setpanic(void)
+{
+	if(breakhandler != 0)	/* don't mess up debugger */
+		return;
+	INTRREG->icmr = 0;
+	spllo();
+	consoleprint = 1;
+	serwrite = uartputs;
+}
+
+int
+isvalid_wa(void *v)
+{
+	return (ulong)v >= KZERO && (ulong)v < conf.topofmem && !((ulong)v & 3);
+}
+
+int
+isvalid_va(void *v)
+{
+	return (ulong)v >= KZERO && (ulong)v < conf.topofmem;
+}
+
+void
+dumplongs(char *msg, ulong *v, int n)
+{
+	int i, l;
+
+	l = 0;
+	iprint("%s at %.8p: ", msg, v);
+	for(i=0; i<n; i++){
+		if(l >= 4){
+			iprint("\n    %.8p: ", v);
+			l = 0;
+		}
+		if(isvalid_va(v)){
+			iprint(" %.8lux", *v++);
+			l++;
+		}else{
+			iprint(" invalid");
+			break;
+		}
+	}
+	iprint("\n");
+}
+
+static void
+_dumpstack(Ureg *ureg)
+{
+	ulong *v, *l;
+	ulong inst;
+	ulong *estack;
+	int i;
+
+	l = (ulong*)(ureg+1);
+	if(!isvalid_wa(l)){
+		iprint("invalid ureg/stack: %.8p\n", l);
+		return;
+	}
+	print("ktrace /kernel/path %.8ux %.8ux %.8ux\n", ureg->pc, ureg->sp, ureg->r14);
+	if(up != nil && l >= (ulong*)up->kstack && l <= (ulong*)(up->kstack+KSTACK-4))
+		estack = (ulong*)(up->kstack+KSTACK);
+	else if(l >= (ulong*)m->stack && l <= (ulong*)((ulong)m+BY2PG-4))
+		estack = (ulong*)((ulong)m+BY2PG-4);
+	else{
+		iprint("unknown stack\n");
+		return;
+	}
+	i = 0;
+	for(; l<estack; l++) {
+		if(!isvalid_wa(l)) {
+			iprint("invalid(%8.8p)", l);
+			break;
+		}
+		v = (ulong*)*l;
+		if(isvalid_wa(v)) {
+			inst = v[-1];
+			if((inst & 0x0ff0f000) == 0x0280f000 &&
+			     (*(v-2) & 0x0ffff000) == 0x028fe000	||
+				(inst & 0x0f000000) == 0x0b000000) {
+				iprint("%8.8p=%8.8lux ", l, v);
+				i++;
+			}
+		}
+		if(i == 4){
+			iprint("\n");
+			i = 0;
+		}
+	}
+	if(i)
+		print("\n");
+}
+
+void
+dumpregs(Ureg* ureg)
+{
+	print("TRAP: %s", trapname(ureg->type));
+	if((ureg->psr & PsrMask) != PsrMsvc)
+		print(" in %s", trapname(ureg->psr));
+	print("\n");
+	print("PSR %8.8uX type %2.2uX PC %8.8uX LINK %8.8uX\n",
+		ureg->psr, ureg->type, ureg->pc, ureg->link);
+	print("R14 %8.8uX R13 %8.8uX R12 %8.8uX R11 %8.8uX R10 %8.8uX\n",
+		ureg->r14, ureg->r13, ureg->r12, ureg->r11, ureg->r10);
+	print("R9  %8.8uX R8  %8.8uX R7  %8.8uX R6  %8.8uX R5  %8.8uX\n",
+		ureg->r9, ureg->r8, ureg->r7, ureg->r6, ureg->r5);
+	print("R4  %8.8uX R3  %8.8uX R2  %8.8uX R1  %8.8uX R0  %8.8uX\n",
+		ureg->r4, ureg->r3, ureg->r2, ureg->r1, ureg->r0);
+	print("Stack is at: %8.8luX\n", ureg);
+	print("PC %8.8lux LINK %8.8lux\n", (ulong)ureg->pc, (ulong)ureg->link);
+
+	if(up)
+		print("Process stack:  %8.8lux-%8.8lux\n",
+			up->kstack, up->kstack+KSTACK-4);
+	else
+		print("System stack: %8.8lux-%8.8lux\n",
+			(ulong)(m+1), (ulong)m+BY2PG-4);
+	dumplongs("stack", (ulong *)(ureg + 1), 16);
+	_dumpstack(ureg);
+}
+
+/*
+ * Fill in enough of Ureg to get a stack trace, and call a function.
+ * Used by debugging interface rdb.
+ */
+void
+callwithureg(void (*fn)(Ureg*))
+{
+	Ureg ureg;
+	ureg.pc = getcallerpc(&fn);
+	ureg.sp = (ulong)&fn;
+	ureg.r14 = 0;
+	fn(&ureg);
+}
+
+void
+dumpstack(void)
+{
+	callwithureg(_dumpstack);
+}
+
+void
+trapspecial(int (*f)(Ureg *, uint))
+{
+	catchdbg = f;
+}
--- /dev/null
+++ b/os/rpcg/NOTICE
@@ -1,0 +1,4 @@
+Inferno® Copyright © 1996-1999 Lucent Technologies Inc.  All rights reserved.
+PowerPC support Copyright © 1995-1997 C H Forsyth (forsyth@terzarima.net).  All rights reserved.
+MPC8xx Inferno PowerPC port Copyright © 1998-2003 Vita Nuova Holdings Limited.  All rights reserved.
+RPCG Inferno PowerPC port Copyright © 2002-2003 Vita Nuova Holdings Limited.  All rights reserved.
--- /dev/null
+++ b/os/rpcg/archrpcg.c
@@ -1,0 +1,428 @@
+#include	"u.h"
+#include	"../port/lib.h"
+#include	"mem.h"
+#include	"dat.h"
+#include	"fns.h"
+#include	"io.h"
+
+#include	<draw.h>
+#include	<memdraw.h>
+#include	<cursor.h> 
+#include	"screen.h"
+
+#include "../port/netif.h"
+#include "../mpc/etherif.h"
+#include "../port/flashif.h"
+#include	"archrpcg.h"
+
+/*
+ * board-specific support for the 850/823 RPCG board
+ */
+
+enum {
+	/* sccr */
+	COM3=	IBIT(1)|IBIT(2),	/* clock output disabled */
+	TBS =	IBIT(6),	/* =0, time base is OSCCLK/{4,16}; =1, time base is GCLK2/16 */
+	RTSEL = IBIT(8),	/* =0, select main oscillator (OSCM); =1, select external crystal (EXTCLK) */
+	RTDIV = IBIT(7),	/* =0, divide by 4; =1, divide by 512 */
+	CRQEN = IBIT(9),	/* =1, switch to high frequency when CPM active */
+	PRQEN = IBIT(10),	/* =1, switch to high frequency when interrupt pending */
+	EDBF2 = IBIT(14),	/* =1, CLKOUT is GCLK2/2 */
+
+	/* plprcr */
+	CSRC = IBIT(21),	/* =0, clock is DFNH; =1, clock is DFNL */
+};
+
+/*
+ * called early in main.c, after machinit:
+ * using board and architecture specific registers, initialise
+ * 8xx registers that need it and complete initialisation of the Mach structure.
+ */
+void
+archinit(void)
+{
+	IMM *io;
+	int mf, i;
+
+	m->bcsr = KADDR(PHYSBCSR);
+	m->bcsr[0] &= ~EnableEnet;
+	io = m->iomem;	/* run by reset code: no need to lock */
+	m->clockgen = 8000000;	/* crystal frequency */
+	m->oscclk = m->clockgen/MHz;
+	io->plprcrk = KEEP_ALIVE_KEY;
+	io->plprcr &= ~CSRC;	/* general system clock is DFNH */
+	mf = (io->plprcr >> 20)+1;	/* use timing set by bootstrap */
+	m->cpuhz = m->clockgen*mf;
+	m->speed = m->cpuhz/MHz;
+	io->plprcrk = ~KEEP_ALIVE_KEY;
+	io->sccrk = KEEP_ALIVE_KEY;
+	io->sccr |= COM3 | TBS | CRQEN | PRQEN;
+	io->sccrk = ~KEEP_ALIVE_KEY;
+	if(0){
+		/* reset PCMCIA in case monitor hasn't */
+		io->pgcr[1] = 1<<7;	/* OP2 high to disable PCMCIA */
+		io->per = 0;
+		io->pscr = ~0;
+		for(i=0; i<8; i++)
+			io->pcmr[i].base = io->pcmr[i].option = 0;
+	}
+}
+
+static ulong
+banksize(int x, ulong *pa)
+{
+	IMM *io;
+
+	io = m->iomem;
+	if((io->memc[x].base & 1) == 0)
+		return 0;	/* bank not valid */
+	*pa = io->memc[x].base & ~0x7FFF;
+	return -(io->memc[x].option&~0x7FFF);
+}
+
+/*
+ * initialise the kernel's memory configuration:
+ * there are two banks (base0, npage0) and (base1, npage1).
+ * initialise any other values in conf that are board-specific.
+ */
+void
+archconfinit(void)
+{
+	ulong nbytes, pa, ktop;
+
+	conf.nscc = 2;
+	conf.nocts2 = 1;	/* TO DO: check this */
+
+	conf.npage0 = 0;
+	nbytes = banksize(DRAM1CS, &pa);
+	if(nbytes){
+		conf.npage0 = nbytes/BY2PG;
+		conf.base0 = pa;
+	}
+
+	conf.npage1 = 0;
+
+	/* the following assumes the kernel text and/or data is in bank 0 */
+	ktop = PGROUND((ulong)end);
+	ktop = PADDR(ktop) - conf.base0;
+	conf.npage0 -= ktop/BY2PG;
+	conf.base0 += ktop;
+
+	/* check for NVRAM */
+	if(m->bcsr[0] & NVRAMBattGood){
+		conf.nvramsize = banksize(NVRAMCS, &pa);
+		conf.nvrambase = KADDR(pa);
+	}
+}
+
+void
+cpuidprint(void)
+{
+	ulong v;
+	int i;
+
+	print("PVR: ");
+	switch(m->cputype){
+	case 0x01:	print("MPC601"); break;
+	case 0x03:	print("MPC603"); break;
+	case 0x04:	print("MPC604"); break;
+	case 0x06:	print("MPC603e"); break;
+	case 0x07:	print("MPC603e-v7"); break;
+	case 0x50:	print("MPC8xx"); break;
+	default:	print("PowerPC version #%x", m->cputype); break;
+	}
+	print(", revision #%lux\n", getpvr()&0xffff);
+	print("IMMR: ");
+	v = getimmr() & 0xFFFF;
+	switch(v>>8){
+	case 0x00:	print("MPC860/821"); break;
+	case 0x20:	print("MPC823"); break;
+	case 0x21:	print("MPC823A"); break;
+	default:	print("Type #%lux", v>>8); break;
+	}
+	print(", mask #%lux\n", v&0xFF);
+	print("plprcr=%8.8lux sccr=%8.8lux bcsr=%8.8lux\n", m->iomem->plprcr, m->iomem->sccr, m->bcsr[0]);
+	print("%lud MHz system\n", m->cpuhz/MHz);
+	print("%lud pages\n", (conf.npage0-conf.base0)/BY2PG);
+	print("%ludK NVRAM\n", conf.nvramsize/1024);
+	print("\n");
+	for(i=0; i<nelem(m->iomem->pcmr); i++)
+		print("%d: %8.8lux %8.8lux\n", i, m->iomem->memc[i].base, m->iomem->memc[i].option);
+}
+
+/*
+ * provide value for #r/switch (devrtc.c)
+ */
+int
+archoptionsw(void)
+{
+	return (m->bcsr[0]&DipSwitchMask)>>4;
+}
+
+/*
+ * invoked by clock.c:/^clockintr
+ */
+static void
+twinkle(void)
+{
+	if(m->ticks%MS2TK(1000) == 0)
+		m->bcsr[0] ^= LedOff;
+}
+
+void	(*archclocktick)(void) = twinkle;
+
+/*
+ * invoked by ../port/taslock.c:/^ilock:
+ * reset watchdog timer here, if there is one and it is enabled
+ * (qboot currently disables it on the FADS board)
+ */
+void
+clockcheck(void)
+{
+}
+
+/*
+ * for devflash.c:/^flashreset
+ * retrieve flash type, virtual base and length and return 0;
+ * return -1 on error (no flash)
+ */
+int
+archflashreset(int bank, Flash *f)
+{
+	if(bank != 0)
+		return -1;
+	f->type = "AMD29F0x0";
+//	f->type = "cfi16";
+	f->addr = KADDR(PHYSFLASH);
+	f->size = 4*1024*1024;
+	f->width = 4;
+	f->interleave = 1;
+	return 0;
+}
+
+int
+archether(int ctlrno, Ether *ether)
+{
+	if(isaconfig("ether", ctlrno, ether) == 0)
+		return -1;
+	return 1;
+}
+
+/*
+ * enable the clocks for the given SCC ether and reveal them to the caller.
+ * do anything else required to prepare the transceiver (eg, set full-duplex, reset loopback).
+ */
+int
+archetherenable(int cpmid, int *rcs, int *tcs, int mbps, int fullduplex)
+{
+	IMM *io;
+
+	if(cpmid != CPscc2)
+		return -1;
+	USED(mbps);
+	USED(fullduplex);
+	io = ioplock();
+	m->bcsr[0] = (m->bcsr[0] & ~(EnableXcrLB|DisableColTest)) | EnableEnet;
+	eieio();
+	io->papar |= SIBIT(6)|SIBIT(4);	/* enable CLK2 and CLK4 */
+	io->padir &= ~(SIBIT(6)|SIBIT(4));
+	iopunlock();
+	*rcs = CLK4;
+	*tcs = CLK2;
+	return 0;
+}
+
+/*
+ * do anything extra required to enable the UART on the given CPM port
+ */
+void
+archenableuart(int id, int irda)
+{
+	USED(id, irda);
+}
+
+/*
+ * do anything extra required to disable the UART on the given CPM port
+ */
+void
+archdisableuart(int id)
+{
+	USED(id);
+}
+
+/*
+ * enable the external USB transceiver
+ *	speed is 12MHz if highspeed is non-zero; 1.5MHz if zero
+ *	master is non-zero if the node is acting as USB Host and should provide power
+ */
+void
+archenableusb(int highspeed, int master)
+{
+	ioplock();
+	if(master)
+		m->bcsr[0] |= EnableUSBPwr;
+	else
+		m->bcsr[0] &= ~EnableUSBPwr;
+	m->bcsr[0] &= ~DisableUSB;
+	if(highspeed)
+		m->bcsr[0] |= HighSpdUSB;
+	else
+		m->bcsr[0] &= ~HighSpdUSB;
+	iopunlock();
+}
+
+/*
+ * shut down the USB transceiver
+ */
+void
+archdisableusb(void)
+{
+	ioplock();
+	m->bcsr[0] |= DisableUSB;
+	m->bcsr[0] &= ~EnableUSBPwr;
+	iopunlock();
+}
+
+/*
+ * set the external infrared transceiver to the given speed
+ */
+void
+archsetirxcvr(int highspeed)
+{
+	USED(highspeed);
+}
+
+/*
+ * force hardware reset/reboot
+ */
+void
+archreboot(void)
+{
+	IMM *io;
+
+	io = m->iomem;
+	io->plprcrk = KEEP_ALIVE_KEY;
+	io->plprcr |= 1<<7;	/* checkstop reset enable */
+	io->plprcrk = ~KEEP_ALIVE_KEY;
+	eieio();
+	io->sdcr = 1;
+	eieio();
+	io->lccr = 0;	/* switch LCD off */
+	eieio();
+	firmware(0);
+}
+
+/*
+ * board-specific PCMCIA support: assumes slot B on 82xFADS
+ */
+int
+pcmslotavail(int slotno)
+{
+	return slotno == 1;
+}
+
+void
+pcmenable(void)
+{
+	ioplock();
+	m->bcsr[0] = m->bcsr[0] & ~(VPPMask|VCCMask);	/* power off */
+	eieio();
+	m->bcsr[0] |= VCC5V | VPPVCC;	/* apply Vcc */
+	eieio();
+	m->iomem->pgcr[1] = 0;	/* OP2 low to enable PCMCIA */
+	iopunlock();
+iprint("B=%8.8lux\n", m->bcsr[0]);
+}
+
+int
+pcmpowered(int)
+{
+	ulong r;
+
+	r = m->bcsr[0]&VCCMask;
+	if(r == VCC5V)
+		return 5;
+	if(r == VCC3V)
+		return 3;
+	return 0;
+}
+
+void
+pcmsetvcc(int, int v)
+{
+	if(v == 5)
+		v = VCC5V;
+	else if(v == 3)
+		v = VCC3V;
+	else
+		v = VCC0V;
+	ioplock();
+	m->bcsr[0] = (m->bcsr[0] & ~VCCMask) | v;
+	iopunlock();
+}
+
+void
+pcmsetvpp(int, int v)
+{
+	if(v == 5 || v == 3)
+		v = VPPVCC;
+	else if(v == 12)
+		v = VPP12V;
+	else if(v == 0)
+		v = VPP0V;
+	else
+		v = VPPHiZ;
+	ioplock();
+	m->bcsr[0] = (m->bcsr[0] & ~VPPMask) | v;
+	iopunlock();
+}
+
+void
+pcmpower(int slotno, int on)
+{
+	if(!on){
+		pcmsetvcc(slotno, 0);	/* turn off card power */
+		pcmsetvpp(slotno, -1);	/* turn off programming voltage (Hi-Z) */
+	}else
+		pcmsetvcc(slotno, 5);
+}
+
+/*
+ * enable/disable the LCD panel's backlight
+ */
+void
+archbacklight(int on)
+{
+	USED(on);
+}
+
+/*
+ * set parameters to describe the screen
+ */
+int
+archlcdmode(Mode *m)
+{
+	m->x = 640;
+	m->y = 480;
+	m->d = 3;
+	m->lcd.freq = 25000000;
+	m->lcd.ac = 0;
+	m->lcd.vpw = 1;
+	m->lcd.wbf = 33;
+	m->lcd.wbl = 228;
+	m->lcd.flags = IsColour | IsTFT | OELow | VsyncLow | ClockLow;
+	return -1;	/* there isn't a screen */
+}
+
+/*
+ * there isn't a keyboard port
+ */
+void
+archkbdinit(void)
+{
+}
+
+void
+archflashwp(Flash*, int)
+{
+}
--- /dev/null
+++ b/os/rpcg/archrpcg.h
@@ -1,0 +1,48 @@
+/*
+ * values for RPXLite AW
+ */
+enum {
+	/* CS assignment */
+	BOOTCS = 0,
+	DRAM1CS = 1,
+	DRAM2CS = 2,
+	BCSRCS = 3,
+	NVRAMCS = 4,
+	/* expansion header on CS5 */
+	PCMCIA0CS=	6,	/* even bytes(!); CS6 to header if OP2 high */
+	PCMCIA1CS= 7,	/* odd bytes(!); CS7 to header if OP2 high */
+};
+
+/*
+ * BCSR bits (there's only one register)
+ */
+enum {
+	EnableEnet =	IBIT(0),
+	EnableXcrLB=	IBIT(1),
+	DisableColTest=	IBIT(2),
+	DisableFullDplx=IBIT(3),
+	LedOff=		IBIT(4),
+	DisableUSB=	IBIT(5),
+	HighSpdUSB=	IBIT(6),
+	EnableUSBPwr=	IBIT(7),
+	/* 8,9,10 unused */
+	VCCMask=	IBIT(12)|IBIT(13),
+	VPPMask=	IBIT(14)|IBIT(15),
+	VCC0V=	0,
+	VCC5V=	IBIT(13),
+	VCC3V=	IBIT(12),
+	VPP0V=	0,
+	VPPVCC=	IBIT(14),
+	VPP12V=	IBIT(15),
+	VPPHiZ=	IBIT(14)|IBIT(15),
+	/* 16-23 NYI */
+	DipSwitchMask=	IBIT(24)|IBIT(25)|IBIT(26)|IBIT(27),
+	DipSwitch0=	IBIT(24),
+	DipSwitch1=	IBIT(25),
+	DipSwitch2=	IBIT(26),
+	DipSwitch3=	IBIT(27),
+	/* bit 28 RESERVED */
+	FlashComplete=	IBIT(29),
+	NVRAMBattGood=	IBIT(30),
+	RTCBattGood=	IBIT(31),
+};
--- /dev/null
+++ b/os/rpcg/clock.c
@@ -1,0 +1,145 @@
+#include	"u.h"
+#include	"../port/lib.h"
+#include	"mem.h"
+#include	"dat.h"
+#include	"fns.h"
+#include	"io.h"
+#include	"ureg.h"
+
+#include	<isa.h>
+#include	<interp.h>
+
+typedef struct Clock0link Clock0link;
+typedef struct Clock0link {
+	void		(*clock)(void);
+	Clock0link*	link;
+} Clock0link;
+
+static Clock0link *clock0link;
+static Lock clock0lock;
+ulong	clkrelinq;
+void	(*kproftick)(ulong);	/* set by devkprof.c when active */
+void	(*archclocktick)(void);	/* set by arch*.c when desired */
+
+Timer*
+addclock0link(void (*clock)(void), int)
+{
+	Clock0link *lp;
+
+	if((lp = malloc(sizeof(Clock0link))) == 0){
+		print("addclock0link: too many links\n");
+		return nil;
+	}
+	ilock(&clock0lock);
+	lp->clock = clock;
+	lp->link = clock0link;
+	clock0link = lp;
+	iunlock(&clock0lock);
+	return nil;
+}
+
+void
+delay(int l)
+{
+	ulong i, j;
+
+	j = m->delayloop;
+	while(l-- > 0)
+		for(i=0; i < j; i++)
+			;
+}
+
+void
+microdelay(int l)
+{
+	ulong i;
+
+	l *= m->delayloop;
+	l /= 1000;
+	if(l <= 0)
+		l = 1;
+	for(i = 0; i < l; i++)
+		;
+}
+
+enum {
+	Timebase = 2,	/* system clock cycles per time base cycle */
+};
+
+static	ulong	clkreload;
+
+void
+clockinit(void)
+{
+	long x;
+
+	m->delayloop = m->cpuhz/1000;	/* initial estimate */
+	do {
+		x = gettbl();
+		delay(10);
+		x = gettbl() - x;
+	} while(x < 0);
+
+	/*
+	 *  fix count
+	 */
+	m->delayloop = ((vlong)m->delayloop*(10*m->clockgen/1000))/(x*Timebase);
+	if(m->delayloop == 0)
+		m->delayloop = 1;
+
+	clkreload = (m->clockgen/Timebase)/HZ-1;
+	putdec(clkreload);
+}
+
+void
+clockintr(Ureg *ur)
+{
+	Clock0link *lp;
+	long v;
+
+	v = -getdec();
+	if(v > clkreload/2){
+		if(v > clkreload)
+			m->ticks += v/clkreload;
+		v = 0;
+	}
+	putdec(clkreload-v);
+
+	/* watchdog */
+	if(m->iomem->sypcr & (1<<2)){
+		m->iomem->swsr = 0x556c;
+		m->iomem->swsr = 0xaa39;
+	}
+
+	m->ticks++;
+	if(archclocktick != nil)
+		archclocktick();
+
+	if(up)
+		up->pc = ur->pc;
+
+	checkalarms();
+	if(m->machno == 0) {
+		if(kproftick != nil)
+			(*kproftick)(ur->pc);
+		if(canlock(&clock0lock)){
+			for(lp = clock0link; lp; lp = lp->link)
+				lp->clock();
+			unlock(&clock0lock);
+		}
+	}
+
+	if(up && up->state == Running){
+		if(cflag && up->type == Interp && tready(nil))
+			ur->cr |= 1;	/* set flag in condition register for ../../libinterp/comp-power.c:/^schedcheck */
+	}
+	/* other preemption checks are done by trap.c */
+}
+
+uvlong
+fastticks(uvlong *hz)
+{
+	if(hz)
+		*hz = HZ;
+	return m->ticks;
+}
--- /dev/null
+++ b/os/rpcg/dat.h
@@ -1,0 +1,162 @@
+typedef struct Conf	Conf;
+typedef struct FPU	FPU;
+typedef struct FPenv	FPenv;
+typedef struct IMM	IMM;
+typedef struct Irqctl	Irqctl;
+typedef struct ISAConf	ISAConf;
+typedef struct Label	Label;
+typedef struct Lock	Lock;
+typedef struct Mach	Mach;
+typedef struct Map	Map;
+typedef struct Power Power;
+typedef struct RMap RMap;
+typedef struct Ureg	Ureg;
+
+typedef ulong Instr;
+
+#define	MACHP(n)	(n==0? &mach0 : *(Mach**)0)
+
+struct	Lock
+{
+	ulong	key;
+	ulong	pc;
+	ulong	sr;
+	int	pri;
+};
+
+struct	Label
+{
+	ulong	sp;
+	ulong	pc;
+};
+
+/*
+ * Proc.fpstate
+ */
+enum
+{
+	FPINIT,
+	FPACTIVE,
+	FPINACTIVE,
+};
+
+/*
+ * This structure must agree with FPsave and FPrestore asm routines
+ */
+struct FPenv
+{
+	union {
+		double	fpscrd;
+		struct {
+			ulong	pad;
+			ulong	fpscr;
+		};
+	};
+	int	fpistate;	/* emulated fp */
+	ulong	emreg[32][3];	/* emulated fp */
+};
+/*
+ * This structure must agree with fpsave and fprestore asm routines
+ */
+struct	FPU
+{
+	double	fpreg[32];
+	FPenv	env;
+};
+
+struct Conf
+{
+	ulong	nmach;		/* processors */
+	ulong	nproc;		/* processes */
+	ulong	npage0;		/* total physical pages of memory */
+	ulong	npage1;		/* total physical pages of memory */
+	ulong	npage;		/* total physical pages of memory */
+	ulong	base0;		/* base of bank 0 */
+	ulong	base1;		/* base of bank 1 */
+	ulong	ialloc;		/* max interrupt time allocation in bytes */
+
+	int	nscc;	/* number of SCCs implemented */
+	ulong	smcuarts;		/* bits for SMCs to define as eiaN */
+	ulong	sccuarts;		/* bits for SCCs to define as eiaN  */
+	int	nocts2;	/* CTS2 and CD2 aren't connected */
+	uchar*	nvrambase;	/* virtual address of nvram */
+	ulong	nvramsize;	/* size in bytes */
+};
+
+#include "../port/portdat.h"
+
+/*
+ *  machine dependent definitions not used by ../port/dat.h
+ */
+
+struct Mach
+{
+	/* OFFSETS OF THE FOLLOWING KNOWN BY l.s */
+	int	machno;			/* physical id of processor (unused) */
+	ulong	splpc;			/* pc of last caller to splhi (unused) */
+	int	mmask;			/* 1<<m->machno (unused) */
+
+	/* ordering from here on irrelevant */
+	ulong	ticks;			/* of the clock since boot time */
+	Proc	*proc;			/* current process on this processor */
+	Label	sched;			/* scheduler wakeup */
+	Lock	alarmlock;		/* access to alarm list */
+	void	*alarm;			/* alarms bound to this clock */
+	int	nrdy;
+	int	speed;	/* general system clock in MHz */
+	long	oscclk;	/* oscillator frequency (MHz) */
+	long	cpuhz;	/* general system clock (cycles) */
+	long	clockgen;	/* clock generator frequency (cycles) */
+	int	cputype;
+	ulong	delayloop;
+	ulong*	bcsr;
+	IMM*	iomem;	/* MPC8xx internal i/o control memory */
+
+	/* MUST BE LAST */
+	int	stack[1];
+};
+extern	Mach	mach0;
+
+
+/*
+ *  a parsed .ini line
+ */
+#define NISAOPT		8
+
+struct ISAConf {
+	char*	type;
+	ulong	port;
+	ulong	irq;
+	ulong	mem;
+	int	dma;
+	ulong	size;
+	ulong	freq;
+	uchar	bus;
+
+	int	nopt;
+	char*	opt[NISAOPT];
+};
+
+struct Map {
+	int	size;
+	ulong	addr;
+};
+
+struct RMap {
+	char*	name;
+	Map*	map;
+	Map*	mapend;
+
+	Lock;
+};
+
+struct Power {
+	Dev*	dev;
+	int	(*powerdown)(Power*);
+	int	(*powerup)(Power*);
+	int	state;
+	void*	arg;
+};
+
+extern register Mach	*m;
+extern register Proc	*up;
--- /dev/null
+++ b/os/rpcg/fns.h
@@ -1,0 +1,119 @@
+#include "../port/portfns.h"
+
+void	addpower(Power*);
+void	archbacklight(int);
+void	archconfinit(void);
+void	archdisableuart(int);
+void	archdisableusb(void);
+void	archdisablevideo(void);
+void	archenableuart(int, int);
+void	archenableusb(int, int);
+void	archenablevideo(void);
+void	archkbdinit(void);
+void	archresetvideo(void);
+int	archetherenable(int, int*, int*, int, int);
+void	archinit(void);
+int	archoptionsw(void);
+void	archreboot(void);
+void	archsetirxcvr(int);
+uchar*	archvideobuffer(long);
+ulong	baudgen(int, int);
+int	brgalloc(void);
+void	brgfree(int);
+int	cistrcmp(char*, char*);
+int	cistrncmp(char*, char*, int);
+void	clockcheck(void);
+void	clockinit(void);
+void	clockintr(Ureg*);
+void	clrfptrap(void);
+#define	coherence()		/* nothing needed for uniprocessor */
+void	cpminit(void);
+void	cpuidprint(void);
+void	dcflush(void*, ulong);
+void	dcinval(void*, ulong);
+void	delay(int);
+void	dtlbmiss(void);
+void	dumplongs(char*, ulong*, int);
+void	dumpregs(Ureg*);
+void	eieio(void);
+void	faultpower(Ureg*);
+void	firmware(int);
+void	fpinit(void);
+int	fpipower(Ureg*);
+void	fpoff(void);
+void	fprestore(FPU*);
+void	fpsave(FPU*);
+ulong	fpstatus(void);
+char*	getconf(char*);
+ulong	getdar(void);
+ulong	getdec(void);
+ulong	getdepn(void);
+ulong	getdsisr(void);
+ulong	getimmr(void);
+ulong	getmsr(void);
+ulong	getpvr(void);
+ulong	gettbl(void);
+ulong	gettbu(void);
+void	gotopc(ulong);
+void	icflush(void*, ulong);
+void	idle(void);
+#define	idlehands()			/* nothing to do in the runproc */
+void	intr(Ureg*);
+void	intrenable(int, void (*)(Ureg*, void*), void*, int, char*);
+void	intrdisable(int, void (*)(Ureg*, void*), void*, int, char*);
+int	intrstats(char*, int);
+void	intrvec(void);
+int	isaconfig(char*, int, ISAConf*);
+int	isvalid_va(void*);
+void	itlbmiss(void);
+void	kbdinit(void);
+void	kbdreset(void);
+void	lcdpanel(int);
+void	links(void);
+void	mapfree(RMap*, ulong, int);
+void	mapinit(RMap*, Map*, int);
+void	mathinit(void);
+void	mmuinit(void);
+ulong*	mmuwalk(ulong*, ulong, int);
+void	pcmenable(void);
+void	pcmintrenable(int, void (*)(Ureg*, void*), void*);
+int	pcmpin(int slot, int type);
+void	pcmpower(int, int);
+int	pcmpowered(int);
+void	pcmsetvcc(int, int);
+void	pcmsetvpp(int, int);
+int	pcmslotavail(int);
+void	procsave(Proc*);
+void	procsetup(Proc*);
+void	putdec(ulong);
+void	putmsr(ulong);
+void	puttwb(ulong);
+ulong	rmapalloc(RMap*, ulong, int, int);
+void	screeninit(void);
+int	screenprint(char*, ...);			/* debugging */
+void	screenputs(char*, int);
+int	segflush(void*, ulong);
+void	setpanic(void);
+long	spioutin(void*, long, void*);
+void	spireset(void);
+ulong	_tas(ulong*);
+void	trapinit(void);
+void	trapvec(void);
+void	uartinstall(void);
+void	uartspecial(int, int, Queue**, Queue**, int (*)(Queue*, int));
+void	uartwait(void);	/* debugging */
+void	videoreset(void);
+void	videotest(void);
+void	wbflush(void);
+
+#define	waserror()	(up->nerrlab++, setlabel(&up->errlab[up->nerrlab-1]))
+ulong	getcallerpc(void*);
+
+#define KADDR(a)	((void*)((ulong)(a)|KZERO))
+#define PADDR(a)	((((ulong)(a)&KSEGM)!=KSEG0)?(ulong)(a):((ulong)(a)&~KZERO))
+
+/* IBM bit field order */
+#define	IBIT(b)	((ulong)1<<(31-(b)))
+#define	SIBIT(n)	((ushort)1<<(15-(n)))
+
+/* compatibility with inf2.1 */
--- /dev/null
+++ b/os/rpcg/io.h
@@ -1,0 +1,1 @@
+#include "../mpc/800io.h"
--- /dev/null
+++ b/os/rpcg/main.c
@@ -1,0 +1,392 @@
+#include	"u.h"
+#include	"../port/lib.h"
+#include	"mem.h"
+#include	"dat.h"
+#include	"fns.h"
+#include	"io.h"
+#include	"ureg.h"
+#include "version.h"
+
+/* where b.com or qboot leaves configuration info */
+#define BOOTARGS	((char*)CONFADDR)
+#define	BOOTARGSLEN	1024
+#define	MAXCONF		32
+
+extern ulong kerndate;
+extern int cflag;
+int	remotedebug;
+
+extern int main_pool_pcnt;
+extern int heap_pool_pcnt;
+extern int image_pool_pcnt;
+
+char	bootargs[BOOTARGSLEN+1];
+char bootdisk[KNAMELEN];
+char *confname[MAXCONF];
+char *confval[MAXCONF];
+int nconf;
+
+extern void addconf(char *, char *);
+
+/*
+ *  arguments passed to initcode and /boot
+ */
+char argbuf[128];
+
+static void
+options(void)
+{
+	long i, n;
+	char *cp, *line[MAXCONF], *p, *q;
+
+	/*
+	 *  parse configuration args from bootstrap
+	 */
+	memmove(bootargs, BOOTARGS, BOOTARGSLEN);	/* where b.com leaves its config */
+	cp = bootargs;
+	cp[BOOTARGSLEN-1] = 0;
+
+	/*
+	 * Strip out '\r', change '\t' -> ' '.
+	 */
+	p = cp;
+	for(q = cp; *q; q++){
+		if(*q == '\r')
+			continue;
+		if(*q == '\t')
+			*q = ' ';
+		*p++ = *q;
+	}
+	*p = 0;
+
+	n = getfields(cp, line, MAXCONF, 1, "\n");
+	for(i = 0; i < n; i++){
+		if(*line[i] == '#')
+			continue;
+		cp = strchr(line[i], '=');
+		if(cp == 0)
+			continue;
+		*cp++ = 0;
+		confname[nconf] = line[i];
+		confval[nconf] = cp;
+		nconf++;
+	}
+}
+
+void
+doc(char *m)
+{
+	USED(m);
+	print("%s...\n", m); uartwait();
+}
+
+static void
+poolsizeinit(void)
+{
+	ulong nb;
+
+	nb = conf.npage*BY2PG;
+	poolsize(mainmem, (nb*main_pool_pcnt)/100, 0);
+	poolsize(heapmem, (nb*heap_pool_pcnt)/100, 0);
+	poolsize(imagmem, (nb*image_pool_pcnt)/100, 1);
+}
+
+static void
+serialconsole(void)
+{
+	char *p;
+	int port, baud;
+
+	p = getconf("console");
+	if(p == nil)
+		p = "0";
+	if(p != nil && !remotedebug){
+		port = strtol(p, nil, 0);
+		baud = 9600;
+		p = getconf("baud");
+		if(p != nil){
+			baud = strtol(p, nil, 0);
+			if(baud < 9600)
+				baud = 9600;
+		}
+		uartspecial(port, baud, &kbdq, &printq, kbdcr2nl);
+	}
+}
+
+void
+main(void)
+{
+	machinit();
+	options();
+	archinit();
+	quotefmtinstall();
+	confinit();
+	cpminit();
+	xinit();
+	poolsizeinit();
+	trapinit();
+	mmuinit();
+	printinit();
+	uartinstall();
+	serialconsole();
+	doc("screeninit");
+	screeninit();
+	doc("kbdinit");
+	kbdinit();
+	doc("clockinit");
+	clockinit();
+	doc("procinit");
+	procinit();
+	cpuidprint();
+	doc("links");
+	links();
+	doc("chandevreset");
+	chandevreset();
+
+	eve = strdup("inferno");
+
+	print("\nInferno %s\n", VERSION);
+	print("Vita Nuova\n");
+	print("conf %s (%lud) jit %d\n\n",conffile, kerndate, cflag);
+
+	doc("userinit");
+	userinit();
+	doc("schedinit");
+	schedinit();
+}
+
+void
+machinit(void)
+{
+	int n;
+
+	n = m->machno;
+	memset(m, 0, sizeof(Mach));
+	m->machno = n;
+	m->mmask = 1<<m->machno;
+	m->iomem = KADDR(getimmr() & ~0xFFFF);
+	m->cputype = getpvr()>>16;
+	m->delayloop = 20000;	/* initial estimate only; set by clockinit */
+	m->speed = 50;	/* initial estimate only; set by archinit */
+}
+
+void
+init0(void)
+{
+	Osenv *o;
+	int i;
+	char buf[2*KNAMELEN];
+
+	up->nerrlab = 0;
+
+	spllo();
+
+	if(waserror())
+		panic("init0");
+	/*
+	 * These are o.k. because rootinit is null.
+	 * Then early kproc's will have a root and dot.
+	 */
+	o = up->env;
+	o->pgrp->slash = namec("#/", Atodir, 0, 0);
+	cnameclose(o->pgrp->slash->name);
+	o->pgrp->slash->name = newcname("/");
+	o->pgrp->dot = cclone(o->pgrp->slash);
+
+	chandevinit();
+
+	if(!waserror()){
+		ksetenv("cputype", "power", 0);
+		snprint(buf, sizeof(buf), "power %s", conffile);
+		ksetenv("terminal", buf, 0);
+		poperror();
+	}
+	for(i = 0; i < nconf; i++)
+		if(confname[i][0] != '*'){
+			if(!waserror()){
+				ksetenv(confname[i], confval[i], 0);
+				poperror();
+			}
+		}
+
+	poperror();
+	disinit("/osinit.dis");
+}
+
+void
+userinit(void)
+{
+	Proc *p;
+	Osenv *o;
+
+	p = newproc();
+	o = p->env;
+
+	o->fgrp = newfgrp(nil);
+
+	o->pgrp = newpgrp();
+	o->egrp = newegrp();
+	kstrdup(&o->user, eve);
+
+	strcpy(p->text, "interp");
+
+	/*
+	 * Kernel Stack
+	 */
+	p->sched.pc = (ulong)init0;
+	p->sched.sp = (ulong)p->kstack+KSTACK;
+
+	ready(p);
+}
+
+Conf	conf;
+
+void
+addconf(char *name, char *val)
+{
+	if(nconf >= MAXCONF)
+		return;
+	confname[nconf] = name;
+	confval[nconf] = val;
+	nconf++;
+}
+
+char*
+getconf(char *name)
+{
+	int i;
+
+	for(i = 0; i < nconf; i++)
+		if(cistrcmp(confname[i], name) == 0)
+			return confval[i];
+	return 0;
+}
+
+void
+confinit(void)
+{
+	char *p;
+	int pcnt;
+
+	if(p = getconf("*kernelpercent"))
+		pcnt = 100 - strtol(p, 0, 0);
+	else
+		pcnt = 0;
+
+	conf.nscc = 4;
+	conf.smcuarts = 1<<0;	/* SMC1 (usual console) */
+	conf.sccuarts = 0;	/* SCC2 not available by default (it's Ether) */
+
+	archconfinit();
+	
+	conf.npage = conf.npage0 + conf.npage1;
+	if(pcnt < 10)
+		pcnt = 70;
+	conf.ialloc = (((conf.npage*(100-pcnt))/100)/2)*BY2PG;
+
+	conf.nproc = 100 + ((conf.npage*BY2PG)/MB)*5;
+	conf.nmach = MAXMACH;
+}
+
+void
+exit(int ispanic)
+{
+	up = 0;
+	spllo();
+	print("cpu %d exiting\n", m->machno);
+
+	/* Shutdown running devices */
+	chandevshutdown();
+
+	delay(1000);
+	splhi();
+	if(ispanic)
+		for(;;);
+	archreboot();
+}
+
+void
+reboot(void)
+{
+	exit(0);
+}
+
+void
+halt(void)
+{
+	print("cpu halted\n");
+	microdelay(1000);
+	for(;;)
+		;
+}
+
+int
+isaconfig(char *class, int ctlrno, ISAConf *isa)
+{
+	char cc[KNAMELEN], *p;
+	int i;
+
+	snprint(cc, sizeof cc, "%s%d", class, ctlrno);
+	p = getconf(cc);
+	if(p == nil)
+		return 0;
+
+	isa->nopt = tokenize(p, isa->opt, NISAOPT);
+	for(i = 0; i < isa->nopt; i++){
+		p = isa->opt[i];
+		if(cistrncmp(p, "type=", 5) == 0)
+			isa->type = p + 5;
+		else if(cistrncmp(p, "port=", 5) == 0)
+			isa->port = strtoul(p+5, &p, 0);
+		else if(cistrncmp(p, "irq=", 4) == 0)
+			isa->irq = strtoul(p+4, &p, 0);
+		else if(cistrncmp(p, "mem=", 4) == 0)
+			isa->mem = strtoul(p+4, &p, 0);
+		else if(cistrncmp(p, "size=", 5) == 0)
+			isa->size = strtoul(p+5, &p, 0);
+		else if(cistrncmp(p, "freq=", 5) == 0)
+			isa->freq = strtoul(p+5, &p, 0);
+		else if(cistrncmp(p, "dma=", 4) == 0)
+			isa->dma = strtoul(p+4, &p, 0);
+	}
+	return 1;
+}
+
+/*
+ *  Save the mach dependent part of the process state.
+ */
+void
+procsave(Proc*)
+{
+}
+
+void
+uartputs(char *s, int n)
+{
+//	screenputs(buf, n);
+	putstrn(s, n);
+	uartwait();
+}
+
+/* stubs */
+void
+setfsr(ulong)
+{
+}
+
+ulong
+getfsr()
+{
+	return 0;
+}
+
+void
+setfcr(ulong)
+{
+}
+
+ulong
+getfcr()
+{
+	return 0;
+}
--- /dev/null
+++ b/os/rpcg/mem.h
@@ -1,0 +1,157 @@
+/*
+ * Memory and machine-specific definitions.  Used in C and assembler.
+ */
+
+/*
+ * Sizes
+ */
+
+#define	BI2BY		8			/* bits per byte */
+#define BI2WD		32			/* bits per word */
+#define	BY2WD		4			/* bytes per word */
+#define	BY2V		8			/* bytes per double word */
+#define	BY2PG		4096			/* bytes per page */
+#define	WD2PG		(BY2PG/BY2WD)		/* words per page */
+#define	PGSHIFT		12			/* log(BY2PG) */
+#define ROUND(s, sz)	(((s)+(sz-1))&~(sz-1))
+#define PGROUND(s)	ROUND(s, BY2PG)
+#define	CACHELINELOG	4
+#define CACHELINESZ	(1<<CACHELINELOG)
+
+#define	MAXMACH		1			/* max # cpus system can run */
+#define	MACHSIZE	BY2PG
+
+/*
+ * Time
+ */
+#define HZ		100			/* clock frequency */
+#define	MS2HZ		(1000/HZ)		/* millisec per clock tick */
+#define	TK2SEC(t)	((t)/HZ)		/* ticks to seconds */
+#define	MS2TK(t)	((t)/MS2HZ)		/* milliseconds to ticks */
+#define	MHz	1000000
+
+/*
+ * MSR bits
+ */
+
+#define	POW	0x40000	/* enable power mgmt */
+#define	TGPR	0x20000	/* GPR0-3 remapped; 603/603e specific */
+#define	ILE	0x10000	/* interrupts little endian */
+#define	EE	0x08000	/* enable external/decrementer interrupts */
+#define	PR	0x04000	/* =1, user mode */
+#define	FPE	0x02000	/* enable floating point */
+#define	ME	0x01000	/* enable machine check exceptions */
+#define	FE0	0x00800
+#define	SE	0x00400	/* single-step trace */
+#define	BE	0x00200	/* branch trace */
+#define	FE1	0x00100
+#define	MSR_IP	0x00040	/* =0, vector to nnnnn; =1, vector to FFFnnnnn */
+#define	IR	0x00020	/* enable instruction address translation */
+#define	DR	0x00010	/* enable data address translation */
+#define	RI	0x00002	/* exception is recoverable */
+#define	LE	0x00001	/* little endian mode */
+
+#define	KMSR	(ME|FE0|FE1|FPE)
+#define	UMSR	(KMSR|PR|EE|IR|DR)
+
+/*
+ * Magic registers
+ */
+
+#define	MACH		30		/* R30 is m-> */
+#define	USER		29		/* R29 is up-> */
+#define	IOMEMR		28		/* R28 will be iomem-> */
+
+/*
+ * Fundamental addresses
+ */
+
+#define	UREGSIZE	((8+32)*4)
+
+/*
+ * MMU
+ */
+
+/* L1 table entry and Mx_TWC flags */
+#define PTEVALID	(1<<0)
+#define PTEWT		(1<<1)	/* write through */
+#define PTE4K		(0<<2)
+#define PTE512K	(1<<2)
+#define PTE8MB	(3<<2)
+#define PTEG		(1<<4)	/* guarded */
+
+/* L2 table entry and Mx_RPN flags (also PTEVALID) */
+#define PTECI		(1<<1)	/*  cache inhibit */
+#define PTESH		(1<<2)	/* page is shared; ASID ignored */
+#define PTELPS		(1<<3)	/* large page size */
+#define PTEWRITE	0x9F0
+
+/* TLB and MxEPN flag */
+#define	TLBVALID	(1<<9)
+
+/*
+ * Address spaces
+ */
+
+#define	KUSEG	0x00000000
+#define KSEG0	0x20000000
+#define	KSEGM	0xE0000000	/* mask to check which seg */
+
+#define	KZERO	KSEG0			/* base of kernel address space */
+#define	KTZERO	(KZERO+0x3000)	/* first address in kernel text */
+#define	KSTACK	8192	/* Size of kernel stack */
+
+#define	CONFADDR	(KZERO|0x200000)	/* where qboot leaves configuration info */
+
+/*
+ * Exception codes (trap vectors)
+ */
+#define	CRESET	0x01
+#define	CMCHECK 0x02
+#define	CDSI	0x03
+#define	CISI	0x04
+#define	CEI	0x05
+#define	CALIGN	0x06
+#define	CPROG	0x07
+#define	CFPU	0x08
+#define	CDEC	0x09
+#define	CSYSCALL 0x0C
+#define	CTRACE	0x0D
+#define	CFPA	0x0E
+/* rest are power-implementation dependent (8xx) */
+#define	CEMU	0x10
+#define	CIMISS	0x11
+#define	CDMISS	0x12
+#define	CITLBE	0x13
+#define	CDTLBE	0x14
+#define	CDBREAK	0x1C
+#define	CIBREAK	0x1D
+#define	CPBREAK	0x1E
+#define	CDPORT	0x1F
+
+/*
+ * MPC8xx physical addresses
+ */
+
+/* those encouraged by rpx lite */
+#define	PHYSDRAM	0x00000000
+#define	PHYSNVRAM	0xFA000000
+#define	PHYSIMM	0xFA200000
+#define	PHYSBCSR	0xFA400000
+#define	PHYSFLASH	0xFE000000
+
+/* remaining ones are our choice */
+#define	PHYSPCMCIA	0x04000000
+#define	PCMCIALEN	(8*MB)	/* chosen to allow mapping by single TLB entry */
+#define	ISAIO	(KZERO|PHYSPCMCIA)	/* for inb.s */
+
+/*
+ * MPC8xx dual-ported CPM memory physical addresses
+ */
+#define	PHYSDPRAM	(PHYSIMM+0x2000)
+#define	DPLEN1	0x200
+#define	DPLEN2	0x400
+#define	DPLEN3	0x800
+#define	DPBASE	(PHYSDPRAM+DPLEN1)
+
+#define KEEP_ALIVE_KEY 0x55ccaa33	/* clock and rtc register key */
--- /dev/null
+++ b/os/rpcg/mkfile
@@ -1,0 +1,108 @@
+SYSTARG=Inferno
+OBJTYPE=power
+<../../mkconfig
+
+#Configurable parameters
+
+CONF=rpcg			#default configuration
+CONFLIST=rpcg
+KZERO=0x20003020
+
+SYSTARG=$OSTARG
+OBJTYPE=power
+INSTALLDIR=$ROOT/Inferno/$OBJTYPE/bin	#path of directory where kernel is installed
+#INSTALLDIR=/$OBJTYPE
+
+#end configurable parameters
+
+<$ROOT/mkfiles/mkfile-$SYSTARG-$OBJTYPE	#set vars based on target system
+
+<| $SHELLNAME ../port/mkdevlist $CONF	#sets $IP, $DEVS, $ETHERS, $VGAS, $PORT, $MISC, $LIBS, $OTHERS
+
+OBJ=\
+	l.$O\
+	tlb.$O\
+	nofp.$O\
+	clock.$O\
+	cpm.$O\
+	faultpower.$O\
+	fpi.$O\
+	fpimem.$O\
+	fpipower.$O\
+	kbd.$O\
+	main.$O\
+	mmu.$O\
+	rmap.$O\
+	trap.$O\
+	$CONF.root.$O\
+	$IP\
+	$DEVS\
+	$ETHERS\
+	$LINKS\
+	$VGAS\
+	$PORT\
+	$MISC\
+	$OTHERS\
+
+LIBNAMES=${LIBS:%=lib%.a}
+
+HFILES=\
+	mem.h\
+	dat.h\
+	fns.h\
+	io.h\
+	../mpc/800io.h\
+	../mpc/screen.h\
+
+CFLAGS=-wFV -I. -I../mpc -I../port -I$ROOT/Inferno/$OBJTYPE/include -I$ROOT/include -I$ROOT/libinterp
+KERNDATE=`{$NDATE}
+
+#default:V: i$CONF.sq
+default:V:	i$CONF
+
+i$CONF:	$OBJ $CONF.c $CONF.root.h $LIBNAMES
+	$CC $CFLAGS '-DKERNDATE='$KERNDATE $CONF.c
+	$LD -o $target -T$KZERO -l $OBJ $CONF.$O $LIBFILES
+	$KSIZE $target
+
+i$CONF.sq:	i$CONF
+	sqz -w i$CONF >$target
+
+install:V: i$CONF # i$CONF.sq
+	cp i$CONF $INSTALLDIR/i$CONF
+	#cp i$CONF.sq $INSTALLDIR/i$CONF.sq
+
+uninstall:V:
+	rm -f $ROOT/$OBJDIR/bin/i$CONF
+	rm -f $ROOT/$OBJDIR/bin/i$CONF.sq
+
+<../port/portmkfile
+
+../init/$INIT.dis:	../init/$INIT.b
+		cd ../init; mk $INIT.dis
+
+clock.$O:	$ROOT/Inferno/$OBJTYPE/include/ureg.h
+devether.$O:	$ROOT/Inferno/$OBJTYPE/include/ureg.h
+faultpower.$O:	$ROOT/Inferno/$OBJTYPE/include/ureg.h
+main.$O:	$ROOT/Inferno/$OBJTYPE/include/ureg.h
+trap.$O:	$ROOT/Inferno/$OBJTYPE/include/ureg.h
+
+devether.$O $ETHERS:	../mpc/etherif.h ../port/netif.h
+
+#$VGAS:		screen.h vga.h
+$IP devip.$O:		../ip/ip.h
+
+%.$O:		../mpc/%.c
+	$CC $CFLAGS -I. -I../mpc ../mpc/$stem.c
+
+%.$O:		../mpc/%.s
+	$AS -I. -I../mpc ../mpc/$stem.s
+
+clock.$O:	clock.c
+	$CC $CFLAGS -I. clock.c
+
+devboot.$O:	devboot.c
+	$CC $CFLAGS devboot.c
+
+devuart.$O:	../mpc/devuart.c
+	$CC $CFLAGS ../mpc/devuart.c
--- /dev/null
+++ b/os/rpcg/mmu.c
@@ -1,0 +1,20 @@
+#include	"u.h"
+#include	"../port/lib.h"
+#include	"mem.h"
+#include	"dat.h"
+#include	"fns.h"
+
+void
+mmuinit(void)
+{
+	/* the l.s initial TLB settings do all that's required */
+}
+
+int
+segflush(void *a, ulong n)
+{
+	/* flush dcache then invalidate icache */
+	dcflush(a, n);
+	icflush(a, n);
+	return 0;
+}
--- /dev/null
+++ b/os/rpcg/rpcg
@@ -1,0 +1,125 @@
+# rpcg RPXLite
+dev
+	root
+	cons	archrpcg
+	env
+	mnt
+	pipe
+	prog
+	rtc
+	srv
+	dup
+	ssl
+	cap
+
+
+	ip	bootp ip ipv6 ipaux iproute arp netlog ptclbsum iprouter plan9 nullmedium pktmedium
+	ether netif netaux
+	uart
+	flash
+#	usb
+#	pcmcia	cis
+#	ata	inb
+
+	ftl
+#	kfs		chk kcon console dat dentry fcall fs fswren iobuf kfs sub uid
+#	kprof
+
+#	vid	i2c
+	i2c	i2c
+
+ip
+	il
+	tcp
+	udp
+	ipifc
+	icmp
+	icmp6
+	ipmux
+
+lib
+	interp
+	tk
+	draw
+	memlayer
+	memdraw
+	keyring
+	sec
+	mp
+	math
+	kern
+
+link
+	etherscc
+	ethermedium
+	flashamd29f0x0
+#	flashcfi16
+#	pppmedium ppp compress
+
+mod
+	sys
+#	draw
+#	tk
+	math
+	keyring
+	crypt
+	ipints
+
+
+port
+	alarm
+	alloc
+	allocb
+	chan
+	dev
+	dial
+	dis
+	discall
+	exception
+	exportfs
+	inferno
+	latin1
+	nocache
+	nodynld
+	parse
+	pgrp
+	print
+	proc
+	qio
+	qlock
+	random
+	sysfile
+	taslock
+	xalloc
+
+code
+	int cflag = 0;
+	int consoleprint = 1;
+	int panicreset = 0;
+	int kernel_pool_pcnt = 10;
+	int main_pool_pcnt = 40;
+	int heap_pool_pcnt = 20;
+	int image_pool_pcnt = 40;
+	void screenputs(char*, int){}
+	void screeninit(void){}	/* disabled until we've got one */
+
+init
+	rpcginit
+
+root
+	/chan
+	/dev
+	/dis
+	/env
+	/fd	/
+	/n
+	/net
+	/nvfs /
+	/prog
+	/icons
+	/osinit.dis
+	/dis/lib/auth.dis
+	/dis/lib/ssl.dis
+	/n/local /
+	/n/remote	/
+	/nvfs/default	/usr/inferno/keyring/default
--- /dev/null
+++ b/os/rpcg/tlb.s
@@ -1,0 +1,24 @@
+#include "mem.h"
+
+#define	MB	(1024*1024)
+
+/*
+ * TLB prototype entries, loaded once-for-all at startup,
+ * remaining unchanged thereafter.
+ * Limit the table to at most 8 entries to ensure
+ * it works on the 823 (other 8xx processors allow up to 32 TLB entries).
+ */
+#define	TLBE(epn,rpn,twc)	WORD	$(epn);  WORD	$(twc); WORD	$(rpn)
+
+TEXT	tlbtab(SB), $-4
+
+	/* epn, rpn, twc */
+	TLBE(KZERO|PHYSDRAM|TLBVALID, PHYSDRAM|PTEWRITE|PTELPS|PTESH|PTEVALID, PTE8MB|/*PTEWT|*/PTEVALID)	/* DRAM, 8M */
+	TLBE(KZERO|(PHYSDRAM+8*MB)|TLBVALID, (PHYSDRAM+8*MB)|PTEWRITE|PTELPS|PTESH|PTEVALID, PTE8MB|PTEVALID)	/* DRAM, 8M */
+	TLBE(KZERO|PHYSBCSR|TLBVALID, PHYSBCSR|PTEWRITE|PTESH|PTECI|PTEVALID, PTE4K|PTEWT|PTEVALID)	/* Board CSR, 4K */
+	TLBE(KZERO|PHYSIMM|TLBVALID, PHYSIMM|PTEWRITE|PTELPS|PTESH|PTECI|PTEVALID, PTE4K|PTEWT|PTEVALID)	/* IMMR, 16K */
+	TLBE(KZERO|PHYSFLASH|TLBVALID, PHYSFLASH|PTEWRITE|PTELPS|PTESH|PTECI|PTEVALID, PTE8MB|PTEWT|PTEVALID)	/* Flash, 8M */
+	TLBE(KZERO|PHYSPCMCIA|TLBVALID, PHYSPCMCIA|PTEWRITE|PTELPS|PTESH|PTECI|PTEVALID, PTE8MB|PTEWT|PTEG|PTEVALID)	/* PCMCIA, 8M */
+	TLBE(KZERO|PHYSNVRAM|TLBVALID, PHYSNVRAM|PTEWRITE|PTELPS|PTESH|PTECI|PTEVALID, PTE512K|PTEWT|PTEG|PTEVALID)	/* NVRAM, 512K */
+TEXT	tlbtabe(SB), $-4
+	RETURN
--- /dev/null
+++ b/os/sa1110/clock.c
@@ -1,0 +1,317 @@
+#include "u.h"
+#include "../port/lib.h"
+#include "mem.h"
+#include "dat.h"
+#include "fns.h"
+#include "io.h"
+
+#include "ureg.h"
+
+static ulong timer_incr[4] = { 0, 0, 0, -1 };
+
+typedef struct Clock0link Clock0link;
+typedef struct Clock0link {
+	void		(*clock)(void);
+	Clock0link*	link;
+} Clock0link;
+
+static Clock0link *clock0link;
+static Lock clock0lock;
+static void (*prof_fcn)(Ureg *, int);
+
+Timer*
+addclock0link(void (*clock)(void), int)
+{
+	Clock0link *lp;
+
+	if((lp = malloc(sizeof(Clock0link))) == 0){
+		print("addclock0link: too many links\n");
+		return nil;
+	}
+	ilock(&clock0lock);
+	lp->clock = clock;
+	lp->link = clock0link;
+	clock0link = lp;
+	iunlock(&clock0lock);
+	return nil;
+}
+
+static void
+profintr(Ureg *ur, void*)
+{
+	OstmrReg *ost = OSTMRREG;
+	int t;
+
+	if((ost->osmr[3] - ost->oscr) < 2*CLOCKFREQ) {
+		/* less than 2 seconds before reset, say something */
+		setpanic();
+		clockpoll();
+		dumpregs(ur);
+		panic("Watchdog timer will expire");
+	}
+
+	/* advance the profile clock tick */
+	ost->osmr[2] += timer_incr[2];
+	ost->ossr = (1 << 2); 			/* Clear the SR */
+	t = 1;
+	while((ost->osmr[2] - ost->oscr) > 0x80000000) {
+		ost->osmr[2] += timer_incr[2];
+		t++;
+	}
+	if(prof_fcn)
+		prof_fcn(ur, t);
+}
+
+static void
+clockintr(Ureg*, void*)
+{
+	Clock0link *lp;
+	int losttick = 0;
+	OstmrReg *ost = OSTMRREG;
+
+	m->ticks++;
+	ost->osmr[3] = ost->oscr + timer_incr[3]; /* restart the watchdog */
+	ost->osmr[0] += timer_incr[0];		/* advance the clock tick */
+	ost->ossr = (1<<0); 			/* Clear the SR */
+
+	while((ost->osmr[0] - ost->oscr) >= 0x80000000) {
+		ost->osmr[0] += timer_incr[0]; 
+		losttick++;
+		m->ticks++;
+	}
+
+	checkalarms();
+
+	if(canlock(&clock0lock)){
+		for(lp = clock0link; lp; lp = lp->link)
+			if(lp->clock)
+				lp->clock();
+		unlock(&clock0lock);
+	}
+
+	/* round robin time slice is done by trap.c and proc.c */
+}
+
+void
+timerenable( int timer, int Hz, void (*f)(Ureg *, void*), void* a)
+{
+	OstmrReg *ost = OSTMRREG;
+	char name[KNAMELEN];
+
+	if(timer < 0 || timer > 3)
+		return;
+	timer_incr[timer] = CLOCKFREQ/Hz;		/* set up freq */
+	ost->osmr[timer] = ost->oscr+timer_incr[timer];
+	snprint(name, sizeof(name), "timer%d", timer);
+	intrenable(OSTimerbit(timer), f, a, BusCPU, name);
+	ost->ossr = (1 << timer);	/* clear any pending interrupt */
+	ost->oier |= (1 << timer);		/* enable interrupt */
+}
+
+void
+timerdisable( int timer )
+{
+	OstmrReg *ost = OSTMRREG;
+
+	if(timer < 0 || timer > 3)
+		return;
+	ost->osmr[timer] = 0;				/* clear freq */
+	ost->oier &= ~(1 << timer);			/* disable interrupt */
+}
+
+void
+installprof(void (*pf)(Ureg *, int))
+{
+	int s;
+
+	s = splfhi();
+	prof_fcn = pf;
+	timerenable( 2, HZ+1, profintr, 0);
+	timer_incr[2] = timer_incr[0]+63;	/* fine tuning */
+	splx(s);
+}
+
+void
+clockinit(void)
+{
+	OstmrReg *ost = OSTMRREG;
+	m->ticks = 0;
+	/* Set up timer registers */
+	ost->ossr = 0xf;	/* clear all four OSSR trigger bits */
+	ost->oier = 0;
+	ost->osmr[0] = 0;
+	ost->osmr[1] = 0;
+	ost->osmr[2] = 0;
+	//	ost->osmr[3] = 0;
+	timerenable( 0, HZ, clockintr, 0);
+	timer_incr[3] = CLOCKFREQ*10;	/* 10 second watchdog */
+	timer_setwatchdog(timer_incr[3]);
+	timerenable( 2, 1, profintr, 0);	/* watch the watchdog */
+}
+
+void
+clockpoll(void)
+{
+	OstmrReg *ost = OSTMRREG;
+	ost->osmr[3] = ost->oscr + timer_incr[3];	/* restart the watchdog */
+}
+
+void
+clockcheck(void)
+{
+	OstmrReg *ost = OSTMRREG;
+
+	if((ost->osmr[3] - ost->oscr) < CLOCKFREQ) {
+		setpanic();
+		clockpoll();
+		dumpstack();
+		panic("Watchdog timer will expire");
+	}
+}
+
+// macros for fixed-point math
+
+ulong _mularsv(ulong m0, ulong m1, ulong a, ulong s);
+
+/* truncated: */
+#define FXDPTDIV(a,b,n) ((ulong)(((uvlong)(a) << (n)) / (b)))
+#define MAXMUL(a,n)     ((ulong)((((uvlong)1<<(n))-1)/(a)))
+#define MULDIV(x,a,b,n) (((x)*FXDPTDIV(a,b,n)) >> (n)) 
+#define MULDIV64(x,a,b,n) ((ulong)_mularsv(x, FXDPTDIV(a,b,n), 0, (n)))
+
+/* rounded: */
+#define FXDPTDIVR(a,b,n) ((ulong)((((uvlong)(a) << (n))+((b)/2)) / (b)))
+#define MAXMULR(a,n)     ((ulong)((((uvlong)1<<(n))-1)/(a)))
+#define MULDIVR(x,a,b,n) (((x)*FXDPTDIVR(a,b,n)+(1<<((n)-1))) >> (n)) 
+#define MULDIVR64(x,a,b,n) ((ulong)_mularsv(x, FXDPTDIVR(a,b,n), 1<<((n)-1), (n)))
+
+
+// these routines are all limited to a maximum of 1165 seconds,
+// due to the wrap-around of the OSTIMER
+
+ulong
+timer_start(void)
+{
+	return OSTMRREG->oscr;
+}
+
+ulong
+timer_ticks(ulong t0)
+{
+	return OSTMRREG->oscr - t0;
+}
+
+int
+timer_devwait(ulong *adr, ulong mask, ulong val, int ost)
+{
+	int i;
+	ulong t0 = timer_start();
+	while((*adr & mask) != val) 
+		if(timer_ticks(t0) > ost)
+			return ((*adr & mask) == val) ? 0 : -1;
+		else
+			for(i = 0; i < 10; i++);	/* don't pound OSCR too hard! (why not?) */
+	return 0;
+}
+
+void
+timer_setwatchdog(int t)
+{
+	OstmrReg *ost = OSTMRREG;
+	ost->osmr[3] = ost->oscr + t;
+	if(t) {
+		ost->ossr = (1<<3);
+		ost->oier |= (1<<3);
+		ost->ower = 1;
+	} else 
+		ost->oier &= ~(1<<3);
+}
+
+void
+timer_delay(int t)
+{	
+	ulong t0 = timer_start();
+	while(timer_ticks(t0) < t)
+		;
+}
+
+
+ulong
+us2tmr(int us)
+{
+	return MULDIV64(us, CLOCKFREQ, 1000000, 24);
+}
+
+int
+tmr2us(ulong t)
+{
+	return MULDIV64(t, 1000000, CLOCKFREQ, 24);
+}
+
+void
+microdelay(int us)
+{
+	ulong t0 = timer_start();
+	ulong t = us2tmr(us);
+	while(timer_ticks(t0) <= t)
+		;
+}
+
+ulong
+ms2tmr(int ms)
+{
+	return MULDIV64(ms, CLOCKFREQ, 1000, 20);
+}
+
+int
+tmr2ms(ulong t)
+{
+	return MULDIV64(t, 1000, CLOCKFREQ, 32);
+}
+
+void
+delay(int ms)
+{
+	ulong t0 = timer_start();
+	ulong t = ms2tmr(ms);
+	while(timer_ticks(t0) <= t)
+		clockpoll();
+}
+
+/*
+ * for devbench.c
+ */
+vlong
+archrdtsc(void)
+{
+	return OSTMRREG->oscr;
+}
+
+ulong
+archrdtsc32(void)
+{
+	return OSTMRREG->oscr;
+}
+
+/*
+ * for devkprof.c
+ */
+long
+archkprofmicrosecondspertick(void)
+{
+	return MS2HZ*1000;
+}
+
+void
+archkprofenable(int)
+{
+	/* TO DO */
+}
+
+uvlong
+fastticks(uvlong *hz)
+{
+	if(hz)
+		*hz = HZ;
+	return m->ticks;
+}
--- /dev/null
+++ b/os/sa1110/devether.c
@@ -1,0 +1,617 @@
+#include "u.h"
+#include "../port/lib.h"
+#include "mem.h"
+#include "dat.h"
+#include "fns.h"
+#include "io.h"
+#include "ureg.h"
+#include "../port/error.h"
+#include "../port/netif.h"
+
+#include "etherif.h"
+
+static Ether *etherxx[MaxEther];
+
+Chan*
+etherattach(char* spec)
+{
+	ulong ctlrno;
+	char *p;
+	Chan *chan;
+	Ether *ether;
+
+	ctlrno = 0;
+	if(spec && *spec){
+		ctlrno = strtoul(spec, &p, 0);
+		if((ctlrno == 0 && p == spec) || *p || (ctlrno >= MaxEther))
+			error(Ebadarg);
+	}
+	if((ether = etherxx[ctlrno]) == 0)
+		error(Enodev);
+	rlock(ether);
+	if(waserror()){
+		runlock(ether);
+		nexterror();
+	}
+	chan = devattach('l', spec);
+	chan->dev = ctlrno;
+	if(ether->attach)
+		ether->attach(etherxx[ctlrno]);
+	poperror();
+	runlock(ether);
+	return chan;
+}
+
+static void
+ethershutdown(void)
+{
+	Ether *ether;
+	int i;
+
+	for(i=0; i<MaxEther; i++){
+		ether = etherxx[i];
+		if(ether != nil && ether->detach != nil)
+			ether->detach(ether);
+	}
+}
+
+static Walkqid*
+etherwalk(Chan* chan, Chan *nchan, char **name, int nname)
+{
+	Walkqid *wq;
+	Ether *ether;
+
+	ether = etherxx[chan->dev];
+	rlock(ether);
+	if(waserror()) {
+		runlock(ether);
+		nexterror();
+	}
+	wq = netifwalk(etherxx[chan->dev], chan, nchan, name, nname);
+	poperror();
+	runlock(ether);
+	return wq;
+}
+
+static int
+etherstat(Chan* chan, uchar* dp, int n)
+{
+	int s;
+	Ether *ether;
+
+	ether = etherxx[chan->dev];
+	rlock(ether);
+	if(waserror()) {
+		runlock(ether);
+		nexterror();
+	}
+	s = netifstat(ether, chan, dp, n);
+	poperror();
+	runlock(ether);
+	return s;
+}
+
+static Chan*
+etheropen(Chan* chan, int omode)
+{
+	Chan *c;
+	Ether *ether;
+
+	ether = etherxx[chan->dev];
+	rlock(ether);
+	if(waserror()) {
+		runlock(ether);
+		nexterror();
+	}
+	c = netifopen(ether, chan, omode);
+	poperror();
+	runlock(ether);
+	return c;
+}
+
+static void
+ethercreate(Chan*, char*, int, ulong)
+{
+}
+
+static void
+etherclose(Chan* chan)
+{
+	Ether *ether;
+
+	ether = etherxx[chan->dev];
+	rlock(ether);
+	if(waserror()) {
+		runlock(ether);
+		nexterror();
+	}
+	netifclose(ether, chan);
+	poperror();
+	runlock(ether);
+}
+
+static long
+etherread(Chan* chan, void* buf, long n, vlong off)
+{
+	Ether *ether;
+	ulong offset = off;
+	long r;
+
+	ether = etherxx[chan->dev];
+	rlock(ether);
+	if(waserror()) {
+		runlock(ether);
+		nexterror();
+	}
+	if((chan->qid.type & QTDIR) == 0 && ether->ifstat){
+		/*
+		 * With some controllers it is necessary to reach
+		 * into the chip to extract statistics.
+		 */
+		if(NETTYPE(chan->qid.path) == Nifstatqid){
+			r = ether->ifstat(ether, buf, n, offset);
+			goto out;
+		}
+		if(NETTYPE(chan->qid.path) == Nstatqid)
+			ether->ifstat(ether, buf, 0, offset);
+	}
+	r = netifread(ether, chan, buf, n, offset);
+out:
+	poperror();
+	runlock(ether);
+	return r;
+}
+
+static Block*
+etherbread(Chan* chan, long n, ulong offset)
+{
+	Block *b;
+	Ether *ether;
+
+	ether = etherxx[chan->dev];
+	rlock(ether);
+	if(waserror()) {
+		runlock(ether);
+		nexterror();
+	}
+	b = netifbread(ether, chan, n, offset);
+	poperror();
+	runlock(ether);
+	return b;
+}
+
+static int
+etherwstat(Chan* chan, uchar* dp, int n)
+{
+	Ether *ether;
+	int r;
+
+	ether = etherxx[chan->dev];
+	rlock(ether);
+	if(waserror()) {
+		runlock(ether);
+		nexterror();
+	}
+	r = netifwstat(ether, chan, dp, n);
+	poperror();
+	runlock(ether);
+	return r;
+}
+
+static void
+etherrtrace(Netfile* f, Etherpkt* pkt, int len)
+{
+	int i, n;
+	Block *bp;
+
+	if(qwindow(f->in) <= 0)
+		return;
+	if(len > 58)
+		n = 58;
+	else
+		n = len;
+	bp = iallocb(64);
+	if(bp == nil)
+		return;
+	memmove(bp->wp, pkt->d, n);
+	i = TK2MS(MACHP(0)->ticks);
+	bp->wp[58] = len>>8;
+	bp->wp[59] = len;
+	bp->wp[60] = i>>24;
+	bp->wp[61] = i>>16;
+	bp->wp[62] = i>>8;
+	bp->wp[63] = i;
+	bp->wp += 64;
+	qpass(f->in, bp);
+}
+
+Block*
+etheriq(Ether* ether, Block* bp, int fromwire)
+{
+	Etherpkt *pkt;
+	ushort type;
+	int len, multi, tome, fromme;
+	Netfile **ep, *f, **fp, *fx;
+	Block *xbp;
+
+	ether->inpackets++;
+
+	pkt = (Etherpkt*)bp->rp;
+	len = BLEN(bp);
+	type = (pkt->type[0]<<8)|pkt->type[1];
+	fx = 0;
+	ep = &ether->f[Ntypes];
+
+	multi = pkt->d[0] & 1;
+	/* check for valid multcast addresses */
+	if(multi && memcmp(pkt->d, ether->bcast, sizeof(pkt->d)) != 0 && ether->prom == 0){
+		if(!activemulti(ether, pkt->d, sizeof(pkt->d))){
+			if(fromwire){
+				freeb(bp);
+				bp = 0;
+			}
+			return bp;
+		}
+	}
+
+	/* is it for me? */
+	tome = memcmp(pkt->d, ether->ea, sizeof(pkt->d)) == 0;
+	fromme = memcmp(pkt->s, ether->ea, sizeof(pkt->s)) == 0;
+
+	/*
+	 * Multiplex the packet to all the connections which want it.
+	 * If the packet is not to be used subsequently (fromwire != 0),
+	 * attempt to simply pass it into one of the connections, thereby
+	 * saving a copy of the data (usual case hopefully).
+	 */
+	for(fp = ether->f; fp < ep; fp++){
+		if((f = *fp) && (f->type == type || f->type < 0))
+		if(tome || multi || f->prom){
+			/* Don't want to hear bridged packets */
+			if(f->bridge && !fromwire && !fromme)
+				continue;
+			if(!f->headersonly){
+				if(fromwire && fx == 0)
+					fx = f;
+				else if(xbp = iallocb(len)){
+					memmove(xbp->wp, pkt, len);
+					xbp->wp += len;
+					if(qpass(f->in, xbp) < 0)
+						ether->soverflows++;
+				}
+				else
+					ether->soverflows++;
+			}
+			else
+				etherrtrace(f, pkt, len);
+		}
+	}
+
+	if(fx){
+		if(qpass(fx->in, bp) < 0)
+			ether->soverflows++;
+		return 0;
+	}
+	if(fromwire){
+		freeb(bp);
+		return 0;
+	}
+
+	return bp;
+}
+
+static int
+etheroq(Ether* ether, Block* bp)
+{
+	int len, loopback, s;
+	Etherpkt *pkt;
+
+	ether->outpackets++;
+
+	/*
+	 * Check if the packet has to be placed back onto the input queue,
+	 * i.e. if it's a loopback or broadcast packet or the interface is
+	 * in promiscuous mode.
+	 * If it's a loopback packet indicate to etheriq that the data isn't
+	 * needed and return, etheriq will pass-on or free the block.
+	 * To enable bridging to work, only packets that were originated
+	 * by this interface are fed back.
+	 */
+	pkt = (Etherpkt*)bp->rp;
+	len = BLEN(bp);
+	loopback = memcmp(pkt->d, ether->ea, sizeof(pkt->d)) == 0;
+	if(loopback || memcmp(pkt->d, ether->bcast, sizeof(pkt->d)) == 0 || ether->prom){
+		s = splhi();
+		etheriq(ether, bp, 0);
+		splx(s);
+	}
+
+	if(!loopback){
+		qbwrite(ether->oq, bp);
+		if(ether->transmit != nil)
+			ether->transmit(ether);
+	}else
+		freeb(bp);
+
+	return len;
+}
+
+static long
+etherwrite(Chan* chan, void* buf, long n, vlong)
+{
+	Ether *ether;
+	Block *bp;
+	int onoff;
+	Cmdbuf *cb;
+	long l;
+
+	ether = etherxx[chan->dev];
+	rlock(ether);
+	if(waserror()) {
+		runlock(ether);
+		nexterror();
+	}
+	if(NETTYPE(chan->qid.path) != Ndataqid) {
+		l = netifwrite(ether, chan, buf, n);
+		if(l >= 0)
+			goto out;
+		cb = parsecmd(buf, n);
+		if(strcmp(cb->f[0], "nonblocking") == 0){
+			if(cb->nf <= 1)
+				onoff = 1;
+			else
+				onoff = atoi(cb->f[1]);
+			qnoblock(ether->oq, onoff);
+			free(cb);
+			goto out;
+		}
+		free(cb);
+		if(ether->ctl!=nil){
+			l = ether->ctl(ether,buf,n);
+			goto out;
+		}
+		error(Ebadctl);
+	}
+
+	if(n > ether->maxmtu)
+		error(Etoobig);
+	if(n < ether->minmtu)
+		error(Etoosmall);
+	bp = allocb(n);
+	if(waserror()){
+		freeb(bp);
+		nexterror();
+	}
+	memmove(bp->rp, buf, n);
+	memmove(bp->rp+Eaddrlen, ether->ea, Eaddrlen);
+	bp->wp += n;
+	poperror();
+
+	l = etheroq(ether, bp);
+out:
+	poperror();
+	runlock(ether);
+	return l;
+}
+
+static long
+etherbwrite(Chan* chan, Block* bp, ulong)
+{
+	Ether *ether;
+	long n;
+
+	n = BLEN(bp);
+	if(NETTYPE(chan->qid.path) != Ndataqid){
+		if(waserror()) {
+			freeb(bp);
+			nexterror();
+		}
+		n = etherwrite(chan, bp->rp, n, 0);
+		poperror();
+		freeb(bp);
+		return n;
+	}
+	ether = etherxx[chan->dev];
+	rlock(ether);
+	if(waserror()) {
+		runlock(ether);
+		nexterror();
+	}
+	if(n > ether->maxmtu){
+		freeb(bp);
+		error(Etoobig);
+	}
+	if(n < ether->minmtu){
+		freeb(bp);
+		error(Etoosmall);
+	}
+	n = etheroq(ether, bp);
+	poperror();
+	runlock(ether);
+	return n;
+}
+
+static struct {
+	char*	type;
+	int	(*reset)(Ether*);
+} cards[MaxEther+1];
+
+void
+addethercard(char* t, int (*r)(Ether*))
+{
+	static int ncard;
+
+	if(ncard == MaxEther)
+		panic("too many ether cards");
+	cards[ncard].type = t;
+	cards[ncard].reset = r;
+	ncard++;
+}
+
+int
+parseether(uchar *to, char *from)
+{
+	char nip[4];
+	char *p;
+	int i;
+
+	p = from;
+	for(i = 0; i < Eaddrlen; i++){
+		if(*p == 0)
+			return -1;
+		nip[0] = *p++;
+		if(*p == 0)
+			return -1;
+		nip[1] = *p++;
+		nip[2] = 0;
+		to[i] = strtoul(nip, 0, 16);
+		if(*p == ':')
+			p++;
+	}
+	return 0;
+}
+
+static void
+etherreset(void)
+{
+	Ether *ether;
+	int i, n, ctlrno;
+	char name[KNAMELEN], buf[128];
+
+	for(ether = 0, ctlrno = 0; ctlrno < MaxEther; ctlrno++){
+		if(ether == 0)
+			ether = malloc(sizeof(Ether));
+		memset(ether, 0, sizeof(Ether));
+		ether->ctlrno = ctlrno;
+		ether->mbps = 10;
+		ether->minmtu = ETHERMINTU;
+		ether->maxmtu = ETHERMAXTU;
+		ether->itype = -1;
+
+		if(archether(ctlrno, ether) <= 0)
+			continue;
+
+		for(n = 0; cards[n].type; n++){
+			if(cistrcmp(cards[n].type, ether->type))
+				continue;
+			for(i = 0; i < ether->nopt; i++){
+				if(cistrncmp(ether->opt[i], "ea=", 3) == 0){
+					if(parseether(ether->ea, &ether->opt[i][3]) == -1)
+						memset(ether->ea, 0, Eaddrlen);
+				}else if(cistrcmp(ether->opt[i], "fullduplex") == 0 ||
+					cistrcmp(ether->opt[i], "10BASE-TFD") == 0)
+					ether->fullduplex = 1;
+				else if(cistrcmp(ether->opt[i], "100BASE-TXFD") == 0)
+					ether->mbps = 100;
+			}
+			if(cards[n].reset(ether))
+				break;
+			snprint(name, sizeof(name), "ether%d", ctlrno);
+
+			if(ether->interrupt != nil)
+				intrenable(ether->irq, ether->interrupt, ether, ether->itype, name);
+
+			i = sprint(buf, "#l%d: %s: %dMbps port 0x%luX irq %lud",
+				ctlrno, ether->type, ether->mbps, ether->port, ether->irq);
+			if(ether->mem)
+				i += sprint(buf+i, " addr 0x%luX", PADDR(ether->mem));
+			if(ether->size)
+				i += sprint(buf+i, " size 0x%luX", ether->size);
+			i += sprint(buf+i, ": %2.2uX%2.2uX%2.2uX%2.2uX%2.2uX%2.2uX",
+				ether->ea[0], ether->ea[1], ether->ea[2],
+				ether->ea[3], ether->ea[4], ether->ea[5]);
+			sprint(buf+i, "\n");
+			iprint(buf);
+
+			if(ether->mbps == 100){
+				netifinit(ether, name, Ntypes, 256*1024);
+				if(ether->oq == 0)
+					ether->oq = qopen(256*1024, Qmsg, 0, 0);
+			}
+			else{
+				netifinit(ether, name, Ntypes, 64*1024);
+				if(ether->oq == 0)
+					ether->oq = qopen(64*1024, Qmsg, 0, 0);
+			}
+			if(ether->oq == 0)
+				panic("etherreset %s", name);
+			ether->alen = Eaddrlen;
+			memmove(ether->addr, ether->ea, Eaddrlen);
+			memset(ether->bcast, 0xFF, Eaddrlen);
+
+			etherxx[ctlrno] = ether;
+			ether = 0;
+			break;
+		}
+	}
+	if(ether)
+		free(ether);
+}
+
+static void
+etherpower(int on)
+{
+	int i;
+	Ether *ether;
+
+	for(i = 0; i < MaxEther; i++){
+		if((ether = etherxx[i]) == nil || ether->power == nil)
+			continue;
+		if(on){
+			if(canrlock(ether))
+				continue;
+			if(ether->power != nil)
+				ether->power(ether, on);
+			wunlock(ether);
+		}else{
+			if(!canrlock(ether))
+				continue;
+			wlock(ether);
+			if(ether->power != nil)
+				ether->power(ether, on);
+			/* Keep locked until power goes back on */
+		}
+	}
+}
+
+#define POLY 0xedb88320
+
+/* really slow 32 bit crc for ethers */
+ulong
+ethercrc(uchar *p, int len)
+{
+	int i, j;
+	ulong crc, b;
+
+	crc = 0xffffffff;
+	for(i = 0; i < len; i++){
+		b = *p++;
+		for(j = 0; j < 8; j++){
+			crc = (crc>>1) ^ (((crc^b) & 1) ? POLY : 0);
+			b >>= 1;
+		}
+	}
+	return crc;
+}
+
+Dev etherdevtab = {
+	'l',
+	"ether",
+
+	etherreset,
+	devinit,
+	ethershutdown,
+	etherattach,
+	etherwalk,
+	etherstat,
+	etheropen,
+	ethercreate,
+	etherclose,
+	etherread,
+	etherbread,
+	etherwrite,
+	etherbwrite,
+	devremove,
+	etherwstat,
+	etherpower,
+};
--- /dev/null
+++ b/os/sa1110/devgpio.c
@@ -1,0 +1,165 @@
+#include	"u.h"
+#include	"../port/lib.h"
+#include	"mem.h"
+#include	"dat.h"
+#include	"fns.h"
+#include	"../port/error.h"
+#include	"io.h"
+
+enum{
+	Qdir,
+	Qgpioset,
+	Qgpioclear,
+	Qgpioedge,
+	Qgpioctl,
+	Qgpiostatus,
+};
+
+Dirtab gpiodir[]={
+	".",				{Qdir,0},			0,	0555,
+	"gpioset",			{Qgpioset, 0},		0,	0664,
+	"gpioclear",		{Qgpioclear, 0},		0,	0664,
+	"gpioedge",		{Qgpioedge, 0},		0,	0664,
+	"gpioctl",			{Qgpioctl,0},		0,	0664,
+	"gpiostatus",		{Qgpiostatus,0},	0,	0444,
+};
+
+static Chan*
+gpioattach(char* spec)
+{
+	return devattach('G', spec);
+}
+
+static Walkqid*
+gpiowalk(Chan* c, Chan *nc, char **name, int nname)
+{
+	return devwalk(c, nc, name, nname, gpiodir, nelem(gpiodir), devgen);
+}
+
+static int	 
+gpiostat(Chan* c, uchar *dp, int n)
+{
+	return devstat(c, dp, n, gpiodir, nelem(gpiodir), devgen);
+}
+
+static Chan*
+gpioopen(Chan* c, int omode)
+{
+	return devopen(c, omode, gpiodir, nelem(gpiodir), devgen);
+}
+
+static void	 
+gpioclose(Chan*)
+{
+}
+
+static long	 
+gpioread(Chan* c, void *buf, long n, vlong offset)
+{
+	char str[128];
+	GpioReg *g;
+	
+	if(c->qid.type & QTDIR)
+		return devdirread(c, buf, n, gpiodir, nelem(gpiodir), devgen);
+
+	g = GPIOREG;
+	switch((ulong)c->qid.path){
+	case Qgpioset:
+	case Qgpioclear:
+		sprint(str, "%8.8lux", g->gplr);
+		break;
+	case Qgpioedge:
+		sprint(str, "%8.8lux", g->gedr);
+		break;
+	case Qgpioctl:
+		/* return 0; */
+	case Qgpiostatus:
+		snprint(str, sizeof(str), "GPDR:%8.8lux\nGRER:%8.8lux\nGFER:%8.8lux\nGAFR:%8.8lux\nGPLR:%8.8lux\n", g->gpdr, g->grer, g->gfer, g->gafr, g->gplr);
+		break;
+	default:
+		error(Ebadarg);
+		return 0;
+	}
+	return readstr(offset, buf, n, str);
+}
+
+static long	 
+gpiowrite(Chan *c, void *a, long n, vlong)
+{
+	char buf[128], *field[3];
+	int pin, set;
+	ulong *r;
+	GpioReg *g;
+
+	if(n >= sizeof(buf))
+		n = sizeof(buf)-1;
+	memmove(buf, a, n);
+	buf[n] = 0;	
+	g = GPIOREG;
+	switch((ulong)c->qid.path){
+	case Qgpioset:
+		g->gpsr = strtol(buf, 0, 16);
+		break;
+	case Qgpioclear:
+		g->gpcr = strtol(buf, 0, 16);
+		break;
+	case Qgpioedge:
+		g->gedr = strtol(buf, 0, 16);
+		break;
+	case Qgpioctl:
+		if(getfields(buf, field, 3, 1, " \n\t") == 3) {
+			pin = strtol(field[1], 0, 0);
+			if(pin < 0 || pin >= 32)
+				error(Ebadarg);
+			set = strtol(field[2], 0, 0);
+			switch(*field[0]) {
+			case 'd':
+				r = &g->gpdr;
+				break;
+			case 'r':
+				r = &g->grer;
+				break;
+			case 'f':
+				r = &g->gfer;
+				break;
+			case 'a':
+				r = &g->gafr;
+				break;
+			default:
+				error(Ebadarg);
+				return 0;
+			}
+			if(set)
+				*r |= 1 << pin;
+			else
+				*r &= ~(1 << pin);
+		} else
+			error(Ebadarg);
+		break;
+	default:
+		error(Ebadusefd);
+		return 0;
+	}
+	return n;
+}
+
+Dev gpiodevtab = {
+	'G',
+	"gpio",
+
+	devreset,
+	devinit,
+	devshutdown,
+	gpioattach,
+	gpiowalk,
+	gpiostat,
+	gpioopen,
+	devcreate,
+	gpioclose,
+	gpioread,
+	devbread,
+	gpiowrite,
+	devbwrite,
+	devremove,
+	devwstat,
+};
--- /dev/null
+++ b/os/sa1110/devpcmcia.c
@@ -1,0 +1,761 @@
+#include "u.h"
+#include "../port/lib.h"
+#include "mem.h"
+#include "dat.h"
+#include "fns.h"
+#include "io.h"
+#include "../port/error.h"
+
+int pcmdebug=0;
+#define DPRINT if(pcmdebug)iprint
+#define DPRINT1 if(pcmdebug > 1)iprint
+#define DPRINT2 if(pcmdebug > 2)iprint
+#define PCMERR(x)	pce(x);
+
+enum
+{
+	Qdir,
+	Qmem,
+	Qattr,
+	Qctl,
+};
+
+#define SLOTNO(c)	(((ulong)c->qid.path>>8)&0xff)
+#define TYPE(c)		((ulong)c->qid.path&0xff)
+#define QID(s,t)	(((s)<<8)|(t))
+
+/*
+ *  Support for 2 card slots usng StrongArm pcmcia support.
+ *
+ */
+enum
+{
+	/*
+	 *  configuration registers - they start at an offset in attribute
+	 *  memory found in the CIS.
+	 */
+	Rconfig=	0,
+	 Creset=	 (1<<7),	/*  reset device */
+	 Clevel=	 (1<<6),	/*  level sensitive interrupt line */
+
+};
+
+
+enum	{
+	Maxctab=	8,	/* maximum configuration table entries */
+	Maxslot=	2
+};
+
+static struct {
+	Ref;
+} pcmcia;
+
+static PCMslot	slot[Maxslot];
+static PCMslot *lastslot ;
+static	int	nslot = Maxslot;
+
+static void	slotdis(PCMslot *);
+static void	pcmciaintr(Ureg*, void*);
+static void	pcmciareset(void);
+static int	pcmio(int, ISAConf*);
+static long	pcmread(int, int, void*, long, ulong);
+static long	pcmwrite(int, int, void*, long, ulong);
+static void slottiming(int, int, int, int, int);
+static void slotmap(int, ulong, ulong, ulong);
+
+static void pcmciadump(PCMslot*);
+
+static ulong GPIOrdy[2];
+static ulong GPIOeject[2];
+static ulong GPIOall[2];
+
+/*
+ *  get info about card
+ */
+static void
+slotinfo(PCMslot *pp)
+{
+	ulong gplr;
+	int was;
+
+	gplr = GPIOREG->gplr;
+	was = pp->occupied;
+	pp->occupied = (gplr & GPIOeject[pp->slotno]) ? 0 : 1;
+	pp->busy = (gplr & GPIOrdy[pp->slotno]) ? 0 : 1;
+	pp->powered = pcmpowered(pp->slotno);
+	pp->battery = 0;
+	pp->wrprot = 0;
+	if (!was & pp->occupied)
+		print("PCMCIA card %d inserted\n", pp->slotno);
+	if (was & !pp->occupied)
+		print("PCMCIA card %d removed!\n", pp->slotno);
+}
+
+/*
+ *  enable the slot card
+ */
+static void
+slotena(PCMslot *pp)
+{
+	if(pp->enabled)
+		return;
+	DPRINT("Enable slot# %d\n", pp->slotno);
+	DPRINT("pcmcia ready %8.8lux\n", GPIOREG->gplr & GPIOrdy[pp->slotno]);
+
+	/* get configuration */
+	slotinfo(pp);
+	if(pp->occupied){
+		if(pp->cisread == 0){
+			pcmcisread(pp);
+			pp->cisread = 1;
+		}
+		pp->enabled = 1;
+	} else
+		slotdis(pp);
+}
+
+/*
+ *  disable the slot card
+ */
+static void
+slotdis(PCMslot *pp)
+{
+	if (pp->enabled)
+		DPRINT("Disable slot# %d\n", pp->slotno);
+	pp->enabled = 0;
+	pp->cisread = 0;
+}
+
+/*
+ *  status change interrupt
+ */
+static void
+pcmciaintr(Ureg*, void*)
+{
+	uchar was;
+	PCMslot *pp;
+
+	if(slot == 0)
+		return;
+	for(pp = slot; pp < lastslot; pp++){
+		was = pp->occupied;
+		slotinfo(pp);
+		if(0 && !pp->occupied){
+			if(was != pp->occupied){
+				slotdis(pp);
+//				if (pp->special && pp->notify.f)
+//					(*pp->notify.f)(ur, pp->notify.a, 1);
+			}
+		}
+	}
+}
+
+static void
+increfp(PCMslot *pp)
+{
+	if(up){
+		wlock(pp);
+		if(waserror()){
+			wunlock(pp);
+			nexterror();
+		}
+	}
+	if(incref(&pcmcia) == 1){
+		pcmpower(pp->slotno, 1);
+		pcmreset(pp->slotno);
+		delay(500);
+	}
+
+	if(incref(&pp->ref) == 1)
+		slotena(pp);
+	if(up){
+		poperror();
+		wunlock(pp);
+	}
+}
+
+static void
+decrefp(PCMslot *pp)
+{
+	if(decref(&pp->ref) == 0)
+		slotdis(pp);
+	if(decref(&pcmcia) == 0)
+		pcmpower(pp->slotno, 0);
+}
+
+/*
+ *  look for a card whose version contains 'idstr'
+ */
+int
+pcmspecial(char *idstr, ISAConf *isa)
+{
+	PCMslot *pp;
+
+	pcmciareset();
+	for(pp = slot; pp < lastslot; pp++){
+		if(pp->special)
+			continue;	/* already taken */
+		increfp(pp);
+
+		if(pp->occupied)
+		if(strstr(pp->verstr, idstr)){
+			DPRINT("PCMslot #%d: Found %s - ",pp->slotno, idstr);
+			if(isa == 0 || pcmio(pp->slotno, isa) == 0){
+				DPRINT("ok.\n");
+				pp->special = 1;
+				return pp->slotno;
+			}
+			print("error with isa io for %s\n", idstr);
+		}
+		decrefp(pp);
+	}
+	return -1;
+}
+
+void
+pcmspecialclose(int slotno)
+{
+	PCMslot *pp;
+	int s;
+
+	if(slotno < 0 || slotno >= nslot)
+		panic("pcmspecialclose");
+	pp = slot + slotno;
+	pp->special = 0;	/* Is this OK ? */
+	s = splhi();
+	GPIOREG->gfer &= ~GPIOrdy[pp->slotno];	/* TO DO: intrdisable */
+	GPIOREG->grer &= ~GPIOrdy[pp->slotno];
+	splx(s);
+	decrefp(pp);
+}
+
+static int
+pcmgen(Chan *c, char*, Dirtab*, int, int i, Dir *dp)
+{
+	int slotno;
+	Qid qid;
+	long len;
+	PCMslot *pp;
+
+	if(i == DEVDOTDOT){
+		mkqid(&qid, Qdir, 0, QTDIR);
+		devdir(c, qid, "#y", 0, eve, 0555, dp);
+		return 1;
+	}
+
+	if(i>=3*nslot)
+		return -1;
+	slotno = i/3;
+	pp = slot + slotno;
+	len = 0;
+	switch(i%3){
+	case 0:
+		qid.path = QID(slotno, Qmem);
+		sprint(up->genbuf, "pcm%dmem", slotno);
+		len = pp->memlen;
+		break;
+	case 1:
+		qid.path = QID(slotno, Qattr);
+		sprint(up->genbuf, "pcm%dattr", slotno);
+		len = pp->memlen;
+		break;
+	case 2:
+		qid.path = QID(slotno, Qctl);
+		sprint(up->genbuf, "pcm%dctl", slotno);
+		break;
+	}
+	qid.vers = 0;
+	qid.type = QTFILE;
+	devdir(c, qid, up->genbuf, len, eve, 0660, dp);
+	return 1;
+}
+
+static void
+pcmciadump(PCMslot *pp)
+{
+	USED(pp);
+}
+
+/*
+ *  set up for slot cards
+ */
+static void
+pcmciareset(void)
+{
+	static int already;
+	int slotno, v, rdypin;
+	PCMslot *pp;
+
+	if(already)
+		return;
+	already = 1;
+	DPRINT("pcmcia reset\n");
+
+	lastslot = slot;
+
+	nslot = 0;
+	for(slotno = 0; slotno < Maxslot; slotno++){
+		rdypin = pcmpin(slotno, PCMready);
+		if(rdypin < 0)
+			break;
+		nslot = slotno+1;
+		slotmap(slotno, PCMCIAIO(slotno), PCMCIAAttr(slotno), PCMCIAMem(slotno));
+		slottiming(slotno, 300, 300, 300, 0);	/* set timing to the default, 300 */
+		pp = lastslot++;
+		GPIOeject[slotno] = (1<<pcmpin(slotno, PCMeject));
+		GPIOrdy[slotno] = (1<<rdypin);
+		GPIOall[slotno] = GPIOeject[slotno] | GPIOrdy[slotno];
+		GPIOREG->gafr &= ~GPIOall[slotno];
+		slotdis(pp);
+		intrenable(pcmpin(slotno, PCMeject), pcmciaintr, 0, BusGPIOrising, "pcmcia eject");
+		if((v = pcmpin(slotno, PCMstschng)) >= 0)	/* status change interrupt */
+			intrenable(v, pcmciaintr, 0, BusGPIOrising, "pcmcia status");
+	}
+}
+
+static Chan*
+pcmciaattach(char *spec)
+{
+	return devattach('y', spec);
+}
+
+static Walkqid*
+pcmciawalk(Chan *c, Chan *nc, char **name, int nname)
+{
+	return devwalk(c, nc, name, nname, 0, 0, pcmgen);
+}
+
+static int
+pcmciastat(Chan *c, uchar *db, int n)
+{
+	return devstat(c, db, n, 0, 0, pcmgen);
+}
+
+static Chan*
+pcmciaopen(Chan *c, int omode)
+{
+	if(c->qid.type & QTDIR){
+		if(omode != OREAD)
+			error(Eperm);
+	} else
+		increfp(slot + SLOTNO(c));
+	c->mode = openmode(omode);
+	c->flag |= COPEN;
+	c->offset = 0;
+	return c;
+}
+
+static void
+pcmciaclose(Chan *c)
+{
+	if(c->flag & COPEN)
+		if((c->qid.type & QTDIR) == 0)
+			decrefp(slot+SLOTNO(c));
+}
+
+/* a memmove using only bytes */
+static void
+memmoveb(uchar *to, uchar *from, int n)
+{
+	while(n-- > 0)
+		*to++ = *from++;
+}
+
+static long
+pcmread(int slotno, int attr, void *a, long n, ulong offset)
+{
+	PCMslot *pp;
+	long i;
+	uchar *b, *p;
+
+	pp = slot + slotno;
+	rlock(pp);
+	if(waserror()){
+		runlock(pp);
+		nexterror();
+	}
+	if(!pp->occupied)
+		error(Eio);
+	if(pp->memlen < offset){
+		runlock(pp);
+		poperror();
+		return 0;
+	}
+	if(pp->memlen < offset + n)
+		n = pp->memlen - offset;
+	if (attr){
+		b = a;
+		p = (uchar*)PCMCIAAttr(slotno) + offset;
+		for(i=0; i<n; i++){
+			if(!pp->occupied)
+				error(Eio);
+			b[0] = *p;
+			i++;
+			if(i<n)
+				b[1] = 0;
+			b += 2;
+			p += 2;
+		}
+	}else
+		memmoveb(a, (uchar *)PCMCIAMem(slotno) + offset, n);
+	poperror();
+	runlock(pp);
+	return n;
+}
+
+static long
+pcmciaread(Chan *c, void *a, long n, vlong offset)
+{
+	char *cp, *buf;
+	ulong p;
+	PCMslot *pp;
+	int i;
+
+	p = TYPE(c);
+	switch(p){
+	case Qdir:
+		return devdirread(c, a, n, 0, 0, pcmgen);
+	case Qmem:
+	case Qattr:
+		return pcmread(SLOTNO(c), p==Qattr, a, n, offset);
+	case Qctl:
+		buf = malloc(2048);
+		if(buf == nil)
+			error(Eio);
+		if(waserror()){
+			free(buf);
+			nexterror();
+		}
+		cp = buf;
+		pp = slot + SLOTNO(c);
+		if(pp->occupied)
+			cp += sprint(cp, "occupied\n");
+		if(pp->enabled)
+			cp += sprint(cp, "enabled\n");
+		if(pp->powered)
+			cp += sprint(cp, "powered\n");
+		if(pp->configed)
+			cp += sprint(cp, "configed\n");
+		if(pp->busy)
+			cp += sprint(cp, "busy\n");
+		if(pp->enabled && (i = strlen(pp->verstr)) > 0)
+			cp += sprint(cp, "verstr %d\n%s\n", i, pp->verstr);
+		cp += sprint(cp, "battery lvl %d\n", pp->battery);
+		/* DUMP registers here */
+		cp += sprint(cp, "mecr 0x%lux\n",
+			(SLOTNO(c) ? MEMCFGREG->mecr >> 16 : MEMCFGREG->mecr) & 0x7fff);
+		*cp = 0;
+		n = readstr(offset, a, n, buf);
+		poperror();
+		free(buf);
+		break;
+	default:
+		n=0;
+		break;
+	}
+	return n;
+}
+
+static long
+pcmwrite(int slotno, int attr, void *a, long n, ulong offset)
+{
+	PCMslot *pp;
+
+	pp = slot + slotno;
+	rlock(pp);
+	if(waserror()){
+		runlock(pp);
+		nexterror();
+	}
+	if(pp->memlen < offset)
+		error(Eio);
+	if(pp->memlen < offset + n)
+		error(Eio);
+	memmoveb((uchar *)(attr ? PCMCIAAttr(slotno) : PCMCIAMem(slotno)) + offset, a, n);
+	poperror();
+	runlock(pp);
+	return n;
+}
+
+/*
+ *  the regions are staticly mapped
+ */
+static void
+slotmap(int slotno, ulong regs, ulong attr, ulong mem)
+{
+	PCMslot *sp;
+
+	if(slotno >= Maxslot)
+		return;
+
+	sp = &slot[slotno];
+	sp->slotno = slotno;
+	sp->memlen = 64*MB;
+	sp->verstr[0] = 0;
+
+	sp->mem = (void*)mem;
+	sp->memmap.ca = 0;
+	sp->memmap.cea = 64*MB;
+	sp->memmap.isa = (ulong)mem;
+	sp->memmap.len = 64*MB;
+	sp->memmap.attr = 0;
+
+	sp->attr = (void*)attr;
+	sp->attrmap.ca = 0;
+	sp->attrmap.cea = MB;
+	sp->attrmap.isa = (ulong)attr;
+	sp->attrmap.len = MB;
+	sp->attrmap.attr = 1;
+
+	sp->regs = (void*)regs;
+}
+
+PCMmap*
+pcmmap(int slotno, ulong, int, int attr)
+{
+	if(slotno >= nslot)
+		panic("pcmmap");
+	if(attr)
+		return &slot[slotno].attrmap;
+	else
+		return &slot[slotno].memmap;
+}
+void
+pcmunmap(int, PCMmap*)
+{
+}
+
+/*
+ *  setup card timings
+ *    times are in ns
+ *    count = ceiling[access-time/(2*3*T)] - 1, where T is a processor cycle
+ *
+ */
+static int
+ns2count(int ns)
+{
+	ulong y;
+
+	/* get 100 times cycle time */
+	y = 100000000/(m->cpuhz/1000);
+
+	/* get 10 times ns/(cycle*6) */
+	y = (1000*ns)/(6*y);
+
+	/* round up */
+	y += 9;
+	y /= 10;
+
+	/* subtract 1 */	
+	y = y-1;
+	if(y < 0)
+		y  = 0;
+	if(y > 0x1F)
+		y = 0x1F;
+
+	return y & 0x1F;
+}
+static void
+slottiming(int slotno, int tio, int tattr, int tmem, int fast)
+{
+	ulong x;
+	MemcfgReg *memconfregs = MEMCFGREG;
+
+	x = ns2count(tio) << 0;
+	x |= ns2count(tattr) << 5;
+	x |= ns2count(tmem) << 10;
+	if(fast)
+		x |= 1<<15;
+	if(slotno == 0){
+		x |= memconfregs->mecr & 0xffff0000;
+	} else {
+		x <<= 16;
+		x |= memconfregs->mecr & 0xffff;
+	}
+	memconfregs->mecr = x;
+}
+
+static long
+pcmciawrite(Chan *c, void *a, long n, vlong offset)
+{
+	ulong p;
+	PCMslot *pp;
+	char buf[32];
+
+	p = TYPE(c);
+	switch(p){
+	case Qctl:
+		if(n >= sizeof(buf))
+			n = sizeof(buf) - 1;
+		strncpy(buf, a, n);
+		buf[n] = 0;
+		pp = slot + SLOTNO(c);
+		if(!pp->occupied)
+			error(Eio);
+
+		if(strncmp(buf, "vpp", 3) == 0)
+			pcmsetvpp(pp->slotno, atoi(buf+3));
+		break;
+	case Qmem:
+	case Qattr:
+		pp = slot + SLOTNO(c);
+		if(pp->occupied == 0 || pp->enabled == 0)
+			error(Eio);
+		n = pcmwrite(SLOTNO(c), p == Qattr, a, n, offset);
+		if(n < 0)
+			error(Eio);
+		break;
+	default:
+		error(Ebadusefd);
+	}
+	return n;
+}
+
+Dev pcmciadevtab = {
+	'y',
+	"pcmcia",
+
+	pcmciareset,
+	devinit,
+	devshutdown,
+	pcmciaattach,
+	pcmciawalk,
+	pcmciastat,
+	pcmciaopen,
+	devcreate,
+	pcmciaclose,
+	pcmciaread,
+	devbread,
+	pcmciawrite,
+	devbwrite,
+	devremove,
+	devwstat,
+};
+
+/*
+ *  configure the PCMslot for IO.  We assume very heavily that we can read
+ *  configuration info from the CIS.  If not, we won't set up correctly.
+ */
+
+static int
+pce(char *s)
+{
+	USED(s);
+	DPRINT("pcmio failed: %s\n", s);
+	return -1;
+}
+
+static int
+pcmio(int slotno, ISAConf *isa)
+{
+	uchar *p;
+	PCMslot *pp;
+	int i, index;
+	char *cp;
+
+	if(slotno >= nslot)
+		return PCMERR("bad slot#");
+	pp = slot + slotno;
+
+	if(!pp->occupied)
+		return PCMERR("empty slot");
+
+	index = 0;
+	if(pp->def)
+		index = pp->def->index;
+	for(i = 0; i < isa->nopt; i++){
+		if(strncmp(isa->opt[i], "index=", 6))
+			continue;
+		index = strtol(&isa->opt[i][6], &cp, 0);
+		if(cp == &isa->opt[i][6] || index < 0 || index >= pp->nctab)
+			return PCMERR("bad index");
+		break;
+	}
+	/* only touch Rconfig if it is present */
+	if(pp->cfg[0].cpresent & (1<<Rconfig)){
+		p = (uchar*)(PCMCIAAttr(slotno) + pp->cfg[0].caddr + Rconfig);
+		*p = index;
+		delay(5);
+	}
+	isa->port = (ulong)pp->regs;
+	isa->mem = (ulong)pp->mem;
+	isa->irq = pcmpin(pp->slotno, PCMready);
+	isa->itype = BusGPIOfalling;
+	return 0;
+}
+
+int
+inb(ulong p)
+{
+	return *(uchar*)p;
+}
+
+int
+ins(ulong p)
+{
+	return *(ushort*)p;
+}
+
+ulong
+inl(ulong p)
+{
+	return *(ulong*)p;
+}
+
+void
+outb(ulong p, int v)
+{
+	*(uchar*)p = v;
+}
+
+void
+outs(ulong p, int v)
+{
+	*(ushort*)p = v;
+}
+
+void
+outl(ulong p, ulong v)
+{
+	*(ulong*)p = v;
+}
+
+void
+inss(ulong p, void* buf, int ns)
+{
+	ushort *addr;
+
+	addr = (ushort*)buf;
+	for(;ns > 0; ns--)
+		*addr++ = *(ushort*)p;
+}
+
+void
+outss(ulong p, void* buf, int ns)
+{
+	ushort *addr;
+
+	addr = (ushort*)buf;
+	for(;ns > 0; ns--)
+		*(ushort*)p = *addr++;
+}
+
+void
+insb(ulong p, void* buf, int ns)
+{
+	uchar *addr;
+
+	addr = (uchar*)buf;
+	for(;ns > 0; ns--)
+		*addr++ = *(uchar*)p;
+}
+
+void
+outsb(ulong p, void* buf, int ns)
+{
+	uchar *addr;
+
+	addr = (uchar*)buf;
+	for(;ns > 0; ns--)
+		*(uchar*)p = *addr++;
+}
--- /dev/null
+++ b/os/sa1110/devpower.c
@@ -1,0 +1,377 @@
+/*
+ * power management
+ */
+
+#include	"u.h"
+#include	"../port/lib.h"
+#include	"mem.h"
+#include	"dat.h"
+#include	"fns.h"
+#include	"../port/error.h"
+
+typedef struct Power Power;
+typedef struct Puser Puser;
+
+enum{
+	Qdir,
+	Qctl,
+	Qdata
+};
+
+static
+Dirtab powertab[]={
+	".",			{Qdir, 0, QTDIR},	0,	0500,
+	"powerctl",		{Qctl, 0},		0,	0600,
+	"powerdata",		{Qdata, 0},	0,	0666,
+};
+
+struct Puser {
+	Ref;
+	ulong	alarm;	/* real time clock alarm time, if non-zero */
+	QLock	rl;		/* mutual exclusion to protect r */
+	Rendez	r;		/* wait for event of interest */
+	int	state;	/* shutdown state of this process */
+	Puser*	next;
+};
+
+enum{
+	Pwridle,
+	Pwroff,
+	Pwrack
+};
+
+static struct {
+	QLock;
+	Puser	*list;
+	Lock	l;	/* protect shutdown, nwaiting */
+	int	shutdown;	/* non-zero if currently shutting down */
+	int	nwaiting;		/* waiting for this many processes */
+	Rendez	ackr;	/* wait here for all acks */
+} pwrusers;
+
+
+static Chan*
+powerattach(char* spec)
+{
+	return devattach(L'↓', spec);
+}
+
+static int
+powerwalk(Chan* c, char* name)
+{
+	return devwalk(c, name, powertab, nelem(powertab), devgen);
+}
+
+static void
+powerstat(Chan* c, char* db)
+{
+	devstat(c, db, powertab, nelem(powertab), devgen);
+}
+
+static Chan*
+poweropen(Chan* c, int omode)
+{
+	Puser *p;
+
+	if(c->qid.type & QTDIR)
+		return devopen(c, omode, powertab, nelem(powertab), devgen);
+	switch(c->qid.path){
+	case Qdata:
+		p = mallocz(sizeof(Puser), 1);
+		if(p == nil)
+			error(Enovmem);
+		p->state = Pwridle;
+		p->ref = 1;
+		if(waserror()){
+			free(p);
+			nexterror();
+		}
+		c = devopen(c, omode, powertab, nelem(powertab), devgen);
+		c->aux = p;
+		qlock(&pwrusers);
+		p->next = pwrusers.list;
+		pwrusers.list = p;	/* note: must place on front of list for correct shutdown ordering */
+		qunlock(&pwrusers);
+		poperror();
+		break;
+	case Qctl:
+		c = devopen(c, omode, powertab, nelem(powertab), devgen);
+		break;
+	}
+	return c;
+}
+
+static Chan *
+powerclone(Chan *c, Chan *nc)
+{
+	Puser *p;
+
+	nc = devclone(c, nc);
+	if((p = nc->aux) != nil)
+		incref(p);
+	return nc;
+}
+
+static void
+powerclose(Chan* c)
+{
+	Puser *p, **l;
+
+	if(c->qid.type & QTDIR || (c->flag & COPEN) == 0)
+		return;
+	p = c->aux;
+	if(p != nil && decref(p) == 0){
+		/* TO DO: cancel alarm */
+		qlock(&pwrusers);
+		for(l = &pwrusers.list; *l != nil; l = &(*l)->next)
+			if(*l == p){
+				*l = p->next;
+				break;
+			}
+		qunlock(&pwrusers);
+		free(p);
+	}
+}
+
+static int
+isshutdown(void *a)
+{
+	return ((Puser*)a)->state == Pwroff;
+}
+
+static long
+powerread(Chan* c, void* a, long n, vlong offset)
+{
+	Puser *p;
+	char *msg;
+
+	switch(c->qid.path & ~CHDIR){
+	case Qdir:
+		return devdirread(c, a, n, powertab, nelem(powertab), devgen);
+	case Qdata:
+		p = c->aux;
+		for(;;){
+			if(!canqlock(&p->rl))
+				error(Einuse);	/* only one reader at a time */
+			if(waserror()){
+				qunlock(&p->rl);
+				nexterror();
+			}
+			sleep(&p->r, isshutdown, p);
+			poperror();
+			qunlock(&p->rl);
+			msg = nil;
+			lock(p);
+			if(p->state == Pwroff){
+				msg = "power off";
+				p->state = Pwrack;
+			}
+			unlock(p);
+			if(msg != nil)
+				return readstr(offset, a, n, msg);
+		}
+		break;
+	case Qctl:
+	default:
+		n=0;
+		break;
+	}
+	return n;
+}
+
+static int
+alldown(void*)
+{
+	return pwrusers.nwaiting == 0;
+}
+
+static long
+powerwrite(Chan* c, void *a, long n, vlong)
+{
+	Cmdbuf *cmd;
+	Puser *p;
+
+	if(c->qid.type & QTDIR)
+		error(Ebadusefd);
+	cmd = parsecmd(a, n);
+	if(waserror()){
+		free(cmd);
+		nexterror();
+	}
+	switch(c->qid.path & ~CHDIR){
+	case Qdata:
+		p = c->aux;
+		if(cmd->nf < 2)
+			error(Ebadarg);
+		if(strcmp(cmd->f[0], "ack") == 0){
+			if(strcmp(cmd->f[1], "power") == 0){
+				lock(p);
+				if(p->state == Pwrack){
+					lock(&pwrusers.l);
+					if(pwrusers.shutdown && pwrusers.nwaiting > 0)
+						pwrusers.nwaiting--;
+					unlock(&pwrusers.l);
+					wakeup(&pwrusers.ackr);
+					p->state = Pwridle;
+				}
+				unlock(p);
+			}else
+				error(Ebadarg);
+		}else if(strcmp(cmd->f[0], "alarm") == 0){
+			/* set alarm */
+		}else
+			error(Ebadarg);
+		break;
+	case Qctl:
+		if(cmd->nf < 1)
+			error(Ebadarg);
+		if(strcmp(cmd->f[0], "suspend") == 0){
+			/* start the suspend action */
+			qlock(&pwrusers);
+			//powersuspend(0);	/* calls poweringdown, then archsuspend() */
+			qunlock(&pwrusers);
+		}else if(strcmp(cmd->f[0], "shutdown") == 0){
+			/* go to it */
+			qlock(&pwrusers);
+			if(waserror()){
+				lock(&pwrusers.l);
+				pwrusers.shutdown = 0;	/* hard luck for those already notified */
+				unlock(&pwrusers.l);
+				qunlock(&pwrusers);
+				nexterror();
+			}
+			lock(&pwrusers.l);
+			pwrusers.shutdown = 1;
+			pwrusers.nwaiting = 0;
+			unlock(&pwrusers.l);
+			for(p = pwrusers.list; p != nil; p = p->next){
+				lock(p);
+				if(p->state == Pwridle){
+					p->state = Pwroff;
+					lock(&pwrusers.l);
+					pwrusers.nwaiting++;
+					unlock(&pwrusers.l);
+				}
+				unlock(p);
+				wakeup(&p->r);
+				/* putting the tsleep here does each in turn; move out of loop to multicast */
+				tsleep(&pwrusers.ackr, alldown, nil, 1000);
+			}
+			poperror();
+			qunlock(&pwrusers);
+			//powersuspend(1);
+		}else
+			error(Ebadarg);
+		free(cmd);
+		break;
+	default:
+		error(Ebadusefd);
+	}
+	poperror();
+	return n;
+}
+
+/*
+ * device-level power management: suspend/resume/shutdown
+ */
+
+struct Power {
+	void	(*f)(int);
+	Power*	prev;
+	Power*	next;
+};
+
+static struct {
+	Lock;
+	Power	list;
+} power;
+
+void
+powerenablereset(void)
+{
+	power.list.next = power.list.prev = &power.list;
+	power.list.f = (void*)-1;	/* something not nil */
+}
+
+void
+powerenable(void (*f)(int))
+{
+	Power *p, *l;
+
+	p = malloc(sizeof(*p));
+	p->f = f;
+	p->prev = nil;
+	p->next = nil;
+	ilock(&power);
+	for(l = power.list.next; l != &power.list; l = l->next)
+		if(l->f == f){
+			iunlock(&power);
+			free(p);
+			return;
+		}
+	l = &power.list;
+	p->prev = l->prev;
+	l->prev = p;
+	p->next = l;
+	p->prev->next = p;
+	iunlock(&power);
+}
+
+void
+powerdisable(void (*f)(int))
+{
+	Power *l;
+
+	ilock(&power);
+	for(l = power.list.next; l != &power.list; l = l->next)
+		if(l->f == f){
+			l->prev->next = l->next;
+			l->next->prev = l->prev;
+			free(l);
+			break;
+		}
+	iunlock(&power);
+}
+
+/*
+ * interrupts are assumed off so there's no need to lock
+ */
+void
+poweringup(void)
+{
+	Power *l;
+
+	for(l = power.list.next; l != &power.list; l = l->next)
+		(*l->f)(1);
+}
+
+void
+poweringdown(void)
+{
+	Power *l;
+
+	for(l = power.list.prev; l != &power.list; l = l->prev)
+		(*l->f)(0);
+}
+
+Dev powerdevtab = {
+	L'↓',
+	"power",
+
+	devreset,
+	devinit,
+	powerattach,
+	devdetach,
+	powerclone,
+	powerwalk,
+	powerstat,
+	poweropen,
+	devcreate,
+	powerclose,
+	powerread,
+	devbread,
+	powerwrite,
+	devbwrite,
+	devremove,
+	devwstat,
+};
--- /dev/null
+++ b/os/sa1110/devrtc.c
@@ -1,0 +1,169 @@
+#include	"u.h"
+#include	"../port/lib.h"
+#include	"mem.h"
+#include	"dat.h"
+#include	"fns.h"
+#include	"../port/error.h"
+
+#include	"io.h"
+
+/*
+ * SA11x0 real time clock
+ *	TO DO: alarms, wakeup, allow trim setting(?)
+ */
+
+enum{
+	Qdir,
+	Qrtc,
+	Qrtctrim,
+};
+
+static Dirtab rtcdir[]={
+	".",		{Qdir,0,QTDIR},	0,	0555,
+	"rtc",		{Qrtc},	NUMSIZE,	0664,
+	"rtctrim",	{Qrtctrim},	0,	0664,
+};
+#define	NRTC	(sizeof(rtcdir)/sizeof(rtcdir[0]))
+
+extern ulong boottime;
+
+enum {
+	RTSR_al=	1<<0,	/* RTC alarm detected */
+	RTSR_hz=	1<<1,	/* 1-Hz rising-edge detected */
+	RTSR_ale=	1<<2,	/* RTC alarm interrupt enabled */
+	RTSR_hze=	1<<3,	/* 1-Hz interrupt enable */
+};
+
+static void
+rtcreset(void)
+{
+	RtcReg *r;
+
+	r = RTCREG;
+	if((r->rttr & 0xFFFF) == 0){	/* reset state */
+		r->rttr = 32768-1;
+		r->rcnr = boottime;	/* typically zero */
+	}
+	r->rtar = ~0;
+	r->rtsr = RTSR_al | RTSR_hz;
+}
+
+static Chan*
+rtcattach(char *spec)
+{
+	return devattach('r', spec);
+}
+
+static Walkqid*
+rtcwalk(Chan *c, Chan *nc, char **name, int nname)
+{
+	return devwalk(c, nc, name, nname, rtcdir, NRTC, devgen);
+}
+
+static int
+rtcstat(Chan *c, uchar *dp, int n)
+{
+	return devstat(c, dp, n, rtcdir, NRTC, devgen);
+}
+
+static Chan*
+rtcopen(Chan *c, int omode)
+{
+	return devopen(c, omode, rtcdir, NRTC, devgen);
+}
+
+static void	 
+rtcclose(Chan*)
+{
+}
+
+static long	 
+rtcread(Chan *c, void *buf, long n, vlong off)
+{
+	if(c->qid.type & QTDIR)
+		return devdirread(c, buf, n, rtcdir, NRTC, devgen);
+
+	switch((ulong)c->qid.path){
+	case Qrtc:
+		return readnum(off, buf, n, RTCREG->rcnr, NUMSIZE);
+	case Qrtctrim:
+		return readnum(off, buf, n, RTCREG->rttr, NUMSIZE);
+	}
+	error(Egreg);
+	return 0;		/* not reached */
+}
+
+static long	 
+rtcwrite(Chan *c, void *buf, long n, vlong off)
+{
+	ulong offset = off;
+	ulong secs;
+	char *cp, sbuf[32];
+
+	switch((ulong)c->qid.path){
+	case Qrtc:
+		/*
+		 *  write the time
+		 */
+		if(offset != 0 || n >= sizeof(sbuf)-1)
+			error(Ebadarg);
+		memmove(sbuf, buf, n);
+		sbuf[n] = '\0';
+		cp = sbuf;
+		while(*cp){
+			if(*cp>='0' && *cp<='9')
+				break;
+			cp++;
+		}
+		secs = strtoul(cp, 0, 0);
+		RTCREG->rcnr = secs;
+		return n;
+
+	case Qrtctrim:
+		if(offset != 0 || n >= sizeof(sbuf)-1)
+			error(Ebadarg);
+		memmove(sbuf, buf, n);
+		sbuf[n] = '\0';
+		RTCREG->rttr = strtoul(sbuf, 0, 0);
+		return n;
+	}
+	error(Egreg);
+	return 0;		/* not reached */
+}
+
+static void
+rtcpower(int on)
+{
+	if(on)
+		boottime = RTCREG->rcnr - TK2SEC(MACHP(0)->ticks);
+	else
+		RTCREG->rcnr = seconds();
+}
+
+long
+rtctime(void)
+{
+	return RTCREG->rcnr;
+}
+
+Dev rtcdevtab = {
+	'r',
+	"rtc",
+
+	rtcreset,
+	devinit,
+	devshutdown,
+	rtcattach,
+	rtcwalk,
+	rtcstat,
+	rtcopen,
+	devcreate,
+	rtcclose,
+	rtcread,
+	devbread,
+	rtcwrite,
+	devbwrite,
+	devremove,
+	devwstat,
+	rtcpower,
+};
--- /dev/null
+++ b/os/sa1110/devuart.c
@@ -1,0 +1,784 @@
+#include	"u.h"
+#include	"../port/lib.h"
+#include	"mem.h"
+#include	"dat.h"
+#include	"fns.h"
+#include	"io.h"
+#include	"../port/error.h"
+#include	"../port/netif.h"
+
+/*
+ * currently no DMA or flow control (hardware or software)
+ */
+
+enum
+{
+	Stagesize= 1024,
+	Dmabufsize=Stagesize/2,
+	Nuart=7,		/* max per machine */
+
+	CTLS= 023,
+	CTLQ= 021,
+};
+
+typedef struct Uart Uart;
+struct Uart
+{
+	QLock;
+
+	int	opens;
+
+	int	enabled;
+
+	int	frame;		/* framing errors */
+	int	overrun;	/* rcvr overruns */
+	int	soverrun;	/* software overruns */
+	int	perror;		/* parity error */
+	int	bps;		/* baud rate */
+	uchar	bits;
+	char	parity;
+
+	int	inters;		/* total interrupt count */
+	int	rinters;	/* interrupts due to read */
+	int	winters;	/* interrupts due to write */
+
+	int	rcount;		/* total read count */
+	int	wcount;		/* total output count */
+
+	int	xonoff;		/* software flow control on */
+	int	blocked;		/* output blocked */
+
+	/* buffers */
+	int	(*putc)(Queue*, int);
+	Queue	*iq;
+	Queue	*oq;
+
+	int	port;
+	UartReg	*reg;
+
+	/* staging areas to avoid some of the per character costs */
+	uchar	*ip;
+	uchar	*ie;
+	uchar	*op;
+	uchar	*oe;
+
+	/* put large buffers last to aid register-offset optimizations: */
+	char	name[KNAMELEN];
+	uchar	istage[Stagesize];
+	uchar	ostage[Stagesize];
+};
+
+enum {
+	UTCR0_PE=	0x01,
+	UTCR0_OES=	0x02,
+	UTCR0_SBS=	0x04,
+	UTCR0_DSS=	0x08,
+	UTCR0_SCE=	0x10,
+	UTCR0_RCE=	0x20,
+	UTCR0_TCE=	0x40,
+
+	UTCR3_RXE=	0x01,
+	UTCR3_TXE=	0x02,
+	UTCR3_BRK=	0x04,
+	UTCR3_RIM=	0x08,
+	UTCR3_TIM=	0x10,
+	UTCR3_LBM=	0x20,
+
+	UTSR0_TFS=	0x01,
+	UTSR0_RFS=	0x02,
+	UTSR0_RID=	0x04,
+	UTSR0_RBB=	0x08,
+	UTSR0_REB=	0x10,
+	UTSR0_EIF=	0x20,
+
+	UTSR1_TBY=	0x01,
+	UTSR1_RNE=	0x02,
+	UTSR1_TNF=	0x04,
+	UTSR1_PRE=	0x08,
+	UTSR1_FRE=	0x10,
+	UTSR1_ROR=	0x20,
+};
+
+static Uart *uart[Nuart];
+static int nuart;
+static int uartspcl;
+int redirectconsole;
+
+static void
+uartset(Uart *p)
+{
+	UartReg *reg = p->reg;
+	ulong ocr3;
+	ulong brdiv;
+	int n;
+
+	brdiv = CLOCKFREQ/16/p->bps - 1;
+	ocr3 = reg->utcr3;
+	reg->utcr3 = ocr3&~(UTCR3_RXE|UTCR3_TXE);
+	reg->utcr1 = brdiv >> 8;
+	reg->utcr2 = brdiv & 0xff;
+	/* set PE and OES appropriately for o/e/n: */
+	reg->utcr0 = ((p->parity&3)^UTCR0_OES)|(p->bits&UTCR0_DSS);
+	reg->utcr3 = ocr3;
+
+	/* set buffer length according to speed, to allow
+	 * at most a 200ms delay before dumping the staging buffer
+	 * into the input queue
+	 */
+	n = p->bps/(10*1000/200);
+	p->ie = &p->istage[n < Stagesize ? n : Stagesize];
+}
+
+/*
+ *  send break
+ */
+static void
+uartbreak(Uart *p, int ms)
+{
+	UartReg *reg = p->reg;
+	if(ms == 0)
+		ms = 200;
+	reg->utcr3 |= UTCR3_BRK;
+	tsleep(&up->sleep, return0, 0, ms);
+	reg->utcr3 &= ~UTCR3_BRK;
+}
+
+/*
+ *  turn on a port
+ */
+static void
+uartenable(Uart *p)
+{
+	UartReg *reg = p->reg;
+
+	if(p->enabled)
+		return;
+
+	archuartpower(p->port, 1);
+	uartset(p);
+	reg->utsr0 = 0xff;		// clear all sticky status bits
+	// enable receive, transmit, and receive interrupt:
+	reg->utcr3 = UTCR3_RXE|UTCR3_TXE|UTCR3_RIM;
+	p->blocked = 0;
+	p->xonoff = 0;
+	p->enabled = 1;
+}
+
+/*
+ *  turn off a port
+ */
+static void
+uartdisable(Uart *p)
+{
+	p->reg->utcr3 = 0;		// disable TX, RX, and ints
+	p->blocked = 0;
+	p->xonoff = 0;
+	p->enabled = 0;
+	archuartpower(p->port, 0);
+}
+
+/*
+ *  put some bytes into the local queue to avoid calling
+ *  qconsume for every character
+ */
+static int
+stageoutput(Uart *p)
+{
+	int n;
+	Queue *q = p->oq;
+
+	if(q == nil)
+		return 0;
+	n = qconsume(q, p->ostage, Stagesize);
+	if(n <= 0)
+		return 0;
+	p->op = p->ostage;
+	p->oe = p->ostage + n;
+	return n;
+}
+
+static void
+uartxmit(Uart *p)
+{
+	UartReg *reg = p->reg;
+	ulong e = 0;
+
+	if(!p->blocked) {
+		while(p->op < p->oe || stageoutput(p)) {	
+			if(reg->utsr1 & UTSR1_TNF) {
+				reg->utdr = *(p->op++);
+				p->wcount++;
+			} else {
+				e = UTCR3_TIM;
+				break;
+			}
+		}
+	}
+	reg->utcr3 = (reg->utcr3&~UTCR3_TIM)|e;
+}
+
+static void
+uartrecvq(Uart *p)
+{
+	uchar *cp = p->istage;
+	int n = p->ip - cp;
+
+	if(n == 0)
+		return;
+	if(p->putc)
+		while(n-- > 0) 
+			p->putc(p->iq, *cp++);
+	else if(p->iq) 
+		if(qproduce(p->iq, p->istage, n) < n){
+			/* if xonoff, should send XOFF when qwindow(p->iq) < threshold */
+			p->soverrun++;
+			//print("qproduce flow control");
+		}
+	p->ip = p->istage;
+}
+
+static void
+uartrecv(Uart *p)
+{
+	UartReg *reg = p->reg;
+	ulong n;
+	while(reg->utsr1 & UTSR1_RNE) {
+		int c;
+		n = reg->utsr1;
+		c = reg->utdr;
+		if(n & (UTSR1_PRE|UTSR1_FRE|UTSR1_ROR)) {
+			if(n & UTSR1_PRE) 
+				p->perror++;
+			if(n & UTSR1_FRE) 
+				p->frame++;
+			if(n & UTSR1_ROR) 
+				p->overrun++;
+			continue;
+		}
+		if(p->xonoff){
+			if(c == CTLS){
+				p->blocked = 1;
+			}else if (c == CTLQ){
+				p->blocked = 0;
+			}
+		}
+		*p->ip++ = c;
+		if(p->ip >= p->ie)
+			uartrecvq(p);
+		p->rcount++;
+	}
+	if(reg->utsr0 & UTSR0_RID) {
+		reg->utsr0 = UTSR0_RID;
+		uartrecvq(p);
+	}
+}
+
+static void
+uartclock(void)
+{
+	Uart *p;
+	int i;
+
+	for(i=0; i<nuart; i++){
+		p = uart[i];
+		if(p != nil)
+			uartrecvq(p);
+	}
+}
+
+static void
+uartkick(void *a)
+{
+	Uart *p = a;
+	int x;
+
+	x = splhi();
+	uartxmit(p);
+	splx(x);
+}
+
+/*
+ *  UART Interrupt Handler
+ */
+static void
+uartintr(Ureg*, void* arg)
+{
+	Uart *p = arg;			
+	UartReg *reg = p->reg;
+	ulong m = reg->utsr0;
+	int dokick;
+
+	dokick = p->blocked;
+	p->inters++;
+	if(m & (UTSR0_RFS|UTSR0_RID|UTSR0_EIF)) {
+		p->rinters++;
+		uartrecv(p);
+	}
+	if(p->blocked)
+		dokick = 0;
+	if((m & UTSR0_TFS) && (reg->utcr3&UTCR3_TIM || dokick)) {
+		p->winters++;
+		uartxmit(p);
+	}
+
+	if(m & (UTSR0_RBB|UTSR0_REB)) {
+		//print("<BREAK>");
+		/* reg->utsr0 = UTSR0_RBB|UTSR0_REB; */
+		reg->utsr0 = m & (UTSR0_RBB|UTSR0_REB);
+		/* what to do? if anything */
+	}
+}
+
+static void
+uartsetup(ulong port, char *name)
+{
+	Uart *p;
+
+	if(nuart >= Nuart)
+		return;
+
+	p = xalloc(sizeof(Uart));
+	uart[nuart++] = p;
+	strcpy(p->name, name);
+
+	p->port = port;
+	p->reg = UARTREG(port);
+	p->bps = 9600;
+	p->bits = 8;
+	p->parity = 'n';
+
+	p->iq = qopen(4*1024, 0, 0 , p);
+	p->oq = qopen(4*1024, 0, uartkick, p);
+
+	p->ip = p->istage;
+	p->ie = &p->istage[Stagesize];
+	p->op = p->ostage;
+	p->oe = p->ostage;
+	if(port == 1)
+		GPCLKREG->gpclkr0 |= 1;	/* SUS=1 for uart on serial 1 */
+
+	intrenable(UARTbit(port), uartintr, p, BusCPU, name);
+}
+
+static void
+uartinstall(void)
+{
+	static int already;
+
+	if(already)
+		return;
+	already = 1;
+
+	uartsetup(3, "eia0");
+	uartsetup(1, "eia1");
+	addclock0link(uartclock, 22);
+}
+
+/*
+ *  called by main() to configure a duart port as a console or a mouse
+ */
+void
+uartspecial(int port, int bps, char parity, Queue **in, Queue **out, int (*putc)(Queue*, int))
+{
+	Uart *p;
+
+	uartinstall();
+	if(port >= nuart) 
+		return;
+	p = uart[port];
+	if(bps) 
+		p->bps = bps;
+	if(parity)
+		p->parity = parity;
+	uartenable(p);
+	p->putc = putc;
+	if(in)
+		*in = p->iq;
+	if(out)
+		*out = p->oq;
+	p->opens++;
+	uartspcl = 1;
+}
+
+Dirtab *uartdir;
+int ndir;
+
+static void
+setlength(int i)
+{
+	Uart *p;
+
+	if(i > 0){
+		p = uart[i];
+		if(p && p->opens && p->iq)
+			uartdir[1+3*i].length = qlen(p->iq);
+	} else for(i = 0; i < nuart; i++){
+		p = uart[i];
+		if(p && p->opens && p->iq)
+			uartdir[1+3*i].length = qlen(p->iq);
+	}
+}
+
+/*
+ *  all uarts must be uartsetup() by this point or inside of uartinstall()
+ */
+static void
+uartreset(void)
+{
+	int i;
+	Dirtab *dp;
+
+	uartinstall();
+
+	ndir = 1+3*nuart;
+	uartdir = xalloc(ndir * sizeof(Dirtab));
+	dp = uartdir;
+	strcpy(dp->name, ".");
+	mkqid(&dp->qid, 0, 0, QTDIR);
+	dp->length = 0;
+	dp->perm = DMDIR|0555;
+	dp++;
+	for(i = 0; i < nuart; i++){
+		/* 3 directory entries per port */
+		strcpy(dp->name, uart[i]->name);
+		dp->qid.path = NETQID(i, Ndataqid);
+		dp->perm = 0660;
+		dp++;
+		sprint(dp->name, "%sctl", uart[i]->name);
+		dp->qid.path = NETQID(i, Nctlqid);
+		dp->perm = 0660;
+		dp++;
+		sprint(dp->name, "%sstatus", uart[i]->name);
+		dp->qid.path = NETQID(i, Nstatqid);
+		dp->perm = 0444;
+		dp++;
+	}
+}
+
+static Chan*
+uartattach(char *spec)
+{
+	return devattach('t', spec);
+}
+
+static Walkqid*
+uartwalk(Chan *c, Chan *nc, char **name, int nname)
+{
+	return devwalk(c, nc, name, nname, uartdir, ndir, devgen);
+}
+
+static int
+uartstat(Chan *c, uchar *dp, int n)
+{
+	if(NETTYPE(c->qid.path) == Ndataqid)
+		setlength(NETID(c->qid.path));
+	return devstat(c, dp, n, uartdir, ndir, devgen);
+}
+
+static Chan*
+uartopen(Chan *c, int omode)
+{
+	Uart *p;
+
+	c = devopen(c, omode, uartdir, ndir, devgen);
+
+	switch(NETTYPE(c->qid.path)){
+	case Nctlqid:
+	case Ndataqid:
+		p = uart[NETID(c->qid.path)];
+		qlock(p);
+		if(p->opens++ == 0){
+			uartenable(p);
+			qreopen(p->iq);
+			qreopen(p->oq);
+		}
+		qunlock(p);
+		break;
+	}
+
+	return c;
+}
+
+static void
+uartclose(Chan *c)
+{
+	Uart *p;
+
+	if(c->qid.type & QTDIR)
+		return;
+	if((c->flag & COPEN) == 0)
+		return;
+	switch(NETTYPE(c->qid.path)){
+	case Ndataqid:
+	case Nctlqid:
+		p = uart[NETID(c->qid.path)];
+		qlock(p);
+		if(--(p->opens) == 0){
+			uartdisable(p);
+			qclose(p->iq);
+			qclose(p->oq);
+			p->ip = p->istage;
+		}
+		qunlock(p);
+		break;
+	}
+}
+
+static long
+uartstatus(Chan *c, Uart *p, void *buf, long n, long offset)
+{
+	char str[256];
+	USED(c);
+
+	str[0] = 0;
+	snprint(str, sizeof(str),
+			"b%d l%d p%c s%d x%d\n"
+			"opens %d ferr %d oerr %d perr %d baud %d parity %c"
+			" intr %d rintr %d wintr %d"
+			" rcount %d wcount %d",
+		p->bps, p->bits, p->parity, (p->reg->utcr0&UTCR0_SBS)?2:1, p->xonoff,
+		p->opens, p->frame, p->overrun+p->soverrun, p->perror, p->bps, p->parity,
+		p->inters, p->rinters, p->winters,
+		p->rcount, p->wcount);
+
+	strcat(str, "\n");
+	return readstr(offset, buf, n, str);
+}
+
+static long
+uartread(Chan *c, void *buf, long n, vlong offset)
+{
+	Uart *p;
+
+	if(c->qid.type & QTDIR){
+		setlength(-1);
+		return devdirread(c, buf, n, uartdir, ndir, devgen);
+	}
+
+	p = uart[NETID(c->qid.path)];
+	switch(NETTYPE(c->qid.path)){
+	case Ndataqid:
+		return qread(p->iq, buf, n);
+	case Nctlqid:
+		return readnum(offset, buf, n, NETID(c->qid.path), NUMSIZE);
+	case Nstatqid:
+		return uartstatus(c, p, buf, n, offset);
+	}
+
+	return 0;
+}
+
+static void
+uartctl(Uart *p, char *cmd)
+{
+	int i, n;
+
+	/* let output drain for a while (up to 4 secs) */
+	for(i = 0; i < 200 && (qlen(p->oq) || p->reg->utsr1 & UTSR1_TBY); i++)
+		tsleep(&up->sleep, return0, 0, 20);
+
+	if(strncmp(cmd, "break", 5) == 0){
+		uartbreak(p, 0);
+		return;
+	}
+
+	n = atoi(cmd+1);
+	switch(*cmd){
+	case 'B':
+	case 'b':
+		if(n <= 0) 
+			error(Ebadarg);
+		p->bps = n;
+		uartset(p);
+		break;
+	case 'f':
+	case 'F':
+		qflush(p->oq);
+		break;
+	case 'H':
+	case 'h':
+		qhangup(p->iq, 0);
+		qhangup(p->oq, 0);
+		break;
+	case 'L':
+	case 'l':
+		if(n < 7 || n > 8)
+			error(Ebadarg);
+		p->bits = n;
+		uartset(p);
+		break;
+	case 'n':
+	case 'N':
+		qnoblock(p->oq, n);
+		break;
+	case 'P':
+	case 'p':
+		p->parity = *(cmd+1);
+		uartset(p);
+		break;
+	case 'K':
+	case 'k':
+		uartbreak(p, n);
+		break;
+	case 'Q':
+	case 'q':
+		qsetlimit(p->iq, n);
+		qsetlimit(p->oq, n);
+		break;
+	case 'X':
+	case 'x':
+		p->xonoff = n;
+		break;
+	}
+}
+
+static long
+uartwrite(Chan *c, void *buf, long n, vlong offset)
+{
+	Uart *p;
+	char cmd[32];
+
+	USED(offset);
+
+	if(c->qid.type & QTDIR)
+		error(Eperm);
+
+	p = uart[NETID(c->qid.path)];
+
+	switch(NETTYPE(c->qid.path)){
+	case Ndataqid:
+		return qwrite(p->oq, buf, n);
+	case Nctlqid:
+
+		if(n >= sizeof(cmd))
+			n = sizeof(cmd)-1;
+		memmove(cmd, buf, n);
+		cmd[n] = 0;
+		uartctl(p, cmd);
+		return n;
+	default:
+		error(Egreg);
+		return 0;
+	}
+}
+
+static int
+uartwstat(Chan *c, uchar *dp, int n)
+{
+	Dir d;
+	Dirtab *dt;
+
+	if(!iseve())
+		error(Eperm);
+	if(c->qid.type & QTDIR)
+		error(Eperm);
+	if(NETTYPE(c->qid.path) == Nstatqid)
+		error(Eperm);
+
+	dt = &uartdir[1+3 * NETID(c->qid.path)];
+	n = convM2D(dp, n, &d, nil);
+	if(d.mode != ~0UL){
+		d.mode &= 0666;
+		dt[0].perm = dt[1].perm = d.mode;
+	}
+	return n;
+}
+
+void
+uartpower(int on)
+{
+	Uart *p;
+	int i;
+
+	for(i=0; i<nuart; i++){
+		p = uart[i];
+		if(p != nil && p->opens){
+			if(on && !p->enabled){
+				p->enabled = 0;
+				uartenable(p);
+				uartkick(p);
+			}else{
+				if(p->port != 3)	/* leave the console */
+					uartdisable(p);
+				p->enabled = 0;
+			}
+		}
+	}
+}
+
+Dev uartdevtab = {
+	't',
+	"uart",
+
+	uartreset,
+	devinit,
+	devshutdown,
+	uartattach,
+	uartwalk,
+	uartstat,
+	uartopen,
+	devcreate,
+	uartclose,
+	uartread,
+	devbread,
+	uartwrite,
+	devbwrite,
+	devremove,
+	uartwstat,
+	uartpower,
+};
+
+/*
+ * for use by iprint
+ */
+void
+uartputc(int c)
+{
+	UartReg *r;
+
+	if(!uartspcl && !redirectconsole)
+		return;
+	if(c == 0)
+		return;
+	r = UARTREG(3);
+	while((r->utsr1 & UTSR1_TNF) == 0)
+		{}
+	r->utdr = c;
+	if(c == '\n')
+		while(r->utsr1 & UTSR1_TBY)	/* flush xmit fifo */
+			{}
+}
+
+void
+uartputs(char *data, int len)
+{
+	int s;
+
+	if(!uartspcl && !redirectconsole)
+		return;
+	clockpoll();
+	s = splfhi();
+	while(--len >= 0){
+		if(*data == '\n')
+			uartputc('\r');
+		uartputc(*data++);
+	}
+	splx(s);
+}
+
+/*
+ * for use by debugger
+ */
+int
+uartgetc(void)
+{
+	UartReg *r;
+
+	if(!uartspcl)
+		return -1;
+	clockcheck();
+	r = UARTREG(3);
+	while(!(r->utsr1 & UTSR1_RNE))
+		clockcheck();
+	return r->utdr;
+}
--- /dev/null
+++ b/os/sa1110/dma.c
@@ -1,0 +1,233 @@
+#include	"u.h"
+#include	"../port/lib.h"
+#include	"mem.h"
+#include	"dat.h"
+#include	"fns.h"
+#include	"../port/error.h"
+#include	"io.h"
+
+enum {
+	 /* DMA CSR bits */
+	CSRrun=		1 << 0,
+	CSRie=		1 << 1,
+	CSRerror=	1 << 2,
+	CSRdonea=	1 << 3,
+	CSRstrta=	1 << 4,
+	CSRdoneb=	1 << 5,
+	CSRstrtb=	1 << 6,
+	CSRbiu=		1 << 7,
+
+	Ndma=	6,		/* number of dma channels */
+};
+
+/* DDAR configuration: DA 31:8, DS 3:0, data width, burst size */
+#define	DMACFG(da, ds, dw, bs) (((da)<<8)|((ds)<<4)|((dw)<<3)|((bs)<<2))
+
+static ulong dmaconfig[16] = {
+[DmaUDC] 	DMACFG(0x80000A, 0, 0, 1),
+[DmaUART0]	DMACFG(0x804005, 4, 0, 0),
+[DmaHSSP]	DMACFG(0x81001B, 6, 0, 1),
+[DmaUART1]	DMACFG(0x80C005, 6, 0, 0),
+[DmaUART2]	DMACFG(0x814005, 8, 0, 0),
+[DmaMCPaudio] DMACFG(0x818002, 10, 1, 1),
+[DmaMCPtelecom] DMACFG(0x818003, 12, 1, 1),
+[DmaSSP]		DMACFG(0x81C01B, 14, 1, 0),	/* see SSP description not DMA section for correct burst size */
+};
+
+struct Dma {
+	int	chan;
+	DmaReg*	reg;
+	void	(*interrupt)(void*, ulong);
+	void*	arg;
+	Rendez	r;
+	int	intrset;
+};
+
+static struct {
+	Lock;
+	int	avail;
+	Dma	dma[Ndma];
+} dmachans;
+
+static	void	dmaintr(Ureg*, void*);
+
+void
+dmareset(void)
+{
+	int i;
+	Dma *d;
+
+	for(i=0; i<nelem(dmachans.dma); i++){
+		dmachans.avail |= 1<<i;
+		d = &dmachans.dma[i];
+		d->chan = i;
+		d->reg = DMAREG(i);
+		d->reg->dcsr_c = 0xFF;
+	}
+	/* this is the place to mask off bits in avail corresponding to broken channels in old revisions */
+}
+
+/*
+ * allocate a DMA channel, reset it, and configure it for the given device
+ */
+Dma*
+dmasetup(int device, int direction, int bigend, void (*interrupt)(void*, ulong), void *arg)
+{
+	Dma *d;
+	DmaReg *dr;
+	ulong cfg;
+	int i;
+	char name[KNAMELEN];
+
+	cfg = dmaconfig[device];
+	if(cfg == 0){
+		print("dmasetup: no device %d\n", device);
+		return nil;
+	}
+
+	ilock(&dmachans);
+	for(i=0; (dmachans.avail & (1<<i)) == 0; i++)
+		if(i >= nelem(dmachans.dma)){
+			iunlock(&dmachans);
+			return nil;
+		}
+	dmachans.avail &= ~(1<<i);
+	iunlock(&dmachans);
+
+	d = &dmachans.dma[i];
+	d->interrupt = interrupt;
+	d->arg = arg;
+	dr = d->reg;
+	dr->dcsr_c = CSRrun | CSRie | CSRerror | CSRdonea | CSRstrta | CSRdoneb | CSRstrtb;
+	dr->ddar = cfg | (direction<<4) | (bigend<<1);
+	if(d->intrset == 0){
+		d->intrset = 1;
+		snprint(name, sizeof(name), "dma%d", i);
+		intrenable(DMAbit(i), dmaintr, d, BusCPU, name);
+	}
+	return d;
+}
+
+void
+dmafree(Dma *dma)
+{
+	dma->reg->dcsr_c = CSRrun | CSRie;
+	ilock(&dmachans);
+	dmachans.avail |= 1<<dma->chan;
+	dma->interrupt = nil;
+	iunlock(&dmachans);
+}
+
+/*
+ * start dma on the given channel on one or two buffers,
+ * each of which must adhere to DMA controller restrictions.
+ * (eg, on some versions of the StrongArm it musn't span 256-byte boundaries).
+ * virtual buffer addresses are assumed to refer to contiguous physical addresses.
+ */
+int
+dmastart(Dma *dma, void *buf, int nbytes)
+{
+	ulong v, csr;
+	DmaReg *dr;
+	int b;
+
+	dr = dma->reg;
+	v = dr->dcsr;
+	if((v & (CSRstrta|CSRstrtb|CSRrun)) == (CSRstrta|CSRstrtb|CSRrun))
+		return 0;	/* fully occupied */
+
+	dcflush(buf, nbytes);
+
+	csr = CSRrun | CSRie;
+
+	/* start first xfer with buffer B or A? */
+	b = (v & CSRbiu) != 0 && (v & CSRstrtb) == 0 || (v & CSRstrta) != 0;
+	if(b)
+		csr |= CSRstrtb;
+	else
+		csr |= CSRstrta;
+
+	if(v & csr & (CSRstrtb|CSRstrta))
+		panic("dmasetup csr=%2.2lux %2.2lux", v, csr);
+
+	 /* set first src/dst and size */
+	dr->buf[b].start = (ulong)buf;
+	dr->buf[b].count = nbytes;
+	dr->dcsr_s = csr;
+	return 1;
+}
+
+/*
+ * stop dma on a channel
+ */
+void
+dmastop(Dma *dma)
+{
+	// print("dmastop (was %ux)\n", dma->reg->dcsr);
+
+	dma->reg->dcsr_c =	CSRrun |
+					CSRie |
+					CSRerror |
+					CSRdonea |
+					CSRstrta |
+					CSRdoneb |
+					CSRstrtb;
+}
+
+/*
+ * return nonzero if there was a memory error during DMA,
+ * and clear the error state
+ */
+int
+dmaerror(Dma *dma)
+{
+	DmaReg *dr;
+	ulong e;
+
+	dr = dma->reg;
+	e = dr->dcsr & CSRerror;
+	dr->dcsr_c = e;
+	return e;
+}
+
+/*
+ * return nonzero if the DMA channel is not busy
+ */
+int
+dmaidle(Dma *d)
+{
+	return (d->reg->dcsr & (CSRstrta|CSRstrtb)) == 0;
+}
+
+static int
+dmaidlep(void *a)
+{
+	return dmaidle((Dma*)a);
+}
+
+void
+dmawait(Dma *d)
+{
+	while(!dmaidle(d))
+		sleep(&d->r, dmaidlep, d);
+}
+
+/*
+ * this interface really only copes with one buffer at once
+ */
+static void
+dmaintr(Ureg*, void *a)
+{
+	Dma *d;
+	ulong s;
+
+	d = (Dma*)a;
+	s = d->reg->dcsr;
+	if(s & CSRerror)
+		iprint("DMA error, chan %d status #%2.2lux\n", d->chan, s);
+	s &= (CSRdonea|CSRdoneb|CSRerror);
+	d->reg->dcsr_c = s;
+	if(d->interrupt != nil)
+		d->interrupt(d->arg, s & (CSRdonea|CSRdoneb));
+	wakeup(&d->r);
+}
--- /dev/null
+++ b/os/sa1110/etherif.h
@@ -1,0 +1,41 @@
+enum {
+	MaxEther	= 3,
+	Ntypes		= 8,
+};
+
+typedef struct Ether Ether;
+struct Ether {
+RWlock;	/* TO DO */
+	ISAConf;			/* hardware info */
+	int	ctlrno;
+	int	minmtu;
+	int	maxmtu;
+	uchar	ea[Eaddrlen];
+	int	encry;
+
+	void	(*attach)(Ether*);	/* filled in by reset routine */
+	void	(*closed)(Ether*);
+	void	(*detach)(Ether*);
+	void	(*transmit)(Ether*);
+	void	(*interrupt)(Ureg*, void*);
+	long	(*ifstat)(Ether*, void*, long, ulong);
+	long	(*ctl)(Ether*, void*, long); /* custom ctl messages */
+	void	(*power)(Ether*, int);	/* power on/off */
+	void	(*shutdown)(Ether*);	/* shutdown hardware before reboot */
+	void	*ctlr;
+	int	pcmslot;		/* PCMCIA */
+	int	fullduplex;	/* non-zero if full duplex */
+
+	Queue*	oq;
+
+	Netif;
+};
+
+extern Block* etheriq(Ether*, Block*, int);
+extern void addethercard(char*, int(*)(Ether*));
+extern int archether(int, Ether*);
+
+#define NEXT(x, l)	(((x)+1)%(l))
+#define PREV(x, l)	(((x) == 0) ? (l)-1: (x)-1)
+#define	HOWMANY(x, y)	(((x)+((y)-1))/(y))
+#define ROUNDUP(x, y)	(HOWMANY((x), (y))*(y))
--- /dev/null
+++ b/os/sa1110/fpi.h
@@ -1,0 +1,61 @@
+typedef long Word;
+typedef unsigned long Single;
+typedef struct {
+	unsigned long l;
+	unsigned long h;
+} Double;
+
+enum {
+	FractBits	= 28,
+	CarryBit	= 0x10000000,
+	HiddenBit	= 0x08000000,
+	MsBit		= HiddenBit,
+	NGuardBits	= 3,
+	GuardMask	= 0x07,
+	LsBit		= (1<<NGuardBits),
+
+	SingleExpBias	= 127,
+	SingleExpMax	= 255,
+	DoubleExpBias	= 1023,
+	DoubleExpMax	= 2047,
+
+	ExpBias		= DoubleExpBias,
+	ExpInfinity	= DoubleExpMax,
+};
+
+typedef struct {
+	unsigned char s;
+	short e;
+	long l;				/* 0000FFFFFFFFFFFFFFFFFFFFFFFFFGGG */
+	long h;				/* 0000HFFFFFFFFFFFFFFFFFFFFFFFFFFF */
+} Internal;
+
+#define IsWeird(n)	((n)->e >= ExpInfinity)
+#define	IsInfinity(n)	(IsWeird(n) && (n)->h == HiddenBit && (n)->l == 0)
+#define	SetInfinity(n)	((n)->e = ExpInfinity, (n)->h = HiddenBit, (n)->l = 0)
+#define IsNaN(n)	(IsWeird(n) && (((n)->h & ~HiddenBit) || (n)->l))
+#define	SetQNaN(n)	((n)->s = 0, (n)->e = ExpInfinity, 		\
+			 (n)->h = HiddenBit|(LsBit<<1), (n)->l = 0)
+#define IsZero(n)	((n)->e == 1 && (n)->h == 0 && (n)->l == 0)
+#define SetZero(n)	((n)->e = 1, (n)->h = 0, (n)->l = 0)
+
+/*
+ * fpi.c
+ */
+extern void fpiround(Internal *);
+extern void fpiadd(Internal *, Internal *, Internal *);
+extern void fpisub(Internal *, Internal *, Internal *);
+extern void fpimul(Internal *, Internal *, Internal *);
+extern void fpidiv(Internal *, Internal *, Internal *);
+extern int fpicmp(Internal *, Internal *);
+extern void fpinormalise(Internal*);
+
+/*
+ * fpimem.c
+ */
+extern void fpis2i(Internal *, void *);
+extern void fpid2i(Internal *, void *);
+extern void fpiw2i(Internal *, void *);
+extern void fpii2s(void *, Internal *);
+extern void fpii2d(void *, Internal *);
+extern void fpii2w(Word *, Internal *);
--- /dev/null
+++ b/os/sa1110/fpiarm.c
@@ -1,0 +1,483 @@
+/*
+ * this doesn't attempt to implement ARM floating-point properties
+ * that aren't visible in the Inferno environment.
+ * all arithmetic is done in double precision.
+ * the FP trap status isn't updated.
+ */
+#include "u.h"
+#include	"../port/lib.h"
+#include	"mem.h"
+#include	"dat.h"
+#include	"fns.h"
+#include	"io.h"
+
+#include	"ureg.h"
+
+#include "fpi.h"
+
+// #define	R13OK	undef this if correct kernel r13 isn't in Ureg; check calculation in fpiarm below
+
+#define	REG(x) (*(long*)(((char*)(ur))+roff[(x)]))
+#define	FPENV	(*(ufp))
+#define	FR(x) (*(Internal*)(ufp)->regs[(x)&7])
+
+/* BUG: check fetch (not worthwhile in Inferno) */
+#define	getubyte(a) (*(uchar*)(a))
+#define	getuword(a) (*(ushort*)(a))
+#define	getulong(a) (*(ulong*)(a))
+
+typedef struct FP2 FP2;
+typedef struct FP1 FP1;
+
+struct FP2 {
+	char*	name;
+	void	(*f)(Internal, Internal, Internal*);
+};
+
+struct FP1 {
+	char*	name;
+	void	(*f)(Internal*, Internal*);
+};
+
+enum {
+	N = 1<<31,
+	Z = 1<<30,
+	C = 1<<29,
+	V = 1<<28,
+	REGPC = 15,
+};
+
+int	fpemudebug = 0;
+
+#undef OFR
+#define	OFR(X)	((ulong)&((Ureg*)0)->X)
+
+static	int	roff[] = {
+	OFR(r0), OFR(r1), OFR(r2), OFR(r3),
+	OFR(r4), OFR(r5), OFR(r6), OFR(r7),
+	OFR(r8), OFR(r9), OFR(r10), OFR(r11),
+#ifdef R13OK
+	OFR(r12), OFR(r13), OFR(r14), OFR(pc),
+#else
+	OFR(r12), OFR(type), OFR(r14), OFR(pc),
+#endif
+};
+
+static Internal fpconst[8] = {	/* indexed by op&7 */
+	/* s, e, l, h */
+	{0, 0x1, 0x00000000, 0x00000000}, /* 0.0 */
+	{0, 0x3FF, 0x00000000, 0x08000000},	/* 1.0 */
+	{0, 0x400, 0x00000000, 0x08000000},	/* 2.0 */
+	{0, 0x400, 0x00000000, 0x0C000000},	/* 3.0 */
+	{0, 0x401, 0x00000000, 0x08000000},	/* 4.0 */
+	{0, 0x401, 0x00000000, 0x0A000000},	/* 5.0 */
+	{0, 0x3FE, 0x00000000, 0x08000000},	/* 0.5 */
+	{0, 0x402, 0x00000000, 0x0A000000},	/* 10.0 */
+};
+
+/*
+ * arm binary operations
+ */
+
+static void
+fadd(Internal m, Internal n, Internal *d)
+{
+	(m.s == n.s? fpiadd: fpisub)(&m, &n, d);
+}
+
+static void
+fsub(Internal m, Internal n, Internal *d)
+{
+	m.s ^= 1;
+	(m.s == n.s? fpiadd: fpisub)(&m, &n, d);
+}
+
+static void
+fsubr(Internal m, Internal n, Internal *d)
+{
+	n.s ^= 1;
+	(n.s == m.s? fpiadd: fpisub)(&n, &m, d);
+}
+
+static void
+fmul(Internal m, Internal n, Internal *d)
+{
+	fpimul(&m, &n, d);
+}
+
+static void
+fdiv(Internal m, Internal n, Internal *d)
+{
+	fpidiv(&m, &n, d);
+}
+
+static void
+fdivr(Internal m, Internal n, Internal *d)
+{
+	fpidiv(&n, &m, d);
+}
+
+/*
+ * arm unary operations
+ */
+
+static void
+fmov(Internal *m, Internal *d)
+{
+	*d = *m;
+}
+
+static void
+fmovn(Internal *m, Internal *d)
+{
+	*d = *m;
+	d->s ^= 1;
+}
+
+static void
+fabsf(Internal *m, Internal *d)
+{
+	*d = *m;
+	d->s = 0;
+}
+
+static void
+frnd(Internal *m, Internal *d)
+{
+	short e;
+
+	(m->s? fsub: fadd)(fpconst[6], *m, d);
+	if(IsWeird(d))
+		return;
+	fpiround(d);
+	e = (d->e - ExpBias) + 1;
+	if(e <= 0)
+		SetZero(d);
+	else if(e > FractBits){
+		if(e < 2*FractBits)
+			d->l &= ~((1<<(2*FractBits - e))-1);
+	}else{
+		d->l = 0;
+		if(e < FractBits)
+			d->h &= ~((1<<(FractBits-e))-1);
+	}
+}
+
+static	FP1	optab1[16] = {	/* Fd := OP Fm */
+[0]	{"MOVF",	fmov},
+[1]	{"NEGF",	fmovn},
+[2]	{"ABSF",	fabsf},
+[3]	{"RNDF",	frnd},
+[4]	{"SQTF",	/*fsqt*/0},
+/* LOG, LGN, EXP, SIN, COS, TAN, ASN, ACS, ATN all `deprecated' */
+/* URD and NRM aren't implemented */
+};
+
+static	FP2	optab2[16] = {	/* Fd := Fn OP Fm */
+[0]	{"ADDF",	fadd},
+[1]	{"MULF",	fmul},
+[2]	{"SUBF",	fsub},
+[3]	{"RSUBF",	fsubr},
+[4]	{"DIVF",	fdiv},
+[5]	{"RDIVF",	fdivr},
+/* POW, RPW deprecated */
+[8]	{"REMF",	/*frem*/0},
+[9]	{"FMF",	fmul},	/* fast multiply */
+[10]	{"FDV",	fdiv},	/* fast divide */
+[11]	{"FRD",	fdivr},	/* fast reverse divide */
+/* POL deprecated */
+};
+
+static ulong
+fcmp(Internal *n, Internal *m)
+{
+	int i;
+
+	if(IsWeird(m) || IsWeird(n)){
+		/* BUG: should trap if not masked */
+		return V|C;
+	}
+	i = fpicmp(n, m);
+	if(i > 0)
+		return C;
+	else if(i == 0)
+		return C|Z;
+	else
+		return N;
+}
+
+static void
+fld(void (*f)(Internal*, void*), int d, ulong ea, int n, FPenv *ufp)
+{
+	void *mem;
+
+	mem = (void*)ea;
+	(*f)(&FR(d), mem);
+	if(fpemudebug)
+		print("MOV%c #%lux, F%d\n", n==8? 'D': 'F', ea, d);
+}
+
+static void
+fst(void (*f)(void*, Internal*), ulong ea, int s, int n, FPenv *ufp)
+{
+	Internal tmp;
+	void *mem;
+
+	mem = (void*)ea;
+	tmp = FR(s);
+	if(fpemudebug)
+		print("MOV%c	F%d,#%lux\n", n==8? 'D': 'F', s, ea);
+	(*f)(mem, &tmp);
+}
+
+static int
+condok(int cc, int c)
+{
+	switch(c){
+	case 0:	/* Z set */
+		return cc&Z;
+	case 1:	/* Z clear */
+		return (cc&Z) == 0;
+	case 2:	/* C set */
+		return cc&C;
+	case 3:	/* C clear */
+		return (cc&C) == 0;
+	case 4:	/* N set */
+		return cc&N;
+	case 5:	/* N clear */
+		return (cc&N) == 0;
+	case 6:	/* V set */
+		return cc&V;
+	case 7:	/* V clear */
+		return (cc&V) == 0;
+	case 8:	/* C set and Z clear */
+		return cc&C && (cc&Z) == 0;
+	case 9:	/* C clear or Z set */
+		return (cc&C) == 0 || cc&Z;
+	case 10:	/* N set and V set, or N clear and V clear */
+		return (~cc&(N|V))==0 || (cc&(N|V)) == 0;
+	case 11:	/* N set and V clear, or N clear and V set */
+		return (cc&(N|V))==N || (cc&(N|V))==V;
+	case 12:	/* Z clear, and either N set and V set or N clear and V clear */
+		return (cc&Z) == 0 && ((~cc&(N|V))==0 || (cc&(N|V))==0);
+	case 13:	/* Z set, or N set and V clear or N clear and V set */
+		return (cc&Z) || (cc&(N|V))==N || (cc&(N|V))==V;
+	case 14:	/* always */
+		return 1;
+	case 15:	/* never (reserved) */
+		return 0;
+	}
+	return 0;	/* not reached */
+}
+
+static void
+unimp(ulong pc, ulong op)
+{
+	char buf[60];
+
+	snprint(buf, sizeof(buf), "sys: fp: pc=%lux unimp fp 0x%.8lux", pc, op);
+	if(fpemudebug)
+		print("FPE: %s\n", buf);
+	error(buf);
+	/* no return */
+}
+
+static void
+fpemu(ulong pc, ulong op, Ureg *ur, FPenv *ufp)
+{
+	int rn, rd, tag, o;
+	long off;
+	ulong ea;
+	Internal tmp, *fm, *fn;
+
+	/* note: would update fault status here if we noted numeric exceptions */
+
+	/*
+	 * LDF, STF; 10.1.1
+	 */
+	if(((op>>25)&7) == 6){
+		if(op & (1<<22))
+			unimp(pc, op);	/* packed or extended */
+		rn = (op>>16)&0xF;
+		off = (op&0xFF)<<2;
+		if((op & (1<<23)) == 0)
+			off = -off;
+		ea = REG(rn);
+		if(rn == REGPC)
+			ea += 8;
+		if(op & (1<<24))
+			ea += off;
+		rd = (op>>12)&7;
+		if(op & (1<<20)){
+			if(op & (1<<15))
+				fld(fpid2i, rd, ea, 8, ufp);
+			else
+				fld(fpis2i, rd, ea, 4, ufp);
+		}else{
+			if(op & (1<<15))
+				fst(fpii2d, ea, rd, 8, ufp);
+			else
+				fst(fpii2s, ea, rd, 4, ufp);
+		}
+		if((op & (1<<24)) == 0)
+			ea += off;
+		if(op & (1<<21))
+			REG(rn) = ea;
+		return;
+	}
+
+	/*
+	 * CPRT/transfer, 10.3
+	 */
+	if(op & (1<<4)){
+		rd = (op>>12) & 0xF;
+
+		/*
+		 * compare, 10.3.1
+		 */
+		if(rd == 15 && op & (1<<20)){
+			rn = (op>>16)&7;
+			fn = &FR(rn);
+			if(op & (1<<3)){
+				fm = &fpconst[op&7];
+				tag = 'C';
+			}else{
+				fm = &FR(op&7);
+				tag = 'F';
+			}
+			switch((op>>21)&7){
+			default:
+				unimp(pc, op);
+			case 4:	/* CMF: Fn :: Fm */
+			case 6:	/* CMFE: Fn :: Fm (with exception) */
+				ur->psr &= ~(N|C|Z|V);
+				ur->psr |= fcmp(fn, fm);
+				break;
+			case 5:	/* CNF: Fn :: -Fm */
+			case 7:	/* CNFE: Fn :: -Fm (with exception) */
+				tmp = *fm;
+				tmp.s ^= 1;
+				ur->psr &= ~(N|C|Z|V);
+				ur->psr |= fcmp(fn, &tmp);
+				break;
+			}
+			if(fpemudebug)
+				print("CMPF	%c%d,F%ld =%x\n", tag, rn, op&7, ur->psr>>28);
+			return;
+		}
+
+		/*
+		 * other transfer, 10.3
+		 */
+		switch((op>>20)&0xF){
+		default:
+			unimp(pc, op);
+		case 0:	/* FLT */
+			rn = (op>>16) & 7;
+			fpiw2i(&FR(rn), &REG(rd));
+			if(fpemudebug)
+				print("MOVW[FD]	R%d, F%d\n", rd, rn);
+			break;
+		case 1:	/* FIX */
+			if(op & (1<<3))
+				unimp(pc, op);
+			rn = op & 7;
+			tmp = FR(rn);
+			fpii2w(&REG(rd), &tmp);
+			if(fpemudebug)
+				print("MOV[FD]W	F%d, R%d =%ld\n", rn, rd, REG(rd));
+			break;
+		case 2:	/* FPSR := Rd */
+			FPENV.status = REG(rd);
+			if(fpemudebug)
+				print("MOVW	R%d, FPSR\n", rd);
+			break;
+		case 3:	/* Rd := FPSR */
+			REG(rd) = FPENV.status;
+			if(fpemudebug)
+				print("MOVW	FPSR, R%d\n", rd);
+			break;
+		case 4:	/* FPCR := Rd */
+			FPENV.control = REG(rd);
+			if(fpemudebug)
+				print("MOVW	R%d, FPCR\n", rd);
+			break;
+		case 5:	/* Rd := FPCR */
+			REG(rd) = FPENV.control;
+			if(fpemudebug)
+				print("MOVW	FPCR, R%d\n", rd);
+			break;
+		}
+		return;
+	}
+
+	/*
+	 * arithmetic
+	 */
+
+	if(op & (1<<3)){	/* constant */
+		fm = &fpconst[op&7];
+		tag = 'C';
+	}else{
+		fm = &FR(op&7);
+		tag = 'F';
+	}
+	rd = (op>>12)&7;
+	o = (op>>20)&0xF;
+	if(op & (1<<15)){	/* monadic */
+		FP1 *fp;
+		fp = &optab1[o];
+		if(fp->f == nil)
+			unimp(pc, op);
+		if(fpemudebug)
+			print("%s	%c%ld,F%d\n", fp->name, tag, op&7, rd);
+		(*fp->f)(fm, &FR(rd));
+	} else {
+		FP2 *fp;
+		fp = &optab2[o];
+		if(fp->f == nil)
+			unimp(pc, op);
+		rn = (op>>16)&7;
+		if(fpemudebug)
+			print("%s	%c%ld,F%d,F%d\n", fp->name, tag, op&7, rn, rd);
+		(*fp->f)(*fm, FR(rn), &FR(rd));
+	}
+}
+
+/*
+ * returns the number of FP instructions emulated
+ */
+int
+fpiarm(Ureg *ur)
+{
+	ulong op, o;
+	FPenv *ufp;
+	int n;
+
+#ifndef R13OK
+/*	ur->type = &ur->pc+1;	/* calculate kernel sp/R13 and put it here for roff[13] */
+	ur->type = (ulong)(ur + 1);
+#endif
+	if (up == nil)
+		panic("fpiarm not in a process");
+	ufp = &up->env->fpu;	/* because all the state is in Osenv, it need not be saved/restored */
+	if(ufp->fpistate != FPACTIVE) {
+		ufp->fpistate = FPACTIVE;
+		ufp->control = 0;
+		ufp->status = (0x01<<28)|(1<<12);	/* software emulation, alternative C flag */
+		for(n = 0; n < 8; n++)
+			FR(n) = fpconst[0];
+	}
+	for(n=0;;n++){
+		op = getulong(ur->pc);
+		o = (op>>24) & 0xF;
+		if(((op>>8) & 0xF) != 1 || o != 0xE && (o&~1) != 0xC)
+			break;
+		if(condok(ur->psr, op>>28))
+			fpemu(ur->pc, op, ur, ufp);
+		ur->pc += 4;
+		if(anyhigher())
+			sched();
+	}
+	return n;
+}
--- /dev/null
+++ b/os/sa1110/gscreen.c
@@ -1,0 +1,587 @@
+#include "u.h"
+#include "../port/lib.h"
+#include "mem.h"
+#include "dat.h"
+#include "fns.h"
+#include "../port/error.h"
+#include "io.h"
+
+#include <draw.h>
+#include <memdraw.h>
+#include <cursor.h>
+
+#include "screen.h"
+
+enum {
+	Backgnd = 0xFF,	/* white */
+	Foregnd =	0x00,	/* black */
+};
+
+#define	DPRINT	if(1)iprint
+
+static Memdata xgdata;
+static Memimage xgscreen =
+{
+	{0, 0, 0, 0},	/* r */
+	{0, 0, 0, 0},	/* clipr */
+	8,			/* depth */
+	1,			/* nchan */
+	CMAP8,		/* chan */
+	nil,			/* cmap */
+	&xgdata,		/* data */
+	0,			/* zero */
+	0,			/* width */
+	nil,			/* layer */
+	0,			/* flags */
+};
+
+Memimage *gscreen;
+Memimage *conscol;
+Memimage *back;
+
+Memsubfont *memdefont;
+
+static Point curpos;
+static Rectangle window;
+
+typedef struct SWcursor SWcursor;
+
+static Vdisplay *vd;
+
+static char printbuf[1024];
+static int printbufpos = 0;
+static void	lcdscreenputs(char*, int);
+static void screenpbuf(char*, int);
+void (*screenputs)(char*, int) = screenpbuf;
+
+static Cursor arrow = {
+	{ -1, -1 },
+	{ 0xFF, 0xFF, 0x80, 0x01, 0x80, 0x02, 0x80, 0x0C,
+	  0x80, 0x10, 0x80, 0x10, 0x80, 0x08, 0x80, 0x04,
+	  0x80, 0x02, 0x80, 0x01, 0x80, 0x02, 0x8C, 0x04,
+	  0x92, 0x08, 0x91, 0x10, 0xA0, 0xA0, 0xC0, 0x40,
+	},
+	{ 0x00, 0x00, 0x7F, 0xFE, 0x7F, 0xFC, 0x7F, 0xF0,
+	  0x7F, 0xE0, 0x7F, 0xE0, 0x7F, 0xF0, 0x7F, 0xF8,
+	  0x7F, 0xFC, 0x7F, 0xFE, 0x7F, 0xFC, 0x73, 0xF8,
+	  0x61, 0xF0, 0x60, 0xE0, 0x40, 0x40, 0x00, 0x00,
+	},
+};
+
+static	ushort	palette16[256];
+static	void	(*flushpixels)(Rectangle, ulong*, int, ulong*, int);
+static	void	flush8to4(Rectangle, ulong*, int, ulong*, int);
+static	void	flush8to4r(Rectangle, ulong*, int, ulong*, int);
+static	void	flush8to16(Rectangle, ulong*, int, ulong*, int);
+static	void	flush8to16r(Rectangle, ulong*, int, ulong*, int);
+
+/*
+lccr0=000000b9 lccr1=0b100930 lccr2=0a0108ef lccr3=00300010
+	---
+vd->wid=320 bwid=640 gscreen->width=60 fb=d0b7cb80 data=d0ba25c0
+ */
+
+int
+setcolor(ulong p, ulong r, ulong g, ulong b)
+{
+	if(vd->depth >= 8)
+		p &= 0xff;
+	else
+		p &= 0xf;
+	vd->colormap[p][0] = r;
+	vd->colormap[p][1] = g;
+	vd->colormap[p][2] = b;
+	palette16[p] = ((r>>(32-4))<<12)|((g>>(32-4))<<7)|((b>>(32-4))<<1);
+	lcd_setcolor(p, r, g, b);
+	return ~0;
+}
+
+void
+getcolor(ulong p, ulong *pr, ulong *pg, ulong *pb)
+{
+	if(vd->depth >= 8)
+		p = (p&0xff)^0xff;
+	else
+		p = (p&0xf)^0xf;
+	*pr = vd->colormap[p][0];
+	*pg = vd->colormap[p][1];
+	*pb = vd->colormap[p][2];
+}
+
+void
+graphicscmap(int invert)
+{
+	int num, den, i, j;
+	int r, g, b, cr, cg, cb, v, p;
+
+	for(r=0,i=0;r!=4;r++) for(v=0;v!=4;v++,i+=16){
+		for(g=0,j=v-r;g!=4;g++) for(b=0;b!=4;b++,j++){
+			den=r;
+			if(g>den) den=g;
+			if(b>den) den=b;
+			if(den==0)	/* divide check -- pick grey shades */
+				cr=cg=cb=v*17;
+			else{
+				num=17*(4*den+v);
+				cr=r*num/den;
+				cg=g*num/den;
+				cb=b*num/den;
+			}
+			p = (i+(j&15));
+			if(invert)
+				p ^= 0xFF;
+			if(vd->depth == 4) {
+				if((p&0xf) != (p>>4))
+					continue;
+				p &= 0xf;
+			}
+			setcolor(p,
+				cr*0x01010101,
+				cg*0x01010101,
+				cb*0x01010101);
+		}
+	}
+	lcd_flush();
+}
+
+static uchar lum[256]={
+  0,   7,  15,  23,  39,  47,  55,  63,  79,  87,  95, 103, 119, 127, 135, 143,
+154,  17,   9,  17,  25,  49,  59,  62,  68,  89,  98, 107, 111, 129, 138, 146,
+157, 166,  34,  11,  19,  27,  59,  71,  69,  73,  99, 109, 119, 119, 139, 148,
+159, 169, 178,  51,  13,  21,  29,  69,  83,  75,  78, 109, 120, 131, 128, 149,
+ 28,  35,  43,  60,  68,  75,  83, 100, 107, 115, 123, 140, 147, 155, 163,  20,
+ 25,  35,  40,  47,  75,  85,  84,  89, 112, 121, 129, 133, 151, 159, 168, 176,
+190,  30,  42,  44,  50,  90, 102,  94,  97, 125, 134, 144, 143, 163, 172, 181,
+194, 204,  35,  49,  49,  54, 105, 119, 103, 104, 137, 148, 158, 154, 175, 184,
+ 56,  63,  80,  88,  96, 103, 120, 128, 136, 143, 160, 168, 175, 183,  40,  48,
+ 54,  63,  69,  90,  99, 107, 111, 135, 144, 153, 155, 173, 182, 190, 198,  45,
+ 50,  60,  70,  74, 100, 110, 120, 120, 150, 160, 170, 167, 186, 195, 204, 214,
+229,  55,  66,  77,  79, 110, 121, 131, 129, 165, 176, 187, 179, 200, 210, 219,
+ 84, 100, 108, 116, 124, 140, 148, 156, 164, 180, 188, 196, 204,  60,  68,  76,
+ 82,  91, 108, 117, 125, 134, 152, 160, 169, 177, 195, 204, 212, 221,  66,  74,
+ 80,  89,  98, 117, 126, 135, 144, 163, 172, 181, 191, 210, 219, 228, 238,  71,
+ 76,  85,  95, 105, 126, 135, 145, 155, 176, 185, 195, 205, 225, 235, 245, 255,
+};
+
+void flushmemscreen(Rectangle r);
+
+void
+screenclear(void)
+{
+	memimagedraw(gscreen, gscreen->r, memwhite, ZP, memopaque, ZP, SoverD);
+	curpos = window.min;
+	flushmemscreen(gscreen->r);
+}
+
+static void
+setscreen(LCDmode *mode)
+{
+	int h;
+
+//	if(swc != nil)
+//		swcurs_destroy(swc);
+
+	vd = lcd_init(mode);
+	if(vd == nil)
+		panic("can't initialise LCD");
+
+	if(lum[255] == 255) {
+		int i;
+		for(i=0; i<256; i++)
+			lum[i] >>= 4;	/* could support depths other than 4 */
+	}
+
+	gscreen = &xgscreen;
+	xgdata.ref = 1;
+
+	if(conf.portrait == 0)
+		gscreen->r = Rect(0, 0, vd->x, vd->y);
+	else
+		gscreen->r = Rect(0, 0, vd->y, vd->x);
+	gscreen->clipr = gscreen->r;
+	gscreen->depth = 8;
+	gscreen->width = wordsperline(gscreen->r, gscreen->depth);
+	if(vd->depth == 4 || vd->depth == 16 || conf.portrait) {	/* use 8 to 4 bit fakeout for speed */
+		if((xgdata.bdata = xspanalloc(gscreen->width*gscreen->r.max.y*BY2WD+CACHELINESZ, CACHELINESZ, 0)) == nil)
+			panic("can't alloc vidmem");
+		xgdata.bdata = minicached(xgdata.bdata);
+		if(conf.portrait == 0)
+			flushpixels = vd->depth==4? flush8to4: flush8to16;
+		else
+			flushpixels = vd->depth==4? flush8to4r: flush8to16r;
+	} else{
+		xgdata.bdata = (uchar*)vd->fb;
+		flushpixels = nil;
+	}
+	memimageinit();
+	memdefont = getmemdefont();
+
+	memsetchan(gscreen, CMAP8);	/* TO DO: could now use RGB16 */
+	back = memwhite;
+	conscol = memblack;
+	memimagedraw(gscreen, gscreen->r, memwhite, ZP, memopaque, ZP, SoverD);
+
+	DPRINT("vd->wid=%d bwid=%d gscreen->width=%ld fb=%p data=%p\n",
+		vd->x, vd->bwid, gscreen->width, vd->fb, xgdata.bdata);
+	graphicscmap(0);
+	h = memdefont->height;
+	window = insetrect(gscreen->r, 4);
+	window.max.y = window.min.y+(Dy(window)/h)*h;
+	screenclear();
+
+//	swc = swcurs_create(gscreendata.data, gscreen.width, gscreen.ldepth, gscreen.r, 1);
+
+	drawcursor(nil);
+}
+
+void
+screeninit(void)
+{
+	LCDmode lcd;
+
+	memset(&lcd, 0, sizeof(lcd));
+	if(archlcdmode(&lcd) < 0)
+		return;
+	setscreen(&lcd);
+	screenputs = lcdscreenputs;
+	if(printbufpos)
+		screenputs("", 0);
+	blanktime = 3;	/* minutes */
+}
+
+uchar*
+attachscreen(Rectangle *r, ulong *chan, int* d, int *width, int *softscreen)
+{
+	*r = gscreen->r;
+	*d = gscreen->depth;
+	*chan = gscreen->chan;
+	*width = gscreen->width;
+	*softscreen = (gscreen->data->bdata != (uchar*)vd->fb);
+
+	return (uchar*)gscreen->data->bdata;
+}
+
+void
+detachscreen(void)
+{
+}
+
+static void
+flush8to4(Rectangle r, ulong *s, int sw, ulong *d, int dw)
+{
+	int i, h, w;
+
+/*
+	print("1) s=%ux sw=%d d=%ux dw=%d r=(%d,%d)(%d,%d)\n",
+		s, sw, d, dw, r.min.x, r.min.y, r.max.x, r.max.y);
+*/
+
+	r.min.x &= ~7;
+	r.max.x = (r.max.x + 7) & ~7;
+	s += (r.min.y*sw)+(r.min.x>>2);
+	d += (r.min.y*dw)+(r.min.x>>3);
+	h = Dy(r);
+	w = Dx(r) >> 3;
+	sw -= w*2;
+	dw -= w;
+
+	while(h--) {
+		for(i=w; i; i--) {
+			ulong v1 = *s++;
+			ulong v2 = *s++;
+			*d++ = 	 (lum[v2>>24]<<28)
+				|(lum[(v2>>16)&0xff]<<24)
+				|(lum[(v2>>8)&0xff]<<20)
+				|(lum[v2&0xff]<<16)
+				|(lum[v1>>24]<<12)
+				|(lum[(v1>>16)&0xff]<<8)
+				|(lum[(v1>>8)&0xff]<<4)
+				|(lum[v1&0xff])
+				;
+		}
+		s += sw;
+		d += dw;
+	}
+}
+
+static void
+flush8to16(Rectangle r, ulong *s, int sw, ulong *d, int dw)
+{
+	int i, h, w;
+	ushort *p;
+
+	if(0)
+		iprint("1) s=%p sw=%d d=%p dw=%d r=[%d,%d, %d,%d]\n",
+		s, sw, d, dw, r.min.x, r.min.y, r.max.x, r.max.y);
+
+	r.min.x &= ~3;
+	r.max.x = (r.max.x + 3) & ~3;	/* nearest ulong */
+	s += (r.min.y*sw)+(r.min.x>>2);
+	d += (r.min.y*dw)+(r.min.x>>1);
+	h = Dy(r);
+	w = Dx(r) >> 2;	/* also ulong */
+	sw -= w;
+	dw -= w*2;
+	if(0)
+		iprint("h=%d w=%d sw=%d dw=%d\n", h, w, sw, dw);
+
+	p = palette16;
+	while(--h >= 0){
+		for(i=w; --i>=0;){
+			ulong v = *s++;
+			*d++ = (p[(v>>8)&0xFF]<<16) | p[v & 0xFF];
+			*d++ = (p[v>>24]<<16) | p[(v>>16)&0xFF];
+		}
+		s += sw;
+		d += dw;
+	}
+}
+
+static void
+flush8to4r(Rectangle r, ulong *s, int sw, ulong *d, int dw)
+{
+	flush8to4(r, s, sw, d, dw);	/* rotation not implemented */
+}
+
+static void
+flush8to16r(Rectangle r, ulong *s, int sw, ulong *d, int dw)
+{
+	int x, y, w, dws;
+	ushort *p;
+	ushort *ds;
+
+	if(0)
+		iprint("1) s=%p sw=%d d=%p dw=%d r=[%d,%d, %d,%d]\n",
+		s, sw, d, dw, r.min.x, r.min.y, r.max.x, r.max.y);
+
+	r.min.y &= ~3;
+	r.max.y = (r.max.y+3) & ~3;
+	r.min.x &= ~7;
+	r.max.x = (r.max.x + 7) & ~7;
+	s += (r.min.y*sw)+(r.min.x>>2);
+//	d += (r.min.y*dw)+(r.min.x>>1);
+	w = Dx(r) >> 2;	/* also ulong */
+	sw -= w;
+	dws = dw*2;
+	if(0)
+		iprint("h=%d w=%d sw=%d dw=%d x,y=%d,%d %d\n", Dy(r), w, sw, dw, r.min.x,r.min.y, dws);
+
+	p = palette16;
+	for(y=r.min.y; y<r.max.y; y++){
+		for(x=r.min.x; x<r.max.x; x+=4){
+			ulong v = *s++;
+			ds = (ushort*)(d + x*dw) + (gscreen->r.max.y-(y+1));
+			ds[0] = p[v & 0xFF];
+			ds[dws] = p[(v>>8)&0xFF];
+			ds[dws*2] = p[(v>>16)&0xFF];
+			ds[dws*3] = p[(v>>24)&0xFF];
+		}
+		s += sw;
+	}
+}
+
+void
+flushmemscreen(Rectangle r)
+{
+	if(rectclip(&r, gscreen->r) == 0)
+		return;
+	if(r.min.x >= r.max.x || r.min.y >= r.max.y)
+		return;
+	if(flushpixels != nil)
+		flushpixels(r, (ulong*)gscreen->data->bdata, gscreen->width, (ulong*)vd->fb, vd->bwid >> 2);
+	lcd_flush();
+}
+
+static void
+scroll(void)
+{
+	int o;
+	Point p;
+	Rectangle r;
+
+	o = 4*memdefont->height;
+	r = Rpt(window.min, Pt(window.max.x, window.max.y-o));
+	p = Pt(window.min.x, window.min.y+o);
+	memimagedraw(gscreen, r, gscreen, p, nil, p, SoverD);
+	flushmemscreen(r);
+	r = Rpt(Pt(window.min.x, window.max.y-o), window.max);
+	memimagedraw(gscreen, r, back, ZP, nil, ZP, SoverD);
+	flushmemscreen(r);
+
+	curpos.y -= o;
+}
+
+static void
+clearline(void)
+{
+	Rectangle r;
+	int yloc = curpos.y;
+
+	r = Rpt(Pt(window.min.x, window.min.y + yloc),
+		Pt(window.max.x, window.min.y+yloc+memdefont->height));
+	memimagedraw(gscreen, r, back, ZP, nil, ZP, SoverD);
+}
+
+static void
+screenputc(char *buf)
+{
+	Point p;
+	int h, w, pos;
+	Rectangle r;
+	static int *xp;
+	static int xbuf[256];
+
+	h = memdefont->height;
+	if(xp < xbuf || xp >= &xbuf[sizeof(xbuf)])
+		xp = xbuf;
+
+	switch(buf[0]) {
+	case '\n':
+		if(curpos.y+h >= window.max.y)
+			scroll();
+		curpos.y += h;
+		/* fall through */
+	case '\r':
+		xp = xbuf;
+		curpos.x = window.min.x;
+		break;
+	case '\t':
+		if(curpos.x == window.min.x)
+			clearline();
+		p = memsubfontwidth(memdefont, " ");
+		w = p.x;
+		*xp++ = curpos.x;
+		pos = (curpos.x-window.min.x)/w;
+		pos = 8-(pos%8);
+		r = Rect(curpos.x, curpos.y, curpos.x+pos*w, curpos.y+h);
+		memimagedraw(gscreen, r, back, ZP, nil, ZP, SoverD);
+		flushmemscreen(r);
+		curpos.x += pos*w;
+		break;
+	case '\b':
+		if(xp <= xbuf)
+			break;
+		xp--;
+		r = Rpt(Pt(*xp, curpos.y), Pt(curpos.x, curpos.y + h));
+		memimagedraw(gscreen, r, back, ZP, nil, ZP, SoverD);
+		flushmemscreen(r);
+		curpos.x = *xp;
+		break;
+	case '\0':
+		break;
+	default:
+		p = memsubfontwidth(memdefont, buf);
+		w = p.x;
+
+		if(curpos.x >= window.max.x-w)
+			screenputc("\n");
+
+		if(curpos.x == window.min.x)
+			clearline();
+		if(xp < xbuf+nelem(xbuf))
+			*xp++ = curpos.x;
+		r = Rect(curpos.x, curpos.y, curpos.x+w, curpos.y+h);
+		memimagedraw(gscreen, r, back, ZP, nil, ZP, SoverD);
+		memimagestring(gscreen, curpos, conscol, ZP, memdefont, buf);
+		flushmemscreen(r);
+		curpos.x += w;
+	}
+}
+
+static void
+screenpbuf(char *s, int n)
+{
+	if(printbufpos+n > sizeof(printbuf))
+		n = sizeof(printbuf)-printbufpos;
+	if(n > 0) {
+		memmove(&printbuf[printbufpos], s, n);
+		printbufpos += n;
+	}
+}
+
+static void
+screendoputs(char *s, int n)
+{
+	int i;
+	Rune r;
+	char buf[4];
+
+	while(n > 0) {
+		i = chartorune(&r, s);
+		if(i == 0){
+			s++;
+			--n;
+			continue;
+		}
+		memmove(buf, s, i);
+		buf[i] = 0;
+		n -= i;
+		s += i;
+		screenputc(buf);
+	}
+}
+
+void
+screenflush(void)
+{
+	int j = 0;
+	int k;
+
+	for (k = printbufpos; j < k; k = printbufpos) {
+		screendoputs(printbuf + j, k - j);
+		j = k;
+	}
+	printbufpos = 0;
+}
+
+static void
+lcdscreenputs(char *s, int n)
+{
+	static Proc *me;
+
+	if(!canlock(vd)) {
+		/* don't deadlock trying to print in interrupt */
+		/* don't deadlock trying to print while in print */
+		if(islo() == 0 || up != nil && up == me){
+			/* save it for later... */
+			/* In some cases this allows seeing a panic message
+			  that would be locked out forever */
+			screenpbuf(s, n);
+			return;
+		}
+		lock(vd);
+	}
+
+	me = up;
+	if(printbufpos)
+		screenflush();
+	screendoputs(s, n);
+	if(printbufpos)
+		screenflush();
+	me = nil;
+
+	unlock(vd);
+}
+
+/*
+ * interface between draw, mouse and cursor
+ */
+void
+cursorupdate(Rectangle r)
+{
+}
+
+void
+cursorenable(void)
+{
+}
+
+void
+cursordisable(void)
+{
+}
+
+void
+drawcursor(Drawcursor* c)
+{
+}
--- /dev/null
+++ b/os/sa1110/gscreen.h
@@ -1,0 +1,87 @@
+typedef struct Cursor Cursor;
+typedef struct LCDmode LCDmode;
+typedef struct LCDparam LCDparam;
+typedef struct Vmode Vmode;
+typedef struct Physdisplay Physdisplay;
+typedef struct Physcursor Physcursor;
+
+#define CURSWID	16
+#define CURSHGT	16
+
+struct Cursor {
+	Point	offset;
+	uchar	clr[CURSWID/BI2BY*CURSHGT];
+	uchar	set[CURSWID/BI2BY*CURSHGT];
+};
+
+struct Vmode {
+	int	x;	/* 0 -> default or any match for all fields */
+	int	y;
+	uchar	depth;
+	uchar	hz;
+};
+
+struct Physdisplay {
+	uchar*	fb;		/* frame buffer */
+	ulong	colormap[256][3];
+	Rectangle	r;
+	int	bwid;
+	void*	aux;
+	Memimage*	gscreen;
+	Memsubfont*	memdefont;
+	Physcursor*	cursor;
+	void*	cdata;	/* cursor data */
+	Lock;
+	Vmode;
+};
+
+struct LCDparam {
+	uchar	pbs;
+	uchar	dual;
+	uchar	mono;
+	uchar	active;
+	uchar	hsync_wid;
+	uchar	sol_wait;
+	uchar	eol_wait;
+	uchar	vsync_hgt;
+	uchar	sof_wait;
+	uchar	eof_wait;
+	uchar	lines_per_int;
+	uchar	palette_delay;
+	uchar	acbias_lines;
+	uchar	obits;
+	uchar	vsynclow;
+	uchar	hsynclow;
+};
+
+struct LCDmode {
+	Vmode;
+	LCDparam;
+};
+
+int	archlcdmode(LCDmode*);
+
+Vdisplay	*lcd_init(LCDmode*);
+void	lcd_setcolor(ulong, ulong, ulong, ulong);
+void	lcd_flush(void);
+
+extern void	blankscreen(int);
+extern void	drawblankscreen(int);
+extern ulong blanktime;
+extern Point mousexy(void);
+
+enum {
+	Backgnd = 0xFF,	/* white */
+	Foregnd =	0x00,	/* black */
+};
+
+struct Physcursor {
+	char*	name;
+
+	void	(*create)(Physdisplay*);
+	void	(*enable)(Physdisplay*);
+	void	(*disable)(Physdisplay*);
+	void	(*load)(Physdisplay*, Cursor*);
+	int	(*move)(Physdisplay*, Point);
+	void	(*destroy)(Physdisplay*);
+};
--- /dev/null
+++ b/os/sa1110/i2c.h
@@ -1,0 +1,16 @@
+/* i2cgpio.c */
+
+int i2c_write_byte(uchar addr, uchar data);
+int i2c_read_byte(uchar addr, uchar *data);
+void i2c_reset(void);
+
+extern unsigned char i2c_iactl[];
+int i2c_setpin(int b);
+int i2c_clrpin(int b);
+int i2c_getpin(int b);
+
+/* GPIO pin assignments (0->31) - defined in arch????.c */
+extern int      gpio_i2c_sda;           /* in/out, as per i2c protocol */
+extern int      gpio_i2c_scl;           /* in/out, as per i2c protocol */
+
+
--- /dev/null
+++ b/os/sa1110/i2cgpio.c
@@ -1,0 +1,274 @@
+/*
+ *	I2C master emulation using GPIO pins.
+ *	7 bit addressing only.
+ */
+#include	"u.h"
+#include	"../port/lib.h"
+#include	"mem.h"
+#include	"dat.h"
+#include	"fns.h"
+#include	"../port/error.h"
+#include	"io.h"
+#include	"i2c.h"
+
+/* GPIO bitmasks */
+static  struct {
+	Lock;
+	ulong	sda;
+	ulong	scl;
+} i2c;
+
+
+/* set pin level high by disabling output drive and allowing pull-up to work */
+static void
+i2c_set(int pin)
+{
+	GPIOREG->gpdr &= ~pin;	/* configure pin as input */
+}
+
+/* set pin level low with output drive */
+static void
+i2c_clear(int pin)
+{
+	GPIOREG->gpcr = pin;	/* set pin output low */
+	GPIOREG->gpdr |= pin;	/* configure pin as output */
+}
+
+static int
+i2c_getack(void)
+{
+	/* scl is low, sda is not defined */
+
+	i2c_set(i2c.sda);		/* set data high */
+	timer_delay(US2TMR(3));
+
+	i2c_set(i2c.scl);		/* raise clock */
+	timer_delay(US2TMR(5));
+
+	/* check for ack from slave! */
+	if (GPIOREG->gplr & i2c.sda)
+		print("I2C: Warning did not get ack!\n");
+
+	i2c_clear(i2c.sda);		/* lower data */
+	i2c_clear(i2c.scl);		/* lower clock */
+	timer_delay(US2TMR(3));
+
+	/* scl is low, sda is low */
+	return 1;
+}
+
+
+static void
+i2c_putack(void)
+{
+	/* scl is low, sda is not defined */
+
+	timer_delay(US2TMR(3));		/* lower data */
+	i2c_clear(i2c.sda);
+
+	i2c_set(i2c.scl);			/* pulse clock */
+	timer_delay(US2TMR(5));
+
+	i2c_clear(i2c.scl);			/* lower clock */
+	timer_delay(US2TMR(3));
+
+	/* scl is low, sda is low */
+}
+
+
+static void
+i2c_putbyte(uchar b)
+{
+	uchar m;
+
+	/* start condition has been sent */
+	/* scl is low, sda is low */
+
+	for(m=0x80; m; m >>= 1) {
+		
+		/* set data bit */
+		if(b&m)
+			i2c_set(i2c.sda);
+		else
+			i2c_clear(i2c.sda);
+
+		/* pulse clock */
+		timer_delay(US2TMR(3));
+		i2c_set(i2c.scl);
+		timer_delay(US2TMR(5));
+		i2c_clear(i2c.scl);
+		timer_delay(US2TMR(3));
+	}
+
+	i2c_clear(i2c.sda);
+	/* scl is low, sda is low */
+}
+
+
+static uchar
+i2c_getbyte(void)
+{
+	/* start condition, address and ack been done */
+	/* scl is low, sda is high */
+	uchar data = 0x00;
+	int i;
+
+	i2c_set(i2c.sda);
+	for (i=7; i >= 0; i--) {
+
+		timer_delay(US2TMR(3));
+
+		/* raise clock */
+		i2c_set(i2c.scl);
+		timer_delay(US2TMR(5));
+
+		/* sample data */
+		if(GPIOREG->gplr & i2c.sda)
+			data |= 1<<i;
+
+		/* lower clock */
+		i2c_clear(i2c.scl);
+		timer_delay(US2TMR(3));
+	}
+
+	i2c_clear(i2c.sda);
+	return data;
+}
+
+/* generate I2C start condition */
+static int
+i2c_start(void)
+{
+	/* check that both scl and sda are high */
+	if ((GPIOREG->gplr & (i2c.sda | i2c.scl)) != (i2c.sda | i2c.scl)) 
+		print("I2C: Bus not clear when attempting start condition\n");
+
+	i2c_clear(i2c.sda);			/* lower sda */
+	timer_delay(US2TMR(5));
+
+	i2c_clear(i2c.scl);			/* lower scl */
+	timer_delay(US2TMR(3));
+
+	return 1;
+}
+
+/* generate I2C stop condition */	
+static int
+i2c_stop(void)
+{
+	/* clock is low, data is low */
+	timer_delay(US2TMR(3));
+
+	i2c_set(i2c.scl);
+	timer_delay(US2TMR(5));
+
+	i2c_set(i2c.sda);
+
+	timer_delay(MS2TMR(1));		/* ensure separation between commands */
+
+	return 1;
+}
+
+/*
+ * external I2C interface
+ */
+
+/* write a byte over the i2c bus */
+int
+i2c_write_byte(uchar addr, uchar data)
+{
+	int rc = 0;
+
+	ilock(&i2c);
+	if(i2c_start() < 0)			/* start condition */
+		rc = -1;
+
+	i2c_putbyte(addr & 0xfe);		/* address byte (LSB = 0 -> write) */
+
+	if (i2c_getack() < 0)			/* get ack */
+		rc = -2;
+
+	i2c_putbyte(data);			/* data byte */
+
+	if (i2c_getack() < 0)			/* get ack */
+		rc = -3;
+
+	if (i2c_stop() < 0)
+		rc = -4;			/* stop condition */
+	iunlock(&i2c);
+
+	return rc;
+}
+
+/* read a byte over the i2c bus */
+int
+i2c_read_byte(uchar addr, uchar *data)
+{
+	int rc = 0;
+
+	ilock(&i2c);
+	if(i2c_start() < 0)			/* start condition */
+		rc = -1;
+
+	i2c_putbyte(addr | 0x01);		/* address byte (LSB = 1 -> read) */
+
+	if(i2c_getack() < 0)			/* get ack */
+		rc = -2;
+
+	*data = i2c_getbyte();			/* data byte */
+
+	i2c_putack();				/* put ack */
+
+	if (i2c_stop() < 0) 			/* stop condition */
+		rc = -4;
+	iunlock(&i2c);
+
+	return rc;
+}
+
+void
+i2c_reset(void)
+{
+	/* initialise bitmasks */
+	i2c.sda = (1 << gpio_i2c_sda);
+	i2c.scl = (1 << gpio_i2c_scl);
+	
+	/* ensure that both clock and data are high */
+	i2c_set(i2c.sda);
+	i2c_set(i2c.scl);
+	timer_delay(MS2TMR(5));
+}
+
+
+/*
+ * external pin set/clear interface
+ */
+uchar i2c_iactl[2] = { 0xff, 0xff };		/* defaults overridden in arch?????.c */
+
+int
+i2c_setpin(int b)
+{
+	int i = b>>3;
+
+	ilock(&i2c);
+	i2c_iactl[i] |= (1 << (b&7));
+	iunlock(&i2c);
+	return i2c_write_byte(0x40 | (i << 1), i2c_iactl[i]);
+}
+
+int
+i2c_clrpin(int b)
+{
+	int i = b>>3;
+
+	ilock(&i2c);
+	i2c_iactl[i] &= ~(1 << (b&7));
+	iunlock(&i2c);
+	return i2c_write_byte(0x40 | (i << 1), i2c_iactl[i]);
+}
+
+int
+i2c_getpin(int b)
+{
+	return (i2c_iactl[(b>>3)&1] & (1<<(b&7))) != 0;
+}
--- /dev/null
+++ b/os/sa1110/l.s
@@ -1,0 +1,530 @@
+#include "mem.h"
+
+/*
+ * Entered here from the boot loader with
+ *	supervisor mode, interrupts disabled;
+ *	MMU, IDC and WB disabled.
+ */
+
+TEXT _startup(SB), $-4
+	MOVW		$setR12(SB), R12 	/* static base (SB) */
+	MOVW		$(PsrDirq|PsrDfiq|PsrMsvc), R1	/* ensure SVC mode with interrupts disabled */
+	MOVW		R1, CPSR
+	MOVW		$(MACHADDR+BY2PG-4), R13	/* stack; 4 bytes for link */
+
+	BL		main(SB)
+dead:
+	B	dead
+	BL	_div(SB)			/* hack to get _div etc loaded */
+
+TEXT	getcpuid(SB), $-4
+	MRC		CpMMU, 0, R0, C(CpCPUID), C(0)
+	RET
+
+TEXT mmugetctl(SB), $-4
+	MRC		CpMMU, 0, R0, C(CpControl), C(0)
+	RET	
+
+TEXT	mmugetdac(SB), $-4
+	MRC		CpMMU, 0, R0, C(CpDAC), C(0)
+	RET
+
+TEXT	mmugetfar(SB), $-4
+	MRC		CpMMU, 0, R0, C(CpFAR), C(0)
+	RET
+
+TEXT	mmugetfsr(SB), $-4
+	MRC		CpMMU, 0, R0, C(CpFSR), C(0)
+	RET
+
+TEXT	mmuputdac(SB), $-4
+	MCR		CpMMU, 0, R0, C(CpDAC), C(0)
+	RET
+
+TEXT	mmuputfsr(SB), $-4
+	MCR		CpMMU, 0, R0, C(CpFSR), C(0)
+	RET
+
+TEXT	mmuputttb(SB), $-4
+	MCR		CpMMU, 0, R0, C(CpTTB), C(0)
+	RET
+
+TEXT mmuputctl(SB), $-4
+	MCR		CpMMU, 0, R0, C(CpControl), C(0)
+	MOVW		R0, R0
+	MOVW		R0, R0
+	RET	
+
+TEXT tlbinvalidateall(SB), $-4
+	MCR		CpMMU, 0, R0, C(CpTLBops), C(7)
+	RET
+
+TEXT tlbinvalidate(SB), $-4
+	MCR		CpMMU, 0, R0, C(CpTLBops), C(7), 1
+	RET
+
+TEXT mmuenable(SB), $-4
+
+	MOVW	$1, R1
+	MCR	CpMMU, 0, R1, C(CpDAC), C(3)	/* set domain 0 to client */
+
+	/* disable and flush all caches and TLB's before (re-)enabling MMU */
+	MOVW	$(CpCi32 | CpCd32 | (1<<6) | CpCsystem), R1
+	MRC		CpMMU, 0, R1, C(CpControl), C(0)
+	MOVW	$0, R1				/* disable everything */
+	MCR	CpMMU, 0, R1, C(CpCacheCtl), C(7), 0	/* Flush I&D Caches */
+	MCR	CpMMU, 0, R1, C(CpCacheCtl), C(10), 4	/* drain write buffer */
+	MCR	CpMMU, 0, R1, C(CpTLBops), C(7), 0	/* Flush I&D TLB */
+	MCR	CpMMU, 0, R1, C(CpRBops), C(0), 0	/* Flush Read Buffer */
+
+	/* enable desired mmu mode (R0) */
+	MCR	CpMMU, 0, R0, C(1), C(0)
+	MOVW	R0, R0
+	MOVW	R0, R0
+	MOVW	R0, R0
+	MOVW	R0, R0
+	RET				/* start running in remapped area */
+
+TEXT setr13(SB), $-4
+	MOVW		4(FP), R1
+
+	MOVW		CPSR, R2
+	BIC		$PsrMask, R2, R3
+	ORR		R0, R3
+	MOVW		R3, CPSR
+
+	MOVW		R13, R0
+	MOVW		R1, R13
+
+	MOVW		R2, CPSR
+	RET
+
+TEXT vectors(SB), $-4
+	MOVW	0x18(R15), R15			/* reset */
+	MOVW	0x18(R15), R15			/* undefined */
+	MOVW	0x18(R15), R15			/* SWI */
+	MOVW	0x18(R15), R15			/* prefetch abort */
+	MOVW	0x18(R15), R15			/* data abort */
+	MOVW	0x18(R15), R15			/* reserved */
+	MOVW	0x18(R15), R15			/* IRQ */
+	MOVW	0x18(R15), R15			/* FIQ */
+
+TEXT vtable(SB), $-4
+	WORD	$_vsvc(SB)			/* reset, in svc mode already */
+	WORD	$_vund(SB)			/* undefined, switch to svc mode */
+	WORD	$_vsvc(SB)			/* swi, in svc mode already */
+	WORD	$_vpab(SB)			/* prefetch abort, switch to svc mode */
+	WORD	$_vdab(SB)			/* data abort, switch to svc mode */
+	WORD	$_vsvc(SB)			/* reserved */
+	WORD	$_virq(SB)			/* IRQ, switch to svc mode */
+	WORD	$_vfiq(SB)			/* FIQ, switch to svc mode */
+
+TEXT _vund(SB), $-4			
+	MOVM.DB		[R0-R3], (R13)
+	MOVW		$PsrMund, R0
+	B		_vswitch
+
+TEXT _vsvc(SB), $-4				
+	MOVW.W		R14, -4(R13)
+	MOVW		CPSR, R14
+	MOVW.W		R14, -4(R13)
+	BIC		$PsrMask, R14
+	ORR		$(PsrDirq|PsrDfiq|PsrMsvc), R14
+	MOVW		R14, CPSR
+	MOVW		$PsrMsvc, R14
+	MOVW.W		R14, -4(R13)
+	B		_vsaveu
+
+TEXT _vpab(SB), $-4			
+	MOVM.DB		[R0-R3], (R13)
+	MOVW		$PsrMabt, R0
+	B		_vswitch
+
+TEXT _vdab(SB), $-4	
+	MOVM.DB		[R0-R3], (R13)
+	MOVW		$(PsrMabt+1), R0
+	B		_vswitch
+
+TEXT _vfiq(SB), $-4				/* FIQ */
+	MOVM.DB		[R0-R3], (R13)
+	MOVW		$PsrMfiq, R0
+	B		_vswitch
+
+TEXT _virq(SB), $-4				/* IRQ */
+	MOVM.DB		[R0-R3], (R13)
+	MOVW		$PsrMirq, R0
+
+_vswitch:					/* switch to svc mode */
+	MOVW		SPSR, R1
+	MOVW		R14, R2
+	MOVW		R13, R3
+
+	MOVW		CPSR, R14
+	BIC		$PsrMask, R14
+	ORR		$(PsrDirq|PsrDfiq|PsrMsvc), R14
+	MOVW		R14, CPSR
+
+	MOVM.DB.W 	[R0-R2], (R13)
+	MOVM.DB	  	(R3), [R0-R3]
+
+_vsaveu:						/* Save Registers */
+	MOVW.W		R14, -4(R13)			/* save link */
+	MCR		CpMMU, 0, R0, C(0), C(0), 0	
+
+	SUB		$8, R13
+	MOVM.DB.W 	[R0-R12], (R13)
+
+	MOVW		R0, R0				/* gratuitous noop */
+
+	MOVW		$setR12(SB), R12		/* static base (SB) */
+	MOVW		R13, R0				/* argument is ureg */
+	SUB		$8, R13				/* space for arg+lnk*/
+	BL		trap(SB)
+
+_vrfe:							/* Restore Regs */
+	MOVW		CPSR, R0			/* splhi on return */
+	ORR		$(PsrDirq|PsrDfiq), R0, R1
+	MOVW		R1, CPSR
+	ADD		$(8+4*15), R13		/* [r0-R14]+argument+link */
+	MOVW		(R13), R14			/* restore link */
+	MOVW		8(R13), R0
+	MOVW		R0, SPSR
+	MOVM.DB.S 	(R13), [R0-R14]		/* restore user registers */
+	MOVW		R0, R0				/* gratuitous nop */
+	ADD		$12, R13		/* skip saved link+type+SPSR*/
+	RFE					/* MOVM.IA.S.W (R13), [R15] */
+	
+TEXT splhi(SB), $-4					
+	MOVW		CPSR, R0
+	ORR		$(PsrDirq), R0, R1
+	MOVW		R1, CPSR
+	MOVW	$(MACHADDR), R6
+	MOVW	R14, (R6)	/* m->splpc */
+	RET
+
+TEXT spllo(SB), $-4
+	MOVW		CPSR, R0
+	BIC		$(PsrDirq|PsrDfiq), R0, R1
+	MOVW		R1, CPSR
+	RET
+
+TEXT splx(SB), $-4
+	MOVW	$(MACHADDR), R6
+	MOVW	R14, (R6)	/* m->splpc */
+
+TEXT splxpc(SB), $-4
+	MOVW		R0, R1
+	MOVW		CPSR, R0
+	MOVW		R1, CPSR
+	RET
+
+TEXT spldone(SB), $-4
+	RET
+
+TEXT islo(SB), $-4
+	MOVW		CPSR, R0
+	AND		$(PsrDirq), R0
+	EOR		$(PsrDirq), R0
+	RET
+
+TEXT splfhi(SB), $-4					
+	MOVW		CPSR, R0
+	ORR		$(PsrDfiq|PsrDirq), R0, R1
+	MOVW		R1, CPSR
+	RET
+
+TEXT splflo(SB), $-4
+	MOVW		CPSR, R0
+	BIC		$(PsrDfiq), R0, R1
+	MOVW		R1, CPSR
+	RET
+
+TEXT getcpsr(SB), $-4
+	MOVW		CPSR, R0
+	RET
+
+TEXT getspsr(SB), $-4
+	MOVW		SPSR, R0
+	RET
+
+TEXT getcallerpc(SB), $-4
+	MOVW		0(R13), R0
+	RET
+
+TEXT _tas(SB), $-4
+	MOVW		R0, R1
+	MOVW		$0xDEADDEAD, R2
+	SWPW		R2, (R1), R0
+	RET
+
+TEXT setlabel(SB), $-4
+	MOVW		R13, 0(R0)		/* sp */
+	MOVW		R14, 4(R0)		/* pc */
+	MOVW		$0, R0
+	RET
+
+TEXT gotolabel(SB), $-4
+	MOVW		0(R0), R13		/* sp */
+	MOVW		4(R0), R14		/* pc */
+	MOVW		$1, R0
+	RET
+
+/*
+ * flush the whole icache
+ */
+TEXT icflushall(SB), $-4
+	MCR	 	CpMMU, 0, R0, C(CpCacheCtl), C(5), 0	
+	MOVW		R0,R0
+	MOVW		R0,R0
+	MOVW		R0,R0
+	MOVW		R0,R0
+	RET
+
+/*
+ * write back whole data cache and drain write buffer
+ */
+TEXT dcflushall(SB), $-4
+_dcflushall:
+	MOVW		$(DCFADDR), R0
+	ADD		$8192, R0, R1
+dcflushall1:
+	MOVW.P	CACHELINESZ(R0), R2
+	CMP		R1,R0
+	BNE		dcflushall1
+	MCR		CpMMU, 0, R0, C(CpCacheCtl), C(10), 4	/* drain write buffer */
+	MOVW		R0,R0								
+	MOVW		R0,R0
+	MOVW		R0,R0
+	MOVW		R0,R0
+	RET
+
+/*
+ * write back a given region and drain write buffer
+ */
+TEXT	dcflush(SB), $-4
+	MOVW	4(FP), R1
+	CMP		$(4*1024), R1
+	BGE		_dcflushall
+	ADD		R0, R1
+	BIC		$(CACHELINESZ-1), R0
+dcflush1:
+	MCR		CpMMU, 0, R0, C(CpCacheCtl), C(10), 1	/* clean entry */
+	ADD		$CACHELINESZ, R0
+	CMP		R1, R0
+	BLO	dcflush1
+	MCR		CpMMU, 0, R0, C(CpCacheCtl), C(10), 4	/* drain write buffer */
+	MOVW		R0,R0								
+	MOVW		R0,R0
+	MOVW		R0,R0
+	MOVW		R0,R0
+	RET
+
+/*
+ * write back mini data cache
+ */
+TEXT minidcflush(SB), $-4		
+	MOVW		$(MCFADDR), R0
+	ADD		$(16*CACHELINESZ), R0, R1
+
+wbbflush:
+	MOVW.P	CACHELINESZ(R0), R2
+	CMP		R1,R0
+	BNE		wbbflush
+	MCR		CpMMU, 0, R0, C(CpCacheCtl), C(10), 4	/* drain write buffer */
+	MOVW		R0,R0								
+	MOVW		R0,R0
+	MOVW		R0,R0
+	MOVW		R0,R0
+	RET
+
+/*
+ * invalidate data caches (main and mini)
+ */
+TEXT dcinval(SB), $-4
+	MCR		CpMMU, 0, R0, C(CpCacheCtl), C(6), 0	
+	RET
+
+/* for devboot */
+TEXT	gotopc(SB), $-4
+	MOVW	R0, R1
+	MOVW	$0, R0
+	MOVW	R1, PC
+	RET
+
+/*
+ * See page 9-26 of the SA1110 developer's manual.
+ * trap copies this to a cache-aligned area.
+ */
+TEXT	_idlemode(SB), $-4
+	MOVW	$UCDRAMZERO, R1
+	MOVW	R0,R0
+	MOVW	R0,R0
+	MOVW	R0,R0
+	MOVW	R0,R0
+	MOVW	R0,R0
+	MOVW	R0,R0
+	MOVW	R0,R0
+	/* the following must be on a cache line boundary */
+	MCR		CpPWR, 0, R0, C(CpTest), C(0x2), 2	/* disable clock switching */
+	MOVW	(R1), R0	/* non-cacheable memory read */
+	MCR		CpPWR, 0, R0, C(CpTest), C(0x8), 2
+	MCR		CpPWR, 0, R0, C(CpTest), C(0x2), 1	/* enable clock switching */
+	RET
+
+/*
+ * the following code is considerably modified from the
+ * sleep code by nemo@gsyc.escet.urjc.es for Plan 9, but that's
+ * where it started.  in particular, there's no need to save regs in all modes,
+ * since here we're called from kernel main level (a kproc) so nothing is live;
+ * the only regs needed are the various R13s, but we have trap restore them on resume.
+ * similarly there's no need to save SPSR, CPSR, etc. (even CpPID isn't really needed here).
+ */
+
+#define MDREFR_k1db2	(1<<22)
+#define MDREFR_slfrsh	(1<<31)	/* self refresh */
+#define MDREFR_e1pin	(1<<20)
+#define MSC_rt		((3<<16)|(3<<0))
+#define MDCFNG_de	((3<<16)|(3<<0))	/* dram enable, banks (3 2), (1 0) */
+
+TEXT suspenditall(SB), $-4
+	MOVW.W	R14, -4(R13)
+	/* push mmu state on stack */
+	MRC		CpMMU, 0, R1, C(CpDAC), C(0)
+	MRC		CpMMU, 0, R2, C(CpTTB), C(0)
+	MRC		CpMMU, 0, R3, C(CpPID), C(0)
+	MRC		CpMMU, 0, R4, C(CpControl), C(0)
+	MOVM.DB.W	[R1-R4], (R13)
+	/* if pspr by convention held a stack pointer pointing to a pc we wouldn't need power_state */
+	MOVW	R13, power_state+0(SB)
+
+	BL	dcflushall(SB)
+	/* don't write DRAM after this */
+
+	MOVW	$PHYSPOWER, R3
+
+	/* put resume address in scratchpad for boot loader */
+	MOVW	$power_resume+0(SB), R2
+	MOVW	R2, 0x8(R3)	/* pspr */
+
+	/* disable clock switching */
+	MCR   	CpPWR, 0, R1, C(CpTest), C(0x2), 2
+
+	/* adjust mem timing first to avoid processor bug causing hang */
+	MOVW	$MDCNFG, R5
+	MOVW	0x1c(R5), R2
+	ORR	$(MDREFR_k1db2), R2
+	MOVW	R2, 0x1c(R5)
+
+	/* set PLL to lower speed w/ delay */
+	MOVW	$(120*206),R0
+l11:	SUB	$1,R0
+	BGT	l11
+	MOVW	$0, R2
+	MOVW	R2, 0x14(R3)	/* ppcr */
+	MOVW	$(120*206),R0
+l12:	SUB	$1,R0
+	BGT	l12
+
+	/*
+	 *  SA1110 fix for various suspend bugs in pre-B4 chips (err. 14-16, 18):
+	 * 	set up register values here for use in code below that is at most
+	 *	one cache line (32 bytes) long, to run without DRAM.
+	 */
+	/* 1. clear RT in MSCx (R1, R7, R8) without changing other bits */
+	MOVW	0x10(R5), R1	/* MSC0 */
+	BIC	$(MSC_rt), R1
+	MOVW	0x14(R5), R7	/* MSC1 */
+	BIC	$(MSC_rt), R7
+	MOVW	0x2c(R5), R8	/* MSC2 */
+	BIC	$(MSC_rt), R8
+	/* 2. clear DRI0-11 in MDREFR (R4) without changing other bits */
+	MOVW	0x1c(R5), R4
+	BIC	$(0xfff0), R4
+	/* 3. set SLFRSH in MDREFR (R6) without changing other bits */
+	ORR	$(MDREFR_slfrsh), R4, R6
+	/* 4. clear DE in MDCNFG (R9), and any other bits desired */
+	MOVW	0x0(R5), R9
+	BIC	$(MDCFNG_de), R9
+	/* 5. clear SLFRSH and E1PIN (R10), without changing other bits */
+	BIC	$(MDREFR_slfrsh), R4, R10
+	BIC	$(MDREFR_e1pin), R10
+	/* 6. force sleep mode in PMCR (R2) */
+	MOVW	$1,R2
+	MOVW	suspendcode+0(SB), R0
+	B	(R0)	/* off to do it */
+
+/*
+ * the following is copied by trap.c to a cache-aligned area (suspendcode),
+ * so that it can all run during disabling of DRAM
+ */
+TEXT _suspendcode(SB), $-4
+	/* 1: clear RT field of all MSCx registers */
+	MOVW	R1, 0x10(R5)
+	MOVW	R7, 0x14(R5)
+	MOVW	R8, 0x2c(R5)
+	/* 2: clear DRI field in MDREFR */
+	MOVW	R4, 0x1c(R5)
+	/* 3: set SLFRSH bit in MDREFR */
+	MOVW	R6, 0x1c(R5)
+	/* 4: clear DE bits in MDCFNG */
+	MOVW	R9, 0x0(R5)
+	/* 5: clear SLFRSH and E1PIN in MDREFR */
+	MOVW	R10, 0x1c(R5)
+	/* 6: suspend request */
+	MOVW	R2, 0x0(R3)	 /* pmcr */
+	B		0(PC)		/* wait for it */
+
+/*
+ * The boot loader comes here after the resume.
+ */
+TEXT power_resume(SB), $-4
+	MOVW	$(PsrDirq|PsrDfiq|PsrMsvc), R0
+	MOVW	R0, CPSR		/* svc mode, interrupts off */
+	MOVW	$setR12(SB), R12
+
+	/* flush caches */
+	MCR		CpMMU, 0, R0, C(CpCacheCtl), C(7), 0
+	/* drain prefetch */
+	MOVW	R0,R0						
+	MOVW	R0,R0
+	MOVW	R0,R0
+	MOVW	R0,R0
+	/* drain write buffer */
+	MCR		CpMMU, 0, R0, C(CpCacheCtl), C(10), 4
+	/* flush tlb */
+	MCR		CpMMU, 0, R0, C(CpTLBops), C(7)
+
+	/* restore state */
+	MOVW	power_state+0(SB), R13
+	MOVM.IA.W	(R13), [R1-R4]
+	MOVW.P	4(R13), R14
+
+	MCR		CpMMU, 0, R1, C(CpDAC), C(0x0)
+	MCR		CpMMU, 0, R2, C(CpTTB), C(0x0)
+	MCR		CpMMU, 0, R3, C(CpPID), C(0x0)
+	MCR		CpMMU, 0, R4, C(CpControl), C(0x0)	/* enable cache and mmu */
+	MOVW	R0,R0						
+	MOVW	R0,R0
+	MOVW	R0,R0
+	MOVW	R0,R0
+	/* flush i&d caches */
+	MCR		CpMMU, 0, R0, C(CpCacheCtl), C(7)
+	/* flush tlb */
+	MCR		CpMMU, 0, R0, C(CpTLBops), C(7)
+	/* drain prefetch */
+	MOVW	R0,R0						
+	MOVW	R0,R0
+	MOVW	R0,R0
+	MOVW	R0,R0
+	/* enable clock switching */
+	MCR   	CpPWR, 0, R1, C(CpTest), C(1), 2
+	RET
+
+	GLOBL	power_state+0(SB), $4
+
+/* for debugging sleep code: */
+TEXT fastreset(SB), $-4
+	MOVW	$PHYSRESET, R7
+	MOVW	$1, R1
+	MOVW	R1, (R7)
+	RET
--- /dev/null
+++ b/os/sa1110/l3gpio.c
@@ -1,0 +1,246 @@
+/*
+ * L3 emulation using GPIO pins
+ *
+ * from the Linux sa1100-uda1341.c,
+ * Copyright (c) 2000 Nicolas Pitre <nico@cam.org>
+ * Portions are Copyright (C) 2000 Lernout & Hauspie Speech Products, N.V.
+ *
+ * This program is free software; you can redistribute it and/or 
+ * modify it under the terms of the GNU General Public License.
+ *
+ * Modified by Vita Nuova 2001
+ *
+ */
+#include	"u.h"
+#include	"../port/lib.h"
+#include	"mem.h"
+#include	"dat.h"
+#include	"fns.h"
+#include	"../port/error.h"
+#include	"io.h"
+
+/*
+ * GPIO based L3 bus support.
+ *
+ * This provides control of Philips L3 type devices. 
+ * GPIO lines are used for clock, data and mode pins.
+ *
+ * Note: The L3 pins are shared with I2C devices. This should not present
+ * any problems as long as an I2C start sequence is not generated. This is
+ * defined as a 1->0 transition on the data lines when the clock is high.
+ * It is critical this code only allow data transitions when the clock
+ * is low. This is always legal in L3.
+ *
+ * The IIC interface requires the clock and data pin to be LOW when idle. We
+ * must make sure we leave them in this state.
+ *
+ * It appears the read data is generated on the falling edge of the clock
+ * and should be held stable during the clock high time.
+ */
+
+/* 
+ * L3 setup and hold times (expressed in us)
+ */
+enum {
+	L3DataSetupTime = 1, /* 190 ns */
+	L3DataHoldTime = 1, /*  30 ns */
+	L3ModeSetupTime = 1, /* 190 ns */
+	L3ModeHoldTime = 1, /* 190 ns */
+	L3ClockHighTime = 1, /* 250 ns (min is 64*fs, 35us @ 44.1 Khz) */
+	L3ClockLowTime = 1, /* 250 ns (min is 64*fs, 35us @ 44.1 Khz) */
+	L3HaltTime = 1, /* 190 ns */
+};
+
+/*
+ * Grab control of the IIC/L3 shared pins
+ */
+static void
+L3acquirepins(void)
+{
+	GpioReg *g = GPIOREG;
+	int s;
+
+	s = splhi();
+	g->gpsr = (L3Mode | L3Clock | L3Data);
+	g->gpdr |=  (L3Mode | L3Clock | L3Data);
+	splx(s);
+//	microdelay(2);
+}
+
+/*
+ * Release control of the IIC/L3 shared pins
+ */
+static void
+L3releasepins(void)
+{
+	GpioReg *g = GPIOREG;
+	int s;
+
+	s = splhi();
+	g->gpdr &= ~(L3Mode | L3Clock | L3Data);
+	splx(s);
+}
+
+/*
+ * Initialize the interface
+ */
+void
+L3init(void)
+{
+	GpioReg *g = GPIOREG;
+	int s;
+
+	s = splhi();
+	g->gafr &= ~(L3Data | L3Clock | L3Mode);
+	splx(s);
+	L3releasepins();
+}
+
+/*
+ * Send a byte. The mode line is set or pulsed based on the mode sequence
+ * count. The mode line is high on entry and exit. The mod line is pulsed
+ * before the second data byte and before ech byte thereafter.
+ */
+static void
+L3sendbyte(int data, int mode)
+{
+	int i;
+	GpioReg *g = GPIOREG;
+
+	switch(mode) {
+	case 0: /* Address mode */
+		g->gpcr = L3Mode;
+		break;
+	case 1: /* First data byte */
+		break;
+	default: /* Subsequent bytes */
+		g->gpcr = L3Mode;
+		microdelay(L3HaltTime);
+		g->gpsr = L3Mode;
+		break;
+	}
+
+	microdelay(L3ModeSetupTime);
+
+	for (i = 0; i < 8; i++){
+		microdelay(2);
+		/*
+		 * Send a bit. The clock is high on entry and on exit. Data is sent only
+		 * when the clock is low (I2C compatibility).
+		 */
+		g->gpcr = L3Clock;
+
+		if (data & (1<<i))
+			g->gpsr = L3Data;
+		else
+			g->gpcr = L3Data;
+
+		/* Assumes L3DataSetupTime < L3ClockLowTime */
+		microdelay(L3ClockLowTime);
+
+		g->gpsr = L3Clock;
+		microdelay(L3ClockHighTime);
+	}
+
+	if (mode == 0)  /* Address mode */
+		g->gpsr = L3Mode;
+
+	microdelay(L3ModeHoldTime);
+
+}
+
+/*
+ * Get a byte. The mode line is set or pulsed based on the mode sequence
+ * count. The mode line is high on entry and exit. The mod line is pulsed
+ * before the second data byte and before each byte thereafter. This
+ * function is never valid with mode == 0 (address cycle) as the address
+ * is always sent on the bus, not read.
+ */
+static int
+L3getbyte(int mode)
+{
+	int data = 0;
+	int i;
+	GpioReg *g = GPIOREG;
+
+	switch(mode) {
+	case 0: /* Address mode - never valid */
+		break;
+	case 1: /* First data byte */
+		break;
+	default: /* Subsequent bytes */
+		g->gpcr = L3Mode;
+		microdelay(L3HaltTime);
+		g->gpsr = L3Mode;
+		break;
+	}
+
+	microdelay(L3ModeSetupTime);
+
+	for (i = 0; i < 8; i++){
+		/*
+		 * Get a bit. The clock is high on entry and on exit. Data is read after
+		 * the clock low time has expired.
+		 */
+		g->gpcr = L3Clock;
+		microdelay(L3ClockLowTime);
+
+		if(g->gplr & L3Data)
+			data |= 1<<i;
+
+	 	g->gpsr = L3Clock;
+		microdelay(L3ClockHighTime);
+	}
+
+	microdelay(L3ModeHoldTime);
+
+	return data;
+}
+
+/*
+ * Write data to a device on the L3 bus. The address is passed as well as
+ * the data and length. The length written is returned. The register space
+ * is encoded in the address (low two bits are set and device address is
+ * in the upper 6 bits).
+ */
+int
+L3write(int addr, void *data, int len)
+{
+	int mode = 0;
+	int bytes = len;
+	uchar *b;
+
+	L3acquirepins();
+	L3sendbyte(addr, mode++);
+	for(b = data; --len >= 0;)
+		L3sendbyte(*b++, mode++);
+	L3releasepins();
+
+	return bytes;
+}
+
+/*
+ * Read data from a device on the L3 bus. The address is passed as well as
+ * the data and length. The length read is returned. The register space
+ * is encoded in the address (low two bits are set and device address is
+ * in the upper 6 bits).
+ */
+int
+L3read(int addr, void *data, int len)
+{
+	int mode = 0;
+	int bytes = len;
+	uchar *b;
+	int s;
+
+	L3acquirepins();
+	L3sendbyte(addr, mode++);
+	s = splhi();
+	GPIOREG->gpdr &= ~(L3Data);
+	splx(s);
+	for(b = data; --len >= 0;)
+		*b++ = L3getbyte(mode++);
+	L3releasepins();
+
+	return bytes;
+}
--- /dev/null
+++ b/os/sa1110/mmu.c
@@ -1,0 +1,140 @@
+#include "u.h"
+#include "../port/lib.h"
+#include "mem.h"
+#include "dat.h"
+#include "fns.h"
+
+/*
+ * return physical address corresponding to a given virtual address,
+ * or 0 if there is no such address
+ */
+ulong
+va2pa(void *v)
+{
+	int idx;
+	ulong pte, ste, *ttb;
+
+	idx = MmuL1x((ulong)v);
+	ttb = (ulong*)KTTB;
+	ste = ttb[idx];
+	switch(ste & MmuL1type) {
+	case MmuL1section:
+		return MmuSBA(ste)|((ulong)v & 0x000fffff);
+	case MmuL1page:
+		pte = ((ulong *)MmuPTBA(ste))[MmuL2x((ulong)v)]; 
+		switch(pte & 3) {
+		case MmuL2large:
+			return (pte & 0xffff0000)|((ulong)v & 0x0000ffff);
+		case MmuL2small:
+			return (pte & 0xfffff000)|((ulong)v & 0x00000fff);
+		}
+	}
+	return 0;
+}
+
+enum {
+	SectionPages = MmuSection/MmuSmallPage,
+	PtAlign = 1<<10,
+
+	MINICACHED = 0x10000000,
+};
+
+/* for debugging */
+void
+prs(char *s)
+{
+	for(; *s; s++)
+		uartputc(*s);
+}
+
+void
+pr16(ulong n)
+{
+	int i;
+
+	for(i=28; i>=0; i-=4)
+		uartputc("0123456789ABCDEF"[(n>>i)&0xF]);
+}
+
+void*
+mmuphysmap(ulong phys, ulong)
+{
+	ulong *ttb;
+	void *va;
+
+	ttb = (ulong*)KTTB;
+	va = KADDR(phys);
+	ttb[MmuL1x((ulong)va)] = phys | 0xC10 | MmuL1section;
+	return va;
+}
+
+/*
+ * Set a 1-1 map of virtual to physical memory, except:
+ *	doubly-map page0 at the alternative interrupt vector address,
+ * 	doubly-map physical memory at KZERO+256*MB as uncached but buffered, and
+ *	disable access to 0 (nil pointers).
+ */
+void
+mmuinit(void)
+{
+	int i;
+	ulong *ttb, *ptable, va;
+
+	ttb = (ulong*)KTTB;
+	for(i=0; i<MmuL1x(0x10000000); i++)
+		ttb[i] = 0;
+	for(; i < 0x1000; i++)
+		ttb[i] = (i<<20) | 0xC10 | MmuL1section;
+	for(va = KZERO; va < KZERO+64*MB; va += MB)
+		ttb[MmuL1x(va)] |= MmuWB | MmuIDC;	/* DRAM is cacheable */
+	for(i = 0; i < 64*MB; i += MB)
+		ttb[MmuL1x(UCDRAMZERO+i)] = (PHYSMEM0+i) | 0xC10 | MmuL1section;
+	/* TO DO: make the text read only */
+	for(va = KZERO; va < KZERO+64*MB; va += MB)
+		ttb[MmuL1x(va|MINICACHED)] = va | 0xC10  | MmuIDC | MmuL1section;	/* cached but unbuffered (thus minicache) for frame buffer */
+	ttb[MmuL1x(DCFADDR)] |= MmuIDC | MmuWB;	/* cached and buffered for cache writeback */
+	ttb[MmuL1x(MCFADDR)] |= MmuIDC;	/* cached and unbuffered for minicache writeback */
+	/* remap flash */
+	for(i=0; i<32*MB; i+=MB)
+		ttb[MmuL1x(FLASHMEM+i)] = (PHYSFLASH0+i) | 0xC10 | MmuL1section;	/* we'll make flash uncached for now */
+
+	/*
+	 * build page table for alternative vector page, mapping trap vectors in *page0
+	 */
+	ptable = xspanalloc(SectionPages*sizeof(*ptable), PtAlign, 0);
+	ptable[MmuL2x(AIVECADDR)] = PADDR(page0) | MmuL2AP(MmuAPsrw) | MmuWB | MmuIDC | MmuL2small;
+	ttb[MmuL1x(AIVECADDR)] = PADDR(ptable) | MmuL1page;
+	mmuputttb(KTTB);
+	mmuputdac(1);	/* client */
+	mmuenable(CpCaltivec | CpCIcache | CpCsystem | (1<<6) | CpCd32 | CpCi32 | CpCwb | CpCDcache | CpCmmu);
+}
+
+/*
+ * flush data in a given address range to memory
+ * and invalidate the region in the instruction cache.
+ */
+int
+segflush(void *a, ulong n)
+{
+	dcflush(a, n);
+	icflushall();	/* can't be more precise */
+	return 0;
+}
+
+/*
+ * map an address to cached but unbuffered memory
+ * forcing load allocations to the mini data cache.
+ * the address a must be in a region that is cache line aligned
+ * with a length that is a multiple of the cache line size
+ */
+void *
+minicached(void *a)
+{
+	if(conf.useminicache == 0)
+		return a;
+	/* must flush and invalidate any data lingering in main cache */
+	dcflushall();
+	minidcflush();
+	dcinval();
+	return (void*)((ulong)a | MINICACHED);
+}
--- /dev/null
+++ b/os/sa1110/sa1110break.c
@@ -1,0 +1,360 @@
+#include "u.h"
+#include "../port/lib.h"
+#include "mem.h"
+#include "dat.h"
+#include "fns.h"
+#include "ureg.h"
+#include "../port/error.h"
+
+//
+// from trap.c
+//
+extern int (*breakhandler)(Ureg *ur, Proc*);
+extern Instr BREAK;
+extern void portbreakinit(void);
+
+//
+// Instructions that can have the PC as a destination register
+//
+enum {
+	IADD = 1,
+	IBRANCH,
+	ILDM,
+	ILDR,
+	IMOV,
+
+	//
+	// These should eventually be implemented
+	//
+	IADC,
+	IAND,
+	IBIC,
+	IEOR,
+	ILDRT,
+	IMRS,
+	IMVN,
+	IORR,
+	IRSB,
+	IRSC,
+	ISBC,
+	ISUB,
+};
+
+static int	instrtype(Instr i);
+static ulong	iadd(Ureg *ur, Instr i);
+static ulong	ibranch(Ureg *ur, Instr i);
+static ulong	ildm(Ureg *ur, Instr i);
+static ulong	ildr(Ureg *ur, Instr i);
+static ulong	imov(Ureg *ur, Instr i);
+static ulong	shifterval(Ureg *ur, Instr i);
+static int	condpass(Instr i, ulong psr);
+static ulong	*address(Ureg *ur, Instr i);
+static ulong*	multiaddr(Ureg *ur, Instr i);
+static int	nbits(ulong v);
+
+#define COND_N(psr)	(((psr) >> 31) & 1)
+#define COND_Z(psr)	(((psr) >> 30) & 1)
+#define COND_C(psr)	(((psr) >> 29) & 1)
+#define COND_V(psr)	(((psr) >> 28) & 1)
+#define REG(i, a, b)	(((i) & BITS((a), (b))) >> (a))
+#define REGVAL(ur, r)	(*((ulong*)(ur) + (r)))
+#define LSR(v, s)	((ulong)(v) >> (s))
+#define ASR(v, s)	((long)(v) >> (s))
+#define ROR(v, s)	(LSR((v), (s)) | (((v) & ((1 << (s))-1)) << (32 - (s))))
+
+void
+machbreakinit(void)
+{
+	portbreakinit();
+	breakhandler = breakhit;
+}
+
+Instr
+machinstr(ulong addr)
+{
+	if (addr < KTZERO)
+		error(Ebadarg);
+	return *(Instr*)addr;
+}
+
+void
+machbreakset(ulong addr)
+{
+	if (addr < KTZERO)
+		error(Ebadarg);
+	*(Instr*)addr = BREAK;
+	segflush((void*)addr, sizeof(Instr));
+}
+
+void
+machbreakclear(ulong addr, Instr i)
+{
+	if (addr < KTZERO)
+		error(Ebadarg);
+	*(Instr*)addr = i;
+	segflush((void*)addr, sizeof(Instr));
+}
+
+//
+// Return the address of the instruction that will be executed after the
+// instruction at address ur->pc.
+//
+// This means decoding the instruction at ur->pc.
+//
+// In the simple case, the PC will simply be the address of the next
+// sequential instruction following ur->pc.
+//
+// In the complex case, the instruction is a branch of some sort, so the
+// value of the PC after the instruction must be computed by decoding
+// and simulating the instruction enough to determine the PC.
+//
+
+ulong
+machnextaddr(Ureg *ur)
+{
+	Instr i;
+	i = machinstr(ur->pc);
+	switch(instrtype(i)) {
+		case IADD:	return iadd(ur,i);
+		case IBRANCH:	return ibranch(ur,i);
+		case ILDM:	return ildm(ur,i);
+		case ILDR:	return ildr(ur,i);
+		case IMOV:	return imov(ur,i);
+
+		case IADC:
+		case IAND:
+		case IBIC:
+		case IEOR:
+		case ILDRT:
+		case IMRS:
+		case IMVN:
+		case IORR:
+		case IRSB:
+		case IRSC:
+		case ISBC:
+		case ISUB:
+			// XXX - Tad: unimplemented
+			//
+			// any of these instructions could possibly have the
+			// PC as Rd.  Eventually, these should all be
+			// checked just like the others.
+		default:
+			return ur->pc+4;
+	}
+
+	return 0;
+}
+
+static int
+instrtype(Instr i)
+{
+	if(i & BITS(26,27) == 0) {
+		switch((i >> 21) & 0xF) {
+			case 0:		return IAND;
+			case 1:		return IEOR;
+			case 2:		return ISUB;
+			case 3:		return IRSB;
+			case 4:		return IADD;
+			case 5:		return IADC;
+			case 6:		return ISBC;
+			case 7:		return IRSC;
+			case 0xD:	return IMOV;
+			case 0xC:	return IORR;
+			case 0xE:	return IBIC;
+			case 0xF:	return IMVN;
+		}
+		if(((i & BIT(25)|BITS(23,24)|BITS(20,21))) >> 20 == 0x10)
+			return IMRS;
+		return 0;
+	}
+
+	if(((i & BITS(27,25)|BIT(20)) >> 20) == 0x81) return ILDM;
+	if(((i & BITS(26,27)|BIT(22)|BIT(20)) >> 20) == 0x41) return ILDR;
+	if(((i & BITS(25,27)) >> 25) == 5) return IBRANCH;
+
+	return 0;
+}
+
+static ulong
+iadd(Ureg *ur, Instr i)
+{
+	ulong Rd = REG(i, 12, 15);
+	ulong Rn = REG(i, 16, 19);
+
+	if(Rd != 15 || !condpass(i, ur->psr))
+		return ur->pc+4;
+
+	return REGVAL(ur, Rn) + shifterval(ur, i);
+}
+
+static ulong
+ibranch(Ureg *ur, Instr i)
+{
+	if(!condpass(i, ur->psr))
+		return ur->pc+4;
+	return ur->pc + ((signed long)(i << 8) >> 6) + 8;
+}
+
+static ulong
+ildm(Ureg *ur, Instr i)
+{
+	if((i & BIT(15)) == 0)
+		return ur->pc+4;
+
+	return *(multiaddr(ur, i) + nbits(i & BITS(15, 0)));
+}
+
+static ulong
+ildr(Ureg *ur, Instr i)
+{
+	if(REG(i, 12, 19) != 15 || !condpass(i, ur->psr))
+		return ur->pc+4;
+
+	return *address(ur, i);
+}
+
+static ulong
+imov(Ureg *ur, Instr i)
+{
+	if(REG(i, 12, 15) != 15 || !condpass(i, ur->psr))
+		return ur->pc+4;
+
+	return shifterval(ur, i);
+}
+
+static int
+condpass(Instr i, ulong psr)
+{
+	uchar n = COND_N(psr);
+	uchar z = COND_Z(psr);
+	uchar c = COND_C(psr);
+	uchar v = COND_V(psr);
+
+	switch(LSR(i,28)) {
+		case 0:		return z;
+		case 1:		return !z;
+		case 2:		return c;
+		case 3:		return !c;
+		case 4:		return n;
+		case 5:		return !n;
+		case 6:		return v;
+		case 7:		return !v;
+		case 8:		return c && !z;
+		case 9:		return !c || z;
+		case 10:	return n == v;
+		case 11:	return n != v;
+		case 12:	return !z && (n == v);
+		case 13:	return z && (n != v);
+		case 14:	return 1;
+		case 15:	return 0;
+	}
+}
+
+static ulong
+shifterval(Ureg *ur, Instr i)
+{
+	if(i & BIT(25)) {					// IMMEDIATE
+		ulong imm = i & BITS(0,7);
+		ulong s = (i & BITS(8,11)) >> 7;  // this contains the * 2
+		return ROR(imm, s);
+	} else {
+		ulong Rm = REGVAL(ur, REG(i, 0, 3));
+		ulong s = (i & BITS(7,11)) >> 7;
+
+		switch((i & BITS(6,4)) >> 4) {
+		case 0: 					// LSL
+			return Rm << s;
+		case 1:						// LSLREG
+			s = REGVAL(ur, s >> 1) & 0xFF;
+			if(s >= 32) return 0;
+			return Rm << s;
+		case 2: 					// LSRIMM
+			return LSR(Rm, s);
+		case 3: 					// LSRREG
+			s = REGVAL(ur, s >> 1) & 0xFF;
+			if(s >= 32) return 0;
+			return LSR(Rm, s);
+		case 4:						// ASRIMM
+			if(s == 0) {
+				if(Rm & BIT(31) == 0)
+					return 0;
+				return 0xFFFFFFFF;
+			}
+			return ASR(Rm, s);
+		case 5:						// ASRREG
+			s = REGVAL(ur, s >> 1) & 0xFF;
+			if(s >= 32) {
+				if(Rm & BIT(31) == 0)
+					return 0;
+				return 0xFFFFFFFF;
+			}
+			return ASR(Rm, s);
+		case 6: 					// RORIMM
+			if(s == 0)
+				return (COND_C(ur->psr) << 31) | LSR(Rm, 1);
+			return ROR(Rm, s);
+		case 7: 					// RORREG
+			s = REGVAL(ur, s >> 1) & 0xFF;
+			if(s == 0 || (s & 0xF) == 0)
+				return Rm;
+			return ROR(Rm, s & 0xF);
+		}
+	}
+}
+
+static ulong*
+address(Ureg *ur, Instr i)
+{
+	ulong Rn = REGVAL(ur, REG(i, 16, 19));
+
+	if(i & BIT(24) == 0) 					// POSTIDX
+		return (ulong*)REGVAL(ur, Rn);
+	if(i & BIT(25) == 0) {					// OFFSET
+		if(i & BIT(23))
+			return (ulong*)(REGVAL(ur, Rn) + (i & BITS(0, 11)));
+		return (ulong*)(REGVAL(ur, Rn) - (i & BITS(0, 11)));
+	} else {						// REGOFF
+		ulong Rm = REGVAL(ur, REG(i, 0, 3));
+		ulong index = 0;
+		switch(i & BITS(5,6) >> 5) {
+		case 0:	index = Rm << ((i & BITS(7, 11)) >> 7);		break;
+		case 1:	index = LSR(Rm, ((i & BITS(7, 11)) >> 7));	break;
+		case 2:	index = ASR(Rm, ((i & BITS(7, 11)) >> 7));	break;
+		case 3:
+			if(i & BITS(7, 11) == 0)
+				index = (COND_C(ur->psr) << 31) | LSR(Rm, 1);
+			else
+				index = ROR(Rm, (i & BITS(7, 11)) >> 7);
+			break;
+		}
+		if(i & BIT(23))
+			return (ulong*)(Rn + index);
+		return (ulong*)(Rn - index);
+	}
+}
+
+static ulong*
+multiaddr(Ureg *ur, Instr i)
+{
+	ulong Rn = REGVAL(ur, REG(i, 16, 19));
+
+	switch((i >> 23) & 3) {
+		case 0: return (ulong*)(Rn - (nbits(i & BITS(0,15))*4)+4);
+		case 1:	return (ulong*)Rn;
+		case 2: return (ulong*)(Rn - (nbits(i & BITS(0,15))*4));
+		case 3: return (ulong*)(Rn + 4);
+	}
+}
+
+static int
+nbits(ulong v)
+{
+	int n = 0;
+	int i;
+	for(i = 0; i < 32; i++) {
+		if(v & 1)
+			++n;
+		v = LSR(v, 1);
+	}
+	return n;
+}
--- /dev/null
+++ b/os/sa1110/sa1110io.h
@@ -1,0 +1,500 @@
+typedef struct PCMconftab PCMconftab;
+typedef struct PCMmap	PCMmap;
+typedef struct PCMslot	PCMslot;
+
+/*
+ * physical device addresses are mapped to the same virtual ones,
+ * allowing the same addresses to be used with or without mmu.
+ */
+
+#define PCMCIAcard(n)	(PHYSPCMCIA0+((n)*PCMCIASIZE))
+#define PCMCIAIO(n)	(PCMCIAcard(n)+0x0)		/* I/O space */
+#define PCMCIAAttr(n)	(PCMCIAcard(n)+0x8000000) /* Attribute space*/
+#define PCMCIAMem(n)	(PCMCIAcard(n)+0xC000000) /* Memory space */
+
+#define INTRREG 	((IntrReg*)PHYSINTR)
+typedef struct IntrReg IntrReg;
+struct IntrReg {
+	ulong	icip;	/*  IRQ pending */
+	ulong	icmr;	/*  mask */
+	ulong	iclr;	/*  level */
+	ulong	iccr;	/*  control */
+	ulong	icfp;	/*  FIQ pending */
+	ulong	rsvd[3];
+	ulong	icpr;	/*  pending */
+};
+
+#define GPIObit(n)	(n)			/* GPIO Edge Detect bits */
+#define LCDbit		(12)			/* LCD Service Request */
+#define UDCbit		(13)			/* UDC Service Request */
+#define SDLCbit		(14)			/* SDLC Service Request */
+#define UARTbit(n)	(15+((n)-1))		/* UART Service Request */
+#define HSSPbit		(16)			/* HSSP Service Request */
+#define MCPbit		(18)			/* MCP Service Request */
+#define SSPbit		(19)			/* SSP Serivce Request */
+#define DMAbit(chan)	(20+(chan))		/* DMA channel Request */
+#define OSTimerbit(n)	(26+(n))		/* OS Timer Request */
+#define RTCticbit	(30)			/* One Hz tic occured */
+#define RTCalarmbit	(31)			/* RTC = alarm register */
+#define MaxIRQbit	31			/* Maximum IRQ */
+#define MaxGPIObit	27			/* Maximum GPIO */
+
+#define GPIOREG		((GpioReg*)PHYSGPIO)
+typedef struct GpioReg GpioReg;
+struct GpioReg {
+	ulong gplr;
+	ulong gpdr;
+	ulong gpsr;
+	ulong gpcr;
+	ulong grer;
+	ulong gfer;
+	ulong gedr;
+	ulong gafr;
+};
+
+enum {
+	/* GPIO alternative functions if gafr bit set (see table on page 9-9) */
+	GPIO_32KHZ_OUT_o = 1<<27,	/* raw 32.768kHz oscillator output */
+	GPIO_RCLK_OUT_o = 1<<26,	/* internal clock/2 (must also set TUCR) */
+	GPIO_RTC_clock_o = 1<<25,	/* real time clock out */
+	GPIO_TREQB_i = 1<<23,	/* TIC request B */
+	GPIO_TREQA_i = 1<<22,	/* TIC request A (or MBREQ) */
+	GPIO_TICK_ACK_o = 1<<21,	/* TIC ack (or MBGNT), when output */
+	GPIO_MCP_CLK_i = 1<<21,	/* MCP clock in, when input */
+	GPIO_UART_SCLK3_i = 1<<20,	/* serial port 3 UART sample clock input */
+	GPIO_SSP_CLK_i = 1<<19,	/* serial port 2 SSP sample clock input */
+	GPIO_UART_SCLK1_i = 1<<18,	/* serial port 1 UART sample clock input */
+	GPIO_GPCLK_OUT_o = 1<<16,	/* serial port 1 general-purpose clock out */
+	GPIO_UART_RXD_i = 1<<15,	/* serial port 1 UART receive */
+	GPIO_UART_TXD_o = 1<<14,	/* serial port 1 UART transmit */
+	GPIO_SSP_SFRM_o = 1<<13,	/* SSP frame clock out */
+	GPIO_SSP_SCLK_o = 1<<12,	/* SSP serial clock out */
+	GPIO_SSP_RXD_i = 1<<11,	/* SSP receive */
+	GPIO_SSP_TXD_o = 1<<10,	/* SSP transmit */
+	GPIO_LDD8_15_o = 0xFF<<2,	/* high-order LCD data (bits 8-15) */
+	GPIO_LDD15_o = 1<<9,
+	GPIO_LDD14_o = 1<<8,
+	GPIO_LDD13_o = 1<<7,
+	GPIO_LDD12_o = 1<<6,
+	GPIO_LDD11_o = 1<<5,
+	GPIO_LDD10_o = 1<<4,
+	GPIO_LDD9_o = 1<<3,
+	GPIO_LDD8_o = 1<<2,
+};
+
+#define RTCREG		((RtcReg*)PHYSRTC)
+typedef struct RtcReg RtcReg;
+struct RtcReg {
+	ulong	rtar;	/*  alarm */
+	ulong	rcnr;	/*  count */
+	ulong	rttr;	/*  trim */
+	ulong	rsvd;
+	ulong	rtsr;	/*  status */
+};
+
+#define OSTMRREG	((OstmrReg*)PHYSOSTMR)
+typedef struct OstmrReg OstmrReg;
+struct OstmrReg {
+	ulong	osmr[4];	/*  match */
+	ulong	oscr;		/*  counter */
+	ulong	ossr;		/*  status */
+	ulong	ower;		/*  watchdog */
+	ulong	oier;		/*  interrupt enable */
+};
+
+#define PMGRREG		((PmgrReg*)PHYSPOWER)
+typedef struct PmgrReg PmgrReg;
+struct PmgrReg {
+	ulong	pmcr;	/*  ctl register */
+	ulong	pssr;		/*  sleep status */
+	ulong	pspr;		/*  scratch pad */
+	ulong	pwer;	/*  wakeup enable */
+	ulong	pcfr;		/*  general conf */
+	ulong	ppcr;	/*  PLL configuration */
+	ulong	pgsr;		/*  GPIO sleep state */
+	ulong	posr;		/*  oscillator status */
+};
+
+enum
+{
+	/* page 9-35 to 40 */
+	PCFR_opde	= 1<<0,	/* oscillator powers down in sleep */
+	PCFR_fp		= 1<<1,	/* float pcmcia */
+	PCFR_fs		= 1<<2,	/* float static memory */
+	PCFR_fo		= 1<<3,	/* force 32k oscillator on */
+
+	PWER_rtc		= 1<<31,	/* wakeup by RTC alarm */
+
+	PSSR_sss		= 1<<0,	/* software sleep status */
+	PSSR_bfs		= 1<<1,	/* battery fault status */
+	PSSR_vfs		= 1<<2,	/* VDD fault status */
+	PSSR_dh		= 1<<3,	/* DRAM control held */
+	PSSR_ph		= 1<<4,	/* peripheral control hold */
+};
+
+#define RESETREG	((ResetReg*)PHYSRESET)
+typedef struct ResetReg ResetReg;
+struct ResetReg {
+	ulong	rsrr;		/*  software reset */
+	ulong	rcsr;		/*  status */
+	ulong	tucr;		/*  reserved for test */
+};
+
+#define MEMCFGREG	((MemcfgReg*)PHYSMEMCFG)
+typedef struct MemcfgReg MemcfgReg;
+struct MemcfgReg {
+	ulong	mdcnfg;		/*  DRAM config */
+	ulong	mdcas0[3];	/* dram banks 0/1 */
+	ulong	msc0;		/* static memory or devices */
+	ulong	msc1;
+	ulong	mecr;		/* expansion bus (pcmcia, CF) */
+	ulong	mdrefr;		/* dram refresh */
+	ulong	mdcas2[3];	/* dram banks 2/3 */
+	ulong	msc2;		/* static memory or devices */
+	ulong	smcnfg;		/* SMROM config */
+};
+
+#define DMAREG(n)	((DmaReg*)(PHYSDMA+0x20*(n)))
+typedef struct DmaReg DmaReg;
+struct DmaReg {
+	ulong	ddar;	/*  DMA device address */
+	ulong	dcsr_s;	/*  set  */
+	ulong	dcsr_c; /*  clear  */
+	ulong	dcsr;   /*  read */
+	struct {
+		ulong	start;
+		ulong	count;
+	} buf[2];
+};
+
+#define LCDREG		((LcdReg*)PHYSLCD)
+typedef struct LcdReg LcdReg;
+struct LcdReg {
+	ulong	lccr0;	/*  control 0 */
+	ulong	lcsr;		/*  status  */
+	ulong	rsvd[2];
+	ulong	dbar1;	/*  DMA chan 1, base */
+	ulong	dcar1;	/*  DMA chan 1, count */
+	ulong	dbar2;	/*  DMA chan 2, base */
+	ulong	dcar2;	/*  DMA chan 2, count */
+	ulong	lccr1;	/*  control 1 */
+	ulong	lccr2;	/*  control 2 */
+	ulong	lccr3;	/*  control 3 */
+};
+
+/* Serial devices:
+ *	0	USB		Serial Port 0
+ *	1	UART	Serial Port 1
+ *	2	SDLC		"
+ *	3	UART	Serial Port 2 (eia1)
+ *	4	ICP/HSSP		"
+ *	5	ICP/UART	Serial Port 3 (eia0)
+ *	6	MPC		Serial Port 4
+ *	7	SSP			"
+ */ 
+
+#define USBREG	((UsbReg*)PHYSUSB)
+typedef struct UsbReg UsbReg;
+struct UsbReg {
+	ulong	udccr;	/*  control */
+	ulong	udcar;	/*  address */
+	ulong	udcomp;	/*  out max packet */
+	ulong	udcimp;	/*  in max packet */
+	ulong	udccs0;	/*  endpoint 0 control/status */
+	ulong	udccs1;	/*  endpoint 1(out) control/status */
+	ulong	udccs2;	/*  endpoint 2(int) control/status */
+	ulong	udcd0;	/*  endpoint 0 data register */
+	ulong	udcwc;	/*  endpoint 0 write control register */
+	ulong	rsvd1;
+	ulong	udcdr;	/*  transmit/receive data register (FIFOs) */
+	ulong	rsvd2;
+	ulong	dcsr;	/*  status/interrupt register */
+};
+
+#define GPCLKREG	((GpclkReg*)PHYSGPCLK)
+typedef struct GpclkReg GpclkReg;
+struct GpclkReg {
+	ulong	gpclkr0;
+	ulong	rsvd[2];
+	ulong	gpclkr1;
+	ulong	gpclkr2;
+};
+
+/* UARTs 1, 2, 3 are mapped to serial devices 1, 3, and 5 */
+#define UARTREG(n)	((UartReg*)(PHYSSERIAL(2*(n)-1)))
+typedef struct UartReg UartReg;
+struct UartReg {
+	ulong	utcr0;	/*  control 0 (bits, parity, clocks) */
+	ulong	utcr1;	/*  control 1 (bps div hi) */
+	ulong	utcr2;	/*  control 2 (bps div lo) */
+	ulong	utcr3;	/*  control 3 */
+	ulong	utcr4;	/*  control 4 (only serial port 2 (device 3)) */
+	ulong	utdr;		/*  data */
+	ulong	rsvd;
+	ulong	utsr0;	/*  status 0 */
+	ulong	utsr1;	/*  status 1 */
+};
+
+#define HSSPREG		((HsspReg*)(0x80040060))
+typedef struct HsspReg HsspReg;
+struct HsspReg {
+	ulong	hscr0;	/*  control 0 */
+	ulong	hscr1;	/*  control 1 */
+	ulong	rsvd1;
+	ulong	hsdr;		/*  data */
+	ulong	rsvd2;
+	ulong	hssr0;	/*  status 0 */
+	ulong	hssr1;	/*  status 1 */
+};
+
+#define MCPREG		((McpReg*)(PHYSMCP))
+typedef struct McpReg McpReg;
+struct McpReg {
+	ulong	mccr;
+	ulong	rsvd1;
+	ulong	mcdr0;
+	ulong	mcdr1;
+	ulong	mcdr2;
+	ulong	rsvd2;
+	ulong	mcsr;
+};
+
+enum {
+	MCCR_M_LBM= 0x800000,
+	MCCR_M_ARM= 0x400000,
+	MCCR_M_ATM= 0x200000,
+	MCCR_M_TRM= 0x100000,
+	MCCR_M_TTM= 0x080000,
+	MCCR_M_ADM= 0x040000,
+	MCCR_M_ECS= 0x020000,
+	MCCR_M_MCE= 0x010000,
+	MCCR_V_TSD= 8,
+	MCCR_V_ASD= 0,
+
+	MCDR2_M_nRW= 0x010000,
+	MCDR2_V_RN= 17,
+
+	MCSR_M_TCE= 0x8000,
+	MCSR_M_ACE= 0X4000,
+	MCSR_M_CRC= 0x2000,
+	MCSR_M_CWC= 0x1000,
+	MCSR_M_TNE= 0x0800,
+	MCSR_M_TNF= 0x0400,
+	MCSR_M_ANE= 0x0200,
+	MCSR_M_ANF= 0x0100,
+	MCSR_M_TRO= 0x0080,
+	MCSR_M_TTU= 0x0040,
+	MCSR_M_ARO= 0x0020,
+	MCSR_M_ATU= 0x0010,
+	MCSR_M_TRS= 0x0008,
+	MCSR_M_TTS= 0x0004,
+	MCSR_M_ARS= 0x0002,
+	MCSR_M_ATS= 0x0001,
+};
+
+#define SSPREG		((SspReg*)PHYSSSP)
+typedef struct SspReg SspReg;
+struct SspReg {
+	ulong	sscr0;	/*  control 0 */
+	ulong	sscr1;	/*  control 1 */
+	ulong	rsvd1;
+	ulong	ssdr;	/*  data */
+	ulong	rsvd2;
+	ulong	sssr;	/*  status */
+};
+
+enum {
+	SSCR0_V_SCR= 0x08,
+	SSCR0_V_SSE= 0x07,
+	SSCR0_V_ECS= 0x06,
+	SSCR0_V_FRF= 0x04,
+
+	SSPCR0_M_DSS= 0x0000000F,
+	SSPCR0_M_FRF= 0x00000030,
+	SSPCR0_M_SSE= 0x00000080,
+	SSPCR0_M_SCR= 0x0000FF00,
+	SSPCR0_V_DSS= 0,
+	SSPCR0_V_FRF= 4,
+	SSPCR0_V_SSE= 7,
+	SSPCR0_V_SCR= 8,
+
+	SSPCR1_M_RIM= 0x00000001,
+	SSPCR1_M_TIN= 0x00000002,
+	SSPCR1_M_LBM= 0x00000004,
+	SSPCR1_V_RIM= 0,
+	SSPCR1_V_TIN= 1,
+	SSPCR1_V_LBM= 2,
+
+	SSPSR_M_TNF= 0x00000002,
+	SSPSR_M_RNE= 0x00000004,
+	SSPSR_M_BSY= 0x00000008,
+	SSPSR_M_TFS= 0x00000010,
+	SSPSR_M_RFS= 0x00000020,
+	SSPSR_M_ROR= 0x00000040,
+	SSPSR_V_TNF= 1,
+	SSPSR_V_RNE= 2,
+	SSPSR_V_BSY= 3,
+	SSPSR_V_TFS= 4,
+	SSPSR_V_RFS= 5,
+	SSPSR_V_ROR= 6,
+};
+
+#define PPCREG		((PpcReg*)PHYSPPC)
+typedef struct PpcReg PpcReg;
+struct PpcReg {
+	ulong	ppdr;	/*  pin direction */
+	ulong	ppsr;		/*  pin state */
+	ulong	ppar;	/*  pin assign */
+	ulong	psdr;		/*  sleep mode */
+	ulong	ppfr;		/*  pin flag reg */
+	uchar	rsvd[0x1c]; /*  pad to 0x30 */
+	ulong	mccr1;	/*  MCP control register 1 */
+};
+
+enum {
+	/* ppdr and ppsr: =0, pin is general-purpose input; =1, pin is general-purpose output (11-168)*/
+	PPC_LDD0_7=	0xFF<<0,	/* LCD data pins 0 to 7 */
+	PPC_L_PCLK=	1<<8,	/* LCD pixel clock */
+	PPC_L_LCLK=	1<<9,	/* LCD line clock */
+	PPC_L_FCLK=	1<<10,	/* LCD frame clock */
+	PPC_L_BIAS=	1<<11,	/* LCD AC bias */
+	PPC_TXD1=	1<<12,	/* serial port 1 UART transmit */
+	PPC_RXD1=	1<<13,	/* serial port 1 UART receive */
+	PPC_TXD2=	1<<14,	/* serial port 2 IPC transmit */
+	PPC_RXD2=	1<<15,	/* serial port 2 IPC receive */
+	PPC_TXD3=	1<<16,	/* serial port 3 UART transmit */
+	PPC_RXD3=	1<<17,	/* serial port 3 UART receive */
+	PPC_TXD4=	1<<18,	/* serial port 4 MCP/SSP transmit */
+	PPC_RXD4=	1<<19,	/* serial port 4 MCP/SSP receive */
+	PPC_SCLK=	1<<20,	/* serial port 4 MCP/SSP serial clock */
+	PPC_SFRM=	1<<21,	/* serial port 4 MCP/SSP frame clock */
+
+	PPAR_UPR=	1<<12,	/* =1, serial port 1 GPCLK/UART pins reassigned */
+	PPAR_SPR=	1<<18,	/* =1, SSP pins reassigned */
+};
+
+/*
+ *	Irq Bus goo
+ */
+
+enum {
+	BusCPU= 1,
+	BusGPIOfalling= 2,	/* falling edge */
+	BusGPIOrising = 3,	/* rising edge */
+	BusGPIOboth = 4,	/* both edges */
+	BusMAX= 4,
+	BUSUNKNOWN= -1,
+};
+
+enum {
+	/*  DMA configuration parameters */
+
+	 /*  DMA Direction */
+	DmaOUT=		0,
+	DmaIN=		1,
+
+	 /*  dma endianess */
+	DmaLittle=	0,
+	DmaBig=		1,
+
+	 /*  dma devices */
+	DmaUDC=		0,
+	DmaSDLC=	2,
+	DmaUART0=	4,
+	DmaHSSP=	6,
+	DmaUART1=	7,	/*  special case (is really 6) */
+	DmaUART2=	8,
+	DmaMCPaudio=	10,
+	DmaMCPtelecom=	12,
+	DmaSSP=		14,
+};
+
+/*
+ *	Interface to platform-specific PCMCIA signals, in arch*.c
+ */
+enum {
+	/* argument to pcmpin() */
+	PCMready,
+	PCMeject,
+	PCMstschng,
+};
+
+/*
+ * PCMCIA structures known by both port/cis.c and the pcmcia driver
+ */
+
+/*
+ * Map between ISA memory space and PCMCIA card memory space.
+ */
+struct PCMmap {
+	ulong	ca;			/* card address */
+	ulong	cea;			/* card end address */
+	ulong	isa;			/* local virtual address */
+	int	len;			/* length of the ISA area */
+	int	attr;			/* attribute memory */
+};
+
+/*
+ *  a PCMCIA configuration entry
+ */
+struct PCMconftab
+{
+	int	index;
+	ushort	irqs;		/* legal irqs */
+	uchar	irqtype;
+	uchar	bit16;		/* true for 16 bit access */
+	struct {
+		ulong	start;
+		ulong	len;
+	} io[16];
+	int	nio;
+	uchar	vpp1;
+	uchar	vpp2;
+	uchar	memwait;
+	ulong	maxwait;
+	ulong	readywait;
+	ulong	otherwait;
+};
+
+/*
+ *  PCMCIA card slot
+ */
+struct PCMslot
+{
+	RWlock;
+
+	Ref	ref;
+
+	long	memlen;		/* memory length */
+	uchar	slotno;		/* slot number */
+	void	*regs;		/* i/o registers */
+	void	*mem;		/* memory */
+	void	*attr;		/* attribute memory */
+
+	/* status */
+	uchar	occupied;	/* card in the slot */
+	uchar	configed;	/* card configured */
+	uchar	busy;
+	uchar	powered;
+	uchar	battery;
+	uchar	wrprot;
+	uchar	enabled;
+	uchar	special;
+	uchar	dsize;
+
+	/* cis info */
+	int	cisread;	/* set when the cis has been read */
+	char	verstr[512];	/* version string */
+	int	ncfg;		/* number of configurations */
+	struct {
+		ushort	cpresent;	/* config registers present */
+		ulong	caddr;		/* relative address of config registers */
+	} cfg[8];
+	int	nctab;		/* number of config table entries */
+	PCMconftab	ctab[8];
+	PCMconftab	*def;		/* default conftab */
+
+	/* maps are fixed */
+	PCMmap memmap;
+	PCMmap attrmap;
+};
--- /dev/null
+++ b/os/sa1110/softcursor.c
@@ -1,0 +1,365 @@
+#include "u.h"
+#include "../port/lib.h"
+#include "mem.h"
+#include "dat.h"
+#include "fns.h"
+#include "../port/error.h"
+#include "io.h"
+
+#include <draw.h>
+#include <memdraw.h>
+#include <cursor.h>
+
+#include "gscreen.h"
+
+/*
+ * Software cursor code: done by hand, might be better to use memimagedraw
+ * but that would need to be done by a process
+ */
+ 
+typedef struct Cursordata Cursordata;
+struct Cursordata {
+	Physdisplay	*vd;
+	ulong	*fb;	/* screen frame buffer */
+	Rectangle r;
+	int	depth;	/* depth of screen */
+	int 	width;	/* width of screen in ulongs */
+	int	x;
+	int	y;
+	int	hotx;
+	int	hoty;
+	int	cbwid;	/* cursor byte width */
+	int	f;	/* flags */
+	int	dx;
+	int	dy;
+	int	hidecount;
+	uchar	data[CURSWID*CURSHGT];
+	uchar	mask[CURSWID*CURSHGT];
+	uchar	save[CURSWID*CURSHGT];
+};
+
+static Cursordata *cd = nil;
+
+enum {
+	Enabled = 0x01,		/* cursor is enabled */
+	Drawn = 0x02,		/* cursor is currently drawn */
+	Bitswap = 0x10,
+};
+
+static Rectangle cursoroffrect;
+static int	cursorisoff;
+
+static void swcursorflush(Point);
+static void	swcurs_draw_or_undraw(Cursordata *);
+
+static void
+cursorupdate0(void)
+{
+	int inrect, x, y;
+	Point m;
+
+	m = mousexy();
+	x = m.x - cd->hotx;
+	y = m.y - cd->hoty;
+	inrect = (x >= cursoroffrect.min.x && x < cursoroffrect.max.x
+		&& y >= cursoroffrect.min.y && y < cursoroffrect.max.y);
+	if (cursorisoff == inrect)
+		return;
+	cursorisoff = inrect;
+	if (inrect)
+		swcurs_hide(swc);
+	else {
+		cd->hidecount = 0;
+		swcurs_draw_or_undraw(swc);
+	}
+	swcursorflush(m);
+}
+
+void
+cursorupdate(Rectangle r)
+{
+	lock(vd);
+	r.min.x -= 16;
+	r.min.y -= 16;
+	cursoroffrect = r;
+	if (vd->cursor != nil)
+		cursorupdate0();
+	unlock(vd);
+}
+
+void
+cursorenable(void)
+{
+	lock(vd);
+	if(vd->cursor != nil)
+		vd->cursor->enable(swc);
+//		swcursorflush(mousexy());
+	unlock(vd);
+}
+
+void
+cursordisable(void)
+{
+
+	lock(vd);
+	if(swc != nil) {
+		swcurs_disable(swc);
+		swcursorflush(mousexy());
+	}
+	unlock(vd);
+}
+
+static void
+swcursupdate(int oldx, int oldy, int x, int y)
+{
+
+	if(!canlock(vd))
+		return;		/* if can't lock, don't wake up stuff */
+
+	if(x < gscreen->r.min.x)
+		x = gscreen->r.min.x;
+	if(x >= gscreen->r.max.x)
+		x = gscreen->r.max.x;
+	if(y < gscreen->r.min.y)
+		y = gscreen->r.min.y;
+	if(y >= gscreen->r.max.y)
+		y = gscreen->r.max.y;
+	if(swc != nil) {
+		swcurs_hide(swc);
+		cd->x = x;
+		cd->y = y;
+		cursorupdate0();
+		swcurs_unhide(swc);
+		swcursorflush(oldx, oldy);
+		swcursorflush(x, y);
+	}
+
+	unlock(vd);
+}
+
+void
+drawcursor(Drawcursor* c)
+{
+	Point p;
+	Cursor curs, *cp;
+	int j, i, h, bpl;
+	uchar *bc, *bs, *cclr, *cset;
+
+	if(swc == nil)
+		return;
+
+	/* Set the default system cursor */
+	if(c == nil || c->data == nil){
+		swcurs_disable(swc);
+		return;
+	}
+	else {
+		cp = &curs;
+		p.x = c->hotx;
+		p.y = c->hoty;
+		cp->offset = p;
+		bpl = bytesperline(Rect(c->minx, c->miny, c->maxx, c->maxy), 1);
+
+		h = (c->maxy-c->miny)/2;
+		if(h > 16)
+			h = 16;
+
+		bc = c->data;
+		bs = c->data + h*bpl;
+
+		cclr = cp->clr;
+		cset = cp->set;
+		for(i = 0; i < h; i++) {
+			for(j = 0; j < 2; j++) {
+				cclr[j] = bc[j];
+				cset[j] = bs[j];
+			}
+			bc += bpl;
+			bs += bpl;
+			cclr += 2;
+			cset += 2;
+		}
+	}
+	swcurs_load(swc, cp);
+	swcursorflush(mousexy());
+	swcurs_enable(swc);
+}
+
+void*
+create(Physdisplay *vd)
+{
+	Cursordata *cd;
+
+	swc = (Cursordata*)malloc(sizeof(Cursordata));
+	cd->vd = vd;
+	cd->fb = vd->gscreen->data->bdata;	/* or vd->fb? */
+	cd->r = vd->gscreen->r;
+	cd->d = vd->gscreen->depth;
+	cd->width = vd->gscreen->width;
+//	cd->f = bitswap ? Bitswap : 0;
+	cd->f = Bitswap;	/* ??? */
+	cd->x = cd->y = 0;
+	cd->hotx = cd->hoty = 0;
+	cd->hidecount = 0;
+	return cd;
+}
+
+void
+swcurs_destroy(Cursordata *cd)
+{
+	swcurs_disable(cd);
+	free(cd);
+}
+
+static void
+swcursorflush(Point p)
+{
+	Rectangle r;
+
+	/* XXX a little too paranoid here */
+	r.min.x = p.x-16;
+	r.min.y = p.y-16;
+	r.max.x = p.x+17;
+	r.max.y = p.y+17;
+	flushmemscreen(r);
+}
+
+static void
+swcurs_draw_or_undraw(Cursordata *cd)
+{
+	uchar *p;
+	uchar *cs;
+	int w, vw;
+	int x1 = cd->r.min.x;
+	int y1 = cd->r.min.y;
+	int x2 = cd->r.max.x;
+	int y2 = cd->r.max.y; 
+	int xp = cd->x - cd->hotx;
+	int yp = cd->y - cd->hoty;
+	int ofs;
+
+	if(((cd->f & Enabled) && (cd->hidecount <= 0))
+			 == ((cd->f & Drawn) != 0))
+		return;
+	w = cd->cbwid*BI2BY/cd->depth;
+	x1 = xp < x1 ? x1 : xp;
+	y1 = yp < y1 ? y1 : yp;
+	x2 = xp+w >= x2 ? x2 : xp+w;
+	y2 = yp+cd->dy >= y2 ? y2 : yp+cd->dy;
+	if(x2 <= x1 || y2 <= y1)
+		return;
+	p = (uchar*)(cd->fb + cd->width*y1) + x1*(1 << cd->d)/BI2BY;
+	y2 -= y1;
+	x2 = (x2-x1)*cd->depth/BI2BY;
+	vw = cd->width*BY2WD - x2;
+	w = cd->cbwid - x2;
+	ofs = cd->cbwid*(y1-yp)+(x1-xp);
+	cs = cd->save + ofs;
+	if((cd->f ^= Drawn) & Drawn) {
+		uchar *cm = cd->mask + ofs; 
+		uchar *cd = cd->data + ofs;
+		while(y2--) {
+			x1 = x2;
+			while(x1--) {
+				*cs++ = *p;
+				*p = (*p & *cm++) ^ *cd++;
+				p++;
+			}
+			cs += w;
+			cm += w;
+			cd += w;
+			p += vw;
+		}
+	} else {
+		while(--y2 >= 0){
+			for(x1 = x2; --x1 >= 0;)
+				*p++ = *cs++;
+			cs += w;
+			p += vw;
+		}
+	}
+}
+
+static void
+swcurs_hide(Cursordata *cd)
+{
+	++cd->hidecount;
+	swcurs_draw_or_undraw(swc);
+}
+
+static void
+swcurs_unhide(Cursordata *cd)
+{
+	if (--cd->hidecount < 0)
+		cd->hidecount = 0;
+	swcurs_draw_or_undraw(swc);
+}
+
+static void
+swcurs_enable(Cursordata *cd)
+{
+	cd->f |= Enabled;
+	swcurs_draw_or_undraw(swc);
+}
+
+void
+swcurs_disable(Cursordata *cd)
+{
+	cd->f &= ~Enabled;
+	swcurs_draw_or_undraw(swc);
+}
+
+static void
+load(Cursordata *cd, Cursor *c)
+{
+	int i, k;
+	uchar *bc, *bs, *cd, *cm;
+	static uchar bdv[4] = {0,Backgnd,Foregnd,0xff};
+	static uchar bmv[4] = {0xff,0,0,0xff};
+	int bits = 1<<cd->depth;
+	uchar mask = (1<<bits)-1;
+	int bswp = (cd->f&Bitswap) ? 8-bits : 0;
+
+	bc = c->clr;
+	bs = c->set;
+
+	swcurs_hide(swc);
+	cd = cd->data;
+	cm = cd->mask;
+	cd->hotx = c->offset.x;
+	cd->hoty = c->offset.y;
+	cd->dy = CURSHGT;
+	cd->dx = CURSWID;
+	cd->cbwid = CURSWID*(1<<cd->d)/BI2BY;
+	for(i = 0; i < CURSWID/BI2BY*CURSHGT; i++) {
+		uchar bcb = *bc++;
+		uchar bsb = *bs++;
+		for(k=0; k<BI2BY;) {
+			uchar cdv = 0;
+			uchar cmv = 0;
+			int z;
+			for(z=0; z<BI2BY; z += bits) {
+				int n = ((bsb&(0x80))|((bcb&(0x80))<<1))>>7;
+				int s = z^bswp;
+				cdv |= (bdv[n]&mask) << s;
+				cmv |= (bmv[n]&mask) << s;
+				bcb <<= 1;
+				bsb <<= 1;
+				k++;
+			}
+			*cd++ = cdv;
+			*cm++ = cmv;
+		}
+	}
+	swcurs_unhide(swc);
+}
+
+Physcursor softcursor = {
+	.name = "softcursor",
+	.create = create,
+	.enable = swenable,
+	.disable = swdisable,
+	.load = load,
+	.move = move,
+	.destroy = destroy,
+};
--- /dev/null
+++ b/os/sa1110/suspend.c
@@ -1,0 +1,161 @@
+#include	"u.h"
+#include	"../port/lib.h"
+#include	"mem.h"
+#include	"dat.h"
+#include	"fns.h"
+#include	"io.h"
+#include	"ureg.h"
+
+/*
+ * Originally written by nemo@gsyc.escet.urjc.es, and
+ * reworked by forsyth@vitanuova.com
+ */
+enum {
+	DEBUG = 0,
+};
+
+/*
+ * TO DO: pcmcia, lcd properly
+ */
+
+/*
+ * it's not clear yet whether we should do it this way,
+ * or using powerenable/powerdisable
+ */
+void
+chandevpower(int up)
+{
+	int i;
+
+	if(up){
+		for(i=0; devtab[i] != nil; i++)
+			if(devtab[i]->power != nil)
+				devtab[i]->power(1);
+	}else{
+		/* power down in reverse order */
+		for(i=0; devtab[i] != nil; i++)
+			;
+		while(--i >= 0)
+			if(devtab[i]->power != nil)
+				devtab[i]->power(0);
+	}
+}
+
+static void
+dumpitall(void)
+{
+	iprint("intr: icip %lux iclr %lux iccr %lux icmr %lux\n",
+		INTRREG->icip,
+		INTRREG->iclr, INTRREG->iccr, INTRREG->icmr );
+	iprint("gpio: lvl %lux dir %lux, re %lux, fe %lux sts %lux alt %lux\n",
+		GPIOREG->gplr,
+		GPIOREG->gpdr, GPIOREG->grer, GPIOREG->gfer,
+		GPIOREG->gpsr, GPIOREG->gafr);
+	iprint("uart1: %lux %lux %lux\nuart3: %lux %lux %lux\n", 
+		UARTREG(1)->utcr0, UARTREG(1)->utsr0, UARTREG(1)->utsr1, 
+		UARTREG(3)->utcr0, UARTREG(3)->utsr0, UARTREG(3)->utsr1); 
+	iprint("tmr: osmr %lux %lux %lux %lux oscr %lux ossr %lux oier %lux\n",
+		OSTMRREG->osmr[0], OSTMRREG->osmr[1],
+		OSTMRREG->osmr[2], OSTMRREG->osmr[3],
+		OSTMRREG->oscr, OSTMRREG->ossr, OSTMRREG->oier);
+	iprint("dram: mdcnfg %lux mdrefr %lux cas %lux %lux %lux %lux %lux %lux\n",
+		MEMCFGREG->mdcnfg, MEMCFGREG->mdrefr,
+		MEMCFGREG->mdcas0[0], MEMCFGREG->mdcas0[1],MEMCFGREG->mdcas0[2],
+		MEMCFGREG->mdcas2[0], MEMCFGREG->mdcas2[1],MEMCFGREG->mdcas2[2]); 
+	iprint("dram: mdcnfg msc %lux %lux %lux mecr %lux\n",
+		MEMCFGREG->msc0, MEMCFGREG->msc1,MEMCFGREG->msc2,
+		MEMCFGREG->mecr);
+}
+
+static ulong *coreregs[] = {
+	/* can't trust the bootstrap to put these back */
+	&MEMCFGREG->mecr,
+	&MEMCFGREG->msc0,
+	&MEMCFGREG->msc1,
+	&MEMCFGREG->msc2,
+
+	&PPCREG->ppdr,
+	&PPCREG->ppsr,	/* should save? */
+	&PPCREG->ppar,
+	&PPCREG->psdr,
+
+	&GPIOREG->grer,
+	&GPIOREG->gfer,
+	&GPIOREG->gafr,
+	&GPIOREG->gpdr,
+	/* gplr handled specially */
+
+	&GPCLKREG->gpclkr1,
+	&GPCLKREG->gpclkr2,
+	&GPCLKREG->gpclkr0,
+
+	&OSTMRREG->osmr[0],
+	&OSTMRREG->osmr[1],
+	&OSTMRREG->osmr[2],
+	&OSTMRREG->osmr[3],
+	&OSTMRREG->oscr,
+	&OSTMRREG->oier,
+	/* skip ower */
+
+	&INTRREG->iclr,
+	&INTRREG->iccr,
+	&INTRREG->icmr,	/* interrupts enabled */
+
+	nil,
+};
+
+static ulong corestate[nelem(coreregs)];
+
+void
+powersuspend(void)
+{
+	extern void suspenditall(void);
+	GpioReg *g;
+	ulong back = 0x43219990;	/* check that the stack's right */
+	ulong pwer, gplr;
+	ulong *rp;
+	int i, s;
+
+	s = splfhi();
+	archpowerdown();	/* sets PMGR and PPC appropriately */
+	if(DEBUG)
+		dumpitall();
+	blankscreen(1);
+	chandevpower(0);
+	gplr = GPIOREG->gplr;
+	for(i=0; (rp = coreregs[i]) != nil; i++)
+		corestate[i] = *rp;
+	pwer = PMGRREG->pwer;
+	if(pwer == 0)
+		pwer = 1<<0;
+	g = GPIOREG;
+	g->grer &= pwer;	/* just the ones archpowerdown requested */
+	g->gfer &= pwer;
+	g->gedr = g->gedr;
+	RESETREG->rcsr = 0xF;	/* reset all status */
+	minidcflush();
+	if(DEBUG)
+		iprint("suspenditall...\n");
+
+	suspenditall();	/* keep us in suspense */
+
+	PMGRREG->pspr = 0;
+	archpowerup();
+	trapstacks();
+	/* set output latches before gpdr restored */
+	GPIOREG->gpsr = gplr;
+	GPIOREG->gpcr = ~gplr;
+	for(i=0; (rp = coreregs[i]) != nil; i++)
+		*rp = corestate[i];
+	GPIOREG->gedr = GPIOREG->gedr;	/* reset GPIO interrupts (should we?) */
+	PMGRREG->pssr = PSSR_ph;	/* cancel peripheral hold */
+	chandevpower(1);
+	if(back != 0x43219990){
+		iprint("back %8.8lux\n", back);
+		panic("powersuspend");
+	}
+	blankscreen(0);
+	if(DEBUG)
+		dumpitall();
+	splx(s);
+}
--- /dev/null
+++ b/os/sa1110/trap.c
@@ -1,0 +1,601 @@
+#include	"u.h"
+#include	"../port/lib.h"
+#include	"mem.h"
+#include	"dat.h"
+#include	"fns.h"
+#include	"io.h"
+#include	"ureg.h"
+#include	"../port/error.h"
+
+#define waslo(sr) (!((sr) & (PsrDirq|PsrDfiq)))
+
+typedef struct Handler Handler;
+
+struct Handler {
+	void	(*r)(Ureg*, void*);
+	void	*a;
+	int	v;
+	char	name[KNAMELEN];
+	Handler	*next;
+};
+
+enum {
+	MinGpioIRQbit = 11,
+	NumGpioIRQbits = MaxGPIObit-MinGpioIRQbit+1,
+	GpioIRQmask = ((1<<NumGpioIRQbits)-1)<<MinGpioIRQbit,
+};
+
+static Handler irqvec[MaxIRQbit+1];
+static Handler gpiovec[NumGpioIRQbits];
+static Lock veclock;
+
+Instr BREAK = 0xE6BAD010;
+
+int (*breakhandler)(Ureg*, Proc*);
+int (*catchdbg)(Ureg *, uint);
+void (*idle)(void);
+void (*suspendcode)(void);
+
+extern void (*serwrite)(char *, int);
+
+/*
+ * Interrupt sources not masked by splhi(): special
+ *  interrupt handlers (eg, profiler or watchdog), not allowed
+ *  to share regular kernel data structures.  All interrupts are
+ *  masked by splfhi(), which should only be used sparingly.
+ * splflo enables FIQ but no others.
+ */
+enum {
+	IRQ_NONMASK = (1 << OSTimerbit(3)) | (1 << OSTimerbit(2)),
+};
+
+void
+intrenable(int v, void (*f)(Ureg*, void*), void* a, int tbdf, char *name)
+{
+	int x;
+	GpioReg *g;
+	Handler *ie;
+
+	ilock(&veclock);
+	switch(tbdf) {
+	case BusGPIOfalling:
+	case BusGPIOrising:
+	case BusGPIOboth:
+		if(v < 0 || v > MaxGPIObit)
+			panic("intrenable: gpio source %d out of range", v);
+		g = GPIOREG;
+		switch(tbdf){
+		case BusGPIOfalling:
+			g->gfer |= 1<<v;
+			g->grer &= ~(1<<v);
+			break;
+		case BusGPIOrising:
+			g->grer |= 1<<v;
+			g->gfer &= ~(1<<v);
+			break;
+		case BusGPIOboth:
+			g->grer |= 1<<v;
+			g->gfer |= 1<<v;
+			break;
+		}
+		g->gpdr &= ~(1<<v);
+		if(v >= MinGpioIRQbit) {
+			ie = &gpiovec[v-MinGpioIRQbit];
+			if(ie->r != nil)
+				iprint("duplicate gpio irq: %d (%s)\n", v, ie->name);
+			ie->r = f;
+			ie->a = a;
+			strncpy(ie->name, name, KNAMELEN-1);
+			ie->name[KNAMELEN-1] = 0;
+			iunlock(&veclock);
+			return;
+		}
+		/*FALLTHROUGH for GPIO sources 0-10 */
+	case BUSUNKNOWN:
+	case BusCPU:
+		if(v < 0 || v > MaxIRQbit)
+			panic("intrenable: irq source %d out of range", v);
+		ie = &irqvec[v];
+		if(ie->r != nil)
+			iprint("duplicate irq: %d (%s)\n", v, ie->name);
+		ie->r = f;
+		ie->a = a;
+		strncpy(ie->name, name, KNAMELEN-1);
+		ie->name[KNAMELEN-1] = 0;
+
+		x = splfhi();
+		/* Enable the interrupt by setting the mask bit */
+		INTRREG->icmr |= 1 << v;
+		splx(x);
+		break;
+	default:
+		panic("intrenable: unknown irq bus %d", tbdf);
+	}
+	iunlock(&veclock);
+}
+
+void
+intrdisable(int v, void (*f)(Ureg*, void*), void* a, int tbdf, char *name)
+{
+	int x;
+	GpioReg *g;
+	Handler *ie;
+
+	ilock(&veclock);
+	switch(tbdf) {
+	case BusGPIOfalling:
+	case BusGPIOrising:
+	case BusGPIOboth:
+		if(v < 0 || v > MaxGPIObit)
+			panic("intrdisable: gpio source %d out of range", v);
+		if(v >= MinGpioIRQbit)
+			ie = &gpiovec[v-MinGpioIRQbit];
+		else
+			ie = &irqvec[v];
+		if(ie->r != f || ie->a != a || strcmp(ie->name, name) != 0)
+			break;
+		ie->r = nil;
+		if(v < MinGpioIRQbit){
+			x = splfhi();
+			INTRREG->icmr &= ~(1<<v);
+			splx(x);
+		}
+		g = GPIOREG;
+		switch(tbdf){
+		case BusGPIOfalling:
+			g->gfer &= ~(1<<v);
+			break;
+		case BusGPIOrising:
+			g->grer &= ~(1<<v);
+			break;
+		case BusGPIOboth:
+			g->grer &= ~(1<<v);
+			g->gfer &= ~(1<<v);
+			break;
+		}
+		break;
+	case BUSUNKNOWN:
+	case BusCPU:
+		if(v < 0 || v > MaxIRQbit)
+			panic("intrdisable: irq source %d out of range", v);
+		ie = &irqvec[v];
+		if(ie->r != f || ie->a != a || strcmp(ie->name, name) != 0)
+			break;
+		ie->r = nil;
+		x = splfhi();
+		INTRREG->icmr &= ~(1<<v);
+		splx(x);
+		break;
+	default:
+		panic("intrdisable: unknown irq bus %d", tbdf);
+	}
+	iunlock(&veclock);
+}
+
+static void
+gpiointr(Ureg *ur, void*)
+{
+	Handler *cur;
+	ulong e;
+	int i;
+
+	e = GPIOREG->gedr & GpioIRQmask;
+	GPIOREG->gedr = e;
+	for(i = MinGpioIRQbit; i <= MaxGPIObit && e != 0; i++){
+		if(e & (1<<i)){
+			cur = &gpiovec[i-MinGpioIRQbit];
+			if(cur->r != nil){
+				cur->r(ur, cur->a);
+				e &= ~(1<<i);
+			}
+		}
+	}
+	if(e != 0){
+		GPIOREG->gfer &= ~e;
+		GPIOREG->grer &= ~e;
+		iprint("spurious GPIO interrupt: %8.8lux\n", e);
+	}
+}
+
+static void
+intrs(Ureg *ur, ulong ibits)
+{
+	Handler *cur;
+	int i, s;
+
+	for(i=0; i<nelem(irqvec) && ibits; i++)
+		if(ibits & (1<<i)){
+			cur = &irqvec[i];
+			if(cur->r != nil){
+				cur->r(ur, cur->a);
+				ibits &= ~(1<<i);
+			}
+		}
+	if(ibits != 0){
+		iprint("spurious irq interrupt: %8.8lux\n", ibits);
+		s = splfhi();
+		INTRREG->icmr &= ~ibits;
+		splx(s);
+	}
+}
+
+/*
+ * initialise R13 in each trap mode, at the start and after suspend reset.
+ */
+void
+trapstacks(void)
+{
+	setr13(PsrMfiq, m->fiqstack+nelem(m->fiqstack));
+	setr13(PsrMirq, m->irqstack+nelem(m->irqstack));
+	setr13(PsrMabt, m->abtstack+nelem(m->abtstack));
+	setr13(PsrMund, m->undstack+nelem(m->undstack));
+}
+
+void
+trapinit(void)
+{
+	int v;
+	IntrReg *intr = INTRREG;
+
+	intr->icmr = 0;
+	intr->iclr = IRQ_NONMASK;
+
+	trapstacks();
+
+	for(v = 0; v < nelem(irqvec); v++) {
+		irqvec[v].r = nil;
+		irqvec[v].a = nil;
+		irqvec[v].v = v;
+	}
+	for(v = 0; v < nelem(gpiovec); v++) {
+		gpiovec[v].r = nil;
+		gpiovec[v].a = nil;
+		gpiovec[v].v = v+MinGpioIRQbit;
+	}
+
+	memmove(page0->vectors, vectors, sizeof(page0->vectors));
+	memmove(page0->vtable, vtable, sizeof(page0->vtable));
+	dcflush(page0, sizeof(*page0));
+
+	idle = xspanalloc(13*sizeof(ulong), CACHELINESZ, 0);
+	memmove(idle, _idlemode, 13*sizeof(ulong));
+	dcflush(idle, 13*sizeof(ulong));
+
+	suspendcode = xspanalloc(16*sizeof(ulong), CACHELINESZ, 0);
+	memmove(suspendcode, _suspendcode, 16*sizeof(ulong));
+	dcflush(suspendcode, 8*sizeof(ulong));
+
+	icflushall();
+
+	intrenable(MinGpioIRQbit, gpiointr, nil, BusCPU, "gpio");
+}
+
+static char *trapnames[PsrMask+1] = {
+	[ PsrMfiq ] "Fiq interrupt",
+	[ PsrMirq ] "Mirq interrupt",
+	[ PsrMsvc ] "SVC/SWI Exception",
+	[ PsrMabt ] "Prefetch Abort/Data Abort",
+	[ PsrMabt+1 ] "Data Abort",
+	[ PsrMund ] "Undefined instruction",
+	[ PsrMsys ] "Sys trap"
+};
+
+static char *
+trapname(int psr)
+{
+	char *s;
+
+	s = trapnames[psr & PsrMask];
+	if(s == nil)
+		s = "Undefined trap";
+	return s;
+}
+
+static void
+sys_trap_error(int type)
+{
+	char errbuf[ERRMAX];
+	sprint(errbuf, "sys: trap: %s\n", trapname(type));
+	error(errbuf);
+}
+
+static void
+faultarm(Ureg *ureg, ulong far)
+{
+	char buf[ERRMAX];
+
+	sprint(buf, "sys: trap: fault pc=%8.8lux addr=0x%lux", (ulong)ureg->pc, far);
+	if(0){
+		iprint("%s\n", buf);
+		dumpregs(ureg);
+	}
+	if(far == ~0)
+		disfault(ureg, "dereference of nil");
+	disfault(ureg, buf);
+}
+
+/*
+ *  All traps come here.  It might be slightly slower to have all traps call trap
+ *  rather than directly vectoring the handler.
+ *  However, this avoids a lot of code duplication and possible bugs.
+ *  trap is called splfhi().
+ */
+void
+trap(Ureg* ureg)
+{
+	ulong far, fsr;
+	int rem, t, itype;
+	Proc *oup;
+
+	if(up != nil)
+		rem = ((char*)ureg)-up->kstack;
+	else
+		rem = ((char*)ureg)-(char*)m->stack;
+	if(ureg->type != PsrMfiq && rem < 256)
+		panic("trap %d bytes remaining (%s), up=#%8.8lux ureg=#%8.8lux pc=#%8.8ux",
+			rem, up?up->text:"", up, ureg, ureg->pc);
+
+	/*
+	 * All interrupts/exceptions should be resumed at ureg->pc-4,
+	 * except for Data Abort which resumes at ureg->pc-8.
+	 */
+	itype = ureg->type;
+	if(itype == PsrMabt+1)
+		ureg->pc -= 8;
+	else
+		ureg->pc -= 4;
+	ureg->sp = (ulong)(ureg+1);
+	if(itype == PsrMfiq){	/* fast interrupt (eg, profiler) */
+		oup = up;
+		up = nil;
+		intrs(ureg, INTRREG->icfp);
+		up = oup;
+		return;
+	}
+
+	/* All other traps */
+
+	if(ureg->psr & PsrDfiq)
+		panic("FIQ disabled");
+
+	if(up){
+		up->pc = ureg->pc;
+		up->dbgreg = ureg;
+	}
+	switch(itype) {
+	case PsrMirq:
+		t = m->ticks;	/* CPU time per proc */
+		up = nil;		/* no process at interrupt level */
+		splflo();	/* allow fast interrupts */
+		intrs(ureg, INTRREG->icip);
+		up = m->proc;
+		preemption(m->ticks - t);
+		break;
+
+	case PsrMund:				/* Undefined instruction */
+		if(*(ulong*)ureg->pc == BREAK && breakhandler) {
+			int s;
+			Proc *p;
+
+			p = up;
+			/* if(!waslo(ureg->psr) || ureg->pc >= (ulong)splhi && ureg->pc < (ulong)islo)
+				p = 0; */
+			s = breakhandler(ureg, p);
+			if(s == BrkSched) {
+				p->preempted = 0;
+				sched();
+			} else if(s == BrkNoSched) {
+				p->preempted = 1;	/* stop it being preempted until next instruction */
+				if(up)
+					up->dbgreg = 0;
+				return;
+			}
+			break;
+		}
+		if(up == nil)
+			goto faultpanic;
+		spllo();
+		if(waserror()) {
+			if(waslo(ureg->psr) && up->type == Interp)
+				disfault(ureg, up->env->errstr);
+			setpanic();
+			dumpregs(ureg);
+			panic("%s", up->env->errstr);
+		}
+		if(!fpiarm(ureg)) {
+			dumpregs(ureg);
+			sys_trap_error(ureg->type);
+		}
+		poperror();
+		break;
+
+	case PsrMsvc:				/* Jump through 0 or SWI */
+		if(waslo(ureg->psr) && up && up->type == Interp) {
+			spllo();
+			dumpregs(ureg);
+			sys_trap_error(ureg->type);
+		}
+		setpanic();
+		dumpregs(ureg);
+		panic("SVC/SWI exception");
+		break;
+
+	case PsrMabt:				/* Prefetch abort */
+		if(catchdbg && catchdbg(ureg, 0))
+			break;
+		/* FALL THROUGH */
+	case PsrMabt+1:			/* Data abort */
+		fsr = mmugetfsr();
+		far = mmugetfar();
+		if(fsr & (1<<9)) {
+			mmuputfsr(fsr & ~(1<<9));
+			if(catchdbg && catchdbg(ureg, fsr))
+				break;
+			print("Debug/");
+		}
+		if(waslo(ureg->psr) && up && up->type == Interp) {
+			spllo();
+			faultarm(ureg, far);
+		}
+		print("Data Abort: FSR %8.8luX FAR %8.8luX\n", fsr, far);
+		/* FALL THROUGH */
+
+	default:				/* ??? */
+faultpanic:
+		setpanic();
+		dumpregs(ureg);
+		panic("exception %uX %s\n", ureg->type, trapname(ureg->type));
+		break;
+	}
+
+	splhi();
+	if(up)
+		up->dbgreg = 0;		/* becomes invalid after return from trap */
+}
+
+void
+setpanic(void)
+{
+	if(breakhandler != 0)	/* don't mess up debugger */
+		return;
+	INTRREG->icmr = 0;
+	spllo();
+	consoleprint = 1;
+	serwrite = uartputs;
+}
+
+int
+isvalid_wa(void *v)
+{
+	return (ulong)v >= KZERO && (ulong)v < conf.topofmem && !((ulong)v & 3);
+}
+
+int
+isvalid_va(void *v)
+{
+	return (ulong)v >= KZERO && (ulong)v < conf.topofmem;
+}
+
+void
+dumplongs(char *msg, ulong *v, int n)
+{
+	int i, l;
+
+	l = 0;
+	iprint("%s at %.8p: ", msg, v);
+	for(i=0; i<n; i++){
+		if(l >= 4){
+			iprint("\n    %.8p: ", v);
+			l = 0;
+		}
+		if(isvalid_va(v)){
+			iprint(" %.8lux", *v++);
+			l++;
+		}else{
+			iprint(" invalid");
+			break;
+		}
+	}
+	iprint("\n");
+}
+
+static void
+_dumpstack(Ureg *ureg)
+{
+	ulong *v, *l;
+	ulong inst;
+	ulong *estack;
+	int i;
+
+	l = (ulong*)(ureg+1);
+	if(!isvalid_wa(l)){
+		iprint("invalid ureg/stack: %.8p\n", l);
+		return;
+	}
+	print("ktrace /kernel/path %.8ux %.8ux %.8ux\n", ureg->pc, ureg->sp, ureg->r14);
+	if(up != nil && l >= (ulong*)up->kstack && l <= (ulong*)(up->kstack+KSTACK-4))
+		estack = (ulong*)(up->kstack+KSTACK);
+	else if(l >= (ulong*)m->stack && l <= (ulong*)((ulong)m+BY2PG-4))
+		estack = (ulong*)((ulong)m+BY2PG-4);
+	else{
+		iprint("unknown stack\n");
+		return;
+	}
+	i = 0;
+	for(; l<estack; l++) {
+		if(!isvalid_wa(l)) {
+			iprint("invalid(%8.8p)", l);
+			break;
+		}
+		v = (ulong*)*l;
+		if(isvalid_wa(v)) {
+			inst = v[-1];
+			if((inst & 0x0ff0f000) == 0x0280f000 &&
+			     (*(v-2) & 0x0ffff000) == 0x028fe000	||
+				(inst & 0x0f000000) == 0x0b000000) {
+				iprint("%8.8p=%8.8lux ", l, v);
+				i++;
+			}
+		}
+		if(i == 4){
+			iprint("\n");
+			i = 0;
+		}
+	}
+	if(i)
+		print("\n");
+}
+
+void
+dumpregs(Ureg* ureg)
+{
+	print("TRAP: %s", trapname(ureg->type));
+	if((ureg->psr & PsrMask) != PsrMsvc)
+		print(" in %s", trapname(ureg->psr));
+	print("\n");
+	print("PSR %8.8uX type %2.2uX PC %8.8uX LINK %8.8uX\n",
+		ureg->psr, ureg->type, ureg->pc, ureg->link);
+	print("R14 %8.8uX R13 %8.8uX R12 %8.8uX R11 %8.8uX R10 %8.8uX\n",
+		ureg->r14, ureg->r13, ureg->r12, ureg->r11, ureg->r10);
+	print("R9  %8.8uX R8  %8.8uX R7  %8.8uX R6  %8.8uX R5  %8.8uX\n",
+		ureg->r9, ureg->r8, ureg->r7, ureg->r6, ureg->r5);
+	print("R4  %8.8uX R3  %8.8uX R2  %8.8uX R1  %8.8uX R0  %8.8uX\n",
+		ureg->r4, ureg->r3, ureg->r2, ureg->r1, ureg->r0);
+	print("Stack is at: %8.8luX\n", ureg);
+	print("PC %8.8lux LINK %8.8lux\n", (ulong)ureg->pc, (ulong)ureg->link);
+
+	if(up)
+		print("Process stack:  %8.8lux-%8.8lux\n",
+			up->kstack, up->kstack+KSTACK-4);
+	else
+		print("System stack: %8.8lux-%8.8lux\n",
+			(ulong)(m+1), (ulong)m+BY2PG-4);
+	dumplongs("stack", (ulong *)(ureg + 1), 16);
+	_dumpstack(ureg);
+}
+
+/*
+ * Fill in enough of Ureg to get a stack trace, and call a function.
+ * Used by debugging interface rdb.
+ */
+void
+callwithureg(void (*fn)(Ureg*))
+{
+	Ureg ureg;
+	ureg.pc = getcallerpc(&fn);
+	ureg.sp = (ulong)&fn;
+	ureg.r14 = 0;
+	fn(&ureg);
+}
+
+void
+dumpstack(void)
+{
+	callwithureg(_dumpstack);
+}
+
+void
+trapspecial(int (*f)(Ureg *, uint))
+{
+	catchdbg = f;
+}